summaryrefslogtreecommitdiffstats
path: root/src/gui
diff options
context:
space:
mode:
authorLars Knoll <lars.knoll@nokia.com>2009-03-23 10:18:55 +0100
committerSimon Hausmann <simon.hausmann@nokia.com>2009-03-23 10:18:55 +0100
commite5fcad302d86d316390c6b0f62759a067313e8a9 (patch)
treec2afbf6f1066b6ce261f14341cf6d310e5595bc1 /src/gui
Long live Qt 4.5!
Diffstat (limited to 'src/gui')
-rw-r--r--src/gui/QtGui.dynlist8
-rw-r--r--src/gui/accessible/accessible.pri24
-rw-r--r--src/gui/accessible/qaccessible.cpp1079
-rw-r--r--src/gui/accessible/qaccessible.h417
-rw-r--r--src/gui/accessible/qaccessible2.cpp181
-rw-r--r--src/gui/accessible/qaccessible2.h217
-rw-r--r--src/gui/accessible/qaccessible_mac.mm2608
-rw-r--r--src/gui/accessible/qaccessible_mac_carbon.cpp119
-rw-r--r--src/gui/accessible/qaccessible_mac_cocoa.mm0
-rw-r--r--src/gui/accessible/qaccessible_mac_p.h479
-rw-r--r--src/gui/accessible/qaccessible_unix.cpp134
-rw-r--r--src/gui/accessible/qaccessible_win.cpp1219
-rw-r--r--src/gui/accessible/qaccessiblebridge.cpp158
-rw-r--r--src/gui/accessible/qaccessiblebridge.h92
-rw-r--r--src/gui/accessible/qaccessibleobject.cpp410
-rw-r--r--src/gui/accessible/qaccessibleobject.h140
-rw-r--r--src/gui/accessible/qaccessibleplugin.cpp107
-rw-r--r--src/gui/accessible/qaccessibleplugin.h87
-rw-r--r--src/gui/accessible/qaccessiblewidget.cpp1041
-rw-r--r--src/gui/accessible/qaccessiblewidget.h141
-rw-r--r--src/gui/dialogs/dialogs.pri97
-rw-r--r--src/gui/dialogs/images/fit-page-24.pngbin0 -> 985 bytes
-rw-r--r--src/gui/dialogs/images/fit-page-32.pngbin0 -> 1330 bytes
-rw-r--r--src/gui/dialogs/images/fit-width-24.pngbin0 -> 706 bytes
-rw-r--r--src/gui/dialogs/images/fit-width-32.pngbin0 -> 1004 bytes
-rw-r--r--src/gui/dialogs/images/go-first-24.pngbin0 -> 796 bytes
-rw-r--r--src/gui/dialogs/images/go-first-32.pngbin0 -> 985 bytes
-rw-r--r--src/gui/dialogs/images/go-last-24.pngbin0 -> 792 bytes
-rw-r--r--src/gui/dialogs/images/go-last-32.pngbin0 -> 984 bytes
-rw-r--r--src/gui/dialogs/images/go-next-24.pngbin0 -> 782 bytes
-rw-r--r--src/gui/dialogs/images/go-next-32.pngbin0 -> 948 bytes
-rw-r--r--src/gui/dialogs/images/go-previous-24.pngbin0 -> 797 bytes
-rw-r--r--src/gui/dialogs/images/go-previous-32.pngbin0 -> 945 bytes
-rw-r--r--src/gui/dialogs/images/layout-landscape-24.pngbin0 -> 820 bytes
-rw-r--r--src/gui/dialogs/images/layout-landscape-32.pngbin0 -> 1353 bytes
-rw-r--r--src/gui/dialogs/images/layout-portrait-24.pngbin0 -> 817 bytes
-rw-r--r--src/gui/dialogs/images/layout-portrait-32.pngbin0 -> 1330 bytes
-rw-r--r--src/gui/dialogs/images/page-setup-24.pngbin0 -> 620 bytes
-rw-r--r--src/gui/dialogs/images/page-setup-32.pngbin0 -> 1154 bytes
-rw-r--r--src/gui/dialogs/images/print-24.pngbin0 -> 914 bytes
-rw-r--r--src/gui/dialogs/images/print-32.pngbin0 -> 1202 bytes
-rw-r--r--src/gui/dialogs/images/qtlogo-64.pngbin0 -> 2991 bytes
-rw-r--r--src/gui/dialogs/images/status-color.pngbin0 -> 1475 bytes
-rw-r--r--src/gui/dialogs/images/status-gray-scale.pngbin0 -> 1254 bytes
-rw-r--r--src/gui/dialogs/images/view-page-multi-24.pngbin0 -> 390 bytes
-rw-r--r--src/gui/dialogs/images/view-page-multi-32.pngbin0 -> 556 bytes
-rw-r--r--src/gui/dialogs/images/view-page-one-24.pngbin0 -> 662 bytes
-rw-r--r--src/gui/dialogs/images/view-page-one-32.pngbin0 -> 810 bytes
-rw-r--r--src/gui/dialogs/images/view-page-sided-24.pngbin0 -> 700 bytes
-rw-r--r--src/gui/dialogs/images/view-page-sided-32.pngbin0 -> 908 bytes
-rw-r--r--src/gui/dialogs/images/zoom-in-24.pngbin0 -> 1302 bytes
-rw-r--r--src/gui/dialogs/images/zoom-in-32.pngbin0 -> 1873 bytes
-rw-r--r--src/gui/dialogs/images/zoom-out-24.pngbin0 -> 1247 bytes
-rw-r--r--src/gui/dialogs/images/zoom-out-32.pngbin0 -> 1749 bytes
-rw-r--r--src/gui/dialogs/qabstractpagesetupdialog.cpp139
-rw-r--r--src/gui/dialogs/qabstractpagesetupdialog.h82
-rw-r--r--src/gui/dialogs/qabstractpagesetupdialog_p.h88
-rw-r--r--src/gui/dialogs/qabstractprintdialog.cpp500
-rw-r--r--src/gui/dialogs/qabstractprintdialog.h127
-rw-r--r--src/gui/dialogs/qabstractprintdialog_p.h95
-rw-r--r--src/gui/dialogs/qcolordialog.cpp1903
-rw-r--r--src/gui/dialogs/qcolordialog.h150
-rw-r--r--src/gui/dialogs/qcolordialog_mac.mm426
-rw-r--r--src/gui/dialogs/qcolordialog_p.h144
-rw-r--r--src/gui/dialogs/qdialog.cpp1159
-rw-r--r--src/gui/dialogs/qdialog.h136
-rw-r--r--src/gui/dialogs/qdialog_p.h113
-rw-r--r--src/gui/dialogs/qdialogsbinarycompat_win.cpp137
-rw-r--r--src/gui/dialogs/qerrormessage.cpp403
-rw-r--r--src/gui/dialogs/qerrormessage.h88
-rw-r--r--src/gui/dialogs/qfiledialog.cpp3299
-rw-r--r--src/gui/dialogs/qfiledialog.h330
-rw-r--r--src/gui/dialogs/qfiledialog.ui320
-rw-r--r--src/gui/dialogs/qfiledialog_mac.mm1113
-rw-r--r--src/gui/dialogs/qfiledialog_p.h458
-rw-r--r--src/gui/dialogs/qfiledialog_win.cpp821
-rw-r--r--src/gui/dialogs/qfiledialog_wince.ui342
-rw-r--r--src/gui/dialogs/qfileinfogatherer.cpp365
-rw-r--r--src/gui/dialogs/qfileinfogatherer_p.h210
-rw-r--r--src/gui/dialogs/qfilesystemmodel.cpp1911
-rw-r--r--src/gui/dialogs/qfilesystemmodel.h179
-rw-r--r--src/gui/dialogs/qfilesystemmodel_p.h305
-rw-r--r--src/gui/dialogs/qfontdialog.cpp1070
-rw-r--r--src/gui/dialogs/qfontdialog.h144
-rw-r--r--src/gui/dialogs/qfontdialog_mac.mm625
-rw-r--r--src/gui/dialogs/qfontdialog_p.h164
-rw-r--r--src/gui/dialogs/qinputdialog.cpp1429
-rw-r--r--src/gui/dialogs/qinputdialog.h237
-rw-r--r--src/gui/dialogs/qmessagebox.cpp2688
-rw-r--r--src/gui/dialogs/qmessagebox.h363
-rw-r--r--src/gui/dialogs/qmessagebox.qrc5
-rw-r--r--src/gui/dialogs/qnspanelproxy_mac.mm246
-rw-r--r--src/gui/dialogs/qpagesetupdialog.cpp185
-rw-r--r--src/gui/dialogs/qpagesetupdialog.h112
-rw-r--r--src/gui/dialogs/qpagesetupdialog_mac.mm313
-rw-r--r--src/gui/dialogs/qpagesetupdialog_unix.cpp620
-rw-r--r--src/gui/dialogs/qpagesetupdialog_unix_p.h105
-rw-r--r--src/gui/dialogs/qpagesetupdialog_win.cpp169
-rw-r--r--src/gui/dialogs/qpagesetupwidget.ui353
-rw-r--r--src/gui/dialogs/qprintdialog.h174
-rw-r--r--src/gui/dialogs/qprintdialog.qrc38
-rw-r--r--src/gui/dialogs/qprintdialog_mac.mm428
-rw-r--r--src/gui/dialogs/qprintdialog_qws.cpp556
-rw-r--r--src/gui/dialogs/qprintdialog_unix.cpp1266
-rw-r--r--src/gui/dialogs/qprintdialog_win.cpp318
-rw-r--r--src/gui/dialogs/qprintpreviewdialog.cpp793
-rw-r--r--src/gui/dialogs/qprintpreviewdialog.h107
-rw-r--r--src/gui/dialogs/qprintpropertieswidget.ui70
-rw-r--r--src/gui/dialogs/qprintsettingsoutput.ui371
-rw-r--r--src/gui/dialogs/qprintwidget.ui116
-rw-r--r--src/gui/dialogs/qprogressdialog.cpp865
-rw-r--r--src/gui/dialogs/qprogressdialog.h145
-rw-r--r--src/gui/dialogs/qsidebar.cpp485
-rw-r--r--src/gui/dialogs/qsidebar_p.h147
-rw-r--r--src/gui/dialogs/qwizard.cpp3765
-rw-r--r--src/gui/dialogs/qwizard.h262
-rw-r--r--src/gui/dialogs/qwizard_win.cpp739
-rw-r--r--src/gui/dialogs/qwizard_win_p.h148
-rw-r--r--src/gui/embedded/embedded.pri226
-rw-r--r--src/gui/embedded/qcopchannel_qws.cpp608
-rw-r--r--src/gui/embedded/qcopchannel_qws.h108
-rw-r--r--src/gui/embedded/qdecoration_qws.cpp404
-rw-r--r--src/gui/embedded/qdecoration_qws.h124
-rw-r--r--src/gui/embedded/qdecorationdefault_qws.cpp803
-rw-r--r--src/gui/embedded/qdecorationdefault_qws.h101
-rw-r--r--src/gui/embedded/qdecorationfactory_qws.cpp156
-rw-r--r--src/gui/embedded/qdecorationfactory_qws.h66
-rw-r--r--src/gui/embedded/qdecorationplugin_qws.cpp116
-rw-r--r--src/gui/embedded/qdecorationplugin_qws.h80
-rw-r--r--src/gui/embedded/qdecorationstyled_qws.cpp313
-rw-r--r--src/gui/embedded/qdecorationstyled_qws.h73
-rw-r--r--src/gui/embedded/qdecorationwindows_qws.cpp407
-rw-r--r--src/gui/embedded/qdecorationwindows_qws.h77
-rw-r--r--src/gui/embedded/qdirectpainter_qws.cpp682
-rw-r--r--src/gui/embedded/qdirectpainter_qws.h112
-rw-r--r--src/gui/embedded/qkbd_qws.cpp248
-rw-r--r--src/gui/embedded/qkbd_qws.h81
-rw-r--r--src/gui/embedded/qkbddriverfactory_qws.cpp193
-rw-r--r--src/gui/embedded/qkbddriverfactory_qws.h70
-rw-r--r--src/gui/embedded/qkbddriverplugin_qws.cpp124
-rw-r--r--src/gui/embedded/qkbddriverplugin_qws.h84
-rw-r--r--src/gui/embedded/qkbdpc101_qws.cpp485
-rw-r--r--src/gui/embedded/qkbdpc101_qws.h95
-rw-r--r--src/gui/embedded/qkbdsl5000_qws.cpp356
-rw-r--r--src/gui/embedded/qkbdsl5000_qws.h79
-rw-r--r--src/gui/embedded/qkbdtty_qws.cpp263
-rw-r--r--src/gui/embedded/qkbdtty_qws.h81
-rw-r--r--src/gui/embedded/qkbdum_qws.cpp143
-rw-r--r--src/gui/embedded/qkbdum_qws.h77
-rw-r--r--src/gui/embedded/qkbdusb_qws.cpp401
-rw-r--r--src/gui/embedded/qkbdusb_qws.h77
-rw-r--r--src/gui/embedded/qkbdvfb_qws.cpp123
-rw-r--r--src/gui/embedded/qkbdvfb_qws.h86
-rw-r--r--src/gui/embedded/qkbdvr41xx_qws.cpp185
-rw-r--r--src/gui/embedded/qkbdvr41xx_qws.h73
-rw-r--r--src/gui/embedded/qkbdyopy_qws.cpp209
-rw-r--r--src/gui/embedded/qkbdyopy_qws.h73
-rw-r--r--src/gui/embedded/qlock.cpp318
-rw-r--r--src/gui/embedded/qlock_p.h100
-rw-r--r--src/gui/embedded/qmouse_qws.cpp653
-rw-r--r--src/gui/embedded/qmouse_qws.h123
-rw-r--r--src/gui/embedded/qmousebus_qws.cpp238
-rw-r--r--src/gui/embedded/qmousebus_qws.h76
-rw-r--r--src/gui/embedded/qmousedriverfactory_qws.cpp195
-rw-r--r--src/gui/embedded/qmousedriverfactory_qws.h67
-rw-r--r--src/gui/embedded/qmousedriverplugin_qws.cpp124
-rw-r--r--src/gui/embedded/qmousedriverplugin_qws.h84
-rw-r--r--src/gui/embedded/qmouselinuxtp_qws.cpp334
-rw-r--r--src/gui/embedded/qmouselinuxtp_qws.h77
-rw-r--r--src/gui/embedded/qmousepc_qws.cpp793
-rw-r--r--src/gui/embedded/qmousepc_qws.h76
-rw-r--r--src/gui/embedded/qmousetslib_qws.cpp371
-rw-r--r--src/gui/embedded/qmousetslib_qws.h80
-rw-r--r--src/gui/embedded/qmousevfb_qws.cpp132
-rw-r--r--src/gui/embedded/qmousevfb_qws.h83
-rw-r--r--src/gui/embedded/qmousevr41xx_qws.cpp250
-rw-r--r--src/gui/embedded/qmousevr41xx_qws.h80
-rw-r--r--src/gui/embedded/qmouseyopy_qws.cpp184
-rw-r--r--src/gui/embedded/qmouseyopy_qws.h80
-rw-r--r--src/gui/embedded/qscreen_qws.cpp3317
-rw-r--r--src/gui/embedded/qscreen_qws.h387
-rw-r--r--src/gui/embedded/qscreendriverfactory_qws.cpp183
-rw-r--r--src/gui/embedded/qscreendriverfactory_qws.h67
-rw-r--r--src/gui/embedded/qscreendriverplugin_qws.cpp123
-rw-r--r--src/gui/embedded/qscreendriverplugin_qws.h84
-rw-r--r--src/gui/embedded/qscreenlinuxfb_qws.cpp1324
-rw-r--r--src/gui/embedded/qscreenlinuxfb_qws.h129
-rw-r--r--src/gui/embedded/qscreenmulti_qws.cpp482
-rw-r--r--src/gui/embedded/qscreenmulti_qws_p.h114
-rw-r--r--src/gui/embedded/qscreenproxy_qws.cpp631
-rw-r--r--src/gui/embedded/qscreenproxy_qws.h153
-rw-r--r--src/gui/embedded/qscreentransformed_qws.cpp734
-rw-r--r--src/gui/embedded/qscreentransformed_qws.h103
-rw-r--r--src/gui/embedded/qscreenvfb_qws.cpp444
-rw-r--r--src/gui/embedded/qscreenvfb_qws.h86
-rw-r--r--src/gui/embedded/qsoundqss_qws.cpp1498
-rw-r--r--src/gui/embedded/qsoundqss_qws.h177
-rw-r--r--src/gui/embedded/qtransportauth_qws.cpp1562
-rw-r--r--src/gui/embedded/qtransportauth_qws.h281
-rw-r--r--src/gui/embedded/qtransportauth_qws_p.h189
-rw-r--r--src/gui/embedded/qtransportauthdefs_qws.h174
-rw-r--r--src/gui/embedded/qunixsocket.cpp1794
-rw-r--r--src/gui/embedded/qunixsocket_p.h202
-rw-r--r--src/gui/embedded/qunixsocketserver.cpp376
-rw-r--r--src/gui/embedded/qunixsocketserver_p.h98
-rw-r--r--src/gui/embedded/qvfbhdr.h89
-rw-r--r--src/gui/embedded/qwindowsystem_p.h315
-rw-r--r--src/gui/embedded/qwindowsystem_qws.cpp4947
-rw-r--r--src/gui/embedded/qwindowsystem_qws.h508
-rw-r--r--src/gui/embedded/qwscommand_qws.cpp610
-rw-r--r--src/gui/embedded/qwscommand_qws_p.h853
-rw-r--r--src/gui/embedded/qwscursor_qws.cpp654
-rw-r--r--src/gui/embedded/qwscursor_qws.h83
-rw-r--r--src/gui/embedded/qwsdisplay_qws.h185
-rw-r--r--src/gui/embedded/qwsdisplay_qws_p.h161
-rw-r--r--src/gui/embedded/qwsembedwidget.cpp227
-rw-r--r--src/gui/embedded/qwsembedwidget.h82
-rw-r--r--src/gui/embedded/qwsevent_qws.cpp216
-rw-r--r--src/gui/embedded/qwsevent_qws.h459
-rw-r--r--src/gui/embedded/qwslock.cpp243
-rw-r--r--src/gui/embedded/qwslock_p.h85
-rw-r--r--src/gui/embedded/qwsmanager_p.h122
-rw-r--r--src/gui/embedded/qwsmanager_qws.cpp535
-rw-r--r--src/gui/embedded/qwsmanager_qws.h122
-rw-r--r--src/gui/embedded/qwsproperty_qws.cpp145
-rw-r--r--src/gui/embedded/qwsproperty_qws.h96
-rw-r--r--src/gui/embedded/qwsprotocolitem_qws.h100
-rw-r--r--src/gui/embedded/qwssharedmemory.cpp185
-rw-r--r--src/gui/embedded/qwssharedmemory_p.h105
-rw-r--r--src/gui/embedded/qwssignalhandler.cpp134
-rw-r--r--src/gui/embedded/qwssignalhandler_p.h99
-rw-r--r--src/gui/embedded/qwssocket_qws.cpp280
-rw-r--r--src/gui/embedded/qwssocket_qws.h120
-rw-r--r--src/gui/embedded/qwsutils_qws.h98
-rw-r--r--src/gui/graphicsview/graphicsview.pri46
-rw-r--r--src/gui/graphicsview/qgraphicsgridlayout.cpp651
-rw-r--r--src/gui/graphicsview/qgraphicsgridlayout.h143
-rw-r--r--src/gui/graphicsview/qgraphicsitem.cpp8803
-rw-r--r--src/gui/graphicsview/qgraphicsitem.h1016
-rw-r--r--src/gui/graphicsview/qgraphicsitem_p.h281
-rw-r--r--src/gui/graphicsview/qgraphicsitemanimation.cpp599
-rw-r--r--src/gui/graphicsview/qgraphicsitemanimation.h120
-rw-r--r--src/gui/graphicsview/qgraphicslayout.cpp423
-rw-r--r--src/gui/graphicsview/qgraphicslayout.h95
-rw-r--r--src/gui/graphicsview/qgraphicslayout_p.cpp198
-rw-r--r--src/gui/graphicsview/qgraphicslayout_p.h103
-rw-r--r--src/gui/graphicsview/qgraphicslayoutitem.cpp852
-rw-r--r--src/gui/graphicsview/qgraphicslayoutitem.h152
-rw-r--r--src/gui/graphicsview/qgraphicslayoutitem_p.h91
-rw-r--r--src/gui/graphicsview/qgraphicslinearlayout.cpp547
-rw-r--r--src/gui/graphicsview/qgraphicslinearlayout.h121
-rw-r--r--src/gui/graphicsview/qgraphicsproxywidget.cpp1494
-rw-r--r--src/gui/graphicsview/qgraphicsproxywidget.h145
-rw-r--r--src/gui/graphicsview/qgraphicsproxywidget_p.h125
-rw-r--r--src/gui/graphicsview/qgraphicsscene.cpp5360
-rw-r--r--src/gui/graphicsview/qgraphicsscene.h301
-rw-r--r--src/gui/graphicsview/qgraphicsscene_bsp.cpp328
-rw-r--r--src/gui/graphicsview/qgraphicsscene_bsp_p.h137
-rw-r--r--src/gui/graphicsview/qgraphicsscene_p.h265
-rw-r--r--src/gui/graphicsview/qgraphicssceneevent.cpp1678
-rw-r--r--src/gui/graphicsview/qgraphicssceneevent.h311
-rw-r--r--src/gui/graphicsview/qgraphicsview.cpp3887
-rw-r--r--src/gui/graphicsview/qgraphicsview.h314
-rw-r--r--src/gui/graphicsview/qgraphicsview_p.h191
-rw-r--r--src/gui/graphicsview/qgraphicswidget.cpp2273
-rw-r--r--src/gui/graphicsview/qgraphicswidget.h249
-rw-r--r--src/gui/graphicsview/qgraphicswidget_p.cpp740
-rw-r--r--src/gui/graphicsview/qgraphicswidget_p.h232
-rw-r--r--src/gui/graphicsview/qgridlayoutengine.cpp1542
-rw-r--r--src/gui/graphicsview/qgridlayoutengine_p.h449
-rw-r--r--src/gui/gui.pro44
-rw-r--r--src/gui/image/image.pri108
-rw-r--r--src/gui/image/qbitmap.cpp403
-rw-r--r--src/gui/image/qbitmap.h106
-rw-r--r--src/gui/image/qbmphandler.cpp833
-rw-r--r--src/gui/image/qbmphandler_p.h117
-rw-r--r--src/gui/image/qicon.cpp1128
-rw-r--r--src/gui/image/qicon.h144
-rw-r--r--src/gui/image/qiconengine.cpp304
-rw-r--r--src/gui/image/qiconengine.h101
-rw-r--r--src/gui/image/qiconengineplugin.cpp171
-rw-r--r--src/gui/image/qiconengineplugin.h104
-rw-r--r--src/gui/image/qimage.cpp6119
-rw-r--r--src/gui/image/qimage.h352
-rw-r--r--src/gui/image/qimage_p.h110
-rw-r--r--src/gui/image/qimageiohandler.cpp571
-rw-r--r--src/gui/image/qimageiohandler.h151
-rw-r--r--src/gui/image/qimagereader.cpp1376
-rw-r--r--src/gui/image/qimagereader.h144
-rw-r--r--src/gui/image/qimagewriter.cpp690
-rw-r--r--src/gui/image/qimagewriter.h116
-rw-r--r--src/gui/image/qmovie.cpp1081
-rw-r--r--src/gui/image/qmovie.h177
-rw-r--r--src/gui/image/qnativeimage.cpp279
-rw-r--r--src/gui/image/qnativeimage_p.h110
-rw-r--r--src/gui/image/qpaintengine_pic.cpp519
-rw-r--r--src/gui/image/qpaintengine_pic_p.h120
-rw-r--r--src/gui/image/qpicture.cpp1968
-rw-r--r--src/gui/image/qpicture.h196
-rw-r--r--src/gui/image/qpicture_p.h170
-rw-r--r--src/gui/image/qpictureformatplugin.cpp139
-rw-r--r--src/gui/image/qpictureformatplugin.h94
-rw-r--r--src/gui/image/qpixmap.cpp2003
-rw-r--r--src/gui/image/qpixmap.h304
-rw-r--r--src/gui/image/qpixmap_mac.cpp1331
-rw-r--r--src/gui/image/qpixmap_mac_p.h136
-rw-r--r--src/gui/image/qpixmap_qws.cpp164
-rw-r--r--src/gui/image/qpixmap_raster.cpp350
-rw-r--r--src/gui/image/qpixmap_raster_p.h105
-rw-r--r--src/gui/image/qpixmap_win.cpp481
-rw-r--r--src/gui/image/qpixmap_x11.cpp2291
-rw-r--r--src/gui/image/qpixmap_x11_p.h131
-rw-r--r--src/gui/image/qpixmapcache.cpp320
-rw-r--r--src/gui/image/qpixmapcache.h69
-rw-r--r--src/gui/image/qpixmapdata.cpp179
-rw-r--r--src/gui/image/qpixmapdata_p.h140
-rw-r--r--src/gui/image/qpixmapdatafactory.cpp105
-rw-r--r--src/gui/image/qpixmapdatafactory_p.h81
-rw-r--r--src/gui/image/qpixmapfilter.cpp849
-rw-r--r--src/gui/image/qpixmapfilter_p.h165
-rw-r--r--src/gui/image/qpnghandler.cpp973
-rw-r--r--src/gui/image/qpnghandler_p.h88
-rw-r--r--src/gui/image/qppmhandler.cpp531
-rw-r--r--src/gui/image/qppmhandler_p.h98
-rw-r--r--src/gui/image/qxbmhandler.cpp350
-rw-r--r--src/gui/image/qxbmhandler_p.h95
-rw-r--r--src/gui/image/qxpmhandler.cpp1309
-rw-r--r--src/gui/image/qxpmhandler_p.h100
-rw-r--r--src/gui/inputmethod/inputmethod.pri26
-rw-r--r--src/gui/inputmethod/qinputcontext.cpp464
-rw-r--r--src/gui/inputmethod/qinputcontext.h134
-rw-r--r--src/gui/inputmethod/qinputcontext_p.h98
-rw-r--r--src/gui/inputmethod/qinputcontextfactory.cpp287
-rw-r--r--src/gui/inputmethod/qinputcontextfactory.h88
-rw-r--r--src/gui/inputmethod/qinputcontextplugin.cpp178
-rw-r--r--src/gui/inputmethod/qinputcontextplugin.h106
-rw-r--r--src/gui/inputmethod/qmacinputcontext_mac.cpp349
-rw-r--r--src/gui/inputmethod/qmacinputcontext_p.h92
-rw-r--r--src/gui/inputmethod/qwininputcontext_p.h96
-rw-r--r--src/gui/inputmethod/qwininputcontext_win.cpp861
-rw-r--r--src/gui/inputmethod/qwsinputcontext_p.h96
-rw-r--r--src/gui/inputmethod/qwsinputcontext_qws.cpp239
-rw-r--r--src/gui/inputmethod/qximinputcontext_p.h141
-rw-r--r--src/gui/inputmethod/qximinputcontext_x11.cpp832
-rw-r--r--src/gui/itemviews/itemviews.pri70
-rw-r--r--src/gui/itemviews/qabstractitemdelegate.cpp387
-rw-r--r--src/gui/itemviews/qabstractitemdelegate.h134
-rw-r--r--src/gui/itemviews/qabstractitemview.cpp3918
-rw-r--r--src/gui/itemviews/qabstractitemview.h370
-rw-r--r--src/gui/itemviews/qabstractitemview_p.h410
-rw-r--r--src/gui/itemviews/qabstractproxymodel.cpp282
-rw-r--r--src/gui/itemviews/qabstractproxymodel.h101
-rw-r--r--src/gui/itemviews/qabstractproxymodel_p.h76
-rw-r--r--src/gui/itemviews/qbsptree.cpp145
-rw-r--r--src/gui/itemviews/qbsptree_p.h119
-rw-r--r--src/gui/itemviews/qcolumnview.cpp1128
-rw-r--r--src/gui/itemviews/qcolumnview.h125
-rw-r--r--src/gui/itemviews/qcolumnview_p.h184
-rw-r--r--src/gui/itemviews/qcolumnviewgrip.cpp194
-rw-r--r--src/gui/itemviews/qcolumnviewgrip_p.h104
-rw-r--r--src/gui/itemviews/qdatawidgetmapper.cpp849
-rw-r--r--src/gui/itemviews/qdatawidgetmapper.h128
-rw-r--r--src/gui/itemviews/qdirmodel.cpp1410
-rw-r--r--src/gui/itemviews/qdirmodel.h160
-rw-r--r--src/gui/itemviews/qfileiconprovider.cpp449
-rw-r--r--src/gui/itemviews/qfileiconprovider.h81
-rw-r--r--src/gui/itemviews/qheaderview.cpp3558
-rw-r--r--src/gui/itemviews/qheaderview.h251
-rw-r--r--src/gui/itemviews/qheaderview_p.h370
-rw-r--r--src/gui/itemviews/qitemdelegate.cpp1337
-rw-r--r--src/gui/itemviews/qitemdelegate.h141
-rw-r--r--src/gui/itemviews/qitemeditorfactory.cpp566
-rw-r--r--src/gui/itemviews/qitemeditorfactory.h124
-rw-r--r--src/gui/itemviews/qitemeditorfactory_p.h99
-rw-r--r--src/gui/itemviews/qitemselectionmodel.cpp1570
-rw-r--r--src/gui/itemviews/qitemselectionmodel.h229
-rw-r--r--src/gui/itemviews/qitemselectionmodel_p.h111
-rw-r--r--src/gui/itemviews/qlistview.cpp3001
-rw-r--r--src/gui/itemviews/qlistview.h203
-rw-r--r--src/gui/itemviews/qlistview_p.h450
-rw-r--r--src/gui/itemviews/qlistwidget.cpp1865
-rw-r--r--src/gui/itemviews/qlistwidget.h335
-rw-r--r--src/gui/itemviews/qlistwidget_p.h175
-rw-r--r--src/gui/itemviews/qproxymodel.cpp547
-rw-r--r--src/gui/itemviews/qproxymodel.h142
-rw-r--r--src/gui/itemviews/qproxymodel_p.h100
-rw-r--r--src/gui/itemviews/qsortfilterproxymodel.cpp2392
-rw-r--r--src/gui/itemviews/qsortfilterproxymodel.h199
-rw-r--r--src/gui/itemviews/qstandarditemmodel.cpp3108
-rw-r--r--src/gui/itemviews/qstandarditemmodel.h456
-rw-r--r--src/gui/itemviews/qstandarditemmodel_p.h189
-rw-r--r--src/gui/itemviews/qstringlistmodel.cpp307
-rw-r--r--src/gui/itemviews/qstringlistmodel.h91
-rw-r--r--src/gui/itemviews/qstyleditemdelegate.cpp763
-rw-r--r--src/gui/itemviews/qstyleditemdelegate.h116
-rw-r--r--src/gui/itemviews/qtableview.cpp2515
-rw-r--r--src/gui/itemviews/qtableview.h193
-rw-r--r--src/gui/itemviews/qtableview_p.h210
-rw-r--r--src/gui/itemviews/qtablewidget.cpp2703
-rw-r--r--src/gui/itemviews/qtablewidget.h377
-rw-r--r--src/gui/itemviews/qtablewidget_p.h223
-rw-r--r--src/gui/itemviews/qtreeview.cpp3851
-rw-r--r--src/gui/itemviews/qtreeview.h241
-rw-r--r--src/gui/itemviews/qtreeview_p.h240
-rw-r--r--src/gui/itemviews/qtreewidget.cpp3437
-rw-r--r--src/gui/itemviews/qtreewidget.h432
-rw-r--r--src/gui/itemviews/qtreewidget_p.h249
-rw-r--r--src/gui/itemviews/qtreewidgetitemiterator.cpp459
-rw-r--r--src/gui/itemviews/qtreewidgetitemiterator.h158
-rw-r--r--src/gui/itemviews/qtreewidgetitemiterator_p.h109
-rw-r--r--src/gui/itemviews/qwidgetitemdata_p.h88
-rw-r--r--src/gui/kernel/kernel.pri209
-rw-r--r--src/gui/kernel/mac.pri4
-rw-r--r--src/gui/kernel/qaction.cpp1396
-rw-r--r--src/gui/kernel/qaction.h246
-rw-r--r--src/gui/kernel/qaction_p.h129
-rw-r--r--src/gui/kernel/qactiongroup.cpp416
-rw-r--r--src/gui/kernel/qactiongroup.h112
-rw-r--r--src/gui/kernel/qapplication.cpp5051
-rw-r--r--src/gui/kernel/qapplication.h391
-rw-r--r--src/gui/kernel/qapplication_mac.mm2976
-rw-r--r--src/gui/kernel/qapplication_p.h438
-rw-r--r--src/gui/kernel/qapplication_qws.cpp3817
-rw-r--r--src/gui/kernel/qapplication_win.cpp3956
-rw-r--r--src/gui/kernel/qapplication_x11.cpp5919
-rw-r--r--src/gui/kernel/qboxlayout.cpp1534
-rw-r--r--src/gui/kernel/qboxlayout.h173
-rw-r--r--src/gui/kernel/qclipboard.cpp651
-rw-r--r--src/gui/kernel/qclipboard.h130
-rw-r--r--src/gui/kernel/qclipboard_mac.cpp612
-rw-r--r--src/gui/kernel/qclipboard_p.h131
-rw-r--r--src/gui/kernel/qclipboard_qws.cpp304
-rw-r--r--src/gui/kernel/qclipboard_win.cpp389
-rw-r--r--src/gui/kernel/qclipboard_x11.cpp1498
-rw-r--r--src/gui/kernel/qcocoaapplication_mac.mm114
-rw-r--r--src/gui/kernel/qcocoaapplication_mac_p.h103
-rw-r--r--src/gui/kernel/qcocoaapplicationdelegate_mac.mm282
-rw-r--r--src/gui/kernel/qcocoaapplicationdelegate_mac_p.h119
-rw-r--r--src/gui/kernel/qcocoamenuloader_mac.mm215
-rw-r--r--src/gui/kernel/qcocoamenuloader_mac_p.h90
-rw-r--r--src/gui/kernel/qcocoapanel_mac.mm79
-rw-r--r--src/gui/kernel/qcocoapanel_mac_p.h65
-rw-r--r--src/gui/kernel/qcocoaview_mac.mm1254
-rw-r--r--src/gui/kernel/qcocoaview_mac_p.h109
-rw-r--r--src/gui/kernel/qcocoawindow_mac.mm185
-rw-r--r--src/gui/kernel/qcocoawindow_mac_p.h72
-rw-r--r--src/gui/kernel/qcocoawindowcustomthemeframe_mac.mm62
-rw-r--r--src/gui/kernel/qcocoawindowcustomthemeframe_mac_p.h61
-rw-r--r--src/gui/kernel/qcocoawindowdelegate_mac.mm349
-rw-r--r--src/gui/kernel/qcocoawindowdelegate_mac_p.h95
-rw-r--r--src/gui/kernel/qcursor.cpp565
-rw-r--r--src/gui/kernel/qcursor.h160
-rw-r--r--src/gui/kernel/qcursor_mac.mm556
-rw-r--r--src/gui/kernel/qcursor_p.h121
-rw-r--r--src/gui/kernel/qcursor_qws.cpp136
-rw-r--r--src/gui/kernel/qcursor_win.cpp493
-rw-r--r--src/gui/kernel/qcursor_x11.cpp594
-rw-r--r--src/gui/kernel/qdesktopwidget.h104
-rw-r--r--src/gui/kernel/qdesktopwidget_mac.mm244
-rw-r--r--src/gui/kernel/qdesktopwidget_mac_p.h74
-rw-r--r--src/gui/kernel/qdesktopwidget_qws.cpp159
-rw-r--r--src/gui/kernel/qdesktopwidget_win.cpp412
-rw-r--r--src/gui/kernel/qdesktopwidget_x11.cpp380
-rw-r--r--src/gui/kernel/qdnd.cpp697
-rw-r--r--src/gui/kernel/qdnd_mac.mm749
-rw-r--r--src/gui/kernel/qdnd_p.h333
-rw-r--r--src/gui/kernel/qdnd_qws.cpp422
-rw-r--r--src/gui/kernel/qdnd_win.cpp1036
-rw-r--r--src/gui/kernel/qdnd_x11.cpp2064
-rw-r--r--src/gui/kernel/qdrag.cpp357
-rw-r--r--src/gui/kernel/qdrag.h105
-rw-r--r--src/gui/kernel/qevent.cpp3509
-rw-r--r--src/gui/kernel/qevent.h726
-rw-r--r--src/gui/kernel/qevent_p.h94
-rw-r--r--src/gui/kernel/qeventdispatcher_glib_qws.cpp195
-rw-r--r--src/gui/kernel/qeventdispatcher_glib_qws_p.h78
-rw-r--r--src/gui/kernel/qeventdispatcher_mac.mm926
-rw-r--r--src/gui/kernel/qeventdispatcher_mac_p.h197
-rw-r--r--src/gui/kernel/qeventdispatcher_qws.cpp168
-rw-r--r--src/gui/kernel/qeventdispatcher_qws_p.h86
-rw-r--r--src/gui/kernel/qeventdispatcher_x11.cpp191
-rw-r--r--src/gui/kernel/qeventdispatcher_x11_p.h86
-rw-r--r--src/gui/kernel/qformlayout.cpp2080
-rw-r--r--src/gui/kernel/qformlayout.h163
-rw-r--r--src/gui/kernel/qgridlayout.cpp1889
-rw-r--r--src/gui/kernel/qgridlayout.h176
-rw-r--r--src/gui/kernel/qguieventdispatcher_glib.cpp217
-rw-r--r--src/gui/kernel/qguieventdispatcher_glib_p.h78
-rw-r--r--src/gui/kernel/qguifunctions_wince.cpp377
-rw-r--r--src/gui/kernel/qguifunctions_wince.h157
-rw-r--r--src/gui/kernel/qguivariant.cpp669
-rw-r--r--src/gui/kernel/qkeymapper.cpp121
-rw-r--r--src/gui/kernel/qkeymapper_mac.cpp931
-rw-r--r--src/gui/kernel/qkeymapper_p.h213
-rw-r--r--src/gui/kernel/qkeymapper_qws.cpp77
-rw-r--r--src/gui/kernel/qkeymapper_win.cpp1259
-rw-r--r--src/gui/kernel/qkeymapper_x11.cpp1678
-rw-r--r--src/gui/kernel/qkeymapper_x11_p.cpp488
-rw-r--r--src/gui/kernel/qkeysequence.cpp1469
-rw-r--r--src/gui/kernel/qkeysequence.h231
-rw-r--r--src/gui/kernel/qkeysequence_p.h98
-rw-r--r--src/gui/kernel/qlayout.cpp1585
-rw-r--r--src/gui/kernel/qlayout.h242
-rw-r--r--src/gui/kernel/qlayout_p.h101
-rw-r--r--src/gui/kernel/qlayoutengine.cpp436
-rw-r--r--src/gui/kernel/qlayoutengine_p.h140
-rw-r--r--src/gui/kernel/qlayoutitem.cpp837
-rw-r--r--src/gui/kernel/qlayoutitem.h182
-rw-r--r--src/gui/kernel/qmacdefines_mac.h192
-rw-r--r--src/gui/kernel/qmime.cpp97
-rw-r--r--src/gui/kernel/qmime.h176
-rw-r--r--src/gui/kernel/qmime_mac.cpp1181
-rw-r--r--src/gui/kernel/qmime_win.cpp1594
-rw-r--r--src/gui/kernel/qmotifdnd_x11.cpp1028
-rw-r--r--src/gui/kernel/qnsframeview_mac_p.h154
-rw-r--r--src/gui/kernel/qnsthemeframe_mac_p.h246
-rw-r--r--src/gui/kernel/qnstitledframe_mac_p.h205
-rw-r--r--src/gui/kernel/qole_win.cpp255
-rw-r--r--src/gui/kernel/qpalette.cpp1396
-rw-r--r--src/gui/kernel/qpalette.h262
-rw-r--r--src/gui/kernel/qsessionmanager.h111
-rw-r--r--src/gui/kernel/qsessionmanager_qws.cpp171
-rw-r--r--src/gui/kernel/qshortcut.cpp408
-rw-r--r--src/gui/kernel/qshortcut.h107
-rw-r--r--src/gui/kernel/qshortcutmap.cpp901
-rw-r--r--src/gui/kernel/qshortcutmap_p.h122
-rw-r--r--src/gui/kernel/qsizepolicy.h225
-rw-r--r--src/gui/kernel/qsound.cpp386
-rw-r--r--src/gui/kernel/qsound.h95
-rw-r--r--src/gui/kernel/qsound_mac.mm184
-rw-r--r--src/gui/kernel/qsound_p.h100
-rw-r--r--src/gui/kernel/qsound_qws.cpp350
-rw-r--r--src/gui/kernel/qsound_win.cpp219
-rw-r--r--src/gui/kernel/qsound_x11.cpp296
-rw-r--r--src/gui/kernel/qstackedlayout.cpp545
-rw-r--r--src/gui/kernel/qstackedlayout.h115
-rw-r--r--src/gui/kernel/qt_cocoa_helpers_mac.mm1096
-rw-r--r--src/gui/kernel/qt_cocoa_helpers_mac_p.h174
-rw-r--r--src/gui/kernel/qt_gui_pch.h85
-rw-r--r--src/gui/kernel/qt_mac.cpp144
-rw-r--r--src/gui/kernel/qt_mac_p.h265
-rw-r--r--src/gui/kernel/qt_x11_p.h723
-rw-r--r--src/gui/kernel/qtooltip.cpp609
-rw-r--r--src/gui/kernel/qtooltip.h84
-rw-r--r--src/gui/kernel/qwhatsthis.cpp772
-rw-r--r--src/gui/kernel/qwhatsthis.h88
-rw-r--r--src/gui/kernel/qwidget.cpp11398
-rw-r--r--src/gui/kernel/qwidget.h1045
-rw-r--r--src/gui/kernel/qwidget_mac.mm4842
-rw-r--r--src/gui/kernel/qwidget_p.h712
-rw-r--r--src/gui/kernel/qwidget_qws.cpp1198
-rw-r--r--src/gui/kernel/qwidget_win.cpp2124
-rw-r--r--src/gui/kernel/qwidget_wince.cpp707
-rw-r--r--src/gui/kernel/qwidget_x11.cpp2891
-rw-r--r--src/gui/kernel/qwidgetaction.cpp288
-rw-r--r--src/gui/kernel/qwidgetaction.h91
-rw-r--r--src/gui/kernel/qwidgetaction_p.h77
-rw-r--r--src/gui/kernel/qwidgetcreate_x11.cpp79
-rw-r--r--src/gui/kernel/qwindowdefs.h152
-rw-r--r--src/gui/kernel/qwindowdefs_win.h132
-rw-r--r--src/gui/kernel/qx11embed_x11.cpp1807
-rw-r--r--src/gui/kernel/qx11embed_x11.h132
-rw-r--r--src/gui/kernel/qx11info_x11.cpp542
-rw-r--r--src/gui/kernel/qx11info_x11.h123
-rw-r--r--src/gui/kernel/win.pri4
-rw-r--r--src/gui/kernel/x11.pri4
-rw-r--r--src/gui/mac/images/copyarrowcursor.pngbin0 -> 1976 bytes
-rw-r--r--src/gui/mac/images/forbiddencursor.pngbin0 -> 1745 bytes
-rw-r--r--src/gui/mac/images/pluscursor.pngbin0 -> 688 bytes
-rw-r--r--src/gui/mac/images/spincursor.pngbin0 -> 748 bytes
-rw-r--r--src/gui/mac/images/waitcursor.pngbin0 -> 724 bytes
-rw-r--r--src/gui/mac/maccursors.qrc9
-rw-r--r--src/gui/mac/qt_menu.nib/classes.nib59
-rw-r--r--src/gui/mac/qt_menu.nib/info.nib18
-rw-r--r--src/gui/mac/qt_menu.nib/keyedobjects.nibbin0 -> 5567 bytes
-rwxr-xr-xsrc/gui/painting/makepsheader.pl155
-rw-r--r--src/gui/painting/painting.pri369
-rw-r--r--src/gui/painting/qbackingstore.cpp1548
-rw-r--r--src/gui/painting/qbackingstore_p.h268
-rw-r--r--src/gui/painting/qbezier.cpp1245
-rw-r--r--src/gui/painting/qbezier_p.h280
-rw-r--r--src/gui/painting/qblendfunctions.cpp1419
-rw-r--r--src/gui/painting/qbrush.cpp2148
-rw-r--r--src/gui/painting/qbrush.h321
-rw-r--r--src/gui/painting/qcolor.cpp2244
-rw-r--r--src/gui/painting/qcolor.h275
-rw-r--r--src/gui/painting/qcolor_p.cpp390
-rw-r--r--src/gui/painting/qcolor_p.h71
-rw-r--r--src/gui/painting/qcolormap.h97
-rw-r--r--src/gui/painting/qcolormap_mac.cpp111
-rw-r--r--src/gui/painting/qcolormap_qws.cpp185
-rw-r--r--src/gui/painting/qcolormap_win.cpp197
-rw-r--r--src/gui/painting/qcolormap_x11.cpp674
-rw-r--r--src/gui/painting/qcssutil.cpp408
-rw-r--r--src/gui/painting/qcssutil_p.h84
-rw-r--r--src/gui/painting/qcups.cpp398
-rw-r--r--src/gui/painting/qcups_p.h120
-rw-r--r--src/gui/painting/qdatabuffer_p.h127
-rw-r--r--src/gui/painting/qdrawhelper.cpp8248
-rw-r--r--src/gui/painting/qdrawhelper_iwmmxt.cpp127
-rw-r--r--src/gui/painting/qdrawhelper_mmx.cpp134
-rw-r--r--src/gui/painting/qdrawhelper_mmx3dnow.cpp106
-rw-r--r--src/gui/painting/qdrawhelper_mmx_p.h893
-rw-r--r--src/gui/painting/qdrawhelper_p.h1910
-rw-r--r--src/gui/painting/qdrawhelper_sse.cpp147
-rw-r--r--src/gui/painting/qdrawhelper_sse2.cpp211
-rw-r--r--src/gui/painting/qdrawhelper_sse3dnow.cpp122
-rw-r--r--src/gui/painting/qdrawhelper_sse_p.h182
-rw-r--r--src/gui/painting/qdrawhelper_x86_p.h130
-rw-r--r--src/gui/painting/qdrawutil.cpp1041
-rw-r--r--src/gui/painting/qdrawutil.h140
-rw-r--r--src/gui/painting/qemulationpaintengine.cpp229
-rw-r--r--src/gui/painting/qemulationpaintengine_p.h107
-rw-r--r--src/gui/painting/qfixed_p.h219
-rw-r--r--src/gui/painting/qgraphicssystem.cpp78
-rw-r--r--src/gui/painting/qgraphicssystem_mac.cpp59
-rw-r--r--src/gui/painting/qgraphicssystem_mac_p.h69
-rw-r--r--src/gui/painting/qgraphicssystem_p.h78
-rw-r--r--src/gui/painting/qgraphicssystem_qws.cpp62
-rw-r--r--src/gui/painting/qgraphicssystem_qws_p.h79
-rw-r--r--src/gui/painting/qgraphicssystem_raster.cpp59
-rw-r--r--src/gui/painting/qgraphicssystem_raster_p.h69
-rw-r--r--src/gui/painting/qgraphicssystemfactory.cpp110
-rw-r--r--src/gui/painting/qgraphicssystemfactory_p.h78
-rw-r--r--src/gui/painting/qgraphicssystemplugin.cpp56
-rw-r--r--src/gui/painting/qgraphicssystemplugin_p.h92
-rw-r--r--src/gui/painting/qgrayraster.c1937
-rw-r--r--src/gui/painting/qgrayraster_p.h101
-rw-r--r--src/gui/painting/qimagescale.cpp1031
-rw-r--r--src/gui/painting/qimagescale_p.h66
-rw-r--r--src/gui/painting/qmath_p.h66
-rw-r--r--src/gui/painting/qmatrix.cpp1180
-rw-r--r--src/gui/painting/qmatrix.h175
-rw-r--r--src/gui/painting/qmemrotate.cpp547
-rw-r--r--src/gui/painting/qmemrotate_p.h103
-rw-r--r--src/gui/painting/qoutlinemapper.cpp412
-rw-r--r--src/gui/painting/qoutlinemapper_p.h238
-rw-r--r--src/gui/painting/qpaintdevice.h173
-rw-r--r--src/gui/painting/qpaintdevice_mac.cpp185
-rw-r--r--src/gui/painting/qpaintdevice_qws.cpp92
-rw-r--r--src/gui/painting/qpaintdevice_win.cpp88
-rw-r--r--src/gui/painting/qpaintdevice_x11.cpp435
-rw-r--r--src/gui/painting/qpaintengine.cpp1026
-rw-r--r--src/gui/painting/qpaintengine.h359
-rw-r--r--src/gui/painting/qpaintengine_alpha.cpp510
-rw-r--r--src/gui/painting/qpaintengine_alpha_p.h134
-rw-r--r--src/gui/painting/qpaintengine_d3d.cpp4576
-rw-r--r--src/gui/painting/qpaintengine_d3d.fx608
-rw-r--r--src/gui/painting/qpaintengine_d3d.qrc5
-rw-r--r--src/gui/painting/qpaintengine_d3d_p.h120
-rw-r--r--src/gui/painting/qpaintengine_mac.cpp1789
-rw-r--r--src/gui/painting/qpaintengine_mac_p.h359
-rw-r--r--src/gui/painting/qpaintengine_p.h125
-rw-r--r--src/gui/painting/qpaintengine_preview.cpp223
-rw-r--r--src/gui/painting/qpaintengine_preview_p.h106
-rw-r--r--src/gui/painting/qpaintengine_raster.cpp6058
-rw-r--r--src/gui/painting/qpaintengine_raster_p.h550
-rw-r--r--src/gui/painting/qpaintengine_x11.cpp2451
-rw-r--r--src/gui/painting/qpaintengine_x11_p.h245
-rw-r--r--src/gui/painting/qpaintengineex.cpp804
-rw-r--r--src/gui/painting/qpaintengineex_p.h240
-rw-r--r--src/gui/painting/qpainter.cpp8533
-rw-r--r--src/gui/painting/qpainter.h953
-rw-r--r--src/gui/painting/qpainter_p.h260
-rw-r--r--src/gui/painting/qpainterpath.cpp3309
-rw-r--r--src/gui/painting/qpainterpath.h409
-rw-r--r--src/gui/painting/qpainterpath_p.h211
-rw-r--r--src/gui/painting/qpathclipper.cpp2042
-rw-r--r--src/gui/painting/qpathclipper_p.h519
-rw-r--r--src/gui/painting/qpdf.cpp2087
-rw-r--r--src/gui/painting/qpdf_p.h303
-rw-r--r--src/gui/painting/qpen.cpp1005
-rw-r--r--src/gui/painting/qpen.h140
-rw-r--r--src/gui/painting/qpen_p.h78
-rw-r--r--src/gui/painting/qpolygon.cpp928
-rw-r--r--src/gui/painting/qpolygon.h173
-rw-r--r--src/gui/painting/qpolygonclipper_p.h316
-rw-r--r--src/gui/painting/qprintengine.h117
-rw-r--r--src/gui/painting/qprintengine_mac.mm926
-rw-r--r--src/gui/painting/qprintengine_mac_p.h165
-rw-r--r--src/gui/painting/qprintengine_pdf.cpp1225
-rw-r--r--src/gui/painting/qprintengine_pdf_p.h194
-rw-r--r--src/gui/painting/qprintengine_ps.cpp969
-rw-r--r--src/gui/painting/qprintengine_ps_p.h137
-rw-r--r--src/gui/painting/qprintengine_qws.cpp881
-rw-r--r--src/gui/painting/qprintengine_qws_p.h213
-rw-r--r--src/gui/painting/qprintengine_win.cpp1968
-rw-r--r--src/gui/painting/qprintengine_win_p.h272
-rw-r--r--src/gui/painting/qprinter.cpp2377
-rw-r--r--src/gui/painting/qprinter.h330
-rw-r--r--src/gui/painting/qprinter_p.h140
-rw-r--r--src/gui/painting/qprinterinfo.h88
-rw-r--r--src/gui/painting/qprinterinfo_mac.cpp241
-rw-r--r--src/gui/painting/qprinterinfo_unix.cpp1141
-rw-r--r--src/gui/painting/qprinterinfo_unix_p.h125
-rw-r--r--src/gui/painting/qprinterinfo_win.cpp279
-rw-r--r--src/gui/painting/qpsprinter.agl452
-rw-r--r--src/gui/painting/qpsprinter.ps449
-rw-r--r--src/gui/painting/qrasterdefs_p.h1280
-rw-r--r--src/gui/painting/qrasterizer.cpp1249
-rw-r--r--src/gui/painting/qrasterizer_p.h91
-rw-r--r--src/gui/painting/qregion.cpp4318
-rw-r--r--src/gui/painting/qregion.h231
-rw-r--r--src/gui/painting/qregion_mac.cpp249
-rw-r--r--src/gui/painting/qregion_qws.cpp3183
-rw-r--r--src/gui/painting/qregion_win.cpp576
-rw-r--r--src/gui/painting/qregion_wince.cpp119
-rw-r--r--src/gui/painting/qregion_x11.cpp92
-rw-r--r--src/gui/painting/qrgb.h88
-rw-r--r--src/gui/painting/qstroker.cpp1136
-rw-r--r--src/gui/painting/qstroker_p.h378
-rw-r--r--src/gui/painting/qstylepainter.cpp176
-rw-r--r--src/gui/painting/qstylepainter.h112
-rw-r--r--src/gui/painting/qtessellator.cpp1499
-rw-r--r--src/gui/painting/qtessellator_p.h102
-rw-r--r--src/gui/painting/qtextureglyphcache.cpp316
-rw-r--r--src/gui/painting/qtextureglyphcache_p.h141
-rw-r--r--src/gui/painting/qtransform.cpp2079
-rw-r--r--src/gui/painting/qtransform.h346
-rw-r--r--src/gui/painting/qvectorpath_p.h166
-rw-r--r--src/gui/painting/qwindowsurface.cpp349
-rw-r--r--src/gui/painting/qwindowsurface_d3d.cpp169
-rw-r--r--src/gui/painting/qwindowsurface_d3d_p.h84
-rw-r--r--src/gui/painting/qwindowsurface_mac.cpp137
-rw-r--r--src/gui/painting/qwindowsurface_mac_p.h84
-rw-r--r--src/gui/painting/qwindowsurface_p.h112
-rw-r--r--src/gui/painting/qwindowsurface_qws.cpp1411
-rw-r--r--src/gui/painting/qwindowsurface_qws_p.h353
-rw-r--r--src/gui/painting/qwindowsurface_raster.cpp413
-rw-r--r--src/gui/painting/qwindowsurface_raster_p.h120
-rw-r--r--src/gui/painting/qwindowsurface_x11.cpp244
-rw-r--r--src/gui/painting/qwindowsurface_x11_p.h90
-rw-r--r--src/gui/painting/qwmatrix.h61
-rw-r--r--src/gui/styles/gtksymbols.cpp904
-rw-r--r--src/gui/styles/gtksymbols_p.h335
-rw-r--r--src/gui/styles/images/cdr-128.pngbin0 -> 16418 bytes
-rw-r--r--src/gui/styles/images/cdr-16.pngbin0 -> 845 bytes
-rw-r--r--src/gui/styles/images/cdr-32.pngbin0 -> 2016 bytes
-rw-r--r--src/gui/styles/images/closedock-16.pngbin0 -> 516 bytes
-rw-r--r--src/gui/styles/images/closedock-down-16.pngbin0 -> 578 bytes
-rw-r--r--src/gui/styles/images/computer-16.pngbin0 -> 782 bytes
-rw-r--r--src/gui/styles/images/computer-32.pngbin0 -> 1807 bytes
-rw-r--r--src/gui/styles/images/desktop-16.pngbin0 -> 773 bytes
-rw-r--r--src/gui/styles/images/desktop-32.pngbin0 -> 1103 bytes
-rw-r--r--src/gui/styles/images/dirclosed-128.pngbin0 -> 1386 bytes
-rw-r--r--src/gui/styles/images/dirclosed-16.pngbin0 -> 231 bytes
-rw-r--r--src/gui/styles/images/dirclosed-32.pngbin0 -> 474 bytes
-rw-r--r--src/gui/styles/images/dirlink-128.pngbin0 -> 5155 bytes
-rw-r--r--src/gui/styles/images/dirlink-16.pngbin0 -> 416 bytes
-rw-r--r--src/gui/styles/images/dirlink-32.pngbin0 -> 1046 bytes
-rw-r--r--src/gui/styles/images/diropen-128.pngbin0 -> 2075 bytes
-rw-r--r--src/gui/styles/images/diropen-16.pngbin0 -> 248 bytes
-rw-r--r--src/gui/styles/images/diropen-32.pngbin0 -> 633 bytes
-rw-r--r--src/gui/styles/images/dockdock-16.pngbin0 -> 438 bytes
-rw-r--r--src/gui/styles/images/dockdock-down-16.pngbin0 -> 406 bytes
-rw-r--r--src/gui/styles/images/down-128.pngbin0 -> 9550 bytes
-rw-r--r--src/gui/styles/images/down-16.pngbin0 -> 817 bytes
-rw-r--r--src/gui/styles/images/down-32.pngbin0 -> 1820 bytes
-rw-r--r--src/gui/styles/images/dvd-128.pngbin0 -> 14941 bytes
-rw-r--r--src/gui/styles/images/dvd-16.pngbin0 -> 892 bytes
-rw-r--r--src/gui/styles/images/dvd-32.pngbin0 -> 2205 bytes
-rw-r--r--src/gui/styles/images/file-128.pngbin0 -> 3997 bytes
-rw-r--r--src/gui/styles/images/file-16.pngbin0 -> 423 bytes
-rw-r--r--src/gui/styles/images/file-32.pngbin0 -> 713 bytes
-rw-r--r--src/gui/styles/images/filecontents-128.pngbin0 -> 8109 bytes
-rw-r--r--src/gui/styles/images/filecontents-16.pngbin0 -> 766 bytes
-rw-r--r--src/gui/styles/images/filecontents-32.pngbin0 -> 1712 bytes
-rw-r--r--src/gui/styles/images/fileinfo-128.pngbin0 -> 12002 bytes
-rw-r--r--src/gui/styles/images/fileinfo-16.pngbin0 -> 849 bytes
-rw-r--r--src/gui/styles/images/fileinfo-32.pngbin0 -> 2010 bytes
-rw-r--r--src/gui/styles/images/filelink-128.pngbin0 -> 5601 bytes
-rw-r--r--src/gui/styles/images/filelink-16.pngbin0 -> 566 bytes
-rw-r--r--src/gui/styles/images/filelink-32.pngbin0 -> 1192 bytes
-rw-r--r--src/gui/styles/images/floppy-128.pngbin0 -> 5074 bytes
-rw-r--r--src/gui/styles/images/floppy-16.pngbin0 -> 602 bytes
-rw-r--r--src/gui/styles/images/floppy-32.pngbin0 -> 1019 bytes
-rw-r--r--src/gui/styles/images/fontbitmap-16.pngbin0 -> 537 bytes
-rw-r--r--src/gui/styles/images/fonttruetype-16.pngbin0 -> 442 bytes
-rw-r--r--src/gui/styles/images/harddrive-128.pngbin0 -> 11250 bytes
-rw-r--r--src/gui/styles/images/harddrive-16.pngbin0 -> 802 bytes
-rw-r--r--src/gui/styles/images/harddrive-32.pngbin0 -> 1751 bytes
-rw-r--r--src/gui/styles/images/left-128.pngbin0 -> 9432 bytes
-rw-r--r--src/gui/styles/images/left-16.pngbin0 -> 826 bytes
-rw-r--r--src/gui/styles/images/left-32.pngbin0 -> 1799 bytes
-rw-r--r--src/gui/styles/images/media-pause-16.pngbin0 -> 229 bytes
-rw-r--r--src/gui/styles/images/media-pause-32.pngbin0 -> 185 bytes
-rw-r--r--src/gui/styles/images/media-play-16.pngbin0 -> 262 bytes
-rw-r--r--src/gui/styles/images/media-play-32.pngbin0 -> 413 bytes
-rw-r--r--src/gui/styles/images/media-seek-backward-16.pngbin0 -> 384 bytes
-rw-r--r--src/gui/styles/images/media-seek-backward-32.pngbin0 -> 548 bytes
-rw-r--r--src/gui/styles/images/media-seek-forward-16.pngbin0 -> 370 bytes
-rw-r--r--src/gui/styles/images/media-seek-forward-32.pngbin0 -> 524 bytes
-rw-r--r--src/gui/styles/images/media-skip-backward-16.pngbin0 -> 396 bytes
-rw-r--r--src/gui/styles/images/media-skip-backward-32.pngbin0 -> 570 bytes
-rw-r--r--src/gui/styles/images/media-skip-forward-16.pngbin0 -> 384 bytes
-rw-r--r--src/gui/styles/images/media-skip-forward-32.pngbin0 -> 549 bytes
-rw-r--r--src/gui/styles/images/media-stop-16.pngbin0 -> 166 bytes
-rw-r--r--src/gui/styles/images/media-stop-32.pngbin0 -> 176 bytes
-rw-r--r--src/gui/styles/images/media-volume-16.pngbin0 -> 799 bytes
-rw-r--r--src/gui/styles/images/media-volume-muted-16.pngbin0 -> 668 bytes
-rw-r--r--src/gui/styles/images/networkdrive-128.pngbin0 -> 18075 bytes
-rw-r--r--src/gui/styles/images/networkdrive-16.pngbin0 -> 885 bytes
-rw-r--r--src/gui/styles/images/networkdrive-32.pngbin0 -> 2245 bytes
-rw-r--r--src/gui/styles/images/newdirectory-128.pngbin0 -> 7503 bytes
-rw-r--r--src/gui/styles/images/newdirectory-16.pngbin0 -> 870 bytes
-rw-r--r--src/gui/styles/images/newdirectory-32.pngbin0 -> 1590 bytes
-rw-r--r--src/gui/styles/images/parentdir-128.pngbin0 -> 8093 bytes
-rw-r--r--src/gui/styles/images/parentdir-16.pngbin0 -> 938 bytes
-rw-r--r--src/gui/styles/images/parentdir-32.pngbin0 -> 1603 bytes
-rw-r--r--src/gui/styles/images/refresh-24.pngbin0 -> 1654 bytes
-rw-r--r--src/gui/styles/images/refresh-32.pngbin0 -> 2431 bytes
-rw-r--r--src/gui/styles/images/right-128.pngbin0 -> 9367 bytes
-rw-r--r--src/gui/styles/images/right-16.pngbin0 -> 811 bytes
-rw-r--r--src/gui/styles/images/right-32.pngbin0 -> 1804 bytes
-rw-r--r--src/gui/styles/images/standardbutton-apply-128.pngbin0 -> 5395 bytes
-rw-r--r--src/gui/styles/images/standardbutton-apply-16.pngbin0 -> 611 bytes
-rw-r--r--src/gui/styles/images/standardbutton-apply-32.pngbin0 -> 1279 bytes
-rw-r--r--src/gui/styles/images/standardbutton-cancel-128.pngbin0 -> 7039 bytes
-rw-r--r--src/gui/styles/images/standardbutton-cancel-16.pngbin0 -> 689 bytes
-rw-r--r--src/gui/styles/images/standardbutton-cancel-32.pngbin0 -> 1573 bytes
-rw-r--r--src/gui/styles/images/standardbutton-clear-128.pngbin0 -> 3094 bytes
-rw-r--r--src/gui/styles/images/standardbutton-clear-16.pngbin0 -> 456 bytes
-rw-r--r--src/gui/styles/images/standardbutton-clear-32.pngbin0 -> 866 bytes
-rw-r--r--src/gui/styles/images/standardbutton-close-128.pngbin0 -> 4512 bytes
-rw-r--r--src/gui/styles/images/standardbutton-close-16.pngbin0 -> 366 bytes
-rw-r--r--src/gui/styles/images/standardbutton-close-32.pngbin0 -> 780 bytes
-rw-r--r--src/gui/styles/images/standardbutton-closetab-16.pngbin0 -> 406 bytes
-rw-r--r--src/gui/styles/images/standardbutton-closetab-down-16.pngbin0 -> 481 bytes
-rw-r--r--src/gui/styles/images/standardbutton-closetab-hover-16.pngbin0 -> 570 bytes
-rw-r--r--src/gui/styles/images/standardbutton-delete-128.pngbin0 -> 5414 bytes
-rw-r--r--src/gui/styles/images/standardbutton-delete-16.pngbin0 -> 722 bytes
-rw-r--r--src/gui/styles/images/standardbutton-delete-32.pngbin0 -> 1541 bytes
-rw-r--r--src/gui/styles/images/standardbutton-help-128.pngbin0 -> 10765 bytes
-rw-r--r--src/gui/styles/images/standardbutton-help-16.pngbin0 -> 840 bytes
-rw-r--r--src/gui/styles/images/standardbutton-help-32.pngbin0 -> 2066 bytes
-rw-r--r--src/gui/styles/images/standardbutton-no-128.pngbin0 -> 6520 bytes
-rw-r--r--src/gui/styles/images/standardbutton-no-16.pngbin0 -> 701 bytes
-rw-r--r--src/gui/styles/images/standardbutton-no-32.pngbin0 -> 1445 bytes
-rw-r--r--src/gui/styles/images/standardbutton-ok-128.pngbin0 -> 4232 bytes
-rw-r--r--src/gui/styles/images/standardbutton-ok-16.pngbin0 -> 584 bytes
-rw-r--r--src/gui/styles/images/standardbutton-ok-32.pngbin0 -> 1246 bytes
-rw-r--r--src/gui/styles/images/standardbutton-open-128.pngbin0 -> 5415 bytes
-rw-r--r--src/gui/styles/images/standardbutton-open-16.pngbin0 -> 629 bytes
-rw-r--r--src/gui/styles/images/standardbutton-open-32.pngbin0 -> 1154 bytes
-rw-r--r--src/gui/styles/images/standardbutton-save-128.pngbin0 -> 4398 bytes
-rw-r--r--src/gui/styles/images/standardbutton-save-16.pngbin0 -> 583 bytes
-rw-r--r--src/gui/styles/images/standardbutton-save-32.pngbin0 -> 1092 bytes
-rw-r--r--src/gui/styles/images/standardbutton-yes-128.pngbin0 -> 6554 bytes
-rw-r--r--src/gui/styles/images/standardbutton-yes-16.pngbin0 -> 687 bytes
-rw-r--r--src/gui/styles/images/standardbutton-yes-32.pngbin0 -> 1504 bytes
-rw-r--r--src/gui/styles/images/stop-24.pngbin0 -> 1267 bytes
-rw-r--r--src/gui/styles/images/stop-32.pngbin0 -> 1878 bytes
-rw-r--r--src/gui/styles/images/trash-128.pngbin0 -> 3296 bytes
-rw-r--r--src/gui/styles/images/trash-16.pngbin0 -> 419 bytes
-rw-r--r--src/gui/styles/images/trash-32.pngbin0 -> 883 bytes
-rw-r--r--src/gui/styles/images/up-128.pngbin0 -> 9363 bytes
-rw-r--r--src/gui/styles/images/up-16.pngbin0 -> 814 bytes
-rw-r--r--src/gui/styles/images/up-32.pngbin0 -> 1798 bytes
-rw-r--r--src/gui/styles/images/viewdetailed-128.pngbin0 -> 4743 bytes
-rw-r--r--src/gui/styles/images/viewdetailed-16.pngbin0 -> 499 bytes
-rw-r--r--src/gui/styles/images/viewdetailed-32.pngbin0 -> 1092 bytes
-rw-r--r--src/gui/styles/images/viewlist-128.pngbin0 -> 4069 bytes
-rw-r--r--src/gui/styles/images/viewlist-16.pngbin0 -> 490 bytes
-rw-r--r--src/gui/styles/images/viewlist-32.pngbin0 -> 1006 bytes
-rw-r--r--src/gui/styles/qcdestyle.cpp305
-rw-r--r--src/gui/styles/qcdestyle.h82
-rw-r--r--src/gui/styles/qcleanlooksstyle.cpp4945
-rw-r--r--src/gui/styles/qcleanlooksstyle.h114
-rw-r--r--src/gui/styles/qcleanlooksstyle_p.h82
-rw-r--r--src/gui/styles/qcommonstyle.cpp6357
-rw-r--r--src/gui/styles/qcommonstyle.h109
-rw-r--r--src/gui/styles/qcommonstyle_p.h142
-rw-r--r--src/gui/styles/qcommonstylepixmaps_p.h186
-rw-r--r--src/gui/styles/qgtkpainter.cpp705
-rw-r--r--src/gui/styles/qgtkpainter_p.h129
-rw-r--r--src/gui/styles/qgtkstyle.cpp3280
-rw-r--r--src/gui/styles/qgtkstyle.h118
-rw-r--r--src/gui/styles/qmacstyle_mac.h144
-rw-r--r--src/gui/styles/qmacstyle_mac.mm6394
-rw-r--r--src/gui/styles/qmacstylepixmaps_mac_p.h1467
-rw-r--r--src/gui/styles/qmotifstyle.cpp2719
-rw-r--r--src/gui/styles/qmotifstyle.h128
-rw-r--r--src/gui/styles/qmotifstyle_p.h82
-rw-r--r--src/gui/styles/qplastiquestyle.cpp6024
-rw-r--r--src/gui/styles/qplastiquestyle.h119
-rw-r--r--src/gui/styles/qstyle.cpp2445
-rw-r--r--src/gui/styles/qstyle.h875
-rw-r--r--src/gui/styles/qstyle.qrc135
-rw-r--r--src/gui/styles/qstyle_p.h104
-rw-r--r--src/gui/styles/qstyle_wince.qrc97
-rw-r--r--src/gui/styles/qstylefactory.cpp259
-rw-r--r--src/gui/styles/qstylefactory.h66
-rw-r--r--src/gui/styles/qstyleoption.cpp5353
-rw-r--r--src/gui/styles/qstyleoption.h949
-rw-r--r--src/gui/styles/qstyleplugin.cpp115
-rw-r--r--src/gui/styles/qstyleplugin.h81
-rw-r--r--src/gui/styles/qstylesheetstyle.cpp5946
-rw-r--r--src/gui/styles/qstylesheetstyle_default.cpp554
-rw-r--r--src/gui/styles/qstylesheetstyle_p.h191
-rw-r--r--src/gui/styles/qwindowscestyle.cpp2422
-rw-r--r--src/gui/styles/qwindowscestyle.h103
-rw-r--r--src/gui/styles/qwindowscestyle_p.h118
-rw-r--r--src/gui/styles/qwindowsmobilestyle.cpp3503
-rw-r--r--src/gui/styles/qwindowsmobilestyle.h116
-rw-r--r--src/gui/styles/qwindowsmobilestyle_p.h93
-rw-r--r--src/gui/styles/qwindowsstyle.cpp3408
-rw-r--r--src/gui/styles/qwindowsstyle.h111
-rw-r--r--src/gui/styles/qwindowsstyle_p.h96
-rw-r--r--src/gui/styles/qwindowsvistastyle.cpp2650
-rw-r--r--src/gui/styles/qwindowsvistastyle.h108
-rw-r--r--src/gui/styles/qwindowsvistastyle_p.h217
-rw-r--r--src/gui/styles/qwindowsxpstyle.cpp4205
-rw-r--r--src/gui/styles/qwindowsxpstyle.h107
-rw-r--r--src/gui/styles/qwindowsxpstyle_p.h356
-rw-r--r--src/gui/styles/styles.pri157
-rw-r--r--src/gui/text/qabstractfontengine_p.h110
-rw-r--r--src/gui/text/qabstractfontengine_qws.cpp776
-rw-r--r--src/gui/text/qabstractfontengine_qws.h221
-rw-r--r--src/gui/text/qabstracttextdocumentlayout.cpp622
-rw-r--r--src/gui/text/qabstracttextdocumentlayout.h149
-rw-r--r--src/gui/text/qabstracttextdocumentlayout_p.h100
-rw-r--r--src/gui/text/qcssparser.cpp2808
-rw-r--r--src/gui/text/qcssparser_p.h835
-rw-r--r--src/gui/text/qcssscanner.cpp1146
-rw-r--r--src/gui/text/qfont.cpp3018
-rw-r--r--src/gui/text/qfont.h354
-rw-r--r--src/gui/text/qfont_mac.cpp158
-rw-r--r--src/gui/text/qfont_p.h278
-rw-r--r--src/gui/text/qfont_qws.cpp134
-rw-r--r--src/gui/text/qfont_win.cpp178
-rw-r--r--src/gui/text/qfont_x11.cpp370
-rw-r--r--src/gui/text/qfontdatabase.cpp2435
-rw-r--r--src/gui/text/qfontdatabase.h176
-rw-r--r--src/gui/text/qfontdatabase_mac.cpp509
-rw-r--r--src/gui/text/qfontdatabase_qws.cpp1062
-rw-r--r--src/gui/text/qfontdatabase_win.cpp1288
-rw-r--r--src/gui/text/qfontdatabase_x11.cpp2064
-rw-r--r--src/gui/text/qfontengine.cpp1623
-rw-r--r--src/gui/text/qfontengine_ft.cpp1904
-rw-r--r--src/gui/text/qfontengine_ft_p.h323
-rw-r--r--src/gui/text/qfontengine_mac.mm1701
-rw-r--r--src/gui/text/qfontengine_p.h617
-rw-r--r--src/gui/text/qfontengine_qpf.cpp1161
-rw-r--r--src/gui/text/qfontengine_qpf_p.h298
-rw-r--r--src/gui/text/qfontengine_qws.cpp625
-rw-r--r--src/gui/text/qfontengine_win.cpp1575
-rw-r--r--src/gui/text/qfontengine_win_p.h156
-rw-r--r--src/gui/text/qfontengine_x11.cpp1180
-rw-r--r--src/gui/text/qfontengine_x11_p.h177
-rw-r--r--src/gui/text/qfontengineglyphcache_p.h95
-rw-r--r--src/gui/text/qfontinfo.h87
-rw-r--r--src/gui/text/qfontmetrics.cpp1739
-rw-r--r--src/gui/text/qfontmetrics.h197
-rw-r--r--src/gui/text/qfontsubset.cpp1743
-rw-r--r--src/gui/text/qfontsubset_p.h99
-rw-r--r--src/gui/text/qfragmentmap.cpp46
-rw-r--r--src/gui/text/qfragmentmap_p.h872
-rw-r--r--src/gui/text/qpfutil.cpp66
-rw-r--r--src/gui/text/qsyntaxhighlighter.cpp618
-rw-r--r--src/gui/text/qsyntaxhighlighter.h111
-rw-r--r--src/gui/text/qtextcontrol.cpp2981
-rw-r--r--src/gui/text/qtextcontrol_p.h303
-rw-r--r--src/gui/text/qtextcontrol_p_p.h219
-rw-r--r--src/gui/text/qtextcursor.cpp2420
-rw-r--r--src/gui/text/qtextcursor.h232
-rw-r--r--src/gui/text/qtextcursor_p.h120
-rw-r--r--src/gui/text/qtextdocument.cpp2929
-rw-r--r--src/gui/text/qtextdocument.h298
-rw-r--r--src/gui/text/qtextdocument_p.cpp1600
-rw-r--r--src/gui/text/qtextdocument_p.h398
-rw-r--r--src/gui/text/qtextdocumentfragment.cpp1217
-rw-r--r--src/gui/text/qtextdocumentfragment.h92
-rw-r--r--src/gui/text/qtextdocumentfragment_p.h236
-rw-r--r--src/gui/text/qtextdocumentlayout.cpp3224
-rw-r--r--src/gui/text/qtextdocumentlayout_p.h119
-rw-r--r--src/gui/text/qtextdocumentwriter.cpp372
-rw-r--r--src/gui/text/qtextdocumentwriter.h93
-rw-r--r--src/gui/text/qtextengine.cpp2648
-rw-r--r--src/gui/text/qtextengine_mac.cpp656
-rw-r--r--src/gui/text/qtextengine_p.h608
-rw-r--r--src/gui/text/qtextformat.cpp3063
-rw-r--r--src/gui/text/qtextformat.h902
-rw-r--r--src/gui/text/qtextformat_p.h111
-rw-r--r--src/gui/text/qtexthtmlparser.cpp1881
-rw-r--r--src/gui/text/qtexthtmlparser_p.h342
-rw-r--r--src/gui/text/qtextimagehandler.cpp234
-rw-r--r--src/gui/text/qtextimagehandler_p.h80
-rw-r--r--src/gui/text/qtextlayout.cpp2453
-rw-r--r--src/gui/text/qtextlayout.h243
-rw-r--r--src/gui/text/qtextlist.cpp261
-rw-r--r--src/gui/text/qtextlist.h94
-rw-r--r--src/gui/text/qtextobject.cpp1711
-rw-r--r--src/gui/text/qtextobject.h328
-rw-r--r--src/gui/text/qtextobject_p.h103
-rw-r--r--src/gui/text/qtextodfwriter.cpp818
-rw-r--r--src/gui/text/qtextodfwriter_p.h115
-rw-r--r--src/gui/text/qtextoption.cpp414
-rw-r--r--src/gui/text/qtextoption.h161
-rw-r--r--src/gui/text/qtexttable.cpp1290
-rw-r--r--src/gui/text/qtexttable.h145
-rw-r--r--src/gui/text/qtexttable_p.h89
-rw-r--r--src/gui/text/qzip.cpp1208
-rw-r--r--src/gui/text/qzipreader_p.h119
-rw-r--r--src/gui/text/qzipwriter_p.h114
-rw-r--r--src/gui/text/text.pri177
-rw-r--r--src/gui/util/qcompleter.cpp1712
-rw-r--r--src/gui/util/qcompleter.h166
-rw-r--r--src/gui/util/qcompleter_p.h262
-rw-r--r--src/gui/util/qdesktopservices.cpp307
-rw-r--r--src/gui/util/qdesktopservices.h91
-rw-r--r--src/gui/util/qdesktopservices_mac.cpp182
-rw-r--r--src/gui/util/qdesktopservices_qws.cpp93
-rw-r--r--src/gui/util/qdesktopservices_win.cpp249
-rw-r--r--src/gui/util/qdesktopservices_x11.cpp234
-rw-r--r--src/gui/util/qsystemtrayicon.cpp675
-rw-r--r--src/gui/util/qsystemtrayicon.h132
-rw-r--r--src/gui/util/qsystemtrayicon_mac.mm547
-rw-r--r--src/gui/util/qsystemtrayicon_p.h181
-rw-r--r--src/gui/util/qsystemtrayicon_qws.cpp91
-rw-r--r--src/gui/util/qsystemtrayicon_win.cpp748
-rw-r--r--src/gui/util/qsystemtrayicon_x11.cpp394
-rw-r--r--src/gui/util/qundogroup.cpp500
-rw-r--r--src/gui/util/qundogroup.h110
-rw-r--r--src/gui/util/qundostack.cpp1129
-rw-r--r--src/gui/util/qundostack.h158
-rw-r--r--src/gui/util/qundostack_p.h111
-rw-r--r--src/gui/util/qundoview.cpp476
-rw-r--r--src/gui/util/qundoview.h102
-rw-r--r--src/gui/util/util.pri40
-rw-r--r--src/gui/widgets/qabstractbutton.cpp1468
-rw-r--r--src/gui/widgets/qabstractbutton.h183
-rw-r--r--src/gui/widgets/qabstractbutton_p.h109
-rw-r--r--src/gui/widgets/qabstractscrollarea.cpp1303
-rw-r--r--src/gui/widgets/qabstractscrollarea.h141
-rw-r--r--src/gui/widgets/qabstractscrollarea_p.h139
-rw-r--r--src/gui/widgets/qabstractslider.cpp914
-rw-r--r--src/gui/widgets/qabstractslider.h184
-rw-r--r--src/gui/widgets/qabstractslider_p.h113
-rw-r--r--src/gui/widgets/qabstractspinbox.cpp2049
-rw-r--r--src/gui/widgets/qabstractspinbox.h177
-rw-r--r--src/gui/widgets/qabstractspinbox_p.h171
-rw-r--r--src/gui/widgets/qbuttongroup.cpp260
-rw-r--r--src/gui/widgets/qbuttongroup.h112
-rw-r--r--src/gui/widgets/qcalendartextnavigator_p.h112
-rw-r--r--src/gui/widgets/qcalendarwidget.cpp3091
-rw-r--r--src/gui/widgets/qcalendarwidget.h204
-rw-r--r--src/gui/widgets/qcheckbox.cpp425
-rw-r--r--src/gui/widgets/qcheckbox.h113
-rw-r--r--src/gui/widgets/qcocoamenu_mac.mm187
-rw-r--r--src/gui/widgets/qcocoamenu_mac_p.h72
-rw-r--r--src/gui/widgets/qcocoatoolbardelegate_mac.mm153
-rw-r--r--src/gui/widgets/qcocoatoolbardelegate_mac_p.h71
-rw-r--r--src/gui/widgets/qcombobox.cpp3186
-rw-r--r--src/gui/widgets/qcombobox.h338
-rw-r--r--src/gui/widgets/qcombobox_p.h410
-rw-r--r--src/gui/widgets/qcommandlinkbutton.cpp384
-rw-r--r--src/gui/widgets/qcommandlinkbutton.h85
-rw-r--r--src/gui/widgets/qdatetimeedit.cpp2647
-rw-r--r--src/gui/widgets/qdatetimeedit.h232
-rw-r--r--src/gui/widgets/qdatetimeedit_p.h184
-rw-r--r--src/gui/widgets/qdial.cpp530
-rw-r--r--src/gui/widgets/qdial.h122
-rw-r--r--src/gui/widgets/qdialogbuttonbox.cpp1136
-rw-r--r--src/gui/widgets/qdialogbuttonbox.h168
-rw-r--r--src/gui/widgets/qdockarealayout.cpp3316
-rw-r--r--src/gui/widgets/qdockarealayout_p.h303
-rw-r--r--src/gui/widgets/qdockwidget.cpp1594
-rw-r--r--src/gui/widgets/qdockwidget.h146
-rw-r--r--src/gui/widgets/qdockwidget_p.h207
-rw-r--r--src/gui/widgets/qeffects.cpp632
-rw-r--r--src/gui/widgets/qeffects_p.h84
-rw-r--r--src/gui/widgets/qfocusframe.cpp267
-rw-r--r--src/gui/widgets/qfocusframe.h82
-rw-r--r--src/gui/widgets/qfontcombobox.cpp467
-rw-r--r--src/gui/widgets/qfontcombobox.h112
-rw-r--r--src/gui/widgets/qframe.cpp566
-rw-r--r--src/gui/widgets/qframe.h148
-rw-r--r--src/gui/widgets/qframe_p.h85
-rw-r--r--src/gui/widgets/qgroupbox.cpp792
-rw-r--r--src/gui/widgets/qgroupbox.h122
-rw-r--r--src/gui/widgets/qlabel.cpp1606
-rw-r--r--src/gui/widgets/qlabel.h175
-rw-r--r--src/gui/widgets/qlabel_p.h152
-rw-r--r--src/gui/widgets/qlcdnumber.cpp1282
-rw-r--r--src/gui/widgets/qlcdnumber.h140
-rw-r--r--src/gui/widgets/qlineedit.cpp3696
-rw-r--r--src/gui/widgets/qlineedit.h283
-rw-r--r--src/gui/widgets/qlineedit_p.h250
-rw-r--r--src/gui/widgets/qmaccocoaviewcontainer_mac.h73
-rw-r--r--src/gui/widgets/qmaccocoaviewcontainer_mac.mm190
-rw-r--r--src/gui/widgets/qmacnativewidget_mac.h74
-rw-r--r--src/gui/widgets/qmacnativewidget_mac.mm136
-rw-r--r--src/gui/widgets/qmainwindow.cpp1591
-rw-r--r--src/gui/widgets/qmainwindow.h217
-rw-r--r--src/gui/widgets/qmainwindowlayout.cpp1986
-rw-r--r--src/gui/widgets/qmainwindowlayout_mac.mm469
-rw-r--r--src/gui/widgets/qmainwindowlayout_p.h374
-rw-r--r--src/gui/widgets/qmdiarea.cpp2597
-rw-r--r--src/gui/widgets/qmdiarea.h169
-rw-r--r--src/gui/widgets/qmdiarea_p.h281
-rw-r--r--src/gui/widgets/qmdisubwindow.cpp3552
-rw-r--r--src/gui/widgets/qmdisubwindow.h159
-rw-r--r--src/gui/widgets/qmdisubwindow_p.h348
-rw-r--r--src/gui/widgets/qmenu.cpp3467
-rw-r--r--src/gui/widgets/qmenu.h428
-rw-r--r--src/gui/widgets/qmenu_mac.mm2038
-rw-r--r--src/gui/widgets/qmenu_p.h329
-rw-r--r--src/gui/widgets/qmenu_wince.cpp608
-rw-r--r--src/gui/widgets/qmenu_wince.rc231
-rw-r--r--src/gui/widgets/qmenu_wince_resource_p.h94
-rw-r--r--src/gui/widgets/qmenubar.cpp2405
-rw-r--r--src/gui/widgets/qmenubar.h363
-rw-r--r--src/gui/widgets/qmenubar_p.h230
-rw-r--r--src/gui/widgets/qmenudata.cpp96
-rw-r--r--src/gui/widgets/qmenudata.h78
-rw-r--r--src/gui/widgets/qplaintextedit.cpp2893
-rw-r--r--src/gui/widgets/qplaintextedit.h326
-rw-r--r--src/gui/widgets/qplaintextedit_p.h182
-rw-r--r--src/gui/widgets/qprintpreviewwidget.cpp829
-rw-r--r--src/gui/widgets/qprintpreviewwidget.h124
-rw-r--r--src/gui/widgets/qprogressbar.cpp592
-rw-r--r--src/gui/widgets/qprogressbar.h130
-rw-r--r--src/gui/widgets/qpushbutton.cpp732
-rw-r--r--src/gui/widgets/qpushbutton.h124
-rw-r--r--src/gui/widgets/qpushbutton_p.h82
-rw-r--r--src/gui/widgets/qradiobutton.cpp288
-rw-r--r--src/gui/widgets/qradiobutton.h88
-rw-r--r--src/gui/widgets/qrubberband.cpp339
-rw-r--r--src/gui/widgets/qrubberband.h104
-rw-r--r--src/gui/widgets/qscrollarea.cpp522
-rw-r--r--src/gui/widgets/qscrollarea.h101
-rw-r--r--src/gui/widgets/qscrollarea_p.h81
-rw-r--r--src/gui/widgets/qscrollbar.cpp740
-rw-r--r--src/gui/widgets/qscrollbar.h104
-rw-r--r--src/gui/widgets/qsizegrip.cpp566
-rw-r--r--src/gui/widgets/qsizegrip.h95
-rw-r--r--src/gui/widgets/qslider.cpp676
-rw-r--r--src/gui/widgets/qslider.h134
-rw-r--r--src/gui/widgets/qspinbox.cpp1536
-rw-r--r--src/gui/widgets/qspinbox.h188
-rw-r--r--src/gui/widgets/qsplashscreen.cpp350
-rw-r--r--src/gui/widgets/qsplashscreen.h99
-rw-r--r--src/gui/widgets/qsplitter.cpp1831
-rw-r--r--src/gui/widgets/qsplitter.h191
-rw-r--r--src/gui/widgets/qsplitter_p.h148
-rw-r--r--src/gui/widgets/qstackedwidget.cpp294
-rw-r--r--src/gui/widgets/qstackedwidget.h100
-rw-r--r--src/gui/widgets/qstatusbar.cpp847
-rw-r--r--src/gui/widgets/qstatusbar.h116
-rw-r--r--src/gui/widgets/qtabbar.cpp2301
-rw-r--r--src/gui/widgets/qtabbar.h228
-rw-r--r--src/gui/widgets/qtabbar_p.h265
-rw-r--r--src/gui/widgets/qtabwidget.cpp1450
-rw-r--r--src/gui/widgets/qtabwidget.h252
-rw-r--r--src/gui/widgets/qtextbrowser.cpp1275
-rw-r--r--src/gui/widgets/qtextbrowser.h140
-rw-r--r--src/gui/widgets/qtextedit.cpp2783
-rw-r--r--src/gui/widgets/qtextedit.h430
-rw-r--r--src/gui/widgets/qtextedit_p.h141
-rw-r--r--src/gui/widgets/qtoolbar.cpp1291
-rw-r--r--src/gui/widgets/qtoolbar.h187
-rw-r--r--src/gui/widgets/qtoolbar_p.h135
-rw-r--r--src/gui/widgets/qtoolbararealayout.cpp1370
-rw-r--r--src/gui/widgets/qtoolbararealayout_p.h199
-rw-r--r--src/gui/widgets/qtoolbarextension.cpp90
-rw-r--r--src/gui/widgets/qtoolbarextension_p.h80
-rw-r--r--src/gui/widgets/qtoolbarlayout.cpp752
-rw-r--r--src/gui/widgets/qtoolbarlayout_p.h136
-rw-r--r--src/gui/widgets/qtoolbarseparator.cpp91
-rw-r--r--src/gui/widgets/qtoolbarseparator_p.h88
-rw-r--r--src/gui/widgets/qtoolbox.cpp822
-rw-r--r--src/gui/widgets/qtoolbox.h148
-rw-r--r--src/gui/widgets/qtoolbutton.cpp1251
-rw-r--r--src/gui/widgets/qtoolbutton.h199
-rw-r--r--src/gui/widgets/qvalidator.cpp909
-rw-r--r--src/gui/widgets/qvalidator.h215
-rw-r--r--src/gui/widgets/qwidgetanimator.cpp198
-rw-r--r--src/gui/widgets/qwidgetanimator_p.h102
-rw-r--r--src/gui/widgets/qwidgetresizehandler.cpp547
-rw-r--r--src/gui/widgets/qwidgetresizehandler_p.h141
-rw-r--r--src/gui/widgets/qworkspace.cpp3382
-rw-r--r--src/gui/widgets/qworkspace.h137
-rw-r--r--src/gui/widgets/widgets.pri162
1183 files changed, 670192 insertions, 0 deletions
diff --git a/src/gui/QtGui.dynlist b/src/gui/QtGui.dynlist
new file mode 100644
index 0000000000..ea47f598a7
--- /dev/null
+++ b/src/gui/QtGui.dynlist
@@ -0,0 +1,8 @@
+{
+ extern "C++" {
+ "qt_x11ft_convert_pattern(_FcPattern*, QByteArray*, int*, bool*)";
+ "QApplication::x11ClientMessage(QWidget*, _XEvent*, bool)";
+ "QApplication::notify(QObject*, QEvent*)";
+ "QApplication::qwsEventFilter(QWSEvent*)";
+ };
+};
diff --git a/src/gui/accessible/accessible.pri b/src/gui/accessible/accessible.pri
new file mode 100644
index 0000000000..76b66879e0
--- /dev/null
+++ b/src/gui/accessible/accessible.pri
@@ -0,0 +1,24 @@
+# Qt accessibility module
+
+contains(QT_CONFIG, accessibility) {
+ HEADERS += accessible/qaccessible.h \
+ accessible/qaccessible2.h \
+ accessible/qaccessibleobject.h \
+ accessible/qaccessiblewidget.h \
+ accessible/qaccessibleplugin.h
+ SOURCES += accessible/qaccessible.cpp \
+ accessible/qaccessible2.cpp \
+ accessible/qaccessibleobject.cpp \
+ accessible/qaccessiblewidget.cpp \
+ accessible/qaccessibleplugin.cpp
+
+ mac:!embedded {
+ HEADERS += accessible/qaccessible_mac_p.h
+ OBJECTIVE_SOURCES += accessible/qaccessible_mac.mm
+ } else:win32 {
+ SOURCES += accessible/qaccessible_win.cpp
+ } else {
+ HEADERS += accessible/qaccessiblebridge.h
+ SOURCES += accessible/qaccessible_unix.cpp accessible/qaccessiblebridge.cpp
+ }
+}
diff --git a/src/gui/accessible/qaccessible.cpp b/src/gui/accessible/qaccessible.cpp
new file mode 100644
index 0000000000..b0878ab220
--- /dev/null
+++ b/src/gui/accessible/qaccessible.cpp
@@ -0,0 +1,1079 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qaccessible.h"
+
+#ifndef QT_NO_ACCESSIBILITY
+
+#include "qaccessibleplugin.h"
+#include "qaccessiblewidget.h"
+#include "qapplication.h"
+#include "qhash.h"
+#include "qmetaobject.h"
+#include "qmutex.h"
+#include <private/qfactoryloader_p.h>
+
+#include "qwidget.h"
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QAccessible
+ \brief The QAccessible class provides enums and static functions
+ relating to accessibility.
+
+ \ingroup accessibility
+ \mainclass
+
+ Accessible applications can be used by people who are not able to
+ use applications by conventional means.
+
+ The functions in this class are used for communication between
+ accessible applications (also called AT Servers) and
+ accessibility tools (AT Clients), such as screen readers and
+ braille displays. Clients and servers communicate in the following way:
+
+ \list
+ \o \e{AT Servers} notify the clients about events through calls to the
+ updateAccessibility() function.
+
+ \o \e{AT Clients} request information about the objects in the server.
+ The QAccessibleInterface class is the core interface, and encapsulates
+ this information in a pure virtual API. Implementations of the interface
+ are provided by Qt through the queryAccessibleInterface() API.
+ \endlist
+
+ The communication between servers and clients is initialized by
+ the setRootObject() function. Function pointers can be installed
+ to replace or extend the default behavior of the static functions
+ in QAccessible.
+
+ Qt supports Microsoft Active Accessibility (MSAA), Mac OS X
+ Accessibility, and the Unix/X11 AT-SPI standard. Other backends
+ can be supported using QAccessibleBridge.
+
+ In addition to QAccessible's static functions, Qt offers one
+ generic interface, QAccessibleInterface, that can be used to wrap
+ all widgets and objects (e.g., QPushButton). This single
+ interface provides all the metadata necessary for the assistive
+ technologies. Qt provides implementations of this interface for
+ its built-in widgets as plugins.
+
+ When you develop custom widgets, you can create custom subclasses
+ of QAccessibleInterface and distribute them as plugins (using
+ QAccessiblePlugin) or compile them into the application.
+ Likewise, Qt's predefined accessibility support can be built as
+ plugin (the default) or directly into the Qt library. The main
+ advantage of using plugins is that the accessibility classes are
+ only loaded into memory if they are actually used; they don't
+ slow down the common case where no assistive technology is being
+ used.
+
+ Qt also includes two convenience classes, QAccessibleObject and
+ QAccessibleWidget, that inherit from QAccessibleInterface and
+ provide the lowest common denominator of metadata (e.g., widget
+ geometry, window title, basic help text). You can use them as
+ base classes when wrapping your custom QObject or QWidget
+ subclasses.
+
+ \sa QAccessibleInterface
+*/
+
+/*!
+ \enum QAccessible::Action
+
+ This enum describes the possible types of action that can occur.
+
+ \value DefaultAction
+ \value Press
+ \value SetFocus
+ \value Increase
+ \value Decrease
+ \value Accept
+ \value Cancel
+ \value Select
+ \value ClearSelection
+ \value RemoveSelection
+ \value ExtendSelection
+ \value AddToSelection
+
+ \value FirstStandardAction
+ \value LastStandardAction
+*/
+
+/*!
+ \enum QAccessible::Method
+
+ This enum describes the possible types of methods that can be
+ invoked on an accessible object.
+
+ \value ListSupportedMethods
+ \value SetCursorPosition
+ \value GetCursorPosition
+
+ \omitvalue ForegroundColor
+ \omitvalue BackgroundColor
+
+ \sa QAccessibleInterface::invokeMethod()
+*/
+
+/*!
+ \fn QSet<Method> QAccessibleInterface::supportedMethods()
+ \since 4.3
+
+ Returns a QSet of \l{QAccessible::}{Method}s that are supported by this
+ accessible interface.
+
+ \sa QAccessible::Method invokeMethod()
+*/
+
+/*!
+ \enum QAccessible::StateFlag
+
+ This enum type defines bit flags that can be combined to indicate
+ the state of an accessible object. The values are:
+
+ \value Animated The object's appearance changes frequently.
+ \value Busy The object cannot accept input at the moment.
+ \value Checked The object's check box is checked.
+ \value Collapsed The object is collapsed, e.g. a closed listview item, or an iconified window.
+ \value DefaultButton The object represents the default button in a dialog.
+ \value Expanded The object is expandable, and currently the children are visible.
+ \value ExtSelectable The object supports extended selection.
+ \value Focusable The object can receive focus. Only objects in the active window can receive focus.
+ \value Focused The object has keyboard focus.
+ \value HasPopup The object opens a popup.
+ \value HotTracked The object's appearance is sensitive to the mouse cursor position.
+ \value Invisible The object is not visible to the user.
+ \value Linked The object is linked to another object, e.g. a hyperlink.
+ \value Marqueed The object displays scrolling contents, e.g. a log view.
+ \value Mixed The state of the object is not determined, e.g. a tri-state check box that is neither checked nor unchecked.
+ \value Modal The object blocks input from other objects.
+ \value Movable The object can be moved.
+ \value MultiSelectable The object supports multiple selected items.
+ \value Normal The normal state.
+ \value Offscreen The object is clipped by the visible area. Objects that are off screen are also invisible.
+ \value Pressed The object is pressed.
+ \value Protected The object is password protected, e.g. a line edit for entering a Password.
+ \value ReadOnly The object can usually be edited, but is explicitly set to read-only.
+ \value Selectable The object is selectable.
+ \value Selected The object is selected.
+ \value SelfVoicing The object describes itself through speech or sound.
+ \value Sizeable The object can be resized, e.g. top-level windows.
+ \value Traversed The object is linked and has been visited.
+ \value Unavailable The object is unavailable to the user, e.g. a disabled widget.
+ \omitvalue Moveable
+ \omitvalue HasInvokeExtension
+
+ Implementations of QAccessibleInterface::state() return a combination
+ of these flags.
+*/
+
+/*!
+ \enum QAccessible::Event
+
+ This enum type defines accessible event types.
+
+ \value AcceleratorChanged
+ \value Alert A system alert (e.g., a message from a QMessageBox)
+ \value ContextHelpEnd Context help (QWhatsThis) for an object is finished.
+ \value ContextHelpStart Context help (QWhatsThis) for an object is initiated.
+ \value DefaultActionChanged The default QAccessible::Action for the accessible object changed
+ \value DescriptionChanged The objects QAccessible::Description changed.
+ \value DialogEnd A dialog (QDialog) is been hidden
+ \value DialogStart A dialog (QDialog) has been set visible.
+ \value DragDropEnd A Drag & Drop operation is about to finished.
+ \value DragDropStart A Drag & Drop operation is about to be initiated.
+ \value Focus An object has gained keyboard focus.
+ \value ForegroundChanged A window has been activated (i.e., a new window has gained focus on the desktop)
+ \value HelpChanged The QAccessible::Help text property of an object has changed
+ \value LocationChanged An objects location on the screen changed
+ \value MenuCommand A menu item is triggered.
+ \value MenuEnd A menu has been closed (Qt uses PopupMenuEnd for all menus)
+ \value MenuStart A menu has been opened on the menubar (Qt uses PopupMenuStart for all menus)
+ \value NameChanged The QAccessible::Name property of an object has changed
+ \value ObjectCreated A new object is created.
+ \value ObjectDestroyed An object is deleted.
+ \value ObjectHide An object is hidden (i.e., with QWidget::hide()). Any children the object that is hidden has do not send this event.
+ It is not send when an object is hidden as it is being obcured by others.
+ \value ObjectReorder A layout or item view has added, removed, or moved an object (Qt does not use this event).
+ \value ObjectShow An object is displayed (i.e., with QWidget::show()).
+ \value ParentChanged An objects parent object changed.
+ \value PopupMenuEnd A popup menu has closed.
+ \value PopupMenuStart A popupmenu has opened.
+ \value ScrollingEnd A scrollbar scroll operation has ended (the mouse has released the slider handle)
+ \value ScrollingStart A scrollbar scroll operation is about to start (i.e., the mouse has pressed on the slider handle)
+ \value Selection The selection has changed in a menu or item view.
+ \value SelectionAdd An item has been added to the selection in an item view.
+ \value SelectionRemove An item has been removed from an item view selection.
+ \value SelectionWithin Several changes to a selection has occurred in an item view.
+ \value SoundPlayed A sound has been played by an object
+ \value StateChanged The QAccessible::State of an object has changed.
+ \value ValueChanged The QAccessible::Value of an object has changed.
+*/
+
+/*!
+ \enum QAccessible::Role
+
+ This enum defines the role of an accessible object. The roles are:
+
+ \value AlertMessage An object that is used to alert the user.
+ \value Animation An object that displays an animation.
+ \value Application The application's main window.
+ \value Assistant An object that provids interactive help.
+ \value Border An object that represents a border.
+ \value ButtonDropDown A button that drops down a list of items.
+ \value ButtonDropGrid A button that drops down a grid.
+ \value ButtonMenu A button that drops down a menu.
+ \value Canvas An object that displays graphics that the user can interact with.
+ \value Caret An object that represents the system caret (text cursor).
+ \value Cell A cell in a table.
+ \value Chart An object that displays a graphical representation of data.
+ \value CheckBox An object that represents an option that can be checked or unchecked. Some options provide a "mixed" state, e.g. neither checked nor unchecked.
+ \value Client The client area in a window.
+ \value Clock A clock displaying time.
+ \value Column A column of cells, usually within a table.
+ \value ColumnHeader A header for a column of data.
+ \value ComboBox A list of choices that the user can select from.
+ \value Cursor An object that represents the mouse cursor.
+ \value Dial An object that represents a dial or knob.
+ \value Dialog A dialog box.
+ \value Document A document window, usually in an MDI environment.
+ \value EditableText Editable text
+ \value Equation An object that represents a mathematical equation.
+ \value Graphic A graphic or picture, e.g. an icon.
+ \value Grip A grip that the user can drag to change the size of widgets.
+ \value Grouping An object that represents a logical grouping of other objects.
+ \value HelpBalloon An object that displays help in a separate, short lived window.
+ \value HotkeyField A hotkey field that allows the user to enter a key sequence.
+ \value Indicator An indicator that represents a current value or item.
+ \value LayeredPane An object that can contain layered children, e.g. in a stack.
+ \value Link A link to something else.
+ \value List A list of items, from which the user can select one or more items.
+ \value ListItem An item in a list of items.
+ \value MenuBar A menu bar from which menus are opened by the user.
+ \value MenuItem An item in a menu or menu bar.
+ \value NoRole The object has no role. This usually indicates an invalid object.
+ \value PageTab A page tab that the user can select to switch to a different page in a dialog.
+ \value PageTabList A list of page tabs.
+ \value Pane A generic container.
+ \value PopupMenu A menu which lists options that the user can select to perform an action.
+ \value ProgressBar The object displays the progress of an operation in progress.
+ \value PropertyPage A property page where the user can change options and settings.
+ \value PushButton A button.
+ \value RadioButton An object that represents an option that is mutually exclusive with other options.
+ \value Row A row of cells, usually within a table.
+ \value RowHeader A header for a row of data.
+ \value ScrollBar A scroll bar, which allows the user to scroll the visible area.
+ \value Separator A separator that divides space into logical areas.
+ \value Slider A slider that allows the user to select a value within a given range.
+ \value Sound An object that represents a sound.
+ \value SpinBox A spin box widget that allows the user to enter a value within a given range.
+ \value Splitter A splitter distributing available space between its child widgets.
+ \value StaticText Static text, such as labels for other widgets.
+ \value StatusBar A status bar.
+ \value Table A table representing data in a grid of rows and columns.
+ \value TitleBar The title bar caption of a window.
+ \value ToolBar A tool bar, which groups widgets that the user accesses frequently.
+ \value ToolTip A tool tip which provides information about other objects.
+ \value Tree A list of items in a tree structure.
+ \value TreeItem An item in a tree structure.
+ \value UserRole The first value to be used for user defined roles.
+ \value Whitespace Blank space between other objects.
+ \value Window A top level window.
+*/
+
+/*!
+ \enum QAccessible::RelationFlag
+
+ This enum type defines bit flags that can be combined to indicate
+ the relationship between two accessible objects.
+
+ \value Unrelated The objects are unrelated.
+ \value Self The objects are the same.
+ \value Ancestor The first object is a parent of the second object.
+ \value Child The first object is a direct child of the second object.
+ \value Descendent The first object is an indirect child of the second object.
+ \value Sibling The objects are siblings.
+
+ \value Up The first object is above the second object.
+ \value Down The first object is below the second object.
+ \value Left The first object is left of the second object.
+ \value Right The first object is right of the second object.
+ \value Covers The first object covers the second object.
+ \value Covered The first object is covered by the second object.
+
+ \value FocusChild The first object is the second object's focus child.
+ \value Label The first object is the label of the second object.
+ \value Labelled The first object is labelled by the second object.
+ \value Controller The first object controls the second object.
+ \value Controlled The first object is controlled by the second object.
+
+ \omitvalue HierarchyMask
+ \omitvalue GeometryMask
+ \omitvalue LogicalMask
+
+ Implementations of relationTo() return a combination of these flags.
+ Some values are mutually exclusive.
+
+ Implementations of navigate() can accept only one distinct value.
+*/
+
+/*!
+ \enum QAccessible::Text
+
+ This enum specifies string information that an accessible object
+ returns.
+
+ \value Name The name of the object. This can be used both
+ as an identifier or a short description by
+ accessible clients.
+ \value Description A short text describing the object.
+ \value Value The value of the object.
+ \value Help A longer text giving information about how to use the object.
+ \value Accelerator The keyboard shortcut that executes the object's default action.
+ \value UserText The first value to be used for user defined text.
+*/
+
+/*!
+ \fn QAccessibleInterface::~QAccessibleInterface()
+
+ Destroys the object.
+*/
+
+/*!
+ \fn void QAccessible::initialize()
+ \internal
+*/
+
+/*!
+ \fn void QAccessible::cleanup()
+ \internal
+*/
+
+#if !defined(QT_NO_LIBRARY) && (!defined(QT_NO_SETTINGS) || !defined(Q_OS_WIN))
+Q_GLOBAL_STATIC_WITH_ARGS(QFactoryLoader, loader,
+ (QAccessibleFactoryInterface_iid, QLatin1String("/accessible")))
+#endif
+
+Q_GLOBAL_STATIC(QList<QAccessible::InterfaceFactory>, qAccessibleFactories);
+
+QAccessible::UpdateHandler QAccessible::updateHandler = 0;
+QAccessible::RootObjectHandler QAccessible::rootObjectHandler = 0;
+
+static bool accessibility_active = false;
+static bool cleanupAdded = false;
+static void qAccessibleCleanup()
+{
+ qAccessibleFactories()->clear();
+}
+
+/*!
+ \typedef QAccessible::InterfaceFactory
+
+ A function pointer type. Use a function with this prototype to install
+ interface factories with installFactory().
+
+ The function receives a QObject pointer. If the QObject
+ provides a QAccessibleInterface, it sets the second parameter to
+ point to the corresponding QAccessibleInterface, and returns true;
+ otherwise returns false.
+
+ Installed factories are called by queryAccessibilityInterface() until
+ one provides an interface.
+*/
+
+/*!
+ \typedef QAccessible::UpdateHandler
+
+ \internal
+
+ A function pointer type. Use a function with this prototype to install
+ your own update function.
+
+ The function is called by updateAccessibility().
+*/
+
+/*!
+ \typedef QAccessible::RootObjectHandler
+
+ \internal
+
+ A function pointer type. Use a function with this prototype to install
+ your own root object handler.
+
+ The function is called by setRootObject().
+*/
+
+/*!
+ Installs the InterfaceFactory \a factory. The last factory added
+ is the first one used by queryAccessibleInterface().
+*/
+void QAccessible::installFactory(InterfaceFactory factory)
+{
+ if (!factory)
+ return;
+
+ if (!cleanupAdded) {
+ qAddPostRoutine(qAccessibleCleanup);
+ cleanupAdded = true;
+ }
+ if (qAccessibleFactories()->contains(factory))
+ return;
+ qAccessibleFactories()->append(factory);
+}
+
+/*!
+ Removes \a factory from the list of installed InterfaceFactories.
+*/
+void QAccessible::removeFactory(InterfaceFactory factory)
+{
+ qAccessibleFactories()->removeAll(factory);
+}
+
+/*!
+ \internal
+
+ Installs the given \a handler as the function to be used by
+ updateAccessibility(), and returns the previously installed
+ handler.
+*/
+QAccessible::UpdateHandler QAccessible::installUpdateHandler(UpdateHandler handler)
+{
+ UpdateHandler old = updateHandler;
+ updateHandler = handler;
+ return old;
+}
+
+/*!
+ Installs the given \a handler as the function to be used by setRootObject(),
+ and returns the previously installed handler.
+*/
+QAccessible::RootObjectHandler QAccessible::installRootObjectHandler(RootObjectHandler handler)
+{
+ RootObjectHandler old = rootObjectHandler;
+ rootObjectHandler = handler;
+ return old;
+}
+
+/*!
+ If a QAccessibleInterface implementation exists for the given \a object,
+ this function returns a pointer to the implementation; otherwise it
+ returns 0.
+
+ The function calls all installed factory functions (from most
+ recently installed to least recently installed) until one is found
+ that provides an interface for the class of \a object. If no
+ factory can provide an accessibility implementation for the class
+ the function loads installed accessibility plugins, and tests if
+ any of the plugins can provide the implementation.
+
+ If no implementation for the object's class is available, the
+ function tries to find an implementation for the object's parent
+ class, using the above strategy.
+
+ \warning The caller is responsible for deleting the returned
+ interface after use.
+*/
+QAccessibleInterface *QAccessible::queryAccessibleInterface(QObject *object)
+{
+ accessibility_active = true;
+ QAccessibleInterface *iface = 0;
+ if (!object)
+ return 0;
+
+ QEvent e(QEvent::AccessibilityPrepare);
+ QApplication::sendEvent(object, &e);
+
+ const QMetaObject *mo = object->metaObject();
+ while (mo) {
+ const QLatin1String cn(mo->className());
+ for (int i = qAccessibleFactories()->count(); i > 0; --i) {
+ InterfaceFactory factory = qAccessibleFactories()->at(i - 1);
+ iface = factory(cn, object);
+ if (iface)
+ return iface;
+ }
+#if !defined(QT_NO_LIBRARY) && (!defined(QT_NO_SETTINGS) || !defined(Q_OS_WIN))
+ QAccessibleFactoryInterface *factory = qobject_cast<QAccessibleFactoryInterface*>(loader()->instance(cn));
+ if (factory) {
+ iface = factory->create(cn, object);
+ if (iface)
+ return iface;
+ }
+#endif
+ mo = mo->superClass();
+ }
+
+ QWidget *widget = qobject_cast<QWidget*>(object);
+ if (widget)
+ return new QAccessibleWidget(widget);
+ else if (object == qApp)
+ return new QAccessibleApplication();
+
+ return 0;
+}
+
+/*!
+ Returns true if an accessibility implementation has been requested
+ during the runtime of the application; otherwise returns false.
+
+ Use this function to prevent potentially expensive notifications via
+ updateAccessibility().
+*/
+bool QAccessible::isActive()
+{
+ return accessibility_active;
+}
+
+/*!
+ \fn void QAccessible::setRootObject(QObject *object)
+
+ Sets the root accessible object of this application to \a object.
+ All other accessible objects in the application can be reached by the
+ client using object navigation.
+
+ You should never need to call this function. Qt sets the QApplication
+ object as the root object immediately before the event loop is entered
+ in QApplication::exec().
+
+ Use QAccessible::installRootObjectHandler() to redirect the function
+ call to a customized handler function.
+
+ \sa queryAccessibleInterface()
+*/
+
+/*!
+ \fn void QAccessible::updateAccessibility(QObject *object, int child, Event reason)
+
+ Notifies accessibility clients about a change in \a object's
+ accessibility information.
+
+ \a reason specifies the cause of the change, for example,
+ \c ValueChange when the position of a slider has been changed. \a
+ child is the (1-based) index of the child element that has changed.
+ When \a child is 0, the object itself has changed.
+
+ Call this function whenever the state of your accessible object or
+ one of its sub-elements has been changed either programmatically
+ (e.g. by calling QLabel::setText()) or by user interaction.
+
+ If there are no accessibility tools listening to this event, the
+ performance penalty for calling this function is small, but if determining
+ the parameters of the call is expensive you can test isActive() to
+ avoid unnecessary computations.
+*/
+
+
+/*!
+ \class QAccessibleInterface
+ \brief The QAccessibleInterface class defines an interface that exposes information
+ about accessible objects.
+
+ \ingroup accessibility
+
+ Accessibility tools (also called AT Clients), such as screen readers
+ or braille displays, require high-level information about
+ accessible objects in an application. Accessible objects provide
+ specialized input and output methods, making it possible for users
+ to use accessibility tools with enabled applications (AT Servers).
+
+ Every element that the user needs to interact with or react to is
+ an accessible object, and should provide this information. These
+ are mainly visual objects, such as widgets and widget elements, but
+ can also be content, such as sounds.
+
+ The AT client uses three basic concepts to acquire information
+ about any accessible object in an application:
+ \list
+ \i \e Properties The client can read information about
+ accessible objects. In some cases the client can also modify these
+ properties; such as text in a line edit.
+ \i \e Actions The client can invoke actions like pressing a button
+ or .
+ \i \e{Relationships and Navigation} The client can traverse from one
+ accessible object to another, using the relationships between objects.
+ \endlist
+
+ The QAccessibleInterface defines the API for these three concepts.
+
+ \section1 Relationships and Navigation
+
+ The functions childCount() and indexOfChild() return the number of
+ children of an accessible object and the index a child object has
+ in its parent. The childAt() function returns the index of a child
+ at a given position.
+
+ The relationTo() function provides information about how two
+ different objects relate to each other, and navigate() allows
+ traversing from one object to another object with a given
+ relationship.
+
+ \section1 Properties
+
+ The central property of an accessible objects is what role() it
+ has. Different objects can have the same role, e.g. both the "Add
+ line" element in a scroll bar and the \c OK button in a dialog have
+ the same role, "button". The role implies what kind of
+ interaction the user can perform with the user interface element.
+
+ An object's state() property is a combination of different state
+ flags and can describe both how the object's state differs from a
+ "normal" state, e.g. it might be unavailable, and also how it
+ behaves, e.g. it might be selectable.
+
+ The text() property provides textual information about the object.
+ An object usually has a name, but can provide extended information
+ such as a description, help text, or information about any
+ keyboard accelerators it provides. Some objects allow changing the
+ text() property through the setText() function, but this
+ information is in most cases read-only.
+
+ The rect() property provides information about the geometry of an
+ accessible object. This information is usually only available for
+ visual objects.
+
+ \section1 Actions and Selection
+
+ To enable the user to interact with an accessible object the
+ object must expose information about the actions that it can
+ perform. userActionCount() returns the number of actions supported by
+ an accessible object, and actionText() returns textual information
+ about those actions. doAction() invokes an action.
+
+ Objects that support selections can define actions to change the selection.
+
+ \section2 Objects and children
+
+ A QAccessibleInterface provides information about the accessible
+ object, and can also provide information for the children of that
+ object if those children don't provide a QAccessibleInterface
+ implementation themselves. This is practical if the object has
+ many similar children (e.g. items in a list view), or if the
+ children are an integral part of the object itself, for example, the
+ different sections in a scroll bar.
+
+ If an accessible object provides information about its children
+ through one QAccessibleInterface, the children are referenced
+ using indexes. The index is 1-based for the children, i.e. 0
+ refers to the object itself, 1 to the first child, 2 to the second
+ child, and so on.
+
+ All functions in QAccessibleInterface that take a child index
+ relate to the object itself if the index is 0, or to the child
+ specified. If a child provides its own interface implementation
+ (which can be retrieved through navigation) asking the parent for
+ information about that child will usually not succeed.
+
+ \sa QAccessible
+*/
+
+/*!
+ \fn bool QAccessibleInterface::isValid() const
+
+ Returns true if all the data necessary to use this interface
+ implementation is valid (e.g. all pointers are non-null);
+ otherwise returns false.
+
+ \sa object()
+*/
+
+/*!
+ \fn QObject *QAccessibleInterface::object() const
+
+ Returns a pointer to the QObject this interface implementation provides
+ information for.
+
+ \sa isValid()
+*/
+
+/*!
+ \fn int QAccessibleInterface::childCount() const
+
+ Returns the number of children that belong to this object. A child
+ can provide accessibility information on its own (e.g. a child
+ widget), or be a sub-element of this accessible object.
+
+ All objects provide this information.
+
+ \sa indexOfChild()
+*/
+
+/*!
+ \fn int QAccessibleInterface::indexOfChild(const QAccessibleInterface *child) const
+
+ Returns the 1-based index of the object \a child in this object's
+ children list, or -1 if \a child is not a child of this object. 0
+ is not a possible return value.
+
+ All objects provide this information about their children.
+
+ \sa childCount()
+*/
+
+/*!
+ \fn QAccessible::Relation QAccessibleInterface::relationTo(int child,
+const QAccessibleInterface *other, int otherChild) const
+
+ Returns the relationship between this object's \a child and the \a
+ other object's \a otherChild. If \a child is 0 the object's own relation
+ is returned.
+
+ The returned value indicates the relation of the called object to
+ the \a other object, e.g. if this object is a child of \a other
+ the return value will be \c Child.
+
+ The return value is a combination of the bit flags in the
+ QAccessible::Relation enumeration.
+
+ All objects provide this information.
+
+ \sa indexOfChild(), navigate()
+*/
+
+/*!
+ \fn int QAccessibleInterface::childAt(int x, int y) const
+
+ Returns the 1-based index of the child that contains the screen
+ coordinates (\a x, \a y). This function returns 0 if the point is
+ positioned on the object itself. If the tested point is outside
+ the boundaries of the object this function returns -1.
+
+ This function is only relyable for visible objects (invisible
+ object might not be laid out correctly).
+
+ All visual objects provide this information.
+
+ \sa rect()
+*/
+
+/*!
+ \fn int QAccessibleInterface::navigate(RelationFlag relation, int entry, QAccessibleInterface
+**target) const
+
+ Navigates from this object to an object that has a relationship
+ \a relation to this object, and returns the respective object in
+ \a target. It is the caller's responsibility to delete *\a target
+ after use.
+
+ If an object is found, \a target is set to point to the object, and
+ the index of the child of \a target is returned. The return value
+ is 0 if \a target itself is the requested object. \a target is set
+ to null if this object is the target object (i.e. the requested
+ object is a handled by this object).
+
+ If no object is found \a target is set to null, and the return
+ value is -1.
+
+ The \a entry parameter has two different meanings:
+ \list
+ \i \e{Hierarchical and Logical relationships} -- if multiple objects with
+ the requested relationship exist \a entry specifies which one to
+ return. \a entry is 1-based, e.g. use 1 to get the first (and
+ possibly only) object with the requested relationship.
+
+ The following code demonstrates how to use this function to
+ navigate to the first child of an object:
+
+ \snippet doc/src/snippets/code/src_gui_accessible_qaccessible.cpp 0
+
+ \i \e{Geometric relationships} -- the index of the child from
+ which to start navigating in the specified direction. \a entry
+ can be 0 to navigate to a sibling of this object, or non-null to
+ navigate within contained children that don't provide their own
+ accessible information.
+ \endlist
+
+ Note that the \c Descendent value for \a relation is not supported.
+
+ All objects support navigation.
+
+ \sa relationTo(), childCount()
+*/
+
+/*!
+ \fn QString QAccessibleInterface::text(Text t, int child) const
+
+ Returns the value of the text property \a t of the object, or of
+ the object's child if \a child is not 0.
+
+ The \l Name is a string used by clients to identify, find, or
+ announce an accessible object for the user. All objects must have
+ a name that is unique within their container. The name can be
+ used differently by clients, so the name should both give a
+ short description of the object and be unique.
+
+ An accessible object's \l Description provides textual information
+ about an object's visual appearance. The description is primarily
+ used to provide greater context for vision-impaired users, but is
+ also used for context searching or other applications. Not all
+ objects have a description. An "OK" button would not need a
+ description, but a tool button that shows a picture of a smiley
+ would.
+
+ The \l Value of an accessible object represents visual information
+ contained by the object, e.g. the text in a line edit. Usually,
+ the value can be modified by the user. Not all objects have a
+ value, e.g. static text labels don't, and some objects have a
+ state that already is the value, e.g. toggle buttons.
+
+ The \l Help text provides information about the function and
+ usage of an accessible object. Not all objects provide this
+ information.
+
+ The \l Accelerator is a keyboard shortcut that activates the
+ object's default action. A keyboard shortcut is the underlined
+ character in the text of a menu, menu item or widget, and is
+ either the character itself, or a combination of this character
+ and a modifier key like Alt, Ctrl or Shift. Command controls like
+ tool buttons also have shortcut keys and usually display them in
+ their tooltip.
+
+ All objects provide a string for \l Name.
+
+ \sa role(), state()
+*/
+
+/*!
+ \fn void QAccessibleInterface::setText(Text t, int child, const QString &text)
+
+ Sets the text property \a t of the object, or of the object's
+ child if \a child is not 0, to \a text.
+
+ Note that the text properties of most objects are read-only.
+
+ \sa text()
+*/
+
+/*!
+ \fn QRect QAccessibleInterface::rect(int child) const
+
+ Returns the geometry of the object, or of the object's child if \a child
+ is not 0. The geometry is in screen coordinates.
+
+ This function is only reliable for visible objects (invisible
+ objects might not be laid out correctly).
+
+ All visual objects provide this information.
+
+ \sa childAt()
+*/
+
+/*!
+ \fn QAccessible::Role QAccessibleInterface::role(int child) const
+
+ Returns the role of the object, or of the object's child if \a child
+ is not 0. The role of an object is usually static.
+
+ All accessible objects have a role.
+
+ \sa text(), state()
+*/
+
+/*!
+ \fn QAccessible::State QAccessibleInterface::state(int child) const
+
+ Returns the current state of the object, or of the object's child if
+ \a child is not 0. The returned value is a combination of the flags in
+ the QAccessible::StateFlag enumeration.
+
+ All accessible objects have a state.
+
+ \sa text(), role()
+*/
+
+/*!
+ \fn int QAccessibleInterface::userActionCount(int child) const
+
+ Returns the number of custom actions of the object, or of the
+ object's child if \a child is not 0.
+
+ The \c Action type enumerates predefined actions: these
+ are not included in the returned value.
+
+ \sa actionText(), doAction()
+*/
+
+/*!
+ \fn QString QAccessibleInterface::actionText(int action, Text t, int child) const
+
+ Returns the text property \a t of the action \a action supported by
+ the object, or of the object's child if \a child is not 0.
+
+ \sa text(), userActionCount()
+*/
+
+/*!
+ \fn bool QAccessibleInterface::doAction(int action, int child, const QVariantList &params)
+
+ Asks the object, or the object's \a child if \a child is not 0, to
+ execute \a action using the parameters, \a params. Returns true if
+ the action could be executed; otherwise returns false.
+
+ \a action can be a predefined or a custom action.
+
+ \sa userActionCount(), actionText()
+*/
+
+/*!
+ \fn QColor QAccessibleInterface::backgroundColor()
+ \internal
+*/
+
+/*!
+ \fn QAccessibleEditableTextInterface *QAccessibleInterface::editableTextInterface()
+ \internal
+*/
+
+/*!
+ \fn QColor QAccessibleInterface::foregroundColor()
+ \internal
+*/
+
+/*!
+ \fn QAccessibleTextInterface *QAccessibleInterface::textInterface()
+ \internal
+*/
+
+/*!
+ \fn QAccessibleValueInterface *QAccessibleInterface::valueInterface()
+ \internal
+*/
+
+/*!
+ \fn QAccessibleTableInterface *QAccessibleInterface::tableInterface()
+ \internal
+*/
+
+/*!
+ \class QAccessibleEvent
+ \brief The QAccessibleEvent class is used to query addition
+ accessibility information about complex widgets.
+
+ The event can be of type QEvent::AccessibilityDescription or
+ QEvent::AccessibilityHelp.
+
+ Some QAccessibleInterface implementations send QAccessibleEvents
+ to the widget they wrap to obtain the description or help text of
+ a widget or of its children. The widget can answer by calling
+ setValue() with the requested information.
+
+ The default QWidget::event() implementation simply sets the text
+ to be the widget's \l{QWidget::toolTip}{tooltip} (for \l
+ AccessibilityDescription event) or its
+ \l{QWidget::whatsThis}{"What's This?" text} (for \l
+ AccessibilityHelp event).
+
+ \ingroup accessibility
+ \ingroup events
+*/
+
+/*!
+ \fn QAccessibleEvent::QAccessibleEvent(Type type, int child)
+
+ Constructs an accessibility event of the given \a type, which
+ must be QEvent::AccessibilityDescription or
+ QEvent::AccessibilityHelp.
+
+ \a child is the (1-based) index of the child to which the request
+ applies. If \a child is 0, the request is for the widget itself.
+
+ \sa child()
+*/
+
+/*!
+ \fn int QAccessibleEvent::child() const
+
+ Returns the (1-based) index of the child to which the request
+ applies. If the child is 0, the request is for the widget itself.
+*/
+
+/*!
+ \fn QString QAccessibleEvent::value() const
+
+ Returns the text set using setValue().
+
+ \sa setValue()
+*/
+
+/*!
+ \fn void QAccessibleEvent::setValue(const QString &text)
+
+ Set the description or help text for the given child() to \a
+ text, thereby answering the request.
+
+ \sa value()
+*/
+
+/*!
+ \since 4.2
+
+ Invokes a \a method on \a child with the given parameters \a params
+ and returns the result of the operation as QVariant.
+
+ Note that the type of the returned QVariant depends on the action.
+
+ Returns an invalid QVariant if the object doesn't support the action.
+*/
+QVariant QAccessibleInterface::invokeMethod(Method method, int child, const QVariantList &params)
+{
+ if (!(state(0) & HasInvokeExtension))
+ return QVariant();
+
+ return static_cast<QAccessibleInterfaceEx *>(this)->invokeMethodEx(method, child, params);
+}
+
+QVariant QAccessibleInterfaceEx::virtual_hook(const QVariant &)
+{
+ return QVariant();
+}
+
+/*! \internal */
+QAccessible2Interface *QAccessibleInterface::cast_helper(QAccessible2::InterfaceType t)
+{
+ if (state(0) & HasInvokeExtension)
+ return static_cast<QAccessibleInterfaceEx *>(this)->interface_cast(t);
+ return 0;
+}
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/gui/accessible/qaccessible.h b/src/gui/accessible/qaccessible.h
new file mode 100644
index 0000000000..91f18b39f6
--- /dev/null
+++ b/src/gui/accessible/qaccessible.h
@@ -0,0 +1,417 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QACCESSIBLE_H
+#define QACCESSIBLE_H
+
+#include <QtCore/qglobal.h>
+#include <QtCore/qobject.h>
+#include <QtCore/qrect.h>
+#include <QtCore/qset.h>
+#include <QtCore/qvector.h>
+#include <QtCore/qvariant.h>
+#include <QtGui/qcolor.h>
+#include <QtGui/qevent.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+#ifndef QT_NO_ACCESSIBILITY
+
+class QAccessibleInterface;
+
+class Q_GUI_EXPORT QAccessible
+{
+public:
+ enum Event {
+ SoundPlayed = 0x0001,
+ Alert = 0x0002,
+ ForegroundChanged = 0x0003,
+ MenuStart = 0x0004,
+ MenuEnd = 0x0005,
+ PopupMenuStart = 0x0006,
+ PopupMenuEnd = 0x0007,
+ ContextHelpStart = 0x000C,
+ ContextHelpEnd = 0x000D,
+ DragDropStart = 0x000E,
+ DragDropEnd = 0x000F,
+ DialogStart = 0x0010,
+ DialogEnd = 0x0011,
+ ScrollingStart = 0x0012,
+ ScrollingEnd = 0x0013,
+
+ MenuCommand = 0x0018,
+
+ ObjectCreated = 0x8000,
+ ObjectDestroyed = 0x8001,
+ ObjectShow = 0x8002,
+ ObjectHide = 0x8003,
+ ObjectReorder = 0x8004,
+ Focus = 0x8005,
+ Selection = 0x8006,
+ SelectionAdd = 0x8007,
+ SelectionRemove = 0x8008,
+ SelectionWithin = 0x8009,
+ StateChanged = 0x800A,
+ LocationChanged = 0x800B,
+ NameChanged = 0x800C,
+ DescriptionChanged = 0x800D,
+ ValueChanged = 0x800E,
+ ParentChanged = 0x800F,
+ HelpChanged = 0x80A0,
+ DefaultActionChanged = 0x80B0,
+ AcceleratorChanged = 0x80C0
+ };
+
+ enum StateFlag {
+ Normal = 0x00000000,
+ Unavailable = 0x00000001,
+ Selected = 0x00000002,
+ Focused = 0x00000004,
+ Pressed = 0x00000008,
+ Checked = 0x00000010,
+ Mixed = 0x00000020,
+ ReadOnly = 0x00000040,
+ HotTracked = 0x00000080,
+ DefaultButton = 0x00000100,
+ Expanded = 0x00000200,
+ Collapsed = 0x00000400,
+ Busy = 0x00000800,
+ // Floating = 0x00001000,
+ Marqueed = 0x00002000,
+ Animated = 0x00004000,
+ Invisible = 0x00008000,
+ Offscreen = 0x00010000,
+ Sizeable = 0x00020000,
+ Movable = 0x00040000,
+#ifdef QT3_SUPPORT
+ Moveable = Movable,
+#endif
+ SelfVoicing = 0x00080000,
+ Focusable = 0x00100000,
+ Selectable = 0x00200000,
+ Linked = 0x00400000,
+ Traversed = 0x00800000,
+ MultiSelectable = 0x01000000,
+ ExtSelectable = 0x02000000,
+ //AlertLow = 0x04000000,
+ //AlertMedium = 0x08000000,
+ //AlertHigh = 0x10000000, /* reused for HasInvokeExtension */
+ Protected = 0x20000000,
+ HasPopup = 0x40000000,
+ Modal = 0x80000000,
+
+ HasInvokeExtension = 0x10000000 // internal
+ };
+ Q_DECLARE_FLAGS(State, StateFlag)
+
+ enum Role {
+ NoRole = 0x00000000,
+ TitleBar = 0x00000001,
+ MenuBar = 0x00000002,
+ ScrollBar = 0x00000003,
+ Grip = 0x00000004,
+ Sound = 0x00000005,
+ Cursor = 0x00000006,
+ Caret = 0x00000007,
+ AlertMessage = 0x00000008,
+ Window = 0x00000009,
+ Client = 0x0000000A,
+ PopupMenu = 0x0000000B,
+ MenuItem = 0x0000000C,
+ ToolTip = 0x0000000D,
+ Application = 0x0000000E,
+ Document = 0x0000000F,
+ Pane = 0x00000010,
+ Chart = 0x00000011,
+ Dialog = 0x00000012,
+ Border = 0x00000013,
+ Grouping = 0x00000014,
+ Separator = 0x00000015,
+ ToolBar = 0x00000016,
+ StatusBar = 0x00000017,
+ Table = 0x00000018,
+ ColumnHeader = 0x00000019,
+ RowHeader = 0x0000001A,
+ Column = 0x0000001B,
+ Row = 0x0000001C,
+ Cell = 0x0000001D,
+ Link = 0x0000001E,
+ HelpBalloon = 0x0000001F,
+ Assistant = 0x00000020,
+ List = 0x00000021,
+ ListItem = 0x00000022,
+ Tree = 0x00000023,
+ TreeItem = 0x00000024,
+ PageTab = 0x00000025,
+ PropertyPage = 0x00000026,
+ Indicator = 0x00000027,
+ Graphic = 0x00000028,
+ StaticText = 0x00000029,
+ EditableText = 0x0000002A, // Editable, selectable, etc.
+ PushButton = 0x0000002B,
+ CheckBox = 0x0000002C,
+ RadioButton = 0x0000002D,
+ ComboBox = 0x0000002E,
+ // DropList = 0x0000002F,
+ ProgressBar = 0x00000030,
+ Dial = 0x00000031,
+ HotkeyField = 0x00000032,
+ Slider = 0x00000033,
+ SpinBox = 0x00000034,
+ Canvas = 0x00000035,
+ Animation = 0x00000036,
+ Equation = 0x00000037,
+ ButtonDropDown = 0x00000038,
+ ButtonMenu = 0x00000039,
+ ButtonDropGrid = 0x0000003A,
+ Whitespace = 0x0000003B,
+ PageTabList = 0x0000003C,
+ Clock = 0x0000003D,
+ Splitter = 0x0000003E,
+ LayeredPane = 0x0000003F,
+ UserRole = 0x0000ffff
+ };
+
+ enum Text {
+ Name = 0,
+ Description,
+ Value,
+ Help,
+ Accelerator,
+ UserText = 0x0000ffff
+ };
+
+ enum RelationFlag {
+ Unrelated = 0x00000000,
+ Self = 0x00000001,
+ Ancestor = 0x00000002,
+ Child = 0x00000004,
+ Descendent = 0x00000008,
+ Sibling = 0x00000010,
+ HierarchyMask = 0x000000ff,
+
+ Up = 0x00000100,
+ Down = 0x00000200,
+ Left = 0x00000400,
+ Right = 0x00000800,
+ Covers = 0x00001000,
+ Covered = 0x00002000,
+ GeometryMask = 0x0000ff00,
+
+ FocusChild = 0x00010000,
+ Label = 0x00020000,
+ Labelled = 0x00040000,
+ Controller = 0x00080000,
+ Controlled = 0x00100000,
+ LogicalMask = 0x00ff0000
+ };
+ Q_DECLARE_FLAGS(Relation, RelationFlag)
+
+ enum Action {
+ DefaultAction = 0,
+ Press = -1,
+ FirstStandardAction = Press,
+ SetFocus = -2,
+ Increase = -3,
+ Decrease = -4,
+ Accept = -5,
+ Cancel = -6,
+ Select = -7,
+ ClearSelection = -8,
+ RemoveSelection = -9,
+ ExtendSelection = -10,
+ AddToSelection = -11,
+ LastStandardAction = AddToSelection
+ };
+
+ enum Method {
+ ListSupportedMethods = 0,
+ SetCursorPosition = 1,
+ GetCursorPosition = 2,
+ ForegroundColor = 3,
+ BackgroundColor = 4
+ };
+
+ typedef QAccessibleInterface*(*InterfaceFactory)(const QString &key, QObject*);
+ typedef void(*UpdateHandler)(QObject*, int who, Event reason);
+ typedef void(*RootObjectHandler)(QObject*);
+
+ static void installFactory(InterfaceFactory);
+ static void removeFactory(InterfaceFactory);
+ static UpdateHandler installUpdateHandler(UpdateHandler);
+ static RootObjectHandler installRootObjectHandler(RootObjectHandler);
+
+ static QAccessibleInterface *queryAccessibleInterface(QObject *);
+ static void updateAccessibility(QObject *, int who, Event reason);
+ static bool isActive();
+ static void setRootObject(QObject*);
+
+ static void initialize();
+ static void cleanup();
+
+private:
+ static UpdateHandler updateHandler;
+ static RootObjectHandler rootObjectHandler;
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(QAccessible::State)
+Q_DECLARE_OPERATORS_FOR_FLAGS(QAccessible::Relation)
+QT_END_NAMESPACE
+Q_DECLARE_METATYPE(QSet<QAccessible::Method>)
+QT_BEGIN_NAMESPACE
+
+namespace QAccessible2
+{
+ enum InterfaceType
+ {
+ TextInterface,
+ EditableTextInterface,
+ ValueInterface,
+ TableInterface
+ };
+}
+
+class QAccessible2Interface;
+class QAccessibleTextInterface;
+class QAccessibleEditableTextInterface;
+class QAccessibleValueInterface;
+class QAccessibleTableInterface;
+
+class Q_GUI_EXPORT QAccessibleInterface : public QAccessible
+{
+public:
+ virtual ~QAccessibleInterface() {}
+ // check for valid pointers
+ virtual bool isValid() const = 0;
+ virtual QObject *object() const = 0;
+
+ // hierarchy
+ virtual int childCount() const = 0;
+ virtual int indexOfChild(const QAccessibleInterface *) const = 0;
+
+ // relations
+ virtual Relation relationTo(int child, const QAccessibleInterface *other,
+ int otherChild) const = 0;
+ virtual int childAt(int x, int y) const = 0;
+
+ // navigation
+ virtual int navigate(RelationFlag relation, int index, QAccessibleInterface **iface) const = 0;
+
+ // properties and state
+ virtual QString text(Text t, int child) const = 0;
+ virtual void setText(Text t, int child, const QString &text) = 0;
+ virtual QRect rect(int child) const = 0;
+ virtual Role role(int child) const = 0;
+ virtual State state(int child) const = 0;
+
+ // action
+ virtual int userActionCount(int child) const = 0;
+ virtual QString actionText(int action, Text t, int child) const = 0;
+ virtual bool doAction(int action, int child, const QVariantList &params = QVariantList()) = 0;
+
+ QVariant invokeMethod(Method method, int child = 0,
+ const QVariantList &params = QVariantList());
+
+ inline QSet<Method> supportedMethods()
+ { return qvariant_cast<QSet<Method> >(invokeMethod(ListSupportedMethods)); }
+
+ inline QColor foregroundColor()
+ { return qvariant_cast<QColor>(invokeMethod(ForegroundColor)); }
+
+ inline QColor backgroundColor()
+ { return qvariant_cast<QColor>(invokeMethod(BackgroundColor)); }
+
+ inline QAccessibleTextInterface *textInterface()
+ { return reinterpret_cast<QAccessibleTextInterface *>(cast_helper(QAccessible2::TextInterface)); }
+
+ inline QAccessibleEditableTextInterface *editableTextInterface()
+ { return reinterpret_cast<QAccessibleEditableTextInterface *>(cast_helper(QAccessible2::EditableTextInterface)); }
+
+ inline QAccessibleValueInterface *valueInterface()
+ { return reinterpret_cast<QAccessibleValueInterface *>(cast_helper(QAccessible2::ValueInterface)); }
+
+ inline QAccessibleTableInterface *tableInterface()
+ { return reinterpret_cast<QAccessibleTableInterface *>(cast_helper(QAccessible2::TableInterface)); }
+
+private:
+ QAccessible2Interface *cast_helper(QAccessible2::InterfaceType);
+};
+
+class Q_GUI_EXPORT QAccessibleInterfaceEx: public QAccessibleInterface
+{
+public:
+ virtual QVariant invokeMethodEx(Method method, int child, const QVariantList &params) = 0;
+ virtual QVariant virtual_hook(const QVariant &data);
+ virtual QAccessible2Interface *interface_cast(QAccessible2::InterfaceType)
+ { return 0; }
+};
+
+
+class Q_GUI_EXPORT QAccessibleEvent : public QEvent
+{
+public:
+ inline QAccessibleEvent(Type type, int child);
+ inline int child() const { return c; }
+ inline QString value() const { return val; }
+ inline void setValue(const QString &aText) { val = aText; }
+
+private:
+ int c;
+ QString val;
+};
+
+inline QAccessibleEvent::QAccessibleEvent(Type atype, int achild)
+ : QEvent(atype), c(achild) {}
+
+#define QAccessibleInterface_iid "com.trolltech.Qt.QAccessibleInterface"
+Q_DECLARE_INTERFACE(QAccessibleInterface, QAccessibleInterface_iid)
+
+#endif // QT_NO_ACCESSIBILITY
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QACCESSIBLE_H
diff --git a/src/gui/accessible/qaccessible2.cpp b/src/gui/accessible/qaccessible2.cpp
new file mode 100644
index 0000000000..465ea4e747
--- /dev/null
+++ b/src/gui/accessible/qaccessible2.cpp
@@ -0,0 +1,181 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qaccessible2.h"
+#include "qapplication.h"
+#include "qclipboard.h"
+
+#ifndef QT_NO_ACCESSIBILITY
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \namespace QAccessible2
+ \ingroup accessibility
+ \internal
+ \preliminary
+
+ \brief The QAccessible2 namespace defines constants relating to
+ IAccessible2-based interfaces
+
+ \link http://www.linux-foundation.org/en/Accessibility/IAccessible2 IAccessible2 Specification \endlink
+*/
+
+/*!
+ \class QAccessibleTextInterface
+
+ \ingroup accessibility
+ \internal
+ \preliminary
+
+ \brief The QAccessibleTextInterface class implements support for
+ the IAccessibleText interface.
+
+ \link http://www.linux-foundation.org/en/Accessibility/IAccessible2 IAccessible2 Specification \endlink
+*/
+
+/*!
+ \class QAccessibleEditableTextInterface
+ \ingroup accessibility
+ \internal
+ \preliminary
+
+ \brief The QAccessibleEditableTextInterface class implements support for
+ the IAccessibleEditableText interface.
+
+ \link http://www.linux-foundation.org/en/Accessibility/IAccessible2 IAccessible2 Specification \endlink
+*/
+
+/*!
+ \class QAccessibleSimpleEditableTextInterface
+ \ingroup accessibility
+ \internal
+ \preliminary
+
+ \brief The QAccessibleSimpleEditableTextInterface class is a convenience class for
+ text-based widgets.
+
+ \link http://www.linux-foundation.org/en/Accessibility/IAccessible2 IAccessible2 Specification \endlink
+*/
+
+/*!
+ \class QAccessibleValueInterface
+ \ingroup accessibility
+ \internal
+ \preliminary
+
+ \brief The QAccessibleValueInterface class implements support for
+ the IAccessibleValue interface.
+
+ \link http://www.linux-foundation.org/en/Accessibility/IAccessible2 IAccessible2 Specification \endlink
+*/
+
+QAccessibleSimpleEditableTextInterface::QAccessibleSimpleEditableTextInterface(
+ QAccessibleInterface *accessibleInterface)
+ : iface(accessibleInterface)
+{
+ Q_ASSERT(iface);
+}
+
+#ifndef QT_NO_CLIPBOARD
+static QString textForRange(QAccessibleInterface *iface, int startOffset, int endOffset)
+{
+ return iface->text(QAccessible::Value, 0).mid(startOffset, endOffset - startOffset);
+}
+#endif
+
+void QAccessibleSimpleEditableTextInterface::copyText(int startOffset, int endOffset)
+{
+#ifdef QT_NO_CLIPBOARD
+ Q_UNUSED(startOffset);
+ Q_UNUSED(endOffset);
+#else
+ QApplication::clipboard()->setText(textForRange(iface, startOffset, endOffset));
+#endif
+}
+
+void QAccessibleSimpleEditableTextInterface::deleteText(int startOffset, int endOffset)
+{
+ QString txt = iface->text(QAccessible::Value, 0);
+ txt.remove(startOffset, endOffset - startOffset);
+ iface->setText(QAccessible::Value, 0, txt);
+}
+
+void QAccessibleSimpleEditableTextInterface::insertText(int offset, const QString &text)
+{
+ QString txt = iface->text(QAccessible::Value, 0);
+ txt.insert(offset, text);
+ iface->setText(QAccessible::Value, 0, txt);
+}
+
+void QAccessibleSimpleEditableTextInterface::cutText(int startOffset, int endOffset)
+{
+#ifdef QT_NO_CLIPBOARD
+ Q_UNUSED(startOffset);
+ Q_UNUSED(endOffset);
+#else
+ QString sub = textForRange(iface, startOffset, endOffset);
+ deleteText(startOffset, endOffset);
+ QApplication::clipboard()->setText(sub);
+#endif
+}
+
+void QAccessibleSimpleEditableTextInterface::pasteText(int offset)
+{
+#ifdef QT_NO_CLIPBOARD
+ Q_UNUSED(offset);
+#else
+ QString txt = iface->text(QAccessible::Value, 0);
+ txt.insert(offset, QApplication::clipboard()->text());
+ iface->setText(QAccessible::Value, 0, txt);
+#endif
+}
+
+void QAccessibleSimpleEditableTextInterface::replaceText(int startOffset, int endOffset, const QString &text)
+{
+ QString txt = iface->text(QAccessible::Value, 0);
+ txt.replace(startOffset, endOffset - startOffset, text);
+ iface->setText(QAccessible::Value, 0, txt);
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_ACCESSIBILITY
diff --git a/src/gui/accessible/qaccessible2.h b/src/gui/accessible/qaccessible2.h
new file mode 100644
index 0000000000..1e7b5f083c
--- /dev/null
+++ b/src/gui/accessible/qaccessible2.h
@@ -0,0 +1,217 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QACCESSIBLE2_H
+#define QACCESSIBLE2_H
+
+#include <QtGui/qaccessible.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+#ifndef QT_NO_ACCESSIBILITY
+
+namespace QAccessible2
+{
+ enum CoordinateType
+ {
+ RelativeToScreen = 0,
+ RelativeToParent = 1
+ };
+
+ enum BoundaryType {
+ CharBoundary,
+ WordBoundary,
+ SentenceBoundary,
+ ParagraphBoundary,
+ LineBoundary,
+ NoBoundary
+ };
+}
+
+class Q_GUI_EXPORT QAccessible2Interface
+{
+public:
+ virtual ~QAccessible2Interface() {}
+};
+
+// catch-all functions. If an accessible class doesn't implement interface T, return 0
+inline QAccessible2Interface *qAccessibleValueCastHelper() { return 0; }
+inline QAccessible2Interface *qAccessibleTextCastHelper() { return 0; }
+inline QAccessible2Interface *qAccessibleEditableTextCastHelper() { return 0; }
+inline QAccessible2Interface *qAccessibleTableCastHelper() { return 0; }
+
+#define Q_ACCESSIBLE_OBJECT \
+ public: \
+ QAccessible2Interface *interface_cast(QAccessible2::InterfaceType t) \
+ { \
+ switch (t) { \
+ case QAccessible2::TextInterface: \
+ return qAccessibleTextCastHelper(); \
+ case QAccessible2::EditableTextInterface: \
+ return qAccessibleEditableTextCastHelper(); \
+ case QAccessible2::ValueInterface: \
+ return qAccessibleValueCastHelper(); \
+ case QAccessible2::TableInterface: \
+ return qAccessibleTableCastHelper(); \
+ } \
+ return 0; \
+ } \
+ private:
+
+class Q_GUI_EXPORT QAccessibleTextInterface: public QAccessible2Interface
+{
+public:
+ inline QAccessible2Interface *qAccessibleTextCastHelper() { return this; }
+
+ virtual ~QAccessibleTextInterface() {}
+
+ virtual void addSelection(int startOffset, int endOffset) = 0;
+ virtual QString attributes(int offset, int *startOffset, int *endOffset) = 0;
+ virtual int cursorPosition() = 0;
+ virtual QRect characterRect(int offset, QAccessible2::CoordinateType coordType) = 0;
+ virtual int selectionCount() = 0;
+ virtual int offsetAtPoint(const QPoint &point, QAccessible2::CoordinateType coordType) = 0;
+ virtual void selection(int selectionIndex, int *startOffset, int *endOffset) = 0;
+ virtual QString text(int startOffset, int endOffset) = 0;
+ virtual QString textBeforeOffset (int offset, QAccessible2::BoundaryType boundaryType,
+ int *startOffset, int *endOffset) = 0;
+ virtual QString textAfterOffset(int offset, QAccessible2::BoundaryType boundaryType,
+ int *startOffset, int *endOffset) = 0;
+ virtual QString textAtOffset(int offset, QAccessible2::BoundaryType boundaryType,
+ int *startOffset, int *endOffset) = 0;
+ virtual void removeSelection(int selectionIndex) = 0;
+ virtual void setCursorPosition(int position) = 0;
+ virtual void setSelection(int selectionIndex, int startOffset, int endOffset) = 0;
+ virtual int characterCount() = 0;
+ virtual void scrollToSubstring(int startIndex, int endIndex) = 0;
+};
+
+class Q_GUI_EXPORT QAccessibleEditableTextInterface: public QAccessible2Interface
+{
+public:
+ inline QAccessible2Interface *qAccessibleEditableTextCastHelper() { return this; }
+
+ virtual ~QAccessibleEditableTextInterface() {}
+
+ virtual void copyText(int startOffset, int endOffset) = 0;
+ virtual void deleteText(int startOffset, int endOffset) = 0;
+ virtual void insertText(int offset, const QString &text) = 0;
+ virtual void cutText(int startOffset, int endOffset) = 0;
+ virtual void pasteText(int offset) = 0;
+ virtual void replaceText(int startOffset, int endOffset, const QString &text) = 0;
+ virtual void setAttributes(int startOffset, int endOffset, const QString &attributes) = 0;
+};
+
+class Q_GUI_EXPORT QAccessibleSimpleEditableTextInterface: public QAccessibleEditableTextInterface
+{
+public:
+ QAccessibleSimpleEditableTextInterface(QAccessibleInterface *accessibleInterface);
+
+ void copyText(int startOffset, int endOffset);
+ void deleteText(int startOffset, int endOffset);
+ void insertText(int offset, const QString &text);
+ void cutText(int startOffset, int endOffset);
+ void pasteText(int offset);
+ void replaceText(int startOffset, int endOffset, const QString &text);
+ inline void setAttributes(int, int, const QString &) {}
+
+private:
+ QAccessibleInterface *iface;
+};
+
+class Q_GUI_EXPORT QAccessibleValueInterface: public QAccessible2Interface
+{
+public:
+ inline QAccessible2Interface *qAccessibleValueCastHelper() { return this; }
+
+ virtual ~QAccessibleValueInterface() {}
+
+ virtual QVariant currentValue() = 0;
+ virtual void setCurrentValue(const QVariant &value) = 0;
+ virtual QVariant maximumValue() = 0;
+ virtual QVariant minimumValue() = 0;
+};
+
+class Q_GUI_EXPORT QAccessibleTableInterface: public QAccessible2Interface
+{
+public:
+ inline QAccessible2Interface *qAccessibleTableCastHelper() { return this; }
+
+ virtual QAccessibleInterface *accessibleAt(int row, int column) = 0;
+ virtual QAccessibleInterface *caption() = 0;
+ virtual int childIndex(int rowIndex, int columnIndex) = 0;
+ virtual QString columnDescription(int column) = 0;
+ virtual int columnSpan(int row, int column) = 0;
+ virtual QAccessibleInterface *columnHeader() = 0;
+ virtual int columnIndex(int childIndex) = 0;
+ virtual int columnCount() = 0;
+ virtual int rowCount() = 0;
+ virtual int selectedColumnCount() = 0;
+ virtual int selectedRowCount() = 0;
+ virtual QString rowDescription(int row) = 0;
+ virtual int rowSpan(int row, int column) = 0;
+ virtual QAccessibleInterface *rowHeader() = 0;
+ virtual int rowIndex(int childIndex) = 0;
+ virtual int selectedRows(int maxRows, QList<int> *rows) = 0;
+ virtual int selectedColumns(int maxColumns, QList<int> *columns) = 0;
+ virtual QAccessibleInterface *summary() = 0;
+ virtual bool isColumnSelected(int column) = 0;
+ virtual bool isRowSelected(int row) = 0;
+ virtual bool isSelected(int row, int column) = 0;
+ virtual void selectRow(int row) = 0;
+ virtual void selectColumn(int column) = 0;
+ virtual void unselectRow(int row) = 0;
+ virtual void unselectColumn(int column) = 0;
+ virtual void cellAtIndex(int index, int *row, int *column, int *rowSpan,
+ int *columnSpan, bool *isSelected) = 0;
+};
+
+#endif // QT_NO_ACCESSIBILITY
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif
diff --git a/src/gui/accessible/qaccessible_mac.mm b/src/gui/accessible/qaccessible_mac.mm
new file mode 100644
index 0000000000..b6412c2113
--- /dev/null
+++ b/src/gui/accessible/qaccessible_mac.mm
@@ -0,0 +1,2608 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qaccessible.h"
+
+#ifndef QT_NO_ACCESSIBILITY
+#include "qaccessible_mac_p.h"
+#include "qhash.h"
+#include "qset.h"
+#include "qpointer.h"
+#include "qapplication.h"
+#include "qmainwindow.h"
+#include "qtextdocument.h"
+#include "qdebug.h"
+#include "qabstractslider.h"
+#include "qsplitter.h"
+#include "qtabwidget.h"
+#include "qlistview.h"
+#include "qtableview.h"
+#include "qdockwidget.h"
+
+#include <private/qt_mac_p.h>
+#include <private/qwidget_p.h>
+#include <CoreFoundation/CoreFoundation.h>
+
+QT_BEGIN_NAMESPACE
+
+/*
+ Set up platform defines. There is a one-to-one correspondence between the
+ Carbon and Cocoa roles and attributes, but the prefix and type changes.
+*/
+#ifdef QT_MAC_USE_COCOA
+typedef NSString * const QAXRoleType;
+#define QAXApplicationRole NSAccessibilityApplicationRole
+#define QAXButtonRole NSAccessibilityButtonRole
+#define QAXCancelAction NSAccessibilityCancelAction
+#define QAXCancelAction NSAccessibilityCancelAction
+#define QAXCheckBoxRole NSAccessibilityCheckBoxRole
+#define QAXChildrenAttribute NSAccessibilityChildrenAttribute
+#define QAXChildrenAttribute NSAccessibilityChildrenAttribute
+#define QAXChildrenAttribute NSAccessibilityChildrenAttribute
+#define QAXCloseButtonAttribute NSAccessibilityCloseButtonAttribute
+#define QAXCloseButtonAttribute NSAccessibilityCloseButtonAttribute
+#define QAXColumnRole NSAccessibilityColumnRole
+#define QAXConfirmAction NSAccessibilityConfirmAction
+#define QAXConfirmAction NSAccessibilityConfirmAction
+#define QAXContentsAttribute NSAccessibilityContentsAttribute
+#define QAXContentsAttribute NSAccessibilityContentsAttribute
+#define QAXDecrementAction NSAccessibilityDecrementAction
+#define QAXDecrementAction NSAccessibilityDecrementAction
+#define QAXDecrementArrowSubrole NSAccessibilityDecrementArrowSubrole
+#define QAXDecrementPageSubrole NSAccessibilityDecrementPageSubrole
+#define QAXDescriptionAttribute NSAccessibilityDescriptionAttribute
+#define QAXDescriptionAttribute NSAccessibilityDescriptionAttribute
+#define QAXDescriptionAttribute NSAccessibilityDescriptionAttribute
+#define QAXDescriptionAttribute NSAccessibilityDescriptionAttribute
+#define QAXEnabledAttribute NSAccessibilityEnabledAttribute
+#define QAXEnabledAttribute NSAccessibilityEnabledAttribute
+#define QAXExpandedAttribute NSAccessibilityExpandedAttribute
+#define QAXFocusedAttribute NSAccessibilityFocusedAttribute
+#define QAXFocusedAttribute NSAccessibilityFocusedAttribute
+#define QAXFocusedAttribute NSAccessibilityFocusedAttribute
+#define QAXFocusedUIElementChangedNotification NSAccessibilityFocusedUIElementChangedNotification
+#define QAXFocusedWindowChangedNotification NSAccessibilityFocusedWindowChangedNotification
+#define QAXGroupRole NSAccessibilityGroupRole
+#define QAXGrowAreaAttribute NSAccessibilityGrowAreaAttribute
+#define QAXGrowAreaAttribute NSAccessibilityGrowAreaAttribute
+#define QAXGrowAreaRole NSAccessibilityGrowAreaRole
+#define QAXHelpAttribute NSAccessibilityHelpAttribute
+#define QAXHelpAttribute NSAccessibilityHelpAttribute
+#define QAXHelpAttribute NSAccessibilityHelpAttribute
+#define QAXHorizontalOrientationValue NSAccessibilityHorizontalOrientationValue
+#define QAXHorizontalScrollBarAttribute NSAccessibilityHorizontalScrollBarAttribute
+#define QAXHorizontalScrollBarAttribute NSAccessibilityHorizontalScrollBarAttribute
+#define QAXIncrementAction NSAccessibilityIncrementAction
+#define QAXIncrementAction NSAccessibilityIncrementAction
+#define QAXIncrementArrowSubrole NSAccessibilityIncrementArrowSubrole
+#define QAXIncrementPageSubrole NSAccessibilityIncrementPageSubrole
+#define QAXIncrementorRole NSAccessibilityIncrementorRole
+#define QAXLinkedUIElementsAttribute NSAccessibilityLinkedUIElementsAttribute
+#define QAXLinkedUIElementsAttribute NSAccessibilityLinkedUIElementsAttribute
+#define QAXListRole NSAccessibilityListRole
+#define QAXMainAttribute NSAccessibilityMainAttribute
+#define QAXMaxValueAttribute NSAccessibilityMaxValueAttribute
+#define QAXMaxValueAttribute NSAccessibilityMaxValueAttribute
+#define QAXMenuBarRole NSAccessibilityMenuBarRole
+#define QAXMenuButtonRole NSAccessibilityMenuButtonRole
+#define QAXMenuClosedNotification NSAccessibilityMenuClosedNotification
+#define QAXMenuItemRole NSAccessibilityMenuItemRole
+#define QAXMenuOpenedNotification NSAccessibilityMenuOpenedNotification
+#define QAXMenuRole NSAccessibilityMenuRole
+#define QAXMinValueAttribute NSAccessibilityMinValueAttribute
+#define QAXMinValueAttribute NSAccessibilityMinValueAttribute
+#define QAXMinimizeButtonAttribute NSAccessibilityMinimizeButtonAttribute
+#define QAXMinimizeButtonAttribute NSAccessibilityMinimizeButtonAttribute
+#define QAXMinimizedAttribute NSAccessibilityMinimizedAttribute
+#define QAXMinimizedAttribute NSAccessibilityMinimizedAttribute
+#define QAXNextContentsAttribute NSAccessibilityNextContentsAttribute
+#define QAXNextContentsAttribute NSAccessibilityNextContentsAttribute
+#define QAXOrientationAttribute NSAccessibilityOrientationAttribute
+#define QAXOrientationAttribute NSAccessibilityOrientationAttribute
+#define QAXOrientationAttribute NSAccessibilityOrientationAttribute
+#define QAXParentAttribute NSAccessibilityParentAttribute
+#define QAXPickAction NSAccessibilityPickAction
+#define QAXPickAction NSAccessibilityPickAction
+#define QAXPopUpButtonRole NSAccessibilityPopUpButtonRole
+#define QAXPositionAttribute NSAccessibilityPositionAttribute
+#define QAXPositionAttribute NSAccessibilityPositionAttribute
+#define QAXPressAction NSAccessibilityPressAction
+#define QAXPressAction NSAccessibilityPressAction
+#define QAXPreviousContentsAttribute NSAccessibilityPreviousContentsAttribute
+#define QAXPreviousContentsAttribute NSAccessibilityPreviousContentsAttribute
+#define QAXPreviousContentsAttribute NSAccessibilityPreviousContentsAttribute
+#define QAXProgressIndicatorRole NSAccessibilityProgressIndicatorRole
+#define QAXRadioButtonRole NSAccessibilityRadioButtonRole
+#define QAXRoleAttribute NSAccessibilityRoleAttribute
+#define QAXRoleAttribute NSAccessibilityRoleAttribute
+#define QAXRoleDescriptionAttribute NSAccessibilityRoleDescriptionAttribute
+#define QAXRowRole NSAccessibilityRowRole
+#define QAXRowsAttribute NSAccessibilityRowsAttribute
+#define QAXRowsAttribute NSAccessibilityRowsAttribute
+#define QAXScrollAreaRole NSAccessibilityScrollAreaRole
+#define QAXScrollAreaRole NSAccessibilityScrollAreaRole
+#define QAXScrollAreaRole NSAccessibilityScrollAreaRole
+#define QAXScrollBarRole NSAccessibilityScrollBarRole
+#define QAXSelectedAttribute NSAccessibilitySelectedAttribute
+#define QAXSelectedChildrenAttribute NSAccessibilitySelectedChildrenAttribute
+#define QAXSelectedRowsAttribute NSAccessibilitySelectedRowsAttribute
+#define QAXSelectedRowsAttribute NSAccessibilitySelectedRowsAttribute
+#define QAXSizeAttribute NSAccessibilitySizeAttribute
+#define QAXSizeAttribute NSAccessibilitySizeAttribute
+#define QAXSliderRole NSAccessibilitySliderRole
+#define QAXSplitGroupRole NSAccessibilitySplitGroupRole
+#define QAXSplitterRole NSAccessibilitySplitterRole
+#define QAXSplitterRole NSAccessibilitySplitterRole
+#define QAXSplitterRole NSAccessibilitySplitterRole
+#define QAXSplittersAttribute NSAccessibilitySplittersAttribute
+#define QAXSplittersAttribute NSAccessibilitySplittersAttribute
+#define QAXStaticTextRole NSAccessibilityStaticTextRole
+#define QAXStaticTextRole NSAccessibilityStaticTextRole
+#define QAXSubroleAttribute NSAccessibilitySubroleAttribute
+#define QAXSubroleAttribute NSAccessibilitySubroleAttribute
+#define QAXSubroleAttribute NSAccessibilitySubroleAttribute
+#define QAXTabGroupRole NSAccessibilityTabGroupRole
+#define QAXTabGroupRole NSAccessibilityTabGroupRole
+#define QAXTableRole NSAccessibilityTableRole
+#define QAXTabsAttribute NSAccessibilityTabsAttribute
+#define QAXTabsAttribute NSAccessibilityTabsAttribute
+#define QAXTextFieldRole NSAccessibilityTextFieldRole
+#define QAXTextFieldRole NSAccessibilityTextFieldRole
+#define QAXTitleAttribute NSAccessibilityTitleAttribute
+#define QAXTitleAttribute NSAccessibilityTitleAttribute
+#define QAXTitleAttribute NSAccessibilityTitleAttribute
+#define QAXTitleAttribute NSAccessibilityTitleAttribute
+#define QAXTitleUIElementAttribute NSAccessibilityTitleUIElementAttribute
+#define QAXTitleUIElementAttribute NSAccessibilityTitleUIElementAttribute
+#define QAXToolbarButtonAttribute NSAccessibilityToolbarButtonAttribute
+#define QAXToolbarButtonAttribute NSAccessibilityToolbarButtonAttribute
+#define QAXToolbarRole NSAccessibilityToolbarRole
+#define QAXTopLevelUIElementAttribute NSAccessibilityTopLevelUIElementAttribute
+#define QAXTopLevelUIElementAttribute NSAccessibilityTopLevelUIElementAttribute
+#define QAXTopLevelUIElementAttribute NSAccessibilityTopLevelUIElementAttribute
+#define QAXUnknownRole NSAccessibilityUnknownRole
+#define QAXUnknownRole NSAccessibilityUnknownRole
+#define QAXValueAttribute NSAccessibilityValueAttribute
+#define QAXValueAttribute NSAccessibilityValueAttribute
+#define QAXValueAttribute NSAccessibilityValueAttribute
+#define QAXValueAttribute NSAccessibilityValueAttribute
+#define QAXValueChangedNotification NSAccessibilityValueChangedNotification
+#define QAXValueIndicatorRole NSAccessibilityValueIndicatorRole
+#define QAXVerticalOrientationValue NSAccessibilityVerticalOrientationValue
+#define QAXVerticalScrollBarAttribute NSAccessibilityVerticalScrollBarAttribute
+#define QAXVerticalScrollBarAttribute NSAccessibilityVerticalScrollBarAttribute
+#define QAXVisibleRowsAttribute NSAccessibilityVisibleRowsAttribute
+#define QAXVisibleRowsAttribute NSAccessibilityVisibleRowsAttribute
+#define QAXWindowAttribute NSAccessibilityWindowAttribute
+#define QAXWindowAttribute NSAccessibilityWindowAttribute
+#define QAXWindowAttribute NSAccessibilityWindowAttribute
+#define QAXWindowCreatedNotification NSAccessibilityWindowCreatedNotification
+#define QAXWindowMovedNotification NSAccessibilityWindowMovedNotification
+#define QAXWindowRole NSAccessibilityWindowRole
+#define QAXZoomButtonAttribute NSAccessibilityZoomButtonAttribute
+#define QAXZoomButtonAttribute NSAccessibilityZoomButtonAttribute
+#else
+typedef CFStringRef const QAXRoleType;
+#define QAXApplicationRole kAXApplicationRole
+#define QAXButtonRole kAXButtonRole
+#define QAXCancelAction kAXCancelAction
+#define QAXCancelAction kAXCancelAction
+#define QAXCheckBoxRole kAXCheckBoxRole
+#define QAXChildrenAttribute kAXChildrenAttribute
+#define QAXChildrenAttribute kAXChildrenAttribute
+#define QAXChildrenAttribute kAXChildrenAttribute
+#define QAXCloseButtonAttribute kAXCloseButtonAttribute
+#define QAXCloseButtonAttribute kAXCloseButtonAttribute
+#define QAXColumnRole kAXColumnRole
+#define QAXConfirmAction kAXConfirmAction
+#define QAXConfirmAction kAXConfirmAction
+#define QAXContentsAttribute kAXContentsAttribute
+#define QAXContentsAttribute kAXContentsAttribute
+#define QAXDecrementAction kAXDecrementAction
+#define QAXDecrementAction kAXDecrementAction
+#define QAXDecrementArrowSubrole kAXDecrementArrowSubrole
+#define QAXDecrementPageSubrole kAXDecrementPageSubrole
+#define QAXDescriptionAttribute kAXDescriptionAttribute
+#define QAXDescriptionAttribute kAXDescriptionAttribute
+#define QAXDescriptionAttribute kAXDescriptionAttribute
+#define QAXDescriptionAttribute kAXDescriptionAttribute
+#define QAXEnabledAttribute kAXEnabledAttribute
+#define QAXEnabledAttribute kAXEnabledAttribute
+#define QAXExpandedAttribute kAXExpandedAttribute
+#define QAXFocusedAttribute kAXFocusedAttribute
+#define QAXFocusedAttribute kAXFocusedAttribute
+#define QAXFocusedAttribute kAXFocusedAttribute
+#define QAXFocusedUIElementChangedNotification kAXFocusedUIElementChangedNotification
+#define QAXFocusedWindowChangedNotification kAXFocusedWindowChangedNotification
+#define QAXGroupRole kAXGroupRole
+#define QAXGrowAreaAttribute kAXGrowAreaAttribute
+#define QAXGrowAreaAttribute kAXGrowAreaAttribute
+#define QAXGrowAreaRole kAXGrowAreaRole
+#define QAXHelpAttribute kAXHelpAttribute
+#define QAXHelpAttribute kAXHelpAttribute
+#define QAXHelpAttribute kAXHelpAttribute
+#define QAXHorizontalOrientationValue kAXHorizontalOrientationValue
+#define QAXHorizontalScrollBarAttribute kAXHorizontalScrollBarAttribute
+#define QAXHorizontalScrollBarAttribute kAXHorizontalScrollBarAttribute
+#define QAXIncrementAction kAXIncrementAction
+#define QAXIncrementAction kAXIncrementAction
+#define QAXIncrementArrowSubrole kAXIncrementArrowSubrole
+#define QAXIncrementPageSubrole kAXIncrementPageSubrole
+#define QAXIncrementorRole kAXIncrementorRole
+#define QAXLinkedUIElementsAttribute kAXLinkedUIElementsAttribute
+#define QAXLinkedUIElementsAttribute kAXLinkedUIElementsAttribute
+#define QAXListRole kAXListRole
+#define QAXMainAttribute kAXMainAttribute
+#define QAXMaxValueAttribute kAXMaxValueAttribute
+#define QAXMaxValueAttribute kAXMaxValueAttribute
+#define QAXMenuBarRole kAXMenuBarRole
+#define QAXMenuButtonRole kAXMenuButtonRole
+#define QAXMenuClosedNotification kAXMenuClosedNotification
+#define QAXMenuItemRole kAXMenuItemRole
+#define QAXMenuOpenedNotification kAXMenuOpenedNotification
+#define QAXMenuRole kAXMenuRole
+#define QAXMinValueAttribute kAXMinValueAttribute
+#define QAXMinValueAttribute kAXMinValueAttribute
+#define QAXMinimizeButtonAttribute kAXMinimizeButtonAttribute
+#define QAXMinimizeButtonAttribute kAXMinimizeButtonAttribute
+#define QAXMinimizedAttribute kAXMinimizedAttribute
+#define QAXMinimizedAttribute kAXMinimizedAttribute
+#define QAXNextContentsAttribute kAXNextContentsAttribute
+#define QAXNextContentsAttribute kAXNextContentsAttribute
+#define QAXOrientationAttribute kAXOrientationAttribute
+#define QAXOrientationAttribute kAXOrientationAttribute
+#define QAXOrientationAttribute kAXOrientationAttribute
+#define QAXParentAttribute kAXParentAttribute
+#define QAXPickAction kAXPickAction
+#define QAXPickAction kAXPickAction
+#define QAXPopUpButtonRole kAXPopUpButtonRole
+#define QAXPositionAttribute kAXPositionAttribute
+#define QAXPositionAttribute kAXPositionAttribute
+#define QAXPressAction kAXPressAction
+#define QAXPressAction kAXPressAction
+#define QAXPreviousContentsAttribute kAXPreviousContentsAttribute
+#define QAXPreviousContentsAttribute kAXPreviousContentsAttribute
+#define QAXPreviousContentsAttribute kAXPreviousContentsAttribute
+#define QAXProgressIndicatorRole kAXProgressIndicatorRole
+#define QAXRadioButtonRole kAXRadioButtonRole
+#define QAXRoleAttribute kAXRoleAttribute
+#define QAXRoleAttribute kAXRoleAttribute
+#define QAXRoleDescriptionAttribute kAXRoleDescriptionAttribute
+#define QAXRowRole kAXRowRole
+#define QAXRowsAttribute kAXRowsAttribute
+#define QAXRowsAttribute kAXRowsAttribute
+#define QAXScrollAreaRole kAXScrollAreaRole
+#define QAXScrollAreaRole kAXScrollAreaRole
+#define QAXScrollAreaRole kAXScrollAreaRole
+#define QAXScrollBarRole kAXScrollBarRole
+#define QAXSelectedAttribute kAXSelectedAttribute
+#define QAXSelectedChildrenAttribute kAXSelectedChildrenAttribute
+#define QAXSelectedRowsAttribute kAXSelectedRowsAttribute
+#define QAXSelectedRowsAttribute kAXSelectedRowsAttribute
+#define QAXSizeAttribute kAXSizeAttribute
+#define QAXSizeAttribute kAXSizeAttribute
+#define QAXSliderRole kAXSliderRole
+#define QAXSplitGroupRole kAXSplitGroupRole
+#define QAXSplitterRole kAXSplitterRole
+#define QAXSplitterRole kAXSplitterRole
+#define QAXSplitterRole kAXSplitterRole
+#define QAXSplittersAttribute kAXSplittersAttribute
+#define QAXSplittersAttribute kAXSplittersAttribute
+#define QAXStaticTextRole kAXStaticTextRole
+#define QAXStaticTextRole kAXStaticTextRole
+#define QAXSubroleAttribute kAXSubroleAttribute
+#define QAXSubroleAttribute kAXSubroleAttribute
+#define QAXSubroleAttribute kAXSubroleAttribute
+#define QAXTabGroupRole kAXTabGroupRole
+#define QAXTabGroupRole kAXTabGroupRole
+#define QAXTableRole kAXTableRole
+#define QAXTabsAttribute kAXTabsAttribute
+#define QAXTabsAttribute kAXTabsAttribute
+#define QAXTextFieldRole kAXTextFieldRole
+#define QAXTextFieldRole kAXTextFieldRole
+#define QAXTitleAttribute kAXTitleAttribute
+#define QAXTitleAttribute kAXTitleAttribute
+#define QAXTitleAttribute kAXTitleAttribute
+#define QAXTitleAttribute kAXTitleAttribute
+#define QAXTitleUIElementAttribute kAXTitleUIElementAttribute
+#define QAXTitleUIElementAttribute kAXTitleUIElementAttribute
+#define QAXToolbarButtonAttribute kAXToolbarButtonAttribute
+#define QAXToolbarButtonAttribute kAXToolbarButtonAttribute
+#define QAXToolbarRole kAXToolbarRole
+#define QAXTopLevelUIElementAttribute kAXTopLevelUIElementAttribute
+#define QAXTopLevelUIElementAttribute kAXTopLevelUIElementAttribute
+#define QAXTopLevelUIElementAttribute kAXTopLevelUIElementAttribute
+#define QAXUnknownRole kAXUnknownRole
+#define QAXUnknownRole kAXUnknownRole
+#define QAXValueAttribute kAXValueAttribute
+#define QAXValueAttribute kAXValueAttribute
+#define QAXValueAttribute kAXValueAttribute
+#define QAXValueAttribute kAXValueAttribute
+#define QAXValueChangedNotification kAXValueChangedNotification
+#define QAXValueIndicatorRole kAXValueIndicatorRole
+#define QAXVerticalOrientationValue kAXVerticalOrientationValue
+#define QAXVerticalScrollBarAttribute kAXVerticalScrollBarAttribute
+#define QAXVerticalScrollBarAttribute kAXVerticalScrollBarAttribute
+#define QAXVisibleRowsAttribute kAXVisibleRowsAttribute
+#define QAXVisibleRowsAttribute kAXVisibleRowsAttribute
+#define QAXWindowAttribute kAXWindowAttribute
+#define QAXWindowAttribute kAXWindowAttribute
+#define QAXWindowAttribute kAXWindowAttribute
+#define QAXWindowCreatedNotification kAXWindowCreatedNotification
+#define QAXWindowMovedNotification kAXWindowMovedNotification
+#define QAXWindowRole kAXWindowRole
+#define QAXZoomButtonAttribute kAXZoomButtonAttribute
+#define QAXZoomButtonAttribute kAXZoomButtonAttribute
+#endif
+
+
+/*****************************************************************************
+ Externals
+ *****************************************************************************/
+extern bool qt_mac_is_macsheet(const QWidget *w); //qwidget_mac.cpp
+extern bool qt_mac_is_macdrawer(const QWidget *w); //qwidget_mac.cpp
+
+/*****************************************************************************
+ QAccessible Bindings
+ *****************************************************************************/
+//hardcoded bindings between control info and (known) QWidgets
+struct QAccessibleTextBinding {
+ int qt;
+ QAXRoleType mac;
+ bool settable;
+} text_bindings[][10] = {
+ { { QAccessible::MenuItem, QAXMenuItemRole, false },
+ { -1, 0, false }
+ },
+ { { QAccessible::MenuBar, QAXMenuBarRole, false },
+ { -1, 0, false }
+ },
+ { { QAccessible::ScrollBar, QAXScrollBarRole, false },
+ { -1, 0, false }
+ },
+ { { QAccessible::Grip, QAXGrowAreaRole, false },
+ { -1, 0, false }
+ },
+ { { QAccessible::Window, QAXWindowRole, false },
+ { -1, 0, false }
+ },
+ { { QAccessible::Dialog, QAXWindowRole, false },
+ { -1, 0, false }
+ },
+ { { QAccessible::AlertMessage, QAXWindowRole, false },
+ { -1, 0, false }
+ },
+ { { QAccessible::ToolTip, QAXWindowRole, false },
+ { -1, 0, false }
+ },
+ { { QAccessible::HelpBalloon, QAXWindowRole, false },
+ { -1, 0, false }
+ },
+ { { QAccessible::PopupMenu, QAXMenuRole, false },
+ { -1, 0, false }
+ },
+ { { QAccessible::Application, QAXApplicationRole, false },
+ { -1, 0, false }
+ },
+ { { QAccessible::Pane, QAXGroupRole, false },
+ { -1, 0, false }
+ },
+ { { QAccessible::Grouping, QAXGroupRole, false },
+ { -1, 0, false }
+ },
+ { { QAccessible::Separator, QAXSplitterRole, false },
+ { -1, 0, false }
+ },
+ { { QAccessible::ToolBar, QAXToolbarRole, false },
+ { -1, 0, false }
+ },
+ { { QAccessible::PageTab, QAXRadioButtonRole, false },
+ { -1, 0, false }
+ },
+ { { QAccessible::ButtonMenu, QAXMenuButtonRole, false },
+ { -1, 0, false }
+ },
+ { { QAccessible::ButtonDropDown, QAXPopUpButtonRole, false },
+ { -1, 0, false }
+ },
+ { { QAccessible::SpinBox, QAXIncrementorRole, false },
+ { -1, 0, false }
+ },
+ { { QAccessible::Slider, QAXSliderRole, false },
+ { -1, 0, false }
+ },
+ { { QAccessible::ProgressBar, QAXProgressIndicatorRole, false },
+ { -1, 0, false }
+ },
+ { { QAccessible::ComboBox, QAXPopUpButtonRole, false },
+ { -1, 0, false }
+ },
+ { { QAccessible::RadioButton, QAXRadioButtonRole, false },
+ { -1, 0, false }
+ },
+ { { QAccessible::CheckBox, QAXCheckBoxRole, false },
+ { -1, 0, false }
+ },
+ { { QAccessible::StaticText, QAXStaticTextRole, false },
+ { QAccessible::Name, QAXValueAttribute, false },
+ { -1, 0, false }
+ },
+ { { QAccessible::Table, QAXTableRole, false },
+ { -1, 0, false }
+ },
+ { { QAccessible::StatusBar, QAXStaticTextRole, false },
+ { -1, 0, false }
+ },
+ { { QAccessible::Column, QAXColumnRole, false },
+ { -1, 0, false }
+ },
+ { { QAccessible::ColumnHeader, QAXColumnRole, false },
+ { -1, 0, false }
+ },
+ { { QAccessible::Row, QAXRowRole, false },
+ { -1, 0, false }
+ },
+ { { QAccessible::RowHeader, QAXRowRole, false },
+ { -1, 0, false }
+ },
+ { { QAccessible::Cell, QAXTextFieldRole, false },
+ { -1, 0, false }
+ },
+ { { QAccessible::PushButton, QAXButtonRole, false },
+ { -1, 0, false }
+ },
+ { { QAccessible::EditableText, QAXTextFieldRole, true },
+ { -1, 0, false }
+ },
+ { { QAccessible::Link, QAXTextFieldRole, false },
+ { -1, 0, false }
+ },
+ { { QAccessible::Indicator, QAXValueIndicatorRole, false },
+ { -1, 0, false }
+ },
+ { { QAccessible::Splitter, QAXSplitGroupRole, false },
+ { -1, 0, false }
+ },
+ { { QAccessible::List, QAXListRole, false },
+ { -1, 0, false }
+ },
+ { { QAccessible::ListItem, QAXStaticTextRole, false },
+ { -1, 0, false }
+ },
+ { { QAccessible::Cell, QAXStaticTextRole, false },
+ { -1, 0, false }
+ },
+ { { -1, 0, false } }
+};
+
+class QAInterface;
+static CFStringRef macRole(const QAInterface &interface);
+
+QDebug operator<<(QDebug debug, const QAInterface &interface)
+{
+ if (interface.isValid() == false)
+ debug << "invalid interface";
+ else
+ debug << interface.object() << "id" << interface.id() << "role" << hex << interface.role();
+ return debug;
+}
+
+// The root of the Qt accessible hiearchy.
+static QObject *rootObject = 0;
+
+
+bool QAInterface::operator==(const QAInterface &other) const
+{
+ if (isValid() == false || other.isValid() == false)
+ return (isValid() && other.isValid());
+
+ // walk up the parent chain, comparing child indexes, until we reach
+ // an interface that has a QObject.
+ QAInterface currentThis = *this;
+ QAInterface currentOther = other;
+
+ while (currentThis.object() == 0) {
+ if (currentOther.object() != 0)
+ return false;
+
+ // fail if the child indexes in the two hirearchies don't match.
+ if (currentThis.parent().indexOfChild(currentThis) !=
+ currentOther.parent().indexOfChild(currentOther))
+ return false;
+
+ currentThis = currentThis.parent();
+ currentOther = currentOther.parent();
+ }
+
+ return (currentThis.object() == currentOther.object() && currentThis.id() == currentOther.id());
+}
+
+bool QAInterface::operator!=(const QAInterface &other) const
+{
+ return !operator==(other);
+}
+
+uint qHash(const QAInterface &item)
+{
+ if (item.isValid())
+ return qHash(item.object()) + qHash(item.id());
+ else
+ return qHash(item.cachedObject()) + qHash(item.id());
+}
+
+QAInterface QAInterface::navigate(RelationFlag relation, int entry) const
+{
+ if (!checkValid())
+ return QAInterface();
+
+ // On a QAccessibleInterface that handles its own children we can short-circut
+ // the navigation if this QAInterface refers to one of the children:
+ if (child != 0) {
+ // The Ancestor interface will always be the same QAccessibleInterface with
+ // a child value of 0.
+ if (relation == QAccessible::Ancestor)
+ return QAInterface(*this, 0);
+
+ // The child hiearchy is only one level deep, so navigating to a child
+ // of a child is not possible.
+ if (relation == QAccessible::Child) {
+ return QAInterface();
+ }
+ }
+ QAccessibleInterface *child_iface = 0;
+
+ const int status = base.interface->navigate(relation, entry, &child_iface);
+
+ if (status == -1)
+ return QAInterface(); // not found;
+
+ // Check if target is a child of this interface.
+ if (!child_iface) {
+ return QAInterface(*this, status);
+ } else {
+ // Target is child_iface or a child of that (status decides).
+ return QAInterface(child_iface, status);
+ }
+}
+
+QAElement::QAElement()
+:elementRef(0)
+{}
+
+QAElement::QAElement(AXUIElementRef elementRef)
+:elementRef(elementRef)
+{
+ if (elementRef != 0) {
+ CFRetain(elementRef);
+ CFRetain(object());
+ }
+}
+
+QAElement::QAElement(const QAElement &element)
+:elementRef(element.elementRef)
+{
+ if (elementRef != 0) {
+ CFRetain(elementRef);
+ CFRetain(object());
+ }
+}
+
+QAElement::QAElement(HIObjectRef object, int child)
+ :elementRef(
+#ifndef QT_MAC_USE_COCOA
+ AXUIElementCreateWithHIObjectAndIdentifier(object, child)
+#endif
+)
+{
+#ifndef QT_MAC_USE_COCOA
+ if (object == 0) {
+ elementRef = 0; // Create invalid QAElement.
+ } else {
+ elementRef = AXUIElementCreateWithHIObjectAndIdentifier(object, child);
+ CFRetain(object);
+ }
+#else
+ Q_UNUSED(object);
+ Q_UNUSED(child);
+#endif
+}
+
+QAElement::~QAElement()
+{
+ if (elementRef != 0) {
+ CFRelease(object());
+ CFRelease(elementRef);
+ }
+}
+
+void QAElement::operator=(const QAElement &other)
+{
+ if (*this == other)
+ return;
+
+ if (elementRef != 0) {
+ CFRelease(object());
+ CFRelease(elementRef);
+ }
+
+ elementRef = other.elementRef;
+
+ if (elementRef != 0) {
+ CFRetain(elementRef);
+ CFRetain(object());
+ }
+}
+
+bool QAElement::operator==(const QAElement &other) const
+{
+ if (elementRef == 0 || other.elementRef == 0)
+ return (elementRef == other.elementRef);
+
+ return CFEqual(elementRef, other.elementRef);
+}
+
+uint qHash(QAElement element)
+{
+ return qHash(element.object()) + qHash(element.id());
+}
+
+#ifndef QT_MAC_USE_COCOA
+static QInterfaceFactory *createFactory(const QAInterface &interface);
+#endif
+Q_GLOBAL_STATIC(QAccessibleHierarchyManager, accessibleHierarchyManager);
+
+/*
+ Reomves all accessibility info accosiated with the sender object.
+*/
+void QAccessibleHierarchyManager::objectDestroyed(QObject *object)
+{
+ HIObjectRef hiObject = qobjectHiobjectHash.value(object);
+ delete qobjectElementHash.value(object);
+ qobjectElementHash.remove(object);
+ hiobjectInterfaceHash.remove(hiObject);
+}
+
+/*
+ Removes all stored items.
+*/
+void QAccessibleHierarchyManager::reset()
+{
+ qDeleteAll(qobjectElementHash);
+ qobjectElementHash.clear();
+ hiobjectInterfaceHash.clear();
+ qobjectHiobjectHash.clear();
+}
+
+QAccessibleHierarchyManager *QAccessibleHierarchyManager::instance()
+{
+ return accessibleHierarchyManager();
+}
+
+#ifndef QT_MAC_USE_COCOA
+static bool isItemView(const QAInterface &interface)
+{
+ QObject *object = interface.object();
+ return (interface.role() == QAccessible::List || interface.role() == QAccessible::Table
+ || (object && qobject_cast<QAbstractItemView *>(interface.object()))
+ || (object && object->objectName() == QLatin1String("qt_scrollarea_viewport")
+ && qobject_cast<QAbstractItemView *>(object->parent())));
+}
+#endif
+
+static bool isTabWidget(const QAInterface &interface)
+{
+ if (QObject *object = interface.object())
+ return (object->inherits("QTabWidget") && interface.id() == 0);
+ return false;
+}
+
+static bool isStandaloneTabBar(const QAInterface &interface)
+{
+ QObject *object = interface.object();
+ if (interface.role() == QAccessible::PageTabList && object)
+ return (qobject_cast<QTabWidget *>(object->parent()) == 0);
+
+ return false;
+}
+
+static bool isEmbeddedTabBar(const QAInterface &interface)
+{
+ QObject *object = interface.object();
+ if (interface.role() == QAccessible::PageTabList && object)
+ return (qobject_cast<QTabWidget *>(object->parent()));
+
+ return false;
+}
+
+/*
+ Decides if a QAInterface is interesting from an accessibility users point of view.
+*/
+bool isItInteresting(const QAInterface &interface)
+{
+ // Mac accessibility does not have an attribute that corresponds to the Invisible/Offscreen
+ // state, so we disable the interface here.
+ const QAccessible::State state = interface.state();
+ if (state & QAccessible::Invisible ||
+ state & QAccessible::Offscreen )
+ return false;
+
+ const QAccessible::Role role = interface.role();
+
+ if (QObject * const object = interface.object()) {
+ const QString className = QLatin1String(object->metaObject()->className());
+
+ // VoiceOver focusing on tool tips can be confusing. The contents of the
+ // tool tip is avalible through the description attribute anyway, so
+ // we disable accessibility for tool tips.
+ if (className == QLatin1String("QTipLabel"))
+ return false;
+
+ // Hide TabBars that has a QTabWidget parent (the tab widget handles the accessibility)
+ if (isEmbeddedTabBar(interface))
+ return false;
+
+ // Hide docked dockwidgets. ### causes infinitie loop in the apple accessibility code.
+ /* if (QDockWidget *dockWidget = qobject_cast<QDockWidget *>(object)) {
+ if (dockWidget->isFloating() == false)
+ return false;
+ }
+ */
+ }
+
+ // Client is a generic role returned by plain QWidgets or other
+ // widgets that does not have separate QAccessible interface, such
+ // as the TabWidget. Return false unless macRole gives the interface
+ // a special role.
+ if (role == QAccessible::Client && macRole(interface) == CFStringRef(QAXUnknownRole))
+ return false;
+
+ // Some roles are not interesting:
+ if (role == QAccessible::Border || // QFrame
+ role == QAccessible::Application || // We use the system-provided application element.
+ role == QAccessible::MenuItem) // The system also provides the menu items.
+ return false;
+
+ // It is probably better to access the toolbar buttons directly than having
+ // to navigate through the toolbar.
+ if (role == QAccessible::ToolBar)
+ return false;
+
+ return true;
+}
+
+QAElement QAccessibleHierarchyManager::registerInterface(QObject *object, int child)
+{
+#ifndef QT_MAC_USE_COCOA
+ return registerInterface(QAInterface(QAccessible::queryAccessibleInterface(object), child));
+#else
+ Q_UNUSED(object);
+ Q_UNUSED(child);
+ return QAElement();
+#endif
+}
+
+/*
+ Creates a QAXUIelement that corresponds to the given QAInterface.
+*/
+QAElement QAccessibleHierarchyManager::registerInterface(const QAInterface &interface)
+{
+#ifndef QT_MAC_USE_COCOA
+ if (interface.isValid() == false)
+ return QAElement();
+ QAInterface objectInterface = interface.objectInterface();
+
+ QObject * qobject = objectInterface.object();
+ HIObjectRef hiobject = objectInterface.hiObject();
+ if (qobject == 0 || hiobject == 0)
+ return QAElement();
+
+ if (qobjectElementHash.contains(qobject) == false) {
+ registerInterface(qobject, hiobject, createFactory(interface));
+ HIObjectSetAccessibilityIgnored(hiobject, !isItInteresting(interface));
+ }
+
+ return QAElement(hiobject, interface.id());
+#else
+ Q_UNUSED(interface);
+ return QAElement();
+#endif
+}
+
+#ifndef QT_MAC_USE_COCOA
+#include "qaccessible_mac_carbon.cpp"
+#endif
+
+void QAccessibleHierarchyManager::registerInterface(QObject * qobject, HIObjectRef hiobject, QInterfaceFactory *interfaceFactory)
+{
+#ifndef QT_MAC_USE_COCOA
+ if (qobjectElementHash.contains(qobject) == false) {
+ qobjectElementHash.insert(qobject, interfaceFactory);
+ qobjectHiobjectHash.insert(qobject, hiobject);
+ connect(qobject, SIGNAL(destroyed(QObject *)), SLOT(objectDestroyed(QObject *)));
+ }
+
+ if (hiobjectInterfaceHash.contains(hiobject) == false) {
+ hiobjectInterfaceHash.insert(hiobject, interfaceFactory);
+ installAcessibilityEventHandler(hiobject);
+ }
+#else
+ Q_UNUSED(qobject);
+ Q_UNUSED(hiobject);
+ Q_UNUSED(interfaceFactory);
+#endif
+}
+
+void QAccessibleHierarchyManager::registerChildren(const QAInterface &interface)
+{
+ QObject * const object = interface.object();
+ if (object == 0)
+ return;
+
+ QInterfaceFactory *interfaceFactory = qobjectElementHash.value(object);
+
+ if (interfaceFactory == 0)
+ return;
+
+ interfaceFactory->registerChildren();
+}
+
+QAInterface QAccessibleHierarchyManager::lookup(const AXUIElementRef &element)
+{
+ if (element == 0)
+ return QAInterface();
+#ifndef QT_MAC_USE_COCOA
+ HIObjectRef hiObject = AXUIElementGetHIObject(element);
+
+ QInterfaceFactory *factory = hiobjectInterfaceHash.value(hiObject);
+ if (factory == 0) {
+ return QAInterface();
+ }
+
+ UInt64 id;
+ AXUIElementGetIdentifier(element, &id);
+ return factory->interface(id);
+#else
+ return QAInterface();
+#endif;
+}
+
+QAInterface QAccessibleHierarchyManager::lookup(const QAElement &element)
+{
+ return lookup(element.element());
+}
+
+QAElement QAccessibleHierarchyManager::lookup(const QAInterface &interface)
+{
+ if (interface.isValid() == false)
+ return QAElement();
+
+ QInterfaceFactory *factory = qobjectElementHash.value(interface.objectInterface().object());
+ if (factory == 0)
+ return QAElement();
+
+ return factory->element(interface);
+}
+
+QAElement QAccessibleHierarchyManager::lookup(QObject * const object, int id)
+{
+ QInterfaceFactory *factory = qobjectElementHash.value(object);
+ if (factory == 0)
+ return QAElement();
+
+ return factory->element(id);
+}
+
+/*
+ Standard interface mapping, return the stored interface
+ or HIObjectRef, and there is an one-to-one mapping between
+ the identifier and child.
+*/
+class QStandardInterfaceFactory : public QInterfaceFactory
+{
+public:
+ QStandardInterfaceFactory(const QAInterface &interface)
+ : m_interface(interface), object(interface.hiObject())
+ {
+ CFRetain(object);
+ }
+
+ ~QStandardInterfaceFactory()
+ {
+ CFRelease(object);
+ }
+
+
+ QAInterface interface(UInt64 identifier)
+ {
+ const int child = identifier;
+ return QAInterface(m_interface, child);
+ }
+
+ QAElement element(int id)
+ {
+ return QAElement(object, id);
+ }
+
+ QAElement element(const QAInterface &interface)
+ {
+ if (interface.object() == 0)
+ return QAElement();
+ return QAElement(object, interface.id());
+ }
+
+ void registerChildren()
+ {
+ const int childCount = m_interface.childCount();
+ for (int i = 1; i <= childCount; ++i) {
+ accessibleHierarchyManager()->registerInterface(m_interface.navigate(QAccessible::Child, i));
+ }
+ }
+
+private:
+ QAInterface m_interface;
+ HIObjectRef object;
+};
+
+/*
+ Interface mapping where that creates one HIObject for each interface child.
+*/
+class QMultipleHIObjectFactory : public QInterfaceFactory
+{
+public:
+ QMultipleHIObjectFactory(const QAInterface &interface)
+ : m_interface(interface)
+ { }
+
+ ~QMultipleHIObjectFactory()
+ {
+ foreach (HIObjectRef object, objects) {
+ CFRelease(object);
+ }
+ }
+
+ QAInterface interface(UInt64 identifier)
+ {
+ const int child = identifier;
+ return QAInterface(m_interface, child);
+ }
+
+ QAElement element(int child)
+ {
+ if (child == 0)
+ return QAElement(m_interface.hiObject(), 0);
+
+ if (child > objects.count())
+ return QAElement();
+
+ return QAElement(objects.at(child - 1), child);
+ }
+
+ void registerChildren()
+ {
+#ifndef QT_MAC_USE_COCOA
+ const int childCount = m_interface.childCount();
+ for (int i = 1; i <= childCount; ++i) {
+ HIObjectRef hiobject;
+ HIObjectCreate(kObjectQtAccessibility, 0, &hiobject);
+ objects.append(hiobject);
+ accessibleHierarchyManager()->registerInterface(m_interface.object(), hiobject, this);
+ HIObjectSetAccessibilityIgnored(hiobject, !isItInteresting(m_interface.navigate(QAccessible::Child, i)));
+ }
+#endif
+ }
+
+private:
+ QAInterface m_interface;
+ QList<HIObjectRef> objects;
+};
+
+class QItemViewInterfaceFactory : public QInterfaceFactory
+{
+public:
+ QItemViewInterfaceFactory(const QAInterface &interface)
+ : m_interface(interface), object(interface.hiObject())
+ {
+ CFRetain(object);
+ columnCount = 0;
+ if (QTableView * tableView = qobject_cast<QTableView *>(interface.parent().object())) {
+ if (tableView->model())
+ columnCount = tableView->model()->columnCount();
+ if (tableView->verticalHeader())
+ ++columnCount;
+ }
+ }
+
+ ~QItemViewInterfaceFactory()
+ {
+ CFRelease(object);
+ }
+
+ QAInterface interface(UInt64 identifier)
+ {
+ if (identifier == 0)
+ return m_interface;
+
+ if (m_interface.role() == QAccessible::List)
+ return m_interface.childAt(identifier);
+
+ if (m_interface.role() == QAccessible::Table) {
+ const int index = identifier;
+ if (index == 0)
+ return m_interface; // return the item view interface.
+
+ const int rowIndex = (index - 1) / (columnCount + 1);
+ const int cellIndex = (index - 1) % (columnCount + 1);
+/*
+ qDebug() << "index" << index;
+ qDebug() << "rowIndex" << rowIndex;
+ qDebug() << "cellIndex" << cellIndex;
+*/
+ const QAInterface rowInterface = m_interface.childAt(rowIndex + 1);
+
+ if ((cellIndex) == 0) // Is it a row?
+ return rowInterface;
+ else {
+ return rowInterface.childAt(cellIndex);
+ }
+ }
+
+ return QAInterface();
+ }
+
+ QAElement element(int id)
+ {
+ if (id != 0) {
+ return QAElement();
+ }
+ return QAElement(object, 0);
+ }
+
+ QAElement element(const QAInterface &interface)
+ {
+ if (interface.object() && interface.object() == m_interface.object()) {
+ return QAElement(object, 0);
+ } else if (m_interface.role() == QAccessible::List) {
+ if (interface.parent().object() && interface.parent().object() == m_interface.object())
+ return QAElement(object, m_interface.indexOfChild(interface));
+ } else if (m_interface.role() == QAccessible::Table) {
+ QAInterface currentInterface = interface;
+ int index = 0;
+
+ while (currentInterface.isValid() && currentInterface.object() == 0) {
+ const QAInterface parentInterface = currentInterface.parent();
+/*
+ qDebug() << "current index" << index;
+ qDebug() << "current interface" << interface;
+
+ qDebug() << "parent interface" << parentInterface;
+ qDebug() << "grandparent interface" << parentInterface.parent();
+ qDebug() << "childCount" << interface.childCount();
+ qDebug() << "index of child" << parentInterface.indexOfChild(currentInterface);
+*/
+ index += ((parentInterface.indexOfChild(currentInterface) - 1) * (currentInterface.childCount() + 1)) + 1;
+ currentInterface = parentInterface;
+// qDebug() << "new current interface" << currentInterface;
+ }
+ if (currentInterface.object() == m_interface.object())
+ return QAElement(object, index);
+
+
+ }
+ return QAElement();
+ }
+
+ void registerChildren()
+ {
+ // Item view child interfraces don't have their own qobjects, so there is nothing to register here.
+ }
+
+private:
+ QAInterface m_interface;
+ HIObjectRef object;
+ int columnCount; // for table views;
+};
+
+#ifndef QT_MAC_USE_COCOA
+static bool managesChildren(const QAInterface &interface)
+{
+ return (interface.childCount() > 0 && interface.childAt(1).id() > 0);
+}
+
+static QInterfaceFactory *createFactory(const QAInterface &interface)
+{
+ if (isItemView(interface)) {
+ return new QItemViewInterfaceFactory(interface);
+ } if (managesChildren(interface)) {
+ return new QMultipleHIObjectFactory(interface);
+ }
+
+ return new QStandardInterfaceFactory(interface);
+}
+#endif
+
+QList<QAElement> lookup(const QList<QAInterface> &interfaces)
+{
+ QList<QAElement> elements;
+ foreach (const QAInterface &interface, interfaces)
+ if (interface.isValid()) {
+ const QAElement element = accessibleHierarchyManager()->lookup(interface);
+ if (element.isValid())
+ elements.append(element);
+ }
+ return elements;
+}
+
+// Debug output helpers:
+/*
+static QString nameForEventKind(UInt32 kind)
+{
+ switch(kind) {
+ case kEventAccessibleGetChildAtPoint: return QString("GetChildAtPoint"); break;
+ case kEventAccessibleGetAllAttributeNames: return QString("GetAllAttributeNames"); break;
+ case kEventAccessibleGetNamedAttribute: return QString("GetNamedAttribute"); break;
+ case kEventAccessibleSetNamedAttribute: return QString("SetNamedAttribute"); break;
+ case kEventAccessibleGetAllActionNames: return QString("GetAllActionNames"); break;
+ case kEventAccessibleGetFocusedChild: return QString("GetFocusedChild"); break;
+ default:
+ return QString("Unknown accessibility event type: %1").arg(kind);
+ break;
+ };
+}
+*/
+#ifndef QT_MAC_USE_COCOA
+static bool qt_mac_append_cf_uniq(CFMutableArrayRef array, CFTypeRef value)
+{
+ if (value == 0)
+ return false;
+
+ CFRange range;
+ range.location = 0;
+ range.length = CFArrayGetCount(array);
+ if(!CFArrayContainsValue(array, range, value)) {
+ CFArrayAppendValue(array, value);
+ return true;
+ }
+ return false;
+}
+
+static OSStatus setAttributeValue(EventRef event, const QList<QAElement> &elements)
+{
+ CFMutableArrayRef array = CFArrayCreateMutable(0, 0, &kCFTypeArrayCallBacks);
+ foreach (const QAElement &element, elements) {
+ if (element.isValid())
+ CFArrayAppendValue(array, element.element());
+ }
+
+ const OSStatus err = SetEventParameter(event, kEventParamAccessibleAttributeValue,
+ typeCFTypeRef, sizeof(array), &array);
+ CFRelease(array);
+ return err;
+}
+#endif //QT_MAC_USE_COCOA
+
+/*
+ Gets the AccessibleObject parameter from an event.
+*/
+static inline AXUIElementRef getAccessibleObjectParameter(EventRef event)
+{
+ AXUIElementRef element;
+ GetEventParameter(event, kEventParamAccessibleObject, typeCFTypeRef, 0,
+ sizeof(element), 0, &element);
+ return element;
+}
+
+/*
+ The application event handler makes sure that all top-level qt windows are registered
+ before any accessibility events are handeled.
+*/
+#ifndef QT_MAC_USE_COCOA
+static OSStatus applicationEventHandler(EventHandlerCallRef next_ref, EventRef event, void *)
+{
+ QAInterface rootInterface(QAccessible::queryAccessibleInterface(rootObject ? rootObject : qApp), 0);
+ accessibleHierarchyManager()->registerChildren(rootInterface);
+
+ return CallNextEventHandler(next_ref, event);
+}
+
+/*
+ Returns the value for element by combining the QAccessibility::Checked and
+ QAccessibility::Mixed flags into an int value that the Mac accessibilty
+ system understands. This works for check boxes, radio buttons, and the like.
+ The return values are:
+ 0: unchecked
+ 1: checked
+ 2: undecided
+*/
+static int buttonValue(QAInterface element)
+{
+ const QAccessible::State state = element.state();
+ if (state & QAccessible::Mixed)
+ return 2;
+ else if(state & QAccessible::Checked)
+ return 1;
+ else
+ return 0;
+}
+
+static QString getValue(const QAInterface &interface)
+{
+ const QAccessible::Role role = interface.role();
+ if (role == QAccessible::RadioButton || role == QAccessible::CheckBox)
+ return QString::number(buttonValue(interface));
+ else
+ return interface.text(QAccessible::Value);
+}
+#endif //QT_MAC_USE_COCOA
+
+/*
+ Translates a QAccessible::Role into a mac accessibility role.
+*/
+static CFStringRef macRole(const QAInterface &interface)
+{
+ const QAccessible::Role qtRole = interface.role();
+
+// qDebug() << "role for" << interface.object() << "interface role" << hex << qtRole;
+
+ // Qt accessibility: QAccessible::Splitter contains QAccessible::Grip.
+ // Mac accessibility: AXSplitGroup contains AXSplitter.
+ if (qtRole == QAccessible::Grip) {
+ const QAInterface parent = interface.parent();
+ if (parent.isValid() && parent.role() == QAccessible::Splitter)
+ return CFStringRef(QAXSplitterRole);
+ }
+
+ // Tab widgets and standalone tab bars get the kAXTabGroupRole. Accessibility
+ // for tab bars emebedded in a tab widget is handled by the tab widget.
+ if (isTabWidget(interface) || isStandaloneTabBar(interface))
+ return kAXTabGroupRole;
+
+ if (QObject *object = interface.object()) {
+ // ### The interface for an abstract scroll area returns the generic "Client"
+ // role, so we have to to an extra detect on the QObject here.
+ if (object->inherits("QAbstractScrollArea") && interface.id() == 0)
+ return CFStringRef(QAXScrollAreaRole);
+
+ if (object->inherits("QDockWidget"))
+ return CFStringRef(QAXUnknownRole);
+ }
+
+ int i = 0;
+ int testRole = text_bindings[i][0].qt;
+ while (testRole != -1) {
+ if (testRole == qtRole)
+ return CFStringRef(text_bindings[i][0].mac);
+ ++i;
+ testRole = text_bindings[i][0].qt;
+ }
+
+// qDebug() << "got unknown role!" << interface << interface.parent();
+
+ return CFStringRef(QAXUnknownRole);
+}
+
+/*
+ Translates a QAccessible::Role and an attribute name into a QAccessible::Text, taking into
+ account execptions listed in text_bindings.
+*/
+#ifndef QT_MAC_USE_COCOA
+static int textForRoleAndAttribute(QAccessible::Role role, CFStringRef attribute)
+{
+ // Search for exception, return it if found.
+ int testRole = text_bindings[0][0].qt;
+ int i = 0;
+ while (testRole != -1) {
+ if (testRole == role) {
+ int j = 1;
+ int qtRole = text_bindings[i][j].qt;
+ CFStringRef testAttribute = CFStringRef(text_bindings[i][j].mac);
+ while (qtRole != -1) {
+ if (CFStringCompare(attribute, testAttribute, 0) == kCFCompareEqualTo) {
+ return (QAccessible::Text)qtRole;
+ }
+ ++j;
+ testAttribute = CFStringRef(text_bindings[i][j].mac); /// ### custom compare
+ qtRole = text_bindings[i][j].qt; /// ### custom compare
+ }
+ break;
+ }
+ ++i;
+ testRole = text_bindings[i][0].qt;
+ }
+
+ // Return default mappping
+ if (CFStringCompare(attribute, CFStringRef(QAXTitleAttribute), 0) == kCFCompareEqualTo)
+ return QAccessible::Name;
+ else if (CFStringCompare(attribute, CFStringRef(QAXValueAttribute), 0) == kCFCompareEqualTo)
+ return QAccessible::Value;
+ else if (CFStringCompare(attribute, CFStringRef(QAXHelpAttribute), 0) == kCFCompareEqualTo)
+ return QAccessible::Help;
+#if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4)
+ else if (CFStringCompare(attribute, CFStringRef(QAXDescriptionAttribute), 0) == kCFCompareEqualTo)
+ return QAccessible::Description;
+#endif
+ else
+ return -1;
+}
+
+/*
+ Returns the subrole string constant for the interface if it has one,
+ else returns an empty string.
+*/
+static QCFString subrole(const QAInterface &interface)
+{
+ const QAInterface parent = interface.parent();
+ if (parent.isValid() == false)
+ return QCFString();
+
+ if (parent.role() == QAccessible::ScrollBar) {
+ QCFString subrole;
+ switch(interface.id()) {
+ case 1: subrole = CFStringRef(QAXDecrementArrowSubrole); break;
+ case 2: subrole = CFStringRef(QAXDecrementPageSubrole); break;
+ case 4: subrole = CFStringRef(QAXIncrementPageSubrole); break;
+ case 5: subrole = CFStringRef(QAXIncrementArrowSubrole); break;
+ default:
+ break;
+ }
+ return subrole;
+ }
+ return QCFString();
+}
+
+// Gets the scroll bar orientation by asking the QAbstractSlider object directly.
+static Qt::Orientation scrollBarOrientation(const QAInterface &scrollBar)
+{
+ QObject *const object = scrollBar.object();
+ if (QAbstractSlider * const sliderObject = qobject_cast<QAbstractSlider * const>(object))
+ return sliderObject->orientation();
+
+ return Qt::Vertical; // D'oh! The interface wasn't a scroll bar.
+}
+
+static QAInterface scrollAreaGetScrollBarInterface(const QAInterface &scrollArea, Qt::Orientation orientation)
+{
+ if (macRole(scrollArea) != CFStringRef(CFStringRef(QAXScrollAreaRole)))
+ return QAInterface();
+
+ // Child 1 is the contents widget, 2 and 3 are the scroll bar containers wich contains possible scroll bars.
+ for (int i = 2; i <= 3; ++i) {
+ QAInterface scrollBarContainer = scrollArea.childAt(i);
+ for (int i = 1; i <= scrollBarContainer.childCount(); ++i) {
+ QAInterface scrollBar = scrollBarContainer.childAt(i);
+ if (scrollBar.isValid() &&
+ scrollBar.role() == QAccessible::ScrollBar &&
+ scrollBarOrientation(scrollBar) == orientation)
+ return scrollBar;
+ }
+ }
+
+ return QAInterface();
+}
+
+static bool scrollAreaHasScrollBar(const QAInterface &scrollArea, Qt::Orientation orientation)
+{
+ return scrollAreaGetScrollBarInterface(scrollArea, orientation).isValid();
+}
+
+static QAElement scrollAreaGetScrollBar(const QAInterface &scrollArea, Qt::Orientation orientation)
+{
+ return accessibleHierarchyManager()->lookup(scrollAreaGetScrollBarInterface(scrollArea, orientation));
+}
+
+static QAElement scrollAreaGetContents(const QAInterface &scrollArea)
+{
+ // Child 1 is the contents widget,
+ return accessibleHierarchyManager()->lookup(scrollArea.navigate(QAccessible::Child, 1));
+}
+
+static QAElement tabWidgetGetContents(const QAInterface &interface)
+{
+ // A kAXTabGroup has a kAXContents attribute, which consists of the
+ // ui elements for the current tab page. Get the current tab page
+ // from the QStackedWidget, where the current visible page can
+ // be found at index 1.
+ QAInterface stackedWidget = interface.childAt(1);
+ accessibleHierarchyManager()->registerChildren(stackedWidget);
+ QAInterface tabPageInterface = stackedWidget.childAt(1);
+ return accessibleHierarchyManager()->lookup(tabPageInterface);
+}
+
+static QList<QAElement> tabBarGetTabs(const QAInterface &interface)
+{
+ // Get the tabs by searching for children with the "PageTab" role.
+ // This filters out the left/right navigation buttons.
+ accessibleHierarchyManager()->registerChildren(interface);
+ QList<QAElement> tabs;
+ const int numChildren = interface.childCount();
+ for (int i = 1; i < numChildren + 1; ++i) {
+ QAInterface child = interface.navigate(QAccessible::Child, i);
+ if (child.isValid() && child.role() == QAccessible::PageTab) {
+ tabs.append(accessibleHierarchyManager()->lookup(child));
+ }
+ }
+ return tabs;
+}
+
+static QList<QAElement> tabWidgetGetTabs(const QAInterface &interface)
+{
+ // Each QTabWidget has two children, a QStackedWidget and a QTabBar.
+ // Get the tabs from the QTabBar.
+ return tabBarGetTabs(interface.childAt(2));
+}
+
+static QList<QAElement> tabWidgetGetChildren(const QAInterface &interface)
+{
+ // The children for a kAXTabGroup should consist of the tabs and the
+ // contents of the current open tab page.
+ QList<QAElement> children = tabWidgetGetTabs(interface);
+ children += tabWidgetGetContents(interface);
+ return children;
+}
+#endif //QT_MAC_USE_COCOA
+
+/*
+ Returns the label (buddy) interface for interface, or 0 if it has none.
+*/
+/*
+static QAInterface findLabel(const QAInterface &interface)
+{
+ return interface.navigate(QAccessible::Label, 1);
+}
+*/
+/*
+ Returns a list of interfaces this interface labels, or an empty list if it doesn't label any.
+*/
+/*
+static QList<QAInterface> findLabelled(const QAInterface &interface)
+{
+ QList<QAInterface> interfaceList;
+
+ int count = 1;
+ const QAInterface labelled = interface.navigate(QAccessible::Labelled, count);
+ while (labelled.isValid()) {
+ interfaceList.append(labelled);
+ ++count;
+ }
+ return interfaceList;
+}
+*/
+/*
+ Tests if the given QAInterface has data for a mac attribute.
+*/
+#ifndef QT_MAC_USE_COCOA
+static bool supportsAttribute(CFStringRef attribute, const QAInterface &interface)
+{
+ const int text = textForRoleAndAttribute(interface.role(), attribute);
+
+ // Special case: Static texts don't have a title.
+ if (interface.role() == QAccessible::StaticText && attribute == CFStringRef(QAXTitleAttribute))
+ return false;
+
+ // Return true if we the attribute matched a QAccessible::Role and we get text for that role from the interface.
+ if (text != -1) {
+ if (text == QAccessible::Value) // Special case for Value, see getValue()
+ return !getValue(interface).isEmpty();
+ else
+ return !interface.text((QAccessible::Text)text).isEmpty();
+ }
+
+ if (CFStringCompare(attribute, CFStringRef(QAXChildrenAttribute), 0) == kCFCompareEqualTo) {
+ if (interface.childCount() > 0)
+ return true;
+ }
+
+ if (CFStringCompare(attribute, CFStringRef(QAXSubroleAttribute), 0) == kCFCompareEqualTo) {
+ return (subrole(interface) != QCFString());
+ }
+
+ return false;
+}
+
+static void appendIfSupported(CFMutableArrayRef array, CFStringRef attribute, const QAInterface &interface)
+{
+ if (supportsAttribute(attribute, interface))
+ qt_mac_append_cf_uniq(array, attribute);
+}
+
+/*
+ Returns the names of the attributes the give QAInterface supports.
+*/
+static OSStatus getAllAttributeNames(EventRef event, const QAInterface &interface, EventHandlerCallRef next_ref)
+{
+ // Call system event handler.
+ OSStatus err = CallNextEventHandler(next_ref, event);
+ if(err != noErr && err != eventNotHandledErr)
+ return err;
+ CFMutableArrayRef attrs = 0;
+ GetEventParameter(event, kEventParamAccessibleAttributeNames, typeCFMutableArrayRef, 0,
+ sizeof(attrs), 0, &attrs);
+
+ if (!attrs)
+ return eventNotHandledErr;
+
+ // Append attribute names that are always supported.
+ qt_mac_append_cf_uniq(attrs, CFStringRef(QAXPositionAttribute));
+ qt_mac_append_cf_uniq(attrs, CFStringRef(QAXSizeAttribute));
+ qt_mac_append_cf_uniq(attrs, CFStringRef(QAXRoleAttribute));
+ qt_mac_append_cf_uniq(attrs, CFStringRef(QAXEnabledAttribute));
+ qt_mac_append_cf_uniq(attrs, CFStringRef(QAXWindowAttribute));
+#if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4)
+ qt_mac_append_cf_uniq(attrs, CFStringRef(QAXTopLevelUIElementAttribute));
+#endif
+
+ // Append these names if the QInterafceItem returns any data for them.
+ appendIfSupported(attrs, CFStringRef(QAXTitleAttribute), interface);
+ appendIfSupported(attrs, CFStringRef(QAXValueAttribute), interface);
+#if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4)
+ appendIfSupported(attrs, CFStringRef(QAXDescriptionAttribute), interface);
+ appendIfSupported(attrs, CFStringRef(QAXLinkedUIElementsAttribute), interface);
+#endif
+ appendIfSupported(attrs, CFStringRef(QAXHelpAttribute), interface);
+ appendIfSupported(attrs, CFStringRef(QAXTitleUIElementAttribute), interface);
+ appendIfSupported(attrs, CFStringRef(QAXChildrenAttribute), interface);
+ appendIfSupported(attrs, CFStringRef(QAXSubroleAttribute), interface);
+
+ // Append attribute names based on the interaface role.
+ switch (interface.role()) {
+ case QAccessible::Window:
+ qt_mac_append_cf_uniq(attrs, CFStringRef(QAXMainAttribute));
+ qt_mac_append_cf_uniq(attrs, CFStringRef(QAXMinimizedAttribute));
+ qt_mac_append_cf_uniq(attrs, CFStringRef(QAXCloseButtonAttribute));
+ qt_mac_append_cf_uniq(attrs, CFStringRef(QAXZoomButtonAttribute));
+ qt_mac_append_cf_uniq(attrs, CFStringRef(QAXMinimizeButtonAttribute));
+ qt_mac_append_cf_uniq(attrs, CFStringRef(QAXToolbarButtonAttribute));
+ qt_mac_append_cf_uniq(attrs, CFStringRef(QAXGrowAreaAttribute));
+ break;
+ case QAccessible::RadioButton:
+ case QAccessible::CheckBox:
+ qt_mac_append_cf_uniq(attrs, CFStringRef(QAXMinValueAttribute));
+ qt_mac_append_cf_uniq(attrs, CFStringRef(QAXMaxValueAttribute));
+ break;
+ case QAccessible::ScrollBar:
+ qt_mac_append_cf_uniq(attrs, CFStringRef(QAXOrientationAttribute));
+ break;
+ case QAccessible::Splitter:
+ qt_mac_append_cf_uniq(attrs, CFStringRef(QAXSplittersAttribute));
+ break;
+ case QAccessible::Table:
+ qt_mac_append_cf_uniq(attrs, CFStringRef(QAXRowsAttribute));
+ qt_mac_append_cf_uniq(attrs, CFStringRef(QAXVisibleRowsAttribute));
+ qt_mac_append_cf_uniq(attrs, CFStringRef(QAXSelectedRowsAttribute));
+ break;
+ default:
+ break;
+ }
+
+ // Append attribute names based on the mac accessibility role.
+ const QCFString mac_role = macRole(interface);
+ if (mac_role == CFStringRef(QAXSplitterRole)) {
+ qt_mac_append_cf_uniq(attrs, CFStringRef(QAXPreviousContentsAttribute));
+ qt_mac_append_cf_uniq(attrs, CFStringRef(QAXNextContentsAttribute));
+ qt_mac_append_cf_uniq(attrs, CFStringRef(QAXOrientationAttribute));
+ } else if (mac_role == CFStringRef(QAXScrollAreaRole)) {
+ if (scrollAreaHasScrollBar(interface, Qt::Horizontal))
+ qt_mac_append_cf_uniq(attrs, CFStringRef(QAXHorizontalScrollBarAttribute));
+ if (scrollAreaHasScrollBar(interface, Qt::Vertical))
+ qt_mac_append_cf_uniq(attrs, CFStringRef(QAXVerticalScrollBarAttribute));
+ qt_mac_append_cf_uniq(attrs, CFStringRef(QAXContentsAttribute));
+ } else if (mac_role == CFStringRef(QAXTabGroupRole)) {
+ qt_mac_append_cf_uniq(attrs, CFStringRef(QAXTabsAttribute));
+ // Only tab widgets can have the contents attribute, there is no way of getting
+ // the contents from a QTabBar.
+ if (isTabWidget(interface))
+ qt_mac_append_cf_uniq(attrs, CFStringRef(QAXContentsAttribute));
+ }
+
+ return noErr;
+}
+
+static void handleStringAttribute(EventRef event, QAccessible::Text text, const QAInterface &interface)
+{
+ QString str = interface.text(text);
+ if (str.isEmpty())
+ return;
+
+ // Remove any html markup from the text string, or VoiceOver will read the html tags.
+ static QTextDocument document;
+ document.setHtml(str);
+ str = document.toPlainText();
+
+ CFStringRef cfstr = QCFString::toCFStringRef(str);
+ SetEventParameter(event, kEventParamAccessibleAttributeValue, typeCFStringRef, sizeof(cfstr), &cfstr);
+}
+
+/*
+ Handles the parent attribute for a interface.
+ There are basically three cases here:
+ 1. interface is a HIView and has only HIView children.
+ 2. interface is a HIView but has children that is not a HIView
+ 3. interface is not a HIView.
+*/
+static OSStatus handleChildrenAttribute(EventHandlerCallRef next_ref, EventRef event, QAInterface &interface)
+{
+ // Add the children for this interface to the global QAccessibelHierachyManager.
+ accessibleHierarchyManager()->registerChildren(interface);
+
+ if (isTabWidget(interface)) {
+ QList<QAElement> children = tabWidgetGetChildren(interface);
+ const int childCount = children.count();
+
+ CFMutableArrayRef array = 0;
+ array = CFArrayCreateMutable(0, 0, &kCFTypeArrayCallBacks);
+ for (int i = 0; i < childCount; ++i) {
+ qt_mac_append_cf_uniq(array, children.at(i).element());
+ }
+
+ OSStatus err;
+ err = SetEventParameter(event, kEventParamAccessibleAttributeValue, typeCFArrayRef, sizeof(array), &array);
+ if (err != noErr)
+ qWarning("Qt:Internal error (%s:%d)", __FILE__, __LINE__);
+
+ return noErr;
+ }
+
+ const QList<QAElement> children = lookup(interface.children());
+ const int childCount = children.count();
+
+ OSStatus err = eventNotHandledErr;
+ if (interface.isHIView())
+ err = CallNextEventHandler(next_ref, event);
+
+ CFMutableArrayRef array = 0;
+ int arraySize = 0;
+ if (err == noErr) {
+ CFTypeRef obj = 0;
+ err = GetEventParameter(event, kEventParamAccessibleAttributeValue, typeCFTypeRef, NULL , sizeof(obj), NULL, &obj);
+ if (err == noErr && obj != 0) {
+ array = (CFMutableArrayRef)obj;
+ arraySize = CFArrayGetCount(array);
+ }
+ }
+
+ if (array) {
+ CFArrayRemoveAllValues(array);
+ for (int i = 0; i < childCount; ++i) {
+ qt_mac_append_cf_uniq(array, children.at(i).element());
+ }
+ } else {
+ array = CFArrayCreateMutable(0, 0, &kCFTypeArrayCallBacks);
+ for (int i = 0; i < childCount; ++i) {
+ qt_mac_append_cf_uniq(array, children.at(i).element());
+ }
+
+ err = SetEventParameter(event, kEventParamAccessibleAttributeValue, typeCFArrayRef, sizeof(array), &array);
+ if (err != noErr)
+ qWarning("Qt:Internal error (%s:%d)", __FILE__, __LINE__);
+ }
+
+ return noErr;
+}
+
+/*
+
+*/
+static OSStatus handleParentAttribute(EventHandlerCallRef next_ref, EventRef event, const QAInterface &interface)
+{
+ OSStatus err = eventNotHandledErr;
+ if (interface.isHIView()) {
+ err = CallNextEventHandler(next_ref, event);
+ }
+ if (err == noErr)
+ return err;
+
+ const QAInterface parentInterface = interface.navigate(QAccessible::Ancestor, 1);
+ const QAElement parentElement = accessibleHierarchyManager()->lookup(parentInterface);
+
+ if (parentElement.isValid() == false)
+ return eventNotHandledErr;
+
+ AXUIElementRef elementRef = parentElement.element();
+ SetEventParameter(event, kEventParamAccessibleAttributeValue, typeCFTypeRef, sizeof(elementRef), &elementRef);
+ return noErr;
+}
+#endif
+
+struct IsWindowTest
+{
+ static inline bool test(const QAInterface &interface)
+ {
+ return (interface.role() == QAccessible::Window);
+ }
+};
+
+struct IsWindowAndNotDrawerOrSheetTest
+{
+ static inline bool test(const QAInterface &interface)
+ {
+ QWidget * const widget = qobject_cast<QWidget*>(interface.object());
+ return (interface.role() == QAccessible::Window &&
+ widget && widget->isWindow() &&
+ !qt_mac_is_macdrawer(widget) &&
+ !qt_mac_is_macsheet(widget));
+ }
+};
+
+/*
+ Navigates up the iterfaces ancestor hierachy until a QAccessibleInterface that
+ passes the Test is found. If we reach a interface that is a HIView we stop the
+ search and call AXUIElementCopyAttributeValue.
+*/
+template <typename TestType>
+OSStatus navigateAncestors(EventHandlerCallRef next_ref, EventRef event, const QAInterface &interface, CFStringRef attribute)
+{
+ if (interface.isHIView())
+ return CallNextEventHandler(next_ref, event);
+
+ QAInterface current = interface;
+ QAElement element;
+ while (current.isValid()) {
+ if (TestType::test(interface)) {
+ element = accessibleHierarchyManager()->lookup(current);
+ break;
+ }
+
+ // If we reach an InterfaceItem that is a HiView we can hand of the search to
+ // the system event handler. This is the common case.
+ if (current.isHIView()) {
+ CFTypeRef value = 0;
+ const QAElement currentElement = accessibleHierarchyManager()->lookup(current);
+ AXError err = AXUIElementCopyAttributeValue(currentElement.element(), attribute, &value);
+ AXUIElementRef newElement = (AXUIElementRef)value;
+
+ if (err == noErr)
+ element = QAElement(newElement);
+
+ if (newElement != 0)
+ CFRelease(newElement);
+ break;
+ }
+
+ QAInterface next = current.parent();
+ if (next.isValid() == false)
+ break;
+ if (next == current)
+ break;
+ current = next;
+ }
+
+ if (element.isValid() == false)
+ return eventNotHandledErr;
+
+
+ AXUIElementRef elementRef = element.element();
+ SetEventParameter(event, kEventParamAccessibleAttributeValue, typeCFTypeRef,
+ sizeof(elementRef), &elementRef);
+ return noErr;
+}
+
+/*
+ Returns the top-level window for an interface, which is the closest ancestor interface that
+ has the Window role, but is not a sheet or a drawer.
+*/
+#ifndef QT_MAC_USE_COCOA
+static OSStatus handleWindowAttribute(EventHandlerCallRef next_ref, EventRef event, const QAInterface &interface)
+{
+ return navigateAncestors<IsWindowAndNotDrawerOrSheetTest>(next_ref, event, interface, CFStringRef(QAXWindowAttribute));
+}
+
+/*
+ Returns the top-level window for an interface, which is the closest ancestor interface that
+ has the Window role. (Can also be a sheet or a drawer)
+*/
+#if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4)
+static OSStatus handleTopLevelUIElementAttribute(EventHandlerCallRef next_ref, EventRef event, const QAInterface &interface)
+{
+ return navigateAncestors<IsWindowTest>(next_ref, event, interface, CFStringRef(QAXTopLevelUIElementAttribute));
+}
+#endif
+
+/*
+ Returns the tab buttons for an interface.
+*/
+static OSStatus handleTabsAttribute(EventHandlerCallRef next_ref, EventRef event, QAInterface &interface)
+{
+ Q_UNUSED(next_ref);
+ if (isTabWidget(interface))
+ return setAttributeValue(event, tabWidgetGetTabs(interface));
+ else
+ return setAttributeValue(event, tabBarGetTabs(interface));
+}
+
+static OSStatus handlePositionAttribute(EventHandlerCallRef, EventRef event, const QAInterface &interface)
+{
+ QPoint qpoint(interface.rect().topLeft());
+ HIPoint point;
+ point.x = qpoint.x();
+ point.y = qpoint.y();
+ SetEventParameter(event, kEventParamAccessibleAttributeValue, typeHIPoint, sizeof(point), &point);
+ return noErr;
+}
+
+static OSStatus handleSizeAttribute(EventHandlerCallRef, EventRef event, const QAInterface &interface)
+{
+ QSize qSize(interface.rect().size());
+ HISize size;
+ size.width = qSize.width();
+ size.height = qSize.height();
+ SetEventParameter(event, kEventParamAccessibleAttributeValue, typeHISize, sizeof(size), &size);
+ return noErr;
+}
+
+static OSStatus handleSubroleAttribute(EventHandlerCallRef, EventRef event, const QAInterface &interface)
+{
+ const QCFString role = subrole(interface);
+ CFStringRef rolestr = (CFStringRef)role;
+ SetEventParameter(event, kEventParamAccessibleAttributeValue, typeCFTypeRef, sizeof(rolestr), &rolestr);
+ return noErr;
+}
+
+static OSStatus handleOrientationAttribute(EventHandlerCallRef next_ref, EventRef event, const QAInterface &interface)
+{
+ QObject *const object = interface.object();
+ Qt::Orientation orientation;
+ if (interface.role() == QAccessible::ScrollBar) {
+ orientation = scrollBarOrientation(interface);
+ } else if (QSplitterHandle * const splitter = qobject_cast<QSplitterHandle * const>(object)) {
+ // Qt reports the layout orientation, but we want the splitter handle orientation.
+ orientation = (splitter->orientation() == Qt::Horizontal) ? Qt::Vertical : Qt::Horizontal;
+ } else {
+ return CallNextEventHandler(next_ref, event);
+ }
+ const CFStringRef orientationString = (orientation == Qt::Vertical)
+ ? CFStringRef(QAXVerticalOrientationValue) : CFStringRef(QAXHorizontalOrientationValue);
+ SetEventParameter(event, kEventParamAccessibleAttributeValue, typeCFStringRef, sizeof(orientationString), &orientationString);
+ return noErr;
+}
+
+/*
+ Figures out the next or previous contents for a splitter.
+*/
+static OSStatus handleSplitterContentsAttribute(EventHandlerCallRef next_ref, EventRef event, const QAInterface &interface, QCFString nextOrPrev)
+{
+ if (interface.isValid() == false || interface.role() != QAccessible::Grip)
+ return eventNotHandledErr;
+
+ const QAInterface parent = interface.parent();
+ if (parent.isValid() == false)
+ return CallNextEventHandler(next_ref, event);
+
+ if (parent.role() != QAccessible::Splitter)
+ return CallNextEventHandler(next_ref, event);
+
+ const QSplitter * const splitter = qobject_cast<const QSplitter * const>(parent.object());
+ if (splitter == 0)
+ return CallNextEventHandler(next_ref, event);
+
+ QWidget * const splitterHandle = qobject_cast<QWidget * const>(interface.object());
+ const int splitterHandleIndex = splitter->indexOf(splitterHandle);
+ const int widgetIndex = (nextOrPrev == QCFString(CFStringRef(QAXPreviousContentsAttribute))) ? splitterHandleIndex - 1 : splitterHandleIndex;
+ const QAElement contentsElement = accessibleHierarchyManager()->lookup(splitter->widget(widgetIndex), 0);
+ return setAttributeValue(event, QList<QAElement>() << contentsElement);
+}
+
+/*
+ Creates a list of all splitter handles the splitter contains.
+*/
+static OSStatus handleSplittersAttribute(EventHandlerCallRef next_ref, EventRef event, QAInterface &interface)
+{
+ const QSplitter * const splitter = qobject_cast<const QSplitter * const>(interface.object());
+ if (splitter == 0)
+ return CallNextEventHandler(next_ref, event);
+
+ accessibleHierarchyManager()->registerChildren(interface);
+
+ QList<QAElement> handles;
+ const int visibleSplitterCount = splitter->count() -1; // skip first handle, it's always invisible.
+ for (int i = 0; i < visibleSplitterCount; ++i)
+ handles.append(accessibleHierarchyManager()->lookup(splitter->handle(i + 1), 0));
+
+ return setAttributeValue(event, handles);
+}
+
+// This handler gets the scroll bars for a scroll area
+static OSStatus handleScrollBarAttribute(EventHandlerCallRef next_ref, EventRef event, QAInterface &scrollArea, Qt::Orientation orientation)
+{
+ QAElement scrollBar = scrollAreaGetScrollBar(scrollArea, orientation);
+ if (scrollBar.isValid() == false)
+ return CallNextEventHandler(next_ref, event);
+
+ AXUIElementRef elementRef = scrollBar.element();
+ SetEventParameter(event, kEventParamAccessibleAttributeValue, typeCFTypeRef, sizeof(elementRef), &elementRef);
+ return noErr;
+}
+
+// This handler gets the contents for a scroll area or tab widget.
+static OSStatus handleContentsAttribute(EventHandlerCallRef next_ref, EventRef event, QAInterface &interface)
+{
+ const QCFString mac_role = macRole(interface);
+
+ QAElement contents;
+
+ if (mac_role == kAXTabGroupRole) {
+ contents = tabWidgetGetContents(interface);
+ } else {
+ contents = scrollAreaGetContents(interface);
+ if (contents.isValid() == false)
+ return CallNextEventHandler(next_ref, event);
+ }
+
+ return setAttributeValue(event, QList<QAElement>() << contents);
+}
+
+static OSStatus handleRowsAttribute(EventHandlerCallRef, EventRef event, QAInterface &tableView)
+{
+ QList<QAElement> rows = lookup(tableView.children());
+
+ // kill the first row which is the horizontal header.
+ rows.removeAt(0);
+
+ return setAttributeValue(event, rows);
+}
+
+static OSStatus handleVisibleRowsAttribute(EventHandlerCallRef, EventRef event, QAInterface &tableView)
+{
+ QList<QAElement> visibleRows;
+
+ QList<QAInterface> rows = tableView.children();
+ // kill the first row which is the horizontal header.
+ rows.removeAt(0);
+
+ foreach (const QAInterface &interface, rows)
+ if ((interface.state() & QAccessible::Invisible) == false)
+ visibleRows.append(accessibleHierarchyManager()->lookup(interface));
+
+ return setAttributeValue(event, visibleRows);
+}
+
+static OSStatus handleSelectedRowsAttribute(EventHandlerCallRef, EventRef event, QAInterface &tableView)
+{
+ QList<QAElement> selectedRows;
+ foreach (const QAInterface &interface, tableView.children())
+ if ((interface.state() & QAccessible::Selected))
+ selectedRows.append(accessibleHierarchyManager()->lookup(interface));
+
+ return setAttributeValue(event, selectedRows);
+}
+
+static OSStatus getNamedAttribute(EventHandlerCallRef next_ref, EventRef event, QAInterface &interface)
+{
+ CFStringRef var;
+ GetEventParameter(event, kEventParamAccessibleAttributeName, typeCFStringRef, 0,
+ sizeof(var), 0, &var);
+
+ if (CFStringCompare(var, CFStringRef(QAXChildrenAttribute), 0) == kCFCompareEqualTo) {
+ return handleChildrenAttribute(next_ref, event, interface);
+#if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4)
+ } else if(CFStringCompare(var, CFStringRef(QAXTopLevelUIElementAttribute), 0) == kCFCompareEqualTo) {
+ return handleTopLevelUIElementAttribute(next_ref, event, interface);
+#endif
+ } else if(CFStringCompare(var, CFStringRef(QAXWindowAttribute), 0) == kCFCompareEqualTo) {
+ return handleWindowAttribute(next_ref, event, interface);
+ } else if(CFStringCompare(var, CFStringRef(QAXParentAttribute), 0) == kCFCompareEqualTo) {
+ return handleParentAttribute(next_ref, event, interface);
+ } else if (CFStringCompare(var, CFStringRef(QAXPositionAttribute), 0) == kCFCompareEqualTo) {
+ return handlePositionAttribute(next_ref, event, interface);
+ } else if (CFStringCompare(var, CFStringRef(QAXSizeAttribute), 0) == kCFCompareEqualTo) {
+ return handleSizeAttribute(next_ref, event, interface);
+ } else if (CFStringCompare(var, CFStringRef(QAXRoleAttribute), 0) == kCFCompareEqualTo) {
+ CFStringRef role = macRole(interface);
+// ###
+// QWidget * const widget = qobject_cast<QWidget *>(interface.object());
+// if (role == CFStringRef(QAXUnknownRole) && widget && widget->isWindow())
+// role = CFStringRef(QAXWindowRole);
+
+ SetEventParameter(event, kEventParamAccessibleAttributeValue, typeCFStringRef,
+ sizeof(role), &role);
+
+ } else if (CFStringCompare(var, CFStringRef(QAXEnabledAttribute), 0) == kCFCompareEqualTo) {
+ Boolean val = !((interface.state() & QAccessible::Unavailable))
+ && !((interface.state() & QAccessible::Invisible));
+ SetEventParameter(event, kEventParamAccessibleAttributeValue, typeBoolean,
+ sizeof(val), &val);
+ } else if (CFStringCompare(var, CFStringRef(QAXExpandedAttribute), 0) == kCFCompareEqualTo) {
+ Boolean val = (interface.state() & QAccessible::Expanded);
+ SetEventParameter(event, kEventParamAccessibleAttributeValue, typeBoolean,
+ sizeof(val), &val);
+ } else if (CFStringCompare(var, CFStringRef(QAXSelectedAttribute), 0) == kCFCompareEqualTo) {
+ Boolean val = (interface.state() & QAccessible::Selection);
+ SetEventParameter(event, kEventParamAccessibleAttributeValue, typeBoolean,
+ sizeof(val), &val);
+ } else if (CFStringCompare(var, CFStringRef(QAXFocusedAttribute), 0) == kCFCompareEqualTo) {
+ Boolean val = (interface.state() & QAccessible::Focus);
+ SetEventParameter(event, kEventParamAccessibleAttributeValue, typeBoolean,
+ sizeof(val), &val);
+ } else if (CFStringCompare(var, CFStringRef(QAXSelectedChildrenAttribute), 0) == kCFCompareEqualTo) {
+ const int cc = interface.childCount();
+ QList<QAElement> selected;
+ for (int i = 1; i <= cc; ++i) {
+ const QAInterface child_iface = interface.navigate(QAccessible::Child, i);
+ if (child_iface.isValid() && child_iface.state() & QAccessible::Selected)
+ selected.append(accessibleHierarchyManager()->lookup(child_iface));
+ }
+
+ return setAttributeValue(event, selected);
+
+ } else if (CFStringCompare(var, CFStringRef(QAXCloseButtonAttribute), 0) == kCFCompareEqualTo) {
+ if(interface.object() && interface.object()->isWidgetType()) {
+ Boolean val = true; //do we want to add a WState for this?
+ SetEventParameter(event, kEventParamAccessibleAttributeValue, typeBoolean,
+ sizeof(val), &val);
+ }
+ } else if (CFStringCompare(var, CFStringRef(QAXZoomButtonAttribute), 0) == kCFCompareEqualTo) {
+ if(interface.object() && interface.object()->isWidgetType()) {
+ QWidget *widget = (QWidget*)interface.object();
+ Boolean val = (widget->windowFlags() & Qt::WindowMaximizeButtonHint);
+ SetEventParameter(event, kEventParamAccessibleAttributeValue, typeBoolean,
+ sizeof(val), &val);
+ }
+ } else if (CFStringCompare(var, CFStringRef(QAXMinimizeButtonAttribute), 0) == kCFCompareEqualTo) {
+ if(interface.object() && interface.object()->isWidgetType()) {
+ QWidget *widget = (QWidget*)interface.object();
+ Boolean val = (widget->windowFlags() & Qt::WindowMinimizeButtonHint);
+ SetEventParameter(event, kEventParamAccessibleAttributeValue, typeBoolean,
+ sizeof(val), &val);
+ }
+ } else if (CFStringCompare(var, CFStringRef(QAXToolbarButtonAttribute), 0) == kCFCompareEqualTo) {
+ if(interface.object() && interface.object()->isWidgetType()) {
+ QWidget *widget = (QWidget*)interface.object();
+ Boolean val = qobject_cast<QMainWindow *>(widget) != 0;
+ SetEventParameter(event, kEventParamAccessibleAttributeValue, typeBoolean,
+ sizeof(val), &val);
+ }
+ } else if (CFStringCompare(var, CFStringRef(QAXGrowAreaAttribute), 0) == kCFCompareEqualTo) {
+ if(interface.object() && interface.object()->isWidgetType()) {
+ Boolean val = true; //do we want to add a WState for this?
+ SetEventParameter(event, kEventParamAccessibleAttributeValue, typeBoolean,
+ sizeof(val), &val);
+ }
+ } else if (CFStringCompare(var, CFStringRef(QAXMinimizedAttribute), 0) == kCFCompareEqualTo) {
+ if (interface.object() && interface.object()->isWidgetType()) {
+ QWidget *widget = (QWidget*)interface.object();
+ Boolean val = (widget->windowState() & Qt::WindowMinimized);
+ SetEventParameter(event, kEventParamAccessibleAttributeValue, typeBoolean,
+ sizeof(val), &val);
+ }
+ } else if (CFStringCompare(var, CFStringRef(QAXSubroleAttribute), 0) == kCFCompareEqualTo) {
+ return handleSubroleAttribute(next_ref, event, interface);
+ } else if (CFStringCompare(var, CFStringRef(QAXRoleDescriptionAttribute), 0) == kCFCompareEqualTo) {
+#if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4) && !defined(QT_MAC_USE_COCOA)
+ if (HICopyAccessibilityRoleDescription) {
+ const CFStringRef roleDescription = HICopyAccessibilityRoleDescription(macRole(interface), 0);
+ SetEventParameter(event, kEventParamAccessibleAttributeValue, typeCFStringRef,
+ sizeof(roleDescription), &roleDescription);
+ } else
+#endif
+ {
+ // Just use Qt::Description on 10.3
+ handleStringAttribute(event, QAccessible::Description, interface);
+ }
+ } else if (CFStringCompare(var, CFStringRef(QAXTitleAttribute), 0) == kCFCompareEqualTo) {
+ const QAccessible::Role role = interface.role();
+ const QAccessible::Text text = (QAccessible::Text)textForRoleAndAttribute(role, var);
+ handleStringAttribute(event, text, interface);
+ } else if (CFStringCompare(var, CFStringRef(QAXValueAttribute), 0) == kCFCompareEqualTo) {
+ const QAccessible::Role role = interface.role();
+ const QAccessible::Text text = (QAccessible::Text)textForRoleAndAttribute(role, var);
+ if (role == QAccessible::CheckBox || role == QAccessible::RadioButton) {
+ int value = buttonValue(interface);
+ SetEventParameter(event, kEventParamAccessibleAttributeValue, typeUInt32, sizeof(value), &value);
+ } else {
+ handleStringAttribute(event, text, interface);
+ }
+#if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4)
+ } else if (CFStringCompare(var, CFStringRef(QAXDescriptionAttribute), 0) == kCFCompareEqualTo) {
+ const QAccessible::Role role = interface.role();
+ const QAccessible::Text text = (QAccessible::Text)textForRoleAndAttribute(role, var);
+ handleStringAttribute(event, text, interface);
+ } else if (CFStringCompare(var, CFStringRef(QAXLinkedUIElementsAttribute), 0) == kCFCompareEqualTo) {
+ return CallNextEventHandler(next_ref, event);
+#endif
+ } else if (CFStringCompare(var, CFStringRef(QAXHelpAttribute), 0) == kCFCompareEqualTo) {
+ const QAccessible::Role role = interface.role();
+ const QAccessible::Text text = (QAccessible::Text)textForRoleAndAttribute(role, var);
+ handleStringAttribute(event, text, interface);
+ } else if (CFStringCompare(var, kAXTitleUIElementAttribute, 0) == kCFCompareEqualTo) {
+ return CallNextEventHandler(next_ref, event);
+ } else if (CFStringCompare(var, CFStringRef(QAXTabsAttribute), 0) == kCFCompareEqualTo) {
+ return handleTabsAttribute(next_ref, event, interface);
+ } else if (CFStringCompare(var, CFStringRef(QAXMinValueAttribute), 0) == kCFCompareEqualTo) {
+ // tabs we first go to the tab bar which is child #2.
+ QAInterface tabBarInterface = interface.childAt(2);
+ return handleTabsAttribute(next_ref, event, tabBarInterface);
+ } else if (CFStringCompare(var, CFStringRef(QAXMinValueAttribute), 0) == kCFCompareEqualTo) {
+ if (interface.role() == QAccessible::RadioButton || interface.role() == QAccessible::CheckBox) {
+ uint value = 0;
+ SetEventParameter(event, kEventParamAccessibleAttributeValue, typeUInt32, sizeof(value), &value);
+ } else {
+ return CallNextEventHandler(next_ref, event);
+ }
+ } else if (CFStringCompare(var, CFStringRef(QAXMaxValueAttribute), 0) == kCFCompareEqualTo) {
+ if (interface.role() == QAccessible::RadioButton || interface.role() == QAccessible::CheckBox) {
+ uint value = 2;
+ SetEventParameter(event, kEventParamAccessibleAttributeValue, typeUInt32, sizeof(value), &value);
+ } else {
+ return CallNextEventHandler(next_ref, event);
+ }
+ } else if (CFStringCompare(var, CFStringRef(QAXOrientationAttribute), 0) == kCFCompareEqualTo) {
+ return handleOrientationAttribute(next_ref, event, interface);
+ } else if (CFStringCompare(var, CFStringRef(QAXPreviousContentsAttribute), 0) == kCFCompareEqualTo) {
+ return handleSplitterContentsAttribute(next_ref, event, interface, CFStringRef(QAXPreviousContentsAttribute));
+ } else if (CFStringCompare(var, CFStringRef(QAXNextContentsAttribute), 0) == kCFCompareEqualTo) {
+ return handleSplitterContentsAttribute(next_ref, event, interface, CFStringRef(QAXNextContentsAttribute));
+ } else if (CFStringCompare(var, CFStringRef(QAXSplittersAttribute), 0) == kCFCompareEqualTo) {
+ return handleSplittersAttribute(next_ref, event, interface);
+ } else if (CFStringCompare(var, CFStringRef(QAXHorizontalScrollBarAttribute), 0) == kCFCompareEqualTo) {
+ return handleScrollBarAttribute(next_ref, event, interface, Qt::Horizontal);
+ } else if (CFStringCompare(var, CFStringRef(QAXVerticalScrollBarAttribute), 0) == kCFCompareEqualTo) {
+ return handleScrollBarAttribute(next_ref, event, interface, Qt::Vertical);
+ } else if (CFStringCompare(var, CFStringRef(QAXContentsAttribute), 0) == kCFCompareEqualTo) {
+ return handleContentsAttribute(next_ref, event, interface);
+ } else if (CFStringCompare(var, CFStringRef(QAXRowsAttribute), 0) == kCFCompareEqualTo) {
+ return handleRowsAttribute(next_ref, event, interface);
+ } else if (CFStringCompare(var, CFStringRef(QAXVisibleRowsAttribute), 0) == kCFCompareEqualTo) {
+ return handleVisibleRowsAttribute(next_ref, event, interface);
+ } else if (CFStringCompare(var, CFStringRef(QAXSelectedRowsAttribute), 0) == kCFCompareEqualTo) {
+ return handleSelectedRowsAttribute(next_ref, event, interface);
+ } else {
+ return CallNextEventHandler(next_ref, event);
+ }
+ return noErr;
+}
+
+static OSStatus isNamedAttributeSettable(EventRef event, const QAInterface &interface)
+{
+ CFStringRef var;
+ GetEventParameter(event, kEventParamAccessibleAttributeName, typeCFStringRef, 0,
+ sizeof(var), 0, &var);
+ Boolean settable = false;
+ if (CFStringCompare(var, CFStringRef(QAXFocusedAttribute), 0) == kCFCompareEqualTo) {
+ settable = true;
+ } else {
+ for (int r = 0; text_bindings[r][0].qt != -1; r++) {
+ if(interface.role() == (QAccessible::Role)text_bindings[r][0].qt) {
+ for (int a = 1; text_bindings[r][a].qt != -1; a++) {
+ if (CFStringCompare(var, CFStringRef(text_bindings[r][a].mac), 0) == kCFCompareEqualTo) {
+ settable = text_bindings[r][a].settable;
+ break;
+ }
+ }
+ }
+ }
+ }
+ SetEventParameter(event, kEventParamAccessibleAttributeSettable, typeBoolean,
+ sizeof(settable), &settable);
+ return noErr;
+}
+
+static OSStatus getChildAtPoint(EventHandlerCallRef next_ref, EventRef event, QAInterface &interface)
+{
+ Q_UNUSED(next_ref);
+ if (interface.isValid() == false)
+ return eventNotHandledErr;
+
+ // Add the children for this interface to the global QAccessibelHierachyManager.
+ accessibleHierarchyManager()->registerChildren(interface);
+
+ Point where;
+ GetEventParameter(event, kEventParamMouseLocation, typeQDPoint, 0, sizeof(where), 0, &where);
+ const QAInterface childInterface = interface.childAt(where.h, where.v);
+
+ if (childInterface.isValid() == false || childInterface == interface)
+ return eventNotHandledErr;
+
+ const QAElement element = accessibleHierarchyManager()->lookup(childInterface);
+ if (element.isValid() == false)
+ return eventNotHandledErr;
+
+ AXUIElementRef elementRef = element.element();
+ CFRetain(elementRef);
+ SetEventParameter(event, kEventParamAccessibleChild, typeCFTypeRef,
+ sizeof(elementRef), &elementRef);
+
+ return noErr;
+}
+
+/*
+ Returns a list of actions the given interface supports.
+ Currently implemented by getting the interface role and deciding based on that.
+*/
+static QList<QAccessible::Action> supportedPredefinedActions(const QAInterface &interface)
+{
+ QList<QAccessible::Action> actions;
+ switch (interface.role()) {
+ default:
+ // Most things can be pressed.
+ actions.append(QAccessible::Press);
+ break;
+ }
+
+ return actions;
+}
+
+/*
+ Translates a predefined QAccessible::Action to a Mac action constant.
+ Returns an empty string if the Qt Action has no mac equivalent.
+*/
+static QCFString translateAction(const QAccessible::Action action)
+{
+ switch (action) {
+ case QAccessible::Press:
+ return CFStringRef(QAXPressAction);
+ break;
+ case QAccessible::Increase:
+ return CFStringRef(QAXIncrementAction);
+ break;
+ case QAccessible::Decrease:
+ return CFStringRef(QAXDecrementAction);
+ break;
+ case QAccessible::Accept:
+ return CFStringRef(QAXConfirmAction);
+ break;
+ case QAccessible::Select:
+ return CFStringRef(QAXPickAction);
+ break;
+ case QAccessible::Cancel:
+ return CFStringRef(QAXCancelAction);
+ break;
+ default:
+ return QCFString();
+ break;
+ }
+}
+
+/*
+ Translates between a Mac action constant and a QAccessible::Action.
+ Returns QAccessible::Default action if there is no Qt predefined equivalent.
+*/
+static QAccessible::Action translateAction(const CFStringRef actionName)
+{
+ if(CFStringCompare(actionName, CFStringRef(QAXPressAction), 0) == kCFCompareEqualTo) {
+ return QAccessible::Press;
+ } else if(CFStringCompare(actionName, CFStringRef(QAXIncrementAction), 0) == kCFCompareEqualTo) {
+ return QAccessible::Increase;
+ } else if(CFStringCompare(actionName, CFStringRef(QAXDecrementAction), 0) == kCFCompareEqualTo) {
+ return QAccessible::Decrease;
+ } else if(CFStringCompare(actionName, CFStringRef(QAXConfirmAction), 0) == kCFCompareEqualTo) {
+ return QAccessible::Accept;
+ } else if(CFStringCompare(actionName, CFStringRef(QAXPickAction), 0) == kCFCompareEqualTo) {
+ return QAccessible::Select;
+ } else if(CFStringCompare(actionName, CFStringRef(QAXCancelAction), 0) == kCFCompareEqualTo) {
+ return QAccessible::Cancel;
+ } else {
+ return QAccessible::DefaultAction;
+ }
+}
+#endif // QT_MAC_USE_COCOA
+
+/*
+ Copies the translated names all supported actions for an interface into the kEventParamAccessibleActionNames
+ event parameter.
+*/
+#ifndef QT_MAC_USE_COCOA
+static OSStatus getAllActionNames(EventHandlerCallRef next_ref, EventRef event, const QAInterface &interface)
+{
+ Q_UNUSED(next_ref);
+
+ CFMutableArrayRef actions = 0;
+ GetEventParameter(event, kEventParamAccessibleActionNames, typeCFMutableArrayRef, 0,
+ sizeof(actions), 0, &actions);
+
+ // Add supported predefined actions.
+ const QList<QAccessible::Action> predefinedActions = supportedPredefinedActions(interface);
+ for (int i = 0; i < predefinedActions.count(); ++i) {
+ const QCFString action = translateAction(predefinedActions.at(i));
+ if (action != QCFString())
+ qt_mac_append_cf_uniq(actions, action);
+ }
+
+ // Add user actions
+ const int actionCount = interface.userActionCount();
+ for (int i = 0; i < actionCount; ++i) {
+ const QString actionName = interface.actionText(i, QAccessible::Name);
+ qt_mac_append_cf_uniq(actions, QCFString::toCFStringRef(actionName));
+ }
+
+ return noErr;
+}
+#endif
+
+/*
+ Handles the perforNamedAction event.
+*/
+#ifndef QT_MAC_USE_COCOA
+static OSStatus performNamedAction(EventHandlerCallRef next_ref, EventRef event, const QAInterface& interface)
+{
+ Q_UNUSED(next_ref);
+
+ CFStringRef act;
+ GetEventParameter(event, kEventParamAccessibleActionName, typeCFStringRef, 0,
+ sizeof(act), 0, &act);
+
+ const QAccessible::Action action = translateAction(act);
+
+ // Perform built-in action
+ if (action != QAccessible::DefaultAction) {
+ interface.doAction(action, QVariantList());
+ return noErr;
+ }
+
+ // Search for user-defined actions and perform it if found.
+ const int actCount = interface.userActionCount();
+ const QString qAct = QCFString::toQString(act);
+ for(int i = 0; i < actCount; i++) {
+ if(interface.actionText(i, QAccessible::Name) == qAct) {
+ interface.doAction(i, QVariantList());
+ break;
+ }
+ }
+ return noErr;
+}
+
+static OSStatus setNamedAttribute(EventHandlerCallRef next_ref, EventRef event, const QAInterface &interface)
+{
+ Q_UNUSED(next_ref);
+ Q_UNUSED(event);
+
+ CFStringRef var;
+ GetEventParameter(event, kEventParamAccessibleAttributeName, typeCFStringRef, 0,
+ sizeof(var), 0, &var);
+ if(CFStringCompare(var, CFStringRef(QAXFocusedAttribute), 0) == kCFCompareEqualTo) {
+ CFTypeRef val;
+ if(GetEventParameter(event, kEventParamAccessibleAttributeValue, typeCFTypeRef, 0,
+ sizeof(val), 0, &val) == noErr) {
+ if(CFGetTypeID(val) == CFBooleanGetTypeID() &&
+ CFEqual(static_cast<CFBooleanRef>(val), kCFBooleanTrue)) {
+ interface.doAction(QAccessible::SetFocus);
+ }
+ }
+ } else {
+ bool found = false;
+ for(int r = 0; text_bindings[r][0].qt != -1; r++) {
+ if(interface.role() == (QAccessible::Role)text_bindings[r][0].qt) {
+ for(int a = 1; text_bindings[r][a].qt != -1; a++) {
+ if(CFStringCompare(var, CFStringRef(text_bindings[r][a].mac), 0) == kCFCompareEqualTo) {
+ if(!text_bindings[r][a].settable) {
+ } else {
+ CFTypeRef val;
+ if(GetEventParameter(event, kEventParamAccessibleAttributeValue, typeCFTypeRef, 0,
+ sizeof(val), 0, &val) == noErr) {
+ if(CFGetTypeID(val) == CFStringGetTypeID())
+ interface.setText((QAccessible::Text)text_bindings[r][a].qt,
+ QCFString::toQString(static_cast<CFStringRef>(val)));
+
+ }
+ }
+ found = true;
+ break;
+ }
+ }
+ break;
+ }
+ }
+ }
+ return noErr;
+}
+
+/*
+ This is the main accessibility event handler.
+*/
+static OSStatus accessibilityEventHandler(EventHandlerCallRef next_ref, EventRef event, void *data)
+{
+ Q_UNUSED(data)
+
+ // Return if this event is not a AccessibleGetNamedAttribute event.
+ const UInt32 eclass = GetEventClass(event);
+ if (eclass != kEventClassAccessibility)
+ return eventNotHandledErr;
+
+ // Get the AXUIElementRef and QAInterface pointer
+ AXUIElementRef element = 0;
+ GetEventParameter(event, kEventParamAccessibleObject, typeCFTypeRef, 0, sizeof(element), 0, &element);
+ QAInterface interface = accessibleHierarchyManager()->lookup(element);
+ if (interface.isValid() == false)
+ return eventNotHandledErr;
+
+ const UInt32 ekind = GetEventKind(event);
+ OSStatus status = noErr;
+ switch (ekind) {
+ case kEventAccessibleGetAllAttributeNames:
+ status = getAllAttributeNames(event, interface, next_ref);
+ break;
+ case kEventAccessibleGetNamedAttribute:
+ status = getNamedAttribute(next_ref, event, interface);
+ break;
+ case kEventAccessibleIsNamedAttributeSettable:
+ status = isNamedAttributeSettable(event, interface);
+ break;
+ case kEventAccessibleGetChildAtPoint:
+ status = getChildAtPoint(next_ref, event, interface);
+ break;
+ case kEventAccessibleGetAllActionNames:
+ status = getAllActionNames(next_ref, event, interface);
+ break;
+ case kEventAccessibleGetFocusedChild:
+ status = CallNextEventHandler(next_ref, event);
+ break;
+ case kEventAccessibleSetNamedAttribute:
+ status = setNamedAttribute(next_ref, event, interface);
+ break;
+ case kEventAccessiblePerformNamedAction:
+ status = performNamedAction(next_ref, event, interface);
+ break;
+ default:
+ status = CallNextEventHandler(next_ref, event);
+ break;
+ };
+ return status;
+}
+#endif
+
+void QAccessible::initialize()
+{
+#ifndef QT_MAC_USE_COCOA
+ registerQtAccessibilityHIObjectSubclass();
+ installApplicationEventhandler();
+#endif
+}
+
+// Sets thre root object for the application
+void QAccessible::setRootObject(QObject *object)
+{
+ // Call installed root object handler if we have one
+ if (rootObjectHandler) {
+ rootObjectHandler(object);
+ return;
+ }
+
+ rootObject = object;
+}
+
+void QAccessible::cleanup()
+{
+ accessibleHierarchyManager()->reset();
+#ifndef QT_MAC_USE_COCOA
+ removeEventhandler(applicationEventHandlerUPP);
+ removeEventhandler(objectCreateEventHandlerUPP);
+ removeEventhandler(accessibilityEventHandlerUPP);
+#endif
+}
+
+void QAccessible::updateAccessibility(QObject *object, int child, Event reason)
+{
+ // Call installed update handler if we have one.
+ if (updateHandler) {
+ updateHandler(object, child, reason);
+ return;
+ }
+
+#ifndef QT_MAC_USE_COCOA
+ // Return if the mac accessibility is not enabled.
+ if(!AXAPIEnabled())
+ return;
+
+ // Work around crash, disable accessiblity for focus frames.
+ if (qstrcmp(object->metaObject()->className(), "QFocusFrame") == 0)
+ return;
+
+// qDebug() << "updateAccessibility" << object << child << hex << reason;
+
+ if (reason == ObjectShow) {
+ QAInterface interface = QAInterface(QAccessible::queryAccessibleInterface(object), child);
+ accessibleHierarchyManager()->registerInterface(interface);
+ }
+
+ const QAElement element = accessibleHierarchyManager()->lookup(object, child);
+ if (element.isValid() == false)
+ return;
+
+
+ CFStringRef notification = 0;
+ if(object && object->isWidgetType() && reason == ObjectCreated) {
+ notification = CFStringRef(QAXWindowCreatedNotification);
+ } else if(reason == ValueChanged) {
+ notification = CFStringRef(QAXValueChangedNotification);
+ } else if(reason == MenuStart) {
+ notification = CFStringRef(QAXMenuOpenedNotification);
+ } else if(reason == MenuEnd) {
+ notification = CFStringRef(QAXMenuClosedNotification);
+ } else if(reason == LocationChanged) {
+ notification = CFStringRef(QAXWindowMovedNotification);
+ } else if(reason == ObjectShow || reason == ObjectHide ) {
+ // When a widget is deleted we get a ObjectHide before the destroyed(QObject *)
+ // signal is emitted (which makes sense). However, at this point we are in the
+ // middle of the QWidget destructor which means that we have to be careful when
+ // using the widget pointer. Since we can't control what the accessibilty interfaces
+ // does when navigate() is called below we ignore the hide update in this case.
+ // (the widget will be deleted soon anyway.)
+ extern QWidgetPrivate * qt_widget_private(QWidget *);
+ if (QWidget *widget = qobject_cast<QWidget*>(object)) {
+ if (qt_widget_private(widget)->data.in_destructor)
+ return;
+
+ // Check widget parent as well, special case for preventing crash
+ // when the viewport() of an abstract scroll area is hidden when
+ // the QWidget destructor hides all its children.
+ QWidget *parentWidget = widget->parentWidget();
+ if (parentWidget && qt_widget_private(parentWidget)->data.in_destructor)
+ return;
+ }
+
+ // There is no equivalent Mac notification for ObjectShow/Hide, so we call HIObjectSetAccessibilityIgnored
+ // and isItIntersting which will mark the HIObject accociated with the element as ignored if the
+ // QAccessible::Invisible state bit is set.
+ QAInterface interface = accessibleHierarchyManager()->lookup(element);
+ if (interface.isValid()) {
+ HIObjectSetAccessibilityIgnored(element.object(), !isItInteresting(interface));
+ }
+
+ // If the interface manages its own children, also check if we should ignore those.
+ if (isItemView(interface) == false && managesChildren(interface)) {
+ for (int i = 1; i <= interface.childCount(); ++i) {
+ QAInterface childInterface = interface.navigate(QAccessible::Child, i);
+ if (childInterface.isValid() && childInterface.isHIView() == false) {
+ const QAElement element = accessibleHierarchyManager()->lookup(childInterface);
+ if (element.isValid()) {
+ HIObjectSetAccessibilityIgnored(element.object(), !isItInteresting(childInterface));
+ }
+ }
+ }
+ }
+
+ } else if(reason == Focus) {
+ if(object && object->isWidgetType()) {
+ QWidget *w = static_cast<QWidget*>(object);
+ if(w->isWindow())
+ notification = CFStringRef(QAXFocusedWindowChangedNotification);
+ else
+ notification = CFStringRef(QAXFocusedUIElementChangedNotification);
+ }
+ }
+
+ if (!notification)
+ return;
+
+ AXNotificationHIObjectNotify(notification, element.object(), element.id());
+#endif
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_ACCESSIBILITY
diff --git a/src/gui/accessible/qaccessible_mac_carbon.cpp b/src/gui/accessible/qaccessible_mac_carbon.cpp
new file mode 100644
index 0000000000..3b2ab68e83
--- /dev/null
+++ b/src/gui/accessible/qaccessible_mac_carbon.cpp
@@ -0,0 +1,119 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+static OSStatus applicationEventHandler(EventHandlerCallRef next_ref, EventRef event, void *data);
+static EventHandlerUPP applicationEventHandlerUPP = 0;
+static EventTypeSpec application_events[] = {
+ { kEventClassAccessibility, kEventAccessibleGetChildAtPoint },
+ { kEventClassAccessibility, kEventAccessibleGetNamedAttribute }
+};
+
+static CFStringRef kObjectQtAccessibility = CFSTR("com.trolltech.qt.accessibility");
+static EventHandlerUPP objectCreateEventHandlerUPP = 0;
+static EventTypeSpec objectCreateEvents[] = {
+ { kEventClassHIObject, kEventHIObjectConstruct },
+ { kEventClassHIObject, kEventHIObjectInitialize },
+ { kEventClassHIObject, kEventHIObjectDestruct },
+ { kEventClassHIObject, kEventHIObjectPrintDebugInfo }
+};
+
+static OSStatus accessibilityEventHandler(EventHandlerCallRef next_ref, EventRef event, void *data);
+static EventHandlerUPP accessibilityEventHandlerUPP = 0;
+static EventTypeSpec accessibilityEvents[] = {
+ { kEventClassAccessibility, kEventAccessibleGetChildAtPoint },
+ { kEventClassAccessibility, kEventAccessibleGetFocusedChild },
+ { kEventClassAccessibility, kEventAccessibleGetAllAttributeNames },
+ { kEventClassAccessibility, kEventAccessibleGetNamedAttribute },
+ { kEventClassAccessibility, kEventAccessibleSetNamedAttribute },
+ { kEventClassAccessibility, kEventAccessibleIsNamedAttributeSettable },
+ { kEventClassAccessibility, kEventAccessibleGetAllActionNames },
+ { kEventClassAccessibility, kEventAccessiblePerformNamedAction },
+ { kEventClassAccessibility, kEventAccessibleGetNamedActionDescription }
+};
+
+static void installAcessibilityEventHandler(HIObjectRef hiObject)
+{
+ if (!accessibilityEventHandlerUPP)
+ accessibilityEventHandlerUPP = NewEventHandlerUPP(accessibilityEventHandler);
+
+ InstallHIObjectEventHandler(hiObject, accessibilityEventHandlerUPP,
+ GetEventTypeCount(accessibilityEvents),
+ accessibilityEvents, 0, 0);
+}
+
+static OSStatus objectCreateEventHandler(EventHandlerCallRef next_ref, EventRef event, void *data)
+{
+ Q_UNUSED(data)
+ Q_UNUSED(event)
+ Q_UNUSED(next_ref)
+ return noErr;
+}
+
+static void registerQtAccessibilityHIObjectSubclass()
+{
+ if (!objectCreateEventHandlerUPP)
+ objectCreateEventHandlerUPP = NewEventHandlerUPP(objectCreateEventHandler);
+ OSStatus err = HIObjectRegisterSubclass(kObjectQtAccessibility, 0, 0, objectCreateEventHandlerUPP,
+ GetEventTypeCount(objectCreateEvents), objectCreateEvents, 0, 0);
+ if (err && err != hiObjectClassExistsErr)
+ qWarning("qaccessible_mac internal error: Could not register accessibility HIObject subclass");
+}
+
+static void installApplicationEventhandler()
+{
+ if (!applicationEventHandlerUPP)
+ applicationEventHandlerUPP = NewEventHandlerUPP(applicationEventHandler);
+
+ OSStatus err = InstallApplicationEventHandler(applicationEventHandlerUPP,
+ GetEventTypeCount(application_events), application_events,
+ 0, 0);
+
+ if (err && err != eventHandlerAlreadyInstalledErr)
+ qWarning("qaccessible_mac internal error: Could not install application accessibility event handler");
+}
+
+static void removeEventhandler(EventHandlerUPP eventHandler)
+{
+ if (eventHandler) {
+ DisposeEventHandlerUPP(eventHandler);
+ eventHandler = 0;
+ }
+}
diff --git a/src/gui/accessible/qaccessible_mac_cocoa.mm b/src/gui/accessible/qaccessible_mac_cocoa.mm
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/src/gui/accessible/qaccessible_mac_cocoa.mm
diff --git a/src/gui/accessible/qaccessible_mac_p.h b/src/gui/accessible/qaccessible_mac_p.h
new file mode 100644
index 0000000000..e271253318
--- /dev/null
+++ b/src/gui/accessible/qaccessible_mac_p.h
@@ -0,0 +1,479 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QACCESSIBLE_MAC_P_H
+#define QACCESSIBLE_MAC_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 <qglobal.h>
+#include <private/qt_mac_p.h>
+#include <qaccessible.h>
+#include <qwidget.h>
+#include <qdebug.h>
+
+//#define Q_ACCESSIBLE_MAC_DEBUG
+
+QT_BEGIN_NAMESPACE
+
+/*
+ QAccessibleInterfaceWrapper wraps QAccessibleInterface and adds
+ a ref count. QAccessibleInterfaceWrapper is a "by-value" class.
+*/
+class Q_AUTOTEST_EXPORT QAccessibleInterfaceWrapper
+{
+public:
+ QAccessibleInterfaceWrapper()
+ : interface(0), childrenIsRegistered(new bool(false)), refCount(new int(1)) { }
+
+ QAccessibleInterfaceWrapper(QAccessibleInterface *interface)
+ :interface(interface), childrenIsRegistered(new bool(false)), refCount(new int(1)) { }
+
+ ~QAccessibleInterfaceWrapper()
+ {
+ if (--(*refCount) == 0) {
+ delete interface;
+ delete refCount;
+ delete childrenIsRegistered;
+ }
+ }
+
+ QAccessibleInterfaceWrapper(const QAccessibleInterfaceWrapper &other)
+ :interface(other.interface), childrenIsRegistered(other.childrenIsRegistered), refCount(other.refCount)
+ {
+ ++(*refCount);
+ }
+
+ void operator=(const QAccessibleInterfaceWrapper &other)
+ {
+ if (other.interface == interface)
+ return;
+
+ if (--(*refCount) == 0) {
+ delete interface;
+ delete refCount;
+ delete childrenIsRegistered;
+ }
+
+ interface = other.interface;
+ childrenIsRegistered = other.childrenIsRegistered;
+ refCount = other.refCount;
+ ++(*refCount);
+ }
+
+ QAccessibleInterface *interface;
+ bool *childrenIsRegistered;
+private:
+ int *refCount;
+};
+
+/*
+ QAInterface represents one accessiblity item. It hides the fact that
+ one QAccessibleInterface may represent more than one item, and it also
+ automates the memory management for QAccessibleInterfaces using the
+ QAccessibleInterfaceWrapper wrapper class.
+
+ It has the same API as QAccessibleInterface, minus the child parameter
+ in the funcitons.
+*/
+class Q_AUTOTEST_EXPORT QAInterface : public QAccessible
+{
+public:
+ QAInterface()
+ : base(QAccessibleInterfaceWrapper())
+ { }
+
+ QAInterface(QAccessibleInterface *interface, int child = 0)
+ {
+ if (interface == 0 || child > interface->childCount()) {
+ base = QAccessibleInterfaceWrapper();
+ } else {
+ base = QAccessibleInterfaceWrapper(interface);
+ m_cachedObject = interface->object();
+ this->child = child;
+ }
+ }
+
+ QAInterface(QAccessibleInterfaceWrapper wrapper, int child = 0)
+ :base(wrapper), m_cachedObject(wrapper.interface->object()), child(child)
+ { }
+
+ QAInterface(const QAInterface &other, int child)
+ {
+ if (other.isValid() == false || child > other.childCount()) {
+ base = QAccessibleInterfaceWrapper();
+ } else {
+ base = other.base;
+ m_cachedObject = other.m_cachedObject;
+ this->child = child;
+ }
+ }
+
+ bool operator==(const QAInterface &other) const;
+ bool operator!=(const QAInterface &other) const;
+
+ inline QString actionText (int action, Text text) const
+ { return base.interface->actionText(action, text, child); }
+
+ QAInterface childAt(int x, int y) const
+ {
+ if (!checkValid())
+ return QAInterface();
+
+ const int foundChild = base.interface->childAt(x, y);
+
+ if (foundChild == -1)
+ return QAInterface();
+
+ if (child == 0)
+ return navigate(QAccessible::Child, foundChild);
+
+ if (foundChild == child)
+ return *this;
+ return QAInterface();
+ }
+
+ int indexOfChild(const QAInterface &child) const
+ {
+ if (!checkValid())
+ return -1;
+
+ if (*this != child.parent())
+ return -1;
+
+ if (object() == child.object())
+ return child.id();
+
+ return base.interface->indexOfChild(child.base.interface);
+ }
+
+ inline int childCount() const
+ {
+ if (!checkValid())
+ return 0;
+
+ if (child != 0)
+ return 0;
+ return base.interface->childCount();
+ }
+
+ QList<QAInterface> children() const
+ {
+ if (!checkValid())
+ return QList<QAInterface>();
+
+ QList<QAInterface> children;
+ for (int i = 1; i <= childCount(); ++i) {
+ children.append(navigate(QAccessible::Child, i));
+ }
+ return children;
+ }
+
+ QAInterface childAt(int index) const
+ {
+ return navigate(QAccessible::Child, index);
+ }
+
+ inline void doAction(int action, const QVariantList &params = QVariantList()) const
+ {
+ if (!checkValid())
+ return;
+
+ base.interface->doAction(action, child, params);
+ }
+
+ QAInterface navigate(RelationFlag relation, int entry) const;
+
+ inline QObject * object() const
+ {
+ if (!checkValid())
+ return 0;
+
+ return base.interface->object();
+ }
+
+ QAInterface objectInterface() const
+ {
+ if (!checkValid())
+ return QAInterface();
+
+ QObject *obj = object();
+ QAInterface current = *this;
+ while (obj == 0)
+ {
+ QAInterface parent = current.parent();
+ if (parent.isValid() == false)
+ break;
+ obj = parent.object();
+ current = parent;
+ }
+ return current;
+ }
+
+ inline HIObjectRef hiObject() const
+ {
+ if (!checkValid())
+ return 0;
+ QWidget * const widget = qobject_cast<QWidget * const>(object());
+ if (widget)
+ return (HIObjectRef)widget->winId();
+ else
+ return 0;
+ }
+
+ inline QObject * cachedObject() const
+ {
+ if (!checkValid())
+ return 0;
+ return m_cachedObject;
+ }
+
+ inline QRect rect() const
+ {
+ if (!checkValid())
+ return QRect();
+ return base.interface->rect(child);
+ }
+
+ inline Role role() const
+ {
+ if (!checkValid())
+ return QAccessible::NoRole;
+ return base.interface->role(child);
+ }
+
+ inline void setText(Text t, const QString &text) const
+ {
+ if (!checkValid())
+ return;
+ base.interface->setText(t, child, text);
+ }
+
+ inline State state() const
+ {
+ if (!checkValid())
+ return 0;
+ return base.interface->state(child);
+ }
+
+ inline QString text (Text text) const
+ {
+ if (!checkValid())
+ return QString();
+ return base.interface->text(text, child);
+ }
+
+ inline QString value() const
+ { return text(QAccessible::Value); }
+
+ inline QString name() const
+ { return text(QAccessible::Name); }
+
+ inline int userActionCount() const
+ {
+ if (!checkValid())
+ return 0;
+ return base.interface->userActionCount(child);
+ }
+
+ inline QString className() const
+ {
+ if (!checkValid())
+ return QString();
+ return QLatin1String(base.interface->object()->metaObject()->className());
+ }
+
+ inline bool isHIView() const
+ { return (child == 0 && object() != 0); }
+
+ inline int id() const
+ { return child; }
+
+ inline bool isValid() const
+ {
+ return (base.interface != 0 && base.interface->isValid());
+ }
+
+ QAInterface parent() const
+ { return navigate(QAccessible::Ancestor, 1); }
+
+ QAccessibleInterfaceWrapper interfaceWrapper() const
+ { return base; }
+
+protected:
+ bool checkValid() const
+ {
+ const bool valid = isValid();
+#ifdef Q_ACCESSIBLE_MAC_DEBUG
+ if (!valid)
+ qFatal("QAccessible_mac: tried to use invalid interface.");
+#endif
+ return valid;
+ }
+
+ QAccessibleInterfaceWrapper base;
+ QObject *m_cachedObject;
+ int child;
+};
+
+Q_AUTOTEST_EXPORT QDebug operator<<(QDebug debug, const QAInterface &interface);
+
+/*
+ QAElement is a thin wrapper around an AXUIElementRef that automates
+ the ref-counting.
+*/
+class Q_AUTOTEST_EXPORT QAElement
+{
+public:
+ QAElement();
+ explicit QAElement(AXUIElementRef elementRef);
+ QAElement(const QAElement &element);
+ QAElement(HIObjectRef, int child);
+ ~QAElement();
+
+ inline HIObjectRef object() const
+ {
+#ifndef Q_WS_MAC64
+ return AXUIElementGetHIObject(elementRef);
+#else
+ return 0;
+#endif
+ }
+
+ inline int id() const
+ {
+ UInt64 theId;
+#ifndef QT_MAC_USE_COCOA
+ AXUIElementGetIdentifier(elementRef, &theId);
+#else
+ theId = 0;
+#endif
+ return theId;
+ }
+
+ inline AXUIElementRef element() const
+ {
+ return elementRef;
+ }
+
+ inline bool isValid() const
+ {
+ return (elementRef != 0);
+ }
+
+ void operator=(const QAElement &other);
+ bool operator==(const QAElement &other) const;
+private:
+ AXUIElementRef elementRef;
+};
+
+
+class QInterfaceFactory
+{
+public:
+ virtual QAInterface interface(UInt64 identifier) = 0;
+ virtual QAElement element(int id) = 0;
+ virtual QAElement element(const QAInterface &interface)
+ {
+ return element(interface.id());
+ }
+ virtual void registerChildren() = 0;
+ virtual ~QInterfaceFactory() {}
+};
+
+/*
+ QAccessibleHierarchyManager bridges the Mac and Qt accessibility hierarchies.
+ There is a one-to-one relationship between QAElements on the Mac side
+ and QAInterfaces on the Qt side, and this class provies lookup funcitons
+ that translates between these to items.
+
+ The identity of a QAInterface is determined by its QAccessibleInterface and
+ child identifier, and the identity of a QAElement is determined by its
+ HIObjectRef and identifier.
+
+ QAccessibleHierarchyManager receives QObject::destroyed() signals and deletes
+ the accessibility objects for destroyed objects.
+*/
+class Q_AUTOTEST_EXPORT QAccessibleHierarchyManager : public QObject
+{
+Q_OBJECT
+public:
+ ~QAccessibleHierarchyManager() { reset(); }
+ static QAccessibleHierarchyManager *instance();
+ void reset();
+
+ QAElement registerInterface(QObject *object, int child);
+ QAElement registerInterface(const QAInterface &interface);
+ void registerInterface(QObject *object, HIObjectRef hiobject, QInterfaceFactory *interfaceFactory);
+
+ void registerChildren(const QAInterface &interface);
+
+ QAInterface lookup(const AXUIElementRef &element);
+ QAInterface lookup(const QAElement &element);
+ QAElement lookup(const QAInterface &interface);
+ QAElement lookup(QObject * const object, int id);
+private slots:
+ void objectDestroyed(QObject *);
+private:
+ typedef QHash<QObject *, QInterfaceFactory *> QObjectElementHash;
+ typedef QHash<HIObjectRef, QInterfaceFactory *> HIObjectInterfaceHash;
+ typedef QHash<QObject *, HIObjectRef> QObjectHIObjectHash;
+
+ QObjectElementHash qobjectElementHash;
+ HIObjectInterfaceHash hiobjectInterfaceHash;
+ QObjectHIObjectHash qobjectHiobjectHash;
+};
+
+Q_AUTOTEST_EXPORT bool isItInteresting(const QAInterface &interface);
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/gui/accessible/qaccessible_unix.cpp b/src/gui/accessible/qaccessible_unix.cpp
new file mode 100644
index 0000000000..ac51e4fb72
--- /dev/null
+++ b/src/gui/accessible/qaccessible_unix.cpp
@@ -0,0 +1,134 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qaccessible.h"
+#include "qaccessiblebridge.h"
+
+#ifndef QT_NO_ACCESSIBILITY
+
+#include "qcoreapplication.h"
+#include "qmutex.h"
+#include "qvector.h"
+#include "private/qfactoryloader_p.h"
+
+#include <stdlib.h>
+
+QT_BEGIN_NAMESPACE
+
+#ifndef QT_NO_LIBRARY
+Q_GLOBAL_STATIC_WITH_ARGS(QFactoryLoader, loader,
+ (QAccessibleBridgeFactoryInterface_iid, QLatin1String("/accessiblebridge")))
+#endif
+Q_GLOBAL_STATIC(QVector<QAccessibleBridge *>, bridges)
+static bool isInit = false;
+
+void QAccessible::initialize()
+{
+ if (isInit)
+ return;
+ isInit = true;
+
+ if (qgetenv("QT_ACCESSIBILITY") != "1")
+ return;
+#ifndef QT_NO_LIBRARY
+ const QStringList l = loader()->keys();
+ for (int i = 0; i < l.count(); ++i) {
+ if (QAccessibleBridgeFactoryInterface *factory =
+ qobject_cast<QAccessibleBridgeFactoryInterface*>(loader()->instance(l.at(i)))) {
+ QAccessibleBridge * bridge = factory->create(l.at(i));
+ if (bridge)
+ bridges()->append(bridge);
+ }
+ }
+#endif
+}
+
+void QAccessible::cleanup()
+{
+ qDeleteAll(*bridges());
+}
+
+void QAccessible::updateAccessibility(QObject *o, int who, Event reason)
+{
+ Q_ASSERT(o);
+
+ if (updateHandler) {
+ updateHandler(o, who, reason);
+ return;
+ }
+
+ initialize();
+ if (bridges()->isEmpty())
+ return;
+
+ QAccessibleInterface *iface = QAccessible::queryAccessibleInterface(o);
+ if (!iface)
+ return;
+
+ for (int i = 0; i < bridges()->count(); ++i)
+ bridges()->at(i)->notifyAccessibilityUpdate(reason, iface, who);
+ delete iface;
+}
+
+void QAccessible::setRootObject(QObject *o)
+{
+ if (rootObjectHandler) {
+ rootObjectHandler(o);
+ return;
+ }
+
+ initialize();
+ if (bridges()->isEmpty())
+ return;
+
+ if (!o)
+ return;
+
+ for (int i = 0; i < bridges()->count(); ++i) {
+ QAccessibleInterface *iface = QAccessible::queryAccessibleInterface(o);
+ bridges()->at(i)->setRootObject(iface);
+ }
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_ACCESSIBILITY
+
diff --git a/src/gui/accessible/qaccessible_win.cpp b/src/gui/accessible/qaccessible_win.cpp
new file mode 100644
index 0000000000..99cc272b78
--- /dev/null
+++ b/src/gui/accessible/qaccessible_win.cpp
@@ -0,0 +1,1219 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#include "qaccessible.h"
+#ifndef QT_NO_ACCESSIBILITY
+
+#include "qapplication.h"
+#include "qlibrary.h"
+#include "qmessagebox.h" // ### dependency
+#include "qt_windows.h"
+#include "qwidget.h"
+#include "qsettings.h"
+
+#include <winuser.h>
+#if !defined(WINABLEAPI)
+# if defined(Q_OS_WINCE)
+# include <bldver.h>
+# endif
+# include <winable.h>
+#endif
+
+#include <oleacc.h>
+#if !defined(Q_CC_BOR) && !defined (Q_CC_GNU)
+#include <comdef.h>
+#endif
+
+#ifdef Q_OS_WINCE
+#include "qguifunctions_wince.h"
+#endif
+
+QT_BEGIN_NAMESPACE
+
+//#define DEBUG_SHOW_ATCLIENT_COMMANDS
+#ifdef DEBUG_SHOW_ATCLIENT_COMMANDS
+QT_BEGIN_INCLUDE_NAMESPACE
+#include <qdebug.h>
+QT_END_INCLUDE_NAMESPACE
+
+static const char *roleString(QAccessible::Role role)
+{
+ static const char *roles[] = {
+ "NoRole" /*= 0x00000000*/,
+ "TitleBar" /*= 0x00000001*/,
+ "MenuBar" /*= 0x00000002*/,
+ "ScrollBar" /*= 0x00000003*/,
+ "Grip" /*= 0x00000004*/,
+ "Sound" /*= 0x00000005*/,
+ "Cursor" /*= 0x00000006*/,
+ "Caret" /*= 0x00000007*/,
+ "AlertMessage" /*= 0x00000008*/,
+ "Window" /*= 0x00000009*/,
+ "Client" /*= 0x0000000A*/,
+ "PopupMenu" /*= 0x0000000B*/,
+ "MenuItem" /*= 0x0000000C*/,
+ "ToolTip" /*= 0x0000000D*/,
+ "Application" /*= 0x0000000E*/,
+ "Document" /*= 0x0000000F*/,
+ "Pane" /*= 0x00000010*/,
+ "Chart" /*= 0x00000011*/,
+ "Dialog" /*= 0x00000012*/,
+ "Border" /*= 0x00000013*/,
+ "Grouping" /*= 0x00000014*/,
+ "Separator" /*= 0x00000015*/,
+ "ToolBar" /*= 0x00000016*/,
+ "StatusBar" /*= 0x00000017*/,
+ "Table" /*= 0x00000018*/,
+ "ColumnHeader" /*= 0x00000019*/,
+ "RowHeader" /*= 0x0000001A*/,
+ "Column" /*= 0x0000001B*/,
+ "Row" /*= 0x0000001C*/,
+ "Cell" /*= 0x0000001D*/,
+ "Link" /*= 0x0000001E*/,
+ "HelpBalloon" /*= 0x0000001F*/,
+ "Assistant" /*= 0x00000020*/,
+ "List" /*= 0x00000021*/,
+ "ListItem" /*= 0x00000022*/,
+ "Tree" /*= 0x00000023*/,
+ "TreeItem" /*= 0x00000024*/,
+ "PageTab" /*= 0x00000025*/,
+ "PropertyPage" /*= 0x00000026*/,
+ "Indicator" /*= 0x00000027*/,
+ "Graphic" /*= 0x00000028*/,
+ "StaticText" /*= 0x00000029*/,
+ "EditableText" /*= 0x0000002A*/, // Editable, selectable, etc.
+ "PushButton" /*= 0x0000002B*/,
+ "CheckBox" /*= 0x0000002C*/,
+ "RadioButton" /*= 0x0000002D*/,
+ "ComboBox" /*= 0x0000002E*/,
+ "DropList" /*= 0x0000002F*/, // commented out
+ "ProgressBar" /*= 0x00000030*/,
+ "Dial" /*= 0x00000031*/,
+ "HotkeyField" /*= 0x00000032*/,
+ "Slider" /*= 0x00000033*/,
+ "SpinBox" /*= 0x00000034*/,
+ "Canvas" /*= 0x00000035*/,
+ "Animation" /*= 0x00000036*/,
+ "Equation" /*= 0x00000037*/,
+ "ButtonDropDown" /*= 0x00000038*/,
+ "ButtonMenu" /*= 0x00000039*/,
+ "ButtonDropGrid" /*= 0x0000003A*/,
+ "Whitespace" /*= 0x0000003B*/,
+ "PageTabList" /*= 0x0000003C*/,
+ "Clock" /*= 0x0000003D*/,
+ "Splitter" /*= 0x0000003E*/,
+ "LayeredPane" /*= 0x0000003F*/,
+ "UserRole" /*= 0x0000ffff*/
+ };
+
+ if (role >=0x40)
+ role = QAccessible::UserRole;
+ return roles[int(role)];
+}
+
+void showDebug(const char* funcName, const QAccessibleInterface *iface)
+{
+ qDebug() << "Role:" << roleString(iface->role(0))
+ << "Name:" << iface->text(QAccessible::Name, 0)
+ << "State:" << QString::number(int(iface->state(0)), 16)
+ << QLatin1String(funcName);
+}
+#else
+# define showDebug(f, iface)
+#endif
+
+void QAccessible::initialize()
+{
+
+}
+void QAccessible::cleanup()
+{
+
+}
+
+void QAccessible::updateAccessibility(QObject *o, int who, Event reason)
+{
+ Q_ASSERT(o);
+
+ if (updateHandler) {
+ updateHandler(o, who, reason);
+ return;
+ }
+
+ QByteArray soundName;
+ switch (reason) {
+ case PopupMenuStart:
+ soundName = "MenuPopup";
+ break;
+
+ case MenuCommand:
+ soundName = "MenuCommand";
+ break;
+
+ case Alert:
+ {
+#ifndef QT_NO_MESSAGEBOX
+ QMessageBox *mb = qobject_cast<QMessageBox*>(o);
+ if (mb) {
+ switch (mb->icon()) {
+ case QMessageBox::Warning:
+ soundName = "SystemExclamation";
+ break;
+ case QMessageBox::Critical:
+ soundName = "SystemHand";
+ break;
+ case QMessageBox::Information:
+ soundName = "SystemAsterisk";
+ break;
+ default:
+ break;
+ }
+ } else
+#endif // QT_NO_MESSAGEBOX
+ {
+ soundName = "SystemAsterisk";
+ }
+
+ }
+ break;
+ default:
+ break;
+ }
+
+ if (soundName.size()) {
+#ifndef QT_NO_SETTINGS
+ QSettings settings(QLatin1String("HKEY_CURRENT_USER\\AppEvents\\Schemes\\Apps\\.Default\\") +
+ QString::fromLatin1(soundName.constData()), QSettings::NativeFormat);
+ QString file = settings.value(QLatin1String(".Current/.")).toString();
+#else
+ QString file;
+#endif
+ if (!file.isEmpty()) {
+ QT_WA({
+ PlaySoundW(reinterpret_cast<const wchar_t *> (QString::fromLatin1(soundName).utf16()), 0, SND_ALIAS | SND_ASYNC | SND_NODEFAULT | SND_NOWAIT );
+ } , {
+ PlaySoundA(soundName.constData(), 0, SND_ALIAS | SND_ASYNC | SND_NODEFAULT | SND_NOWAIT );
+ });
+ }
+ }
+
+ if (!isActive())
+ return;
+
+ typedef void (WINAPI *PtrNotifyWinEvent)(DWORD, HWND, LONG, LONG);
+
+#if defined(Q_OS_WINCE) // ### TODO: check for NotifyWinEvent in CE 6.0
+ // There is no user32.lib nor NotifyWinEvent for CE
+ return;
+#else
+ static PtrNotifyWinEvent ptrNotifyWinEvent = 0;
+ static bool resolvedNWE = false;
+ if (!resolvedNWE) {
+ resolvedNWE = true;
+ ptrNotifyWinEvent = (PtrNotifyWinEvent)QLibrary::resolve(QLatin1String("user32"), "NotifyWinEvent");
+ }
+ if (!ptrNotifyWinEvent)
+ return;
+
+ // An event has to be associated with a window,
+ // so find the first parent that is a widget.
+ QWidget *w = 0;
+ if (o->isWidgetType()) {
+ w = (QWidget*)o;
+ } else {
+ QObject *p = o;
+ while ((p = p->parent()) != 0) {
+ if (p->isWidgetType()) {
+ w = (QWidget*)p;
+ break;
+ }
+ }
+ }
+
+ if (!w) {
+ if (reason != QAccessible::ContextHelpStart &&
+ reason != QAccessible::ContextHelpEnd)
+ w = qApp->focusWidget();
+ if (!w) {
+ w = qApp->activeWindow();
+
+ if (!w)
+ return;
+
+// ### Fixme
+// if (!w) {
+// w = qApp->mainWidget();
+// if (!w)
+// return;
+// }
+ }
+ }
+
+ if (reason != MenuCommand) { // MenuCommand is faked
+ ptrNotifyWinEvent(reason, w->winId(), OBJID_CLIENT, who);
+ }
+#endif // Q_OS_WINCE
+}
+
+void QAccessible::setRootObject(QObject *o)
+{
+ if (rootObjectHandler) {
+ rootObjectHandler(o);
+ }
+}
+
+class QWindowsEnumerate : public IEnumVARIANT
+{
+public:
+ QWindowsEnumerate(const QVector<int> &a)
+ : ref(0), current(0),array(a)
+ {
+ }
+
+ virtual ~QWindowsEnumerate() {}
+
+ HRESULT STDMETHODCALLTYPE QueryInterface(REFIID, LPVOID *);
+ ULONG STDMETHODCALLTYPE AddRef();
+ ULONG STDMETHODCALLTYPE Release();
+
+ HRESULT STDMETHODCALLTYPE Clone(IEnumVARIANT **ppEnum);
+ HRESULT STDMETHODCALLTYPE Next(unsigned long celt, VARIANT FAR* rgVar, unsigned long FAR* pCeltFetched);
+ HRESULT STDMETHODCALLTYPE Reset();
+ HRESULT STDMETHODCALLTYPE Skip(unsigned long celt);
+
+private:
+ ULONG ref;
+ ULONG current;
+ QVector<int> array;
+};
+
+HRESULT STDMETHODCALLTYPE QWindowsEnumerate::QueryInterface(REFIID id, LPVOID *iface)
+{
+ *iface = 0;
+ if (id == IID_IUnknown)
+ *iface = (IUnknown*)this;
+ else if (id == IID_IEnumVARIANT)
+ *iface = (IEnumVARIANT*)this;
+
+ if (*iface) {
+ AddRef();
+ return S_OK;
+ }
+
+ return E_NOINTERFACE;
+}
+
+ULONG STDMETHODCALLTYPE QWindowsEnumerate::AddRef()
+{
+ return ++ref;
+}
+
+ULONG STDMETHODCALLTYPE QWindowsEnumerate::Release()
+{
+ if (!--ref) {
+ delete this;
+ return 0;
+ }
+ return ref;
+}
+
+HRESULT STDMETHODCALLTYPE QWindowsEnumerate::Clone(IEnumVARIANT **ppEnum)
+{
+ QWindowsEnumerate *penum = 0;
+ *ppEnum = 0;
+
+ penum = new QWindowsEnumerate(array);
+ if (!penum)
+ return E_OUTOFMEMORY;
+ penum->current = current;
+ penum->array = array;
+ penum->AddRef();
+ *ppEnum = penum;
+
+ return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE QWindowsEnumerate::Next(unsigned long celt, VARIANT FAR* rgVar, unsigned long FAR* pCeltFetched)
+{
+ if (pCeltFetched)
+ *pCeltFetched = 0;
+
+ ULONG l;
+ for (l = 0; l < celt; l++) {
+ VariantInit(&rgVar[l]);
+ if ((current+1) > (ULONG)array.size()) {
+ *pCeltFetched = l;
+ return S_FALSE;
+ }
+
+ rgVar[l].vt = VT_I4;
+ rgVar[l].lVal = array[(int)current];
+ ++current;
+ }
+ *pCeltFetched = l;
+ return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE QWindowsEnumerate::Reset()
+{
+ current = 0;
+ return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE QWindowsEnumerate::Skip(unsigned long celt)
+{
+ current += celt;
+ if (current > (ULONG)array.size()) {
+ current = array.size();
+ return S_FALSE;
+ }
+ return S_OK;
+}
+
+/*
+*/
+class QWindowsAccessible : public IAccessible, IOleWindow, QAccessible
+{
+public:
+ QWindowsAccessible(QAccessibleInterface *a)
+ : ref(0), accessible(a)
+ {
+ }
+
+ virtual ~QWindowsAccessible()
+ {
+ delete accessible;
+ }
+
+ HRESULT STDMETHODCALLTYPE QueryInterface(REFIID, LPVOID *);
+ ULONG STDMETHODCALLTYPE AddRef();
+ ULONG STDMETHODCALLTYPE Release();
+
+ HRESULT STDMETHODCALLTYPE GetTypeInfoCount(unsigned int *);
+ HRESULT STDMETHODCALLTYPE GetTypeInfo(unsigned int, unsigned long, ITypeInfo **);
+ HRESULT STDMETHODCALLTYPE GetIDsOfNames(const _GUID &, wchar_t **, unsigned int, unsigned long, long *);
+ HRESULT STDMETHODCALLTYPE Invoke(long, const _GUID &, unsigned long, unsigned short, tagDISPPARAMS *, tagVARIANT *, tagEXCEPINFO *, unsigned int *);
+
+ HRESULT STDMETHODCALLTYPE accHitTest(long xLeft, long yTop, VARIANT *pvarID);
+ HRESULT STDMETHODCALLTYPE accLocation(long *pxLeft, long *pyTop, long *pcxWidth, long *pcyHeight, VARIANT varID);
+ HRESULT STDMETHODCALLTYPE accNavigate(long navDir, VARIANT varStart, VARIANT *pvarEnd);
+ HRESULT STDMETHODCALLTYPE get_accChild(VARIANT varChildID, IDispatch** ppdispChild);
+ HRESULT STDMETHODCALLTYPE get_accChildCount(long* pcountChildren);
+ HRESULT STDMETHODCALLTYPE get_accParent(IDispatch** ppdispParent);
+
+ HRESULT STDMETHODCALLTYPE accDoDefaultAction(VARIANT varID);
+ HRESULT STDMETHODCALLTYPE get_accDefaultAction(VARIANT varID, BSTR* pszDefaultAction);
+ HRESULT STDMETHODCALLTYPE get_accDescription(VARIANT varID, BSTR* pszDescription);
+ HRESULT STDMETHODCALLTYPE get_accHelp(VARIANT varID, BSTR *pszHelp);
+ HRESULT STDMETHODCALLTYPE get_accHelpTopic(BSTR *pszHelpFile, VARIANT varChild, long *pidTopic);
+ HRESULT STDMETHODCALLTYPE get_accKeyboardShortcut(VARIANT varID, BSTR *pszKeyboardShortcut);
+ HRESULT STDMETHODCALLTYPE get_accName(VARIANT varID, BSTR* pszName);
+ HRESULT STDMETHODCALLTYPE put_accName(VARIANT varChild, BSTR szName);
+ HRESULT STDMETHODCALLTYPE get_accRole(VARIANT varID, VARIANT *pvarRole);
+ HRESULT STDMETHODCALLTYPE get_accState(VARIANT varID, VARIANT *pvarState);
+ HRESULT STDMETHODCALLTYPE get_accValue(VARIANT varID, BSTR* pszValue);
+ HRESULT STDMETHODCALLTYPE put_accValue(VARIANT varChild, BSTR szValue);
+
+ HRESULT STDMETHODCALLTYPE accSelect(long flagsSelect, VARIANT varID);
+ HRESULT STDMETHODCALLTYPE get_accFocus(VARIANT *pvarID);
+ HRESULT STDMETHODCALLTYPE get_accSelection(VARIANT *pvarChildren);
+
+ HRESULT STDMETHODCALLTYPE GetWindow(HWND *phwnd);
+ HRESULT STDMETHODCALLTYPE ContextSensitiveHelp(BOOL fEnterMode);
+
+private:
+ ULONG ref;
+ QAccessibleInterface *accessible;
+};
+
+static inline BSTR QStringToBSTR(const QString &str)
+{
+ BSTR bstrVal;
+
+ int wlen = str.length()+1;
+ bstrVal = SysAllocStringByteLen(0, wlen*2);
+ memcpy(bstrVal, str.unicode(), sizeof(QChar)*(wlen));
+ bstrVal[wlen] = 0;
+
+ return bstrVal;
+}
+
+/*
+*/
+IAccessible *qt_createWindowsAccessible(QAccessibleInterface *access)
+{
+ QWindowsAccessible *acc = new QWindowsAccessible(access);
+ IAccessible *iface;
+ acc->QueryInterface(IID_IAccessible, (void**)&iface);
+
+ return iface;
+}
+
+/*
+ IUnknown
+*/
+HRESULT STDMETHODCALLTYPE QWindowsAccessible::QueryInterface(REFIID id, LPVOID *iface)
+{
+ *iface = 0;
+ if (id == IID_IUnknown)
+ *iface = (IUnknown*)(IDispatch*)this;
+ else if (id == IID_IDispatch)
+ *iface = (IDispatch*)this;
+ else if (id == IID_IAccessible)
+ *iface = (IAccessible*)this;
+ else if (id == IID_IOleWindow)
+ *iface = (IOleWindow*)this;
+ else
+ return E_NOINTERFACE;
+
+ AddRef();
+ return S_OK;
+}
+
+ULONG STDMETHODCALLTYPE QWindowsAccessible::AddRef()
+{
+ return ++ref;
+}
+
+ULONG STDMETHODCALLTYPE QWindowsAccessible::Release()
+{
+ if (!--ref) {
+ delete this;
+ return 0;
+ }
+ return ref;
+}
+
+/*
+ IDispatch
+*/
+
+HRESULT STDMETHODCALLTYPE QWindowsAccessible::GetTypeInfoCount(unsigned int * pctinfo)
+{
+ // We don't use a type library
+ *pctinfo = 0;
+ return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE QWindowsAccessible::GetTypeInfo(unsigned int, unsigned long, ITypeInfo **pptinfo)
+{
+ // We don't use a type library
+ *pptinfo = 0;
+ return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE QWindowsAccessible::GetIDsOfNames(const _GUID &, wchar_t **rgszNames, unsigned int, unsigned long, long *rgdispid)
+{
+#if !defined(Q_CC_BOR) && !defined(Q_CC_GNU)
+ // PROPERTIES: Hierarchical
+ if (_bstr_t(rgszNames[0]) == _bstr_t(L"accParent"))
+ rgdispid[0] = DISPID_ACC_PARENT;
+ else if(_bstr_t(rgszNames[0]) == _bstr_t(L"accChildCount"))
+ rgdispid[0] = DISPID_ACC_CHILDCOUNT;
+ else if(_bstr_t(rgszNames[0]) == _bstr_t(L"accChild"))
+ rgdispid[0] = DISPID_ACC_CHILD;
+
+ // PROPERTIES: Descriptional
+ else if(_bstr_t(rgszNames[0]) == _bstr_t(L"accName("))
+ rgdispid[0] = DISPID_ACC_NAME;
+ else if(_bstr_t(rgszNames[0]) == _bstr_t(L"accValue"))
+ rgdispid[0] = DISPID_ACC_VALUE;
+ else if(_bstr_t(rgszNames[0]) == _bstr_t(L"accDescription"))
+ rgdispid[0] = DISPID_ACC_DESCRIPTION;
+ else if(_bstr_t(rgszNames[0]) == _bstr_t(L"accRole"))
+ rgdispid[0] = DISPID_ACC_ROLE;
+ else if(_bstr_t(rgszNames[0]) == _bstr_t(L"accState"))
+ rgdispid[0] = DISPID_ACC_STATE;
+ else if(_bstr_t(rgszNames[0]) == _bstr_t(L"accHelp"))
+ rgdispid[0] = DISPID_ACC_HELP;
+ else if(_bstr_t(rgszNames[0]) == _bstr_t(L"accHelpTopic"))
+ rgdispid[0] = DISPID_ACC_HELPTOPIC;
+ else if(_bstr_t(rgszNames[0]) == _bstr_t(L"accKeyboardShortcut"))
+ rgdispid[0] = DISPID_ACC_KEYBOARDSHORTCUT;
+ else if(_bstr_t(rgszNames[0]) == _bstr_t(L"accFocus"))
+ rgdispid[0] = DISPID_ACC_FOCUS;
+ else if(_bstr_t(rgszNames[0]) == _bstr_t(L"accSelection"))
+ rgdispid[0] = DISPID_ACC_SELECTION;
+ else if(_bstr_t(rgszNames[0]) == _bstr_t(L"accDefaultAction"))
+ rgdispid[0] = DISPID_ACC_DEFAULTACTION;
+
+ // METHODS
+ else if(_bstr_t(rgszNames[0]) == _bstr_t(L"accSelect"))
+ rgdispid[0] = DISPID_ACC_SELECT;
+ else if(_bstr_t(rgszNames[0]) == _bstr_t(L"accLocation"))
+ rgdispid[0] = DISPID_ACC_LOCATION;
+ else if(_bstr_t(rgszNames[0]) == _bstr_t(L"accNavigate"))
+ rgdispid[0] = DISPID_ACC_NAVIGATE;
+ else if(_bstr_t(rgszNames[0]) == _bstr_t(L"accHitTest"))
+ rgdispid[0] = DISPID_ACC_HITTEST;
+ else if(_bstr_t(rgszNames[0]) == _bstr_t(L"accDoDefaultAction"))
+ rgdispid[0] = DISPID_ACC_DODEFAULTACTION;
+ else
+ return DISP_E_UNKNOWNINTERFACE;
+
+ return S_OK;
+#else
+ Q_UNUSED(rgszNames);
+ Q_UNUSED(rgdispid);
+
+ return DISP_E_MEMBERNOTFOUND;
+#endif
+}
+
+HRESULT STDMETHODCALLTYPE QWindowsAccessible::Invoke(long dispIdMember, const _GUID &, unsigned long, unsigned short wFlags, tagDISPPARAMS *pDispParams, tagVARIANT *pVarResult, tagEXCEPINFO *, unsigned int *)
+{
+ HRESULT hr = DISP_E_MEMBERNOTFOUND;
+
+ switch(dispIdMember)
+ {
+ case DISPID_ACC_PARENT:
+ if (wFlags == DISPATCH_PROPERTYGET) {
+ if (!pVarResult)
+ return E_INVALIDARG;
+ hr = get_accParent(&pVarResult->pdispVal);
+ } else {
+ hr = DISP_E_MEMBERNOTFOUND;
+ }
+ break;
+
+ case DISPID_ACC_CHILDCOUNT:
+ if (wFlags == DISPATCH_PROPERTYGET) {
+ if (!pVarResult)
+ return E_INVALIDARG;
+ hr = get_accChildCount(&pVarResult->lVal);
+ } else {
+ hr = DISP_E_MEMBERNOTFOUND;
+ }
+ break;
+
+ case DISPID_ACC_CHILD:
+ if (wFlags == DISPATCH_PROPERTYGET)
+ hr = get_accChild(pDispParams->rgvarg[0], &pVarResult->pdispVal);
+ else
+ hr = DISP_E_MEMBERNOTFOUND;
+ break;
+
+ case DISPID_ACC_NAME:
+ if (wFlags == DISPATCH_PROPERTYGET)
+ hr = get_accName(pDispParams->rgvarg[0], &pVarResult->bstrVal);
+ else if (wFlags == DISPATCH_PROPERTYPUT)
+ hr = put_accName(pDispParams->rgvarg[0], pVarResult->bstrVal);
+ else
+ hr = DISP_E_MEMBERNOTFOUND;
+ break;
+
+ case DISPID_ACC_VALUE:
+ if (wFlags == DISPATCH_PROPERTYGET)
+ hr = get_accValue(pDispParams->rgvarg[0], &pVarResult->bstrVal);
+ else if (wFlags == DISPATCH_PROPERTYPUT)
+ hr = put_accValue(pDispParams->rgvarg[0], pVarResult->bstrVal);
+ else
+ hr = DISP_E_MEMBERNOTFOUND;
+ break;
+
+ case DISPID_ACC_DESCRIPTION:
+ if (wFlags == DISPATCH_PROPERTYGET)
+ hr = get_accDescription(pDispParams->rgvarg[0], &pVarResult->bstrVal);
+ else
+ hr = DISP_E_MEMBERNOTFOUND;
+ break;
+
+ case DISPID_ACC_ROLE:
+ if (wFlags == DISPATCH_PROPERTYGET)
+ hr = get_accRole(pDispParams->rgvarg[0], pVarResult);
+ else
+ hr = DISP_E_MEMBERNOTFOUND;
+ break;
+
+ case DISPID_ACC_STATE:
+ if (wFlags == DISPATCH_PROPERTYGET)
+ hr = get_accState(pDispParams->rgvarg[0], pVarResult);
+ else
+ hr = DISP_E_MEMBERNOTFOUND;
+ break;
+
+ case DISPID_ACC_HELP:
+ if (wFlags == DISPATCH_PROPERTYGET)
+ hr = get_accHelp(pDispParams->rgvarg[0], &pVarResult->bstrVal);
+ else
+ hr = DISP_E_MEMBERNOTFOUND;
+ break;
+
+ case DISPID_ACC_HELPTOPIC:
+ if (wFlags == DISPATCH_PROPERTYGET)
+ hr = get_accHelpTopic(&pDispParams->rgvarg[2].bstrVal, pDispParams->rgvarg[1], &pDispParams->rgvarg[0].lVal);
+ else
+ hr = DISP_E_MEMBERNOTFOUND;
+ break;
+
+ case DISPID_ACC_KEYBOARDSHORTCUT:
+ if (wFlags == DISPATCH_PROPERTYGET)
+ hr = get_accKeyboardShortcut(pDispParams->rgvarg[0], &pVarResult->bstrVal);
+ else
+ hr = DISP_E_MEMBERNOTFOUND;
+ break;
+
+ case DISPID_ACC_FOCUS:
+ if (wFlags == DISPATCH_PROPERTYGET)
+ hr = get_accFocus(pVarResult);
+ else
+ hr = DISP_E_MEMBERNOTFOUND;
+ break;
+
+ case DISPID_ACC_SELECTION:
+ if (wFlags == DISPATCH_PROPERTYGET)
+ hr = get_accSelection(pVarResult);
+ else
+ hr = DISP_E_MEMBERNOTFOUND;
+ break;
+
+ case DISPID_ACC_DEFAULTACTION:
+ if (wFlags == DISPATCH_PROPERTYGET)
+ hr = get_accDefaultAction(pDispParams->rgvarg[0], &pVarResult->bstrVal);
+ else
+ hr = DISP_E_MEMBERNOTFOUND;
+ break;
+
+ case DISPID_ACC_SELECT:
+ if (wFlags == DISPATCH_METHOD)
+ hr = accSelect(pDispParams->rgvarg[1].lVal, pDispParams->rgvarg[0]);
+ else
+ hr = DISP_E_MEMBERNOTFOUND;
+ break;
+
+ case DISPID_ACC_LOCATION:
+ if (wFlags == DISPATCH_METHOD)
+ hr = accLocation(&pDispParams->rgvarg[4].lVal, &pDispParams->rgvarg[3].lVal, &pDispParams->rgvarg[2].lVal, &pDispParams->rgvarg[1].lVal, pDispParams->rgvarg[0]);
+ else
+ hr = DISP_E_MEMBERNOTFOUND;
+ break;
+
+ case DISPID_ACC_NAVIGATE:
+ if (wFlags == DISPATCH_METHOD)
+ hr = accNavigate(pDispParams->rgvarg[1].lVal, pDispParams->rgvarg[0], pVarResult);
+ else
+ hr = DISP_E_MEMBERNOTFOUND;
+ break;
+
+ case DISPID_ACC_HITTEST:
+ if (wFlags == DISPATCH_METHOD)
+ hr = accHitTest(pDispParams->rgvarg[1].lVal, pDispParams->rgvarg[0].lVal, pVarResult);
+ else
+ hr = DISP_E_MEMBERNOTFOUND;
+ break;
+
+ case DISPID_ACC_DODEFAULTACTION:
+ if (wFlags == DISPATCH_METHOD)
+ hr = accDoDefaultAction(pDispParams->rgvarg[0]);
+ else
+ hr = DISP_E_MEMBERNOTFOUND;
+ break;
+
+ default:
+ hr = DISP_E_MEMBERNOTFOUND;
+ break;
+ }
+
+ if (!SUCCEEDED(hr)) {
+ return hr;
+ }
+ return hr;
+}
+
+/*
+ IAccessible
+*/
+HRESULT STDMETHODCALLTYPE QWindowsAccessible::accHitTest(long xLeft, long yTop, VARIANT *pvarID)
+{
+ showDebug(__FUNCTION__, accessible);
+ if (!accessible->isValid())
+ return E_FAIL;
+
+ int control = accessible->childAt(xLeft, yTop);
+ if (control == -1) {
+ (*pvarID).vt = VT_EMPTY;
+ return S_FALSE;
+ }
+ QAccessibleInterface *acc = 0;
+ if (control)
+ accessible->navigate(Child, control, &acc);
+ if (!acc) {
+ (*pvarID).vt = VT_I4;
+ (*pvarID).lVal = control;
+ return S_OK;
+ }
+
+ QWindowsAccessible* wacc = new QWindowsAccessible(acc);
+ IDispatch *iface = 0;
+ wacc->QueryInterface(IID_IDispatch, (void**)&iface);
+ if (iface) {
+ (*pvarID).vt = VT_DISPATCH;
+ (*pvarID).pdispVal = iface;
+ return S_OK;
+ } else {
+ delete wacc;
+ }
+
+ (*pvarID).vt = VT_EMPTY;
+ return S_FALSE;
+}
+
+HRESULT STDMETHODCALLTYPE QWindowsAccessible::accLocation(long *pxLeft, long *pyTop, long *pcxWidth, long *pcyHeight, VARIANT varID)
+{
+ showDebug(__FUNCTION__, accessible);
+ if (!accessible->isValid())
+ return E_FAIL;
+
+ QRect rect = accessible->rect(varID.lVal);
+ if (rect.isValid()) {
+ *pxLeft = rect.x();
+ *pyTop = rect.y();
+ *pcxWidth = rect.width();
+ *pcyHeight = rect.height();
+ } else {
+ *pxLeft = 0;
+ *pyTop = 0;
+ *pcxWidth = 0;
+ *pcyHeight = 0;
+ }
+ return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE QWindowsAccessible::accNavigate(long navDir, VARIANT varStart, VARIANT *pvarEnd)
+{
+ showDebug(__FUNCTION__, accessible);
+ if (!accessible->isValid())
+ return E_FAIL;
+
+ QAccessibleInterface *acc = 0;
+ int control = -1;
+ switch(navDir) {
+ case NAVDIR_FIRSTCHILD:
+ control = accessible->navigate(Child, 1, &acc);
+ break;
+ case NAVDIR_LASTCHILD:
+ control = accessible->navigate(Child, accessible->childCount(), &acc);
+ break;
+ case NAVDIR_NEXT:
+ case NAVDIR_PREVIOUS:
+ if (!varStart.lVal){
+ QAccessibleInterface *parent = 0;
+ accessible->navigate(Ancestor, 1, &parent);
+ if (parent) {
+ int index = parent->indexOfChild(accessible);
+ index += (navDir == NAVDIR_NEXT) ? 1 : -1;
+ if (index > 0 && index <= parent->childCount())
+ control = parent->navigate(Child, index, &acc);
+ delete parent;
+ }
+ } else {
+ int index = varStart.lVal;
+ index += (navDir == NAVDIR_NEXT) ? 1 : -1;
+ if (index > 0 && index <= accessible->childCount())
+ control = accessible->navigate(Child, index, &acc);
+ }
+ break;
+ case NAVDIR_UP:
+ control = accessible->navigate(Up, varStart.lVal, &acc);
+ break;
+ case NAVDIR_DOWN:
+ control = accessible->navigate(Down, varStart.lVal, &acc);
+ break;
+ case NAVDIR_LEFT:
+ control = accessible->navigate(Left, varStart.lVal, &acc);
+ break;
+ case NAVDIR_RIGHT:
+ control = accessible->navigate(Right, varStart.lVal, &acc);
+ break;
+ default:
+ break;
+ }
+ if (control == -1) {
+ (*pvarEnd).vt = VT_EMPTY;
+ return S_FALSE;
+ }
+ if (!acc) {
+ (*pvarEnd).vt = VT_I4;
+ (*pvarEnd).lVal = control;
+ return S_OK;
+ }
+
+ QWindowsAccessible* wacc = new QWindowsAccessible(acc);
+
+ IDispatch *iface = 0;
+ wacc->QueryInterface(IID_IDispatch, (void**)&iface);
+ if (iface) {
+ (*pvarEnd).vt = VT_DISPATCH;
+ (*pvarEnd).pdispVal = iface;
+ return S_OK;
+ } else {
+ delete wacc;
+ }
+
+ (*pvarEnd).vt = VT_EMPTY;
+ return S_FALSE;
+}
+
+HRESULT STDMETHODCALLTYPE QWindowsAccessible::get_accChild(VARIANT varChildID, IDispatch** ppdispChild)
+{
+ showDebug(__FUNCTION__, accessible);
+ if (!accessible->isValid())
+ return E_FAIL;
+
+ if (varChildID.vt == VT_EMPTY)
+ return E_INVALIDARG;
+
+ QAccessibleInterface *acc = 0;
+ RelationFlag rel = varChildID.lVal ? Child : Self;
+ accessible->navigate(rel, varChildID.lVal, &acc);
+
+ if (acc) {
+ QWindowsAccessible* wacc = new QWindowsAccessible(acc);
+ wacc->QueryInterface(IID_IDispatch, (void**)ppdispChild);
+ return S_OK;
+ }
+
+ *ppdispChild = 0;
+ return S_FALSE;
+}
+
+HRESULT STDMETHODCALLTYPE QWindowsAccessible::get_accChildCount(long* pcountChildren)
+{
+ showDebug(__FUNCTION__, accessible);
+ if (!accessible->isValid())
+ return E_FAIL;
+
+ *pcountChildren = accessible->childCount();
+ return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE QWindowsAccessible::get_accParent(IDispatch** ppdispParent)
+{
+ showDebug(__FUNCTION__, accessible);
+ if (!accessible->isValid())
+ return E_FAIL;
+
+ QAccessibleInterface *acc = 0;
+ accessible->navigate(Ancestor, 1, &acc);
+ if (acc) {
+ QWindowsAccessible* wacc = new QWindowsAccessible(acc);
+ wacc->QueryInterface(IID_IDispatch, (void**)ppdispParent);
+
+ if (*ppdispParent)
+ return S_OK;
+ }
+
+ *ppdispParent = 0;
+ return S_FALSE;
+}
+
+/*
+ Properties and methods
+*/
+HRESULT STDMETHODCALLTYPE QWindowsAccessible::accDoDefaultAction(VARIANT varID)
+{
+ showDebug(__FUNCTION__, accessible);
+ if (!accessible->isValid())
+ return E_FAIL;
+
+ return accessible->doAction(DefaultAction, varID.lVal, QVariantList()) ? S_OK : S_FALSE;
+}
+
+HRESULT STDMETHODCALLTYPE QWindowsAccessible::get_accDefaultAction(VARIANT varID, BSTR* pszDefaultAction)
+{
+ showDebug(__FUNCTION__, accessible);
+ if (!accessible->isValid())
+ return E_FAIL;
+
+ QString def = accessible->actionText(DefaultAction, Name, varID.lVal);
+ if (def.isEmpty()) {
+ *pszDefaultAction = 0;
+ return S_FALSE;
+ }
+
+ *pszDefaultAction = QStringToBSTR(def);
+ return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE QWindowsAccessible::get_accDescription(VARIANT varID, BSTR* pszDescription)
+{
+ showDebug(__FUNCTION__, accessible);
+ if (!accessible->isValid())
+ return E_FAIL;
+
+ QString descr = accessible->text(Description, varID.lVal);
+ if (descr.size()) {
+ *pszDescription = QStringToBSTR(descr);
+ return S_OK;
+ }
+
+ *pszDescription = 0;
+ return S_FALSE;
+}
+
+HRESULT STDMETHODCALLTYPE QWindowsAccessible::get_accHelp(VARIANT varID, BSTR *pszHelp)
+{
+ showDebug(__FUNCTION__, accessible);
+ if (!accessible->isValid())
+ return E_FAIL;
+
+ QString help = accessible->text(Help, varID.lVal);
+ if (help.size()) {
+ *pszHelp = QStringToBSTR(help);
+ return S_OK;
+ }
+
+ *pszHelp = 0;
+ return S_FALSE;
+}
+
+HRESULT STDMETHODCALLTYPE QWindowsAccessible::get_accHelpTopic(BSTR *, VARIANT, long *)
+{
+ return DISP_E_MEMBERNOTFOUND;
+}
+
+HRESULT STDMETHODCALLTYPE QWindowsAccessible::get_accKeyboardShortcut(VARIANT varID, BSTR *pszKeyboardShortcut)
+{
+ showDebug(__FUNCTION__, accessible);
+ if (!accessible->isValid())
+ return E_FAIL;
+
+ QString sc = accessible->text(Accelerator, varID.lVal);
+ if (sc.size()) {
+ *pszKeyboardShortcut = QStringToBSTR(sc);
+ return S_OK;
+ }
+
+ *pszKeyboardShortcut = 0;
+ return S_FALSE;
+}
+
+HRESULT STDMETHODCALLTYPE QWindowsAccessible::get_accName(VARIANT varID, BSTR* pszName)
+{
+ showDebug(__FUNCTION__, accessible);
+ if (!accessible->isValid())
+ return E_FAIL;
+
+ QString n = accessible->text(Name, varID.lVal);
+ if (n.size()) {
+ *pszName = QStringToBSTR(n);
+ return S_OK;
+ }
+
+ *pszName = 0;
+ return S_FALSE;
+}
+
+HRESULT STDMETHODCALLTYPE QWindowsAccessible::put_accName(VARIANT, BSTR)
+{
+ showDebug(__FUNCTION__, accessible);
+ return DISP_E_MEMBERNOTFOUND;
+}
+
+HRESULT STDMETHODCALLTYPE QWindowsAccessible::get_accRole(VARIANT varID, VARIANT *pvarRole)
+{
+ showDebug(__FUNCTION__, accessible);
+ if (!accessible->isValid())
+ return E_FAIL;
+
+ Role role = accessible->role(varID.lVal);
+ if (role != NoRole) {
+ (*pvarRole).vt = VT_I4;
+ (*pvarRole).lVal = role;
+ } else {
+ (*pvarRole).vt = VT_EMPTY;
+ }
+ return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE QWindowsAccessible::get_accState(VARIANT varID, VARIANT *pvarState)
+{
+ showDebug(__FUNCTION__, accessible);
+ if (!accessible->isValid())
+ return E_FAIL;
+
+ (*pvarState).vt = VT_I4;
+ (*pvarState).lVal = accessible->state(varID.lVal);
+ return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE QWindowsAccessible::get_accValue(VARIANT varID, BSTR* pszValue)
+{
+ showDebug(__FUNCTION__, accessible);
+ if (!accessible->isValid())
+ return E_FAIL;
+
+ QString value = accessible->text(Value, varID.lVal);
+ if (!value.isNull()) {
+ *pszValue = QStringToBSTR(value);
+ return S_OK;
+ }
+
+ *pszValue = 0;
+ return S_FALSE;
+}
+
+HRESULT STDMETHODCALLTYPE QWindowsAccessible::put_accValue(VARIANT, BSTR)
+{
+ showDebug(__FUNCTION__, accessible);
+ return DISP_E_MEMBERNOTFOUND;
+}
+
+HRESULT STDMETHODCALLTYPE QWindowsAccessible::accSelect(long flagsSelect, VARIANT varID)
+{
+ showDebug(__FUNCTION__, accessible);
+ if (!accessible->isValid())
+ return E_FAIL;
+
+ bool res = false;
+
+ if (flagsSelect & SELFLAG_TAKEFOCUS)
+ res = accessible->doAction(SetFocus, varID.lVal, QVariantList());
+ if (flagsSelect & SELFLAG_TAKESELECTION) {
+ accessible->doAction(ClearSelection, 0, QVariantList());
+ res = accessible->doAction(AddToSelection, varID.lVal, QVariantList());
+ }
+ if (flagsSelect & SELFLAG_EXTENDSELECTION)
+ res = accessible->doAction(ExtendSelection, varID.lVal, QVariantList());
+ if (flagsSelect & SELFLAG_ADDSELECTION)
+ res = accessible->doAction(AddToSelection, varID.lVal, QVariantList());
+ if (flagsSelect & SELFLAG_REMOVESELECTION)
+ res = accessible->doAction(RemoveSelection, varID.lVal, QVariantList());
+
+ return res ? S_OK : S_FALSE;
+}
+
+HRESULT STDMETHODCALLTYPE QWindowsAccessible::get_accFocus(VARIANT *pvarID)
+{
+ showDebug(__FUNCTION__, accessible);
+ if (!accessible->isValid())
+ return E_FAIL;
+
+ QAccessibleInterface *acc = 0;
+ int control = accessible->navigate(FocusChild, 1, &acc);
+ if (control == -1) {
+ (*pvarID).vt = VT_EMPTY;
+ return S_FALSE;
+ }
+ if (!acc || control == 0) {
+ (*pvarID).vt = VT_I4;
+ (*pvarID).lVal = control ? control : CHILDID_SELF;
+ return S_OK;
+ }
+
+ QWindowsAccessible* wacc = new QWindowsAccessible(acc);
+ IDispatch *iface = 0;
+ wacc->QueryInterface(IID_IDispatch, (void**)&iface);
+ if (iface) {
+ (*pvarID).vt = VT_DISPATCH;
+ (*pvarID).pdispVal = iface;
+ return S_OK;
+ } else {
+ delete wacc;
+ }
+
+ (*pvarID).vt = VT_EMPTY;
+ return S_FALSE;
+}
+
+HRESULT STDMETHODCALLTYPE QWindowsAccessible::get_accSelection(VARIANT *pvarChildren)
+{
+ showDebug(__FUNCTION__, accessible);
+ if (!accessible->isValid())
+ return E_FAIL;
+
+ int cc = accessible->childCount();
+ QVector<int> sel(cc);
+ int selIndex = 0;
+ for (int i = 1; i <= cc; ++i) {
+ QAccessibleInterface *child = 0;
+ int i2 = accessible->navigate(Child, i, &child);
+ bool isSelected = false;
+ if (child) {
+ isSelected = child->state(0) & Selected;
+ delete child;
+ child = 0;
+ } else {
+ isSelected = accessible->state(i2) & Selected;
+ }
+ if (isSelected)
+ sel[selIndex++] = i;
+ }
+ sel.resize(selIndex);
+ if (sel.isEmpty()) {
+ (*pvarChildren).vt = VT_EMPTY;
+ return S_FALSE;
+ }
+ if (sel.size() == 1) {
+ (*pvarChildren).vt = VT_I4;
+ (*pvarChildren).lVal = sel[0];
+ return S_OK;
+ }
+ IEnumVARIANT *iface = new QWindowsEnumerate(sel);
+ IUnknown *uiface;
+ iface->QueryInterface(IID_IUnknown, (void**)&uiface);
+ (*pvarChildren).vt = VT_UNKNOWN;
+ (*pvarChildren).punkVal = uiface;
+
+ return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE QWindowsAccessible::GetWindow(HWND *phwnd)
+{
+ *phwnd = 0;
+ if (!accessible->isValid())
+ return E_UNEXPECTED;
+
+ QObject *o = accessible->object();
+ if (!o || !o->isWidgetType())
+ return E_FAIL;
+
+ *phwnd = static_cast<QWidget*>(o)->winId();
+ return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE QWindowsAccessible::ContextSensitiveHelp(BOOL)
+{
+ return S_OK;
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_ACCESSIBILITY
diff --git a/src/gui/accessible/qaccessiblebridge.cpp b/src/gui/accessible/qaccessiblebridge.cpp
new file mode 100644
index 0000000000..7c03e9cf59
--- /dev/null
+++ b/src/gui/accessible/qaccessiblebridge.cpp
@@ -0,0 +1,158 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qaccessiblebridge.h"
+
+#ifndef QT_NO_ACCESSIBILITY
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QAccessibleBridge
+ \brief The QAccessibleBridge class is the base class for
+ accessibility back-ends.
+
+ \ingroup accessibility
+
+ Qt supports Microsoft Active Accessibility (MSAA), Mac OS X
+ Accessibility, and the Unix/X11 AT-SPI standard. By subclassing
+ QAccessibleBridge, you can support other backends than the
+ predefined ones.
+
+ Currently, custom bridges are only supported on Unix. We might
+ add support for them on other platforms as well if there is
+ enough demand.
+
+ \sa QAccessible, QAccessibleBridgePlugin
+*/
+
+/*!
+ \fn QAccessibleBridge::~QAccessibleBridge()
+
+ Destroys the accessibility bridge object.
+*/
+
+/*!
+ \fn void QAccessibleBridge::setRootObject(QAccessibleInterface *object)
+
+ This function is called by Qt at application startup to set the
+ root accessible object of the application to \a object. All other
+ accessible objects in the application can be reached by the
+ client using object navigation.
+*/
+
+/*!
+ \fn void QAccessibleBridge::notifyAccessibilityUpdate(int reason, QAccessibleInterface *interface, int child)
+
+ This function is called by Qt to notify the bridge about a change
+ in the accessibility information for object wrapped by the given
+ \a interface.
+
+ \a reason specifies the cause of the change. It can take values
+ of type QAccessible::Event.
+
+ \a child is the (1-based) index of the child element that has
+ changed. When \a child is 0, the object itself has changed.
+
+ \sa QAccessible::updateAccessibility()
+*/
+
+/*!
+ \class QAccessibleBridgePlugin
+ \brief The QAccessibleBridgePlugin class provides an abstract
+ base for accessibility bridge plugins.
+
+ \ingroup plugins
+ \ingroup accessibility
+
+ Writing an accessibility bridge plugin is achieved by subclassing
+ this base class, reimplementing the pure virtual functions keys()
+ and create(), and exporting the class with the
+ Q_EXPORT_PLUGIN2() macro.
+
+ \sa QAccessibleBridge, QAccessiblePlugin, {How to Create Qt Plugins}
+*/
+
+/*!
+ Constructs an accessibility bridge plugin with the given \a
+ parent. This is invoked automatically by the Q_EXPORT_PLUGIN2()
+ macro.
+*/
+QAccessibleBridgePlugin::QAccessibleBridgePlugin(QObject *parent)
+ : QObject(parent)
+{
+
+}
+
+/*!
+ Destroys the accessibility bridge plugin.
+
+ You never have to call this explicitly. Qt destroys a plugin
+ automatically when it is no longer used.
+*/
+QAccessibleBridgePlugin::~QAccessibleBridgePlugin()
+{
+
+}
+
+/*!
+ \fn QStringList QAccessibleBridgePlugin::keys() const
+
+ Returns the list of keys this plugins supports.
+
+ These keys must be the names of the bridges that this
+ plugin provides.
+
+ \sa create()
+*/
+
+/*!
+ \fn QAccessibleBridge *QAccessibleBridgePlugin::create(const QString &key)
+
+ Creates and returns the QAccessibleBridge object corresponding to
+ the given \a key. Keys are case sensitive.
+
+ \sa keys()
+*/
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_ACCESSIBILITY
diff --git a/src/gui/accessible/qaccessiblebridge.h b/src/gui/accessible/qaccessiblebridge.h
new file mode 100644
index 0000000000..0725e190d7
--- /dev/null
+++ b/src/gui/accessible/qaccessiblebridge.h
@@ -0,0 +1,92 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QACCESSIBLEBRIDGE_H
+#define QACCESSIBLEBRIDGE_H
+
+#include <QtCore/qplugin.h>
+#include <QtCore/qfactoryinterface.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+#ifndef QT_NO_ACCESSIBILITY
+
+class QAccessibleInterface;
+
+class QAccessibleBridge
+{
+public:
+ virtual ~QAccessibleBridge() {}
+ virtual void setRootObject(QAccessibleInterface *) = 0;
+ virtual void notifyAccessibilityUpdate(int, QAccessibleInterface*, int) = 0;
+};
+
+struct Q_GUI_EXPORT QAccessibleBridgeFactoryInterface : public QFactoryInterface
+{
+ virtual QAccessibleBridge *create(const QString& name) = 0;
+};
+
+#define QAccessibleBridgeFactoryInterface_iid "com.trolltech.Qt.QAccessibleBridgeFactoryInterface"
+Q_DECLARE_INTERFACE(QAccessibleBridgeFactoryInterface, QAccessibleBridgeFactoryInterface_iid)
+
+class Q_GUI_EXPORT QAccessibleBridgePlugin : public QObject, public QAccessibleBridgeFactoryInterface
+{
+ Q_OBJECT
+ Q_INTERFACES(QAccessibleBridgeFactoryInterface:QFactoryInterface)
+public:
+ explicit QAccessibleBridgePlugin(QObject *parent = 0);
+ ~QAccessibleBridgePlugin();
+
+ virtual QStringList keys() const = 0;
+ virtual QAccessibleBridge *create(const QString &key) = 0;
+};
+
+#endif // QT_NO_ACCESSIBILITY
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QACCESSIBLEBRIDGE_H
diff --git a/src/gui/accessible/qaccessibleobject.cpp b/src/gui/accessible/qaccessibleobject.cpp
new file mode 100644
index 0000000000..7df097b918
--- /dev/null
+++ b/src/gui/accessible/qaccessibleobject.cpp
@@ -0,0 +1,410 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qaccessibleobject.h"
+
+#ifndef QT_NO_ACCESSIBILITY
+
+#include "qapplication.h"
+#include "qwidget.h"
+#include "qpointer.h"
+#include "qmetaobject.h"
+#include "qvarlengtharray.h"
+
+QT_BEGIN_NAMESPACE
+
+class QAccessibleObjectPrivate
+{
+public:
+ QPointer<QObject> object;
+
+ QList<QByteArray> actionList() const;
+};
+
+QList<QByteArray> QAccessibleObjectPrivate::actionList() const
+{
+ QList<QByteArray> actionList;
+
+ if (!object)
+ return actionList;
+
+ const QMetaObject *mo = object->metaObject();
+ Q_ASSERT(mo);
+
+ QByteArray defaultAction = QMetaObject::normalizedSignature(
+ mo->classInfo(mo->indexOfClassInfo("DefaultSlot")).value());
+
+ for (int i = 0; i < mo->methodCount(); ++i) {
+ const QMetaMethod member = mo->method(i);
+ if (member.methodType() != QMetaMethod::Slot && member.access() != QMetaMethod::Public)
+ continue;
+
+ if (!qstrcmp(member.tag(), "QACCESSIBLE_SLOT")) {
+ if (member.signature() == defaultAction)
+ actionList.prepend(defaultAction);
+ else
+ actionList << member.signature();
+ }
+ }
+
+ return actionList;
+}
+
+/*!
+ \class QAccessibleObject
+ \brief The QAccessibleObject class implements parts of the
+ QAccessibleInterface for QObjects.
+
+ \ingroup accessibility
+
+ This class is mainly provided for convenience. All subclasses of
+ the QAccessibleInterface that provide implementations of non-widget objects
+ should use this class as their base class.
+
+ \sa QAccessible, QAccessibleWidget
+*/
+
+/*!
+ Creates a QAccessibleObject for \a object.
+*/
+QAccessibleObject::QAccessibleObject(QObject *object)
+{
+ d = new QAccessibleObjectPrivate;
+ d->object = object;
+}
+
+/*!
+ Destroys the QAccessibleObject.
+
+ This only happens when a call to release() decrements the internal
+ reference counter to zero.
+*/
+QAccessibleObject::~QAccessibleObject()
+{
+ delete d;
+}
+
+/*!
+ \reimp
+*/
+QObject *QAccessibleObject::object() const
+{
+#ifndef QT_NO_DEBUG
+ if (!d->object)
+ qWarning("QAccessibleInterface is invalid. Crash pending...");
+#endif
+ return d->object;
+}
+
+/*!
+ \reimp
+*/
+bool QAccessibleObject::isValid() const
+{
+ return !d->object.isNull();
+}
+
+/*! \reimp */
+QRect QAccessibleObject::rect(int) const
+{
+ return QRect();
+}
+
+/*! \reimp */
+void QAccessibleObject::setText(Text, int, const QString &)
+{
+}
+
+/*! \reimp */
+int QAccessibleObject::userActionCount(int) const
+{
+ return 0;
+}
+
+/*! \reimp */
+bool QAccessibleObject::doAction(int, int, const QVariantList &)
+{
+ return false;
+}
+
+static const char * const action_text[][5] =
+{
+ // Name, Description, Value, Help, Accelerator
+ { "Press", "", "", "", "Space" },
+ { "SetFocus", "Passes focus to this widget", "", "", "" },
+ { "Increase", "", "", "", "" },
+ { "Decrease", "", "", "", "" },
+ { "Accept", "", "", "", "" },
+ { "Cancel", "", "", "", "" },
+ { "Select", "", "", "", "" },
+ { "ClearSelection", "", "", "", "" },
+ { "RemoveSelection", "", "", "", "" },
+ { "ExtendSelection", "", "", "", "" },
+ { "AddToSelection", "", "", "", "" }
+};
+
+/*! \reimp */
+QString QAccessibleObject::actionText(int action, Text t, int child) const
+{
+ if (child || action > FirstStandardAction || action < LastStandardAction || t > Accelerator)
+ return QString();
+
+ return QString::fromLatin1(action_text[-(action - FirstStandardAction)][t]);
+}
+
+
+/*!
+ \class QAccessibleApplication
+ \brief The QAccessibleApplication class implements the QAccessibleInterface for QApplication.
+
+ \internal
+
+ \ingroup accessibility
+*/
+
+/*!
+ Creates a QAccessibleApplication for the QApplication object referenced by qApp.
+*/
+QAccessibleApplication::QAccessibleApplication()
+: QAccessibleObject(qApp)
+{
+}
+
+// all toplevel widgets except popups and the desktop
+static QWidgetList topLevelWidgets()
+{
+ QWidgetList list;
+ const QWidgetList tlw(qApp->topLevelWidgets());
+ for (int i = 0; i < tlw.count(); ++i) {
+ QWidget *w = tlw.at(i);
+ if (!(w->windowType() == Qt::Popup) && !(w->windowType() == Qt::Desktop))
+ list.append(w);
+ }
+
+ return list;
+}
+
+/*! \reimp */
+int QAccessibleApplication::childCount() const
+{
+ return topLevelWidgets().count();
+}
+
+/*! \reimp */
+int QAccessibleApplication::indexOfChild(const QAccessibleInterface *child) const
+{
+ if (!child->object()->isWidgetType())
+ return -1;
+
+ const QWidgetList tlw(topLevelWidgets());
+ int index = tlw.indexOf(static_cast<QWidget*>(child->object()));
+ if (index != -1)
+ ++index;
+ return index;
+}
+
+/*! \reimp */
+int QAccessibleApplication::childAt(int x, int y) const
+{
+ const QWidgetList tlw(topLevelWidgets());
+ for (int i = 0; i < tlw.count(); ++i) {
+ QWidget *w = tlw.at(i);
+ if (w->frameGeometry().contains(x,y))
+ return i+1;
+ }
+ return -1;
+}
+
+/*! \reimp */
+QAccessible::Relation QAccessibleApplication::relationTo(int child, const
+ QAccessibleInterface *other, int otherChild) const
+{
+ QObject *o = other ? other->object() : 0;
+ if (!o)
+ return Unrelated;
+
+ if(o == object()) {
+ if (child && !otherChild)
+ return Child;
+ if (!child && otherChild)
+ return Ancestor;
+ if (!child && !otherChild)
+ return Self;
+ }
+
+ QWidgetList tlw(topLevelWidgets());
+ if (tlw.contains(qobject_cast<QWidget*>(o)))
+ return Ancestor;
+
+ for (int i = 0; i < tlw.count(); ++i) {
+ QWidget *w = tlw.at(i);
+ QObjectList cl = qFindChildren<QObject *>(w, QString());
+ if (cl.contains(o))
+ return Ancestor;
+ }
+
+ return Unrelated;
+}
+
+/*! \reimp */
+int QAccessibleApplication::navigate(RelationFlag relation, int entry,
+ QAccessibleInterface **target) const
+{
+ if (!target)
+ return -1;
+
+ *target = 0;
+ QObject *targetObject = 0;
+
+ switch (relation) {
+ case Self:
+ targetObject = object();
+ break;
+ case Child:
+ if (entry > 0 && entry <= childCount()) {
+ const QWidgetList tlw(topLevelWidgets());
+ if (tlw.count() >= entry)
+ targetObject = tlw.at(entry-1);
+ } else {
+ return -1;
+ }
+ break;
+ case FocusChild:
+ targetObject = qApp->activeWindow();
+ break;
+ default:
+ break;
+ }
+ *target = QAccessible::queryAccessibleInterface(targetObject);
+ return *target ? 0 : -1;
+}
+
+/*! \reimp */
+QString QAccessibleApplication::text(Text t, int) const
+{
+ switch (t) {
+ case Name:
+ if (qApp->activeWindow())
+ return qApp->activeWindow()->windowTitle();
+ break;
+ case Description:
+ return qApp->applicationFilePath();
+ default:
+ break;
+ }
+ return QString();
+}
+
+/*! \reimp */
+QAccessible::Role QAccessibleApplication::role(int) const
+{
+ return Application;
+}
+
+/*! \reimp */
+QAccessible::State QAccessibleApplication::state(int) const
+{
+ return qApp->activeWindow() ? Focused : Normal;
+}
+
+/*! \reimp */
+int QAccessibleApplication::userActionCount(int) const
+{
+ return 1;
+}
+
+/*! \reimp */
+bool QAccessibleApplication::doAction(int action, int child, const QVariantList &param)
+{
+ if (action == 0 || action == 1) {
+ QWidget *w = 0;
+ w = qApp->activeWindow();
+ if (!w)
+ w = topLevelWidgets().at(0);
+ if (!w)
+ return false;
+ w->activateWindow();
+ return true;
+ }
+ return QAccessibleObject::doAction(action, child, param);
+}
+
+/*! \reimp */
+QString QAccessibleApplication::actionText(int action, Text text, int child) const
+{
+ QString str;
+ if ((action == 0 || action == 1) && !child) switch (text) {
+ case Name:
+ return QApplication::tr("Activate");
+ case Description:
+ return QApplication::tr("Activates the program's main window");
+ default:
+ break;
+ }
+ return QAccessibleObject::actionText(action, text, child);
+}
+
+// ### Qt 5: remove me - binary compatibility hack
+QAccessibleObjectEx::QAccessibleObjectEx(QObject *object)
+{
+ d = new QAccessibleObjectPrivate;
+ d->object = object;
+}
+bool QAccessibleObjectEx::isValid() const
+{ return reinterpret_cast<const QAccessibleObject *>(this)->QAccessibleObject::isValid(); }
+QObject *QAccessibleObjectEx::object() const
+{ return reinterpret_cast<const QAccessibleObject *>(this)->QAccessibleObject::object(); }
+QRect QAccessibleObjectEx::rect(int child) const
+{ return reinterpret_cast<const QAccessibleObject *>(this)->QAccessibleObject::rect(child); }
+void QAccessibleObjectEx::setText(Text t, int child, const QString &text)
+{ reinterpret_cast<QAccessibleObject *>(this)->QAccessibleObject::setText(t, child, text); }
+int QAccessibleObjectEx::userActionCount(int child) const
+{ return reinterpret_cast<const QAccessibleObject *>(this)->QAccessibleObject::userActionCount(child); }
+bool QAccessibleObjectEx::doAction(int action, int child, const QVariantList &params)
+{ return reinterpret_cast<QAccessibleObject *>(this)->QAccessibleObject::doAction(action, child, params); }
+QString QAccessibleObjectEx::actionText(int action, Text t, int child) const
+{ return reinterpret_cast<const QAccessibleObject *>(this)->QAccessibleObject::actionText(action, t, child); }
+QAccessibleObjectEx::~QAccessibleObjectEx()
+{ delete d; }
+
+QT_END_NAMESPACE
+
+#endif //QT_NO_ACCESSIBILITY
diff --git a/src/gui/accessible/qaccessibleobject.h b/src/gui/accessible/qaccessibleobject.h
new file mode 100644
index 0000000000..aef7ce7b34
--- /dev/null
+++ b/src/gui/accessible/qaccessibleobject.h
@@ -0,0 +1,140 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QACCESSIBLEOBJECT_H
+#define QACCESSIBLEOBJECT_H
+
+#include <QtGui/qaccessible.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+#ifndef QT_NO_ACCESSIBILITY
+
+class QAccessibleObjectPrivate;
+class QObject;
+
+class Q_GUI_EXPORT QAccessibleObject : public QAccessibleInterface
+{
+public:
+ explicit QAccessibleObject(QObject *object);
+
+ bool isValid() const;
+ QObject *object() const;
+
+ // properties
+ QRect rect(int child) const;
+ void setText(Text t, int child, const QString &text);
+
+ // actions
+ int userActionCount(int child) const;
+ bool doAction(int action, int child, const QVariantList &params);
+ QString actionText(int action, Text t, int child) const;
+
+protected:
+ virtual ~QAccessibleObject();
+
+private:
+ friend class QAccessibleObjectEx;
+ QAccessibleObjectPrivate *d;
+ Q_DISABLE_COPY(QAccessibleObject)
+};
+
+class Q_GUI_EXPORT QAccessibleObjectEx : public QAccessibleInterfaceEx
+{
+public:
+ explicit QAccessibleObjectEx(QObject *object);
+
+ bool isValid() const;
+ QObject *object() const;
+
+ // properties
+ QRect rect(int child) const;
+ void setText(Text t, int child, const QString &text);
+
+ // actions
+ int userActionCount(int child) const;
+ bool doAction(int action, int child, const QVariantList &params);
+ QString actionText(int action, Text t, int child) const;
+
+protected:
+ virtual ~QAccessibleObjectEx();
+
+private:
+ QAccessibleObjectPrivate *d;
+ Q_DISABLE_COPY(QAccessibleObjectEx)
+};
+
+class Q_GUI_EXPORT QAccessibleApplication : public QAccessibleObject
+{
+public:
+ QAccessibleApplication();
+
+ // relations
+ int childCount() const;
+ int indexOfChild(const QAccessibleInterface*) const;
+ Relation relationTo(int, const QAccessibleInterface *, int) const;
+
+ // navigation
+ int childAt(int x, int y) const;
+ int navigate(RelationFlag, int, QAccessibleInterface **) const;
+
+ // properties and state
+ QString text(Text t, int child) const;
+ Role role(int child) const;
+ State state(int child) const;
+
+ // actions
+ int userActionCount(int child) const;
+ bool doAction(int action, int child, const QVariantList &params);
+ QString actionText(int action, Text t, int child) const;
+};
+
+#endif // QT_NO_ACCESSIBILITY
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QACCESSIBLEOBJECT_H
diff --git a/src/gui/accessible/qaccessibleplugin.cpp b/src/gui/accessible/qaccessibleplugin.cpp
new file mode 100644
index 0000000000..00fe2b176a
--- /dev/null
+++ b/src/gui/accessible/qaccessibleplugin.cpp
@@ -0,0 +1,107 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qaccessibleplugin.h"
+
+#ifndef QT_NO_ACCESSIBILITY
+
+#include "qaccessible.h"
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QAccessiblePlugin
+ \brief The QAccessiblePlugin class provides an abstract base for
+ accessibility plugins.
+
+ \ingroup plugins
+ \ingroup accessibility
+
+ Writing an accessibility plugin is achieved by subclassing this
+ base class, reimplementing the pure virtual functions keys() and
+ create(), and exporting the class with the Q_EXPORT_PLUGIN2()
+ macro.
+
+ \sa QAccessibleBridgePlugin, {How to Create Qt Plugins}
+*/
+
+/*!
+ Constructs an accessibility plugin with the given \a parent. This
+ is invoked automatically by the Q_EXPORT_PLUGIN2() macro.
+*/
+QAccessiblePlugin::QAccessiblePlugin(QObject *parent)
+ : QObject(parent)
+{
+}
+
+/*!
+ Destroys the accessibility plugin.
+
+ You never have to call this explicitly. Qt destroys a plugin
+ automatically when it is no longer used.
+*/
+QAccessiblePlugin::~QAccessiblePlugin()
+{
+}
+
+/*!
+ \fn QStringList QAccessiblePlugin::keys() const
+
+ Returns the list of keys this plugin supports.
+
+ These keys must be the class names that this plugin provides
+ an accessibility implementation for.
+
+ \sa create()
+*/
+
+/*!
+ \fn QAccessibleInterface *QAccessiblePlugin::create(const QString &key, QObject *object)
+
+ Creates and returns a QAccessibleInterface implementation for the
+ class \a key and the object \a object. Keys are case sensitive.
+
+ \sa keys()
+*/
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_ACCESSIBILITY
diff --git a/src/gui/accessible/qaccessibleplugin.h b/src/gui/accessible/qaccessibleplugin.h
new file mode 100644
index 0000000000..a566921e28
--- /dev/null
+++ b/src/gui/accessible/qaccessibleplugin.h
@@ -0,0 +1,87 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QACCESSIBLEPLUGIN_H
+#define QACCESSIBLEPLUGIN_H
+
+#include <QtGui/qaccessible.h>
+#include <QtCore/qfactoryinterface.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+#ifndef QT_NO_ACCESSIBILITY
+
+class QStringList;
+class QAccessibleInterface;
+
+struct Q_GUI_EXPORT QAccessibleFactoryInterface : public QAccessible, public QFactoryInterface
+{
+ virtual QAccessibleInterface* create(const QString &key, QObject *object) = 0;
+};
+
+#define QAccessibleFactoryInterface_iid "com.trolltech.Qt.QAccessibleFactoryInterface"
+Q_DECLARE_INTERFACE(QAccessibleFactoryInterface, QAccessibleFactoryInterface_iid)
+
+class QAccessiblePluginPrivate;
+
+class Q_GUI_EXPORT QAccessiblePlugin : public QObject, public QAccessibleFactoryInterface
+{
+ Q_OBJECT
+ Q_INTERFACES(QAccessibleFactoryInterface:QFactoryInterface)
+public:
+ explicit QAccessiblePlugin(QObject *parent = 0);
+ ~QAccessiblePlugin();
+
+ virtual QStringList keys() const = 0;
+ virtual QAccessibleInterface *create(const QString &key, QObject *object) = 0;
+};
+
+#endif // QT_NO_ACCESSIBILITY
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QACCESSIBLEPLUGIN_H
diff --git a/src/gui/accessible/qaccessiblewidget.cpp b/src/gui/accessible/qaccessiblewidget.cpp
new file mode 100644
index 0000000000..4b2b2abe76
--- /dev/null
+++ b/src/gui/accessible/qaccessiblewidget.cpp
@@ -0,0 +1,1041 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qaccessiblewidget.h"
+
+#ifndef QT_NO_ACCESSIBILITY
+
+#include "qaction.h"
+#include "qapplication.h"
+#include "qgroupbox.h"
+#include "qlabel.h"
+#include "qtooltip.h"
+#include "qwhatsthis.h"
+#include "qwidget.h"
+#include "qdebug.h"
+#include <qmath.h>
+#include <QRubberBand>
+#include <QtGui/QFocusFrame>
+#include <QtGui/QMenu>
+
+QT_BEGIN_NAMESPACE
+
+static QList<QWidget*> childWidgets(const QWidget *widget)
+{
+ QList<QObject*> list = widget->children();
+ QList<QWidget*> widgets;
+ for (int i = 0; i < list.size(); ++i) {
+ QWidget *w = qobject_cast<QWidget *>(list.at(i));
+ if (w && !w->isWindow()
+ && !qobject_cast<QFocusFrame*>(w)
+#if !defined(QT_NO_MENU)
+ && !qobject_cast<QMenu*>(w)
+#endif
+ && w->objectName() != QLatin1String("qt_rubberband"))
+ widgets.append(w);
+ }
+ return widgets;
+}
+
+static QString buddyString(const QWidget *widget)
+{
+ if (!widget)
+ return QString();
+ QWidget *parent = widget->parentWidget();
+ if (!parent)
+ return QString();
+#ifndef QT_NO_SHORTCUT
+ QObjectList ol = parent->children();
+ for (int i = 0; i < ol.size(); ++i) {
+ QLabel *label = qobject_cast<QLabel*>(ol.at(i));
+ if (label && label->buddy() == widget)
+ return label->text();
+ }
+#endif
+
+#ifndef QT_NO_GROUPBOX
+ QGroupBox *groupbox = qobject_cast<QGroupBox*>(parent);
+ if (groupbox)
+ return groupbox->title();
+#endif
+
+ return QString();
+}
+
+QString Q_GUI_EXPORT qt_accStripAmp(const QString &text)
+{
+ if (text.isEmpty())
+ return text;
+
+ const QChar *ch = text.unicode();
+ int length = text.length();
+ QString str;
+ while (length > 0) {
+ if (*ch == QLatin1Char('&')) {
+ ++ch;
+ --length;
+ if (!ch)
+ --ch;
+ }
+ str += *ch;
+ ++ch;
+ --length;
+ }
+ return str;
+}
+
+QString Q_GUI_EXPORT qt_accHotKey(const QString &text)
+{
+#ifndef QT_NO_SHORTCUT
+ if (text.isEmpty())
+ return text;
+
+ int fa = 0;
+ QChar ac;
+ while ((fa = text.indexOf(QLatin1Char('&'), fa)) != -1) {
+ if (fa == text.length() - 1 || text.at(fa+1) != QLatin1Char('&')) {
+ ac = text.at(fa+1);
+ break;
+ }
+ }
+ if (ac.isNull())
+ return QString();
+ return (QString)QKeySequence(Qt::ALT) + ac.toUpper();
+#else
+ Q_UNUSED(text);
+ return QString();
+#endif
+}
+
+class QAccessibleWidgetPrivate : public QAccessible
+{
+public:
+ QAccessibleWidgetPrivate()
+ :role(Client)
+ {}
+
+ Role role;
+ QString name;
+ QString description;
+ QString value;
+ QString help;
+ QString accelerator;
+ QStringList primarySignals;
+ const QAccessibleInterface *asking;
+};
+
+/*!
+ \class QAccessibleWidget
+ \brief The QAccessibleWidget class implements the QAccessibleInterface for QWidgets.
+
+ \ingroup accessibility
+
+ This class is convenient to use as a base class for custom
+ implementations of QAccessibleInterfaces that provide information
+ about widget objects.
+
+ The class provides functions to retrieve the parentObject() (the
+ widget's parent widget), and the associated widget(). Controlling
+ signals can be added with addControllingSignal(), and setters are
+ provided for various aspects of the interface implementation, for
+ example setValue(), setDescription(), setAccelerator(), and
+ setHelp().
+
+ \sa QAccessible, QAccessibleObject
+*/
+
+/*!
+ Creates a QAccessibleWidget object for widget \a w.
+ \a role and \a name are optional parameters that set the object's
+ role and name properties.
+*/
+QAccessibleWidget::QAccessibleWidget(QWidget *w, Role role, const QString &name)
+: QAccessibleObject(w)
+{
+ Q_ASSERT(widget());
+ d = new QAccessibleWidgetPrivate();
+ d->role = role;
+ d->name = name;
+ d->asking = 0;
+}
+
+/*!
+ Destroys this object.
+*/
+QAccessibleWidget::~QAccessibleWidget()
+{
+ delete d;
+}
+
+/*!
+ Returns the associated widget.
+*/
+QWidget *QAccessibleWidget::widget() const
+{
+ return qobject_cast<QWidget*>(object());
+}
+
+/*!
+ Returns the associated widget's parent object, which is either the
+ parent widget, or qApp for top-level widgets.
+*/
+QObject *QAccessibleWidget::parentObject() const
+{
+ QObject *parent = object()->parent();
+ if (!parent)
+ parent = qApp;
+ return parent;
+}
+
+/*! \reimp */
+int QAccessibleWidget::childAt(int x, int y) const
+{
+ QWidget *w = widget();
+ if (!w->isVisible())
+ return -1;
+ QPoint gp = w->mapToGlobal(QPoint(0, 0));
+ if (!QRect(gp.x(), gp.y(), w->width(), w->height()).contains(x, y))
+ return -1;
+
+ QWidgetList list = childWidgets(w);
+ int ccount = childCount();
+
+ // a complex child
+ if (list.size() < ccount) {
+ for (int i = 1; i <= ccount; ++i) {
+ if (rect(i).contains(x, y))
+ return i;
+ }
+ return 0;
+ }
+
+ QPoint rp = w->mapFromGlobal(QPoint(x, y));
+ for (int i = 0; i<list.size(); ++i) {
+ QWidget *child = list.at(i);
+ if (!child->isWindow() && !child->isHidden() && child->geometry().contains(rp)) {
+ return i + 1;
+ }
+ }
+ return 0;
+}
+
+/*! \reimp */
+QRect QAccessibleWidget::rect(int child) const
+{
+ if (child) {
+ qWarning("QAccessibleWidget::rect: This implementation does not support subelements! "
+ "(ID %d unknown for %s)", child, widget()->metaObject()->className());
+ }
+
+ QWidget *w = widget();
+ if (!w->isVisible())
+ return QRect();
+ QPoint wpos = w->mapToGlobal(QPoint(0, 0));
+
+ return QRect(wpos.x(), wpos.y(), w->width(), w->height());
+}
+
+QT_BEGIN_INCLUDE_NAMESPACE
+#include <private/qobject_p.h>
+QT_END_INCLUDE_NAMESPACE
+
+class QACConnectionObject : public QObject
+{
+ Q_DECLARE_PRIVATE(QObject)
+public:
+ inline bool isSender(const QObject *receiver, const char *signal) const
+ { return d_func()->isSender(receiver, signal); }
+ inline QObjectList receiverList(const char *signal) const
+ { return d_func()->receiverList(signal); }
+ inline QObjectList senderList() const
+ { return d_func()->senderList(); }
+};
+
+/*!
+ Registers \a signal as a controlling signal.
+
+ An object is a Controller to any other object connected to a
+ controlling signal.
+*/
+void QAccessibleWidget::addControllingSignal(const QString &signal)
+{
+ QByteArray s = QMetaObject::normalizedSignature(signal.toAscii());
+ if (object()->metaObject()->indexOfSignal(s) < 0)
+ qWarning("Signal %s unknown in %s", s.constData(), object()->metaObject()->className());
+ d->primarySignals << QLatin1String(s);
+}
+
+/*!
+ Sets the value of this interface implementation to \a value.
+
+ The default implementation of text() returns the set value for
+ the Value text.
+
+ Note that the object wrapped by this interface is not modified.
+*/
+void QAccessibleWidget::setValue(const QString &value)
+{
+ d->value = value;
+}
+
+/*!
+ Sets the description of this interface implementation to \a desc.
+
+ The default implementation of text() returns the set value for
+ the Description text.
+
+ Note that the object wrapped by this interface is not modified.
+*/
+void QAccessibleWidget::setDescription(const QString &desc)
+{
+ d->description = desc;
+}
+
+/*!
+ Sets the help of this interface implementation to \a help.
+
+ The default implementation of text() returns the set value for
+ the Help text.
+
+ Note that the object wrapped by this interface is not modified.
+*/
+void QAccessibleWidget::setHelp(const QString &help)
+{
+ d->help = help;
+}
+
+/*!
+ Sets the accelerator of this interface implementation to \a accel.
+
+ The default implementation of text() returns the set value for
+ the Accelerator text.
+
+ Note that the object wrapped by this interface is not modified.
+*/
+void QAccessibleWidget::setAccelerator(const QString &accel)
+{
+ d->accelerator = accel;
+}
+
+static inline bool isAncestor(const QObject *obj, const QObject *child)
+{
+ while (child) {
+ if (child == obj)
+ return true;
+ child = child->parent();
+ }
+ return false;
+}
+
+
+/*! \reimp */
+QAccessible::Relation QAccessibleWidget::relationTo(int child,
+ const QAccessibleInterface *other, int otherChild) const
+{
+ Relation relation = Unrelated;
+ if (d->asking == this) // recursive call
+ return relation;
+
+ QObject *o = other ? other->object() : 0;
+ if (!o)
+ return relation;
+
+ QWidget *focus = widget()->focusWidget();
+ if (object() == focus && isAncestor(o, focus))
+ relation |= FocusChild;
+
+ QACConnectionObject *connectionObject = (QACConnectionObject*)object();
+ for (int sig = 0; sig < d->primarySignals.count(); ++sig) {
+ if (connectionObject->isSender(o, d->primarySignals.at(sig).toAscii())) {
+ relation |= Controller;
+ break;
+ }
+ }
+ // test for passive relationships.
+ // d->asking protects from endless recursion.
+ d->asking = this;
+ int inverse = other->relationTo(otherChild, this, child);
+ d->asking = 0;
+
+ if (inverse & Controller)
+ relation |= Controlled;
+ if (inverse & Label)
+ relation |= Labelled;
+
+ if(o == object()) {
+ if (child && !otherChild)
+ return relation | Child;
+ if (!child && otherChild)
+ return relation | Ancestor;
+ if (!child && !otherChild)
+ return relation | Self;
+ }
+
+ QObject *parent = object()->parent();
+ if (o == parent)
+ return relation | Child;
+
+ if (o->parent() == parent) {
+ relation |= Sibling;
+ QAccessibleInterface *sibIface = QAccessible::queryAccessibleInterface(o);
+ Q_ASSERT(sibIface);
+ QRect wg = rect(0);
+ QRect sg = sibIface->rect(0);
+ if (wg.intersects(sg)) {
+ QAccessibleInterface *pIface = 0;
+ sibIface->navigate(Ancestor, 1, &pIface);
+ if (pIface && !((sibIface->state(0) | state(0)) & Invisible)) {
+ int wi = pIface->indexOfChild(this);
+ int si = pIface->indexOfChild(sibIface);
+
+ if (wi > si)
+ relation |= QAccessible::Covers;
+ else
+ relation |= QAccessible::Covered;
+ }
+ delete pIface;
+ } else {
+ QPoint wc = wg.center();
+ QPoint sc = sg.center();
+ if (wc.x() < sc.x())
+ relation |= QAccessible::Left;
+ else if(wc.x() > sc.x())
+ relation |= QAccessible::Right;
+ if (wc.y() < sc.y())
+ relation |= QAccessible::Up;
+ else if (wc.y() > sc.y())
+ relation |= QAccessible::Down;
+ }
+ delete sibIface;
+
+ return relation;
+ }
+
+ if (isAncestor(o, object()))
+ return relation | Descendent;
+ if (isAncestor(object(), o))
+ return relation | Ancestor;
+
+ return relation;
+}
+
+/*! \reimp */
+int QAccessibleWidget::navigate(RelationFlag relation, int entry,
+ QAccessibleInterface **target) const
+{
+ if (!target)
+ return -1;
+
+ *target = 0;
+ QObject *targetObject = 0;
+
+ QWidgetList childList = childWidgets(widget());
+ bool complexWidget = childList.size() < childCount();
+
+ switch (relation) {
+ // Hierarchical
+ case Self:
+ targetObject = object();
+ break;
+ case Child:
+ if (complexWidget) {
+ if (entry > 0 && entry <= childCount())
+ return entry;
+ return -1;
+ }else {
+ if (entry > 0 && childList.size() >= entry)
+ targetObject = childList.at(entry - 1);
+ }
+ break;
+ case Ancestor:
+ {
+ if (entry <= 0)
+ return -1;
+ targetObject = widget()->parentWidget();
+ int i;
+ for (i = entry; i > 1 && targetObject; --i)
+ targetObject = targetObject->parent();
+ if (!targetObject && i == 1)
+ targetObject = qApp;
+ }
+ break;
+ case Sibling:
+ {
+ QAccessibleInterface *iface = QAccessible::queryAccessibleInterface(parentObject());
+ if (!iface)
+ return -1;
+
+ iface->navigate(Child, entry, target);
+ delete iface;
+ if (*target)
+ return 0;
+ }
+ break;
+
+ // Geometrical
+ case QAccessible::Left:
+ if (complexWidget && entry) {
+ if (entry < 2 || widget()->height() > widget()->width() + 20) // looks vertical
+ return -1;
+ return entry - 1;
+ }
+ // fall through
+ case QAccessible::Right:
+ if (complexWidget && entry) {
+ if (entry >= childCount() || widget()->height() > widget()->width() + 20) // looks vertical
+ return -1;
+ return entry + 1;
+ }
+ // fall through
+ case QAccessible::Up:
+ if (complexWidget && entry) {
+ if (entry < 2 || widget()->width() > widget()->height() + 20) // looks horizontal
+ return - 1;
+ return entry - 1;
+ }
+ // fall through
+ case QAccessible::Down:
+ if (complexWidget && entry) {
+ if (entry >= childCount() || widget()->width() > widget()->height() + 20) // looks horizontal
+ return - 1;
+ return entry + 1;
+ } else {
+ QAccessibleInterface *pIface = QAccessible::queryAccessibleInterface(parentObject());
+ if (!pIface)
+ return -1;
+
+ QRect startg = rect(0);
+ QPoint startc = startg.center();
+ QAccessibleInterface *candidate = 0;
+ int mindist = 100000;
+ int sibCount = pIface->childCount();
+ for (int i = 0; i < sibCount; ++i) {
+ QAccessibleInterface *sibling = 0;
+ pIface->navigate(Child, i+1, &sibling);
+ Q_ASSERT(sibling);
+ if ((relationTo(0, sibling, 0) & Self) || (sibling->state(0) & QAccessible::Invisible)) {
+ //ignore ourself and invisible siblings
+ delete sibling;
+ continue;
+ }
+
+ QRect sibg = sibling->rect(0);
+ QPoint sibc = sibg.center();
+ QPoint sibp;
+ QPoint startp;
+ QPoint distp;
+ switch (relation) {
+ case QAccessible::Left:
+ startp = QPoint(startg.left(), startg.top() + startg.height() / 2);
+ sibp = QPoint(sibg.right(), sibg.top() + sibg.height() / 2);
+ if (QPoint(sibc - startc).x() >= 0) {
+ delete sibling;
+ continue;
+ }
+ distp = sibp - startp;
+ break;
+ case QAccessible::Right:
+ startp = QPoint(startg.right(), startg.top() + startg.height() / 2);
+ sibp = QPoint(sibg.left(), sibg.top() + sibg.height() / 2);
+ if (QPoint(sibc - startc).x() <= 0) {
+ delete sibling;
+ continue;
+ }
+ distp = sibp - startp;
+ break;
+ case QAccessible::Up:
+ startp = QPoint(startg.left() + startg.width() / 2, startg.top());
+ sibp = QPoint(sibg.left() + sibg.width() / 2, sibg.bottom());
+ if (QPoint(sibc - startc).y() >= 0) {
+ delete sibling;
+ continue;
+ }
+ distp = sibp - startp;
+ break;
+ case QAccessible::Down:
+ startp = QPoint(startg.left() + startg.width() / 2, startg.bottom());
+ sibp = QPoint(sibg.left() + sibg.width() / 2, sibg.top());
+ if (QPoint(sibc - startc).y() <= 0) {
+ delete sibling;
+ continue;
+ }
+ distp = sibp - startp;
+ break;
+ default:
+ break;
+ }
+
+ int dist = (int)qSqrt((qreal)distp.x() * distp.x() + distp.y() * distp.y());
+ if (dist < mindist) {
+ delete candidate;
+ candidate = sibling;
+ mindist = dist;
+ } else {
+ delete sibling;
+ }
+ }
+ delete pIface;
+ *target = candidate;
+ if (*target)
+ return 0;
+ }
+ break;
+ case Covers:
+ if (entry > 0) {
+ QAccessibleInterface *pIface = QAccessible::queryAccessibleInterface(parentObject());
+ if (!pIface)
+ return -1;
+
+ QRect r = rect(0);
+ int sibCount = pIface->childCount();
+ QAccessibleInterface *sibling = 0;
+ for (int i = pIface->indexOfChild(this) + 1; i <= sibCount && entry; ++i) {
+ pIface->navigate(Child, i, &sibling);
+ if (!sibling || (sibling->state(0) & Invisible)) {
+ delete sibling;
+ sibling = 0;
+ continue;
+ }
+ if (sibling->rect(0).intersects(r))
+ --entry;
+ if (!entry)
+ break;
+ delete sibling;
+ sibling = 0;
+ }
+ delete pIface;
+ *target = sibling;
+ if (*target)
+ return 0;
+ }
+ break;
+ case Covered:
+ if (entry > 0) {
+ QAccessibleInterface *pIface = QAccessible::queryAccessibleInterface(parentObject());
+ if (!pIface)
+ return -1;
+
+ QRect r = rect(0);
+ int index = pIface->indexOfChild(this);
+ QAccessibleInterface *sibling = 0;
+ for (int i = 1; i < index && entry; ++i) {
+ pIface->navigate(Child, i, &sibling);
+ Q_ASSERT(sibling);
+ if (!sibling || (sibling->state(0) & Invisible)) {
+ delete sibling;
+ sibling = 0;
+ continue;
+ }
+ if (sibling->rect(0).intersects(r))
+ --entry;
+ if (!entry)
+ break;
+ delete sibling;
+ sibling = 0;
+ }
+ delete pIface;
+ *target = sibling;
+ if (*target)
+ return 0;
+ }
+ break;
+
+ // Logical
+ case FocusChild:
+ {
+ if (widget()->hasFocus()) {
+ targetObject = object();
+ break;
+ }
+
+ QWidget *fw = widget()->focusWidget();
+ if (!fw)
+ return -1;
+
+ if (isAncestor(widget(), fw) || fw == widget())
+ targetObject = fw;
+ /* ###
+ QWidget *parent = fw;
+ while (parent && !targetObject) {
+ parent = parent->parentWidget();
+ if (parent == widget())
+ targetObject = fw;
+ }
+ */
+ }
+ break;
+ case Label:
+ if (entry > 0) {
+ QAccessibleInterface *pIface = QAccessible::queryAccessibleInterface(parentObject());
+ if (!pIface)
+ return -1;
+
+ // first check for all siblings that are labels to us
+ // ideally we would go through all objects and check, but that
+ // will be too expensive
+ int sibCount = pIface->childCount();
+ QAccessibleInterface *candidate = 0;
+ for (int i = 0; i < sibCount && entry; ++i) {
+ pIface->navigate(Child, i+1, &candidate);
+ Q_ASSERT(candidate);
+ if (candidate->relationTo(0, this, 0) & Label)
+ --entry;
+ if (!entry)
+ break;
+ delete candidate;
+ candidate = 0;
+ }
+ if (!candidate) {
+ if (pIface->relationTo(0, this, 0) & Label)
+ --entry;
+ if (!entry)
+ candidate = pIface;
+ }
+ if (pIface != candidate)
+ delete pIface;
+
+ *target = candidate;
+ if (*target)
+ return 0;
+ }
+ break;
+ case Labelled: // only implemented in subclasses
+ break;
+ case Controller:
+ if (entry > 0) {
+ // check all senders we are connected to,
+ // and figure out which one are controllers to us
+ QACConnectionObject *connectionObject = (QACConnectionObject*)object();
+ QObjectList allSenders = connectionObject->senderList();
+ QObjectList senders;
+ for (int s = 0; s < allSenders.size(); ++s) {
+ QObject *sender = allSenders.at(s);
+ QAccessibleInterface *candidate = QAccessible::queryAccessibleInterface(sender);
+ if (!candidate)
+ continue;
+ if (candidate->relationTo(0, this, 0)&Controller)
+ senders << sender;
+ delete candidate;
+ }
+ if (entry <= senders.size())
+ targetObject = senders.at(entry-1);
+ }
+ break;
+ case Controlled:
+ if (entry > 0) {
+ QObjectList allReceivers;
+ QACConnectionObject *connectionObject = (QACConnectionObject*)object();
+ for (int sig = 0; sig < d->primarySignals.count(); ++sig) {
+ QObjectList receivers = connectionObject->receiverList(d->primarySignals.at(sig).toAscii());
+ allReceivers += receivers;
+ }
+ if (entry <= allReceivers.size())
+ targetObject = allReceivers.at(entry-1);
+ }
+ break;
+ default:
+ break;
+ }
+
+ *target = QAccessible::queryAccessibleInterface(targetObject);
+ return *target ? 0 : -1;
+}
+
+/*! \reimp */
+int QAccessibleWidget::childCount() const
+{
+ QWidgetList cl = childWidgets(widget());
+ return cl.size();
+}
+
+/*! \reimp */
+int QAccessibleWidget::indexOfChild(const QAccessibleInterface *child) const
+{
+ QWidgetList cl = childWidgets(widget());
+ int index = cl.indexOf(qobject_cast<QWidget *>(child->object()));
+ if (index != -1)
+ ++index;
+ return index;
+}
+
+// from qwidget.cpp
+extern QString qt_setWindowTitle_helperHelper(const QString &, const QWidget*);
+
+/*! \reimp */
+QString QAccessibleWidget::text(Text t, int child) const
+{
+ QString str;
+
+ switch (t) {
+ case Name:
+ if (!d->name.isEmpty()) {
+ str = d->name;
+ } else if (!widget()->accessibleName().isEmpty()) {
+ str = widget()->accessibleName();
+ } else if (!child && widget()->isWindow()) {
+ if (widget()->isMinimized())
+ str = qt_setWindowTitle_helperHelper(widget()->windowIconText(), widget());
+ else
+ str = qt_setWindowTitle_helperHelper(widget()->windowTitle(), widget());
+ } else {
+ str = qt_accStripAmp(buddyString(widget()));
+ }
+ break;
+ case Description:
+ if (!d->description.isEmpty())
+ str = d->description;
+ else if (!widget()->accessibleDescription().isEmpty())
+ str = widget()->accessibleDescription();
+#ifndef QT_NO_TOOLTIP
+ else
+ str = widget()->toolTip();
+#endif
+ break;
+ case Help:
+ if (!d->help.isEmpty())
+ str = d->help;
+#ifndef QT_NO_WHATSTHIS
+ else
+ str = widget()->whatsThis();
+#endif
+ break;
+ case Accelerator:
+ if (!d->accelerator.isEmpty())
+ str = d->accelerator;
+ else
+ str = qt_accHotKey(buddyString(widget()));
+ break;
+ case Value:
+ str = d->value;
+ break;
+ default:
+ break;
+ }
+ return str;
+}
+
+#ifndef QT_NO_ACTION
+
+/*! \reimp */
+int QAccessibleWidget::userActionCount(int child) const
+{
+ if (child)
+ return 0;
+ return widget()->actions().count();
+}
+
+/*! \reimp */
+QString QAccessibleWidget::actionText(int action, Text t, int child) const
+{
+ if (action == DefaultAction)
+ action = SetFocus;
+
+ if (action > 0 && !child) {
+ QAction *act = widget()->actions().value(action - 1);
+ if (act) {
+ switch (t) {
+ case Name:
+ return act->text();
+ case Description:
+ return act->toolTip();
+#ifndef QT_NO_SHORTCUT
+ case Accelerator:
+ return act->shortcut().toString();
+#endif
+ default:
+ break;
+ }
+ }
+ }
+
+ return QAccessibleObject::actionText(action, t, child);
+}
+
+/*! \reimp */
+bool QAccessibleWidget::doAction(int action, int child, const QVariantList &params)
+{
+ if (action == SetFocus || action == DefaultAction) {
+ if (child || !widget()->isEnabled())
+ return false;
+ if (widget()->focusPolicy() != Qt::NoFocus)
+ widget()->setFocus();
+ else if (widget()->isWindow())
+ widget()->activateWindow();
+ else
+ return false;
+ return true;
+ } else if (action > 0) {
+ if (QAction *act = widget()->actions().value(action - 1)) {
+ act->trigger();
+ return true;
+ }
+ }
+ return QAccessibleObject::doAction(action, child, params);
+}
+
+#endif // QT_NO_ACTION
+
+/*! \reimp */
+QAccessible::Role QAccessibleWidget::role(int child) const
+{
+ if (!child)
+ return d->role;
+
+ QWidgetList childList = childWidgets(widget());
+ if (childList.count() > 0 && child <= childList.count()) {
+ QWidget *targetWidget = childList.at(child - 1);
+ QAccessibleInterface *iface = QAccessible::queryAccessibleInterface(targetWidget);
+ if (iface) {
+ QAccessible::Role role = iface->role(0);
+ delete iface;
+ return role;
+ }
+ }
+
+ return NoRole;
+}
+
+/*! \reimp */
+QAccessible::State QAccessibleWidget::state(int child) const
+{
+ if (child)
+ return Normal;
+
+ QAccessible::State state = Normal;
+
+ QWidget *w = widget();
+ if (w->testAttribute(Qt::WA_WState_Visible) == false)
+ state |= Invisible;
+ if (w->focusPolicy() != Qt::NoFocus && w->isActiveWindow())
+ state |= Focusable;
+ if (w->hasFocus())
+ state |= Focused;
+ if (!w->isEnabled())
+ state |= Unavailable;
+ if (w->isWindow()) {
+ if (w->windowFlags() & Qt::WindowSystemMenuHint)
+ state |= Movable;
+ if (w->minimumSize() != w->maximumSize())
+ state |= Sizeable;
+ }
+
+ return state;
+}
+
+// ### Qt 5: remove me - binary compatibility hack
+QAccessibleWidgetEx::QAccessibleWidgetEx(QWidget *o, Role role, const QString& name)
+ : QAccessibleObjectEx(o)
+{
+ Q_ASSERT(widget());
+ d = new QAccessibleWidgetPrivate();
+ d->role = role;
+ d->name = name;
+ d->asking = 0;
+}
+
+int QAccessibleWidgetEx::childCount() const
+{ return reinterpret_cast<const QAccessibleWidget *>(this)->QAccessibleWidget::childCount(); }
+int QAccessibleWidgetEx::indexOfChild(const QAccessibleInterface *child) const
+{ return reinterpret_cast<const QAccessibleWidget *>(this)->QAccessibleWidget::indexOfChild(child); }
+QAccessible::Relation QAccessibleWidgetEx::relationTo(int child, const QAccessibleInterface *other, int otherChild) const
+{ return reinterpret_cast<const QAccessibleWidget *>(this)->QAccessibleWidget::relationTo(child, other, otherChild); }
+
+int QAccessibleWidgetEx::childAt(int x, int y) const
+{ return reinterpret_cast<const QAccessibleWidget *>(this)->QAccessibleWidget::childAt(x, y); }
+QRect QAccessibleWidgetEx::rect(int child) const
+{ return reinterpret_cast<const QAccessibleWidget *>(this)->QAccessibleWidget::rect(child); }
+int QAccessibleWidgetEx::navigate(RelationFlag rel, int entry, QAccessibleInterface **target) const
+{ return reinterpret_cast<const QAccessibleWidget *>(this)->QAccessibleWidget::navigate(rel, entry, target); }
+
+QString QAccessibleWidgetEx::text(Text t, int child) const
+{ return reinterpret_cast<const QAccessibleWidget *>(this)->QAccessibleWidget::text(t, child); }
+QAccessible::Role QAccessibleWidgetEx::role(int child) const
+{ return reinterpret_cast<const QAccessibleWidget *>(this)->QAccessibleWidget::role(child); }
+QAccessible::State QAccessibleWidgetEx::state(int child) const
+{ return (reinterpret_cast<const QAccessibleWidget *>(this)->QAccessibleWidget::state(child))
+ | HasInvokeExtension; }
+
+QString QAccessibleWidgetEx::actionText(int action, Text t, int child) const
+{ return reinterpret_cast<const QAccessibleWidget *>(this)->QAccessibleWidget::actionText(action, t, child); }
+bool QAccessibleWidgetEx::doAction(int action, int child, const QVariantList &params)
+{ return reinterpret_cast<QAccessibleWidget *>(this)->QAccessibleWidget::doAction(action, child, params); }
+
+QAccessibleWidgetEx::~QAccessibleWidgetEx()
+{ delete d; }
+QWidget *QAccessibleWidgetEx::widget() const
+{ return reinterpret_cast<const QAccessibleWidget *>(this)->QAccessibleWidget::widget(); }
+QObject *QAccessibleWidgetEx::parentObject() const
+{ return reinterpret_cast<const QAccessibleWidget *>(this)->QAccessibleWidget::parentObject(); }
+
+void QAccessibleWidgetEx::addControllingSignal(const QString &signal)
+{ reinterpret_cast<QAccessibleWidget *>(this)->QAccessibleWidget::addControllingSignal(signal); }
+void QAccessibleWidgetEx::setValue(const QString &value)
+{ reinterpret_cast<QAccessibleWidget *>(this)->QAccessibleWidget::setValue(value); }
+void QAccessibleWidgetEx::setDescription(const QString &desc)
+{ reinterpret_cast<QAccessibleWidget *>(this)->QAccessibleWidget::setDescription(desc); }
+void QAccessibleWidgetEx::setHelp(const QString &help)
+{ reinterpret_cast<QAccessibleWidget *>(this)->QAccessibleWidget::setHelp(help); }
+void QAccessibleWidgetEx::setAccelerator(const QString &accel)
+{ reinterpret_cast<QAccessibleWidget *>(this)->QAccessibleWidget::setAccelerator(accel); }
+
+QVariant QAccessibleWidgetEx::invokeMethodEx(Method method, int child, const QVariantList & /*params*/)
+{
+ if (child)
+ return QVariant();
+
+ switch (method) {
+ case ListSupportedMethods: {
+ QSet<QAccessible::Method> set;
+ set << ListSupportedMethods << ForegroundColor << BackgroundColor;
+ return qVariantFromValue(set);
+ }
+ case ForegroundColor:
+ return widget()->palette().color(widget()->foregroundRole());
+ case BackgroundColor:
+ return widget()->palette().color(widget()->backgroundRole());
+ default:
+ return QVariant();
+ }
+}
+
+QT_END_NAMESPACE
+
+#endif //QT_NO_ACCESSIBILITY
diff --git a/src/gui/accessible/qaccessiblewidget.h b/src/gui/accessible/qaccessiblewidget.h
new file mode 100644
index 0000000000..e6423d95fc
--- /dev/null
+++ b/src/gui/accessible/qaccessiblewidget.h
@@ -0,0 +1,141 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QACCESSIBLEWIDGET_H
+#define QACCESSIBLEWIDGET_H
+
+#include <QtGui/qaccessibleobject.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+#ifndef QT_NO_ACCESSIBILITY
+
+class QAccessibleWidgetPrivate;
+
+class Q_GUI_EXPORT QAccessibleWidget : public QAccessibleObject
+{
+public:
+ explicit QAccessibleWidget(QWidget *o, Role r = Client, const QString& name = QString());
+
+ int childCount() const;
+ int indexOfChild(const QAccessibleInterface *child) const;
+ Relation relationTo(int child, const QAccessibleInterface *other, int otherChild) const;
+
+ int childAt(int x, int y) const;
+ QRect rect(int child) const;
+ int navigate(RelationFlag rel, int entry, QAccessibleInterface **target) const;
+
+ QString text(Text t, int child) const;
+ Role role(int child) const;
+ State state(int child) const;
+
+#ifndef QT_NO_ACTION
+ int userActionCount(int child) const;
+ QString actionText(int action, Text t, int child) const;
+ bool doAction(int action, int child, const QVariantList &params);
+#endif
+
+protected:
+ ~QAccessibleWidget();
+ QWidget *widget() const;
+ QObject *parentObject() const;
+
+ void addControllingSignal(const QString &signal);
+ void setValue(const QString &value);
+ void setDescription(const QString &desc);
+ void setHelp(const QString &help);
+ void setAccelerator(const QString &accel);
+
+private:
+ friend class QAccessibleWidgetEx;
+ QAccessibleWidgetPrivate *d;
+ Q_DISABLE_COPY(QAccessibleWidget)
+};
+
+class Q_GUI_EXPORT QAccessibleWidgetEx : public QAccessibleObjectEx
+{
+public:
+ explicit QAccessibleWidgetEx(QWidget *o, Role r = Client, const QString& name = QString());
+
+ int childCount() const;
+ int indexOfChild(const QAccessibleInterface *child) const;
+ Relation relationTo(int child, const QAccessibleInterface *other, int otherChild) const;
+
+ int childAt(int x, int y) const;
+ QRect rect(int child) const;
+ int navigate(RelationFlag rel, int entry, QAccessibleInterface **target) const;
+
+ QString text(Text t, int child) const;
+ Role role(int child) const;
+ State state(int child) const;
+
+ QString actionText(int action, Text t, int child) const;
+ bool doAction(int action, int child, const QVariantList &params);
+
+ QVariant invokeMethodEx(Method method, int child, const QVariantList &params);
+
+protected:
+ ~QAccessibleWidgetEx();
+ QWidget *widget() const;
+ QObject *parentObject() const;
+
+ void addControllingSignal(const QString &signal);
+ void setValue(const QString &value);
+ void setDescription(const QString &desc);
+ void setHelp(const QString &help);
+ void setAccelerator(const QString &accel);
+
+private:
+ QAccessibleWidgetPrivate *d;
+ Q_DISABLE_COPY(QAccessibleWidgetEx)
+};
+
+#endif // QT_NO_ACCESSIBILITY
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QACCESSIBLEWIDGET_H
diff --git a/src/gui/dialogs/dialogs.pri b/src/gui/dialogs/dialogs.pri
new file mode 100644
index 0000000000..f1ec8588bd
--- /dev/null
+++ b/src/gui/dialogs/dialogs.pri
@@ -0,0 +1,97 @@
+# Qt dialogs module
+
+HEADERS += \
+ dialogs/qabstractprintdialog.h \
+ dialogs/qabstractprintdialog_p.h \
+ dialogs/qabstractpagesetupdialog.h \
+ dialogs/qabstractpagesetupdialog_p.h \
+ dialogs/qcolordialog.h \
+ dialogs/qcolordialog_p.h \
+ dialogs/qdialog.h \
+ dialogs/qdialog_p.h \
+ dialogs/qerrormessage.h \
+ dialogs/qfiledialog.h \
+ dialogs/qfiledialog_p.h \
+ dialogs/qfontdialog.h \
+ dialogs/qfontdialog_p.h \
+ dialogs/qinputdialog.h \
+ dialogs/qmessagebox.h \
+ dialogs/qpagesetupdialog.h \
+ dialogs/qprintdialog.h \
+ dialogs/qprogressdialog.h \
+ dialogs/qsidebar_p.h \
+ dialogs/qfilesystemmodel.h \
+ dialogs/qfilesystemmodel_p.h \
+ dialogs/qfileinfogatherer_p.h \
+ dialogs/qwizard.h \
+ dialogs/qprintpreviewdialog.h
+
+!embedded:mac {
+ OBJECTIVE_SOURCES += dialogs/qcolordialog_mac.mm \
+ dialogs/qfiledialog_mac.mm \
+ dialogs/qfontdialog_mac.mm \
+ dialogs/qnspanelproxy_mac.mm \
+ dialogs/qpagesetupdialog_mac.mm \
+ dialogs/qprintdialog_mac.mm
+}
+win32 {
+ HEADERS += dialogs/qwizard_win_p.h
+ SOURCES += dialogs/qdialogsbinarycompat_win.cpp \
+ dialogs/qfiledialog_win.cpp \
+ dialogs/qpagesetupdialog_win.cpp \
+ dialogs/qprintdialog_win.cpp \
+ dialogs/qwizard_win.cpp
+
+ !win32-borland:!wince*: LIBS += -lshell32 # the filedialog needs this library
+}
+
+!mac:!embedded:unix {
+ HEADERS += dialogs/qpagesetupdialog_unix_p.h
+ SOURCES += dialogs/qprintdialog_unix.cpp \
+ dialogs/qpagesetupdialog_unix.cpp
+ FORMS += dialogs/qprintsettingsoutput.ui \
+ dialogs/qprintwidget.ui \
+ dialogs/qprintpropertieswidget.ui
+}
+
+embedded {
+ contains(QT_CONFIG,qtopia) {
+ HEADERS += dialogs/qpagesetupdialog_unix_p.h
+ DEFINES += QTOPIA_PRINTDIALOG
+ SOURCES += dialogs/qprintdialog_qws.cpp \
+ dialogs/qpagesetupdialog_unix.cpp
+ } else {
+ HEADERS += dialogs/qpagesetupdialog_unix_p.h
+ SOURCES += dialogs/qprintdialog_unix.cpp \
+ dialogs/qpagesetupdialog_unix.cpp
+ FORMS += dialogs/qprintsettingsoutput.ui \
+ dialogs/qprintwidget.ui \
+ dialogs/qprintpropertieswidget.ui
+ }
+}
+
+wince*: FORMS += dialogs/qfiledialog_wince.ui
+else: FORMS += dialogs/qfiledialog.ui
+
+INCLUDEPATH += $$PWD
+SOURCES += \
+ dialogs/qabstractprintdialog.cpp \
+ dialogs/qabstractpagesetupdialog.cpp \
+ dialogs/qcolordialog.cpp \
+ dialogs/qdialog.cpp \
+ dialogs/qerrormessage.cpp \
+ dialogs/qfiledialog.cpp \
+ dialogs/qfontdialog.cpp \
+ dialogs/qinputdialog.cpp \
+ dialogs/qmessagebox.cpp \
+ dialogs/qprogressdialog.cpp \
+ dialogs/qsidebar.cpp \
+ dialogs/qfilesystemmodel.cpp \
+ dialogs/qfileinfogatherer.cpp \
+ dialogs/qpagesetupdialog.cpp \
+ dialogs/qwizard.cpp \
+ dialogs/qprintpreviewdialog.cpp
+
+FORMS += dialogs/qpagesetupwidget.ui
+RESOURCES += dialogs/qprintdialog.qrc
+RESOURCES += dialogs/qmessagebox.qrc \ No newline at end of file
diff --git a/src/gui/dialogs/images/fit-page-24.png b/src/gui/dialogs/images/fit-page-24.png
new file mode 100644
index 0000000000..c7b39d8853
--- /dev/null
+++ b/src/gui/dialogs/images/fit-page-24.png
Binary files differ
diff --git a/src/gui/dialogs/images/fit-page-32.png b/src/gui/dialogs/images/fit-page-32.png
new file mode 100644
index 0000000000..98bc12d3ed
--- /dev/null
+++ b/src/gui/dialogs/images/fit-page-32.png
Binary files differ
diff --git a/src/gui/dialogs/images/fit-width-24.png b/src/gui/dialogs/images/fit-width-24.png
new file mode 100644
index 0000000000..a729ffda54
--- /dev/null
+++ b/src/gui/dialogs/images/fit-width-24.png
Binary files differ
diff --git a/src/gui/dialogs/images/fit-width-32.png b/src/gui/dialogs/images/fit-width-32.png
new file mode 100644
index 0000000000..470a8b45d0
--- /dev/null
+++ b/src/gui/dialogs/images/fit-width-32.png
Binary files differ
diff --git a/src/gui/dialogs/images/go-first-24.png b/src/gui/dialogs/images/go-first-24.png
new file mode 100644
index 0000000000..55315ffa38
--- /dev/null
+++ b/src/gui/dialogs/images/go-first-24.png
Binary files differ
diff --git a/src/gui/dialogs/images/go-first-32.png b/src/gui/dialogs/images/go-first-32.png
new file mode 100644
index 0000000000..0fe6f94b77
--- /dev/null
+++ b/src/gui/dialogs/images/go-first-32.png
Binary files differ
diff --git a/src/gui/dialogs/images/go-last-24.png b/src/gui/dialogs/images/go-last-24.png
new file mode 100644
index 0000000000..81061b80f2
--- /dev/null
+++ b/src/gui/dialogs/images/go-last-24.png
Binary files differ
diff --git a/src/gui/dialogs/images/go-last-32.png b/src/gui/dialogs/images/go-last-32.png
new file mode 100644
index 0000000000..887506107e
--- /dev/null
+++ b/src/gui/dialogs/images/go-last-32.png
Binary files differ
diff --git a/src/gui/dialogs/images/go-next-24.png b/src/gui/dialogs/images/go-next-24.png
new file mode 100644
index 0000000000..9a55ef3d86
--- /dev/null
+++ b/src/gui/dialogs/images/go-next-24.png
Binary files differ
diff --git a/src/gui/dialogs/images/go-next-32.png b/src/gui/dialogs/images/go-next-32.png
new file mode 100644
index 0000000000..6d98f50f4f
--- /dev/null
+++ b/src/gui/dialogs/images/go-next-32.png
Binary files differ
diff --git a/src/gui/dialogs/images/go-previous-24.png b/src/gui/dialogs/images/go-previous-24.png
new file mode 100644
index 0000000000..2ea769eb8d
--- /dev/null
+++ b/src/gui/dialogs/images/go-previous-24.png
Binary files differ
diff --git a/src/gui/dialogs/images/go-previous-32.png b/src/gui/dialogs/images/go-previous-32.png
new file mode 100644
index 0000000000..37ba0c4e8d
--- /dev/null
+++ b/src/gui/dialogs/images/go-previous-32.png
Binary files differ
diff --git a/src/gui/dialogs/images/layout-landscape-24.png b/src/gui/dialogs/images/layout-landscape-24.png
new file mode 100644
index 0000000000..6f89a31cb6
--- /dev/null
+++ b/src/gui/dialogs/images/layout-landscape-24.png
Binary files differ
diff --git a/src/gui/dialogs/images/layout-landscape-32.png b/src/gui/dialogs/images/layout-landscape-32.png
new file mode 100644
index 0000000000..6a94946c36
--- /dev/null
+++ b/src/gui/dialogs/images/layout-landscape-32.png
Binary files differ
diff --git a/src/gui/dialogs/images/layout-portrait-24.png b/src/gui/dialogs/images/layout-portrait-24.png
new file mode 100644
index 0000000000..e0dbabc83b
--- /dev/null
+++ b/src/gui/dialogs/images/layout-portrait-24.png
Binary files differ
diff --git a/src/gui/dialogs/images/layout-portrait-32.png b/src/gui/dialogs/images/layout-portrait-32.png
new file mode 100644
index 0000000000..d17468c0a4
--- /dev/null
+++ b/src/gui/dialogs/images/layout-portrait-32.png
Binary files differ
diff --git a/src/gui/dialogs/images/page-setup-24.png b/src/gui/dialogs/images/page-setup-24.png
new file mode 100644
index 0000000000..4bfafdace0
--- /dev/null
+++ b/src/gui/dialogs/images/page-setup-24.png
Binary files differ
diff --git a/src/gui/dialogs/images/page-setup-32.png b/src/gui/dialogs/images/page-setup-32.png
new file mode 100644
index 0000000000..2313b8fe3b
--- /dev/null
+++ b/src/gui/dialogs/images/page-setup-32.png
Binary files differ
diff --git a/src/gui/dialogs/images/print-24.png b/src/gui/dialogs/images/print-24.png
new file mode 100644
index 0000000000..c6bf3e8672
--- /dev/null
+++ b/src/gui/dialogs/images/print-24.png
Binary files differ
diff --git a/src/gui/dialogs/images/print-32.png b/src/gui/dialogs/images/print-32.png
new file mode 100644
index 0000000000..5830888653
--- /dev/null
+++ b/src/gui/dialogs/images/print-32.png
Binary files differ
diff --git a/src/gui/dialogs/images/qtlogo-64.png b/src/gui/dialogs/images/qtlogo-64.png
new file mode 100644
index 0000000000..4f68e162de
--- /dev/null
+++ b/src/gui/dialogs/images/qtlogo-64.png
Binary files differ
diff --git a/src/gui/dialogs/images/status-color.png b/src/gui/dialogs/images/status-color.png
new file mode 100644
index 0000000000..af3cbfa31c
--- /dev/null
+++ b/src/gui/dialogs/images/status-color.png
Binary files differ
diff --git a/src/gui/dialogs/images/status-gray-scale.png b/src/gui/dialogs/images/status-gray-scale.png
new file mode 100644
index 0000000000..4462588809
--- /dev/null
+++ b/src/gui/dialogs/images/status-gray-scale.png
Binary files differ
diff --git a/src/gui/dialogs/images/view-page-multi-24.png b/src/gui/dialogs/images/view-page-multi-24.png
new file mode 100644
index 0000000000..87241472ae
--- /dev/null
+++ b/src/gui/dialogs/images/view-page-multi-24.png
Binary files differ
diff --git a/src/gui/dialogs/images/view-page-multi-32.png b/src/gui/dialogs/images/view-page-multi-32.png
new file mode 100644
index 0000000000..130885a041
--- /dev/null
+++ b/src/gui/dialogs/images/view-page-multi-32.png
Binary files differ
diff --git a/src/gui/dialogs/images/view-page-one-24.png b/src/gui/dialogs/images/view-page-one-24.png
new file mode 100644
index 0000000000..4c6457b892
--- /dev/null
+++ b/src/gui/dialogs/images/view-page-one-24.png
Binary files differ
diff --git a/src/gui/dialogs/images/view-page-one-32.png b/src/gui/dialogs/images/view-page-one-32.png
new file mode 100644
index 0000000000..537193984e
--- /dev/null
+++ b/src/gui/dialogs/images/view-page-one-32.png
Binary files differ
diff --git a/src/gui/dialogs/images/view-page-sided-24.png b/src/gui/dialogs/images/view-page-sided-24.png
new file mode 100644
index 0000000000..2131305c41
--- /dev/null
+++ b/src/gui/dialogs/images/view-page-sided-24.png
Binary files differ
diff --git a/src/gui/dialogs/images/view-page-sided-32.png b/src/gui/dialogs/images/view-page-sided-32.png
new file mode 100644
index 0000000000..e4d63f9992
--- /dev/null
+++ b/src/gui/dialogs/images/view-page-sided-32.png
Binary files differ
diff --git a/src/gui/dialogs/images/zoom-in-24.png b/src/gui/dialogs/images/zoom-in-24.png
new file mode 100644
index 0000000000..d29b142b6c
--- /dev/null
+++ b/src/gui/dialogs/images/zoom-in-24.png
Binary files differ
diff --git a/src/gui/dialogs/images/zoom-in-32.png b/src/gui/dialogs/images/zoom-in-32.png
new file mode 100644
index 0000000000..34d70af37b
--- /dev/null
+++ b/src/gui/dialogs/images/zoom-in-32.png
Binary files differ
diff --git a/src/gui/dialogs/images/zoom-out-24.png b/src/gui/dialogs/images/zoom-out-24.png
new file mode 100644
index 0000000000..19703474f8
--- /dev/null
+++ b/src/gui/dialogs/images/zoom-out-24.png
Binary files differ
diff --git a/src/gui/dialogs/images/zoom-out-32.png b/src/gui/dialogs/images/zoom-out-32.png
new file mode 100644
index 0000000000..b832206612
--- /dev/null
+++ b/src/gui/dialogs/images/zoom-out-32.png
Binary files differ
diff --git a/src/gui/dialogs/qabstractpagesetupdialog.cpp b/src/gui/dialogs/qabstractpagesetupdialog.cpp
new file mode 100644
index 0000000000..a07cb0e29d
--- /dev/null
+++ b/src/gui/dialogs/qabstractpagesetupdialog.cpp
@@ -0,0 +1,139 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qabstractpagesetupdialog.h"
+#include "qabstractpagesetupdialog_p.h"
+
+#ifndef QT_NO_PRINTDIALOG
+
+#include <QtCore/qcoreapplication.h>
+#include <QtGui/qprinter.h>
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \internal
+ \class QAbstractPageSetupDialog
+
+ \brief The QAbstractPageSetupDialog class provides a base for
+ implementations of page setup dialogs.
+*/
+
+/*!
+ Constructs the page setup dialog for the printer \a printer with
+ \a parent as parent widget.
+*/
+QAbstractPageSetupDialog::QAbstractPageSetupDialog(QPrinter *printer, QWidget *parent)
+ : QDialog(*(new QAbstractPageSetupDialogPrivate), parent)
+{
+ Q_D(QAbstractPageSetupDialog);
+ setWindowTitle(QCoreApplication::translate("QPrintPreviewDialog", "Page Setup"));
+ d->setPrinter(printer);
+}
+
+/*!
+ \internal
+*/
+QAbstractPageSetupDialog::QAbstractPageSetupDialog(QAbstractPageSetupDialogPrivate &ptr,
+ QPrinter *printer, QWidget *parent)
+ : QDialog(ptr, parent)
+{
+ Q_D(QAbstractPageSetupDialog);
+ setWindowTitle(QCoreApplication::translate("QPrintPreviewDialog", "Page Setup"));
+ d->setPrinter(printer);
+}
+
+QAbstractPageSetupDialog::~QAbstractPageSetupDialog()
+{
+ Q_D(QAbstractPageSetupDialog);
+ if (d->opts & QPageSetupDialog::OwnsPrinter)
+ delete d->printer;
+}
+
+/*!
+ Returns the printer that this page setup dialog is operating on.
+*/
+QPrinter *QAbstractPageSetupDialog::printer()
+{
+ Q_D(QAbstractPageSetupDialog);
+ return d->printer;
+}
+
+void QAbstractPageSetupDialogPrivate::setPrinter(QPrinter *newPrinter)
+{
+ if (newPrinter) {
+ printer = newPrinter;
+ } else {
+ printer = new QPrinter;
+ opts |= QPageSetupDialog::OwnsPrinter;
+ }
+#ifndef Q_WS_X11
+ if (printer->outputFormat() != QPrinter::NativeFormat)
+ qWarning("QPageSetupDialog: Cannot be used on non-native printers");
+#endif
+}
+
+/*!
+ \fn int QAbstractPageSetupDialog::exec()
+
+ This virtual function is called to pop up the dialog. It must be
+ reimplemented in subclasses.
+*/
+
+/*!
+ \reimp
+*/
+void QAbstractPageSetupDialog::done(int result)
+{
+ Q_D(QAbstractPageSetupDialog);
+ QDialog::done(result);
+ if (d->receiverToDisconnectOnClose) {
+ disconnect(this, SIGNAL(accepted()),
+ d->receiverToDisconnectOnClose, d->memberToDisconnectOnClose);
+ d->receiverToDisconnectOnClose = 0;
+ }
+ d->memberToDisconnectOnClose.clear();
+
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_PRINTDIALOG
diff --git a/src/gui/dialogs/qabstractpagesetupdialog.h b/src/gui/dialogs/qabstractpagesetupdialog.h
new file mode 100644
index 0000000000..3169984dcf
--- /dev/null
+++ b/src/gui/dialogs/qabstractpagesetupdialog.h
@@ -0,0 +1,82 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QABSTRACTPAGESETUPDIALOG_H
+#define QABSTRACTPAGESETUPDIALOG_H
+
+#include <QtGui/qdialog.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+#ifndef QT_NO_PRINTDIALOG
+
+class QAbstractPageSetupDialogPrivate;
+class QPrinter;
+
+// ### Qt 5: Remove this class
+class Q_GUI_EXPORT QAbstractPageSetupDialog : public QDialog
+{
+ Q_DECLARE_PRIVATE(QAbstractPageSetupDialog)
+ Q_OBJECT
+
+public:
+ explicit QAbstractPageSetupDialog(QPrinter *printer, QWidget *parent = 0);
+ QAbstractPageSetupDialog(QAbstractPageSetupDialogPrivate &ptr,
+ QPrinter *printer, QWidget *parent = 0);
+ ~QAbstractPageSetupDialog();
+
+ virtual int exec() = 0;
+ void done(int result);
+
+ QPrinter *printer();
+};
+
+#endif // QT_NO_PRINTDIALOG
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QABSTRACTPAGESETUPDIALOG_H
diff --git a/src/gui/dialogs/qabstractpagesetupdialog_p.h b/src/gui/dialogs/qabstractpagesetupdialog_p.h
new file mode 100644
index 0000000000..5d09342b6a
--- /dev/null
+++ b/src/gui/dialogs/qabstractpagesetupdialog_p.h
@@ -0,0 +1,88 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QABSTRACTPAGESETUPDIALOG_P_H
+#define QABSTRACTPAGESETUPDIALOG_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
+// to version without notice, or even be removed.
+//
+// We mean it.
+//
+//
+
+#include "private/qdialog_p.h"
+
+#ifndef QT_NO_PRINTDIALOG
+
+#include "qbytearray.h"
+#include "qpagesetupdialog.h"
+#include "qpointer.h"
+
+QT_BEGIN_NAMESPACE
+
+class QPrinter;
+
+class QAbstractPageSetupDialogPrivate : public QDialogPrivate
+{
+ Q_DECLARE_PUBLIC(QAbstractPageSetupDialog)
+
+public:
+ QAbstractPageSetupDialogPrivate() : printer(0) {}
+
+ void setPrinter(QPrinter *newPrinter);
+
+ QPrinter *printer;
+ QPageSetupDialog::PageSetupDialogOptions opts;
+ QPointer<QObject> receiverToDisconnectOnClose;
+ QByteArray memberToDisconnectOnClose;
+};
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_PRINTDIALOG
+
+#endif // QABSTRACTPAGESETUPDIALOG_P_H
diff --git a/src/gui/dialogs/qabstractprintdialog.cpp b/src/gui/dialogs/qabstractprintdialog.cpp
new file mode 100644
index 0000000000..0dc16c90b3
--- /dev/null
+++ b/src/gui/dialogs/qabstractprintdialog.cpp
@@ -0,0 +1,500 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qabstractprintdialog_p.h"
+#include "qcoreapplication.h"
+#include "qprintdialog.h"
+#include "qprinter.h"
+#include "private/qprinter_p.h"
+
+#ifndef QT_NO_PRINTDIALOG
+
+QT_BEGIN_NAMESPACE
+
+// hack
+class QPrintDialogPrivate : public QAbstractPrintDialogPrivate
+{
+};
+
+/*!
+ \class QAbstractPrintDialog
+ \brief The QAbstractPrintDialog class provides a base implementation for
+ print dialogs used to configure printers.
+
+ This class implements getter and setter functions that are used to
+ customize settings shown in print dialogs, but it is not used directly.
+ Use QPrintDialog to display a print dialog in your application.
+
+ \sa QPrintDialog, QPrinter, {Printing with Qt}
+*/
+
+/*!
+ \enum QAbstractPrintDialog::PrintRange
+
+ Used to specify the print range selection option.
+
+ \value AllPages All pages should be printed.
+ \value Selection Only the selection should be printed.
+ \value PageRange The specified page range should be printed.
+
+ \sa QPrinter::PrintRange
+*/
+
+/*!
+ \enum QAbstractPrintDialog::PrintDialogOption
+
+ Used to specify which parts of the print dialog should be visible.
+
+ \value None None of the options are enabled.
+ \value PrintToFile The print to file option is enabled.
+ \value PrintSelection The print selection option is enabled.
+ \value PrintPageRange The page range selection option is enabled.
+ \value PrintCollateCopies
+
+ This value is obsolete and does nothing since Qt 4.5:
+
+ \value DontUseSheet In previous versions of Qt, exec() the print dialog
+ would create a sheet by default the dialog was given a parent.
+ This is no longer supported in Qt 4.5. If you want to use sheets, use
+ QPrintDialog::open() instead.
+
+ \value PrintShowPageSize Show the page size + margins page only if this is enabled.
+*/
+
+/*!
+ Constructs an abstract print dialog for \a printer with \a parent
+ as parent widget.
+*/
+QAbstractPrintDialog::QAbstractPrintDialog(QPrinter *printer, QWidget *parent)
+ : QDialog(*(new QAbstractPrintDialogPrivate), parent)
+{
+ Q_D(QAbstractPrintDialog);
+ setWindowTitle(QCoreApplication::translate("QPrintDialog", "Print"));
+ d->setPrinter(printer);
+}
+
+/*!
+ \internal
+*/
+QAbstractPrintDialog::QAbstractPrintDialog(QAbstractPrintDialogPrivate &ptr,
+ QPrinter *printer,
+ QWidget *parent)
+ : QDialog(ptr, parent)
+{
+ Q_D(QAbstractPrintDialog);
+ setWindowTitle(QCoreApplication::translate("QPrintDialog", "Print"));
+ d->setPrinter(printer);
+}
+
+/*!
+ \internal
+*/
+QAbstractPrintDialog::~QAbstractPrintDialog()
+{
+ Q_D(QAbstractPrintDialog);
+ if (d->ownsPrinter)
+ delete d->printer;
+}
+
+/*!
+ Sets the given \a option to be enabled if \a on is true;
+ otherwise, clears the given \a option.
+
+ \sa options, testOption()
+*/
+void QPrintDialog::setOption(PrintDialogOption option, bool on)
+{
+ Q_D(QPrintDialog);
+ if (!(d->pd->options & option) != !on)
+ setOptions(d->pd->options ^ option);
+}
+
+/*!
+ Returns true if the given \a option is enabled; otherwise, returns
+ false.
+
+ \sa options, setOption()
+*/
+bool QPrintDialog::testOption(PrintDialogOption option) const
+{
+ Q_D(const QPrintDialog);
+ return (d->pd->options & option) != 0;
+}
+
+/*!
+ \property QPrintDialog::options
+ \brief the various options that affect the look and feel of the dialog
+ \since 4.5
+
+ By default, all options are disabled.
+
+ Options should be set before showing the dialog. Setting them while the
+ dialog is visible is not guaranteed to have an immediate effect on the
+ dialog (depending on the option and on the platform).
+
+ \sa setOption(), testOption()
+*/
+void QPrintDialog::setOptions(PrintDialogOptions options)
+{
+ Q_D(QPrintDialog);
+
+ PrintDialogOptions changed = (options ^ d->pd->options);
+ if (!changed)
+ return;
+
+ d->pd->options = options;
+}
+
+QPrintDialog::PrintDialogOptions QPrintDialog::options() const
+{
+ Q_D(const QPrintDialog);
+ return d->pd->options;
+}
+
+/*!
+ \obsolete
+
+ Use QPrintDialog::setOptions() instead.
+*/
+void QAbstractPrintDialog::setEnabledOptions(PrintDialogOptions options)
+{
+ Q_D(QAbstractPrintDialog);
+ d->pd->options = options;
+}
+
+/*!
+ \obsolete
+
+ Use QPrintDialog::setOption(\a option, true) instead.
+*/
+void QAbstractPrintDialog::addEnabledOption(PrintDialogOption option)
+{
+ Q_D(QAbstractPrintDialog);
+ d->pd->options |= option;
+}
+
+/*!
+ \obsolete
+
+ Use QPrintDialog::options() instead.
+*/
+QAbstractPrintDialog::PrintDialogOptions QAbstractPrintDialog::enabledOptions() const
+{
+ Q_D(const QAbstractPrintDialog);
+ return d->pd->options;
+}
+
+/*!
+ \obsolete
+
+ Use QPrintDialog::testOption(\a option) instead.
+*/
+bool QAbstractPrintDialog::isOptionEnabled(PrintDialogOption option) const
+{
+ Q_D(const QAbstractPrintDialog);
+ return d->pd->options & option;
+}
+
+/*!
+ Sets the print range option in to be \a range.
+ */
+void QAbstractPrintDialog::setPrintRange(PrintRange range)
+{
+ Q_D(QAbstractPrintDialog);
+ d->pd->printRange = range;
+}
+
+/*!
+ Returns the print range.
+*/
+QAbstractPrintDialog::PrintRange QAbstractPrintDialog::printRange() const
+{
+ Q_D(const QAbstractPrintDialog);
+ return d->pd->printRange;
+}
+
+/*!
+ Sets the page range in this dialog to be from \a min to \a max. This also
+ enables the PrintPageRange option.
+*/
+void QAbstractPrintDialog::setMinMax(int min, int max)
+{
+ Q_D(QAbstractPrintDialog);
+ Q_ASSERT_X(min <= max, "QAbstractPrintDialog::setMinMax",
+ "'min' must be less than or equal to 'max'");
+ d->pd->minPage = min;
+ d->pd->maxPage = max;
+ d->pd->options |= PrintPageRange;
+}
+
+/*!
+ Returns the minimum page in the page range.
+ By default, this value is set to 1.
+*/
+int QAbstractPrintDialog::minPage() const
+{
+ Q_D(const QAbstractPrintDialog);
+ return d->pd->minPage;
+}
+
+/*!
+ Returns the maximum page in the page range. As of Qt 4.4, this
+ function returns INT_MAX by default. Previous versions returned 1
+ by default.
+*/
+int QAbstractPrintDialog::maxPage() const
+{
+ Q_D(const QAbstractPrintDialog);
+ return d->pd->maxPage;
+}
+
+/*!
+ Sets the range in the print dialog to be from \a from to \a to.
+*/
+void QAbstractPrintDialog::setFromTo(int from, int to)
+{
+ Q_D(QAbstractPrintDialog);
+ Q_ASSERT_X(from <= to, "QAbstractPrintDialog::setFromTo",
+ "'from' must be less than or equal to 'to'");
+ d->pd->fromPage = from;
+ d->pd->toPage = to;
+
+ if (d->pd->minPage == 0 && d->pd->maxPage == 0)
+ setMinMax(1, to);
+}
+
+/*!
+ Returns the first page to be printed
+ By default, this value is set to 0.
+*/
+int QAbstractPrintDialog::fromPage() const
+{
+ Q_D(const QAbstractPrintDialog);
+ return d->pd->fromPage;
+}
+
+/*!
+ Returns the last page to be printed.
+ By default, this value is set to 0.
+*/
+int QAbstractPrintDialog::toPage() const
+{
+ Q_D(const QAbstractPrintDialog);
+ return d->pd->toPage;
+}
+
+
+/*!
+ Returns the printer that this printer dialog operates
+ on.
+*/
+QPrinter *QAbstractPrintDialog::printer() const
+{
+ Q_D(const QAbstractPrintDialog);
+ return d->printer;
+}
+
+void QAbstractPrintDialogPrivate::setPrinter(QPrinter *newPrinter)
+{
+ if (newPrinter) {
+ printer = newPrinter;
+ ownsPrinter = false;
+ } else {
+ printer = new QPrinter;
+ ownsPrinter = true;
+ }
+ pd = printer->d_func();
+}
+
+/*!
+ \fn int QAbstractPrintDialog::exec()
+
+ This virtual function is called to pop up the dialog. It must be
+ reimplemented in subclasses.
+*/
+
+/*!
+ \class QPrintDialog
+
+ \brief The QPrintDialog class provides a dialog for specifying
+ the printer's configuration.
+
+ \ingroup dialogs
+
+ The dialog allows users to change document-related settings, such
+ as the paper size and orientation, type of print (color or
+ grayscale), range of pages, and number of copies to print.
+
+ Controls are also provided to enable users to choose from the
+ printers available, including any configured network printers.
+
+ Typically, QPrintDialog objects are constructed with a QPrinter
+ object, and executed using the exec() function.
+
+ \snippet doc/src/snippets/code/src_gui_dialogs_qabstractprintdialog.cpp 0
+
+ If the dialog is accepted by the user, the QPrinter object is
+ correctly configured for printing.
+
+ \raw HTML
+ <table align="center">
+ <tr><td>
+ \endraw
+ \inlineimage plastique-printdialog.png
+ \raw HTML
+ </td><td>
+ \endraw
+ \inlineimage plastique-printdialog-properties.png
+ \raw HTML
+ </td></tr>
+ </table>
+ \endraw
+
+ The printer dialog (shown above in Plastique style) enables access to common
+ printing properties. On X11 platforms that use the CUPS printing system, the
+ settings for each available printer can be modified via the dialog's
+ \gui{Properties} push button.
+
+ On Windows and Mac OS X, the native print dialog is used, which means that
+ some QWidget and QDialog properties set on the dialog won't be respected.
+ The native print dialog on
+ Mac OS X does not support setting printer options, i.e.
+ QAbstractPrintDialog::setEnabledOptions() and
+ QAbstractPrintDialog::addEnabledOption() have no effect.
+
+ In Qt 4.4, it was possible to use the satic functions to show a sheet on
+ Mac OS X. This is no longer supported in Qt 4.5. If you want this
+ functionality, use QPrintDialog::open().
+
+ \sa QPageSetupDialog, QPrinter, {Pixelator Example}, {Order Form Example},
+ {Image Viewer Example}, {Scribble Example}
+*/
+
+/*!
+ \fn QPrintDialog::QPrintDialog(QPrinter *printer, QWidget *parent)
+
+ Constructs a new modal printer dialog for the given \a printer
+ with the given \a parent.
+*/
+
+/*!
+ \fn QPrintDialog::~QPrintDialog()
+
+ Destroys the print dialog.
+*/
+
+/*!
+ \fn int QPrintDialog::exec()
+ \reimp
+*/
+
+/*!
+ \since 4.4
+
+ Set a list of widgets as \a tabs to be shown on the print dialog, if supported.
+
+ Currently this option is only supported on X11.
+
+ Setting the option tabs will transfer their ownership to the print dialog.
+*/
+void QAbstractPrintDialog::setOptionTabs(const QList<QWidget*> &tabs)
+{
+ Q_D(QAbstractPrintDialog);
+ d->setTabs(tabs);
+}
+
+/*!
+
+ \fn void QPrintDialog::accepted(QPrinter *printer)
+
+ This signal is emitted when the user accepts the values set in the print dialog.
+ The \a printer parameter includes the printer that the settings were applied to.
+*/
+
+/*!
+ \fn QPrinter *QPrintDialog::printer()
+
+ Returns the printer that this printer dialog operates
+ on. This can be useful when using the QPrintDialog::open() method.
+*/
+
+/*!
+ Closes the dialog and sets its result code to \a result. If this dialog
+ is shown with exec(), done() causes the local event loop to finish,
+ and exec() to return \a result.
+
+ \sa QDialog::done()
+*/
+void QPrintDialog::done(int result)
+{
+ Q_D(QPrintDialog);
+ QDialog::done(result);
+ if (result == Accepted)
+ emit accepted(printer());
+ if (d->receiverToDisconnectOnClose) {
+ disconnect(this, SIGNAL(accepted(QPrinter*)),
+ d->receiverToDisconnectOnClose, d->memberToDisconnectOnClose);
+ d->receiverToDisconnectOnClose = 0;
+ }
+ d->memberToDisconnectOnClose.clear();
+}
+
+/*!
+ \since 4.5
+ \overload
+
+ Opens the dialog and connects its accepted() signal to the slot specified
+ by \a receiver and \a member.
+
+ The signal will be disconnected from the slot when the dialog is closed.
+*/
+void QPrintDialog::open(QObject *receiver, const char *member)
+{
+ Q_D(QPrintDialog);
+ connect(this, SIGNAL(accepted(QPrinter*)), receiver, member);
+ d->receiverToDisconnectOnClose = receiver;
+ d->memberToDisconnectOnClose = member;
+ QDialog::open();
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_PRINTDIALOG
diff --git a/src/gui/dialogs/qabstractprintdialog.h b/src/gui/dialogs/qabstractprintdialog.h
new file mode 100644
index 0000000000..fc1750cb16
--- /dev/null
+++ b/src/gui/dialogs/qabstractprintdialog.h
@@ -0,0 +1,127 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QABSTRACTPRINTDIALOG_H
+#define QABSTRACTPRINTDIALOG_H
+
+#include <QtGui/qdialog.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+#ifndef QT_NO_PRINTER
+
+class QAbstractPrintDialogPrivate;
+class QPrinter;
+
+// ### Qt 5: remove this class
+class Q_GUI_EXPORT QAbstractPrintDialog : public QDialog
+{
+ Q_DECLARE_PRIVATE(QAbstractPrintDialog)
+ Q_OBJECT
+
+public:
+ enum PrintRange {
+ AllPages,
+ Selection,
+ PageRange
+ };
+
+ enum PrintDialogOption {
+ None = 0x0000, // obsolete
+ PrintToFile = 0x0001,
+ PrintSelection = 0x0002,
+ PrintPageRange = 0x0004,
+ PrintShowPageSize = 0x0008,
+ PrintCollateCopies = 0x0010,
+ DontUseSheet = 0x0020
+ };
+
+ Q_DECLARE_FLAGS(PrintDialogOptions, PrintDialogOption)
+
+#ifndef QT_NO_PRINTDIALOG
+ explicit QAbstractPrintDialog(QPrinter *printer, QWidget *parent = 0);
+ ~QAbstractPrintDialog();
+
+ virtual int exec() = 0;
+
+ // obsolete
+ void addEnabledOption(PrintDialogOption option);
+ void setEnabledOptions(PrintDialogOptions options);
+ PrintDialogOptions enabledOptions() const;
+ bool isOptionEnabled(PrintDialogOption option) const;
+
+ void setOptionTabs(const QList<QWidget*> &tabs);
+
+ void setPrintRange(PrintRange range);
+ PrintRange printRange() const;
+
+ void setMinMax(int min, int max);
+ int minPage() const;
+ int maxPage() const;
+
+ void setFromTo(int fromPage, int toPage);
+ int fromPage() const;
+ int toPage() const;
+
+ QPrinter *printer() const;
+
+protected:
+ QAbstractPrintDialog(QAbstractPrintDialogPrivate &ptr, QPrinter *printer, QWidget *parent = 0);
+
+private:
+ Q_DISABLE_COPY(QAbstractPrintDialog)
+
+#endif // QT_NO_PRINTDIALOG
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(QAbstractPrintDialog::PrintDialogOptions)
+
+#endif // QT_NO_PRINTER
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QABSTRACTPRINTDIALOG_H
diff --git a/src/gui/dialogs/qabstractprintdialog_p.h b/src/gui/dialogs/qabstractprintdialog_p.h
new file mode 100644
index 0000000000..6bbe5a631b
--- /dev/null
+++ b/src/gui/dialogs/qabstractprintdialog_p.h
@@ -0,0 +1,95 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QABSTRACTPRINTDIALOG_P_H
+#define QABSTRACTPRINTDIALOG_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/qdialog_p.h"
+
+#ifndef QT_NO_PRINTDIALOG
+
+#include "QtGui/qabstractprintdialog.h"
+
+QT_BEGIN_NAMESPACE
+
+#ifndef QT_NO_PRINTER
+
+class QPrinter;
+class QPrinterPrivate;
+
+class QAbstractPrintDialogPrivate : public QDialogPrivate
+{
+ Q_DECLARE_PUBLIC(QAbstractPrintDialog)
+
+public:
+ QAbstractPrintDialogPrivate()
+ : printer(0), pd(0), ownsPrinter(false)
+ {
+ }
+
+ QPrinter *printer;
+ QPrinterPrivate *pd;
+ bool ownsPrinter;
+ QPointer<QObject> receiverToDisconnectOnClose;
+ QByteArray memberToDisconnectOnClose;
+
+ virtual void setTabs(const QList<QWidget *> &) {}
+ void setPrinter(QPrinter *newPrinter);
+};
+
+#endif //QT_NO_PRINTER
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_PRINTDIALOG
+
+#endif // QABSTRACTPRINTDIALOG_P_H
diff --git a/src/gui/dialogs/qcolordialog.cpp b/src/gui/dialogs/qcolordialog.cpp
new file mode 100644
index 0000000000..b744dca1c8
--- /dev/null
+++ b/src/gui/dialogs/qcolordialog.cpp
@@ -0,0 +1,1903 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qcolordialog_p.h"
+
+#ifndef QT_NO_COLORDIALOG
+
+#include "qapplication.h"
+#include "qdesktopwidget.h"
+#include "qdrawutil.h"
+#include "qevent.h"
+#include "qimage.h"
+#include "qlabel.h"
+#include "qlayout.h"
+#include "qlineedit.h"
+#include "qmenu.h"
+#include "qpainter.h"
+#include "qpixmap.h"
+#include "qpushbutton.h"
+#include "qsettings.h"
+#include "qstyle.h"
+#include "qstyleoption.h"
+#include "qvalidator.h"
+#include "qmime.h"
+#include "qspinbox.h"
+#include "qdialogbuttonbox.h"
+
+QT_BEGIN_NAMESPACE
+
+//////////// QWellArray BEGIN
+
+struct QWellArrayData;
+
+class QWellArray : public QWidget
+{
+ Q_OBJECT
+ Q_PROPERTY(int selectedColumn READ selectedColumn)
+ Q_PROPERTY(int selectedRow READ selectedRow)
+
+public:
+ QWellArray(int rows, int cols, QWidget* parent=0);
+ ~QWellArray() {}
+ QString cellContent(int row, int col) const;
+
+ int selectedColumn() const { return selCol; }
+ int selectedRow() const { return selRow; }
+
+ virtual void setCurrent(int row, int col);
+ virtual void setSelected(int row, int col);
+
+ QSize sizeHint() const;
+
+ virtual void setCellBrush(int row, int col, const QBrush &);
+ QBrush cellBrush(int row, int col);
+
+ inline int cellWidth() const
+ { return cellw; }
+
+ inline int cellHeight() const
+ { return cellh; }
+
+ inline int rowAt(int y) const
+ { return y / cellh; }
+
+ inline int columnAt(int x) const
+ { if (isRightToLeft()) return ncols - (x / cellw) - 1; return x / cellw; }
+
+ inline int rowY(int row) const
+ { return cellh * row; }
+
+ inline int columnX(int column) const
+ { if (isRightToLeft()) return cellw * (ncols - column - 1); return cellw * column; }
+
+ inline int numRows() const
+ { return nrows; }
+
+ inline int numCols() const
+ {return ncols; }
+
+ inline QRect cellRect() const
+ { return QRect(0, 0, cellw, cellh); }
+
+ inline QSize gridSize() const
+ { return QSize(ncols * cellw, nrows * cellh); }
+
+ QRect cellGeometry(int row, int column)
+ {
+ QRect r;
+ if (row >= 0 && row < nrows && column >= 0 && column < ncols)
+ r.setRect(columnX(column), rowY(row), cellw, cellh);
+ return r;
+ }
+
+ inline void updateCell(int row, int column) { update(cellGeometry(row, column)); }
+
+signals:
+ void selected(int row, int col);
+
+protected:
+ virtual void paintCell(QPainter *, int row, int col, const QRect&);
+ virtual void paintCellContents(QPainter *, int row, int col, const QRect&);
+
+ void mousePressEvent(QMouseEvent*);
+ void mouseReleaseEvent(QMouseEvent*);
+ void keyPressEvent(QKeyEvent*);
+ void focusInEvent(QFocusEvent*);
+ void focusOutEvent(QFocusEvent*);
+ void paintEvent(QPaintEvent *);
+
+private:
+ Q_DISABLE_COPY(QWellArray)
+
+ int nrows;
+ int ncols;
+ int cellw;
+ int cellh;
+ int curRow;
+ int curCol;
+ int selRow;
+ int selCol;
+ QWellArrayData *d;
+};
+
+void QWellArray::paintEvent(QPaintEvent *e)
+{
+ QRect r = e->rect();
+ int cx = r.x();
+ int cy = r.y();
+ int ch = r.height();
+ int cw = r.width();
+ int colfirst = columnAt(cx);
+ int collast = columnAt(cx + cw);
+ int rowfirst = rowAt(cy);
+ int rowlast = rowAt(cy + ch);
+
+ if (isRightToLeft()) {
+ int t = colfirst;
+ colfirst = collast;
+ collast = t;
+ }
+
+ QPainter painter(this);
+ QPainter *p = &painter;
+ QRect rect(0, 0, cellWidth(), cellHeight());
+
+
+ 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 = rowY(r);
+
+ // 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 = columnX(c);
+ // Translate painter and draw the cell
+ rect.translate(colp, rowp);
+ paintCell(p, r, c, rect);
+ rect.translate(-colp, -rowp);
+ }
+ }
+}
+
+struct QWellArrayData {
+ QBrush *brush;
+};
+
+QWellArray::QWellArray(int rows, int cols, QWidget *parent)
+ : QWidget(parent)
+ ,nrows(rows), ncols(cols)
+{
+ d = 0;
+ setFocusPolicy(Qt::StrongFocus);
+ cellw = 28;
+ cellh = 24;
+ curCol = 0;
+ curRow = 0;
+ selCol = -1;
+ selRow = -1;
+}
+
+QSize QWellArray::sizeHint() const
+{
+ ensurePolished();
+ return gridSize().boundedTo(QSize(640, 480));
+}
+
+
+void QWellArray::paintCell(QPainter* p, int row, int col, const QRect &rect)
+{
+ int b = 3; //margin
+
+ const QPalette & g = palette();
+ QStyleOptionFrame opt;
+ int dfw = style()->pixelMetric(QStyle::PM_DefaultFrameWidth);
+ opt.lineWidth = dfw;
+ opt.midLineWidth = 1;
+ opt.rect = rect.adjusted(b, b, -b, -b);
+ opt.palette = g;
+ opt.state = QStyle::State_Enabled | QStyle::State_Sunken;
+ style()->drawPrimitive(QStyle::PE_Frame, &opt, p, this);
+ b += dfw;
+
+ if ((row == curRow) && (col == curCol)) {
+ if (hasFocus()) {
+ QStyleOptionFocusRect opt;
+ opt.palette = g;
+ opt.rect = rect;
+ opt.state = QStyle::State_None | QStyle::State_KeyboardFocusChange;
+ style()->drawPrimitive(QStyle::PE_FrameFocusRect, &opt, p, this);
+ }
+ }
+ paintCellContents(p, row, col, opt.rect.adjusted(dfw, dfw, -dfw, -dfw));
+}
+
+/*!
+ Reimplement this function to change the contents of the well array.
+ */
+void QWellArray::paintCellContents(QPainter *p, int row, int col, const QRect &r)
+{
+ if (d) {
+ p->fillRect(r, d->brush[row*numCols()+col]);
+ } else {
+ p->fillRect(r, Qt::white);
+ p->setPen(Qt::black);
+ p->drawLine(r.topLeft(), r.bottomRight());
+ p->drawLine(r.topRight(), r.bottomLeft());
+ }
+}
+
+void QWellArray::mousePressEvent(QMouseEvent *e)
+{
+ // The current cell marker is set to the cell the mouse is pressed in
+ QPoint pos = e->pos();
+ setCurrent(rowAt(pos.y()), columnAt(pos.x()));
+}
+
+void QWellArray::mouseReleaseEvent(QMouseEvent * /* event */)
+{
+ // The current cell marker is set to the cell the mouse is clicked in
+ setSelected(curRow, curCol);
+}
+
+
+/*
+ Sets the cell currently having the focus. This is not necessarily
+ the same as the currently selected cell.
+*/
+
+void QWellArray::setCurrent(int row, int col)
+{
+ if ((curRow == row) && (curCol == col))
+ return;
+
+ if (row < 0 || col < 0)
+ row = col = -1;
+
+ int oldRow = curRow;
+ int oldCol = curCol;
+
+ curRow = row;
+ curCol = col;
+
+ updateCell(oldRow, oldCol);
+ updateCell(curRow, curCol);
+}
+
+/*
+ Sets the currently selected cell to \a row, \a column. If \a row or
+ \a column are less than zero, the current cell is unselected.
+
+ Does not set the position of the focus indicator.
+*/
+void QWellArray::setSelected(int row, int col)
+{
+ int oldRow = selRow;
+ int oldCol = selCol;
+
+ if (row < 0 || col < 0)
+ row = col = -1;
+
+ selCol = col;
+ selRow = row;
+
+ updateCell(oldRow, oldCol);
+ updateCell(selRow, selCol);
+ if (row >= 0)
+ emit selected(row, col);
+
+#ifndef QT_NO_MENU
+ if (isVisible() && qobject_cast<QMenu*>(parentWidget()))
+ parentWidget()->close();
+#endif
+}
+
+void QWellArray::focusInEvent(QFocusEvent*)
+{
+ updateCell(curRow, curCol);
+}
+
+void QWellArray::setCellBrush(int row, int col, const QBrush &b)
+{
+ if (!d) {
+ d = new QWellArrayData;
+ int i = numRows()*numCols();
+ d->brush = new QBrush[i];
+ }
+ if (row >= 0 && row < numRows() && col >= 0 && col < numCols())
+ d->brush[row*numCols()+col] = b;
+}
+
+/*
+ Returns the brush set for the cell at \a row, \a column. If no brush is
+ set, Qt::NoBrush is returned.
+*/
+
+QBrush QWellArray::cellBrush(int row, int col)
+{
+ if (d && row >= 0 && row < numRows() && col >= 0 && col < numCols())
+ return d->brush[row*numCols()+col];
+ return Qt::NoBrush;
+}
+
+
+
+/*!\reimp
+*/
+
+void QWellArray::focusOutEvent(QFocusEvent*)
+{
+ updateCell(curRow, curCol);
+}
+
+/*\reimp
+*/
+void QWellArray::keyPressEvent(QKeyEvent* e)
+{
+ switch(e->key()) { // Look at the key code
+ case Qt::Key_Left: // If 'left arrow'-key,
+ if(curCol > 0) // and cr't not in leftmost col
+ setCurrent(curRow, curCol - 1); // set cr't to next left column
+ break;
+ case Qt::Key_Right: // Correspondingly...
+ if(curCol < numCols()-1)
+ setCurrent(curRow, curCol + 1);
+ break;
+ case Qt::Key_Up:
+ if(curRow > 0)
+ setCurrent(curRow - 1, curCol);
+ break;
+ case Qt::Key_Down:
+ if(curRow < numRows()-1)
+ setCurrent(curRow + 1, curCol);
+ break;
+#if 0
+ // bad idea that shouldn't have been implemented; very counterintuitive
+ case Qt::Key_Return:
+ case Qt::Key_Enter:
+ /*
+ ignore the key, so that the dialog get it, but still select
+ the current row/col
+ */
+ e->ignore();
+ // fallthrough intended
+#endif
+ case Qt::Key_Space:
+ setSelected(curRow, curCol);
+ break;
+ default: // If not an interesting key,
+ e->ignore(); // we don't accept the event
+ return;
+ }
+
+}
+
+//////////// QWellArray END
+
+static bool initrgb = false;
+static QRgb stdrgb[6*8];
+static QRgb cusrgb[2*8];
+static bool customSet = false;
+
+
+static void initRGB()
+{
+ if (initrgb)
+ return;
+ initrgb = true;
+ int i = 0;
+ for (int g = 0; g < 4; g++)
+ for (int r = 0; r < 4; r++)
+ for (int b = 0; b < 3; b++)
+ stdrgb[i++] = qRgb(r * 255 / 3, g * 255 / 3, b * 255 / 2);
+
+ for (i = 0; i < 2*8; i++)
+ cusrgb[i] = 0xffffffff;
+}
+
+/*!
+ Returns the number of custom colors supported by QColorDialog. All
+ color dialogs share the same custom colors.
+*/
+int QColorDialog::customCount()
+{
+ return 2 * 8;
+}
+
+/*!
+ \since 4.5
+
+ Returns the custom color at the given \a index as a QRgb value.
+*/
+QRgb QColorDialog::customColor(int index)
+{
+ if (uint(index) >= uint(customCount()))
+ return qRgb(255, 255, 255);
+ initRGB();
+ return cusrgb[index];
+}
+
+/*!
+ Sets the custom color at \a index to the QRgb \a color value.
+
+ \note This function does not apply to the Native Color Dialog on the Mac
+ OS X platform. If you still require this function, use the
+ QColorDialog::DontUseNativeDialog option.
+*/
+void QColorDialog::setCustomColor(int index, QRgb color)
+{
+ if (uint(index) >= uint(customCount()))
+ return;
+ initRGB();
+ customSet = true;
+ cusrgb[index] = color;
+}
+
+/*!
+ Sets the standard color at \a index to the QRgb \a color value.
+
+ \note This function does not apply to the Native Color Dialog on the Mac
+ OS X platform. If you still require this function, use the
+ QColorDialog::DontUseNativeDialog option.
+*/
+
+void QColorDialog::setStandardColor(int index, QRgb color)
+{
+ if (uint(index) >= uint(6 * 8))
+ return;
+ initRGB();
+ stdrgb[index] = color;
+}
+
+static inline void rgb2hsv(QRgb rgb, int &h, int &s, int &v)
+{
+ QColor c;
+ c.setRgb(rgb);
+ c.getHsv(&h, &s, &v);
+}
+
+class QColorWell : public QWellArray
+{
+public:
+ QColorWell(QWidget *parent, int r, int c, QRgb *vals)
+ :QWellArray(r, c, parent), values(vals), mousePressed(false), oldCurrent(-1, -1)
+ { setSizePolicy(QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum)); }
+
+protected:
+ void paintCellContents(QPainter *, int row, int col, const QRect&);
+ void mousePressEvent(QMouseEvent *e);
+ void mouseMoveEvent(QMouseEvent *e);
+ void mouseReleaseEvent(QMouseEvent *e);
+#ifndef QT_NO_DRAGANDDROP
+ void dragEnterEvent(QDragEnterEvent *e);
+ void dragLeaveEvent(QDragLeaveEvent *e);
+ void dragMoveEvent(QDragMoveEvent *e);
+ void dropEvent(QDropEvent *e);
+#endif
+
+private:
+ QRgb *values;
+ bool mousePressed;
+ QPoint pressPos;
+ QPoint oldCurrent;
+
+};
+
+void QColorWell::paintCellContents(QPainter *p, int row, int col, const QRect &r)
+{
+ int i = row + col*numRows();
+ p->fillRect(r, QColor(values[i]));
+}
+
+void QColorWell::mousePressEvent(QMouseEvent *e)
+{
+ oldCurrent = QPoint(selectedRow(), selectedColumn());
+ QWellArray::mousePressEvent(e);
+ mousePressed = true;
+ pressPos = e->pos();
+}
+
+void QColorWell::mouseMoveEvent(QMouseEvent *e)
+{
+ QWellArray::mouseMoveEvent(e);
+#ifndef QT_NO_DRAGANDDROP
+ if (!mousePressed)
+ return;
+ if ((pressPos - e->pos()).manhattanLength() > QApplication::startDragDistance()) {
+ setCurrent(oldCurrent.x(), oldCurrent.y());
+ int i = rowAt(pressPos.y()) + columnAt(pressPos.x()) * numRows();
+ QColor col(values[i]);
+ QMimeData *mime = new QMimeData;
+ mime->setColorData(col);
+ QPixmap pix(cellWidth(), cellHeight());
+ pix.fill(col);
+ QPainter p(&pix);
+ p.drawRect(0, 0, pix.width() - 1, pix.height() - 1);
+ p.end();
+ QDrag *drg = new QDrag(this);
+ drg->setMimeData(mime);
+ drg->setPixmap(pix);
+ mousePressed = false;
+ drg->start();
+ }
+#endif
+}
+
+#ifndef QT_NO_DRAGANDDROP
+void QColorWell::dragEnterEvent(QDragEnterEvent *e)
+{
+ if (qvariant_cast<QColor>(e->mimeData()->colorData()).isValid())
+ e->accept();
+ else
+ e->ignore();
+}
+
+void QColorWell::dragLeaveEvent(QDragLeaveEvent *)
+{
+ if (hasFocus())
+ parentWidget()->setFocus();
+}
+
+void QColorWell::dragMoveEvent(QDragMoveEvent *e)
+{
+ if (qvariant_cast<QColor>(e->mimeData()->colorData()).isValid()) {
+ setCurrent(rowAt(e->pos().y()), columnAt(e->pos().x()));
+ e->accept();
+ } else {
+ e->ignore();
+ }
+}
+
+void QColorWell::dropEvent(QDropEvent *e)
+{
+ QColor col = qvariant_cast<QColor>(e->mimeData()->colorData());
+ if (col.isValid()) {
+ int i = rowAt(e->pos().y()) + columnAt(e->pos().x()) * numRows();
+ values[i] = col.rgb();
+ update();
+ e->accept();
+ } else {
+ e->ignore();
+ }
+}
+
+#endif // QT_NO_DRAGANDDROP
+
+void QColorWell::mouseReleaseEvent(QMouseEvent *e)
+{
+ if (!mousePressed)
+ return;
+ QWellArray::mouseReleaseEvent(e);
+ mousePressed = false;
+}
+
+class QColorPicker : public QFrame
+{
+ Q_OBJECT
+public:
+ QColorPicker(QWidget* parent);
+ ~QColorPicker();
+
+public slots:
+ void setCol(int h, int s);
+
+signals:
+ void newCol(int h, int s);
+
+protected:
+ QSize sizeHint() const;
+ void paintEvent(QPaintEvent*);
+ void mouseMoveEvent(QMouseEvent *);
+ void mousePressEvent(QMouseEvent *);
+
+private:
+ int hue;
+ int sat;
+
+ QPoint colPt();
+ int huePt(const QPoint &pt);
+ int satPt(const QPoint &pt);
+ void setCol(const QPoint &pt);
+
+ QPixmap *pix;
+};
+
+static int pWidth = 220;
+static int pHeight = 200;
+
+class QColorLuminancePicker : public QWidget
+{
+ Q_OBJECT
+public:
+ QColorLuminancePicker(QWidget* parent=0);
+ ~QColorLuminancePicker();
+
+public slots:
+ void setCol(int h, int s, int v);
+ void setCol(int h, int s);
+
+signals:
+ void newHsv(int h, int s, int v);
+
+protected:
+ void paintEvent(QPaintEvent*);
+ void mouseMoveEvent(QMouseEvent *);
+ void mousePressEvent(QMouseEvent *);
+
+private:
+ enum { foff = 3, coff = 4 }; //frame and contents offset
+ int val;
+ int hue;
+ int sat;
+
+ int y2val(int y);
+ int val2y(int val);
+ void setVal(int v);
+
+ QPixmap *pix;
+};
+
+
+int QColorLuminancePicker::y2val(int y)
+{
+ int d = height() - 2*coff - 1;
+ return 255 - (y - coff)*255/d;
+}
+
+int QColorLuminancePicker::val2y(int v)
+{
+ int d = height() - 2*coff - 1;
+ return coff + (255-v)*d/255;
+}
+
+QColorLuminancePicker::QColorLuminancePicker(QWidget* parent)
+ :QWidget(parent)
+{
+ hue = 100; val = 100; sat = 100;
+ pix = 0;
+ // setAttribute(WA_NoErase, true);
+}
+
+QColorLuminancePicker::~QColorLuminancePicker()
+{
+ delete pix;
+}
+
+void QColorLuminancePicker::mouseMoveEvent(QMouseEvent *m)
+{
+ setVal(y2val(m->y()));
+}
+void QColorLuminancePicker::mousePressEvent(QMouseEvent *m)
+{
+ setVal(y2val(m->y()));
+}
+
+void QColorLuminancePicker::setVal(int v)
+{
+ if (val == v)
+ return;
+ val = qMax(0, qMin(v,255));
+ delete pix; pix=0;
+ repaint();
+ emit newHsv(hue, sat, val);
+}
+
+//receives from a hue,sat chooser and relays.
+void QColorLuminancePicker::setCol(int h, int s)
+{
+ setCol(h, s, val);
+ emit newHsv(h, s, val);
+}
+
+void QColorLuminancePicker::paintEvent(QPaintEvent *)
+{
+ int w = width() - 5;
+
+ QRect r(0, foff, w, height() - 2*foff);
+ int wi = r.width() - 2;
+ int hi = r.height() - 2;
+ if (!pix || pix->height() != hi || pix->width() != wi) {
+ delete pix;
+ QImage img(wi, hi, QImage::Format_RGB32);
+ int y;
+ uint *pixel = (uint *) img.scanLine(0);
+ for (y = 0; y < hi; y++) {
+ const uint *end = pixel + wi;
+ while (pixel < end) {
+ QColor c;
+ c.setHsv(hue, sat, y2val(y+coff));
+ *pixel = c.rgb();
+ ++pixel;
+ }
+ }
+ pix = new QPixmap(QPixmap::fromImage(img));
+ }
+ QPainter p(this);
+ p.drawPixmap(1, coff, *pix);
+ const QPalette &g = palette();
+ qDrawShadePanel(&p, r, g, true);
+ p.setPen(g.foreground().color());
+ p.setBrush(g.foreground());
+ QPolygon a;
+ int y = val2y(val);
+ a.setPoints(3, w, y, w+5, y+5, w+5, y-5);
+ p.eraseRect(w, 0, 5, height());
+ p.drawPolygon(a);
+}
+
+void QColorLuminancePicker::setCol(int h, int s , int v)
+{
+ val = v;
+ hue = h;
+ sat = s;
+ delete pix; pix=0;
+ repaint();
+}
+
+QPoint QColorPicker::colPt()
+{ return QPoint((360-hue)*(pWidth-1)/360, (255-sat)*(pHeight-1)/255); }
+int QColorPicker::huePt(const QPoint &pt)
+{ return 360 - pt.x()*360/(pWidth-1); }
+int QColorPicker::satPt(const QPoint &pt)
+{ return 255 - pt.y()*255/(pHeight-1) ; }
+void QColorPicker::setCol(const QPoint &pt)
+{ setCol(huePt(pt), satPt(pt)); }
+
+QColorPicker::QColorPicker(QWidget* parent)
+ : QFrame(parent)
+{
+ hue = 0; sat = 0;
+ setCol(150, 255);
+
+ QImage img(pWidth, pHeight, QImage::Format_RGB32);
+ int x, y;
+ uint *pixel = (uint *) img.scanLine(0);
+ for (y = 0; y < pHeight; y++) {
+ const uint *end = pixel + pWidth;
+ x = 0;
+ while (pixel < end) {
+ QPoint p(x, y);
+ QColor c;
+ c.setHsv(huePt(p), satPt(p), 200);
+ *pixel = c.rgb();
+ ++pixel;
+ ++x;
+ }
+ }
+ pix = new QPixmap(QPixmap::fromImage(img));
+ setAttribute(Qt::WA_NoSystemBackground);
+ setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed) );
+}
+
+QColorPicker::~QColorPicker()
+{
+ delete pix;
+}
+
+QSize QColorPicker::sizeHint() const
+{
+ return QSize(pWidth + 2*frameWidth(), pHeight + 2*frameWidth());
+}
+
+void QColorPicker::setCol(int h, int s)
+{
+ int nhue = qMin(qMax(0,h), 359);
+ int nsat = qMin(qMax(0,s), 255);
+ if (nhue == hue && nsat == sat)
+ return;
+
+ QRect r(colPt(), QSize(20,20));
+ hue = nhue; sat = nsat;
+ r = r.united(QRect(colPt(), QSize(20,20)));
+ r.translate(contentsRect().x()-9, contentsRect().y()-9);
+ // update(r);
+ repaint(r);
+}
+
+void QColorPicker::mouseMoveEvent(QMouseEvent *m)
+{
+ QPoint p = m->pos() - contentsRect().topLeft();
+ setCol(p);
+ emit newCol(hue, sat);
+}
+
+void QColorPicker::mousePressEvent(QMouseEvent *m)
+{
+ QPoint p = m->pos() - contentsRect().topLeft();
+ setCol(p);
+ emit newCol(hue, sat);
+}
+
+void QColorPicker::paintEvent(QPaintEvent* )
+{
+ QPainter p(this);
+ drawFrame(&p);
+ QRect r = contentsRect();
+
+ p.drawPixmap(r.topLeft(), *pix);
+ QPoint pt = colPt() + r.topLeft();
+ p.setPen(Qt::black);
+
+ p.fillRect(pt.x()-9, pt.y(), 20, 2, Qt::black);
+ p.fillRect(pt.x(), pt.y()-9, 2, 20, Qt::black);
+
+}
+
+class QColSpinBox : public QSpinBox
+{
+public:
+ QColSpinBox(QWidget *parent)
+ : QSpinBox(parent) { setRange(0, 255); }
+ void setValue(int i) {
+ bool block = signalsBlocked();
+ blockSignals(true);
+ QSpinBox::setValue(i);
+ blockSignals(block);
+ }
+};
+
+class QColorShowLabel;
+
+class QColorShower : public QWidget
+{
+ Q_OBJECT
+public:
+ QColorShower(QColorDialog *parent);
+
+ //things that don't emit signals
+ void setHsv(int h, int s, int v);
+
+ int currentAlpha() const
+ { return (colorDialog->options() & QColorDialog::ShowAlphaChannel) ? alphaEd->value() : 255; }
+ void setCurrentAlpha(int a) { alphaEd->setValue(a); rgbEd(); }
+ void showAlpha(bool b);
+ bool isAlphaVisible() const;
+
+ QRgb currentColor() const { return curCol; }
+ QColor currentQColor() const { return curQColor; }
+ void retranslateStrings();
+ void updateQColor();
+
+public slots:
+ void setRgb(QRgb rgb);
+
+signals:
+ void newCol(QRgb rgb);
+ void currentColorChanged(const QColor &color);
+
+private slots:
+ void rgbEd();
+ void hsvEd();
+private:
+ void showCurrentColor();
+ int hue, sat, val;
+ QRgb curCol;
+ QColor curQColor;
+ QLabel *lblHue;
+ QLabel *lblSat;
+ QLabel *lblVal;
+ QLabel *lblRed;
+ QLabel *lblGreen;
+ QLabel *lblBlue;
+ QColSpinBox *hEd;
+ QColSpinBox *sEd;
+ QColSpinBox *vEd;
+ QColSpinBox *rEd;
+ QColSpinBox *gEd;
+ QColSpinBox *bEd;
+ QColSpinBox *alphaEd;
+ QLabel *alphaLab;
+ QColorShowLabel *lab;
+ bool rgbOriginal;
+ QColorDialog *colorDialog;
+
+ friend class QColorDialog;
+ friend class QColorDialogPrivate;
+};
+
+class QColorShowLabel : public QFrame
+{
+ Q_OBJECT
+
+public:
+ QColorShowLabel(QWidget *parent) : QFrame(parent) {
+ setFrameStyle(QFrame::Panel|QFrame::Sunken);
+ setAcceptDrops(true);
+ mousePressed = false;
+ }
+ void setColor(QColor c) { col = c; }
+
+signals:
+ void colorDropped(QRgb);
+
+protected:
+ void paintEvent(QPaintEvent *);
+ void mousePressEvent(QMouseEvent *e);
+ void mouseMoveEvent(QMouseEvent *e);
+ void mouseReleaseEvent(QMouseEvent *e);
+#ifndef QT_NO_DRAGANDDROP
+ void dragEnterEvent(QDragEnterEvent *e);
+ void dragLeaveEvent(QDragLeaveEvent *e);
+ void dropEvent(QDropEvent *e);
+#endif
+
+private:
+ QColor col;
+ bool mousePressed;
+ QPoint pressPos;
+};
+
+void QColorShowLabel::paintEvent(QPaintEvent *e)
+{
+ QPainter p(this);
+ drawFrame(&p);
+ p.fillRect(contentsRect()&e->rect(), col);
+}
+
+void QColorShower::showAlpha(bool b)
+{
+ alphaLab->setVisible(b);
+ alphaEd->setVisible(b);
+}
+
+inline bool QColorShower::isAlphaVisible() const
+{
+ return alphaLab->isVisible();
+}
+
+void QColorShowLabel::mousePressEvent(QMouseEvent *e)
+{
+ mousePressed = true;
+ pressPos = e->pos();
+}
+
+void QColorShowLabel::mouseMoveEvent(QMouseEvent *e)
+{
+#ifdef QT_NO_DRAGANDDROP
+ Q_UNUSED(e);
+#else
+ if (!mousePressed)
+ return;
+ if ((pressPos - e->pos()).manhattanLength() > QApplication::startDragDistance()) {
+ QMimeData *mime = new QMimeData;
+ mime->setColorData(col);
+ QPixmap pix(30, 20);
+ pix.fill(col);
+ QPainter p(&pix);
+ p.drawRect(0, 0, pix.width() - 1, pix.height() - 1);
+ p.end();
+ QDrag *drg = new QDrag(this);
+ drg->setMimeData(mime);
+ drg->setPixmap(pix);
+ mousePressed = false;
+ drg->start();
+ }
+#endif
+}
+
+#ifndef QT_NO_DRAGANDDROP
+void QColorShowLabel::dragEnterEvent(QDragEnterEvent *e)
+{
+ if (qvariant_cast<QColor>(e->mimeData()->colorData()).isValid())
+ e->accept();
+ else
+ e->ignore();
+}
+
+void QColorShowLabel::dragLeaveEvent(QDragLeaveEvent *)
+{
+}
+
+void QColorShowLabel::dropEvent(QDropEvent *e)
+{
+ QColor color = qvariant_cast<QColor>(e->mimeData()->colorData());
+ if (color.isValid()) {
+ col = color;
+ repaint();
+ emit colorDropped(col.rgb());
+ e->accept();
+ } else {
+ e->ignore();
+ }
+}
+#endif // QT_NO_DRAGANDDROP
+
+void QColorShowLabel::mouseReleaseEvent(QMouseEvent *)
+{
+ if (!mousePressed)
+ return;
+ mousePressed = false;
+}
+
+QColorShower::QColorShower(QColorDialog *parent)
+ : QWidget(parent)
+{
+ colorDialog = parent;
+
+ curCol = qRgb(255, 255, 255);
+ curQColor = Qt::white;
+
+ QGridLayout *gl = new QGridLayout(this);
+ gl->setMargin(gl->spacing());
+ lab = new QColorShowLabel(this);
+#ifndef Q_OS_WINCE
+ lab->setMinimumWidth(60);
+#else
+ lab->setMinimumWidth(20);
+#endif
+ gl->addWidget(lab, 0, 0, -1, 1);
+ connect(lab, SIGNAL(colorDropped(QRgb)), this, SIGNAL(newCol(QRgb)));
+ connect(lab, SIGNAL(colorDropped(QRgb)), this, SLOT(setRgb(QRgb)));
+
+ hEd = new QColSpinBox(this);
+ hEd->setRange(0, 359);
+ lblHue = new QLabel(this);
+#ifndef QT_NO_SHORTCUT
+ lblHue->setBuddy(hEd);
+#endif
+ lblHue->setAlignment(Qt::AlignRight|Qt::AlignVCenter);
+ gl->addWidget(lblHue, 0, 1);
+ gl->addWidget(hEd, 0, 2);
+
+ sEd = new QColSpinBox(this);
+ lblSat = new QLabel(this);
+#ifndef QT_NO_SHORTCUT
+ lblSat->setBuddy(sEd);
+#endif
+ lblSat->setAlignment(Qt::AlignRight|Qt::AlignVCenter);
+ gl->addWidget(lblSat, 1, 1);
+ gl->addWidget(sEd, 1, 2);
+
+ vEd = new QColSpinBox(this);
+ lblVal = new QLabel(this);
+#ifndef QT_NO_SHORTCUT
+ lblVal->setBuddy(vEd);
+#endif
+ lblVal->setAlignment(Qt::AlignRight|Qt::AlignVCenter);
+ gl->addWidget(lblVal, 2, 1);
+ gl->addWidget(vEd, 2, 2);
+
+ rEd = new QColSpinBox(this);
+ lblRed = new QLabel(this);
+#ifndef QT_NO_SHORTCUT
+ lblRed->setBuddy(rEd);
+#endif
+ lblRed->setAlignment(Qt::AlignRight|Qt::AlignVCenter);
+ gl->addWidget(lblRed, 0, 3);
+ gl->addWidget(rEd, 0, 4);
+
+ gEd = new QColSpinBox(this);
+ lblGreen = new QLabel(this);
+#ifndef QT_NO_SHORTCUT
+ lblGreen->setBuddy(gEd);
+#endif
+ lblGreen->setAlignment(Qt::AlignRight|Qt::AlignVCenter);
+ gl->addWidget(lblGreen, 1, 3);
+ gl->addWidget(gEd, 1, 4);
+
+ bEd = new QColSpinBox(this);
+ lblBlue = new QLabel(this);
+#ifndef QT_NO_SHORTCUT
+ lblBlue->setBuddy(bEd);
+#endif
+ lblBlue->setAlignment(Qt::AlignRight|Qt::AlignVCenter);
+ gl->addWidget(lblBlue, 2, 3);
+ gl->addWidget(bEd, 2, 4);
+
+ alphaEd = new QColSpinBox(this);
+ alphaLab = new QLabel(this);
+#ifndef QT_NO_SHORTCUT
+ alphaLab->setBuddy(alphaEd);
+#endif
+ alphaLab->setAlignment(Qt::AlignRight|Qt::AlignVCenter);
+ gl->addWidget(alphaLab, 3, 1, 1, 3);
+ gl->addWidget(alphaEd, 3, 4);
+ alphaEd->hide();
+ alphaLab->hide();
+
+ connect(hEd, SIGNAL(valueChanged(int)), this, SLOT(hsvEd()));
+ connect(sEd, SIGNAL(valueChanged(int)), this, SLOT(hsvEd()));
+ connect(vEd, SIGNAL(valueChanged(int)), this, SLOT(hsvEd()));
+
+ connect(rEd, SIGNAL(valueChanged(int)), this, SLOT(rgbEd()));
+ connect(gEd, SIGNAL(valueChanged(int)), this, SLOT(rgbEd()));
+ connect(bEd, SIGNAL(valueChanged(int)), this, SLOT(rgbEd()));
+ connect(alphaEd, SIGNAL(valueChanged(int)), this, SLOT(rgbEd()));
+
+ retranslateStrings();
+}
+
+inline QRgb QColorDialogPrivate::currentColor() const { return cs->currentColor(); }
+inline int QColorDialogPrivate::currentAlpha() const { return cs->currentAlpha(); }
+inline void QColorDialogPrivate::setCurrentAlpha(int a) { cs->setCurrentAlpha(a); }
+inline void QColorDialogPrivate::showAlpha(bool b) { cs->showAlpha(b); }
+inline bool QColorDialogPrivate::isAlphaVisible() const { return cs->isAlphaVisible(); }
+
+QColor QColorDialogPrivate::currentQColor() const
+{
+ return cs->currentQColor();
+}
+
+void QColorShower::showCurrentColor()
+{
+ lab->setColor(currentColor());
+ lab->repaint();
+}
+
+void QColorShower::rgbEd()
+{
+ rgbOriginal = true;
+ curCol = qRgba(rEd->value(), gEd->value(), bEd->value(), currentAlpha());
+
+ rgb2hsv(currentColor(), hue, sat, val);
+
+ hEd->setValue(hue);
+ sEd->setValue(sat);
+ vEd->setValue(val);
+
+ showCurrentColor();
+ emit newCol(currentColor());
+ updateQColor();
+}
+
+void QColorShower::hsvEd()
+{
+ rgbOriginal = false;
+ hue = hEd->value();
+ sat = sEd->value();
+ val = vEd->value();
+
+ QColor c;
+ c.setHsv(hue, sat, val);
+ curCol = c.rgb();
+
+ rEd->setValue(qRed(currentColor()));
+ gEd->setValue(qGreen(currentColor()));
+ bEd->setValue(qBlue(currentColor()));
+
+ showCurrentColor();
+ emit newCol(currentColor());
+ updateQColor();
+}
+
+void QColorShower::setRgb(QRgb rgb)
+{
+ rgbOriginal = true;
+ curCol = rgb;
+
+ rgb2hsv(currentColor(), hue, sat, val);
+
+ hEd->setValue(hue);
+ sEd->setValue(sat);
+ vEd->setValue(val);
+
+ rEd->setValue(qRed(currentColor()));
+ gEd->setValue(qGreen(currentColor()));
+ bEd->setValue(qBlue(currentColor()));
+
+ showCurrentColor();
+ updateQColor();
+}
+
+void QColorShower::setHsv(int h, int s, int v)
+{
+ if (h < -1 || (uint)s > 255 || (uint)v > 255)
+ return;
+
+ rgbOriginal = false;
+ hue = h; val = v; sat = s;
+ QColor c;
+ c.setHsv(hue, sat, val);
+ curCol = c.rgb();
+
+ hEd->setValue(hue);
+ sEd->setValue(sat);
+ vEd->setValue(val);
+
+ rEd->setValue(qRed(currentColor()));
+ gEd->setValue(qGreen(currentColor()));
+ bEd->setValue(qBlue(currentColor()));
+
+ showCurrentColor();
+ updateQColor();
+}
+
+void QColorShower::retranslateStrings()
+{
+ lblHue->setText(QColorDialog::tr("Hu&e:"));
+ lblSat->setText(QColorDialog::tr("&Sat:"));
+ lblVal->setText(QColorDialog::tr("&Val:"));
+ lblRed->setText(QColorDialog::tr("&Red:"));
+ lblGreen->setText(QColorDialog::tr("&Green:"));
+ lblBlue->setText(QColorDialog::tr("Bl&ue:"));
+ alphaLab->setText(QColorDialog::tr("A&lpha channel:"));
+}
+
+void QColorShower::updateQColor()
+{
+ QColor oldQColor(curQColor);
+ curQColor.setRgba(qRgba(qRed(curCol), qGreen(curCol), qBlue(curCol), qAlpha(curCol)));
+ if (curQColor != oldQColor)
+ emit currentColorChanged(curQColor);
+}
+
+//sets all widgets to display h,s,v
+void QColorDialogPrivate::_q_newHsv(int h, int s, int v)
+{
+ cs->setHsv(h, s, v);
+ cp->setCol(h, s);
+ lp->setCol(h, s, v);
+}
+
+//sets all widgets to display rgb
+void QColorDialogPrivate::setCurrentColor(QRgb rgb)
+{
+ cs->setRgb(rgb);
+ _q_newColorTypedIn(rgb);
+}
+
+// hack; doesn't keep curCol in sync, so use with care
+void QColorDialogPrivate::setCurrentQColor(const QColor &color)
+{
+ Q_Q(QColorDialog);
+ if (cs->curQColor != color) {
+ cs->curQColor = color;
+ emit q->currentColorChanged(color);
+ }
+}
+
+bool QColorDialogPrivate::selectColor(const QColor &col)
+{
+ QRgb color = col.rgb();
+ int i = 0, j = 0;
+ // Check standard colors
+ if (standard) {
+ for (i = 0; i < 6; i++) {
+ for (j = 0; j < 8; j++) {
+ if (color == stdrgb[i + j*6]) {
+ _q_newStandard(i, j);
+ standard->setCurrent(i, j);
+ standard->setSelected(i, j);
+ standard->setFocus();
+ return true;
+ }
+ }
+ }
+ }
+ // Check custom colors
+ if (custom) {
+ for (i = 0; i < 2; i++) {
+ for (j = 0; j < 8; j++) {
+ if (color == cusrgb[i + j*2]) {
+ _q_newCustom(i, j);
+ custom->setCurrent(i, j);
+ custom->setSelected(i, j);
+ custom->setFocus();
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+}
+
+//sets all widgets except cs to display rgb
+void QColorDialogPrivate::_q_newColorTypedIn(QRgb rgb)
+{
+ int h, s, v;
+ rgb2hsv(rgb, h, s, v);
+ cp->setCol(h, s);
+ lp->setCol(h, s, v);
+}
+
+void QColorDialogPrivate::_q_newCustom(int r, int c)
+{
+ int i = r+2*c;
+ setCurrentColor(cusrgb[i]);
+ nextCust = i;
+ if (standard)
+ standard->setSelected(-1,-1);
+}
+
+void QColorDialogPrivate::_q_newStandard(int r, int c)
+{
+ setCurrentColor(stdrgb[r+c*6]);
+ if (custom)
+ custom->setSelected(-1,-1);
+}
+
+void QColorDialogPrivate::init(const QColor &initial)
+{
+ Q_Q(QColorDialog);
+
+ q->setSizeGripEnabled(false);
+ q->setWindowTitle(QColorDialog::tr("Select Color"));
+
+ nextCust = 0;
+ QVBoxLayout *mainLay = new QVBoxLayout(q);
+ // there's nothing in this dialog that benefits from sizing up
+ mainLay->setSizeConstraint(QLayout::SetFixedSize);
+
+ QHBoxLayout *topLay = new QHBoxLayout();
+ mainLay->addLayout(topLay);
+
+ leftLay = 0;
+
+#if defined(Q_OS_WINCE)
+ smallDisplay = true;
+ const int lumSpace = 20;
+#else
+ // small displays (e.g. PDAs) cannot fit the full color dialog,
+ // so just use the color picker.
+ smallDisplay = (qApp->desktop()->width() < 480 || qApp->desktop()->height() < 350);
+ const int lumSpace = topLay->spacing() / 2;
+#endif
+
+ if (!smallDisplay) {
+ leftLay = new QVBoxLayout;
+ topLay->addLayout(leftLay);
+ }
+
+ initRGB();
+
+#ifndef QT_NO_SETTINGS
+ if (!customSet) {
+ QSettings settings(QSettings::UserScope, QLatin1String("Trolltech"));
+ for (int i = 0; i < 2*8; ++i) {
+ QVariant v = settings.value(QLatin1String("Qt/customColors/") + QString::number(i));
+ if (v.isValid()) {
+ QRgb rgb = v.toUInt();
+ cusrgb[i] = rgb;
+ }
+ }
+ }
+#endif
+
+ if (!smallDisplay) {
+ standard = new QColorWell(q, 6, 8, stdrgb);
+ lblBasicColors = new QLabel(q);
+#ifndef QT_NO_SHORTCUT
+ lblBasicColors->setBuddy(standard);
+#endif
+ q->connect(standard, SIGNAL(selected(int,int)), SLOT(_q_newStandard(int,int)));
+ leftLay->addWidget(lblBasicColors);
+ leftLay->addWidget(standard);
+
+#if !defined(Q_OS_WINCE)
+ leftLay->addStretch();
+#endif
+
+ custom = new QColorWell(q, 2, 8, cusrgb);
+ custom->setAcceptDrops(true);
+
+ q->connect(custom, SIGNAL(selected(int,int)), SLOT(_q_newCustom(int,int)));
+ lblCustomColors = new QLabel(q);
+#ifndef QT_NO_SHORTCUT
+ lblCustomColors->setBuddy(custom);
+#endif
+ leftLay->addWidget(lblCustomColors);
+ leftLay->addWidget(custom);
+
+ addCusBt = new QPushButton(q);
+ QObject::connect(addCusBt, SIGNAL(clicked()), q, SLOT(_q_addCustom()));
+ leftLay->addWidget(addCusBt);
+ } else {
+ // better color picker size for small displays
+ pWidth = 150;
+ pHeight = 100;
+ custom = 0;
+ standard = 0;
+ }
+
+ QVBoxLayout *rightLay = new QVBoxLayout;
+ topLay->addLayout(rightLay);
+
+ QHBoxLayout *pickLay = new QHBoxLayout;
+ rightLay->addLayout(pickLay);
+
+ QVBoxLayout *cLay = new QVBoxLayout;
+ pickLay->addLayout(cLay);
+ cp = new QColorPicker(q);
+
+ cp->setFrameStyle(QFrame::Panel + QFrame::Sunken);
+ cLay->addSpacing(lumSpace);
+ cLay->addWidget(cp);
+ cLay->addSpacing(lumSpace);
+
+ lp = new QColorLuminancePicker(q);
+ lp->setFixedWidth(20);
+ pickLay->addWidget(lp);
+
+ QObject::connect(cp, SIGNAL(newCol(int,int)), lp, SLOT(setCol(int,int)));
+ QObject::connect(lp, SIGNAL(newHsv(int,int,int)), q, SLOT(_q_newHsv(int,int,int)));
+
+ rightLay->addStretch();
+
+ cs = new QColorShower(q);
+ QObject::connect(cs, SIGNAL(newCol(QRgb)), q, SLOT(_q_newColorTypedIn(QRgb)));
+ QObject::connect(cs, SIGNAL(currentColorChanged(const QColor&)),
+ q, SIGNAL(currentColorChanged(const QColor&)));
+ rightLay->addWidget(cs);
+
+ buttons = new QDialogButtonBox(q);
+ mainLay->addWidget(buttons);
+
+ ok = buttons->addButton(QDialogButtonBox::Ok);
+ QObject::connect(ok, SIGNAL(clicked()), q, SLOT(accept()));
+ ok->setDefault(true);
+ cancel = buttons->addButton(QDialogButtonBox::Cancel);
+ QObject::connect(cancel, SIGNAL(clicked()), q, SLOT(reject()));
+
+ retranslateStrings();
+
+#ifdef Q_WS_MAC
+ delegate = 0;
+#endif
+
+ q->setCurrentColor(initial);
+}
+
+void QColorDialogPrivate::_q_addCustom()
+{
+ cusrgb[nextCust] = cs->currentColor();
+ if (custom)
+ custom->update();
+ nextCust = (nextCust+1) % 16;
+}
+
+void QColorDialogPrivate::retranslateStrings()
+{
+ if (!smallDisplay) {
+ lblBasicColors->setText(QColorDialog::tr("&Basic colors"));
+ lblCustomColors->setText(QColorDialog::tr("&Custom colors"));
+ addCusBt->setText(QColorDialog::tr("&Add to Custom Colors"));
+ }
+
+ cs->retranslateStrings();
+}
+
+static const Qt::WindowFlags DefaultWindowFlags =
+ Qt::Dialog | Qt::WindowTitleHint | Qt::MSWindowsFixedSizeDialogHint
+ | Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint;
+
+/*!
+ \class QColorDialog
+ \brief The QColorDialog class provides a dialog widget for specifying colors.
+
+ \mainclass
+ \ingroup dialogs
+ \ingroup multimedia
+
+ The color dialog's function is to allow users to choose colors.
+ For example, you might use this in a drawing program to allow the
+ user to set the brush color.
+
+ The static functions provide modal color dialogs.
+ \omit
+ If you require a modeless dialog, use the QColorDialog constructor.
+ \endomit
+
+ The static getColor() function shows the dialog, and allows the
+ user to specify a color. The getRgba() function does the same, but
+ also allows the user to specify a color with an alpha channel
+ (transparency) value.
+
+ The user can store customCount() different custom colors. The
+ custom colors are shared by all color dialogs, and remembered
+ during the execution of the program. Use setCustomColor() to set
+ the custom colors, and use customColor() to get them.
+
+ Additional widgets that allow users to pick colors are available
+ as \l{Qt Solutions}.
+
+ The \l{dialogs/standarddialogs}{Standard Dialogs} example shows
+ how to use QColorDialog as well as other built-in Qt dialogs.
+
+ \image plastique-colordialog.png A color dialog in the Plastique widget style.
+
+ \sa QColor, QFileDialog, QPrintDialog, QFontDialog, {Standard Dialogs Example}
+*/
+
+/*!
+ \since 4.5
+
+ Constructs a color dialog with the given \a parent.
+*/
+QColorDialog::QColorDialog(QWidget *parent)
+ : QDialog(*new QColorDialogPrivate, parent, DefaultWindowFlags)
+{
+ Q_D(QColorDialog);
+ d->init(Qt::white);
+}
+
+/*!
+ \since 4.5
+
+ Constructs a color dialog with the given \a parent and specified
+ \a initial color.
+*/
+QColorDialog::QColorDialog(const QColor &initial, QWidget *parent)
+ : QDialog(*new QColorDialogPrivate, parent, DefaultWindowFlags)
+{
+ Q_D(QColorDialog);
+ d->init(initial);
+}
+
+/*!
+ \property QColorDialog::currentColor
+ \brief the currently selected color in the dialog
+*/
+
+void QColorDialog::setCurrentColor(const QColor &color)
+{
+ Q_D(QColorDialog);
+ d->setCurrentColor(color.rgb());
+ d->selectColor(color.rgb());
+ d->setCurrentAlpha(color.alpha());
+
+#ifdef Q_WS_MAC
+ if (d->delegate)
+ QColorDialogPrivate::setColor(d->delegate, color);
+#endif
+}
+
+QColor QColorDialog::currentColor() const
+{
+ Q_D(const QColorDialog);
+ return d->currentQColor();
+}
+
+
+/*!
+ Returns the color that the user selected by clicking the \gui{OK}
+ or equivalent button.
+
+ \note This color is not always the same as the color held by the
+ \l currentColor property since the user can choose different colors
+ before finally selecting the one to use.
+*/
+QColor QColorDialog::selectedColor() const
+{
+ Q_D(const QColorDialog);
+ return d->selectedQColor;
+}
+
+/*!
+ Sets the given \a option to be enabled if \a on is true;
+ otherwise, clears the given \a option.
+
+ \sa options, testOption()
+*/
+void QColorDialog::setOption(ColorDialogOption option, bool on)
+{
+ Q_D(QColorDialog);
+ if (!(d->opts & option) != !on)
+ setOptions(d->opts ^ option);
+}
+
+/*!
+ \since 4.5
+
+ Returns true if the given \a option is enabled; otherwise, returns
+ false.
+
+ \sa options, setOption()
+*/
+bool QColorDialog::testOption(ColorDialogOption option) const
+{
+ Q_D(const QColorDialog);
+ return (d->opts & option) != 0;
+}
+
+/*!
+ \property QColorDialog::options
+ \brief the various options that affect the look and feel of the dialog
+
+ By default, all options are disabled.
+
+ Options should be set before showing the dialog. Setting them while the
+ dialog is visible is not guaranteed to have an immediate effect on the
+ dialog (depending on the option and on the platform).
+
+ \sa setOption(), testOption()
+*/
+void QColorDialog::setOptions(ColorDialogOptions options)
+{
+ Q_D(QColorDialog);
+
+ ColorDialogOptions changed = (options ^ d->opts);
+ if (!changed)
+ return;
+
+ d->opts = options;
+ d->buttons->setVisible(!(options & NoButtons));
+ d->showAlpha(options & ShowAlphaChannel);
+}
+
+QColorDialog::ColorDialogOptions QColorDialog::options() const
+{
+ Q_D(const QColorDialog);
+ return d->opts;
+}
+
+/*!
+ \enum QColorDialog::ColorDialogOption
+
+ \since 4.5
+
+ This enum specifies various options that affect the look and feel
+ of a color dialog.
+
+ \value ShowAlphaChannel Allow the user to select the alpha component of a color.
+ \value NoButtons Don't display \gui{OK} and \gui{Cancel} buttons. (Useful for "live dialogs".)
+ \value DontUseNativeDialog Use Qt's standard color dialog on the Mac instead of Apple's
+ native color panel.
+
+ \sa options, setOption(), testOption(), windowModality()
+*/
+
+/*!
+ \fn void QColorDialog::currentColorChanged(const QColor &color)
+
+ This signal is emitted whenever the current color changes in the dialog.
+ The current color is specified by \a color.
+
+ \sa color, colorSelected()
+*/
+
+#ifdef Q_WS_MAC
+// can only have one Cocoa color panel active
+bool QColorDialogPrivate::sharedColorPanelAvailable = true;
+#endif
+
+/*!
+ \fn void QColorDialog::colorSelected(const QColor &color);
+
+ This signal is emitted just after the user has clicked \gui{OK} to
+ select a color to use. The chosen color is specified by \a color.
+
+ \sa color, currentColorChanged()
+*/
+
+/*!
+ Changes the visibility of the dialog. If \a visible is true, the dialog
+ is shown; otherwise, it is hidden.
+*/
+void QColorDialog::setVisible(bool visible)
+{
+ Q_D(QColorDialog);
+
+ if (visible){
+ if (testAttribute(Qt::WA_WState_ExplicitShowHide) && !testAttribute(Qt::WA_WState_Hidden))
+ return;
+ } else if (testAttribute(Qt::WA_WState_ExplicitShowHide) && testAttribute(Qt::WA_WState_Hidden))
+ return;
+
+ if (visible)
+ d->selectedQColor = QColor();
+
+#if defined(Q_WS_MAC)
+ if (visible) {
+ if (!d->delegate && QColorDialogPrivate::sharedColorPanelAvailable &&
+ !(testAttribute(Qt::WA_DontShowOnScreen) || (d->opts & DontUseNativeDialog))){
+ d->delegate = QColorDialogPrivate::openCocoaColorPanel(
+ currentColor(), parentWidget(), windowTitle(), options(), d);
+ QColorDialogPrivate::sharedColorPanelAvailable = false;
+ setAttribute(Qt::WA_DontShowOnScreen);
+ }
+ setWindowFlags(windowModality() == Qt::WindowModal ? Qt::Sheet : DefaultWindowFlags);
+ } else {
+ if (d->delegate) {
+ QColorDialogPrivate::closeCocoaColorPanel(d->delegate);
+ d->delegate = 0;
+ QColorDialogPrivate::sharedColorPanelAvailable = true;
+ setAttribute(Qt::WA_DontShowOnScreen, false);
+ }
+ }
+#endif
+
+ QDialog::setVisible(visible);
+}
+
+/*!
+ \overload
+ \since 4.5
+
+ Opens the dialog and connects its accepted() signal to the slot specified
+ by \a receiver and \a member.
+
+ The signal will be disconnected from the slot when the dialog is closed.
+*/
+void QColorDialog::open(QObject *receiver, const char *member)
+{
+ Q_D(QColorDialog);
+ connect(this, SIGNAL(colorSelected(const QColor&)), receiver, member);
+ d->receiverToDisconnectOnClose = receiver;
+ d->memberToDisconnectOnClose = member;
+ QDialog::open();
+}
+
+/*!
+ \fn QColorDialog::open()
+
+ \since 4.5
+ Shows the dialog as a \l{QDialog#Modal Dialogs}{window modal dialog},
+ returning immediately.
+
+ \sa QDialog::open()
+*/
+
+/*!
+ \since 4.5
+
+ Pops up a modal color dialog with the given window \a title (or "Select Color" if none is
+ specified), lets the user choose a color, and returns that color. The color is initially set
+ to \a initial. The dialog is a child of \a parent. It returns an invalid (see
+ QColor::isValid()) color if the user cancels the dialog.
+
+ The \a options argument allows you to customize the dialog.
+*/
+QColor QColorDialog::getColor(const QColor &initial, QWidget *parent, const QString &title,
+ ColorDialogOptions options)
+{
+ QColorDialog dlg(parent);
+ if (!title.isEmpty())
+ dlg.setWindowTitle(title);
+ dlg.setOptions(options);
+ dlg.setCurrentColor(initial);
+ dlg.exec();
+ return dlg.selectedColor();
+}
+
+/*!
+ Pops up a modal color dialog, lets the user choose a color, and
+ returns that color. The color is initially set to \a initial. The
+ dialog is a child of \a parent. It returns an invalid (see
+ QColor::isValid()) color if the user cancels the dialog.
+*/
+
+QColor QColorDialog::getColor(const QColor &initial, QWidget *parent)
+{
+ return getColor(initial, parent, QString(), ColorDialogOptions(0));
+}
+
+
+/*!
+ \obsolete
+
+ Pops up a modal color dialog to allow the user to choose a color
+ and an alpha channel (transparency) value. The color+alpha is
+ initially set to \a initial. The dialog is a child of \a parent.
+
+ If \a ok is non-null, \e *\a ok is set to true if the user clicked
+ \gui{OK}, and to false if the user clicked Cancel.
+
+ If the user clicks Cancel, the \a initial value is returned.
+
+ Use QColorDialog::getColor() instead, passing the
+ QColorDialog::ShowAlphaChannel option.
+*/
+
+QRgb QColorDialog::getRgba(QRgb initial, bool *ok, QWidget *parent)
+{
+ QColor color(getColor(QColor(initial), parent, QString(), ShowAlphaChannel));
+ QRgb result = color.isValid() ? color.rgba() : initial;
+ if (ok)
+ *ok = color.isValid();
+ return result;
+}
+
+/*!
+ Destroys the color dialog.
+*/
+
+QColorDialog::~QColorDialog()
+{
+#ifndef QT_NO_SETTINGS
+ if (!customSet) {
+ QSettings settings(QSettings::UserScope, QLatin1String("Trolltech"));
+ for (int i = 0; i < 2*8; ++i)
+ settings.setValue(QLatin1String("Qt/customColors/") + QString::number(i), cusrgb[i]);
+ }
+#endif
+}
+
+
+/*!
+ \reimp
+*/
+void QColorDialog::changeEvent(QEvent *e)
+{
+ Q_D(QColorDialog);
+ if (e->type() == QEvent::LanguageChange)
+ d->retranslateStrings();
+ QDialog::changeEvent(e);
+}
+
+/*!
+ Closes the dialog and sets its result code to \a result. If this dialog
+ is shown with exec(), done() causes the local event loop to finish,
+ and exec() to return \a result.
+
+ \sa QDialog::done()
+*/
+void QColorDialog::done(int result)
+{
+ Q_D(QColorDialog);
+ QDialog::done(result);
+ if (result == Accepted) {
+ d->selectedQColor = d->currentQColor();
+ emit colorSelected(d->selectedQColor);
+ } else {
+ d->selectedQColor = QColor();
+ }
+ if (d->receiverToDisconnectOnClose) {
+ disconnect(this, SIGNAL(colorSelected(const QColor&)),
+ d->receiverToDisconnectOnClose, d->memberToDisconnectOnClose);
+ d->receiverToDisconnectOnClose = 0;
+ }
+ d->memberToDisconnectOnClose.clear();
+}
+
+QT_END_NAMESPACE
+
+#include "qcolordialog.moc"
+#include "moc_qcolordialog.cpp"
+
+#endif // QT_NO_COLORDIALOG
+
+/*!
+ \fn QColor QColorDialog::getColor(const QColor &init, QWidget *parent, const char *name)
+ \compat
+*/
+
+/*!
+ \fn QRgb QColorDialog::getRgba(QRgb rgba, bool *ok, QWidget *parent, const char *name)
+ \compat
+*/
diff --git a/src/gui/dialogs/qcolordialog.h b/src/gui/dialogs/qcolordialog.h
new file mode 100644
index 0000000000..7ee2e0e772
--- /dev/null
+++ b/src/gui/dialogs/qcolordialog.h
@@ -0,0 +1,150 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QCOLORDIALOG_H
+#define QCOLORDIALOG_H
+
+#include <QtGui/qdialog.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+#ifndef QT_NO_COLORDIALOG
+
+class QColorDialogPrivate;
+
+class Q_GUI_EXPORT QColorDialog : public QDialog
+{
+ Q_OBJECT
+ Q_DECLARE_PRIVATE(QColorDialog)
+ Q_ENUMS(ColorDialogOption)
+ Q_PROPERTY(QColor currentColor READ currentColor WRITE setCurrentColor
+ NOTIFY currentColorChanged)
+ Q_PROPERTY(ColorDialogOptions options READ options WRITE setOptions)
+
+public:
+ enum ColorDialogOption {
+ ShowAlphaChannel = 0x00000001,
+ NoButtons = 0x00000002,
+ DontUseNativeDialog = 0x00000004
+ };
+
+ Q_DECLARE_FLAGS(ColorDialogOptions, ColorDialogOption)
+
+ explicit QColorDialog(QWidget *parent = 0);
+ explicit QColorDialog(const QColor &initial, QWidget *parent = 0);
+ ~QColorDialog();
+
+ void setCurrentColor(const QColor &color);
+ QColor currentColor() const;
+
+ QColor selectedColor() const;
+
+ void setOption(ColorDialogOption option, bool on = true);
+ bool testOption(ColorDialogOption option) const;
+ void setOptions(ColorDialogOptions options);
+ ColorDialogOptions options() const;
+
+#ifdef Q_NO_USING_KEYWORD
+ void open() { QDialog::open(); }
+#else
+ using QDialog::open;
+#endif
+ void open(QObject *receiver, const char *member);
+
+ void setVisible(bool visible);
+
+ // ### Qt 5: merge overloads with title = QString()
+ static QColor getColor(const QColor &initial, QWidget *parent, const QString &title,
+ ColorDialogOptions options = 0);
+ static QColor getColor(const QColor &initial = Qt::white, QWidget *parent = 0);
+
+ // obsolete
+ static QRgb getRgba(QRgb rgba = 0xffffffff, bool *ok = 0, QWidget *parent = 0);
+
+ // ### Qt 5: use QColor in signatures
+ static int customCount();
+ static QRgb customColor(int index);
+ static void setCustomColor(int index, QRgb color);
+ static void setStandardColor(int index, QRgb color);
+
+#ifdef QT3_SUPPORT
+ static QColor getColor(const QColor &init, QWidget *parent, const char *name)
+ { Q_UNUSED(name); return getColor(init, parent); }
+ static QRgb getRgba(QRgb rgba, bool *ok, QWidget *parent, const char *name)
+ { Q_UNUSED(name); return getRgba(rgba, ok, parent); }
+#endif
+
+Q_SIGNALS:
+ void currentColorChanged(const QColor &color);
+ void colorSelected(const QColor &color);
+
+protected:
+ void changeEvent(QEvent *event);
+ void done(int result);
+
+private:
+ Q_DISABLE_COPY(QColorDialog)
+
+ Q_PRIVATE_SLOT(d_func(), void _q_addCustom())
+ Q_PRIVATE_SLOT(d_func(), void _q_newHsv(int h, int s, int v))
+ Q_PRIVATE_SLOT(d_func(), void _q_newColorTypedIn(QRgb rgb))
+ Q_PRIVATE_SLOT(d_func(), void _q_newCustom(int, int))
+ Q_PRIVATE_SLOT(d_func(), void _q_newStandard(int, int))
+#if defined(Q_WS_MAC)
+ Q_PRIVATE_SLOT(d_func(), void _q_macRunNativeAppModalPanel())
+#endif
+
+ friend class QColorShower;
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(QColorDialog::ColorDialogOptions)
+
+#endif // QT_NO_COLORDIALOG
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QCOLORDIALOG_H
diff --git a/src/gui/dialogs/qcolordialog_mac.mm b/src/gui/dialogs/qcolordialog_mac.mm
new file mode 100644
index 0000000000..2556265a59
--- /dev/null
+++ b/src/gui/dialogs/qcolordialog_mac.mm
@@ -0,0 +1,426 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qcolordialog_p.h"
+#if !defined(QT_NO_COLORDIALOG) && defined(Q_WS_MAC)
+#include <qapplication.h>
+#include <qtimer.h>
+#include <qdialogbuttonbox.h>
+#include <private/qapplication_p.h>
+#include <private/qt_mac_p.h>
+#include <qdebug.h>
+#import <AppKit/AppKit.h>
+#import <Foundation/Foundation.h>
+
+#if !CGFLOAT_DEFINED
+typedef float CGFloat; // Should only not be defined on 32-bit platforms
+#endif
+
+QT_USE_NAMESPACE
+
+@class QCocoaColorPanelDelegate;
+
+@interface QCocoaColorPanelDelegate : NSObject {
+ NSColorPanel *mColorPanel;
+ NSView *mStolenContentView;
+ NSButton *mOkButton;
+ NSButton *mCancelButton;
+ QColorDialogPrivate *mPriv;
+ QColor *mQtColor;
+ CGFloat mMinWidth; // currently unused
+ CGFloat mExtraHeight; // currently unused
+ BOOL mHackedPanel;
+}
+- (id)initWithColorPanel:(NSColorPanel *)panel
+ stolenContentView:(NSView *)stolenContentView
+ okButton:(NSButton *)okButton
+ cancelButton:(NSButton *)cancelButton
+ priv:(QColorDialogPrivate *)priv;
+- (BOOL)windowShouldClose:(id)window;
+- (void)windowDidResize:(NSNotification *)notification;
+- (void)colorChanged:(NSNotification *)notification;
+- (void)relayout;
+- (void)onOkClicked;
+- (void)onCancelClicked;
+- (void)updateQtColor;
+- (NSColorPanel *)colorPanel;
+- (QColor)qtColor;
+- (void)finishOffWithCode:(NSInteger)result;
+- (void)cleanUpAfterMyself;
+@end
+
+@implementation QCocoaColorPanelDelegate
+- (id)initWithColorPanel:(NSColorPanel *)panel
+ stolenContentView:(NSView *)stolenContentView
+ okButton:(NSButton *)okButton
+ cancelButton:(NSButton *)cancelButton
+ priv:(QColorDialogPrivate *)priv
+{
+ self = [super init];
+
+ mColorPanel = panel;
+ mStolenContentView = stolenContentView;
+ mOkButton = okButton;
+ mCancelButton = cancelButton;
+ mPriv = priv;
+ mMinWidth = 0.0;
+ mExtraHeight = 0.0;
+ mHackedPanel = (okButton != 0);
+
+ if (mHackedPanel) {
+ [self relayout];
+
+ [okButton setAction:@selector(onOkClicked)];
+ [okButton setTarget:self];
+
+ [cancelButton setAction:@selector(onCancelClicked)];
+ [cancelButton setTarget:self];
+ }
+
+ if (mPriv)
+ [[NSNotificationCenter defaultCenter] addObserver:self
+ selector:@selector(colorChanged:)
+ name:NSColorPanelColorDidChangeNotification
+ object:mColorPanel];
+ mQtColor = new QColor();
+ return self;
+}
+
+- (void)dealloc
+{
+ if (mPriv)
+ [[NSNotificationCenter defaultCenter] removeObserver:self];
+ delete mQtColor;
+ [super dealloc];
+}
+
+- (BOOL)windowShouldClose:(id)window
+{
+ Q_UNUSED(window);
+ if (mHackedPanel) {
+ [self onCancelClicked];
+ } else {
+ [self updateQtColor];
+ [self finishOffWithCode:NSCancelButton];
+ }
+ return true;
+}
+
+- (void)windowDidResize:(NSNotification *)notification
+{
+ Q_UNUSED(notification);
+ if (mHackedPanel)
+ [self relayout];
+}
+
+- (void)colorChanged:(NSNotification *)notification;
+{
+ Q_UNUSED(notification);
+ if (mPriv)
+ [self updateQtColor];
+}
+
+- (void)relayout
+{
+ Q_ASSERT(mHackedPanel);
+
+ NSRect rect = [[mStolenContentView superview] frame];
+
+ // should a priori be kept in sync with qfontdialog_mac.mm
+ const CGFloat ButtonMinWidth = 78.0; // 84.0 for Carbon
+ const CGFloat ButtonMinHeight = 32.0;
+ const CGFloat ButtonSpacing = 0.0;
+ const CGFloat ButtonTopMargin = 0.0;
+ const CGFloat ButtonBottomMargin = 7.0;
+ const CGFloat ButtonSideMargin = 9.0;
+
+ [mOkButton sizeToFit];
+ NSSize okSizeHint = [mOkButton frame].size;
+
+ [mCancelButton sizeToFit];
+ NSSize cancelSizeHint = [mCancelButton frame].size;
+
+ const CGFloat ButtonWidth = qMin(qMax(ButtonMinWidth,
+ qMax(okSizeHint.width, cancelSizeHint.width)),
+ CGFloat((rect.size.width - 2.0 * ButtonSideMargin - ButtonSpacing) * 0.5));
+ const CGFloat ButtonHeight = qMax(ButtonMinHeight,
+ qMax(okSizeHint.height, cancelSizeHint.height));
+
+ NSRect okRect = { { rect.size.width - ButtonSideMargin - ButtonWidth,
+ ButtonBottomMargin },
+ { ButtonWidth, ButtonHeight } };
+ [mOkButton setFrame:okRect];
+ [mOkButton setNeedsDisplay:YES];
+
+ NSRect cancelRect = { { okRect.origin.x - ButtonSpacing - ButtonWidth,
+ ButtonBottomMargin },
+ { ButtonWidth, ButtonHeight } };
+ [mCancelButton setFrame:cancelRect];
+ [mCancelButton setNeedsDisplay:YES];
+
+ const CGFloat Y = ButtonBottomMargin + ButtonHeight + ButtonTopMargin;
+ NSRect stolenCVRect = { { 0.0, Y },
+ { rect.size.width, rect.size.height - Y } };
+ [mStolenContentView setFrame:stolenCVRect];
+ [mStolenContentView setNeedsDisplay:YES];
+
+ [[mStolenContentView superview] setNeedsDisplay:YES];
+ mMinWidth = 2 * ButtonSideMargin + ButtonSpacing + 2 * ButtonWidth;
+ mExtraHeight = Y;
+}
+
+- (void)onOkClicked
+{
+ Q_ASSERT(mHackedPanel);
+ [[mStolenContentView window] close];
+ [self updateQtColor];
+ [self finishOffWithCode:NSOKButton];
+}
+
+- (void)onCancelClicked
+{
+ Q_ASSERT(mHackedPanel);
+ [[mStolenContentView window] close];
+ delete mQtColor;
+ mQtColor = new QColor();
+ [self finishOffWithCode:NSCancelButton];
+}
+
+- (void)updateQtColor
+{
+ delete mQtColor;
+ mQtColor = new QColor();
+ NSColor *color = [mColorPanel color];
+ NSString *colorSpace = [color colorSpaceName];
+ if (colorSpace == NSDeviceCMYKColorSpace) {
+ CGFloat cyan, magenta, yellow, black, alpha;
+ [color getCyan:&cyan magenta:&magenta yellow:&yellow black:&black alpha:&alpha];
+ mQtColor->setCmykF(cyan, magenta, yellow, black, alpha);
+ } else {
+ NSColor *tmpColor;
+ if (colorSpace == NSCalibratedRGBColorSpace || colorSpace == NSDeviceRGBColorSpace) {
+ tmpColor = color;
+ } else {
+ tmpColor = [color colorUsingColorSpaceName:NSCalibratedRGBColorSpace];
+ }
+ CGFloat red, green, blue, alpha;
+ [tmpColor getRed:&red green:&green blue:&blue alpha:&alpha];
+ mQtColor->setRgbF(red, green, blue, alpha);
+ }
+
+ if (mPriv)
+ mPriv->setCurrentQColor(*mQtColor);
+}
+
+- (NSColorPanel *)colorPanel
+{
+ return mColorPanel;
+}
+
+- (QColor)qtColor
+{
+ return *mQtColor;
+}
+
+- (void)finishOffWithCode:(NSInteger)code
+{
+ if (mPriv) {
+ // Finish the QColorDialog as well. But since a call to accept or reject will
+ // close down the QEventLoop found in QDialog, we need to make sure that the
+ // current thread has exited the native dialogs modal session/run loop first.
+ // We ensure this by posting the call:
+ [NSApp stopModalWithCode:code];
+ if (code == NSOKButton)
+ QMetaObject::invokeMethod(mPriv->colorDialog(), "accept", Qt::QueuedConnection);
+ else
+ QMetaObject::invokeMethod(mPriv->colorDialog(), "reject", Qt::QueuedConnection);
+ } else {
+ [NSApp stopModalWithCode:code];
+ }
+}
+
+- (void)cleanUpAfterMyself
+{
+ if (mHackedPanel) {
+ NSView *ourContentView = [mColorPanel contentView];
+
+ // return stolen stuff to its rightful owner
+ [mStolenContentView removeFromSuperview];
+ [mColorPanel setContentView:mStolenContentView];
+
+ [mOkButton release];
+ [mCancelButton release];
+ [ourContentView release];
+ }
+ [mColorPanel setDelegate:nil];
+}
+@end
+
+QT_BEGIN_NAMESPACE
+
+extern void macStartInterceptNSPanelCtor();
+extern void macStopInterceptNSPanelCtor();
+extern NSButton *macCreateButton(const char *text, NSView *superview);
+
+void *QColorDialogPrivate::openCocoaColorPanel(const QColor &initial,
+ QWidget *parent, const QString &title, QColorDialog::ColorDialogOptions options,
+ QColorDialogPrivate *priv)
+{
+ Q_UNUSED(parent); // we would use the parent if only NSColorPanel could be a sheet
+ QMacCocoaAutoReleasePool pool;
+
+ /*
+ The standard Cocoa color panel has no OK or Cancel button and
+ is created as a utility window, whereas we want something like
+ the Carbon color panel. We need to take the following steps:
+
+ 1. Intercept the color panel constructor to turn off the
+ NSUtilityWindowMask flag. This is done by temporarily
+ replacing initWithContentRect:styleMask:backing:defer:
+ in NSPanel by our own method.
+
+ 2. Modify the color panel so that its content view is part
+ of a new content view that contains it as well as two
+ buttons (OK and Cancel).
+
+ 3. Lay out the original content view and the buttons when
+ the color panel is shown and whenever it is resized.
+
+ 4. Clean up after ourselves.
+ */
+
+ bool hackColorPanel = !(options & QColorDialog::NoButtons);
+
+ if (hackColorPanel)
+ macStartInterceptNSPanelCtor();
+ NSColorPanel *colorPanel = [NSColorPanel sharedColorPanel];
+ if (hackColorPanel)
+ macStopInterceptNSPanelCtor();
+
+ [colorPanel setHidesOnDeactivate:false];
+
+ // set up the Cocoa color panel
+ [colorPanel setShowsAlpha:options & QColorDialog::ShowAlphaChannel];
+ [colorPanel setTitle:(NSString*)(CFStringRef)QCFString(title)];
+
+ NSView *stolenContentView = 0;
+ NSButton *okButton = 0;
+ NSButton *cancelButton = 0;
+
+ if (hackColorPanel) {
+ // steal the color panel's contents view
+ stolenContentView = [colorPanel contentView];
+ [stolenContentView retain];
+ [colorPanel setContentView:0];
+
+ // create a new content view and add the stolen one as a subview
+ NSRect frameRect = { { 0.0, 0.0 }, { 0.0, 0.0 } };
+ NSView *ourContentView = [[NSView alloc] initWithFrame:frameRect];
+ [ourContentView addSubview:stolenContentView];
+
+ // create OK and Cancel buttons and add these as subviews
+ okButton = macCreateButton("&OK", ourContentView);
+ cancelButton = macCreateButton("Cancel", ourContentView);
+
+ [colorPanel setContentView:ourContentView];
+ [colorPanel setDefaultButtonCell:[okButton cell]];
+ }
+
+ // create a delegate and set it
+ QCocoaColorPanelDelegate *delegate =
+ [[QCocoaColorPanelDelegate alloc] initWithColorPanel:colorPanel
+ stolenContentView:stolenContentView
+ okButton:okButton
+ cancelButton:cancelButton
+ priv:priv];
+ [colorPanel setDelegate:delegate];
+ setColor(delegate, initial);
+ [colorPanel makeKeyAndOrderFront:colorPanel];
+
+ return delegate;
+}
+
+void QColorDialogPrivate::closeCocoaColorPanel(void *delegate)
+{
+ QMacCocoaAutoReleasePool pool;
+ QCocoaColorPanelDelegate *theDelegate = static_cast<QCocoaColorPanelDelegate *>(delegate);
+ [[theDelegate colorPanel] close];
+ [theDelegate cleanUpAfterMyself];
+ [theDelegate autorelease];
+}
+
+void QColorDialogPrivate::mac_nativeDialogModalHelp()
+{
+ // Do a queued meta-call to open the native modal dialog so it opens after the new
+ // event loop has started to execute (in QDialog::exec). Using a timer rather than
+ // a queued meta call is intentional to ensure that the call is only delivered when
+ // [NSApp run] runs (timers are handeled special in cocoa). If NSApp is not
+ // running (which is the case if e.g a top-most QEventLoop has been
+ // interrupted, and the second-most event loop has not yet been reactivated (regardless
+ // if [NSApp run] is still on the stack)), showing a native modal dialog will fail.
+ if (delegate){
+ Q_Q(QColorDialog);
+ QTimer::singleShot(1, q, SLOT(_q_macRunNativeAppModalPanel()));
+ }
+}
+
+void QColorDialogPrivate::_q_macRunNativeAppModalPanel()
+{
+ QBoolBlocker nativeDialogOnTop(QApplicationPrivate::native_modal_dialog_active);
+ QMacCocoaAutoReleasePool pool;
+ QCocoaColorPanelDelegate *delegateCasted = static_cast<QCocoaColorPanelDelegate *>(delegate);
+ [NSApp runModalForWindow:[delegateCasted colorPanel]];
+}
+
+void QColorDialogPrivate::setColor(void *delegate, const QColor &color)
+{
+ QMacCocoaAutoReleasePool pool;
+ QCocoaColorPanelDelegate *theDelegate = static_cast<QCocoaColorPanelDelegate *>(delegate);
+ NSColor *nsColor = [NSColor colorWithCalibratedRed:color.red() / 255.0
+ green:color.green() / 255.0
+ blue:color.blue() / 255.0
+ alpha:color.alpha() / 255.0];
+ [[theDelegate colorPanel] setColor:nsColor];
+}
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/gui/dialogs/qcolordialog_p.h b/src/gui/dialogs/qcolordialog_p.h
new file mode 100644
index 0000000000..1f819858a2
--- /dev/null
+++ b/src/gui/dialogs/qcolordialog_p.h
@@ -0,0 +1,144 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QCOLORDIALOG_P_H
+#define QCOLORDIALOG_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
+// to version without notice, or even be removed.
+//
+// We mean it.
+//
+//
+
+#include "private/qdialog_p.h"
+#include "qcolordialog.h"
+
+#ifndef QT_NO_COLORDIALOG
+
+QT_BEGIN_NAMESPACE
+
+class QColorLuminancePicker;
+class QColorPicker;
+class QColorShower;
+class QDialogButtonBox;
+class QLabel;
+class QVBoxLayout;
+class QPushButton;
+class QWellArray;
+
+class QColorDialogPrivate : public QDialogPrivate
+{
+ Q_DECLARE_PUBLIC(QColorDialog)
+
+public:
+ void init(const QColor &initial);
+ QRgb currentColor() const;
+ QColor currentQColor() const;
+ void setCurrentColor(QRgb rgb);
+ void setCurrentQColor(const QColor &color);
+ bool selectColor(const QColor &color);
+
+ int currentAlpha() const;
+ void setCurrentAlpha(int a);
+ void showAlpha(bool b);
+ bool isAlphaVisible() const;
+ void retranslateStrings();
+
+ void _q_addCustom();
+
+ void _q_newHsv(int h, int s, int v);
+ void _q_newColorTypedIn(QRgb rgb);
+ void _q_newCustom(int, int);
+ void _q_newStandard(int, int);
+
+ QWellArray *custom;
+ QWellArray *standard;
+
+ QDialogButtonBox *buttons;
+ QVBoxLayout *leftLay;
+ QColorPicker *cp;
+ QColorLuminancePicker *lp;
+ QColorShower *cs;
+ QLabel *lblBasicColors;
+ QLabel *lblCustomColors;
+ QPushButton *ok;
+ QPushButton *cancel;
+ QPushButton *addCusBt;
+ QColor selectedQColor;
+ int nextCust;
+ bool smallDisplay;
+ QColorDialog::ColorDialogOptions opts;
+ QPointer<QObject> receiverToDisconnectOnClose;
+ QByteArray memberToDisconnectOnClose;
+
+#ifdef Q_WS_MAC
+ static void *openCocoaColorPanel(const QColor &initial,
+ QWidget *parent, const QString &title,
+ QColorDialog::ColorDialogOptions options,
+ QColorDialogPrivate *priv = 0);
+ static void closeCocoaColorPanel(void *delegate);
+ static QColor execCocoaColorPanel(const QColor &initial, QWidget *parent,
+ const QString &title, QColorDialog::ColorDialogOptions options);
+ static void setColor(void *delegate, const QColor &color);
+
+ inline void done(int result) { q_func()->done(result); }
+ inline QColorDialog *colorDialog() { return q_func(); }
+
+ void *delegate;
+
+ static bool sharedColorPanelAvailable;
+
+ void _q_macRunNativeAppModalPanel();
+ void mac_nativeDialogModalHelp();
+#endif
+};
+
+#endif // QT_NO_COLORDIALOG
+
+QT_END_NAMESPACE
+
+#endif // QCOLORDIALOG_P_H
diff --git a/src/gui/dialogs/qdialog.cpp b/src/gui/dialogs/qdialog.cpp
new file mode 100644
index 0000000000..14d8162efd
--- /dev/null
+++ b/src/gui/dialogs/qdialog.cpp
@@ -0,0 +1,1159 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qdialog.h"
+
+
+#include "qevent.h"
+#include "qdesktopwidget.h"
+#include "qpushbutton.h"
+#include "qapplication.h"
+#include "qlayout.h"
+#include "qsizegrip.h"
+#include "qwhatsthis.h"
+#include "qmenu.h"
+#include "qcursor.h"
+#include "private/qdialog_p.h"
+#ifndef QT_NO_ACCESSIBILITY
+#include "qaccessible.h"
+#endif
+#if defined(Q_OS_WINCE)
+#include "qt_windows.h"
+#include "qmenubar.h"
+#include "qpointer.h"
+#include "qguifunctions_wince.h"
+extern bool qt_wince_is_mobile(); //defined in qguifunctions_wce.cpp
+extern bool qt_wince_is_smartphone(); //is defined in qguifunctions_wce.cpp
+#elif defined(Q_WS_X11)
+# include "../kernel/qt_x11_p.h"
+#endif
+
+#ifndef SPI_GETSNAPTODEFBUTTON
+# define SPI_GETSNAPTODEFBUTTON 95
+#endif
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QDialog
+ \brief The QDialog class is the base class of dialog windows.
+
+ \ingroup dialogs
+ \ingroup abstractwidgets
+ \mainclass
+
+ A dialog window is a top-level window mostly used for short-term
+ tasks and brief communications with the user. QDialogs may be
+ modal or modeless. QDialogs can
+ provide a \link #return return
+ value\endlink, and they can have \link #default default
+ buttons\endlink. QDialogs can also have a QSizeGrip in their
+ lower-right corner, using setSizeGripEnabled().
+
+ Note that QDialog (an any other widget that has type Qt::Dialog) uses
+ the parent widget slightly differently from other classes in Qt. A
+ dialog is always a top-level widget, but if it has a parent, its
+ default location is centered on top of the parent's top-level widget
+ (if it is not top-level itself). It will also share the parent's
+ taskbar entry.
+
+ Use the overload of the QWidget::setParent() function to change
+ the ownership of a QDialog widget. This function allows you to
+ explicitly set the window flags of the reparented widget; using
+ the overloaded function will clear the window flags specifying the
+ window-system properties for the widget (in particular it will
+ reset the Qt::Dialog flag).
+
+ \section1 Modal Dialogs
+
+ A \bold{modal} dialog is a dialog that blocks input to other
+ visible windows in the same application. Dialogs that are used to
+ request a file name from the user or that are used to set
+ application preferences are usually modal. Dialogs can be
+ \l{Qt::ApplicationModal}{application modal} (the default) or
+ \l{Qt::WindowModal}{window modal}.
+
+ When an application modal dialog is opened, the user must finish
+ interacting with the dialog and close it before they can access
+ any other window in the application. Window modal dialogs only
+ block access to the window associated with the dialog, allowing
+ the user to continue to use other windows in an application.
+
+ The most common way to display a modal dialog is to call its
+ exec() function. When the user closes the dialog, exec() will
+ provide a useful \link #return return value\endlink. Typically,
+ to get the dialog to close and return the appropriate value, we
+ connect a default button, e.g. \gui OK, to the accept() slot and a
+ \gui Cancel button to the reject() slot.
+ Alternatively you can call the done() slot with \c Accepted or
+ \c Rejected.
+
+ An alternative is to call setModal(true) or setWindowModality(),
+ then show(). Unlike exec(), show() returns control to the caller
+ immediately. Calling setModal(true) is especially useful for
+ progress dialogs, where the user must have the ability to interact
+ with the dialog, e.g. to cancel a long running operation. If you
+ use show() and setModal(true) together to perform a long operation,
+ you must call QApplication::processEvents() periodically during
+ processing to enable the user to interact with the dialog. (See
+ QProgressDialog.)
+
+ \section1 Modeless Dialogs
+
+ A \bold{modeless} dialog is a dialog that operates
+ independently of other windows in the same application. Find and
+ replace dialogs in word-processors are often modeless to allow the
+ user to interact with both the application's main window and with
+ the dialog.
+
+ Modeless dialogs are displayed using show(), which returns control
+ to the caller immediately.
+
+ If you invoke the \l{QWidget::show()}{show()} function after hiding
+ a dialog, the dialog will be displayed in its original position. This is
+ because the window manager decides the position for windows that
+ have not been explicitly placed by the programmer. To preserve the
+ position of a dialog that has been moved by the user, save its position
+ in your \l{QWidget::closeEvent()}{closeEvent()} handler and then
+ move the dialog to that position, before showing it again.
+
+ \target default
+ \section1 Default Button
+
+ A dialog's \e default button is the button that's pressed when the
+ user presses Enter (Return). This button is used to signify that
+ the user accepts the dialog's settings and wants to close the
+ dialog. Use QPushButton::setDefault(), QPushButton::isDefault()
+ and QPushButton::autoDefault() to set and control the dialog's
+ default button.
+
+ \target escapekey
+ \section1 Escape Key
+
+ If the user presses the Esc key in a dialog, QDialog::reject()
+ will be called. This will cause the window to close: The \link
+ QCloseEvent close event \endlink cannot be \link
+ QCloseEvent::ignore() ignored \endlink.
+
+ \section1 Extensibility
+
+ Extensibility is the ability to show the dialog in two ways: a
+ partial dialog that shows the most commonly used options, and a
+ full dialog that shows all the options. Typically an extensible
+ dialog will initially appear as a partial dialog, but with a
+ \gui More toggle button. If the user presses the \gui More button down,
+ the dialog is expanded. The \l{Extension Example} shows how to achieve
+ extensible dialogs using Qt.
+
+ \target return
+ \section1 Return Value (Modal Dialogs)
+
+ Modal dialogs are often used in situations where a return value is
+ required, e.g. to indicate whether the user pressed \gui OK or
+ \gui Cancel. A dialog can be closed by calling the accept() or the
+ reject() slots, and exec() will return \c Accepted or \c Rejected
+ as appropriate. The exec() call returns the result of the dialog.
+ The result is also available from result() if the dialog has not
+ been destroyed.
+
+ In order to modify your dialog's close behavior, you can reimplement
+ the functions accept(), reject() or done(). The
+ \l{QWidget::closeEvent()}{closeEvent()} function should only be
+ reimplemented to preserve the dialog's position or to override the
+ standard close or reject behavior.
+
+ \target examples
+ \section1 Code Examples
+
+ A modal dialog:
+
+ \snippet doc/src/snippets/dialogs/dialogs.cpp 1
+
+ A modeless dialog:
+
+ \snippet doc/src/snippets/dialogs/dialogs.cpp 0
+
+ \sa QDialogButtonBox, QTabWidget, QWidget, QProgressDialog,
+ {fowler}{GUI Design Handbook: Dialogs, Standard}, {Extension Example},
+ {Standard Dialogs Example}
+*/
+
+/*! \enum QDialog::DialogCode
+
+ The value returned by a modal dialog.
+
+ \value Accepted
+ \value Rejected
+*/
+
+/*!
+ \property QDialog::sizeGripEnabled
+ \brief whether the size grip is enabled
+
+ A QSizeGrip is placed in the bottom-right corner of the dialog when this
+ property is enabled. By default, the size grip is disabled.
+*/
+
+
+/*!
+ Constructs a dialog with parent \a parent.
+
+ A dialog is always a top-level widget, but if it has a parent, its
+ default location is centered on top of the parent. It will also
+ share the parent's taskbar entry.
+
+ The widget flags \a f are passed on to the QWidget constructor.
+ If, for example, you don't want a What's This button in the title bar
+ of the dialog, pass Qt::WindowTitleHint | Qt::WindowSystemMenuHint in \a f.
+
+ \sa QWidget::setWindowFlags()
+*/
+
+QDialog::QDialog(QWidget *parent, Qt::WindowFlags f)
+ : QWidget(*new QDialogPrivate, parent,
+ f | QFlag((f & Qt::WindowType_Mask) == 0 ? Qt::Dialog : 0))
+{
+#ifdef Q_OS_WINCE
+ if (!qt_wince_is_smartphone())
+ setWindowFlags(windowFlags() | Qt::WindowOkButtonHint | QFlag(qt_wince_is_mobile() ? 0 : Qt::WindowCancelButtonHint));
+#endif
+}
+
+#ifdef QT3_SUPPORT
+/*!
+ \overload
+ \obsolete
+*/
+QDialog::QDialog(QWidget *parent, const char *name, bool modal, Qt::WindowFlags f)
+ : QWidget(*new QDialogPrivate, parent,
+ f
+ | QFlag(modal ? Qt::WShowModal : 0)
+ | QFlag((f & Qt::WindowType_Mask) == 0 ? Qt::Dialog : 0)
+ )
+{
+ setObjectName(QString::fromAscii(name));
+}
+#endif
+
+/*!
+ \overload
+ \internal
+*/
+QDialog::QDialog(QDialogPrivate &dd, QWidget *parent, Qt::WindowFlags f)
+ : QWidget(dd, parent, f | QFlag((f & Qt::WindowType_Mask) == 0 ? Qt::Dialog : 0))
+{
+#ifdef Q_OS_WINCE
+ if (!qt_wince_is_smartphone())
+ setWindowFlags(windowFlags() | Qt::WindowOkButtonHint | QFlag(qt_wince_is_mobile() ? 0 : Qt::WindowCancelButtonHint));
+#endif
+}
+
+/*!
+ Destroys the QDialog, deleting all its children.
+*/
+
+QDialog::~QDialog()
+{
+ // Need to hide() here, as our (to-be) overridden hide()
+ // will not be called in ~QWidget.
+ hide();
+}
+
+/*!
+ \internal
+ This function is called by the push button \a pushButton when it
+ becomes the default button. If \a pushButton is 0, the dialogs
+ default default button becomes the default button. This is what a
+ push button calls when it loses focus.
+*/
+void QDialogPrivate::setDefault(QPushButton *pushButton)
+{
+ Q_Q(QDialog);
+ bool hasMain = false;
+ QList<QPushButton*> list = qFindChildren<QPushButton*>(q);
+ for (int i=0; i<list.size(); ++i) {
+ QPushButton *pb = list.at(i);
+ if (pb->window() == q) {
+ if (pb == mainDef)
+ hasMain = true;
+ if (pb != pushButton)
+ pb->setDefault(false);
+ }
+ }
+ if (!pushButton && hasMain)
+ mainDef->setDefault(true);
+ if (!hasMain)
+ mainDef = pushButton;
+}
+
+/*!
+ \internal
+ This function sets the default default push button to \a pushButton.
+ This function is called by QPushButton::setDefault().
+*/
+void QDialogPrivate::setMainDefault(QPushButton *pushButton)
+{
+ mainDef = 0;
+ setDefault(pushButton);
+}
+
+/*!
+ \internal
+ Hides the default button indicator. Called when non auto-default
+ push button get focus.
+ */
+void QDialogPrivate::hideDefault()
+{
+ Q_Q(QDialog);
+ QList<QPushButton*> list = qFindChildren<QPushButton*>(q);
+ for (int i=0; i<list.size(); ++i) {
+ list.at(i)->setDefault(false);
+ }
+}
+
+void QDialogPrivate::resetModalitySetByOpen()
+{
+ Q_Q(QDialog);
+ if (resetModalityTo != -1 && !q->testAttribute(Qt::WA_SetWindowModality)) {
+ // open() changed the window modality and the user didn't touch it afterwards; restore it
+ q->setWindowModality(Qt::WindowModality(resetModalityTo));
+ q->setAttribute(Qt::WA_SetWindowModality, wasModalitySet);
+#ifdef Q_WS_MAC
+ Q_ASSERT(resetModalityTo != Qt::WindowModal);
+ q->setParent(q->parentWidget(), Qt::Dialog);
+#endif
+ }
+ resetModalityTo = -1;
+}
+
+#ifdef Q_OS_WINCE
+#ifdef Q_OS_WINCE_WM
+void QDialogPrivate::_q_doneAction()
+{
+ //Done...
+ QApplication::postEvent(q_func(), new QEvent(QEvent::OkRequest));
+}
+#endif
+
+/*!
+ \reimp
+*/
+bool QDialog::event(QEvent *e)
+{
+ bool result = QWidget::event(e);
+ if (e->type() == QEvent::OkRequest) {
+ accept();
+ result = true;
+ }
+ return result;
+}
+#endif
+
+/*!
+ Returns the modal dialog's result code, \c Accepted or \c Rejected.
+
+ Do not call this function if the dialog was constructed with the
+ Qt::WA_DeleteOnClose attribute.
+*/
+int QDialog::result() const
+{
+ Q_D(const QDialog);
+ return d->rescode;
+}
+
+/*!
+ \fn void QDialog::setResult(int i)
+
+ Sets the modal dialog's result code to \a i.
+
+ \note We recommend that you use one of the values defined by
+ QDialog::DialogCode.
+*/
+void QDialog::setResult(int r)
+{
+ Q_D(QDialog);
+ d->rescode = r;
+}
+
+/*!
+ \since 4.5
+
+ Shows the dialog as a \l{QDialog#Modal Dialogs}{window modal dialog},
+ returning immediately.
+
+ \sa exec(), show(), result(), setWindowModality()
+*/
+void QDialog::open()
+{
+ Q_D(QDialog);
+
+ Qt::WindowModality modality = windowModality();
+ if (modality != Qt::WindowModal) {
+ d->resetModalityTo = modality;
+ d->wasModalitySet = testAttribute(Qt::WA_SetWindowModality);
+ setWindowModality(Qt::WindowModal);
+ setAttribute(Qt::WA_SetWindowModality, false);
+#ifdef Q_WS_MAC
+ setParent(parentWidget(), Qt::Sheet);
+#endif
+ }
+
+ setResult(0);
+ show();
+}
+
+/*!
+ Shows the dialog as a \l{QDialog#Modal Dialogs}{modal dialog},
+ blocking until the user closes it. The function returns a \l
+ DialogCode result.
+
+ If the dialog is \l{Qt::ApplicationModal}{application modal}, users cannot
+ interact with any other window in the same application until they close
+ the dialog. If the dialog is \l{Qt::ApplicationModal}{window modal}, only
+ interaction with the parent window is blocked while the dialog is open.
+ By default, the dialog is application modal.
+
+ \sa open(), show(), result(), setWindowModality()
+*/
+
+int QDialog::exec()
+{
+ Q_D(QDialog);
+
+ if (d->eventLoop) {
+ qWarning("QDialog::exec: Recursive call detected");
+ return -1;
+ }
+
+ bool deleteOnClose = testAttribute(Qt::WA_DeleteOnClose);
+ setAttribute(Qt::WA_DeleteOnClose, false);
+
+ d->resetModalitySetByOpen();
+
+ bool wasShowModal = testAttribute(Qt::WA_ShowModal);
+ setAttribute(Qt::WA_ShowModal, true);
+ setResult(0);
+
+//On Windows Mobile we create an empty menu to hide the current menu
+#ifdef Q_OS_WINCE_WM
+#ifndef QT_NO_MENUBAR
+ QMenuBar *menuBar = 0;
+ if (!findChild<QMenuBar *>())
+ menuBar = new QMenuBar(this);
+ if (qt_wince_is_smartphone()) {
+ QAction *doneAction = new QAction(tr("Done"), this);
+ menuBar->setDefaultAction(doneAction);
+ connect(doneAction, SIGNAL(triggered()), this, SLOT(_q_doneAction()));
+ }
+#endif //QT_NO_MENUBAR
+#endif //Q_OS_WINCE_WM
+
+ show();
+
+#ifdef Q_WS_MAC
+ d->mac_nativeDialogModalHelp();
+#endif
+
+ QEventLoop eventLoop;
+ d->eventLoop = &eventLoop;
+ QPointer<QDialog> guard = this;
+ (void) eventLoop.exec(QEventLoop::DialogExec);
+ if (guard.isNull())
+ return QDialog::Rejected;
+ d->eventLoop = 0;
+
+ setAttribute(Qt::WA_ShowModal, wasShowModal);
+
+ int res = result();
+ if (deleteOnClose)
+ delete this;
+#ifdef Q_OS_WINCE_WM
+#ifndef QT_NO_MENUBAR
+ else if (menuBar)
+ delete menuBar;
+#endif //QT_NO_MENUBAR
+#endif //Q_OS_WINCE_WM
+ return res;
+}
+
+
+/*!
+ Closes the dialog and sets its result code to \a r. If this dialog
+ is shown with exec(), done() causes the local event loop to finish,
+ and exec() to return \a r.
+
+ As with QWidget::close(), done() deletes the dialog if the
+ Qt::WA_DeleteOnClose flag is set. If the dialog is the application's
+ main widget, the application terminates. If the dialog is the
+ last window closed, the QApplication::lastWindowClosed() signal is
+ emitted.
+
+ \sa accept(), reject(), QApplication::activeWindow(), QApplication::quit()
+*/
+
+void QDialog::done(int r)
+{
+ Q_D(QDialog);
+ hide();
+ setResult(r);
+
+ d->close_helper(QWidgetPrivate::CloseNoEvent);
+ d->resetModalitySetByOpen();
+
+ emit finished(r);
+ if (r == Accepted)
+ emit accepted();
+ else if (r == Rejected)
+ emit rejected();
+}
+
+/*!
+ Hides the modal dialog and sets the result code to \c Accepted.
+
+ \sa reject() done()
+*/
+
+void QDialog::accept()
+{
+ done(Accepted);
+}
+
+/*!
+ Hides the modal dialog and sets the result code to \c Rejected.
+
+ \sa accept() done()
+*/
+
+void QDialog::reject()
+{
+ done(Rejected);
+}
+
+/*! \reimp */
+bool QDialog::eventFilter(QObject *o, QEvent *e)
+{
+ return QWidget::eventFilter(o, e);
+}
+
+/*****************************************************************************
+ Event handlers
+ *****************************************************************************/
+
+#ifndef QT_NO_CONTEXTMENU
+/*! \reimp */
+void QDialog::contextMenuEvent(QContextMenuEvent *e)
+{
+#if defined(QT_NO_WHATSTHIS) || defined(QT_NO_MENU)
+ Q_UNUSED(e);
+#else
+ QWidget *w = childAt(e->pos());
+ if (!w) {
+ w = rect().contains(e->pos()) ? this : 0;
+ if (!w)
+ return;
+ }
+ while (w && w->whatsThis().size() == 0 && !w->testAttribute(Qt::WA_CustomWhatsThis))
+ w = w->isWindow() ? 0 : w->parentWidget();
+ if (w) {
+ QMenu p(this);
+ QAction *wt = p.addAction(tr("What's This?"));
+ if (p.exec(e->globalPos()) == wt) {
+ QHelpEvent e(QEvent::WhatsThis, w->rect().center(),
+ w->mapToGlobal(w->rect().center()));
+ QApplication::sendEvent(w, &e);
+ }
+ }
+#endif
+}
+#endif // QT_NO_CONTEXTMENU
+
+/*! \reimp */
+void QDialog::keyPressEvent(QKeyEvent *e)
+{
+ // Calls reject() if Escape is pressed. Simulates a button
+ // click for the default button if Enter is pressed. Move focus
+ // for the arrow keys. Ignore the rest.
+#ifdef Q_WS_MAC
+ if(e->modifiers() == Qt::ControlModifier && e->key() == Qt::Key_Period) {
+ reject();
+ } else
+#endif
+ if (!e->modifiers() || (e->modifiers() & Qt::KeypadModifier && e->key() == Qt::Key_Enter)) {
+ switch (e->key()) {
+ case Qt::Key_Enter:
+ case Qt::Key_Return: {
+ QList<QPushButton*> list = qFindChildren<QPushButton*>(this);
+ for (int i=0; i<list.size(); ++i) {
+ QPushButton *pb = list.at(i);
+ if (pb->isDefault() && pb->isVisible()) {
+ if (pb->isEnabled())
+ pb->click();
+ return;
+ }
+ }
+ }
+ break;
+ case Qt::Key_Escape:
+ reject();
+ break;
+ case Qt::Key_Up:
+ case Qt::Key_Left:
+ if (focusWidget() &&
+ (focusWidget()->focusPolicy() == Qt::StrongFocus ||
+ focusWidget()->focusPolicy() == Qt::WheelFocus)) {
+ e->ignore();
+ break;
+ }
+ // call ours, since c++ blocks us from calling the one
+ // belonging to focusWidget().
+ focusNextPrevChild(false);
+ break;
+ case Qt::Key_Down:
+ case Qt::Key_Right:
+ if (focusWidget() &&
+ (focusWidget()->focusPolicy() == Qt::StrongFocus ||
+ focusWidget()->focusPolicy() == Qt::WheelFocus)) {
+ e->ignore();
+ break;
+ }
+ focusNextPrevChild(true);
+ break;
+ default:
+ e->ignore();
+ return;
+ }
+ } else {
+ e->ignore();
+ }
+}
+
+/*! \reimp */
+void QDialog::closeEvent(QCloseEvent *e)
+{
+#ifndef QT_NO_WHATSTHIS
+ if (isModal() && QWhatsThis::inWhatsThisMode())
+ QWhatsThis::leaveWhatsThisMode();
+#endif
+ if (isVisible()) {
+ QPointer<QObject> that = this;
+ reject();
+ if (that && isVisible())
+ e->ignore();
+ } else {
+ e->accept();
+ }
+}
+
+/*****************************************************************************
+ Geometry management.
+ *****************************************************************************/
+
+/*! \reimp
+*/
+
+void QDialog::setVisible(bool visible)
+{
+ Q_D(QDialog);
+ if (visible) {
+ if (testAttribute(Qt::WA_WState_ExplicitShowHide) && !testAttribute(Qt::WA_WState_Hidden))
+ return;
+
+ if (!testAttribute(Qt::WA_Moved)) {
+ Qt::WindowStates state = windowState();
+ adjustPosition(parentWidget());
+ setAttribute(Qt::WA_Moved, false); // not really an explicit position
+ if (state != windowState())
+ setWindowState(state);
+ }
+ QWidget::setVisible(visible);
+ showExtension(d->doShowExtension);
+ QWidget *fw = window()->focusWidget();
+ if (!fw)
+ fw = this;
+
+ /*
+ The following block is to handle a special case, and does not
+ really follow propper logic in concern of autoDefault and TAB
+ order. However, it's here to ease usage for the users. If a
+ dialog has a default QPushButton, and first widget in the TAB
+ order also is a QPushButton, then we give focus to the main
+ default QPushButton. This simplifies code for the developers,
+ and actually catches most cases... If not, then they simply
+ have to use [widget*]->setFocus() themselves...
+ */
+ if (d->mainDef && fw->focusPolicy() == Qt::NoFocus) {
+ QWidget *first = fw;
+ while ((first = first->nextInFocusChain()) != fw && first->focusPolicy() == Qt::NoFocus)
+ ;
+ if (first != d->mainDef && qobject_cast<QPushButton*>(first))
+ d->mainDef->setFocus();
+ }
+ if (!d->mainDef && isWindow()) {
+ QWidget *w = fw;
+ while ((w = w->nextInFocusChain()) != fw) {
+ QPushButton *pb = qobject_cast<QPushButton *>(w);
+ if (pb && pb->autoDefault() && pb->focusPolicy() != Qt::NoFocus) {
+ pb->setDefault(true);
+ break;
+ }
+ }
+ }
+ if (fw && !fw->hasFocus()) {
+ QFocusEvent e(QEvent::FocusIn, Qt::TabFocusReason);
+ QApplication::sendEvent(fw, &e);
+ }
+
+#ifndef QT_NO_ACCESSIBILITY
+ QAccessible::updateAccessibility(this, 0, QAccessible::DialogStart);
+#endif
+
+ } else {
+ if (testAttribute(Qt::WA_WState_ExplicitShowHide) && testAttribute(Qt::WA_WState_Hidden))
+ return;
+
+#ifndef QT_NO_ACCESSIBILITY
+ if (isVisible())
+ QAccessible::updateAccessibility(this, 0, QAccessible::DialogEnd);
+#endif
+
+ // Reimplemented to exit a modal event loop when the dialog is hidden.
+ QWidget::setVisible(visible);
+ if (d->eventLoop)
+ d->eventLoop->exit();
+ }
+#ifdef Q_WS_WIN
+ if (d->mainDef && isActiveWindow()) {
+ BOOL snapToDefault = false;
+ if ( QT_WA_INLINE( SystemParametersInfo(SPI_GETSNAPTODEFBUTTON, 0, &snapToDefault, 0) ,
+ SystemParametersInfoA(SPI_GETSNAPTODEFBUTTON, 0, &snapToDefault, 0) )) {
+ if (snapToDefault)
+ QCursor::setPos(d->mainDef->mapToGlobal(d->mainDef->rect().center()));
+ }
+ }
+#endif
+}
+
+/*!\reimp */
+void QDialog::showEvent(QShowEvent *event)
+{
+ if (!event->spontaneous() && !testAttribute(Qt::WA_Moved)) {
+ Qt::WindowStates state = windowState();
+ adjustPosition(parentWidget());
+ setAttribute(Qt::WA_Moved, false); // not really an explicit position
+ if (state != windowState())
+ setWindowState(state);
+ }
+}
+
+/*! \internal */
+void QDialog::adjustPosition(QWidget* w)
+{
+#ifdef Q_WS_X11
+ // if the WM advertises that it will place the windows properly for us, let it do it :)
+ if (X11->isSupportedByWM(ATOM(_NET_WM_FULL_PLACEMENT)))
+ return;
+#endif
+
+ QPoint p(0, 0);
+ int extraw = 0, extrah = 0, scrn = 0;
+ if (w)
+ w = w->window();
+ QRect desk;
+ if (w) {
+ scrn = QApplication::desktop()->screenNumber(w);
+ } else if (QApplication::desktop()->isVirtualDesktop()) {
+ scrn = QApplication::desktop()->screenNumber(QCursor::pos());
+ } else {
+ scrn = QApplication::desktop()->screenNumber(this);
+ }
+ desk = QApplication::desktop()->availableGeometry(scrn);
+
+ QWidgetList list = QApplication::topLevelWidgets();
+ for (int i = 0; (extraw == 0 || extrah == 0) && i < list.size(); ++i) {
+ QWidget * current = list.at(i);
+ if (current->isVisible()) {
+ int framew = current->geometry().x() - current->x();
+ int frameh = current->geometry().y() - current->y();
+
+ extraw = qMax(extraw, framew);
+ extrah = qMax(extrah, frameh);
+ }
+ }
+
+ // sanity check for decoration frames. With embedding, we
+ // might get extraordinary values
+ if (extraw == 0 || extrah == 0 || extraw >= 10 || extrah >= 40) {
+ extrah = 40;
+ extraw = 10;
+ }
+
+
+ if (w) {
+ // Use mapToGlobal rather than geometry() in case w might
+ // be embedded in another application
+ QPoint pp = w->mapToGlobal(QPoint(0,0));
+ p = QPoint(pp.x() + w->width()/2,
+ pp.y() + w->height()/ 2);
+ } else {
+ // p = middle of the desktop
+ p = QPoint(desk.x() + desk.width()/2, desk.y() + desk.height()/2);
+ }
+
+ // p = origin of this
+ p = QPoint(p.x()-width()/2 - extraw,
+ p.y()-height()/2 - extrah);
+
+
+ if (p.x() + extraw + width() > desk.x() + desk.width())
+ p.setX(desk.x() + desk.width() - width() - extraw);
+ if (p.x() < desk.x())
+ p.setX(desk.x());
+
+ if (p.y() + extrah + height() > desk.y() + desk.height())
+ p.setY(desk.y() + desk.height() - height() - extrah);
+ if (p.y() < desk.y())
+ p.setY(desk.y());
+
+ move(p);
+}
+
+
+/*!
+ \obsolete
+
+ If \a orientation is Qt::Horizontal, the extension will be displayed
+ to the right of the dialog's main area. If \a orientation is
+ Qt::Vertical, the extension will be displayed below the dialog's main
+ area.
+
+ Instead of using this functionality, we recommend that you simply call
+ show() or hide() on the part of the dialog that you want to use as an
+ extension. See the \l{Extension Example} for details.
+
+ \sa setExtension()
+*/
+void QDialog::setOrientation(Qt::Orientation orientation)
+{
+ Q_D(QDialog);
+ d->orientation = orientation;
+}
+
+/*!
+ \obsolete
+
+ Returns the dialog's extension orientation.
+
+ Instead of using this functionality, we recommend that you simply call
+ show() or hide() on the part of the dialog that you want to use as an
+ extension. See the \l{Extension Example} for details.
+
+ \sa extension()
+*/
+Qt::Orientation QDialog::orientation() const
+{
+ Q_D(const QDialog);
+ return d->orientation;
+}
+
+/*!
+ \obsolete
+
+ Sets the widget, \a extension, to be the dialog's extension,
+ deleting any previous extension. The dialog takes ownership of the
+ extension. Note that if 0 is passed any existing extension will be
+ deleted. This function must only be called while the dialog is hidden.
+
+ Instead of using this functionality, we recommend that you simply call
+ show() or hide() on the part of the dialog that you want to use as an
+ extension. See the \l{Extension Example} for details.
+
+ \sa showExtension(), setOrientation()
+*/
+void QDialog::setExtension(QWidget* extension)
+{
+ Q_D(QDialog);
+ delete d->extension;
+ d->extension = extension;
+
+ if (!extension)
+ return;
+
+ if (extension->parentWidget() != this)
+ extension->setParent(this);
+ extension->hide();
+}
+
+/*!
+ \obsolete
+
+ Returns the dialog's extension or 0 if no extension has been
+ defined.
+
+ Instead of using this functionality, we recommend that you simply call
+ show() or hide() on the part of the dialog that you want to use as an
+ extension. See the \l{Extension Example} for details.
+
+ \sa showExtension(), setOrientation()
+*/
+QWidget* QDialog::extension() const
+{
+ Q_D(const QDialog);
+ return d->extension;
+}
+
+
+/*!
+ \obsolete
+
+ If \a showIt is true, the dialog's extension is shown; otherwise the
+ extension is hidden.
+
+ Instead of using this functionality, we recommend that you simply call
+ show() or hide() on the part of the dialog that you want to use as an
+ extension. See the \l{Extension Example} for details.
+
+ \sa show(), setExtension(), setOrientation()
+*/
+void QDialog::showExtension(bool showIt)
+{
+ Q_D(QDialog);
+ d->doShowExtension = showIt;
+ if (!d->extension)
+ return;
+ if (!testAttribute(Qt::WA_WState_Visible))
+ return;
+ if (d->extension->isVisible() == showIt)
+ return;
+
+ if (showIt) {
+ d->size = size();
+ d->min = minimumSize();
+ d->max = maximumSize();
+ if (layout())
+ layout()->setEnabled(false);
+ QSize s(d->extension->sizeHint()
+ .expandedTo(d->extension->minimumSize())
+ .boundedTo(d->extension->maximumSize()));
+ if (d->orientation == Qt::Horizontal) {
+ int h = qMax(height(), s.height());
+ d->extension->setGeometry(width(), 0, s.width(), h);
+ setFixedSize(width() + s.width(), h);
+ } else {
+ int w = qMax(width(), s.width());
+ d->extension->setGeometry(0, height(), w, s.height());
+ setFixedSize(w, height() + s.height());
+ }
+ d->extension->show();
+#ifndef QT_NO_SIZEGRIP
+ const bool sizeGripEnabled = isSizeGripEnabled();
+ setSizeGripEnabled(false);
+ d->sizeGripEnabled = sizeGripEnabled;
+#endif
+ } else {
+ d->extension->hide();
+ // workaround for CDE window manager that won't shrink with (-1,-1)
+ setMinimumSize(d->min.expandedTo(QSize(1, 1)));
+ setMaximumSize(d->max);
+ resize(d->size);
+ if (layout())
+ layout()->setEnabled(true);
+#ifndef QT_NO_SIZEGRIP
+ setSizeGripEnabled(d->sizeGripEnabled);
+#endif
+ }
+}
+
+
+/*! \reimp */
+QSize QDialog::sizeHint() const
+{
+ Q_D(const QDialog);
+ if (d->extension) {
+ if (d->orientation == Qt::Horizontal)
+ return QSize(QWidget::sizeHint().width(),
+ qMax(QWidget::sizeHint().height(),d->extension->sizeHint().height()));
+ else
+ return QSize(qMax(QWidget::sizeHint().width(), d->extension->sizeHint().width()),
+ QWidget::sizeHint().height());
+ }
+
+ return QWidget::sizeHint();
+}
+
+
+/*! \reimp */
+QSize QDialog::minimumSizeHint() const
+{
+ Q_D(const QDialog);
+ if (d->extension) {
+ if (d->orientation == Qt::Horizontal)
+ return QSize(QWidget::minimumSizeHint().width(),
+ qMax(QWidget::minimumSizeHint().height(), d->extension->minimumSizeHint().height()));
+ else
+ return QSize(qMax(QWidget::minimumSizeHint().width(), d->extension->minimumSizeHint().width()),
+ QWidget::minimumSizeHint().height());
+ }
+
+ return QWidget::minimumSizeHint();
+}
+
+/*!
+ \property QDialog::modal
+ \brief whether show() should pop up the dialog as modal or modeless
+
+ By default, this property is false and show() pops up the dialog
+ as modeless. Setting his property to true is equivalent to setting
+ QWidget::windowModality to Qt::ApplicationModal.
+
+ exec() ignores the value of this property and always pops up the
+ dialog as modal.
+
+ \sa QWidget::windowModality, show(), exec()
+*/
+
+void QDialog::setModal(bool modal)
+{
+ setAttribute(Qt::WA_ShowModal, modal);
+}
+
+
+bool QDialog::isSizeGripEnabled() const
+{
+#ifndef QT_NO_SIZEGRIP
+ Q_D(const QDialog);
+ return !!d->resizer;
+#else
+ return false;
+#endif
+}
+
+
+void QDialog::setSizeGripEnabled(bool enabled)
+{
+#ifdef QT_NO_SIZEGRIP
+ Q_UNUSED(enabled);
+#else
+ Q_D(QDialog);
+#ifndef QT_NO_SIZEGRIP
+ d->sizeGripEnabled = enabled;
+ if (enabled && d->doShowExtension)
+ return;
+#endif
+ if (!enabled != !d->resizer) {
+ if (enabled) {
+ d->resizer = new QSizeGrip(this);
+ // adjustSize() processes all events, which is suboptimal
+ d->resizer->resize(d->resizer->sizeHint());
+ if (isRightToLeft())
+ d->resizer->move(rect().bottomLeft() -d->resizer->rect().bottomLeft());
+ else
+ d->resizer->move(rect().bottomRight() -d->resizer->rect().bottomRight());
+ d->resizer->raise();
+ d->resizer->show();
+ } else {
+ delete d->resizer;
+ d->resizer = 0;
+ }
+ }
+#endif //QT_NO_SIZEGRIP
+}
+
+
+
+/*! \reimp */
+void QDialog::resizeEvent(QResizeEvent *)
+{
+#ifndef QT_NO_SIZEGRIP
+ Q_D(QDialog);
+ if (d->resizer) {
+ if (isRightToLeft())
+ d->resizer->move(rect().bottomLeft() -d->resizer->rect().bottomLeft());
+ else
+ d->resizer->move(rect().bottomRight() -d->resizer->rect().bottomRight());
+ d->resizer->raise();
+ }
+#endif
+}
+
+/*! \fn void QDialog::finished(int result)
+ \since 4.1
+
+ This signal is emitted when the dialog's \a result code has been
+ set, either by the user or by calling done(), accept(), or
+ reject().
+
+ Note that this signal is \e not emitted when hiding the dialog
+ with hide() or setVisible(false). This includes deleting the
+ dialog while it is visible.
+
+ \sa accepted(), rejected()
+*/
+
+/*! \fn void QDialog::accepted()
+ \since 4.1
+
+ This signal is emitted when the dialog has been accepted either by
+ the user or by calling accept() or done() with the
+ QDialog::Accepted argument.
+
+ Note that this signal is \e not emitted when hiding the dialog
+ with hide() or setVisible(false). This includes deleting the
+ dialog while it is visible.
+
+ \sa finished(), rejected()
+*/
+
+/*! \fn void QDialog::rejected()
+ \since 4.1
+
+ This signal is emitted when the dialog has been rejected either by
+ the user or by calling reject() or done() with the
+ QDialog::Rejected argument.
+
+ Note that this signal is \e not emitted when hiding the dialog
+ with hide() or setVisible(false). This includes deleting the
+ dialog while it is visible.
+
+ \sa finished(), accepted()
+*/
+
+QT_END_NAMESPACE
+#include "moc_qdialog.cpp"
diff --git a/src/gui/dialogs/qdialog.h b/src/gui/dialogs/qdialog.h
new file mode 100644
index 0000000000..ee1997af41
--- /dev/null
+++ b/src/gui/dialogs/qdialog.h
@@ -0,0 +1,136 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QDIALOG_H
+#define QDIALOG_H
+
+#include <QtGui/qwidget.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+class QPushButton;
+class QDialogPrivate;
+
+class Q_GUI_EXPORT QDialog : public QWidget
+{
+ Q_OBJECT
+ friend class QPushButton;
+
+ Q_PROPERTY(bool sizeGripEnabled READ isSizeGripEnabled WRITE setSizeGripEnabled)
+ Q_PROPERTY(bool modal READ isModal WRITE setModal)
+
+public:
+ explicit QDialog(QWidget *parent = 0, Qt::WindowFlags f = 0);
+#ifdef QT3_SUPPORT
+ QT3_SUPPORT_CONSTRUCTOR QDialog(QWidget *parent, const char *name, bool modal = false,
+ Qt::WindowFlags f = 0);
+#endif
+ ~QDialog();
+
+ enum DialogCode { Rejected, Accepted };
+
+ int result() const;
+
+ void setVisible(bool visible);
+
+ void setOrientation(Qt::Orientation orientation);
+ Qt::Orientation orientation() const;
+
+ void setExtension(QWidget* extension);
+ QWidget* extension() const;
+
+ QSize sizeHint() const;
+ QSize minimumSizeHint() const;
+
+ void setSizeGripEnabled(bool);
+ bool isSizeGripEnabled() const;
+
+ void setModal(bool modal);
+ void setResult(int r);
+
+Q_SIGNALS:
+ void finished(int result);
+ void accepted();
+ void rejected();
+
+public Q_SLOTS:
+ void open();
+ int exec();
+ virtual void done(int);
+ virtual void accept();
+ virtual void reject();
+
+ void showExtension(bool);
+
+protected:
+ QDialog(QDialogPrivate &, QWidget *parent, Qt::WindowFlags f = 0);
+
+#ifdef Q_OS_WINCE
+ bool event(QEvent *e);
+#endif
+ void keyPressEvent(QKeyEvent *);
+ void closeEvent(QCloseEvent *);
+ void showEvent(QShowEvent *);
+ void resizeEvent(QResizeEvent *);
+#ifndef QT_NO_CONTEXTMENU
+ void contextMenuEvent(QContextMenuEvent *);
+#endif
+ bool eventFilter(QObject *, QEvent *);
+ void adjustPosition(QWidget*);
+
+private:
+ Q_DECLARE_PRIVATE(QDialog)
+ Q_DISABLE_COPY(QDialog)
+
+#ifdef Q_OS_WINCE_WM
+ Q_PRIVATE_SLOT(d_func(), void _q_doneAction())
+#endif
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QDIALOG_H
diff --git a/src/gui/dialogs/qdialog_p.h b/src/gui/dialogs/qdialog_p.h
new file mode 100644
index 0000000000..81a7b19dff
--- /dev/null
+++ b/src/gui/dialogs/qdialog_p.h
@@ -0,0 +1,113 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QDIALOG_P_H
+#define QDIALOG_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"
+#include "QtCore/qeventloop.h"
+#include "QtCore/qpointer.h"
+#include "QtGui/qdialog.h"
+#include "QtGui/qpushbutton.h"
+
+QT_BEGIN_NAMESPACE
+
+class QSizeGrip;
+
+class QDialogPrivate : public QWidgetPrivate
+{
+ Q_DECLARE_PUBLIC(QDialog)
+public:
+
+ QDialogPrivate()
+ : mainDef(0), orientation(Qt::Horizontal),extension(0), doShowExtension(false),
+#ifndef QT_NO_SIZEGRIP
+ resizer(0),
+ sizeGripEnabled(false),
+#endif
+ rescode(0), resetModalityTo(-1), wasModalitySet(true), eventLoop(0)
+ {}
+
+ QPointer<QPushButton> mainDef;
+ Qt::Orientation orientation;
+ QWidget *extension;
+ bool doShowExtension;
+ QSize size, min, max;
+#ifndef QT_NO_SIZEGRIP
+ QSizeGrip *resizer;
+ bool sizeGripEnabled;
+#endif
+ QPoint lastRMBPress;
+
+ void setDefault(QPushButton *);
+ void setMainDefault(QPushButton *);
+ void hideDefault();
+ void resetModalitySetByOpen();
+
+#ifdef Q_OS_WINCE_WM
+ void _q_doneAction();
+#endif
+
+#ifdef Q_WS_MAC
+ virtual void mac_nativeDialogModalHelp(){};
+#endif
+
+ int rescode;
+ int resetModalityTo;
+ bool wasModalitySet;
+
+ QPointer<QEventLoop> eventLoop;
+};
+
+QT_END_NAMESPACE
+
+#endif // QDIALOG_P_H
diff --git a/src/gui/dialogs/qdialogsbinarycompat_win.cpp b/src/gui/dialogs/qdialogsbinarycompat_win.cpp
new file mode 100644
index 0000000000..5c0efd90f3
--- /dev/null
+++ b/src/gui/dialogs/qdialogsbinarycompat_win.cpp
@@ -0,0 +1,137 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <qglobal.h>
+
+// ### Qt 5: eliminate this file
+
+/*
+ This is evil. MSVC doesn't let us remove private symbols, nor change their
+ visibility; yet there are some symbols we really needed to make public, e.g.,
+ ~QColorDialog(), and then there were some totally needless symbols in our
+ header files, e.g., setSelectedAlpha(). So we define a new version of
+ QColorDialog & Co. with only the private symbols that we removed from the
+ public header files. The friends are there only to prevent potential compiler
+ warnings.
+
+ It would have been nicer to export the missing symbols as mangled "C" symbols
+ instead but unfortunately MSVC uses out-of-reach characters like @ and . in
+ their mangled C++ symbols.
+*/
+
+#if QT_VERSION < 0x050000 && defined(Q_CC_MSVC)
+
+QT_BEGIN_NAMESPACE
+
+#include <QtGui/QColor>
+#include <QtGui/QFont>
+
+class QColorDialogPrivate;
+class QFontDialogPrivate;
+class QInputDialogPrivate;
+class QWidget;
+
+class Q_GUI_EXPORT QColorDialog
+{
+private:
+ explicit QColorDialog(QWidget *, bool);
+ ~QColorDialog();
+
+ void setColor(const QColor &);
+ QColor color() const;
+ bool selectColor(const QColor &);
+ void setSelectedAlpha(int);
+ int selectedAlpha() const;
+
+ friend class QColorDialogPrivate;
+};
+
+QColorDialog::QColorDialog(QWidget *, bool) {}
+QColorDialog::~QColorDialog() {}
+void QColorDialog::setColor(const QColor &) {}
+QColor QColorDialog::color() const { return QColor(); }
+bool QColorDialog::selectColor(const QColor &) { return false; }
+void QColorDialog::setSelectedAlpha(int) {}
+int QColorDialog::selectedAlpha() const { return 0; }
+
+class Q_GUI_EXPORT QFontDialog
+{
+private:
+ explicit QFontDialog(QWidget *, bool, Qt::WindowFlags);
+ ~QFontDialog();
+
+ QFont font() const;
+ void setFont(const QFont &);
+ void updateFamilies();
+ void updateStyles();
+ void updateSizes();
+
+ static QFont getFont(bool *, const QFont *, QWidget *);
+
+ friend class QFontDialogPrivate;
+};
+
+QFontDialog::QFontDialog(QWidget *, bool, Qt::WindowFlags) {}
+QFontDialog::~QFontDialog() {}
+QFont QFontDialog::font() const { return QFont(); }
+void QFontDialog::setFont(const QFont &) { }
+void QFontDialog::updateFamilies() {}
+void QFontDialog::updateStyles() {}
+void QFontDialog::updateSizes() {}
+QFont QFontDialog::getFont(bool *, const QFont *, QWidget *) { return QFont(); }
+
+class Q_GUI_EXPORT QInputDialog
+{
+private:
+ enum Type { LineEdit, SpinBox, DoubleSpinBox, ComboBox, EditableComboBox };
+
+ QInputDialog(const QString &, QWidget *, Type, Qt::WindowFlags);
+ QInputDialog(const QString &, const QString &, QWidget *, QWidget *, Qt::WindowFlags);
+ ~QInputDialog();
+};
+
+QInputDialog::QInputDialog(const QString &, QWidget *, Type, Qt::WindowFlags) {}
+QInputDialog::QInputDialog(const QString &, const QString &, QWidget *, QWidget *, Qt::WindowFlags) {}
+QInputDialog::~QInputDialog() {}
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/gui/dialogs/qerrormessage.cpp b/src/gui/dialogs/qerrormessage.cpp
new file mode 100644
index 0000000000..f79c6b2ad4
--- /dev/null
+++ b/src/gui/dialogs/qerrormessage.cpp
@@ -0,0 +1,403 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qerrormessage.h"
+
+#ifndef QT_NO_ERRORMESSAGE
+
+#include "qapplication.h"
+#include "qcheckbox.h"
+#include "qlabel.h"
+#include "qlayout.h"
+#include "qmessagebox.h"
+#include "qpushbutton.h"
+#include "qstringlist.h"
+#include "qtextedit.h"
+#include "qdialog_p.h"
+#include "qpixmap.h"
+#include "qmetaobject.h"
+#include "qthread.h"
+#include "qqueue.h"
+#include "qset.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#ifdef Q_OS_WINCE
+extern bool qt_wince_is_mobile(); //defined in qguifunctions_wince.cpp
+extern bool qt_wince_is_high_dpi(); //defined in qguifunctions_wince.cpp
+
+#include "qguifunctions_wince.h"
+#endif
+
+QT_BEGIN_NAMESPACE
+
+class QErrorMessagePrivate : public QDialogPrivate
+{
+ Q_DECLARE_PUBLIC(QErrorMessage)
+public:
+ QPushButton * ok;
+ QCheckBox * again;
+ QTextEdit * errors;
+ QLabel * icon;
+ QQueue<QPair<QString, QString> > pending;
+ QSet<QString> doNotShow;
+ QSet<QString> doNotShowType;
+ QString currentMessage;
+ QString currentType;
+
+ bool nextPending();
+ void retranslateStrings();
+};
+
+class QErrorMessageTextView : public QTextEdit
+{
+public:
+ QErrorMessageTextView(QWidget *parent)
+ : QTextEdit(parent) { setReadOnly(true); }
+
+ virtual QSize minimumSizeHint() const;
+ virtual QSize sizeHint() const;
+};
+
+QSize QErrorMessageTextView::minimumSizeHint() const
+{
+#ifdef Q_OS_WINCE
+ if (qt_wince_is_mobile())
+ if (qt_wince_is_high_dpi())
+ return QSize(200, 200);
+ else
+ return QSize(100, 100);
+ else
+ return QSize(70, 70);
+#else
+ return QSize(50, 50);
+#endif
+}
+
+QSize QErrorMessageTextView::sizeHint() const
+{
+#ifdef Q_OS_WINCE
+ if (qt_wince_is_mobile())
+ if (qt_wince_is_high_dpi())
+ return QSize(400, 200);
+ else
+ return QSize(320, 120);
+ else
+ return QSize(300, 100);
+#else
+ return QSize(250, 75);
+#endif
+}
+
+/*!
+ \class QErrorMessage
+
+ \brief The QErrorMessage class provides an error message display dialog.
+
+ \ingroup dialogs
+ \ingroup misc
+
+ An error message widget consists of a text label and a checkbox. The
+ checkbox lets the user control whether the same error message will be
+ displayed again in the future, typically displaying the text,
+ "Show this message again" translated into the appropriate local
+ language.
+
+ For production applications, the class can be used to display messages which
+ the user only needs to see once. To use QErrorMessage like this, you create
+ the dialog in the usual way, and show it by calling the showMessage() slot or
+ connecting signals to it.
+
+ The static qtHandler() function installs a message handler
+ using qInstallMsgHandler() and creates a QErrorMessage that displays
+ qDebug(), qWarning() and qFatal() messages. This is most useful in
+ environments where no console is available to display warnings and
+ error messages.
+
+ In both cases QErrorMessage will queue pending messages and display
+ them in order, with each new message being shown as soon as the user
+ has accepted the previous message. Once the user has specified that a
+ message is not to be shown again it is automatically skipped, and the
+ dialog will show the next appropriate message in the queue.
+
+ The \l{dialogs/standarddialogs}{Standard Dialogs} example shows
+ how to use QErrorMessage as well as other built-in Qt dialogs.
+
+ \img qerrormessage.png
+
+ \sa QMessageBox, QStatusBar::showMessage(), {Standard Dialogs Example}
+*/
+
+static QErrorMessage * qtMessageHandler = 0;
+
+static void deleteStaticcQErrorMessage() // post-routine
+{
+ if (qtMessageHandler) {
+ delete qtMessageHandler;
+ qtMessageHandler = 0;
+ }
+}
+
+static bool metFatal = false;
+
+static void jump(QtMsgType t, const char * m)
+{
+ if (!qtMessageHandler)
+ return;
+
+ QString rich;
+
+ switch (t) {
+ case QtDebugMsg:
+ default:
+ rich = QErrorMessage::tr("Debug Message:");
+ break;
+ case QtWarningMsg:
+ rich = QErrorMessage::tr("Warning:");
+ break;
+ case QtFatalMsg:
+ rich = QErrorMessage::tr("Fatal Error:");
+ }
+ rich = QString::fromLatin1("<p><b>%1</b></p>").arg(rich);
+ rich += Qt::convertFromPlainText(QLatin1String(m), Qt::WhiteSpaceNormal);
+
+ // ### work around text engine quirk
+ if (rich.endsWith(QLatin1String("</p>")))
+ rich.chop(4);
+
+ if (!metFatal) {
+ if (QThread::currentThread() == qApp->thread()) {
+ qtMessageHandler->showMessage(rich);
+ } else {
+ QMetaObject::invokeMethod(qtMessageHandler,
+ "showMessage",
+ Qt::QueuedConnection,
+ Q_ARG(QString, rich));
+ }
+ metFatal = (t == QtFatalMsg);
+ }
+}
+
+
+/*!
+ Constructs and installs an error handler window with the given \a
+ parent.
+*/
+
+QErrorMessage::QErrorMessage(QWidget * parent)
+ : QDialog(*new QErrorMessagePrivate, parent)
+{
+ Q_D(QErrorMessage);
+ QGridLayout * grid = new QGridLayout(this);
+ d->icon = new QLabel(this);
+#ifndef QT_NO_MESSAGEBOX
+ d->icon->setPixmap(QMessageBox::standardIcon(QMessageBox::Information));
+ d->icon->setAlignment(Qt::AlignHCenter | Qt::AlignTop);
+#endif
+ grid->addWidget(d->icon, 0, 0, Qt::AlignTop);
+ d->errors = new QErrorMessageTextView(this);
+ grid->addWidget(d->errors, 0, 1);
+ d->again = new QCheckBox(this);
+ d->again->setChecked(true);
+ grid->addWidget(d->again, 1, 1, Qt::AlignTop);
+ d->ok = new QPushButton(this);
+#ifdef Q_OS_WINCE
+ d->ok->setFixedSize(0,0);
+#endif
+ connect(d->ok, SIGNAL(clicked()), this, SLOT(accept()));
+ d->ok->setFocus();
+ grid->addWidget(d->ok, 2, 0, 1, 2, Qt::AlignCenter);
+ grid->setColumnStretch(1, 42);
+ grid->setRowStretch(0, 42);
+ d->retranslateStrings();
+}
+
+
+/*!
+ Destroys the error message dialog.
+*/
+
+QErrorMessage::~QErrorMessage()
+{
+ if (this == qtMessageHandler) {
+ qtMessageHandler = 0;
+ QtMsgHandler tmp = qInstallMsgHandler(0);
+ // in case someone else has later stuck in another...
+ if (tmp != jump)
+ qInstallMsgHandler(tmp);
+ }
+}
+
+
+/*! \reimp */
+
+void QErrorMessage::done(int a)
+{
+ Q_D(QErrorMessage);
+ if (!d->again->isChecked() && !d->currentMessage.isEmpty() && d->currentType.isEmpty()) {
+ d->doNotShow.insert(d->currentMessage);
+ }
+ if (!d->again->isChecked() && !d->currentType.isEmpty()) {
+ d->doNotShowType.insert(d->currentType);
+ }
+ d->currentMessage.clear();
+ d->currentType.clear();
+ if (!d->nextPending()) {
+ QDialog::done(a);
+ if (this == qtMessageHandler && metFatal)
+ exit(1);
+ }
+}
+
+
+/*!
+ Returns a pointer to a QErrorMessage object that outputs the
+ default Qt messages. This function creates such an object, if there
+ isn't one already.
+*/
+
+QErrorMessage * QErrorMessage::qtHandler()
+{
+ if (!qtMessageHandler) {
+ qtMessageHandler = new QErrorMessage(0);
+ qAddPostRoutine(deleteStaticcQErrorMessage); // clean up
+ qtMessageHandler->setWindowTitle(qApp->applicationName());
+ qInstallMsgHandler(jump);
+ }
+ return qtMessageHandler;
+}
+
+
+/*! \internal */
+
+bool QErrorMessagePrivate::nextPending()
+{
+ while (!pending.isEmpty()) {
+ QPair<QString,QString> pendingMessage = pending.dequeue();
+ QString message = pendingMessage.first;
+ QString type = pendingMessage.second;
+ if (!message.isEmpty() && ((type.isEmpty() && !doNotShow.contains(message)) || (!type.isEmpty() && !doNotShowType.contains(type)))) {
+#ifndef QT_NO_TEXTHTMLPARSER
+ errors->setHtml(message);
+#else
+ errors->setPlainText(message);
+#endif
+ currentMessage = message;
+ currentType = type;
+ return true;
+ }
+ }
+ return false;
+}
+
+
+/*!
+ Shows the given message, \a message, and returns immediately. If the user
+ has requested for the message not to be shown again, this function does
+ nothing.
+
+ Normally, the message is displayed immediately. However, if there are
+ pending messages, it will be queued to be displayed later.
+*/
+
+void QErrorMessage::showMessage(const QString &message)
+{
+ Q_D(QErrorMessage);
+ if (d->doNotShow.contains(message))
+ return;
+ d->pending.enqueue(qMakePair(message,QString()));
+ if (!isVisible() && d->nextPending())
+ show();
+}
+
+/*!
+ \since 4.5
+ \overload
+
+ Shows the given message, \a message, and returns immediately. If the user
+ has requested for messages of type, \a type, not to be shown again, this
+ function does nothing.
+
+ Normally, the message is displayed immediately. However, if there are
+ pending messages, it will be queued to be displayed later.
+
+ \sa showMessage()
+*/
+
+void QErrorMessage::showMessage(const QString &message, const QString &type)
+{
+ Q_D(QErrorMessage);
+ if (d->doNotShow.contains(message) && d->doNotShowType.contains(type))
+ return;
+ d->pending.push_back(qMakePair(message,type));
+ if (!isVisible() && d->nextPending())
+ show();
+}
+
+
+/*!
+ \reimp
+*/
+void QErrorMessage::changeEvent(QEvent *e)
+{
+ Q_D(QErrorMessage);
+ if (e->type() == QEvent::LanguageChange) {
+ d->retranslateStrings();
+ }
+ QDialog::changeEvent(e);
+}
+
+void QErrorMessagePrivate::retranslateStrings()
+{
+ again->setText(QErrorMessage::tr("&Show this message again"));
+ ok->setText(QErrorMessage::tr("&OK"));
+}
+
+/*!
+ \fn void QErrorMessage::message(const QString & message)
+
+ Use showMessage(\a message) instead.
+*/
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_ERRORMESSAGE
diff --git a/src/gui/dialogs/qerrormessage.h b/src/gui/dialogs/qerrormessage.h
new file mode 100644
index 0000000000..798631d52b
--- /dev/null
+++ b/src/gui/dialogs/qerrormessage.h
@@ -0,0 +1,88 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QERRORMESSAGE_H
+#define QERRORMESSAGE_H
+
+#include <QtGui/qdialog.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+#ifndef QT_NO_ERRORMESSAGE
+
+class QErrorMessagePrivate;
+
+class Q_GUI_EXPORT QErrorMessage: public QDialog
+{
+ Q_OBJECT
+ Q_DECLARE_PRIVATE(QErrorMessage)
+public:
+ explicit QErrorMessage(QWidget* parent = 0);
+ ~QErrorMessage();
+
+ static QErrorMessage * qtHandler();
+
+public Q_SLOTS:
+ void showMessage(const QString &message);
+ void showMessage(const QString &message, const QString &type);
+#ifdef QT3_SUPPORT
+ inline QT_MOC_COMPAT void message(const QString &text) { showMessage(text); }
+#endif
+
+protected:
+ void done(int);
+ void changeEvent(QEvent *e);
+
+private:
+ Q_DISABLE_COPY(QErrorMessage)
+};
+
+#endif // QT_NO_ERRORMESSAGE
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QERRORMESSAGE_H
diff --git a/src/gui/dialogs/qfiledialog.cpp b/src/gui/dialogs/qfiledialog.cpp
new file mode 100644
index 0000000000..2ce55632e4
--- /dev/null
+++ b/src/gui/dialogs/qfiledialog.cpp
@@ -0,0 +1,3299 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <qvariant.h>
+#include <private/qwidgetitemdata_p.h>
+#include "qfiledialog.h"
+
+#ifndef QT_NO_FILEDIALOG
+#include "qfiledialog_p.h"
+#include <qfontmetrics.h>
+#include <qaction.h>
+#include <qheaderview.h>
+#include <qshortcut.h>
+#include <qgridlayout.h>
+#include <qmenu.h>
+#include <qmessagebox.h>
+#include <qinputdialog.h>
+#include <stdlib.h>
+#include <qsettings.h>
+#include <qdebug.h>
+#include <qapplication.h>
+#include <qstylepainter.h>
+#ifndef Q_OS_WINCE
+#include "ui_qfiledialog.h"
+#else
+#include "ui_qfiledialog_wince.h"
+extern bool qt_priv_ptr_valid;
+#endif
+
+QT_BEGIN_NAMESPACE
+
+Q_GLOBAL_STATIC(QString, lastVisitedDir)
+
+/*
+ \internal
+
+ Exported hooks that can be used to customize the static functions.
+ */
+typedef QString (*_qt_filedialog_existing_directory_hook)(QWidget *parent, const QString &caption, const QString &dir, QFileDialog::Options options);
+Q_GUI_EXPORT _qt_filedialog_existing_directory_hook qt_filedialog_existing_directory_hook = 0;
+
+typedef QString (*_qt_filedialog_open_filename_hook)(QWidget * parent, const QString &caption, const QString &dir, const QString &filter, QString *selectedFilter, QFileDialog::Options options);
+Q_GUI_EXPORT _qt_filedialog_open_filename_hook qt_filedialog_open_filename_hook = 0;
+
+typedef QStringList (*_qt_filedialog_open_filenames_hook)(QWidget * parent, const QString &caption, const QString &dir, const QString &filter, QString *selectedFilter, QFileDialog::Options options);
+Q_GUI_EXPORT _qt_filedialog_open_filenames_hook qt_filedialog_open_filenames_hook = 0;
+
+typedef QString (*_qt_filedialog_save_filename_hook)(QWidget * parent, const QString &caption, const QString &dir, const QString &filter, QString *selectedFilter, QFileDialog::Options options);
+Q_GUI_EXPORT _qt_filedialog_save_filename_hook qt_filedialog_save_filename_hook = 0;
+
+/*!
+ \class QFileDialog
+ \brief The QFileDialog class provides a dialog that allow users to select files or directories.
+ \ingroup dialogs
+ \mainclass
+
+ The QFileDialog class enables a user to traverse the file system in
+ order to select one or many files or a directory.
+
+ The easiest way to create a QFileDialog 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_gui_dialogs_qfiledialog.cpp 0
+
+ In the above example, a modal QFileDialog is created using a static
+ function. The dialog initially displays the contents of the "/home/jana"
+ directory, and displays files matching the patterns given in the
+ string "Image Files (*.png *.jpg *.bmp)". The parent of the file dialog
+ is set to \e this, and the window title is set to "Open Image".
+
+ If you want to use multiple filters, separate each one with
+ \e two semicolons. For example:
+
+ \snippet doc/src/snippets/code/src_gui_dialogs_qfiledialog.cpp 1
+
+ You can create your own QFileDialog without using the static
+ functions. By calling setFileMode(), you can specify what the user must
+ select in the dialog:
+
+ \snippet doc/src/snippets/code/src_gui_dialogs_qfiledialog.cpp 2
+
+ In the above example, the mode of the file dialog is set to
+ 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
+ "Save As" file dialog. Use ExistingFile if the user must select an
+ existing file, or \l Directory if only a directory may be selected.
+ See the \l QFileDialog::FileMode enum for the complete list of modes.
+
+ The fileMode property contains the mode of operation for the dialog;
+ this indicates what types of objects the user is expected to select.
+ Use setNameFilter() to set the dialog's file filter. For example:
+
+ \snippet doc/src/snippets/code/src_gui_dialogs_qfiledialog.cpp 3
+
+ In the above example, the filter is set to \c{"Images (*.png *.xpm *.jpg)"},
+ this means that only files with the extension \c png, \c xpm,
+ or \c jpg will be shown in the QFileDialog. You can apply
+ several filters by using setNameFilters(). Use selectNameFilter() to select
+ one of the filters you've given as the file dialog's default filter.
+
+ The file dialog has two view modes: \l{QFileDialog::}{List} and
+ \l{QFileDialog::}{Detail}.
+ \l{QFileDialog::}{List} presents the contents of the current directory
+ as a list of file and directory names. \l{QFileDialog::}{Detail} also
+ displays a list of file and directory names, but provides additional
+ information alongside each name, such as the file size and modification
+ date. Set the mode with setViewMode():
+
+ \snippet doc/src/snippets/code/src_gui_dialogs_qfiledialog.cpp 4
+
+ The last important function you will need to use when creating your
+ own file dialog is selectedFiles().
+
+ \snippet doc/src/snippets/code/src_gui_dialogs_qfiledialog.cpp 5
+
+ In the above example, a modal file dialog is created and shown. If
+ the user clicked OK, the file they selected is put in \c fileName.
+
+ The dialog's working directory can be set with setDirectory().
+ Each file in the current directory can be selected using
+ the selectFile() function.
+
+ The \l{dialogs/standarddialogs}{Standard Dialogs} example shows
+ how to use QFileDialog as well as other built-in Qt dialogs.
+
+ \sa QDir, QFileInfo, QFile, QPrintDialog, QColorDialog, QFontDialog, {Standard Dialogs Example},
+ {Application Example}
+*/
+
+/*!
+ \enum QFileDialog::AcceptMode
+
+ \value AcceptOpen
+ \value AcceptSave
+*/
+
+/*!
+ \enum QFileDialog::ViewMode
+
+ This enum describes the view mode of the file dialog; i.e. what
+ information about each file will be displayed.
+
+ \value Detail Displays an icon, a name, and details for each item in
+ the directory.
+ \value List Displays only an icon and a name for each item in the
+ directory.
+
+ \sa setViewMode()
+*/
+
+/*!
+ \enum QFileDialog::FileMode
+
+ 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 ExistingFiles The names of zero or more existing files.
+
+ This value is obsolete since Qt 4.5:
+
+ \value DirectoryOnly Use \c Directory and setOption(ShowDirsOnly, true) instead.
+
+ \sa setFileMode()
+*/
+
+/*!
+ \enum QFileDialog::Option
+
+ \value ShowDirsOnly Only show directories in the file dialog. By default both files and
+ directories are shown. (Valid only in the \l Directory file mode.)
+ \value DontResolveSymlinks Don't resolve symlinks in the file dialog. By default symlinks
+ are resolved.
+ \value DontConfirmOverwrite Don't ask for confirmation if an existing file is selected.
+ By default confirmation is requested.
+ \value DontUseNativeDialog Don't use the native file dialog. By default on Mac OS X and Windows,
+ the native file dialog is used unless you use a subclass of QFileDialog that contains the
+ Q_OBJECT macro.
+ \value ReadOnly Indicates that the model is readonly.
+ \value HideNameFilterDetails Indicates if the is hidden or not.
+
+ This value is obsolete and does nothing since Qt 4.5:
+
+ \value DontUseSheet In previous versions of Qt, the static functions would
+ create a sheet by default if the static function was given a parent. This
+ is no longer supported in Qt 4.5, The static functions will always be an
+ application modal dialog. If you want to use sheets, use
+ QFileDialog::open() instead.
+
+*/
+
+/*!
+ \enum QFileDialog::DialogLabel
+
+ \value LookIn
+ \value FileName
+ \value FileType
+ \value Accept
+ \value Reject
+*/
+
+/*!
+ \fn void QFileDialog::filesSelected(const QStringList &selected)
+
+ When the selection changes and the dialog is accepted, this signal is
+ emitted with the (possibly empty) list of \a selected files.
+
+ \sa currentChanged(), QDialog::Accepted
+*/
+
+
+/*!
+ \fn void QFileDialog::fileSelected(const QString &file)
+
+ When the selection changes and the dialog is accepted, this signal is
+ emitted with the (possibly empty) selected \a file.
+
+ \sa currentChanged(), QDialog::Accepted
+*/
+
+
+/*!
+ \fn void QFileDialog::currentChanged(const QString &path)
+
+ When the current file changes, this signal is emitted with the
+ new file name as the \a path parameter.
+
+ \sa filesSelected()
+*/
+
+/*!
+ \fn void QFileDialog::directoryEntered(const QString &directory)
+ \since 4.3
+
+ This signal is emitted when the user enters a \a directory.
+*/
+
+/*!
+ \fn void QFileDialog::filterSelected(const QString &filter)
+ \since 4.3
+
+ This signal is emitted when the user selects a \a filter.
+*/
+
+#if defined(Q_WS_WIN) || defined(Q_WS_MAC)
+bool Q_GUI_EXPORT qt_use_native_dialogs = true; // for the benefit of testing tools, until we have a proper API
+#endif
+
+QT_BEGIN_INCLUDE_NAMESPACE
+#ifdef Q_WS_WIN
+#include <qwindowsstyle.h>
+#endif
+#include <qshortcut.h>
+#ifdef Q_WS_MAC
+#include <private/qunicodetables_p.h>
+#include <qmacstyle_mac.h>
+#endif
+QT_END_INCLUDE_NAMESPACE
+
+/*!
+ \fn QFileDialog::QFileDialog(QWidget *parent, Qt::WindowFlags flags)
+
+ Constructs a file dialog with the given \a parent and widget \a flags.
+*/
+QFileDialog::QFileDialog(QWidget *parent, Qt::WindowFlags f)
+ : QDialog(*new QFileDialogPrivate, parent, f)
+{
+ Q_D(QFileDialog);
+ d->init();
+}
+
+/*!
+ Constructs a file dialog with the given \a parent and \a caption that
+ initially displays the contents of the specified \a directory.
+ The contents of the directory are filtered before being shown in the
+ dialog, using a semicolon-separated list of filters specified by
+ \a filter.
+*/
+QFileDialog::QFileDialog(QWidget *parent,
+ const QString &caption,
+ const QString &directory,
+ const QString &filter)
+ : QDialog(*new QFileDialogPrivate, parent, 0)
+{
+ Q_D(QFileDialog);
+ d->init(directory, filter, caption);
+}
+
+/*!
+ \internal
+*/
+QFileDialog::QFileDialog(const QFileDialogArgs &args)
+ : QDialog(*new QFileDialogPrivate, args.parent, 0)
+{
+ Q_D(QFileDialog);
+ d->init(args.directory, args.filter, args.caption);
+ setFileMode(args.mode);
+ setOptions(args.options);
+ selectFile(args.selection);
+ d->lineEdit()->selectAll();
+}
+
+/*!
+ Destroys the file dialog.
+*/
+QFileDialog::~QFileDialog()
+{
+ Q_D(QFileDialog);
+#ifndef QT_NO_SETTINGS
+ QSettings settings(QSettings::UserScope, QLatin1String("Trolltech"));
+ settings.beginGroup(QLatin1String("Qt"));
+ settings.setValue(QLatin1String("filedialog"), saveState());
+#endif
+ delete d->qFileDialogUi;
+ d->deleteNativeDialog_sys();
+}
+
+/*!
+ \since 4.3
+ Sets the \a urls that are located in the sidebar.
+
+ For instance:
+
+ \snippet doc/src/snippets/filedialogurls.cpp 0
+
+ The file dialog will then look like this:
+
+ \image filedialogurls.png
+
+ \sa sidebarUrls()
+*/
+void QFileDialog::setSidebarUrls(const QList<QUrl> &urls)
+{
+ Q_D(QFileDialog);
+ d->qFileDialogUi->sidebar->setUrls(urls);
+}
+
+/*!
+ \since 4.3
+ Returns a list of urls that are currently in the sidebar
+*/
+QList<QUrl> QFileDialog::sidebarUrls() const
+{
+ Q_D(const QFileDialog);
+ return d->qFileDialogUi->sidebar->urls();
+}
+
+static const qint32 QFileDialogMagic = 0xbe;
+
+/*!
+ \since 4.3
+ Saves the state of the dialog's layout, history and current directory.
+
+ Typically this is used in conjunction with QSettings to remember the size
+ for a future session. A version number is stored as part of the data.
+*/
+QByteArray QFileDialog::saveState() const
+{
+ Q_D(const QFileDialog);
+ int version = 3;
+ QByteArray data;
+ QDataStream stream(&data, QIODevice::WriteOnly);
+
+ stream << qint32(QFileDialogMagic);
+ stream << qint32(version);
+ stream << d->qFileDialogUi->splitter->saveState();
+ stream << d->qFileDialogUi->sidebar->urls();
+ stream << history();
+ stream << *lastVisitedDir();
+ stream << d->qFileDialogUi->treeView->header()->saveState();
+ stream << qint32(viewMode());
+ return data;
+}
+
+/*!
+ \since 4.3
+ Restores the dialogs's layout, history and current directory to the \a state specified.
+
+ Typically this is used in conjunction with QSettings to restore the size
+ from a past session.
+
+ Returns false if there are errors
+*/
+bool QFileDialog::restoreState(const QByteArray &state)
+{
+ Q_D(QFileDialog);
+ int version = 3;
+ QByteArray sd = state;
+ QDataStream stream(&sd, QIODevice::ReadOnly);
+ if (stream.atEnd())
+ return false;
+ QByteArray splitterState;
+ QByteArray headerData;
+ QList<QUrl> bookmarks;
+ QStringList history;
+ QString currentDirectory;
+ qint32 marker;
+ qint32 v;
+ qint32 viewMode;
+ stream >> marker;
+ stream >> v;
+ if (marker != QFileDialogMagic || v != version)
+ return false;
+
+ stream >> splitterState
+ >> bookmarks
+ >> history
+ >> currentDirectory
+ >> headerData
+ >> viewMode;
+
+ if (!d->qFileDialogUi->splitter->restoreState(splitterState))
+ return false;
+ QList<int> list = d->qFileDialogUi->splitter->sizes();
+ if (list.count() >= 2 && list.at(0) == 0 && list.at(1) == 0) {
+ for (int i = 0; i < list.count(); ++i)
+ list[i] = d->qFileDialogUi->splitter->widget(i)->sizeHint().width();
+ d->qFileDialogUi->splitter->setSizes(list);
+ }
+
+ d->qFileDialogUi->sidebar->setUrls(bookmarks);
+ while (history.count() > 5)
+ history.pop_front();
+ setHistory(history);
+ setDirectory(lastVisitedDir()->isEmpty() ? currentDirectory : *lastVisitedDir());
+ if (!d->qFileDialogUi->treeView->header()->restoreState(headerData))
+ return false;
+
+ setViewMode(ViewMode(viewMode));
+ return true;
+}
+
+/*!
+ \reimp
+*/
+void QFileDialog::changeEvent(QEvent *e)
+{
+ Q_D(QFileDialog);
+ if (e->type() == QEvent::LanguageChange) {
+ d->retranslateWindowTitle();
+ d->retranslateStrings();
+ }
+ QDialog::changeEvent(e);
+}
+
+void QFileDialogPrivate::retranslateWindowTitle()
+{
+ Q_Q(QFileDialog);
+ if (!useDefaultCaption || setWindowTitle != q->windowTitle())
+ return;
+ if (acceptMode == QFileDialog::AcceptOpen) {
+ if (fileMode == QFileDialog::DirectoryOnly || fileMode == QFileDialog::Directory)
+ q->setWindowTitle(QFileDialog::tr("Find Directory"));
+ else
+ q->setWindowTitle(QFileDialog::tr("Open"));
+ } else
+ q->setWindowTitle(QFileDialog::tr("Save As"));
+
+ setWindowTitle = q->windowTitle();
+}
+
+void QFileDialogPrivate::setLastVisitedDirectory(const QString &dir)
+{
+ *lastVisitedDir() = dir;
+}
+
+void QFileDialogPrivate::retranslateStrings()
+{
+ Q_Q(QFileDialog);
+ /* WIDGETS */
+ if (defaultFileTypes)
+ q->setNameFilter(QFileDialog::tr("All Files (*)"));
+
+ QList<QAction*> actions = qFileDialogUi->treeView->header()->actions();
+ QAbstractItemModel *abstractModel = model;
+#ifndef QT_NO_PROXYMODEL
+ if (proxyModel)
+ abstractModel = proxyModel;
+#endif
+ int total = qMin(abstractModel->columnCount(QModelIndex()), actions.count() + 1);
+ for (int i = 1; i < total; ++i) {
+ actions.at(i - 1)->setText(QFileDialog::tr("Show ") + abstractModel->headerData(i, Qt::Horizontal, Qt::DisplayRole).toString());
+ }
+
+ /* MENU ACTIONS */
+ renameAction->setText(QFileDialog::tr("&Rename"));
+ deleteAction->setText(QFileDialog::tr("&Delete"));
+ showHiddenAction->setText(QFileDialog::tr("Show &hidden files"));
+ newFolderAction->setText(QFileDialog::tr("&New Folder"));
+ qFileDialogUi->retranslateUi(q);
+
+ if (!fileNameLabelExplicitlySat){
+ if (fileMode == QFileDialog::DirectoryOnly || fileMode == QFileDialog::Directory) {
+ q->setLabelText(QFileDialog::FileName, QFileDialog::tr("Directory:"));
+ } else {
+ q->setLabelText(QFileDialog::FileName, QFileDialog::tr("File &name:"));
+ }
+ fileNameLabelExplicitlySat = false;
+ }
+}
+
+void QFileDialogPrivate::emitFilesSelected(const QStringList &files)
+{
+ Q_Q(QFileDialog);
+ emit q->filesSelected(files);
+ if (files.count() == 1)
+ emit q->fileSelected(files.first());
+}
+
+bool QFileDialogPrivate::canBeNativeDialog()
+{
+ Q_Q(QFileDialog);
+ if (nativeDialogInUse)
+ return true;
+ if (q->testAttribute(Qt::WA_DontShowOnScreen))
+ return false;
+ if (opts & QFileDialog::DontUseNativeDialog)
+ return false;
+
+ QLatin1String staticName(QFileDialog::staticMetaObject.className());
+ QLatin1String dynamicName(q->metaObject()->className());
+ return (staticName == dynamicName);
+}
+
+/*!
+ Sets the given \a option to be enabled if \a on is true;
+ otherwise, clears the given \a option.
+
+ \sa options, testOption()
+*/
+void QFileDialog::setOption(Option option, bool on)
+{
+ Q_D(QFileDialog);
+ if (!(d->opts & option) != !on)
+ setOptions(d->opts ^ option);
+}
+
+/*!
+ \since 4.5
+
+ Returns true if the given \a option is enabled; otherwise, returns
+ false.
+
+ \sa options, setOption()
+*/
+bool QFileDialog::testOption(Option option) const
+{
+ Q_D(const QFileDialog);
+ return (d->opts & option) != 0;
+}
+
+/*!
+ \property QFileDialog::options
+ \brief the various options that affect the look and feel of the dialog
+ \since 4.5
+
+ By default, all options are disabled.
+
+ Options should be set before showing the dialog. Setting them while the
+ dialog is visible is not guaranteed to have an immediate effect on the
+ dialog (depending on the option and on the platform).
+
+ \sa setOption(), testOption()
+*/
+void QFileDialog::setOptions(Options options)
+{
+ Q_D(QFileDialog);
+
+ Options changed = (options ^ d->opts);
+ if (!changed)
+ return;
+
+ d->opts = options;
+ if (changed & DontResolveSymlinks)
+ d->model->setResolveSymlinks(!(options & DontResolveSymlinks));
+ if (changed & ReadOnly) {
+ bool ro = (options & ReadOnly);
+ d->model->setReadOnly(ro);
+ d->qFileDialogUi->newFolderButton->setEnabled(!ro);
+ d->renameAction->setEnabled(!ro);
+ d->deleteAction->setEnabled(!ro);
+ }
+ if (changed & HideNameFilterDetails)
+ setNameFilters(d->nameFilters);
+}
+
+QFileDialog::Options QFileDialog::options() const
+{
+ Q_D(const QFileDialog);
+ return d->opts;
+}
+
+/*!
+ \overload
+
+ \since 4.5
+
+ Opens the dialog and connects its accepted() signal to the slot specified
+ by \a receiver and \a member.
+
+ The signal will be disconnected from the slot when the dialog is closed.
+*/
+void QFileDialog::open(QObject *receiver, const char *member)
+{
+ Q_D(QFileDialog);
+ const char *signal = (fileMode() == ExistingFiles) ? SIGNAL(filesSelected(const QStringList&))
+ : SIGNAL(fileSelected(const QString&));
+ connect(this, signal, receiver, member);
+ d->signalToDisconnectOnClose = signal;
+ d->receiverToDisconnectOnClose = receiver;
+ d->memberToDisconnectOnClose = member;
+
+ QDialog::open();
+}
+
+
+/*!
+ \reimp
+*/
+void QFileDialog::setVisible(bool visible)
+{
+ Q_D(QFileDialog);
+ if (visible){
+ if (testAttribute(Qt::WA_WState_ExplicitShowHide) && !testAttribute(Qt::WA_WState_Hidden))
+ return;
+ } else if (testAttribute(Qt::WA_WState_ExplicitShowHide) && testAttribute(Qt::WA_WState_Hidden))
+ return;
+
+ if (d->canBeNativeDialog()){
+ if (d->setVisible_sys(visible)){
+ d->nativeDialogInUse = true;
+ // Set WA_DontShowOnScreen so that QDialog::setVisible(visible) below
+ // updates the state correctly, but skips showing the non-native version:
+ setAttribute(Qt::WA_DontShowOnScreen);
+ } else {
+ d->nativeDialogInUse = false;
+ setAttribute(Qt::WA_DontShowOnScreen, false);
+ }
+ }
+
+ if (!d->nativeDialogInUse)
+ d->qFileDialogUi->fileNameEdit->setFocus();
+
+ QDialog::setVisible(visible);
+}
+
+/*!
+ \internal
+ set the directory to url
+*/
+void QFileDialogPrivate::_q_goToUrl(const QUrl &url)
+{
+ QModelIndex idx = model->index(url.toLocalFile());
+ _q_enterDirectory(idx);
+}
+
+/*!
+ \fn void QFileDialog::setDirectory(const QDir &directory)
+
+ \overload
+*/
+
+/*!
+ Sets the file dialog's current \a directory.
+*/
+void QFileDialog::setDirectory(const QString &directory)
+{
+ Q_D(QFileDialog);
+ QString newDirectory = directory;
+ QFileInfo info(directory);
+ //we remove .. and . from the given path if exist
+ if (!directory.isEmpty())
+ newDirectory = QDir::cleanPath(directory);
+
+ if (!directory.isEmpty() && newDirectory.isEmpty())
+ return;
+
+ d->setLastVisitedDirectory(newDirectory);
+
+ if (d->nativeDialogInUse){
+ d->setDirectory_sys(newDirectory);
+ return;
+ }
+ if (d->rootPath() == newDirectory)
+ return;
+ QModelIndex root = d->model->setRootPath(newDirectory);
+ d->qFileDialogUi->newFolderButton->setEnabled(d->model->flags(root) & Qt::ItemIsDropEnabled);
+ if (root != d->rootIndex()) {
+#ifndef QT_NO_COMPLETER
+ if (directory.endsWith(QLatin1Char('/')))
+ d->completer->setCompletionPrefix(newDirectory);
+ else
+ d->completer->setCompletionPrefix(newDirectory + QLatin1Char('/'));
+#endif
+ d->setRootIndex(root);
+ }
+ d->qFileDialogUi->listView->selectionModel()->clear();
+}
+
+/*!
+ Returns the directory currently being displayed in the dialog.
+*/
+QDir QFileDialog::directory() const
+{
+ Q_D(const QFileDialog);
+ return QDir(d->nativeDialogInUse ? d->directory_sys() : d->rootPath());
+}
+
+/*!
+ Selects the given \a filename in the file dialog.
+
+ \sa selectedFiles()
+*/
+void QFileDialog::selectFile(const QString &filename)
+{
+ Q_D(QFileDialog);
+ if (filename.isEmpty())
+ return;
+
+ if (d->nativeDialogInUse){
+ d->selectFile_sys(filename);
+ return;
+ }
+
+ if (!QDir::isRelativePath(filename)) {
+ QFileInfo info(filename);
+ QString filenamePath = info.absoluteDir().path();
+
+ if (d->model->rootPath() != filenamePath)
+ setDirectory(filenamePath);
+ }
+
+ QModelIndex index = d->model->index(filename);
+ if (!index.isValid()) {
+ // save as dialog where we want to input a default value
+ QString text = filename;
+ if (QFileInfo(filename).isAbsolute()) {
+ QString current = d->rootPath();
+ text.remove(current);
+ if (text.at(0) == QDir::separator()
+#ifdef Q_OS_WIN
+ //On Windows both cases can happen
+ || text.at(0) == QLatin1Char('/')
+#endif
+ )
+ text = text.remove(0,1);
+ }
+ if (!isVisible() || !d->lineEdit()->hasFocus())
+ d->lineEdit()->setText(text);
+ } else {
+ d->qFileDialogUi->listView->selectionModel()->clear();
+ if (!isVisible() || !d->lineEdit()->hasFocus())
+ d->lineEdit()->setText(index.data().toString());
+ }
+}
+
+/**
+ Returns the text in the line edit which can be one or more file names
+ */
+QStringList QFileDialogPrivate::typedFiles() const
+{
+ QStringList files;
+ QString editText = lineEdit()->text();
+ if (!editText.contains(QLatin1Char('"')))
+ files << editText;
+ else {
+ // " is used to separate files like so: "file1" "file2" "file3" ...
+ // ### need escape character for filenames with quotes (")
+ QStringList tokens = editText.split(QLatin1Char('\"'));
+ for (int i=0; i<tokens.size(); ++i) {
+ if ((i % 2) == 0)
+ continue; // Every even token is a separator
+ files << toInternal(tokens.at(i));
+ }
+ }
+ return addDefaultSuffixToFiles(files);
+}
+
+QStringList QFileDialogPrivate::addDefaultSuffixToFiles(const QStringList filesToFix) const
+{
+ QStringList files;
+ for (int i=0; i<filesToFix.size(); ++i) {
+ QString name = toInternal(filesToFix.at(i));
+ QFileInfo info(name);
+ // if the filename has no suffix, add the default suffix
+ if (!defaultSuffix.isEmpty() && !info.isDir() && name.lastIndexOf(QLatin1Char('.')) == -1)
+ name += QLatin1Char('.') + defaultSuffix;
+ if (info.isAbsolute()) {
+ files.append(name);
+ } else {
+ // at this point the path should only have Qt path separators.
+ // This check is needed since we might be at the root directory
+ // and on Windows it already ends with slash.
+ QString path = rootPath();
+ if (!path.endsWith(QLatin1String("/")))
+ path += QLatin1String("/");
+ path += name;
+ files.append(path);
+ }
+ }
+ return files;
+}
+
+
+/*!
+ Returns a list of strings containing the absolute paths of the
+ selected files in the dialog. If no files are selected, or
+ the mode is not ExistingFiles or ExistingFile, selectedFiles() contains the current path in the viewport.
+
+ \sa selectedNameFilter(), selectFile()
+*/
+QStringList QFileDialog::selectedFiles() const
+{
+ Q_D(const QFileDialog);
+ if (d->nativeDialogInUse)
+ return d->addDefaultSuffixToFiles(d->selectedFiles_sys());
+
+ QModelIndexList indexes = d->qFileDialogUi->listView->selectionModel()->selectedRows();
+ QStringList files;
+ for (int i = 0; i < indexes.count(); ++i)
+ files.append(indexes.at(i).data(QFileSystemModel::FilePathRole).toString());
+
+ if (files.isEmpty() && !d->lineEdit()->text().isEmpty())
+ files = d->typedFiles();
+
+ if (files.isEmpty() && !(d->fileMode == ExistingFile || d->fileMode == ExistingFiles))
+ files.append(d->rootIndex().data(QFileSystemModel::FilePathRole).toString());
+ return files;
+}
+
+/*
+ Makes a list of filters from ;;-separated text.
+ Used by the mac and windows implementations
+*/
+QStringList qt_make_filter_list(const QString &filter)
+{
+ QString f(filter);
+
+ if (f.isEmpty())
+ return QStringList();
+
+ QString sep(QLatin1String(";;"));
+ int i = f.indexOf(sep, 0);
+ if (i == -1) {
+ if (f.indexOf(QLatin1Char('\n'), 0) != -1) {
+ sep = QLatin1Char('\n');
+ i = f.indexOf(sep, 0);
+ }
+ }
+
+ return f.split(sep);
+}
+
+/*!
+ \since 4.4
+
+ Sets the filter used in the file dialog to the given \a filter.
+
+ If \a filter contains a pair of parentheses containing one or more
+ of \bold{anything*something}, separated by spaces, 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_gui_dialogs_qfiledialog.cpp 6
+
+ \sa setNameFilters()
+*/
+void QFileDialog::setNameFilter(const QString &filter)
+{
+ setNameFilters(qt_make_filter_list(filter));
+}
+
+/*!
+ \obsolete
+
+ Use setNameFilter() instead.
+*/
+void QFileDialog::setFilter(const QString &filter)
+{
+ setNameFilter(filter);
+}
+
+/*!
+ \property QFileDialog::nameFilterDetailsVisible
+ \obsolete
+ \brief This property holds whether the filter details is shown or not.
+ \since 4.4
+
+ When this property is true (the default), the filter details are shown
+ in the combo box. When the property is set to false, these are hidden.
+
+ Use setOption(HideNameFilterDetails, !\e enabled) or
+ !testOption(HideNameFilterDetails).
+*/
+void QFileDialog::setNameFilterDetailsVisible(bool enabled)
+{
+ setOption(HideNameFilterDetails, !enabled);
+}
+
+bool QFileDialog::isNameFilterDetailsVisible() const
+{
+ return !testOption(HideNameFilterDetails);
+}
+
+
+/*!
+ \since 4.4
+
+ Sets the \a filters used in the file dialog.
+
+ \snippet doc/src/snippets/code/src_gui_dialogs_qfiledialog.cpp 7
+*/
+void QFileDialog::setNameFilters(const QStringList &filters)
+{
+ Q_D(QFileDialog);
+ d->defaultFileTypes = (filters == QStringList(QFileDialog::tr("All Files (*)")));
+ d->nameFilters = filters;
+
+ if (d->nativeDialogInUse){
+ d->setNameFilters_sys(filters);
+ return;
+ }
+
+ d->qFileDialogUi->fileTypeCombo->clear();
+ if (filters.isEmpty())
+ return;
+
+ if (testOption(HideNameFilterDetails)) {
+ QStringList strippedFilters;
+ for (int i = 0; i < filters.count(); ++i) {
+ strippedFilters.append(filters[i].mid(0, filters[i].indexOf(QLatin1String(" ("))));
+ }
+ d->qFileDialogUi->fileTypeCombo->addItems(strippedFilters);
+ } else {
+ d->qFileDialogUi->fileTypeCombo->addItems(filters);
+ }
+ d->_q_useNameFilter(0);
+}
+
+/*!
+ \obsolete
+
+ Use setNameFilters() instead.
+*/
+void QFileDialog::setFilters(const QStringList &filters)
+{
+ setNameFilters(filters);
+}
+
+/*!
+ \since 4.4
+
+ Returns the file type filters that are in operation on this file
+ dialog.
+*/
+QStringList QFileDialog::nameFilters() const
+{
+ return d_func()->nameFilters;
+}
+
+/*!
+ \obsolete
+
+ Use nameFilters() instead.
+*/
+
+QStringList QFileDialog::filters() const
+{
+ return nameFilters();
+}
+
+/*!
+ \since 4.4
+
+ Sets the current file type \a filter. Multiple filters can be
+ passed in \a filter by separating them with semicolons or spaces.
+
+ \sa setNameFilter(), setNameFilters(), selectedNameFilter()
+*/
+void QFileDialog::selectNameFilter(const QString &filter)
+{
+ Q_D(QFileDialog);
+ if (d->nativeDialogInUse) {
+ d->selectNameFilter_sys(filter);
+ return;
+ }
+
+ int i = d->qFileDialogUi->fileTypeCombo->findText(filter);
+ if (i >= 0) {
+ d->qFileDialogUi->fileTypeCombo->setCurrentIndex(i);
+ d->_q_useNameFilter(d->qFileDialogUi->fileTypeCombo->currentIndex());
+ }
+}
+
+/*!
+ \obsolete
+
+ Use selectNameFilter() instead.
+*/
+
+void QFileDialog::selectFilter(const QString &filter)
+{
+ selectNameFilter(filter);
+}
+
+/*!
+ \since 4.4
+
+ Returns the filter that the user selected in the file dialog.
+
+ \sa selectedFiles()
+*/
+QString QFileDialog::selectedNameFilter() const
+{
+ Q_D(const QFileDialog);
+ if (d->nativeDialogInUse)
+ return d->selectedNameFilter_sys();
+
+ return d->qFileDialogUi->fileTypeCombo->currentText();
+}
+
+/*!
+ \obsolete
+
+ Use selectedNameFilter() instead.
+*/
+QString QFileDialog::selectedFilter() const
+{
+ return selectedNameFilter();
+}
+
+/*!
+ \since 4.4
+
+ Returns the filter that is used when displaying files.
+
+ \sa setFilter()
+*/
+QDir::Filters QFileDialog::filter() const
+{
+ Q_D(const QFileDialog);
+ return d->model->filter();
+}
+
+/*!
+ \since 4.4
+
+ Sets the filter used by the model to \a filters. The filter is used
+ to specify the kind of files that should be shown.
+
+ \sa filter()
+*/
+
+void QFileDialog::setFilter(QDir::Filters filters)
+{
+ Q_D(QFileDialog);
+ d->model->setFilter(filters);
+ if (d->nativeDialogInUse){
+ d->setFilter_sys();
+ return;
+ }
+
+ d->showHiddenAction->setChecked((filters & QDir::Hidden));
+}
+
+/*!
+ \property QFileDialog::viewMode
+ \brief the way files and directories are displayed in the dialog
+
+ By default, the \c Detail mode is used to display information about
+ files and directories.
+
+ \sa ViewMode
+*/
+void QFileDialog::setViewMode(QFileDialog::ViewMode mode)
+{
+ Q_D(QFileDialog);
+ if (mode == Detail)
+ d->_q_showDetailsView();
+ else
+ d->_q_showListView();
+}
+
+QFileDialog::ViewMode QFileDialog::viewMode() const
+{
+ Q_D(const QFileDialog);
+ return (d->qFileDialogUi->stackedWidget->currentWidget() == d->qFileDialogUi->listView->parent() ? QFileDialog::List : QFileDialog::Detail);
+}
+
+/*!
+ \property QFileDialog::fileMode
+ \brief the file mode of the dialog
+
+ The file mode defines the number and type of items that the user is
+ expected to select in the dialog.
+
+ By default, this property is set to AnyFile.
+
+ This function will set the labels for the FileName and
+ \l{QFileDialog::}{Accept} \l{DialogLabel}s. It is possible to set
+ custom text after the call to setFileMode().
+
+ \sa FileMode
+*/
+void QFileDialog::setFileMode(QFileDialog::FileMode mode)
+{
+ Q_D(QFileDialog);
+ if (d->nativeDialogInUse){
+ d->model->setFilter(d->filterForMode(filter()));
+ d->setFilter_sys();
+ return;
+ }
+
+ d->fileMode = mode;
+ d->retranslateWindowTitle();
+
+ // keep ShowDirsOnly option in sync with fileMode (BTW, DirectoryOnly is obsolete)
+ setOption(ShowDirsOnly, mode == DirectoryOnly);
+
+ // set selection mode and behavior
+ QAbstractItemView::SelectionMode selectionMode;
+ if (mode == QFileDialog::ExistingFiles)
+ selectionMode = QAbstractItemView::ExtendedSelection;
+ else
+ selectionMode = QAbstractItemView::SingleSelection;
+ d->qFileDialogUi->listView->setSelectionMode(selectionMode);
+ d->qFileDialogUi->treeView->setSelectionMode(selectionMode);
+ // set filter
+ d->model->setFilter(d->filterForMode(filter()));
+ // setup file type for directory
+ QString buttonText = (d->acceptMode == AcceptOpen ? tr("&Open") : tr("&Save"));
+ if (mode == DirectoryOnly || mode == Directory) {
+ d->qFileDialogUi->fileTypeCombo->clear();
+ d->qFileDialogUi->fileTypeCombo->addItem(tr("Directories"));
+ d->qFileDialogUi->fileTypeCombo->setEnabled(false);
+
+ if (!d->fileNameLabelExplicitlySat){
+ setLabelText(FileName, tr("Directory:"));
+ d->fileNameLabelExplicitlySat = false;
+ }
+ buttonText = tr("&Choose");
+ } else {
+ if (!d->fileNameLabelExplicitlySat){
+ setLabelText(FileName, tr("File &name:"));
+ d->fileNameLabelExplicitlySat = false;
+ }
+ }
+ setLabelText(Accept, buttonText);
+ d->qFileDialogUi->fileTypeCombo->setEnabled(!testOption(ShowDirsOnly));
+ d->_q_updateOkButton();
+}
+
+QFileDialog::FileMode QFileDialog::fileMode() const
+{
+ Q_D(const QFileDialog);
+ return d->fileMode;
+}
+
+/*!
+ \property QFileDialog::acceptMode
+ \brief the accept mode of the dialog
+
+ The action mode defines whether the dialog is for opening or saving files.
+
+ By default, this property is set to \l{AcceptOpen}.
+
+ \sa AcceptMode
+*/
+void QFileDialog::setAcceptMode(QFileDialog::AcceptMode mode)
+{
+ Q_D(QFileDialog);
+ d->acceptMode = mode;
+ bool directoryMode = (d->fileMode == Directory || d->fileMode == DirectoryOnly);
+ QDialogButtonBox::StandardButton button = (mode == AcceptOpen ? QDialogButtonBox::Open : QDialogButtonBox::Save);
+ d->qFileDialogUi->buttonBox->setStandardButtons(button | QDialogButtonBox::Cancel);
+ d->qFileDialogUi->buttonBox->button(button)->setEnabled(false);
+ d->_q_updateOkButton();
+ if (mode == AcceptOpen && directoryMode)
+ setLabelText(Accept, tr("&Choose"));
+ else
+ setLabelText(Accept, (mode == AcceptOpen ? tr("&Open") : tr("&Save")));
+ if (mode == AcceptSave) {
+ d->qFileDialogUi->lookInCombo->setEditable(false);
+ }
+ d->retranslateWindowTitle();
+}
+
+/*
+ Returns the file system model index that is the root index in the
+ views
+*/
+QModelIndex QFileDialogPrivate::rootIndex() const {
+ return mapToSource(qFileDialogUi->listView->rootIndex());
+}
+
+QAbstractItemView *QFileDialogPrivate::currentView() const {
+ if (!qFileDialogUi->stackedWidget)
+ return 0;
+ if (qFileDialogUi->stackedWidget->currentWidget() == qFileDialogUi->listView->parent())
+ return qFileDialogUi->listView;
+ return qFileDialogUi->treeView;
+}
+
+QLineEdit *QFileDialogPrivate::lineEdit() const {
+ return (QLineEdit*)qFileDialogUi->fileNameEdit;
+}
+
+/*
+ Sets the view root index to be the file system model index
+*/
+void QFileDialogPrivate::setRootIndex(const QModelIndex &index) const {
+ Q_ASSERT(index.isValid() ? index.model() == model : true);
+ QModelIndex idx = mapFromSource(index);
+ qFileDialogUi->treeView->setRootIndex(idx);
+ qFileDialogUi->listView->setRootIndex(idx);
+}
+/*
+ Select a file system model index
+ returns the index that was selected (or not depending upon sortfilterproxymodel)
+*/
+QModelIndex QFileDialogPrivate::select(const QModelIndex &index) const {
+ Q_ASSERT(index.isValid() ? index.model() == model : true);
+
+ QModelIndex idx = mapFromSource(index);
+ if (idx.isValid() && !qFileDialogUi->listView->selectionModel()->isSelected(idx))
+ qFileDialogUi->listView->selectionModel()->select(idx,
+ QItemSelectionModel::Select | QItemSelectionModel::Rows);
+ return idx;
+}
+
+QFileDialog::AcceptMode QFileDialog::acceptMode() const
+{
+ Q_D(const QFileDialog);
+ return d->acceptMode;
+}
+
+/*!
+ \property QFileDialog::readOnly
+ \obsolete
+ \brief Whether the filedialog is read-only
+
+ If this property is set to false, the file dialog will allow renaming,
+ and deleting of files and directories and creating directories.
+
+ Use setOption(ReadOnly, \e enabled) or testOption(ReadOnly) instead.
+*/
+void QFileDialog::setReadOnly(bool enabled)
+{
+ setOption(ReadOnly, enabled);
+}
+
+bool QFileDialog::isReadOnly() const
+{
+ return testOption(ReadOnly);
+}
+
+/*!
+ \property QFileDialog::resolveSymlinks
+ \obsolete
+ \brief whether the filedialog should resolve shortcuts
+
+ If this property is set to true, the file dialog will resolve
+ shortcuts or symbolic links.
+
+ Use setOption(DontResolveSymlinks, !\a enabled) or
+ !testOption(DontResolveSymlinks).
+*/
+void QFileDialog::setResolveSymlinks(bool enabled)
+{
+ setOption(DontResolveSymlinks, !enabled);
+}
+
+bool QFileDialog::resolveSymlinks() const
+{
+ return !testOption(DontResolveSymlinks);
+}
+
+/*!
+ \property QFileDialog::confirmOverwrite
+ \obsolete
+ \brief whether the filedialog should ask before accepting a selected file,
+ when the accept mode is AcceptSave
+
+ Use setOption(DontConfirmOverwrite, !\e enabled) or
+ !testOption(DontConfirmOverwrite) instead.
+*/
+void QFileDialog::setConfirmOverwrite(bool enabled)
+{
+ setOption(DontConfirmOverwrite, !enabled);
+}
+
+bool QFileDialog::confirmOverwrite() const
+{
+ return !testOption(DontConfirmOverwrite);
+}
+
+/*!
+ \property QFileDialog::defaultSuffix
+ \brief suffix added to the filename if no other suffix was specified
+
+ This property specifies a string that will be added to the
+ filename if it has no suffix already. The suffix is typically
+ used to indicate the file type (e.g. "txt" indicates a text
+ file).
+*/
+void QFileDialog::setDefaultSuffix(const QString &suffix)
+{
+ Q_D(QFileDialog);
+ d->defaultSuffix = suffix;
+}
+
+QString QFileDialog::defaultSuffix() const
+{
+ Q_D(const QFileDialog);
+ return d->defaultSuffix;
+}
+
+/*!
+ Sets the browsing history of the filedialog to contain the given
+ \a paths.
+*/
+void QFileDialog::setHistory(const QStringList &paths)
+{
+ Q_D(QFileDialog);
+ d->qFileDialogUi->lookInCombo->setHistory(paths);
+}
+
+void QFileDialogComboBox::setHistory(const QStringList &paths)
+{
+ m_history = paths;
+ // Only populate the first item, showPopup will populate the rest if needed
+ QList<QUrl> list;
+ QModelIndex idx = d_ptr->model->index(d_ptr->rootPath());
+ //On windows the popup display the "C:\", convert to nativeSeparators
+ QUrl url = QUrl::fromLocalFile(QDir::toNativeSeparators(idx.data(QFileSystemModel::FilePathRole).toString()));
+ if (url.isValid())
+ list.append(url);
+ urlModel->setUrls(list);
+}
+
+/*!
+ Returns the browsing history of the filedialog as a list of paths.
+*/
+QStringList QFileDialog::history() const
+{
+ Q_D(const QFileDialog);
+ QStringList currentHistory = d->qFileDialogUi->lookInCombo->history();
+ //On windows the popup display the "C:\", convert to nativeSeparators
+ QString newHistory = QDir::toNativeSeparators(d->rootIndex().data(QFileSystemModel::FilePathRole).toString());
+ if (!currentHistory.contains(newHistory))
+ currentHistory << newHistory;
+ return currentHistory;
+}
+
+/*!
+ Sets the item delegate used to render items in the views in the
+ file dialog to the given \a delegate.
+
+ \warning You should not share the same instance of a delegate between views.
+ Doing so can cause incorrect or unintuitive editing behavior since each
+ view connected to a given delegate may receive the \l{QAbstractItemDelegate::}{closeEditor()}
+ signal, and attempt to access, modify or close an editor that has already been closed.
+
+ Note that the model used is QFileSystemModel. It has custom item data roles, which is
+ described by the \l{QFileSystemModel::}{Roles} enum. You can use a QFileIconProvider if
+ you only want custom icons.
+
+ \sa itemDelegate(), setIconProvider(), QFileSystemModel
+*/
+void QFileDialog::setItemDelegate(QAbstractItemDelegate *delegate)
+{
+ Q_D(QFileDialog);
+ d->qFileDialogUi->listView->setItemDelegate(delegate);
+ d->qFileDialogUi->treeView->setItemDelegate(delegate);
+}
+
+/*!
+ Returns the item delegate used to render the items in the views in the filedialog.
+*/
+QAbstractItemDelegate *QFileDialog::itemDelegate() const
+{
+ Q_D(const QFileDialog);
+ return d->qFileDialogUi->listView->itemDelegate();
+}
+
+/*!
+ Sets the icon provider used by the filedialog to the specified \a provider.
+*/
+void QFileDialog::setIconProvider(QFileIconProvider *provider)
+{
+ Q_D(QFileDialog);
+ d->model->setIconProvider(provider);
+}
+
+/*!
+ Returns the icon provider used by the filedialog.
+*/
+QFileIconProvider *QFileDialog::iconProvider() const
+{
+ Q_D(const QFileDialog);
+ return d->model->iconProvider();
+}
+
+/*!
+ Sets the \a text shown in the filedialog in the specified \a label.
+*/
+void QFileDialog::setLabelText(DialogLabel label, const QString &text)
+{
+ Q_D(QFileDialog);
+ QPushButton *button;
+ switch (label) {
+ case LookIn:
+ d->qFileDialogUi->lookInLabel->setText(text);
+ break;
+ case FileName:
+ d->qFileDialogUi->fileNameLabel->setText(text);
+ d->fileNameLabelExplicitlySat = true;
+ break;
+ case FileType:
+ d->qFileDialogUi->fileTypeLabel->setText(text);
+ break;
+ case Accept:
+ d->acceptLabel = text;
+ if (acceptMode() == AcceptOpen)
+ button = d->qFileDialogUi->buttonBox->button(QDialogButtonBox::Open);
+ else
+ button = d->qFileDialogUi->buttonBox->button(QDialogButtonBox::Save);
+ if (button)
+ button->setText(text);
+ break;
+ case Reject:
+ button = d->qFileDialogUi->buttonBox->button(QDialogButtonBox::Cancel);
+ if (button)
+ button->setText(text);
+ break;
+ }
+}
+
+/*!
+ Returns the text shown in the filedialog in the specified \a label.
+*/
+QString QFileDialog::labelText(DialogLabel label) const
+{
+ QPushButton *button;
+ Q_D(const QFileDialog);
+ switch (label) {
+ case LookIn:
+ return d->qFileDialogUi->lookInLabel->text();
+ case FileName:
+ return d->qFileDialogUi->fileNameLabel->text();
+ case FileType:
+ return d->qFileDialogUi->fileTypeLabel->text();
+ case Accept:
+ if (acceptMode() == AcceptOpen)
+ button = d->qFileDialogUi->buttonBox->button(QDialogButtonBox::Open);
+ else
+ button = d->qFileDialogUi->buttonBox->button(QDialogButtonBox::Save);
+ if (button)
+ return button->text();
+ case Reject:
+ button = d->qFileDialogUi->buttonBox->button(QDialogButtonBox::Cancel);
+ if (button)
+ return button->text();
+ }
+ return QString();
+}
+
+/*
+ For the native file dialogs
+*/
+
+#if defined(Q_WS_WIN)
+extern QString qt_win_get_open_file_name(const QFileDialogArgs &args,
+ QString *initialDirectory,
+ QString *selectedFilter);
+
+extern QString qt_win_get_save_file_name(const QFileDialogArgs &args,
+ QString *initialDirectory,
+ QString *selectedFilter);
+
+extern QStringList qt_win_get_open_file_names(const QFileDialogArgs &args,
+ QString *initialDirectory,
+ QString *selectedFilter);
+
+extern QString qt_win_get_existing_directory(const QFileDialogArgs &args);
+#endif
+
+/*!
+ This is a convenience static function that returns an existing file
+ selected by the user. If the user presses Cancel, it returns a null
+ string.
+
+ \snippet doc/src/snippets/code/src_gui_dialogs_qfiledialog.cpp 8
+
+ The function creates a modal file dialog with the given \a parent widget.
+ If the parent is not 0, the dialog will be shown centered over the
+ parent widget.
+
+ The file dialog's working directory will be set to \a dir. If \a
+ dir includes a file name, the file will be selected. Only files
+ that match the given \a filter are shown. The filter selected is
+ set to \a selectedFilter. The parameters \a dir, \a
+ selectedFilter, and \a filter may be empty strings. If you want
+ multiple filters, separate them with ';;', for example:
+
+ \code
+ "Images (*.png *.xpm *.jpg);;Text files (*.txt);;XML files (*.xml)"
+ \endcode
+
+ The \a options argument holds various
+ options about how to run the dialog, see the QFileDialog::Option enum for
+ more information on the flags you can pass.
+
+ 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 QFileDialog.
+
+ 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 \c{/usr/tmp} is a symlink to
+ \c{/var/tmp}, the file dialog will change to \c{/var/tmp} after
+ entering \c{/usr/tmp}. If \a options includes DontResolveSymlinks,
+ the file dialog will treat symlinks as regular directories.
+
+ \warning Do not delete \a parent during the execution of the dialog.
+ If you want to do this, you should create the dialog
+ yourself using one of the QFileDialog constructors.
+
+ \sa getOpenFileNames(), getSaveFileName(), getExistingDirectory()
+*/
+QString QFileDialog::getOpenFileName(QWidget *parent,
+ const QString &caption,
+ const QString &dir,
+ const QString &filter,
+ QString *selectedFilter,
+ Options options)
+{
+ if (qt_filedialog_open_filename_hook && !(options & DontUseNativeDialog))
+ return qt_filedialog_open_filename_hook(parent, caption, dir, filter, selectedFilter, options);
+ QFileDialogArgs args;
+ args.parent = parent;
+ args.caption = caption;
+ args.directory = QFileDialogPrivate::workingDirectory(dir);
+ args.selection = QFileDialogPrivate::initialSelection(dir);
+ args.filter = filter;
+ args.mode = ExistingFile;
+ args.options = options;
+#if defined(Q_WS_WIN)
+ if (qt_use_native_dialogs && !(args.options & DontUseNativeDialog)) {
+ return qt_win_get_open_file_name(args, &(args.directory), selectedFilter);
+ }
+#endif
+
+ // create a qt dialog
+ QFileDialog dialog(args);
+ if (selectedFilter)
+ dialog.selectNameFilter(*selectedFilter);
+ if (dialog.exec() == QDialog::Accepted) {
+ if (selectedFilter)
+ *selectedFilter = dialog.selectedFilter();
+ return dialog.selectedFiles().value(0);
+ }
+ return QString();
+}
+
+/*!
+ This is a convenience static function that will return one or more
+ existing files selected by the user.
+
+ \snippet doc/src/snippets/code/src_gui_dialogs_qfiledialog.cpp 9
+
+ This function creates a modal file dialog with the given \a parent
+ widget. If the parent is not 0, the dialog will be shown centered
+ over the parent widget.
+
+ 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. If you
+ need multiple filters, separate them with ';;', for instance:
+
+ \code
+ "Images (*.png *.xpm *.jpg);;Text files (*.txt);;XML files (*.xml)"
+ \endcode
+
+ 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 QFileDialog. On Mac OS X, the \a dir argument
+ is ignored, the native dialog always displays the last visited directory.
+
+ 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 \c{/usr/tmp} is a symlink to
+ \c{/var/tmp}, the file dialog will change to \c{/var/tmp} after
+ entering \c{/usr/tmp}. The \a options argument holds various
+ options about how to run the dialog, see the QFileDialog::Option enum for
+ more information on the flags you can pass.
+
+ Note that if you want to iterate over the list of files, you should
+ iterate over a copy. For example:
+
+ \snippet doc/src/snippets/code/src_gui_dialogs_qfiledialog.cpp 10
+
+ \warning Do not delete \a parent during the execution of the dialog.
+ If you want to do this, you should create the dialog
+ yourself using one of the QFileDialog constructors.
+
+ \sa getOpenFileName(), getSaveFileName(), getExistingDirectory()
+*/
+QStringList QFileDialog::getOpenFileNames(QWidget *parent,
+ const QString &caption,
+ const QString &dir,
+ const QString &filter,
+ QString *selectedFilter,
+ Options options)
+{
+ if (qt_filedialog_open_filenames_hook)
+ return qt_filedialog_open_filenames_hook(parent, caption, dir, filter, selectedFilter, options);
+ QFileDialogArgs args;
+ args.parent = parent;
+ args.caption = caption;
+ args.directory = QFileDialogPrivate::workingDirectory(dir);
+ args.selection = QFileDialogPrivate::initialSelection(dir);
+ args.filter = filter;
+ args.mode = ExistingFiles;
+ args.options = options;
+
+#if defined(Q_WS_WIN)
+ if (qt_use_native_dialogs && !(args.options & DontUseNativeDialog)) {
+ return qt_win_get_open_file_names(args, &(args.directory), selectedFilter);
+ }
+#endif
+
+ // create a qt dialog
+ QFileDialog dialog(args);
+ if (selectedFilter)
+ dialog.selectNameFilter(*selectedFilter);
+ if (dialog.exec() == QDialog::Accepted) {
+ if (selectedFilter)
+ *selectedFilter = dialog.selectedFilter();
+ return dialog.selectedFiles();
+ }
+ return QStringList();
+}
+
+/*!
+ 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 with the given \a parent widget. If the
+ parent is not 0, the dialog will be shown centered over the parent
+ widget.
+
+ \snippet doc/src/snippets/code/src_gui_dialogs_qfiledialog.cpp 11
+
+ The file dialog's working directory will be set to \a dir. If \a
+ dir includes a file name, the file will be selected. Only files that
+ match the \a filter are shown. The filter selected is set to
+ \a selectedFilter. The parameters \a dir, \a selectedFilter, and
+ \a filter may be empty strings. Multiple filters are separated with ';;'.
+ For instance:
+
+ \code
+ "Images (*.png *.xpm *.jpg);;Text files (*.txt);;XML files (*.xml)"
+ \endcode
+
+ The \a options argument holds various
+ options about how to run the dialog, see the QFileDialog::Option enum for
+ more information on the flags you can pass.
+
+ The default filter can be chosen by setting \a selectedFilter to the desired value.
+
+ 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 QFileDialog.
+
+ 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.
+ On Mac OS X, the filter argument is ignored.
+
+ Under Unix/X11, the normal behavior of the file dialog is to resolve
+ and follow symlinks. For example, if \c{/usr/tmp} is a symlink to
+ \c{/var/tmp}, the file dialog will change to \c{/var/tmp} after
+ entering \c{/usr/tmp}. If \a options includes DontResolveSymlinks,
+ the file dialog will treat symlinks as regular directories.
+
+ \warning Do not delete \a parent during the execution of the dialog.
+ If you want to do this, you should create the dialog
+ yourself using one of the QFileDialog constructors.
+
+ \sa getOpenFileName(), getOpenFileNames(), getExistingDirectory()
+*/
+QString QFileDialog::getSaveFileName(QWidget *parent,
+ const QString &caption,
+ const QString &dir,
+ const QString &filter,
+ QString *selectedFilter,
+ Options options)
+{
+ if (qt_filedialog_save_filename_hook)
+ return qt_filedialog_save_filename_hook(parent, caption, dir, filter, selectedFilter, options);
+ QFileDialogArgs args;
+ args.parent = parent;
+ args.caption = caption;
+ args.directory = QFileDialogPrivate::workingDirectory(dir);
+ args.selection = QFileDialogPrivate::initialSelection(dir);
+ args.filter = filter;
+ args.mode = AnyFile;
+ args.options = options;
+
+#if defined(Q_WS_WIN)
+ if (qt_use_native_dialogs && !(args.options & DontUseNativeDialog)) {
+ return qt_win_get_save_file_name(args, &(args.directory), selectedFilter);
+ }
+#endif
+
+ // create a qt dialog
+ QFileDialog dialog(args);
+ dialog.setAcceptMode(AcceptSave);
+ if (selectedFilter)
+ dialog.selectNameFilter(*selectedFilter);
+ if (dialog.exec() == QDialog::Accepted) {
+ if (selectedFilter)
+ *selectedFilter = dialog.selectedFilter();
+ return dialog.selectedFiles().value(0);
+ }
+
+ return QString();
+}
+
+/*!
+ This is a convenience static function that will return an existing
+ directory selected by the user.
+
+ \snippet doc/src/snippets/code/src_gui_dialogs_qfiledialog.cpp 12
+
+ This function creates a modal file dialog with the given \a parent
+ widget. If the parent is not 0, the dialog will be shown centered over
+ the parent widget.
+
+ 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.
+
+ The \a options argument holds various
+ options about how to run the dialog, see the QFileDialog::Option enum for
+ more information on the flags you can pass. Note that \l{QFileDialog::}{ShowDirsOnly}
+ must be set to ensure a native file dialog.
+
+ Under Windows and Mac OS X, this static function will use the native
+ file dialog and not a QFileDialog. On Mac OS X, the \a dir argument
+ is ignored, the native dialog always displays the last visited directory.
+ On Windows CE, if the device has no native file dialog, a QFileDialog
+ will be used.
+
+ Under Unix/X11, the normal behavior of the file dialog is to resolve
+ and follow symlinks. For example, if \c{/usr/tmp} is a symlink to
+ \c{/var/tmp}, the file dialog will change to \c{/var/tmp} after
+ entering \c{/usr/tmp}. If \a options includes DontResolveSymlinks,
+ 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.
+
+ \warning Do not delete \a parent during the execution of the dialog.
+ If you want to do this, you should create the dialog
+ yourself using one of the QFileDialog constructors.
+
+ \sa getOpenFileName(), getOpenFileNames(), getSaveFileName()
+*/
+QString QFileDialog::getExistingDirectory(QWidget *parent,
+ const QString &caption,
+ const QString &dir,
+ Options options)
+{
+ if (qt_filedialog_existing_directory_hook)
+ return qt_filedialog_existing_directory_hook(parent, caption, dir, options);
+ QFileDialogArgs args;
+ args.parent = parent;
+ args.caption = caption;
+ args.directory = QFileDialogPrivate::workingDirectory(dir);
+ args.mode = (options & ShowDirsOnly ? DirectoryOnly : Directory);
+ args.options = options;
+
+#if defined(Q_WS_WIN)
+ if (qt_use_native_dialogs && !(args.options & DontUseNativeDialog) && (options & ShowDirsOnly)
+#if defined(Q_OS_WINCE)
+ && qt_priv_ptr_valid
+#endif
+ ) {
+ return qt_win_get_existing_directory(args);
+ }
+#endif
+
+ // create a qt dialog
+ QFileDialog dialog(args);
+ if (dialog.exec() == QDialog::Accepted) {
+ return dialog.selectedFiles().value(0);
+ }
+ return QString();
+}
+
+inline static QString _qt_get_directory(const QString &path)
+{
+ QFileInfo info = QFileInfo(QDir::current(), path);
+ if (info.exists() && info.isDir())
+ return QDir::cleanPath(info.absoluteFilePath());
+ info.setFile(info.absolutePath());
+ if (info.exists() && info.isDir())
+ return info.absoluteFilePath();
+ return QString();
+}
+/*
+ Get the initial directory path
+
+ \sa initialSelection()
+ */
+QString QFileDialogPrivate::workingDirectory(const QString &path)
+{
+ if (!path.isEmpty()) {
+ QString directory = _qt_get_directory(path);
+ if (!directory.isEmpty())
+ return directory;
+ }
+ QString directory = _qt_get_directory(*lastVisitedDir());
+ if (!directory.isEmpty())
+ return directory;
+ return QDir::currentPath();
+}
+
+/*
+ Get the initial selection given a path. The initial directory
+ can contain both the initial directory and initial selection
+ /home/user/foo.txt
+
+ \sa workingDirectory()
+ */
+QString QFileDialogPrivate::initialSelection(const QString &path)
+{
+ if (!path.isEmpty()) {
+ QFileInfo info(path);
+ if (!info.isDir())
+ return info.fileName();
+ }
+ return QString();
+}
+
+/*!
+ \reimp
+*/
+void QFileDialog::done(int result)
+{
+ Q_D(QFileDialog);
+
+ QDialog::done(result);
+
+ if (d->receiverToDisconnectOnClose) {
+ disconnect(this, d->signalToDisconnectOnClose,
+ d->receiverToDisconnectOnClose, d->memberToDisconnectOnClose);
+ d->receiverToDisconnectOnClose = 0;
+ }
+ d->memberToDisconnectOnClose.clear();
+ d->signalToDisconnectOnClose.clear();
+}
+
+/*!
+ \reimp
+*/
+void QFileDialog::accept()
+{
+ Q_D(QFileDialog);
+ QStringList files = selectedFiles();
+ if (files.isEmpty())
+ return;
+ if (d->nativeDialogInUse){
+ d->emitFilesSelected(files);
+ QDialog::accept();
+ return;
+ }
+
+ QString lineEditText = d->lineEdit()->text();
+ // "hidden feature" type .. and then enter, and it will move up a dir
+ // special case for ".."
+ if (lineEditText == QLatin1String("..")) {
+ d->_q_navigateToParent();
+ bool block = d->qFileDialogUi->fileNameEdit->blockSignals(true);
+ d->lineEdit()->selectAll();
+ d->qFileDialogUi->fileNameEdit->blockSignals(block);
+ return;
+ }
+
+ switch (d->fileMode) {
+ case DirectoryOnly:
+ case Directory: {
+ QString fn = files.first();
+ QFileInfo info(fn);
+ if (!info.exists())
+ info = QFileInfo(d->getEnvironmentVariable(fn));
+ if (!info.exists()) {
+#ifndef QT_NO_MESSAGEBOX
+ QString message = tr("%1\nDirectory not found.\nPlease verify the "
+ "correct directory name was given.");
+ QMessageBox::warning(this, windowTitle(), message.arg(info.fileName()));
+#endif // QT_NO_MESSAGEBOX
+ return;
+ }
+ if (info.isDir()) {
+ d->emitFilesSelected(files);
+ QDialog::accept();
+ }
+ return;
+ }
+
+ case AnyFile: {
+ QString fn = files.first();
+ QFileInfo info(fn);
+ if (info.isDir()) {
+ setDirectory(info.absoluteFilePath());
+ return;
+ }
+
+ if (!info.exists()) {
+ int maxNameLength = d->maxNameLength(info.path());
+ if (maxNameLength >= 0 && info.fileName().length() > maxNameLength)
+ return;
+ }
+
+ // check if we have to ask for permission to overwrite the file
+ if (!info.exists() || !confirmOverwrite() || acceptMode() == AcceptOpen) {
+ d->emitFilesSelected(QStringList(fn));
+ QDialog::accept();
+#ifndef QT_NO_MESSAGEBOX
+ } else {
+ if (QMessageBox::warning(this, windowTitle(),
+ tr("%1 already exists.\nDo you want to replace it?")
+ .arg(info.fileName()),
+ QMessageBox::Yes | QMessageBox::No, QMessageBox::No)
+ == QMessageBox::Yes) {
+ d->emitFilesSelected(QStringList(fn));
+ QDialog::accept();
+ }
+#endif
+ }
+ return;
+ }
+
+ case ExistingFile:
+ case ExistingFiles:
+ for (int i = 0; i < files.count(); ++i) {
+ QFileInfo info(files.at(i));
+ if (!info.exists())
+ info = QFileInfo(d->getEnvironmentVariable(files.at(i)));
+ if (!info.exists()) {
+#ifndef QT_NO_MESSAGEBOX
+ QString message = tr("%1\nFile not found.\nPlease verify the "
+ "correct file name was given.");
+ QMessageBox::warning(this, windowTitle(), message.arg(info.fileName()));
+#endif // QT_NO_MESSAGEBOX
+ return;
+ }
+ if (info.isDir()) {
+ setDirectory(info.absoluteFilePath());
+ d->lineEdit()->clear();
+ return;
+ }
+ }
+ d->emitFilesSelected(files);
+ QDialog::accept();
+ return;
+ }
+}
+
+/*!
+ \internal
+
+ Create widgets, layout and set default values
+*/
+void QFileDialogPrivate::init(const QString &directory, const QString &nameFilter,
+ const QString &caption)
+{
+ Q_Q(QFileDialog);
+ if (!caption.isEmpty()) {
+ useDefaultCaption = false;
+ setWindowTitle = caption;
+ q->setWindowTitle(caption);
+ }
+
+ createWidgets();
+ createMenuActions();
+ retranslateStrings();
+ q->setFileMode(fileMode);
+
+#ifndef QT_NO_SETTINGS
+ QSettings settings(QSettings::UserScope, QLatin1String("Trolltech"));
+ settings.beginGroup(QLatin1String("Qt"));
+ if (!directory.isEmpty())
+ setLastVisitedDirectory(workingDirectory(directory));
+ q->restoreState(settings.value(QLatin1String("filedialog")).toByteArray());
+#endif
+
+#ifdef Q_OS_WINCE
+ qFileDialogUi->lookInLabel->setVisible(false);
+ qFileDialogUi->fileNameLabel->setVisible(false);
+ qFileDialogUi->fileTypeLabel->setVisible(false);
+ qFileDialogUi->sidebar->hide();
+#endif
+ // Default case
+ if (!nameFilter.isEmpty())
+ q->setNameFilter(nameFilter);
+ q->setAcceptMode(QFileDialog::AcceptOpen);
+ q->setDirectory(workingDirectory(directory));
+ q->selectFile(initialSelection(directory));
+
+ _q_updateOkButton();
+ q->resize(q->sizeHint());
+}
+
+/*!
+ \internal
+
+ Create the widgets, set properties and connections
+*/
+void QFileDialogPrivate::createWidgets()
+{
+ Q_Q(QFileDialog);
+ model = new QFileSystemModel(q);
+ model->setObjectName(QLatin1String("qt_filesystem_model"));
+#ifdef Q_WS_MAC
+ model->setNameFilterDisables(true);
+#else
+ model->setNameFilterDisables(false);
+#endif
+ QFileDialog::connect(model, SIGNAL(fileRenamed(const QString &, const QString &, const QString &)), q, SLOT(_q_fileRenamed(const QString &, const QString &, const QString &)));
+ QFileDialog::connect(model, SIGNAL(rootPathChanged(const QString &)),
+ q, SLOT(_q_pathChanged(const QString &)));
+ QFileDialog::connect(model, SIGNAL(rowsInserted(const QModelIndex &, int, int)),
+ q, SLOT(_q_rowsInserted(const QModelIndex &)));
+ model->setReadOnly(false);
+
+ qFileDialogUi = new Ui_QFileDialog();
+ qFileDialogUi->setupUi(q);
+
+ QList<QUrl> initialBookmarks;
+ initialBookmarks << QUrl::fromLocalFile(QLatin1String(""))
+ << QUrl::fromLocalFile(QDir::homePath());
+ qFileDialogUi->sidebar->init(model, initialBookmarks);
+ QFileDialog::connect(qFileDialogUi->sidebar, SIGNAL(goToUrl(const QUrl &)),
+ q, SLOT(_q_goToUrl(const QUrl &)));
+
+ QObject::connect(qFileDialogUi->buttonBox, SIGNAL(accepted()), q, SLOT(accept()));
+ QObject::connect(qFileDialogUi->buttonBox, SIGNAL(rejected()), q, SLOT(reject()));
+
+
+ qFileDialogUi->lookInCombo->init(this);
+ QObject::connect(qFileDialogUi->lookInCombo, SIGNAL(activated(QString)), q, SLOT(_q_goToDirectory(QString)));
+
+ qFileDialogUi->lookInCombo->setInsertPolicy(QComboBox::NoInsert);
+ qFileDialogUi->lookInCombo->setDuplicatesEnabled(false);
+
+ // filename
+ qFileDialogUi->fileNameEdit->init(this);
+#ifndef QT_NO_SHORTCUT
+ qFileDialogUi->fileNameLabel->setBuddy(qFileDialogUi->fileNameEdit);
+#endif
+#ifndef QT_NO_COMPLETER
+ completer = new QFSCompletor(model, q);
+ qFileDialogUi->fileNameEdit->setCompleter(completer);
+ QObject::connect(qFileDialogUi->fileNameEdit, SIGNAL(textChanged(QString)),
+ q, SLOT(_q_autoCompleteFileName(QString)));
+#endif // QT_NO_COMPLETER
+ QObject::connect(qFileDialogUi->fileNameEdit, SIGNAL(textChanged(QString)),
+ q, SLOT(_q_updateOkButton()));
+
+ QObject::connect(qFileDialogUi->fileNameEdit, SIGNAL(returnPressed()), q, SLOT(accept()));
+
+ // filetype
+ qFileDialogUi->fileTypeCombo->setDuplicatesEnabled(false);
+ qFileDialogUi->fileTypeCombo->setSizeAdjustPolicy(QComboBox::AdjustToMinimumContentsLength);
+ qFileDialogUi->fileTypeCombo->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
+ QObject::connect(qFileDialogUi->fileTypeCombo, SIGNAL(activated(int)),
+ q, SLOT(_q_useNameFilter(int)));
+ QObject::connect(qFileDialogUi->fileTypeCombo, SIGNAL(activated(const QString &)),
+ q, SIGNAL(filterSelected(const QString &)));
+
+ qFileDialogUi->listView->init(this);
+ qFileDialogUi->listView->setModel(model);
+ QObject::connect(qFileDialogUi->listView, SIGNAL(activated(QModelIndex)),
+ q, SLOT(_q_enterDirectory(QModelIndex)));
+ QObject::connect(qFileDialogUi->listView, SIGNAL(customContextMenuRequested(QPoint)),
+ q, SLOT(_q_showContextMenu(QPoint)));
+#ifndef QT_NO_SHORTCUT
+ QShortcut *shortcut = new QShortcut(qFileDialogUi->listView);
+ shortcut->setKey(QKeySequence(QLatin1String("Delete")));
+ QObject::connect(shortcut, SIGNAL(activated()), q, SLOT(_q_deleteCurrent()));
+#endif
+
+ qFileDialogUi->treeView->init(this);
+ qFileDialogUi->treeView->setModel(model);
+ QHeaderView *treeHeader = qFileDialogUi->treeView->header();
+ QFontMetrics fm(q->font());
+ treeHeader->resizeSection(0, fm.width(QLatin1String("wwwwwwwwwwwwwwwwwwwwwwwwww")));
+ treeHeader->resizeSection(1, fm.width(QLatin1String("128.88 GB")));
+ treeHeader->resizeSection(2, fm.width(QLatin1String("mp3Folder")));
+ treeHeader->resizeSection(3, fm.width(QLatin1String("10/29/81 02:02PM")));
+ treeHeader->setContextMenuPolicy(Qt::ActionsContextMenu);
+
+ QActionGroup *showActionGroup = new QActionGroup(q);
+ showActionGroup->setExclusive(false);
+ QObject::connect(showActionGroup, SIGNAL(triggered(QAction *)),
+ q, SLOT(_q_showHeader(QAction *)));;
+
+ QAbstractItemModel *abstractModel = model;
+#ifndef QT_NO_PROXYMODEL
+ if (proxyModel)
+ abstractModel = proxyModel;
+#endif
+ for (int i = 1; i < abstractModel->columnCount(QModelIndex()); ++i) {
+ QAction *showHeader = new QAction(showActionGroup);
+ showHeader->setCheckable(true);
+ showHeader->setChecked(true);
+ treeHeader->addAction(showHeader);
+ }
+
+ QItemSelectionModel *selModel = qFileDialogUi->treeView->selectionModel();
+ qFileDialogUi->treeView->setSelectionModel(qFileDialogUi->listView->selectionModel());
+ delete selModel;
+ QObject::connect(qFileDialogUi->treeView, SIGNAL(activated(QModelIndex)),
+ q, SLOT(_q_enterDirectory(QModelIndex)));
+ QObject::connect(qFileDialogUi->treeView, SIGNAL(customContextMenuRequested(QPoint)),
+ q, SLOT(_q_showContextMenu(QPoint)));
+#ifndef QT_NO_SHORTCUT
+ shortcut = new QShortcut(qFileDialogUi->treeView);
+ shortcut->setKey(QKeySequence(QLatin1String("Delete")));
+ QObject::connect(shortcut, SIGNAL(activated()), q, SLOT(_q_deleteCurrent()));
+#endif
+
+ // Selections
+ QItemSelectionModel *selections = qFileDialogUi->listView->selectionModel();
+ QObject::connect(selections, SIGNAL(selectionChanged(const QItemSelection &, const QItemSelection &)),
+ q, SLOT(_q_selectionChanged()));
+ QObject::connect(selections, SIGNAL(currentChanged(QModelIndex,QModelIndex)),
+ q, SLOT(_q_currentChanged(QModelIndex)));
+ qFileDialogUi->splitter->setStretchFactor(qFileDialogUi->splitter->indexOf(qFileDialogUi->splitter->widget(1)), QSizePolicy::Expanding);
+
+ createToolButtons();
+}
+
+void QFileDialogPrivate::_q_showHeader(QAction *action)
+{
+ Q_Q(QFileDialog);
+ QActionGroup *actionGroup = qobject_cast<QActionGroup*>(q->sender());
+ qFileDialogUi->treeView->header()->setSectionHidden(actionGroup->actions().indexOf(action) + 1, !action->isChecked());
+}
+
+#ifndef QT_NO_PROXYMODEL
+/*!
+ \since 4.3
+
+ Sets the model for the views to the given \a proxyModel. This is useful if you
+ want to modify the underlying model; for example, to add columns, filter
+ data or add drives.
+
+ Any existing proxy model will be removed, but not deleted. The file dialog
+ will take ownership of the \a proxyModel.
+
+ \sa proxyModel()
+*/
+void QFileDialog::setProxyModel(QAbstractProxyModel *proxyModel)
+{
+ Q_D(QFileDialog);
+ if ((!proxyModel && !d->proxyModel)
+ || (proxyModel == d->proxyModel))
+ return;
+
+ QModelIndex idx = d->rootIndex();
+ if (d->proxyModel) {
+ disconnect(d->proxyModel, SIGNAL(rowsInserted(const QModelIndex &, int, int)),
+ this, SLOT(_q_rowsInserted(const QModelIndex &)));
+ } else {
+ disconnect(d->model, SIGNAL(rowsInserted(const QModelIndex &, int, int)),
+ this, SLOT(_q_rowsInserted(const QModelIndex &)));
+ }
+
+ if (proxyModel != 0) {
+ proxyModel->setParent(this);
+ d->proxyModel = proxyModel;
+ proxyModel->setSourceModel(d->model);
+ d->qFileDialogUi->listView->setModel(d->proxyModel);
+ d->qFileDialogUi->treeView->setModel(d->proxyModel);
+ connect(d->proxyModel, SIGNAL(rowsInserted(const QModelIndex &, int, int)),
+ this, SLOT(_q_rowsInserted(const QModelIndex &)));
+ } else {
+ d->proxyModel = 0;
+ d->qFileDialogUi->listView->setModel(d->model);
+ d->qFileDialogUi->treeView->setModel(d->model);
+ connect(d->model, SIGNAL(rowsInserted(const QModelIndex &, int, int)),
+ this, SLOT(_q_rowsInserted(const QModelIndex &)));
+ }
+ QItemSelectionModel *selModel = d->qFileDialogUi->treeView->selectionModel();
+ d->qFileDialogUi->treeView->setSelectionModel(d->qFileDialogUi->listView->selectionModel());
+ delete selModel;
+ d->setRootIndex(idx);
+
+ // reconnect selection
+ QItemSelectionModel *selections = d->qFileDialogUi->listView->selectionModel();
+ QObject::connect(selections, SIGNAL(selectionChanged(const QItemSelection &, const QItemSelection &)),
+ this, SLOT(_q_selectionChanged()));
+ QObject::connect(selections, SIGNAL(currentChanged(QModelIndex,QModelIndex)),
+ this, SLOT(_q_currentChanged(QModelIndex)));
+}
+
+/*!
+ Returns the proxy model used by the file dialog. By default no proxy is set.
+
+ \sa setProxyModel()
+*/
+QAbstractProxyModel *QFileDialog::proxyModel() const
+{
+ Q_D(const QFileDialog);
+ return d->proxyModel;
+}
+#endif // QT_NO_PROXYMODEL
+
+/*!
+ \internal
+
+ Create tool buttons, set properties and connections
+*/
+void QFileDialogPrivate::createToolButtons()
+{
+ Q_Q(QFileDialog);
+ qFileDialogUi->backButton->setIcon(q->style()->standardIcon(QStyle::SP_ArrowBack, 0, q));
+ qFileDialogUi->backButton->setAutoRaise(true);
+ qFileDialogUi->backButton->setEnabled(false);
+ QObject::connect(qFileDialogUi->backButton, SIGNAL(clicked()), q, SLOT(_q_navigateBackward()));
+
+ qFileDialogUi->forwardButton->setIcon(q->style()->standardIcon(QStyle::SP_ArrowForward, 0, q));
+ qFileDialogUi->forwardButton->setAutoRaise(true);
+ qFileDialogUi->forwardButton->setEnabled(false);
+ QObject::connect(qFileDialogUi->forwardButton, SIGNAL(clicked()), q, SLOT(_q_navigateForward()));
+
+ qFileDialogUi->toParentButton->setIcon(q->style()->standardIcon(QStyle::SP_FileDialogToParent, 0, q));
+ qFileDialogUi->toParentButton->setAutoRaise(true);
+ qFileDialogUi->toParentButton->setEnabled(false);
+ QObject::connect(qFileDialogUi->toParentButton, SIGNAL(clicked()), q, SLOT(_q_navigateToParent()));
+
+ qFileDialogUi->listModeButton->setIcon(q->style()->standardIcon(QStyle::SP_FileDialogListView, 0, q));
+ qFileDialogUi->listModeButton->setAutoRaise(true);
+ qFileDialogUi->listModeButton->setDown(true);
+ QObject::connect(qFileDialogUi->listModeButton, SIGNAL(clicked()), q, SLOT(_q_showListView()));
+
+ qFileDialogUi->detailModeButton->setIcon(q->style()->standardIcon(QStyle::SP_FileDialogDetailedView, 0, q));
+ qFileDialogUi->detailModeButton->setAutoRaise(true);
+ QObject::connect(qFileDialogUi->detailModeButton, SIGNAL(clicked()), q, SLOT(_q_showDetailsView()));
+
+ QSize toolSize(qFileDialogUi->fileNameEdit->sizeHint().height(), qFileDialogUi->fileNameEdit->sizeHint().height());
+ qFileDialogUi->backButton->setFixedSize(toolSize);
+ qFileDialogUi->listModeButton->setFixedSize(toolSize);
+ qFileDialogUi->detailModeButton->setFixedSize(toolSize);
+ qFileDialogUi->forwardButton->setFixedSize(toolSize);
+ qFileDialogUi->toParentButton->setFixedSize(toolSize);
+
+ qFileDialogUi->newFolderButton->setIcon(q->style()->standardIcon(QStyle::SP_FileDialogNewFolder, 0, q));
+ qFileDialogUi->newFolderButton->setFixedSize(toolSize);
+ qFileDialogUi->newFolderButton->setAutoRaise(true);
+ qFileDialogUi->newFolderButton->setEnabled(false);
+ QObject::connect(qFileDialogUi->newFolderButton, SIGNAL(clicked()), q, SLOT(_q_createDirectory()));
+}
+
+/*!
+ \internal
+
+ Create actions which will be used in the right click.
+*/
+void QFileDialogPrivate::createMenuActions()
+{
+ Q_Q(QFileDialog);
+
+ QAction *goHomeAction = new QAction(q);
+#ifndef QT_NO_SHORTCUT
+ goHomeAction->setShortcut(Qt::CTRL + Qt::Key_H + Qt::SHIFT);
+#endif
+ QObject::connect(goHomeAction, SIGNAL(triggered()), q, SLOT(_q_goHome()));
+ q->addAction(goHomeAction);
+
+ // ### TODO add Desktop & Computer actions
+
+ QAction *goToParent = new QAction(q);
+ goToParent->setObjectName(QLatin1String("qt_goto_parent_action"));
+#ifndef QT_NO_SHORTCUT
+ goToParent->setShortcut(Qt::CTRL + Qt::UpArrow);
+#endif
+ QObject::connect(goToParent, SIGNAL(triggered()), q, SLOT(_q_navigateToParent()));
+ q->addAction(goToParent);
+
+ renameAction = new QAction(q);
+ renameAction->setEnabled(false);
+ renameAction->setObjectName(QLatin1String("qt_rename_action"));
+ QObject::connect(renameAction, SIGNAL(triggered()), q, SLOT(_q_renameCurrent()));
+
+ deleteAction = new QAction(q);
+ deleteAction->setEnabled(false);
+ deleteAction->setObjectName(QLatin1String("qt_delete_action"));
+ QObject::connect(deleteAction, SIGNAL(triggered()), q, SLOT(_q_deleteCurrent()));
+
+ showHiddenAction = new QAction(q);
+ showHiddenAction->setObjectName(QLatin1String("qt_show_hidden_action"));
+ showHiddenAction->setCheckable(true);
+ QObject::connect(showHiddenAction, SIGNAL(triggered()), q, SLOT(_q_showHidden()));
+
+ newFolderAction = new QAction(q);
+ newFolderAction->setObjectName(QLatin1String("qt_new_folder_action"));
+ QObject::connect(newFolderAction, SIGNAL(triggered()), q, SLOT(_q_createDirectory()));
+}
+
+void QFileDialogPrivate::_q_goHome()
+{
+ Q_Q(QFileDialog);
+ q->setDirectory(QDir::homePath());
+}
+
+/*!
+ \internal
+
+ Update history with new path, buttons, and combo
+*/
+void QFileDialogPrivate::_q_pathChanged(const QString &newPath)
+{
+ Q_Q(QFileDialog);
+ QDir dir(model->rootDirectory());
+ qFileDialogUi->toParentButton->setEnabled(dir.exists());
+ qFileDialogUi->sidebar->selectUrl(QUrl::fromLocalFile(newPath));
+ q->setHistory(qFileDialogUi->lookInCombo->history());
+
+ if (currentHistoryLocation < 0 || currentHistory.value(currentHistoryLocation) != QDir::toNativeSeparators(newPath)) {
+ while (currentHistoryLocation >= 0 && currentHistoryLocation + 1 < currentHistory.count()) {
+ currentHistory.removeLast();
+ }
+ currentHistory.append(QDir::toNativeSeparators(newPath));
+ ++currentHistoryLocation;
+ }
+ qFileDialogUi->forwardButton->setEnabled(currentHistory.size() - currentHistoryLocation > 1);
+ qFileDialogUi->backButton->setEnabled(currentHistoryLocation > 0);
+}
+
+/*!
+ \internal
+
+ Navigates to the last directory viewed in the dialog.
+*/
+void QFileDialogPrivate::_q_navigateBackward()
+{
+ Q_Q(QFileDialog);
+ if (!currentHistory.isEmpty() && currentHistoryLocation > 0) {
+ --currentHistoryLocation;
+ QString previousHistory = currentHistory.at(currentHistoryLocation);
+ q->setDirectory(previousHistory);
+ }
+}
+
+/*!
+ \internal
+
+ Navigates to the last directory viewed in the dialog.
+*/
+void QFileDialogPrivate::_q_navigateForward()
+{
+ Q_Q(QFileDialog);
+ if (!currentHistory.isEmpty() && currentHistoryLocation < currentHistory.size() - 1) {
+ ++currentHistoryLocation;
+ QString nextHistory = currentHistory.at(currentHistoryLocation);
+ q->setDirectory(nextHistory);
+ }
+}
+
+/*!
+ \internal
+
+ Navigates to the parent directory of the currently displayed directory
+ in the dialog.
+*/
+void QFileDialogPrivate::_q_navigateToParent()
+{
+ Q_Q(QFileDialog);
+ QDir dir(model->rootDirectory());
+ QString newDirectory;
+ if (dir.isRoot()) {
+ newDirectory = model->myComputer().toString();
+ } else {
+ dir.cdUp();
+ newDirectory = dir.absolutePath();
+ }
+ q->setDirectory(newDirectory);
+ emit q->directoryEntered(newDirectory);
+}
+
+/*!
+ \internal
+
+ Creates a new directory, first asking the user for a suitable name.
+*/
+void QFileDialogPrivate::_q_createDirectory()
+{
+ Q_Q(QFileDialog);
+ qFileDialogUi->listView->clearSelection();
+
+ QString newFolderString = QFileDialog::tr("New Folder");
+ QString folderName = newFolderString;
+ QString prefix = q->directory().absolutePath() + QDir::separator();
+ if (QFile::exists(prefix + folderName)) {
+ qlonglong suffix = 2;
+ while (QFile::exists(prefix + folderName)) {
+ folderName = newFolderString + QString::number(suffix++);
+ }
+ }
+
+ QModelIndex parent = rootIndex();
+ QModelIndex index = model->mkdir(parent, folderName);
+ if (!index.isValid())
+ return;
+
+ index = select(index);
+ if (index.isValid()) {
+ qFileDialogUi->treeView->setCurrentIndex(index);
+ currentView()->edit(index);
+ }
+}
+
+void QFileDialogPrivate::_q_showListView()
+{
+ qFileDialogUi->listModeButton->setDown(true);
+ qFileDialogUi->detailModeButton->setDown(false);
+ qFileDialogUi->treeView->hide();
+ qFileDialogUi->listView->show();
+ qFileDialogUi->stackedWidget->setCurrentWidget(qFileDialogUi->listView->parentWidget());
+ qFileDialogUi->listView->doItemsLayout();
+}
+
+void QFileDialogPrivate::_q_showDetailsView()
+{
+ qFileDialogUi->listModeButton->setDown(false);
+ qFileDialogUi->detailModeButton->setDown(true);
+ qFileDialogUi->listView->hide();
+ qFileDialogUi->treeView->show();
+ qFileDialogUi->stackedWidget->setCurrentWidget(qFileDialogUi->treeView->parentWidget());
+ qFileDialogUi->treeView->doItemsLayout();
+}
+
+/*!
+ \internal
+
+ Show the context menu for the file/dir under position
+*/
+void QFileDialogPrivate::_q_showContextMenu(const QPoint &position)
+{
+#ifdef QT_NO_MENU
+ Q_UNUSED(position);
+#else
+ Q_Q(QFileDialog);
+ QAbstractItemView *view = 0;
+ if (q->viewMode() == QFileDialog::Detail)
+ view = qFileDialogUi->treeView;
+ else
+ view = qFileDialogUi->listView;
+ QModelIndex index = view->indexAt(position);
+ index = mapToSource(index.sibling(index.row(), 0));
+
+ QMenu menu(view);
+ if (index.isValid()) {
+ // file context menu
+ QFile::Permissions p(index.parent().data(QFileSystemModel::FilePermissions).toInt());
+ renameAction->setEnabled(p & QFile::WriteUser);
+ menu.addAction(renameAction);
+ deleteAction->setEnabled(p & QFile::WriteUser);
+ menu.addAction(deleteAction);
+ menu.addSeparator();
+ }
+ menu.addAction(showHiddenAction);
+ if (qFileDialogUi->newFolderButton->isVisible()) {
+ newFolderAction->setEnabled(qFileDialogUi->newFolderButton->isEnabled());
+ menu.addAction(newFolderAction);
+ }
+ menu.exec(view->viewport()->mapToGlobal(position));
+#endif // QT_NO_MENU
+}
+
+/*!
+ \internal
+*/
+void QFileDialogPrivate::_q_renameCurrent()
+{
+ Q_Q(QFileDialog);
+ QModelIndex index = qFileDialogUi->listView->currentIndex();
+ index = index.sibling(index.row(), 0);
+ if (q->viewMode() == QFileDialog::List)
+ qFileDialogUi->listView->edit(index);
+ else
+ qFileDialogUi->treeView->edit(index);
+}
+
+bool QFileDialogPrivate::removeDirectory(const QString &path)
+{
+ QModelIndex modelIndex = model->index(path);
+ return model->remove(modelIndex);
+}
+
+/*!
+ \internal
+
+ Deletes the currently selected item in the dialog.
+*/
+void QFileDialogPrivate::_q_deleteCurrent()
+{
+ if (model->isReadOnly())
+ return;
+
+ QModelIndexList list = qFileDialogUi->listView->selectionModel()->selectedRows();
+ for (int i = list.count() - 1; i >= 0; --i) {
+ QModelIndex index = list.at(i);
+ if (index == qFileDialogUi->listView->rootIndex())
+ continue;
+
+ index = mapToSource(index.sibling(index.row(), 0));
+ if (!index.isValid())
+ continue;
+
+ QString fileName = index.data(QFileSystemModel::FileNameRole).toString();
+ QString filePath = index.data(QFileSystemModel::FilePathRole).toString();
+ bool isDir = model->isDir(index);
+
+ QFile::Permissions p(index.parent().data(QFileSystemModel::FilePermissions).toInt());
+#ifndef QT_NO_MESSAGEBOX
+ Q_Q(QFileDialog);
+ if (!(p & QFile::WriteUser) && (QMessageBox::warning(q_func(), q_func()->windowTitle(),
+ QFileDialog::tr("'%1' is write protected.\nDo you want to delete it anyway?")
+ .arg(fileName),
+ QMessageBox::Yes | QMessageBox::No, QMessageBox::No) == QMessageBox::No))
+ return;
+ else if (QMessageBox::warning(q_func(), q_func()->windowTitle(),
+ QFileDialog::tr("Are sure you want to delete '%1'?")
+ .arg(fileName),
+ QMessageBox::Yes | QMessageBox::No, QMessageBox::No) == QMessageBox::No)
+ return;
+
+#else
+ if (!(p & QFile::WriteUser))
+ return;
+#endif // QT_NO_MESSAGEBOX
+
+ // the event loop has run, we can NOT reuse index because the model might have removed it.
+ if (isDir) {
+ if (!removeDirectory(filePath)) {
+#ifndef QT_NO_MESSAGEBOX
+ QMessageBox::warning(q, q->windowTitle(),
+ QFileDialog::tr("Could not delete directory."));
+#endif
+ }
+ } else {
+ model->remove(index);
+ }
+ }
+}
+
+void QFileDialogPrivate::_q_autoCompleteFileName(const QString &text)
+{
+ if (text.startsWith(QLatin1String("//")) || text.startsWith(QLatin1String("\\"))) {
+ qFileDialogUi->listView->selectionModel()->clearSelection();
+ return;
+ }
+
+ QStringList multipleFiles = typedFiles();
+ if (multipleFiles.count() > 0) {
+ QModelIndexList oldFiles = qFileDialogUi->listView->selectionModel()->selectedRows();
+ QModelIndexList newFiles;
+ for (int i = 0; i < multipleFiles.count(); ++i) {
+ QModelIndex idx = model->index(multipleFiles.at(i));
+ if (oldFiles.contains(idx))
+ oldFiles.removeAll(idx);
+ else
+ newFiles.append(idx);
+ }
+ for (int i = 0; i < newFiles.count(); ++i)
+ select(newFiles.at(i));
+ if (lineEdit()->hasFocus())
+ for (int i = 0; i < oldFiles.count(); ++i)
+ qFileDialogUi->listView->selectionModel()->select(oldFiles.at(i),
+ QItemSelectionModel::Toggle | QItemSelectionModel::Rows);
+ }
+}
+
+/*!
+ \internal
+*/
+void QFileDialogPrivate::_q_updateOkButton()
+{
+ Q_Q(QFileDialog);
+ QPushButton *button = qFileDialogUi->buttonBox->button((acceptMode == QFileDialog::AcceptOpen)
+ ? QDialogButtonBox::Open : QDialogButtonBox::Save);
+ if (!button)
+ return;
+
+ bool enableButton = true;
+ bool isOpenDirectory = false;
+
+ QStringList files = q->selectedFiles();
+ QString lineEditText = lineEdit()->text();
+
+ if (lineEditText.startsWith(QLatin1String("//")) || lineEditText.startsWith(QLatin1String("\\"))) {
+ button->setEnabled(true);
+ if (acceptMode == QFileDialog::AcceptSave)
+ button->setText(isOpenDirectory ? QFileDialog::tr("&Open") : acceptLabel);
+ return;
+ }
+
+ if (files.isEmpty()) {
+ enableButton = false;
+ } else if (lineEditText == QLatin1String("..")) {
+ isOpenDirectory = true;
+ } else {
+ switch (fileMode) {
+ case QFileDialog::DirectoryOnly:
+ case QFileDialog::Directory: {
+ QString fn = files.first();
+ QModelIndex idx = model->index(fn);
+ if (!idx.isValid())
+ idx = model->index(getEnvironmentVariable(fn));
+ if (!idx.isValid() || !model->isDir(idx))
+ enableButton = false;
+ break;
+ }
+ case QFileDialog::AnyFile: {
+ QString fn = files.first();
+ QFileInfo info(fn);
+ QModelIndex idx = model->index(fn);
+ QString fileDir;
+ QString fileName;
+ if (info.isDir()) {
+ fileDir = info.canonicalFilePath();
+ } else {
+ fileDir = fn.mid(0, fn.lastIndexOf(QLatin1Char('/')));
+ fileName = fn.mid(fileDir.length() + 1);
+ }
+ if (lineEditText.contains(QLatin1String(".."))) {
+ fileDir = info.canonicalFilePath();
+ fileName = info.fileName();
+ }
+
+ if (fileDir == q->directory().canonicalPath() && fileName.isEmpty()) {
+ enableButton = false;
+ break;
+ }
+ if (idx.isValid() && model->isDir(idx)) {
+ isOpenDirectory = true;
+ enableButton = true;
+ break;
+ }
+ if (!idx.isValid()) {
+ int maxLength = maxNameLength(fileDir);
+ enableButton = maxLength < 0 || fileName.length() <= maxLength;
+ }
+ break;
+ }
+ case QFileDialog::ExistingFile:
+ case QFileDialog::ExistingFiles:
+ for (int i = 0; i < files.count(); ++i) {
+ QModelIndex idx = model->index(files.at(i));
+ if (!idx.isValid())
+ idx = model->index(getEnvironmentVariable(files.at(i)));
+ if (!idx.isValid()) {
+ enableButton = false;
+ break;
+ }
+ if (idx.isValid() && model->isDir(idx)) {
+ isOpenDirectory = true;
+ break;
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ button->setEnabled(enableButton);
+ if (acceptMode == QFileDialog::AcceptSave)
+ button->setText(isOpenDirectory ? QFileDialog::tr("&Open") : acceptLabel);
+}
+
+/*!
+ \internal
+*/
+void QFileDialogPrivate::_q_currentChanged(const QModelIndex &index)
+{
+ _q_updateOkButton();
+ emit q_func()->currentChanged(index.data(QFileSystemModel::FilePathRole).toString());
+}
+
+/*!
+ \internal
+
+ This is called when the user double clicks on a file with the corresponding
+ model item \a index.
+*/
+void QFileDialogPrivate::_q_enterDirectory(const QModelIndex &index)
+{
+ Q_Q(QFileDialog);
+ // My Computer or a directory
+ QString path = index.data(QFileSystemModel::FilePathRole).toString();
+ if (path.isEmpty() || model->isDir(index)) {
+ q->setDirectory(path);
+ emit q->directoryEntered(path);
+ if (fileMode == QFileDialog::Directory
+ || fileMode == QFileDialog::DirectoryOnly) {
+ // ### find out why you have to do both of these.
+ lineEdit()->setText(QString());
+ lineEdit()->clear();
+ }
+ } else {
+ q->accept();
+ }
+}
+
+/*!
+ \internal
+
+ Changes the file dialog's current directory to the one specified
+ by \a path.
+*/
+void QFileDialogPrivate::_q_goToDirectory(const QString &path)
+{
+ #ifndef QT_NO_MESSAGEBOX
+ Q_Q(QFileDialog);
+#endif
+ QModelIndex index = qFileDialogUi->lookInCombo->model()->index(qFileDialogUi->lookInCombo->currentIndex(),
+ qFileDialogUi->lookInCombo->modelColumn(),
+ qFileDialogUi->lookInCombo->rootModelIndex());
+ QString path2 = path;
+ if (!index.isValid())
+ index = mapFromSource(model->index(getEnvironmentVariable(path)));
+ else {
+ path2 = index.data(UrlRole).toUrl().toLocalFile();
+ index = mapFromSource(model->index(path2));
+ }
+ QDir dir(path2);
+ if (!dir.exists())
+ dir = getEnvironmentVariable(path2);
+
+ if (dir.exists() || path2.isEmpty() || path2 == model->myComputer().toString()) {
+ _q_enterDirectory(index);
+#ifndef QT_NO_MESSAGEBOX
+ } else {
+ QString message = QFileDialog::tr("%1\nDirectory not found.\nPlease verify the "
+ "correct directory name was given.");
+ QMessageBox::warning(q, q->windowTitle(), message.arg(path2));
+#endif // QT_NO_MESSAGEBOX
+ }
+}
+
+const char *qt_file_dialog_filter_reg_exp =
+ "^([^()]*)\\(([a-zA-Z0-9_.*? +;#\\-\\[\\]@\\{\\}/!<>\\$%&=^~:\\|]*)\\)$";
+
+// Makes a list of filters from a normal filter string "Image Files (*.png *.jpg)"
+QStringList qt_clean_filter_list(const QString &filter)
+{
+ QRegExp regexp(QString::fromLatin1(qt_file_dialog_filter_reg_exp));
+ QString f = filter;
+ int i = regexp.indexIn(f);
+ if (i >= 0)
+ f = regexp.cap(2);
+ return f.split(QLatin1Char(' '), QString::SkipEmptyParts);
+}
+
+/*!
+ \internal
+
+ Sets the current name filter to be nameFilter and
+ update the qFileDialogUi->fileNameEdit when in AcceptSave mode with the new extension.
+*/
+void QFileDialogPrivate::_q_useNameFilter(int index)
+{
+ if (index == nameFilters.size()) {
+ QAbstractItemModel *comboModel = qFileDialogUi->fileTypeCombo->model();
+ nameFilters.append(comboModel->index(comboModel->rowCount() - 1, 0).data().toString());
+ }
+
+ QString nameFilter = nameFilters.at(index);
+ QStringList newNameFilters = qt_clean_filter_list(nameFilter);
+ if (acceptMode == QFileDialog::AcceptSave) {
+ QString newNameFilterExtension;
+ if (newNameFilters.count() > 0)
+ newNameFilterExtension = QFileInfo(newNameFilters.at(0)).suffix();
+
+ QString fileName = lineEdit()->text();
+ const QString fileNameExtension = QFileInfo(fileName).suffix();
+ if (!fileNameExtension.isEmpty() && !newNameFilterExtension.isEmpty()) {
+ const int fileNameExtensionLength = fileNameExtension.count();
+ fileName.replace(fileName.count() - fileNameExtensionLength,
+ fileNameExtensionLength, newNameFilterExtension);
+ lineEdit()->setText(fileName);
+ }
+ }
+
+ model->setNameFilters(newNameFilters);
+}
+
+/*!
+ \internal
+
+ This is called when the model index corresponding to the current file is changed
+ from \a index to \a current.
+*/
+void QFileDialogPrivate::_q_selectionChanged()
+{
+ QModelIndexList indexes = qFileDialogUi->listView->selectionModel()->selectedRows();
+ bool stripDirs = (fileMode != QFileDialog::DirectoryOnly && fileMode != QFileDialog::Directory);
+
+ QStringList allFiles;
+ for (int i = 0; i < indexes.count(); ++i) {
+ if (stripDirs && model->isDir(mapToSource(indexes.at(i))))
+ continue;
+ allFiles.append(indexes.at(i).data().toString());
+ }
+ if (allFiles.count() > 1)
+ for (int i = 0; i < allFiles.count(); ++i) {
+ allFiles.replace(i, QString(QLatin1Char('"') + allFiles.at(i) + QLatin1Char('"')));
+ }
+
+ QString finalFiles = allFiles.join(QLatin1String(" "));
+ if (!finalFiles.isEmpty() && !lineEdit()->hasFocus() && lineEdit()->isVisible())
+ lineEdit()->setText(finalFiles);
+ else
+ _q_updateOkButton();
+}
+
+/*!
+ \internal
+
+ Includes hidden files and directories in the items displayed in the dialog.
+*/
+void QFileDialogPrivate::_q_showHidden()
+{
+ Q_Q(QFileDialog);
+ QDir::Filters dirFilters = q->filter();
+ if (showHiddenAction->isChecked())
+ dirFilters |= QDir::Hidden;
+ else
+ dirFilters &= ~QDir::Hidden;
+ q->setFilter(dirFilters);
+}
+
+/*!
+ \internal
+
+ When parent is root and rows have been inserted when none was there before
+ then select the first one.
+*/
+void QFileDialogPrivate::_q_rowsInserted(const QModelIndex &parent)
+{
+ if (!qFileDialogUi->treeView
+ || parent != qFileDialogUi->treeView->rootIndex()
+ || !qFileDialogUi->treeView->selectionModel()
+ || qFileDialogUi->treeView->selectionModel()->hasSelection()
+ || qFileDialogUi->treeView->model()->rowCount(parent) == 0)
+ return;
+}
+
+void QFileDialogPrivate::_q_fileRenamed(const QString &path, const QString oldName, const QString newName)
+{
+ if (fileMode == QFileDialog::Directory || fileMode == QFileDialog::DirectoryOnly) {
+ if (path == rootPath() && lineEdit()->text() == oldName)
+ lineEdit()->setText(newName);
+ }
+}
+
+/*!
+ \internal
+
+ For the list and tree view watch keys to goto parent and back in the history
+
+ returns true if handled
+*/
+bool QFileDialogPrivate::itemViewKeyboardEvent(QKeyEvent *event) {
+
+ Q_Q(QFileDialog);
+ switch (event->key()) {
+ case Qt::Key_Backspace:
+ _q_navigateToParent();
+ return true;
+ case Qt::Key_Back:
+#ifdef QT_KEYPAD_NAVIGATION
+ if (QApplication::keypadNavigationEnabled())
+ return false;
+#endif
+ case Qt::Key_Left:
+ if (event->key() == Qt::Key_Back || event->modifiers() == Qt::AltModifier) {
+ _q_navigateBackward();
+ return true;
+ }
+ break;
+ case Qt::Key_Escape:
+ q->hide();
+ return true;
+ default:
+ break;
+ }
+ return false;
+}
+
+QString QFileDialogPrivate::getEnvironmentVariable(const QString &string)
+{
+#ifdef Q_OS_UNIX
+ if (string.size() > 1 && string.startsWith(QLatin1Char('$'))) {
+ return QString::fromLocal8Bit(getenv(string.mid(1).toLatin1().constData()));
+ }
+#else
+ if (string.size() > 2 && string.startsWith(QLatin1Char('%')) && string.endsWith(QLatin1Char('%'))) {
+ return QString::fromLocal8Bit(qgetenv(string.mid(1, string.size() - 2).toLatin1().constData()));
+ }
+#endif
+ return string;
+}
+
+void QFileDialogComboBox::init(QFileDialogPrivate *d_pointer) {
+ d_ptr = d_pointer;
+ urlModel = new QUrlModel(this);
+ urlModel->showFullPath = true;
+ urlModel->setFileSystemModel(d_ptr->model);
+ setModel(urlModel);
+}
+
+void QFileDialogComboBox::showPopup()
+{
+ if (model()->rowCount() > 1)
+ QComboBox::showPopup();
+
+ urlModel->setUrls(QList<QUrl>());
+ QList<QUrl> list;
+ QModelIndex idx = d_ptr->model->index(d_ptr->rootPath());
+ while (idx.isValid()) {
+ QUrl url = QUrl::fromLocalFile(idx.data(QFileSystemModel::FilePathRole).toString());
+ if (url.isValid())
+ list.append(url);
+ idx = idx.parent();
+ }
+ // add "my computer"
+ list.append(QUrl::fromLocalFile(QLatin1String("")));
+ urlModel->addUrls(list, 0);
+ idx = model()->index(model()->rowCount() - 1, 0);
+
+ // append history
+ QList<QUrl> urls;
+ for (int i = 0; i < m_history.count(); ++i) {
+ QUrl path = QUrl::fromLocalFile(m_history.at(i));
+ if (!urls.contains(path))
+ urls.prepend(path);
+ }
+ if (urls.count() > 0) {
+ model()->insertRow(model()->rowCount());
+ idx = model()->index(model()->rowCount()-1, 0);
+ // ### TODO maybe add a horizontal line before this
+ model()->setData(idx, QFileDialog::tr("Recent Places"));
+ QStandardItemModel *m = qobject_cast<QStandardItemModel*>(model());
+ if (m) {
+ Qt::ItemFlags flags = m->flags(idx);
+ flags &= ~Qt::ItemIsEnabled;
+ m->item(idx.row(), idx.column())->setFlags(flags);
+ }
+ urlModel->addUrls(urls, -1, false);
+ }
+ setCurrentIndex(0);
+
+ QComboBox::showPopup();
+}
+
+// Exact same as QComboBox::paintEvent(), except we elide the text.
+void QFileDialogComboBox::paintEvent(QPaintEvent *)
+{
+ QStylePainter painter(this);
+ painter.setPen(palette().color(QPalette::Text));
+
+ // draw the combobox frame, focusrect and selected etc.
+ QStyleOptionComboBox opt;
+ initStyleOption(&opt);
+
+ QRect editRect = style()->subControlRect(QStyle::CC_ComboBox, &opt,
+ QStyle::SC_ComboBoxEditField, this);
+ int size = editRect.width() - opt.iconSize.width() - 4;
+ opt.currentText = opt.fontMetrics.elidedText(opt.currentText, Qt::ElideMiddle, size);
+ painter.drawComplexControl(QStyle::CC_ComboBox, opt);
+
+ // draw the icon and text
+ painter.drawControl(QStyle::CE_ComboBoxLabel, opt);
+}
+
+QFileDialogListView::QFileDialogListView(QWidget *parent) : QListView(parent)
+{
+}
+
+void QFileDialogListView::init(QFileDialogPrivate *d_pointer)
+{
+ d_ptr = d_pointer;
+ setSelectionBehavior(QAbstractItemView::SelectRows);
+ setWrapping(true);
+ setResizeMode(QListView::Adjust);
+ setEditTriggers(QAbstractItemView::EditKeyPressed);
+ setContextMenuPolicy(Qt::CustomContextMenu);
+#ifndef QT_NO_DRAGANDDROP
+ setDragDropMode(QAbstractItemView::InternalMove);
+#endif
+}
+
+QSize QFileDialogListView::sizeHint() const
+{
+ int height = qMax(10, sizeHintForRow(0));
+ return QSize(QListView::sizeHint().width() * 2, height * 30);
+}
+
+void QFileDialogListView::keyPressEvent(QKeyEvent *e)
+{
+ if (!d_ptr->itemViewKeyboardEvent(e)) {
+ QListView::keyPressEvent(e);
+ }
+ e->accept();
+}
+
+QFileDialogTreeView::QFileDialogTreeView(QWidget *parent) : QTreeView(parent)
+{
+}
+
+void QFileDialogTreeView::init(QFileDialogPrivate *d_pointer)
+{
+ d_ptr = d_pointer;
+ setSelectionBehavior(QAbstractItemView::SelectRows);
+ setRootIsDecorated(false);
+ setItemsExpandable(false);
+ setSortingEnabled(true);
+ header()->setSortIndicator(0, Qt::AscendingOrder);
+ header()->setStretchLastSection(false);
+ setTextElideMode(Qt::ElideMiddle);
+ setEditTriggers(QAbstractItemView::EditKeyPressed);
+ setContextMenuPolicy(Qt::CustomContextMenu);
+#ifndef QT_NO_DRAGANDDROP
+ setDragDropMode(QAbstractItemView::InternalMove);
+#endif
+}
+
+void QFileDialogTreeView::keyPressEvent(QKeyEvent *e)
+{
+ if (!d_ptr->itemViewKeyboardEvent(e)) {
+ QTreeView::keyPressEvent(e);
+ }
+ e->accept();
+}
+
+QSize QFileDialogTreeView::sizeHint() const
+{
+ int height = qMax(10, sizeHintForRow(0));
+ QSize sizeHint = header()->sizeHint();
+ return QSize(sizeHint.width() * 4, height * 30);
+}
+
+/*!
+ // FIXME: this is a hack to avoid propagating key press events
+ // to the dialog and from there to the "Ok" button
+*/
+void QFileDialogLineEdit::keyPressEvent(QKeyEvent *e)
+{
+ int key = e->key();
+ QLineEdit::keyPressEvent(e);
+ if (key != Qt::Key_Escape)
+ e->accept();
+ if (hideOnEsc && (key == Qt::Key_Escape || key == Qt::Key_Return || key == Qt::Key_Enter)) {
+ e->accept();
+ hide();
+ d_ptr->currentView()->setFocus(Qt::ShortcutFocusReason);
+ }
+}
+
+#ifndef QT_NO_COMPLETER
+
+QString QFSCompletor::pathFromIndex(const QModelIndex &index) const
+{
+ const QFileSystemModel *dirModel = static_cast<const QFileSystemModel *>(model());
+ QString currentLocation = dirModel->rootPath();
+ QString path = index.data(QFileSystemModel::FilePathRole).toString();
+ if (!currentLocation.isEmpty() && path.startsWith(currentLocation)) {
+ return path.mid(currentLocation.length() + 1);
+ }
+ return index.data(QFileSystemModel::FilePathRole).toString();
+}
+
+QStringList QFSCompletor::splitPath(const QString &path) const
+{
+ if (path.isEmpty())
+ return QStringList(completionPrefix());
+
+ QString pathCopy = QDir::toNativeSeparators(path);
+ QString sep = QDir::separator();
+#ifdef Q_OS_WIN
+ if (pathCopy == QLatin1String("\\") || pathCopy == QLatin1String("\\\\"))
+ return QStringList(pathCopy);
+ QString doubleSlash(QLatin1String("\\\\"));
+ if (pathCopy.startsWith(doubleSlash))
+ pathCopy = pathCopy.mid(2);
+ else
+ doubleSlash.clear();
+#endif
+
+ QRegExp re(QLatin1String("[") + QRegExp::escape(sep) + QLatin1String("]"));
+
+#ifdef Q_OS_WIN
+ QStringList parts = pathCopy.split(re, QString::SkipEmptyParts);
+ if (!doubleSlash.isEmpty() && !parts.isEmpty())
+ parts[0].prepend(doubleSlash);
+ if (pathCopy.endsWith(sep))
+ parts.append(QString());
+#else
+ QStringList parts = pathCopy.split(re);
+ if (path[0] == sep[0]) // read the "/" at the beginning as the split removed it
+ parts[0] = sep[0];
+#endif
+
+#ifdef Q_OS_WIN
+ bool startsFromRoot = !parts.isEmpty() && parts[0].endsWith(QLatin1Char(':'));
+#else
+ bool startsFromRoot = path[0] == sep[0];
+#endif
+ if (parts.count() == 1 || (parts.count() > 1 && !startsFromRoot)) {
+ const QFileSystemModel *dirModel = static_cast<const QFileSystemModel *>(model());
+ QString currentLocation = QDir::toNativeSeparators(dirModel->rootPath());
+ if (currentLocation.contains(sep) && path != currentLocation) {
+ QStringList currentLocationList = splitPath(currentLocation);
+ while (!currentLocationList.isEmpty()
+ && parts.count() > 0
+ && parts.at(0) == QLatin1String("..")) {
+ parts.removeFirst();
+ currentLocationList.removeLast();
+ }
+ if (!currentLocationList.isEmpty() && currentLocationList.last().isEmpty())
+ currentLocationList.removeLast();
+ return currentLocationList + parts;
+ }
+ }
+ return parts;
+}
+
+#endif // QT_NO_COMPLETER
+
+#ifdef QT3_SUPPORT
+/*!
+ Use selectedFiles() instead.
+
+ \oldcode
+ QString selected = dialog->selectedFile();
+ \newcode
+ QStringList files = dialog->selectedFiles();
+ QString selected;
+ if (!files.isEmpty())
+ selected = files[0];
+ \endcode
+*/
+QString QFileDialog::selectedFile() const
+{
+ QStringList files = selectedFiles();
+ return files.size() ? files.at(0) : QString();
+}
+
+/*!
+ \typedef QFileDialog::Mode
+
+ Use QFileDialog::FileMode instead.
+*/
+
+/*!
+ \fn void QFileDialog::setMode(FileMode m)
+
+ Use setFileMode() instead.
+*/
+
+/*!
+ \fn FileMode QFileDialog::mode() const
+
+ Use fileMode() instead.
+*/
+
+/*!
+ \fn void QFileDialog::setDir(const QString &directory)
+
+ Use setDirectory() instead.
+*/
+
+/*!
+ \fn void QFileDialog::setDir( const QDir &directory )
+
+ Use setDirectory() instead.
+*/
+
+/*!
+ \fn QStringList QFileDialog::getOpenFileNames(const QString &filter,
+ const QString &dir, QWidget *parent, const char* name,
+ const QString &caption, QString *selectedFilter, bool resolveSymlinks)
+
+ Use the getOpenFileNames() overload that takes \a parent as the first
+ argument instead.
+*/
+
+/*!
+ \fn QString QFileDialog::getOpenFileName(const QString &dir,
+ const QString &filter, QWidget *parent = 0, const char *name,
+ const QString &caption, QString *selectedFilter, bool resolveSymlinks)
+
+ Use the getOpenFileName() overload that takes \a parent as the first
+ argument instead.
+*/
+
+/*!
+ \fn QString QFileDialog::getSaveFileName(const QString &dir,
+ const QString &filter, QWidget *parent, const char *name,
+ const QString &caption, QString *selectedFilter, bool resolveSymlinks)
+
+ Use the getSaveFileName() overload that takes \a parent as the first
+ argument instead.
+*/
+
+/*!
+ \fn QString QFileDialog::getExistingDirectory(const QString &dir,
+ QWidget *parent, const char *name, const QString &caption,
+ bool dirOnly, bool resolveSymlinks)
+
+ Use the getExistingDirectory() overload that takes \a parent as
+ the first argument instead.
+*/
+
+#endif // QT3_SUPPORT
+
+QT_END_NAMESPACE
+
+#include "moc_qfiledialog.cpp"
+
+#endif // QT_NO_FILEDIALOG
diff --git a/src/gui/dialogs/qfiledialog.h b/src/gui/dialogs/qfiledialog.h
new file mode 100644
index 0000000000..d4b8d855d8
--- /dev/null
+++ b/src/gui/dialogs/qfiledialog.h
@@ -0,0 +1,330 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QFILEDIALOG_H
+#define QFILEDIALOG_H
+
+#include <QtCore/qdir.h>
+#include <QtCore/qstring.h>
+#include <QtGui/qdialog.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+#ifndef QT_NO_FILEDIALOG
+
+class QModelIndex;
+class QItemSelection;
+struct QFileDialogArgs;
+class QFileIconProvider;
+class QFileDialogPrivate;
+class QAbstractItemDelegate;
+class QAbstractProxyModel;
+class QUrl;
+
+class Q_GUI_EXPORT QFileDialog : public QDialog
+{
+ Q_OBJECT
+ Q_ENUMS(ViewMode FileMode AcceptMode Option)
+ Q_PROPERTY(ViewMode viewMode READ viewMode WRITE setViewMode)
+ Q_PROPERTY(FileMode fileMode READ fileMode WRITE setFileMode)
+ Q_PROPERTY(AcceptMode acceptMode READ acceptMode WRITE setAcceptMode)
+ Q_PROPERTY(bool readOnly READ isReadOnly WRITE setReadOnly DESIGNABLE false)
+ Q_PROPERTY(bool resolveSymlinks READ resolveSymlinks WRITE setResolveSymlinks DESIGNABLE false)
+ Q_PROPERTY(bool confirmOverwrite READ confirmOverwrite WRITE setConfirmOverwrite DESIGNABLE false)
+ Q_PROPERTY(QString defaultSuffix READ defaultSuffix WRITE setDefaultSuffix)
+ Q_PROPERTY(bool nameFilterDetailsVisible READ isNameFilterDetailsVisible
+ WRITE setNameFilterDetailsVisible DESIGNABLE false)
+ Q_PROPERTY(Options options READ options WRITE setOptions)
+
+public:
+ enum ViewMode { Detail, List };
+ enum FileMode { AnyFile, ExistingFile, Directory, ExistingFiles, DirectoryOnly };
+ enum AcceptMode { AcceptOpen, AcceptSave };
+ enum DialogLabel { LookIn, FileName, FileType, Accept, Reject };
+
+ // ### Rename to FileDialogOption and FileDialogOptions for Qt 5.0
+ enum Option
+ {
+ ShowDirsOnly = 0x00000001,
+ DontResolveSymlinks = 0x00000002,
+ DontConfirmOverwrite = 0x00000004,
+ DontUseSheet = 0x00000008,
+ DontUseNativeDialog = 0x00000010,
+ ReadOnly = 0x00000020,
+ HideNameFilterDetails = 0x00000040
+ };
+ Q_DECLARE_FLAGS(Options, Option)
+
+ QFileDialog(QWidget *parent, Qt::WindowFlags f);
+ explicit QFileDialog(QWidget *parent = 0,
+ const QString &caption = QString(),
+ const QString &directory = QString(),
+ const QString &filter = QString());
+ ~QFileDialog();
+
+ void setDirectory(const QString &directory);
+ inline void setDirectory(const QDir &directory);
+ QDir directory() const;
+
+ void selectFile(const QString &filename);
+ QStringList selectedFiles() const;
+
+#ifdef QT_DEPRECATED
+ QT_DEPRECATED void setFilter(const QString &filter);
+ QT_DEPRECATED void setFilters(const QStringList &filters);
+ QT_DEPRECATED QStringList filters() const;
+ QT_DEPRECATED void selectFilter(const QString &filter);
+ QT_DEPRECATED QString selectedFilter() const;
+#endif
+ void setNameFilterDetailsVisible(bool enabled);
+ bool isNameFilterDetailsVisible() const;
+
+ void setNameFilter(const QString &filter);
+ void setNameFilters(const QStringList &filters);
+ QStringList nameFilters() const;
+ void selectNameFilter(const QString &filter);
+ QString selectedNameFilter() const;
+
+ QDir::Filters filter() const;
+ void setFilter(QDir::Filters filters);
+
+ void setViewMode(ViewMode mode);
+ ViewMode viewMode() const;
+
+ void setFileMode(FileMode mode);
+ FileMode fileMode() const;
+
+ void setAcceptMode(AcceptMode mode);
+ AcceptMode acceptMode() const;
+
+ void setReadOnly(bool enabled);
+ bool isReadOnly() const;
+
+ void setResolveSymlinks(bool enabled);
+ bool resolveSymlinks() const;
+
+ void setSidebarUrls(const QList<QUrl> &urls);
+ QList<QUrl> sidebarUrls() const;
+
+ QByteArray saveState() const;
+ bool restoreState(const QByteArray &state);
+
+ void setConfirmOverwrite(bool enabled);
+ bool confirmOverwrite() const;
+
+ void setDefaultSuffix(const QString &suffix);
+ QString defaultSuffix() const;
+
+ void setHistory(const QStringList &paths);
+ QStringList history() const;
+
+ void setItemDelegate(QAbstractItemDelegate *delegate);
+ QAbstractItemDelegate *itemDelegate() const;
+
+ void setIconProvider(QFileIconProvider *provider);
+ QFileIconProvider *iconProvider() const;
+
+ void setLabelText(DialogLabel label, const QString &text);
+ QString labelText(DialogLabel label) const;
+
+#ifndef QT_NO_PROXYMODEL
+ void setProxyModel(QAbstractProxyModel *model);
+ QAbstractProxyModel *proxyModel() const;
+#endif
+
+ void setOption(Option option, bool on = true);
+ bool testOption(Option option) const;
+ void setOptions(Options options);
+ Options options() const;
+
+#ifdef Q_NO_USING_KEYWORD
+#ifndef Q_QDOC
+ void open() { QDialog::open(); }
+#endif
+#else
+ using QDialog::open;
+#endif
+ void open(QObject *receiver, const char *member);
+ void setVisible(bool visible);
+
+Q_SIGNALS:
+ void fileSelected(const QString &file);
+ void filesSelected(const QStringList &files);
+ void currentChanged(const QString &path);
+ void directoryEntered(const QString &directory);
+ void filterSelected(const QString &filter);
+
+public:
+#ifdef QT3_SUPPORT
+ typedef FileMode Mode;
+ inline QT3_SUPPORT void setMode(FileMode m) { setFileMode(m); }
+ inline QT3_SUPPORT FileMode mode() const { return fileMode(); }
+ inline QT3_SUPPORT void setDir(const QString &directory) { setDirectory(directory); }
+ inline QT3_SUPPORT void setDir( const QDir &directory ) { setDirectory(directory); }
+ QT3_SUPPORT QString selectedFile() const;
+#endif
+
+ static QString getOpenFileName(QWidget *parent = 0,
+ const QString &caption = QString(),
+ const QString &dir = QString(),
+ const QString &filter = QString(),
+ QString *selectedFilter = 0,
+ Options options = 0);
+
+ static QString getSaveFileName(QWidget *parent = 0,
+ const QString &caption = QString(),
+ const QString &dir = QString(),
+ const QString &filter = QString(),
+ QString *selectedFilter = 0,
+ Options options = 0);
+
+ static QString getExistingDirectory(QWidget *parent = 0,
+ const QString &caption = QString(),
+ const QString &dir = QString(),
+ Options options = ShowDirsOnly);
+
+ static QStringList getOpenFileNames(QWidget *parent = 0,
+ const QString &caption = QString(),
+ const QString &dir = QString(),
+ const QString &filter = QString(),
+ QString *selectedFilter = 0,
+ Options options = 0);
+
+#ifdef QT3_SUPPORT
+ inline static QString QT3_SUPPORT getOpenFileName(const QString &dir,
+ const QString &filter = QString(),
+ QWidget *parent = 0, const char* name = 0,
+ const QString &caption = QString(),
+ QString *selectedFilter = 0,
+ bool resolveSymlinks = true)
+ { Q_UNUSED(name);
+ return getOpenFileName(parent, caption, dir, filter, selectedFilter,
+ resolveSymlinks ? Option(0) : DontResolveSymlinks); }
+
+ inline static QString QT3_SUPPORT getSaveFileName(const QString &dir,
+ const QString &filter = QString(),
+ QWidget *parent = 0, const char* name = 0,
+ const QString &caption = QString(),
+ QString *selectedFilter = 0,
+ bool resolveSymlinks = true)
+ { Q_UNUSED(name);
+ return getSaveFileName(parent, caption, dir, filter, selectedFilter,
+ resolveSymlinks ? Option(0) : DontResolveSymlinks); }
+
+ inline static QString QT3_SUPPORT getExistingDirectory(const QString &dir,
+ QWidget *parent = 0,
+ const char* name = 0,
+ const QString &caption = QString(),
+ bool dirOnly = true,
+ bool resolveSymlinks = true)
+ { Q_UNUSED(name);
+ return getExistingDirectory(parent, caption, dir,
+ Options((resolveSymlinks ? Option(0) : DontResolveSymlinks)
+ | (dirOnly ? ShowDirsOnly : Option(0)))); }
+
+ inline static QStringList QT3_SUPPORT getOpenFileNames(const QString &filter,
+ const QString &dir = QString(),
+ QWidget *parent = 0,
+ const char* name = 0,
+ const QString &caption = QString(),
+ QString *selectedFilter = 0,
+ bool resolveSymlinks = true)
+ { Q_UNUSED(name);
+ return getOpenFileNames(parent, caption, dir, filter, selectedFilter,
+ resolveSymlinks ? Option(0) : DontResolveSymlinks); }
+#endif // QT3_SUPPORT
+
+protected:
+ QFileDialog(const QFileDialogArgs &args);
+ void done(int result);
+ void accept();
+ void changeEvent(QEvent *e);
+
+private:
+ Q_DECLARE_PRIVATE(QFileDialog)
+ Q_DISABLE_COPY(QFileDialog)
+
+ Q_PRIVATE_SLOT(d_func(), void _q_pathChanged(const QString &))
+
+ Q_PRIVATE_SLOT(d_func(), void _q_navigateBackward())
+ Q_PRIVATE_SLOT(d_func(), void _q_navigateForward())
+ Q_PRIVATE_SLOT(d_func(), void _q_navigateToParent())
+ Q_PRIVATE_SLOT(d_func(), void _q_createDirectory())
+ Q_PRIVATE_SLOT(d_func(), void _q_showListView())
+ Q_PRIVATE_SLOT(d_func(), void _q_showDetailsView())
+ Q_PRIVATE_SLOT(d_func(), void _q_showContextMenu(const QPoint &))
+ Q_PRIVATE_SLOT(d_func(), void _q_renameCurrent())
+ Q_PRIVATE_SLOT(d_func(), void _q_deleteCurrent())
+ Q_PRIVATE_SLOT(d_func(), void _q_showHidden())
+ Q_PRIVATE_SLOT(d_func(), void _q_updateOkButton())
+ Q_PRIVATE_SLOT(d_func(), void _q_currentChanged(const QModelIndex &index))
+ Q_PRIVATE_SLOT(d_func(), void _q_enterDirectory(const QModelIndex &index))
+ Q_PRIVATE_SLOT(d_func(), void _q_goToDirectory(const QString &path))
+ Q_PRIVATE_SLOT(d_func(), void _q_useNameFilter(int index))
+ Q_PRIVATE_SLOT(d_func(), void _q_selectionChanged())
+ Q_PRIVATE_SLOT(d_func(), void _q_goToUrl(const QUrl &url))
+ Q_PRIVATE_SLOT(d_func(), void _q_goHome())
+ Q_PRIVATE_SLOT(d_func(), void _q_showHeader(QAction *))
+ Q_PRIVATE_SLOT(d_func(), void _q_autoCompleteFileName(const QString &text))
+ Q_PRIVATE_SLOT(d_func(), void _q_rowsInserted(const QModelIndex & parent))
+ Q_PRIVATE_SLOT(d_func(), void _q_fileRenamed(const QString &path,
+ const QString oldName, const QString newName))
+#if defined(Q_WS_MAC)
+ Q_PRIVATE_SLOT(d_func(), void _q_macRunNativeAppModalPanel())
+#endif
+};
+
+inline void QFileDialog::setDirectory(const QDir &adirectory)
+{ setDirectory(adirectory.absolutePath()); }
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(QFileDialog::Options)
+
+#endif // QT_NO_FILEDIALOG
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QFILEDIALOG_H
diff --git a/src/gui/dialogs/qfiledialog.ui b/src/gui/dialogs/qfiledialog.ui
new file mode 100644
index 0000000000..3afbd893c7
--- /dev/null
+++ b/src/gui/dialogs/qfiledialog.ui
@@ -0,0 +1,320 @@
+<ui version="4.0" >
+ <comment>*********************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+*********************************************************************</comment>
+ <class>QFileDialog</class>
+ <widget class="QDialog" name="QFileDialog" >
+ <property name="geometry" >
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>521</width>
+ <height>316</height>
+ </rect>
+ </property>
+ <property name="sizeGripEnabled" >
+ <bool>true</bool>
+ </property>
+ <layout class="QGridLayout" >
+ <item row="0" column="0" >
+ <widget class="QLabel" name="lookInLabel" >
+ <property name="text" >
+ <string>Look in:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1" colspan="2" >
+ <layout class="QHBoxLayout" >
+ <item>
+ <widget class="QFileDialogComboBox" name="lookInCombo" >
+ <property name="sizePolicy" >
+ <sizepolicy vsizetype="Fixed" hsizetype="Ignored" >
+ <horstretch>1</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize" >
+ <size>
+ <width>50</width>
+ <height>0</height>
+ </size>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QToolButton" name="backButton" >
+ <property name="toolTip" >
+ <string>Back</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QToolButton" name="forwardButton" >
+ <property name="toolTip" >
+ <string>Forward</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QToolButton" name="toParentButton" >
+ <property name="toolTip" >
+ <string>Parent Directory</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QToolButton" name="newFolderButton" >
+ <property name="toolTip" >
+ <string>Create New Folder</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QToolButton" name="listModeButton" >
+ <property name="toolTip" >
+ <string>List View</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QToolButton" name="detailModeButton" >
+ <property name="toolTip" >
+ <string>Detail View</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item row="1" column="0" colspan="3" >
+ <widget class="QSplitter" name="splitter" >
+ <property name="sizePolicy" >
+ <sizepolicy vsizetype="Expanding" hsizetype="Expanding" >
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="orientation" >
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <widget class="QSidebar" name="sidebar" />
+ <widget class="QFrame" name="frame" >
+ <property name="frameShape" >
+ <enum>QFrame::NoFrame</enum>
+ </property>
+ <property name="frameShadow" >
+ <enum>QFrame::Raised</enum>
+ </property>
+ <layout class="QVBoxLayout" >
+ <property name="spacing" >
+ <number>0</number>
+ </property>
+ <property name="leftMargin" >
+ <number>0</number>
+ </property>
+ <property name="topMargin" >
+ <number>0</number>
+ </property>
+ <property name="rightMargin" >
+ <number>0</number>
+ </property>
+ <property name="bottomMargin" >
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="QStackedWidget" name="stackedWidget" >
+ <property name="currentIndex" >
+ <number>0</number>
+ </property>
+ <widget class="QWidget" name="page" >
+ <layout class="QVBoxLayout" >
+ <property name="spacing" >
+ <number>0</number>
+ </property>
+ <property name="leftMargin" >
+ <number>0</number>
+ </property>
+ <property name="topMargin" >
+ <number>0</number>
+ </property>
+ <property name="rightMargin" >
+ <number>0</number>
+ </property>
+ <property name="bottomMargin" >
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="QFileDialogListView" name="listView" />
+ </item>
+ </layout>
+ </widget>
+ <widget class="QWidget" name="page_2" >
+ <layout class="QVBoxLayout" >
+ <property name="spacing" >
+ <number>0</number>
+ </property>
+ <property name="leftMargin" >
+ <number>0</number>
+ </property>
+ <property name="topMargin" >
+ <number>0</number>
+ </property>
+ <property name="rightMargin" >
+ <number>0</number>
+ </property>
+ <property name="bottomMargin" >
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="QFileDialogTreeView" name="treeView" />
+ </item>
+ </layout>
+ </widget>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </widget>
+ </item>
+ <item row="2" column="0" >
+ <widget class="QLabel" name="fileNameLabel" >
+ <property name="sizePolicy" >
+ <sizepolicy vsizetype="Preferred" hsizetype="Minimum" >
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize" >
+ <size>
+ <width>0</width>
+ <height>0</height>
+ </size>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="1" >
+ <widget class="QFileDialogLineEdit" name="fileNameEdit" >
+ <property name="sizePolicy" >
+ <sizepolicy vsizetype="Fixed" hsizetype="Expanding" >
+ <horstretch>1</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ </widget>
+ </item>
+ <item rowspan="2" row="2" column="2" >
+ <widget class="QDialogButtonBox" name="buttonBox" >
+ <property name="orientation" >
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="standardButtons" >
+ <set>QDialogButtonBox::Cancel|QDialogButtonBox::NoButton|QDialogButtonBox::Ok</set>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="0" >
+ <widget class="QLabel" name="fileTypeLabel" >
+ <property name="sizePolicy" >
+ <sizepolicy vsizetype="Fixed" hsizetype="Preferred" >
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text" >
+ <string>Files of type:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="1" >
+ <widget class="QComboBox" name="fileTypeCombo" >
+ <property name="sizePolicy" >
+ <sizepolicy vsizetype="Fixed" hsizetype="Expanding" >
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <customwidgets>
+ <customwidget>
+ <class>QFileDialogTreeView</class>
+ <extends>QTreeView</extends>
+ <header>qfiledialog_p.h</header>
+ </customwidget>
+ <customwidget>
+ <class>QFileDialogListView</class>
+ <extends>QListView</extends>
+ <header>qfiledialog_p.h</header>
+ </customwidget>
+ <customwidget>
+ <class>QSidebar</class>
+ <extends>QListWidget</extends>
+ <header>qsidebar_p.h</header>
+ </customwidget>
+ <customwidget>
+ <class>QFileDialogLineEdit</class>
+ <extends>QLineEdit</extends>
+ <header>qfiledialog_p.h</header>
+ </customwidget>
+ <customwidget>
+ <class>QFileDialogComboBox</class>
+ <extends>QComboBox</extends>
+ <header>qfiledialog_p.h</header>
+ </customwidget>
+ </customwidgets>
+ <tabstops>
+ <tabstop>lookInCombo</tabstop>
+ <tabstop>backButton</tabstop>
+ <tabstop>forwardButton</tabstop>
+ <tabstop>toParentButton</tabstop>
+ <tabstop>newFolderButton</tabstop>
+ <tabstop>listModeButton</tabstop>
+ <tabstop>detailModeButton</tabstop>
+ <tabstop>sidebar</tabstop>
+ <tabstop>listView</tabstop>
+ <tabstop>fileNameEdit</tabstop>
+ <tabstop>fileTypeCombo</tabstop>
+ <tabstop>buttonBox</tabstop>
+ <tabstop>treeView</tabstop>
+ </tabstops>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/src/gui/dialogs/qfiledialog_mac.mm b/src/gui/dialogs/qfiledialog_mac.mm
new file mode 100644
index 0000000000..4c13d0113c
--- /dev/null
+++ b/src/gui/dialogs/qfiledialog_mac.mm
@@ -0,0 +1,1113 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qfiledialog.h"
+
+#ifndef QT_NO_FILEDIALOG
+
+/*****************************************************************************
+ QFileDialog debug facilities
+ *****************************************************************************/
+//#define DEBUG_FILEDIALOG_FILTERS
+
+#include <qapplication.h>
+#include <private/qapplication_p.h>
+#include <private/qfiledialog_p.h>
+#include <private/qt_mac_p.h>
+#include <private/qt_cocoa_helpers_mac_p.h>
+#include <qregexp.h>
+#include <qbuffer.h>
+#include <qdebug.h>
+#include <qstringlist.h>
+#include <qaction.h>
+#include <qtextcodec.h>
+#include <qvarlengtharray.h>
+#include <qdesktopwidget.h>
+#include <stdlib.h>
+#include "ui_qfiledialog.h"
+
+QT_BEGIN_NAMESPACE
+
+extern QStringList qt_make_filter_list(const QString &filter); // qfiledialog.cpp
+extern QStringList qt_clean_filter_list(const QString &filter); // qfiledialog.cpp
+extern const char *qt_file_dialog_filter_reg_exp; // qfiledialog.cpp
+extern bool qt_mac_is_macsheet(const QWidget *w); // qwidget_mac.mm
+
+QT_END_NAMESPACE
+
+QT_FORWARD_DECLARE_CLASS(QFileDialogPrivate)
+QT_FORWARD_DECLARE_CLASS(QString)
+QT_FORWARD_DECLARE_CLASS(QStringList)
+QT_FORWARD_DECLARE_CLASS(QWidget)
+QT_FORWARD_DECLARE_CLASS(QAction)
+QT_FORWARD_DECLARE_CLASS(QFileInfo)
+QT_USE_NAMESPACE
+
+@class QNSOpenSavePanelDelegate;
+
+@interface QNSOpenSavePanelDelegate : NSObject {
+ @public
+ NSOpenPanel *mOpenPanel;
+ NSSavePanel *mSavePanel;
+ NSView *mAccessoryView;
+ NSPopUpButton *mPopUpButton;
+ NSTextField *mTextField;
+ QFileDialogPrivate *mPriv;
+ NSString *mCurrentDir;
+ bool mConfirmOverwrite;
+ int mReturnCode;
+
+ QT_PREPEND_NAMESPACE(QFileDialog::AcceptMode) mAcceptMode;
+ QT_PREPEND_NAMESPACE(QDir::Filters) *mQDirFilter;
+ QT_PREPEND_NAMESPACE(QFileDialog::FileMode) mFileMode;
+ QT_PREPEND_NAMESPACE(QFileDialog::Options) *mFileOptions;
+
+ QString *mLastFilterCheckPath;
+ QString *mCurrentSelection;
+ QStringList *mQDirFilterEntryList;
+ QStringList *mNameFilterDropDownList;
+ QStringList *mSelectedNameFilter;
+}
+
+- (NSString *)strip:(const QString &)label;
+- (BOOL)panel:(id)sender shouldShowFilename:(NSString *)filename;
+- (void)filterChanged:(id)sender;
+- (void)showModelessPanel;
+- (BOOL)runApplicationModalPanel;
+- (void)showWindowModalSheet:(QWidget *)docWidget;
+- (void)updateProperties;
+- (QStringList)acceptableExtensionsForSave;
+- (QString)removeExtensions:(const QString &)filter;
+- (void)createTextField;
+- (void)createPopUpButton:(const QString &)selectedFilter hideDetails:(BOOL)hideDetails;
+- (void)createAccessory;
+
+@end
+
+@implementation QNSOpenSavePanelDelegate
+
+- (id)initWithAcceptMode:(QT_PREPEND_NAMESPACE(QFileDialog::AcceptMode))acceptMode
+ title:(const QString &)title
+ nameFilters:(const QStringList &)nameFilters
+ selectedNameFilter:(const QString &)selectedNameFilter
+ hideNameFilterDetails:(bool)hideNameFilterDetails
+ qDirFilter:(QT_PREPEND_NAMESPACE(QDir::Filters))qDirFilter
+ fileOptions:(QT_PREPEND_NAMESPACE(QFileDialog::Options))fileOptions
+ fileMode:(QT_PREPEND_NAMESPACE(QFileDialog::FileMode))fileMode
+ selectFile:(const QString &)selectFile
+ confirmOverwrite:(bool)confirm
+ priv:(QFileDialogPrivate *)priv
+{
+ self = [super init];
+
+ mAcceptMode = acceptMode;
+ if (mAcceptMode == QT_PREPEND_NAMESPACE(QFileDialog::AcceptOpen)){
+ mOpenPanel = [NSOpenPanel openPanel];
+ mSavePanel = mOpenPanel;
+ } else {
+ mSavePanel = [NSSavePanel savePanel];
+ mOpenPanel = 0;
+ }
+
+ [mSavePanel setLevel:NSModalPanelWindowLevel];
+ [mSavePanel setDelegate:self];
+ mQDirFilter = new QT_PREPEND_NAMESPACE(QDir::Filters)(qDirFilter);
+ mFileOptions = new QT_PREPEND_NAMESPACE(QFileDialog::Options)(fileOptions);
+ mFileMode = fileMode;
+ mConfirmOverwrite = confirm;
+ mReturnCode = -1;
+ mPriv = priv;
+ mLastFilterCheckPath = new QString;
+ mQDirFilterEntryList = new QStringList;
+ mNameFilterDropDownList = new QStringList(nameFilters);
+ mSelectedNameFilter = new QStringList(qt_clean_filter_list(selectedNameFilter));
+ QFileInfo sel(selectFile);
+ if (sel.isDir()){
+ mCurrentDir = [qt_mac_QStringToNSString(sel.absoluteFilePath()) retain];
+ mCurrentSelection = new QString;
+ } else {
+ mCurrentDir = [qt_mac_QStringToNSString(sel.absolutePath()) retain];
+ mCurrentSelection = new QString(sel.absoluteFilePath());
+ }
+ [mSavePanel setTitle:qt_mac_QStringToNSString(title)];
+ [self createPopUpButton:selectedNameFilter hideDetails:hideNameFilterDetails];
+ [self createTextField];
+ [self createAccessory];
+ [mSavePanel setAccessoryView:mNameFilterDropDownList->size() > 1 ? mAccessoryView : nil];
+
+ if (mPriv){
+ [mSavePanel setPrompt:[self strip:mPriv->acceptLabel]];
+ if (mPriv->fileNameLabelExplicitlySat)
+ [mSavePanel setNameFieldLabel:[self strip:mPriv->qFileDialogUi->fileNameLabel->text()]];
+ }
+
+ [self updateProperties];
+ [mSavePanel retain];
+ return self;
+}
+
+- (void)dealloc
+{
+ delete mQDirFilter;
+ delete mFileOptions;
+ delete mLastFilterCheckPath;
+ delete mQDirFilterEntryList;
+ delete mNameFilterDropDownList;
+ delete mSelectedNameFilter;
+ delete mCurrentSelection;
+
+ [mSavePanel orderOut:mSavePanel];
+ [mSavePanel setAccessoryView:nil];
+ [mPopUpButton release];
+ [mTextField release];
+ [mAccessoryView release];
+ [mSavePanel setDelegate:nil];
+ [mSavePanel release];
+ [mCurrentDir release];
+ [super dealloc];
+}
+
+- (NSString *)strip:(const QString &)label
+{
+ QAction a(label, 0);
+ return qt_mac_QStringToNSString(a.iconText());
+}
+
+- (void)closePanel
+{
+ *mCurrentSelection = QT_PREPEND_NAMESPACE(qt_mac_NSStringToQString)([mSavePanel filename]);
+ [mSavePanel close];
+}
+
+- (void)showModelessPanel
+{
+ if (mOpenPanel){
+ QFileInfo info(*mCurrentSelection);
+ NSString *filename = QT_PREPEND_NAMESPACE(qt_mac_QStringToNSString)(info.fileName());
+ NSString *filepath = QT_PREPEND_NAMESPACE(qt_mac_QStringToNSString)(info.filePath());
+ bool selectable = (mAcceptMode == QFileDialog::AcceptSave)
+ || [self panel:nil shouldShowFilename:filepath];
+ [mOpenPanel
+ beginForDirectory:mCurrentDir
+ file:selectable ? filename : nil
+ types:nil
+ modelessDelegate:self
+ didEndSelector:@selector(openPanelDidEnd:returnCode:contextInfo:)
+ contextInfo:nil];
+ }
+}
+
+- (BOOL)runApplicationModalPanel
+{
+ QFileInfo info(*mCurrentSelection);
+ NSString *filename = QT_PREPEND_NAMESPACE(qt_mac_QStringToNSString)(info.fileName());
+ NSString *filepath = QT_PREPEND_NAMESPACE(qt_mac_QStringToNSString)(info.filePath());
+ bool selectable = (mAcceptMode == QFileDialog::AcceptSave)
+ || [self panel:nil shouldShowFilename:filepath];
+ mReturnCode = [mSavePanel
+ runModalForDirectory:mCurrentDir
+ file:selectable ? filename : @"untitled"];
+ return (mReturnCode == NSOKButton);
+}
+
+- (QT_PREPEND_NAMESPACE(QDialog::DialogCode))dialogResultCode
+{
+ return (mReturnCode == NSOKButton) ? QT_PREPEND_NAMESPACE(QDialog::Accepted) : QT_PREPEND_NAMESPACE(QDialog::Rejected);
+}
+
+- (void)showWindowModalSheet:(QWidget *)docWidget
+{
+ Q_UNUSED(docWidget);
+ QFileInfo info(*mCurrentSelection);
+ NSString *filename = QT_PREPEND_NAMESPACE(qt_mac_QStringToNSString)(info.fileName());
+ NSString *filepath = QT_PREPEND_NAMESPACE(qt_mac_QStringToNSString)(info.filePath());
+ bool selectable = (mAcceptMode == QFileDialog::AcceptSave)
+ || [self panel:nil shouldShowFilename:filepath];
+ [mSavePanel
+ beginSheetForDirectory:mCurrentDir
+ file:selectable ? filename : nil
+#ifdef QT_MAC_USE_COCOA
+ modalForWindow:QT_PREPEND_NAMESPACE(qt_mac_window_for)(docWidget)
+#else
+ modalForWindow:nil
+#endif
+ modalDelegate:self
+ didEndSelector:@selector(openPanelDidEnd:returnCode:contextInfo:)
+ contextInfo:nil];
+}
+
+- (BOOL)panel:(id)sender shouldShowFilename:(NSString *)filename
+{
+ Q_UNUSED(sender);
+ QString qtFileName = QT_PREPEND_NAMESPACE(qt_mac_NSStringToQString)(filename);
+ QFileInfo info(qtFileName);
+ QString path = info.absolutePath();
+ if (path != *mLastFilterCheckPath){
+ *mLastFilterCheckPath = path;
+ *mQDirFilterEntryList = info.dir().entryList(*mQDirFilter);
+ }
+ // Check if the QDir filter accepts the file:
+ if (!mQDirFilterEntryList->contains(info.fileName()))
+ return NO;
+
+ // Always accept directories regardless of their names:
+ BOOL isDir;
+ if ([[NSFileManager defaultManager] fileExistsAtPath:filename isDirectory:&isDir] && isDir)
+ return YES;
+
+ // No filter means accept everything
+ if (mSelectedNameFilter->isEmpty())
+ return YES;
+ // Check if the current file name filter accepts the file:
+ for (int i=0; i<mSelectedNameFilter->size(); ++i) {
+ if (QDir::match(mSelectedNameFilter->at(i), qtFileName))
+ return YES;
+ }
+ return NO;
+}
+
+- (NSString *)panel:(id)sender userEnteredFilename:(NSString *)filename confirmed:(BOOL)okFlag
+{
+ Q_UNUSED(sender);
+ if (!okFlag)
+ return filename;
+ if (mConfirmOverwrite)
+ return filename;
+
+ // User has clicked save, and no overwrite confirmation should occur.
+ // To get the latter, we need to change the name we return (hence the prefix):
+ return [@"___qt_very_unlikely_prefix_" stringByAppendingString:filename];
+}
+
+- (void)setNameFilters:(const QStringList &)filters hideDetails:(BOOL)hideDetails
+{
+ [mPopUpButton removeAllItems];
+ *mNameFilterDropDownList = filters;
+ if (filters.size() > 0){
+ for (int i=0; i<filters.size(); ++i) {
+ QString filter = hideDetails ? [self removeExtensions:filters.at(i)] : filters.at(i);
+ [mPopUpButton addItemWithTitle:QT_PREPEND_NAMESPACE(qt_mac_QStringToNSString)(filter)];
+ }
+ [mPopUpButton selectItemAtIndex:0];
+ [mSavePanel setAccessoryView:mAccessoryView];
+ } else
+ [mSavePanel setAccessoryView:nil];
+
+ [self filterChanged:self];
+}
+
+- (void)filterChanged:(id)sender
+{
+ // This mDelegate function is called when the _name_ filter changes.
+ Q_UNUSED(sender);
+ QString selection = mNameFilterDropDownList->value([mPopUpButton indexOfSelectedItem]);
+ *mSelectedNameFilter = QT_PREPEND_NAMESPACE(qt_clean_filter_list)(selection);
+ [mSavePanel validateVisibleColumns];
+ [self updateProperties];
+ if (mPriv)
+ mPriv->QNSOpenSavePanelDelegate_filterSelected([mPopUpButton indexOfSelectedItem]);
+}
+
+- (QString)currentNameFilter
+{
+ return mNameFilterDropDownList->value([mPopUpButton indexOfSelectedItem]);
+}
+
+- (QStringList)selectedFiles
+{
+ if (mOpenPanel)
+ return QT_PREPEND_NAMESPACE(qt_mac_NSArrayToQStringList)([mOpenPanel filenames]);
+ else{
+ QStringList result;
+ QString filename = QT_PREPEND_NAMESPACE(qt_mac_NSStringToQString)([mSavePanel filename]);
+ result << filename.remove(QLatin1String("___qt_very_unlikely_prefix_"));
+ return result;
+ }
+}
+
+- (void)updateProperties
+{
+ // Call this functions if mFileMode, mFileOptions,
+ // mNameFilterDropDownList or mQDirFilter changes.
+ // The savepanel does not contain the neccessary functions for this.
+ bool chooseFilesOnly = mFileMode == QT_PREPEND_NAMESPACE(QFileDialog::ExistingFile)
+ || mFileMode == QT_PREPEND_NAMESPACE(QFileDialog::ExistingFiles);
+ bool chooseDirsOnly = mFileMode == QT_PREPEND_NAMESPACE(QFileDialog::Directory)
+ || mFileMode == QT_PREPEND_NAMESPACE(QFileDialog::DirectoryOnly)
+ || *mFileOptions & QT_PREPEND_NAMESPACE(QFileDialog::ShowDirsOnly);
+
+ [mOpenPanel setCanChooseFiles:!chooseDirsOnly];
+ [mOpenPanel setCanChooseDirectories:!chooseFilesOnly];
+ [mSavePanel setCanCreateDirectories:!(*mFileOptions & QT_PREPEND_NAMESPACE(QFileDialog::ReadOnly))];
+ [mOpenPanel setAllowsMultipleSelection:(mFileMode == QT_PREPEND_NAMESPACE(QFileDialog::ExistingFiles))];
+ [mOpenPanel setResolvesAliases:!(*mFileOptions & QT_PREPEND_NAMESPACE(QFileDialog::DontResolveSymlinks))];
+
+ QStringList ext = [self acceptableExtensionsForSave];
+ if (mPriv && !ext.isEmpty() && !mPriv->defaultSuffix.isEmpty())
+ ext.prepend(mPriv->defaultSuffix);
+ [mSavePanel setAllowedFileTypes:ext.isEmpty() ? nil : QT_PREPEND_NAMESPACE(qt_mac_QStringListToNSMutableArray(ext))];
+
+ if ([mSavePanel isVisible])
+ [mOpenPanel validateVisibleColumns];
+}
+
+- (void)panelSelectionDidChange:(id)sender
+{
+ Q_UNUSED(sender);
+ *mCurrentSelection = QT_PREPEND_NAMESPACE(qt_mac_NSStringToQString([mSavePanel filename]));
+ if (mPriv)
+ mPriv->QNSOpenSavePanelDelegate_selectionChanged(*mCurrentSelection);
+}
+
+- (void)openPanelDidEnd:(NSOpenPanel *)panel returnCode:(int)returnCode contextInfo:(void *)contextInfo
+{
+ Q_UNUSED(panel);
+ Q_UNUSED(contextInfo);
+ mReturnCode = returnCode;
+ if (mPriv)
+ mPriv->QNSOpenSavePanelDelegate_panelClosed(returnCode == NSOKButton);
+}
+
+- (void)panel:(id)sender directoryDidChange:(NSString *)path
+{
+ Q_UNUSED(sender);
+ if (!mPriv)
+ return;
+ if ([path isEqualToString:mCurrentDir])
+ return;
+
+ [mCurrentDir release];
+ mCurrentDir = [path retain];
+ mPriv->QNSOpenSavePanelDelegate_directoryEntered(QT_PREPEND_NAMESPACE(qt_mac_NSStringToQString(mCurrentDir)));
+}
+
+/*
+ Returns a list of extensions (e.g. "png", "jpg", "gif")
+ for the current name filter. If a filter do not conform
+ to the format *.xyz or * or *.*, an empty list
+ is returned meaning accept everything.
+*/
+- (QStringList)acceptableExtensionsForSave
+{
+ QStringList result;
+ for (int i=0; i<mSelectedNameFilter->count(); ++i) {
+ const QString &filter = mSelectedNameFilter->at(i);
+ if (filter.startsWith(QLatin1String("*."))
+ && !filter.contains(QLatin1Char('?'))
+ && filter.count(QLatin1Char('*')) == 1) {
+ result += filter.mid(2);
+ } else {
+ return QStringList(); // Accept everything
+ }
+ }
+ return result;
+}
+
+- (QString)removeExtensions:(const QString &)filter
+{
+ QRegExp regExp(QT_PREPEND_NAMESPACE(QString::fromLatin1)(QT_PREPEND_NAMESPACE(qt_file_dialog_filter_reg_exp)));
+ if (regExp.indexIn(filter) != -1)
+ return regExp.cap(1).trimmed();
+ return filter;
+}
+
+- (void)createTextField
+{
+ NSRect textRect = { { 0.0, 3.0 }, { 100.0, 25.0 } };
+ mTextField = [[NSTextField alloc] initWithFrame:textRect];
+ [[mTextField cell] setFont:[NSFont systemFontOfSize:
+ [NSFont systemFontSizeForControlSize:NSRegularControlSize]]];
+ [mTextField setAlignment:NSRightTextAlignment];
+ [mTextField setEditable:false];
+ [mTextField setSelectable:false];
+ [mTextField setBordered:false];
+ [mTextField setDrawsBackground:false];
+ if (mPriv){
+ [mTextField setStringValue:[self strip:mPriv->qFileDialogUi->fileTypeLabel->text()]];
+ } else
+ [mTextField setStringValue:QT_PREPEND_NAMESPACE(qt_mac_QStringToNSString)(QT_PREPEND_NAMESPACE(QFileDialog::tr)("Files of type:"))];
+}
+
+- (void)createPopUpButton:(const QString &)selectedFilter hideDetails:(BOOL)hideDetails
+{
+ NSRect popUpRect = { { 100.0, 5.0 }, { 250.0, 25.0 } };
+ mPopUpButton = [[NSPopUpButton alloc] initWithFrame:popUpRect pullsDown:NO];
+ [mPopUpButton setTarget:self];
+ [mPopUpButton setAction:@selector(filterChanged:)];
+
+ QStringList *filters = mNameFilterDropDownList;
+ if (filters->size() > 0){
+ for (int i=0; i<mNameFilterDropDownList->size(); ++i) {
+ QString filter = hideDetails ? [self removeExtensions:filters->at(i)] : filters->at(i);
+ [mPopUpButton addItemWithTitle:QT_PREPEND_NAMESPACE(qt_mac_QStringToNSString)(filter)];
+ if (filters->at(i) == selectedFilter)
+ [mPopUpButton selectItemAtIndex:i];
+ }
+ }
+}
+
+- (void)createAccessory
+{
+ NSRect accessoryRect = { { 0.0, 0.0 }, { 450.0, 33.0 } };
+ mAccessoryView = [[NSView alloc] initWithFrame:accessoryRect];
+ [mAccessoryView addSubview:mTextField];
+ [mAccessoryView addSubview:mPopUpButton];
+}
+
+@end
+
+QT_BEGIN_NAMESPACE
+
+void QFileDialogPrivate::QNSOpenSavePanelDelegate_selectionChanged(const QString &newPath)
+{
+ emit q_func()->currentChanged(newPath);
+}
+
+void QFileDialogPrivate::QNSOpenSavePanelDelegate_panelClosed(bool accepted)
+{
+ if (accepted)
+ q_func()->accept();
+ else
+ q_func()->reject();
+}
+
+void QFileDialogPrivate::QNSOpenSavePanelDelegate_directoryEntered(const QString &newDir)
+{
+ setLastVisitedDirectory(newDir);
+ emit q_func()->directoryEntered(newDir);
+}
+
+void QFileDialogPrivate::QNSOpenSavePanelDelegate_filterSelected(int menuIndex)
+{
+ emit q_func()->filterSelected(nameFilters.at(menuIndex));
+}
+
+extern OSErr qt_mac_create_fsref(const QString &, FSRef *); // qglobal.cpp
+extern void qt_mac_to_pascal_string(QString s, Str255 str, TextEncoding encoding=0, int len=-1); // qglobal.cpp
+
+void QFileDialogPrivate::setDirectory_sys(const QString &directory)
+{
+#ifndef QT_MAC_USE_COCOA
+ if (directory == mCurrentLocation)
+ return;
+ mCurrentLocation = directory;
+ emit q_func()->directoryEntered(mCurrentLocation);
+
+ FSRef fsRef;
+ if (qt_mac_create_fsref(directory, &fsRef) == noErr) {
+ AEDesc desc;
+ if (AECreateDesc(typeFSRef, &fsRef, sizeof(FSRef), &desc) == noErr)
+ NavCustomControl(mDialog, kNavCtlSetLocation, (void*)&desc);
+ }
+#else
+ QMacCocoaAutoReleasePool pool;
+ QNSOpenSavePanelDelegate *delegate = static_cast<QNSOpenSavePanelDelegate *>(mDelegate);
+ [delegate->mSavePanel setDirectory:qt_mac_QStringToNSString(directory)];
+#endif
+}
+
+QString QFileDialogPrivate::directory_sys() const
+{
+#ifndef QT_MAC_USE_COCOA
+ return mCurrentLocation;
+#else
+ QMacCocoaAutoReleasePool pool;
+ QNSOpenSavePanelDelegate *delegate = static_cast<QNSOpenSavePanelDelegate *>(mDelegate);
+ return qt_mac_NSStringToQString([delegate->mSavePanel directory]);
+#endif
+}
+
+void QFileDialogPrivate::selectFile_sys(const QString &filename)
+{
+ QString filePath = filename;
+ if (QDir::isRelativePath(filePath))
+ filePath = QFileInfo(directory_sys(), filePath).filePath();
+
+#ifndef QT_MAC_USE_COCOA
+ // Update the selection list immidiatly, so
+ // subsequent calls to selectedFiles() gets correct:
+ mCurrentSelectionList.clear();
+ mCurrentSelectionList << filename;
+ if (mCurrentSelection != filename){
+ mCurrentSelection = filename;
+ emit q_func()->currentChanged(mCurrentSelection);
+ }
+
+ AEDescList descList;
+ if (AECreateList(0, 0, false, &descList) != noErr)
+ return;
+
+ FSRef fsRef;
+ if (qt_mac_create_fsref(filePath, &fsRef) == noErr) {
+ AEDesc desc;
+ if (AECreateDesc(typeFSRef, &fsRef, sizeof(FSRef), &desc) == noErr){
+ if (AEPutDesc(&descList, 0, &desc) == noErr)
+ NavCustomControl(mDialog, kNavCtlSetSelection, (void*)&descList);
+ }
+ }
+
+ // Type the file name into the save dialog's text field:
+ UInt8 *strBuffer = (UInt8 *)malloc(1024);
+ qt_mac_to_pascal_string(QFileInfo(filename).fileName(), strBuffer);
+ NavCustomControl(mDialog, kNavCtlSetEditFileName, strBuffer);
+ free(strBuffer);
+#else
+ // There seems to no way to select a file once the dialog is running.
+ // So do the next best thing, set the file's directory:
+ setDirectory_sys(QFileInfo(filePath).absolutePath());
+#endif
+}
+
+QStringList QFileDialogPrivate::selectedFiles_sys() const
+{
+#ifndef QT_MAC_USE_COCOA
+ if (q_func()->acceptMode() == QFileDialog::AcceptOpen){
+ return mCurrentSelectionList;
+ } else {
+ return QStringList() << mCurrentLocation + QLatin1Char('/')
+ + QCFString::toQString(NavDialogGetSaveFileName(mDialog));
+ }
+#else
+ QMacCocoaAutoReleasePool pool;
+ QNSOpenSavePanelDelegate *delegate = static_cast<QNSOpenSavePanelDelegate *>(mDelegate);
+ return [delegate selectedFiles];
+#endif
+}
+
+void QFileDialogPrivate::setNameFilters_sys(const QStringList &filters)
+{
+#ifndef QT_MAC_USE_COCOA
+ Q_UNUSED(filters);
+#else
+ QMacCocoaAutoReleasePool pool;
+ QNSOpenSavePanelDelegate *delegate = static_cast<QNSOpenSavePanelDelegate *>(mDelegate);
+ bool hideDetails = q_func()->testOption(QFileDialog::HideNameFilterDetails);
+ [delegate setNameFilters:filters hideDetails:hideDetails];
+#endif
+}
+
+void QFileDialogPrivate::setFilter_sys()
+{
+#ifndef QT_MAC_USE_COCOA
+#else
+ QMacCocoaAutoReleasePool pool;
+ QNSOpenSavePanelDelegate *delegate = static_cast<QNSOpenSavePanelDelegate *>(mDelegate);
+ *(delegate->mQDirFilter) = model->filter();
+ [delegate updateProperties];
+#endif
+}
+
+void QFileDialogPrivate::selectNameFilter_sys(const QString &filter)
+{
+ int index = nameFilters.indexOf(filter);
+ if (index != -1) {
+#ifndef QT_MAC_USE_COCOA
+ NavMenuItemSpec navSpec;
+ bzero(&navSpec, sizeof(NavMenuItemSpec));
+ navSpec.menuType = index;
+ NavCustomControl(mDialog, kNavCtlSelectCustomType, &navSpec);
+#else
+ QMacCocoaAutoReleasePool pool;
+ QNSOpenSavePanelDelegate *delegate = static_cast<QNSOpenSavePanelDelegate *>(mDelegate);
+ [delegate->mPopUpButton selectItemAtIndex:index];
+ [delegate filterChanged:nil];
+#endif
+ }
+}
+
+QString QFileDialogPrivate::selectedNameFilter_sys() const
+{
+#ifndef QT_MAC_USE_COCOA
+ int index = filterInfo.currentSelection;
+#else
+ QMacCocoaAutoReleasePool pool;
+ QNSOpenSavePanelDelegate *delegate = static_cast<QNSOpenSavePanelDelegate *>(mDelegate);
+ int index = [delegate->mPopUpButton indexOfSelectedItem];
+#endif
+ return index != -1 ? nameFilters.at(index) : QString();
+}
+
+void QFileDialogPrivate::deleteNativeDialog_sys()
+{
+#ifndef QT_MAC_USE_COCOA
+ if (mDialog)
+ NavDialogDispose(mDialog);
+ mDialog = 0;
+ mDialogStarted = false;
+#else
+ QMacCocoaAutoReleasePool pool;
+ [reinterpret_cast<QNSOpenSavePanelDelegate *>(mDelegate) release];
+ mDelegate = 0;
+#endif
+ nativeDialogInUse = false;
+}
+
+bool QFileDialogPrivate::setVisible_sys(bool visible)
+{
+ Q_Q(QFileDialog);
+ if (!visible == q->isHidden())
+ return false;
+
+#ifndef QT_MAC_USE_COCOA
+ return visible ? showCarbonNavServicesDialog() : hideCarbonNavServicesDialog();
+#else
+ return visible ? showCocoaFilePanel() : hideCocoaFilePanel();
+#endif
+}
+
+#ifndef QT_MAC_USE_COCOA
+Boolean QFileDialogPrivate::qt_mac_filedialog_filter_proc(AEDesc *theItem, void *info,
+ void *data, NavFilterModes)
+{
+ QFileDialogPrivate *fileDialogPrivate = static_cast<QFileDialogPrivate *>(data);
+
+ if (!fileDialogPrivate || fileDialogPrivate->filterInfo.filters.isEmpty()
+ || (fileDialogPrivate->filterInfo.currentSelection < 0
+ && fileDialogPrivate->filterInfo.currentSelection
+ >= fileDialogPrivate->filterInfo.filters.size()))
+ return true;
+
+ NavFileOrFolderInfo *theInfo = static_cast<NavFileOrFolderInfo *>(info);
+ QString file;
+ const QtMacFilterName &fn
+ = fileDialogPrivate->filterInfo.filters.at(fileDialogPrivate->filterInfo.currentSelection);
+ if (theItem->descriptorType == typeFSRef) {
+ FSRef ref;
+ AEGetDescData(theItem, &ref, sizeof(ref));
+ UInt8 str_buffer[1024];
+ FSRefMakePath(&ref, str_buffer, 1024);
+ file = QString::fromUtf8(reinterpret_cast<const char *>(str_buffer));
+ int slsh = file.lastIndexOf(QLatin1Char('/'));
+ if (slsh != -1)
+ file = file.right(file.length() - slsh - 1);
+ }
+ QStringList reg = fn.regexp.split(QLatin1String(";"));
+ for (QStringList::const_iterator it = reg.constBegin(); it != reg.constEnd(); ++it) {
+ QRegExp rg(*it, Qt::CaseInsensitive, QRegExp::Wildcard);
+#ifdef DEBUG_FILEDIALOG_FILTERS
+ qDebug("QFileDialogPrivate::qt_mac_filedialog_filter_proc:%d, asked to filter.. %s (%s)", __LINE__,
+ qPrintable(file), qPrintable(*it));
+#endif
+ if (rg.exactMatch(file))
+ return true;
+ }
+ return (theInfo->isFolder && !file.endsWith(QLatin1String(".app")));
+}
+
+void QFileDialogPrivate::qt_mac_filedialog_event_proc(const NavEventCallbackMessage msg,
+ NavCBRecPtr p, NavCallBackUserData data)
+{
+ QFileDialogPrivate *fileDialogPrivate = static_cast<QFileDialogPrivate *>(data);
+
+ switch(msg) {
+ case kNavCBPopupMenuSelect: {
+ NavMenuItemSpec *s = static_cast<NavMenuItemSpec *>(p->eventData.eventDataParms.param);
+ if (int(s->menuType) != fileDialogPrivate->filterInfo.currentSelection) {
+ fileDialogPrivate->filterInfo.currentSelection = s->menuType;
+ emit fileDialogPrivate->q_func()->filterSelected(fileDialogPrivate->nameFilters.at(s->menuType));
+ }
+ if (fileDialogPrivate->acceptMode == QFileDialog::AcceptSave) {
+ QString base = QCFString::toQString(NavDialogGetSaveFileName(p->context));
+ QFileInfo fi(base);
+ base = fi.completeBaseName();
+ const QtMacFilterName &fn = fileDialogPrivate->filterInfo.filters.at(
+ fileDialogPrivate->filterInfo.currentSelection);
+ QStringList reg = fn.regexp.split(QLatin1String(";"), QString::SkipEmptyParts);
+ QString r = reg.first();
+ r = r.right(r.length()-1); // Strip the *
+ base += r; //"." + QString::number(s->menuType);
+ NavDialogSetSaveFileName(p->context, QCFString::toCFStringRef(base));
+ }
+#ifdef DEBUG_FILEDIALOG_FILTERS
+ qDebug("QFileDialogPrivate::qt_mac_filedialog_event_proc:%d - Selected a filter: %ld", __LINE__, s->menuType);
+#endif
+ break; }
+ case kNavCBStart:{
+ fileDialogPrivate->mDialogStarted = true;
+ // Set selected file:
+ QModelIndexList indexes = fileDialogPrivate->qFileDialogUi->listView->selectionModel()->selectedRows();
+ QString selected;
+ if (!indexes.isEmpty())
+ selected = indexes.at(0).data(QFileSystemModel::FilePathRole).toString();
+ else
+ selected = fileDialogPrivate->typedFiles().value(0);
+ fileDialogPrivate->selectFile_sys(selected);
+ fileDialogPrivate->selectNameFilter_sys(fileDialogPrivate->qFileDialogUi->fileTypeCombo->currentText());
+ break; }
+ case kNavCBSelectEntry:{
+ // Event: Current selection has changed.
+ QStringList prevSelectionList = fileDialogPrivate->mCurrentSelectionList;
+ fileDialogPrivate->mCurrentSelectionList.clear();
+ QString fileNameToEmit;
+
+ AEDescList *descList = (AEDescList *)p->eventData.eventDataParms.param;
+ // Get the number of files selected:
+ UInt8 strBuffer[1024];
+ long count;
+ OSErr err = AECountItems(descList, &count);
+ if (err != noErr || !count)
+ break;
+
+ for (long index=1; index<=count; ++index) {
+ FSRef ref;
+ err = AEGetNthPtr(descList, index, typeFSRef, 0, 0, &ref, sizeof(ref), 0);
+ if (err != noErr)
+ break;
+ FSRefMakePath(&ref, strBuffer, 1024);
+ QString selected = QString::fromUtf8((const char *)strBuffer);
+ fileDialogPrivate->mCurrentSelectionList << selected;
+ if (!prevSelectionList.contains(selected))
+ fileNameToEmit = selected;
+ }
+
+ if (!fileNameToEmit.isEmpty() && fileNameToEmit != fileDialogPrivate->mCurrentSelection)
+ emit fileDialogPrivate->q_func()->currentChanged(fileNameToEmit);
+ fileDialogPrivate->mCurrentSelection = fileNameToEmit;
+ break; }
+ case kNavCBShowDesktop:
+ case kNavCBNewLocation:{
+ // Event: Current directory has changed.
+ AEDesc *desc = (AEDesc *)p->eventData.eventDataParms.param;
+ FSRef ref;
+ AEGetDescData(desc, &ref, sizeof(ref));
+ UInt8 *strBuffer = (UInt8 *)malloc(1024);
+ FSRefMakePath(&ref, strBuffer, 1024);
+ QString newLocation = QString::fromUtf8((const char *)strBuffer);
+ free(strBuffer);
+ if (fileDialogPrivate->mCurrentLocation != newLocation){
+ fileDialogPrivate->mCurrentLocation = newLocation;
+ QFileDialog::FileMode mode = fileDialogPrivate->fileMode;
+ if (mode == QFileDialog::AnyFile || mode == QFileDialog::ExistingFile
+ || mode == QFileDialog::ExistingFiles){
+ // When changing directory, the current selection is cleared if
+ // we are supposed to be selecting files only:
+ fileDialogPrivate->mCurrentSelectionList.clear();
+ if (!fileDialogPrivate->mCurrentSelection.isEmpty()){
+ fileDialogPrivate->mCurrentSelection.clear();
+ emit fileDialogPrivate->q_func()->currentChanged(fileDialogPrivate->mCurrentSelection);
+ }
+ }
+ fileDialogPrivate->setLastVisitedDirectory(newLocation);
+ emit fileDialogPrivate->q_func()->directoryEntered(newLocation);
+ }
+ break; }
+ case kNavCBAccept:
+ fileDialogPrivate->mDialogClosed = true;
+ fileDialogPrivate->q_func()->accept();
+ break;
+ case kNavCBCancel:
+ fileDialogPrivate->mDialogClosed = true;
+ fileDialogPrivate->q_func()->reject();
+ break;
+ }
+}
+
+static QFileDialogPrivate::QtMacFilterName qt_mac_extract_filter(const QString &rawFilter, bool showDetails)
+{
+ QFileDialogPrivate::QtMacFilterName ret;
+ ret.filter = rawFilter;
+ QString result = rawFilter;
+ QRegExp r(QString::fromLatin1(qt_file_dialog_filter_reg_exp));
+ int index = r.indexIn(result);
+ if (index >= 0)
+ result = r.cap(2);
+
+ if (showDetails) {
+ ret.description = rawFilter;
+ } else {
+ if (index >= 0)
+ ret.description = r.cap(1).trimmed();
+ if (ret.description.isEmpty())
+ ret.description = result;
+ }
+ ret.regexp = result.replace(QLatin1Char(' '), QLatin1Char(';'));
+ return ret;
+}
+
+static QList<QFileDialogPrivate::QtMacFilterName> qt_mac_make_filters_list(const QString &filter, bool showDetails)
+{
+#ifdef DEBUG_FILEDIALOG_FILTERS
+ qDebug("QFileDialog:%d - Got filter (%s)", __LINE__, filter.latin1());
+#endif
+
+ QList<QFileDialogPrivate::QtMacFilterName> ret;
+ QString f(filter);
+ if (f.isEmpty())
+ f = QFileDialog::tr("All Files (*)");
+ if (f.isEmpty())
+ return ret;
+ QStringList filts = qt_make_filter_list(f);
+ for (QStringList::const_iterator it = filts.constBegin(); it != filts.constEnd(); ++it) {
+ QFileDialogPrivate::QtMacFilterName filter = qt_mac_extract_filter(*it, showDetails);
+#ifdef DEBUG_FILEDIALOG_FILTERS
+ qDebug("QFileDialog:%d Split out filter (%d) '%s' '%s' [%s]", __LINE__, ret.count(),
+ filter->regxp.latin1(), filter->description.latin1(), (*it).latin1());
+#endif
+ ret.append(filter);
+ }
+ return ret;
+}
+
+void QFileDialogPrivate::createNavServicesDialog()
+{
+ Q_Q(QFileDialog);
+ if (mDialog)
+ deleteNativeDialog_sys();
+
+ NavDialogCreationOptions navOptions;
+ NavGetDefaultDialogCreationOptions(&navOptions);
+
+ // Translate QFileDialog settings into NavDialog options:
+ if (qt_mac_is_macsheet(q)) {
+ navOptions.modality = kWindowModalityWindowModal;
+ navOptions.parentWindow = qt_mac_window_for(q->parentWidget());
+ } else if (q->windowModality() == Qt::ApplicationModal)
+ navOptions.modality = kWindowModalityAppModal;
+ else
+ navOptions.modality = kWindowModalityNone;
+ navOptions.optionFlags |= kNavSupportPackages;
+ if (q->testOption(QFileDialog::DontConfirmOverwrite))
+ navOptions.optionFlags |= kNavDontConfirmReplacement;
+ if (fileMode != QFileDialog::ExistingFiles)
+ navOptions.optionFlags &= ~kNavAllowMultipleFiles;
+
+ navOptions.windowTitle = QCFString::toCFStringRef(q->windowTitle());
+
+ static const int w = 450, h = 350;
+ navOptions.location.h = navOptions.location.v = -1;
+ QWidget *parent = q->parentWidget();
+ if (parent && parent->isVisible()) {
+ WindowClass wclass;
+ GetWindowClass(qt_mac_window_for(parent), &wclass);
+ parent = parent->window();
+ QString s = parent->windowTitle();
+ navOptions.clientName = QCFString::toCFStringRef(s);
+ navOptions.location.h = (parent->x() + (parent->width() / 2)) - (w / 2);
+ navOptions.location.v = (parent->y() + (parent->height() / 2)) - (h / 2);
+
+ QRect r = QApplication::desktop()->screenGeometry(
+ QApplication::desktop()->screenNumber(parent));
+ const int border = 10;
+ if (navOptions.location.h + w > r.right())
+ navOptions.location.h -= (navOptions.location.h + w) - r.right() + border;
+ if (navOptions.location.v + h > r.bottom())
+ navOptions.location.v -= (navOptions.location.v + h) - r.bottom() + border;
+ if (navOptions.location.h < r.left())
+ navOptions.location.h = r.left() + border;
+ if (navOptions.location.v < r.top())
+ navOptions.location.v = r.top() + border;
+ }
+
+ filterInfo.currentSelection = 0;
+ filterInfo.filters = qt_mac_make_filters_list(nameFilters.join(QLatin1String(";;")), q->isNameFilterDetailsVisible());
+ QCFType<CFArrayRef> filterArray;
+ if (filterInfo.filters.size() > 1) {
+ int i = 0;
+ CFStringRef *cfstringArray = static_cast<CFStringRef *>(malloc(sizeof(CFStringRef)
+ * filterInfo.filters.size()));
+ for (i = 0; i < filterInfo.filters.size(); ++i) {
+ cfstringArray[i] = QCFString::toCFStringRef(filterInfo.filters.at(i).description);
+ }
+ filterArray = CFArrayCreate(kCFAllocatorDefault,
+ reinterpret_cast<const void **>(cfstringArray), filterInfo.filters.size(),
+ &kCFTypeArrayCallBacks);
+ navOptions.popupExtension = filterArray;
+ free(cfstringArray);
+ }
+
+ if (q->acceptMode() == QFileDialog::AcceptSave) {
+ if (NavCreatePutFileDialog(&navOptions, 'cute', kNavGenericSignature,
+ QFileDialogPrivate::qt_mac_filedialog_event_proc, this, &mDialog)) {
+ qDebug("Shouldn't happen %s:%d", __FILE__, __LINE__);
+ return;
+ }
+ } else if (fileMode == QFileDialog::DirectoryOnly || fileMode == QFileDialog::Directory) {
+ if (NavCreateChooseFolderDialog(&navOptions,
+ QFileDialogPrivate::qt_mac_filedialog_event_proc, 0, this, &mDialog)) {
+ qDebug("Shouldn't happen %s:%d", __FILE__, __LINE__);
+ return;
+ }
+ } else {
+ if (NavCreateGetFileDialog(&navOptions, 0,
+ QFileDialogPrivate::qt_mac_filedialog_event_proc, 0,
+ QFileDialogPrivate::qt_mac_filedialog_filter_proc, this, &mDialog)) {
+ qDebug("Shouldn't happen %s:%d", __FILE__, __LINE__);
+ return;
+ }
+ }
+
+ // Set start-up directory:
+ if (mCurrentLocation.isEmpty())
+ mCurrentLocation = rootPath();
+ FSRef fsRef;
+ if (qt_mac_create_fsref(mCurrentLocation, &fsRef) == noErr) {
+ AEDesc desc;
+ if (AECreateDesc(typeFSRef, &fsRef, sizeof(FSRef), &desc) == noErr)
+ NavCustomControl(mDialog, kNavCtlSetLocation, (void*)&desc);
+ }
+}
+
+bool QFileDialogPrivate::showCarbonNavServicesDialog()
+{
+ Q_Q(QFileDialog);
+ if (q->acceptMode() == QFileDialog::AcceptSave && q->windowModality() == Qt::NonModal)
+ return false; // cannot do native no-modal save dialogs.
+ createNavServicesDialog();
+ mDialogClosed = false;
+ if (q->windowModality() != Qt::ApplicationModal)
+ NavDialogRun(mDialog);
+ return true;
+}
+
+bool QFileDialogPrivate::hideCarbonNavServicesDialog()
+{
+ if (!mDialogClosed){
+ mDialogClosed = true;
+ NavCustomControl(mDialog, kNavCtlCancel, 0);
+ }
+ return true;
+}
+
+#else // Cocoa
+
+void QFileDialogPrivate::createNSOpenSavePanelDelegate()
+{
+ Q_Q(QFileDialog);
+ if (mDelegate)
+ return;
+
+ bool selectDir = q->selectedFiles().isEmpty();
+ QString selection(selectDir ? q->directory().absolutePath() : q->selectedFiles().value(0));
+ QNSOpenSavePanelDelegate *delegate = [[QNSOpenSavePanelDelegate alloc]
+ initWithAcceptMode:acceptMode
+ title:q->windowTitle()
+ nameFilters:q->nameFilters()
+ selectedNameFilter:q->selectedNameFilter()
+ hideNameFilterDetails:q->testOption(QFileDialog::HideNameFilterDetails)
+ qDirFilter:model->filter()
+ fileOptions:opts
+ fileMode:fileMode
+ selectFile:selection
+ confirmOverwrite:!q->testOption(QFileDialog::DontConfirmOverwrite)
+ priv:this];
+
+ mDelegate = delegate;
+}
+
+bool QFileDialogPrivate::showCocoaFilePanel()
+{
+ Q_Q(QFileDialog);
+ QMacCocoaAutoReleasePool pool;
+ createNSOpenSavePanelDelegate();
+ QNSOpenSavePanelDelegate *delegate = static_cast<QNSOpenSavePanelDelegate *>(mDelegate);
+ if (qt_mac_is_macsheet(q))
+ [delegate showWindowModalSheet:q->parentWidget()];
+ else
+ [delegate showModelessPanel];
+ return true;
+}
+
+bool QFileDialogPrivate::hideCocoaFilePanel()
+{
+ if (!mDelegate){
+ // Nothing to do. We return false to leave the question
+ // open regarding whether or not to go native:
+ return false;
+ } else {
+ QMacCocoaAutoReleasePool pool;
+ QNSOpenSavePanelDelegate *delegate = static_cast<QNSOpenSavePanelDelegate *>(mDelegate);
+ [delegate closePanel];
+ // Even when we hide it, we are still using a
+ // native dialog, so return true:
+ return true;
+ }
+}
+
+#endif
+
+void QFileDialogPrivate::mac_nativeDialogModalHelp()
+{
+ // Do a queued meta-call to open the native modal dialog so it opens after the new
+ // event loop has started to execute (in QDialog::exec). Using a timer rather than
+ // a queued meta call is intentional to ensure that the call is only delivered when
+ // [NSApp run] runs (timers are handeled special in cocoa). If NSApp is not
+ // running (which is the case if e.g a top-most QEventLoop has been
+ // interrupted, and the second-most event loop has not yet been reactivated (regardless
+ // if [NSApp run] is still on the stack)), showing a native modal dialog will fail.
+ if (nativeDialogInUse){
+ Q_Q(QFileDialog);
+ QTimer::singleShot(1, q, SLOT(_q_macRunNativeAppModalPanel()));
+ }
+}
+
+void QFileDialogPrivate::_q_macRunNativeAppModalPanel()
+{
+ QBoolBlocker nativeDialogOnTop(QApplicationPrivate::native_modal_dialog_active);
+#ifndef QT_MAC_USE_COCOA
+ NavDialogRun(mDialog);
+#else
+ Q_Q(QFileDialog);
+ QMacCocoaAutoReleasePool pool;
+ QNSOpenSavePanelDelegate *delegate = static_cast<QNSOpenSavePanelDelegate *>(mDelegate);
+ [delegate runApplicationModalPanel];
+ dialogResultCode_sys() == QDialog::Accepted ? q->accept() : q->reject();
+#endif
+}
+
+QDialog::DialogCode QFileDialogPrivate::dialogResultCode_sys()
+{
+#ifndef QT_MAC_USE_COCOA
+ NavUserAction result = NavDialogGetUserAction(mDialog);
+ if (result == kNavUserActionCancel || result == kNavUserActionNone)
+ return QDialog::Rejected;
+ else
+ return QDialog::Accepted;
+#else
+ QNSOpenSavePanelDelegate *delegate = static_cast<QNSOpenSavePanelDelegate *>(mDelegate);
+ return [delegate dialogResultCode];
+#endif
+}
+
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_FILEDIALOG
+
diff --git a/src/gui/dialogs/qfiledialog_p.h b/src/gui/dialogs/qfiledialog_p.h
new file mode 100644
index 0000000000..8cb2cb0b0a
--- /dev/null
+++ b/src/gui/dialogs/qfiledialog_p.h
@@ -0,0 +1,458 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QFILEDIALOG_P_H
+#define QFILEDIALOG_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_FILEDIALOG
+
+#include "qfiledialog.h"
+#include "private/qdialog_p.h"
+#include "qplatformdefs.h"
+
+#include "qfilesystemmodel_p.h"
+#include <qlistview.h>
+#include <qtreeview.h>
+#include <qcombobox.h>
+#include <qtoolbutton.h>
+#include <qlabel.h>
+#include <qevent.h>
+#include <qlineedit.h>
+#include <qurl.h>
+#include <qstackedwidget.h>
+#include <qdialogbuttonbox.h>
+#include <qabstractproxymodel.h>
+#include <qcompleter.h>
+#include <qpointer.h>
+#include <qtimeline.h>
+#include <qdebug.h>
+#include "qsidebar_p.h"
+
+#if defined (Q_OS_UNIX)
+#include <unistd.h>
+#endif
+
+QT_BEGIN_NAMESPACE
+
+class QFileDialogListView;
+class QFileDialogTreeView;
+class QFileDialogLineEdit;
+class QGridLayout;
+class QCompleter;
+class QHBoxLayout;
+class Ui_QFileDialog;
+
+struct QFileDialogArgs
+{
+ QFileDialogArgs() : parent(0), mode(QFileDialog::AnyFile) {}
+
+ QWidget *parent;
+ QString caption;
+ QString directory;
+ QString selection;
+ QString filter;
+ QFileDialog::FileMode mode;
+ QFileDialog::Options options;
+};
+
+#define UrlRole (Qt::UserRole + 1)
+
+class Q_AUTOTEST_EXPORT QFileDialogPrivate : public QDialogPrivate
+{
+ Q_DECLARE_PUBLIC(QFileDialog)
+
+public:
+ QFileDialogPrivate() :
+#ifndef QT_NO_PROXYMODEL
+ proxyModel(0),
+#endif
+ model(0),
+ fileMode(QFileDialog::AnyFile),
+ acceptMode(QFileDialog::AcceptOpen),
+ currentHistoryLocation(-1),
+ renameAction(0),
+ deleteAction(0),
+ showHiddenAction(0),
+ useDefaultCaption(true),
+ defaultFileTypes(true),
+ fileNameLabelExplicitlySat(false),
+ nativeDialogInUse(false),
+#ifdef Q_WS_MAC
+ mDelegate(0),
+#ifndef QT_MAC_USE_COCOA
+ mDialog(0),
+ mDialogStarted(false),
+ mDialogClosed(true),
+#endif
+#endif
+ qFileDialogUi(0)
+ {}
+
+ void createToolButtons();
+ void createMenuActions();
+ void createWidgets();
+
+ void init(const QString &directory = QString(), const QString &nameFilter = QString(),
+ const QString &caption = QString());
+ bool itemViewKeyboardEvent(QKeyEvent *event);
+ QString getEnvironmentVariable(const QString &string);
+ static QString workingDirectory(const QString &path);
+ static QString initialSelection(const QString &path);
+ QStringList typedFiles() const;
+ QStringList addDefaultSuffixToFiles(const QStringList filesToFix) const;
+ bool removeDirectory(const QString &path);
+
+ inline QModelIndex mapToSource(const QModelIndex &index) const;
+ inline QModelIndex mapFromSource(const QModelIndex &index) const;
+ inline QModelIndex rootIndex() const;
+ inline void setRootIndex(const QModelIndex &index) const;
+ inline QModelIndex select(const QModelIndex &index) const;
+ inline QString rootPath() const;
+
+ QLineEdit *lineEdit() const;
+
+ int maxNameLength(const QString &path) {
+#if defined(Q_OS_UNIX)
+ return ::pathconf(QFile::encodeName(path).data(), _PC_NAME_MAX);
+#elif defined(Q_OS_WIN)
+#ifndef Q_OS_WINCE
+ DWORD maxLength;
+ QString drive = path.left(3);
+ if (QT_WA_INLINE(::GetVolumeInformationW(reinterpret_cast<const WCHAR *>(drive.utf16()), NULL, 0, NULL, &maxLength, NULL, NULL, 0),
+ ::GetVolumeInformationA(drive.toLocal8Bit().constData(), NULL, 0, NULL, &maxLength, NULL, NULL, 0)) == FALSE)
+ return -1;
+ return maxLength;
+#else
+ Q_UNUSED(path);
+ return MAX_PATH;
+#endif //Q_OS_WINCE
+#else
+ Q_UNUSED(path);
+#endif
+ return -1;
+ }
+
+ QString basename(const QString &path) const
+ {
+ int separator = QDir::toNativeSeparators(path).lastIndexOf(QDir::separator());
+ if (separator != -1)
+ return path.mid(separator + 1);
+ return path;
+ }
+
+ QDir::Filters filterForMode(QDir::Filters filters) const
+ {
+ if (fileMode == QFileDialog::DirectoryOnly) {
+ filters |= QDir::Drives | QDir::AllDirs | QDir::Dirs;
+ filters &= ~QDir::Files;
+ } else {
+ filters |= QDir::Drives | QDir::AllDirs | QDir::Files | QDir::Dirs;
+ }
+ return filters;
+ }
+
+ QAbstractItemView *currentView() const;
+
+ static inline QString toInternal(const QString &path)
+ {
+#if defined(Q_FS_FAT) || defined(Q_OS_OS2EMX)
+ QString n(path);
+ for (int i = 0; i < (int)n.length(); ++i)
+ if (n[i] == QLatin1Char('\\')) n[i] = QLatin1Char('/');
+#if defined(Q_OS_WINCE)
+ if ((n.size() > 1) && (n.startsWith(QLatin1String("//"))))
+ n = n.mid(1);
+#endif
+ return n;
+#else // the compile should optimize away this
+ return path;
+#endif
+ }
+
+ void setLastVisitedDirectory(const QString &dir);
+ void retranslateWindowTitle();
+ void retranslateStrings();
+ void emitFilesSelected(const QStringList &files);
+
+ void _q_goHome();
+ void _q_pathChanged(const QString &);
+ void _q_navigateBackward();
+ void _q_navigateForward();
+ void _q_navigateToParent();
+ void _q_createDirectory();
+ void _q_showListView();
+ void _q_showDetailsView();
+ void _q_showContextMenu(const QPoint &position);
+ void _q_renameCurrent();
+ void _q_deleteCurrent();
+ void _q_showHidden();
+ void _q_showHeader(QAction *);
+ void _q_updateOkButton();
+ void _q_currentChanged(const QModelIndex &index);
+ void _q_enterDirectory(const QModelIndex &index);
+ void _q_goToDirectory(const QString &);
+ void _q_useNameFilter(int index);
+ void _q_selectionChanged();
+ void _q_goToUrl(const QUrl &url);
+ void _q_autoCompleteFileName(const QString &);
+ void _q_rowsInserted(const QModelIndex & parent);
+ void _q_fileRenamed(const QString &path, const QString oldName, const QString newName);
+
+ // layout
+#ifndef QT_NO_PROXYMODEL
+ QAbstractProxyModel *proxyModel;
+#endif
+
+ // data
+ QStringList watching;
+ QFileSystemModel *model;
+ QCompleter *completer;
+
+ QFileDialog::FileMode fileMode;
+ QFileDialog::AcceptMode acceptMode;
+ bool confirmOverwrite;
+ QString defaultSuffix;
+ QString setWindowTitle;
+
+ QStringList currentHistory;
+ int currentHistoryLocation;
+
+ QAction *renameAction;
+ QAction *deleteAction;
+ QAction *showHiddenAction;
+ QAction *newFolderAction;
+
+ bool useDefaultCaption;
+ bool defaultFileTypes;
+ bool fileNameLabelExplicitlySat;
+ QStringList nameFilters;
+
+ // Members for using native dialogs:
+ bool nativeDialogInUse;
+ // setVisible_sys returns true if it ends up showing a native
+ // dialog. Returning false means that a non-native dialog must be
+ // used instead.
+ bool canBeNativeDialog();
+ bool setVisible_sys(bool visible);
+ void deleteNativeDialog_sys();
+ QDialog::DialogCode dialogResultCode_sys();
+
+ void setDirectory_sys(const QString &directory);
+ QString directory_sys() const;
+ void selectFile_sys(const QString &filename);
+ QStringList selectedFiles_sys() const;
+ void setFilter_sys();
+ void setNameFilters_sys(const QStringList &filters);
+ void selectNameFilter_sys(const QString &filter);
+ QString selectedNameFilter_sys() const;
+ //////////////////////////////////////////////
+
+#if defined(Q_WS_MAC)
+ void *mDelegate;
+#ifndef QT_MAC_USE_COCOA
+ NavDialogRef mDialog;
+ bool mDialogStarted;
+ bool mDialogClosed;
+ QString mCurrentLocation;
+ QString mCurrentSelection;
+ QStringList mCurrentSelectionList;
+
+ struct QtMacFilterName {
+ QString description;
+ QString regexp;
+ QString filter;
+ };
+ struct QtMacNavFilterInfo {
+ QtMacNavFilterInfo() : currentSelection(-1) {}
+ int currentSelection;
+ QList<QtMacFilterName> filters;
+ } filterInfo;
+
+ static void qt_mac_filedialog_event_proc(const NavEventCallbackMessage msg, NavCBRecPtr p,
+ NavCallBackUserData data);
+ static Boolean qt_mac_filedialog_filter_proc(AEDesc *theItem, void *info, void *data,
+ NavFilterModes);
+ bool showCarbonNavServicesDialog();
+ bool hideCarbonNavServicesDialog();
+ void createNavServicesDialog();
+#else
+ bool showCocoaFilePanel();
+ bool hideCocoaFilePanel();
+#endif
+ void createNSOpenSavePanelDelegate();
+ void QNSOpenSavePanelDelegate_selectionChanged(const QString &newPath);
+ void QNSOpenSavePanelDelegate_panelClosed(bool accepted);
+ void QNSOpenSavePanelDelegate_directoryEntered(const QString &newDir);
+ void QNSOpenSavePanelDelegate_filterSelected(int menuIndex);
+ void _q_macRunNativeAppModalPanel();
+ void mac_nativeDialogModalHelp();
+#endif
+
+ Ui_QFileDialog *qFileDialogUi;
+
+ QString acceptLabel;
+
+ QPointer<QObject> receiverToDisconnectOnClose;
+ QByteArray memberToDisconnectOnClose;
+ QByteArray signalToDisconnectOnClose;
+
+ QFileDialog::Options opts;
+};
+
+class QFileDialogLineEdit : public QLineEdit
+{
+public:
+ QFileDialogLineEdit(QWidget *parent = 0) : QLineEdit(parent), hideOnEsc(false), d_ptr(0){}
+ void init(QFileDialogPrivate *d_pointer) {d_ptr = d_pointer; }
+ void keyPressEvent(QKeyEvent *e);
+ bool hideOnEsc;
+private:
+ QFileDialogPrivate *d_ptr;
+};
+
+class QFileDialogComboBox : public QComboBox
+{
+public:
+ QFileDialogComboBox(QWidget *parent = 0) : QComboBox(parent), urlModel(0) {}
+ void init(QFileDialogPrivate *d_pointer);
+ void showPopup();
+ void setHistory(const QStringList &paths);
+ QStringList history() const { return m_history; }
+ void paintEvent(QPaintEvent *);
+
+private:
+ QUrlModel *urlModel;
+ QFileDialogPrivate *d_ptr;
+ QStringList m_history;
+};
+
+class QFileDialogListView : public QListView
+{
+public:
+ QFileDialogListView(QWidget *parent = 0);
+ void init(QFileDialogPrivate *d_pointer);
+ QSize sizeHint() const;
+protected:
+ void keyPressEvent(QKeyEvent *e);
+private:
+ QFileDialogPrivate *d_ptr;
+};
+
+class QFileDialogTreeView : public QTreeView
+{
+public:
+ QFileDialogTreeView(QWidget *parent);
+ void init(QFileDialogPrivate *d_pointer);
+ QSize sizeHint() const;
+
+protected:
+ void keyPressEvent(QKeyEvent *e);
+private:
+ QFileDialogPrivate *d_ptr;
+};
+
+inline QModelIndex QFileDialogPrivate::mapToSource(const QModelIndex &index) const {
+#ifdef QT_NO_PROXYMODEL
+ return index;
+#else
+ return proxyModel ? proxyModel->mapToSource(index) : index;
+#endif
+}
+inline QModelIndex QFileDialogPrivate::mapFromSource(const QModelIndex &index) const {
+#ifdef QT_NO_PROXYMODEL
+ return index;
+#else
+ return proxyModel ? proxyModel->mapFromSource(index) : index;
+#endif
+}
+
+inline QString QFileDialogPrivate::rootPath() const {
+ return model->rootPath();
+}
+
+#ifndef Q_WS_MAC
+ // Dummies for platforms that don't use native dialogs:
+ inline void QFileDialogPrivate::deleteNativeDialog_sys() {}
+ inline bool QFileDialogPrivate::setVisible_sys(bool) { return false; }
+ inline QDialog::DialogCode QFileDialogPrivate::dialogResultCode_sys(){ return QDialog::Rejected; }
+ inline void QFileDialogPrivate::setDirectory_sys(const QString &) {}
+ inline QString QFileDialogPrivate::directory_sys() const { return QString(); }
+ inline void QFileDialogPrivate::selectFile_sys(const QString &) {}
+ inline QStringList QFileDialogPrivate::selectedFiles_sys() const { return QStringList(); }
+ inline void QFileDialogPrivate::setFilter_sys() {}
+ inline void QFileDialogPrivate::setNameFilters_sys(const QStringList &) {}
+ inline void QFileDialogPrivate::selectNameFilter_sys(const QString &) {}
+ inline QString QFileDialogPrivate::selectedNameFilter_sys() const { return QString(); }
+#endif
+
+#ifndef QT_NO_COMPLETER
+/*!
+ QCompleter that can deal with QFileSystemModel
+ */
+class QFSCompletor : public QCompleter {
+public:
+ QFSCompletor(QAbstractItemModel *model, QObject *parent = 0) : QCompleter(model, parent)
+ {
+#ifdef Q_OS_WIN
+ setCaseSensitivity(Qt::CaseInsensitive);
+#endif
+ }
+ QString pathFromIndex(const QModelIndex &index) const;
+ QStringList splitPath(const QString& path) const;
+};
+#endif // QT_NO_COMPLETER
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_FILEDIALOG
+
+#endif // QFILEDIALOG_P_H
diff --git a/src/gui/dialogs/qfiledialog_win.cpp b/src/gui/dialogs/qfiledialog_win.cpp
new file mode 100644
index 0000000000..84314882ef
--- /dev/null
+++ b/src/gui/dialogs/qfiledialog_win.cpp
@@ -0,0 +1,821 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qfiledialog.h"
+
+#ifndef QT_NO_FILEDIALOG
+
+#include <private/qfiledialog_p.h>
+#include <qapplication.h>
+#include <private/qapplication_p.h>
+#include <qt_windows.h>
+#include <qglobal.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>
+# ifndef BFFM_SETSELECTION
+# define BFFM_SETSELECTION (WM_USER + 102)
+# endif
+// Windows Mobile has a broken definition for BROWSEINFO
+// Only compile fix
+typedef struct qt_priv_browseinfo {
+ HWND hwndOwner;
+ LPCITEMIDLIST pidlRoot;
+ LPTSTR pszDisplayName;
+ LPCTSTR lpszTitle;
+ UINT ulFlags;
+ BFFCALLBACK lpfn;
+ LPARAM lParam;
+ int iImage;
+} qt_BROWSEINFO;
+bool qt_priv_ptr_valid = false;
+#endif
+
+
+// Don't remove the lines below!
+//
+// resolving the W methods manually is needed, because Windows 95 doesn't include
+// these methods in Shell32.lib (not even stubs!), so you'd get an unresolved symbol
+// when Qt calls getExistingDirectory(), etc.
+typedef LPITEMIDLIST (WINAPI *PtrSHBrowseForFolder)(BROWSEINFO*);
+static PtrSHBrowseForFolder ptrSHBrowseForFolder = 0;
+typedef BOOL (WINAPI *PtrSHGetPathFromIDList)(LPITEMIDLIST,LPWSTR);
+static PtrSHGetPathFromIDList ptrSHGetPathFromIDList = 0;
+
+QT_BEGIN_NAMESPACE
+
+static void qt_win_resolve_libs()
+{
+ static bool triedResolve = false;
+
+ if (!triedResolve) {
+#ifndef QT_NO_THREAD
+ // protect initialization
+ QMutexLocker locker(QMutexPool::globalInstanceGet(&triedResolve));
+ // check triedResolve again, since another thread may have already
+ // done the initialization
+ if (triedResolve) {
+ // another thread did initialize the security function pointers,
+ // so we shouldn't do it again.
+ return;
+ }
+#endif
+
+ triedResolve = true;
+ if (!(QSysInfo::WindowsVersion & QSysInfo::WV_DOS_based)) {
+#if !defined(Q_OS_WINCE)
+ QLibrary lib(QLatin1String("shell32"));
+ ptrSHBrowseForFolder = (PtrSHBrowseForFolder) lib.resolve("SHBrowseForFolderW");
+ ptrSHGetPathFromIDList = (PtrSHGetPathFromIDList) lib.resolve("SHGetPathFromIDListW");
+#else
+ // CE stores them in a different lib and does not use unicode version
+ HINSTANCE handle = LoadLibraryW(L"Ceshell");
+ ptrSHBrowseForFolder = (PtrSHBrowseForFolder)GetProcAddress(handle, L"SHBrowseForFolder");
+ ptrSHGetPathFromIDList = (PtrSHGetPathFromIDList)GetProcAddress(handle, L"SHGetPathFromIDList");
+ if (ptrSHBrowseForFolder && ptrSHGetPathFromIDList)
+ qt_priv_ptr_valid = true;
+#endif
+ }
+ }
+}
+
+extern const char* qt_file_dialog_filter_reg_exp; // defined in qfiledialog.cpp
+extern QStringList qt_make_filter_list(const QString &filter);
+
+const int maxNameLen = 1023;
+const int maxMultiLen = 65535;
+
+// Returns the wildcard part of a filter.
+static QString qt_win_extract_filter(const QString &rawFilter)
+{
+ QString result = rawFilter;
+ QRegExp r(QString::fromLatin1(qt_file_dialog_filter_reg_exp));
+ int index = r.indexIn(result);
+ if (index >= 0)
+ result = r.cap(2);
+ QStringList list = result.split(QLatin1Char(' '));
+ for(QStringList::iterator it = list.begin(); it < list.end(); ++it) {
+ if (*it == QLatin1String("*")) {
+ *it = QLatin1String("*.*");
+ break;
+ }
+ }
+ return list.join(QLatin1String(";"));
+}
+
+static QStringList qt_win_make_filters_list(const QString &filter)
+{
+ QString f(filter);
+
+ if (f.isEmpty())
+ f = QFileDialog::tr("All Files (*.*)");
+
+ return qt_make_filter_list(f);
+}
+
+// Makes a NUL-oriented Windows filter from a Qt filter.
+static QString qt_win_filter(const QString &filter)
+{
+ QStringList filterLst = qt_win_make_filters_list(filter);
+ QStringList::Iterator it = filterLst.begin();
+ QString winfilters;
+ for (; it != filterLst.end(); ++it) {
+ QString subfilter = *it;
+ if (!subfilter.isEmpty()) {
+ winfilters += subfilter;
+ winfilters += QChar();
+ winfilters += qt_win_extract_filter(subfilter);
+ winfilters += QChar();
+ }
+ }
+ winfilters += QChar();
+ return winfilters;
+}
+
+static QString qt_win_selected_filter(const QString &filter, DWORD idx)
+{
+ return qt_win_make_filters_list(filter).at((int)idx - 1);
+}
+
+#ifndef Q_OS_WINCE
+// Static vars for OFNA funcs:
+static QByteArray aInitDir;
+static QByteArray aInitSel;
+static QByteArray aTitle;
+static QByteArray aFilter;
+// Use ANSI strings and API
+
+// If you change this, then make sure you change qt_win_make_OFN (below) too
+static OPENFILENAMEA *qt_win_make_OFNA(QWidget *parent,
+ const QString &initialSelection,
+ const QString &initialDirectory,
+ const QString &title,
+ const QString &filters,
+ QFileDialog::FileMode mode,
+ QFileDialog::Options options)
+{
+ if (parent)
+ parent = parent->window();
+ else
+ parent = qApp->activeWindow();
+
+ aTitle = title.toLocal8Bit();
+ aInitDir = QDir::toNativeSeparators(initialDirectory).toLocal8Bit();
+ if (initialSelection.isEmpty()) {
+ aInitSel = "";
+ } else {
+ aInitSel = QDir::toNativeSeparators(initialSelection).toLocal8Bit();
+ aInitSel.replace("<", "");
+ aInitSel.replace(">", "");
+ aInitSel.replace("\"", "");
+ aInitSel.replace("|", "");
+ }
+ int maxLen = mode == QFileDialog::ExistingFiles ? maxMultiLen : maxNameLen;
+ aInitSel.resize(maxLen + 1); // make room for return value
+ aFilter = filters.toLocal8Bit();
+
+ OPENFILENAMEA* ofn = new OPENFILENAMEA;
+ memset(ofn, 0, sizeof(OPENFILENAMEA));
+
+#if defined(Q_CC_BOR) && (WINVER >= 0x0500) && (_WIN32_WINNT >= 0x0500)
+ // according to the MSDN, this should also be necessary for MSVC, but
+ // OPENFILENAME_SIZE_VERSION_400A is in not Microsoft header, as it seems
+ if (QApplication::winVersion()==Qt::WV_NT || QApplication::winVersion()&Qt::WV_DOS_based) {
+ ofn->lStructSize = OPENFILENAME_SIZE_VERSION_400A;
+ } else {
+ ofn->lStructSize = sizeof(OPENFILENAMEA);
+ }
+#else
+ ofn->lStructSize = sizeof(OPENFILENAMEA);
+#endif
+ Q_ASSERT(!parent ||parent->testAttribute(Qt::WA_WState_Created));
+ ofn->hwndOwner = parent ? parent->winId() : 0;
+ ofn->lpstrFilter = aFilter;
+ ofn->lpstrFile = aInitSel.data();
+ ofn->nMaxFile = maxLen;
+ ofn->lpstrInitialDir = aInitDir.data();
+ ofn->lpstrTitle = aTitle.data();
+ ofn->Flags = (OFN_NOCHANGEDIR | OFN_HIDEREADONLY | OFN_EXPLORER);
+
+ if (mode == QFileDialog::ExistingFile ||
+ mode == QFileDialog::ExistingFiles)
+ ofn->Flags |= (OFN_FILEMUSTEXIST | OFN_PATHMUSTEXIST);
+ if (mode == QFileDialog::ExistingFiles)
+ ofn->Flags |= (OFN_ALLOWMULTISELECT);
+ if (!(options & QFileDialog::DontConfirmOverwrite))
+ ofn->Flags |= OFN_OVERWRITEPROMPT;
+
+ return ofn;
+}
+
+static void qt_win_clean_up_OFNA(OPENFILENAMEA **ofn)
+{
+ delete *ofn;
+ *ofn = 0;
+}
+#endif
+
+static QString tFilters, tTitle, tInitDir;
+
+#ifdef UNICODE
+// If you change this, then make sure you change qt_win_make_OFNA (above) too
+static OPENFILENAME* qt_win_make_OFN(QWidget *parent,
+ const QString& initialSelection,
+ const QString& initialDirectory,
+ const QString& title,
+ const QString& filters,
+ QFileDialog::FileMode mode,
+ QFileDialog::Options options)
+{
+ if (parent)
+ parent = parent->window();
+ else
+ parent = qApp->activeWindow();
+
+ tInitDir = QDir::toNativeSeparators(initialDirectory);
+ tFilters = filters;
+ tTitle = title;
+ QString initSel = QDir::toNativeSeparators(initialSelection);
+ if (!initSel.isEmpty()) {
+ initSel.replace(QLatin1String("<"), QLatin1String(""));
+ initSel.replace(QLatin1String(">"), QLatin1String(""));
+ initSel.replace(QLatin1String("\""), QLatin1String(""));
+ initSel.replace(QLatin1String("|"), QLatin1String(""));
+ }
+
+ int maxLen = mode == QFileDialog::ExistingFiles ? maxMultiLen : maxNameLen;
+ TCHAR *tInitSel = new TCHAR[maxLen+1];
+ if (initSel.length() > 0 && initSel.length() <= maxLen)
+ memcpy(tInitSel, initSel.utf16(), (initSel.length()+1)*sizeof(QChar));
+ else
+ tInitSel[0] = 0;
+
+ OPENFILENAME* ofn = new OPENFILENAME;
+ memset(ofn, 0, sizeof(OPENFILENAME));
+
+#if defined(Q_CC_BOR) && (WINVER >= 0x0500) && (_WIN32_WINNT >= 0x0500)
+ // according to the MSDN, this should also be necessary for MSVC, but
+ // OPENFILENAME_SIZE_VERSION_400 is in not Microsoft header, as it seems
+ if (QApplication::winVersion()==Qt::WV_NT || QApplication::winVersion()&Qt::WV_DOS_based) {
+ ofn->lStructSize= OPENFILENAME_SIZE_VERSION_400;
+ } else {
+ ofn->lStructSize = sizeof(OPENFILENAME);
+ }
+#else
+ ofn->lStructSize = sizeof(OPENFILENAME);
+#endif
+ Q_ASSERT(!parent ||parent->testAttribute(Qt::WA_WState_Created));
+ ofn->hwndOwner = parent ? parent->winId() : 0;
+ ofn->lpstrFilter = (TCHAR *)tFilters.utf16();
+ ofn->lpstrFile = tInitSel;
+ ofn->nMaxFile = maxLen;
+ ofn->lpstrInitialDir = (TCHAR *)tInitDir.utf16();
+ ofn->lpstrTitle = (TCHAR *)tTitle.utf16();
+ ofn->Flags = (OFN_NOCHANGEDIR | OFN_HIDEREADONLY | OFN_EXPLORER);
+ if (mode == QFileDialog::ExistingFile ||
+ mode == QFileDialog::ExistingFiles)
+ ofn->Flags |= (OFN_FILEMUSTEXIST | OFN_PATHMUSTEXIST);
+ if (mode == QFileDialog::ExistingFiles)
+ ofn->Flags |= (OFN_ALLOWMULTISELECT);
+ if (!(options & QFileDialog::DontConfirmOverwrite))
+ ofn->Flags |= OFN_OVERWRITEPROMPT;
+
+ return ofn;
+}
+
+
+static void qt_win_clean_up_OFN(OPENFILENAME **ofn)
+{
+ delete [] (*ofn)->lpstrFile;
+ delete *ofn;
+ *ofn = 0;
+}
+
+#endif // UNICODE
+
+extern void qt_win_eatMouseMove();
+
+QString qt_win_get_open_file_name(const QFileDialogArgs &args,
+ QString *initialDirectory,
+ QString *selectedFilter)
+{
+ QString result;
+
+ QString isel = args.selection;
+
+ if (initialDirectory && initialDirectory->left(5) == QLatin1String("file:"))
+ initialDirectory->remove(0, 5);
+ QFileInfo fi(*initialDirectory);
+
+ if (initialDirectory && !fi.isDir()) {
+ *initialDirectory = fi.absolutePath();
+ if (isel.isEmpty())
+ isel = fi.fileName();
+ }
+
+ if (!fi.exists())
+ *initialDirectory = QDir::homePath();
+
+ DWORD selFilIdx = 0;
+
+ int idx = 0;
+ if (selectedFilter) {
+ QStringList filterLst = qt_win_make_filters_list(args.filter);
+ idx = filterLst.indexOf(*selectedFilter);
+ }
+
+ QDialog modal_widget;
+ modal_widget.setAttribute(Qt::WA_NoChildEventsForParent, true);
+ modal_widget.setParent(args.parent, Qt::Window);
+ QApplicationPrivate::enterModal(&modal_widget);
+ QT_WA({
+ // Use Unicode strings and API
+ OPENFILENAME* ofn = qt_win_make_OFN(args.parent, args.selection,
+ args.directory, args.caption,
+ qt_win_filter(args.filter),
+ QFileDialog::ExistingFile,
+ args.options);
+ if (idx)
+ ofn->nFilterIndex = idx + 1;
+ if (GetOpenFileName(ofn)) {
+ result = QString::fromUtf16((ushort*)ofn->lpstrFile);
+ selFilIdx = ofn->nFilterIndex;
+ }
+ qt_win_clean_up_OFN(&ofn);
+ } , {
+ // Use ANSI strings and API
+ OPENFILENAMEA* ofn = qt_win_make_OFNA(args.parent, args.selection,
+ args.directory, args.caption,
+ qt_win_filter(args.filter),
+ QFileDialog::ExistingFile,
+ args.options);
+ if (idx)
+ ofn->nFilterIndex = idx + 1;
+ if (GetOpenFileNameA(ofn)) {
+ result = QString::fromLocal8Bit(ofn->lpstrFile);
+ selFilIdx = ofn->nFilterIndex;
+ }
+ qt_win_clean_up_OFNA(&ofn);
+ });
+ QApplicationPrivate::leaveModal(&modal_widget);
+
+ qt_win_eatMouseMove();
+
+ if (result.isEmpty())
+ return result;
+
+ fi = result;
+ *initialDirectory = fi.path();
+ if (selectedFilter)
+ *selectedFilter = qt_win_selected_filter(args.filter, selFilIdx);
+ return fi.absoluteFilePath();
+}
+
+QString qt_win_get_save_file_name(const QFileDialogArgs &args,
+ QString *initialDirectory,
+ QString *selectedFilter)
+{
+ QString result;
+
+ QString isel = args.selection;
+ if (initialDirectory && initialDirectory->left(5) == QLatin1String("file:"))
+ initialDirectory->remove(0, 5);
+ QFileInfo fi(*initialDirectory);
+
+ if (initialDirectory && !fi.isDir()) {
+ *initialDirectory = fi.absolutePath();
+ if (isel.isEmpty())
+ isel = fi.fileName();
+ }
+
+ if (!fi.exists())
+ *initialDirectory = QDir::homePath();
+
+ DWORD selFilIdx = 0;
+
+ int idx = 0;
+ if (selectedFilter) {
+ QStringList filterLst = qt_win_make_filters_list(args.filter);
+ idx = filterLst.indexOf(*selectedFilter);
+ }
+
+ QDialog modal_widget;
+ modal_widget.setAttribute(Qt::WA_NoChildEventsForParent, true);
+ modal_widget.setParent(args.parent, Qt::Window);
+ QApplicationPrivate::enterModal(&modal_widget);
+
+ // This block is used below for the lpstrDefExt member.
+ // Note that the current MSDN docs document this member wrong.
+ // It should rather be documented as "the default extension if no extension was given and if the
+ // current filter does not have a extension (e.g (*)). If the current filter have an extension, use
+ // the extension of the current filter"
+ QString defaultSaveExt;
+ if (selectedFilter && !selectedFilter->isEmpty()) {
+ defaultSaveExt = qt_win_extract_filter(*selectedFilter);
+ // make sure we only have the extension
+ int firstDot = defaultSaveExt.indexOf(QLatin1Char('.'));
+ if (firstDot != -1) {
+ defaultSaveExt.remove(0, firstDot + 1);
+ } else {
+ defaultSaveExt.clear();
+ }
+ }
+
+ QT_WA({
+ // Use Unicode strings and API
+ OPENFILENAME *ofn = qt_win_make_OFN(args.parent, args.selection,
+ args.directory, args.caption,
+ qt_win_filter(args.filter),
+ QFileDialog::AnyFile,
+ args.options);
+
+ ofn->lpstrDefExt = (TCHAR *)defaultSaveExt.utf16();
+
+ if (idx)
+ ofn->nFilterIndex = idx + 1;
+ if (GetSaveFileName(ofn)) {
+ result = QString::fromUtf16((ushort*)ofn->lpstrFile);
+ selFilIdx = ofn->nFilterIndex;
+ }
+ qt_win_clean_up_OFN(&ofn);
+ } , {
+ // Use ANSI strings and API
+ OPENFILENAMEA *ofn = qt_win_make_OFNA(args.parent, args.selection,
+ args.directory, args.caption,
+ qt_win_filter(args.filter),
+ QFileDialog::AnyFile,
+ args.options);
+ QByteArray asciiExt = defaultSaveExt.toAscii();
+ ofn->lpstrDefExt = asciiExt.data();
+
+ if (idx)
+ ofn->nFilterIndex = idx + 1;
+ if (GetSaveFileNameA(ofn)) {
+ result = QString::fromLocal8Bit(ofn->lpstrFile);
+ selFilIdx = ofn->nFilterIndex;
+ }
+ qt_win_clean_up_OFNA(&ofn);
+ });
+#if defined(Q_OS_WINCE)
+ int semIndex = result.indexOf(QLatin1Char(';'));
+ if (semIndex >= 0)
+ result = result.left(semIndex);
+#endif
+
+ QApplicationPrivate::leaveModal(&modal_widget);
+
+ qt_win_eatMouseMove();
+
+ if (result.isEmpty())
+ return result;
+
+ fi = result;
+ *initialDirectory = fi.path();
+ if (selectedFilter)
+ *selectedFilter = qt_win_selected_filter(args.filter, selFilIdx);
+ return fi.absoluteFilePath();
+}
+
+QStringList qt_win_get_open_file_names(const QFileDialogArgs &args,
+ QString *initialDirectory,
+ 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.absolutePath();
+ isel = fi.fileName();
+ }
+
+ if (!fi.exists())
+ *initialDirectory = QDir::homePath();
+
+ DWORD selFilIdx = 0;
+
+ int idx = 0;
+ if (selectedFilter) {
+ QStringList filterLst = qt_win_make_filters_list(args.filter);
+ idx = filterLst.indexOf(*selectedFilter);
+ }
+
+ QDialog modal_widget;
+ modal_widget.setAttribute(Qt::WA_NoChildEventsForParent, true);
+ modal_widget.setParent(args.parent, Qt::Window);
+ QApplicationPrivate::enterModal(&modal_widget);
+ QT_WA({
+ OPENFILENAME* ofn = qt_win_make_OFN(args.parent, args.selection,
+ args.directory, args.caption,
+ qt_win_filter(args.filter),
+ QFileDialog::ExistingFiles,
+ args.options);
+ if (idx)
+ ofn->nFilterIndex = idx + 1;
+ if (GetOpenFileName(ofn)) {
+ QString fileOrDir = QString::fromUtf16((ushort*)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.absoluteFilePath();
+ if (!res.isEmpty())
+ result.append(res);
+ }
+ else {
+ // Several files selected; first string is path
+ dir.setPath(fileOrDir);
+ QString f;
+ while(!(f = QString::fromUtf16((ushort*)ofn->lpstrFile+offset)).isEmpty()) {
+ fi.setFile(dir, f);
+ QString res = fi.absoluteFilePath();
+ if (!res.isEmpty())
+ result.append(res);
+ offset += f.length() + 1;
+ }
+ }
+ }
+ qt_win_clean_up_OFN(&ofn);
+ } , {
+ OPENFILENAMEA* ofn = qt_win_make_OFNA(args.parent, args.selection,
+ args.directory, args.caption,
+ qt_win_filter(args.filter),
+ QFileDialog::ExistingFiles,
+ args.options);
+ if (idx)
+ ofn->nFilterIndex = idx + 1;
+ if (GetOpenFileNameA(ofn)) {
+ QByteArray fileOrDir(ofn->lpstrFile);
+ selFilIdx = ofn->nFilterIndex;
+ int offset = fileOrDir.length() + 1;
+ if (ofn->lpstrFile[offset] == '\0') {
+ // Only one file selected; has full path
+ fi.setFile(QString::fromLocal8Bit(fileOrDir));
+ QString res = fi.absoluteFilePath();
+ if (!res.isEmpty())
+ result.append(res);
+ }
+ else {
+ // Several files selected; first string is path
+ dir.setPath(QString::fromLocal8Bit(fileOrDir));
+ QByteArray f;
+ while (!(f = QByteArray(ofn->lpstrFile + offset)).isEmpty()) {
+ fi.setFile(dir, QString::fromLocal8Bit(f));
+ QString res = fi.absoluteFilePath();
+ if (!res.isEmpty())
+ result.append(res);
+ offset += f.length() + 1;
+ }
+ }
+ qt_win_clean_up_OFNA(&ofn);
+ }
+ });
+ QApplicationPrivate::leaveModal(&modal_widget);
+
+ qt_win_eatMouseMove();
+
+ if (!result.isEmpty()) {
+ *initialDirectory = fi.path(); // only save the path if there is a result
+ if (selectedFilter)
+ *selectedFilter = qt_win_selected_filter(args.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)
+{
+ if (uMsg == BFFM_INITIALIZED && lpData != 0) {
+ QString *initDir = (QString *)(lpData);
+ if (!initDir->isEmpty()) {
+ // ### Lars asks: is this correct for the A version????
+ QT_WA({
+ SendMessage(hwnd, BFFM_SETSELECTION, TRUE, LPARAM(initDir->utf16()));
+ } , {
+ SendMessageA(hwnd, BFFM_SETSELECTION, TRUE, LPARAM(initDir->utf16()));
+ });
+ }
+ } else if (uMsg == BFFM_SELCHANGED) {
+ QT_WA({
+ qt_win_resolve_libs();
+ TCHAR path[MAX_PATH];
+ ptrSHGetPathFromIDList(LPITEMIDLIST(lParam), path);
+ QString tmpStr = QString::fromUtf16((ushort*)path);
+ if (!tmpStr.isEmpty())
+ SendMessage(hwnd, BFFM_ENABLEOK, 1, 1);
+ else
+ SendMessage(hwnd, BFFM_ENABLEOK, 0, 0);
+ SendMessage(hwnd, BFFM_SETSTATUSTEXT, 1, LPARAM(path));
+ } , {
+ char path[MAX_PATH];
+ SHGetPathFromIDListA(LPITEMIDLIST(lParam), path);
+ QString tmpStr = QString::fromLocal8Bit(path);
+ if (!tmpStr.isEmpty())
+ SendMessageA(hwnd, BFFM_ENABLEOK, 1, 1);
+ else
+ SendMessageA(hwnd, BFFM_ENABLEOK, 0, 0);
+ SendMessageA(hwnd, BFFM_SETSTATUSTEXT, 1, LPARAM(path));
+ });
+ }
+ return 0;
+}
+
+#ifndef BIF_NEWDIALOGSTYLE
+#define BIF_NEWDIALOGSTYLE 0x0040 // Use the new dialog layout with the ability to resize
+#endif
+
+
+QString qt_win_get_existing_directory(const QFileDialogArgs &args)
+{
+ QString currentDir = QDir::currentPath();
+ QString result;
+ QWidget *parent = args.parent;
+ if (parent)
+ parent = parent->window();
+ else
+ parent = qApp->activeWindow();
+ if (parent)
+ parent->createWinId();
+
+ QDialog modal_widget;
+ modal_widget.setAttribute(Qt::WA_NoChildEventsForParent, true);
+ modal_widget.setParent(parent, Qt::Window);
+ QApplicationPrivate::enterModal(&modal_widget);
+#if !defined(Q_OS_WINCE)
+ QT_WA({
+ qt_win_resolve_libs();
+ QString initDir = QDir::toNativeSeparators(args.directory);
+ TCHAR path[MAX_PATH];
+ TCHAR initPath[MAX_PATH];
+ initPath[0] = 0;
+ path[0] = 0;
+ tTitle = args.caption;
+ BROWSEINFO bi;
+ Q_ASSERT(!parent ||parent->testAttribute(Qt::WA_WState_Created));
+ bi.hwndOwner = (parent ? parent->winId() : 0);
+ bi.pidlRoot = NULL;
+ //### This does not seem to be respected? - the dialog always displays "Browse for folder"
+ bi.lpszTitle = (TCHAR*)tTitle.utf16();
+ bi.pszDisplayName = initPath;
+ bi.ulFlags = BIF_RETURNONLYFSDIRS | BIF_STATUSTEXT | BIF_NEWDIALOGSTYLE;
+ bi.lpfn = winGetExistDirCallbackProc;
+ bi.lParam = LPARAM(&initDir);
+ if (ptrSHBrowseForFolder) {
+ LPITEMIDLIST pItemIDList = ptrSHBrowseForFolder(&bi);
+ if (pItemIDList && ptrSHGetPathFromIDList) {
+ ptrSHGetPathFromIDList(pItemIDList, path);
+ IMalloc *pMalloc;
+ if (SHGetMalloc(&pMalloc) != NOERROR)
+ result = QString();
+ else {
+ pMalloc->Free(pItemIDList);
+ pMalloc->Release();
+ result = QString::fromUtf16((ushort*)path);
+ }
+ } else
+ result = QString();
+ }
+ tTitle = QString();
+ } , {
+ QString initDir = QDir::toNativeSeparators(args.directory);
+ char path[MAX_PATH];
+ char initPath[MAX_PATH];
+ QByteArray ctitle = args.caption.toLocal8Bit();
+ initPath[0]=0;
+ path[0]=0;
+ BROWSEINFOA bi;
+ Q_ASSERT(!parent ||parent->testAttribute(Qt::WA_WState_Created));
+ bi.hwndOwner = (parent ? parent->winId() : 0);
+ bi.pidlRoot = NULL;
+ bi.lpszTitle = ctitle;
+ bi.pszDisplayName = initPath;
+ bi.ulFlags = BIF_RETURNONLYFSDIRS | BIF_STATUSTEXT | BIF_NEWDIALOGSTYLE;
+ bi.lpfn = winGetExistDirCallbackProc;
+ bi.lParam = LPARAM(&initDir);
+ LPITEMIDLIST pItemIDList = SHBrowseForFolderA(&bi);
+ if (pItemIDList) {
+ SHGetPathFromIDListA(pItemIDList, path);
+ IMalloc *pMalloc;
+ if (SHGetMalloc(&pMalloc) != NOERROR)
+ result = QString();
+ else {
+ pMalloc->Free(pItemIDList);
+ pMalloc->Release();
+ result = QString::fromLocal8Bit(path);
+ }
+ } else
+ result = QString();
+ });
+#else
+ qt_win_resolve_libs();
+ QString initDir = QDir::toNativeSeparators(args.directory);
+ TCHAR path[MAX_PATH];
+ TCHAR initPath[MAX_PATH];
+ memset(initPath, 0 , MAX_PATH*sizeof(TCHAR));
+ memset(path, 0, MAX_PATH*sizeof(TCHAR));
+ tTitle = args.caption;
+ qt_BROWSEINFO bi;
+ Q_ASSERT(!parent ||parent->testAttribute(Qt::WA_WState_Created));
+ bi.hwndOwner = (parent ? parent->winId() : 0);
+ bi.pidlRoot = NULL;
+ bi.lpszTitle = (TCHAR*)tTitle.utf16();
+ bi.pszDisplayName = initPath;
+ bi.ulFlags = BIF_RETURNONLYFSDIRS | BIF_STATUSTEXT | BIF_NEWDIALOGSTYLE;
+ bi.lpfn = winGetExistDirCallbackProc;
+ bi.lParam = LPARAM(&initDir);
+ if (ptrSHBrowseForFolder) {
+ LPITEMIDLIST pItemIDList = ptrSHBrowseForFolder((BROWSEINFO*)&bi);
+ if (pItemIDList && ptrSHGetPathFromIDList) {
+ ptrSHGetPathFromIDList(pItemIDList, path);
+ IMalloc *pMalloc;
+ if (SHGetMalloc(&pMalloc) != NOERROR)
+ result = QString();
+ else {
+ pMalloc->Free(pItemIDList);
+ pMalloc->Release();
+ result = QString::fromUtf16((ushort*)path);
+ }
+ } else
+ result = QString();
+ }
+ tTitle = QString();
+
+#endif
+ QApplicationPrivate::leaveModal(&modal_widget);
+
+ qt_win_eatMouseMove();
+
+ // Due to a bug on Windows Me, we need to reset the current
+ // directory
+ if ((QSysInfo::WindowsVersion == QSysInfo::WV_98 || QSysInfo::WindowsVersion == QSysInfo::WV_Me)
+ && QDir::currentPath() != currentDir)
+ QDir::setCurrent(currentDir);
+
+ if (!result.isEmpty())
+ result.replace(QLatin1String("\\"), QLatin1String("/"));
+ return result;
+}
+
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/gui/dialogs/qfiledialog_wince.ui b/src/gui/dialogs/qfiledialog_wince.ui
new file mode 100644
index 0000000000..8aa9e1b30f
--- /dev/null
+++ b/src/gui/dialogs/qfiledialog_wince.ui
@@ -0,0 +1,342 @@
+<ui version="4.0" >
+ <comment>*********************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
+** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+**
+*********************************************************************</comment>
+ <class>QFileDialog</class>
+ <widget class="QDialog" name="QFileDialog" >
+ <property name="geometry" >
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>240</width>
+ <height>320</height>
+ </rect>
+ </property>
+ <property name="sizeGripEnabled" >
+ <bool>true</bool>
+ </property>
+ <layout class="QVBoxLayout" >
+ <item>
+ <widget class="QFileDialogComboBox" name="lookInCombo" >
+ <property name="sizePolicy" >
+ <sizepolicy vsizetype="Fixed" hsizetype="Expanding" >
+ <horstretch>1</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" >
+ <item>
+ <widget class="QToolButton" name="backButton" >
+ <property name="toolTip" >
+ <string>Back</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QToolButton" name="forwardButton" >
+ <property name="toolTip" >
+ <string>Forward</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QToolButton" name="toParentButton" >
+ <property name="toolTip" >
+ <string>Parent Directory</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QToolButton" name="newFolderButton" >
+ <property name="toolTip" >
+ <string>Create New Folder</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QToolButton" name="listModeButton" >
+ <property name="toolTip" >
+ <string>List View</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QToolButton" name="detailModeButton" >
+ <property name="toolTip" >
+ <string>Detail View</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <widget class="QSplitter" name="splitter" >
+ <property name="sizePolicy" >
+ <sizepolicy vsizetype="Expanding" hsizetype="Expanding" >
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="orientation" >
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <widget class="QSidebar" name="sidebar" />
+ <widget class="QFrame" name="frame" >
+ <property name="frameShape" >
+ <enum>QFrame::NoFrame</enum>
+ </property>
+ <property name="frameShadow" >
+ <enum>QFrame::Raised</enum>
+ </property>
+ <layout class="QVBoxLayout" >
+ <property name="spacing" >
+ <number>0</number>
+ </property>
+ <property name="margin" >
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="QStackedWidget" name="stackedWidget" >
+ <property name="currentIndex" >
+ <number>0</number>
+ </property>
+ <widget class="QWidget" name="page" >
+ <property name="geometry" >
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>108</width>
+ <height>164</height>
+ </rect>
+ </property>
+ <layout class="QVBoxLayout" >
+ <property name="spacing" >
+ <number>0</number>
+ </property>
+ <property name="margin" >
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="QFileDialogListView" name="listView" />
+ </item>
+ </layout>
+ </widget>
+ <widget class="QWidget" name="page_2" >
+ <property name="geometry" >
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>100</width>
+ <height>30</height>
+ </rect>
+ </property>
+ <layout class="QVBoxLayout" >
+ <property name="spacing" >
+ <number>0</number>
+ </property>
+ <property name="margin" >
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="QFileDialogTreeView" name="treeView" />
+ </item>
+ </layout>
+ </widget>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </widget>
+ </item>
+ <item>
+ <layout class="QGridLayout" >
+ <item row="0" column="0" >
+ <widget class="QFileDialogLineEdit" name="fileNameEdit" >
+ <property name="sizePolicy" >
+ <sizepolicy vsizetype="Fixed" hsizetype="Expanding" >
+ <horstretch>1</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ </widget>
+ </item>
+ <item rowspan="2" row="0" column="1" >
+ <widget class="QDialogButtonBox" name="buttonBox" >
+ <property name="orientation" >
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="standardButtons" >
+ <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0" >
+ <widget class="QComboBox" name="fileTypeCombo" >
+ <property name="sizePolicy" >
+ <sizepolicy vsizetype="Fixed" hsizetype="Expanding" >
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <widget class="QLabel" name="fileNameLabel" >
+ <property name="enabled" >
+ <bool>false</bool>
+ </property>
+ <property name="sizePolicy" >
+ <sizepolicy vsizetype="Fixed" hsizetype="Fixed" >
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize" >
+ <size>
+ <width>0</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize" >
+ <size>
+ <width>0</width>
+ <height>0</height>
+ </size>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLabel" name="fileTypeLabel" >
+ <property name="enabled" >
+ <bool>false</bool>
+ </property>
+ <property name="sizePolicy" >
+ <sizepolicy vsizetype="Fixed" hsizetype="Fixed" >
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="maximumSize" >
+ <size>
+ <width>0</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="text" >
+ <string>Files of type:</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLabel" name="lookInLabel" >
+ <property name="enabled" >
+ <bool>false</bool>
+ </property>
+ <property name="sizePolicy" >
+ <sizepolicy vsizetype="Fixed" hsizetype="Fixed" >
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="maximumSize" >
+ <size>
+ <width>0</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="text" >
+ <string>Look in:</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <customwidgets>
+ <customwidget>
+ <class>QFileDialogTreeView</class>
+ <extends>QTreeView</extends>
+ <header>qfiledialog_p.h</header>
+ </customwidget>
+ <customwidget>
+ <class>QFileDialogListView</class>
+ <extends>QListView</extends>
+ <header>qfiledialog_p.h</header>
+ </customwidget>
+ <customwidget>
+ <class>QSidebar</class>
+ <extends>QListWidget</extends>
+ <header>qsidebar_p.h</header>
+ </customwidget>
+ <customwidget>
+ <class>QFileDialogLineEdit</class>
+ <extends>QLineEdit</extends>
+ <header>qfiledialog_p.h</header>
+ </customwidget>
+ <customwidget>
+ <class>QFileDialogComboBox</class>
+ <extends>QComboBox</extends>
+ <header>qfiledialog_p.h</header>
+ </customwidget>
+ </customwidgets>
+ <tabstops>
+ <tabstop>lookInCombo</tabstop>
+ <tabstop>backButton</tabstop>
+ <tabstop>forwardButton</tabstop>
+ <tabstop>toParentButton</tabstop>
+ <tabstop>newFolderButton</tabstop>
+ <tabstop>listModeButton</tabstop>
+ <tabstop>detailModeButton</tabstop>
+ <tabstop>sidebar</tabstop>
+ <tabstop>listView</tabstop>
+ <tabstop>fileNameEdit</tabstop>
+ <tabstop>fileTypeCombo</tabstop>
+ <tabstop>buttonBox</tabstop>
+ <tabstop>treeView</tabstop>
+ </tabstops>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/src/gui/dialogs/qfileinfogatherer.cpp b/src/gui/dialogs/qfileinfogatherer.cpp
new file mode 100644
index 0000000000..3fe64ff7a2
--- /dev/null
+++ b/src/gui/dialogs/qfileinfogatherer.cpp
@@ -0,0 +1,365 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qfileinfogatherer_p.h"
+#include <qdebug.h>
+#include <qfsfileengine.h>
+#include <qdiriterator.h>
+#ifndef Q_OS_WIN
+#include <unistd.h>
+#include <sys/types.h>
+#endif
+
+QT_BEGIN_NAMESPACE
+
+#ifndef QT_NO_FILESYSTEMMODEL
+
+bool QFileInfoGatherer::fetchedRoot = false;
+
+/*!
+ Creates thread
+*/
+QFileInfoGatherer::QFileInfoGatherer(QObject *parent)
+ : QThread(parent), abort(false),
+#ifndef QT_NO_FILESYSTEMWATCHER
+ watcher(0),
+#endif
+ m_resolveSymlinks(false), m_iconProvider(&defaultProvider)
+{
+#ifndef Q_OS_WIN
+ userId = getuid();
+ groupId = getgid();
+#else
+ m_resolveSymlinks = true;
+#endif
+#ifndef QT_NO_FILESYSTEMWATCHER
+ watcher = new QFileSystemWatcher(this);
+ connect(watcher, SIGNAL(directoryChanged(const QString &)), this, SLOT(list(const QString &)));
+ connect(watcher, SIGNAL(fileChanged(const QString &)), this, SLOT(updateFile(const QString &)));
+#endif
+ start(LowPriority);
+}
+
+/*!
+ Destroys thread
+*/
+QFileInfoGatherer::~QFileInfoGatherer()
+{
+ mutex.lock();
+ abort = true;
+ condition.wakeOne();
+ mutex.unlock();
+ wait();
+}
+
+void QFileInfoGatherer::setResolveSymlinks(bool enable)
+{
+ Q_UNUSED(enable);
+#ifdef Q_OS_WIN
+ mutex.lock();
+ m_resolveSymlinks = enable;
+ mutex.unlock();
+#endif
+}
+
+bool QFileInfoGatherer::resolveSymlinks() const
+{
+ return m_resolveSymlinks;
+}
+
+void QFileInfoGatherer::setIconProvider(QFileIconProvider *provider)
+{
+ mutex.lock();
+ m_iconProvider = provider;
+ mutex.unlock();
+}
+
+QFileIconProvider *QFileInfoGatherer::iconProvider() const
+{
+ return m_iconProvider;
+}
+
+/*!
+ Fetch extended information for all \a files in \a path
+
+ \sa updateFile(), update(), resolvedName()
+*/
+void QFileInfoGatherer::fetchExtendedInformation(const QString &path, const QStringList &files)
+{
+ mutex.lock();
+ // See if we already have this dir/file in our que
+ int loc = this->path.lastIndexOf(path);
+ while (loc > 0) {
+ if (this->files.at(loc) == files) {
+ mutex.unlock();
+ return;
+ }
+ loc = this->path.lastIndexOf(path, loc - 1);
+ }
+ this->path.push(path);
+ this->files.push(files);
+ condition.wakeAll();
+ mutex.unlock();
+}
+
+/*!
+ Fetch extended information for all \a filePath
+
+ \sa fetchExtendedInformation()
+*/
+void QFileInfoGatherer::updateFile(const QString &filePath)
+{
+ QString dir = filePath.mid(0, filePath.lastIndexOf(QDir::separator()));
+ QString fileName = filePath.mid(dir.length() + 1);
+ fetchExtendedInformation(dir, QStringList(fileName));
+}
+
+/*
+ List all files in \a directoryPath
+
+ \sa listed()
+*/
+void QFileInfoGatherer::clear()
+{
+#ifndef QT_NO_FILESYSTEMWATCHER
+ mutex.lock();
+ watcher->removePaths(watcher->files());
+ watcher->removePaths(watcher->directories());
+ mutex.unlock();
+#endif
+}
+
+/*
+ List all files in \a directoryPath
+
+ \sa listed()
+*/
+void QFileInfoGatherer::list(const QString &directoryPath)
+{
+ fetchExtendedInformation(directoryPath, QStringList());
+}
+
+/*
+ Until aborted wait to fetch a directory or files
+*/
+void QFileInfoGatherer::run()
+{
+ forever {
+ bool updateFiles = false;
+ mutex.lock();
+ if (abort) {
+ mutex.unlock();
+ return;
+ }
+ if (this->path.isEmpty())
+ condition.wait(&mutex);
+ QString path;
+ QStringList list;
+ if (!this->path.isEmpty()) {
+ path = this->path.first();
+ list = this->files.first();
+ this->path.pop_front();
+ this->files.pop_front();
+ updateFiles = true;
+ }
+ mutex.unlock();
+ if (updateFiles) getFileInfos(path, list);
+ }
+}
+
+/*
+ QFileInfo::permissions is different depending upon your platform.
+
+ "normalize this" so they can mean the same to us.
+*/
+QFile::Permissions QFileInfoGatherer::translatePermissions(const QFileInfo &fileInfo) const {
+ QFile::Permissions permissions = fileInfo.permissions();
+#ifdef Q_OS_WIN
+ return permissions;
+#else
+ QFile::Permissions p = permissions;
+ p &= ~(QFile::ReadUser|QFile::WriteUser|QFile::ExeUser);
+ if ( permissions & QFile::ReadOther
+ || (fileInfo.ownerId() == userId && permissions & QFile::ReadOwner)
+ || (fileInfo.groupId() == groupId && permissions & QFile::ReadGroup))
+ p |= QFile::ReadUser;
+
+ if ( permissions & QFile::WriteOther
+ || (fileInfo.ownerId() == userId && permissions & QFile::WriteOwner)
+ || (fileInfo.groupId() == groupId && permissions & QFile::WriteGroup))
+ p |= QFile::WriteUser;
+
+ if ( permissions & QFile::ExeOther
+ || (fileInfo.ownerId() == userId && permissions & QFile::ExeOwner)
+ || (fileInfo.groupId() == groupId && permissions & QFile::ExeGroup))
+ p |= QFile::ExeUser;
+ return p;
+#endif
+}
+
+QExtendedInformation QFileInfoGatherer::getInfo(const QFileInfo &fileInfo) const
+{
+ QExtendedInformation info(fileInfo);
+ info.icon = m_iconProvider->icon(fileInfo);
+ info.setPermissions(translatePermissions(fileInfo));
+ info.displayType = m_iconProvider->type(fileInfo);
+#ifndef QT_NO_FILESYSTEMWATCHER
+ // ### Not ready to listen all modifications
+ #if 0
+ // Enable the next two commented out lines to get updates when the file sizes change...
+ if (!fileInfo.exists() && !fileInfo.isSymLink()) {
+ info.size = -1;
+ //watcher->removePath(fileInfo.absoluteFilePath());
+ } else {
+ if (!fileInfo.absoluteFilePath().isEmpty() && fileInfo.exists() && fileInfo.isReadable()
+ && !watcher->files().contains(fileInfo.absoluteFilePath())) {
+ //watcher->addPath(fileInfo.absoluteFilePath());
+ }
+ }
+ #endif
+#endif
+
+ if (fileInfo.isSymLink() && m_resolveSymlinks) {
+ QFileInfo resolvedInfo(fileInfo.symLinkTarget());
+ resolvedInfo = resolvedInfo.canonicalFilePath();
+ if (resolvedInfo.exists()) {
+ emit nameResolved(fileInfo.filePath(), resolvedInfo.fileName());
+ }
+ }
+ return info;
+}
+
+QString QFileInfoGatherer::translateDriveName(const QFileInfo &drive) const
+{
+ QString driveName = drive.absoluteFilePath();
+#if defined(Q_OS_WIN) && !defined(Q_OS_WINCE)
+ if (driveName.startsWith(QLatin1Char('/'))) // UNC host
+ return drive.fileName();
+ if (driveName.endsWith(QLatin1Char('/')))
+ driveName.chop(1);
+#endif
+ return driveName;
+}
+
+/*
+ Get specific file info's, batch the files so update when we have 100
+ items and every 200ms after that
+ */
+void QFileInfoGatherer::getFileInfos(const QString &path, const QStringList &files)
+{
+#ifndef QT_NO_FILESYSTEMWATCHER
+ //### We test here if the path still exist before adding it in the watcher
+ //### because sometime the file is deleted just before enter here so QStringList files is not up to date
+ //### It is not a proper fix, perhaps in 4.6 we should have a better way to avoid that
+ //### to ensure the gatherer have fresh information
+ QFileInfo info(path);
+ if (files.isEmpty()
+ && !watcher->directories().contains(path)
+ && !path.isEmpty()
+ && info.exists()
+ && !path.startsWith(QLatin1String("//")) /*don't watch UNC path*/) {
+ watcher->addPath(path);
+ }
+#endif
+
+ // List drives
+ if (path.isEmpty()) {
+#if defined Q_AUTOTEST_EXPORT
+ fetchedRoot = true;
+#endif
+ QFileInfoList infoList;
+ if (files.isEmpty()) {
+ infoList = QDir::drives();
+ } else {
+ for (int i = 0; i < files.count(); ++i)
+ infoList << QFileInfo(files.at(i));
+ }
+ for (int i = infoList.count() - 1; i >= 0; --i) {
+ QString driveName = translateDriveName(infoList.at(i));
+ QList<QPair<QString,QFileInfo> > updatedFiles;
+ updatedFiles.append(QPair<QString,QFileInfo>(driveName, infoList.at(i)));
+ emit updates(path, updatedFiles);
+ }
+ return;
+ }
+
+ QTime base = QTime::currentTime();
+ QFileInfo fileInfo;
+ bool firstTime = true;
+ QList<QPair<QString, QFileInfo> > updatedFiles;
+ QStringList filesToCheck = files;
+
+ QString itPath = QDir::fromNativeSeparators(files.isEmpty() ? path : QLatin1String(""));
+ QDirIterator dirIt(itPath, QDir::AllEntries | QDir::System | QDir::Hidden);
+ QStringList allFiles;
+ while(!abort && dirIt.hasNext()) {
+ dirIt.next();
+ fileInfo = dirIt.fileInfo();
+ allFiles.append(fileInfo.fileName());
+ fetch(fileInfo, base, firstTime, updatedFiles, path);
+ }
+ if (!allFiles.isEmpty())
+ emit newListOfFiles(path, allFiles);
+
+ QStringList::const_iterator filesIt = filesToCheck.constBegin();
+ while(!abort && filesIt != filesToCheck.constEnd()) {
+ fileInfo.setFile(path + QDir::separator() + *filesIt);
+ ++filesIt;
+ fetch(fileInfo, base, firstTime, updatedFiles, path);
+ }
+ if (!updatedFiles.isEmpty())
+ emit updates(path, updatedFiles);
+}
+
+void QFileInfoGatherer::fetch(const QFileInfo &fileInfo, QTime &base, bool &firstTime, QList<QPair<QString, QFileInfo> > &updatedFiles, const QString &path) {
+ updatedFiles.append(QPair<QString, QFileInfo>(fileInfo.fileName(), fileInfo));
+ QTime current = QTime::currentTime();
+ if ((firstTime && updatedFiles.count() > 100) || base.msecsTo(current) > 1000) {
+ emit updates(path, updatedFiles);
+ updatedFiles.clear();
+ base = current;
+ firstTime = false;
+ }
+}
+
+#endif // QT_NO_FILESYSTEMMODEL
+
+QT_END_NAMESPACE
diff --git a/src/gui/dialogs/qfileinfogatherer_p.h b/src/gui/dialogs/qfileinfogatherer_p.h
new file mode 100644
index 0000000000..eac0d462e2
--- /dev/null
+++ b/src/gui/dialogs/qfileinfogatherer_p.h
@@ -0,0 +1,210 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QFILEINFOGATHERER_H
+#define QFILEINFOGATHERER_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 <qthread.h>
+#include <qmutex.h>
+#include <qwaitcondition.h>
+#include <qfilesystemwatcher.h>
+#include <qfileiconprovider.h>
+#include <qfsfileengine.h>
+#include <qpair.h>
+#include <qdatetime.h>
+#include <qstack.h>
+#include <qdir.h>
+
+QT_BEGIN_NAMESPACE
+
+class QExtendedInformation {
+public:
+ enum Type { Dir, File, System };
+
+ QExtendedInformation() {}
+ QExtendedInformation(const QFileInfo &info) : mFileInfo(info) {}
+
+ inline bool isDir() { return type() == Dir; }
+ inline bool isFile() { return type() == File; }
+ inline bool isSystem() { return type() == System; }
+
+ bool operator ==(const QExtendedInformation &fileInfo) const {
+ return mFileInfo == fileInfo.mFileInfo
+ && displayType == fileInfo.displayType
+ && permissions() == fileInfo.permissions();
+ }
+
+ bool isCaseSensitive() const {
+ QFSFileEngine fe(mFileInfo.absoluteFilePath());
+ return fe.caseSensitive();
+ }
+ QFile::Permissions permissions() const {
+ return mPermissions;
+ }
+
+ void setPermissions (QFile::Permissions permissions) {
+ mPermissions = permissions;
+ }
+
+ Type type() const {
+ if (mFileInfo.isDir()) {
+ return QExtendedInformation::Dir;
+ }
+ if (mFileInfo.isFile()) {
+ return QExtendedInformation::File;
+ }
+ if (!mFileInfo.exists() && mFileInfo.isSymLink()) {
+ return QExtendedInformation::System;
+ }
+ return QExtendedInformation::System;
+ }
+
+ bool isSymLink() const {
+ return mFileInfo.isSymLink();
+ }
+
+ bool isHidden() const {
+ return mFileInfo.isHidden();
+ }
+
+ QFileInfo fileInfo() const {
+ return mFileInfo;
+ }
+
+ QDateTime lastModified() const {
+ return mFileInfo.lastModified();
+ }
+
+ qint64 size() const {
+ qint64 size = -1;
+ if (type() == QExtendedInformation::Dir)
+ size = 0;
+ if (type() == QExtendedInformation::File)
+ size = mFileInfo.size();
+ if (!mFileInfo.exists() && !mFileInfo.isSymLink())
+ size = -1;
+ return size;
+ }
+
+ QString displayType;
+ QIcon icon;
+
+private :
+ QFileInfo mFileInfo;
+ QFile::Permissions mPermissions;
+};
+
+class QFileIconProvider;
+
+#ifndef QT_NO_FILESYSTEMMODEL
+
+class Q_AUTOTEST_EXPORT QFileInfoGatherer : public QThread
+{
+Q_OBJECT
+
+Q_SIGNALS:
+ void updates(const QString &directory, const QList<QPair<QString, QFileInfo> > &updates);
+ void newListOfFiles(const QString &directory, const QStringList &listOfFiles) const;
+ void nameResolved(const QString &fileName, const QString &resolvedName) const;
+
+public:
+ QFileInfoGatherer(QObject *parent = 0);
+ ~QFileInfoGatherer();
+
+ void clear();
+ QExtendedInformation getInfo(const QFileInfo &info) const;
+
+public Q_SLOTS:
+ void list(const QString &directoryPath);
+ void fetchExtendedInformation(const QString &path, const QStringList &files);
+ void updateFile(const QString &path);
+ void setResolveSymlinks(bool enable);
+ bool resolveSymlinks() const;
+ void setIconProvider(QFileIconProvider *provider);
+ QFileIconProvider *iconProvider() const;
+
+protected:
+ void run();
+ void getFileInfos(const QString &path, const QStringList &files);
+
+private:
+ void fetch(const QFileInfo &info, QTime &base, bool &firstTime, QList<QPair<QString, QFileInfo> > &updatedFiles, const QString &path);
+ QString translateDriveName(const QFileInfo &drive) const;
+ QFile::Permissions translatePermissions(const QFileInfo &fileInfo) const;
+
+ QMutex mutex;
+ QWaitCondition condition;
+ bool abort;
+
+ QStack<QString> path;
+ QStack<QStringList> files;
+
+#ifndef QT_NO_FILESYSTEMWATCHER
+ QFileSystemWatcher *watcher;
+#endif
+ bool m_resolveSymlinks;
+ QFileIconProvider *m_iconProvider;
+ QFileIconProvider defaultProvider;
+#ifndef Q_OS_WIN
+ uint userId;
+ uint groupId;
+#endif
+public :
+ //for testing purpose
+ static bool fetchedRoot;
+};
+#endif // QT_NO_FILESYSTEMMODEL
+
+
+QT_END_NAMESPACE
+#endif // QFILEINFOGATHERER_H
+
diff --git a/src/gui/dialogs/qfilesystemmodel.cpp b/src/gui/dialogs/qfilesystemmodel.cpp
new file mode 100644
index 0000000000..51d33146f1
--- /dev/null
+++ b/src/gui/dialogs/qfilesystemmodel.cpp
@@ -0,0 +1,1911 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qfilesystemmodel_p.h"
+#include "qfilesystemmodel.h"
+#include <qlocale.h>
+#include <qmime.h>
+#include <qurl.h>
+#include <qdebug.h>
+#include <qmessagebox.h>
+#include <qapplication.h>
+
+#ifdef Q_OS_WIN
+#include <windows.h>
+#endif
+
+QT_BEGIN_NAMESPACE
+
+#ifndef QT_NO_FILESYSTEMMODEL
+
+/*!
+ \enum QFileSystemModel::Roles
+ \value FileIconRole
+ \value FilePathRole
+ \value FileNameRole
+ \value FilePermissions
+*/
+
+/*!
+ \class QFileSystemModel
+ \since 4.4
+
+ \brief The QFileSystemModel class provides a data model for the local filesystem.
+
+ \ingroup model-view
+
+ This class provides access to the local filesystem, providing functions
+ for renaming and removing files and directories, and for creating new
+ directories. In the simplest case, it can be used with a suitable display
+ widget as part of a browser or filter.
+
+ QFileSystemModel will not fetch any files or directories until setRootPath
+ is called. This will prevent any unnecessary querying on the file system
+ until that point such as listing the drives on Windows.
+
+ Unlike the QDirModel, QFileSystemModel uses a separate thread to populate
+ itself so it will not cause the main thread to hang as the file system
+ is being queried. Calls to rowCount() will return 0 until the model
+ populates a directory.
+
+ QFileSystemModel keeps a cache with file information. The cache is
+ automatically kept up to date using the QFileSystemWatcher.
+
+ QFileSystemModel can be accessed using the standard interface provided by
+ QAbstractItemModel, but it also provides some convenience functions that are
+ specific to a directory model.
+ The fileInfo(), isDir(), name(), and path() functions provide information
+ about the underlying files and directories related to items in the model.
+ Directories can be created and removed using mkdir(), rmdir().
+
+ \note QFileSystemModel requires an instance of a GUI application.
+
+ \sa {Model Classes}
+*/
+
+/*!
+ \fn bool QFileSystemModel::rmdir(const QModelIndex &index) const
+
+ Removes the directory corresponding to the model item \a index in the
+ file system model and \bold{deletes the corresponding directory from the
+ file system}, returning true if successful. If the directory cannot be
+ removed, false is returned.
+
+ \warning This function deletes directories from the file system; it does
+ \bold{not} move them to a location where they can be recovered.
+
+ \sa remove()
+*/
+
+/*!
+ \fn QIcon QFileSystemModel::fileName(const QModelIndex &index) const
+
+ Returns the file name for the item stored in the model under the given
+ \a index.
+*/
+
+/*!
+ \fn QIcon QFileSystemModel::fileIcon(const QModelIndex &index) const
+
+ Returns the icon for the item stored in the model under the given
+ \a index.
+*/
+
+/*!
+ \fn QFileInfo QFileSystemModel::fileInfo(const QModelIndex &index) const
+
+ Returns the QFileInfo for the item stored in the model under the given
+ \a index.
+*/
+
+/*!
+ \fn void QFileSystemModel::rootPathChanged(const QString &newPath);
+
+ This signal is emitted whenever the root path has been changed to a \a newPath.
+*/
+
+/*!
+ \fn void QFileSystemModel::fileRenamed(const QString &path, const QString &oldName, const QString &newName)
+
+ This signal is emitted whenever a file with the \a oldName is successfully
+ renamed to \a newName. The file is located in in the directory \a path.
+*/
+
+/*!
+ \fn bool QFileSystemModel::remove(const QModelIndex &index) const
+
+ Removes the model item \a index from the file system model and \bold{deletes the
+ corresponding file from the file system}, returning true if successful. If the
+ item cannot be removed, false is returned.
+
+ \warning This function deletes files from the file system; it does \bold{not}
+ move them to a location where they can be recovered.
+
+ \sa rmdir()
+*/
+
+bool QFileSystemModel::remove(const QModelIndex &aindex) const
+{
+ //### TODO optim
+ QString path = filePath(aindex);
+ QDirIterator it(path,
+ QDir::AllDirs | QDir:: Files | QDir::NoDotAndDotDot,
+ QDirIterator::Subdirectories);
+ QStringList children;
+ while (it.hasNext())
+ children.prepend(it.next());
+ children.append(path);
+
+ bool error = false;
+ for (int i = 0; i < children.count(); ++i) {
+ QFileInfo info(children.at(i));
+ QModelIndex modelIndex = index(children.at(i));
+ if (info.isDir()) {
+ QDir dir;
+ if (children.at(i) != path)
+ error |= remove(modelIndex);
+ error |= rmdir(modelIndex);
+ } else {
+ error |= QFile::remove(filePath(modelIndex));
+ }
+ }
+ return error;
+}
+
+/*!
+ Constructs a file system model with the given \a parent.
+*/
+QFileSystemModel::QFileSystemModel(QObject *parent)
+ : QAbstractItemModel(*new QFileSystemModelPrivate, parent)
+{
+ Q_D(QFileSystemModel);
+ d->init();
+}
+
+/*!
+ \internal
+*/
+QFileSystemModel::QFileSystemModel(QFileSystemModelPrivate &dd, QObject *parent)
+ : QAbstractItemModel(dd, parent)
+{
+ Q_D(QFileSystemModel);
+ d->init();
+}
+
+/*!
+ Destroys this file system model.
+*/
+QFileSystemModel::~QFileSystemModel()
+{
+}
+
+/*!
+ \reimp
+*/
+QModelIndex QFileSystemModel::index(int row, int column, const QModelIndex &parent) const
+{
+ Q_D(const QFileSystemModel);
+ if (row < 0 || column < 0 || row >= rowCount(parent) || column >= columnCount(parent))
+ return QModelIndex();
+
+ // get the parent node
+ QFileSystemModelPrivate::QFileSystemNode *parentNode = (d->indexValid(parent) ? d->node(parent) :
+ const_cast<QFileSystemModelPrivate::QFileSystemNode*>(&d->root));
+ Q_ASSERT(parentNode);
+
+ // now get the internal pointer for the index
+ QString childName = parentNode->visibleChildren[d->translateVisibleLocation(parentNode, row)];
+ const QFileSystemModelPrivate::QFileSystemNode *indexNode = parentNode->children.value(childName);
+ Q_ASSERT(indexNode);
+
+ return createIndex(row, column, const_cast<QFileSystemModelPrivate::QFileSystemNode*>(indexNode));
+}
+
+/*!
+ \overload
+
+ Returns the model item index for the given \a path and \a column.
+*/
+QModelIndex QFileSystemModel::index(const QString &path, int column) const
+{
+ Q_D(const QFileSystemModel);
+ QFileSystemModelPrivate::QFileSystemNode *node = d->node(path, false);
+ QModelIndex idx = d->index(node);
+ if (idx.column() != column)
+ idx = idx.sibling(idx.row(), column);
+ return idx;
+}
+
+/*!
+ \internal
+
+ Return the QFileSystemNode that goes to index.
+ */
+QFileSystemModelPrivate::QFileSystemNode *QFileSystemModelPrivate::node(const QModelIndex &index) const
+{
+ if (!index.isValid())
+ return const_cast<QFileSystemNode*>(&root);
+ QFileSystemModelPrivate::QFileSystemNode *indexNode = static_cast<QFileSystemModelPrivate::QFileSystemNode*>(index.internalPointer());
+ Q_ASSERT(indexNode);
+ return indexNode;
+}
+
+#ifdef Q_OS_WIN
+static QString qt_GetLongPathName(const QString &strShortPath)
+{
+ QString longPath;
+ int i = 0;
+ if (strShortPath == QLatin1String(".")
+ || (strShortPath.startsWith(QLatin1String("//")))
+ || (strShortPath.startsWith(QLatin1String("\\\\")))) // unc
+ return strShortPath;
+ QString::const_iterator it = strShortPath.constBegin();
+ QString::const_iterator constEnd = strShortPath.constEnd();
+ do {
+ bool isSep = (*it == QLatin1Char('\\') || *it == QLatin1Char('/'));
+ if (isSep || it == constEnd) {
+ QString section = (it == constEnd ? strShortPath : strShortPath.left(i));
+ // FindFirstFile does not handle volumes ("C:"), so we have to catch that ourselves.
+ if (section.endsWith(QLatin1Char(':'))) {
+ longPath.append(section.toUpper());
+ } else {
+ HANDLE h;
+#ifndef Q_OS_WINCE
+ //We add the extend length prefix to handle long path
+ QString longSection = QLatin1String("\\\\?\\")+QDir::toNativeSeparators(section);
+#else
+ QString longSection = QDir::toNativeSeparators(section);
+#endif
+ QT_WA({
+ WIN32_FIND_DATAW findData;
+ h = ::FindFirstFileW((wchar_t *)longSection.utf16(), &findData);
+ if (h != INVALID_HANDLE_VALUE)
+ longPath.append(QString::fromUtf16((ushort*)findData.cFileName));
+ } , {
+ WIN32_FIND_DATAA findData;
+ h = ::FindFirstFileA(section.toLocal8Bit(), &findData);
+ if (h != INVALID_HANDLE_VALUE)
+ longPath.append(QString::fromLocal8Bit(findData.cFileName));
+ });
+ if (h == INVALID_HANDLE_VALUE) {
+ longPath.append(section);
+ break;
+ } else {
+ ::FindClose(h);
+ }
+ }
+ if (it != constEnd)
+ longPath.append(*it);
+ else
+ break;
+ }
+ ++it;
+ if (isSep && it == constEnd) // break out if the last character is a separator
+ break;
+ ++i;
+ } while (true);
+ return longPath;
+}
+#endif
+
+/*!
+ \internal
+
+ Given a path return the matching QFileSystemNode or &root if invalid
+*/
+QFileSystemModelPrivate::QFileSystemNode *QFileSystemModelPrivate::node(const QString &path, bool fetch) const
+{
+ Q_Q(const QFileSystemModel);
+ Q_UNUSED(q);
+ if (path.isEmpty() || path == myComputer() || path.startsWith(QLatin1String(":")))
+ return const_cast<QFileSystemModelPrivate::QFileSystemNode*>(&root);
+
+ // Construct the nodes up to the new root path if they need to be built
+ QString absolutePath;
+#ifdef Q_OS_WIN
+ QString longPath = qt_GetLongPathName(path);
+#else
+ QString longPath = path;
+#endif
+ if (longPath == rootDir.path())
+ absolutePath = rootDir.absolutePath();
+ else
+ absolutePath = QDir(longPath).absolutePath();
+
+ // ### TODO can we use bool QAbstractFileEngine::caseSensitive() const?
+ QStringList pathElements = absolutePath.split(QLatin1Char('/'), QString::SkipEmptyParts);
+ if ((pathElements.isEmpty())
+#if !defined(Q_OS_WIN) || defined(Q_OS_WINCE)
+ && QDir::fromNativeSeparators(longPath) != QLatin1String("/")
+#endif
+ )
+ return const_cast<QFileSystemModelPrivate::QFileSystemNode*>(&root);
+ QModelIndex index = QModelIndex(); // start with "My Computer"
+#if defined(Q_OS_WIN) && !defined(Q_OS_WINCE)
+ if (absolutePath.startsWith(QLatin1String("//"))) { // UNC path
+ QString host = QLatin1String("\\\\") + pathElements.first();
+ if (absolutePath == QDir::fromNativeSeparators(host))
+ absolutePath.append(QLatin1Char('/'));
+ if (longPath.endsWith(QLatin1Char('/')) && !absolutePath.endsWith(QLatin1Char('/')))
+ absolutePath.append(QLatin1Char('/'));
+ int r = 0;
+ QFileSystemModelPrivate::QFileSystemNode *rootNode = const_cast<QFileSystemModelPrivate::QFileSystemNode*>(&root);
+ if (!root.children.contains(host.toLower())) {
+ if (pathElements.count() == 1 && !absolutePath.endsWith(QLatin1Char('/')))
+ return rootNode;
+ QFileInfo info(host);
+ if (!info.exists())
+ return rootNode;
+ QFileSystemModelPrivate *p = const_cast<QFileSystemModelPrivate*>(this);
+ p->addNode(rootNode, host);
+ p->addVisibleFiles(rootNode, QStringList(host));
+ }
+ r = rootNode->visibleLocation(host);
+ r = translateVisibleLocation(rootNode, r);
+ index = q->index(r, 0, QModelIndex());
+ pathElements.pop_front();
+ } else {
+ if (!pathElements.at(0).contains(QLatin1String(":")))
+ pathElements.prepend(QDir(longPath).rootPath());
+ if (pathElements.at(0).endsWith(QLatin1Char('/')))
+ pathElements[0].chop(1);
+ }
+#else
+ // add the "/" item, since it is a valid path element on Unix
+ if (absolutePath[0] == QLatin1Char('/'))
+ pathElements.prepend(QLatin1String("/"));
+#endif
+
+ QFileSystemModelPrivate::QFileSystemNode *parent = node(index);
+ for (int i = 0; i < pathElements.count(); ++i) {
+ QString element = pathElements.at(i);
+#ifdef Q_OS_WIN
+ // On Windows, "filename......." and "filename" are equivalent Task #133928
+ while (element.endsWith(QLatin1Char('.')))
+ element.chop(1);
+#endif
+ bool alreadyExisted = parent->children.contains(element);
+
+ // we couldn't find the path element, we create a new node since we
+ // _know_ that the path is valid
+ if (alreadyExisted) {
+ if ((parent->children.count() == 0)
+ || (parent->caseSensitive()
+ && parent->children.value(element)->fileName != element)
+ || (!parent->caseSensitive()
+ && parent->children.value(element)->fileName.toLower() != element.toLower()))
+ alreadyExisted = false;
+ }
+
+ QFileSystemModelPrivate::QFileSystemNode *node;
+ if (!alreadyExisted) {
+ // Someone might call ::index("file://cookie/monster/doesn't/like/veggies"),
+ // a path that doesn't exists, I.E. don't blindly create directories.
+ QFileInfo info(absolutePath);
+ if (!info.exists())
+ return const_cast<QFileSystemModelPrivate::QFileSystemNode*>(&root);
+ QFileSystemModelPrivate *p = const_cast<QFileSystemModelPrivate*>(this);
+ node = p->addNode(parent, element);
+#ifndef QT_NO_FILESYSTEMWATCHER
+ node->populate(fileInfoGatherer.getInfo(info));
+#endif
+ } else {
+ node = parent->children.value(element);
+ }
+
+ Q_ASSERT(node);
+ if (!node->isVisible) {
+ // It has been filtered out
+ if (alreadyExisted && node->hasInformation() && !fetch)
+ return const_cast<QFileSystemModelPrivate::QFileSystemNode*>(&root);
+
+ QFileSystemModelPrivate *p = const_cast<QFileSystemModelPrivate*>(this);
+ p->addVisibleFiles(parent, QStringList(element));
+ if (!p->bypassFilters.contains(node))
+ p->bypassFilters[node] = 1;
+ QString dir = q->filePath(this->index(parent));
+ if (!node->hasInformation() && fetch) {
+ Fetching f;
+ f.dir = dir;
+ f.file = element;
+ f.node = node;
+ p->toFetch.append(f);
+ p->fetchingTimer.start(0, const_cast<QFileSystemModel*>(q));
+ }
+ }
+ parent = node;
+ }
+
+ return parent;
+}
+
+/*!
+ \reimp
+*/
+void QFileSystemModel::timerEvent(QTimerEvent *event)
+{
+ Q_D(QFileSystemModel);
+ if (event->timerId() == d->fetchingTimer.timerId()) {
+ d->fetchingTimer.stop();
+#ifndef QT_NO_FILESYSTEMWATCHER
+ for (int i = 0; i < d->toFetch.count(); ++i) {
+ const QFileSystemModelPrivate::QFileSystemNode *node = d->toFetch.at(i).node;
+ if (!node->hasInformation()) {
+ d->fileInfoGatherer.fetchExtendedInformation(d->toFetch.at(i).dir,
+ QStringList(d->toFetch.at(i).file));
+ } else {
+ // qDebug() << "yah!, you saved a little gerbil soul";
+ }
+ }
+#endif
+ d->toFetch.clear();
+ }
+}
+
+/*!
+ Returns true if the model item \a index represents a directory;
+ otherwise returns false.
+*/
+bool QFileSystemModel::isDir(const QModelIndex &index) const
+{
+ // This function is for public usage only because it could create a file info
+ Q_D(const QFileSystemModel);
+ if (!index.isValid())
+ return true;
+ QFileSystemModelPrivate::QFileSystemNode *n = d->node(index);
+ if (n->hasInformation())
+ return n->isDir();
+ return fileInfo(index).isDir();
+}
+
+/*!
+ Returns the size in bytes of \a index. If the file does not exist, 0 is returned.
+ */
+qint64 QFileSystemModel::size(const QModelIndex &index) const
+{
+ Q_D(const QFileSystemModel);
+ if (!index.isValid())
+ return 0;
+ return d->node(index)->size();
+}
+
+/*!
+ Returns the type of file \a index such as "Directory" or "JPEG file".
+ */
+QString QFileSystemModel::type(const QModelIndex &index) const
+{
+ Q_D(const QFileSystemModel);
+ if (!index.isValid())
+ return QString();
+ return d->node(index)->type();
+}
+
+/*!
+ Returns the date and time when \a index was last modified.
+ */
+QDateTime QFileSystemModel::lastModified(const QModelIndex &index) const
+{
+ Q_D(const QFileSystemModel);
+ if (!index.isValid())
+ return QDateTime();
+ return d->node(index)->lastModified();
+}
+
+/*!
+ \reimp
+*/
+QModelIndex QFileSystemModel::parent(const QModelIndex &index) const
+{
+ Q_D(const QFileSystemModel);
+ if (!d->indexValid(index))
+ return QModelIndex();
+
+ QFileSystemModelPrivate::QFileSystemNode *indexNode = d->node(index);
+ Q_ASSERT(indexNode != 0);
+ QFileSystemModelPrivate::QFileSystemNode *parentNode = (indexNode ? indexNode->parent : 0);
+ if (parentNode == 0 || parentNode == &d->root)
+ return QModelIndex();
+
+ // get the parent's row
+ QFileSystemModelPrivate::QFileSystemNode *grandParentNode = parentNode->parent;
+ Q_ASSERT(grandParentNode->children.contains(parentNode->fileName));
+ int visualRow = d->translateVisibleLocation(grandParentNode, grandParentNode->visibleLocation(grandParentNode->children.value(parentNode->fileName)->fileName));
+ if (visualRow == -1)
+ return QModelIndex();
+ return createIndex(visualRow, 0, parentNode);
+}
+
+/*
+ \internal
+
+ return the index for node
+*/
+QModelIndex QFileSystemModelPrivate::index(const QFileSystemModelPrivate::QFileSystemNode *node) const
+{
+ Q_Q(const QFileSystemModel);
+ QFileSystemModelPrivate::QFileSystemNode *parentNode = (node ? node->parent : 0);
+ if (node == &root || !parentNode)
+ return QModelIndex();
+
+ // get the parent's row
+ Q_ASSERT(node);
+ if (!node->isVisible)
+ return QModelIndex();
+
+ int visualRow = translateVisibleLocation(parentNode, parentNode->visibleLocation(node->fileName));
+ return q->createIndex(visualRow, 0, const_cast<QFileSystemNode*>(node));
+}
+
+/*!
+ \reimp
+*/
+bool QFileSystemModel::hasChildren(const QModelIndex &parent) const
+{
+ Q_D(const QFileSystemModel);
+ if (parent.column() > 0)
+ return false;
+
+ if (!parent.isValid()) // drives
+ return true;
+
+ const QFileSystemModelPrivate::QFileSystemNode *indexNode = d->node(parent);
+ Q_ASSERT(indexNode);
+ return (indexNode->isDir());
+}
+
+/*!
+ \reimp
+ */
+bool QFileSystemModel::canFetchMore(const QModelIndex &parent) const
+{
+ Q_D(const QFileSystemModel);
+ const QFileSystemModelPrivate::QFileSystemNode *indexNode = d->node(parent);
+ return (!indexNode->populatedChildren);
+}
+
+/*!
+ \reimp
+ */
+void QFileSystemModel::fetchMore(const QModelIndex &parent)
+{
+ Q_D(QFileSystemModel);
+ if (!d->setRootPath)
+ return;
+ QFileSystemModelPrivate::QFileSystemNode *indexNode = d->node(parent);
+ if (indexNode->populatedChildren)
+ return;
+ indexNode->populatedChildren = true;
+ d->fileInfoGatherer.list(filePath(parent));
+}
+
+/*!
+ \reimp
+*/
+int QFileSystemModel::rowCount(const QModelIndex &parent) const
+{
+ Q_D(const QFileSystemModel);
+ if (parent.column() > 0)
+ return 0;
+
+ if (!parent.isValid())
+ return d->root.visibleChildren.count();
+
+ const QFileSystemModelPrivate::QFileSystemNode *parentNode = d->node(parent);
+ return parentNode->visibleChildren.count();
+}
+
+/*!
+ \reimp
+*/
+int QFileSystemModel::columnCount(const QModelIndex &parent) const
+{
+ return (parent.column() > 0) ? 0 : 4;
+}
+
+/*!
+ Returns the data stored under the given \a role for the item "My Computer".
+
+ \sa Qt::ItemDataRole
+ */
+QVariant QFileSystemModel::myComputer(int role) const
+{
+ Q_D(const QFileSystemModel);
+ switch (role) {
+ case Qt::DisplayRole:
+ return d->myComputer();
+ case Qt::DecorationRole:
+ return d->fileInfoGatherer.iconProvider()->icon(QFileIconProvider::Computer);
+ }
+ return QVariant();
+}
+
+/*!
+ \reimp
+*/
+QVariant QFileSystemModel::data(const QModelIndex &index, int role) const
+{
+ Q_D(const QFileSystemModel);
+ if (!index.isValid() || index.model() != this)
+ return QVariant();
+
+ switch (role) {
+ case Qt::EditRole:
+ case Qt::DisplayRole:
+ switch (index.column()) {
+ case 0: return d->name(index);
+ case 1: return d->size(index);
+ case 2: return d->type(index);
+ case 3: return d->time(index);
+ default:
+ qWarning("data: invalid display value column %d", index.column());
+ break;
+ }
+ break;
+ case FilePathRole:
+ return filePath(index);
+ case FileNameRole:
+ return d->name(index);
+ case Qt::DecorationRole:
+ if (index.column() == 0) {
+ QIcon icon = d->icon(index);
+ if (icon.isNull()) {
+ if (d->node(index)->isDir())
+ icon = d->fileInfoGatherer.iconProvider()->icon(QFileIconProvider::Folder);
+ else
+ icon = d->fileInfoGatherer.iconProvider()->icon(QFileIconProvider::File);
+ }
+ return icon;
+ }
+ break;
+ case Qt::TextAlignmentRole:
+ if (index.column() == 1)
+ return Qt::AlignRight;
+ break;
+ case FilePermissions:
+ int p = permissions(index);
+ return p;
+ }
+
+ return QVariant();
+}
+
+/*!
+ \internal
+*/
+QString QFileSystemModelPrivate::size(const QModelIndex &index) const
+{
+ if (!index.isValid())
+ return QString();
+ const QFileSystemNode *n = node(index);
+ if (n->isDir()) {
+#ifdef Q_OS_MAC
+ return QLatin1String("--");
+#else
+ return QLatin1String("");
+#endif
+ // Windows - ""
+ // OS X - "--"
+ // Konqueror - "4 KB"
+ // Nautilus - "9 items" (the number of children)
+ }
+ return size(n->size());
+}
+
+QString QFileSystemModelPrivate::size(qint64 bytes)
+{
+ // According to the Si standard KB is 1000 bytes, KiB is 1024
+ // but on windows sizes are calculated by dividing by 1024 so we do what they do.
+ const qint64 kb = 1024;
+ const qint64 mb = 1024 * kb;
+ const qint64 gb = 1024 * mb;
+ const qint64 tb = 1024 * gb;
+ if (bytes >= tb)
+ return QFileSystemModel::tr("%1 TB").arg(QLocale().toString(qreal(bytes) / tb, 'f', 3));
+ if (bytes >= gb)
+ return QFileSystemModel::tr("%1 GB").arg(QLocale().toString(qreal(bytes) / gb, 'f', 2));
+ if (bytes >= mb)
+ return QFileSystemModel::tr("%1 MB").arg(QLocale().toString(qreal(bytes) / mb, 'f', 1));
+ if (bytes >= kb)
+ return QFileSystemModel::tr("%1 KB").arg(QLocale().toString(bytes / kb));
+ return QFileSystemModel::tr("%1 bytes").arg(QLocale().toString(bytes));
+}
+
+/*!
+ \internal
+*/
+QString QFileSystemModelPrivate::time(const QModelIndex &index) const
+{
+ if (!index.isValid())
+ return QString();
+#ifndef QT_NO_DATESTRING
+ return node(index)->lastModified().toString(Qt::SystemLocaleDate);
+#else
+ Q_UNUSED(index);
+ return QString();
+#endif
+}
+
+/*
+ \internal
+*/
+QString QFileSystemModelPrivate::type(const QModelIndex &index) const
+{
+ if (!index.isValid())
+ return QString();
+ return node(index)->type();
+}
+
+/*!
+ \internal
+*/
+QString QFileSystemModelPrivate::name(const QModelIndex &index) const
+{
+ if (!index.isValid())
+ return QString();
+ QFileSystemNode *dirNode = node(index);
+ if (dirNode->isSymLink() && fileInfoGatherer.resolveSymlinks()) {
+ QString fullPath = QDir::fromNativeSeparators(filePath(index));
+ if (resolvedSymLinks.contains(fullPath))
+ return resolvedSymLinks[fullPath];
+ }
+ // ### TODO it would be nice to grab the volume name if dirNode->parent == root
+ return dirNode->fileName;
+}
+
+/*!
+ \internal
+*/
+QIcon QFileSystemModelPrivate::icon(const QModelIndex &index) const
+{
+ if (!index.isValid())
+ return QIcon();
+ return node(index)->icon();
+}
+
+/*!
+ \reimp
+*/
+bool QFileSystemModel::setData(const QModelIndex &idx, const QVariant &value, int role)
+{
+ Q_D(QFileSystemModel);
+ if (!idx.isValid()
+ || idx.column() != 0
+ || role != Qt::EditRole
+ || (flags(idx) & Qt::ItemIsEditable) == 0) {
+ return false;
+ }
+
+ QString newName = value.toString();
+ QString oldName = idx.data().toString();
+ if (newName == idx.data().toString())
+ return true;
+
+ if (newName.isEmpty()
+ || newName.contains(QDir::separator())
+ || !QDir(filePath(parent(idx))).rename(oldName, newName)) {
+#ifndef QT_NO_MESSAGEBOX
+ QMessageBox::information(0, QFileSystemModel::tr("Invalid filename"),
+ QFileSystemModel::tr("<b>The name \"%1\" can not be used.</b><p>Try using another name, with fewer characters or no punctuations marks.")
+ .arg(newName),
+ QMessageBox::Ok);
+#endif // QT_NO_MESSAGEBOX
+ return false;
+ } else {
+ /*
+ *After re-naming something we don't want the selection to change*
+ - can't remove rows and later insert
+ - can't quickly remove and insert
+ - index pointer can't change because treeview doesn't use persistant index's
+
+ - if this get any more complicated think of changing it to just
+ use layoutChanged
+ */
+
+ QFileSystemModelPrivate::QFileSystemNode *indexNode = d->node(idx);
+ QFileSystemModelPrivate::QFileSystemNode *parentNode = indexNode->parent;
+ int visibleLocation = parentNode->visibleLocation(parentNode->children.value(indexNode->fileName)->fileName);
+
+ d->addNode(parentNode, newName);
+ parentNode->visibleChildren.removeAt(visibleLocation);
+ QFileSystemModelPrivate::QFileSystemNode * oldValue = parentNode->children.value(oldName);
+ parentNode->children[newName] = oldValue;
+ QFileInfo info(d->rootDir, newName);
+ oldValue->fileName = newName;
+ oldValue->parent = parentNode;
+ oldValue->populate(d->fileInfoGatherer.getInfo(info));
+ oldValue->isVisible = true;
+
+ parentNode->children.remove(oldName);
+ parentNode->visibleChildren.insert(visibleLocation, newName);
+
+ d->delayedSort();
+ emit fileRenamed(filePath(idx.parent()), oldName, newName);
+ }
+ return true;
+}
+
+/*!
+ \reimp
+*/
+QVariant QFileSystemModel::headerData(int section, Qt::Orientation orientation, int role) const
+{
+ switch (role) {
+ case Qt::DecorationRole:
+ if (section == 0) {
+ // ### TODO oh man this is ugly and doesn't even work all the way!
+ // it is still 2 pixels off
+ QImage pixmap(16, 1, QImage::Format_Mono);
+ pixmap.fill(0);
+ pixmap.setAlphaChannel(pixmap.createAlphaMask());
+ return pixmap;
+ }
+ case Qt::TextAlignmentRole:
+ return Qt::AlignLeft;
+ }
+
+ if (orientation != Qt::Horizontal || role != Qt::DisplayRole)
+ return QAbstractItemModel::headerData(section, orientation, role);
+
+ QString returnValue;
+ switch (section) {
+ case 0: returnValue = tr("Name");
+ break;
+ case 1: returnValue = tr("Size");
+ break;
+ case 2: returnValue =
+#ifdef Q_OS_MAC
+ tr("Kind", "Match OS X Finder");
+#else
+ tr("Type", "All other platforms");
+#endif
+ break;
+ // Windows - Type
+ // OS X - Kind
+ // Konqueror - File Type
+ // Nautilus - Type
+ case 3: returnValue = tr("Date Modified");
+ break;
+ default: return QVariant();
+ }
+ return returnValue;
+}
+
+/*!
+ \reimp
+*/
+Qt::ItemFlags QFileSystemModel::flags(const QModelIndex &index) const
+{
+ Q_D(const QFileSystemModel);
+ Qt::ItemFlags flags = QAbstractItemModel::flags(index);
+ if (!index.isValid())
+ return flags;
+
+ QFileSystemModelPrivate::QFileSystemNode *indexNode = d->node(index);
+ if (d->nameFilterDisables && !d->passNameFilters(indexNode)) {
+ flags &= ~Qt::ItemIsEnabled;
+ // ### TODO you shouldn't be able to set this as the current item, task 119433
+ return flags;
+ }
+
+ flags |= Qt::ItemIsDragEnabled;
+ if (d->readOnly)
+ return flags;
+ if ((index.column() == 0) && indexNode->permissions() & QFile::WriteUser) {
+ flags |= Qt::ItemIsEditable;
+ if (indexNode->isDir())
+ flags |= Qt::ItemIsDropEnabled;
+ }
+ return flags;
+}
+
+/*!
+ \internal
+*/
+void QFileSystemModelPrivate::_q_performDelayedSort()
+{
+ Q_Q(QFileSystemModel);
+ q->sort(sortColumn, sortOrder);
+}
+
+static inline QChar getNextChar(const QString &s, int location)
+{
+ return (location < s.length()) ? s.at(location) : QChar();
+}
+
+/*!
+ Natural number sort, skips spaces.
+
+ Examples:
+ 1, 2, 10, 55, 100
+ 01.jpg, 2.jpg, 10.jpg
+
+ Note on the algorithm:
+ Only as many characters as necessary are looked at and at most they all
+ are looked at once.
+
+ Slower then QString::compare() (of course)
+ */
+int QFileSystemModelPrivate::naturalCompare(const QString &s1, const QString &s2, Qt::CaseSensitivity cs)
+{
+ for (int l1 = 0, l2 = 0; l1 <= s1.count() && l2 <= s2.count(); ++l1, ++l2) {
+ // skip spaces, tabs and 0's
+ QChar c1 = getNextChar(s1, l1);
+ while (c1.isSpace())
+ c1 = getNextChar(s1, ++l1);
+ QChar c2 = getNextChar(s2, l2);
+ while (c2.isSpace())
+ c2 = getNextChar(s2, ++l2);
+
+ if (c1.isDigit() && c2.isDigit()) {
+ while (c1.digitValue() == 0)
+ c1 = getNextChar(s1, ++l1);
+ while (c2.digitValue() == 0)
+ c2 = getNextChar(s2, ++l2);
+
+ int lookAheadLocation1 = l1;
+ int lookAheadLocation2 = l2;
+ int currentReturnValue = 0;
+ // find the last digit, setting currentReturnValue as we go if it isn't equal
+ for (
+ QChar lookAhead1 = c1, lookAhead2 = c2;
+ (lookAheadLocation1 <= s1.length() && lookAheadLocation2 <= s2.length());
+ lookAhead1 = getNextChar(s1, ++lookAheadLocation1),
+ lookAhead2 = getNextChar(s2, ++lookAheadLocation2)
+ ) {
+ bool is1ADigit = !lookAhead1.isNull() && lookAhead1.isDigit();
+ bool is2ADigit = !lookAhead2.isNull() && lookAhead2.isDigit();
+ if (!is1ADigit && !is2ADigit)
+ break;
+ if (!is1ADigit)
+ return -1;
+ if (!is2ADigit)
+ return 1;
+ if (currentReturnValue == 0) {
+ if (lookAhead1 < lookAhead2) {
+ currentReturnValue = -1;
+ } else if (lookAhead1 > lookAhead2) {
+ currentReturnValue = 1;
+ }
+ }
+ }
+ if (currentReturnValue != 0)
+ return currentReturnValue;
+ }
+
+ if (cs == Qt::CaseInsensitive) {
+ if (!c1.isLower()) c1 = c1.toLower();
+ if (!c2.isLower()) c2 = c2.toLower();
+ }
+ int r = QString::localeAwareCompare(c1, c2);
+ if (r < 0)
+ return -1;
+ if (r > 0)
+ return 1;
+ }
+ // The two strings are the same (02 == 2) so fall back to the normal sort
+ return QString::compare(s1, s2, cs);
+}
+
+/*
+ \internal
+ Helper functor used by sort()
+*/
+class QFileSystemModelSorter
+{
+public:
+ inline QFileSystemModelSorter(int column) : sortColumn(column) {}
+
+ bool compareNodes(const QFileSystemModelPrivate::QFileSystemNode *l,
+ const QFileSystemModelPrivate::QFileSystemNode *r) const
+ {
+ switch (sortColumn) {
+ case 0: {
+#ifndef Q_OS_MAC
+ // place directories before files
+ bool left = l->isDir();
+ bool right = r->isDir();
+ if (left ^ right)
+ return left;
+#endif
+ return QFileSystemModelPrivate::naturalCompare(l->fileName,
+ r->fileName, Qt::CaseInsensitive) < 0;
+ }
+ case 1:
+ // Directories go first
+ if (l->isDir() && !r->isDir())
+ return true;
+ return l->size() < r->size();
+ case 2:
+ return l->type() < r->type();
+ case 3:
+ return l->lastModified() < r->lastModified();
+ }
+ Q_ASSERT(false);
+ return false;
+ }
+
+ bool operator()(const QPair<QFileSystemModelPrivate::QFileSystemNode*, int> &l,
+ const QPair<QFileSystemModelPrivate::QFileSystemNode*, int> &r) const
+ {
+ return compareNodes(l.first, r.first);
+ }
+
+
+private:
+ int sortColumn;
+};
+
+/*
+ \internal
+
+ Sort all of the children of parent
+*/
+void QFileSystemModelPrivate::sortChildren(int column, const QModelIndex &parent)
+{
+ QFileSystemModelPrivate::QFileSystemNode *indexNode = node(parent);
+ if (indexNode->children.count() == 0)
+ return;
+
+ QList<QPair<QFileSystemModelPrivate::QFileSystemNode*, int> > values;
+ QHash<QString, QFileSystemNode *>::const_iterator iterator;
+ int i = 0;
+ for(iterator = indexNode->children.begin() ; iterator != indexNode->children.end() ; ++iterator) {
+ if (filtersAcceptsNode(iterator.value())) {
+ values.append(QPair<QFileSystemModelPrivate::QFileSystemNode*, int>((iterator.value()), i));
+ } else {
+ iterator.value()->isVisible = false;
+ }
+ i++;
+ }
+ QFileSystemModelSorter ms(column);
+ qStableSort(values.begin(), values.end(), ms);
+ // First update the new visible list
+ indexNode->visibleChildren.clear();
+ for (int i = 0; i < values.count(); ++i) {
+ indexNode->visibleChildren.append(values.at(i).first->fileName);
+ values.at(i).first->isVisible = true;
+ }
+}
+
+/*!
+ \reimp
+*/
+void QFileSystemModel::sort(int column, Qt::SortOrder order)
+{
+ Q_D(QFileSystemModel);
+ if (d->sortOrder == order && d->sortColumn == column && !d->forceSort)
+ return;
+
+ emit layoutAboutToBeChanged();
+ QModelIndexList oldList = persistentIndexList();
+ QList<QPair<QFileSystemModelPrivate::QFileSystemNode*, int> > oldNodes;
+ for (int i = 0; i < oldList.count(); ++i) {
+ QPair<QFileSystemModelPrivate::QFileSystemNode*, int> pair(d->node(oldList.at(i)), oldList.at(i).column());
+ oldNodes.append(pair);
+ }
+
+ if (!(d->sortColumn == column && d->sortOrder != order && !d->forceSort)) {
+ //we sort only from where we are, don't need to sort all the model
+ d->sortChildren(column, index(rootPath()));
+ d->sortColumn = column;
+ d->forceSort = false;
+ }
+ d->sortOrder = order;
+
+ QModelIndexList newList;
+ for (int i = 0; i < oldNodes.count(); ++i) {
+ QModelIndex idx = d->index(oldNodes.at(i).first);
+ idx = idx.sibling(idx.row(), oldNodes.at(i).second);
+ newList.append(idx);
+ }
+ changePersistentIndexList(oldList, newList);
+ emit layoutChanged();
+}
+
+/*!
+ Returns a list of MIME types that can be used to describe a list of items
+ in the model.
+*/
+QStringList QFileSystemModel::mimeTypes() const
+{
+ return QStringList(QLatin1String("text/uri-list"));
+}
+
+/*!
+ Returns an object that contains a serialized description of the specified
+ \a indexes. The format used to describe the items corresponding to the
+ indexes is obtained from the mimeTypes() function.
+
+ If the list of indexes is empty, 0 is returned rather than a serialized
+ empty list.
+*/
+QMimeData *QFileSystemModel::mimeData(const QModelIndexList &indexes) const
+{
+ QList<QUrl> urls;
+ QList<QModelIndex>::const_iterator it = indexes.begin();
+ for (; it != indexes.end(); ++it)
+ if ((*it).column() == 0)
+ urls << QUrl::fromLocalFile(filePath(*it));
+ QMimeData *data = new QMimeData();
+ data->setUrls(urls);
+ return data;
+}
+
+/*!
+ Handles the \a data supplied by a drag and drop operation that ended with
+ the given \a action over the row in the model specified by the \a row and
+ \a column and by the \a parent index.
+
+ \sa supportedDropActions()
+*/
+bool QFileSystemModel::dropMimeData(const QMimeData *data, Qt::DropAction action,
+ int row, int column, const QModelIndex &parent)
+{
+ Q_UNUSED(row);
+ Q_UNUSED(column);
+ if (!parent.isValid() || isReadOnly())
+ return false;
+
+ bool success = true;
+ QString to = filePath(parent) + QDir::separator();
+
+ QList<QUrl> urls = data->urls();
+ QList<QUrl>::const_iterator it = urls.constBegin();
+
+ switch (action) {
+ case Qt::CopyAction:
+ for (; it != urls.constEnd(); ++it) {
+ QString path = (*it).toLocalFile();
+ success = QFile::copy(path, to + QFileInfo(path).fileName()) && success;
+ }
+ break;
+ case Qt::LinkAction:
+ for (; it != urls.constEnd(); ++it) {
+ QString path = (*it).toLocalFile();
+ success = QFile::link(path, to + QFileInfo(path).fileName()) && success;
+ }
+ break;
+ case Qt::MoveAction:
+ for (; it != urls.constEnd(); ++it) {
+ QString path = (*it).toLocalFile();
+ success = QFile::copy(path, to + QFileInfo(path).fileName())
+ && QFile::remove(path) && success;
+ }
+ break;
+ default:
+ return false;
+ }
+
+ return success;
+}
+
+/*!
+ \reimp
+*/
+Qt::DropActions QFileSystemModel::supportedDropActions() const
+{
+ return Qt::CopyAction | Qt::MoveAction | Qt::LinkAction;
+}
+
+/*!
+ Returns the path of the item stored in the model under the
+ \a index given.
+*/
+QString QFileSystemModel::filePath(const QModelIndex &index) const
+{
+ Q_D(const QFileSystemModel);
+ QString fullPath = d->filePath(index);
+ QFileSystemModelPrivate::QFileSystemNode *dirNode = d->node(index);
+ if (dirNode->isSymLink() && d->fileInfoGatherer.resolveSymlinks()
+ && d->resolvedSymLinks.contains(fullPath)
+ && dirNode->isDir()) {
+ QFileInfo resolvedInfo(fullPath);
+ resolvedInfo = resolvedInfo.canonicalFilePath();
+ if (resolvedInfo.exists())
+ return resolvedInfo.filePath();
+ }
+ return fullPath;
+}
+
+QString QFileSystemModelPrivate::filePath(const QModelIndex &index) const
+{
+ Q_Q(const QFileSystemModel);
+ Q_UNUSED(q);
+ if (!index.isValid())
+ return QString();
+ Q_ASSERT(index.model() == q);
+
+ QStringList path;
+ QModelIndex idx = index;
+ while (idx.isValid()) {
+ QFileSystemModelPrivate::QFileSystemNode *dirNode = node(idx);
+ if (dirNode)
+ path.prepend(dirNode->fileName);
+ idx = idx.parent();
+ }
+ QString fullPath = QDir::fromNativeSeparators(path.join(QDir::separator()));
+#if !defined(Q_OS_WIN) || defined(Q_OS_WINCE)
+ if ((fullPath.length() > 2) && fullPath[0] == QLatin1Char('/') && fullPath[1] == QLatin1Char('/'))
+ fullPath = fullPath.mid(1);
+#endif
+ return fullPath;
+}
+
+/*!
+ Create a directory with the \a name in the \a parent model index.
+*/
+QModelIndex QFileSystemModel::mkdir(const QModelIndex &parent, const QString &name)
+{
+ Q_D(QFileSystemModel);
+ if (!parent.isValid())
+ return parent;
+
+ QDir dir(filePath(parent));
+ if (!dir.mkdir(name))
+ return QModelIndex();
+ QFileSystemModelPrivate::QFileSystemNode *parentNode = d->node(parent);
+ d->addNode(parentNode, name);
+ Q_ASSERT(parentNode->children.contains(name));
+ QFileSystemModelPrivate::QFileSystemNode *node = parentNode->children[name];
+ node->populate(d->fileInfoGatherer.getInfo(QFileInfo(dir.absolutePath() + QDir::separator() + name)));
+ d->addVisibleFiles(parentNode, QStringList(name));
+ return d->index(node);
+}
+
+/*!
+ Returns the complete OR-ed together combination of QFile::Permission for the \a index.
+ */
+QFile::Permissions QFileSystemModel::permissions(const QModelIndex &index) const
+{
+ Q_D(const QFileSystemModel);
+ QFile::Permissions p = d->node(index)->permissions();
+ if (d->readOnly) {
+ p ^= (QFile::WriteOwner | QFile::WriteUser
+ | QFile::WriteGroup | QFile::WriteOther);
+ }
+ return p;
+}
+
+/*!
+ Sets the directory that is being watched by the model to \a newPath by
+ installing a \l{QFileSystemWatcher}{file system watcher} on it. Any
+ changes to files and directories within this directory will be
+ reflected in the model.
+
+ If the path is changed, the rootPathChanged() signal will be emitted.
+
+ \note This function does not change the structure of the model or
+ modify the data available to views. In other words, the "root" of
+ the model is \e not changed to include only files and directories
+ within the directory specified by \a newPath in the file system.
+ */
+QModelIndex QFileSystemModel::setRootPath(const QString &newPath)
+{
+ Q_D(QFileSystemModel);
+#ifdef Q_OS_WIN
+ QString longNewPath = QDir::fromNativeSeparators(qt_GetLongPathName(newPath));
+#else
+ QString longNewPath = newPath;
+#endif
+ QDir newPathDir(longNewPath);
+ //we remove .. and . from the given path if exist
+ if (!newPath.isEmpty()) {
+ longNewPath = QDir::cleanPath(longNewPath);
+ newPathDir.setPath(longNewPath);
+ }
+
+ d->setRootPath = true;
+
+ //user don't ask for the root path ("") but the conversion failed
+ if (!newPath.isEmpty() && longNewPath.isEmpty())
+ return d->index(rootPath());
+
+ if (d->rootDir.path() == longNewPath)
+ return d->index(rootPath());
+
+ bool showDrives = (longNewPath.isEmpty() || longNewPath == d->myComputer());
+ if (!showDrives && !newPathDir.exists())
+ return d->index(rootPath());
+
+ // We have a new valid root path
+ d->rootDir = newPathDir;
+ QModelIndex newRootIndex;
+ if (showDrives) {
+ // otherwise dir will become '.'
+ d->rootDir.setPath(QLatin1String(""));
+ } else {
+ newRootIndex = d->index(newPathDir.path());
+ }
+ fetchMore(newRootIndex);
+ emit rootPathChanged(longNewPath);
+ d->forceSort = true;
+ d->delayedSort();
+ return newRootIndex;
+}
+
+/*!
+ The currently set root path
+
+ \sa rootDirectory()
+*/
+QString QFileSystemModel::rootPath() const
+{
+ Q_D(const QFileSystemModel);
+ return d->rootDir.path();
+}
+
+/*!
+ The currently set directory
+
+ \sa rootPath()
+*/
+QDir QFileSystemModel::rootDirectory() const
+{
+ Q_D(const QFileSystemModel);
+ QDir dir(d->rootDir);
+ dir.setNameFilters(nameFilters());
+ dir.setFilter(filter());
+ return dir;
+}
+
+/*!
+ Sets the \a provider of file icons for the directory model.
+*/
+void QFileSystemModel::setIconProvider(QFileIconProvider *provider)
+{
+ Q_D(QFileSystemModel);
+ d->fileInfoGatherer.setIconProvider(provider);
+ qApp->processEvents();
+ d->root.updateIcon(provider, QString());
+}
+
+/*!
+ Returns the file icon provider for this directory model.
+*/
+QFileIconProvider *QFileSystemModel::iconProvider() const
+{
+ Q_D(const QFileSystemModel);
+ return d->fileInfoGatherer.iconProvider();
+}
+
+/*!
+ Sets the directory model's filter to that specified by \a filters.
+
+ Note that the filter you set should always include the QDir::AllDirs enum value,
+ otherwise QFileSystemModel won't be able to read the directory structure.
+
+ \sa QDir::Filters
+*/
+void QFileSystemModel::setFilter(QDir::Filters filters)
+{
+ Q_D(QFileSystemModel);
+ if (d->filters == filters)
+ return;
+ d->filters = filters;
+ // CaseSensitivity might have changed
+ setNameFilters(nameFilters());
+ d->forceSort = true;
+ d->delayedSort();
+}
+
+/*!
+ Returns the filter specification for the directory model.
+
+ \sa QDir::Filters
+*/
+QDir::Filters QFileSystemModel::filter() const
+{
+ Q_D(const QFileSystemModel);
+ return d->filters;
+}
+
+/*!
+ \property QFileSystemModel::resolveSymlinks
+ \brief Whether the directory model should resolve symbolic links
+
+ This is only relevant on operating systems that support symbolic links.
+
+ By default, this property is false.
+*/
+void QFileSystemModel::setResolveSymlinks(bool enable)
+{
+ Q_D(QFileSystemModel);
+ d->fileInfoGatherer.setResolveSymlinks(enable);
+}
+
+bool QFileSystemModel::resolveSymlinks() const
+{
+ Q_D(const QFileSystemModel);
+ return d->fileInfoGatherer.resolveSymlinks();
+}
+
+/*!
+ \property QFileSystemModel::readOnly
+ \brief Whether the directory model allows writing to the file system
+
+ If this property is set to false, the directory model will allow renaming, copying
+ and deleting of files and directories.
+
+ This property is true by default
+*/
+void QFileSystemModel::setReadOnly(bool enable)
+{
+ Q_D(QFileSystemModel);
+ d->readOnly = enable;
+}
+
+bool QFileSystemModel::isReadOnly() const
+{
+ Q_D(const QFileSystemModel);
+ return d->readOnly;
+}
+
+/*!
+ \property QFileSystemModel::nameFilterDisables
+ \brief Whether files that don't pass the name filter are hidden or disabled
+
+ This property is true by default
+*/
+void QFileSystemModel::setNameFilterDisables(bool enable)
+{
+ Q_D(QFileSystemModel);
+ if (d->nameFilterDisables == enable)
+ return;
+ d->nameFilterDisables = enable;
+ d->forceSort = true;
+ d->delayedSort();
+}
+
+bool QFileSystemModel::nameFilterDisables() const
+{
+ Q_D(const QFileSystemModel);
+ return d->nameFilterDisables;
+}
+
+/*!
+ Sets the name \a filters to apply against the existing files.
+*/
+void QFileSystemModel::setNameFilters(const QStringList &filters)
+{
+ // Prep the regexp's ahead of time
+#ifndef QT_NO_REGEXP
+ Q_D(QFileSystemModel);
+
+ if (!d->bypassFilters.isEmpty()) {
+ // update the bypass filter to only bypass the stuff that must be kept around
+ d->bypassFilters.clear();
+ // We guarantee that rootPath will stick around
+ QPersistentModelIndex root(index(rootPath()));
+ QModelIndexList persistantList = persistentIndexList();
+ for (int i = 0; i < persistantList.count(); ++i) {
+ QFileSystemModelPrivate::QFileSystemNode *node;
+ node = d->node(persistantList.at(i));
+ while (node) {
+ if (d->bypassFilters.contains(node))
+ break;
+ if (node->isDir())
+ d->bypassFilters[node] = true;
+ node = node->parent;
+ }
+ }
+ }
+
+ d->nameFilters.clear();
+ const Qt::CaseSensitivity caseSensitive =
+ (filter() & QDir::CaseSensitive) ? Qt::CaseSensitive : Qt::CaseInsensitive;
+ for (int i = 0; i < filters.size(); ++i) {
+ d->nameFilters << QRegExp(filters.at(i), caseSensitive, QRegExp::Wildcard);
+ }
+ d->forceSort = true;
+ d->delayedSort();
+#endif
+}
+
+/*!
+ Returns a list of filters applied to the names in the model.
+*/
+QStringList QFileSystemModel::nameFilters() const
+{
+ Q_D(const QFileSystemModel);
+ QStringList filters;
+#ifndef QT_NO_REGEXP
+ for (int i = 0; i < d->nameFilters.size(); ++i) {
+ filters << d->nameFilters.at(i).pattern();
+ }
+#endif
+ return filters;
+}
+
+/*!
+ \reimp
+*/
+bool QFileSystemModel::event(QEvent *event)
+{
+ Q_D(QFileSystemModel);
+ if (event->type() == QEvent::LanguageChange) {
+ d->root.retranslateStrings(d->fileInfoGatherer.iconProvider(), QString());
+ return true;
+ }
+ return QAbstractItemModel::event(event);
+}
+
+/*!
+ \internal
+
+ Performed quick listing and see if any files have been added or removed,
+ then fetch more information on visible files.
+ */
+void QFileSystemModelPrivate::_q_directoryChanged(const QString &directory, const QStringList &files)
+{
+ QFileSystemModelPrivate::QFileSystemNode *parentNode = node(directory, false);
+ if (parentNode->children.count() == 0)
+ return;
+ QStringList toRemove;
+ QStringList newFiles = files;
+ qSort(newFiles.begin(), newFiles.end());
+ QHash<QString, QFileSystemNode*>::const_iterator i = parentNode->children.constBegin();
+ while (i != parentNode->children.constEnd()) {
+ QStringList::iterator iterator;
+ iterator = qBinaryFind(newFiles.begin(), newFiles.end(), i.value()->fileName);
+ if (iterator == newFiles.end()) {
+ toRemove.append(i.value()->fileName);
+ }
+ ++i;
+ }
+ for (int i = 0 ; i < toRemove.count() ; ++i )
+ removeNode(parentNode, toRemove[i]);
+}
+
+/*!
+ \internal
+
+ Adds a new file to the children of parentNode
+
+ *WARNING* this will change the count of children
+*/
+QFileSystemModelPrivate::QFileSystemNode* QFileSystemModelPrivate::addNode(QFileSystemNode *parentNode, const QString &fileName)
+{
+ // In the common case, itemLocation == count() so check there first
+ QFileSystemModelPrivate::QFileSystemNode *node = new QFileSystemModelPrivate::QFileSystemNode(fileName, parentNode);
+ parentNode->children.insert(fileName, node);
+ return node;
+}
+
+/*!
+ \internal
+
+ File at parentNode->children(itemLocation) has been removed, remove from the lists
+ and emit signals if necessary
+
+ *WARNING* this will change the count of children and could change visibleChildren
+ */
+void QFileSystemModelPrivate::removeNode(QFileSystemModelPrivate::QFileSystemNode *parentNode, const QString& name)
+{
+ Q_Q(QFileSystemModel);
+ QModelIndex parent = index(parentNode);
+ bool indexHidden = isHiddenByFilter(parentNode, parent);
+
+ int vLocation = parentNode->visibleLocation(name);
+ if (vLocation >= 0 && !indexHidden)
+ q->beginRemoveRows(parent, translateVisibleLocation(parentNode, vLocation),
+ translateVisibleLocation(parentNode, vLocation));
+ QFileSystemNode * node = parentNode->children.take(name);
+ delete node;
+ // cleanup sort files after removing rather then re-sorting which is O(n)
+ if (vLocation >= 0)
+ parentNode->visibleChildren.removeAt(vLocation);
+ if (vLocation >= 0 && !indexHidden)
+ q->endRemoveRows();
+}
+
+/*
+ \internal
+ Helper functor used by addVisibleFiles()
+*/
+class QFileSystemModelVisibleFinder
+{
+public:
+ inline QFileSystemModelVisibleFinder(QFileSystemModelPrivate::QFileSystemNode *node, QFileSystemModelSorter *sorter) : parentNode(node), sorter(sorter) {}
+
+ bool operator()(const QString &, QString r) const
+ {
+ return sorter->compareNodes(parentNode->children.value(name), parentNode->children.value(r));
+ }
+
+ QString name;
+private:
+ QFileSystemModelPrivate::QFileSystemNode *parentNode;
+ QFileSystemModelSorter *sorter;
+};
+
+/*!
+ \internal
+
+ File at parentNode->children(itemLocation) was not visible before, but now should be
+ and emit signals if necessary.
+
+ *WARNING* this will change the visible count
+ */
+void QFileSystemModelPrivate::addVisibleFiles(QFileSystemNode *parentNode, const QStringList &newFiles)
+{
+ Q_Q(QFileSystemModel);
+ QModelIndex parent = index(parentNode);
+ bool indexHidden = isHiddenByFilter(parentNode, parent);
+ if (!indexHidden) {
+ q->beginInsertRows(parent, parentNode->visibleChildren.count() , parentNode->visibleChildren.count() + newFiles.count() - 1);
+ }
+ for (int i = 0; i < newFiles.count(); ++i) {
+ parentNode->visibleChildren.append(newFiles.at(i));
+ parentNode->children[newFiles.at(i)]->isVisible = true;
+ }
+ if (!indexHidden)
+ q->endInsertRows();
+}
+
+/*!
+ \internal
+
+ File was visible before, but now should NOT be
+
+ *WARNING* this will change the visible count
+ */
+void QFileSystemModelPrivate::removeVisibleFile(QFileSystemNode *parentNode, int vLocation)
+{
+ Q_Q(QFileSystemModel);
+ if (vLocation == -1)
+ return;
+ QModelIndex parent = index(parentNode);
+ bool indexHidden = isHiddenByFilter(parentNode, parent);
+ if (!indexHidden)
+ q->beginRemoveRows(parent, translateVisibleLocation(parentNode, vLocation),
+ translateVisibleLocation(parentNode, vLocation));
+ parentNode->children[parentNode->visibleChildren.at(vLocation)]->isVisible = false;
+ parentNode->visibleChildren.removeAt(vLocation);
+ if (!indexHidden)
+ q->endRemoveRows();
+}
+
+/*!
+ \internal
+
+ The thread has received new information about files,
+ update and emit dataChanged if it has actually changed.
+ */
+void QFileSystemModelPrivate::_q_fileSystemChanged(const QString &path, const QList<QPair<QString, QFileInfo> > &updates)
+{
+ Q_Q(QFileSystemModel);
+ QVector<QString> rowsToUpdate;
+ QStringList newFiles;
+ QFileSystemModelPrivate::QFileSystemNode *parentNode = node(path, false);
+ QModelIndex parentIndex = index(parentNode);
+ for (int i = 0; i < updates.count(); ++i) {
+ QString fileName = updates.at(i).first;
+ Q_ASSERT(!fileName.isEmpty());
+ QExtendedInformation info = fileInfoGatherer.getInfo(updates.at(i).second);
+ bool previouslyHere = parentNode->children.contains(fileName);
+ if (!previouslyHere) {
+ addNode(parentNode, fileName);
+ }
+ QFileSystemModelPrivate::QFileSystemNode * node = parentNode->children.value(fileName);
+ bool isCaseSensitive = parentNode->caseSensitive();
+ if (isCaseSensitive) {
+ if (node->fileName != fileName)
+ continue;
+ } else {
+ if (QString::compare(node->fileName,fileName,Qt::CaseInsensitive) != 0)
+ continue;
+ }
+ if (isCaseSensitive) {
+ Q_ASSERT(node->fileName == fileName);
+ } else {
+ node->fileName = fileName;
+ }
+
+ if (info.size() == -1) {
+ removeNode(parentNode, fileName);
+ continue;
+ }
+ if (*node != info ) {
+ node->populate(info);
+ bypassFilters.remove(node);
+ // brand new information.
+ if (filtersAcceptsNode(node)) {
+ if (!node->isVisible) {
+ newFiles.append(fileName);
+ } else {
+ rowsToUpdate.append(fileName);
+ }
+ } else {
+ if (node->isVisible) {
+ int visibleLocation = parentNode->visibleLocation(fileName);
+ removeVisibleFile(parentNode, visibleLocation);
+ } else {
+ // The file is not visible, don't do anything
+ }
+ }
+ }
+ }
+
+ // bundle up all of the changed signals into as few as possible.
+ qSort(rowsToUpdate.begin(), rowsToUpdate.end());
+ QString min;
+ QString max;
+ for (int i = 0; i < rowsToUpdate.count(); ++i) {
+ QString value = rowsToUpdate.at(i);
+ //##TODO is there a way to bundle signals with QString as the content of the list?
+ /*if (min.isEmpty()) {
+ min = value;
+ if (i != rowsToUpdate.count() - 1)
+ continue;
+ }
+ if (i != rowsToUpdate.count() - 1) {
+ if ((value == min + 1 && max.isEmpty()) || value == max + 1) {
+ max = value;
+ continue;
+ }
+ }*/
+ max = value;
+ min = value;
+ int visibleMin = parentNode->visibleLocation(min);
+ int visibleMax = parentNode->visibleLocation(max);
+ if (visibleMin >= 0
+ && visibleMin < parentNode->visibleChildren.count()
+ && parentNode->visibleChildren.at(visibleMin) == min
+ && visibleMax >= 0) {
+ QModelIndex bottom = q->index(translateVisibleLocation(parentNode, visibleMin), 0, parentIndex);
+ QModelIndex top = q->index(translateVisibleLocation(parentNode, visibleMax), 3, parentIndex);
+ emit q->dataChanged(bottom, top);
+ }
+
+ /*min = QString();
+ max = QString();*/
+ }
+
+ if (newFiles.count() > 0) {
+ addVisibleFiles(parentNode, newFiles);
+ }
+
+ if (newFiles.count() > 0 || (sortColumn != 0 && rowsToUpdate.count() > 0)) {
+ forceSort = true;
+ delayedSort();
+ }
+}
+
+/*!
+ \internal
+*/
+void QFileSystemModelPrivate::_q_resolvedName(const QString &fileName, const QString &resolvedName)
+{
+ resolvedSymLinks[fileName] = resolvedName;
+}
+
+/*!
+ \internal
+*/
+void QFileSystemModelPrivate::init()
+{
+ Q_Q(QFileSystemModel);
+ qRegisterMetaType<QList<QPair<QString,QFileInfo> > >("QList<QPair<QString,QFileInfo> >");
+ q->connect(&fileInfoGatherer, SIGNAL(newListOfFiles(const QString &, const QStringList &)),
+ q, SLOT(_q_directoryChanged(const QString &, const QStringList &)));
+ q->connect(&fileInfoGatherer, SIGNAL(updates(const QString &, const QList<QPair<QString, QFileInfo> > &)),
+ q, SLOT(_q_fileSystemChanged(const QString &, const QList<QPair<QString, QFileInfo> > &)));
+ q->connect(&fileInfoGatherer, SIGNAL(nameResolved(const QString &, const QString &)),
+ q, SLOT(_q_resolvedName(const QString &, const QString &)));
+ q->connect(&delayedSortTimer, SIGNAL(timeout()), q, SLOT(_q_performDelayedSort()), Qt::QueuedConnection);
+}
+
+/*!
+ \internal
+
+ Returns false if node doesn't pass the filters otherwise true
+
+ QDir::Modified is not supported
+ QDir::Drives is not supported
+*/
+bool QFileSystemModelPrivate::filtersAcceptsNode(const QFileSystemNode *node) const
+{
+ // always accept drives
+ if (node->parent == &root || bypassFilters.contains(node))
+ return true;
+
+ // If we don't know anything yet don't accept it
+ if (!node->hasInformation())
+ return false;
+
+ const bool filterPermissions = ((filters & QDir::PermissionMask)
+ && (filters & QDir::PermissionMask) != QDir::PermissionMask);
+ const bool hideDirs = !(filters & (QDir::Dirs | QDir::AllDirs));
+ const bool hideFiles = !(filters & QDir::Files);
+ const bool hideReadable = !(!filterPermissions || (filters & QDir::Readable));
+ const bool hideWritable = !(!filterPermissions || (filters & QDir::Writable));
+ const bool hideExecutable = !(!filterPermissions || (filters & QDir::Executable));
+ const bool hideHidden = !(filters & QDir::Hidden);
+ const bool hideSystem = !(filters & QDir::System);
+ const bool hideSymlinks = (filters & QDir::NoSymLinks);
+ const bool hideDotAndDotDot = (filters & QDir::NoDotAndDotDot);
+
+ // Note that we match the behavior of entryList and not QFileInfo on this and this
+ // incompatibility won't be fixed until Qt 5 at least
+ bool isDotOrDot = ( (node->fileName == QLatin1String(".")
+ || node->fileName == QLatin1String("..")));
+ if ( (hideHidden && (!isDotOrDot && node->isHidden()))
+ || (hideSystem && node->isSystem())
+ || (hideDirs && node->isDir())
+ || (hideFiles && node->isFile())
+ || (hideSymlinks && node->isSymLink())
+ || (hideReadable && node->isReadable())
+ || (hideWritable && node->isWritable())
+ || (hideExecutable && node->isExecutable())
+ || (hideDotAndDotDot && isDotOrDot))
+ return false;
+
+ return nameFilterDisables || passNameFilters(node);
+}
+
+/*
+ \internal
+
+ Returns true if node passes the name filters and should be visible.
+ */
+bool QFileSystemModelPrivate::passNameFilters(const QFileSystemNode *node) const
+{
+#ifndef QT_NO_REGEXP
+ if (nameFilters.isEmpty())
+ return true;
+
+ // Check the name regularexpression filters
+ if (!(node->isDir() && (filters & QDir::AllDirs))) {
+ for (int i = 0; i < nameFilters.size(); ++i) {
+ if (nameFilters.at(i).exactMatch(node->fileName))
+ return true;
+ }
+ return false;
+ }
+#endif
+ return true;
+}
+
+#include "moc_qfilesystemmodel.cpp"
+
+#endif // QT_NO_FILESYSTEMMODEL
+
+QT_END_NAMESPACE
diff --git a/src/gui/dialogs/qfilesystemmodel.h b/src/gui/dialogs/qfilesystemmodel.h
new file mode 100644
index 0000000000..52ecaf9181
--- /dev/null
+++ b/src/gui/dialogs/qfilesystemmodel.h
@@ -0,0 +1,179 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QFILESYSTEMMODEL_H
+#define QFILESYSTEMMODEL_H
+
+#include <QtCore/qabstractitemmodel.h>
+#include <QtCore/qpair.h>
+#include <QtCore/qdir.h>
+#include <QtGui/qicon.h>
+#include <QtCore/qdiriterator.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+#ifndef QT_NO_FILESYSTEMMODEL
+
+class ExtendedInformation;
+class QFileSystemModelPrivate;
+class QFileIconProvider;
+
+class Q_GUI_EXPORT QFileSystemModel : public QAbstractItemModel
+{
+ Q_OBJECT
+ Q_PROPERTY(bool resolveSymlinks READ resolveSymlinks WRITE setResolveSymlinks)
+ Q_PROPERTY(bool readOnly READ isReadOnly WRITE setReadOnly)
+ Q_PROPERTY(bool nameFilterDisables READ nameFilterDisables WRITE setNameFilterDisables)
+
+Q_SIGNALS:
+ void rootPathChanged(const QString &newPath);
+ void fileRenamed(const QString &path, const QString &oldName, const QString &newName);
+
+public:
+ enum Roles {
+ FileIconRole = Qt::DecorationRole,
+ FilePathRole = Qt::UserRole + 1,
+ FileNameRole = Qt::UserRole + 2,
+ FilePermissions = Qt::UserRole + 3
+ };
+
+ explicit QFileSystemModel(QObject *parent = 0);
+ ~QFileSystemModel();
+
+ QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const;
+ QModelIndex index(const QString &path, int column = 0) const;
+ QModelIndex parent(const QModelIndex &child) const;
+ bool hasChildren(const QModelIndex &parent = QModelIndex()) const;
+ bool canFetchMore(const QModelIndex &parent) const;
+ void fetchMore(const QModelIndex &parent);
+
+ int rowCount(const QModelIndex &parent = QModelIndex()) const;
+ int columnCount(const QModelIndex &parent = QModelIndex()) const;
+
+ QVariant myComputer(int role = Qt::DisplayRole) const;
+ QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
+ bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole);
+
+ QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;
+
+ Qt::ItemFlags flags(const QModelIndex &index) const;
+
+ void sort(int column, Qt::SortOrder order = Qt::AscendingOrder);
+
+ QStringList mimeTypes() const;
+ QMimeData *mimeData(const QModelIndexList &indexes) const;
+ bool dropMimeData(const QMimeData *data, Qt::DropAction action,
+ int row, int column, const QModelIndex &parent);
+ Qt::DropActions supportedDropActions() const;
+
+ // QFileSystemModel specific API
+ QModelIndex setRootPath(const QString &path);
+ QString rootPath() const;
+ QDir rootDirectory() const;
+
+ void setIconProvider(QFileIconProvider *provider);
+ QFileIconProvider *iconProvider() const;
+
+ void setFilter(QDir::Filters filters);
+ QDir::Filters filter() const;
+
+ void setResolveSymlinks(bool enable);
+ bool resolveSymlinks() const;
+
+ void setReadOnly(bool enable);
+ bool isReadOnly() const;
+
+ void setNameFilterDisables(bool enable);
+ bool nameFilterDisables() const;
+
+ void setNameFilters(const QStringList &filters);
+ QStringList nameFilters() const;
+
+ QString filePath(const QModelIndex &index) const;
+ bool isDir(const QModelIndex &index) const;
+ qint64 size(const QModelIndex &index) const;
+ QString type(const QModelIndex &index) const;
+ QDateTime lastModified(const QModelIndex &index) const;
+
+ QModelIndex mkdir(const QModelIndex &parent, const QString &name);
+ inline bool rmdir(const QModelIndex &index) const;
+ inline QString fileName(const QModelIndex &index) const;
+ inline QIcon fileIcon(const QModelIndex &index) const;
+ QFile::Permissions permissions(const QModelIndex &index) const;
+ inline QFileInfo fileInfo(const QModelIndex &index) const;
+ bool remove(const QModelIndex &index) const;
+
+protected:
+ QFileSystemModel(QFileSystemModelPrivate &, QObject *parent = 0);
+ void timerEvent(QTimerEvent *event);
+ bool event(QEvent *event);
+
+private:
+ Q_DECLARE_PRIVATE(QFileSystemModel)
+ Q_DISABLE_COPY(QFileSystemModel)
+
+ Q_PRIVATE_SLOT(d_func(), void _q_directoryChanged(const QString &directory, const QStringList &list))
+ Q_PRIVATE_SLOT(d_func(), void _q_performDelayedSort())
+ Q_PRIVATE_SLOT(d_func(), void _q_fileSystemChanged(const QString &path, const QList<QPair<QString, QFileInfo> > &))
+ Q_PRIVATE_SLOT(d_func(), void _q_resolvedName(const QString &fileName, const QString &resolvedName))
+};
+
+inline bool QFileSystemModel::rmdir(const QModelIndex &aindex) const
+{ QDir dir; return dir.rmdir(filePath(aindex)); }
+inline QString QFileSystemModel::fileName(const QModelIndex &aindex) const
+{ return aindex.data(Qt::DisplayRole).toString(); }
+inline QIcon QFileSystemModel::fileIcon(const QModelIndex &aindex) const
+{ return qvariant_cast<QIcon>(aindex.data(Qt::DecorationRole)); }
+inline QFileInfo QFileSystemModel::fileInfo(const QModelIndex &aindex) const
+{ return QFileInfo(filePath(aindex)); }
+
+#endif // QT_NO_FILESYSTEMMODEL
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QFILESYSTEMMODEL_H
+
diff --git a/src/gui/dialogs/qfilesystemmodel_p.h b/src/gui/dialogs/qfilesystemmodel_p.h
new file mode 100644
index 0000000000..f1e798b52e
--- /dev/null
+++ b/src/gui/dialogs/qfilesystemmodel_p.h
@@ -0,0 +1,305 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QFILESYSTEMMODEL_P_H
+#define QFILESYSTEMMODEL_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 "qfilesystemmodel.h"
+
+#ifndef QT_NO_FILESYSTEMMODEL
+
+#include <private/qabstractitemmodel_p.h>
+#include <qabstractitemmodel.h>
+#include "qfileinfogatherer_p.h"
+#include <qpair.h>
+#include <qdir.h>
+#include <qicon.h>
+#include <qdir.h>
+#include <qicon.h>
+#include <qfileinfo.h>
+#include <qtimer.h>
+#include <qhash.h>
+
+QT_BEGIN_NAMESPACE
+
+class ExtendedInformation;
+class QFileSystemModelPrivate;
+class QFileIconProvider;
+
+class Q_AUTOTEST_EXPORT QFileSystemModelPrivate : public QAbstractItemModelPrivate
+{
+ Q_DECLARE_PUBLIC(QFileSystemModel)
+
+public:
+ class QFileSystemNode
+ {
+ public:
+ QFileSystemNode(const QString &filename = QString(), QFileSystemNode *p = 0)
+ : fileName(filename), populatedChildren(false), isVisible(false), parent(p), info(0) {}
+ ~QFileSystemNode() {
+ QHash<QString, QFileSystemNode*>::const_iterator i = children.constBegin();
+ while (i != children.constEnd()) {
+ delete i.value();
+ ++i;
+ }
+ delete info;
+ info = 0;
+ parent = 0;
+ }
+
+ QString fileName;
+
+ inline qint64 size() const { if (info && !info->isDir()) return info->size(); return 0; }
+ inline QString type() const { if (info) return info->displayType; return QLatin1String(""); }
+ inline QDateTime lastModified() const { if (info) return info->lastModified(); return QDateTime(); }
+ inline QFile::Permissions permissions() const { if (info) return info->permissions(); return 0; }
+ inline bool isReadable() const { return ((permissions() & QFile::ReadUser) != 0); }
+ inline bool isWritable() const { return ((permissions() & QFile::WriteUser) != 0); }
+ inline bool isExecutable() const { return ((permissions() & QFile::ExeUser) != 0); }
+ inline bool isDir() const {
+ if (info)
+ return info->isDir();
+ if (children.count() > 0)
+ return true;
+ return false;
+ }
+ inline bool isFile() const { if (info) return info->isFile(); return true; }
+ inline bool isSystem() const { if (info) return info->isSystem(); return true; }
+ inline bool isHidden() const { if (info) return info->isHidden(); return false; }
+ inline bool isSymLink() const { if (info) return info->isSymLink(); return false; }
+ inline bool caseSensitive() const { if (info) return info->isCaseSensitive(); return false; }
+ inline QIcon icon() const { if (info) return info->icon; return QIcon(); }
+
+ inline bool operator <(const QFileSystemNode &node) const {
+ if (caseSensitive() || node.caseSensitive())
+ return fileName < node.fileName;
+ return QString::compare(fileName, node.fileName, Qt::CaseInsensitive) < 0;
+ }
+ inline bool operator >(const QString &name) const {
+ if (caseSensitive())
+ return fileName > name;
+ return QString::compare(fileName, name, Qt::CaseInsensitive) > 0;
+ }
+ inline bool operator <(const QString &name) const {
+ if (caseSensitive())
+ return fileName < name;
+ return QString::compare(fileName, name, Qt::CaseInsensitive) < 0;
+ }
+ inline bool operator !=(const QExtendedInformation &fileInfo) const {
+ return !operator==(fileInfo);
+ }
+ bool operator ==(const QString &name) const {
+ if (caseSensitive())
+ return fileName == name;
+ return QString::compare(fileName, name, Qt::CaseInsensitive) == 0;
+ }
+ bool operator ==(const QExtendedInformation &fileInfo) const {
+ return info && (*info == fileInfo);
+ }
+
+ inline bool hasInformation() const { return info != 0; }
+
+ void populate(const QExtendedInformation &fileInfo) {
+ if (!info)
+ info = new QExtendedInformation(fileInfo.fileInfo());
+ (*info) = fileInfo;
+ }
+
+ // children shouldn't normally be accessed directly, use node()
+ inline int visibleLocation(QString childName) {
+ return visibleChildren.indexOf(childName);
+ }
+ void updateIcon(QFileIconProvider *iconProvider, const QString &path) {
+ if (info)
+ info->icon = iconProvider->icon(QFileInfo(path));
+ QHash<QString, QFileSystemNode *>::const_iterator iterator;
+ for(iterator = children.constBegin() ; iterator != children.constEnd() ; ++iterator) {
+ iterator.value()->updateIcon(iconProvider, path + QLatin1Char('/') + iterator.value()->fileName);
+ }
+ }
+
+ void retranslateStrings(QFileIconProvider *iconProvider, const QString &path) {
+ if (info)
+ info->displayType = iconProvider->type(QFileInfo(path));
+ QHash<QString, QFileSystemNode *>::const_iterator iterator;
+ for(iterator = children.constBegin() ; iterator != children.constEnd() ; ++iterator) {
+ iterator.value()->retranslateStrings(iconProvider, path + QLatin1Char('/') + iterator.value()->fileName);
+ }
+ }
+
+ bool populatedChildren;
+ bool isVisible;
+ QHash<QString,QFileSystemNode *> children;
+ QList<QString> visibleChildren;
+ QFileSystemNode *parent;
+
+ private:
+ QExtendedInformation *info;
+
+ };
+
+ QFileSystemModelPrivate() :
+ forceSort(true),
+ sortColumn(0),
+ sortOrder(Qt::AscendingOrder),
+ readOnly(true),
+ setRootPath(false),
+ filters(QDir::AllEntries | QDir::NoDotAndDotDot | QDir::AllDirs),
+ nameFilterDisables(true) // false on windows, true on mac and unix
+ {
+ delayedSortTimer.setSingleShot(true);
+ }
+
+ void init();
+ /*
+ \internal
+
+ Return true if index which is owned by node is hidden by the filter.
+ */
+ inline bool isHiddenByFilter(QFileSystemNode *indexNode, const QModelIndex &index) const
+ {
+ return (indexNode != &root && !index.isValid());
+ }
+ QFileSystemNode *node(const QModelIndex &index) const;
+ QFileSystemNode *node(const QString &path, bool fetch = true) const;
+ inline QModelIndex index(const QString &path) { return index(node(path)); }
+ QModelIndex index(const QFileSystemNode *node) const;
+ bool filtersAcceptsNode(const QFileSystemNode *node) const;
+ bool passNameFilters(const QFileSystemNode *node) const;
+ void removeNode(QFileSystemNode *parentNode, const QString &name);
+ QFileSystemNode* addNode(QFileSystemNode *parentNode, const QString &fileName);
+ void addVisibleFiles(QFileSystemNode *parentNode, const QStringList &newFiles);
+ void removeVisibleFile(QFileSystemNode *parentNode, int visibleLocation);
+ void sortChildren(int column, const QModelIndex &parent);
+
+ inline int translateVisibleLocation(QFileSystemNode *parent, int row) const {
+ return (sortOrder == Qt::AscendingOrder) ? row : parent->visibleChildren.count() - row - 1;
+ }
+
+ inline static QString myComputer() {
+ // ### TODO We should query the system to find out what the string should be
+ // XP == "My Computer",
+ // Vista == "Computer",
+ // OS X == "Computer" (sometime user generated) "Benjamin's PowerBook G4"
+#ifdef Q_OS_WIN
+ return QFileSystemModel::tr("My Computer");
+#else
+ return QFileSystemModel::tr("Computer");
+#endif
+ }
+
+ inline void delayedSort() {
+ if (!delayedSortTimer.isActive())
+ delayedSortTimer.start(0);
+ }
+
+ static bool caseInsensitiveLessThan(const QString &s1, const QString &s2)
+ {
+ return QString::compare(s1, s2, Qt::CaseInsensitive) < 0;
+ }
+
+ static bool nodeCaseInsensitiveLessThan(const QFileSystemModelPrivate::QFileSystemNode &s1, const QFileSystemModelPrivate::QFileSystemNode &s2)
+ {
+ return QString::compare(s1.fileName, s2.fileName, Qt::CaseInsensitive) < 0;
+ }
+
+ QIcon icon(const QModelIndex &index) const;
+ QString name(const QModelIndex &index) const;
+ QString filePath(const QModelIndex &index) const;
+ QString size(const QModelIndex &index) const;
+ static QString size(qint64 bytes);
+ QString type(const QModelIndex &index) const;
+ QString time(const QModelIndex &index) const;
+
+ void _q_directoryChanged(const QString &directory, const QStringList &list);
+ void _q_performDelayedSort();
+ void _q_fileSystemChanged(const QString &path, const QList<QPair<QString, QFileInfo> > &);
+ void _q_resolvedName(const QString &fileName, const QString &resolvedName);
+
+ static int naturalCompare(const QString &s1, const QString &s2, Qt::CaseSensitivity cs);
+
+ QDir rootDir;
+#ifndef QT_NO_FILESYSTEMWATCHER
+ QFileInfoGatherer fileInfoGatherer;
+#endif
+ QTimer delayedSortTimer;
+ bool forceSort;
+ int sortColumn;
+ Qt::SortOrder sortOrder;
+ bool readOnly;
+ bool setRootPath;
+ QDir::Filters filters;
+ QHash<const QFileSystemNode*, bool> bypassFilters;
+ bool nameFilterDisables;
+#ifndef QT_NO_REGEXP
+ QList<QRegExp> nameFilters;
+#endif
+ // ### Qt 5: resolvedSymLinks goes away
+ QHash<QString, QString> resolvedSymLinks;
+
+ QFileSystemNode root;
+
+ QBasicTimer fetchingTimer;
+ struct Fetching {
+ QString dir;
+ QString file;
+ const QFileSystemNode *node;
+ };
+ QList<Fetching> toFetch;
+
+};
+#endif // QT_NO_FILESYSTEMMODEL
+
+QT_END_NAMESPACE
+
+#endif
+
diff --git a/src/gui/dialogs/qfontdialog.cpp b/src/gui/dialogs/qfontdialog.cpp
new file mode 100644
index 0000000000..4c5bf4fbaa
--- /dev/null
+++ b/src/gui/dialogs/qfontdialog.cpp
@@ -0,0 +1,1070 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qwindowdefs.h"
+
+#ifndef QT_NO_FONTDIALOG
+
+#include "qfontdialog.h"
+#include "qfontdialog_p.h"
+
+#include <qapplication.h>
+#include <qcheckbox.h>
+#include <qcombobox.h>
+#include <qevent.h>
+#include <qfontdatabase.h>
+#include <qgroupbox.h>
+#include <qlabel.h>
+#include <qlayout.h>
+#include <qlineedit.h>
+#include <qpushbutton.h>
+#include <qstyle.h>
+#include <qdialogbuttonbox.h>
+#include <qheaderview.h>
+#include <qlistview.h>
+#include <qstringlistmodel.h>
+#include <qvalidator.h>
+#include <private/qdialog_p.h>
+#include <private/qfont_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QFontListView : public QListView
+{
+ Q_OBJECT
+public:
+ QFontListView(QWidget *parent);
+ inline QStringListModel *model() const {
+ return static_cast<QStringListModel *>(QListView::model());
+ }
+ inline void setCurrentItem(int item) {
+ QListView::setCurrentIndex(static_cast<QAbstractListModel*>(model())->index(item));
+ }
+ inline int currentItem() const {
+ return QListView::currentIndex().row();
+ }
+ inline int count() const {
+ return model()->rowCount();
+ }
+ inline QString currentText() const {
+ int row = QListView::currentIndex().row();
+ return row < 0 ? QString() : model()->stringList().at(row);
+ }
+ void currentChanged(const QModelIndex &current, const QModelIndex &previous) {
+ QListView::currentChanged(current, previous);
+ if (current.isValid())
+ emit highlighted(current.row());
+ }
+ QString text(int i) const {
+ return model()->stringList().at(i);
+ }
+signals:
+ void highlighted(int);
+};
+
+QFontListView::QFontListView(QWidget *parent)
+ : QListView(parent)
+{
+ setModel(new QStringListModel(parent));
+ setEditTriggers(NoEditTriggers);
+}
+
+static const Qt::WindowFlags DefaultWindowFlags =
+ Qt::Dialog | Qt::WindowSystemMenuHint;
+
+/*!
+ \class QFontDialog
+ \ingroup dialogs
+ \ingroup text
+ \mainclass
+ \brief The QFontDialog class provides a dialog widget for selecting a font.
+
+ A font dialog is created through one of the static getFont()
+ functions.
+
+ Examples:
+
+ \snippet doc/src/snippets/code/src_gui_dialogs_qfontdialog.cpp 0
+
+ The dialog can also be used to set a widget's font directly:
+ \snippet doc/src/snippets/code/src_gui_dialogs_qfontdialog.cpp 1
+ If the user clicks OK the font they chose will be used for myWidget,
+ and if they click Cancel the original font is used.
+
+ \image plastique-fontdialog.png A font dialog in the Plastique widget style.
+
+ \sa QFont, QFontInfo, QFontMetrics, QColorDialog, QFileDialog, QPrintDialog,
+ {Standard Dialogs Example}
+*/
+
+/*!
+ \since 4.5
+
+ Constructs a standard font dialog.
+
+ Use setCurrentFont() to set the initial font attributes.
+
+ The \a parent parameter is passed to the QDialog constructor.
+
+ \sa getFont()
+*/
+QFontDialog::QFontDialog(QWidget *parent)
+ : QDialog(*new QFontDialogPrivate, parent, DefaultWindowFlags)
+{
+ Q_D(QFontDialog);
+ d->init();
+}
+
+/*!
+ \since 4.5
+
+ Constructs a standard font dialog with the given \a parent and specified
+ \a initial color.
+*/
+QFontDialog::QFontDialog(const QFont &initial, QWidget *parent)
+ : QDialog(*new QFontDialogPrivate, parent, DefaultWindowFlags)
+{
+ Q_D(QFontDialog);
+ d->init();
+ setCurrentFont(initial);
+}
+
+void QFontDialogPrivate::init()
+{
+ Q_Q(QFontDialog);
+
+ q->setSizeGripEnabled(true);
+ q->setWindowTitle(QFontDialog::tr("Select Font"));
+
+ // grid
+ familyEdit = new QLineEdit(q);
+ familyEdit->setReadOnly(true);
+ familyList = new QFontListView(q);
+ familyEdit->setFocusProxy(familyList);
+
+ familyAccel = new QLabel(q);
+#ifndef QT_NO_SHORTCUT
+ familyAccel->setBuddy(familyList);
+#endif
+ familyAccel->setIndent(2);
+
+ styleEdit = new QLineEdit(q);
+ styleEdit->setReadOnly(true);
+ styleList = new QFontListView(q);
+ styleEdit->setFocusProxy(styleList);
+
+ styleAccel = new QLabel(q);
+#ifndef QT_NO_SHORTCUT
+ styleAccel->setBuddy(styleList);
+#endif
+ styleAccel->setIndent(2);
+
+ sizeEdit = new QLineEdit(q);
+ sizeEdit->setFocusPolicy(Qt::ClickFocus);
+ QIntValidator *validator = new QIntValidator(1, 512, q);
+ sizeEdit->setValidator(validator);
+ sizeList = new QFontListView(q);
+
+ sizeAccel = new QLabel(q);
+#ifndef QT_NO_SHORTCUT
+ sizeAccel->setBuddy(sizeEdit);
+#endif
+ sizeAccel->setIndent(2);
+
+ // effects box
+ effects = new QGroupBox(q);
+ QVBoxLayout *vbox = new QVBoxLayout(effects);
+ strikeout = new QCheckBox(effects);
+ vbox->addWidget(strikeout);
+ underline = new QCheckBox(effects);
+ vbox->addWidget(underline);
+
+ sample = new QGroupBox(q);
+ QHBoxLayout *hbox = new QHBoxLayout(sample);
+ sampleEdit = new QLineEdit(sample);
+ sampleEdit->setSizePolicy(QSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored));
+ sampleEdit->setAlignment(Qt::AlignCenter);
+ // Note that the sample text is *not* translated with tr(), as the
+ // characters used depend on the charset encoding.
+ sampleEdit->setText(QLatin1String("AaBbYyZz"));
+ hbox->addWidget(sampleEdit);
+
+ writingSystemCombo = new QComboBox(q);
+
+ writingSystemAccel = new QLabel(q);
+#ifndef QT_NO_SHORTCUT
+ writingSystemAccel->setBuddy(writingSystemCombo);
+#endif
+ writingSystemAccel->setIndent(2);
+
+ size = 0;
+ smoothScalable = false;
+
+ QObject::connect(writingSystemCombo, SIGNAL(activated(int)), q, SLOT(_q_writingSystemHighlighted(int)));
+ QObject::connect(familyList, SIGNAL(highlighted(int)), q, SLOT(_q_familyHighlighted(int)));
+ QObject::connect(styleList, SIGNAL(highlighted(int)), q, SLOT(_q_styleHighlighted(int)));
+ QObject::connect(sizeList, SIGNAL(highlighted(int)), q, SLOT(_q_sizeHighlighted(int)));
+ QObject::connect(sizeEdit, SIGNAL(textChanged(QString)), q, SLOT(_q_sizeChanged(QString)));
+
+ QObject::connect(strikeout, SIGNAL(clicked()), q, SLOT(_q_updateSample()));
+ QObject::connect(underline, SIGNAL(clicked()), q, SLOT(_q_updateSample()));
+
+ for (int i = 0; i < QFontDatabase::WritingSystemsCount; ++i) {
+ QFontDatabase::WritingSystem ws = QFontDatabase::WritingSystem(i);
+ QString writingSystemName = QFontDatabase::writingSystemName(ws);
+ if (writingSystemName.isEmpty())
+ break;
+ writingSystemCombo->addItem(writingSystemName);
+ }
+
+ updateFamilies();
+ if (familyList->count() != 0)
+ familyList->setCurrentItem(0);
+
+ // grid layout
+ QGridLayout *mainGrid = new QGridLayout(q);
+
+ int spacing = mainGrid->spacing();
+ if (spacing >= 0) { // uniform spacing
+ mainGrid->setSpacing(0);
+
+ mainGrid->setColumnMinimumWidth(1, spacing);
+ mainGrid->setColumnMinimumWidth(3, spacing);
+
+ int margin = 0;
+ mainGrid->getContentsMargins(0, 0, 0, &margin);
+
+ mainGrid->setRowMinimumHeight(3, margin);
+ mainGrid->setRowMinimumHeight(6, 2);
+ mainGrid->setRowMinimumHeight(8, margin);
+ }
+
+ mainGrid->addWidget(familyAccel, 0, 0);
+ mainGrid->addWidget(familyEdit, 1, 0);
+ mainGrid->addWidget(familyList, 2, 0);
+
+ mainGrid->addWidget(styleAccel, 0, 2);
+ mainGrid->addWidget(styleEdit, 1, 2);
+ mainGrid->addWidget(styleList, 2, 2);
+
+ mainGrid->addWidget(sizeAccel, 0, 4);
+ mainGrid->addWidget(sizeEdit, 1, 4);
+ mainGrid->addWidget(sizeList, 2, 4);
+
+ mainGrid->setColumnStretch(0, 38);
+ mainGrid->setColumnStretch(2, 24);
+ mainGrid->setColumnStretch(4, 10);
+
+ mainGrid->addWidget(effects, 4, 0);
+
+ mainGrid->addWidget(sample, 4, 2, 4, 3);
+
+ mainGrid->addWidget(writingSystemAccel, 5, 0);
+ mainGrid->addWidget(writingSystemCombo, 7, 0);
+
+ buttonBox = new QDialogButtonBox(q);
+ mainGrid->addWidget(buttonBox, 9, 0, 1, 5);
+
+ QPushButton *button
+ = static_cast<QPushButton *>(buttonBox->addButton(QDialogButtonBox::Ok));
+ QObject::connect(buttonBox, SIGNAL(accepted()), q, SLOT(accept()));
+ button->setDefault(true);
+
+ buttonBox->addButton(QDialogButtonBox::Cancel);
+ QObject::connect(buttonBox, SIGNAL(rejected()), q, SLOT(reject()));
+
+#if defined(Q_OS_WINCE)
+ q->resize(180, 120);
+#else
+ q->resize(500, 360);
+#endif // Q_OS_WINCE
+
+ sizeEdit->installEventFilter(q);
+ familyList->installEventFilter(q);
+ styleList->installEventFilter(q);
+ sizeList->installEventFilter(q);
+
+ familyList->setFocus();
+ retranslateStrings();
+
+#ifdef Q_WS_MAC
+ delegate = 0;
+#endif
+}
+
+/*!
+ \internal
+ Destroys the font dialog and frees up its storage.
+*/
+
+QFontDialog::~QFontDialog()
+{
+}
+
+/*!
+ Executes a modal font dialog and returns a font.
+
+ If the user clicks \gui OK, the selected font is returned. If the user
+ clicks \gui Cancel, the \a initial font is returned.
+
+ The dialog is constructed with the given \a parent and the options specified
+ in \a options. \a title is shown as the window title of the dialog and \a
+ initial is the initially selected font. If the \a ok parameter is not-null,
+ the value it refers to is set to true if the user clicks \gui OK, and set to
+ false if the user clicks \gui Cancel.
+
+ Examples:
+ \snippet doc/src/snippets/code/src_gui_dialogs_qfontdialog.cpp 2
+
+ The dialog can also be used to set a widget's font directly:
+ \snippet doc/src/snippets/code/src_gui_dialogs_qfontdialog.cpp 3
+ In this example, if the user clicks OK the font they chose will be
+ used, and if they click Cancel the original font is used.
+
+ \warning Do not delete \a parent during the execution of the dialog.
+ If you want to do this, you should create the dialog
+ yourself using one of the QFontDialog constructors.
+*/
+QFont QFontDialog::getFont(bool *ok, const QFont &initial, QWidget *parent, const QString &title,
+ FontDialogOptions options)
+{
+ return QFontDialogPrivate::getFont(ok, initial, parent, title, options);
+}
+
+/*!
+ \overload
+ \since 4.5
+*/
+QFont QFontDialog::getFont(bool *ok, const QFont &initial, QWidget *parent, const QString &title)
+{
+ return QFontDialogPrivate::getFont(ok, initial, parent, title, 0);
+}
+
+/*!
+ \overload
+*/
+QFont QFontDialog::getFont(bool *ok, const QFont &initial, QWidget *parent)
+{
+ return QFontDialogPrivate::getFont(ok, initial, parent, QString(), 0);
+}
+
+/*!
+ \overload
+
+ Executes a modal font dialog and returns a font.
+
+ If the user clicks \gui OK, the selected font is returned. If the user
+ clicks \gui Cancel, the Qt default font is returned.
+
+ The dialog is constructed with the given \a parent.
+ If the \a ok parameter is not-null, the value it refers to is set
+ to true if the user clicks \gui OK, and false if the user clicks
+ \gui Cancel.
+
+ Example:
+ \snippet doc/src/snippets/code/src_gui_dialogs_qfontdialog.cpp 4
+
+ \warning Do not delete \a parent during the execution of the dialog.
+ If you want to do this, you should create the dialog
+ yourself using one of the QFontDialog constructors.
+*/
+QFont QFontDialog::getFont(bool *ok, QWidget *parent)
+{
+ QFont initial;
+ return QFontDialogPrivate::getFont(ok, initial, parent, QString(), 0);
+}
+
+QFont QFontDialogPrivate::getFont(bool *ok, const QFont &initial, QWidget *parent,
+ const QString &title, QFontDialog::FontDialogOptions options)
+{
+#ifdef Q_WS_MAC
+ if (!(options & QFontDialog::DontUseNativeDialog)
+ && QFontDialogPrivate::sharedFontPanelAvailable) {
+ return QFontDialogPrivate::execCocoaFontPanel(ok, initial, parent,
+ title.isEmpty() ? QFontDialog::tr("Select Font") : title, options);
+ }
+#endif
+
+ QFontDialog dlg(parent);
+ dlg.setOptions(options);
+ dlg.setCurrentFont(initial);
+ if (!title.isEmpty())
+ dlg.setWindowTitle(title);
+
+ int ret = (dlg.exec() || (options & QFontDialog::NoButtons));
+ if (ok)
+ *ok = !!ret;
+ if (ret) {
+ return dlg.selectedFont();
+ } else {
+ return initial;
+ }
+}
+
+/*!
+ \internal
+ An event filter to make the Up, Down, PageUp and PageDown keys work
+ correctly in the line edits. The source of the event is the object
+ \a o and the event is \a e.
+*/
+
+bool QFontDialog::eventFilter(QObject *o , QEvent *e)
+{
+ Q_D(QFontDialog);
+ if (e->type() == QEvent::KeyPress) {
+ QKeyEvent *k = (QKeyEvent *)e;
+ if (o == d->sizeEdit &&
+ (k->key() == Qt::Key_Up ||
+ k->key() == Qt::Key_Down ||
+ k->key() == Qt::Key_PageUp ||
+ k->key() == Qt::Key_PageDown)) {
+
+ int ci = d->sizeList->currentItem();
+ (void)QApplication::sendEvent(d->sizeList, k);
+
+ if (ci != d->sizeList->currentItem()
+ && style()->styleHint(QStyle::SH_FontDialog_SelectAssociatedText, 0, this))
+ d->sizeEdit->selectAll();
+ return true;
+ } else if ((o == d->familyList || o == d->styleList) &&
+ (k->key() == Qt::Key_Return || k->key() == Qt::Key_Enter)) {
+ k->accept();
+ accept();
+ return true;
+ }
+ } else if (e->type() == QEvent::FocusIn
+ && style()->styleHint(QStyle::SH_FontDialog_SelectAssociatedText, 0, this)) {
+ if (o == d->familyList)
+ d->familyEdit->selectAll();
+ else if (o == d->styleList)
+ d->styleEdit->selectAll();
+ else if (o == d->sizeList)
+ d->sizeEdit->selectAll();
+ } else if (e->type() == QEvent::MouseButtonPress && o == d->sizeList) {
+ d->sizeEdit->setFocus();
+ }
+ return QDialog::eventFilter(o, e);
+}
+
+/*
+ Updates the contents of the "font family" list box. This
+ function can be reimplemented if you have special requirements.
+*/
+
+void QFontDialogPrivate::updateFamilies()
+{
+ Q_Q(QFontDialog);
+
+ familyList->blockSignals(true);
+
+ enum match_t { MATCH_NONE = 0, MATCH_LAST_RESORT = 1, MATCH_APP = 2, MATCH_FAMILY = 3 };
+
+ QStringList familyNames = fdb.families(writingSystem);
+
+ familyList->model()->setStringList(familyNames);
+
+ QString foundryName1, familyName1, foundryName2, familyName2;
+ int bestFamilyMatch = -1;
+ match_t bestFamilyType = MATCH_NONE;
+
+ QFont f;
+
+ // ##### do the right thing for a list of family names in the font.
+ QFontDatabase::parseFontName(family, foundryName1, familyName1);
+
+ QStringList::const_iterator it = familyNames.constBegin();
+ int i = 0;
+ for(; it != familyNames.constEnd(); ++it, ++i) {
+ QFontDatabase::parseFontName(*it, foundryName2, familyName2);
+
+ //try to match...
+ if (familyName1 == familyName2) {
+ bestFamilyType = MATCH_FAMILY;
+ if (foundryName1 == foundryName2) {
+ bestFamilyMatch = i;
+ break;
+ }
+ if (bestFamilyMatch < MATCH_FAMILY)
+ bestFamilyMatch = i;
+ }
+
+ //and try some fall backs
+ match_t type = MATCH_NONE;
+ if (bestFamilyType <= MATCH_NONE && familyName2 == f.lastResortFamily())
+ type = MATCH_LAST_RESORT;
+ if (bestFamilyType <= MATCH_LAST_RESORT && familyName2 == f.family())
+ type = MATCH_APP;
+ // ### add fallback for writingSystem
+ if (type != MATCH_NONE) {
+ bestFamilyType = type;
+ bestFamilyMatch = i;
+ }
+ }
+
+ if (i != -1 && bestFamilyType != MATCH_NONE)
+ familyList->setCurrentItem(bestFamilyMatch);
+ else
+ familyList->setCurrentItem(0);
+ familyEdit->setText(familyList->currentText());
+ if (q->style()->styleHint(QStyle::SH_FontDialog_SelectAssociatedText, 0, q)
+ && familyList->hasFocus())
+ familyEdit->selectAll();
+
+ familyList->blockSignals(false);
+ updateStyles();
+}
+
+/*
+ Updates the contents of the "font style" list box. This
+ function can be reimplemented if you have special requirements.
+*/
+void QFontDialogPrivate::updateStyles()
+{
+ Q_Q(QFontDialog);
+
+ styleList->blockSignals(true);
+
+ QStringList styles = fdb.styles(familyList->currentText());
+ styleList->model()->setStringList(styles);
+
+ if (styles.isEmpty()) {
+ styleEdit->clear();
+ smoothScalable = false;
+ } else {
+ if (!style.isEmpty()) {
+ bool found = false;
+ bool first = true;
+ QString cstyle = style;
+
+ redo:
+ for (int i = 0; i < (int)styleList->count(); i++) {
+ if (cstyle == styleList->text(i)) {
+ styleList->setCurrentItem(i);
+ found = true;
+ break;
+ }
+ }
+ if (!found && first) {
+ if (cstyle.contains(QLatin1String("Italic"))) {
+ cstyle.replace(QLatin1String("Italic"), QLatin1String("Oblique"));
+ first = false;
+ goto redo;
+ } else if (cstyle.contains(QLatin1String("Oblique"))) {
+ cstyle.replace(QLatin1String("Oblique"), QLatin1String("Italic"));
+ first = false;
+ goto redo;
+ }
+ }
+ if (!found)
+ styleList->setCurrentItem(0);
+ }
+
+ styleEdit->setText(styleList->currentText());
+ if (q->style()->styleHint(QStyle::SH_FontDialog_SelectAssociatedText, 0, q)
+ && styleList->hasFocus())
+ styleEdit->selectAll();
+
+ smoothScalable = fdb.isSmoothlyScalable(familyList->currentText(), styleList->currentText());
+ }
+
+ styleList->blockSignals(false);
+
+ updateSizes();
+}
+
+/*!
+ \internal
+ Updates the contents of the "font size" list box. This
+ function can be reimplemented if you have special requirements.
+*/
+
+void QFontDialogPrivate::updateSizes()
+{
+ Q_Q(QFontDialog);
+
+ sizeList->blockSignals(true);
+
+ if (!familyList->currentText().isEmpty()) {
+ QList<int> sizes = fdb.pointSizes(familyList->currentText(), styleList->currentText());
+
+ int i = 0;
+ int current = -1;
+ QStringList str_sizes;
+ for(QList<int>::const_iterator it = sizes.constBegin(); it != sizes.constEnd(); ++it) {
+ str_sizes.append(QString::number(*it));
+ if (current == -1 && *it >= size)
+ current = i;
+ ++i;
+ }
+ sizeList->model()->setStringList(str_sizes);
+ if (current == -1) {
+ // we request a size bigger than the ones in the list, select the biggest one
+ current = sizeList->count() - 1;
+ }
+ sizeList->setCurrentItem(current);
+
+ sizeEdit->blockSignals(true);
+ sizeEdit->setText((smoothScalable ? QString::number(size) : sizeList->currentText()));
+ if (q->style()->styleHint(QStyle::SH_FontDialog_SelectAssociatedText, 0, q)
+ && sizeList->hasFocus())
+ sizeEdit->selectAll();
+ sizeEdit->blockSignals(false);
+ } else {
+ sizeEdit->clear();
+ }
+
+ sizeList->blockSignals(false);
+ _q_updateSample();
+}
+
+void QFontDialogPrivate::_q_updateSample()
+{
+ // compute new font
+ int pSize = sizeEdit->text().toInt();
+ QFont newFont(fdb.font(familyList->currentText(), style, pSize));
+ newFont.setStrikeOut(strikeout->isChecked());
+ newFont.setUnderline(underline->isChecked());
+
+ if (familyList->currentText().isEmpty())
+ sampleEdit->clear();
+
+ updateSampleFont(newFont);
+}
+
+void QFontDialogPrivate::updateSampleFont(const QFont &newFont)
+{
+ Q_Q(QFontDialog);
+ if (newFont != sampleEdit->font()) {
+ sampleEdit->setFont(newFont);
+ emit q->currentFontChanged(newFont);
+ }
+}
+
+/*!
+ \internal
+*/
+void QFontDialogPrivate::_q_writingSystemHighlighted(int index)
+{
+ writingSystem = QFontDatabase::WritingSystem(index);
+ sampleEdit->setText(fdb.writingSystemSample(writingSystem));
+ updateFamilies();
+}
+
+/*!
+ \internal
+*/
+void QFontDialogPrivate::_q_familyHighlighted(int i)
+{
+ Q_Q(QFontDialog);
+ family = familyList->text(i);
+ familyEdit->setText(family);
+ if (q->style()->styleHint(QStyle::SH_FontDialog_SelectAssociatedText, 0, q)
+ && familyList->hasFocus())
+ familyEdit->selectAll();
+
+ updateStyles();
+}
+
+
+/*!
+ \internal
+*/
+
+void QFontDialogPrivate::_q_styleHighlighted(int index)
+{
+ Q_Q(QFontDialog);
+ QString s = styleList->text(index);
+ styleEdit->setText(s);
+ if (q->style()->styleHint(QStyle::SH_FontDialog_SelectAssociatedText, 0, q)
+ && styleList->hasFocus())
+ styleEdit->selectAll();
+
+ style = s;
+
+ updateSizes();
+}
+
+
+/*!
+ \internal
+*/
+
+void QFontDialogPrivate::_q_sizeHighlighted(int index)
+{
+ Q_Q(QFontDialog);
+ QString s = sizeList->text(index);
+ sizeEdit->setText(s);
+ if (q->style()->styleHint(QStyle::SH_FontDialog_SelectAssociatedText, 0, q)
+ && sizeEdit->hasFocus())
+ sizeEdit->selectAll();
+
+ size = s.toInt();
+ _q_updateSample();
+}
+
+/*!
+ \internal
+ This slot is called if the user changes the font size.
+ The size is passed in the \a s argument as a \e string.
+*/
+
+void QFontDialogPrivate::_q_sizeChanged(const QString &s)
+{
+ // no need to check if the conversion is valid, since we have an QIntValidator in the size edit
+ int size = s.toInt();
+ if (this->size == size)
+ return;
+
+ this->size = size;
+ if (sizeList->count() != 0) {
+ int i;
+ for (i = 0; i < sizeList->count() - 1; i++) {
+ if (sizeList->text(i).toInt() >= this->size)
+ break;
+ }
+ sizeList->blockSignals(true);
+ sizeList->setCurrentItem(i);
+ sizeList->blockSignals(false);
+ }
+ _q_updateSample();
+}
+
+void QFontDialogPrivate::retranslateStrings()
+{
+ familyAccel->setText(QFontDialog::tr("&Font"));
+ styleAccel->setText(QFontDialog::tr("Font st&yle"));
+ sizeAccel->setText(QFontDialog::tr("&Size"));
+ effects->setTitle(QFontDialog::tr("Effects"));
+ strikeout->setText(QFontDialog::tr("Stri&keout"));
+ underline->setText(QFontDialog::tr("&Underline"));
+ sample->setTitle(QFontDialog::tr("Sample"));
+ writingSystemAccel->setText(QFontDialog::tr("Wr&iting System"));
+}
+
+/*!
+ \reimp
+*/
+void QFontDialog::changeEvent(QEvent *e)
+{
+ Q_D(QFontDialog);
+ if (e->type() == QEvent::LanguageChange) {
+ d->retranslateStrings();
+ }
+ QDialog::changeEvent(e);
+}
+
+/*!
+ \since 4.5
+
+ \property QFontDialog::currentFont
+ \brief the current font of the dialog.
+*/
+
+/*!
+ \since 4.5
+
+ Sets the font highlighted in the QFontDialog to the given \a font.
+
+ \sa selectedFont()
+*/
+void QFontDialog::setCurrentFont(const QFont &font)
+{
+ Q_D(QFontDialog);
+ d->family = font.family();
+ d->style = d->fdb.styleString(font);
+ d->size = font.pointSize();
+ if (d->size == -1) {
+ QFontInfo fi(font);
+ d->size = fi.pointSize();
+ }
+ d->strikeout->setChecked(font.strikeOut());
+ d->underline->setChecked(font.underline());
+ d->updateFamilies();
+
+#ifdef Q_WS_MAC
+ if (d->delegate)
+ QFontDialogPrivate::setFont(d->delegate, font);
+#endif
+}
+
+/*!
+ \since 4.5
+
+ Returns the current font.
+
+ \sa selectedFont()
+*/
+QFont QFontDialog::currentFont() const
+{
+ Q_D(const QFontDialog);
+ return d->sampleEdit->font();
+}
+
+/*!
+ Returns the font that the user selected by clicking the \gui{OK}
+ or equivalent button.
+
+ \note This font is not always the same as the font held by the
+ \l currentFont property since the user can choose different fonts
+ before finally selecting the one to use.
+*/
+QFont QFontDialog::selectedFont() const
+{
+ Q_D(const QFontDialog);
+ return d->selectedFont;
+}
+
+/*!
+ \enum QFontDialog::FontDialogOption
+ \since 4.5
+
+ This enum specifies various options that affect the look and feel
+ of a font dialog.
+
+ \value NoButtons Don't display \gui{OK} and \gui{Cancel} buttons. (Useful for "live dialogs".)
+ \value DontUseNativeDialog Use Qt's standard font dialog on the Mac instead of Apple's
+ native font panel. (Currently, the native dialog is never used,
+ but this is likely to change in future Qt releases.)
+
+ \sa options, setOption(), testOption()
+*/
+
+/*!
+ Sets the given \a option to be enabled if \a on is true;
+ otherwise, clears the given \a option.
+
+ \sa options, testOption()
+*/
+void QFontDialog::setOption(FontDialogOption option, bool on)
+{
+ Q_D(QFontDialog);
+ if (!(d->opts & option) != !on)
+ setOptions(d->opts ^ option);
+}
+
+/*!
+ Returns true if the given \a option is enabled; otherwise, returns
+ false.
+
+ \sa options, setOption()
+*/
+bool QFontDialog::testOption(FontDialogOption option) const
+{
+ Q_D(const QFontDialog);
+ return (d->opts & option) != 0;
+}
+
+/*!
+ \property QFontDialog::options
+ \brief the various options that affect the look and feel of the dialog
+ \since 4.5
+
+ By default, all options are disabled.
+
+ Options should be set before showing the dialog. Setting them while the
+ dialog is visible is not guaranteed to have an immediate effect on the
+ dialog (depending on the option and on the platform).
+
+ \sa setOption(), testOption()
+*/
+void QFontDialog::setOptions(FontDialogOptions options)
+{
+ Q_D(QFontDialog);
+
+ FontDialogOptions changed = (options ^ d->opts);
+ if (!changed)
+ return;
+
+ d->opts = options;
+ d->buttonBox->setVisible(!(options & NoButtons));
+}
+
+QFontDialog::FontDialogOptions QFontDialog::options() const
+{
+ Q_D(const QFontDialog);
+ return d->opts;
+}
+
+#ifdef Q_WS_MAC
+// can only have one Cocoa font panel active
+bool QFontDialogPrivate::sharedFontPanelAvailable = true;
+#endif
+
+/*!
+ \since 4.5
+ \overload
+
+ Opens the dialog and connects its accepted() signal to the slot specified
+ by \a receiver and \a member.
+
+ The signal will be disconnected from the slot when the dialog is closed.
+*/
+void QFontDialog::open(QObject *receiver, const char *member)
+{
+ Q_D(QFontDialog);
+ connect(this, SIGNAL(fontSelected(const QFont&)), receiver, member);
+ d->receiverToDisconnectOnClose = receiver;
+ d->memberToDisconnectOnClose = member;
+ QDialog::open();
+}
+
+/*!
+ \since 4.5
+
+ \fn void QFontDialog::currentFontChanged(const QFont &font)
+
+ This signal is emitted when the current font is changed. The new font is
+ specified in \a font.
+
+ The signal is emitted while a user is selecting a font. Ultimately, the
+ chosen font may differ from the font currently selected.
+
+ \sa currentFont, fontSelected(), selectedFont()
+*/
+
+/*!
+ \since 4.5
+
+ \fn void QFontDialog::fontSelected(const QFont &font)
+
+ This signal is emitted when a font has been selected. The selected font is
+ specified in \a font.
+
+ The signal is only emitted when a user has chosen the final font to be
+ used. It is not emitted while the user is changing the current font in the
+ font dialog.
+
+ \sa selectedFont(), currentFontChanged(), currentFont
+*/
+
+/*!
+ \reimp
+*/
+void QFontDialog::setVisible(bool visible)
+{
+ Q_D(QFontDialog);
+ if (visible)
+ d->selectedFont = QFont();
+
+#if defined(Q_WS_MAC)
+ bool isCurrentlyVisible = (isVisible() || d->delegate);
+
+ if (!visible == !isCurrentlyVisible)
+ return;
+
+ if (visible) {
+ if (!(d->opts & DontUseNativeDialog) && QFontDialogPrivate::sharedFontPanelAvailable) {
+ d->delegate = QFontDialogPrivate::openCocoaFontPanel(
+ currentFont(), parentWidget(), windowTitle(), options(), d);
+ QFontDialogPrivate::sharedFontPanelAvailable = false;
+ return;
+ }
+
+ setWindowFlags(windowModality() == Qt::WindowModal ? Qt::Sheet : DefaultWindowFlags);
+ } else {
+ if (d->delegate) {
+ QFontDialogPrivate::closeCocoaFontPanel(d->delegate);
+ d->delegate = 0;
+ QFontDialogPrivate::sharedFontPanelAvailable = true;
+ return;
+ }
+ }
+#endif
+
+ QDialog::setVisible(visible);
+}
+
+/*!
+ Closes the dialog and sets its result code to \a result. If this dialog
+ is shown with exec(), done() causes the local event loop to finish,
+ and exec() to return \a result.
+
+ \sa QDialog::done()
+*/
+void QFontDialog::done(int result)
+{
+ Q_D(QFontDialog);
+ QDialog::done(result);
+ if (result == Accepted) {
+ d->selectedFont = currentFont();
+ emit fontSelected(d->selectedFont);
+ } else {
+ d->selectedFont = QFont();
+ }
+ if (d->receiverToDisconnectOnClose) {
+ disconnect(this, SIGNAL(fontSelected(const QFont&)),
+ d->receiverToDisconnectOnClose, d->memberToDisconnectOnClose);
+ d->receiverToDisconnectOnClose = 0;
+ }
+ d->memberToDisconnectOnClose.clear();
+}
+
+/*!
+ \fn QFont QFontDialog::getFont(bool *ok, const QFont &initial, QWidget* parent, const char* name)
+ \since 4.5
+
+ Call getFont(\a ok, \a initial, \a parent) instead.
+
+ \warning Do not delete \a parent during the execution of the dialog.
+ If you want to do this, you should create the dialog
+ yourself using one of the QFontDialog constructors.
+
+ The \a name parameter is ignored.
+*/
+
+/*!
+ \fn QFont QFontDialog::getFont(bool *ok, QWidget* parent, const char* name)
+
+ Call getFont(\a ok, \a parent) instead.
+
+ \warning Do not delete \a parent during the execution of the dialog.
+ If you want to do this, you should create the dialog
+ yourself using one of the QFontDialog constructors.
+
+ The \a name parameter is ignored.
+*/
+
+QT_END_NAMESPACE
+
+#include "qfontdialog.moc"
+#include "moc_qfontdialog.cpp"
+
+#endif // QT_NO_FONTDIALOG
diff --git a/src/gui/dialogs/qfontdialog.h b/src/gui/dialogs/qfontdialog.h
new file mode 100644
index 0000000000..303241c664
--- /dev/null
+++ b/src/gui/dialogs/qfontdialog.h
@@ -0,0 +1,144 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QFONTDIALOG_H
+#define QFONTDIALOG_H
+
+#include <QtGui/qwindowdefs.h>
+#include <QtGui/qdialog.h>
+#include <QtGui/qfont.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+#ifndef QT_NO_FONTDIALOG
+
+class QFontDialogPrivate;
+
+class Q_GUI_EXPORT QFontDialog : public QDialog
+{
+ Q_OBJECT
+ Q_DECLARE_PRIVATE(QFontDialog)
+ Q_ENUMS(FontDialogOption)
+ Q_PROPERTY(QFont currentFont READ currentFont WRITE setCurrentFont NOTIFY currentFontChanged)
+ Q_PROPERTY(FontDialogOptions options READ options WRITE setOptions)
+
+public:
+ enum FontDialogOption {
+ NoButtons = 0x00000001,
+ DontUseNativeDialog = 0x00000002
+ };
+
+ Q_DECLARE_FLAGS(FontDialogOptions, FontDialogOption)
+
+ explicit QFontDialog(QWidget *parent = 0);
+ explicit QFontDialog(const QFont &initial, QWidget *parent = 0);
+ ~QFontDialog();
+
+ void setCurrentFont(const QFont &font);
+ QFont currentFont() const;
+
+ QFont selectedFont() const;
+
+ void setOption(FontDialogOption option, bool on = true);
+ bool testOption(FontDialogOption option) const;
+ void setOptions(FontDialogOptions options);
+ FontDialogOptions options() const;
+
+#ifdef Q_NO_USING_KEYWORD
+#ifndef Q_QDOC
+ void open() { QDialog::open(); }
+#endif
+#else
+ using QDialog::open;
+#endif
+ void open(QObject *receiver, const char *member);
+
+ void setVisible(bool visible);
+
+ // ### Qt 5: merge overloads
+ static QFont getFont(bool *ok, const QFont &initial, QWidget *parent, const QString &title,
+ FontDialogOptions options);
+ static QFont getFont(bool *ok, const QFont &initial, QWidget *parent, const QString &title);
+ static QFont getFont(bool *ok, const QFont &initial, QWidget *parent = 0);
+ static QFont getFont(bool *ok, QWidget *parent = 0);
+
+#ifdef QT3_SUPPORT
+ static QFont getFont(bool *ok, const QFont &initial, QWidget *parent, const char *name)
+ { Q_UNUSED(name); return getFont(ok, initial, parent); }
+ static QFont getFont(bool *ok, QWidget *parent, const char *name)
+ { Q_UNUSED(name); return getFont(ok, parent); }
+#endif
+
+Q_SIGNALS:
+ void currentFontChanged(const QFont &font);
+ void fontSelected(const QFont &font);
+
+protected:
+ void changeEvent(QEvent *event);
+ void done(int result);
+
+private:
+ // ### Qt 5: make protected
+ bool eventFilter(QObject *object, QEvent *event);
+
+ Q_DISABLE_COPY(QFontDialog)
+
+ Q_PRIVATE_SLOT(d_func(), void _q_sizeChanged(const QString &))
+ Q_PRIVATE_SLOT(d_func(), void _q_familyHighlighted(int))
+ Q_PRIVATE_SLOT(d_func(), void _q_writingSystemHighlighted(int))
+ Q_PRIVATE_SLOT(d_func(), void _q_styleHighlighted(int))
+ Q_PRIVATE_SLOT(d_func(), void _q_sizeHighlighted(int))
+ Q_PRIVATE_SLOT(d_func(), void _q_updateSample())
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(QFontDialog::FontDialogOptions)
+
+#endif // QT_NO_FONTDIALOG
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QFONTDIALOG_H
diff --git a/src/gui/dialogs/qfontdialog_mac.mm b/src/gui/dialogs/qfontdialog_mac.mm
new file mode 100644
index 0000000000..d6ddfa3567
--- /dev/null
+++ b/src/gui/dialogs/qfontdialog_mac.mm
@@ -0,0 +1,625 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qfontdialog_p.h"
+#if !defined(QT_NO_FONTDIALOG) && defined(Q_WS_MAC)
+#include <qapplication.h>
+#include <qdialogbuttonbox.h>
+#include <qlineedit.h>
+#include <private/qapplication_p.h>
+#include <private/qfont_p.h>
+#include <private/qfontengine_p.h>
+#include <private/qt_mac_p.h>
+#include <qdebug.h>
+#import <AppKit/AppKit.h>
+#import <Foundation/Foundation.h>
+
+#if !CGFLOAT_DEFINED
+typedef float CGFloat; // Should only not be defined on 32-bit platforms
+#endif
+
+QT_USE_NAMESPACE
+
+// should a priori be kept in sync with qcolordialog_mac.mm
+const CGFloat ButtonMinWidth = 78.0;
+const CGFloat ButtonMinHeight = 32.0;
+const CGFloat ButtonSpacing = 0.0;
+const CGFloat ButtonTopMargin = 0.0;
+const CGFloat ButtonBottomMargin = 7.0;
+const CGFloat ButtonSideMargin = 9.0;
+
+// looks better with some margins
+const CGFloat DialogTopMargin = 7.0;
+const CGFloat DialogSideMargin = 9.0;
+
+const int StyleMask = NSTitledWindowMask | NSClosableWindowMask | NSResizableWindowMask;
+
+@class QCocoaFontPanelDelegate;
+
+
+#if MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_5
+
+@protocol NSWindowDelegate <NSObject> @end
+
+#endif
+
+@interface QCocoaFontPanelDelegate : NSObject <NSWindowDelegate> {
+ NSFontPanel *mFontPanel;
+ NSView *mStolenContentView;
+ NSButton *mOkButton;
+ NSButton *mCancelButton;
+ QFontDialogPrivate *mPriv;
+ NSFont *mCocoaFont;
+ QFont *mQtFont;
+ BOOL mPanelHackedWithButtons;
+ CGFloat mDialogExtraWidth;
+ CGFloat mDialogExtraHeight;
+ NSModalSession mModalSession;
+}
+- (id)initWithFontPanel:(NSFontPanel *)panel
+ stolenContentView:(NSView *)stolenContentView
+ okButton:(NSButton *)okButton
+ cancelButton:(NSButton *)cancelButton
+ priv:(QFontDialogPrivate *)priv
+ extraWidth:(CGFloat)extraWidth
+ extraHeight:(CGFloat)extraHeight;
+- (void)changeFont:(id)sender;
+- (void)changeAttributes:(id)sender;
+- (void)setModalSession:(NSModalSession)session;
+- (BOOL)windowShouldClose:(id)window;
+- (NSSize)windowWillResize:(NSWindow *)window toSize:(NSSize)proposedFrameSize;
+- (void)relayout;
+- (void)relayoutToContentSize:(NSSize)frameSize;
+- (void)onOkClicked;
+- (void)onCancelClicked;
+- (NSFontPanel *)fontPanel;
+- (NSWindow *)actualPanel;
+- (NSSize)dialogExtraSize;
+- (void)setQtFont:(const QFont &)newFont;
+- (QFont)qtFont;
+- (void)finishOffWithCode:(NSInteger)result;
+- (void)cleanUpAfterMyself;
+@end
+
+@implementation QCocoaFontPanelDelegate
+- (id)initWithFontPanel:(NSFontPanel *)panel
+ stolenContentView:(NSView *)stolenContentView
+ okButton:(NSButton *)okButton
+ cancelButton:(NSButton *)cancelButton
+ priv:(QFontDialogPrivate *)priv
+ extraWidth:(CGFloat)extraWidth
+ extraHeight:(CGFloat)extraHeight
+{
+ self = [super init];
+ mFontPanel = panel;
+ mStolenContentView = stolenContentView;
+ mOkButton = okButton;
+ mCancelButton = cancelButton;
+ mPriv = priv;
+ mCocoaFont = 0;
+ mPanelHackedWithButtons = (okButton != 0);
+ mDialogExtraWidth = extraWidth;
+ mDialogExtraHeight = extraHeight;
+ mModalSession = 0;
+
+ if (mPanelHackedWithButtons) {
+ [self relayout];
+
+ [okButton setAction:@selector(onOkClicked)];
+ [okButton setTarget:self];
+
+ [cancelButton setAction:@selector(onCancelClicked)];
+ [cancelButton setTarget:self];
+ }
+ mQtFont = new QFont();
+ return self;
+}
+
+- (void)dealloc
+{
+ if (mCocoaFont)
+ [mCocoaFont release];
+ delete mQtFont;
+ [super dealloc];
+}
+
+- (void)changeFont:(id)sender
+{
+ Q_UNUSED(sender);
+
+ QFont newFont;
+
+ if (mCocoaFont)
+ [mCocoaFont autorelease];
+ NSFont *dummyFont = [NSFont userFontOfSize:12.0];
+ mCocoaFont = [sender convertFont:dummyFont];
+ if (mCocoaFont) {
+ [mCocoaFont retain];
+
+ int pSize = qRound([mCocoaFont pointSize]);
+ QString family(QCFString::toQString(reinterpret_cast<CFStringRef>([mCocoaFont familyName])));
+ QString typeface(QCFString::toQString(reinterpret_cast<CFStringRef>([mCocoaFont fontName])));
+// qDebug() << "original family" << family << "typeface" << typeface << "psize" << pSize;
+ int hyphenPos = typeface.indexOf(QLatin1Char('-'));
+ if (hyphenPos != -1) {
+ typeface.remove(0, hyphenPos + 1);
+ } else {
+ typeface = QLatin1String("Normal");
+ }
+// qDebug() << " massaged family" << family << "typeface" << typeface << "psize" << pSize;
+ newFont = QFontDatabase().font(family, typeface, pSize);
+ newFont.setUnderline(mQtFont->underline());
+ newFont.setStrikeOut(mQtFont->strikeOut());
+ }
+
+ [self setQtFont:newFont];
+ if (mPriv)
+ mPriv->updateSampleFont(*mQtFont);
+}
+
+- (void)changeAttributes:(id)sender
+{
+ NSDictionary *dummyAttribs = [NSDictionary dictionary];
+ NSDictionary *attribs = [sender convertAttributes:dummyAttribs];
+
+#ifdef QT_MAC_USE_COCOA
+ for (id key in attribs) {
+#else
+ NSEnumerator *enumerator = [attribs keyEnumerator];
+ id key;
+ while((key = [enumerator nextObject])) {
+#endif
+ NSNumber *number = static_cast<NSNumber *>([attribs objectForKey:key]);
+ if ([key isEqual:NSUnderlineStyleAttributeName]) {
+ mQtFont->setUnderline([number intValue] != NSUnderlineStyleNone);
+ } else if ([key isEqual:NSStrikethroughStyleAttributeName]) {
+ mQtFont->setStrikeOut([number intValue] != NSUnderlineStyleNone);
+ }
+ }
+
+ if (mPriv)
+ mPriv->updateSampleFont(*mQtFont);
+}
+
+- (void)setModalSession:(NSModalSession)session
+{
+ Q_ASSERT(!mModalSession);
+ mModalSession = session;
+}
+
+- (BOOL)windowShouldClose:(id)window
+{
+ Q_UNUSED(window);
+ if (mPanelHackedWithButtons) {
+ [self onCancelClicked];
+ } else {
+ [self finishOffWithCode:NSCancelButton];
+ }
+ return true;
+}
+
+- (NSSize)windowWillResize:(NSWindow *)window toSize:(NSSize)proposedFrameSize
+{
+ if (mFontPanel == window) {
+ proposedFrameSize = [static_cast<id <NSWindowDelegate> >(mFontPanel) windowWillResize:mFontPanel toSize:proposedFrameSize];
+ } else {
+ /*
+ Ugly hack: NSFontPanel rearranges the layout of its main
+ component in windowWillResize:toSize:. So we temporarily
+ restore the stolen content view to its rightful owner,
+ call windowWillResize:toSize:, and steal the content view
+ again.
+ */
+ [mStolenContentView removeFromSuperview];
+ [mFontPanel setContentView:mStolenContentView];
+ NSSize extraSize = [self dialogExtraSize];
+ proposedFrameSize.width -= extraSize.width;
+ proposedFrameSize.height -= extraSize.height;
+ proposedFrameSize = [static_cast<id <NSWindowDelegate> >(mFontPanel) windowWillResize:mFontPanel toSize:proposedFrameSize];
+ NSRect frameRect = { { 0.0, 0.0 }, proposedFrameSize };
+ [mFontPanel setFrame:frameRect display:NO];
+ [mFontPanel setContentView:0];
+ [[window contentView] addSubview:mStolenContentView];
+ proposedFrameSize.width += extraSize.width;
+ proposedFrameSize.height += extraSize.height;
+ }
+ if (mPanelHackedWithButtons) {
+ NSRect frameRect = { { 0.0, 0.0 }, proposedFrameSize };
+ NSRect contentRect = [NSWindow contentRectForFrameRect:frameRect styleMask:[window styleMask]];
+ [self relayoutToContentSize:contentRect.size];
+ }
+ return proposedFrameSize;
+}
+
+- (void)relayout
+{
+ [self relayoutToContentSize:[[mStolenContentView superview] frame].size];
+}
+
+- (void)relayoutToContentSize:(NSSize)frameSize;
+{
+ Q_ASSERT(mPanelHackedWithButtons);
+
+ [mOkButton sizeToFit];
+ NSSize okSizeHint = [mOkButton frame].size;
+
+ [mCancelButton sizeToFit];
+ NSSize cancelSizeHint = [mCancelButton frame].size;
+
+ const CGFloat ButtonWidth = qMin(qMax(ButtonMinWidth,
+ qMax(okSizeHint.width, cancelSizeHint.width)),
+ CGFloat((frameSize.width - 2.0 * ButtonSideMargin
+ - ButtonSpacing) * 0.5));
+ const CGFloat ButtonHeight = qMax(ButtonMinHeight,
+ qMax(okSizeHint.height, cancelSizeHint.height));
+
+ const CGFloat X = DialogSideMargin;
+ const CGFloat Y = ButtonBottomMargin + ButtonHeight + ButtonTopMargin;
+
+ NSRect okRect = { { frameSize.width - ButtonSideMargin - ButtonWidth,
+ ButtonBottomMargin },
+ { ButtonWidth, ButtonHeight } };
+ [mOkButton setFrame:okRect];
+ [mOkButton setNeedsDisplay:YES];
+
+ NSRect cancelRect = { { okRect.origin.x - ButtonSpacing - ButtonWidth,
+ ButtonBottomMargin },
+ { ButtonWidth, ButtonHeight } };
+ [mCancelButton setFrame:cancelRect];
+ [mCancelButton setNeedsDisplay:YES];
+
+ NSRect stolenCVRect = { { X, Y },
+ { frameSize.width - X - X, frameSize.height - Y - DialogTopMargin } };
+ [mStolenContentView setFrame:stolenCVRect];
+ [mStolenContentView setNeedsDisplay:YES];
+
+ [[mStolenContentView superview] setNeedsDisplay:YES];
+}
+
+- (void)onOkClicked
+{
+ Q_ASSERT(mPanelHackedWithButtons);
+ [[mStolenContentView window] close];
+ [self finishOffWithCode:NSOKButton];
+}
+
+- (void)onCancelClicked
+{
+ Q_ASSERT(mPanelHackedWithButtons);
+ [[mStolenContentView window] close];
+ [self finishOffWithCode:NSCancelButton];
+}
+
+- (NSFontPanel *)fontPanel
+{
+ return mFontPanel;
+}
+
+- (NSWindow *)actualPanel
+{
+ return [mStolenContentView window];
+}
+
+- (NSSize)dialogExtraSize
+{
+ // this must be recomputed each time, because sometimes the
+ // NSFontPanel has the NSDocModalWindowMask flag set, and sometimes
+ // not -- which affects the frame rect vs. content rect measurements
+
+ // take the different frame rectangles into account for dialogExtra{Width,Height}
+ NSRect someRect = { { 0.0, 0.0 }, { 100000.0, 100000.0 } };
+ NSRect sharedFontPanelContentRect = [mFontPanel contentRectForFrameRect:someRect];
+ NSRect ourPanelContentRect = [NSWindow contentRectForFrameRect:someRect styleMask:StyleMask];
+
+ NSSize result = { mDialogExtraWidth, mDialogExtraHeight };
+ result.width -= ourPanelContentRect.size.width - sharedFontPanelContentRect.size.width;
+ result.height -= ourPanelContentRect.size.height - sharedFontPanelContentRect.size.height;
+ return result;
+}
+
+- (void)setQtFont:(const QFont &)newFont
+{
+ delete mQtFont;
+ mQtFont = new QFont(newFont);
+}
+
+- (QFont)qtFont
+{
+ return *mQtFont;
+}
+
+- (void)finishOffWithCode:(NSInteger)code
+{
+ if (mPriv) {
+ if (mModalSession) {
+ [NSApp endModalSession:mModalSession];
+ mModalSession = 0;
+ }
+
+ // temporary hack to work around bug in deleteLater() in Qt/Mac Cocoa
+#if 1
+ bool deleteDialog = mPriv->fontDialog()->testAttribute(Qt::WA_DeleteOnClose);
+ mPriv->fontDialog()->setAttribute(Qt::WA_DeleteOnClose, false);
+#endif
+ mPriv->done((code == NSOKButton) ? QDialog::Accepted : QDialog::Rejected);
+#if 1
+ if (deleteDialog)
+ delete mPriv->fontDialog();
+#endif
+ } else {
+ [NSApp stopModalWithCode:code];
+ }
+}
+
+- (void)cleanUpAfterMyself
+{
+ if (mPanelHackedWithButtons) {
+ NSView *ourContentView = [mFontPanel contentView];
+
+ // return stolen stuff to its rightful owner
+ [mStolenContentView removeFromSuperview];
+ [mFontPanel setContentView:mStolenContentView];
+
+ [mOkButton release];
+ [mCancelButton release];
+ [ourContentView release];
+ }
+ [mFontPanel setDelegate:nil];
+ [[NSFontManager sharedFontManager] setDelegate:nil];
+}
+@end
+
+QT_BEGIN_NAMESPACE
+
+extern void macStartInterceptNSPanelCtor();
+extern void macStopInterceptNSPanelCtor();
+extern NSButton *macCreateButton(const char *text, NSView *superview);
+
+void *QFontDialogPrivate::openCocoaFontPanel(const QFont &initial,
+ QWidget *parent, const QString &title, QFontDialog::FontDialogOptions options,
+ QFontDialogPrivate *priv)
+{
+ Q_UNUSED(parent); // we would use the parent if only NSFontPanel could be a sheet
+ QMacCocoaAutoReleasePool pool;
+
+ /*
+ The standard Cocoa font panel has no OK or Cancel button and
+ is created as a utility window. For strange reasons (which seem
+ to stem from the fact that the font panel is based on a NIB
+ file), the approach we use for the color panel doesn't work for
+ the font panel (and, inversely, the approach we use here doesn't
+ quite work for color panel, and crashed last time I tried). So
+ instead, we take the following steps:
+
+ 1. Constructs a plain NSPanel that looks the way we want it
+ to look. Specifically, if the NoButtons option is off, we
+ construct a panel without the NSUtilityWindowMask flag
+ and with buttons (OK and Cancel).
+
+ 2. Steal the content view from the shared NSFontPanel and
+ put it inside our new NSPanel's content view, together
+ with the OK and Cancel buttons.
+
+ 3. Lay out the original content view and the buttons when
+ the font panel is shown and whenever it is resized.
+
+ 4. Clean up after ourselves.
+
+ PS. Some customization is also done in QCocoaApplication
+ validModesForFontPanel:.
+ */
+
+ Qt::WindowModality modality = Qt::ApplicationModal;
+ if (priv)
+ modality = priv->fontDialog()->windowModality();
+
+ bool needButtons = !(options & QFontDialog::NoButtons);
+ // don't need our own panel if the title bar isn't visible anyway (in a sheet)
+ bool needOwnPanel = (needButtons && modality != Qt::WindowModal);
+
+ bool sharedFontPanelExisted = [NSFontPanel sharedFontPanelExists];
+ NSFontPanel *sharedFontPanel = [NSFontPanel sharedFontPanel];
+ [sharedFontPanel setHidesOnDeactivate:false];
+
+ // hack to ensure that QCocoaApplication's validModesForFontPanel:
+ // implementation is honored
+ if (!sharedFontPanelExisted && needOwnPanel) {
+ [sharedFontPanel makeKeyAndOrderFront:sharedFontPanel];
+ [sharedFontPanel close];
+ }
+
+ NSPanel *ourPanel = 0;
+ NSView *stolenContentView = 0;
+ NSButton *okButton = 0;
+ NSButton *cancelButton = 0;
+
+ CGFloat dialogExtraWidth = 0.0;
+ CGFloat dialogExtraHeight = 0.0;
+
+ if (!needOwnPanel) {
+ // we can reuse the NSFontPanel unchanged
+ ourPanel = sharedFontPanel;
+ } else {
+ // compute dialogExtra{Width,Height}
+ dialogExtraWidth = 2.0 * DialogSideMargin;
+ dialogExtraHeight = DialogTopMargin + ButtonTopMargin + ButtonMinHeight
+ + ButtonBottomMargin;
+
+ // compute initial contents rectangle
+ NSRect contentRect = [sharedFontPanel contentRectForFrameRect:[sharedFontPanel frame]];
+ contentRect.size.width += dialogExtraWidth;
+ contentRect.size.height += dialogExtraHeight;
+
+ // create the new panel
+ ourPanel = [[NSPanel alloc] initWithContentRect:contentRect
+ styleMask:StyleMask
+ backing:NSBackingStoreBuffered
+ defer:YES];
+ [ourPanel setReleasedWhenClosed:YES];
+ }
+
+ stolenContentView = [sharedFontPanel contentView];
+
+ if (needButtons) {
+ // steal the font panel's contents view
+ [stolenContentView retain];
+ [sharedFontPanel setContentView:0];
+
+ // create a new content view and add the stolen one as a subview
+ NSRect frameRect = { { 0.0, 0.0 }, { 0.0, 0.0 } };
+ NSView *ourContentView = [[NSView alloc] initWithFrame:frameRect];
+ [ourContentView addSubview:stolenContentView];
+
+ // create OK and Cancel buttons and add these as subviews
+ okButton = macCreateButton("&OK", ourContentView);
+ cancelButton = macCreateButton("Cancel", ourContentView);
+
+ [ourPanel setContentView:ourContentView];
+ [ourPanel setDefaultButtonCell:[okButton cell]];
+ }
+
+ // create a delegate and set it
+ QCocoaFontPanelDelegate *delegate =
+ [[QCocoaFontPanelDelegate alloc] initWithFontPanel:sharedFontPanel
+ stolenContentView:stolenContentView
+ okButton:okButton
+ cancelButton:cancelButton
+ priv:priv
+ extraWidth:dialogExtraWidth
+ extraHeight:dialogExtraHeight];
+ [ourPanel setDelegate:delegate];
+ [[NSFontManager sharedFontManager] setDelegate:delegate];
+ setFont(delegate, initial);
+
+ // hack to get correct initial layout
+ NSRect frameRect = [ourPanel frame];
+ frameRect.size.width += 1.0;
+ [ourPanel setFrame:frameRect display:NO];
+ frameRect.size.width -= 1.0;
+ frameRect.size = [delegate windowWillResize:ourPanel toSize:frameRect.size];
+ [ourPanel setFrame:frameRect display:NO];
+ [ourPanel center];
+
+ [ourPanel setTitle:(NSString*)(CFStringRef)QCFString(title)];
+
+ if (priv) {
+ switch (modality) {
+ case Qt::WindowModal:
+ if (parent) {
+#ifndef QT_MAC_USE_COCOA
+ WindowRef hiwindowRef = qt_mac_window_for(parent);
+ NSWindow *window =
+ [[NSWindow alloc] initWithWindowRef:hiwindowRef];
+ // Cocoa docs say I should retain the Carbon ref.
+ CFRetain(hiwindowRef);
+#else
+ NSWindow *window = qt_mac_window_for(parent);
+#endif
+ [NSApp beginSheet:ourPanel
+ modalForWindow:window
+ modalDelegate:0
+ didEndSelector:0
+ contextInfo:0];
+#ifndef QT_MAC_USE_COCOA
+ [window release];
+#endif
+ break;
+ }
+ // fallthrough
+ case Qt::ApplicationModal:
+ [delegate setModalSession:[NSApp beginModalSessionForWindow:ourPanel]];
+ break;
+ default:
+ [ourPanel makeKeyAndOrderFront:ourPanel];
+ }
+ }
+
+ return delegate;
+}
+
+void QFontDialogPrivate::closeCocoaFontPanel(void *delegate)
+{
+ QCocoaFontPanelDelegate *theDelegate = static_cast<QCocoaFontPanelDelegate *>(delegate);
+ NSWindow *ourPanel = [theDelegate actualPanel];
+ [ourPanel close];
+ [theDelegate cleanUpAfterMyself];
+ [theDelegate autorelease];
+}
+
+QFont QFontDialogPrivate::execCocoaFontPanel(bool *ok, const QFont &initial,
+ QWidget *parent, const QString &title, QFontDialog::FontDialogOptions options)
+{
+ QMacCocoaAutoReleasePool pool;
+ QCocoaFontPanelDelegate *delegate =
+ static_cast<QCocoaFontPanelDelegate *>(
+ openCocoaFontPanel(initial, parent, title, options));
+ NSWindow *ourPanel = [delegate actualPanel];
+ [ourPanel retain];
+ int rval = [NSApp runModalForWindow:ourPanel];
+ QFont font([delegate qtFont]);
+ [ourPanel release];
+ [delegate cleanUpAfterMyself];
+ [delegate release];
+ bool isOk = ((options & QFontDialog::NoButtons) || rval == NSOKButton);
+ if (ok)
+ *ok = isOk;
+ if (isOk) {
+ return font;
+ } else {
+ return initial;
+ }
+}
+
+void QFontDialogPrivate::setFont(void * delegate, const QFont &font)
+{
+ QFontEngine *fe = font.d->engineForScript(QUnicodeTables::Common);
+#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5
+ if (qstrcmp(fe->name(), "CoreText") == 0) {
+ const NSFont *nsFont = reinterpret_cast<const NSFont *>(static_cast<QCoreTextFontEngineMulti *>(fe)->ctfont);
+ [[NSFontManager sharedFontManager] setSelectedFont:nsFont isMultiple:NO];
+ }
+#endif
+ [static_cast<QCocoaFontPanelDelegate *>(delegate) setQtFont:font];
+}
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/gui/dialogs/qfontdialog_p.h b/src/gui/dialogs/qfontdialog_p.h
new file mode 100644
index 0000000000..3a69949d80
--- /dev/null
+++ b/src/gui/dialogs/qfontdialog_p.h
@@ -0,0 +1,164 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QFONTDIALOG_P_H
+#define QFONTDIALOG_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/qdialog_p.h"
+#include "qfontdatabase.h"
+#include "qfontdialog.h"
+
+#ifndef QT_NO_FONTDIALOG
+
+QT_BEGIN_NAMESPACE
+
+class QBoxLayout;
+class QCheckBox;
+class QComboBox;
+class QDialogButtonBox;
+class QFontListView;
+class QGroupBox;
+class QLabel;
+class QLineEdit;
+
+class QFontDialogPrivate : public QDialogPrivate
+{
+ Q_DECLARE_PUBLIC(QFontDialog)
+
+public:
+ inline QFontDialogPrivate()
+ : writingSystem(QFontDatabase::Any)
+ { }
+
+ void updateFamilies();
+ void updateStyles();
+ void updateSizes();
+
+ static QFont getFont(bool *ok, const QFont &initial, QWidget *parent,
+ const QString &title, QFontDialog::FontDialogOptions options);
+
+ void init();
+ void _q_sizeChanged(const QString &);
+ void _q_familyHighlighted(int);
+ void _q_writingSystemHighlighted(int);
+ void _q_styleHighlighted(int);
+ void _q_sizeHighlighted(int);
+ void _q_updateSample();
+ void updateSampleFont(const QFont &newFont);
+ void retranslateStrings();
+
+ QLabel *familyAccel;
+ QLineEdit *familyEdit;
+ QFontListView *familyList;
+
+ QLabel *styleAccel;
+ QLineEdit *styleEdit;
+ QFontListView *styleList;
+
+ QLabel *sizeAccel;
+ QLineEdit *sizeEdit;
+ QFontListView *sizeList;
+
+ QGroupBox *effects;
+ QCheckBox *strikeout;
+ QCheckBox *underline;
+ QComboBox *color;
+
+ QGroupBox *sample;
+ QLineEdit *sampleEdit;
+
+ QLabel *writingSystemAccel;
+ QComboBox *writingSystemCombo;
+
+ QBoxLayout *buttonLayout;
+ QBoxLayout *effectsLayout;
+ QBoxLayout *sampleLayout;
+ QBoxLayout *sampleEditLayout;
+
+ QDialogButtonBox *buttonBox;
+
+ QFontDatabase fdb;
+ QString family;
+ QFontDatabase::WritingSystem writingSystem;
+ QString style;
+ int size;
+ bool smoothScalable;
+ QFont selectedFont;
+ QFontDialog::FontDialogOptions opts;
+ QPointer<QObject> receiverToDisconnectOnClose;
+ QByteArray memberToDisconnectOnClose;
+
+#ifdef Q_WS_MAC
+ static void *openCocoaFontPanel(const QFont &initial,
+ QWidget *parent, const QString &title,
+ QFontDialog::FontDialogOptions options,
+ QFontDialogPrivate *priv = 0);
+ static void closeCocoaFontPanel(void *delegate);
+ static QFont execCocoaFontPanel(bool *ok, const QFont &initial, QWidget *parent,
+ const QString &title, QFontDialog::FontDialogOptions options);
+ static void setFont(void *delegate, const QFont &font);
+
+ inline void done(int result) { q_func()->done(result); }
+ inline QFontDialog *fontDialog() { return q_func(); }
+
+ void *delegate;
+
+ static bool sharedFontPanelAvailable;
+#endif
+};
+
+#endif // QT_NO_FONTDIALOG
+
+QT_END_NAMESPACE
+
+#endif // QFONTDIALOG_P_H
diff --git a/src/gui/dialogs/qinputdialog.cpp b/src/gui/dialogs/qinputdialog.cpp
new file mode 100644
index 0000000000..b63c8ee446
--- /dev/null
+++ b/src/gui/dialogs/qinputdialog.cpp
@@ -0,0 +1,1429 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qinputdialog.h"
+
+#ifndef QT_NO_INPUTDIALOG
+
+#include "qapplication.h"
+#include "qcombobox.h"
+#include "qdialogbuttonbox.h"
+#include "qlabel.h"
+#include "qlayout.h"
+#include "qlineedit.h"
+#include "qlistwidget.h"
+#include "qpushbutton.h"
+#include "qspinbox.h"
+#include "qstackedlayout.h"
+#include "qvalidator.h"
+#include "qevent.h"
+#include "qdialog_p.h"
+
+QT_USE_NAMESPACE
+
+static const char *signalForMember(const char *member)
+{
+ static const int NumCandidates = 4;
+ static const char * const candidateSignals[NumCandidates] = {
+ SIGNAL(textValueSelected(QString)),
+ SIGNAL(intValueSelected(int)),
+ SIGNAL(doubleValueSelected(double)),
+ SIGNAL(accepted())
+ };
+
+ QByteArray normalizedMember(QMetaObject::normalizedSignature(member));
+
+ int i = 0;
+ while (i < NumCandidates - 1) { // sic
+ if (QMetaObject::checkConnectArgs(candidateSignals[i], normalizedMember))
+ break;
+ ++i;
+ }
+ return candidateSignals[i];
+}
+
+QT_BEGIN_NAMESPACE
+
+/*
+ These internal classes add extra validation to QSpinBox and QDoubleSpinBox by emitting
+ textChanged(bool) after events that may potentially change the visible text. Return or
+ Enter key presses are not propagated if the visible text is invalid. Instead, the visible
+ text is modified to the last valid value.
+*/
+class QInputDialogSpinBox : public QSpinBox
+{
+ Q_OBJECT
+
+public:
+ QInputDialogSpinBox(QWidget *parent)
+ : QSpinBox(parent) {
+ connect(lineEdit(), SIGNAL(textChanged(const QString&)), this, SLOT(notifyTextChanged()));
+ connect(this, SIGNAL(editingFinished()), this, SLOT(notifyTextChanged()));
+ }
+
+signals:
+ void textChanged(bool);
+
+private slots:
+ void notifyTextChanged() { emit textChanged(hasAcceptableInput()); }
+
+private:
+ void keyPressEvent(QKeyEvent *event) {
+ if ((event->key() == Qt::Key_Return || event->key() == Qt::Key_Enter) && !hasAcceptableInput()) {
+#ifndef QT_NO_PROPERTIES
+ setProperty("value", property("value"));
+#endif
+ } else {
+ QSpinBox::keyPressEvent(event);
+ }
+ notifyTextChanged();
+ }
+
+ void mousePressEvent(QMouseEvent *event) {
+ QSpinBox::mousePressEvent(event);
+ notifyTextChanged();
+ }
+};
+
+class QInputDialogDoubleSpinBox : public QDoubleSpinBox
+{
+ Q_OBJECT
+
+public:
+ QInputDialogDoubleSpinBox(QWidget *parent = 0)
+ : QDoubleSpinBox(parent) {
+ connect(lineEdit(), SIGNAL(textChanged(const QString&)), this, SLOT(notifyTextChanged()));
+ connect(this, SIGNAL(editingFinished()), this, SLOT(notifyTextChanged()));
+ }
+
+signals:
+ void textChanged(bool);
+
+private slots:
+ void notifyTextChanged() { emit textChanged(hasAcceptableInput()); }
+
+private:
+ void keyPressEvent(QKeyEvent *event) {
+ if ((event->key() == Qt::Key_Return || event->key() == Qt::Key_Enter) && !hasAcceptableInput()) {
+#ifndef QT_NO_PROPERTIES
+ setProperty("value", property("value"));
+#endif
+ } else {
+ QDoubleSpinBox::keyPressEvent(event);
+ }
+ notifyTextChanged();
+ }
+
+ void mousePressEvent(QMouseEvent *event) {
+ QDoubleSpinBox::mousePressEvent(event);
+ notifyTextChanged();
+ }
+};
+
+QT_BEGIN_INCLUDE_NAMESPACE
+#include "qinputdialog.moc"
+QT_END_INCLUDE_NAMESPACE
+
+class QInputDialogPrivate : public QDialogPrivate
+{
+ Q_DECLARE_PUBLIC(QInputDialog)
+
+public:
+ QInputDialogPrivate();
+
+ void ensureLayout();
+ void ensureLineEdit();
+ void ensureComboBox();
+ void ensureListView();
+ void ensureIntSpinBox();
+ void ensureDoubleSpinBox();
+ void ensureEnabledConnection(QAbstractSpinBox *spinBox);
+ void setInputWidget(QWidget *widget);
+ void chooseRightTextInputWidget();
+ void setComboBoxText(const QString &text);
+ void setListViewText(const QString &text);
+ QString listViewText() const;
+ void ensureLayout() const { const_cast<QInputDialogPrivate *>(this)->ensureLayout(); }
+ bool useComboBoxOrListView() const { return comboBox && comboBox->count() > 0; }
+ void _q_textChanged(const QString &text);
+ void _q_currentRowChanged(const QModelIndex &newIndex, const QModelIndex &oldIndex);
+
+ mutable QLabel *label;
+ mutable QDialogButtonBox *buttonBox;
+ mutable QLineEdit *lineEdit;
+ mutable QSpinBox *intSpinBox;
+ mutable QDoubleSpinBox *doubleSpinBox;
+ mutable QComboBox *comboBox;
+ mutable QListView *listView;
+ mutable QWidget *inputWidget;
+ mutable QVBoxLayout *mainLayout;
+ QInputDialog::InputDialogOptions opts;
+ QString textValue;
+ QPointer<QObject> receiverToDisconnectOnClose;
+ QByteArray memberToDisconnectOnClose;
+};
+
+QInputDialogPrivate::QInputDialogPrivate()
+ : label(0), buttonBox(0), lineEdit(0), intSpinBox(0), doubleSpinBox(0), comboBox(0), listView(0),
+ inputWidget(0), mainLayout(0)
+{
+}
+
+void QInputDialogPrivate::ensureLayout()
+{
+ Q_Q(QInputDialog);
+
+ if (mainLayout)
+ return;
+
+ if (!inputWidget) {
+ ensureLineEdit();
+ inputWidget = lineEdit;
+ }
+
+ if (!label)
+ label = new QLabel(QInputDialog::tr("Enter a value:"), q);
+#ifndef QT_NO_SHORTCUT
+ label->setBuddy(inputWidget);
+#endif
+ label->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed);
+
+ buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, Qt::Horizontal, q);
+ QObject::connect(buttonBox, SIGNAL(accepted()), q, SLOT(accept()));
+ QObject::connect(buttonBox, SIGNAL(rejected()), q, SLOT(reject()));
+
+ mainLayout = new QVBoxLayout(q);
+ mainLayout->setSizeConstraint(QLayout::SetMinAndMaxSize);
+ mainLayout->addWidget(label);
+ mainLayout->addWidget(inputWidget);
+ mainLayout->addWidget(buttonBox);
+ ensureEnabledConnection(qobject_cast<QAbstractSpinBox *>(inputWidget));
+ inputWidget->show();
+}
+
+void QInputDialogPrivate::ensureLineEdit()
+{
+ Q_Q(QInputDialog);
+ if (!lineEdit) {
+ lineEdit = new QLineEdit(q);
+ lineEdit->hide();
+ QObject::connect(lineEdit, SIGNAL(textChanged(const QString&)),
+ q, SLOT(_q_textChanged(const QString&)));
+ }
+}
+
+void QInputDialogPrivate::ensureComboBox()
+{
+ Q_Q(QInputDialog);
+ if (!comboBox) {
+ comboBox = new QComboBox(q);
+ comboBox->hide();
+ QObject::connect(comboBox, SIGNAL(editTextChanged(const QString&)),
+ q, SLOT(_q_textChanged(const QString&)));
+ QObject::connect(comboBox, SIGNAL(currentIndexChanged(const QString&)),
+ q, SLOT(_q_textChanged(const QString&)));
+ }
+}
+
+void QInputDialogPrivate::ensureListView()
+{
+ Q_Q(QInputDialog);
+ if (!listView) {
+ ensureComboBox();
+
+ listView = new QListView(q);
+ listView->hide();
+ listView->setEditTriggers(QAbstractItemView::NoEditTriggers);
+ listView->setSelectionMode(QAbstractItemView::SingleSelection);
+ listView->setModel(comboBox->model());
+ listView->setCurrentIndex(QModelIndex()); // ###
+ QObject::connect(listView->selectionModel(),
+ SIGNAL(currentRowChanged(const QModelIndex&, const QModelIndex&)),
+ q, SLOT(_q_currentRowChanged(const QModelIndex&, const QModelIndex&)));
+ }
+}
+
+void QInputDialogPrivate::ensureIntSpinBox()
+{
+ Q_Q(QInputDialog);
+ if (!intSpinBox) {
+ intSpinBox = new QInputDialogSpinBox(q);
+ intSpinBox->hide();
+ QObject::connect(intSpinBox, SIGNAL(valueChanged(int)),
+ q, SIGNAL(intValueChanged(int)));
+ }
+}
+
+void QInputDialogPrivate::ensureDoubleSpinBox()
+{
+ Q_Q(QInputDialog);
+ if (!doubleSpinBox) {
+ doubleSpinBox = new QInputDialogDoubleSpinBox(q);
+ doubleSpinBox->hide();
+ QObject::connect(doubleSpinBox, SIGNAL(valueChanged(double)),
+ q, SIGNAL(doubleValueChanged(double)));
+ }
+}
+
+void QInputDialogPrivate::ensureEnabledConnection(QAbstractSpinBox *spinBox)
+{
+ if (spinBox) {
+ QAbstractButton *okButton = buttonBox->button(QDialogButtonBox::Ok);
+ QObject::disconnect(spinBox, SIGNAL(textChanged(bool)), okButton, SLOT(setEnabled(bool)));
+ QObject::connect(spinBox, SIGNAL(textChanged(bool)), okButton, SLOT(setEnabled(bool)));
+ }
+}
+
+void QInputDialogPrivate::setInputWidget(QWidget *widget)
+{
+ Q_ASSERT(widget);
+ if (inputWidget == widget)
+ return;
+
+ if (mainLayout) {
+ Q_ASSERT(inputWidget);
+ mainLayout->removeWidget(inputWidget);
+ inputWidget->hide();
+ mainLayout->insertWidget(1, widget);
+ widget->show();
+
+ // disconnect old input widget
+ QAbstractButton *okButton = buttonBox->button(QDialogButtonBox::Ok);
+ if (QAbstractSpinBox *spinBox = qobject_cast<QAbstractSpinBox *>(inputWidget))
+ QObject::disconnect(spinBox, SIGNAL(textChanged(bool)), okButton, SLOT(setEnabled(bool)));
+
+ // connect new input widget and update enabled state of OK button
+ QAbstractSpinBox *spinBox = qobject_cast<QAbstractSpinBox *>(widget);
+ ensureEnabledConnection(spinBox);
+ okButton->setEnabled(!spinBox || spinBox->hasAcceptableInput());
+ }
+
+ inputWidget = widget;
+
+ // synchronize the text shown in the new text editor with the current
+ // textValue
+ if (widget == lineEdit) {
+ lineEdit->setText(textValue);
+ } else if (widget == comboBox) {
+ setComboBoxText(textValue);
+ } else if (widget == listView) {
+ setListViewText(textValue);
+ ensureLayout();
+ buttonBox->button(QDialogButtonBox::Ok)->setEnabled(listView->selectionModel()->hasSelection());
+ }
+}
+
+void QInputDialogPrivate::chooseRightTextInputWidget()
+{
+ QWidget *widget;
+
+ if (useComboBoxOrListView()) {
+ if ((opts & QInputDialog::UseListViewForComboBoxItems) && !comboBox->isEditable()) {
+ ensureListView();
+ widget = listView;
+ } else {
+ widget = comboBox;
+ }
+ } else {
+ ensureLineEdit();
+ widget = lineEdit;
+ }
+
+ setInputWidget(widget);
+
+ if (inputWidget == comboBox) {
+ _q_textChanged(comboBox->currentText());
+ } else if (inputWidget == listView) {
+ _q_textChanged(listViewText());
+ }
+}
+
+void QInputDialogPrivate::setComboBoxText(const QString &text)
+{
+ int index = comboBox->findText(text);
+ if (index != -1) {
+ comboBox->setCurrentIndex(index);
+ } else if (comboBox->isEditable()) {
+ comboBox->setEditText(text);
+ }
+}
+
+void QInputDialogPrivate::setListViewText(const QString &text)
+{
+ int row = comboBox->findText(text);
+ if (row != -1) {
+ QModelIndex index(comboBox->model()->index(row, 0));
+ listView->selectionModel()->setCurrentIndex(index, QItemSelectionModel::Clear
+ | QItemSelectionModel::SelectCurrent);
+ }
+}
+
+QString QInputDialogPrivate::listViewText() const
+{
+ if (listView->selectionModel()->hasSelection()) {
+ int row = listView->selectionModel()->selectedRows().value(0).row();
+ return comboBox->itemText(row);
+ } else {
+ return QString();
+ }
+}
+
+void QInputDialogPrivate::_q_textChanged(const QString &text)
+{
+ Q_Q(QInputDialog);
+ if (textValue != text) {
+ textValue = text;
+ emit q->textValueChanged(text);
+ }
+}
+
+void QInputDialogPrivate::_q_currentRowChanged(const QModelIndex &newIndex,
+ const QModelIndex & /* oldIndex */)
+{
+ _q_textChanged(comboBox->model()->data(newIndex).toString());
+ buttonBox->button(QDialogButtonBox::Ok)->setEnabled(true);
+}
+
+/*!
+ \class QInputDialog
+ \brief The QInputDialog class provides a simple convenience dialog to get a single value from the user.
+ \ingroup dialogs
+ \mainclass
+
+ The input value can be a string, a number or an item from a list. A
+ label must be set to tell the user what they should enter.
+
+ Four static convenience functions are provided:
+ getText(), getInt(), getDouble(), and getItem(). All the
+ functions can be used in a similar way, for example:
+
+ \snippet examples/dialogs/standarddialogs/dialog.cpp 3
+
+ The \c ok variable is set to true if the user clicks \gui OK;
+ otherwise it is set to false.
+
+ \img inputdialogs.png Input Dialogs
+
+ The \l{dialogs/standarddialogs}{Standard Dialogs} example shows
+ how to use QInputDialog as well as other built-in Qt dialogs.
+
+ \sa QMessageBox, {Standard Dialogs Example}
+*/
+
+/*!
+ \enum QInputDialog::InputMode
+ \since 4.5
+
+ This enum describes the different modes of input that can be selected for the dialog.
+
+ \value TextInput Used to input text strings.
+ \value IntInput Used to input integers.
+ \value DoubleInput Used to input floating point numbers with double precision accuracy.
+
+ \sa inputMode
+*/
+
+/*!
+ \since 4.5
+
+ Constructs a new input dialog with the given \a parent and window \a flags.
+*/
+QInputDialog::QInputDialog(QWidget *parent, Qt::WindowFlags flags)
+ : QDialog(*new QInputDialogPrivate, parent, flags)
+{
+}
+
+/*!
+ \since 4.5
+
+ Destroys the input dialog.
+*/
+QInputDialog::~QInputDialog()
+{
+}
+
+/*!
+ \since 4.5
+
+ \property QInputDialog::inputMode
+
+ \brief the mode used for input
+
+ This property help determines which widget is used for entering input into the dialog.
+*/
+void QInputDialog::setInputMode(InputMode mode)
+{
+ Q_D(QInputDialog);
+
+ QWidget *widget;
+
+ /*
+ Warning: Some functions in QInputDialog rely on implementation details
+ of the code below. Look for the comments that accompany the calls to
+ setInputMode() throughout this file before you change the code below.
+ */
+
+ switch (mode) {
+ case IntInput:
+ d->ensureIntSpinBox();
+ widget = d->intSpinBox;
+ break;
+ case DoubleInput:
+ d->ensureDoubleSpinBox();
+ widget = d->doubleSpinBox;
+ break;
+ default:
+ Q_ASSERT(mode == TextInput);
+ d->chooseRightTextInputWidget();
+ return;
+ }
+
+ d->setInputWidget(widget);
+}
+
+QInputDialog::InputMode QInputDialog::inputMode() const
+{
+ Q_D(const QInputDialog);
+
+ if (d->inputWidget) {
+ if (d->inputWidget == d->intSpinBox) {
+ return IntInput;
+ } else if (d->inputWidget == d->doubleSpinBox) {
+ return DoubleInput;
+ }
+ }
+
+ return TextInput;
+}
+
+/*!
+ \since 4.5
+
+ \property QInputDialog::labelText
+
+ \brief the text to for the label to describe what needs to be input
+*/
+void QInputDialog::setLabelText(const QString &text)
+{
+ Q_D(QInputDialog);
+ if (!d->label) {
+ d->label = new QLabel(text, this);
+ } else {
+ d->label->setText(text);
+ }
+}
+
+QString QInputDialog::labelText() const
+{
+ Q_D(const QInputDialog);
+ d->ensureLayout();
+ return d->label->text();
+}
+
+/*!
+ \enum QInputDialog::InputDialogOption
+
+ \since 4.5
+
+ This enum specifies various options that affect the look and feel
+ of an input dialog.
+
+ \value NoButtons Don't display \gui{OK} and \gui{Cancel} buttons. (Useful for "live dialogs".)
+ \value UseListViewForComboBoxItems Use a QListView rather than a non-editable QComboBox for
+ displaying the items set with setComboBoxItems().
+
+ \sa options, setOption(), testOption()
+*/
+
+/*!
+ Sets the given \a option to be enabled if \a on is true;
+ otherwise, clears the given \a option.
+
+ \sa options, testOption()
+*/
+void QInputDialog::setOption(InputDialogOption option, bool on)
+{
+ Q_D(QInputDialog);
+ if (!(d->opts & option) != !on)
+ setOptions(d->opts ^ option);
+}
+
+/*!
+ Returns true if the given \a option is enabled; otherwise, returns
+ false.
+
+ \sa options, setOption()
+*/
+bool QInputDialog::testOption(InputDialogOption option) const
+{
+ Q_D(const QInputDialog);
+ return (d->opts & option) != 0;
+}
+
+/*!
+ \property QInputDialog::options
+ \brief the various options that affect the look and feel of the dialog
+ \since 4.5
+
+ By default, all options are disabled.
+
+ \sa setOption(), testOption()
+*/
+void QInputDialog::setOptions(InputDialogOptions options)
+{
+ Q_D(QInputDialog);
+
+ InputDialogOptions changed = (options ^ d->opts);
+ if (!changed)
+ return;
+
+ d->opts = options;
+ d->ensureLayout();
+
+ if (changed & NoButtons)
+ d->buttonBox->setVisible(!(options & NoButtons));
+ if ((changed & UseListViewForComboBoxItems) && inputMode() == TextInput)
+ d->chooseRightTextInputWidget();
+}
+
+QInputDialog::InputDialogOptions QInputDialog::options() const
+{
+ Q_D(const QInputDialog);
+ return d->opts;
+}
+
+/*!
+ \since 4.5
+
+ \property QInputDialog::textValue
+
+ \brief the text value for the input dialog
+
+ This property is only relevant when the input dialog is used in
+ TextInput mode.
+*/
+void QInputDialog::setTextValue(const QString &text)
+{
+ Q_D(QInputDialog);
+
+ setInputMode(TextInput);
+ if (d->inputWidget == d->lineEdit) {
+ d->lineEdit->setText(text);
+ } else if (d->inputWidget == d->comboBox) {
+ d->setComboBoxText(text);
+ } else {
+ d->setListViewText(text);
+ }
+}
+
+QString QInputDialog::textValue() const
+{
+ Q_D(const QInputDialog);
+ return d->textValue;
+}
+
+/*!
+ \since 4.5
+
+ \property QInputDialog::textEchoMode
+
+ \brief the echo mode for the text value
+
+ This property is only relevant when the input dialog is used in
+ TextInput mode.
+*/
+void QInputDialog::setTextEchoMode(QLineEdit::EchoMode mode)
+{
+ Q_D(QInputDialog);
+ d->ensureLineEdit();
+ d->lineEdit->setEchoMode(mode);
+}
+
+QLineEdit::EchoMode QInputDialog::textEchoMode() const
+{
+ Q_D(const QInputDialog);
+ if (d->lineEdit) {
+ return d->lineEdit->echoMode();
+ } else {
+ return QLineEdit::Normal;
+ }
+}
+
+/*!
+ \since 4.5
+
+ \property QInputDialog::comboBoxEditable
+
+ \brief whether or not the combo box is used in the input dialog is editable
+*/
+void QInputDialog::setComboBoxEditable(bool editable)
+{
+ Q_D(QInputDialog);
+ d->ensureComboBox();
+ d->comboBox->setEditable(editable);
+ if (inputMode() == TextInput)
+ d->chooseRightTextInputWidget();
+}
+
+bool QInputDialog::isComboBoxEditable() const
+{
+ Q_D(const QInputDialog);
+ if (d->comboBox) {
+ return d->comboBox->isEditable();
+ } else {
+ return false;
+ }
+}
+
+/*!
+ \since 4.5
+
+ \property QInputDialog::comboBoxItems
+
+ \brief the items used in the combobox for the input dialog
+*/
+void QInputDialog::setComboBoxItems(const QStringList &items)
+{
+ Q_D(QInputDialog);
+
+ d->ensureComboBox();
+ d->comboBox->blockSignals(true);
+ d->comboBox->clear();
+ d->comboBox->addItems(items);
+ d->comboBox->blockSignals(false);
+
+ if (inputMode() == TextInput)
+ d->chooseRightTextInputWidget();
+}
+
+QStringList QInputDialog::comboBoxItems() const
+{
+ Q_D(const QInputDialog);
+ QStringList result;
+ if (d->comboBox) {
+ const int count = d->comboBox->count();
+ for (int i = 0; i < count; ++i)
+ result.append(d->comboBox->itemText(i));
+ }
+ return result;
+}
+
+/*!
+ \property QInputDialog::intValue
+ \since 4.5
+ \brief the current integer value accepted as input
+
+ This property is only relevant when the input dialog is used in
+ IntInput mode.
+*/
+void QInputDialog::setIntValue(int value)
+{
+ Q_D(QInputDialog);
+ setInputMode(IntInput);
+ d->intSpinBox->setValue(value);
+}
+
+int QInputDialog::intValue() const
+{
+ Q_D(const QInputDialog);
+ if (d->intSpinBox) {
+ return d->intSpinBox->value();
+ } else {
+ return 0;
+ }
+}
+
+/*!
+ \property QInputDialog::intMinimum
+ \since 4.5
+ \brief the minimum integer value accepted as input
+
+ This property is only relevant when the input dialog is used in
+ IntInput mode.
+*/
+void QInputDialog::setIntMinimum(int min)
+{
+ Q_D(QInputDialog);
+ d->ensureIntSpinBox();
+ d->intSpinBox->setMinimum(min);
+}
+
+int QInputDialog::intMinimum() const
+{
+ Q_D(const QInputDialog);
+ if (d->intSpinBox) {
+ return d->intSpinBox->minimum();
+ } else {
+ return 0;
+ }
+}
+
+/*!
+ \property QInputDialog::intMaximum
+ \since 4.5
+ \brief the maximum integer value accepted as input
+
+ This property is only relevant when the input dialog is used in
+ IntInput mode.
+*/
+void QInputDialog::setIntMaximum(int max)
+{
+ Q_D(QInputDialog);
+ d->ensureIntSpinBox();
+ d->intSpinBox->setMaximum(max);
+}
+
+int QInputDialog::intMaximum() const
+{
+ Q_D(const QInputDialog);
+ if (d->intSpinBox) {
+ return d->intSpinBox->maximum();
+ } else {
+ return 99;
+ }
+}
+
+/*!
+ Sets the range of integer values accepted by the dialog when used in
+ IntInput mode, with minimum and maximum values specified by \a min and
+ \a max respectively.
+*/
+void QInputDialog::setIntRange(int min, int max)
+{
+ Q_D(QInputDialog);
+ d->ensureIntSpinBox();
+ d->intSpinBox->setRange(min, max);
+}
+
+/*!
+ \property QInputDialog::intStep
+ \since 4.5
+ \brief the step by which the integer value is increased and decreased
+
+ This property is only relevant when the input dialog is used in
+ IntInput mode.
+*/
+void QInputDialog::setIntStep(int step)
+{
+ Q_D(QInputDialog);
+ d->ensureIntSpinBox();
+ d->intSpinBox->setSingleStep(step);
+}
+
+int QInputDialog::intStep() const
+{
+ Q_D(const QInputDialog);
+ if (d->intSpinBox) {
+ return d->intSpinBox->singleStep();
+ } else {
+ return 1;
+ }
+}
+
+/*!
+ \property QInputDialog::doubleValue
+ \since 4.5
+ \brief the current double precision floating point value accepted as input
+
+ This property is only relevant when the input dialog is used in
+ DoubleInput mode.
+*/
+void QInputDialog::setDoubleValue(double value)
+{
+ Q_D(QInputDialog);
+ setInputMode(DoubleInput);
+ d->doubleSpinBox->setValue(value);
+}
+
+double QInputDialog::doubleValue() const
+{
+ Q_D(const QInputDialog);
+ if (d->doubleSpinBox) {
+ return d->doubleSpinBox->value();
+ } else {
+ return 0.0;
+ }
+}
+
+/*!
+ \property QInputDialog::doubleMinimum
+ \since 4.5
+ \brief the minimum double precision floating point value accepted as input
+
+ This property is only relevant when the input dialog is used in
+ DoubleInput mode.
+*/
+void QInputDialog::setDoubleMinimum(double min)
+{
+ Q_D(QInputDialog);
+ d->ensureDoubleSpinBox();
+ d->doubleSpinBox->setMinimum(min);
+}
+
+double QInputDialog::doubleMinimum() const
+{
+ Q_D(const QInputDialog);
+ if (d->doubleSpinBox) {
+ return d->doubleSpinBox->minimum();
+ } else {
+ return 0.0;
+ }
+}
+
+/*!
+ \property QInputDialog::doubleMaximum
+ \since 4.5
+ \brief the maximum double precision floating point value accepted as input
+
+ This property is only relevant when the input dialog is used in
+ DoubleInput mode.
+*/
+void QInputDialog::setDoubleMaximum(double max)
+{
+ Q_D(QInputDialog);
+ d->ensureDoubleSpinBox();
+ d->doubleSpinBox->setMaximum(max);
+}
+
+double QInputDialog::doubleMaximum() const
+{
+ Q_D(const QInputDialog);
+ if (d->doubleSpinBox) {
+ return d->doubleSpinBox->maximum();
+ } else {
+ return 99.99;
+ }
+}
+
+/*!
+ Sets the range of double precision floating point values accepted by the
+ dialog when used in DoubleInput mode, with minimum and maximum values
+ specified by \a min and \a max respectively.
+*/
+void QInputDialog::setDoubleRange(double min, double max)
+{
+ Q_D(QInputDialog);
+ d->ensureDoubleSpinBox();
+ d->doubleSpinBox->setRange(min, max);
+}
+
+/*!
+ \since 4.5
+
+ \property QInputDialog::doubleDecimals
+
+ \brief sets the percision of the double spinbox in decimals
+
+ \sa QDoubleSpinBox::setDecimals()
+*/
+void QInputDialog::setDoubleDecimals(int decimals)
+{
+ Q_D(QInputDialog);
+ d->ensureDoubleSpinBox();
+ d->doubleSpinBox->setDecimals(decimals);
+}
+
+int QInputDialog::doubleDecimals() const
+{
+ Q_D(const QInputDialog);
+ if (d->doubleSpinBox) {
+ return d->doubleSpinBox->decimals();
+ } else {
+ return 2;
+ }
+}
+
+/*!
+ \since 4.5
+
+ \property QInputDialog::okButtonText
+
+ \brief the text for the button used to accept the entry in the dialog
+*/
+void QInputDialog::setOkButtonText(const QString &text)
+{
+ Q_D(const QInputDialog);
+ d->ensureLayout();
+ d->buttonBox->button(QDialogButtonBox::Ok)->setText(text);
+}
+
+QString QInputDialog::okButtonText() const
+{
+ Q_D(const QInputDialog);
+ d->ensureLayout();
+ return d->buttonBox->button(QDialogButtonBox::Ok)->text();
+}
+
+/*!
+ \since 4.5
+
+ \property QInputDialog::cancelButtonText
+ \brief the text for the button used to cancel the dialog
+*/
+void QInputDialog::setCancelButtonText(const QString &text)
+{
+ Q_D(const QInputDialog);
+ d->ensureLayout();
+ d->buttonBox->button(QDialogButtonBox::Cancel)->setText(text);
+}
+
+QString QInputDialog::cancelButtonText() const
+{
+ Q_D(const QInputDialog);
+ d->ensureLayout();
+ return d->buttonBox->button(QDialogButtonBox::Cancel)->text();
+}
+
+/*!
+ \since 4.5
+ \overload
+
+ Opens the dialog and connects its accepted() signal to the slot specified
+ by \a receiver and \a member.
+
+ The signal will be disconnected from the slot when the dialog is closed.
+*/
+void QInputDialog::open(QObject *receiver, const char *member)
+{
+ Q_D(QInputDialog);
+ connect(this, signalForMember(member), receiver, member);
+ d->receiverToDisconnectOnClose = receiver;
+ d->memberToDisconnectOnClose = member;
+ QDialog::open();
+}
+
+/*!
+ \reimp
+*/
+QSize QInputDialog::minimumSizeHint() const
+{
+ Q_D(const QInputDialog);
+ d->ensureLayout();
+ return QDialog::minimumSizeHint();
+}
+
+/*!
+ \reimp
+*/
+QSize QInputDialog::sizeHint() const
+{
+ Q_D(const QInputDialog);
+ d->ensureLayout();
+ return QDialog::sizeHint();
+}
+
+/*!
+ \reimp
+*/
+void QInputDialog::setVisible(bool visible)
+{
+ Q_D(const QInputDialog);
+ if (visible) {
+ d->ensureLayout();
+ d->inputWidget->setFocus();
+ if (d->inputWidget == d->lineEdit) {
+ d->lineEdit->selectAll();
+ } else if (d->inputWidget == d->intSpinBox) {
+ d->intSpinBox->selectAll();
+ } else if (d->inputWidget == d->doubleSpinBox) {
+ d->doubleSpinBox->selectAll();
+ }
+ }
+ QDialog::setVisible(visible);
+}
+
+/*!
+ Closes the dialog and sets its result code to \a result. If this dialog
+ is shown with exec(), done() causes the local event loop to finish,
+ and exec() to return \a result.
+
+ \sa QDialog::done()
+*/
+void QInputDialog::done(int result)
+{
+ Q_D(QInputDialog);
+ QDialog::done(result);
+ if (result) {
+ InputMode mode = inputMode();
+ switch (mode) {
+ case DoubleInput:
+ emit doubleValueSelected(doubleValue());
+ break;
+ case IntInput:
+ emit intValueSelected(intValue());
+ break;
+ default:
+ Q_ASSERT(mode == TextInput);
+ emit textValueSelected(textValue());
+ }
+ }
+ if (d->receiverToDisconnectOnClose) {
+ disconnect(this, signalForMember(d->memberToDisconnectOnClose),
+ d->receiverToDisconnectOnClose, d->memberToDisconnectOnClose);
+ d->receiverToDisconnectOnClose = 0;
+ }
+ d->memberToDisconnectOnClose.clear();
+}
+
+/*!
+ Static convenience function to get a string from the user. \a
+ title is the text which is displayed in the title bar of the
+ dialog. \a label is the text which is shown to the user (it should
+ say what should be entered). \a text is the default text which is
+ placed in the line edit. The \a mode is the echo mode the line
+ edit will use. If \a ok is nonnull \e *\a ok will be set to true
+ if the user pressed \gui OK and to false if the user pressed
+ \gui Cancel. The dialog's parent is \a parent. The dialog will be
+ modal and uses the specified widget \a flags.
+
+ This function returns the text which has been entered in the line
+ edit. It will not return an empty string.
+
+ Use this static function like this:
+
+ \snippet examples/dialogs/standarddialogs/dialog.cpp 3
+
+ \warning Do not delete \a parent during the execution of the dialog.
+ If you want to do this, you should create the dialog
+ yourself using one of the QInputDialog constructors.
+
+ \sa getInteger(), getDouble(), getItem()
+*/
+
+QString QInputDialog::getText(QWidget *parent, const QString &title, const QString &label,
+ QLineEdit::EchoMode mode, const QString &text, bool *ok,
+ Qt::WindowFlags flags)
+{
+ QInputDialog dialog(parent, flags);
+ dialog.setWindowTitle(title);
+ dialog.setLabelText(label);
+ dialog.setTextValue(text);
+ dialog.setTextEchoMode(mode);
+
+ int ret = dialog.exec();
+ if (ok)
+ *ok = !!ret;
+ if (ret) {
+ return dialog.textValue();
+ } else {
+ return text;
+ }
+}
+
+/*!
+ Static convenience function to get an integer input from the
+ user. \a title is the text which is displayed in the title bar
+ of the dialog. \a label is the text which is shown to the user
+ (it should say what should be entered). \a value is the default
+ integer which the spinbox will be set to. \a min and \a
+ max are the minimum and maximum values the user may choose,
+ and \a step is the amount by which the values change as the user
+ presses the arrow buttons to increment or decrement the value.
+
+ If \a ok is nonnull *\a ok will be set to true if the user
+ pressed \gui OK and to false if the user pressed \gui Cancel. The
+ dialog's parent is \a parent. The dialog will be modal and uses
+ the widget \a flags.
+
+ On success, this function returns the integer which has been
+ entered by the user; on failure, it returns the initial \a value.
+
+ Use this static function like this:
+
+ \snippet examples/dialogs/standarddialogs/dialog.cpp 0
+
+ \warning Do not delete \a parent during the execution of the dialog.
+ If you want to do this, you should create the dialog
+ yourself using one of the QInputDialog constructors.
+
+ \sa getText(), getDouble(), getItem()
+*/
+
+int QInputDialog::getInt(QWidget *parent, const QString &title, const QString &label, int value,
+ int min, int max, int step, bool *ok, Qt::WindowFlags flags)
+{
+ QInputDialog dialog(parent, flags);
+ dialog.setWindowTitle(title);
+ dialog.setLabelText(label);
+ dialog.setIntRange(min, max);
+ dialog.setIntValue(value);
+ dialog.setIntStep(step);
+
+ int ret = dialog.exec();
+ if (ok)
+ *ok = !!ret;
+ if (ret) {
+ return dialog.intValue();
+ } else {
+ return value;
+ }
+}
+
+/*!
+ Static convenience function to get a floating point number from
+ the user. \a title is the text which is displayed in the title
+ bar of the dialog. \a label is the text which is shown to the user
+ (it should say what should be entered). \a value is the default
+ floating point number that the line edit will be set to. \a
+ min and \a max are the minimum and maximum values the
+ user may choose, and \a decimals is the maximum number of decimal
+ places the number may have.
+
+ If \a ok is nonnull, *\a ok will be set to true if the user
+ pressed \gui OK and to false if the user pressed \gui Cancel. The
+ dialog's parent is \a parent. The dialog will be modal and uses
+ the widget \a flags.
+
+ This function returns the floating point number which has been
+ entered by the user.
+
+ Use this static function like this:
+
+ \snippet examples/dialogs/standarddialogs/dialog.cpp 1
+
+ \warning Do not delete \a parent during the execution of the dialog.
+ If you want to do this, you should create the dialog
+ yourself using one of the QInputDialog constructors.
+
+ \sa getText(), getInteger(), getItem()
+*/
+
+double QInputDialog::getDouble(QWidget *parent, const QString &title, const QString &label,
+ double value, double min, double max, int decimals, bool *ok,
+ Qt::WindowFlags flags)
+{
+ QInputDialog dialog(parent, flags);
+ dialog.setWindowTitle(title);
+ dialog.setLabelText(label);
+ dialog.setDoubleRange(min, max);
+ dialog.setDoubleValue(value);
+ dialog.setDoubleDecimals(decimals);
+
+ int ret = dialog.exec();
+ if (ok)
+ *ok = !!ret;
+ if (ret) {
+ return dialog.doubleValue();
+ } else {
+ return value;
+ }
+}
+
+/*!
+ Static convenience function to let the user select an item from a
+ string list. \a title is the text which is displayed in the title
+ bar of the dialog. \a label is the text which is shown to the user (it
+ should say what should be entered). \a items is the
+ string list which is inserted into the combobox, and \a current is the number
+ of the item which should be the current item. If \a editable is true
+ the user can enter their own text; if \a editable is false the user
+ may only select one of the existing items.
+
+ If \a ok is nonnull \e *\a ok will be set to true if the user
+ pressed \gui OK and to false if the user pressed \gui Cancel. The
+ dialog's parent is \a parent. The dialog will be modal and uses
+ the widget \a flags.
+
+ This function returns the text of the current item, or if \a
+ editable is true, the current text of the combobox.
+
+ Use this static function like this:
+
+ \snippet examples/dialogs/standarddialogs/dialog.cpp 2
+
+ \warning Do not delete \a parent during the execution of the dialog.
+ If you want to do this, you should create the dialog
+ yourself using one of the QInputDialog constructors.
+
+ \sa getText(), getInteger(), getDouble()
+*/
+
+QString QInputDialog::getItem(QWidget *parent, const QString &title, const QString &label,
+ const QStringList &items, int current, bool editable, bool *ok,
+ Qt::WindowFlags flags)
+{
+ QString text(items.value(current));
+
+ QInputDialog dialog(parent, flags);
+ dialog.setWindowTitle(title);
+ dialog.setLabelText(label);
+ dialog.setComboBoxItems(items);
+ dialog.setTextValue(text);
+ dialog.setComboBoxEditable(editable);
+
+ int ret = dialog.exec();
+ if (ok)
+ *ok = !!ret;
+ if (ret) {
+ return dialog.textValue();
+ } else {
+ return text;
+ }
+}
+
+/*!
+ \obsolete
+
+ Use getInt() instead.
+*/
+int QInputDialog::getInteger(QWidget *parent, const QString &title, const QString &label,
+ int value, int min, int max, int step, bool *ok,
+ Qt::WindowFlags flags)
+{
+ return getInt(parent, title, label, value, min, max, step, ok, flags);
+}
+
+/*!
+ \fn QString QInputDialog::getText(const QString &title, const QString &label,
+ QLineEdit::EchoMode echo = QLineEdit::Normal,
+ const QString &text = QString(), bool *ok = 0,
+ QWidget *parent = 0, const char *name = 0, Qt::WindowFlags flags = 0)
+
+ Call getText(\a parent, \a title, \a label, \a echo, \a text, \a
+ ok, \a flags) instead.
+
+ The \a name parameter is ignored.
+*/
+
+/*!
+ \fn int QInputDialog::getInteger(const QString &title, const QString &label, int value = 0,
+ int min = -2147483647, int max = 2147483647,
+ int step = 1, bool *ok = 0,
+ QWidget *parent = 0, const char *name = 0, Qt::WindowFlags flags = 0)
+
+
+ Call getInteger(\a parent, \a title, \a label, \a value, \a
+ min, \a max, \a step, \a ok, \a flags) instead.
+
+ The \a name parameter is ignored.
+*/
+
+/*!
+ \fn double QInputDialog::getDouble(const QString &title, const QString &label, double value = 0,
+ double min = -2147483647, double max = 2147483647,
+ int decimals = 1, bool *ok = 0,
+ QWidget *parent = 0, const char *name = 0, Qt::WindowFlags flags = 0)
+
+ Call getDouble(\a parent, \a title, \a label, \a value, \a
+ min, \a max, \a decimals, \a ok, \a flags).
+
+ The \a name parameter is ignored.
+*/
+
+/*!
+ \fn QString QInputDialog::getItem(const QString &title, const QString &label, const QStringList &list,
+ int current = 0, bool editable = true, bool *ok = 0,
+ QWidget *parent = 0, const char *name = 0, Qt::WindowFlags flags = 0)
+
+ Call getItem(\a parent, \a title, \a label, \a list, \a current,
+ \a editable, \a ok, \a flags) instead.
+
+ The \a name parameter is ignored.
+*/
+
+/*!
+ \fn void QInputDialog::doubleValueChanged(double value)
+
+ This signal is emitted whenever the double value changes in the dialog.
+ The current value is specified by \a value.
+
+ This signal is only relevant when the input dialog is used in
+ DoubleInput mode.
+*/
+
+/*!
+ \fn void QInputDialog::doubleValueSelected(double value)
+
+ This signal is emitted whenever the user selects a double value by
+ accepting the dialog; for example, by clicking the \gui{OK} button.
+ The selected value is specified by \a value.
+
+ This signal is only relevant when the input dialog is used in
+ DoubleInput mode.
+*/
+
+/*!
+ \fn void QInputDialog::intValueChanged(int value)
+
+ This signal is emitted whenever the integer value changes in the dialog.
+ The current value is specified by \a value.
+
+ This signal is only relevant when the input dialog is used in
+ IntInput mode.
+*/
+
+/*!
+ \fn void QInputDialog::intValueSelected(int value)
+
+ This signal is emitted whenever the user selects a integer value by
+ accepting the dialog; for example, by clicking the \gui{OK} button.
+ The selected value is specified by \a value.
+
+ This signal is only relevant when the input dialog is used in
+ IntInput mode.
+*/
+
+/*!
+ \fn void QInputDialog::textValueChanged(const QString &text)
+
+ This signal is emitted whenever the text string changes in the dialog.
+ The current string is specified by \a text.
+
+ This signal is only relevant when the input dialog is used in
+ TextInput mode.
+*/
+
+/*!
+ \fn void QInputDialog::textValueSelected(const QString &text)
+
+ This signal is emitted whenever the user selects a text string by
+ accepting the dialog; for example, by clicking the \gui{OK} button.
+ The selected string is specified by \a text.
+
+ This signal is only relevant when the input dialog is used in
+ TextInput mode.
+*/
+
+QT_END_NAMESPACE
+
+#include "moc_qinputdialog.cpp"
+
+#endif // QT_NO_INPUTDIALOG
diff --git a/src/gui/dialogs/qinputdialog.h b/src/gui/dialogs/qinputdialog.h
new file mode 100644
index 0000000000..52b338af45
--- /dev/null
+++ b/src/gui/dialogs/qinputdialog.h
@@ -0,0 +1,237 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QINPUTDIALOG_H
+#define QINPUTDIALOG_H
+
+#include <QtGui/qdialog.h>
+#include <QtCore/qstring.h>
+#include <QtGui/qlineedit.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+#ifndef QT_NO_INPUTDIALOG
+
+class QInputDialogPrivate;
+
+class Q_GUI_EXPORT QInputDialog : public QDialog
+{
+ Q_OBJECT
+ Q_DECLARE_PRIVATE(QInputDialog)
+// Q_ENUMS(InputMode InputDialogOption)
+ QDOC_PROPERTY(InputMode inputMode READ inputMode WRITE setInputMode)
+ QDOC_PROPERTY(QString labelText READ labelText WRITE setLabelText)
+ QDOC_PROPERTY(InputDialogOptions options READ options WRITE setOptions)
+ QDOC_PROPERTY(QString textValue READ textValue WRITE setTextValue NOTIFY textValueChanged)
+ QDOC_PROPERTY(int intValue READ intValue WRITE setIntValue NOTIFY intValueChanged)
+ QDOC_PROPERTY(int doubleValue READ doubleValue WRITE setDoubleValue NOTIFY doubleValueChanged)
+ QDOC_PROPERTY(QLineEdit::EchoMode textEchoMode READ textEchoMode WRITE setTextEchoMode)
+ QDOC_PROPERTY(bool comboBoxEditable READ isComboBoxEditable WRITE setComboBoxEditable)
+ QDOC_PROPERTY(QStringList comboBoxItems READ comboBoxItems WRITE setComboBoxItems)
+ QDOC_PROPERTY(int intMinimum READ intMinimum WRITE setIntMinimum)
+ QDOC_PROPERTY(int intMaximum READ intMaximum WRITE setIntMaximum)
+ QDOC_PROPERTY(int intStep READ intStep WRITE setIntStep)
+ QDOC_PROPERTY(double doubleMinimum READ doubleMinimum WRITE setDoubleMinimum)
+ QDOC_PROPERTY(double doubleMaximum READ doubleMaximum WRITE setDoubleMaximum)
+ QDOC_PROPERTY(int doubleDecimals READ doubleDecimals WRITE setDoubleDecimals)
+ QDOC_PROPERTY(QString okButtonText READ okButtonText WRITE setOkButtonText)
+ QDOC_PROPERTY(QString cancelButtonText READ cancelButtonText WRITE setCancelButtonText)
+
+public:
+ enum InputDialogOption {
+ NoButtons = 0x00000001,
+ UseListViewForComboBoxItems = 0x00000002
+ };
+
+ Q_DECLARE_FLAGS(InputDialogOptions, InputDialogOption)
+
+ enum InputMode {
+ TextInput,
+ IntInput,
+ DoubleInput
+ };
+
+ QInputDialog(QWidget *parent = 0, Qt::WindowFlags flags = 0);
+ ~QInputDialog();
+
+ void setInputMode(InputMode mode);
+ InputMode inputMode() const;
+
+ void setLabelText(const QString &text);
+ QString labelText() const;
+
+ void setOption(InputDialogOption option, bool on = true);
+ bool testOption(InputDialogOption option) const;
+ void setOptions(InputDialogOptions options);
+ InputDialogOptions options() const;
+
+ void setTextValue(const QString &text);
+ QString textValue() const;
+
+ void setTextEchoMode(QLineEdit::EchoMode mode);
+ QLineEdit::EchoMode textEchoMode() const;
+
+ void setComboBoxEditable(bool editable);
+ bool isComboBoxEditable() const;
+
+ void setComboBoxItems(const QStringList &items);
+ QStringList comboBoxItems() const;
+
+ void setIntValue(int value);
+ int intValue() const;
+
+ void setIntMinimum(int min);
+ int intMinimum() const;
+
+ void setIntMaximum(int max);
+ int intMaximum() const;
+
+ void setIntRange(int min, int max);
+
+ void setIntStep(int step);
+ int intStep() const;
+
+ void setDoubleValue(double value);
+ double doubleValue() const;
+
+ void setDoubleMinimum(double min);
+ double doubleMinimum() const;
+
+ void setDoubleMaximum(double max);
+ double doubleMaximum() const;
+
+ void setDoubleRange(double min, double max);
+
+ void setDoubleDecimals(int decimals);
+ int doubleDecimals() const;
+
+ void setOkButtonText(const QString &text);
+ QString okButtonText() const;
+
+ void setCancelButtonText(const QString &text);
+ QString cancelButtonText() const;
+
+#ifdef Q_NO_USING_KEYWORD
+#ifndef Q_QDOC
+ void open() { QDialog::open(); }
+#endif
+#else
+ using QDialog::open;
+#endif
+ void open(QObject *receiver, const char *member);
+
+ QSize minimumSizeHint() const;
+ QSize sizeHint() const;
+
+ void setVisible(bool visible);
+
+ static QString getText(QWidget *parent, const QString &title, const QString &label,
+ QLineEdit::EchoMode echo = QLineEdit::Normal,
+ const QString &text = QString(), bool *ok = 0, Qt::WindowFlags flags = 0);
+ static int getInt(QWidget *parent, const QString &title, const QString &label, int value = 0,
+ int minValue = -2147483647, int maxValue = 2147483647,
+ int step = 1, bool *ok = 0, Qt::WindowFlags flags = 0);
+ static double getDouble(QWidget *parent, const QString &title, const QString &label, double value = 0,
+ double minValue = -2147483647, double maxValue = 2147483647,
+ int decimals = 1, bool *ok = 0, Qt::WindowFlags flags = 0);
+ static QString getItem(QWidget *parent, const QString &title, const QString &label,
+ const QStringList &items, int current = 0, bool editable = true,
+ bool *ok = 0, Qt::WindowFlags flags = 0);
+
+ // obsolete
+ static int getInteger(QWidget *parent, const QString &title, const QString &label, int value = 0,
+ int minValue = -2147483647, int maxValue = 2147483647,
+ int step = 1, bool *ok = 0, Qt::WindowFlags flags = 0);
+
+#ifdef QT3_SUPPORT
+ inline static QT3_SUPPORT QString getText(const QString &title, const QString &label,
+ QLineEdit::EchoMode echo = QLineEdit::Normal,
+ const QString &text = QString(), bool *ok = 0,
+ QWidget *parent = 0, const char * = 0, Qt::WindowFlags flags = 0)
+ { return getText(parent, title, label, echo, text, ok, flags); }
+ inline static QT3_SUPPORT int getInteger(const QString &title, const QString &label, int value = 0,
+ int minValue = -2147483647, int maxValue = 2147483647,
+ int step = 1, bool *ok = 0,
+ QWidget *parent = 0, const char * = 0, Qt::WindowFlags flags = 0)
+ { return getInteger(parent, title, label, value, minValue, maxValue, step, ok, flags); }
+ inline static QT3_SUPPORT double getDouble(const QString &title, const QString &label, double value = 0,
+ double minValue = -2147483647, double maxValue = 2147483647,
+ int decimals = 1, bool *ok = 0,
+ QWidget *parent = 0, const char * = 0, Qt::WindowFlags flags = 0)
+ { return getDouble(parent, title, label, value, minValue, maxValue, decimals, ok, flags); }
+ inline static QT3_SUPPORT QString getItem(const QString &title, const QString &label, const QStringList &list,
+ int current = 0, bool editable = true, bool *ok = 0,
+ QWidget *parent = 0, const char * = 0, Qt::WindowFlags flags = 0)
+ { return getItem(parent, title, label, list, current, editable, ok, flags); }
+#endif
+
+Q_SIGNALS:
+ // ### emit signals!
+ void textValueChanged(const QString &text);
+ void textValueSelected(const QString &text);
+ void intValueChanged(int value);
+ void intValueSelected(int value);
+ void doubleValueChanged(double value);
+ void doubleValueSelected(double value);
+
+
+public:
+ void done(int result); // ### Qt 5: Make protected.
+
+private:
+ Q_DISABLE_COPY(QInputDialog)
+ Q_PRIVATE_SLOT(d_func(), void _q_textChanged(const QString&))
+ Q_PRIVATE_SLOT(d_func(), void _q_currentRowChanged(const QModelIndex&, const QModelIndex&))
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(QInputDialog::InputDialogOptions)
+
+#endif // QT_NO_INPUTDIALOG
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QINPUTDIALOG_H
diff --git a/src/gui/dialogs/qmessagebox.cpp b/src/gui/dialogs/qmessagebox.cpp
new file mode 100644
index 0000000000..f3434050af
--- /dev/null
+++ b/src/gui/dialogs/qmessagebox.cpp
@@ -0,0 +1,2688 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtGui/qmessagebox.h>
+
+#ifndef QT_NO_MESSAGEBOX
+
+#include <QtGui/qdialogbuttonbox.h>
+#include "private/qlabel_p.h"
+#include <QtCore/qlist.h>
+#include <QtCore/qdebug.h>
+#include <QtGui/qstyle.h>
+#include <QtGui/qstyleoption.h>
+#include <QtGui/qgridlayout.h>
+#include <QtGui/qdesktopwidget.h>
+#include <QtGui/qpushbutton.h>
+#include <QtGui/qaccessible.h>
+#include <QtGui/qicon.h>
+#include <QtGui/qtextdocument.h>
+#include <QtGui/qapplication.h>
+#include <QtGui/qtextedit.h>
+#include <QtGui/qmenu.h>
+#include "qdialog_p.h"
+#include <QtGui/qfont.h>
+#include <QtGui/qfontmetrics.h>
+#include <QtGui/qclipboard.h>
+
+#ifdef Q_OS_WINCE
+extern bool qt_wince_is_mobile(); //defined in qguifunctions_wince.cpp
+extern bool qt_wince_is_smartphone();//defined in qguifunctions_wince.cpp
+extern bool qt_wince_is_pocket_pc(); //defined in qguifunctions_wince.cpp
+
+#include "qguifunctions_wince.h"
+#endif
+
+QT_BEGIN_NAMESPACE
+
+extern QHash<QByteArray, QFont> *qt_app_fonts_hash();
+
+enum Button { Old_Ok = 1, Old_Cancel = 2, Old_Yes = 3, Old_No = 4, Old_Abort = 5, Old_Retry = 6,
+ Old_Ignore = 7, Old_YesAll = 8, Old_NoAll = 9, Old_ButtonMask = 0xFF,
+ NewButtonMask = 0xFFFFFC00 };
+
+enum DetailButtonLabel { ShowLabel = 0, HideLabel = 1 };
+#ifndef QT_NO_TEXTEDIT
+class QMessageBoxDetailsText : public QWidget
+{
+public:
+ class TextEdit : public QTextEdit
+ {
+ public:
+ TextEdit(QWidget *parent=0) : QTextEdit(parent) { }
+ void contextMenuEvent(QContextMenuEvent * e)
+ {
+#ifndef QT_NO_CONTEXTMENU
+ QMenu *menu = createStandardContextMenu();
+ menu->exec(e->globalPos());
+ delete menu;
+#else
+ Q_UNUSED(e);
+#endif
+ }
+ };
+
+ QMessageBoxDetailsText(QWidget *parent=0)
+ : QWidget(parent)
+ {
+ QVBoxLayout *layout = new QVBoxLayout;
+ layout->setMargin(0);
+ QFrame *line = new QFrame(this);
+ line->setFrameShape(QFrame::HLine);
+ line->setFrameShadow(QFrame::Sunken);
+ layout->addWidget(line);
+ textEdit = new TextEdit();
+ textEdit->setFixedHeight(100);
+ textEdit->setFocusPolicy(Qt::NoFocus);
+ textEdit->setReadOnly(true);
+ layout->addWidget(textEdit);
+ setLayout(layout);
+ }
+ void setText(const QString &text) { textEdit->setPlainText(text); }
+ QString text() const { return textEdit->toPlainText(); }
+ QString label(DetailButtonLabel label)
+ { return label == ShowLabel ? QMessageBox::tr("Show Details...")
+ : QMessageBox::tr("Hide Details..."); }
+private:
+ TextEdit *textEdit;
+};
+#endif // QT_NO_TEXTEDIT
+
+class QMessageBoxPrivate : public QDialogPrivate
+{
+ Q_DECLARE_PUBLIC(QMessageBox)
+
+public:
+ QMessageBoxPrivate() : escapeButton(0), defaultButton(0), clickedButton(0), detailsButton(0),
+#ifndef QT_NO_TEXTEDIT
+ detailsText(0),
+#endif
+ compatMode(false), autoAddOkButton(true),
+ detectedEscapeButton(0), informativeLabel(0) { }
+
+ void init(const QString &title = QString(), const QString &text = QString());
+ void _q_buttonClicked(QAbstractButton *);
+
+ QAbstractButton *findButton(int button0, int button1, int button2, int flags);
+ void addOldButtons(int button0, int button1, int button2);
+
+ QAbstractButton *abstractButtonForId(int id) const;
+ int execReturnCode(QAbstractButton *button);
+
+ void detectEscapeButton();
+ void updateSize();
+ int layoutMinimumWidth();
+ void retranslateStrings();
+
+#ifdef Q_OS_WINCE
+ void hideSpecial();
+#endif
+
+ static int showOldMessageBox(QWidget *parent, QMessageBox::Icon icon,
+ const QString &title, const QString &text,
+ int button0, int button1, int button2);
+ static int showOldMessageBox(QWidget *parent, QMessageBox::Icon icon,
+ const QString &title, const QString &text,
+ const QString &button0Text,
+ const QString &button1Text,
+ const QString &button2Text,
+ int defaultButtonNumber,
+ int escapeButtonNumber);
+
+ static QMessageBox::StandardButton showNewMessageBox(QWidget *parent,
+ QMessageBox::Icon icon, const QString& title, const QString& text,
+ QMessageBox::StandardButtons buttons, QMessageBox::StandardButton defaultButton);
+
+ static QPixmap standardIcon(QMessageBox::Icon icon, QMessageBox *mb);
+
+ QLabel *label;
+ QMessageBox::Icon icon;
+ QLabel *iconLabel;
+ QDialogButtonBox *buttonBox;
+ QList<QAbstractButton *> customButtonList;
+ QAbstractButton *escapeButton;
+ QPushButton *defaultButton;
+ QAbstractButton *clickedButton;
+ QPushButton *detailsButton;
+#ifndef QT_NO_TEXTEDIT
+ QMessageBoxDetailsText *detailsText;
+#endif
+ bool compatMode;
+ bool autoAddOkButton;
+ QAbstractButton *detectedEscapeButton;
+ QLabel *informativeLabel;
+ QPointer<QObject> receiverToDisconnectOnClose;
+ QByteArray memberToDisconnectOnClose;
+ QByteArray signalToDisconnectOnClose;
+};
+
+void QMessageBoxPrivate::init(const QString &title, const QString &text)
+{
+ Q_Q(QMessageBox);
+
+ label = new QLabel;
+ label->setObjectName(QLatin1String("qt_msgbox_label"));
+ label->setTextInteractionFlags(Qt::TextInteractionFlags(q->style()->styleHint(QStyle::SH_MessageBox_TextInteractionFlags, 0, q)));
+ label->setAlignment(Qt::AlignVCenter | Qt::AlignLeft);
+ label->setOpenExternalLinks(true);
+#if defined(Q_WS_MAC)
+ label->setContentsMargins(16, 0, 0, 0);
+#elif !defined(Q_WS_QWS)
+ label->setContentsMargins(2, 0, 0, 0);
+ label->setIndent(9);
+#endif
+ icon = QMessageBox::NoIcon;
+ iconLabel = new QLabel;
+ iconLabel->setObjectName(QLatin1String("qt_msgboxex_icon_label"));
+ iconLabel->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
+
+ buttonBox = new QDialogButtonBox;
+ buttonBox->setObjectName(QLatin1String("qt_msgbox_buttonbox"));
+ buttonBox->setCenterButtons(q->style()->styleHint(QStyle::SH_MessageBox_CenterButtons, 0, q));
+ QObject::connect(buttonBox, SIGNAL(clicked(QAbstractButton*)),
+ q, SLOT(_q_buttonClicked(QAbstractButton*)));
+
+ QGridLayout *grid = new QGridLayout;
+#ifndef Q_WS_MAC
+ grid->addWidget(iconLabel, 0, 0, 2, 1, Qt::AlignTop);
+ grid->addWidget(label, 0, 1, 1, 1);
+ // -- leave space for information label --
+ grid->addWidget(buttonBox, 2, 0, 1, 2);
+#else
+ grid->setMargin(0);
+ grid->setVerticalSpacing(8);
+ grid->setHorizontalSpacing(0);
+ q->setContentsMargins(24, 15, 24, 20);
+ grid->addWidget(iconLabel, 0, 0, 2, 1, Qt::AlignTop | Qt::AlignLeft);
+ grid->addWidget(label, 0, 1, 1, 1);
+ // -- leave space for information label --
+ grid->setRowStretch(1, 100);
+ grid->setRowMinimumHeight(2, 6);
+ grid->addWidget(buttonBox, 3, 1, 1, 1);
+#endif
+
+ grid->setSizeConstraint(QLayout::SetNoConstraint);
+ q->setLayout(grid);
+
+ if (!title.isEmpty() || !text.isEmpty()) {
+ q->setWindowTitle(title);
+ q->setText(text);
+ }
+ q->setModal(true);
+
+#ifdef Q_WS_MAC
+ QFont f = q->font();
+ f.setBold(true);
+ label->setFont(f);
+#endif
+ retranslateStrings();
+}
+
+int QMessageBoxPrivate::layoutMinimumWidth()
+{
+ Q_Q(QMessageBox);
+
+ q->layout()->activate();
+ return q->layout()->totalMinimumSize().width();
+}
+
+void QMessageBoxPrivate::updateSize()
+{
+ Q_Q(QMessageBox);
+
+ if (!q->isVisible())
+ return;
+
+ QSize screenSize = QApplication::desktop()->availableGeometry(QCursor::pos()).size();
+#ifdef Q_WS_QWS
+ // the width of the screen, less the window border.
+ int hardLimit = screenSize.width() - (q->frameGeometry().width() - q->geometry().width());
+#elif defined(Q_OS_WINCE)
+ // the width of the screen, less the window border.
+ int hardLimit = screenSize.width() - (q->frameGeometry().width() - q->geometry().width());
+#else
+ int hardLimit = qMin(screenSize.width() - 480, 1000); // can never get bigger than this
+#endif
+#ifdef Q_WS_MAC
+ int softLimit = qMin(screenSize.width()/2, 420);
+#elif defined(Q_WS_QWS)
+ int softLimit = qMin(hardLimit, 500);
+#else
+ // note: ideally on windows, hard and soft limits but it breaks compat
+#ifndef Q_OS_WINCE
+ int softLimit = qMin(screenSize.width()/2, 500);
+#else
+ int softLimit = qMin(screenSize.width() * 3 / 4, 500);
+#endif //Q_OS_WINCE
+#endif
+
+ if (informativeLabel)
+ informativeLabel->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored);
+
+ label->setWordWrap(false); // makes the label return min size
+ int width = layoutMinimumWidth();
+
+ if (width > softLimit) {
+ label->setWordWrap(true);
+ width = qMax(softLimit, layoutMinimumWidth());
+
+ if (width > hardLimit) {
+ label->d_func()->ensureTextControl();
+ if (QTextControl *control = label->d_func()->control) {
+ QTextOption opt = control->document()->defaultTextOption();
+ opt.setWrapMode(QTextOption::WrapAnywhere);
+ control->document()->setDefaultTextOption(opt);
+ }
+ width = hardLimit;
+ }
+ }
+
+ if (informativeLabel) {
+ label->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored);
+ QSizePolicy policy(QSizePolicy::Minimum, QSizePolicy::Preferred);
+ policy.setHeightForWidth(true);
+ informativeLabel->setSizePolicy(policy);
+ width = qMax(width, layoutMinimumWidth());
+ if (width > hardLimit) { // longest word is really big, so wrap anywhere
+ informativeLabel->d_func()->ensureTextControl();
+ if (QTextControl *control = informativeLabel->d_func()->control) {
+ QTextOption opt = control->document()->defaultTextOption();
+ opt.setWrapMode(QTextOption::WrapAnywhere);
+ control->document()->setDefaultTextOption(opt);
+ }
+ width = hardLimit;
+ }
+ policy.setHeightForWidth(label->wordWrap());
+ label->setSizePolicy(policy);
+ }
+
+ QFontMetrics fm(qApp->font("QWorkspaceTitleBar"));
+ int windowTitleWidth = qMin(fm.width(q->windowTitle()) + 50, hardLimit);
+ if (windowTitleWidth > width)
+ width = windowTitleWidth;
+
+ q->layout()->activate();
+ int height = (q->layout()->hasHeightForWidth())
+ ? q->layout()->totalHeightForWidth(width)
+ : q->layout()->totalMinimumSize().height();
+ q->setFixedSize(width, height);
+ QCoreApplication::removePostedEvents(q, QEvent::LayoutRequest);
+}
+
+
+#ifdef Q_OS_WINCE
+/*!
+ \internal
+ Hides special buttons which are rather shown in the title bar
+ on WinCE, to conserve screen space.
+*/
+
+void QMessageBoxPrivate::hideSpecial()
+{
+ Q_Q(QMessageBox);
+ QList<QPushButton*> list = qFindChildren<QPushButton*>(q);
+ for (int i=0; i<list.size(); ++i) {
+ QPushButton *pb = list.at(i);
+ QString text = pb->text();
+ text.remove(QChar::fromLatin1('&'));
+ if (text == qApp->translate("QMessageBox", "OK" ))
+ pb->setFixedSize(0,0);
+ }
+}
+#endif
+
+static int oldButton(int button)
+{
+ switch (button & QMessageBox::ButtonMask) {
+ case QMessageBox::Ok:
+ return Old_Ok;
+ case QMessageBox::Cancel:
+ return Old_Cancel;
+ case QMessageBox::Yes:
+ return Old_Yes;
+ case QMessageBox::No:
+ return Old_No;
+ case QMessageBox::Abort:
+ return Old_Abort;
+ case QMessageBox::Retry:
+ return Old_Retry;
+ case QMessageBox::Ignore:
+ return Old_Ignore;
+ case QMessageBox::YesToAll:
+ return Old_YesAll;
+ case QMessageBox::NoToAll:
+ return Old_NoAll;
+ default:
+ return 0;
+ }
+}
+
+int QMessageBoxPrivate::execReturnCode(QAbstractButton *button)
+{
+ int ret = buttonBox->standardButton(button);
+ if (ret == QMessageBox::NoButton) {
+ ret = customButtonList.indexOf(button); // if button == 0, correctly sets ret = -1
+ } else if (compatMode) {
+ ret = oldButton(ret);
+ }
+ return ret;
+}
+
+void QMessageBoxPrivate::_q_buttonClicked(QAbstractButton *button)
+{
+ Q_Q(QMessageBox);
+#ifndef QT_NO_TEXTEDIT
+ if (detailsButton && detailsText && button == detailsButton) {
+ detailsButton->setText(detailsText->isHidden() ? detailsText->label(HideLabel) : detailsText->label(ShowLabel));
+ detailsText->setHidden(!detailsText->isHidden());
+ updateSize();
+ } else
+#endif
+ {
+ clickedButton = button;
+ q->done(execReturnCode(button)); // does not trigger closeEvent
+ emit q->buttonClicked(button);
+
+ if (receiverToDisconnectOnClose) {
+ QObject::disconnect(q, signalToDisconnectOnClose, receiverToDisconnectOnClose,
+ memberToDisconnectOnClose);
+ receiverToDisconnectOnClose = 0;
+ }
+ signalToDisconnectOnClose.clear();
+ memberToDisconnectOnClose.clear();
+ }
+}
+
+/*!
+ \class QMessageBox
+
+ \brief The QMessageBox class provides a modal dialog for informing
+ the user or for asking the user a question and receiving an answer.
+
+ \ingroup dialogs
+ \mainclass
+
+ A message box displays a primary \l{QMessageBox::text}{text} to
+ alert the user to a situation, an \l{QMessageBox::informativeText}
+ {informative text} to further explain the alert or to ask the user
+ a question, and an optional \l{QMessageBox::detailedText}
+ {detailed text} to provide even more data if the user requests
+ it. A message box can also display an \l{QMessageBox::icon} {icon}
+ and \l{QMessageBox::standardButtons} {standard buttons} for
+ accepting a user response.
+
+ Two APIs for using QMessageBox are provided, the property-based
+ API, and the static functions. Calling one of the static functions
+ is the simpler approach, but it is less flexible than using the
+ property-based API, and the result is less informative. Using the
+ property-based API is recommended.
+
+ \section1 The Property-based API
+
+ To use the property-based API, construct an instance of
+ QMessageBox, set the desired properties, and call exec() to show
+ the message. The simplest configuration is to set only the
+ \l{QMessageBox::text} {message text} property.
+
+ \snippet doc/src/snippets/code/src_gui_dialogs_qmessagebox.cpp 5
+
+ The user must click the \gui{OK} button to dismiss the message
+ box. The rest of the GUI is blocked until the message box is
+ dismissed.
+
+ \image msgbox1.png
+
+ A better approach than just alerting the user to an event is to
+ also ask the user what to do about it. Store the question in the
+ \l{QMessageBox::informativeText} {informative text} property, and
+ set the \l{QMessageBox::standardButtons} {standard buttons}
+ property to the set of buttons you want as the set of user
+ responses. The buttons are specified by combining values from
+ StandardButtons using the bitwise OR operator. The display order
+ for the buttons is platform-dependent. For example, on Windows,
+ \gui{Save} is displayed to the left of \gui{Cancel}, whereas on
+ Mac OS, the order is reversed.
+
+ Mark one of your standard buttons to be your
+ \l{QMessageBox::defaultButton()} {default button}.
+
+ \snippet doc/src/snippets/code/src_gui_dialogs_qmessagebox.cpp 6
+
+ This is the approach recommended in the
+ \l{http://developer.apple.com/documentation/UserExperience/Conceptual/AppleHIGuidelines/XHIGWindows/chapter_18_section_7.html}
+ {Mac OS X Guidlines}. Similar guidlines apply for the other
+ platforms, but note the different ways the
+ \l{QMessageBox::informativeText} {informative text} is handled for
+ different platforms.
+
+ \image msgbox2.png
+
+ The exec() slot returns the StandardButtons value of the button
+ that was clicked.
+
+ \snippet doc/src/snippets/code/src_gui_dialogs_qmessagebox.cpp 7
+
+ To give the user more information to help him answer the question,
+ set the \l{QMessageBox::detailedText} {detailed text} property. If
+ the \l{QMessageBox::detailedText} {detailed text} property is set,
+ the \gui{Show Details...} button will be shown.
+
+ \image msgbox3.png
+
+ Clicking the \gui{Show Details...} button displays the detailed text.
+
+ \image msgbox4.png
+
+ \section2 Rich Text and the Text Format Property
+
+ The \l{QMessageBox::detailedText} {detailed text} property is
+ always interpreted as plain text. The \l{QMessageBox::text} {main
+ text} and \l{QMessageBox::informativeText} {informative text}
+ properties can be either plain text or rich text. These strings
+ are interpreted according to the setting of the
+ \l{QMessageBox::textFormat} {text format} property. The default
+ setting is \l{Qt::AutoText} {auto-text}.
+
+ Note that for some plain text strings containing XML
+ meta-characters, the auto-text \l{Qt::mightBeRichText()} {rich
+ text detection test} may fail causing your plain text string to be
+ interpreted incorrectly as rich text. In these rare cases, use
+ Qt::convertFromPlainText() to convert your plain text string to a
+ visually equivalent rich text string, or set the
+ \l{QMessageBox::textFormat} {text format} property explicitly with
+ setTextFormat().
+
+ \section2 Severity Levels and the Icon and Pixmap Properties
+
+ QMessageBox supports four predefined message severity levels, or
+ message types, which really only differ in the predefined icon
+ they each show. Specify one of the four predefined message types
+ by setting the \l{QMessageBox::icon} {icon} property to one of the
+ \l{QMessageBox::Icon} {predefined Icons}. The following rules are
+ guidelines:
+
+ \table
+ \row
+ \o \img qmessagebox-quest.png
+ \o \l Question
+ \o For asking a question during normal operations.
+ \row
+ \o \img qmessagebox-info.png
+ \o \l Information
+ \o For reporting information about normal operations.
+ \row
+ \o \img qmessagebox-warn.png
+ \o \l Warning
+ \o For reporting non-critical errors.
+ \row
+ \o \img qmessagebox-crit.png
+ \o \l Critical
+ \o For reporting critical errors.
+ \endtable
+
+ The default value is \l{QMessageBox::NoIcon} {No Icon}. The
+ message boxes are otherwise the same for all cases. When using a
+ standard icon, use the one recommended in the table, or use the
+ one recommended by the style guidelines for your platform. If none
+ of the standard icons is right for your message box, you can use a
+ custom icon by setting the \l{QMessageBox::iconPixmap} {icon
+ pixmap} property instead of setting the \l{QMessageBox::icon}
+ {icon} property.
+
+ In summary, to set an icon, use \e{either} setIcon() for one of
+ the standard icons, \e{or} setIconPixmap() for a custom icon.
+
+ \section1 The Static Functions API
+
+ Building message boxes with the static functions API, although
+ convenient, is less flexible than using the property-based API,
+ because the static function signatures lack parameters for setting
+ the \l{QMessageBox::informativeText} {informative text} and
+ \l{QMessageBox::detailedText} {detailed text} properties. One
+ work-around for this has been to use the \c{title} parameter as
+ the message box main text and the \c{text} parameter as the
+ message box informative text. Because this has the obvious
+ drawback of making a less readable message box, platform
+ guidelines do not recommend it. The \e{Microsoft Windows User
+ Interface Guidelines} recommend using the
+ \l{QCoreApplication::applicationName} {application name} as the
+ \l{QMessageBox::setWindowTitle()} {window's title}, which means
+ that if you have an informative text in addition to your main
+ text, you must concatenate it to the \c{text} parameter.
+
+ Note that the static function signatures have changed with respect
+ to their button parameters, which are now used to set the
+ \l{QMessageBox::standardButtons} {standard buttons} and the
+ \l{QMessageBox::defaultButton()} {default button}.
+
+ Static functions are available for creating information(),
+ question(), warning(), and critical() message boxes.
+
+ \snippet doc/src/snippets/code/src_gui_dialogs_qmessagebox.cpp 0
+
+ The \l{dialogs/standarddialogs}{Standard Dialogs} example shows
+ how to use QMessageBox and the other built-in Qt dialogs.
+
+ \section1 Advanced Usage
+
+ If the \l{QMessageBox::StandardButtons} {standard buttons} are not
+ flexible enough for your message box, you can use the addButton()
+ overload that takes a text and a ButtonRoleto to add custom
+ buttons. The ButtonRole is used by QMessageBox to determine the
+ ordering of the buttons on screen (which varies according to the
+ platform). You can test the value of clickedButton() after calling
+ exec(). For example,
+
+ \snippet doc/src/snippets/code/src_gui_dialogs_qmessagebox.cpp 2
+
+ \section1 Default and Escape Keys
+
+ The default button (i.e., the button activated when \key Enter is
+ pressed) can be specified using setDefaultButton(). If a default
+ button is not specified, QMessageBox tries to find one based on
+ the \l{ButtonRole} {button roles} of the buttons used in the
+ message box.
+
+ The escape button (the button activated when \key Esc is pressed)
+ can be specified using setEscapeButton(). If an escape button is
+ not specified, QMessageBox tries to find one using these rules:
+
+ \list 1
+
+ \o If there is only one button, it is the button activated when
+ \key Esc is pressed.
+
+ \o If there is a \l Cancel button, it is the button activated when
+ \key Esc is pressed.
+
+ \o If there is exactly one button having either
+ \l{QMessageBox::RejectRole} {the Reject role} or the
+ \l{QMessageBox::NoRole} {the No role}, it is the button
+ activated when \key Esc is pressed.
+
+ \endlist
+
+ When an escape button can't be determined using these rules,
+ pressing \key Esc has no effect.
+
+ \sa QDialogButtonBox, {fowler}{GUI Design Handbook: Message Box}, {Standard Dialogs Example}, {Application Example}
+*/
+
+/*!
+ \enum QMessageBox::StandardButton
+ \since 4.2
+
+ These enums describe flags for standard buttons. Each button has a
+ defined \l ButtonRole.
+
+ \value Ok An "OK" button defined with the \l AcceptRole.
+ \value Open A "Open" button defined with the \l AcceptRole.
+ \value Save A "Save" button defined with the \l AcceptRole.
+ \value Cancel A "Cancel" button defined with the \l RejectRole.
+ \value Close A "Close" button defined with the \l RejectRole.
+ \value Discard A "Discard" or "Don't Save" button, depending on the platform,
+ defined with the \l DestructiveRole.
+ \value Apply An "Apply" button defined with the \l ApplyRole.
+ \value Reset A "Reset" button defined with the \l ResetRole.
+ \value RestoreDefaults A "Restore Defaults" button defined with the \l ResetRole.
+ \value Help A "Help" button defined with the \l HelpRole.
+ \value SaveAll A "Save All" button defined with the \l AcceptRole.
+ \value Yes A "Yes" button defined with the \l YesRole.
+ \value YesToAll A "Yes to All" button defined with the \l YesRole.
+ \value No A "No" button defined with the \l NoRole.
+ \value NoToAll A "No to All" button defined with the \l NoRole.
+ \value Abort An "Abort" button defined with the \l RejectRole.
+ \value Retry A "Retry" button defined with the \l AcceptRole.
+ \value Ignore An "Ignore" button defined with the \l AcceptRole.
+
+ \value NoButton An invalid button.
+
+ \omitvalue FirstButton
+ \omitvalue LastButton
+
+ The following values are obsolete:
+
+ \value YesAll Use YesToAll instead.
+ \value NoAll Use NoToAll instead.
+ \value Default Use the \c defaultButton argument of
+ information(), warning(), etc. instead, or call
+ setDefaultButton().
+ \value Escape Call setEscapeButton() instead.
+ \value FlagMask
+ \value ButtonMask
+
+ \sa ButtonRole, standardButtons
+*/
+
+/*!
+ \fn void QMessageBox::buttonClicked(QAbstractButton *button)
+
+ This signal is emitted whenever a button is clicked inside the QMessageBox.
+ The button that was clicked in returned in \a button.
+*/
+
+/*!
+ Constructs a message box with no text and no buttons. \a parent is
+ passed to the QDialog constructor.
+
+ If \a parent is 0, the message box is an \l{Qt::ApplicationModal}
+ {application modal} dialog box. If \a parent is a widget, the
+ message box is \l{Qt::WindowModal} {window modal} relative to \a
+ parent.
+
+ On Mac OS X, if \a parent is not 0 and you want your message box
+ to appear as a Qt::Sheet of that parent, set the message box's
+ \l{setWindowModality()} {window modality} to Qt::WindowModal
+ (default). Otherwise, the message box will be a standard dialog.
+
+*/
+QMessageBox::QMessageBox(QWidget *parent)
+ : QDialog(*new QMessageBoxPrivate, parent, Qt::MSWindowsFixedSizeDialogHint | Qt::WindowTitleHint | Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint)
+{
+ Q_D(QMessageBox);
+ d->init();
+}
+
+/*!
+ Constructs a message box with the given \a icon, \a title, \a
+ text, and standard \a buttons. Standard or custom buttons can be
+ added at any time using addButton(). The \a parent and \a f
+ arguments are passed to the QDialog constructor.
+
+ If \a parent is 0, the message box is an \l{Qt::ApplicationModal}
+ {application modal} dialog box. If \a parent is a widget, the
+ message box is \l{Qt::WindowModal} {window modal} relative to \a
+ parent.
+
+ On Mac OS X, if \a parent is not 0 and you want your message box
+ to appear as a Qt::Sheet of that parent, set the message box's
+ \l{setWindowModality()} {window modality} to Qt::WindowModal
+ (default). Otherwise, the message box will be a standard dialog.
+
+ \sa setWindowTitle(), setText(), setIcon(), setStandardButtons()
+*/
+QMessageBox::QMessageBox(Icon icon, const QString &title, const QString &text,
+ StandardButtons buttons, QWidget *parent,
+ Qt::WindowFlags f)
+: QDialog(*new QMessageBoxPrivate, parent, f | Qt::MSWindowsFixedSizeDialogHint | Qt::WindowTitleHint | Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint)
+{
+ Q_D(QMessageBox);
+ d->init(title, text);
+ setIcon(icon);
+ if (buttons != NoButton)
+ setStandardButtons(buttons);
+}
+
+/*!
+ Destroys the message box.
+*/
+QMessageBox::~QMessageBox()
+{
+}
+
+/*!
+ \since 4.2
+
+ Adds the given \a button to the message box with the specified \a
+ role.
+
+ \sa removeButton(), button(), setStandardButtons()
+*/
+void QMessageBox::addButton(QAbstractButton *button, ButtonRole role)
+{
+ Q_D(QMessageBox);
+ if (!button)
+ return;
+ removeButton(button);
+ d->buttonBox->addButton(button, (QDialogButtonBox::ButtonRole)role);
+ d->customButtonList.append(button);
+ d->autoAddOkButton = false;
+}
+
+/*!
+ \since 4.2
+ \overload
+
+ Creates a button with the given \a text, adds it to the message box for the
+ specified \a role, and returns it.
+*/
+QPushButton *QMessageBox::addButton(const QString& text, ButtonRole role)
+{
+ Q_D(QMessageBox);
+ QPushButton *pushButton = new QPushButton(text);
+ addButton(pushButton, role);
+ d->updateSize();
+ return pushButton;
+}
+
+/*!
+ \since 4.2
+ \overload
+
+ Adds a standard \a button to the message box if it is valid to do so, and
+ returns the push button.
+
+ \sa setStandardButtons()
+*/
+QPushButton *QMessageBox::addButton(StandardButton button)
+{
+ Q_D(QMessageBox);
+ QPushButton *pushButton = d->buttonBox->addButton((QDialogButtonBox::StandardButton)button);
+ if (pushButton)
+ d->autoAddOkButton = false;
+ return pushButton;
+}
+
+/*!
+ \since 4.2
+
+ Removes \a button from the button box without deleting it.
+
+ \sa addButton(), setStandardButtons()
+*/
+void QMessageBox::removeButton(QAbstractButton *button)
+{
+ Q_D(QMessageBox);
+ d->customButtonList.removeAll(button);
+ if (d->escapeButton == button)
+ d->escapeButton = 0;
+ if (d->defaultButton == button)
+ d->defaultButton = 0;
+ d->buttonBox->removeButton(button);
+ d->updateSize();
+}
+
+/*!
+ \property QMessageBox::standardButtons
+ \brief collection of standard buttons in the message box
+ \since 4.2
+
+ This property controls which standard buttons are used by the message box.
+
+ By default, this property contains no standard buttons.
+
+ \sa addButton()
+*/
+void QMessageBox::setStandardButtons(StandardButtons buttons)
+{
+ Q_D(QMessageBox);
+ d->buttonBox->setStandardButtons(QDialogButtonBox::StandardButtons(int(buttons)));
+
+ QList<QAbstractButton *> buttonList = d->buttonBox->buttons();
+ if (!buttonList.contains(d->escapeButton))
+ d->escapeButton = 0;
+ if (!buttonList.contains(d->defaultButton))
+ d->defaultButton = 0;
+ d->autoAddOkButton = false;
+ d->updateSize();
+}
+
+QMessageBox::StandardButtons QMessageBox::standardButtons() const
+{
+ Q_D(const QMessageBox);
+ return QMessageBox::StandardButtons(int(d->buttonBox->standardButtons()));
+}
+
+/*!
+ \since 4.2
+
+ Returns the standard button enum value corresponding to the given \a button,
+ or NoButton if the given \a button isn't a standard button.
+
+ \sa button(), standardButtons()
+*/
+QMessageBox::StandardButton QMessageBox::standardButton(QAbstractButton *button) const
+{
+ Q_D(const QMessageBox);
+ return (QMessageBox::StandardButton)d->buttonBox->standardButton(button);
+}
+
+/*!
+ \since 4.2
+
+ Returns a pointer corresponding to the standard button \a which,
+ or 0 if the standard button doesn't exist in this message box.
+
+ \sa standardButtons, standardButton()
+*/
+QAbstractButton *QMessageBox::button(StandardButton which) const
+{
+ Q_D(const QMessageBox);
+ return d->buttonBox->button(QDialogButtonBox::StandardButton(which));
+}
+
+/*!
+ \since 4.2
+
+ Returns the button that is activated when escape is pressed.
+
+ By default, QMessageBox attempts to automatically detect an
+ escape button as follows:
+
+ \list 1
+ \o If there is only one button, it is made the escape button.
+ \o If there is a \l Cancel button, it is made the escape button.
+ \o On Mac OS X only, if there is exactly one button with the role
+ QMessageBox::RejectRole, it is made the escape button.
+ \endlist
+
+ When an escape button could not be automatically detected, pressing
+ \key Esc has no effect.
+
+ \sa addButton()
+*/
+QAbstractButton *QMessageBox::escapeButton() const
+{
+ Q_D(const QMessageBox);
+ return d->escapeButton;
+}
+
+/*!
+ \since 4.2
+
+ Sets the button that gets activated when the \key Escape key is
+ pressed to \a button.
+
+ \sa addButton(), clickedButton()
+*/
+void QMessageBox::setEscapeButton(QAbstractButton *button)
+{
+ Q_D(QMessageBox);
+ if (d->buttonBox->buttons().contains(button))
+ d->escapeButton = button;
+}
+
+/*!
+ \since 4.3
+
+ Sets the buttons that gets activated when the \key Escape key is
+ pressed to \a button.
+
+ \sa addButton(), clickedButton()
+*/
+void QMessageBox::setEscapeButton(QMessageBox::StandardButton button)
+{
+ Q_D(QMessageBox);
+ setEscapeButton(d->buttonBox->button(QDialogButtonBox::StandardButton(button)));
+}
+
+void QMessageBoxPrivate::detectEscapeButton()
+{
+ if (escapeButton) { // escape button explicitly set
+ detectedEscapeButton = escapeButton;
+ return;
+ }
+
+ // Cancel button automatically becomes escape button
+ detectedEscapeButton = buttonBox->button(QDialogButtonBox::Cancel);
+ if (detectedEscapeButton)
+ return;
+
+ // If there is only one button, make it the escape button
+ const QList<QAbstractButton *> buttons = buttonBox->buttons();
+ if (buttons.count() == 1) {
+ detectedEscapeButton = buttons.first();
+ return;
+ }
+
+ // if the message box has one RejectRole button, make it the escape button
+ for (int i = 0; i < buttons.count(); i++) {
+ if (buttonBox->buttonRole(buttons.at(i)) == QDialogButtonBox::RejectRole) {
+ if (detectedEscapeButton) { // already detected!
+ detectedEscapeButton = 0;
+ break;
+ }
+ detectedEscapeButton = buttons.at(i);
+ }
+ }
+ if (detectedEscapeButton)
+ return;
+
+ // if the message box has one NoRole button, make it the escape button
+ for (int i = 0; i < buttons.count(); i++) {
+ if (buttonBox->buttonRole(buttons.at(i)) == QDialogButtonBox::NoRole) {
+ if (detectedEscapeButton) { // already detected!
+ detectedEscapeButton = 0;
+ break;
+ }
+ detectedEscapeButton = buttons.at(i);
+ }
+ }
+}
+
+/*!
+ \since 4.2
+
+ Returns the button that was clicked by the user,
+ or 0 if the user hit the \key Esc key and
+ no \l{setEscapeButton()}{escape button} was set.
+
+ If exec() hasn't been called yet, returns 0.
+
+ Example:
+
+ \snippet doc/src/snippets/code/src_gui_dialogs_qmessagebox.cpp 3
+
+ \sa standardButton(), button()
+*/
+QAbstractButton *QMessageBox::clickedButton() const
+{
+ Q_D(const QMessageBox);
+ return d->clickedButton;
+}
+
+/*!
+ \since 4.2
+
+ Returns the button that should be the message box's
+ \l{QPushButton::setDefault()}{default button}. Returns 0
+ if no default button was set.
+
+ \sa addButton(), QPushButton::setDefault()
+*/
+QPushButton *QMessageBox::defaultButton() const
+{
+ Q_D(const QMessageBox);
+ return d->defaultButton;
+}
+
+/*!
+ \since 4.2
+
+ Sets the message box's \l{QPushButton::setDefault()}{default button}
+ to \a button.
+
+ \sa addButton(), QPushButton::setDefault()
+*/
+void QMessageBox::setDefaultButton(QPushButton *button)
+{
+ Q_D(QMessageBox);
+ if (!d->buttonBox->buttons().contains(button))
+ return;
+ d->defaultButton = button;
+ button->setDefault(true);
+ button->setFocus();
+}
+
+/*!
+ \since 4.3
+
+ Sets the message box's \l{QPushButton::setDefault()}{default button}
+ to \a button.
+
+ \sa addButton(), QPushButton::setDefault()
+*/
+void QMessageBox::setDefaultButton(QMessageBox::StandardButton button)
+{
+ Q_D(QMessageBox);
+ setDefaultButton(d->buttonBox->button(QDialogButtonBox::StandardButton(button)));
+}
+
+/*!
+ \property QMessageBox::text
+ \brief the message box text to be displayed.
+
+ The text will be interpreted either as a plain text or as rich text,
+ depending on the text format setting (\l QMessageBox::textFormat).
+ The default setting is Qt::AutoText, i.e., the message box will try
+ to auto-detect the format of the text.
+
+ The default value of this property is an empty string.
+
+ \sa textFormat, QMessageBox::informativeText, QMessageBox::detailedText
+*/
+QString QMessageBox::text() const
+{
+ Q_D(const QMessageBox);
+ return d->label->text();
+}
+
+void QMessageBox::setText(const QString &text)
+{
+ Q_D(QMessageBox);
+ d->label->setText(text);
+ d->label->setWordWrap(d->label->textFormat() == Qt::RichText
+ || (d->label->textFormat() == Qt::AutoText && Qt::mightBeRichText(text)));
+ d->updateSize();
+}
+
+/*!
+ \enum QMessageBox::Icon
+
+ This enum has the following values:
+
+ \value NoIcon the message box does not have any icon.
+
+ \value Question an icon indicating that
+ the message is asking a question.
+
+ \value Information an icon indicating that
+ the message is nothing out of the ordinary.
+
+ \value Warning an icon indicating that the
+ message is a warning, but can be dealt with.
+
+ \value Critical an icon indicating that
+ the message represents a critical problem.
+
+*/
+
+/*!
+ \property QMessageBox::icon
+ \brief the message box's icon
+
+ The icon of the message box can be specified with one of the
+ values:
+
+ \list
+ \o QMessageBox::NoIcon
+ \o QMessageBox::Question
+ \o QMessageBox::Information
+ \o QMessageBox::Warning
+ \o QMessageBox::Critical
+ \endlist
+
+ The default is QMessageBox::NoIcon.
+
+ The pixmap used to display the actual icon depends on the current
+ \l{QWidget::style()} {GUI style}. You can also set a custom pixmap
+ for the icon by setting the \l{QMessageBox::iconPixmap} {icon
+ pixmap} property.
+
+ \sa iconPixmap
+*/
+QMessageBox::Icon QMessageBox::icon() const
+{
+ Q_D(const QMessageBox);
+ return d->icon;
+}
+
+void QMessageBox::setIcon(Icon icon)
+{
+ Q_D(QMessageBox);
+ setIconPixmap(QMessageBoxPrivate::standardIcon((QMessageBox::Icon)icon,
+ this));
+ d->icon = icon;
+}
+
+/*!
+ \property QMessageBox::iconPixmap
+ \brief the current icon
+
+ The icon currently used by the message box. Note that it's often
+ hard to draw one pixmap that looks appropriate in all GUI styles;
+ you may want to supply a different pixmap for each platform.
+
+ By default, this property is undefined.
+
+ \sa icon
+*/
+QPixmap QMessageBox::iconPixmap() const
+{
+ Q_D(const QMessageBox);
+ if (d->iconLabel && d->iconLabel->pixmap())
+ return *d->iconLabel->pixmap();
+ return QPixmap();
+}
+
+void QMessageBox::setIconPixmap(const QPixmap &pixmap)
+{
+ Q_D(QMessageBox);
+ d->iconLabel->setPixmap(pixmap);
+ d->updateSize();
+ d->icon = NoIcon;
+}
+
+/*!
+ \property QMessageBox::textFormat
+ \brief the format of the text displayed by the message box
+
+ The current text format used by the message box. See the \l
+ Qt::TextFormat enum for an explanation of the possible options.
+
+ The default format is Qt::AutoText.
+
+ \sa setText()
+*/
+Qt::TextFormat QMessageBox::textFormat() const
+{
+ Q_D(const QMessageBox);
+ return d->label->textFormat();
+}
+
+void QMessageBox::setTextFormat(Qt::TextFormat format)
+{
+ Q_D(QMessageBox);
+ d->label->setTextFormat(format);
+ d->label->setWordWrap(format == Qt::RichText
+ || (format == Qt::AutoText && Qt::mightBeRichText(d->label->text())));
+ d->updateSize();
+}
+
+/*!
+ \reimp
+*/
+bool QMessageBox::event(QEvent *e)
+{
+ bool result =QDialog::event(e);
+ switch (e->type()) {
+ case QEvent::LayoutRequest:
+ d_func()->updateSize();
+ break;
+ case QEvent::LanguageChange:
+ d_func()->retranslateStrings();
+ break;
+#ifdef Q_OS_WINCE
+ case QEvent::OkRequest:
+ case QEvent::HelpRequest: {
+ QString bName =
+ (e->type() == QEvent::OkRequest)
+ ? qApp->translate("QMessageBox", "OK")
+ : qApp->translate("QMessageBox", "Help");
+ QList<QPushButton*> list = qFindChildren<QPushButton*>(this);
+ for (int i=0; i<list.size(); ++i) {
+ QPushButton *pb = list.at(i);
+ if (pb->text() == bName) {
+ if (pb->isEnabled())
+ pb->click();
+ return pb->isEnabled();
+ }
+ }
+ }
+#endif
+ default:
+ break;
+ }
+ return result;
+}
+
+/*!
+ \reimp
+*/
+void QMessageBox::resizeEvent(QResizeEvent *event)
+{
+ QDialog::resizeEvent(event);
+}
+
+/*!
+ \reimp
+*/
+void QMessageBox::closeEvent(QCloseEvent *e)
+{
+ Q_D(QMessageBox);
+ if (!d->detectedEscapeButton) {
+ e->ignore();
+ return;
+ }
+ QDialog::closeEvent(e);
+ d->clickedButton = d->detectedEscapeButton;
+ setResult(d->execReturnCode(d->detectedEscapeButton));
+}
+
+/*!
+ \reimp
+*/
+void QMessageBox::changeEvent(QEvent *ev)
+{
+ Q_D(QMessageBox);
+ switch (ev->type()) {
+ case QEvent::StyleChange:
+ {
+ if (d->icon != NoIcon)
+ setIcon(d->icon);
+ Qt::TextInteractionFlags flags(style()->styleHint(QStyle::SH_MessageBox_TextInteractionFlags, 0, this));
+ d->label->setTextInteractionFlags(flags);
+ d->buttonBox->setCenterButtons(style()->styleHint(QStyle::SH_MessageBox_CenterButtons, 0, this));
+ if (d->informativeLabel)
+ d->informativeLabel->setTextInteractionFlags(flags);
+ // intentional fall through
+ }
+ case QEvent::FontChange:
+ case QEvent::ApplicationFontChange:
+#ifdef Q_WS_MAC
+ {
+ QFont f = font();
+ f.setBold(true);
+ d->label->setFont(f);
+ }
+#endif
+ default:
+ break;
+ }
+ QDialog::changeEvent(ev);
+}
+
+/*!
+ \reimp
+*/
+void QMessageBox::keyPressEvent(QKeyEvent *e)
+{
+ Q_D(QMessageBox);
+ if (e->key() == Qt::Key_Escape
+#ifdef Q_WS_MAC
+ || (e->modifiers() == Qt::ControlModifier && e->key() == Qt::Key_Period)
+#endif
+ ) {
+ if (d->detectedEscapeButton) {
+#ifdef Q_WS_MAC
+ d->detectedEscapeButton->animateClick();
+#else
+ d->detectedEscapeButton->click();
+#endif
+ }
+ return;
+ }
+
+#if defined (Q_OS_WIN) && !defined(QT_NO_CLIPBOARD) && !defined(QT_NO_SHORTCUT)
+ if (e == QKeySequence::Copy) {
+ QString separator = QString::fromLatin1("---------------------------\n");
+ QString textToCopy = separator;
+ separator.prepend(QLatin1String("\n"));
+ textToCopy += windowTitle() + separator; // title
+ textToCopy += d->label->text() + separator; // text
+
+ if (d->informativeLabel)
+ textToCopy += d->informativeLabel->text() + separator;
+
+ QString buttonTexts;
+ QList<QAbstractButton *> buttons = d->buttonBox->buttons();
+ for (int i = 0; i < buttons.count(); i++) {
+ buttonTexts += buttons[i]->text() + QLatin1String(" ");
+ }
+ textToCopy += buttonTexts + separator;
+
+ qApp->clipboard()->setText(textToCopy);
+ return;
+ }
+#endif //QT_NO_SHORTCUT QT_NO_CLIPBOARD Q_OS_WIN
+
+#ifndef QT_NO_SHORTCUT
+ if (!(e->modifiers() & Qt::AltModifier)) {
+ int key = e->key() & ~((int)Qt::MODIFIER_MASK|(int)Qt::UNICODE_ACCEL);
+ if (key) {
+ const QList<QAbstractButton *> buttons = d->buttonBox->buttons();
+ for (int i = 0; i < buttons.count(); ++i) {
+ QAbstractButton *pb = buttons.at(i);
+ int acc = pb->shortcut() & ~((int)Qt::MODIFIER_MASK|(int)Qt::UNICODE_ACCEL);
+ if (acc == key) {
+ pb->animateClick();
+ return;
+ }
+ }
+ }
+ }
+#endif
+ QDialog::keyPressEvent(e);
+}
+
+#ifdef Q_OS_WINCE
+/*!
+ \reimp
+*/
+void QMessageBox::setVisible(bool visible)
+{
+ Q_D(QMessageBox);
+ if (visible)
+ d->hideSpecial();
+ QDialog::setVisible(visible);
+}
+#endif
+
+
+/*!
+ \overload
+
+ Opens the dialog and connects its accepted() signal to the slot specified
+ by \a receiver and \a member.
+
+ The signal will be disconnected from the slot when the dialog is closed.
+*/
+void QMessageBox::open(QObject *receiver, const char *member)
+{
+ Q_D(QMessageBox);
+ const char *signal = member && strchr(member, '*') ? SIGNAL(buttonClicked(QAbstractButton*))
+ : SIGNAL(finished(int));
+ connect(this, signal, receiver, member);
+ d->signalToDisconnectOnClose = signal;
+ d->receiverToDisconnectOnClose = receiver;
+ d->memberToDisconnectOnClose = member;
+ QDialog::open();
+}
+
+/*!
+ \since 4.5
+
+ Returns a list of all the buttons that have been added to the message box.
+
+ \sa buttonRole(), addButton(), removeButton()
+*/
+QList<QAbstractButton *> QMessageBox::buttons() const
+{
+ Q_D(const QMessageBox);
+ return d->buttonBox->buttons();
+}
+
+/*!
+ \since 4.5
+
+ Returns the button role for the specified \a button. This function returns
+ \l InvalidRole if \a button is 0 or has not been added to the message box.
+
+ \sa buttons(), addButton()
+*/
+QMessageBox::ButtonRole QMessageBox::buttonRole(QAbstractButton *button) const
+{
+ Q_D(const QMessageBox);
+ return QMessageBox::ButtonRole(d->buttonBox->buttonRole(button));
+}
+
+/*!
+ \reimp
+*/
+void QMessageBox::showEvent(QShowEvent *e)
+{
+ Q_D(QMessageBox);
+ if (d->autoAddOkButton) {
+ addButton(Ok);
+#if defined(Q_OS_WINCE)
+ d->hideSpecial();
+#endif
+ }
+ if (d->detailsButton)
+ addButton(d->detailsButton, QMessageBox::ActionRole);
+ d->detectEscapeButton();
+ d->updateSize();
+
+#ifndef QT_NO_ACCESSIBILITY
+ QAccessible::updateAccessibility(this, 0, QAccessible::Alert);
+#endif
+#ifdef Q_WS_WIN
+ HMENU systemMenu = GetSystemMenu((HWND)winId(), FALSE);
+ if (!d->detectedEscapeButton) {
+ EnableMenuItem(systemMenu, SC_CLOSE, MF_BYCOMMAND|MF_GRAYED);
+ }
+ else {
+ EnableMenuItem(systemMenu, SC_CLOSE, MF_BYCOMMAND|MF_ENABLED);
+ }
+#endif
+ QDialog::showEvent(e);
+}
+
+
+static QMessageBox::StandardButton showNewMessageBox(QWidget *parent,
+ QMessageBox::Icon icon,
+ const QString& title, const QString& text,
+ QMessageBox::StandardButtons buttons,
+ QMessageBox::StandardButton defaultButton)
+{
+ // necessary for source compatibility with Qt 4.0 and 4.1
+ // handles (Yes, No) and (Yes|Default, No)
+ if (defaultButton && !(buttons & defaultButton))
+ return (QMessageBox::StandardButton)
+ QMessageBoxPrivate::showOldMessageBox(parent, icon, title,
+ text, int(buttons),
+ int(defaultButton), 0);
+
+ QMessageBox msgBox(icon, title, text, QMessageBox::NoButton, parent);
+ QDialogButtonBox *buttonBox = qFindChild<QDialogButtonBox*>(&msgBox);
+ Q_ASSERT(buttonBox != 0);
+
+ uint mask = QMessageBox::FirstButton;
+ while (mask <= QMessageBox::LastButton) {
+ uint sb = buttons & mask;
+ mask <<= 1;
+ if (!sb)
+ continue;
+ QPushButton *button = msgBox.addButton((QMessageBox::StandardButton)sb);
+ // Choose the first accept role as the default
+ if (msgBox.defaultButton())
+ continue;
+ if ((defaultButton == QMessageBox::NoButton && buttonBox->buttonRole(button) == QDialogButtonBox::AcceptRole)
+ || (defaultButton != QMessageBox::NoButton && sb == uint(defaultButton)))
+ msgBox.setDefaultButton(button);
+ }
+ if (msgBox.exec() == -1)
+ return QMessageBox::Cancel;
+ return msgBox.standardButton(msgBox.clickedButton());
+}
+
+/*!
+ \since 4.2
+
+ Opens an information message box with the specified \a title and
+ \a text. The standard \a buttons are added to the message box. \a
+ defaultButton specifies the button used when \key Enter is
+ pressed. \a defaultButton must refer to a button that was given in \a buttons.
+ If \a defaultButton is QMessageBox::NoButton, QMessageBox
+ chooses a suitable default automatically.
+
+ Returns the identity of the standard button that was clicked. If
+ \key Esc was pressed instead, the \l{Default and Escape Keys}
+ {escape button} is returned.
+
+ If \a parent is 0, the message box is an \l{Qt::ApplicationModal}
+ {application modal} dialog box. If \a parent is a widget, the
+ message box is \l{Qt::WindowModal} {window modal} relative to \a
+ parent.
+
+ \sa question(), warning(), critical()
+*/
+QMessageBox::StandardButton QMessageBox::information(QWidget *parent, const QString &title,
+ const QString& text, StandardButtons buttons,
+ StandardButton defaultButton)
+{
+ return showNewMessageBox(parent, Information, title, text, buttons,
+ defaultButton);
+}
+
+
+/*!
+ \since 4.2
+
+ Opens a question message box with the specified \a title and \a
+ text. The standard \a buttons are added to the message box. \a
+ defaultButton specifies the button used when \key Enter is
+ pressed. \a defaultButton must refer to a button that was given in \a buttons.
+ If \a defaultButton is QMessageBox::NoButton, QMessageBox
+ chooses a suitable default automatically.
+
+ Returns the identity of the standard button that was clicked. If
+ \key Esc was pressed instead, the \l{Default and Escape Keys}
+ {escape button} is returned.
+
+ If \a parent is 0, the message box is an \l{Qt::ApplicationModal}
+ {application modal} dialog box. If \a parent is a widget, the
+ message box is \l{Qt::WindowModal} {window modal} relative to \a
+ parent.
+
+ \sa information(), warning(), critical()
+*/
+QMessageBox::StandardButton QMessageBox::question(QWidget *parent, const QString &title,
+ const QString& text, StandardButtons buttons,
+ StandardButton defaultButton)
+{
+ return showNewMessageBox(parent, Question, title, text, buttons, defaultButton);
+}
+
+/*!
+ \since 4.2
+
+ Opens a warning message box with the specified \a title and \a
+ text. The standard \a buttons are added to the message box. \a
+ defaultButton specifies the button used when \key Enter is
+ pressed. \a defaultButton must refer to a button that was given in \a buttons.
+ If \a defaultButton is QMessageBox::NoButton, QMessageBox
+ chooses a suitable default automatically.
+
+ Returns the identity of the standard button that was clicked. If
+ \key Esc was pressed instead, the \l{Default and Escape Keys}
+ {escape button} is returned.
+
+ If \a parent is 0, the message box is an \l{Qt::ApplicationModal}
+ {application modal} dialog box. If \a parent is a widget, the
+ message box is \l{Qt::WindowModal} {window modal} relative to \a
+ parent.
+
+ \sa question(), information(), critical()
+*/
+QMessageBox::StandardButton QMessageBox::warning(QWidget *parent, const QString &title,
+ const QString& text, StandardButtons buttons,
+ StandardButton defaultButton)
+{
+ return showNewMessageBox(parent, Warning, title, text, buttons, defaultButton);
+}
+
+/*!
+ \since 4.2
+
+ Opens a critical message box with the specified \a title and \a
+ text. The standard \a buttons are added to the message box. \a
+ defaultButton specifies the button used when \key Enter is
+ pressed. \a defaultButton must refer to a button that was given in \a buttons.
+ If \a defaultButton is QMessageBox::NoButton, QMessageBox
+ chooses a suitable default automatically.
+
+ Returns the identity of the standard button that was clicked. If
+ \key Esc was pressed instead, the \l{Default and Escape Keys}
+ {escape button} is returned.
+
+ If \a parent is 0, the message box is an \l{Qt::ApplicationModal}
+ {application modal} dialog box. If \a parent is a widget, the
+ message box is \l{Qt::WindowModal} {window modal} relative to \a
+ parent.
+
+ \warning Do not delete \a parent during the execution of the dialog.
+ If you want to do this, you should create the dialog
+ yourself using one of the QMessageBox constructors.
+
+ \sa question(), warning(), information()
+*/
+QMessageBox::StandardButton QMessageBox::critical(QWidget *parent, const QString &title,
+ const QString& text, StandardButtons buttons,
+ StandardButton defaultButton)
+{
+ return showNewMessageBox(parent, Critical, title, text, buttons, defaultButton);
+}
+
+/*!
+ Displays a simple about box with title \a title and text \a
+ text. The about box's parent is \a parent.
+
+ about() looks for a suitable icon in four locations:
+
+ \list 1
+ \o It prefers \link QWidget::windowIcon() parent->icon() \endlink
+ if that exists.
+ \o If not, it tries the top-level widget containing \a parent.
+ \o If that fails, it tries the \link
+ QApplication::activeWindow() active window. \endlink
+ \o As a last resort it uses the Information icon.
+ \endlist
+
+ The about box has a single button labelled "OK". On Mac OS X, the
+ about box is popped up as a modeless window; on other platforms,
+ it is currently a window modal.
+
+ \sa QWidget::windowIcon(), QApplication::activeWindow()
+*/
+void QMessageBox::about(QWidget *parent, const QString &title, const QString &text)
+{
+#ifdef Q_WS_MAC
+ static QPointer<QMessageBox> oldMsgBox;
+
+ if (oldMsgBox && oldMsgBox->text() == text) {
+ oldMsgBox->show();
+ oldMsgBox->raise();
+ oldMsgBox->activateWindow();
+ return;
+ }
+#endif
+
+ QMessageBox *msgBox = new QMessageBox(title, text, Information, 0, 0, 0, parent
+#ifdef Q_WS_MAC
+ , Qt::WindowTitleHint | Qt::WindowSystemMenuHint
+#endif
+ );
+ msgBox->setAttribute(Qt::WA_DeleteOnClose);
+ QIcon icon = msgBox->windowIcon();
+ QSize size = icon.actualSize(QSize(64, 64));
+ msgBox->setIconPixmap(icon.pixmap(size));
+
+ // should perhaps be a style hint
+#ifdef Q_WS_MAC
+ oldMsgBox = msgBox;
+#if 0
+ // ### doesn't work until close button is enabled in title bar
+ msgBox->d_func()->autoAddOkButton = false;
+#else
+ msgBox->d_func()->buttonBox->setCenterButtons(true);
+#endif
+ msgBox->show();
+#else
+ msgBox->exec();
+#endif
+}
+
+/*!
+ Displays a simple message box about Qt, with the given \a title
+ and centered over \a parent (if \a parent is not 0). The message
+ includes the version number of Qt being used by the application.
+
+ This is useful for inclusion in the \gui Help menu of an application,
+ as shown in the \l{mainwindows/menus}{Menus} example.
+
+ QApplication provides this functionality as a slot.
+
+ On Mac OS X, the about box is popped up as a modeless window; on
+ other platforms, it is currently window modal.
+
+ \sa QApplication::aboutQt()
+*/
+void QMessageBox::aboutQt(QWidget *parent, const QString &title)
+{
+#ifdef Q_WS_MAC
+ static QPointer<QMessageBox> oldMsgBox;
+
+ if (oldMsgBox) {
+ oldMsgBox->show();
+ oldMsgBox->raise();
+ oldMsgBox->activateWindow();
+ return;
+ }
+#endif
+
+ QString translatedTextAboutQt;
+ translatedTextAboutQt = QMessageBox::tr(
+ "<h3>About Qt</h3>"
+ "%1<p>Qt is a C++ toolkit for cross-platform "
+ "application development.</p>"
+ "<p>Qt provides single-source "
+ "portability across MS&nbsp;Windows, Mac&nbsp;OS&nbsp;X, "
+ "Linux, and all major commercial Unix variants. Qt is also"
+ " available for embedded devices as Qt for Embedded Linux"
+ " and Qt for Windows CE.</p>"
+ "<p>Qt is a Nokia product. See "
+ "<a href=\"http://qtsoftware.com/qt/\">qtsoftware.com/qt/</a> for more information.</p>"
+ )
+#if QT_EDITION != QT_EDITION_OPENSOURCE
+ .arg(QMessageBox::tr("<p>This program uses Qt version %1.</p>"))
+#else
+ .arg(QMessageBox::tr("<p>This program uses Qt Open Source Edition version %1.</p>"
+ "<p>Qt Open Source Edition is intended for the development "
+ "of Open Source applications. You need a commercial Qt "
+ "license for development of proprietary (closed source) "
+ "applications.</p>"
+ "<p>Please see <a href=\"http://qtsoftware.com/company/model/\">qtsoftware.com/company/model/</a> "
+ "for an overview of Qt licensing.</p>"))
+#endif
+
+ .arg(QLatin1String(QT_VERSION_STR));
+
+ QMessageBox *msgBox = new QMessageBox(parent);
+ msgBox->setAttribute(Qt::WA_DeleteOnClose);
+ msgBox->setWindowTitle(title.isEmpty() ? tr("About Qt") : title);
+ msgBox->setText(translatedTextAboutQt);
+
+ QPixmap pm(QLatin1String(":/trolltech/qmessagebox/images/qtlogo-64.png"));
+ if (!pm.isNull())
+ msgBox->setIconPixmap(pm);
+#if defined(Q_OS_WINCE)
+ msgBox->setDefaultButton(msgBox->addButton(QMessageBox::Ok));
+#endif
+
+ // should perhaps be a style hint
+#ifdef Q_WS_MAC
+ oldMsgBox = msgBox;
+#if 0
+ // ### doesn't work until close button is enabled in title bar
+ msgBox->d_func()->autoAddOkButton = false;
+#else
+ msgBox->d_func()->buttonBox->setCenterButtons(true);
+#endif
+ msgBox->show();
+#else
+ msgBox->exec();
+#endif
+}
+
+/*!
+ \internal
+*/
+QSize QMessageBox::sizeHint() const
+{
+ // ### Qt 5: remove
+ return QDialog::sizeHint();
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Source and binary compatibility routines for 4.0 and 4.1
+
+static QMessageBox::StandardButton newButton(int button)
+{
+ // this is needed for source compatibility with Qt 4.0 and 4.1
+ if (button == QMessageBox::NoButton || (button & NewButtonMask))
+ return QMessageBox::StandardButton(button & QMessageBox::ButtonMask);
+
+#if QT_VERSION < 0x050000
+ // this is needed for binary compatibility with Qt 4.0 and 4.1
+ switch (button & Old_ButtonMask) {
+ case Old_Ok:
+ return QMessageBox::Ok;
+ case Old_Cancel:
+ return QMessageBox::Cancel;
+ case Old_Yes:
+ return QMessageBox::Yes;
+ case Old_No:
+ return QMessageBox::No;
+ case Old_Abort:
+ return QMessageBox::Abort;
+ case Old_Retry:
+ return QMessageBox::Retry;
+ case Old_Ignore:
+ return QMessageBox::Ignore;
+ case Old_YesAll:
+ return QMessageBox::YesToAll;
+ case Old_NoAll:
+ return QMessageBox::NoToAll;
+ default:
+ return QMessageBox::NoButton;
+ }
+#endif
+}
+
+static bool detectedCompat(int button0, int button1, int button2)
+{
+ if (button0 != 0 && !(button0 & NewButtonMask))
+ return true;
+ if (button1 != 0 && !(button1 & NewButtonMask))
+ return true;
+ if (button2 != 0 && !(button2 & NewButtonMask))
+ return true;
+ return false;
+}
+
+QAbstractButton *QMessageBoxPrivate::findButton(int button0, int button1, int button2, int flags)
+{
+ Q_Q(QMessageBox);
+ int button = 0;
+
+ if (button0 & flags) {
+ button = button0;
+ } else if (button1 & flags) {
+ button = button1;
+ } else if (button2 & flags) {
+ button = button2;
+ }
+ return q->button(newButton(button));
+}
+
+void QMessageBoxPrivate::addOldButtons(int button0, int button1, int button2)
+{
+ Q_Q(QMessageBox);
+ q->addButton(newButton(button0));
+ q->addButton(newButton(button1));
+ q->addButton(newButton(button2));
+ q->setDefaultButton(
+ static_cast<QPushButton *>(findButton(button0, button1, button2, QMessageBox::Default)));
+ q->setEscapeButton(findButton(button0, button1, button2, QMessageBox::Escape));
+ compatMode = detectedCompat(button0, button1, button2);
+}
+
+QAbstractButton *QMessageBoxPrivate::abstractButtonForId(int id) const
+{
+ Q_Q(const QMessageBox);
+ QAbstractButton *result = customButtonList.value(id);
+ if (result)
+ return result;
+ if (id & QMessageBox::FlagMask) // for compatibility with Qt 4.0/4.1 (even if it is silly)
+ return 0;
+ return q->button(newButton(id));
+}
+
+int QMessageBoxPrivate::showOldMessageBox(QWidget *parent, QMessageBox::Icon icon,
+ const QString &title, const QString &text,
+ int button0, int button1, int button2)
+{
+ QMessageBox messageBox(icon, title, text, QMessageBox::NoButton, parent);
+ messageBox.d_func()->addOldButtons(button0, button1, button2);
+ return messageBox.exec();
+}
+
+int QMessageBoxPrivate::showOldMessageBox(QWidget *parent, QMessageBox::Icon icon,
+ const QString &title, const QString &text,
+ const QString &button0Text,
+ const QString &button1Text,
+ const QString &button2Text,
+ int defaultButtonNumber,
+ int escapeButtonNumber)
+{
+ QMessageBox messageBox(icon, title, text, QMessageBox::NoButton, parent);
+ QString myButton0Text = button0Text;
+ if (myButton0Text.isEmpty())
+ myButton0Text = QDialogButtonBox::tr("OK");
+ messageBox.addButton(myButton0Text, QMessageBox::ActionRole);
+ if (!button1Text.isEmpty())
+ messageBox.addButton(button1Text, QMessageBox::ActionRole);
+ if (!button2Text.isEmpty())
+ messageBox.addButton(button2Text, QMessageBox::ActionRole);
+
+ const QList<QAbstractButton *> &buttonList = messageBox.d_func()->customButtonList;
+ messageBox.setDefaultButton(static_cast<QPushButton *>(buttonList.value(defaultButtonNumber)));
+ messageBox.setEscapeButton(buttonList.value(escapeButtonNumber));
+
+ return messageBox.exec();
+}
+
+void QMessageBoxPrivate::retranslateStrings()
+{
+#ifndef QT_NO_TEXTEDIT
+ if (detailsButton)
+ detailsButton->setText(detailsText->isHidden() ? detailsText->label(HideLabel) : detailsText->label(ShowLabel));
+#endif
+}
+
+/*!
+ \obsolete
+
+ Constructs a message box with a \a title, a \a text, an \a icon,
+ and up to three buttons.
+
+ The \a icon must be one of the following:
+ \list
+ \o QMessageBox::NoIcon
+ \o QMessageBox::Question
+ \o QMessageBox::Information
+ \o QMessageBox::Warning
+ \o QMessageBox::Critical
+ \endlist
+
+ Each button, \a button0, \a button1 and \a button2, can have one
+ of the following values:
+ \list
+ \o QMessageBox::NoButton
+ \o QMessageBox::Ok
+ \o QMessageBox::Cancel
+ \o QMessageBox::Yes
+ \o QMessageBox::No
+ \o QMessageBox::Abort
+ \o QMessageBox::Retry
+ \o QMessageBox::Ignore
+ \o QMessageBox::YesAll
+ \o QMessageBox::NoAll
+ \endlist
+
+ Use QMessageBox::NoButton for the later parameters to have fewer
+ than three buttons in your message box. If you don't specify any
+ buttons at all, QMessageBox will provide an Ok button.
+
+ One of the buttons can be OR-ed with the QMessageBox::Default
+ flag to make it the default button (clicked when Enter is
+ pressed).
+
+ One of the buttons can be OR-ed with the QMessageBox::Escape flag
+ to make it the cancel or close button (clicked when \key Esc is
+ pressed).
+
+ \snippet doc/src/snippets/dialogs/dialogs.cpp 2
+
+ If \a parent is 0, the message box is an \l{Qt::ApplicationModal}
+ {application modal} dialog box. If \a parent is a widget, the
+ message box is \l{Qt::WindowModal} {window modal} relative to \a
+ parent.
+
+ The \a parent and \a f arguments are passed to
+ the QDialog constructor.
+
+ \sa setWindowTitle(), setText(), setIcon()
+*/
+QMessageBox::QMessageBox(const QString &title, const QString &text, Icon icon,
+ int button0, int button1, int button2, QWidget *parent,
+ Qt::WindowFlags f)
+ : QDialog(*new QMessageBoxPrivate, parent,
+ f /*| Qt::MSWindowsFixedSizeDialogHint #### */| Qt::WindowTitleHint | Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint)
+{
+ Q_D(QMessageBox);
+ d->init(title, text);
+ setIcon(icon);
+ d->addOldButtons(button0, button1, button2);
+}
+
+/*!
+ \obsolete
+
+ Opens an information message box with the given \a title and the
+ \a text. The dialog may have up to three buttons. Each of the
+ buttons, \a button0, \a button1 and \a button2 may be set to one
+ of the following values:
+
+ \list
+ \o QMessageBox::NoButton
+ \o QMessageBox::Ok
+ \o QMessageBox::Cancel
+ \o QMessageBox::Yes
+ \o QMessageBox::No
+ \o QMessageBox::Abort
+ \o QMessageBox::Retry
+ \o QMessageBox::Ignore
+ \o QMessageBox::YesAll
+ \o QMessageBox::NoAll
+ \endlist
+
+ If you don't want all three buttons, set the last button, or last
+ two buttons to QMessageBox::NoButton.
+
+ One button can be OR-ed with QMessageBox::Default, and one
+ button can be OR-ed with QMessageBox::Escape.
+
+ Returns the identity (QMessageBox::Ok, or QMessageBox::No, etc.)
+ of the button that was clicked.
+
+ If \a parent is 0, the message box is an \l{Qt::ApplicationModal}
+ {application modal} dialog box. If \a parent is a widget, the
+ message box is \l{Qt::WindowModal} {window modal} relative to \a
+ parent.
+
+ \warning Do not delete \a parent during the execution of the dialog.
+ If you want to do this, you should create the dialog
+ yourself using one of the QMessageBox constructors.
+
+ \sa question(), warning(), critical()
+*/
+int QMessageBox::information(QWidget *parent, const QString &title, const QString& text,
+ int button0, int button1, int button2)
+{
+ return QMessageBoxPrivate::showOldMessageBox(parent, Information, title, text,
+ button0, button1, button2);
+}
+
+/*!
+ \obsolete
+ \overload
+
+ Displays an information message box with the given \a title and
+ \a text, as well as one, two or three buttons. Returns the index
+ of the button that was clicked (0, 1 or 2).
+
+ \a button0Text is the text of the first button, and is optional.
+ If \a button0Text is not supplied, "OK" (translated) will be
+ used. \a button1Text is the text of the second button, and is
+ optional. \a button2Text is the text of the third button, and is
+ optional. \a defaultButtonNumber (0, 1 or 2) is the index of the
+ default button; pressing Return or Enter is the same as clicking
+ the default button. It defaults to 0 (the first button). \a
+ escapeButtonNumber is the index of the escape button; pressing
+ \key Esc is the same as clicking this button. It defaults to -1;
+ supply 0, 1 or 2 to make pressing \key Esc equivalent to clicking
+ the relevant button.
+
+ If \a parent is 0, the message box is an \l{Qt::ApplicationModal}
+ {application modal} dialog box. If \a parent is a widget, the
+ message box is \l{Qt::WindowModal} {window modal} relative to \a
+ parent.
+
+ \warning Do not delete \a parent during the execution of the dialog.
+ If you want to do this, you should create the dialog
+ yourself using one of the QMessageBox constructors.
+
+ \sa question(), warning(), critical()
+*/
+
+int QMessageBox::information(QWidget *parent, const QString &title, const QString& text,
+ const QString& button0Text, const QString& button1Text,
+ const QString& button2Text, int defaultButtonNumber,
+ int escapeButtonNumber)
+{
+ return QMessageBoxPrivate::showOldMessageBox(parent, Information, title, text,
+ button0Text, button1Text, button2Text,
+ defaultButtonNumber, escapeButtonNumber);
+}
+
+/*!
+ \obsolete
+
+ Opens a question message box with the given \a title and \a text.
+ The dialog may have up to three buttons. Each of the buttons, \a
+ button0, \a button1 and \a button2 may be set to one of the
+ following values:
+
+ \list
+ \o QMessageBox::NoButton
+ \o QMessageBox::Ok
+ \o QMessageBox::Cancel
+ \o QMessageBox::Yes
+ \o QMessageBox::No
+ \o QMessageBox::Abort
+ \o QMessageBox::Retry
+ \o QMessageBox::Ignore
+ \o QMessageBox::YesAll
+ \o QMessageBox::NoAll
+ \endlist
+
+ If you don't want all three buttons, set the last button, or last
+ two buttons to QMessageBox::NoButton.
+
+ One button can be OR-ed with QMessageBox::Default, and one
+ button can be OR-ed with QMessageBox::Escape.
+
+ Returns the identity (QMessageBox::Yes, or QMessageBox::No, etc.)
+ of the button that was clicked.
+
+ If \a parent is 0, the message box is an \l{Qt::ApplicationModal}
+ {application modal} dialog box. If \a parent is a widget, the
+ message box is \l{Qt::WindowModal} {window modal} relative to \a
+ parent.
+
+ \warning Do not delete \a parent during the execution of the dialog.
+ If you want to do this, you should create the dialog
+ yourself using one of the QMessageBox constructors.
+
+ \sa information(), warning(), critical()
+*/
+int QMessageBox::question(QWidget *parent, const QString &title, const QString& text,
+ int button0, int button1, int button2)
+{
+ return QMessageBoxPrivate::showOldMessageBox(parent, Question, title, text,
+ button0, button1, button2);
+}
+
+/*!
+ \obsolete
+ \overload
+
+ Displays a question message box with the given \a title and \a
+ text, as well as one, two or three buttons. Returns the index of
+ the button that was clicked (0, 1 or 2).
+
+ \a button0Text is the text of the first button, and is optional.
+ If \a button0Text is not supplied, "OK" (translated) will be used.
+ \a button1Text is the text of the second button, and is optional.
+ \a button2Text is the text of the third button, and is optional.
+ \a defaultButtonNumber (0, 1 or 2) is the index of the default
+ button; pressing Return or Enter is the same as clicking the
+ default button. It defaults to 0 (the first button). \a
+ escapeButtonNumber is the index of the Escape button; pressing
+ Escape is the same as clicking this button. It defaults to -1;
+ supply 0, 1 or 2 to make pressing Escape equivalent to clicking
+ the relevant button.
+
+ If \a parent is 0, the message box is an \l{Qt::ApplicationModal}
+ {application modal} dialog box. If \a parent is a widget, the
+ message box is \l{Qt::WindowModal} {window modal} relative to \a
+ parent.
+
+ \warning Do not delete \a parent during the execution of the dialog.
+ If you want to do this, you should create the dialog
+ yourself using one of the QMessageBox constructors.
+
+ \sa information(), warning(), critical()
+*/
+int QMessageBox::question(QWidget *parent, const QString &title, const QString& text,
+ const QString& button0Text, const QString& button1Text,
+ const QString& button2Text, int defaultButtonNumber,
+ int escapeButtonNumber)
+{
+ return QMessageBoxPrivate::showOldMessageBox(parent, Question, title, text,
+ button0Text, button1Text, button2Text,
+ defaultButtonNumber, escapeButtonNumber);
+}
+
+
+/*!
+ \obsolete
+
+ Opens a warning message box with the given \a title and \a text.
+ The dialog may have up to three buttons. Each of the button
+ parameters, \a button0, \a button1 and \a button2 may be set to
+ one of the following values:
+
+ \list
+ \o QMessageBox::NoButton
+ \o QMessageBox::Ok
+ \o QMessageBox::Cancel
+ \o QMessageBox::Yes
+ \o QMessageBox::No
+ \o QMessageBox::Abort
+ \o QMessageBox::Retry
+ \o QMessageBox::Ignore
+ \o QMessageBox::YesAll
+ \o QMessageBox::NoAll
+ \endlist
+
+ If you don't want all three buttons, set the last button, or last
+ two buttons to QMessageBox::NoButton.
+
+ One button can be OR-ed with QMessageBox::Default, and one
+ button can be OR-ed with QMessageBox::Escape.
+
+ Returns the identity (QMessageBox::Ok or QMessageBox::No or ...)
+ of the button that was clicked.
+
+ If \a parent is 0, the message box is an \l{Qt::ApplicationModal}
+ {application modal} dialog box. If \a parent is a widget, the
+ message box is \l{Qt::WindowModal} {window modal} relative to \a
+ parent.
+
+ \warning Do not delete \a parent during the execution of the dialog.
+ If you want to do this, you should create the dialog
+ yourself using one of the QMessageBox constructors.
+
+ \sa information(), question(), critical()
+*/
+int QMessageBox::warning(QWidget *parent, const QString &title, const QString& text,
+ int button0, int button1, int button2)
+{
+ return QMessageBoxPrivate::showOldMessageBox(parent, Warning, title, text,
+ button0, button1, button2);
+}
+
+/*!
+ \obsolete
+ \overload
+
+ Displays a warning message box with the given \a title and \a
+ text, as well as one, two, or three buttons. Returns the number
+ of the button that was clicked (0, 1, or 2).
+
+ \a button0Text is the text of the first button, and is optional.
+ If \a button0Text is not supplied, "OK" (translated) will be used.
+ \a button1Text is the text of the second button, and is optional,
+ and \a button2Text is the text of the third button, and is
+ optional. \a defaultButtonNumber (0, 1 or 2) is the index of the
+ default button; pressing Return or Enter is the same as clicking
+ the default button. It defaults to 0 (the first button). \a
+ escapeButtonNumber is the index of the Escape button; pressing
+ Escape is the same as clicking this button. It defaults to -1;
+ supply 0, 1, or 2 to make pressing Escape equivalent to clicking
+ the relevant button.
+
+ If \a parent is 0, the message box is an \l{Qt::ApplicationModal}
+ {application modal} dialog box. If \a parent is a widget, the
+ message box is \l{Qt::WindowModal} {window modal} relative to \a
+ parent.
+
+ \warning Do not delete \a parent during the execution of the dialog.
+ If you want to do this, you should create the dialog
+ yourself using one of the QMessageBox constructors.
+
+ \sa information(), question(), critical()
+*/
+int QMessageBox::warning(QWidget *parent, const QString &title, const QString& text,
+ const QString& button0Text, const QString& button1Text,
+ const QString& button2Text, int defaultButtonNumber,
+ int escapeButtonNumber)
+{
+ return QMessageBoxPrivate::showOldMessageBox(parent, Warning, title, text,
+ button0Text, button1Text, button2Text,
+ defaultButtonNumber, escapeButtonNumber);
+}
+
+/*!
+ \obsolete
+
+ Opens a critical message box with the given \a title and \a text.
+ The dialog may have up to three buttons. Each of the button
+ parameters, \a button0, \a button1 and \a button2 may be set to
+ one of the following values:
+
+ \list
+ \o QMessageBox::NoButton
+ \o QMessageBox::Ok
+ \o QMessageBox::Cancel
+ \o QMessageBox::Yes
+ \o QMessageBox::No
+ \o QMessageBox::Abort
+ \o QMessageBox::Retry
+ \o QMessageBox::Ignore
+ \o QMessageBox::YesAll
+ \o QMessageBox::NoAll
+ \endlist
+
+ If you don't want all three buttons, set the last button, or last
+ two buttons to QMessageBox::NoButton.
+
+ One button can be OR-ed with QMessageBox::Default, and one
+ button can be OR-ed with QMessageBox::Escape.
+
+ Returns the identity (QMessageBox::Ok, or QMessageBox::No, etc.)
+ of the button that was clicked.
+
+ If \a parent is 0, the message box is an \l{Qt::ApplicationModal}
+ {application modal} dialog box. If \a parent is a widget, the
+ message box is \l{Qt::WindowModal} {window modal} relative to \a
+ parent.
+
+ \warning Do not delete \a parent during the execution of the dialog.
+ If you want to do this, you should create the dialog
+ yourself using one of the QMessageBox constructors.
+
+ \sa information(), question(), warning()
+*/
+
+int QMessageBox::critical(QWidget *parent, const QString &title, const QString& text,
+ int button0, int button1, int button2)
+{
+ return QMessageBoxPrivate::showOldMessageBox(parent, Critical, title, text,
+ button0, button1, button2);
+}
+
+/*!
+ \obsolete
+ \overload
+
+ Displays a critical error message box with the given \a title and
+ \a text, as well as one, two, or three buttons. Returns the
+ number of the button that was clicked (0, 1 or 2).
+
+ \a button0Text is the text of the first button, and is optional.
+ If \a button0Text is not supplied, "OK" (translated) will be used.
+ \a button1Text is the text of the second button, and is optional,
+ and \a button2Text is the text of the third button, and is
+ optional. \a defaultButtonNumber (0, 1 or 2) is the index of the
+ default button; pressing Return or Enter is the same as clicking
+ the default button. It defaults to 0 (the first button). \a
+ escapeButtonNumber is the index of the Escape button; pressing
+ Escape is the same as clicking this button. It defaults to -1;
+ supply 0, 1, or 2 to make pressing Escape equivalent to clicking
+ the relevant button.
+
+ If \a parent is 0, the message box is an \l{Qt::ApplicationModal}
+ {application modal} dialog box. If \a parent is a widget, the
+ message box is \l{Qt::WindowModal} {window modal} relative to \a
+ parent.
+
+ \warning Do not delete \a parent during the execution of the dialog.
+ If you want to do this, you should create the dialog
+ yourself using one of the QMessageBox constructors.
+
+ \sa information(), question(), warning()
+*/
+int QMessageBox::critical(QWidget *parent, const QString &title, const QString& text,
+ const QString& button0Text, const QString& button1Text,
+ const QString& button2Text, int defaultButtonNumber,
+ int escapeButtonNumber)
+{
+ return QMessageBoxPrivate::showOldMessageBox(parent, Critical, title, text,
+ button0Text, button1Text, button2Text,
+ defaultButtonNumber, escapeButtonNumber);
+}
+
+
+/*!
+ \obsolete
+
+ Returns the text of the message box button \a button, or
+ an empty string if the message box does not contain the button.
+
+ Use button() and QPushButton::text() instead.
+*/
+QString QMessageBox::buttonText(int button) const
+{
+ Q_D(const QMessageBox);
+
+ if (QAbstractButton *abstractButton = d->abstractButtonForId(button)) {
+ return abstractButton->text();
+ } else if (d->buttonBox->buttons().isEmpty() && (button == Ok || button == Old_Ok)) {
+ // for compatibility with Qt 4.0/4.1
+ return QDialogButtonBox::tr("OK");
+ }
+ return QString();
+}
+
+/*!
+ \obsolete
+
+ Sets the text of the message box button \a button to \a text.
+ Setting the text of a button that is not in the message box is
+ silently ignored.
+
+ Use addButton() instead.
+*/
+void QMessageBox::setButtonText(int button, const QString &text)
+{
+ Q_D(QMessageBox);
+ if (QAbstractButton *abstractButton = d->abstractButtonForId(button)) {
+ abstractButton->setText(text);
+ } else if (d->buttonBox->buttons().isEmpty() && (button == Ok || button == Old_Ok)) {
+ // for compatibility with Qt 4.0/4.1
+ addButton(QMessageBox::Ok)->setText(text);
+ }
+}
+
+#ifndef QT_NO_TEXTEDIT
+/*!
+ \property QMessageBox::detailedText
+ \brief the text to be displayed in the details area.
+ \since 4.2
+
+ The text will be interpreted as a plain text.
+
+ By default, this property contains an empty string.
+
+ \sa QMessageBox::text, QMessageBox::informativeText
+*/
+QString QMessageBox::detailedText() const
+{
+ Q_D(const QMessageBox);
+ return d->detailsText ? d->detailsText->text() : QString();
+}
+
+void QMessageBox::setDetailedText(const QString &text)
+{
+ Q_D(QMessageBox);
+ if (text.isEmpty()) {
+ delete d->detailsText;
+ d->detailsText = 0;
+ removeButton(d->detailsButton);
+ delete d->detailsButton;
+ d->detailsButton = 0;
+ return;
+ }
+
+ if (!d->detailsText) {
+ d->detailsText = new QMessageBoxDetailsText(this);
+ QGridLayout* grid = qobject_cast<QGridLayout*>(layout());
+ if (grid)
+ grid->addWidget(d->detailsText, grid->rowCount(), 0, 1, grid->columnCount());
+ d->detailsText->hide();
+ }
+ if (!d->detailsButton) {
+ d->detailsButton = new QPushButton(d->detailsText->label(ShowLabel), this);
+ QPushButton hideDetails(d->detailsText->label(HideLabel));
+ d->detailsButton->setFixedSize(d->detailsButton->sizeHint().expandedTo(hideDetails.sizeHint()));
+ }
+ d->detailsText->setText(text);
+}
+#endif // QT_NO_TEXTEDIT
+
+/*!
+ \property QMessageBox::informativeText
+
+ \brief the informative text that provides a fuller description for
+ the message
+
+ \since 4.2
+
+ Infromative text can be used to expand upon the text() to give more
+ information to the user. On the Mac, this text appears in small
+ system font below the text(). On other platforms, it is simply
+ appended to the existing text.
+
+ By default, this property contains an empty string.
+
+ \sa QMessageBox::text, QMessageBox::detailedText
+*/
+QString QMessageBox::informativeText() const
+{
+ Q_D(const QMessageBox);
+ return d->informativeLabel ? d->informativeLabel->text() : QString();
+}
+
+void QMessageBox::setInformativeText(const QString &text)
+{
+ Q_D(QMessageBox);
+ if (text.isEmpty()) {
+ layout()->removeWidget(d->informativeLabel);
+ delete d->informativeLabel;
+ d->informativeLabel = 0;
+#ifndef Q_WS_MAC
+ d->label->setContentsMargins(2, 0, 0, 0);
+#endif
+ d->updateSize();
+ return;
+ }
+
+ if (!d->informativeLabel) {
+ QLabel *label = new QLabel;
+ label->setObjectName(QLatin1String("qt_msgbox_informativelabel"));
+ label->setTextInteractionFlags(Qt::TextInteractionFlags(style()->styleHint(QStyle::SH_MessageBox_TextInteractionFlags, 0, this)));
+ label->setAlignment(Qt::AlignTop | Qt::AlignLeft);
+ label->setOpenExternalLinks(true);
+ label->setWordWrap(true);
+#ifndef Q_WS_MAC
+ d->label->setContentsMargins(2, 0, 0, 0);
+ label->setContentsMargins(2, 0, 0, 6);
+ label->setIndent(9);
+#else
+ label->setContentsMargins(16, 0, 0, 0);
+ // apply a smaller font the information label on the mac
+ label->setFont(qt_app_fonts_hash()->value("QTipLabel"));
+#endif
+ label->setWordWrap(true);
+ QGridLayout *grid = static_cast<QGridLayout *>(layout());
+ grid->addWidget(label, 1, 1, 1, 1);
+ d->informativeLabel = label;
+ }
+ d->informativeLabel->setText(text);
+ d->updateSize();
+}
+
+/*!
+ \since 4.2
+
+ This function shadows QWidget::setWindowTitle().
+
+ Sets the title of the message box to \a title. On Mac OS X,
+ the window title is ignored (as required by the Mac OS X
+ Guidelines).
+*/
+void QMessageBox::setWindowTitle(const QString &title)
+{
+ // Message boxes on the mac do not have a title
+#ifndef Q_WS_MAC
+ QDialog::setWindowTitle(title);
+#else
+ Q_UNUSED(title);
+#endif
+}
+
+
+/*!
+ \since 4.2
+
+ This function shadows QWidget::setWindowModality().
+
+ Sets the modality of the message box to \a windowModality.
+
+ On Mac OS X, if the modality is set to Qt::WindowModal and the message box
+ has a parent, then the message box will be a Qt::Sheet, otherwise the
+ message box will be a standard dialog.
+*/
+void QMessageBox::setWindowModality(Qt::WindowModality windowModality)
+{
+ QDialog::setWindowModality(windowModality);
+
+ if (parentWidget() && windowModality == Qt::WindowModal)
+ setParent(parentWidget(), Qt::Sheet);
+ else
+ setParent(parentWidget(), Qt::Dialog);
+ setDefaultButton(d_func()->defaultButton);
+}
+
+#ifdef QT3_SUPPORT
+/*!
+ \compat
+
+ Constructs a message box with the given \a parent, \a name, and
+ window flags, \a f.
+ The window title is specified by \a title, and the message box
+ displays message text and an icon specified by \a text and \a icon.
+
+ The buttons that the user can access to respond to the message are
+ defined by \a button0, \a button1, and \a button2.
+*/
+QMessageBox::QMessageBox(const QString& title,
+ const QString &text, Icon icon,
+ int button0, int button1, int button2,
+ QWidget *parent, const char *name,
+ bool modal, Qt::WindowFlags f)
+ : QDialog(*new QMessageBoxPrivate, parent,
+ f | Qt::WStyle_Customize | Qt::WStyle_DialogBorder | Qt::WStyle_Title | Qt::WStyle_SysMenu | Qt::WindowCloseButtonHint)
+{
+ Q_D(QMessageBox);
+ setObjectName(QString::fromAscii(name));
+ d->init(title, text);
+ d->addOldButtons(button0, button1, button2);
+ setModal(modal);
+ setIcon(icon);
+}
+
+/*!
+ \compat
+ Constructs a message box with the given \a parent and \a name.
+*/
+QMessageBox::QMessageBox(QWidget *parent, const char *name)
+ : QDialog(*new QMessageBoxPrivate, parent,
+ Qt::WStyle_Customize | Qt::WStyle_DialogBorder | Qt::WStyle_Title | Qt::WStyle_SysMenu | Qt::WindowCloseButtonHint)
+{
+ Q_D(QMessageBox);
+ setObjectName(QString::fromAscii(name));
+ d->init();
+}
+
+/*!
+ Returns the pixmap used for a standard icon. This
+ allows the pixmaps to be used in more complex message boxes.
+ \a icon specifies the required icon, e.g. QMessageBox::Information,
+ QMessageBox::Warning or QMessageBox::Critical.
+
+ \a style is unused.
+*/
+
+QPixmap QMessageBox::standardIcon(Icon icon, Qt::GUIStyle style)
+{
+ Q_UNUSED(style);
+ return QMessageBox::standardIcon(icon);
+}
+
+/*!
+ \fn int QMessageBox::message(const QString &title, const QString &text,
+ const QString &buttonText, QWidget *parent = 0,
+ const char *name = 0)
+
+ Opens a modal message box with the given \a title and showing the
+ given \a text. The message box has a single button which has the
+ given \a buttonText (or tr("OK")). The message box is centred over
+ its \a parent and is called \a name.
+
+ Use information(), warning(), question(), or critical() instead.
+
+ \oldcode
+ QMessageBox::message(tr("My App"), tr("All occurrences replaced."),
+ tr("Close"), this);
+ \newcode
+ QMessageBox::information(this, tr("My App"),
+ tr("All occurrences replaced."),
+ QMessageBox::Close);
+ \endcode
+*/
+
+/*!
+ \fn bool QMessageBox::query(const QString &caption,
+ const QString& text,
+ const QString& yesButtonText,
+ const QString& noButtonText,
+ QWidget *parent, const char *name)
+
+ \obsolete
+
+ Queries the user using a modal message box with up to two buttons.
+ The message box has the given \a caption (although some window
+ managers don't show it), and shows the given \a text. The left
+ button has the \a yesButtonText (or tr("OK")), and the right button
+ has the \a noButtonText (or isn't shown). The message box is centred
+ over its \a parent and is called \a name.
+
+ Use information(), question(), warning(), or critical() instead.
+*/
+
+#endif
+
+QPixmap QMessageBoxPrivate::standardIcon(QMessageBox::Icon icon, QMessageBox *mb)
+{
+ QStyle *style = mb ? mb->style() : QApplication::style();
+ int iconSize = style->pixelMetric(QStyle::PM_MessageBoxIconSize, 0, mb);
+ QIcon tmpIcon;
+ switch (icon) {
+ case QMessageBox::Information:
+ tmpIcon = style->standardIcon(QStyle::SP_MessageBoxInformation, 0, mb);
+ break;
+ case QMessageBox::Warning:
+ tmpIcon = style->standardIcon(QStyle::SP_MessageBoxWarning, 0, mb);
+ break;
+ case QMessageBox::Critical:
+ tmpIcon = style->standardIcon(QStyle::SP_MessageBoxCritical, 0, mb);
+ break;
+ case QMessageBox::Question:
+ tmpIcon = style->standardIcon(QStyle::SP_MessageBoxQuestion, 0, mb);
+ default:
+ break;
+ }
+ if (!tmpIcon.isNull())
+ return tmpIcon.pixmap(iconSize, iconSize);
+ return QPixmap();
+}
+
+/*!
+ \obsolete
+
+ Returns the pixmap used for a standard icon. This allows the
+ pixmaps to be used in more complex message boxes. \a icon
+ specifies the required icon, e.g. QMessageBox::Question,
+ QMessageBox::Information, QMessageBox::Warning or
+ QMessageBox::Critical.
+
+ Call QStyle::standardIcon() with QStyle::SP_MessageBoxInformation etc.
+ instead.
+*/
+
+QPixmap QMessageBox::standardIcon(Icon icon)
+{
+ return QMessageBoxPrivate::standardIcon(icon, 0);
+}
+
+/*!
+ \typedef QMessageBox::Button
+ \obsolete
+
+ Use QMessageBox::StandardButton instead.
+*/
+
+/*!
+ \fn int QMessageBox::information(QWidget *parent, const QString &title,
+ const QString& text, StandardButton button0,
+ StandardButton button1)
+ \fn int QMessageBox::warning(QWidget *parent, const QString &title,
+ const QString& text, StandardButton button0,
+ StandardButton button1)
+ \fn int QMessageBox::critical(QWidget *parent, const QString &title,
+ const QString& text, StandardButton button0,
+ StandardButton button1)
+ \fn int QMessageBox::question(QWidget *parent, const QString &title,
+ const QString& text, StandardButton button0,
+ StandardButton button1)
+ \internal
+
+ ### Needed for Qt 4 source compatibility
+*/
+
+/*!
+ \fn int QMessageBox::exec()
+
+ Shows the message box as a \l{QDialog#Modal Dialogs}{modal dialog},
+ blocking until the user closes it.
+
+ When using a QMessageBox with standard buttons, this functions returns a
+ \l StandardButton value indicating the standard button that was clicked.
+ When using QMessageBox with custom buttons, this function returns an
+ opaque value; use clickedButton() to determine which button was clicked.
+
+ Users cannot interact with any other window in the same
+ application until they close the dialog, either by clicking a
+ button or by using a mechanism provided by the window system.
+
+ \sa show(), result()
+*/
+
+QT_END_NAMESPACE
+
+#include "moc_qmessagebox.cpp"
+
+#endif // QT_NO_MESSAGEBOX
diff --git a/src/gui/dialogs/qmessagebox.h b/src/gui/dialogs/qmessagebox.h
new file mode 100644
index 0000000000..e1667d6f2c
--- /dev/null
+++ b/src/gui/dialogs/qmessagebox.h
@@ -0,0 +1,363 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QMESSAGEBOX_H
+#define QMESSAGEBOX_H
+
+#include <QtGui/qdialog.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+#ifndef QT_NO_MESSAGEBOX
+
+class QLabel;
+class QMessageBoxPrivate;
+class QAbstractButton;
+
+class Q_GUI_EXPORT QMessageBox : public QDialog
+{
+ Q_OBJECT
+ Q_ENUMS(Icon)
+ Q_FLAGS(StandardButtons)
+ Q_PROPERTY(QString text READ text WRITE setText)
+ // ### Qt 5: Rename 'icon' 'standardIcon' and 'iconPixmap' 'icon' (and use QIcon?)
+ Q_PROPERTY(Icon icon READ icon WRITE setIcon)
+ Q_PROPERTY(QPixmap iconPixmap READ iconPixmap WRITE setIconPixmap)
+ Q_PROPERTY(Qt::TextFormat textFormat READ textFormat WRITE setTextFormat)
+ Q_PROPERTY(StandardButtons standardButtons READ standardButtons WRITE setStandardButtons)
+#ifndef QT_NO_TEXTEDIT
+ Q_PROPERTY(QString detailedText READ detailedText WRITE setDetailedText)
+#endif
+ Q_PROPERTY(QString informativeText READ informativeText WRITE setInformativeText)
+
+public:
+ enum Icon {
+ NoIcon = 0,
+ Information = 1,
+ Warning = 2,
+ Critical = 3,
+ Question = 4
+ };
+
+ enum ButtonRole {
+ // keep this in sync with QDialogButtonBox::ButtonRole
+ InvalidRole = -1,
+ AcceptRole,
+ RejectRole,
+ DestructiveRole,
+ ActionRole,
+ HelpRole,
+ YesRole,
+ NoRole,
+ ResetRole,
+ ApplyRole,
+
+ NRoles
+ };
+
+ enum StandardButton {
+ // keep this in sync with QDialogButtonBox::StandardButton
+ NoButton = 0x00000000,
+ Ok = 0x00000400,
+ Save = 0x00000800,
+ SaveAll = 0x00001000,
+ Open = 0x00002000,
+ Yes = 0x00004000,
+ YesToAll = 0x00008000,
+ No = 0x00010000,
+ NoToAll = 0x00020000,
+ Abort = 0x00040000,
+ Retry = 0x00080000,
+ Ignore = 0x00100000,
+ Close = 0x00200000,
+ Cancel = 0x00400000,
+ Discard = 0x00800000,
+ Help = 0x01000000,
+ Apply = 0x02000000,
+ Reset = 0x04000000,
+ RestoreDefaults = 0x08000000,
+
+ FirstButton = Ok, // internal
+ LastButton = RestoreDefaults, // internal
+
+ YesAll = YesToAll, // obsolete
+ NoAll = NoToAll, // obsolete
+
+ Default = 0x00000100, // obsolete
+ Escape = 0x00000200, // obsolete
+ FlagMask = 0x00000300, // obsolete
+ ButtonMask = ~FlagMask // obsolete
+ };
+ typedef StandardButton Button; // obsolete
+
+ Q_DECLARE_FLAGS(StandardButtons, StandardButton)
+
+ explicit QMessageBox(QWidget *parent = 0);
+ QMessageBox(Icon icon, const QString &title, const QString &text,
+ StandardButtons buttons = NoButton, QWidget *parent = 0,
+ Qt::WindowFlags flags = Qt::Dialog | Qt::MSWindowsFixedSizeDialogHint);
+ ~QMessageBox();
+
+ void addButton(QAbstractButton *button, ButtonRole role);
+ QPushButton *addButton(const QString &text, ButtonRole role);
+ QPushButton *addButton(StandardButton button);
+ void removeButton(QAbstractButton *button);
+
+#ifdef Q_OS_WINCE
+ void setVisible(bool visible);
+#endif
+
+#ifdef Q_NO_USING_KEYWORD
+#ifndef Q_QDOC
+ void open() { QDialog::open(); }
+#endif
+#else
+ using QDialog::open;
+#endif
+ void open(QObject *receiver, const char *member);
+
+ QList<QAbstractButton *> buttons() const;
+ ButtonRole buttonRole(QAbstractButton *button) const;
+
+ void setStandardButtons(StandardButtons buttons);
+ StandardButtons standardButtons() const;
+ StandardButton standardButton(QAbstractButton *button) const;
+ QAbstractButton *button(StandardButton which) const;
+
+ QPushButton *defaultButton() const;
+ void setDefaultButton(QPushButton *button);
+ void setDefaultButton(StandardButton button);
+
+ QAbstractButton *escapeButton() const;
+ void setEscapeButton(QAbstractButton *button);
+ void setEscapeButton(StandardButton button);
+
+ QAbstractButton *clickedButton() const;
+
+ QString text() const;
+ void setText(const QString &text);
+
+ Icon icon() const;
+ void setIcon(Icon);
+
+ QPixmap iconPixmap() const;
+ void setIconPixmap(const QPixmap &pixmap);
+
+ Qt::TextFormat textFormat() const;
+ void setTextFormat(Qt::TextFormat format);
+
+ static StandardButton information(QWidget *parent, const QString &title,
+ const QString &text, StandardButtons buttons = Ok,
+ StandardButton defaultButton = NoButton);
+ static StandardButton question(QWidget *parent, const QString &title,
+ const QString &text, StandardButtons buttons = Ok,
+ StandardButton defaultButton = NoButton);
+ static StandardButton warning(QWidget *parent, const QString &title,
+ const QString &text, StandardButtons buttons = Ok,
+ StandardButton defaultButton = NoButton);
+ static StandardButton critical(QWidget *parent, const QString &title,
+ const QString &text, StandardButtons buttons = Ok,
+ StandardButton defaultButton = NoButton);
+ static void about(QWidget *parent, const QString &title, const QString &text);
+ static void aboutQt(QWidget *parent, const QString &title = QString());
+
+ QSize sizeHint() const;
+
+ // the following functions are obsolete:
+
+ QMessageBox(const QString &title, const QString &text, Icon icon,
+ int button0, int button1, int button2,
+ QWidget *parent = 0,
+ Qt::WindowFlags f = Qt::Dialog | Qt::MSWindowsFixedSizeDialogHint);
+
+ static int information(QWidget *parent, const QString &title,
+ const QString& text,
+ int button0, int button1 = 0, int button2 = 0);
+ static int information(QWidget *parent, const QString &title,
+ const QString& text,
+ const QString& button0Text,
+ const QString& button1Text = QString(),
+ const QString& button2Text = QString(),
+ int defaultButtonNumber = 0,
+ int escapeButtonNumber = -1);
+ inline static StandardButton information(QWidget *parent, const QString &title,
+ const QString& text,
+ StandardButton button0, StandardButton button1 = NoButton)
+ { return information(parent, title, text, StandardButtons(button0), button1); }
+
+ static int question(QWidget *parent, const QString &title,
+ const QString& text,
+ int button0, int button1 = 0, int button2 = 0);
+ static int question(QWidget *parent, const QString &title,
+ const QString& text,
+ const QString& button0Text,
+ const QString& button1Text = QString(),
+ const QString& button2Text = QString(),
+ int defaultButtonNumber = 0,
+ int escapeButtonNumber = -1);
+ inline static int question(QWidget *parent, const QString &title,
+ const QString& text,
+ StandardButton button0, StandardButton button1)
+ { return question(parent, title, text, StandardButtons(button0), button1); }
+
+ static int warning(QWidget *parent, const QString &title,
+ const QString& text,
+ int button0, int button1, int button2 = 0);
+ static int warning(QWidget *parent, const QString &title,
+ const QString& text,
+ const QString& button0Text,
+ const QString& button1Text = QString(),
+ const QString& button2Text = QString(),
+ int defaultButtonNumber = 0,
+ int escapeButtonNumber = -1);
+ inline static int warning(QWidget *parent, const QString &title,
+ const QString& text,
+ StandardButton button0, StandardButton button1)
+ { return warning(parent, title, text, StandardButtons(button0), button1); }
+
+ static int critical(QWidget *parent, const QString &title,
+ const QString& text,
+ int button0, int button1, int button2 = 0);
+ static int critical(QWidget *parent, const QString &title,
+ const QString& text,
+ const QString& button0Text,
+ const QString& button1Text = QString(),
+ const QString& button2Text = QString(),
+ int defaultButtonNumber = 0,
+ int escapeButtonNumber = -1);
+ inline static int critical(QWidget *parent, const QString &title,
+ const QString& text,
+ StandardButton button0, StandardButton button1)
+ { return critical(parent, title, text, StandardButtons(button0), button1); }
+
+ QString buttonText(int button) const;
+ void setButtonText(int button, const QString &text);
+
+ QString informativeText() const;
+ void setInformativeText(const QString &text);
+
+#ifndef QT_NO_TEXTEDIT
+ QString detailedText() const;
+ void setDetailedText(const QString &text);
+#endif
+
+ void setWindowTitle(const QString &title);
+ void setWindowModality(Qt::WindowModality windowModality);
+
+#ifdef QT3_SUPPORT
+ QT3_SUPPORT_CONSTRUCTOR QMessageBox(const QString &title, const QString &text, Icon icon,
+ int button0, int button1, int button2,
+ QWidget *parent, const char *name, bool modal,
+ Qt::WindowFlags f = Qt::Dialog | Qt::MSWindowsFixedSizeDialogHint);
+ QT3_SUPPORT_CONSTRUCTOR QMessageBox(QWidget *parent, const char *name);
+
+ static QT3_SUPPORT QPixmap standardIcon(Icon icon, Qt::GUIStyle);
+ static QT3_SUPPORT int message(const QString &title,
+ const QString& text,
+ const QString& buttonText=QString(),
+ QWidget *parent = 0, const char * = 0) {
+ return QMessageBox::information(parent, title, text,
+ buttonText.isEmpty() ? tr("OK") : buttonText) == 0;
+ }
+ static QT3_SUPPORT bool query(const QString &title,
+ const QString& text,
+ const QString& yesButtonText = QString(),
+ const QString& noButtonText = QString(),
+ QWidget *parent = 0, const char * = 0) {
+ return QMessageBox::information(parent, title, text,
+ yesButtonText.isEmpty() ? tr("OK") : yesButtonText,
+ noButtonText) == 0;
+ }
+#endif
+
+ static QPixmap standardIcon(Icon icon);
+
+Q_SIGNALS:
+ void buttonClicked(QAbstractButton *button);
+
+#ifdef qdoc
+public Q_SLOTS:
+ int exec();
+#endif
+
+protected:
+ bool event(QEvent *e);
+ void resizeEvent(QResizeEvent *event);
+ void showEvent(QShowEvent *event);
+ void closeEvent(QCloseEvent *event);
+ void keyPressEvent(QKeyEvent *event);
+ void changeEvent(QEvent *event);
+
+private:
+ Q_PRIVATE_SLOT(d_func(), void _q_buttonClicked(QAbstractButton *))
+
+ Q_DISABLE_COPY(QMessageBox)
+ Q_DECLARE_PRIVATE(QMessageBox)
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(QMessageBox::StandardButtons)
+
+#define QT_REQUIRE_VERSION(argc, argv, str) { QString s = QString::fromLatin1(str);\
+QString sq = QString::fromLatin1(qVersion()); \
+if ((sq.section(QChar::fromLatin1('.'),0,0).toInt()<<16)+\
+(sq.section(QChar::fromLatin1('.'),1,1).toInt()<<8)+\
+sq.section(QChar::fromLatin1('.'),2,2).toInt()<(s.section(QChar::fromLatin1('.'),0,0).toInt()<<16)+\
+(s.section(QChar::fromLatin1('.'),1,1).toInt()<<8)+\
+s.section(QChar::fromLatin1('.'),2,2).toInt()) { \
+if (!qApp){ \
+ new QApplication(argc,argv); \
+} \
+QString s = QApplication::tr("Executable '%1' requires Qt "\
+ "%2, found Qt %3.").arg(qAppName()).arg(QString::fromLatin1(\
+str)).arg(QString::fromLatin1(qVersion())); QMessageBox::critical(0, QApplication::tr(\
+"Incompatible Qt Library Error"), s, QMessageBox::Abort, 0); qFatal(s.toLatin1().data()); }}
+
+#endif // QT_NO_MESSAGEBOX
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QMESSAGEBOX_H
diff --git a/src/gui/dialogs/qmessagebox.qrc b/src/gui/dialogs/qmessagebox.qrc
new file mode 100644
index 0000000000..8e6d7af671
--- /dev/null
+++ b/src/gui/dialogs/qmessagebox.qrc
@@ -0,0 +1,5 @@
+<!DOCTYPE RCC><RCC version="1.0">
+<qresource prefix="/trolltech/qmessagebox">
+ <file>images/qtlogo-64.png</file>
+</qresource>
+</RCC>
diff --git a/src/gui/dialogs/qnspanelproxy_mac.mm b/src/gui/dialogs/qnspanelproxy_mac.mm
new file mode 100644
index 0000000000..e934b49bc2
--- /dev/null
+++ b/src/gui/dialogs/qnspanelproxy_mac.mm
@@ -0,0 +1,246 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <qdialogbuttonbox.h>
+#if defined(Q_WS_MAC)
+#include <private/qt_mac_p.h>
+#import <AppKit/AppKit.h>
+#import <Foundation/Foundation.h>
+#import <objc/objc-class.h>
+
+QT_BEGIN_NAMESPACE
+static QWidget *currentWindow = 0;
+QT_END_NAMESPACE
+
+QT_USE_NAMESPACE
+
+@class QNSPanelProxy;
+
+@interface QNSPanelProxy : NSWindow {
+}
+- (id)initWithContentRect:(NSRect)contentRect styleMask:(NSUInteger)windowStyle
+ backing:(NSBackingStoreType)bufferingType defer:(BOOL)deferCreation;
+- (id)initWithContentRect:(NSRect)contentRect styleMask:(NSUInteger)windowStyle
+ backing:(NSBackingStoreType)bufferingType defer:(BOOL)deferCreation screen:(NSScreen *)screen;
+- (id)qt_fakeInitWithContentRect:(NSRect)contentRect styleMask:(NSUInteger)windowStyle
+ backing:(NSBackingStoreType)bufferingType defer:(BOOL)deferCreation;
+- (id)qt_fakeInitWithContentRect:(NSRect)contentRect styleMask:(NSUInteger)windowStyle
+ backing:(NSBackingStoreType)bufferingType defer:(BOOL)deferCreation screen:(NSScreen *)screen;
+@end
+
+@implementation QNSPanelProxy
+- (id)initWithContentRect:(NSRect)contentRect styleMask:(NSUInteger)windowStyle
+ backing:(NSBackingStoreType)bufferingType defer:(BOOL)deferCreation
+{
+ // remove evil flag
+ windowStyle &= ~NSUtilityWindowMask;
+ self = [self qt_fakeInitWithContentRect:contentRect styleMask:windowStyle
+ backing:bufferingType defer:deferCreation];
+ return self;
+}
+
+- (id)initWithContentRect:(NSRect)contentRect styleMask:(NSUInteger)windowStyle
+ backing:(NSBackingStoreType)bufferingType defer:(BOOL)deferCreation screen:(NSScreen *)screen
+{
+ // remove evil flag
+ windowStyle &= ~NSUtilityWindowMask;
+ return [self qt_fakeInitWithContentRect:contentRect styleMask:windowStyle
+ backing:bufferingType defer:deferCreation screen:screen];
+}
+
+- (id)qt_fakeInitWithContentRect:(NSRect)contentRect styleMask:(NSUInteger)windowStyle
+ backing:(NSBackingStoreType)bufferingType defer:(BOOL)deferCreation
+{
+ Q_UNUSED(contentRect);
+ Q_UNUSED(windowStyle);
+ Q_UNUSED(bufferingType);
+ Q_UNUSED(deferCreation);
+ return nil;
+}
+
+- (id)qt_fakeInitWithContentRect:(NSRect)contentRect styleMask:(NSUInteger)windowStyle
+ backing:(NSBackingStoreType)bufferingType defer:(BOOL)deferCreation screen:(NSScreen *)screen
+{
+ Q_UNUSED(contentRect);
+ Q_UNUSED(windowStyle);
+ Q_UNUSED(bufferingType);
+ Q_UNUSED(deferCreation);
+ Q_UNUSED(screen);
+ return nil;
+}
+@end
+
+@class QNSWindowProxy;
+
+@interface QNSWindowProxy : NSWindow {
+}
+- (void)setTitle:(NSString *)title;
+- (void)qt_fakeSetTitle:(NSString *)title;
+@end
+
+@implementation QNSWindowProxy
+- (void)setTitle:(NSString *)title
+{
+ QCFString cftitle(currentWindow->windowTitle());
+
+ // evil reverse engineering
+ if ([title isEqualToString:@"Print"]
+ || [title isEqualToString:@"Page Setup"]
+ || [[self className] isEqualToString:@"PMPrintingWindow"])
+ title = (NSString *)(static_cast<CFStringRef>(cftitle));
+ return [self qt_fakeSetTitle:title];
+}
+
+- (void)qt_fakeSetTitle:(NSString *)title
+{
+ Q_UNUSED(title);
+}
+@end
+
+QT_BEGIN_NAMESPACE
+
+void macStartIntercept(SEL originalSel, SEL fakeSel, Class baseClass, Class proxyClass)
+{
+#ifndef QT_MAC_USE_COCOA
+ if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_5)
+#endif
+ {
+#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5
+ Method originalMethod = class_getInstanceMethod(baseClass, originalSel);
+ Method newMethod = class_getInstanceMethod(proxyClass, originalSel);
+ Method fakeMethod = class_getInstanceMethod(proxyClass, fakeSel);
+
+ IMP originalCtorImp = method_setImplementation(originalMethod,
+ method_getImplementation(newMethod));
+ class_addMethod(baseClass, fakeSel, originalCtorImp,
+ method_getTypeEncoding(fakeMethod));
+#endif
+ }
+}
+
+void macStopIntercept(SEL originalSel, SEL fakeSel, Class baseClass, Class /* proxyClass */)
+{
+#ifndef QT_MAC_USE_COCOA
+ if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_5)
+#endif
+ {
+#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5
+ Method originalMethod = class_getInstanceMethod(baseClass, originalSel);
+ Method fakeMethodInBaseClass = class_getInstanceMethod(baseClass, fakeSel);
+ method_setImplementation(originalMethod, method_getImplementation(fakeMethodInBaseClass));
+#endif
+ }
+}
+
+/*
+ Intercept the NSColorPanel constructor if the shared
+ color panel doesn't exist yet. What's going on here is
+ quite wacky, because we want to override the NSPanel
+ constructor and at the same time call the old NSPanel
+ constructor. So what we do is we effectively rename the
+ old NSPanel constructor qt_fakeInitWithContentRect:...
+ and have the new one call the old one.
+*/
+void macStartInterceptNSPanelCtor()
+{
+ macStartIntercept(@selector(initWithContentRect:styleMask:backing:defer:),
+ @selector(qt_fakeInitWithContentRect:styleMask:backing:defer:),
+ [NSPanel class], [QNSPanelProxy class]);
+ macStartIntercept(@selector(initWithContentRect:styleMask:backing:defer:screen:),
+ @selector(qt_fakeInitWithContentRect:styleMask:backing:defer:screen:),
+ [NSPanel class], [QNSPanelProxy class]);
+}
+
+/*
+ Restore things as they were.
+*/
+void macStopInterceptNSPanelCtor()
+{
+ macStopIntercept(@selector(initWithContentRect:styleMask:backing:defer:screen:),
+ @selector(qt_fakeInitWithContentRect:styleMask:backing:defer:screen:),
+ [NSPanel class], [QNSPanelProxy class]);
+ macStopIntercept(@selector(initWithContentRect:styleMask:backing:defer:),
+ @selector(qt_fakeInitWithContentRect:styleMask:backing:defer:),
+ [NSPanel class], [QNSPanelProxy class]);
+}
+
+/*
+ Intercept the NSPrintPanel and NSPageLayout setTitle: calls. The
+ hack is similar as for NSColorPanel above.
+*/
+void macStartInterceptWindowTitle(QWidget *window)
+{
+ currentWindow = window;
+ macStartIntercept(@selector(setTitle:), @selector(qt_fakeSetTitle:),
+ [NSWindow class], [QNSWindowProxy class]);
+}
+
+/*
+ Restore things as they were.
+*/
+void macStopInterceptWindowTitle()
+{
+ currentWindow = 0;
+ macStopIntercept(@selector(setTitle:), @selector(qt_fakeSetTitle:),
+ [NSWindow class], [QNSWindowProxy class]);
+}
+
+/*
+ Doesn't really belong in here.
+*/
+NSButton *macCreateButton(const char *text, NSView *superview)
+{
+ static const NSRect buttonFrameRect = { { 0.0, 0.0 }, { 0.0, 0.0 } };
+
+ NSButton *button = [[NSButton alloc] initWithFrame:buttonFrameRect];
+ [button setButtonType:NSMomentaryLightButton];
+ [button setBezelStyle:NSRoundedBezelStyle];
+ [button setTitle:(NSString*)(CFStringRef)QCFString(QDialogButtonBox::tr(text)
+ .remove(QLatin1Char('&')))];
+ [[button cell] setFont:[NSFont systemFontOfSize:
+ [NSFont systemFontSizeForControlSize:NSRegularControlSize]]];
+ [superview addSubview:button];
+ return button;
+}
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/gui/dialogs/qpagesetupdialog.cpp b/src/gui/dialogs/qpagesetupdialog.cpp
new file mode 100644
index 0000000000..63775d2065
--- /dev/null
+++ b/src/gui/dialogs/qpagesetupdialog.cpp
@@ -0,0 +1,185 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <private/qabstractpagesetupdialog_p.h>
+
+#ifndef QT_NO_PRINTDIALOG
+
+QT_BEGIN_NAMESPACE
+
+// hack
+class QPageSetupDialogPrivate : public QAbstractPageSetupDialogPrivate
+{
+};
+
+/*!
+ \enum QPageSetupDialog::PageSetupDialogOption
+ \since 4.4
+
+ Used to specify options to the page setup dialog
+
+ This value is obsolete and does nothing since Qt 4.5:
+
+ \value DontUseSheet In previous versions of Qt, exec() the page setup dialog
+ would create a sheet by default if the dialog was given a parent.
+ This is no longer supported in Qt 4.5. If you want to use sheets, use
+ QPageSetupDialog::open() instead.
+
+ \omitvalue None
+ \omitvalue OwnsPrinter
+*/
+
+/*!
+ Sets the given \a option to be enabled if \a on is true;
+ otherwise, clears the given \a option.
+
+ \sa options, testOption()
+*/
+void QPageSetupDialog::setOption(PageSetupDialogOption option, bool on)
+{
+ Q_D(QPageSetupDialog);
+ if (!(d->opts & option) != !on)
+ setOptions(d->opts ^ option);
+}
+
+/*!
+ Returns true if the given \a option is enabled; otherwise, returns
+ false.
+
+ \sa options, setOption()
+*/
+bool QPageSetupDialog::testOption(PageSetupDialogOption option) const
+{
+ Q_D(const QPageSetupDialog);
+ return (d->opts & option) != 0;
+}
+
+/*!
+ \property QPageSetupDialog::options
+ \brief the various options that affect the look and feel of the dialog
+ \since 4.5
+
+ By default, all options are disabled.
+
+ Options should be set before showing the dialog. Setting them while the
+ dialog is visible is not guaranteed to have an immediate effect on the
+ dialog (depending on the option and on the platform).
+
+ \sa setOption(), testOption()
+*/
+void QPageSetupDialog::setOptions(PageSetupDialogOptions options)
+{
+ Q_D(QPageSetupDialog);
+
+ PageSetupDialogOptions changed = (options ^ d->opts);
+ if (!changed)
+ return;
+
+ d->opts = options;
+}
+
+QPageSetupDialog::PageSetupDialogOptions QPageSetupDialog::options() const
+{
+ Q_D(const QPageSetupDialog);
+ return d->opts;
+}
+
+/*!
+ \obsolete
+
+ Use setOption(\a option, true) instead.
+*/
+void QPageSetupDialog::addEnabledOption(PageSetupDialogOption option)
+{
+ setOption(option, true);
+}
+
+/*!
+ \obsolete
+
+ Use setOptions(\a options) instead.
+*/
+void QPageSetupDialog::setEnabledOptions(PageSetupDialogOptions options)
+{
+ setOptions(options);
+}
+
+/*!
+ \obsolete
+
+ Use options() instead.
+*/
+QPageSetupDialog::PageSetupDialogOptions QPageSetupDialog::enabledOptions() const
+{
+ return options();
+}
+
+/*!
+ \obsolete
+
+ Use testOption(\a option) instead.
+*/
+bool QPageSetupDialog::isOptionEnabled(PageSetupDialogOption option) const
+{
+ return testOption(option);
+}
+
+/*!
+ \overload
+ \since 4.5
+
+ Opens the dialog and connects its accepted() signal to the slot specified
+ by \a receiver and \a member.
+
+ The signal will be disconnected from the slot when the dialog is closed.
+*/
+void QPageSetupDialog::open(QObject *receiver, const char *member)
+{
+ Q_D(QPageSetupDialog);
+ connect(this, SIGNAL(accepted()), receiver, member);
+ d->receiverToDisconnectOnClose = receiver;
+ d->memberToDisconnectOnClose = member;
+ QDialog::open();
+}
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/gui/dialogs/qpagesetupdialog.h b/src/gui/dialogs/qpagesetupdialog.h
new file mode 100644
index 0000000000..03420ee5d1
--- /dev/null
+++ b/src/gui/dialogs/qpagesetupdialog.h
@@ -0,0 +1,112 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QPAGESETUPDIALOG_H
+#define QPAGESETUPDIALOG_H
+
+#include <QtGui/qabstractpagesetupdialog.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+#ifndef QT_NO_PRINTDIALOG
+
+class QPageSetupDialogPrivate;
+
+class Q_GUI_EXPORT QPageSetupDialog : public QAbstractPageSetupDialog
+{
+ Q_OBJECT
+ Q_DECLARE_PRIVATE(QPageSetupDialog)
+ Q_ENUMS(PageSetupDialogOption)
+ Q_PROPERTY(PageSetupDialogOptions options READ options WRITE setOptions)
+
+public:
+ enum PageSetupDialogOption {
+ None = 0x00000000, // internal
+ DontUseSheet = 0x00000001,
+ OwnsPrinter = 0x80000000 // internal
+ };
+
+ Q_DECLARE_FLAGS(PageSetupDialogOptions, PageSetupDialogOption)
+
+ explicit QPageSetupDialog(QPrinter *printer, QWidget *parent = 0);
+ explicit QPageSetupDialog(QWidget *parent = 0);
+
+ // obsolete
+ void addEnabledOption(PageSetupDialogOption option);
+ void setEnabledOptions(PageSetupDialogOptions options);
+ PageSetupDialogOptions enabledOptions() const;
+ bool isOptionEnabled(PageSetupDialogOption option) const;
+
+ void setOption(PageSetupDialogOption option, bool on = true);
+ bool testOption(PageSetupDialogOption option) const;
+ void setOptions(PageSetupDialogOptions options);
+ PageSetupDialogOptions options() const;
+
+#if defined(Q_WS_MAC) || defined(Q_OS_WIN)
+ virtual void setVisible(bool visible);
+#endif
+ virtual int exec();
+
+#ifdef Q_NO_USING_KEYWORD
+#ifndef Q_QDOC
+ void open() { QDialog::open(); }
+#endif
+#else
+ using QDialog::open;
+#endif
+ void open(QObject *receiver, const char *member);
+
+#ifdef qdoc
+ QPrinter *printer();
+#endif
+};
+
+#endif // QT_NO_PRINTDIALOG
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QPAGESETUPDIALOG_H
diff --git a/src/gui/dialogs/qpagesetupdialog_mac.mm b/src/gui/dialogs/qpagesetupdialog_mac.mm
new file mode 100644
index 0000000000..401d95f817
--- /dev/null
+++ b/src/gui/dialogs/qpagesetupdialog_mac.mm
@@ -0,0 +1,313 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qpagesetupdialog.h"
+
+#include <qhash.h>
+#include <private/qapplication_p.h>
+#include <private/qprintengine_mac_p.h>
+#include <private/qabstractpagesetupdialog_p.h>
+
+#ifndef QT_NO_PRINTDIALOG
+
+QT_USE_NAMESPACE
+
+@class QCocoaPageLayoutDelegate;
+
+@interface QCocoaPageLayoutDelegate : NSObject {
+ QMacPrintEnginePrivate *pe;
+}
+- (id)initWithMacPrintEngine:(QMacPrintEnginePrivate *)printEngine;
+- (void)pageLayoutDidEnd:(NSPageLayout *)pageLayout
+ returnCode:(int)returnCode contextInfo:(void *)contextInfo;
+@end
+
+@implementation QCocoaPageLayoutDelegate
+- (id)initWithMacPrintEngine:(QMacPrintEnginePrivate *)printEngine;
+{
+ self = [super init];
+ if (self) {
+ pe = printEngine;
+ }
+ return self;
+
+}
+- (void)pageLayoutDidEnd:(NSPageLayout *)pageLayout
+ returnCode:(int)returnCode contextInfo:(void *)contextInfo
+{
+ Q_UNUSED(pageLayout);
+ QPageSetupDialog *dialog = static_cast<QPageSetupDialog *>(contextInfo);
+ if (returnCode == NSOKButton) {
+ PMRect paperRect;
+ PMGetUnadjustedPaperRect(pe->format, &paperRect);
+ pe->customSize = QSizeF(paperRect.right - paperRect.left,
+ paperRect.bottom - paperRect.top);
+ }
+ dialog->done((returnCode == NSOKButton) ? QDialog::Accepted : QDialog::Rejected);
+}
+@end
+
+QT_BEGIN_NAMESPACE
+
+extern void macStartInterceptWindowTitle(QWidget *window);
+extern void macStopInterceptWindowTitle();
+
+class QPageSetupDialogPrivate : public QAbstractPageSetupDialogPrivate
+{
+ Q_DECLARE_PUBLIC(QPageSetupDialog)
+
+public:
+ QPageSetupDialogPrivate() : ep(0)
+#ifndef QT_MAC_USE_COCOA
+ ,upp(0)
+#else
+ ,pageLayout(0)
+#endif
+ {}
+
+ ~QPageSetupDialogPrivate() {
+#ifndef QT_MAC_USE_COCOA
+ if (upp) {
+ DisposePMSheetDoneUPP(upp);
+ upp = 0;
+ }
+ QHash<PMPrintSession, QPageSetupDialogPrivate *>::iterator it = sheetCallbackMap.begin();
+ while (it != sheetCallbackMap.end()) {
+ if (it.value() == this) {
+ it = sheetCallbackMap.erase(it);
+ } else {
+ ++it;
+ }
+ }
+#endif
+ }
+
+#ifndef QT_MAC_USE_COCOA
+ void openCarbonPageLayout(Qt::WindowModality modality);
+ void closeCarbonPageLayout();
+ static void pageSetupDialogSheetDoneCallback(PMPrintSession printSession, WindowRef /*documentWindow*/, Boolean accepted) {
+ QPageSetupDialogPrivate *priv = sheetCallbackMap.value(printSession);
+ if (!priv) {
+ qWarning("%s:%d: QPageSetupDialog::exec: Could not retrieve data structure, "
+ "you most likely now have an infinite modal loop", __FILE__, __LINE__);
+ return;
+ }
+ priv->q_func()->done(accepted ? QDialog::Accepted : QDialog::Rejected);
+ }
+#else
+ void openCocoaPageLayout(Qt::WindowModality modality);
+ void closeCocoaPageLayout();
+#endif
+
+ QMacPrintEnginePrivate *ep;
+#ifndef QT_MAC_USE_COCOA
+ PMSheetDoneUPP upp;
+ static QHash<PMPrintSession, QPageSetupDialogPrivate*> sheetCallbackMap;
+#else
+ NSPageLayout *pageLayout;
+#endif
+};
+
+#ifndef QT_MAC_USE_COCOA
+QHash<PMPrintSession, QPageSetupDialogPrivate*> QPageSetupDialogPrivate::sheetCallbackMap;
+void QPageSetupDialogPrivate::openCarbonPageLayout(Qt::WindowModality modality)
+{
+ Q_Q(QPageSetupDialog);
+ // If someone is reusing a QPrinter object, the end released all our old
+ // information. In this case, we must reinitialize.
+ if (ep->session == 0)
+ ep->initialize();
+
+ sheetCallbackMap.insert(ep->session, this);
+ if (modality == Qt::ApplicationModal) {
+ QWidget modal_widg(0, Qt::Window);
+ modal_widg.setObjectName(QLatin1String(__FILE__ "__modal_dlg"));
+ modal_widg.createWinId();
+ QApplicationPrivate::enterModal(&modal_widg);
+ QApplicationPrivate::native_modal_dialog_active = true;
+ Boolean accepted;
+ PMSessionPageSetupDialog(ep->session, ep->format, &accepted);
+ QApplicationPrivate::leaveModal(&modal_widg);
+ QApplicationPrivate::native_modal_dialog_active = false;
+ pageSetupDialogSheetDoneCallback(ep->session, 0, accepted);
+ } else {
+ // Window Modal means that we use a sheet at the moment, there's no other way to do it correctly.
+ if (!upp)
+ upp = NewPMSheetDoneUPP(QPageSetupDialogPrivate::pageSetupDialogSheetDoneCallback);
+ PMSessionUseSheets(ep->session, qt_mac_window_for(q->parentWidget()), upp);
+ Boolean unused;
+ PMSessionPageSetupDialog(ep->session, ep->format, &unused);
+ }
+}
+
+void QPageSetupDialogPrivate::closeCarbonPageLayout()
+{
+ // if the margins have changed, we have to use the margins from the new
+ // PMFormat object
+ if (q_func()->result() == QDialog::Accepted) {
+ PMPaper paper;
+ PMPaperMargins margins;
+ PMGetPageFormatPaper(ep->format, &paper);
+ PMPaperGetMargins(paper, &margins);
+ ep->leftMargin = margins.left;
+ ep->topMargin = margins.top;
+ ep->rightMargin = margins.right;
+ ep->bottomMargin = margins.bottom;
+
+ PMRect paperRect;
+ PMGetUnadjustedPaperRect(ep->format, &paperRect);
+ ep->customSize = QSizeF(paperRect.right - paperRect.left,
+ paperRect.bottom - paperRect.top);
+ }
+ sheetCallbackMap.remove(ep->session);
+}
+#else
+void QPageSetupDialogPrivate::openCocoaPageLayout(Qt::WindowModality modality)
+{
+ Q_Q(QPageSetupDialog);
+
+ // If someone is reusing a QPrinter object, the end released all our old
+ // information. In this case, we must reinitialize.
+ if (ep->session == 0)
+ ep->initialize();
+
+ macStartInterceptWindowTitle(q);
+ pageLayout = [NSPageLayout pageLayout];
+ // Keep a copy to this since we plan on using it for a bit.
+ [pageLayout retain];
+ QCocoaPageLayoutDelegate *delegate = [[QCocoaPageLayoutDelegate alloc] initWithMacPrintEngine:ep];
+
+ if (modality == Qt::ApplicationModal) {
+ int rval = [pageLayout runModalWithPrintInfo:ep->printInfo];
+ [delegate pageLayoutDidEnd:pageLayout returnCode:rval contextInfo:q];
+ } else {
+ Q_ASSERT(q->parentWidget());
+ [pageLayout beginSheetWithPrintInfo:ep->printInfo
+ modalForWindow:qt_mac_window_for(q->parentWidget())
+ delegate:delegate
+ didEndSelector:@selector(pageLayoutDidEnd:returnCode:contextInfo:)
+ contextInfo:q];
+ }
+
+ macStopInterceptWindowTitle();
+}
+
+void QPageSetupDialogPrivate::closeCocoaPageLayout()
+{
+ [pageLayout release];
+ pageLayout = 0;
+}
+#endif
+
+QPageSetupDialog::QPageSetupDialog(QPrinter *printer, QWidget *parent)
+ : QAbstractPageSetupDialog(*(new QPageSetupDialogPrivate), printer, parent)
+{
+ Q_D(QPageSetupDialog);
+ d->ep = static_cast<QMacPrintEngine *>(d->printer->paintEngine())->d_func();
+}
+
+QPageSetupDialog::QPageSetupDialog(QWidget *parent)
+ : QAbstractPageSetupDialog(*(new QPageSetupDialogPrivate), 0, parent)
+{
+ Q_D(QPageSetupDialog);
+ d->ep = static_cast<QMacPrintEngine *>(d->printer->paintEngine())->d_func();
+}
+
+/*!
+ \reimp
+*/
+void QPageSetupDialog::setVisible(bool visible)
+{
+ Q_D(QPageSetupDialog);
+
+ if (d->printer->outputFormat() != QPrinter::NativeFormat)
+ return;
+
+#ifndef QT_MAC_USE_COCOA
+ bool isCurrentlyVisible = d->sheetCallbackMap.contains(d->ep->session);
+#else
+ bool isCurrentlyVisible = (d->pageLayout != 0);
+#endif
+ if (!visible == !isCurrentlyVisible)
+ return;
+
+ if (visible) {
+#ifndef QT_MAC_USE_COCOA
+ d->openCarbonPageLayout(parentWidget() ? Qt::WindowModal
+ : Qt::ApplicationModal);
+#else
+ d->openCocoaPageLayout(parentWidget() ? Qt::WindowModal
+ : Qt::ApplicationModal);
+#endif
+ return;
+ } else {
+#ifndef QT_MAC_USE_COCOA
+ d->closeCarbonPageLayout();
+#else
+ if (d->pageLayout) {
+ d->closeCocoaPageLayout();
+ return;
+ }
+#endif
+ }
+}
+
+int QPageSetupDialog::exec()
+{
+ Q_D(QPageSetupDialog);
+
+ if (d->printer->outputFormat() != QPrinter::NativeFormat)
+ return Rejected;
+
+#ifndef QT_MAC_USE_COCOA
+ d->openCarbonPageLayout(Qt::ApplicationModal);
+ d->closeCarbonPageLayout();
+#else
+ QMacCocoaAutoReleasePool pool;
+ d->openCocoaPageLayout(Qt::ApplicationModal);
+ d->closeCocoaPageLayout();
+#endif
+ return result();
+}
+
+QT_END_NAMESPACE
+
+#endif QT_NO_PRINTDIALOG
diff --git a/src/gui/dialogs/qpagesetupdialog_unix.cpp b/src/gui/dialogs/qpagesetupdialog_unix.cpp
new file mode 100644
index 0000000000..fc1f8b625e
--- /dev/null
+++ b/src/gui/dialogs/qpagesetupdialog_unix.cpp
@@ -0,0 +1,620 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qpagesetupdialog.h"
+
+#ifndef QT_NO_PRINTDIALOG
+#include "qpagesetupdialog_unix_p.h"
+
+#include "qpainter.h"
+#include "qprintdialog.h"
+#include "qdialogbuttonbox.h"
+#include <ui_qpagesetupwidget.h>
+
+#include <QtGui/qprinter.h>
+#include <private/qabstractpagesetupdialog_p.h>
+#include <private/qprinter_p.h>
+
+#if !defined(QT_NO_CUPS) && !defined(QT_NO_LIBRARY)
+# include <private/qcups_p.h>
+# include <cups/cups.h>
+# include <private/qpdf_p.h>
+#endif
+
+
+QT_BEGIN_NAMESPACE
+
+QSizeF qt_printerPaperSize(QPrinter::Orientation, QPrinter::PaperSize, QPrinter::Unit, int);
+
+// Disabled until we have support for papersources on unix
+// #define PSD_ENABLE_PAPERSOURCE
+
+static void populatePaperSizes(QComboBox* cb)
+{
+ cb->addItem(QPrintDialog::tr("A0"), QPrinter::A0);
+ cb->addItem(QPrintDialog::tr("A1"), QPrinter::A1);
+ cb->addItem(QPrintDialog::tr("A2"), QPrinter::A2);
+ cb->addItem(QPrintDialog::tr("A3"), QPrinter::A3);
+ cb->addItem(QPrintDialog::tr("A4"), QPrinter::A4);
+ cb->addItem(QPrintDialog::tr("A5"), QPrinter::A5);
+ cb->addItem(QPrintDialog::tr("A6"), QPrinter::A6);
+ cb->addItem(QPrintDialog::tr("A7"), QPrinter::A7);
+ cb->addItem(QPrintDialog::tr("A8"), QPrinter::A8);
+ cb->addItem(QPrintDialog::tr("A9"), QPrinter::A9);
+ cb->addItem(QPrintDialog::tr("B0"), QPrinter::B0);
+ cb->addItem(QPrintDialog::tr("B1"), QPrinter::B1);
+ cb->addItem(QPrintDialog::tr("B2"), QPrinter::B2);
+ cb->addItem(QPrintDialog::tr("B3"), QPrinter::B3);
+ cb->addItem(QPrintDialog::tr("B4"), QPrinter::B4);
+ cb->addItem(QPrintDialog::tr("B5"), QPrinter::B5);
+ cb->addItem(QPrintDialog::tr("B6"), QPrinter::B6);
+ cb->addItem(QPrintDialog::tr("B7"), QPrinter::B7);
+ cb->addItem(QPrintDialog::tr("B8"), QPrinter::B8);
+ cb->addItem(QPrintDialog::tr("B9"), QPrinter::B9);
+ cb->addItem(QPrintDialog::tr("B10"), QPrinter::B10);
+ cb->addItem(QPrintDialog::tr("C5E"), QPrinter::C5E);
+ cb->addItem(QPrintDialog::tr("DLE"), QPrinter::DLE);
+ cb->addItem(QPrintDialog::tr("Executive"), QPrinter::Executive);
+ cb->addItem(QPrintDialog::tr("Folio"), QPrinter::Folio);
+ cb->addItem(QPrintDialog::tr("Ledger"), QPrinter::Ledger);
+ cb->addItem(QPrintDialog::tr("Legal"), QPrinter::Legal);
+ cb->addItem(QPrintDialog::tr("Letter"), QPrinter::Letter);
+ cb->addItem(QPrintDialog::tr("Tabloid"), QPrinter::Tabloid);
+ cb->addItem(QPrintDialog::tr("US Common #10 Envelope"), QPrinter::Comm10E);
+ cb->addItem(QPrintDialog::tr("Custom"), QPrinter::Custom);
+}
+
+
+static QSizeF sizeForOrientation(QPrinter::Orientation orientation, const QSizeF &size)
+{
+ return (orientation == QPrinter::Portrait) ? size : QSizeF(size.height(), size.width());
+}
+
+#ifdef PSD_ENABLE_PAPERSOURCE
+static const char *paperSourceNames[] = {
+ "Only One",
+ "Lower",
+ "Middle",
+ "Manual",
+ "Envelope",
+ "Envelope manual",
+ "Auto",
+ "Tractor",
+ "Small format",
+ "Large format",
+ "Large capacity",
+ "Cassette",
+ "Form source",
+ 0
+};
+
+struct PaperSourceNames
+{
+ PaperSourceNames(const char *nam, QPrinter::PaperSource ps)
+ : paperSource(ps), name(nam) {}
+ QPrinter::PaperSource paperSource;
+ const char *name;
+};
+#endif
+
+
+class QPagePreview : public QWidget
+{
+public:
+ QPagePreview(QWidget *parent) : QWidget(parent)
+ {
+ setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
+ setMinimumSize(50, 50);
+ }
+
+ void setPaperSize(const QSizeF& size)
+ {
+ m_size = size;
+ update();
+ }
+
+ void setMargins(qreal left, qreal top, qreal right, qreal bottom)
+ {
+ m_left = left;
+ m_top = top;
+ m_right = right;
+ m_bottom = bottom;
+ update();
+ }
+
+protected:
+ void paintEvent(QPaintEvent *)
+ {
+ QRect pageRect;
+ QSizeF adjustedSize(m_size);
+ adjustedSize.scale(width()-10, height()-10, Qt::KeepAspectRatio);
+ pageRect = QRect(QPoint(0,0), adjustedSize.toSize());
+ pageRect.moveCenter(rect().center());
+
+ qreal width_factor = pageRect.width() / m_size.width();
+ qreal height_factor = pageRect.height() / m_size.height();
+ int leftSize = qRound(m_left*width_factor);
+ int topSize = qRound(m_top*height_factor);
+ int rightSize = qRound(m_right*width_factor);
+ int bottomSize = qRound(m_bottom * height_factor);
+ QRect marginRect(pageRect.x()+leftSize,
+ pageRect.y()+topSize,
+ pageRect.width() - (leftSize+rightSize+1),
+ pageRect.height() - (topSize+bottomSize+1));
+
+ QPainter p(this);
+ QColor shadow(palette().mid().color());
+ for (int i=1; i<6; ++i) {
+ shadow.setAlpha(180-i*30);
+ QRect offset(pageRect.adjusted(i, i, i, i));
+ p.setPen(shadow);
+ p.drawLine(offset.left(), offset.bottom(), offset.right(), offset.bottom());
+ p.drawLine(offset.right(), offset.top(), offset.right(), offset.bottom()-1);
+ }
+ p.fillRect(pageRect, palette().light());
+
+ if (marginRect.isValid()) {
+ p.setPen(QPen(palette().color(QPalette::Dark), 0, Qt::DotLine));
+ p.drawRect(marginRect);
+
+ marginRect.adjust(2, 2, -1, -1);
+ p.setClipRect(marginRect);
+ QFont font;
+ font.setPointSizeF(font.pointSizeF()*0.25);
+ p.setFont(font);
+ p.setPen(palette().color(QPalette::Dark));
+ QString text(QLatin1String("Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat. Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat. Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis at vero eros et accumsan et iusto odio dignissim qui blandit praesent luptatum zzril delenit augue duis dolore te feugait nulla facilisi."));
+ for (int i=0; i<3; ++i)
+ text += text;
+ p.drawText(marginRect, Qt::TextWordWrap|Qt::AlignVCenter, text);
+ }
+ }
+
+private:
+ // all these are in points
+ qreal m_left, m_top, m_right, m_bottom;
+ QSizeF m_size;
+};
+
+
+class QPageSetupDialogPrivate : public QAbstractPageSetupDialogPrivate
+{
+ Q_DECLARE_PUBLIC(QPageSetupDialog)
+
+public:
+ ~QPageSetupDialogPrivate();
+ void init();
+
+ QPageSetupWidget *widget;
+#if !defined(QT_NO_CUPS) && !defined(QT_NO_LIBRARY)
+ QCUPSSupport *cups;
+#endif
+};
+
+QPageSetupDialogPrivate::~QPageSetupDialogPrivate()
+{
+#if !defined(QT_NO_CUPS) && !defined(QT_NO_LIBRARY)
+ delete cups;
+#endif
+}
+
+void QPageSetupDialogPrivate::init()
+{
+ Q_Q(QPageSetupDialog);
+
+ widget = new QPageSetupWidget(q);
+ widget->setPrinter(printer);
+#if !defined(QT_NO_CUPS) && !defined(QT_NO_LIBRARY)
+ if (printer->outputFormat() == QPrinter::NativeFormat && QCUPSSupport::isAvailable()) {
+ cups = new QCUPSSupport;
+ widget->selectPrinter(cups);
+ } else {
+ cups = 0;
+ }
+#endif
+
+ QDialogButtonBox *buttons = new QDialogButtonBox(QDialogButtonBox::Ok
+ | QDialogButtonBox::Cancel,
+ Qt::Horizontal, q);
+ QObject::connect(buttons, SIGNAL(accepted()), q, SLOT(accept()));
+ QObject::connect(buttons, SIGNAL(rejected()), q, SLOT(reject()));
+
+ QVBoxLayout *lay = new QVBoxLayout(q);
+ lay->addWidget(widget);
+ lay->addWidget(buttons);
+}
+
+QPageSetupWidget::QPageSetupWidget(QWidget *parent)
+ : QWidget(parent),
+ m_printer(0),
+ m_blockSignals(false),
+ m_cups(0)
+{
+ widget.setupUi(this);
+
+ QString suffix = (QLocale::system().measurementSystem() == QLocale::ImperialSystem)
+ ? QString::fromLatin1(" in")
+ : QString::fromLatin1(" mm");
+ widget.topMargin->setSuffix(suffix);
+ widget.bottomMargin->setSuffix(suffix);
+ widget.leftMargin->setSuffix(suffix);
+ widget.rightMargin->setSuffix(suffix);
+ widget.paperWidth->setSuffix(suffix);
+ widget.paperHeight->setSuffix(suffix);
+
+ QVBoxLayout *lay = new QVBoxLayout(widget.preview);
+ widget.preview->setLayout(lay);
+ m_pagePreview = new QPagePreview(widget.preview);
+ lay->addWidget(m_pagePreview);
+
+ setAttribute(Qt::WA_WState_Polished, false);
+
+#ifdef PSD_ENABLE_PAPERSOURCE
+ for (int i=0; paperSourceNames[i]; ++i)
+ widget.paperSource->insertItem(paperSourceNames[i]);
+#else
+ widget.paperSourceLabel->setVisible(false);
+ widget.paperSource->setVisible(false);
+#endif
+
+ widget.reverseLandscape->setVisible(false);
+ widget.reversePortrait->setVisible(false);
+
+ populatePaperSizes(widget.paperSize);
+
+ QStringList units;
+ units << tr("Centimeters (cm)") << tr("Millimeters (mm)") << tr("Inches (in)") << tr("Points (pt)");
+ widget.unit->addItems(units);
+ connect(widget.unit, SIGNAL(activated(int)), this, SLOT(unitChanged(int)));
+ widget.unit->setCurrentIndex((QLocale::system().measurementSystem() == QLocale::ImperialSystem) ? 2 : 1);
+
+ connect(widget.paperSize, SIGNAL(currentIndexChanged(int)), this, SLOT(_q_paperSizeChanged()));
+ connect(widget.paperWidth, SIGNAL(valueChanged(double)), this, SLOT(_q_paperSizeChanged()));
+ connect(widget.paperHeight, SIGNAL(valueChanged(double)), this, SLOT(_q_paperSizeChanged()));
+
+ connect(widget.leftMargin, SIGNAL(valueChanged(double)), this, SLOT(setLeftMargin(double)));
+ connect(widget.topMargin, SIGNAL(valueChanged(double)), this, SLOT(setTopMargin(double)));
+ connect(widget.rightMargin, SIGNAL(valueChanged(double)), this, SLOT(setRightMargin(double)));
+ connect(widget.bottomMargin, SIGNAL(valueChanged(double)), this, SLOT(setBottomMargin(double)));
+
+ connect(widget.portrait, SIGNAL(clicked()), this, SLOT(_q_pageOrientationChanged()));
+ connect(widget.landscape, SIGNAL(clicked()), this, SLOT(_q_pageOrientationChanged()));
+}
+
+void QPageSetupWidget::setPrinter(QPrinter *printer)
+{
+ m_printer = printer;
+ m_blockSignals = true;
+ selectPdfPsPrinter(printer);
+ printer->getPageMargins(&m_leftMargin, &m_topMargin, &m_rightMargin, &m_bottomMargin, QPrinter::Point);
+ unitChanged(widget.unit->currentIndex());
+ m_pagePreview->setMargins(m_leftMargin, m_topMargin, m_rightMargin, m_bottomMargin);
+ m_paperSize = printer->paperSize(QPrinter::Point);
+ widget.paperWidth->setValue(m_paperSize.width() / m_currentMultiplier);
+ widget.paperHeight->setValue(m_paperSize.height() / m_currentMultiplier);
+
+ widget.landscape->setChecked(printer->orientation() == QPrinter::Landscape);
+
+#ifdef PSD_ENABLE_PAPERSOURCE
+ widget.paperSource->setCurrentItem(printer->paperSource());
+#endif
+ Q_ASSERT(m_blockSignals);
+ m_blockSignals = false;
+ _q_paperSizeChanged();
+}
+
+// set gui data on printer
+void QPageSetupWidget::setupPrinter() const
+{
+ QPrinter::Orientation orientation = widget.portrait->isChecked()
+ ? QPrinter::Portrait
+ : QPrinter::Landscape;
+ m_printer->setOrientation(orientation);
+ // paper format
+ QVariant val = widget.paperSize->itemData(widget.paperSize->currentIndex());
+ int ps = m_printer->pageSize();
+ if (val.type() == QVariant::Int) {
+ ps = val.toInt();
+ }
+#if !defined(QT_NO_CUPS) && !defined(QT_NO_LIBRARY)
+ else if (m_cups && QCUPSSupport::isAvailable() && m_cups->currentPPD()) {
+ QByteArray cupsPageSize = val.toByteArray();
+ QPrintEngine *engine = m_printer->printEngine();
+ engine->setProperty(PPK_CupsStringPageSize, QString::fromLatin1(cupsPageSize));
+ engine->setProperty(PPK_CupsOptions, m_cups->options());
+
+ QRect pageRect = m_cups->pageRect(cupsPageSize);
+ engine->setProperty(PPK_CupsPageRect, pageRect);
+
+ QRect paperRect = m_cups->paperRect(cupsPageSize);
+ engine->setProperty(PPK_CupsPaperRect, paperRect);
+
+ for(ps = 0; ps < QPrinter::NPaperSize; ++ps) {
+ QPdf::PaperSize size = QPdf::paperSize(QPrinter::PaperSize(ps));
+ if (size.width == paperRect.width() && size.height == paperRect.height())
+ break;
+ }
+ }
+#endif
+ if (ps == QPrinter::Custom)
+ m_printer->setPaperSize(sizeForOrientation(orientation, m_paperSize), QPrinter::Point);
+ else
+ m_printer->setPaperSize(static_cast<QPrinter::PaperSize>(ps));
+
+#ifdef PSD_ENABLE_PAPERSOURCE
+ m_printer->setPaperSource((QPrinter::PaperSource)widget.paperSource->currentIndex());
+#endif
+ m_printer->setPageMargins(m_leftMargin, m_topMargin, m_rightMargin, m_bottomMargin, QPrinter::Point);
+
+}
+
+void QPageSetupWidget::selectPrinter(QCUPSSupport *cups)
+{
+ m_cups = cups;
+ widget.paperSize->clear();
+#if !defined(QT_NO_CUPS) && !defined(QT_NO_LIBRARY)
+ if (m_cups && QCUPSSupport::isAvailable()) {
+ const ppd_option_t* pageSizes = m_cups->pageSizes();
+ const int numChoices = pageSizes ? pageSizes->num_choices : 0;
+
+ int cupsDefaultSize = 0;
+ QSize qtPreferredSize = m_printer->paperSize(QPrinter::Point).toSize();
+ bool preferredSizeMatched = false;
+ for (int i = 0; i < numChoices; ++i) {
+ widget.paperSize->addItem(QString::fromLocal8Bit(pageSizes->choices[i].text), QByteArray(pageSizes->choices[i].choice));
+ if (static_cast<int>(pageSizes->choices[i].marked) == 1)
+ cupsDefaultSize = i;
+ if (m_printer->d_func()->hasUserSetPageSize) {
+ QRect cupsPaperSize = m_cups->paperRect(pageSizes->choices[i].choice);
+ QSize diff = cupsPaperSize.size() - qtPreferredSize;
+ if (qAbs(diff.width()) < 5 && qAbs(diff.height()) < 5) {
+ widget.paperSize->setCurrentIndex(i);
+ preferredSizeMatched = true;
+ }
+ }
+ }
+ if (!preferredSizeMatched)
+ widget.paperSize->setCurrentIndex(cupsDefaultSize);
+ if (m_printer->d_func()->hasCustomPageMargins) {
+ m_printer->getPageMargins(&m_leftMargin, &m_topMargin, &m_rightMargin, &m_bottomMargin, QPrinter::Point);
+ } else {
+ QByteArray cupsPaperSizeChoice = widget.paperSize->itemData(widget.paperSize->currentIndex()).toByteArray();
+ QRect paper = m_cups->paperRect(cupsPaperSizeChoice);
+ QRect content = m_cups->pageRect(cupsPaperSizeChoice);
+
+ m_leftMargin = content.x() - paper.x();
+ m_topMargin = content.y() - paper.y();
+ m_rightMargin = paper.right() - content.right();
+ m_bottomMargin = paper.bottom() - content.bottom();
+ }
+ }
+#endif
+ if (widget.paperSize->count() == 0) {
+ populatePaperSizes(widget.paperSize);
+ widget.paperSize->setCurrentIndex(widget.paperSize->findData(
+ QLocale::system().measurementSystem() == QLocale::ImperialSystem ? QPrinter::Letter : QPrinter::A4));
+ }
+
+ unitChanged(widget.unit->currentIndex());
+ m_pagePreview->setMargins(m_leftMargin, m_topMargin, m_rightMargin, m_bottomMargin);
+}
+
+void QPageSetupWidget::selectPdfPsPrinter(const QPrinter *p)
+{
+ m_cups = 0;
+ widget.paperSize->clear();
+ populatePaperSizes(widget.paperSize);
+ widget.paperSize->setCurrentIndex(widget.paperSize->findData(p->paperSize()));
+
+ m_leftMargin = 90;
+ m_topMargin = 72;
+ m_bottomMargin = 72;
+ m_rightMargin = 90;
+ unitChanged(widget.unit->currentIndex());
+ m_pagePreview->setMargins(m_leftMargin, m_topMargin, m_rightMargin, m_bottomMargin);
+}
+
+// Updates size/preview after the combobox has been changed.
+void QPageSetupWidget::_q_paperSizeChanged()
+{
+ QVariant val = widget.paperSize->itemData(widget.paperSize->currentIndex());
+ int index = m_printer->pageSize();
+ if (val.type() == QVariant::Int) {
+ index = val.toInt();
+ }
+
+ if (m_blockSignals) return;
+ m_blockSignals = true;
+
+ QPrinter::PaperSize size = QPrinter::PaperSize(index);
+ QPrinter::Orientation orientation = widget.portrait->isChecked()
+ ? QPrinter::Portrait
+ : QPrinter::Landscape;
+
+ bool custom = size == QPrinter::Custom;
+
+#if !defined(QT_NO_CUPS) && !defined(QT_NO_LIBRARY)
+ custom = custom ? !m_cups : custom;
+#endif
+
+ widget.paperWidth->setEnabled(custom);
+ widget.paperHeight->setEnabled(custom);
+ widget.widthLabel->setEnabled(custom);
+ widget.heightLabel->setEnabled(custom);
+ if (custom) {
+ m_paperSize.setWidth( widget.paperWidth->value() * m_currentMultiplier);
+ m_paperSize.setHeight( widget.paperHeight->value() * m_currentMultiplier);
+ m_pagePreview->setPaperSize(m_paperSize);
+ } else {
+ Q_ASSERT(m_printer);
+#if !defined(QT_NO_CUPS) && !defined(QT_NO_LIBRARY)
+ if (m_cups) { // combobox is filled with cups based data
+ QByteArray cupsPageSize = widget.paperSize->itemData(widget.paperSize->currentIndex()).toByteArray();
+ m_paperSize = m_cups->paperRect(cupsPageSize).size();
+ if (orientation == QPrinter::Landscape)
+ m_paperSize = QSizeF(m_paperSize.height(), m_paperSize.width()); // swap
+ }
+ else
+#endif
+ m_paperSize = qt_printerPaperSize(orientation, size, QPrinter::Point, 1);
+
+ m_pagePreview->setPaperSize(m_paperSize);
+ widget.paperWidth->setValue(m_paperSize.width() / m_currentMultiplier);
+ widget.paperHeight->setValue(m_paperSize.height() / m_currentMultiplier);
+ }
+ m_blockSignals = false;
+}
+
+void QPageSetupWidget::_q_pageOrientationChanged()
+{
+ if (QPrinter::PaperSize(widget.paperSize->currentIndex()) == QPrinter::Custom) {
+ double tmp = widget.paperWidth->value();
+ widget.paperWidth->setValue(widget.paperHeight->value());
+ widget.paperHeight->setValue(tmp);
+ }
+ _q_paperSizeChanged();
+}
+
+extern double qt_multiplierForUnit(QPrinter::Unit unit, int resolution);
+
+void QPageSetupWidget::unitChanged(int item)
+{
+ QString suffix;
+ switch(item) {
+ case 0:
+ m_currentMultiplier = 10 * qt_multiplierForUnit(QPrinter::Millimeter, 1);
+ suffix = QString::fromLatin1(" cm");
+ break;
+ case 2:
+ m_currentMultiplier = qt_multiplierForUnit(QPrinter::Inch, 1);
+ suffix = QString::fromLatin1(" in");
+ break;
+ case 3:
+ m_currentMultiplier = qt_multiplierForUnit(QPrinter::Point, 1);
+ suffix = QString::fromLatin1(" pt");
+ break;
+ case 1:
+ default:
+ m_currentMultiplier = qt_multiplierForUnit(QPrinter::Millimeter, 1);
+ suffix = QString::fromLatin1(" mm");
+ break;
+ }
+ const bool old = m_blockSignals;
+ m_blockSignals = true;
+ widget.topMargin->setSuffix(suffix);
+ widget.leftMargin->setSuffix(suffix);
+ widget.rightMargin->setSuffix(suffix);
+ widget.bottomMargin->setSuffix(suffix);
+ widget.paperWidth->setSuffix(suffix);
+ widget.paperHeight->setSuffix(suffix);
+ widget.topMargin->setValue(m_topMargin / m_currentMultiplier);
+ widget.leftMargin->setValue(m_leftMargin / m_currentMultiplier);
+ widget.rightMargin->setValue(m_rightMargin / m_currentMultiplier);
+ widget.bottomMargin->setValue(m_bottomMargin / m_currentMultiplier);
+ widget.paperWidth->setValue(m_paperSize.width() / m_currentMultiplier);
+ widget.paperHeight->setValue(m_paperSize.height() / m_currentMultiplier);
+ m_blockSignals = old;
+}
+
+void QPageSetupWidget::setTopMargin(double newValue)
+{
+ if (m_blockSignals) return;
+ m_topMargin = newValue * m_currentMultiplier;
+ m_pagePreview->setMargins(m_leftMargin, m_topMargin, m_rightMargin, m_bottomMargin);
+}
+
+void QPageSetupWidget::setBottomMargin(double newValue)
+{
+ if (m_blockSignals) return;
+ m_bottomMargin = newValue * m_currentMultiplier;
+ m_pagePreview->setMargins(m_leftMargin, m_topMargin, m_rightMargin, m_bottomMargin);
+}
+
+void QPageSetupWidget::setLeftMargin(double newValue)
+{
+ if (m_blockSignals) return;
+ m_leftMargin = newValue * m_currentMultiplier;
+ m_pagePreview->setMargins(m_leftMargin, m_topMargin, m_rightMargin, m_bottomMargin);
+}
+
+void QPageSetupWidget::setRightMargin(double newValue)
+{
+ if (m_blockSignals) return;
+ m_rightMargin = newValue * m_currentMultiplier;
+ m_pagePreview->setMargins(m_leftMargin, m_topMargin, m_rightMargin, m_bottomMargin);
+}
+
+
+
+QPageSetupDialog::QPageSetupDialog(QPrinter *printer, QWidget *parent)
+ : QAbstractPageSetupDialog(*(new QPageSetupDialogPrivate), printer, parent)
+{
+ Q_D(QPageSetupDialog);
+ d->init();
+}
+
+
+QPageSetupDialog::QPageSetupDialog(QWidget *parent)
+ : QAbstractPageSetupDialog(*(new QPageSetupDialogPrivate), 0, parent)
+{
+ Q_D(QPageSetupDialog);
+ d->init();
+}
+
+/*!
+ \internal
+*/
+int QPageSetupDialog::exec()
+{
+ Q_D(QPageSetupDialog);
+
+ int ret = QDialog::exec();
+ if (ret == Accepted)
+ d->widget->setupPrinter();
+ return ret;
+}
+
+
+QT_END_NAMESPACE
+
+#include "moc_qpagesetupdialog.cpp"
+
+#endif // QT_NO_PRINTDIALOG
diff --git a/src/gui/dialogs/qpagesetupdialog_unix_p.h b/src/gui/dialogs/qpagesetupdialog_unix_p.h
new file mode 100644
index 0000000000..08f0f4d3c6
--- /dev/null
+++ b/src/gui/dialogs/qpagesetupdialog_unix_p.h
@@ -0,0 +1,105 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#ifndef QPAGESETUPWIDGET_H
+#define QPAGESETUPWIDGET_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
+// to version without notice, or even be removed.
+//
+// We mean it.
+//
+//
+
+#include "qglobal.h"
+
+#ifndef QT_NO_PRINTDIALOG
+
+#include <ui_qpagesetupwidget.h>
+
+QT_BEGIN_NAMESPACE
+
+class QPrinter;
+class QPagePreview;
+class QCUPSSupport;
+
+class QPageSetupWidget : public QWidget {
+ Q_OBJECT
+public:
+ QPageSetupWidget(QWidget *parent = 0);
+ QPageSetupWidget(QPrinter *printer, QWidget *parent = 0);
+ void setPrinter(QPrinter *printer);
+ /// copy information from the widget and apply that to the printer.
+ void setupPrinter() const;
+ void selectPrinter(QCUPSSupport *m_cups);
+ void selectPdfPsPrinter(const QPrinter *p);
+
+private slots:
+ void _q_pageOrientationChanged();
+ void _q_paperSizeChanged();
+ void unitChanged(int item);
+ void setTopMargin(double newValue);
+ void setBottomMargin(double newValue);
+ void setLeftMargin(double newValue);
+ void setRightMargin(double newValue);
+
+private:
+ Ui::QPageSetupWidget widget;
+ QPagePreview *m_pagePreview;
+ QPrinter *m_printer;
+ qreal m_leftMargin;
+ qreal m_topMargin;
+ qreal m_rightMargin;
+ qreal m_bottomMargin;
+ QSizeF m_paperSize;
+ qreal m_currentMultiplier;
+ bool m_blockSignals;
+ QCUPSSupport *m_cups;
+};
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_PRINTDIALOG
+#endif
diff --git a/src/gui/dialogs/qpagesetupdialog_win.cpp b/src/gui/dialogs/qpagesetupdialog_win.cpp
new file mode 100644
index 0000000000..4bb571c6bf
--- /dev/null
+++ b/src/gui/dialogs/qpagesetupdialog_win.cpp
@@ -0,0 +1,169 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qpagesetupdialog.h"
+
+#ifndef QT_NO_PRINTDIALOG
+#include <qapplication.h>
+
+#include <private/qprintengine_win_p.h>
+#include <private/qabstractpagesetupdialog_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QPageSetupDialogPrivate : public QAbstractPageSetupDialogPrivate
+{
+};
+
+QPageSetupDialog::QPageSetupDialog(QPrinter *printer, QWidget *parent)
+ : QAbstractPageSetupDialog(*(new QPageSetupDialogPrivate), printer, parent)
+{
+}
+
+QPageSetupDialog::QPageSetupDialog(QWidget *parent)
+ : QAbstractPageSetupDialog(*(new QPageSetupDialogPrivate), 0, parent)
+{
+}
+
+int QPageSetupDialog::exec()
+{
+ Q_D(QPageSetupDialog);
+
+ if (d->printer->outputFormat() != QPrinter::NativeFormat)
+ return Rejected;
+
+ QWin32PrintEngine *engine = static_cast<QWin32PrintEngine*>(d->printer->paintEngine());
+ QWin32PrintEnginePrivate *ep = static_cast<QWin32PrintEnginePrivate *>(engine->d_ptr);
+
+ PAGESETUPDLG psd;
+ memset(&psd, 0, sizeof(PAGESETUPDLG));
+ psd.lStructSize = sizeof(PAGESETUPDLG);
+
+ // we need a temp DEVMODE struct if we don't have a global DEVMODE
+ HGLOBAL hDevMode;
+ int devModeSize;
+ if (!ep->globalDevMode) {
+ QT_WA( { devModeSize = sizeof(DEVMODEW) + ((DEVMODEW *) ep->devMode)->dmDriverExtra; },
+ { devModeSize = sizeof(DEVMODEA) + ((DEVMODEA *) ep->devMode)->dmDriverExtra; });
+ hDevMode = GlobalAlloc(GHND, devModeSize);
+ if (hDevMode) {
+ void *dest = GlobalLock(hDevMode);
+ memcpy(dest, ep->devMode, devModeSize);
+ GlobalUnlock(hDevMode);
+ }
+ psd.hDevMode = hDevMode;
+ } else {
+ psd.hDevMode = ep->devMode;
+ }
+
+ HGLOBAL *tempDevNames = ep->createDevNames();
+ psd.hDevNames = tempDevNames;
+
+ QWidget *parent = parentWidget();
+ parent = parent ? parent->window() : qApp->activeWindow();
+ Q_ASSERT(!parent ||parent->testAttribute(Qt::WA_WState_Created));
+ psd.hwndOwner = parent ? parent->winId() : 0;
+
+ QRect paperRect = d->printer->paperRect();
+ QRect pageRect = d->printer->pageRect();
+
+ psd.Flags = PSD_MARGINS;
+ double multiplier = 1;
+ switch (QLocale::system().measurementSystem()) {
+ case QLocale::MetricSystem:
+ psd.Flags |= PSD_INHUNDREDTHSOFMILLIMETERS;
+ multiplier = 1;
+ break;
+ case QLocale::ImperialSystem:
+ psd.Flags |= PSD_INTHOUSANDTHSOFINCHES;
+ multiplier = 25.4/10;
+ break;
+ }
+
+ QRect marginRect = ep->getPageMargins();
+ psd.rtMargin.left = marginRect.left() / multiplier;
+ psd.rtMargin.top = marginRect.top() / multiplier;
+ psd.rtMargin.right = marginRect.width() / multiplier;;
+ psd.rtMargin.bottom = marginRect.height() / multiplier;;
+
+ bool result = PageSetupDlg(&psd);
+ if (result) {
+ ep->readDevnames(psd.hDevNames);
+ ep->readDevmode(psd.hDevMode);
+
+ QRect theseMargins = QRect(psd.rtMargin.left * multiplier,
+ psd.rtMargin.top * multiplier,
+ psd.rtMargin.right * multiplier,
+ psd.rtMargin.bottom * multiplier);
+
+ if (theseMargins != marginRect) {
+ ep->setPageMargins(psd.rtMargin.left * multiplier,
+ psd.rtMargin.top * multiplier,
+ psd.rtMargin.right * multiplier,
+ psd.rtMargin.bottom * multiplier);
+ }
+
+ ep->updateCustomPaperSize();
+
+ // copy from our temp DEVMODE struct
+ if (!ep->globalDevMode && hDevMode) {
+ void *src = GlobalLock(hDevMode);
+ memcpy(ep->devMode, src, devModeSize);
+ GlobalUnlock(hDevMode);
+ }
+ }
+
+ if (!ep->globalDevMode && hDevMode)
+ GlobalFree(hDevMode);
+ GlobalFree(tempDevNames);
+ done(result);
+ return result;
+}
+
+void QPageSetupDialog::setVisible(bool visible)
+{
+ if (!visible)
+ return;
+ exec();
+}
+
+QT_END_NAMESPACE
+#endif
diff --git a/src/gui/dialogs/qpagesetupwidget.ui b/src/gui/dialogs/qpagesetupwidget.ui
new file mode 100644
index 0000000000..ace2ab8f44
--- /dev/null
+++ b/src/gui/dialogs/qpagesetupwidget.ui
@@ -0,0 +1,353 @@
+<ui version="4.0" >
+ <class>QPageSetupWidget</class>
+ <widget class="QWidget" name="QPageSetupWidget" >
+ <property name="geometry" >
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>416</width>
+ <height>488</height>
+ </rect>
+ </property>
+ <property name="windowTitle" >
+ <string>Form</string>
+ </property>
+ <layout class="QGridLayout" name="gridLayout_3" >
+ <property name="margin" >
+ <number>0</number>
+ </property>
+ <item row="0" column="0" colspan="2" >
+ <layout class="QHBoxLayout" name="horizontalLayout_4" >
+ <item>
+ <widget class="QComboBox" name="unit" />
+ </item>
+ <item>
+ <spacer name="horizontalSpacer_3" >
+ <property name="orientation" >
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0" >
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </item>
+ <item row="1" column="0" colspan="2" >
+ <widget class="QGroupBox" name="groupBox_2" >
+ <property name="title" >
+ <string>Paper</string>
+ </property>
+ <layout class="QGridLayout" name="gridLayout_2" >
+ <item row="0" column="0" >
+ <widget class="QLabel" name="pageSizeLabel" >
+ <property name="text" >
+ <string>Page size:</string>
+ </property>
+ <property name="buddy" >
+ <cstring>paperSize</cstring>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1" >
+ <widget class="QComboBox" name="paperSize" />
+ </item>
+ <item row="1" column="0" >
+ <widget class="QLabel" name="widthLabel" >
+ <property name="text" >
+ <string>Width:</string>
+ </property>
+ <property name="buddy" >
+ <cstring>paperWidth</cstring>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1" >
+ <layout class="QHBoxLayout" name="horizontalLayout_3" >
+ <item>
+ <widget class="QDoubleSpinBox" name="paperWidth" >
+ <property name="maximum" >
+ <double>9999.989999999999782</double>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLabel" name="heightLabel" >
+ <property name="text" >
+ <string>Height:</string>
+ </property>
+ <property name="buddy" >
+ <cstring>paperHeight</cstring>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QDoubleSpinBox" name="paperHeight" >
+ <property name="maximum" >
+ <double>9999.989999999999782</double>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item row="2" column="0" >
+ <widget class="QLabel" name="paperSourceLabel" >
+ <property name="text" >
+ <string>Paper source:</string>
+ </property>
+ <property name="buddy" >
+ <cstring>paperSource</cstring>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="1" >
+ <widget class="QComboBox" name="paperSource" />
+ </item>
+ <item row="1" column="2" >
+ <spacer name="horizontalSpacer_4" >
+ <property name="orientation" >
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0" >
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item row="2" column="0" >
+ <widget class="QGroupBox" name="groupBox_3" >
+ <property name="title" >
+ <string>Orientation</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout" >
+ <item>
+ <widget class="QRadioButton" name="portrait" >
+ <property name="text" >
+ <string>Portrait</string>
+ </property>
+ <property name="checked" >
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QRadioButton" name="landscape" >
+ <property name="text" >
+ <string>Landscape</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QRadioButton" name="reverseLandscape" >
+ <property name="text" >
+ <string>Reverse landscape</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QRadioButton" name="reversePortrait" >
+ <property name="text" >
+ <string>Reverse portrait</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer name="verticalSpacer_5" >
+ <property name="orientation" >
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0" >
+ <size>
+ <width>0</width>
+ <height>0</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item rowspan="2" row="2" column="1" >
+ <widget class="QWidget" native="1" name="preview" />
+ </item>
+ <item row="3" column="0" >
+ <widget class="QGroupBox" name="groupBox" >
+ <property name="title" >
+ <string>Margins</string>
+ </property>
+ <layout class="QHBoxLayout" name="horizontalLayout_2" >
+ <item>
+ <layout class="QGridLayout" name="gridLayout" >
+ <item row="0" column="1" >
+ <widget class="QDoubleSpinBox" name="topMargin" >
+ <property name="toolTip" >
+ <string>top margin</string>
+ </property>
+ <property name="accessibleName" >
+ <string>top margin</string>
+ </property>
+ <property name="alignment" >
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ <property name="maximum" >
+ <double>999.990000000000009</double>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0" colspan="3" >
+ <layout class="QHBoxLayout" name="horizontalLayout" >
+ <item>
+ <spacer name="horizontalSpacer_7" >
+ <property name="orientation" >
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0" >
+ <size>
+ <width>0</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QDoubleSpinBox" name="leftMargin" >
+ <property name="toolTip" >
+ <string>left margin</string>
+ </property>
+ <property name="accessibleName" >
+ <string>left margin</string>
+ </property>
+ <property name="alignment" >
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ <property name="maximum" >
+ <double>999.990000000000009</double>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer name="horizontalSpacer" >
+ <property name="orientation" >
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeType" >
+ <enum>QSizePolicy::MinimumExpanding</enum>
+ </property>
+ <property name="sizeHint" stdset="0" >
+ <size>
+ <width>0</width>
+ <height>0</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QDoubleSpinBox" name="rightMargin" >
+ <property name="toolTip" >
+ <string>right margin</string>
+ </property>
+ <property name="accessibleName" >
+ <string>right margin</string>
+ </property>
+ <property name="alignment" >
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ <property name="maximum" >
+ <double>999.990000000000009</double>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer name="horizontalSpacer_8" >
+ <property name="orientation" >
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0" >
+ <size>
+ <width>0</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </item>
+ <item row="2" column="1" >
+ <widget class="QDoubleSpinBox" name="bottomMargin" >
+ <property name="toolTip" >
+ <string>bottom margin</string>
+ </property>
+ <property name="accessibleName" >
+ <string>bottom margin</string>
+ </property>
+ <property name="alignment" >
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ <property name="maximum" >
+ <double>999.990000000000009</double>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="2" >
+ <spacer name="horizontalSpacer_2" >
+ <property name="orientation" >
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeType" >
+ <enum>QSizePolicy::MinimumExpanding</enum>
+ </property>
+ <property name="sizeHint" stdset="0" >
+ <size>
+ <width>0</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item row="0" column="0" >
+ <spacer name="horizontalSpacer_5" >
+ <property name="orientation" >
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeType" >
+ <enum>QSizePolicy::MinimumExpanding</enum>
+ </property>
+ <property name="sizeHint" stdset="0" >
+ <size>
+ <width>0</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item row="4" column="0" >
+ <spacer name="verticalSpacer" >
+ <property name="orientation" >
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0" >
+ <size>
+ <width>20</width>
+ <height>0</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/src/gui/dialogs/qprintdialog.h b/src/gui/dialogs/qprintdialog.h
new file mode 100644
index 0000000000..b3492266db
--- /dev/null
+++ b/src/gui/dialogs/qprintdialog.h
@@ -0,0 +1,174 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QPRINTDIALOG_H
+#define QPRINTDIALOG_H
+
+#include <QtGui/qabstractprintdialog.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+#ifndef QT_NO_PRINTDIALOG
+
+class QPrintDialogPrivate;
+class QPushButton;
+class QPrinter;
+
+#if defined (Q_OS_UNIX) && !defined(QTOPIA_PRINTDIALOG) && !defined(Q_WS_MAC)
+class QUnixPrintWidgetPrivate;
+
+class Q_GUI_EXPORT QUnixPrintWidget : public QWidget
+{
+ Q_OBJECT
+
+public:
+ QUnixPrintWidget(QPrinter *printer, QWidget *parent = 0);
+ ~QUnixPrintWidget();
+ void updatePrinter();
+
+private:
+ friend class QPrintDialogPrivate;
+ friend class QUnixPrintWidgetPrivate;
+ QUnixPrintWidgetPrivate *d;
+ Q_PRIVATE_SLOT(d, void _q_printerChanged(int))
+ Q_PRIVATE_SLOT(d, void _q_btnBrowseClicked())
+ Q_PRIVATE_SLOT(d, void _q_btnPropertiesClicked())
+};
+#endif
+
+class Q_GUI_EXPORT QPrintDialog : public QAbstractPrintDialog
+{
+ Q_OBJECT
+ Q_DECLARE_PRIVATE(QPrintDialog)
+ Q_ENUMS(PrintDialogOption)
+ Q_PROPERTY(PrintDialogOptions options READ options WRITE setOptions)
+
+public:
+ explicit QPrintDialog(QPrinter *printer, QWidget *parent = 0);
+ explicit QPrintDialog(QWidget *parent = 0);
+ ~QPrintDialog();
+
+ int exec();
+#if defined (Q_OS_UNIX) && !defined(QTOPIA_PRINTDIALOG) && !defined(Q_WS_MAC)
+ virtual void accept();
+#endif
+ void done(int result);
+
+#if defined (Q_OS_UNIX) && defined (QT3_SUPPORT)
+ void setPrinter(QPrinter *, bool = false);
+ QPrinter *printer() const;
+ void addButton(QPushButton *button);
+#endif
+
+ void setOption(PrintDialogOption option, bool on = true);
+ bool testOption(PrintDialogOption option) const;
+ void setOptions(PrintDialogOptions options);
+ PrintDialogOptions options() const;
+
+#if defined(Q_OS_UNIX) || defined(Q_WS_MAC) || defined(Q_OS_WIN)
+ void setVisible(bool visible);
+#endif
+
+#ifdef Q_NO_USING_KEYWORD
+#ifndef Q_QDOC
+ void open() { QDialog::open(); }
+#endif
+#else
+ using QDialog::open;
+#endif
+ void open(QObject *receiver, const char *member);
+
+#ifdef qdoc
+ QPrinter *printer();
+#endif
+
+#ifdef QTOPIA_PRINTDIALOG
+public:
+ bool eventFilter(QObject *, QEvent *);
+#endif
+
+#ifdef Q_NO_USING_KEYWORD
+#ifndef Q_QDOC
+ void accepted() { QDialog::accepted(); }
+#endif
+#else
+ using QDialog::accepted;
+#endif
+
+Q_SIGNALS:
+ void accepted(QPrinter *printer);
+
+private:
+#ifndef QTOPIA_PRINTDIALOG
+ Q_PRIVATE_SLOT(d_func(), void _q_chbPrintLastFirstToggled(bool))
+#if defined (Q_OS_UNIX) && !defined (Q_OS_MAC)
+ Q_PRIVATE_SLOT(d_func(), void _q_collapseOrExpandDialog())
+#endif
+# if defined(Q_OS_UNIX) && !defined (Q_OS_MAC) && !defined(QT_NO_MESSAGEBOX)
+ Q_PRIVATE_SLOT(d_func(), void _q_checkFields())
+# endif
+#else // QTOPIA_PRINTDIALOG
+ Q_PRIVATE_SLOT(d_func(), void _q_okClicked())
+ Q_PRIVATE_SLOT(d_func(),void _q_printerOrFileSelected(QAbstractButton *b))
+ Q_PRIVATE_SLOT(d_func(),void _q_paperSizeSelected(int))
+ Q_PRIVATE_SLOT(d_func(), void _q_orientSelected(int))
+ Q_PRIVATE_SLOT(d_func(), void _q_pageOrderSelected(int))
+ Q_PRIVATE_SLOT(d_func(), void _q_colorModeSelected(QAbstractButton *))
+ Q_PRIVATE_SLOT(d_func(), void _q_setNumCopies(int))
+ Q_PRIVATE_SLOT(d_func(), void _q_printRangeSelected(int))
+ Q_PRIVATE_SLOT(d_func(), void _q_setFirstPage(int))
+ Q_PRIVATE_SLOT(d_func(), void _q_setLastPage(int))
+ Q_PRIVATE_SLOT(d_func(), void _q_fileNameEditChanged(const QString &text))
+#endif // QTOPIA_PRINTDIALOG
+ friend class QUnixPrintWidget;
+};
+
+#endif // QT_NO_PRINTDIALOG
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QPRINTDIALOG_H
diff --git a/src/gui/dialogs/qprintdialog.qrc b/src/gui/dialogs/qprintdialog.qrc
new file mode 100644
index 0000000000..f54eb6b5ee
--- /dev/null
+++ b/src/gui/dialogs/qprintdialog.qrc
@@ -0,0 +1,38 @@
+<!DOCTYPE RCC><RCC version="1.0">
+<qresource prefix="/trolltech/dialogs/qprintpreviewdialog">
+<file>images/fit-page-24.png</file>
+<file>images/fit-page-32.png</file>
+<file>images/fit-width-24.png</file>
+<file>images/fit-width-32.png</file>
+<file>images/go-first-24.png</file>
+<file>images/go-first-32.png</file>
+<file>images/go-last-24.png</file>
+<file>images/go-last-32.png</file>
+<file>images/go-next-24.png</file>
+<file>images/go-next-32.png</file>
+<file>images/go-previous-24.png</file>
+<file>images/go-previous-32.png</file>
+<file>images/layout-landscape-24.png</file>
+<file>images/layout-landscape-32.png</file>
+<file>images/layout-portrait-24.png</file>
+<file>images/layout-portrait-32.png</file>
+<file>images/page-setup-24.png</file>
+<file>images/page-setup-32.png</file>
+<file>images/print-24.png</file>
+<file>images/print-32.png</file>
+<file>images/view-page-multi-24.png</file>
+<file>images/view-page-multi-32.png</file>
+<file>images/view-page-one-24.png</file>
+<file>images/view-page-one-32.png</file>
+<file>images/view-page-sided-24.png</file>
+<file>images/view-page-sided-32.png</file>
+<file>images/zoom-in-24.png</file>
+<file>images/zoom-in-32.png</file>
+<file>images/zoom-out-24.png</file>
+<file>images/zoom-out-32.png</file>
+</qresource>
+<qresource prefix="/trolltech/dialogs/qprintdialog">
+<file>images/status-color.png</file>
+<file>images/status-gray-scale.png</file>
+</qresource>
+</RCC>
diff --git a/src/gui/dialogs/qprintdialog_mac.mm b/src/gui/dialogs/qprintdialog_mac.mm
new file mode 100644
index 0000000000..362dcb0df1
--- /dev/null
+++ b/src/gui/dialogs/qprintdialog_mac.mm
@@ -0,0 +1,428 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QT_NO_PRINTDIALOG
+
+#include <private/qt_mac_p.h>
+
+#include <qhash.h>
+#include <qprintdialog.h>
+#include <private/qapplication_p.h>
+#include <private/qabstractprintdialog_p.h>
+#include <private/qprintengine_mac_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QPrintDialogPrivate : public QAbstractPrintDialogPrivate
+{
+ Q_DECLARE_PUBLIC(QPrintDialog)
+
+public:
+ QPrintDialogPrivate() : ep(0), printPanel(0)
+#ifndef QT_MAC_USE_COCOA
+ ,upp(0)
+#endif
+ {}
+#ifndef QT_MAC_USE_COCOA
+ ~QPrintDialogPrivate() {
+ if (upp) {
+ DisposePMSheetDoneUPP(upp);
+ upp = 0;
+ }
+ QHash<PMPrintSession, QPrintDialogPrivate *>::iterator it = sheetCallbackMap.begin();
+ while (it != sheetCallbackMap.end()) {
+ if (it.value() == this) {
+ it = sheetCallbackMap.erase(it);
+ } else {
+ ++it;
+ }
+ }
+ }
+#endif
+
+#ifndef QT_MAC_USE_COCOA
+ void openCarbonPrintPanel(Qt::WindowModality modality);
+ void closeCarbonPrintPanel();
+ static void printDialogSheetDoneCallback(PMPrintSession printSession, WindowRef /*documentWindow*/, Boolean accepted) {
+ QPrintDialogPrivate *priv = sheetCallbackMap.value(printSession);
+ if (!priv) {
+ qWarning("%s:%d: QPrintDialog::exec: Could not retrieve data structure, "
+ "you most likely now have an infinite loop", __FILE__, __LINE__);
+ return;
+ }
+ priv->q_func()->done(accepted ? QDialog::Accepted : QDialog::Rejected);
+ priv->closeCarbonPrintPanel();
+ }
+#else
+ void openCocoaPrintPanel(Qt::WindowModality modality);
+ void closeCocoaPrintPanel();
+#endif
+ void initBeforeRun();
+
+ inline QPrintDialog *printDialog() { return q_func(); }
+
+ inline void _q_printToFileChanged(int) {}
+ inline void _q_rbPrintRangeToggled(bool) {}
+ inline void _q_printerChanged(int) {}
+#ifndef QT_NO_MESSAGEBOX
+ inline void _q_checkFields() {}
+#endif
+ inline void _q_chbPrintLastFirstToggled(bool) {}
+ inline void _q_paperSizeChanged(int) {}
+ inline void _q_btnBrowseClicked() {}
+ inline void _q_btnPropertiesClicked() {}
+
+ QMacPrintEnginePrivate *ep;
+ NSPrintPanel *printPanel;
+#ifndef QT_MAC_USE_COCOA
+ PMSheetDoneUPP upp;
+ static QHash<PMPrintSession, QPrintDialogPrivate *> sheetCallbackMap;
+#endif
+};
+
+QT_END_NAMESPACE
+
+QT_USE_NAMESPACE
+
+@class QCocoaPrintPanelDelegate;
+
+@interface QCocoaPrintPanelDelegate : NSObject {
+}
+- (void)printPanelDidEnd:(NSPrintPanel *)printPanel
+ returnCode:(int)returnCode contextInfo:(void *)contextInfo;
+@end
+
+@implementation QCocoaPrintPanelDelegate
+- (void)printPanelDidEnd:(NSPrintPanel *)printPanel
+ returnCode:(int)returnCode contextInfo:(void *)contextInfo
+{
+ Q_UNUSED(printPanel);
+
+ QPrintDialogPrivate *d = static_cast<QPrintDialogPrivate *>(contextInfo);
+ QPrintDialog *dialog = d->printDialog();
+ // temporary hack to work around bug in deleteLater() in Qt/Mac Cocoa
+#if 1
+ bool deleteDialog = dialog->testAttribute(Qt::WA_DeleteOnClose);
+ dialog->setAttribute(Qt::WA_DeleteOnClose, false);
+#endif
+
+ if (returnCode == NSOKButton) {
+ UInt32 frompage, topage;
+ PMGetFirstPage(d->ep->settings, &frompage);
+ PMGetLastPage(d->ep->settings, &topage);
+ topage = qMin(UInt32(INT_MAX), topage);
+ dialog->setFromTo(frompage, topage);
+
+ // OK, I need to map these values back let's see
+ // If from is 1 and to is INT_MAX, then print it all
+ // (Apologies to the folks with more than INT_MAX pages)
+ if (dialog->fromPage() == 1 && dialog->toPage() == INT_MAX) {
+ dialog->setPrintRange(QPrintDialog::AllPages);
+ dialog->setFromTo(0, 0);
+ } else {
+ dialog->setPrintRange(QPrintDialog::PageRange); // In a way a lie, but it shouldn't hurt.
+ // Carbon hands us back a very large number here even for ALL, set it to max
+ // in that case to follow the behavior of the other print dialogs.
+ if (dialog->maxPage() < dialog->toPage())
+ dialog->setFromTo(dialog->fromPage(), dialog->maxPage());
+ }
+ // Keep us in sync with file output
+ PMDestinationType dest;
+ PMSessionGetDestinationType(d->ep->session, d->ep->settings, &dest);
+ if (dest == kPMDestinationFile) {
+ QCFType<CFURLRef> file;
+ PMSessionCopyDestinationLocation(d->ep->session, d->ep->settings, &file);
+ UInt8 localFile[2048]; // Assuming there's a POSIX file system here.
+ CFURLGetFileSystemRepresentation(file, true, localFile, sizeof(localFile));
+ d->ep->outputFilename
+ = QString::fromUtf8(reinterpret_cast<const char *>(localFile));
+ } else {
+ // Keep output format.
+ QPrinter::OutputFormat format;
+ format = d->printer->outputFormat();
+ d->printer->setOutputFileName(QString());
+ d->printer->setOutputFormat(format);
+ }
+ }
+
+ dialog->done((returnCode == NSOKButton) ? QDialog::Accepted : QDialog::Rejected);
+#if 1
+ if (deleteDialog)
+ delete dialog;
+#endif
+}
+@end
+
+QT_BEGIN_NAMESPACE
+
+extern void macStartInterceptWindowTitle(QWidget *window);
+extern void macStopInterceptWindowTitle();
+
+
+void QPrintDialogPrivate::initBeforeRun()
+{
+ Q_Q(QPrintDialog);
+ // If someone is reusing a QPrinter object, the end released all our old
+ // information. In this case, we must reinitialize.
+ if (ep->session == 0)
+ ep->initialize();
+
+
+ // It seems the only way that PM lets you use all is if the minimum
+ // for the page range is 1. This _kind of_ makes sense if you think about
+ // it. However, calling PMSetFirstPage() or PMSetLastPage() always enforces
+ // the range.
+ PMSetPageRange(ep->settings, q->minPage(), q->maxPage());
+ if (q->printRange() == QAbstractPrintDialog::PageRange) {
+ PMSetFirstPage(ep->settings, q->fromPage(), false);
+ PMSetLastPage(ep->settings, q->toPage(), false);
+ }
+}
+
+#ifndef QT_MAC_USE_COCOA
+QHash<PMPrintSession, QPrintDialogPrivate *> QPrintDialogPrivate::sheetCallbackMap;
+void QPrintDialogPrivate::openCarbonPrintPanel(Qt::WindowModality modality)
+{
+ Q_Q(QPrintDialog);
+ initBeforeRun();
+ sheetCallbackMap.insert(ep->session, this);
+ if (modality == Qt::ApplicationModal) {
+ QWidget modal_widg(0, Qt::Window);
+ modal_widg.setObjectName(QLatin1String(__FILE__ "__modal_dlg"));
+ modal_widg.createWinId();
+ QApplicationPrivate::enterModal(&modal_widg);
+ QApplicationPrivate::native_modal_dialog_active = true;
+ Boolean acceptStatus;
+ PMSessionPrintDialog(ep->session, ep->settings, ep->format, &acceptStatus);
+ QApplicationPrivate::leaveModal(&modal_widg);
+ QApplicationPrivate::native_modal_dialog_active = false;
+ printDialogSheetDoneCallback(ep->session, 0, acceptStatus);
+ } else {
+ // Window Modal means that we use a sheet at the moment, there's no other way to do it correctly.
+ if (!upp)
+ upp = NewPMSheetDoneUPP(QPrintDialogPrivate::printDialogSheetDoneCallback);
+ PMSessionUseSheets(ep->session, qt_mac_window_for(q->parentWidget()), upp);
+ QApplicationPrivate::native_modal_dialog_active = true;
+ Boolean unused;
+ PMSessionPrintDialog(ep->session, ep->settings, ep->format, &unused);
+ }
+}
+
+void QPrintDialogPrivate::closeCarbonPrintPanel()
+{
+ Q_Q(QPrintDialog);
+ QApplicationPrivate::native_modal_dialog_active = false;
+ if (q->result() == QDialog::Accepted) {
+ UInt32 frompage, topage;
+ PMGetFirstPage(ep->settings, &frompage);
+ PMGetLastPage(ep->settings, &topage);
+ topage = qMin(UInt32(INT_MAX), topage);
+ q->setFromTo(frompage, topage);
+
+ // OK, I need to map these values back let's see
+ // If from is 1 and to is INT_MAX, then print it all
+ // (Apologies to the folks with more than INT_MAX pages)
+ // ...that's a joke.
+ if (q->fromPage() == 1 && q->toPage() == INT_MAX) {
+ q->setPrintRange(QAbstractPrintDialog::AllPages);
+ q->setFromTo(0,0);
+ } else {
+ q->setPrintRange(QAbstractPrintDialog::PageRange); // In a way a lie, but it shouldn't hurt.
+ // Carbon hands us back a very large number here even for ALL, set it to max
+ // in that case to follow the behavior of the other print dialogs.
+ if (q->maxPage() < q->toPage())
+ q->setFromTo(q->fromPage(), q->maxPage());
+ }
+ // Keep us in sync with file output
+ PMDestinationType dest;
+ PMSessionGetDestinationType(ep->session, ep->settings, &dest);
+ if (dest == kPMDestinationFile) {
+ QCFType<CFURLRef> file;
+ PMSessionCopyDestinationLocation(ep->session, ep->settings, &file);
+ UInt8 localFile[2048]; // Assuming there's a POSIX file system here.
+ CFURLGetFileSystemRepresentation(file, true, localFile, sizeof(localFile));
+ ep->outputFilename = QString::fromUtf8(reinterpret_cast<const char *>(localFile));
+ } else {
+ ep->outputFilename = QString();
+ }
+ }
+ sheetCallbackMap.remove(ep->session);
+}
+#else
+void QPrintDialogPrivate::openCocoaPrintPanel(Qt::WindowModality modality)
+{
+ Q_Q(QPrintDialog);
+
+ initBeforeRun();
+
+ QPrintDialog::PrintDialogOptions qtOptions = q->options();
+ NSPrintPanelOptions macOptions = NSPrintPanelShowsCopies;
+ if (qtOptions & QPrintDialog::PrintPageRange)
+ macOptions |= NSPrintPanelShowsPageRange;
+ if (qtOptions & QPrintDialog::PrintShowPageSize)
+ macOptions |= NSPrintPanelShowsPaperSize | NSPrintPanelShowsPageSetupAccessory
+ | NSPrintPanelShowsOrientation;
+
+ macStartInterceptWindowTitle(q);
+ printPanel = [NSPrintPanel printPanel];
+ QCocoaPrintPanelDelegate *delegate = [[QCocoaPrintPanelDelegate alloc] init];
+ [printPanel setOptions:macOptions];
+
+ if (modality == Qt::ApplicationModal) {
+ int rval = [printPanel runModalWithPrintInfo:ep->printInfo];
+ [delegate printPanelDidEnd:printPanel returnCode:rval contextInfo:this];
+ } else {
+ Q_ASSERT(q->parentWidget());
+ NSWindow *windowRef = qt_mac_window_for(q->parentWidget());
+ [printPanel beginSheetWithPrintInfo:ep->printInfo
+ modalForWindow:windowRef
+ delegate:delegate
+ didEndSelector:@selector(printPanelDidEnd:returnCode:contextInfo:)
+ contextInfo:this];
+ }
+
+ macStopInterceptWindowTitle();
+}
+
+void QPrintDialogPrivate::closeCocoaPrintPanel()
+{
+ // ###
+}
+#endif
+
+static bool warnIfNotNative(QPrinter *printer)
+{
+ if (printer->outputFormat() != QPrinter::NativeFormat) {
+ qWarning("QPrintDialog: Cannot be used on non-native printers");
+ return false;
+ }
+ return true;
+}
+
+
+QPrintDialog::QPrintDialog(QPrinter *printer, QWidget *parent)
+ : QAbstractPrintDialog(*(new QPrintDialogPrivate), printer, parent)
+{
+ Q_D(QPrintDialog);
+ if (!warnIfNotNative(d->printer))
+ return;
+ d->ep = static_cast<QMacPrintEngine *>(d->printer->paintEngine())->d_func();
+}
+
+QPrintDialog::QPrintDialog(QWidget *parent)
+ : QAbstractPrintDialog(*(new QPrintDialogPrivate), 0, parent)
+{
+ Q_D(QPrintDialog);
+ if (!warnIfNotNative(d->printer))
+ return;
+ d->ep = static_cast<QMacPrintEngine *>(d->printer->paintEngine())->d_func();
+}
+
+QPrintDialog::~QPrintDialog()
+{
+}
+
+int QPrintDialog::exec()
+{
+ Q_D(QPrintDialog);
+ if (!warnIfNotNative(d->printer))
+ return QDialog::Rejected;
+
+#ifndef QT_MAC_USE_COCOA
+ d->openCarbonPrintPanel(Qt::ApplicationModal);
+#else
+ QMacCocoaAutoReleasePool pool;
+
+ d->openCocoaPrintPanel(Qt::ApplicationModal);
+ d->closeCocoaPrintPanel();
+#endif
+ return result();
+}
+
+#ifdef QT3_SUPPORT
+QPrinter *QPrintDialog::printer() const
+{
+ Q_D(const QPrintDialog);
+ return d->printer;
+}
+#endif
+
+/*!
+ \reimp
+*/
+void QPrintDialog::setVisible(bool visible)
+{
+ Q_D(QPrintDialog);
+
+ bool isCurrentlyVisible = (d->printPanel != 0);
+
+ if (!visible == !isCurrentlyVisible)
+ return;
+
+ if (d->printer->outputFormat() != QPrinter::NativeFormat)
+ return;
+
+ if (visible) {
+#ifndef QT_MAC_USE_COCOA
+ d->openCarbonPrintPanel(parentWidget() ? Qt::WindowModal
+ : Qt::ApplicationModal);
+#else
+ d->openCocoaPrintPanel(parentWidget() ? Qt::WindowModal
+ : Qt::ApplicationModal);
+#endif
+ return;
+ } else {
+ if (d->printPanel) {
+#ifndef QT_MAC_USE_COCOA
+ d->closeCarbonPrintPanel();
+#else
+ d->closeCocoaPrintPanel();
+#endif
+ return;
+ }
+ }
+}
+
+QT_END_NAMESPACE
+
+#include "moc_qprintdialog.cpp"
+
+#endif // QT_NO_PRINTDIALOG
diff --git a/src/gui/dialogs/qprintdialog_qws.cpp b/src/gui/dialogs/qprintdialog_qws.cpp
new file mode 100644
index 0000000000..70eeffa4b5
--- /dev/null
+++ b/src/gui/dialogs/qprintdialog_qws.cpp
@@ -0,0 +1,556 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qplatformdefs.h"
+
+#include <private/qabstractprintdialog_p.h>
+#include "qprintdialog.h"
+
+#ifndef QT_NO_PRINTDIALOG
+
+#include "qapplication.h"
+#include "qbuttongroup.h"
+#include "qradiobutton.h"
+#include "qcombobox.h"
+#include "qspinbox.h"
+#include "qprinter.h"
+#include "qlineedit.h"
+#include "qdir.h"
+#include "qmessagebox.h"
+#include "qinputdialog.h"
+#include "qlayout.h"
+#include "qlabel.h"
+
+#include "qlibrary.h"
+
+#ifndef QT_NO_NIS
+
+#ifndef BOOL_DEFINED
+#define BOOL_DEFINED
+#endif
+
+#include <rpcsvc/ypclnt.h>
+#include <rpcsvc/yp_prot.h>
+
+#endif //QT_NO_NIS
+
+#include <ctype.h>
+#include <stdlib.h>
+
+#include <qdebug.h>
+
+QT_BEGIN_NAMESPACE
+
+typedef void (*QPrintDialogCreator)(QPrintDialog *parent);
+Q_GUI_EXPORT QPrintDialogCreator _qt_print_dialog_creator;
+
+class QPrintDialogPrivate : public QAbstractPrintDialogPrivate
+{
+ Q_DECLARE_PUBLIC(QPrintDialog)
+public:
+ QButtonGroup *printerOrFile;
+ bool outputToFile;
+ QRadioButton *printToPrinterButton;
+ QRadioButton *printToFileButton;
+ QLineEdit *fileName;
+
+ QButtonGroup *colorMode;
+ QRadioButton *printColor;
+ QRadioButton *printGray;
+ QPrinter::ColorMode colorMode2;
+
+ QComboBox *orientationCombo, *sizeCombo;
+ QPrinter::PaperSize pageSize;
+ QPrinter::Orientation orientation;
+
+ QSpinBox *copies;
+ int numCopies;
+ QPrinter::PaperSize indexToPaperSize[QPrinter::NPaperSize];
+
+ QComboBox *rangeCombo;
+ QSpinBox *firstPage;
+ QSpinBox *lastPage;
+
+ QComboBox *pageOrderCombo;
+ QPrinter::PageOrder pageOrder2;
+
+ QString faxNum;
+
+ void init();
+
+ void _q_okClicked();
+ void _q_printerOrFileSelected(QAbstractButton *b);
+ void _q_paperSizeSelected(int);
+ void _q_orientSelected(int);
+ void _q_pageOrderSelected(int);
+ void _q_colorModeSelected(QAbstractButton *);
+ void _q_setNumCopies(int);
+ void _q_printRangeSelected(int);
+ void _q_setFirstPage(int);
+ void _q_setLastPage(int);
+ void _q_fileNameEditChanged(const QString &text);
+
+ void setupDestination();
+ void setupPrinterSettings();
+ void setupPaper();
+ void setupOptions();
+
+ void setPrinter(QPrinter *p, bool pickUpSettings);
+};
+
+static void isc(QPrintDialogPrivate *d, const QString & text,
+ QPrinter::PaperSize ps);
+
+void QPrintDialogPrivate::_q_okClicked()
+{
+ Q_Q(QPrintDialog);
+#ifndef QT_NO_MESSAGEBOX
+ if (outputToFile && fileName->isModified() && QFileInfo(fileName->text()).exists()) {
+ int confirm = QMessageBox::warning(
+ q, QPrintDialog::tr("File exists"),
+ QPrintDialog::tr("<qt>Do you want to overwrite it?</qt>"),
+ QMessageBox::Yes, QMessageBox::No);
+ if (confirm == QMessageBox::No)
+ return;
+ }
+#endif // QT_NO_MESSAGEBOX
+
+ lastPage->interpretText();
+ firstPage->interpretText();
+ copies->interpretText();
+ if (outputToFile) {
+ printer->setOutputFileName(fileName->text());
+ }
+ printer->setOrientation(orientation);
+ printer->setPaperSize(pageSize);
+ printer->setPageOrder(pageOrder2);
+ printer->setColorMode(colorMode2);
+ printer->setNumCopies(numCopies);
+
+ switch ((rangeCombo->itemData(rangeCombo->currentIndex())).toInt()){
+ case (int)QPrintDialog::AllPages:
+ q->setPrintRange(QPrintDialog::AllPages);
+ q->setFromTo(0, 0);
+ break;
+ case (int)QPrintDialog::Selection:
+ q->setPrintRange(QPrintDialog::Selection);
+ q->setFromTo(0, 0);
+ break;
+ case (int)QPrintDialog::PageRange:
+ q->setPrintRange(QPrintDialog::PageRange);
+ q->setFromTo(firstPage->value(), lastPage->value());
+ break;
+ }
+ q->accept();
+}
+
+void QPrintDialogPrivate::_q_printerOrFileSelected(QAbstractButton *b)
+{
+ outputToFile = (b == printToFileButton);
+ if (outputToFile) {
+ _q_fileNameEditChanged(fileName->text());
+ if (!fileName->isModified() && fileName->text().isEmpty()) {
+ QString file = "print.tiff";
+ fileName->setText(file);
+ fileName->setCursorPosition(file.length());
+ fileName->selectAll();
+ fileName->setModified(true); // confirm overwrite when OK clicked
+
+ }
+ fileName->setEnabled(true);
+ fileName->setFocus();
+ } else {
+ fileName->setText(QString());
+ if (fileName->isEnabled())
+ fileName->setEnabled(false);
+ }
+}
+
+void QPrintDialogPrivate::_q_paperSizeSelected(int id)
+{
+ if (id < QPrinter::NPaperSize)
+ pageSize = QPrinter::PaperSize(indexToPaperSize[id]);
+}
+
+void QPrintDialogPrivate::_q_orientSelected(int id)
+{
+ orientation = (QPrinter::Orientation)id;
+}
+
+void QPrintDialogPrivate::_q_pageOrderSelected(int id)
+{
+ pageOrder2 = (QPrinter::PageOrder)id;
+}
+
+void QPrintDialogPrivate::_q_colorModeSelected(QAbstractButton *b)
+{
+ colorMode2 = (b == printColor) ? QPrinter::Color : QPrinter::GrayScale;
+}
+
+void QPrintDialogPrivate::_q_setNumCopies(int copies)
+{
+ numCopies = copies;
+}
+
+void QPrintDialogPrivate::_q_printRangeSelected(int id)
+{
+ bool enable = (rangeCombo->itemData(id).toInt() == (int)QPrintDialog::PageRange);
+ firstPage->setEnabled(enable);
+ lastPage->setEnabled(enable);
+}
+
+void QPrintDialogPrivate::_q_setFirstPage(int fp)
+{
+ Q_Q(QPrintDialog);
+ if (printer) {
+ lastPage->setMinimum(fp);
+ lastPage->setMaximum(qMax(fp, q->maxPage()));
+ }
+}
+
+void QPrintDialogPrivate::_q_setLastPage(int lp)
+{
+ Q_Q(QPrintDialog);
+ if (printer) {
+ firstPage->setMinimum(qMin(lp, q->minPage()));
+ firstPage->setMaximum(lp);
+ }
+}
+
+void QPrintDialogPrivate::_q_fileNameEditChanged(const QString &text)
+{
+ Q_UNUSED(text);
+}
+
+void QPrintDialogPrivate::setupDestination()
+{
+ Q_Q(QPrintDialog);
+
+ // print destinations
+ printerOrFile = new QButtonGroup(q);
+ QObject::connect(printerOrFile, SIGNAL(buttonClicked(QAbstractButton *)),
+ q, SLOT(_q_printerOrFileSelected(QAbstractButton *)));
+
+ printToPrinterButton = q->findChild<QRadioButton *>("printToPrinterButton");
+ printerOrFile->addButton(printToPrinterButton);
+ printToFileButton = q->findChild<QRadioButton *>("printToFileButton");
+ printerOrFile->addButton(printToFileButton);
+
+ // file name
+ fileName = q->findChild<QLineEdit *>("fileName");
+ QObject::connect(fileName, SIGNAL(textChanged(QString)),
+ q, SLOT(_q_fileNameEditChanged(QString)));
+
+ outputToFile = false;
+}
+
+void QPrintDialogPrivate::setupPrinterSettings()
+{
+ Q_Q(QPrintDialog);
+
+ // color mode
+ colorMode = new QButtonGroup(q);
+ QObject::connect(colorMode, SIGNAL(buttonClicked(QAbstractButton *)),
+ q, SLOT(_q_colorModeSelected(QAbstractButton *)));
+
+ printColor = q->findChild<QRadioButton *>("printColor");
+ colorMode->addButton(printColor);
+ printGray = q->findChild<QRadioButton *>("printGray");
+ colorMode->addButton(printGray);
+}
+
+void isc(QPrintDialogPrivate *ptr, const QString & text, QPrinter::PaperSize ps)
+{
+ if (ptr && !text.isEmpty() && ps < QPrinter::NPaperSize) {
+ ptr->sizeCombo->addItem(text);
+ int index = ptr->sizeCombo->count()-1;
+ if (index >= 0 && index < QPrinter::NPaperSize)
+ ptr->indexToPaperSize[index] = ps;
+ }
+}
+
+void QPrintDialogPrivate::setupPaper()
+{
+ Q_Q(QPrintDialog);
+
+ pageSize = QPrinter::A4;
+
+ // paper orientation
+ orientationCombo = q->findChild<QComboBox *>("orientationCombo");
+ orientation = QPrinter::Portrait;
+ QObject::connect(orientationCombo, SIGNAL(activated(int)),
+ q, SLOT(_q_orientSelected(int)));
+
+ // paper size
+ sizeCombo = q->findChild<QComboBox *>("sizeCombo");
+
+ int n;
+ for(n=0; n<QPrinter::NPaperSize; n++)
+ indexToPaperSize[n] = QPrinter::A4;
+
+ isc(this, QPrintDialog::tr("A0 (841 x 1189 mm)"), QPrinter::A0);
+ isc(this, QPrintDialog::tr("A1 (594 x 841 mm)"), QPrinter::A1);
+ isc(this, QPrintDialog::tr("A2 (420 x 594 mm)"), QPrinter::A2);
+ isc(this, QPrintDialog::tr("A3 (297 x 420 mm)"), QPrinter::A3);
+ isc(this, QPrintDialog::tr("A4 (210 x 297 mm, 8.26 x 11.7 inches)"), QPrinter::A4);
+ isc(this, QPrintDialog::tr("A5 (148 x 210 mm)"), QPrinter::A5);
+ isc(this, QPrintDialog::tr("A6 (105 x 148 mm)"), QPrinter::A6);
+ isc(this, QPrintDialog::tr("A7 (74 x 105 mm)"), QPrinter::A7);
+ isc(this, QPrintDialog::tr("A8 (52 x 74 mm)"), QPrinter::A8);
+ isc(this, QPrintDialog::tr("A9 (37 x 52 mm)"), QPrinter::A9);
+ isc(this, QPrintDialog::tr("B0 (1000 x 1414 mm)"), QPrinter::B0);
+ isc(this, QPrintDialog::tr("B1 (707 x 1000 mm)"), QPrinter::B1);
+ isc(this, QPrintDialog::tr("B2 (500 x 707 mm)"), QPrinter::B2);
+ isc(this, QPrintDialog::tr("B3 (353 x 500 mm)"), QPrinter::B3);
+ isc(this, QPrintDialog::tr("B4 (250 x 353 mm)"), QPrinter::B4);
+ isc(this, QPrintDialog::tr("B5 (176 x 250 mm, 6.93 x 9.84 inches)"), QPrinter::B5);
+ isc(this, QPrintDialog::tr("B6 (125 x 176 mm)"), QPrinter::B6);
+ isc(this, QPrintDialog::tr("B7 (88 x 125 mm)"), QPrinter::B7);
+ isc(this, QPrintDialog::tr("B8 (62 x 88 mm)"), QPrinter::B8);
+ isc(this, QPrintDialog::tr("B9 (44 x 62 mm)"), QPrinter::B9);
+ isc(this, QPrintDialog::tr("B10 (31 x 44 mm)"), QPrinter::B10);
+ isc(this, QPrintDialog::tr("C5E (163 x 229 mm)"), QPrinter::C5E);
+ isc(this, QPrintDialog::tr("DLE (110 x 220 mm)"), QPrinter::DLE);
+ isc(this, QPrintDialog::tr("Executive (7.5 x 10 inches, 191 x 254 mm)"), QPrinter::Executive);
+ isc(this, QPrintDialog::tr("Folio (210 x 330 mm)"), QPrinter::Folio);
+ isc(this, QPrintDialog::tr("Ledger (432 x 279 mm)"), QPrinter::Ledger);
+ isc(this, QPrintDialog::tr("Legal (8.5 x 14 inches, 216 x 356 mm)"), QPrinter::Legal);
+ isc(this, QPrintDialog::tr("Letter (8.5 x 11 inches, 216 x 279 mm)"), QPrinter::Letter);
+ isc(this, QPrintDialog::tr("Tabloid (279 x 432 mm)"), QPrinter::Tabloid);
+ isc(this, QPrintDialog::tr("US Common #10 Envelope (105 x 241 mm)"), QPrinter::Comm10E);
+
+ QObject::connect(sizeCombo, SIGNAL(activated(int)),
+ q, SLOT(_q_paperSizeSelected(int)));
+}
+
+void QPrintDialogPrivate::setupOptions()
+{
+ Q_Q(QPrintDialog);
+
+ // no. of copies
+ copies = q->findChild<QSpinBox *>("copies");
+ QObject::connect(copies, SIGNAL(valueChanged(int)),
+ q, SLOT(_q_setNumCopies(int)));
+
+ // print range
+ rangeCombo = q->findChild<QComboBox *>("rangeCombo");
+ rangeCombo->addItem(QPrintDialog::tr("Print all"), QPrintDialog::AllPages);
+ rangeCombo->addItem(QPrintDialog::tr("Print selection"), QPrintDialog::Selection);
+ rangeCombo->addItem(QPrintDialog::tr("Print range"), QPrintDialog::PageRange);
+ QObject::connect(rangeCombo, SIGNAL(activated(int)),
+ q, SLOT(_q_printRangeSelected(int)));
+
+ // page range
+ firstPage = q->findChild<QSpinBox *>("firstPage");
+ firstPage->setRange(1, 9999);
+ firstPage->setValue(1);
+ QObject::connect(firstPage, SIGNAL(valueChanged(int)),
+ q, SLOT(_q_setFirstPage(int)));
+
+ lastPage = q->findChild<QSpinBox *>("lastPage");
+ lastPage->setRange(1, 9999);
+ lastPage->setValue(1);
+ QObject::connect(lastPage, SIGNAL(valueChanged(int)),
+ q, SLOT(_q_setLastPage(int)));
+
+ // print order
+ pageOrderCombo = q->findChild<QComboBox *>("pageOrderCombo");
+ QObject::connect(pageOrderCombo, SIGNAL(activated(int)),
+ q, SLOT(_q_pageOrderSelected(int)));
+}
+
+bool QPrintDialog::eventFilter(QObject *o, QEvent *e)
+{
+ Q_UNUSED(o);
+
+ Q_D(QPrintDialog);
+ switch (e->type()){
+ case QEvent::KeyPress:
+ switch (static_cast<QKeyEvent*>(e)->key()) {
+ case Qt::Key_Back:
+ d->_q_okClicked();
+ return true;
+ }
+ break;
+ default:
+ break;
+ }
+ return false;
+}
+
+QPrintDialog::QPrintDialog(QPrinter *printer, QWidget *parent)
+ : QAbstractPrintDialog(*(new QPrintDialogPrivate), printer, parent)
+{
+ d_func()->init();
+}
+
+QPrintDialog::QPrintDialog(QWidget *parent)
+ : QAbstractPrintDialog(*(new QPrintDialogPrivate), 0, parent)
+{
+ d_func()->init();
+}
+
+QPrintDialog::~QPrintDialog()
+{
+}
+
+void QPrintDialogPrivate::setPrinter(QPrinter *p, bool pickUpSettings)
+{
+ Q_Q(QPrintDialog);
+ printer = p;
+
+ if (p && pickUpSettings) {
+ // top to bottom in the old dialog.
+ // printer or file
+ outputToFile = !p->outputFileName().isEmpty() && q->isOptionEnabled(QPrintDialog::PrintToFile);
+ if (outputToFile)
+ printToFileButton->setChecked(true);
+ else
+ printToPrinterButton->setChecked(true);
+ fileName->setEnabled(outputToFile);
+
+ // file name
+ if (q->isOptionEnabled(QPrintDialog::PrintToFile)) {
+ fileName->setText(p->outputFileName());
+ fileName->setModified(!fileName->text().isEmpty());
+ } else {
+ printToFileButton->setEnabled(false);
+ }
+
+ // orientation
+ orientationCombo->setCurrentIndex((int)p->orientation());
+ _q_orientSelected(p->orientation());
+
+ // page size
+ int n = 0;
+ while (n < QPrinter::NPaperSize &&
+ indexToPaperSize[n] != p->pageSize())
+ n++;
+ sizeCombo->setCurrentIndex(n);
+ _q_paperSizeSelected(n);
+
+ // page order
+ pageOrder2 = p->pageOrder();
+ pageOrderCombo->setCurrentIndex((int)pageOrder2);
+
+ // color mode
+ colorMode2 = p->colorMode();
+ if (colorMode2 == QPrinter::Color)
+ printColor->setChecked(true);
+ else
+ printGray->setChecked(true);
+
+ // number of copies
+ copies->setValue(p->numCopies());
+ _q_setNumCopies(p->numCopies());
+ }
+
+ if (p) {
+ if (!q->isOptionEnabled(QPrintDialog::PrintSelection)
+ && rangeCombo->findData(QPrintDialog::Selection) > 0)
+ rangeCombo->removeItem(rangeCombo->findData(QPrintDialog::Selection));
+ if (!q->isOptionEnabled(QPrintDialog::PrintPageRange)
+ && rangeCombo->findData(QPrintDialog::PageRange) > 0)
+ rangeCombo->removeItem(rangeCombo->findData(QPrintDialog::PageRange));
+
+ switch (q->printRange()) {
+ case QPrintDialog::AllPages:
+ rangeCombo->setCurrentIndex((int)(QPrintDialog::AllPages));
+ break;
+ case QPrintDialog::Selection:
+ rangeCombo->setCurrentIndex((int)(QPrintDialog::Selection));
+ break;
+ case QPrintDialog::PageRange:
+ rangeCombo->setCurrentIndex((int)(QPrintDialog::PageRange));
+ break;
+ }
+ }
+
+ if (p && q->maxPage()) {
+ int from = q->minPage();
+ int to = q->maxPage();
+ if (q->printRange() == QPrintDialog::PageRange) {
+ from = q->fromPage();
+ to = q->toPage();
+ }
+ firstPage->setRange(q->minPage(), to);
+ lastPage->setRange(from, q->maxPage());
+ firstPage->setValue(from);
+ lastPage->setValue(to);
+ }
+}
+
+int QPrintDialog::exec()
+{
+ Q_D(QPrintDialog);
+ d->setPrinter(d->printer, true);
+ return QDialog::exec();
+}
+
+void QPrintDialogPrivate::init()
+{
+ Q_Q(QPrintDialog);
+ numCopies = 1;
+
+ if (_qt_print_dialog_creator)
+ (*_qt_print_dialog_creator)(q);
+
+ setupDestination();
+ setupPrinterSettings();
+ setupPaper();
+ setupOptions();
+
+ setPrinter(printer, true);
+
+ q->installEventFilter(q);
+}
+
+void QPrintDialog::setVisible(bool visible)
+{
+ QAbstractPrintDialog::setVisible(visible);
+}
+
+QT_END_NAMESPACE
+
+#include "moc_qprintdialog.cpp"
+#include "qrc_qprintdialog.cpp"
+
+#endif // QT_NO_PRINTDIALOG
diff --git a/src/gui/dialogs/qprintdialog_unix.cpp b/src/gui/dialogs/qprintdialog_unix.cpp
new file mode 100644
index 0000000000..76c22d0347
--- /dev/null
+++ b/src/gui/dialogs/qprintdialog_unix.cpp
@@ -0,0 +1,1266 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qplatformdefs.h"
+
+#ifndef QT_NO_PRINTDIALOG
+
+#include "private/qabstractprintdialog_p.h"
+#include "qfiledialog_p.h"
+#include <QtGui/qmessagebox.h>
+#include "qprintdialog.h"
+#include "qfiledialog.h"
+#include <QtCore/qdir.h>
+#include <QtGui/qfilesystemmodel.h>
+#include <QtGui/qstyleditemdelegate.h>
+#include <QtGui/qprinter.h>
+
+#include <QtGui/qdialogbuttonbox.h>
+
+#include "ui_qprintpropertieswidget.h"
+#include "ui_qprintsettingsoutput.h"
+#include "ui_qprintwidget.h"
+
+#if !defined(QT_NO_CUPS) && !defined(QT_NO_LIBRARY)
+# include <private/qcups_p.h>
+# include <cups/cups.h>
+# include <private/qpdf_p.h>
+#else
+# include <QtCore/qlibrary.h>
+#endif
+
+#include <private/qprinterinfo_unix_p.h>
+
+QT_BEGIN_NAMESPACE
+
+extern int qt_printerRealNumCopies(QPaintEngine *);
+
+class QOptionTreeItem;
+class QPPDOptionsModel;
+
+class QPrintPropertiesDialog : public QDialog
+{
+ Q_OBJECT
+public:
+ QPrintPropertiesDialog(QAbstractPrintDialog *parent = 0);
+ ~QPrintPropertiesDialog();
+
+#if !defined(QT_NO_CUPS) && !defined(QT_NO_LIBRARY)
+ void setCups(QCUPSSupport *cups) { m_cups = cups; }
+ void addItemToOptions(QOptionTreeItem *parent, QList<const ppd_option_t*>& options, QList<const char*>& markedOptions) const;
+#endif
+
+ void selectPrinter();
+ void selectPdfPsPrinter(const QPrinter *p);
+
+ /// copy printer properties to the widget
+ void applyPrinterProperties(QPrinter *p);
+ void setupPrinter() const;
+
+protected:
+ void showEvent(QShowEvent* event);
+
+private:
+ Ui::QPrintPropertiesWidget widget;
+ QDialogButtonBox *m_buttons;
+#if !defined(QT_NO_CUPS) && !defined(QT_NO_LIBRARY)
+ QCUPSSupport *m_cups;
+ QPPDOptionsModel *m_cupsOptionsModel;
+#endif
+};
+
+class QPrintDialogPrivate : public QAbstractPrintDialogPrivate
+{
+ Q_DECLARE_PUBLIC(QPrintDialog)
+ Q_DECLARE_TR_FUNCTIONS(QPrintDialog)
+public:
+ QPrintDialogPrivate();
+ ~QPrintDialogPrivate();
+
+ void init();
+ /// copy printer properties to the widget
+ void applyPrinterProperties(QPrinter *p);
+
+#if !defined(QT_NO_CUPS) && !defined(QT_NO_LIBRARY)
+ void selectPrinter(QCUPSSupport *cups);
+#endif
+
+ void _q_chbPrintLastFirstToggled(bool);
+#ifndef QT_NO_MESSAGEBOX
+ void _q_checkFields();
+#endif
+ void _q_collapseOrExpandDialog();
+
+ void setupPrinter();
+ void updateWidgets();
+
+ virtual void setTabs(const QList<QWidget*> &tabs);
+
+ Ui::QPrintSettingsOutput options;
+ QUnixPrintWidget *top;
+ QWidget *bottom;
+ QDialogButtonBox *buttons;
+ QPushButton *collapseButton;
+};
+
+#if defined (Q_OS_UNIX)
+class QUnixPrintWidgetPrivate
+{
+public:
+ QUnixPrintWidgetPrivate(QUnixPrintWidget *q);
+ ~QUnixPrintWidgetPrivate();
+
+ /// copy printer properties to the widget
+ void applyPrinterProperties(QPrinter *p);
+ bool checkFields();
+ void setupPrinter();
+ void setOptionsPane(QPrintDialogPrivate *pane);
+
+// slots
+ void _q_printerChanged(int index);
+ void _q_btnPropertiesClicked();
+ void _q_btnBrowseClicked();
+
+ QUnixPrintWidget * const parent;
+ QPrintPropertiesDialog *propertiesDialog;
+ Ui::QPrintWidget widget;
+ QAbstractPrintDialog * q;
+ QPrinter *printer;
+ QList<QPrinterDescription> lprPrinters;
+ void updateWidget();
+
+private:
+ QPrintDialogPrivate *optionsPane;
+ bool filePrintersAdded;
+#if !defined(QT_NO_CUPS) && !defined(QT_NO_LIBRARY)
+ QCUPSSupport* cups;
+ int cupsPrinterCount;
+ const cups_dest_t* cupsPrinters;
+ const ppd_file_t* cupsPPD;
+#endif
+};
+#endif
+
+#if !defined(QT_NO_CUPS) && !defined(QT_NO_LIBRARY)
+class QOptionTreeItem
+{
+public:
+ enum ItemType { Root, Group, Option, Choice };
+
+ QOptionTreeItem(ItemType t, int i, const void* p, const char* desc, QOptionTreeItem* pi)
+ : type(t),
+ index(i),
+ ptr(p),
+ description(desc),
+ selected(-1),
+ selDescription(0),
+ parentItem(pi) {};
+
+ ~QOptionTreeItem() {
+ while (!childItems.isEmpty())
+ delete childItems.takeFirst();
+ };
+
+ ItemType type;
+ int index;
+ const void* ptr;
+ const char* description;
+ int selected;
+ const char* selDescription;
+ QOptionTreeItem* parentItem;
+ QList<QOptionTreeItem*> childItems;
+};
+
+class QPPDOptionsModel : public QAbstractItemModel
+{
+ friend class QPPDOptionsEditor;
+public:
+ QPPDOptionsModel(QCUPSSupport *cups, QObject *parent = 0);
+ ~QPPDOptionsModel();
+
+ int columnCount(const QModelIndex& parent = QModelIndex()) const;
+ int rowCount(const QModelIndex& parent = QModelIndex()) const;
+ QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const;
+ QModelIndex index(int row, int column, const QModelIndex& parent = QModelIndex()) const;
+ QModelIndex parent(const QModelIndex& index) const;
+ Qt::ItemFlags flags(const QModelIndex& index) const;
+ QVariant headerData ( int section, Qt::Orientation orientation, int role = Qt::DisplayRole ) const;
+
+ QOptionTreeItem* rootItem;
+ QCUPSSupport *cups;
+ const ppd_file_t* ppd;
+ void parseItems();
+ void parseGroups(QOptionTreeItem* parent);
+ void parseOptions(QOptionTreeItem* parent);
+ void parseChoices(QOptionTreeItem* parent);
+};
+
+class QPPDOptionsEditor : public QStyledItemDelegate
+{
+ Q_OBJECT
+public:
+ QPPDOptionsEditor(QObject* parent = 0) : QStyledItemDelegate(parent) {};
+ ~QPPDOptionsEditor() {};
+
+ QWidget* createEditor(QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index) const;
+ void setEditorData(QWidget* editor, const QModelIndex& index) const;
+ void setModelData( QWidget* editor, QAbstractItemModel* model, const QModelIndex& index) const;
+
+private slots:
+ void cbChanged(int index);
+
+};
+
+#endif
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+QPrintPropertiesDialog::QPrintPropertiesDialog(QAbstractPrintDialog *parent)
+ : QDialog(parent)
+#if !defined(QT_NO_CUPS) && !defined(QT_NO_LIBRARY)
+ , m_cups(0), m_cupsOptionsModel(0)
+#endif
+{
+ QVBoxLayout *lay = new QVBoxLayout(this);
+ this->setLayout(lay);
+ QWidget *content = new QWidget(this);
+ widget.setupUi(content);
+ m_buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, Qt::Horizontal, this);
+ lay->addWidget(content);
+ lay->addWidget(m_buttons);
+
+ connect(m_buttons->button(QDialogButtonBox::Ok), SIGNAL(clicked()), this, SLOT(accept()));
+ connect(m_buttons->button(QDialogButtonBox::Cancel), SIGNAL(clicked()), this, SLOT(reject()));
+}
+
+QPrintPropertiesDialog::~QPrintPropertiesDialog()
+{
+#if !defined(QT_NO_CUPS) && !defined(QT_NO_LIBRARY)
+ delete m_cupsOptionsModel;
+#else
+ delete widget.cupsPropertiesPage;
+#endif
+}
+
+void QPrintPropertiesDialog::applyPrinterProperties(QPrinter *p)
+{
+ widget.pageSetup->setPrinter(p);
+}
+
+void QPrintPropertiesDialog::setupPrinter() const
+{
+ widget.pageSetup->setupPrinter();
+
+#if !defined(QT_NO_CUPS) && !defined(QT_NO_LIBRARY)
+ QPPDOptionsModel* model = static_cast<QPPDOptionsModel*>(widget.treeView->model());
+ if (model) {
+ QOptionTreeItem* rootItem = model->rootItem;
+ QList<const ppd_option_t*> options;
+ QList<const char*> markedOptions;
+
+ addItemToOptions(rootItem, options, markedOptions);
+ model->cups->saveOptions(options, markedOptions);
+ }
+#endif
+}
+
+void QPrintPropertiesDialog::selectPrinter()
+{
+#if !defined(QT_NO_CUPS) && !defined(QT_NO_LIBRARY)
+ widget.pageSetup->selectPrinter(m_cups);
+ widget.treeView->setModel(0);
+ if (m_cups && QCUPSSupport::isAvailable()) {
+
+ if (m_cupsOptionsModel == 0) {
+ m_cupsOptionsModel = new QPPDOptionsModel(m_cups);
+
+ widget.treeView->setItemDelegate(new QPPDOptionsEditor(this));
+ } else {
+ // update the model
+ m_cupsOptionsModel->parseItems();
+ }
+
+ if (m_cupsOptionsModel->rowCount() > 0) {
+ widget.treeView->setModel(m_cupsOptionsModel);
+
+ for (int i = 0; i < m_cupsOptionsModel->rowCount(); ++i)
+ widget.treeView->expand(m_cupsOptionsModel->index(i,0));
+
+ widget.tabs->setTabEnabled(1, true); // enable the advanced tab
+ } else {
+ widget.tabs->setTabEnabled(1, false);
+ }
+
+ } else
+#endif
+ {
+ widget.cupsPropertiesPage->setEnabled(false);
+ widget.pageSetup->selectPrinter(0);
+ }
+}
+
+void QPrintPropertiesDialog::selectPdfPsPrinter(const QPrinter *p)
+{
+ widget.treeView->setModel(0);
+ widget.pageSetup->selectPdfPsPrinter(p);
+ widget.tabs->setTabEnabled(1, false); // disable the advanced tab
+}
+
+void QPrintPropertiesDialog::showEvent(QShowEvent* event)
+{
+ widget.treeView->resizeColumnToContents(0);
+ event->accept();
+}
+
+#if !defined(QT_NO_CUPS) && !defined(QT_NO_LIBRARY)
+void QPrintPropertiesDialog::addItemToOptions(QOptionTreeItem *parent, QList<const ppd_option_t*>& options, QList<const char*>& markedOptions) const
+{
+ for (int i = 0; i < parent->childItems.count(); ++i) {
+ QOptionTreeItem *itm = parent->childItems.at(i);
+ if (itm->type == QOptionTreeItem::Option) {
+ const ppd_option_t* opt = reinterpret_cast<const ppd_option_t*>(itm->ptr);
+ options << opt;
+ if (qstrcmp(opt->defchoice, opt->choices[itm->selected].choice) != 0) {
+ markedOptions << opt->keyword << opt->choices[itm->selected].choice;
+ }
+ } else {
+ addItemToOptions(itm, options, markedOptions);
+ }
+ }
+}
+#endif
+
+QPrintDialogPrivate::QPrintDialogPrivate()
+ : top(0), bottom(0), buttons(0), collapseButton(0)
+{
+}
+
+QPrintDialogPrivate::~QPrintDialogPrivate()
+{
+}
+
+void QPrintDialogPrivate::init()
+{
+ Q_Q(QPrintDialog);
+
+ top = new QUnixPrintWidget(0, q);
+ bottom = new QWidget(q);
+ options.setupUi(bottom);
+ options.color->setIconSize(QSize(32, 32));
+ options.color->setIcon(QIcon(QLatin1String(":/trolltech/dialogs/qprintdialog/images/status-color.png")));
+ options.grayscale->setIconSize(QSize(32, 32));
+ options.grayscale->setIcon(QIcon(QLatin1String(":/trolltech/dialogs/qprintdialog/images/status-gray-scale.png")));
+ top->d->setOptionsPane(this);
+
+ buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, Qt::Horizontal, q);
+ collapseButton = new QPushButton(QPrintDialog::tr("&Options >>"), buttons);
+ buttons->addButton(collapseButton, QDialogButtonBox::ResetRole);
+ bottom->setVisible(false);
+
+ QPushButton *printButton = buttons->button(QDialogButtonBox::Ok);
+ printButton->setText(QPrintDialog::tr("&Print"));
+ printButton->setDefault(true);
+
+ QVBoxLayout *lay = new QVBoxLayout(q);
+ q->setLayout(lay);
+ lay->addWidget(top);
+ lay->addWidget(bottom);
+ lay->addWidget(buttons);
+
+ QPrinter* p = q->printer();
+
+ applyPrinterProperties(p);
+
+#ifdef QT_NO_MESSAGEBOX
+ QObject::connect(buttons, SIGNAL(accepted()), q, SLOT(accept()));
+#else
+ QObject::connect(buttons, SIGNAL(accepted()), q, SLOT(_q_checkFields()));
+#endif
+ QObject::connect(buttons, SIGNAL(rejected()), q, SLOT(reject()));
+
+ QObject::connect(options.reverse, SIGNAL(toggled(bool)),
+ q, SLOT(_q_chbPrintLastFirstToggled(bool)));
+
+ QObject::connect(collapseButton, SIGNAL(released()), q, SLOT(_q_collapseOrExpandDialog()));
+}
+
+void QPrintDialogPrivate::applyPrinterProperties(QPrinter *p)
+{
+ if (p->colorMode() == QPrinter::Color)
+ options.color->setChecked(true);
+ else
+ options.grayscale->setChecked(true);
+
+ switch(p->duplex()) {
+ case QPrinter::DuplexNone:
+ options.noDuplex->setChecked(true); break;
+ case QPrinter::DuplexLongSide:
+ case QPrinter::DuplexAuto:
+ options.duplexLong->setChecked(true); break;
+ case QPrinter::DuplexShortSide:
+ options.duplexShort->setChecked(true); break;
+ }
+ options.copies->setValue(qt_printerRealNumCopies(p->paintEngine()));
+ options.collate->setChecked(p->collateCopies());
+ options.reverse->setChecked(p->pageOrder() == QPrinter::LastPageFirst);
+ top->d->applyPrinterProperties(p);
+}
+
+void QPrintDialogPrivate::_q_chbPrintLastFirstToggled(bool checked)
+{
+ Q_Q(QPrintDialog);
+ if (checked)
+ q->printer()->setPageOrder(QPrinter::LastPageFirst);
+ else
+ q->printer()->setPageOrder(QPrinter::FirstPageFirst);
+}
+
+void QPrintDialogPrivate::_q_collapseOrExpandDialog()
+{
+ int collapseHeight = 0;
+ Q_Q(QPrintDialog);
+ QWidget *widgetToHide = bottom;
+ if (widgetToHide->isVisible()) {
+ collapseButton->setText(QPrintDialog::tr("&Options >>"));
+ collapseHeight = widgetToHide->y() + widgetToHide->height() - (top->y() + top->height());
+ }
+ else
+ collapseButton->setText(QPrintDialog::tr("&Options <<"));
+ widgetToHide->setVisible(! widgetToHide->isVisible());
+ if (! widgetToHide->isVisible()) { // make it shrink
+ q->layout()->activate();
+ q->resize( QSize(q->width(), q->height() - collapseHeight) );
+ }
+}
+
+#ifndef QT_NO_MESSAGEBOX
+void QPrintDialogPrivate::_q_checkFields()
+{
+ Q_Q(QPrintDialog);
+ if (top->d->checkFields())
+ q->accept();
+}
+#endif // QT_NO_MESSAGEBOX
+
+void QPrintDialogPrivate::setupPrinter()
+{
+ Q_Q(QPrintDialog);
+ QPrinter* p = q->printer();
+
+ if (options.duplex->isEnabled()) {
+ if (options.noDuplex->isChecked())
+ p->setDuplex(QPrinter::DuplexNone);
+ else if (options.duplexLong->isChecked())
+ p->setDuplex(QPrinter::DuplexLongSide);
+ else
+ p->setDuplex(QPrinter::DuplexShortSide);
+ }
+
+ p->setColorMode( options.color->isChecked() ? QPrinter::Color : QPrinter::GrayScale );
+
+ // print range
+ if (options.printAll->isChecked()) {
+ p->setPrintRange(QPrinter::AllPages);
+ p->setFromTo(0,0);
+ } else if (options.printSelection->isChecked()) {
+ p->setPrintRange(QPrinter::Selection);
+ p->setFromTo(0,0);
+ } else if (options.printRange->isChecked()) {
+ p->setPrintRange(QPrinter::PageRange);
+ p->setFromTo(options.from->value(), qMax(options.from->value(), options.to->value()));
+ }
+
+ // copies
+ p->setNumCopies(options.copies->value());
+ p->setCollateCopies(options.collate->isChecked());
+
+ top->d->setupPrinter();
+}
+
+void QPrintDialogPrivate::updateWidgets()
+{
+ Q_Q(QPrintDialog);
+ options.gbPrintRange->setVisible(q->isOptionEnabled(QPrintDialog::PrintPageRange) ||
+ q->isOptionEnabled(QPrintDialog::PrintSelection));
+
+ options.printRange->setEnabled(q->isOptionEnabled(QPrintDialog::PrintPageRange));
+ options.printSelection->setVisible(q->isOptionEnabled(QPrintDialog::PrintSelection));
+ options.collate->setVisible(q->isOptionEnabled(QPrintDialog::PrintCollateCopies));
+
+ switch (q->printRange()) {
+ case QPrintDialog::AllPages:
+ options.printAll->setChecked(true);
+ break;
+ case QPrintDialog::Selection:
+ options.printSelection->setChecked(true);
+ break;
+ case QPrintDialog::PageRange:
+ options.printRange->setChecked(true);
+ break;
+ default:
+ break;
+ }
+ const int minPage = qMax(1, qMin(q->minPage() , q->maxPage()));
+ const int maxPage = qMax(1, q->maxPage() == INT_MAX ? 9999 : q->maxPage());
+
+ options.from->setMinimum(minPage);
+ options.to->setMinimum(minPage);
+ options.from->setMaximum(maxPage);
+ options.to->setMaximum(maxPage);
+
+ options.from->setValue(q->fromPage());
+ options.to->setValue(q->toPage());
+ top->d->updateWidget();
+}
+
+void QPrintDialogPrivate::setTabs(const QList<QWidget*> &tabWidgets)
+{
+ while(options.tabs->count() > 2)
+ delete options.tabs->widget(2);
+
+ QList<QWidget*>::ConstIterator iter = tabWidgets.begin();
+ while(iter != tabWidgets.constEnd()) {
+ QWidget *tab = *iter;
+ options.tabs->addTab(tab, tab->windowTitle());
+ ++iter;
+ }
+}
+
+#if !defined(QT_NO_CUPS) && !defined(QT_NO_LIBRARY)
+void QPrintDialogPrivate::selectPrinter(QCUPSSupport *cups)
+{
+ options.duplex->setEnabled(cups && cups->ppdOption("Duplex"));
+}
+#endif
+
+////////////////////////////////////////////////////////////////////////////////
+
+QPrintDialog::QPrintDialog(QPrinter *printer, QWidget *parent)
+ : QAbstractPrintDialog(*(new QPrintDialogPrivate), printer, parent)
+{
+ Q_D(QPrintDialog);
+ d->init();
+}
+
+/*!
+ Constructs a print dialog with the given \a parent.
+*/
+QPrintDialog::QPrintDialog(QWidget *parent)
+ : QAbstractPrintDialog(*(new QPrintDialogPrivate), 0, parent)
+{
+ Q_D(QPrintDialog);
+ d->init();
+}
+
+QPrintDialog::~QPrintDialog()
+{
+}
+
+void QPrintDialog::setVisible(bool visible)
+{
+ Q_D(QPrintDialog);
+
+ if (visible)
+ d->updateWidgets();
+
+ QAbstractPrintDialog::setVisible(visible);
+}
+
+int QPrintDialog::exec()
+{
+ return QDialog::exec();
+}
+
+void QPrintDialog::accept()
+{
+ Q_D(QPrintDialog);
+ d->setupPrinter();
+ QDialog::accept();
+}
+
+#ifdef QT3_SUPPORT
+QPrinter *QPrintDialog::printer() const
+{
+ Q_D(const QPrintDialog);
+ return d->printer;
+}
+
+void QPrintDialog::setPrinter(QPrinter *printer, bool pickupSettings)
+{
+ if (!printer)
+ return;
+
+ Q_D(QPrintDialog);
+ d->printer = printer;
+
+ if (pickupSettings)
+ d->applyPrinterProperties(printer);
+}
+
+void QPrintDialog::addButton(QPushButton *button)
+{
+ Q_D(QPrintDialog);
+ d->buttons->addButton(button, QDialogButtonBox::HelpRole);
+}
+#endif // QT3_SUPPORT
+
+#if defined (Q_OS_UNIX)
+
+/*! \internal
+*/
+QUnixPrintWidgetPrivate::QUnixPrintWidgetPrivate(QUnixPrintWidget *p)
+ : parent(p), propertiesDialog(0), printer(0), optionsPane(0), filePrintersAdded(false)
+#if !defined(QT_NO_CUPS) && !defined(QT_NO_LIBRARY)
+ , cups(0), cupsPrinterCount(0), cupsPrinters(0), cupsPPD(0)
+#endif
+{
+ q = 0;
+ if (parent)
+ q = qobject_cast<QAbstractPrintDialog*> (parent->parent());
+
+ widget.setupUi(parent);
+
+ int currentPrinterIndex = 0;
+#if !defined(QT_NO_CUPS) && !defined(QT_NO_LIBRARY)
+ cups = new QCUPSSupport;
+ if (QCUPSSupport::isAvailable()) {
+ cupsPPD = cups->currentPPD();
+ cupsPrinterCount = cups->availablePrintersCount();
+ cupsPrinters = cups->availablePrinters();
+
+ for (int i = 0; i < cupsPrinterCount; ++i) {
+ QString printerName(QString::fromLocal8Bit(cupsPrinters[i].name));
+ if (cupsPrinters[i].instance)
+ printerName += QLatin1String("/") + QString::fromLocal8Bit(cupsPrinters[i].instance);
+
+ widget.printers->addItem(printerName);
+ if (cupsPrinters[i].is_default)
+ widget.printers->setCurrentIndex(i);
+ }
+ // the model depends on valid ppd. so before enabling the
+ // properties button we make sure the ppd is in fact valid.
+ if (cupsPrinterCount && cups->currentPPD()) {
+ widget.properties->setEnabled(true);
+ }
+ currentPrinterIndex = cups->currentPrinterIndex();
+ } else {
+#endif
+ currentPrinterIndex = qt_getLprPrinters(lprPrinters);
+ // populating printer combo
+ QList<QPrinterDescription>::const_iterator i = lprPrinters.constBegin();
+ for(; i != lprPrinters.constEnd(); ++i)
+ widget.printers->addItem((*i).name);
+#if !defined(QT_NO_CUPS) && !defined(QT_NO_LIBRARY)
+ }
+#endif
+
+#ifndef QT_NO_FILESYSTEMMODEL
+ QFileSystemModel *fsm = new QFileSystemModel(widget.filename);
+ fsm->setRootPath(QDir::homePath());
+#if !defined(QT_NO_COMPLETER) && !defined(QT_NO_FILEDIALOG)
+ widget.filename->setCompleter(new QFSCompletor(fsm, widget.filename));
+#endif
+#endif
+ _q_printerChanged(currentPrinterIndex);
+
+ QObject::connect(widget.printers, SIGNAL(currentIndexChanged(int)),
+ parent, SLOT(_q_printerChanged(int)));
+ QObject::connect(widget.fileBrowser, SIGNAL(clicked()), parent, SLOT(_q_btnBrowseClicked()));
+ QObject::connect(widget.properties, SIGNAL(clicked()), parent, SLOT(_q_btnPropertiesClicked()));
+
+ // disable features that QPrinter does not yet support.
+ widget.preview->setVisible(false);
+}
+
+void QUnixPrintWidgetPrivate::updateWidget()
+{
+ const bool printToFile = q == 0 || q->isOptionEnabled(QPrintDialog::PrintToFile);
+ if (printToFile && !filePrintersAdded) {
+ if (widget.printers->count())
+ widget.printers->insertSeparator(widget.printers->count());
+ widget.printers->addItem(QPrintDialog::tr("Print to File (PDF)"));
+ widget.printers->addItem(QPrintDialog::tr("Print to File (Postscript)"));
+ filePrintersAdded = true;
+ }
+ if (!printToFile && filePrintersAdded) {
+ widget.printers->removeItem(widget.printers->count()-1);
+ widget.printers->removeItem(widget.printers->count()-1);
+ if (widget.printers->count())
+ widget.printers->removeItem(widget.printers->count()-1); // remove separator
+ filePrintersAdded = false;
+ }
+ if (printer && filePrintersAdded && printer->printerName().isEmpty()) {
+ if (printer->outputFormat() == QPrinter::PdfFormat)
+ widget.printers->setCurrentIndex(widget.printers->count() - 2);
+ else if (printer->outputFormat() == QPrinter::PostScriptFormat)
+ widget.printers->setCurrentIndex(widget.printers->count() - 1);
+ widget.filename->setEnabled(true);
+ widget.lOutput->setEnabled(true);
+ }
+
+ widget.filename->setVisible(printToFile);
+ widget.lOutput->setVisible(printToFile);
+ widget.fileBrowser->setVisible(printToFile);
+
+ widget.properties->setVisible(q->isOptionEnabled(QAbstractPrintDialog::PrintShowPageSize));
+}
+
+QUnixPrintWidgetPrivate::~QUnixPrintWidgetPrivate()
+{
+#if !defined(QT_NO_CUPS) && !defined(QT_NO_LIBRARY)
+ delete cups;
+#endif
+}
+
+void QUnixPrintWidgetPrivate::_q_printerChanged(int index)
+{
+ if (index < 0)
+ return;
+ const int printerCount = widget.printers->count();
+ widget.filename->setEnabled(false);
+ widget.lOutput->setEnabled(false);
+
+ if (filePrintersAdded) {
+ Q_ASSERT(index != printerCount - 3); // separator
+ if (index > printerCount - 3) { // PDF or postscript
+ bool pdfPrinter = (index == printerCount - 2);
+ widget.location->setText(QPrintDialog::tr("Local file"));
+ widget.type->setText(QPrintDialog::tr("Write %1 file").arg(pdfPrinter ? QString::fromLatin1("PDF")
+ : QString::fromLatin1("PostScript")));
+ widget.properties->setEnabled(true);
+ widget.filename->setEnabled(true);
+ QString filename = widget.filename->text();
+ QString suffix = QFileInfo(filename).suffix();
+ if (pdfPrinter && suffix == QLatin1String("ps"))
+ filename = filename.replace(QLatin1String(".ps"), QLatin1String(".pdf"));
+ if (!pdfPrinter && suffix == QLatin1String("pdf"))
+ filename = filename.replace(QLatin1String(".pdf"), QLatin1String(".ps"));
+ widget.filename->setText(filename);
+ widget.lOutput->setEnabled(true);
+ if (propertiesDialog)
+ propertiesDialog->selectPdfPsPrinter(printer);
+#if !defined(QT_NO_CUPS) && !defined(QT_NO_LIBRARY)
+ if (optionsPane)
+ optionsPane->selectPrinter(0);
+#endif
+ return;
+ }
+ }
+
+ widget.location->setText(QString());
+#if !defined(QT_NO_CUPS) && !defined(QT_NO_LIBRARY)
+ if (QCUPSSupport::isAvailable()) {
+ cups->setCurrentPrinter(index);
+
+ const cups_option_t *opt = cups->printerOption(QString::fromLatin1("printer-location"));
+ QString location;
+ if (opt)
+ location = QString::fromLocal8Bit(opt->value);
+ widget.location->setText(location);
+
+ cupsPPD = cups->currentPPD();
+ // set printer type line
+ QString type;
+ if (cupsPPD)
+ type = QString::fromLocal8Bit(cupsPPD->manufacturer) + QLatin1String(" - ") + QString::fromLocal8Bit(cupsPPD->modelname);
+ widget.type->setText(type);
+ if (propertiesDialog)
+ propertiesDialog->selectPrinter();
+ if (optionsPane)
+ optionsPane->selectPrinter(cups);
+ } else {
+ if (optionsPane)
+ optionsPane->selectPrinter(0);
+#endif
+ if (lprPrinters.count() > 0) {
+ QString type = lprPrinters.at(index).name + QLatin1String("@") + lprPrinters.at(index).host;
+ if (!lprPrinters.at(index).comment.isEmpty())
+ type += QLatin1String(", ") + lprPrinters.at(index).comment;
+ widget.type->setText(type);
+ if (propertiesDialog)
+ propertiesDialog->selectPrinter();
+ }
+#if !defined(QT_NO_CUPS) && !defined(QT_NO_LIBRARY)
+ }
+#endif
+}
+
+void QUnixPrintWidgetPrivate::setOptionsPane(QPrintDialogPrivate *pane)
+{
+ optionsPane = pane;
+ if (optionsPane)
+ _q_printerChanged(widget.printers->currentIndex());
+}
+
+void QUnixPrintWidgetPrivate::_q_btnBrowseClicked()
+{
+ const int prevPrinter = widget.printers->currentIndex();
+ widget.printers->setCurrentIndex(widget.printers->count() - 2); // the pdf one
+
+ QString filename = widget.filename->text();
+#ifndef QT_NO_FILEDIALOG
+ filename = QFileDialog::getSaveFileName(parent, QPrintDialog::tr("Print To File ..."), filename,
+ QString(), 0, QFileDialog::DontConfirmOverwrite);
+#else
+ filename.clear();
+#endif
+ if (!filename.isEmpty()) {
+ widget.filename->setText(filename);
+ if (filename.endsWith(QString::fromLatin1(".ps"), Qt::CaseInsensitive))
+ widget.printers->setCurrentIndex(widget.printers->count() - 1); // the postscript one
+ }
+ else
+ widget.printers->setCurrentIndex(prevPrinter);
+}
+
+void QUnixPrintWidgetPrivate::applyPrinterProperties(QPrinter *p)
+{
+ if (p == 0)
+ return;
+ printer = p;
+ if (p->outputFileName().isEmpty()) {
+ QString home = QString::fromLocal8Bit(qgetenv("HOME").constData());
+ QString cur = QDir::currentPath();
+ if (home.at(home.length()-1) != QLatin1Char('/'))
+ home += QLatin1Char('/');
+ if (cur.at(cur.length()-1) != QLatin1Char('/'))
+ cur += QLatin1Char('/');
+ if (cur.left(home.length()) != home)
+ cur = home;
+#ifdef Q_WS_X11
+ if (p->docName().isEmpty()) {
+ if (p->outputFormat() == QPrinter::PostScriptFormat)
+ cur += QLatin1String("print.ps");
+ else
+ cur += QLatin1String("print.pdf");
+ } else {
+ QRegExp re(QString::fromLatin1("(.*)\\.\\S+"));
+ if (re.exactMatch(p->docName()))
+ cur += re.cap(1);
+ else
+ cur += p->docName();
+ if (p->outputFormat() == QPrinter::PostScriptFormat)
+ cur += QLatin1String(".ps");
+ else
+ cur += QLatin1String(".pdf");
+ }
+#endif
+ widget.filename->setText(cur);
+ }
+ else
+ widget.filename->setText( p->outputFileName() );
+ QString printer = p->printerName();
+ if (!printer.isEmpty()) {
+ for (int i = 0; i < widget.printers->count(); ++i) {
+ if (widget.printers->itemText(i) == printer) {
+ widget.printers->setCurrentIndex(i);
+ break;
+ }
+ }
+ }
+ // PDF and PS printers are not added to the dialog yet, we'll handle those cases in QUnixPrintWidgetPrivate::updateWidget
+
+ if (propertiesDialog)
+ propertiesDialog->applyPrinterProperties(p);
+}
+
+#ifndef QT_NO_MESSAGEBOX
+bool QUnixPrintWidgetPrivate::checkFields()
+{
+ if (widget.filename->isEnabled()) {
+ QString file = widget.filename->text();
+ QFile f(file);
+ QFileInfo fi(f);
+ bool exists = fi.exists();
+ bool opened = false;
+ if (exists && fi.isDir()) {
+ QMessageBox::warning(q, q->windowTitle(),
+ QPrintDialog::tr("%1 is a directory.\nPlease choose a different file name.").arg(file));
+ return false;
+ } else if ((exists && !fi.isWritable()) || !(opened = f.open(QFile::Append))) {
+ QMessageBox::warning(q, q->windowTitle(),
+ QPrintDialog::tr("File %1 is not writable.\nPlease choose a different file name.").arg(file));
+ return false;
+ } else if (exists) {
+ int ret = QMessageBox::question(q, q->windowTitle(),
+ QPrintDialog::tr("%1 already exists.\nDo you want to overwrite it?").arg(file),
+ QMessageBox::Yes|QMessageBox::No, QMessageBox::No);
+ if (ret == QMessageBox::No)
+ return false;
+ }
+ if (opened) {
+ f.close();
+ if (!exists)
+ f.remove();
+ }
+ }
+
+ // Every test passed. Accept the dialog.
+ return true;
+}
+#endif // QT_NO_MESSAGEBOX
+
+void QUnixPrintWidgetPrivate::_q_btnPropertiesClicked()
+{
+ if (propertiesDialog == 0) {
+ propertiesDialog = new QPrintPropertiesDialog(q);
+ propertiesDialog->setResult(QDialog::Rejected);
+ }
+
+ if (propertiesDialog->result() == QDialog::Rejected) {
+#if !defined(QT_NO_CUPS) && !defined(QT_NO_LIBRARY)
+ propertiesDialog->setCups(cups);
+#endif
+ propertiesDialog->applyPrinterProperties(q->printer());
+
+ if (q->isOptionEnabled(QPrintDialog::PrintToFile)
+ && (widget.printers->currentIndex() > widget.printers->count() - 3)) // PDF or postscript
+ propertiesDialog->selectPdfPsPrinter(q->printer());
+ else
+ propertiesDialog->selectPrinter();
+ }
+ propertiesDialog->exec();
+}
+
+void QUnixPrintWidgetPrivate::setupPrinter()
+{
+ const int printerCount = widget.printers->count();
+ const int index = widget.printers->currentIndex();
+
+ if (filePrintersAdded && index > printerCount - 3) { // PDF or postscript
+ printer->setPrinterName(QString());
+ Q_ASSERT(index != printerCount - 3); // separator
+ if (index == printerCount - 2)
+ printer->setOutputFormat(QPrinter::PdfFormat);
+ else
+ printer->setOutputFormat(QPrinter::PostScriptFormat);
+ QString path = widget.filename->text();
+ if (QDir::isRelativePath(path))
+ path = QDir::homePath() + QDir::separator() + path;
+ printer->setOutputFileName(path);
+ }
+ else {
+ printer->setPrinterName(widget.printers->currentText());
+ printer->setOutputFileName(QString());
+ }
+
+ if (propertiesDialog && propertiesDialog->result() == QDialog::Accepted)
+ propertiesDialog->setupPrinter();
+}
+
+
+/*! \internal
+*/
+QUnixPrintWidget::QUnixPrintWidget(QPrinter *printer, QWidget *parent)
+ : QWidget(parent), d(new QUnixPrintWidgetPrivate(this))
+{
+ d->applyPrinterProperties(printer);
+}
+
+/*! \internal
+*/
+QUnixPrintWidget::~QUnixPrintWidget()
+{
+ delete d;
+}
+
+/*! \internal
+
+ Updates the printer with the states held in the QUnixPrintWidget.
+*/
+void QUnixPrintWidget::updatePrinter()
+{
+ d->setupPrinter();
+}
+
+#endif
+
+////////////////////////////////////////////////////////////////////////////////
+#if !defined(QT_NO_CUPS) && !defined(QT_NO_LIBRARY)
+
+QPPDOptionsModel::QPPDOptionsModel(QCUPSSupport *c, QObject *parent)
+ : QAbstractItemModel(parent), rootItem(0), cups(c), ppd(c->currentPPD())
+{
+ parseItems();
+}
+
+QPPDOptionsModel::~QPPDOptionsModel()
+{
+}
+
+int QPPDOptionsModel::columnCount(const QModelIndex&) const
+{
+ return 2;
+}
+
+int QPPDOptionsModel::rowCount(const QModelIndex& parent) const
+{
+ QOptionTreeItem* itm;
+ if (!parent.isValid())
+ itm = rootItem;
+ else
+ itm = reinterpret_cast<QOptionTreeItem*>(parent.internalPointer());
+
+ if (itm->type == QOptionTreeItem::Option)
+ return 0;
+
+ return itm->childItems.count();
+}
+
+QVariant QPPDOptionsModel::data(const QModelIndex& index, int role) const
+{
+ switch(role) {
+ case Qt::FontRole: {
+ QOptionTreeItem* itm = reinterpret_cast<QOptionTreeItem*>(index.internalPointer());
+ if (itm && itm->type == QOptionTreeItem::Group){
+ QFont font = QApplication::font();
+ font.setBold(true);
+ return QVariant(font);
+ }
+ return QVariant();
+ }
+ break;
+
+ case Qt::DisplayRole: {
+ QOptionTreeItem* itm;
+ if (!index.isValid())
+ itm = rootItem;
+ else
+ itm = reinterpret_cast<QOptionTreeItem*>(index.internalPointer());
+
+ if (index.column() == 0)
+ return cups->unicodeString(itm->description);
+ else if (itm->type == QOptionTreeItem::Option && itm->selected > -1)
+ return cups->unicodeString(itm->selDescription);
+ else
+ return QVariant();
+ }
+ break;
+
+ default:
+ return QVariant();
+ }
+ if (role != Qt::DisplayRole)
+ return QVariant();
+}
+
+QModelIndex QPPDOptionsModel::index(int row, int column, const QModelIndex& parent) const
+{
+ QOptionTreeItem* itm;
+ if (!parent.isValid())
+ itm = rootItem;
+ else
+ itm = reinterpret_cast<QOptionTreeItem*>(parent.internalPointer());
+
+ return createIndex(row, column, itm->childItems.at(row));
+}
+
+
+QModelIndex QPPDOptionsModel::parent(const QModelIndex& index) const
+{
+ if (!index.isValid())
+ return QModelIndex();
+
+ QOptionTreeItem* itm = reinterpret_cast<QOptionTreeItem*>(index.internalPointer());
+
+ if (itm->parentItem && itm->parentItem != rootItem)
+ return createIndex(itm->parentItem->index, 0, itm->parentItem);
+ else
+ return QModelIndex();
+}
+
+Qt::ItemFlags QPPDOptionsModel::flags(const QModelIndex& index) const
+{
+ if (!index.isValid() || reinterpret_cast<QOptionTreeItem*>(index.internalPointer())->type == QOptionTreeItem::Group)
+ return Qt::ItemIsEnabled;
+
+ if (index.column() == 1)
+ return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable;
+
+ return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
+}
+
+void QPPDOptionsModel::parseItems()
+{
+ emit layoutAboutToBeChanged();
+ ppd = cups->currentPPD();
+ delete rootItem;
+ rootItem = new QOptionTreeItem(QOptionTreeItem::Root, 0, ppd, "Root Item", 0);
+ parseGroups(rootItem);
+ emit layoutChanged();
+}
+
+void QPPDOptionsModel::parseGroups(QOptionTreeItem* parent)
+{
+ if (parent->type == QOptionTreeItem::Root) {
+
+ const ppd_file_t* ppdFile = reinterpret_cast<const ppd_file_t*>(parent->ptr);
+
+ if (ppdFile) {
+ for (int i = 0; i < ppdFile->num_groups; ++i) {
+ QOptionTreeItem* group = new QOptionTreeItem(QOptionTreeItem::Group, i, &ppdFile->groups[i], ppdFile->groups[i].text, parent);
+ parent->childItems.append(group);
+ parseGroups(group); // parse possible subgroups
+ parseOptions(group); // parse options
+ }
+ }
+ } else if (parent->type == QOptionTreeItem::Group) {
+
+ const ppd_group_t* group = reinterpret_cast<const ppd_group_t*>(parent->ptr);
+
+ if (group) {
+ for (int i = 0; i < group->num_subgroups; ++i) {
+ QOptionTreeItem* subgroup = new QOptionTreeItem(QOptionTreeItem::Group, i, &group->subgroups[i], group->subgroups[i].text, parent);
+ parent->childItems.append(subgroup);
+ parseGroups(subgroup); // parse possible subgroups
+ parseOptions(subgroup); // parse options
+ }
+ }
+ }
+}
+
+void QPPDOptionsModel::parseOptions(QOptionTreeItem* parent)
+{
+ const ppd_group_t* group = reinterpret_cast<const ppd_group_t*>(parent->ptr);
+ for (int i = 0; i < group->num_options; ++i) {
+ QOptionTreeItem* opt = new QOptionTreeItem(QOptionTreeItem::Option, i, &group->options[i], group->options[i].text, parent);
+ parent->childItems.append(opt);
+ parseChoices(opt);
+ }
+}
+
+void QPPDOptionsModel::parseChoices(QOptionTreeItem* parent)
+{
+ const ppd_option_t* option = reinterpret_cast<const ppd_option_t*>(parent->ptr);
+ bool marked = false;
+ for (int i = 0; i < option->num_choices; ++i) {
+ QOptionTreeItem* choice = new QOptionTreeItem(QOptionTreeItem::Choice, i, &option->choices[i], option->choices[i].text, parent);
+ if (static_cast<int>(option->choices[i].marked) == 1) {
+ parent->selected = i;
+ parent->selDescription = option->choices[i].text;
+ marked = true;
+ } else if (!marked && qstrcmp(option->choices[i].choice, option->defchoice) == 0) {
+ parent->selected = i;
+ parent->selDescription = option->choices[i].text;
+ }
+ parent->childItems.append(choice);
+ }
+}
+
+QVariant QPPDOptionsModel::headerData(int section, Qt::Orientation, int role) const
+{
+ if (role != Qt::DisplayRole)
+ return QVariant();
+
+ switch(section){
+ case 0:
+ return QVariant(QApplication::translate("QPPDOptionsModel","Name"));
+ case 1:
+ return QVariant(QApplication::translate("QPPDOptionsModel","Value"));
+ default:
+ return QVariant();
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+QWidget* QPPDOptionsEditor::createEditor(QWidget* parent, const QStyleOptionViewItem&, const QModelIndex& index) const
+{
+ if (index.column() == 1 && reinterpret_cast<QOptionTreeItem*>(index.internalPointer())->type == QOptionTreeItem::Option)
+ return new QComboBox(parent);
+ else
+ return 0;
+}
+
+void QPPDOptionsEditor::setEditorData(QWidget* editor, const QModelIndex& index) const
+{
+ if (index.column() != 1)
+ return;
+
+ QComboBox* cb = static_cast<QComboBox*>(editor);
+ QOptionTreeItem* itm = reinterpret_cast<QOptionTreeItem*>(index.internalPointer());
+
+ if (itm->selected == -1)
+ cb->addItem(QString());
+
+ for (int i = 0; i < itm->childItems.count(); ++i)
+ cb->addItem(QString::fromLocal8Bit(itm->childItems.at(i)->description));
+
+ if (itm->selected > -1)
+ cb->setCurrentIndex(itm->selected);
+
+ connect(cb, SIGNAL(currentIndexChanged(int)), this, SLOT(cbChanged(int)));
+}
+
+void QPPDOptionsEditor::setModelData(QWidget* editor, QAbstractItemModel* model, const QModelIndex& index) const
+{
+ QComboBox* cb = static_cast<QComboBox*>(editor);
+ QOptionTreeItem* itm = reinterpret_cast<QOptionTreeItem*>(index.internalPointer());
+
+ if (itm->selected == cb->currentIndex())
+ return;
+
+ const ppd_option_t* opt = reinterpret_cast<const ppd_option_t*>(itm->ptr);
+ QPPDOptionsModel* m = static_cast<QPPDOptionsModel*>(model);
+
+ if (m->cups->markOption(opt->keyword, opt->choices[cb->currentIndex()].choice) == 0) {
+ itm->selected = cb->currentIndex();
+ itm->selDescription = reinterpret_cast<const ppd_option_t*>(itm->ptr)->choices[itm->selected].text;
+ }
+}
+
+void QPPDOptionsEditor::cbChanged(int)
+{
+/*
+ emit commitData(static_cast<QWidget*>(sender()));
+*/
+}
+
+#endif
+
+QT_END_NAMESPACE
+
+#include "moc_qprintdialog.cpp"
+#include "qprintdialog_unix.moc"
+#include "qrc_qprintdialog.cpp"
+
+#endif // QT_NO_PRINTDIALOG
+
diff --git a/src/gui/dialogs/qprintdialog_win.cpp b/src/gui/dialogs/qprintdialog_win.cpp
new file mode 100644
index 0000000000..936d1ed9e8
--- /dev/null
+++ b/src/gui/dialogs/qprintdialog_win.cpp
@@ -0,0 +1,318 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QT_NO_PRINTDIALOG
+
+#include "qprintdialog.h"
+
+#include <qwidget.h>
+#include <qapplication.h>
+#include <qmessagebox.h>
+#include <private/qapplication_p.h>
+
+#include <private/qabstractprintdialog_p.h>
+#include <private/qprintengine_win_p.h>
+#include <private/qprinter_p.h>
+
+QT_BEGIN_NAMESPACE
+
+extern void qt_win_eatMouseMove();
+
+class QPrintDialogPrivate : public QAbstractPrintDialogPrivate
+{
+ Q_DECLARE_PUBLIC(QPrintDialog)
+public:
+ QPrintDialogPrivate()
+ : ep(0)
+ {
+ }
+
+ inline void _q_printToFileChanged(int) {}
+ inline void _q_rbPrintRangeToggled(bool) {}
+ inline void _q_printerChanged(int) {}
+ inline void _q_chbPrintLastFirstToggled(bool) {}
+ inline void _q_paperSizeChanged(int) {}
+ inline void _q_btnBrowseClicked() {}
+ inline void _q_btnPropertiesClicked() {}
+ int openWindowsPrintDialogModally();
+
+ QWin32PrintEnginePrivate *ep;
+};
+
+#ifndef Q_OS_WINCE
+// If you change this function, make sure you also change the unicode equivalent
+template <class PrintDialog, class DeviceMode>
+static PrintDialog *qt_win_make_PRINTDLG(QWidget *parent,
+ QPrintDialog *pdlg,
+ QPrintDialogPrivate *d, HGLOBAL *tempDevNames)
+{
+ PrintDialog *pd = new PrintDialog;
+ memset(pd, 0, sizeof(PrintDialog));
+ pd->lStructSize = sizeof(PrintDialog);
+
+ void *devMode = sizeof(DeviceMode) == sizeof(DEVMODEA)
+ ? (void *) d->ep->devModeA()
+ : (void *) d->ep->devModeW();
+
+ if (devMode) {
+ int size = sizeof(DeviceMode) + ((DeviceMode *) devMode)->dmDriverExtra;
+ pd->hDevMode = GlobalAlloc(GHND, size);
+ {
+ void *dest = GlobalLock(pd->hDevMode);
+ memcpy(dest, d->ep->devMode, size);
+ GlobalUnlock(pd->hDevMode);
+ }
+ } else {
+ pd->hDevMode = NULL;
+ }
+ pd->hDevNames = tempDevNames;
+
+ pd->Flags = PD_RETURNDC;
+ pd->Flags |= PD_USEDEVMODECOPIESANDCOLLATE;
+
+ if (!pdlg->isOptionEnabled(QPrintDialog::PrintSelection))
+ pd->Flags |= PD_NOSELECTION;
+ if (pdlg->isOptionEnabled(QPrintDialog::PrintPageRange)) {
+ pd->nMinPage = pdlg->minPage();
+ pd->nMaxPage = pdlg->maxPage();
+ }
+
+ if(!pdlg->isOptionEnabled(QPrintDialog::PrintToFile))
+ pd->Flags |= PD_DISABLEPRINTTOFILE;
+
+ if (pdlg->printRange() == QPrintDialog::Selection)
+ pd->Flags |= PD_SELECTION;
+ else if (pdlg->printRange() == QPrintDialog::PageRange)
+ pd->Flags |= PD_PAGENUMS;
+ else
+ pd->Flags |= PD_ALLPAGES;
+
+ // As stated by MSDN, to enable collate option when minpage==maxpage==0
+ // set the PD_NOPAGENUMS flag
+ if (pd->nMinPage==0 && pd->nMaxPage==0)
+ pd->Flags |= PD_NOPAGENUMS;
+
+ if (d->ep->printToFile)
+ pd->Flags |= PD_PRINTTOFILE;
+ Q_ASSERT(!parent ||parent->testAttribute(Qt::WA_WState_Created));
+ pd->hwndOwner = parent ? parent->winId() : 0;
+ pd->nFromPage = qMax(pdlg->fromPage(), pdlg->minPage());
+ pd->nToPage = (pdlg->toPage() > 0) ? qMin(pdlg->toPage(), pdlg->maxPage()) : 1;
+ pd->nCopies = d->ep->num_copies;
+
+ return pd;
+}
+#endif // Q_OS_WINCE
+
+// If you change this function, make sure you also change the ansi equivalent
+template <typename T>
+static void qt_win_clean_up_PRINTDLG(T **pd)
+{
+ delete *pd;
+ *pd = 0;
+}
+
+
+// If you change this function, make sure you also change the ansi equivalent
+template <typename T>
+static void qt_win_read_back_PRINTDLG(T *pd, QPrintDialog *pdlg, QPrintDialogPrivate *d)
+{
+ if (pd->Flags & PD_SELECTION) {
+ pdlg->setPrintRange(QPrintDialog::Selection);
+ pdlg->setFromTo(0, 0);
+ } else if (pd->Flags & PD_PAGENUMS) {
+ pdlg->setPrintRange(QPrintDialog::PageRange);
+ pdlg->setFromTo(pd->nFromPage, pd->nToPage);
+ } else {
+ pdlg->setPrintRange(QPrintDialog::AllPages);
+ pdlg->setFromTo(0, 0);
+ }
+
+ d->ep->printToFile = (pd->Flags & PD_PRINTTOFILE) != 0;
+
+ d->ep->readDevnames(pd->hDevNames);
+ d->ep->readDevmode(pd->hDevMode);
+ d->ep->updateCustomPaperSize();
+
+ if (d->ep->printToFile && d->ep->fileName.isEmpty())
+ d->ep->fileName = d->ep->port;
+ else if (!d->ep->printToFile && d->ep->fileName == QLatin1String("FILE:"))
+ d->ep->fileName.clear();
+}
+
+static bool warnIfNotNative(QPrinter *printer)
+{
+ if (printer->outputFormat() != QPrinter::NativeFormat) {
+ qWarning("QPrintDialog: Cannot be used on non-native printers");
+ return false;
+ }
+ return true;
+}
+
+QPrintDialog::QPrintDialog(QPrinter *printer, QWidget *parent)
+ : QAbstractPrintDialog( *(new QPrintDialogPrivate), printer, parent)
+{
+ Q_D(QPrintDialog);
+ if (!warnIfNotNative(d->printer))
+ return;
+ d->ep = static_cast<QWin32PrintEngine *>(d->printer->paintEngine())->d_func();
+}
+
+QPrintDialog::QPrintDialog(QWidget *parent)
+ : QAbstractPrintDialog( *(new QPrintDialogPrivate), 0, parent)
+{
+ Q_D(QPrintDialog);
+ if (!warnIfNotNative(d->printer))
+ return;
+ d->ep = static_cast<QWin32PrintEngine *>(d->printer->paintEngine())->d_func();
+}
+
+QPrintDialog::~QPrintDialog()
+{
+}
+
+int QPrintDialog::exec()
+{
+ if (!warnIfNotNative(printer()))
+ return 0;
+
+ Q_D(QPrintDialog);
+ return d->openWindowsPrintDialogModally();
+}
+
+int QPrintDialogPrivate::openWindowsPrintDialogModally()
+{
+ Q_Q(QPrintDialog);
+ QWidget *parent = q->parentWidget();
+ if (parent)
+ parent = parent->window();
+ else
+ parent = qApp->activeWindow();
+
+ QWidget modal_widget;
+ modal_widget.setAttribute(Qt::WA_NoChildEventsForParent, true);
+ modal_widget.setParent(parent, Qt::Window);
+ QApplicationPrivate::enterModal(&modal_widget);
+
+ HGLOBAL *tempDevNames = ep->createDevNames();
+
+ bool result;
+ bool done;
+ void *pd = QT_WA_INLINE(
+ (void*)(qt_win_make_PRINTDLG<PRINTDLGW, DEVMODEW>(parent, q, this, tempDevNames)),
+ (void*)(qt_win_make_PRINTDLG<PRINTDLGA, DEVMODEA>(parent, q, this, tempDevNames))
+ );
+
+ do {
+ done = true;
+ QT_WA({
+ PRINTDLGW *pdw = reinterpret_cast<PRINTDLGW *>(pd);
+ result = PrintDlgW(pdw);
+ if ((pdw->Flags & PD_PAGENUMS) && (pdw->nFromPage > pdw->nToPage))
+ done = false;
+ if (result && pdw->hDC == 0)
+ result = false;
+ else if (!result)
+ done = true;
+ }, {
+ PRINTDLGA *pda = reinterpret_cast<PRINTDLGA *>(pd);
+ result = PrintDlgA(pda);
+ if ((pda->Flags & PD_PAGENUMS) && (pda->nFromPage > pda->nToPage))
+ done = false;
+ if (result && pda->hDC == 0)
+ result = false;
+ else if (!result)
+ done = true;
+ });
+ if (!done) {
+ QMessageBox::warning(0, QPrintDialog::tr("Print"),
+ QPrintDialog::tr("The 'From' value cannot be greater than the 'To' value."),
+ QPrintDialog::tr("OK"));
+ }
+ } while (!done);
+
+ QApplicationPrivate::leaveModal(&modal_widget);
+
+ qt_win_eatMouseMove();
+
+ // write values back...
+ if (result) {
+ QT_WA({
+ PRINTDLGW *pdw = reinterpret_cast<PRINTDLGW *>(pd);
+ qt_win_read_back_PRINTDLG(pdw, q, this);
+ qt_win_clean_up_PRINTDLG(&pdw);
+ }, {
+ PRINTDLGA *pda = reinterpret_cast<PRINTDLGA *>(pd);
+ qt_win_read_back_PRINTDLG(pda, q, this);
+ qt_win_clean_up_PRINTDLG(&pda);
+ });
+ // update printer validity
+ printer->d_func()->validPrinter = !ep->name.isEmpty();
+ }
+
+ // Cleanup...
+ GlobalFree(tempDevNames);
+
+ q->done(result);
+
+ return result;
+}
+
+void QPrintDialog::setVisible(bool visible)
+{
+ Q_D(QPrintDialog);
+
+ // its always modal, so we cannot hide a native print dialog
+ if (!visible)
+ return;
+
+ if (!warnIfNotNative(d->printer))
+ return;
+
+ (void)d->openWindowsPrintDialogModally();
+ return;
+}
+
+QT_END_NAMESPACE
+
+#include "moc_qprintdialog.cpp"
+
+#endif // QT_NO_PRINTDIALOG
diff --git a/src/gui/dialogs/qprintpreviewdialog.cpp b/src/gui/dialogs/qprintpreviewdialog.cpp
new file mode 100644
index 0000000000..c00bd14339
--- /dev/null
+++ b/src/gui/dialogs/qprintpreviewdialog.cpp
@@ -0,0 +1,793 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qprintpreviewdialog.h"
+#include "qprintpreviewwidget.h"
+#include <private/qprinter_p.h>
+
+#include <QtGui/qaction.h>
+#include <QtGui/qboxlayout.h>
+#include <QtGui/qcombobox.h>
+#include <QtGui/qlabel.h>
+#include <QtGui/qlineedit.h>
+#include <QtGui/qpagesetupdialog.h>
+#include <QtGui/qprinter.h>
+#include <QtGui/qstyle.h>
+#include <QtGui/qtoolbutton.h>
+#include <QtGui/qvalidator.h>
+#include <QtGui/qfiledialog.h>
+#include <QtCore/QCoreApplication>
+
+#include <math.h>
+
+#ifndef QT_NO_PRINTPREVIEWDIALOG
+
+QT_BEGIN_NAMESPACE
+
+namespace {
+class ZoomFactorValidator : public QDoubleValidator
+{
+public:
+ ZoomFactorValidator(QObject* parent)
+ : QDoubleValidator(parent) {}
+ ZoomFactorValidator(qreal bottom, qreal top, int decimals, QObject *parent)
+ : QDoubleValidator(bottom, top, decimals, parent) {}
+
+ State validate(QString &input, int &pos) const
+ {
+ bool replacePercent = false;
+ if (input.endsWith(QLatin1Char('%'))) {
+ input = input.left(input.length() - 1);
+ replacePercent = true;
+ }
+ State state = QDoubleValidator::validate(input, pos);
+ if (replacePercent)
+ input += QLatin1Char('%');
+ const int num_size = 4;
+ if (state == Intermediate) {
+ int i = input.indexOf(QLocale::system().decimalPoint());
+ if ((i == -1 && input.size() > num_size)
+ || (i != -1 && i > num_size))
+ return Invalid;
+ }
+ return state;
+ }
+};
+
+class LineEdit : public QLineEdit
+{
+ Q_OBJECT
+public:
+ LineEdit(QWidget* parent = 0)
+ : QLineEdit(parent)
+ {
+ setContextMenuPolicy(Qt::NoContextMenu);
+ connect(this, SIGNAL(returnPressed()), SLOT(handleReturnPressed()));
+ }
+
+protected:
+ void focusInEvent(QFocusEvent *e)
+ {
+ origText = text();
+ QLineEdit::focusInEvent(e);
+ }
+
+ void focusOutEvent(QFocusEvent *e)
+ {
+ if (isModified() && !hasAcceptableInput())
+ setText(origText);
+ QLineEdit::focusOutEvent(e);
+ }
+
+private slots:
+ void handleReturnPressed()
+ {
+ origText = text();
+ }
+
+private:
+ QString origText;
+};
+} // anonymous namespace
+
+class QPrintPreviewDialogPrivate
+{
+ Q_DECLARE_PUBLIC(QPrintPreviewDialog)
+public:
+ QPrintPreviewDialogPrivate(QPrintPreviewDialog *q)
+ : q_ptr(q), printDialog(0), ownPrinter(false),
+ initialized(false) {}
+
+ // private slots
+ void _q_fit(QAction *action);
+ void _q_zoomIn();
+ void _q_zoomOut();
+ void _q_navigate(QAction *action);
+ void _q_setMode(QAction *action);
+ void _q_pageNumEdited();
+ void _q_print();
+ void _q_pageSetup();
+ void _q_previewChanged();
+ void _q_zoomFactorChanged();
+
+ void init(QPrinter *printer = 0);
+ void populateScene();
+ void layoutPages();
+ void setupActions();
+ void updateNavActions();
+ void setFitting(bool on);
+ bool isFitting();
+ void updatePageNumLabel();
+ void updateZoomFactor();
+
+ QPrintPreviewDialog *q_ptr;
+ QPrintDialog *printDialog;
+ QPrintPreviewWidget *preview;
+ QPrinter *printer;
+ bool ownPrinter;
+ bool initialized;
+
+ // widgets:
+ QLineEdit *pageNumEdit;
+ QLabel *pageNumLabel;
+ QComboBox *zoomFactor;
+
+ // actions:
+ QActionGroup* navGroup;
+ QAction *nextPageAction;
+ QAction *prevPageAction;
+ QAction *firstPageAction;
+ QAction *lastPageAction;
+
+ QActionGroup* fitGroup;
+ QAction *fitWidthAction;
+ QAction *fitPageAction;
+
+ QActionGroup* zoomGroup;
+ QAction *zoomInAction;
+ QAction *zoomOutAction;
+
+ QActionGroup* orientationGroup;
+ QAction *portraitAction;
+ QAction *landscapeAction;
+
+ QActionGroup* modeGroup;
+ QAction *singleModeAction;
+ QAction *facingModeAction;
+ QAction *overviewModeAction;
+
+ QActionGroup *printerGroup;
+ QAction *printAction;
+ QAction *pageSetupAction;
+ QAction *closeAction;
+
+ QPointer<QObject> receiverToDisconnectOnClose;
+ QByteArray memberToDisconnectOnClose;
+};
+
+void QPrintPreviewDialogPrivate::init(QPrinter *_printer)
+{
+ Q_Q(QPrintPreviewDialog);
+
+ if (_printer) {
+ preview = new QPrintPreviewWidget(_printer, q);
+ printer = _printer;
+ } else {
+ ownPrinter = true;
+ printer = new QPrinter;
+ preview = new QPrintPreviewWidget(printer, q);
+ }
+ QObject::connect(preview, SIGNAL(paintRequested(QPrinter *)), q, SIGNAL(paintRequested(QPrinter *)));
+ QObject::connect(preview, SIGNAL(previewChanged()), q, SLOT(_q_previewChanged()));
+ setupActions();
+
+ // Navigation
+ QToolButton* nextPageButton = new QToolButton;
+ nextPageButton->setDefaultAction(nextPageAction);
+ QToolButton* prevPageButton = new QToolButton;
+ prevPageButton->setDefaultAction(prevPageAction);
+ QToolButton* firstPageButton = new QToolButton;
+ firstPageButton->setDefaultAction(firstPageAction);
+ QToolButton* lastPageButton = new QToolButton;
+ lastPageButton->setDefaultAction(lastPageAction);
+
+ pageNumEdit = new LineEdit;
+ pageNumEdit->setAlignment(Qt::AlignRight);
+ pageNumEdit->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::MinimumExpanding));
+ pageNumLabel = new QLabel;
+ QObject::connect(pageNumEdit, SIGNAL(editingFinished()), q, SLOT(_q_pageNumEdited()));
+
+ QToolButton* fitWidthButton = new QToolButton;
+ fitWidthButton->setDefaultAction(fitWidthAction);
+ QToolButton* fitPageButton = new QToolButton;
+ fitPageButton->setDefaultAction(fitPageAction);
+
+ zoomFactor = new QComboBox;
+ zoomFactor->setEditable(true);
+ zoomFactor->setMinimumContentsLength(7);
+ zoomFactor->setInsertPolicy(QComboBox::NoInsert);
+ LineEdit *zoomEditor = new LineEdit;
+ zoomEditor->setValidator(new ZoomFactorValidator(1, 1000, 1, zoomEditor));
+ zoomFactor->setLineEdit(zoomEditor);
+ static const short factorsX2[] = { 25, 50, 100, 200, 250, 300, 400, 800, 1600 };
+ for (int i = 0; i < int(sizeof(factorsX2) / sizeof(factorsX2[0])); ++i)
+ zoomFactor->addItem(QPrintPreviewDialog::tr("%1%").arg(factorsX2[i] / 2.0));
+ QObject::connect(zoomFactor->lineEdit(), SIGNAL(editingFinished()),
+ q, SLOT(_q_zoomFactorChanged()));
+ QObject::connect(zoomFactor, SIGNAL(currentIndexChanged(int)),
+ q, SLOT(_q_zoomFactorChanged()));
+
+ QToolButton* zoomInButton = new QToolButton;
+ zoomInButton->setDefaultAction(zoomInAction);
+ zoomInButton->setAutoRepeat(true);
+ zoomInButton->setAutoRepeatInterval(200);
+ zoomInButton->setAutoRepeatDelay(200);
+
+ QToolButton* zoomOutButton = new QToolButton;
+ zoomOutButton->setDefaultAction(zoomOutAction);
+ zoomOutButton->setAutoRepeat(true);
+ zoomOutButton->setAutoRepeatInterval(200);
+ zoomOutButton->setAutoRepeatDelay(200);
+
+ //Cannot use the actions' triggered signal here, since it doesnt autorepeat
+ QObject::connect(zoomInButton, SIGNAL(clicked()), q, SLOT(_q_zoomIn()));
+ QObject::connect(zoomOutButton, SIGNAL(clicked()), q, SLOT(_q_zoomOut()));
+
+ QToolButton* portraitButton = new QToolButton;
+ portraitButton->setDefaultAction(portraitAction);
+ QToolButton* landscapeButton = new QToolButton;
+ landscapeButton->setDefaultAction(landscapeAction);
+
+ QToolButton* singleModeButton = new QToolButton;
+ singleModeButton->setDefaultAction(singleModeAction);
+ QToolButton* facingModeButton = new QToolButton;
+ facingModeButton->setDefaultAction(facingModeAction);
+ QToolButton* overviewModeButton = new QToolButton;
+ overviewModeButton->setDefaultAction(overviewModeAction);
+
+ QToolButton *printButton = new QToolButton;
+ printButton->setDefaultAction(printAction);
+ QToolButton *pageSetupButton = new QToolButton;
+ pageSetupButton->setDefaultAction(pageSetupAction);
+ QToolButton *closeButton = new QToolButton;
+ closeButton->setDefaultAction(closeAction);
+
+ QHBoxLayout* modeLayout = new QHBoxLayout;
+ modeLayout->setSpacing(0);
+ modeLayout->addWidget(singleModeButton);
+ modeLayout->addWidget(facingModeButton);
+ modeLayout->addWidget(overviewModeButton);
+
+ QHBoxLayout *barLayout = new QHBoxLayout;
+ barLayout->addWidget(fitWidthButton);
+ barLayout->addWidget(fitPageButton);
+ barLayout->addWidget(zoomFactor);
+ barLayout->addWidget(zoomOutButton);
+ barLayout->addWidget(zoomInButton);
+ barLayout->addWidget(portraitButton);
+ barLayout->addWidget(landscapeButton);
+ barLayout->addStretch();
+ barLayout->addWidget(firstPageButton);
+ barLayout->addWidget(prevPageButton);
+ barLayout->addWidget(pageNumEdit);
+ barLayout->addWidget(pageNumLabel);
+ barLayout->addWidget(nextPageButton);
+ barLayout->addWidget(lastPageButton);
+ barLayout->addStretch();
+ barLayout->addLayout(modeLayout);
+ barLayout->addStretch();
+ barLayout->addWidget(pageSetupButton);
+ barLayout->addWidget(printButton);
+ barLayout->addWidget(closeButton);
+
+ QWidget* buttonBar = new QWidget;
+ buttonBar->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Maximum));
+ barLayout->setMargin(0);
+ buttonBar->setLayout(barLayout);
+
+ QVBoxLayout *topLayout = new QVBoxLayout;
+ topLayout->addWidget(buttonBar);
+ topLayout->addWidget(preview);
+ q->setLayout(topLayout);
+
+ QString caption = QCoreApplication::translate("QPrintPreviewDialog", "Print Preview");
+ if (!printer->docName().isEmpty())
+ caption += QString::fromLatin1(": ") + printer->docName();
+ q->setWindowTitle(caption);
+
+ if (!printer->isValid()
+#if defined(Q_WS_WIN) || defined(Q_WS_MAC)
+ || printer->outputFormat() != QPrinter::NativeFormat
+#endif
+ )
+ pageSetupButton->setEnabled(false);
+}
+
+static inline void qt_setupActionIcon(QAction *action, const QLatin1String &name)
+{
+ QLatin1String imagePrefix(":/trolltech/dialogs/qprintpreviewdialog/images/");
+ QIcon icon;
+ icon.addFile(imagePrefix + name + QLatin1String("-24.png"), QSize(24, 24));
+ icon.addFile(imagePrefix + name + QLatin1String("-32.png"), QSize(32, 32));
+ action->setIcon(icon);
+}
+
+void QPrintPreviewDialogPrivate::setupActions()
+{
+ Q_Q(QPrintPreviewDialog);
+
+ // Navigation
+ navGroup = new QActionGroup(q);
+ navGroup->setExclusive(false);
+ nextPageAction = navGroup->addAction(QCoreApplication::translate("QPrintPreviewDialog", "Next page"));
+ prevPageAction = navGroup->addAction(QCoreApplication::translate("QPrintPreviewDialog", "Previous page"));
+ firstPageAction = navGroup->addAction(QCoreApplication::translate("QPrintPreviewDialog", "First page"));
+ lastPageAction = navGroup->addAction(QCoreApplication::translate("QPrintPreviewDialog", "Last page"));
+ qt_setupActionIcon(nextPageAction, QLatin1String("go-next"));
+ qt_setupActionIcon(prevPageAction, QLatin1String("go-previous"));
+ qt_setupActionIcon(firstPageAction, QLatin1String("go-first"));
+ qt_setupActionIcon(lastPageAction, QLatin1String("go-last"));
+ QObject::connect(navGroup, SIGNAL(triggered(QAction*)), q, SLOT(_q_navigate(QAction*)));
+
+
+ fitGroup = new QActionGroup(q);
+ fitWidthAction = fitGroup->addAction(QCoreApplication::translate("QPrintPreviewDialog", "Fit width"));
+ fitPageAction = fitGroup->addAction(QCoreApplication::translate("QPrintPreviewDialog", "Fit page"));
+ fitWidthAction->setObjectName(QLatin1String("fitWidthAction"));
+ fitPageAction->setObjectName(QLatin1String("fitPageAction"));
+ fitWidthAction->setCheckable(true);
+ fitPageAction->setCheckable(true);
+ qt_setupActionIcon(fitWidthAction, QLatin1String("fit-width"));
+ qt_setupActionIcon(fitPageAction, QLatin1String("fit-page"));
+ QObject::connect(fitGroup, SIGNAL(triggered(QAction*)), q, SLOT(_q_fit(QAction*)));
+
+ // Zoom
+ zoomGroup = new QActionGroup(q);
+ zoomInAction = zoomGroup->addAction(QCoreApplication::translate("QPrintPreviewDialog", "Zoom in"));
+ zoomOutAction = zoomGroup->addAction(QCoreApplication::translate("QPrintPreviewDialog", "Zoom out"));
+ qt_setupActionIcon(zoomInAction, QLatin1String("zoom-in"));
+ qt_setupActionIcon(zoomOutAction, QLatin1String("zoom-out"));
+
+ // Portrait/Landscape
+ orientationGroup = new QActionGroup(q);
+ portraitAction = orientationGroup->addAction(QCoreApplication::translate("QPrintPreviewDialog", "Portrait"));
+ landscapeAction = orientationGroup->addAction(QCoreApplication::translate("QPrintPreviewDialog", "Landscape"));
+ portraitAction->setCheckable(true);
+ landscapeAction->setCheckable(true);
+ qt_setupActionIcon(portraitAction, QLatin1String("layout-portrait"));
+ qt_setupActionIcon(landscapeAction, QLatin1String("layout-landscape"));
+ QObject::connect(portraitAction, SIGNAL(triggered(bool)), preview, SLOT(setPortraitOrientation()));
+ QObject::connect(landscapeAction, SIGNAL(triggered(bool)), preview, SLOT(setLandscapeOrientation()));
+
+ // Display mode
+ modeGroup = new QActionGroup(q);
+ singleModeAction = modeGroup->addAction(QCoreApplication::translate("QPrintPreviewDialog", "Show single page"));
+ facingModeAction = modeGroup->addAction(QCoreApplication::translate("QPrintPreviewDialog", "Show facing pages"));
+ overviewModeAction = modeGroup->addAction(QCoreApplication::translate("QPrintPreviewDialog", "Show overview of all pages"));
+ qt_setupActionIcon(singleModeAction, QLatin1String("view-page-one"));
+ qt_setupActionIcon(facingModeAction, QLatin1String("view-page-sided"));
+ qt_setupActionIcon(overviewModeAction, QLatin1String("view-page-multi"));
+ singleModeAction->setObjectName(QLatin1String("singleModeAction"));
+ facingModeAction->setObjectName(QLatin1String("facingModeAction"));
+ overviewModeAction->setObjectName(QLatin1String("overviewModeAction"));
+
+ singleModeAction->setCheckable(true);
+ facingModeAction->setCheckable(true);
+ overviewModeAction->setCheckable(true);
+ QObject::connect(modeGroup, SIGNAL(triggered(QAction*)), q, SLOT(_q_setMode(QAction*)));
+
+ // Print
+ printerGroup = new QActionGroup(q);
+ printAction = printerGroup->addAction(QCoreApplication::translate("QPrintPreviewDialog", "Print"));
+ pageSetupAction = printerGroup->addAction(QCoreApplication::translate("QPrintPreviewDialog", "Page setup"));
+ closeAction = printerGroup->addAction(QCoreApplication::translate("QPrintPreviewDialog", "Close"));
+ qt_setupActionIcon(printAction, QLatin1String("print"));
+ qt_setupActionIcon(pageSetupAction, QLatin1String("page-setup"));
+ QObject::connect(printAction, SIGNAL(triggered(bool)), q, SLOT(_q_print()));
+ QObject::connect(pageSetupAction, SIGNAL(triggered(bool)), q, SLOT(_q_pageSetup()));
+ QObject::connect(closeAction, SIGNAL(triggered(bool)), q, SLOT(reject()));
+
+ // Initial state:
+ fitPageAction->setChecked(true);
+ singleModeAction->setChecked(true);
+ if (preview->orientation() == QPrinter::Portrait)
+ portraitAction->setChecked(true);
+ else
+ landscapeAction->setChecked(true);
+}
+
+
+bool QPrintPreviewDialogPrivate::isFitting()
+{
+ return (fitGroup->isExclusive()
+ && (fitWidthAction->isChecked() || fitPageAction->isChecked()));
+}
+
+
+void QPrintPreviewDialogPrivate::setFitting(bool on)
+{
+ if (isFitting() == on)
+ return;
+ fitGroup->setExclusive(on);
+ if (on) {
+ QAction* action = fitWidthAction->isChecked() ? fitWidthAction : fitPageAction;
+ action->setChecked(true);
+ if (fitGroup->checkedAction() != action) {
+ // work around exclusitivity problem
+ fitGroup->removeAction(action);
+ fitGroup->addAction(action);
+ }
+ } else {
+ fitWidthAction->setChecked(false);
+ fitPageAction->setChecked(false);
+ }
+}
+
+void QPrintPreviewDialogPrivate::updateNavActions()
+{
+ int curPage = preview->currentPage();
+ int numPages = preview->numPages();
+ nextPageAction->setEnabled(curPage < numPages);
+ prevPageAction->setEnabled(curPage > 1);
+ firstPageAction->setEnabled(curPage > 1);
+ lastPageAction->setEnabled(curPage < numPages);
+ pageNumEdit->setText(QString::number(curPage));
+}
+
+void QPrintPreviewDialogPrivate::updatePageNumLabel()
+{
+ Q_Q(QPrintPreviewDialog);
+
+ int numPages = preview->numPages();
+ int maxChars = QString::number(numPages).length();
+ pageNumLabel->setText(QString(QLatin1String("/ %1")).arg(numPages));
+ int cyphersWidth = q->fontMetrics().width(QString().fill(QLatin1Char('8'), maxChars));
+ int maxWidth = pageNumEdit->minimumSizeHint().width() + cyphersWidth;
+ pageNumEdit->setMinimumWidth(maxWidth);
+ pageNumEdit->setMaximumWidth(maxWidth);
+ pageNumEdit->setValidator(new QIntValidator(1, numPages, pageNumEdit));
+ // any old one will be deleted later along with its parent pageNumEdit
+}
+
+void QPrintPreviewDialogPrivate::updateZoomFactor()
+{
+ zoomFactor->lineEdit()->setText(QString().sprintf("%.1f%%", preview->zoomFactor()*100));
+}
+
+void QPrintPreviewDialogPrivate::_q_fit(QAction* action)
+{
+ setFitting(true);
+ if (action == fitPageAction)
+ preview->fitInView();
+ else
+ preview->fitToWidth();
+}
+
+void QPrintPreviewDialogPrivate::_q_zoomIn()
+{
+ setFitting(false);
+ preview->zoomIn();
+ updateZoomFactor();
+}
+
+void QPrintPreviewDialogPrivate::_q_zoomOut()
+{
+ setFitting(false);
+ preview->zoomOut();
+ updateZoomFactor();
+}
+
+void QPrintPreviewDialogPrivate::_q_pageNumEdited()
+{
+ bool ok = false;
+ int res = pageNumEdit->text().toInt(&ok);
+ if (ok)
+ preview->setCurrentPage(res);
+}
+
+void QPrintPreviewDialogPrivate::_q_navigate(QAction* action)
+{
+ int curPage = preview->currentPage();
+ if (action == prevPageAction)
+ preview->setCurrentPage(curPage - 1);
+ else if (action == nextPageAction)
+ preview->setCurrentPage(curPage + 1);
+ else if (action == firstPageAction)
+ preview->setCurrentPage(1);
+ else if (action == lastPageAction)
+ preview->setCurrentPage(preview->numPages());
+ updateNavActions();
+}
+
+void QPrintPreviewDialogPrivate::_q_setMode(QAction* action)
+{
+ if (action == overviewModeAction) {
+ preview->setViewMode(QPrintPreviewWidget::AllPagesView);
+ setFitting(false);
+ fitGroup->setEnabled(false);
+ navGroup->setEnabled(false);
+ pageNumEdit->setEnabled(false);
+ pageNumLabel->setEnabled(false);
+ } else if (action == facingModeAction) {
+ preview->setViewMode(QPrintPreviewWidget::FacingPagesView);
+ } else {
+ preview->setViewMode(QPrintPreviewWidget::SinglePageView);
+ }
+ if (action == facingModeAction || action == singleModeAction) {
+ fitGroup->setEnabled(true);
+ navGroup->setEnabled(true);
+ pageNumEdit->setEnabled(true);
+ pageNumLabel->setEnabled(true);
+ setFitting(true);
+ }
+}
+
+void QPrintPreviewDialogPrivate::_q_print()
+{
+ Q_Q(QPrintPreviewDialog);
+
+#if defined(Q_WS_WIN) || defined(Q_WS_MAC)
+ if (printer->outputFormat() != QPrinter::NativeFormat) {
+ QString title;
+ QString suffix;
+ if (printer->outputFormat() == QPrinter::PdfFormat) {
+ title = QCoreApplication::translate("QPrintPreviewDialog", "Export to PDF");
+ suffix = QLatin1String(".pdf");
+ } else {
+ title = QCoreApplication::translate("QPrintPreviewDialog", "Export to PostScript");
+ suffix = QLatin1String(".ps");
+ }
+ QString fileName = QFileDialog::getSaveFileName(q, title, printer->outputFileName(),
+ QLatin1String("*") + suffix);
+ if (!fileName.isEmpty()) {
+ if (QFileInfo(fileName).suffix().isEmpty())
+ fileName.append(suffix);
+ printer->setOutputFileName(fileName);
+ }
+ if (!printer->outputFileName().isEmpty())
+ preview->print();
+ q->accept();
+ return;
+ }
+#endif
+
+ if (!printDialog)
+ printDialog = new QPrintDialog(printer, q);
+ if (printDialog->exec() == QDialog::Accepted) {
+ preview->print();
+ q->accept();
+ }
+}
+
+void QPrintPreviewDialogPrivate::_q_pageSetup()
+{
+ Q_Q(QPrintPreviewDialog);
+
+ QPageSetupDialog pageSetup(printer, q);
+ if (pageSetup.exec() == QDialog::Accepted) {
+ // update possible orientation changes
+ if (preview->orientation() == QPrinter::Portrait) {
+ portraitAction->setChecked(true);
+ preview->setPortraitOrientation();
+ }else {
+ landscapeAction->setChecked(true);
+ preview->setLandscapeOrientation();
+ }
+ }
+}
+
+void QPrintPreviewDialogPrivate::_q_previewChanged()
+{
+ updateNavActions();
+ updatePageNumLabel();
+ updateZoomFactor();
+}
+
+void QPrintPreviewDialogPrivate::_q_zoomFactorChanged()
+{
+ QString text = zoomFactor->lineEdit()->text();
+ bool ok;
+ qreal factor = text.remove(QLatin1Char('%')).toFloat(&ok);
+ factor = qMax(qreal(1.0), qMin(qreal(1000.0), factor));
+ if (ok) {
+ preview->setZoomFactor(factor/100.0);
+ zoomFactor->setEditText(QString(QLatin1String("%1%")).arg(factor));
+ setFitting(false);
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////
+
+/*!
+ \class QPrintPreviewDialog
+ \since 4.4
+
+ \brief The QPrintPreviewDialog class provides a dialog for
+ previewing and configuring page layouts for printer output.
+
+ Using QPrintPreviewDialog in your existing application is
+ straightforward:
+
+ \list 1
+ \o Create the QPrintPreviewDialog.
+
+ You can construct a QPrintPreviewDialog with an existing QPrinter
+ object, or you can have QPrintPreviewDialog create one for you,
+ which will be the system default printer.
+
+ \o Connect the paintRequested() signal to a slot.
+
+ When the dialog needs to generate a set of preview pages, the
+ paintRequested() signal will be emitted. You can use the exact
+ same code for the actual printing as for having the preview
+ generated, including calling QPrinter::newPage() to start a new
+ page in the preview. Connect a slot to the paintRequested()
+ signal, where you draw onto the QPrinter object that is passed
+ into the slot.
+
+ \o Call exec().
+
+ Call QPrintPreviewDialog::exec() to show the preview dialog.
+ \endlist
+
+
+ \sa QPrinter, QPrintDialog, QPageSetupDialog, QPrintPreviewWidget
+*/
+
+/*!
+ Constructs a QPrintPreviewDialog based on \a printer and with \a
+ parent as the parent widget. The widget flags \a flags are passed on
+ to the QWidget constructor.
+
+ \sa QWidget::setWindowFlags()
+*/
+QPrintPreviewDialog::QPrintPreviewDialog(QPrinter* printer, QWidget *parent, Qt::WindowFlags flags)
+ : QDialog(parent, flags), d_ptr(new QPrintPreviewDialogPrivate(this))
+{
+ Q_D(QPrintPreviewDialog);
+ d->init(printer);
+}
+
+/*!
+ \overload
+ \fn QPrintPreviewDialog::QPrintPreviewDialog(QWidget *parent, Qt::WindowFlags flags)
+
+ This will create an internal QPrinter object, which will use the
+ system default printer.
+*/
+QPrintPreviewDialog::QPrintPreviewDialog(QWidget *parent, Qt::WindowFlags f)
+ : QDialog(parent, f), d_ptr(new QPrintPreviewDialogPrivate(this))
+{
+ Q_D(QPrintPreviewDialog);
+ d->init();
+}
+
+/*!
+ Destroys the QPrintPreviewDialog.
+*/
+QPrintPreviewDialog::~QPrintPreviewDialog()
+{
+ Q_D(QPrintPreviewDialog);
+ if (d->ownPrinter)
+ delete d->printer;
+ delete d->printDialog;
+ delete d_ptr;
+}
+
+/*!
+ \reimp
+*/
+void QPrintPreviewDialog::setVisible(bool visible)
+{
+ Q_D(QPrintPreviewDialog);
+ // this will make the dialog get a decent default size
+ if (visible && !d->initialized) {
+ d->preview->updatePreview();
+ d->initialized = true;
+ }
+ QDialog::setVisible(visible);
+}
+
+/*!
+ \reimp
+*/
+void QPrintPreviewDialog::done(int result)
+{
+ Q_D(QPrintPreviewDialog);
+ QDialog::done(result);
+ if (d->receiverToDisconnectOnClose) {
+ disconnect(this, SIGNAL(finished(int)),
+ d->receiverToDisconnectOnClose, d->memberToDisconnectOnClose);
+ d->receiverToDisconnectOnClose = 0;
+ }
+ d->memberToDisconnectOnClose.clear();
+}
+
+/*!
+ \overload
+ \since 4.5
+
+ Opens the dialog and connects its finished(int) signal to the slot specified
+ by \a receiver and \a member.
+
+ The signal will be disconnected from the slot when the dialog is closed.
+*/
+void QPrintPreviewDialog::open(QObject *receiver, const char *member)
+{
+ Q_D(QPrintPreviewDialog);
+ // the int parameter isn't very useful here; we could just as well connect
+ // to reject(), but this feels less robust somehow
+ connect(this, SIGNAL(finished(int)), receiver, member);
+ d->receiverToDisconnectOnClose = receiver;
+ d->memberToDisconnectOnClose = member;
+ QDialog::open();
+}
+
+/*!
+ Returns a pointer to the QPrinter object this dialog is currently
+ operating on.
+*/
+QPrinter *QPrintPreviewDialog::printer()
+{
+ Q_D(QPrintPreviewDialog);
+ return d->printer;
+}
+
+/*!
+ \fn void QPrintPreviewDialog::paintRequested(QPrinter *printer)
+
+ This signal is emitted when the QPrintPreviewDialog needs to generate
+ a set of preview pages.
+
+ The \a printer instance supplied is the paint device onto which you should
+ paint the contents of each page, using the QPrinter instance in the same way
+ as you would when printing directly.
+*/
+
+
+QT_END_NAMESPACE
+
+#include "moc_qprintpreviewdialog.cpp"
+#include "qprintpreviewdialog.moc"
+
+#endif // QT_NO_PRINTPREVIEWDIALOG
+
+
diff --git a/src/gui/dialogs/qprintpreviewdialog.h b/src/gui/dialogs/qprintpreviewdialog.h
new file mode 100644
index 0000000000..c3a4d57867
--- /dev/null
+++ b/src/gui/dialogs/qprintpreviewdialog.h
@@ -0,0 +1,107 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QPRINTPREVIEWDIALOG_H
+#define QPRINTPREVIEWDIALOG_H
+
+#include <QtGui/qdialog.h>
+
+#ifndef QT_NO_PRINTPREVIEWDIALOG
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+class QGraphicsView;
+class QPrintPreviewDialogPrivate;
+
+class Q_GUI_EXPORT QPrintPreviewDialog : public QDialog
+{
+ Q_OBJECT
+ Q_DECLARE_PRIVATE(QPrintPreviewDialog)
+
+public:
+ explicit QPrintPreviewDialog(QWidget *parent = 0, Qt::WindowFlags flags = 0);
+ explicit QPrintPreviewDialog(QPrinter *printer, QWidget *parent = 0, Qt::WindowFlags flags = 0);
+ ~QPrintPreviewDialog();
+
+#ifdef Q_NO_USING_KEYWORD
+#ifndef Q_QDOC
+ void open() { QDialog::open(); }
+#endif
+#else
+ using QDialog::open;
+#endif
+ void open(QObject *receiver, const char *member);
+
+ QPrinter *printer();
+
+ void setVisible(bool visible);
+ void done(int result);
+
+Q_SIGNALS:
+ void paintRequested(QPrinter *printer);
+
+private:
+ Q_PRIVATE_SLOT(d_func(), void _q_fit(QAction *action))
+ Q_PRIVATE_SLOT(d_func(), void _q_zoomIn())
+ Q_PRIVATE_SLOT(d_func(), void _q_zoomOut())
+ Q_PRIVATE_SLOT(d_func(), void _q_navigate(QAction *action))
+ Q_PRIVATE_SLOT(d_func(), void _q_setMode(QAction *action))
+ Q_PRIVATE_SLOT(d_func(), void _q_pageNumEdited())
+ Q_PRIVATE_SLOT(d_func(), void _q_print())
+ Q_PRIVATE_SLOT(d_func(), void _q_pageSetup())
+ Q_PRIVATE_SLOT(d_func(), void _q_previewChanged())
+ Q_PRIVATE_SLOT(d_func(), void _q_zoomFactorChanged())
+
+ QPrintPreviewDialogPrivate *d_ptr;
+};
+
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QT_NO_PRINTPREVIEWDIALOG
+
+#endif // QPRINTPREVIEWDIALOG_H
diff --git a/src/gui/dialogs/qprintpropertieswidget.ui b/src/gui/dialogs/qprintpropertieswidget.ui
new file mode 100644
index 0000000000..26fa09599e
--- /dev/null
+++ b/src/gui/dialogs/qprintpropertieswidget.ui
@@ -0,0 +1,70 @@
+<ui version="4.0" >
+ <class>QPrintPropertiesWidget</class>
+ <widget class="QWidget" name="QPrintPropertiesWidget" >
+ <property name="geometry" >
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>396</width>
+ <height>288</height>
+ </rect>
+ </property>
+ <property name="windowTitle" >
+ <string>Form</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_4" >
+ <property name="margin" >
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="QTabWidget" name="tabs" >
+ <property name="currentIndex" >
+ <number>0</number>
+ </property>
+ <widget class="QWidget" name="tabPage" >
+ <property name="geometry" >
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>392</width>
+ <height>261</height>
+ </rect>
+ </property>
+ <attribute name="title" >
+ <string>Page</string>
+ </attribute>
+ <layout class="QHBoxLayout" name="horizontalLayout" >
+ <item>
+ <widget class="QPageSetupWidget" native="1" name="pageSetup" />
+ </item>
+ </layout>
+ </widget>
+ <widget class="QWidget" name="cupsPropertiesPage" >
+ <attribute name="title" >
+ <string>Advanced</string>
+ </attribute>
+ <layout class="QHBoxLayout" name="horizontalLayout_2" >
+ <item>
+ <widget class="QTreeView" name="treeView" >
+ <property name="alternatingRowColors" >
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <customwidgets>
+ <customwidget>
+ <class>QPageSetupWidget</class>
+ <extends>QWidget</extends>
+ <header>qpagesetupdialog_unix_p.h</header>
+ <container>1</container>
+ </customwidget>
+ </customwidgets>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/src/gui/dialogs/qprintsettingsoutput.ui b/src/gui/dialogs/qprintsettingsoutput.ui
new file mode 100644
index 0000000000..fc57e863f9
--- /dev/null
+++ b/src/gui/dialogs/qprintsettingsoutput.ui
@@ -0,0 +1,371 @@
+<ui version="4.0" >
+ <class>QPrintSettingsOutput</class>
+ <widget class="QWidget" name="QPrintSettingsOutput" >
+ <property name="geometry" >
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>416</width>
+ <height>166</height>
+ </rect>
+ </property>
+ <property name="windowTitle" >
+ <string>Form</string>
+ </property>
+ <layout class="QHBoxLayout" name="horizontalLayout_2" >
+ <property name="margin" >
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="QTabWidget" name="tabs" >
+ <property name="currentIndex" >
+ <number>0</number>
+ </property>
+ <widget class="QWidget" name="copiesTab" >
+ <property name="geometry" >
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>412</width>
+ <height>139</height>
+ </rect>
+ </property>
+ <attribute name="title" >
+ <string>Copies</string>
+ </attribute>
+ <layout class="QHBoxLayout" name="horizontalLayout" >
+ <item>
+ <widget class="QGroupBox" name="gbPrintRange" >
+ <property name="sizePolicy" >
+ <sizepolicy vsizetype="Minimum" hsizetype="Preferred" >
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="title" >
+ <string>Print range</string>
+ </property>
+ <layout class="QVBoxLayout" name="_3" >
+ <property name="spacing" >
+ <number>4</number>
+ </property>
+ <property name="margin" >
+ <number>6</number>
+ </property>
+ <item>
+ <widget class="QRadioButton" name="printAll" >
+ <property name="text" >
+ <string>Print all</string>
+ </property>
+ <property name="checked" >
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="_4" >
+ <property name="spacing" >
+ <number>6</number>
+ </property>
+ <property name="margin" >
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="QRadioButton" name="printRange" >
+ <property name="text" >
+ <string>Pages from</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QSpinBox" name="from" >
+ <property name="enabled" >
+ <bool>false</bool>
+ </property>
+ <property name="minimum" >
+ <number>1</number>
+ </property>
+ <property name="maximum" >
+ <number>999</number>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLabel" name="label_3" >
+ <property name="text" >
+ <string>to</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QSpinBox" name="to" >
+ <property name="enabled" >
+ <bool>false</bool>
+ </property>
+ <property name="minimum" >
+ <number>1</number>
+ </property>
+ <property name="maximum" >
+ <number>999</number>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer>
+ <property name="orientation" >
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0" >
+ <size>
+ <width>0</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <widget class="QRadioButton" name="printSelection" >
+ <property name="text" >
+ <string>Selection</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer name="verticalSpacer" >
+ <property name="orientation" >
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0" >
+ <size>
+ <width>1</width>
+ <height>1</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="groupBox" >
+ <property name="title" >
+ <string>Output Settings</string>
+ </property>
+ <layout class="QGridLayout" name="gridLayout" >
+ <item row="0" column="0" >
+ <widget class="QLabel" name="label" >
+ <property name="text" >
+ <string>Copies:</string>
+ </property>
+ <property name="buddy" >
+ <cstring>copies</cstring>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1" colspan="2" >
+ <widget class="QSpinBox" name="copies" >
+ <property name="minimum" >
+ <number>1</number>
+ </property>
+ <property name="maximum" >
+ <number>999</number>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="3" >
+ <spacer name="horizontalSpacer" >
+ <property name="orientation" >
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0" >
+ <size>
+ <width>91</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item row="1" column="0" colspan="2" >
+ <widget class="QCheckBox" name="collate" >
+ <property name="text" >
+ <string>Collate</string>
+ </property>
+ </widget>
+ </item>
+ <item rowspan="2" row="1" column="2" colspan="2" >
+ <widget class="QLabel" name="outputIcon" >
+ <property name="sizePolicy" >
+ <sizepolicy vsizetype="Ignored" hsizetype="Ignored" >
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="0" colspan="2" >
+ <widget class="QCheckBox" name="reverse" >
+ <property name="text" >
+ <string>Reverse</string>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="0" colspan="4" >
+ <spacer name="verticalSpacer_2" >
+ <property name="orientation" >
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0" >
+ <size>
+ <width>0</width>
+ <height>1</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <widget class="QWidget" name="optionsTab" >
+ <property name="geometry" >
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>412</width>
+ <height>139</height>
+ </rect>
+ </property>
+ <attribute name="title" >
+ <string>Options</string>
+ </attribute>
+ <layout class="QGridLayout" name="gridLayout_2" >
+ <item row="0" column="1" >
+ <widget class="QGroupBox" name="colorMode" >
+ <property name="title" >
+ <string>Color Mode</string>
+ </property>
+ <layout class="QGridLayout" name="gridLayout_4" >
+ <item row="2" column="0" >
+ <spacer name="verticalSpacer_6" >
+ <property name="orientation" >
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0" >
+ <size>
+ <width>1</width>
+ <height>0</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item row="0" column="0" >
+ <widget class="QRadioButton" name="color" >
+ <property name="text" >
+ <string>Color</string>
+ </property>
+ </widget>
+ </item>
+ <item rowspan="3" row="0" column="1" >
+ <widget class="QLabel" name="colorIcon" />
+ </item>
+ <item row="1" column="0" >
+ <widget class="QRadioButton" name="grayscale" >
+ <property name="text" >
+ <string>Grayscale</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item row="0" column="0" >
+ <widget class="QGroupBox" name="duplex" >
+ <property name="title" >
+ <string>Duplex Printing</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout" >
+ <item>
+ <widget class="QRadioButton" name="noDuplex" >
+ <property name="text" >
+ <string>None</string>
+ </property>
+ <property name="checked" >
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QRadioButton" name="duplexLong" >
+ <property name="text" >
+ <string>Long side</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QRadioButton" name="duplexShort" >
+ <property name="text" >
+ <string>Short side</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer name="verticalSpacer_42" >
+ <property name="orientation" >
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0" >
+ <size>
+ <width>1</width>
+ <height>0</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections>
+ <connection>
+ <sender>printRange</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>from</receiver>
+ <slot>setEnabled(bool)</slot>
+ <hints>
+ <hint type="sourcelabel" >
+ <x>76</x>
+ <y>59</y>
+ </hint>
+ <hint type="destinationlabel" >
+ <x>122</x>
+ <y>57</y>
+ </hint>
+ </hints>
+ </connection>
+ <connection>
+ <sender>printRange</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>to</receiver>
+ <slot>setEnabled(bool)</slot>
+ <hints>
+ <hint type="sourcelabel" >
+ <x>69</x>
+ <y>67</y>
+ </hint>
+ <hint type="destinationlabel" >
+ <x>215</x>
+ <y>67</y>
+ </hint>
+ </hints>
+ </connection>
+ </connections>
+</ui>
diff --git a/src/gui/dialogs/qprintwidget.ui b/src/gui/dialogs/qprintwidget.ui
new file mode 100644
index 0000000000..8a4f3bde0a
--- /dev/null
+++ b/src/gui/dialogs/qprintwidget.ui
@@ -0,0 +1,116 @@
+<ui version="4.0" >
+ <class>QPrintWidget</class>
+ <widget class="QWidget" name="QPrintWidget" >
+ <property name="geometry" >
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>443</width>
+ <height>175</height>
+ </rect>
+ </property>
+ <property name="windowTitle" >
+ <string>Form</string>
+ </property>
+ <layout class="QHBoxLayout" name="horizontalLayout_2" >
+ <property name="margin" >
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="QGroupBox" name="printerGroup" >
+ <property name="title" >
+ <string>Printer</string>
+ </property>
+ <layout class="QGridLayout" name="gridLayout" >
+ <item row="0" column="0" >
+ <widget class="QLabel" name="label" >
+ <property name="text" >
+ <string>&amp;Name:</string>
+ </property>
+ <property name="buddy" >
+ <cstring>printers</cstring>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1" >
+ <widget class="QComboBox" name="printers" >
+ <property name="sizePolicy" >
+ <sizepolicy vsizetype="Fixed" hsizetype="Preferred" >
+ <horstretch>3</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="2" >
+ <widget class="QPushButton" name="properties" >
+ <property name="sizePolicy" >
+ <sizepolicy vsizetype="Fixed" hsizetype="Minimum" >
+ <horstretch>1</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text" >
+ <string>P&amp;roperties</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0" >
+ <widget class="QLabel" name="label_2" >
+ <property name="text" >
+ <string>Location:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1" >
+ <widget class="QLabel" name="location" />
+ </item>
+ <item row="1" column="2" >
+ <widget class="QCheckBox" name="preview" >
+ <property name="text" >
+ <string>Preview</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="0" >
+ <widget class="QLabel" name="label_3" >
+ <property name="text" >
+ <string>Type:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="1" >
+ <widget class="QLabel" name="type" />
+ </item>
+ <item row="3" column="0" >
+ <widget class="QLabel" name="lOutput" >
+ <property name="text" >
+ <string>Output &amp;file:</string>
+ </property>
+ <property name="buddy" >
+ <cstring>filename</cstring>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="1" colspan="2" >
+ <layout class="QHBoxLayout" name="horizontalLayout" >
+ <item>
+ <widget class="QLineEdit" name="filename" />
+ </item>
+ <item>
+ <widget class="QToolButton" name="fileBrowser" >
+ <property name="text" >
+ <string>...</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/src/gui/dialogs/qprogressdialog.cpp b/src/gui/dialogs/qprogressdialog.cpp
new file mode 100644
index 0000000000..66a1285003
--- /dev/null
+++ b/src/gui/dialogs/qprogressdialog.cpp
@@ -0,0 +1,865 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qprogressdialog.h"
+
+#ifndef QT_NO_PROGRESSDIALOG
+
+#include "qshortcut.h"
+#include "qpainter.h"
+#include "qdrawutil.h"
+#include "qdatetime.h"
+#include "qlabel.h"
+#include "qprogressbar.h"
+#include "qapplication.h"
+#include "qstyle.h"
+#include "qpushbutton.h"
+#include "qcursor.h"
+#include "qtimer.h"
+#include <private/qdialog_p.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;
+
+class QProgressDialogPrivate : public QDialogPrivate
+{
+ Q_DECLARE_PUBLIC(QProgressDialog)
+
+public:
+ QProgressDialogPrivate() : label(0), cancel(0), bar(0),
+ shown_once(false),
+ cancellation_flag(false),
+ showTime(defaultShowTime),
+#ifndef QT_NO_SHORTCUT
+ escapeShortcut(0),
+#endif
+ useDefaultCancelText(false)
+ {
+ }
+
+ void init(const QString &labelText, const QString &cancelText, int min, int max);
+ void layout();
+ void retranslateStrings();
+ void _q_disconnectOnClose();
+
+ QLabel *label;
+ QPushButton *cancel;
+ QProgressBar *bar;
+ QTimer *forceTimer;
+ bool shown_once;
+ bool cancellation_flag;
+ QTime starttime;
+#ifndef QT_NO_CURSOR
+ QCursor parentCursor;
+#endif
+ int showTime;
+ bool autoClose;
+ bool autoReset;
+ bool forceHide;
+#ifndef QT_NO_SHORTCUT
+ QShortcut *escapeShortcut;
+#endif
+ bool useDefaultCancelText;
+ QPointer<QObject> receiverToDisconnectOnClose;
+ QByteArray memberToDisconnectOnClose;
+};
+
+void QProgressDialogPrivate::init(const QString &labelText, const QString &cancelText,
+ int min, int max)
+{
+ Q_Q(QProgressDialog);
+ label = new QLabel(labelText, q);
+ int align = q->style()->styleHint(QStyle::SH_ProgressDialog_TextLabelAlignment, 0, q);
+ label->setAlignment(Qt::Alignment(align));
+ bar = new QProgressBar(q);
+ bar->setRange(min, max);
+ autoClose = true;
+ autoReset = true;
+ forceHide = false;
+ QObject::connect(q, SIGNAL(canceled()), q, SLOT(cancel()));
+ forceTimer = new QTimer(q);
+ QObject::connect(forceTimer, SIGNAL(timeout()), q, SLOT(forceShow()));
+ if (useDefaultCancelText) {
+ retranslateStrings();
+ } else {
+ q->setCancelButtonText(cancelText);
+ }
+}
+
+void QProgressDialogPrivate::layout()
+{
+ Q_Q(QProgressDialog);
+ int sp = q->style()->pixelMetric(QStyle::PM_DefaultLayoutSpacing);
+ int mtb = q->style()->pixelMetric(QStyle::PM_DefaultTopLevelMargin);
+ int mlr = qMin(q->width() / 10, mtb);
+ const bool centered =
+ bool(q->style()->styleHint(QStyle::SH_ProgressDialog_CenterCancelButton, 0, q));
+
+ QSize cs = cancel ? 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 = cancel ? cs.height() + sp : 0;
+ lh = qMax(0, q->height() - mtb - bh.height() - sp - cspc);
+
+ if (lh < q->height()/4) {
+ // Getting cramped
+ sp /= 2;
+ mtb /= 2;
+ if (cancel) {
+ cs.setHeight(qMax(4,cs.height()-sp-2));
+ }
+ bh.setHeight(qMax(4,bh.height()-sp-1));
+ } else {
+ break;
+ }
+ }
+
+ if (cancel) {
+ cancel->setGeometry(
+ centered ? q->width()/2 - cs.width()/2 : q->width() - mlr - cs.width(),
+ q->height() - mtb - cs.height(),
+ cs.width(), cs.height());
+ }
+
+ if (label)
+ label->setGeometry(mlr, 0, q->width()-mlr*2, lh);
+ bar->setGeometry(mlr, lh+sp, q->width()-mlr*2, bh.height());
+}
+
+void QProgressDialogPrivate::retranslateStrings()
+{
+ Q_Q(QProgressDialog);
+ if (useDefaultCancelText)
+ q->setCancelButtonText(QProgressDialog::tr("Cancel"));
+}
+
+void QProgressDialogPrivate::_q_disconnectOnClose()
+{
+ Q_Q(QProgressDialog);
+ if (receiverToDisconnectOnClose) {
+ QObject::disconnect(q, SIGNAL(canceled()), receiverToDisconnectOnClose,
+ memberToDisconnectOnClose);
+ receiverToDisconnectOnClose = 0;
+ }
+ memberToDisconnectOnClose.clear();
+}
+
+/*!
+ \class QProgressDialog
+ \brief The QProgressDialog class provides feedback on the progress of a slow operation.
+ \ingroup dialogs
+ \mainclass
+
+ 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. QProgressDialog 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 setMinimum() and setMaximum() or the constructor to set the number of
+ "steps" in the operation and call setValue() as the operation
+ progresses. The number of steps 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 the value set by setMinimum(),
+ and the progress dialog shows that the operation has finished when
+ you call setValue() with the value set by setMaximum() as its argument.
+
+ The dialog automatically resets and hides itself at the end of the
+ operation. Use setAutoReset() and setAutoClose() to change this
+ behavior. Note that if you set a new maximum (using setMaximum() or
+ setRange()) that equals your current value(), the dialog will not
+ close regardless.
+
+ There are two ways of using QProgressDialog: modal and modeless.
+
+ Compared to a modeless QProgressDialog, a modal QProgressDialog is simpler
+ to use for the programmer. Do the operation in a loop, call \l setValue() at
+ intervals, and check for cancellation with wasCanceled(). For example:
+
+ \snippet doc/src/snippets/dialogs/dialogs.cpp 3
+
+ 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 QProgressBar 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
+ setValue() at intervals. For example:
+
+ \snippet doc/src/snippets/dialogs/dialogs.cpp 4
+ \codeline
+ \snippet doc/src/snippets/dialogs/dialogs.cpp 5
+ \codeline
+ \snippet doc/src/snippets/dialogs/dialogs.cpp 6
+
+ 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.
+
+ \image plastique-progressdialog.png A progress dialog shown in the Plastique widget style.
+
+ \sa QDialog, QProgressBar, {fowler}{GUI Design Handbook: Progress Indicator},
+ {Find Files Example}, {Pixelator Example}
+*/
+
+
+/*!
+ Constructs a progress dialog.
+
+ Default settings:
+ \list
+ \i The label text is empty.
+ \i The cancel button text is (translated) "Cancel".
+ \i minimum is 0;
+ \i maximum is 100
+ \endlist
+
+ The \a parent argument is dialog's parent widget. The widget flags, \a f, are
+ passed to the QDialog::QDialog() constructor.
+
+ \sa setLabelText(), setCancelButtonText(), setCancelButton(),
+ setMinimum(), setMaximum()
+*/
+
+QProgressDialog::QProgressDialog(QWidget *parent, Qt::WindowFlags f)
+ : QDialog(*(new QProgressDialogPrivate), parent, f)
+{
+ Q_D(QProgressDialog);
+ d->useDefaultCancelText = true;
+ d->init(QString::fromLatin1(""), QString(), 0, 100);
+}
+
+/*!
+ Constructs a progress dialog.
+
+ The \a labelText is the text used to remind the user what is progressing.
+
+ The \a cancelButtonText is the text to display on the cancel button. If
+ QString() is passed then no cancel button is shown.
+
+ The \a minimum and \a maximum is the 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 minimum value would be 0,
+ and the maximum would be 50. Before examining the first file, call
+ setValue(0). As each file is processed call setValue(1), setValue(2),
+ etc., finally calling setValue(50) after examining the last file.
+
+ The \a parent argument is the dialog's parent widget. The parent, \a parent, and
+ widget flags, \a f, are passed to the QDialog::QDialog() constructor.
+
+ \sa setLabelText(), setLabel(), setCancelButtonText(), setCancelButton(),
+ setMinimum(), setMaximum()
+*/
+
+QProgressDialog::QProgressDialog(const QString &labelText,
+ const QString &cancelButtonText,
+ int minimum, int maximum,
+ QWidget *parent, Qt::WindowFlags f)
+ : QDialog(*(new QProgressDialogPrivate), parent, f)
+{
+ Q_D(QProgressDialog);
+ d->init(labelText, cancelButtonText, minimum, maximum);
+}
+
+
+/*!
+ Destroys the progress dialog.
+*/
+
+QProgressDialog::~QProgressDialog()
+{
+}
+
+/*!
+ \fn void QProgressDialog::canceled()
+
+ This signal is emitted when the cancel button is clicked.
+ It is connected to the cancel() slot by default.
+
+ \sa wasCanceled()
+*/
+
+
+/*!
+ 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 QProgressDialog::setLabel(QLabel *label)
+{
+ Q_D(QProgressDialog);
+ 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 QProgressDialog::labelText
+ \brief the label's text
+
+ The default text is an empty string.
+*/
+
+QString QProgressDialog::labelText() const
+{
+ Q_D(const QProgressDialog);
+ if (d->label)
+ return d->label->text();
+ return QString();
+}
+
+void QProgressDialog::setLabelText(const QString &text)
+{
+ Q_D(QProgressDialog);
+ if (d->label) {
+ d->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. If 0 is passed then
+ no cancel button will be shown.
+
+ \sa setCancelButtonText()
+*/
+
+void QProgressDialog::setCancelButton(QPushButton *cancelButton)
+{
+ Q_D(QProgressDialog);
+ 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()));
+#ifndef QT_NO_SHORTCUT
+ d->escapeShortcut = new QShortcut(Qt::Key_Escape, this, SIGNAL(canceled()));
+#endif
+ } else {
+#ifndef QT_NO_SHORTCUT
+ delete d->escapeShortcut;
+ d->escapeShortcut = 0;
+#endif
+ }
+ 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. If the text
+ is set to QString() then it will cause the cancel button to be
+ hidden and deleted.
+
+ \sa setCancelButton()
+*/
+
+void QProgressDialog::setCancelButtonText(const QString &cancelButtonText)
+{
+ Q_D(QProgressDialog);
+ d->useDefaultCancelText = false;
+
+ 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 QProgressDialog::setBar(QProgressBar *bar)
+{
+ Q_D(QProgressDialog);
+ if (!bar) {
+ qWarning("QProgressDialog::setBar: Cannot set a null progress bar");
+ return;
+ }
+#ifndef QT_NO_DEBUG
+ if (value() > 0)
+ qWarning("QProgressDialog::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 QProgressDialog::wasCanceled
+ \brief whether the dialog was canceled
+*/
+
+bool QProgressDialog::wasCanceled() const
+{
+ Q_D(const QProgressDialog);
+ return d->cancellation_flag;
+}
+
+
+/*!
+ \property QProgressDialog::maximum
+ \brief the highest value represented by the progress bar
+
+ The default is 0.
+
+ \sa minimum, setRange()
+*/
+
+int QProgressDialog::maximum() const
+{
+ Q_D(const QProgressDialog);
+ return d->bar->maximum();
+}
+
+void QProgressDialog::setMaximum(int maximum)
+{
+ Q_D(QProgressDialog);
+ d->bar->setMaximum(maximum);
+}
+
+/*!
+ \property QProgressDialog::minimum
+ \brief the lowest value represented by the progress bar
+
+ The default is 0.
+
+ \sa maximum, setRange()
+*/
+
+int QProgressDialog::minimum() const
+{
+ Q_D(const QProgressDialog);
+ return d->bar->minimum();
+}
+
+void QProgressDialog::setMinimum(int minimum)
+{
+ Q_D(QProgressDialog);
+ d->bar->setMinimum(minimum);
+}
+
+/*!
+ Sets the progress dialog's minimum and maximum values
+ to \a minimum and \a maximum, respectively.
+
+ If \a maximum is smaller than \a minimum, \a minimum becomes the only
+ legal value.
+
+ If the current value falls outside the new range, the progress
+ dialog is reset with reset().
+
+ \sa minimum, maximum
+*/
+void QProgressDialog::setRange(int minimum, int maximum)
+{
+ Q_D(QProgressDialog);
+ d->bar->setRange(minimum, maximum);
+}
+
+
+/*!
+ Resets the progress dialog.
+ The progress dialog becomes hidden if autoClose() is true.
+
+ \sa setAutoClose(), setAutoReset()
+*/
+
+void QProgressDialog::reset()
+{
+ Q_D(QProgressDialog);
+#ifndef QT_NO_CURSOR
+ if (value() >= 0) {
+ if (parentWidget())
+ parentWidget()->setCursor(d->parentCursor);
+ }
+#endif
+ if (d->autoClose || d->forceHide)
+ hide();
+ d->bar->reset();
+ d->cancellation_flag = false;
+ d->shown_once = false;
+ d->forceTimer->stop();
+
+ /*
+ I wish we could disconnect the user slot provided to open() here but
+ unfortunately reset() is usually called before the slot has been invoked.
+ (reset() is itself invoked when canceled() is emitted.)
+ */
+ if (d->receiverToDisconnectOnClose)
+ QMetaObject::invokeMethod(this, "_q_disconnectOnClose", Qt::QueuedConnection);
+}
+
+/*!
+ Resets the progress dialog. wasCanceled() becomes true until
+ the progress dialog is reset.
+ The progress dialog becomes hidden.
+*/
+
+void QProgressDialog::cancel()
+{
+ Q_D(QProgressDialog);
+ d->forceHide = true;
+ reset();
+ d->forceHide = false;
+ d->cancellation_flag = true;
+}
+
+
+int QProgressDialog::value() const
+{
+ Q_D(const QProgressDialog);
+ return d->bar->value();
+}
+
+/*!
+ \property QProgressDialog::value
+ \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
+ QProgressDialog::maximum(); you can call setValue() any number of times
+ in-between.
+
+ \warning If the progress dialog is modal
+ (see QProgressDialog::QProgressDialog()),
+ 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 QProgressDialog inside a paintEvent()!
+
+ \sa minimum, maximum
+*/
+void QProgressDialog::setValue(int progress)
+{
+ Q_D(QProgressDialog);
+ if (progress == d->bar->value()
+ || (d->bar->value() == -1 && progress == d->bar->maximum()))
+ return;
+
+ d->bar->setValue(progress);
+
+ if (d->shown_once) {
+ if (isModal())
+ qApp->processEvents();
+ } else {
+ if (progress == 0) {
+ d->starttime.start();
+ d->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;
+ int totalSteps = maximum() - minimum();
+ int myprogress = progress - minimum();
+ if ((totalSteps - myprogress) >= INT_MAX / elapsed)
+ estimate = (totalSteps - myprogress) / myprogress * elapsed;
+ else
+ estimate = elapsed * (totalSteps - myprogress) / myprogress;
+ 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 == d->bar->maximum() && d->autoReset)
+ reset();
+}
+
+/*!
+ 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 QProgressDialog::sizeHint() const
+{
+ Q_D(const QProgressDialog);
+ QSize sh = d->label ? d->label->sizeHint() : QSize(0, 0);
+ QSize bh = d->bar->sizeHint();
+ int margin = style()->pixelMetric(QStyle::PM_DefaultTopLevelMargin);
+ int spacing = style()->pixelMetric(QStyle::PM_DefaultLayoutSpacing);
+ int h = margin * 2 + bh.height() + sh.height() + spacing;
+ if (d->cancel)
+ h += d->cancel->sizeHint().height() + spacing;
+ return QSize(qMax(200, sh.width() + 2 * margin), h);
+}
+
+/*!\reimp
+*/
+void QProgressDialog::resizeEvent(QResizeEvent *)
+{
+ Q_D(QProgressDialog);
+ d->layout();
+}
+
+/*!
+ \reimp
+*/
+void QProgressDialog::changeEvent(QEvent *ev)
+{
+ Q_D(QProgressDialog);
+ if (ev->type() == QEvent::StyleChange)
+ d->layout();
+ else if (ev->type() == QEvent::LanguageChange)
+ d->retranslateStrings();
+ QDialog::changeEvent(ev);
+}
+
+/*!
+ \property QProgressDialog::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 QProgressDialog::setMinimumDuration(int ms)
+{
+ Q_D(QProgressDialog);
+ d->showTime = ms;
+ if (d->bar->value() == 0) {
+ d->forceTimer->stop();
+ d->forceTimer->start(ms);
+ }
+}
+
+int QProgressDialog::minimumDuration() const
+{
+ Q_D(const QProgressDialog);
+ return d->showTime;
+}
+
+
+/*!
+ \reimp
+*/
+
+void QProgressDialog::closeEvent(QCloseEvent *e)
+{
+ emit canceled();
+ QDialog::closeEvent(e);
+}
+
+/*!
+ \property QProgressDialog::autoReset
+ \brief whether the progress dialog calls reset() as soon as value() equals maximum()
+
+ The default is true.
+
+ \sa setAutoClose()
+*/
+
+void QProgressDialog::setAutoReset(bool b)
+{
+ Q_D(QProgressDialog);
+ d->autoReset = b;
+}
+
+bool QProgressDialog::autoReset() const
+{
+ Q_D(const QProgressDialog);
+ return d->autoReset;
+}
+
+/*!
+ \property QProgressDialog::autoClose
+ \brief whether the dialog gets hidden by reset()
+
+ The default is true.
+
+ \sa setAutoReset()
+*/
+
+void QProgressDialog::setAutoClose(bool close)
+{
+ Q_D(QProgressDialog);
+ d->autoClose = close;
+}
+
+bool QProgressDialog::autoClose() const
+{
+ Q_D(const QProgressDialog);
+ return d->autoClose;
+}
+
+/*!
+ \reimp
+*/
+
+void QProgressDialog::showEvent(QShowEvent *e)
+{
+ Q_D(QProgressDialog);
+ QDialog::showEvent(e);
+ int w = qMax(isVisible() ? width() : 0, sizeHint().width());
+ int h = qMax(isVisible() ? height() : 0, sizeHint().height());
+ resize(w, h);
+ d->forceTimer->stop();
+}
+
+/*!
+ Shows the dialog if it is still hidden after the algorithm has been started
+ and minimumDuration milliseconds have passed.
+
+ \sa setMinimumDuration()
+*/
+
+void QProgressDialog::forceShow()
+{
+ Q_D(QProgressDialog);
+ d->forceTimer->stop();
+ if (d->shown_once || d->cancellation_flag)
+ return;
+
+ show();
+ d->shown_once = true;
+}
+
+/*!
+ \since 4.5
+ \overload
+
+ Opens the dialog and connects its accepted() signal to the slot specified
+ by \a receiver and \a member.
+
+ The signal will be disconnected from the slot when the dialog is closed.
+*/
+void QProgressDialog::open(QObject *receiver, const char *member)
+{
+ Q_D(QProgressDialog);
+ connect(this, SIGNAL(canceled()), receiver, member);
+ d->receiverToDisconnectOnClose = receiver;
+ d->memberToDisconnectOnClose = member;
+ QDialog::open();
+}
+
+QT_END_NAMESPACE
+
+#include "moc_qprogressdialog.cpp"
+
+#endif // QT_NO_PROGRESSDIALOG
diff --git a/src/gui/dialogs/qprogressdialog.h b/src/gui/dialogs/qprogressdialog.h
new file mode 100644
index 0000000000..256981c5f1
--- /dev/null
+++ b/src/gui/dialogs/qprogressdialog.h
@@ -0,0 +1,145 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QPROGRESSDIALOG_H
+#define QPROGRESSDIALOG_H
+
+#include <QtGui/qdialog.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+#ifndef QT_NO_PROGRESSDIALOG
+
+class QPushButton;
+class QLabel;
+class QProgressBar;
+class QTimer;
+class QProgressDialogPrivate;
+
+class Q_GUI_EXPORT QProgressDialog : public QDialog
+{
+ Q_OBJECT
+ Q_DECLARE_PRIVATE(QProgressDialog)
+ Q_PROPERTY(bool wasCanceled READ wasCanceled)
+ Q_PROPERTY(int minimum READ minimum WRITE setMinimum)
+ Q_PROPERTY(int maximum READ maximum WRITE setMaximum)
+ Q_PROPERTY(int value READ value WRITE setValue)
+ 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:
+ explicit QProgressDialog(QWidget *parent = 0, Qt::WindowFlags flags = 0);
+ QProgressDialog(const QString &labelText, const QString &cancelButtonText,
+ int minimum, int maximum, QWidget *parent = 0, Qt::WindowFlags flags = 0);
+ ~QProgressDialog();
+
+ void setLabel(QLabel *label);
+ void setCancelButton(QPushButton *button);
+ void setBar(QProgressBar *bar);
+
+ bool wasCanceled() const;
+
+ int minimum() const;
+ int maximum() const;
+
+ int value() const;
+
+ QSize sizeHint() const;
+
+ QString labelText() const;
+ int minimumDuration() const;
+
+ void setAutoReset(bool reset);
+ bool autoReset() const;
+ void setAutoClose(bool close);
+ bool autoClose() const;
+
+#ifdef Q_NO_USING_KEYWORD
+#ifndef Q_QDOC
+ void open() { QDialog::open(); }
+#endif
+#else
+ using QDialog::open;
+#endif
+ void open(QObject *receiver, const char *member);
+
+public Q_SLOTS:
+ void cancel();
+ void reset();
+ void setMaximum(int maximum);
+ void setMinimum(int minimum);
+ void setRange(int minimum, int maximum);
+ void setValue(int progress);
+ void setLabelText(const QString &text);
+ void setCancelButtonText(const QString &text);
+ void setMinimumDuration(int ms);
+
+Q_SIGNALS:
+ void canceled();
+
+protected:
+ void resizeEvent(QResizeEvent *event);
+ void closeEvent(QCloseEvent *event);
+ void changeEvent(QEvent *event);
+ void showEvent(QShowEvent *event);
+
+protected Q_SLOTS:
+ void forceShow();
+
+private:
+ Q_DISABLE_COPY(QProgressDialog)
+
+ Q_PRIVATE_SLOT(d_func(), void _q_disconnectOnClose())
+};
+
+#endif // QT_NO_PROGRESSDIALOG
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QPROGRESSDIALOG_H
diff --git a/src/gui/dialogs/qsidebar.cpp b/src/gui/dialogs/qsidebar.cpp
new file mode 100644
index 0000000000..1bd2b7d1e4
--- /dev/null
+++ b/src/gui/dialogs/qsidebar.cpp
@@ -0,0 +1,485 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qsidebar_p.h"
+#include "qfilesystemmodel.h"
+
+#ifndef QT_NO_FILEDIALOG
+
+#include <qaction.h>
+#include <qurl.h>
+#include <qmenu.h>
+#include <qmimedata.h>
+#include <qevent.h>
+#include <qdebug.h>
+#include <qfileiconprovider.h>
+#include <qfiledialog.h>
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ QUrlModel lets you have indexes from a QFileSystemModel to a list. When QFileSystemModel
+ changes them QUrlModel will automatically update.
+
+ Example usage: File dialog sidebar and combo box
+ */
+QUrlModel::QUrlModel(QObject *parent) : QStandardItemModel(parent), showFullPath(false), fileSystemModel(0)
+{
+}
+
+/*!
+ \reimp
+*/
+QStringList QUrlModel::mimeTypes() const
+{
+ return QStringList(QLatin1String("text/uri-list"));
+}
+
+/*!
+ \reimp
+*/
+Qt::ItemFlags QUrlModel::flags(const QModelIndex &index) const
+{
+ Qt::ItemFlags flags = QStandardItemModel::flags(index);
+ if (index.isValid()) {
+ flags &= ~Qt::ItemIsEditable;
+ // ### some future version could support "moving" urls onto a folder
+ flags &= ~Qt::ItemIsDropEnabled;
+ }
+
+ if (index.data(Qt::DecorationRole).isNull())
+ flags &= ~Qt::ItemIsEnabled;
+
+ if (invalidUrls.contains(index.data(UrlRole).toUrl()))
+ flags &= ~Qt::ItemIsEnabled;
+
+ return flags;
+}
+
+/*!
+ \reimp
+*/
+QMimeData *QUrlModel::mimeData(const QModelIndexList &indexes) const
+{
+ QList<QUrl> list;
+ for (int i = 0; i < indexes.count(); ++i) {
+ if (indexes.at(i).column() == 0)
+ list.append(indexes.at(i).data(UrlRole).toUrl());
+ }
+ QMimeData *data = new QMimeData();
+ data->setUrls(list);
+ return data;
+}
+
+#ifndef QT_NO_DRAGANDDROP
+
+/*!
+ Decide based upon the data if it should be accepted or not
+
+ We only accept dirs and not files
+*/
+bool QUrlModel::canDrop(QDragEnterEvent *event)
+{
+ if (!event->mimeData()->formats().contains(mimeTypes().first()))
+ return false;
+
+ const QList<QUrl> list = event->mimeData()->urls();
+ for (int i = 0; i < list.count(); ++i) {
+ QModelIndex idx = fileSystemModel->index(list.at(0).toLocalFile());
+ if (!fileSystemModel->isDir(idx))
+ return false;
+ }
+ return true;
+}
+
+/*!
+ \reimp
+*/
+bool QUrlModel::dropMimeData(const QMimeData *data, Qt::DropAction action,
+ int row, int column, const QModelIndex &parent)
+{
+ if (!data->formats().contains(mimeTypes().first()))
+ return false;
+ Q_UNUSED(action);
+ Q_UNUSED(column);
+ Q_UNUSED(parent);
+ addUrls(data->urls(), row);
+ return true;
+}
+
+#endif // QT_NO_DRAGANDDROP
+
+/*!
+ \reimp
+
+ If the role is the UrlRole then handle otherwise just pass to QStandardItemModel
+*/
+bool QUrlModel::setData(const QModelIndex &index, const QVariant &value, int role)
+{
+ if (value.type() == QVariant::Url) {
+ QUrl url = value.toUrl();
+ QModelIndex dirIndex = fileSystemModel->index(url.toLocalFile());
+ //On windows the popup display the "C:\", convert to nativeSeparators
+ if (showFullPath)
+ QStandardItemModel::setData(index, QDir::toNativeSeparators(fileSystemModel->data(dirIndex, QFileSystemModel::FilePathRole).toString()));
+ else {
+ QStandardItemModel::setData(index, QDir::toNativeSeparators(fileSystemModel->data(dirIndex, QFileSystemModel::FilePathRole).toString()), Qt::ToolTipRole);
+ QStandardItemModel::setData(index, fileSystemModel->data(dirIndex).toString());
+ }
+ QStandardItemModel::setData(index, fileSystemModel->data(dirIndex, Qt::DecorationRole),
+ Qt::DecorationRole);
+ QStandardItemModel::setData(index, url, UrlRole);
+ return true;
+ }
+ return QStandardItemModel::setData(index, value, role);
+}
+
+void QUrlModel::setUrl(const QModelIndex &index, const QUrl &url, const QModelIndex &dirIndex)
+{
+ setData(index, url, UrlRole);
+ if (url.path().isEmpty()) {
+ setData(index, fileSystemModel->myComputer());
+ setData(index, fileSystemModel->myComputer(Qt::DecorationRole), Qt::DecorationRole);
+ } else {
+ QString newName;
+ if (showFullPath) {
+ //On windows the popup display the "C:\", convert to nativeSeparators
+ newName = QDir::toNativeSeparators(dirIndex.data(QFileSystemModel::FilePathRole).toString());
+ } else {
+ newName = dirIndex.data().toString();
+ }
+
+ QIcon newIcon = qvariant_cast<QIcon>(dirIndex.data(Qt::DecorationRole));
+ if (!dirIndex.isValid()) {
+ newIcon = fileSystemModel->iconProvider()->icon(QFileIconProvider::Folder);
+ newName = QFileInfo(url.toLocalFile()).fileName();
+ if (!invalidUrls.contains(url))
+ invalidUrls.append(url);
+ }
+
+ // Make sure that we have at least 32x32 images
+ const QSize size = newIcon.actualSize(QSize(32,32));
+ if (size.width() < 32) {
+ QPixmap smallPixmap = newIcon.pixmap(QSize(32, 32));
+ newIcon.addPixmap(smallPixmap.scaledToWidth(32, Qt::SmoothTransformation));
+ }
+
+ if (index.data().toString() != newName)
+ setData(index, newName);
+ QIcon oldIcon = qvariant_cast<QIcon>(index.data(Qt::DecorationRole));
+ if (oldIcon.cacheKey() != newIcon.cacheKey())
+ setData(index, newIcon, Qt::DecorationRole);
+ }
+}
+
+void QUrlModel::setUrls(const QList<QUrl> &list)
+{
+ removeRows(0, rowCount());
+ invalidUrls.clear();
+ watching.clear();
+ addUrls(list, 0);
+}
+
+/*!
+ Add urls \a list into the list at \a row. If move then movie
+ existing ones to row.
+
+ \sa dropMimeData()
+*/
+void QUrlModel::addUrls(const QList<QUrl> &list, int row, bool move)
+{
+ if (row == -1)
+ row = rowCount();
+ row = qMin(row, rowCount());
+ for (int i = list.count() - 1; i >= 0; --i) {
+ QUrl url = list.at(i);
+ if (!url.isValid() || url.scheme() != QLatin1String("file"))
+ continue;
+ for (int j = 0; move && j < rowCount(); ++j) {
+ if (index(j, 0).data(UrlRole) == url) {
+ removeRow(j);
+ if (j <= row)
+ row--;
+ break;
+ }
+ }
+ row = qMax(row, 0);
+ QModelIndex idx = fileSystemModel->index(url.toLocalFile());
+ if (!fileSystemModel->isDir(idx))
+ continue;
+ insertRows(row, 1);
+ setUrl(index(row, 0), url, idx);
+ watching.append(QPair<QModelIndex, QString>(idx, url.toLocalFile()));
+ }
+}
+
+/*!
+ Return the complete list of urls in a QList.
+*/
+QList<QUrl> QUrlModel::urls() const
+{
+ QList<QUrl> list;
+ for (int i = 0; i < rowCount(); ++i)
+ list.append(data(index(i, 0), UrlRole).toUrl());
+ return list;
+}
+
+/*!
+ QFileSystemModel to get index's from, clears existing rows
+*/
+void QUrlModel::setFileSystemModel(QFileSystemModel *model)
+{
+ if (model == fileSystemModel)
+ return;
+ if (fileSystemModel != 0) {
+ disconnect(model, SIGNAL(dataChanged(const QModelIndex &, const QModelIndex &)),
+ this, SLOT(dataChanged(const QModelIndex &, const QModelIndex &)));
+ disconnect(model, SIGNAL(layoutChanged()),
+ this, SLOT(layoutChanged()));
+ disconnect(model, SIGNAL(rowsRemoved(const QModelIndex &, int, int)),
+ this, SLOT(layoutChanged()));
+ }
+ fileSystemModel = model;
+ if (fileSystemModel != 0) {
+ connect(model, SIGNAL(dataChanged(const QModelIndex &, const QModelIndex &)),
+ this, SLOT(dataChanged(const QModelIndex &, const QModelIndex &)));
+ connect(model, SIGNAL(layoutChanged()),
+ this, SLOT(layoutChanged()));
+ connect(model, SIGNAL(rowsRemoved(const QModelIndex &, int, int)),
+ this, SLOT(layoutChanged()));
+ }
+ clear();
+ insertColumns(0, 1);
+}
+
+/*
+ If one of the index's we are watching has changed update our internal data
+*/
+void QUrlModel::dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight)
+{
+ QModelIndex parent = topLeft.parent();
+ for (int i = 0; i < watching.count(); ++i) {
+ QModelIndex index = watching.at(i).first;
+ if (index.model() && topLeft.model()) {
+ Q_ASSERT(index.model() == topLeft.model());
+ }
+ if ( index.row() >= topLeft.row()
+ && index.row() <= bottomRight.row()
+ && index.column() >= topLeft.column()
+ && index.column() <= bottomRight.column()
+ && index.parent() == parent) {
+ changed(watching.at(i).second);
+ }
+ }
+}
+
+/*!
+ Re-get all of our data, anything could have changed!
+ */
+void QUrlModel::layoutChanged()
+{
+ QStringList paths;
+ for (int i = 0; i < watching.count(); ++i)
+ paths.append(watching.at(i).second);
+ watching.clear();
+ for (int i = 0; i < paths.count(); ++i) {
+ QString path = paths.at(i);
+ QModelIndex newIndex = fileSystemModel->index(path);
+ watching.append(QPair<QModelIndex, QString>(newIndex, path));
+ if (newIndex.isValid())
+ changed(path);
+ }
+}
+
+/*!
+ The following path changed data update our copy of that data
+
+ \sa layoutChanged() dataChanged()
+*/
+void QUrlModel::changed(const QString &path)
+{
+ for (int i = 0; i < rowCount(); ++i) {
+ QModelIndex idx = index(i, 0);
+ if (idx.data(UrlRole).toUrl().toLocalFile() == path) {
+ setData(idx, idx.data(UrlRole).toUrl());
+ }
+ }
+}
+
+QSidebar::QSidebar(QWidget *parent) : QListView(parent)
+{
+}
+
+void QSidebar::init(QFileSystemModel *model, const QList<QUrl> &newUrls)
+{
+ // ### TODO make icon size dynamic
+ setIconSize(QSize(24,24));
+ setUniformItemSizes(true);
+ urlModel = new QUrlModel(this);
+ urlModel->setFileSystemModel(model);
+ setModel(urlModel);
+
+ connect(selectionModel(), SIGNAL(currentChanged(const QModelIndex &, const QModelIndex &)),
+ this, SLOT(clicked(const QModelIndex &)));
+#ifndef QT_NO_DRAGANDDROP
+ setDragDropMode(QAbstractItemView::DragDrop);
+#endif
+ setContextMenuPolicy(Qt::CustomContextMenu);
+ connect(this, SIGNAL(customContextMenuRequested(const QPoint &)),
+ this, SLOT(showContextMenu(const QPoint &)));
+ urlModel->setUrls(newUrls);
+ setCurrentIndex(this->model()->index(0,0));
+}
+
+QSidebar::~QSidebar()
+{
+}
+
+#ifndef QT_NO_DRAGANDDROP
+void QSidebar::dragEnterEvent(QDragEnterEvent *event)
+{
+ if (urlModel->canDrop(event))
+ QListView::dragEnterEvent(event);
+}
+#endif // QT_NO_DRAGANDDROP
+
+QSize QSidebar::sizeHint() const
+{
+ if (model())
+ return QListView::sizeHintForIndex(model()->index(0, 0)) + QSize(2 * frameWidth(), 2 * frameWidth());
+ return QListView::sizeHint();
+}
+
+void QSidebar::selectUrl(const QUrl &url)
+{
+ disconnect(selectionModel(), SIGNAL(currentChanged(const QModelIndex &, const QModelIndex &)),
+ this, SLOT(clicked(const QModelIndex &)));
+
+ selectionModel()->clear();
+ for (int i = 0; i < model()->rowCount(); ++i) {
+ if (model()->index(i, 0).data(QUrlModel::UrlRole).toUrl() == url) {
+ selectionModel()->select(model()->index(i, 0), QItemSelectionModel::Select);
+ break;
+ }
+ }
+
+ connect(selectionModel(), SIGNAL(currentChanged(const QModelIndex &, const QModelIndex &)),
+ this, SLOT(clicked(const QModelIndex &)));
+}
+
+#ifndef QT_NO_MENU
+/*!
+ \internal
+
+ \sa removeEntry()
+*/
+void QSidebar::showContextMenu(const QPoint &position)
+{
+ QList<QAction *> actions;
+ if (indexAt(position).isValid()) {
+ QAction *action = new QAction(QFileDialog::tr("Remove"), this);
+ if (indexAt(position).data(QUrlModel::UrlRole).toUrl().path().isEmpty())
+ action->setEnabled(false);
+ connect(action, SIGNAL(triggered()), this, SLOT(removeEntry()));
+ actions.append(action);
+ }
+ if (actions.count() > 0)
+ QMenu::exec(actions, mapToGlobal(position));
+}
+#endif // QT_NO_MENU
+
+/*!
+ \internal
+
+ \sa showContextMenu()
+*/
+void QSidebar::removeEntry()
+{
+ QList<QModelIndex> idxs = selectionModel()->selectedIndexes();
+ QList<QPersistentModelIndex> indexes;
+ for (int i = 0; i < idxs.count(); i++)
+ indexes.append(idxs.at(i));
+
+ for (int i = 0; i < indexes.count(); ++i)
+ if (!indexes.at(i).data(QUrlModel::UrlRole).toUrl().path().isEmpty())
+ model()->removeRow(indexes.at(i).row());
+}
+
+/*!
+ \internal
+
+ \sa goToUrl()
+*/
+void QSidebar::clicked(const QModelIndex &index)
+{
+ QUrl url = model()->index(index.row(), 0).data(QUrlModel::UrlRole).toUrl();
+ emit goToUrl(url);
+ selectUrl(url);
+}
+
+/*!
+ \reimp
+ Don't automatically select something
+ */
+void QSidebar::focusInEvent(QFocusEvent *event)
+{
+ QAbstractScrollArea::focusInEvent(event);
+ viewport()->update();
+}
+
+/*!
+ \reimp
+ */
+bool QSidebar::event(QEvent * event)
+{
+ if (event->type() == QEvent::KeyRelease) {
+ QKeyEvent* ke = (QKeyEvent*) event;
+ if (ke->key() == Qt::Key_Delete) {
+ removeEntry();
+ return true;
+ }
+ }
+ return QListView::event(event);
+}
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/gui/dialogs/qsidebar_p.h b/src/gui/dialogs/qsidebar_p.h
new file mode 100644
index 0000000000..ecbbb3783a
--- /dev/null
+++ b/src/gui/dialogs/qsidebar_p.h
@@ -0,0 +1,147 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSIDEBAR_H
+#define QSIDEBAR_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 <qlistwidget.h>
+#include <qstandarditemmodel.h>
+#include <qurl.h>
+
+#ifndef QT_NO_FILEDIALOG
+
+QT_BEGIN_NAMESPACE
+
+class QFileSystemModel;
+class Q_AUTOTEST_EXPORT QUrlModel : public QStandardItemModel
+{
+ Q_OBJECT
+
+public:
+ enum Roles {
+ UrlRole = Qt::UserRole + 1
+ };
+
+ QUrlModel(QObject *parent = 0);
+
+ QStringList mimeTypes() const;
+ QMimeData *mimeData(const QModelIndexList &indexes) const;
+#ifndef QT_NO_DRAGANDDROP
+ bool canDrop(QDragEnterEvent *event);
+ bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent);
+#endif
+ Qt::ItemFlags flags(const QModelIndex &index) const;
+ bool setData(const QModelIndex &index, const QVariant &value, int role=Qt::EditRole);
+
+ void setUrls(const QList<QUrl> &list);
+ void addUrls(const QList<QUrl> &urls, int row = -1, bool move = true);
+ QList<QUrl> urls() const;
+ void setFileSystemModel(QFileSystemModel *model);
+ bool showFullPath;
+
+private Q_SLOTS:
+ void dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight);
+ void layoutChanged();
+
+private:
+ void setUrl(const QModelIndex &index, const QUrl &url, const QModelIndex &dirIndex);
+ void changed(const QString &path);
+ void addIndexToWatch(const QString &path, const QModelIndex &index);
+ QFileSystemModel *fileSystemModel;
+ QList<QPair<QModelIndex, QString> > watching;
+ QList<QUrl> invalidUrls;
+};
+
+class Q_AUTOTEST_EXPORT QSidebar : public QListView
+{
+ Q_OBJECT
+
+Q_SIGNALS:
+ void goToUrl(const QUrl &url);
+
+public:
+ QSidebar(QWidget *parent = 0);
+ void init(QFileSystemModel *model, const QList<QUrl> &newUrls);
+ ~QSidebar();
+
+ QSize sizeHint() const;
+
+ void setUrls(const QList<QUrl> &list) { urlModel->setUrls(list); }
+ void addUrls(const QList<QUrl> &list, int row) { urlModel->addUrls(list, row); }
+ QList<QUrl> urls() const { return urlModel->urls(); }
+
+ void selectUrl(const QUrl &url);
+
+protected:
+ bool event(QEvent * e);
+ void focusInEvent(QFocusEvent *event);
+#ifndef QT_NO_DRAGANDDROP
+ void dragEnterEvent(QDragEnterEvent *event);
+#endif
+
+private Q_SLOTS:
+ void clicked(const QModelIndex &index);
+#ifndef QT_NO_MENU
+ void showContextMenu(const QPoint &position);
+#endif
+ void removeEntry();
+
+private:
+ QUrlModel *urlModel;
+};
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_FILEDIALOG
+
+#endif // QSIDEBAR_H
+
diff --git a/src/gui/dialogs/qwizard.cpp b/src/gui/dialogs/qwizard.cpp
new file mode 100644
index 0000000000..32395c412b
--- /dev/null
+++ b/src/gui/dialogs/qwizard.cpp
@@ -0,0 +1,3765 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qwizard.h"
+
+#ifndef QT_NO_WIZARD
+
+#include "qabstractspinbox.h"
+#include "qalgorithms.h"
+#include "qapplication.h"
+#include "qboxlayout.h"
+#include "qlayoutitem.h"
+#include "qdesktopwidget.h"
+#include "qevent.h"
+#include "qframe.h"
+#include "qlabel.h"
+#include "qlineedit.h"
+#include "qpainter.h"
+#include "qpushbutton.h"
+#include "qset.h"
+#include "qstyle.h"
+#include "qvarlengtharray.h"
+#if defined(Q_WS_MAC)
+#include "private/qt_mac_p.h"
+#include "qlibrary.h"
+#elif !defined(QT_NO_STYLE_WINDOWSVISTA)
+#include "qwizard_win_p.h"
+#include "qtimer.h"
+#endif
+
+#include "private/qdialog_p.h"
+#include <qdebug.h>
+
+#ifdef Q_OS_WINCE
+extern bool qt_wince_is_mobile(); //defined in qguifunctions_wce.cpp
+#endif
+
+#include <string.h> // for memset()
+
+QT_BEGIN_NAMESPACE
+
+// These fudge terms were needed a few places to obtain pixel-perfect results
+const int GapBetweenLogoAndRightEdge = 5;
+const int ModernHeaderTopMargin = 2;
+const int ClassicHMargin = 4;
+const int MacButtonTopMargin = 13;
+const int MacLayoutLeftMargin = 20;
+const int MacLayoutTopMargin = 14;
+const int MacLayoutRightMargin = 20;
+const int MacLayoutBottomMargin = 17;
+
+static void changeSpacerSize(QLayout *layout, int index, int width, int height)
+{
+ QSpacerItem *spacer = layout->itemAt(index)->spacerItem();
+ if (!spacer)
+ return;
+ spacer->changeSize(width, height);
+}
+
+static QWidget *iWantTheFocus(QWidget *ancestor)
+{
+ const int MaxIterations = 100;
+
+ QWidget *candidate = ancestor;
+ for (int i = 0; i < MaxIterations; ++i) {
+ candidate = candidate->nextInFocusChain();
+ if (!candidate)
+ break;
+
+ if (candidate->focusPolicy() & Qt::TabFocus) {
+ if (candidate != ancestor && ancestor->isAncestorOf(candidate))
+ return candidate;
+ }
+ }
+ return 0;
+}
+
+static bool objectInheritsXAndXIsCloserThanY(const QObject *object, const QByteArray &classX,
+ const QByteArray &classY)
+{
+ const QMetaObject *metaObject = object->metaObject();
+ while (metaObject) {
+ if (metaObject->className() == classX)
+ return true;
+ if (metaObject->className() == classY)
+ return false;
+ metaObject = metaObject->superClass();
+ }
+ return false;
+}
+
+const int NFallbackDefaultProperties = 7;
+
+const struct {
+ const char *className;
+ const char *property;
+ const char *changedSignal;
+} fallbackProperties[NFallbackDefaultProperties] = {
+ // If you modify this list, make sure to update the documentation (and the auto test)
+ { "QAbstractButton", "checked", SIGNAL(toggled(bool)) },
+ { "QAbstractSlider", "value", SIGNAL(valueChanged(int)) },
+ { "QComboBox", "currentIndex", SIGNAL(currentIndexChanged(int)) },
+ { "QDateTimeEdit", "dateTime", SIGNAL(dateTimeChanged(QDateTime)) },
+ { "QLineEdit", "text", SIGNAL(textChanged(QString)) },
+ { "QListWidget", "currentRow", SIGNAL(currentRowChanged(int)) },
+ { "QSpinBox", "value", SIGNAL(valueChanged(int)) }
+};
+
+class QWizardDefaultProperty
+{
+public:
+ QByteArray className;
+ QByteArray property;
+ QByteArray changedSignal;
+
+ inline QWizardDefaultProperty() {}
+ inline QWizardDefaultProperty(const char *className, const char *property,
+ const char *changedSignal)
+ : className(className), property(property), changedSignal(changedSignal) {}
+};
+
+class QWizardField
+{
+public:
+ inline QWizardField() {}
+ QWizardField(QWizardPage *page, const QString &spec, QObject *object, const char *property,
+ const char *changedSignal);
+
+ void resolve(const QVector<QWizardDefaultProperty> &defaultPropertyTable);
+ void findProperty(const QWizardDefaultProperty *properties, int propertyCount);
+
+ QWizardPage *page;
+ QString name;
+ bool mandatory;
+ QObject *object;
+ QByteArray property;
+ QByteArray changedSignal;
+ QVariant initialValue;
+};
+
+QWizardField::QWizardField(QWizardPage *page, const QString &spec, QObject *object,
+ const char *property, const char *changedSignal)
+ : page(page), name(spec), mandatory(false), object(object), property(property),
+ changedSignal(changedSignal)
+{
+ if (name.endsWith(QLatin1Char('*'))) {
+ name.chop(1);
+ mandatory = true;
+ }
+}
+
+void QWizardField::resolve(const QVector<QWizardDefaultProperty> &defaultPropertyTable)
+{
+ if (property.isEmpty())
+ findProperty(defaultPropertyTable.constData(), defaultPropertyTable.count());
+ initialValue = object->property(property);
+}
+
+void QWizardField::findProperty(const QWizardDefaultProperty *properties, int propertyCount)
+{
+ QByteArray className;
+
+ for (int i = 0; i < propertyCount; ++i) {
+ if (objectInheritsXAndXIsCloserThanY(object, properties[i].className, className)) {
+ className = properties[i].className;
+ property = properties[i].property;
+ changedSignal = properties[i].changedSignal;
+ }
+ }
+}
+
+class QWizardLayoutInfo
+{
+public:
+ inline QWizardLayoutInfo()
+ : topLevelMarginLeft(-1), topLevelMarginRight(-1), topLevelMarginTop(-1),
+ topLevelMarginBottom(-1), childMarginLeft(-1), childMarginRight(-1),
+ childMarginTop(-1), childMarginBottom(-1), hspacing(-1), vspacing(-1),
+ wizStyle(QWizard::ClassicStyle), header(false), watermark(false), title(false),
+ subTitle(false), extension(false) {}
+
+ int topLevelMarginLeft;
+ int topLevelMarginRight;
+ int topLevelMarginTop;
+ int topLevelMarginBottom;
+ int childMarginLeft;
+ int childMarginRight;
+ int childMarginTop;
+ int childMarginBottom;
+ int hspacing;
+ int vspacing;
+ int buttonSpacing;
+ QWizard::WizardStyle wizStyle;
+ bool header;
+ bool watermark;
+ bool title;
+ bool subTitle;
+ bool extension;
+
+ bool operator==(const QWizardLayoutInfo &other);
+ inline bool operator!=(const QWizardLayoutInfo &other) { return !operator==(other); }
+};
+
+bool QWizardLayoutInfo::operator==(const QWizardLayoutInfo &other)
+{
+ return topLevelMarginLeft == other.topLevelMarginLeft
+ && topLevelMarginRight == other.topLevelMarginRight
+ && topLevelMarginTop == other.topLevelMarginTop
+ && topLevelMarginBottom == other.topLevelMarginBottom
+ && childMarginLeft == other.childMarginLeft
+ && childMarginRight == other.childMarginRight
+ && childMarginTop == other.childMarginTop
+ && childMarginBottom == other.childMarginBottom
+ && hspacing == other.hspacing
+ && vspacing == other.vspacing
+ && buttonSpacing == other.buttonSpacing
+ && wizStyle == other.wizStyle
+ && header == other.header
+ && watermark == other.watermark
+ && title == other.title
+ && subTitle == other.subTitle
+ && extension == other.extension;
+}
+
+class QWizardHeader : public QWidget
+{
+public:
+ enum RulerType { Ruler };
+
+ inline QWizardHeader(RulerType /* ruler */, QWidget *parent = 0)
+ : QWidget(parent) { setFixedHeight(2); }
+ QWizardHeader(QWidget *parent = 0);
+
+ void setup(const QWizardLayoutInfo &info, const QString &title,
+ const QString &subTitle, const QPixmap &logo, const QPixmap &banner,
+ Qt::TextFormat titleFormat, Qt::TextFormat subTitleFormat);
+
+protected:
+ void paintEvent(QPaintEvent *event);
+#if !defined(QT_NO_STYLE_WINDOWSVISTA)
+private:
+ bool vistaDisabled() const;
+#endif
+private:
+ QLabel *titleLabel;
+ QLabel *subTitleLabel;
+ QLabel *logoLabel;
+ QGridLayout *layout;
+ QPixmap bannerPixmap;
+};
+
+QWizardHeader::QWizardHeader(QWidget *parent)
+ : QWidget(parent)
+{
+ setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
+ setBackgroundRole(QPalette::Base);
+
+ titleLabel = new QLabel(this);
+ titleLabel->setBackgroundRole(QPalette::Base);
+
+ subTitleLabel = new QLabel(this);
+ subTitleLabel->setAlignment(Qt::AlignTop | Qt::AlignLeft);
+ subTitleLabel->setWordWrap(true);
+
+ logoLabel = new QLabel(this);
+
+ QFont font = titleLabel->font();
+ font.setBold(true);
+ titleLabel->setFont(font);
+
+ layout = new QGridLayout(this);
+ layout->setMargin(0);
+ layout->setSpacing(0);
+
+ layout->setRowMinimumHeight(3, 1);
+ layout->setRowStretch(4, 1);
+
+ layout->setColumnStretch(2, 1);
+ layout->setColumnMinimumWidth(4, 2 * GapBetweenLogoAndRightEdge);
+ layout->setColumnMinimumWidth(6, GapBetweenLogoAndRightEdge);
+
+ layout->addWidget(titleLabel, 2, 1, 1, 2);
+ layout->addWidget(subTitleLabel, 4, 2);
+ layout->addWidget(logoLabel, 1, 5, 5, 1);
+}
+
+#if !defined(QT_NO_STYLE_WINDOWSVISTA)
+bool QWizardHeader::vistaDisabled() const
+{
+ bool styleDisabled = false;
+ QWizard *wiz = parentWidget() ? qobject_cast <QWizard *>(parentWidget()->parentWidget()) : 0;
+ if (wiz) {
+ // Designer dosen't support the Vista style for Wizards. This property is used to turn
+ // off the Vista style.
+ const QVariant v = wiz->property("_q_wizard_vista_off");
+ styleDisabled = v.isValid() && v.toBool();
+ }
+ return styleDisabled;
+}
+#endif
+
+void QWizardHeader::setup(const QWizardLayoutInfo &info, const QString &title,
+ const QString &subTitle, const QPixmap &logo, const QPixmap &banner,
+ Qt::TextFormat titleFormat, Qt::TextFormat subTitleFormat)
+{
+ bool modern = ((info.wizStyle == QWizard::ModernStyle)
+#if !defined(QT_NO_STYLE_WINDOWSVISTA)
+ || ((info.wizStyle == QWizard::AeroStyle)
+ && (QVistaHelper::vistaState() == QVistaHelper::Classic) || vistaDisabled())
+#endif
+ );
+
+ layout->setRowMinimumHeight(0, modern ? ModernHeaderTopMargin : 0);
+ layout->setRowMinimumHeight(1, modern ? info.topLevelMarginTop - ModernHeaderTopMargin - 1 : 0);
+ layout->setRowMinimumHeight(6, (modern ? 3 : GapBetweenLogoAndRightEdge) + 2);
+
+ int minColumnWidth0 = modern ? info.topLevelMarginLeft + info.topLevelMarginRight : 0;
+ int minColumnWidth1 = modern ? info.topLevelMarginLeft + info.topLevelMarginRight + 1
+ : info.topLevelMarginLeft + ClassicHMargin;
+ layout->setColumnMinimumWidth(0, minColumnWidth0);
+ layout->setColumnMinimumWidth(1, minColumnWidth1);
+
+ titleLabel->setTextFormat(titleFormat);
+ titleLabel->setText(title);
+ logoLabel->setPixmap(logo);
+
+ subTitleLabel->setTextFormat(subTitleFormat);
+ subTitleLabel->setText(QLatin1String("Pq\nPq"));
+ int desiredSubTitleHeight = subTitleLabel->sizeHint().height();
+ subTitleLabel->setText(subTitle);
+
+ if (modern) {
+ bannerPixmap = banner;
+ } else {
+ bannerPixmap = QPixmap();
+ }
+
+ if (bannerPixmap.isNull()) {
+ /*
+ There is no widthForHeight() function, so we simulate it with a loop.
+ */
+ int candidateSubTitleWidth = qMin(512, 2 * qApp->desktop()->width() / 3);
+ int delta = candidateSubTitleWidth >> 1;
+ while (delta > 0) {
+ if (subTitleLabel->heightForWidth(candidateSubTitleWidth - delta)
+ <= desiredSubTitleHeight)
+ candidateSubTitleWidth -= delta;
+ delta >>= 1;
+ }
+
+ subTitleLabel->setMinimumSize(candidateSubTitleWidth, desiredSubTitleHeight);
+
+ QSize size = layout->totalMinimumSize();
+ setMinimumSize(size);
+ setMaximumSize(QWIDGETSIZE_MAX, size.height());
+ } else {
+ subTitleLabel->setMinimumSize(0, 0);
+ setFixedSize(banner.size() + QSize(0, 2));
+ }
+ updateGeometry();
+}
+
+void QWizardHeader::paintEvent(QPaintEvent * /* event */)
+{
+ QPainter painter(this);
+ painter.drawPixmap(0, 0, bannerPixmap);
+
+ int x = width() - 2;
+ int y = height() - 2;
+ const QPalette &pal = palette();
+ painter.setPen(pal.mid().color());
+ painter.drawLine(0, y, x, y);
+ painter.setPen(pal.base().color());
+ painter.drawPoint(x + 1, y);
+ painter.drawLine(0, y + 1, x + 1, y + 1);
+}
+
+// We save one vtable by basing QWizardRuler on QWizardHeader
+class QWizardRuler : public QWizardHeader
+{
+public:
+ inline QWizardRuler(QWidget *parent = 0)
+ : QWizardHeader(Ruler, parent) {}
+};
+
+class QWizardPagePrivate : public QWidgetPrivate
+{
+ Q_DECLARE_PUBLIC(QWizardPage)
+
+public:
+ enum TriState { Tri_Unknown = -1, Tri_False, Tri_True };
+
+ inline QWizardPagePrivate()
+ : wizard(0), completeState(Tri_Unknown), explicitlyFinal(false), commit(false) {}
+
+ bool cachedIsComplete() const;
+ void _q_maybeEmitCompleteChanged();
+ void _q_updateCachedCompleteState();
+
+ QWizard *wizard;
+ QString title;
+ QString subTitle;
+ QPixmap pixmaps[QWizard::NPixmaps];
+ QVector<QWizardField> pendingFields;
+ mutable TriState completeState;
+ bool explicitlyFinal;
+ bool commit;
+ QMap<int, QString> buttonCustomTexts;
+};
+
+bool QWizardPagePrivate::cachedIsComplete() const
+{
+ Q_Q(const QWizardPage);
+ if (completeState == Tri_Unknown)
+ completeState = q->isComplete() ? Tri_True : Tri_False;
+ return completeState == Tri_True;
+}
+
+void QWizardPagePrivate::_q_maybeEmitCompleteChanged()
+{
+ Q_Q(QWizardPage);
+ TriState newState = q->isComplete() ? Tri_True : Tri_False;
+ if (newState != completeState)
+ emit q->completeChanged();
+}
+
+void QWizardPagePrivate::_q_updateCachedCompleteState()
+{
+ Q_Q(QWizardPage);
+ completeState = q->isComplete() ? Tri_True : Tri_False;
+}
+
+class QWizardAntiFlickerWidget : public QWidget
+{
+ QWizard *wizard;
+ QWizardPrivate *wizardPrivate;
+public:
+ QWizardAntiFlickerWidget(QWizard *wizard, QWizardPrivate *wizardPrivate)
+ : QWidget(wizard)
+ , wizard(wizard)
+ , wizardPrivate(wizardPrivate) {}
+#if !defined(QT_NO_STYLE_WINDOWSVISTA)
+protected:
+ void paintEvent(QPaintEvent *);
+#endif
+};
+
+class QWizardPrivate : public QDialogPrivate
+{
+ Q_DECLARE_PUBLIC(QWizard)
+
+public:
+ typedef QMap<int, QWizardPage *> PageMap;
+
+ enum Direction {
+ Backward,
+ Forward
+ };
+
+ inline QWizardPrivate()
+ : start(-1)
+ , current(-1)
+ , canContinue(false)
+ , canFinish(false)
+ , disableUpdatesCount(0)
+ , opts(0)
+ , buttonsHaveCustomLayout(false)
+ , titleFmt(Qt::AutoText)
+ , subTitleFmt(Qt::AutoText)
+ , placeholderWidget1(0)
+ , placeholderWidget2(0)
+ , headerWidget(0)
+ , watermarkLabel(0)
+ , titleLabel(0)
+ , subTitleLabel(0)
+ , bottomRuler(0)
+#if !defined(QT_NO_STYLE_WINDOWSVISTA)
+ , vistaInitPending(false)
+ , vistaState(QVistaHelper::Dirty)
+ , vistaStateChanged(false)
+ , inHandleAeroStyleChange(false)
+#endif
+ , minimumWidth(0)
+ , minimumHeight(0)
+ , maximumWidth(QWIDGETSIZE_MAX)
+ , maximumHeight(QWIDGETSIZE_MAX)
+ {
+ for (int i = 0; i < QWizard::NButtons; ++i)
+ btns[i] = 0;
+#if !defined(QT_NO_STYLE_WINDOWSVISTA)
+ if (QSysInfo::WindowsVersion >= QSysInfo::WV_VISTA
+ && QSysInfo::WindowsVersion < QSysInfo::WV_NT_based)
+ vistaInitPending = true;
+#endif
+ }
+
+ void init();
+ void reset();
+ void cleanupPagesNotInHistory();
+ void addField(const QWizardField &field);
+ void removeFieldAt(int index);
+ void switchToPage(int newId, Direction direction);
+ QWizardLayoutInfo layoutInfoForCurrentPage();
+ void recreateLayout(const QWizardLayoutInfo &info);
+ void updateLayout();
+ void updateMinMaxSizes(const QWizardLayoutInfo &info);
+ void updateCurrentPage();
+ bool ensureButton(QWizard::WizardButton which) const;
+ void connectButton(QWizard::WizardButton which) const;
+ void updateButtonTexts();
+ void updateButtonLayout();
+ void setButtonLayout(const QWizard::WizardButton *array, int size);
+ bool buttonLayoutContains(QWizard::WizardButton which);
+ void updatePixmap(QWizard::WizardPixmap which);
+#if !defined(QT_NO_STYLE_WINDOWSVISTA)
+ bool vistaDisabled() const;
+ bool isVistaThemeEnabled(QVistaHelper::VistaState state) const;
+ void handleAeroStyleChange();
+#endif
+ bool isVistaThemeEnabled() const;
+ void disableUpdates();
+ void enableUpdates();
+ void _q_emitCustomButtonClicked();
+ void _q_updateButtonStates();
+ void setStyle(QStyle *style);
+#ifdef Q_WS_MAC
+ static QPixmap findDefaultBackgroundPixmap();
+#endif
+
+ PageMap pageMap;
+ QVector<QWizardField> fields;
+ QMap<QString, int> fieldIndexMap;
+ QVector<QWizardDefaultProperty> defaultPropertyTable;
+ QList<int> history;
+ QSet<int> initialized; // ### remove and move bit to QWizardPage?
+ int start;
+ int current;
+ bool canContinue;
+ bool canFinish;
+ QWizardLayoutInfo layoutInfo;
+ int disableUpdatesCount;
+
+ QWizard::WizardStyle wizStyle;
+ QWizard::WizardOptions opts;
+ QMap<int, QString> buttonCustomTexts;
+ bool buttonsHaveCustomLayout;
+ QList<QWizard::WizardButton> buttonsCustomLayout;
+ Qt::TextFormat titleFmt;
+ Qt::TextFormat subTitleFmt;
+ mutable QPixmap defaultPixmaps[QWizard::NPixmaps];
+
+ union {
+ // keep in sync with QWizard::WizardButton
+ mutable struct {
+ QAbstractButton *back;
+ QAbstractButton *next;
+ QAbstractButton *commit;
+ QAbstractButton *finish;
+ QAbstractButton *cancel;
+ QAbstractButton *help;
+ } btn;
+ mutable QAbstractButton *btns[QWizard::NButtons];
+ };
+ QWizardAntiFlickerWidget *antiFlickerWidget;
+ QWidget *placeholderWidget1;
+ QWidget *placeholderWidget2;
+ QWizardHeader *headerWidget;
+ QLabel *watermarkLabel;
+ QFrame *pageFrame;
+ QLabel *titleLabel;
+ QLabel *subTitleLabel;
+ QWizardRuler *bottomRuler;
+
+ QVBoxLayout *pageVBoxLayout;
+ QHBoxLayout *buttonLayout;
+ QGridLayout *mainLayout;
+
+#if !defined(QT_NO_STYLE_WINDOWSVISTA)
+ QVistaHelper *vistaHelper;
+ bool vistaInitPending;
+ QVistaHelper::VistaState vistaState;
+ bool vistaStateChanged;
+ bool inHandleAeroStyleChange;
+#endif
+ int minimumWidth;
+ int minimumHeight;
+ int maximumWidth;
+ int maximumHeight;
+};
+
+static QString buttonDefaultText(int wstyle, int which, const QWizardPrivate *wizardPrivate)
+{
+#if defined(QT_NO_STYLE_WINDOWSVISTA)
+ Q_UNUSED(wizardPrivate);
+#endif
+ const bool macStyle = (wstyle == QWizard::MacStyle);
+ switch (which) {
+ case QWizard::BackButton:
+ return macStyle ? QWizard::tr("Go Back") : QWizard::tr("< &Back");
+ case QWizard::NextButton:
+ if (macStyle)
+ return QWizard::tr("Continue");
+ else
+ return wizardPrivate->isVistaThemeEnabled()
+ ? QWizard::tr("&Next") : QWizard::tr("&Next >");
+ case QWizard::CommitButton:
+ return QWizard::tr("Commit");
+ case QWizard::FinishButton:
+ return macStyle ? QWizard::tr("Done") : QWizard::tr("&Finish");
+ case QWizard::CancelButton:
+ return QWizard::tr("Cancel");
+ case QWizard::HelpButton:
+ return macStyle ? QWizard::tr("Help") : QWizard::tr("&Help");
+ default:
+ return QString();
+ }
+}
+
+void QWizardPrivate::init()
+{
+ Q_Q(QWizard);
+
+ antiFlickerWidget = new QWizardAntiFlickerWidget(q, this);
+ wizStyle = QWizard::WizardStyle(q->style()->styleHint(QStyle::SH_WizardStyle, 0, q));
+ if (wizStyle == QWizard::MacStyle) {
+ opts = (QWizard::NoDefaultButton | QWizard::NoCancelButton);
+ } else if (wizStyle == QWizard::ModernStyle) {
+ opts = QWizard::HelpButtonOnRight;
+ }
+
+#if !defined(QT_NO_STYLE_WINDOWSVISTA)
+ vistaHelper = new QVistaHelper(q);
+#endif
+
+ // create these buttons right away; create the other buttons as necessary
+ ensureButton(QWizard::BackButton);
+ ensureButton(QWizard::NextButton);
+ ensureButton(QWizard::CommitButton);
+ ensureButton(QWizard::FinishButton);
+
+ pageFrame = new QFrame(antiFlickerWidget);
+ pageFrame->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
+
+ pageVBoxLayout = new QVBoxLayout(pageFrame);
+ pageVBoxLayout->setSpacing(0);
+ pageVBoxLayout->addSpacing(0);
+ QSpacerItem *spacerItem = new QSpacerItem(0, 0, QSizePolicy::Ignored, QSizePolicy::MinimumExpanding);
+ pageVBoxLayout->addItem(spacerItem);
+
+ buttonLayout = new QHBoxLayout;
+ mainLayout = new QGridLayout(antiFlickerWidget);
+ mainLayout->setSizeConstraint(QLayout::SetNoConstraint);
+
+ updateButtonLayout();
+
+ for (int i = 0; i < NFallbackDefaultProperties; ++i)
+ defaultPropertyTable.append(QWizardDefaultProperty(fallbackProperties[i].className,
+ fallbackProperties[i].property,
+ fallbackProperties[i].changedSignal));
+}
+
+void QWizardPrivate::reset()
+{
+ Q_Q(QWizard);
+ if (current != -1) {
+ q->currentPage()->hide();
+ cleanupPagesNotInHistory();
+ for (int i = history.count() - 1; i >= 0; --i)
+ q->cleanupPage(history.at(i));
+ history.clear();
+ initialized.clear();
+
+ current = -1;
+ emit q->currentIdChanged(-1);
+ }
+}
+
+void QWizardPrivate::cleanupPagesNotInHistory()
+{
+ Q_Q(QWizard);
+
+ const QSet<int> original = initialized;
+ QSet<int>::const_iterator i = original.constBegin();
+ QSet<int>::const_iterator end = original.constEnd();
+
+ for (; i != end; ++i) {
+ if (!history.contains(*i)) {
+ q->cleanupPage(*i);
+ initialized.remove(*i);
+ }
+ }
+}
+
+void QWizardPrivate::addField(const QWizardField &field)
+{
+ QWizardField myField = field;
+ myField.resolve(defaultPropertyTable);
+
+ if (fieldIndexMap.contains(myField.name)) {
+ qWarning("QWizardPage::addField: Duplicate field '%s'", qPrintable(myField.name));
+ return;
+ }
+
+ fieldIndexMap.insert(myField.name, fields.count());
+ fields += myField;
+ if (myField.mandatory && !myField.changedSignal.isEmpty())
+ QObject::connect(myField.object, myField.changedSignal,
+ myField.page, SLOT(_q_maybeEmitCompleteChanged()));
+}
+
+void QWizardPrivate::removeFieldAt(int index)
+{
+ const QWizardField &field = fields.at(index);
+ fieldIndexMap.remove(field.name);
+ if (field.mandatory && !field.changedSignal.isEmpty())
+ QObject::disconnect(field.object, field.changedSignal,
+ field.page, SLOT(_q_maybeEmitCompleteChanged()));
+ fields.remove(index);
+}
+
+void QWizardPrivate::switchToPage(int newId, Direction direction)
+{
+ Q_Q(QWizard);
+
+ disableUpdates();
+
+ int oldId = current;
+ if (QWizardPage *oldPage = q->currentPage()) {
+ oldPage->hide();
+
+ if (direction == Backward) {
+ if (!(opts & QWizard::IndependentPages)) {
+ q->cleanupPage(oldId);
+ initialized.remove(oldId);
+ }
+ Q_ASSERT(history.last() == oldId);
+ history.removeLast();
+ Q_ASSERT(history.last() == newId);
+ }
+ }
+
+ current = newId;
+
+ QWizardPage *newPage = q->currentPage();
+ if (newPage) {
+ if (direction == Forward) {
+ if (!initialized.contains(current)) {
+ initialized.insert(current);
+ q->initializePage(current);
+ }
+ history.append(current);
+ }
+ newPage->show();
+ }
+
+ canContinue = (q->nextId() != -1);
+ canFinish = (newPage && newPage->isFinalPage());
+
+ _q_updateButtonStates();
+ updateButtonTexts();
+
+ const QWizard::WizardButton nextOrCommit =
+ newPage && newPage->isCommitPage() ? QWizard::CommitButton : QWizard::NextButton;
+ QAbstractButton *nextOrFinishButton =
+ btns[canContinue ? nextOrCommit : QWizard::FinishButton];
+ QWidget *candidate = 0;
+
+ /*
+ If there is no default button and the Next or Finish button
+ is enabled, give focus directly to it as a convenience to the
+ user. This is the normal case on Mac OS X.
+
+ Otherwise, give the focus to the new page's first child that
+ can handle it. If there is no such child, give the focus to
+ Next or Finish.
+ */
+ if ((opts & QWizard::NoDefaultButton) && nextOrFinishButton->isEnabled()) {
+ candidate = nextOrFinishButton;
+ } else if (newPage) {
+ candidate = iWantTheFocus(newPage);
+ }
+ if (!candidate)
+ candidate = nextOrFinishButton;
+ candidate->setFocus();
+
+ if (wizStyle == QWizard::MacStyle)
+ q->updateGeometry();
+
+ enableUpdates();
+ updateLayout();
+
+ emit q->currentIdChanged(current);
+}
+
+// keep in sync with QWizard::WizardButton
+static const char * const buttonSlots[QWizard::NStandardButtons] = {
+ SLOT(back()), SLOT(next()), SLOT(next()), SLOT(accept()), SLOT(reject()),
+ SIGNAL(helpRequested())
+};
+
+QWizardLayoutInfo QWizardPrivate::layoutInfoForCurrentPage()
+{
+ Q_Q(QWizard);
+ QStyle *style = q->style();
+
+ QWizardLayoutInfo info;
+
+ const int layoutHorizontalSpacing = style->pixelMetric(QStyle::PM_LayoutHorizontalSpacing);
+ info.topLevelMarginLeft = style->pixelMetric(QStyle::PM_LayoutLeftMargin, 0, q);
+ info.topLevelMarginRight = style->pixelMetric(QStyle::PM_LayoutRightMargin, 0, q);
+ info.topLevelMarginTop = style->pixelMetric(QStyle::PM_LayoutTopMargin, 0, q);
+ info.topLevelMarginBottom = style->pixelMetric(QStyle::PM_LayoutBottomMargin, 0, q);
+ info.childMarginLeft = style->pixelMetric(QStyle::PM_LayoutLeftMargin, 0, titleLabel);
+ info.childMarginRight = style->pixelMetric(QStyle::PM_LayoutRightMargin, 0, titleLabel);
+ info.childMarginTop = style->pixelMetric(QStyle::PM_LayoutTopMargin, 0, titleLabel);
+ info.childMarginBottom = style->pixelMetric(QStyle::PM_LayoutBottomMargin, 0, titleLabel);
+ info.hspacing = (layoutHorizontalSpacing == -1)
+ ? style->layoutSpacing(QSizePolicy::DefaultType, QSizePolicy::DefaultType, Qt::Horizontal)
+ : layoutHorizontalSpacing;
+ info.vspacing = style->pixelMetric(QStyle::PM_LayoutVerticalSpacing);
+ info.buttonSpacing = (layoutHorizontalSpacing == -1)
+ ? style->layoutSpacing(QSizePolicy::PushButton, QSizePolicy::PushButton, Qt::Horizontal)
+ : layoutHorizontalSpacing;
+
+ if (wizStyle == QWizard::MacStyle)
+ info.buttonSpacing = 12;
+
+ info.wizStyle = wizStyle;
+ if ((info.wizStyle == QWizard::AeroStyle)
+#if !defined(QT_NO_STYLE_WINDOWSVISTA)
+ && (QVistaHelper::vistaState() == QVistaHelper::Classic || vistaDisabled())
+#endif
+ )
+ info.wizStyle = QWizard::ModernStyle;
+
+ QString titleText;
+ QString subTitleText;
+ QPixmap backgroundPixmap;
+ QPixmap watermarkPixmap;
+
+ if (QWizardPage *page = q->currentPage()) {
+ titleText = page->title();
+ subTitleText = page->subTitle();
+ backgroundPixmap = page->pixmap(QWizard::BackgroundPixmap);
+ watermarkPixmap = page->pixmap(QWizard::WatermarkPixmap);
+ }
+
+ info.header = (info.wizStyle == QWizard::ClassicStyle || info.wizStyle == QWizard::ModernStyle)
+ && !(opts & QWizard::IgnoreSubTitles) && !subTitleText.isEmpty();
+ info.watermark = (info.wizStyle != QWizard::MacStyle) && (info.wizStyle != QWizard::AeroStyle)
+ && !watermarkPixmap.isNull();
+ info.title = !info.header && !titleText.isEmpty();
+ info.subTitle = !(opts & QWizard::IgnoreSubTitles) && !info.header && !subTitleText.isEmpty();
+ info.extension = info.watermark && (opts & QWizard::ExtendedWatermarkPixmap);
+
+ return info;
+}
+
+void QWizardPrivate::recreateLayout(const QWizardLayoutInfo &info)
+{
+ Q_Q(QWizard);
+
+ /*
+ Start by undoing the main layout.
+ */
+ for (int i = mainLayout->count() - 1; i >= 0; --i) {
+ QLayoutItem *item = mainLayout->takeAt(i);
+ if (item->layout()) {
+ item->layout()->setParent(0);
+ } else {
+ delete item;
+ }
+ }
+ for (int i = mainLayout->columnCount() - 1; i >= 0; --i)
+ mainLayout->setColumnMinimumWidth(i, 0);
+ for (int i = mainLayout->rowCount() - 1; i >= 0; --i)
+ mainLayout->setRowMinimumHeight(i, 0);
+
+ /*
+ Now, recreate it.
+ */
+
+ bool mac = (info.wizStyle == QWizard::MacStyle);
+ bool classic = (info.wizStyle == QWizard::ClassicStyle);
+ bool modern = (info.wizStyle == QWizard::ModernStyle);
+ bool aero = (info.wizStyle == QWizard::AeroStyle);
+ int deltaMarginLeft = info.topLevelMarginLeft - info.childMarginLeft;
+ int deltaMarginRight = info.topLevelMarginRight - info.childMarginRight;
+ int deltaMarginTop = info.topLevelMarginTop - info.childMarginTop;
+ int deltaMarginBottom = info.topLevelMarginBottom - info.childMarginBottom;
+ int deltaVSpacing = info.topLevelMarginBottom - info.vspacing;
+
+ int row = 0;
+ int numColumns;
+ if (mac) {
+ numColumns = 3;
+ } else if (info.watermark) {
+ numColumns = 2;
+ } else {
+ numColumns = 1;
+ }
+ int pageColumn = qMin(1, numColumns - 1);
+
+ if (mac) {
+ mainLayout->setMargin(0);
+ mainLayout->setSpacing(0);
+ buttonLayout->setContentsMargins(MacLayoutLeftMargin, MacButtonTopMargin, MacLayoutRightMargin, MacLayoutBottomMargin);
+ pageVBoxLayout->setMargin(7);
+ } else {
+ if (modern) {
+ mainLayout->setMargin(0);
+ mainLayout->setSpacing(0);
+ pageVBoxLayout->setContentsMargins(deltaMarginLeft, deltaMarginTop,
+ deltaMarginRight, deltaMarginBottom);
+ buttonLayout->setContentsMargins(info.topLevelMarginLeft, info.topLevelMarginTop,
+ info.topLevelMarginRight, info.topLevelMarginBottom);
+ } else {
+ mainLayout->setContentsMargins(info.topLevelMarginLeft, info.topLevelMarginTop,
+ info.topLevelMarginRight, info.topLevelMarginBottom);
+ mainLayout->setHorizontalSpacing(info.hspacing);
+ mainLayout->setVerticalSpacing(info.vspacing);
+ pageVBoxLayout->setContentsMargins(0, 0, 0, 0);
+ buttonLayout->setContentsMargins(0, 0, 0, 0);
+ }
+ }
+ buttonLayout->setSpacing(info.buttonSpacing);
+
+ if (info.header) {
+ if (!headerWidget)
+ headerWidget = new QWizardHeader(antiFlickerWidget);
+ headerWidget->setAutoFillBackground(modern);
+ mainLayout->addWidget(headerWidget, row++, 0, 1, numColumns);
+ }
+ if (headerWidget)
+ headerWidget->setVisible(info.header);
+
+ int watermarkStartRow = row;
+
+ if (mac)
+ mainLayout->setRowMinimumHeight(row++, 10);
+
+ if (info.title) {
+ if (!titleLabel) {
+ titleLabel = new QLabel(antiFlickerWidget);
+ titleLabel->setBackgroundRole(QPalette::Base);
+ titleLabel->setWordWrap(true);
+ }
+
+ QFont titleFont = q->font();
+ titleFont.setPointSize(titleFont.pointSize() + (mac ? 3 : 4));
+ titleFont.setBold(true);
+ titleLabel->setPalette(QPalette());
+
+ if (aero) {
+ // ### hardcoded for now:
+ titleFont = QFont(QLatin1String("Segoe UI"), 12);
+ QPalette pal(titleLabel->palette());
+ pal.setColor(QPalette::Text, "#003399");
+ titleLabel->setPalette(pal);
+ }
+
+ titleLabel->setFont(titleFont);
+ const int aeroTitleIndent = 25; // ### hardcoded for now - should be calculated somehow
+ if (aero)
+ titleLabel->setIndent(aeroTitleIndent);
+ else if (mac)
+ titleLabel->setIndent(2);
+ else if (classic)
+ titleLabel->setIndent(info.childMarginLeft);
+ else
+ titleLabel->setIndent(info.topLevelMarginLeft);
+ if (modern) {
+ if (!placeholderWidget1) {
+ placeholderWidget1 = new QWidget(antiFlickerWidget);
+ placeholderWidget1->setBackgroundRole(QPalette::Base);
+ }
+ placeholderWidget1->setFixedHeight(info.topLevelMarginLeft + 2);
+ mainLayout->addWidget(placeholderWidget1, row++, pageColumn);
+ }
+ mainLayout->addWidget(titleLabel, row++, pageColumn);
+ if (modern) {
+ if (!placeholderWidget2) {
+ placeholderWidget2 = new QWidget(antiFlickerWidget);
+ placeholderWidget2->setBackgroundRole(QPalette::Base);
+ }
+ placeholderWidget2->setFixedHeight(5);
+ mainLayout->addWidget(placeholderWidget2, row++, pageColumn);
+ }
+ if (mac)
+ mainLayout->setRowMinimumHeight(row++, 7);
+ }
+ if (placeholderWidget1)
+ placeholderWidget1->setVisible(info.title && modern);
+ if (placeholderWidget2)
+ placeholderWidget2->setVisible(info.title && modern);
+
+ if (info.subTitle) {
+ if (!subTitleLabel) {
+ subTitleLabel = new QLabel(pageFrame);
+ subTitleLabel->setWordWrap(true);
+
+ subTitleLabel->setContentsMargins(info.childMarginLeft , 0,
+ info.childMarginRight , 0);
+
+ pageVBoxLayout->insertWidget(1, subTitleLabel);
+ }
+ }
+
+ // ### try to replace with margin.
+ changeSpacerSize(pageVBoxLayout, 0, 0, info.subTitle ? info.childMarginLeft : 0);
+
+ int hMargin = mac ? 1 : 0;
+ int vMargin = hMargin;
+
+ pageFrame->setFrameStyle(mac ? (QFrame::Box | QFrame::Raised) : QFrame::NoFrame);
+ pageFrame->setLineWidth(0);
+ pageFrame->setMidLineWidth(hMargin);
+
+ if (info.header) {
+ if (modern) {
+ hMargin = info.topLevelMarginLeft;
+ vMargin = deltaMarginBottom;
+ } else if (classic) {
+ hMargin = deltaMarginLeft + ClassicHMargin;
+ vMargin = 0;
+ }
+ }
+
+ if (aero) {
+ int leftMargin = 18; // ### hardcoded for now - should be calculated somehow
+ int topMargin = vMargin;
+ int rightMargin = hMargin; // ### for now
+ int bottomMargin = vMargin;
+ pageFrame->setContentsMargins(leftMargin, topMargin, rightMargin, bottomMargin);
+ } else {
+ pageFrame->setContentsMargins(hMargin, vMargin, hMargin, vMargin);
+ }
+
+ if (info.watermark && !watermarkLabel) {
+ watermarkLabel = new QLabel(antiFlickerWidget);
+ watermarkLabel->setBackgroundRole(QPalette::Base);
+ watermarkLabel->setMinimumHeight(1);
+ watermarkLabel->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Expanding);
+ watermarkLabel->setAlignment(Qt::AlignLeft | Qt::AlignTop);
+ }
+
+ //bool wasSemiTransparent = pageFrame->testAttribute(Qt::WA_SetPalette);
+ const bool wasSemiTransparent =
+ pageFrame->palette().brush(QPalette::Window).color().alpha() < 255
+ || pageFrame->palette().brush(QPalette::Base).color().alpha() < 255;
+ if (mac) {
+ if (!wasSemiTransparent) {
+ QPalette pal = pageFrame->palette();
+ pal.setBrush(QPalette::Window, QColor(255, 255, 255, 153));
+ // ### The next line is required to ensure visual semitransparency when
+ // ### switching from ModernStyle to MacStyle. See TAG1 below.
+ pal.setBrush(QPalette::Base, QColor(255, 255, 255, 153));
+ pageFrame->setPalette(pal);
+ pageFrame->setAutoFillBackground(true);
+ antiFlickerWidget->setAutoFillBackground(false);
+ }
+ } else {
+ if (wasSemiTransparent)
+ pageFrame->setPalette(QPalette());
+
+ bool baseBackground = (modern && !info.header); // ### TAG1
+ pageFrame->setBackgroundRole(baseBackground ? QPalette::Base : QPalette::Window);
+
+ if (titleLabel)
+ titleLabel->setAutoFillBackground(baseBackground);
+ pageFrame->setAutoFillBackground(baseBackground);
+ if (watermarkLabel)
+ watermarkLabel->setAutoFillBackground(baseBackground);
+ if (placeholderWidget1)
+ placeholderWidget1->setAutoFillBackground(baseBackground);
+ if (placeholderWidget2)
+ placeholderWidget2->setAutoFillBackground(baseBackground);
+
+ if (aero) {
+ QPalette pal = pageFrame->palette();
+ pal.setBrush(QPalette::Window, QColor(255, 255, 255));
+ pageFrame->setPalette(pal);
+ pageFrame->setAutoFillBackground(true);
+ pal = antiFlickerWidget->palette();
+ pal.setBrush(QPalette::Window, QColor(255, 255, 255));
+ antiFlickerWidget->setPalette(pal);
+ antiFlickerWidget->setAutoFillBackground(true);
+ }
+ }
+
+ mainLayout->addWidget(pageFrame, row++, pageColumn);
+
+ int watermarkEndRow = row;
+ if (classic)
+ mainLayout->setRowMinimumHeight(row++, deltaVSpacing);
+
+ if (aero) {
+ buttonLayout->setContentsMargins(9, 9, 9, 9);
+ mainLayout->setContentsMargins(0, 11, 0, 0);
+ }
+
+ int buttonStartColumn = info.extension ? 1 : 0;
+ int buttonNumColumns = info.extension ? 1 : numColumns;
+
+ if (classic || modern) {
+ if (!bottomRuler)
+ bottomRuler = new QWizardRuler(antiFlickerWidget);
+ mainLayout->addWidget(bottomRuler, row++, buttonStartColumn, 1, buttonNumColumns);
+ }
+
+ if (classic)
+ mainLayout->setRowMinimumHeight(row++, deltaVSpacing);
+
+ mainLayout->addLayout(buttonLayout, row++, buttonStartColumn, 1, buttonNumColumns);
+
+ if (info.watermark) {
+ if (info.extension)
+ watermarkEndRow = row;
+ mainLayout->addWidget(watermarkLabel, watermarkStartRow, 0,
+ watermarkEndRow - watermarkStartRow, 1);
+ }
+
+ mainLayout->setColumnMinimumWidth(0, mac && !info.watermark ? 181 : 0);
+ if (mac)
+ mainLayout->setColumnMinimumWidth(2, 21);
+
+ if (headerWidget)
+ headerWidget->setVisible(info.header);
+ if (titleLabel)
+ titleLabel->setVisible(info.title);
+ if (subTitleLabel)
+ subTitleLabel->setVisible(info.subTitle);
+ if (bottomRuler)
+ bottomRuler->setVisible(classic || modern);
+ if (watermarkLabel)
+ watermarkLabel->setVisible(info.watermark);
+
+ layoutInfo = info;
+}
+
+void QWizardPrivate::updateLayout()
+{
+ Q_Q(QWizard);
+
+ disableUpdates();
+
+ QWizardLayoutInfo info = layoutInfoForCurrentPage();
+ if (layoutInfo != info)
+ recreateLayout(info);
+ QWizardPage *page = q->currentPage();
+
+ // If the page can expand vertically, let it stretch "infinitely" more
+ // than the QSpacerItem at the bottom. Otherwise, let the QSpacerItem
+ // stretch "infinitely" more than the page. Change the bottom item's
+ // policy accordingly. The case that the page has no layout is basically
+ // for Designer, only.
+ if (page) {
+ bool expandPage = !page->layout();
+ if (!expandPage) {
+ const QLayoutItem *pageItem = pageVBoxLayout->itemAt(pageVBoxLayout->indexOf(page));
+ expandPage = pageItem->expandingDirections() & Qt::Vertical;
+ }
+ QSpacerItem *bottomSpacer = pageVBoxLayout->itemAt(pageVBoxLayout->count() - 1)->spacerItem();
+ Q_ASSERT(bottomSpacer);
+ bottomSpacer->changeSize(0, 0, QSizePolicy::Ignored, expandPage ? QSizePolicy::Ignored : QSizePolicy::MinimumExpanding);
+ pageVBoxLayout->invalidate();
+ }
+
+ if (info.header) {
+ Q_ASSERT(page);
+ headerWidget->setup(info, page->title(), page->subTitle(),
+ page->pixmap(QWizard::LogoPixmap), page->pixmap(QWizard::BannerPixmap),
+ titleFmt, subTitleFmt);
+ }
+
+ if (info.watermark) {
+ Q_ASSERT(page);
+ watermarkLabel->setPixmap(page->pixmap(QWizard::WatermarkPixmap));
+ }
+ if (info.title) {
+ Q_ASSERT(page);
+ titleLabel->setTextFormat(titleFmt);
+ titleLabel->setText(page->title());
+ }
+ if (info.subTitle) {
+ Q_ASSERT(page);
+ subTitleLabel->setTextFormat(subTitleFmt);
+ subTitleLabel->setText(page->subTitle());
+ }
+
+ enableUpdates();
+ updateMinMaxSizes(info);
+}
+
+void QWizardPrivate::updateMinMaxSizes(const QWizardLayoutInfo &info)
+{
+ Q_Q(QWizard);
+
+ int extraHeight = 0;
+#if !defined(QT_NO_STYLE_WINDOWSVISTA)
+ if (isVistaThemeEnabled())
+ extraHeight = vistaHelper->titleBarSize() + vistaHelper->topOffset();
+#endif
+ QSize minimumSize = mainLayout->totalMinimumSize() + QSize(0, extraHeight);
+ QSize maximumSize;
+#if defined(Q_WS_WIN)
+ if (QSysInfo::WindowsVersion > QSysInfo::WV_Me) // ### See Tasks 164078 and 161660
+#endif
+ maximumSize = mainLayout->totalMaximumSize();
+ if (info.header && headerWidget->maximumWidth() != QWIDGETSIZE_MAX) {
+ minimumSize.setWidth(headerWidget->maximumWidth());
+ maximumSize.setWidth(headerWidget->maximumWidth());
+ }
+ if (info.watermark) {
+ minimumSize.setHeight(mainLayout->totalSizeHint().height());
+ maximumSize.setHeight(mainLayout->totalSizeHint().height());
+ }
+ if (q->minimumWidth() == minimumWidth) {
+ minimumWidth = minimumSize.width();
+ q->setMinimumWidth(minimumWidth);
+ }
+ if (q->minimumHeight() == minimumHeight) {
+ minimumHeight = minimumSize.height();
+ q->setMinimumHeight(minimumHeight);
+ }
+ if (q->maximumWidth() == maximumWidth) {
+ maximumWidth = maximumSize.width();
+ q->setMaximumWidth(maximumWidth);
+ }
+ if (q->maximumHeight() == maximumHeight) {
+ maximumHeight = maximumSize.height();
+ q->setMaximumHeight(maximumHeight);
+ }
+}
+
+void QWizardPrivate::updateCurrentPage()
+{
+ Q_Q(QWizard);
+ if (q->currentPage()) {
+ canContinue = (q->nextId() != -1);
+ canFinish = q->currentPage()->isFinalPage();
+ } else {
+ canContinue = false;
+ canFinish = false;
+ }
+ _q_updateButtonStates();
+ updateButtonTexts();
+}
+
+bool QWizardPrivate::ensureButton(QWizard::WizardButton which) const
+{
+ Q_Q(const QWizard);
+ if (uint(which) >= QWizard::NButtons)
+ return false;
+
+ if (!btns[which]) {
+ QPushButton *pushButton = new QPushButton(antiFlickerWidget);
+ QStyle *style = q->style();
+ if (style != QApplication::style()) // Propagate style
+ pushButton->setStyle(style);
+ // Make navigation buttons detectable as passive interactor in designer
+ switch (which) {
+ case QWizard::CommitButton:
+ case QWizard::FinishButton:
+ case QWizard::CancelButton:
+ break;
+ default: {
+ QString objectName = QLatin1String("__qt__passive_wizardbutton");
+ objectName += QString::number(which);
+ pushButton->setObjectName(objectName);
+ }
+ break;
+ }
+#ifdef Q_WS_MAC
+ pushButton->setAutoDefault(false);
+#endif
+ pushButton->hide();
+#ifdef Q_CC_HPACC
+ const_cast<QWizardPrivate *>(this)->btns[which] = pushButton;
+#else
+ btns[which] = pushButton;
+#endif
+ if (which < QWizard::NStandardButtons)
+ pushButton->setText(buttonDefaultText(wizStyle, which, this));
+ connectButton(which);
+ }
+ return true;
+}
+
+void QWizardPrivate::connectButton(QWizard::WizardButton which) const
+{
+ Q_Q(const QWizard);
+ if (which < QWizard::NStandardButtons) {
+ QObject::connect(btns[which], SIGNAL(clicked()), q, buttonSlots[which]);
+ } else {
+ QObject::connect(btns[which], SIGNAL(clicked()), q, SLOT(_q_emitCustomButtonClicked()));
+ }
+}
+
+void QWizardPrivate::updateButtonTexts()
+{
+ Q_Q(QWizard);
+ for (int i = 0; i < QWizard::NButtons; ++i) {
+ if (btns[i]) {
+ if (q->currentPage() && (q->currentPage()->d_func()->buttonCustomTexts.contains(i)))
+ btns[i]->setText(q->currentPage()->d_func()->buttonCustomTexts.value(i));
+ else if (buttonCustomTexts.contains(i))
+ btns[i]->setText(buttonCustomTexts.value(i));
+ else if (i < QWizard::NStandardButtons)
+ btns[i]->setText(buttonDefaultText(wizStyle, i, this));
+ }
+ }
+}
+
+void QWizardPrivate::updateButtonLayout()
+{
+ if (buttonsHaveCustomLayout) {
+ QVarLengthArray<QWizard::WizardButton> array(buttonsCustomLayout.count());
+ for (int i = 0; i < buttonsCustomLayout.count(); ++i)
+ array[i] = buttonsCustomLayout.at(i);
+ setButtonLayout(array.constData(), array.count());
+ } else {
+ // Positions:
+ // Help Stretch Custom1 Custom2 Custom3 Cancel Back Next Commit Finish Cancel Help
+
+ const int ArraySize = 12;
+ QWizard::WizardButton array[ArraySize];
+ memset(array, -1, sizeof(array));
+ Q_ASSERT(array[0] == QWizard::NoButton);
+
+ if (opts & QWizard::HaveHelpButton) {
+ int i = (opts & QWizard::HelpButtonOnRight) ? 11 : 0;
+ array[i] = QWizard::HelpButton;
+ }
+ array[1] = QWizard::Stretch;
+ if (opts & QWizard::HaveCustomButton1)
+ array[2] = QWizard::CustomButton1;
+ if (opts & QWizard::HaveCustomButton2)
+ array[3] = QWizard::CustomButton2;
+ if (opts & QWizard::HaveCustomButton3)
+ array[4] = QWizard::CustomButton3;
+
+ if (!(opts & QWizard::NoCancelButton)) {
+ int i = (opts & QWizard::CancelButtonOnLeft) ? 5 : 10;
+ array[i] = QWizard::CancelButton;
+ }
+ array[6] = QWizard::BackButton;
+ array[7] = QWizard::NextButton;
+ array[8] = QWizard::CommitButton;
+ array[9] = QWizard::FinishButton;
+
+ setButtonLayout(array, ArraySize);
+ }
+}
+
+void QWizardPrivate::setButtonLayout(const QWizard::WizardButton *array, int size)
+{
+ QWidget *prev = pageFrame;
+
+ for (int i = buttonLayout->count() - 1; i >= 0; --i) {
+ QLayoutItem *item = buttonLayout->takeAt(i);
+ if (QWidget *widget = item->widget())
+ widget->hide();
+ delete item;
+ }
+
+ for (int i = 0; i < size; ++i) {
+ QWizard::WizardButton which = array[i];
+ if (which == QWizard::Stretch) {
+ buttonLayout->addStretch(1);
+ } else if (which != QWizard::NoButton) {
+ ensureButton(which);
+ buttonLayout->addWidget(btns[which]);
+
+ // Back, Next, Commit, and Finish are handled in _q_updateButtonStates()
+ if (which != QWizard::BackButton && which != QWizard::NextButton
+ && which != QWizard::CommitButton && which != QWizard::FinishButton)
+ btns[which]->show();
+
+ if (prev)
+ QWidget::setTabOrder(prev, btns[which]);
+ prev = btns[which];
+ }
+ }
+
+ _q_updateButtonStates();
+}
+
+bool QWizardPrivate::buttonLayoutContains(QWizard::WizardButton which)
+{
+ return !buttonsHaveCustomLayout || buttonsCustomLayout.contains(which);
+}
+
+void QWizardPrivate::updatePixmap(QWizard::WizardPixmap which)
+{
+ Q_Q(QWizard);
+ if (which == QWizard::BackgroundPixmap) {
+ if (wizStyle == QWizard::MacStyle) {
+ q->update();
+ q->updateGeometry();
+ }
+ } else {
+ updateLayout();
+ }
+}
+
+#if !defined(QT_NO_STYLE_WINDOWSVISTA)
+bool QWizardPrivate::vistaDisabled() const
+{
+ Q_Q(const QWizard);
+ const QVariant v = q->property("_q_wizard_vista_off");
+ return v.isValid() && v.toBool();
+}
+
+bool QWizardPrivate::isVistaThemeEnabled(QVistaHelper::VistaState state) const
+{
+ return wizStyle == QWizard::AeroStyle
+ && QVistaHelper::vistaState() == state
+ && !vistaDisabled();
+}
+
+void QWizardPrivate::handleAeroStyleChange()
+{
+ Q_Q(QWizard);
+
+ if (inHandleAeroStyleChange)
+ return; // prevent recursion
+ inHandleAeroStyleChange = true;
+
+ vistaHelper->backButton()->disconnect();
+ q->removeEventFilter(vistaHelper);
+
+ if (isVistaThemeEnabled()) {
+ if (isVistaThemeEnabled(QVistaHelper::VistaAero)) {
+ vistaHelper->setDWMTitleBar(QVistaHelper::ExtendedTitleBar);
+ q->installEventFilter(vistaHelper);
+ q->setMouseTracking(true);
+ antiFlickerWidget->move(0, vistaHelper->titleBarSize() + vistaHelper->topOffset());
+ vistaHelper->backButton()->move(
+ 0, vistaHelper->topOffset() // ### should ideally work without the '+ 1'
+ - qMin(vistaHelper->topOffset(), vistaHelper->topPadding() + 1));
+ } else {
+ vistaHelper->setDWMTitleBar(QVistaHelper::NormalTitleBar);
+ q->setMouseTracking(true);
+ antiFlickerWidget->move(0, vistaHelper->topOffset());
+ vistaHelper->backButton()->move(0, -1); // ### should ideally work with (0, 0)
+ }
+ vistaHelper->setTitleBarIconAndCaptionVisible(false);
+ QObject::connect(
+ vistaHelper->backButton(), SIGNAL(clicked()), q, buttonSlots[QWizard::BackButton]);
+ vistaHelper->backButton()->show();
+ } else {
+ q->setMouseTracking(true); // ### original value possibly different
+ q->unsetCursor(); // ### ditto
+ antiFlickerWidget->move(0, 0);
+ vistaHelper->backButton()->hide();
+ vistaHelper->setTitleBarIconAndCaptionVisible(true);
+ }
+
+ _q_updateButtonStates();
+
+ if (q->isVisible())
+ vistaHelper->setWindowPosHack();
+
+ inHandleAeroStyleChange = false;
+}
+#endif
+
+bool QWizardPrivate::isVistaThemeEnabled() const
+{
+#if !defined(QT_NO_STYLE_WINDOWSVISTA)
+ return isVistaThemeEnabled(QVistaHelper::VistaAero)
+ || isVistaThemeEnabled(QVistaHelper::VistaBasic);
+#else
+ return false;
+#endif
+}
+
+void QWizardPrivate::disableUpdates()
+{
+ Q_Q(QWizard);
+ if (disableUpdatesCount++ == 0) {
+ q->setUpdatesEnabled(false);
+ antiFlickerWidget->hide();
+ }
+}
+
+void QWizardPrivate::enableUpdates()
+{
+ Q_Q(QWizard);
+ if (--disableUpdatesCount == 0) {
+ antiFlickerWidget->show();
+ q->setUpdatesEnabled(true);
+ }
+}
+
+void QWizardPrivate::_q_emitCustomButtonClicked()
+{
+ Q_Q(QWizard);
+ QObject *button = q->sender();
+ for (int i = QWizard::NStandardButtons; i < QWizard::NButtons; ++i) {
+ if (btns[i] == button) {
+ emit q->customButtonClicked(QWizard::WizardButton(i));
+ break;
+ }
+ }
+}
+
+void QWizardPrivate::_q_updateButtonStates()
+{
+ Q_Q(QWizard);
+
+ disableUpdates();
+
+ const QWizardPage *page = q->currentPage();
+ bool complete = page && page->isComplete();
+
+ btn.back->setEnabled(history.count() > 1
+ && !q->page(history.at(history.count() - 2))->isCommitPage()
+ && (!canFinish || !(opts & QWizard::DisabledBackButtonOnLastPage)));
+ btn.next->setEnabled(canContinue && complete);
+ btn.commit->setEnabled(canContinue && complete);
+ btn.finish->setEnabled(canFinish && complete);
+
+ const bool backButtonVisible = buttonLayoutContains(QWizard::BackButton)
+ && (history.count() > 1 || !(opts & QWizard::NoBackButtonOnStartPage))
+ && (canContinue || !(opts & QWizard::NoBackButtonOnLastPage));
+ bool commitPage = page && page->isCommitPage();
+ btn.back->setVisible(backButtonVisible);
+ btn.next->setVisible(buttonLayoutContains(QWizard::NextButton) && !commitPage
+ && (canContinue || (opts & QWizard::HaveNextButtonOnLastPage)));
+ btn.commit->setVisible(buttonLayoutContains(QWizard::CommitButton) && commitPage
+ && canContinue);
+ btn.finish->setVisible(buttonLayoutContains(QWizard::FinishButton)
+ && (canFinish || (opts & QWizard::HaveFinishButtonOnEarlyPages)));
+
+ bool useDefault = !(opts & QWizard::NoDefaultButton);
+ if (QPushButton *nextPush = qobject_cast<QPushButton *>(btn.next))
+ nextPush->setDefault(canContinue && useDefault && !commitPage);
+ if (QPushButton *commitPush = qobject_cast<QPushButton *>(btn.commit))
+ commitPush->setDefault(canContinue && useDefault && commitPage);
+ if (QPushButton *finishPush = qobject_cast<QPushButton *>(btn.finish))
+ finishPush->setDefault(!canContinue && useDefault);
+
+#if !defined(QT_NO_STYLE_WINDOWSVISTA)
+ if (isVistaThemeEnabled()) {
+ vistaHelper->backButton()->setEnabled(btn.back->isEnabled());
+ vistaHelper->backButton()->setVisible(backButtonVisible);
+ btn.back->setVisible(false);
+ }
+#endif
+
+ enableUpdates();
+}
+
+void QWizardPrivate::setStyle(QStyle *style)
+{
+ for (int i = 0; i < QWizard::NButtons; i++)
+ if (btns[i])
+ btns[i]->setStyle(style);
+ const PageMap::const_iterator pcend = pageMap.constEnd();
+ for (PageMap::const_iterator it = pageMap.constBegin(); it != pcend; ++it)
+ it.value()->setStyle(style);
+}
+
+#ifdef Q_WS_MAC
+
+#ifdef Q_WS_MAC32
+QT_BEGIN_INCLUDE_NAMESPACE
+#include <QuickTime/QuickTime.h>
+QT_END_INCLUDE_NAMESPACE
+typedef OSErr (*PtrQTNewDataReferenceFromCFURL)(CFURLRef, UInt32, Handle*, OSType*);
+typedef OSErr (*PtrGetGraphicsImporterForDataRefWithFlags)(Handle, OSType, ComponentInstance*, long);
+typedef ComponentResult (*PtrGraphicsImportSetFlags)(GraphicsImportComponent, long);
+typedef ComponentResult (*PtrGraphicsImportCreateCGImage)(GraphicsImportComponent, CGImageRef*, UInt32);
+
+static PtrQTNewDataReferenceFromCFURL ptrQTNewDataReferenceFromCFURL = 0;
+static PtrGetGraphicsImporterForDataRefWithFlags ptrGetGraphicsImporterForDataRefWithFlags = 0;
+static PtrGraphicsImportSetFlags ptrGraphicsImportSetFlags = 0;
+static PtrGraphicsImportCreateCGImage ptrGraphicsImportCreateCGImage = 0;
+
+static bool resolveQuickTimeSymbols()
+{
+ if (ptrQTNewDataReferenceFromCFURL == 0) {
+ QLibrary library(QLatin1String("/System/Library/Frameworks/QuickTime.framework/QuickTime"));
+ ptrQTNewDataReferenceFromCFURL = reinterpret_cast<PtrQTNewDataReferenceFromCFURL>(library.resolve("QTNewDataReferenceFromCFURL"));
+ ptrGetGraphicsImporterForDataRefWithFlags = reinterpret_cast<PtrGetGraphicsImporterForDataRefWithFlags>(library.resolve("GetGraphicsImporterForDataRefWithFlags"));
+ ptrGraphicsImportSetFlags = reinterpret_cast<PtrGraphicsImportSetFlags>(library.resolve("GraphicsImportSetFlags"));
+ ptrGraphicsImportCreateCGImage = reinterpret_cast<PtrGraphicsImportCreateCGImage>(library.resolve("GraphicsImportCreateCGImage"));
+ }
+
+ return ptrQTNewDataReferenceFromCFURL != 0 && ptrGetGraphicsImporterForDataRefWithFlags != 0
+ && ptrGraphicsImportSetFlags != 0 && ptrGraphicsImportCreateCGImage != 0;
+}
+
+
+static QPixmap quicktimeTiff(const CFURLRef url)
+{
+ if (!resolveQuickTimeSymbols())
+ return QPixmap();
+
+ QCFType <CGImageRef> imageRef = 0;
+ Handle dataRef;
+ OSType dataRefType;
+ GraphicsImportComponent gi;
+ ComponentResult result;
+ result = ptrQTNewDataReferenceFromCFURL(url, 0, &dataRef, &dataRefType);
+ if (dataRef != 0) {
+ OSStatus err = ptrGetGraphicsImporterForDataRefWithFlags(dataRef, dataRefType, &gi, 0);
+ if (err == noErr && gi) {
+ result = ptrGraphicsImportSetFlags(gi, (kGraphicsImporterDontDoGammaCorrection
+ + kGraphicsImporterDontUseColorMatching));
+ if (!result)
+ result = ptrGraphicsImportCreateCGImage(gi, &imageRef, 0);
+ if (result)
+ qWarning("Qt: Problem reading TIFF image %ld(%s:%d)", result, __FILE__, __LINE__);
+ DisposeHandle(dataRef);
+ CloseComponent(gi);
+ }
+ }
+
+ if (imageRef)
+ return QPixmap::fromMacCGImageRef(imageRef);
+ return QPixmap();
+}
+#endif // Q_WS_MAC32
+
+QPixmap QWizardPrivate::findDefaultBackgroundPixmap()
+{
+ QCFType<CFURLRef> url;
+ const int ExpectedImageWidth = 242;
+ const int ExpectedImageHeight = 414;
+ if (LSFindApplicationForInfo(kLSUnknownCreator, CFSTR("com.apple.KeyboardSetupAssistant"),
+ 0, 0, &url) == noErr) {
+ QCFType<CFBundleRef> bundle = CFBundleCreate(kCFAllocatorDefault, url);
+ if (bundle) {
+ url = CFBundleCopyResourceURL(bundle, CFSTR("Background"), CFSTR("tif"), 0);
+ if (url) {
+#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4
+ if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_4) {
+ QCFType<CGImageSourceRef> imageSource = CGImageSourceCreateWithURL(url, 0);
+ QCFType<CGImageRef> image = CGImageSourceCreateImageAtIndex(imageSource, 0, 0);
+ if (image) {
+ int width = CGImageGetWidth(image);
+ int height = CGImageGetHeight(image);
+ if (width == ExpectedImageWidth && height == ExpectedImageHeight)
+ return QPixmap::fromMacCGImageRef(image);
+ }
+ } else
+#endif
+ {
+#ifdef Q_WS_MAC32
+ return quicktimeTiff(url);
+#endif
+ }
+ }
+ }
+ }
+ return QPixmap();
+
+}
+
+#endif
+
+#if !defined(QT_NO_STYLE_WINDOWSVISTA)
+void QWizardAntiFlickerWidget::paintEvent(QPaintEvent *)
+{
+ if (wizardPrivate->isVistaThemeEnabled()) {
+ int leftMargin, topMargin, rightMargin, bottomMargin;
+ wizardPrivate->buttonLayout->getContentsMargins(
+ &leftMargin, &topMargin, &rightMargin, &bottomMargin);
+ const int buttonLayoutTop = wizardPrivate->buttonLayout->contentsRect().top() - topMargin;
+ QPainter painter(this);
+ const QBrush brush(QColor(240, 240, 240)); // ### hardcoded for now
+ painter.fillRect(0, buttonLayoutTop, width(), height() - buttonLayoutTop, brush);
+ painter.setPen(QPen(QBrush(QColor(223, 223, 223)), 0)); // ### hardcoded for now
+ painter.drawLine(0, buttonLayoutTop, width(), buttonLayoutTop);
+ if (wizardPrivate->isVistaThemeEnabled(QVistaHelper::VistaBasic)) {
+ if (window()->isActiveWindow())
+ painter.setPen(QPen(QBrush(QColor(169, 191, 214)), 0)); // ### hardcoded for now
+ else
+ painter.setPen(QPen(QBrush(QColor(182, 193, 204)), 0)); // ### hardcoded for now
+ painter.drawLine(0, 0, width(), 0);
+ }
+ }
+}
+#endif
+
+/*!
+ \class QWizard
+ \since 4.3
+ \brief The QWizard class provides a framework for wizards.
+
+ A wizard (also called an assistant on Mac OS X) is a special type
+ of input dialog that consists of a sequence of pages. A wizard's
+ purpose is to guide the user through a process step by step.
+ Wizards are useful for complex or infrequent tasks that users may
+ find difficult to learn.
+
+ QWizard inherits QDialog and represents a wizard. Each page is a
+ QWizardPage (a QWidget subclass). To create your own wizards, you
+ can use these classes directly, or you can subclass them for more
+ control.
+
+ Topics:
+
+ \tableofcontents
+
+ \section1 A Trivial Example
+
+ The following example illustrates how to create wizard pages and
+ add them to a wizard. For more advanced examples, see
+ \l{dialogs/classwizard}{Class Wizard} and \l{dialogs/licensewizard}{License
+ Wizard}.
+
+ \snippet examples/dialogs/trivialwizard/trivialwizard.cpp 1
+ \snippet examples/dialogs/trivialwizard/trivialwizard.cpp 3
+ \dots
+ \snippet examples/dialogs/trivialwizard/trivialwizard.cpp 4
+ \codeline
+ \snippet examples/dialogs/trivialwizard/trivialwizard.cpp 5
+ \snippet examples/dialogs/trivialwizard/trivialwizard.cpp 7
+ \dots
+ \snippet examples/dialogs/trivialwizard/trivialwizard.cpp 8
+ \codeline
+ \snippet examples/dialogs/trivialwizard/trivialwizard.cpp 10
+
+ \section1 Wizard Look and Feel
+
+ QWizard supports four wizard looks:
+
+ \list
+ \o ClassicStyle
+ \o ModernStyle
+ \o MacStyle
+ \o AeroStyle
+ \endlist
+
+ You can explicitly set the look to use using setWizardStyle()
+ (e.g., if you want the same look on all platforms).
+
+ \table
+ \header \o ClassicStyle
+ \o ModernStyle
+ \o MacStyle
+ \o AeroStyle
+ \row \o \inlineimage qtwizard-classic1.png
+ \o \inlineimage qtwizard-modern1.png
+ \o \inlineimage qtwizard-mac1.png
+ \o \inlineimage qtwizard-aero1.png
+ \row \o \inlineimage qtwizard-classic2.png
+ \o \inlineimage qtwizard-modern2.png
+ \o \inlineimage qtwizard-mac2.png
+ \o \inlineimage qtwizard-aero2.png
+ \endtable
+
+ Note: AeroStyle has effect only on a Windows Vista system with alpha compositing enabled.
+ ModernStyle is used as a fallback when this condition is not met.
+
+ In addition to the wizard style, there are several options that
+ control the look and feel of the wizard. These can be set using
+ setOption() or setOptions(). For example, HaveHelpButton makes
+ QWizard show a \gui Help button along with the other wizard
+ buttons.
+
+ You can even change the order of the wizard buttons to any
+ arbitrary order using setButtonLayout(), and you can add up to
+ three custom buttons (e.g., a \gui Print button) to the button
+ row. This is achieved by calling setButton() or setButtonText()
+ with CustomButton1, CustomButton2, or CustomButton3 to set up the
+ button, and by enabling the HaveCustomButton1, HaveCustomButton2,
+ or HaveCustomButton3 options. Whenever the user clicks a custom
+ button, customButtonClicked() is emitted. For example:
+
+ \snippet examples/dialogs/licensewizard/licensewizard.cpp 29
+
+ \section1 Elements of a Wizard Page
+
+ Wizards consist of a sequence of \l{QWizardPage}s. At any time,
+ only one page is shown. A page has the following attributes:
+
+ \list
+ \o A \l{QWizardPage::}{title}.
+ \o A \l{QWizardPage::}{subTitle}.
+ \o A set of pixmaps, which may or may not be honored, depending
+ on the wizard's style:
+ \list
+ \o WatermarkPixmap (used by ClassicStyle and ModernStyle)
+ \o BannerPixmap (used by ModernStyle)
+ \o LogoPixmap (used by ClassicStyle and ModernStyle)
+ \o BackgroundPixmap (used by MacStyle)
+ \endlist
+ \endlist
+
+ The diagram belows shows how QWizard renders these attributes,
+ assuming they are all present and ModernStyle is used:
+
+ \image qtwizard-nonmacpage.png
+
+ When a \l{QWizardPage::}{subTitle} is set, QWizard displays it
+ in a header, in which case it also uses the BannerPixmap and the
+ LogoPixmap to decorate the header. The WatermarkPixmap is
+ displayed on the left side, below the header. At the bottom,
+ there is a row of buttons allowing the user to navigate through
+ the pages.
+
+ The page itself (the \l{QWizardPage} widget) occupies the area
+ between the header, the watermark, and the button row. Typically,
+ the page is a QWizardPage on which a QGridLayout is installed,
+ with standard child widgets (\l{QLabel}s, \l{QLineEdit}s, etc.).
+
+ If the wizard's style is MacStyle, the page looks radically
+ different:
+
+ \image qtwizard-macpage.png
+
+ The watermark, banner, and logo pixmaps are ignored by the
+ MacStyle. If the BackgroundPixmap is set, it is used as the
+ background for the wizard; otherwise, a default "assistant" image
+ is used.
+
+ The title and subtitle are set by calling
+ QWizardPage::setTitle() and QWizardPage::setSubTitle() on the
+ individual pages. They may be plain text or HTML (see titleFormat
+ and subTitleFormat). The pixmaps can be set globally for the
+ entire wizard using setPixmap(), or on a per-page basis using
+ QWizardPage::setPixmap().
+
+ \target field mechanism
+ \section1 Registering and Using Fields
+
+ In many wizards, the contents of a page may affect the default
+ values of the fields of a later page. To make it easy to
+ communicate between pages, QWizard supports a "field" mechanism
+ that allows you to register a field (e.g., a QLineEdit) on a page
+ and to access its value from any page. It is also possible to
+ specify mandatory fields (i.e., fields that must be filled before
+ the user can advance to the next page).
+
+ To register a field, call QWizardPage::registerField() field.
+ For example:
+
+ \snippet examples/dialogs/classwizard/classwizard.cpp 8
+ \dots
+ \snippet examples/dialogs/classwizard/classwizard.cpp 10
+ \snippet examples/dialogs/classwizard/classwizard.cpp 11
+ \dots
+ \snippet examples/dialogs/classwizard/classwizard.cpp 13
+
+ The above code registers three fields, \c className, \c
+ baseClass, and \c qobjectMacro, which are associated with three
+ child widgets. The asterisk (\c *) next to \c className denotes a
+ mandatory field.
+
+ \target initialize page
+ The fields of any page are accessible from any other page. For
+ example:
+
+ \snippet examples/dialogs/classwizard/classwizard.cpp 17
+
+ Here, we call QWizardPage::field() to access the contents of the
+ \c className field (which was defined in the \c ClassInfoPage)
+ and use it to initialize the \c OuputFilePage. The field's
+ contents is returned as a QVariant.
+
+ When we create a field using QWizardPage::registerField(), we
+ pass a unique field name and a widget. We can also provide a Qt
+ property name and a "changed" signal (a signal that is emitted
+ when the property changes) as third and fourth arguments;
+ however, this is not necessary for the most common Qt widgets,
+ such as QLineEdit, QCheckBox, and QComboBox, because QWizard
+ knows which properties to look for.
+
+ \target mandatory fields
+
+ If an asterisk (\c *) is appended to the name when the property
+ is registered, the field is a \e{mandatory field}. When a page has
+ mandatory fields, the \gui Next and/or \gui Finish buttons are
+ enabled only when all mandatory fields are filled.
+
+ To consider a field "filled", QWizard simply checks that the
+ field's current value doesn't equal the original value (the value
+ it had when initializePage() was called). For QLineEdit and
+ QAbstractSpinBox subclasses, QWizard also checks that
+ \l{QLineEdit::hasAcceptableInput()}{hasAcceptableInput()} returns
+ true, to honor any validator or mask.
+
+ QWizard's mandatory field mechanism is provided for convenience.
+ A more powerful (but also more cumbersome) alternative is to
+ reimplement QWizardPage::isComplete() and to emit the
+ QWizardPage::completeChanged() signal whenever the page becomes
+ complete or incomplete.
+
+ The enabled/disabled state of the \gui Next and/or \gui Finish
+ buttons is one way to perform validation on the user input.
+ Another way is to reimplement validateCurrentPage() (or
+ QWizardPage::validatePage()) to perform some last-minute
+ validation (and show an error message if the user has entered
+ incomplete or invalid information). If the function returns true,
+ the next page is shown (or the wizard finishes); otherwise, the
+ current page stays up.
+
+ \section1 Creating Linear Wizards
+
+ Most wizards have a linear structure, with page 1 followed by
+ page 2 and so on until the last page. The \l{dialogs/classwizard}{Class
+ Wizard} example is such a wizard. With QWizard, linear wizards
+ are created by instantiating the \l{QWizardPage}s and inserting
+ them using addPage(). By default, the pages are shown in the
+ order in which they were added. For example:
+
+ \snippet examples/dialogs/classwizard/classwizard.cpp 0
+ \dots
+ \snippet examples/dialogs/classwizard/classwizard.cpp 2
+
+ When a page is about to be shown, QWizard calls initializePage()
+ (which in turn calls QWizardPage::initializePage()) to fill the
+ page with default values. By default, this function does nothing,
+ but it can be reimplemented to initialize the page's contents
+ based on other pages' fields (see the \l{initialize page}{example
+ above}).
+
+ If the user presses \gui Back, cleanupPage() is called (which in
+ turn calls QWizardPage::cleanupPage()). The default
+ implementation resets the page's fields to their original values
+ (the values they had before initializePage() was called). If you
+ want the \gui Back button to be non-destructive and keep the
+ values entered by the user, simply enable the IndependentPages
+ option.
+
+ \section1 Creating Non-Linear Wizards
+
+ Some wizards are more complex in that they allow different
+ traversal paths based on the information provided by the user.
+ The \l{dialogs/licensewizard}{License Wizard} example illustrates this.
+ It provides five wizard pages; depending on which options are
+ selected, the user can reach different pages.
+
+ \image licensewizard-flow.png
+
+ In complex wizards, pages are identified by IDs. These IDs are
+ typically defined using an enum. For example:
+
+ \snippet examples/dialogs/licensewizard/licensewizard.h 0
+ \dots
+ \snippet examples/dialogs/licensewizard/licensewizard.h 2
+ \dots
+ \snippet examples/dialogs/licensewizard/licensewizard.h 3
+
+ The pages are inserted using setPage(), which takes an ID and an
+ instance of QWizardPage (or of a subclass):
+
+ \snippet examples/dialogs/licensewizard/licensewizard.cpp 1
+ \dots
+ \snippet examples/dialogs/licensewizard/licensewizard.cpp 8
+
+ By default, the pages are shown in increasing ID order. To
+ provide a dynamic order that depends on the options chosen by the
+ user, we must reimplement QWizardPage::nextId(). For example:
+
+ \snippet examples/dialogs/licensewizard/licensewizard.cpp 18
+ \codeline
+ \snippet examples/dialogs/licensewizard/licensewizard.cpp 23
+ \codeline
+ \snippet examples/dialogs/licensewizard/licensewizard.cpp 24
+ \codeline
+ \snippet examples/dialogs/licensewizard/licensewizard.cpp 25
+ \codeline
+ \snippet examples/dialogs/licensewizard/licensewizard.cpp 26
+
+ It would also be possible to put all the logic in one place, in a
+ QWizard::nextId() reimplementation. For example:
+
+ \snippet doc/src/snippets/code/src_gui_dialogs_qwizard.cpp 0
+
+ To start at another page than the page with the lowest ID, call
+ setStartId().
+
+ To test whether a page has been visited or not, call
+ hasVisitedPage(). For example:
+
+ \snippet examples/dialogs/licensewizard/licensewizard.cpp 27
+
+ \sa QWizardPage, {Class Wizard Example}, {License Wizard Example}
+*/
+
+/*!
+ \enum QWizard::WizardButton
+
+ This enum specifies the buttons in a wizard.
+
+ \value BackButton The \gui Back button (\gui {Go Back} on Mac OS X)
+ \value NextButton The \gui Next button (\gui Continue on Mac OS X)
+ \value CommitButton The \gui Commit button
+ \value FinishButton The \gui Finish button (\gui Done on Mac OS X)
+ \value CancelButton The \gui Cancel button (see also NoCancelButton)
+ \value HelpButton The \gui Help button (see also HaveHelpButton)
+ \value CustomButton1 The first user-defined button (see also HaveCustomButton1)
+ \value CustomButton2 The second user-defined button (see also HaveCustomButton2)
+ \value CustomButton3 The third user-defined button (see also HaveCustomButton3)
+
+ The following value is only useful when calling setButtonLayout():
+
+ \value Stretch A horizontal stretch in the button layout
+
+ \omitvalue NoButton
+ \omitvalue NStandardButtons
+ \omitvalue NButtons
+
+ \sa setButton(), setButtonText(), setButtonLayout(), customButtonClicked()
+*/
+
+/*!
+ \enum QWizard::WizardPixmap
+
+ This enum specifies the pixmaps that can be associated with a page.
+
+ \value WatermarkPixmap The tall pixmap on the left side of a ClassicStyle or ModernStyle page
+ \value LogoPixmap The small pixmap on the right side of a ClassicStyle or ModernStyle page header
+ \value BannerPixmap The pixmap that occupies the background of a ModernStyle page header
+ \value BackgroundPixmap The pixmap that occupies the background of a MacStyle wizard
+
+ \omitvalue NPixmaps
+
+ \sa setPixmap(), QWizardPage::setPixmap(), {Elements of a Wizard Page}
+*/
+
+/*!
+ \enum QWizard::WizardStyle
+
+ This enum specifies the different looks supported by QWizard.
+
+ \value ClassicStyle Classic Windows look
+ \value ModernStyle Modern Windows look
+ \value MacStyle Mac OS X look
+ \value AeroStyle Windows Aero look
+
+ \omitvalue NStyles
+
+ \sa setWizardStyle(), WizardOption, {Wizard Look and Feel}
+*/
+
+/*!
+ \enum QWizard::WizardOption
+
+ This enum specifies various options that affect the look and feel
+ of a wizard.
+
+ \value IndependentPages The pages are independent of each other
+ (i.e., they don't derive values from each
+ other).
+ \value IgnoreSubTitles Don't show any subtitles, even if they are set.
+ \value ExtendedWatermarkPixmap Extend any WatermarkPixmap all the
+ way down to the window's edge.
+ \value NoDefaultButton Don't make the \gui Next or \gui Finish button the
+ dialog's \l{QPushButton::setDefault()}{default button}.
+ \value NoBackButtonOnStartPage Don't show the \gui Back button on the start page.
+ \value NoBackButtonOnLastPage Don't show the \gui Back button on the last page.
+ \value DisabledBackButtonOnLastPage Disable the \gui Back button on the last page.
+ \value HaveNextButtonOnLastPage Show the (disabled) \gui Next button on the last page.
+ \value HaveFinishButtonOnEarlyPages Show the (disabled) \gui Finish button on non-final pages.
+ \value NoCancelButton Don't show the \gui Cancel button.
+ \value CancelButtonOnLeft Put the \gui Cancel button on the left of \gui Back (rather than on
+ the right of \gui Finish or \gui Next).
+ \value HaveHelpButton Show the \gui Help button.
+ \value HelpButtonOnRight Put the \gui Help button on the far right of the button layout
+ (rather than on the far left).
+ \value HaveCustomButton1 Show the first user-defined button (CustomButton1).
+ \value HaveCustomButton2 Show the second user-defined button (CustomButton2).
+ \value HaveCustomButton3 Show the third user-defined button (CustomButton3).
+
+ \sa setOptions(), setOption(), testOption()
+*/
+
+/*!
+ Constructs a wizard with the given \a parent and window \a flags.
+
+ \sa parent(), windowFlags()
+*/
+QWizard::QWizard(QWidget *parent, Qt::WindowFlags flags)
+ : QDialog(*new QWizardPrivate, parent, flags)
+{
+ Q_D(QWizard);
+ d->init();
+#ifdef Q_OS_WINCE
+ if (!qt_wince_is_mobile())
+ setWindowFlags(windowFlags() & ~Qt::WindowOkButtonHint);
+#endif
+}
+
+/*!
+ Destroys the wizard and its pages, releasing any allocated resources.
+*/
+QWizard::~QWizard()
+{
+ Q_D(QWizard);
+ delete d->buttonLayout;
+}
+
+/*!
+ Adds the given \a page to the wizard, and returns the page's ID.
+
+ The ID is guaranteed to be larger than any other ID in the
+ QWizard so far.
+
+ \sa setPage(), page()
+*/
+int QWizard::addPage(QWizardPage *page)
+{
+ Q_D(QWizard);
+ int theid = 0;
+ if (!d->pageMap.isEmpty())
+ theid = (d->pageMap.constEnd() - 1).key() + 1;
+ setPage(theid, page);
+ return theid;
+}
+
+/*!
+ \fn void QWizard::setPage(int id, QWizardPage *page)
+
+ Adds the given \a page to the wizard with the given \a id.
+
+ \sa addPage(), page()
+*/
+void QWizard::setPage(int theid, QWizardPage *page)
+{
+ Q_D(QWizard);
+
+ if (!page) {
+ qWarning("QWizard::setPage: Cannot insert null page");
+ return;
+ }
+
+ if (theid == -1) {
+ qWarning("QWizard::setPage: Cannot insert page with ID -1");
+ return;
+ }
+
+ if (d->pageMap.contains(theid)) {
+ qWarning("QWizard::setPage: Page with duplicate ID %d ignored", theid);
+ return;
+ }
+
+ page->setParent(d->pageFrame);
+
+ QVector<QWizardField> &pendingFields = page->d_func()->pendingFields;
+ for (int i = 0; i < pendingFields.count(); ++i)
+ d->addField(pendingFields.at(i));
+ pendingFields.clear();
+
+ connect(page, SIGNAL(completeChanged()), this, SLOT(_q_updateButtonStates()));
+
+ d->pageMap.insert(theid, page);
+ page->d_func()->wizard = this;
+
+ int n = d->pageVBoxLayout->count();
+
+ // disable layout to prevent layout updates while adding
+ bool pageVBoxLayoutEnabled = d->pageVBoxLayout->isEnabled();
+ d->pageVBoxLayout->setEnabled(false);
+
+ d->pageVBoxLayout->insertWidget(n - 1, page);
+
+ // hide new page and reset layout to old status
+ page->hide();
+ d->pageVBoxLayout->setEnabled(pageVBoxLayoutEnabled);
+}
+
+/*!
+ Removes the page with the given \a id. cleanupPage() will be called if necessary.
+ \since 4.5
+ \sa addPage(), setPage()
+*/
+void QWizard::removePage(int id)
+{
+ Q_D(QWizard);
+
+ QWizardPage *removedPage = 0;
+
+ if (d->start == id)
+ d->start = -1;
+
+ if (!d->history.contains(id)) {
+ // Case 1: removing a page not in the history
+ removedPage = d->pageMap.take(id);
+ d->updateCurrentPage();
+ } else if (id != d->current) {
+ // Case 2: removing a page in the history before the current page
+ removedPage = d->pageMap.take(id);
+ d->history.removeOne(id);
+ d->_q_updateButtonStates();
+ } else if (d->history.count() == 1) {
+ // Case 3: removing the current page which is the first (and only) one in the history
+ d->reset();
+ removedPage = d->pageMap.take(id);
+ if (d->pageMap.isEmpty())
+ d->updateCurrentPage();
+ else
+ restart();
+ } else {
+ // Case 4: removing the current page which is not the first one in the history
+ back();
+ removedPage = d->pageMap.take(id);
+ d->updateCurrentPage();
+ }
+
+ if (removedPage) {
+ if (d->initialized.contains(id)) {
+ cleanupPage(id);
+ d->initialized.remove(id);
+ }
+
+ d->pageVBoxLayout->removeWidget(removedPage);
+
+ for (int i = d->fields.count() - 1; i >= 0; --i) {
+ if (d->fields.at(i).page == removedPage) {
+ removedPage->d_func()->pendingFields += d->fields.at(i);
+ d->removeFieldAt(i);
+ }
+ }
+ }
+}
+
+/*!
+ \fn QWizardPage *QWizard::page(int id) const
+
+ Returns the page with the given \a id, or 0 if there is no such
+ page.
+
+ \sa addPage(), setPage()
+*/
+QWizardPage *QWizard::page(int theid) const
+{
+ Q_D(const QWizard);
+ return d->pageMap.value(theid);
+}
+
+/*!
+ \fn bool QWizard::hasVisitedPage(int id) const
+
+ Returns true if the page history contains page \a id; otherwise,
+ returns false.
+
+ Pressing \gui Back marks the current page as "unvisited" again.
+
+ \sa visitedPages()
+*/
+bool QWizard::hasVisitedPage(int theid) const
+{
+ Q_D(const QWizard);
+ return d->history.contains(theid);
+}
+
+/*!
+ Returns the list of IDs of visited pages, in the order in which the pages
+ were visited.
+
+ Pressing \gui Back marks the current page as "unvisited" again.
+
+ \sa hasVisitedPage()
+*/
+QList<int> QWizard::visitedPages() const
+{
+ Q_D(const QWizard);
+ return d->history;
+}
+
+/*!
+ Returns the list of page IDs.
+ \since 4.5
+*/
+QList<int> QWizard::pageIds() const
+{
+ Q_D(const QWizard);
+ return d->pageMap.keys();
+}
+
+/*!
+ \property QWizard::startId
+ \brief the ID of the first page
+
+ If this property isn't explicitly set, this property defaults to
+ the lowest page ID in this wizard, or -1 if no page has been
+ inserted yet.
+
+ \sa restart(), nextId()
+*/
+void QWizard::setStartId(int theid)
+{
+ Q_D(QWizard);
+ if (!d->pageMap.contains(theid)) {
+ qWarning("QWizard::setStartId: Invalid page ID %d", theid);
+ return;
+ }
+ d->start = theid;
+}
+
+int QWizard::startId() const
+{
+ Q_D(const QWizard);
+ if (d->start != -1)
+ return d->start;
+ if (!d->pageMap.isEmpty())
+ return d->pageMap.constBegin().key();
+ return -1;
+}
+
+/*!
+ Returns a pointer to the current page, or 0 if there is no current
+ page (e.g., before the wizard is shown).
+
+ This is equivalent to calling page(currentId()).
+
+ \sa page(), currentId(), restart()
+*/
+QWizardPage *QWizard::currentPage() const
+{
+ Q_D(const QWizard);
+ return page(d->current);
+}
+
+/*!
+ \property QWizard::currentId
+ \brief the ID of the current page
+
+ This property cannot be set directly. To change the current page,
+ call next(), back(), or restart().
+
+ By default, this property has a value of -1, indicating that no page is
+ currently shown.
+
+ \sa currentIdChanged(), currentPage()
+*/
+int QWizard::currentId() const
+{
+ Q_D(const QWizard);
+ return d->current;
+}
+
+/*!
+ Sets the value of the field called \a name to \a value.
+
+ This function can be used to set fields on any page of the wizard.
+
+ \sa QWizardPage::registerField(), QWizardPage::setField(), field()
+*/
+void QWizard::setField(const QString &name, const QVariant &value)
+{
+ Q_D(QWizard);
+
+ int index = d->fieldIndexMap.value(name, -1);
+ if (index != -1) {
+ const QWizardField &field = d->fields.at(index);
+ if (!field.object->setProperty(field.property, value))
+ qWarning("QWizard::setField: Couldn't write to property '%s'",
+ field.property.constData());
+ return;
+ }
+
+ qWarning("QWizard::setField: No such field '%s'", qPrintable(name));
+}
+
+/*!
+ Returns the value of the field called \a name.
+
+ This function can be used to access fields on any page of the wizard.
+
+ \sa QWizardPage::registerField(), QWizardPage::field(), setField()
+*/
+QVariant QWizard::field(const QString &name) const
+{
+ Q_D(const QWizard);
+
+ int index = d->fieldIndexMap.value(name, -1);
+ if (index != -1) {
+ const QWizardField &field = d->fields.at(index);
+ return field.object->property(field.property);
+ }
+
+ qWarning("QWizard::field: No such field '%s'", qPrintable(name));
+ return QVariant();
+}
+
+/*!
+ \property QWizard::wizardStyle
+ \brief the look and feel of the wizard
+
+ By default, QWizard uses the AeroStyle on a Windows Vista system with alpha compositing
+ enabled, regardless of the current widget style. If this is not the case, the default
+ wizard style depends on the current widget style as follows: MacStyle is the default if
+ the current widget style is QMacStyle, ModernStyle is the default if the current widget
+ style is QWindowsStyle, and ClassicStyle is the default in all other cases.
+
+ \sa {Wizard Look and Feel}, options
+*/
+void QWizard::setWizardStyle(WizardStyle style)
+{
+ Q_D(QWizard);
+
+ const bool styleChange = style != d->wizStyle;
+
+#if !defined(QT_NO_STYLE_WINDOWSVISTA)
+ const bool aeroStyleChange =
+ d->vistaInitPending || d->vistaStateChanged || (styleChange && (style == AeroStyle || d->wizStyle == AeroStyle));
+ d->vistaStateChanged = false;
+ d->vistaInitPending = false;
+#endif
+
+ if (styleChange
+#if !defined(QT_NO_STYLE_WINDOWSVISTA)
+ || aeroStyleChange
+#endif
+ ) {
+ d->disableUpdates();
+ d->wizStyle = style;
+ d->updateButtonTexts();
+ d->updateLayout();
+ updateGeometry();
+ d->enableUpdates();
+#if !defined(QT_NO_STYLE_WINDOWSVISTA)
+ if (aeroStyleChange)
+ d->handleAeroStyleChange();
+#endif
+ }
+}
+
+QWizard::WizardStyle QWizard::wizardStyle() const
+{
+ Q_D(const QWizard);
+ return d->wizStyle;
+}
+
+/*!
+ Sets the given \a option to be enabled if \a on is true;
+ otherwise, clears the given \a option.
+
+ \sa options, testOption(), setWizardStyle()
+*/
+void QWizard::setOption(WizardOption option, bool on)
+{
+ Q_D(QWizard);
+ if (!(d->opts & option) != !on)
+ setOptions(d->opts ^ option);
+}
+
+/*!
+ Returns true if the given \a option is enabled; otherwise, returns
+ false.
+
+ \sa options, setOption(), setWizardStyle()
+*/
+bool QWizard::testOption(WizardOption option) const
+{
+ Q_D(const QWizard);
+ return (d->opts & option) != 0;
+}
+
+/*!
+ \property QWizard::options
+ \brief the various options that affect the look and feel of the wizard
+
+ By default, the following options are set (depending on the platform):
+
+ \list
+ \o Windows: HelpButtonOnRight.
+ \o Mac OS X: NoDefaultButton and NoCancelButton.
+ \o X11 and QWS (Qt for Embedded Linux): none.
+ \endlist
+
+ \sa wizardStyle
+*/
+void QWizard::setOptions(WizardOptions options)
+{
+ Q_D(QWizard);
+
+ WizardOptions changed = (options ^ d->opts);
+ if (!changed)
+ return;
+
+ d->disableUpdates();
+
+ d->opts = options;
+ if ((changed & IndependentPages) && !(d->opts & IndependentPages))
+ d->cleanupPagesNotInHistory();
+
+ if (changed & (NoDefaultButton | HaveHelpButton | HelpButtonOnRight | NoCancelButton
+ | CancelButtonOnLeft | HaveCustomButton1 | HaveCustomButton2
+ | HaveCustomButton3)) {
+ d->updateButtonLayout();
+ } else if (changed & (NoBackButtonOnStartPage | NoBackButtonOnLastPage
+ | HaveNextButtonOnLastPage | HaveFinishButtonOnEarlyPages
+ | DisabledBackButtonOnLastPage)) {
+ d->_q_updateButtonStates();
+ }
+
+ d->enableUpdates();
+ d->updateLayout();
+}
+
+QWizard::WizardOptions QWizard::options() const
+{
+ Q_D(const QWizard);
+ return d->opts;
+}
+
+/*!
+ Sets the text on button \a which to be \a text.
+
+ By default, the text on buttons depends on the wizardStyle. For
+ example, on Mac OS X, the \gui Next button is called \gui
+ Continue.
+
+ To add extra buttons to the wizard (e.g., a \gui Print button),
+ one way is to call setButtonText() with CustomButton1,
+ CustomButton2, or CustomButton3 to set their text, and make the
+ buttons visible using the HaveCustomButton1, HaveCustomButton2,
+ and/or HaveCustomButton3 options.
+
+ Button texts may also be set on a per-page basis using QWizardPage::setButtonText().
+
+ \sa setButton(), button(), setButtonLayout(), setOptions(), QWizardPage::setButtonText()
+*/
+void QWizard::setButtonText(WizardButton which, const QString &text)
+{
+ Q_D(QWizard);
+
+ if (!d->ensureButton(which))
+ return;
+
+ d->buttonCustomTexts.insert(which, text);
+
+ if (!currentPage() || !currentPage()->d_func()->buttonCustomTexts.contains(which))
+ d->btns[which]->setText(text);
+}
+
+/*!
+ Returns the text on button \a which.
+
+ If a text has ben set using setButtonText(), this text is returned.
+
+ By default, the text on buttons depends on the wizardStyle. For
+ example, on Mac OS X, the \gui Next button is called \gui
+ Continue.
+
+ \sa button(), setButton(), setButtonText(), QWizardPage::buttonText(),
+ QWizardPage::setButtonText()
+*/
+QString QWizard::buttonText(WizardButton which) const
+{
+ Q_D(const QWizard);
+
+ if (!d->ensureButton(which))
+ return QString();
+
+ if (d->buttonCustomTexts.contains(which))
+ return d->buttonCustomTexts.value(which);
+
+ const QString defText = buttonDefaultText(d->wizStyle, which, d);
+ if(!defText.isNull())
+ return defText;
+
+ return d->btns[which]->text();
+}
+
+/*!
+ Sets the order in which buttons are displayed to \a layout, where
+ \a layout is a list of \l{WizardButton}s.
+
+ The default layout depends on the options (e.g., whether
+ HelpButtonOnRight) that are set. You can call this function if
+ you need more control over the buttons' layout than what \l
+ options already provides.
+
+ You can specify horizontal stretches in the layout using \l
+ Stretch.
+
+ Example:
+
+ \snippet doc/src/snippets/code/src_gui_dialogs_qwizard.cpp 1
+
+ \sa setButton(), setButtonText(), setOptions()
+*/
+void QWizard::setButtonLayout(const QList<WizardButton> &layout)
+{
+ Q_D(QWizard);
+
+ for (int i = 0; i < layout.count(); ++i) {
+ WizardButton button1 = layout.at(i);
+
+ if (button1 == NoButton || button1 == Stretch)
+ continue;
+ if (!d->ensureButton(button1))
+ return;
+
+ // O(n^2), but n is very small
+ for (int j = 0; j < i; ++j) {
+ WizardButton button2 = layout.at(j);
+ if (button2 == button1) {
+ qWarning("QWizard::setButtonLayout: Duplicate button in layout");
+ return;
+ }
+ }
+ }
+
+ d->buttonsHaveCustomLayout = true;
+ d->buttonsCustomLayout = layout;
+ d->updateButtonLayout();
+}
+
+/*!
+ Sets the button corresponding to role \a which to \a button.
+
+ To add extra buttons to the wizard (e.g., a \gui Print button),
+ one way is to call setButton() with CustomButton1 to
+ CustomButton3, and make the buttons visible using the
+ HaveCustomButton1 to HaveCustomButton3 options.
+
+ \sa setButtonText(), setButtonLayout(), options
+*/
+void QWizard::setButton(WizardButton which, QAbstractButton *button)
+{
+ Q_D(QWizard);
+
+ if (uint(which) >= NButtons || d->btns[which] == button)
+ return;
+
+ if (QAbstractButton *oldButton = d->btns[which]) {
+ d->buttonLayout->removeWidget(oldButton);
+ delete oldButton;
+ }
+
+ d->btns[which] = button;
+ if (button) {
+ button->setParent(d->antiFlickerWidget);
+ d->buttonCustomTexts.insert(which, button->text());
+ d->connectButton(which);
+ } else {
+ d->buttonCustomTexts.remove(which); // ### what about page-specific texts set for 'which'
+ d->ensureButton(which); // (QWizardPage::setButtonText())? Clear them as well?
+ }
+
+ d->updateButtonLayout();
+}
+
+/*!
+ Returns the button corresponding to role \a which.
+
+ \sa setButton(), setButtonText()
+*/
+QAbstractButton *QWizard::button(WizardButton which) const
+{
+ Q_D(const QWizard);
+#if !defined(QT_NO_STYLE_WINDOWSVISTA)
+ if (d->wizStyle == AeroStyle && which == BackButton)
+ return d->vistaHelper->backButton();
+#endif
+ if (!d->ensureButton(which))
+ return 0;
+ return d->btns[which];
+}
+
+/*!
+ \property QWizard::titleFormat
+ \brief the text format used by page titles
+
+ The default format is Qt::AutoText.
+
+ \sa QWizardPage::title, subTitleFormat
+*/
+void QWizard::setTitleFormat(Qt::TextFormat format)
+{
+ Q_D(QWizard);
+ d->titleFmt = format;
+ d->updateLayout();
+}
+
+Qt::TextFormat QWizard::titleFormat() const
+{
+ Q_D(const QWizard);
+ return d->titleFmt;
+}
+
+/*!
+ \property QWizard::subTitleFormat
+ \brief the text format used by page subtitles
+
+ The default format is Qt::AutoText.
+
+ \sa QWizardPage::title, titleFormat
+*/
+void QWizard::setSubTitleFormat(Qt::TextFormat format)
+{
+ Q_D(QWizard);
+ d->subTitleFmt = format;
+ d->updateLayout();
+}
+
+Qt::TextFormat QWizard::subTitleFormat() const
+{
+ Q_D(const QWizard);
+ return d->subTitleFmt;
+}
+
+/*!
+ Sets the pixmap for role \a which to \a pixmap.
+
+ The pixmaps are used by QWizard when displaying a page. Which
+ pixmaps are actually used depend on the \l{Wizard Look and
+ Feel}{wizard style}.
+
+ Pixmaps can also be set for a specific page using
+ QWizardPage::setPixmap().
+
+ \sa QWizardPage::setPixmap(), {Elements of a Wizard Page}
+*/
+void QWizard::setPixmap(WizardPixmap which, const QPixmap &pixmap)
+{
+ Q_D(QWizard);
+ Q_ASSERT(uint(which) < NPixmaps);
+ d->defaultPixmaps[which] = pixmap;
+ d->updatePixmap(which);
+}
+
+/*!
+ Returns the pixmap set for role \a which.
+
+ By default, the only pixmap that is set is the BackgroundPixmap on
+ Mac OS X.
+
+ \sa QWizardPage::pixmap(), {Elements of a Wizard Page}
+*/
+QPixmap QWizard::pixmap(WizardPixmap which) const
+{
+ Q_D(const QWizard);
+ Q_ASSERT(uint(which) < NPixmaps);
+#ifdef Q_WS_MAC
+ if (which == BackgroundPixmap && d->defaultPixmaps[BackgroundPixmap].isNull())
+ d->defaultPixmaps[BackgroundPixmap] = d->findDefaultBackgroundPixmap();
+#endif
+ return d->defaultPixmaps[which];
+}
+
+/*!
+ Sets the default property for \a className to be \a property,
+ and the associated change signal to be \a changedSignal.
+
+ The default property is used when an instance of \a className (or
+ of one of its subclasses) is passed to
+ QWizardPage::registerField() and no property is specified.
+
+ QWizard knows the most common Qt widgets. For these (or their
+ subclasses), you don't need to specify a \a property or a \a
+ changedSignal. The table below lists these widgets:
+
+ \table
+ \header \o Widget \o Property \o Change Notification Signal
+ \row \o QAbstractButton \o bool \l{QAbstractButton::}{checked} \o \l{QAbstractButton::}{toggled()}
+ \row \o QAbstractSlider \o int \l{QAbstractSlider::}{value} \o \l{QAbstractSlider::}{valueChanged()}
+ \row \o QComboBox \o int \l{QComboBox::}{currentIndex} \o \l{QComboBox::}{currentIndexChanged()}
+ \row \o QDateTimeEdit \o QDateTime \l{QDateTimeEdit::}{dateTime} \o \l{QDateTimeEdit::}{dateTimeChanged()}
+ \row \o QLineEdit \o QString \l{QLineEdit::}{text} \o \l{QLineEdit::}{textChanged()}
+ \row \o QListWidget \o int \l{QListWidget::}{currentRow} \o \l{QListWidget::}{currentRowChanged()}
+ \row \o QSpinBox \o int \l{QSpinBox::}{value} \o \l{QSpinBox::}{valueChanged()}
+ \endtable
+
+ \sa QWizardPage::registerField()
+*/
+void QWizard::setDefaultProperty(const char *className, const char *property,
+ const char *changedSignal)
+{
+ Q_D(QWizard);
+ for (int i = d->defaultPropertyTable.count() - 1; i >= 0; --i) {
+ if (qstrcmp(d->defaultPropertyTable.at(i).className, className) == 0) {
+ d->defaultPropertyTable.remove(i);
+ break;
+ }
+ }
+ d->defaultPropertyTable.append(QWizardDefaultProperty(className, property, changedSignal));
+}
+
+/*!
+ \reimp
+*/
+void QWizard::setVisible(bool visible)
+{
+ Q_D(QWizard);
+ if (visible) {
+ if (d->current == -1)
+ restart();
+ }
+ QDialog::setVisible(visible);
+}
+
+/*!
+ \reimp
+*/
+QSize QWizard::sizeHint() const
+{
+ Q_D(const QWizard);
+ QSize result = d->mainLayout->totalSizeHint();
+ QSize extra(500, 360);
+ if (d->wizStyle == MacStyle && d->current != -1) {
+ QSize pixmap(currentPage()->pixmap(BackgroundPixmap).size());
+ extra.setWidth(616);
+ if (!pixmap.isNull()) {
+ extra.setHeight(pixmap.height());
+
+ /*
+ The width isn't always reliable as a size hint, as
+ some wizard backgrounds just cover the leftmost area.
+ Use a rule of thumb to determine if the width is
+ reliable or not.
+ */
+ if (pixmap.width() >= pixmap.height())
+ extra.setWidth(pixmap.width());
+ }
+ }
+ return result.expandedTo(extra);
+}
+
+/*!
+ \fn void QWizard::currentIdChanged(int id)
+
+ This signal is emitted when the current page changes, with the new
+ current \a id.
+
+ \sa currentId(), currentPage()
+*/
+
+/*!
+ \fn void QWizard::helpRequested()
+
+ This signal is emitted when the user clicks the \gui Help button.
+
+ By default, no \gui Help button is shown. Call
+ setOption(HaveHelpButton, true) to have one.
+
+ Example:
+
+ \snippet examples/dialogs/licensewizard/licensewizard.cpp 0
+ \dots
+ \snippet examples/dialogs/licensewizard/licensewizard.cpp 5
+ \snippet examples/dialogs/licensewizard/licensewizard.cpp 7
+ \dots
+ \snippet examples/dialogs/licensewizard/licensewizard.cpp 8
+ \codeline
+ \snippet examples/dialogs/licensewizard/licensewizard.cpp 10
+ \dots
+ \snippet examples/dialogs/licensewizard/licensewizard.cpp 12
+ \codeline
+ \snippet examples/dialogs/licensewizard/licensewizard.cpp 14
+ \codeline
+ \snippet examples/dialogs/licensewizard/licensewizard.cpp 15
+
+ \sa customButtonClicked()
+*/
+
+/*!
+ \fn void QWizard::customButtonClicked(int which)
+
+ This signal is emitted when the user clicks a custom button. \a
+ which can be CustomButton1, CustomButton2, or CustomButton3.
+
+ By default, no custom button is shown. Call setOption() with
+ HaveCustomButton1, HaveCustomButton2, or HaveCustomButton3 to have
+ one, and use setButtonText() or setButton() to configure it.
+
+ \sa helpRequested()
+*/
+
+/*!
+ Goes back to the previous page.
+
+ This is equivalent to pressing the \gui Back button.
+
+ \sa next(), accept(), reject(), restart()
+*/
+void QWizard::back()
+{
+ Q_D(QWizard);
+ int n = d->history.count() - 2;
+ if (n < 0)
+ return;
+ d->switchToPage(d->history.at(n), QWizardPrivate::Backward);
+}
+
+/*!
+ Advances to the next page.
+
+ This is equivalent to pressing the \gui Next or \gui Commit button.
+
+ \sa nextId(), back(), accept(), reject(), restart()
+*/
+void QWizard::next()
+{
+ Q_D(QWizard);
+
+ if (d->current == -1)
+ return;
+
+ if (validateCurrentPage()) {
+ int next = nextId();
+ if (next != -1) {
+ if (d->history.contains(next)) {
+ qWarning("QWizard::next: Page %d already met", next);
+ return;
+ }
+ if (!d->pageMap.contains(next)) {
+ qWarning("QWizard::next: No such page %d", next);
+ return;
+ }
+ d->switchToPage(next, QWizardPrivate::Forward);
+ }
+ }
+}
+
+/*!
+ Restarts the wizard at the start page. This function is called automatically when the
+ wizard is shown.
+
+ \sa startId()
+*/
+void QWizard::restart()
+{
+ Q_D(QWizard);
+ d->disableUpdates();
+ d->reset();
+ d->switchToPage(startId(), QWizardPrivate::Forward);
+ d->enableUpdates();
+}
+
+/*!
+ \reimp
+*/
+bool QWizard::event(QEvent *event)
+{
+ Q_D(QWizard);
+ if (event->type() == QEvent::StyleChange) { // Propagate style
+ d->setStyle(style());
+ d->updateLayout();
+ }
+#if !defined(QT_NO_STYLE_WINDOWSVISTA)
+ else if (event->type() == QEvent::Show && d->vistaInitPending) {
+ d->vistaInitPending = false;
+ d->wizStyle = AeroStyle;
+ d->handleAeroStyleChange();
+ }
+ else if (d->isVistaThemeEnabled()) {
+ d->vistaHelper->mouseEvent(event);
+ }
+#endif
+ return QDialog::event(event);
+}
+
+/*!
+ \reimp
+*/
+void QWizard::resizeEvent(QResizeEvent *event)
+{
+ Q_D(QWizard);
+ int heightOffset = 0;
+#if !defined(QT_NO_STYLE_WINDOWSVISTA)
+ if (d->isVistaThemeEnabled()) {
+ heightOffset = d->vistaHelper->topOffset();
+ if (d->isVistaThemeEnabled(QVistaHelper::VistaAero))
+ heightOffset += d->vistaHelper->titleBarSize();
+ }
+#endif
+ d->antiFlickerWidget->resize(event->size().width(), event->size().height() - heightOffset);
+#if !defined(QT_NO_STYLE_WINDOWSVISTA)
+ if (d->isVistaThemeEnabled())
+ d->vistaHelper->resizeEvent(event);
+#endif
+ QDialog::resizeEvent(event);
+}
+
+/*!
+ \reimp
+*/
+void QWizard::paintEvent(QPaintEvent * event)
+{
+ Q_D(QWizard);
+ if (d->wizStyle == MacStyle && currentPage()) {
+ QPixmap backgroundPixmap = currentPage()->pixmap(BackgroundPixmap);
+ if (backgroundPixmap.isNull())
+ return;
+
+ QPainter painter(this);
+ painter.drawPixmap(0, (height() - backgroundPixmap.height()) / 2, backgroundPixmap);
+ }
+#if !defined(QT_NO_STYLE_WINDOWSVISTA)
+ else if (d->isVistaThemeEnabled()) {
+ if (d->isVistaThemeEnabled(QVistaHelper::VistaBasic)) {
+ QPainter painter(this);
+ QColor color = d->vistaHelper->basicWindowFrameColor();
+ painter.fillRect(0, 0, width(), QVistaHelper::topOffset(), color);
+ }
+ d->vistaHelper->paintEvent(event);
+ }
+#else
+ Q_UNUSED(event);
+#endif
+}
+
+#if defined(Q_WS_WIN)
+/*!
+ \reimp
+*/
+bool QWizard::winEvent(MSG *message, long *result)
+{
+#if !defined(QT_NO_STYLE_WINDOWSVISTA)
+ Q_D(QWizard);
+ if (d->isVistaThemeEnabled()) {
+ const bool winEventResult = d->vistaHelper->handleWinEvent(message, result);
+ if (QVistaHelper::vistaState() != d->vistaState) {
+ d->vistaState = QVistaHelper::vistaState();
+ d->vistaStateChanged = true;
+ setWizardStyle(AeroStyle);
+ }
+ return winEventResult;
+ } else {
+ return QDialog::winEvent(message, result);
+ }
+#else
+ return QDialog::winEvent(message, result);
+#endif
+}
+#endif
+
+/*!
+ \reimp
+*/
+void QWizard::done(int result)
+{
+ Q_D(QWizard);
+ // canceling leaves the wizard in a known state
+ if (result == Rejected) {
+ d->reset();
+ } else {
+ if (!validateCurrentPage())
+ return;
+ }
+ QDialog::done(result);
+}
+
+/*!
+ \fn void QWizard::initializePage(int id)
+
+ This virtual function is called by QWizard to prepare page \a id
+ just before it is shown either as a result of QWizard::restart()
+ being called, or as a result of the user clicking \gui Next. (However, if the \l
+ QWizard::IndependentPages option is set, this function is only
+ called the first time the page is shown.)
+
+ By reimplementing this function, you can ensure that the page's
+ fields are properly initialized based on fields from previous
+ pages.
+
+ The default implementation calls QWizardPage::initializePage() on
+ page(\a id).
+
+ \sa QWizardPage::initializePage(), cleanupPage()
+*/
+void QWizard::initializePage(int theid)
+{
+ QWizardPage *page = this->page(theid);
+ if (page)
+ page->initializePage();
+}
+
+/*!
+ \fn void QWizard::cleanupPage(int id)
+
+ This virtual function is called by QWizard to clean up page \a id just before the
+ user leaves it by clicking \gui Back (unless the \l QWizard::IndependentPages option is set).
+
+ The default implementation calls QWizardPage::cleanupPage() on
+ page(\a id).
+
+ \sa QWizardPage::cleanupPage(), initializePage()
+*/
+void QWizard::cleanupPage(int theid)
+{
+ QWizardPage *page = this->page(theid);
+ if (page)
+ page->cleanupPage();
+}
+
+/*!
+ This virtual function is called by QWizard when the user clicks
+ \gui Next or \gui Finish to perform some last-minute validation.
+ If it returns true, the next page is shown (or the wizard
+ finishes); otherwise, the current page stays up.
+
+ The default implementation calls QWizardPage::validatePage() on
+ the currentPage().
+
+ When possible, it is usually better style to disable the \gui
+ Next or \gui Finish button (by specifying \l{mandatory fields} or
+ by reimplementing QWizardPage::isComplete()) than to reimplement
+ validateCurrentPage().
+
+ \sa QWizardPage::validatePage(), currentPage()
+*/
+bool QWizard::validateCurrentPage()
+{
+ QWizardPage *page = currentPage();
+ if (!page)
+ return true;
+
+ return page->validatePage();
+}
+
+/*!
+ This virtual function is called by QWizard to find out which page
+ to show when the user clicks the \gui Next button.
+
+ The return value is the ID of the next page, or -1 if no page follows.
+
+ The default implementation calls QWizardPage::nextId() on the
+ currentPage().
+
+ By reimplementing this function, you can specify a dynamic page
+ order.
+
+ \sa QWizardPage::nextId(), currentPage()
+*/
+int QWizard::nextId() const
+{
+ const QWizardPage *page = currentPage();
+ if (!page)
+ return -1;
+
+ return page->nextId();
+}
+
+/*!
+ \class QWizardPage
+ \since 4.3
+ \brief The QWizardPage class is the base class for wizard pages.
+
+ QWizard represents a wizard. Each page is a QWizardPage. When
+ you create your own wizards, you can use QWizardPage directly,
+ or you can subclass it for more control.
+
+ A page has the following attributes, which are rendered by
+ QWizard: a \l title, a \l subTitle, and a \l{setPixmap()}{set of
+ pixmaps}. See \l{Elements of a Wizard Page} for details. Once a
+ page is added to the wizard (using QWizard::addPage() or
+ QWizard::setPage()), wizard() returns a pointer to the
+ associated QWizard object.
+
+ Page provides five virtual functions that can be reimplemented to
+ provide custom behavior:
+
+ \list
+ \o initializePage() is called to initialize the page's contents
+ when the user clicks the wizard's \gui Next button. If you
+ want to derive the page's default from what the user entered
+ on previous pages, this is the function to reimplement.
+ \o cleanupPage() is called to reset the page's contents when the
+ user clicks the wizard's \gui Back button.
+ \o validatePage() validates the page when the user clicks \gui
+ Next or \gui Finish. It is often used to show an error message
+ if the user has entered incomplete or invalid information.
+ \o nextId() returns the ID of the next page. It is useful when
+ \l{creating non-linear wizards}, which allow different
+ traversal paths based on the information provided by the user.
+ \o isComplete() is called to determine whether the \gui Next
+ and/or \gui Finish button should be enabled or disabled. If
+ you reimplement isComplete(), also make sure that
+ completeChanged() is emitted whenever the complete state
+ changes.
+ \endlist
+
+ Normally, the \gui Next button and the \gui Finish button of a
+ wizard are mutually exclusive. If isFinalPage() returns true, \gui
+ Finish is available; otherwise, \gui Next is available. By
+ default, isFinalPage() is true only when nextId() returns -1. If
+ you want to show \gui Next and \gui Final simultaneously for a
+ page (letting the user perform an "early finish"), call
+ setFinalPage(true) on that page. For wizards that support early
+ finishes, you might also want to set the
+ \l{QWizard::}{HaveNextButtonOnLastPage} and
+ \l{QWizard::}{HaveFinishButtonOnEarlyPages} options on the
+ wizard.
+
+ In many wizards, the contents of a page may affect the default
+ values of the fields of a later page. To make it easy to
+ communicate between pages, QWizard supports a \l{Registering and
+ Using Fields}{"field" mechanism} that allows you to register a
+ field (e.g., a QLineEdit) on a page and to access its value from
+ any page. Fields are global to the entire wizard and make it easy
+ for any single page to access information stored by another page,
+ without having to put all the logic in QWizard or having the
+ pages know explicitly about each other. Fields are registered
+ using registerField() and can be accessed at any time using
+ field() and setField().
+
+ \sa QWizard, {Class Wizard Example}, {License Wizard Example}
+*/
+
+/*!
+ Constructs a wizard page with the given \a parent.
+
+ When the page is inserted into a wizard using QWizard::addPage()
+ or QWizard::setPage(), the parent is automatically set to be the
+ wizard.
+
+ \sa wizard()
+*/
+QWizardPage::QWizardPage(QWidget *parent)
+ : QWidget(*new QWizardPagePrivate, parent, 0)
+{
+ connect(this, SIGNAL(completeChanged()), this, SLOT(_q_updateCachedCompleteState()));
+}
+
+/*!
+ \property QWizardPage::title
+ \brief the title of the page
+
+ The title is shown by the QWizard, above the actual page. All
+ pages should have a title.
+
+ The title may be plain text or HTML, depending on the value of the
+ \l{QWizard::titleFormat} property.
+
+ By default, this property contains an empty string.
+
+ \sa subTitle, {Elements of a Wizard Page}
+*/
+void QWizardPage::setTitle(const QString &title)
+{
+ Q_D(QWizardPage);
+ d->title = title;
+ if (d->wizard && d->wizard->currentPage() == this)
+ d->wizard->d_func()->updateLayout();
+}
+
+QString QWizardPage::title() const
+{
+ Q_D(const QWizardPage);
+ return d->title;
+}
+
+/*!
+ \property QWizardPage::subTitle
+ \brief the subtitle of the page
+
+ The subtitle is shown by the QWizard, between the title and the
+ actual page. Subtitles are optional. In
+ \l{QWizard::ClassicStyle}{ClassicStyle} and
+ \l{QWizard::ModernStyle}{ModernStyle}, using subtitles is
+ necessary to make the header appear. In
+ \l{QWizard::MacStyle}{MacStyle}, the subtitle is shown as a text
+ label just above the actual page.
+
+ The subtitle may be plain text or HTML, depending on the value of
+ the \l{QWizard::subTitleFormat} property.
+
+ By default, this property contains an empty string.
+
+ \sa title, QWizard::IgnoreSubTitles, {Elements of a Wizard Page}
+*/
+void QWizardPage::setSubTitle(const QString &subTitle)
+{
+ Q_D(QWizardPage);
+ d->subTitle = subTitle;
+ if (d->wizard && d->wizard->currentPage() == this)
+ d->wizard->d_func()->updateLayout();
+}
+
+QString QWizardPage::subTitle() const
+{
+ Q_D(const QWizardPage);
+ return d->subTitle;
+}
+
+/*!
+ Sets the pixmap for role \a which to \a pixmap.
+
+ The pixmaps are used by QWizard when displaying a page. Which
+ pixmaps are actually used depend on the \l{Wizard Look and
+ Feel}{wizard style}.
+
+ Pixmaps can also be set for the entire wizard using
+ QWizard::setPixmap(), in which case they apply for all pages that
+ don't specify a pixmap.
+
+ \sa QWizard::setPixmap(), {Elements of a Wizard Page}
+*/
+void QWizardPage::setPixmap(QWizard::WizardPixmap which, const QPixmap &pixmap)
+{
+ Q_D(QWizardPage);
+ Q_ASSERT(uint(which) < QWizard::NPixmaps);
+ d->pixmaps[which] = pixmap;
+ if (d->wizard && d->wizard->currentPage() == this)
+ d->wizard->d_func()->updatePixmap(which);
+}
+
+/*!
+ Returns the pixmap set for role \a which.
+
+ Pixmaps can also be set for the entire wizard using
+ QWizard::setPixmap(), in which case they apply for all pages that
+ don't specify a pixmap.
+
+ \sa QWizard::pixmap(), {Elements of a Wizard Page}
+*/
+QPixmap QWizardPage::pixmap(QWizard::WizardPixmap which) const
+{
+ Q_D(const QWizardPage);
+ Q_ASSERT(uint(which) < QWizard::NPixmaps);
+
+ const QPixmap &pixmap = d->pixmaps[which];
+ if (!pixmap.isNull())
+ return pixmap;
+
+ if (wizard())
+ return wizard()->pixmap(which);
+
+ return pixmap;
+}
+
+/*!
+ This virtual function is called by QWizard::initializePage() to
+ prepare the page just before it is shown either as a result of QWizard::restart()
+ being called, or as a result of the user clicking \gui Next.
+ (However, if the \l QWizard::IndependentPages option is set, this function is only
+ called the first time the page is shown.)
+
+ By reimplementing this function, you can ensure that the page's
+ fields are properly initialized based on fields from previous
+ pages. For example:
+
+ \snippet examples/dialogs/classwizard/classwizard.cpp 17
+
+ The default implementation does nothing.
+
+ \sa QWizard::initializePage(), cleanupPage(), QWizard::IndependentPages
+*/
+void QWizardPage::initializePage()
+{
+}
+
+/*!
+ This virtual function is called by QWizard::cleanupPage() when
+ the user leaves the page by clicking \gui Back (unless the \l QWizard::IndependentPages
+ option is set).
+
+ The default implementation resets the page's fields to their
+ original values (the values they had before initializePage() was
+ called).
+
+ \sa QWizard::cleanupPage(), initializePage(), QWizard::IndependentPages
+*/
+void QWizardPage::cleanupPage()
+{
+ Q_D(QWizardPage);
+ if (d->wizard) {
+ QVector<QWizardField> &fields = d->wizard->d_func()->fields;
+ for (int i = 0; i < fields.count(); ++i) {
+ const QWizardField &field = fields.at(i);
+ if (field.page == this)
+ field.object->setProperty(field.property, field.initialValue);
+ }
+ }
+}
+
+/*!
+ This virtual function is called by QWizard::validateCurrentPage()
+ when the user clicks \gui Next or \gui Finish to perform some
+ last-minute validation. If it returns true, the next page is shown
+ (or the wizard finishes); otherwise, the current page stays up.
+
+ The default implementation returns true.
+
+ When possible, it is usually better style to disable the \gui
+ Next or \gui Finish button (by specifying \l{mandatory fields} or
+ reimplementing isComplete()) than to reimplement validatePage().
+
+ \sa QWizard::validateCurrentPage(), isComplete()
+*/
+bool QWizardPage::validatePage()
+{
+ return true;
+}
+
+/*!
+ This virtual function is called by QWizard to determine whether
+ the \gui Next or \gui Finish button should be enabled or
+ disabled.
+
+ The default implementation returns true if all \l{mandatory
+ fields} are filled; otherwise, it returns false.
+
+ If you reimplement this function, make sure to emit completeChanged(),
+ from the rest of your implementation, whenever the value of isComplete()
+ changes. This ensures that QWizard updates the enabled or disabled state of
+ its buttons. An example of the reimplementation is
+ available \l{http://doc.trolltech.com/qq/qq22-qwizard.html#validatebeforeitstoolate}
+ {here}.
+
+ \sa completeChanged(), isFinalPage()
+*/
+bool QWizardPage::isComplete() const
+{
+ Q_D(const QWizardPage);
+
+ if (!d->wizard)
+ return true;
+
+ const QVector<QWizardField> &wizardFields = d->wizard->d_func()->fields;
+ for (int i = wizardFields.count() - 1; i >= 0; --i) {
+ const QWizardField &field = wizardFields.at(i);
+ if (field.page == this && field.mandatory) {
+ QVariant value = field.object->property(field.property);
+ if (value == field.initialValue)
+ return false;
+
+#ifndef QT_NO_LINEEDIT
+ if (QLineEdit *lineEdit = qobject_cast<QLineEdit *>(field.object)) {
+ if (!lineEdit->hasAcceptableInput())
+ return false;
+ }
+#endif
+#ifndef QT_NO_SPINBOX
+ if (QAbstractSpinBox *spinBox = qobject_cast<QAbstractSpinBox *>(field.object)) {
+ if (!spinBox->hasAcceptableInput())
+ return false;
+ }
+#endif
+ }
+ }
+ return true;
+}
+
+/*!
+ Explicitly sets this page to be final if \a finalPage is true.
+
+ After calling setFinalPage(true), isFinalPage() returns true and the \gui
+ Finish button is visible (and enabled if isComplete() returns
+ true).
+
+ After calling setFinalPage(false), isFinalPage() returns true if
+ nextId() returns -1; otherwise, it returns false.
+
+ \sa isComplete(), QWizard::HaveFinishButtonOnEarlyPages
+*/
+void QWizardPage::setFinalPage(bool finalPage)
+{
+ Q_D(QWizardPage);
+ d->explicitlyFinal = finalPage;
+ QWizard *wizard = this->wizard();
+ if (wizard && wizard->currentPage() == this)
+ wizard->d_func()->updateCurrentPage();
+}
+
+/*!
+ This function is called by QWizard to determine whether the \gui
+ Finish button should be shown for this page or not.
+
+ By default, it returns true if there is no next page
+ (i.e., nextId() returns -1); otherwise, it returns false.
+
+ By explicitly calling setFinalPage(true), you can let the user perform an
+ "early finish".
+
+ \sa isComplete(), QWizard::HaveFinishButtonOnEarlyPages
+*/
+bool QWizardPage::isFinalPage() const
+{
+ Q_D(const QWizardPage);
+ if (d->explicitlyFinal)
+ return true;
+
+ QWizard *wizard = this->wizard();
+ if (wizard && wizard->currentPage() == this) {
+ // try to use the QWizard implementation if possible
+ return wizard->nextId() == -1;
+ } else {
+ return nextId() == -1;
+ }
+}
+
+/*!
+ Sets this page to be a commit page if \a commitPage is true; otherwise,
+ sets it to be a normal page.
+
+ A commit page is a page that represents an action which cannot be undone
+ by clicking \gui Back or \gui Cancel.
+
+ A \gui Commit button replaces the \gui Next button on a commit page. Clicking this
+ button simply calls QWizard::next() just like clicking \gui Next does.
+
+ A page entered directly from a commit page has its \gui Back button disabled.
+
+ \sa isCommitPage()
+*/
+void QWizardPage::setCommitPage(bool commitPage)
+{
+ Q_D(QWizardPage);
+ d->commit = commitPage;
+ QWizard *wizard = this->wizard();
+ if (wizard && wizard->currentPage() == this)
+ wizard->d_func()->updateCurrentPage();
+}
+
+/*!
+ Returns true if this page is a commit page; otherwise returns false.
+
+ \sa setCommitPage()
+*/
+bool QWizardPage::isCommitPage() const
+{
+ Q_D(const QWizardPage);
+ return d->commit;
+}
+
+/*!
+ Sets the text on button \a which to be \a text on this page.
+
+ By default, the text on buttons depends on the QWizard::wizardStyle,
+ but may be redefined for the wizard as a whole using QWizard::setButtonText().
+
+ \sa buttonText(), QWizard::setButtonText(), QWizard::buttonText()
+*/
+void QWizardPage::setButtonText(QWizard::WizardButton which, const QString &text)
+{
+ Q_D(QWizardPage);
+ d->buttonCustomTexts.insert(which, text);
+ if (wizard() && wizard()->currentPage() == this && wizard()->d_func()->btns[which])
+ wizard()->d_func()->btns[which]->setText(text);
+}
+
+/*!
+ Returns the text on button \a which on this page.
+
+ If a text has ben set using setButtonText(), this text is returned.
+ Otherwise, if a text has been set using QWizard::setButtonText(),
+ this text is returned.
+
+ By default, the text on buttons depends on the QWizard::wizardStyle.
+ For example, on Mac OS X, the \gui Next button is called \gui
+ Continue.
+
+ \sa setButtonText(), QWizard::buttonText(), QWizard::setButtonText()
+*/
+QString QWizardPage::buttonText(QWizard::WizardButton which) const
+{
+ Q_D(const QWizardPage);
+
+ if (d->buttonCustomTexts.contains(which))
+ return d->buttonCustomTexts.value(which);
+
+ if (wizard())
+ return wizard()->buttonText(which);
+
+ return QString();
+}
+
+/*!
+ This virtual function is called by QWizard::nextId() to find
+ out which page to show when the user clicks the \gui Next button.
+
+ The return value is the ID of the next page, or -1 if no page follows.
+
+ By default, this function returns the lowest ID greater than the ID
+ of the current page, or -1 if there is no such ID.
+
+ By reimplementing this function, you can specify a dynamic page
+ order. For example:
+
+ \snippet examples/dialogs/licensewizard/licensewizard.cpp 18
+
+ \sa QWizard::nextId()
+*/
+int QWizardPage::nextId() const
+{
+ Q_D(const QWizardPage);
+
+ if (!d->wizard)
+ return -1;
+
+ bool foundCurrentPage = false;
+
+ const QWizardPrivate::PageMap &pageMap = d->wizard->d_func()->pageMap;
+ QWizardPrivate::PageMap::const_iterator i = pageMap.constBegin();
+ QWizardPrivate::PageMap::const_iterator end = pageMap.constEnd();
+
+ for (; i != end; ++i) {
+ if (i.value() == this) {
+ foundCurrentPage = true;
+ } else if (foundCurrentPage) {
+ return i.key();
+ }
+ }
+ return -1;
+}
+
+/*!
+ \fn void QWizardPage::completeChanged()
+
+ This signal is emitted whenever the complete state of the page
+ (i.e., the value of isComplete()) changes.
+
+ If you reimplement isComplete(), make sure to emit
+ completeChanged() whenever the value of isComplete() changes, to
+ ensure that QWizard updates the enabled or disabled state of its
+ buttons.
+
+ \sa isComplete()
+*/
+
+/*!
+ Sets the value of the field called \a name to \a value.
+
+ This function can be used to set fields on any page of the wizard.
+ It is equivalent to calling
+ wizard()->\l{QWizard::setField()}{setField(\a name, \a value)}.
+
+ \sa QWizard::setField(), field(), registerField()
+*/
+void QWizardPage::setField(const QString &name, const QVariant &value)
+{
+ Q_D(QWizardPage);
+ if (!d->wizard)
+ return;
+ d->wizard->setField(name, value);
+}
+
+/*!
+ Returns the value of the field called \a name.
+
+ This function can be used to access fields on any page of the
+ wizard. It is equivalent to calling
+ wizard()->\l{QWizard::field()}{field(\a name)}.
+
+ Example:
+
+ \snippet examples/dialogs/classwizard/classwizard.cpp 17
+
+ \sa QWizard::field(), setField(), registerField()
+*/
+QVariant QWizardPage::field(const QString &name) const
+{
+ Q_D(const QWizardPage);
+ if (!d->wizard)
+ return QVariant();
+ return d->wizard->field(name);
+}
+
+/*!
+ Creates a field called \a name associated with the given \a
+ property of the given \a widget. From then on, that property
+ becomes accessible using field() and setField().
+
+ Fields are global to the entire wizard and make it easy for any
+ single page to access information stored by another page, without
+ having to put all the logic in QWizard or having the pages know
+ explicitly about each other.
+
+ If \a name ends with an asterisk (\c *), the field is a mandatory
+ field. When a page has mandatory fields, the \gui Next and/or
+ \gui Finish buttons are enabled only when all mandatory fields
+ are filled. This requires a \a changedSignal to be specified, to
+ tell QWizard to recheck the value stored by the mandatory field.
+
+ QWizard knows the most common Qt widgets. For these (or their
+ subclasses), you don't need to specify a \a property or a \a
+ changedSignal. The table below lists these widgets:
+
+ \table
+ \header \o Widget \o Property \o Change Notification Signal
+ \row \o QAbstractButton \o bool \l{QAbstractButton::}{checked} \o \l{QAbstractButton::}{toggled()}
+ \row \o QAbstractSlider \o int \l{QAbstractSlider::}{value} \o \l{QAbstractSlider::}{valueChanged()}
+ \row \o QComboBox \o int \l{QComboBox::}{currentIndex} \o \l{QComboBox::}{currentIndexChanged()}
+ \row \o QDateTimeEdit \o QDateTime \l{QDateTimeEdit::}{dateTime} \o \l{QDateTimeEdit::}{dateTimeChanged()}
+ \row \o QLineEdit \o QString \l{QLineEdit::}{text} \o \l{QLineEdit::}{textChanged()}
+ \row \o QListWidget \o int \l{QListWidget::}{currentRow} \o \l{QListWidget::}{currentRowChanged()}
+ \row \o QSpinBox \o int \l{QSpinBox::}{value} \o \l{QSpinBox::}{valueChanged()}
+ \endtable
+
+ You can use QWizard::setDefaultProperty() to add entries to this
+ table or to override existing entries.
+
+ To consider a field "filled", QWizard simply checks that their
+ current value doesn't equal their original value (the value they
+ had before initializePage() was called). For QLineEdit, it also
+ checks that
+ \l{QLineEdit::hasAcceptableInput()}{hasAcceptableInput()} returns
+ true, to honor any validator or mask.
+
+ QWizard's mandatory field mechanism is provided for convenience.
+ It can be bypassed by reimplementing QWizardPage::isComplete().
+
+ \sa field(), setField(), QWizard::setDefaultProperty()
+*/
+void QWizardPage::registerField(const QString &name, QWidget *widget, const char *property,
+ const char *changedSignal)
+{
+ Q_D(QWizardPage);
+ QWizardField field(this, name, widget, property, changedSignal);
+ if (d->wizard) {
+ d->wizard->d_func()->addField(field);
+ } else {
+ d->pendingFields += field;
+ }
+}
+
+/*!
+ Returns the wizard associated with this page, or 0 if this page
+ hasn't been inserted into a QWizard yet.
+
+ \sa QWizard::addPage(), QWizard::setPage()
+*/
+QWizard *QWizardPage::wizard() const
+{
+ Q_D(const QWizardPage);
+ return d->wizard;
+}
+
+QT_END_NAMESPACE
+
+#include "moc_qwizard.cpp"
+
+#endif // QT_NO_WIZARD
diff --git a/src/gui/dialogs/qwizard.h b/src/gui/dialogs/qwizard.h
new file mode 100644
index 0000000000..ef3ed39aff
--- /dev/null
+++ b/src/gui/dialogs/qwizard.h
@@ -0,0 +1,262 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QWIZARD_H
+#define QWIZARD_H
+
+#include <QtGui/qdialog.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+#ifndef QT_NO_WIZARD
+
+class QAbstractButton;
+class QWizardPage;
+class QWizardPrivate;
+
+class Q_GUI_EXPORT QWizard : public QDialog
+{
+ Q_OBJECT
+ Q_ENUMS(WizardStyle WizardOption)
+ Q_FLAGS(WizardOptions)
+ Q_PROPERTY(WizardStyle wizardStyle READ wizardStyle WRITE setWizardStyle)
+ Q_PROPERTY(WizardOptions options READ options WRITE setOptions)
+ Q_PROPERTY(Qt::TextFormat titleFormat READ titleFormat WRITE setTitleFormat)
+ Q_PROPERTY(Qt::TextFormat subTitleFormat READ subTitleFormat WRITE setSubTitleFormat)
+ Q_PROPERTY(int startId READ startId WRITE setStartId)
+ Q_PROPERTY(int currentId READ currentId NOTIFY currentIdChanged)
+
+public:
+ enum WizardButton {
+ BackButton,
+ NextButton,
+ CommitButton,
+ FinishButton,
+ CancelButton,
+ HelpButton,
+ CustomButton1,
+ CustomButton2,
+ CustomButton3,
+ Stretch,
+
+ NoButton = -1,
+ NStandardButtons = 6,
+ NButtons = 9
+ };
+
+ enum WizardPixmap {
+ WatermarkPixmap,
+ LogoPixmap,
+ BannerPixmap,
+ BackgroundPixmap,
+ NPixmaps
+ };
+
+ enum WizardStyle {
+ ClassicStyle,
+ ModernStyle,
+ MacStyle,
+ AeroStyle,
+ NStyles
+ };
+
+ enum WizardOption {
+ IndependentPages = 0x00000001,
+ IgnoreSubTitles = 0x00000002,
+ ExtendedWatermarkPixmap = 0x00000004,
+ NoDefaultButton = 0x00000008,
+ NoBackButtonOnStartPage = 0x00000010,
+ NoBackButtonOnLastPage = 0x00000020,
+ DisabledBackButtonOnLastPage = 0x00000040,
+ HaveNextButtonOnLastPage = 0x00000080,
+ HaveFinishButtonOnEarlyPages = 0x00000100,
+ NoCancelButton = 0x00000200,
+ CancelButtonOnLeft = 0x00000400,
+ HaveHelpButton = 0x00000800,
+ HelpButtonOnRight = 0x00001000,
+ HaveCustomButton1 = 0x00002000,
+ HaveCustomButton2 = 0x00004000,
+ HaveCustomButton3 = 0x00008000
+ };
+
+ Q_DECLARE_FLAGS(WizardOptions, WizardOption)
+
+ explicit QWizard(QWidget *parent = 0, Qt::WindowFlags flags = 0);
+ ~QWizard();
+
+ int addPage(QWizardPage *page);
+ void setPage(int id, QWizardPage *page);
+ void removePage(int id);
+ QWizardPage *page(int id) const;
+ bool hasVisitedPage(int id) const;
+ QList<int> visitedPages() const; // ### visitedIds()?
+ QList<int> pageIds() const;
+ void setStartId(int id);
+ int startId() const;
+ QWizardPage *currentPage() const;
+ int currentId() const;
+
+ virtual bool validateCurrentPage();
+ virtual int nextId() const;
+
+ void setField(const QString &name, const QVariant &value);
+ QVariant field(const QString &name) const;
+
+ void setWizardStyle(WizardStyle style);
+ WizardStyle wizardStyle() const;
+
+ void setOption(WizardOption option, bool on = true);
+ bool testOption(WizardOption option) const;
+ void setOptions(WizardOptions options);
+ WizardOptions options() const;
+
+ void setButtonText(WizardButton which, const QString &text);
+ QString buttonText(WizardButton which) const;
+ void setButtonLayout(const QList<WizardButton> &layout);
+ void setButton(WizardButton which, QAbstractButton *button);
+ QAbstractButton *button(WizardButton which) const;
+
+ void setTitleFormat(Qt::TextFormat format);
+ Qt::TextFormat titleFormat() const;
+ void setSubTitleFormat(Qt::TextFormat format);
+ Qt::TextFormat subTitleFormat() const;
+ void setPixmap(WizardPixmap which, const QPixmap &pixmap);
+ QPixmap pixmap(WizardPixmap which) const;
+
+ void setDefaultProperty(const char *className, const char *property,
+ const char *changedSignal);
+
+ void setVisible(bool visible);
+ QSize sizeHint() const;
+
+Q_SIGNALS:
+ void currentIdChanged(int id);
+ void helpRequested();
+ void customButtonClicked(int which);
+
+public Q_SLOTS:
+ void back();
+ void next();
+ void restart();
+
+protected:
+ bool event(QEvent *event);
+ void resizeEvent(QResizeEvent *event);
+ void paintEvent(QPaintEvent *event);
+#if defined(Q_WS_WIN)
+ bool winEvent(MSG * message, long * result);
+#endif
+ void done(int result);
+ virtual void initializePage(int id);
+ virtual void cleanupPage(int id);
+
+private:
+ Q_DISABLE_COPY(QWizard)
+ Q_DECLARE_PRIVATE(QWizard)
+ Q_PRIVATE_SLOT(d_func(), void _q_emitCustomButtonClicked())
+ Q_PRIVATE_SLOT(d_func(), void _q_updateButtonStates())
+
+ friend class QWizardPage;
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(QWizard::WizardOptions)
+
+class QWizardPagePrivate;
+
+class Q_GUI_EXPORT QWizardPage : public QWidget
+{
+ Q_OBJECT
+ Q_PROPERTY(QString title READ title WRITE setTitle)
+ Q_PROPERTY(QString subTitle READ subTitle WRITE setSubTitle)
+
+public:
+ QWizardPage(QWidget *parent = 0);
+
+ void setTitle(const QString &title);
+ QString title() const;
+ void setSubTitle(const QString &subTitle);
+ QString subTitle() const;
+ void setPixmap(QWizard::WizardPixmap which, const QPixmap &pixmap);
+ QPixmap pixmap(QWizard::WizardPixmap which) const;
+ void setFinalPage(bool finalPage);
+ bool isFinalPage() const;
+ void setCommitPage(bool commitPage);
+ bool isCommitPage() const;
+ void setButtonText(QWizard::WizardButton which, const QString &text);
+ QString buttonText(QWizard::WizardButton which) const;
+
+ virtual void initializePage();
+ virtual void cleanupPage();
+ virtual bool validatePage();
+ virtual bool isComplete() const;
+ virtual int nextId() const;
+
+Q_SIGNALS:
+ void completeChanged();
+
+protected:
+ void setField(const QString &name, const QVariant &value);
+ QVariant field(const QString &name) const;
+ void registerField(const QString &name, QWidget *widget, const char *property = 0,
+ const char *changedSignal = 0);
+ QWizard *wizard() const;
+
+private:
+ Q_DISABLE_COPY(QWizardPage)
+ Q_DECLARE_PRIVATE(QWizardPage)
+ Q_PRIVATE_SLOT(d_func(), void _q_maybeEmitCompleteChanged())
+ Q_PRIVATE_SLOT(d_func(), void _q_updateCachedCompleteState())
+
+ friend class QWizard;
+ friend class QWizardPrivate;
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QT_NO_WIZARD
+
+#endif // QWIZARD_H
diff --git a/src/gui/dialogs/qwizard_win.cpp b/src/gui/dialogs/qwizard_win.cpp
new file mode 100644
index 0000000000..64696de219
--- /dev/null
+++ b/src/gui/dialogs/qwizard_win.cpp
@@ -0,0 +1,739 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QT_NO_WIZARD
+#ifndef QT_NO_STYLE_WINDOWSVISTA
+
+#include "qwizard_win_p.h"
+#include "qlibrary.h"
+#include "qwizard.h"
+#include "qpaintengine.h"
+#include "qapplication.h"
+#include <QtGui/QMouseEvent>
+#include <QtGui/QDesktopWidget>
+
+// Note, these tests are duplicates in qwindowsxpstyle_p.h.
+#ifdef Q_CC_GNU
+# include <w32api.h>
+# if (__W32API_MAJOR_VERSION >= 3 || (__W32API_MAJOR_VERSION == 2 && __W32API_MINOR_VERSION >= 5))
+# ifdef _WIN32_WINNT
+# undef _WIN32_WINNT
+# endif
+# define _WIN32_WINNT 0x0501
+# include <commctrl.h>
+# endif
+#endif
+
+#include <uxtheme.h>
+
+QT_BEGIN_NAMESPACE
+
+//DWM related
+typedef struct { //MARGINS
+ int cxLeftWidth; // width of left border that retains its size
+ int cxRightWidth; // width of right border that retains its size
+ int cyTopHeight; // height of top border that retains its size
+ int cyBottomHeight; // height of bottom border that retains its size
+} WIZ_MARGINS;
+typedef struct { //DTTOPTS
+ DWORD dwSize;
+ DWORD dwFlags;
+ COLORREF crText;
+ COLORREF crBorder;
+ COLORREF crShadow;
+ int eTextShadowType;
+ POINT ptShadowOffset;
+ int iBorderSize;
+ int iFontPropId;
+ int iColorPropId;
+ int iStateId;
+ BOOL fApplyOverlay;
+ int iGlowSize;
+} WIZ_DTTOPTS;
+
+typedef struct {
+ DWORD dwFlags;
+ DWORD dwMask;
+} WIZ_WTA_OPTIONS;
+
+#define WIZ_WM_THEMECHANGED 0x031A
+#define WIZ_WM_DWMCOMPOSITIONCHANGED 0x031E
+
+enum WIZ_WINDOWTHEMEATTRIBUTETYPE {
+ WIZ_WTA_NONCLIENT = 1
+};
+
+#define WIZ_WTNCA_NODRAWCAPTION 0x00000001
+#define WIZ_WTNCA_NODRAWICON 0x00000002
+
+#define WIZ_DT_CENTER 0x00000001 //DT_CENTER
+#define WIZ_DT_VCENTER 0x00000004
+#define WIZ_DT_SINGLELINE 0x00000020
+#define WIZ_DT_NOPREFIX 0x00000800
+
+enum WIZ_NAVIGATIONPARTS { //NAVIGATIONPARTS
+ WIZ_NAV_BACKBUTTON = 1,
+ WIZ_NAV_FORWARDBUTTON = 2,
+ WIZ_NAV_MENUBUTTON = 3,
+};
+
+enum WIZ_NAV_BACKBUTTONSTATES { //NAV_BACKBUTTONSTATES
+ WIZ_NAV_BB_NORMAL = 1,
+ WIZ_NAV_BB_HOT = 2,
+ WIZ_NAV_BB_PRESSED = 3,
+ WIZ_NAV_BB_DISABLED = 4,
+};
+
+#define WIZ_TMT_CAPTIONFONT (801) //TMT_CAPTIONFONT
+#define WIZ_DTT_COMPOSITED (1UL << 13) //DTT_COMPOSITED
+#define WIZ_DTT_GLOWSIZE (1UL << 11) //DTT_GLOWSIZE
+
+#define WIZ_WM_NCMOUSELEAVE 674 //WM_NCMOUSELEAVE
+
+#define WIZ_WP_CAPTION 1 //WP_CAPTION
+#define WIZ_CS_ACTIVE 1 //CS_ACTIVE
+#define WIZ_TMT_FILLCOLORHINT 3821 //TMT_FILLCOLORHINT
+#define WIZ_TMT_BORDERCOLORHINT 3822 //TMT_BORDERCOLORHINT
+
+typedef BOOL (WINAPI *PtrDwmDefWindowProc)(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, LRESULT *plResult);
+typedef HRESULT (WINAPI *PtrDwmIsCompositionEnabled)(BOOL* pfEnabled);
+typedef HRESULT (WINAPI *PtrDwmExtendFrameIntoClientArea)(HWND hWnd, const WIZ_MARGINS* pMarInset);
+typedef HRESULT (WINAPI *PtrSetWindowThemeAttribute)(HWND hwnd, enum WIZ_WINDOWTHEMEATTRIBUTETYPE eAttribute, PVOID pvAttribute, DWORD cbAttribute);
+
+static PtrDwmDefWindowProc pDwmDefWindowProc = 0;
+static PtrDwmIsCompositionEnabled pDwmIsCompositionEnabled = 0;
+static PtrDwmExtendFrameIntoClientArea pDwmExtendFrameIntoClientArea = 0;
+static PtrSetWindowThemeAttribute pSetWindowThemeAttribute = 0;
+
+//Theme related
+typedef bool (WINAPI *PtrIsAppThemed)();
+typedef bool (WINAPI *PtrIsThemeActive)();
+typedef HANDLE (WINAPI *PtrOpenThemeData)(HWND hwnd, LPCWSTR pszClassList);
+typedef HRESULT (WINAPI *PtrCloseThemeData)(HANDLE hTheme);
+typedef HRESULT (WINAPI *PtrGetThemeSysFont)(HANDLE hTheme, int iFontId, LOGFONTW *plf);
+typedef HRESULT (WINAPI *PtrDrawThemeTextEx)(HANDLE hTheme, HDC hdc, int iPartId, int iStateId, LPCWSTR pszText, int cchText, DWORD dwTextFlags, LPRECT pRect, const WIZ_DTTOPTS *pOptions);
+typedef HRESULT (WINAPI *PtrDrawThemeBackground)(HANDLE hTheme, HDC hdc, int iPartId, int iStateId, const RECT *pRect, OPTIONAL const RECT *pClipRect);
+typedef HRESULT (WINAPI *PtrGetThemePartSize)(HANDLE hTheme, HDC hdc, int iPartId, int iStateId, OPTIONAL RECT *prc, enum THEMESIZE eSize, OUT SIZE *psz);
+typedef HRESULT (WINAPI *PtrGetThemeColor)(HANDLE hTheme, int iPartId, int iStateId, int iPropId, OUT COLORREF *pColor);
+
+static PtrIsAppThemed pIsAppThemed = 0;
+static PtrIsThemeActive pIsThemeActive = 0;
+static PtrOpenThemeData pOpenThemeData = 0;
+static PtrCloseThemeData pCloseThemeData = 0;
+static PtrGetThemeSysFont pGetThemeSysFont = 0;
+static PtrDrawThemeTextEx pDrawThemeTextEx = 0;
+static PtrDrawThemeBackground pDrawThemeBackground = 0;
+static PtrGetThemePartSize pGetThemePartSize = 0;
+static PtrGetThemeColor pGetThemeColor = 0;
+
+bool QVistaHelper::is_vista = false;
+QVistaHelper::VistaState QVistaHelper::cachedVistaState = QVistaHelper::Dirty;
+
+/******************************************************************************
+** QVistaBackButton
+*/
+
+QVistaBackButton::QVistaBackButton(QWidget *widget)
+ : QAbstractButton(widget)
+{
+ setFocusPolicy(Qt::NoFocus);
+}
+
+QSize QVistaBackButton::sizeHint() const
+{
+ ensurePolished();
+ int width = 32, height = 32;
+/*
+ HANDLE theme = pOpenThemeData(0, L"Navigation");
+ SIZE size;
+ if (pGetThemePartSize(theme, 0, WIZ_NAV_BACKBUTTON, WIZ_NAV_BB_NORMAL, 0, TS_TRUE, &size) == S_OK) {
+ width = size.cx;
+ height = size.cy;
+ }
+*/
+ return QSize(width, height);
+}
+
+void QVistaBackButton::enterEvent(QEvent *event)
+{
+ if (isEnabled())
+ update();
+ QAbstractButton::enterEvent(event);
+}
+
+void QVistaBackButton::leaveEvent(QEvent *event)
+{
+ if (isEnabled())
+ update();
+ QAbstractButton::leaveEvent(event);
+}
+
+void QVistaBackButton::paintEvent(QPaintEvent *)
+{
+ QPainter p(this);
+ QRect r = rect();
+ HANDLE theme = pOpenThemeData(0, L"Navigation");
+ //RECT rect;
+ RECT clipRect;
+ int xoffset = QWidget::mapToParent(r.topLeft()).x();
+ int yoffset = QWidget::mapToParent(r.topLeft()).y();
+
+ clipRect.top = r.top() + yoffset;
+ clipRect.bottom = r.bottom() + yoffset;
+ clipRect.left = r.left() + xoffset;
+ clipRect.right = r.right() + xoffset;
+
+ int state = WIZ_NAV_BB_NORMAL;
+ if (!isEnabled())
+ state = WIZ_NAV_BB_DISABLED;
+ else if (isDown())
+ state = WIZ_NAV_BB_PRESSED;
+ else if (underMouse())
+ state = WIZ_NAV_BB_HOT;
+
+ pDrawThemeBackground(theme, p.paintEngine()->getDC(), WIZ_NAV_BACKBUTTON, state, &clipRect, &clipRect);
+}
+
+/******************************************************************************
+** QVistaHelper
+*/
+
+QVistaHelper::QVistaHelper(QWizard *wizard)
+ : pressed(false)
+ , wizard(wizard)
+{
+ is_vista = resolveSymbols();
+ backButton_ = new QVistaBackButton(wizard);
+}
+
+QVistaHelper::~QVistaHelper()
+{
+}
+
+bool QVistaHelper::isCompositionEnabled()
+{
+ bool value = is_vista;
+ if (is_vista) {
+ HRESULT hr;
+ BOOL bEnabled;
+
+ hr = pDwmIsCompositionEnabled(&bEnabled);
+ value = (SUCCEEDED(hr) && bEnabled);
+ }
+ return value;
+}
+
+bool QVistaHelper::isThemeActive()
+{
+ return is_vista && pIsThemeActive();
+}
+
+QVistaHelper::VistaState QVistaHelper::vistaState()
+{
+ if (cachedVistaState == Dirty)
+ cachedVistaState =
+ isCompositionEnabled() ? VistaAero : isThemeActive() ? VistaBasic : Classic;
+ return cachedVistaState;
+}
+
+QColor QVistaHelper::basicWindowFrameColor()
+{
+ DWORD rgb;
+ HANDLE hTheme = pOpenThemeData(qApp->desktop()->winId(), L"WINDOW");
+ pGetThemeColor(
+ hTheme, WIZ_WP_CAPTION, WIZ_CS_ACTIVE,
+ wizard->isActiveWindow() ? WIZ_TMT_FILLCOLORHINT : WIZ_TMT_BORDERCOLORHINT,
+ &rgb);
+ BYTE r = GetRValue(rgb);
+ BYTE g = GetGValue(rgb);
+ BYTE b = GetBValue(rgb);
+ return QColor(r, g, b);
+}
+
+bool QVistaHelper::setDWMTitleBar(TitleBarChangeType type)
+{
+ bool value = false;
+ if (vistaState() == VistaAero) {
+ WIZ_MARGINS mar = {0};
+ if (type == NormalTitleBar)
+ mar.cyTopHeight = 0;
+ else
+ mar.cyTopHeight = titleBarSize() + topOffset();
+ HRESULT hr = pDwmExtendFrameIntoClientArea(wizard->winId(), &mar);
+ value = SUCCEEDED(hr);
+ }
+ return value;
+}
+
+void QVistaHelper::drawTitleBar(QPainter *painter)
+{
+ if (vistaState() == VistaAero)
+ drawBlackRect(
+ QRect(0, 0, wizard->width(), titleBarSize() + topOffset()),
+ painter->paintEngine()->getDC());
+
+ const int btnTop = backButton_->mapToParent(QPoint()).y();
+ const int btnHeight = backButton_->size().height();
+ const int verticalCenter = (btnTop + btnHeight / 2);
+
+ wizard->windowIcon().paint(
+ painter, QRect(leftMargin(), verticalCenter - iconSize() / 2, iconSize(), iconSize()));
+
+ const QString text = wizard->window()->windowTitle();
+ const QFont font = QApplication::font("QWorkspaceTitleBar");
+ const QFontMetrics fontMetrics(font);
+ const QRect brect = fontMetrics.boundingRect(text);
+ int textHeight = brect.height();
+ int textWidth = brect.width();
+ if (vistaState() == VistaAero) {
+ textHeight += 2 * glowSize();
+ textWidth += 2 * glowSize();
+ }
+ drawTitleText(
+ painter, text,
+ QRect(titleOffset(), verticalCenter - textHeight / 2, textWidth, textHeight),
+ painter->paintEngine()->getDC());
+}
+
+void QVistaHelper::setTitleBarIconAndCaptionVisible(bool visible)
+{
+ if (is_vista) {
+ WIZ_WTA_OPTIONS opt;
+ opt.dwFlags = WIZ_WTNCA_NODRAWICON | WIZ_WTNCA_NODRAWCAPTION;
+ if (visible)
+ opt.dwMask = 0;
+ else
+ opt.dwMask = WIZ_WTNCA_NODRAWICON | WIZ_WTNCA_NODRAWCAPTION;
+ pSetWindowThemeAttribute(wizard->winId(), WIZ_WTA_NONCLIENT, &opt, sizeof(WIZ_WTA_OPTIONS));
+ }
+}
+
+bool QVistaHelper::winEvent(MSG* msg, long* result)
+{
+ bool retval = true;
+
+ switch (msg->message) {
+ case WM_NCHITTEST: {
+ LRESULT lResult;
+ pDwmDefWindowProc(msg->hwnd, msg->message, msg->wParam, msg->lParam, &lResult);
+ if (lResult == HTCLOSE || lResult == HTMAXBUTTON || lResult == HTMINBUTTON || lResult == HTHELP)
+ *result = lResult;
+ else
+ *result = DefWindowProc(msg->hwnd, msg->message, msg->wParam, msg->lParam);
+ break;
+ }
+ case WM_NCMOUSEMOVE:
+ case WM_NCLBUTTONDOWN:
+ case WM_NCLBUTTONUP:
+ case WIZ_WM_NCMOUSELEAVE: {
+ LRESULT lResult;
+ pDwmDefWindowProc(msg->hwnd, msg->message, msg->wParam, msg->lParam, &lResult);
+ *result = DefWindowProc(msg->hwnd, msg->message, msg->wParam, msg->lParam);
+ break;
+ }
+ case WM_NCCALCSIZE: {
+ NCCALCSIZE_PARAMS* lpncsp = (NCCALCSIZE_PARAMS*)msg->lParam;
+ *result = DefWindowProc(msg->hwnd, msg->message, msg->wParam, msg->lParam);
+ lpncsp->rgrc[0].top -= (vistaState() == VistaAero ? titleBarSize() : 0);
+ break;
+ }
+ default:
+ retval = false;
+ }
+
+ return retval;
+}
+
+void QVistaHelper::setMouseCursor(QPoint pos)
+{
+ if (rtTop.contains(pos))
+ wizard->setCursor(Qt::SizeVerCursor);
+ else
+ wizard->setCursor(Qt::ArrowCursor);
+}
+
+void QVistaHelper::mouseEvent(QEvent *event)
+{
+ switch (event->type()) {
+ case QEvent::MouseMove:
+ mouseMoveEvent(static_cast<QMouseEvent *>(event));
+ break;
+ case QEvent::MouseButtonPress:
+ mousePressEvent(static_cast<QMouseEvent *>(event));
+ break;
+ case QEvent::MouseButtonRelease:
+ mouseReleaseEvent(static_cast<QMouseEvent *>(event));
+ break;
+ default:
+ break;
+ }
+}
+
+// The following hack ensures that the titlebar is updated correctly
+// when the wizard style changes to and from AeroStyle. Specifically,
+// this function causes a Windows message of type WM_NCCALCSIZE to
+// be triggered.
+void QVistaHelper::setWindowPosHack()
+{
+ const int x = wizard->geometry().x(); // ignored by SWP_NOMOVE
+ const int y = wizard->geometry().y(); // ignored by SWP_NOMOVE
+ const int w = wizard->width();
+ const int h = wizard->height();
+ SetWindowPos(wizard->winId(), 0, x, y, w, h, SWP_NOMOVE | SWP_NOZORDER | SWP_FRAMECHANGED);
+}
+
+// The following hack allows any QWidget subclass to access
+// QWidgetPrivate::topData() without being declared as a
+// friend by QWidget.
+class QHackWidget : public QWidget
+{
+public:
+ Q_DECLARE_PRIVATE(QWidget)
+ QTLWExtra* topData() { return d_func()->topData(); }
+};
+
+void QVistaHelper::collapseTopFrameStrut()
+{
+ QTLWExtra *top = ((QHackWidget *)wizard)->d_func()->topData();
+ int x1, y1, x2, y2;
+ top->frameStrut.getCoords(&x1, &y1, &x2, &y2);
+ top->frameStrut.setCoords(x1, 0, x2, y2);
+}
+
+bool QVistaHelper::handleWinEvent(MSG *message, long *result)
+{
+ if (message->message == WIZ_WM_THEMECHANGED || message->message == WIZ_WM_DWMCOMPOSITIONCHANGED)
+ cachedVistaState = Dirty;
+
+ bool status = false;
+ if (wizard->wizardStyle() == QWizard::AeroStyle && vistaState() == VistaAero) {
+ status = winEvent(message, result);
+ if (message->message == WM_NCCALCSIZE) {
+ if (status)
+ collapseTopFrameStrut();
+ } else if (message->message == WM_NCPAINT) {
+ wizard->update();
+ }
+ }
+ return status;
+}
+
+void QVistaHelper::resizeEvent(QResizeEvent * event)
+{
+ Q_UNUSED(event);
+ rtTop = QRect (0, 0, wizard->width(), frameSize());
+ int height = captionSize() + topOffset();
+ if (vistaState() == VistaBasic)
+ height -= titleBarSize();
+ rtTitle = QRect (0, frameSize(), wizard->width(), height);
+}
+
+void QVistaHelper::paintEvent(QPaintEvent *event)
+{
+ Q_UNUSED(event);
+ QPainter painter(wizard);
+ drawTitleBar(&painter);
+}
+
+void QVistaHelper::mouseMoveEvent(QMouseEvent *event)
+{
+ if (wizard->windowState() & Qt::WindowMaximized) {
+ event->ignore();
+ return;
+ }
+
+ QRect rect = wizard->geometry();
+ if (pressed) {
+ switch (change) {
+ case resizeTop:
+ {
+ const int dy = event->pos().y() - pressedPos.y();
+ if ((dy > 0 && rect.height() > wizard->minimumHeight())
+ || (dy < 0 && rect.height() < wizard->maximumHeight()))
+ rect.setTop(rect.top() + dy);
+ }
+ break;
+ case movePosition: {
+ QPoint newPos = event->pos() - pressedPos;
+ rect.moveLeft(rect.left() + newPos.x());
+ rect.moveTop(rect.top() + newPos.y());
+ break; }
+ default:
+ break;
+ }
+ wizard->setGeometry(rect);
+
+ } else if (vistaState() == VistaAero) {
+ setMouseCursor(event->pos());
+ }
+ event->ignore();
+}
+
+void QVistaHelper::mousePressEvent(QMouseEvent *event)
+{
+ change = noChange;
+
+ if (wizard->windowState() & Qt::WindowMaximized) {
+ event->ignore();
+ return;
+ }
+
+ if (rtTitle.contains(event->pos())) {
+ change = movePosition;
+ } else if (rtTop.contains(event->pos()))
+ change = (vistaState() == VistaAero) ? resizeTop : movePosition;
+
+ if (change != noChange) {
+ if (vistaState() == VistaAero)
+ setMouseCursor(event->pos());
+ pressed = true;
+ pressedPos = event->pos();
+ } else {
+ event->ignore();
+ }
+}
+
+void QVistaHelper::mouseReleaseEvent(QMouseEvent *event)
+{
+ change = noChange;
+ if (pressed) {
+ pressed = false;
+ wizard->releaseMouse();
+ if (vistaState() == VistaAero)
+ setMouseCursor(event->pos());
+ }
+ event->ignore();
+}
+
+bool QVistaHelper::eventFilter(QObject *obj, QEvent *event)
+{
+ if (obj != wizard)
+ return QObject::eventFilter(obj, event);
+
+ if (event->type() == QEvent::MouseMove) {
+ QMouseEvent *mouseEvent = static_cast<QMouseEvent*>(event);
+ long result;
+ MSG msg;
+ msg.message = WM_NCHITTEST;
+ msg.wParam = 0;
+ msg.lParam = MAKELPARAM(mouseEvent->globalX(), mouseEvent->globalY());
+ msg.hwnd = wizard->winId();
+ winEvent(&msg, &result);
+ msg.wParam = result;
+ msg.message = WM_NCMOUSEMOVE;
+ winEvent(&msg, &result);
+ } else if (event->type() == QEvent::MouseButtonPress) {
+ QMouseEvent *mouseEvent = static_cast<QMouseEvent*>(event);
+ long result;
+ MSG msg;
+ msg.message = WM_NCHITTEST;
+ msg.wParam = 0;
+ msg.lParam = MAKELPARAM(mouseEvent->globalX(), mouseEvent->globalY());
+ msg.hwnd = wizard->winId();
+ winEvent(&msg, &result);
+ msg.wParam = result;
+ msg.message = WM_NCLBUTTONDOWN;
+ winEvent(&msg, &result);
+ } else if (event->type() == QEvent::MouseButtonRelease) {
+ QMouseEvent *mouseEvent = static_cast<QMouseEvent*>(event);
+ long result;
+ MSG msg;
+ msg.message = WM_NCHITTEST;
+ msg.wParam = 0;
+ msg.lParam = MAKELPARAM(mouseEvent->globalX(), mouseEvent->globalY());
+ msg.hwnd = wizard->winId();
+ winEvent(&msg, &result);
+ msg.wParam = result;
+ msg.message = WM_NCLBUTTONUP;
+ winEvent(&msg, &result);
+ }
+
+ return false;
+}
+
+HFONT QVistaHelper::getCaptionFont(HANDLE hTheme)
+{
+ LOGFONT lf = {0};
+
+ if (!hTheme)
+ pGetThemeSysFont(hTheme, WIZ_TMT_CAPTIONFONT, &lf);
+ else
+ {
+ NONCLIENTMETRICS ncm = {sizeof(NONCLIENTMETRICS)};
+ SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(NONCLIENTMETRICS), &ncm, false);
+ lf = ncm.lfMessageFont;
+ }
+ return CreateFontIndirect(&lf);
+}
+
+bool QVistaHelper::drawTitleText(QPainter *painter, const QString &text, const QRect &rect, HDC hdc)
+{
+ bool value = false;
+ if (vistaState() == VistaAero) {
+ HANDLE hTheme = pOpenThemeData(qApp->desktop()->winId(), L"WINDOW");
+ if (!hTheme) return false;
+ // Set up a memory DC and bitmap that we'll draw into
+ HDC dcMem;
+ HBITMAP bmp;
+ BITMAPINFO dib = {0};
+ dcMem = CreateCompatibleDC(hdc);
+
+ dib.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
+ dib.bmiHeader.biWidth = rect.width();
+ dib.bmiHeader.biHeight = -rect.height();
+ dib.bmiHeader.biPlanes = 1;
+ dib.bmiHeader.biBitCount = 32;
+ dib.bmiHeader.biCompression = BI_RGB;
+
+ bmp = CreateDIBSection(hdc, &dib, DIB_RGB_COLORS, NULL, NULL, 0);
+
+ // Set up the DC
+ HFONT hCaptionFont = getCaptionFont(hTheme);
+ HBITMAP hOldBmp = (HBITMAP)SelectObject(dcMem, (HGDIOBJ) bmp);
+ HFONT hOldFont = (HFONT)SelectObject(dcMem, (HGDIOBJ) hCaptionFont);
+
+ // Draw the text!
+ WIZ_DTTOPTS dto = { sizeof(WIZ_DTTOPTS) };
+ const UINT uFormat = WIZ_DT_SINGLELINE|WIZ_DT_CENTER|WIZ_DT_VCENTER|WIZ_DT_NOPREFIX;
+ RECT rctext ={0,0, rect.width(), rect.height()};
+
+ dto.dwFlags = WIZ_DTT_COMPOSITED|WIZ_DTT_GLOWSIZE;
+ dto.iGlowSize = glowSize();
+
+ pDrawThemeTextEx(hTheme, dcMem, 0, 0, (LPCWSTR)text.utf16(), -1, uFormat, &rctext, &dto );
+ BitBlt(hdc, rect.left(), rect.top(), rect.width(), rect.height(), dcMem, 0, 0, SRCCOPY);
+ SelectObject(dcMem, (HGDIOBJ) hOldBmp);
+ SelectObject(dcMem, (HGDIOBJ) hOldFont);
+ DeleteObject(bmp);
+ DeleteObject(hCaptionFont);
+ DeleteDC(dcMem);
+ //ReleaseDC(hwnd, hdc);
+ } else if (vistaState() == VistaBasic) {
+ painter->drawText(rect, text);
+ }
+ return value;
+}
+
+bool QVistaHelper::drawBlackRect(const QRect &rect, HDC hdc)
+{
+ bool value = false;
+ if (vistaState() == VistaAero) {
+ // Set up a memory DC and bitmap that we'll draw into
+ HDC dcMem;
+ HBITMAP bmp;
+ BITMAPINFO dib = {0};
+ dcMem = CreateCompatibleDC(hdc);
+
+ dib.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
+ dib.bmiHeader.biWidth = rect.width();
+ dib.bmiHeader.biHeight = -rect.height();
+ dib.bmiHeader.biPlanes = 1;
+ dib.bmiHeader.biBitCount = 32;
+ dib.bmiHeader.biCompression = BI_RGB;
+
+ bmp = CreateDIBSection(hdc, &dib, DIB_RGB_COLORS, NULL, NULL, 0);
+ HBITMAP hOldBmp = (HBITMAP)SelectObject(dcMem, (HGDIOBJ) bmp);
+
+ BitBlt(hdc, rect.left(), rect.top(), rect.width(), rect.height(), dcMem, 0, 0, SRCCOPY);
+ SelectObject(dcMem, (HGDIOBJ) hOldBmp);
+
+ DeleteObject(bmp);
+ DeleteDC(dcMem);
+ }
+ return value;
+}
+
+bool QVistaHelper::resolveSymbols()
+{
+ static bool tried = false;
+ if (!tried) {
+ tried = true;
+ QLibrary dwmLib(QString::fromAscii("dwmapi"));
+ pDwmIsCompositionEnabled =
+ (PtrDwmIsCompositionEnabled)dwmLib.resolve("DwmIsCompositionEnabled");
+ if (pDwmIsCompositionEnabled) {
+ pDwmDefWindowProc = (PtrDwmDefWindowProc)dwmLib.resolve("DwmDefWindowProc");
+ pDwmExtendFrameIntoClientArea =
+ (PtrDwmExtendFrameIntoClientArea)dwmLib.resolve("DwmExtendFrameIntoClientArea");
+ }
+ QLibrary themeLib(QString::fromAscii("uxtheme"));
+ pIsAppThemed = (PtrIsAppThemed)themeLib.resolve("IsAppThemed");
+ if (pIsAppThemed) {
+ pDrawThemeBackground = (PtrDrawThemeBackground)themeLib.resolve("DrawThemeBackground");
+ pGetThemePartSize = (PtrGetThemePartSize)themeLib.resolve("GetThemePartSize");
+ pGetThemeColor = (PtrGetThemeColor)themeLib.resolve("GetThemeColor");
+ pIsThemeActive = (PtrIsThemeActive)themeLib.resolve("IsThemeActive");
+ pOpenThemeData = (PtrOpenThemeData)themeLib.resolve("OpenThemeData");
+ pCloseThemeData = (PtrCloseThemeData)themeLib.resolve("CloseThemeData");
+ pGetThemeSysFont = (PtrGetThemeSysFont)themeLib.resolve("GetThemeSysFont");
+ pDrawThemeTextEx = (PtrDrawThemeTextEx)themeLib.resolve("DrawThemeTextEx");
+ pSetWindowThemeAttribute = (PtrSetWindowThemeAttribute)themeLib.resolve("SetWindowThemeAttribute");
+ }
+ }
+
+ return (
+ pDwmIsCompositionEnabled != 0
+ && pDwmDefWindowProc != 0
+ && pDwmExtendFrameIntoClientArea != 0
+ && pIsAppThemed != 0
+ && pDrawThemeBackground != 0
+ && pGetThemePartSize != 0
+ && pGetThemeColor != 0
+ && pIsThemeActive != 0
+ && pOpenThemeData != 0
+ && pCloseThemeData != 0
+ && pGetThemeSysFont != 0
+ && pDrawThemeTextEx != 0
+ && pSetWindowThemeAttribute != 0
+ );
+}
+
+int QVistaHelper::titleOffset()
+{
+ int iconOffset = wizard ->windowIcon().isNull() ? 0 : iconSize() + padding();
+ return leftMargin() + iconOffset;
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_STYLE_WINDOWSVISTA
+
+#endif // QT_NO_WIZARD
diff --git a/src/gui/dialogs/qwizard_win_p.h b/src/gui/dialogs/qwizard_win_p.h
new file mode 100644
index 0000000000..cbb3b17f62
--- /dev/null
+++ b/src/gui/dialogs/qwizard_win_p.h
@@ -0,0 +1,148 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QWIZARD_WIN_P_H
+#define QWIZARD_WIN_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_WIZARD
+#ifndef QT_NO_STYLE_WINDOWSVISTA
+
+#include <windows.h>
+#include <qobject.h>
+#include <qwidget.h>
+#include <qabstractbutton.h>
+#include <QtGui/private/qwidget_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QVistaBackButton : public QAbstractButton
+{
+public:
+ QVistaBackButton(QWidget *widget);
+
+ QSize sizeHint() const;
+ inline QSize minimumSizeHint() const
+ { return sizeHint(); }
+
+ void enterEvent(QEvent *event);
+ void leaveEvent(QEvent *event);
+ void paintEvent(QPaintEvent *event);
+};
+
+class QWizard;
+
+class QVistaHelper : public QObject
+{
+ Q_OBJECT
+public:
+ QVistaHelper(QWizard *wizard);
+ ~QVistaHelper();
+ enum TitleBarChangeType { NormalTitleBar, ExtendedTitleBar };
+ bool setDWMTitleBar(TitleBarChangeType type);
+ void setTitleBarIconAndCaptionVisible(bool visible);
+ void mouseEvent(QEvent *event);
+ bool handleWinEvent(MSG *message, long *result);
+ void resizeEvent(QResizeEvent *event);
+ void paintEvent(QPaintEvent *event);
+ QVistaBackButton *backButton() const { return backButton_; }
+ void setWindowPosHack();
+ QColor basicWindowFrameColor();
+ enum VistaState { VistaAero, VistaBasic, Classic, Dirty };
+ static VistaState vistaState();
+ static int titleBarSize() { return frameSize() + captionSize(); }
+ static int topPadding() { return 8; }
+ static int topOffset() { return titleBarSize() + (vistaState() == VistaAero ? 13 : 3); }
+
+private:
+ static HFONT getCaptionFont(HANDLE hTheme);
+ bool drawTitleText(QPainter *painter, const QString &text, const QRect &rect, HDC hdc);
+ static bool drawBlackRect(const QRect &rect, HDC hdc);
+
+ static int frameSize() { return GetSystemMetrics(SM_CYSIZEFRAME); }
+ static int captionSize() { return GetSystemMetrics(SM_CYCAPTION); }
+
+ static int backButtonSize() { return 31; } // ### should be queried from back button itself
+ static int iconSize() { return 16; } // Standard Aero
+ static int padding() { return 7; } // Standard Aero
+ static int leftMargin() { return backButtonSize() + padding(); }
+ static int glowSize() { return 10; }
+
+ int titleOffset();
+ bool resolveSymbols();
+ void drawTitleBar(QPainter *painter);
+ void setMouseCursor(QPoint pos);
+ void collapseTopFrameStrut();
+ bool winEvent(MSG *message, long *result);
+ void mouseMoveEvent(QMouseEvent *event);
+ void mousePressEvent(QMouseEvent *event);
+ void mouseReleaseEvent(QMouseEvent *event);
+ bool eventFilter(QObject *obj, QEvent *event);
+
+ static bool is_vista;
+ static VistaState cachedVistaState;
+ static bool isCompositionEnabled();
+ static bool isThemeActive();
+ enum Changes { resizeTop, movePosition, noChange } change;
+ QPoint pressedPos;
+ bool pressed;
+ QRect rtTop;
+ QRect rtTitle;
+ QWizard *wizard;
+ QVistaBackButton *backButton_;
+};
+
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_STYLE_WINDOWSVISTA
+#endif // QT_NO_WIZARD
+#endif // QWIZARD_WIN_P_H
diff --git a/src/gui/embedded/embedded.pri b/src/gui/embedded/embedded.pri
new file mode 100644
index 0000000000..95c41326b4
--- /dev/null
+++ b/src/gui/embedded/embedded.pri
@@ -0,0 +1,226 @@
+# Qt for Embedded Linux
+
+embedded {
+ CONFIG -= opengl x11
+ LIBS -= -dl
+ KERNEL_P = kernel
+
+ !mac:HEADERS += embedded/qsoundqss_qws.h
+ HEADERS += \
+ embedded/qcopchannel_qws.h \
+ embedded/qdecoration_qws.h \
+ embedded/qdecorationfactory_qws.h \
+ embedded/qdecorationplugin_qws.h \
+ embedded/qdirectpainter_qws.h \
+ embedded/qlock_p.h \
+ embedded/qscreen_qws.h \
+ embedded/qscreenmulti_qws_p.h \
+ embedded/qscreenproxy_qws.h \
+ embedded/qwindowsystem_qws.h \
+ embedded/qwindowsystem_p.h \
+ embedded/qwscommand_qws_p.h \
+ embedded/qwscursor_qws.h \
+ embedded/qwsdisplay_qws.h \
+ embedded/qwsdisplay_qws_p.h \
+ embedded/qwsevent_qws.h \
+ embedded/qwsmanager_qws.h \
+ embedded/qwsmanager_p.h \
+ embedded/qwsproperty_qws.h \
+ embedded/qwsprotocolitem_qws.h \
+ embedded/qtransportauth_qws.h \
+ embedded/qtransportauth_qws_p.h \
+ embedded/qtransportauthdefs_qws.h \
+ embedded/qwssocket_qws.h \
+ embedded/qwslock_p.h \
+ embedded/qwsutils_qws.h \
+ embedded/qwssharedmemory_p.h \
+ embedded/qwssignalhandler_p.h \
+ embedded/qwsembedwidget.h
+
+ !mac:SOURCES += embedded/qsoundqss_qws.cpp
+ SOURCES += \
+ embedded/qcopchannel_qws.cpp \
+ embedded/qdecoration_qws.cpp \
+ embedded/qdecorationfactory_qws.cpp \
+ embedded/qdecorationplugin_qws.cpp \
+ embedded/qdirectpainter_qws.cpp \
+ embedded/qlock.cpp \
+ embedded/qscreen_qws.cpp \
+ embedded/qscreenmulti_qws.cpp \
+ embedded/qscreenproxy_qws.cpp \
+ embedded/qwindowsystem_qws.cpp \
+ embedded/qwscommand_qws.cpp \
+ embedded/qwscursor_qws.cpp \
+ embedded/qwsevent_qws.cpp \
+ embedded/qwsmanager_qws.cpp \
+ embedded/qwsproperty_qws.cpp \
+ embedded/qtransportauth_qws.cpp \
+ embedded/qwslock.cpp \
+ embedded/qwssharedmemory.cpp \
+ embedded/qwssocket_qws.cpp \
+ embedded/qwssignalhandler.cpp \
+ embedded/qwsembedwidget.cpp
+
+ contains(QT_CONFIG,sxe)|contains(QT_CONFIG,qtopia) {
+ SOURCES += embedded/qunixsocket.cpp embedded/qunixsocketserver.cpp
+ HEADERS += embedded/qunixsocket_p.h embedded/qunixsocketserver_p.h
+ }
+
+#
+# Decorations
+#
+ contains( decorations, default ) {
+ HEADERS += embedded/qdecorationdefault_qws.h
+ SOURCES += embedded/qdecorationdefault_qws.cpp
+ }
+ contains( decorations, styled ) {
+ HEADERS += embedded/qdecorationstyled_qws.h
+ SOURCES += embedded/qdecorationstyled_qws.cpp
+ }
+
+ contains( decorations, windows ) {
+ HEADERS += embedded/qdecorationwindows_qws.h
+ SOURCES += embedded/qdecorationwindows_qws.cpp
+ }
+
+#
+# Qt for Embedded Linux Drivers
+#
+ HEADERS += embedded/qscreendriverplugin_qws.h \
+ embedded/qscreendriverfactory_qws.h \
+ embedded/qkbd_qws.h \
+ embedded/qkbddriverplugin_qws.h \
+ embedded/qkbddriverfactory_qws.h \
+ embedded/qmouse_qws.h \
+ embedded/qmousedriverplugin_qws.h \
+ embedded/qmousedriverfactory_qws.h
+
+ SOURCES += embedded/qscreendriverplugin_qws.cpp \
+ embedded/qscreendriverfactory_qws.cpp \
+ embedded/qkbd_qws.cpp \
+ embedded/qkbddriverplugin_qws.cpp \
+ embedded/qkbddriverfactory_qws.cpp \
+ embedded/qmouse_qws.cpp \
+ embedded/qmousedriverplugin_qws.cpp \
+ embedded/qmousedriverfactory_qws.cpp
+
+#
+# Graphics drivers
+#
+ contains( gfx-drivers, linuxfb ) {
+ HEADERS += embedded/qscreenlinuxfb_qws.h
+ SOURCES += embedded/qscreenlinuxfb_qws.cpp
+ }
+
+ contains( gfx-drivers, qvfb ) {
+ HEADERS += embedded/qscreenvfb_qws.h
+ SOURCES += embedded/qscreenvfb_qws.cpp
+ }
+
+ contains( gfx-drivers, vnc ) {
+ VNCDIR = $$QT_SOURCE_TREE/src/plugins/gfxdrivers/vnc
+ INCLUDEPATH += $$VNCDIR
+ HEADERS += $$VNCDIR/qscreenvnc_qws.h \
+ $$VNCDIR/qscreenvnc_p.h
+ SOURCES += $$VNCDIR/qscreenvnc_qws.cpp
+ }
+
+ contains( gfx-drivers, transformed ) {
+ HEADERS += embedded/qscreentransformed_qws.h
+ SOURCES += embedded/qscreentransformed_qws.cpp
+ }
+
+#
+# Keyboard drivers
+#
+ contains( kbd-drivers, qvfb ) {
+ HEADERS +=embedded/qkbdvfb_qws.h
+ SOURCES +=embedded/qkbdvfb_qws.cpp
+ !contains( kbd-drivers, qvfb ) {
+ kbd-drivers += qvfb
+ }
+ }
+
+ contains( kbd-drivers, sl5000 ) {
+ HEADERS +=embedded/qkbdsl5000_qws.h
+ SOURCES +=embedded/qkbdsl5000_qws.cpp
+ !contains( kbd-drivers, tty ) {
+ kbd-drivers += tty
+ }
+ }
+
+ contains( kbd-drivers, tty ) {
+ HEADERS +=embedded/qkbdtty_qws.h
+ SOURCES +=embedded/qkbdtty_qws.cpp
+ !contains( kbd-drivers, pc101 ) {
+ kbd-drivers += pc101
+ }
+ }
+
+ contains( kbd-drivers, usb ) {
+ HEADERS +=embedded/qkbdusb_qws.h
+ SOURCES +=embedded/qkbdusb_qws.cpp
+ !contains( kbd-drivers, pc101 ) {
+ kbd-drivers += pc101
+ }
+ }
+
+ contains( kbd-drivers, um ) {
+ HEADERS +=embedded/qkbdum_qws.h
+ SOURCES +=embedded/qkbdum_qws.cpp
+ }
+
+ contains( kbd-drivers, pc101 ) {
+ HEADERS +=embedded/qkbdpc101_qws.h
+ SOURCES +=embedded/qkbdpc101_qws.cpp
+ }
+
+ contains( kbd-drivers, yopy ) {
+ HEADERS +=embedded/qkbdyopy_qws.h
+ SOURCES +=embedded/qkbdyopy_qws.cpp
+ }
+
+ contains( kbd-drivers, vr41xx ) {
+ HEADERS +=embedded/qkbdvr41xx_qws.h
+ SOURCES +=embedded/qkbdvr41xx_qws.cpp
+ }
+
+#
+# Mouse drivers
+#
+ contains( mouse-drivers, qvfb ) {
+ HEADERS +=embedded/qmousevfb_qws.h
+ SOURCES +=embedded/qmousevfb_qws.cpp
+ }
+
+ contains( mouse-drivers, pc ) {
+ HEADERS +=embedded/qmousepc_qws.h
+ SOURCES +=embedded/qmousepc_qws.cpp
+ }
+
+ contains( mouse-drivers, bus ) {
+ HEADERS +=embedded/qmousebus_qws.h
+ SOURCES +=embedded/qmousebus_qws.cpp
+ }
+
+ contains( mouse-drivers, linuxtp ) {
+ HEADERS +=embedded/qmouselinuxtp_qws.h
+ SOURCES +=embedded/qmouselinuxtp_qws.cpp
+ }
+
+ contains( mouse-drivers, vr41xx ) {
+ HEADERS +=embedded/qmousevr41xx_qws.h
+ SOURCES +=embedded/qmousevr41xx_qws.cpp
+ }
+
+ contains( mouse-drivers, yopy ) {
+ HEADERS +=embedded/qmouseyopy_qws.h
+ SOURCES +=embedded/qmouseyopy_qws.cpp
+ }
+
+ contains( mouse-drivers, tslib ) {
+ LIBS += -lts
+ HEADERS +=embedded/qmousetslib_qws.h
+ SOURCES +=embedded/qmousetslib_qws.cpp
+ }
+}
diff --git a/src/gui/embedded/qcopchannel_qws.cpp b/src/gui/embedded/qcopchannel_qws.cpp
new file mode 100644
index 0000000000..00cf5dcd9c
--- /dev/null
+++ b/src/gui/embedded/qcopchannel_qws.cpp
@@ -0,0 +1,608 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qcopchannel_qws.h"
+
+#ifndef QT_NO_COP
+
+#include "qwsdisplay_qws.h"
+#include "qwscommand_qws_p.h"
+#include "qwindowsystem_qws.h"
+#include "qwindowsystem_p.h"
+#include "qlist.h"
+#include "qmap.h"
+#include "qdatastream.h"
+#include "qpointer.h"
+#include "qmutex.h"
+
+#include "qdebug.h"
+
+QT_BEGIN_NAMESPACE
+
+typedef QMap<QString, QList<QWSClient*> > QCopServerMap;
+static QCopServerMap *qcopServerMap = 0;
+
+class QCopServerRegexp
+{
+public:
+ QCopServerRegexp( const QString& channel, QWSClient *client );
+ QCopServerRegexp( const QCopServerRegexp& other );
+
+ QString channel;
+ QWSClient *client;
+ QRegExp regexp;
+};
+
+QCopServerRegexp::QCopServerRegexp( const QString& channel, QWSClient *client )
+{
+ this->channel = channel;
+ this->client = client;
+ this->regexp = QRegExp( channel, Qt::CaseSensitive, QRegExp::Wildcard );
+}
+
+QCopServerRegexp::QCopServerRegexp( const QCopServerRegexp& other )
+{
+ channel = other.channel;
+ client = other.client;
+ regexp = other.regexp;
+}
+
+typedef QList<QCopServerRegexp> QCopServerRegexpList;
+static QCopServerRegexpList *qcopServerRegexpList = 0;
+
+typedef QMap<QString, QList< QPointer<QCopChannel> > > QCopClientMap;
+static QCopClientMap *qcopClientMap = 0;
+
+Q_GLOBAL_STATIC(QMutex, qcopClientMapMutex)
+
+// Determine if a channel name contains wildcard characters.
+static bool containsWildcards( const QString& channel )
+{
+ return channel.contains(QLatin1Char('*'));
+}
+
+class QCopChannelPrivate
+{
+public:
+ QString channel;
+};
+
+/*!
+ \class QCopChannel
+ \ingroup qws
+
+ \brief The QCopChannel class provides communication capabilities
+ between clients in \l{Qt for Embedded Linux}.
+
+ Note that this class is only available in \l{Qt for Embedded Linux}.
+
+ The Qt COmmunication Protocol (QCOP) is a many-to-many protocol
+ for transferring messages across registered channels. A channel is
+ registered by name, and anyone who wants to can listen to the
+ channel as well as send messages through it. The QCOP protocol
+ allows clients to communicate both within the same address space
+ and between different processes.
+
+ To send messages to a given channel, QCopChannel provides the
+ static send() function. Using this function alone, the messages
+ are queued until Qt re-enters the event loop. To immediately flush
+ all queued messages to the registered listeners, call the static
+ flush() function.
+
+ To listen to the traffic on a given channel, you typically
+ instantiate a QCopChannel object for the given channel and connect
+ to its received() signal that is emitted whenever there is
+ incoming data. Use the static isRegistered() function to query
+ the server for the existence of a given channel. QCopChannel
+ provides the channel() function returning the name of this
+ QCopChannel object's channel.
+
+ In additon, QCopChannel provides the virtual receive() function
+ that can be reimplemented to filter the incoming messages and
+ data. The default implementation simply emits the received()
+ signal.
+
+ \sa QWSServer, QWSClient, {Qt for Embedded Linux Architecture}
+*/
+
+/*!
+ Constructs a QCopChannel object for the specified \a channel, with
+ the given \a parent. Once created, the channel is registered by
+ the server.
+
+ \sa isRegistered(), channel()
+*/
+
+QCopChannel::QCopChannel(const QString& channel, QObject *parent) :
+ QObject(parent)
+{
+ init(channel);
+}
+
+#ifdef QT3_SUPPORT
+/*!
+ Use the two argument overload instead, and call the
+ QObject::setObjectName() function to \a name the instance.
+*/
+QCopChannel::QCopChannel(const QString& channel, QObject *parent, const char *name) :
+ QObject(parent)
+{
+ setObjectName(QString::fromAscii(name));
+ init(channel);
+}
+#endif
+
+void QCopChannel::init(const QString& channel)
+{
+ d = new QCopChannelPrivate;
+ d->channel = channel;
+
+ if (!qt_fbdpy) {
+ qFatal("QCopChannel: Must construct a QApplication "
+ "before QCopChannel");
+ return;
+ }
+
+ {
+ QMutexLocker locker(qcopClientMapMutex());
+
+ if (!qcopClientMap)
+ qcopClientMap = new QCopClientMap;
+
+ // do we need a new channel list ?
+ QCopClientMap::Iterator it = qcopClientMap->find(channel);
+ if (it != qcopClientMap->end()) {
+ it.value().append(this);
+ return;
+ }
+
+ it = qcopClientMap->insert(channel, QList< QPointer<QCopChannel> >());
+ it.value().append(QPointer<QCopChannel>(this));
+ }
+
+ // inform server about this channel
+ qt_fbdpy->registerChannel(channel);
+}
+
+/*!
+ \internal
+
+ Resend all channel registrations
+ */
+void QCopChannel::reregisterAll()
+{
+ if(qcopClientMap)
+ for(QCopClientMap::Iterator iter = qcopClientMap->begin();
+ iter != qcopClientMap->end();
+ ++iter)
+ qt_fbdpy->registerChannel(iter.key());
+}
+
+/*!
+ Destroys this QCopChannel object.
+
+ The server is notified that this particular listener has closed
+ its connection. The server will keep the channel open until the
+ last registered listener detaches.
+
+ \sa isRegistered(), channel()
+*/
+
+QCopChannel::~QCopChannel()
+{
+ QMutexLocker locker(qcopClientMapMutex());
+ QCopClientMap::Iterator it = qcopClientMap->find(d->channel);
+ Q_ASSERT(it != qcopClientMap->end());
+ it.value().removeAll(this);
+ // still any clients connected locally ?
+ if (it.value().isEmpty()) {
+ QByteArray data;
+ QDataStream s(&data, QIODevice::WriteOnly);
+ s << d->channel;
+ if (qt_fbdpy)
+ send(QLatin1String(""), QLatin1String("detach()"), data);
+ qcopClientMap->remove(d->channel);
+ }
+
+ delete d;
+}
+
+/*!
+ Returns the name of this object's channel.
+
+ \sa isRegistered()
+*/
+
+QString QCopChannel::channel() const
+{
+ return d->channel;
+}
+
+/*!
+ \fn void QCopChannel::receive(const QString& message, const QByteArray &data)
+
+ Processes the incoming \a message and \a data.
+
+ This function is called by the server when this object's channel
+ receives new messages. Note that the default implementation simply
+ emits the received() signal; reimplement this function to process
+ the incoming \a message and \a data.
+
+ Note that the format of the given \a data has to be well defined
+ in order to extract the information it contains. In addition, it
+ is recommended to use the DCOP convention. This is not a
+ requirement, but you must ensure that the sender and receiver
+ agree on the argument types. For example:
+
+ \snippet doc/src/snippets/code/src_gui_embedded_qcopchannel_qws.cpp 0
+
+ The above code assumes that the \c message is a DCOP-style
+ function signature and the \c data contains the function's
+ arguments.
+
+ \sa send(), channel(), received()
+ */
+void QCopChannel::receive(const QString& msg, const QByteArray &data)
+{
+ emit received(msg, data);
+}
+
+/*!
+ \fn void QCopChannel::received(const QString& message, const QByteArray &data)
+
+ This signal is emitted whenever this object's channel receives new
+ messages (i.e., it is emitted by the receive() function), passing
+ the incoming \a message and \a data as parameters.
+
+ \sa receive(), channel()
+*/
+
+/*!
+ Queries the server for the existence of the given \a channel. Returns true
+ if the channel is registered; otherwise returns false.
+
+ \sa channel(), send()
+*/
+
+bool QCopChannel::isRegistered(const QString& channel)
+{
+ QByteArray data;
+ QDataStream s(&data, QIODevice::WriteOnly);
+ s << channel;
+ if (!send(QLatin1String(""), QLatin1String("isRegistered()"), data))
+ return false;
+
+ QWSQCopMessageEvent *e = qt_fbdpy->waitForQCopResponse();
+ bool known = e->message == "known";
+ delete e;
+ return known;
+}
+
+/*!
+ \fn bool QCopChannel::send(const QString& channel, const QString& message)
+ \overload
+*/
+
+bool QCopChannel::send(const QString& channel, const QString& msg)
+{
+ QByteArray data;
+ return send(channel, msg, data);
+}
+
+/*!
+ \fn bool QCopChannel::send(const QString& channel, const QString& message,
+ const QByteArray &data)
+
+ Sends the given \a message on the specified \a channel with the
+ given \a data. The message will be distributed to all clients
+ subscribed to the channel. Returns true if the message is sent
+ successfully; otherwise returns false.
+
+ It is recommended to use the DCOP convention. This is not a
+ requirement, but you must ensure that the sender and receiver
+ agree on the argument types.
+
+ Note that QDataStream provides a convenient way to fill the byte
+ array with auxiliary data. For example:
+
+ \snippet doc/src/snippets/code/src_gui_embedded_qcopchannel_qws.cpp 1
+
+ In the code above the channel is \c "System/Shell". The \c message
+ is an arbitrary string, but in the example we've used the DCOP
+ convention of passing a function signature. Such a signature is
+ formatted as \c "functionname(types)" where \c types is a list of
+ zero or more comma-separated type names, with no whitespace, no
+ consts and no pointer or reference marks, i.e. no "*" or "&".
+
+ \sa receive(), isRegistered()
+*/
+
+bool QCopChannel::send(const QString& channel, const QString& msg,
+ const QByteArray &data)
+{
+ if (!qt_fbdpy) {
+ qFatal("QCopChannel::send: Must construct a QApplication "
+ "before using QCopChannel");
+ return false;
+ }
+
+ qt_fbdpy->sendMessage(channel, msg, data);
+
+ return true;
+}
+
+/*!
+ \since 4.2
+
+ Flushes all queued messages to the registered listeners.
+
+ Note that this function returns false if no QApplication has been
+ constructed, otherwise it returns true.
+
+ \sa send()
+
+*/
+bool QCopChannel::flush()
+{
+ if (!qt_fbdpy) {
+ qFatal("QCopChannel::flush: Must construct a QApplication "
+ "before using QCopChannel");
+ return false;
+ }
+
+ qt_fbdpy->flushCommands();
+
+ return true;
+}
+
+class QWSServerSignalBridge : public QObject {
+ Q_OBJECT
+
+public:
+ void emitNewChannel(const QString& channel);
+ void emitRemovedChannel(const QString& channel);
+
+ signals:
+ void newChannel(const QString& channel);
+ void removedChannel(const QString& channel);
+};
+
+void QWSServerSignalBridge::emitNewChannel(const QString& channel){
+ emit newChannel(channel);
+}
+
+void QWSServerSignalBridge::emitRemovedChannel(const QString& channel) {
+ emit removedChannel(channel);
+}
+
+/*!
+ \internal
+ Server side: subscribe client \a cl on channel \a ch.
+*/
+
+void QCopChannel::registerChannel(const QString& ch, QWSClient *cl)
+{
+ if (!qcopServerMap)
+ qcopServerMap = new QCopServerMap;
+
+ // do we need a new channel list ?
+ QCopServerMap::Iterator it = qcopServerMap->find(ch);
+ if (it == qcopServerMap->end())
+ it = qcopServerMap->insert(ch, QList<QWSClient*>());
+
+ // If the channel name contains wildcard characters, then we also
+ // register it on the server regexp matching list.
+ if (containsWildcards( ch )) {
+ QCopServerRegexp item(ch, cl);
+ if (!qcopServerRegexpList)
+ qcopServerRegexpList = new QCopServerRegexpList;
+ qcopServerRegexpList->append( item );
+ }
+
+ // If this is the first client in the channel, announce the channel as being created.
+ if (it.value().count() == 0) {
+ QWSServerSignalBridge* qwsBridge = new QWSServerSignalBridge();
+ connect(qwsBridge, SIGNAL(newChannel(QString)), qwsServer, SIGNAL(newChannel(QString)));
+ qwsBridge->emitNewChannel(ch);
+ delete qwsBridge;
+ }
+
+ it.value().append(cl);
+}
+
+/*!
+ \internal
+ Server side: unsubscribe \a cl from all channels.
+*/
+
+void QCopChannel::detach(QWSClient *cl)
+{
+ if (!qcopServerMap)
+ return;
+
+ QCopServerMap::Iterator it = qcopServerMap->begin();
+ for (; it != qcopServerMap->end(); ++it) {
+ if (it.value().contains(cl)) {
+ it.value().removeAll(cl);
+ // If this was the last client in the channel, announce the channel as dead.
+ if (it.value().count() == 0) {
+ QWSServerSignalBridge* qwsBridge = new QWSServerSignalBridge();
+ connect(qwsBridge, SIGNAL(removedChannel(QString)), qwsServer, SIGNAL(removedChannel(QString)));
+ qwsBridge->emitRemovedChannel(it.key());
+ delete qwsBridge;
+ }
+ }
+ }
+
+ if (!qcopServerRegexpList)
+ return;
+
+ QCopServerRegexpList::Iterator it2 = qcopServerRegexpList->begin();
+ while(it2 != qcopServerRegexpList->end()) {
+ if ((*it2).client == cl)
+ it2 = qcopServerRegexpList->erase(it2);
+ else
+ ++it2;
+ }
+}
+
+/*!
+ \internal
+ Server side: transmit the message to all clients registered to the
+ specified channel.
+*/
+
+void QCopChannel::answer(QWSClient *cl, const QString& ch,
+ const QString& msg, const QByteArray &data)
+{
+ // internal commands
+ if (ch.isEmpty()) {
+ if (msg == QLatin1String("isRegistered()")) {
+ QString c;
+ QDataStream s(data);
+ s >> c;
+ bool known = qcopServerMap && qcopServerMap->contains(c)
+ && !((*qcopServerMap)[c]).isEmpty();
+ // Yes, it's a typo, it's not user-visible, and we choose not to fix it for compatibility
+ QLatin1String ans = QLatin1String(known ? "known" : "unkown");
+ QWSServerPrivate::sendQCopEvent(cl, QLatin1String(""),
+ ans, data, true);
+ return;
+ } else if (msg == QLatin1String("detach()")) {
+ QString c;
+ QDataStream s(data);
+ s >> c;
+ Q_ASSERT(qcopServerMap);
+ QCopServerMap::Iterator it = qcopServerMap->find(c);
+ if (it != qcopServerMap->end()) {
+ //Q_ASSERT(it.value().contains(cl));
+ it.value().removeAll(cl);
+ if (it.value().isEmpty()) {
+ // If this was the last client in the channel, announce the channel as dead
+ QWSServerSignalBridge* qwsBridge = new QWSServerSignalBridge();
+ connect(qwsBridge, SIGNAL(removedChannel(QString)), qwsServer, SIGNAL(removedChannel(QString)));
+ qwsBridge->emitRemovedChannel(it.key());
+ delete qwsBridge;
+ qcopServerMap->erase(it);
+ }
+ }
+ if (qcopServerRegexpList && containsWildcards(c)) {
+ // Remove references to a wildcarded channel.
+ QCopServerRegexpList::Iterator it
+ = qcopServerRegexpList->begin();
+ while(it != qcopServerRegexpList->end()) {
+ if ((*it).client == cl && (*it).channel == c)
+ it = qcopServerRegexpList->erase(it);
+ else
+ ++it;
+ }
+ }
+ return;
+ }
+ qWarning("QCopChannel: unknown internal command %s", qPrintable(msg));
+ QWSServerPrivate::sendQCopEvent(cl, QLatin1String(""),
+ QLatin1String("bad"), data);
+ return;
+ }
+
+ if (qcopServerMap) {
+ QList<QWSClient*> clist = qcopServerMap->value(ch);
+ for (int i=0; i < clist.size(); ++i) {
+ QWSClient *c = clist.at(i);
+ QWSServerPrivate::sendQCopEvent(c, ch, msg, data);
+ }
+ }
+
+ if(qcopServerRegexpList && !containsWildcards(ch)) {
+ // Search for wildcard matches and forward the message on.
+ QCopServerRegexpList::ConstIterator it = qcopServerRegexpList->constBegin();
+ for (; it != qcopServerRegexpList->constEnd(); ++it) {
+ if ((*it).regexp.exactMatch(ch)) {
+ QByteArray newData;
+ {
+ QDataStream stream
+ (&newData, QIODevice::WriteOnly | QIODevice::Append);
+ stream << ch;
+ stream << msg;
+ stream << data;
+ // Stream is flushed and closed at this point.
+ }
+ QWSServerPrivate::sendQCopEvent
+ ((*it).client, (*it).channel,
+ QLatin1String("forwardedMessage(QString,QString,QByteArray)"),
+ newData);
+ }
+ }
+ }
+}
+
+/*!
+ \internal
+ Client side: distribute received event to the QCop instance managing the
+ channel.
+*/
+void QCopChannel::sendLocally(const QString& ch, const QString& msg,
+ const QByteArray &data)
+{
+ Q_ASSERT(qcopClientMap);
+
+ // filter out internal events
+ if (ch.isEmpty())
+ return;
+
+ // feed local clients with received data
+ QList< QPointer<QCopChannel> > clients;
+ {
+ QMutexLocker locker(qcopClientMapMutex());
+ clients = (*qcopClientMap)[ch];
+ }
+ for (int i = 0; i < clients.size(); ++i) {
+ QCopChannel *channel = (QCopChannel *)clients.at(i);
+ if ( channel )
+ channel->receive(msg, data);
+ }
+}
+
+QT_END_NAMESPACE
+
+#include "qcopchannel_qws.moc"
+
+#endif
diff --git a/src/gui/embedded/qcopchannel_qws.h b/src/gui/embedded/qcopchannel_qws.h
new file mode 100644
index 0000000000..d6e6e6b5aa
--- /dev/null
+++ b/src/gui/embedded/qcopchannel_qws.h
@@ -0,0 +1,108 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QCOPCHANNEL_QWS_H
+#define QCOPCHANNEL_QWS_H
+
+#include <QtCore/qobject.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+#ifndef QT_NO_COP
+
+class QWSClient;
+class QCopChannelPrivate;
+
+class Q_GUI_EXPORT QCopChannel : public QObject
+{
+ Q_OBJECT
+public:
+ explicit QCopChannel(const QString& channel, QObject *parent=0);
+#ifdef QT3_SUPPORT
+ QT3_SUPPORT_CONSTRUCTOR QCopChannel(const QString& channel, QObject *parent, const char *name);
+#endif
+ virtual ~QCopChannel();
+
+ QString channel() const;
+
+ static bool isRegistered(const QString& channel);
+ static bool send(const QString& channel, const QString& msg);
+ static bool send(const QString& channel, const QString& msg,
+ const QByteArray &data);
+
+ static bool flush();
+
+ static void sendLocally( const QString& ch, const QString& msg,
+ const QByteArray &data);
+ static void reregisterAll();
+
+ virtual void receive(const QString& msg, const QByteArray &data);
+
+Q_SIGNALS:
+ void received(const QString& msg, const QByteArray &data);
+
+private:
+ void init(const QString& channel);
+
+ // server side
+ static void registerChannel(const QString& ch, QWSClient *cl);
+ static void detach(QWSClient *cl);
+ static void answer(QWSClient *cl, const QString& ch,
+ const QString& msg, const QByteArray &data);
+ // client side
+ QCopChannelPrivate* d;
+
+ friend class QWSServer;
+ friend class QWSServerPrivate;
+ friend class QApplication;
+};
+
+#endif // QT_NO_COP
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QCOPCHANNEL_QWS_H
diff --git a/src/gui/embedded/qdecoration_qws.cpp b/src/gui/embedded/qdecoration_qws.cpp
new file mode 100644
index 0000000000..4ac1f01d4b
--- /dev/null
+++ b/src/gui/embedded/qdecoration_qws.cpp
@@ -0,0 +1,404 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qdecoration_qws.h"
+
+#include "qapplication.h"
+#include "qdrawutil.h"
+#include "qpainter.h"
+#include "qregion.h"
+#include "qwhatsthis.h"
+
+#include "qmenu.h"
+#include "private/qwidget_p.h"
+#include "qwsmanager_qws.h"
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QDecoration
+ \ingroup qws
+
+ \brief The QDecoration class is a base class for window
+ decorations in Qt for Embedded Linux
+
+ Note that this class is non-portable and only available in
+ \l{Qt for Embedded Linux}.
+
+ \l{Qt for Embedded Linux} provides window management of top level windows
+ and several ready made decorations (i.e., \c Default, \c Styled
+ and \c Windows). Custom decorations can be implemented by
+ subclassing the QDecoration class and creating a decoration plugin
+ (derived from QDecorationPlugin). The default
+ implementation of the QDecorationFactory class will automatically
+ detect the plugin, and load the decoration into the application at
+ run-time using Qt's \l {How to Create Qt Plugins}{plugin
+ system}. To actually apply a decoration, use the
+ QApplication::qwsSetDecoration() function.
+
+ When creating a custom decoration, implement the paint() function
+ to paint the border and title decoration, and the region()
+ function to return the regions the decoration
+ occupies. Reimplement the regionClicked() and
+ regionDoubleClicked() functions to respond to mouse clicks (the
+ default implementations responds to (single) clicks on items in a
+ widget's system menu and double clicks on a widget's title).
+
+ QDecoration provides the DecorationRegion enum that describes the
+ various regions of the window decoration, and the regionAt()
+ function to determine the region containing a given point. The
+ QDecoration class also provides the DecorationState enum
+ describing the state of a given region, e.g. whether it is active
+ or not.
+
+ In addition, it is possible to build the system menu for a given
+ top level widget using the buildSysMenu() function; whenever an
+ action in this menu is triggered, the menuTriggered() function is
+ called automatically.
+
+ Finally, the QDecoration class provides a couple of static
+ functions, startMove() and startResize(), which start a move or
+ resize action by making the appropriate decoration region active
+ and grabbing the mouse input.
+
+ \sa QDecorationFactory, QDecorationPlugin, {Qt for Embedded Linux
+ Architecture}
+*/
+
+/*!
+ \fn QDecoration::QDecoration()
+
+ Constructs a decoration object.
+*/
+
+/*!
+ \fn QDecoration::~QDecoration()
+
+ Destroys this decoration object.
+*/
+
+/*!
+ \enum QDecoration::DecorationRegion
+
+ This enum describes the various regions of the window decoration.
+
+ \value All The entire region used by the window decoration.
+
+ \value Top The top border used to vertically resize the window.
+ \value Bottom The bottom border used to vertically resize the window.
+ \value Left The left border used to horizontally resize the window.
+ \value Right The right border used to horizontally resize the window.
+ \value TopLeft The top-left corner of the window used to resize the
+ window both horizontally and vertically.
+ \value TopRight The top-right corner of the window used to resize the
+ window both horizontally and vertically.
+ \value BottomLeft The bottom-left corner of the window used to resize the
+ window both horizontally and vertically.
+ \value BottomRight The bottom-right corner of the window used to resize the
+ window both horizontally and vertically.
+ \value Borders All the regions used to describe the window's borders.
+
+ \value Title The region containing the window title, used
+ to move the window by dragging with the mouse cursor.
+ \value Close The region occupied by the close button. Clicking in this
+ region closes the window.
+ \value Minimize The region occupied by the minimize button. Clicking in
+ this region minimizes the window.
+ \value Maximize The region occupied by the maximize button. Clicking in
+ this region maximizes the window.
+ \value Normalize The region occupied by a button used to restore a window's
+ normal size. Clicking in this region restores a maximized
+ window to its previous size. The region used for this
+ button is often also the Maximize region.
+ \value Menu The region occupied by the window's menu button. Clicking
+ in this region opens the window operations (system) menu.
+ \value Help The region occupied by the window's help button. Clicking
+ in this region causes the context-sensitive help function
+ to be enabled.
+ \value Resize The region used to resize the window.
+ \value Move The region used to move the window.
+ \value None No region.
+
+ \sa region(), regionAt(), DecorationState
+*/
+
+/*!
+ \enum QDecoration::DecorationState
+
+ This enum describes the various states of a decoration region.
+
+ \value Normal The region is active
+ \value Disabled The region is inactive.
+ \value Hover The cursor is hovering over the region.
+ \value Pressed The region is pressed.
+
+ \sa paint(), DecorationRegion
+*/
+
+/*!
+ \fn QRegion QDecoration::region(const QWidget *widget, const QRect & rectangle, int decorationRegion)
+
+ Implement this function to return the region specified by \a
+ decorationRegion for the given top level \a widget.
+
+ The \a rectangle parameter specifies the rectangle the decoration
+ is wrapped around. The \a decorationRegion is a bitmask of the
+ values described by the DecorationRegion enum.
+
+ \sa regionAt(), paint()
+*/
+
+/*!
+ \fn QRegion QDecoration::region(const QWidget *widget, int decorationRegion)
+ \overload
+*/
+
+/*!
+ \fn bool QDecoration::paint(QPainter *painter, const QWidget *widget, int decorationRegion,
+ DecorationState state)
+
+ Implement this function to paint the border and title decoration
+ for the specified top level \a widget using the given \a painter
+ and decoration \a state. The specified \a decorationRegion is a
+ bitmask of the values described by the DecorationRegion enum.
+
+ Note that \l{Qt for Embedded Linux} expects this function to return true if
+ any of the widget's decorations are repainted; otherwise it should
+ return false.
+
+ \sa region()
+*/
+
+/*!
+ \fn int QDecoration::regionAt(const QWidget *widget, const QPoint &point)
+
+ Returns the type of the first region of the specified top level \a
+ widget containing the given \a point.
+
+ The return value is one of the DecorationRegion enum's values. Use
+ the region() function to retrieve the actual region. If none of
+ the widget's regions contain the point, this function returns \l
+ None.
+
+ \sa region()
+*/
+int QDecoration::regionAt(const QWidget *w, const QPoint &point)
+{
+ int regions[] = {
+ TopLeft, Top, TopRight, Left, Right, BottomLeft, Bottom, BottomRight, // Borders first
+ Menu, Title, Help, Minimize, Normalize, Maximize, Close, // then buttons
+ None
+ };
+
+// char *regions_str[] = {
+// "TopLeft", "Top", "TopRight", "Left", "Right", "BottomLeft", "Bottom", "BottomRight",
+// "Menu", "Title", "Help", "Minimize", "Normalize", "Maximize", "Close",
+// "None"
+// };
+
+ // First check to see if within all regions at all
+ QRegion reg = region(w, w->geometry(), All);
+ if (!reg.contains(point)) {
+ return None;
+ }
+
+ int i = 0;
+ while (regions[i]) {
+ reg = region(w, w->geometry(), regions[i]);
+ if (reg.contains(point)) {
+// qDebug("In region %s", regions_str[i]);
+ return regions[i];
+ }
+ ++i;
+ }
+ return None;
+}
+
+#ifndef QT_NO_MENU
+/*!
+ Builds the system menu for the given top level \a widget, adding
+ \gui Restore, \gui Move, \gui Size, \gui Minimize, \gui Maximize
+ and \gui Close actions to the given \a menu.
+
+ \sa menuTriggered()
+*/
+void QDecoration::buildSysMenu(QWidget *widget, QMenu *menu)
+{
+ QDecorationAction *act = new QDecorationAction(QLatin1String("Restore"),
+ menu, Maximize);
+ act->setEnabled(widget->windowState() & Qt::WindowMaximized);
+ menu->addAction(act);
+ act = new QDecorationAction(QLatin1String("Move"), menu, Move);
+ act->setEnabled(!(widget->windowState() & Qt::WindowMaximized));
+ menu->addAction(act);
+ menu->addAction(new QDecorationAction(QLatin1String("Size"), menu, Resize));
+ act = new QDecorationAction(QLatin1String("Minimize"), menu, Minimize);
+ menu->addAction(act);
+ act = new QDecorationAction(QLatin1String("Maximize"), menu, Maximize);
+ act->setDisabled(widget->windowState() & Qt::WindowMaximized);
+ menu->addAction(act);
+ menu->addSeparator();
+ menu->addAction(new QDecorationAction(QLatin1String("Close"), menu, Close));
+}
+
+/*!
+ This function is called whenever an action in a top level widget's
+ menu is triggered, and simply calls the regionClicked() function
+ passing the \a widget and \a action parameters as arguments.
+
+ \sa buildSysMenu()
+*/
+void QDecoration::menuTriggered(QWidget *widget, QAction *action)
+{
+ QDecorationAction *decAction = static_cast<QDecorationAction *>(action);
+ regionClicked(widget, decAction->reg);
+}
+#endif // QT_NO_MENU
+
+/*!
+ \fn void QDecoration::regionClicked(QWidget *widget, int region)
+
+ Handles the event that the specified \a region in the given top
+ level \a widget is activated by a single click (the \a region
+ parameter is described using the DecorationRegion enum).
+
+ This function is called whenever a region in a top level widget is
+ clicked; the default implementation responds to clicks on items in
+ the system menu, performing the requested actions.
+
+ \sa regionDoubleClicked(), region()
+*/
+void QDecoration::regionClicked(QWidget *widget, int reg)
+{
+ switch(reg) {
+ case Move:
+ startMove(widget);
+ break;
+ case Resize:
+ startResize(widget);
+ break;
+ case Help:
+#ifndef QT_NO_WHATSTHIS
+ if (QWhatsThis::inWhatsThisMode())
+ QWhatsThis::leaveWhatsThisMode();
+ else
+ QWhatsThis::enterWhatsThisMode();
+#endif
+ break;
+ case Close:
+ widget->close();
+ break;
+ case Normalize:
+ widget->showNormal();
+ break;
+ case Maximize:
+ if (widget->windowState() & Qt::WindowMaximized)
+ widget->showNormal();
+ else
+ widget->showMaximized();
+ break;
+ }
+}
+
+/*!
+ \fn void QDecoration::regionDoubleClicked(QWidget *widget, int region)
+
+ Handles the event that the specified \a region in the given top
+ level \a widget is activated by a double click (the region
+ parameter is described using the DecorationRegion enum).
+
+ This function is called whenever a region in a top level widget is
+ double clicked; the default implementation responds to a double
+ click on the widget's title, toggling its size between the maximum
+ and its normal size.
+
+ \sa regionClicked(), region()
+*/
+void QDecoration::regionDoubleClicked(QWidget *widget, int reg)
+{
+ switch(reg)
+ {
+ case Title: {
+ if (widget->windowState() & Qt::WindowMaximized)
+ widget->showNormal();
+ else
+ widget->showMaximized();
+ break;
+ }
+ }
+}
+
+/*!
+ Starts to move the given top level \a widget by making its \l
+ Title region active and grabbing the mouse input.
+
+ \sa startResize()
+*/
+void QDecoration::startMove(QWidget *widget)
+{
+#ifdef QT_NO_QWS_MANAGER
+ Q_UNUSED(widget);
+#else
+ QWSManager *manager = widget->d_func()->topData()->qwsManager;
+ if (manager)
+ manager->startMove();
+#endif
+}
+
+/*!
+ Starts to resize the given top level \a widget by making its \l
+ BottomRight region active and grabbing the mouse input.
+
+ \sa startMove()
+*/
+void QDecoration::startResize(QWidget *widget)
+{
+#ifdef QT_NO_QWS_MANAGER
+ Q_UNUSED(widget);
+#else
+ QWSManager *manager = widget->d_func()->topData()->qwsManager;
+ if (manager)
+ manager->startResize();
+#endif
+}
+
+
+QT_END_NAMESPACE
diff --git a/src/gui/embedded/qdecoration_qws.h b/src/gui/embedded/qdecoration_qws.h
new file mode 100644
index 0000000000..ff4ebac29d
--- /dev/null
+++ b/src/gui/embedded/qdecoration_qws.h
@@ -0,0 +1,124 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QDECORATION_QWS_H
+#define QDECORATION_QWS_H
+
+#include <QtGui/qregion.h>
+#include <QtGui/qwidget.h>
+#include <QtGui/qaction.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+class QPopupMenu;
+class QMenu;
+
+#ifndef QT_NO_ACTION
+class QDecorationAction : public QAction
+{
+public:
+ QDecorationAction(const QString &text, QObject* parent, int region)
+ : QAction(text, parent), reg(region) {}
+ int reg;
+};
+#endif // QT_NO_ACTION
+
+/*
+ Implements decoration styles
+*/
+class Q_GUI_EXPORT QDecoration
+{
+public:
+ QDecoration() {}
+ virtual ~QDecoration() {}
+
+ /* AABBBBBBBBBBCC Items in DecorationRegion:
+ AijjjjjjjklmnC
+ A C A = TopLeft B = Top C = TopRight
+ D E D = Left E = Right
+ D E F = BottomLeft H = Bottom G = BottomRight
+ F G i = Menu j = Title k = Help
+ FFHHHHHHHHHHGG l = Minimize m = Maximize n = Close
+
+ */
+
+ enum DecorationRegion {
+ None = 0x0000000000, All = 0x7fffffff,
+ TopLeft = 0x0000000001, Top = 0x0000000002, TopRight = 0x0000000004,
+ Left = 0x0000000008, Right = 0x0000000010,
+ BottomLeft = 0x0000000020, Bottom = 0x0000000040, BottomRight = 0x0000000080,
+ Borders = 0x00000000ff,
+ Menu = 0x0000000100, Title = 0x0000000200, Help = 0x0000000400,
+ Minimize = 0x0000000800, Maximize = 0x0000001000, Normalize = 0x0000002000,
+ Close = 0x0000004000, Move = 0x0000008000, Resize = 0x0000010000
+ };
+
+ enum DecorationState { Normal = 0x04, Disabled = 0x08, Hover = 0x01, Pressed = 0x02 };
+
+ virtual QRegion region(const QWidget *w, const QRect &rect, int decorationRegion = All ) = 0;
+ QRegion region(const QWidget *w, int decorationRegion = All )
+ { return region(w, w->rect(), decorationRegion); }
+ virtual int regionAt(const QWidget *w, const QPoint &point);
+
+ virtual void regionClicked(QWidget *widget, int region);
+ virtual void regionDoubleClicked(QWidget *widget, int region);
+#ifndef QT_NO_MENU
+ virtual void buildSysMenu(QWidget *widget, QMenu *menu);
+ void menuTriggered(QWidget *widget, QAction *action);
+#endif
+
+ static void startMove(QWidget *widget);
+ static void startResize(QWidget *widget);
+
+ virtual bool paint(QPainter *p, const QWidget *w, int decorationRegion = All,
+ DecorationState state = Normal) = 0;
+
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QDECORATION_QWS_H
diff --git a/src/gui/embedded/qdecorationdefault_qws.cpp b/src/gui/embedded/qdecorationdefault_qws.cpp
new file mode 100644
index 0000000000..6e5314d7f2
--- /dev/null
+++ b/src/gui/embedded/qdecorationdefault_qws.cpp
@@ -0,0 +1,803 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <qapplication.h>
+#include <qwidget.h>
+#include <qpainter.h>
+#include <qpaintengine.h>
+#include <qdrawutil.h>
+#include "qdecorationdefault_qws.h"
+
+QT_BEGIN_NAMESPACE
+
+#if !defined(QT_NO_QWS_DECORATION_DEFAULT) || defined(QT_PLUGIN)
+
+QPixmap *QDecorationDefault::staticHelpPixmap = 0;
+QPixmap *QDecorationDefault::staticMenuPixmap = 0;
+QPixmap *QDecorationDefault::staticClosePixmap = 0;
+QPixmap *QDecorationDefault::staticMinimizePixmap = 0;
+QPixmap *QDecorationDefault::staticMaximizePixmap = 0;
+QPixmap *QDecorationDefault::staticNormalizePixmap = 0;
+
+#ifndef QT_NO_IMAGEFORMAT_XPM
+
+/* XPM */
+static const char * const default_menu_xpm[] = {
+/* width height ncolors chars_per_pixel */
+"16 16 11 1",
+/* colors */
+" c #000000",
+". c #336600",
+"X c #666600",
+"o c #99CC00",
+"O c #999933",
+"+ c #333300",
+"@ c #669900",
+"# c #999900",
+"$ c #336633",
+"% c #666633",
+"& c #99CC33",
+/* pixels */
+"oooooooooooooooo",
+"oooooooooooooooo",
+"ooooo#.++X#ooooo",
+"ooooX Xoooo",
+"oooX XO#% X&oo",
+"oo# Ooo&@O Ooo",
+"oo. Xoo#+ @X Xoo",
+"oo+ OoO+ +O# +oo",
+"oo+ #O+ +## +oo",
+"oo. %@ ++ +. Xoo",
+"oo# O@OO+ #oo",
+"oooX X##$ Ooo",
+"ooooX Xoo",
+"oooo&OX++X#OXooo",
+"oooooooooooooooo",
+"oooooooooooooooo"
+};
+
+static const char * const default_help_xpm[] = {
+"16 16 3 1",
+" s None c None",
+". c #ffffff",
+"X c #707070",
+" ",
+" ",
+" ...... ",
+" ..XXXXXX ",
+" .XX .XX ",
+" .XX .XX ",
+" ..XX ",
+" ..XX ",
+" ..XX ",
+" .XX ",
+" .XX ",
+" .. ",
+" .XX ",
+" .XX ",
+" ",
+" "};
+
+static const char * const default_close_xpm[] = {
+"16 16 3 1",
+" s None c None",
+". c #ffffff",
+"X c #707070",
+" ",
+" ",
+" .X .X ",
+" .XX .XX ",
+" .XX .XX ",
+" .XX .XX ",
+" .XX.XX ",
+" .XXX ",
+" .XXX ",
+" .XX.XX ",
+" .XX .XX ",
+" .XX .XX ",
+" .XX .XX ",
+" .X .X ",
+" ",
+" "};
+
+static const char * const default_maximize_xpm[] = {
+"16 16 3 1",
+" s None c None",
+". c #ffffff",
+"X c #707070",
+" ",
+" ",
+" ........... ",
+" .XXXXXXXXXX ",
+" .X .X ",
+" .X .X ",
+" .X .X ",
+" .X .X ",
+" .X .X ",
+" .X .X ",
+" .X .X ",
+" .X........X ",
+" .XXXXXXXXXX ",
+" ",
+" ",
+" "};
+
+static const char * const default_minimize_xpm[] = {
+"16 16 3 1",
+" s None c None",
+". c #ffffff",
+"X c #707070",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ... ",
+" . X ",
+" .XX ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" "};
+
+static const char * const default_normalize_xpm[] = {
+"16 16 3 1",
+" s None c None",
+". c #ffffff",
+"X c #707070",
+" ",
+" ",
+" ........ ",
+" .XXXXXXXX ",
+" .X .X ",
+" .X .X ",
+" ....X... .X ",
+" .XXXXXXXX .X ",
+" .X .XXXX ",
+" .X .X ",
+" .X .X ",
+" .X......X ",
+" .XXXXXXXX ",
+" ",
+" ",
+" "};
+
+#endif // QT_NO_IMAGEFORMAT_XPM
+
+/*!
+ \class QDecorationDefault
+ \since 4.4
+ \ingroup qws
+ \brief The QDecorationDefault class is a base class providing default window decorations.
+
+ See the documentation for class QDecoration for a detailed
+ description. This subclass of QDecoration provides standard
+ icons for the decoration regions.
+
+ Note that this class is non-portable and only available in
+ \l{Qt for Embedded Linux}.
+ */
+
+/*!
+ Default constructor.
+ */
+QDecorationDefault::QDecorationDefault()
+ : QDecoration()
+{
+ menu_width = 20;
+ help_width = 20;
+ close_width = 20;
+ minimize_width = 20;
+ maximize_width = 20;
+ normalize_width = 20;
+}
+
+/*!
+ The constructor deletes the static pixmaps.
+ */
+QDecorationDefault::~QDecorationDefault()
+{
+ delete staticMenuPixmap;
+ delete staticClosePixmap;
+ delete staticMinimizePixmap;
+ delete staticMaximizePixmap;
+ delete staticNormalizePixmap;
+
+ // This makes it safe to delete and then create a QDecorationDefault
+ staticMenuPixmap = 0;
+ staticClosePixmap = 0;
+ staticMinimizePixmap = 0;
+ staticMaximizePixmap = 0;
+ staticNormalizePixmap = 0;
+}
+
+/*!
+ \fn const char **QDecorationDefault::xpmForRegion(int region)
+
+ Returns a pointer to the X pixmap for the icon specified by
+ \a region. An X pixmap is an ASCII-text-based image. The value
+ of \a region must be one of a subset of the values of enum
+ DecorationRegion. The supported values are \e Help, \e Menu,
+ \e Close, \e Minimize, \e Maximize, and \e Normalize. Other
+ values of \a region cause zero to be returned.
+
+ \sa QDecoration::DecorationRegion
+ */
+const char **QDecorationDefault::xpmForRegion(int reg)
+{
+#ifdef QT_NO_IMAGEFORMAT_XPM
+ Q_UNUSED(reg);
+#else
+ switch(reg)
+ {
+ case Help:
+ return (const char **)default_help_xpm;
+ case Menu:
+ return (const char **)default_menu_xpm;
+ case Close:
+ return (const char **)default_close_xpm;
+ case Minimize:
+ return (const char **)default_minimize_xpm;
+ case Maximize:
+ return (const char **)default_maximize_xpm;
+ case Normalize:
+ return (const char **)default_normalize_xpm;
+ }
+#endif
+ return 0;
+}
+
+/*!
+ \fn QPixmap QDecorationDefault::pixmapFor(const QWidget *widget,
+ int decorationRegion, int &xoff, int &yoff)
+
+ Returns a pointer to the QPixmap for the widget specified by \a widget and
+ \a decorationRegion. The returned QPixmap is constructed from the default
+ X pixmap obtained from xpmForRegion().
+
+ \a xoff and \a yoff specify the offset for the pixmap.
+
+ The value of \a decorationRegion must be one of a subset of the values
+ of enum DecorationRegion. The supported values are \e Help,
+ \e Menu, \e Close, \e Minimize, \e Maximize, and \e Normalize.
+ Other values of \a decorationRegion return 0.
+
+ \sa QDecoration::DecorationRegion
+*/
+QPixmap QDecorationDefault::pixmapFor(const QWidget *widget,
+ int decorationRegion,
+ int &xoff,
+ int &/*yoff*/)
+{
+#ifdef QT_NO_IMAGEFORMAT_XPM
+ Q_UNUSED(widget);
+ Q_UNUSED(decorationRegion);
+ Q_UNUSED(xoff);
+ return QPixmap();
+#else
+ static const char **staticHelpPixmapXPM = 0;
+ static const char **staticMenuPixmapXPM = 0;
+ static const char **staticClosePixmapXPM = 0;
+ static const char **staticMinimizePixmapXPM = 0;
+ static const char **staticMaximizePixmapXPM = 0;
+ static const char **staticNormalizePixmapXPM = 0;
+ const char **xpm;
+
+ // Why don't we just use/extend the enum type...
+
+ if (staticHelpPixmapXPM != (xpm = xpmForRegion(Help)) || !staticHelpPixmap) {
+ staticHelpPixmapXPM = xpm;
+ staticHelpPixmap = new QPixmap(xpm);
+ }
+ if (staticMenuPixmapXPM != (xpm = xpmForRegion(Menu)) || !staticMenuPixmap) {
+ staticMenuPixmapXPM = xpm;
+ staticMenuPixmap = new QPixmap(xpm);
+ }
+ if (staticClosePixmapXPM != (xpm = xpmForRegion(Close)) || !staticClosePixmap) {
+ staticClosePixmapXPM = xpm;
+ staticClosePixmap = new QPixmap(xpm);
+ }
+ if (staticMinimizePixmapXPM != (xpm = xpmForRegion(Minimize)) || !staticMinimizePixmap) {
+ staticMinimizePixmapXPM = xpm;
+ staticMinimizePixmap = new QPixmap(xpm);
+ }
+ if (staticMaximizePixmapXPM != (xpm = xpmForRegion(Maximize)) || !staticMaximizePixmap) {
+ staticMaximizePixmapXPM = xpm;
+ staticMaximizePixmap = new QPixmap(xpm);
+ }
+ if (staticNormalizePixmapXPM != (xpm = xpmForRegion(Normalize)) || !staticNormalizePixmap) {
+ staticNormalizePixmapXPM = xpm;
+ staticNormalizePixmap = new QPixmap(xpm);
+ }
+
+ const QPixmap *pm = 0;
+
+ switch (decorationRegion) {
+ case Help:
+ pm = staticHelpPixmap;
+ break;
+ case Menu:
+ if (!widget->windowIcon().isNull())
+ return widget->windowIcon().pixmap(16,16); //##### QIcon::pixmap() needs a size !!!!!!"
+ if (!pm) {
+ xoff = 1;
+ pm = staticMenuPixmap;
+ }
+ break;
+ case Close:
+ pm = staticClosePixmap;
+ break;
+ case Maximize:
+ pm = staticMaximizePixmap;
+ break;
+ case Normalize:
+ pm = staticNormalizePixmap;
+ break;
+ case Minimize:
+ pm = staticMinimizePixmap;
+ break;
+ default:
+ break;
+ }
+ return *pm;
+#endif
+}
+
+/*!
+ \fn int QDecorationDefault::titleBarHeight(const QWidget *widget)
+
+ Returns the title bar height in pixels for the given \a widget. It is the
+ greater of 20, or the sum of the application font's line spacing value
+ plus a border width fudge factor.
+*/
+int QDecorationDefault::titleBarHeight(const QWidget *)
+{
+ return qMax(20, QApplication::fontMetrics().lineSpacing() + BORDER_WIDTH);
+}
+
+/*!
+ Returns the region specified by \a decorationRegion for the
+ top-level \a widget. \a rect specifies the rectangle the decoration
+ wraps. The value of \a decorationRegion is a combination of the
+ bitmask values of enum DecorationRegion.
+ */
+QRegion QDecorationDefault::region(const QWidget *widget,
+ const QRect &rect,
+ int decorationRegion)
+{
+ Qt::WindowFlags flags = widget->windowFlags();
+ bool hasBorder = !widget->isMaximized();
+ bool hasTitle = flags & Qt::WindowTitleHint;
+ bool hasSysMenu = flags & Qt::WindowSystemMenuHint;
+ bool hasContextHelp = flags & Qt::WindowContextHelpButtonHint;
+ bool hasMinimize = flags & Qt::WindowMinimizeButtonHint;
+ bool hasMaximize = flags & Qt::WindowMaximizeButtonHint;
+ int state = widget->windowState();
+ bool isMinimized = state & Qt::WindowMinimized;
+ bool isMaximized = state & Qt::WindowMaximized;
+
+ int titleHeight = hasTitle ? titleBarHeight(widget) : 0;
+ int bw = hasBorder ? BORDER_WIDTH : 0;
+ int bbw = hasBorder ? BOTTOM_BORDER_WIDTH : 0;
+
+ QRegion region;
+ switch (decorationRegion) {
+ case All: {
+ QRect r(rect.left() - bw,
+ rect.top() - titleHeight - bw,
+ rect.width() + 2 * bw,
+ rect.height() + titleHeight + bw + bbw);
+ region = r;
+ region -= rect;
+ }
+ break;
+
+ case Title: {
+ QRect r(rect.left()
+ + (hasSysMenu ? menu_width : 0),
+ rect.top() - titleHeight,
+ rect.width()
+ - (hasSysMenu ? menu_width : 0)
+ - close_width
+ - (hasMaximize ? maximize_width : 0)
+ - (hasMinimize ? minimize_width : 0)
+ - (hasContextHelp ? help_width : 0),
+
+ titleHeight);
+ if (r.width() > 0)
+ region = r;
+ }
+ break;
+
+ case Top: {
+ QRect r(rect.left() + CORNER_GRAB,
+ rect.top() - titleHeight - bw,
+ rect.width() - 2 * CORNER_GRAB,
+ bw);
+ region = r;
+ }
+ break;
+
+ case Left: {
+ QRect r(rect.left() - bw,
+ rect.top() - titleHeight + CORNER_GRAB,
+ bw,
+ rect.height() + titleHeight - 2 * CORNER_GRAB);
+ region = r;
+ }
+ break;
+
+ case Right: {
+ QRect r(rect.right() + 1,
+ rect.top() - titleHeight + CORNER_GRAB,
+ bw,
+ rect.height() + titleHeight - 2 * CORNER_GRAB);
+ region = r;
+ }
+ break;
+
+ case Bottom: {
+ QRect r(rect.left() + CORNER_GRAB,
+ rect.bottom() + 1,
+ rect.width() - 2 * CORNER_GRAB,
+ bw);
+ region = r;
+ }
+ break;
+
+ case TopLeft: {
+ QRect r1(rect.left() - bw,
+ rect.top() - bw - titleHeight,
+ CORNER_GRAB + bw,
+ bw);
+
+ QRect r2(rect.left() - bw,
+ rect.top() - bw - titleHeight,
+ bw,
+ CORNER_GRAB + bw);
+
+ region = QRegion(r1) + r2;
+ }
+ break;
+
+ case TopRight: {
+ QRect r1(rect.right() - CORNER_GRAB,
+ rect.top() - bw - titleHeight,
+ CORNER_GRAB + bw,
+ bw);
+
+ QRect r2(rect.right() + 1,
+ rect.top() - bw - titleHeight,
+ bw,
+ CORNER_GRAB + bw);
+
+ region = QRegion(r1) + r2;
+ }
+ break;
+
+ case BottomLeft: {
+ QRect r1(rect.left() - bw,
+ rect.bottom() + 1,
+ CORNER_GRAB + bw,
+ bw);
+
+ QRect r2(rect.left() - bw,
+ rect.bottom() - CORNER_GRAB,
+ bw,
+ CORNER_GRAB + bw);
+ region = QRegion(r1) + r2;
+ }
+ break;
+
+ case BottomRight: {
+ QRect r1(rect.right() - CORNER_GRAB,
+ rect.bottom() + 1,
+ CORNER_GRAB + bw,
+ bw);
+
+ QRect r2(rect.right() + 1,
+ rect.bottom() - CORNER_GRAB,
+ bw,
+ CORNER_GRAB + bw);
+ region = QRegion(r1) + r2;
+ }
+ break;
+
+ case Menu: {
+ if (hasSysMenu) {
+ region = QRect(rect.left(), rect.top() - titleHeight,
+ menu_width, titleHeight);
+ }
+ }
+ break;
+
+ case Help: {
+ if (hasContextHelp) {
+ QRect r(rect.right()
+ - close_width
+ - (hasMaximize ? maximize_width : 0)
+ - (hasMinimize ? minimize_width : 0)
+ - help_width + 1, rect.top() - titleHeight,
+ help_width, titleHeight);
+ if (r.left() > rect.left() + titleHeight)
+ region = r;
+ }
+ }
+ break;
+
+
+ case Minimize: {
+ if (hasMinimize && !isMinimized) {
+ QRect r(rect.right() - close_width
+ - (hasMaximize ? maximize_width : 0)
+ - minimize_width + 1, rect.top() - titleHeight,
+ minimize_width, titleHeight);
+ if (r.left() > rect.left() + titleHeight)
+ region = r;
+ }
+ }
+ break;
+
+ case Maximize: {
+ if (hasMaximize && !isMaximized) {
+ QRect r(rect.right() - close_width - maximize_width + 1,
+ rect.top() - titleHeight, maximize_width, titleHeight);
+ if (r.left() > rect.left() + titleHeight)
+ region = r;
+ }
+ }
+ break;
+
+ case Normalize: {
+ if (hasMinimize && isMinimized) {
+ QRect r(rect.right() - close_width
+ - (hasMaximize ? maximize_width : 0)
+ - minimize_width + 1, rect.top() - titleHeight,
+ minimize_width, titleHeight);
+ if (r.left() > rect.left() + titleHeight)
+ region = r;
+ } else if (hasMaximize && isMaximized) {
+ QRect r(rect.right() - close_width - maximize_width + 1,
+ rect.top() - titleHeight, maximize_width, titleHeight);
+ if (r.left() > rect.left() + titleHeight)
+ region = r;
+ }
+ }
+ break;
+
+ case Close: {
+ QRect r(rect.right() - close_width + 1, rect.top() - titleHeight,
+ close_width, titleHeight);
+ if (r.left() > rect.left() + titleHeight)
+ region = r;
+ }
+ break;
+
+ default: {
+ int i = 1;
+ while (i) {
+ if (i & decorationRegion)
+ region += this->region(widget, rect, i);
+ i <<= 1;
+ }
+ }
+ break;
+ }
+
+ return region;
+}
+
+/*!
+ Paints the border and title decoration for the top-level \a widget
+ using the \a painter provided and the decoration \a state. The value
+ of \a decorationRegion is a combination of the bitmask values of
+ enum DecorationRegion.
+
+ Note that Qt for Embedded Linux expects this function to return true if any of
+ the widget's decorations are repainted; otherwise it returns false.
+ */
+bool QDecorationDefault::paint(QPainter *painter,
+ const QWidget *widget,
+ int decorationRegion,
+ DecorationState state)
+{
+ if (decorationRegion == None)
+ return false;
+
+ const QRect titleRect = QDecoration::region(widget, Title).boundingRect();
+ const QPalette pal = QApplication::palette();
+ int titleHeight = titleRect.height();
+ int titleWidth = titleRect.width();
+ QRegion oldClipRegion = painter->clipRegion();
+
+
+ Qt::WindowFlags flags = widget->windowFlags();
+ bool hasBorder = !widget->isMaximized();
+ bool hasTitle = flags & Qt::WindowTitleHint;
+ bool hasSysMenu = flags & Qt::WindowSystemMenuHint;
+ bool hasContextHelp = flags & Qt::WindowContextHelpButtonHint;
+ bool hasMinimize = flags & Qt::WindowMinimizeButtonHint;
+ bool hasMaximize = flags & Qt::WindowMaximizeButtonHint;
+
+ bool paintAll = (decorationRegion == int(All));
+ bool handled = false;
+
+ bool porterDuff = painter->paintEngine()->hasFeature(QPaintEngine::PorterDuff);
+
+ if ((paintAll || decorationRegion & Borders) && state == Normal && hasBorder) {
+ if (hasTitle) { // reduce flicker
+ QRect rect(widget->rect());
+ QRect r(rect.left(), rect.top() - titleHeight,
+ rect.width(), titleHeight);
+ painter->setClipRegion(oldClipRegion - r);
+ }
+ QRect br = QDecoration::region(widget).boundingRect();
+ if (porterDuff)
+ painter->setCompositionMode(QPainter::CompositionMode_Source);
+ qDrawWinPanel(painter, br.x(), br.y(), br.width(),
+ br.height(), pal, false,
+ &pal.brush(QPalette::Window));
+ if (porterDuff)
+ painter->setCompositionMode(QPainter::CompositionMode_SourceOver);
+ handled |= true;
+ }
+
+ if ((paintAll || decorationRegion & Title && titleWidth > 0) && state == Normal && hasTitle) {
+ painter->setClipRegion(oldClipRegion);
+ QBrush titleBrush;
+ QPen titlePen;
+
+ if (widget == qApp->activeWindow()) {
+ titleBrush = pal.brush(QPalette::Highlight);
+ titlePen = pal.color(QPalette::HighlightedText);
+ } else {
+ titleBrush = pal.brush(QPalette::Window);
+ titlePen = pal.color(QPalette::Text);
+ }
+
+ if (porterDuff)
+ painter->setCompositionMode(QPainter::CompositionMode_Source);
+ qDrawShadePanel(painter,
+ titleRect.x(), titleRect.y(), titleRect.width(), titleRect.height(),
+ pal, true, 1, &titleBrush);
+ if (porterDuff)
+ painter->setCompositionMode(QPainter::CompositionMode_SourceOver);
+
+ painter->setPen(titlePen);
+ painter->drawText(titleRect.x() + 4, titleRect.y(),
+ titleRect.width() - 8, titleRect.height(),
+ Qt::AlignVCenter, windowTitleFor(widget));
+ handled |= true;
+ }
+
+ if (state != Hover) {
+ painter->setClipRegion(oldClipRegion);
+ if ((paintAll || decorationRegion & Menu) && hasSysMenu) {
+ paintButton(painter, widget, Menu, state, pal);
+ handled |= true;
+ }
+
+ if ((paintAll || decorationRegion & Help) && hasContextHelp) {
+ paintButton(painter, widget, Help, state, pal);
+ handled |= true;
+ }
+
+ if ((paintAll || decorationRegion & Minimize) && hasMinimize) {
+ paintButton(painter, widget, Minimize, state, pal);
+ handled |= true;
+ }
+
+ if ((paintAll || decorationRegion & Maximize) && hasMaximize) {
+ paintButton(painter, widget,
+ ((widget->windowState() & Qt::WindowMaximized)? Normalize : Maximize),
+ state, pal);
+ handled |= true;
+ }
+
+ if (paintAll || decorationRegion & Close) {
+ paintButton(painter, widget, Close, state, pal);
+ handled |= true;
+ }
+ }
+ return handled;
+}
+
+/*!
+ \fn void QDecorationDefault::paintButton(QPainter *painter, const
+ QWidget *widget, int buttonRegion, DecorationState state,
+ const QPalette &palette)
+
+ Paints a region of the top-level \a widget. The region is
+ painted in the specified decoration \a state using the
+ \a painter and \a palette provided. The region to be painted is specified
+ by \a buttonRegion, which is a combination of the bitmask values of
+ DecorationRegion. If the value of \a buttonRegion is one of \e Help,
+ \e Menu, \e Close, \e Minimize, \e Maximize, and \e Normalize, the
+ button pixmap for that region is painted.
+
+ \sa pixmapFor()
+ */
+void QDecorationDefault::paintButton(QPainter *painter,
+ const QWidget *widget,
+ int buttonRegion,
+ DecorationState state,
+ const QPalette &pal)
+{
+ int xoff = 2;
+ int yoff = 2;
+
+ const QPixmap pm = pixmapFor(widget, buttonRegion, xoff, yoff);
+ QRect brect(QDecoration::region(widget, buttonRegion).boundingRect());
+ bool porterDuff = painter->paintEngine()->hasFeature(QPaintEngine::PorterDuff);
+
+ if (state & QDecoration::Pressed) {
+ if (porterDuff)
+ painter->setCompositionMode(QPainter::CompositionMode_Source);
+ qDrawWinPanel(painter, brect, pal, true, &pal.brush(QPalette::Window));
+ if (porterDuff)
+ painter->setCompositionMode(QPainter::CompositionMode_SourceOver);
+ ++xoff;
+ ++yoff;
+ } else {
+ painter->fillRect(brect, pal.brush(QPalette::Window));
+ }
+
+ if (!pm.isNull())
+ painter->drawPixmap(brect.x() + xoff, brect.y() + yoff, pm);
+}
+
+extern QString qt_setWindowTitle_helperHelper(const QString&, const QWidget*);
+
+/*!
+ \internal
+ */
+QString QDecorationDefault::windowTitleFor(const QWidget *widget) const
+{
+ return qt_setWindowTitle_helperHelper(widget->windowTitle(), widget);
+}
+
+#endif // QT_NO_QWS_DECORATION_DEFAULT
+
+QT_END_NAMESPACE
diff --git a/src/gui/embedded/qdecorationdefault_qws.h b/src/gui/embedded/qdecorationdefault_qws.h
new file mode 100644
index 0000000000..4cc3703484
--- /dev/null
+++ b/src/gui/embedded/qdecorationdefault_qws.h
@@ -0,0 +1,101 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QDECORATIONDEFAULT_QWS_H
+#define QDECORATIONDEFAULT_QWS_H
+
+#include <QtGui/qdecoration_qws.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+#if !defined(QT_NO_QWS_DECORATION_DEFAULT) || defined(QT_PLUGIN)
+
+#define CORNER_GRAB 16
+#define BORDER_WIDTH 4
+#define BOTTOM_BORDER_WIDTH BORDER_WIDTH
+
+class Q_GUI_EXPORT QDecorationDefault : public QDecoration
+{
+public:
+ QDecorationDefault();
+ virtual ~QDecorationDefault();
+
+ virtual QRegion region(const QWidget *widget, const QRect &rect, int decorationRegion = All);
+ virtual bool paint(QPainter *painter, const QWidget *widget, int decorationRegion = All,
+ DecorationState state = Normal);
+
+protected:
+ virtual int titleBarHeight(const QWidget *widget);
+
+ virtual void paintButton(QPainter *painter, const QWidget *widget, int buttonRegion,
+ DecorationState state, const QPalette &pal);
+ virtual QPixmap pixmapFor(const QWidget *widget, int decorationRegion, int &xoff, int &yoff);
+ virtual const char **xpmForRegion(int region);
+
+ QString windowTitleFor(const QWidget *widget) const;
+
+ int menu_width;
+ int help_width;
+ int close_width;
+ int minimize_width;
+ int maximize_width;
+ int normalize_width;
+
+private:
+ static QPixmap *staticHelpPixmap;
+ static QPixmap *staticMenuPixmap;
+ static QPixmap *staticClosePixmap;
+ static QPixmap *staticMinimizePixmap;
+ static QPixmap *staticMaximizePixmap;
+ static QPixmap *staticNormalizePixmap;
+
+};
+
+
+QT_END_NAMESPACE
+#endif // QT_NO_QWS_DECORATION_DEFAULT
+QT_END_HEADER
+
+#endif // QDECORATIONDEFAULT_QWS_H
diff --git a/src/gui/embedded/qdecorationfactory_qws.cpp b/src/gui/embedded/qdecorationfactory_qws.cpp
new file mode 100644
index 0000000000..999e346505
--- /dev/null
+++ b/src/gui/embedded/qdecorationfactory_qws.cpp
@@ -0,0 +1,156 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qdecorationfactory_qws.h"
+#include "qdecorationplugin_qws.h"
+#include "private/qfactoryloader_p.h"
+#include "qmutex.h"
+
+#include "qapplication.h"
+#include "qdecorationdefault_qws.h"
+#include "qdecorationwindows_qws.h"
+#include "qdecorationstyled_qws.h"
+
+QT_BEGIN_NAMESPACE
+
+#ifndef QT_NO_LIBRARY
+Q_GLOBAL_STATIC_WITH_ARGS(QFactoryLoader, loader,
+ (QDecorationFactoryInterface_iid,
+ QLatin1String("/decorations"), Qt::CaseInsensitive))
+#endif
+
+
+
+/*!
+ \class QDecorationFactory
+ \ingroup qws
+ \ingroup appearance
+
+ \brief The QDecorationFactory class creates window decorations in
+ Qt for Embedded Linux.
+
+ Note that this class is only available in \l{Qt for Embedded Linux}.
+
+ QDecorationFactory is used to detect and instantiate the available
+ decorations, allowing \l{Qt for Embedded Linux} to load the preferred
+ decoration into the application at runtime. The create() function
+ returns a QDecoration object representing the decoration
+ identified by a given key. The valid keys (i.e. the supported
+ decorations) can be retrieved using the keys() function.
+
+ \l{Qt for Embedded Linux} provides three built-in decorations: \c Default,
+ \c Styled and \c Windows. In addition, custom decorations can be
+ added using Qt's \l {How to Create Qt Plugins}{plugin mechanism},
+ i.e. by subclassing the QDecoration class and creating a mouse
+ driver plugin (QDecorationPlugin).
+
+ \sa QDecoration, QDecorationPlugin
+*/
+
+/*!
+ Creates the decoration specified by the given \a key. Note that
+ the keys are case-insensitive.
+
+ \sa keys()
+*/
+
+QDecoration *QDecorationFactory::create(const QString& key)
+{
+ QDecoration *ret = 0;
+ QString decoration = key.toLower();
+#ifndef QT_NO_QWS_DECORATION_DEFAULT
+ if (decoration == QLatin1String("default"))
+ ret = new QDecorationDefault;
+ else
+#endif
+#ifndef QT_NO_QWS_DECORATION_WINDOWS
+ if (decoration == QLatin1String("windows"))
+ ret = new QDecorationWindows;
+ else
+#endif
+#ifndef QT_NO_QWS_DECORATION_STYLED
+ if (decoration == QLatin1String("styled"))
+ ret = new QDecorationStyled;
+ else
+#endif
+ { } // Keep these here - they make the #ifdefery above work
+#ifndef QT_NO_LIBRARY
+ if (!ret) {
+ if (QDecorationFactoryInterface *factory = qobject_cast<QDecorationFactoryInterface*>(loader()->instance(decoration))) {
+ ret = factory->create(decoration);
+ }
+ }
+#endif
+ return ret;
+}
+
+/*!
+ Returns the list of valid keys, i.e., the available decorations.
+
+ \sa create()
+*/
+QStringList QDecorationFactory::keys()
+{
+ QStringList list;
+#ifndef QT_NO_QWS_DECORATION_STYLED
+ list << QLatin1String("Styled");
+#endif
+#ifndef QT_NO_QWS_DECORATION_DEFAULT
+ list << QLatin1String("Default");
+#endif
+#ifndef QT_NO_QWS_DECORATION_WINDOWS
+ list << QLatin1String("Windows");
+#endif
+
+#if !defined(Q_OS_WIN32) || defined(QT_MAKEDLL)
+#ifndef QT_NO_LIBRARY
+ QStringList plugins = loader()->keys();
+ for (int i = 0; i < plugins.size(); ++i) {
+ if (!list.contains(plugins.at(i)))
+ list += plugins.at(i);
+ }
+#endif //QT_NO_LIBRARY
+#endif //QT_MAKEDLL
+
+ return list;
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/embedded/qdecorationfactory_qws.h b/src/gui/embedded/qdecorationfactory_qws.h
new file mode 100644
index 0000000000..7397c78826
--- /dev/null
+++ b/src/gui/embedded/qdecorationfactory_qws.h
@@ -0,0 +1,66 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QDECORATIONFACTORY_QWS_H
+#define QDECORATIONFACTORY_QWS_H
+
+#include <QtCore/qstringlist.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+class QDecoration;
+
+class Q_GUI_EXPORT QDecorationFactory
+{
+public:
+ static QStringList keys();
+ static QDecoration *create(const QString&);
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QDECORATIONFACTORY_QWS_H
diff --git a/src/gui/embedded/qdecorationplugin_qws.cpp b/src/gui/embedded/qdecorationplugin_qws.cpp
new file mode 100644
index 0000000000..eb37e2143c
--- /dev/null
+++ b/src/gui/embedded/qdecorationplugin_qws.cpp
@@ -0,0 +1,116 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qdecorationplugin_qws.h"
+#include "qdecoration_qws.h"
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QDecorationPlugin
+ \ingroup qws
+ \ingroup plugins
+
+ \brief The QDecorationPlugin class is an abstract base class for
+ window decoration plugins in Qt for Embedded Linux.
+
+ Note that this class is only available in \l{Qt for Embedded Linux}.
+
+ \l{Qt for Embedded Linux} provides three ready-made decoration styles: \c
+ Default, \c Styled and \c Windows. Custom decorations can be
+ implemented by subclassing the QDecoration class and creating a
+ decoration plugin.
+
+ A decoration plugin can be created by subclassing
+ QDecorationPlugin and implementing the pure virtual keys() and
+ create() functions. By exporting the derived class using the
+ Q_EXPORT_PLUGIN2() macro, the default implementation of the
+ QDecorationFactory class will automatically detect the plugin and
+ load the driver into the application at run-time. See \l{How to
+ Create Qt Plugins} for details.
+
+ To actually apply a decoration, use the
+ QApplication::qwsSetDecoration() function.
+
+ \sa QDecoration, QDecorationFactory
+*/
+
+/*!
+ \fn QStringList QDecorationPlugin::keys() const
+
+ Returns the list of valid keys, i.e., the decorations supported by
+ this plugin.
+
+ \sa create()
+*/
+
+/*!
+ \fn QDecoration *QDecorationPlugin::create(const QString &key)
+
+ Creates a decoration matching the given \a key. Note that keys are
+ case-insensitive.
+
+ \sa keys()
+*/
+
+/*!
+ Constructs a decoration plugin with the given \a parent.
+
+ Note that this constructor is invoked automatically by the
+ Q_EXPORT_PLUGIN2() macro, so there is no need for calling it
+ explicitly.
+*/
+QDecorationPlugin::QDecorationPlugin(QObject *parent)
+ : QObject(parent)
+{
+}
+
+/*!
+ Destroys the decoration plugin.
+
+ Note that Qt destroys a plugin automatically when it is no longer
+ used, so there is no need for calling the destructor explicitly.
+*/
+QDecorationPlugin::~QDecorationPlugin()
+{
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/embedded/qdecorationplugin_qws.h b/src/gui/embedded/qdecorationplugin_qws.h
new file mode 100644
index 0000000000..c58aaab4f1
--- /dev/null
+++ b/src/gui/embedded/qdecorationplugin_qws.h
@@ -0,0 +1,80 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QDECORATIONPLUGIN_QWS_H
+#define QDECORATIONPLUGIN_QWS_H
+
+#include <QtCore/qplugin.h>
+#include <QtCore/qfactoryinterface.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+class QDecoration;
+
+struct Q_GUI_EXPORT QDecorationFactoryInterface : public QFactoryInterface
+{
+ virtual QDecoration *create(const QString &key) = 0;
+};
+
+#define QDecorationFactoryInterface_iid "com.trolltech.Qt.QDecorationFactoryInterface"
+Q_DECLARE_INTERFACE(QDecorationFactoryInterface, QDecorationFactoryInterface_iid)
+
+class Q_GUI_EXPORT QDecorationPlugin : public QObject, public QDecorationFactoryInterface
+{
+ Q_OBJECT
+ Q_INTERFACES(QDecorationFactoryInterface:QFactoryInterface)
+ public:
+ explicit QDecorationPlugin(QObject *parent = 0);
+ ~QDecorationPlugin();
+
+ virtual QStringList keys() const = 0;
+ virtual QDecoration *create(const QString &key) = 0;
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QDECORATIONPLUGIN_QWS_H
diff --git a/src/gui/embedded/qdecorationstyled_qws.cpp b/src/gui/embedded/qdecorationstyled_qws.cpp
new file mode 100644
index 0000000000..847881b5c8
--- /dev/null
+++ b/src/gui/embedded/qdecorationstyled_qws.cpp
@@ -0,0 +1,313 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <qapplication.h>
+#include <qwidget.h>
+#include <qpainter.h>
+#include <qdrawutil.h>
+#include "qdecorationstyled_qws.h"
+#include "qstyle.h"
+#include "qstyleoption.h"
+#include "qpaintengine.h"
+
+QT_BEGIN_NAMESPACE
+
+#if !defined(QT_NO_QWS_DECORATION_STYLED) || defined(QT_PLUGIN)
+
+QDecorationStyled::QDecorationStyled()
+ : QDecorationDefault()
+{
+}
+
+QDecorationStyled::~QDecorationStyled()
+{
+}
+
+int QDecorationStyled::titleBarHeight(const QWidget *widget)
+{
+ QStyleOptionTitleBar opt;
+ opt.subControls = QStyle::SC_TitleBarLabel
+ | QStyle::SC_TitleBarSysMenu
+ | QStyle::SC_TitleBarNormalButton
+ | QStyle::SC_TitleBarContextHelpButton
+ | QStyle::SC_TitleBarMinButton
+ | QStyle::SC_TitleBarMaxButton
+ | QStyle::SC_TitleBarCloseButton;
+ opt.titleBarFlags = widget->windowFlags();
+ opt.direction = QApplication::layoutDirection();
+ opt.text = windowTitleFor(widget);
+ opt.icon = widget->windowIcon();
+ opt.rect = widget->rect();
+
+ QStyle *style = QApplication::style();
+ if (!style)
+ return 18;
+
+ return style->pixelMetric(QStyle::PM_TitleBarHeight, &opt, 0);
+}
+
+bool QDecorationStyled::paint(QPainter *painter, const QWidget *widget, int decorationRegion,
+ DecorationState state)
+{
+ if (decorationRegion == None)
+ return false;
+
+ bool isActive = (widget == qApp->activeWindow());
+ QPalette pal = qApp->palette();
+ //ideally, the difference between Active and Inactive should be enough, so we shouldn't need to test this
+ if (!isActive) {
+ //pal.setCurrentColorGroup(QPalette::Disabled); //Can't do this either, because of palette limitations
+ //copied from Q3TitleBar:
+ 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));
+ }
+
+ Qt::WindowFlags flags = widget->windowFlags();
+ bool hasBorder = !widget->isMaximized();
+ bool hasTitle = flags & Qt::WindowTitleHint;
+ bool hasSysMenu = flags & Qt::WindowSystemMenuHint;
+ bool hasContextHelp = flags & Qt::WindowContextHelpButtonHint;
+ bool hasMinimize = flags & Qt::WindowMinimizeButtonHint;
+ bool hasMaximize = flags & Qt::WindowMaximizeButtonHint;
+
+ bool paintAll = (DecorationRegion(decorationRegion) == All);
+ bool handled = false;
+
+ QStyle *style = QApplication::style();
+
+ // In the case of a borderless title bar, the title bar must be expanded one
+ // borderWidth to the left, right and up.
+ bool noTitleBorder = style->styleHint(QStyle::SH_TitleBar_NoBorder, 0, widget);
+ int borderWidth = style->pixelMetric(QStyle::PM_MDIFrameWidth, 0, 0);
+ int titleHeight = titleBarHeight(widget) + (noTitleBorder ? borderWidth : 0);
+ int titleExtra = noTitleBorder ? borderWidth : 0;
+
+ if ((paintAll || decorationRegion & Borders) && state == Normal && hasBorder) {
+ QRegion newClip = painter->clipRegion();
+ if (hasTitle) { // reduce flicker
+ QRect rect(widget->rect());
+ QRect r(rect.left() - titleExtra, rect.top() - titleHeight,
+ rect.width() + 2 * titleExtra, titleHeight);
+ newClip -= r;
+ }
+ if (!newClip.isEmpty()) {
+ QRect br = QDecoration::region(widget).boundingRect();
+ painter->save();
+ painter->setClipRegion(newClip);
+
+ QStyleOptionFrame opt;
+ opt.palette = pal;
+ opt.rect = br;
+ opt.lineWidth = borderWidth;
+
+ if (isActive)
+ opt.state |= QStyle::State_Active;
+ bool porterDuff = painter->paintEngine()->hasFeature(QPaintEngine::PorterDuff);
+ if (porterDuff)
+ painter->setCompositionMode(QPainter::CompositionMode_Source);
+ painter->fillRect(br, pal.window());
+ if (porterDuff)
+ painter->setCompositionMode(QPainter::CompositionMode_SourceOver);
+ style->drawPrimitive(QStyle::PE_FrameWindow, &opt, painter, 0);
+ painter->restore();
+
+ decorationRegion &= (~Borders);
+ handled |= true;
+ }
+ }
+
+ if (hasTitle) {
+ painter->save();
+
+ QStyleOptionTitleBar opt;
+ opt.subControls = (decorationRegion & Title
+ ? QStyle::SC_TitleBarLabel : QStyle::SubControl(0))
+ | (decorationRegion & Menu
+ ? QStyle::SC_TitleBarSysMenu : QStyle::SubControl(0))
+ | (decorationRegion & Help
+ ? QStyle::SC_TitleBarContextHelpButton : QStyle::SubControl(0))
+ | (decorationRegion & Minimize
+ ? QStyle::SC_TitleBarMinButton : QStyle::SubControl(0))
+ | (decorationRegion & Maximize
+ ? QStyle::SC_TitleBarMaxButton : QStyle::SubControl(0))
+ | (decorationRegion & (Minimize | Maximize)
+ ? QStyle::SC_TitleBarNormalButton : QStyle::SubControl(0))
+ | (decorationRegion & Close
+ ? QStyle::SC_TitleBarCloseButton : QStyle::SubControl(0));
+ opt.titleBarFlags = widget->windowFlags();
+ opt.titleBarState = widget->windowState();
+ if (isActive)
+ opt.titleBarState |= QStyle::State_Active;
+ opt.text = windowTitleFor(widget);
+ opt.icon = widget->windowIcon();
+ opt.palette = pal;
+ opt.rect = QRect(widget->rect().x() - titleExtra, -titleHeight,
+ widget->rect().width() + 2 * titleExtra, titleHeight);
+
+ if (paintAll) {
+ painter->setClipRegion(opt.rect);
+ } else {
+ const QRect widgetRect = widget->rect();
+ QRegion newClip = opt.rect;
+ if (!(decorationRegion & Menu) && hasSysMenu)
+ newClip -= region(widget, widgetRect, Menu);
+ if (!(decorationRegion & Title) && hasTitle)
+ newClip -= region(widget, widgetRect, Title);
+ if (!(decorationRegion & Help) && hasContextHelp)
+ newClip -= region(widget, widgetRect, Help);
+ if (!(decorationRegion & Minimize) && hasMinimize)
+ newClip -= region(widget, widgetRect, Minimize);
+ if (!(decorationRegion & Maximize) && hasMaximize)
+ newClip -= region(widget, widgetRect, Maximize);
+ if (!(decorationRegion & (Minimize | Maximize)) && (hasMaximize | hasMinimize))
+ newClip -= region(widget, widgetRect, Normal);
+ if (!(decorationRegion & Close))
+ newClip -= region(widget, widgetRect, Close);
+ painter->setClipRegion(newClip);
+ }
+
+ if (state == Pressed)
+ opt.activeSubControls = opt.subControls;
+
+ style->drawComplexControl(QStyle::CC_TitleBar, &opt, painter, 0);
+ painter->restore();
+
+ decorationRegion &= ~(Title | Menu | Help | Normalize | Minimize | Maximize | Close);
+ handled |= true;
+ }
+
+ return handled;
+}
+
+QRegion QDecorationStyled::region(const QWidget *widget, const QRect &rect, int decorationRegion)
+{
+ QStyle *style = QApplication::style();
+
+ // In the case of a borderless title bar, the title bar must be expanded one
+ // borderWidth to the left, right and up.
+ bool noTitleBorder = style->styleHint(QStyle::SH_TitleBar_NoBorder, 0, widget);
+ int borderWidth = style->pixelMetric(QStyle::PM_MDIFrameWidth, 0, 0);
+ int titleHeight = titleBarHeight(widget) + (noTitleBorder ? borderWidth : 0);
+ int titleExtra = noTitleBorder ? borderWidth : 0;
+
+ QRect inside = QRect(rect.x() - titleExtra, rect.top() - titleHeight,
+ rect.width() + 2 * titleExtra, titleHeight);
+
+ Qt::WindowFlags flags = widget->windowFlags();
+ bool hasSysMenu = flags & Qt::WindowSystemMenuHint;
+ bool hasContextHelp = flags & Qt::WindowContextHelpButtonHint;
+ bool hasMinimize = flags & Qt::WindowMinimizeButtonHint;
+ bool hasMaximize = flags & Qt::WindowMaximizeButtonHint;
+
+ QStyleOptionTitleBar opt;
+ opt.subControls = QStyle::SC_TitleBarLabel
+ | QStyle::SC_TitleBarSysMenu
+ | QStyle::SC_TitleBarNormalButton
+ | QStyle::SC_TitleBarMinButton
+ | QStyle::SC_TitleBarMaxButton
+ | QStyle::SC_TitleBarCloseButton;
+ opt.titleBarFlags = widget->windowFlags();
+ opt.direction = QApplication::layoutDirection();
+ opt.text = windowTitleFor(widget);
+ opt.icon = widget->windowIcon();
+ opt.rect = inside;
+
+ QRegion region;
+ switch (decorationRegion) {
+ case Title:
+ region = style->subControlRect(QStyle::CC_TitleBar, &opt,
+ QStyle::SC_TitleBarLabel, 0);
+ break;
+ case Menu:
+ if (hasSysMenu)
+ region = style->subControlRect(QStyle::CC_TitleBar, &opt,
+ QStyle::SC_TitleBarSysMenu, 0);
+ break;
+ case Help:
+ if (hasContextHelp)
+ region = style->subControlRect(QStyle::CC_TitleBar, &opt,
+ QStyle::SC_TitleBarContextHelpButton,
+ 0);
+ break;
+ case Normalize:
+ if (hasMaximize | hasMinimize)
+ region = style->subControlRect(QStyle::CC_TitleBar, &opt,
+ QStyle::SC_TitleBarNormalButton,
+ 0);
+ break;
+ case Minimize:
+ if (hasMinimize)
+ region = style->subControlRect(QStyle::CC_TitleBar, &opt,
+ QStyle::SC_TitleBarMinButton,
+ 0);
+ break;
+ case Maximize:
+ if (hasMaximize)
+ region = style->subControlRect(QStyle::CC_TitleBar, &opt,
+ QStyle::SC_TitleBarMaxButton,
+ 0);
+ break;
+ case Close:
+ region = style->subControlRect(QStyle::CC_TitleBar, &opt,
+ QStyle::SC_TitleBarCloseButton, 0);
+ break;
+
+ default:
+ region = QDecorationDefault::region(widget, rect, decorationRegion);
+ }
+
+ opt.rect = QRect(rect.x() - titleExtra, rect.top() - titleHeight,
+ rect.width() + 2 * titleExtra,
+ rect.height() + titleHeight + titleExtra);
+
+ QStyleHintReturnMask mask;
+ style->styleHint(QStyle::SH_WindowFrame_Mask, &opt, 0, &mask);
+
+ return (mask.region.isEmpty() ? region : (region & mask.region));
+}
+
+#endif // QT_NO_QWS_DECORATION_STYLED
+
+QT_END_NAMESPACE
diff --git a/src/gui/embedded/qdecorationstyled_qws.h b/src/gui/embedded/qdecorationstyled_qws.h
new file mode 100644
index 0000000000..583c3f281b
--- /dev/null
+++ b/src/gui/embedded/qdecorationstyled_qws.h
@@ -0,0 +1,73 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QDECORATIONSTYLED_QWS_H
+#define QDECORATIONSTYLED_QWS_H
+
+#include <QtGui/qdecorationdefault_qws.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+#if !defined(QT_NO_QWS_DECORATION_STYLED) || defined(QT_PLUGIN)
+
+class Q_GUI_EXPORT QDecorationStyled : public QDecorationDefault
+{
+public:
+ QDecorationStyled();
+ virtual ~QDecorationStyled();
+
+ QRegion region(const QWidget *widget, const QRect &rect, int decorationRegion = All);
+ bool paint(QPainter *painter, const QWidget *widget, int decorationRegion = All,
+ DecorationState state = Normal);
+ int titleBarHeight(const QWidget *widget);
+};
+
+#endif // QT_NO_QWS_DECORATION_STYLED
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QDECORATIONSTYLED_QWS_H
diff --git a/src/gui/embedded/qdecorationwindows_qws.cpp b/src/gui/embedded/qdecorationwindows_qws.cpp
new file mode 100644
index 0000000000..374f45eebf
--- /dev/null
+++ b/src/gui/embedded/qdecorationwindows_qws.cpp
@@ -0,0 +1,407 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <qapplication.h>
+#include <qwidget.h>
+#include <qpainter.h>
+#include <qdrawutil.h>
+#include "qdecorationwindows_qws.h"
+
+QT_BEGIN_NAMESPACE
+
+#if !defined(QT_NO_QWS_DECORATION_WINDOWS) || defined(QT_PLUGIN)
+
+#ifndef QT_NO_IMAGEFORMAT_XPM
+
+/* XPM */
+static const char * const win_close_xpm[] = {
+"16 16 4 1",
+" s None c None",
+". c #000000",
+"X c #FFFFFF",
+"Y c #707070",
+" ",
+" ",
+" ",
+" Y. .Y ",
+" .. .. ",
+" .. .. ",
+" .YY. ",
+" Y..Y ",
+" .YY. ",
+" .. .. ",
+" .. .. ",
+" Y. .Y ",
+" ",
+" ",
+" ",
+" "};
+
+static const char * const win_help_xpm[] = {
+"16 16 3 1",
+" s None c None",
+". c #ffffff",
+"X c #000000",
+" ",
+" ",
+" ",
+" XXXXXX ",
+" XX XX ",
+" XX XX ",
+" XX ",
+" XX ",
+" XX ",
+" XX ",
+" ",
+" XX ",
+" XX ",
+" ",
+" ",
+" "};
+
+static const char * const win_maximize_xpm[] = {
+"16 16 4 1",
+" s None c None",
+". c #000000",
+"X c #FFFFFF",
+"Y c #707070",
+" ",
+" ",
+" ",
+" .......... ",
+" .......... ",
+" . . ",
+" . . ",
+" . . ",
+" . . ",
+" . . ",
+" . . ",
+" .......... ",
+" ",
+" ",
+" ",
+" "};
+
+static const char * const win_minimize_xpm[] = {
+"16 16 4 1",
+" s None c None",
+". c #000000",
+"X c #FFFFFF",
+"Y c #707070",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ........ ",
+" ........ ",
+" ",
+" ",
+" ",
+" "};
+
+static const char * const win_normalize_xpm[] = {
+"16 16 4 1",
+" s None c None",
+". c #000000",
+"X c #FFFFFF",
+"Y c #707070",
+" ",
+" ",
+" ......... ",
+" ......... ",
+" . . ",
+" . . ",
+" ......... . ",
+" ......... . ",
+" . . . ",
+" . .... ",
+" . . ",
+" . . ",
+" ......... ",
+" ",
+" ",
+" "};
+
+#endif // QT_NO_IMAGEFORMAT_XPM
+
+
+QDecorationWindows::QDecorationWindows()
+ : QDecorationDefault()
+{
+ menu_width = 16;
+ help_width = 18;
+ minimize_width = 18;
+ maximize_width = 18;
+ close_width = 18;
+}
+
+QDecorationWindows::~QDecorationWindows()
+{
+}
+
+const char **QDecorationWindows::xpmForRegion(int reg)
+{
+#ifdef QT_NO_IMAGEFORMAT_XPM
+ Q_UNUSED(reg);
+#else
+ switch(reg)
+ {
+ case Close:
+ return (const char **)win_close_xpm;
+ case Help:
+ return (const char **)win_help_xpm;
+ case Minimize:
+ return (const char **)win_minimize_xpm;
+ case Maximize:
+ return (const char **)win_maximize_xpm;
+ case Normalize:
+ return (const char **)win_normalize_xpm;
+ default:
+ return QDecorationDefault::xpmForRegion(reg);
+ }
+#endif
+ return 0;
+}
+
+QRegion QDecorationWindows::region(const QWidget *widget, const QRect &rect, int type)
+{
+ Qt::WindowFlags flags = widget->windowFlags();
+ bool hasTitle = flags & Qt::WindowTitleHint;
+ bool hasSysMenu = flags & Qt::WindowSystemMenuHint;
+ bool hasContextHelp = flags & Qt::WindowContextHelpButtonHint;
+ bool hasMinimize = flags & Qt::WindowMinimizeButtonHint;
+ bool hasMaximize = flags & Qt::WindowMaximizeButtonHint;
+ const QFontMetrics fontMetrics = QApplication::fontMetrics();
+ int titleHeight = hasTitle ? qMax(20, fontMetrics.lineSpacing()) : 0;
+ int state = widget->windowState();
+ bool isMinimized = state & Qt::WindowMinimized;
+ bool isMaximized = state & Qt::WindowMaximized;
+
+ QRegion region;
+ switch (type) {
+ case Menu: {
+ if (hasSysMenu) {
+ region = QRect(rect.left() + 2, rect.top() - titleHeight,
+ menu_width, titleHeight);
+ }
+ }
+ break;
+
+ case Title: {
+ QRect r(rect.left()
+ + (hasSysMenu ? menu_width + 4: 0),
+ rect.top() - titleHeight,
+ rect.width()
+ - (hasSysMenu ? menu_width : 0)
+ - close_width
+ - (hasMaximize ? maximize_width : 0)
+ - (hasMinimize ? minimize_width : 0)
+ - (hasContextHelp ? help_width : 0)
+ - 3,
+ titleHeight);
+ if (r.width() > 0)
+ region = r;
+ }
+ break;
+ case Help: {
+ if (hasContextHelp) {
+ QRect r(rect.right()
+ - close_width
+ - (hasMaximize ? maximize_width : 0)
+ - (hasMinimize ? minimize_width : 0)
+ - help_width - 3, rect.top() - titleHeight,
+ help_width, titleHeight);
+ if (r.left() > rect.left() + titleHeight)
+ region = r;
+ }
+ }
+ break;
+
+ case Minimize: {
+ if (hasMinimize && !isMinimized) {
+ QRect r(rect.right() - close_width
+ - (hasMaximize ? maximize_width : 0)
+ - minimize_width - 3, rect.top() - titleHeight,
+ minimize_width, titleHeight);
+ if (r.left() > rect.left() + titleHeight)
+ region = r;
+ }
+ }
+ break;
+
+ case Maximize: {
+ if (hasMaximize && !isMaximized) {
+ QRect r(rect.right() - close_width - maximize_width - 3,
+ rect.top() - titleHeight, maximize_width, titleHeight);
+ if (r.left() > rect.left() + titleHeight)
+ region = r;
+ }
+ }
+ break;
+
+ case Normalize: {
+ if (hasMinimize && isMinimized) {
+ QRect r(rect.right() - close_width
+ - (hasMaximize ? maximize_width : 0)
+ - minimize_width - 3, rect.top() - titleHeight,
+ minimize_width, titleHeight);
+ if (r.left() > rect.left() + titleHeight)
+ region = r;
+ } else if (hasMaximize && isMaximized) {
+ QRect r(rect.right() - close_width - maximize_width - 3,
+ rect.top() - titleHeight, maximize_width, titleHeight);
+ if (r.left() > rect.left() + titleHeight)
+ region = r;
+ }
+ }
+ break;
+
+ case Close: {
+ QRect r(rect.right() - close_width - 1, rect.top() - titleHeight,
+ close_width, titleHeight);
+ if (r.left() > rect.left() + titleHeight)
+ region = r;
+ }
+ break;
+
+ default:
+ region = QDecorationDefault::region(widget, rect, type);
+ break;
+ }
+
+ return region;
+}
+
+bool QDecorationWindows::paint(QPainter *painter, const QWidget *widget, int decorationRegion,
+ DecorationState state)
+{
+ if (decorationRegion == None)
+ return false;
+
+ const QRect titleRect = QDecoration::region(widget, Title).boundingRect();
+ const QPalette pal = QApplication::palette();
+ QRegion oldClipRegion = painter->clipRegion();
+
+ bool paintAll = (decorationRegion == int(All));
+ if ((paintAll || decorationRegion & Title && titleRect.width() > 0) && state == Normal
+ && (widget->windowFlags() & Qt::WindowTitleHint) ) {
+ painter->setClipRegion(oldClipRegion);
+ QColor fromBrush, toBrush;
+ QPen titlePen;
+
+ if (widget == qApp->activeWindow() || qApp->activeWindow() == qApp->activePopupWidget()) {
+ fromBrush = pal.color(QPalette::Highlight);
+ titlePen = pal.color(QPalette::HighlightedText);
+ } else {
+ fromBrush = pal.color(QPalette::Window);
+ titlePen = pal.color(QPalette::Text);
+ }
+ toBrush = fromBrush.lighter(300);
+
+ painter->setPen(Qt::NoPen);
+ QPoint p1(titleRect.x(), titleRect.y() + titleRect.height()/2);
+ QPoint p2(titleRect.right(), titleRect.y() + titleRect.height()/2);
+ QLinearGradient lg(p1, p2);
+ lg.setColorAt(0, fromBrush);
+ lg.setColorAt(1, toBrush);
+ painter->fillRect(titleRect, lg);
+
+ painter->setPen(titlePen);
+ painter->drawText(titleRect, Qt::AlignVCenter, windowTitleFor(widget));
+ decorationRegion ^= Title;
+ }
+
+ return QDecorationDefault::paint(painter, widget, decorationRegion, state);
+}
+
+void QDecorationWindows::paintButton(QPainter *painter, const QWidget *widget, int buttonRegion,
+ DecorationState state, const QPalette &pal)
+{
+ QBrush fromBrush, toBrush;
+ QPen titlePen;
+
+ if (widget == qApp->activeWindow() || qApp->activeWindow() == qApp->activePopupWidget()) {
+ fromBrush = pal.brush(QPalette::Highlight);
+ titlePen = pal.color(QPalette::HighlightedText);
+ } else {
+ fromBrush = pal.brush(QPalette::Window);
+ titlePen = pal.color(QPalette::Text);
+ }
+ toBrush = fromBrush.color().lighter(300);
+
+ QRect brect(QDecoration::region(widget, buttonRegion).boundingRect());
+ if (buttonRegion != Close && buttonRegion != Menu)
+ painter->fillRect(brect, toBrush);
+ else
+ painter->fillRect(brect.x() - 2, brect.y(), brect.width() + 4, brect.height(),
+ buttonRegion == Menu ? fromBrush : toBrush);
+
+ int xoff = 1;
+ int yoff = 2;
+ const QPixmap pm = pixmapFor(widget, buttonRegion, xoff, yoff);
+ if (buttonRegion != Menu) {
+ if (state & Normal) {
+ qDrawWinPanel(painter, brect.x(), brect.y() + 2, brect.width(),
+ brect.height() - 4, pal, false, &pal.brush(QPalette::Window));
+ } else if (state & Pressed) {
+ qDrawWinPanel(painter, brect.x(), brect.y() + 2, brect.width(),
+ brect.height() - 4, pal, true, &pal.brush(QPalette::Window));
+ ++xoff;
+ ++yoff;
+ }
+ } else {
+ xoff = 0;
+ yoff = 2;
+ }
+
+ if (!pm.isNull())
+ painter->drawPixmap(brect.x() + xoff, brect.y() + yoff, pm);
+}
+
+#endif // QT_NO_QWS_DECORATION_WINDOWS || QT_PLUGIN
+
+QT_END_NAMESPACE
diff --git a/src/gui/embedded/qdecorationwindows_qws.h b/src/gui/embedded/qdecorationwindows_qws.h
new file mode 100644
index 0000000000..c7eb80ece9
--- /dev/null
+++ b/src/gui/embedded/qdecorationwindows_qws.h
@@ -0,0 +1,77 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QDECORATIONWINDOWS_QWS_H
+#define QDECORATIONWINDOWS_QWS_H
+
+#include <QtGui/qdecorationdefault_qws.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+#if !defined(QT_NO_QWS_DECORATION_WINDOWS) || defined(QT_PLUGIN)
+
+class Q_GUI_EXPORT QDecorationWindows : public QDecorationDefault
+{
+public:
+ QDecorationWindows();
+ virtual ~QDecorationWindows();
+
+ QRegion region(const QWidget *widget, const QRect &rect, int decorationRegion = All);
+ bool paint(QPainter *painter, const QWidget *widget, int decorationRegion = All,
+ DecorationState state = Normal);
+
+protected:
+ void paintButton(QPainter *painter, const QWidget *widget, int buttonRegion,
+ DecorationState state, const QPalette &pal);
+ const char **xpmForRegion(int reg);
+};
+
+#endif // QT_NO_QWS_DECORATION_WINDOWS
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QDECORATIONWINDOWS_QWS_H
diff --git a/src/gui/embedded/qdirectpainter_qws.cpp b/src/gui/embedded/qdirectpainter_qws.cpp
new file mode 100644
index 0000000000..113e60747c
--- /dev/null
+++ b/src/gui/embedded/qdirectpainter_qws.cpp
@@ -0,0 +1,682 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qdirectpainter_qws.h"
+
+#include "qscreen_qws.h"
+#include "private/qobject_p.h"
+#include "private/qapplication_p.h"
+#include "qwsdisplay_qws.h"
+#include "qwidget.h"
+#include "qimage.h"
+#include <qwsevent_qws.h>
+#include <private/qwindowsurface_qws_p.h>
+#include <private/qwsdisplay_qws_p.h>
+
+QT_BEGIN_NAMESPACE
+
+#ifdef Q_WS_QWS
+#ifndef QT_NO_DIRECTPAINTER
+
+/*!
+ \class QDirectPainter
+ \ingroup multimedia
+ \ingroup qws
+
+ \brief The QDirectPainter class provides direct access to the
+ underlying hardware in Qt for Embedded Linux.
+
+ Note that this class is only available in \l{Qt for Embedded Linux}.
+
+ QDirectPainter allows a client application to reserve a region of
+ the framebuffer and render directly onto the screen. There are two
+ ways of using the QDirectPainter class: You can either reserve a
+ region using the provided static functions, or you can instantiate
+ an object and make use of its more dynamic API.
+
+ \tableofcontents
+
+ \section1 Dynamic Allocation
+
+ By instantiating a QDirectPainter object using the default
+ QDirectPainter::NonReserved surface flag, the client application
+ only gets some control over the reserved region, i.e., it can
+ still render directly onto the screen but the allocated region may
+ change (for example, if a window with a higher focus requests
+ parts of the same region). The currently allocated region can be
+ retrieved using the allocatedRegion() function, while the
+ requestedRegion() function returns the originally reserved
+ region.
+
+
+ \section1 Static Allocation
+
+
+ Using the static approach, the client application gets complete
+ control over the reserved region, i.e., the affected region will
+ never be modified by the screen driver.
+
+ To create a static region, pass the QDirectPainter::Reserved
+ surface flag to the constructor. After the reserved region is
+ reported through regionChanged(), the allocated region will not
+ change, unless setRegion() is called.
+
+ If QDirectPainter::ReservedSynchronous is passed to the
+ constructor, calls to setRegion() will block until the region is
+ reserved, meaning that allocatedRegion() will be available immediately.
+ Note that in the current version setRegion() will cause the application
+ event loop to be entered, potentially causing reentrancy issues.
+
+ \section1 Rendering
+
+ To draw on a given region, the application must first get hold of
+ a pointer to the framebuffer. In most cases, this pointer can be
+ retrieved using the QDirectPainter::frameBuffer() function. But
+ note that if the current screen has subscreens, you must query the
+ screen driver instead to identify the correct subscreen. A pointer
+ to the current screen driver can always be retrieved using the
+ static QScreen::instance() function. Then use QScreen's \l
+ {QScreen::}{subScreenIndexAt()} and \l {QScreen::}{subScreens()}
+ functions to access the correct subscreen, and the subscreen's \l
+ {QScreen::}{base()} function to retrieve a pointer to the
+ framebuffer.
+
+ Depending on the hardware, it might be necessary to lock the
+ framebuffer for exclusive use while writing to it. This is
+ possible using the lock() and unlock() functions. Note that
+ calling lock() will prevent all other applications from working
+ until unlock() is called.
+
+ In addition, QDirectPainter provides several functions returning
+ information about the framebuffer: the linestep() function returns
+ the length (in bytes) of each scanline of the framebuffer while
+ the screenDepth(), screenWidth() and screenHeight() function
+ return the screen metrics.
+
+ \sa QScreen, QWSEmbedWidget, {Qt for Embedded Linux Architecture}
+*/
+
+/*!
+ \enum QDirectPainter::SurfaceFlag
+
+ This enum describes the behavior of the region reserved by this
+ QDirectPainter object.
+
+ \value NonReserved The allocated region may change, e.g., if a
+ window with a higher focus requests parts of the same region. See
+ also \l {Dynamic Allocation}.
+
+ \value Reserved The allocated region will never change. See also
+ \l {Static Allocation}.
+
+ \value ReservedSynchronous The allocated region will never change and
+ each function that changes the allocated region will be blocking.
+
+ \sa reservedRegion(), allocatedRegion()
+*/
+
+/*!
+ \fn QRegion QDirectPainter::region()
+ \obsolete
+
+ Use QDirectPainter::reservedRegion() instead.
+*/
+
+static inline QScreen *getPrimaryScreen()
+{
+ QScreen *screen = QScreen::instance();
+ if (!screen->base()) {
+ QList<QScreen*> subScreens = screen->subScreens();
+ if (subScreens.size() < 1)
+ return 0;
+ screen = subScreens.at(0);
+ }
+ return screen;
+}
+
+static inline QSize screenS()
+{
+ QScreen *screen = getPrimaryScreen();
+ if (!screen)
+ return QSize();
+ return QSize(screen->width(), screen->height());
+}
+
+static inline QSize devS()
+{
+ QScreen *screen = getPrimaryScreen();
+ if (!screen)
+ return QSize();
+ return QSize(screen->deviceWidth(), screen->deviceHeight());
+}
+
+
+class QDirectPainterPrivate : public QObjectPrivate
+{
+ Q_DECLARE_PUBLIC(QDirectPainter);
+public:
+
+ QDirectPainterPrivate() : surface(0), seenRegion(false) {}
+
+ ~QDirectPainterPrivate() {
+ if (QPaintDevice::qwsDisplay()) { // make sure not in QApplication destructor
+ qApp->d_func()->directPainters->remove(surface->windowId());
+ surface->setGeometry(QRect());
+ }
+ delete surface;
+ }
+
+ QWSDirectPainterSurface *surface;
+ QRegion requested_region;
+
+ static QDirectPainter *staticPainter;
+ bool seenRegion;
+};
+
+QDirectPainter *QDirectPainterPrivate::staticPainter = 0;
+
+void qt_directpainter_region(QDirectPainter *dp, const QRegion &alloc, int type)
+{
+ QDirectPainterPrivate *d = dp->d_func();
+
+ QRegion r = alloc;
+ QScreen *screen = d->surface->screen();
+ if (screen->isTransformed()) {
+ const QSize screenSize(screen->width(), screen->height());
+ r = screen->mapToDevice(r, screenSize);
+ }
+ if (type == QWSRegionEvent::Allocation) {
+ d->surface->setClipRegion(alloc);
+ d->seenRegion = true;
+ if (dp != QDirectPainterPrivate::staticPainter) {
+ if (!d->surface->flushingRegionEvents) // recursion guard
+ dp->regionChanged(r);
+ }
+ }
+}
+
+#ifndef QT_NO_QWSEMBEDWIDGET
+void qt_directpainter_embedevent(QDirectPainter *dp, const QWSEmbedEvent *event)
+{
+ if (event->type | QWSEmbedEvent::Region) {
+ QScreen *screen = dp->d_func()->surface->screen();
+ QRegion r = event->region;
+ if (screen->isTransformed()) {
+ const QSize screenSize(screen->width(), screen->height());
+ r = screen->mapToDevice(r, screenSize);
+ }
+ dp->setRegion(r);
+ }
+}
+#endif
+
+/*!
+ Constructs a QDirectPainter object with the given \a parent and
+ surface \a flag.
+*/
+QDirectPainter::QDirectPainter(QObject *parent, SurfaceFlag flag)
+ :QObject(*new QDirectPainterPrivate, parent)
+{
+ Q_D(QDirectPainter);
+ d->surface = new QWSDirectPainterSurface(true, flag);
+
+ if (flag != NonReserved)
+ d->surface->setReserved();
+
+ QApplicationPrivate *ad = qApp->d_func();
+ if (!ad->directPainters)
+ ad->directPainters = new QMap<WId, QDirectPainter*>;
+ ad->directPainters->insert(d->surface->windowId(), this);
+}
+
+/*!
+ Destroys this QDirectPainter object, releasing the reserved region.
+
+ \sa allocatedRegion()
+*/
+QDirectPainter::~QDirectPainter()
+{
+ /* should not be necessary
+ if (this == QDirectPainterPrivate::staticPainter)
+ QDirectPainterPrivate::staticPainter = 0;
+ */
+}
+
+/*!
+ \fn void QDirectPainter::setGeometry(const QRect &rectangle)
+ \since 4.2
+
+ Request to reserve the given \a rectangle of the framebuffer.
+
+ Note that the actually allocated region might differ from the
+ requested one, e.g., if the given region overlaps with the
+ region of another QDirectPainter object.
+
+ \sa geometry(), allocatedRegion(), setRegion()
+*/
+void QDirectPainter::setGeometry(const QRect &rect)
+{
+ setRegion(rect);
+}
+
+/*!
+ \since 4.2
+
+ Returns the bounding rectangle of the requested region.
+
+ \sa setGeometry(), requestedRegion()
+*/
+QRect QDirectPainter::geometry() const
+{
+ Q_D(const QDirectPainter);
+ return d->requested_region.boundingRect();
+}
+
+/*!
+ \since 4.2
+
+ Requests to reserve the given \a region of the framebuffer.
+
+ Note that the actually allocated region might differ from the
+ requested one, e.g., if the given region overlaps with the region
+ of another QDirectPainter object.
+
+ \sa requestedRegion(), allocatedRegion(), {Dynamic Allocation}
+*/
+void QDirectPainter::setRegion(const QRegion &region)
+{
+ Q_D(QDirectPainter);
+ d->requested_region = region;
+
+ const QScreen *screen = d->surface->screen();
+ if (screen->isTransformed()) {
+ const QSize devSize(screen->deviceWidth(), screen->deviceHeight());
+ const QRegion r = screen->mapFromDevice(region, devSize);
+ d->surface->setRegion(r);
+ } else {
+ d->surface->setRegion(region);
+ }
+}
+
+/*!
+ \since 4.2
+
+ Returns the region requested by this QDirectPainter.
+
+ Note that if the QDirectPainter::Reserved flag is set, the region
+ returned by this function will always be equivalent to the region
+ returned by the allocatedRegion() function. Otherwise they might
+ differ (see \l {Dynamic Allocation} for details).
+
+ \sa geometry(), setRegion()
+*/
+QRegion QDirectPainter::requestedRegion() const
+{
+ Q_D(const QDirectPainter);
+ return d->requested_region;
+}
+
+/*!
+ \since 4.2
+
+ Returns the currently reserved region.
+
+ Note that if the QDirectPainter::Reserved flag is set, the region
+ returned by this function will always be equivalent to the region
+ returned by the requestedRegion() function. Otherwise they might
+ differ (see \l {Dynamic Allocation} for details).
+
+ \sa requestedRegion(), geometry()
+*/
+QRegion QDirectPainter::allocatedRegion() const
+{
+ Q_D(const QDirectPainter);
+ const QScreen *screen = d->surface->screen();
+ if (screen->isTransformed()) {
+ const QSize screenSize(screen->width(), screen->height());
+ return screen->mapToDevice(d->surface->region(), screenSize);
+ } else {
+ return d->surface->region();
+ }
+}
+
+/*!
+ \since 4.2
+
+ Returns the window system identifier of the widget.
+*/
+WId QDirectPainter::winId() const
+{
+ Q_D(const QDirectPainter);
+ return d->surface->windowId();
+}
+
+/*!
+ \fn void QDirectPainter::regionChanged(const QRegion &newRegion)
+ \since 4.2
+
+ This function is called when the allocated region changes.
+
+ This function is not called for region changes that happen while the
+ startPainting() function is executing.
+
+ Note that the given region, \a newRegion, is not guaranteed to be correct at the
+ time you access the display. To prevent reentrancy problems you should
+ always call startPainting() before updating the display and then use
+ allocatedRegion() to retrieve the correct region.
+
+ \sa allocatedRegion(), startPainting(), {Dynamic Allocation}
+*/
+void QDirectPainter::regionChanged(const QRegion &region)
+{
+ Q_UNUSED(region);
+}
+
+/*!
+ \since 4.2
+
+ Call this function before you start updating the pixels in the
+ allocated region. The hardware will be notified, if necessary,
+ that you are about to start painting operations.
+
+ Set \a lockDisplay if you want startPainting() and endPainting()
+ to lock() and unlock() the display automatically.
+
+ Note that for a NonReserved direct painter, you must call
+ allocatedRegion() after calling this function, since the allocated
+ region is only guaranteed to be correct after this function has
+ returned.
+
+ The regionChanged() function will not be called between startPainting()
+ and endPainting().
+
+ \sa endPainting(), flush()
+*/
+void QDirectPainter::startPainting(bool lockDisplay)
+{
+ Q_D(QDirectPainter);
+ d->surface->setLocking(lockDisplay);
+
+ const QScreen *screen = d->surface->screen();
+ if (screen->isTransformed()) {
+ const QSize devSize(screen->deviceWidth(), screen->deviceHeight());
+ const QRegion r = screen->mapFromDevice(d->surface->region(), devSize);
+ d->surface->beginPaint(r);
+ } else {
+ d->surface->beginPaint(d->surface->region());
+ }
+}
+
+/*!
+ \since 4.2
+
+ Call this function when you are done updating the screen. It will
+ notify the hardware, if necessary, that your painting operations
+ have ended.
+*/
+void QDirectPainter::endPainting()
+{
+ Q_D(QDirectPainter);
+
+ const QScreen *screen = d->surface->screen();
+ if (screen->isTransformed()) {
+ const QSize devSize(screen->deviceWidth(), screen->deviceHeight());
+ const QRegion r = screen->mapFromDevice(d->surface->region(), devSize);
+ d->surface->endPaint(r);
+ } else {
+ d->surface->endPaint(d->surface->region());
+ }
+}
+
+/*!
+ \since 4.3
+ \overload
+
+ This function will automatically call flush() to flush the
+ \a region to the display before notifying the hardware, if
+ necessary, that painting operations have ended.
+*/
+void QDirectPainter::endPainting(const QRegion &region)
+{
+ endPainting();
+ flush(region);
+}
+
+/*!
+ \since 4.3
+
+ Flushes the \a region onto the screen.
+*/
+void QDirectPainter::flush(const QRegion &region)
+{
+ Q_D(QDirectPainter);
+
+ const QScreen *screen = d->surface->screen();
+ if (screen->isTransformed()) {
+ const QSize devSize(screen->deviceWidth(), screen->deviceHeight());
+ const QRegion r = screen->mapFromDevice(region, devSize);
+ d->surface->flush(0, r, QPoint());
+ } else {
+ d->surface->flush(0, region, QPoint());
+ }
+}
+
+/*!
+ \since 4.2
+
+ Raises the reserved region to the top of the widget stack.
+
+ After this call the reserved region will be visually in front of
+ any overlapping widgets.
+
+ \sa lower(), requestedRegion()
+*/
+void QDirectPainter::raise()
+{
+ QWidget::qwsDisplay()->setAltitude(winId(),QWSChangeAltitudeCommand::Raise);
+}
+
+/*!
+ \since 4.2
+
+ Lowers the reserved region to the bottom of the widget stack.
+
+ After this call the reserved region will be visually behind (and
+ therefore obscured by) any overlapping widgets.
+
+ \sa raise(), requestedRegion()
+*/
+void QDirectPainter::lower()
+{
+ QWidget::qwsDisplay()->setAltitude(winId(),QWSChangeAltitudeCommand::Lower);
+}
+
+
+/*!
+ \fn QRegion QDirectPainter::reserveRegion(const QRegion &region)
+
+ Attempts to reserve the \a region and returns the region that is
+ actually reserved.
+
+ This function also releases the previously reserved region if
+ any. If not released explicitly, the region will be released on
+ application exit.
+
+ \sa reservedRegion(), {Static Allocation}
+
+ \obsolete
+
+ Construct a QDirectPainter using QDirectPainter::ReservedSynchronous instead.
+*/
+QRegion QDirectPainter::reserveRegion(const QRegion &reg)
+{
+ if (!QDirectPainterPrivate::staticPainter)
+ QDirectPainterPrivate::staticPainter = new QDirectPainter(qApp, ReservedSynchronous);
+
+ QDirectPainter *dp = QDirectPainterPrivate::staticPainter;
+ dp->setRegion(reg);
+
+ return dp->allocatedRegion();
+}
+
+/*!
+ Returns a pointer to the beginning of the display memory.
+
+ Note that it is the application's responsibility to limit itself
+ to modifying only the reserved region.
+
+ Do not use this pointer if the current screen has subscreens,
+ query the screen driver instead: A pointer to the current screen
+ driver can always be retrieved using the static
+ QScreen::instance() function. Then use QScreen's \l
+ {QScreen::}{subScreenIndexAt()} and \l {QScreen::}{subScreens()}
+ functions to access the correct subscreen, and the subscreen's \l
+ {QScreen::}{base()} function to retrieve a pointer to the
+ framebuffer.
+
+ \sa requestedRegion(), allocatedRegion(), linestep()
+*/
+uchar* QDirectPainter::frameBuffer()
+{
+ QScreen *screen = getPrimaryScreen();
+ if (!screen)
+ return 0;
+ return screen->base();
+}
+
+/*!
+ \since 4.2
+
+ Returns the reserved region.
+
+ \sa reserveRegion(), frameBuffer()
+
+ \obsolete
+
+ Use allocatedRegion() instead.
+*/
+QRegion QDirectPainter::reservedRegion()
+{
+ return QDirectPainterPrivate::staticPainter
+ ? QDirectPainterPrivate::staticPainter->allocatedRegion() : QRegion();
+}
+
+/*!
+ Returns the bit depth of the display.
+
+ \sa screenHeight(), screenWidth()
+*/
+int QDirectPainter::screenDepth()
+{
+ QScreen *screen = getPrimaryScreen();
+ if (!screen)
+ return 0;
+ return screen->depth();
+}
+
+/*!
+ Returns the width of the display in pixels.
+
+ \sa screenHeight(), screenDepth()
+*/
+int QDirectPainter::screenWidth()
+{
+ QScreen *screen = getPrimaryScreen();
+ if (!screen)
+ return 0;
+ return screen->deviceWidth();
+}
+
+/*!
+ Returns the height of the display in pixels.
+
+ \sa screenWidth(), screenDepth()
+*/
+int QDirectPainter::screenHeight()
+{
+ QScreen *screen = getPrimaryScreen();
+ if (!screen)
+ return 0;
+ return screen->deviceHeight();
+}
+
+/*!
+ Returns the length (in bytes) of each scanline of the framebuffer.
+
+ \sa frameBuffer()
+*/
+int QDirectPainter::linestep()
+{
+ QScreen *screen = getPrimaryScreen();
+ if (!screen)
+ return 0;
+ return screen->linestep();
+}
+
+
+/*!
+ Locks access to the framebuffer.
+
+ Note that calling this function will prevent all other
+ applications from updating the display until unlock() is called.
+
+ \sa unlock()
+*/
+void QDirectPainter::lock()
+{
+ QWSDisplay::grab(true);
+}
+/*!
+ Unlocks the lock on the framebuffer (set using the lock()
+ function), allowing other applications to access the screen.
+
+ \sa lock()
+ */
+void QDirectPainter::unlock()
+{
+ QWSDisplay::ungrab();
+}
+
+#endif //QT_NO_DIRECTPAINTER
+
+#endif
+
+QT_END_NAMESPACE
diff --git a/src/gui/embedded/qdirectpainter_qws.h b/src/gui/embedded/qdirectpainter_qws.h
new file mode 100644
index 0000000000..8c5c30f5b0
--- /dev/null
+++ b/src/gui/embedded/qdirectpainter_qws.h
@@ -0,0 +1,112 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QDIRECTPAINTER_QWS_H
+#define QDIRECTPAINTER_QWS_H
+
+#include <QtCore/qobject.h>
+#include <QtGui/qregion.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+#ifndef QT_NO_DIRECTPAINTER
+class QDirectPainterPrivate;
+class QWSEmbedEvent;
+
+class Q_GUI_EXPORT QDirectPainter : public QObject {
+ Q_OBJECT
+ Q_DECLARE_PRIVATE(QDirectPainter)
+public:
+
+ enum SurfaceFlag { NonReserved = 0,
+ Reserved = 1,
+ ReservedSynchronous = 3 };
+
+ explicit QDirectPainter(QObject *parentObject = 0, SurfaceFlag flag = NonReserved);
+ ~QDirectPainter();
+
+ void setRegion(const QRegion&);
+ QRegion requestedRegion() const;
+ QRegion allocatedRegion() const;
+
+ void setGeometry(const QRect&);
+ QRect geometry() const;
+
+ WId winId() const;
+ virtual void regionChanged(const QRegion &exposedRegion);
+
+ void startPainting(bool lockDisplay = true);
+ void endPainting();
+ void endPainting(const QRegion &region);
+ void flush(const QRegion &region);
+
+ void raise();
+ void lower();
+
+
+ static QRegion reserveRegion(const QRegion&);
+ static QRegion reservedRegion();
+ static QRegion region() { return reservedRegion(); }
+
+ static uchar* frameBuffer();
+ static int screenDepth();
+ static int screenWidth();
+ static int screenHeight();
+ static int linestep();
+
+ static void lock();
+ static void unlock();
+private:
+ friend void qt_directpainter_region(QDirectPainter *dp, const QRegion &alloc, int type);
+ friend void qt_directpainter_embedevent(QDirectPainter*, const QWSEmbedEvent*);
+};
+
+#endif
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QDIRECTPAINTER_QWS_H
diff --git a/src/gui/embedded/qkbd_qws.cpp b/src/gui/embedded/qkbd_qws.cpp
new file mode 100644
index 0000000000..8cf87db0e8
--- /dev/null
+++ b/src/gui/embedded/qkbd_qws.cpp
@@ -0,0 +1,248 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qkbd_qws.h"
+
+#ifndef QT_NO_QWS_KEYBOARD
+
+#include "qwindowsystem_qws.h"
+#include "qscreen_qws.h"
+#include "qtimer.h"
+#include <stdlib.h>
+
+QT_BEGIN_NAMESPACE
+
+class QWSKbPrivate : public QObject
+{
+ Q_OBJECT
+public:
+ QWSKbPrivate(QWSKeyboardHandler *h) {
+ handler = h;
+ arTimer = new QTimer(this);
+ arTimer->setSingleShot(true);
+ connect(arTimer, SIGNAL(timeout()), SLOT(autoRepeat()));
+ repeatdelay = 400;
+ repeatperiod = 80;
+ }
+
+ void beginAutoRepeat(int uni, int code, Qt::KeyboardModifiers mod) {
+ unicode = uni;
+ keycode = code;
+ modifier = mod;
+ arTimer->start(repeatdelay);
+ }
+ void endAutoRepeat() {
+ arTimer->stop();
+ }
+
+private slots:
+ void autoRepeat() {
+ handler->processKeyEvent(unicode, keycode, modifier, false, true);
+ handler->processKeyEvent(unicode, keycode, modifier, true, true);
+ arTimer->start(repeatperiod);
+ }
+
+private:
+ QWSKeyboardHandler *handler;
+ int unicode;
+ int keycode;
+ Qt::KeyboardModifiers modifier;
+ int repeatdelay;
+ int repeatperiod;
+ QTimer *arTimer;
+};
+
+/*!
+ \class QWSKeyboardHandler
+ \ingroup qws
+
+ \brief The QWSKeyboardHandler class is a base class for keyboard
+ drivers in Qt for Embedded Linux.
+
+ Note that this class is only available in \l{Qt for Embedded Linux}.
+
+ \l{Qt for Embedded Linux} provides ready-made drivers for several keyboard
+ protocols, see the \l{Qt for Embedded Linux Character Input}{character
+ input} documentation for details. Custom keyboard drivers can be
+ implemented by subclassing the QWSKeyboardHandler class and
+ creating a keyboard driver plugin (derived from
+ QKbdDriverPlugin). The default implementation of the
+ QKbdDriverFactory class will automatically detect the plugin, and
+ load the driver into the server application at run-time using Qt's
+ \l{How to Create Qt Plugins}{plugin system}.
+
+ The keyboard driver receives keyboard events from the system
+ device and encapsulates each event with an instance of the
+ QWSEvent class which it then passes to the server application (the
+ server is responsible for propagating the event to the appropriate
+ client). To receive keyboard events, a QWSKeyboardHandler object
+ will usually create a QSocketNotifier object for the given
+ device. The QSocketNotifier class provides support for monitoring
+ activity on a file descriptor. When the socket notifier receives
+ data, it will call the keyboard driver's processKeyEvent()
+ function to send the event to the \l{Qt for Embedded Linux} server
+ application for relaying to clients.
+
+
+ QWSKeyboardHandler also provides functions to control
+ auto-repetion of key sequences, beginAutoRepeat() and
+ endAutoRepeat(), and the transformDirKey() function enabling
+ transformation of arrow keys according to the display orientation.
+
+ \sa QKbdDriverPlugin, QKbdDriverFactory, {Qt for Embedded Linux Character Input}
+*/
+
+
+/*!
+ Constructs a keyboard driver.
+
+ Call the QWSServer::setKeyboardHandler() function to make the
+ newly created keyboard driver, the primary driver. Note that the
+ primary driver is controlled by the system, i.e., the system will
+ delete it upon exit.
+*/
+QWSKeyboardHandler::QWSKeyboardHandler()
+{
+ d = new QWSKbPrivate(this);
+}
+
+/*!
+ Destroys this keyboard driver.
+
+ Do not call this function if this driver is the primary keyboard
+ handler, i.e., if QWSServer::setKeyboardHandler() function has
+ been called passing this driver as argument. The primary keyboard
+ driver is deleted by the system.
+*/
+QWSKeyboardHandler::~QWSKeyboardHandler()
+{
+ delete d;
+}
+
+
+/*!
+ Sends a key event to the \l{Qt for Embedded Linux} server application.
+
+ The key event is identified by its \a unicode value and the \a
+ keycode, \a modifiers, \a isPress and \a autoRepeat parameters.
+
+ The \a keycode parameter is the Qt keycode value as defined by the
+ Qt::Key enum. The \a modifiers is an OR combination of
+ Qt::KeyboardModifier values, indicating whether \gui
+ Shift/Alt/Ctrl keys are pressed. The \a isPress parameter is true
+ if the event is a key press event and \a autoRepeat is true if the
+ event is caused by an auto-repeat mechanism and not an actual key
+ press.
+
+ \sa beginAutoRepeat(), endAutoRepeat(), transformDirKey()
+*/
+void QWSKeyboardHandler::processKeyEvent(int unicode, int keycode, Qt::KeyboardModifiers modifiers,
+ bool isPress, bool autoRepeat)
+{
+ qwsServer->processKeyEvent(unicode, keycode, modifiers, isPress, autoRepeat);
+}
+
+/*!
+ \fn int QWSKeyboardHandler::transformDirKey(int keycode)
+
+ Transforms the arrow key specified by the given \a keycode, to the
+ orientation of the display and returns the transformed keycode.
+
+ The \a keycode is a Qt::Key value. The values identifying arrow
+ keys are:
+
+ \list
+ \o Qt::Key_Left
+ \o Qt::Key_Up
+ \o Qt::Key_Right
+ \o Qt::Key_Down
+ \endlist
+
+ \sa processKeyEvent()
+ */
+int QWSKeyboardHandler::transformDirKey(int key)
+{
+ static int dir_keyrot = -1;
+ if (dir_keyrot < 0) {
+ // get the rotation
+ switch (qgetenv("QWS_CURSOR_ROTATION").toInt()) {
+ case 90: dir_keyrot = 1; break;
+ case 180: dir_keyrot = 2; break;
+ case 270: dir_keyrot = 3; break;
+ default: dir_keyrot = 0; break;
+ }
+ }
+ int xf = qt_screen->transformOrientation() + dir_keyrot;
+ return (key-Qt::Key_Left+xf)%4+Qt::Key_Left;
+}
+
+/*!
+ \fn void QWSKeyboardHandler::beginAutoRepeat(int unicode, int keycode, Qt::KeyboardModifiers modifier)
+
+ Begins auto-repeating the specified key press; after a short delay
+ the key press is sent periodically until the endAutoRepeat()
+ function is called.
+
+ The key press is specified by its \a unicode, \a keycode and \a
+ modifier state.
+
+ \sa endAutoRepeat(), processKeyEvent()
+*/
+void QWSKeyboardHandler::beginAutoRepeat(int uni, int code, Qt::KeyboardModifiers mod)
+{
+ d->beginAutoRepeat(uni, code, mod);
+}
+
+/*!
+ Stops auto-repeating a key press.
+
+ \sa beginAutoRepeat(), processKeyEvent()
+*/
+void QWSKeyboardHandler::endAutoRepeat()
+{
+ d->endAutoRepeat();
+}
+
+QT_END_NAMESPACE
+
+#include "qkbd_qws.moc"
+
+#endif // QT_NO_QWS_KEYBOARD
diff --git a/src/gui/embedded/qkbd_qws.h b/src/gui/embedded/qkbd_qws.h
new file mode 100644
index 0000000000..8809f0a5b5
--- /dev/null
+++ b/src/gui/embedded/qkbd_qws.h
@@ -0,0 +1,81 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QKBD_QWS_H
+#define QKBD_QWS_H
+
+#include <QtGui/qapplication.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+#ifndef QT_NO_QWS_KEYBOARD
+
+class QWSKbPrivate;
+
+class Q_GUI_EXPORT QWSKeyboardHandler
+{
+public:
+ QWSKeyboardHandler();
+ virtual ~QWSKeyboardHandler();
+
+ virtual void processKeyEvent(int unicode, int keycode, Qt::KeyboardModifiers modifiers,
+ bool isPress, bool autoRepeat);
+
+protected:
+ int transformDirKey(int key);
+ void beginAutoRepeat(int uni, int code, Qt::KeyboardModifiers mod);
+ void endAutoRepeat();
+
+private:
+ QWSKbPrivate *d;
+};
+
+#endif // QT_NO_QWS_KEYBOARD
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QKBD_QWS_H
diff --git a/src/gui/embedded/qkbddriverfactory_qws.cpp b/src/gui/embedded/qkbddriverfactory_qws.cpp
new file mode 100644
index 0000000000..1ade652a9c
--- /dev/null
+++ b/src/gui/embedded/qkbddriverfactory_qws.cpp
@@ -0,0 +1,193 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qkbddriverfactory_qws.h"
+
+#ifndef QT_NO_QWS_KEYBOARD
+
+#include "qapplication.h"
+#include "qkbdtty_qws.h"
+#include "qkbdusb_qws.h"
+#include "qkbdum_qws.h"
+#include "qkbdsl5000_qws.h"
+#include "qkbdvfb_qws.h"
+#include "qkbdyopy_qws.h"
+#include "qkbdvr41xx_qws.h"
+#include <stdlib.h>
+#include "private/qfactoryloader_p.h"
+#include "qkbddriverplugin_qws.h"
+
+QT_BEGIN_NAMESPACE
+
+#if !defined(Q_OS_WIN32) || defined(QT_MAKEDLL)
+#ifndef QT_NO_LIBRARY
+Q_GLOBAL_STATIC_WITH_ARGS(QFactoryLoader, loader,
+ (QWSKeyboardHandlerFactoryInterface_iid,
+ QLatin1String("/kbddrivers"), Qt::CaseInsensitive))
+
+#endif //QT_NO_LIBRARY
+#endif //QT_MAKEDLL
+
+/*!
+ \class QKbdDriverFactory
+ \ingroup qws
+
+ \brief The QKbdDriverFactory class creates keyboard drivers in
+ Qt for Embedded Linux.
+
+ Note that this class is only available in \l{Qt for Embedded Linux}.
+
+ QKbdDriverFactory is used to detect and instantiate the available
+ keyboard drivers, allowing \l{Qt for Embedded Linux} to load the preferred
+ driver into the server application at runtime. The create()
+ function returns a QWSKeyboardHandler object representing the
+ keyboard driver identified by a given key. The valid keys
+ (i.e. the supported drivers) can be retrieved using the keys()
+ function.
+
+ \l{Qt for Embedded Linux} provides several built-in keyboard drivers. In
+ addition, custom keyboard drivers can be added using Qt's plugin
+ mechanism, i.e. by subclassing the QWSKeyboardHandler class and
+ creating a keyboard driver plugin (QKbdDriverPlugin). See the
+ \l{Qt for Embedded Linux Character Input}{character input} documentation
+ for details.
+
+ \sa QWSKeyboardHandler, QKbdDriverPlugin
+*/
+
+/*!
+ Creates the keyboard driver specified by the given \a key, using
+ the display specified by the given \a device.
+
+ Note that the keys are case-insensitive.
+
+ \sa keys()
+*/
+QWSKeyboardHandler *QKbdDriverFactory::create(const QString& key, const QString& device)
+{
+ QString driver = key.toLower();
+#ifndef QT_NO_QWS_KBD_SL5000
+ if (driver == QLatin1String("sl5000") || driver.isEmpty())
+ return new QWSSL5000KeyboardHandler(device);
+#endif
+#ifndef QT_NO_QWS_KBD_YOPY
+ if (driver == QLatin1String("yopy") || driver.isEmpty())
+ return new QWSYopyKeyboardHandler(device);
+#endif
+#ifndef QT_NO_QWS_KBD_VR41XX
+ if (driver == QLatin1String("vr41xx") || driver.isEmpty())
+ return new QWSVr41xxKeyboardHandler(device);
+#endif
+#ifndef QT_NO_QWS_KEYBOARD
+# ifndef QT_NO_QWS_KBD_TTY
+ if (driver == QLatin1String("tty") || driver.isEmpty())
+ return new QWSTtyKeyboardHandler(device);
+# endif
+# ifndef QT_NO_QWS_KBD_USB
+ if (driver == QLatin1String("usb"))
+ return new QWSUsbKeyboardHandler(device);
+# endif
+# ifndef QT_NO_QWS_KBD_UM
+ if (driver == QLatin1String("um") || driver == QLatin1String("qvfbkeyboard"))
+ return new QWSUmKeyboardHandler(device);
+# endif
+# ifndef QT_NO_QWS_KBD_QVFB
+ if (driver == QLatin1String("qvfbkbd")
+ || driver == QLatin1String("qvfbkeyboard")
+ || driver == QLatin1String("qvfb"))
+ return new QVFbKeyboardHandler(device);
+# endif
+#endif
+
+#if !defined(Q_OS_WIN32) || defined(QT_MAKEDLL)
+#ifndef QT_NO_LIBRARY
+ if (QWSKeyboardHandlerFactoryInterface *factory = qobject_cast<QWSKeyboardHandlerFactoryInterface*>(loader()->instance(driver)))
+ return factory->create(driver, device);
+#endif
+#endif
+ return 0;
+}
+
+/*!
+ Returns the list of valid keys, i.e. the available keyboard
+ drivers.
+
+ \sa create()
+*/
+QStringList QKbdDriverFactory::keys()
+{
+ QStringList list;
+
+#ifndef QT_NO_QWS_KBD_SL5000
+ list << QLatin1String("SL5000");
+#endif
+#ifndef QT_NO_QWS_KBD_YOPY
+ list << QLatin1String("YOPY");
+#endif
+#ifndef QT_NO_QWS_KBD_VR41XX
+ list << QLatin1String("VR41xx");
+#endif
+#ifndef QT_NO_QWS_KBD_TTY
+ list << QLatin1String("TTY");
+#endif
+#ifndef QT_NO_QWS_KBD_USB
+ list << QLatin1String("USB");
+#endif
+#ifndef QT_NO_QWS_KBD_UM
+ list << QLatin1String("UM");
+#endif
+
+#if !defined(Q_OS_WIN32) || defined(QT_MAKEDLL)
+#ifndef QT_NO_LIBRARY
+ QStringList plugins = loader()->keys();
+ for (int i = 0; i < plugins.size(); ++i) {
+ if (!list.contains(plugins.at(i)))
+ list += plugins.at(i);
+ }
+#endif //QT_NO_LIBRARY
+#endif //QT_MAKEDLL
+
+ return list;
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_QWS_KEYBOARD
diff --git a/src/gui/embedded/qkbddriverfactory_qws.h b/src/gui/embedded/qkbddriverfactory_qws.h
new file mode 100644
index 0000000000..45e5664428
--- /dev/null
+++ b/src/gui/embedded/qkbddriverfactory_qws.h
@@ -0,0 +1,70 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QKBDDRIVERFACTORY_QWS_H
+#define QKBDDRIVERFACTORY_QWS_H
+
+#include <QtCore/qstringlist.h>
+
+#ifndef QT_NO_QWS_KEYBOARD
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+class QString;
+class QWSKeyboardHandler;
+
+class Q_GUI_EXPORT QKbdDriverFactory
+{
+public:
+ static QStringList keys();
+ static QWSKeyboardHandler *create(const QString&, const QString&);
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QT_NO_QWS_KEYBOARD
+#endif // QKBDDRIVERFACTORY_QWS_H
diff --git a/src/gui/embedded/qkbddriverplugin_qws.cpp b/src/gui/embedded/qkbddriverplugin_qws.cpp
new file mode 100644
index 0000000000..93dd9df458
--- /dev/null
+++ b/src/gui/embedded/qkbddriverplugin_qws.cpp
@@ -0,0 +1,124 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qkbddriverplugin_qws.h"
+
+#ifndef QT_NO_LIBRARY
+
+#include "qkbd_qws.h"
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QKbdDriverPlugin
+ \ingroup plugins
+ \ingroup qws
+
+ \brief The QKbdDriverPlugin class is an abstract base class for
+ keyboard driver plugins in Qt for Embedded Linux.
+
+ Note that this class is only available in \l{Qt for Embedded Linux}.
+
+ \l{Qt for Embedded Linux} provides ready-made drivers for several keyboard
+ protocols, see the \l{Qt for Embedded Linux Character Input}{character
+ input} documentation for details. Custom keyboard drivers can be
+ implemented by subclassing the QWSKeyboardHandler class and
+ creating a keyboard driver plugin.
+
+ A keyboard driver plugin can be created by subclassing
+ QKbdDriverPlugin and reimplementing the pure virtual keys() and
+ create() functions. By exporting the derived class using the
+ Q_EXPORT_PLUGIN2() macro, the default implementation of the
+ QKbdDriverFactory class will automatically detect the plugin and
+ load the driver into the server application at run-time. See
+ \l{How to Create Qt Plugins} for details.
+
+ \sa QKbdDriverFactory, QWSKeyboardHandler
+*/
+
+/*!
+ \fn QStringList QKbdDriverPlugin::keys() const
+
+ Implement this function to return the list of valid keys, i.e. the
+ keyboard drivers supported by this plugin.
+
+ \l{Qt for Embedded Linux} provides ready-made drivers for several keyboard
+ protocols, see the \l{Qt for Embedded Linux Character Input}{character
+ input} documentation for details.
+
+ \sa create()
+*/
+
+/*!
+ Constructs a keyboard driver plugin with the given \a parent.
+
+ Note that this constructor is invoked automatically by the
+ Q_EXPORT_PLUGIN2() macro, so there is no need for calling it
+ explicitly.
+*/
+QKbdDriverPlugin::QKbdDriverPlugin(QObject *parent)
+ : QObject(parent)
+{
+}
+
+/*!
+ Destroys the keyboard driver plugin.
+
+ Note that Qt destroys a plugin automatically when it is no longer
+ used, so there is no need for calling the destructor explicitly.
+*/
+QKbdDriverPlugin::~QKbdDriverPlugin()
+{
+}
+
+/*!
+ \fn QScreen *QKbdDriverPlugin::create(const QString &key, const QString &device)
+
+ Implement this function to create a driver matching the type
+ specified by the given \a key and \a device parameters. Note that
+ keys are case-insensitive.
+
+ \sa keys()
+*/
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_LIBRARY
diff --git a/src/gui/embedded/qkbddriverplugin_qws.h b/src/gui/embedded/qkbddriverplugin_qws.h
new file mode 100644
index 0000000000..e320b17b8c
--- /dev/null
+++ b/src/gui/embedded/qkbddriverplugin_qws.h
@@ -0,0 +1,84 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QKBDDRIVERPLUGIN_QWS_H
+#define QKBDDRIVERPLUGIN_QWS_H
+
+#include <QtCore/qplugin.h>
+#include <QtCore/qfactoryinterface.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+#ifndef QT_NO_LIBRARY
+
+class QWSKeyboardHandler;
+
+struct Q_GUI_EXPORT QWSKeyboardHandlerFactoryInterface : public QFactoryInterface
+{
+ virtual QWSKeyboardHandler* create(const QString &name, const QString &device) = 0;
+};
+
+#define QWSKeyboardHandlerFactoryInterface_iid "com.trolltech.Qt.QWSKeyboardHandlerFactoryInterface"
+Q_DECLARE_INTERFACE(QWSKeyboardHandlerFactoryInterface, QWSKeyboardHandlerFactoryInterface_iid)
+
+class Q_GUI_EXPORT QKbdDriverPlugin : public QObject, public QWSKeyboardHandlerFactoryInterface
+{
+ Q_OBJECT
+ Q_INTERFACES(QWSKeyboardHandlerFactoryInterface:QFactoryInterface)
+public:
+ explicit QKbdDriverPlugin(QObject *parent = 0);
+ ~QKbdDriverPlugin();
+
+ virtual QStringList keys() const = 0;
+ virtual QWSKeyboardHandler* create(const QString& driver, const QString &device) = 0;
+};
+
+#endif // QT_NO_LIBRARY
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QKBDDRIVERPLUGIN_QWS_H
diff --git a/src/gui/embedded/qkbdpc101_qws.cpp b/src/gui/embedded/qkbdpc101_qws.cpp
new file mode 100644
index 0000000000..3173645785
--- /dev/null
+++ b/src/gui/embedded/qkbdpc101_qws.cpp
@@ -0,0 +1,485 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qkbdpc101_qws.h"
+
+#ifndef QT_NO_QWS_KEYBOARD
+
+#include "qscreen_qws.h"
+#include "qwindowsystem_qws.h"
+#include "qnamespace.h"
+#include "qapplication.h"
+
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#ifdef Q_OS_LINUX
+#include <sys/kd.h>
+#include <sys/vt.h>
+#endif
+
+QT_BEGIN_NAMESPACE
+
+static const QWSKeyMap pc101KeyM[] = {
+ { Qt::Key_unknown, 0xffff , 0xffff , 0xffff },
+ { Qt::Key_Escape, 27 , 27 , 0xffff },
+ { Qt::Key_1, '1' , '!' , 0xffff },
+ { Qt::Key_2, '2' , '@' , 0xffff },
+ { Qt::Key_3, '3' , '#' , 0xffff },
+ { Qt::Key_4, '4' , '$' , 0xffff },
+ { Qt::Key_5, '5' , '%' , 0xffff },
+ { Qt::Key_6, '6' , '^' , 0xffff },
+ { Qt::Key_7, '7' , '&' , 0xffff },
+ { Qt::Key_8, '8' , '*' , 0xffff },
+ { Qt::Key_9, '9' , '(' , 0xffff }, // 10
+ { Qt::Key_0, '0' , ')' , 0xffff },
+ { Qt::Key_Minus, '-' , '_' , 0xffff },
+ { Qt::Key_Equal, '=' , '+' , 0xffff },
+ { Qt::Key_Backspace, 8 , 8 , 0xffff },
+ { Qt::Key_Tab, 9 , 9 , 0xffff },
+ { Qt::Key_Q, 'q' , 'Q' , 'Q'-64 },
+ { Qt::Key_W, 'w' , 'W' , 'W'-64 },
+ { Qt::Key_E, 'e' , 'E' , 'E'-64 },
+ { Qt::Key_R, 'r' , 'R' , 'R'-64 },
+ { Qt::Key_T, 't' , 'T' , 'T'-64 }, // 20
+ { Qt::Key_Y, 'y' , 'Y' , 'Y'-64 },
+ { Qt::Key_U, 'u' , 'U' , 'U'-64 },
+ { Qt::Key_I, 'i' , 'I' , 'I'-64 },
+ { Qt::Key_O, 'o' , 'O' , 'O'-64 },
+ { Qt::Key_P, 'p' , 'P' , 'P'-64 },
+ { Qt::Key_BraceLeft, '[' , '{' , 0xffff },
+ { Qt::Key_BraceRight, ']' , '}' , 0xffff },
+ { Qt::Key_Return, 13 , 13 , 0xffff },
+ { Qt::Key_Control, 0xffff , 0xffff , 0xffff },
+ { Qt::Key_A, 'a' , 'A' , 'A'-64 }, // 30
+ { Qt::Key_S, 's' , 'S' , 'S'-64 },
+ { Qt::Key_D, 'd' , 'D' , 'D'-64 },
+ { Qt::Key_F, 'f' , 'F' , 'F'-64 },
+ { Qt::Key_G, 'g' , 'G' , 'G'-64 },
+ { Qt::Key_H, 'h' , 'H' , 'H'-64 },
+ { Qt::Key_J, 'j' , 'J' , 'J'-64 },
+ { Qt::Key_K, 'k' , 'K' , 'K'-64 },
+ { Qt::Key_L, 'l' , 'L' , 'L'-64 },
+ { Qt::Key_Semicolon, ';' , ':' , 0xffff },
+ { Qt::Key_Apostrophe, '\'' , '"' , 0xffff }, // 40
+ { Qt::Key_QuoteLeft, '`' , '~' , 0xffff },
+ { Qt::Key_Shift, 0xffff , 0xffff , 0xffff },
+ { Qt::Key_Backslash, '\\' , '|' , 0xffff },
+ { Qt::Key_Z, 'z' , 'Z' , 'Z'-64 },
+ { Qt::Key_X, 'x' , 'X' , 'X'-64 },
+ { Qt::Key_C, 'c' , 'C' , 'C'-64 },
+ { Qt::Key_V, 'v' , 'V' , 'V'-64 },
+ { Qt::Key_B, 'b' , 'B' , 'B'-64 },
+ { Qt::Key_N, 'n' , 'N' , 'N'-64 },
+ { Qt::Key_M, 'm' , 'M' , 'M'-64 }, // 50
+ { Qt::Key_Comma, ',' , '<' , 0xffff },
+ { Qt::Key_Period, '.' , '>' , 0xffff },
+ { Qt::Key_Slash, '/' , '?' , 0xffff },
+ { Qt::Key_Shift, 0xffff , 0xffff , 0xffff },
+ { Qt::Key_Asterisk, '*' , '*' , 0xffff },
+ { Qt::Key_Alt, 0xffff , 0xffff , 0xffff },
+ { Qt::Key_Space, ' ' , ' ' , 0xffff },
+ { Qt::Key_CapsLock, 0xffff , 0xffff , 0xffff },
+ { Qt::Key_F1, 0xffff , 0xffff , 0xffff },
+ { Qt::Key_F2, 0xffff , 0xffff , 0xffff }, // 60
+ { Qt::Key_F3, 0xffff , 0xffff , 0xffff },
+ { Qt::Key_F4, 0xffff , 0xffff , 0xffff },
+ { Qt::Key_F5, 0xffff , 0xffff , 0xffff },
+ { Qt::Key_F6, 0xffff , 0xffff , 0xffff },
+ { Qt::Key_F7, 0xffff , 0xffff , 0xffff },
+ { Qt::Key_F8, 0xffff , 0xffff , 0xffff },
+ { Qt::Key_F9, 0xffff , 0xffff , 0xffff },
+ { Qt::Key_F10, 0xffff , 0xffff , 0xffff },
+ { Qt::Key_NumLock, 0xffff , 0xffff , 0xffff },
+ { Qt::Key_ScrollLock, 0xffff , 0xffff , 0xffff }, // 70
+ { Qt::Key_7, '7' , '7' , 0xffff },
+ { Qt::Key_8, '8' , '8' , 0xffff },
+ { Qt::Key_9, '9' , '9' , 0xffff },
+ { Qt::Key_Minus, '-' , '-' , 0xffff },
+ { Qt::Key_4, '4' , '4' , 0xffff },
+ { Qt::Key_5, '5' , '5' , 0xffff },
+ { Qt::Key_6, '6' , '6' , 0xffff },
+ { Qt::Key_Plus, '+' , '+' , 0xffff },
+ { Qt::Key_1, '1' , '1' , 0xffff },
+ { Qt::Key_2, '2' , '2' , 0xffff }, // 80
+ { Qt::Key_3, '3' , '3' , 0xffff },
+ { Qt::Key_0, '0' , '0' , 0xffff },
+ { Qt::Key_Period, '.' , '.' , 0xffff },
+ { Qt::Key_SysReq, 0xffff , 0xffff , 0xffff },
+ { Qt::Key_unknown, 0xffff , 0xffff , 0xffff },
+ { Qt::Key_Less, '<' , '>' , 0xffff },
+ { Qt::Key_F11, 0xffff , 0xffff , 0xffff },
+ { Qt::Key_F12, 0xffff , 0xffff , 0xffff },
+ { Qt::Key_unknown, 0xffff , 0xffff , 0xffff },
+ { Qt::Key_unknown, 0xffff , 0xffff , 0xffff }, // 90
+ { Qt::Key_unknown, 0xffff , 0xffff , 0xffff },
+ { Qt::Key_unknown, 0xffff , 0xffff , 0xffff },
+ { Qt::Key_unknown, 0xffff , 0xffff , 0xffff },
+ { Qt::Key_unknown, 0xffff , 0xffff , 0xffff },
+ { Qt::Key_unknown, 0xffff , 0xffff , 0xffff },
+ { Qt::Key_Enter, 13 , 13 , 0xffff },
+ { Qt::Key_Control, 0xffff , 0xffff , 0xffff },
+ { Qt::Key_Slash, '/' , '/' , 0xffff },
+ { Qt::Key_SysReq, 0xffff , 0xffff , 0xffff },
+ { Qt::Key_Meta, 0xffff , 0xffff , 0xffff }, // 100
+ { Qt::Key_unknown, 0xffff , 0xffff , 0xffff }, // break
+ { Qt::Key_Home, 0xffff , 0xffff , 0xffff },
+ { Qt::Key_Up, 0xffff , 0xffff , 0xffff },
+ { Qt::Key_PageUp, 0xffff , 0xffff , 0xffff },
+ { Qt::Key_Left, 0xffff , 0xffff , 0xffff },
+ { Qt::Key_Right, 0xffff , 0xffff , 0xffff },
+ { Qt::Key_End, 0xffff , 0xffff , 0xffff },
+ { Qt::Key_Down, 0xffff , 0xffff , 0xffff },
+ { Qt::Key_PageDown, 0xffff , 0xffff , 0xffff },
+ { Qt::Key_Insert, 0xffff , 0xffff , 0xffff }, // 110
+ { Qt::Key_Delete, 0xffff , 0xffff , 0xffff },
+ { Qt::Key_unknown, 0xffff , 0xffff , 0xffff }, // macro
+ { Qt::Key_F13, 0xffff , 0xffff , 0xffff },
+ { Qt::Key_F14, 0xffff , 0xffff , 0xffff },
+ { Qt::Key_Help, 0xffff , 0xffff , 0xffff },
+ { Qt::Key_unknown, 0xffff , 0xffff , 0xffff }, // do
+ { Qt::Key_F17, 0xffff , 0xffff , 0xffff },
+ { Qt::Key_Plus, '+' , '-' , 0xffff },
+ { Qt::Key_Pause, 0xffff , 0xffff , 0xffff },
+ { Qt::Key_unknown, 0xffff , 0xffff , 0xffff },
+ { Qt::Key_unknown, 0xffff , 0xffff , 0xffff },
+ { Qt::Key_unknown, 0xffff , 0xffff , 0xffff },
+ { Qt::Key_unknown, 0xffff , 0xffff , 0xffff },
+ { Qt::Key_unknown, 0xffff , 0xffff , 0xffff },
+ { Qt::Key_unknown, 0xffff , 0xffff , 0xffff },
+ { Qt::Key_unknown, 0xffff , 0xffff , 0xffff },
+ { Qt::Key_unknown, 0xffff , 0xffff , 0xffff },
+ { 0, 0xffff , 0xffff , 0xffff }
+};
+
+static const int keyMSize = sizeof(pc101KeyM)/sizeof(QWSKeyMap)-1;
+
+//===========================================================================
+
+//
+// PC-101 type keyboards
+//
+
+/*!
+ \class QWSPC101KeyboardHandler
+ \ingroup qws
+
+ \internal
+*/
+
+QWSPC101KeyboardHandler::QWSPC101KeyboardHandler(const QString&)
+{
+ shift = false;
+ alt = false;
+ ctrl = false;
+ extended = 0;
+ prevuni = 0;
+ prevkey = 0;
+ caps = false;
+#if defined(QT_QWS_IPAQ)
+ // iPAQ Action Key has ScanCode 0x60: 0x60|0x80 = 0xe0 == extended mode 1 !
+ ipaq_return_pressed = false;
+#endif
+}
+
+QWSPC101KeyboardHandler::~QWSPC101KeyboardHandler()
+{
+}
+
+const QWSKeyMap *QWSPC101KeyboardHandler::keyMap() const
+{
+ return pc101KeyM;
+}
+
+void QWSPC101KeyboardHandler::doKey(uchar code)
+{
+
+ int keyCode = Qt::Key_unknown;
+ bool release = false;
+ int keypad = 0;
+ bool softwareRepeat = false;
+
+#ifndef QT_QWS_USE_KEYCODES
+ // extended?
+ if (code == 224
+#if defined(QT_QWS_IPAQ)
+ && !ipaq_return_pressed
+#endif
+ ) {
+ extended = 1;
+ return;
+ } else if (code == 225) {
+ extended = 2;
+ return;
+ }
+#endif
+
+ if (code & 0x80) {
+ release = true;
+ code &= 0x7f;
+ }
+
+#ifndef QT_QWS_USE_KEYCODES
+ if (extended == 1) {
+ switch (code) {
+ case 72:
+ keyCode = Qt::Key_Up;
+ break;
+ case 75:
+ keyCode = Qt::Key_Left;
+ break;
+ case 77:
+ keyCode = Qt::Key_Right;
+ break;
+ case 80:
+ keyCode = Qt::Key_Down;
+ break;
+ case 82:
+ keyCode = Qt::Key_Insert;
+ break;
+ case 71:
+ keyCode = Qt::Key_Home;
+ break;
+ case 73:
+ keyCode = Qt::Key_PageUp;
+ break;
+ case 83:
+ keyCode = Qt::Key_Delete;
+ break;
+ case 79:
+ keyCode = Qt::Key_End;
+ break;
+ case 81:
+ keyCode = Qt::Key_PageDown;
+ break;
+ case 28:
+ keyCode = Qt::Key_Enter;
+ break;
+ case 53:
+ keyCode = Qt::Key_Slash;
+ break;
+ case 0x1d:
+ keyCode = Qt::Key_Control;
+ break;
+ case 0x2a:
+ keyCode = Qt::Key_Print;
+ break;
+ case 0x38:
+ keyCode = Qt::Key_Alt;
+ break;
+ case 0x5b:
+ keyCode = Qt::Key_Super_L;
+ break;
+ case 0x5c:
+ keyCode = Qt::Key_Super_R;
+ break;
+ case 0x5d:
+ keyCode = Qt::Key_Menu;
+ break;
+#if 0
+ default:
+ qDebug("extended1 code %x release %d", code, release);
+ break;
+#endif
+ }
+ } else if (extended == 2) {
+ switch (code) {
+ case 0x1d:
+ return;
+ case 0x45:
+ keyCode = Qt::Key_Pause;
+ break;
+ }
+ } else
+#endif
+ {
+ if (code < keyMSize) {
+ keyCode = pc101KeyM[code].key_code;
+ }
+
+#if defined(QT_QWS_IPAQ) || defined(QT_QWS_EBX)
+ softwareRepeat = true;
+
+ switch (code) {
+ case 0x7a: case 0x7b: case 0x7c: case 0x7d:
+ keyCode = code - 0x7a + Qt::Key_F9;
+ softwareRepeat = false;
+ break;
+ case 0x79:
+ keyCode = Qt::Key_SysReq;
+ softwareRepeat = false;
+ break;
+ case 0x78:
+# ifdef QT_QWS_IPAQ
+ keyCode = Qt::Key_F24; // record
+# else
+ keyCode = Qt::Key_Escape;
+# endif
+ softwareRepeat = false;
+ break;
+ case 0x60:
+ keyCode = Qt::Key_Return;
+# ifdef QT_QWS_IPAQ
+ ipaq_return_pressed = !release;
+# endif
+ break;
+ case 0x67:
+ keyCode = Qt::Key_Right;
+ break;
+ case 0x69:
+ keyCode = Qt::Key_Up;
+ break;
+ case 0x6a:
+ keyCode = Qt::Key_Down;
+ break;
+ case 0x6c:
+ keyCode = Qt::Key_Left;
+ break;
+ }
+
+ if (qt_screen->isTransformed()
+ && keyCode >= Qt::Key_Left && keyCode <= Qt::Key_Down)
+ {
+ keyCode = transformDirKey(keyCode);
+ }
+#endif
+ /*
+ Translate shift+Qt::Key_Tab to Qt::Key_Backtab
+ */
+ if ((keyCode == Qt::Key_Tab) && shift)
+ keyCode = Qt::Key_Backtab;
+ }
+
+#ifndef QT_QWS_USE_KEYCODES
+ /*
+ Qt::Keypad consists of extended keys 53 and 28,
+ and non-extended keys 55 and 71 through 83.
+ */
+ if ((extended == 1) ? (code == 53 || code == 28) :
+ (code == 55 || (code >= 71 && code <= 83)))
+#else
+ if (code == 55 || code >= 71 && code <= 83 || code == 96
+ || code == 98 || code == 118)
+#endif
+ {
+ keypad = Qt::KeypadModifier;
+ }
+
+ // Ctrl-Alt-Backspace exits qws
+ if (ctrl && alt && keyCode == Qt::Key_Backspace) {
+ qApp->quit();
+ }
+
+ if (keyCode == Qt::Key_Alt) {
+ alt = !release;
+ } else if (keyCode == Qt::Key_Control) {
+ ctrl = !release;
+ } else if (keyCode == Qt::Key_Shift) {
+ shift = !release;
+ } else if (keyCode == Qt::Key_CapsLock && release) {
+ caps = !caps;
+#if defined(Q_OS_LINUX)
+ char leds;
+ ioctl(0, KDGETLED, &leds);
+ leds = leds & ~LED_CAP;
+ if (caps) leds |= LED_CAP;
+ ioctl(0, KDSETLED, leds);
+#endif
+ }
+ if (keyCode != Qt::Key_unknown) {
+ bool bAlt = alt;
+ bool bCtrl = ctrl;
+ bool bShift = shift;
+ int unicode = 0;
+ if (code < keyMSize) {
+ if (!extended) {
+ bool bCaps = shift ||
+ (caps ? QChar(keyMap()[code].unicode).isLetter() : false);
+ if (bCtrl)
+ unicode = keyMap()[code].ctrl_unicode ? keyMap()[code].ctrl_unicode : 0xffff;
+ else if (bCaps)
+ unicode = keyMap()[code].shift_unicode ? keyMap()[code].shift_unicode : 0xffff;
+ else
+ unicode = keyMap()[code].unicode ? keyMap()[code].unicode : 0xffff;
+#ifndef QT_QWS_USE_KEYCODES
+ } else if (extended==1) {
+ if (code == 53)
+ unicode = '/';
+#endif
+ }
+ }
+
+ modifiers = 0;
+ if (bAlt) modifiers |= Qt::AltModifier;
+ if (bCtrl) modifiers |= Qt::ControlModifier;
+ if (bShift) modifiers |= Qt::ShiftModifier;
+ if (keypad) modifiers |= Qt::KeypadModifier;
+
+ // looks wrong -- WWA
+ bool repeat = false;
+ if (prevuni == unicode && prevkey == keyCode && !release)
+ repeat = true;
+
+ processKeyEvent(unicode, keyCode, modifiers, !release, repeat);
+
+ if (!release) {
+ prevuni = unicode;
+ prevkey = keyCode;
+ } else {
+ prevkey = prevuni = 0;
+ }
+ }
+
+ if (softwareRepeat && !release)
+ beginAutoRepeat(prevuni, prevkey, modifiers);
+ else
+ endAutoRepeat();
+
+ extended = 0;
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_QWS_KEYBOARD
diff --git a/src/gui/embedded/qkbdpc101_qws.h b/src/gui/embedded/qkbdpc101_qws.h
new file mode 100644
index 0000000000..f9f0104d4c
--- /dev/null
+++ b/src/gui/embedded/qkbdpc101_qws.h
@@ -0,0 +1,95 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QKBDPC101_QWS_H
+#define QKBDPC101_QWS_H
+
+#include <QtGui/qkbd_qws.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+#ifndef QT_NO_QWS_KEYBOARD
+
+#ifndef QT_NO_QWS_KBD_PC101
+
+struct QWSKeyMap {
+ uint key_code;
+ ushort unicode;
+ ushort shift_unicode;
+ ushort ctrl_unicode;
+};
+
+class QWSPC101KeyboardHandler : public QWSKeyboardHandler
+{
+public:
+ explicit QWSPC101KeyboardHandler(const QString&);
+ virtual ~QWSPC101KeyboardHandler();
+
+ virtual void doKey(uchar scancode);
+ virtual const QWSKeyMap *keyMap() const;
+
+protected:
+ bool shift;
+ bool alt;
+ bool ctrl;
+ bool caps;
+#if defined(QT_QWS_IPAQ)
+ uint ipaq_return_pressed:1;
+#endif
+ uint extended:2;
+ Qt::KeyboardModifiers modifiers;
+ int prevuni;
+ int prevkey;
+};
+
+#endif // QT_NO_QWS_KBD_PC101
+
+#endif // QT_NO_QWS_KEYBOARD
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QKBDPC101_QWS_H
diff --git a/src/gui/embedded/qkbdsl5000_qws.cpp b/src/gui/embedded/qkbdsl5000_qws.cpp
new file mode 100644
index 0000000000..bc412b6ee8
--- /dev/null
+++ b/src/gui/embedded/qkbdsl5000_qws.cpp
@@ -0,0 +1,356 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qkbdsl5000_qws.h"
+
+#ifndef QT_NO_QWS_KBD_SL5000
+
+#include "qwindowsystem_qws.h"
+#include "qwsutils_qws.h"
+#include "qscreen_qws.h"
+
+#include "qapplication.h"
+#include "qnamespace.h"
+#include "qtimer.h"
+
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <signal.h>
+
+#include <asm/sharp_char.h>
+
+#include <string.h>
+
+QT_BEGIN_NAMESPACE
+
+static const QWSKeyMap sl5000KeyMap[] = {
+ { Qt::Key_unknown, 0xffff , 0xffff , 0xffff }, // 00
+ { Qt::Key_A, 'a' , 'A' , 'A'-64 }, // 01
+ { Qt::Key_B, 'b' , 'B' , 'B'-64 }, // 02
+ { Qt::Key_C, 'c' , 'C' , 'C'-64 }, // 03
+ { Qt::Key_D, 'd' , 'D' , 'D'-64 }, // 04
+ { Qt::Key_E, 'e' , 'E' , 'E'-64 }, // 05
+ { Qt::Key_F, 'f' , 'F' , 'F'-64 }, // 06
+ { Qt::Key_G, 'g' , 'G' , 'G'-64 }, // 07
+ { Qt::Key_H, 'h' , 'H' , 'H'-64 }, // 08
+ { Qt::Key_I, 'i' , 'I' , 'I'-64 }, // 09
+ { Qt::Key_J, 'j' , 'J' , 'J'-64 }, // 0a 10
+ { Qt::Key_K, 'k' , 'K' , 'K'-64 }, // 0b
+ { Qt::Key_L, 'l' , 'L' , 'L'-64 }, // 0c
+ { Qt::Key_M, 'm' , 'M' , 'M'-64 }, // 0d
+ { Qt::Key_N, 'n' , 'N' , 'N'-64 }, // 0e
+ { Qt::Key_O, 'o' , 'O' , 'O'-64 }, // 0f
+ { Qt::Key_P, 'p' , 'P' , 'P'-64 }, // 10
+ { Qt::Key_Q, 'q' , 'Q' , 'Q'-64 }, // 11
+ { Qt::Key_R, 'r' , 'R' , 'R'-64 }, // 12
+ { Qt::Key_S, 's' , 'S' , 'S'-64 }, // 13
+ { Qt::Key_T, 't' , 'T' , 'T'-64 }, // 14 20
+ { Qt::Key_U, 'u' , 'U' , 'U'-64 }, // 15
+ { Qt::Key_V, 'v' , 'V' , 'V'-64 }, // 16
+ { Qt::Key_W, 'w' , 'W' , 'W'-64 }, // 17
+ { Qt::Key_X, 'x' , 'X' , 'X'-64 }, // 18
+ { Qt::Key_Y, 'y' , 'Y' , 'Y'-64 }, // 19
+ { Qt::Key_Z, 'z' , 'Z' , 'Z'-64 }, // 1a
+ { Qt::Key_Shift, 0xffff , 0xffff , 0xffff }, // 1b
+ { Qt::Key_Return, 13 , 13 , 0xffff }, // 1c
+ { Qt::Key_F11, 0xffff , 0xffff , 0xffff }, // 1d todo
+ { Qt::Key_F22, 0xffff , 0xffff , 0xffff }, // 1e 30
+ { Qt::Key_Backspace, 8 , 8 , 0xffff }, // 1f
+ { Qt::Key_F31, 0xffff , 0xffff , 0xffff }, // 20
+ { Qt::Key_F35, 0xffff , 0xffff , 0xffff }, // 21 light
+ { Qt::Key_Escape, 0xffff , 0xffff , 0xffff }, // 22
+
+ // Direction key code are for *UNROTATED* display.
+ { Qt::Key_Up, 0xffff , 0xffff , 0xffff }, // 23
+ { Qt::Key_Right, 0xffff , 0xffff , 0xffff }, // 24
+ { Qt::Key_Left, 0xffff , 0xffff , 0xffff }, // 25
+ { Qt::Key_Down, 0xffff , 0xffff , 0xffff }, // 26
+
+ { Qt::Key_F33, 0xffff , 0xffff , 0xffff }, // 27 OK
+ { Qt::Key_F12, 0xffff , 0xffff , 0xffff }, // 28 40 home
+ { Qt::Key_1, '1' , 'q' , 'Q'-64 }, // 29
+ { Qt::Key_2, '2' , 'w' , 'W'-64 }, // 2a
+ { Qt::Key_3, '3' , 'e' , 'E'-64 }, // 2b
+ { Qt::Key_4, '4' , 'r' , 'R'-64 }, // 2c
+ { Qt::Key_5, '5' , 't' , 'T'-64 }, // 2d
+ { Qt::Key_6, '6' , 'y' , 'Y'-64 }, // 2e
+ { Qt::Key_7, '7' , 'u' , 'U'-64 }, // 2f
+ { Qt::Key_8, '8' , 'i' , 'I'-64 }, // 30
+ { Qt::Key_9, '9' , 'o' , 'O'-64 }, // 31
+ { Qt::Key_0, '0' , 'p' , 'P'-64 }, // 32 50
+ { Qt::Key_unknown, 0xffff , 0xffff , 0xffff }, // 33
+ { Qt::Key_unknown, 0xffff , 0xffff , 0xffff }, // 34
+ { Qt::Key_unknown, 0xffff , 0xffff , 0xffff }, // 35
+ { Qt::Key_unknown, 0xffff , 0xffff , 0xffff }, // 36
+ { Qt::Key_unknown, 0xffff , 0xffff , 0xffff }, // 37
+ { Qt::Key_unknown, 0xffff , 0xffff , 0xffff }, // 38
+ { Qt::Key_unknown, 0xffff , 0xffff , 0xffff }, // 39
+ { Qt::Key_Minus, '-' , 'b' , 'B'-64 }, // 3a
+ { Qt::Key_Plus, '+' , 'n' , 'N'-64 }, // 3b
+ { Qt::Key_CapsLock, 0xffff , 0xffff , 0xffff }, // 3c 60
+ { Qt::Key_At, '@' , 's' , 'S'-64 }, // 3d
+ { Qt::Key_Question, '?' , '?' , 0xffff }, // 3e
+ { Qt::Key_Comma, ',' , ',' , 0xffff }, // 3f
+ { Qt::Key_Period, '.' , '.' , 0xffff }, // 40
+ { Qt::Key_Tab, 9 , '\\' , 0xffff }, // 41
+ { Qt::Key_X, 0xffff , 'x' , 'X'-64 }, // 42
+ { Qt::Key_C, 0xffff , 'c' , 'C'-64 }, // 43
+ { Qt::Key_V, 0xffff , 'v' , 'V'-64 }, // 44
+ { Qt::Key_Slash, '/' , '/' , 0xffff }, // 45
+ { Qt::Key_Apostrophe, '\'' , '\'' , 0xffff }, // 46 70
+ { Qt::Key_Semicolon, ';' , ';' , 0xffff }, // 47
+ { Qt::Key_QuoteDbl, '\"' , '\"' , 0xffff }, // 48
+ { Qt::Key_Colon, ':' , ':' , 0xffff }, // 49
+ { Qt::Key_NumberSign, '#' , 'd' , 'D'-64 }, // 4a
+ { Qt::Key_Dollar, '$' , 'f' , 'F'-64 }, // 4b
+ { Qt::Key_Percent, '%' , 'g' , 'G'-64 }, // 4c
+ { Qt::Key_Underscore, '_' , 'h' , 'H'-64 }, // 4d
+ { Qt::Key_Ampersand, '&' , 'j' , 'J'-64 }, // 4e
+ { Qt::Key_Asterisk, '*' , 'k' , 'K'-64 }, // 4f
+ { Qt::Key_ParenLeft, '(' , 'l' , 'L'-64 }, // 50 80
+ { Qt::Key_Delete, '[' , '[' , '[' }, // 51
+ { Qt::Key_Z, 0xffff , 'z' , 'Z'-64 }, // 52
+ { Qt::Key_Equal, '=' , 'm' , 'M'-64 }, // 53
+ { Qt::Key_ParenRight, ')' , ']' , ']' }, // 54
+ { Qt::Key_AsciiTilde, '~' , '^' , '^' }, // 55
+ { Qt::Key_Less, '<' , '{' , '{' }, // 56
+ { Qt::Key_Greater, '>' , '}' , '}' }, // 57
+ { Qt::Key_F9, 0xffff , 0xffff , 0xffff }, // 58 datebook
+ { Qt::Key_F10, 0xffff , 0xffff , 0xffff }, // 59 address
+ { Qt::Key_F13, 0xffff , 0xffff , 0xffff }, // 5a 90 email
+ { Qt::Key_F30, ' ' , ' ' , 0xffff }, // 5b select
+ { Qt::Key_Space, ' ' , '|' , '`' }, // 5c
+ { Qt::Key_unknown, 0xffff , 0xffff , 0xffff }, // 5d
+ { Qt::Key_Exclam, '!' , 'a' , 'A'-64 }, // 5e
+ { Qt::Key_unknown, 0xffff , 0xffff , 0xffff }, // 5f
+ { Qt::Key_unknown, 0xffff , 0xffff , 0xffff }, // 60
+ { Qt::Key_unknown, 0xffff , 0xffff , 0xffff }, // 61
+ { Qt::Key_unknown, 0xffff , 0xffff , 0xffff }, // 62
+ { Qt::Key_unknown, 0xffff , 0xffff , 0xffff }, // 63
+ { Qt::Key_unknown, 0xffff , 0xffff , 0xffff }, // 64
+ { Qt::Key_unknown, 0xffff , 0xffff , 0xffff }, // 65
+ { Qt::Key_unknown, 0xffff , 0xffff , 0xffff }, // 66
+ { Qt::Key_Meta, 0xffff , 0xffff , 0xffff }, // 67
+ { Qt::Key_unknown, 0xffff , 0xffff , 0xffff }, // 68
+ { Qt::Key_unknown, 0xffff , 0xffff , 0xffff }, // 69
+ { Qt::Key_unknown, 0xffff , 0xffff , 0xffff }, // 6a
+ { Qt::Key_unknown, 0xffff , 0xffff , 0xffff }, // 6b
+ { Qt::Key_unknown, 0xffff , 0xffff , 0xffff }, // 6c
+ { Qt::Key_F34, 0xffff , 0xffff , 0xffff }, // 6d power
+ { Qt::Key_F13, 0xffff , 0xffff , 0xffff }, // 6e mail long
+ { Qt::Key_unknown, 0xffff , 0xffff , 0xffff }, // 6f
+ { Qt::Key_NumLock, 0xffff , 0xffff , 0xffff }, // 70
+ { Qt::Key_unknown, 0xffff , 0xffff , 0xffff }, // 71
+ { Qt::Key_unknown, 0xffff , 0xffff , 0xffff }, // 72
+ { 0x20ac, 0xffff , 0x20ac , 0x20ac }, // 73 Euro sign
+ { Qt::Key_unknown, 0xffff , 0xffff , 0xffff }, // 74
+ { Qt::Key_F32, 0xffff , 0xffff , 0xffff }, // 75 Sync
+ { 0, 0xffff , 0xffff , 0xffff }
+};
+
+static const int keyMSize = sizeof(sl5000KeyMap)/sizeof(QWSKeyMap)-1;
+
+QWSSL5000KeyboardHandler::QWSSL5000KeyboardHandler(const QString &device)
+ : QWSTtyKeyboardHandler(device)
+{
+ meta = false;
+ fn = false;
+ numLock = false;
+
+ sharp_kbdctl_modifstat st;
+ int dev = ::open(device.isEmpty()?"/dev/sharp_kbdctl":device.toLocal8Bit().constData(), O_RDWR);
+ if (dev >= 0) {
+ memset(&st, 0, sizeof(st));
+ st.which = 3;
+ int ret = ioctl(dev, SHARP_KBDCTL_GETMODIFSTAT, (char*)&st);
+ if(!ret)
+ numLock = (bool)st.stat;
+ ::close(dev);
+ }
+}
+
+QWSSL5000KeyboardHandler::~QWSSL5000KeyboardHandler()
+{
+}
+
+const QWSKeyMap *QWSSL5000KeyboardHandler::keyMap() const
+{
+ return sl5000KeyMap;
+}
+
+void QWSSL5000KeyboardHandler::doKey(uchar code)
+{
+ int keyCode = Qt::Key_unknown;
+ bool release = false;
+
+ if (code & 0x80) {
+ release = true;
+ code &= 0x7f;
+ }
+
+ if (fn && !meta && (code >= 0x42 && code <= 0x52)) {
+ ushort unicode=0;
+ int scan=0;
+ if (code == 0x42) { unicode='X'-'@'; scan=Qt::Key_X; } // Cut
+ else if (code == 0x43) { unicode='C'-'@'; scan=Qt::Key_C; } // Copy
+ else if (code == 0x44) { unicode='V'-'@'; scan=Qt::Key_V; } // Paste
+ else if (code == 0x52) { unicode='Z'-'@'; scan=Qt::Key_Z; } // Undo
+ if (scan) {
+ processKeyEvent(unicode, scan, Qt::ControlModifier, !release, false);
+ return;
+ }
+ }
+
+ if (code < keyMSize) {
+ keyCode = keyMap()[code].key_code;
+ }
+
+ bool repeatable = true;
+
+ if (release && (keyCode == Qt::Key_F34 || keyCode == Qt::Key_F35))
+ return; // no release for power and light keys
+ if (keyCode >= Qt::Key_F1 && keyCode <= Qt::Key_F35
+ || keyCode == Qt::Key_Escape || keyCode == Qt::Key_Home
+ || keyCode == Qt::Key_Shift || keyCode == Qt::Key_Meta)
+ repeatable = false;
+
+ if (qt_screen->isTransformed()
+ && keyCode >= Qt::Key_Left && keyCode <= Qt::Key_Down)
+ {
+ keyCode = transformDirKey(keyCode);
+ }
+
+ // Ctrl-Alt-Delete exits qws
+ if (ctrl && alt && keyCode == Qt::Key_Delete) {
+ qApp->quit();
+ }
+
+ if (keyCode == Qt::Key_F22) { /* Fn key */
+ fn = !release;
+ } else if (keyCode == Qt::Key_NumLock) {
+ if (release)
+ numLock = !numLock;
+ } else if (keyCode == Qt::AltModifier) {
+ alt = !release;
+ } else if (keyCode == Qt::ControlModifier) {
+ ctrl = !release;
+ } else if (keyCode == Qt::ShiftModifier) {
+ shift = !release;
+ } else if (keyCode == Qt::MetaModifier) {
+ meta = !release;
+ } else if (keyCode == Qt::Key_CapsLock && release) {
+ caps = !caps;
+ }
+ if (keyCode != Qt::Key_unknown) {
+ bool bAlt = alt;
+ bool bCtrl = ctrl;
+ bool bShift = shift;
+ int unicode = 0;
+ if (code < keyMSize) {
+ bool bCaps = caps ^ shift;
+ if (fn) {
+ if (shift) {
+ bCaps = bShift = false;
+ bCtrl = true;
+ }
+ if (meta) {
+ bCaps = bShift = true;
+ bAlt = true;
+ }
+ } else if (meta) {
+ bCaps = bShift = true;
+ }
+ if (code > 40 && caps) {
+ // fn-keys should only react to shift, not caps
+ bCaps = bShift = shift;
+ }
+ if (numLock) {
+ if (keyCode != Qt::Key_Space && keyCode != Qt::Key_Tab)
+ bCaps = bShift = false;
+ }
+ if (keyCode == Qt::Key_Delete && (bAlt || bCtrl)) {
+ keyCode = Qt::Key_BraceLeft;
+ unicode = '[';
+ bCaps = bShift = bAlt = bCtrl = false;
+ } else if (keyCode == Qt::Key_F31 && bCtrl) {
+ keyCode = Qt::Key_QuoteLeft;
+ unicode = '`';
+ } else if (bCtrl)
+ unicode = keyMap()[code].ctrl_unicode ? keyMap()[code].ctrl_unicode : 0xffff;
+ else if (bCaps)
+ unicode = keyMap()[code].shift_unicode ? keyMap()[code].shift_unicode : 0xffff;
+ else
+ unicode = keyMap()[code].unicode ? keyMap()[code].unicode : 0xffff;
+ }
+
+ modifiers = 0;
+ if (bAlt) modifiers |= Qt::AltModifier;
+ if (bCtrl) modifiers |= Qt::ControlModifier;
+ if (bShift) modifiers |= Qt::ShiftModifier;
+
+ // looks wrong -- WWA
+ bool repeat = false;
+ if (prevuni == unicode && prevkey == keyCode && !release)
+ repeat = true;
+
+ processKeyEvent(unicode, keyCode, modifiers, !release, repeat);
+
+ if (!release) {
+ prevuni = unicode;
+ prevkey = keyCode;
+ } else {
+ prevkey = prevuni = 0;
+ }
+ }
+
+ if (repeatable && !release)
+ beginAutoRepeat(prevuni, prevkey, modifiers);
+ else
+ endAutoRepeat();
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_QWS_KBD_SL5000
diff --git a/src/gui/embedded/qkbdsl5000_qws.h b/src/gui/embedded/qkbdsl5000_qws.h
new file mode 100644
index 0000000000..514d6028c7
--- /dev/null
+++ b/src/gui/embedded/qkbdsl5000_qws.h
@@ -0,0 +1,79 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QKBDSL5000_QWS_H
+#define QKBDSL5000_QWS_H
+
+#include <QtGui/qkbdtty_qws.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+#ifndef QT_NO_QWS_KBD_SL5000
+
+class QWSSL5000KbPrivate;
+
+class QWSSL5000KeyboardHandler : public QWSTtyKeyboardHandler
+{
+public:
+ explicit QWSSL5000KeyboardHandler(const QString&);
+ virtual ~QWSSL5000KeyboardHandler();
+
+ virtual void doKey(uchar scancode);
+ virtual const QWSKeyMap *keyMap() const;
+
+private:
+ bool meta;
+ bool fn;
+ bool numLock;
+ QWSSL5000KbPrivate *d;
+};
+
+#endif // QT_NO_QWS_KBD_SL5000
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QKBDSL5000_QWS_H
diff --git a/src/gui/embedded/qkbdtty_qws.cpp b/src/gui/embedded/qkbdtty_qws.cpp
new file mode 100644
index 0000000000..b588e55d7b
--- /dev/null
+++ b/src/gui/embedded/qkbdtty_qws.cpp
@@ -0,0 +1,263 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qkbdtty_qws.h"
+
+#if !defined(QT_NO_QWS_KEYBOARD) && !defined(QT_NO_QWS_KBD_TTY)
+
+#include "qscreen_qws.h"
+
+#include "qwindowsystem_qws.h"
+#include "qapplication.h"
+#include "qsocketnotifier.h"
+#include "qnamespace.h"
+#include "qtimer.h"
+#include <private/qwssignalhandler_p.h>
+#include <private/qwindowsurface_qws_p.h>
+
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <signal.h>
+#include <termios.h>
+
+#include <qeventloop.h>
+
+#ifdef Q_OS_LINUX
+#include <sys/kd.h>
+#include <sys/vt.h>
+#endif
+
+QT_BEGIN_NAMESPACE
+
+#define VTACQSIG SIGUSR1
+#define VTRELSIG SIGUSR2
+
+static int vtQws = 0;
+static int kbdFD = -1;
+
+//===========================================================================
+
+//
+// Tty keyboard
+//
+
+class QWSTtyKbPrivate : public QObject
+{
+ Q_OBJECT
+public:
+ QWSTtyKbPrivate(QWSPC101KeyboardHandler *, const QString &device);
+ ~QWSTtyKbPrivate();
+
+private slots:
+ void readKeyboardData();
+ void handleTtySwitch(int);
+
+private:
+ QWSPC101KeyboardHandler *handler;
+ struct termios origTermData;
+};
+
+QWSTtyKeyboardHandler::QWSTtyKeyboardHandler(const QString &device)
+ : QWSPC101KeyboardHandler(device)
+{
+ d = new QWSTtyKbPrivate(this, device);
+}
+
+QWSTtyKeyboardHandler::~QWSTtyKeyboardHandler()
+{
+ delete d;
+}
+
+void QWSTtyKeyboardHandler::processKeyEvent(int unicode, int keycode,
+ Qt::KeyboardModifiers modifiers, bool isPress,
+ bool autoRepeat)
+{
+#if defined(Q_OS_LINUX)
+ // Virtual console switching
+ int term = 0;
+ bool ctrl = modifiers & Qt::ControlModifier;
+ bool alt = modifiers & Qt::AltModifier;
+ if (ctrl && alt && keycode >= Qt::Key_F1 && keycode <= Qt::Key_F10)
+ term = keycode - Qt::Key_F1 + 1;
+ else if (ctrl && alt && keycode == Qt::Key_Left)
+ term = qMax(vtQws - 1, 1);
+ else if (ctrl && alt && keycode == Qt::Key_Right)
+ term = qMin(vtQws + 1, 10);
+ if (term && isPress) {
+ ioctl(kbdFD, VT_ACTIVATE, term);
+ return;
+ }
+#endif
+
+ QWSPC101KeyboardHandler::processKeyEvent(unicode, keycode, modifiers,
+ isPress, autoRepeat);
+}
+
+
+QWSTtyKbPrivate::QWSTtyKbPrivate(QWSPC101KeyboardHandler *h, const QString &device) : handler(h)
+{
+ kbdFD = ::open(device.isEmpty()?"/dev/tty0":device.toLatin1().constData(), O_RDWR|O_NDELAY, 0);
+#ifndef QT_NO_QWS_SIGNALHANDLER
+ QWSSignalHandler::instance()->addObject(this);
+#endif
+
+ if (kbdFD >= 0) {
+ QSocketNotifier *notifier;
+ notifier = new QSocketNotifier(kbdFD, QSocketNotifier::Read, this);
+ connect(notifier, SIGNAL(activated(int)),this,
+ SLOT(readKeyboardData()));
+
+ // save for restore.
+ tcgetattr(kbdFD, &origTermData);
+
+ struct termios termdata;
+ tcgetattr(kbdFD, &termdata);
+
+#if defined(Q_OS_LINUX)
+# ifdef QT_QWS_USE_KEYCODES
+ ioctl(kbdFD, KDSKBMODE, K_MEDIUMRAW);
+# else
+ ioctl(kbdFD, KDSKBMODE, K_RAW);
+# endif
+#endif
+
+ termdata.c_iflag = (IGNPAR | IGNBRK) & (~PARMRK) & (~ISTRIP);
+ termdata.c_oflag = 0;
+ termdata.c_cflag = CREAD | CS8;
+ termdata.c_lflag = 0;
+ termdata.c_cc[VTIME]=0;
+ termdata.c_cc[VMIN]=1;
+ cfsetispeed(&termdata, 9600);
+ cfsetospeed(&termdata, 9600);
+ tcsetattr(kbdFD, TCSANOW, &termdata);
+
+#if defined(Q_OS_LINUX)
+
+ connect(QApplication::instance(), SIGNAL(unixSignal(int)), this, SLOT(handleTtySwitch(int)));
+ QApplication::instance()->watchUnixSignal(VTACQSIG, true);
+ QApplication::instance()->watchUnixSignal(VTRELSIG, true);
+
+ struct vt_mode vtMode;
+ ioctl(kbdFD, VT_GETMODE, &vtMode);
+
+ // let us control VT switching
+ vtMode.mode = VT_PROCESS;
+ vtMode.relsig = VTRELSIG;
+ vtMode.acqsig = VTACQSIG;
+ ioctl(kbdFD, VT_SETMODE, &vtMode);
+
+ struct vt_stat vtStat;
+ ioctl(kbdFD, VT_GETSTATE, &vtStat);
+ vtQws = vtStat.v_active;
+#endif
+ } else {
+ qCritical("Cannot open keyboard: %s", strerror(errno));
+ }
+
+}
+
+QWSTtyKbPrivate::~QWSTtyKbPrivate()
+{
+ if (kbdFD >= 0) {
+#if defined(Q_OS_LINUX)
+ ioctl(kbdFD, KDSKBMODE, K_XLATE);
+#endif
+ tcsetattr(kbdFD, TCSANOW, &origTermData);
+ ::close(kbdFD);
+ kbdFD = -1;
+ }
+}
+
+void QWSTtyKbPrivate::handleTtySwitch(int sig)
+{
+#if defined(Q_OS_LINUX)
+ if (sig == VTACQSIG) {
+ if (ioctl(kbdFD, VT_RELDISP, VT_ACKACQ) == 0) {
+ qwsServer->enablePainting(true);
+ qt_screen->restore();
+ qwsServer->resumeMouse();
+ qwsServer->refresh();
+ }
+ } else if (sig == VTRELSIG) {
+ qwsServer->enablePainting(false);
+
+ // Check for reserved surfaces which might still do painting
+ bool allWindowsHidden = true;
+ const QList<QWSWindow*> windows = QWSServer::instance()->clientWindows();
+ for (int i = 0; i < windows.size(); ++i) {
+ const QWSWindow *w = windows.at(i);
+ QWSWindowSurface *s = w->windowSurface();
+ if (s && s->isRegionReserved() && !w->allocatedRegion().isEmpty()) {
+ allWindowsHidden = false;
+ break;
+ }
+ }
+
+ if (!allWindowsHidden) {
+ ioctl(kbdFD, VT_RELDISP, 0); // abort console switch
+ qwsServer->enablePainting(true);
+ } else if (ioctl(kbdFD, VT_RELDISP, 1) == 0) {
+ qt_screen->save();
+ qwsServer->suspendMouse();
+ } else {
+ qwsServer->enablePainting(true);
+ }
+ }
+#endif
+}
+
+void QWSTtyKbPrivate::readKeyboardData()
+{
+ unsigned char buf[81];
+ int n = read(kbdFD, buf, 80);
+ for (int loop = 0; loop < n; loop++)
+ handler->doKey(buf[loop]);
+}
+
+QT_END_NAMESPACE
+
+#include "qkbdtty_qws.moc"
+
+#endif // QT_NO_QWS_KEYBOARD || QT_NO_QWS_KBD_TTY
diff --git a/src/gui/embedded/qkbdtty_qws.h b/src/gui/embedded/qkbdtty_qws.h
new file mode 100644
index 0000000000..4f93d6cbbb
--- /dev/null
+++ b/src/gui/embedded/qkbdtty_qws.h
@@ -0,0 +1,81 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QKBDTTY_QWS_H
+#define QKBDTTY_QWS_H
+
+#include <QtGui/qkbdpc101_qws.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+#ifndef QT_NO_QWS_KEYBOARD
+
+#ifndef QT_NO_QWS_KBD_TTY
+
+class QWSTtyKbPrivate;
+
+class QWSTtyKeyboardHandler : public QWSPC101KeyboardHandler
+{
+public:
+ explicit QWSTtyKeyboardHandler(const QString&);
+ virtual ~QWSTtyKeyboardHandler();
+
+protected:
+ virtual void processKeyEvent(int unicode, int keycode, Qt::KeyboardModifiers modifiers,
+ bool isPress, bool autoRepeat);
+
+private:
+ QWSTtyKbPrivate *d;
+};
+
+#endif // QT_NO_QWS_KBD_TTY
+
+#endif // QT_NO_QWS_KEYBOARD
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QKBDTTY_QWS_H
diff --git a/src/gui/embedded/qkbdum_qws.cpp b/src/gui/embedded/qkbdum_qws.cpp
new file mode 100644
index 0000000000..d525c667c9
--- /dev/null
+++ b/src/gui/embedded/qkbdum_qws.cpp
@@ -0,0 +1,143 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qkbdum_qws.h"
+#include "qvfbhdr.h"
+
+#if !defined(QT_NO_QWS_KEYBOARD) && !defined(QT_NO_QWS_KBD_UM)
+
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#include <qstring.h>
+#include <qwindowsystem_qws.h>
+#include <qsocketnotifier.h>
+
+QT_BEGIN_NAMESPACE
+
+class QWSUmKeyboardHandlerPrivate : public QObject
+{
+ Q_OBJECT
+
+public:
+ QWSUmKeyboardHandlerPrivate(const QString&);
+ ~QWSUmKeyboardHandlerPrivate();
+
+private slots:
+ void readKeyboardData();
+
+private:
+ int kbdFD;
+ int kbdIdx;
+ const int kbdBufferLen;
+ unsigned char *kbdBuffer;
+ QSocketNotifier *notifier;
+};
+
+QWSUmKeyboardHandlerPrivate::QWSUmKeyboardHandlerPrivate(const QString &device)
+ : kbdFD(-1), kbdIdx(0), kbdBufferLen(sizeof(QVFbKeyData)*5)
+{
+ kbdBuffer = new unsigned char [kbdBufferLen];
+
+ if ((kbdFD = open((const char *)device.toLocal8Bit(), O_RDONLY | O_NDELAY)) < 0) {
+ qDebug("Cannot open %s (%s)", (const char *)device.toLocal8Bit(),
+ strerror(errno));
+ } else {
+ // Clear pending input
+ char buf[2];
+ while (read(kbdFD, buf, 1) > 0) { }
+
+ notifier = new QSocketNotifier(kbdFD, QSocketNotifier::Read, this);
+ connect(notifier, SIGNAL(activated(int)),this, SLOT(readKeyboardData()));
+ }
+}
+
+QWSUmKeyboardHandlerPrivate::~QWSUmKeyboardHandlerPrivate()
+{
+ if (kbdFD >= 0)
+ close(kbdFD);
+ delete [] kbdBuffer;
+}
+
+
+void QWSUmKeyboardHandlerPrivate::readKeyboardData()
+{
+ int n;
+ do {
+ n = read(kbdFD, kbdBuffer+kbdIdx, kbdBufferLen - kbdIdx);
+ if (n > 0)
+ kbdIdx += n;
+ } while (n > 0);
+
+ int idx = 0;
+ while (kbdIdx - idx >= (int)sizeof(QVFbKeyData)) {
+ QVFbKeyData *kd = (QVFbKeyData *)(kbdBuffer + idx);
+ // Qtopia Key filters must still work.
+ QWSServer::processKeyEvent(kd->unicode, kd->keycode, kd->modifiers, kd->press, kd->repeat);
+ idx += sizeof(QVFbKeyData);
+ }
+
+ int surplus = kbdIdx - idx;
+ for (int i = 0; i < surplus; i++)
+ kbdBuffer[i] = kbdBuffer[idx+i];
+ kbdIdx = surplus;
+}
+
+QWSUmKeyboardHandler::QWSUmKeyboardHandler(const QString &device)
+ : QWSKeyboardHandler()
+{
+ d = new QWSUmKeyboardHandlerPrivate(device);
+}
+
+QWSUmKeyboardHandler::~QWSUmKeyboardHandler()
+{
+ delete d;
+}
+
+QT_END_NAMESPACE
+
+#include "qkbdum_qws.moc"
+
+#endif // QT_NO_QWS_KEYBOARD && QT_NO_QWS_KBD_UM
diff --git a/src/gui/embedded/qkbdum_qws.h b/src/gui/embedded/qkbdum_qws.h
new file mode 100644
index 0000000000..96aaef6349
--- /dev/null
+++ b/src/gui/embedded/qkbdum_qws.h
@@ -0,0 +1,77 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QKBDUM_QWS_H
+#define QKBDUM_QWS_H
+
+#include <QtGui/qkbd_qws.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+#ifndef QT_NO_QWS_KEYBOARD
+
+#ifndef QT_NO_QWS_KBD_UM
+
+class QWSUmKeyboardHandlerPrivate;
+
+class QWSUmKeyboardHandler : public QWSKeyboardHandler
+{
+public:
+ QWSUmKeyboardHandler(const QString &);
+ virtual ~QWSUmKeyboardHandler();
+
+private:
+
+ QWSUmKeyboardHandlerPrivate *d;
+};
+#endif // QT_NO_QWS_KBD_UM
+
+#endif // QT_NO_QWS_KEYBOARD
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QKBDUM_QWS_H
diff --git a/src/gui/embedded/qkbdusb_qws.cpp b/src/gui/embedded/qkbdusb_qws.cpp
new file mode 100644
index 0000000000..e35ac55ff5
--- /dev/null
+++ b/src/gui/embedded/qkbdusb_qws.cpp
@@ -0,0 +1,401 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qkbdusb_qws.h"
+
+#ifndef QT_NO_QWS_KEYBOARD
+
+#include "qscreen_qws.h"
+
+#include "qwindowsystem_qws.h"
+#include "qapplication.h"
+#include "qsocketnotifier.h"
+#include "qnamespace.h"
+#include "qtimer.h"
+
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <signal.h>
+
+#include <linux/input.h>
+
+#ifdef Q_OS_LINUX
+#include <sys/kd.h>
+#include <sys/vt.h>
+#endif
+
+QT_BEGIN_NAMESPACE
+
+/* USB driver */
+
+
+class QWSUsbKbPrivate : public QObject
+{
+ Q_OBJECT
+public:
+ QWSUsbKbPrivate(QWSPC101KeyboardHandler *, const QString &);
+ ~QWSUsbKbPrivate();
+
+private slots:
+ void readKeyboardData();
+
+private:
+ QWSPC101KeyboardHandler *handler;
+ int fd;
+#ifdef QT_QWS_ZYLONITE
+ bool shift;
+#endif
+};
+
+QWSUsbKeyboardHandler::QWSUsbKeyboardHandler(const QString &device)
+ : QWSPC101KeyboardHandler(device)
+{
+ d = new QWSUsbKbPrivate(this, device);
+}
+
+QWSUsbKeyboardHandler::~QWSUsbKeyboardHandler()
+{
+ delete d;
+}
+
+QWSUsbKbPrivate::QWSUsbKbPrivate(QWSPC101KeyboardHandler *h, const QString &device) : handler(h)
+{
+#ifdef QT_QWS_ZYLONITE
+ shift = FALSE;
+#endif
+ fd = ::open(device.isEmpty()?"/dev/input/event1":device.toLocal8Bit(),O_RDONLY, 0);
+ if (fd >= 0) {
+ QSocketNotifier *notifier;
+ notifier = new QSocketNotifier(fd, QSocketNotifier::Read, this);
+ connect(notifier, SIGNAL(activated(int)),this,
+ SLOT(readKeyboardData()));
+ }
+}
+
+QWSUsbKbPrivate::~QWSUsbKbPrivate()
+{
+ ::close(fd);
+}
+
+void QWSUsbKbPrivate::readKeyboardData()
+{
+ input_event event;
+ if (read(fd, &event, sizeof(input_event)) != sizeof(input_event))
+ return;
+
+ if (event.type != EV_KEY)
+ return;
+
+#ifdef QT_QWS_ZYLONITE
+ qDebug("keypressed: code=%03d (%s)\n",event.code,((event.value)!=0) ? "Down":"Up");
+ int modifiers=0;
+ int unicode=0xffff;
+ int key_code=0;
+
+ switch(event.code)
+ {
+ case 0xA2:
+ key_code = ((!shift) ? Qt::Key_0 : Qt::Key_Plus );
+ unicode = ((!shift) ? 0x30 : 0x2B );
+ break;
+ case 0x70:
+ key_code = ((!shift) ? Qt::Key_1 : Qt::Key_At );
+ unicode = ((!shift) ? 0x31 : 0x40 );
+ break;
+ case 0x72:
+ key_code = ((!shift) ? Qt::Key_2 : Qt::Key_Ampersand );
+ unicode = ((!shift) ? 0x32 : 0x26 );
+ break;
+ case 0x74:
+ key_code = ((!shift) ? Qt::Key_3 : Qt::Key_At );
+ unicode = ((!shift) ? 0x33 : 0x3F );
+ break;
+ case 0x80:
+ key_code = ((!shift) ? Qt::Key_4 : Qt::Key_Minus );
+ unicode = ((!shift) ? 0x34 : 0x2D );
+ break;
+ case 0x82:
+ key_code = ((!shift) ? Qt::Key_5 : Qt::Key_Apostrophe);
+ unicode = ((!shift) ? 0x35 : 0x27 );
+ break;
+ case 0x84:
+ key_code = ((!shift) ? Qt::Key_6 : Qt::Key_Slash );
+ unicode = ((!shift) ? 0x36 : 0x5C );
+ break;
+ case 0x90:
+ key_code = ((!shift) ? Qt::Key_7 : Qt::Key_Colon );
+ unicode = ((!shift) ? 0x37 : 0x3A );
+ break;
+ case 0x92:
+ key_code = ((!shift) ? Qt::Key_8 : Qt::Key_Semicolon );
+ unicode = ((!shift) ? 0x38 : 0x3B );
+ break;
+ case 0x94:
+ key_code = ((!shift) ? Qt::Key_9 : Qt::Key_QuoteDbl );
+ unicode = ((!shift) ? 0x39 : 0x22 );
+ break;
+ case 0x0:
+ key_code = Qt::Key_A;
+ unicode = ((!shift) ? 0x61 : 0x41 );
+ break;
+ case 0x10:
+ key_code = Qt::Key_B;
+ unicode = ((!shift) ? 0x62 : 0x42 );
+ break;
+ case 0x20:
+ key_code = Qt::Key_C;
+ unicode = ((!shift) ? 0x63 : 0x43 );
+ break;
+ case 0x30:
+ key_code = Qt::Key_D;
+ unicode = ((!shift) ? 0x64 : 0x44 );
+ break;
+ case 0x40:
+ key_code = Qt::Key_E;
+ unicode = ((!shift) ? 0x65 : 0x45 );
+ break;
+ case 0x50:
+ key_code = Qt::Key_F;
+ unicode = ((!shift) ? 0x66 : 0x46 );
+ break;
+ case 0x01:
+ key_code = Qt::Key_G;
+ unicode = ((!shift) ? 0x67 : 0x47 );
+ break;
+ case 0x11:
+ key_code = Qt::Key_H;
+ unicode = ((!shift) ? 0x68 : 0x48 );
+ break;
+ case 0x21:
+ key_code = Qt::Key_I;
+ unicode = ((!shift) ? 0x69 : 0x49 );
+ break;
+ case 0x31:
+ key_code = Qt::Key_J;
+ unicode = ((!shift) ? 0x6A : 0x4A );
+ break;
+ case 0x41:
+ key_code = Qt::Key_K;
+ unicode = ((!shift) ? 0x6B : 0x4B );
+ break;
+ case 0x51:
+ key_code = Qt::Key_L;
+ unicode = ((!shift) ? 0x6C : 0x4C );
+ break;
+ case 0x02:
+ key_code = Qt::Key_M;
+ unicode = ((!shift) ? 0x6D : 0x4D );
+ break;
+ case 0x12:
+ key_code = Qt::Key_N;
+ unicode = ((!shift) ? 0x6E : 0x4E );
+ break;
+ case 0x22:
+ key_code = Qt::Key_O;
+ unicode = ((!shift) ? 0x6F : 0x4F );
+ break;
+ case 0x32:
+ key_code = Qt::Key_P;
+ unicode = ((!shift) ? 0x70 : 0x50 );
+ break;
+ case 0x42:
+ key_code = Qt::Key_Q;
+ unicode = ((!shift) ? 0x71 : 0x51 );
+ break;
+ case 0x52:
+ key_code = Qt::Key_R;
+ unicode = ((!shift) ? 0x72 : 0x52 );
+ break;
+ case 0x03:
+ key_code = Qt::Key_S;
+ unicode = ((!shift) ? 0x73 : 0x53 );
+ break;
+ case 0x13:
+ key_code = Qt::Key_T;
+ unicode = ((!shift) ? 0x74 : 0x54 );
+ break;
+ case 0x23:
+ key_code = Qt::Key_U;
+ unicode = ((!shift) ? 0x75 : 0x55 );
+ break;
+ case 0x33:
+ key_code = Qt::Key_V;
+ unicode = ((!shift) ? 0x76 : 0x56 );
+ break;
+ case 0x43:
+ key_code = Qt::Key_W;
+ unicode = ((!shift) ? 0x77 : 0x57 );
+ break;
+ case 0x53:
+ key_code = Qt::Key_X;
+ unicode = ((!shift) ? 0x78 : 0x58 );
+ break;
+ case 0x24:
+ key_code = Qt::Key_Y;
+ unicode = ((!shift) ? 0x79 : 0x59 );
+ break;
+ case 0x34:
+ key_code = Qt::Key_Z;
+ unicode = ((!shift) ? 0x7A : 0x5A );
+ break;
+ case 0xA4:
+ key_code = ((!shift) ? Qt::Key_NumberSign : Qt::Key_Period);
+ unicode = ((!shift) ? 0x23 : 0x2E );
+ break;
+ case 0xA0:
+ key_code = ((!shift) ? Qt::Key_Asterisk : Qt::Key_NumberSign );
+ unicode = ((!shift) ? 0x2A : 0x2C );
+ break;
+ case 0x25:
+ key_code = Qt::Key_Space;
+ unicode = 0x20;
+ break;
+ case 0x06:
+ key_code = Qt::Key_Up;
+ unicode = 0xffff; modifiers |= Qt::KeypadModifier;
+ break;
+ case 0x16:
+ key_code = Qt::Key_Down;
+ unicode = 0xffff; modifiers |= Qt::KeypadModifier;
+ break;
+ case 0x26:
+ key_code = Qt::Key_Left;
+ unicode = 0xffff; modifiers |= Qt::KeypadModifier;
+ break;
+ case 0x36:
+ key_code = Qt::Key_Right;
+ unicode = 0xffff; modifiers |= Qt::KeypadModifier;
+ break;
+ case 0x46:
+ key_code = Qt::Key_Select;
+ unicode = 0xffff; modifiers |= Qt::KeypadModifier;
+ break;
+ case 0x61:
+ key_code = Qt::Key_No;
+ unicode = 0xffff; modifiers |= Qt::KeypadModifier;
+ break;
+ case 0x60:
+ key_code = Qt::Key_Call;
+ unicode = 0xffff; modifiers |= Qt::KeypadModifier;
+ break;
+ case 0x55:
+ key_code = Qt::Key_Hangup;
+ unicode = 0xffff; modifiers |= Qt::KeypadModifier;
+ break;
+ case 0x62:
+ key_code = Qt::Key_Context1;
+ unicode = 0xffff; modifiers |= Qt::KeypadModifier;
+ break;
+ case 0x63:
+ key_code = Qt::Key_No;
+ unicode = 0xffff; modifiers |= Qt::KeypadModifier;
+ break;
+ case 0x05:
+ key_code = Qt::Key_Home;
+ unicode = 0xffff; modifiers |= Qt::KeypadModifier;
+ break;
+ case 0x15:
+ key_code = Qt::Key_Shift;
+ unicode = 0xffff; modifiers |= Qt::ShiftModifier;
+ if(event.value==0) break;
+ if(shift) {
+ shift = FALSE;
+ qWarning("Caps Off!");
+ } else {
+ shift = TRUE;
+ qWarning("Caps On!");
+ }
+ break;
+ case 0x1C:
+ key_code = ((!shift) ? Qt::Key_Back : Qt::Key_Enter );
+ unicode = 0xffff; modifiers |= Qt::KeypadModifier;
+ break;
+ case 0x19:
+ key_code = Qt::Key_Context2;
+ unicode = 0xffff; modifiers |= Qt::KeypadModifier;
+ break;
+ case 0x1A:
+ key_code = Qt::Key_Context3;
+ unicode = 0xffff; modifiers |= Qt::KeypadModifier;
+ break;
+ case 0x1B:
+ key_code = Qt::Key_Context4;
+ unicode = 0xffff; modifiers |= Qt::KeypadModifier;
+ break;
+ }
+ if(shift) modifiers |= Qt::ShiftModifier;
+ handler->processKeyEvent(unicode, key_code, (Qt::KeyboardModifiers)modifiers, event.value!=0, false);
+#else
+
+ int key=event.code;
+#ifndef QT_QWS_USE_KEYCODES
+ // Handle SOME keys, otherwise it's useless.
+
+ if(key==103) {
+ handler->processKeyEvent(0, Qt::Key_Up, 0, event.value!=0, false);
+ } else if(key==106) {
+ handler->processKeyEvent(0, Qt::Key_Right, 0, event.value!=0, false );
+ } else if(key==108) {
+ handler->processKeyEvent(0, Qt::Key_Down, 0, event.value!=0, false);
+ } else if(key==105) {
+ handler->processKeyEvent(0, Qt::Key_Left, 0, event.value!=0, false);
+ } else
+
+#endif
+
+ {
+ if(event.value == 0) {
+ key=key | 0x80;
+ }
+ handler->doKey(key);
+ }
+#endif
+}
+
+QT_END_NAMESPACE
+
+#include "qkbdusb_qws.moc"
+
+#endif // QT_NO_QWS_KEYBOARD
diff --git a/src/gui/embedded/qkbdusb_qws.h b/src/gui/embedded/qkbdusb_qws.h
new file mode 100644
index 0000000000..81d0103b47
--- /dev/null
+++ b/src/gui/embedded/qkbdusb_qws.h
@@ -0,0 +1,77 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QKBDUSB_QWS_H
+#define QKBDUSB_QWS_H
+
+#include <QtGui/qkbdpc101_qws.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+#ifndef QT_NO_QWS_KEYBOARD
+
+#ifndef QT_NO_QWS_KBD_USB
+
+class QWSUsbKbPrivate;
+
+class QWSUsbKeyboardHandler : public QWSPC101KeyboardHandler
+{
+public:
+ QWSUsbKeyboardHandler(const QString&);
+ virtual ~QWSUsbKeyboardHandler();
+
+private:
+ QWSUsbKbPrivate *d;
+};
+
+#endif // QT_NO_QWS_KBD_USB
+
+#endif // QT_NO_QWS_KEYBOARD
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QKBDUSB_QWS_H
diff --git a/src/gui/embedded/qkbdvfb_qws.cpp b/src/gui/embedded/qkbdvfb_qws.cpp
new file mode 100644
index 0000000000..1d53ce9150
--- /dev/null
+++ b/src/gui/embedded/qkbdvfb_qws.cpp
@@ -0,0 +1,123 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#include <qvfbhdr.h>
+#include <qkbdvfb_qws.h>
+
+#ifndef QT_NO_QWS_KEYBOARD
+#ifndef QT_NO_QWS_KBD_QVFB
+
+#include <qwindowsystem_qws.h>
+#include <qsocketnotifier.h>
+#include <qapplication.h>
+
+QT_BEGIN_NAMESPACE
+
+QVFbKeyboardHandler::QVFbKeyboardHandler(const QString &device)
+ : QObject()
+{
+ terminalName = device;
+ if (terminalName.isEmpty())
+ terminalName = QLatin1String("/dev/vkdb");
+ kbdFD = -1;
+ kbdIdx = 0;
+ kbdBufferLen = sizeof(QVFbKeyData) * 5;
+ kbdBuffer = new unsigned char [kbdBufferLen];
+
+ if ((kbdFD = open(terminalName.toLatin1().constData(), O_RDONLY | O_NDELAY)) < 0) {
+ qWarning("Cannot open %s (%s)", terminalName.toLatin1().constData(),
+ strerror(errno));
+ } else {
+ // Clear pending input
+ char buf[2];
+ while (read(kbdFD, buf, 1) > 0) { }
+
+ notifier = new QSocketNotifier(kbdFD, QSocketNotifier::Read, this);
+ connect(notifier, SIGNAL(activated(int)),this, SLOT(readKeyboardData()));
+ }
+}
+
+QVFbKeyboardHandler::~QVFbKeyboardHandler()
+{
+ if (kbdFD >= 0)
+ close(kbdFD);
+ delete [] kbdBuffer;
+}
+
+
+void QVFbKeyboardHandler::readKeyboardData()
+{
+ int n;
+ do {
+ n = read(kbdFD, kbdBuffer+kbdIdx, kbdBufferLen - kbdIdx);
+ if (n > 0)
+ kbdIdx += n;
+ } while (n > 0);
+
+ int idx = 0;
+ while (kbdIdx - idx >= (int)sizeof(QVFbKeyData)) {
+ QVFbKeyData *kd = (QVFbKeyData *)(kbdBuffer + idx);
+ if (kd->unicode == 0 && kd->keycode == 0 && kd->modifiers == 0 && kd->press) {
+ // magic exit key
+ qWarning("Instructed to quit by Virtual Keyboard");
+ qApp->quit();
+ }
+ QWSServer::processKeyEvent(kd->unicode ? kd->unicode : 0xffff, kd->keycode, kd->modifiers, kd->press, kd->repeat);
+ idx += sizeof(QVFbKeyData);
+ }
+
+ int surplus = kbdIdx - idx;
+ for (int i = 0; i < surplus; i++)
+ kbdBuffer[i] = kbdBuffer[idx+i];
+ kbdIdx = surplus;
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_QWS_KBD_QVFB
+#endif // QT_NO_QWS_KEYBOARD
diff --git a/src/gui/embedded/qkbdvfb_qws.h b/src/gui/embedded/qkbdvfb_qws.h
new file mode 100644
index 0000000000..47fa5d37cb
--- /dev/null
+++ b/src/gui/embedded/qkbdvfb_qws.h
@@ -0,0 +1,86 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QKBDVFB_QWS_H
+#define QKBDVFB_QWS_H
+
+#include <QtGui/qkbd_qws.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+#ifndef QT_NO_QWS_KEYBOARD
+
+#ifndef QT_NO_QWS_KBD_QVFB
+
+class QSocketNotifier;
+
+class QVFbKeyboardHandler : public QObject, public QWSKeyboardHandler
+{
+ Q_OBJECT
+public:
+ QVFbKeyboardHandler(const QString &device);
+ virtual ~QVFbKeyboardHandler();
+
+private Q_SLOTS:
+ void readKeyboardData();
+
+private:
+ QString terminalName;
+ int kbdFD;
+ int kbdIdx;
+ int kbdBufferLen;
+ unsigned char *kbdBuffer;
+ QSocketNotifier *notifier;
+};
+
+#endif // QT_NO_QWS_KBD_QVFB
+
+#endif // QT_NO_QWS_KEYBOARD
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QKBDVFB_QWS_H
diff --git a/src/gui/embedded/qkbdvr41xx_qws.cpp b/src/gui/embedded/qkbdvr41xx_qws.cpp
new file mode 100644
index 0000000000..4613891416
--- /dev/null
+++ b/src/gui/embedded/qkbdvr41xx_qws.cpp
@@ -0,0 +1,185 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qkbdvr41xx_qws.h"
+
+#if !defined(QT_NO_QWS_KEYBOARD) && !defined(QT_NO_QWS_KBD_VR41XX)
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <fcntl.h>
+#include <termios.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include <qsocketnotifier.h>
+
+QT_BEGIN_NAMESPACE
+
+class QWSVr41xxKbPrivate : public QObject
+{
+ Q_OBJECT
+public:
+ QWSVr41xxKbPrivate(QWSVr41xxKeyboardHandler *h, const QString&);
+ virtual ~QWSVr41xxKbPrivate();
+
+ bool isOpen() { return buttonFD > 0; }
+
+private slots:
+ void readKeyboardData();
+
+private:
+ QString terminalName;
+ int buttonFD;
+ int kbdIdx;
+ int kbdBufferLen;
+ unsigned char *kbdBuffer;
+ QSocketNotifier *notifier;
+ QWSVr41xxKeyboardHandler *handler;
+};
+
+QWSVr41xxKeyboardHandler::QWSVr41xxKeyboardHandler(const QString &device)
+{
+ d = new QWSVr41xxKbPrivate(this, device);
+}
+
+QWSVr41xxKeyboardHandler::~QWSVr41xxKeyboardHandler()
+{
+ delete d;
+}
+
+QWSVr41xxKbPrivate::QWSVr41xxKbPrivate(QWSVr41xxKeyboardHandler *h, const QString &device) : handler(h)
+{
+ terminalName = device;
+ if (terminalName.isEmpty())
+ terminalName = QLatin1String("/dev/buttons");
+ buttonFD = -1;
+ notifier = 0;
+
+ buttonFD = open(terminalName.toLatin1().constData(), O_RDWR | O_NDELAY, 0);;
+ if (buttonFD < 0) {
+ qWarning("Cannot open %s\n", qPrintable(terminalName));
+ return;
+ }
+
+ if (buttonFD >= 0) {
+ notifier = new QSocketNotifier(buttonFD, QSocketNotifier::Read, this);
+ connect(notifier, SIGNAL(activated(int)),this,
+ SLOT(readKeyboardData()));
+ }
+
+ kbdBufferLen = 80;
+ kbdBuffer = new unsigned char [kbdBufferLen];
+ kbdIdx = 0;
+}
+
+QWSVr41xxKbPrivate::~QWSVr41xxKbPrivate()
+{
+ if (buttonFD > 0) {
+ ::close(buttonFD);
+ buttonFD = -1;
+ }
+ delete notifier;
+ notifier = 0;
+ delete [] kbdBuffer;
+}
+
+void QWSVr41xxKbPrivate::readKeyboardData()
+{
+ int n = 0;
+ do {
+ n = read(buttonFD, kbdBuffer+kbdIdx, kbdBufferLen - kbdIdx);
+ if (n > 0)
+ kbdIdx += n;
+ } while (n > 0);
+
+ int idx = 0;
+ while (kbdIdx - idx >= 2) {
+ unsigned char *next = kbdBuffer + idx;
+ unsigned short *code = (unsigned short *)next;
+ int keycode = Qt::Key_unknown;
+ switch ((*code) & 0x0fff) {
+ case 0x7:
+ keycode = Qt::Key_Up;
+ break;
+ case 0x9:
+ keycode = Qt::Key_Right;
+ break;
+ case 0x8:
+ keycode = Qt::Key_Down;
+ break;
+ case 0xa:
+ keycode = Qt::Key_Left;
+ break;
+ case 0x3:
+ keycode = Qt::Key_Up;
+ break;
+ case 0x4:
+ keycode = Qt::Key_Down;
+ break;
+ case 0x1:
+ keycode = Qt::Key_Return;
+ break;
+ case 0x2:
+ keycode = Qt::Key_F4;
+ break;
+ default:
+ qDebug("Unrecognised key sequence %d", *code);
+ }
+ if ((*code) & 0x8000)
+ handler->processKeyEvent(0, keycode, 0, false, false);
+ else
+ handler->processKeyEvent(0, keycode, 0, true, false);
+ idx += 2;
+ }
+
+ int surplus = kbdIdx - idx;
+ for (int i = 0; i < surplus; i++)
+ kbdBuffer[i] = kbdBuffer[idx+i];
+ kbdIdx = surplus;
+}
+
+QT_END_NAMESPACE
+
+#include "qkbdvr41xx_qws.moc"
+
+#endif // QT_NO_QWS_KBD_VR41XX
diff --git a/src/gui/embedded/qkbdvr41xx_qws.h b/src/gui/embedded/qkbdvr41xx_qws.h
new file mode 100644
index 0000000000..51719cf64e
--- /dev/null
+++ b/src/gui/embedded/qkbdvr41xx_qws.h
@@ -0,0 +1,73 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QKBDVR41XX_QWS_H
+#define QKBDVR41XX_QWS_H
+
+#include <QtGui/qkbd_qws.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+#if !defined(QT_NO_QWS_KEYBOARD) && !defined(QT_NO_QWS_KBD_VR41XX)
+
+class QWSVr41xxKbPrivate;
+
+class QWSVr41xxKeyboardHandler : public QWSKeyboardHandler
+{
+public:
+ explicit QWSVr41xxKeyboardHandler(const QString&);
+ virtual ~QWSVr41xxKeyboardHandler();
+
+private:
+ QWSVr41xxKbPrivate *d;
+};
+
+#endif // QT_NO_QWS_KBD_VR41XX
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QKBDVR41XX_QWS_H
diff --git a/src/gui/embedded/qkbdyopy_qws.cpp b/src/gui/embedded/qkbdyopy_qws.cpp
new file mode 100644
index 0000000000..bfa8c647ac
--- /dev/null
+++ b/src/gui/embedded/qkbdyopy_qws.cpp
@@ -0,0 +1,209 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*
+ * YOPY buttons driver
+ * Contributed by Ron Victorelli (victorrj at icubed.com)
+ */
+
+#include "qkbdyopy_qws.h"
+
+#ifndef QT_NO_QWS_KBD_YOPY
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <fcntl.h>
+#include <termios.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include <linux/kd.h>
+#include <linux/fb.h>
+#include <linux/yopy_button.h>
+
+extern "C" {
+ int getpgid(int);
+}
+
+#include <qwidget.h>
+#include <qsocketnotifier.h>
+
+QT_BEGIN_NAMESPACE
+
+class QWSYopyKbPrivate : public QObject
+{
+ Q_OBJECT
+public:
+ QWSYopyKbPrivate(QWSYopyKeyboardHandler *h, const QString&);
+ virtual ~QWSYopyKbPrivate();
+
+ bool isOpen() { return buttonFD > 0; }
+
+private slots:
+ void readKeyboardData();
+
+private:
+ QString terminalName;
+ int buttonFD;
+ struct termios newT, oldT;
+ QSocketNotifier *notifier;
+ QWSYopyKeyboardHandler *handler;
+};
+
+QWSYopyKeyboardHandler::QWSYopyKeyboardHandler(const QString &device)
+{
+ d = new QWSYopyKbPrivate(this, device);
+}
+
+QWSYopyKeyboardHandler::~QWSYopyKeyboardHandler()
+{
+ delete d;
+}
+
+QWSYopyKbPrivate::QWSYopyKbPrivate(QWSYopyKeyboardHandler *h, const QString &device) : handler(h)
+{
+ terminalName = device.isEmpty()?"/dev/tty1":device.toLatin1().constData();
+ buttonFD = -1;
+ notifier = 0;
+
+ buttonFD = ::open(terminalName.toLatin1().constData(), O_RDWR | O_NDELAY, 0);
+ if (buttonFD < 0) {
+ qWarning("Cannot open %s\n", qPrintable(terminalName));
+ return;
+ } else {
+
+ tcsetpgrp(buttonFD, getpgid(0));
+
+ /* put tty into "straight through" mode.
+ */
+ if (tcgetattr(buttonFD, &oldT) < 0) {
+ qFatal("Linux-kbd: tcgetattr failed");
+ }
+
+ newT = oldT;
+ newT.c_lflag &= ~(ICANON | ECHO | ISIG);
+ newT.c_iflag &= ~(ISTRIP | IGNCR | ICRNL | INLCR | IXOFF | IXON);
+ newT.c_iflag |= IGNBRK;
+ newT.c_cc[VMIN] = 0;
+ newT.c_cc[VTIME] = 0;
+
+
+ if (tcsetattr(buttonFD, TCSANOW, &newT) < 0) {
+ qFatal("Linux-kbd: TCSANOW tcsetattr failed");
+ }
+
+ if (ioctl(buttonFD, KDSKBMODE, K_MEDIUMRAW) < 0) {
+ qFatal("Linux-kbd: KDSKBMODE tcsetattr failed");
+ }
+
+ notifier = new QSocketNotifier(buttonFD, QSocketNotifier::Read, this);
+ connect(notifier, SIGNAL(activated(int)),this,
+ SLOT(readKeyboardData()));
+ }
+}
+
+QWSYopyKbPrivate::~QWSYopyKbPrivate()
+{
+ if (buttonFD > 0) {
+ ::close(buttonFD);
+ buttonFD = -1;
+ }
+}
+
+void QWSYopyKbPrivate::readKeyboardData()
+{
+ uchar buf[1];
+ char c='1';
+ int fd;
+
+ int n=read(buttonFD,buf,1);
+ if (n<0) {
+ qDebug("Keyboard read error %s",strerror(errno));
+ } else {
+ uint code = buf[0]&YPBUTTON_CODE_MASK;
+ bool press = !(buf[0]&0x80);
+ // printf("Key=%d/%d/%d\n",buf[1],code,press);
+ int k=(-1);
+ switch(code) {
+ case 39: k=Qt::Key_Up; break;
+ case 44: k=Qt::Key_Down; break;
+ case 41: k=Qt::Key_Left; break;
+ case 42: k=Qt::Key_Right; break;
+ case 56: k=Qt::Key_F1; break; //windows
+ case 29: k=Qt::Key_F2; break; //cycle
+ case 24: k=Qt::Key_F3; break; //record
+ case 23: k=Qt::Key_F4; break; //mp3
+ case 4: k=Qt::Key_F5; break; // PIMS
+ case 1: k=Qt::Key_Escape; break; // Escape
+ case 40: k=Qt::Key_Up; break; // prev
+ case 45: k=Qt::Key_Down; break; // next
+ case 35: if(!press) {
+ fd = open("/proc/sys/pm/sleep",O_RDWR,0);
+ if(fd >= 0) {
+ write(fd,&c,sizeof(c));
+ close(fd);
+ //
+ // Updates all widgets.
+ //
+ QWidgetList list = QApplication::allWidgets();
+ for (int i = 0; i < list.size(); ++i) {
+ QWidget *w = list.at(i);
+ w->update();
+ }
+ }
+ }
+ break;
+
+ default: k=(-1); break;
+ }
+
+ if (k >= 0) {
+ handler->processKeyEvent(0, k, 0, press, false);
+ }
+ }
+}
+
+QT_END_NAMESPACE
+
+#include "qkbdyopy_qws.moc"
+
+#endif // QT_NO_QWS_KBD_YOPY
diff --git a/src/gui/embedded/qkbdyopy_qws.h b/src/gui/embedded/qkbdyopy_qws.h
new file mode 100644
index 0000000000..81c1c4d505
--- /dev/null
+++ b/src/gui/embedded/qkbdyopy_qws.h
@@ -0,0 +1,73 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QKBDYOPY_QWS_H
+#define QKBDYOPY_QWS_H
+
+#include <QtGui/qkbd_qws.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+#ifndef QT_NO_QWS_KBD_YOPY
+
+class QWSYopyKbPrivate;
+
+class QWSYopyKeyboardHandler : public QWSKeyboardHandler
+{
+public:
+ explicit QWSYopyKeyboardHandler(const QString&);
+ virtual ~QWSYopyKeyboardHandler();
+
+private:
+ QWSYopyKbPrivate *d;
+};
+
+#endif // QT_NO_QWS_KBD_YOPY
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QKBDYOPY_QWS_H
diff --git a/src/gui/embedded/qlock.cpp b/src/gui/embedded/qlock.cpp
new file mode 100644
index 0000000000..874ca7d205
--- /dev/null
+++ b/src/gui/embedded/qlock.cpp
@@ -0,0 +1,318 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qlock_p.h"
+
+#ifndef QT_NO_QWS_MULTIPROCESS
+
+#include "qwssignalhandler_p.h"
+#include <unistd.h>
+#include <sys/types.h>
+#if defined(Q_OS_DARWIN)
+# define Q_NO_SEMAPHORE
+# include <sys/stat.h>
+# include <sys/file.h>
+#else // Q_OS_DARWIN
+# include <sys/sem.h>
+# if (defined(__GNU_LIBRARY__) && !defined(_SEM_SEMUN_UNDEFINED) && !defined(QT_LINUXBASE)) \
+ || defined(Q_OS_FREEBSD) || defined(Q_OS_OPENBSD) || defined(Q_OS_NETBSD) \
+ || defined(Q_OS_BSDI)
+ /* union semun is defined by including <sys/sem.h> */
+# else
+/* according to X/OPEN we have to define it ourselves */
+union semun {
+ int val; /* value for SETVAL */
+ struct semid_ds *buf; /* buffer for IPC_STAT, IPC_SET */
+ unsigned short *array; /* array for GETALL, SETALL */
+};
+# endif
+#endif // Q_OS_DARWIN
+#include <sys/ipc.h>
+#include <string.h>
+#include <errno.h>
+#include <qdebug.h>
+#include <signal.h>
+
+#endif // QT_NO_QWS_MULTIPROCESS
+
+#define MAX_LOCKS 200 // maximum simultaneous read locks
+
+QT_BEGIN_NAMESPACE
+
+
+#ifndef QT_NO_QWS_MULTIPROCESS
+class QLockData
+{
+public:
+#ifdef Q_NO_SEMAPHORE
+ QByteArray file;
+#endif // Q_NO_SEMAPHORE
+ int id;
+ int count;
+ bool owned;
+};
+#endif // QT_NO_QWS_MULTIPROCESS
+
+/*!
+ \class QLock
+ \brief The QLock class is a wrapper for a System V shared semaphore.
+
+ \ingroup qws
+ \ingroup io
+
+ \internal
+
+ It is used by \l{Qt for Embedded Linux} for synchronizing access to the graphics
+ card and shared memory region between processes.
+*/
+
+/*!
+ \enum QLock::Type
+
+ \value Read
+ \value Write
+*/
+
+/*!
+ \fn QLock::QLock(const QString &filename, char id, bool create)
+
+ Creates a lock. \a filename is the file path of the Unix-domain
+ socket the \l{Qt for Embedded Linux} client is using. \a id is the name of the
+ particular lock to be created on that socket. If \a create is true
+ the lock is to be created (as the Qt for Embedded Linux server does); if \a
+ create is false the lock should exist already (as the Qt for Embedded Linux
+ client expects).
+*/
+
+QLock::QLock(const QString &filename, char id, bool create)
+{
+#ifdef QT_NO_QWS_MULTIPROCESS
+ Q_UNUSED(filename);
+ Q_UNUSED(id);
+ Q_UNUSED(create);
+#else
+ data = new QLockData;
+ data->count = 0;
+#ifdef Q_NO_SEMAPHORE
+ data->file = QString(filename+id).toLocal8Bit().constData();
+ for(int x = 0; x < 2; x++) {
+ data->id = open(data->file, O_RDWR | (x ? O_CREAT : 0), S_IRWXU);
+ if(data->id != -1 || !create) {
+ data->owned = x;
+ break;
+ }
+ }
+#else
+ key_t semkey = ftok(filename.toLocal8Bit().constData(), id);
+ data->id = semget(semkey,0,0);
+ data->owned = create;
+ if (create) {
+ semun arg; arg.val = 0;
+ if (data->id != -1)
+ semctl(data->id,0,IPC_RMID,arg);
+ data->id = semget(semkey,1,IPC_CREAT|0600);
+ arg.val = MAX_LOCKS;
+ semctl(data->id,0,SETVAL,arg);
+
+ QWSSignalHandler::instance()->addSemaphore(data->id);
+ }
+#endif
+ if (data->id == -1) {
+ int eno = errno;
+ qWarning("Cannot %s semaphore %s '%c'", (create ? "create" : "get"),
+ qPrintable(filename), id);
+ qDebug() << "Error" << eno << strerror(eno);
+ }
+#endif
+}
+
+/*!
+ \fn QLock::~QLock()
+
+ Destroys a lock
+*/
+
+QLock::~QLock()
+{
+#ifndef QT_NO_QWS_MULTIPROCESS
+ if (locked())
+ unlock();
+#ifdef Q_NO_SEMAPHORE
+ if(isValid()) {
+ close(data->id);
+ if(data->owned)
+ unlink(data->file);
+ }
+#else
+ if(data->owned)
+ QWSSignalHandler::instance()->removeSemaphore(data->id);
+#endif
+ delete data;
+#endif
+}
+
+/*!
+ \fn bool QLock::isValid() const
+
+ Returns true if the lock constructor was successful; returns false if
+ the lock could not be created or was not available to connect to.
+*/
+
+bool QLock::isValid() const
+{
+#ifndef QT_NO_QWS_MULTIPROCESS
+ return (data->id != -1);
+#else
+ return true;
+#endif
+}
+
+/*!
+ Locks the semaphore with a lock of type \a t. Locks can either be
+ \c Read or \c Write. If a lock is \c Read, attempts by other
+ processes to obtain \c Read locks will succeed, and \c Write
+ attempts will block until the lock is unlocked. If locked as \c
+ Write, all attempts to lock by other processes will block until
+ the lock is unlocked. Locks are stacked: i.e. a given QLock can be
+ locked multiple times by the same process without blocking, and
+ will only be unlocked after a corresponding number of unlock()
+ calls.
+*/
+
+void QLock::lock(Type t)
+{
+#ifdef QT_NO_QWS_MULTIPROCESS
+ Q_UNUSED(t);
+#else
+ if (!data->count) {
+#ifdef Q_NO_SEMAPHORE
+ int op = LOCK_SH;
+ if(t == Write)
+ op = LOCK_EX;
+ for(int rv=1; rv;) {
+ rv = flock(data->id, op);
+ if (rv == -1 && errno != EINTR)
+ qDebug("Semop lock failure %s",strerror(errno));
+ }
+#else
+ sembuf sops;
+ sops.sem_num = 0;
+ sops.sem_flg = SEM_UNDO;
+
+ if (t == Write) {
+ sops.sem_op = -MAX_LOCKS;
+ type = Write;
+ } else {
+ sops.sem_op = -1;
+ type = Read;
+ }
+
+ int rv;
+ do {
+ rv = semop(data->id,&sops,1);
+ if (rv == -1 && errno != EINTR)
+ qDebug("Semop lock failure %s",strerror(errno));
+ } while (rv == -1 && errno == EINTR);
+#endif
+ }
+ data->count++;
+#endif
+}
+
+/*!
+ \fn void QLock::unlock()
+
+ Unlocks the semaphore. If other processes were blocking waiting to
+ lock() the semaphore, one of them will wake up and succeed in
+ lock()ing.
+*/
+
+void QLock::unlock()
+{
+#ifndef QT_NO_QWS_MULTIPROCESS
+ if(data->count) {
+ data->count--;
+ if(!data->count) {
+#ifdef Q_NO_SEMAPHORE
+ for(int rv=1; rv;) {
+ rv = flock(data->id, LOCK_UN);
+ if (rv == -1 && errno != EINTR)
+ qDebug("Semop lock failure %s",strerror(errno));
+ }
+#else
+ sembuf sops;
+ sops.sem_num = 0;
+ sops.sem_op = 1;
+ sops.sem_flg = SEM_UNDO;
+ if (type == Write)
+ sops.sem_op = MAX_LOCKS;
+
+ int rv;
+ do {
+ rv = semop(data->id,&sops,1);
+ if (rv == -1 && errno != EINTR)
+ qDebug("Semop unlock failure %s",strerror(errno));
+ } while (rv == -1 && errno == EINTR);
+#endif
+ }
+ } else {
+ qDebug("Unlock without corresponding lock");
+ }
+#endif
+}
+
+/*!
+ \fn bool QLock::locked() const
+
+ Returns true if the lock is currently held by the current process;
+ otherwise returns false.
+*/
+
+bool QLock::locked() const
+{
+#ifndef QT_NO_QWS_MULTIPROCESS
+ return (data->count > 0);
+#else
+ return false;
+#endif
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/embedded/qlock_p.h b/src/gui/embedded/qlock_p.h
new file mode 100644
index 0000000000..92e0704e56
--- /dev/null
+++ b/src/gui/embedded/qlock_p.h
@@ -0,0 +1,100 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QLOCK_P_H
+#define QLOCK_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. This header file may
+// change from version to version without notice, or even be
+// removed.
+//
+// We mean it.
+//
+
+#include "QtCore/qstring.h"
+
+QT_BEGIN_NAMESPACE
+
+class QLockData;
+
+class Q_GUI_EXPORT QLock
+{
+public:
+ QLock(const QString &filename, char id, bool create = false);
+ ~QLock();
+
+ enum Type { Read, Write };
+
+ bool isValid() const;
+ void lock(Type type);
+ void unlock();
+ bool locked() const;
+
+private:
+ Type type;
+ QLockData *data;
+};
+
+
+// Nice class for ensuring the lock is released.
+// Just create one on the stack and the lock is automatically released
+// when QLockHandle is destructed.
+class Q_GUI_EXPORT QLockHandle
+{
+public:
+ QLockHandle(QLock *l, QLock::Type type) : qlock(l) { qlock->lock(type); }
+ ~QLockHandle() { if (locked()) qlock->unlock(); }
+
+ void lock(QLock::Type type) { qlock->lock(type); }
+ void unlock() { qlock->unlock(); }
+ bool locked() const { return qlock->locked(); }
+
+private:
+ QLock *qlock;
+};
+
+QT_END_NAMESPACE
+
+#endif // QLOCK_P_H
diff --git a/src/gui/embedded/qmouse_qws.cpp b/src/gui/embedded/qmouse_qws.cpp
new file mode 100644
index 0000000000..044a574444
--- /dev/null
+++ b/src/gui/embedded/qmouse_qws.cpp
@@ -0,0 +1,653 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qmouse_qws.h"
+#include "qwindowsystem_qws.h"
+#include "qscreen_qws.h"
+#include "qapplication.h"
+#include "qtextstream.h"
+#include "qfile.h"
+#include "qdebug.h"
+#include "qscreen_qws.h"
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QWSPointerCalibrationData
+ \ingroup qws
+
+ \brief The QWSPointerCalibrationData class is a container for
+ mouse calibration data in Qt for Embedded Linux.
+
+ Note that this class is only available in \l{Qt for Embedded Linux}.
+
+ QWSPointerCalibrationData stores device and screen coordinates in
+ the devPoints and screenPoints variables, respectively.
+
+ A calibration program should create a QWSPointerCalibrationData
+ object, fill the devPoints and screenPoints variables with its
+ device and screen coordinates, and pass the object to the mouse
+ driver using the QWSMouseHandler::calibrate() function.
+
+ \sa QWSCalibratedMouseHandler, {Mouse Calibration Example}
+*/
+
+/*!
+ \variable QWSPointerCalibrationData::devPoints
+ \brief the raw device coordinates for each value of the Location enum.
+*/
+
+/*!
+ \variable QWSPointerCalibrationData::screenPoints
+ \brief the logical screen coordinates for each value of the Location enum.
+*/
+
+/*!
+ \enum QWSPointerCalibrationData::Location
+
+ This enum describes the various logical positions that can be
+ specified by the devPoints and screenPoints variables.
+
+ \value TopLeft Index of the top left corner of the screen.
+ \value BottomLeft Index of the bottom left corner of the screen.
+ \value BottomRight Index of the bottom right corner of the screen.
+ \value TopRight Index of the top right corner of the screen.
+ \value Center Index of the center of the screen.
+ \value LastLocation Last index in the pointer arrays.
+*/
+
+class QWSMouseHandlerPrivate
+{
+public:
+ QWSMouseHandlerPrivate() : screen(qt_screen) {}
+
+ const QScreen *screen;
+};
+
+/*!
+ \class QWSMouseHandler
+ \ingroup qws
+
+ \brief The QWSMouseHandler class is a base class for mouse drivers in
+ Qt for Embedded Linux.
+
+ Note that this class is only available in \l{Qt for Embedded Linux}.
+
+ \l{Qt for Embedded Linux} provides ready-made drivers for several mouse
+ protocols, see the \l{Qt for Embedded Linux Pointer Handling}{pointer
+ handling} documentation for details. Custom mouse drivers can be
+ implemented by subclassing the QWSMouseHandler class and creating
+ a mouse driver plugin (derived from QMouseDriverPlugin).
+ The default implementation of the QMouseDriverFactory class
+ will automatically detect the plugin, and load the driver into the
+ server application at run-time using Qt's \l {How to Create Qt
+ Plugins}{plugin system}.
+
+ The mouse driver receives mouse events from the system device and
+ encapsulates each event with an instance of the QWSEvent class
+ which it then passes to the server application (the server is
+ responsible for propagating the event to the appropriate
+ client). To receive mouse events, a QWSMouseHandler object will
+ usually create a QSocketNotifier object for the given device. The
+ QSocketNotifier class provides support for monitoring activity on
+ a file descriptor. When the socket notifier receives data, it will
+ call the mouse driver's mouseChanged() function to send the event
+ to the \l{Qt for Embedded Linux} server application for relaying to
+ clients.
+
+ If you are creating a driver for a device that needs calibration
+ or noise reduction, such as a touchscreen, use the
+ QWSCalibratedMouseHandler subclass instead to take advantage of
+ the calibrate() and clearCalibration() functions. The \l
+ {qws/mousecalibration}{Mouse Calibration}
+ demonstrates how to write a simple program using the mechanisms
+ provided by the QWSMouseHandler class to calibrate a mouse driver.
+
+ Note that when deriving from the QWSMouseHandler class, the
+ resume() and suspend() functions must be reimplemented to control
+ the flow of mouse input, i.e., the default implementation does
+ nothing. Reimplementations of these functions typically call the
+ QSocketNotifier::setEnabled() function to enable or disable the
+ socket notifier, respectively.
+
+ In addition, QWSMouseHandler provides the setScreen() function
+ that allows you to specify a screen for your mouse driver and the
+ limitToScreen() function that ensures that a given position is
+ within this screen's boundaries (changing the position if
+ necessary). Finally, QWSMouseHandler provides the pos() function
+ returning the current mouse position.
+
+ \sa QMouseDriverPlugin, QMouseDriverFactory, {Qt for Embedded Linux Pointer
+ Handling}
+*/
+
+
+/*!
+ \fn void QWSMouseHandler::suspend()
+
+ Implement this function to suspend reading and handling of mouse
+ events, e.g., call the QSocketNotifier::setEnabled() function to
+ disable the socket notifier.
+
+ \sa resume()
+*/
+
+/*!
+ \fn void QWSMouseHandler::resume()
+
+ Implement this function to resume reading and handling mouse
+ events, e.g., call the QSocketNotifier::setEnabled() function to
+ enable the socket notifier.
+
+ \sa suspend()
+*/
+
+/*!
+ \fn const QPoint &QWSMouseHandler::pos() const
+
+ Returns the current mouse position.
+
+ \sa mouseChanged(), limitToScreen()
+*/
+
+/*!
+ Constructs a mouse driver. The \a driver and \a device arguments
+ are passed by the QWS_MOUSE_PROTO environment variable.
+
+ Call the QWSServer::setMouseHandler() function to make the newly
+ created mouse driver, the primary driver. Note that the primary
+ driver is controlled by the system, i.e., the system will delete
+ it upon exit.
+*/
+QWSMouseHandler::QWSMouseHandler(const QString &, const QString &)
+ : mousePos(QWSServer::mousePosition), d_ptr(new QWSMouseHandlerPrivate)
+{
+}
+
+/*!
+ Destroys this mouse driver.
+
+ Do not call this function if this driver is the primary mouse
+ driver, i.e., if QWSServer::setMouseHandler() function has been
+ called passing this driver as argument. The primary mouse
+ driver is deleted by the system.
+*/
+QWSMouseHandler::~QWSMouseHandler()
+{
+ delete d_ptr;
+}
+
+/*!
+ Ensures that the given \a position is within the screen's
+ boundaries, changing the \a position if necessary.
+
+ \sa pos(), setScreen()
+*/
+
+void QWSMouseHandler::limitToScreen(QPoint &position)
+{
+ position.setX(qMin(d_ptr->screen->deviceWidth() - 1, qMax(0, position.x())));
+ position.setY(qMin(d_ptr->screen->deviceHeight() - 1, qMax(0, position.y())));
+}
+
+/*!
+ \since 4.2
+
+ Sets the screen for this mouse driver to be the given \a screen.
+
+ \sa limitToScreen()
+*/
+void QWSMouseHandler::setScreen(const QScreen *screen)
+{
+ d_ptr->screen = (screen ? screen : qt_screen);
+}
+
+/*!
+ Notifies the system of a new mouse event.
+
+ This function updates the current mouse position and sends the
+ event to the \l{Qt for Embedded Linux} server application for
+ delivery to the correct widget. Note that a custom mouse driver must call
+ this function whenever it wants to deliver a new mouse event.
+
+ The given \a position is the global position of the mouse cursor.
+ The \a state parameter is a bitmask of the Qt::MouseButton enum's
+ values, indicating which mouse buttons are pressed. The \a wheel
+ parameter is the delta value of the mouse wheel as returned by
+ QWheelEvent::delta().
+
+ \sa pos()
+*/
+void QWSMouseHandler::mouseChanged(const QPoint &position, int state, int wheel)
+{
+ mousePos = position + d_ptr->screen->offset();
+ QWSServer::sendMouseEvent(mousePos, state, wheel);
+}
+
+/*!
+ \fn QWSMouseHandler::clearCalibration()
+
+ This virtual function allows subclasses of QWSMouseHandler to
+ clear the calibration information. Note that the default
+ implementation does nothing.
+
+ \sa QWSCalibratedMouseHandler::clearCalibration(), calibrate()
+*/
+
+/*!
+ \fn QWSMouseHandler::calibrate(const QWSPointerCalibrationData *data)
+
+ This virtual function allows subclasses of QWSMouseHandler to set
+ the calibration information passed in the given \a data. Note that
+ the default implementation does nothing.
+
+ \sa QWSCalibratedMouseHandler::calibrate(), clearCalibration()
+*/
+
+/*! \fn QWSMouseHandler::getCalibration(QWSPointerCalibrationData *data) const
+ This virtual function allows subclasses of QWSMouseHandler
+ to fill in the device coordinates in \a data with values
+ that correspond to screen coordinates that are already in
+ \a data. Note that the default implementation does nothing.
+ */
+
+/*!
+ \class QWSCalibratedMouseHandler
+ \ingroup qws
+
+ \brief The QWSCalibratedMouseHandler class provides mouse
+ calibration and noise reduction in Qt for Embedded Linux.
+
+ Note that this class is only available in \l{Qt for Embedded Linux}.
+
+ \l{Qt for Embedded Linux} provides ready-made drivers for several mouse
+ protocols, see the \l{Qt for Embedded Linux Pointer Handling}{pointer
+ handling} documentation for details. In general, custom mouse
+ drivers can be implemented by subclassing the QWSMouseHandler
+ class. But when the system device does not have a fixed mapping
+ between device and screen coordinates and/or produces noisy events
+ (e.g., a touchscreen), you should derive from the
+ QWSCalibratedMouseHandler class instead to take advantage of its
+ calibration functionality. As always, you must also create a mouse
+ driver plugin (derived from QMouseDriverPlugin);
+ the implementation of the QMouseDriverFactory class will then
+ automatically detect the plugin, and load the driver into the
+ server application at run-time using Qt's
+ \l{How to Create Qt Plugins}{plugin system}.
+
+ QWSCalibratedMouseHandler provides an implementation of the
+ calibrate() function to update the calibration parameters based on
+ coordinate mapping of the given calibration data. The calibration
+ data is represented by an QWSPointerCalibrationData object. The
+ linear transformation between device coordinates and screen
+ coordinates is performed by calling the transform() function
+ explicitly on the points passed to the
+ QWSMouseHandler::mouseChanged() function. Use the
+ clearCalibration() function to make the mouse driver return mouse
+ events in raw device coordinates and not in screen coordinates.
+
+ The calibration parameters are recalculated whenever calibrate()
+ is called, and they can be stored using the writeCalibration()
+ function. Previously written parameters can be retrieved at any
+ time using the readCalibration() function (calibration parameters
+ are always read when the class is instantiated). Note that the
+ calibration parameters is written to and read from the file
+ currently specified by the POINTERCAL_FILE environment variable;
+ the default file is \c /etc/pointercal.
+
+ To achieve noise reduction, QWSCalibratedMouseHandler provides the
+ sendFiltered() function. Use this function instead of
+ mouseChanged() whenever a mouse event occurs. The filter's size
+ can be manipulated using the setFilterSize() function.
+
+ \sa QWSMouseHandler, QWSPointerCalibrationData,
+ {Mouse Calibration Example}
+*/
+
+
+/*!
+ \internal
+ */
+
+QWSCalibratedMouseHandler::QWSCalibratedMouseHandler(const QString &, const QString &)
+ : samples(5), currSample(0), numSamples(0)
+{
+ clearCalibration();
+ readCalibration();
+}
+
+/*!
+ Fills \a cd with the device coordinates corresponding to the given
+ screen coordinates.
+
+ \internal
+*/
+void QWSCalibratedMouseHandler::getCalibration(QWSPointerCalibrationData *cd) const
+{
+ const qint64 scale = qint64(a) * qint64(e) - qint64(b) * qint64(d);
+ const qint64 xOff = qint64(b) * qint64(f) - qint64(c) * qint64(e);
+ const qint64 yOff = qint64(c) * qint64(d) - qint64(a) * qint64(f);
+ for (int i = 0; i <= QWSPointerCalibrationData::LastLocation; ++i) {
+ const qint64 sX = cd->screenPoints[i].x();
+ const qint64 sY = cd->screenPoints[i].y();
+ const qint64 dX = (s*(e*sX - b*sY) + xOff) / scale;
+ const qint64 dY = (s*(a*sY - d*sX) + yOff) / scale;
+ cd->devPoints[i] = QPoint(dX, dY);
+ }
+}
+
+/*!
+ Clears the current calibration, i.e., makes the mouse
+ driver return mouse events in raw device coordinates instead of
+ screen coordinates.
+
+ \sa calibrate()
+*/
+void QWSCalibratedMouseHandler::clearCalibration()
+{
+ a = 1;
+ b = 0;
+ c = 0;
+ d = 0;
+ e = 1;
+ f = 0;
+ s = 1;
+}
+
+
+/*!
+ Saves the current calibration parameters in \c /etc/pointercal
+ (separated by whitespace and in alphabetical order).
+
+ You can override the default \c /etc/pointercal by specifying
+ another file using the POINTERCAL_FILE environment variable.
+
+ \sa readCalibration()
+*/
+void QWSCalibratedMouseHandler::writeCalibration()
+{
+ QString calFile;
+ calFile = QString::fromLocal8Bit(qgetenv("POINTERCAL_FILE"));
+ if (calFile.isEmpty())
+ calFile = QLatin1String("/etc/pointercal");
+
+#ifndef QT_NO_TEXTSTREAM
+ QFile file(calFile);
+ if (file.open(QIODevice::WriteOnly)) {
+ QTextStream t(&file);
+ t << a << " " << b << " " << c << " ";
+ t << d << " " << e << " " << f << " " << s << endl;
+ } else
+#endif
+ {
+ qCritical("QWSCalibratedMouseHandler::writeCalibration: "
+ "Could not save calibration into %s", qPrintable(calFile));
+ }
+}
+
+/*!
+ Reads previously written calibration parameters which are stored
+ in \c /etc/pointercal (separated by whitespace and in alphabetical
+ order).
+
+ You can override the default \c /etc/pointercal by specifying
+ another file using the POINTERCAL_FILE environment variable.
+
+
+ \sa writeCalibration()
+*/
+void QWSCalibratedMouseHandler::readCalibration()
+{
+ QString calFile = QString::fromLocal8Bit(qgetenv("POINTERCAL_FILE"));
+ if (calFile.isEmpty())
+ calFile = QLatin1String("/etc/pointercal");
+
+#ifndef QT_NO_TEXTSTREAM
+ QFile file(calFile);
+ if (file.open(QIODevice::ReadOnly)) {
+ QTextStream t(&file);
+ t >> a >> b >> c >> d >> e >> f >> s;
+ if (s == 0 || t.status() != QTextStream::Ok) {
+ qCritical("Corrupt calibration data");
+ clearCalibration();
+ }
+ } else
+#endif
+ {
+ qDebug() << "Could not read calibration:" <<calFile;
+ }
+}
+
+static int ilog2(quint32 n)
+{
+ int result = 0;
+
+ if (n & 0xffff0000) {
+ n >>= 16;
+ result += 16;
+ }
+ if (n & 0xff00) {
+ n >>= 8;
+ result += 8;}
+ if (n & 0xf0) {
+ n >>= 4;
+ result += 4;
+ }
+ if (n & 0xc) {
+ n >>= 2;
+ result += 2;
+ }
+ if (n & 0x2)
+ result += 1;
+
+ return result;
+}
+
+/*!
+ Updates the calibration parameters based on coordinate mapping of
+ the given \a data.
+
+ Create an instance of the QWSPointerCalibrationData class, fill in
+ the device and screen coordinates and pass that object to the mouse
+ driver using this function.
+
+ \sa clearCalibration(), transform()
+*/
+void QWSCalibratedMouseHandler::calibrate(const QWSPointerCalibrationData *data)
+{
+ // Algorithm derived from
+ // "How To Calibrate Touch Screens" by Carlos E. Vidales,
+ // printed in Embedded Systems Programming, Vol. 15 no 6, June 2002
+ // URL: http://www.embedded.com/showArticle.jhtml?articleID=9900629
+
+ const QPoint pd0 = data->devPoints[QWSPointerCalibrationData::TopLeft];
+ const QPoint pd1 = data->devPoints[QWSPointerCalibrationData::TopRight];
+ const QPoint pd2 = data->devPoints[QWSPointerCalibrationData::BottomRight];
+ const QPoint p0 = data->screenPoints[QWSPointerCalibrationData::TopLeft];
+ const QPoint p1 = data->screenPoints[QWSPointerCalibrationData::TopRight];
+ const QPoint p2 = data->screenPoints[QWSPointerCalibrationData::BottomRight];
+
+ const qint64 xd0 = pd0.x();
+ const qint64 xd1 = pd1.x();
+ const qint64 xd2 = pd2.x();
+ const qint64 yd0 = pd0.y();
+ const qint64 yd1 = pd1.y();
+ const qint64 yd2 = pd2.y();
+ const qint64 x0 = p0.x();
+ const qint64 x1 = p1.x();
+ const qint64 x2 = p2.x();
+ const qint64 y0 = p0.y();
+ const qint64 y1 = p1.y();
+ const qint64 y2 = p2.y();
+
+ qint64 scale = ((xd0 - xd2)*(yd1 - yd2) - (xd1 - xd2)*(yd0 - yd2));
+ int shift = 0;
+ qint64 absScale = qAbs(scale);
+ // use maximum 16 bit precision to reduce risk of integer overflow
+ if (absScale > (1 << 16)) {
+ shift = ilog2(absScale >> 16) + 1;
+ scale >>= shift;
+ }
+
+ s = scale;
+ a = ((x0 - x2)*(yd1 - yd2) - (x1 - x2)*(yd0 - yd2)) >> shift;
+ b = ((xd0 - xd2)*(x1 - x2) - (x0 - x2)*(xd1 - xd2)) >> shift;
+ c = (yd0*(xd2*x1 - xd1*x2) + yd1*(xd0*x2 - xd2*x0) + yd2*(xd1*x0 - xd0*x1)) >> shift;
+ d = ((y0 - y2)*(yd1 - yd2) - (y1 - y2)*(yd0 - yd2)) >> shift;
+ e = ((xd0 - xd2)*(y1 - y2) - (y0 - y2)*(xd1 - xd2)) >> shift;
+ f = (yd0*(xd2*y1 - xd1*y2) + yd1*(xd0*y2 - xd2*y0) + yd2*(xd1*y0 - xd0*y1)) >> shift;
+
+ writeCalibration();
+}
+
+/*!
+ Transforms the given \a position from device coordinates to screen
+ coordinates, and returns the transformed position.
+
+ This function is typically called explicitly on the points passed
+ to the QWSMouseHandler::mouseChanged() function.
+
+ This implementation is a linear transformation using 7 parameters
+ (\c a, \c b, \c c, \c d, \c e, \c f and \c s) to transform the
+ device coordinates (\c Xd, \c Yd) into screen coordinates (\c Xs,
+ \c Ys) using the following equations:
+
+ \snippet doc/src/snippets/code/src_gui_embedded_qmouse_qws.cpp 0
+
+ \sa mouseChanged()
+*/
+QPoint QWSCalibratedMouseHandler::transform(const QPoint &position)
+{
+ QPoint tp;
+
+ tp.setX((a * position.x() + b * position.y() + c) / s);
+ tp.setY((d * position.x() + e * position.y() + f) / s);
+
+ return tp;
+}
+
+/*!
+ Sets the size of the filter used in noise reduction to the given
+ \a size.
+
+ The sendFiltered() function reduces noice by calculating an
+ average position from a collection of mouse event positions. The
+ filter size determines the number of positions that forms the
+ basis for these calculations.
+
+ \sa sendFiltered()
+*/
+void QWSCalibratedMouseHandler::setFilterSize(int size)
+{
+ samples.resize(qMax(1, size));
+ numSamples = 0;
+ currSample = 0;
+}
+
+/*!
+ \fn bool QWSCalibratedMouseHandler::sendFiltered(const QPoint &position, int state)
+
+ Notifies the system of a new mouse event \e after applying a noise
+ reduction filter. Returns true if the filtering process is
+ successful; otherwise returns false. Note that if the filtering
+ process failes, the system is not notified about the event.
+
+ The given \a position is the global position of the mouse. The \a
+ state parameter is a bitmask of the Qt::MouseButton enum's values
+ indicating which mouse buttons are pressed.
+
+ The noice is reduced by calculating an average position from a
+ collection of mouse event positions and then calling the
+ mouseChanged() function with the new position. The number of
+ positions that is used is determined by the filter size.
+
+ \sa mouseChanged(), setFilterSize()
+*/
+bool QWSCalibratedMouseHandler::sendFiltered(const QPoint &position, int button)
+{
+ if (!button) {
+ if (numSamples >= samples.count())
+ mouseChanged(transform(position), 0);
+ currSample = 0;
+ numSamples = 0;
+ return true;
+ }
+
+ bool sent = false;
+ samples[currSample] = position;
+ numSamples++;
+ if (numSamples >= samples.count()) {
+
+ int ignore = -1;
+ if (samples.count() > 2) { // throw away the "worst" sample
+ int maxd = 0;
+ for (int i = 0; i < samples.count(); i++) {
+ int d = (mousePos - samples[i]).manhattanLength();
+ if (d > maxd) {
+ maxd = d;
+ ignore = i;
+ }
+ }
+ }
+
+ // average the rest
+ QPoint pos(0, 0);
+ int numAveraged = 0;
+ for (int i = 0; i < samples.count(); i++) {
+ if (ignore == i)
+ continue;
+ pos += samples[i];
+ ++numAveraged;
+ }
+ if (numAveraged)
+ pos /= numAveraged;
+
+ mouseChanged(transform(pos), button);
+ sent = true;
+ }
+ currSample++;
+ if (currSample >= samples.count())
+ currSample = 0;
+
+ return sent;
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/embedded/qmouse_qws.h b/src/gui/embedded/qmouse_qws.h
new file mode 100644
index 0000000000..d003b4ee92
--- /dev/null
+++ b/src/gui/embedded/qmouse_qws.h
@@ -0,0 +1,123 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QMOUSE_QWS_H
+#define QMOUSE_QWS_H
+
+#include <QtCore/qobject.h>
+#include <QtGui/qpolygon.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+class QWSMouseHandlerPrivate;
+class QScreen;
+
+class Q_GUI_EXPORT QWSPointerCalibrationData
+{
+public:
+ enum Location { TopLeft = 0, BottomLeft = 1, BottomRight = 2, TopRight = 3,
+ Center = 4, LastLocation = Center };
+ QPoint devPoints[5];
+ QPoint screenPoints[5];
+};
+
+class Q_GUI_EXPORT QWSMouseHandler
+{
+public:
+ explicit QWSMouseHandler(const QString &driver = QString(),
+ const QString &device = QString());
+ virtual ~QWSMouseHandler();
+
+ virtual void clearCalibration() {}
+ virtual void calibrate(const QWSPointerCalibrationData *) {}
+ virtual void getCalibration(QWSPointerCalibrationData *) const {}
+
+ virtual void resume() = 0;
+ virtual void suspend() = 0;
+
+ void limitToScreen(QPoint &pt);
+ void mouseChanged(const QPoint& pos, int bstate, int wheel = 0);
+ const QPoint &pos() const { return mousePos; }
+
+ void setScreen(const QScreen *screen);
+
+protected:
+ QPoint &mousePos;
+ QWSMouseHandlerPrivate *d_ptr;
+};
+
+
+class Q_GUI_EXPORT QWSCalibratedMouseHandler : public QWSMouseHandler
+{
+public:
+ explicit QWSCalibratedMouseHandler(const QString &driver = QString(),
+ const QString &device = QString());
+
+ virtual void clearCalibration();
+ virtual void calibrate(const QWSPointerCalibrationData *);
+ virtual void getCalibration(QWSPointerCalibrationData *) const;
+
+protected:
+ bool sendFiltered(const QPoint &, int button);
+ QPoint transform(const QPoint &);
+
+ void readCalibration();
+ void writeCalibration();
+ void setFilterSize(int);
+
+private:
+ int a, b, c;
+ int d, e, f;
+ int s;
+ QPolygon samples;
+ int currSample;
+ int numSamples;
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QMOUSE_QWS_H
diff --git a/src/gui/embedded/qmousebus_qws.cpp b/src/gui/embedded/qmousebus_qws.cpp
new file mode 100644
index 0000000000..6b26349cb5
--- /dev/null
+++ b/src/gui/embedded/qmousebus_qws.cpp
@@ -0,0 +1,238 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qmousebus_qws.h"
+
+#ifndef QT_NO_QWS_MOUSE_BUS
+
+#include "qwindowsystem_qws.h"
+#include "qsocketnotifier.h"
+
+#include "qapplication.h"
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <termios.h>
+
+QT_BEGIN_NAMESPACE
+
+/*
+ * bus mouse driver (a.k.a. Logitech busmouse)
+ */
+
+class QWSBusMouseHandlerPrivate : public QObject
+{
+ Q_OBJECT
+public:
+ QWSBusMouseHandlerPrivate(QWSBusMouseHandler *h, const QString &driver, const QString &device);
+ ~QWSBusMouseHandlerPrivate();
+
+ void suspend();
+ void resume();
+
+private slots:
+ void readMouseData();
+
+protected:
+ enum { mouseBufSize = 128 };
+ QWSBusMouseHandler *handler;
+ QSocketNotifier *mouseNotifier;
+ int mouseFD;
+ int mouseIdx;
+ int obstate;
+ uchar mouseBuf[mouseBufSize];
+};
+
+QWSBusMouseHandler::QWSBusMouseHandler(const QString &driver, const QString &device)
+ : QWSMouseHandler(driver, device)
+{
+ d = new QWSBusMouseHandlerPrivate(this, driver, device);
+}
+
+QWSBusMouseHandler::~QWSBusMouseHandler()
+{
+ delete d;
+}
+
+void QWSBusMouseHandler::suspend()
+{
+ d->suspend();
+}
+
+void QWSBusMouseHandler::resume()
+{
+ d->resume();
+}
+
+
+QWSBusMouseHandlerPrivate::QWSBusMouseHandlerPrivate(QWSBusMouseHandler *h,
+ const QString &, const QString &device)
+ : handler(h)
+
+{
+ QString mouseDev = device;
+ if (mouseDev.isEmpty())
+ mouseDev = QLatin1String("/dev/mouse");
+ obstate = -1;
+ mouseFD = -1;
+ mouseFD = open(mouseDev.toLocal8Bit(), O_RDWR | O_NDELAY);
+ if (mouseFD < 0)
+ mouseFD = open(mouseDev.toLocal8Bit(), O_RDONLY | O_NDELAY);
+ if (mouseFD < 0)
+ qDebug("Cannot open %s (%s)", qPrintable(mouseDev), strerror(errno));
+
+ // Clear pending input
+ tcflush(mouseFD,TCIFLUSH);
+ usleep(50000);
+
+ char buf[100]; // busmouse driver will not read if bufsize < 3, YYD
+ while (read(mouseFD, buf, 100) > 0) { } // eat unwanted replies
+
+ mouseIdx = 0;
+
+ mouseNotifier = new QSocketNotifier(mouseFD, QSocketNotifier::Read, this);
+ connect(mouseNotifier, SIGNAL(activated(int)),this, SLOT(readMouseData()));
+}
+
+QWSBusMouseHandlerPrivate::~QWSBusMouseHandlerPrivate()
+{
+ if (mouseFD >= 0) {
+ tcflush(mouseFD,TCIFLUSH); // yyd.
+ close(mouseFD);
+ }
+}
+
+
+void QWSBusMouseHandlerPrivate::suspend()
+{
+ mouseNotifier->setEnabled(false);
+}
+
+
+void QWSBusMouseHandlerPrivate::resume()
+{
+ mouseIdx = 0;
+ obstate = -1;
+ mouseNotifier->setEnabled(true);
+}
+
+void QWSBusMouseHandlerPrivate::readMouseData()
+{
+ int n;
+ // It'll only read 3 bytes a time and return all other buffer zeroed, thus cause protocol errors
+ for (;;) {
+ if (mouseBufSize - mouseIdx < 3)
+ break;
+ n = read(mouseFD, mouseBuf+mouseIdx, 3);
+ if (n != 3)
+ break;
+ mouseIdx += 3;
+ }
+
+ static const int accel_limit = 5;
+ static const int accel = 2;
+
+ int idx = 0;
+ int bstate = 0;
+ int dx = 0, dy = 0;
+ bool sendEvent = false;
+ int tdx = 0, tdy = 0;
+
+ while (mouseIdx-idx >= 3) {
+#if 0 // debug
+ qDebug("Got mouse data");
+#endif
+ uchar *mb = mouseBuf+idx;
+ bstate = 0;
+ dx = 0;
+ dy = 0;
+ sendEvent = false;
+ if (((mb[0] & 0x04)))
+ bstate |= Qt::LeftButton;
+ if (((mb[0] & 0x01)))
+ bstate |= Qt::RightButton;
+
+ dx=(signed char)mb[1];
+ dy=(signed char)mb[2];
+ sendEvent=true;
+
+ if (sendEvent) {
+ if (qAbs(dx) > accel_limit || qAbs(dy) > accel_limit) {
+ dx *= accel;
+ dy *= accel;
+ }
+ tdx += dx;
+ tdy += dy;
+ if (bstate != obstate) {
+ QPoint pos = handler->pos() + QPoint(tdx,-tdy);
+ handler->limitToScreen(pos);
+ handler->mouseChanged(pos,bstate);
+ sendEvent = false;
+ tdx = 0;
+ tdy = 0;
+ obstate = bstate;
+ }
+ }
+ idx += 3;
+ }
+ if (sendEvent) {
+ QPoint pos = handler->pos() + QPoint(tdx,-tdy);
+ handler->limitToScreen(pos);
+ handler->mouseChanged(pos,bstate);
+ }
+
+ int surplus = mouseIdx - idx;
+ for (int i = 0; i < surplus; i++)
+ mouseBuf[i] = mouseBuf[idx+i];
+ mouseIdx = surplus;
+}
+
+QT_END_NAMESPACE
+
+#include "qmousebus_qws.moc"
+
+#endif // QT_NO_QWS_MOUSE_BUS
diff --git a/src/gui/embedded/qmousebus_qws.h b/src/gui/embedded/qmousebus_qws.h
new file mode 100644
index 0000000000..636b466374
--- /dev/null
+++ b/src/gui/embedded/qmousebus_qws.h
@@ -0,0 +1,76 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QMOUSEBUS_QWS_H
+#define QMOUSEBUS_QWS_H
+
+#include <QtGui/qmouse_qws.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+#ifndef QT_NO_QWS_MOUSE_BUS
+
+class QWSBusMouseHandlerPrivate;
+
+class QWSBusMouseHandler : public QWSMouseHandler
+{
+public:
+ explicit QWSBusMouseHandler(const QString & = QString(),
+ const QString & = QString());
+ ~QWSBusMouseHandler();
+
+ void suspend();
+ void resume();
+protected:
+ QWSBusMouseHandlerPrivate *d;
+};
+
+#endif // QT_NO_QWS_MOUSE_BUS
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QMOUSEBUS_QWS_H
diff --git a/src/gui/embedded/qmousedriverfactory_qws.cpp b/src/gui/embedded/qmousedriverfactory_qws.cpp
new file mode 100644
index 0000000000..db1d7a4262
--- /dev/null
+++ b/src/gui/embedded/qmousedriverfactory_qws.cpp
@@ -0,0 +1,195 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qmousedriverfactory_qws.h"
+
+#include "qapplication.h"
+#include "qmousepc_qws.h"
+#include "qmousebus_qws.h"
+#include "qmousevr41xx_qws.h"
+#include "qmouseyopy_qws.h"
+#include "qmouselinuxtp_qws.h"
+#include "qmousevfb_qws.h"
+#include "qmousetslib_qws.h"
+#include <stdlib.h>
+#include "private/qfactoryloader_p.h"
+#include "qmousedriverplugin_qws.h"
+#include "qdebug.h"
+
+QT_BEGIN_NAMESPACE
+
+#if !defined(Q_OS_WIN32) || defined(QT_MAKEDLL)
+#ifndef QT_NO_LIBRARY
+
+Q_GLOBAL_STATIC_WITH_ARGS(QFactoryLoader, loader,
+ (QWSMouseHandlerFactoryInterface_iid,
+ QLatin1String("/mousedrivers"), Qt::CaseInsensitive))
+
+#endif //QT_NO_LIBRARY
+#endif //QT_MAKEDLL
+
+/*!
+ \class QMouseDriverFactory
+ \ingroup qws
+
+ \brief The QMouseDriverFactory class creates mouse drivers in
+ Qt for Embedded Linux.
+
+ Note that this class is only available in \l{Qt for Embedded Linux}.
+
+ QMouseDriverFactory is used to detect and instantiate the
+ available mouse drivers, allowing \l{Qt for Embedded Linux} to load the
+ preferred driver into the server application at runtime. The
+ create() function returns a QWSMouseHandler object representing
+ the mouse driver identified by a given key. The valid keys
+ (i.e. the supported drivers) can be retrieved using the keys()
+ function.
+
+ \l{Qt for Embedded Linux} provides several built-in mouse drivers. In
+ addition, custom mouse drivers can be added using Qt's plugin
+ mechanism, i.e. by subclassing the QWSMouseHandler class and
+ creating a mouse driver plugin (QMouseDriverPlugin). See the
+ \l{Qt for Embedded Linux Pointer Handling}{pointer handling}
+ documentation for details.
+
+ \sa QWSMouseHandler, QMouseDriverPlugin
+*/
+
+/*!
+ Creates the mouse driver specified by the given \a key, using the
+ display specified by the given \a device.
+
+ Note that the keys are case-insensitive.
+
+ \sa keys()
+*/
+QWSMouseHandler *QMouseDriverFactory::create(const QString& key, const QString &device)
+{
+ QString driver = key.toLower();
+#ifndef QT_NO_QWS_MOUSE_LINUXTP
+ if (driver == QLatin1String("linuxtp") || driver.isEmpty())
+ return new QWSLinuxTPMouseHandler(key, device);
+#endif
+#ifndef QT_NO_QWS_MOUSE_YOPY
+ if (driver == QLatin1String("yopy") || driver.isEmpty())
+ return new QWSYopyMouseHandler(key, device);
+#endif
+#ifndef QT_NO_QWS_MOUSE_VR41XX
+ if (driver == QLatin1String("vr41xx") || driver.isEmpty())
+ return new QWSVr41xxMouseHandler(key, device);
+#endif
+#ifndef QT_NO_QWS_MOUSE_PC
+ if (driver == QLatin1String("auto")
+ || driver == QLatin1String("intellimouse")
+ || driver == QLatin1String("microsoft")
+ || driver == QLatin1String("mousesystems")
+ || driver == QLatin1String("mouseman")
+ || driver.isEmpty()) {
+ return new QWSPcMouseHandler(key, device);
+ }
+#endif
+#ifndef QT_NO_QWS_MOUSE_BUS
+ if (driver == QLatin1String("bus"))
+ return new QWSBusMouseHandler(key, device);
+#endif
+#ifndef QT_NO_QWS_MOUSE_TSLIB
+ if (driver == QLatin1String("tslib") || driver.isEmpty())
+ return new QWSTslibMouseHandler(key, device);
+#endif
+#ifndef QT_NO_QWS_MOUSE_QVFB
+ if (driver == QLatin1String("qvfbmouse") || driver == QLatin1String("qvfb"))
+ return new QVFbMouseHandler(key, device);
+#endif
+
+#if !defined(Q_OS_WIN32) || defined(QT_MAKEDLL)
+#ifndef QT_NO_LIBRARY
+ if (QWSMouseHandlerFactoryInterface *factory = qobject_cast<QWSMouseHandlerFactoryInterface*>(loader()->instance(driver)))
+ return factory->create(driver, device);
+#endif
+#endif
+ return 0;
+}
+
+/*!
+ Returns the list of valid keys, i.e. the available mouse drivers.
+
+ \sa create()
+*/
+QStringList QMouseDriverFactory::keys()
+{
+ QStringList list;
+
+#ifndef QT_NO_QWS_MOUSE_LINUXTP
+ list << QLatin1String("LinuxTP");
+#endif
+#ifndef QT_NO_QWS_MOUSE_YOPY
+ list << QLatin1String("Yopy");
+#endif
+#ifndef QT_NO_QWS_MOUSE_VR41XX
+ list << QLatin1String("VR41xx");
+#endif
+#ifndef QT_NO_QWS_MOUSE_PC
+ list << QLatin1String("Auto")
+ << QLatin1String("IntelliMouse")
+ << QLatin1String("Microsoft")
+ << QLatin1String("MouseSystems")
+ << QLatin1String("MouseMan");
+#endif
+#ifndef QT_NO_QWS_MOUSE_BUS
+ list << QLatin1String("Bus");
+#endif
+#ifndef QT_NO_QWS_MOUSE_TSLIB
+ list << QLatin1String("Tslib");
+#endif
+
+#if !defined(Q_OS_WIN32) || defined(QT_MAKEDLL)
+#ifndef QT_NO_LIBRARY
+ QStringList plugins = loader()->keys();
+ for (int i = 0; i < plugins.size(); ++i) {
+ if (!list.contains(plugins.at(i)))
+ list += plugins.at(i);
+ }
+#endif //QT_NO_LIBRARY
+#endif //QT_MAKEDLL
+ return list;
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/embedded/qmousedriverfactory_qws.h b/src/gui/embedded/qmousedriverfactory_qws.h
new file mode 100644
index 0000000000..6a13bb71d5
--- /dev/null
+++ b/src/gui/embedded/qmousedriverfactory_qws.h
@@ -0,0 +1,67 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QMOUSEDRIVERFACTORY_QWS_H
+#define QMOUSEDRIVERFACTORY_QWS_H
+
+#include <QtCore/qstringlist.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+class QString;
+class QWSMouseHandler;
+
+class Q_GUI_EXPORT QMouseDriverFactory
+{
+public:
+ static QStringList keys();
+ static QWSMouseHandler *create(const QString&, const QString &);
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QMOUSEDRIVERFACTORY_QWS_H
diff --git a/src/gui/embedded/qmousedriverplugin_qws.cpp b/src/gui/embedded/qmousedriverplugin_qws.cpp
new file mode 100644
index 0000000000..e7f11b372d
--- /dev/null
+++ b/src/gui/embedded/qmousedriverplugin_qws.cpp
@@ -0,0 +1,124 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qmousedriverplugin_qws.h"
+
+#ifndef QT_NO_LIBRARY
+
+#include "qmouse_qws.h"
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QMouseDriverPlugin
+ \ingroup plugins
+ \ingroup qws
+
+ \brief The QMouseDriverPlugin class is an abstract base class for
+ mouse driver plugins in Qt for Embedded Linux.
+
+ Note that this class is only available in \l{Qt for Embedded Linux}.
+
+ \l{Qt for Embedded Linux} provides ready-made drivers for several mouse
+ protocols, see the \l{Qt for Embedded Linux Pointer Handling}{pointer
+ handling} documentation for details. Custom mouse drivers can be
+ implemented by subclassing the QWSMouseHandler class and creating
+ a mouse driver plugin.
+
+ A mouse driver plugin can be created by subclassing
+ QMouseDriverPlugin and reimplementing the pure virtual keys() and
+ create() functions. By exporting the derived class using the
+ Q_EXPORT_PLUGIN2() macro, The default implementation of the
+ QMouseDriverFactory class will automatically detect the plugin and
+ load the driver into the server application at run-time. See \l
+ {How to Create Qt Plugins} for details.
+
+ \sa QWSMouseHandler, QMouseDriverFactory
+*/
+
+/*!
+ \fn QStringList QMouseDriverPlugin::keys() const
+
+ Implement this function to return the list of valid keys, i.e. the
+ mouse drivers supported by this plugin.
+
+ \l{Qt for Embedded Linux} provides ready-made drivers for several mouse
+ protocols, see the \l {Qt for Embedded Linux Pointer Handling}{pointer
+ handling} documentation for details.
+
+ \sa create()
+*/
+
+/*!
+ Constructs a mouse driver plugin with the given \a parent.
+
+ Note that this constructor is invoked automatically by the
+ Q_EXPORT_PLUGIN2() macro, so there is no need for calling it
+ explicitly.
+*/
+QMouseDriverPlugin::QMouseDriverPlugin(QObject *parent)
+ : QObject(parent)
+{
+}
+
+/*!
+ Destroys the mouse driver plugin.
+
+ Note that Qt destroys a plugin automatically when it is no longer
+ used, so there is no need for calling the destructor explicitly.
+*/
+QMouseDriverPlugin::~QMouseDriverPlugin()
+{
+}
+
+/*!
+ \fn QScreen* QMouseDriverPlugin::create(const QString &key, const QString& device)
+
+ Implement this function to create a driver matching the type
+ specified by the given \a key and \a device parameters. Note that
+ keys are case-insensitive.
+
+ \sa keys()
+*/
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_LIBRARY
diff --git a/src/gui/embedded/qmousedriverplugin_qws.h b/src/gui/embedded/qmousedriverplugin_qws.h
new file mode 100644
index 0000000000..4a864f98a3
--- /dev/null
+++ b/src/gui/embedded/qmousedriverplugin_qws.h
@@ -0,0 +1,84 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QMOUSEDRIVERPLUGIN_QWS_H
+#define QMOUSEDRIVERPLUGIN_QWS_H
+
+#include <QtCore/qplugin.h>
+#include <QtCore/qfactoryinterface.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+#ifndef QT_NO_LIBRARY
+
+class QWSMouseHandler;
+
+struct Q_GUI_EXPORT QWSMouseHandlerFactoryInterface : public QFactoryInterface
+{
+ virtual QWSMouseHandler* create(const QString &name, const QString &device) = 0;
+};
+
+#define QWSMouseHandlerFactoryInterface_iid "com.trolltech.Qt.QWSMouseHandlerFactoryInterface"
+Q_DECLARE_INTERFACE(QWSMouseHandlerFactoryInterface, QWSMouseHandlerFactoryInterface_iid)
+
+class Q_GUI_EXPORT QMouseDriverPlugin : public QObject, public QWSMouseHandlerFactoryInterface
+{
+ Q_OBJECT
+ Q_INTERFACES(QWSMouseHandlerFactoryInterface:QFactoryInterface)
+public:
+ explicit QMouseDriverPlugin(QObject *parent = 0);
+ ~QMouseDriverPlugin();
+
+ virtual QStringList keys() const = 0;
+ virtual QWSMouseHandler* create(const QString& driver, const QString &device) = 0;
+};
+
+#endif // QT_NO_LIBRARY
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QMOUSEDRIVERPLUGIN_QWS_H
diff --git a/src/gui/embedded/qmouselinuxtp_qws.cpp b/src/gui/embedded/qmouselinuxtp_qws.cpp
new file mode 100644
index 0000000000..7683be3ba4
--- /dev/null
+++ b/src/gui/embedded/qmouselinuxtp_qws.cpp
@@ -0,0 +1,334 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qmouselinuxtp_qws.h"
+
+#ifndef QT_NO_QWS_MOUSE_LINUXTP
+#include "qwindowsystem_qws.h"
+#include "qsocketnotifier.h"
+#include "qtimer.h"
+#include "qapplication.h"
+#include "qscreen_qws.h"
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <termios.h>
+
+QT_BEGIN_NAMESPACE
+
+#if defined(QT_QWS_IPAQ)
+ #define QT_QWS_IPAQ_RAW
+ #define QT_QWS_SCREEN_COORDINATES
+ typedef struct {
+ unsigned short pressure;
+ unsigned short x;
+ unsigned short y;
+ unsigned short pad;
+ } TS_EVENT;
+#elif defined(QT_QWS_EBX)
+ #define QT_QWS_EBX_RAW
+ #define QT_QWS_SCREEN_COORDINATES
+#ifndef QT_QWS_SHARP
+ typedef struct {
+ unsigned short pressure;
+ unsigned short x;
+ unsigned short y;
+ unsigned short pad;
+ } TS_EVENT;
+ #else
+ typedef struct {
+ long y;
+ long x;
+ long pressure;
+ long long millisecs;
+ } TS_EVENT;
+ #define QT_QWS_TP_SAMPLE_SIZE 10
+ #define QT_QWS_TP_MINIMUM_SAMPLES 4
+ #define QT_QWS_TP_PRESSURE_THRESHOLD 500
+ #define QT_QWS_TP_MOVE_LIMIT 50
+ #define QT_QWS_TP_JITTER_LIMIT 2
+ #endif
+#else // not IPAQ, not SHARP
+ typedef struct {
+ unsigned short pressure;
+ unsigned short x;
+ unsigned short y;
+ unsigned short pad;
+ } TS_EVENT;
+#endif
+
+#ifndef QT_QWS_TP_SAMPLE_SIZE
+#define QT_QWS_TP_SAMPLE_SIZE 5
+#endif
+
+#ifndef QT_QWS_TP_MINIMUM_SAMPLES
+#define QT_QWS_TP_MINIMUM_SAMPLES 5
+#endif
+
+#ifndef QT_QWS_TP_PRESSURE_THRESHOLD
+#define QT_QWS_TP_PRESSURE_THRESHOLD 1
+#endif
+
+#ifndef QT_QWS_TP_MOVE_LIMIT
+#define QT_QWS_TP_MOVE_LIMIT 100
+#endif
+
+#ifndef QT_QWS_TP_JITTER_LIMIT
+#define QT_QWS_TP_JITTER_LIMIT 2
+#endif
+
+class QWSLinuxTPMouseHandlerPrivate : public QObject
+{
+ Q_OBJECT
+public:
+ QWSLinuxTPMouseHandlerPrivate(QWSLinuxTPMouseHandler *h, const QString &);
+ ~QWSLinuxTPMouseHandlerPrivate();
+
+ void suspend();
+ void resume();
+private:
+ static const int mouseBufSize = 2048;
+ int mouseFD;
+ QPoint oldmouse;
+ QPoint oldTotalMousePos;
+ bool waspressed;
+ QPolygon samples;
+ int currSample;
+ int lastSample;
+ int numSamples;
+ int skipCount;
+ int mouseIdx;
+ uchar mouseBuf[mouseBufSize];
+ QWSLinuxTPMouseHandler *handler;
+ QSocketNotifier *mouseNotifier;
+
+private slots:
+ void readMouseData();
+};
+
+QWSLinuxTPMouseHandler::QWSLinuxTPMouseHandler(const QString &driver, const QString &device)
+ : QWSCalibratedMouseHandler(driver, device)
+{
+ d = new QWSLinuxTPMouseHandlerPrivate(this, device);
+}
+
+QWSLinuxTPMouseHandler::~QWSLinuxTPMouseHandler()
+{
+ delete d;
+}
+
+void QWSLinuxTPMouseHandler::suspend()
+{
+ d->suspend();
+}
+
+void QWSLinuxTPMouseHandler::resume()
+{
+ d->resume();
+}
+
+QWSLinuxTPMouseHandlerPrivate::QWSLinuxTPMouseHandlerPrivate(QWSLinuxTPMouseHandler *h,
+ const QString &device)
+ : samples(QT_QWS_TP_SAMPLE_SIZE), currSample(0), lastSample(0),
+ numSamples(0), skipCount(0), handler(h)
+{
+ QString mousedev;
+ if (device.isEmpty()) {
+#if defined(QT_QWS_IPAQ)
+# ifdef QT_QWS_IPAQ_RAW
+ mousedev = QLatin1String("/dev/h3600_tsraw");
+# else
+ mousedev = QLatin1String("/dev/h3600_ts");
+# endif
+#else
+ mousedev = QLatin1String("/dev/ts");
+#endif
+ } else {
+ mousedev = device;
+ }
+ if ((mouseFD = open(mousedev.toLatin1().constData(), O_RDONLY | O_NDELAY)) < 0) {
+ qWarning("Cannot open %s (%s)", qPrintable(mousedev), strerror(errno));
+ return;
+ }
+
+ mouseNotifier = new QSocketNotifier(mouseFD, QSocketNotifier::Read,
+ this);
+ connect(mouseNotifier, SIGNAL(activated(int)),this, SLOT(readMouseData()));
+ waspressed=false;
+ mouseIdx = 0;
+}
+
+QWSLinuxTPMouseHandlerPrivate::~QWSLinuxTPMouseHandlerPrivate()
+{
+ if (mouseFD >= 0)
+ close(mouseFD);
+}
+
+void QWSLinuxTPMouseHandlerPrivate::suspend()
+{
+ if (mouseNotifier)
+ mouseNotifier->setEnabled(false);
+}
+
+void QWSLinuxTPMouseHandlerPrivate::resume()
+{
+ mouseIdx=0;
+ currSample=0;
+ lastSample=0;
+ numSamples=0;
+ skipCount=0;
+ if (mouseNotifier)
+ mouseNotifier->setEnabled(true);
+}
+
+
+void QWSLinuxTPMouseHandlerPrivate::readMouseData()
+{
+ if(!qt_screen)
+ return;
+
+ int n;
+ do {
+ n = read(mouseFD, mouseBuf+mouseIdx, mouseBufSize-mouseIdx);
+ if (n > 0)
+ mouseIdx += n;
+ } while (n > 0 && mouseIdx < mouseBufSize);
+
+ //qDebug("readMouseData()");
+
+ TS_EVENT *data;
+ int idx = 0;
+
+ // perhaps we shouldn't be reading EVERY SAMPLE.
+ while (mouseIdx-idx >= (int)sizeof(TS_EVENT)) {
+ uchar *mb = mouseBuf+idx;
+ data = (TS_EVENT *) mb;
+
+ if(data->pressure >= QT_QWS_TP_PRESSURE_THRESHOLD) {
+#ifdef QT_QWS_SHARP
+ samples[currSample] = QPoint(1000 - data->x, data->y);
+#else
+ samples[currSample] = QPoint(data->x, data->y);
+#endif
+ numSamples++;
+ if (numSamples >= QT_QWS_TP_MINIMUM_SAMPLES) {
+ int sampleCount = qMin(numSamples + 1,samples.count());
+
+ // average the rest
+ QPoint mousePos = QPoint(0, 0);
+ QPoint totalMousePos = oldTotalMousePos;
+ totalMousePos += samples[currSample];
+ if(numSamples >= samples.count())
+ totalMousePos -= samples[lastSample];
+
+ mousePos = totalMousePos / (sampleCount - 1);
+#if defined(QT_QWS_SCREEN_COORDINATES)
+ mousePos = handler->transform(mousePos);
+#endif
+ if(!waspressed)
+ oldmouse = mousePos;
+ QPoint dp = mousePos - oldmouse;
+ int dxSqr = dp.x() * dp.x();
+ int dySqr = dp.y() * dp.y();
+ if (dxSqr + dySqr < (QT_QWS_TP_MOVE_LIMIT * QT_QWS_TP_MOVE_LIMIT)) {
+ if (waspressed) {
+ if ((dxSqr + dySqr > (QT_QWS_TP_JITTER_LIMIT * QT_QWS_TP_JITTER_LIMIT)) || skipCount > 2) {
+ handler->mouseChanged(mousePos,Qt::LeftButton);
+ oldmouse = mousePos;
+ skipCount = 0;
+ } else {
+ skipCount++;
+ }
+ } else {
+ handler->mouseChanged(mousePos,Qt::LeftButton);
+ oldmouse=mousePos;
+ waspressed=true;
+ }
+
+ // save recuring information
+ currSample++;
+ if (numSamples >= samples.count())
+ lastSample++;
+ oldTotalMousePos = totalMousePos;
+ } else {
+ numSamples--; // don't use this sample, it was bad.
+ }
+ } else {
+ // build up the average
+ oldTotalMousePos += samples[currSample];
+ currSample++;
+ }
+ if (currSample >= samples.count())
+ currSample = 0;
+ if (lastSample >= samples.count())
+ lastSample = 0;
+ } else {
+ currSample = 0;
+ lastSample = 0;
+ numSamples = 0;
+ skipCount = 0;
+ oldTotalMousePos = QPoint(0,0);
+ if (waspressed) {
+ handler->mouseChanged(oldmouse,0);
+ oldmouse = QPoint(-100, -100);
+ waspressed=false;
+ }
+ }
+ idx += sizeof(TS_EVENT);
+ }
+
+ int surplus = mouseIdx - idx;
+ for (int i = 0; i < surplus; i++)
+ mouseBuf[i] = mouseBuf[idx+i];
+ mouseIdx = surplus;
+}
+
+QT_END_NAMESPACE
+
+#include "qmouselinuxtp_qws.moc"
+
+#endif //QT_NO_QWS_MOUSE_LINUXTP
diff --git a/src/gui/embedded/qmouselinuxtp_qws.h b/src/gui/embedded/qmouselinuxtp_qws.h
new file mode 100644
index 0000000000..2385455c19
--- /dev/null
+++ b/src/gui/embedded/qmouselinuxtp_qws.h
@@ -0,0 +1,77 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QMOUSELINUXTP_QWS_H
+#define QMOUSELINUXTP_QWS_H
+
+#include <QtGui/qmouse_qws.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+#ifndef QT_NO_QWS_MOUSE_LINUXTP
+
+class QWSLinuxTPMouseHandlerPrivate;
+
+class QWSLinuxTPMouseHandler : public QWSCalibratedMouseHandler
+{
+ friend class QWSLinuxTPMouseHandlerPrivate;
+public:
+ explicit QWSLinuxTPMouseHandler(const QString & = QString(),
+ const QString & = QString());
+ ~QWSLinuxTPMouseHandler();
+
+ void suspend();
+ void resume();
+protected:
+ QWSLinuxTPMouseHandlerPrivate *d;
+};
+
+#endif // QT_NO_QWS_MOUSE_LINUXTP
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QMOUSELINUXTP_QWS_H
diff --git a/src/gui/embedded/qmousepc_qws.cpp b/src/gui/embedded/qmousepc_qws.cpp
new file mode 100644
index 0000000000..a0cb032b60
--- /dev/null
+++ b/src/gui/embedded/qmousepc_qws.cpp
@@ -0,0 +1,793 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qmousepc_qws.h"
+
+#ifndef QT_NO_QWS_MOUSE_PC
+
+#include "qwindowsystem_qws.h"
+#include "qsocketnotifier.h"
+#include "qwsevent_qws.h"
+#include "qwscommand_qws_p.h"
+#include "qwsutils_qws.h"
+
+#include "qapplication.h"
+#include "qpolygon.h"
+#include "qtimer.h"
+#include "qfile.h"
+#include "qtextstream.h"
+#include "qstringlist.h"
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <termios.h>
+
+#include <qscreen_qws.h>
+
+QT_BEGIN_NAMESPACE
+
+//#define QWS_MOUSE_DEBUG
+
+/*
+ * Automatic-detection mouse driver
+ */
+
+class QWSPcMouseSubHandler {
+protected:
+ enum { max_buf=32 };
+
+ int fd;
+
+ uchar buffer[max_buf];
+ int nbuf;
+
+ QPoint motion;
+ int bstate;
+ int wheel;
+
+ int goodness;
+ int badness;
+
+ virtual int tryData()=0;
+
+public:
+ QWSPcMouseSubHandler(int f) : fd(f)
+ {
+ initState();
+ }
+ virtual ~QWSPcMouseSubHandler() {}
+
+ int file() const { return fd; }
+
+ void closeIfNot(int& f)
+ {
+ if (fd != f) {
+ f = fd;
+ close(fd);
+ }
+ }
+
+ void initState() { nbuf = bstate = goodness = badness = 0; }
+
+ void worse(int by=1) { badness+=by; }
+ bool reliable() const { return goodness >= 5 && badness < 50; }
+ int buttonState() const { return bstate; }
+ bool motionPending() const { return motion!=QPoint(0,0); }
+ QPoint takeMotion() { QPoint r=motion; motion=QPoint(0,0); return r; }
+ int takeWheel() { int result = wheel; wheel = 0; return result; }
+
+ void appendData(uchar* data, int length)
+ {
+ memcpy(buffer+nbuf, data, length);
+ nbuf += length;
+ }
+
+ enum UsageResult { Insufficient, Motion, Button };
+
+ UsageResult useData()
+ {
+ int pbstate = bstate;
+ int n = tryData();
+#ifdef QWS_MOUSE_DEBUG
+ if (n) {
+ fprintf(stderr, "QWSPcMouseSubHandler tryData read %d bytes:", n);
+ for (int i=0; i<n; ++i)
+ fprintf(stderr, " %02x", buffer[i]);
+ fprintf(stderr, "\n");
+ }
+#endif
+ if (n > 0) {
+ if (n<nbuf)
+ memmove(buffer, buffer+n, nbuf-n);
+ nbuf -= n;
+ return (wheel || pbstate != bstate) ? Button : Motion;
+ }
+ return Insufficient;
+ }
+};
+
+class QWSPcMouseSubHandler_intellimouse : public QWSPcMouseSubHandler {
+ int packetsize;
+public:
+ QWSPcMouseSubHandler_intellimouse(int f) : QWSPcMouseSubHandler(f)
+ {
+ init();
+ }
+
+ void init()
+ {
+ int n;
+ uchar reply[20];
+
+ if (tcflush(fd,TCIOFLUSH) == -1) {
+#ifdef QWS_MOUSE_DEBUG
+ perror("QWSPcMouseSubHandler_intellimouse: pre-init tcflush");
+#endif
+ }
+ static const uchar initseq[] = { 243, 200, 243, 100, 243, 80 };
+ static const uchar query[] = { 0xf2 };
+ if (write(fd, initseq, sizeof(initseq))!=sizeof(initseq)) {
+ badness = 100;
+ return;
+ }
+ usleep(10000);
+ if (tcflush(fd,TCIOFLUSH) == -1) {
+#ifdef QWS_MOUSE_DEBUG
+ perror("QWSPcMouseSubHandler_intellimouse: post-init tcflush");
+#endif
+ }
+ if (write(fd, query, sizeof(query))!=sizeof(query)) {
+ badness = 100;
+ return;
+ }
+ usleep(10000);
+ n = read(fd, reply, 20);
+ if (n > 0) {
+ goodness = 10;
+ switch (reply[n-1]) {
+ case 3:
+ case 4:
+ packetsize = 4;
+ break;
+ default:
+ packetsize = 3;
+ }
+ } else {
+ badness = 100;
+ }
+ }
+
+ int tryData()
+ {
+ if (nbuf >= packetsize) {
+ //int overflow = (buffer[0]>>6)& 0x03;
+
+ if (/*overflow ||*/ !(buffer[0] & 8)) {
+#ifdef QWS_MOUSE_DEBUG
+ qDebug("Intellimouse: skipping (overflow)");
+#endif
+ badness++;
+ return 1;
+ } else {
+ QPoint delta((buffer[0] & 0x10) ? buffer[1]-256 : buffer[1],
+ (buffer[0] & 0x20) ? 256-buffer[2] : -buffer[2]);
+ motion += delta;
+ int nbstate = buffer[0] & 0x7;
+#ifdef QWS_MOUSE_DEBUG
+ int debugwheel =
+#endif
+ wheel = packetsize > 3 ? -(signed char)buffer[3] : 0;
+ if (wheel < -2 || wheel > 2)
+ wheel = 0;
+ wheel *= 120; // WHEEL_DELTA?
+#ifdef QWS_MOUSE_DEBUG
+ qDebug("Intellimouse: motion %d,%d, state %d, raw wheel %d, wheel %d", motion.x(), motion.y(), nbstate, debugwheel, wheel);
+#endif
+ if (motion.x() || motion.y() || bstate != nbstate || wheel) {
+ bstate = nbstate;
+ goodness++;
+ } else {
+ badness++;
+ return 1;
+ }
+ }
+ return packetsize;
+ }
+ return 0;
+ }
+};
+
+class QWSPcMouseSubHandler_mouseman : public QWSPcMouseSubHandler {
+ int packetsize;
+public:
+ QWSPcMouseSubHandler_mouseman(int f) : QWSPcMouseSubHandler(f)
+ {
+ init();
+ }
+
+ void init()
+ {
+ if (tcflush(fd,TCIOFLUSH) == -1) {
+#ifdef QWS_MOUSE_DEBUG
+ perror("QWSPcMouseSubHandler_mouseman: initial tcflush");
+#endif
+ }
+ write(fd,"",1);
+ usleep(50000);
+ write(fd,"@EeI!",5);
+ usleep(10000);
+ static const char ibuf[] = { 246, 244 };
+ write(fd,ibuf,1);
+ write(fd,ibuf+1,1);
+ if (tcflush(fd,TCIOFLUSH) == -1) {
+#ifdef QWS_MOUSE_DEBUG
+ perror("QWSPcMouseSubHandler_mouseman: tcflush");
+#endif
+ }
+ usleep(10000);
+
+ char buf[100];
+ while (read(fd, buf, 100) > 0) { } // eat unwanted replies
+ }
+
+ int tryData()
+ {
+ if (nbuf >= 3) {
+ int nbstate = 0;
+ if (buffer[0] & 0x01)
+ nbstate |= Qt::LeftButton;
+ if (buffer[0] & 0x02)
+ nbstate |= Qt::RightButton;
+ if (buffer[0] & 0x04)
+ nbstate |= Qt::MidButton;
+
+ int overflow = (buffer[0]>>6)& 0x03;
+ if (overflow) {
+ //### wheel events signalled with overflow bit, ignore for now
+ badness++;
+ return 1;
+ } else {
+ bool xs = buffer[0] & 0x10;
+ bool ys = buffer[0] & 0x20;
+ int dx = xs ? buffer[1]-256 : buffer[1];
+ int dy = ys ? buffer[2]-256 : buffer[2];
+
+ motion += QPoint(dx, -dy);
+ if (motion.x() || motion.y() || bstate != nbstate) {
+ bstate = nbstate;
+ goodness++;
+ } else {
+ badness++;
+ return 1;
+ }
+ }
+ return 3;
+ }
+ return 0;
+ }
+};
+
+class QWSPcMouseSubHandler_serial : public QWSPcMouseSubHandler {
+public:
+ QWSPcMouseSubHandler_serial(int f) : QWSPcMouseSubHandler(f)
+ {
+ initSerial();
+ }
+
+protected:
+ void setflags(int f)
+ {
+ termios tty;
+ if (tcgetattr(fd, &tty) == -1) {
+#ifdef QWS_MOUSE_DEBUG
+ perror("QWSPcMouseSubHandler_serial: tcgetattr");
+#endif
+ }
+ tty.c_iflag = IGNBRK | IGNPAR;
+ tty.c_oflag = 0;
+ tty.c_lflag = 0;
+ tty.c_cflag = f | CREAD | CLOCAL | HUPCL;
+#if !defined(Q_OS_DARWIN) && !defined(Q_OS_SOLARIS) && !defined(Q_OS_INTEGRITY)
+ tty.c_line = 0;
+#endif
+ tty.c_cc[VTIME] = 0;
+ tty.c_cc[VMIN] = 1;
+ if (tcsetattr(fd, TCSANOW, &tty) == -1) {
+#ifdef QWS_MOUSE_DEBUG
+ perror("QWSPcMouseSubHandler_serial: tcgetattr");
+#endif
+ }
+ }
+
+private:
+ void initSerial()
+ {
+ int speed[4] = { B9600, B4800, B2400, B1200 };
+
+ for (int n = 0; n < 4; n++) {
+ setflags(CSTOPB | speed[n]);
+ write(fd, "*q", 2);
+ usleep(10000);
+ }
+ }
+};
+
+class QWSPcMouseSubHandler_mousesystems : public QWSPcMouseSubHandler_serial {
+public:
+ // ##### This driver has not been tested
+
+ QWSPcMouseSubHandler_mousesystems(int f) : QWSPcMouseSubHandler_serial(f)
+ {
+ init();
+ }
+
+ void init()
+ {
+ setflags(B1200|CS8|CSTOPB);
+ // 60Hz
+ if (write(fd, "R", 1)!=1) {
+ badness = 100;
+ return;
+ }
+ if (tcflush(fd,TCIOFLUSH) == -1) {
+#ifdef QT_QWS_VNC_DEBUG
+ perror("QWSPcMouseSubHandler_mousesystems: tcflush");
+#endif
+ }
+ }
+
+ int tryData()
+ {
+ if (nbuf >= 5) {
+ if ((buffer[0] & 0xf8) != 0x80) {
+ badness++;
+ return 1;
+ }
+ motion +=
+ QPoint((signed char)buffer[1] + (signed char)buffer[3],
+ -(signed char)buffer[2] + (signed char)buffer[4]);
+ int t = ~buffer[0];
+ int nbstate = ((t&3) << 1) | ((t&4) >> 2);
+ if (motion.x() || motion.y() || bstate != nbstate) {
+ bstate = nbstate;
+ goodness++;
+ } else {
+ badness++;
+ return 1;
+ }
+ return 5;
+ }
+ return 0;
+ }
+};
+
+class QWSPcMouseSubHandler_ms : public QWSPcMouseSubHandler_serial {
+ int mman;
+public:
+ QWSPcMouseSubHandler_ms(int f) : QWSPcMouseSubHandler_serial(f)
+ {
+ mman=0;
+ init();
+ }
+
+ void init()
+ {
+ setflags(B1200|CS7);
+ // 60Hz
+ if (write(fd, "R", 1)!=1) {
+ badness = 100;
+ return;
+ }
+ if (tcflush(fd,TCIOFLUSH) == -1) {
+#ifdef QWS_MOUSE_DEBUG
+ perror("QWSPcMouseSubHandler_ms: tcflush");
+#endif
+ }
+ }
+
+ int tryData()
+ {
+ if (!(buffer[0] & 0x40)) {
+ if (buffer[0] == 0x20 && (bstate & Qt::MidButton)) {
+ mman=1; // mouseman extension
+ }
+ return 1;
+ }
+ int extra = mman&&(bstate & Qt::MidButton);
+ if (nbuf >= 3+extra) {
+ int nbstate = 0;
+ if (buffer[0] == 0x40 && !bstate && !buffer[1] && !buffer[2]) {
+ nbstate = Qt::MidButton;
+ } else {
+ nbstate = ((buffer[0] & 0x20) >> 5)
+ | ((buffer[0] & 0x10) >> 3);
+ if (extra && buffer[3] == 0x20)
+ nbstate = Qt::MidButton;
+ }
+
+ if (buffer[1] & 0x40) {
+ badness++;
+ return 1;
+ } else {
+ motion +=
+ QPoint((signed char)((buffer[0]&0x3)<<6)
+ |(signed char)(buffer[1]&0x3f),
+ (signed char)((buffer[0]&0xc)<<4)
+ |(signed char)(buffer[2]&0x3f));
+ if (motion.x() || motion.y() || bstate != nbstate) {
+ bstate = nbstate;
+ goodness++;
+ } else {
+ badness++;
+ return 1;
+ }
+ return 3+extra;
+ }
+ }
+ return 0;
+ }
+};
+
+//===========================================================================
+
+class QWSPcMouseHandlerPrivate : public QObject
+{
+ Q_OBJECT
+public:
+ QWSPcMouseHandlerPrivate(QWSPcMouseHandler *h, const QString &, const QString &);
+ ~QWSPcMouseHandlerPrivate();
+
+ void suspend();
+ void resume();
+
+private:
+ enum { max_dev=32 };
+ QWSPcMouseSubHandler *sub[max_dev];
+ QList<QSocketNotifier*> notifiers;
+ int nsub;
+ int retries;
+
+private slots:
+ void readMouseData(int);
+
+private:
+ void openDevices();
+ void closeDevices();
+ void notify(int fd);
+ bool sendEvent(QWSPcMouseSubHandler& h);
+
+private:
+ QWSPcMouseHandler *handler;
+ QString driver;
+ QString device;
+ qreal accel;
+ int accel_limit;
+};
+
+QWSPcMouseHandler::QWSPcMouseHandler(const QString &driver, const QString &device)
+ : QWSMouseHandler(driver, device)
+{
+ d = new QWSPcMouseHandlerPrivate(this, driver, device);
+}
+
+QWSPcMouseHandler::~QWSPcMouseHandler()
+{
+ delete d;
+}
+
+void QWSPcMouseHandler::suspend()
+{
+ d->suspend();
+}
+
+void QWSPcMouseHandler::resume()
+{
+ d->resume();
+}
+
+
+QWSPcMouseHandlerPrivate::QWSPcMouseHandlerPrivate(QWSPcMouseHandler *h,
+ const QString &drv, const QString &arg)
+ : handler(h), driver(drv)
+{
+ QStringList args = arg.split(QLatin1Char(':'), QString::SkipEmptyParts);
+
+ int index;
+
+ accel = qreal(2.0);
+ QRegExp accelRegex(QLatin1String("^accel=(\\d+\\.?\\d*)$"));
+ index = args.indexOf(accelRegex);
+ if (index >= 0) {
+ accel = qreal(accelRegex.cap(1).toDouble());
+ args.removeAt(index);
+ }
+
+ accel_limit = 5;
+ QRegExp accelLimitRegex(QLatin1String("^accel_limit=(\\d+)$"));
+ index = args.indexOf(accelLimitRegex);
+ if (index >= 0) {
+ accel_limit = accelLimitRegex.cap(1).toInt();
+ args.removeAt(index);
+ }
+
+ device = args.join(QString());
+
+ retries = 0;
+ openDevices();
+}
+
+QWSPcMouseHandlerPrivate::~QWSPcMouseHandlerPrivate()
+{
+ closeDevices();
+}
+
+/*
+QWSPcMouseHandler::UsageResult QWSPcMouseHandler::useDev(Dev& d)
+{
+ if (d.nbuf >= mouseData[d.protocol].bytesPerPacket) {
+ uchar *mb = d.buf;
+ int bstate = 0;
+ int dx = 0;
+ int dy = 0;
+
+ switch (mouseProtocol) {
+ case MouseMan:
+ case IntelliMouse:
+ {
+ bstate = mb[0] & 0x7; // assuming Qt::*Button order
+
+ int overflow = (mb[0]>>6)& 0x03;
+ if (mouseProtocol == MouseMan && overflow) {
+ //### wheel events signalled with overflow bit, ignore for now
+ }
+ else {
+ bool xs = mb[0] & 0x10;
+ bool ys = mb[0] & 0x20;
+ dx = xs ? mb[1]-256 : mb[1];
+ dy = ys ? mb[2]-256 : mb[2];
+ }
+ break;
+ }
+ case Microsoft:
+ if (((mb[0] & 0x20) >> 3)) {
+ bstate |= Qt::LeftButton;
+ }
+ if (((mb[0] & 0x10) >> 4)) {
+ bstate |= Qt::RightButton;
+ }
+
+ dx=(signed char)(((mb[0] & 0x03) << 6) | (mb[1] & 0x3f));
+ dy=-(signed char)(((mb[0] & 0x0c) << 4) | (mb[2] & 0x3f));
+
+ break;
+ }
+ }
+ }
+*/
+
+
+bool QWSPcMouseHandlerPrivate::sendEvent(QWSPcMouseSubHandler& h)
+{
+ if (h.reliable()) {
+ QPoint motion = h.takeMotion();
+ if (qAbs(motion.x()) > accel_limit || qAbs(motion.y()) > accel_limit)
+ motion *= accel;
+ QPoint newPos = handler->pos() + motion;
+ if (qt_screen->isTransformed()) {
+ QSize s = QSize(qt_screen->width(), qt_screen->height());
+ newPos = qt_screen->mapToDevice(newPos, s);
+ }
+ handler->limitToScreen(newPos);
+
+ handler->mouseChanged(newPos, h.buttonState(), h.takeWheel());
+ return true;
+ } else {
+ h.takeMotion();
+ if (h.buttonState() & (Qt::RightButton|Qt::MidButton)) {
+ // Strange for the user to press right or middle without
+ // a moving mouse!
+ h.worse();
+ }
+ return false;
+ }
+}
+
+void QWSPcMouseHandlerPrivate::openDevices()
+{
+ nsub=0;
+ int fd = -1;
+
+ QString drv = driver.toLower();
+ if (!drv.isEmpty() && drv != QLatin1String("auto")) {
+ // Manually specified mouse
+ QByteArray dev = device.toLatin1();
+ if (drv == QLatin1String("intellimouse")) {
+ if (dev.isEmpty())
+ dev = "/dev/psaux";
+ fd = open(dev, O_RDWR | O_NDELAY);
+ if (fd >= 0)
+ sub[nsub++] = new QWSPcMouseSubHandler_intellimouse(fd);
+ } else if (drv == QLatin1String("microsoft")) {
+ if (dev.isEmpty())
+ dev = "/dev/ttyS0";
+ fd = open(dev, O_RDWR | O_NDELAY);
+ if (fd >= 0)
+ sub[nsub++] = new QWSPcMouseSubHandler_ms(fd);
+ } else if (drv == QLatin1String("mousesystems")) {
+ if (dev.isEmpty())
+ dev = "/dev/ttyS0";
+ fd = open(dev, O_RDWR | O_NDELAY);
+ if (fd >= 0)
+ sub[nsub++] = new QWSPcMouseSubHandler_mousesystems(fd);
+ } else if (drv == QLatin1String("mouseman")) {
+ if (dev.isEmpty())
+ dev = "/dev/psaux";
+ fd = open(dev, O_RDWR | O_NDELAY);
+ if (fd >= 0)
+ sub[nsub++] = new QWSPcMouseSubHandler_mouseman(fd);
+ }
+ if (fd >= 0)
+ notify(fd);
+ else
+ qCritical("Error opening mouse device '%s': %s",
+ dev.constData(), strerror(errno));
+ } else {
+ // Try automatically
+ fd = open("/dev/psaux", O_RDWR | O_NDELAY);
+ if (fd >= 0) {
+ sub[nsub++] = new QWSPcMouseSubHandler_intellimouse(fd);
+ notify(fd);
+ }
+ fd = open("/dev/input/mice", O_RDWR | O_NDELAY);
+ if (fd >= 0) {
+ sub[nsub++] = new QWSPcMouseSubHandler_intellimouse(fd);
+ notify(fd);
+ //qDebug("/dev/input/mice fd %d #%d", fd, nsub-1);
+ }
+
+// include the code below to auto-detect serial mice, and to mess up
+// any sort of serial communication
+#if 0
+ const char fn[4][11] = { "/dev/ttyS0", "/dev/ttyS1", "/dev/ttyS2", "/dev/ttyS3" };
+ for (int ch = 0; ch < 4; ++ch) {
+ fd = open(fn[ch], O_RDWR | O_NDELAY);
+ if (fd >= 0) {
+ //sub[nsub++] = new QWSPcMouseSubHandler_intellimouse(fd);
+ sub[nsub++] = new QWSPcMouseSubHandler_mousesystems(fd);
+ sub[nsub++] = new QWSPcMouseSubHandler_ms(fd);
+ notify(fd);
+ }
+ }
+#endif
+ }
+}
+
+void QWSPcMouseHandlerPrivate::closeDevices()
+{
+ int pfd=-1;
+ for (int i=0; i<nsub; i++) {
+ sub[i]->closeIfNot(pfd);
+ delete sub[i];
+ }
+ qDeleteAll(notifiers);
+ notifiers.clear();
+}
+
+void QWSPcMouseHandlerPrivate::suspend()
+{
+ for (int i=0; i<notifiers.size(); ++i)
+ notifiers.at(i)->setEnabled(false);
+}
+
+void QWSPcMouseHandlerPrivate::resume()
+{
+ for (int i=0; i<nsub; i++)
+ sub[i]->initState();
+
+ for (int i=0; i<notifiers.size(); ++i)
+ notifiers.at(i)->setEnabled(true);
+}
+
+
+
+void QWSPcMouseHandlerPrivate::notify(int fd)
+{
+ QSocketNotifier *mouseNotifier
+ = new QSocketNotifier(fd, QSocketNotifier::Read, this);
+ connect(mouseNotifier, SIGNAL(activated(int)),this, SLOT(readMouseData(int)));
+ notifiers.append(mouseNotifier);
+}
+
+void QWSPcMouseHandlerPrivate::readMouseData(int fd)
+{
+ for (;;) {
+ uchar buf[8];
+ int n = read(fd, buf, 8);
+ if (n<=0)
+ break;
+ for (int i=0; i<nsub; i++) {
+ QWSPcMouseSubHandler& h = *sub[i];
+ if (h.file() == fd) {
+ h.appendData(buf,n);
+ for (;;) {
+ switch (h.useData()) {
+ case QWSPcMouseSubHandler::Button:
+ sendEvent(h);
+ break;
+ case QWSPcMouseSubHandler::Insufficient:
+ goto breakbreak;
+ case QWSPcMouseSubHandler::Motion:
+ break;
+ }
+ }
+ breakbreak:
+ ;
+ }
+ }
+ }
+ bool any_reliable=false;
+ for (int i=0; i<nsub; i++) {
+ QWSPcMouseSubHandler& h = *sub[i];
+ if (h.motionPending())
+ sendEvent(h);
+ any_reliable = any_reliable || h.reliable();
+ }
+ if (any_reliable) {
+ // ... get rid of all unreliable ones? All bad ones?
+ } else if (retries < 2) {
+ // Try again - maybe the mouse was being moved when we tried to init.
+ closeDevices();
+ openDevices();
+ retries++;
+ }
+}
+
+QT_END_NAMESPACE
+
+#include "qmousepc_qws.moc"
+
+#endif // QT_NO_MOUSE_PC
diff --git a/src/gui/embedded/qmousepc_qws.h b/src/gui/embedded/qmousepc_qws.h
new file mode 100644
index 0000000000..6a088780ca
--- /dev/null
+++ b/src/gui/embedded/qmousepc_qws.h
@@ -0,0 +1,76 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QMOUSEPC_QWS_H
+#define QMOUSEPC_QWS_H
+
+#include <QtGui/qmouse_qws.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+#ifndef QT_NO_QWS_MOUSE_PC
+
+class QWSPcMouseHandlerPrivate;
+
+class QWSPcMouseHandler : public QWSMouseHandler
+{
+public:
+ explicit QWSPcMouseHandler(const QString & = QString(),
+ const QString & = QString());
+ ~QWSPcMouseHandler();
+
+ void suspend();
+ void resume();
+protected:
+ QWSPcMouseHandlerPrivate *d;
+};
+
+#endif // QT_NO_QWS_MOUSE_PC
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QMOUSEPC_QWS_H
diff --git a/src/gui/embedded/qmousetslib_qws.cpp b/src/gui/embedded/qmousetslib_qws.cpp
new file mode 100644
index 0000000000..8edbc62245
--- /dev/null
+++ b/src/gui/embedded/qmousetslib_qws.cpp
@@ -0,0 +1,371 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qmousetslib_qws.h"
+
+#if !defined(QT_NO_QWS_MOUSE_TSLIB) || defined(QT_PLUGIN)
+
+#include <QtCore/qregexp.h>
+#include <QtCore/qstringlist.h>
+#include "qsocketnotifier.h"
+#include "qscreen_qws.h"
+
+#include <tslib.h>
+#include <errno.h>
+
+QT_BEGIN_NAMESPACE
+
+#ifdef TSLIBMOUSEHANDLER_DEBUG
+# include <QtCore/QDebug>
+#endif
+
+/*!
+ \internal
+
+ \class QWSTslibMouseHandler
+ \ingroup qws
+
+ \brief The QWSTslibMouseHandler class implements a mouse driver
+ for the Universal Touch Screen Library, tslib.
+
+ QWSTslibMouseHandler inherits the QWSCalibratedMouseHandler class,
+ providing calibration and noise reduction functionality in
+ addition to generating mouse events, for devices using the
+ Universal Touch Screen Library.
+
+ To be able to compile this mouse handler, \l{Qt for Embedded Linux}
+ must be configured with the \c -qt-mouse-tslib option, see the
+ \l{Pointer Handling} documentation for details. In addition, the tslib
+ headers and library must be present in the build environment. The
+ tslib sources can be downloaded from \l
+ {http://tslib.berlios.de/}. Use the \c -L and \c -I options
+ with \c configure to explicitly specify the location of the
+ library and its headers:
+
+ \snippet doc/src/snippets/code/src_gui_embedded_qmousetslib_qws.cpp 0
+
+ In order to use this mouse handler, tslib must also be correctly
+ installed on the target machine. This includes providing a \c
+ ts.conf configuration file and setting the necessary environment
+ variables, see the README file provided with tslib for details.
+
+ The ts.conf file will usually contain the following two lines
+
+ \snippet doc/src/snippets/code/src_gui_embedded_qmousetslib_qws.cpp 1
+
+ To make \l{Qt for Embedded Linux} explicitly choose the tslib mouse
+ handler, set the QWS_MOUSE_PROTO environment variable.
+
+ \sa {Pointer Handling}, {Qt for Embedded Linux}
+*/
+
+class QWSTslibMouseHandlerPrivate : public QObject
+{
+ Q_OBJECT
+public:
+ QWSTslibMouseHandlerPrivate(QWSTslibMouseHandler *h,
+ const QString &device);
+ ~QWSTslibMouseHandlerPrivate();
+
+ void suspend();
+ void resume();
+
+ void calibrate(const QWSPointerCalibrationData *data);
+ void clearCalibration();
+
+private:
+ QWSTslibMouseHandler *handler;
+ struct tsdev *dev;
+ QSocketNotifier *mouseNotifier;
+ int jitter_limit;
+
+ struct ts_sample lastSample;
+ bool wasPressed;
+ int lastdx;
+ int lastdy;
+
+ bool calibrated;
+ QString devName;
+
+ bool open();
+ void close();
+ inline bool get_sample(struct ts_sample *sample);
+
+private slots:
+ void readMouseData();
+};
+
+QWSTslibMouseHandlerPrivate::QWSTslibMouseHandlerPrivate(QWSTslibMouseHandler *h,
+ const QString &device)
+ : handler(h), dev(0), mouseNotifier(0), jitter_limit(3)
+{
+ QStringList args = device.split(QLatin1Char(':'), QString::SkipEmptyParts);
+ QRegExp jitterRegex(QLatin1String("^jitter_limit=(\\d+)$"));
+ int index = args.indexOf(jitterRegex);
+ if (index >= 0) {
+ jitter_limit = jitterRegex.cap(1).toInt();
+ args.removeAt(index);
+ }
+
+ devName = args.join(QString());
+
+ if (devName.isNull()) {
+ const char *str = getenv("TSLIB_TSDEVICE");
+ if (str)
+ devName = QString::fromLocal8Bit(str);
+ }
+
+ if (devName.isNull())
+ devName = QLatin1String("/dev/ts");
+
+ if (!open())
+ return;
+
+ calibrated = true;
+
+ int fd = ts_fd(dev);
+ mouseNotifier = new QSocketNotifier(fd, QSocketNotifier::Read, this);
+ connect(mouseNotifier, SIGNAL(activated(int)),this, SLOT(readMouseData()));
+ resume();
+}
+
+QWSTslibMouseHandlerPrivate::~QWSTslibMouseHandlerPrivate()
+{
+ close();
+}
+
+bool QWSTslibMouseHandlerPrivate::open()
+{
+ dev = ts_open(devName.toLocal8Bit().constData(), 1);
+ if (!dev) {
+ qCritical("QWSTslibMouseHandlerPrivate: ts_open() failed"
+ " with error: '%s'", strerror(errno));
+ qCritical("Please check your tslib installation!");
+ return false;
+ }
+
+ if (ts_config(dev)) {
+ qCritical("QWSTslibMouseHandlerPrivate: ts_config() failed"
+ " with error: '%s'", strerror(errno));
+ qCritical("Please check your tslib installation!");
+ close();
+ return false;
+ }
+
+ return true;
+}
+
+void QWSTslibMouseHandlerPrivate::close()
+{
+ if (dev)
+ ts_close(dev);
+}
+
+void QWSTslibMouseHandlerPrivate::suspend()
+{
+ if (mouseNotifier)
+ mouseNotifier->setEnabled(false);
+}
+
+void QWSTslibMouseHandlerPrivate::resume()
+{
+ memset(&lastSample, 0, sizeof(lastSample));
+ wasPressed = false;
+ lastdx = 0;
+ lastdy = 0;
+ if (mouseNotifier)
+ mouseNotifier->setEnabled(true);
+}
+
+bool QWSTslibMouseHandlerPrivate::get_sample(struct ts_sample *sample)
+{
+ if (!calibrated)
+ return (ts_read_raw(dev, sample, 1) == 1);
+
+ return (ts_read(dev, sample, 1) == 1);
+}
+
+void QWSTslibMouseHandlerPrivate::readMouseData()
+{
+ if (!qt_screen)
+ return;
+
+ for(;;) {
+ struct ts_sample sample = lastSample;
+ bool pressed = wasPressed;
+
+ // Fast return if there's no events.
+ if (!get_sample(&sample))
+ return;
+ pressed = (sample.pressure > 0);
+
+ // Only return last sample unless there's a press/release event.
+ while (pressed == wasPressed) {
+ if (!get_sample(&sample))
+ break;
+ pressed = (sample.pressure > 0);
+ }
+
+ // work around missing coordinates on mouse release in raw mode
+ if (!calibrated && !pressed && sample.x == 0 && sample.y == 0) {
+ sample.x = lastSample.x;
+ sample.y = lastSample.y;
+ }
+
+ int dx = sample.x - lastSample.x;
+ int dy = sample.y - lastSample.y;
+
+ // Remove small movements in oppsite direction
+ if (dx * lastdx < 0 && qAbs(dx) < jitter_limit) {
+ sample.x = lastSample.x;
+ dx = 0;
+ }
+ if (dy * lastdy < 0 && qAbs(dy) < jitter_limit) {
+ sample.y = lastSample.y;
+ dy = 0;
+ }
+
+ if (wasPressed == pressed && dx == 0 && dy == 0)
+ return;
+
+#ifdef TSLIBMOUSEHANDLER_DEBUG
+ qDebug() << "last" << QPoint(lastSample.x, lastSample.y)
+ << "curr" << QPoint(sample.x, sample.y)
+ << "dx,dy" << QPoint(dx, dy)
+ << "ddx,ddy" << QPoint(dx*lastdx, dy*lastdy)
+ << "pressed" << wasPressed << pressed;
+#endif
+
+ lastSample = sample;
+ wasPressed = pressed;
+ if (dx != 0)
+ lastdx = dx;
+ if (dy != 0)
+ lastdy = dy;
+
+ const QPoint p(sample.x, sample.y);
+ if (calibrated) {
+ // tslib should do all the translation and filtering, so we send a
+ // "raw" mouse event
+ handler->QWSMouseHandler::mouseChanged(p, pressed);
+ } else {
+ handler->sendFiltered(p, pressed);
+ }
+ }
+}
+
+void QWSTslibMouseHandlerPrivate::clearCalibration()
+{
+ suspend();
+ close();
+ handler->QWSCalibratedMouseHandler::clearCalibration();
+ calibrated = false;
+ open();
+ resume();
+}
+
+void QWSTslibMouseHandlerPrivate::calibrate(const QWSPointerCalibrationData *data)
+{
+ suspend();
+ close();
+ // default implementation writes to /etc/pointercal
+ // using the same format as the tslib linear module.
+ handler->QWSCalibratedMouseHandler::calibrate(data);
+ calibrated = true;
+ open();
+ resume();
+}
+
+/*!
+ \internal
+*/
+QWSTslibMouseHandler::QWSTslibMouseHandler(const QString &driver,
+ const QString &device)
+ : QWSCalibratedMouseHandler(driver, device)
+{
+ d = new QWSTslibMouseHandlerPrivate(this, device);
+}
+
+/*!
+ \internal
+*/
+QWSTslibMouseHandler::~QWSTslibMouseHandler()
+{
+ delete d;
+}
+
+/*!
+ \reimp
+*/
+void QWSTslibMouseHandler::suspend()
+{
+ d->suspend();
+}
+
+/*!
+ \reimp
+*/
+void QWSTslibMouseHandler::resume()
+{
+ d->resume();
+}
+
+/*!
+ \reimp
+*/
+void QWSTslibMouseHandler::clearCalibration()
+{
+ d->clearCalibration();
+}
+
+/*!
+ \reimp
+*/
+void QWSTslibMouseHandler::calibrate(const QWSPointerCalibrationData *data)
+{
+ d->calibrate(data);
+}
+
+QT_END_NAMESPACE
+
+#include "qmousetslib_qws.moc"
+
+#endif //QT_NO_QWS_MOUSE_TSLIB
diff --git a/src/gui/embedded/qmousetslib_qws.h b/src/gui/embedded/qmousetslib_qws.h
new file mode 100644
index 0000000000..07bacde31f
--- /dev/null
+++ b/src/gui/embedded/qmousetslib_qws.h
@@ -0,0 +1,80 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QMOUSETSLIB_QWS_H
+#define QMOUSETSLIB_QWS_H
+
+#include <QtGui/qmouse_qws.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+#if !defined(QT_NO_QWS_MOUSE_TSLIB) || defined(QT_PLUGIN)
+
+class QWSTslibMouseHandlerPrivate;
+
+class QWSTslibMouseHandler : public QWSCalibratedMouseHandler
+{
+public:
+ explicit QWSTslibMouseHandler(const QString &driver = QString(),
+ const QString &device = QString());
+ ~QWSTslibMouseHandler();
+
+ void suspend();
+ void resume();
+
+ void calibrate(const QWSPointerCalibrationData *data);
+ void clearCalibration();
+
+protected:
+ friend class QWSTslibMouseHandlerPrivate;
+ QWSTslibMouseHandlerPrivate *d;
+};
+
+
+QT_END_NAMESPACE
+#endif // QT_NO_QWS_MOUSE_TSLIB
+QT_END_HEADER
+
+#endif // QMOUSETSLIB_QWS_H
diff --git a/src/gui/embedded/qmousevfb_qws.cpp b/src/gui/embedded/qmousevfb_qws.cpp
new file mode 100644
index 0000000000..9d81201f70
--- /dev/null
+++ b/src/gui/embedded/qmousevfb_qws.cpp
@@ -0,0 +1,132 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QT_NO_QWS_MOUSE_QVFB
+
+#include <stdlib.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <stdio.h>
+
+#include <qvfbhdr.h>
+#include <qmousevfb_qws.h>
+#include <qwindowsystem_qws.h>
+#include <qsocketnotifier.h>
+#include <qapplication.h>
+#include <qtimer.h>
+
+QT_BEGIN_NAMESPACE
+
+QVFbMouseHandler::QVFbMouseHandler(const QString &driver, const QString &device)
+ : QObject(), QWSMouseHandler(driver, device)
+{
+ QString mouseDev = device;
+ if (device.isEmpty())
+ mouseDev = QLatin1String("/dev/vmouse");
+
+ mouseFD = open(mouseDev.toLatin1().constData(), O_RDWR | O_NDELAY);
+ if (mouseFD == -1) {
+ perror("QVFbMouseHandler::QVFbMouseHandler");
+ qWarning("QVFbMouseHander: Unable to open device %s",
+ qPrintable(mouseDev));
+ return;
+ }
+
+ // Clear pending input
+ char buf[2];
+ while (read(mouseFD, buf, 1) > 0) { }
+
+ mouseIdx = 0;
+
+ mouseNotifier = new QSocketNotifier(mouseFD, QSocketNotifier::Read, this);
+ connect(mouseNotifier, SIGNAL(activated(int)),this, SLOT(readMouseData()));
+}
+
+QVFbMouseHandler::~QVFbMouseHandler()
+{
+ if (mouseFD >= 0)
+ close(mouseFD);
+}
+
+void QVFbMouseHandler::resume()
+{
+ mouseNotifier->setEnabled(true);
+}
+
+void QVFbMouseHandler::suspend()
+{
+ mouseNotifier->setEnabled(false);
+}
+
+void QVFbMouseHandler::readMouseData()
+{
+ int n;
+ do {
+ n = read(mouseFD, mouseBuf+mouseIdx, mouseBufSize-mouseIdx);
+ if (n > 0)
+ mouseIdx += n;
+ } while (n > 0);
+
+ int idx = 0;
+ static const int packetsize = sizeof(QPoint) + 2*sizeof(int);
+ while (mouseIdx-idx >= packetsize) {
+ uchar *mb = mouseBuf+idx;
+ QPoint mousePos = *reinterpret_cast<QPoint *>(mb);
+ mb += sizeof(QPoint);
+ int bstate = *reinterpret_cast<int *>(mb);
+ mb += sizeof(int);
+ int wheel = *reinterpret_cast<int *>(mb);
+// limitToScreen(mousePos);
+ mouseChanged(mousePos, bstate, wheel);
+ idx += packetsize;
+ }
+
+ int surplus = mouseIdx - idx;
+ for (int i = 0; i < surplus; i++)
+ mouseBuf[i] = mouseBuf[idx+i];
+ mouseIdx = surplus;
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_QWS_MOUSE_QVFB
diff --git a/src/gui/embedded/qmousevfb_qws.h b/src/gui/embedded/qmousevfb_qws.h
new file mode 100644
index 0000000000..fbf1efe817
--- /dev/null
+++ b/src/gui/embedded/qmousevfb_qws.h
@@ -0,0 +1,83 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QMOUSEVFB_QWS_H
+#define QMOUSEVFB_QWS_H
+
+#include <QtGui/qmouse_qws.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+#ifndef QT_NO_QWS_MOUSE_QVFB
+
+class QSocketNotifier;
+
+class QVFbMouseHandler : public QObject, public QWSMouseHandler {
+ Q_OBJECT
+public:
+ QVFbMouseHandler(const QString &driver = QString(),
+ const QString &device = QString());
+ ~QVFbMouseHandler();
+
+ void resume();
+ void suspend();
+
+private:
+ int mouseFD;
+ int mouseIdx;
+ enum {mouseBufSize = 128};
+ uchar mouseBuf[mouseBufSize];
+ QSocketNotifier *mouseNotifier;
+
+private Q_SLOTS:
+ void readMouseData();
+};
+#endif // QT_NO_QWS_MOUSE_QVFB
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QMOUSEVFB_QWS_H
diff --git a/src/gui/embedded/qmousevr41xx_qws.cpp b/src/gui/embedded/qmousevr41xx_qws.cpp
new file mode 100644
index 0000000000..b4828bf9ba
--- /dev/null
+++ b/src/gui/embedded/qmousevr41xx_qws.cpp
@@ -0,0 +1,250 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qmousevr41xx_qws.h"
+
+#ifndef QT_NO_QWS_MOUSE_VR41XX
+#include "qwindowsystem_qws.h"
+#include "qsocketnotifier.h"
+#include "qtimer.h"
+#include "qapplication.h"
+#include "qscreen_qws.h"
+#include <qstringlist.h>
+#include <qvarlengtharray.h>
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <termios.h>
+
+QT_BEGIN_NAMESPACE
+
+static const int defaultFilterSize = 3;
+
+class QWSVr41xxMouseHandlerPrivate : public QObject
+{
+ Q_OBJECT
+public:
+ QWSVr41xxMouseHandlerPrivate(QWSVr41xxMouseHandler *, const QString &, const QString &);
+ ~QWSVr41xxMouseHandlerPrivate();
+
+ void resume();
+ void suspend();
+
+private slots:
+ void sendRelease();
+ void readMouseData();
+
+private:
+ bool getSample();
+ ushort currSample[6];
+ uint currLength;
+
+ int mouseFD;
+ int mouseIdx;
+ QTimer *rtimer;
+ QSocketNotifier *mouseNotifier;
+ QWSVr41xxMouseHandler *handler;
+ QPoint lastPos;
+ bool isPressed;
+ int filterSize;
+ int pressLimit;
+};
+
+QWSVr41xxMouseHandler::QWSVr41xxMouseHandler(const QString &drv, const QString &dev)
+ : QWSCalibratedMouseHandler(drv, dev)
+{
+ d = new QWSVr41xxMouseHandlerPrivate(this, drv, dev);
+}
+
+QWSVr41xxMouseHandler::~QWSVr41xxMouseHandler()
+{
+ delete d;
+}
+
+void QWSVr41xxMouseHandler::resume()
+{
+ d->resume();
+}
+
+void QWSVr41xxMouseHandler::suspend()
+{
+ d->suspend();
+}
+
+QWSVr41xxMouseHandlerPrivate::QWSVr41xxMouseHandlerPrivate(QWSVr41xxMouseHandler *h, const QString &, const QString &device)
+ : currLength(0), handler(h)
+{
+ QStringList options = device.split(QLatin1String(":"));
+ int index = -1;
+
+ filterSize = defaultFilterSize;
+ QRegExp filterRegExp(QLatin1String("filter=(\\d+)"));
+ index = options.indexOf(filterRegExp);
+ if (index != -1) {
+ filterSize = qMax(1, filterRegExp.cap(1).toInt());
+ options.removeAt(index);
+ }
+ handler->setFilterSize(filterSize);
+
+ pressLimit = 750;
+ QRegExp pressRegExp(QLatin1String("press=(\\d+)"));
+ index = options.indexOf(pressRegExp);
+ if (index != -1) {
+ pressLimit = filterRegExp.cap(1).toInt();
+ options.removeAt(index);
+ }
+
+ QString dev;
+ if (options.isEmpty())
+ dev = QLatin1String("/dev/vrtpanel");
+ else
+ dev = options.first();
+
+ if ((mouseFD = open(dev.toLocal8Bit().constData(), O_RDONLY)) < 0) {
+ qWarning("Cannot open %s (%s)", qPrintable(dev), strerror(errno));
+ return;
+ }
+ sleep(1);
+
+ if (fcntl(mouseFD, F_SETFL, O_NONBLOCK) < 0) {
+ qWarning("Error initializing touch panel.");
+ return;
+ }
+
+ mouseNotifier = new QSocketNotifier(mouseFD, QSocketNotifier::Read, this);
+ connect(mouseNotifier, SIGNAL(activated(int)),this, SLOT(readMouseData()));
+
+ rtimer = new QTimer(this);
+ rtimer->setSingleShot(true);
+ connect(rtimer, SIGNAL(timeout()), this, SLOT(sendRelease()));
+ mouseIdx = 0;
+}
+
+QWSVr41xxMouseHandlerPrivate::~QWSVr41xxMouseHandlerPrivate()
+{
+ if (mouseFD >= 0)
+ close(mouseFD);
+}
+
+void QWSVr41xxMouseHandlerPrivate::suspend()
+{
+ mouseNotifier->setEnabled(false);
+}
+
+
+void QWSVr41xxMouseHandlerPrivate::resume()
+{
+ mouseIdx = 0;
+ mouseNotifier->setEnabled(true);
+}
+
+void QWSVr41xxMouseHandlerPrivate::sendRelease()
+{
+ handler->sendFiltered(lastPos, Qt::NoButton);
+ isPressed = false;
+}
+
+bool QWSVr41xxMouseHandlerPrivate::getSample()
+{
+ const int n = read(mouseFD,
+ reinterpret_cast<uchar*>(currSample) + currLength,
+ sizeof(currSample) - currLength);
+
+ if (n > 0)
+ currLength += n;
+
+ if (currLength < sizeof(currSample))
+ return false;
+
+ currLength = 0;
+ return true;
+}
+
+void QWSVr41xxMouseHandlerPrivate::readMouseData()
+{
+ const int sampleLength = sizeof(currSample) / sizeof(ushort);
+ QVarLengthArray<ushort, sampleLength * defaultFilterSize> samples(sampleLength * filterSize);
+
+ // Only return last 'filterSize' samples
+ int head = 0;
+ int tail = 0;
+ int nSamples = 0;
+ while (getSample()) {
+ if (!(currSample[0] & 0x8000) || (currSample[5] < pressLimit))
+ continue;
+
+ ushort *data = samples.data() + head * sampleLength;
+ memcpy(data, currSample, sizeof(currSample));
+ ++nSamples;
+ head = (head + 1) % filterSize;
+ if (nSamples >= filterSize)
+ tail = (tail + 1) % filterSize;
+ }
+
+ if (nSamples == 0)
+ return;
+
+ // send mouse events
+ while (tail != head || filterSize == 1) {
+ const ushort *data = samples.data() + tail * sampleLength;
+ lastPos = QPoint(data[3] - data[4], data[2] - data[1]);
+ handler->sendFiltered(lastPos, Qt::LeftButton);
+ isPressed = true;
+ tail = (tail + 1) % filterSize;
+ if (filterSize == 1)
+ break;
+ }
+
+ if (isPressed)
+ rtimer->start(50); // release unreliable
+}
+
+QT_END_NAMESPACE
+
+#include "qmousevr41xx_qws.moc"
+
+#endif //QT_NO_QWS_MOUSE_VR41
diff --git a/src/gui/embedded/qmousevr41xx_qws.h b/src/gui/embedded/qmousevr41xx_qws.h
new file mode 100644
index 0000000000..ad21013903
--- /dev/null
+++ b/src/gui/embedded/qmousevr41xx_qws.h
@@ -0,0 +1,80 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QMOUSEVR41XX_QWS_H
+#define QMOUSEVR41XX_QWS_H
+
+#include <QtGui/qmouse_qws.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+#ifndef QT_NO_QWS_MOUSE_VR41XX
+
+class QWSVr41xxMouseHandlerPrivate;
+
+class QWSVr41xxMouseHandler : public QWSCalibratedMouseHandler
+{
+public:
+ explicit QWSVr41xxMouseHandler(const QString & = QString(),
+ const QString & = QString());
+ ~QWSVr41xxMouseHandler();
+
+ void resume();
+ void suspend();
+
+protected:
+ QWSVr41xxMouseHandlerPrivate *d;
+
+private:
+ friend class QWSVr41xxMouseHandlerPrivate;
+};
+
+#endif // QT_NO_QWS_MOUSE_VR41XX
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QMOUSEVR41XX_QWS_H
diff --git a/src/gui/embedded/qmouseyopy_qws.cpp b/src/gui/embedded/qmouseyopy_qws.cpp
new file mode 100644
index 0000000000..fcf5193f25
--- /dev/null
+++ b/src/gui/embedded/qmouseyopy_qws.cpp
@@ -0,0 +1,184 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qmouseyopy_qws.h"
+
+#ifndef QT_NO_QWS_MOUSE_YOPY
+#include "qwindowsystem_qws.h"
+#include "qsocketnotifier.h"
+#include "qapplication.h"
+#include "qscreen_qws.h"
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <termios.h>
+
+QT_BEGIN_NAMESPACE
+
+class QWSYopyMouseHandlerPrivate : public QObject
+{
+ Q_OBJECT
+public:
+ QWSYopyMouseHandlerPrivate(QWSYopyMouseHandler *h);
+ ~QWSYopyMouseHandlerPrivate();
+
+ void suspend();
+ void resume();
+
+private slots:
+ void readMouseData();
+
+private:
+ int mouseFD;
+ int prevstate;
+ QSocketNotifier *mouseNotifier;
+ QWSYopyMouseHandler *handler;
+};
+
+QWSYopyMouseHandler::QWSYopyMouseHandler(const QString &driver, const QString &device)
+ : QWSMouseHandler(driver, device)
+{
+ d = new QWSYopyMouseHandlerPrivate(this);
+}
+
+QWSYopyMouseHandler::~QWSYopyMouseHandler()
+{
+ delete d;
+}
+
+void QWSYopyMouseHandler::resume()
+{
+ d->resume();
+}
+
+void QWSYopyMouseHandler::suspend()
+{
+ d->suspend();
+}
+
+QWSYopyMouseHandlerPrivate::QWSYopyMouseHandlerPrivate(QWSYopyMouseHandler *h)
+ : handler(h)
+{
+ if ((mouseFD = open("/dev/ts", O_RDONLY)) < 0) {
+ qWarning("Cannot open /dev/ts (%s)", strerror(errno));
+ return;
+ } else {
+ sleep(1);
+ }
+ prevstate=0;
+ mouseNotifier = new QSocketNotifier(mouseFD, QSocketNotifier::Read,
+ this);
+ connect(mouseNotifier, SIGNAL(activated(int)),this, SLOT(readMouseData()));
+}
+
+QWSYopyMouseHandlerPrivate::~QWSYopyMouseHandlerPrivate()
+{
+ if (mouseFD >= 0)
+ close(mouseFD);
+}
+
+#define YOPY_XPOS(d) (d[1]&0x3FF)
+#define YOPY_YPOS(d) (d[2]&0x3FF)
+#define YOPY_PRES(d) (d[0]&0xFF)
+#define YOPY_STAT(d) (d[3]&0x01)
+
+struct YopyTPdata {
+
+ unsigned char status;
+ unsigned short xpos;
+ unsigned short ypos;
+
+};
+
+void QWSYopyMouseHandlerPrivate::suspend()
+{
+ mouseNotifier->setEnabled(false);
+}
+
+
+void QWSYopyMouseHandlerPrivate::resume()
+{
+ prevstate = 0;
+ mouseNotifier->setEnabled(true);
+}
+
+void QWSYopyMouseHandlerPrivate::readMouseData()
+{
+ if(!qt_screen)
+ return;
+ YopyTPdata data;
+
+ unsigned int yopDat[4];
+
+ int ret;
+
+ ret=read(mouseFD,&yopDat,sizeof(yopDat));
+
+ if(ret) {
+ data.status= (YOPY_PRES(yopDat)) ? 1 : 0;
+ data.xpos=YOPY_XPOS(yopDat);
+ data.ypos=YOPY_YPOS(yopDat);
+ QPoint q;
+ q.setX(data.xpos);
+ q.setY(data.ypos);
+ if (data.status && !prevstate) {
+ handler->mouseChanged(q,Qt::LeftButton);
+ } else if(!data.status && prevstate) {
+ handler->mouseChanged(q,0);
+ }
+ prevstate = data.status;
+ }
+ if(ret<0) {
+ qDebug("Error %s",strerror(errno));
+ }
+}
+
+QT_END_NAMESPACE
+
+#include "qmouseyopy_qws.moc"
+
+#endif //QT_NO_QWS_MOUSE_YOPY
diff --git a/src/gui/embedded/qmouseyopy_qws.h b/src/gui/embedded/qmouseyopy_qws.h
new file mode 100644
index 0000000000..0209f104f6
--- /dev/null
+++ b/src/gui/embedded/qmouseyopy_qws.h
@@ -0,0 +1,80 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QMOUSEYOPY_QWS_H
+#define QMOUSEYOPY_QWS_H
+
+#include <QtGui/qmouse_qws.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+#ifndef QT_NO_QWS_MOUSE_YOPY
+
+// YOPY touch panel support based on changes contributed by Ron Victorelli
+// (victorrj at icubed.com) to Custom TP driver.
+
+class QWSYopyMouseHandlerPrivate;
+
+class QWSYopyMouseHandler : public QWSMouseHandler
+{
+public:
+ explicit QWSYopyMouseHandler(const QString & = QString(),
+ const QString & = QString());
+ ~QWSYopyMouseHandler();
+
+ void resume();
+ void suspend();
+
+protected:
+ QWSYopyMouseHandlerPrivate *d;
+};
+
+#endif // QT_NO_QWS_MOUSE_YOPY
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QMOUSEYOPY_QWS_H
diff --git a/src/gui/embedded/qscreen_qws.cpp b/src/gui/embedded/qscreen_qws.cpp
new file mode 100644
index 0000000000..6741f2cedc
--- /dev/null
+++ b/src/gui/embedded/qscreen_qws.cpp
@@ -0,0 +1,3317 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qscreen_qws.h"
+
+#include "qcolormap.h"
+#include "qscreendriverfactory_qws.h"
+#include "qwindowsystem_qws.h"
+#include "qwidget.h"
+#include "qcolor.h"
+#include "qpixmap.h"
+#include "qvarlengtharray.h"
+#include "qwsdisplay_qws.h"
+#include <private/qdrawhelper_p.h>
+#include <private/qpaintengine_raster_p.h>
+#include <private/qpixmap_raster_p.h>
+#include <private/qwindowsurface_qws_p.h>
+#include <private/qpainter_p.h>
+#include <private/qwidget_p.h>
+#include <private/qgraphicssystem_qws_p.h>
+
+QT_BEGIN_NAMESPACE
+
+// #define QT_USE_MEMCPY_DUFF
+
+#ifndef QT_NO_QWS_CURSOR
+bool qt_sw_cursor=false;
+Q_GUI_EXPORT QScreenCursor * qt_screencursor = 0;
+#endif
+Q_GUI_EXPORT QScreen * qt_screen = 0;
+
+ClearCacheFunc QScreen::clearCacheFunc = 0;
+
+#ifndef QT_NO_QWS_CURSOR
+/*!
+ \class QScreenCursor
+ \ingroup qws
+
+ \brief The QScreenCursor class is a base class for screen cursors
+ in Qt for Embedded Linux.
+
+ Note that this class is non-portable, and that it is only
+ available in \l{Qt for Embedded Linux}.
+
+ QScreenCursor implements a software cursor, but can be subclassed
+ to support hardware cursors as well. When deriving from the
+ QScreenCursor class it is important to maintain the cursor's
+ image, position, hot spot (the point within the cursor's image
+ that will be the position of the associated mouse events) and
+ visibility as well as informing whether it is hardware accelerated
+ or not.
+
+ Note that there may only be one screen cursor at a time. Use the
+ static instance() function to retrieve a pointer to the current
+ screen cursor. Typically, the cursor is constructed by the QScreen
+ class or one of its descendants when it is initializing the
+ device; the QScreenCursor class should never be instantiated
+ explicitly.
+
+ Use the move() function to change the position of the cursor, and
+ the set() function to alter its image or its hot spot. In
+ addition, you can find out whether the cursor is accelerated or
+ not, using the isAccelerated() function, and the boundingRect()
+ function returns the cursor's bounding rectangle.
+
+ The cursor's appearance can be controlled using the isVisible(),
+ hide() and show() functions; alternatively the QWSServer class
+ provides some means of controlling the cursor's appearance using
+ the QWSServer::isCursorVisible() and QWSServer::setCursorVisible()
+ functions.
+
+ \sa QScreen, QWSServer
+*/
+
+/*!
+ \fn static QScreenCursor* QScreenCursor::instance()
+ \since 4.2
+
+ Returns a pointer to the application's unique screen cursor.
+*/
+
+extern bool qws_sw_cursor;
+
+/*!
+ Constructs a screen cursor
+*/
+QScreenCursor::QScreenCursor()
+{
+ pos = QPoint(qt_screen->deviceWidth()/2, qt_screen->deviceHeight()/2);
+ size = QSize(0,0);
+ enable = true;
+ hwaccel = false;
+ supportsAlpha = true;
+}
+
+/*!
+ Destroys the screen cursor.
+*/
+QScreenCursor::~QScreenCursor()
+{
+}
+
+/*!
+ Hides the cursor from the screen.
+
+ \sa show()
+*/
+void QScreenCursor::hide()
+{
+ if (enable) {
+ enable = false;
+ if (!hwaccel)
+ qt_screen->exposeRegion(boundingRect(), 0);
+ }
+}
+
+/*!
+ Shows the mouse cursor.
+
+ \sa hide()
+*/
+void QScreenCursor::show()
+{
+ if (!enable) {
+ enable = true;
+ if (!hwaccel)
+ qt_screen->exposeRegion(boundingRect(), 0);
+ }
+}
+
+/*!
+ Sets the cursor's image to be the given \a image.
+
+ The \a hotx and \a hoty parameters define the cursor's hot spot,
+ i.e., the point within the cursor's image that will be the
+ position of the associated mouse events.
+
+ \sa move()
+*/
+void QScreenCursor::set(const QImage &image, int hotx, int hoty)
+{
+ const QRect r = boundingRect();
+
+ hotspot = QPoint(hotx, hoty);
+ cursor = image;
+ size = image.size();
+
+ if (enable && !hwaccel)
+ qt_screen->exposeRegion(r | boundingRect(), 0);
+}
+
+/*!
+ Moves the mouse cursor to the given position, i.e., (\a x, \a y).
+
+ Note that the given position defines the top-left corner of the
+ cursor's image, i.e., not the cursor's hot spot (the position of
+ the associated mouse events).
+
+ \sa set()
+*/
+void QScreenCursor::move(int x, int y)
+{
+ const QRegion r = boundingRect();
+ pos = QPoint(x,y);
+ if (enable && !hwaccel)
+ qt_screen->exposeRegion(r | boundingRect(), 0);
+}
+
+
+/*!
+ \fn void QScreenCursor::initSoftwareCursor ()
+
+ Initializes the screen cursor.
+
+ This function is typically called from the screen driver when
+ initializing the device. Alternatively, the cursor can be set
+ directly using the pointer returned by the static instance()
+ function.
+
+ \sa QScreen::initDevice()
+*/
+void QScreenCursor::initSoftwareCursor()
+{
+ qt_screencursor = new QScreenCursor;
+}
+
+
+#endif // QT_NO_QWS_CURSOR
+
+
+/*!
+ \fn QRect QScreenCursor::boundingRect () const
+
+ Returns the cursor's bounding rectangle.
+*/
+
+/*!
+ \internal
+ \fn bool QScreenCursor::enabled ()
+*/
+
+/*!
+ \fn QImage QScreenCursor::image () const
+
+ Returns the cursor's image.
+*/
+
+
+/*!
+ \fn bool QScreenCursor::isAccelerated () const
+
+ Returns true if the cursor is accelerated; otherwise false.
+*/
+
+/*!
+ \fn bool QScreenCursor::isVisible () const
+
+ Returns true if the cursor is visible; otherwise false.
+*/
+
+/*!
+ \internal
+ \fn bool QScreenCursor::supportsAlphaCursor () const
+*/
+
+/*
+ \variable QScreenCursor::cursor
+
+ \brief the cursor's image.
+
+ \sa image()
+*/
+
+/*
+ \variable QScreenCursor::size
+
+ \brief the cursor's size
+*/
+
+/*
+ \variable QScreenCursor::pos
+
+ \brief the cursor's position, i.e., the position of the top-left
+ corner of the crsor's image
+
+ \sa set(), move()
+*/
+
+/*
+ \variable QScreenCursor::hotspot
+
+ \brief the cursor's hotspot, i.e., the point within the cursor's
+ image that will be the position of the associated mouse events.
+
+ \sa set(), move()
+*/
+
+/*
+ \variable QScreenCursor::enable
+
+ \brief whether the cursor is visible or not
+
+ \sa isVisible()
+*/
+
+/*
+ \variable QScreenCursor::hwaccel
+
+ \brief holds whether the cursor is accelerated or not
+
+ If the cursor is not accelerated, its image will be included by
+ the screen when it composites the window surfaces.
+
+ \sa isAccelerated()
+
+*/
+
+/*
+ \variable QScreenCursor::supportsAlpha
+*/
+
+/*!
+ \internal
+ \macro qt_screencursor
+ \relates QScreenCursor
+
+ A global pointer referring to the unique screen cursor. It is
+ equivalent to the pointer returned by the
+ QScreenCursor::instance() function.
+*/
+
+
+
+class QScreenPrivate
+{
+public:
+ QScreenPrivate(QScreen *parent, QScreen::ClassId id = QScreen::CustomClass);
+ ~QScreenPrivate();
+
+ inline QImage::Format preferredImageFormat() const;
+
+ typedef void (*SolidFillFunc)(QScreen*, const QColor&, const QRegion&);
+ typedef void (*BlitFunc)(QScreen*, const QImage&, const QPoint&, const QRegion&);
+
+ SolidFillFunc solidFill;
+ BlitFunc blit;
+
+ QPoint offset;
+ QList<QScreen*> subScreens;
+ QPixmapDataFactory* pixmapFactory;
+ QGraphicsSystem* graphicsSystem;
+ QWSGraphicsSystem defaultGraphicsSystem; //###
+ QImage::Format pixelFormat;
+#if Q_BYTE_ORDER == Q_BIG_ENDIAN
+ bool fb_is_littleEndian;
+#endif
+#ifdef QT_QWS_CLIENTBLIT
+ bool supportsBlitInClients;
+#endif
+ int classId;
+ QScreen *q_ptr;
+};
+
+template <typename T>
+static void solidFill_template(QScreen *screen, const QColor &color,
+ const QRegion &region)
+{
+ T *dest = reinterpret_cast<T*>(screen->base());
+ const T c = qt_colorConvert<T, quint32>(color.rgba(), 0);
+ const int stride = screen->linestep();
+ const QVector<QRect> rects = region.rects();
+
+ for (int i = 0; i < rects.size(); ++i) {
+ const QRect r = rects.at(i);
+ qt_rectfill(dest, c, r.x(), r.y(), r.width(), r.height(), stride);
+ }
+}
+
+#ifdef QT_QWS_DEPTH_GENERIC
+static void solidFill_rgb_32bpp(QScreen *screen, const QColor &color,
+ const QRegion &region)
+{
+ quint32 *dest = reinterpret_cast<quint32*>(screen->base());
+ const quint32 c = qt_convertToRgb<quint32>(color.rgba());
+
+ const int stride = screen->linestep();
+ const QVector<QRect> rects = region.rects();
+
+ for (int i = 0; i < rects.size(); ++i) {
+ const QRect r = rects.at(i);
+ qt_rectfill(dest, c, r.x(), r.y(), r.width(), r.height(), stride);
+ }
+}
+
+static void solidFill_rgb_16bpp(QScreen *screen, const QColor &color,
+ const QRegion &region)
+{
+ quint16 *dest = reinterpret_cast<quint16*>(screen->base());
+ const quint16 c = qt_convertToRgb<quint32>(color.rgba());
+
+ const int stride = screen->linestep();
+ const QVector<QRect> rects = region.rects();
+
+ for (int i = 0; i < rects.size(); ++i) {
+ const QRect r = rects.at(i);
+ qt_rectfill(dest, c, r.x(), r.y(), r.width(), r.height(), stride);
+ }
+}
+#endif // QT_QWS_DEPTH_GENERIC
+
+#ifdef QT_QWS_DEPTH_4
+static inline void qt_rectfill_gray4(quint8 *dest, quint8 value,
+ int x, int y, int width, int height,
+ int stride)
+{
+ const int pixelsPerByte = 2;
+ dest += y * stride + x / pixelsPerByte;
+ const int doAlign = x & 1;
+ const int doTail = (width - doAlign) & 1;
+ const int width8 = (width - doAlign) / pixelsPerByte;
+
+ for (int j = 0; j < height; ++j) {
+ if (doAlign)
+ *dest = (*dest & 0xf0) | (value & 0x0f);
+ if (width8)
+ qt_memfill<quint8>(dest + doAlign, value, width8);
+ if (doTail) {
+ quint8 *d = dest + doAlign + width8;
+ *d = (*d & 0x0f) | (value & 0xf0);
+ }
+ dest += stride;
+ }
+}
+
+static void solidFill_gray4(QScreen *screen, const QColor &color,
+ const QRegion &region)
+{
+ quint8 *dest = reinterpret_cast<quint8*>(screen->base());
+ const quint8 c = qGray(color.rgba()) >> 4;
+ const quint8 c8 = (c << 4) | c;
+
+ const int stride = screen->linestep();
+ const QVector<QRect> rects = region.rects();
+
+ for (int i = 0; i < rects.size(); ++i) {
+ const QRect r = rects.at(i);
+ qt_rectfill_gray4(dest, c8, r.x(), r.y(), r.width(), r.height(),
+ stride);
+ }
+}
+#endif // QT_QWS_DEPTH_4
+
+#ifdef QT_QWS_DEPTH_1
+static inline void qt_rectfill_mono(quint8 *dest, quint8 value,
+ int x, int y, int width, int height,
+ int stride)
+{
+ const int pixelsPerByte = 8;
+ const int alignWidth = qMin(width, (8 - (x & 7)) & 7);
+ const int doAlign = (alignWidth > 0 ? 1 : 0);
+ const int alignStart = pixelsPerByte - 1 - (x & 7);
+ const int alignStop = alignStart - (alignWidth - 1);
+ const quint8 alignMask = ((1 << alignWidth) - 1) << alignStop;
+ const int tailWidth = (width - alignWidth) & 7;
+ const int doTail = (tailWidth > 0 ? 1 : 0);
+ const quint8 tailMask = (1 << (pixelsPerByte - tailWidth)) - 1;
+ const int width8 = (width - alignWidth) / pixelsPerByte;
+
+ dest += y * stride + x / pixelsPerByte;
+ stride -= (doAlign + width8);
+
+ for (int j = 0; j < height; ++j) {
+ if (doAlign) {
+ *dest = (*dest & ~alignMask) | (value & alignMask);
+ ++dest;
+ }
+ if (width8) {
+ qt_memfill<quint8>(dest, value, width8);
+ dest += width8;
+ }
+ if (doTail)
+ *dest = (*dest & tailMask) | (value & ~tailMask);
+ dest += stride;
+ }
+}
+
+static void solidFill_mono(QScreen *screen, const QColor &color,
+ const QRegion &region)
+{
+ quint8 *dest = reinterpret_cast<quint8*>(screen->base());
+ const quint8 c8 = (qGray(color.rgba()) >> 7) * 0xff;
+
+ const int stride = screen->linestep();
+ const QVector<QRect> rects = region.rects();
+
+ for (int i = 0; i < rects.size(); ++i) {
+ const QRect r = rects.at(i);
+ qt_rectfill_mono(dest, c8, r.x(), r.y(), r.width(), r.height(),
+ stride);
+ }
+}
+#endif // QT_QWS_DEPTH_1
+
+void qt_solidFill_setup(QScreen *screen, const QColor &color,
+ const QRegion &region)
+{
+ switch (screen->depth()) {
+#ifdef QT_QWS_DEPTH_32
+ case 32:
+ if (screen->pixelType() == QScreen::NormalPixel)
+ screen->d_ptr->solidFill = solidFill_template<quint32>;
+ else
+ screen->d_ptr->solidFill = solidFill_template<qabgr8888>;
+ break;
+#endif
+#ifdef QT_QWS_DEPTH_24
+ case 24:
+ if (screen->pixelType() == QScreen::NormalPixel)
+ screen->d_ptr->solidFill = solidFill_template<qrgb888>;
+ else
+ screen->d_ptr->solidFill = solidFill_template<quint24>;
+ break;
+#endif
+#ifdef QT_QWS_DEPTH_18
+ case 18:
+ screen->d_ptr->solidFill = solidFill_template<quint18>;
+ break;
+#endif
+#ifdef QT_QWS_DEPTH_16
+ case 16:
+ if (screen->pixelType() == QScreen::NormalPixel)
+ screen->d_ptr->solidFill = solidFill_template<quint16>;
+ else
+ screen->d_ptr->solidFill = solidFill_template<qbgr565>;
+ break;
+#endif
+#ifdef QT_QWS_DEPTH_15
+ case 15:
+ if (screen->pixelType() == QScreen::NormalPixel)
+ screen->d_ptr->solidFill = solidFill_template<qrgb555>;
+ else
+ screen->d_ptr->solidFill = solidFill_template<qbgr555>;
+ break;
+#endif
+#ifdef QT_QWS_DEPTH_12
+ case 12:
+ screen->d_ptr->solidFill = solidFill_template<qrgb444>;
+ break;
+#endif
+#ifdef QT_QWS_DEPTH_8
+ case 8:
+ screen->d_ptr->solidFill = solidFill_template<quint8>;
+ break;
+#endif
+#ifdef QT_QWS_DEPTH_4
+ case 4:
+ screen->d_ptr->solidFill = solidFill_gray4;
+ break;
+#endif
+#ifdef QT_QWS_DEPTH_1
+ case 1:
+ screen->d_ptr->solidFill = solidFill_mono;
+ break;
+#endif
+ default:
+ qFatal("solidFill_setup(): Screen depth %d not supported!",
+ screen->depth());
+ screen->d_ptr->solidFill = 0;
+ break;
+ }
+ screen->d_ptr->solidFill(screen, color, region);
+}
+
+template <typename DST, typename SRC>
+static void blit_template(QScreen *screen, const QImage &image,
+ const QPoint &topLeft, const QRegion &region)
+{
+ DST *dest = reinterpret_cast<DST*>(screen->base());
+ const int screenStride = screen->linestep();
+ const int imageStride = image.bytesPerLine();
+
+ if (region.numRects() == 1) {
+ const QRect r = region.boundingRect();
+ const SRC *src = reinterpret_cast<const SRC*>(image.scanLine(r.y()))
+ + r.x();
+ qt_rectconvert<DST, SRC>(dest, src,
+ r.x() + topLeft.x(), r.y() + topLeft.y(),
+ r.width(), r.height(),
+ screenStride, imageStride);
+ } else {
+ const QVector<QRect> rects = region.rects();
+
+ for (int i = 0; i < rects.size(); ++i) {
+ const QRect r = rects.at(i);
+ const SRC *src = reinterpret_cast<const SRC*>(image.scanLine(r.y()))
+ + r.x();
+ qt_rectconvert<DST, SRC>(dest, src,
+ r.x() + topLeft.x(), r.y() + topLeft.y(),
+ r.width(), r.height(),
+ screenStride, imageStride);
+ }
+ }
+}
+
+#ifdef QT_QWS_DEPTH_32
+static void blit_32(QScreen *screen, const QImage &image,
+ const QPoint &topLeft, const QRegion &region)
+{
+ switch (image.format()) {
+ case QImage::Format_RGB32:
+ case QImage::Format_ARGB32:
+ case QImage::Format_ARGB32_Premultiplied:
+ blit_template<quint32, quint32>(screen, image, topLeft, region);
+ return;
+#ifdef QT_QWS_DEPTH_16
+ case QImage::Format_RGB16:
+ blit_template<quint32, quint16>(screen, image, topLeft, region);
+ return;
+#endif
+ default:
+ qCritical("blit_32(): Image format %d not supported!", image.format());
+ }
+}
+#endif // QT_QWS_DEPTH_32
+
+#ifdef QT_QWS_DEPTH_24
+static void blit_24(QScreen *screen, const QImage &image,
+ const QPoint &topLeft, const QRegion &region)
+{
+ switch (image.format()) {
+ case QImage::Format_RGB32:
+ case QImage::Format_ARGB32:
+ case QImage::Format_ARGB32_Premultiplied:
+ blit_template<quint24, quint32>(screen, image, topLeft, region);
+ return;
+ case QImage::Format_RGB888:
+ blit_template<quint24, qrgb888>(screen, image, topLeft, region);
+ return;
+#ifdef QT_QWS_DEPTH_16
+ case QImage::Format_RGB16:
+ blit_template<quint24, quint16>(screen, image, topLeft, region);
+ return;
+#endif
+ default:
+ qCritical("blit_24(): Image format %d not supported!", image.format());
+ }
+}
+
+static void blit_qrgb888(QScreen *screen, const QImage &image,
+ const QPoint &topLeft, const QRegion &region)
+{
+ switch (image.format()) {
+ case QImage::Format_RGB32:
+ case QImage::Format_ARGB32:
+ case QImage::Format_ARGB32_Premultiplied:
+ blit_template<qrgb888, quint32>(screen, image, topLeft, region);
+ return;
+ case QImage::Format_RGB888:
+ blit_template<qrgb888, qrgb888>(screen, image, topLeft, region);
+ return;
+#ifdef QT_QWS_DEPTH_16
+ case QImage::Format_RGB16:
+ blit_template<qrgb888, quint16>(screen, image, topLeft, region);
+ return;
+#endif
+ default:
+ qCritical("blit_24(): Image format %d not supported!", image.format());
+ break;
+ }
+}
+#endif // QT_QWS_DEPTH_24
+
+#ifdef QT_QWS_DEPTH_18
+static void blit_18(QScreen *screen, const QImage &image,
+ const QPoint &topLeft, const QRegion &region)
+{
+ switch (image.format()) {
+ case QImage::Format_RGB32:
+ case QImage::Format_ARGB32:
+ case QImage::Format_ARGB32_Premultiplied:
+ blit_template<qrgb666, quint32>(screen, image, topLeft, region);
+ return;
+ case QImage::Format_RGB666:
+ blit_template<qrgb666, qrgb666>(screen, image, topLeft, region);
+ return;
+#ifdef QT_QWS_DEPTH_16
+ case QImage::Format_RGB16:
+ blit_template<qrgb666, quint16>(screen, image, topLeft, region);
+ return;
+#endif
+ default:
+ qCritical("blit_18(): Image format %d not supported!", image.format());
+ }
+}
+#endif // QT_QWS_DEPTH_18
+
+#if (Q_BYTE_ORDER == Q_BIG_ENDIAN) && (defined(QT_QWS_DEPTH_16) || defined(QT_QWS_DEPTH_15))
+class quint16LE
+{
+public:
+ inline quint16LE(quint32 v) {
+ data = ((v & 0xff00) >> 8) | ((v & 0x00ff) << 8);
+ }
+
+ inline quint16LE(int v) {
+ data = ((v & 0xff00) >> 8) | ((v & 0x00ff) << 8);
+ }
+
+ inline quint16LE(quint16 v) {
+ data = ((v & 0xff00) >> 8) | ((v & 0x00ff) << 8);
+ }
+
+ inline quint16LE(qrgb555 v) {
+ data = (( (quint16)v & 0xff00) >> 8) |
+ (( (quint16)v & 0x00ff) << 8);
+ }
+
+ inline bool operator==(const quint16LE &v) const
+ {
+ return data == v.data;
+ }
+
+private:
+ quint16 data;
+};
+#endif
+
+#ifdef QT_QWS_DEPTH_16
+static void blit_16(QScreen *screen, const QImage &image,
+ const QPoint &topLeft, const QRegion &region)
+{
+ switch (image.format()) {
+ case QImage::Format_RGB32:
+ case QImage::Format_ARGB32:
+ case QImage::Format_ARGB32_Premultiplied:
+ // ### This probably doesn't work but it's a case which should never happen
+ blit_template<quint16, quint32>(screen, image, topLeft, region);
+ return;
+ case QImage::Format_RGB16:
+ blit_template<quint16, quint16>(screen, image, topLeft, region);
+ return;
+ default:
+ qCritical("blit_16(): Image format %d not supported!", image.format());
+ }
+}
+
+#if Q_BYTE_ORDER == Q_BIG_ENDIAN
+static void blit_16_bigToLittleEndian(QScreen *screen, const QImage &image,
+ const QPoint &topLeft,
+ const QRegion &region)
+{
+ switch (image.format()) {
+ case QImage::Format_RGB32:
+ case QImage::Format_ARGB32:
+ case QImage::Format_ARGB32_Premultiplied:
+ blit_template<quint16LE, quint32>(screen, image, topLeft, region);
+ return;
+ case QImage::Format_RGB16:
+ blit_template<quint16LE, quint16>(screen, image, topLeft, region);
+ return;
+ default:
+ qCritical("blit_16_bigToLittleEndian(): Image format %d not supported!", image.format());
+ }
+}
+
+#endif // Q_BIG_ENDIAN
+#endif // QT_QWS_DEPTH_16
+
+#ifdef QT_QWS_DEPTH_15
+static void blit_15(QScreen *screen, const QImage &image,
+ const QPoint &topLeft, const QRegion &region)
+{
+ switch (image.format()) {
+ case QImage::Format_RGB32:
+ case QImage::Format_ARGB32:
+ case QImage::Format_ARGB32_Premultiplied:
+ blit_template<qrgb555, quint32>(screen, image, topLeft, region);
+ return;
+ case QImage::Format_RGB555:
+ blit_template<qrgb555, qrgb555>(screen, image, topLeft, region);
+ return;
+ case QImage::Format_RGB16:
+ blit_template<qrgb555, quint16>(screen, image, topLeft, region);
+ return;
+ default:
+ qCritical("blit_15(): Image format %d not supported!", image.format());
+ }
+}
+
+#if Q_BYTE_ORDER == Q_BIG_ENDIAN
+static void blit_15_bigToLittleEndian(QScreen *screen, const QImage &image,
+ const QPoint &topLeft,
+ const QRegion &region)
+{
+ switch (image.format()) {
+ case QImage::Format_RGB555:
+ blit_template<quint16LE, qrgb555>(screen, image, topLeft, region);
+ return;
+ default:
+ qCritical("blit_15_bigToLittleEndian(): Image format %d not supported!", image.format());
+ }
+}
+#endif // Q_BIG_ENDIAN
+#endif // QT_QWS_DEPTH_15
+
+
+#ifdef QT_QWS_DEPTH_12
+static void blit_12(QScreen *screen, const QImage &image,
+ const QPoint &topLeft, const QRegion &region)
+{
+ switch (image.format()) {
+ case QImage::Format_ARGB4444_Premultiplied:
+ blit_template<qrgb444, qargb4444>(screen, image, topLeft, region);
+ return;
+ case QImage::Format_RGB444:
+ blit_template<qrgb444, qrgb444>(screen, image, topLeft, region);
+ return;
+ default:
+ qCritical("blit_12(): Image format %d not supported!", image.format());
+ }
+}
+#endif // QT_QWS_DEPTH_12
+
+#ifdef QT_QWS_DEPTH_8
+static void blit_8(QScreen *screen, const QImage &image,
+ const QPoint &topLeft, const QRegion &region)
+{
+ switch (image.format()) {
+ case QImage::Format_RGB32:
+ case QImage::Format_ARGB32:
+ case QImage::Format_ARGB32_Premultiplied:
+ blit_template<quint8, quint32>(screen, image, topLeft, region);
+ return;
+ case QImage::Format_RGB16:
+ blit_template<quint8, quint16>(screen, image, topLeft, region);
+ return;
+ case QImage::Format_ARGB4444_Premultiplied:
+ blit_template<quint8, qargb4444>(screen, image, topLeft, region);
+ return;
+ case QImage::Format_RGB444:
+ blit_template<quint8, qrgb444>(screen, image, topLeft, region);
+ return;
+ default:
+ qCritical("blit_8(): Image format %d not supported!", image.format());
+ }
+}
+#endif // QT_QWS_DEPTH_8
+
+#ifdef QT_QWS_DEPTH_4
+
+struct qgray4 { quint8 dummy; } Q_PACKED;
+
+template <typename SRC>
+Q_STATIC_TEMPLATE_FUNCTION inline quint8 qt_convertToGray4(SRC color);
+
+template <>
+inline quint8 qt_convertToGray4(quint32 color)
+{
+ return qGray(color) >> 4;
+}
+
+template <>
+inline quint8 qt_convertToGray4(quint16 color)
+{
+ const int r = (color & 0xf800) >> 11;
+ const int g = (color & 0x07e0) >> 6; // only keep 5 bit
+ const int b = (color & 0x001f);
+ return (r * 11 + g * 16 + b * 5) >> 6;
+}
+
+template <>
+inline quint8 qt_convertToGray4(qrgb444 color)
+{
+ return qt_convertToGray4(quint32(color));
+}
+
+template <>
+inline quint8 qt_convertToGray4(qargb4444 color)
+{
+ return qt_convertToGray4(quint32(color));
+}
+
+template <typename SRC>
+Q_STATIC_TEMPLATE_FUNCTION inline void qt_rectconvert_gray4(qgray4 *dest4, const SRC *src,
+ int x, int y, int width, int height,
+ int dstStride, int srcStride)
+{
+ const int pixelsPerByte = 2;
+ quint8 *dest8 = reinterpret_cast<quint8*>(dest4)
+ + y * dstStride + x / pixelsPerByte;
+ const int doAlign = x & 1;
+ const int doTail = (width - doAlign) & 1;
+ const int width8 = (width - doAlign) / pixelsPerByte;
+ const int count8 = (width8 + 3) / 4;
+
+ srcStride = srcStride / sizeof(SRC) - width;
+ dstStride -= (width8 + doAlign);
+
+ for (int i = 0; i < height; ++i) {
+ if (doAlign) {
+ *dest8 = (*dest8 & 0xf0) | qt_convertToGray4<SRC>(*src++);
+ ++dest8;
+ }
+ if (count8) {
+ int n = count8;
+ switch (width8 & 0x03) // duff's device
+ {
+ case 0: do { *dest8++ = qt_convertToGray4<SRC>(src[0]) << 4
+ | qt_convertToGray4<SRC>(src[1]);
+ src += 2;
+ case 3: *dest8++ = qt_convertToGray4<SRC>(src[0]) << 4
+ | qt_convertToGray4<SRC>(src[1]);
+ src += 2;
+ case 2: *dest8++ = qt_convertToGray4<SRC>(src[0]) << 4
+ | qt_convertToGray4<SRC>(src[1]);
+ src += 2;
+ case 1: *dest8++ = qt_convertToGray4<SRC>(src[0]) << 4
+ | qt_convertToGray4<SRC>(src[1]);
+ src += 2;
+ } while (--n > 0);
+ }
+ }
+
+ if (doTail)
+ *dest8 = qt_convertToGray4<SRC>(*src++) << 4 | (*dest8 & 0x0f);
+
+ dest8 += dstStride;
+ src += srcStride;
+ }
+}
+
+template <>
+void qt_rectconvert(qgray4 *dest, const quint32 *src,
+ int x, int y, int width, int height,
+ int dstStride, int srcStride)
+{
+ qt_rectconvert_gray4<quint32>(dest, src, x, y, width, height,
+ dstStride, srcStride);
+}
+
+template <>
+void qt_rectconvert(qgray4 *dest, const quint16 *src,
+ int x, int y, int width, int height,
+ int dstStride, int srcStride)
+{
+ qt_rectconvert_gray4<quint16>(dest, src, x, y, width, height,
+ dstStride, srcStride);
+}
+
+template <>
+void qt_rectconvert(qgray4 *dest, const qrgb444 *src,
+ int x, int y, int width, int height,
+ int dstStride, int srcStride)
+{
+ qt_rectconvert_gray4<qrgb444>(dest, src, x, y, width, height,
+ dstStride, srcStride);
+}
+
+template <>
+void qt_rectconvert(qgray4 *dest, const qargb4444 *src,
+ int x, int y, int width, int height,
+ int dstStride, int srcStride)
+{
+ qt_rectconvert_gray4<qargb4444>(dest, src, x, y, width, height,
+ dstStride, srcStride);
+}
+
+static void blit_4(QScreen *screen, const QImage &image,
+ const QPoint &topLeft, const QRegion &region)
+{
+ switch (image.format()) {
+ case QImage::Format_ARGB32_Premultiplied:
+ blit_template<qgray4, quint32>(screen, image, topLeft, region);
+ return;
+ case QImage::Format_RGB16:
+ blit_template<qgray4, quint16>(screen, image, topLeft, region);
+ return;
+ case QImage::Format_RGB444:
+ blit_template<qgray4, qrgb444>(screen, image, topLeft, region);
+ return;
+ case QImage::Format_ARGB4444_Premultiplied:
+ blit_template<qgray4, qargb4444>(screen, image, topLeft, region);
+ return;
+ default:
+ qCritical("blit_4(): Image format %d not supported!", image.format());
+ }
+}
+#endif // QT_QWS_DEPTH_4
+
+#ifdef QT_QWS_DEPTH_1
+
+struct qmono { quint8 dummy; } Q_PACKED;
+
+template <typename SRC>
+Q_STATIC_TEMPLATE_FUNCTION inline quint8 qt_convertToMono(SRC color);
+
+template <>
+inline quint8 qt_convertToMono(quint32 color)
+{
+ return qGray(color) >> 7;
+}
+
+template <>
+inline quint8 qt_convertToMono(quint16 color)
+{
+ return (qGray(qt_colorConvert<quint32, quint16>(color, 0)) >> 7);
+}
+
+template <>
+inline quint8 qt_convertToMono(qargb4444 color)
+{
+ return (qGray(quint32(color)) >> 7);
+}
+
+template <>
+inline quint8 qt_convertToMono(qrgb444 color)
+{
+ return (qGray(quint32(color)) >> 7);
+}
+
+template <typename SRC>
+inline void qt_rectconvert_mono(qmono *dest, const SRC *src,
+ int x, int y, int width, int height,
+ int dstStride, int srcStride)
+{
+ const int pixelsPerByte = 8;
+ quint8 *dest8 = reinterpret_cast<quint8*>(dest)
+ + y * dstStride + x / pixelsPerByte;
+ const int alignWidth = qMin(width, (8 - (x & 7)) & 7);
+ const int doAlign = (alignWidth > 0 ? 1 : 0);
+ const int alignStart = pixelsPerByte - 1 - (x & 7);
+ const int alignStop = alignStart - (alignWidth - 1);
+ const quint8 alignMask = ((1 << alignWidth) - 1) << alignStop;
+ const int tailWidth = (width - alignWidth) & 7;
+ const int doTail = (tailWidth > 0 ? 1 : 0);
+ const quint8 tailMask = (1 << (pixelsPerByte - tailWidth)) - 1;
+ const int width8 = (width - alignWidth) / pixelsPerByte;
+
+ srcStride = srcStride / sizeof(SRC) - (width8 * 8 + alignWidth);
+ dstStride -= (width8 + doAlign);
+
+ for (int j = 0; j < height; ++j) {
+ if (doAlign) {
+ quint8 d = *dest8 & ~alignMask;
+ for (int i = alignStart; i >= alignStop; --i)
+ d |= qt_convertToMono<SRC>(*src++) << i;
+ *dest8++ = d;
+ }
+ for (int i = 0; i < width8; ++i) {
+ *dest8 = (qt_convertToMono<SRC>(src[0]) << 7)
+ | (qt_convertToMono<SRC>(src[1]) << 6)
+ | (qt_convertToMono<SRC>(src[2]) << 5)
+ | (qt_convertToMono<SRC>(src[3]) << 4)
+ | (qt_convertToMono<SRC>(src[4]) << 3)
+ | (qt_convertToMono<SRC>(src[5]) << 2)
+ | (qt_convertToMono<SRC>(src[6]) << 1)
+ | (qt_convertToMono<SRC>(src[7]));
+ src += 8;
+ ++dest8;
+ }
+ if (doTail) {
+ quint8 d = *dest8 & tailMask;
+ switch (tailWidth) {
+ case 7: d |= qt_convertToMono<SRC>(src[6]) << 1;
+ case 6: d |= qt_convertToMono<SRC>(src[5]) << 2;
+ case 5: d |= qt_convertToMono<SRC>(src[4]) << 3;
+ case 4: d |= qt_convertToMono<SRC>(src[3]) << 4;
+ case 3: d |= qt_convertToMono<SRC>(src[2]) << 5;
+ case 2: d |= qt_convertToMono<SRC>(src[1]) << 6;
+ case 1: d |= qt_convertToMono<SRC>(src[0]) << 7;
+ }
+ *dest8 = d;
+ }
+
+ dest8 += dstStride;
+ src += srcStride;
+ }
+}
+
+template <>
+void qt_rectconvert(qmono *dest, const quint32 *src,
+ int x, int y, int width, int height,
+ int dstStride, int srcStride)
+{
+ qt_rectconvert_mono<quint32>(dest, src, x, y, width, height,
+ dstStride, srcStride);
+}
+
+template <>
+void qt_rectconvert(qmono *dest, const quint16 *src,
+ int x, int y, int width, int height,
+ int dstStride, int srcStride)
+{
+ qt_rectconvert_mono<quint16>(dest, src, x, y, width, height,
+ dstStride, srcStride);
+}
+
+template <>
+void qt_rectconvert(qmono *dest, const qrgb444 *src,
+ int x, int y, int width, int height,
+ int dstStride, int srcStride)
+{
+ qt_rectconvert_mono<qrgb444>(dest, src, x, y, width, height,
+ dstStride, srcStride);
+}
+
+template <>
+void qt_rectconvert(qmono *dest, const qargb4444 *src,
+ int x, int y, int width, int height,
+ int dstStride, int srcStride)
+{
+ qt_rectconvert_mono<qargb4444>(dest, src, x, y, width, height,
+ dstStride, srcStride);
+}
+
+static void blit_1(QScreen *screen, const QImage &image,
+ const QPoint &topLeft, const QRegion &region)
+{
+ switch (image.format()) {
+ case QImage::Format_ARGB32_Premultiplied:
+ blit_template<qmono, quint32>(screen, image, topLeft, region);
+ return;
+ case QImage::Format_RGB16:
+ blit_template<qmono, quint16>(screen, image, topLeft, region);
+ return;
+ case QImage::Format_RGB444:
+ blit_template<qmono, qrgb444>(screen, image, topLeft, region);
+ return;
+ case QImage::Format_ARGB4444_Premultiplied:
+ blit_template<qmono, qargb4444>(screen, image, topLeft, region);
+ return;
+ default:
+ qCritical("blit_1(): Image format %d not supported!", image.format());
+ }
+}
+#endif // QT_QWS_DEPTH_1
+
+#ifdef QT_QWS_DEPTH_GENERIC
+
+static void blit_rgb(QScreen *screen, const QImage &image,
+ const QPoint &topLeft, const QRegion &region)
+{
+ switch (image.format()) {
+ case QImage::Format_ARGB32_Premultiplied:
+ blit_template<qrgb, quint32>(screen, image, topLeft, region);
+ return;
+ case QImage::Format_RGB16:
+ blit_template<qrgb, quint16>(screen, image, topLeft, region);
+ return;
+ default:
+ qCritical("blit_rgb(): Image format %d not supported!", image.format());
+ }
+}
+
+void qt_set_generic_blit(QScreen *screen, int bpp,
+ int len_red, int len_green, int len_blue, int len_alpha,
+ int off_red, int off_green, int off_blue, int off_alpha)
+{
+ qrgb::bpp = bpp / 8;
+ qrgb::len_red = len_red;
+ qrgb::len_green = len_green;
+ qrgb::len_blue = len_blue;
+ qrgb::len_alpha = len_alpha;
+ qrgb::off_red = off_red;
+ qrgb::off_green = off_green;
+ qrgb::off_blue = off_blue;
+ qrgb::off_alpha = off_alpha;
+ screen->d_ptr->blit = blit_rgb;
+ if (bpp == 16)
+ screen->d_ptr->solidFill = solidFill_rgb_16bpp;
+ else if (bpp == 32)
+ screen->d_ptr->solidFill = solidFill_rgb_32bpp;
+}
+
+#endif // QT_QWS_DEPTH_GENERIC
+
+void qt_blit_setup(QScreen *screen, const QImage &image,
+ const QPoint &topLeft, const QRegion &region)
+{
+ switch (screen->depth()) {
+#ifdef QT_QWS_DEPTH_32
+ case 32:
+ if (screen->pixelType() == QScreen::NormalPixel)
+ screen->d_ptr->blit = blit_32;
+ else
+ screen->d_ptr->blit = blit_template<qabgr8888, quint32>;
+ break;
+#endif
+#ifdef QT_QWS_DEPTH_24
+ case 24:
+ if (screen->pixelType() == QScreen::NormalPixel)
+ screen->d_ptr->blit = blit_qrgb888;
+ else
+ screen->d_ptr->blit = blit_24;
+ break;
+#endif
+#ifdef QT_QWS_DEPTH_18
+ case 18:
+ screen->d_ptr->blit = blit_18;
+ break;
+#endif
+#ifdef QT_QWS_DEPTH_16
+ case 16:
+#if Q_BYTE_ORDER == Q_BIG_ENDIAN
+ if (screen->d_ptr->fb_is_littleEndian)
+ screen->d_ptr->blit = blit_16_bigToLittleEndian;
+ else
+#endif
+ if (screen->pixelType() == QScreen::NormalPixel)
+ screen->d_ptr->blit = blit_16;
+ else
+ screen->d_ptr->blit = blit_template<qbgr565, quint16>;
+ break;
+#endif
+#ifdef QT_QWS_DEPTH_15
+ case 15:
+#if Q_BYTE_ORDER == Q_BIG_ENDIAN
+ if (screen->d_ptr->fb_is_littleEndian)
+ screen->d_ptr->blit = blit_15_bigToLittleEndian;
+ else
+#endif // Q_BIG_ENDIAN
+ if (screen->pixelType() == QScreen::NormalPixel)
+ screen->d_ptr->blit = blit_15;
+ else
+ screen->d_ptr->blit = blit_template<qbgr555, qrgb555>;
+ break;
+#endif
+#ifdef QT_QWS_DEPTH_12
+ case 12:
+ screen->d_ptr->blit = blit_12;
+ break;
+#endif
+#ifdef QT_QWS_DEPTH_8
+ case 8:
+ screen->d_ptr->blit = blit_8;
+ break;
+#endif
+#ifdef QT_QWS_DEPTH_4
+ case 4:
+ screen->d_ptr->blit = blit_4;
+ break;
+#endif
+#ifdef QT_QWS_DEPTH_1
+ case 1:
+ screen->d_ptr->blit = blit_1;
+ break;
+#endif
+ default:
+ qFatal("blit_setup(): Screen depth %d not supported!",
+ screen->depth());
+ screen->d_ptr->blit = 0;
+ break;
+ }
+ screen->d_ptr->blit(screen, image, topLeft, region);
+}
+
+QScreenPrivate::QScreenPrivate(QScreen *parent, QScreen::ClassId id)
+ : defaultGraphicsSystem(QWSGraphicsSystem(parent)),
+ pixelFormat(QImage::Format_Invalid),
+#ifdef QT_QWS_CLIENTBLIT
+ supportsBlitInClients(false),
+#endif
+ classId(id), q_ptr(parent)
+{
+ solidFill = qt_solidFill_setup;
+ blit = qt_blit_setup;
+#if Q_BYTE_ORDER == Q_BIG_ENDIAN
+ fb_is_littleEndian = false;
+#endif
+ pixmapFactory = 0;
+ graphicsSystem = &defaultGraphicsSystem;
+}
+
+QScreenPrivate::~QScreenPrivate()
+{
+}
+
+QImage::Format QScreenPrivate::preferredImageFormat() const
+{
+ if (pixelFormat > QImage::Format_Indexed8)
+ return pixelFormat;
+
+ if (q_ptr->depth() <= 16)
+ return QImage::Format_RGB16;
+ else
+ return QImage::Format_ARGB32_Premultiplied;
+}
+
+/*!
+ \class QScreen
+ \ingroup qws
+
+ \brief The QScreen class is a base class for screen drivers in
+ Qt for Embedded Linux.
+
+ Note that this class is only available in \l{Qt for Embedded Linux}.
+
+ \l{Qt for Embedded Linux} provides ready-made drivers for several screen
+ protocols, see the \l{Qt for Embedded Linux Display Management}{display
+ management} documentation for details. Custom screen drivers can
+ be implemented by subclassing the QScreen class and creating a
+ screen driver plugin (derived from QScreenDriverPlugin). The
+ default implementation of the QScreenDriverFactory class
+ will automatically detect the plugin, and load the driver into the
+ server application at run-time using Qt's \l {How to Create Qt
+ Plugins}{plugin system}.
+
+ When rendering, the default behavior is for each
+ client to render its widgets as well as its decorations into
+ memory, while the server copies the memory content to the device's
+ framebuffer using the screen driver. See the \l{Qt for Embedded Linux
+ Architecture} overview for details (note that it is possible for
+ the clients to manipulate and control the underlying hardware
+ directly as well).
+
+ Starting with Qt 4.2, it is also possible to add an
+ accelerated graphics driver to take advantage of available
+ hardware resources. See the \l{Adding an Accelerated Graphics
+ Driver to Qt for Embedded Linux} documentation for details.
+
+ \tableofcontents
+
+ \section1 Framebuffer Management
+
+ When a \l{Qt for Embedded Linux} application starts running, it
+ calls the screen driver's connect() function to map the
+ framebuffer and the accelerated drivers that the graphics card
+ control registers. The connect() function should then read out the
+ parameters of the framebuffer and use them as required to set this
+ class's protected variables.
+
+ The initDevice() function can be reimplemented to initialize the
+ graphics card. Note, however, that connect() is called \e before
+ the initDevice() function, so, for some hardware configurations,
+ some of the initialization that would normally be done in the
+ initDevice() function might have to be done in the connect()
+ function.
+
+ Likewise, just before a \l{Qt for Embedded Linux} application
+ exits, it calls the screen driver's disconnect() function. The
+ server application will in addition call the shutdownDevice()
+ function before it calls disconnect(). Note that the default
+ implementation of the shutdownDevice() function only hides the
+ mouse cursor.
+
+ QScreen also provides the save() and restore() functions, making
+ it possible to save and restore the state of the graphics
+ card. Note that the default implementations do nothing. Hardware
+ screen drivers should reimplement these functions to save (and
+ restore) its registers, enabling switching between virtual
+ consoles.
+
+ In addition, you can use the base() function to retrieve a pointer
+ to the beginning of the framebuffer, and the region() function to
+ retrieve the framebuffer's region. Use the onCard() function to
+ determine whether the framebuffer is within the graphics card's
+ memory, and the totalSize() function to determine the size of the
+ available graphics card memory (including the screen). Finally,
+ you can use the offset() function to retrieve the offset between
+ the framebuffer's coordinates and the application's coordinate
+ system.
+
+ \section1 Palette Management
+
+ QScreen provides several functions to retrieve information about
+ the color palette: The clut() function returns a pointer to the
+ color lookup table (i.e. its color palette). Use the numCols()
+ function to determine the number of entries in this table, and the
+ alloc() function to retrieve the palette index of the color that
+ is the closest match to a given RGB value.
+
+ To determine if the screen driver supports a given color depth,
+ use the supportsDepth() function that returns true of the
+ specified depth is supported.
+
+ \section1 Drawing on Screen
+
+ When a screen update is required, the \l{Qt for Embedded Linux} server runs
+ through all the top-level windows that intersect with the region
+ that is about to be updated, and ensures that the associated
+ clients have updated their memory buffer. Then the server calls
+ the exposeRegion() function that composes the window surfaces and
+ copies the content of memory to screen by calling the blit() and
+ solidFill() functions.
+
+ The blit() function copies a given region in a given image to a
+ specified point using device coordinates, while the solidFill()
+ function fills the given region of the screen with the specified
+ color. Note that normally there is no need to call either of these
+ functions explicitly.
+
+ In addition, QScreen provides the blank() function that can be
+ reimplemented to prevent any contents from being displayed on the
+ screen, and the setDirty() function that can be reimplemented to
+ indicate that a given rectangle of the screen has been
+ altered. Note that the default implementations of these functions
+ do nothing.
+
+ Reimplement the the mapFromDevice() and mapToDevice() functions to
+ map objects from the framebuffer coordinate system to the
+ coordinate space used by the application, and vice versa. Be aware
+ that the default implementations simply return the given objects
+ as they are.
+
+ \section1 Properties
+
+ \table
+ \header \o Property \o Functions
+ \row
+ \o Size
+ \o
+
+ The size of the screen can be retrieved using the screenSize()
+ function. The size is returned in bytes.
+
+ The framebuffer's logical width and height can be retrieved using
+ width() and height(), respectively. These functions return values
+ are given in pixels. Alternatively, the physicalWidth() and
+ physicalHeight() function returns the same metrics in
+ millimeters. QScreen also provides the deviceWidth() and
+ deviceHeight() functions returning the physical width and height
+ of the device in pixels. Note that the latter metrics can differ
+ from the ones used if the display is centered within the
+ framebuffer.
+
+ \row
+ \o Resolution
+ \o
+
+ Reimplement the setMode() function to be able to set the
+ framebuffer to a new resolution (width and height) and bit depth.
+
+ The current depth of the framebuffer can be always be retrieved
+ using the depth() function. Use the pixmapDepth() function to
+ obtain the preferred depth for pixmaps.
+
+ \row
+ \o Pixmap Alignment
+ \o
+
+ Use the pixmapOffsetAlignment() function to retrieve the value to
+ which the start address of pixmaps held in the graphics card's
+ memory, should be aligned.
+
+ Use the pixmapLinestepAlignment() to retrieve the value to which
+ the \e {individual scanlines} of pixmaps should be aligned.
+
+ \row
+ \o Image Display
+ \o
+
+ The isInterlaced() function tells whether the screen is displaying
+ images progressively, and the isTransformed() function whether it
+ is rotated. The transformOrientation() function can be
+ reimplemented to return the current rotation.
+
+ \row
+ \o Scanlines
+ \o
+
+ Use the linestep() function to retrieve the length of each
+ scanline of the framebuffer.
+
+ \row
+ \o Pixel Type
+ \o
+
+ The pixelType() function returns the screen's pixel storage format as
+ described by the PixelType enum.
+
+ \endtable
+
+ \section1 Subclassing and Initial Values
+
+ You need to set the following members when implementing a subclass of QScreen:
+
+ \table
+ \header \o Member \o Initial Value
+ \row \o \l{QScreen::}{data} \o A pointer to the framebuffer if possible;
+ 0 otherwise.
+ \row \o \l{QScreen::}{lstep} \o The number of bytes between each scanline
+ in the framebuffer.
+ \row \o \l{QScreen::}{w} \o The logical screen width in pixels.
+ \row \o \l{QScreen::}{h} \o The logical screen height in pixels.
+ \row \o \l{QScreen::}{dw} \o The real screen width in pixels.
+ \row \o \l{QScreen::}{dh} \o The real screen height in pixels.
+ \row \o \l{QScreen::}{d} \o The number of bits per pixel.
+ \row \o \l{QScreen::}{physWidth} \o The screen width in millimeters.
+ \row \o \l{QScreen::}{physHeight} \o The screen height in millimeters.
+ \endtable
+
+ The logical screen values are the same as the real screen values unless the
+ screen is transformed in some way; e.g., rotated.
+
+ See also the \l{Accelerated Graphics Driver Example} for an example that
+ shows how to initialize these values.
+
+ \sa QScreenDriverPlugin, QScreenDriverFactory, {Qt for Embedded Linux Display
+ Management}
+*/
+
+/*!
+ \enum QScreen::PixelType
+
+ This enum describes the pixel storage format of the screen,
+ i.e. the order of the red (R), green (G) and blue (B) components
+ of a pixel.
+
+ \value NormalPixel Red-green-blue (RGB)
+ \value BGRPixel Blue-green-red (BGR)
+
+ \sa pixelType()
+*/
+
+/*!
+ \enum QScreen::ClassId
+
+ This enum defines the class identifiers for the known screen subclasses.
+
+ \value LinuxFBClass QLinuxFBScreen
+ \value TransformedClass QTransformedScreen
+ \value VNCClass QVNCScreen
+ \value MultiClass QMultiScreen
+ \value VFbClass QVFbScreen
+ \value DirectFBClass QDirectFBScreen
+ \value SvgalibClass QSvgalibScreen
+ \value ProxyClass QProxyScreen
+ \value GLClass QGLScreen
+ \value CustomClass Unknown QScreen subclass
+
+ \sa classId()
+*/
+
+/*!
+ \variable QScreen::screenclut
+ \brief the color table
+
+ Initialize this variable in a subclass using a paletted screen mode,
+ and initialize its partner, QScreen::screencols.
+
+ \sa screencols
+*/
+
+/*!
+ \variable QScreen::screencols
+ \brief the number of entries in the color table
+
+ Initialize this variable in a subclass using a paletted screen mode,
+ and initialize its partner, QScreen::screenclut.
+
+ \sa screenclut
+*/
+
+/*!
+ \variable QScreen::data
+ \brief points to the first visible pixel in the frame buffer.
+
+ You must initialize this variable if you are using the default
+ implementation of non-buffered painting Qt::WA_PaintOnScreen,
+ QPixmap::grabWindow() or QDirectPainter::frameBuffer(). If you
+ initialize this variable, you must also initialize QScreen::size and
+ QScreen::mapsize.
+
+ \sa QScreen::size, QScreen::mapsize
+*/
+
+/*!
+ \variable QScreen::w
+ \brief the logical width of the screen.
+
+ This variable \e{must} be initialized by a subclass.
+*/
+
+/*!
+ \variable QScreen::lstep
+ \brief the number of bytes representing a line in the frame buffer.
+
+ i.e., \e{line step}. \c {data[lstep * 2]} is the address of the
+ first visible pixel in the third line of the frame buffer.
+
+ \sa data
+*/
+
+/*!
+ \variable QScreen::h
+ \brief the logical height of the screen.
+
+ This variable \e{must} be initialized by a subclass.
+*/
+
+/*!
+ \variable QScreen::d
+ \brief the pixel depth
+
+ This is the number of significant bits used to set a pixel
+ color. This variable \e{must} be initialized by a subclass.
+*/
+
+/*!
+ \variable QScreen::pixeltype
+ \brief set to BGRPixel
+
+ Set this variable to BGRPixel in a subclass, if the screen pixel
+ format is a BGR type and you have used setPixelFormat() to set the
+ pixel format to the corresponding RGB format. e.g., you have set the
+ pixel format to QImage::Format_RGB555, but your screen really uses
+ BGR, not RGB.
+*/
+
+/*!
+ \variable QScreen::grayscale
+ \brief the gray scale screen mode flag
+
+ Set this variable to true in a subclass, if you are using a
+ grayscale screen mode. e.g., in an 8-bit mode where you don't want
+ to use the palette, but you want to use the grayscales.
+*/
+
+/*!
+ \variable QScreen::dw
+ \brief the device width
+
+ This is the number of pixels in a row of the physical screen. It
+ \e{must} be initialized by a subclass. Normally, it should be set to
+ the logical width QScreen::w, but it might be different, e.g., if
+ you are doing rotations in software.
+
+ \sa QScreen::w
+*/
+
+/*!
+ \variable QScreen::dh
+ \brief the device height
+
+ This is the number of pixels in a column of the physical screen. It
+ \e{must} be initialized by a subclass. Normally, it should be set to
+ the logical height QScreen::h, but it might be different, e.g., if
+ you are doing rotations in software.
+
+ \sa QScreen::h
+*/
+
+/*!
+ \variable QScreen::size
+ \brief the number of bytes in the visible region of the frame buffer
+
+ This is the number of bytes in the visible part of the block pointed
+ to by the QScreen::data pointer. You must initialize this variable
+ if you initialize the QScreen::data pointer.
+
+ \sa QScreen::data, QScreen::mapsize
+*/
+
+/*!
+ \variable QScreen::mapsize
+ \brief the total number of bytes in the frame buffer
+
+ This is the total number of bytes in the block pointed to by the
+ QScreen::data pointer. You must initialize this variable if you
+ initialize the QScreen::data pointer.
+
+ \sa QScreen::data, QScreen::size
+*/
+
+/*!
+ \variable QScreen::physWidth
+ \brief the physical width of the screen in millimeters.
+
+ Currently, this variable is used when calculating the screen DPI,
+ which in turn is used when deciding the actual font size Qt is
+ using.
+*/
+
+/*!
+ \variable QScreen::physHeight
+ \brief the physical height of the screen in millimeters.
+
+ Currently, this variable is used when calculating the screen DPI,
+ which in turn is used when deciding the actual font size Qt is
+ using.
+*/
+
+/*!
+ \fn static QScreen* QScreen::instance()
+
+ Returns a pointer to the application's QScreen instance.
+
+ If this screen consists of several subscreens, operations to the
+ returned instance will affect all its subscreens. Use the
+ subscreens() function to retrieve access to a particular
+ subscreen.
+
+ \sa subScreens(), subScreenIndexAt()
+*/
+
+/*!
+ \fn QList<QScreen*> QScreen::subScreens() const
+ \since 4.2
+
+ Returns a list of this screen's subscreens. Use the
+ subScreenIndexAt() function to retrieve the index of a screen at a
+ given position.
+
+ Note that if \e this screen consists of several subscreens,
+ operations to \e this instance will affect all subscreens by
+ default.
+
+ \sa instance(), subScreenIndexAt()
+*/
+
+/*!
+ \fn int QScreen::physicalWidth() const
+ \since 4.2
+
+ Returns the physical width of the screen in millimeters.
+
+ \sa width(), deviceWidth(), physicalHeight()
+*/
+
+/*!
+ \fn int QScreen::physicalHeight() const
+ \since 4.2
+
+ Returns the physical height of the screen in millimeters.
+
+ \sa height(), deviceHeight(), physicalWidth()
+*/
+
+/*!
+ \fn virtual bool QScreen::initDevice() = 0
+
+ This function is called by the \l{Qt for Embedded Linux} server to
+ initialize the framebuffer. Note that a server application will call the
+ connect() function prior to this function.
+
+ Implement this function to make accelerated drivers set up the
+ graphics card. Return true to indicate success and false to indicate
+ failure.
+
+ \sa shutdownDevice(), connect()
+*/
+
+/*!
+ \fn virtual bool QScreen::connect(const QString &displaySpec) = 0
+
+ This function is called by every \l{Qt for Embedded Linux}
+ application on startup, and must be implemented to map in the
+ framebuffer and the accelerated drivers that the graphics card
+ control registers. Note that coonnect must be called \e before
+ the initDevice() function.
+
+ Ensure that true is returned if a connection to the screen device
+ is made. Otherwise, return false. Upon making the connection, the
+ function should read out the parameters of the framebuffer and use
+ them as required to set this class's protected variables.
+
+ The \a displaySpec argument is passed by the QWS_DISPLAY
+ environment variable or the -display command line parameter, and
+ has the following syntax:
+
+ \snippet doc/src/snippets/code/src_gui_embedded_qscreen_qws.cpp 0
+
+ For example, to use the mach64 driver on fb1 as display 2:
+
+ \snippet doc/src/snippets/code/src_gui_embedded_qscreen_qws.cpp 1
+
+ See \l{Qt for Embedded Linux Display Management} for more details.
+
+ \sa disconnect(), initDevice(), {Running Qt for Embedded Linux Applications}
+*/
+
+/*!
+ \fn QScreen::disconnect()
+
+ This function is called by every \l{Qt for Embedded Linux} application
+ before exiting, and must be implemented to unmap the
+ framebuffer. Note that a server application will call the
+ shutdownDevice() function prior to this function.
+
+ \sa connect(), shutdownDevice(), {Running Qt for Embedded Linux
+ Applications}
+*/
+
+/*!
+ \fn QScreen::setMode(int width, int height, int depth)
+
+ Implement this function to reset the framebuffer's resolution (\a
+ width and \a height) and bit \a depth.
+
+ After the resolution has been set, existing paint engines will be
+ invalid and the framebuffer should be completely redrawn. In a
+ multiple-process situation, all other applications must be
+ notified to reset their mode and update themselves accordingly.
+*/
+
+/*!
+ \fn QScreen::blank(bool on)
+
+ Prevents the screen driver form displaying any content on the
+ screen.
+
+ Note that the default implementation does nothing.
+
+ Reimplement this function to prevent the screen driver from
+ displaying any contents on the screen if \a on is true; otherwise
+ the contents is expected to be shown.
+
+ \sa blit()
+*/
+
+/*!
+ \fn int QScreen::pixmapOffsetAlignment()
+
+ Returns the value (in bits) to which the start address of pixmaps
+ held in the graphics card's memory, should be aligned.
+
+ Note that the default implementation returns 64; reimplement this
+ function to override the return value, e.g., when implementing an
+ accelerated driver (see the \l {Adding an Accelerated Graphics
+ Driver to Qt for Embedded Linux}{Adding an Accelerated Graphics Driver}
+ documentation for details).
+
+ \sa pixmapLinestepAlignment()
+*/
+
+/*!
+ \fn int QScreen::pixmapLinestepAlignment()
+
+ Returns the value (in bits) to which individual scanlines of
+ pixmaps held in the graphics card's memory, should be
+ aligned.
+
+ Note that the default implementation returns 64; reimplement this
+ function to override the return value, e.g., when implementing an
+ accelerated driver (see the \l {Adding an Accelerated Graphics
+ Driver to Qt for Embedded Linux}{Adding an Accelerated Graphics Driver}
+ documentation for details).
+
+ \sa pixmapOffsetAlignment()
+*/
+
+/*!
+ \fn QScreen::width() const
+
+ Returns the logical width of the framebuffer in pixels.
+
+ \sa deviceWidth(), physicalWidth(), height()
+*/
+
+/*!
+ \fn int QScreen::height() const
+
+ Returns the logical height of the framebuffer in pixels.
+
+ \sa deviceHeight(), physicalHeight(), width()
+*/
+
+/*!
+ \fn QScreen::depth() const
+
+ Returns the depth of the framebuffer, in bits per pixel.
+
+ Note that the returned depth is the number of bits each pixel
+ fills rather than the number of significant bits, so 24bpp and
+ 32bpp express the same range of colors (8 bits of red, green and
+ blue).
+
+ \sa clut(), pixmapDepth()
+*/
+
+/*!
+ \fn int QScreen::pixmapDepth() const
+
+ Returns the preferred depth for pixmaps, in bits per pixel.
+
+ \sa depth()
+*/
+
+/*!
+ \fn QScreen::linestep() const
+
+ Returns the length of each scanline of the framebuffer in bytes.
+
+ \sa isInterlaced()
+*/
+
+/*!
+ \fn QScreen::deviceWidth() const
+
+ Returns the physical width of the framebuffer device in pixels.
+
+ Note that the returned width can differ from the width which
+ \l{Qt for Embedded Linux} will actually use, that is if the display is
+ centered within the framebuffer.
+
+ \sa width(), physicalWidth(), deviceHeight()
+*/
+
+/*!
+ \fn QScreen::deviceHeight() const
+
+ Returns the full height of the framebuffer device in pixels.
+
+ Note that the returned height can differ from the height which
+ \l{Qt for Embedded Linux} will actually use, that is if the display is
+ centered within the framebuffer.
+
+ \sa height(), physicalHeight(), deviceWidth()
+*/
+
+/*!
+ \fn uchar *QScreen::base() const
+
+ Returns a pointer to the beginning of the framebuffer.
+
+ \sa onCard(), region(), totalSize()
+*/
+
+/*!
+ \fn uchar *QScreen::cache(int)
+
+ \internal
+
+ This function is used to store pixmaps in graphics memory for the
+ use of the accelerated drivers. See QLinuxFbScreen (where the
+ caching is implemented) for more information.
+*/
+
+/*!
+ \fn QScreen::uncache(uchar *)
+
+ \internal
+
+ This function is called on pixmap destruction to remove them from
+ graphics card memory.
+*/
+
+/*!
+ \fn QScreen::screenSize() const
+
+ Returns the size of the screen in bytes.
+
+ The screen size is always located at the beginning of framebuffer
+ memory, i.e. it can also be retrieved using the base() function.
+
+ \sa base(), region()
+*/
+
+/*!
+ \fn QScreen::totalSize() const
+
+ Returns the size of the available graphics card memory (including
+ the screen) in bytes.
+
+ \sa onCard()
+*/
+
+// Unaccelerated screen/driver setup. Can be overridden by accelerated
+// drivers
+
+/*!
+ \fn QScreen::QScreen(int displayId)
+
+ Constructs a new screen driver.
+
+ The \a displayId identifies the \l{Qt for Embedded Linux} server to connect
+ to.
+*/
+
+/*!
+ \fn QScreen::clut()
+
+ Returns a pointer to the screen's color lookup table (i.e. its
+ color palette).
+
+ Note that this function only apply in paletted modes like 8-bit,
+ i.e. in modes where only the palette indexes (and not the actual
+ color values) are stored in memory.
+
+ \sa alloc(), depth(), numCols()
+*/
+
+/*!
+ \fn int QScreen::numCols()
+
+ Returns the number of entries in the screen's color lookup table
+ (i.e. its color palette). A pointer to the color table can be
+ retrieved using the clut() function.
+
+ \sa clut(), alloc()
+*/
+
+/*!
+ \since 4.4
+
+ Constructs a new screen driver.
+
+ The \a display_id identifies the \l{Qt for Embedded Linux}
+ server to connect to. The \a classId specifies the class
+ identifier.
+*/
+QScreen::QScreen(int display_id, ClassId classId)
+ : screencols(0), data(0), entries(0), entryp(0), lowest(0),
+ w(0), lstep(0), h(0), d(1), pixeltype(NormalPixel), grayscale(false),
+ dw(0), dh(0), size(0), mapsize(0), displayId(display_id),
+ physWidth(0), physHeight(0), d_ptr(new QScreenPrivate(this, classId))
+{
+ clearCacheFunc = 0;
+}
+
+QScreen::QScreen(int display_id)
+ : screencols(0), data(0), entries(0), entryp(0), lowest(0),
+ w(0), lstep(0), h(0), d(1), pixeltype(NormalPixel), grayscale(false),
+ dw(0), dh(0), size(0), mapsize(0), displayId(display_id),
+ physWidth(0), physHeight(0), d_ptr(new QScreenPrivate(this))
+{
+ clearCacheFunc = 0;
+}
+
+/*!
+ Destroys this screen driver.
+*/
+
+QScreen::~QScreen()
+{
+ delete d_ptr;
+}
+
+/*!
+ This function is called by the \l{Qt for Embedded Linux} server before it
+ calls the disconnect() function when exiting.
+
+ Note that the default implementation only hides the mouse cursor;
+ reimplement this function to do the necessary graphics card
+ specific cleanup.
+
+ \sa initDevice(), disconnect()
+*/
+
+void QScreen::shutdownDevice()
+{
+#ifndef QT_NO_QWS_CURSOR
+ if (qt_screencursor)
+ qt_screencursor->hide();
+#endif
+}
+
+extern bool qws_accel; //in qapplication_qws.cpp
+
+/*!
+ \fn PixelType QScreen::pixelType() const
+
+ Returns the pixel storage format of the screen.
+*/
+
+/*!
+ Returns the pixel format of the screen, or \c QImage::Format_Invalid
+ if the pixel format is not a supported image format.
+
+*/
+QImage::Format QScreen::pixelFormat() const
+{
+ return d_ptr->pixelFormat;
+}
+
+/*!
+ Sets the screen's pixel format to \a format.
+ */
+void QScreen::setPixelFormat(QImage::Format format)
+{
+ d_ptr->pixelFormat = format;
+}
+
+
+/*!
+ \fn int QScreen::alloc(unsigned int red, unsigned int green, unsigned int blue)
+
+ Returns the index in the screen's palette which is the closest
+ match to the given RGB value (\a red, \a green, \a blue).
+
+ Note that this function only apply in paletted modes like 8-bit,
+ i.e. in modes where only the palette indexes (and not the actual
+ color values) are stored in memory.
+
+ \sa clut(), numCols()
+*/
+
+int QScreen::alloc(unsigned int r,unsigned int g,unsigned int b)
+{
+ int ret = 0;
+ if (d == 8) {
+ if (grayscale)
+ return qGray(r, g, b);
+
+ // First we look to see if we match a default color
+ const int pos = (r + 25) / 51 * 36 + (g + 25) / 51 * 6 + (b + 25) / 51;
+ if (pos < screencols && screenclut[pos] == qRgb(r, g, b)) {
+ return pos;
+ }
+
+ // search for nearest color
+ unsigned int mindiff = 0xffffffff;
+ unsigned int diff;
+ int dr,dg,db;
+
+ for (int loopc = 0; loopc < screencols; ++loopc) {
+ dr = qRed(screenclut[loopc]) - r;
+ dg = qGreen(screenclut[loopc]) - g;
+ db = qBlue(screenclut[loopc]) - b;
+ diff = dr*dr + dg*dg + db*db;
+
+ if (diff < mindiff) {
+ ret = loopc;
+ if (!diff)
+ break;
+ mindiff = diff;
+ }
+ }
+ } else if (d == 4) {
+ ret = qGray(r, g, b) >> 4;
+ } else if (d == 1) {
+ ret = qGray(r, g, b) >= 128;
+ } else {
+ qFatal("cannot alloc %dbpp color", d);
+ }
+
+ return ret;
+}
+
+/*!
+ Saves the current state of the graphics card.
+
+ For example, hardware screen drivers should reimplement the save()
+ and restore() functions to save and restore its registers,
+ enabling swintching between virtual consoles.
+
+ Note that the default implementation does nothing.
+
+ \sa restore()
+*/
+
+void QScreen::save()
+{
+}
+
+/*!
+ Restores the previously saved state of the graphics card.
+
+ For example, hardware screen drivers should reimplement the save()
+ and restore() functions to save and restore its registers,
+ enabling swintching between virtual consoles.
+
+ Note that the default implementation does nothing.
+
+ \sa save()
+*/
+
+void QScreen::restore()
+{
+}
+
+void QScreen::blank(bool)
+{
+}
+
+/*!
+ \internal
+*/
+
+void QScreen::set(unsigned int, unsigned int, unsigned int, unsigned int)
+{
+}
+
+/*!
+ \fn bool QScreen::supportsDepth(int depth) const
+
+ Returns true if the screen supports the specified color \a depth;
+ otherwise returns false.
+
+ \sa clut()
+*/
+
+bool QScreen::supportsDepth(int d) const
+{
+ if (false) {
+ //Just to simplify the ifdeffery
+#ifdef QT_QWS_DEPTH_1
+ } else if(d==1) {
+ return true;
+#endif
+#ifdef QT_QWS_DEPTH_4
+ } else if(d==4) {
+ return true;
+#endif
+#ifdef QT_QWS_DEPTH_8
+ } else if(d==8) {
+ return true;
+#endif
+#ifdef QT_QWS_DEPTH_16
+ } else if(d==16) {
+ return true;
+#endif
+#ifdef QT_QWS_DEPTH_15
+ } else if (d == 15) {
+ return true;
+#endif
+#ifdef QT_QWS_DEPTH_18
+ } else if(d==18 || d==19) {
+ return true;
+#endif
+#ifdef QT_QWS_DEPTH_24
+ } else if(d==24) {
+ return true;
+#endif
+#ifdef QT_QWS_DEPTH_32
+ } else if(d==32) {
+ return true;
+#endif
+ }
+ return false;
+}
+
+/*!
+ \fn bool QScreen::onCard(const unsigned char *buffer) const
+
+ Returns true if the specified \a buffer is within the graphics
+ card's memory; otherwise returns false (i.e. if it's in main RAM).
+
+ \sa base(), totalSize()
+*/
+
+bool QScreen::onCard(const unsigned char * p) const
+{
+ long t=(unsigned long)p;
+ long bmin=(unsigned long)data;
+ if (t < bmin)
+ return false;
+ if(t >= bmin+mapsize)
+ return false;
+ return true;
+}
+
+/*!
+ \fn bool QScreen::onCard(const unsigned char * buffer, ulong& offset) const
+ \overload
+
+ If the specified \a buffer is within the graphics card's memory,
+ this function stores the offset from the start of graphics card
+ memory (in bytes), in the location specified by the \a offset
+ parameter.
+*/
+
+bool QScreen::onCard(const unsigned char * p, ulong& offset) const
+{
+ long t=(unsigned long)p;
+ long bmin=(unsigned long)data;
+ if (t < bmin)
+ return false;
+ long o = t - bmin;
+ if (o >= mapsize)
+ return false;
+ offset = o;
+ return true;
+}
+
+/*
+#if !defined(QT_NO_QWS_REPEATER)
+ { "Repeater", qt_get_screen_repeater, 0 },
+#endif
+#if defined(QT_QWS_EE)
+ { "EE", qt_get_screen_ee, 0 },
+#endif
+
+*/
+
+/*
+Given a display_id (number of the \l{Qt for Embedded Linux} server to connect to)
+and a spec (e.g. Mach64:/dev/fb0) return a QScreen-descendant.
+The QScreenDriverFactory is queried for a suitable driver and, if found,
+asked to create a driver.
+People writing new graphics drivers should either hook their own
+QScreen-descendant into QScreenDriverFactory or use the QScreenDriverPlugin
+to make a dynamically loadable driver.
+*/
+
+Q_GUI_EXPORT QScreen* qt_get_screen(int display_id, const char *spec)
+{
+ QString displaySpec = QString::fromAscii(spec);
+ QString driver = displaySpec;
+ int colon = displaySpec.indexOf(QLatin1Char(':'));
+ if (colon >= 0)
+ driver.truncate(colon);
+ driver = driver.trimmed();
+
+ bool foundDriver = false;
+ QString driverName = driver;
+
+ QStringList driverList;
+ if (!driver.isEmpty())
+ driverList << driver;
+ else
+ driverList = QScreenDriverFactory::keys();
+
+ for (int i = 0; i < driverList.size(); ++i) {
+ const QString driverName = driverList.at(i);
+ qt_screen = QScreenDriverFactory::create(driverName, display_id);
+ if (qt_screen) {
+ foundDriver = true;
+ if (qt_screen->connect(displaySpec)) {
+ return qt_screen;
+ } else {
+ delete qt_screen;
+ qt_screen = 0;
+ }
+ }
+ }
+
+ if (driver.isNull())
+ qFatal("No suitable driver found");
+ else if (foundDriver)
+ qFatal("%s: driver cannot connect", driver.toLatin1().constData());
+ else
+ qFatal("%s: driver not found", driver.toLatin1().constData());
+
+ return 0;
+}
+
+#ifndef QT_NO_QWS_CURSOR
+static void blendCursor(QImage *dest, const QImage &cursor, const QPoint &offset)
+{
+ QRasterBuffer rb;
+ rb.prepare(dest);
+
+ QSpanData spanData;
+ spanData.init(&rb, 0);
+ spanData.type = QSpanData::Texture;
+ spanData.initTexture(&cursor, 256);
+ spanData.dx = -offset.x();
+ spanData.dy = -offset.y();
+ if (!spanData.blend)
+ return;
+
+ const QRect rect = QRect(offset, cursor.size())
+ & QRect(QPoint(0, 0), dest->size());
+ const int w = rect.width();
+ const int h = rect.height();
+
+ QVarLengthArray<QT_FT_Span, 32> spans(h);
+ for (int i = 0; i < h; ++i) {
+ spans[i].x = rect.x();
+ spans[i].len = w;
+ spans[i].y = rect.y() + i;
+ spans[i].coverage = 255;
+ }
+ spanData.blend(h, spans.constData(), &spanData);
+}
+#endif // QT_NO_QWS_CURSOR
+
+/*!
+ \fn void QScreen::exposeRegion(QRegion region, int windowIndex)
+
+ This function is called by the \l{Qt for Embedded Linux} server whenever a
+ screen update is required. \a region is the area on the screen
+ that must be updated, and \a windowIndex is the index into
+ QWSServer::clientWindows() of the window that required the
+ update. QWSWindow::state() gives more information about the cause.
+
+ The default implementation composes the
+ affected windows and paints the given \a region on screen by
+ calling the blit() and solidFill() functions
+
+ This function can be reimplemented to perform composition in
+ hardware, or to perform transition effects.
+ For simpler hardware acceleration, or to interface with
+ this is typically done by reimplementing the blit() and
+ solidFill() functions instead.
+
+ Note that there is no need to call this function explicitly.
+
+ \sa blit(), solidFill(), blank()
+*/
+void QScreen::exposeRegion(QRegion r, int windowIndex)
+{
+ r &= region();
+ if (r.isEmpty())
+ return;
+
+ int changing = windowIndex;
+ // when we have just lowered a window, we have to expose all the windows below where the
+ // window used to be.
+ if (changing && qwsServer->clientWindows().at(changing)->state() == QWSWindow::Lowering)
+ changing = 0;
+#ifdef QTOPIA_PERFTEST
+ static enum { PerfTestUnknown, PerfTestOn, PerfTestOff } perfTestState = PerfTestUnknown;
+ if(PerfTestUnknown == perfTestState) {
+ if(::getenv("QTOPIA_PERFTEST"))
+ perfTestState = PerfTestOn;
+ else
+ perfTestState = PerfTestOff;
+ }
+ if(PerfTestOn == perfTestState) {
+ QWSWindow *changed = qwsServer->clientWindows().at(changing);
+ if(!changed->client()->identity().isEmpty())
+ qDebug() << "Performance : expose_region :"
+ << changed->client()->identity()
+ << r.boundingRect() << ": "
+ << qPrintable( QTime::currentTime().toString( "h:mm:ss.zzz" ) );
+ }
+#endif
+
+ const QRect bounds = r.boundingRect();
+ QRegion blendRegion;
+ QImage *blendBuffer = 0;
+
+#ifndef QT_NO_QWS_CURSOR
+ if (qt_screencursor && !qt_screencursor->isAccelerated()) {
+ blendRegion = r & qt_screencursor->boundingRect();
+ }
+#endif
+ compose(0, r, blendRegion, &blendBuffer, changing);
+
+ if (blendBuffer) {
+ const QPoint offset = blendRegion.boundingRect().topLeft();
+#ifndef QT_NO_QWS_CURSOR
+ if (qt_screencursor && !qt_screencursor->isAccelerated()) {
+ const QRect cursorRect = qt_screencursor->boundingRect();
+ if (blendRegion.intersects(cursorRect)) {
+ blendCursor(blendBuffer, qt_screencursor->image(),
+ cursorRect.topLeft() - offset);
+ }
+ }
+#endif // QT_NO_QWS_CURSOR
+ blit(*blendBuffer, offset, blendRegion);
+ delete blendBuffer;
+ }
+
+ if (r.numRects() == 1) {
+ setDirty(r.boundingRect());
+ } else {
+ const QVector<QRect> rects = r.rects();
+ for (int i = 0; i < rects.size(); ++i)
+ setDirty(rects.at(i));
+ }
+}
+
+/*!
+ \fn void QScreen::blit(const QImage &image, const QPoint &topLeft, const QRegion &region)
+
+ Copies the given \a region in the given \a image to the point
+ specified by \a topLeft using device coordinates.
+
+ This function is called from the exposeRegion() function; it is
+ not intended to be called explicitly.
+
+ Reimplement this function to make use of \l{Adding an Accelerated
+ Graphics Driver to Qt for Embedded Linux}{accelerated hardware}. Note that
+ this function must be reimplemented if the framebuffer format is
+ not supported by \l{Qt for Embedded Linux} (See the
+ \l{Qt for Embedded Linux Display Management}{Display Management}
+ documentation for more details).
+
+ \sa exposeRegion(), solidFill(), blank()
+*/
+void QScreen::blit(const QImage &img, const QPoint &topLeft, const QRegion &reg)
+{
+ const QRect bound = (region() & QRect(topLeft, img.size())).boundingRect();
+ QWSDisplay::grab();
+ d_ptr->blit(this, img, topLeft - offset(),
+ (reg & bound).translated(-topLeft));
+ QWSDisplay::ungrab();
+}
+
+#ifdef QT_QWS_CLIENTBLIT
+/*!
+ Returns true if this screen driver supports calling QScreen::blit() and
+ QScreen::setDirty() directly from non-server applications, otherwise returns
+ false.
+
+ If available, this is used to optimize the performance of non-occluded, opaque
+ client windows by removing the server round trip when they are updated.
+
+ \sa setSupportsBlitInClients()
+ */
+bool QScreen::supportsBlitInClients() const
+{
+ return d_ptr->supportsBlitInClients;
+}
+
+/*!
+ If \a supported, the screen driver is marked as supporting blitting directly
+ from non-server applications.
+
+ \sa supportsBlitInClients()
+ */
+void QScreen::setSupportsBlitInClients(bool supported)
+{
+ d_ptr->supportsBlitInClients = supported;
+}
+#endif
+
+/*!
+ \internal
+*/
+
+void QScreen::blit(QWSWindow *win, const QRegion &clip)
+{
+ QWSWindowSurface *surface = win->windowSurface();
+ if (!surface)
+ return;
+
+ const QImage &img = surface->image();
+ if (img.isNull())
+ return;
+
+ const QRegion rgn = clip & win->paintedRegion();
+ if (rgn.isEmpty())
+ return;
+
+ surface->lock();
+ blit(img, win->requestedRegion().boundingRect().topLeft(), rgn);
+ surface->unlock();
+}
+
+struct fill_data {
+ quint32 color;
+ uchar *data;
+ int lineStep;
+ int x;
+ int y;
+ int w;
+ int h;
+};
+
+/*!
+ Fills the given \a region of the screen with the specified \a
+ color.
+
+ This function is called from the exposeRegion() function; it is
+ not intended to be called explicitly.
+
+ Reimplement this function to make use of \l{Adding an Accelerated
+ Graphics Driver to Qt for Embedded Linux}{accelerated hardware}. Note that
+ this function must be reimplemented if the framebuffer format is
+ not supported by \l{Qt for Embedded Linux} (See the
+ \l{Qt for Embedded Linux Display Management}{Display Management}
+ documentation for more details).
+
+ \sa exposeRegion(), blit(), blank()
+*/
+// the base class implementation works in device coordinates, so that transformed drivers can use it
+void QScreen::solidFill(const QColor &color, const QRegion &region)
+{
+ QWSDisplay::grab();
+ d_ptr->solidFill(this, color,
+ region.translated(-offset()) & QRect(0, 0, dw, dh));
+ QWSDisplay::ungrab();
+}
+
+/*!
+ \since 4.2
+
+ Creates and returns a new window surface matching the given \a
+ key.
+
+ The server application will call this function whenever it needs
+ to create a server side representation of a window, e.g. when
+ copying the content of memory to the screen using the screen
+ driver.
+
+ Note that this function must be reimplemented when adding an
+ accelerated graphics driver. See the
+ \l{Adding an Accelerated Graphics Driver to Qt for Embedded Linux}
+ {Adding an Accelerated Graphics Driver} documentation for details.
+
+ \sa {Qt for Embedded Linux Architecture}
+*/
+QWSWindowSurface* QScreen::createSurface(const QString &key) const
+{
+#ifndef QT_NO_PAINTONSCREEN
+ if (key == QLatin1String("OnScreen"))
+ return new QWSOnScreenSurface;
+ else
+#endif
+ if (key == QLatin1String("mem"))
+ return new QWSLocalMemSurface;
+#ifndef QT_NO_QWS_MULTIPROCESS
+ else if (key == QLatin1String("shm"))
+ return new QWSSharedMemSurface;
+#endif
+#ifndef QT_NO_PAINT_DEBUG
+ else if (key == QLatin1String("Yellow"))
+ return new QWSYellowSurface;
+#endif
+#ifndef QT_NO_DIRECTPAINTER
+ else if (key == QLatin1String("DirectPainter"))
+ return new QWSDirectPainterSurface;
+#endif
+
+ return 0;
+}
+
+#ifndef QT_NO_PAINTONSCREEN
+bool QScreen::isWidgetPaintOnScreen(const QWidget *w)
+{
+ static int doOnScreen = -1;
+ if (doOnScreen == -1) {
+ const QByteArray env = qgetenv("QT_ONSCREEN_PAINT");
+ if (env == "force")
+ doOnScreen = 2;
+ else
+ doOnScreen = (env.toInt() > 0 ? 1 : 0);
+ }
+
+ if (doOnScreen == 2) // force
+ return true;
+
+ if (doOnScreen == 0 && !w->testAttribute(Qt::WA_PaintOnScreen))
+ return false;
+
+ return w->d_func()->isOpaque;
+}
+#endif
+
+/*!
+ \overload
+
+ Creates and returns a new window surface for the given \a widget.
+*/
+QWSWindowSurface* QScreen::createSurface(QWidget *widget) const
+{
+#ifndef QT_NO_PAINTONSCREEN
+ if (isWidgetPaintOnScreen(widget) && base())
+ return new QWSOnScreenSurface(widget);
+ else
+#endif
+ if (QApplication::type() == QApplication::GuiServer)
+ return new QWSLocalMemSurface(widget);
+#ifndef QT_NO_QWS_MULTIPROCESS
+ else
+ return new QWSSharedMemSurface(widget);
+#endif
+
+ return 0;
+}
+
+void QScreen::compose(int level, const QRegion &exposed, QRegion &blend,
+ QImage **blendbuffer, int changing_level)
+{
+ QRect exposed_bounds = exposed.boundingRect();
+ QWSWindow *win = 0;
+ do {
+ win = qwsServer->clientWindows().value(level); // null is background
+ ++level;
+ } while (win && !win->paintedRegion().boundingRect().intersects(exposed_bounds));
+
+ QWSWindowSurface *surface = (win ? win->windowSurface() : 0);
+ bool above_changing = level <= changing_level; // 0 is topmost
+
+ QRegion exposedBelow = exposed;
+ bool opaque = true;
+
+ if (win) {
+ opaque = win->isOpaque() || !surface->isBuffered();
+ if (opaque) {
+ exposedBelow -= win->paintedRegion();
+ if (above_changing || !surface->isBuffered())
+ blend -= exposed & win->paintedRegion();
+ } else {
+ blend += exposed & win->paintedRegion();
+ }
+ }
+ if (win && !exposedBelow.isEmpty()) {
+ compose(level, exposedBelow, blend, blendbuffer, changing_level);
+ } else {
+ QSize blendSize = blend.boundingRect().size();
+ if (!blendSize.isNull()) {
+ *blendbuffer = new QImage(blendSize, d_ptr->preferredImageFormat());
+ }
+ }
+
+ const QRegion blitRegion = exposed - blend;
+ if (!win)
+ paintBackground(blitRegion);
+ else if (!above_changing && surface->isBuffered())
+ blit(win, blitRegion);
+
+ QRegion blendRegion = exposed & blend;
+
+ if (win)
+ blendRegion &= win->paintedRegion();
+ if (!blendRegion.isEmpty()) {
+
+ QPoint off = blend.boundingRect().topLeft();
+
+ QRasterBuffer rb;
+ rb.prepare(*blendbuffer);
+ QSpanData spanData;
+ spanData.init(&rb, 0);
+ if (!win) {
+ const QImage::Format format = (*blendbuffer)->format();
+ switch (format) {
+ case QImage::Format_ARGB32_Premultiplied:
+ case QImage::Format_ARGB32:
+ case QImage::Format_ARGB8565_Premultiplied:
+ case QImage::Format_ARGB8555_Premultiplied:
+ case QImage::Format_ARGB6666_Premultiplied:
+ case QImage::Format_ARGB4444_Premultiplied:
+ spanData.rasterBuffer->compositionMode = QPainter::CompositionMode_Source;
+ break;
+ default:
+ break;
+ }
+ spanData.setup(qwsServer->backgroundBrush(), 256);
+ spanData.dx = off.x();
+ spanData.dy = off.y();
+ } else if (!surface->isBuffered()) {
+ return;
+ } else {
+ const QImage &img = surface->image();
+ QPoint winoff = off - win->requestedRegion().boundingRect().topLeft();
+ // convert win->opacity() from scale [0..255] to [0..256]
+ int const_alpha = win->opacity();
+ const_alpha += (const_alpha >> 7);
+ spanData.type = QSpanData::Texture;
+ spanData.initTexture(&img, const_alpha);
+ spanData.dx = winoff.x();
+ spanData.dy = winoff.y();
+ }
+ if (!spanData.blend)
+ return;
+
+ if (surface)
+ surface->lock();
+ const QVector<QRect> rects = blendRegion.rects();
+ const int nspans = 256;
+ QT_FT_Span spans[nspans];
+ for (int i = 0; i < rects.size(); ++i) {
+ int y = rects.at(i).y() - off.y();
+ int ye = y + rects.at(i).height();
+ int x = rects.at(i).x() - off.x();
+ int len = rects.at(i).width();
+ while (y < ye) {
+ int n = qMin(nspans, ye - y);
+ int i = 0;
+ while (i < n) {
+ spans[i].x = x;
+ spans[i].len = len;
+ spans[i].y = y + i;
+ spans[i].coverage = 255;
+ ++i;
+ }
+ spanData.blend(n, spans, &spanData);
+ y += n;
+ }
+ }
+ if (surface)
+ surface->unlock();
+ }
+}
+
+void QScreen::paintBackground(const QRegion &r)
+{
+ const QBrush &bg = qwsServer->backgroundBrush();
+ Qt::BrushStyle bs = bg.style();
+ if (bs == Qt::NoBrush || r.isEmpty())
+ return;
+
+ if (bs == Qt::SolidPattern) {
+ solidFill(bg.color(), r);
+ } else {
+ const QRect br = r.boundingRect();
+ QImage img(br.size(), d_ptr->preferredImageFormat());
+ QPoint off = br.topLeft();
+ QRasterBuffer rb;
+ rb.prepare(&img);
+ QSpanData spanData;
+ spanData.init(&rb, 0);
+ spanData.setup(bg, 256);
+ spanData.dx = off.x();
+ spanData.dy = off.y();
+ Q_ASSERT(spanData.blend);
+
+ const QVector<QRect> rects = r.rects();
+ const int nspans = 256;
+ QT_FT_Span spans[nspans];
+ for (int i = 0; i < rects.size(); ++i) {
+ int y = rects.at(i).y() - off.y();
+ int ye = y + rects.at(i).height();
+ int x = rects.at(i).x() - off.x();
+ int len = rects.at(i).width();
+ while (y < ye) {
+ int n = qMin(nspans, ye - y);
+ int i = 0;
+ while (i < n) {
+ spans[i].x = x;
+ spans[i].len = len;
+ spans[i].y = y + i;
+ spans[i].coverage = 255;
+ ++i;
+ }
+ spanData.blend(n, spans, &spanData);
+ y += n;
+ }
+ }
+ blit(img, br.topLeft(), r);
+ }
+}
+
+/*!
+ \fn virtual int QScreen::sharedRamSize(void *)
+
+ \internal
+*/
+
+/*!
+ \fn QScreen::setDirty(const QRect& rectangle)
+
+ Marks the given \a rectangle as dirty.
+
+ Note that the default implementation does nothing; reimplement
+ this function to indicate that the given \a rectangle has been
+ altered.
+*/
+
+void QScreen::setDirty(const QRect&)
+{
+}
+
+/*!
+ \fn QScreen::isTransformed() const
+
+ Returns true if the screen is transformed (for instance, rotated
+ 90 degrees); otherwise returns false.
+
+ \sa transformOrientation(), isInterlaced()
+*/
+
+bool QScreen::isTransformed() const
+{
+ return false;
+}
+
+/*!
+ \fn QScreen::isInterlaced() const
+
+ Returns true if the display is interlaced (i.e. is displaying
+ images progressively like a television screen); otherwise returns
+ false.
+
+ If the display is interlaced, the drawing is altered to look
+ better.
+
+ \sa isTransformed(), linestep()
+*/
+
+bool QScreen::isInterlaced() const
+{
+ return false;//qws_screen_is_interlaced;;
+}
+
+/*!
+ \fn QScreen::mapToDevice(const QSize &size) const
+
+ Maps the given \a size from the coordinate space used by the
+ application to the framebuffer coordinate system. Note that the
+ default implementation simply returns the given \a size as it is.
+
+ Reimplement this function to use the given device's coordinate
+ system when mapping.
+
+ \sa mapFromDevice()
+*/
+
+QSize QScreen::mapToDevice(const QSize &s) const
+{
+ return s;
+}
+
+/*!
+ \fn QScreen::mapFromDevice(const QSize &size) const
+
+ Maps the given \a size from the framebuffer coordinate system to
+ the coordinate space used by the application. Note that the
+ default implementation simply returns the given \a size as it is.
+
+ Reimplement this function to use the given device's coordinate
+ system when mapping.
+
+ \sa mapToDevice()
+*/
+
+QSize QScreen::mapFromDevice(const QSize &s) const
+{
+ return s;
+}
+
+/*!
+ \fn QScreen::mapToDevice(const QPoint &point, const QSize &screenSize) const
+ \overload
+
+ Maps the given \a point from the coordinate space used by the
+ application to the framebuffer coordinate system, passing the
+ device's \a screenSize as argument. Note that the default
+ implementation returns the given \a point as it is.
+*/
+
+QPoint QScreen::mapToDevice(const QPoint &p, const QSize &) const
+{
+ return p;
+}
+
+/*!
+ \fn QScreen::mapFromDevice(const QPoint &point, const QSize &screenSize) const
+ \overload
+
+ Maps the given \a point from the framebuffer coordinate system to
+ the coordinate space used by the application, passing the device's
+ \a screenSize as argument. Note that the default implementation
+ simply returns the given \a point as it is.
+*/
+
+QPoint QScreen::mapFromDevice(const QPoint &p, const QSize &) const
+{
+ return p;
+}
+
+/*!
+ \fn QScreen::mapToDevice(const QRect &rectangle, const QSize &screenSize) const
+ \overload
+
+ Maps the given \a rectangle from the coordinate space used by the
+ application to the framebuffer coordinate system, passing the
+ device's \a screenSize as argument. Note that the default
+ implementation returns the given \a rectangle as it is.
+*/
+
+QRect QScreen::mapToDevice(const QRect &r, const QSize &) const
+{
+ return r;
+}
+
+/*!
+ \fn QScreen::mapFromDevice(const QRect &rectangle, const QSize &screenSize) const
+ \overload
+
+ Maps the given \a rectangle from the framebuffer coordinate system to
+ the coordinate space used by the application, passing the device's
+ \a screenSize as argument. Note that the default implementation
+ simply returns the given \a rectangle as it is.
+*/
+
+QRect QScreen::mapFromDevice(const QRect &r, const QSize &) const
+{
+ return r;
+}
+
+/*!
+ \fn QScreen::mapToDevice(const QImage &image) const
+ \overload
+
+ Maps the given \a image from the coordinate space used by the
+ application to the framebuffer coordinate system. Note that the
+ default implementation returns the given \a image as it is.
+*/
+
+QImage QScreen::mapToDevice(const QImage &i) const
+{
+ return i;
+}
+
+/*!
+ \fn QScreen::mapFromDevice(const QImage &image) const
+ \overload
+
+ Maps the given \a image from the framebuffer coordinate system to
+ the coordinate space used by the application. Note that the
+ default implementation simply returns the given \a image as it is.
+*/
+
+QImage QScreen::mapFromDevice(const QImage &i) const
+{
+ return i;
+}
+
+/*!
+ \fn QScreen::mapToDevice(const QRegion &region, const QSize &screenSize) const
+ \overload
+
+ Maps the given \a region from the coordinate space used by the
+ application to the framebuffer coordinate system, passing the
+ device's \a screenSize as argument. Note that the default
+ implementation returns the given \a region as it is.
+*/
+
+QRegion QScreen::mapToDevice(const QRegion &r, const QSize &) const
+{
+ return r;
+}
+
+/*!
+ \fn QScreen::mapFromDevice(const QRegion &region, const QSize &screenSize) const
+ \overload
+
+ Maps the given \a region from the framebuffer coordinate system to
+ the coordinate space used by the application, passing the device's
+ \a screenSize as argument. Note that the default implementation
+ simply returns the given \a region as it is.
+*/
+
+QRegion QScreen::mapFromDevice(const QRegion &r, const QSize &) const
+{
+ return r;
+}
+
+/*!
+ \fn QScreen::transformOrientation() const
+
+ Returns the current rotation as an integer value.
+
+ Note that the default implementation returns 0; reimplement this
+ function to override this value.
+
+ \sa isTransformed()
+*/
+
+int QScreen::transformOrientation() const
+{
+ return 0;
+}
+
+int QScreen::pixmapDepth() const
+{
+ return depth();
+}
+
+/*!
+ \internal
+*/
+int QScreen::memoryNeeded(const QString&)
+{
+ return 0;
+}
+
+/*!
+ \internal
+*/
+void QScreen::haltUpdates()
+{
+}
+
+/*!
+ \internal
+*/
+void QScreen::resumeUpdates()
+{
+}
+
+/*!
+ \fn QRegion QScreen::region() const
+ \since 4.2
+
+ Returns the region covered by this screen driver.
+
+ \sa base(), screenSize()
+*/
+
+/*!
+ \internal
+*/
+void QScreen::setOffset(const QPoint &p)
+{
+ d_ptr->offset = p;
+}
+
+/*!
+ \since 4.2
+
+ Returns the logical offset of the screen, i.e., the offset between
+ (0,0) in screen coordinates and the application coordinate system.
+*/
+QPoint QScreen::offset() const
+{
+ return d_ptr->offset;
+}
+
+#if Q_BYTE_ORDER == Q_BIG_ENDIAN
+void QScreen::setFrameBufferLittleEndian(bool littleEndian)
+{
+ d_ptr->fb_is_littleEndian = littleEndian;
+}
+
+bool QScreen::frameBufferLittleEndian() const
+{
+ return d_ptr->fb_is_littleEndian;
+}
+#endif
+
+/*!
+ \fn int QScreen::subScreenIndexAt(const QPoint &position) const
+ \since 4.2
+
+ Returns the index of the subscreen at the given \a position;
+ returns -1 if no screen is found.
+
+ The index identifies the subscreen in the list of pointers
+ returned by the subScreens() function.
+
+ \sa instance(), subScreens()
+*/
+int QScreen::subScreenIndexAt(const QPoint &p) const
+{
+ const QList<QScreen*> screens = subScreens();
+ const int n = screens.count();
+ for (int i = 0; i < n; ++i) {
+ if (screens.at(i)->region().contains(p))
+ return i;
+ }
+
+ return -1;
+}
+
+#if 0
+#ifdef QT_LOADABLE_MODULES
+
+// ### needs update after driver init changes
+
+static QScreen * qt_dodriver(char * driver,char * a,unsigned char * b)
+
+{
+ char buf[200];
+ strcpy(buf,"/etc/qws/drivers/");
+ qstrcpy(buf+17,driver);
+ qDebug("Attempting driver %s",driver);
+
+ void * handle;
+ handle=dlopen(buf,RTLD_LAZY);
+ if(handle==0) {
+ qFatal("Module load error");
+ }
+ QScreen *(*qt_get_screen_func)(char *,unsigned char *);
+ qt_get_screen_func=dlsym(handle,"qt_get_screen");
+ if(qt_get_screen_func==0) {
+ qFatal("Couldn't get symbol");
+ }
+ QScreen * ret=qt_get_screen_func(a,b);
+ return ret;
+}
+
+static QScreen * qt_do_entry(char * entry)
+{
+ unsigned char config[256];
+
+ FILE * f=fopen(entry,"r");
+ if(!f) {
+ return 0;
+ }
+
+ int r=fread(config,256,1,f);
+ if(r<1)
+ return 0;
+
+ fclose(f);
+
+ unsigned short vendorid=*((unsigned short int *)config);
+ unsigned short deviceid=*(((unsigned short int *)config)+1);
+ if(config[0xb]!=3)
+ return 0;
+
+ if(vendorid==0x1002) {
+ if(deviceid==0x4c4d) {
+ qDebug("Compaq Armada/IBM Thinkpad's Mach64 card");
+ return qt_dodriver("mach64.so",entry,config);
+ } else if(deviceid==0x4742) {
+ qDebug("Desktop Rage Pro Mach64 card");
+ return qt_dodriver("mach64.so",entry,config);
+ } else {
+ qDebug("Unrecognised ATI card id %x",deviceid);
+ return 0;
+ }
+ } else {
+ qDebug("Unrecognised vendor");
+ }
+ return 0;
+}
+
+extern bool qws_accel;
+
+/// ** NOT SUPPPORTED **
+
+QScreen * qt_probe_bus()
+{
+ if(!qws_accel) {
+ return qt_dodriver("unaccel.so",0,0);
+ }
+
+ DIR * dirptr=opendir("/proc/bus/pci");
+ if(!dirptr)
+ return qt_dodriver("unaccel.so",0,0);
+ DIR * dirptr2;
+ dirent * cards;
+
+ dirent * busses=readdir(dirptr);
+
+ while(busses) {
+ if(busses->d_name[0]!='.') {
+ char buf[100];
+ strcpy(buf,"/proc/bus/pci/");
+ qstrcpy(buf+14,busses->d_name);
+ int p=strlen(buf);
+ dirptr2=opendir(buf);
+ if(dirptr2) {
+ cards=readdir(dirptr2);
+ while(cards) {
+ if(cards->d_name[0]!='.') {
+ buf[p]='/';
+ qstrcpy(buf+p+1,cards->d_name);
+ QScreen * ret=qt_do_entry(buf);
+ if(ret)
+ return ret;
+ }
+ cards=readdir(dirptr2);
+ }
+ closedir(dirptr2);
+ }
+ }
+ busses=readdir(dirptr);
+ }
+ closedir(dirptr);
+
+ return qt_dodriver("unaccel.so",0,0);
+}
+
+#else
+
+char *qt_qws_hardcoded_slot = "/proc/bus/pci/01/00.0";
+
+const unsigned char* qt_probe_bus()
+{
+ const char * slot;
+ slot=::getenv("QWS_CARD_SLOT");
+ if(!slot)
+ slot=qt_qws_hardcoded_slot;
+ if (slot) {
+ static unsigned char config[256];
+ FILE * f=fopen(slot,"r");
+ if(!f) {
+ qDebug("Open failure for %s",slot);
+ slot=0;
+ } else {
+ int r=fread((char*)config,256,1,f);
+ fclose(f);
+ if(r<1) {
+ qDebug("Read failure");
+ return 0;
+ } else {
+ return config;
+ }
+ }
+ }
+ return 0;
+}
+
+#endif
+
+#endif // 0
+
+/*!
+ \internal
+ \since 4.4
+*/
+void QScreen::setPixmapDataFactory(QPixmapDataFactory *factory)
+{
+ static bool shownWarning = false;
+ if (!shownWarning) {
+ qWarning("QScreen::setPixmapDataFactory() is deprecated - use setGraphicsSystem() instead");
+ shownWarning = true;
+ }
+
+ d_ptr->pixmapFactory = factory;
+}
+
+/*!
+ \internal
+ \since 4.4
+*/
+QPixmapDataFactory* QScreen::pixmapDataFactory() const
+{
+ return d_ptr->pixmapFactory;
+}
+
+/*!
+ \internal
+ \since 4.5
+*/
+void QScreen::setGraphicsSystem(QGraphicsSystem* system)
+{
+ d_ptr->graphicsSystem = system;
+}
+
+/*!
+ \internal
+ \since 4.5
+*/
+QGraphicsSystem* QScreen::graphicsSystem() const
+{
+ return d_ptr->graphicsSystem;
+}
+
+/*!
+ \since 4.4
+
+ Returns the class identifier for the screen object.
+*/
+QScreen::ClassId QScreen::classId() const
+{
+ return static_cast<ClassId>(d_ptr->classId);
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/embedded/qscreen_qws.h b/src/gui/embedded/qscreen_qws.h
new file mode 100644
index 0000000000..e7773178cd
--- /dev/null
+++ b/src/gui/embedded/qscreen_qws.h
@@ -0,0 +1,387 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSCREEN_QWS_H
+#define QSCREEN_QWS_H
+
+#include <QtCore/qnamespace.h>
+#include <QtCore/qpoint.h>
+#include <QtCore/qlist.h>
+#include <QtGui/qrgb.h>
+#include <QtCore/qrect.h>
+#include <QtGui/qimage.h>
+#include <QtGui/qregion.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+class QScreenCursor;
+class QBrush;
+class QWSWindow;
+class QWSWindowSurface;
+class QGraphicsSystem;
+class QPixmapData;
+
+#ifndef QT_QWS_DEPTH16_RGB
+#define QT_QWS_DEPTH16_RGB 565
+#endif
+static const int qt_rbits = (QT_QWS_DEPTH16_RGB/100);
+static const int qt_gbits = (QT_QWS_DEPTH16_RGB/10%10);
+static const int qt_bbits = (QT_QWS_DEPTH16_RGB%10);
+static const int qt_red_shift = qt_bbits+qt_gbits-(8-qt_rbits);
+static const int qt_green_shift = qt_bbits-(8-qt_gbits);
+static const int qt_neg_blue_shift = 8-qt_bbits;
+static const int qt_blue_mask = (1<<qt_bbits)-1;
+static const int qt_green_mask = (1<<(qt_gbits+qt_bbits))-(1<<qt_bbits);
+static const int qt_red_mask = (1<<(qt_rbits+qt_gbits+qt_bbits))-(1<<(qt_gbits+qt_bbits));
+
+static const int qt_red_rounding_shift = qt_red_shift + qt_rbits;
+static const int qt_green_rounding_shift = qt_green_shift + qt_gbits;
+static const int qt_blue_rounding_shift = qt_bbits - qt_neg_blue_shift;
+
+
+inline ushort qt_convRgbTo16(const int r, const int g, const int b)
+{
+ const int tr = r << qt_red_shift;
+ const int tg = g << qt_green_shift;
+ const int tb = b >> qt_neg_blue_shift;
+
+ return (tb & qt_blue_mask) | (tg & qt_green_mask) | (tr & qt_red_mask);
+}
+
+inline ushort qt_convRgbTo16(QRgb c)
+{
+ const int tr = qRed(c) << qt_red_shift;
+ const int tg = qGreen(c) << qt_green_shift;
+ const int tb = qBlue(c) >> qt_neg_blue_shift;
+
+ return (tb & qt_blue_mask) | (tg & qt_green_mask) | (tr & qt_red_mask);
+}
+
+inline QRgb qt_conv16ToRgb(ushort c)
+{
+ const int r=(c & qt_red_mask);
+ const int g=(c & qt_green_mask);
+ const int b=(c & qt_blue_mask);
+ const int tr = r >> qt_red_shift | r >> qt_red_rounding_shift;
+ const int tg = g >> qt_green_shift | g >> qt_green_rounding_shift;
+ const int tb = b << qt_neg_blue_shift | b >> qt_blue_rounding_shift;
+
+ return qRgb(tr,tg,tb);
+}
+
+inline void qt_conv16ToRgb(ushort c, int& r, int& g, int& b)
+{
+ const int tr=(c & qt_red_mask);
+ const int tg=(c & qt_green_mask);
+ const int tb=(c & qt_blue_mask);
+ r = tr >> qt_red_shift | tr >> qt_red_rounding_shift;
+ g = tg >> qt_green_shift | tg >> qt_green_rounding_shift;
+ b = tb << qt_neg_blue_shift | tb >> qt_blue_rounding_shift;
+}
+
+const int SourceSolid=0;
+const int SourcePixmap=1;
+
+#ifndef QT_NO_QWS_CURSOR
+
+class QScreenCursor;
+extern QScreenCursor *qt_screencursor;
+extern bool qt_sw_cursor;
+
+class Q_GUI_EXPORT QScreenCursor
+{
+public:
+ QScreenCursor();
+ virtual ~QScreenCursor();
+
+ virtual void set(const QImage &image, int hotx, int hoty);
+ virtual void move(int x, int y);
+ virtual void show();
+ virtual void hide();
+
+ bool supportsAlphaCursor() const { return supportsAlpha; }
+
+ static bool enabled() { return qt_sw_cursor; }
+
+ QRect boundingRect() const { return QRect(pos - hotspot, size); }
+ QImage image() const { return cursor; }
+ bool isVisible() const { return enable; }
+ bool isAccelerated() const { return hwaccel; }
+
+ static void initSoftwareCursor();
+ static QScreenCursor* instance() { return qt_screencursor; }
+
+protected:
+ QImage cursor;
+
+ QSize size;
+ QPoint pos;
+ QPoint hotspot;
+ uint enable : 1;
+ uint hwaccel : 1;
+ uint supportsAlpha : 1;
+
+private:
+ friend class QProxyScreenCursor;
+};
+
+#endif // QT_NO_QWS_CURSOR
+
+struct fb_cmap;
+
+// A (used) chunk of offscreen memory
+
+class QPoolEntry
+{
+public:
+ unsigned int start;
+ unsigned int end;
+ int clientId;
+};
+
+class QScreen;
+class QScreenPrivate;
+class QPixmapDataFactory;
+
+extern Q_GUI_EXPORT QScreen *qt_screen;
+typedef void(*ClearCacheFunc)(QScreen *obj, int);
+
+class Q_GUI_EXPORT QScreen {
+
+public:
+ enum ClassId { LinuxFBClass, TransformedClass, VNCClass, MultiClass,
+ VFbClass, DirectFBClass, SvgalibClass, ProxyClass,
+ GLClass, CustomClass = 1024 };
+
+ QScreen(int display_id, ClassId classId);
+ explicit QScreen(int display_id);
+ virtual ~QScreen();
+ static QScreen* instance() { return qt_screen; }
+ virtual bool initDevice() = 0;
+ virtual bool connect(const QString &displaySpec) = 0;
+ virtual void disconnect() = 0;
+ virtual void shutdownDevice();
+ virtual void setMode(int,int,int) = 0;
+ virtual bool supportsDepth(int) const;
+
+ virtual void save();
+ virtual void restore();
+ virtual void blank(bool on);
+
+ virtual int pixmapOffsetAlignment() { return 64; }
+ virtual int pixmapLinestepAlignment() { return 64; }
+ virtual int sharedRamSize(void *) { return 0; }
+
+ virtual bool onCard(const unsigned char *) const;
+ virtual bool onCard(const unsigned char *, ulong& out_offset) const;
+
+ enum PixelType { NormalPixel, BGRPixel };
+
+ // sets a single color in the colormap
+ virtual void set(unsigned int,unsigned int,unsigned int,unsigned int);
+ // allocates a color
+ virtual int alloc(unsigned int,unsigned int,unsigned int);
+
+ int width() const { return w; }
+ int height() const { return h; }
+ int depth() const { return d; }
+ virtual int pixmapDepth() const;
+ PixelType pixelType() const { return pixeltype; }
+ int linestep() const { return lstep; }
+ int deviceWidth() const { return dw; }
+ int deviceHeight() const { return dh; }
+ uchar * base() const { return data; }
+ // Ask for memory from card cache with alignment
+ virtual uchar * cache(int) { return 0; }
+ virtual void uncache(uchar *) {}
+
+ QImage::Format pixelFormat() const;
+
+ int screenSize() const { return size; }
+ int totalSize() const { return mapsize; }
+
+ QRgb * clut() { return screenclut; }
+ int numCols() { return screencols; }
+
+ virtual QSize mapToDevice(const QSize &) const;
+ virtual QSize mapFromDevice(const QSize &) const;
+ virtual QPoint mapToDevice(const QPoint &, const QSize &) const;
+ virtual QPoint mapFromDevice(const QPoint &, const QSize &) const;
+ virtual QRect mapToDevice(const QRect &, const QSize &) const;
+ virtual QRect mapFromDevice(const QRect &, const QSize &) const;
+ virtual QImage mapToDevice(const QImage &) const;
+ virtual QImage mapFromDevice(const QImage &) const;
+ virtual QRegion mapToDevice(const QRegion &, const QSize &) const;
+ virtual QRegion mapFromDevice(const QRegion &, const QSize &) const;
+ virtual int transformOrientation() const;
+ virtual bool isTransformed() const;
+ virtual bool isInterlaced() const;
+
+ virtual void setDirty(const QRect&);
+
+ virtual int memoryNeeded(const QString&);
+
+ virtual void haltUpdates();
+ virtual void resumeUpdates();
+
+ // composition manager methods
+ virtual void exposeRegion(QRegion r, int changing);
+
+ // these work directly on the screen
+ virtual void blit(const QImage &img, const QPoint &topLeft, const QRegion &region);
+ virtual void solidFill(const QColor &color, const QRegion &region);
+ void blit(QWSWindow *bs, const QRegion &clip);
+
+ virtual QWSWindowSurface* createSurface(QWidget *widget) const;
+ virtual QWSWindowSurface* createSurface(const QString &key) const;
+
+ virtual QList<QScreen*> subScreens() const { return QList<QScreen*>(); }
+ virtual QRegion region() const { return QRect(offset(), QSize(w, h)); }
+ int subScreenIndexAt(const QPoint &p) const;
+
+ void setOffset(const QPoint &p);
+ QPoint offset() const;
+
+ int physicalWidth() const { return physWidth; } // physical display size in mm
+ int physicalHeight() const { return physHeight; } // physical display size in mm
+
+ QPixmapDataFactory* pixmapDataFactory() const; // Deprecated, will be removed in 4.6
+ QGraphicsSystem* graphicsSystem() const;
+
+#ifdef QT_QWS_CLIENTBLIT
+ bool supportsBlitInClients() const;
+ void setSupportsBlitInClients(bool);
+#endif
+
+ ClassId classId() const;
+
+protected:
+ void setPixelFormat(QImage::Format format);
+ void setPixmapDataFactory(QPixmapDataFactory *factory); // Deprecated, will be removed in 4.6
+ void setGraphicsSystem(QGraphicsSystem* system);
+
+ QRgb screenclut[256];
+ int screencols;
+
+ uchar * data;
+
+ // Table of allocated lumps, kept in sorted highest-to-lowest order
+ // The table itself is allocated at the bottom of offscreen memory
+ // i.e. it's similar to having a stack (the table) and a heap
+ // (the allocated blocks). Freed space is implicitly described
+ // by the gaps between the allocated lumps (this saves entries and
+ // means we don't need to worry about coalescing freed lumps)
+
+ QPoolEntry * entries;
+ int * entryp;
+ unsigned int * lowest;
+
+ int w;
+ int lstep;
+ int h;
+ int d;
+ PixelType pixeltype;
+ bool grayscale;
+
+ int dw;
+ int dh;
+
+ int size; // Screen size
+ int mapsize; // Total mapped memory
+
+ int displayId;
+
+ int physWidth;
+ int physHeight;
+
+ friend class QWSServer;
+ friend class QWSServerPrivate;
+ static ClearCacheFunc clearCacheFunc;
+
+private:
+ void compose(int level, const QRegion &exposed, QRegion &blend,
+ QImage **blendbuffer, int changing_level);
+ void paintBackground(const QRegion &);
+
+ friend class QWSOnScreenSurface;
+ static bool isWidgetPaintOnScreen(const QWidget *w);
+
+#if Q_BYTE_ORDER == Q_BIG_ENDIAN
+ void setFrameBufferLittleEndian(bool littleEndian);
+ bool frameBufferLittleEndian() const;
+ friend class QVNCScreen;
+ friend class QLinuxFbScreen;
+ friend class QVFbScreen;
+ friend class QProxyScreen;
+#endif
+ friend void qt_solidFill_setup(QScreen*, const QColor&, const QRegion&);
+ friend void qt_blit_setup(QScreen *screen, const QImage &image,
+ const QPoint &topLeft, const QRegion &region);
+#ifdef QT_QWS_DEPTH_GENERIC
+ friend void qt_set_generic_blit(QScreen *screen, int bpp,
+ int len_red, int len_green, int len_blue,
+ int len_alpha, int off_red, int off_green,
+ int off_blue, int off_alpha);
+#endif
+
+ QScreenPrivate *d_ptr;
+};
+
+// This lives in loadable modules
+
+#ifndef QT_LOADABLE_MODULES
+extern "C" QScreen * qt_get_screen(int display_id, const char* spec);
+#endif
+
+// This is in main lib, loads the right module, calls qt_get_screen
+// In non-loadable cases just aliases to qt_get_screen
+
+const unsigned char * qt_probe_bus();
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QSCREEN_QWS_H
diff --git a/src/gui/embedded/qscreendriverfactory_qws.cpp b/src/gui/embedded/qscreendriverfactory_qws.cpp
new file mode 100644
index 0000000000..8b17fbae7f
--- /dev/null
+++ b/src/gui/embedded/qscreendriverfactory_qws.cpp
@@ -0,0 +1,183 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qscreendriverfactory_qws.h"
+
+#include "qscreen_qws.h"
+#include "qapplication.h"
+#include "qscreenlinuxfb_qws.h"
+#include "qscreentransformed_qws.h"
+#include "qscreenvfb_qws.h"
+#include "qscreenmulti_qws_p.h"
+#include <stdlib.h>
+#include "private/qfactoryloader_p.h"
+#include "qscreendriverplugin_qws.h"
+
+#ifndef QT_NO_QWS_VNC
+#include "qscreenvnc_qws.h"
+#endif
+
+QT_BEGIN_NAMESPACE
+
+#if !defined(Q_OS_WIN32) || defined(QT_MAKEDLL)
+#ifndef QT_NO_LIBRARY
+
+Q_GLOBAL_STATIC_WITH_ARGS(QFactoryLoader, loader,
+ (QScreenDriverFactoryInterface_iid,
+ QLatin1String("/gfxdrivers"), Qt::CaseInsensitive))
+
+#endif //QT_NO_LIBRARY
+#endif //QT_MAKEDLL
+
+/*!
+ \class QScreenDriverFactory
+ \ingroup qws
+
+ \brief The QScreenDriverFactory class creates screen drivers in
+ Qt for Embedded Linux.
+
+ Note that this class is only available in \l{Qt for Embedded Linux}.
+
+ QScreenDriverFactory is used to detect and instantiate the
+ available screen drivers, allowing \l{Qt for Embedded Linux} to load the
+ preferred driver into the server application at runtime. The
+ create() function returns a QScreen object representing the screen
+ driver identified by a given key. The valid keys (i.e. the
+ supported drivers) can be retrieved using the keys() function.
+
+
+ \l{Qt for Embedded Linux} provides several built-in screen drivers. In
+ addition, custom screen drivers can be added using Qt's plugin
+ mechanism, i.e. by subclassing the QScreen class and creating a
+ screen driver plugin (QScreenDriverPlugin). See the
+ \l{Qt for Embedded Linux Display Management}{display management}
+ documentation for details.
+
+ \sa QScreen, QScreenDriverPlugin
+*/
+
+/*!
+ Creates the screen driver specified by the given \a key, using the
+ display specified by the given \a displayId.
+
+ Note that the keys are case-insensitive.
+
+ \sa keys()
+*/
+QScreen *QScreenDriverFactory::create(const QString& key, int displayId)
+{
+ QString driver = key.toLower();
+#ifndef QT_NO_QWS_QVFB
+ if (driver == QLatin1String("qvfb") || driver.isEmpty())
+ return new QVFbScreen(displayId);
+#endif
+#ifndef QT_NO_QWS_LINUXFB
+ if (driver == QLatin1String("linuxfb") || driver.isEmpty())
+ return new QLinuxFbScreen(displayId);
+#endif
+#ifndef QT_NO_QWS_TRANSFORMED
+ if (driver == QLatin1String("transformed"))
+ return new QTransformedScreen(displayId);
+#endif
+#ifndef QT_NO_QWS_VNC
+ if (driver == QLatin1String("vnc"))
+ return new QVNCScreen(displayId);
+#endif
+#ifndef QT_NO_QWS_MULTISCREEN
+ if (driver == QLatin1String("multi"))
+ return new QMultiScreen(displayId);
+#endif
+
+#if !defined(Q_OS_WIN32) || defined(QT_MAKEDLL)
+#ifndef QT_NO_LIBRARY
+
+ if (QScreenDriverFactoryInterface *factory = qobject_cast<QScreenDriverFactoryInterface*>(loader()->instance(key)))
+ return factory->create(driver, displayId);
+
+#endif
+#endif
+ return 0;
+}
+
+/*!
+ Returns the list of valid keys, i.e. the available screen drivers.
+
+ \sa create()
+*/
+QStringList QScreenDriverFactory::keys()
+{
+ QStringList list;
+
+#ifndef QT_NO_QWS_QVFB
+ list << QLatin1String("QVFb");
+#endif
+#ifndef QT_NO_QWS_LINUXFB
+ list << QLatin1String("LinuxFb");
+#endif
+#ifndef QT_NO_QWS_TRANSFORMED
+ list << QLatin1String("Transformed");
+#endif
+#ifndef QT_NO_QWS_VNC
+ list << QLatin1String("VNC");
+#endif
+#ifndef QT_NO_QWS_MULTISCREEN
+ list << QLatin1String("Multi");
+#endif
+
+#if !defined(Q_OS_WIN32) || defined(QT_MAKEDLL)
+#ifndef QT_NO_LIBRARY
+ QStringList plugins = loader()->keys();
+ for (int i = 0; i < plugins.size(); ++i) {
+# ifdef QT_NO_QWS_QVFB
+ // give QVFb top priority for autodetection
+ if (plugins.at(i) == QLatin1String("QVFb"))
+ list.prepend(plugins.at(i));
+ else
+# endif
+ if (!list.contains(plugins.at(i)))
+ list += plugins.at(i);
+ }
+#endif //QT_NO_LIBRARY
+#endif //QT_MAKEDLL
+ return list;
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/embedded/qscreendriverfactory_qws.h b/src/gui/embedded/qscreendriverfactory_qws.h
new file mode 100644
index 0000000000..eb9364cc21
--- /dev/null
+++ b/src/gui/embedded/qscreendriverfactory_qws.h
@@ -0,0 +1,67 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSCREENDRIVERFACTORY_QWS_H
+#define QSCREENDRIVERFACTORY_QWS_H
+
+#include <QtCore/qstringlist.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+class QString;
+class QScreen;
+
+class Q_GUI_EXPORT QScreenDriverFactory
+{
+public:
+ static QStringList keys();
+ static QScreen *create(const QString&, int);
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QSCREENDRIVERFACTORY_QWS_H
diff --git a/src/gui/embedded/qscreendriverplugin_qws.cpp b/src/gui/embedded/qscreendriverplugin_qws.cpp
new file mode 100644
index 0000000000..5429bde4dd
--- /dev/null
+++ b/src/gui/embedded/qscreendriverplugin_qws.cpp
@@ -0,0 +1,123 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qscreendriverplugin_qws.h"
+
+QT_BEGIN_NAMESPACE
+
+#ifndef QT_NO_LIBRARY
+
+/*!
+ \class QScreenDriverPlugin
+ \ingroup plugins
+ \ingroup qws
+
+ \brief The QScreenDriverPlugin class is an abstract base class for
+ screen driver plugins in Qt for Embedded Linux.
+
+ Note that this class is only available in \l{Qt for Embedded Linux}.
+
+ \l{Qt for Embedded Linux} provides ready-made drivers for several screen
+ protocols, see the \l{Qt for Embedded Linux Display Management}{display
+ management} documentation for details. Custom screen drivers can be
+ implemented by subclassing the QScreen class and creating a screen
+ driver plugin.
+
+ A screen driver plugin can be created by subclassing
+ QScreenDriverPlugin and reimplementing the pure virtual keys() and
+ create() functions. By exporting the derived class using the
+ Q_EXPORT_PLUGIN2() macro, The default implementation of the
+ QScreenDriverFactory class will automatically detect the plugin
+ and load the driver into the server application at run-time. See
+ \l{How to Create Qt Plugins} for details.
+
+ \sa QScreen, QScreenDriverFactory
+*/
+
+/*!
+ \fn QStringList QScreenDriverPlugin::keys() const
+
+ Implement this function to return the list of valid keys, i.e. the
+ screen drivers supported by this plugin.
+
+ \l{Qt for Embedded Linux} provides ready-made drivers for several screen
+ protocols, see the \l{Qt for Embedded Linux Display Management}{display
+ management} documentation for details.
+
+ \sa create()
+*/
+
+/*!
+ Constructs a screen driver plugin with the given \a parent.
+
+ Note that this constructor is invoked automatically by the
+ Q_EXPORT_PLUGIN2() macro, so there is no need for calling it
+ explicitly.
+*/
+QScreenDriverPlugin::QScreenDriverPlugin(QObject *parent)
+ : QObject(parent)
+{
+}
+
+/*!
+ Destroys this screen driver plugin.
+
+ Note that Qt destroys a plugin automatically when it is no longer
+ used, so there is no need for calling the destructor explicitly.
+*/
+QScreenDriverPlugin::~QScreenDriverPlugin()
+{
+}
+
+
+/*!
+ \fn QScreen* QScreenDriverPlugin::create(const QString &key, int displayId)
+
+ Implement this function to create a driver matching the type
+ specified by the given \a key and \a displayId parameters. Note
+ that keys are case-insensitive.
+
+ \sa keys()
+*/
+
+#endif // QT_NO_LIBRARY
+
+QT_END_NAMESPACE
diff --git a/src/gui/embedded/qscreendriverplugin_qws.h b/src/gui/embedded/qscreendriverplugin_qws.h
new file mode 100644
index 0000000000..f7dd0fce1d
--- /dev/null
+++ b/src/gui/embedded/qscreendriverplugin_qws.h
@@ -0,0 +1,84 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSCREENDRIVERPLUGIN_QWS_H
+#define QSCREENDRIVERPLUGIN_QWS_H
+
+#include <QtCore/qplugin.h>
+#include <QtCore/qfactoryinterface.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+#ifndef QT_NO_LIBRARY
+
+class QScreen;
+
+struct Q_GUI_EXPORT QScreenDriverFactoryInterface : public QFactoryInterface
+{
+ virtual QScreen* create(const QString& driver, int displayId) = 0;
+};
+
+#define QScreenDriverFactoryInterface_iid "com.trolltech.Qt.QScreenDriverFactoryInterface"
+Q_DECLARE_INTERFACE(QScreenDriverFactoryInterface, QScreenDriverFactoryInterface_iid)
+
+class Q_GUI_EXPORT QScreenDriverPlugin : public QObject, public QScreenDriverFactoryInterface
+{
+ Q_OBJECT
+ Q_INTERFACES(QScreenDriverFactoryInterface:QFactoryInterface)
+public:
+ explicit QScreenDriverPlugin(QObject *parent = 0);
+ ~QScreenDriverPlugin();
+
+ virtual QStringList keys() const = 0;
+ virtual QScreen *create(const QString& driver, int displayId) = 0;
+};
+
+#endif // QT_NO_LIBRARY
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QSCREENDRIVERPLUGIN_QWS_H
diff --git a/src/gui/embedded/qscreenlinuxfb_qws.cpp b/src/gui/embedded/qscreenlinuxfb_qws.cpp
new file mode 100644
index 0000000000..48fe881600
--- /dev/null
+++ b/src/gui/embedded/qscreenlinuxfb_qws.cpp
@@ -0,0 +1,1324 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qscreenlinuxfb_qws.h"
+
+#ifndef QT_NO_QWS_LINUXFB
+//#include "qmemorymanager_qws.h"
+#include "qwsdisplay_qws.h"
+#include "qpixmap.h"
+#include <private/qwssignalhandler_p.h>
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <sys/kd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <stdio.h>
+#include <limits.h>
+#include <signal.h>
+
+#include "qwindowsystem_qws.h"
+
+#if !defined(Q_OS_DARWIN) && !defined(Q_OS_FREEBSD)
+#include <linux/fb.h>
+
+#ifdef __i386__
+#include <asm/mtrr.h>
+#endif
+#endif
+
+QT_BEGIN_NAMESPACE
+
+extern int qws_client_id;
+
+//#define DEBUG_CACHE
+
+class QLinuxFbScreenPrivate : public QObject
+{
+public:
+ QLinuxFbScreenPrivate();
+ ~QLinuxFbScreenPrivate();
+
+ void openTty();
+ void closeTty();
+
+ int fd;
+ int startupw;
+ int startuph;
+ int startupd;
+ bool blank;
+
+ bool doGraphicsMode;
+#ifdef QT_QWS_DEPTH_GENERIC
+ bool doGenericColors;
+#endif
+ int ttyfd;
+ long oldKdMode;
+ QString ttyDevice;
+ QString displaySpec;
+};
+
+QLinuxFbScreenPrivate::QLinuxFbScreenPrivate()
+ : fd(-1), blank(true), doGraphicsMode(true),
+#ifdef QT_QWS_DEPTH_GENERIC
+ doGenericColors(false),
+#endif
+ ttyfd(-1), oldKdMode(KD_TEXT)
+{
+ QWSSignalHandler::instance()->addObject(this);
+}
+
+QLinuxFbScreenPrivate::~QLinuxFbScreenPrivate()
+{
+ closeTty();
+}
+
+void QLinuxFbScreenPrivate::openTty()
+{
+ const char *const devs[] = {"/dev/tty0", "/dev/tty", "/dev/console", 0};
+
+ if (ttyDevice.isEmpty()) {
+ for (const char * const *dev = devs; *dev; ++dev) {
+ ttyfd = ::open(*dev, O_RDWR);
+ if (ttyfd != -1)
+ break;
+ }
+ } else {
+ ttyfd = ::open(ttyDevice.toAscii().constData(), O_RDWR);
+ }
+
+ if (ttyfd == -1)
+ return;
+
+ if (doGraphicsMode) {
+ ioctl(ttyfd, KDGETMODE, &oldKdMode);
+ if (oldKdMode != KD_GRAPHICS) {
+ int ret = ioctl(ttyfd, KDSETMODE, KD_GRAPHICS);
+ if (ret == -1)
+ doGraphicsMode = false;
+ }
+ }
+
+ // No blankin' screen, no blinkin' cursor!, no cursor!
+ const char termctl[] = "\033[9;0]\033[?33l\033[?25l\033[?1c";
+ ::write(ttyfd, termctl, sizeof(termctl));
+}
+
+void QLinuxFbScreenPrivate::closeTty()
+{
+ if (ttyfd == -1)
+ return;
+
+ if (doGraphicsMode)
+ ioctl(ttyfd, KDSETMODE, oldKdMode);
+
+ // Blankin' screen, blinkin' cursor!
+ const char termctl[] = "\033[9;15]\033[?33h\033[?25h\033[?0c";
+ ::write(ttyfd, termctl, sizeof(termctl));
+
+ ::close(ttyfd);
+ ttyfd = -1;
+}
+
+/*!
+ \internal
+
+ \class QLinuxFbScreen
+ \ingroup qws
+
+ \brief The QLinuxFbScreen class implements a screen driver for the
+ Linux framebuffer.
+
+ Note that this class is only available in \l{Qt for Embedded Linux}.
+ Custom screen drivers can be added by subclassing the
+ QScreenDriverPlugin class, using the QScreenDriverFactory class to
+ dynamically load the driver into the application, but there should
+ only be one screen object per application.
+
+ The QLinuxFbScreen class provides the cache() function allocating
+ off-screen graphics memory, and the complementary uncache()
+ function releasing the allocated memory. The latter function will
+ first sync the graphics card to ensure the memory isn't still
+ being used by a command in the graphics card FIFO queue. The
+ deleteEntry() function deletes the given memory block without such
+ synchronization. Given the screen instance and client id, the
+ memory can also be released using the clearCache() function, but
+ this should only be necessary if a client exits abnormally.
+
+ In addition, when in paletted graphics modes, the set() function
+ provides the possibility of setting a specified color index to a
+ given RGB value.
+
+ The QLinuxFbScreen class also acts as a factory for the
+ unaccelerated screen cursor and the unaccelerated raster-based
+ implementation of QPaintEngine (\c QRasterPaintEngine);
+ accelerated drivers for Linux should derive from this class.
+
+ \sa QScreen, QScreenDriverPlugin, {Running Applications}
+*/
+
+/*!
+ \fn bool QLinuxFbScreen::useOffscreen()
+ \internal
+*/
+
+// Unaccelerated screen/driver setup. Can be overridden by accelerated
+// drivers
+
+/*!
+ \fn QLinuxFbScreen::QLinuxFbScreen(int displayId)
+
+ Constructs a QLinuxFbScreen object. The \a displayId argument
+ identifies the Qt for Embedded Linux server to connect to.
+*/
+
+QLinuxFbScreen::QLinuxFbScreen(int display_id)
+ : QScreen(display_id, LinuxFBClass), d_ptr(new QLinuxFbScreenPrivate)
+{
+ canaccel=false;
+ clearCacheFunc = &clearCache;
+#ifdef QT_QWS_CLIENTBLIT
+ setSupportsBlitInClients(true);
+#endif
+}
+
+/*!
+ Destroys this QLinuxFbScreen object.
+*/
+
+QLinuxFbScreen::~QLinuxFbScreen()
+{
+}
+
+/*!
+ \reimp
+
+ This is called by \l{Qt for Embedded Linux} clients to map in the framebuffer.
+ It should be reimplemented by accelerated drivers to map in
+ graphics card registers; those drivers should then call this
+ function in order to set up offscreen memory management. The
+ device is specified in \a displaySpec; e.g. "/dev/fb".
+
+ \sa disconnect()
+*/
+
+bool QLinuxFbScreen::connect(const QString &displaySpec)
+{
+ d_ptr->displaySpec = displaySpec;
+
+ const QStringList args = displaySpec.split(QLatin1Char(':'));
+
+ if (args.contains(QLatin1String("nographicsmodeswitch")))
+ d_ptr->doGraphicsMode = false;
+
+#ifdef QT_QWS_DEPTH_GENERIC
+ if (args.contains(QLatin1String("genericcolors")))
+ d_ptr->doGenericColors = true;
+#endif
+
+ QRegExp ttyRegExp(QLatin1String("tty=(.*)"));
+ if (args.indexOf(ttyRegExp) != -1)
+ d_ptr->ttyDevice = ttyRegExp.cap(1);
+
+#if Q_BYTE_ORDER == Q_BIG_ENDIAN
+#ifndef QT_QWS_FRAMEBUFFER_LITTLE_ENDIAN
+ if (args.contains(QLatin1String("littleendian")))
+#endif
+ QScreen::setFrameBufferLittleEndian(true);
+#endif
+
+ // Check for explicitly specified device
+ const int len = 8; // "/dev/fbx"
+ int m = displaySpec.indexOf(QLatin1String("/dev/fb"));
+
+ QString dev;
+ if (m > 0)
+ dev = displaySpec.mid(m, len);
+ else
+ dev = QLatin1String("/dev/fb0");
+
+ if (access(dev.toLatin1().constData(), R_OK|W_OK) == 0)
+ d_ptr->fd = open(dev.toLatin1().constData(), O_RDWR);
+ if (d_ptr->fd == -1) {
+ if (QApplication::type() == QApplication::GuiServer) {
+ perror("QScreenLinuxFb::connect");
+ qCritical("Error opening framebuffer device %s", qPrintable(dev));
+ return false;
+ }
+ if (access(dev.toLatin1().constData(), R_OK) == 0)
+ d_ptr->fd = open(dev.toLatin1().constData(), O_RDONLY);
+ }
+
+ fb_fix_screeninfo finfo;
+ fb_var_screeninfo vinfo;
+ //#######################
+ // Shut up Valgrind
+ memset(&vinfo, 0, sizeof(vinfo));
+ memset(&finfo, 0, sizeof(finfo));
+ //#######################
+
+ /* Get fixed screen information */
+ if (d_ptr->fd != -1 && ioctl(d_ptr->fd, FBIOGET_FSCREENINFO, &finfo)) {
+ perror("QLinuxFbScreen::connect");
+ qWarning("Error reading fixed information");
+ return false;
+ }
+
+ if (finfo.type == FB_TYPE_VGA_PLANES) {
+ qWarning("VGA16 video mode not supported");
+ return false;
+ }
+
+ /* Get variable screen information */
+ if (d_ptr->fd != -1 && ioctl(d_ptr->fd, FBIOGET_VSCREENINFO, &vinfo)) {
+ perror("QLinuxFbScreen::connect");
+ qWarning("Error reading variable information");
+ return false;
+ }
+
+ grayscale = vinfo.grayscale;
+ d = vinfo.bits_per_pixel;
+ if (d == 24) {
+ d = vinfo.red.length + vinfo.green.length + vinfo.blue.length;
+ if (d <= 0)
+ d = 24; // reset if color component lengths are not reported
+ } else if (d == 16) {
+ d = vinfo.red.length + vinfo.green.length + vinfo.blue.length;
+ if (d <= 0)
+ d = 16;
+ }
+ lstep = finfo.line_length;
+
+ int xoff = vinfo.xoffset;
+ int yoff = vinfo.yoffset;
+ const char* qwssize;
+ if((qwssize=::getenv("QWS_SIZE")) && sscanf(qwssize,"%dx%d",&w,&h)==2) {
+ if (d_ptr->fd != -1) {
+ if ((uint)w > vinfo.xres) w = vinfo.xres;
+ if ((uint)h > vinfo.yres) h = vinfo.yres;
+ }
+ dw=w;
+ dh=h;
+ int xxoff, yyoff;
+ if (sscanf(qwssize, "%*dx%*d+%d+%d", &xxoff, &yyoff) == 2) {
+ if (xxoff < 0 || xxoff + w > vinfo.xres)
+ xxoff = vinfo.xres - w;
+ if (yyoff < 0 || yyoff + h > vinfo.yres)
+ yyoff = vinfo.yres - h;
+ xoff += xxoff;
+ yoff += yyoff;
+ } else {
+ xoff += (vinfo.xres - w)/2;
+ yoff += (vinfo.yres - h)/2;
+ }
+ } else {
+ dw=w=vinfo.xres;
+ dh=h=vinfo.yres;
+ }
+
+ if (w == 0 || h == 0) {
+ qWarning("QScreenLinuxFb::connect(): Unable to find screen geometry, "
+ "will use 320x240.");
+ dw = w = 320;
+ dh = h = 240;
+ }
+
+ setPixelFormat(vinfo);
+
+ // Handle display physical size spec.
+ QStringList displayArgs = displaySpec.split(QLatin1Char(':'));
+ QRegExp mmWidthRx(QLatin1String("mmWidth=?(\\d+)"));
+ int dimIdxW = displayArgs.indexOf(mmWidthRx);
+ QRegExp mmHeightRx(QLatin1String("mmHeight=?(\\d+)"));
+ int dimIdxH = displayArgs.indexOf(mmHeightRx);
+ if (dimIdxW >= 0) {
+ mmWidthRx.exactMatch(displayArgs.at(dimIdxW));
+ physWidth = mmWidthRx.cap(1).toInt();
+ if (dimIdxH < 0)
+ physHeight = dh*physWidth/dw;
+ }
+ if (dimIdxH >= 0) {
+ mmHeightRx.exactMatch(displayArgs.at(dimIdxH));
+ physHeight = mmHeightRx.cap(1).toInt();
+ if (dimIdxW < 0)
+ physWidth = dw*physHeight/dh;
+ }
+ if (dimIdxW < 0 && dimIdxH < 0) {
+ if (vinfo.width != 0 && vinfo.height != 0
+ && vinfo.width != UINT_MAX && vinfo.height != UINT_MAX) {
+ physWidth = vinfo.width;
+ physHeight = vinfo.height;
+ } else {
+ const int dpi = 72;
+ physWidth = qRound(dw * 25.4 / dpi);
+ physHeight = qRound(dh * 25.4 / dpi);
+ }
+ }
+
+ dataoffset = yoff * lstep + xoff * d / 8;
+ //qDebug("Using %dx%dx%d screen",w,h,d);
+
+ /* Figure out the size of the screen in bytes */
+ size = h * lstep;
+
+ mapsize = finfo.smem_len;
+
+ data = (unsigned char *)-1;
+ if (d_ptr->fd != -1)
+ data = (unsigned char *)mmap(0, mapsize, PROT_READ | PROT_WRITE,
+ MAP_SHARED, d_ptr->fd, 0);
+
+ if ((long)data == -1) {
+ if (QApplication::type() == QApplication::GuiServer) {
+ perror("QLinuxFbScreen::connect");
+ qWarning("Error: failed to map framebuffer device to memory.");
+ return false;
+ }
+ data = 0;
+ } else {
+ data += dataoffset;
+ }
+
+ canaccel = useOffscreen();
+ if(canaccel)
+ setupOffScreen();
+
+ // Now read in palette
+ if((vinfo.bits_per_pixel==8) || (vinfo.bits_per_pixel==4)) {
+ screencols= (vinfo.bits_per_pixel==8) ? 256 : 16;
+ int loopc;
+ fb_cmap startcmap;
+ startcmap.start=0;
+ startcmap.len=screencols;
+ startcmap.red=(unsigned short int *)
+ malloc(sizeof(unsigned short int)*screencols);
+ startcmap.green=(unsigned short int *)
+ malloc(sizeof(unsigned short int)*screencols);
+ startcmap.blue=(unsigned short int *)
+ malloc(sizeof(unsigned short int)*screencols);
+ startcmap.transp=(unsigned short int *)
+ malloc(sizeof(unsigned short int)*screencols);
+ if (d_ptr->fd == -1 || ioctl(d_ptr->fd, FBIOGETCMAP, &startcmap)) {
+ perror("QLinuxFbScreen::connect");
+ qWarning("Error reading palette from framebuffer, using default palette");
+ createPalette(startcmap, vinfo, finfo);
+ }
+ int bits_used = 0;
+ for(loopc=0;loopc<screencols;loopc++) {
+ screenclut[loopc]=qRgb(startcmap.red[loopc] >> 8,
+ startcmap.green[loopc] >> 8,
+ startcmap.blue[loopc] >> 8);
+ bits_used |= startcmap.red[loopc]
+ | startcmap.green[loopc]
+ | startcmap.blue[loopc];
+ }
+ // WORKAROUND: Some framebuffer drivers only return 8 bit
+ // color values, so we need to not bit shift them..
+ if ((bits_used & 0x00ff) && !(bits_used & 0xff00)) {
+ for(loopc=0;loopc<screencols;loopc++) {
+ screenclut[loopc] = qRgb(startcmap.red[loopc],
+ startcmap.green[loopc],
+ startcmap.blue[loopc]);
+ }
+ qWarning("8 bits cmap returned due to faulty FB driver, colors corrected");
+ }
+ free(startcmap.red);
+ free(startcmap.green);
+ free(startcmap.blue);
+ free(startcmap.transp);
+ } else {
+ screencols=0;
+ }
+
+ return true;
+}
+
+/*!
+ \reimp
+
+ This unmaps the framebuffer.
+
+ \sa connect()
+*/
+
+void QLinuxFbScreen::disconnect()
+{
+ data -= dataoffset;
+ if (data)
+ munmap((char*)data,mapsize);
+ close(d_ptr->fd);
+}
+
+// #define DEBUG_VINFO
+
+void QLinuxFbScreen::createPalette(fb_cmap &cmap, fb_var_screeninfo &vinfo, fb_fix_screeninfo &finfo)
+{
+ if((vinfo.bits_per_pixel==8) || (vinfo.bits_per_pixel==4)) {
+ screencols= (vinfo.bits_per_pixel==8) ? 256 : 16;
+ cmap.start=0;
+ cmap.len=screencols;
+ cmap.red=(unsigned short int *)
+ malloc(sizeof(unsigned short int)*screencols);
+ cmap.green=(unsigned short int *)
+ malloc(sizeof(unsigned short int)*screencols);
+ cmap.blue=(unsigned short int *)
+ malloc(sizeof(unsigned short int)*screencols);
+ cmap.transp=(unsigned short int *)
+ malloc(sizeof(unsigned short int)*screencols);
+
+ if (screencols==16) {
+ if (finfo.type == FB_TYPE_PACKED_PIXELS) {
+ // We'll setup a grayscale cmap for 4bpp linear
+ int val = 0;
+ for (int idx = 0; idx < 16; ++idx, val += 17) {
+ cmap.red[idx] = (val<<8)|val;
+ cmap.green[idx] = (val<<8)|val;
+ cmap.blue[idx] = (val<<8)|val;
+ screenclut[idx]=qRgb(val, val, val);
+ }
+ } else {
+ // Default 16 colour palette
+ // Green is now trolltech green so certain images look nicer
+ // black d_gray l_gray white red green blue cyan magenta yellow
+ unsigned char reds[16] = { 0x00, 0x7F, 0xBF, 0xFF, 0xFF, 0xA2, 0x00, 0xFF, 0xFF, 0x00, 0x7F, 0x7F, 0x00, 0x00, 0x00, 0x82 };
+ unsigned char greens[16] = { 0x00, 0x7F, 0xBF, 0xFF, 0x00, 0xC5, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x7F, 0x7F, 0x7F };
+ unsigned char blues[16] = { 0x00, 0x7F, 0xBF, 0xFF, 0x00, 0x11, 0xFF, 0x00, 0xFF, 0xFF, 0x00, 0x7F, 0x7F, 0x7F, 0x00, 0x00 };
+
+ for (int idx = 0; idx < 16; ++idx) {
+ cmap.red[idx] = ((reds[idx]) << 8)|reds[idx];
+ cmap.green[idx] = ((greens[idx]) << 8)|greens[idx];
+ cmap.blue[idx] = ((blues[idx]) << 8)|blues[idx];
+ cmap.transp[idx] = 0;
+ screenclut[idx]=qRgb(reds[idx], greens[idx], blues[idx]);
+ }
+ }
+ } else {
+ if (grayscale) {
+ // Build grayscale palette
+ int i;
+ for(i=0;i<screencols;++i) {
+ int bval = screencols == 256 ? i : (i << 4);
+ ushort val = (bval << 8) | bval;
+ cmap.red[i] = val;
+ cmap.green[i] = val;
+ cmap.blue[i] = val;
+ cmap.transp[i] = 0;
+ screenclut[i] = qRgb(bval,bval,bval);
+ }
+ } else {
+ // 6x6x6 216 color cube
+ int idx = 0;
+ for(int ir = 0x0; ir <= 0xff; ir+=0x33) {
+ for(int ig = 0x0; ig <= 0xff; ig+=0x33) {
+ for(int ib = 0x0; ib <= 0xff; ib+=0x33) {
+ cmap.red[idx] = (ir << 8)|ir;
+ cmap.green[idx] = (ig << 8)|ig;
+ cmap.blue[idx] = (ib << 8)|ib;
+ cmap.transp[idx] = 0;
+ screenclut[idx]=qRgb(ir, ig, ib);
+ ++idx;
+ }
+ }
+ }
+ // Fill in rest with 0
+ for (int loopc=0; loopc<40; ++loopc) {
+ screenclut[idx]=0;
+ ++idx;
+ }
+ screencols=idx;
+ }
+ }
+ } else if(finfo.visual==FB_VISUAL_DIRECTCOLOR) {
+ cmap.start=0;
+ int rbits=0,gbits=0,bbits=0;
+ switch (vinfo.bits_per_pixel) {
+ case 8:
+ rbits=vinfo.red.length;
+ gbits=vinfo.green.length;
+ bbits=vinfo.blue.length;
+ if(rbits==0 && gbits==0 && bbits==0) {
+ // cyber2000 driver bug hack
+ rbits=3;
+ gbits=3;
+ bbits=2;
+ }
+ break;
+ case 15:
+ rbits=5;
+ gbits=5;
+ bbits=5;
+ break;
+ case 16:
+ rbits=5;
+ gbits=6;
+ bbits=5;
+ break;
+ case 18:
+ case 19:
+ rbits=6;
+ gbits=6;
+ bbits=6;
+ break;
+ case 24: case 32:
+ rbits=gbits=bbits=8;
+ break;
+ }
+ screencols=cmap.len=1<<qMax(rbits,qMax(gbits,bbits));
+ cmap.red=(unsigned short int *)
+ malloc(sizeof(unsigned short int)*256);
+ cmap.green=(unsigned short int *)
+ malloc(sizeof(unsigned short int)*256);
+ cmap.blue=(unsigned short int *)
+ malloc(sizeof(unsigned short int)*256);
+ cmap.transp=(unsigned short int *)
+ malloc(sizeof(unsigned short int)*256);
+ for(unsigned int i = 0x0; i < cmap.len; i++) {
+ cmap.red[i] = i*65535/((1<<rbits)-1);
+ cmap.green[i] = i*65535/((1<<gbits)-1);
+ cmap.blue[i] = i*65535/((1<<bbits)-1);
+ cmap.transp[i] = 0;
+ }
+ }
+}
+
+/*!
+ \reimp
+
+ This is called by the \l{Qt for Embedded Linux} server at startup time.
+ It turns off console blinking, sets up the color palette, enables write
+ combining on the framebuffer and initialises the off-screen memory
+ manager.
+*/
+
+bool QLinuxFbScreen::initDevice()
+{
+ d_ptr->openTty();
+
+ // Grab current mode so we can reset it
+ fb_var_screeninfo vinfo;
+ fb_fix_screeninfo finfo;
+ //#######################
+ // Shut up Valgrind
+ memset(&vinfo, 0, sizeof(vinfo));
+ memset(&finfo, 0, sizeof(finfo));
+ //#######################
+
+ if (ioctl(d_ptr->fd, FBIOGET_VSCREENINFO, &vinfo)) {
+ perror("QLinuxFbScreen::initDevice");
+ qFatal("Error reading variable information in card init");
+ return false;
+ }
+
+#ifdef DEBUG_VINFO
+ qDebug("Greyscale %d",vinfo.grayscale);
+ qDebug("Nonstd %d",vinfo.nonstd);
+ qDebug("Red %d %d %d",vinfo.red.offset,vinfo.red.length,
+ vinfo.red.msb_right);
+ qDebug("Green %d %d %d",vinfo.green.offset,vinfo.green.length,
+ vinfo.green.msb_right);
+ qDebug("Blue %d %d %d",vinfo.blue.offset,vinfo.blue.length,
+ vinfo.blue.msb_right);
+ qDebug("Transparent %d %d %d",vinfo.transp.offset,vinfo.transp.length,
+ vinfo.transp.msb_right);
+#endif
+
+ d_ptr->startupw=vinfo.xres;
+ d_ptr->startuph=vinfo.yres;
+ d_ptr->startupd=vinfo.bits_per_pixel;
+ grayscale = vinfo.grayscale;
+
+ if (ioctl(d_ptr->fd, FBIOGET_FSCREENINFO, &finfo)) {
+ perror("QLinuxFbScreen::initDevice");
+ qCritical("Error reading fixed information in card init");
+ // It's not an /error/ as such, though definitely a bad sign
+ // so we return true
+ return true;
+ }
+
+#ifdef __i386__
+ // Now init mtrr
+ if(!::getenv("QWS_NOMTRR")) {
+ int mfd=open("/proc/mtrr",O_WRONLY,0);
+ // MTRR entry goes away when file is closed - i.e.
+ // hopefully when QWS is killed
+ if(mfd != -1) {
+ mtrr_sentry sentry;
+ sentry.base=(unsigned long int)finfo.smem_start;
+ //qDebug("Physical framebuffer address %p",(void*)finfo.smem_start);
+ // Size needs to be in 4k chunks, but that's not always
+ // what we get thanks to graphics card registers. Write combining
+ // these is Not Good, so we write combine what we can
+ // (which is not much - 4 megs on an 8 meg card, it seems)
+ unsigned int size=finfo.smem_len;
+ size=size >> 22;
+ size=size << 22;
+ sentry.size=size;
+ sentry.type=MTRR_TYPE_WRCOMB;
+ if(ioctl(mfd,MTRRIOC_ADD_ENTRY,&sentry)==-1) {
+ //printf("Couldn't add mtrr entry for %lx %lx, %s\n",
+ //sentry.base,sentry.size,strerror(errno));
+ }
+ }
+ }
+#endif
+ if ((vinfo.bits_per_pixel==8) || (vinfo.bits_per_pixel==4) || (finfo.visual==FB_VISUAL_DIRECTCOLOR))
+ {
+ fb_cmap cmap;
+ createPalette(cmap, vinfo, finfo);
+ if (ioctl(d_ptr->fd, FBIOPUTCMAP, &cmap)) {
+ perror("QLinuxFbScreen::initDevice");
+ qWarning("Error writing palette to framebuffer");
+ }
+ free(cmap.red);
+ free(cmap.green);
+ free(cmap.blue);
+ free(cmap.transp);
+ }
+
+ if (canaccel) {
+ *entryp=0;
+ *lowest = mapsize;
+ insert_entry(*entryp, *lowest, *lowest); // dummy entry to mark start
+ }
+
+ shared->fifocount = 0;
+ shared->buffer_offset = 0xffffffff; // 0 would be a sensible offset (screen)
+ shared->linestep = 0;
+ shared->cliptop = 0xffffffff;
+ shared->clipleft = 0xffffffff;
+ shared->clipright = 0xffffffff;
+ shared->clipbottom = 0xffffffff;
+ shared->rop = 0xffffffff;
+
+#ifdef QT_QWS_DEPTH_GENERIC
+ if (pixelFormat() == QImage::Format_Invalid && screencols == 0
+ && d_ptr->doGenericColors)
+ {
+ qt_set_generic_blit(this, vinfo.bits_per_pixel,
+ vinfo.red.length, vinfo.green.length,
+ vinfo.blue.length, vinfo.transp.length,
+ vinfo.red.offset, vinfo.green.offset,
+ vinfo.blue.offset, vinfo.transp.offset);
+ }
+#endif
+
+#ifndef QT_NO_QWS_CURSOR
+ QScreenCursor::initSoftwareCursor();
+#endif
+ blank(false);
+
+ return true;
+}
+
+/*
+ The offscreen memory manager's list of entries is stored at the bottom
+ of the offscreen memory area and consistes of a series of QPoolEntry's,
+ each of which keep track of a block of allocated memory. Unallocated memory
+ is implicitly indicated by the gap between blocks indicated by QPoolEntry's.
+ The memory manager looks through any unallocated memory before the end
+ of currently-allocated memory to see if a new block will fit in the gap;
+ if it doesn't it allocated it from the end of currently-allocated memory.
+ Memory is allocated from the top of the framebuffer downwards; if it hits
+ the list of entries then offscreen memory is full and further allocations
+ are made from main RAM (and hence unaccelerated). Allocated memory can
+ be seen as a sort of upside-down stack; lowest keeps track of the
+ bottom of the stack.
+*/
+
+void QLinuxFbScreen::delete_entry(int pos)
+{
+ if (pos > *entryp || pos < 0) {
+ qWarning("Attempt to delete odd pos! %d %d", pos, *entryp);
+ return;
+ }
+
+#ifdef DEBUG_CACHE
+ qDebug("Remove entry: %d", pos);
+#endif
+
+ QPoolEntry *qpe = &entries[pos];
+ if (qpe->start <= *lowest) {
+ // Lowest goes up again
+ *lowest = entries[pos-1].start;
+#ifdef DEBUG_CACHE
+ qDebug(" moved lowest to %d", *lowest);
+#endif
+ }
+
+ (*entryp)--;
+ if (pos == *entryp)
+ return;
+
+ int size = (*entryp)-pos;
+ memmove(&entries[pos], &entries[pos+1], size*sizeof(QPoolEntry));
+}
+
+void QLinuxFbScreen::insert_entry(int pos, int start, int end)
+{
+ if (pos > *entryp) {
+ qWarning("Attempt to insert odd pos! %d %d",pos,*entryp);
+ return;
+ }
+
+#ifdef DEBUG_CACHE
+ qDebug("Insert entry: %d, %d -> %d", pos, start, end);
+#endif
+
+ if (start < (int)*lowest) {
+ *lowest = start;
+#ifdef DEBUG_CACHE
+ qDebug(" moved lowest to %d", *lowest);
+#endif
+ }
+
+ if (pos == *entryp) {
+ entries[pos].start = start;
+ entries[pos].end = end;
+ entries[pos].clientId = qws_client_id;
+ (*entryp)++;
+ return;
+ }
+
+ int size=(*entryp)-pos;
+ memmove(&entries[pos+1],&entries[pos],size*sizeof(QPoolEntry));
+ entries[pos].start=start;
+ entries[pos].end=end;
+ entries[pos].clientId=qws_client_id;
+ (*entryp)++;
+}
+
+/*!
+ \fn uchar * QLinuxFbScreen::cache(int amount)
+
+ Requests the specified \a amount of offscreen graphics card memory
+ from the memory manager, and returns a pointer to the data within
+ the framebuffer (or 0 if there is no free memory).
+
+ Note that the display is locked while memory is allocated in order to
+ preserve the memory pool's integrity.
+
+ Use the QScreen::onCard() function to retrieve an offset (in
+ bytes) from the start of graphics card memory for the returned
+ pointer.
+
+ \sa uncache(), clearCache(), deleteEntry()
+*/
+
+uchar * QLinuxFbScreen::cache(int amount)
+{
+ if (!canaccel || entryp == 0)
+ return 0;
+
+ qt_fbdpy->grab();
+
+ int startp = cacheStart + (*entryp+1) * sizeof(QPoolEntry);
+ if (startp >= (int)*lowest) {
+ // We don't have room for another cache QPoolEntry.
+#ifdef DEBUG_CACHE
+ qDebug("No room for pool entry in VRAM");
+#endif
+ qt_fbdpy->ungrab();
+ return 0;
+ }
+
+ int align = pixmapOffsetAlignment();
+
+ if (*entryp > 1) {
+ // Try to find a gap in the allocated blocks.
+ for (int loopc = 0; loopc < *entryp-1; loopc++) {
+ int freestart = entries[loopc+1].end;
+ int freeend = entries[loopc].start;
+ if (freestart != freeend) {
+ while (freestart % align) {
+ freestart++;
+ }
+ int len=freeend-freestart;
+ if (len >= amount) {
+ insert_entry(loopc+1, freestart, freestart+amount);
+ qt_fbdpy->ungrab();
+ return data+freestart;
+ }
+ }
+ }
+ }
+
+ // No free blocks in already-taken memory; get some more
+ // if we can
+ int newlowest = (*lowest)-amount;
+ if (newlowest % align) {
+ newlowest -= align;
+ while (newlowest % align) {
+ newlowest++;
+ }
+ }
+ if (startp >= newlowest) {
+ qt_fbdpy->ungrab();
+#ifdef DEBUG_CACHE
+ qDebug("No VRAM available for %d bytes", amount);
+#endif
+ return 0;
+ }
+ insert_entry(*entryp, newlowest, *lowest);
+ qt_fbdpy->ungrab();
+
+ return data + newlowest;
+}
+
+/*!
+ \fn void QLinuxFbScreen::uncache(uchar * memoryBlock)
+
+ Deletes the specified \a memoryBlock allocated from the graphics
+ card memory.
+
+ Note that the display is locked while memory is unallocated in
+ order to preserve the memory pool's integrity.
+
+ This function will first sync the graphics card to ensure the
+ memory isn't still being used by a command in the graphics card
+ FIFO queue. It is possible to speed up a driver by overriding this
+ function to avoid syncing. For example, the driver might delay
+ deleting the memory until it detects that all commands dealing
+ with the memory are no longer in the queue. Note that it will then
+ be up to the driver to ensure that the specified \a memoryBlock no
+ longer is being used.
+
+ \sa cache(), deleteEntry(), clearCache()
+ */
+void QLinuxFbScreen::uncache(uchar * c)
+{
+ // need to sync graphics card
+
+ deleteEntry(c);
+}
+
+/*!
+ \fn void QLinuxFbScreen::deleteEntry(uchar * memoryBlock)
+
+ Deletes the specified \a memoryBlock allocated from the graphics
+ card memory.
+
+ \sa uncache(), cache(), clearCache()
+*/
+void QLinuxFbScreen::deleteEntry(uchar * c)
+{
+ qt_fbdpy->grab();
+ unsigned long pos=(unsigned long)c;
+ pos-=((unsigned long)data);
+ unsigned int hold=(*entryp);
+ for(unsigned int loopc=1;loopc<hold;loopc++) {
+ if (entries[loopc].start==pos) {
+ if (entries[loopc].clientId == qws_client_id)
+ delete_entry(loopc);
+ else
+ qWarning("Attempt to delete client id %d cache entry",
+ entries[loopc].clientId);
+ qt_fbdpy->ungrab();
+ return;
+ }
+ }
+ qt_fbdpy->ungrab();
+ qWarning("Attempt to delete unknown offset %ld",pos);
+}
+
+/*!
+ Removes all entries from the cache for the specified screen \a
+ instance and client identified by the given \a clientId.
+
+ Calling this function should only be necessary if a client exits
+ abnormally.
+
+ \sa cache(), uncache(), deleteEntry()
+*/
+void QLinuxFbScreen::clearCache(QScreen *instance, int clientId)
+{
+ QLinuxFbScreen *screen = (QLinuxFbScreen *)instance;
+ if (!screen->canaccel || !screen->entryp)
+ return;
+ qt_fbdpy->grab();
+ for (int loopc = 0; loopc < *(screen->entryp); loopc++) {
+ if (screen->entries[loopc].clientId == clientId) {
+ screen->delete_entry(loopc);
+ loopc--;
+ }
+ }
+ qt_fbdpy->ungrab();
+}
+
+
+void QLinuxFbScreen::setupOffScreen()
+{
+ // Figure out position of offscreen memory
+ // Set up pool entries pointer table and 64-bit align it
+ int psize = size;
+
+ // hw: this causes the limitation of cursors to 64x64
+ // the cursor should rather use the normal pixmap mechanism
+ psize += 4096; // cursor data
+ psize += 8; // for alignment
+ psize &= ~0x7; // align
+
+ unsigned long pos = (unsigned long)data;
+ pos += psize;
+ entryp = ((int *)pos);
+ lowest = ((unsigned int *)pos)+1;
+ pos += (sizeof(int))*4;
+ entries = (QPoolEntry *)pos;
+
+ // beginning of offscreen memory available for pixmaps.
+ cacheStart = psize + 4*sizeof(int) + sizeof(QPoolEntry);
+}
+
+/*!
+ \reimp
+
+ This is called by the \l{Qt for Embedded Linux} server when it shuts
+ down, and should be inherited if you need to do any card-specific cleanup.
+ The default version hides the screen cursor and reenables the blinking
+ cursor and screen blanking.
+*/
+
+void QLinuxFbScreen::shutdownDevice()
+{
+ // Causing crashes. Not needed.
+ //setMode(startupw,startuph,startupd);
+/*
+ if (startupd == 8) {
+ ioctl(fd,FBIOPUTCMAP,startcmap);
+ free(startcmap->red);
+ free(startcmap->green);
+ free(startcmap->blue);
+ free(startcmap->transp);
+ delete startcmap;
+ startcmap = 0;
+ }
+*/
+ d_ptr->closeTty();
+}
+
+/*!
+ \fn void QLinuxFbScreen::set(unsigned int index,unsigned int red,unsigned int green,unsigned int blue)
+
+ Sets the specified color \a index to the specified RGB value, (\a
+ red, \a green, \a blue), when in paletted graphics modes.
+*/
+
+void QLinuxFbScreen::set(unsigned int i,unsigned int r,unsigned int g,unsigned int b)
+{
+ if (d_ptr->fd != -1) {
+ fb_cmap cmap;
+ cmap.start=i;
+ cmap.len=1;
+ cmap.red=(unsigned short int *)
+ malloc(sizeof(unsigned short int)*256);
+ cmap.green=(unsigned short int *)
+ malloc(sizeof(unsigned short int)*256);
+ cmap.blue=(unsigned short int *)
+ malloc(sizeof(unsigned short int)*256);
+ cmap.transp=(unsigned short int *)
+ malloc(sizeof(unsigned short int)*256);
+ cmap.red[0]=r << 8;
+ cmap.green[0]=g << 8;
+ cmap.blue[0]=b << 8;
+ cmap.transp[0]=0;
+ ioctl(d_ptr->fd, FBIOPUTCMAP, &cmap);
+ free(cmap.red);
+ free(cmap.green);
+ free(cmap.blue);
+ free(cmap.transp);
+ }
+ screenclut[i] = qRgb(r, g, b);
+}
+
+/*!
+ \reimp
+
+ Sets the framebuffer to a new resolution and bit depth. The width is
+ in \a nw, the height is in \a nh, and the depth is in \a nd. After
+ doing this any currently-existing paint engines will be invalid and the
+ screen should be completely redrawn. In a multiple-process
+ Embedded Qt situation you must signal all other applications to
+ call setMode() to the same mode and redraw.
+*/
+
+void QLinuxFbScreen::setMode(int nw,int nh,int nd)
+{
+ if (d_ptr->fd == -1)
+ return;
+
+ fb_fix_screeninfo finfo;
+ fb_var_screeninfo vinfo;
+ //#######################
+ // Shut up Valgrind
+ memset(&vinfo, 0, sizeof(vinfo));
+ memset(&finfo, 0, sizeof(finfo));
+ //#######################
+
+ if (ioctl(d_ptr->fd, FBIOGET_VSCREENINFO, &vinfo)) {
+ perror("QLinuxFbScreen::setMode");
+ qFatal("Error reading variable information in mode change");
+ }
+
+ vinfo.xres=nw;
+ vinfo.yres=nh;
+ vinfo.bits_per_pixel=nd;
+
+ if (ioctl(d_ptr->fd, FBIOPUT_VSCREENINFO, &vinfo)) {
+ perror("QLinuxFbScreen::setMode");
+ qCritical("Error writing variable information in mode change");
+ }
+
+ if (ioctl(d_ptr->fd, FBIOGET_VSCREENINFO, &vinfo)) {
+ perror("QLinuxFbScreen::setMode");
+ qFatal("Error reading changed variable information in mode change");
+ }
+
+ if (ioctl(d_ptr->fd, FBIOGET_FSCREENINFO, &finfo)) {
+ perror("QLinuxFbScreen::setMode");
+ qFatal("Error reading fixed information");
+ }
+
+ disconnect();
+ connect(d_ptr->displaySpec);
+ exposeRegion(region(), 0);
+}
+
+// save the state of the graphics card
+// This is needed so that e.g. we can restore the palette when switching
+// between linux virtual consoles.
+
+/*!
+ \reimp
+
+ This doesn't do anything; accelerated drivers may wish to reimplement
+ it to save graphics cards registers. It's called by the
+ \l{Qt for Embedded Linux} server when the virtual console is switched.
+*/
+
+void QLinuxFbScreen::save()
+{
+ // nothing to do.
+}
+
+
+// restore the state of the graphics card.
+/*!
+ \reimp
+
+ This is called when the virtual console is switched back to
+ \l{Qt for Embedded Linux} and restores the palette.
+*/
+void QLinuxFbScreen::restore()
+{
+ if (d_ptr->fd == -1)
+ return;
+
+ if ((d == 8) || (d == 4)) {
+ fb_cmap cmap;
+ cmap.start=0;
+ cmap.len=screencols;
+ cmap.red=(unsigned short int *)
+ malloc(sizeof(unsigned short int)*256);
+ cmap.green=(unsigned short int *)
+ malloc(sizeof(unsigned short int)*256);
+ cmap.blue=(unsigned short int *)
+ malloc(sizeof(unsigned short int)*256);
+ cmap.transp=(unsigned short int *)
+ malloc(sizeof(unsigned short int)*256);
+ for (int loopc = 0; loopc < screencols; loopc++) {
+ cmap.red[loopc] = qRed(screenclut[loopc]) << 8;
+ cmap.green[loopc] = qGreen(screenclut[loopc]) << 8;
+ cmap.blue[loopc] = qBlue(screenclut[loopc]) << 8;
+ cmap.transp[loopc] = 0;
+ }
+ ioctl(d_ptr->fd, FBIOPUTCMAP, &cmap);
+ free(cmap.red);
+ free(cmap.green);
+ free(cmap.blue);
+ free(cmap.transp);
+ }
+}
+
+/*!
+ \fn int QLinuxFbScreen::sharedRamSize(void * end)
+ \internal
+*/
+
+// This works like the QScreenCursor code. end points to the end
+// of our shared structure, we return the amount of memory we reserved
+int QLinuxFbScreen::sharedRamSize(void * end)
+{
+ shared=(QLinuxFb_Shared *)end;
+ shared--;
+ return sizeof(QLinuxFb_Shared);
+}
+
+/*!
+ \reimp
+*/
+void QLinuxFbScreen::blank(bool on)
+{
+ if (d_ptr->blank == on)
+ return;
+
+#if defined(QT_QWS_IPAQ)
+ if (on)
+ system("apm -suspend");
+#else
+ if (d_ptr->fd == -1)
+ return;
+// Some old kernel versions don't have this. These defines should go
+// away eventually
+#if defined(FBIOBLANK)
+#if defined(VESA_POWERDOWN) && defined(VESA_NO_BLANKING)
+ ioctl(d_ptr->fd, FBIOBLANK, on ? VESA_POWERDOWN : VESA_NO_BLANKING);
+#else
+ ioctl(d_ptr->fd, FBIOBLANK, on ? 1 : 0);
+#endif
+#endif
+#endif
+
+ d_ptr->blank = on;
+}
+
+void QLinuxFbScreen::setPixelFormat(struct fb_var_screeninfo info)
+{
+ const fb_bitfield rgba[4] = { info.red, info.green,
+ info.blue, info.transp };
+
+ QImage::Format format = QImage::Format_Invalid;
+
+ switch (d) {
+ case 32: {
+ const fb_bitfield argb8888[4] = {{16, 8, 0}, {8, 8, 0},
+ {0, 8, 0}, {24, 8, 0}};
+ const fb_bitfield abgr8888[4] = {{0, 8, 0}, {8, 8, 0},
+ {16, 8, 0}, {24, 8, 0}};
+ if (memcmp(rgba, argb8888, 4 * sizeof(fb_bitfield)) == 0) {
+ format = QImage::Format_ARGB32;
+ } else if (memcmp(rgba, argb8888, 3 * sizeof(fb_bitfield)) == 0) {
+ format = QImage::Format_RGB32;
+ } else if (memcmp(rgba, abgr8888, 3 * sizeof(fb_bitfield)) == 0) {
+ format = QImage::Format_RGB32;
+ pixeltype = QScreen::BGRPixel;
+ }
+ break;
+ }
+ case 24: {
+ const fb_bitfield rgb888[4] = {{16, 8, 0}, {8, 8, 0},
+ {0, 8, 0}, {0, 0, 0}};
+ const fb_bitfield bgr888[4] = {{0, 8, 0}, {8, 8, 0},
+ {16, 8, 0}, {0, 0, 0}};
+ if (memcmp(rgba, rgb888, 3 * sizeof(fb_bitfield)) == 0) {
+ format = QImage::Format_RGB888;
+ } else if (memcmp(rgba, bgr888, 3 * sizeof(fb_bitfield)) == 0) {
+ format = QImage::Format_RGB888;
+ pixeltype = QScreen::BGRPixel;
+ }
+ break;
+ }
+ case 18: {
+ const fb_bitfield rgb666[4] = {{12, 6, 0}, {6, 6, 0},
+ {0, 6, 0}, {0, 0, 0}};
+ if (memcmp(rgba, rgb666, 3 * sizeof(fb_bitfield)) == 0)
+ format = QImage::Format_RGB666;
+ break;
+ }
+ case 16: {
+ const fb_bitfield rgb565[4] = {{11, 5, 0}, {5, 6, 0},
+ {0, 5, 0}, {0, 0, 0}};
+ const fb_bitfield bgr565[4] = {{0, 5, 0}, {5, 6, 0},
+ {11, 5, 0}, {0, 0, 0}};
+ if (memcmp(rgba, rgb565, 3 * sizeof(fb_bitfield)) == 0) {
+ format = QImage::Format_RGB16;
+ } else if (memcmp(rgba, bgr565, 3 * sizeof(fb_bitfield)) == 0) {
+ format = QImage::Format_RGB16;
+ pixeltype = QScreen::BGRPixel;
+ }
+ break;
+ }
+ case 15: {
+ const fb_bitfield rgb1555[4] = {{10, 5, 0}, {5, 5, 0},
+ {0, 5, 0}, {15, 1, 0}};
+ const fb_bitfield bgr1555[4] = {{0, 5, 0}, {5, 5, 0},
+ {10, 5, 0}, {15, 1, 0}};
+ if (memcmp(rgba, rgb1555, 3 * sizeof(fb_bitfield)) == 0) {
+ format = QImage::Format_RGB555;
+ } else if (memcmp(rgba, bgr1555, 3 * sizeof(fb_bitfield)) == 0) {
+ format = QImage::Format_RGB555;
+ pixeltype = QScreen::BGRPixel;
+ }
+ break;
+ }
+ case 12: {
+ const fb_bitfield rgb444[4] = {{8, 4, 0}, {4, 4, 0},
+ {0, 4, 0}, {0, 0, 0}};
+ if (memcmp(rgba, rgb444, 3 * sizeof(fb_bitfield)) == 0)
+ format = QImage::Format_RGB444;
+ break;
+ }
+ case 8:
+ break;
+ case 1:
+ format = QImage::Format_Mono; //###: LSB???
+ break;
+ default:
+ break;
+ }
+
+ QScreen::setPixelFormat(format);
+}
+
+bool QLinuxFbScreen::useOffscreen()
+{
+ if ((mapsize - size) < 16*1024)
+ return false;
+
+ return true;
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_QWS_LINUXFB
diff --git a/src/gui/embedded/qscreenlinuxfb_qws.h b/src/gui/embedded/qscreenlinuxfb_qws.h
new file mode 100644
index 0000000000..781b20585b
--- /dev/null
+++ b/src/gui/embedded/qscreenlinuxfb_qws.h
@@ -0,0 +1,129 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSCREENLINUXFB_QWS_H
+#define QSCREENLINUXFB_QWS_H
+
+#include <QtGui/qscreen_qws.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+#ifndef QT_NO_QWS_LINUXFB
+
+class QLinuxFb_Shared
+{
+public:
+ volatile int lastop;
+ volatile int optype;
+ volatile int fifocount; // Accel drivers only
+ volatile int fifomax;
+ volatile int forecol; // Foreground colour caching
+ volatile unsigned int buffer_offset; // Destination
+ volatile int linestep;
+ volatile int cliptop; // Clip rectangle
+ volatile int clipleft;
+ volatile int clipright;
+ volatile int clipbottom;
+ volatile unsigned int rop;
+
+};
+
+struct fb_cmap;
+struct fb_var_screeninfo;
+struct fb_fix_screeninfo;
+class QLinuxFbScreenPrivate;
+
+class Q_GUI_EXPORT QLinuxFbScreen : public QScreen
+{
+public:
+ explicit QLinuxFbScreen(int display_id);
+ virtual ~QLinuxFbScreen();
+
+ virtual bool initDevice();
+ virtual bool connect(const QString &displaySpec);
+
+ virtual bool useOffscreen();
+
+ virtual void disconnect();
+ virtual void shutdownDevice();
+ virtual void setMode(int,int,int);
+ virtual void save();
+ virtual void restore();
+ virtual void blank(bool on);
+ virtual void set(unsigned int,unsigned int,unsigned int,unsigned int);
+ virtual uchar * cache(int);
+ virtual void uncache(uchar *);
+ virtual int sharedRamSize(void *);
+
+ QLinuxFb_Shared * shared;
+
+protected:
+
+ void deleteEntry(uchar *);
+
+ bool canaccel;
+ int dataoffset;
+ int cacheStart;
+
+ static void clearCache(QScreen *instance, int);
+
+private:
+
+ void delete_entry(int);
+ void insert_entry(int,int,int);
+ void setupOffScreen();
+ void createPalette(fb_cmap &cmap, fb_var_screeninfo &vinfo, fb_fix_screeninfo &finfo);
+ void setPixelFormat(struct fb_var_screeninfo);
+
+ QLinuxFbScreenPrivate *d_ptr;
+};
+
+#endif // QT_NO_QWS_LINUXFB
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QSCREENLINUXFB_QWS_H
diff --git a/src/gui/embedded/qscreenmulti_qws.cpp b/src/gui/embedded/qscreenmulti_qws.cpp
new file mode 100644
index 0000000000..1914b4423f
--- /dev/null
+++ b/src/gui/embedded/qscreenmulti_qws.cpp
@@ -0,0 +1,482 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qscreenmulti_qws_p.h"
+
+#ifndef QT_NO_QWS_MULTISCREEN
+
+#include <qlist.h>
+#include <qstringlist.h>
+#include <qwidget.h>
+
+QT_BEGIN_NAMESPACE
+
+#ifndef QT_NO_QWS_CURSOR
+
+class QMultiScreenCursor : public QScreenCursor
+{
+public:
+ QMultiScreenCursor() : currentCursor(qt_screencursor) { enable = false; }
+ ~QMultiScreenCursor() { qt_screencursor = 0; }
+
+ void set(const QImage &image, int hotx, int hoty);
+ void move(int x, int y);
+ void show();
+ void hide();
+
+ void addCursor(QScreenCursor *cursor);
+
+private:
+ void setCurrentCursor(QScreenCursor *newCursor);
+
+ QScreenCursor *currentCursor;
+ QList<QScreenCursor*> cursors;
+};
+
+void QMultiScreenCursor::set(const QImage &image, int hotx, int hoty)
+{
+ QScreenCursor::set(image, hotx, hoty);
+ if (currentCursor)
+ currentCursor->set(image, hotx, hoty);
+}
+
+void QMultiScreenCursor::setCurrentCursor(QScreenCursor *newCursor)
+{
+ *((QScreenCursor*)this) = *newCursor;
+ currentCursor = newCursor;
+}
+
+// XXX: this is a mess!
+void QMultiScreenCursor::move(int x, int y)
+{
+ const int oldIndex = qt_screen->subScreenIndexAt(pos);
+ QScreenCursor::move(x, y); // updates pos
+ const int newIndex = qt_screen->subScreenIndexAt(pos);
+
+ if (!currentCursor && oldIndex != -1)
+ setCurrentCursor(cursors.at(oldIndex));
+ QScreenCursor *oldCursor = currentCursor;
+
+ if (oldIndex != -1) {
+ const QScreen *oldScreen = qt_screen->subScreens().at(oldIndex);
+ if (newIndex == -1 || oldScreen->region().contains(pos)) {
+ oldCursor->move(x, y);
+ return;
+ }
+ }
+
+ if (newIndex != -1) {
+ QScreenCursor *newCursor = cursors.at(newIndex);
+ newCursor->set(cursor, hotspot.x(), hotspot.y());
+
+ if (oldCursor) {
+ if (oldCursor->isVisible())
+ newCursor->show();
+ oldCursor->hide();
+ }
+
+ newCursor->move(x, y);
+
+ setCurrentCursor(newCursor);
+ }
+}
+
+void QMultiScreenCursor::show()
+{
+ if (currentCursor)
+ currentCursor->show();
+}
+
+void QMultiScreenCursor::hide()
+{
+ if (currentCursor)
+ currentCursor->hide();
+}
+
+void QMultiScreenCursor::addCursor(QScreenCursor *cursor)
+{
+ cursors.append(cursor);
+}
+
+#endif
+
+class QMultiScreenPrivate
+{
+public:
+ QMultiScreenPrivate()
+#ifndef QT_NO_QWS_CURSOR
+ : cursor(0)
+#endif
+ {}
+ ~QMultiScreenPrivate()
+ {
+#ifndef QT_NO_QWS_CURSOR
+ delete cursor;
+#endif
+ }
+
+ QList<QScreen*> screens;
+ QRegion region;
+#ifndef QT_NO_QWS_CURSOR
+ QMultiScreenCursor *cursor;
+#endif
+};
+
+QMultiScreen::QMultiScreen(int displayId)
+ : QScreen(displayId, MultiClass), d_ptr(new QMultiScreenPrivate)
+{
+}
+
+QMultiScreen::~QMultiScreen()
+{
+ delete d_ptr;
+}
+
+bool QMultiScreen::initDevice()
+{
+ bool ok = true;
+
+#ifndef QT_NO_QWS_CURSOR
+ d_ptr->cursor = new QMultiScreenCursor;
+#endif
+
+ const int n = d_ptr->screens.count();
+ for (int i = 0; i < n; ++i) {
+ QScreen *s = d_ptr->screens.at(i);
+ ok = s->initDevice() && ok;
+#ifndef QT_NO_QWS_CURSOR
+ d_ptr->cursor->addCursor(qt_screencursor); // XXX
+#endif
+ }
+
+#ifndef QT_NO_QWS_CURSOR
+ // XXX
+ qt_screencursor = d_ptr->cursor;
+#endif
+
+ return ok;
+}
+
+static int getDisplayId(const QString &spec)
+{
+ QRegExp regexp(QLatin1String(":(\\d+)\\b"));
+ if (regexp.lastIndexIn(spec) != -1) {
+ const QString capture = regexp.cap(1);
+ return capture.toInt();
+ }
+ return 0;
+}
+
+static QPoint filterDisplayOffset(QString &spec)
+{
+ QRegExp regexp(QLatin1String(":offset=(\\d+),(\\d+)\\b"));
+ if (regexp.indexIn(spec) == -1)
+ return QPoint();
+
+ const int x = regexp.cap(1).toInt();
+ const int y = regexp.cap(2).toInt();
+ spec.remove(regexp.pos(0), regexp.matchedLength());
+ return QPoint(x, y);
+}
+
+bool QMultiScreen::connect(const QString &displaySpec)
+{
+ QString dSpec = displaySpec;
+ if (dSpec.startsWith(QLatin1String("Multi:"), Qt::CaseInsensitive))
+ dSpec = dSpec.mid(QString(QLatin1String("Multi:")).size());
+
+ const QString displayIdSpec = QString(QLatin1String(" :%1")).arg(displayId);
+ if (dSpec.endsWith(displayIdSpec))
+ dSpec = dSpec.left(dSpec.size() - displayIdSpec.size());
+
+ QStringList specs = dSpec.split(QLatin1Char(' '), QString::SkipEmptyParts);
+ foreach (QString spec, specs) {
+ const int id = getDisplayId(spec);
+ const QPoint offset = filterDisplayOffset(spec);
+ QScreen *s = qt_get_screen(id, spec.toLatin1().constData());
+ s->setOffset(offset);
+ addSubScreen(s);
+ }
+
+ QScreen *firstScreen = d_ptr->screens.at(0);
+ Q_ASSERT(firstScreen);
+
+ // XXX
+ QScreen::d = firstScreen->depth();
+
+ QScreen::lstep = 0;
+ QScreen::data = 0;
+ QScreen::size = 0;
+
+ QScreen::w = d_ptr->region.boundingRect().width();
+ QScreen::h = d_ptr->region.boundingRect().height();
+
+ QScreen::dw = QScreen::w;
+ QScreen::dh = QScreen::h;
+
+ // XXX - Extend the physical size based on the first screen
+ // to encompass all screens, so that code that uses the multi
+ // screen to calculate dpi values will get the right numbers.
+ QScreen::physWidth = firstScreen->physicalWidth() * w / firstScreen->width();
+ QScreen::physHeight = firstScreen->physicalHeight() * h / firstScreen->height();
+
+ // XXXXX
+ qt_screen = this;
+
+ return true;
+}
+
+void QMultiScreen::disconnect()
+{
+ const int n = d_ptr->screens.size();
+ for (int i = 0; i < n; ++i)
+ d_ptr->screens.at(i)->disconnect();
+}
+
+void QMultiScreen::shutdownDevice()
+{
+ const int n = d_ptr->screens.size();
+ for (int i = 0; i < n; ++i)
+ d_ptr->screens.at(i)->shutdownDevice();
+}
+
+void QMultiScreen::setMode(int, int, int)
+{
+ return;
+}
+
+bool QMultiScreen::supportsDepth(int) const
+{
+ return false;
+}
+
+void QMultiScreen::save()
+{
+ const int n = d_ptr->screens.size();
+ for (int i = 0; i < n; ++i)
+ d_ptr->screens.at(i)->save();
+}
+
+void QMultiScreen::restore()
+{
+ const int n = d_ptr->screens.size();
+ for (int i = 0; i < n; ++i)
+ d_ptr->screens.at(i)->restore();
+}
+
+void QMultiScreen::blank(bool on)
+{
+ const int n = d_ptr->screens.size();
+ for (int i = 0; i < n; ++i)
+ d_ptr->screens.at(i)->blank(on);
+}
+
+bool QMultiScreen::onCard(const unsigned char *ptr) const
+{
+ const int n = d_ptr->screens.size();
+ for (int i = 0; i < n; ++i)
+ if (d_ptr->screens.at(i)->onCard(ptr))
+ return true;
+ return false;
+}
+
+bool QMultiScreen::onCard(const unsigned char *ptr, ulong &offset) const
+{
+ const int n = d_ptr->screens.size();
+ for (int i = 0; i < n; ++i)
+ if (d_ptr->screens.at(i)->onCard(ptr, offset))
+ return true;
+ return false;
+}
+
+bool QMultiScreen::isInterlaced() const
+{
+ const int n = d_ptr->screens.size();
+ for (int i = 0; i < n; ++i)
+ if (d_ptr->screens.at(i)->isInterlaced())
+ return true;
+
+ return false;
+}
+
+int QMultiScreen::memoryNeeded(const QString &string)
+{
+ int total = 0;
+ const int n = d_ptr->screens.size();
+ for (int i = 0; i < n; ++i)
+ total += d_ptr->screens.at(i)->memoryNeeded(string);
+ return total;
+}
+
+int QMultiScreen::sharedRamSize(void *arg)
+{
+ int total = 0;
+ const int n = d_ptr->screens.size();
+ for (int i = 0; i < n; ++i)
+ total += d_ptr->screens.at(i)->sharedRamSize(arg);
+ return total;
+}
+
+void QMultiScreen::haltUpdates()
+{
+ const int n = d_ptr->screens.size();
+ for (int i = 0; i < n; ++i)
+ d_ptr->screens.at(i)->haltUpdates();
+}
+
+void QMultiScreen::resumeUpdates()
+{
+ const int n = d_ptr->screens.size();
+ for (int i = 0; i < n; ++i)
+ d_ptr->screens.at(i)->resumeUpdates();
+}
+
+void QMultiScreen::exposeRegion(QRegion region, int changing)
+{
+ const int n = d_ptr->screens.size();
+ for (int i = 0; i < n; ++i) {
+ QScreen *screen = d_ptr->screens.at(i);
+ const QRegion r = region & screen->region();
+ if (r.isEmpty())
+ continue;
+ screen->exposeRegion(r, changing);
+ }
+}
+
+void QMultiScreen::solidFill(const QColor &color, const QRegion &region)
+{
+ const int n = d_ptr->screens.size();
+ for (int i = 0; i < n; ++i) {
+ QScreen *screen = d_ptr->screens.at(i);
+ const QRegion r = region & screen->region();
+ if (r.isEmpty())
+ continue;
+ screen->solidFill(color, r);
+ }
+}
+
+void QMultiScreen::blit(const QImage &img, const QPoint &topLeft,
+ const QRegion &region)
+{
+ const int n = d_ptr->screens.size();
+ for (int i = 0; i < n; ++i) {
+ QScreen *screen = d_ptr->screens.at(i);
+ const QRegion r = region & screen->region();
+ if (r.isEmpty())
+ continue;
+ screen->blit(img, topLeft, r);
+ }
+}
+
+void QMultiScreen::blit(QWSWindow *bs, const QRegion &clip)
+{
+ const int n = d_ptr->screens.size();
+ for (int i = 0; i < n; ++i) {
+ QScreen *screen = d_ptr->screens.at(i);
+ const QRegion r = clip & screen->region();
+ if (r.isEmpty())
+ continue;
+ screen->blit(bs, r);
+ }
+}
+
+void QMultiScreen::setDirty(const QRect &rect)
+{
+ const int n = d_ptr->screens.size();
+ for (int i = 0; i < n; ++i) {
+ QScreen *screen = d_ptr->screens.at(i);
+ const QRegion r = screen->region() & rect;
+ if (r.isEmpty())
+ continue;
+ screen->setDirty(r.boundingRect());
+ }
+}
+
+
+QWSWindowSurface* QMultiScreen::createSurface(const QString &key) const
+{
+ QWSWindowSurface* surf = 0;
+ const int n = d_ptr->screens.size();
+ for (int i = 0; i < n; ++i) {
+ QScreen *screen = d_ptr->screens.at(i);
+ surf = screen->createSurface(key);
+ if (surf)
+ break;
+ }
+ return surf;
+}
+
+
+QWSWindowSurface* QMultiScreen::createSurface(QWidget *widget) const
+{
+ const QPoint midpoint = (widget->frameGeometry().topLeft()
+ + widget->frameGeometry().bottomRight()) / 2;
+ int index = subScreenIndexAt(midpoint);
+ if (index == -1)
+ index = 0; // XXX
+ return d_ptr->screens.at(index)->createSurface(widget);
+}
+
+QList<QScreen*> QMultiScreen::subScreens() const
+{
+ return d_ptr->screens;
+}
+
+QRegion QMultiScreen::region() const
+{
+ return d_ptr->region;
+}
+
+void QMultiScreen::addSubScreen(QScreen *screen)
+{
+ d_ptr->screens.append(screen);
+ d_ptr->region += screen->region();
+}
+
+void QMultiScreen::removeSubScreen(QScreen *screen)
+{
+ d_ptr->screens.removeAll(screen);
+ d_ptr->region -= screen->region();
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_QWS_MULTISCREEN
diff --git a/src/gui/embedded/qscreenmulti_qws_p.h b/src/gui/embedded/qscreenmulti_qws_p.h
new file mode 100644
index 0000000000..e01b167726
--- /dev/null
+++ b/src/gui/embedded/qscreenmulti_qws_p.h
@@ -0,0 +1,114 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QMULTISCREEN_QWS_P_H
+#define QMULTISCREEN_QWS_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of the QLibrary class. This header file may change from
+// version to version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <qscreen_qws.h>
+
+QT_BEGIN_NAMESPACE
+
+#ifndef QT_NO_QWS_MULTISCREEN
+
+class QMultiScreenPrivate;
+
+class QMultiScreen : public QScreen
+{
+public:
+ QMultiScreen(int displayId);
+ ~QMultiScreen();
+ bool initDevice();
+ bool connect(const QString &displaySpec);
+ void disconnect();
+ void shutdownDevice();
+ void setMode(int,int,int);
+ bool supportsDepth(int) const;
+
+ void save();
+ void restore();
+ void blank(bool on);
+
+ bool onCard(const unsigned char *) const;
+ bool onCard(const unsigned char *, ulong& out_offset) const;
+
+ bool isInterlaced() const;
+
+ int memoryNeeded(const QString&);
+ int sharedRamSize(void *);
+
+ void haltUpdates();
+ void resumeUpdates();
+
+ void exposeRegion(QRegion r, int changing);
+
+ void blit(const QImage &img, const QPoint &topLeft, const QRegion &region);
+ void solidFill(const QColor &color, const QRegion &region);
+ void blit(QWSWindow *bs, const QRegion &clip);
+ void setDirty(const QRect&);
+
+ QWSWindowSurface* createSurface(QWidget *widget) const;
+ QWSWindowSurface* createSurface(const QString &key) const;
+
+ QList<QScreen*> subScreens() const;
+ QRegion region() const;
+
+private:
+ void addSubScreen(QScreen *screen);
+ void removeSubScreen(QScreen *screen);
+
+ QMultiScreenPrivate *d_ptr;
+};
+
+
+QT_END_NAMESPACE
+#endif // QT_NO_QWS_MULTISCREEN
+#endif // QMULTISCREEN_QWS_P_H
diff --git a/src/gui/embedded/qscreenproxy_qws.cpp b/src/gui/embedded/qscreenproxy_qws.cpp
new file mode 100644
index 0000000000..5b8f6f0794
--- /dev/null
+++ b/src/gui/embedded/qscreenproxy_qws.cpp
@@ -0,0 +1,631 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <qscreenproxy_qws.h>
+
+#ifndef QT_NO_QWS_PROXYSCREEN
+
+#include <qregexp.h>
+
+#ifndef QT_NO_QWS_CURSOR
+
+/*!
+ \class QProxyScreenCursor
+ \since 4.5
+ \ingroup qws
+ \brief The QProxyScreenCursor class provides a generic interface to
+ QScreenCursor implementations.
+*/
+
+/*!
+ Constructs a proxy screen cursor.
+*/
+QProxyScreenCursor::QProxyScreenCursor()
+ : QScreenCursor(), realCursor(0), d_ptr(0)
+{
+}
+
+/*!
+ Destroys the proxy screen cursor.
+*/
+QProxyScreenCursor::~QProxyScreenCursor()
+{
+}
+
+/*!
+ Sets the real screen cursor to be used for the proxy screen cursor to
+ the \a cursor specified.
+
+ \sa screenCursor()
+*/
+void QProxyScreenCursor::setScreenCursor(QScreenCursor *cursor)
+{
+ realCursor = cursor;
+ configure();
+}
+
+/*!
+ Returns the real screen cursor used by the proxy screen cursor.
+
+ \sa setScreenCursor()
+*/
+QScreenCursor* QProxyScreenCursor::screenCursor() const
+{
+ return realCursor;
+}
+
+/*!
+ \reimp
+*/
+void QProxyScreenCursor::set(const QImage &image, int hotx, int hoty)
+{
+ if (realCursor) {
+ hotspot = QPoint(hotx, hoty);
+ cursor = image;
+ size = image.size();
+ realCursor->set(image, hotx, hoty);
+ } else {
+ QScreenCursor::set(image, hotx, hoty);
+ }
+}
+
+/*!
+ \reimp
+*/
+void QProxyScreenCursor::move(int x, int y)
+{
+ if (realCursor) {
+ pos = QPoint(x, y);
+ realCursor->move(x, y);
+ } else {
+ QScreenCursor::move(x, y);
+ }
+}
+
+/*!
+ \reimp
+*/
+void QProxyScreenCursor::show()
+{
+ if (realCursor) {
+ realCursor->show();
+ enable = true;
+ } else {
+ QScreenCursor::show();
+ }
+}
+
+/*!
+ \reimp
+*/
+void QProxyScreenCursor::hide()
+{
+ if (realCursor) {
+ realCursor->hide();
+ enable = false;
+ } else {
+ QScreenCursor::hide();
+ }
+}
+
+/*!
+ \internal
+*/
+void QProxyScreenCursor::configure()
+{
+ if (!realCursor)
+ return;
+
+ cursor = realCursor->cursor;
+ size = realCursor->size;
+ pos = realCursor->pos;
+ hotspot = realCursor->hotspot;
+ enable = realCursor->enable;
+ hwaccel = realCursor->hwaccel;
+ supportsAlpha = realCursor->supportsAlpha;
+}
+
+#endif // QT_NO_QWS_CURSOR
+
+/*!
+ \class QProxyScreen
+ \ingroup qws
+ \brief The QProxyScreen class provides a generic interface to QScreen implementations.
+*/
+
+/*!
+ \fn QProxyScreen::QProxyScreen(int displayId, ClassId classId)
+
+ Constructs a proxy screen with the given \a displayId and \a classId.
+*/
+QProxyScreen::QProxyScreen(int displayId, QScreen::ClassId classId)
+ : QScreen(displayId, classId), realScreen(0), d_ptr(0)
+{
+}
+
+/*!
+ Destroys the proxy screen.
+*/
+QProxyScreen::~QProxyScreen()
+{
+}
+
+/*!
+ Sets the real \a screen to be used by the proxy screen.
+
+ \sa screen()
+*/
+void QProxyScreen::setScreen(QScreen *screen)
+{
+ realScreen = screen;
+ configure();
+}
+
+/*!
+ Returns the real screen used by the proxy screen.
+
+ \sa setScreen()
+*/
+QScreen* QProxyScreen::screen() const
+{
+ return realScreen;
+}
+
+
+/*!
+ \internal
+*/
+void QProxyScreen::configure()
+{
+ if (!realScreen)
+ return;
+
+ d = realScreen->depth();
+ w = realScreen->width();
+ h = realScreen->height();
+ dw = realScreen->deviceWidth();
+ dh = realScreen->deviceHeight();
+ lstep = realScreen->linestep();
+ data = realScreen->base();
+ lstep = realScreen->linestep();
+ size = realScreen->screenSize();
+ physWidth = realScreen->physicalWidth();
+ physHeight = realScreen->physicalHeight();
+ pixeltype = realScreen->pixelType();
+#if Q_BYTE_ORDER == Q_BIG_ENDIAN
+ setFrameBufferLittleEndian(realScreen->frameBufferLittleEndian());
+#endif
+
+ setOffset(realScreen->offset());
+ setPixelFormat(realScreen->pixelFormat());
+
+#ifdef QT_QWS_CLIENTBLIT
+ setSupportsBlitInClients(realScreen->supportsBlitInClients());
+#endif
+}
+
+/*!
+ \internal
+ Returns the display ID that corresponds to the given \a spec.
+*/
+static int getDisplayId(const QString &spec)
+{
+ QRegExp regexp(QLatin1String(":(\\d+)\\b"));
+ if (regexp.lastIndexIn(spec) != -1) {
+ const QString capture = regexp.cap(1);
+ return capture.toInt();
+ }
+ return 0;
+}
+
+/*!
+ \reimp
+*/
+bool QProxyScreen::connect(const QString &displaySpec)
+{
+ const int id = getDisplayId(displaySpec);
+ realScreen = qt_get_screen(id, displaySpec.toLatin1().constData());
+ configure();
+
+ return true;
+}
+
+/*!
+ \reimp
+*/
+void QProxyScreen::exposeRegion(QRegion r, int changing)
+{
+ if (!realScreen) {
+ QScreen::exposeRegion(r, changing);
+ return;
+ }
+
+ realScreen->exposeRegion(r, changing);
+
+ const QVector<QRect> rects = r.rects();
+ for (int i = 0; i < rects.size(); ++i)
+ setDirty(rects.at(i));
+}
+
+/*!
+ \reimp
+*/
+void QProxyScreen::blit(const QImage &image, const QPoint &topLeft,
+ const QRegion &region)
+{
+ if (!realScreen) {
+ QScreen::blit(image, topLeft, region);
+ return;
+ }
+
+ realScreen->blit(image, topLeft, region);
+}
+
+/*!
+ \reimp
+*/
+void QProxyScreen::solidFill(const QColor &color, const QRegion &region)
+{
+ if (!realScreen) {
+ QScreen::solidFill(color, region);
+ return;
+ }
+ realScreen->solidFill(color, region);
+}
+
+/*!
+ \reimp
+*/
+QSize QProxyScreen::mapToDevice(const QSize &s) const
+{
+ if (!realScreen)
+ return QScreen::mapToDevice(s);
+
+ return realScreen->mapToDevice(s);
+}
+
+/*!
+ \reimp
+*/
+QSize QProxyScreen::mapFromDevice(const QSize &s) const
+{
+ if (!realScreen)
+ return QScreen::mapFromDevice(s);
+
+ return realScreen->mapFromDevice(s);
+}
+
+/*!
+ \reimp
+*/
+QPoint QProxyScreen::mapToDevice(const QPoint &p, const QSize &s) const
+{
+ if (!realScreen)
+ return QScreen::mapToDevice(p, s);
+
+ return realScreen->mapToDevice(p, s);
+}
+
+/*!
+ \reimp
+*/
+QPoint QProxyScreen::mapFromDevice(const QPoint &p, const QSize &s) const
+{
+ if (!realScreen)
+ return QScreen::mapFromDevice(p, s);
+
+ return realScreen->mapFromDevice(p, s);
+}
+
+/*!
+ \reimp
+*/
+QRect QProxyScreen::mapToDevice(const QRect &r, const QSize &s) const
+{
+ if (!realScreen)
+ return QScreen::mapToDevice(r, s);
+
+ return realScreen->mapToDevice(r, s);
+}
+
+/*!
+ \reimp
+*/
+QRect QProxyScreen::mapFromDevice(const QRect &r, const QSize &s) const
+{
+ if (!realScreen)
+ return QScreen::mapFromDevice(r, s);
+
+ return realScreen->mapFromDevice(r, s);
+}
+
+/*!
+ \reimp
+*/
+QRegion QProxyScreen::mapToDevice(const QRegion &r, const QSize &s) const
+{
+ if (!realScreen)
+ return QScreen::mapToDevice(r, s);
+
+ return realScreen->mapToDevice(r, s);
+}
+
+/*!
+ \reimp
+*/
+QRegion QProxyScreen::mapFromDevice(const QRegion &r, const QSize &s) const
+{
+ if (!realScreen)
+ return QScreen::mapFromDevice(r, s);
+
+ return realScreen->mapFromDevice(r, s);
+}
+
+/*!
+ \reimp
+*/
+void QProxyScreen::disconnect()
+{
+ if (realScreen) {
+ realScreen->disconnect();
+ delete realScreen;
+ realScreen = 0;
+ }
+}
+
+/*!
+*/
+bool QProxyScreen::initDevice()
+{
+ if (realScreen)
+ return realScreen->initDevice();
+
+ return false;
+}
+
+/*!
+ \reimp
+*/
+void QProxyScreen::shutdownDevice()
+{
+ if (realScreen)
+ realScreen->shutdownDevice();
+}
+
+/*!
+ \reimp
+*/
+void QProxyScreen::setMode(int w,int h, int d)
+{
+ if (realScreen) {
+ realScreen->setMode(w, h, d);
+ } else {
+ QScreen::dw = QScreen::w = w;
+ QScreen::dh = QScreen::h = h;
+ QScreen::d = d;
+ }
+ configure();
+ exposeRegion(region(), 0);
+}
+
+/*!
+ \reimp
+*/
+bool QProxyScreen::supportsDepth(int depth) const
+{
+ if (realScreen)
+ return realScreen->supportsDepth(depth);
+ return false;
+}
+
+/*!
+ \reimp
+*/
+void QProxyScreen::save()
+{
+ if (realScreen)
+ realScreen->save();
+ QScreen::save();
+}
+
+/*!
+ \reimp
+*/
+void QProxyScreen::restore()
+{
+ if (realScreen)
+ realScreen->restore();
+ QScreen::restore();
+}
+
+/*!
+ \reimp
+*/
+void QProxyScreen::blank(bool on)
+{
+ if (realScreen)
+ realScreen->blank(on);
+}
+
+/*!
+ \reimp
+*/
+bool QProxyScreen::onCard(const unsigned char *ptr) const
+{
+ if (realScreen)
+ return realScreen->onCard(ptr);
+ return false;
+}
+
+/*!
+ \reimp
+*/
+bool QProxyScreen::onCard(const unsigned char *ptr, ulong &offset) const
+{
+ if (realScreen)
+ return realScreen->onCard(ptr, offset);
+ return false;
+}
+
+/*!
+ \reimp
+*/
+bool QProxyScreen::isInterlaced() const
+{
+ if (realScreen)
+ return realScreen->isInterlaced();
+ return false;
+}
+
+/*!
+ \reimp
+*/
+bool QProxyScreen::isTransformed() const
+{
+ if (realScreen)
+ return realScreen->isTransformed();
+ return QScreen::isTransformed();
+}
+
+/*!
+ \reimp
+*/
+int QProxyScreen::transformOrientation() const
+{
+ if (realScreen)
+ return realScreen->transformOrientation();
+ return QScreen::transformOrientation();
+}
+
+/*!
+\reimp
+*/
+int QProxyScreen::memoryNeeded(const QString &str)
+{
+ if (realScreen)
+ return realScreen->memoryNeeded(str);
+ else
+ return QScreen::memoryNeeded(str);
+}
+
+/*!
+\reimp
+*/
+int QProxyScreen::sharedRamSize(void *ptr)
+{
+ if (realScreen)
+ return realScreen->sharedRamSize(ptr);
+ else
+ return QScreen::sharedRamSize(ptr);
+}
+
+/*!
+\reimp
+*/
+void QProxyScreen::haltUpdates()
+{
+ if (realScreen)
+ realScreen->haltUpdates();
+}
+
+/*!
+\reimp
+*/
+void QProxyScreen::resumeUpdates()
+{
+ if (realScreen)
+ realScreen->resumeUpdates();
+}
+
+/*!
+ \reimp
+*/
+void QProxyScreen::setDirty(const QRect &rect)
+{
+ if (realScreen)
+ realScreen->setDirty(rect);
+}
+
+/*!
+ \reimp
+*/
+QWSWindowSurface* QProxyScreen::createSurface(QWidget *widget) const
+{
+ if (realScreen)
+ return realScreen->createSurface(widget);
+
+ return QScreen::createSurface(widget);
+}
+
+/*!
+ \reimp
+*/
+QWSWindowSurface* QProxyScreen::createSurface(const QString &key) const
+{
+ if (realScreen)
+ return realScreen->createSurface(key);
+
+ return QScreen::createSurface(key);
+}
+
+/*!
+ \reimp
+*/
+QList<QScreen*> QProxyScreen::subScreens() const
+{
+ if (realScreen)
+ return realScreen->subScreens();
+
+ return QScreen::subScreens();
+}
+
+/*!
+ \reimp
+*/
+QRegion QProxyScreen::region() const
+{
+ if (realScreen)
+ return realScreen->region();
+ else
+ return QScreen::region();
+}
+
+#endif // QT_NO_QWS_PROXYSCREEN
diff --git a/src/gui/embedded/qscreenproxy_qws.h b/src/gui/embedded/qscreenproxy_qws.h
new file mode 100644
index 0000000000..6373c2af7b
--- /dev/null
+++ b/src/gui/embedded/qscreenproxy_qws.h
@@ -0,0 +1,153 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QPROXYSCREEN_QWS_H
+#define QPROXYSCREEN_QWS_H
+
+#include <QtGui/qscreen_qws.h>
+
+#ifndef QT_NO_QWS_PROXYSCREEN
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+class QProxyScreenPrivate;
+
+#ifndef QT_NO_QWS_CURSOR
+
+class QProxyScreenCursorPrivate;
+
+class Q_GUI_EXPORT QProxyScreenCursor : public QScreenCursor
+{
+public:
+ QProxyScreenCursor();
+ ~QProxyScreenCursor();
+
+ void setScreenCursor(QScreenCursor *cursor);
+ QScreenCursor* screenCursor() const;
+
+ void set(const QImage &image, int hotx, int hoty);
+ void move(int x, int y);
+ void show();
+ void hide();
+
+private:
+ void configure();
+
+ QScreenCursor *realCursor;
+ QProxyScreenCursorPrivate *d_ptr;
+};
+
+#endif // QT_NO_QWS_CURSOR
+
+class Q_GUI_EXPORT QProxyScreen : public QScreen
+{
+public:
+ QProxyScreen(int display_id, ClassId = ProxyClass);
+ ~QProxyScreen();
+
+ void setScreen(QScreen *screen);
+ QScreen *screen() const;
+
+ QSize mapToDevice(const QSize &s) const;
+ QSize mapFromDevice(const QSize &s) const;
+
+ QPoint mapToDevice(const QPoint &, const QSize &) const;
+ QPoint mapFromDevice(const QPoint &, const QSize &) const;
+
+ QRect mapToDevice(const QRect &, const QSize &) const;
+ QRect mapFromDevice(const QRect &, const QSize &) const;
+
+ QRegion mapToDevice(const QRegion &, const QSize &) const;
+ QRegion mapFromDevice(const QRegion &, const QSize &) const;
+
+ bool connect(const QString &displaySpec);
+ bool initDevice();
+ void shutdownDevice();
+ void disconnect();
+
+ void setMode(int width, int height, int depth);
+ bool supportsDepth(int) const;
+
+ void save();
+ void restore();
+ void blank(bool on);
+
+ bool onCard(const unsigned char *) const;
+ bool onCard(const unsigned char *, ulong& out_offset) const;
+
+ bool isInterlaced() const;
+ bool isTransformed() const;
+ int transformOrientation() const;
+
+ int memoryNeeded(const QString&);
+ int sharedRamSize(void *);
+
+ void haltUpdates();
+ void resumeUpdates();
+
+ void exposeRegion(QRegion r, int changing);
+ void blit(const QImage &img, const QPoint &topLeft, const QRegion &region);
+ void solidFill(const QColor &color, const QRegion &region);
+ void setDirty(const QRect&);
+
+ QWSWindowSurface* createSurface(QWidget *widget) const;
+ QWSWindowSurface* createSurface(const QString &key) const;
+
+ QList<QScreen*> subScreens() const;
+ QRegion region() const;
+
+private:
+ void configure();
+
+ QScreen *realScreen;
+ QProxyScreenPrivate *d_ptr;
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QT_NO_QWS_PROXYSCREEN
+#endif // QPROXYSCREEN_QWS_H
diff --git a/src/gui/embedded/qscreentransformed_qws.cpp b/src/gui/embedded/qscreentransformed_qws.cpp
new file mode 100644
index 0000000000..f9887890fd
--- /dev/null
+++ b/src/gui/embedded/qscreentransformed_qws.cpp
@@ -0,0 +1,734 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qscreentransformed_qws.h"
+
+#ifndef QT_NO_QWS_TRANSFORMED
+#include <qscreendriverfactory_qws.h>
+#include <qvector.h>
+#include <private/qpainter_p.h>
+#include <private/qmemrotate_p.h>
+#include <qmatrix.h>
+
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#include <qwindowsystem_qws.h>
+#include <qwsdisplay_qws.h>
+
+QT_BEGIN_NAMESPACE
+
+//#define QT_REGION_DEBUG
+
+#ifdef QT_REGION_DEBUG
+#include <QDebug>
+#endif
+
+class QTransformedScreenPrivate
+{
+public:
+ QTransformedScreenPrivate(QTransformedScreen *parent);
+
+ void configure();
+
+ QTransformedScreen::Transformation transformation;
+#ifdef QT_QWS_DEPTH_GENERIC
+ bool doGenericColors;
+#endif
+ QTransformedScreen *q;
+};
+
+QTransformedScreenPrivate::QTransformedScreenPrivate(QTransformedScreen *parent)
+ : transformation(QTransformedScreen::None),
+#ifdef QT_QWS_DEPTH_GENERIC
+ doGenericColors(false),
+#endif
+ q(parent)
+{
+}
+
+extern "C"
+#ifndef QT_BUILD_GUI_LIB
+Q_DECL_EXPORT
+#endif
+void qws_setScreenTransformation(QScreen *that, int t)
+{
+ QTransformedScreen *tscreen = static_cast<QTransformedScreen*>(that);
+ tscreen->setTransformation((QTransformedScreen::Transformation)t);
+}
+
+// ---------------------------------------------------------------------------
+// Transformed Screen
+// ---------------------------------------------------------------------------
+
+/*!
+ \internal
+
+ \class QTransformedScreen
+ \ingroup qws
+
+ \brief The QTransformedScreen class implements a screen driver for
+ a transformed screen.
+
+ Note that this class is only available in \l{Qt for Embedded Linux}.
+ Custom screen drivers can be added by subclassing the
+ QScreenDriverPlugin class, using the QScreenDriverFactory class to
+ dynamically load the driver into the application, but there should
+ only be one screen object per application.
+
+ Use the QScreen::isTransformed() function to determine if a screen
+ is transformed. The QTransformedScreen class itself provides means
+ of rotating the screen with its setTransformation() function; the
+ transformation() function returns the currently set rotation in
+ terms of the \l Transformation enum (which describes the various
+ available rotation settings). Alternatively, QTransformedScreen
+ provides an implementation of the QScreen::transformOrientation()
+ function, returning the current rotation as an integer value.
+
+ \sa QScreen, QScreenDriverPlugin, {Running Applications}
+*/
+
+/*!
+ \enum QTransformedScreen::Transformation
+
+ This enum describes the various rotations a transformed screen can
+ have.
+
+ \value None No rotation
+ \value Rot90 90 degrees rotation
+ \value Rot180 180 degrees rotation
+ \value Rot270 270 degrees rotation
+*/
+
+/*!
+ \fn bool QTransformedScreen::isTransformed() const
+ \reimp
+*/
+
+/*!
+ Constructs a QTransformedScreen object. The \a displayId argument
+ identifies the Qt for Embedded Linux server to connect to.
+*/
+QTransformedScreen::QTransformedScreen(int displayId)
+ : QProxyScreen(displayId, QScreen::TransformedClass)
+{
+ d_ptr = new QTransformedScreenPrivate(this);
+ d_ptr->transformation = None;
+
+#ifdef QT_REGION_DEBUG
+ qDebug() << "QTransformedScreen::QTransformedScreen";
+#endif
+}
+
+void QTransformedScreenPrivate::configure()
+{
+ // ###: works because setTransformation recalculates unconditionally
+ q->setTransformation(transformation);
+}
+
+/*!
+ Destroys the QTransformedScreen object.
+*/
+QTransformedScreen::~QTransformedScreen()
+{
+ delete d_ptr;
+}
+
+static int getDisplayId(const QString &spec)
+{
+ QRegExp regexp(QLatin1String(":(\\d+)\\b"));
+ if (regexp.lastIndexIn(spec) != -1) {
+ const QString capture = regexp.cap(1);
+ return capture.toInt();
+ }
+ return 0;
+}
+
+static QTransformedScreen::Transformation filterTransformation(QString &spec)
+{
+ QRegExp regexp(QLatin1String("\\bRot(\\d+):?\\b"), Qt::CaseInsensitive);
+ if (regexp.indexIn(spec) == -1)
+ return QTransformedScreen::None;
+
+ const int degrees = regexp.cap(1).toInt();
+ spec.remove(regexp.pos(0), regexp.matchedLength());
+
+ return static_cast<QTransformedScreen::Transformation>(degrees / 90);
+}
+
+/*!
+ \reimp
+*/
+bool QTransformedScreen::connect(const QString &displaySpec)
+{
+ QString dspec = displaySpec.trimmed();
+ if (dspec.startsWith(QLatin1String("Transformed:"), Qt::CaseInsensitive))
+ dspec = dspec.mid(QString(QLatin1String("Transformed:")).size());
+ else if (!dspec.compare(QLatin1String("Transformed"), Qt::CaseInsensitive))
+ dspec = QString();
+
+ const QString displayIdSpec = QString(QLatin1String(" :%1")).arg(displayId);
+ if (dspec.endsWith(displayIdSpec))
+ dspec = dspec.left(dspec.size() - displayIdSpec.size());
+
+ d_ptr->transformation = filterTransformation(dspec);
+
+ QString driver = dspec;
+ int colon = driver.indexOf(QLatin1Char(':'));
+ if (colon >= 0)
+ driver.truncate(colon);
+
+ if (!QScreenDriverFactory::keys().contains(driver, Qt::CaseInsensitive))
+ if (!dspec.isEmpty())
+ dspec.prepend(QLatin1String(":"));
+
+ const int id = getDisplayId(dspec);
+ QScreen *s = qt_get_screen(id, dspec.toLatin1().constData());
+ setScreen(s);
+
+#ifdef QT_QWS_DEPTH_GENERIC
+ d_ptr->doGenericColors = dspec.contains(QLatin1String("genericcolors"));
+#endif
+
+ d_ptr->configure();
+
+ // XXX
+ qt_screen = this;
+
+ return true;
+}
+
+/*!
+ Returns the currently set rotation.
+
+ \sa setTransformation(), QScreen::transformOrientation()
+*/
+QTransformedScreen::Transformation QTransformedScreen::transformation() const
+{
+ return d_ptr->transformation;
+}
+
+/*!
+ \reimp
+*/
+int QTransformedScreen::transformOrientation() const
+{
+ return (int)d_ptr->transformation;
+}
+
+/*!
+ \reimp
+*/
+void QTransformedScreen::exposeRegion(QRegion region, int changing)
+{
+ if (!data || d_ptr->transformation == None) {
+ QProxyScreen::exposeRegion(region, changing);
+ return;
+ }
+ QScreen::exposeRegion(region, changing);
+}
+
+/*!
+ Rotates this screen object according to the specified \a transformation.
+
+ \sa transformation()
+*/
+void QTransformedScreen::setTransformation(Transformation transformation)
+{
+ d_ptr->transformation = transformation;
+ QSize size = mapFromDevice(QSize(dw, dh));
+ w = size.width();
+ h = size.height();
+
+ const QScreen *s = screen();
+ size = mapFromDevice(QSize(s->physicalWidth(), s->physicalHeight()));
+ physWidth = size.width();
+ physHeight = size.height();
+
+#ifdef QT_REGION_DEBUG
+ qDebug() << "QTransformedScreen::setTransformation" << transformation
+ << "size" << w << h << "dev size" << dw << dh;
+#endif
+
+}
+
+static inline QRect correctNormalized(const QRect &r) {
+ const int x1 = qMin(r.left(), r.right());
+ const int x2 = qMax(r.left(), r.right());
+ const int y1 = qMin(r.top(), r.bottom());
+ const int y2 = qMax(r.top(), r.bottom());
+
+ return QRect( QPoint(x1,y1), QPoint(x2,y2) );
+}
+
+template <class DST, class SRC>
+static inline void blit90(QScreen *screen, const QImage &image,
+ const QRect &rect, const QPoint &topLeft)
+{
+ const SRC *src = (const SRC*)(image.scanLine(rect.top())) + rect.left();
+ DST *dest = (DST*)(screen->base() + topLeft.y() * screen->linestep())
+ + topLeft.x();
+ qt_memrotate90(src, rect.width(), rect.height(), image.bytesPerLine(),
+ dest, screen->linestep());
+}
+
+template <class DST, class SRC>
+static inline void blit180(QScreen *screen, const QImage &image,
+ const QRect &rect, const QPoint &topLeft)
+{
+ const SRC *src = (const SRC*)(image.scanLine(rect.top())) + rect.left();
+ DST *dest = (DST*)(screen->base() + topLeft.y() * screen->linestep())
+ + topLeft.x();
+ qt_memrotate180(src, rect.width(), rect.height(), image.bytesPerLine(),
+ dest, screen->linestep());
+}
+
+template <class DST, class SRC>
+static inline void blit270(QScreen *screen, const QImage &image,
+ const QRect &rect, const QPoint &topLeft)
+{
+ const SRC *src = (const SRC *)(image.scanLine(rect.top())) + rect.left();
+ DST *dest = (DST*)(screen->base() + topLeft.y() * screen->linestep())
+ + topLeft.x();
+ qt_memrotate270(src, rect.width(), rect.height(), image.bytesPerLine(),
+ dest, screen->linestep());
+}
+
+typedef void (*BlitFunc)(QScreen *, const QImage &, const QRect &, const QPoint &);
+
+#define SET_BLIT_FUNC(dst, src, rotation, func) \
+do { \
+ switch (rotation) { \
+ case Rot90: \
+ func = blit90<dst, src>; \
+ break; \
+ case Rot180: \
+ func = blit180<dst, src>; \
+ break; \
+ case Rot270: \
+ func = blit270<dst, src>; \
+ break; \
+ default: \
+ break; \
+ } \
+} while (0)
+
+/*!
+ \reimp
+*/
+void QTransformedScreen::blit(const QImage &image, const QPoint &topLeft,
+ const QRegion &region)
+{
+ const Transformation trans = d_ptr->transformation;
+ if (trans == None) {
+ QProxyScreen::blit(image, topLeft, region);
+ return;
+ }
+
+ const QVector<QRect> rects = region.rects();
+ const QRect bound = QRect(0, 0, QScreen::w, QScreen::h)
+ & QRect(topLeft, image.size());
+
+ BlitFunc func = 0;
+#ifdef QT_QWS_DEPTH_GENERIC
+ if (d_ptr->doGenericColors && depth() == 16) {
+ if (image.depth() == 16)
+ SET_BLIT_FUNC(qrgb_generic16, quint16, trans, func);
+ else
+ SET_BLIT_FUNC(qrgb_generic16, quint32, trans, func);
+ } else
+#endif
+ switch (depth()) {
+#ifdef QT_QWS_DEPTH_32
+ case 32:
+#ifdef QT_QWS_DEPTH_16
+ if (image.depth() == 16)
+ SET_BLIT_FUNC(quint32, quint16, trans, func);
+ else
+#endif
+ SET_BLIT_FUNC(quint32, quint32, trans, func);
+ break;
+#endif
+#if defined(QT_QWS_DEPTH_24) || defined(QT_QWS_DEPTH18)
+ case 24:
+ case 18:
+ SET_BLIT_FUNC(quint24, quint24, trans, func);
+ break;
+#endif
+#if defined(QT_QWS_DEPTH_16) || defined(QT_QWS_DEPTH_15) || defined(QT_QWS_DEPTH_12)
+ case 16:
+ case 15:
+ case 12:
+ if (image.depth() == 16)
+ SET_BLIT_FUNC(quint16, quint16, trans, func);
+ else
+ SET_BLIT_FUNC(quint16, quint32, trans, func);
+ break;
+#endif
+#ifdef QT_QWS_DEPTH_8
+ case 8:
+ if (image.depth() == 16)
+ SET_BLIT_FUNC(quint8, quint16, trans, func);
+ else
+ SET_BLIT_FUNC(quint8, quint32, trans, func);
+ break;
+#endif
+ default:
+ return;
+ }
+ if (!func)
+ return;
+
+ QWSDisplay::grab();
+ for (int i = 0; i < rects.size(); ++i) {
+ const QRect r = rects.at(i) & bound;
+
+ QPoint dst;
+ switch (trans) {
+ case Rot90:
+ dst = mapToDevice(r.topRight(), QSize(w, h));
+ break;
+ case Rot180:
+ dst = mapToDevice(r.bottomRight(), QSize(w, h));
+ break;
+ case Rot270:
+ dst = mapToDevice(r.bottomLeft(), QSize(w, h));
+ break;
+ default:
+ break;
+ }
+ func(this, image, r.translated(-topLeft), dst);
+ }
+ QWSDisplay::ungrab();
+
+}
+
+/*!
+ \reimp
+*/
+void QTransformedScreen::solidFill(const QColor &color, const QRegion &region)
+{
+ const QRegion tr = mapToDevice(region, QSize(w,h));
+
+ Q_ASSERT(tr.boundingRect() == mapToDevice(region.boundingRect(), QSize(w,h)));
+
+#ifdef QT_REGION_DEBUG
+ qDebug() << "QTransformedScreen::solidFill region" << region << "transformed" << tr;
+#endif
+ QProxyScreen::solidFill(color, tr);
+}
+
+/*!
+ \reimp
+*/
+QSize QTransformedScreen::mapToDevice(const QSize &s) const
+{
+ switch (d_ptr->transformation) {
+ case None:
+ case Rot180:
+ break;
+ case Rot90:
+ case Rot270:
+ return QSize(s.height(), s.width());
+ break;
+ }
+ return s;
+}
+
+/*!
+ \reimp
+*/
+QSize QTransformedScreen::mapFromDevice(const QSize &s) const
+{
+ switch (d_ptr->transformation) {
+ case None:
+ case Rot180:
+ break;
+ case Rot90:
+ case Rot270:
+ return QSize(s.height(), s.width());
+ break;
+ }
+ return s;
+}
+
+/*!
+ \reimp
+*/
+QPoint QTransformedScreen::mapToDevice(const QPoint &p, const QSize &s) const
+{
+ QPoint rp(p);
+
+ switch (d_ptr->transformation) {
+ case None:
+ break;
+ case Rot90:
+ rp.setX(p.y());
+ rp.setY(s.width() - p.x() - 1);
+ break;
+ case Rot180:
+ rp.setX(s.width() - p.x() - 1);
+ rp.setY(s.height() - p.y() - 1);
+ break;
+ case Rot270:
+ rp.setX(s.height() - p.y() - 1);
+ rp.setY(p.x());
+ break;
+ }
+
+ return rp;
+}
+
+/*!
+ \reimp
+*/
+QPoint QTransformedScreen::mapFromDevice(const QPoint &p, const QSize &s) const
+{
+ QPoint rp(p);
+
+ switch (d_ptr->transformation) {
+ case None:
+ break;
+ case Rot90:
+ rp.setX(s.height() - p.y() - 1);
+ rp.setY(p.x());
+ break;
+ case Rot180:
+ rp.setX(s.width() - p.x() - 1);
+ rp.setY(s.height() - p.y() - 1);
+ break;
+ case Rot270:
+ rp.setX(p.y());
+ rp.setY(s.width() - p.x() - 1);
+ break;
+ }
+
+ return rp;
+}
+
+/*!
+ \reimp
+*/
+QRect QTransformedScreen::mapToDevice(const QRect &r, const QSize &s) const
+{
+ if (r.isNull())
+ return QRect();
+
+ QRect tr;
+ switch (d_ptr->transformation) {
+ case None:
+ tr = r;
+ break;
+ case Rot90:
+ tr.setCoords(r.y(), s.width() - r.x() - 1,
+ r.bottom(), s.width() - r.right() - 1);
+ break;
+ case Rot180:
+ tr.setCoords(s.width() - r.x() - 1, s.height() - r.y() - 1,
+ s.width() - r.right() - 1, s.height() - r.bottom() - 1);
+ break;
+ case Rot270:
+ tr.setCoords(s.height() - r.y() - 1, r.x(),
+ s.height() - r.bottom() - 1, r.right());
+ break;
+ }
+
+ return correctNormalized(tr);
+}
+
+/*!
+ \reimp
+*/
+QRect QTransformedScreen::mapFromDevice(const QRect &r, const QSize &s) const
+{
+ if (r.isNull())
+ return QRect();
+
+ QRect tr;
+ switch (d_ptr->transformation) {
+ case None:
+ tr = r;
+ break;
+ case Rot90:
+ tr.setCoords(s.height() - r.y() - 1, r.x(),
+ s.height() - r.bottom() - 1, r.right());
+ break;
+ case Rot180:
+ tr.setCoords(s.width() - r.x() - 1, s.height() - r.y() - 1,
+ s.width() - r.right() - 1, s.height() - r.bottom() - 1);
+ break;
+ case Rot270:
+ tr.setCoords(r.y(), s.width() - r.x() - 1,
+ r.bottom(), s.width() - r.right() - 1);
+ break;
+ }
+
+ return correctNormalized(tr);
+}
+
+/*!
+ \reimp
+*/
+QRegion QTransformedScreen::mapToDevice(const QRegion &rgn, const QSize &s) const
+{
+ if (d_ptr->transformation == None)
+ return QProxyScreen::mapToDevice(rgn, s);
+
+#ifdef QT_REGION_DEBUG
+ qDebug() << "mapToDevice size" << s << "rgn: " << rgn;
+#endif
+ QRect tr;
+ QRegion trgn;
+ QVector<QRect> a = rgn.rects();
+ const QRect *r = a.data();
+
+ int w = s.width();
+ int h = s.height();
+ int size = a.size();
+
+ switch (d_ptr->transformation) {
+ case None:
+ break;
+ case Rot90:
+ for (int i = 0; i < size; i++, r++) {
+ tr.setCoords(r->y(), w - r->x() - 1,
+ r->bottom(), w - r->right() - 1);
+ trgn |= correctNormalized(tr);
+ }
+ break;
+ case Rot180:
+ for (int i = 0; i < size; i++, r++) {
+ tr.setCoords(w - r->x() - 1, h - r->y() - 1,
+ w - r->right() - 1, h - r->bottom() - 1);
+ trgn |= correctNormalized(tr);
+ }
+ break;
+ case Rot270:
+ for (int i = 0; i < size; i++, r++) {
+ tr.setCoords(h - r->y() - 1, r->x(),
+ h - r->bottom() - 1, r->right());
+ trgn |= correctNormalized(tr);
+ }
+ break;
+ }
+#ifdef QT_REGION_DEBUG
+ qDebug() << "mapToDevice trgn: " << trgn;
+#endif
+ return trgn;
+}
+
+/*!
+ \reimp
+*/
+QRegion QTransformedScreen::mapFromDevice(const QRegion &rgn, const QSize &s) const
+{
+ if (d_ptr->transformation == None)
+ return QProxyScreen::mapFromDevice(rgn, s);
+
+#ifdef QT_REGION_DEBUG
+ qDebug() << "fromDevice: realRegion count: " << rgn.rects().size() << " isEmpty? " << rgn.isEmpty() << " bounds:" << rgn.boundingRect();
+#endif
+ QRect tr;
+ QRegion trgn;
+ QVector<QRect> a = rgn.rects();
+ const QRect *r = a.data();
+
+ int w = s.width();
+ int h = s.height();
+ int size = a.size();
+
+ switch (d_ptr->transformation) {
+ case None:
+ break;
+ case Rot90:
+ for (int i = 0; i < size; i++, r++) {
+ tr.setCoords(h - r->y() - 1, r->x(),
+ h - r->bottom() - 1, r->right());
+ trgn |= correctNormalized(tr);
+ }
+ break;
+ case Rot180:
+ for (int i = 0; i < size; i++, r++) {
+ tr.setCoords(w - r->x() - 1, h - r->y() - 1,
+ w - r->right() - 1, h - r->bottom() - 1);
+ trgn |= correctNormalized(tr);
+ }
+ break;
+ case Rot270:
+ for (int i = 0; i < size; i++, r++) {
+ tr.setCoords(r->y(), w - r->x() - 1,
+ r->bottom(), w - r->right() - 1);
+ trgn |= correctNormalized(tr);
+ }
+ break;
+ }
+#ifdef QT_REGION_DEBUG
+ qDebug() << "fromDevice: transRegion count: " << trgn.rects().size() << " isEmpty? " << trgn.isEmpty() << " bounds:" << trgn.boundingRect();
+#endif
+ return trgn;
+}
+
+/*!
+ \reimp
+*/
+void QTransformedScreen::setDirty(const QRect& rect)
+{
+ const QRect r = mapToDevice(rect, QSize(width(), height()));
+ QProxyScreen::setDirty(r);
+}
+
+/*!
+ \reimp
+*/
+QRegion QTransformedScreen::region() const
+{
+ QRegion deviceRegion = QProxyScreen::region();
+ return mapFromDevice(deviceRegion, QSize(deviceWidth(), deviceHeight()));
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_QWS_TRANSFORMED
diff --git a/src/gui/embedded/qscreentransformed_qws.h b/src/gui/embedded/qscreentransformed_qws.h
new file mode 100644
index 0000000000..e2d5a33e7f
--- /dev/null
+++ b/src/gui/embedded/qscreentransformed_qws.h
@@ -0,0 +1,103 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSCREENTRANSFORMED_QWS_H
+#define QSCREENTRANSFORMED_QWS_H
+
+#include <QtGui/qscreenproxy_qws.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+#ifndef QT_NO_QWS_TRANSFORMED
+
+class QTransformedScreenPrivate;
+
+class Q_AUTOTEST_EXPORT QTransformedScreen : public QProxyScreen
+{
+public:
+ explicit QTransformedScreen(int display_id);
+ ~QTransformedScreen();
+
+ enum Transformation { None, Rot90, Rot180, Rot270 };
+
+ void setTransformation(Transformation t);
+ Transformation transformation() const;
+ int transformOrientation() const;
+
+ QSize mapToDevice(const QSize &s) const;
+ QSize mapFromDevice(const QSize &s) const;
+
+ QPoint mapToDevice(const QPoint &, const QSize &) const;
+ QPoint mapFromDevice(const QPoint &, const QSize &) const;
+
+ QRect mapToDevice(const QRect &, const QSize &) const;
+ QRect mapFromDevice(const QRect &, const QSize &) const;
+
+ QRegion mapToDevice(const QRegion &, const QSize &) const;
+ QRegion mapFromDevice(const QRegion &, const QSize &) const;
+
+ bool connect(const QString &displaySpec);
+
+ bool isTransformed() const { return transformation() != None; }
+
+ void exposeRegion(QRegion region, int changing);
+ void blit(const QImage &img, const QPoint &topLeft, const QRegion &region);
+ void solidFill(const QColor &color, const QRegion &region);
+ void setDirty(const QRect&);
+
+ QRegion region() const;
+
+private:
+ friend class QTransformedScreenPrivate;
+ QTransformedScreenPrivate *d_ptr;
+};
+
+#endif // QT_NO_QWS_TRANSFORMED
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QSCREENTRANSFORMED_QWS_H
diff --git a/src/gui/embedded/qscreenvfb_qws.cpp b/src/gui/embedded/qscreenvfb_qws.cpp
new file mode 100644
index 0000000000..abbe73bd91
--- /dev/null
+++ b/src/gui/embedded/qscreenvfb_qws.cpp
@@ -0,0 +1,444 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QT_NO_QWS_QVFB
+
+#define QTOPIA_QVFB_BRIGHTNESS
+
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/ipc.h>
+#include <sys/shm.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#include <qvfbhdr.h>
+#include <qscreenvfb_qws.h>
+#include <qkbdvfb_qws.h>
+#include <qmousevfb_qws.h>
+#include <qwindowsystem_qws.h>
+#include <qsocketnotifier.h>
+#include <qapplication.h>
+#include <qscreen_qws.h>
+#include <qmousedriverfactory_qws.h>
+#include <qkbddriverfactory_qws.h>
+#include <qdebug.h>
+
+QT_BEGIN_NAMESPACE
+
+class QVFbScreenPrivate
+{
+public:
+ QVFbScreenPrivate();
+ ~QVFbScreenPrivate();
+
+ bool success;
+ unsigned char *shmrgn;
+ int brightness;
+ bool blank;
+ QVFbHeader *hdr;
+ QWSMouseHandler *mouse;
+#ifndef QT_NO_QWS_KEYBOARD
+ QWSKeyboardHandler *keyboard;
+#endif
+};
+
+QVFbScreenPrivate::QVFbScreenPrivate()
+ : mouse(0)
+
+{
+#ifndef QT_NO_QWS_KEYBOARD
+ keyboard = 0;
+#endif
+ brightness = 255;
+ blank = false;
+}
+
+QVFbScreenPrivate::~QVFbScreenPrivate()
+{
+ delete mouse;
+#ifndef QT_NO_QWS_KEYBOARD
+ delete keyboard;
+#endif
+}
+
+/*!
+ \internal
+
+ \class QVFbScreen
+ \ingroup qws
+
+ \brief The QVFbScreen class implements a screen driver for the
+ virtual framebuffer.
+
+ Note that this class is only available in \l{Qt for Embedded Linux}.
+ Custom screen drivers can be added by subclassing the
+ QScreenDriverPlugin class, using the QScreenDriverFactory class to
+ dynamically load the driver into the application, but there should
+ only be one screen object per application.
+
+ The Qt for Embedded Linux platform provides a \l{The Virtual
+ Framebuffer}{virtual framebuffer} for development and debugging;
+ the virtual framebuffer allows Qt for Embedded Linux applications to be
+ developed on a desktop machine, without switching between consoles
+ and X11.
+
+ \sa QScreen, QScreenDriverPlugin, {Running Applications}
+*/
+
+/*!
+ \fn bool QVFbScreen::connect(const QString & displaySpec)
+ \reimp
+*/
+
+/*!
+ \fn void QVFbScreen::disconnect()
+ \reimp
+*/
+
+/*!
+ \fn bool QVFbScreen::initDevice()
+ \reimp
+*/
+
+/*!
+ \fn void QVFbScreen::restore()
+ \reimp
+*/
+
+/*!
+ \fn void QVFbScreen::save()
+ \reimp
+*/
+
+/*!
+ \fn void QVFbScreen::setDirty(const QRect & r)
+ \reimp
+*/
+
+/*!
+ \fn void QVFbScreen::setMode(int nw, int nh, int nd)
+ \reimp
+*/
+
+/*!
+ \fn void QVFbScreen::shutdownDevice()
+ \reimp
+*/
+
+/*!
+ \fn QVFbScreen::QVFbScreen(int displayId)
+
+ Constructs a QVNCScreen object. The \a displayId argument
+ identifies the Qt for Embedded Linux server to connect to.
+*/
+QVFbScreen::QVFbScreen(int display_id)
+ : QScreen(display_id, VFbClass), d_ptr(new QVFbScreenPrivate)
+{
+ d_ptr->shmrgn = 0;
+ d_ptr->hdr = 0;
+ data = 0;
+}
+
+/*!
+ Destroys this QVFbScreen object.
+*/
+QVFbScreen::~QVFbScreen()
+{
+ delete d_ptr;
+}
+
+static QVFbScreen *connected = 0;
+
+bool QVFbScreen::connect(const QString &displaySpec)
+{
+ QStringList displayArgs = displaySpec.split(QLatin1Char(':'));
+ if (displayArgs.contains(QLatin1String("Gray")))
+ grayscale = true;
+
+ key_t key = ftok(QByteArray(QT_VFB_MOUSE_PIPE).replace("%1", QByteArray::number(displayId)), 'b');
+
+ if (key == -1)
+ return false;
+
+#if Q_BYTE_ORDER == Q_BIG_ENDIAN
+#ifndef QT_QWS_FRAMEBUFFER_LITTLE_ENDIAN
+ if (displayArgs.contains(QLatin1String("littleendian")))
+#endif
+ QScreen::setFrameBufferLittleEndian(true);
+#endif
+
+ int shmId = shmget(key, 0, 0);
+ if (shmId != -1)
+ d_ptr->shmrgn = (unsigned char *)shmat(shmId, 0, 0);
+ else
+ return false;
+
+ if ((long)d_ptr->shmrgn == -1 || d_ptr->shmrgn == 0) {
+ qDebug("No shmrgn %ld", (long)d_ptr->shmrgn);
+ return false;
+ }
+
+ d_ptr->hdr = (QVFbHeader *)d_ptr->shmrgn;
+ data = d_ptr->shmrgn + d_ptr->hdr->dataoffset;
+
+ dw = w = d_ptr->hdr->width;
+ dh = h = d_ptr->hdr->height;
+ d = d_ptr->hdr->depth;
+
+ switch (d) {
+ case 1:
+ setPixelFormat(QImage::Format_Mono);
+ break;
+ case 8:
+ setPixelFormat(QImage::Format_Indexed8);
+ break;
+ case 12:
+ setPixelFormat(QImage::Format_RGB444);
+ break;
+ case 15:
+ setPixelFormat(QImage::Format_RGB555);
+ break;
+ case 16:
+ setPixelFormat(QImage::Format_RGB16);
+ break;
+ case 18:
+ setPixelFormat(QImage::Format_RGB666);
+ break;
+ case 24:
+ setPixelFormat(QImage::Format_RGB888);
+ break;
+ case 32:
+ setPixelFormat(QImage::Format_ARGB32_Premultiplied);
+ break;
+ }
+
+ lstep = d_ptr->hdr->linestep;
+
+ // Handle display physical size spec.
+ int dimIdxW = -1;
+ int dimIdxH = -1;
+ for (int i = 0; i < displayArgs.size(); ++i) {
+ if (displayArgs.at(i).startsWith(QLatin1String("mmWidth"))) {
+ dimIdxW = i;
+ break;
+ }
+ }
+ for (int i = 0; i < displayArgs.size(); ++i) {
+ if (displayArgs.at(i).startsWith(QLatin1String("mmHeight"))) {
+ dimIdxH = i;
+ break;
+ }
+ }
+ if (dimIdxW >= 0) {
+ bool ok;
+ int pos = 7;
+ if (displayArgs.at(dimIdxW).at(pos) == QLatin1Char('='))
+ ++pos;
+ int pw = displayArgs.at(dimIdxW).mid(pos).toInt(&ok);
+ if (ok) {
+ physWidth = pw;
+ if (dimIdxH < 0)
+ physHeight = dh*physWidth/dw;
+ }
+ }
+ if (dimIdxH >= 0) {
+ bool ok;
+ int pos = 8;
+ if (displayArgs.at(dimIdxH).at(pos) == QLatin1Char('='))
+ ++pos;
+ int ph = displayArgs.at(dimIdxH).mid(pos).toInt(&ok);
+ if (ok) {
+ physHeight = ph;
+ if (dimIdxW < 0)
+ physWidth = dw*physHeight/dh;
+ }
+ }
+ if (dimIdxW < 0 && dimIdxH < 0) {
+ const int dpi = 72;
+ physWidth = qRound(dw * 25.4 / dpi);
+ physHeight = qRound(dh * 25.4 / dpi);
+ }
+
+ qDebug("Connected to VFB server %s: %d x %d x %d %dx%dmm (%dx%ddpi)", displaySpec.toLatin1().data(),
+ w, h, d, physWidth, physHeight, qRound(dw*25.4/physWidth), qRound(dh*25.4/physHeight) );
+
+ size = lstep * h;
+ mapsize = size;
+ screencols = d_ptr->hdr->numcols;
+ memcpy(screenclut, d_ptr->hdr->clut, sizeof(QRgb) * screencols);
+
+ connected = this;
+
+ return true;
+}
+
+void QVFbScreen::disconnect()
+{
+ connected = 0;
+ if ((long)d_ptr->shmrgn != -1 && d_ptr->shmrgn) {
+ if (qApp->type() == QApplication::GuiServer && d_ptr->hdr->dataoffset >= (int)sizeof(QVFbHeader)) {
+ d_ptr->hdr->serverVersion = 0;
+ }
+ shmdt((char*)d_ptr->shmrgn);
+ }
+}
+
+bool QVFbScreen::initDevice()
+{
+#ifndef QT_NO_QWS_MOUSE_QVFB
+ const QString mouseDev = QString(QLatin1String(QT_VFB_MOUSE_PIPE))
+ .arg(displayId);
+ d_ptr->mouse = new QVFbMouseHandler(QLatin1String("QVFbMouse"), mouseDev);
+ qwsServer->setDefaultMouse("None");
+ if (d_ptr->mouse)
+ d_ptr->mouse->setScreen(this);
+#endif
+
+#if !defined(QT_NO_QWS_KBD_QVFB) && !defined(QT_NO_QWS_KEYBOARD)
+ const QString keyboardDev = QString(QLatin1String(QT_VFB_KEYBOARD_PIPE))
+ .arg(displayId);
+ d_ptr->keyboard = new QVFbKeyboardHandler(keyboardDev);
+ qwsServer->setDefaultKeyboard("None");
+#endif
+
+ if (d_ptr->hdr->dataoffset >= (int)sizeof(QVFbHeader))
+ d_ptr->hdr->serverVersion = QT_VERSION;
+
+ if(d==8) {
+ screencols=256;
+ if (grayscale) {
+ // Build grayscale palette
+ for(int loopc=0;loopc<256;loopc++) {
+ screenclut[loopc]=qRgb(loopc,loopc,loopc);
+ }
+ } else {
+ // 6x6x6 216 color cube
+ int idx = 0;
+ for(int ir = 0x0; ir <= 0xff; ir+=0x33) {
+ for(int ig = 0x0; ig <= 0xff; ig+=0x33) {
+ for(int ib = 0x0; ib <= 0xff; ib+=0x33) {
+ screenclut[idx]=qRgb(ir, ig, ib);
+ idx++;
+ }
+ }
+ }
+ screencols=idx;
+ }
+ memcpy(d_ptr->hdr->clut, screenclut, sizeof(QRgb) * screencols);
+ d_ptr->hdr->numcols = screencols;
+ } else if (d == 4) {
+ int val = 0;
+ for (int idx = 0; idx < 16; idx++, val += 17) {
+ screenclut[idx] = qRgb(val, val, val);
+ }
+ screencols = 16;
+ memcpy(d_ptr->hdr->clut, screenclut, sizeof(QRgb) * screencols);
+ d_ptr->hdr->numcols = screencols;
+ } else if (d == 1) {
+ screencols = 2;
+ screenclut[1] = qRgb(0xff, 0xff, 0xff);
+ screenclut[0] = qRgb(0, 0, 0);
+ memcpy(d_ptr->hdr->clut, screenclut, sizeof(QRgb) * screencols);
+ d_ptr->hdr->numcols = screencols;
+ }
+
+#ifndef QT_NO_QWS_CURSOR
+ QScreenCursor::initSoftwareCursor();
+#endif
+ return true;
+}
+
+void QVFbScreen::shutdownDevice()
+{
+}
+
+void QVFbScreen::setMode(int ,int ,int)
+{
+}
+
+// save the state of the graphics card
+// This is needed so that e.g. we can restore the palette when switching
+// between linux virtual consoles.
+void QVFbScreen::save()
+{
+ // nothing to do.
+}
+
+// restore the state of the graphics card.
+void QVFbScreen::restore()
+{
+}
+void QVFbScreen::setDirty(const QRect& rect)
+{
+ const QRect r = rect.translated(-offset());
+ d_ptr->hdr->dirty = true;
+ d_ptr->hdr->update = d_ptr->hdr->update.united(r);
+}
+
+void QVFbScreen::setBrightness(int b)
+{
+ if (connected) {
+ connected->d_ptr->brightness = b;
+
+ QVFbHeader *hdr = connected->d_ptr->hdr;
+ if (hdr->viewerVersion < 0x040400) // brightness not supported
+ return;
+
+ const int br = connected->d_ptr->blank ? 0 : b;
+ if (hdr->brightness != br) {
+ hdr->brightness = br;
+ connected->setDirty(connected->region().boundingRect());
+ }
+ }
+}
+
+void QVFbScreen::blank(bool on)
+{
+ d_ptr->blank = on;
+ setBrightness(connected->d_ptr->brightness);
+}
+
+#endif // QT_NO_QWS_QVFB
+
+QT_END_NAMESPACE
diff --git a/src/gui/embedded/qscreenvfb_qws.h b/src/gui/embedded/qscreenvfb_qws.h
new file mode 100644
index 0000000000..1f8edf2d7a
--- /dev/null
+++ b/src/gui/embedded/qscreenvfb_qws.h
@@ -0,0 +1,86 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSCREENVFB_QWS_H
+#define QSCREENVFB_QWS_H
+
+#include <QtGui/qscreen_qws.h>
+#include <QtGui/qvfbhdr.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+#ifndef QT_NO_QWS_QVFB
+
+class QVFbScreenPrivate;
+
+class Q_GUI_EXPORT QVFbScreen : public QScreen
+{
+public:
+ explicit QVFbScreen(int display_id);
+ virtual ~QVFbScreen();
+ virtual bool initDevice();
+ virtual bool connect(const QString &displaySpec);
+ virtual void disconnect();
+ virtual void shutdownDevice();
+ virtual void save();
+ virtual void restore();
+ virtual void setMode(int nw,int nh,int nd);
+ virtual void setDirty(const QRect& r);
+ virtual void blank(bool);
+#ifdef QTOPIA_QVFB_BRIGHTNESS
+ static void setBrightness(int b);
+#endif
+
+private:
+ QVFbScreenPrivate *d_ptr;
+};
+
+#endif // QT_NO_QWS_QVFB
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QSCREENVFB_QWS_H
diff --git a/src/gui/embedded/qsoundqss_qws.cpp b/src/gui/embedded/qsoundqss_qws.cpp
new file mode 100644
index 0000000000..c72ea9121b
--- /dev/null
+++ b/src/gui/embedded/qsoundqss_qws.cpp
@@ -0,0 +1,1498 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qsoundqss_qws.h"
+
+#ifndef QT_NO_SOUND
+#include <qbytearray.h>
+#include <qlist.h>
+#include <qsocketnotifier.h>
+#include <qfile.h>
+#include <qfileinfo.h>
+#include <qstringlist.h>
+#include <qevent.h>
+#include <qalgorithms.h>
+#include <qtimer.h>
+#include <qpointer.h>
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <time.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <sys/soundcard.h>
+
+#include <qdebug.h>
+
+QT_BEGIN_NAMESPACE
+
+extern int errno;
+
+#define QT_QWS_SOUND_16BIT 1 // or 0, or undefined for always 0
+#define QT_QWS_SOUND_STEREO 1 // or 0, or undefined for always 0
+
+// Zaurus SL5000D doesn't seem to return any error if setting to 44000 and it fails,
+// however 44100 works, 44100 is more common that 44000.
+static int sound_speed = 44100;
+#ifndef QT_NO_QWS_SOUNDSERVER
+extern int qws_display_id;
+#define SOUND_PIPE "/tmp/.qt_soundserver-%1"
+#endif
+
+static char *zeroMem = 0;
+
+struct QRiffChunk {
+ char id[4];
+ quint32 size;
+ char data[4/*size*/];
+};
+
+#if defined(QT_QWS_IPAQ)
+static const int sound_fragment_size = 12;
+#else
+static const int sound_fragment_size = 12;
+#endif
+static const int sound_buffer_size = 1 << sound_fragment_size;
+// nb. there will be an sound startup delay of
+// 2^sound_fragment_size / sound_speed seconds.
+// (eg. sound_fragment_size==12, sound_speed==44000 means 0.093s delay)
+
+#ifdef QT_QWS_SOUND_STEREO
+static int sound_stereo=QT_QWS_SOUND_STEREO;
+#else
+static const int sound_stereo=0;
+#endif
+#ifdef QT_QWS_SOUND_16BIT
+static bool sound_16bit=QT_QWS_SOUND_16BIT;
+#else
+static const bool sound_16bit=false;
+#endif
+
+#ifndef QT_NO_QWS_SOUNDSERVER
+class QWSSoundServerClient : public QObject {
+ Q_OBJECT
+
+public:
+ QWSSoundServerClient(QWS_SOCK_BASE *s, QObject* parent);
+ ~QWSSoundServerClient();
+
+public slots:
+ void sendSoundCompleted(int, int);
+ void sendDeviceReady(int, int);
+ void sendDeviceError(int, int, int);
+
+signals:
+ void play(int, int, const QString&);
+ void play(int, int, const QString&, int, int);
+ void playRaw(int, int, const QString&, int, int, int, int);
+
+ void pause(int, int);
+ void stop(int, int);
+ void resume(int, int);
+ void setVolume(int, int, int, int);
+ void setMute(int, int, bool);
+
+ void stopAll(int);
+
+ void playPriorityOnly(bool);
+
+ void setSilent( bool );
+
+private slots:
+ void tryReadCommand();
+
+private:
+ void sendClientMessage(QString msg);
+ int mCurrentID;
+ int left, right;
+ bool priExist;
+ static int lastId;
+ static int nextId() { return ++lastId; }
+ QPointer<QWS_SOCK_BASE> socket;
+};
+
+int QWSSoundServerClient::lastId = 0;
+
+QWSSoundServerClient::QWSSoundServerClient(QWS_SOCK_BASE *s, QObject* parent) :
+ QObject( parent )
+{
+ socket = s;
+ priExist = false;
+ mCurrentID = nextId();
+ connect(socket,SIGNAL(readyRead()),
+ this,SLOT(tryReadCommand()));
+ connect(socket, SIGNAL(disconnected()), this, SLOT(deleteLater()));
+}
+
+QWSSoundServerClient::~QWSSoundServerClient()
+{
+ if (priExist)
+ playPriorityOnly(false);
+ emit stopAll(mCurrentID);
+ if (socket)
+ socket->deleteLater();
+}
+
+static QString getStringTok(QString &in)
+{
+ int pos = in.indexOf(QLatin1Char(' '));
+ QString ret;
+ if (pos > 0) {
+ ret = in.left(pos);
+ in = in.mid(pos+1);
+ } else {
+ ret = in;
+ in = QString::null;
+ }
+ return ret;
+}
+
+static int getNumTok(QString &in)
+{
+ return getStringTok(in).toInt();
+}
+
+void QWSSoundServerClient::tryReadCommand()
+{
+ while ( socket->canReadLine() ) {
+ QString l = QString::fromAscii(socket->readLine());
+ l.truncate(l.length()-1); // chomp
+ QString functionName = getStringTok(l);
+ int soundid = getNumTok(l);
+ if (functionName == QLatin1String("PLAY")) {
+ emit play(mCurrentID, soundid, l);
+ } else if (functionName == QLatin1String("PLAYEXTEND")) {
+ int volume = getNumTok(l);
+ int flags = getNumTok(l);
+ emit play(mCurrentID, soundid, l, volume, flags);
+ } else if (functionName == QLatin1String("PLAYRAW")) {
+ int chs = getNumTok(l);
+ int freq = getNumTok(l);
+ int bitspersample = getNumTok(l);
+ int flags = getNumTok(l);
+ emit playRaw(mCurrentID, soundid, l, freq, chs, bitspersample, flags);
+ } else if (functionName == QLatin1String("PAUSE")) {
+ emit pause(mCurrentID, soundid);
+ } else if (functionName == QLatin1String("STOP")) {
+ emit stop(mCurrentID, soundid);
+ } else if (functionName == QLatin1String("RESUME")) {
+ emit resume(mCurrentID, soundid);
+ } else if (functionName == QLatin1String("SETVOLUME")) {
+ int left = getNumTok(l);
+ int right = getNumTok(l);
+ emit setVolume(mCurrentID, soundid, left, right);
+ } else if (functionName == QLatin1String("MUTE")) {
+ emit setMute(mCurrentID, soundid, true);
+ } else if (functionName == QLatin1String("UNMUTE")) {
+ emit setMute(mCurrentID, soundid, false);
+ } else if (functionName == QLatin1String("PRIORITYONLY")) {
+ bool sPri = soundid != 0;
+ if (sPri != priExist) {
+ priExist = sPri;
+ emit playPriorityOnly(sPri);
+ }
+ } else if(functionName == QLatin1String("SILENT")) {
+ emit setSilent( soundid != 0 );
+ }
+ }
+}
+
+void QWSSoundServerClient::sendClientMessage(QString msg)
+{
+#ifndef QT_NO_TEXTCODEC
+ QByteArray u = msg.toUtf8();
+#else
+ QByteArray u = msg.toLatin1();
+#endif
+ socket->write(u.data(), u.length());
+ socket->flush();
+}
+
+void QWSSoundServerClient::sendSoundCompleted(int gid, int sid)
+{
+ if (gid == mCurrentID)
+ sendClientMessage(QLatin1String("SOUNDCOMPLETED ")
+ + QString::number(sid) + QLatin1Char('\n'));
+}
+
+void QWSSoundServerClient::sendDeviceReady(int gid, int sid)
+{
+ if (gid == mCurrentID)
+ sendClientMessage(QLatin1String("DEVICEREADY ")
+ + QString::number(sid) + QLatin1Char('\n'));
+}
+
+void QWSSoundServerClient::sendDeviceError(int gid, int sid, int err)
+{
+ if (gid == mCurrentID)
+ sendClientMessage(QLatin1String("DEVICEERROR ")
+ + QString::number(sid) + QLatin1Char(' ')
+ + QString::number(err) + QLatin1Char('\n'));
+}
+#endif
+
+static const int maxVolume = 100;
+static const int runinLength = 2*sound_buffer_size;
+class QWSSoundServerProvider {
+public:
+ QWSSoundServerProvider(int w, int s)
+ : mWid(w), mSid(s), mMuted(false)
+ {
+ leftVolume = maxVolume>>1;
+ rightVolume = maxVolume>>1;
+ isPriority = false;
+ samples_due = 0;
+ max1 = max2 = out = 0;//= sound_buffer_size;
+ data = data1;
+ max = &max1;
+ sampleRunin = 0;
+ dev = -1;
+ }
+
+ virtual ~QWSSoundServerProvider() {
+ }
+
+ int groupId() const { return mWid; }
+ int soundId() const { return mSid; }
+
+ void startSampleRunin() {
+ // inteded to provide even audio return from mute/pause/dead samples.
+ //sampleRunin = runinLength; // or more?
+ }
+
+
+ void setVolume(int lv, int rv) {
+ leftVolume = qMin(maxVolume, qMax(0, lv));
+ rightVolume = qMin(maxVolume, qMax(0, rv));
+ }
+
+ void setMute(bool m) { mMuted = m; }
+ bool muted() { return mMuted; }
+
+ void setPriority(bool p) {
+ if (p != isPriority) {
+ isPriority = p; // currently meaningless.
+ }
+ }
+
+
+ static void setPlayPriorityOnly(bool p)
+ {
+ if (p)
+ priorityExists++;
+ else
+ priorityExists--;
+
+ if (priorityExists < 0)
+ qDebug("QSS: got more priority offs than ons");
+ }
+
+ // return -1 for file broken, give up.
+ // else return sampels ready for playing.
+ // argument is max samples server is looking for,
+ // in terms of current device status.
+ virtual int readySamples(int) = 0;
+
+ int getSample(int off, int bps) {
+ return (bps == 1) ? (data[out+off] - 128) * 128 : ((short*)data)[(out/2)+off];
+ }
+
+ int add(int* mixl, int* mixr, int count)
+ {
+ int bytesPerSample = chunkdata.wBitsPerSample >> 3;
+
+ if ( mMuted ) {
+ sampleRunin -= qMin(sampleRunin,count);
+ while (count && (dev != -1)) {
+ if (out >= *max) {
+ // switch buffers
+ out = 0;
+ if (data == data1 && max2 != 0) {
+ data = data2;
+ max = &max2;
+ max1 = 0;
+ } else if (data == data2 && max1 != 0) {
+ data = data1;
+ max = &max1;
+ max2 = 0;
+ } else {
+ qDebug("QSS Read Error: both buffers empty");
+ return 0;
+ }
+ }
+ samples_due += sound_speed;
+ while (count && samples_due >= chunkdata.samplesPerSec) {
+ samples_due -= chunkdata.samplesPerSec;
+ count--;
+ }
+ out += bytesPerSample * chunkdata.channels;
+ }
+ return count;
+ }
+
+ // This shouldn't be the case
+ if ( !mixl || !mixr )
+ return 0;
+
+ int lVolNum = leftVolume, lVolDen = maxVolume;
+ int rVolNum = rightVolume, rVolDen = maxVolume;
+ if (priorityExists > 0 && !isPriority) {
+ lVolNum = 0; // later, make this gradually fade in and out.
+ lVolDen = 5;
+ rVolNum = 0;
+ rVolDen = 5;
+ }
+
+ while (count && (dev != -1)) {
+ if (out >= *max) {
+ // switch buffers
+ out = 0;
+ if (data == data1 && max2 != 0) {
+ data = data2;
+ max = &max2;
+ max1 = 0;
+ } else if (data == data2 && max1 != 0) {
+ data = data1;
+ max = &max1;
+ max2 = 0;
+ } else {
+ qDebug("QSS Read Error: both buffers empty");
+ return 0;
+ }
+ }
+ samples_due += sound_speed;
+ if (count && samples_due >= chunkdata.samplesPerSec) {
+ int l = getSample(0,bytesPerSample)*lVolNum/lVolDen;
+ int r = (chunkdata.channels == 2) ? getSample(1,bytesPerSample)*rVolNum/rVolDen : l;
+ if (!sound_stereo && chunkdata.channels == 2)
+ l += r;
+ if (sampleRunin) {
+ while (sampleRunin && count && samples_due >= chunkdata.samplesPerSec) {
+ mixl++;
+ if (sound_stereo)
+ mixr++;
+ samples_due -= chunkdata.samplesPerSec;
+ sampleRunin--;
+ count--;
+ }
+ }
+ while (count && samples_due >= chunkdata.samplesPerSec) {
+ *mixl++ += l;
+ if (sound_stereo)
+ *mixr++ += r;
+ samples_due -= chunkdata.samplesPerSec;
+ count--;
+ }
+ }
+
+ // optimize out manipulation of sample if downsampling and we skip it
+ out += bytesPerSample * chunkdata.channels;
+ }
+
+ return count;
+ }
+
+ virtual bool finished() const = 0;
+
+ bool equal(int wid, int sid)
+ {
+ return (wid == mWid && sid == mSid);
+ }
+
+protected:
+
+ char * prepareBuffer( int &size)
+ {
+ // keep reading as long as there is 50 % or more room in off buffer.
+ if (data == data1 && (max2<<1 < sound_buffer_size)) {
+ size=sound_buffer_size - max2;
+ return (char *)data2;
+ } else if (data == data2 && (max1<<1 < sound_buffer_size)) {
+ size=sound_buffer_size - max1;
+ return (char *)data1;
+ } else {
+ size = 0;
+ return 0;
+ }
+ }
+
+ void updateBuffer(int read)
+ {
+ // always reads to off buffer.
+ if (read >= 0) {
+ if (data == data2) {
+ max1 = read;
+ } else {
+ max2 = read;
+ }
+ }
+ }
+
+ int devSamples()
+ {
+ int possible = (((max1+max2-out) / ((chunkdata.wBitsPerSample>>3)*chunkdata.channels))
+ *sound_speed)/chunkdata.samplesPerSec;
+
+ return possible;
+ }
+
+
+ struct {
+ qint16 formatTag;
+ qint16 channels;
+ qint32 samplesPerSec;
+ qint32 avgBytesPerSec;
+ qint16 blockAlign;
+ qint16 wBitsPerSample;
+ } chunkdata;
+ int dev;
+ int samples_due;
+private:
+ int mWid;
+ int mSid;
+ int leftVolume;
+ int rightVolume;
+ bool isPriority;
+ static int priorityExists;
+ int *max;
+ uchar *data;
+ uchar data1[sound_buffer_size+4]; // +4 to handle badly aligned input data
+ uchar data2[sound_buffer_size+4]; // +4 to handle badly aligned input data
+ int out, max1, max2;
+ int sampleRunin;
+ bool mMuted;
+};
+
+int QWSSoundServerProvider::priorityExists = 0;
+
+class QWSSoundServerBucket : public QWSSoundServerProvider {
+public:
+ QWSSoundServerBucket(int d, int wid, int sid)
+ : QWSSoundServerProvider(wid, sid)
+ {
+ dev = d;
+ wavedata_remaining = -1;
+ mFinishedRead = false;
+ mInsufficientSamples = false;
+ }
+ ~QWSSoundServerBucket()
+ {
+ //dev->close();
+ ::close(dev);
+ }
+ bool finished() const
+ {
+ //return !max;
+ return mInsufficientSamples && mFinishedRead ;
+ }
+ int readySamples(int)
+ {
+ int size;
+ char *dest = prepareBuffer(size);
+ // may want to change this to something like
+ // if (data == data1 && max2<<1 < sound_buffer_size
+ // ||
+ // data == data2 && max1<<1 < sound_buffer_size)
+ // so will keep filling off buffer while there is +50% space left
+ if (size > 0 && dest != 0) {
+ while ( wavedata_remaining < 0 ) {
+ //max = 0;
+ wavedata_remaining = -1;
+ // Keep reading chunks...
+ const int n = sizeof(chunk)-sizeof(chunk.data);
+ int nr = ::read(dev, (void*)&chunk,n);
+ if ( nr != n ) {
+ // XXX check error? or don't we care?
+ wavedata_remaining = 0;
+ mFinishedRead = true;
+ } else if ( qstrncmp(chunk.id,"data",4) == 0 ) {
+ wavedata_remaining = chunk.size;
+
+ //out = max = sound_buffer_size;
+
+ } else if ( qstrncmp(chunk.id,"RIFF",4) == 0 ) {
+ char d[4];
+ if ( read(dev, d, 4) != 4 ) {
+ // XXX check error? or don't we care?
+ //qDebug("couldn't read riff");
+ mInsufficientSamples = true;
+ mFinishedRead = true;
+ return 0;
+ } else if ( qstrncmp(d,"WAVE",4) != 0 ) {
+ // skip
+ if ( chunk.size > 1000000000 || lseek(dev,chunk.size-4, SEEK_CUR) == -1 ) {
+ //qDebug("oversized wav chunk");
+ mFinishedRead = true;
+ }
+ }
+ } else if ( qstrncmp(chunk.id,"fmt ",4) == 0 ) {
+ if ( ::read(dev,(char*)&chunkdata,sizeof(chunkdata)) != sizeof(chunkdata) ) {
+ // XXX check error? or don't we care?
+ //qDebug("couldn't ready chunkdata");
+ mFinishedRead = true;
+ }
+#define WAVE_FORMAT_PCM 1
+ else if ( chunkdata.formatTag != WAVE_FORMAT_PCM ) {
+ //qDebug("WAV file: UNSUPPORTED FORMAT %d",chunkdata.formatTag);
+ mFinishedRead = true;
+ }
+ } else {
+ // ignored chunk
+ if ( chunk.size > 1000000000 || lseek(dev, chunk.size, SEEK_CUR) == -1) {
+ //qDebug("chunk size too big");
+ mFinishedRead = true;
+ }
+ }
+ }
+ // this looks wrong.
+ if (wavedata_remaining <= 0) {
+ mFinishedRead = true;
+ }
+
+ }
+ // may want to change this to something like
+ // if (data == data1 && max2<<1 < sound_buffer_size
+ // ||
+ // data == data2 && max1<<1 < sound_buffer_size)
+ // so will keep filling off buffer while there is +50% space left
+
+ if (wavedata_remaining) {
+ if (size > 0 && dest != 0) {
+ int read = ::read(dev, dest, qMin(size, wavedata_remaining));
+ // XXX check error? or don't we care?
+ wavedata_remaining -= read;
+ updateBuffer(read);
+ if (read <= 0) // data unexpectidly ended
+ mFinishedRead = true;
+ }
+ }
+ int possible = devSamples();
+ if (possible == 0)
+ mInsufficientSamples = true;
+ return possible;
+ }
+
+protected:
+ QRiffChunk chunk;
+ int wavedata_remaining;
+ bool mFinishedRead;
+ bool mInsufficientSamples;
+};
+
+class QWSSoundServerStream : public QWSSoundServerProvider {
+public:
+ QWSSoundServerStream(int d,int c, int f, int b,
+ int wid, int sid)
+ : QWSSoundServerProvider(wid, sid)
+ {
+ chunkdata.channels = c;
+ chunkdata.samplesPerSec = f;
+ chunkdata.wBitsPerSample = b;
+ dev = d;
+ //fcntl( dev, F_SETFL, O_NONBLOCK );
+ lasttime = 0;
+ }
+
+ ~QWSSoundServerStream()
+ {
+ if (dev != -1) {
+ ::close(dev);
+ dev = -1;
+ }
+ }
+
+ bool finished() const
+ {
+ return (dev == -1);
+ }
+
+
+ int readySamples(int)
+ {
+ int size;
+ char *dest = prepareBuffer(size);
+ if (size > 0 && dest != 0 && dev != -1) {
+
+ int read = ::read(dev, dest, size);
+ if (read < 0) {
+ switch(errno) {
+ case EAGAIN:
+ case EINTR:
+ // means read may yet succeed on the next attempt
+ break;
+ default:
+ // unexpected error, fail.
+ ::close(dev);
+ dev = -1;
+ }
+ } else if (read == 0) {
+ // 0 means writer has closed dev and/or
+ // file is at end.
+ ::close(dev);
+ dev = -1;
+ } else {
+ updateBuffer(read);
+ }
+ }
+ int possible = devSamples();
+ if (possible == 0)
+ startSampleRunin();
+ return possible;
+ }
+
+protected:
+ time_t lasttime;
+};
+
+#ifndef QT_NO_QWS_SOUNDSERVER
+QWSSoundServerSocket::QWSSoundServerSocket(QObject *parent) :
+ QWSServerSocket(QString::fromLatin1(SOUND_PIPE).arg(qws_display_id), parent)
+{
+ connect(this, SIGNAL(newConnection()), this, SLOT(newConnection()));
+}
+
+
+#ifdef QT3_SUPPORT
+QWSSoundServerSocket::QWSSoundServerSocket(QObject *parent, const char *name) :
+ QWSServerSocket(QString::fromLatin1(SOUND_PIPE).arg(qws_display_id), parent)
+{
+ if (name)
+ setObjectName(QString::fromAscii(name));
+ connect(this, SIGNAL(newConnection()), this, SLOT(newConnection()));
+}
+#endif
+
+void QWSSoundServerSocket::newConnection()
+{
+ while (QWS_SOCK_BASE *sock = nextPendingConnection()) {
+ QWSSoundServerClient* client = new QWSSoundServerClient(sock,this);
+
+ connect(client, SIGNAL(play(int,int,QString)),
+ this, SIGNAL(playFile(int,int,QString)));
+ connect(client, SIGNAL(play(int,int,QString,int,int)),
+ this, SIGNAL(playFile(int,int,QString,int,int)));
+ connect(client, SIGNAL(playRaw(int,int,QString,int,int,int,int)),
+ this, SIGNAL(playRawFile(int,int,QString,int,int,int,int)));
+
+ connect(client, SIGNAL(pause(int,int)),
+ this, SIGNAL(pauseFile(int,int)));
+ connect(client, SIGNAL(stop(int,int)),
+ this, SIGNAL(stopFile(int,int)));
+ connect(client, SIGNAL(playPriorityOnly(bool)),
+ this, SIGNAL(playPriorityOnly(bool)));
+ connect(client, SIGNAL(stopAll(int)),
+ this, SIGNAL(stopAll(int)));
+ connect(client, SIGNAL(resume(int,int)),
+ this, SIGNAL(resumeFile(int,int)));
+
+ connect(client, SIGNAL(setSilent(bool)),
+ this, SIGNAL(setSilent(bool)));
+
+ connect(client, SIGNAL(setMute(int,int,bool)),
+ this, SIGNAL(setMute(int,int,bool)));
+ connect(client, SIGNAL(setVolume(int,int,int,int)),
+ this, SIGNAL(setVolume(int,int,int,int)));
+
+ connect(this, SIGNAL(soundFileCompleted(int,int)),
+ client, SLOT(sendSoundCompleted(int,int)));
+ connect(this, SIGNAL(deviceReady(int,int)),
+ client, SLOT(sendDeviceReady(int,int)));
+ connect(this, SIGNAL(deviceError(int,int,int)),
+ client, SLOT(sendDeviceError(int,int,int)));
+ }
+}
+
+#endif
+
+class QWSSoundServerPrivate : public QObject {
+ Q_OBJECT
+
+public:
+ QWSSoundServerPrivate(QObject* parent=0, const char* name=0) :
+ QObject(parent)
+ {
+ timerId = 0;
+ if (name)
+ setObjectName(QString::fromAscii(name));
+#ifndef QT_NO_QWS_SOUNDSERVER
+ server = new QWSSoundServerSocket(this);
+
+ connect(server, SIGNAL(playFile(int,int,QString)),
+ this, SLOT(playFile(int,int,QString)));
+ connect(server, SIGNAL(playFile(int,int,QString,int,int)),
+ this, SLOT(playFile(int,int,QString,int,int)));
+ connect(server, SIGNAL(playRawFile(int,int,QString,int,int,int,int)),
+ this, SLOT(playRawFile(int,int,QString,int,int,int,int)));
+
+ connect(server, SIGNAL(pauseFile(int,int)),
+ this, SLOT(pauseFile(int,int)));
+ connect(server, SIGNAL(stopFile(int,int)),
+ this, SLOT(stopFile(int,int)));
+ connect(server, SIGNAL(stopAll(int)),
+ this, SLOT(stopAll(int)));
+ connect(server, SIGNAL(playPriorityOnly(bool)),
+ this, SLOT(playPriorityOnly(bool)));
+ connect(server, SIGNAL(resumeFile(int,int)),
+ this, SLOT(resumeFile(int,int)));
+
+ connect( server, SIGNAL(setSilent(bool)),
+ this, SLOT(setSilent(bool)));
+
+ connect(server, SIGNAL(setMute(int,int,bool)),
+ this, SLOT(setMute(int,int,bool)));
+ connect(server, SIGNAL(setVolume(int,int,int,int)),
+ this, SLOT(setVolume(int,int,int,int)));
+
+ connect(this, SIGNAL(soundFileCompleted(int,int)),
+ server, SIGNAL(soundFileCompleted(int,int)));
+ connect(this, SIGNAL(deviceReady(int,int)),
+ server, SIGNAL(deviceReady(int,int)));
+ connect(this, SIGNAL(deviceError(int,int,int)),
+ server, SIGNAL(deviceError(int,int,int)));
+
+#endif
+ silent = false;
+ fd = -1;
+ unwritten = 0;
+ can_GETOSPACE = true;
+ }
+
+ ~QWSSoundServerPrivate()
+ {
+ qDeleteAll(active);
+ qDeleteAll(inactive);
+ }
+
+signals:
+ void soundFileCompleted(int, int);
+ void deviceReady(int, int);
+ void deviceError(int, int, int);
+
+public slots:
+ void playRawFile(int wid, int sid, const QString &filename, int freq, int channels, int bitspersample, int flags);
+ void playFile(int wid, int sid, const QString& filename);
+ void playFile(int wid, int sid, const QString& filename, int v, int flags);
+ void checkPresetVolumes(int wid, int sid, QWSSoundServerProvider *p);
+ void pauseFile(int wid, int sid);
+ void resumeFile(int wid, int sid);
+ void stopFile(int wid, int sid);
+ void stopAll(int wid);
+ void setVolume(int wid, int sid, int lv, int rv);
+ void setMute(int wid, int sid, bool m);
+ void playPriorityOnly(bool p);
+ void sendCompletedSignals();
+ void feedDevice(int fd);
+ void setSilent( bool enabled );
+
+protected:
+ void timerEvent(QTimerEvent* event);
+
+private:
+ int openFile(int wid, int sid, const QString& filename);
+ bool openDevice();
+ void closeDevice()
+ {
+ if (fd >= 0) {
+ ::close(fd);
+ fd = -1;
+ }
+ }
+
+ QList<QWSSoundServerProvider*> active;
+ QList<QWSSoundServerProvider*> inactive;
+ struct PresetVolume {
+ int wid;
+ int sid;
+ int left;
+ int right;
+ bool mute;
+ };
+ QList<PresetVolume> volumes;
+ struct CompletedInfo {
+ CompletedInfo( ) : groupId( 0 ), soundId( 0 ) { }
+ CompletedInfo( int _groupId, int _soundId ) : groupId( _groupId ), soundId( _soundId ) { }
+ int groupId;
+ int soundId;
+ };
+ QList<CompletedInfo> completed;
+
+ bool silent;
+
+ int fd;
+ int unwritten;
+ int timerId;
+ char* cursor;
+ short data[sound_buffer_size*2];
+ bool can_GETOSPACE;
+#ifndef QT_NO_QWS_SOUNDSERVER
+ QWSSoundServerSocket *server;
+#endif
+};
+
+void QWSSoundServerPrivate::setSilent( bool enabled )
+{
+ // Close output device
+ closeDevice();
+ if( !unwritten && !active.count() ) {
+ sendCompletedSignals();
+ }
+ // Stop processing audio
+ killTimer( timerId );
+ silent = enabled;
+ // If audio remaining, open output device and continue processing
+ if( unwritten || active.count() ) {
+ openDevice();
+ }
+}
+
+void QWSSoundServerPrivate::timerEvent(QTimerEvent* event)
+{
+ // qDebug("QSS timer event");
+ if( event->timerId() == timerId ) {
+ if (fd >= 0)
+ feedDevice(fd);
+ if (fd < 0) {
+ killTimer(timerId);
+ timerId = 0;
+ }
+ }
+}
+
+void QWSSoundServerPrivate::playRawFile(int wid, int sid, const QString &filename,
+ int freq, int channels, int bitspersample, int flags)
+{
+#ifdef QT_NO_QWS_SOUNDSERVER
+ Q_UNUSED(flags);
+#endif
+ int f = openFile(wid, sid, filename);
+ if ( f ) {
+ QWSSoundServerStream *b = new QWSSoundServerStream(f, channels, freq, bitspersample, wid, sid);
+ // check preset volumes.
+ checkPresetVolumes(wid, sid, b);
+#ifndef QT_NO_QWS_SOUNDSERVER
+ b->setPriority((flags & QWSSoundClient::Priority) == QWSSoundClient::Priority);
+#endif
+ active.append(b);
+ emit deviceReady(wid, sid);
+ }
+}
+
+void QWSSoundServerPrivate::playFile(int wid, int sid, const QString& filename)
+{
+ int f = openFile(wid, sid, filename);
+ if ( f ) {
+ QWSSoundServerProvider *b = new QWSSoundServerBucket(f, wid, sid);
+ checkPresetVolumes(wid, sid, b);
+ active.append( b );
+ emit deviceReady(wid, sid);
+ }
+}
+
+void QWSSoundServerPrivate::playFile(int wid, int sid, const QString& filename,
+ int v, int flags)
+{
+#ifdef QT_NO_QWS_SOUNDSERVER
+ Q_UNUSED(flags);
+#endif
+ int f = openFile(wid, sid, filename);
+ if ( f ) {
+ QWSSoundServerProvider *b = new QWSSoundServerBucket(f, wid, sid);
+ checkPresetVolumes(wid, sid, b);
+ b->setVolume(v, v);
+#ifndef QT_NO_QWS_SOUNDSERVER
+ b->setPriority((flags & QWSSoundClient::Priority) == QWSSoundClient::Priority);
+#endif
+ active.append(b);
+ emit deviceReady(wid, sid);
+ }
+}
+
+void QWSSoundServerPrivate::checkPresetVolumes(int wid, int sid, QWSSoundServerProvider *p)
+{
+ QList<PresetVolume>::Iterator it = volumes.begin();
+ while (it != volumes.end()) {
+ PresetVolume v = *it;
+ if (v.wid == wid && v.sid == sid) {
+ p->setVolume(v.left, v.right);
+ p->setMute(v.mute);
+ it = volumes.erase(it);
+ return;
+ } else {
+ ++it;
+ }
+ }
+}
+
+void QWSSoundServerPrivate::pauseFile(int wid, int sid)
+{
+ QWSSoundServerProvider *bucket;
+ for (int i = 0; i < active.size(); ++i ) {
+ bucket = active.at(i);
+ if (bucket->equal(wid, sid)) {
+ // found bucket....
+ active.removeAt(i);
+ inactive.append(bucket);
+ return;
+ }
+ }
+}
+
+void QWSSoundServerPrivate::resumeFile(int wid, int sid)
+{
+ QWSSoundServerProvider *bucket;
+ for (int i = 0; i < inactive.size(); ++i ) {
+ bucket = inactive.at(i);
+ if (bucket->equal(wid, sid)) {
+ // found bucket....
+ inactive.removeAt(i);
+ active.append(bucket);
+ return;
+ }
+ }
+}
+
+void QWSSoundServerPrivate::stopFile(int wid, int sid)
+{
+ QWSSoundServerProvider *bucket;
+ for (int i = 0; i < active.size(); ++i ) {
+ bucket = active.at(i);
+ if (bucket->equal(wid, sid)) {
+ active.removeAt(i);
+ delete bucket;
+ return;
+ }
+ }
+ for (int i = 0; i < inactive.size(); ++i ) {
+ bucket = inactive.at(i);
+ if (bucket->equal(wid, sid)) {
+ inactive.removeAt(i);
+ delete bucket;
+ return;
+ }
+ }
+}
+
+void QWSSoundServerPrivate::stopAll(int wid)
+{
+ QWSSoundServerProvider *bucket;
+ QList<QWSSoundServerProvider*>::Iterator it = active.begin();
+ while (it != active.end()) {
+ bucket = *it;
+ if (bucket->groupId() == wid) {
+ it = active.erase(it);
+ delete bucket;
+ } else {
+ ++it;
+ }
+ }
+ it = inactive.begin();
+ while (it != inactive.end()) {
+ bucket = *it;
+ if (bucket->groupId() == wid) {
+ it = inactive.erase(it);
+ delete bucket;
+ } else {
+ ++it;
+ }
+ }
+}
+
+void QWSSoundServerPrivate::setVolume(int wid, int sid, int lv, int rv)
+{
+ QWSSoundServerProvider *bucket;
+ for( int i = 0; i < active.size(); ++i ) {
+ bucket = active.at(i);
+ if (bucket->equal(wid, sid)) {
+ bucket->setVolume(lv,rv);
+ return;
+ }
+ }
+ // If gotten here, then it means wid/sid wasn't set up yet.
+ // first find and remove current preset volumes, then add this one.
+ QList<PresetVolume>::Iterator it = volumes.begin();
+ while (it != volumes.end()) {
+ PresetVolume v = *it;
+ if (v.wid == wid && v.sid == sid)
+ it = volumes.erase(it);
+ else
+ ++it;
+ }
+ // and then add this volume
+ PresetVolume nv;
+ nv.wid = wid;
+ nv.sid = sid;
+ nv.left = lv;
+ nv.right = rv;
+ nv.mute = false;
+ volumes.append(nv);
+}
+
+void QWSSoundServerPrivate::setMute(int wid, int sid, bool m)
+{
+ QWSSoundServerProvider *bucket;
+ for( int i = 0; i < active.size(); ++i ) {
+ bucket = active.at(i);
+ if (bucket->equal(wid, sid)) {
+ bucket->setMute(m);
+ return;
+ }
+ }
+ // if gotten here then setting is being applied before item
+ // is created.
+ QList<PresetVolume>::Iterator it = volumes.begin();
+ while (it != volumes.end()) {
+ PresetVolume v = *it;
+ if (v.wid == wid && v.sid == sid) {
+ (*it).mute = m;
+ return;
+ }
+ }
+ if (m) {
+ PresetVolume nv;
+ nv.wid = wid;
+ nv.sid = sid;
+ nv.left = maxVolume>>1;
+ nv.right = maxVolume>>1;
+ nv.mute = true;
+ volumes.append(nv);
+ }
+}
+
+void QWSSoundServerPrivate::playPriorityOnly(bool p)
+{
+ QWSSoundServerProvider::setPlayPriorityOnly(p);
+}
+
+void QWSSoundServerPrivate::sendCompletedSignals()
+{
+ while( !completed.isEmpty() ) {
+ emit soundFileCompleted( (*completed.begin()).groupId,
+ (*completed.begin()).soundId );
+ completed.erase( completed.begin() );
+ }
+}
+
+
+int QWSSoundServerPrivate::openFile(int wid, int sid, const QString& filename)
+{
+ stopFile(wid, sid); // close and re-open.
+ int f = ::open(QFile::encodeName(filename), O_RDONLY|O_NONBLOCK);
+ if (f == -1) {
+ // XXX check ferror, check reason.
+ qDebug("Failed opening \"%s\"",filename.toLatin1().data());
+#ifndef QT_NO_QWS_SOUNDSERVER
+ emit deviceError(wid, sid, (int)QWSSoundClient::ErrOpeningFile );
+#endif
+ } else if ( openDevice() ) {
+ return f;
+ }
+#ifndef QT_NO_QWS_SOUNDSERVER
+ emit deviceError(wid, sid, (int)QWSSoundClient::ErrOpeningAudioDevice );
+#endif
+ return 0;
+}
+
+bool QWSSoundServerPrivate::openDevice()
+{
+ if (fd < 0) {
+ if( silent ) {
+ fd = ::open( "/dev/null", O_WRONLY );
+ // Emulate write to audio device
+ int delay = 1000*(sound_buffer_size>>(sound_stereo+sound_16bit))/sound_speed/2;
+ timerId = startTimer(delay);
+
+ return true;
+ }
+ //
+ // Don't block open right away.
+ //
+ bool openOkay = false;
+ if ((fd = ::open("/dev/dsp", O_WRONLY|O_NONBLOCK)) != -1) {
+ int flags = fcntl(fd, F_GETFL);
+ flags &= ~O_NONBLOCK;
+ openOkay = (fcntl(fd, F_SETFL, flags) == 0);
+ }
+ if (!openOkay) {
+ qDebug("Failed opening audio device");
+ return false;
+ }
+
+ // Setup soundcard at 16 bit mono
+ int v;
+ //v=0x00010000+sound_fragment_size;
+ // um the media player did this instead.
+ v=0x10000 * 4 + sound_fragment_size;
+ if (ioctl(fd, SNDCTL_DSP_SETFRAGMENT, &v))
+ qWarning("Could not set fragments to %08x",v);
+#ifdef QT_QWS_SOUND_16BIT
+ v=AFMT_S16_LE; if (ioctl(fd, SNDCTL_DSP_SETFMT, &v))
+ qWarning("Could not set format %d",v);
+ if (AFMT_S16_LE != v)
+ qDebug("Want format %d got %d", AFMT_S16_LE, v);
+#else
+ v=AFMT_U8; if (ioctl(fd, SNDCTL_DSP_SETFMT, &v))
+ qWarning("Could not set format %d",v);
+ if (AFMT_U8 != v)
+ qDebug("Want format %d got %d", AFMT_U8, v);
+#endif
+ v=sound_stereo; if (ioctl(fd, SNDCTL_DSP_STEREO, &v))
+ qWarning("Could not set stereo %d",v);
+ if (sound_stereo != v)
+ qDebug("Want stereo %d got %d", sound_stereo, v);
+#ifdef QT_QWS_SOUND_STEREO
+ sound_stereo=v;
+#endif
+ v=sound_speed; if (ioctl(fd, SNDCTL_DSP_SPEED, &sound_speed))
+ qWarning("Could not set speed %d",v);
+ if (v != sound_speed)
+ qDebug("Want speed %d got %d", v, sound_speed);
+
+ int delay = 1000*(sound_buffer_size>>(sound_stereo+sound_16bit))
+ /sound_speed/2;
+ // qDebug("QSS delay: %d", delay);
+ timerId = startTimer(delay);
+
+ //
+ // Check system volume
+ //
+ int mixerHandle = ::open( "/dev/mixer", O_RDWR|O_NONBLOCK );
+ if ( mixerHandle >= 0 ) {
+ int volume;
+ ioctl( mixerHandle, MIXER_READ(0), &volume );
+ close( mixerHandle );
+ if ( volume < 1<<(sound_stereo+sound_16bit) )
+ qDebug("Want sound at %d got %d",
+ 1<<(sound_stereo+sound_16bit), volume);
+ } else
+ qDebug( "get volume of audio device failed" );
+
+ }
+ return true;
+}
+
+void QWSSoundServerPrivate::feedDevice(int fd)
+{
+ if ( !unwritten && active.size() == 0 ) {
+ closeDevice();
+ sendCompletedSignals();
+ return;
+ } else {
+ sendCompletedSignals();
+ }
+
+ QWSSoundServerProvider* bucket;
+
+ // find out how much audio is possible
+ int available = sound_buffer_size;
+ QList<QWSSoundServerProvider*> running;
+ for (int i = 0; i < active.size(); ++i) {
+ bucket = active.at(i);
+ int ready = bucket->readySamples(available);
+ if (ready > 0) {
+ available = qMin(available, ready);
+ running.append(bucket);
+ }
+ }
+
+ audio_buf_info info;
+ if (can_GETOSPACE && ioctl(fd,SNDCTL_DSP_GETOSPACE,&info)) {
+ can_GETOSPACE = false;
+ fcntl(fd, F_SETFL, O_NONBLOCK);
+ }
+ if (!can_GETOSPACE)
+ info.fragments = 4; // #### configurable?
+ if (info.fragments > 0) {
+ if (!unwritten) {
+ int left[sound_buffer_size];
+ memset(left,0,available*sizeof(int));
+ int right[sound_buffer_size];
+ if ( sound_stereo )
+ memset(right,0,available*sizeof(int));
+
+ if (running.size() > 0) {
+ // should do volume mod here in regards to each bucket to avoid flattened/bad peaks.
+ for (int i = 0; i < running.size(); ++i ) {
+ bucket = running.at(i);
+ int unused = bucket->add(left,right,available);
+ if (unused > 0) {
+ // this error is quite serious, as
+ // it will really screw up mixing.
+ qDebug("provider lied about samples ready");
+ }
+ }
+ if ( sound_16bit ) {
+ short *d = (short*)data;
+ for (int i=0; i<available; i++) {
+ *d++ = (short)qMax(qMin(left[i],32767),-32768);
+ if ( sound_stereo )
+ *d++ = (short)qMax(qMin(right[i],32767),-32768);
+ }
+ } else {
+ signed char *d = (signed char *)data;
+ for (int i=0; i<available; i++) {
+ *d++ = (signed char)qMax(qMin(left[i]/256,127),-128)+128;
+ if ( sound_stereo )
+ *d++ = (signed char)qMax(qMin(right[i]/256,127),-128)+128;
+ }
+ }
+ unwritten = available*(sound_16bit+1)*(sound_stereo+1);
+ cursor = (char*)data;
+ }
+ }
+ // sound open, but nothing written. Should clear the buffer.
+
+ int w;
+ if (unwritten) {
+ w = ::write(fd,cursor,unwritten);
+
+ if (w < 0) {
+ if (can_GETOSPACE)
+ return;
+ w = 0;
+ }
+
+ cursor += w;
+ unwritten -= w;
+ } else {
+ // write some zeros to clear the buffer?
+ if (!zeroMem)
+ zeroMem = (char *)calloc(sound_buffer_size, sizeof(char));
+ w = ::write(fd, zeroMem, sound_buffer_size);
+ if (w < 0)
+ w = 0;
+ }
+ }
+
+ QList<QWSSoundServerProvider*>::Iterator it = active.begin();
+ while (it != active.end()) {
+ bucket = *it;
+ if (bucket->finished()) {
+ completed.append(CompletedInfo(bucket->groupId(), bucket->soundId()));
+ it = active.erase(it);
+ delete bucket;
+ } else {
+ ++it;
+ }
+ }
+}
+
+
+QWSSoundServer::QWSSoundServer(QObject* parent) :
+ QObject(parent)
+{
+ d = new QWSSoundServerPrivate(this);
+
+ connect( d, SIGNAL(soundFileCompleted(int,int)),
+ this, SLOT(translateSoundCompleted(int,int)) );
+}
+
+void QWSSoundServer::playFile( int sid, const QString& filename )
+{
+ //wid == 0, as it is the server initiating rather than a client
+ // if wid was passable, would accidently collide with server
+ // sockect's wids.
+ d->playFile(0, sid, filename);
+}
+
+void QWSSoundServer::pauseFile( int sid )
+{
+ d->pauseFile(0, sid);
+}
+
+void QWSSoundServer::stopFile( int sid )
+{
+ d->stopFile(0, sid);
+}
+
+void QWSSoundServer::resumeFile( int sid )
+{
+ d->resumeFile(0, sid);
+}
+
+QWSSoundServer::~QWSSoundServer()
+{
+ d->stopAll(0);
+}
+
+void QWSSoundServer::translateSoundCompleted( int, int sid )
+{
+ emit soundCompleted( sid );
+}
+
+#ifndef QT_NO_QWS_SOUNDSERVER
+QWSSoundClient::QWSSoundClient(QObject* parent) :
+ QWSSocket(parent)
+{
+ connectToLocalFile(QString::fromLatin1(SOUND_PIPE).arg(qws_display_id));
+ QObject::connect(this,SIGNAL(readyRead()),
+ this,SLOT(tryReadCommand()));
+ if( state() == QWS_SOCK_BASE::ConnectedState ) QTimer::singleShot(1, this, SIGNAL(connected()));
+ else QTimer::singleShot(1, this, SLOT(emitConnectionRefused()));
+}
+
+QWSSoundClient::~QWSSoundClient( )
+{
+ flush();
+}
+
+void QWSSoundClient::reconnect()
+{
+ connectToLocalFile(QString::fromLatin1(SOUND_PIPE).arg(qws_display_id));
+ if( state() == QWS_SOCK_BASE::ConnectedState ) emit connected();
+ else emit error( QTcpSocket::ConnectionRefusedError );
+}
+
+void QWSSoundClient::sendServerMessage(QString msg)
+{
+#ifndef QT_NO_TEXTCODEC
+ QByteArray u = msg.toUtf8();
+#else
+ QByteArray u = msg.toLatin1();
+#endif
+ write(u.data(), u.length());
+ flush();
+}
+
+void QWSSoundClient::play( int id, const QString& filename )
+{
+ QFileInfo fi(filename);
+ sendServerMessage(QLatin1String("PLAY ")
+ + QString::number(id) + QLatin1Char(' ')
+ + fi.absoluteFilePath() + QLatin1Char('\n'));
+}
+
+void QWSSoundClient::play( int id, const QString& filename, int volume, int flags)
+{
+ QFileInfo fi(filename);
+ sendServerMessage(QLatin1String("PLAYEXTEND ")
+ + QString::number(id) + QLatin1Char(' ')
+ + QString::number(volume) + QLatin1Char(' ')
+ + QString::number(flags) + QLatin1Char(' ')
+ + fi.absoluteFilePath() + QLatin1Char('\n'));
+}
+
+void QWSSoundClient::pause( int id )
+{
+ sendServerMessage(QLatin1String("PAUSE ")
+ + QString::number(id) + QLatin1Char('\n'));
+}
+
+void QWSSoundClient::stop( int id )
+{
+ sendServerMessage(QLatin1String("STOP ")
+ + QString::number(id) + QLatin1Char('\n'));
+}
+
+void QWSSoundClient::resume( int id )
+{
+ sendServerMessage(QLatin1String("RESUME ")
+ + QString::number(id) + QLatin1Char('\n'));
+}
+
+void QWSSoundClient::playRaw( int id, const QString& filename,
+ int freq, int chs, int bitspersample, int flags)
+{
+ QFileInfo fi(filename);
+ sendServerMessage(QLatin1String("PLAYRAW ")
+ + QString::number(id) + QLatin1Char(' ')
+ + QString::number(chs) + QLatin1Char(' ')
+ + QString::number(freq) + QLatin1Char(' ')
+ + QString::number(bitspersample) + QLatin1Char(' ')
+ + QString::number(flags) + QLatin1Char(' ')
+ + fi.absoluteFilePath() + QLatin1Char('\n'));
+}
+
+void QWSSoundClient::setMute( int id, bool m )
+{
+ sendServerMessage(QLatin1String(m ? "MUTE " : "UNMUTE ")
+ + QString::number(id) + QLatin1Char('\n'));
+}
+
+void QWSSoundClient::setVolume( int id, int leftVol, int rightVol )
+{
+ sendServerMessage(QLatin1String("SETVOLUME ")
+ + QString::number(id) + QLatin1Char(' ')
+ + QString::number(leftVol) + QLatin1Char(' ')
+ + QString::number(rightVol) + QLatin1Char('\n'));
+}
+
+void QWSSoundClient::playPriorityOnly( bool pri )
+{
+ sendServerMessage(QLatin1String("PRIORITYONLY ")
+ + QString::number(pri ? 1 : 0) + QLatin1Char('\n'));
+}
+
+void QWSSoundClient::setSilent( bool enable )
+{
+ sendServerMessage(QLatin1String("SILENT ")
+ + QString::number( enable ? 1 : 0 ) + QLatin1Char('\n'));
+}
+
+void QWSSoundClient::tryReadCommand()
+{
+ while ( canReadLine() ) {
+ QString l = QString::fromAscii(readLine());
+ l.truncate(l.length()-1); // chomp
+ QStringList token = l.split(QLatin1Char(' '));
+ if (token[0] == QLatin1String("SOUNDCOMPLETED")) {
+ emit soundCompleted(token[1].toInt());
+ } else if (token[0] == QLatin1String("DEVICEREADY")) {
+ emit deviceReady(token[1].toInt());
+ } else if (token[0] == QLatin1String("DEVICEERROR")) {
+ emit deviceError(token[1].toInt(),(DeviceErrors)token[2].toInt());
+ }
+ }
+}
+
+void QWSSoundClient::emitConnectionRefused()
+{
+ emit error( QTcpSocket::ConnectionRefusedError );
+}
+#endif
+
+QT_END_NAMESPACE
+
+#include "qsoundqss_qws.moc"
+
+#endif // QT_NO_SOUND
diff --git a/src/gui/embedded/qsoundqss_qws.h b/src/gui/embedded/qsoundqss_qws.h
new file mode 100644
index 0000000000..072a694f50
--- /dev/null
+++ b/src/gui/embedded/qsoundqss_qws.h
@@ -0,0 +1,177 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSOUNDQSS_QWS_H
+#define QSOUNDQSS_QWS_H
+
+#include <QtCore/qglobal.h>
+
+#ifndef QT_NO_SOUND
+
+#include <QtNetwork/qtcpserver.h>
+#include <QtNetwork/qtcpsocket.h>
+#include <QtGui/qwssocket_qws.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+#if defined(QT_NO_NETWORK) || defined(QT_NO_DNS)
+#define QT_NO_QWS_SOUNDSERVER
+#endif
+
+#ifndef Q_OS_MAC
+
+class QWSSoundServerPrivate;
+
+class Q_GUI_EXPORT QWSSoundServer : public QObject {
+ Q_OBJECT
+public:
+ explicit QWSSoundServer(QObject *parent=0);
+ ~QWSSoundServer();
+ void playFile( int id, const QString& filename );
+ void stopFile( int id );
+ void pauseFile( int id );
+ void resumeFile( int id );
+
+Q_SIGNALS:
+ void soundCompleted( int );
+
+private Q_SLOTS:
+ void translateSoundCompleted( int, int );
+
+private:
+ QWSSoundServerPrivate* d;
+};
+
+#ifndef QT_NO_QWS_SOUNDSERVER
+class Q_GUI_EXPORT QWSSoundClient : public QWSSocket {
+ Q_OBJECT
+public:
+
+ enum SoundFlags {
+ Priority = 0x01,
+ Streaming = 0x02 // currently ignored, but but could set up so both Raw and non raw can be done streaming or not.
+ };
+ enum DeviceErrors {
+ ErrOpeningAudioDevice = 0x01,
+ ErrOpeningFile = 0x02,
+ ErrReadingFile = 0x04
+ };
+ explicit QWSSoundClient(QObject* parent=0);
+ ~QWSSoundClient( );
+ void reconnect();
+ void play( int id, const QString& filename );
+ void play( int id, const QString& filename, int volume, int flags = 0 );
+ void playRaw( int id, const QString&, int, int, int, int flags = 0 );
+
+ void pause( int id );
+ void stop( int id );
+ void resume( int id );
+ void setVolume( int id, int left, int right );
+ void setMute( int id, bool m );
+
+ // to be used by server only, to protect phone conversation/rings.
+ void playPriorityOnly(bool);
+
+ // If silent, tell sound server to release audio device
+ // Otherwise, allow sound server to regain audio device
+ void setSilent(bool);
+
+Q_SIGNALS:
+ void soundCompleted(int);
+ void deviceReady(int id);
+ void deviceError(int id, QWSSoundClient::DeviceErrors);
+
+private Q_SLOTS:
+ void tryReadCommand();
+ void emitConnectionRefused();
+
+private:
+ void sendServerMessage(QString msg);
+};
+
+class QWSSoundServerSocket : public QWSServerSocket {
+ Q_OBJECT
+
+public:
+ explicit QWSSoundServerSocket(QObject *parent=0);
+public Q_SLOTS:
+ void newConnection();
+
+#ifdef QT3_SUPPORT
+public:
+ QT3_SUPPORT_CONSTRUCTOR QWSSoundServerSocket(QObject *parent, const char *name);
+#endif
+
+Q_SIGNALS:
+ void playFile(int, int, const QString&);
+ void playFile(int, int, const QString&, int, int);
+ void playRawFile(int, int, const QString&, int, int, int, int);
+ void pauseFile(int, int);
+ void stopFile(int, int);
+ void resumeFile(int, int);
+ void setVolume(int, int, int, int);
+ void setMute(int, int, bool);
+
+ void stopAll(int);
+
+ void playPriorityOnly(bool);
+
+ void setSilent(bool);
+
+ void soundFileCompleted(int, int);
+ void deviceReady(int, int);
+ void deviceError(int, int, int);
+};
+#endif
+
+#endif // Q_OS_MAC
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QT_NO_SOUND
+
+#endif // QSOUNDQSS_QWS_H
diff --git a/src/gui/embedded/qtransportauth_qws.cpp b/src/gui/embedded/qtransportauth_qws.cpp
new file mode 100644
index 0000000000..97ba5b8a54
--- /dev/null
+++ b/src/gui/embedded/qtransportauth_qws.cpp
@@ -0,0 +1,1562 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qtransportauth_qws.h"
+#include "qtransportauth_qws_p.h"
+
+#ifndef QT_NO_SXE
+
+#include "../../3rdparty/md5/md5.h"
+#include "../../3rdparty/md5/md5.cpp"
+#include "qwsutils_qws.h"
+#include "qwssocket_qws.h"
+#include "qwscommand_qws_p.h"
+#include "qwindowsystem_qws.h"
+#include "qbuffer.h"
+#include "qthread.h"
+#include "qabstractsocket.h"
+#include "qlibraryinfo.h"
+#include "qfile.h"
+#include "qdebug.h"
+
+#include <syslog.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/file.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <time.h>
+
+#include <QtCore/qcache.h>
+
+#define BUF_SIZE 512
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \internal
+ memset for security purposes, guaranteed not to be optimized away
+ http://www.faqs.org/docs/Linux-HOWTO/Secure-Programs-HOWTO.html
+*/
+Q_GUI_EXPORT void *guaranteed_memset(void *v,int c,size_t n)
+{
+ volatile char *p = (char *)v; while (n--) *p++=c; return v;
+}
+
+/*!
+ \class QTransportAuth
+ \internal
+
+ \brief Authenticate a message transport.
+
+ For performance reasons, message authentication is tied to an individual
+ message transport instance. For example in connection oriented transports
+ the authentication cookie can be cached against the connection avoiding
+ the overhead of authentication on every message.
+
+ For each process there is one instance of the QTransportAuth object.
+ For server processes it can determine the \link secure-exe-environ.html SXE
+ Program Identity \endlink and provide access to policy data to determine if
+ the message should be forwarded for action. If not actioned, the message
+ may be treated as being from a flawed or malicious process.
+
+ Retrieve the instance with the getInstance() method. The constructor is
+ disabled and instances of QTransportAuth should never be constructed by
+ calling classes.
+
+ To make the Authentication easier to use a proxied QIODevice is provided
+ which uses an internal QBuffer.
+
+ In the server code first get a pointer to a QTransportAuth::Data object
+ using the connectTransport() method:
+
+ \snippet doc/src/snippets/code/src_gui_embedded_qtransportauth_qws.cpp 0
+
+ Here it is asserted that the transport is trusted. See the assumptions
+ listed in the \link secure-exe-environ.html SXE documentation \endlink
+
+ Then proxy in the authentication device:
+
+ \snippet doc/src/snippets/code/src_gui_embedded_qtransportauth_qws.cpp 1
+
+ In the client code it is similar. Use the connectTransport() method
+ just the same then proxy in the authentication device instead of the
+ socket in write calls:
+
+ \snippet doc/src/snippets/code/src_gui_embedded_qtransportauth_qws.cpp 2
+*/
+
+static int hmac_md5(
+ unsigned char* text, /* pointer to data stream */
+ int text_length, /* length of data stream */
+ const unsigned char* key, /* pointer to authentication key */
+ int key_length, /* length of authentication key */
+ unsigned char * digest /* caller digest to be filled in */
+ );
+
+
+
+#define KEY_CACHE_SIZE 30
+
+const char * const errorStrings[] = {
+ "pending identity verification",
+ "message too small to carry auth data",
+ "cache miss on connection oriented transport",
+ "no magic bytes on message",
+ "key not found for prog id",
+ "authorization key match failed",
+ "key out of date"
+};
+
+const char *QTransportAuth::errorString( const Data &d )
+{
+ if (( d.status & ErrMask ) == Success )
+ return "success";
+ int e = d.status & ErrMask;
+ if ( e > OutOfDate )
+ return "unknown";
+ return errorStrings[e];
+}
+
+SxeRegistryLocker::SxeRegistryLocker( QObject *reg )
+ : m_success( false )
+ , m_reg( 0 )
+{
+ if ( reg )
+ if ( !QMetaObject::invokeMethod( reg, "lockManifest", Q_RETURN_ARG(bool, m_success) ))
+ m_success = false;
+ m_reg = reg;
+}
+
+SxeRegistryLocker::~SxeRegistryLocker()
+{
+ if ( m_success )
+ QMetaObject::invokeMethod( m_reg, "unlockManifest" );
+}
+
+
+QTransportAuthPrivate::QTransportAuthPrivate()
+ : keyInitialised(false)
+ , m_packageRegistry( 0 )
+{
+}
+
+QTransportAuthPrivate::~QTransportAuthPrivate()
+{
+}
+
+/*!
+ \internal
+ Construct a new QTransportAuth
+*/
+QTransportAuth::QTransportAuth() : QObject(*new QTransportAuthPrivate)
+{
+ // qDebug( "creating transport auth" );
+}
+
+/*!
+ \internal
+ Destructor
+*/
+QTransportAuth::~QTransportAuth()
+{
+ // qDebug( "deleting transport auth" );
+}
+
+/*!
+ Set the process key for this currently running Qt Extended process to
+ the \a authdata. \a authdata should be sizeof(struct AuthCookie)
+ in length and contain the key and program id. Use this method
+ when setting or changing the SXE identity of the current program.
+*/
+void QTransportAuth::setProcessKey( const char *authdata )
+{
+ Q_D(QTransportAuth);
+ ::memcpy(&d->authKey, authdata, sizeof(struct AuthCookie));
+ QFile proc_key( QLatin1String("/proc/self/lids_key") );
+ // where proc key exists use that instead
+ if ( proc_key.open( QIODevice::ReadOnly ))
+ {
+ qint64 kb = proc_key.read( (char*)&d->authKey.key, QSXE_KEY_LEN );
+#ifdef QTRANSPORTAUTH_DEBUG
+ qDebug( "Using %li bytes of /proc/%i/lids_key\n", (long int)kb, getpid() );
+#else
+ Q_UNUSED( kb );
+#endif
+ }
+ d->keyInitialised = true;
+}
+
+
+/*!
+ Apply \a key as the process key for the currently running application.
+
+ \a prog is current ignored
+
+ Deprecated function
+*/
+void QTransportAuth::setProcessKey( const char *key, const char *prog )
+{
+ Q_UNUSED(prog);
+ setProcessKey( key );
+#ifdef QTRANSPORTAUTH_DEBUG
+ char displaybuf[QSXE_KEY_LEN*2+1];
+ hexstring( displaybuf, (const unsigned char *)key, QSXE_KEY_LEN );
+ qDebug() << "key" << displaybuf << "set";
+#endif
+}
+
+/*!
+ Register \a pr as a policy handler object. The object pointed to
+ by \a pr should have a slot as follows
+ \snippet doc/src/snippets/code/src_gui_embedded_qtransportauth_qws.cpp 3
+ All requests received by this server will then generate a call to
+ this slot, and may be processed for policy compliance.
+*/
+void QTransportAuth::registerPolicyReceiver( QObject *pr )
+{
+ // not every policy receiver needs setup - no error if this fails
+ QMetaObject::invokeMethod( pr, "setupPolicyCheck" );
+
+ connect( this, SIGNAL(policyCheck(QTransportAuth::Data&,QString)),
+ pr, SLOT(policyCheck(QTransportAuth::Data&,QString)), Qt::DirectConnection );
+}
+
+/*!
+ Unregister the \a pr from being a policy handler. No more policyCheck signals
+ are received by this object.
+*/
+void QTransportAuth::unregisterPolicyReceiver( QObject *pr )
+{
+ disconnect( pr );
+ // not every policy receiver needs tear down - no error if this fails
+ QMetaObject::invokeMethod( pr, "teardownPolicyCheck" );
+}
+
+/*!
+ Record a new transport connection with \a properties and \a descriptor.
+
+ The calling code is responsible for destroying the returned data when the
+ tranport connection is closed.
+*/
+QTransportAuth::Data *QTransportAuth::connectTransport( unsigned char properties, int descriptor )
+{
+ Data *data = new Data(properties, descriptor);
+ data->status = Pending;
+ return data;
+}
+
+/*!
+ Is the transport trusted. This is true iff data written into the
+ transport medium cannot be intercepted or modified by another process.
+ This is for example true for Unix Domain Sockets, but not for shared
+ memory or UDP sockets.
+
+ There is of course an underlying assumption that the kernel implementing
+ the transport is sound, ie it cannot be compromised by writing to
+ /dev/kmem or loading untrusted modules
+*/
+inline bool QTransportAuth::Data::trusted() const
+{
+ return (bool)(properties & Trusted);
+}
+
+/*!
+ Assert that the transport is trusted.
+
+ For example with respect to shared memory, if it is ensured that no untrusted
+ root processes are running, and that unix permissions have been set such that
+ any untrusted non-root processes do not have access rights, then a shared
+ memory transport could be asserted to be trusted.
+
+ \sa trusted()
+*/
+inline void QTransportAuth::Data::setTrusted( bool t )
+{
+ properties = t ? properties | Trusted : properties & ~Trusted;
+}
+
+/*!
+ Is the transport connection oriented. This is true iff once a connection
+ has been accepted, and state established, then further messages over the
+ transport are guaranteed to have come from the original connecting entity.
+ This is for example true for Unix Domain Sockets, but not
+ for shared memory or UDP sockets.
+
+ By extension if the transport is not trusted() then it should not be
+ assumed to be connection oriented, since spoofed connection information
+ could be created. For example if we assume the TCP/IP transport is
+ trusted, it can be treated as connection oriented; but this is only the
+ case if intervening routers are trusted.
+
+ Connection oriented transports have authorization cached against the
+ connection, and thus authorization is only done at connect time.
+*/
+inline bool QTransportAuth::Data::connection() const
+{
+ return (bool)(properties & Connection);
+}
+
+/*!
+ Assert that the transport is connection oriented.
+
+ \sa connection()
+*/
+inline void QTransportAuth::Data::setConnection( bool t )
+{
+ properties = t ? properties | Connection : properties & ~Connection;
+}
+
+/*!
+ Return a pointer to the instance of this process's QTransportAuth object
+*/
+QTransportAuth *QTransportAuth::getInstance()
+{
+ static QTransportAuth theInstance;
+
+ return &theInstance;
+}
+
+/*!
+ Set the full path to the key file
+
+ Since this is normally relative to Qtopia::qpeDir() this needs to be
+ set within the Qt Extended framework.
+
+ The keyfile should be protected by file permissions or by MAC rules
+ such that it can only be read/written by the "qpe" server process
+*/
+void QTransportAuth::setKeyFilePath( const QString &path )
+{
+ Q_D(QTransportAuth);
+ d->m_keyFilePath = path;
+}
+
+QString QTransportAuth::keyFilePath() const
+{
+ Q_D(const QTransportAuth);
+ return d->m_keyFilePath;
+}
+
+void QTransportAuth::setLogFilePath( const QString &path )
+{
+ Q_D(QTransportAuth);
+ d->m_logFilePath = path;
+}
+
+QString QTransportAuth::logFilePath() const
+{
+ Q_D(const QTransportAuth);
+ return d->m_logFilePath;
+}
+
+void QTransportAuth::setPackageRegistry( QObject *registry )
+{
+ Q_D(QTransportAuth);
+ d->m_packageRegistry = registry;
+}
+
+bool QTransportAuth::isDiscoveryMode() const
+{
+#if defined(SXE_DISCOVERY)
+ static bool checked = false;
+ static bool yesItIs = false;
+
+ if ( checked ) return yesItIs;
+
+ yesItIs = ( getenv( "SXE_DISCOVERY_MODE" ) != 0 );
+ if ( yesItIs )
+ {
+ qWarning("SXE Discovery mode on, ALLOWING ALL requests and logging to %s",
+ qPrintable(logFilePath()));
+ QFile::remove( logFilePath() );
+ }
+ checked = true;
+ return yesItIs;
+#else
+ return false;
+#endif
+}
+
+/*!
+ \internal
+ Return the authorizer device mapped to this client. Note that this
+ could probably all be void* instead of QWSClient* for generality.
+ Until the need for that rears its head its QWSClient* to save the casts.
+
+ #### OK the need has arrived, but the public API is frozen.
+*/
+QIODevice *QTransportAuth::passThroughByClient( QWSClient *client ) const
+{
+ Q_D(const QTransportAuth);
+
+ if ( client == 0 ) return 0;
+ if ( d->buffersByClient.contains( client ))
+ {
+ return d->buffersByClient[client];
+ }
+ // qWarning( "buffer not found for client %p", client );
+ return 0;
+}
+
+/*!
+ \internal
+ Return a QIODevice pointer (to an internal QBuffer) which can be used
+ to receive data after authorisation on transport \a d.
+
+ The return QIODevice will act as a pass-through.
+
+ The data will be consumed from \a iod and forwarded on to the returned
+ QIODevice which can be connected to readyRead() signal handlers in
+ place of the original QIODevice \a iod.
+
+ This will be called in the server process to handle incoming
+ authenticated requests.
+
+ The returned QIODevice will take ownership of \a data which will be deleted
+ when the QIODevice is delected.
+
+ \sa setTargetDevice()
+*/
+QAuthDevice *QTransportAuth::recvBuf( QTransportAuth::Data *data, QIODevice *iod )
+{
+ return new QAuthDevice( iod, data, QAuthDevice::Receive );
+}
+
+/*!
+ Return a QIODevice pointer (to an internal QBuffer) which can be used
+ to write data onto, for authorisation on transport \a d.
+
+ The return QIODevice will act as a pass-through.
+
+ The data written to the return QIODevice will be forwarded on to the
+ returned QIODevice. In the case of a QTcpSocket, this will cause it
+ to send out the data with the authentication information on it.
+
+ This will be called in the client process to generate outgoing
+ authenticated requests.
+
+ The returned QIODevice will take ownership of \a data which will be deleted
+ when the QIODevice is delected.
+
+ \sa setTargetDevice()
+*/
+QAuthDevice *QTransportAuth::authBuf( QTransportAuth::Data *data, QIODevice *iod )
+{
+ return new QAuthDevice( iod, data, QAuthDevice::Send );
+}
+
+const unsigned char *QTransportAuth::getClientKey( unsigned char progId )
+{
+ Q_D(QTransportAuth);
+ return d->getClientKey( progId );
+}
+
+void QTransportAuth::invalidateClientKeyCache()
+{
+ Q_D(QTransportAuth);
+ d->invalidateClientKeyCache();
+}
+
+QMutex *QTransportAuth::getKeyFileMutex()
+{
+ Q_D(QTransportAuth);
+ return &d->keyfileMutex;
+}
+
+/*
+ \internal
+ Respond to the destroyed(QObject*) signal of the QAuthDevice's
+ client object and remove it from the buffersByClient lookup hash.
+*/
+void QTransportAuth::bufferDestroyed( QObject *cli )
+{
+ Q_D(QTransportAuth);
+ if ( cli == NULL ) return;
+
+ if ( d->buffersByClient.contains( cli ))
+ {
+ d->buffersByClient.remove( cli );
+ // qDebug( "@@@@@@@ client %p removed @@@@@@@@@", cli );
+ }
+ // qDebug( " client count %d", d->buffersByClient.count() );
+}
+
+bool QTransportAuth::authorizeRequest( QTransportAuth::Data &d, const QString &request )
+{
+ bool isAuthorized = true;
+
+ if ( !request.isEmpty() && request != QLatin1String("Unknown") )
+ {
+ d.status &= QTransportAuth::ErrMask; // clear the status
+ emit policyCheck( d, request );
+ isAuthorized = (( d.status & QTransportAuth::StatusMask ) == QTransportAuth::Allow );
+ }
+#if defined(SXE_DISCOVERY)
+ if (isDiscoveryMode()) {
+#ifndef QT_NO_TEXTSTREAM
+ if (!logFilePath().isEmpty()) {
+ QFile log( logFilePath() );
+ if (!log.open(QIODevice::WriteOnly | QIODevice::Append)) {
+ qWarning("Could not write to log in discovery mode: %s",
+ qPrintable(logFilePath()));
+ } else {
+ QTextStream ts( &log );
+ ts << d.progId << '\t' << ( isAuthorized ? "Allow" : "Deny" ) << '\t' << request << endl;
+ }
+ }
+#endif
+ isAuthorized = true;
+ }
+#endif
+ if ( !isAuthorized )
+ {
+ qWarning( "%s - denied: for Program Id %u [PID %d]"
+ , qPrintable(request), d.progId, d.processId );
+
+ char linkTarget[BUF_SIZE]="";
+ char exeLink[BUF_SIZE]="";
+ char cmdlinePath[BUF_SIZE]="";
+ char cmdline[BUF_SIZE]="";
+
+ //get executable from /proc/pid/exe
+ snprintf( exeLink, BUF_SIZE, "/proc/%d/exe", d.processId );
+ if ( -1 == ::readlink( exeLink, linkTarget, BUF_SIZE - 1 ) )
+ {
+ qWarning( "SXE:- Error encountered in retrieving executable link target from /proc/%u/exe : %s",
+ d.processId, strerror(errno) );
+ snprintf( linkTarget, BUF_SIZE, "%s", linkTarget );
+ }
+
+ //get cmdline from proc/pid/cmdline
+ snprintf( cmdlinePath, BUF_SIZE, "/proc/%d/cmdline", d.processId );
+ int cmdlineFd = open( cmdlinePath, O_RDONLY );
+ if ( cmdlineFd == -1 )
+ {
+ qWarning( "SXE:- Error encountered in opening /proc/%u/cmdline: %s",
+ d.processId, strerror(errno) );
+ snprintf( cmdline, BUF_SIZE, "%s", "Unknown" );
+ }
+ else
+ {
+ if ( -1 == ::read(cmdlineFd, cmdline, BUF_SIZE - 1 ) )
+ {
+ qWarning( "SXE:- Error encountered in reading /proc/%u/cmdline : %s",
+ d.processId, strerror(errno) );
+ snprintf( cmdline, BUF_SIZE, "%s", "Unknown" );
+ }
+ close( cmdlineFd );
+ }
+
+ syslog( LOG_ERR | LOG_LOCAL6, "%s // PID:%u // ProgId:%u // Exe:%s // Request:%s // Cmdline:%s",
+ "<SXE Breach>", d.processId, d.progId, linkTarget, qPrintable(request), cmdline);
+ }
+
+ return isAuthorized;
+}
+
+inline bool __fileOpen( QFile *f )
+{
+#ifdef QTRANSPORTAUTH_DEBUG
+ if ( f->open( QIODevice::ReadOnly ))
+ {
+ qDebug( "Opened file: %s\n", qPrintable( f->fileName() ));
+ return true;
+ }
+ else
+ {
+ qWarning( "Could not open file: %s\n", qPrintable( f->fileName() ));
+ return false;
+ }
+#else
+ return ( f->open( QIODevice::ReadOnly ));
+#endif
+}
+
+/*!
+ \internal
+ Find client keys for the \a progId. If it is cached should be very
+ fast, otherwise requires a read of the secret key file
+
+ In the success case a pointer to the keys is returned. The pointer is
+ to storage allocated for the internal cache and must be used asap.
+
+ The list returned is a sequence of one or more keys which match the
+ progId. There is no separator, each 16 byte sequence represents a key.
+ The sequence is followed by two iterations of the SXE magic
+ bytes,eg 0xBA, 0xD4, 0xD4, 0xBA, 0xBA, 0xD4, 0xD4, 0xBA
+
+ NULL is returned in the following cases:
+ \list
+ \o the keyfiles could not be accessed - error condition
+ \o there was no key for the supplied program id - key auth failed
+ \endlist
+
+ Note that for the keyfiles, there is multi-thread and multi-process
+ concurrency issues: they can be read by the qpe process when
+ QTransportAuth calls getClientKey to verify a request, and they can be
+ read or written by the packagemanager when updating package data.
+
+ To protect against this, the keyfileMutex & SxeRegistryLocker is used.
+
+ The sxe_installer tool can also update inode and device numbers in
+ the manifest file, but this only occurs outside of normal operation,
+ so qpe and packagemanager are never running when this occurs.
+*/
+const unsigned char *QTransportAuthPrivate::getClientKey(unsigned char progId)
+{
+ int manifestMatchCount = 0;
+ struct IdBlock mr;
+ int total_size = 0;
+ char *result = 0;
+ char *result_ptr;
+ int keysFound = 0;
+ bool foundKey;
+ int keysRead = 0;
+ struct usr_key_entry keys_list[128];
+
+ if ( keyCache.contains( progId ))
+ return (const unsigned char *)keyCache[progId];
+
+ SxeRegistryLocker rlock( m_packageRegistry );
+
+ // ### Qt 4.3: this is hacky - see documentation for setKeyFilePath
+ QString manifestPath = m_keyFilePath + QLatin1String("/manifest");
+ QString actualKeyPath = QLatin1String("/proc/lids/keys");
+ bool noFailOnKeyMissing = true;
+ if ( !QFile::exists( actualKeyPath )) {
+ actualKeyPath = m_keyFilePath + QLatin1String( "/" QSXE_KEYFILE );
+ }
+ QFile kf( actualKeyPath );
+ QFile mn( manifestPath );
+ if ( !__fileOpen( &mn ))
+ goto key_not_found;
+ // first find how much storage is needed
+ while ( mn.read( (char*)&mr, sizeof(struct IdBlock)) > 0 )
+ if ( mr.progId == progId )
+ manifestMatchCount++;
+ if ( manifestMatchCount == 0 )
+ goto key_not_found;
+ if ( !__fileOpen( &kf ))
+ {
+ noFailOnKeyMissing = false;
+ goto key_not_found;
+ }
+ total_size = 2 * QSXE_MAGIC_BYTES + manifestMatchCount * QSXE_KEY_LEN;
+ result = (char*)malloc( total_size );
+ Q_CHECK_PTR( result );
+ mn.seek( 0 );
+ result_ptr = result;
+ /* reading whole key array in is much more efficient, 99% case is this loop only
+ executes once, should not have more than 128 keyed items */
+ while (( keysRead = kf.read( (char*)keys_list, sizeof(struct usr_key_entry)*128 )) > 0 )
+ {
+ /* qDebug("PID %d: getClientKey() - read %d bytes = %d keys from %s", getpid(), keysRead,
+ keysRead/sizeof(struct usr_key_entry), qPrintable(actualKeyPath)); */
+ keysRead /= sizeof(struct usr_key_entry);
+ while ( mn.read( (char*)&mr, sizeof(struct IdBlock)) > 0 )
+ {
+ if ( mr.progId == progId )
+ {
+ foundKey = false;
+ for ( int i = 0; i < keysRead; ++i )
+ {
+ /* if ( i == 0 )
+ qDebug() << " pid" << getpid() << "looking for device" << (dev_t)mr.device << "inode" << (ino_t)mr.inode;
+ qDebug() << " pid" << getpid() << "trying device" << keys_list[i].dev << "inode" << keys_list[i].ino; */
+ if ( keys_list[i].ino == (ino_t)mr.inode && keys_list[i].dev == (dev_t)mr.device )
+ {
+ memcpy( result_ptr, keys_list[i].key, QSXE_KEY_LEN );
+ result_ptr += QSXE_KEY_LEN;
+ foundKey = true;
+ break;
+ }
+ }
+ if ( foundKey )
+ {
+ keysFound++;
+ if ( keysFound == manifestMatchCount )
+ break;
+ }
+ }
+ }
+ }
+ if ( result_ptr == result ) // nothing found!
+ goto key_not_found;
+ // 2 x magic bytes sentinel at end of sequence
+ for ( int i = 0; i < 2; ++i )
+ for ( int j = 0; j < QSXE_MAGIC_BYTES; ++j )
+ *result_ptr++ = magic[j];
+ keyCache.insert( progId, result, total_size / 10 );
+ /* qDebug( "PID %d : Found %d client keys for prog %u", getpid(), keysFound, progId ); */
+ goto success_out;
+
+key_not_found:
+ if ( noFailOnKeyMissing ) // return an "empty" set of keys in this case
+ {
+ if ( result == 0 )
+ {
+ result = (char*)malloc( 2 * QSXE_MAGIC_BYTES );
+ Q_CHECK_PTR( result );
+ }
+ result_ptr = result;
+ for ( int i = 0; i < 2; ++i )
+ for ( int j = 0; j < QSXE_MAGIC_BYTES; ++j )
+ *result_ptr++ = magic[j];
+ return (unsigned char *)result;
+ }
+ qWarning( "PID %d : Not found client key for prog %u", getpid(), progId );
+ if ( result )
+ {
+ free( result );
+ result = 0;
+ }
+success_out:
+ if ( mn.isOpen() )
+ mn.close();
+ if ( kf.isOpen() )
+ kf.close();
+ return (unsigned char *)result;
+}
+
+void QTransportAuthPrivate::invalidateClientKeyCache()
+{
+ keyfileMutex.lock();
+ keyCache.clear();
+ keyfileMutex.unlock();
+}
+
+////////////////////////////////////////////////////////////////////////
+////
+//// RequestAnalyzer definition
+////
+
+
+RequestAnalyzer::RequestAnalyzer()
+ : moreData( false )
+ , dataSize( 0 )
+{
+}
+
+RequestAnalyzer::~RequestAnalyzer()
+{
+}
+
+/*!
+ Analzye the data in the\a msgQueue according to some protocol
+ and produce a request string for policy analysis.
+
+ If enough data is in the queue for analysis of a complete message,
+ return a non-null string, and set a flag so requireMoreData() will
+ return false; otherwise return a null string and requireMoreData()
+ return true.
+
+ The amount of bytes analyzed is then available via bytesAnalyzed().
+
+ A null string is also returned in the case where the message was
+ corrupt and could not be analyzed. In this case requireMoreData()
+ returns false.
+
+Note: this method will modify the msgQueue and pull off the data
+ deemed to be corrupt, in the case of corrupt data.
+
+ In all other cases the msgQueue is left alone. The calling code
+ should then pull off the analyzed data. Use bytesAnalzyed() to
+ find how much data to pull off the queue.
+*/
+QString RequestAnalyzer::analyze( QByteArray *msgQueue )
+{
+#ifdef Q_WS_QWS
+ dataSize = 0;
+ moreData = false;
+ QBuffer cmdBuf( msgQueue );
+ cmdBuf.open( QIODevice::ReadOnly | QIODevice::Unbuffered );
+ QWSCommand::Type command_type = (QWSCommand::Type)(qws_read_uint( &cmdBuf ));
+ QWSCommand *command = QWSCommand::factory(command_type);
+ // if NULL, factory will have already printed warning for bogus
+ // command_type just purge the bad stuff and attempt to recover
+ if ( command == NULL )
+ {
+ *msgQueue = msgQueue->mid( sizeof(int) );
+ return QString();
+ }
+ QString request = QLatin1String(qws_getCommandTypeString(command_type));
+#ifndef QT_NO_COP
+ if ( !command->read( &cmdBuf ))
+ {
+ // not all command arrived yet - come back later
+ delete command;
+ moreData = true;
+ return QString();
+ }
+ if ( command_type == QWSCommand::QCopSend )
+ {
+ QWSQCopSendCommand *sendCommand = static_cast<QWSQCopSendCommand*>(command);
+ request += QString( QLatin1String("/QCop/%1/%2") ).arg( sendCommand->channel ).arg( sendCommand->message );
+ }
+ if ( command_type == QWSCommand::QCopRegisterChannel )
+ {
+ QWSQCopRegisterChannelCommand *registerCommand = static_cast<QWSQCopRegisterChannelCommand*>(command);
+ request += QString( QLatin1String("/QCop/RegisterChannel/%1") ).arg( registerCommand->channel );
+ }
+#endif
+ dataSize = QWS_PROTOCOL_ITEM_SIZE( *command );
+ delete command;
+ return request;
+#else
+ Q_UNUSED(msgQueue);
+ return QString();
+#endif
+}
+
+////////////////////////////////////////////////////////////////////////
+////
+//// AuthDevice definition
+////
+
+/*!
+ Constructs a new auth device for the transport \a data and I/O device \a parent.
+
+ Incoming or outgoing data will be authenticated according to the auth direction \a dir.
+
+ The auth device will take ownership of the transport \a data and delete it when the device
+ is destroyed.
+*/
+QAuthDevice::QAuthDevice( QIODevice *parent, QTransportAuth::Data *data, AuthDirection dir )
+ : QIODevice( parent )
+ , d( data )
+ , way( dir )
+ , m_target( parent )
+ , m_client( 0 )
+ , m_bytesAvailable( 0 )
+ , m_skipWritten( 0 )
+ , analyzer( 0 )
+{
+ if ( dir == Receive ) // server side
+ {
+ connect( m_target, SIGNAL(readyRead()),
+ this, SLOT(recvReadyRead()));
+ } else {
+ connect( m_target, SIGNAL(readyRead()),
+ this, SIGNAL(readyRead()));
+ }
+ connect( m_target, SIGNAL(bytesWritten(qint64)),
+ this, SLOT(targetBytesWritten(qint64)) );
+ open( QIODevice::ReadWrite | QIODevice::Unbuffered );
+}
+
+QAuthDevice::~QAuthDevice()
+{
+ if ( analyzer )
+ delete analyzer;
+ delete d;
+}
+
+/*!
+ \internal
+ Store a pointer to the related device or instance which this
+ authorizer is proxying for
+*/
+void QAuthDevice::setClient( QObject *cli )
+{
+ m_client = cli;
+ QTransportAuth::getInstance()->d_func()->buffersByClient[cli] = this;
+ QObject::connect( cli, SIGNAL(destroyed(QObject*)),
+ QTransportAuth::getInstance(), SLOT(bufferDestroyed(QObject*)) );
+ // qDebug( "@@@@@@@@@@@@ client set %p @@@@@@@@@", cli );
+ // qDebug( " client count %d", QTransportAuth::getInstance()->d_func()->buffersByClient.count() );
+}
+
+QObject *QAuthDevice::client() const
+{
+ return m_client;
+}
+
+/*
+ \fn void QAuthDevice::authViolation(QTransportAuth::Data &)
+
+ This signal is emitted if an authorization failure is generated, as
+ described in checkAuth();
+
+ \sa checkAuth()
+*/
+
+
+/*
+ \fn void QAuthDevice::policyCheck(QTransportAuth::Data &transport, const QString &request )
+
+ This signal is emitted when a transport successfully delivers a request
+ and gives the opportunity to either deny or accept the request.
+
+ This signal must be connected in the same thread, ie it cannot be queued.
+
+ As soon as all handlers connected to this signal are processed the Allow or
+ Deny state on the \a transport is checked, and the request is allowed or denied
+ accordingly.
+
+ \sa checkAuth()
+*/
+
+/*!
+ \internal
+ Reimplement QIODevice writeData method.
+
+ For client end, when the device is written to the incoming data is
+ processed and an authentication header calculated. This is pushed
+ into the target device, followed by the actual incoming data (the
+ payload).
+
+ For server end, it is a fatal error to write to the device.
+*/
+qint64 QAuthDevice::writeData(const char *data, qint64 len)
+{
+ if ( way == Receive ) // server
+ return m_target->write( data, len );
+ // client
+#ifdef QTRANSPORTAUTH_DEBUG
+ char displaybuf[1024];
+#endif
+ char header[QSXE_HEADER_LEN];
+ ::memset( header, 0, QSXE_HEADER_LEN );
+ qint64 bytes = 0;
+ if ( QTransportAuth::getInstance()->authToMessage( *d, header, data, len ))
+ {
+ m_target->write( header, QSXE_HEADER_LEN );
+#ifdef QTRANSPORTAUTH_DEBUG
+ hexstring( displaybuf, (const unsigned char *)header, QSXE_HEADER_LEN );
+ qDebug( "%d QAuthDevice::writeData - CLIENT: Header written: %s", getpid(), displaybuf );
+#endif
+ m_skipWritten += QSXE_HEADER_LEN;
+ }
+ m_target->write( data, len );
+ bytes += len;
+#ifdef QTRANSPORTAUTH_DEBUG
+ int bytesToDisplay = bytes;
+ const unsigned char *dataptr = (const unsigned char *)data;
+ while ( bytesToDisplay > 0 )
+ {
+ int amt = bytes < 500 ? bytes : 500;
+ hexstring( displaybuf, dataptr, amt );
+ qDebug( "%d QAuthDevice::writeData - CLIENT: %s", getpid(), bytes > 0 ? displaybuf : "(null)" );
+ dataptr += 500;
+ bytesToDisplay -= 500;
+ }
+#endif
+ if ( m_target->inherits( "QAbstractSocket" ))
+ static_cast<QAbstractSocket*>(m_target)->flush();
+ return bytes;
+}
+
+/*!
+ Reimplement from QIODevice
+
+ Read data out of the internal message queue, reduce the queue by the amount
+ read. Note that the amount available is only ever the size of a command
+ (although a command can be very big) since we need to check at command
+ boundaries for new authentication headers.
+*/
+qint64 QAuthDevice::readData( char *data, qint64 maxSize )
+{
+ if ( way == Send ) // client
+ return m_target->read( data, maxSize );
+ if ( msgQueue.size() == 0 )
+ return 0;
+#ifdef QTRANSPORTAUTH_DEBUG
+ char displaybuf[1024];
+ hexstring( displaybuf, reinterpret_cast<const unsigned char *>(msgQueue.constData()),
+ msgQueue.size() > 500 ? 500 : msgQueue.size() );
+ qDebug() << getpid() << "QAuthDevice::readData() buffered/requested/avail"
+ << msgQueue.size() << maxSize << m_bytesAvailable << displaybuf;
+#endif
+ Q_ASSERT( m_bytesAvailable <= msgQueue.size() );
+ qint64 bytes = ( maxSize > m_bytesAvailable ) ? m_bytesAvailable : maxSize;
+ ::memcpy( data, msgQueue.constData(), bytes );
+ msgQueue = msgQueue.mid( bytes );
+ m_bytesAvailable -= bytes;
+ return bytes;
+}
+
+/*!
+ \internal
+ Receive readyRead signal from the target recv device. In response
+ authorize the data, and write results out to the recvBuf() device
+ for processing by the application. Trigger the readyRead signal.
+
+ Authorizing involves first checking the transport is valid, ie the
+ handshake has either already been done and is cached on a trusted
+ transport, or was valid with this message; then second passing the
+ string representation of the service request up to any policyReceivers
+
+ If either of these fail, the message is denied. In discovery mode
+ denied messages are allowed, but the message is logged.
+*/
+void QAuthDevice::recvReadyRead()
+{
+ qint64 bytes = m_target->bytesAvailable();
+ if ( bytes <= 0 ) return;
+ open( QIODevice::ReadWrite | QIODevice::Unbuffered );
+ QUnixSocket *usock = static_cast<QUnixSocket*>(m_target);
+ QUnixSocketMessage msg = usock->read();
+ msgQueue.append( msg.bytes() );
+ d->processId = msg.processId();
+ // if "fragmented" packet 1/2 way through start of a command, ie
+ // in the QWS msg type, cant do anything, come back later when
+ // there's more of the packet
+ if ( msgQueue.size() < (int)sizeof(int) )
+ {
+ // qDebug() << "returning: msg size too small" << msgQueue.size();
+ return;
+ }
+#ifdef QTRANSPORTAUTH_DEBUG
+ char displaybuf[1024];
+ hexstring( displaybuf, reinterpret_cast<const unsigned char *>(msgQueue.constData()),
+ msgQueue.size() > 500 ? 500 : msgQueue.size() );
+ qDebug( "%d ***** SERVER read %lli bytes - msg %s", getpid(), bytes, displaybuf );
+#endif
+
+ bool bufHasMessages = msgQueue.size() >= (int)sizeof(int);
+ while ( bufHasMessages )
+ {
+ unsigned char saveStatus = d->status;
+ if (( d->status & QTransportAuth::ErrMask ) == QTransportAuth::NoSuchKey )
+ {
+ QTransportAuth::getInstance()->authorizeRequest( *d, QLatin1String("NoSuchKey") );
+ break;
+ }
+ if ( !QTransportAuth::getInstance()->authFromMessage( *d, msgQueue, msgQueue.size() ))
+ {
+ // not all arrived yet? come back later
+ if (( d->status & QTransportAuth::ErrMask ) == QTransportAuth::TooSmall )
+ {
+ d->status = saveStatus;
+ return;
+ }
+ }
+ if (( d->status & QTransportAuth::ErrMask ) == QTransportAuth::NoMagic )
+ {
+ // no msg auth header, don't change the success status for connections
+ if ( d->connection() )
+ d->status = saveStatus;
+ }
+ else
+ {
+ // msg auth header detected and auth determined, remove hdr
+ msgQueue = msgQueue.mid( QSXE_HEADER_LEN );
+ }
+ if ( !authorizeMessage() )
+ break;
+ bufHasMessages = msgQueue.size() >= (int)sizeof(int);
+ }
+}
+
+/**
+ \internal
+ Handle bytesWritten signals from the underlying target device.
+ We adjust the target's value for bytes that are part of auth packets.
+*/
+void QAuthDevice::targetBytesWritten( qint64 bytes )
+{
+ if ( m_skipWritten >= bytes ) {
+ m_skipWritten -= bytes;
+ bytes = 0;
+ } else if ( m_skipWritten > 0 ) {
+ bytes -= m_skipWritten;
+ m_skipWritten = 0;
+ }
+ if ( bytes > 0 ) {
+ emit bytesWritten( bytes );
+ }
+}
+
+/**
+ \internal
+ Pre-process the message to determine what QWS command it is. This
+ information is used as the "request" for the purposes of authorization.
+
+ The request and other data on the connection (id, PID, etc.) are forwarded
+ to all policy listeners by emitting a signal.
+
+ The signal must be processed synchronously because on return the allow/deny
+ status is used immediately to either drop or continue processing the message.
+*/
+bool QAuthDevice::authorizeMessage()
+{
+ if ( analyzer == NULL )
+ analyzer = new RequestAnalyzer();
+ QString request = (*analyzer)( &msgQueue );
+ if ( analyzer->requireMoreData() )
+ return false;
+ bool isAuthorized = true;
+
+ if ( !request.isEmpty() && request != QLatin1String("Unknown") )
+ {
+ isAuthorized = QTransportAuth::getInstance()->authorizeRequest( *d, request );
+ }
+
+ bool moreToProcess = ( msgQueue.size() - analyzer->bytesAnalyzed() ) > (int)sizeof(int);
+ if ( isAuthorized )
+ {
+#ifdef QTRANSPORTAUTH_DEBUG
+ qDebug() << getpid() << "SERVER authorized: releasing" << analyzer->bytesAnalyzed() << "byte command" << request;
+#endif
+ m_bytesAvailable = analyzer->bytesAnalyzed();
+ emit QIODevice::readyRead();
+ return moreToProcess;
+ }
+ else
+ {
+ msgQueue = msgQueue.mid( analyzer->bytesAnalyzed() );
+ }
+
+ return true;
+}
+
+void QAuthDevice::setRequestAnalyzer( RequestAnalyzer *ra )
+{
+ Q_ASSERT( ra );
+ if ( analyzer )
+ delete analyzer;
+ analyzer = ra;
+}
+
+/*!
+ \internal
+ Add authentication header to the beginning of a message
+
+ Note that the per-process auth cookie is used. This key should be rewritten in
+ the binary image of the executable at install time to make it unique.
+
+ For this to be secure some mechanism (eg MAC kernel or other
+ permissions) must prevent other processes from reading the key.
+
+ The buffer must have AUTH_SPACE(0) bytes spare at the beginning for the
+ authentication header to be added.
+
+ Returns true if header successfully added. Will fail if the
+ per-process key has not yet been set with setProcessKey()
+*/
+bool QTransportAuth::authToMessage( QTransportAuth::Data &d, char *hdr, const char *msg, int msgLen )
+{
+ // qDebug( "authToMessage(): prog id %u", d.progId );
+ // only authorize connection oriented transports once, unless key has changed
+ if ( d.connection() && ((d.status & QTransportAuth::ErrMask) != QTransportAuth::Pending) &&
+ d_func()->authKey.progId == d.progId )
+ return false;
+ d.progId = d_func()->authKey.progId;
+ // If Unix socket credentials are being used the key wont be set
+ if ( !d_func()->keyInitialised )
+ return false;
+ unsigned char digest[QSXE_KEY_LEN];
+ char *msgPtr = hdr;
+ // magic always goes on the beginning
+ for ( int m = 0; m < QSXE_MAGIC_BYTES; ++m )
+ *msgPtr++ = magic[m];
+ hdr[ QSXE_LEN_IDX ] = (unsigned char)msgLen;
+ if ( !d.trusted())
+ {
+ // Use HMAC
+ int rc = hmac_md5( (unsigned char *)msg, msgLen, d_func()->authKey.key, QSXE_KEY_LEN, digest );
+ if ( rc == -1 )
+ return false;
+ memcpy( hdr + QSXE_KEY_IDX, digest, QSXE_KEY_LEN );
+ }
+ else
+ {
+ memcpy( hdr + QSXE_KEY_IDX, d_func()->authKey.key, QSXE_KEY_LEN );
+ }
+
+ hdr[ QSXE_PROG_IDX ] = d_func()->authKey.progId;
+
+#ifdef QTRANSPORTAUTH_DEBUG
+ char keydisplay[QSXE_KEY_LEN*2+1];
+ hexstring( keydisplay, d_func()->authKey.key, QSXE_KEY_LEN );
+
+ qDebug( "%d CLIENT Auth to message %s against prog id %u and key %s\n",
+ getpid(), msg, d_func()->authKey.progId, keydisplay );
+#endif
+
+ // TODO implement sequence to prevent replay attack, not required
+ // for trusted transports
+ hdr[ QSXE_SEQ_IDX ] = 1; // dummy sequence
+
+ d.status = ( d.status & QTransportAuth::StatusMask ) | QTransportAuth::Success;
+ return true;
+}
+
+
+/*!
+ Check authorization on the \a msg, which must be of size \a msgLen,
+ for the transport \a d.
+
+ If able to determine authorization, return the program identity of
+ the message source in the reference \a progId, and return true.
+
+ Otherwise return false.
+
+ If data is being received on a socket, it may be that more data is yet
+ needed before authentication can proceed.
+
+ Also the message may not be an authenticated at all.
+
+ In these cases the method returns false to indicate authorization could
+ not be determined:
+ \list
+ \i The message is too small to carry the authentication data
+ (status TooSmall is set on the \a d transport )
+ \i The 4 magic bytes are missing from the message start
+ (status NoMagic is set on the \a d transport )
+ \i The message is too small to carry the auth + claimed payload
+ (status TooSmall is set on the \a d transport )
+ \endlist
+
+ If however the authentication header (preceded by the magic bytes) and
+ any authenticated payload is received the method will determine the
+ authentication status, and return true.
+
+ In the following cases as well as returning true it will also emit
+ an authViolation():
+ \list
+ \i If the program id claimed by the message is not found in the key file
+ (status NoSuchKey is set on the \a d transport )
+ \i The authentication token failed against the claimed program id:
+ \list
+ \i in the case of trusted transports, the secret did not match
+ \i in the case of untrusted transports the HMAC code did not match
+ \endlist
+ (status FailMatch is set on the \a d transport )
+ \endlist
+
+ In these cases the authViolation( QTransportAuth::Data d ) signal is emitted
+ and the error string can be obtained from the status like this:
+ \snippet doc/src/snippets/code/src_gui_embedded_qtransportauth_qws.cpp 4
+*/
+bool QTransportAuth::authFromMessage( QTransportAuth::Data &d, const char *msg, int msgLen )
+{
+ if ( msgLen < QSXE_MAGIC_BYTES )
+ {
+ d.status = ( d.status & QTransportAuth::StatusMask ) | QTransportAuth::TooSmall;
+ return false;
+ }
+ // if no magic bytes, exit straight away
+ int m;
+ const unsigned char *mptr = reinterpret_cast<const unsigned char *>(msg);
+ for ( m = 0; m < QSXE_MAGIC_BYTES; ++m )
+ {
+ if ( *mptr++ != magic[m] )
+ {
+ d.status = ( d.status & QTransportAuth::StatusMask ) | QTransportAuth::NoMagic;
+ return false;
+ }
+ }
+
+ if ( msgLen < AUTH_SPACE(1) )
+ {
+ d.status = ( d.status & QTransportAuth::StatusMask ) | QTransportAuth::TooSmall;
+ return false;
+ }
+
+ // At this point we know the header is at least long enough to contain valid auth
+ // data, however the data may be spoofed. If it is not verified then the status will
+ // be set to uncertified so the spoofed data will not be relied on. However we want to
+ // know the program id which is being reported (even if it might be spoofed) for
+ // policy debugging purposes. So set it here, rather than after verification.
+ d.progId = msg[QSXE_PROG_IDX];
+
+#ifdef QTRANSPORTAUTH_DEBUG
+ char authhdr[QSXE_HEADER_LEN*2+1];
+ hexstring( authhdr, reinterpret_cast<const unsigned char *>(msg), QSXE_HEADER_LEN );
+ qDebug( "%d SERVER authFromMessage(): message header is %s",
+ getpid(), authhdr );
+#endif
+
+ unsigned char authLen = (unsigned char)(msg[ QSXE_LEN_IDX ]);
+
+ if ( msgLen < AUTH_SPACE(authLen) )
+ {
+ d.status = ( d.status & QTransportAuth::StatusMask ) | QTransportAuth::TooSmall;
+ return false;
+ }
+
+ bool isCached = d_func()->keyCache.contains( d.progId );
+ const unsigned char *clientKey = d_func()->getClientKey( d.progId );
+ if ( clientKey == NULL )
+ {
+ d.status = ( d.status & QTransportAuth::StatusMask ) | QTransportAuth::NoSuchKey;
+ return false;
+ }
+
+#ifdef QTRANSPORTAUTH_DEBUG
+ char keydisplay[QSXE_KEY_LEN*2+1];
+ hexstring( keydisplay, clientKey, QSXE_KEY_LEN );
+ qDebug( "\t\tauthFromMessage(): message %s against prog id %u and key %s\n",
+ AUTH_DATA(msg), ((unsigned int)d.progId), keydisplay );
+#endif
+
+ const unsigned char *auth_tok;
+ unsigned char digest[QSXE_KEY_LEN];
+ bool multi_tok = false;
+
+ bool need_to_recheck=false;
+ do
+ {
+ if ( !d.trusted())
+ {
+ hmac_md5( AUTH_DATA(msg), authLen, clientKey, QSXE_KEY_LEN, digest );
+ auth_tok = digest;
+ }
+ else
+ {
+ auth_tok = clientKey;
+ multi_tok = true; // 1 or more keys are in the clientKey
+ }
+ while( true )
+ {
+ if ( memcmp( auth_tok, magic, QSXE_MAGIC_BYTES ) == 0
+ && memcmp( auth_tok + QSXE_MAGIC_BYTES, magic, QSXE_MAGIC_BYTES ) == 0 )
+ break;
+ if ( memcmp( msg + QSXE_KEY_IDX, auth_tok, QSXE_KEY_LEN ) == 0 )
+ {
+ d.status = ( d.status & QTransportAuth::StatusMask ) | QTransportAuth::Success;
+ return true;
+ }
+ if ( !multi_tok )
+ break;
+ auth_tok += QSXE_KEY_LEN;
+ }
+ //the keys cached on d.progId may not contain the binary key because the cache entry was made
+ //before the binary had first started, must search for client key again.
+ if ( isCached )
+ {
+ d_func()->keyCache.remove(d.progId);
+ isCached = false;
+
+#ifdef QTRANSPORTAUTH_DEBUG
+ qDebug() << "QTransportAuth::authFromMessage(): key not found in set of keys cached"
+ << "against prog Id =" << d.progId << ". Re-obtaining client key. ";
+#endif
+ clientKey = d_func()->getClientKey( d.progId );
+ if ( clientKey == NULL )
+ {
+ d.status = ( d.status & QTransportAuth::StatusMask ) | QTransportAuth::NoSuchKey;
+ return false;
+ }
+ need_to_recheck = true;
+ }
+ else
+ {
+ need_to_recheck = false;
+ }
+ } while( need_to_recheck );
+
+ d.status = ( d.status & QTransportAuth::StatusMask ) | QTransportAuth::FailMatch;
+ qWarning() << "QTransportAuth::authFromMessage():failed authentication";
+ FAREnforcer::getInstance()->logAuthAttempt( QDateTime::currentDateTime() );
+ emit authViolation( d );
+ return false;
+}
+
+
+#ifdef QTRANSPORTAUTH_DEBUG
+/*!
+ sprintf into hex - dest \a buf, src \a key, \a key_len is length of key.
+
+ The target buf should be [ key_len * 2 + 1 ] in size
+*/
+void hexstring( char *buf, const unsigned char* key, size_t key_len )
+{
+ unsigned int i, p;
+ for ( i = 0, p = 0; i < key_len; i++, p+=2 )
+ {
+ unsigned char lo_nibble = key[i] & 0x0f;
+ unsigned char hi_nibble = key[i] >> 4;
+ buf[p] = (int)hi_nibble > 9 ? hi_nibble-10 + 'A' : hi_nibble + '0';
+ buf[p+1] = (int)lo_nibble > 9 ? lo_nibble-10 + 'A' : lo_nibble + '0';
+ }
+ buf[p] = '\0';
+}
+#endif
+
+/*
+ HMAC MD5 as listed in RFC 2104
+
+ This code is taken from:
+
+ http://www.faqs.org/rfcs/rfc2104.html
+
+ with the allowance for keys other than length 16 removed, but otherwise
+ a straight cut-and-paste.
+
+ The HMAC_MD5 transform looks like:
+
+ \snippet doc/src/snippets/code/src.gui.embedded.qtransportauth_qws.cpp 5
+
+ \list
+ \i where K is an n byte key
+ \i ipad is the byte 0x36 repeated 64 times
+ \i opad is the byte 0x5c repeated 64 times
+ \i and text is the data being protected
+ \endlist
+
+ Hardware is available with accelerated implementations of HMAC-MD5 and
+ HMAC-SHA1. Where this hardware is available, this routine should be
+ replaced with a call into the accelerated version.
+*/
+
+static int hmac_md5(
+ unsigned char* text, /* pointer to data stream */
+ int text_length, /* length of data stream */
+ const unsigned char* key, /* pointer to authentication key */
+ int key_length, /* length of authentication key */
+ unsigned char * digest /* caller digest to be filled in */
+ )
+{
+ MD5Context context;
+ unsigned char k_ipad[65]; /* inner padding - * key XORd with ipad */
+ unsigned char k_opad[65]; /* outer padding - * key XORd with opad */
+ int i;
+
+ /* in this implementation key_length == 16 */
+ if ( key_length != 16 )
+ {
+ fprintf( stderr, "Key length was %d - must be 16 bytes", key_length );
+ return 0;
+ }
+
+ /* start out by storing key in pads */
+ memset( k_ipad, 0, sizeof k_ipad );
+ memset( k_opad, 0, sizeof k_opad );
+ memcpy( k_ipad, key, key_length );
+ memcpy( k_opad, key, key_length );
+
+ /* XOR key with ipad and opad values */
+ for (i=0; i<64; i++) {
+ k_ipad[i] ^= 0x36;
+ k_opad[i] ^= 0x5c;
+ }
+
+ /* perform inner MD5 */
+ MD5Init(&context); /* init context for 1st pass */
+ MD5Update(&context, k_ipad, 64); /* start with inner pad */
+ MD5Update(&context, text, text_length); /* then text of datagram */
+ MD5Final(&context, digest); /* finish up 1st pass */
+
+ /* perform outer MD5 */
+ MD5Init(&context); /* init context for 2nd pass */
+ MD5Update(&context, k_opad, 64); /* start with outer pad */
+ MD5Update(&context, digest, 16); /* then results of 1st * hash */
+ MD5Final(&context, digest); /* finish up 2nd pass */
+ return 1;
+}
+
+
+const int FAREnforcer::minutelyRate = 4; //allowed number of false authentication attempts per minute
+const QString FAREnforcer::FARMessage = QLatin1String("FAR_Exceeded");
+const QString FAREnforcer::SxeTag = QLatin1String("<SXE Breach>");
+const int FAREnforcer::minute = 60;
+
+FAREnforcer::FAREnforcer():authAttempts()
+{
+ QDateTime nullDateTime = QDateTime();
+ for (int i = 0; i < minutelyRate; i++ )
+ authAttempts << nullDateTime;
+}
+
+
+FAREnforcer *FAREnforcer::getInstance()
+{
+ static FAREnforcer theInstance;
+ return &theInstance;
+}
+
+void FAREnforcer::logAuthAttempt( QDateTime time )
+{
+ QDateTime dt = authAttempts.takeFirst();
+
+ authAttempts.append( time );
+ if ( dt.secsTo( authAttempts.last() ) <= minute )
+ {
+#if defined(SXE_DISCOVERY)
+ if ( QTransportAuth::getInstance()->isDiscoveryMode() ) {
+ static QBasicAtomicInt reported = Q_BASIC_ATOMIC_INITIALIZER(0);
+ if ( reported.testAndSetRelaxed(0,1) ) {
+#ifndef QT_NO_TEXTSTREAM
+ QString logFilePath = QTransportAuth::getInstance()->logFilePath();
+ if ( !logFilePath.isEmpty() ) {
+ QFile log( logFilePath );
+ if ( !log.open(QIODevice::WriteOnly | QIODevice::Append) ) {
+ qWarning("Could not write to log in discovery mode: %s",
+ qPrintable(logFilePath) );
+ } else {
+ QTextStream ts( &log );
+ ts << "\t\tWarning: False Authentication Rate of " << minutelyRate << "\n"
+ << "\t\tserver connections/authentications per minute has been exceeded,\n"
+ << "\t\tno further warnings will be issued\n";
+ }
+ }
+ }
+#endif
+ reset();
+ return;
+ }
+#endif
+ syslog( LOG_ERR | LOG_LOCAL6, "%s %s",
+ qPrintable( FAREnforcer::SxeTag ),
+ qPrintable( FAREnforcer::FARMessage ) );
+ reset();
+ }
+}
+
+void FAREnforcer::reset()
+{
+ QDateTime nullDateTime = QDateTime();
+ for (int i = 0; i < minutelyRate; i++ )
+ authAttempts[i] = nullDateTime;
+}
+
+QT_END_NAMESPACE
+
+#include "moc_qtransportauth_qws_p.cpp"
+
+#endif // QT_NO_SXE
diff --git a/src/gui/embedded/qtransportauth_qws.h b/src/gui/embedded/qtransportauth_qws.h
new file mode 100644
index 0000000000..2d2dd529fc
--- /dev/null
+++ b/src/gui/embedded/qtransportauth_qws.h
@@ -0,0 +1,281 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QTRANSPORTAUTH_QWS_H
+#define QTRANSPORTAUTH_QWS_H
+
+#include <QtCore/qglobal.h>
+
+#if !defined(QT_NO_SXE) || defined(SXE_INSTALLER)
+
+#include <QtCore/qobject.h>
+#include <QtCore/qhash.h>
+#include <QtCore/qstring.h>
+#include <QtCore/qbuffer.h>
+#include <QtCore/qpointer.h>
+
+#include <sys/types.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+class QAuthDevice;
+class QWSClient;
+class QIODevice;
+class QTransportAuthPrivate;
+class QMutex;
+
+class Q_GUI_EXPORT QTransportAuth : public QObject
+{
+ Q_OBJECT
+public:
+ static QTransportAuth *getInstance();
+
+ enum Result {
+ // Error codes
+ Pending = 0x00,
+ TooSmall = 0x01,
+ CacheMiss = 0x02,
+ NoMagic = 0x03,
+ NoSuchKey = 0x04,
+ FailMatch = 0x05,
+ OutOfDate = 0x06,
+ // reserved for expansion
+ Success = 0x1e,
+ ErrMask = 0x1f,
+
+ // Verification codes
+ Allow = 0x20,
+ Deny = 0x40,
+ Ask = 0x60,
+ // reserved
+ StatusMask = 0xe0
+ };
+
+ enum Properties {
+ Trusted = 0x01,
+ Connection = 0x02,
+ UnixStreamSock = 0x04,
+ SharedMemory = 0x08,
+ MessageQueue = 0x10,
+ UDP = 0x20,
+ TCP = 0x40,
+ UserDefined = 0x80,
+ TransportType = 0xfc
+ };
+
+ struct Data
+ {
+ Data() { processId = -1; }
+ Data( unsigned char p, int d )
+ : properties( p )
+ , descriptor( d )
+ , processId( -1 )
+ {
+ if (( properties & TransportType ) == TCP ||
+ ( properties & TransportType ) == UnixStreamSock )
+ properties |= Connection;
+ }
+
+ unsigned char properties;
+ unsigned char progId;
+ unsigned char status;
+ unsigned int descriptor; // socket fd or shmget key
+ pid_t processId;
+
+ bool trusted() const;
+ void setTrusted( bool );
+ bool connection() const;
+ void setConnection( bool );
+ };
+
+ static const char *errorString( const QTransportAuth::Data & );
+
+ QTransportAuth::Data *connectTransport( unsigned char, int );
+
+ QAuthDevice *authBuf( QTransportAuth::Data *, QIODevice * );
+ QAuthDevice *recvBuf( QTransportAuth::Data *, QIODevice * );
+ QIODevice *passThroughByClient( QWSClient * ) const;
+
+ void setKeyFilePath( const QString & );
+ QString keyFilePath() const;
+ const unsigned char *getClientKey( unsigned char progId );
+ void invalidateClientKeyCache();
+ QMutex *getKeyFileMutex();
+ void setLogFilePath( const QString & );
+ QString logFilePath() const;
+ void setPackageRegistry( QObject *registry );
+ bool isDiscoveryMode() const;
+ void setProcessKey( const char * );
+ void setProcessKey( const char *, const char * );
+ void registerPolicyReceiver( QObject * );
+ void unregisterPolicyReceiver( QObject * );
+
+ bool authToMessage( QTransportAuth::Data &d, char *hdr, const char *msg, int msgLen );
+ bool authFromMessage( QTransportAuth::Data &d, const char *msg, int msgLen );
+
+ bool authorizeRequest( QTransportAuth::Data &d, const QString &request );
+
+Q_SIGNALS:
+ void policyCheck( QTransportAuth::Data &, const QString & );
+ void authViolation( QTransportAuth::Data & );
+private Q_SLOTS:
+ void bufferDestroyed( QObject * );
+
+private:
+ // users should never construct their own
+ QTransportAuth();
+ ~QTransportAuth();
+
+ friend class QAuthDevice;
+ Q_DECLARE_PRIVATE(QTransportAuth)
+};
+
+class Q_GUI_EXPORT RequestAnalyzer
+{
+public:
+ RequestAnalyzer();
+ virtual ~RequestAnalyzer();
+ QString operator()( QByteArray *data ) { return analyze( data ); }
+ bool requireMoreData() const { return moreData; }
+ qint64 bytesAnalyzed() const { return dataSize; }
+protected:
+ virtual QString analyze( QByteArray * );
+ bool moreData;
+ qint64 dataSize;
+};
+
+/*!
+ \internal
+ \class QAuthDevice
+
+ \brief Pass-through QIODevice sub-class for authentication.
+
+ Use this class to forward on or receive forwarded data over a real
+ device for authentication.
+*/
+class Q_GUI_EXPORT QAuthDevice : public QIODevice
+{
+ Q_OBJECT
+public:
+ enum AuthDirection {
+ Receive,
+ Send
+ };
+ QAuthDevice( QIODevice *, QTransportAuth::Data *, AuthDirection );
+ ~QAuthDevice();
+ void setTarget( QIODevice *t ) { m_target = t; }
+ QIODevice *target() const { return m_target; }
+ void setClient( QObject* );
+ QObject *client() const;
+ void setRequestAnalyzer( RequestAnalyzer * );
+ bool isSequential() const;
+ bool atEnd() const;
+ qint64 bytesAvailable() const;
+ qint64 bytesToWrite() const;
+ bool seek( qint64 );
+ QByteArray & buffer();
+
+protected:
+ qint64 readData( char *, qint64 );
+ qint64 writeData(const char *, qint64 );
+private Q_SLOTS:
+ void recvReadyRead();
+ void targetBytesWritten( qint64 );
+private:
+ bool authorizeMessage();
+
+ QTransportAuth::Data *d;
+ AuthDirection way;
+ QIODevice *m_target;
+ QObject *m_client;
+ QByteArray msgQueue;
+ qint64 m_bytesAvailable;
+ qint64 m_skipWritten;
+
+ RequestAnalyzer *analyzer;
+};
+
+inline bool QAuthDevice::isSequential() const
+{
+ return true;
+}
+
+inline bool QAuthDevice::seek( qint64 )
+{
+ return false;
+}
+
+inline bool QAuthDevice::atEnd() const
+{
+ return msgQueue.isEmpty();
+}
+
+inline qint64 QAuthDevice::bytesAvailable() const
+{
+ if ( way == Receive )
+ return m_bytesAvailable;
+ else
+ return ( m_target ? m_target->bytesAvailable() : 0 );
+}
+
+inline qint64 QAuthDevice::bytesToWrite() const
+{
+ return msgQueue.size();
+}
+
+inline QByteArray &QAuthDevice::buffer()
+{
+ return msgQueue;
+}
+
+
+
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QT_NO_SXE
+#endif // QTRANSPORTAUTH_QWS_H
diff --git a/src/gui/embedded/qtransportauth_qws_p.h b/src/gui/embedded/qtransportauth_qws_p.h
new file mode 100644
index 0000000000..33e2edc532
--- /dev/null
+++ b/src/gui/embedded/qtransportauth_qws_p.h
@@ -0,0 +1,189 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QTRANSPORTAUTH_QWS_P_H
+#define QTRANSPORTAUTH_QWS_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 <QtCore/qglobal.h>
+
+#ifndef QT_NO_SXE
+
+#include "qtransportauth_qws.h"
+#include "qtransportauthdefs_qws.h"
+#include "qbuffer.h"
+
+#include <qmutex.h>
+#include <qdatetime.h>
+#include "private/qobject_p.h"
+
+#include <QtCore/qcache.h>
+
+QT_BEGIN_NAMESPACE
+
+// Uncomment to generate debug output
+// #define QTRANSPORTAUTH_DEBUG 1
+
+#ifdef QTRANSPORTAUTH_DEBUG
+void hexstring( char *buf, const unsigned char* key, size_t sz );
+#endif
+
+// proj id for ftok usage in sxe
+#define SXE_PROJ 10022
+
+/*!
+ \internal
+ memset for security purposes, guaranteed not to be optimized away
+ http://www.faqs.org/docs/Linux-HOWTO/Secure-Programs-HOWTO.html
+*/
+void *guaranteed_memset(void *v,int c,size_t n);
+
+class QUnixSocketMessage;
+
+/*!
+ \internal
+ \class AuthCookie
+ Struct to carry process authentication key and id
+*/
+#define QSXE_HEADER_LEN 24
+
+/*!
+ \macro AUTH_ID
+ Macro to manage authentication header. Format of header is:
+ \table
+ \header \i BYTES \i CONTENT
+ \row \i 0-3 \i magic numbers
+ \row \i 4 \i length of authenticated data (max 255 bytes)
+ \row i\ 5 \i reserved
+ \row \i 6-21 \i MAC digest, or shared secret in case of simple auth
+ \row \i 22 \i program id
+ \row \i 23 \i sequence number
+ \endtable
+ Total length of the header is 24 bytes
+
+ However this may change. Instead of coding these numbers use the AUTH_ID,
+ AUTH_KEY, AUTH_DATA and AUTH_SPACE macros.
+*/
+
+#define AUTH_ID(k) ((unsigned char)(k[QSXE_KEY_LEN]))
+#define AUTH_KEY(k) ((unsigned char *)(k))
+
+#define AUTH_DATA(x) (unsigned char *)((x) + QSXE_HEADER_LEN)
+#define AUTH_SPACE(x) ((x) + QSXE_HEADER_LEN)
+#define QSXE_LEN_IDX 4
+#define QSXE_KEY_IDX 6
+#define QSXE_PROG_IDX 22
+#define QSXE_SEQ_IDX 23
+
+class SxeRegistryLocker : public QObject
+{
+ Q_OBJECT
+public:
+ SxeRegistryLocker( QObject * );
+ ~SxeRegistryLocker();
+ bool success() const { return m_success; }
+private:
+ bool m_success;
+ QObject *m_reg;
+};
+
+class QTransportAuthPrivate : public QObjectPrivate
+{
+ Q_DECLARE_PUBLIC(QTransportAuth)
+public:
+ QTransportAuthPrivate();
+ ~QTransportAuthPrivate();
+
+ const unsigned char *getClientKey( unsigned char progId );
+ void invalidateClientKeyCache();
+
+ bool keyInitialised;
+ QString m_logFilePath;
+ QString m_keyFilePath;
+ QObject *m_packageRegistry;
+ AuthCookie authKey;
+ QCache<unsigned char, char> keyCache;
+ QHash< QObject*, QIODevice*> buffersByClient;
+ QMutex keyfileMutex;
+};
+
+/*!
+ \internal
+ Enforces the False Authentication Rate. If more than 4 authentications
+ are received per minute the sxemonitor is notified that the FAR has been exceeded
+*/
+class FAREnforcer
+{
+ public:
+ static FAREnforcer *getInstance();
+ void logAuthAttempt( QDateTime time = QDateTime::currentDateTime() );
+ void reset();
+
+#ifndef TEST_FAR_ENFORCER
+ private:
+#endif
+ FAREnforcer();
+ FAREnforcer( const FAREnforcer & );
+ FAREnforcer &operator=(FAREnforcer const & );
+
+ static const QString FARMessage;
+ static const int minutelyRate;
+ static const QString SxeTag;
+ static const int minute;
+
+ QList<QDateTime> authAttempts;
+};
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_SXE
+#endif // QTRANSPORTAUTH_QWS_P_H
+
diff --git a/src/gui/embedded/qtransportauthdefs_qws.h b/src/gui/embedded/qtransportauthdefs_qws.h
new file mode 100644
index 0000000000..85218b6647
--- /dev/null
+++ b/src/gui/embedded/qtransportauthdefs_qws.h
@@ -0,0 +1,174 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QTRANSPORTAUTHDEFS_QWS_H
+#define QTRANSPORTAUTHDEFS_QWS_H
+
+#include <sys/types.h>
+#include <string.h>
+
+#include <QtCore/qglobal.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+#define QSXE_KEY_LEN 16
+#define QSXE_MAGIC_BYTES 4
+
+// Number of bytes of each message to authenticate. Just need to ensure
+// that the command at the beginning hasn't been tampered with. This value
+// does not matter for trusted transports.
+#define AMOUNT_TO_AUTHENTICATE 200
+
+#define AUTH_ID(k) ((unsigned char)(k[QSXE_KEY_LEN]))
+#define AUTH_KEY(k) ((unsigned char *)(k))
+
+// must be a largish -ve number under any endianess when cast as an int
+const unsigned char magic[QSXE_MAGIC_BYTES] = { 0xBA, 0xD4, 0xD4, 0xBA };
+const int magicInt = 0xBAD4D4BA;
+
+#define QSXE_KEYFILE "keyfile"
+
+/*
+ Header in above format, less the magic bytes.
+ Useful for reading off the socket
+*/
+struct AuthHeader
+{
+ unsigned char len;
+ unsigned char pad;
+ unsigned char digest[QSXE_KEY_LEN];
+ unsigned char id;
+ unsigned char seq;
+};
+
+/*
+ Header in a form suitable for authentication routines
+*/
+struct AuthMessage
+{
+ AuthMessage()
+ {
+ ::memset( authData, 0, sizeof(authData) );
+ ::memcpy( pad_magic, magic, QSXE_MAGIC_BYTES );
+ }
+ unsigned char pad_magic[QSXE_MAGIC_BYTES];
+ union {
+ AuthHeader hdr;
+ char authData[sizeof(AuthHeader)];
+ };
+ char payLoad[AMOUNT_TO_AUTHENTICATE];
+};
+
+/**
+ Auth data as stored in _key
+*/
+struct AuthCookie
+{
+ unsigned char key[QSXE_KEY_LEN];
+ unsigned char pad;
+ unsigned char progId;
+};
+
+/*
+ Auth data as written to the key file - SUPERSEDED by usr_key_entry
+
+ This is still used internally for some functions, ie the socket
+ related calls.
+*/
+struct AuthRecord
+{
+ union {
+ AuthCookie auth;
+ char data[sizeof(struct AuthCookie)];
+ };
+ time_t change_time;
+};
+
+/*!
+ \class usr_key_entry
+ This comes from the SXE kernel patch file include/linux/lidsif.h
+
+ This is the (new) data record for the key file (version 2).
+
+ The key file is (now) either /proc/lids/keys (and the per-process
+ keys in /proc/<pid>/lids_key) OR for desktop/development ONLY (not
+ for production) it is $QPEDIR/etc/keyfile
+
+ The key file maps keys to files.
+
+ File are identified by inode and device numbers, not paths.
+
+ (See the "installs" file for path to inode/device mapping)
+*/
+struct usr_key_entry
+{
+ char key[QSXE_KEY_LEN];
+ ino_t ino;
+ dev_t dev;
+};
+
+
+/*!
+ \class IdBlock
+ \brief Data record for the manifest file.
+ The manifest file maps program id's to files
+*/
+struct IdBlock
+{
+ quint64 inode;
+ quint64 device;
+ unsigned char pad;
+ unsigned char progId;
+ unsigned short installId;
+ unsigned int keyOffset;
+ qint64 install_time;
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QTRANSPORTAUTHDEFS_QWS_H
+
diff --git a/src/gui/embedded/qunixsocket.cpp b/src/gui/embedded/qunixsocket.cpp
new file mode 100644
index 0000000000..16f2caecb8
--- /dev/null
+++ b/src/gui/embedded/qunixsocket.cpp
@@ -0,0 +1,1794 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qunixsocket_p.h"
+
+// #define QUNIXSOCKET_DEBUG 1
+
+#include <QtCore/qsocketnotifier.h>
+#include <QtCore/qqueue.h>
+#include <QtCore/qdatetime.h>
+
+#ifdef QUNIXSOCKET_DEBUG
+#include <QtCore/qdebug.h>
+#endif
+
+extern "C" {
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+};
+
+#define UNIX_PATH_MAX 108 // From unix(7)
+
+#ifdef QT_LINUXBASE
+// LSB doesn't declare ucred
+struct ucred
+{
+ pid_t pid; /* PID of sending process. */
+ uid_t uid; /* UID of sending process. */
+ gid_t gid; /* GID of sending process. */
+};
+
+// LSB doesn't define the ones below
+#ifndef SO_PASSCRED
+# define SO_PASSCRED 16
+#endif
+#ifndef SCM_CREDENTIALS
+# define SCM_CREDENTIALS 0x02
+#endif
+#ifndef MSG_DONTWAIT
+# define MSG_DONTWAIT 0x40
+#endif
+#ifndef MSG_NOSIGNAL
+# define MSG_NOSIGNAL 0x4000
+#endif
+
+#endif // QT_LINUXBASE
+
+QT_BEGIN_NAMESPACE
+
+///////////////////////////////////////////////////////////////////////////////
+// class QUnixSocketRights
+///////////////////////////////////////////////////////////////////////////////
+/*!
+ \class QUnixSocketRights
+ \internal
+
+ \brief The QUnixSocketRights class encapsulates QUnixSocket rights data.
+ \omit
+ \ingroup Platform::DeviceSpecific
+ \ingroup Platform::OS
+ \ingroup Platform::Communications
+ \endomit
+ \ingroup qws
+
+ \l QUnixSocket allows you to transfer Unix file descriptors between processes.
+ A file descriptor is referred to as "rights data" as it allows one process to
+ transfer its right to access a resource to another.
+
+ The Unix system verifies resource permissions only when the resource is first
+ opened. For example, consider a file on disk readable only by the user "qt".
+ A process running as user "qt" will be able to open this file for reading.
+ If, while the process was still reading from the file, the ownership was
+ changed from user "qt" to user "root", the process would be allowed to
+ continue reading from the file, even though attempting to reopen the file
+ would be denied. Permissions are associated with special descriptors called
+ file descriptors which are returned to a process after it initially opens a
+ resource.
+
+ File descriptors can be duplicated within a process through the dup(2) system
+ call. File descriptors can be passed between processes using the
+ \l QUnixSocket class in the same way. Even though the receiving process never
+ opened the resource directly, it has the same permissions to access it as the
+ process that did.
+
+ \sa QUnixSocket
+ */
+struct QUnixSocketRightsPrivate : public QSharedData
+{
+ virtual ~QUnixSocketRightsPrivate() {
+#ifdef QUNIXSOCKET_DEBUG
+ int closerv =
+#endif
+ ::close(fd);
+#ifdef QUNIXSOCKET_DEBUG
+ if(0 != closerv) {
+ qDebug() << "QUnixSocketRightsPrivate: Unable to close managed"
+ " file descriptor (" << ::strerror(errno) << ")";
+ }
+#endif
+ }
+
+ int fd;
+};
+
+/*!
+ Create a new QUnixSocketRights instance containing the file descriptor \a fd.
+ \a fd will be dup(2)'d internally, so the application is free to close \a fd
+ following this call.
+
+ If the dup(2) fails, or you pass an invalid \a fd, an
+ \l {QUnixSocketRights::isValid()}{invalid } object will be
+ constructed.
+
+ QUnixSocketRights instances are immutable and the internal file descriptor
+ will be shared between any copies made of this object. The system will
+ close(2) the file descriptor once it is no longer needed.
+ */
+QUnixSocketRights::QUnixSocketRights(int fd)
+{
+ d = new QUnixSocketRightsPrivate();
+ if(-1 == fd) {
+ d->fd = -1;
+ } else {
+ d->fd = ::dup(fd);
+#ifdef QUNIXSOCKET_DEBUG
+ if(-1 == d->fd) {
+ qDebug() << "QUnixSocketRights: Unable to duplicate fd "
+ << fd << " (" << ::strerror(errno) << ")";
+ }
+#endif
+ }
+}
+
+/*!
+ \internal
+
+ Construct a QUnixSocketRights instance on \a fd without dup(2)'ing the file
+ descriptor.
+ */
+QUnixSocketRights::QUnixSocketRights(int fd,int)
+{
+ Q_ASSERT(-1 != fd);
+ d = new QUnixSocketRightsPrivate();
+ d->fd = fd;
+}
+
+/*!
+ Destroys the QUnixSocketRights instance.
+ */
+QUnixSocketRights::~QUnixSocketRights()
+{
+}
+
+/*!
+ Create a copy of \a other.
+ */
+QUnixSocketRights &
+QUnixSocketRights::operator=(const QUnixSocketRights & other)
+{
+ d = other.d;
+ return *this;
+}
+
+/*!
+ Create a copy of \a other.
+ */
+QUnixSocketRights::QUnixSocketRights(const QUnixSocketRights & other)
+: d(other.d)
+{
+}
+
+/*!
+ Returns true if this QUnixSocketRights instance is managing a valid file
+ descriptor. This method is equivalent to (-1 != peekFd()).
+
+ \sa QUnixSocketRights::peekFd()
+ */
+bool QUnixSocketRights::isValid() const
+{
+ return d->fd != -1;
+}
+
+/*!
+ Return a duplicate of the file descriptor contained in this object. If this
+ is an \l {QUnixSocketRights::isValid()}{invalid } object, or the
+ dup(2) call fails, an invalid file descriptor (-1) will be returned.
+
+ \sa QUnixSocketRights::peekFd()
+ */
+int QUnixSocketRights::dupFd() const
+{
+ if(-1 == d->fd) return -1;
+
+ int rv = ::dup(d->fd);
+
+#ifdef QUNIXSOCKET_DEBUG
+ if(-1 == rv)
+ qDebug() << "QUnixSocketRights: Unable to duplicate managed file "
+ "descriptor (" << ::strerror(errno) << ")";
+#endif
+
+ return rv;
+}
+
+/*!
+ Returns the file descriptor contained in this object. If this
+ is an \l {QUnixSocketRights::isValid()}{invalid } object an invalid
+ file descriptor (-1) will be returned.
+
+ The lifetime of this file descriptor is tied to the lifetime of the
+ QUnixSocketRights instance. The file descriptor returned by this method
+ \e may be close(2)'d when the QUnixSocketRights instance is destroyed. If
+ you want to continue to use the file descriptor use
+ \l QUnixSocketRights::dupFd() instead.
+
+ \sa QUnixSocketRights::dupFd()
+ */
+int QUnixSocketRights::peekFd() const
+{
+ return d->fd;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// class QUnixSocketMessage
+///////////////////////////////////////////////////////////////////////////////
+struct QUnixSocketMessagePrivate : public QSharedData
+{
+ QUnixSocketMessagePrivate()
+ : state(Default), vec(0), iovecLen(0), dataSize(0) {}
+ QUnixSocketMessagePrivate(const QByteArray & b)
+ : bytes(b), state(Default), vec(0), iovecLen(0), dataSize(0) {}
+ QUnixSocketMessagePrivate(const QByteArray & b,
+ const QList<QUnixSocketRights> & r)
+ : bytes(b), rights(r), state(Default), vec(0), iovecLen(0), dataSize(0) {}
+
+ int size() const { return vec ? dataSize : bytes.size(); }
+ void removeBytes( unsigned int );
+
+ QByteArray bytes;
+ QList<QUnixSocketRights> rights;
+
+ enum AncillaryDataState {
+ Default = 0x00,
+ Truncated = 0x01,
+ Credential = 0x02
+ };
+ AncillaryDataState state;
+
+ pid_t pid;
+ gid_t gid;
+ uid_t uid;
+
+ ::iovec *vec;
+ int iovecLen; // number of vectors in array
+ int dataSize; // total size of vectors = payload
+};
+
+/*!
+ \internal
+ Remove \a bytesToDequeue bytes from the front of this message
+*/
+void QUnixSocketMessagePrivate::removeBytes( unsigned int bytesToDequeue )
+{
+ if ( vec )
+ {
+ ::iovec *vecPtr = vec;
+ if ( bytesToDequeue > (unsigned int)dataSize ) bytesToDequeue = dataSize;
+ while ( bytesToDequeue > 0 && iovecLen > 0 )
+ {
+ if ( vecPtr->iov_len > bytesToDequeue )
+ {
+ // dequeue the bytes by taking them off the front of the
+ // current vector. since we don't own the iovec, its okay
+ // to "leak" this away by pointing past it
+ char **base = reinterpret_cast<char**>(&(vecPtr->iov_base));
+ *base += bytesToDequeue;
+ vecPtr->iov_len -= bytesToDequeue;
+ bytesToDequeue = 0;
+ }
+ else
+ {
+ // dequeue bytes by skipping a whole vector. again, its ok
+ // to lose the pointers to this data
+ bytesToDequeue -= vecPtr->iov_len;
+ iovecLen--;
+ vecPtr++;
+ }
+ }
+ dataSize -= bytesToDequeue;
+ if ( iovecLen == 0 ) vec = 0;
+ }
+ else
+ {
+ bytes.remove(0, bytesToDequeue );
+ }
+}
+
+
+/*!
+ \class QUnixSocketMessage
+ \internal
+
+ \brief The QUnixSocketMessage class encapsulates a message sent or received
+ through the QUnixSocket class.
+ \omit
+ \ingroup Platform::DeviceSpecific
+ \ingroup Platform::OS
+ \ingroup Platform::Communications
+ \endomit
+ \ingroup qws
+
+ In addition to transmitting regular byte stream data, messages sent over Unix
+ domain sockets may have special ancillary properties. QUnixSocketMessage
+ instances allow programmers to retrieve and control these properties.
+
+ Every QUnixSocketMessage sent has an associated set of credentials. A
+ message's credentials consist of the process id, the user id and the group id
+ of the sending process. Normally these credentials are set automatically for
+ you by the QUnixSocketMessage class and can be queried by the receiving
+ process using the \l QUnixSocketMessage::processId(),
+ \l QUnixSocketMessage::userId() and \l QUnixSocketMessage::groupId() methods
+ respectively.
+
+ Advanced applications may wish to change the credentials that their message
+ is sent with, and may do so though the \l QUnixSocketMessage::setProcessId(),
+ \l QUnixSocketMessage::setUserId() and \l QUnixSocketMessage::setGroupId()
+ methods. The validity of these credentials is verified by the system kernel.
+ Only the root user can send messages with credentials that are not his own.
+ Sending of the message will fail for any non-root user who attempts to
+ fabricate credentials. Note that this failure is enforced by the system
+ kernel - receivers can trust the accuracy of credential data!
+
+ Unix domain socket messages may also be used to transmit Unix file descriptors
+ between processes. In this context, file descriptors are known as rights data
+ and are encapsulated by the \l QUnixSocketRights class. Senders can set the
+ file descriptors to transmit using the \l QUnixSocketMessage::setRights() and
+ receivers can retrieve this data through a call to
+ \l QUnixSocketMessage::rights(). \l QUnixSocket and \l QUnixSocketRights
+ discuss the specific copy and ordering semantic associated with rights data.
+
+ QUnixSocketMessage messages are sent by the \l QUnixSocket::write() method.
+ Like any normal network message, attempting to transmit an empty
+ QUnixSocketMessage will succeed, but result in a no-op. Limitations in the
+ Unix domain protocol semantic will cause a transmission of a
+ QUnixSocketMessage with rights data, but no byte data portion, to fail.
+
+ \sa QUnixSocket QUnixSocketRights
+ */
+
+/*!
+ Construct an empty QUnixSocketMessage. This instance will have not data and
+ no rights information. The message's credentials will be set to the
+ application's default credentials.
+ */
+QUnixSocketMessage::QUnixSocketMessage()
+: d(new QUnixSocketMessagePrivate())
+{
+}
+
+/*!
+ Construct a QUnixSocketMessage with an initial data payload of \a bytes. The
+ message's credentials will be set to the application's default credentials.
+ */
+QUnixSocketMessage::QUnixSocketMessage(const QByteArray & bytes)
+: d(new QUnixSocketMessagePrivate(bytes))
+{
+}
+
+/*!
+ Construct a QUnixSocketMessage with an initial data payload of \a bytes and
+ an initial rights payload of \a rights. The message's credentials will be set
+ to the application's default credentials.
+
+ A message with rights data but an empty data payload cannot be transmitted
+ by the system.
+ */
+QUnixSocketMessage::QUnixSocketMessage(const QByteArray & bytes,
+ const QList<QUnixSocketRights> & rights)
+: d(new QUnixSocketMessagePrivate(bytes, rights))
+{
+}
+
+/*!
+ Create a copy of \a other.
+ */
+QUnixSocketMessage::QUnixSocketMessage(const QUnixSocketMessage & other)
+: d(other.d)
+{
+}
+
+/*!
+ \fn QUnixSocketMessage::QUnixSocketMessage(const iovec* data, int vecLen)
+
+ Construct a QUnixSocketMessage with an initial data payload of \a
+ data which points to an array of \a vecLen iovec structures. The
+ message's credentials will be set to the application's default
+ credentials.
+
+ This method can be used to avoid the overhead of copying buffers of data
+ and will directly send the data pointed to by \a data on the socket. It also
+ avoids the syscall overhead of making a number of small socket write calls,
+ if a number of data items can be delivered with one write.
+
+ Caller must ensure the iovec * \a data remains valid until the message
+ is flushed. Caller retains ownership of the iovec structs.
+ */
+QUnixSocketMessage::QUnixSocketMessage(const ::iovec* data, int vecLen )
+: d(new QUnixSocketMessagePrivate())
+{
+ for ( int v = 0; v < vecLen; v++ )
+ d->dataSize += data[v].iov_len;
+ d->vec = const_cast<iovec*>(data);
+ d->iovecLen = vecLen;
+}
+
+/*!
+ Assign the contents of \a other to this object.
+ */
+QUnixSocketMessage & QUnixSocketMessage::operator=(const QUnixSocketMessage & other)
+{
+ d = other.d;
+ return *this;
+}
+
+/*!
+ Destroy this instance.
+ */
+QUnixSocketMessage::~QUnixSocketMessage()
+{
+}
+
+/*!
+ Set the data portion of the message to \a bytes.
+
+ \sa QUnixSocketMessage::bytes()
+ */
+void QUnixSocketMessage::setBytes(const QByteArray & bytes)
+{
+ d.detach();
+ d->bytes = bytes;
+}
+
+/*!
+ Set the rights portion of the message to \a rights.
+
+ A message with rights data but an empty byte data payload cannot be
+ transmitted by the system.
+
+ \sa QUnixSocketMessage::rights()
+ */
+void QUnixSocketMessage::setRights(const QList<QUnixSocketRights> & rights)
+{
+ d.detach();
+ d->rights = rights;
+}
+
+/*!
+ Return the rights portion of the message.
+
+ \sa QUnixSocketMessage::setRights()
+ */
+const QList<QUnixSocketRights> & QUnixSocketMessage::rights() const
+{
+ return d->rights;
+}
+
+/*!
+ Returns true if the rights portion of the message was truncated on reception
+ due to insufficient buffer size. The rights buffer size can be adjusted
+ through calls to the \l QUnixSocket::setRightsBufferSize() method.
+ \l QUnixSocket contains a discussion of the buffering and truncation
+ characteristics of the Unix domain protocol.
+
+ \sa QUnixSocket QUnixSocket::setRightsBufferSize()
+ */
+bool QUnixSocketMessage::rightsWereTruncated() const
+{
+ return d->state & QUnixSocketMessagePrivate::Truncated;
+}
+
+/*!
+ Return the data portion of the message.
+
+ \sa QUnixSocketMessage::setBytes()
+ */
+const QByteArray & QUnixSocketMessage::bytes() const
+{
+ return d->bytes;
+}
+
+/*!
+ Returns the process id credential associated with this message.
+
+ \sa QUnixSocketMessage::setProcessId()
+ */
+pid_t QUnixSocketMessage::processId() const
+{
+ if(QUnixSocketMessagePrivate::Credential & d->state)
+ return d->pid;
+ else
+ return ::getpid();
+}
+
+/*!
+ Returns the user id credential associated with this message.
+
+ \sa QUnixSocketMessage::setUserId()
+ */
+uid_t QUnixSocketMessage::userId() const
+{
+ if(QUnixSocketMessagePrivate::Credential & d->state)
+ return d->uid;
+ else
+ return ::geteuid();
+}
+
+/*!
+ Returns the group id credential associated with this message.
+
+ \sa QUnixSocketMessage::setGroupId()
+ */
+gid_t QUnixSocketMessage::groupId() const
+{
+ if(QUnixSocketMessagePrivate::Credential & d->state)
+ return d->gid;
+ else
+ return ::getegid();
+}
+
+/*!
+ Set the process id credential associated with this message to \a pid. Unless
+ you are the root user, setting a fraudulant credential will cause this message
+ to fail.
+
+ \sa QUnixSocketMessage::processId()
+ */
+void QUnixSocketMessage::setProcessId(pid_t pid)
+{
+ if(!(d->state & QUnixSocketMessagePrivate::Credential)) {
+ d->state = (QUnixSocketMessagePrivate::AncillaryDataState)( d->state | QUnixSocketMessagePrivate::Credential );
+ d->uid = ::geteuid();
+ d->gid = ::getegid();
+ }
+ d->pid = pid;
+}
+
+/*!
+ Set the user id credential associated with this message to \a uid. Unless
+ you are the root user, setting a fraudulant credential will cause this message
+ to fail.
+
+ \sa QUnixSocketMessage::userId()
+ */
+void QUnixSocketMessage::setUserId(uid_t uid)
+{
+ if(!(d->state & QUnixSocketMessagePrivate::Credential)) {
+ d->state = (QUnixSocketMessagePrivate::AncillaryDataState)( d->state | QUnixSocketMessagePrivate::Credential );
+ d->pid = ::getpid();
+ d->gid = ::getegid();
+ }
+ d->uid = uid;
+}
+
+/*!
+ Set the group id credential associated with this message to \a gid. Unless
+ you are the root user, setting a fraudulant credential will cause this message
+ to fail.
+
+ \sa QUnixSocketMessage::groupId()
+ */
+void QUnixSocketMessage::setGroupId(gid_t gid)
+{
+ if(!(d->state & QUnixSocketMessagePrivate::Credential)) {
+ d->state = (QUnixSocketMessagePrivate::AncillaryDataState)( d->state | QUnixSocketMessagePrivate::Credential );
+ d->pid = ::getpid();
+ d->uid = ::geteuid();
+ }
+ d->gid = gid;
+}
+
+/*!
+ Return true if this message is valid. A message with rights data but an empty
+ byte data payload cannot be transmitted by the system and is marked as
+ invalid.
+ */
+bool QUnixSocketMessage::isValid() const
+{
+ return d->rights.isEmpty() || !d->bytes.isEmpty();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// class QUnixSocket
+///////////////////////////////////////////////////////////////////////////////
+#define QUNIXSOCKET_DEFAULT_READBUFFER 1024
+#define QUNIXSOCKET_DEFAULT_ANCILLARYBUFFER 0
+
+/*!
+ \class QUnixSocket
+ \internal
+
+ \brief The QUnixSocket class provides a Unix domain socket.
+
+ \omit
+ \ingroup Platform::DeviceSpecific
+ \ingroup Platform::OS
+ \ingroup Platform::Communications
+ \endomit
+ \ingroup qws
+
+ Unix domain sockets provide an efficient mechanism for communications between
+ Unix processes on the same machine. Unix domain sockets support a reliable,
+ stream-oriented, connection-oriented transport protocol, much like TCP
+ sockets. Unlike IP based sockets, the connection endpoint of a Unix domain
+ socket is a file on disk of type socket.
+
+ In addition to transporting raw data bytes, Unix domain sockets are able to
+ transmit special ancillary data. The two types of ancillary data supported
+ by the QUnixSocket class are:
+
+ \list
+ \o Credential Data - Allows a receiver
+ to reliably identify the process sending each message.
+ \o \l {QUnixSocketRights}{Rights Data } - Allows Unix file descriptors
+ to be transmitted between processes.
+ \endlist
+
+ Because of the need to support ancillary data, QUnixSocket is not a QIODevice,
+ like QTcpSocket and QUdpSocket. Instead, QUnixSocket contains a number of
+ read and write methods that clients must invoke directly. Rather than
+ returning raw data bytes, \l QUnixSocket::read() returns \l QUnixSocketMessage
+ instances that encapsulate the message's byte data and any other ancillary
+ data.
+
+ Ancillary data is transmitted "out of band". Every \l QUnixSocketMessage
+ received will have credential data associated with it that the client can
+ access through calls to \l QUnixSocketMessage::processId(),
+ \l QUnixSocketMessage::groupId() and \l QUnixSocketMessage::userId().
+ Likewise, message creators can set the credential data to send through calls
+ to \l QUnixSocketMessage::setProcessId(), \l QUnixSocketMessage::setGroupId()
+ and \l QUnixSocketMessage::setUserId() respectively. The authenticity of the
+ credential values is verified by the system kernel and cannot be fabricated
+ by unprivileged processes. Only processes running as the root user can
+ specify credential data that does not match the sending process.
+
+ Unix file descriptors, known as "rights data", transmitted between processes
+ appear as though they had been dup(2)'d between the two. As Unix
+ domain sockets present a continuous stream of bytes to the receiver, the
+ rights data - which is transmitted out of band - must be "slotted" in at some
+ point. The rights data is logically associated with the first byte - called
+ the anchor byte - of the \l QUnixSocketMessage to which they are attached.
+ Received rights data will be available from the
+ \l QUnixSocketMessage::rights() method for the \l QUnixSocketMessage
+ instance that contains the anchor byte.
+
+ In addition to a \l QUnixSocket::write() that takes a \l QUnixSocketMessage
+ instance - allowing a client to transmit both byte and rights data - a
+ number of convenience overloads are provided for use when only transmitting
+ simple byte data. Unix requires that at least one byte of raw data be
+ transmitted in order to send rights data. A \l QUnixSocketMessage instance
+ with rights data, but no byte data, cannot be transmitted.
+
+ Unix sockets present a stream interface, such that, for example, a single
+ six byte transmission might be received as two three byte messages. Rights
+ data, on the other hand, is conceptually transmitted as unfragmentable
+ datagrams. If the receiving buffer is not large enough to contain all the
+ transmitted rights information, the data is truncated and irretreivably lost.
+ Users should use the \l QUnixSocket::setRightsBufferSize() method to control
+ the buffer size used for this data, and develop protocols that avoid the
+ problem. If the buffer size is too small and rights data is truncated,
+ the \l QUnixSocketMessage::rightsWereTruncated() flag will be set.
+
+ \sa QUnixSocketMessage QUnixSocketRights
+*/
+
+/*!
+ \enum QUnixSocket::SocketError
+
+ The SocketError enumeration represents the various errors that can occur on
+ a Unix domain socket. The most recent error for the socket is available
+ through the \l QUnixSocket::error() method.
+
+ \value NoError No error has occurred.
+ \value InvalidPath An invalid path endpoint was passed to
+ \l QUnixSocket::connect(). As defined by unix(7), invalid paths
+ include an empty path, or what more than 107 characters long.
+ \value ResourceError An error acquiring or manipulating the system's socket
+ resources occurred. For example, if the process runs out of available
+ socket descriptors, a ResourceError will occur.
+ \value NonexistentPath The endpoing passed to \l QUnixSocket::connect() does
+ not refer to a Unix domain socket entity on disk.
+ \value ConnectionRefused The connection to the specified endpoint was refused.
+ Generally this means that there is no server listening on that
+ endpoint.
+ \value UnknownError An unknown error has occurred.
+ \value ReadFailure An error occurred while reading bytes from the connection.
+ \value WriteFailure An error occurred while writing bytes into the connection.
+ */
+
+/*!
+ \enum QUnixSocket::SocketState
+
+ The SocketState enumeration represents the connection state of a QUnixSocket
+ instance.
+
+ \value UnconnectedState The connection is not established.
+ \value ConnectedState The connection is established.
+ \value ClosingState The connection is being closed, following a call to
+ \l QUnixSocket::close(). While closing, any pending data will be
+ transmitted, but further writes by the application will be refused.
+ */
+
+/*
+ \fn QUnixSocket::bytesWritten(qint64 bytes)
+
+ This signal is emitted every time a payload of data has been written to the
+ connection. The \a bytes argument is set to the number of bytes that were
+ written in this payload.
+
+ \sa QUnixSocket::readyRead()
+*/
+
+/*
+ \fn QUnixSocket::readyRead()
+
+ This signal is emitted once every time new data is available for reading from
+ the connection. It will only be emitted again once new data is available.
+
+ \sa QUnixSocket::bytesWritten()
+*/
+
+/*!
+ \fn QUnixSocket::stateChanged(SocketState socketState)
+
+ This signal is emitted each time the socket changes connection state.
+ \a socketState will be set to the socket's new state.
+*/
+
+class QUnixSocketPrivate : public QObject {
+Q_OBJECT
+public:
+ QUnixSocketPrivate(QUnixSocket * _me)
+ : me(_me), fd(-1), readNotifier(0), writeNotifier(0),
+ state(QUnixSocket::UnconnectedState), error(QUnixSocket::NoError),
+ writeQueueBytes(0), messageValid(false), dataBuffer(0),
+ dataBufferLength(0), dataBufferCapacity(0), ancillaryBuffer(0),
+ ancillaryBufferCount(0), closingTimer(0) {
+ QObject::connect(this, SIGNAL(readyRead()), me, SIGNAL(readyRead()));
+ QObject::connect(this, SIGNAL(bytesWritten(qint64)),
+ me, SIGNAL(bytesWritten(qint64)));
+ }
+ ~QUnixSocketPrivate()
+ {
+ if(dataBuffer)
+ delete [] dataBuffer;
+ if(ancillaryBuffer)
+ delete [] ancillaryBuffer;
+ }
+
+ enum { CausedAbort = 0x70000000 };
+
+ QUnixSocket * me;
+
+ int fd;
+
+ QSocketNotifier * readNotifier;
+ QSocketNotifier * writeNotifier;
+
+ QUnixSocket::SocketState state;
+ QUnixSocket::SocketError error;
+
+ QQueue<QUnixSocketMessage> writeQueue;
+ unsigned int writeQueueBytes;
+
+ bool messageValid;
+ ::msghdr message;
+ inline void flushAncillary()
+ {
+ if(!messageValid) return;
+ ::cmsghdr * h = (::cmsghdr *)CMSG_FIRSTHDR(&(message));
+ while(h) {
+
+ if(SCM_RIGHTS == h->cmsg_type) {
+ int * fds = (int *)CMSG_DATA(h);
+ int numFds = (h->cmsg_len - CMSG_LEN(0)) / sizeof(int);
+
+ for(int ii = 0; ii < numFds; ++ii)
+ ::close(fds[ii]);
+ }
+
+ h = (::cmsghdr *)CMSG_NXTHDR(&(message), h);
+ }
+
+ messageValid = false;
+ }
+
+
+ char * dataBuffer;
+ unsigned int dataBufferLength;
+ unsigned int dataBufferCapacity;
+
+ char * ancillaryBuffer;
+ inline unsigned int ancillaryBufferCapacity()
+ {
+ return CMSG_SPACE(sizeof(::ucred)) + CMSG_SPACE(sizeof(int) * ancillaryBufferCount);
+ }
+ unsigned int ancillaryBufferCount;
+
+ QByteArray address;
+
+ int closingTimer;
+
+ virtual void timerEvent(QTimerEvent *)
+ {
+ me->abort();
+ killTimer(closingTimer);
+ closingTimer = 0;
+ }
+signals:
+ void readyRead();
+ void bytesWritten(qint64);
+
+public slots:
+ void readActivated();
+ qint64 writeActivated();
+};
+
+/*!
+ Construct a QUnixSocket instance, with \a parent.
+
+ The read buffer is initially set to 1024 bytes, and the rights buffer to 0
+ entries.
+
+ \sa QUnixSocket::readBufferSize() QUnixSocket::rightsBufferSize()
+ */
+QUnixSocket::QUnixSocket(QObject * parent)
+: QIODevice(parent), d(new QUnixSocketPrivate(this))
+{
+ setOpenMode(QIODevice::NotOpen);
+ setReadBufferSize(QUNIXSOCKET_DEFAULT_READBUFFER);
+ setRightsBufferSize(QUNIXSOCKET_DEFAULT_ANCILLARYBUFFER);
+}
+
+/*!
+ Construct a QUnixSocket instance, with \a parent.
+
+ The read buffer is initially set to \a readBufferSize bytes, and the rights
+ buffer to \a rightsBufferSize entries.
+
+ \sa QUnixSocket::readBufferSize() QUnixSocket::rightsBufferSize()
+ */
+QUnixSocket::QUnixSocket(qint64 readBufferSize, qint64 rightsBufferSize,
+ QObject * parent)
+: QIODevice(parent), d(new QUnixSocketPrivate(this))
+{
+ Q_ASSERT(readBufferSize > 0 && rightsBufferSize >= 0);
+
+ setOpenMode(QIODevice::NotOpen);
+ setReadBufferSize(readBufferSize);
+ setRightsBufferSize(rightsBufferSize);
+}
+
+/*!
+ Destroys the QUnixSocket instance. Any unsent data is discarded.
+ */
+QUnixSocket::~QUnixSocket()
+{
+ abort();
+ delete d;
+}
+
+/*!
+ Attempt to connect to \a path.
+
+ This method is synchronous and will return true if the connection succeeds and
+ false otherwise. In the case of failure, \l QUnixSocket::error() will be set
+ accordingly.
+
+ Any existing connection will be aborted, and all pending data will be
+ discarded.
+
+ \sa QUnixSocket::close() QUnixSocket::abort() QUnixSocket::error()
+ */
+bool QUnixSocket::connect(const QByteArray & path)
+{
+ int _true;
+ int crv;
+#ifdef QUNIXSOCKET_DEBUG
+ qDebug() << "QUnixSocket: Connect requested to '"
+ << path << "'";
+#endif
+
+ abort(); // Reset any existing connection
+
+ if(UnconnectedState != d->state) // abort() caused a signal and someone messed
+ // with us. We'll assume they know what
+ // they're doing and bail. Alternative is to
+ // have a special "Connecting" state
+ return false;
+
+
+ if(path.isEmpty() || path.size() > UNIX_PATH_MAX) {
+ d->error = InvalidPath;
+ return false;
+ }
+
+ // Create the socket
+ d->fd = ::socket(PF_UNIX, SOCK_STREAM, 0);
+ if(-1 == d->fd) {
+#ifdef QUNIXSOCKET_DEBUG
+ qDebug() << "QUnixSocket: Unable to create socket ("
+ << strerror(errno) << ")";
+#endif
+ d->error = ResourceError;
+ goto connect_error;
+ }
+
+ // Set socket options
+ _true = 1;
+ crv = ::setsockopt(d->fd, SOL_SOCKET, SO_PASSCRED, (void *)&_true,
+ sizeof(int));
+ if(-1 == crv) {
+#ifdef QUNIXSOCKET_DEBUG
+ qDebug() << "QUnixSocket: Unable to configure socket ("
+ << ::strerror(errno) << ")";
+#endif
+ d->error = ResourceError;
+
+ goto connect_error;
+ }
+
+ // Construct our unix address
+ struct ::sockaddr_un addr;
+ addr.sun_family = AF_UNIX;
+ ::memcpy(addr.sun_path, path.data(), path.size());
+ if(path.size() < UNIX_PATH_MAX)
+ addr.sun_path[path.size()] = '\0';
+
+ // Attempt the connect
+ crv = ::connect(d->fd, (sockaddr *)&addr, sizeof(sockaddr_un));
+ if(-1 == crv) {
+#ifdef QUNIXSOCKET_DEBUG
+ qDebug() << "QUnixSocket: Unable to connect ("
+ << ::strerror(errno) << ")";
+#endif
+ if(ECONNREFUSED == errno)
+ d->error = ConnectionRefused;
+ else if(ENOENT == errno)
+ d->error = NonexistentPath;
+ else
+ d->error = UnknownError;
+
+ goto connect_error;
+ }
+
+ // We're connected!
+ d->address = path;
+ d->state = ConnectedState;
+ d->readNotifier = new QSocketNotifier(d->fd, QSocketNotifier::Read, d);
+ d->writeNotifier = new QSocketNotifier(d->fd, QSocketNotifier::Write, d);
+ QObject::connect(d->readNotifier, SIGNAL(activated(int)),
+ d, SLOT(readActivated()));
+ QObject::connect(d->writeNotifier, SIGNAL(activated(int)),
+ d, SLOT(writeActivated()));
+ d->readNotifier->setEnabled(true);
+ d->writeNotifier->setEnabled(false);
+ setOpenMode(QIODevice::ReadWrite);
+ emit stateChanged(ConnectedState);
+
+#ifdef QUNIXSOCKET_DEBUG
+ qDebug() << "QUnixSocket: Connected to " << path;
+#endif
+ return true;
+
+connect_error: // Cleanup failed connection
+ if(-1 != d->fd) {
+#ifdef QUNIXSOCKET_DEBUG
+ int closerv =
+#endif
+ ::close(d->fd);
+#ifdef QUNIXSOCKET_DEBUG
+ if(0 != closerv) {
+ qDebug() << "QUnixSocket: Unable to close file descriptor after "
+ "failed connect (" << ::strerror(errno) << ")";
+ }
+#endif
+ }
+ d->fd = -1;
+ return false;
+}
+
+/*!
+ Sets the socket descriptor to use to \a socketDescriptor, bypassing
+ QUnixSocket's connection infrastructure, and return true on success and false
+ on failure. \a socketDescriptor must be in the connected state, and must be
+ a Unix domain socket descriptor. Following a successful call to this method,
+ the QUnixSocket instance will be in the Connected state and will have assumed
+ ownership of \a socketDescriptor.
+
+ Any existing connection will be aborted, and all pending data will be
+ discarded.
+
+ \sa QUnixSocket::connect()
+*/
+bool QUnixSocket::setSocketDescriptor(int socketDescriptor)
+{
+ abort();
+
+ if(UnconnectedState != state()) // See QUnixSocket::connect()
+ return false;
+
+ // Attempt to set the socket options
+ if(-1 == socketDescriptor) {
+#ifdef QUNIXSOCKET_DEBUG
+ qDebug() << "QUnixSocket: User provided socket is invalid";
+#endif
+ d->error = ResourceError;
+ return false;
+ }
+
+ // Set socket options
+ int _true = 1;
+ int crv = ::setsockopt(socketDescriptor, SOL_SOCKET,
+ SO_PASSCRED, (void *)&_true, sizeof(int));
+ if(-1 == crv) {
+#ifdef QUNIXSOCKET_DEBUG
+ qDebug() << "QUnixSocket: Unable to configure client provided socket ("
+ << ::strerror(errno) << ")";
+#endif
+ d->error = ResourceError;
+
+ return false;
+ }
+
+ d->fd = socketDescriptor;
+ d->state = ConnectedState;
+ d->address = QByteArray();
+ setOpenMode(QIODevice::ReadWrite);
+ d->readNotifier = new QSocketNotifier(d->fd, QSocketNotifier::Read, d);
+ d->writeNotifier = new QSocketNotifier(d->fd, QSocketNotifier::Write, d);
+ QObject::connect(d->readNotifier, SIGNAL(activated(int)),
+ d, SLOT(readActivated()));
+ QObject::connect(d->writeNotifier, SIGNAL(activated(int)),
+ d, SLOT(writeActivated()));
+ d->readNotifier->setEnabled(true);
+ d->writeNotifier->setEnabled(false);
+ emit stateChanged(d->state);
+
+ return true;
+}
+
+/*!
+ Returns the socket descriptor currently in use. This method will return -1
+ if the QUnixSocket instance is in the UnconnectedState \l {QUnixSocket::state()}{state. }
+
+ \sa QUnixSocket::setSocketDescriptor()
+ */
+int QUnixSocket::socketDescriptor() const
+{
+ return d->fd;
+}
+
+/*!
+ Abort the connection. This will immediately disconnect (if connected) and
+ discard any pending data. Following a call to QUnixSocket::abort() the
+ object will always be in the disconnected \link QUnixSocket::state() state.
+ \endlink
+
+ \sa QUnixSocket::close()
+*/
+void QUnixSocket::abort()
+{
+ setOpenMode(QIODevice::NotOpen);
+
+ // We want to be able to use QUnixSocket::abort() to cleanup our state but
+ // also preserve the error message that caused the abort. It is not
+ // possible to reorder code to do this:
+ // abort();
+ // d->error = SomeError
+ // as QUnixSocket::abort() might emit a signal and we need the error to be
+ // set within that signal. So, if we want an error message to be preserved
+ // across a *single* call to abort(), we set the
+ // QUnixSocketPrivate::CausedAbort flag in the error.
+ if(d->error & QUnixSocketPrivate::CausedAbort)
+ d->error = (QUnixSocket::SocketError)(d->error &
+ ~QUnixSocketPrivate::CausedAbort);
+ else
+ d->error = NoError;
+
+ if( UnconnectedState == d->state) return;
+
+#ifdef QUNIXSOCKET_DEBUG
+ int closerv =
+#endif
+ ::close(d->fd);
+#ifdef QUNIXSOCKET_DEBUG
+ if(0 != closerv) {
+ qDebug() << "QUnixSocket: Unable to close socket during abort ("
+ << strerror(errno) << ")";
+ }
+#endif
+
+ // Reset variables
+ d->fd = -1;
+ d->state = UnconnectedState;
+ d->dataBufferLength = 0;
+ d->flushAncillary();
+ d->address = QByteArray();
+ if(d->readNotifier) {
+ d->readNotifier->setEnabled(false);
+ d->readNotifier->deleteLater();
+ }
+ if(d->writeNotifier) {
+ d->writeNotifier->setEnabled(false);
+ d->writeNotifier->deleteLater();
+ }
+ d->readNotifier = 0;
+ d->writeNotifier = 0;
+ d->writeQueue.clear();
+ d->writeQueueBytes = 0;
+ if(d->closingTimer) {
+ d->killTimer(d->closingTimer);
+ }
+ d->closingTimer = 0;
+ emit stateChanged(d->state);
+}
+
+/*!
+ Close the connection. The instance will enter the Closing
+ \l {QUnixSocket::state()}{state } until all pending data has been
+ transmitted, at which point it will enter the Unconnected state.
+
+ Even if there is no pending data for transmission, the object will never
+ jump directly to Disconnect without first passing through the
+ Closing state.
+
+ \sa QUnixSocket::abort()
+ */
+void QUnixSocket::close()
+{
+ if(ConnectedState != state()) return;
+
+ d->state = ClosingState;
+ if(d->writeQueue.isEmpty()) {
+ d->closingTimer = d->startTimer(0); // Start a timer to "fake"
+ // completing writes
+ }
+ emit stateChanged(d->state);
+}
+
+/*!
+ This function writes as much as possible from the internal write buffer to
+ the underlying socket, without blocking. If any data was written, this
+ function returns true; otherwise false is returned.
+*/
+// Note! docs partially copied from QAbstractSocket::flush()
+bool QUnixSocket::flush()
+{
+ // This needs to have the same semantics as QAbstractSocket, if it is to
+ // be used interchangeably with that class.
+ if (d->writeQueue.isEmpty())
+ return false;
+
+ d->writeActivated();
+ return true;
+}
+
+/*!
+ Returns the last error to have occurred on this object. This method is not
+ destructive, so multiple calls to QUnixSocket::error() will return the same
+ value. The error is only reset by a call to \l QUnixSocket::connect() or
+ \l QUnixSocket::abort()
+ */
+QUnixSocket::SocketError QUnixSocket::error() const
+{
+ return (QUnixSocket::SocketError)
+ (d->error & ~QUnixSocketPrivate::CausedAbort);
+}
+
+/*!
+ Returns the connection state of this instance.
+ */
+QUnixSocket::SocketState QUnixSocket::state() const
+{
+ return d->state;
+}
+
+/*!
+ Returns the Unix path address passed to \l QUnixSocket::connect(). This
+ method will return an empty path if the object is in the Unconnected
+ \l {QUnixSocket::state()}{state } or was connected through a call
+ to \l QUnixSocket::setSocketDescriptor()
+
+ \sa QUnixSocket::connect() QUnixSocket::setSocketDescriptor()
+ */
+QByteArray QUnixSocket::address() const
+{
+ return d->address;
+}
+
+/*!
+ Returns the number of bytes available for immediate retrieval through a call
+ to \l QUnixSocket::read().
+ */
+qint64 QUnixSocket::bytesAvailable() const
+{
+ return QIODevice::bytesAvailable() + d->dataBufferLength;
+}
+
+/*!
+ Returns the number of enqueued bytes still to be written to the socket.
+ */
+qint64 QUnixSocket::bytesToWrite() const
+{
+ return d->writeQueueBytes;
+}
+
+/*!
+ Returns the size of the read buffer in bytes. The read buffer size
+ determines the amount of byte data that can be read from the socket in one go.
+ The read buffer size caps the maximum value that can be returned by
+ \l QUnixSocket::bytesAvailable() and will always be greater than zero. By
+ default, the read buffer size is 1024 bytes.
+
+ The size of the read buffer is independent of the rights buffer, which can be
+ queried by \l QUnixSocket::rightsBufferSize().
+
+ \sa QUnixSocket::setReadBufferSize()
+ */
+qint64 QUnixSocket::readBufferSize() const
+{
+ return d->dataBufferCapacity;
+}
+
+/*!
+ Sets the \a size of the socket's read buffer in bytes.
+
+ The size of the read buffer is independent of the rights buffer, which can be
+ set by \l QUnixSocket::setRightsBufferSize().
+
+ Attempting to reduce the buffer size while bytes are available for reading
+ (ie. while the buffer is in use) will fail.
+
+ \sa QUnixSocket::readBufferSize()
+ */
+void QUnixSocket::setReadBufferSize(qint64 size)
+{
+ Q_ASSERT(size > 0);
+ if(size == d->dataBufferCapacity || d->dataBufferLength) return;
+ if(d->dataBuffer) delete [] d->dataBuffer;
+ d->dataBuffer = new char[size];
+ d->dataBufferCapacity = size;
+}
+
+/*!
+ Returns the size of the rights buffer in rights entries. The rights buffer
+ size determines the number of rights transferences that can be received in
+ any message. Unlike byte stream data which can be fragmented into many
+ smaller messages if the \link QUnixSocket::readBufferSize() read buffer
+ \endlink is not large enough to contain all the available data, rights data
+ is transmitted as unfragmentable datagrams. If the rights buffer is not
+ large enough to contain this unfragmentable datagram, the datagram will be
+ truncated and rights data irretrievably lost. If truncation occurs, the
+ \l QUnixSocketMessage::rightsWereTruncated() flag will be set. By default
+ the rights buffer size is 0 entries - rights data cannot be received.
+
+ The size of the rights buffer is independent of the read buffer, which can be
+ queried by \l QUnixSocket::readBufferSize().
+
+ \sa QUnixSocket::setRightsBufferSize()
+ */
+qint64 QUnixSocket::rightsBufferSize() const
+{
+ return d->ancillaryBufferCount;
+}
+
+/*!
+ Sets the \a size of the socket's rights buffer in rights entries.
+
+ The size of the rights buffer is independent of the read buffer, which can be
+ set by \l QUnixSocket::setReadBufferSize().
+
+ Attempting to reduce the buffer size while bytes are available for reading
+ (ie. while the buffer is in use) will fail.
+
+ \sa QUnixSocket::rightsBufferSize()
+ */
+void QUnixSocket::setRightsBufferSize(qint64 size)
+{
+ Q_ASSERT(size >= 0);
+
+ if((size == d->ancillaryBufferCount || d->dataBufferLength) &&
+ d->ancillaryBuffer)
+ return;
+
+ qint64 byteSize = CMSG_SPACE(sizeof(::ucred)) +
+ CMSG_SPACE(size * sizeof(int));
+
+ if(d->ancillaryBuffer) delete [] d->ancillaryBuffer;
+ d->ancillaryBuffer = new char[byteSize];
+ d->ancillaryBufferCount = size;
+}
+
+/*!
+ \overload
+
+ Writes \a socketdata to the socket. In addition to failing if the socket
+ is not in the Connected state, writing will fail if \a socketdata is
+ \l {QUnixSocketMessage::isValid()}{invalid. }
+
+ Writes through the QUnixSocket class are asynchronous. Rather than being
+ written immediately, data is enqueued and written once the application
+ reenters the Qt event loop and the socket becomes available for writing.
+ Thus, this method will only fail if the socket is not in the Connected state
+ - it is illegal to attempt a write on a Unconnected or Closing socket.
+
+ Applications can monitor the progress of data writes through the
+ \l QUnixSocket::bytesWritten() signal and \l QUnixSocket::bytesToWrite()
+ method.
+
+ \sa QUnixSocketMessage
+ */
+qint64 QUnixSocket::write(const QUnixSocketMessage & socketdata)
+{
+ if(ConnectedState != state() || !socketdata.isValid()) return -1;
+ if(socketdata.d->size() == 0) return 0;
+
+ d->writeQueue.enqueue(socketdata);
+ d->writeQueueBytes += socketdata.d->size();
+ d->writeNotifier->setEnabled(true);
+
+ return socketdata.d->size();
+}
+
+/*!
+ Return the next available message, or an empty message if none is available.
+
+ To avoid retrieving empty messages, applications should connect to the
+ \l QUnixSocket::readyRead() signal to be notified when new messages are
+ available or periodically poll the \l QUnixSocket::bytesAvailable() method.
+
+ \sa QUnixSocket::readyRead() QUnixSocket::bytesAvailable()
+ */
+QUnixSocketMessage QUnixSocket::read()
+{
+ QUnixSocketMessage data;
+ if(!d->dataBufferLength)
+ return data;
+
+ data.d->state = QUnixSocketMessagePrivate::Credential;
+
+ // Bytes are easy
+ data.setBytes(QByteArray(d->dataBuffer, d->dataBufferLength));
+
+ // Extract ancillary data
+ QList<QUnixSocketRights> a;
+
+ ::cmsghdr * h = (::cmsghdr *)CMSG_FIRSTHDR(&(d->message));
+ while(h) {
+
+ if(SCM_CREDENTIALS == h->cmsg_type) {
+ ::ucred * cred = (::ucred *)CMSG_DATA(h);
+#ifdef QUNIXSOCKET_DEBUG
+ qDebug( "Credentials recd: pid %lu - gid %lu - uid %lu",
+ cred->pid, cred->gid, cred->uid );
+#endif
+ data.d->pid = cred->pid;
+ data.d->gid = cred->gid;
+ data.d->uid = cred->uid;
+
+ } else if(SCM_RIGHTS == h->cmsg_type) {
+
+ int * fds = (int *)CMSG_DATA(h);
+ int numFds = (h->cmsg_len - CMSG_LEN(0)) / sizeof(int);
+
+ for(int ii = 0; ii < numFds; ++ii) {
+ QUnixSocketRights qusr(fds[ii], 0);
+ a.append(qusr);
+ }
+
+ } else {
+
+#ifdef QUNIXSOCKET_DEBUG
+ qFatal("QUnixSocket: Unknown ancillary data type (%d) received.",
+ h->cmsg_type);
+#endif
+
+ }
+
+ h = (::cmsghdr *)CMSG_NXTHDR(&(d->message), h);
+ }
+
+ if(d->message.msg_flags & MSG_CTRUNC) {
+ data.d->state = (QUnixSocketMessagePrivate::AncillaryDataState)(QUnixSocketMessagePrivate::Truncated |
+ QUnixSocketMessagePrivate::Credential );
+ }
+
+ if(!a.isEmpty())
+ data.d->rights = a;
+
+ d->dataBufferLength = 0;
+ d->messageValid = false;
+ d->readNotifier->setEnabled(true);
+
+ return data;
+}
+
+/*! \internal */
+bool QUnixSocket::isSequential() const
+{
+ return true;
+}
+
+/*! \internal */
+bool QUnixSocket::waitForReadyRead(int msecs)
+{
+ if(UnconnectedState == d->state)
+ return false;
+
+ if(d->messageValid) {
+ return true;
+ }
+
+ Q_ASSERT(-1 != d->fd);
+
+ int timeout = msecs;
+ struct timeval tv;
+ struct timeval *ptrTv = 0;
+ QTime stopWatch;
+
+ stopWatch.start();
+
+ do
+ {
+ fd_set readset;
+
+ FD_ZERO(&readset);
+ FD_SET(d->fd, &readset);
+
+ if(-1 != msecs) {
+ tv.tv_sec = timeout / 1000;
+ tv.tv_usec = (timeout % 1000) * 1000;
+ ptrTv = &tv;
+ }
+
+ int rv = ::select(d->fd + 1, &readset, 0, 0, ptrTv);
+ switch(rv) {
+ case 0:
+ // timeout
+ return false;
+ case 1:
+ // ok
+ d->readActivated();
+ return true;
+ default:
+ if (errno != EINTR)
+ abort(); // error
+ break;
+ }
+
+ timeout = msecs - stopWatch.elapsed();
+ }
+ while (timeout > 0);
+
+ return false;
+}
+
+bool QUnixSocket::waitForBytesWritten(int msecs)
+{
+ if(UnconnectedState == d->state)
+ return false;
+
+ Q_ASSERT(-1 != d->fd);
+
+ if ( d->writeQueue.isEmpty() )
+ return true;
+
+ QTime stopWatch;
+ stopWatch.start();
+
+ while ( true )
+ {
+ fd_set fdwrite;
+ FD_ZERO(&fdwrite);
+ FD_SET(d->fd, &fdwrite);
+ int timeout = msecs < 0 ? 0 : msecs - stopWatch.elapsed();
+ struct timeval tv;
+ struct timeval *ptrTv = 0;
+ if ( -1 != msecs )
+ {
+ tv.tv_sec = timeout / 1000;
+ tv.tv_usec = (timeout % 1000) * 1000;
+ ptrTv = &tv;
+ }
+
+ int rv = ::select(d->fd + 1, 0, &fdwrite, 0, ptrTv);
+ switch ( rv )
+ {
+ case 0:
+ // timeout
+ return false;
+ case 1:
+ {
+ // ok to write
+ qint64 bytesWritten = d->writeActivated();
+ if (bytesWritten == 0) {
+ // We need to retry
+ int delay = 1;
+ do {
+ if (-1 != msecs) {
+ timeout = msecs - stopWatch.elapsed();
+ if (timeout <= 0) {
+ // We have exceeded our allotted time
+ return false;
+ } else {
+ if (delay > timeout)
+ delay = timeout;
+ }
+ }
+
+ // Pause before we make another attempt to send
+ ::usleep(delay * 1000);
+ if (delay < 1024)
+ delay *= 2;
+
+ bytesWritten = d->writeActivated();
+ } while (bytesWritten == 0);
+ }
+ return (bytesWritten != -1);
+ }
+ default:
+ // error - or an uncaught signal!!!!!!!!!
+ if ( rv == EINTR )
+ continue;
+ abort();
+ return false;
+ }
+ }
+ return false; // fix warnings
+}
+
+/*! \internal */
+bool QUnixSocket::canReadLine() const
+{
+ for(unsigned int ii = 0; ii < d->dataBufferLength; ++ii)
+ if(d->dataBuffer[ii] == '\n') return true;
+ return false;
+}
+
+/*! \internal */
+qint64 QUnixSocket::readData(char * data, qint64 maxSize)
+{
+ Q_ASSERT(data);
+ if(0 >= maxSize) return 0;
+ if(!d->dataBufferLength) return 0;
+
+ // Read data
+ unsigned int size = d->dataBufferLength>maxSize?maxSize:d->dataBufferLength;
+ memcpy(data, d->dataBuffer, size);
+ if(size == d->dataBufferLength) {
+ d->dataBufferLength = 0;
+ } else {
+ memmove(d->dataBuffer, d->dataBuffer + size, d->dataBufferLength - size);
+ d->dataBufferLength -= size;
+ }
+
+
+ // Flush ancillary
+ d->flushAncillary();
+
+ if(0 == d->dataBufferLength)
+ d->readNotifier->setEnabled(true);
+
+ return size;
+}
+
+/*! \internal */
+qint64 QUnixSocket::writeData (const char * data, qint64 maxSize)
+{
+ return write(QUnixSocketMessage(QByteArray(data, maxSize)));
+}
+
+qint64 QUnixSocketPrivate::writeActivated()
+{
+ writeNotifier->setEnabled(false);
+
+ QUnixSocketMessage & m = writeQueue.head();
+ const QList<QUnixSocketRights> & a = m.rights();
+
+ //
+ // Construct the message
+ //
+ ::iovec vec;
+ if ( !m.d->vec ) // message does not already have an iovec
+ {
+ vec.iov_base = (void *)m.bytes().constData();
+ vec.iov_len = m.bytes().size();
+ }
+
+ // Allocate the control buffer
+ ::msghdr sendmessage;
+ ::bzero(&sendmessage, sizeof(::msghdr));
+ if ( m.d->vec )
+ {
+ sendmessage.msg_iov = m.d->vec;
+ sendmessage.msg_iovlen = m.d->iovecLen;
+ }
+ else
+ {
+ sendmessage.msg_iov = &vec;
+ sendmessage.msg_iovlen = 1;
+ }
+ unsigned int required = CMSG_SPACE(sizeof(::ucred)) +
+ a.size() * CMSG_SPACE(sizeof(int));
+ sendmessage.msg_control = new char[required];
+ ::bzero(sendmessage.msg_control, required);
+ sendmessage.msg_controllen = required;
+
+ // Create ancillary buffer
+ ::cmsghdr * h = CMSG_FIRSTHDR(&sendmessage);
+
+ if(m.d->state & QUnixSocketMessagePrivate::Credential) {
+ h->cmsg_len = CMSG_LEN(sizeof(::ucred));
+ h->cmsg_level = SOL_SOCKET;
+ h->cmsg_type = SCM_CREDENTIALS;
+ ((::ucred *)CMSG_DATA(h))->pid = m.d->pid;
+ ((::ucred *)CMSG_DATA(h))->gid = m.d->gid;
+ ((::ucred *)CMSG_DATA(h))->uid = m.d->uid;
+ h = CMSG_NXTHDR(&sendmessage, h);
+ } else {
+ sendmessage.msg_controllen -= CMSG_SPACE(sizeof(::ucred));
+ }
+
+ for(int ii = 0; ii < a.count(); ++ii) {
+ const QUnixSocketRights & r = a.at(ii);
+
+ if(r.isValid()) {
+ h->cmsg_len = CMSG_LEN(sizeof(int));
+ h->cmsg_level = SOL_SOCKET;
+ h->cmsg_type = SCM_RIGHTS;
+ *((int *)CMSG_DATA(h)) = r.peekFd();
+ h = CMSG_NXTHDR(&sendmessage, h);
+ } else {
+ sendmessage.msg_controllen -= CMSG_SPACE(sizeof(int));
+ }
+ }
+
+#ifdef QUNIXSOCKET_DEBUG
+ qDebug() << "QUnixSocket: Transmitting message (length" << m.d->size() << ")";
+#endif
+ ::ssize_t s = ::sendmsg(fd, &sendmessage, MSG_DONTWAIT | MSG_NOSIGNAL);
+#ifdef QUNIXSOCKET_DEBUG
+ qDebug() << "QUnixSocket: Transmitted message (" << s << ")";
+#endif
+
+ if(-1 == s) {
+ if(EAGAIN == errno || EWOULDBLOCK == errno || EINTR == errno) {
+ writeNotifier->setEnabled(true);
+ } else if(EPIPE == errno) {
+#ifdef QUNIXSOCKET_DEBUG
+ qDebug() << "QUnixSocket: Remote side disconnected during transmit "
+ "(" << ::strerror(errno) << ")";
+#endif
+ me->abort();
+ } else {
+#ifdef QUNIXSOCKET_DEBUG
+ qDebug() << "QUnixSocket: Unable to transmit data ("
+ << ::strerror(errno) << ")";
+#endif
+ error = (QUnixSocket::SocketError)(QUnixSocket::WriteFailure |
+ CausedAbort);
+ me->abort();
+ }
+ } else if(s != m.d->size()) {
+
+ // A partial transmission
+ writeNotifier->setEnabled(true);
+ delete [] (char *)sendmessage.msg_control;
+ m.d->rights = QList<QUnixSocketRights>();
+ m.d->removeBytes( s );
+ writeQueueBytes -= s;
+ emit bytesWritten(s);
+ return s;
+
+ } else {
+
+ // Success!
+ writeQueue.dequeue();
+ Q_ASSERT(writeQueueBytes >= (unsigned)s);
+ writeQueueBytes -= s;
+ emit bytesWritten(s);
+
+ }
+
+ delete [] (char *)sendmessage.msg_control;
+ if(-1 != s && !writeQueue.isEmpty())
+ return writeActivated();
+ else if(QUnixSocket::ClosingState == me->state() && writeQueue.isEmpty())
+ me->abort();
+
+ if((-1 == s) && (EAGAIN == errno || EWOULDBLOCK == errno || EINTR == errno))
+ // Return zero bytes written to indicate retry may be required
+ return 0;
+ else
+ return s;
+}
+
+void QUnixSocketPrivate::readActivated()
+{
+#ifdef QUNIXSOCKET_DEBUG
+ qDebug() << "QUnixSocket: readActivated";
+#endif
+ readNotifier->setEnabled(false);
+
+ ::iovec vec;
+ vec.iov_base = dataBuffer;
+ vec.iov_len = dataBufferCapacity;
+
+ bzero(&message, sizeof(::msghdr));
+ message.msg_iov = &vec;
+ message.msg_iovlen = 1;
+ message.msg_controllen = ancillaryBufferCapacity();
+ message.msg_control = ancillaryBuffer;
+
+ int recvrv = ::recvmsg(fd, &message, 0);
+#ifdef QUNIXSOCKET_DEBUG
+ qDebug() << "QUnixSocket: Received message (" << recvrv << ")";
+#endif
+ if(-1 == recvrv) {
+#ifdef QUNIXSOCKET_DEBUG
+ qDebug() << "QUnixSocket: Unable to receive data ("
+ << ::strerror(errno) << ")";
+#endif
+ error = (QUnixSocket::SocketError)(QUnixSocket::ReadFailure |
+ CausedAbort);
+ me->abort();
+ } else if(0 == recvrv) {
+ me->abort();
+ } else {
+ Q_ASSERT(recvrv);
+ Q_ASSERT((unsigned)recvrv <= dataBufferCapacity);
+ dataBufferLength = recvrv;
+ messageValid = true;
+
+#ifdef QUNIXSOCKET_DEBUG
+ qDebug() << "QUnixSocket: readyRead() " << dataBufferLength;
+#endif
+ emit readyRead();
+ }
+}
+
+QT_END_NAMESPACE
+
+#include "qunixsocket.moc"
diff --git a/src/gui/embedded/qunixsocket_p.h b/src/gui/embedded/qunixsocket_p.h
new file mode 100644
index 0000000000..6b9bf61cf7
--- /dev/null
+++ b/src/gui/embedded/qunixsocket_p.h
@@ -0,0 +1,202 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QUNIXSOCKET_P_H
+#define QUNIXSOCKET_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 <QtNetwork/qabstractsocket.h>
+#include <QtCore/qiodevice.h>
+#include <QtCore/qlist.h>
+#include <QtCore/qshareddata.h>
+
+extern "C" {
+#include <sys/types.h>
+};
+
+QT_BEGIN_NAMESPACE
+
+class QUnixSocketRights;
+class QUnixSocketRightsPrivate;
+class QUnixSocketPrivate;
+class QUnixSocketMessagePrivate;
+struct iovec;
+
+class Q_GUI_EXPORT QUnixSocketRights {
+public:
+ QUnixSocketRights(int);
+ ~QUnixSocketRights();
+
+ QUnixSocketRights(const QUnixSocketRights &);
+ QUnixSocketRights & operator=(const QUnixSocketRights &);
+
+ bool isValid() const;
+
+ int dupFd() const;
+ int peekFd() const;
+
+private:
+ friend class QUnixSocket;
+ QUnixSocketRights(int,int);
+ QSharedDataPointer<QUnixSocketRightsPrivate> d;
+};
+
+class Q_GUI_EXPORT QUnixSocketMessage {
+public:
+ QUnixSocketMessage();
+ QUnixSocketMessage(const QByteArray &);
+ QUnixSocketMessage(const QByteArray &, const QList<QUnixSocketRights> &);
+ QUnixSocketMessage(const QUnixSocketMessage &);
+ QUnixSocketMessage(const iovec*, int);
+ QUnixSocketMessage & operator=(const QUnixSocketMessage &);
+ ~QUnixSocketMessage();
+
+ void setBytes(const QByteArray &);
+ void setRights(const QList<QUnixSocketRights> &);
+
+ const QList<QUnixSocketRights> & rights() const;
+ bool rightsWereTruncated() const;
+
+ const QByteArray & bytes() const;
+
+ pid_t processId() const;
+ uid_t userId() const;
+ gid_t groupId() const;
+
+ void setProcessId(pid_t);
+ void setUserId(uid_t);
+ void setGroupId(gid_t);
+
+ bool isValid() const;
+private:
+ friend class QUnixSocket;
+ friend class QUnixSocketPrivate;
+ QSharedDataPointer<QUnixSocketMessagePrivate> d;
+};
+
+class Q_GUI_EXPORT QUnixSocket : public QIODevice
+{
+ Q_OBJECT
+public:
+ QUnixSocket(QObject * = 0);
+ QUnixSocket(qint64, qint64, QObject * = 0);
+ virtual ~QUnixSocket();
+
+ enum SocketState {
+ UnconnectedState = QAbstractSocket::UnconnectedState,
+ HostLookupState = QAbstractSocket::HostLookupState,
+ ConnectingState = QAbstractSocket::ConnectingState,
+ ConnectedState = QAbstractSocket::ConnectedState,
+ BoundState = QAbstractSocket::BoundState,
+ ClosingState = QAbstractSocket::ClosingState,
+ ListeningState = QAbstractSocket::ListeningState,
+ };
+
+ enum SocketError { NoError, InvalidPath, ResourceError,
+ NonexistentPath, ConnectionRefused, UnknownError,
+ ReadFailure, WriteFailure };
+
+ bool connect(const QByteArray & path);
+ bool setSocketDescriptor(int socketDescriptor);
+ int socketDescriptor() const;
+ void abort();
+ void close();
+
+ bool flush();
+
+ SocketError error() const;
+
+ SocketState state() const;
+ QByteArray address() const;
+
+ qint64 bytesAvailable() const;
+ qint64 bytesToWrite() const;
+
+ qint64 readBufferSize() const;
+ void setReadBufferSize(qint64 size);
+ qint64 rightsBufferSize() const;
+ void setRightsBufferSize(qint64 size);
+
+ bool canReadLine() const;
+
+ qint64 write(const char * data, qint64 maxSize)
+ { return QIODevice::write(data, maxSize); }
+ qint64 write(const QByteArray & byteArray)
+ { return QIODevice::write(byteArray); }
+ qint64 read(char * data, qint64 maxSize)
+ { return QIODevice::read(data, maxSize); }
+ QByteArray read(qint64 maxSize)
+ { return QIODevice::read(maxSize); }
+
+ qint64 write(const QUnixSocketMessage &);
+ QUnixSocketMessage read();
+
+ virtual bool isSequential() const;
+ virtual bool waitForReadyRead(int msec = 300);
+ virtual bool waitForBytesWritten(int msec = 300);
+
+Q_SIGNALS:
+ void stateChanged(SocketState socketState);
+
+protected:
+ virtual qint64 readData(char * data, qint64 maxSize);
+ virtual qint64 writeData (const char * data, qint64 maxSize);
+
+private:
+ QUnixSocket(const QUnixSocket &);
+ QUnixSocket & operator=(const QUnixSocket &);
+
+ QUnixSocketPrivate * d;
+};
+
+QT_END_NAMESPACE
+
+#endif // QUNIXSOCKET_P_H
diff --git a/src/gui/embedded/qunixsocketserver.cpp b/src/gui/embedded/qunixsocketserver.cpp
new file mode 100644
index 0000000000..6e9347b8af
--- /dev/null
+++ b/src/gui/embedded/qunixsocketserver.cpp
@@ -0,0 +1,376 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qunixsocketserver_p.h"
+
+// #define QUNIXSOCKETSERVER_DEBUG
+
+#ifdef QUNIXSOCKETSERVER_DEBUG
+#include <QDebug>
+#endif
+
+#include <QtCore/qsocketnotifier.h>
+
+extern "C" {
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <unistd.h>
+#include <errno.h>
+};
+
+#define UNIX_PATH_MAX 108 // From unix(7)
+
+QT_BEGIN_NAMESPACE
+
+class QUnixSocketServerPrivate : public QObject
+{
+Q_OBJECT
+public:
+ QUnixSocketServerPrivate(QUnixSocketServer * parent)
+ : QObject(), me(parent), fd(-1), maxConns(30),
+ error(QUnixSocketServer::NoError), acceptNotifier(0)
+ {}
+
+ QUnixSocketServer * me;
+ int fd;
+ int maxConns;
+ QByteArray address;
+ QUnixSocketServer::ServerError error;
+ QSocketNotifier * acceptNotifier;
+public slots:
+ void acceptActivated();
+};
+
+/*!
+ \class QUnixSocketServer
+ \internal
+
+ \brief The QUnixSocketServer class provides a Unix domain socket based server.
+ \omit
+ \ingroup Platform::DeviceSpecific
+ \ingroup Platform::OS
+ \ingroup Platform::Communications
+ \endomit
+ \ingroup qws
+
+ This class makes it possible to accept incoming Unix domain socket
+ connections. Call \l QUnixSocketServer::listen() to have the server listen
+ for incoming connections on a specified path. The pure virtual
+ \l QUnixSocketServer::incomingConnection() is called each time a new
+ connection is established. Users must inherit from QUnixSocketServer and
+ implement this method.
+
+ If an error occurs, \l QUnixSocketServer::serverError() returns the type of
+ error. Errors can only occur during server establishment - that is, during a
+ call to \l QUnixSocketServer::listen(). Calling \l QUnixSocketServer::close()
+ causes QUnixSocketServer to stop listening for connections and reset its
+ state.
+
+ QUnixSocketServer is often used in conjunction with the \l QUnixSocket class.
+
+ \sa QUnixSocket
+*/
+
+/*!
+ \enum QUnixSocketServer::ServerError
+
+ The ServerError enumeration represents the errors that can occur during server
+ establishment. The most recent error can be retrieved through a call to
+ \l QUnixSocketServer::serverError().
+
+ \value NoError No error has occurred.
+ \value InvalidPath An invalid path endpoint was passed to
+ \l QUnixSocketServer::listen(). As defined by unix(7), invalid paths
+ include an empty path, or what more than 107 characters long.
+ \value ResourceError An error acquiring or manipulating the system's socket
+ resources occurred. For example, if the process runs out of available
+ socket descriptors, a ResourceError will occur.
+ \value BindError The server was unable to bind to the specified path.
+ \value ListenError The server was unable to listen on the specified path for
+ incoming connections.
+ */
+
+/*!
+ Create a new Unix socket server with the given \a parent.
+ */
+QUnixSocketServer::QUnixSocketServer(QObject *parent)
+: QObject(parent), d(0)
+{
+}
+
+/*!
+ Stops listening for incoming connection and destroys the Unix socket server.
+ */
+QUnixSocketServer::~QUnixSocketServer()
+{
+ close();
+ if(d)
+ delete d;
+}
+
+/*!
+ Stop listening for incoming connections and resets the Unix socket server's
+ state. Calling this method while \l {QUnixSocketServer::isListening()}{not listening } for incoming connections is a no-op.
+
+ \sa QUnixSocketServer::listen()
+ */
+void QUnixSocketServer::close()
+{
+ if(!d)
+ return;
+
+ if(d->acceptNotifier) {
+ d->acceptNotifier->setEnabled(false);
+ delete d->acceptNotifier;
+ }
+ d->acceptNotifier = 0;
+
+ if(-1 != d->fd) {
+#ifdef QUNIXSOCKET_DEBUG
+ int closerv =
+#endif
+ ::close(d->fd);
+#ifdef QUNIXSOCKET_DEBUG
+ if(0 != closerv) {
+ qDebug() << "QUnixSocketServer: Unable to close socket ("
+ << strerror(errno) << ")";
+ }
+#endif
+ }
+ d->fd = -1;
+ d->address = QByteArray();
+ d->error = NoError;
+}
+
+/*!
+ Returns the last server error. Errors may only occur within a call to
+ \l QUnixSocketServer::listen(), and only when such a call fails.
+
+ This method is not destructive, so multiple calls to
+ QUnixSocketServer::serverError() will return the same value. The error is
+ only reset by an explicit call to \l QUnixSocketServer::close() or
+ by further calls to \l QUnixSocketServer::listen().
+ */
+QUnixSocketServer::ServerError QUnixSocketServer::serverError() const
+{
+ if(!d)
+ return NoError;
+
+ return d->error;
+}
+
+/*!
+ Returns true if this server is listening for incoming connections, false
+ otherwise.
+
+ \sa QUnixSocketServer::listen()
+ */
+bool QUnixSocketServer::isListening() const
+{
+ if(!d)
+ return false;
+
+ return (-1 != d->fd);
+}
+
+/*!
+ Tells the server to listen for incoming connections on \a path. Returns true
+ if it successfully initializes, false otherwise. In the case of failure, the
+ \l QUnixSocketServer::serverError() error status is set accordingly.
+
+ Calling this method while the server is already running will result in the
+ server begin reset, and then attempting to listen on \a path. This will not
+ affect connections established prior to the server being reset, but further
+ incoming connections on the previous path will be refused.
+
+ The server can be explicitly reset by a call to \l QUnixSocketServer::close().
+
+ \sa QUnixSocketServer::close()
+ */
+bool QUnixSocketServer::listen(const QByteArray & path)
+{
+ if(d) {
+ close(); // Any existing server is destroyed
+ } else {
+ d = new QUnixSocketServerPrivate(this);
+ }
+
+ if(path.isEmpty() || path.size() > UNIX_PATH_MAX) {
+ d->error = InvalidPath;
+ return false;
+ }
+ unlink( path ); // ok if this fails
+
+ // Create the socket
+ d->fd = ::socket(PF_UNIX, SOCK_STREAM, 0);
+ if(-1 == d->fd) {
+#ifdef QUNIXSOCKETSERVER_DEBUG
+ qDebug() << "QUnixSocketServer: Unable to create socket ("
+ << strerror(errno) << ")";
+#endif
+ close();
+ d->error = ResourceError;
+ return false;
+ }
+
+ // Construct our unix address
+ struct ::sockaddr_un addr;
+ addr.sun_family = AF_UNIX;
+ ::memcpy(addr.sun_path, path.data(), path.size());
+ if(path.size() < UNIX_PATH_MAX)
+ addr.sun_path[path.size()] = '\0';
+
+ // Attempt to bind
+ if(-1 == ::bind(d->fd, (sockaddr *)&addr, sizeof(sockaddr_un))) {
+#ifdef QUNIXSOCKETSERVER_DEBUG
+ qDebug() << "QUnixSocketServer: Unable to bind socket ("
+ << strerror(errno) << ")";
+#endif
+ close();
+ d->error = BindError;
+ return false;
+ }
+
+ // Listen to socket
+ if(-1 == ::listen(d->fd, d->maxConns)) {
+#ifdef QUNIXSOCKETSERVER_DEBUG
+ qDebug() << "QUnixSocketServer: Unable to listen socket ("
+ << strerror(errno) << ")";
+#endif
+ close();
+ d->error = ListenError;
+ return false;
+ }
+
+ // Success!
+ d->address = path;
+ d->acceptNotifier = new QSocketNotifier(d->fd, QSocketNotifier::Read, d);
+ d->acceptNotifier->setEnabled(true);
+ QObject::connect(d->acceptNotifier, SIGNAL(activated(int)),
+ d, SLOT(acceptActivated()));
+
+ return true;
+}
+
+/*!
+ Returns the Unix path on which this server is listening. If this server is
+ not listening, and empty address will be returned.
+ */
+QByteArray QUnixSocketServer::serverAddress() const
+{
+ if(!d)
+ return QByteArray();
+ return d->address;
+}
+
+int QUnixSocketServer::socketDescriptor() const
+{
+ if (!d)
+ return -1;
+ return d->fd;
+}
+
+
+/*!
+ Returns the maximum length the queue of pending connections may grow to. That
+ is, the maximum number of clients attempting to connect for which the Unix
+ socket server has not yet accepted and passed to
+ \l QUnixSocketServer::incomingConnection(). If a connection request arrives
+ with the queue full, the client may receive a connection refused notification.
+
+ By default a queue length of 30 is used.
+
+ \sa QUnixSocketServer::setMaxPendingConnections()
+ */
+int QUnixSocketServer::maxPendingConnections() const
+{
+ if(!d)
+ return 30;
+
+ return d->maxConns;
+}
+
+/*!
+ Sets the maximum length the queue of pending connections may grow to
+ \a numConnections. This value will only apply to
+ \l QUnixSocketServer::listen() calls made following the value change - it will
+ not be retroactively applied.
+
+ \sa QUnixSocketServer::maxPendingConnections()
+ */
+void QUnixSocketServer::setMaxPendingConnections(int numConnections)
+{
+ Q_ASSERT(numConnections >= 1);
+ if(!d)
+ d = new QUnixSocketServerPrivate(this);
+
+ d->maxConns = numConnections;
+}
+
+/*!
+ \fn void QUnixSocketServer::incomingConnection(int socketDescriptor)
+
+ This method is invoked each time a new incoming connection is established with
+ the server. Clients must reimplement this function in their QUnixSocketServer
+ derived class to handle the connection.
+
+ A common approach to handling the connection is to pass \a socketDescriptor to
+ a QUnixSocket instance.
+
+ \sa QUnixSocket
+ */
+
+void QUnixSocketServerPrivate::acceptActivated()
+{
+ ::sockaddr_un r;
+ socklen_t len = sizeof(sockaddr_un);
+ int connsock = ::accept(fd, (sockaddr *)&r, &len);
+#ifdef QUNIXSOCKETSERVER_DEBUG
+ qDebug() << "QUnixSocketServer: Accept connection " << connsock;
+#endif
+ if(-1 != connsock)
+ me->incomingConnection(connsock);
+}
+
+QT_END_NAMESPACE
+
+#include "qunixsocketserver.moc"
diff --git a/src/gui/embedded/qunixsocketserver_p.h b/src/gui/embedded/qunixsocketserver_p.h
new file mode 100644
index 0000000000..60d4cfcbc4
--- /dev/null
+++ b/src/gui/embedded/qunixsocketserver_p.h
@@ -0,0 +1,98 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QUNIXSOCKETSERVER_P_H
+#define QUNIXSOCKETSERVER_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 <QtCore/qobject.h>
+
+QT_BEGIN_NAMESPACE
+
+class QUnixSocketServerPrivate;
+class Q_GUI_EXPORT QUnixSocketServer : public QObject
+{
+ Q_OBJECT
+public:
+ enum ServerError { NoError, InvalidPath, ResourceError, BindError,
+ ListenError };
+
+ QUnixSocketServer(QObject *parent=0);
+ virtual ~QUnixSocketServer();
+
+ void close();
+
+ ServerError serverError() const;
+
+ bool isListening() const;
+ bool listen(const QByteArray & path);
+
+ int socketDescriptor() const;
+ QByteArray serverAddress() const;
+
+ int maxPendingConnections() const;
+ void setMaxPendingConnections(int numConnections);
+
+protected:
+ virtual void incomingConnection(int socketDescriptor) = 0;
+
+private:
+ QUnixSocketServer(const QUnixSocketServer &);
+ QUnixSocketServer & operator=(const QUnixSocketServer &);
+
+ friend class QUnixSocketServerPrivate;
+ QUnixSocketServerPrivate * d;
+};
+
+
+QT_END_NAMESPACE
+#endif // QUNIXSOCKETSERVER_P_H
+
diff --git a/src/gui/embedded/qvfbhdr.h b/src/gui/embedded/qvfbhdr.h
new file mode 100644
index 0000000000..421d991777
--- /dev/null
+++ b/src/gui/embedded/qvfbhdr.h
@@ -0,0 +1,89 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QVFBHDR_H
+#define QVFBHDR_H
+
+#include <QtGui/qcolor.h>
+#include <QtGui/qwindowdefs.h>
+#include <QtCore/qrect.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+#define QT_VFB_MOUSE_PIPE "/tmp/.qtvfb_mouse-%1"
+#define QT_VFB_KEYBOARD_PIPE "/tmp/.qtvfb_keyboard-%1"
+#define QT_VFB_MAP "/tmp/.qtvfb_map-%1"
+
+struct QVFbHeader
+{
+ int width;
+ int height;
+ int depth;
+ int linestep;
+ int dataoffset;
+ QRect update;
+ bool dirty;
+ int numcols;
+ QRgb clut[256];
+ int viewerVersion;
+ int serverVersion;
+ int brightness; // since 4.4.0
+ WId windowId; // since 4.5.0
+};
+
+struct QVFbKeyData
+{
+ unsigned int keycode;
+ Qt::KeyboardModifiers modifiers;
+ unsigned short int unicode;
+ bool press;
+ bool repeat;
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QVFBHDR_H
diff --git a/src/gui/embedded/qwindowsystem_p.h b/src/gui/embedded/qwindowsystem_p.h
new file mode 100644
index 0000000000..7782206fca
--- /dev/null
+++ b/src/gui/embedded/qwindowsystem_p.h
@@ -0,0 +1,315 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QWINDOWSYSTEM_QWS_P_H
+#define QWINDOWSYSTEM_QWS_P_H
+
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of the QWSServer class. This header file may change from
+// version to version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "private/qobject_p.h"
+#include "qwindowsystem_qws.h"
+#include "qbrush.h"
+#include "qwsproperty_qws.h"
+#include "qwscommand_qws_p.h"
+#include "QtCore/qbasictimer.h"
+
+QT_BEGIN_NAMESPACE
+
+class QWSServerPrivate : public QObjectPrivate {
+ friend class QCopChannel;
+ friend class QWSMouseHandler;
+ friend class QWSWindow;
+ friend class QWSDisplay;
+ friend class QWSInputMethod;
+ Q_DECLARE_PUBLIC(QWSServer)
+
+public:
+ QWSServerPrivate()
+ : screensaverintervals(0)
+ , screensavereventblocklevel(-1), screensaverblockevents(false)
+ , saver(0), cursorClient(0), mouseState(0), nReserved(0)
+ , doClientIsActive(false)
+ {
+ }
+ ~QWSServerPrivate()
+ {
+ closeDisplay();
+
+ qDeleteAll(deletedWindows);
+ delete [] screensaverintervals;
+ delete saver;
+
+ qDeleteAll(windows);
+ windows.clear();
+
+ delete bgBrush;
+ bgBrush = 0;
+ }
+ QTime screensavertime;
+ QTimer* screensavertimer;
+ int* screensaverintervals;
+ int screensavereventblocklevel;
+ bool screensaverblockevents;
+ bool screensaverblockevent( int index, int *screensaverinterval, bool isDown );
+ QWSScreenSaver* saver;
+ QWSClient *cursorClient;
+ int mouseState;
+// bool prevWin;
+ QList<QWSWindow*> deletedWindows;
+ QList<int> crashedClientIds;
+
+ void update_regions();
+//private functions moved from class
+
+private:
+ void initServer(int flags);
+#ifndef QT_NO_COP
+ static void sendQCopEvent(QWSClient *c, const QString &ch,
+ const QString &msg, const QByteArray &data,
+ bool response = false);
+#endif
+ void move_region(const QWSRegionMoveCommand *);
+ void set_altitude(const QWSChangeAltitudeCommand *);
+ void set_opacity(const QWSSetOpacityCommand *);
+ void request_focus(const QWSRequestFocusCommand *);
+ QRegion reserve_region(QWSWindow *window, const QRegion &region);
+ void request_region(int winId, const QString &surfaceKey,
+ const QByteArray &surfaceData,
+ const QRegion &region);
+ void repaint_region(int winId, int windowFlags, bool opaque, const QRegion &);
+ void destroy_region(const QWSRegionDestroyCommand *);
+ void name_region(const QWSRegionNameCommand *);
+ void set_identity(const QWSIdentifyCommand *);
+#ifndef QT_NO_QWS_PROPERTIES
+ bool get_property(int winId, int property, const char *&data, int &len);
+#endif
+#ifndef QT_NO_QWS_INPUTMETHODS
+ void im_response(const QWSIMResponseCommand *);
+
+ void im_update(const QWSIMUpdateCommand *);
+
+ void send_im_mouse(const QWSIMMouseCommand *);
+#endif
+ // not in ifndef as this results in more readable functions.
+ static void sendKeyEventUnfiltered(int unicode, int keycode, Qt::KeyboardModifiers modifiers,
+ bool isPress, bool autoRepeat);
+ static void sendMouseEventUnfiltered(const QPoint &pos, int state, int wheel = 0);
+ static void emergency_cleanup();
+
+ static QBrush *bgBrush;
+
+ void sendMaxWindowRectEvents(const QRect &rect);
+
+ void invokeIdentify(const QWSIdentifyCommand *cmd, QWSClient *client);
+ void invokeCreate(QWSCreateCommand *cmd, QWSClient *client);
+ void invokeRegionName(const QWSRegionNameCommand *cmd, QWSClient *client);
+ void invokeRegion(QWSRegionCommand *cmd, QWSClient *client);
+ void invokeRegionMove(const QWSRegionMoveCommand *cmd, QWSClient *client);
+ void invokeRegionDestroy(const QWSRegionDestroyCommand *cmd, QWSClient *client);
+ void invokeSetAltitude(const QWSChangeAltitudeCommand *cmd, QWSClient *client);
+ void invokeSetOpacity(const QWSSetOpacityCommand *cmd, QWSClient *client);
+#ifndef QT_NO_QWS_PROPERTIES
+ void invokeAddProperty(QWSAddPropertyCommand *cmd);
+ void invokeSetProperty(QWSSetPropertyCommand *cmd);
+ void invokeRemoveProperty(QWSRemovePropertyCommand *cmd);
+ void invokeGetProperty(QWSGetPropertyCommand *cmd, QWSClient *client);
+#endif //QT_NO_QWS_PROPERTIES
+ void invokeSetSelectionOwner(QWSSetSelectionOwnerCommand *cmd);
+ void invokeConvertSelection(QWSConvertSelectionCommand *cmd);
+ void invokeSetFocus(const QWSRequestFocusCommand *cmd, QWSClient *client);
+
+ void initIO();
+ void setFocus(QWSWindow*, bool gain);
+#ifndef QT_NO_QWS_CURSOR
+ void invokeDefineCursor(QWSDefineCursorCommand *cmd, QWSClient *client);
+ void invokeSelectCursor(QWSSelectCursorCommand *cmd, QWSClient *client);
+ void invokePositionCursor(QWSPositionCursorCommand *cmd, QWSClient *client);
+#endif
+ void invokeGrabMouse(QWSGrabMouseCommand *cmd, QWSClient *client);
+ void invokeGrabKeyboard(QWSGrabKeyboardCommand *cmd, QWSClient *client);
+#ifndef QT_NO_SOUND
+ void invokePlaySound(QWSPlaySoundCommand *cmd, QWSClient *client);
+#endif
+#ifndef QT_NO_COP
+ void invokeRegisterChannel(QWSQCopRegisterChannelCommand *cmd,
+ QWSClient *client);
+ void invokeQCopSend(QWSQCopSendCommand *cmd, QWSClient *client);
+#endif
+ void invokeRepaintRegion(QWSRepaintRegionCommand *cmd,
+ QWSClient *client);
+#ifndef QT_NO_QWSEMBEDWIDGET
+ void invokeEmbed(QWSEmbedCommand *cmd, QWSClient *client);
+#endif
+#ifndef QT_NO_QWS_INPUTMETHODS
+ void invokeIMResponse(const QWSIMResponseCommand *cmd,
+ QWSClient *client);
+ void invokeIMUpdate(const QWSIMUpdateCommand *cmd,
+ QWSClient *client);
+#endif
+ void invokeFont(const QWSFontCommand *cmd, QWSClient *client);
+ void invokeScreenTransform(const QWSScreenTransformCommand *cmd,
+ QWSClient *client);
+
+ QWSMouseHandler* newMouseHandler(const QString& spec);
+ void openDisplay();
+ void closeDisplay();
+
+ void showCursor();
+ void hideCursor();
+ void initializeCursor();
+
+ void resetEngine();
+
+//private Q_SLOTS:
+
+#ifndef QT_NO_QWS_MULTIPROCESS
+ void _q_clientClosed();
+ void _q_doClient();
+ void _q_deleteWindowsLater();
+#endif
+
+ void _q_screenSaverWake();
+ void _q_screenSaverSleep();
+ void _q_screenSaverTimeout();
+#ifndef QT_NO_QWS_MULTIPROCESS
+ void _q_newConnection();
+#endif
+
+//other private moved from class
+
+ void disconnectClient(QWSClient *);
+ void screenSave(int level);
+ void doClient(QWSClient *);
+ typedef QMap<int,QWSClient*>::Iterator ClientIterator;
+ typedef QMap<int,QWSClient*> ClientMap;
+ void handleWindowClose(QWSWindow *w);
+ void releaseMouse(QWSWindow* w);
+ void releaseKeyboard(QWSWindow* w);
+ void updateClientCursorPos();
+
+ uchar* sharedram;
+ int ramlen;
+
+ ClientMap clientMap;
+#ifndef QT_NO_QWS_PROPERTIES
+ QWSPropertyManager propertyManager;
+#endif
+ struct SelectionOwner {
+ int windowid;
+ struct Time {
+ void set(int h, int m, int s, int s2) {
+ hour = h; minute = m; sec = s; ms = s2;
+ }
+ int hour, minute, sec, ms;
+ } time;
+ } selectionOwner;
+ QTime timer;
+ int* screensaverinterval;
+
+ QWSWindow *focusw;
+ QWSWindow *mouseGrabber;
+ bool mouseGrabbing;
+ bool inputMethodMouseGrabbed;
+ int swidth, sheight, sdepth;
+#ifndef QT_NO_QWS_CURSOR
+ bool haveviscurs;
+ QWSCursor *cursor; // cursor currently shown
+ QWSCursor *nextCursor; // cursor to show once grabbing is off
+#endif
+
+ bool disablePainting;
+ QList<QWSMouseHandler*> mousehandlers;
+#ifndef QT_NO_QWS_KEYBOARD
+ QList<QWSKeyboardHandler*> keyboardhandlers;
+#endif
+
+ QList<QWSCommandStruct*> commandQueue;
+
+ // Window management
+ QList<QWSWindow*> windows; // first=topmost
+ int nReserved;
+ QWSWindow* newWindow(int id, QWSClient* client);
+ QWSWindow* findWindow(int windowid, QWSClient* client = 0);
+ void moveWindowRegion(QWSWindow*, int dx, int dy);
+ void setWindowRegion(QWSWindow*, const QRegion &r);
+ void raiseWindow(QWSWindow *, int = 0);
+ void lowerWindow(QWSWindow *, int = -1);
+ void exposeRegion(const QRegion &, int index = 0);
+
+ void setCursor(QWSCursor *curs);
+
+ // multimedia
+#ifndef QT_NO_SOUND
+ QWSSoundServer *soundserver;
+#endif
+#ifndef QT_NO_COP
+ QMap<QString, QList<QWSClient*> > channels;
+#endif
+
+#ifndef QT_NO_QWS_MULTIPROCESS
+ QWSServerSocket *ssocket;
+#endif
+
+ // filename -> refcount
+ QMap<QByteArray, int> fontReferenceCount;
+ QBasicTimer fontCleanupTimer;
+ void referenceFont(QWSClientPrivate *client, const QByteArray &font);
+ void dereferenceFont(QWSClientPrivate *client, const QByteArray &font);
+ void cleanupFonts(bool force = false);
+ void sendFontRemovedEvent(const QByteArray &font);
+
+ bool doClientIsActive;
+ QList<QWSClient*> pendingDoClients;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/gui/embedded/qwindowsystem_qws.cpp b/src/gui/embedded/qwindowsystem_qws.cpp
new file mode 100644
index 0000000000..dffebf2781
--- /dev/null
+++ b/src/gui/embedded/qwindowsystem_qws.cpp
@@ -0,0 +1,4947 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qwindowsystem_qws.h"
+#include "qwsevent_qws.h"
+#include "qwscommand_qws_p.h"
+#include "qtransportauth_qws_p.h"
+#include "qwsutils_qws.h"
+#include "qwscursor_qws.h"
+#include "qwsdisplay_qws.h"
+#include "qmouse_qws.h"
+#include "qcopchannel_qws.h"
+#include "qwssocket_qws.h"
+
+#include "qapplication.h"
+#include "private/qapplication_p.h"
+#include "qsocketnotifier.h"
+#include "qpolygon.h"
+#include "qimage.h"
+#include "qcursor.h"
+#include <private/qpaintengine_raster_p.h>
+#include "qscreen_qws.h"
+#include "qwindowdefs.h"
+#include "private/qlock_p.h"
+#include "qwslock_p.h"
+#include "qfile.h"
+#include "qtimer.h"
+#include "qpen.h"
+#include "qdesktopwidget.h"
+#include "qevent.h"
+#include "qinputcontext.h"
+#include "qpainter.h"
+
+#include <qdebug.h>
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+
+#ifndef QT_NO_QWS_MULTIPROCESS
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/ipc.h>
+#include <sys/shm.h>
+#ifndef Q_OS_DARWIN
+# include <sys/sem.h>
+#endif
+#include <sys/param.h>
+#include <sys/mount.h>
+#endif
+#include <signal.h>
+#include <fcntl.h>
+
+#if !defined(QT_NO_SOUND) && !defined(Q_OS_DARWIN)
+#ifdef QT_USE_OLD_QWS_SOUND
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <sys/soundcard.h>
+#else
+#include "qsoundqss_qws.h"
+#endif
+#endif
+
+#include "qkbddriverfactory_qws.h"
+#include "qmousedriverfactory_qws.h"
+
+#include <qbuffer.h>
+#include <qdir.h>
+
+#include <private/qwindowsurface_qws_p.h>
+#include <private/qfontengine_qpf_p.h>
+
+#include "qwindowsystem_p.h"
+
+//#define QWS_DEBUG_FONTCLEANUP
+
+QT_BEGIN_NAMESPACE
+
+QWSServer Q_GUI_EXPORT *qwsServer=0;
+static QWSServerPrivate *qwsServerPrivate=0;
+
+#define MOUSE 0
+#define KEY 1
+//#define EVENT_BLOCK_DEBUG
+
+QWSScreenSaver::~QWSScreenSaver()
+{
+}
+
+extern QByteArray qws_display_spec;
+extern void qt_init_display(); //qapplication_qws.cpp
+extern QString qws_qtePipeFilename();
+
+extern void qt_client_enqueue(const QWSEvent *); //qapplication_qws.cpp
+extern QList<QWSCommand*> *qt_get_server_queue();
+
+Q_GLOBAL_STATIC_WITH_ARGS(QString, defaultMouse, (QLatin1String("Auto")))
+Q_GLOBAL_STATIC_WITH_ARGS(QString, defaultKeyboard, (QLatin1String("TTY")))
+static const int FontCleanupInterval = 60 * 1000;
+
+static int qws_keyModifiers = 0;
+
+static QWSWindow *keyboardGrabber;
+static bool keyboardGrabbing;
+
+static int get_object_id(int count = 1)
+{
+ static int next=1000;
+ int n = next;
+ next += count;
+ return n;
+}
+#ifndef QT_NO_QWS_INPUTMETHODS
+static QWSInputMethod *current_IM = 0;
+
+static QWSWindow *current_IM_composing_win = 0;
+static int current_IM_winId = -1;
+static bool force_reject_strokeIM = false;
+#endif
+
+static void cleanupFontsDir();
+
+//#define QWS_REGION_DEBUG
+
+/*!
+ \class QWSScreenSaver
+ \ingroup qws
+
+ \brief The QWSScreenSaver class is a base class for screensavers
+ in Qt for Embedded Linux.
+
+ When running \l{Qt for Embedded Linux} applications, it is the server
+ application that installs and controls the screensaver.
+ \l{Qt for Embedded Linux} supports multilevel screen saving; i.e., it is possible to
+ specify several different levels of screen responsiveness. For
+ example, you can choose to first turn off the light before you
+ fully activate the screensaver.
+
+ Note that there exists no default screensaver implementation.
+
+ To create a custom screensaver, derive from this class and
+ reimplement the restore() and save() functions. These functions
+ are called whenever the screensaver is activated or deactivated,
+ respectively. Once an instance of your custom screensaver is
+ created, you can use the QWSServer::setScreenSaver() function to
+ install it.
+
+ \sa QWSServer, QScreen, {Qt for Embedded Linux}
+*/
+
+/*!
+ \fn QWSScreenSaver::~QWSScreenSaver()
+
+ Reimplement this function to destroy the screensaver.
+*/
+
+/*!
+ \fn QWSScreenSaver::restore()
+
+ Implement this function to deactivate the screensaver, restoring
+ the previously saved screen.
+
+ \sa save(), QWSServer::screenSaverActivate()
+*/
+
+/*!
+ \fn QWSScreenSaver::save(int level)
+
+ Implement this function to activate the screensaver, saving the
+ current screen.
+
+ \l{Qt for Embedded Linux} supports multilevel screen saving; i.e., it is
+ possible to specify several different levels of screen
+ responsiveness. For example, you can choose to first turn off the
+ light before you fully activate the screensaver. Use the
+ QWSServer::setScreenSaverIntervals() to specify the time intervals
+ between the different levels.
+
+ This function should return true if the screensaver successfully
+ enters the given \a level; otherwise it should return false.
+
+ \sa restore(), QWSServer::screenSaverActivate()
+*/
+
+class QWSWindowPrivate
+{
+public:
+ QWSWindowPrivate();
+
+#ifdef QT_QWS_CLIENTBLIT
+ QRegion directPaintRegion;
+#endif
+ QRegion allocatedRegion;
+#ifndef QT_NO_QWSEMBEDWIDGET
+ QList<QWSWindow*> embedded;
+ QWSWindow *embedder;
+#endif
+ QWSWindow::State state;
+ Qt::WindowFlags windowFlags;
+ QRegion dirtyOnScreen;
+ bool painted;
+};
+
+QWSWindowPrivate::QWSWindowPrivate()
+ :
+#ifndef QT_NO_QWSEMBEDWIDGET
+ embedder(0), state(QWSWindow::NoState),
+#endif
+ painted(false)
+{
+}
+
+/*!
+ \class QWSWindow
+ \ingroup qws
+
+ \brief The QWSWindow class encapsulates a top-level window in
+ Qt for Embedded Linux.
+
+ When you run a \l{Qt for Embedded Linux} application, it either runs as a
+ server or connects to an existing server. As applications add and
+ remove windows, the server process maintains information about
+ each window. In \l{Qt for Embedded Linux}, top-level windows are
+ encapsulated as QWSWindow objects. Note that you should never
+ construct the QWSWindow class yourself; the current top-level
+ windows can be retrieved using the QWSServer::clientWindows()
+ function.
+
+ With a window at hand, you can retrieve its caption, name, opacity
+ and ID using the caption(), name(), opacity() and winId()
+ functions, respectively. Use the client() function to retrieve a
+ pointer to the client that owns the window.
+
+ Use the isVisible() function to find out if the window is
+ visible. You can find out if the window is completely obscured by
+ another window or by the bounds of the screen, using the
+ isFullyObscured() function. The isOpaque() function returns true
+ if the window has an alpha channel equal to 255. Finally, the
+ requestedRegion() function returns the region of the display the
+ window wants to draw on.
+
+ \sa QWSServer, QWSClient, {Qt for Embedded Linux Architecture}
+*/
+
+/*!
+ \fn int QWSWindow::winId() const
+
+ Returns the window's ID.
+
+ \sa name(), caption()
+*/
+
+/*!
+ \fn const QString &QWSWindow::name() const
+
+ Returns the window's name, which is taken from the \l {QWidget::}{objectName()}
+ at the time of \l {QWidget::}{show()}.
+
+ \sa caption(), winId()
+*/
+
+/*!
+ \fn const QString &QWSWindow::caption() const
+
+ Returns the window's caption.
+
+ \sa name(), winId()
+*/
+
+/*!
+ \fn QWSClient* QWSWindow::client() const
+
+ Returns a reference to the QWSClient object that owns this window.
+
+ \sa requestedRegion()
+*/
+
+/*!
+ \fn QRegion QWSWindow::requestedRegion() const
+
+ Returns the region that the window has requested to draw onto,
+ including any window decorations.
+
+ \sa client()
+*/
+
+/*!
+ \fn bool QWSWindow::isVisible() const
+
+ Returns true if the window is visible; otherwise returns false.
+
+ \sa isFullyObscured()
+*/
+
+/*!
+ \fn bool QWSWindow::isOpaque() const
+
+ Returns true if the window is opaque, i.e., if its alpha channel
+ equals 255; otherwise returns false.
+
+ \sa opacity()
+*/
+
+/*!
+ \fn uint QWSWindow::opacity () const
+
+ Returns the window's alpha channel value.
+
+ \sa isOpaque()
+*/
+
+/*!
+ \fn bool QWSWindow::isPartiallyObscured() const
+ \internal
+
+ Returns true if the window is partially obsured by another window
+ or by the bounds of the screen; otherwise returns false.
+*/
+
+/*!
+ \fn bool QWSWindow::isFullyObscured() const
+
+ Returns true if the window is completely obsured by another window
+ or by the bounds of the screen; otherwise returns false.
+
+ \sa isVisible()
+*/
+
+/*!
+ \fn QWSWindowSurface* QWSWindow::windowSurface() const
+ \internal
+*/
+
+QWSWindow::QWSWindow(int i, QWSClient* client)
+ : id(i), modified(false),
+ onTop(false), c(client), last_focus_time(0), _opacity(255),
+ opaque(true), d(new QWSWindowPrivate)
+{
+ surface = 0;
+}
+
+
+/*!
+ \enum QWSWindow::State
+
+ This enum describes the state of a window. Most of the
+ transitional states are set just before a call to
+ QScreen::exposeRegion() and reset immediately afterwards.
+
+ \value NoState Initial state before the window is properly initialized.
+ \value Hidden The window is not visible.
+ \value Showing The window is being shown.
+ \value Visible The window is visible, and not in a transition.
+ \value Hiding The window is being hidden.
+ \value Raising The windoe is being raised.
+ \value Lowering The window is being raised.
+ \value Moving The window is being moved.
+ \value ChangingGeometry The window's geometry is being changed.
+ \value Destroyed The window is destroyed.
+
+ \sa state(), QScreen::exposeRegion()
+*/
+
+/*!
+ Returns the current state of the window.
+
+ \since 4.3
+*/
+QWSWindow::State QWSWindow::state() const
+{
+ return d->state;
+}
+
+/*!
+ Returns the window flags of the window. This value is only available
+ after the first paint event.
+
+ \since 4.3
+*/
+Qt::WindowFlags QWSWindow::windowFlags() const
+{
+ return d->windowFlags;
+}
+
+/*!
+ Returns the region that has been repainted since the previous
+ QScreen::exposeRegion(), and needs to be copied to the screen.
+ \since 4.3
+*/
+QRegion QWSWindow::dirtyOnScreen() const
+{
+ return d->dirtyOnScreen;
+}
+
+void QWSWindow::createSurface(const QString &key, const QByteArray &data)
+{
+#ifndef QT_NO_QWS_MULTIPROCESS
+ if (surface && !surface->isBuffered())
+ c->removeUnbufferedSurface();
+#endif
+
+ delete surface;
+ surface = qt_screen->createSurface(key);
+ surface->setPermanentState(data);
+
+#ifndef QT_NO_QWS_MULTIPROCESS
+ if (!surface->isBuffered())
+ c->addUnbufferedSurface();
+#endif
+}
+
+/*!
+ \internal
+ Raises the window above all other windows except "Stay on top" windows.
+*/
+void QWSWindow::raise()
+{
+ qwsServerPrivate->raiseWindow(this);
+#ifndef QT_NO_QWSEMBEDWIDGET
+ const int n = d->embedded.size();
+ for (int i = 0; i < n; ++i)
+ d->embedded.at(i)->raise();
+#endif
+}
+
+/*!
+ \internal
+ Lowers the window below other windows.
+*/
+void QWSWindow::lower()
+{
+ qwsServerPrivate->lowerWindow(this);
+#ifndef QT_NO_QWSEMBEDWIDGET
+ const int n = d->embedded.size();
+ for (int i = 0; i < n; ++i)
+ d->embedded.at(i)->lower();
+#endif
+}
+
+/*!
+ \internal
+ Shows the window.
+*/
+void QWSWindow::show()
+{
+ operation(QWSWindowOperationEvent::Show);
+#ifndef QT_NO_QWSEMBEDWIDGET
+ const int n = d->embedded.size();
+ for (int i = 0; i < n; ++i)
+ d->embedded.at(i)->show();
+#endif
+}
+
+/*!
+ \internal
+ Hides the window.
+*/
+void QWSWindow::hide()
+{
+ operation(QWSWindowOperationEvent::Hide);
+#ifndef QT_NO_QWSEMBEDWIDGET
+ const int n = d->embedded.size();
+ for (int i = 0; i < n; ++i)
+ d->embedded.at(i)->hide();
+#endif
+}
+
+/*!
+ \internal
+ Make this the active window (i.e., sets the keyboard focus to this
+ window).
+*/
+void QWSWindow::setActiveWindow()
+{
+ qwsServerPrivate->setFocus(this, true);
+#ifndef QT_NO_QWSEMBEDWIDGET
+ const int n = d->embedded.size();
+ for (int i = 0; i < n; ++i)
+ d->embedded.at(i)->setActiveWindow();
+#endif
+}
+
+void QWSWindow::setName(const QString &n)
+{
+ rgnName = n;
+}
+
+/*!
+ \internal
+ Sets the window's caption to \a c.
+*/
+void QWSWindow::setCaption(const QString &c)
+{
+ rgnCaption = c;
+}
+
+
+static int global_focus_time_counter=100;
+
+void QWSWindow::focus(bool get)
+{
+ if (get)
+ last_focus_time = global_focus_time_counter++;
+ if (c) {
+ QWSFocusEvent event;
+ event.simpleData.window = id;
+ event.simpleData.get_focus = get;
+ c->sendEvent(&event);
+ }
+}
+
+void QWSWindow::operation(QWSWindowOperationEvent::Operation o)
+{
+ if (!c)
+ return;
+ QWSWindowOperationEvent event;
+ event.simpleData.window = id;
+ event.simpleData.op = o;
+ c->sendEvent(&event);
+}
+
+/*!
+ \internal
+ Destructor.
+*/
+QWSWindow::~QWSWindow()
+{
+#ifndef QT_NO_QWS_INPUTMETHODS
+ if (current_IM_composing_win == this)
+ current_IM_composing_win = 0;
+#endif
+#ifndef QT_NO_QWSEMBEDWIDGET
+ QWSWindow *embedder = d->embedder;
+ if (embedder) {
+ embedder->d->embedded.removeAll(this);
+ d->embedder = 0;
+ }
+ while (!d->embedded.isEmpty())
+ stopEmbed(d->embedded.first());
+#endif
+
+#ifndef QT_NO_QWS_MULTIPROCESS
+ if (surface && !surface->isBuffered()) {
+ if (c && c->d_func()) // d_func() will be 0 if client is deleted
+ c->removeUnbufferedSurface();
+ }
+#endif
+
+ delete surface;
+ delete d;
+}
+
+/*!
+ \internal
+
+ Returns the region that the window is allowed to draw onto,
+ including any window decorations but excluding regions covered by
+ other windows.
+
+ \sa paintedRegion(), requestedRegion()
+*/
+QRegion QWSWindow::allocatedRegion() const
+{
+ return d->allocatedRegion;
+}
+
+#ifdef QT_QWS_CLIENTBLIT
+QRegion QWSWindow::directPaintRegion() const
+{
+ return d->directPaintRegion;
+}
+
+inline void QWSWindow::setDirectPaintRegion(const QRegion &r)
+{
+ d->directPaintRegion = r;
+}
+#endif
+
+/*!
+ \internal
+
+ Returns the region that the window is known to have drawn into.
+
+ \sa allocatedRegion(), requestedRegion()
+*/
+QRegion QWSWindow::paintedRegion() const
+{
+ return (d->painted ? d->allocatedRegion : QRegion());
+}
+
+inline void QWSWindow::setAllocatedRegion(const QRegion &region)
+{
+ d->allocatedRegion = region;
+}
+
+#ifndef QT_NO_QWSEMBEDWIDGET
+inline void QWSWindow::startEmbed(QWSWindow *w)
+{
+ d->embedded.append(w);
+ w->d->embedder = this;
+}
+
+inline void QWSWindow::stopEmbed(QWSWindow *w)
+{
+ w->d->embedder = 0;
+ w->client()->sendEmbedEvent(w->winId(), QWSEmbedEvent::Region, QRegion());
+ d->embedded.removeAll(w);
+}
+#endif // QT_NO_QWSEMBEDWIDGET
+
+/*********************************************************************
+ *
+ * Class: QWSClient
+ *
+ *********************************************************************/
+
+class QWSClientPrivate : public QObjectPrivate
+{
+ Q_DECLARE_PUBLIC(QWSClient)
+
+public:
+ QWSClientPrivate();
+ ~QWSClientPrivate();
+
+ void setLockId(int id);
+ void unlockCommunication();
+
+private:
+#ifndef QT_NO_QWS_MULTIPROCESS
+ QWSLock *clientLock;
+ bool shutdown;
+ int numUnbufferedSurfaces;
+#endif
+ QSet<QByteArray> usedFonts;
+ friend class QWSServerPrivate;
+};
+
+QWSClientPrivate::QWSClientPrivate()
+{
+#ifndef QT_NO_QWS_MULTIPROCESS
+ clientLock = 0;
+ shutdown = false;
+ numUnbufferedSurfaces = 0;
+#endif
+}
+
+QWSClientPrivate::~QWSClientPrivate()
+{
+#ifndef QT_NO_QWS_MULTIPROCESS
+ delete clientLock;
+#endif
+}
+
+void QWSClientPrivate::setLockId(int id)
+{
+#ifdef QT_NO_QWS_MULTIPROCESS
+ Q_UNUSED(id);
+#else
+ clientLock = new QWSLock(id);
+#endif
+}
+
+void QWSClientPrivate::unlockCommunication()
+{
+#ifndef QT_NO_QWS_MULTIPROCESS
+ if (clientLock)
+ clientLock->unlock(QWSLock::Communication);
+#endif
+}
+
+/*!
+ \class QWSClient
+ \ingroup qws
+
+ \brief The QWSClient class encapsulates a client process in Qt for Embedded Linux.
+
+ When you run a \l{Qt for Embedded Linux} application, it either runs as a
+ server or connects to an existing server. The server and client
+ processes have different responsibilities: The client process
+ performs all application specific operations. The server process
+ is responsible for managing the clients as well as taking care of
+ the pointer handling, character input, and screen output. In
+ addition, the server provides functionality to handle input
+ methods.
+
+ As applications add and remove windows, the server process
+ maintains information about each window. In \l{Qt for Embedded Linux},
+ top-level windows are encapsulated as QWSWindow objects. A list of
+ the current windows can be retrieved using the
+ QWSServer::clientWindows() function, and each window can tell
+ which client that owns it through its QWSWindow::client()
+ function.
+
+ A QWSClient object has an unique ID that can be retrieved using
+ its clientId() function. QWSClient also provides the identity()
+ function which typically returns the name of this client's running
+ application.
+
+ \sa QWSServer, QWSWindow, {Qt for Embedded Linux Architecture}
+*/
+
+/*!
+ \internal
+*/
+//always use frame buffer
+QWSClient::QWSClient(QObject* parent, QWS_SOCK_BASE* sock, int id)
+ : QObject(*new QWSClientPrivate, parent), command(0), cid(id)
+{
+#ifdef QT_NO_QWS_MULTIPROCESS
+ Q_UNUSED(sock);
+ isClosed = false;
+#else
+ csocket = 0;
+ if (!sock) {
+ socketDescriptor = -1;
+ isClosed = false;
+ } else {
+ csocket = static_cast<QWSSocket*>(sock); //###
+ isClosed = false;
+
+ csocket->flush();
+ socketDescriptor = csocket->socketDescriptor();
+ connect(csocket, SIGNAL(readyRead()), this, SIGNAL(readyRead()));
+ connect(csocket, SIGNAL(disconnected()), this, SLOT(closeHandler()));
+ connect(csocket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(errorHandler()));
+ }
+#endif //QT_NO_QWS_MULTIPROCESS
+}
+
+/*!
+ \internal
+*/
+QWSClient::~QWSClient()
+{
+ qDeleteAll(cursors);
+ delete command;
+#ifndef QT_NO_QWS_MULTIPROCESS
+ delete csocket;
+#endif
+}
+
+#ifndef QT_NO_QWS_MULTIPROCESS
+void QWSClient::removeUnbufferedSurface()
+{
+ Q_D(QWSClient);
+ --d->numUnbufferedSurfaces;
+}
+
+void QWSClient::addUnbufferedSurface()
+{
+ Q_D(QWSClient);
+ ++d->numUnbufferedSurfaces;
+}
+#endif // QT_NO_QWS_MULTIPROCESS
+
+/*!
+ \internal
+*/
+void QWSClient::setIdentity(const QString& i)
+{
+ id = i;
+}
+
+void QWSClient::closeHandler()
+{
+ isClosed = true;
+ emit connectionClosed();
+}
+
+void QWSClient::errorHandler()
+{
+#if defined(QWS_SOCKET_DEBUG)
+ qDebug("Client %p error %s", this, csocket ? csocket->errorString().toLatin1().constData() : "(no socket)");
+#endif
+ isClosed = true;
+//####Do we need to clean out the pipes?
+
+ emit connectionClosed();
+}
+
+/*!
+ \internal
+*/
+int QWSClient::socket() const
+{
+ return socketDescriptor;
+}
+
+/*!
+ \internal
+*/
+void QWSClient::sendEvent(QWSEvent* event)
+{
+#ifndef QT_NO_QWS_MULTIPROCESS
+ if (csocket) {
+ // qDebug() << "QWSClient::sendEvent type " << event->type << " socket state " << csocket->state();
+ if ((QAbstractSocket::SocketState)(csocket->state()) == QAbstractSocket::ConnectedState) {
+ event->write(csocket);
+ }
+ }
+ else
+#endif
+ {
+ qt_client_enqueue(event);
+ }
+}
+
+/*!
+ \internal
+*/
+void QWSClient::sendRegionEvent(int winid, QRegion rgn, int type
+#ifdef QT_QWS_CLIENTBLIT
+ , int id
+#endif
+ )
+{
+#ifndef QT_NO_QWS_MULTIPROCESS
+ Q_D(QWSClient);
+ if (d->clientLock)
+ d->clientLock->lock(QWSLock::RegionEvent);
+#endif
+
+ QWSRegionEvent event;
+ event.setData(winid, rgn, type);
+#ifdef QT_QWS_CLIENTBLIT
+ event.simpleData.id = id;
+#endif
+
+// qDebug() << "Sending Region event to" << winid << "rgn" << rgn << "type" << type;
+
+ sendEvent(&event);
+}
+
+extern int qt_servershmid;
+
+/*!
+ \internal
+*/
+void QWSClient::sendConnectedEvent(const char *display_spec)
+{
+ QWSConnectedEvent event;
+ event.simpleData.window = 0;
+ event.simpleData.len = strlen(display_spec) + 1;
+ event.simpleData.clientId = cid;
+ event.simpleData.servershmid = qt_servershmid;
+ char * tmp=(char *)display_spec;
+ event.setData(tmp, event.simpleData.len);
+ sendEvent(&event);
+}
+
+/*!
+ \internal
+*/
+void QWSClient::sendMaxWindowRectEvent(const QRect &rect)
+{
+ QWSMaxWindowRectEvent event;
+ event.simpleData.window = 0;
+ event.simpleData.rect = rect;
+ sendEvent(&event);
+}
+
+/*!
+ \internal
+*/
+#ifndef QT_NO_QWS_PROPERTIES
+void QWSClient::sendPropertyNotifyEvent(int property, int state)
+{
+ QWSPropertyNotifyEvent event;
+ event.simpleData.window = 0; // not used yet
+ event.simpleData.property = property;
+ event.simpleData.state = state;
+ sendEvent(&event);
+}
+
+/*!
+ \internal
+*/
+void QWSClient::sendPropertyReplyEvent(int property, int len, const char *data)
+{
+ QWSPropertyReplyEvent event;
+ event.simpleData.window = 0; // not used yet
+ event.simpleData.property = property;
+ event.simpleData.len = len;
+ event.setData(data, len);
+ sendEvent(&event);
+}
+#endif //QT_NO_QWS_PROPERTIES
+
+/*!
+ \internal
+*/
+void QWSClient::sendSelectionClearEvent(int windowid)
+{
+ QWSSelectionClearEvent event;
+ event.simpleData.window = windowid;
+ sendEvent(&event);
+}
+
+/*!
+ \internal
+*/
+void QWSClient::sendSelectionRequestEvent(QWSConvertSelectionCommand *cmd, int windowid)
+{
+ QWSSelectionRequestEvent event;
+ event.simpleData.window = windowid;
+ event.simpleData.requestor = cmd->simpleData.requestor;
+ event.simpleData.property = cmd->simpleData.selection;
+ event.simpleData.mimeTypes = cmd->simpleData.mimeTypes;
+ sendEvent(&event);
+}
+
+#ifndef QT_NO_QWSEMBEDWIDGET
+/*!
+ \internal
+*/
+void QWSClient::sendEmbedEvent(int windowid, QWSEmbedEvent::Type type,
+ const QRegion &region)
+{
+ QWSEmbedEvent event;
+ event.setData(windowid, type, region);
+ sendEvent(&event);
+}
+#endif // QT_NO_QWSEMBEDWIDGET
+
+/*!
+ \fn void QWSClient::connectionClosed()
+ \internal
+*/
+
+/*!
+ \fn void QWSClient::readyRead();
+ \internal
+*/
+
+/*!
+ \fn int QWSClient::clientId () const
+
+ Returns an integer uniquely identfying this client.
+*/
+
+/*!
+ \fn QString QWSClient::identity () const
+
+ Returns the name of this client's running application.
+*/
+/*********************************************************************
+ *
+ * Class: QWSServer
+ *
+ *********************************************************************/
+
+/*!
+ \class QWSServer
+ \brief The QWSServer class encapsulates a server process in Qt for Embedded Linux.
+
+ \ingroup qws
+
+ When you run a \l{Qt for Embedded Linux} application, it either runs as a
+ server or connects to an existing server. The server and client
+ processes have different responsibilities: The client process
+ performs all application specific operations. The server process
+ is responsible for managing the clients as well as taking care of
+ the pointer handling, character input, and screen output. In
+ addition, the server provides functionality to handle input
+ methods.
+
+ In \l{Qt for Embedded Linux}, all system generated events are passed to the
+ server application which then propagates the event to the
+ appropriate client. See the \l{Qt for Embedded Linux Architecture}
+ documentation for details.
+
+ Note that this class is instantiated by QApplication for
+ \l{Qt for Embedded Linux} server processes; you should never construct this
+ class yourself. Use the instance() function to retrieve a pointer
+ to the server object.
+
+ Note that the static functions of the QWSServer class can only be
+ used in the server process.
+
+ \tableofcontents
+
+ \section1 Client Administration
+
+ As applications add and remove windows, the server process
+ maintains information about each window. In \l{Qt for Embedded Linux},
+ top-level windows are encapsulated as QWSWindow objects. Each
+ window can tell which client that owns it through its
+ QWSWindow::client() function. Use the clientWindows() function to
+ retrieve a list of the current top-level windows. Given a
+ particular position on the display, the window containing it can
+ be retrieved using the windowAt() function.
+
+ QWSServer also provides the windowEvent() signal which is emitted
+ whenever something happens to a top level window; the WindowEvent
+ enum describes the various types of events that the signal
+ recognizes. In addition, the server class provides the
+ markedText() signal which is emitted whenever some text has been
+ selected in any of the windows, passing the selection as
+ parameter.
+
+ The QCopChannel class and the QCOP communication protocol enable
+ transfer of messages between clients. QWSServer provides the
+ newChannel() and removedChannel() signals that is emitted whenever
+ a new QCopChannel object is created or destroyed, respectively.
+
+ See also: QWSWindow, QWSClient and QCopChannel.
+
+
+ \section1 Mouse Handling
+
+ The mouse driver (represented by an instance of the
+ QWSMouseHandler class) is loaded by the server application when it
+ starts running, using Qt's \l {How to Create Qt Plugins}{plugin
+ system}. A mouse driver receives mouse events from the device and
+ encapsulates each event with an instance of the QWSEvent class
+ which it then passes to the server.
+
+ The openMouse() function opens the mouse devices specified by the
+ QWS_MOUSE_PROTO environment variable, and the setMouseHandler()
+ functions sets the primary mouse driver. Alternatively, the static
+ setDefaultMouse() function provides means of specifying the mouse
+ driver to use if the QWS_MOUSE_PROTO variable is not defined (note
+ that the default is otherwise platform dependent). The primary
+ mouse driver can be retrieved using the static mouseHandler()
+ function. Use the closeMouse() function to delete the mouse
+ drivers.
+
+ In addition, the QWSServer class can control the flow of mouse
+ input using the suspendMouse() and resumeMouse() functions.
+
+ See also: QWSMouseHandler and \l{Qt for Embedded Linux Pointer Handling}.
+
+ \section1 Keyboard Handling
+
+ The keyboard driver (represented by an instance of the
+ QWSKeyboardHandler class) is loaded by the server application when
+ it starts running, using Qt's \l {How to Create Qt Plugins}{plugin
+ system}. A keyboard driver receives keyboard events from the
+ device and encapsulates each event with an instance of the
+ QWSEvent class which it then passes to the server.
+
+ The openKeyboard() function opens the keyboard devices specified
+ by the QWS_KEYBOARD environment variable, and the
+ setKeyboardHandler() functions sets the primary keyboard
+ driver. Alternatively, the static setDefaultKeyboard() function
+ provides means of specifying the keyboard driver to use if the
+ QWS_KEYBOARD variable is not defined (note again that the default
+ is otherwise platform dependent). The primary keyboard driver can
+ be retrieved using the static keyboardHandler() function. Use the
+ closeKeyboard() function to delete the keyboard drivers.
+
+ In addition, the QWSServer class can handle key events from both
+ physical and virtual keyboards using the processKeyEvent() and
+ sendKeyEvent() functions, respectively. Use the
+ addKeyboardFilter() function to filter the key events from
+ physical keyboard drivers, the most recently added filter can be
+ removed and deleted using the removeKeyboardFilter() function.
+
+ See also: QWSKeyboardHandler and \l{Qt for Embedded Linux Character Input}.
+
+ \section1 Display Handling
+
+ When a screen update is required, the server runs through all the
+ top-level windows that intersect with the region that is about to
+ be updated, and ensures that the associated clients have updated
+ their memory buffer. Then the server uses the screen driver
+ (represented by an instance of the QScreen class) to copy the
+ content of the memory to the screen.
+
+ In addition, the QWSServer class provides some means of managing
+ the screen output: Use the refresh() function to refresh the
+ entire display, or alternatively a specified region of it. The
+ enablePainting() function can be used to disable (and enable)
+ painting onto the screen. QWSServer also provide the
+ setMaxWindowRect() function restricting the area of the screen
+ which \l{Qt for Embedded Linux} applications will consider to be the
+ maximum area to use for windows. To set the brush used as the
+ background in the absence of obscuring windows, QWSServer provides
+ the static setBackground() function. The corresponding
+ backgroundBrush() function returns the currently set brush.
+
+ QWSServer also controls the screen saver: Use the setScreenSaver()
+ to install a custom screen saver derived from the QWSScreenSaver
+ class. Once installed, the screensaver can be activated using the
+ screenSaverActivate() function, and the screenSaverActive()
+ function returns its current status. Use the
+ setScreenSaverInterval() function to specify the timeout interval.
+ \l{Qt for Embedded Linux} also supports multilevel screen saving, use the
+ setScreenSaverIntervals() function to specify the various levels
+ and their timeout intervals.
+
+ Finally, the QWSServer class controls the cursor's appearance,
+ i.e., use the setCursorVisible() function to hide or show the
+ cursor, and the isCursorVisible() function to determine whether
+ the cursor is visible on the display or not.
+
+ See also: QScreen and \l{Qt for Embedded Linux Display Management}.
+
+ \section1 Input Method Handling
+
+ Whenever the server receives an event, it queries its stack of
+ top-level windows to find the window containing the event's
+ position (each window can identify the client application that
+ created it). Then the server forwards the event to the appropriate
+ client. If an input method is installed, it is used as a filter
+ between the server and the client application.
+
+ Derive from the QWSInputMethod class to create custom input
+ methods, and use the server's setCurrentInputMethod() function to
+ install it. Use the sendIMEvent() and sendIMQuery() functions to
+ send input method events and queries.
+
+ QWSServer provides the IMMouse enum describing the various mouse
+ events recognized by the QWSInputMethod::mouseHandler()
+ function. The latter function allows subclasses of QWSInputMethod
+ to handle mouse events within the preedit text.
+
+ \sa QWSInputMethod
+*/
+
+/*!
+ \enum QWSServer::IMState
+ \obsolete
+
+ This enum describes the various states of an input method.
+
+ \value IMCompose Composing.
+ \value IMStart Equivalent to IMCompose.
+ \value IMEnd Finished composing.
+
+ \sa QWSInputMethod::sendIMEvent()
+*/
+
+/*!
+ \enum QWSServer::IMMouse
+
+ This enum describes the various types of mouse events recognized
+ by the QWSInputMethod::mouseHandler() function.
+
+ \value MousePress An event generated by pressing a mouse button.
+ \value MouseRelease An event generated by relasing a mouse button.
+ \value MouseMove An event generated by moving the mouse cursor.
+ \value MouseOutside This value is only reserved, i.e., it is not used in
+ current implementations.
+
+ \sa QWSInputMethod, setCurrentInputMethod()
+*/
+
+/*!
+ \enum QWSServer::ServerFlags
+ \internal
+
+ This enum is used to pass various options to the window system
+ server.
+
+ \value DisableKeyboard Ignore all keyboard input.
+ \value DisableMouse Ignore all mouse input.
+*/
+
+/*!
+ \enum QWSServer::WindowEvent
+
+ This enum specifies the various events that can occur in a
+ top-level window.
+
+ \value Create A new window has been created (by the QWidget constructor).
+ \value Destroy The window has been closed and deleted (by the QWidget destructor).
+ \value Hide The window has been hidden using the QWidget::hide() function.
+ \value Show The window has been shown using the QWidget::show() function or similar.
+ \value Raise The window has been raised to the top of the desktop.
+ \value Lower The window has been lowered.
+ \value Geometry The window has changed size or position.
+ \value Active The window has become the active window (i.e., it has keyboard focus).
+ \value Name The window has been named.
+
+ \sa windowEvent()
+*/
+
+/*!
+ \fn void QWSServer::markedText(const QString &selection)
+
+ This signal is emitted whenever some text is selected in any of
+ the running applications, passing the selected text in the \a
+ selection parameter.
+
+ \sa windowEvent()
+*/
+
+/*!
+ \fn const QList<QWSWindow*> &QWSServer::clientWindows()
+
+ Returns the list of current top-level windows.
+
+ Note that the collection of top-level windows changes as
+ applications add and remove widgets so it should not be stored for
+ future use. The windows are sorted in stacking order from top-most
+ to bottom-most.
+
+ Use the QWSWindow::client() function to retrieve the client
+ application that owns a given window.
+
+ \sa windowAt(), instance()
+*/
+
+/*!
+ \fn void QWSServer::newChannel(const QString& channel)
+
+ This signal is emitted whenever a new QCopChannel object is
+ created, passing the channel's name in the \a channel parameter.
+
+ \sa removedChannel()
+*/
+
+/*!
+ \fn void QWSServer::removedChannel(const QString& channel)
+
+ This signal is emitted immediately after the given the QCopChannel
+ object specified by \a channel, is destroyed.
+
+ Note that a channel is not destroyed until all its listeners have
+ been unregistered.
+
+ \sa newChannel()
+*/
+
+/*!
+ \fn QWSServer::QWSServer(int flags, QObject *parent)
+ \internal
+
+ Construct a QWSServer object with the given \a parent. The \a
+ flags are used for keyboard and mouse settings.
+
+ \warning This class is instantiated by QApplication for
+ \l{Qt for Embedded Linux} server processes. You should never construct
+ this class yourself.
+
+ \sa {Running Applications}
+*/
+
+/*!
+ \fn static QWSServer* QWSServer::instance()
+ \since 4.2
+
+ Returns a pointer to the server instance.
+
+ Note that the pointer will be 0 if the application is not the
+ server, i.e., if the QApplication::type() function doesn't return
+ QApplication::GuiServer.
+
+ \sa clientWindows(), windowAt()
+*/
+
+struct QWSCommandStruct
+{
+ QWSCommandStruct(QWSCommand *c, QWSClient *cl) :command(c),client(cl){}
+ ~QWSCommandStruct() { delete command; }
+
+ QWSCommand *command;
+ QWSClient *client;
+
+};
+
+QWSServer::QWSServer(int flags, QObject *parent) :
+ QObject(*new QWSServerPrivate, parent)
+{
+ Q_D(QWSServer);
+ d->initServer(flags);
+}
+
+#ifdef QT3_SUPPORT
+/*!
+ Use the two-argument overload and call the
+ QObject::setObjectName() function instead.
+*/
+QWSServer::QWSServer(int flags, QObject *parent, const char *name) :
+ QObject(*new QWSServerPrivate, parent)
+{
+ Q_D(QWSServer);
+ setObjectName(QString::fromAscii(name));
+ d->initServer(flags);
+}
+#endif
+
+
+#ifndef QT_NO_QWS_MULTIPROCESS
+static void ignoreSignal(int) {} // Used to eat SIGPIPE signals below
+#endif
+
+bool QWSServerPrivate::screensaverblockevent( int index, int *screensaverinterval, bool isDown )
+{
+ static bool ignoreEvents[2] = { false, false };
+ if ( isDown ) {
+ if ( !ignoreEvents[index] ) {
+ bool wake = false;
+ if ( screensaverintervals ) {
+ if ( screensaverinterval != screensaverintervals ) {
+ wake = true;
+ }
+ }
+ if ( screensaverblockevents && wake ) {
+#ifdef EVENT_BLOCK_DEBUG
+ qDebug( "waking the screen" );
+#endif
+ ignoreEvents[index] = true;
+ } else if ( !screensaverblockevents ) {
+#ifdef EVENT_BLOCK_DEBUG
+ qDebug( "the screen was already awake" );
+#endif
+ ignoreEvents[index] = false;
+ }
+ }
+ } else {
+ if ( ignoreEvents[index] ) {
+#ifdef EVENT_BLOCK_DEBUG
+ qDebug( "mouseup?" );
+#endif
+ ignoreEvents[index] = false;
+ return true;
+ }
+ }
+ return ignoreEvents[index];
+}
+
+void QWSServerPrivate::initServer(int flags)
+{
+ Q_Q(QWSServer);
+ Q_ASSERT(!qwsServer);
+ qwsServer = q;
+ qwsServerPrivate = this;
+ disablePainting = false;
+#ifndef QT_NO_QWS_MULTIPROCESS
+ ssocket = new QWSServerSocket(qws_qtePipeFilename(), q);
+ QObject::connect(ssocket, SIGNAL(newConnection()), q, SLOT(_q_newConnection()));
+
+ if ( !ssocket->isListening()) {
+ perror("QWSServerPrivate::initServer: server socket not listening");
+ qFatal("Failed to bind to %s", qws_qtePipeFilename().toLatin1().constData());
+ }
+
+ struct linger tmp;
+ tmp.l_onoff=1;
+ tmp.l_linger=0;
+ setsockopt(ssocket->socketDescriptor(),SOL_SOCKET,SO_LINGER,(char *)&tmp,sizeof(tmp));
+
+
+ signal(SIGPIPE, ignoreSignal); //we get it when we read
+#endif
+ focusw = 0;
+ mouseGrabber = 0;
+ mouseGrabbing = false;
+ inputMethodMouseGrabbed = false;
+ keyboardGrabber = 0;
+ keyboardGrabbing = false;
+#ifndef QT_NO_QWS_CURSOR
+ haveviscurs = false;
+ cursor = 0;
+ nextCursor = 0;
+#endif
+
+#ifndef QT_NO_QWS_MULTIPROCESS
+
+ if (!geteuid()) {
+#if !defined(Q_OS_FREEBSD) && !defined(Q_OS_SOLARIS) && !defined(Q_OS_DARWIN) && !defined(QT_LINUXBASE)
+ if(mount(0,"/var/shm", "shm", 0, 0)) {
+ /* This just confuses people with 2.2 kernels
+ if (errno != EBUSY)
+ qDebug("Failed mounting shm fs on /var/shm: %s",strerror(errno));
+ */
+ }
+#endif
+ }
+#endif
+
+ // no selection yet
+ selectionOwner.windowid = -1;
+ selectionOwner.time.set(-1, -1, -1, -1);
+
+ cleanupFontsDir();
+
+ // initialize the font database
+ // from qfontdatabase_qws.cpp
+ extern void qt_qws_init_fontdb();
+ qt_qws_init_fontdb();
+
+ openDisplay();
+
+ screensavertimer = new QTimer(q);
+ screensavertimer->setSingleShot(true);
+ QObject::connect(screensavertimer, SIGNAL(timeout()), q, SLOT(_q_screenSaverTimeout()));
+ _q_screenSaverWake();
+
+ clientMap[-1] = new QWSClient(q, 0, 0);
+
+ if (!bgBrush)
+ bgBrush = new QBrush(QColor(0x20, 0xb0, 0x50));
+
+ initializeCursor();
+
+ // input devices
+ if (!(flags&QWSServer::DisableMouse)) {
+ q->openMouse();
+ }
+#ifndef QT_NO_QWS_KEYBOARD
+ if (!(flags&QWSServer::DisableKeyboard)) {
+ q->openKeyboard();
+ }
+#endif
+
+#if !defined(QT_NO_SOUND) && !defined(QT_EXTERNAL_SOUND_SERVER) && !defined(Q_OS_DARWIN)
+ soundserver = new QWSSoundServer(q);
+#endif
+}
+
+/*!
+ \internal
+ Destructs this server.
+*/
+QWSServer::~QWSServer()
+{
+ closeMouse();
+#ifndef QT_NO_QWS_KEYBOARD
+ closeKeyboard();
+#endif
+ d_func()->cleanupFonts(/*force =*/true);
+}
+
+/*!
+ \internal
+ */
+void QWSServer::timerEvent(QTimerEvent *e)
+{
+ Q_D(QWSServer);
+ if (e->timerId() == d->fontCleanupTimer.timerId()) {
+ d->cleanupFonts();
+ d->fontCleanupTimer.stop();
+ } else {
+ QObject::timerEvent(e);
+ }
+}
+
+const QList<QWSWindow*> &QWSServer::clientWindows()
+{
+ Q_D(QWSServer);
+ return d->windows;
+}
+
+/*!
+ \internal
+*/
+void QWSServerPrivate::releaseMouse(QWSWindow* w)
+{
+ if (w && mouseGrabber == w) {
+ mouseGrabber = 0;
+ mouseGrabbing = false;
+#ifndef QT_NO_QWS_CURSOR
+ if (nextCursor) {
+ // Not grabbing -> set the correct cursor
+ setCursor(nextCursor);
+ nextCursor = 0;
+ }
+#endif
+ }
+}
+
+/*!
+ \internal
+*/
+void QWSServerPrivate::releaseKeyboard(QWSWindow* w)
+{
+ if (keyboardGrabber == w) {
+ keyboardGrabber = 0;
+ keyboardGrabbing = false;
+ }
+}
+
+void QWSServerPrivate::handleWindowClose(QWSWindow *w)
+{
+ w->shuttingDown();
+ if (focusw == w)
+ setFocus(w,false);
+ if (mouseGrabber == w)
+ releaseMouse(w);
+ if (keyboardGrabber == w)
+ releaseKeyboard(w);
+}
+
+
+#ifndef QT_NO_QWS_MULTIPROCESS
+/*!
+ \internal
+*/
+void QWSServerPrivate::_q_newConnection()
+{
+ Q_Q(QWSServer);
+ while (QWS_SOCK_BASE *sock = ssocket->nextPendingConnection()) {
+ int socket = sock->socketDescriptor();
+ sock->setParent(0);
+
+ QWSClient *client = new QWSClient(q,sock, get_object_id());
+ clientMap[socket] = client;
+
+#ifndef QT_NO_SXE
+#ifdef QTRANSPORTAUTH_DEBUG
+ qDebug( "Transport auth connected: unix stream socket %d", socket );
+#endif
+ // get a handle to the per-process authentication service
+ QTransportAuth *a = QTransportAuth::getInstance();
+
+ // assert that this transport is trusted
+ QTransportAuth::Data *d = a->connectTransport(
+ QTransportAuth::UnixStreamSock |
+ QTransportAuth::Trusted, socket );
+
+ QAuthDevice *ad = a->recvBuf( d, sock );
+ ad->setClient(client);
+
+ QObject::connect(ad, SIGNAL(readyRead()),
+ q, SLOT(_q_doClient()));
+
+ QObject::connect(client, SIGNAL(connectionClosed()),
+ q, SLOT(_q_clientClosed()));
+#else
+ QObject::connect(client, SIGNAL(readyRead()),
+ q, SLOT(_q_doClient()));
+ QObject::connect(client, SIGNAL(connectionClosed()),
+ q, SLOT(_q_clientClosed()));
+#endif // QT_NO_SXE
+
+ client->sendConnectedEvent(qws_display_spec.constData());
+
+ if (clientMap.contains(socket)) {
+ QList<QScreen*> screens = qt_screen->subScreens();
+ if (screens.isEmpty())
+ screens.append(qt_screen);
+ for (int i = 0; i < screens.size(); ++i) {
+ const QApplicationPrivate *ap = QApplicationPrivate::instance();
+ QScreen *screen = screens.at(i);
+ const QRect rect = ap->maxWindowRect(screen);
+ if (!rect.isEmpty())
+ client->sendMaxWindowRectEvent(rect);
+ if (screen->isTransformed()) {
+ QWSScreenTransformationEvent event;
+ event.simpleData.screen = i;
+ event.simpleData.transformation = screen->transformOrientation();
+ client->sendEvent(&event);
+ }
+ }
+ }
+
+ // pre-provide some object id's
+ QWSCreateCommand cmd(30);
+ invokeCreate(&cmd, client);
+ }
+}
+/*!
+ \internal
+*/
+void QWSServerPrivate::_q_clientClosed()
+{
+ Q_Q(QWSServer);
+ QWSClient* cl = (QWSClient*)q->sender();
+
+ // Remove any queued commands for this client
+ int i = 0;
+ while (i < commandQueue.size()) {
+ QWSCommandStruct *cs = commandQueue.at(i);
+ if (cs->client == cl) {
+ commandQueue.removeAt(i);
+ delete cs;
+ } else {
+ ++i;
+ }
+ }
+
+#ifndef QT_NO_COP
+ // Enfore unsubscription from all channels.
+ QCopChannel::detach(cl);
+#endif
+
+ // Shut down all windows for this client
+ for (int i = 0; i < windows.size(); ++i) {
+ QWSWindow* w = windows.at(i);
+ if (w->forClient(cl))
+ w->shuttingDown();
+ }
+
+ // Delete all windows for this client
+ QRegion exposed;
+ i = 0;
+ while (i < windows.size()) {
+ QWSWindow* w = windows.at(i);
+ if (w->forClient(cl)) {
+ windows.takeAt(i);
+ w->c = 0; //so we don't send events to it anymore
+ releaseMouse(w);
+ releaseKeyboard(w);
+ exposed += w->allocatedRegion();
+// rgnMan->remove(w->allocationIndex());
+ if (focusw == w)
+ setFocus(focusw,0);
+ if (mouseGrabber == w)
+ releaseMouse(w);
+ if (i < nReserved)
+ --nReserved;
+#ifndef QT_NO_QWS_PROPERTIES
+ propertyManager.removeProperties(w->winId());
+#endif
+ emit q->windowEvent(w, QWSServer::Destroy);
+ w->d->state = QWSWindow::Destroyed; //???
+ deletedWindows.append(w);
+ } else {
+ ++i;
+ }
+ }
+ if (deletedWindows.count())
+ QTimer::singleShot(0, q, SLOT(_q_deleteWindowsLater()));
+
+ QWSClientPrivate *clientPrivate = cl->d_func();
+ if (!clientPrivate->shutdown) {
+#if defined(QWS_DEBUG_FONTCLEANUP)
+ qDebug() << "client" << cl->clientId() << "crashed";
+#endif
+ // this would be the place to emit a signal to notify about the
+ // crash of a client
+ crashedClientIds.append(cl->clientId());
+ fontCleanupTimer.start(10, q_func());
+ }
+ clientPrivate->shutdown = true;
+
+ while (!clientPrivate->usedFonts.isEmpty()) {
+ const QByteArray font = *clientPrivate->usedFonts.begin();
+#if defined(QWS_DEBUG_FONTCLEANUP)
+ qDebug() << "dereferencing font" << font << "from disconnected client";
+#endif
+ dereferenceFont(clientPrivate, font);
+ }
+ clientPrivate->usedFonts.clear();
+
+ //qDebug("removing client %d with socket %d", cl->clientId(), cl->socket());
+ clientMap.remove(cl->socket());
+ if (cl == cursorClient)
+ cursorClient = 0;
+ if (qt_screen->clearCacheFunc)
+ (qt_screen->clearCacheFunc)(qt_screen, cl->clientId()); // remove any remaining cache entries.
+ cl->deleteLater();
+
+ update_regions();
+ exposeRegion(exposed);
+}
+
+void QWSServerPrivate::_q_deleteWindowsLater()
+{
+ qDeleteAll(deletedWindows);
+ deletedWindows.clear();
+}
+
+#endif //QT_NO_QWS_MULTIPROCESS
+
+void QWSServerPrivate::referenceFont(QWSClientPrivate *client, const QByteArray &font)
+{
+ if (!client->usedFonts.contains(font)) {
+ client->usedFonts.insert(font);
+
+ ++fontReferenceCount[font];
+#if defined(QWS_DEBUG_FONTCLEANUP)
+ qDebug() << "Client" << client->q_func()->clientId() << "added font" << font;
+ qDebug() << "Refcount is" << fontReferenceCount[font];
+#endif
+ }
+}
+
+void QWSServerPrivate::dereferenceFont(QWSClientPrivate *client, const QByteArray &font)
+{
+ if (client->usedFonts.contains(font)) {
+ client->usedFonts.remove(font);
+
+ Q_ASSERT(fontReferenceCount[font]);
+ if (!--fontReferenceCount[font] && !fontCleanupTimer.isActive())
+ fontCleanupTimer.start(FontCleanupInterval, q_func());
+
+#if defined(QWS_DEBUG_FONTCLEANUP)
+ qDebug() << "Client" << client->q_func()->clientId() << "removed font" << font;
+ qDebug() << "Refcount is" << fontReferenceCount[font];
+#endif
+ }
+}
+
+static void cleanupFontsDir()
+{
+ static bool dontDelete = !qgetenv("QWS_KEEP_FONTS").isEmpty();
+ if (dontDelete)
+ return;
+
+ extern QString qws_fontCacheDir();
+ QDir dir(qws_fontCacheDir(), QLatin1String("*.qsf"));
+ for (uint i = 0; i < dir.count(); ++i) {
+#if defined(QWS_DEBUG_FONTCLEANUP)
+ qDebug() << "removing stale font file" << dir[i];
+#endif
+ dir.remove(dir[i]);
+ }
+}
+
+void QWSServerPrivate::cleanupFonts(bool force)
+{
+ static bool dontDelete = !qgetenv("QWS_KEEP_FONTS").isEmpty();
+ if (dontDelete)
+ return;
+
+#if defined(QWS_DEBUG_FONTCLEANUP)
+ qDebug() << "cleanupFonts()";
+#endif
+ QMap<QByteArray, int>::Iterator it = fontReferenceCount.begin();
+ while (it != fontReferenceCount.end()) {
+ if (it.value() && !force) {
+ ++it;
+ continue;
+ }
+
+ const QByteArray &fontName = it.key();
+#if defined(QWS_DEBUG_FONTCLEANUP)
+ qDebug() << "removing unused font file" << fontName;
+#endif
+ QFile::remove(QFile::decodeName(fontName));
+ sendFontRemovedEvent(fontName);
+
+ it = fontReferenceCount.erase(it);
+ }
+
+ if (crashedClientIds.isEmpty())
+ return;
+
+ QList<QByteArray> removedFonts;
+#if !defined(QT_NO_QWS_QPF2) && !defined(QT_FONTS_ARE_RESOURCES)
+ removedFonts = QFontEngineQPF::cleanUpAfterClientCrash(crashedClientIds);
+#endif
+ crashedClientIds.clear();
+
+ for (int i = 0; i < removedFonts.count(); ++i)
+ sendFontRemovedEvent(removedFonts.at(i));
+}
+
+void QWSServerPrivate::sendFontRemovedEvent(const QByteArray &font)
+{
+ QWSFontEvent event;
+ event.simpleData.type = QWSFontEvent::FontRemoved;
+ event.setData(font.constData(), font.length(), false);
+
+ QMap<int,QWSClient*>::const_iterator it = clientMap.constBegin();
+ for (; it != clientMap.constEnd(); ++it)
+ (*it)->sendEvent(&event);
+}
+
+/*!
+ \internal
+*/
+QWSCommand* QWSClient::readMoreCommand()
+{
+#ifndef QT_NO_QWS_MULTIPROCESS
+ QIODevice *socket = 0;
+#endif
+#ifndef QT_NO_SXE
+ if (socketDescriptor != -1) // not server socket
+ socket = QTransportAuth::getInstance()->passThroughByClient( this );
+#if QTRANSPORTAUTH_DEBUG
+ if (socket) {
+ char displaybuf[1024];
+ qint64 bytes = socket->bytesAvailable();
+ if ( bytes > 511 ) bytes = 511;
+ hexstring( displaybuf, ((unsigned char *)(reinterpret_cast<QAuthDevice*>(socket)->buffer().constData())), bytes );
+ qDebug( "readMoreCommand: %lli bytes - %s", socket->bytesAvailable(), displaybuf );
+ }
+#endif
+#endif // QT_NO_SXE
+
+#ifndef QT_NO_QWS_MULTIPROCESS
+ if (!socket)
+ socket = csocket; // server socket
+ if (socket) {
+ // read next command
+ if (!command) {
+ int command_type = qws_read_uint(socket);
+
+ if (command_type >= 0)
+ command = QWSCommand::factory(command_type);
+ }
+ if (command) {
+ if (command->read(socket)) {
+ // Finished reading a whole command.
+ QWSCommand* result = command;
+ command = 0;
+ return result;
+ }
+ }
+
+ // Not finished reading a whole command.
+ return 0;
+ } else
+#endif // QT_NO_QWS_MULTIPROCESS
+ {
+ QList<QWSCommand*> *serverQueue = qt_get_server_queue();
+ return serverQueue->isEmpty() ? 0 : serverQueue->takeFirst();
+ }
+}
+
+
+/*!
+ \internal
+*/
+void QWSServer::processEventQueue()
+{
+ if (qwsServerPrivate)
+ qwsServerPrivate->doClient(qwsServerPrivate->clientMap.value(-1));
+}
+
+
+#ifndef QT_NO_QWS_MULTIPROCESS
+void QWSServerPrivate::_q_doClient()
+{
+ Q_Q(QWSServer);
+
+ QWSClient* client;
+#ifndef QT_NO_SXE
+ QAuthDevice *ad = qobject_cast<QAuthDevice*>(q->sender());
+ if (ad)
+ client = (QWSClient*)ad->client();
+ else
+#endif
+ client = (QWSClient*)q->sender();
+
+ if (doClientIsActive) {
+ pendingDoClients.append(client);
+ return;
+ }
+ doClientIsActive = true;
+
+ doClient(client);
+
+ while (!pendingDoClients.isEmpty()) {
+ doClient(pendingDoClients.takeFirst());
+ }
+
+ doClientIsActive = false;
+}
+#endif // QT_NO_QWS_MULTIPROCESS
+
+void QWSServerPrivate::doClient(QWSClient *client)
+{
+ QWSCommand* command=client->readMoreCommand();
+
+ while (command) {
+ QWSCommandStruct *cs = new QWSCommandStruct(command, client);
+ commandQueue.append(cs);
+ // Try for some more...
+ command=client->readMoreCommand();
+ }
+
+ while (!commandQueue.isEmpty()) {
+ QWSCommandStruct *cs = commandQueue.takeAt(0);
+ switch (cs->command->type) {
+ case QWSCommand::Identify:
+ invokeIdentify((QWSIdentifyCommand*)cs->command, cs->client);
+ break;
+ case QWSCommand::Create:
+ invokeCreate((QWSCreateCommand*)cs->command, cs->client);
+ break;
+#ifndef QT_NO_QWS_MULTIPROCESS
+ case QWSCommand::Shutdown:
+ cs->client->d_func()->shutdown = true;
+ break;
+#endif
+ case QWSCommand::RegionName:
+ invokeRegionName((QWSRegionNameCommand*)cs->command, cs->client);
+ break;
+ case QWSCommand::Region:
+ invokeRegion((QWSRegionCommand*)cs->command, cs->client);
+ cs->client->d_func()->unlockCommunication();
+ break;
+ case QWSCommand::RegionMove:
+ invokeRegionMove((QWSRegionMoveCommand*)cs->command, cs->client);
+ cs->client->d_func()->unlockCommunication();
+ break;
+ case QWSCommand::RegionDestroy:
+ invokeRegionDestroy((QWSRegionDestroyCommand*)cs->command, cs->client);
+ break;
+#ifndef QT_NO_QWS_PROPERTIES
+ case QWSCommand::AddProperty:
+ invokeAddProperty((QWSAddPropertyCommand*)cs->command);
+ break;
+ case QWSCommand::SetProperty:
+ invokeSetProperty((QWSSetPropertyCommand*)cs->command);
+ break;
+ case QWSCommand::RemoveProperty:
+ invokeRemoveProperty((QWSRemovePropertyCommand*)cs->command);
+ break;
+ case QWSCommand::GetProperty:
+ invokeGetProperty((QWSGetPropertyCommand*)cs->command, cs->client);
+ break;
+#endif
+ case QWSCommand::SetSelectionOwner:
+ invokeSetSelectionOwner((QWSSetSelectionOwnerCommand*)cs->command);
+ break;
+ case QWSCommand::RequestFocus:
+ invokeSetFocus((QWSRequestFocusCommand*)cs->command, cs->client);
+ break;
+ case QWSCommand::ChangeAltitude:
+ invokeSetAltitude((QWSChangeAltitudeCommand*)cs->command,
+ cs->client);
+ cs->client->d_func()->unlockCommunication();
+ break;
+ case QWSCommand::SetOpacity:
+ invokeSetOpacity((QWSSetOpacityCommand*)cs->command,
+ cs->client);
+ break;
+
+#ifndef QT_NO_QWS_CURSOR
+ case QWSCommand::DefineCursor:
+ invokeDefineCursor((QWSDefineCursorCommand*)cs->command, cs->client);
+ break;
+ case QWSCommand::SelectCursor:
+ invokeSelectCursor((QWSSelectCursorCommand*)cs->command, cs->client);
+ break;
+ case QWSCommand::PositionCursor:
+ invokePositionCursor((QWSPositionCursorCommand*)cs->command, cs->client);
+ break;
+#endif
+ case QWSCommand::GrabMouse:
+ invokeGrabMouse((QWSGrabMouseCommand*)cs->command, cs->client);
+ break;
+ case QWSCommand::GrabKeyboard:
+ invokeGrabKeyboard((QWSGrabKeyboardCommand*)cs->command, cs->client);
+ break;
+#if !defined(QT_NO_SOUND) && !defined(Q_OS_DARWIN)
+ case QWSCommand::PlaySound:
+ invokePlaySound((QWSPlaySoundCommand*)cs->command, cs->client);
+ break;
+#endif
+#ifndef QT_NO_COP
+ case QWSCommand::QCopRegisterChannel:
+ invokeRegisterChannel((QWSQCopRegisterChannelCommand*)cs->command,
+ cs->client);
+ break;
+ case QWSCommand::QCopSend:
+ invokeQCopSend((QWSQCopSendCommand*)cs->command, cs->client);
+ break;
+#endif
+#ifndef QT_NO_QWS_INPUTMETHODS
+ case QWSCommand::IMUpdate:
+ invokeIMUpdate((QWSIMUpdateCommand*)cs->command, cs->client);
+ break;
+ case QWSCommand::IMResponse:
+ invokeIMResponse((QWSIMResponseCommand*)cs->command, cs->client);
+ break;
+ case QWSCommand::IMMouse:
+ {
+ if (current_IM) {
+ QWSIMMouseCommand *cmd = (QWSIMMouseCommand *) cs->command;
+ current_IM->mouseHandler(cmd->simpleData.index,
+ cmd->simpleData.state);
+ }
+ }
+ break;
+#endif
+ case QWSCommand::Font:
+ invokeFont((QWSFontCommand *)cs->command, cs->client);
+ break;
+ case QWSCommand::RepaintRegion:
+ invokeRepaintRegion((QWSRepaintRegionCommand*)cs->command,
+ cs->client);
+ cs->client->d_func()->unlockCommunication();
+ break;
+#ifndef QT_NO_QWSEMBEDWIDGET
+ case QWSCommand::Embed:
+ invokeEmbed(static_cast<QWSEmbedCommand*>(cs->command),
+ cs->client);
+ break;
+#endif
+ case QWSCommand::ScreenTransform:
+ invokeScreenTransform(static_cast<QWSScreenTransformCommand*>(cs->command),
+ cs->client);
+ break;
+ }
+ delete cs;
+ }
+}
+
+
+void QWSServerPrivate::showCursor()
+{
+#ifndef QT_NO_QWS_CURSOR
+ if (qt_screencursor)
+ qt_screencursor->show();
+#endif
+}
+
+void QWSServerPrivate::hideCursor()
+{
+#ifndef QT_NO_QWS_CURSOR
+ if (qt_screencursor)
+ qt_screencursor->hide();
+#endif
+}
+
+/*!
+ \fn void QWSServer::enablePainting(bool enable)
+
+ Enables painting onto the screen if \a enable is true; otherwise
+ painting is disabled.
+
+ \sa {Qt for Embedded Linux Architecture#Drawing on Screen}{Qt for Embedded Linux
+ Architecture}
+*/
+void QWSServer::enablePainting(bool enable)
+{
+ Q_D(QWSServer);
+
+ if (d->disablePainting == !enable)
+ return;
+
+ d->disablePainting = !enable;
+
+ if (enable) {
+ // Reset the server side allocated regions to ensure update_regions()
+ // will send out region events.
+ for (int i = 0; i < d->windows.size(); ++i) {
+ QWSWindow *w = d->windows.at(i);
+ w->setAllocatedRegion(QRegion());
+#ifdef QT_QWS_CLIENTBLIT
+ w->setDirectPaintRegion(QRegion());
+#endif
+ }
+ d->update_regions();
+ d->showCursor();
+ } else {
+ // Disable painting by clients by taking away their allocated region.
+ // To ensure mouse events are still delivered to the correct windows,
+ // the allocated regions are not modified on the server.
+ for (int i = 0; i < d->windows.size(); ++i) {
+ QWSWindow *w = d->windows.at(i);
+ w->client()->sendRegionEvent(w->winId(), QRegion(),
+ QWSRegionEvent::Allocation);
+#ifdef QT_QWS_CLIENTBLIT
+ w->client()->sendRegionEvent(w->winId(), QRegion(),
+ QWSRegionEvent::DirectPaint);
+#endif
+ }
+ d->hideCursor();
+ }
+}
+
+/*!
+ Refreshes the display by making the screen driver update the
+ entire display.
+
+ \sa QScreen::exposeRegion()
+*/
+void QWSServer::refresh()
+{
+ Q_D(QWSServer);
+ d->exposeRegion(QScreen::instance()->region());
+//### send repaint to non-buffered windows
+}
+
+/*!
+ \fn void QWSServer::refresh(QRegion & region)
+ \overload
+
+ Refreshes the given \a region of the display.
+*/
+void QWSServer::refresh(QRegion & r)
+{
+ Q_D(QWSServer);
+ d->exposeRegion(r);
+//### send repaint to non-buffered windows
+}
+
+/*!
+ \fn void QWSServer::setMaxWindowRect(const QRect& rectangle)
+
+ Sets the maximum area of the screen that \l{Qt for Embedded Linux}
+ applications can use, to be the given \a rectangle.
+
+ Note that this function can only be used in the server process.
+
+ \sa QWidget::showMaximized()
+*/
+void QWSServer::setMaxWindowRect(const QRect &rect)
+{
+ QList<QScreen*> subScreens = qt_screen->subScreens();
+ if (subScreens.isEmpty() && qt_screen != 0)
+ subScreens.append(qt_screen);
+
+ for (int i = 0; i < subScreens.size(); ++i) {
+ const QScreen *screen = subScreens.at(i);
+ const QRect r = (screen->region() & rect).boundingRect();
+ if (r.isEmpty())
+ continue;
+
+ QApplicationPrivate *ap = QApplicationPrivate::instance();
+ if (ap->maxWindowRect(screen) != r) {
+ ap->setMaxWindowRect(screen, i, r);
+ qwsServerPrivate->sendMaxWindowRectEvents(r);
+ }
+ }
+}
+
+/*!
+ \internal
+*/
+void QWSServerPrivate::sendMaxWindowRectEvents(const QRect &rect)
+{
+ QMap<int,QWSClient*>::const_iterator it = clientMap.constBegin();
+ for (; it != clientMap.constEnd(); ++it)
+ (*it)->sendMaxWindowRectEvent(rect);
+}
+
+/*!
+ \fn void QWSServer::setDefaultMouse(const char *mouseDriver)
+
+ Sets the mouse driver that will be used if the QWS_MOUSE_PROTO
+ environment variable is not defined, to be the given \a
+ mouseDriver.
+
+ Note that the default is platform-dependent. This function can
+ only be used in the server process.
+
+
+ \sa setMouseHandler(), {Qt for Embedded Linux Pointer Handling}
+*/
+void QWSServer::setDefaultMouse(const char *m)
+{
+ *defaultMouse() = QString::fromAscii(m);
+}
+
+/*!
+ \fn void QWSServer::setDefaultKeyboard(const char *keyboardDriver)
+
+ Sets the keyboard driver that will be used if the QWS_KEYBOARD
+ environment variable is not defined, to be the given \a
+ keyboardDriver.
+
+ Note that the default is platform-dependent. This function can
+ only be used in the server process.
+
+ \sa setKeyboardHandler(), {Qt for Embedded Linux Character Input}
+*/
+void QWSServer::setDefaultKeyboard(const char *k)
+{
+ *defaultKeyboard() = QString::fromAscii(k);
+}
+
+#ifndef QT_NO_QWS_CURSOR
+static bool prevWin;
+#endif
+
+
+extern int *qt_last_x,*qt_last_y;
+
+
+/*!
+ \internal
+
+ Send a mouse event. \a pos is the screen position where the mouse
+ event occurred and \a state is a mask indicating which buttons are
+ pressed.
+
+ \a pos is in device coordinates
+*/
+void QWSServer::sendMouseEvent(const QPoint& pos, int state, int wheel)
+{
+ bool block = qwsServerPrivate->screensaverblockevent(MOUSE, qwsServerPrivate->screensaverinterval, state);
+#ifdef EVENT_BLOCK_DEBUG
+ qDebug() << "sendMouseEvent" << pos.x() << pos.y() << state << (block?"block":"pass");
+#endif
+
+ if (state || wheel)
+ qwsServerPrivate->_q_screenSaverWake();
+
+ if ( block )
+ return;
+
+ QPoint tpos;
+ // transformations
+ if (qt_screen->isTransformed()) {
+ QSize s = QSize(qt_screen->deviceWidth(), qt_screen->deviceHeight());
+ tpos = qt_screen->mapFromDevice(pos, s);
+ } else {
+ tpos = pos;
+ }
+
+ if (qt_last_x) {
+ *qt_last_x = tpos.x();
+ *qt_last_y = tpos.y();
+ }
+ QWSServer::mousePosition = tpos;
+ qwsServerPrivate->mouseState = state;
+
+#ifndef QT_NO_QWS_INPUTMETHODS
+ const int btnMask = Qt::LeftButton | Qt::RightButton | Qt::MidButton;
+ int stroke_count; // number of strokes to keep shown.
+ if (force_reject_strokeIM || !current_IM)
+ {
+ stroke_count = 0;
+ } else {
+ stroke_count = current_IM->filter(tpos, state, wheel);
+ }
+
+ if (stroke_count == 0) {
+ if (state&btnMask)
+ force_reject_strokeIM = true;
+ QWSServerPrivate::sendMouseEventUnfiltered(tpos, state, wheel);
+ }
+ // stop force reject after stroke ends.
+ if (state&btnMask && force_reject_strokeIM)
+ force_reject_strokeIM = false;
+ // on end of stroke, force_rejct
+ // and once a stroke is rejected, do not try again till pen is lifted
+#else
+ QWSServerPrivate::sendMouseEventUnfiltered(tpos, state, wheel);
+#endif // end QT_NO_QWS_FSIM
+}
+
+void QWSServerPrivate::sendMouseEventUnfiltered(const QPoint &pos, int state, int wheel)
+{
+ const int btnMask = Qt::LeftButton | Qt::RightButton | Qt::MidButton;
+ QWSMouseEvent event;
+
+ QWSWindow *win = qwsServer->windowAt(pos);
+
+ QWSClient *serverClient = qwsServerPrivate->clientMap.value(-1);
+ QWSClient *winClient = win ? win->client() : 0;
+
+
+ bool imMouse = false;
+#ifndef QT_NO_QWS_INPUTMETHODS
+ // check for input method window
+ if (current_IM && current_IM_winId != -1) {
+ QWSWindow *kbw = keyboardGrabber ? keyboardGrabber :
+ qwsServerPrivate->focusw;
+
+ imMouse = kbw == win;
+ if ( !imMouse ) {
+ QWidget *target = winClient == serverClient ?
+ QApplication::widgetAt(pos) : 0;
+ imMouse = target && (target->testAttribute(Qt::WA_InputMethodTransparent));
+ }
+ }
+#endif
+
+ //If grabbing window disappears, grab is still active until
+ //after mouse release.
+ if ( qwsServerPrivate->mouseGrabber && (!imMouse || qwsServerPrivate->inputMethodMouseGrabbed)) {
+ win = qwsServerPrivate->mouseGrabber;
+ winClient = win ? win->client() : 0;
+ }
+ event.simpleData.window = win ? win->id : 0;
+
+#ifndef QT_NO_QWS_CURSOR
+ if (qt_screencursor)
+ qt_screencursor->move(pos.x(),pos.y());
+
+ // Arrow cursor over desktop
+ // prevWin remembers if the last event was over a window
+ if (!win && prevWin) {
+ if (!qwsServerPrivate->mouseGrabber)
+ qwsServerPrivate->setCursor(QWSCursor::systemCursor(Qt::ArrowCursor));
+ else
+ qwsServerPrivate->nextCursor = QWSCursor::systemCursor(Qt::ArrowCursor);
+ prevWin = false;
+ }
+ // reset prevWin
+ if (win && !prevWin)
+ prevWin = true;
+#endif
+
+ if ((state&btnMask) && !qwsServerPrivate->mouseGrabbing) {
+ qwsServerPrivate->mouseGrabber = win;
+ if (imMouse)
+ qwsServerPrivate->inputMethodMouseGrabbed = true;
+ }
+ if (!(state&btnMask))
+ qwsServerPrivate->inputMethodMouseGrabbed = false;
+
+ event.simpleData.x_root=pos.x();
+ event.simpleData.y_root=pos.y();
+ event.simpleData.state=state | qws_keyModifiers;
+ event.simpleData.delta = wheel;
+ event.simpleData.time=qwsServerPrivate->timer.elapsed();
+
+ static int oldstate = 0;
+
+#ifndef QT_NO_QWS_INPUTMETHODS
+ //tell the input method if we click on a different window that is not IM transparent
+ bool isPress = state > oldstate;
+ if (isPress && !imMouse && current_IM && current_IM_winId != -1)
+ current_IM->mouseHandler(-1, QWSServer::MouseOutside);
+#endif
+
+ if (serverClient)
+ serverClient->sendEvent(&event);
+ if (winClient && winClient != serverClient)
+ winClient->sendEvent(&event);
+
+ if ( !imMouse ) {
+ // Make sure that if we leave a window, that window gets one last mouse
+ // event so that it knows the mouse has left.
+ QWSClient *oldClient = qwsServer->d_func()->cursorClient;
+ if (oldClient && oldClient != winClient && oldClient != serverClient) {
+ event.simpleData.state = oldstate | qws_keyModifiers;
+ oldClient->sendEvent(&event);
+ }
+ }
+
+ oldstate = state;
+ if ( !imMouse )
+ qwsServer->d_func()->cursorClient = winClient;
+
+ if (!(state&btnMask) && !qwsServerPrivate->mouseGrabbing)
+ qwsServerPrivate->releaseMouse(qwsServerPrivate->mouseGrabber);
+}
+
+/*!
+ Returns the primary mouse driver.
+
+ Note that this function can only be used in the server process.
+
+ \sa setMouseHandler(), openMouse(), closeMouse()
+*/
+QWSMouseHandler *QWSServer::mouseHandler()
+{
+ if (qwsServerPrivate->mousehandlers.empty())
+ return 0;
+ return qwsServerPrivate->mousehandlers.first();
+}
+
+/*!
+ \since 4.5
+
+ Returns list of all mouse handlers
+
+ Note that this function can only be used in the server process.
+
+ \sa mouseHandler(), setMouseHandler(), openMouse(), closeMouse()
+*/
+const QList<QWSMouseHandler*>& QWSServer::mouseHandlers()
+{
+ return qwsServerPrivate->mousehandlers;
+}
+
+
+// called by QWSMouseHandler constructor, not user code.
+/*!
+ \fn void QWSServer::setMouseHandler(QWSMouseHandler* driver)
+
+ Sets the primary mouse driver to be the given \a driver.
+
+ \l{Qt for Embedded Linux} provides several ready-made mouse drivers, and
+ custom drivers are typically added using Qt's plugin
+ mechanism. See the \l{Qt for Embedded Linux Pointer Handling} documentation
+ for details.
+
+ Note that this function can only be used in the server process.
+
+ \sa mouseHandler(), setDefaultMouse()
+*/
+void QWSServer::setMouseHandler(QWSMouseHandler* mh)
+{
+ if (!mh)
+ return;
+ qwsServerPrivate->mousehandlers.removeAll(mh);
+ qwsServerPrivate->mousehandlers.prepend(mh);
+}
+
+/*!
+ \internal
+ \obsolete
+ Caller owns data in list, and must delete contents
+*/
+QList<QWSInternalWindowInfo*> * QWSServer::windowList()
+{
+ QList<QWSInternalWindowInfo*> * ret=new QList<QWSInternalWindowInfo*>;
+ for (int i=0; i < qwsServerPrivate->windows.size(); ++i) {
+ QWSWindow *window = qwsServerPrivate->windows.at(i);
+ QWSInternalWindowInfo * qwi=new QWSInternalWindowInfo();
+ qwi->winid=window->winId();
+ qwi->clientid=window->client()->clientId();
+ ret->append(qwi);
+ }
+ return ret;
+}
+
+#ifndef QT_NO_COP
+/*!
+ \internal
+*/
+void QWSServerPrivate::sendQCopEvent(QWSClient *c, const QString &ch,
+ const QString &msg, const QByteArray &data,
+ bool response)
+{
+ Q_ASSERT(c);
+
+ QWSQCopMessageEvent event;
+ event.channel = ch.toLatin1();
+ event.message = msg.toLatin1();
+ event.data = data;
+ event.simpleData.is_response = response;
+ event.simpleData.lchannel = ch.length();
+ event.simpleData.lmessage = msg.length();
+ event.simpleData.ldata = data.size();
+ int l = event.simpleData.lchannel + event.simpleData.lmessage +
+ event.simpleData.ldata;
+
+ // combine channel, message and data into one block of raw bytes
+ char *tmp = new char [l];
+ char *d = tmp;
+ memcpy(d, event.channel.constData(), event.simpleData.lchannel);
+ d += event.simpleData.lchannel;
+ memcpy(d, event.message.constData(), event.simpleData.lmessage);
+ d += event.simpleData.lmessage;
+ memcpy(d, data.constData(), event.simpleData.ldata);
+
+ event.setDataDirect(tmp, l);
+
+ c->sendEvent(&event);
+}
+#endif
+
+/*!
+ \fn QWSWindow *QWSServer::windowAt(const QPoint& position)
+
+ Returns the window containing the given \a position.
+
+ Note that if there is no window under the specified point this
+ function returns 0.
+
+ \sa clientWindows(), instance()
+*/
+QWSWindow *QWSServer::windowAt(const QPoint& pos)
+{
+ Q_D(QWSServer);
+ for (int i=0; i<d->windows.size(); ++i) {
+ QWSWindow* w = d->windows.at(i);
+ if (w->allocatedRegion().contains(pos))
+ return w;
+ }
+ return 0;
+}
+
+#ifndef QT_NO_QWS_KEYBOARD
+static int keyUnicode(int keycode)
+{
+ int code = 0xffff;
+
+ if (keycode >= Qt::Key_A && keycode <= Qt::Key_Z)
+ code = keycode - Qt::Key_A + 'a';
+ else if (keycode >= Qt::Key_0 && keycode <= Qt::Key_9)
+ code = keycode - Qt::Key_0 + '0';
+
+ return code;
+}
+#endif
+
+/*!
+ Sends the given key event. The key is identified by its \a unicode
+ value and the given \a keycode, \a modifiers, \a isPress and \a
+ autoRepeat parameters.
+
+ Use this function to send key events generated by "virtual
+ keyboards" (note that the processKeyEvent() function is
+ impelemented using this function).
+
+ The \a keycode parameter is the Qt keycode value as defined by the
+ Qt::Key enum. The \a modifiers is an OR combination of
+ Qt::KeyboardModifier values, indicating whether \gui
+ Shift/Alt/Ctrl keys are pressed. The \a isPress parameter is true
+ if the event is a key press event and \a autoRepeat is true if the
+ event is caused by an auto-repeat mechanism and not an actual key
+ press.
+
+ Note that this function can only be used in the server process.
+
+ \sa processKeyEvent(), {Qt for Embedded Linux Character Input}
+*/
+void QWSServer::sendKeyEvent(int unicode, int keycode, Qt::KeyboardModifiers modifiers,
+ bool isPress, bool autoRepeat)
+{
+ qws_keyModifiers = modifiers;
+
+ if (isPress) {
+ if (keycode != Qt::Key_F34 && keycode != Qt::Key_F35)
+ qwsServerPrivate->_q_screenSaverWake();
+ }
+
+#ifndef QT_NO_QWS_INPUTMETHODS
+
+ if (!current_IM || !current_IM->filter(unicode, keycode, modifiers, isPress, autoRepeat))
+ QWSServerPrivate::sendKeyEventUnfiltered(unicode, keycode, modifiers, isPress, autoRepeat);
+#else
+ QWSServerPrivate::sendKeyEventUnfiltered(unicode, keycode, modifiers, isPress, autoRepeat);
+#endif
+}
+
+void QWSServerPrivate::sendKeyEventUnfiltered(int unicode, int keycode, Qt::KeyboardModifiers modifiers,
+ bool isPress, bool autoRepeat)
+{
+
+ QWSKeyEvent event;
+ QWSWindow *win = keyboardGrabber ? keyboardGrabber :
+ qwsServerPrivate->focusw;
+
+ event.simpleData.window = win ? win->winId() : 0;
+
+ event.simpleData.unicode =
+#ifndef QT_NO_QWS_KEYBOARD
+ unicode < 0 ? keyUnicode(keycode) :
+#endif
+ unicode;
+ event.simpleData.keycode = keycode;
+ event.simpleData.modifiers = modifiers;
+ event.simpleData.is_press = isPress;
+ event.simpleData.is_auto_repeat = autoRepeat;
+
+ QWSClient *serverClient = qwsServerPrivate->clientMap.value(-1);
+ QWSClient *winClient = win ? win->client() : 0;
+ if (serverClient)
+ serverClient->sendEvent(&event);
+ if (winClient && winClient != serverClient)
+ winClient->sendEvent(&event);
+}
+
+/*!
+ \internal
+*/
+void QWSServer::beginDisplayReconfigure()
+{
+ qwsServer->enablePainting(false);
+#ifndef QT_NO_QWS_CURSOR
+ if (qt_screencursor)
+ qt_screencursor->hide();
+#endif
+ QWSDisplay::grab(true);
+ qt_screen->disconnect();
+}
+
+/*!
+ \internal
+*/
+void QWSServer::endDisplayReconfigure()
+{
+ qt_screen->connect(QString());
+ qwsServerPrivate->swidth = qt_screen->deviceWidth();
+ qwsServerPrivate->sheight = qt_screen->deviceHeight();
+
+ QWSDisplay::ungrab();
+#ifndef QT_NO_QWS_CURSOR
+ if (qt_screencursor)
+ qt_screencursor->show();
+#endif
+ QApplicationPrivate *ap = QApplicationPrivate::instance();
+ ap->setMaxWindowRect(qt_screen, 0,
+ QRect(0, 0, qt_screen->width(), qt_screen->height()));
+ QSize olds = qApp->desktop()->size();
+ qApp->desktop()->resize(qt_screen->width(), qt_screen->height());
+ qApp->postEvent(qApp->desktop(), new QResizeEvent(qApp->desktop()->size(), olds));
+ qwsServer->enablePainting(true);
+ qwsServer->refresh();
+ qDebug("Desktop size: %dx%d", qApp->desktop()->width(), qApp->desktop()->height());
+}
+
+void QWSServerPrivate::resetEngine()
+{
+#ifndef QT_NO_QWS_CURSOR
+ if (!qt_screencursor)
+ return;
+ qt_screencursor->hide();
+ qt_screencursor->show();
+#endif
+}
+
+
+#ifndef QT_NO_QWS_CURSOR
+/*!
+ \fn void QWSServer::setCursorVisible(bool visible)
+
+ Shows the cursor if \a visible is true: otherwise the cursor is
+ hidden.
+
+ Note that this function can only be used in the server process.
+
+ \sa isCursorVisible()
+*/
+void QWSServer::setCursorVisible(bool vis)
+{
+ if (qwsServerPrivate && qwsServerPrivate->haveviscurs != vis) {
+ QWSCursor* c = qwsServerPrivate->cursor;
+ qwsServerPrivate->setCursor(QWSCursor::systemCursor(Qt::BlankCursor));
+ qwsServerPrivate->haveviscurs = vis;
+ qwsServerPrivate->setCursor(c);
+ }
+}
+
+/*!
+ Returns true if the cursor is visible; otherwise returns false.
+
+ Note that this function can only be used in the server process.
+
+ \sa setCursorVisible()
+*/
+bool QWSServer::isCursorVisible()
+{
+ return qwsServerPrivate ? qwsServerPrivate->haveviscurs : true;
+}
+#endif
+
+#ifndef QT_NO_QWS_INPUTMETHODS
+
+
+/*!
+ \fn void QWSServer::sendIMEvent(const QInputMethodEvent *event)
+
+ Sends the given input method \a event.
+
+ The \c QInputMethodEvent class is derived from QWSEvent, i.e., it
+ is a QWSEvent object of the QWSEvent::IMEvent type.
+
+ If there is a window actively composing the preedit string, the
+ event is sent to that window. Otherwise, the event is sent to the
+ window currently in focus.
+
+ \sa sendIMQuery(), QWSInputMethod::sendEvent()
+*/
+void QWSServer::sendIMEvent(const QInputMethodEvent *ime)
+{
+ QWSIMEvent event;
+
+ QWSWindow *win = keyboardGrabber ? keyboardGrabber :
+ qwsServerPrivate->focusw;
+
+ //if currently composing then event must go to the composing window
+
+ if (current_IM_composing_win)
+ win = current_IM_composing_win;
+
+ event.simpleData.window = win ? win->winId() : 0;
+ event.simpleData.replaceFrom = ime->replacementStart();;
+ event.simpleData.replaceLength = ime->replacementLength();
+
+ QBuffer buffer;
+ buffer.open(QIODevice::WriteOnly);
+ QDataStream out(&buffer);
+
+ out << ime->preeditString();
+ out << ime->commitString();
+
+ const QList<QInputMethodEvent::Attribute> &attributes = ime->attributes();
+ for (int i = 0; i < attributes.count(); ++i) {
+ const QInputMethodEvent::Attribute &a = attributes.at(i);
+ out << a.type << a.start << a.length << a.value;
+ }
+ event.setData(buffer.data(), buffer.size());
+ QWSClient *serverClient = qwsServerPrivate->clientMap.value(-1);
+ if (serverClient)
+ serverClient->sendEvent(&event);
+ if (win && win->client() && win->client() != serverClient)
+ win->client()->sendEvent(&event);
+
+ current_IM_composing_win = ime->preeditString().isEmpty() ? 0 : win;
+ current_IM_winId = win ? win->winId() : 0;
+}
+
+
+/*!
+ Sends an input method query for the given \a property.
+
+ To receive responses to input method queries, the virtual
+ QWSInputMethod::queryResponse() function must be reimplemented in
+ a QWSInputMethod subclass that is activated using the
+ setCurrentInputMethod() function.
+
+ \sa sendIMEvent(), setCurrentInputMethod()
+*/
+void QWSServer::sendIMQuery(int property)
+{
+ QWSIMQueryEvent event;
+
+ QWSWindow *win = keyboardGrabber ? keyboardGrabber :
+ qwsServerPrivate->focusw;
+ if (current_IM_composing_win)
+ win = current_IM_composing_win;
+
+ event.simpleData.window = win ? win->winId() : 0;
+ event.simpleData.property = property;
+ if (win && win->client())
+ win->client()->sendEvent(&event);
+}
+
+
+
+/*!
+ \fn void QWSServer::setCurrentInputMethod(QWSInputMethod *method)
+
+ Sets the current input method to be the given \a method.
+
+ Note that this function can only be used in the server process.
+
+ \sa sendIMQuery(), sendIMEvent()
+*/
+void QWSServer::setCurrentInputMethod(QWSInputMethod *im)
+{
+ if (current_IM)
+ current_IM->reset(); //??? send an update event instead ?
+ current_IM = im;
+}
+
+/*!
+ \fn static void QWSServer::resetInputMethod()
+
+ \internal
+*/
+
+#endif //QT_NO_QWS_INPUTMETHODS
+
+#ifndef QT_NO_QWS_PROPERTIES
+/*!
+ \internal
+*/
+void QWSServer::sendPropertyNotifyEvent(int property, int state)
+{
+ Q_D(QWSServer);
+ QWSServerPrivate::ClientIterator it = d->clientMap.begin();
+ while (it != d->clientMap.end()) {
+ QWSClient *cl = *it;
+ ++it;
+ cl->sendPropertyNotifyEvent(property, state);
+ }
+}
+#endif
+
+void QWSServerPrivate::invokeIdentify(const QWSIdentifyCommand *cmd, QWSClient *client)
+{
+ client->setIdentity(cmd->id);
+#ifndef QT_NO_QWS_MULTIPROCESS
+ if (client->clientId() > 0)
+ client->d_func()->setLockId(cmd->simpleData.idLock);
+#endif
+}
+
+void QWSServerPrivate::invokeCreate(QWSCreateCommand *cmd, QWSClient *client)
+{
+ QWSCreationEvent event;
+ event.simpleData.objectid = get_object_id(cmd->count);
+ event.simpleData.count = cmd->count;
+ client->sendEvent(&event);
+}
+
+void QWSServerPrivate::invokeRegionName(const QWSRegionNameCommand *cmd, QWSClient *client)
+{
+ Q_Q(QWSServer);
+ QWSWindow* changingw = findWindow(cmd->simpleData.windowid, client);
+ if (changingw && (changingw->name() != cmd->name || changingw->caption() !=cmd->caption)) {
+ changingw->setName(cmd->name);
+ changingw->setCaption(cmd->caption);
+ emit q->windowEvent(changingw, QWSServer::Name);
+ }
+}
+
+void QWSServerPrivate::invokeRegion(QWSRegionCommand *cmd, QWSClient *client)
+{
+#ifdef QWS_REGION_DEBUG
+ qDebug("QWSServer::invokeRegion %d rects (%d)",
+ cmd->simpleData.nrectangles, cmd->simpleData.windowid);
+#endif
+
+ QWSWindow* changingw = findWindow(cmd->simpleData.windowid, 0);
+ if (!changingw) {
+ qWarning("Invalid window handle %08x",cmd->simpleData.windowid);
+ return;
+ }
+ if (!changingw->forClient(client)) {
+ qWarning("Disabled: clients changing other client's window region");
+ return;
+ }
+
+ request_region(cmd->simpleData.windowid, cmd->surfaceKey, cmd->surfaceData,
+ cmd->region);
+}
+
+void QWSServerPrivate::invokeRegionMove(const QWSRegionMoveCommand *cmd, QWSClient *client)
+{
+ Q_Q(QWSServer);
+ QWSWindow* changingw = findWindow(cmd->simpleData.windowid, 0);
+ if (!changingw) {
+ qWarning("invokeRegionMove: Invalid window handle %d",cmd->simpleData.windowid);
+ return;
+ }
+ if (!changingw->forClient(client)) {
+ qWarning("Disabled: clients changing other client's window region");
+ return;
+ }
+
+// changingw->setNeedAck(true);
+ moveWindowRegion(changingw, cmd->simpleData.dx, cmd->simpleData.dy);
+ emit q->windowEvent(changingw, QWSServer::Geometry);
+}
+
+void QWSServerPrivate::invokeRegionDestroy(const QWSRegionDestroyCommand *cmd, QWSClient *client)
+{
+ Q_Q(QWSServer);
+ QWSWindow* changingw = findWindow(cmd->simpleData.windowid, 0);
+ if (!changingw) {
+ qWarning("invokeRegionDestroy: Invalid window handle %d",cmd->simpleData.windowid);
+ return;
+ }
+ if (!changingw->forClient(client)) {
+ qWarning("Disabled: clients changing other client's window region");
+ return;
+ }
+
+ setWindowRegion(changingw, QRegion());
+// rgnMan->remove(changingw->allocationIndex());
+ for (int i = 0; i < windows.size(); ++i) {
+ if (windows.at(i) == changingw) {
+ windows.takeAt(i);
+ if (i < nReserved)
+ --nReserved;
+ break;
+ }
+ }
+
+ handleWindowClose(changingw);
+#ifndef QT_NO_QWS_PROPERTIES
+ propertyManager.removeProperties(changingw->winId());
+#endif
+ emit q->windowEvent(changingw, QWSServer::Destroy);
+ delete changingw;
+}
+
+void QWSServerPrivate::invokeSetFocus(const QWSRequestFocusCommand *cmd, QWSClient *client)
+{
+ int winId = cmd->simpleData.windowid;
+ int gain = cmd->simpleData.flag;
+
+ if (gain != 0 && gain != 1) {
+ qWarning("Only 0(lose) and 1(gain) supported");
+ return;
+ }
+
+ QWSWindow* changingw = findWindow(winId, 0);
+ if (!changingw)
+ return;
+
+ if (!changingw->forClient(client)) {
+ qWarning("Disabled: clients changing other client's focus");
+ return;
+ }
+
+ setFocus(changingw, gain);
+}
+
+void QWSServerPrivate::setFocus(QWSWindow* changingw, bool gain)
+{
+ Q_Q(QWSServer);
+#ifndef QT_NO_QWS_INPUTMETHODS
+ /*
+ This is the logic:
+ QWSWindow *loser = 0;
+ if (gain && focusw != changingw)
+ loser = focusw;
+ else if (!gain && focusw == changingw)
+ loser = focusw;
+ But these five lines can be reduced to one:
+ */
+ if (current_IM) {
+ QWSWindow *loser = (!gain == (focusw==changingw)) ? focusw : 0;
+ if (loser && loser->winId() == current_IM_winId)
+ current_IM->updateHandler(QWSInputMethod::FocusOut);
+ }
+#endif
+ if (gain) {
+ if (focusw != changingw) {
+ if (focusw) focusw->focus(0);
+ focusw = changingw;
+ focusw->focus(1);
+ emit q->windowEvent(focusw, QWSServer::Active);
+ }
+ } else if (focusw == changingw) {
+ if (changingw->client())
+ changingw->focus(0);
+ focusw = 0;
+ // pass focus to window which most recently got it...
+ QWSWindow* bestw=0;
+ for (int i=0; i<windows.size(); ++i) {
+ QWSWindow* w = windows.at(i);
+ if (w != changingw && !w->hidden() &&
+ (!bestw || bestw->focusPriority() < w->focusPriority()))
+ bestw = w;
+ }
+ if (!bestw && changingw->focusPriority()) { // accept focus back?
+ bestw = changingw; // must be the only one
+ }
+ focusw = bestw;
+ if (focusw) {
+ focusw->focus(1);
+ emit q->windowEvent(focusw, QWSServer::Active);
+ }
+ }
+}
+
+
+
+void QWSServerPrivate::invokeSetOpacity(const QWSSetOpacityCommand *cmd, QWSClient *client)
+{
+ Q_UNUSED( client );
+ int winId = cmd->simpleData.windowid;
+ int opacity = cmd->simpleData.opacity;
+
+ QWSWindow* changingw = findWindow(winId, 0);
+
+ if (!changingw) {
+ qWarning("invokeSetOpacity: Invalid window handle %d", winId);
+ return;
+ }
+
+ int altitude = windows.indexOf(changingw);
+ const bool wasOpaque = changingw->isOpaque();
+ changingw->_opacity = opacity;
+ if (wasOpaque != changingw->isOpaque())
+ update_regions();
+ exposeRegion(changingw->allocatedRegion(), altitude);
+}
+
+void QWSServerPrivate::invokeSetAltitude(const QWSChangeAltitudeCommand *cmd,
+ QWSClient *client)
+{
+ Q_UNUSED(client);
+
+ int winId = cmd->simpleData.windowid;
+ int alt = cmd->simpleData.altitude;
+ bool fixed = cmd->simpleData.fixed;
+#if 0
+ qDebug("QWSServer::invokeSetAltitude winId %d alt %d)", winId, alt);
+#endif
+
+ if (alt < -1 || alt > 1) {
+ qWarning("QWSServer::invokeSetAltitude Only lower, raise and stays-on-top supported");
+ return;
+ }
+
+ QWSWindow* changingw = findWindow(winId, 0);
+ if (!changingw) {
+ qWarning("invokeSetAltitude: Invalid window handle %d", winId);
+ return;
+ }
+
+ if (fixed && alt >= 1) {
+ changingw->onTop = true;
+ }
+ if (alt == QWSChangeAltitudeCommand::Lower)
+ changingw->lower();
+ else
+ changingw->raise();
+
+// if (!changingw->forClient(client)) {
+// refresh();
+// }
+}
+
+#ifndef QT_NO_QWS_PROPERTIES
+void QWSServerPrivate::invokeAddProperty(QWSAddPropertyCommand *cmd)
+{
+ propertyManager.addProperty(cmd->simpleData.windowid, cmd->simpleData.property);
+}
+
+void QWSServerPrivate::invokeSetProperty(QWSSetPropertyCommand *cmd)
+{
+ Q_Q(QWSServer);
+ if (propertyManager.setProperty(cmd->simpleData.windowid,
+ cmd->simpleData.property,
+ cmd->simpleData.mode,
+ cmd->data,
+ cmd->rawLen)) {
+ q->sendPropertyNotifyEvent(cmd->simpleData.property,
+ QWSPropertyNotifyEvent::PropertyNewValue);
+#ifndef QT_NO_QWS_INPUTMETHODS
+ if (cmd->simpleData.property == QT_QWS_PROPERTY_MARKEDTEXT) {
+ QString s((const QChar*)cmd->data, cmd->rawLen/2);
+ emit q->markedText(s);
+ }
+#endif
+ }
+}
+
+void QWSServerPrivate::invokeRemoveProperty(QWSRemovePropertyCommand *cmd)
+{
+ Q_Q(QWSServer);
+ if (propertyManager.removeProperty(cmd->simpleData.windowid,
+ cmd->simpleData.property)) {
+ q->sendPropertyNotifyEvent(cmd->simpleData.property,
+ QWSPropertyNotifyEvent::PropertyDeleted);
+ }
+}
+
+
+bool QWSServerPrivate:: get_property(int winId, int property, const char *&data, int &len)
+{
+ return propertyManager.getProperty(winId, property, data, len);
+}
+
+
+void QWSServerPrivate::invokeGetProperty(QWSGetPropertyCommand *cmd, QWSClient *client)
+{
+ const char *data;
+ int len;
+
+ if (propertyManager.getProperty(cmd->simpleData.windowid,
+ cmd->simpleData.property,
+ data, len)) {
+ client->sendPropertyReplyEvent(cmd->simpleData.property, len, data);
+ } else {
+ client->sendPropertyReplyEvent(cmd->simpleData.property, -1, 0);
+ }
+}
+#endif //QT_NO_QWS_PROPERTIES
+
+void QWSServerPrivate::invokeSetSelectionOwner(QWSSetSelectionOwnerCommand *cmd)
+{
+ qDebug("QWSServer::invokeSetSelectionOwner");
+
+ SelectionOwner so;
+ so.windowid = cmd->simpleData.windowid;
+ so.time.set(cmd->simpleData.hour, cmd->simpleData.minute,
+ cmd->simpleData.sec, cmd->simpleData.ms);
+
+ if (selectionOwner.windowid != -1) {
+ QWSWindow *win = findWindow(selectionOwner.windowid, 0);
+ if (win)
+ win->client()->sendSelectionClearEvent(selectionOwner.windowid);
+ else
+ qDebug("couldn't find window %d", selectionOwner.windowid);
+ }
+
+ selectionOwner = so;
+}
+
+void QWSServerPrivate::invokeConvertSelection(QWSConvertSelectionCommand *cmd)
+{
+ qDebug("QWSServer::invokeConvertSelection");
+
+ if (selectionOwner.windowid != -1) {
+ QWSWindow *win = findWindow(selectionOwner.windowid, 0);
+ if (win)
+ win->client()->sendSelectionRequestEvent(cmd, selectionOwner.windowid);
+ else
+ qDebug("couldn't find window %d", selectionOwner.windowid);
+ }
+}
+
+#ifndef QT_NO_QWS_CURSOR
+void QWSServerPrivate::invokeDefineCursor(QWSDefineCursorCommand *cmd, QWSClient *client)
+{
+ if (cmd->simpleData.height > 64 || cmd->simpleData.width > 64) {
+ qDebug("Cannot define cursor size > 64x64");
+ return;
+ }
+
+ delete client->cursors.take(cmd->simpleData.id);
+
+ int dataLen = cmd->simpleData.height * ((cmd->simpleData.width+7) / 8);
+
+ if (dataLen > 0 && cmd->data) {
+ QWSCursor *curs = new QWSCursor(cmd->data, cmd->data + dataLen,
+ cmd->simpleData.width, cmd->simpleData.height,
+ cmd->simpleData.hotX, cmd->simpleData.hotY);
+ client->cursors.insert(cmd->simpleData.id, curs);
+ }
+}
+
+void QWSServerPrivate::invokeSelectCursor(QWSSelectCursorCommand *cmd, QWSClient *client)
+{
+ int id = cmd->simpleData.id;
+ QWSCursor *curs = 0;
+ if (id <= Qt::LastCursor) {
+ curs = QWSCursor::systemCursor(id);
+ }
+ else {
+ QWSCursorMap cursMap = client->cursors;
+ QWSCursorMap::Iterator it = cursMap.find(id);
+ if (it != cursMap.end()) {
+ curs = it.value();
+ }
+ }
+ if (curs == 0) {
+ curs = QWSCursor::systemCursor(Qt::ArrowCursor);
+ }
+
+ QWSWindow* win = findWindow(cmd->simpleData.windowid, 0);
+ if (mouseGrabber) {
+ // If the mouse is being grabbed, we don't want just anyone to
+ // be able to change the cursor. We do want the cursor to be set
+ // correctly once mouse grabbing is stopped though.
+ if (win != mouseGrabber)
+ nextCursor = curs;
+ else
+ setCursor(curs);
+ } else if (win && win->allocatedRegion().contains(QWSServer::mousePosition)) { //##################### cursor
+ // A non-grabbing window can only set the cursor shape if the
+ // cursor is within its allocated region.
+ setCursor(curs);
+ }
+}
+
+void QWSServerPrivate::invokePositionCursor(QWSPositionCursorCommand *cmd, QWSClient *)
+{
+ Q_Q(QWSServer);
+ QPoint newPos(cmd->simpleData.newX, cmd->simpleData.newY);
+ if (newPos != QWSServer::mousePosition)
+ q->sendMouseEvent(newPos, qwsServer->d_func()->mouseState);
+}
+#endif
+
+void QWSServerPrivate::invokeGrabMouse(QWSGrabMouseCommand *cmd, QWSClient *client)
+{
+ QWSWindow* win = findWindow(cmd->simpleData.windowid, 0);
+ if (!win)
+ return;
+
+ if (cmd->simpleData.grab) {
+ if (!mouseGrabber || mouseGrabber->client() == client) {
+ mouseGrabbing = true;
+ mouseGrabber = win;
+ }
+ } else {
+ releaseMouse(mouseGrabber);
+ }
+}
+
+void QWSServerPrivate::invokeGrabKeyboard(QWSGrabKeyboardCommand *cmd, QWSClient *client)
+{
+ QWSWindow* win = findWindow(cmd->simpleData.windowid, 0);
+ if (!win)
+ return;
+
+ if (cmd->simpleData.grab) {
+ if (!keyboardGrabber || (keyboardGrabber->client() == client)) {
+ keyboardGrabbing = true;
+ keyboardGrabber = win;
+ }
+ } else {
+ releaseKeyboard(keyboardGrabber);
+ }
+}
+
+#if !defined(QT_NO_SOUND)
+void QWSServerPrivate::invokePlaySound(QWSPlaySoundCommand *cmd, QWSClient *)
+{
+#if !defined(QT_EXTERNAL_SOUND_SERVER) && !defined(Q_OS_DARWIN)
+ soundserver->playFile( 1, cmd->filename );
+#else
+ Q_UNUSED(cmd);
+#endif
+}
+#endif
+
+#ifndef QT_NO_COP
+void QWSServerPrivate::invokeRegisterChannel(QWSQCopRegisterChannelCommand *cmd,
+ QWSClient *client)
+{
+ // QCopChannel will force us to emit the newChannel signal if this channel
+ // didn't already exist.
+ QCopChannel::registerChannel(cmd->channel, client);
+}
+
+void QWSServerPrivate::invokeQCopSend(QWSQCopSendCommand *cmd, QWSClient *client)
+{
+ QCopChannel::answer(client, cmd->channel, cmd->message, cmd->data);
+}
+
+#endif
+
+#ifndef QT_NO_QWS_INPUTMETHODS
+void QWSServer::resetInputMethod()
+{
+ if (current_IM && qwsServer) {
+ current_IM->reset();
+ }
+}
+
+void QWSServerPrivate::invokeIMResponse(const QWSIMResponseCommand *cmd,
+ QWSClient *)
+{
+ if (current_IM)
+ current_IM->queryResponse(cmd->simpleData.property, cmd->result);
+}
+
+void QWSServerPrivate::invokeIMUpdate(const QWSIMUpdateCommand *cmd,
+ QWSClient *)
+{
+ if (cmd->simpleData.type == QWSInputMethod::FocusIn)
+ current_IM_winId = cmd->simpleData.windowid;
+
+ if (current_IM && (current_IM_winId == cmd->simpleData.windowid || cmd->simpleData.windowid == -1))
+ current_IM->updateHandler(cmd->simpleData.type);
+}
+
+#endif
+
+void QWSServerPrivate::invokeFont(const QWSFontCommand *cmd, QWSClient *client)
+{
+ QWSClientPrivate *priv = client->d_func();
+ if (cmd->simpleData.type == QWSFontCommand::StartedUsingFont) {
+ referenceFont(priv, cmd->fontName);
+ } else if (cmd->simpleData.type == QWSFontCommand::StoppedUsingFont) {
+ dereferenceFont(priv, cmd->fontName);
+ }
+}
+
+void QWSServerPrivate::invokeRepaintRegion(QWSRepaintRegionCommand * cmd,
+ QWSClient *)
+{
+ QRegion r;
+ r.setRects(cmd->rectangles,cmd->simpleData.nrectangles);
+ repaint_region(cmd->simpleData.windowid, cmd->simpleData.windowFlags, cmd->simpleData.opaque, r);
+}
+
+#ifndef QT_NO_QWSEMBEDWIDGET
+void QWSServerPrivate::invokeEmbed(QWSEmbedCommand *cmd, QWSClient *client)
+{
+ // Should find these two windows in a single loop
+ QWSWindow *embedder = findWindow(cmd->simpleData.embedder, client);
+ QWSWindow *embedded = findWindow(cmd->simpleData.embedded);
+
+ if (!embedder) {
+ qWarning("QWSServer: Embed command from window %i failed: No such id.",
+ static_cast<int>(cmd->simpleData.embedder));
+ return;
+ }
+
+ if (!embedded) {
+ qWarning("QWSServer: Embed command on window %i failed: No such id.",
+ static_cast<int>(cmd->simpleData.embedded));
+ return;
+ }
+
+ switch (cmd->simpleData.type) {
+ case QWSEmbedEvent::StartEmbed:
+ embedder->startEmbed(embedded);
+ windows.removeAll(embedded);
+ windows.insert(windows.indexOf(embedder), embedded);
+ break;
+ case QWSEmbedEvent::StopEmbed:
+ embedder->stopEmbed(embedded);
+ break;
+ case QWSEmbedEvent::Region:
+ break;
+ }
+
+ embedded->client()->sendEmbedEvent(embedded->winId(),
+ cmd->simpleData.type, cmd->region);
+ const QRegion oldAllocated = embedded->allocatedRegion();
+ update_regions();
+ exposeRegion(oldAllocated - embedded->allocatedRegion(),
+ windows.indexOf(embedded));
+}
+#endif // QT_NO_QWSEMBEDWIDGET
+
+void QWSServerPrivate::invokeScreenTransform(const QWSScreenTransformCommand *cmd,
+ QWSClient *client)
+{
+ Q_UNUSED(client);
+
+ QWSScreenTransformationEvent event;
+ event.simpleData.screen = cmd->simpleData.screen;
+ event.simpleData.transformation = cmd->simpleData.transformation;
+
+ QMap<int, QWSClient*>::const_iterator it = clientMap.constBegin();
+ for (; it != clientMap.constEnd(); ++it)
+ (*it)->sendEvent(&event);
+}
+
+QWSWindow* QWSServerPrivate::newWindow(int id, QWSClient* client)
+{
+ Q_Q(QWSServer);
+ // Make a new window, put it on top.
+ QWSWindow* w = new QWSWindow(id,client);
+
+ // insert after "stays on top" windows
+ bool added = false;
+ for (int i = nReserved; i < windows.size(); ++i) {
+ QWSWindow *win = windows.at(i);
+ if (!win->onTop) {
+ windows.insert(i, w);
+ added = true;
+ break;
+ }
+ }
+ if (!added)
+ windows.append(w);
+ emit q->windowEvent(w, QWSServer::Create);
+ return w;
+}
+
+QWSWindow* QWSServerPrivate::findWindow(int windowid, QWSClient* client)
+{
+ for (int i=0; i<windows.size(); ++i) {
+ QWSWindow* w = windows.at(i);
+ if (w->winId() == windowid)
+ return w;
+ }
+ if (client)
+ return newWindow(windowid,client);
+ else
+ return 0;
+}
+
+void QWSServerPrivate::raiseWindow(QWSWindow *changingw, int /*alt*/)
+{
+ Q_Q(QWSServer);
+ if (changingw == windows.first())
+ return;
+ QWSWindow::State oldstate = changingw->d->state;
+ changingw->d->state = QWSWindow::Raising;
+ // Expose regions previously overlapped by transparent windows
+ const QRegion bound = changingw->allocatedRegion();
+ QRegion expose;
+ int windowPos = 0;
+
+ //change position in list:
+ for (int i = 0; i < windows.size(); ++i) {
+ QWSWindow *w = windows.at(i);
+ if (w == changingw) {
+ windowPos = i;
+ windows.takeAt(i);
+ break;
+ }
+ if (!w->isOpaque())
+ expose += (w->allocatedRegion() & bound);
+ }
+
+ bool onTop = changingw->onTop;
+
+#ifndef QT_NO_QWSEMBEDWIDGET
+ // an embedded window is on top if the embedder is on top
+ QWSWindow *embedder = changingw->d->embedder;
+ while (!onTop && embedder) {
+ onTop = embedder->onTop;
+ embedder = embedder->d->embedder;
+ }
+#endif
+
+ int newPos = -1;
+ if (onTop) {
+ windows.insert(nReserved, changingw);
+ newPos = nReserved;
+ } else {
+ // insert after "stays on top" windows
+ bool in = false;
+ for (int i = nReserved; i < windows.size(); ++i) {
+ QWSWindow *w = windows.at(i);
+ if (!w->onTop) {
+ windows.insert(i, changingw);
+ in = true;
+ newPos = i;
+ break;
+ }
+ }
+ if (!in) {
+ windows.append(changingw);
+ newPos = windows.size()-1;
+ }
+ }
+
+ if (windowPos != newPos) {
+ update_regions();
+ if (!expose.isEmpty())
+ exposeRegion(expose, newPos);
+ }
+ changingw->d->state = oldstate;
+ emit q->windowEvent(changingw, QWSServer::Raise);
+}
+
+void QWSServerPrivate::lowerWindow(QWSWindow *changingw, int /*alt*/)
+{
+ Q_Q(QWSServer);
+ if (changingw == windows.last())
+ return;
+ QWSWindow::State oldstate = changingw->d->state;
+ changingw->d->state = QWSWindow::Lowering;
+
+ int i = windows.indexOf(changingw);
+ int newIdx = windows.size()-1;
+ windows.move(i, newIdx);
+
+ const QRegion bound = changingw->allocatedRegion();
+
+ update_regions();
+
+ // Expose regions previously overlapped by transparent window
+ if (!changingw->isOpaque()) {
+ QRegion expose;
+ for (int j = i; j < windows.size() - 1; ++j)
+ expose += (windows.at(j)->allocatedRegion() & bound);
+ if (!expose.isEmpty())
+ exposeRegion(expose, newIdx);
+ }
+
+ changingw->d->state = oldstate;
+ emit q->windowEvent(changingw, QWSServer::Lower);
+}
+
+void QWSServerPrivate::update_regions()
+{
+ if (disablePainting)
+ return;
+
+ QRegion available = QRect(0, 0, qt_screen->width(), qt_screen->height());
+ QRegion transparentRegion;
+
+ // only really needed if there are unbuffered surfaces...
+ const bool doLock = (clientMap.size() > 1);
+ if (doLock)
+ QWSDisplay::grab(true);
+
+ for (int i = 0; i < windows.count(); ++i) {
+ QWSWindow *w = windows.at(i);
+ QRegion r = (w->requested_region & available);
+
+#ifndef QT_NO_QWSEMBEDWIDGET
+ // Subtract regions needed for embedded windows
+ const int n = w->d->embedded.size();
+ for (int i = 0; i < n; ++i)
+ r -= w->d->embedded.at(i)->allocatedRegion();
+
+ // Limited to the embedder region
+ if (w->d->embedder)
+ r &= w->d->embedder->requested_region;
+#endif // QT_NO_QWSEMBEDWIDGET
+
+ QWSWindowSurface *surface = w->windowSurface();
+ const bool opaque = w->isOpaque()
+ && (w->d->painted || !surface || !surface->isBuffered());
+
+ if (!opaque) {
+ transparentRegion += r;
+ } else {
+ if (surface && (surface->isRegionReserved() || !surface->isBuffered()))
+ r -= transparentRegion;
+ available -= r;
+ }
+
+ if (r != w->allocatedRegion()) {
+ w->setAllocatedRegion(r);
+ w->client()->sendRegionEvent(w->winId(), r,
+ QWSRegionEvent::Allocation);
+ }
+
+#ifdef QT_QWS_CLIENTBLIT
+#ifdef QT_NO_QWS_CURSOR
+ // This optimization only really works when there isn't a crazy cursor
+ // wizzing around.
+ QRegion directPaint = (r - transparentRegion); // in gloal coords
+ if(directPaint != w->directPaintRegion()) {
+ w->setDirectPaintRegion(directPaint);
+ static int id = 0;
+ surface->setDirectRegion(directPaint, ++id);
+ w->client()->sendRegionEvent(w->winId(), directPaint,
+ QWSRegionEvent::DirectPaint, id);
+ }
+#endif
+#endif
+ }
+
+ if (doLock)
+ QWSDisplay::ungrab();
+}
+
+void QWSServerPrivate::moveWindowRegion(QWSWindow *changingw, int dx, int dy)
+{
+ if (!changingw)
+ return;
+
+ QWSWindow::State oldState = changingw->d->state;
+ changingw->d->state = QWSWindow::Moving;
+ const QRegion oldRegion(changingw->allocatedRegion());
+ changingw->requested_region.translate(dx, dy);
+
+ // hw: Even if the allocated region doesn't change, the requested region
+ // region has changed and we need to send region events.
+ // Resetting the allocated region to force update_regions to send events.
+ changingw->setAllocatedRegion(QRegion());
+ update_regions();
+ const QRegion newRegion(changingw->allocatedRegion());
+
+ QWSWindowSurface *surface = changingw->windowSurface();
+ QRegion expose;
+ if (surface)
+ expose = surface->move(QPoint(dx, dy), changingw->allocatedRegion());
+ else
+ expose = oldRegion + newRegion;
+
+ if (!changingw->d->painted && !expose.isEmpty())
+ expose = oldRegion - newRegion;
+
+ int idx = windows.indexOf(changingw);
+ exposeRegion(expose, idx);
+ changingw->d->state = oldState;
+}
+
+/*!
+ Changes the requested region of window \a changingw to \a r
+ If \a changingw is 0, the server's reserved region is changed.
+*/
+void QWSServerPrivate::setWindowRegion(QWSWindow* changingw, const QRegion &r)
+{
+ if (!changingw) {
+ qWarning("Not implemented in this release");
+ return;
+ }
+
+ if (changingw->requested_region == r)
+ return;
+
+ const QRegion oldRegion(changingw->allocatedRegion());
+ changingw->requested_region = r;
+ update_regions();
+ const QRegion newRegion(changingw->allocatedRegion());
+
+ int idx = windows.indexOf(changingw);
+ exposeRegion(oldRegion - newRegion, idx);
+}
+
+
+void QWSServerPrivate::exposeRegion(const QRegion &r, int changing)
+{
+ if (disablePainting)
+ return;
+
+ if (r.isEmpty())
+ return;
+
+ static bool initial = true;
+ if (initial) {
+ changing = 0;
+ initial = false;
+ qt_screen->exposeRegion(qt_screen->region(), changing);
+ } else {
+ qt_screen->exposeRegion(r, changing);
+ }
+}
+
+/*!
+ Closes all pointer devices (specified by the QWS_MOUSE_PROTO
+ environment variable) by deleting the associated mouse drivers.
+
+ \sa openMouse(), mouseHandler()
+*/
+void QWSServer::closeMouse()
+{
+ Q_D(QWSServer);
+ qDeleteAll(d->mousehandlers);
+ d->mousehandlers.clear();
+}
+
+/*!
+ Opens the mouse devices specified by the QWS_MOUSE_PROTO
+ environment variable. Be advised that closeMouse() is called first
+ to delete all the existing mouse handlers. This behaviour could be
+ the cause of problems if you were not expecting it.
+
+ \sa closeMouse(), mouseHandler()
+*/
+void QWSServer::openMouse()
+{
+ Q_D(QWSServer);
+ QString mice = QString::fromLatin1(qgetenv("QWS_MOUSE_PROTO"));
+#if defined(QT_QWS_CASSIOPEIA)
+ if (mice.isEmpty())
+ mice = QLatin1String("TPanel:/dev/tpanel");
+#endif
+ if (mice.isEmpty())
+ mice = *defaultMouse();
+ closeMouse();
+ bool needviscurs = true;
+ if (mice != QLatin1String("None")) {
+ const QStringList mouse = mice.split(QLatin1Char(' '));
+ for (int i = mouse.size() - 1; i >= 0; --i) {
+ QWSMouseHandler *handler = d->newMouseHandler(mouse.at(i));
+ setMouseHandler(handler);
+ /* XXX handle mouse cursor visibility sensibly
+ if (!h->inherits("QCalibratedMouseHandler"))
+ needviscurs = true;
+ */
+ }
+ }
+#ifndef QT_NO_QWS_CURSOR
+ setCursorVisible(needviscurs);
+#else
+ Q_UNUSED(needviscurs)
+#endif
+}
+
+/*!
+ Suspends pointer handling by deactivating all the mouse drivers
+ registered by the QWS_MOUSE_PROTO environment variable.
+
+
+ \sa resumeMouse(), QWSMouseHandler::suspend()
+*/
+void QWSServer::suspendMouse()
+{
+ Q_D(QWSServer);
+ for (int i=0; i < d->mousehandlers.size(); ++i)
+ d->mousehandlers.at(i)->suspend();
+}
+
+/*!
+ Resumes pointer handling by reactivating all the mouse drivers
+ registered by the QWS_MOUSE_PROTO environment variable.
+
+ \sa suspendMouse(), QWSMouseHandler::resume()
+*/
+void QWSServer::resumeMouse()
+{
+ Q_D(QWSServer);
+ for (int i=0; i < d->mousehandlers.size(); ++i)
+ d->mousehandlers.at(i)->resume();
+}
+
+
+
+QWSMouseHandler* QWSServerPrivate::newMouseHandler(const QString& spec)
+{
+ int c = spec.indexOf(QLatin1Char(':'));
+ QString mouseProto;
+ QString mouseDev;
+ if (c >= 0) {
+ mouseProto = spec.left(c);
+ mouseDev = spec.mid(c+1);
+ } else {
+ mouseProto = spec;
+ }
+
+ int screen = -1;
+ const QList<QRegExp> regexps = QList<QRegExp>()
+ << QRegExp(QLatin1String(":screen=(\\d+)\\b"))
+ << QRegExp(QLatin1String("\\bscreen=(\\d+):"));
+ for (int i = 0; i < regexps.size(); ++i) {
+ QRegExp regexp = regexps.at(i);
+ if (regexp.indexIn(mouseDev) == -1)
+ continue;
+ screen = regexp.cap(1).toInt();
+ mouseDev.remove(regexp.pos(0), regexp.matchedLength());
+ break;
+ }
+
+ QWSMouseHandler *handler = 0;
+ handler = QMouseDriverFactory::create(mouseProto, mouseDev);
+ if (screen != -1)
+ handler->setScreen(qt_screen->subScreens().at(screen));
+
+ return handler;
+}
+
+#ifndef QT_NO_QWS_KEYBOARD
+
+/*!
+ Closes all the keyboard devices (specified by the QWS_KEYBOARD
+ environment variable) by deleting the associated keyboard
+ drivers.
+
+ \sa openKeyboard(), keyboardHandler()
+*/
+void QWSServer::closeKeyboard()
+{
+ Q_D(QWSServer);
+ qDeleteAll(d->keyboardhandlers);
+ d->keyboardhandlers.clear();
+}
+
+/*!
+ Returns the primary keyboard driver.
+
+ Note that this function can only be used in the server process.
+
+ \sa setKeyboardHandler(), openKeyboard(), closeKeyboard()
+*/
+QWSKeyboardHandler* QWSServer::keyboardHandler()
+{
+ return qwsServerPrivate->keyboardhandlers.first();
+}
+
+/*!
+ \fn void QWSServer::setKeyboardHandler(QWSKeyboardHandler* driver)
+
+ Sets the primary keyboard driver to be the given \a driver.
+
+ \l{Qt for Embedded Linux} provides several ready-made keyboard drivers, and
+ custom drivers are typically added using Qt's plugin
+ mechanism. See the \l{Qt for Embedded Linux Character Input} documentation
+ for details.
+
+ Note that this function can only be used in the server process.
+
+ \sa keyboardHandler(), setDefaultKeyboard()
+*/
+void QWSServer::setKeyboardHandler(QWSKeyboardHandler* kh)
+{
+ if (!kh)
+ return;
+ qwsServerPrivate->keyboardhandlers.removeAll(kh);
+ qwsServerPrivate->keyboardhandlers.prepend(kh);
+}
+
+/*!
+ Opens the keyboard devices specified by the QWS_KEYBOARD
+ environment variable.
+
+ \sa closeKeyboard(), keyboardHandler()
+*/
+void QWSServer::openKeyboard()
+{
+ QString keyboards = QString::fromLatin1(qgetenv("QWS_KEYBOARD"));
+#if defined(QT_QWS_CASSIOPEIA)
+ if (keyboards.isEmpty())
+ keyboards = QLatin1String("Buttons");
+#endif
+ if (keyboards.isEmpty())
+ keyboards = *defaultKeyboard();
+
+ closeKeyboard();
+ if (keyboards == QLatin1String("None"))
+ return;
+
+ QString device;
+ QString type;
+ QStringList keyboard = keyboards.split(QLatin1Char(' '));
+ for (int i = keyboard.size() - 1; i >= 0; --i) {
+ const QString spec = keyboard.at(i);
+ int colon=spec.indexOf(QLatin1Char(':'));
+ if (colon>=0) {
+ type = spec.left(colon);
+ device = spec.mid(colon+1);
+ } else {
+ type = spec;
+ device = QString();
+ }
+ QWSKeyboardHandler *handler = QKbdDriverFactory::create(type, device);
+ setKeyboardHandler(handler);
+ }
+}
+
+#endif //QT_NO_QWS_KEYBOARD
+
+QPoint QWSServer::mousePosition;
+QBrush *QWSServerPrivate::bgBrush = 0;
+
+void QWSServerPrivate::move_region(const QWSRegionMoveCommand *cmd)
+{
+ QWSClient *serverClient = clientMap.value(-1);
+ invokeRegionMove(cmd, serverClient);
+}
+
+void QWSServerPrivate::set_altitude(const QWSChangeAltitudeCommand *cmd)
+{
+ QWSClient *serverClient = clientMap.value(-1);
+ invokeSetAltitude(cmd, serverClient);
+}
+
+void QWSServerPrivate::set_opacity(const QWSSetOpacityCommand *cmd)
+{
+ QWSClient *serverClient = clientMap.value(-1);
+ invokeSetOpacity(cmd, serverClient);
+}
+
+
+void QWSServerPrivate::request_focus(const QWSRequestFocusCommand *cmd)
+{
+ invokeSetFocus(cmd, clientMap.value(-1));
+}
+
+void QWSServerPrivate::set_identity(const QWSIdentifyCommand *cmd)
+{
+ invokeIdentify(cmd, clientMap.value(-1));
+}
+
+void QWSServerPrivate::repaint_region(int wid, int windowFlags, bool opaque,
+ const QRegion &region)
+{
+ QWSWindow* changingw = findWindow(wid, 0);
+ if (!changingw) {
+ return;
+ }
+
+ const bool isOpaque = changingw->opaque;
+ const bool wasPainted = changingw->d->painted;
+ changingw->opaque = opaque;
+ changingw->d->windowFlags = QFlag(windowFlags);
+ changingw->d->dirtyOnScreen |= region;
+ changingw->d->painted = true;
+ if (isOpaque != opaque || !wasPainted)
+ update_regions();
+
+ int level = windows.indexOf(changingw);
+ exposeRegion(region, level);
+ changingw->d->dirtyOnScreen = QRegion();
+}
+
+QRegion QWSServerPrivate::reserve_region(QWSWindow *win, const QRegion &region)
+{
+ QRegion r = region;
+
+ int oldPos = windows.indexOf(win);
+ int newPos = oldPos < nReserved ? nReserved - 1 : nReserved;
+ for (int i = 0; i < nReserved; ++i) {
+ if (i != oldPos) {
+ QWSWindow *w = windows.at(i);
+ r -= w->requested_region;
+ }
+ }
+ windows.move(oldPos, newPos);
+ nReserved = newPos + 1;
+
+ return r;
+}
+
+void QWSServerPrivate::request_region(int wid, const QString &surfaceKey,
+ const QByteArray &surfaceData,
+ const QRegion &region)
+{
+ QWSWindow *changingw = findWindow(wid, 0);
+ if (!changingw)
+ return;
+
+ Q_Q(QWSServer);
+ QWSWindow::State windowState = QWSWindow::NoState;
+
+ if (region.isEmpty()) {
+ windowState = QWSWindow::Hiding;
+ emit q->windowEvent(changingw, QWSServer::Hide);
+ }
+
+ const bool wasOpaque = changingw->opaque;
+
+ changingw->createSurface(surfaceKey, surfaceData);
+ QWSWindowSurface *surface = changingw->windowSurface();
+
+ changingw->opaque = surface->isOpaque();
+
+ QRegion r;
+ if (surface->isRegionReserved())
+ r = reserve_region(changingw, region);
+ else
+ r = region;
+
+ if (!region.isEmpty()) {
+ if (changingw->isVisible())
+ windowState = QWSWindow::ChangingGeometry;
+ else
+ windowState = QWSWindow::Showing;
+ }
+ changingw->d->state = windowState;
+
+ if (!r.isEmpty() && wasOpaque != changingw->opaque && surface->isBuffered())
+ changingw->requested_region = QRegion(); // XXX: force update_regions
+
+ const QRegion oldAllocated = changingw->allocatedRegion();
+ setWindowRegion(changingw, r);
+ if (oldAllocated == changingw->allocatedRegion()) {
+ // Always send region event to the requesting window even if the
+ // region didn't change. This is necessary as the client will reset
+ // the clip region until an event is received.
+ changingw->client()->sendRegionEvent(wid, changingw->allocatedRegion(),
+ QWSRegionEvent::Allocation);
+ }
+
+ surface->QWindowSurface::setGeometry(r.boundingRect());
+
+ if (windowState == QWSWindow::Showing)
+ emit q->windowEvent(changingw, QWSServer::Show);
+ else if (windowState == QWSWindow::ChangingGeometry)
+ emit q->windowEvent(changingw, QWSServer::Geometry);
+ if (windowState == QWSWindow::Hiding) {
+ handleWindowClose(changingw);
+ changingw->d->state = QWSWindow::Hidden;
+ changingw->d->painted = false;
+ } else {
+ changingw->d->state = QWSWindow::Visible;
+ }
+}
+
+void QWSServerPrivate::destroy_region(const QWSRegionDestroyCommand *cmd)
+{
+ invokeRegionDestroy(cmd, clientMap.value(-1));
+}
+
+void QWSServerPrivate::name_region(const QWSRegionNameCommand *cmd)
+{
+ invokeRegionName(cmd, clientMap.value(-1));
+}
+
+#ifndef QT_NO_QWS_INPUTMETHODS
+void QWSServerPrivate::im_response(const QWSIMResponseCommand *cmd)
+ {
+ invokeIMResponse(cmd, clientMap.value(-1));
+}
+
+void QWSServerPrivate::im_update(const QWSIMUpdateCommand *cmd)
+{
+ invokeIMUpdate(cmd, clientMap.value(-1));
+}
+
+void QWSServerPrivate::send_im_mouse(const QWSIMMouseCommand *cmd)
+{
+ if (current_IM)
+ current_IM->mouseHandler(cmd->simpleData.index, cmd->simpleData.state);
+}
+#endif
+
+void QWSServerPrivate::openDisplay()
+{
+ qt_init_display();
+
+// rgnMan = qt_fbdpy->regionManager();
+ swidth = qt_screen->deviceWidth();
+ sheight = qt_screen->deviceHeight();
+}
+
+void QWSServerPrivate::closeDisplay()
+{
+ qt_screen->shutdownDevice();
+}
+
+/*!
+ Returns the brush used as background in the absence of obscuring
+ windows.
+
+ \sa setBackground()
+*/
+const QBrush &QWSServer::backgroundBrush() const
+{
+ return *QWSServerPrivate::bgBrush;
+}
+
+/*!
+ Sets the brush used as background in the absence of obscuring
+ windows, to be the given \a brush.
+
+ Note that this function can only be used in the server process.
+
+ \sa backgroundBrush()
+*/
+void QWSServer::setBackground(const QBrush &brush)
+{
+ if (!QWSServerPrivate::bgBrush)
+ QWSServerPrivate::bgBrush = new QBrush(brush);
+ else
+ *QWSServerPrivate::bgBrush = brush;
+ if (!qwsServer)
+ return;
+ qt_screen->exposeRegion(QRect(0,0,qt_screen->width(), qt_screen->height()), 0);
+}
+
+
+#ifdef QT3_SUPPORT
+/*!
+ \fn void QWSServer::setDesktopBackground(const QImage &image)
+
+ Sets the image used as background in the absence of obscuring
+ windows, to be the given \a image.
+
+ Use the setBackground() function instead.
+
+ \oldcode
+ QImage image;
+ setDesktopBackground(image);
+ \newcode
+ QImage image;
+ setBackground(QBrush(image));
+ \endcode
+*/
+void QWSServer::setDesktopBackground(const QImage &img)
+{
+ if (img.isNull())
+ setBackground(Qt::NoBrush);
+ else
+ setBackground(QBrush(QPixmap::fromImage(img)));
+}
+
+/*!
+ \fn void QWSServer::setDesktopBackground(const QColor &color)
+ \overload
+
+ Sets the color used as background in the absence of obscuring
+ windows, to be the given \a color.
+
+ Use the setBackground() function instead.
+
+ \oldcode
+ QColor color;
+ setDesktopBackground(color);
+ \newcode
+ QColor color;
+ setBackground(QBrush(color));
+ \endcode
+*/
+void QWSServer::setDesktopBackground(const QColor &c)
+{
+ setDesktopBackground(QBrush(c));
+}
+#endif //QT3_SUPPORT
+
+/*!
+ \internal
+ */
+void QWSServer::startup(int flags)
+{
+ if (qwsServer)
+ return;
+ unlink(qws_qtePipeFilename().toLatin1().constData());
+ (void)new QWSServer(flags);
+}
+
+/*!
+ \internal
+*/
+
+void QWSServer::closedown()
+{
+ unlink(qws_qtePipeFilename().toLatin1().constData());
+ delete qwsServer;
+ qwsServer = 0;
+}
+
+void QWSServerPrivate::emergency_cleanup()
+{
+#ifndef QT_NO_QWS_KEYBOARD
+ if (qwsServer)
+ qwsServer->closeKeyboard();
+#endif
+}
+
+#ifndef QT_NO_QWS_KEYBOARD
+static QList<QWSServer::KeyboardFilter*> *keyFilters = 0;
+
+/*!
+ Processes the given key event. The key is identified by its \a
+ unicode value and the given \a keycode, \a modifiers, \a isPress
+ and \a autoRepeat parameters.
+
+ The \a keycode parameter is the Qt keycode value as defined by the
+ Qt::Key enum. The \a modifiers is an OR combination of
+ Qt::KeyboardModifier values, indicating whether \gui
+ Shift/Alt/Ctrl keys are pressed. The \a isPress parameter is true
+ if the event is a key press event and \a autoRepeat is true if the
+ event is caused by an auto-repeat mechanism and not an actual key
+ press.
+
+ This function is typically called internally by keyboard drivers.
+ Note that this function can only be used in the server process.
+
+ \sa sendKeyEvent(), {Qt for Embedded Linux Character Input}
+*/
+void QWSServer::processKeyEvent(int unicode, int keycode, Qt::KeyboardModifiers modifiers,
+ bool isPress, bool autoRepeat)
+{
+ bool block;
+ // Don't block the POWER or LIGHT keys
+ if ( keycode == Qt::Key_F34 || keycode == Qt::Key_F35 )
+ block = false;
+ else
+ block = qwsServerPrivate->screensaverblockevent(KEY, qwsServerPrivate->screensaverinterval, isPress);
+
+#ifdef EVENT_BLOCK_DEBUG
+ qDebug() << "processKeyEvent" << unicode << keycode << modifiers << isPress << autoRepeat << (block?"block":"pass");
+#endif
+
+ // If we press a key and it's going to be blocked, wake up the screen
+ if ( block && isPress )
+ qwsServerPrivate->_q_screenSaverWake();
+
+ if ( block )
+ return;
+
+ if (keyFilters) {
+ for (int i = 0; i < keyFilters->size(); ++i) {
+ QWSServer::KeyboardFilter *keyFilter = keyFilters->at(i);
+ if (keyFilter->filter(unicode, keycode, modifiers, isPress, autoRepeat))
+ return;
+ }
+ }
+ sendKeyEvent(unicode, keycode, modifiers, isPress, autoRepeat);
+}
+
+/*!
+ \fn void QWSServer::addKeyboardFilter(KeyboardFilter *filter)
+
+ Activates the given keyboard \a filter all key events generated by
+ physical keyboard drivers (i.e., events sent using the
+ processKeyEvent() function).
+
+ Note that the filter is not invoked for keys generated by \e
+ virtual keyboard drivers (i.e., events sent using the
+ sendKeyEvent() function).
+
+ Note that this function can only be used in the server process.
+
+ \sa removeKeyboardFilter()
+*/
+void QWSServer::addKeyboardFilter(KeyboardFilter *f)
+{
+ if (!keyFilters)
+ keyFilters = new QList<QWSServer::KeyboardFilter*>;
+ if (f) {
+ keyFilters->prepend(f);
+ }
+}
+
+/*
+//#######
+ We should probably obsolete the whole keyboard filter thing since
+ it's not useful for input methods anyway
+
+ We could do removeKeyboardFilter(KeyboardFilter *f), but
+ the "remove and delete the filter" concept does not match "user
+ remembers the pointer".
+*/
+
+/*!
+ Removes and deletes the most recently added filter.
+
+ Note that the programmer is responsible for removing each added
+ keyboard filter.
+
+ Note that this function can only be used in the server process.
+
+ \sa addKeyboardFilter()
+*/
+void QWSServer::removeKeyboardFilter()
+{
+ if (!keyFilters || keyFilters->isEmpty())
+ return;
+ delete keyFilters->takeAt(0);
+}
+#endif // QT_NO_QWS_KEYBOARD
+
+/*!
+ \fn void QWSServer::setScreenSaverIntervals(int* intervals)
+
+ Specifies the time \a intervals (in milliseconds) between the
+ different levels of screen responsiveness.
+
+ \l{Qt for Embedded Linux} supports multilevel screen saving, i.e., it is
+ possible to specify several different levels of screen
+ responsiveness by implementing the QWSScreenSaver::save()
+ function. For example, you can choose to first turn off the light
+ before you fully activate the screensaver. See the QWSScreenSaver
+ documentation for details.
+
+ Note that an interval of 0 milliseconds will turn off the
+ screensaver, and that the \a intervals array must be 0-terminated.
+ This function can only be used in the server process.
+
+ \sa setScreenSaverInterval(), setScreenSaverBlockLevel()
+*/
+void QWSServer::setScreenSaverIntervals(int* ms)
+{
+ if (!qwsServerPrivate)
+ return;
+
+ delete [] qwsServerPrivate->screensaverintervals;
+ if (ms) {
+ int* t=ms;
+ int n=0;
+ while (*t++) n++;
+ if (n) {
+ n++; // the 0
+ qwsServerPrivate->screensaverintervals = new int[n];
+ memcpy(qwsServerPrivate->screensaverintervals, ms, n*sizeof(int));
+ } else {
+ qwsServerPrivate->screensaverintervals = 0;
+ }
+ } else {
+ qwsServerPrivate->screensaverintervals = 0;
+ }
+ qwsServerPrivate->screensaverinterval = 0;
+
+ qwsServerPrivate->screensavertimer->stop();
+ qt_screen->blank(false);
+ qwsServerPrivate->_q_screenSaverWake();
+}
+
+/*!
+ \fn void QWSServer::setScreenSaverInterval(int milliseconds)
+
+ Sets the timeout interval for the screensaver to the specified \a
+ milliseconds. To turn off the screensaver, set the timout interval
+ to 0.
+
+ Note that this function can only be used in the server process.
+
+ \sa setScreenSaverIntervals(), setScreenSaverBlockLevel()
+*/
+void QWSServer::setScreenSaverInterval(int ms)
+{
+ int v[2];
+ v[0] = ms;
+ v[1] = 0;
+ setScreenSaverIntervals(v);
+}
+
+/*!
+ Block the key or mouse event that wakes the system from level \a eventBlockLevel or higher.
+ To completely disable event blocking (the default behavior), set \a eventBlockLevel to -1.
+
+ The algorithm blocks the "down", "up" as well as any "repeat" events for the same key
+ but will not block other key events after the initial "down" event. For mouse events, the
+ algorithm blocks all mouse events until an event with no buttons pressed is received.
+
+ There are 2 keys that are never blocked, Qt::Key_F34 (POWER) and Qt::Key_F35 (LIGHT).
+
+ Example usage:
+
+ \snippet doc/src/snippets/code/src_gui_embedded_qwindowsystem_qws.cpp 0
+
+ Note that this function can only be used in the server process.
+
+ \sa setScreenSaverIntervals(), setScreenSaverInterval()
+*/
+void QWSServer::setScreenSaverBlockLevel(int eventBlockLevel)
+{
+ if (!qwsServerPrivate)
+ return;
+ qwsServerPrivate->screensavereventblocklevel = eventBlockLevel;
+#ifdef EVENT_BLOCK_DEBUG
+ qDebug() << "QWSServer::setScreenSaverBlockLevel() " << eventBlockLevel;
+#endif
+}
+
+extern bool qt_disable_lowpriority_timers; //in qeventloop_unix.cpp
+
+void QWSServerPrivate::_q_screenSaverWake()
+{
+ if (screensaverintervals) {
+ if (screensaverinterval != screensaverintervals) {
+ if (saver) saver->restore();
+ screensaverinterval = screensaverintervals;
+ screensaverblockevents = false;
+ } else {
+ if (!screensavertimer->isActive()) {
+ qt_screen->blank(false);
+ if (saver) saver->restore();
+ }
+ }
+ screensavertimer->start(*screensaverinterval);
+ screensavertime.start();
+ }
+ qt_disable_lowpriority_timers=false;
+}
+
+void QWSServerPrivate::_q_screenSaverSleep()
+{
+ qt_screen->blank(true);
+#if !defined(QT_QWS_IPAQ) && !defined(QT_QWS_EBX)
+ screensavertimer->stop();
+#else
+ if (screensaverinterval) {
+ screensavertimer->start(*screensaverinterval);
+ screensavertime.start();
+ } else {
+ screensavertimer->stop();
+ }
+#endif
+ qt_disable_lowpriority_timers=true;
+}
+
+/*!
+ \fn void QWSServer::setScreenSaver(QWSScreenSaver* screenSaver)
+
+ Installs the given \a screenSaver, deleting the current screen
+ saver.
+
+ Note that this function can only be used in the server process.
+
+ \sa screenSaverActivate(), setScreenSaverInterval(), setScreenSaverIntervals(), setScreenSaverBlockLevel()
+*/
+void QWSServer::setScreenSaver(QWSScreenSaver* ss)
+{
+ QWSServerPrivate *qd = qwsServer->d_func();
+ delete qd->saver;
+ qd->saver = ss;
+}
+
+void QWSServerPrivate::screenSave(int level)
+{
+ if (saver) {
+ // saver->save() may call QCoreApplication::processEvents,
+ // block event before calling saver->save().
+ bool oldScreensaverblockevents = screensaverblockevents;
+ if (*screensaverinterval >= 1000) {
+ screensaverblockevents = (screensavereventblocklevel >= 0 && screensavereventblocklevel <= level);
+#ifdef EVENT_BLOCK_DEBUG
+ if (screensaverblockevents)
+ qDebug("ready to block events");
+#endif
+ }
+ int *oldScreensaverinterval = screensaverinterval;
+ if (saver->save(level)) {
+ // only update screensaverinterval if it hasn't already changed
+ if (oldScreensaverinterval == screensaverinterval) {
+ if (screensaverinterval && screensaverinterval[1]) {
+ screensavertimer->start(*++screensaverinterval);
+ screensavertime.start();
+ } else {
+ screensaverinterval = 0;
+ }
+ }
+ } else {
+ // restore previous state
+ screensaverblockevents = oldScreensaverblockevents;
+
+ // for some reason, the saver don't want us to change to the
+ // next level, so we'll stay at this level for another interval
+ if (screensaverinterval && *screensaverinterval) {
+ screensavertimer->start(*screensaverinterval);
+ screensavertime.start();
+ }
+ }
+ } else {
+ screensaverinterval = 0;//screensaverintervals;
+ screensaverblockevents = false;
+ _q_screenSaverSleep();
+ }
+}
+
+void QWSServerPrivate::_q_screenSaverTimeout()
+{
+ if (screensaverinterval) {
+ if (screensavertime.elapsed() > *screensaverinterval*2) {
+ // bogus (eg. unsuspend, system time changed)
+ _q_screenSaverWake(); // try again
+ return;
+ }
+ screenSave(screensaverinterval - screensaverintervals);
+ }
+}
+
+/*!
+ Returns true if the screen saver is active; otherwise returns
+ false.
+
+ Note that this function can only be used in the server process.
+
+ \sa screenSaverActivate()
+*/
+bool QWSServer::screenSaverActive()
+{
+ return qwsServerPrivate->screensaverinterval
+ && !qwsServerPrivate->screensavertimer->isActive();
+}
+
+/*!
+ \internal
+*/
+void QWSServer::updateWindowRegions() const
+{
+ qwsServerPrivate->update_regions();
+}
+
+/*!
+ Activates the screen saver if \a activate is true; otherwise it is
+ deactivated.
+
+ Note that this function can only be used in the server process.
+
+ \sa screenSaverActive(), setScreenSaver()
+*/
+void QWSServer::screenSaverActivate(bool activate)
+{
+ if (activate)
+ qwsServerPrivate->_q_screenSaverSleep();
+ else
+ qwsServerPrivate->_q_screenSaverWake();
+}
+
+void QWSServerPrivate::disconnectClient(QWSClient *c)
+{
+ QTimer::singleShot(0, c, SLOT(closeHandler()));
+}
+
+void QWSServerPrivate::updateClientCursorPos()
+{
+ Q_Q(QWSServer);
+ QWSWindow *win = qwsServerPrivate->mouseGrabber ? qwsServerPrivate->mouseGrabber : qwsServer->windowAt(QWSServer::mousePosition);
+ QWSClient *winClient = win ? win->client() : 0;
+ if (winClient && winClient != cursorClient)
+ q->sendMouseEvent(QWSServer::mousePosition, mouseState);
+}
+
+#ifndef QT_NO_QWS_INPUTMETHODS
+
+/*!
+ \class QWSInputMethod
+ \preliminary
+ \ingroup qws
+
+ \brief The QWSInputMethod class provides international input methods
+ in Qt for Embedded Linux.
+
+ Note that this class is only available in \l{Qt for Embedded Linux}.
+
+ A \l{Qt for Embedded Linux} application requires a server application to be
+ running, or to be the server application itself. All system
+ generated events, including keyboard and mouse events, are passed
+ to the server application which then propagates the event to the
+ appropriate client.
+
+ An input method consists of a filter and optionally a graphical
+ interface, and is used to filter input events between the server
+ and the client application.
+
+ \tableofcontents
+
+ \section1 Creating Custom Input Methods
+
+ To implement a custom input method, derive from the QWSInputMethod
+ class, and use the server's \l
+ {QWSServer::}{setCurrentInputMethod()} function to install it.
+
+ When subclassing QWSInputMethod, you can reimplement the filter()
+ functions to handle input from both physical and virtual keyboards
+ as well as mouse devices. Note that the default implementations do
+ nothing. Use the setInputResolution() function to control the
+ number of bits shifted when filtering mouse input, i.e., when
+ going from pointer resolution to screen resolution (the current
+ resolution can be retrieved using the inputResolutionShift()
+ function).
+
+ Reimplement the reset() function to restore the state of the input
+ method. Note that the default implementation calls the sendEvent()
+ function with empty preedit and commit strings if the input method
+ is in compose mode (i.e., if the input method is actively
+ composing a preedit string).
+
+ To receive replies to an input method query (sent using the
+ sendQuery() function), you must reimplement the queryResponse()
+ function, while the mouseHandler() function must be reimplemented
+ if you want to handle mouse events within the preedit
+ text. Reimplement the updateHandler() function to handle update
+ events including resets and focus changes. The UpdateType enum
+ describes the various types of update events recognized by the
+ input method.
+
+ \section1 Using Input Methods
+
+ In addition to the filter(), reset(), queryResponse(),
+ mouseHandler() and updateHandler() function mentioned in the
+ previous section, the QWSInputMethod provides several other
+ functions helping the window system to manage the installed input
+ methods.
+
+ The sendEvent() function sends the given event to the focus
+ widget, while the sendPreeditString() function sends the given
+ preedit text (encapsulated by an event). QWSInputMethod also
+ provides the sendCommitString() convenience function which sends
+ an event encapsulating the given commit string to the current
+ focus widget, and the sendMouseEvent() function which sends the
+ given mouse event.
+
+ Finally, the QWSInputMethod class provides the sendQuery()
+ function for sending input method queries. This function
+ encapsulates the event with a QWSEvent instance of the \l
+ {QWSEvent::}{IMQuery} type.
+
+ \sa QWSServer, {Qt for Embedded Linux Architecture}
+*/
+
+/*!
+ Constructs a new input method.
+
+ Use the QWSServer::setCurrentInputMethod() function to install it.
+*/
+
+QWSInputMethod::QWSInputMethod()
+{
+
+}
+
+/*!
+ Destroys this input method, uninstalling it if it is installed.
+*/
+QWSInputMethod::~QWSInputMethod()
+{
+ if (current_IM == this)
+ current_IM = 0;
+}
+
+/*!
+ Filters the key input identified by the given \a unicode, \a
+ keycode, \a modifiers, \a isPress and \a autoRepeat parameters.
+
+ Note that the default implementation does nothing; reimplement
+ this function to handle input from both physical and virtual
+ devices.
+
+ The \a keycode is a Qt::Key value, and the \a modifiers is an OR
+ combination of Qt::KeyboardModifiers. The \a isPress parameter is
+ telling whether the input is a key press or key release, and the
+ \a autoRepeat parameter determines whether the input is
+ autorepeated ( i.e., in which case the
+ QWSKeyboardHandler::beginAutoRepeat() function has been called).
+
+ To block the event from further processing, return true when
+ reimplementing this function; the default implementation returns
+ false.
+
+ \sa setInputResolution(), inputResolutionShift()
+*/
+bool QWSInputMethod::filter(int unicode, int keycode, int modifiers, bool isPress, bool autoRepeat)
+{
+ Q_UNUSED(unicode);
+ Q_UNUSED(keycode);
+ Q_UNUSED(modifiers);
+ Q_UNUSED(isPress);
+ Q_UNUSED(autoRepeat);
+ return false;
+}
+
+/*!
+ \overload
+
+ Filters the mouse input identified by the given \a position, \a
+ state, and \a wheel parameters.
+*/
+bool QWSInputMethod::filter(const QPoint &position, int state, int wheel)
+{
+ Q_UNUSED(position);
+ Q_UNUSED(state);
+ Q_UNUSED(wheel);
+ return false;
+}
+
+/*!
+ Resets the state of the input method.
+
+ If the input method is in compose mode, i.e., the input method is
+ actively composing a preedit string, the default implementation
+ calls sendEvent() with empty preedit and commit strings; otherwise
+ it does nothing. Reimplement this function to alter this behavior.
+
+ \sa sendEvent()
+*/
+void QWSInputMethod::reset()
+{
+ if (current_IM_composing_win) {
+ QInputMethodEvent ime;
+ sendEvent(&ime);
+ }
+}
+
+/*!
+ \enum QWSInputMethod::UpdateType
+
+ This enum describes the various types of update events recognized
+ by the input method.
+
+ \value Update The input widget is updated in some way; use sendQuery() with
+ Qt::ImMicroFocus as an argument for more information.
+ \value FocusIn A new input widget receives focus.
+ \value FocusOut The input widget loses focus.
+ \value Reset The input method should be reset.
+ \value Destroyed The input widget is destroyed.
+
+ \sa updateHandler()
+*/
+
+/*!
+ Handles update events including resets and focus changes. The
+ update events are specified by the given \a type which is one of
+ the UpdateType enum values.
+
+ Note that reimplementations of this function must call the base
+ implementation for all cases that it does not handle itself.
+
+ \sa UpdateType
+*/
+void QWSInputMethod::updateHandler(int type)
+{
+ switch (type) {
+ case FocusOut:
+ case Reset:
+ reset();
+ break;
+
+ default:
+ break;
+ }
+}
+
+
+/*!
+ Receive replies to an input method query.
+
+ Note that the default implementation does nothing; reimplement
+ this function to receive such replies.
+
+ Internally, an input method query is passed encapsulated by an \l
+ {QWSEvent::IMQuery}{IMQuery} event generated by the sendQuery()
+ function. The queried property and the result is passed in the \a
+ property and \a result parameters.
+
+ \sa sendQuery(), QWSServer::sendIMQuery()
+*/
+void QWSInputMethod::queryResponse(int property, const QVariant &result)
+{
+ Q_UNUSED(property);
+ Q_UNUSED(result);
+}
+
+
+
+/*!
+ \fn void QWSInputMethod::mouseHandler(int offset, int state)
+
+ Handles mouse events within the preedit text.
+
+ Note that the default implementation resets the input method on
+ all mouse presses; reimplement this function to alter this
+ behavior.
+
+ The \a offset parameter specifies the position of the mouse event
+ within the string, and \a state specifies the type of the mouse
+ event as described by the QWSServer::IMMouse enum. If \a state is
+ less than 0, the mouse event is inside the associated widget, but
+ outside the preedit text. When clicking in a different widget, the
+ \a state is QWSServer::MouseOutside.
+
+ \sa sendPreeditString(), reset()
+*/
+void QWSInputMethod::mouseHandler(int, int state)
+{
+ if (state == QWSServer::MousePress || state == QWSServer::MouseOutside)
+ reset();
+}
+
+
+/*!
+ Sends an event encapsulating the given \a preeditString, to the
+ focus widget.
+
+ The specified \a selectionLength is the number of characters to be
+ marked as selected (starting at the given \a cursorPosition). If
+ \a selectionLength is negative, the text \e before \a
+ cursorPosition is marked.
+
+ The preedit string is marked with QInputContext::PreeditFormat,
+ and the selected part is marked with
+ QInputContext::SelectionFormat.
+
+ Sending an input method event with a non-empty preedit string will
+ cause the input method to enter compose mode. Sending an input
+ method event with an empty preedit string will cause the input
+ method to leave compose mode, i.e., the input method will no longer
+ be actively composing the preedit string.
+
+ Internally, the event is represented by a QWSEvent object of the
+ \l {QWSEvent::IMEvent}{IMEvent} type.
+
+ \sa sendEvent(), sendCommitString()
+*/
+
+void QWSInputMethod::sendPreeditString(const QString &preeditString, int cursorPosition, int selectionLength)
+{
+ QList<QInputMethodEvent::Attribute> attributes;
+
+ int selPos = cursorPosition;
+ if (selectionLength == 0) {
+ selPos = 0;
+ } else if (selectionLength < 0) {
+ selPos += selectionLength;
+ selectionLength = -selectionLength;
+ }
+ if (selPos > 0)
+ attributes += QInputMethodEvent::Attribute(QInputMethodEvent::TextFormat, 0, selPos,
+ QVariant(int(QInputContext::PreeditFormat)));
+
+ if (selectionLength)
+ attributes += QInputMethodEvent::Attribute(QInputMethodEvent::TextFormat, selPos, selectionLength,
+ QVariant(int(QInputContext::SelectionFormat)));
+
+ if (selPos + selectionLength < preeditString.length())
+ attributes += QInputMethodEvent::Attribute(QInputMethodEvent::TextFormat,
+ selPos + selectionLength,
+ preeditString.length() - selPos - selectionLength,
+ QVariant(int(QInputContext::PreeditFormat)));
+
+ attributes += QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, cursorPosition, 0, QVariant());
+
+ QInputMethodEvent ime(preeditString, attributes);
+ qwsServer->sendIMEvent(&ime);
+}
+
+/*!
+ \fn void QWSInputMethod::sendCommitString(const QString &commitString, int replaceFromPosition, int replaceLength)
+
+ Sends an event encapsulating the given \a commitString, to the
+ focus widget.
+
+ Note that this will cause the input method to leave compose mode,
+ i.e., the input method will no longer be actively composing the
+ preedit string.
+
+ If the specified \a replaceLength is greater than 0, the commit
+ string will replace the given number of characters of the
+ receiving widget's previous text, starting at the given \a
+ replaceFromPosition relative to the start of the current preedit
+ string.
+
+ Internally, the event is represented by a QWSEvent object of the
+ \l {QWSEvent::IMEvent}{IMEvent} type.
+
+ \sa sendEvent(), sendPreeditString()
+*/
+void QWSInputMethod::sendCommitString(const QString &commitString, int replaceFrom, int replaceLength)
+{
+ QInputMethodEvent ime;
+ ime.setCommitString(commitString, replaceFrom, replaceLength);
+ qwsServer->sendIMEvent(&ime);
+}
+
+/*!
+ \fn QWSInputMethod::sendIMEvent(QWSServer::IMState state, const QString &text, int cursorPosition, int selectionLength)
+ \obsolete
+
+ Sends a QInputMethodEvent object to the focus widget.
+
+ If the specified \a state is QWSServer::IMCompose, \a text is a
+ preedit string, \a cursorPosition is the cursor's position within
+ the preedit string, and \a selectionLength is the number of
+ characters (starting at \a cursorPosition) that should be marked
+ as selected by the input widget receiving the event. If the
+ specified \a state is QWSServer::IMEnd, \a text is a commit
+ string.
+
+ Use sendEvent(), sendPreeditString() or sendCommitString() instead.
+*/
+
+/*!
+ \fn QWSInputMethod::sendEvent(const QInputMethodEvent *event)
+
+ Sends the given \a event to the focus widget.
+
+ The \c QInputMethodEvent class is derived from QWSEvent, i.e., the
+ given \a event is a QWSEvent object of the \l
+ {QWSEvent::IMEvent}{IMEvent} type.
+
+ \sa sendPreeditString(), sendCommitString(), reset()
+*/
+
+
+/*!
+ \fn void QWSInputMethod::sendQuery(int property)
+
+ Sends an input method query (internally encapsulated by a QWSEvent
+ of the \l {QWSEvent::IMQuery}{IMQuery} type) for the specified \a
+ property.
+
+ To receive responses to input method queries, the virtual
+ queryResponse() function must be reimplemented.
+
+ \sa queryResponse(), QWSServer::sendIMQuery()
+*/
+
+/*!
+ Sets and returns the number of bits shifted to go from pointer
+ resolution to screen resolution when filtering mouse input.
+
+ If \a isHigh is true and the device has a pointer device
+ resolution twice or more of the screen resolution, the positions
+ passed to the filter() function will be presented at the higher
+ resolution; otherwise the resolution will be equal to that of the
+ screen resolution.
+
+ \sa inputResolutionShift(), filter()
+*/
+uint QWSInputMethod::setInputResolution(bool isHigh)
+{
+ mIResolution = isHigh;
+ return inputResolutionShift();
+}
+
+/*!
+ Returns the number of bits shifted to go from pointer resolution
+ to screen resolution when filtering mouse input.
+
+ \sa setInputResolution(), filter()
+*/
+uint QWSInputMethod::inputResolutionShift() const
+{
+ return 0; // default for devices with single resolution.
+}
+
+/*!
+ \fn void QWSInputMethod::sendMouseEvent( const QPoint &position, int state, int wheel )
+
+ Sends a mouse event specified by the given \a position, \a state
+ and \a wheel parameters.
+
+ The given \a position will be transformed if the screen
+ coordinates do not match the pointer device coordinates.
+
+ Note that the event will be not be tested by the active input
+ method, but calling the QWSServer::sendMouseEvent() function will
+ make the current input method filter the event.
+
+ \sa mouseHandler(), sendEvent()
+*/
+void QWSInputMethod::sendMouseEvent( const QPoint &pos, int state, int wheel )
+{
+ if (qt_last_x) {
+ *qt_last_x = pos.x();
+ *qt_last_y = pos.y();
+ }
+ QWSServer::mousePosition = pos;
+ qwsServerPrivate->mouseState = state;
+ QWSServerPrivate::sendMouseEventUnfiltered(pos, state, wheel);
+}
+#endif // QT_NO_QWS_INPUTMETHODS
+
+/*!
+ \fn QWSWindow::QWSWindow(int i, QWSClient * client)
+ \internal
+
+ Constructs a new top-level window, associated with the client \a
+ client and giving it the id \a i.
+*/
+
+/*!
+ \fn QWSServer::windowEvent(QWSWindow * window, QWSServer::WindowEvent eventType)
+
+ This signal is emitted whenever something happens to a top-level
+ window (e.g., it's created or destroyed), passing a pointer to the
+ window and the event's type in the \a window and \a eventType
+ parameters, respectively.
+
+ \sa markedText()
+*/
+
+/*!
+ \class QWSServer::KeyboardFilter
+ \ingroup qws
+
+ \brief The KeyboardFilter class is a base class for global
+ keyboard event filters in Qt for Embedded Linux.
+
+ Note that this class is only available in \l{Qt for Embedded Linux}.
+
+ In \l{Qt for Embedded Linux}, all system generated events, including
+ keyboard events, are passed to the server application which then
+ propagates the event to the appropriate client. The KeyboardFilter
+ class is used to implement a global, low-level filter on the
+ server side. The server applies the filter to all keyboard events
+ before passing them on to the clients:
+
+ \image qwsserver_keyboardfilter.png
+
+ This feature can, for example, be used to filter things like APM
+ (advanced power management) suspended from a button without having
+ to filter for it in all applications.
+
+ To add a new keyboard filter you must first create the filter by
+ deriving from this class, reimplementing the pure virtual filter()
+ function. Then you can install the filter on the server using
+ QWSServer's \l {QWSServer::}{addKeyboardFilter()}
+ function. QWSServer also provides a \l
+ {QWSServer::}{removeKeyboardFilter()} function.
+
+ \sa {Qt for Embedded Linux Architecture}, QWSServer, QWSInputMethod
+*/
+
+/*!
+ \fn QWSServer::KeyboardFilter::~KeyboardFilter()
+
+ Destroys the keyboard filter.
+*/
+
+/*!
+ \fn bool QWSServer::KeyboardFilter::filter(int unicode, int keycode, int modifiers, bool isPress, bool autoRepeat)
+
+ Implement this function to return true if a given key event should
+ be stopped from being processed any further; otherwise it should
+ return false.
+
+ A key event can be identified by the given \a unicode value and
+ the \a keycode, \a modifiers, \a isPress and \a autoRepeat
+ parameters.
+
+ The \a keycode parameter is the Qt keycode value as defined by the
+ Qt::Key enum. The \a modifiers is an OR combination of
+ Qt::KeyboardModifier values, indicating whether \gui
+ Shift/Alt/Ctrl keys are pressed. The \a isPress parameter is true
+ if the event is a key press event and \a autoRepeat is true if the
+ event is caused by an auto-repeat mechanism and not an actual key
+ press.
+*/
+
+QT_END_NAMESPACE
+
+#include "moc_qwindowsystem_qws.cpp"
diff --git a/src/gui/embedded/qwindowsystem_qws.h b/src/gui/embedded/qwindowsystem_qws.h
new file mode 100644
index 0000000000..8af59a3aa9
--- /dev/null
+++ b/src/gui/embedded/qwindowsystem_qws.h
@@ -0,0 +1,508 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QWINDOWSYSTEM_QWS_H
+#define QWINDOWSYSTEM_QWS_H
+
+#include <QtCore/qbytearray.h>
+#include <QtCore/qmap.h>
+#include <QtCore/qdatetime.h>
+#include <QtCore/qlist.h>
+
+#include <QtGui/qwsevent_qws.h>
+#include <QtGui/qkbd_qws.h>
+#include <QtGui/qregion.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+struct QWSWindowPrivate;
+class QWSCursor;
+class QWSClient;
+class QWSRegionManager;
+class QBrush;
+class QVariant;
+class QInputMethodEvent;
+class QWSInputMethod;
+class QWSBackingStore;
+class QWSWindowSurface;
+
+#ifdef QT3_SUPPORT
+class QImage;
+class QColor;
+#endif
+
+class QWSInternalWindowInfo
+{
+public:
+ int winid;
+ unsigned int clientid;
+ QString name; // Corresponds to QObject name of top-level widget
+};
+
+
+class Q_GUI_EXPORT QWSScreenSaver
+{
+public:
+ virtual ~QWSScreenSaver();
+ virtual void restore()=0;
+ virtual bool save(int level)=0;
+};
+
+
+class Q_GUI_EXPORT QWSWindow
+{
+ friend class QWSServer;
+ friend class QWSServerPrivate;
+
+public:
+ QWSWindow(int i, QWSClient* client);
+ ~QWSWindow();
+
+ int winId() const { return id; }
+ const QString &name() const { return rgnName; }
+ const QString &caption() const { return rgnCaption; }
+ QWSClient* client() const { return c; }
+ const QRegion &requestedRegion() const { return requested_region; }
+ QRegion allocatedRegion() const;
+ QRegion paintedRegion() const;
+ bool isVisible() const { return !requested_region.isEmpty(); }
+ bool isPartiallyObscured() const { return requested_region != allocatedRegion(); }
+ bool isFullyObscured() const { return allocatedRegion().isEmpty(); }
+
+ enum State { NoState, Hidden, Showing, Visible, Hiding, Raising, Lowering, Moving, ChangingGeometry, Destroyed };
+ State state() const;
+ Qt::WindowFlags windowFlags() const;
+ QRegion dirtyOnScreen() const;
+
+ void raise();
+ void lower();
+ void show();
+ void hide();
+ void setActiveWindow();
+
+ bool isOpaque() const {return opaque && _opacity == 255;}
+ uint opacity() const { return _opacity; }
+
+ QWSWindowSurface* windowSurface() const { return surface; }
+
+private:
+ bool hidden() const { return requested_region.isEmpty(); }
+ bool forClient(const QWSClient* cl) const { return cl==c; }
+
+ void setName(const QString &n);
+ void setCaption(const QString &c);
+
+ void focus(bool get);
+ int focusPriority() const { return last_focus_time; }
+ void operation(QWSWindowOperationEvent::Operation o);
+ void shuttingDown() { last_focus_time=0; }
+
+#ifdef QT_QWS_CLIENTBLIT
+ QRegion directPaintRegion() const;
+ inline void setDirectPaintRegion(const QRegion &topmost);
+#endif
+ inline void setAllocatedRegion(const QRegion &region);
+
+ void createSurface(const QString &key, const QByteArray &data);
+
+#ifndef QT_NO_QWSEMBEDWIDGET
+ void startEmbed(QWSWindow *window);
+ void stopEmbed(QWSWindow *window);
+#endif
+
+private:
+ int id;
+ QString rgnName;
+ QString rgnCaption;
+ bool modified;
+ bool onTop;
+ QWSClient* c;
+ QRegion requested_region;
+ QRegion exposed;
+ int last_focus_time;
+ QWSWindowSurface *surface;
+ uint _opacity;
+ bool opaque;
+ QWSWindowPrivate *d;
+#ifdef QT3_SUPPORT
+ inline QT3_SUPPORT QRegion requested() const { return requested_region; }
+// inline QT3_SUPPORT QRegion allocation() const { return allocated_region; }
+#endif
+};
+
+
+#ifndef QT_NO_SOUND
+class QWSSoundServer;
+#ifdef QT_USE_OLD_QWS_SOUND
+class QWSSoundServerData;
+
+class Q_GUI_EXPORT QWSSoundServer : public QObject {
+ Q_OBJECT
+public:
+ QWSSoundServer(QObject* parent);
+ ~QWSSoundServer();
+ void playFile(const QString& filename);
+private Q_SLOTS:
+ void feedDevice(int fd);
+private:
+ QWSSoundServerData* d;
+};
+#endif
+#endif
+
+
+/*********************************************************************
+ *
+ * Class: QWSServer
+ *
+ *********************************************************************/
+
+class QWSMouseHandler;
+struct QWSCommandStruct;
+class QWSServerPrivate;
+class QWSServer;
+
+extern Q_GUI_EXPORT QWSServer *qwsServer;
+
+class Q_GUI_EXPORT QWSServer : public QObject
+{
+ friend class QCopChannel;
+ friend class QWSMouseHandler;
+ friend class QWSWindow;
+ friend class QWSDisplay;
+ friend class QWSInputMethod;
+ Q_OBJECT
+ Q_DECLARE_PRIVATE(QWSServer)
+public:
+ explicit QWSServer(int flags = 0, QObject *parent=0);
+#ifdef QT3_SUPPORT
+ QT3_SUPPORT_CONSTRUCTOR QWSServer(int flags, QObject *parent, const char *name);
+#endif
+ ~QWSServer();
+ enum ServerFlags { DisableKeyboard = 0x01,
+ DisableMouse = 0x02 };
+
+ static void sendKeyEvent(int unicode, int keycode, Qt::KeyboardModifiers modifiers,
+ bool isPress, bool autoRepeat);
+#ifndef QT_NO_QWS_KEYBOARD
+ static void processKeyEvent(int unicode, int keycode, Qt::KeyboardModifiers modifiers,
+ bool isPress, bool autoRepeat);
+#endif
+
+ static QWSServer* instance() { return qwsServer; }
+
+#ifndef QT_NO_QWS_INPUTMETHODS
+#ifdef QT3_SUPPORT
+ enum IMState { IMCompose, IMEnd, IMStart = IMCompose };
+#endif
+ enum IMMouse { MousePress, MouseRelease, MouseMove, MouseOutside }; //MouseMove reserved but not used
+ void sendIMEvent(const QInputMethodEvent*);
+ void sendIMQuery(int property);
+#endif
+
+#ifndef QT_NO_QWS_KEYBOARD
+ class KeyboardFilter
+ {
+ public:
+ virtual ~KeyboardFilter() {}
+ virtual bool filter(int unicode, int keycode, int modifiers,
+ bool isPress, bool autoRepeat)=0;
+ };
+ static void addKeyboardFilter(KeyboardFilter *f);
+ static void removeKeyboardFilter();
+#endif
+
+#ifndef QT_NO_QWS_INPUTMETHODS
+ static void setCurrentInputMethod(QWSInputMethod *im);
+ static void resetInputMethod();
+#endif
+
+ static void setDefaultMouse(const char *);
+ static void setDefaultKeyboard(const char *);
+ static void setMaxWindowRect(const QRect&);
+ static void sendMouseEvent(const QPoint& pos, int state, int wheel = 0);
+
+ static void setBackground(const QBrush &);
+#ifdef QT3_SUPPORT
+ static QT3_SUPPORT void setDesktopBackground(const QImage &img);
+ static QT3_SUPPORT void setDesktopBackground(const QColor &);
+#endif
+ static QWSMouseHandler *mouseHandler();
+ static const QList<QWSMouseHandler*>& mouseHandlers();
+ static void setMouseHandler(QWSMouseHandler*);
+#ifndef QT_NO_QWS_KEYBOARD
+ static QWSKeyboardHandler* keyboardHandler();
+ static void setKeyboardHandler(QWSKeyboardHandler* kh);
+#endif
+ QWSWindow *windowAt(const QPoint& pos);
+
+ const QList<QWSWindow*> &clientWindows();
+
+ void openMouse();
+ void closeMouse();
+ void suspendMouse();
+ void resumeMouse();
+#ifndef QT_NO_QWS_KEYBOARD
+ void openKeyboard();
+ void closeKeyboard();
+#endif
+
+ static void setScreenSaver(QWSScreenSaver*);
+ static void setScreenSaverIntervals(int* ms);
+ static void setScreenSaverInterval(int);
+ static void setScreenSaverBlockLevel(int);
+ static bool screenSaverActive();
+ static void screenSaverActivate(bool);
+
+ // the following are internal.
+ void refresh();
+ void refresh(QRegion &);
+
+ void enablePainting(bool);
+ static void processEventQueue();
+ static QList<QWSInternalWindowInfo*> * windowList();
+
+ void sendPropertyNotifyEvent(int property, int state);
+
+ static QPoint mousePosition;
+
+ static void startup(int flags);
+ static void closedown();
+
+ static void beginDisplayReconfigure();
+ static void endDisplayReconfigure();
+
+#ifndef QT_NO_QWS_CURSOR
+ static void setCursorVisible(bool);
+ static bool isCursorVisible();
+#endif
+
+ const QBrush &backgroundBrush() const;
+
+ enum WindowEvent { Create=0x0001, Destroy=0x0002, Hide=0x0004, Show=0x0008,
+ Raise=0x0010, Lower=0x0020, Geometry=0x0040, Active = 0x0080,
+ Name=0x0100 };
+
+Q_SIGNALS:
+ void windowEvent(QWSWindow *w, QWSServer::WindowEvent e);
+
+#ifndef QT_NO_COP
+ void newChannel(const QString& channel);
+ void removedChannel(const QString& channel);
+
+#endif
+#ifndef QT_NO_QWS_INPUTMETHODS
+ void markedText(const QString &);
+#endif
+
+protected:
+ void timerEvent(QTimerEvent *e);
+
+private:
+ friend class QApplicationPrivate;
+ void updateWindowRegions() const;
+
+#ifdef QT3_SUPPORT
+#ifndef QT_NO_QWS_KEYBOARD
+ static inline QT3_SUPPORT void setKeyboardFilter(QWSServer::KeyboardFilter *f)
+ { if (f) addKeyboardFilter(f); else removeKeyboardFilter(); }
+#endif
+#endif
+
+private:
+#ifndef QT_NO_QWS_MULTIPROCESS
+ Q_PRIVATE_SLOT(d_func(), void _q_clientClosed())
+ Q_PRIVATE_SLOT(d_func(), void _q_doClient())
+ Q_PRIVATE_SLOT(d_func(), void _q_deleteWindowsLater())
+#endif
+
+ Q_PRIVATE_SLOT(d_func(), void _q_screenSaverWake())
+ Q_PRIVATE_SLOT(d_func(), void _q_screenSaverSleep())
+ Q_PRIVATE_SLOT(d_func(), void _q_screenSaverTimeout())
+
+#ifndef QT_NO_QWS_MULTIPROCESS
+ Q_PRIVATE_SLOT(d_func(), void _q_newConnection())
+#endif
+};
+
+#ifndef QT_NO_QWS_INPUTMETHODS
+class Q_GUI_EXPORT QWSInputMethod : public QObject
+{
+ Q_OBJECT
+public:
+ QWSInputMethod();
+ virtual ~QWSInputMethod();
+
+ enum UpdateType {Update, FocusIn, FocusOut, Reset, Destroyed};
+
+ virtual bool filter(int unicode, int keycode, int modifiers,
+ bool isPress, bool autoRepeat);
+
+ virtual bool filter(const QPoint &, int state, int wheel);
+
+ virtual void reset();
+ virtual void updateHandler(int type);
+ virtual void mouseHandler(int pos, int state);
+ virtual void queryResponse(int property, const QVariant&);
+
+protected:
+ uint setInputResolution(bool isHigh);
+ uint inputResolutionShift() const;
+ // needed for required transform
+ void sendMouseEvent(const QPoint &pos, int state, int wheel);
+
+ void sendEvent(const QInputMethodEvent*);
+ void sendPreeditString(const QString &preeditString, int cursorPosition, int selectionLength = 0);
+ void sendCommitString(const QString &commitString, int replaceFrom = 0, int replaceLength = 0);
+ void sendQuery(int property);
+
+#ifdef QT3_SUPPORT
+ inline void sendIMEvent(QWSServer::IMState, const QString& txt, int cpos, int selLen = 0);
+#endif
+private:
+ bool mIResolution;
+};
+
+inline void QWSInputMethod::sendEvent(const QInputMethodEvent *ime)
+{
+ qwsServer->sendIMEvent(ime);
+}
+#ifdef QT3_SUPPORT
+inline void QWSInputMethod::sendIMEvent(QWSServer::IMState state, const QString& txt, int cpos, int selLen)
+{
+ if (state == QWSServer::IMCompose) sendPreeditString(txt, cpos, selLen); else sendCommitString(txt);
+}
+#endif
+
+inline void QWSInputMethod::sendQuery(int property)
+{
+ qwsServer->sendIMQuery(property);
+}
+
+// mouse events not inline as involve transformations.
+#endif // QT_NO_QWS_INPUTMETHODS
+
+
+
+/*********************************************************************
+ *
+ * Class: QWSClient
+ *
+ *********************************************************************/
+
+struct QWSMouseEvent;
+
+typedef QMap<int, QWSCursor*> QWSCursorMap;
+
+class QWSClientPrivate;
+class QWSCommand;
+class QWSConvertSelectionCommand;
+
+class Q_GUI_EXPORT QWSClient : public QObject
+{
+ Q_OBJECT
+ Q_DECLARE_PRIVATE(QWSClient)
+public:
+ QWSClient(QObject* parent, QWS_SOCK_BASE *, int id);
+ ~QWSClient();
+
+ int socket() const;
+
+ void setIdentity(const QString&);
+ QString identity() const { return id; }
+
+ void sendEvent(QWSEvent* event);
+ void sendConnectedEvent(const char *display_spec);
+ void sendMaxWindowRectEvent(const QRect &rect);
+ void sendPropertyNotifyEvent(int property, int state);
+ void sendPropertyReplyEvent(int property, int len, const char *data);
+ void sendSelectionClearEvent(int windowid);
+ void sendSelectionRequestEvent(QWSConvertSelectionCommand *cmd, int windowid);
+#ifndef QT_QWS_CLIENTBLIT
+ void sendRegionEvent(int winid, QRegion rgn, int type);
+#else
+ void sendRegionEvent(int winid, QRegion rgn, int type, int id = 0);
+#endif
+#ifndef QT_NO_QWSEMBEDWIDGET
+ void sendEmbedEvent(int winid, QWSEmbedEvent::Type type,
+ const QRegion &region = QRegion());
+#endif
+ QWSCommand* readMoreCommand();
+
+ int clientId() const { return cid; }
+
+ QWSCursorMap cursors; // cursors defined by this client
+Q_SIGNALS:
+ void connectionClosed();
+ void readyRead();
+private Q_SLOTS:
+ void closeHandler();
+ void errorHandler();
+
+private:
+#ifndef QT_NO_QWS_MULTIPROCESS
+ friend class QWSWindow;
+ void removeUnbufferedSurface();
+ void addUnbufferedSurface();
+#endif
+
+private:
+ int socketDescriptor;
+#ifndef QT_NO_QWS_MULTIPROCESS
+ QWSSocket *csocket;
+#endif
+ QWSCommand* command;
+ uint isClosed : 1;
+ QString id;
+ int cid;
+
+ friend class QWSServerPrivate;
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QWINDOWSYSTEM_QWS_H
diff --git a/src/gui/embedded/qwscommand_qws.cpp b/src/gui/embedded/qwscommand_qws.cpp
new file mode 100644
index 0000000000..88e33a35e4
--- /dev/null
+++ b/src/gui/embedded/qwscommand_qws.cpp
@@ -0,0 +1,610 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qwscommand_qws_p.h"
+#include "qtransportauth_qws.h"
+#include "qtransportauth_qws_p.h"
+
+#include <sys/uio.h>
+#include <unistd.h>
+
+// #define QWSCOMMAND_DEBUG 1 // Uncomment to debug client/server communication
+
+#ifdef QWSCOMMAND_DEBUG
+# include <qdebug.h>
+# include "qfile.h"
+# include <ctype.h>
+#endif
+
+QT_BEGIN_NAMESPACE
+
+#ifdef QWSCOMMAND_DEBUG
+// QWSHexDump -[ start ]---------------------------------------------
+# define QWSHEXDUMP_MAX 32
+class QWSHexDump
+{
+public:
+
+ QWSHexDump(const void *address, int len, int wrapAt = 16)
+ : wrap(wrapAt), dataSize(len)
+ {
+ init();
+ data = reinterpret_cast<const char*>(address);
+ if (len < 0)
+ dataSize = 0;
+ }
+
+ QWSHexDump(const char *str, int len = -1, int wrapAt = 16)
+ : wrap(wrapAt), dataSize(len)
+ {
+ init();
+ data = str;
+ if (len == -1)
+ dataSize = str ? strlen(str) : 0;
+ }
+
+ QWSHexDump(const QByteArray &array, int wrapAt = 16)
+ : wrap(wrapAt)
+ {
+ init();
+ data = array.data();
+ dataSize = array.size();
+ }
+
+ // Sets a customized prefix for the hexdump
+ void setPrefix(const char *str) { prefix = str; }
+
+ // Sets number of bytes to cluster together
+ void setClusterSize(uint num) { clustering = num; }
+
+ // Output hexdump to a text stream
+ void intoTextStream(QTextStream &strm) {
+ outstrm = &strm;
+ hexDump();
+ }
+
+ // Output hexdump to a QString
+ QString toString();
+
+protected:
+ void init();
+ void hexDump();
+ void sideviewDump(int at);
+
+private:
+ uint wrap;
+ uint clustering;
+ uint dataSize;
+ int dataWidth;
+ const char *data;
+ const char *prefix;
+ bool dirty;
+
+ char sideviewLayout[QWSHEXDUMP_MAX + 1];
+ char sideview[15];
+
+ QTextStream *outstrm;
+};
+
+void QWSHexDump::init()
+{
+ prefix = "> "; // Standard line prefix
+ clustering = 2; // Word-size clustering by default
+ if (wrap > QWSHEXDUMP_MAX) // No wider than QWSHexDump_MAX bytes
+ wrap = QWSHEXDUMP_MAX;
+}
+
+void QWSHexDump::hexDump()
+{
+ *outstrm << "(" << dataSize << " bytes):\n" << prefix;
+ sprintf(sideviewLayout, " [%%-%us]", wrap);
+ dataWidth = (2 * wrap) + (wrap / clustering);
+
+ dirty = false;
+ uint wrapIndex = 0;
+ for (uint i = 0; i < dataSize; i++) {
+ uint c = static_cast<uchar>(data[i]);
+ sideview[wrapIndex = i%wrap] = isprint(c) ? c : '.';
+
+ if (wrapIndex && (wrapIndex % clustering == 0))
+ *outstrm << " ";
+
+ outstrm->setFieldWidth(2);
+ outstrm->setPadChar('0');
+ outstrm->setNumberFlags( QTextStream::ShowBase );
+ *outstrm << hex << c;
+ dirty = true;
+
+ if (wrapIndex == wrap-1) {
+ sideviewDump(wrapIndex);
+ wrapIndex = 0;
+ if (i+1 < dataSize)
+ *outstrm << endl << prefix;
+ }
+
+ }
+ sideviewDump(wrapIndex);
+}
+
+void QWSHexDump::sideviewDump(int at)
+{
+ if (dirty) {
+ dirty = false;
+ ++at;
+ sideview[at] = '\0';
+ int currentWidth = (2 * at) + (at / clustering) - (at%clustering?0:1);
+ int missing = qMax(dataWidth - currentWidth, 0);
+ while (missing--)
+ *outstrm << " ";
+
+ *outstrm << " [";
+ outstrm->setPadChar(' ');
+ outstrm->setFieldWidth(wrap);
+ outstrm->setFieldAlignment( QTextStream::AlignLeft );
+ *outstrm << sideview;
+ *outstrm << "]";
+ }
+}
+
+// Output hexdump to a QString
+QString QWSHexDump::toString() {
+ QString result;
+ QTextStream strm(&result, QFile::WriteOnly);
+ outstrm = &strm;
+ hexDump();
+ return result;
+}
+
+#ifndef QT_NO_DEBUG
+QDebug &operator<<(QDebug &dbg, QWSHexDump *hd) {
+ if (!hd)
+ return dbg << "QWSHexDump(0x0)";
+ QString result = hd->toString();
+ dbg.nospace() << result;
+ return dbg.space();
+}
+
+// GCC & Intel wont handle references here
+QDebug operator<<(QDebug dbg, QWSHexDump hd) {
+ return dbg << &hd;
+}
+#endif
+// QWSHexDump -[ end ]-----------------------------------------------
+
+
+QDebug &operator<<(QDebug &dbg, QWSCommand::Type tp)
+{
+ dbg << qws_getCommandTypeString( tp );
+ return dbg;
+}
+
+#define N_EVENTS 19
+const char * eventNames[N_EVENTS] = {
+ "NoEvent",
+ "Connected",
+ "Mouse", "Focus", "Key",
+ "Region",
+ "Creation",
+ "PropertyNotify",
+ "PropertyReply",
+ "SelectionClear",
+ "SelectionRequest",
+ "SelectionNotify",
+ "MaxWindowRect",
+ "QCopMessage",
+ "WindowOperation",
+ "IMEvent",
+ "IMQuery",
+ "IMInit",
+ "Font"
+ };
+
+class QWSServer;
+extern QWSServer *qwsServer;
+#endif
+
+const char *qws_getCommandTypeString( QWSCommand::Type tp )
+{
+ const char *typeStr;
+ switch(tp) {
+ case QWSCommand::Create:
+ typeStr = "Create";
+ break;
+ case QWSCommand::Shutdown:
+ typeStr = "Shutdown";
+ break;
+ case QWSCommand::Region:
+ typeStr = "Region";
+ break;
+ case QWSCommand::RegionMove:
+ typeStr = "RegionMove";
+ break;
+ case QWSCommand::RegionDestroy:
+ typeStr = "RegionDestroy";
+ break;
+ case QWSCommand::SetProperty:
+ typeStr = "SetProperty";
+ break;
+ case QWSCommand::AddProperty:
+ typeStr = "AddProperty";
+ break;
+ case QWSCommand::RemoveProperty:
+ typeStr = "RemoveProperty";
+ break;
+ case QWSCommand::GetProperty:
+ typeStr = "GetProperty";
+ break;
+ case QWSCommand::SetSelectionOwner:
+ typeStr = "SetSelectionOwner";
+ break;
+ case QWSCommand::ConvertSelection:
+ typeStr = "ConvertSelection";
+ break;
+ case QWSCommand::RequestFocus:
+ typeStr = "RequestFocus";
+ break;
+ case QWSCommand::ChangeAltitude:
+ typeStr = "ChangeAltitude";
+ break;
+ case QWSCommand::SetOpacity:
+ typeStr = "SetOpacity";
+ break;
+ case QWSCommand::DefineCursor:
+ typeStr = "DefineCursor";
+ break;
+ case QWSCommand::SelectCursor:
+ typeStr = "SelectCursor";
+ break;
+ case QWSCommand::PositionCursor:
+ typeStr = "PositionCursor";
+ break;
+ case QWSCommand::GrabMouse:
+ typeStr = "GrabMouse";
+ break;
+ case QWSCommand::PlaySound:
+ typeStr = "PlaySound";
+ break;
+ case QWSCommand::QCopRegisterChannel:
+ typeStr = "QCopRegisterChannel";
+ break;
+ case QWSCommand::QCopSend:
+ typeStr = "QCopSend";
+ break;
+ case QWSCommand::RegionName:
+ typeStr = "RegionName";
+ break;
+ case QWSCommand::Identify:
+ typeStr = "Identify";
+ break;
+ case QWSCommand::GrabKeyboard:
+ typeStr = "GrabKeyboard";
+ break;
+ case QWSCommand::RepaintRegion:
+ typeStr = "RepaintRegion";
+ break;
+ case QWSCommand::IMMouse:
+ typeStr = "IMMouse";
+ break;
+ case QWSCommand::IMUpdate:
+ typeStr = "IMUpdate";
+ break;
+ case QWSCommand::IMResponse:
+ typeStr = "IMResponse";
+ break;
+ case QWSCommand::Font:
+ typeStr = "Font";
+ break;
+ case QWSCommand::Unknown:
+ default:
+ typeStr = "Unknown";
+ break;
+ }
+ return typeStr;
+}
+
+
+/*********************************************************************
+ *
+ * Functions to read/write commands on/from a socket
+ *
+ *********************************************************************/
+
+#ifndef QT_NO_QWS_MULTIPROCESS
+void qws_write_command(QIODevice *socket, int type, char *simpleData, int simpleLen,
+ char *rawData, int rawLen)
+{
+#ifdef QWSCOMMAND_DEBUG
+ if (simpleLen) qDebug() << "WRITE simpleData " << QWSHexDump(simpleData, simpleLen);
+ if (rawLen > 0) qDebug() << "WRITE rawData " << QWSHexDump(rawData, rawLen);
+#endif
+
+#ifndef QT_NO_SXE
+ QTransportAuth *a = QTransportAuth::getInstance();
+ // ###### as soon as public API can be modified get rid of horrible casts
+ QIODevice *ad = a->passThroughByClient(reinterpret_cast<QWSClient*>(socket));
+ if (ad)
+ socket = ad;
+#endif
+
+ qws_write_uint(socket, type);
+
+ if (rawLen > MAX_COMMAND_SIZE) {
+ qWarning("qws_write_command: Message of size %d too big. "
+ "Truncated to %d", rawLen, MAX_COMMAND_SIZE);
+ rawLen = MAX_COMMAND_SIZE;
+ }
+
+ qws_write_uint(socket, rawLen == -1 ? 0 : rawLen);
+
+ if (simpleData && simpleLen)
+ socket->write(simpleData, simpleLen);
+
+ if (rawLen && rawData)
+ socket->write(rawData, rawLen);
+}
+
+/*
+ command format: [type][rawLen][simpleData][rawData]
+ type is already read when entering this function
+*/
+
+bool qws_read_command(QIODevice *socket, char *&simpleData, int &simpleLen,
+ char *&rawData, int &rawLen, int &bytesRead)
+{
+
+ // read rawLen
+ if (rawLen == -1) {
+ rawLen = qws_read_uint(socket);
+ if (rawLen == -1)
+ return false;
+ }
+
+ // read simpleData, assumes socket is capable of buffering all the data
+ if (simpleLen && !rawData) {
+ if (socket->bytesAvailable() < uint(simpleLen))
+ return false;
+ int tmp = socket->read(simpleData, simpleLen);
+ Q_ASSERT(tmp == simpleLen);
+ Q_UNUSED(tmp);
+ }
+
+ if (rawLen > MAX_COMMAND_SIZE) {
+ socket->close();
+ qWarning("qws_read_command: Won't read command of length %d, "
+ "connection closed.", rawLen);
+ return false;
+ }
+
+ // read rawData
+ if (rawLen && !rawData) {
+ rawData = new char[rawLen];
+ bytesRead = 0;
+ }
+ if (bytesRead < rawLen && socket->bytesAvailable())
+ bytesRead += socket->read(rawData + bytesRead, rawLen - bytesRead);
+
+ return (bytesRead == rawLen);
+}
+#endif
+
+/*********************************************************************
+ *
+ * QWSCommand base class - only use derived classes from that
+ *
+ *********************************************************************/
+QWSProtocolItem::~QWSProtocolItem() {
+ if (deleteRaw)
+ delete []rawDataPtr;
+}
+
+#ifndef QT_NO_QWS_MULTIPROCESS
+void QWSProtocolItem::write(QIODevice *s) {
+#ifdef QWSCOMMAND_DEBUG
+ if (!qwsServer)
+ qDebug() << "QWSProtocolItem::write sending type " << static_cast<QWSCommand::Type>(type);
+ else
+ qDebug() << "QWSProtocolItem::write sending event " << (type < N_EVENTS ? eventNames[type] : "unknown");
+#endif
+ qws_write_command(s, type, simpleDataPtr, simpleLen, rawDataPtr, rawLen);
+}
+
+bool QWSProtocolItem::read(QIODevice *s) {
+#ifdef QWSCOMMAND_DEBUG
+ QLatin1String reread( (rawLen == -1) ? "" : "REREAD");
+ if (qwsServer)
+ qDebug() << "QWSProtocolItem::read reading type " << static_cast<QWSCommand::Type>(type) << reread;
+ else
+ qDebug() << "QWSProtocolItem::read reading event " << (type < N_EVENTS ? eventNames[type] : "unknown") << reread;
+ //qDebug("QWSProtocolItem::read reading event %s", type < N_EVENTS ? eventNames[type] : "unknown");
+#endif
+ bool b = qws_read_command(s, simpleDataPtr, simpleLen, rawDataPtr, rawLen, bytesRead);
+ if (b) {
+ setData(rawDataPtr, rawLen, false);
+ deleteRaw = true;
+ }
+#ifdef QWSCOMMAND_DEBUG
+ else
+ {
+ qDebug() << "error in reading command " << static_cast<QWSCommand::Type>(type);
+ }
+#endif
+ return b;
+}
+#endif // QT_NO_QWS_MULTIPROCESS
+
+void QWSProtocolItem::copyFrom(const QWSProtocolItem *item) {
+ if (this == item)
+ return;
+ simpleLen = item->simpleLen;
+ memcpy(simpleDataPtr, item->simpleDataPtr, simpleLen);
+ setData(item->rawDataPtr, item->rawLen);
+}
+
+void QWSProtocolItem::setData(const char *data, int len, bool allocateMem) {
+ if (deleteRaw)
+ delete [] rawDataPtr;
+ if (!data || len <= 0) {
+ rawDataPtr = 0;
+ rawLen = 0;
+ return;
+ }
+ if (allocateMem) {
+ rawDataPtr = new char[len];
+ memcpy(rawDataPtr, data, len);
+ deleteRaw = true;
+ } else {
+ rawDataPtr = const_cast<char *>(data);
+ deleteRaw = false;
+ }
+ rawLen = len;
+}
+
+QWSCommand *QWSCommand::factory(int type)
+{
+ QWSCommand *command = 0;
+ switch (type) {
+ case QWSCommand::Create:
+ command = new QWSCreateCommand;
+ break;
+ case QWSCommand::Shutdown:
+ command = new QWSCommand(type, 0, 0);
+ break;
+ case QWSCommand::Region:
+ command = new QWSRegionCommand;
+ break;
+ case QWSCommand::RegionMove:
+ command = new QWSRegionMoveCommand;
+ break;
+ case QWSCommand::RegionDestroy:
+ command = new QWSRegionDestroyCommand;
+ break;
+ case QWSCommand::AddProperty:
+ command = new QWSAddPropertyCommand;
+ break;
+ case QWSCommand::SetProperty:
+ command = new QWSSetPropertyCommand;
+ break;
+ case QWSCommand::RemoveProperty:
+ command = new QWSRemovePropertyCommand;
+ break;
+ case QWSCommand::GetProperty:
+ command = new QWSGetPropertyCommand;
+ break;
+ case QWSCommand::SetSelectionOwner:
+ command = new QWSSetSelectionOwnerCommand;
+ break;
+ case QWSCommand::RequestFocus:
+ command = new QWSRequestFocusCommand;
+ break;
+ case QWSCommand::ChangeAltitude:
+ command = new QWSChangeAltitudeCommand;
+ break;
+ case QWSCommand::SetOpacity:
+ command = new QWSSetOpacityCommand;
+ break;
+ case QWSCommand::DefineCursor:
+ command = new QWSDefineCursorCommand;
+ break;
+ case QWSCommand::SelectCursor:
+ command = new QWSSelectCursorCommand;
+ break;
+ case QWSCommand::GrabMouse:
+ command = new QWSGrabMouseCommand;
+ break;
+ case QWSCommand::GrabKeyboard:
+ command = new QWSGrabKeyboardCommand;
+ break;
+#ifndef QT_NO_SOUND
+ case QWSCommand::PlaySound:
+ command = new QWSPlaySoundCommand;
+ break;
+#endif
+#ifndef QT_NO_COP
+ case QWSCommand::QCopRegisterChannel:
+ command = new QWSQCopRegisterChannelCommand;
+ break;
+ case QWSCommand::QCopSend:
+ command = new QWSQCopSendCommand;
+ break;
+#endif
+ case QWSCommand::RegionName:
+ command = new QWSRegionNameCommand;
+ break;
+ case QWSCommand::Identify:
+ command = new QWSIdentifyCommand;
+ break;
+ case QWSCommand::RepaintRegion:
+ command = new QWSRepaintRegionCommand;
+ break;
+#ifndef QT_NO_QWS_INPUTMETHODS
+ case QWSCommand::IMUpdate:
+ command = new QWSIMUpdateCommand;
+ break;
+
+ case QWSCommand::IMMouse:
+ command = new QWSIMMouseCommand;
+ break;
+
+ case QWSCommand::IMResponse:
+ command = new QWSIMResponseCommand;
+ break;
+#endif
+ case QWSCommand::PositionCursor:
+ command = new QWSPositionCursorCommand;
+ break;
+#ifndef QT_NO_QWSEMBEDWIDGET
+ case QWSCommand::Embed:
+ command = new QWSEmbedCommand;
+ break;
+#endif
+ case QWSCommand::Font:
+ command = new QWSFontCommand;
+ break;
+ case QWSCommand::ScreenTransform:
+ command = new QWSScreenTransformCommand;
+ break;
+ default:
+ qWarning("QWSCommand::factory : Type error - got %08x!", type);
+ }
+ return command;
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/embedded/qwscommand_qws_p.h b/src/gui/embedded/qwscommand_qws_p.h
new file mode 100644
index 0000000000..2155333281
--- /dev/null
+++ b/src/gui/embedded/qwscommand_qws_p.h
@@ -0,0 +1,853 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QWSCOMMAND_QWS_P_H
+#define QWSCOMMAND_QWS_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.
+//
+
+// When reading commands "off the wire" in the server, the rawLen is read
+// and then that many bytes are allocated. If the rawLen is corrupted (or
+// the protocol is being attacked) too many bytes can be allocated. Set
+// a hard limit here for security.
+#define MAX_COMMAND_SIZE (16 * 1024)
+
+#include <QtCore/qbytearray.h>
+#include <QtGui/qwsutils_qws.h>
+#include <QtGui/qfont.h>
+#include <QtCore/qdatastream.h>
+#include <QtCore/qvariant.h>
+#include <QtCore/qrect.h>
+#include <QtGui/qregion.h>
+#include <QtCore/qvector.h>
+#include <QtCore/qvarlengtharray.h>
+#include <QtGui/qwsevent_qws.h>
+#include "qwsprotocolitem_qws.h"
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+#define QTE_PIPE "QtEmbedded-%1"
+
+class QRect;
+
+/*********************************************************************
+ *
+ * Functions to read/write commands on/from a socket
+ *
+ *********************************************************************/
+#ifndef QT_NO_QWS_MULTIPROCESS
+void qws_write_command(QIODevice *socket, int type, char *simpleData, int simpleLen, char *rawData, int rawLen);
+bool qws_read_command(QIODevice *socket, char *&simpleData, int &simpleLen, char *&rawData, int &rawLen, int &bytesRead);
+#endif
+
+struct QWSCommand : QWSProtocolItem
+{
+ QWSCommand(int t, int len, char *ptr) : QWSProtocolItem(t,len,ptr) {}
+
+ enum Type {
+ Unknown = 0,
+ Create,
+ Shutdown,
+ Region,
+ RegionMove,
+ RegionDestroy,
+ SetProperty,
+ AddProperty,
+ RemoveProperty,
+ GetProperty,
+ SetSelectionOwner,
+ ConvertSelection,
+ RequestFocus,
+ ChangeAltitude,
+ SetOpacity,
+ DefineCursor,
+ SelectCursor,
+ PositionCursor,
+ GrabMouse,
+ PlaySound,
+ QCopRegisterChannel,
+ QCopSend,
+ RegionName,
+ Identify,
+ GrabKeyboard,
+ RepaintRegion,
+ IMMouse,
+ IMUpdate,
+ IMResponse,
+ Embed,
+ Font,
+ ScreenTransform
+ };
+ static QWSCommand *factory(int type);
+};
+
+const char *qws_getCommandTypeString( QWSCommand::Type tp );
+
+#ifndef QT_NO_DEBUG
+class QDebug;
+QDebug &operator<<(QDebug &dbg, QWSCommand::Type tp);
+#endif // QT_NO_DEBUG
+
+/*********************************************************************
+ *
+ * Commands
+ *
+ *********************************************************************/
+
+struct QWSIdentifyCommand : public QWSCommand
+{
+ QWSIdentifyCommand() :
+ QWSCommand(QWSCommand::Identify,
+ sizeof(simpleData), reinterpret_cast<char *>(&simpleData))
+ {
+ simpleData.idLen = 0;
+ simpleData.idLock = -1;
+ }
+
+ void setData(const char *d, int len, bool allocateMem) {
+ QWSCommand::setData(d, len, allocateMem);
+ if ( simpleData.idLen > MAX_COMMAND_SIZE )
+ {
+ qWarning( "Identify command - name length %d - too big!", simpleData.idLen );
+ simpleData.idLen = MAX_COMMAND_SIZE;
+ }
+ if ( simpleData.idLen * int(sizeof(QChar)) > len )
+ {
+ qWarning( "Identify command - name length %d - buffer size %d - buffer overrun!", simpleData.idLen, len );
+ }
+ else
+ {
+ id = QString(reinterpret_cast<const QChar*>(d), simpleData.idLen);
+ }
+ }
+
+ void setId(const QString& i, int lock)
+ {
+ id = i;
+ simpleData.idLen = id.length();
+ simpleData.idLock = lock;
+ setData(reinterpret_cast<const char*>(id.unicode()), simpleData.idLen*2, true);
+ }
+
+ struct SimpleData {
+ int idLen;
+ int idLock;
+ } simpleData;
+ QString id;
+};
+
+struct QWSCreateCommand : public QWSCommand
+{
+ QWSCreateCommand(int n = 1) :
+ QWSCommand(QWSCommand::Create, sizeof(count),
+ reinterpret_cast<char *>(&count)), count(n) {}
+ int count;
+};
+
+struct QWSRegionNameCommand : public QWSCommand
+{
+ QWSRegionNameCommand() :
+ QWSCommand(QWSCommand::RegionName,
+ sizeof(simpleData), reinterpret_cast<char *>(&simpleData)) {}
+
+ void setData(const char *d, int len, bool allocateMem) {
+ QWSCommand::setData(d, len, allocateMem);
+ if ( simpleData.nameLen > MAX_COMMAND_SIZE )
+ {
+ qWarning( "region name command - name length too big!" );
+ simpleData.nameLen = MAX_COMMAND_SIZE;
+ }
+ if ( simpleData.captionLen > MAX_COMMAND_SIZE )
+ {
+ qWarning( "region name command - caption length too big!" );
+ simpleData.captionLen = MAX_COMMAND_SIZE;
+ }
+ if ( simpleData.nameLen + simpleData.captionLen > len )
+ {
+ qWarning( "region name command - name length %d - caption length %d - buffer size %d - buffer overrun!",
+ simpleData.nameLen, simpleData.captionLen, len );
+
+ }
+ else
+ {
+ name = QString(reinterpret_cast<const QChar*>(d), simpleData.nameLen/2);
+ d += simpleData.nameLen;
+ caption = QString(reinterpret_cast<const QChar*>(d), simpleData.captionLen/2);
+ }
+ }
+
+ void setName(const QString& n, const QString &c)
+ {
+ name = n;
+ caption = c;
+ int l = simpleData.nameLen = name.length()*2;
+ l += simpleData.captionLen = caption.length()*2;
+ char *d = new char[l];
+ memcpy(d, name.unicode(), simpleData.nameLen);
+ memcpy(d+simpleData.nameLen, caption.unicode(), simpleData.captionLen);
+ setData(d, l, true);
+ delete[] d;
+ }
+
+ struct SimpleData {
+ int windowid;
+ int nameLen;
+ int captionLen;
+ } simpleData;
+ QString name;
+ QString caption;
+};
+
+struct QWSRegionCommand : public QWSCommand
+{
+ QWSRegionCommand() :
+ QWSCommand(QWSCommand::Region, sizeof(simpleData),
+ reinterpret_cast<char*>(&simpleData)) {}
+
+ void setData(const char *d, int len, bool allocateMem = true) {
+ QWSCommand::setData(d, len, allocateMem);
+
+ if( simpleData.nrectangles * int(sizeof(QRect)) + simpleData.surfacekeylength * int(sizeof(QChar)) + simpleData.surfacedatalength * int(sizeof(char)) > len )
+ {
+ qWarning( "region command - rectangle count %d - surface key length %d - region data size %d - buffer size %d - buffer overrun!",
+ simpleData.nrectangles, simpleData.surfacekeylength, simpleData.surfacedatalength, len );
+ }
+ else
+ {
+ char *ptr = rawDataPtr;
+
+ region.setRects(reinterpret_cast<QRect*>(ptr), simpleData.nrectangles);
+ ptr += simpleData.nrectangles * sizeof(QRect);
+
+ surfaceKey = QString(reinterpret_cast<QChar*>(ptr),
+ simpleData.surfacekeylength);
+ ptr += simpleData.surfacekeylength * sizeof(QChar);
+
+ surfaceData = QByteArray(ptr, simpleData.surfacedatalength);
+ }
+ }
+
+ void setData(int id, const QString &key, const QByteArray &data,
+ const QRegion &reg)
+ {
+ surfaceKey = key;
+ surfaceData = data;
+ region = reg;
+
+ const QVector<QRect> rects = reg.rects();
+
+ simpleData.windowid = id;
+ simpleData.surfacekeylength = key.size();
+ simpleData.surfacedatalength = data.size();
+ simpleData.nrectangles = rects.count();
+
+ QVarLengthArray<char, 256> buffer;
+ buffer.append(reinterpret_cast<const char*>(rects.constData()),
+ rects.count() * sizeof(QRect));
+ buffer.append(reinterpret_cast<const char*>(key.constData()),
+ key.size() * sizeof(QChar));
+ buffer.append(data, data.size());
+
+ QWSCommand::setData(buffer.constData(), buffer.size(), true);
+ }
+
+ /* XXX this will pad out in a compiler dependent way,
+ should move nrectangles to before windowtype, and
+ add reserved bytes.
+ Symptom will be valgrind reported uninitialized memory usage
+ */
+ struct SimpleData {
+ int windowid;
+ int surfacekeylength;
+ int surfacedatalength;
+ int nrectangles;
+ } simpleData;
+
+ QString surfaceKey;
+ QByteArray surfaceData;
+ QRegion region;
+};
+
+struct QWSSetOpacityCommand : public QWSCommand
+{
+ QWSSetOpacityCommand() :
+ QWSCommand(QWSCommand::SetOpacity, sizeof(simpleData),
+ reinterpret_cast<char*>(&simpleData)) {}
+
+ struct SimpleData {
+ int windowid;
+ uchar opacity;
+ } simpleData;
+};
+
+struct QWSRegionMoveCommand : public QWSCommand
+{
+ QWSRegionMoveCommand() :
+ QWSCommand(QWSCommand::RegionMove, sizeof(simpleData),
+ reinterpret_cast<char*>(&simpleData)) {}
+
+ struct SimpleData {
+ int windowid;
+ int dx;
+ int dy;
+ } simpleData;
+
+};
+
+struct QWSRegionDestroyCommand : public QWSCommand
+{
+ QWSRegionDestroyCommand() :
+ QWSCommand(QWSCommand::RegionDestroy, sizeof(simpleData),
+ reinterpret_cast<char*>(&simpleData)) {}
+
+ struct SimpleData {
+ int windowid;
+ } simpleData;
+
+};
+
+struct QWSRequestFocusCommand : public QWSCommand
+{
+ QWSRequestFocusCommand() :
+ QWSCommand(QWSCommand::RequestFocus, sizeof(simpleData), reinterpret_cast<char*>(&simpleData)) {}
+
+ struct SimpleData {
+ int windowid;
+ int flag;
+ } simpleData;
+};
+
+struct QWSChangeAltitudeCommand : public QWSCommand
+{
+ QWSChangeAltitudeCommand() :
+ QWSCommand(QWSCommand::ChangeAltitude, sizeof(simpleData), reinterpret_cast<char*>(&simpleData)) {}
+
+ enum Altitude {
+ Lower = -1,
+ Raise = 0,
+ StaysOnTop = 1
+ };
+
+ struct SimpleData {
+ int windowid;
+ Altitude altitude;
+ bool fixed;
+ } simpleData;
+
+};
+
+
+struct QWSAddPropertyCommand : public QWSCommand
+{
+ QWSAddPropertyCommand() :
+ QWSCommand(QWSCommand::AddProperty, sizeof(simpleData), reinterpret_cast<char*>(&simpleData)) {}
+
+ struct SimpleData {
+ int windowid, property;
+ } simpleData;
+
+};
+
+struct QWSSetPropertyCommand : public QWSCommand
+{
+ QWSSetPropertyCommand() :
+ QWSCommand(QWSCommand::SetProperty, sizeof(simpleData),
+ reinterpret_cast<char*>(&simpleData)) { data = 0; }
+
+ void setData(const char *d, int len, bool allocateMem = true) {
+ QWSCommand::setData(d, len, allocateMem);
+ data = rawDataPtr;
+ }
+
+ struct SimpleData {
+ int windowid, property, mode;
+ } simpleData;
+
+ char *data;
+};
+
+struct QWSRepaintRegionCommand : public QWSCommand
+{
+ QWSRepaintRegionCommand() :
+ QWSCommand(QWSCommand::RepaintRegion, sizeof(simpleData),
+ reinterpret_cast<char*>(&simpleData)) {}
+
+ void setData(const char *d, int len, bool allocateMem = true) {
+ QWSCommand::setData(d, len, allocateMem);
+
+ if( simpleData.nrectangles * int(sizeof(QRect)) > len )
+ {
+ qWarning( "repaint region command - region rectangle count %d - buffer size %d - buffer overrun",
+ simpleData.nrectangles, len );
+
+ simpleData.nrectangles = len / sizeof(QRect);
+ }
+ rectangles = reinterpret_cast<QRect *>(rawDataPtr);
+ }
+
+ struct SimpleData {
+ int windowid;
+ int windowFlags;
+ bool opaque;
+ int nrectangles;
+ } simpleData;
+
+ QRect * rectangles;
+
+};
+
+struct QWSRemovePropertyCommand : public QWSCommand
+{
+ QWSRemovePropertyCommand() :
+ QWSCommand(QWSCommand::RemoveProperty, sizeof(simpleData), reinterpret_cast<char*>(&simpleData)) {}
+
+ struct SimpleData {
+ int windowid, property;
+ } simpleData;
+
+};
+
+struct QWSGetPropertyCommand : public QWSCommand
+{
+ QWSGetPropertyCommand() :
+ QWSCommand(QWSCommand::GetProperty, sizeof(simpleData), reinterpret_cast<char*>(&simpleData)) {}
+
+ struct SimpleData {
+ int windowid, property;
+ } simpleData;
+
+};
+
+struct QWSSetSelectionOwnerCommand : public QWSCommand
+{
+ QWSSetSelectionOwnerCommand() :
+ QWSCommand(QWSCommand::SetSelectionOwner,
+ sizeof(simpleData), reinterpret_cast<char*>(&simpleData)) {}
+
+ struct SimpleData {
+ int windowid;
+ int hour, minute, sec, ms; // time
+ } simpleData;
+
+};
+
+struct QWSConvertSelectionCommand : public QWSCommand
+{
+ QWSConvertSelectionCommand() :
+ QWSCommand(QWSCommand::ConvertSelection,
+ sizeof(simpleData), reinterpret_cast<char*>(&simpleData)) {}
+
+ struct SimpleData {
+ int requestor; // requestor window of the selection
+ int selection; // property on requestor into which the selection should be stored
+ int mimeTypes; // property ion requestor in which the mimetypes, in which the selection may be, are stored
+ } simpleData;
+
+};
+
+struct QWSDefineCursorCommand : public QWSCommand
+{
+ QWSDefineCursorCommand() :
+ QWSCommand(QWSCommand::DefineCursor,
+ sizeof(simpleData), reinterpret_cast<char *>(&simpleData)) {}
+
+ void setData(const char *d, int len, bool allocateMem = true) {
+ QWSCommand::setData(d, len, allocateMem);
+ data = reinterpret_cast<unsigned char *>(rawDataPtr);
+ if (simpleData.height * ((simpleData.width+7) / 8) > len) {
+ qWarning("define cursor command - width %d height %d- buffer size %d - buffer overrun",
+ simpleData.width, simpleData.height, len );
+ simpleData.width = simpleData.height = 0;
+ }
+ }
+
+ struct SimpleData {
+ int width;
+ int height;
+ int hotX;
+ int hotY;
+ int id;
+ } simpleData;
+
+ unsigned char *data;
+};
+
+struct QWSSelectCursorCommand : public QWSCommand
+{
+ QWSSelectCursorCommand() :
+ QWSCommand(QWSCommand::SelectCursor,
+ sizeof(simpleData), reinterpret_cast<char *>(&simpleData)) {}
+
+ struct SimpleData {
+ int windowid;
+ int id;
+ } simpleData;
+};
+
+struct QWSPositionCursorCommand : public QWSCommand
+{
+ QWSPositionCursorCommand() :
+ QWSCommand(QWSCommand::PositionCursor,
+ sizeof(simpleData), reinterpret_cast<char *>(&simpleData)) {}
+
+ struct SimpleData {
+ int newX;
+ int newY;
+ } simpleData;
+};
+
+struct QWSGrabMouseCommand : public QWSCommand
+{
+ QWSGrabMouseCommand() :
+ QWSCommand(QWSCommand::GrabMouse,
+ sizeof(simpleData), reinterpret_cast<char *>(&simpleData)) {}
+
+ struct SimpleData {
+ int windowid;
+ bool grab; // grab or ungrab?
+ } simpleData;
+};
+
+struct QWSGrabKeyboardCommand : public QWSCommand
+{
+ QWSGrabKeyboardCommand() :
+ QWSCommand(QWSCommand::GrabKeyboard,
+ sizeof(simpleData), reinterpret_cast<char *>(&simpleData)) {}
+
+ struct SimpleData {
+ int windowid;
+ bool grab; // grab or ungrab?
+ } simpleData;
+};
+
+#ifndef QT_NO_SOUND
+struct QWSPlaySoundCommand : public QWSCommand
+{
+ QWSPlaySoundCommand() :
+ QWSCommand(QWSCommand::PlaySound,
+ sizeof(simpleData), reinterpret_cast<char *>(&simpleData)) {}
+
+ void setData(const char *d, int len, bool allocateMem) {
+ QWSCommand::setData(d, len, allocateMem);
+ filename = QString(reinterpret_cast<QChar*>(rawDataPtr),len/2);
+ }
+ void setFileName(const QString& n)
+ {
+ setData(reinterpret_cast<const char*>(n.unicode()), n.length()*2, true);
+ }
+
+ struct SimpleData {
+ int windowid;
+ } simpleData;
+ QString filename;
+};
+#endif
+
+
+#ifndef QT_NO_COP
+struct QWSQCopRegisterChannelCommand : public QWSCommand
+{
+ QWSQCopRegisterChannelCommand() :
+ QWSCommand(QWSCommand::QCopRegisterChannel,
+ sizeof(simpleData), reinterpret_cast<char *>(&simpleData)) {}
+
+ void setData(const char *d, int len, bool allocateMem) {
+ QWSCommand::setData(d, len, allocateMem);
+ if ( simpleData.chLen > MAX_COMMAND_SIZE )
+ {
+ qWarning( "Command channel name too large!" );
+ simpleData.chLen = MAX_COMMAND_SIZE;
+ }
+ if( simpleData.chLen * int(sizeof(QChar)) > len )
+ {
+ qWarning( "register qcop channel command - channel name length %d - buffer size %d - buffer overrun!", simpleData.chLen, len );
+ }
+ else
+ {
+ channel = QString(reinterpret_cast<const QChar*>(d), simpleData.chLen);
+ }
+ }
+
+ void setChannel(const QString& n)
+ {
+ channel = n;
+ simpleData.chLen = channel.length();
+ setData(reinterpret_cast<const char*>(channel.unicode()), simpleData.chLen*2, true);
+ }
+
+ struct SimpleData {
+ int chLen;
+ } simpleData;
+ QString channel;
+};
+
+struct QWSQCopSendCommand : public QWSCommand
+{
+ QWSQCopSendCommand() :
+ QWSCommand(QWSCommand::QCopSend,
+ sizeof(simpleData), reinterpret_cast<char *>(&simpleData)) {}
+
+ void setData(const char *d, int len, bool allocateMem) {
+ QWSCommand::setData(d, len, allocateMem);
+
+ if( simpleData.clen * int(sizeof(QChar)) + simpleData.mlen * int(sizeof(QChar)) + simpleData.dlen * int(sizeof(char)) > len )
+ {
+ qWarning( "qcop send command - channel name length %d - message name length %d - data size %d - buffer size %d - buffer overrun!",
+ simpleData.clen, simpleData.mlen, simpleData.dlen, len );
+ }
+ else
+ {
+ const QChar *cd = reinterpret_cast<const QChar*>(d);
+ channel = QString(cd,simpleData.clen); cd += simpleData.clen;
+ message = QString(cd,simpleData.mlen);
+ d += simpleData.clen*sizeof(QChar) + simpleData.mlen*sizeof(QChar);
+ data = QByteArray(d, simpleData.dlen);
+ }
+ }
+
+ void setMessage(const QString &c, const QString &m,
+ const QByteArray &data)
+ {
+ this->channel = c;
+ this->message = m;
+ this->data = data;
+ simpleData.clen = c.length();
+ simpleData.mlen = m.length();
+ simpleData.dlen = data.size();
+ int l = simpleData.clen*sizeof(QChar);
+ l += simpleData.mlen*sizeof(QChar);
+ l += simpleData.dlen;
+ char *tmp = new char[l];
+ char *d = tmp;
+ memcpy(d, c.unicode(), simpleData.clen*sizeof(QChar));
+ d += simpleData.clen*sizeof(QChar);
+ memcpy(d, m.unicode(), simpleData.mlen*sizeof(QChar));
+ d += simpleData.mlen*sizeof(QChar);
+ memcpy(d, data.data(), simpleData.dlen);
+ QWSCommand::setData(tmp, l, false);
+ deleteRaw = true;
+ }
+
+ struct SimpleData {
+ int clen;
+ int mlen;
+ int dlen;
+ } simpleData;
+ QString channel;
+ QString message;
+ QByteArray data;
+};
+
+#endif
+
+
+#ifndef QT_NO_QWS_INPUTMETHODS
+
+struct QWSIMMouseCommand : public QWSCommand
+{
+ QWSIMMouseCommand() :
+ QWSCommand(QWSCommand::IMMouse,
+ sizeof(simpleData), reinterpret_cast<char *>(&simpleData)) {}
+
+ struct SimpleData {
+ int windowid;
+ int state;
+ int index;
+ } simpleData;
+};
+
+
+struct QWSIMResponseCommand : public QWSCommand
+{
+ QWSIMResponseCommand() :
+ QWSCommand(QWSCommand::IMResponse,
+ sizeof(simpleData), reinterpret_cast<char *>(&simpleData)) {}
+
+ void setData(const char *d, int len, bool allocateMem) {
+ QWSCommand::setData(d, len, allocateMem);
+
+ QByteArray tmp = QByteArray::fromRawData(d, len);
+ QDataStream s(tmp);
+ s >> result;
+ }
+
+ void setResult(const QVariant & v)
+ {
+ QByteArray tmp;
+ QDataStream s(&tmp, QIODevice::WriteOnly);
+ s << v;
+ setData(tmp.data(), tmp.size(), true);
+ }
+
+ struct SimpleData {
+ int windowid;
+ int property;
+ } simpleData;
+
+ QVariant result;
+};
+
+struct QWSIMUpdateCommand: public QWSCommand
+{
+ QWSIMUpdateCommand() :
+ QWSCommand(QWSCommand::IMUpdate,
+ sizeof(simpleData), reinterpret_cast<char *>(&simpleData)) {}
+
+ struct SimpleData {
+ int windowid;
+ int type;
+ int widgetid;
+ } simpleData;
+};
+
+#endif
+
+#ifndef QT_NO_QWSEMBEDWIDGET
+struct QWSEmbedCommand : public QWSCommand
+{
+ QWSEmbedCommand() : QWSCommand(QWSCommand::Embed,
+ sizeof(simpleData),
+ reinterpret_cast<char*>(&simpleData))
+ {}
+
+ void setData(const char *d, int len, bool allocateMem = true)
+ {
+ QWSCommand::setData(d, len, allocateMem);
+
+ if( simpleData.rects * int(sizeof(QRect)) > len )
+ {
+ qWarning( "embed command - region rectangle count %d - buffer size %d - buffer overrun!",
+ simpleData.rects, len );
+ }
+ else
+ {
+ region.setRects(reinterpret_cast<QRect*>(rawDataPtr),
+ simpleData.rects);
+ }
+ }
+
+ void setData(WId embedder, WId embedded, QWSEmbedEvent::Type type,
+ const QRegion reg = QRegion())
+ {
+ simpleData.embedder = embedder;
+ simpleData.embedded = embedded;
+ simpleData.type = type;
+
+ region = reg;
+ const QVector<QRect> rects = reg.rects();
+ simpleData.rects = rects.count();
+
+ QWSCommand::setData(reinterpret_cast<const char*>(rects.constData()),
+ rects.count() * sizeof(QRect));
+ }
+
+ struct {
+ WId embedder;
+ WId embedded;
+ QWSEmbedEvent::Type type;
+ int rects;
+ } simpleData;
+
+ QRegion region;
+};
+#endif // QT_NO_QWSEMBEDWIDGET
+
+struct QWSFontCommand : public QWSCommand
+{
+ enum CommandType {
+ StartedUsingFont,
+ StoppedUsingFont
+ };
+
+ QWSFontCommand() :
+ QWSCommand(QWSCommand::Font,
+ sizeof(simpleData), reinterpret_cast<char *>(&simpleData)) {}
+
+ void setData(const char *d, int len, bool allocateMem) {
+ QWSCommand::setData(d, len, allocateMem);
+
+ fontName = QByteArray(d, len);
+ }
+
+ void setFontName(const QByteArray &name)
+ {
+ setData(name.constData(), name.size(), true);
+ }
+
+ struct SimpleData {
+ int type;
+ } simpleData;
+
+ QByteArray fontName;
+};
+
+struct QWSScreenTransformCommand : public QWSCommand
+{
+ QWSScreenTransformCommand() :
+ QWSCommand(QWSCommand::ScreenTransform,
+ sizeof(simpleData), reinterpret_cast<char *>(&simpleData)) {}
+
+ void setTransformation(int screen, int transformation)
+ {
+ simpleData.screen = screen;
+ simpleData.transformation = transformation;
+ }
+
+ struct SimpleData {
+ int screen;
+ int transformation;
+ } simpleData;
+};
+
+QT_END_NAMESPACE
+
+#endif // QWSCOMMAND_QWS_P_H
diff --git a/src/gui/embedded/qwscursor_qws.cpp b/src/gui/embedded/qwscursor_qws.cpp
new file mode 100644
index 0000000000..3a5bd2cb66
--- /dev/null
+++ b/src/gui/embedded/qwscursor_qws.cpp
@@ -0,0 +1,654 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qcursor.h"
+#include "qbitmap.h"
+#include "qscreen_qws.h"
+#include "qapplication.h"
+#include "qwindowsystem_qws.h"
+#include "qwindowsystem_p.h"
+#include "qwscursor_qws.h"
+
+QT_BEGIN_NAMESPACE
+
+#ifndef QT_NO_QWS_CURSOR
+static QWSCursor *systemCursorTable[Qt::LastCursor+1];
+static bool systemCursorTableInit = false;
+
+// 16 x 16
+static const uchar cur_arrow_bits[] = {
+ 0x07, 0x00, 0x39, 0x00, 0xc1, 0x01, 0x02, 0x0e, 0x02, 0x10, 0x02, 0x08,
+ 0x04, 0x04, 0x04, 0x02, 0x04, 0x04, 0x88, 0x08, 0x48, 0x11, 0x28, 0x22,
+ 0x10, 0x44, 0x00, 0x28, 0x00, 0x10, 0x00, 0x00 };
+static const uchar mcur_arrow_bits[] = {
+ 0x07, 0x00, 0x3f, 0x00, 0xff, 0x01, 0xfe, 0x0f, 0xfe, 0x1f, 0xfe, 0x0f,
+ 0xfc, 0x07, 0xfc, 0x03, 0xfc, 0x07, 0xf8, 0x0f, 0x78, 0x1f, 0x38, 0x3e,
+ 0x10, 0x7c, 0x00, 0x38, 0x00, 0x10, 0x00, 0x00 };
+
+static const unsigned char cur_up_arrow_bits[] = {
+ 0x80, 0x00, 0x40, 0x01, 0x40, 0x01, 0x20, 0x02, 0x20, 0x02, 0x10, 0x04,
+ 0x10, 0x04, 0x08, 0x08, 0x78, 0x0f, 0x40, 0x01, 0x40, 0x01, 0x40, 0x01,
+ 0x40, 0x01, 0x40, 0x01, 0x40, 0x01, 0xc0, 0x01};
+static const unsigned char mcur_up_arrow_bits[] = {
+ 0x80, 0x00, 0xc0, 0x01, 0xc0, 0x01, 0xe0, 0x03, 0xe0, 0x03, 0xf0, 0x07,
+ 0xf0, 0x07, 0xf8, 0x0f, 0xf8, 0x0f, 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x01,
+ 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x01};
+
+static const unsigned char cur_cross_bits[] = {
+ 0xc0, 0x01, 0x40, 0x01, 0x40, 0x01, 0x40, 0x01, 0x40, 0x01, 0x40, 0x01,
+ 0x7f, 0x7f, 0x01, 0x40, 0x7f, 0x7f, 0x40, 0x01, 0x40, 0x01, 0x40, 0x01,
+ 0x40, 0x01, 0x40, 0x01, 0xc0, 0x01, 0x00, 0x00};
+static const unsigned char mcur_cross_bits[] = {
+ 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x01,
+ 0xff, 0x7f, 0xff, 0x7f, 0xff, 0x7f, 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x01,
+ 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x01, 0x00, 0x00};
+
+static const uchar cur_ibeam_bits[] = {
+ 0x00, 0x00, 0xe0, 0x03, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00,
+ 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00,
+ 0x80, 0x00, 0xe0, 0x03, 0x00, 0x00, 0x00, 0x00 };
+static const uchar mcur_ibeam_bits[] = {
+ 0xf0, 0x07, 0xf0, 0x07, 0xf0, 0x07, 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x01,
+ 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x01,
+ 0xf0, 0x07, 0xf0, 0x07, 0xf0, 0x07, 0x00, 0x00 };
+
+static const uchar cur_ver_bits[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x80, 0x01, 0xc0, 0x03, 0xe0, 0x07, 0xf0, 0x0f,
+ 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0xf0, 0x0f,
+ 0xe0, 0x07, 0xc0, 0x03, 0x80, 0x01, 0x00, 0x00 };
+static const uchar mcur_ver_bits[] = {
+ 0x00, 0x00, 0x80, 0x03, 0xc0, 0x07, 0xe0, 0x0f, 0xf0, 0x1f, 0xf8, 0x3f,
+ 0xfc, 0x7f, 0xc0, 0x07, 0xc0, 0x07, 0xc0, 0x07, 0xfc, 0x7f, 0xf8, 0x3f,
+ 0xf0, 0x1f, 0xe0, 0x0f, 0xc0, 0x07, 0x80, 0x03 };
+
+static const uchar cur_hor_bits[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x08, 0x30, 0x18,
+ 0x38, 0x38, 0xfc, 0x7f, 0xfc, 0x7f, 0x38, 0x38, 0x30, 0x18, 0x20, 0x08,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+static const uchar mcur_hor_bits[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x40, 0x04, 0x60, 0x0c, 0x70, 0x1c, 0x78, 0x3c,
+ 0xfc, 0x7f, 0xfe, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xfc, 0x7f, 0x78, 0x3c,
+ 0x70, 0x1c, 0x60, 0x0c, 0x40, 0x04, 0x00, 0x00 };
+static const uchar cur_bdiag_bits[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x3e, 0x00, 0x3c, 0x00, 0x3e,
+ 0x00, 0x37, 0x88, 0x23, 0xd8, 0x01, 0xf8, 0x00, 0x78, 0x00, 0xf8, 0x00,
+ 0xf8, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+static const uchar mcur_bdiag_bits[] = {
+ 0x00, 0x00, 0xc0, 0x7f, 0x80, 0x7f, 0x00, 0x7f, 0x00, 0x7e, 0x04, 0x7f,
+ 0x8c, 0x7f, 0xdc, 0x77, 0xfc, 0x63, 0xfc, 0x41, 0xfc, 0x00, 0xfc, 0x01,
+ 0xfc, 0x03, 0xfc, 0x07, 0x00, 0x00, 0x00, 0x00 };
+static const uchar cur_fdiag_bits[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x01, 0xf8, 0x00, 0x78, 0x00,
+ 0xf8, 0x00, 0xd8, 0x01, 0x88, 0x23, 0x00, 0x37, 0x00, 0x3e, 0x00, 0x3c,
+ 0x00, 0x3e, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x00 };
+static const uchar mcur_fdiag_bits[] = {
+ 0x00, 0x00, 0x00, 0x00, 0xfc, 0x07, 0xfc, 0x03, 0xfc, 0x01, 0xfc, 0x00,
+ 0xfc, 0x41, 0xfc, 0x63, 0xdc, 0x77, 0x8c, 0x7f, 0x04, 0x7f, 0x00, 0x7e,
+ 0x00, 0x7f, 0x80, 0x7f, 0xc0, 0x7f, 0x00, 0x00 };
+static const uchar cur_blank_bits[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+
+// 20 x 20
+static const uchar forbidden_bits[] = {
+ 0x00,0x00,0x00,0x80,0x1f,0x00,0xe0,0x7f,0x00,0xf0,0xf0,0x00,0x38,0xc0,0x01,
+ 0x7c,0x80,0x03,0xec,0x00,0x03,0xce,0x01,0x07,0x86,0x03,0x06,0x06,0x07,0x06,
+ 0x06,0x0e,0x06,0x06,0x1c,0x06,0x0e,0x38,0x07,0x0c,0x70,0x03,0x1c,0xe0,0x03,
+ 0x38,0xc0,0x01,0xf0,0xe0,0x00,0xe0,0x7f,0x00,0x80,0x1f,0x00,0x00,0x00,0x00 };
+
+static const uchar forbiddenm_bits[] = {
+ 0x80,0x1f,0x00,0xe0,0x7f,0x00,0xf0,0xff,0x00,0xf8,0xff,0x01,0xfc,0xf0,0x03,
+ 0xfe,0xc0,0x07,0xfe,0x81,0x07,0xff,0x83,0x0f,0xcf,0x07,0x0f,0x8f,0x0f,0x0f,
+ 0x0f,0x1f,0x0f,0x0f,0x3e,0x0f,0x1f,0xfc,0x0f,0x1e,0xf8,0x07,0x3e,0xf0,0x07,
+ 0xfc,0xe0,0x03,0xf8,0xff,0x01,0xf0,0xff,0x00,0xe0,0x7f,0x00,0x80,0x1f,0x00};
+
+// 32 x 32
+static const uchar wait_data_bits[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x7f, 0x00,
+ 0x00, 0x04, 0x40, 0x00, 0x00, 0xfc, 0x7f, 0x00, 0x00, 0x08, 0x20, 0x00,
+ 0x00, 0x08, 0x20, 0x00, 0x00, 0x08, 0x20, 0x00, 0x00, 0x08, 0x20, 0x00,
+ 0x00, 0x50, 0x15, 0x00, 0x00, 0xa0, 0x0a, 0x00, 0x00, 0x40, 0x05, 0x00,
+ 0x00, 0x80, 0x02, 0x00, 0x00, 0x40, 0x04, 0x00, 0x00, 0x20, 0x08, 0x00,
+ 0x00, 0x10, 0x10, 0x00, 0x00, 0x08, 0x21, 0x00, 0x00, 0x88, 0x22, 0x00,
+ 0x00, 0x48, 0x25, 0x00, 0x00, 0xa8, 0x2a, 0x00, 0x00, 0xfc, 0x7f, 0x00,
+ 0x00, 0x04, 0x40, 0x00, 0x00, 0xfc, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+static const uchar wait_mask_bits[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x7f, 0x00,
+ 0x00, 0xfc, 0x7f, 0x00, 0x00, 0xfc, 0x7f, 0x00, 0x00, 0xf8, 0x3f, 0x00,
+ 0x00, 0xf8, 0x3f, 0x00, 0x00, 0xf8, 0x3f, 0x00, 0x00, 0xf8, 0x3f, 0x00,
+ 0x00, 0xf0, 0x1f, 0x00, 0x00, 0xe0, 0x0f, 0x00, 0x00, 0xc0, 0x07, 0x00,
+ 0x00, 0x80, 0x03, 0x00, 0x00, 0xc0, 0x07, 0x00, 0x00, 0xe0, 0x0f, 0x00,
+ 0x00, 0xf0, 0x1f, 0x00, 0x00, 0xf8, 0x3f, 0x00, 0x00, 0xf8, 0x3f, 0x00,
+ 0x00, 0xf8, 0x3f, 0x00, 0x00, 0xf8, 0x3f, 0x00, 0x00, 0xfc, 0x7f, 0x00,
+ 0x00, 0xfc, 0x7f, 0x00, 0x00, 0xfc, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+
+static const uchar hsplit_bits[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x02, 0x00, 0x00, 0x40, 0x02, 0x00,
+ 0x00, 0x40, 0x02, 0x00, 0x00, 0x40, 0x02, 0x00, 0x00, 0x40, 0x02, 0x00,
+ 0x00, 0x41, 0x82, 0x00, 0x80, 0x41, 0x82, 0x01, 0xc0, 0x7f, 0xfe, 0x03,
+ 0x80, 0x41, 0x82, 0x01, 0x00, 0x41, 0x82, 0x00, 0x00, 0x40, 0x02, 0x00,
+ 0x00, 0x40, 0x02, 0x00, 0x00, 0x40, 0x02, 0x00, 0x00, 0x40, 0x02, 0x00,
+ 0x00, 0x40, 0x02, 0x00, 0x00, 0x40, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+static const uchar hsplitm_bits[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0xe0, 0x07, 0x00, 0x00, 0xe0, 0x07, 0x00, 0x00, 0xe0, 0x07, 0x00,
+ 0x00, 0xe0, 0x07, 0x00, 0x00, 0xe2, 0x47, 0x00, 0x00, 0xe3, 0xc7, 0x00,
+ 0x80, 0xe3, 0xc7, 0x01, 0xc0, 0xff, 0xff, 0x03, 0xe0, 0xff, 0xff, 0x07,
+ 0xc0, 0xff, 0xff, 0x03, 0x80, 0xe3, 0xc7, 0x01, 0x00, 0xe3, 0xc7, 0x00,
+ 0x00, 0xe2, 0x47, 0x00, 0x00, 0xe0, 0x07, 0x00, 0x00, 0xe0, 0x07, 0x00,
+ 0x00, 0xe0, 0x07, 0x00, 0x00, 0xe0, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+static const uchar vsplit_bits[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x80, 0x00, 0x00, 0x00, 0xc0, 0x01, 0x00, 0x00, 0xe0, 0x03, 0x00,
+ 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00,
+ 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0xff, 0x7f, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x7f, 0x00,
+ 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00,
+ 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0xe0, 0x03, 0x00,
+ 0x00, 0xc0, 0x01, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+static const uchar vsplitm_bits[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00,
+ 0x00, 0xc0, 0x01, 0x00, 0x00, 0xe0, 0x03, 0x00, 0x00, 0xf0, 0x07, 0x00,
+ 0x00, 0xf8, 0x0f, 0x00, 0x00, 0xc0, 0x01, 0x00, 0x00, 0xc0, 0x01, 0x00,
+ 0x00, 0xc0, 0x01, 0x00, 0x80, 0xff, 0xff, 0x00, 0x80, 0xff, 0xff, 0x00,
+ 0x80, 0xff, 0xff, 0x00, 0x80, 0xff, 0xff, 0x00, 0x80, 0xff, 0xff, 0x00,
+ 0x80, 0xff, 0xff, 0x00, 0x00, 0xc0, 0x01, 0x00, 0x00, 0xc0, 0x01, 0x00,
+ 0x00, 0xc0, 0x01, 0x00, 0x00, 0xf8, 0x0f, 0x00, 0x00, 0xf0, 0x07, 0x00,
+ 0x00, 0xe0, 0x03, 0x00, 0x00, 0xc0, 0x01, 0x00, 0x00, 0x80, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+static const uchar phand_bits[] = {
+ 0x00, 0x00, 0x00, 0x00, 0xfe, 0x01, 0x00, 0x00, 0x01, 0x02, 0x00, 0x00,
+ 0x7e, 0x04, 0x00, 0x00, 0x08, 0x08, 0x00, 0x00, 0x70, 0x08, 0x00, 0x00,
+ 0x08, 0x08, 0x00, 0x00, 0x70, 0x14, 0x00, 0x00, 0x08, 0x22, 0x00, 0x00,
+ 0x30, 0x41, 0x00, 0x00, 0xc0, 0x20, 0x00, 0x00, 0x40, 0x12, 0x00, 0x00,
+ 0x80, 0x08, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+static const uchar phandm_bits[] = {
+ 0xfe, 0x01, 0x00, 0x00, 0xff, 0x03, 0x00, 0x00, 0xff, 0x07, 0x00, 0x00,
+ 0xff, 0x0f, 0x00, 0x00, 0xfe, 0x1f, 0x00, 0x00, 0xf8, 0x1f, 0x00, 0x00,
+ 0xfc, 0x1f, 0x00, 0x00, 0xf8, 0x3f, 0x00, 0x00, 0xfc, 0x7f, 0x00, 0x00,
+ 0xf8, 0xff, 0x00, 0x00, 0xf0, 0x7f, 0x00, 0x00, 0xe0, 0x3f, 0x00, 0x00,
+ 0xc0, 0x1f, 0x00, 0x00, 0x80, 0x0f, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00,
+ 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+
+static const uchar size_all_data_bits[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x80, 0x00, 0x00, 0x00, 0xc0, 0x01, 0x00, 0x00, 0xe0, 0x03, 0x00,
+ 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00,
+ 0x00, 0x80, 0x00, 0x00, 0x00, 0x81, 0x40, 0x00, 0x80, 0x81, 0xc0, 0x00,
+ 0xc0, 0xff, 0xff, 0x01, 0x80, 0x81, 0xc0, 0x00, 0x00, 0x81, 0x40, 0x00,
+ 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00,
+ 0x00, 0x80, 0x00, 0x00, 0x00, 0xe0, 0x03, 0x00, 0x00, 0xc0, 0x01, 0x00,
+ 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+static const uchar size_all_mask_bits[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00,
+ 0x00, 0xc0, 0x01, 0x00, 0x00, 0xe0, 0x03, 0x00, 0x00, 0xf0, 0x07, 0x00,
+ 0x00, 0xf8, 0x0f, 0x00, 0x00, 0xc0, 0x01, 0x00, 0x00, 0xc2, 0x21, 0x00,
+ 0x00, 0xc3, 0x61, 0x00, 0x80, 0xc3, 0xe1, 0x00, 0xc0, 0xff, 0xff, 0x01,
+ 0xe0, 0xff, 0xff, 0x03, 0xc0, 0xff, 0xff, 0x01, 0x80, 0xc3, 0xe1, 0x00,
+ 0x00, 0xc3, 0x61, 0x00, 0x00, 0xc2, 0x21, 0x00, 0x00, 0xc0, 0x01, 0x00,
+ 0x00, 0xf8, 0x0f, 0x00, 0x00, 0xf0, 0x07, 0x00, 0x00, 0xe0, 0x03, 0x00,
+ 0x00, 0xc0, 0x01, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+
+static const uchar whatsthis_bits[] = {
+ 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x05, 0xf0, 0x07, 0x00,
+ 0x09, 0x18, 0x0e, 0x00, 0x11, 0x1c, 0x0e, 0x00, 0x21, 0x1c, 0x0e, 0x00,
+ 0x41, 0x1c, 0x0e, 0x00, 0x81, 0x1c, 0x0e, 0x00, 0x01, 0x01, 0x07, 0x00,
+ 0x01, 0x82, 0x03, 0x00, 0xc1, 0xc7, 0x01, 0x00, 0x49, 0xc0, 0x01, 0x00,
+ 0x95, 0xc0, 0x01, 0x00, 0x93, 0xc0, 0x01, 0x00, 0x21, 0x01, 0x00, 0x00,
+ 0x20, 0xc1, 0x01, 0x00, 0x40, 0xc2, 0x01, 0x00, 0x40, 0x02, 0x00, 0x00,
+ 0x80, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, };
+static const uchar whatsthism_bits[] = {
+ 0x01, 0x00, 0x00, 0x00, 0x03, 0xf0, 0x07, 0x00, 0x07, 0xf8, 0x0f, 0x00,
+ 0x0f, 0xfc, 0x1f, 0x00, 0x1f, 0x3e, 0x1f, 0x00, 0x3f, 0x3e, 0x1f, 0x00,
+ 0x7f, 0x3e, 0x1f, 0x00, 0xff, 0x3e, 0x1f, 0x00, 0xff, 0x9d, 0x0f, 0x00,
+ 0xff, 0xc3, 0x07, 0x00, 0xff, 0xe7, 0x03, 0x00, 0x7f, 0xe0, 0x03, 0x00,
+ 0xf7, 0xe0, 0x03, 0x00, 0xf3, 0xe0, 0x03, 0x00, 0xe1, 0xe1, 0x03, 0x00,
+ 0xe0, 0xe1, 0x03, 0x00, 0xc0, 0xe3, 0x03, 0x00, 0xc0, 0xe3, 0x03, 0x00,
+ 0x80, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, };
+
+static const uchar busy_bits[] = {
+ 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00,
+ 0x09, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00,
+ 0x41, 0xe0, 0xff, 0x00, 0x81, 0x20, 0x80, 0x00, 0x01, 0xe1, 0xff, 0x00,
+ 0x01, 0x42, 0x40, 0x00, 0xc1, 0x47, 0x40, 0x00, 0x49, 0x40, 0x55, 0x00,
+ 0x95, 0x80, 0x2a, 0x00, 0x93, 0x00, 0x15, 0x00, 0x21, 0x01, 0x0a, 0x00,
+ 0x20, 0x01, 0x11, 0x00, 0x40, 0x82, 0x20, 0x00, 0x40, 0x42, 0x44, 0x00,
+ 0x80, 0x41, 0x4a, 0x00, 0x00, 0x40, 0x55, 0x00, 0x00, 0xe0, 0xff, 0x00,
+ 0x00, 0x20, 0x80, 0x00, 0x00, 0xe0, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+static const uchar busym_bits[] = {
+ 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00,
+ 0x0f, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00,
+ 0x7f, 0xe0, 0xff, 0x00, 0xff, 0xe0, 0xff, 0x00, 0xff, 0xe1, 0xff, 0x00,
+ 0xff, 0xc3, 0x7f, 0x00, 0xff, 0xc7, 0x7f, 0x00, 0x7f, 0xc0, 0x7f, 0x00,
+ 0xf7, 0x80, 0x3f, 0x00, 0xf3, 0x00, 0x1f, 0x00, 0xe1, 0x01, 0x0e, 0x00,
+ 0xe0, 0x01, 0x1f, 0x00, 0xc0, 0x83, 0x3f, 0x00, 0xc0, 0xc3, 0x7f, 0x00,
+ 0x80, 0xc1, 0x7f, 0x00, 0x00, 0xc0, 0x7f, 0x00, 0x00, 0xe0, 0xff, 0x00,
+ 0x00, 0xe0, 0xff, 0x00, 0x00, 0xe0, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+
+// 16 x 16
+static const uchar openhand_bits[] = {
+ 0x80,0x01,0x58,0x0e,0x64,0x12,0x64,0x52,0x48,0xb2,0x48,0x92,
+ 0x16,0x90,0x19,0x80,0x11,0x40,0x02,0x40,0x04,0x40,0x04,0x20,
+ 0x08,0x20,0x10,0x10,0x20,0x10,0x00,0x00};
+static const uchar openhandm_bits[] = {
+ 0x80,0x01,0xd8,0x0f,0xfc,0x1f,0xfc,0x5f,0xf8,0xff,0xf8,0xff,
+ 0xfe,0xff,0xff,0xff,0xff,0x7f,0xfe,0x7f,0xfc,0x7f,0xfc,0x3f,
+ 0xf8,0x3f,0xf0,0x1f,0xe0,0x1f,0x00,0x00};
+static const uchar closedhand_bits[] = {
+ 0x00,0x00,0x00,0x00,0x00,0x00,0xb0,0x0d,0x48,0x32,0x08,0x50,
+ 0x10,0x40,0x18,0x40,0x04,0x40,0x04,0x20,0x08,0x20,0x10,0x10,
+ 0x20,0x10,0x20,0x10,0x00,0x00,0x00,0x00};
+static const uchar closedhandm_bits[] = {
+ 0x00,0x00,0x00,0x00,0x00,0x00,0xb0,0x0d,0xf8,0x3f,0xf8,0x7f,
+ 0xf0,0x7f,0xf8,0x7f,0xfc,0x7f,0xfc,0x3f,0xf8,0x3f,0xf0,0x1f,
+ 0xe0,0x1f,0xe0,0x1f,0x00,0x00,0x00,0x00};
+
+#endif
+
+void QWSServerPrivate::initializeCursor()
+{
+ Q_Q(QWSServer);
+ // setup system cursors
+#ifndef QT_NO_QWS_CURSOR
+// qt_screen->initCursor(sharedram + ramlen,true);
+
+ // default cursor
+ cursor = 0;
+ setCursor(QWSCursor::systemCursor(Qt::ArrowCursor));
+#endif
+ q->sendMouseEvent(QPoint(swidth/2, sheight/2), 0);
+}
+
+void QWSServerPrivate::setCursor(QWSCursor *curs)
+{
+#ifdef QT_NO_QWS_CURSOR
+ Q_UNUSED(curs);
+#else
+ if (cursor == curs)
+ return;
+
+ cursor = curs;
+
+ if (!haveviscurs || !curs)
+ curs = QWSCursor::systemCursor(Qt::BlankCursor);
+
+ if (qt_screencursor) {
+ qt_screencursor->set(curs->image(),
+ curs->hotSpot().x(),
+ curs->hotSpot().y());
+ }
+#endif
+}
+
+#ifndef QT_NO_QWS_CURSOR
+static void cleanupSystemCursorTable()
+{
+ for (int i = 0; i <= Qt::LastCursor; i++)
+ if (systemCursorTable[i]) {
+ delete systemCursorTable[i];
+ systemCursorTable[i] = 0;
+ }
+}
+#endif
+
+void QWSCursor::createSystemCursor(int id)
+{
+#ifdef QT_NO_QWS_CURSOR
+ Q_UNUSED(id);
+#else
+ if (!systemCursorTableInit) {
+ for (int i = 0; i <= Qt::LastCursor; i++)
+ systemCursorTable[i] = 0;
+ qAddPostRoutine(cleanupSystemCursorTable);
+ systemCursorTableInit = true;
+ }
+ switch (id) {
+ // 16x16 cursors
+ case Qt::ArrowCursor:
+ systemCursorTable[Qt::ArrowCursor] =
+ new QWSCursor(cur_arrow_bits, mcur_arrow_bits, 16, 16, 0, 0);
+ break;
+
+ case Qt::UpArrowCursor:
+ systemCursorTable[Qt::UpArrowCursor] =
+ new QWSCursor(cur_up_arrow_bits, mcur_up_arrow_bits, 16, 16, 7, 0);
+ break;
+
+ case Qt::CrossCursor:
+ systemCursorTable[Qt::CrossCursor] =
+ new QWSCursor(cur_cross_bits, mcur_cross_bits, 16, 16, 7, 7);
+ break;
+
+ case Qt::IBeamCursor:
+ systemCursorTable[Qt::IBeamCursor] =
+ new QWSCursor(cur_ibeam_bits, mcur_ibeam_bits, 16, 16, 7, 7);
+ break;
+
+ case Qt::SizeVerCursor:
+ systemCursorTable[Qt::SizeVerCursor] =
+ new QWSCursor(cur_ver_bits, mcur_ver_bits, 16, 16, 7, 7);
+ break;
+
+ case Qt::SizeHorCursor:
+ systemCursorTable[Qt::SizeHorCursor] =
+ new QWSCursor(cur_hor_bits, mcur_hor_bits, 16, 16, 7, 7);
+ break;
+
+ case Qt::SizeBDiagCursor:
+ systemCursorTable[Qt::SizeBDiagCursor] =
+ new QWSCursor(cur_bdiag_bits, mcur_bdiag_bits, 16, 16, 7, 7);
+ break;
+
+ case Qt::SizeFDiagCursor:
+ systemCursorTable[Qt::SizeFDiagCursor] =
+ new QWSCursor(cur_fdiag_bits, mcur_fdiag_bits, 16, 16, 7, 7);
+ break;
+
+ case Qt::BlankCursor:
+ systemCursorTable[Qt::BlankCursor] =
+ new QWSCursor(0, 0, 0, 0, 0, 0);
+ break;
+
+ // 20x20 cursors
+ case Qt::ForbiddenCursor:
+ systemCursorTable[Qt::ForbiddenCursor] =
+ new QWSCursor(forbidden_bits, forbiddenm_bits, 20, 20, 10, 10);
+ break;
+
+ // 32x32 cursors
+ case Qt::WaitCursor:
+ systemCursorTable[Qt::WaitCursor] =
+ new QWSCursor(wait_data_bits, wait_mask_bits, 32, 32, 15, 15);
+ break;
+
+ case Qt::SplitVCursor:
+ systemCursorTable[Qt::SplitVCursor] =
+ new QWSCursor(vsplit_bits, vsplitm_bits, 32, 32, 15, 15);
+ break;
+
+ case Qt::SplitHCursor:
+ systemCursorTable[Qt::SplitHCursor] =
+ new QWSCursor(hsplit_bits, hsplitm_bits, 32, 32, 15, 15);
+ break;
+
+ case Qt::SizeAllCursor:
+ systemCursorTable[Qt::SizeAllCursor] =
+ new QWSCursor(size_all_data_bits, size_all_mask_bits, 32, 32, 15, 15);
+ break;
+
+ case Qt::PointingHandCursor:
+ systemCursorTable[Qt::PointingHandCursor] =
+ new QWSCursor(phand_bits, phandm_bits, 32, 32, 0, 0);
+ break;
+
+ case Qt::WhatsThisCursor:
+ systemCursorTable[Qt::WhatsThisCursor] =
+ new QWSCursor(whatsthis_bits, whatsthism_bits, 32, 32, 0, 0);
+ break;
+ case Qt::BusyCursor:
+ systemCursorTable[Qt::BusyCursor] =
+ new QWSCursor(busy_bits, busym_bits, 32, 32, 0, 0);
+ break;
+
+ case Qt::OpenHandCursor:
+ systemCursorTable[Qt::OpenHandCursor] =
+ new QWSCursor(openhand_bits, openhandm_bits, 16, 16, 8, 8);
+ break;
+ case Qt::ClosedHandCursor:
+ systemCursorTable[Qt::ClosedHandCursor] =
+ new QWSCursor(closedhand_bits, closedhandm_bits, 16, 16, 8, 8);
+ break;
+ default:
+ qWarning("Unknown system cursor %d", id);
+ }
+#endif
+}
+
+QWSCursor *QWSCursor::systemCursor(int id)
+{
+ QWSCursor *cursor = 0;
+#ifdef QT_NO_QWS_CURSOR
+ Q_UNUSED(id);
+#else
+ if (id >= 0 && id <= Qt::LastCursor) {
+ if (!systemCursorTable[id])
+ createSystemCursor(id);
+ cursor = systemCursorTable[id];
+ }
+
+ if (cursor == 0) {
+ if (!systemCursorTable[Qt::ArrowCursor])
+ createSystemCursor(Qt::ArrowCursor);
+ cursor = systemCursorTable[Qt::ArrowCursor];
+ }
+#endif
+ return cursor;
+}
+
+void QWSCursor::set(const uchar *data, const uchar *mask,
+ int width, int height, int hx, int hy)
+{
+#ifdef QT_NO_QWS_CURSOR
+ Q_UNUSED(data);
+ Q_UNUSED(mask);
+ Q_UNUSED(width);
+ Q_UNUSED(height);
+ Q_UNUSED(hx);
+ Q_UNUSED(hy);
+#else
+ hot.setX(hx);
+ hot.setY(hy);
+
+ cursor = QImage(width,height, QImage::Format_Indexed8);
+
+ if (!width || !height || !data || !mask)
+ return;
+
+ cursor.setNumColors(3);
+ cursor.setColor(0, 0xff000000);
+ cursor.setColor(1, 0xffffffff);
+ cursor.setColor(2, 0x00000000);
+
+ int bytesPerLine = (width + 7) / 8;
+ int p = 0;
+ int d, m;
+
+ int x = -1, w = 0;
+
+ uchar *cursor_data = cursor.bits();
+ int bpl = cursor.bytesPerLine();
+ for (int i = 0; i < height; i++)
+ {
+ for (int j = 0; j < bytesPerLine; j++, data++, mask++)
+ {
+ for (int b = 0; b < 8 && j*8+b < width; b++)
+ {
+ d = *data & (1 << b);
+ m = *mask & (1 << b);
+ if (d && m) p = 0;
+ else if (!d && m) p = 1;
+ else p = 2;
+ cursor_data[j*8+b] = p;
+
+ // calc region
+ if (x < 0 && m)
+ x = j*8+b;
+ else if (x >= 0 && !m) {
+ x = -1;
+ w = 0;
+ }
+ if (m)
+ w++;
+ }
+ }
+ if (x >= 0) {
+ x = -1;
+ w = 0;
+ }
+ cursor_data += bpl;
+ }
+
+ if (qt_screencursor && qt_screencursor->supportsAlphaCursor())
+ createDropShadow(5, 2);
+#endif
+}
+
+// now we're really silly
+void QWSCursor::createDropShadow(int dropx, int dropy)
+{
+ //####
+#if 1 || defined(QT_NO_QWS_CURSOR) || defined(QT_NO_QWS_ALHPA_CURSOR)
+ Q_UNUSED(dropx);
+ Q_UNUSED(dropy);
+#else
+ if (cursor.width() + dropx > 64 || cursor.height() + dropy > 64)
+ return;
+
+ if (!cursor.hasAlphaBuffer()) {
+ cursor.setAlphaBuffer(true);
+
+ const int nblur=4;
+ const int darkness=140;
+
+ QImage drop(cursor.width()+dropx+nblur, cursor.height()+dropy+nblur, 8, 18);
+ drop.setColor(0, 0xff000000); // bg (black)
+ drop.setColor(1, 0xffffffff); // fg (white)
+ for (int i=0; i<16; i++) {
+ drop.setColor(2+i, (darkness*i/16)<<24);
+ }
+ drop.fill(2); // all trans
+ QImage drop2 = drop.copy();
+
+ int cp;
+
+ // made solid shadow
+ for (int row = 0; row < cursor.height(); row++) {
+ for (int col = 0; col < cursor.width(); col++) {
+ cp = cursor.pixelIndex(col, row);
+ if (cp != 2)
+ drop.setPixel(col+dropx, row+dropy, 17);
+ }
+ }
+
+ // blur shadow
+ for (int blur=0; blur<nblur; blur++) {
+ QImage& to((blur&1)?drop:drop2);
+ QImage& from((blur&1)?drop2:drop);
+ for (int row = 1; row < drop.height()-1; row++) {
+ for (int col = 1; col < drop.width()-1; col++) {
+ int t=0;
+ for (int dx=-1; dx<=1; dx++) {
+ for (int dy=-1; dy<=1; dy++) {
+ t += from.pixelIndex(col+dx,row+dy)-2;
+ }
+ }
+ to.setPixel(col,row,2+t/9);
+ }
+ }
+ }
+
+ // copy cursor
+ for (int row = 0; row < cursor.height(); row++) {
+ for (int col = 0; col < cursor.width(); col++) {
+ cp = cursor.pixelIndex(col, row);
+ if (cp != 2)
+ drop.setPixel(col, row, cp);
+ }
+ }
+
+ cursor = drop;
+ }
+#endif
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/embedded/qwscursor_qws.h b/src/gui/embedded/qwscursor_qws.h
new file mode 100644
index 0000000000..6d19c31aa0
--- /dev/null
+++ b/src/gui/embedded/qwscursor_qws.h
@@ -0,0 +1,83 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QWSCURSOR_QWS_H
+#define QWSCURSOR_QWS_H
+
+#include <QtGui/qimage.h>
+#include <QtGui/qregion.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+class QWSCursor
+{
+public:
+ QWSCursor() {}
+ QWSCursor(const uchar *data, const uchar *mask, int width, int height,
+ int hotX, int hotY)
+ { set(data, mask, width, height, hotX, hotY); }
+
+ void set(const uchar *data, const uchar *mask,
+ int width, int height, int hotX, int hotY);
+
+ QPoint hotSpot() const { return hot; }
+ QImage &image() { return cursor; }
+
+ static QWSCursor *systemCursor(int id);
+
+private:
+ static void createSystemCursor(int id);
+ void createDropShadow(int dropx, int dropy);
+
+private:
+ QPoint hot;
+ QImage cursor;
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QWSCURSOR_QWS_H
diff --git a/src/gui/embedded/qwsdisplay_qws.h b/src/gui/embedded/qwsdisplay_qws.h
new file mode 100644
index 0000000000..455d46db5d
--- /dev/null
+++ b/src/gui/embedded/qwsdisplay_qws.h
@@ -0,0 +1,185 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QWSDISPLAY_QWS_H
+#define QWSDISPLAY_QWS_H
+
+#include <QtCore/qobject.h>
+#include <QtCore/qbytearray.h>
+#include <QtGui/qregion.h>
+#include <QtGui/qimage.h>
+#include <QtGui/qwindowdefs.h>
+#include <QtCore/qlist.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+class QWSEvent;
+class QWSMouseEvent;
+class QWSQCopMessageEvent;
+class QVariant;
+class QLock;
+
+class QWSWindowInfo
+{
+
+public:
+
+ int winid;
+ unsigned int clientid;
+ QString name;
+
+};
+
+#define QT_QWS_PROPERTY_CONVERTSELECTION 999
+#define QT_QWS_PROPERTY_WINDOWNAME 998
+#define QT_QWS_PROPERTY_MARKEDTEXT 997
+
+class QWSDisplay;
+extern Q_GUI_EXPORT QWSDisplay *qt_fbdpy;
+
+class Q_GUI_EXPORT QWSDisplay
+{
+public:
+ QWSDisplay();
+ ~QWSDisplay();
+
+ static QWSDisplay* instance() { return qt_fbdpy; }
+
+ bool eventPending() const;
+ QWSEvent *getEvent();
+// QWSRegionManager *regionManager() const;
+
+ uchar* frameBuffer() const;
+ int width() const;
+ int height() const;
+ int depth() const;
+ int pixmapDepth() const;
+ bool supportsDepth(int) const;
+
+ uchar *sharedRam() const;
+ int sharedRamSize() const;
+
+#ifndef QT_NO_QWS_PROPERTIES
+ void addProperty(int winId, int property);
+ void setProperty(int winId, int property, int mode, const QByteArray &data);
+ void setProperty(int winId, int property, int mode, const char * data);
+ void removeProperty(int winId, int property);
+ bool getProperty(int winId, int property, char *&data, int &len);
+#endif // QT_NO_QWS_PROPERTIES
+
+ QList<QWSWindowInfo> windowList();
+ int windowAt(const QPoint &);
+
+ void setIdentity(const QString &appName);
+ void nameRegion(int winId, const QString& n, const QString &c);
+ void requestRegion(int winId, const QString &surfacekey,
+ const QByteArray &surfaceData,
+ const QRegion &region);
+ void repaintRegion(int winId, int windowFlags, bool opaque, QRegion);
+ void moveRegion(int winId, int dx, int dy);
+ void destroyRegion(int winId);
+ void requestFocus(int winId, bool get);
+ void setAltitude(int winId, int altitude, bool fixed = false);
+ void setOpacity(int winId, int opacity);
+ int takeId();
+ void setSelectionOwner(int winId, const QTime &time);
+ void convertSelection(int winId, int selectionProperty, const QString &mimeTypes);
+ void defineCursor(int id, const QBitmap &curs, const QBitmap &mask,
+ int hotX, int hotY);
+ void destroyCursor(int id);
+ void selectCursor(QWidget *w, unsigned int id);
+ void setCursorPosition(int x, int y);
+ void grabMouse(QWidget *w, bool grab);
+ void grabKeyboard(QWidget *w, bool grab);
+ void playSoundFile(const QString&);
+ void registerChannel(const QString &channel);
+ void sendMessage(const QString &channel, const QString &msg,
+ const QByteArray &data);
+ void flushCommands();
+#ifndef QT_NO_QWS_INPUTMETHODS
+ void sendIMUpdate(int type, int winId, int widgetid);
+ void resetIM();
+ void sendIMResponse(int winId, int property, const QVariant &result);
+ void sendIMMouseEvent(int index, bool isPress);
+#endif
+ QWSQCopMessageEvent* waitForQCopResponse();
+ void sendFontCommand(int type, const QByteArray &fontName);
+
+ void setWindowCaption(QWidget *w, const QString &);
+
+ // Lock display for access only by this process
+ static bool initLock(const QString &filename, bool create = false);
+ static bool grabbed();
+ static void grab();
+ static void grab(bool write);
+ static void ungrab();
+
+ static void setTransformation(int transformation, int screenNo = -1);
+ static void setRawMouseEventFilter(void (*filter)(QWSMouseEvent *));
+
+private:
+ friend int qt_fork_qapplication();
+ friend void qt_app_reinit( const QString& newAppName );
+ friend class QApplication;
+ friend class QCopChannel;
+ friend class QWSEmbedWidget;
+ friend class QWSEmbedWidgetPrivate;
+ class Data;
+ friend class Data;
+ Data *d;
+
+ friend class QWSMemorySurface;
+ friend class QWSOnScreenSurface;
+ friend class QWSDirectPainterSurface;
+ int getPropertyLen;
+ char *getPropertyData;
+ static QLock *lock;
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QWSDISPLAY_QWS_H
diff --git a/src/gui/embedded/qwsdisplay_qws_p.h b/src/gui/embedded/qwsdisplay_qws_p.h
new file mode 100644
index 0000000000..819b826d69
--- /dev/null
+++ b/src/gui/embedded/qwsdisplay_qws_p.h
@@ -0,0 +1,161 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QWSDISPLAY_QWS_P_H
+#define QWSDISPLAY_QWS_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 "qwsdisplay_qws.h"
+#include "qwssocket_qws.h"
+#include "qwsevent_qws.h"
+#include <private/qwssharedmemory_p.h>
+#include "qwscommand_qws_p.h"
+#include "qwslock_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class QWSDisplay::Data
+{
+public:
+ Data(QObject* parent, bool singleProcess = false);
+ ~Data();
+
+ void flush();
+
+ bool queueNotEmpty();
+ QWSEvent *dequeue();
+ QWSEvent *peek();
+
+ bool directServerConnection();
+ void fillQueue();
+#ifndef QT_NO_QWS_MULTIPROCESS
+ void connectToPipe();
+ void waitForConnection();
+ void waitForPropertyReply();
+ void waitForRegionAck(int winId);
+ void waitForRegionEvents(int winId, bool ungrabDisplay);
+ bool hasPendingRegionEvents() const;
+#endif
+ void waitForCreation();
+#ifndef QT_NO_COP
+ void waitForQCopResponse();
+#endif
+ void init();
+ void reinit( const QString& newAppName );
+ void create(int n = 1);
+
+ void flushCommands();
+ void sendCommand(QWSCommand & cmd);
+ void sendSynchronousCommand(QWSCommand & cmd);
+
+ QWSEvent *readMore();
+
+ int takeId();
+
+ void setMouseFilter(void (*filter)(QWSMouseEvent*));
+
+ //####public data members
+
+// QWSRegionManager *rgnMan;
+ uchar *sharedRam;
+#ifndef QT_NO_QWS_MULTIPROCESS
+ QWSSharedMemory shm;
+#endif
+ int sharedRamSize;
+
+#ifndef QT_NO_QWS_MULTIPROCESS
+ static QWSLock *clientLock;
+
+ static bool lockClient(QWSLock::LockType, int timeout = -1);
+ static void unlockClient(QWSLock::LockType);
+ static bool waitClient(QWSLock::LockType, int timeout = -1);
+ static QWSLock* getClientLock();
+#endif // QT_NO_QWS_MULTIPROCESS
+
+private:
+#ifndef QT_NO_QWS_MULTIPROCESS
+ QWSSocket *csocket;
+#endif
+ QList<QWSEvent*> queue;
+
+#if 0
+ void debugQueue() {
+ for (int i = 0; i < queue.size(); ++i) {
+ QWSEvent *e = queue.at(i);
+ qDebug( " ev %d type %d sl %d rl %d", i, e->type, e->simpleLen, e->rawLen);
+ }
+ }
+#endif
+
+ QWSConnectedEvent* connected_event;
+ QWSMouseEvent* mouse_event;
+ int region_events_count;
+ int mouse_state;
+ int mouse_winid;
+ QPoint region_offset;
+ int region_offset_window;
+#ifndef QT_NO_COP
+ QWSQCopMessageEvent *qcop_response;
+#endif
+ QWSEvent* current_event;
+ QList<int> unused_identifiers;
+#ifdef QAPPLICATION_EXTRA_DEBUG
+ int mouse_event_count;
+#endif
+ void (*mouseFilter)(QWSMouseEvent *);
+
+ enum { VariableEvent=-1 };
+
+};
+
+QT_END_NAMESPACE
+
+#endif // QWSDISPLAY_QWS_P_H
diff --git a/src/gui/embedded/qwsembedwidget.cpp b/src/gui/embedded/qwsembedwidget.cpp
new file mode 100644
index 0000000000..a449c65f06
--- /dev/null
+++ b/src/gui/embedded/qwsembedwidget.cpp
@@ -0,0 +1,227 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qwsembedwidget.h"
+
+#ifndef QT_NO_QWSEMBEDWIDGET
+
+#include <qwsdisplay_qws.h>
+#include <private/qwidget_p.h>
+#include <private/qwsdisplay_qws_p.h>
+#include <private/qwscommand_qws_p.h>
+
+QT_BEGIN_NAMESPACE
+
+// TODO:
+// Must remove window decorations from the embedded window
+// Focus In/Out, Keyboard/Mouse...
+//
+// BUG: what if my parent change parent?
+
+class QWSEmbedWidgetPrivate : public QWidgetPrivate
+{
+ Q_DECLARE_PUBLIC(QWSEmbedWidget);
+
+public:
+ QWSEmbedWidgetPrivate(int winId);
+ void updateWindow();
+ void resize(const QSize &size);
+
+ QWidget *window;
+ WId windowId;
+ WId embeddedId;
+};
+
+QWSEmbedWidgetPrivate::QWSEmbedWidgetPrivate(int winId)
+ : window(0), windowId(0), embeddedId(winId)
+{
+}
+
+void QWSEmbedWidgetPrivate::updateWindow()
+{
+ Q_Q(QWSEmbedWidget);
+
+ QWidget *win = q->window();
+ if (win == window)
+ return;
+
+ if (window) {
+ window->removeEventFilter(q);
+ QWSEmbedCommand command;
+ command.setData(windowId, embeddedId, QWSEmbedEvent::StopEmbed);
+ QWSDisplay::instance()->d->sendCommand(command);
+ }
+
+ window = win;
+ if (!window)
+ return;
+ windowId = window->winId();
+
+ QWSEmbedCommand command;
+ command.setData(windowId, embeddedId, QWSEmbedEvent::StartEmbed);
+ QWSDisplay::instance()->d->sendCommand(command);
+ window->installEventFilter(q);
+ q->installEventFilter(q);
+}
+
+void QWSEmbedWidgetPrivate::resize(const QSize &size)
+{
+ if (!window)
+ return;
+
+ Q_Q(QWSEmbedWidget);
+
+ QWSEmbedCommand command;
+ command.setData(windowId, embeddedId, QWSEmbedEvent::Region,
+ QRect(q->mapToGlobal(QPoint(0, 0)), size));
+ QWSDisplay::instance()->d->sendCommand(command);
+}
+
+/*!
+ \class QWSEmbedWidget
+ \since 4.2
+ \ingroup qws
+ \ingroup advanced
+
+ \brief The QWSEmbedWidget class enables embedded top-level widgets
+ in Qt for Embedded Linux.
+
+ Note that this class is only available in \l{Qt for Embedded Linux}.
+
+ QWSEmbedWidget inherits QWidget and acts as any other widget, but
+ in addition it is capable of embedding another top-level widget.
+
+ An example of use is when painting directly onto the screen using
+ the QDirectPainter class. Then the reserved region can be embedded
+ into an instance of the QWSEmbedWidget class, providing for
+ example event handling and size policies for the reserved region.
+
+ All that is required to embed a top-level widget is its window ID.
+
+ \sa {Qt for Embedded Linux Architecture}
+*/
+
+/*!
+ Constructs a widget with the given \a parent, embedding the widget
+ identified by the given window \a id.
+*/
+QWSEmbedWidget::QWSEmbedWidget(WId id, QWidget *parent)
+ : QWidget(*new QWSEmbedWidgetPrivate(id), parent, 0)
+{
+ Q_D(QWSEmbedWidget);
+ d->updateWindow();
+}
+
+/*!
+ Destroys this widget.
+*/
+QWSEmbedWidget::~QWSEmbedWidget()
+{
+ Q_D(QWSEmbedWidget);
+ if (!d->window)
+ return;
+
+ QWSEmbedCommand command;
+ command.setData(d->windowId, d->embeddedId, QWSEmbedEvent::StopEmbed);
+ QWSDisplay::instance()->d->sendCommand(command);
+}
+
+/*!
+ \reimp
+*/
+bool QWSEmbedWidget::eventFilter(QObject *object, QEvent *event)
+{
+ Q_D(QWSEmbedWidget);
+ if (object == d->window && event->type() == QEvent::Move)
+ resizeEvent(0);
+ else if (object == this && event->type() == QEvent::Hide)
+ d->resize(QSize());
+ return QWidget::eventFilter(object, event);
+}
+
+/*!
+ \reimp
+*/
+void QWSEmbedWidget::changeEvent(QEvent *event)
+{
+ Q_D(QWSEmbedWidget);
+ if (event->type() == QEvent::ParentChange)
+ d->updateWindow();
+}
+
+/*!
+ \reimp
+*/
+void QWSEmbedWidget::resizeEvent(QResizeEvent*)
+{
+ Q_D(QWSEmbedWidget);
+ d->resize(rect().size());
+}
+
+/*!
+ \reimp
+*/
+void QWSEmbedWidget::moveEvent(QMoveEvent*)
+{
+ resizeEvent(0);
+}
+
+/*!
+ \reimp
+*/
+void QWSEmbedWidget::hideEvent(QHideEvent*)
+{
+ Q_D(QWSEmbedWidget);
+ d->resize(QSize());
+}
+
+/*!
+ \reimp
+*/
+void QWSEmbedWidget::showEvent(QShowEvent*)
+{
+ Q_D(QWSEmbedWidget);
+ d->resize(rect().size());
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_QWSEMBEDWIDGET
diff --git a/src/gui/embedded/qwsembedwidget.h b/src/gui/embedded/qwsembedwidget.h
new file mode 100644
index 0000000000..6badf76c2c
--- /dev/null
+++ b/src/gui/embedded/qwsembedwidget.h
@@ -0,0 +1,82 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QWSEMBEDWIDGET_H
+#define QWSEMBEDWIDGET_H
+
+#include <QtGui/qwidget.h>
+
+#ifndef QT_NO_QWSEMBEDWIDGET
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+class QWSEmbedWidgetPrivate;
+
+class Q_GUI_EXPORT QWSEmbedWidget : public QWidget
+{
+ Q_OBJECT
+
+public:
+ QWSEmbedWidget(WId winId, QWidget *parent = 0);
+ ~QWSEmbedWidget();
+
+protected:
+ bool eventFilter(QObject *object, QEvent *event);
+ void changeEvent(QEvent *event);
+ void resizeEvent(QResizeEvent *event);
+ void moveEvent(QMoveEvent *event);
+ void hideEvent(QHideEvent *event);
+ void showEvent(QShowEvent *event);
+
+private:
+ Q_DECLARE_PRIVATE(QWSEmbedWidget)
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QT_NO_QWSEMBEDWIDGET
+#endif // QWSEMBEDWIDGET_H
diff --git a/src/gui/embedded/qwsevent_qws.cpp b/src/gui/embedded/qwsevent_qws.cpp
new file mode 100644
index 0000000000..e23eacd5dd
--- /dev/null
+++ b/src/gui/embedded/qwsevent_qws.cpp
@@ -0,0 +1,216 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qwsevent_qws.h"
+
+QT_BEGIN_NAMESPACE
+
+QWSEvent *QWSEvent::factory(int type)
+{
+ QWSEvent *event = 0;
+ switch (type) {
+ case QWSEvent::Connected:
+ event = new QWSConnectedEvent;
+ break;
+ case QWSEvent::MaxWindowRect:
+ event = new QWSMaxWindowRectEvent;
+ break;
+ case QWSEvent::Mouse:
+ event = new QWSMouseEvent;
+ break;
+ case QWSEvent::Focus:
+ event = new QWSFocusEvent;
+ break;
+ case QWSEvent::Key:
+ event = new QWSKeyEvent;
+ break;
+ case QWSEvent::Region:
+ event = new QWSRegionEvent;
+ break;
+ case QWSEvent::Creation:
+ event = new QWSCreationEvent;
+ break;
+#ifndef QT_NO_QWS_PROPERTIES
+ case QWSEvent::PropertyNotify:
+ event = new QWSPropertyNotifyEvent;
+ break;
+ case QWSEvent::PropertyReply:
+ event = new QWSPropertyReplyEvent;
+ break;
+#endif // QT_NO_QWS_PROPERTIES
+ case QWSEvent::SelectionClear:
+ event = new QWSSelectionClearEvent;
+ break;
+ case QWSEvent::SelectionRequest:
+ event = new QWSSelectionRequestEvent;
+ break;
+ case QWSEvent::SelectionNotify:
+ event = new QWSSelectionNotifyEvent;
+ break;
+#ifndef QT_NO_COP
+ case QWSEvent::QCopMessage:
+ event = new QWSQCopMessageEvent;
+ break;
+#endif
+ case QWSEvent::WindowOperation:
+ event = new QWSWindowOperationEvent;
+ break;
+
+#ifndef QT_NO_QWS_INPUTMETHODS
+ case QWSEvent::IMEvent:
+ event = new QWSIMEvent;
+ break;
+ case QWSEvent::IMQuery:
+ event = new QWSIMQueryEvent;
+ break;
+ case QWSEvent::IMInit:
+ event = new QWSIMInitEvent;
+ break;
+#endif
+#ifndef QT_NO_QWSEMBEDWIDGET
+ case QWSEvent::Embed:
+ event = new QWSEmbedEvent;
+ break;
+#endif
+ case QWSEvent::Font:
+ event = new QWSFontEvent;
+ break;
+ case QWSEvent::ScreenTransformation:
+ event = new QWSScreenTransformationEvent;
+ break;
+ default:
+ qCritical("QWSEvent::factory() : Unknown event type %08x!", type);
+ }
+ return event;
+}
+
+/*!
+ \class QWSEvent
+ \ingroup qws
+
+ \brief The QWSEvent class encapsulates an event in Qt for Embedded Linux.
+
+ When running a \l{Qt for Embedded Linux} application, it either runs as a
+ server or connects to an existing server. All system generated
+ events are passed to the server application which then propagates
+ the event to the appropriate client.
+
+ Whenever the server receives an event, it queries its stack of
+ top-level windows to find the window containing the event's
+ position. Each window can identify the client application that
+ created it, and returns its ID to the server upon
+ request. Finally, the server forwards the event, encapsulated by
+ an instance of the QWSEvent class, to the appropriate client.
+
+ \image qt-embedded-client.png
+
+ The server communicates with the client applications over the UNIX
+ domain socket. You can retrieve direct access to all the events a
+ client receives from the server, by reimplementing QApplication's
+ \l {QApplication::}{qwsEventFilter()} function.
+
+ QWSEvent provides the \l Type enum specifying the origin of the
+ event. Internally, each type is represented by a QWSEvent
+ subclass, e.g., \c QWSKeyEvent.
+
+ \sa QWSServer, QWSClient, {Qt for Embedded Linux Architecture}
+*/
+
+/*!
+ \enum QWSEvent::Type
+
+ This enum describes the origin of the event.
+
+ \value NoEvent No event has occurred.
+ \value Connected An application has connected to the server.
+ \value Mouse A mouse button is pressed or released, or the mouse cursor is moved.
+ See also \l{Qt for Embedded Linux Pointer Handling}.
+ \value Focus A window has lost or received focus.
+ \value Key A key is pressed or released. See also \l{Qt for Embedded Linux Character Input}.
+ \value Region A region has changed.
+ \value Creation The server has created an ID, typically for a window.
+ \value PropertyNotify A property has changed.
+ \value PropertyReply The server is responding to a request for a property's value.
+ \value SelectionClear A selection is deleted.
+ \value SelectionRequest The server has queried for a selection.
+ \value SelectionNotify A new selection has been created.
+ \value MaxWindowRect The server has changed the maximum window for an application.
+ \value QCopMessage A new Qt Cop message has appeared. See also QCopChannel
+ \value WindowOperation A window operation, e.g. resizing, has occurred.
+ \value IMEvent An input method has been used to enter text for languages with
+ non-Latin alphabets. See also QWSInputMethod.
+ \value IMQuery An input method query for a specified property has occurred.
+ See also QWSInputMethod.
+ \value NEvent The number of events has changed.
+ \value Embed An event used internally to implement embedded windows. See also
+ QWSEmbedWidget.
+ \value ScreenTransformation An event used internally to notify the client processes
+ that the screen has changed for example, rotation, etc.
+ \omitvalue Font
+ \omitvalue IMInit
+*/
+
+/*!
+ \fn QWSMouseEvent *QWSEvent::asMouse()
+ \internal
+*/
+
+/*!
+ \fn int QWSEvent::window()
+ \internal
+*/
+
+/*!
+ \fn int QWSEvent::window() const
+ \internal
+*/
+
+/*!
+ \fn QWSEvent *QWSEvent::factory(int type)
+ \internal
+*/
+
+/*!
+ \fn QWSEvent::QWSEvent( int t, int len, char * ptr)
+ \internal
+*/
+
+QT_END_NAMESPACE
diff --git a/src/gui/embedded/qwsevent_qws.h b/src/gui/embedded/qwsevent_qws.h
new file mode 100644
index 0000000000..f231db2133
--- /dev/null
+++ b/src/gui/embedded/qwsevent_qws.h
@@ -0,0 +1,459 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QWSEVENT_QWS_H
+#define QWSEVENT_QWS_H
+
+#include <QtGui/qwsutils_qws.h>
+#include <QtGui/qwsprotocolitem_qws.h>
+#include <QtCore/qrect.h>
+#include <QtGui/qregion.h>
+#include <QtCore/qvector.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+struct QWSMouseEvent;
+
+struct QWSEvent : QWSProtocolItem {
+
+ QWSEvent(int t, int len, char *ptr) : QWSProtocolItem(t,len,ptr) {}
+
+
+
+ enum Type {
+ NoEvent,
+ Connected,
+ Mouse,
+ Focus,
+ Key,
+ Region,
+ Creation,
+ PropertyNotify,
+ PropertyReply,
+ SelectionClear,
+ SelectionRequest,
+ SelectionNotify,
+ MaxWindowRect,
+ QCopMessage,
+ WindowOperation,
+ IMEvent,
+ IMQuery,
+ IMInit,
+ Embed,
+ Font,
+ ScreenTransformation,
+ NEvent
+ };
+
+ QWSMouseEvent *asMouse()
+ { return type == Mouse ? reinterpret_cast<QWSMouseEvent*>(this) : 0; }
+ int window() { return *(reinterpret_cast<int*>(simpleDataPtr)); }
+ int window() const { return *(reinterpret_cast<int*>(simpleDataPtr)); }
+ static QWSEvent *factory(int type);
+};
+
+
+//All events must start with windowID
+
+struct QWSConnectedEvent : QWSEvent {
+ QWSConnectedEvent()
+ : QWSEvent(QWSEvent::Connected, sizeof(simpleData),
+ reinterpret_cast<char*>(&simpleData)) {}
+
+ void setData(const char *d, int len, bool allocateMem = true) {
+ QWSEvent::setData(d, len, allocateMem);
+ display = reinterpret_cast<char*>(rawDataPtr);
+ }
+
+ struct SimpleData {
+ int window;
+ int len;
+ int clientId;
+ int servershmid;
+ } simpleData;
+
+ char *display;
+};
+
+struct QWSMaxWindowRectEvent : QWSEvent {
+ QWSMaxWindowRectEvent()
+ : QWSEvent(MaxWindowRect, sizeof(simpleData), reinterpret_cast<char*>(&simpleData)) { }
+ struct SimpleData {
+ int window;
+ QRect rect;
+ } simpleData;
+};
+
+struct QWSMouseEvent : QWSEvent {
+ QWSMouseEvent()
+ : QWSEvent(QWSEvent::Mouse, sizeof(simpleData),
+ reinterpret_cast<char*>(&simpleData)) {}
+ struct SimpleData {
+ int window;
+ int x_root, y_root, state, delta;
+ int time; // milliseconds
+ } simpleData;
+};
+
+struct QWSFocusEvent : QWSEvent {
+ QWSFocusEvent()
+ : QWSEvent(QWSEvent::Focus, sizeof(simpleData), reinterpret_cast<char*>(&simpleData))
+ { memset(reinterpret_cast<char*>(&simpleData),0,sizeof(simpleData)); }
+ struct SimpleData {
+ int window;
+ uint get_focus:1;
+ } simpleData;
+};
+
+struct QWSKeyEvent: QWSEvent {
+ QWSKeyEvent()
+ : QWSEvent(QWSEvent::Key, sizeof(simpleData),
+ reinterpret_cast<char*>(&simpleData)) {}
+ struct SimpleData {
+ int window;
+ uint keycode;
+ Qt::KeyboardModifiers modifiers;
+ ushort unicode;
+ uint is_press:1;
+ uint is_auto_repeat:1;
+ } simpleData;
+};
+
+
+struct QWSCreationEvent : QWSEvent {
+ QWSCreationEvent()
+ : QWSEvent(QWSEvent::Creation, sizeof(simpleData),
+ reinterpret_cast<char*>(&simpleData)) {}
+ struct SimpleData {
+ int objectid;
+ int count;
+ } simpleData;
+};
+
+#ifndef QT_NO_QWS_PROPERTIES
+struct QWSPropertyNotifyEvent : QWSEvent {
+ QWSPropertyNotifyEvent()
+ : QWSEvent(QWSEvent::PropertyNotify, sizeof(simpleData),
+ reinterpret_cast<char*>(&simpleData)) {}
+ enum State {
+ PropertyNewValue,
+ PropertyDeleted
+ };
+ struct SimpleData {
+ int window;
+ int property;
+ int state;
+ } simpleData;
+};
+#endif
+
+struct QWSSelectionClearEvent : QWSEvent {
+ QWSSelectionClearEvent()
+ : QWSEvent(QWSEvent::SelectionClear, sizeof(simpleData),
+ reinterpret_cast<char*>(&simpleData)) {}
+ struct SimpleData {
+ int window;
+ } simpleData;
+};
+
+struct QWSSelectionRequestEvent : QWSEvent {
+ QWSSelectionRequestEvent()
+ : QWSEvent(QWSEvent::SelectionRequest, sizeof(simpleData),
+ reinterpret_cast<char*>(&simpleData)) {}
+ struct SimpleData {
+ int window;
+ int requestor; // window which wants the selection
+ int property; // property on requestor into which the selection should be stored, normally QWSProperty::PropSelection
+ int mimeTypes; // Value is stored in the property mimeType on the requestor window. This value may contain
+ // multiple mimeTypes separated by ;; where the order reflects the priority
+ } simpleData;
+};
+
+struct QWSSelectionNotifyEvent : QWSEvent {
+ QWSSelectionNotifyEvent()
+ : QWSEvent(QWSEvent::SelectionNotify, sizeof(simpleData),
+ reinterpret_cast<char*>(&simpleData)) {}
+ struct SimpleData {
+ int window;
+ int requestor; // the window which wanted the selection and to which this event is sent
+ int property; // property of requestor in which the data of the selection is stored
+ int mimeType; // a property on the requestor in which the mime type in which the selection is, is stored
+ } simpleData;
+};
+
+//complex events:
+
+struct QWSRegionEvent : QWSEvent {
+ QWSRegionEvent()
+ : QWSEvent(QWSEvent::Region, sizeof(simpleData),
+ reinterpret_cast<char*>(&simpleData))
+ { memset(reinterpret_cast<char*>(&simpleData),0,sizeof(simpleData)); }
+
+ void setData(const char *d, int len, bool allocateMem = true) {
+ QWSEvent::setData(d, len, allocateMem);
+ rectangles = reinterpret_cast<QRect*>(rawDataPtr);
+ }
+
+ void setData(int winId, const QRegion &region, uint type) {
+ const QVector<QRect> rects = region.rects();
+ setData(reinterpret_cast<const char*>(rects.constData()),
+ rects.size() * sizeof(QRect));
+ simpleData.window = winId;
+ simpleData.nrectangles = rects.size();
+ simpleData.type = type;
+#ifdef QT_QWS_CLIENTBLIT
+ simpleData.id = 0;
+#endif
+ }
+
+ enum Type {Allocation
+#ifdef QT_QWS_CLIENTBLIT
+ , DirectPaint
+#endif
+ };
+ struct SimpleData {
+ int window;
+ int nrectangles;
+#ifdef QT_QWS_CLIENTBLIT
+ int id;
+#endif
+ uint type:8;
+ } simpleData;
+
+ QRect *rectangles;
+};
+
+#ifndef QT_NO_QWSEMBEDWIDGET
+struct QWSEmbedEvent : QWSEvent
+{
+ QWSEmbedEvent() : QWSEvent(QWSEvent::Embed, sizeof(simpleData),
+ reinterpret_cast<char*>(&simpleData))
+ {}
+
+ enum Type { StartEmbed = 1, StopEmbed = 2, Region = 4 };
+
+ void setData(const char *d, int len, bool allocateMem = true) {
+ QWSEvent::setData(d, len, allocateMem);
+ region.setRects(reinterpret_cast<const QRect *>(rawDataPtr),
+ simpleData.nrectangles);
+ }
+
+ void setData(int winId, Type type, const QRegion &reg = QRegion()) {
+ simpleData.window = winId;
+ simpleData.nrectangles = reg.rects().size();
+ simpleData.type = type;
+ region = reg;
+ const QVector<QRect> rects = reg.rects();
+ QWSEvent::setData(reinterpret_cast<const char*>(rects.data()),
+ rects.size() * sizeof(QRect));
+ }
+
+ struct SimpleData {
+ int window;
+ int nrectangles;
+ Type type;
+ } simpleData;
+
+ QRegion region;
+};
+#endif // QT_NO_QWSEMBEDWIDGET
+
+#ifndef QT_NO_QWS_PROPERTIES
+struct QWSPropertyReplyEvent : QWSEvent {
+ QWSPropertyReplyEvent()
+ : QWSEvent(QWSEvent::PropertyReply, sizeof(simpleData),
+ reinterpret_cast<char*>(&simpleData)) {}
+
+ void setData(const char *d, int len, bool allocateMem = true) {
+ QWSEvent::setData(d, len, allocateMem);
+ data = reinterpret_cast<char*>(rawDataPtr);
+ }
+
+ struct SimpleData {
+ int window;
+ int property;
+ int len;
+ } simpleData;
+ char *data;
+};
+#endif //QT_NO_QWS_PROPERTIES
+
+#ifndef QT_NO_COP
+struct QWSQCopMessageEvent : QWSEvent {
+ QWSQCopMessageEvent()
+ : QWSEvent(QWSEvent::QCopMessage, sizeof(simpleData),
+ reinterpret_cast<char*>(&simpleData))
+ { memset(reinterpret_cast<char*>(&simpleData),0,sizeof(simpleData)); }
+
+ void setData(const char *d, int len, bool allocateMem = true) {
+ QWSEvent::setData(d, len, allocateMem);
+ char* p = rawDataPtr;
+ channel = QByteArray(p, simpleData.lchannel);
+ p += simpleData.lchannel;
+ message = QByteArray(p, simpleData.lmessage);
+ p += simpleData.lmessage;
+ data = QByteArray(p, simpleData.ldata);
+ }
+
+ void setDataDirect(const char *d, int len) {
+ QWSEvent::setData(d, len, false);
+ deleteRaw = true;
+ }
+
+ struct SimpleData {
+ bool is_response;
+ int lchannel;
+ int lmessage;
+ int ldata;
+ } simpleData;
+
+ QByteArray channel;
+ QByteArray message;
+ QByteArray data;
+};
+
+#endif
+
+struct QWSWindowOperationEvent : QWSEvent {
+ QWSWindowOperationEvent()
+ : QWSEvent(WindowOperation, sizeof(simpleData), reinterpret_cast<char*>(&simpleData)) { }
+
+ enum Operation { Show, Hide, ShowMaximized, ShowNormal, ShowMinimized, Close };
+ struct SimpleData {
+ int window;
+ Operation op;
+ } simpleData;
+};
+
+#ifndef QT_NO_QWS_INPUTMETHODS
+
+
+struct QWSIMEvent : QWSEvent {
+ QWSIMEvent()
+ : QWSEvent(IMEvent, sizeof(simpleData), reinterpret_cast<char*>(&simpleData))
+ { memset(reinterpret_cast<char*>(&simpleData),0,sizeof(simpleData)); }
+
+ struct SimpleData {
+ int window;
+ int replaceFrom;
+ int replaceLength;
+ } simpleData;
+
+ void setData(const char *d, int len, bool allocateMem = true) {
+ QWSEvent::setData(d, len, allocateMem);
+ streamingData = QByteArray::fromRawData(rawDataPtr, len);
+ }
+ QByteArray streamingData;
+};
+
+
+struct QWSIMInitEvent : QWSEvent {
+ QWSIMInitEvent()
+ : QWSEvent(IMInit, sizeof(simpleData), reinterpret_cast<char*>(&simpleData))
+ { memset(reinterpret_cast<char*>(&simpleData),0,sizeof(simpleData)); }
+
+ struct SimpleData {
+ int window;
+ int existence;
+ } simpleData;
+
+ void setData(const char *d, int len, bool allocateMem = true) {
+ QWSEvent::setData(d, len, allocateMem);
+ streamingData = QByteArray::fromRawData(rawDataPtr, len);
+ }
+ QByteArray streamingData;
+};
+
+
+struct QWSIMQueryEvent : QWSEvent {
+ QWSIMQueryEvent()
+ : QWSEvent(QWSEvent::IMQuery, sizeof(simpleData),
+ reinterpret_cast<char*>(&simpleData)) {}
+
+ struct SimpleData {
+ int window;
+ int property;
+ } simpleData;
+
+};
+
+#endif
+
+struct QWSFontEvent : QWSEvent {
+ QWSFontEvent()
+ : QWSEvent(QWSEvent::Font, sizeof(simpleData),
+ reinterpret_cast<char*>(&simpleData)) {}
+
+ enum EventType {
+ FontRemoved
+ };
+
+ void setData(const char *d, int len, bool allocateMem = true) {
+ QWSEvent::setData(d, len, allocateMem);
+ fontName = QByteArray::fromRawData(rawDataPtr, len);
+ }
+
+ struct SimpleData {
+ uchar type;
+ } simpleData;
+ QByteArray fontName;
+};
+
+struct QWSScreenTransformationEvent : QWSEvent {
+ QWSScreenTransformationEvent()
+ : QWSEvent(QWSEvent::ScreenTransformation, sizeof(simpleData),
+ reinterpret_cast<char*>(&simpleData)) {}
+
+ struct SimpleData {
+ int screen;
+ int transformation;
+ } simpleData;
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QWSEVENT_QWS_H
diff --git a/src/gui/embedded/qwslock.cpp b/src/gui/embedded/qwslock.cpp
new file mode 100644
index 0000000000..5fb588c6d7
--- /dev/null
+++ b/src/gui/embedded/qwslock.cpp
@@ -0,0 +1,243 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qwslock_p.h"
+
+#ifndef QT_NO_QWS_MULTIPROCESS
+
+#include "qwssignalhandler_p.h"
+
+#include <qglobal.h>
+#include <qdebug.h>
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/ipc.h>
+#include <sys/sem.h>
+#include <sys/time.h>
+#include <time.h>
+#ifdef Q_OS_LINUX
+#include <linux/version.h>
+#endif
+#include <unistd.h>
+
+QT_BEGIN_NAMESPACE
+
+#ifdef QT_NO_SEMAPHORE
+#error QWSLock currently requires semaphores
+#endif
+
+#ifndef Q_OS_BSD4
+union semun {
+ int val;
+ struct semid_ds *buf;
+ unsigned short *array;
+ struct seminfo *__buf;
+};
+#endif
+
+QWSLock::QWSLock()
+{
+ semId = semget(IPC_PRIVATE, 3, IPC_CREAT | 0666);
+
+ if (semId == -1) {
+ perror("QWSLock::QWSLock");
+ qFatal("Unable to create semaphore");
+ }
+ QWSSignalHandler::instance()->addSemaphore(semId);
+
+ semun semval;
+ semval.val = 1;
+
+ if (semctl(semId, BackingStore, SETVAL, semval) == -1) {
+ perror("QWSLock::QWSLock");
+ qFatal("Unable to initialize backingstore semaphore");
+ }
+ lockCount[BackingStore] = 0;
+
+ if (semctl(semId, Communication, SETVAL, semval) == -1) {
+ perror("QWSLock::QWSLock");
+ qFatal("Unable to initialize communication semaphore");
+ }
+ lockCount[Communication] = 0;
+
+ semval.val = 0;
+ if (semctl(semId, RegionEvent, SETVAL, semval) == -1) {
+ perror("QWSLock::QWSLock");
+ qFatal("Unable to initialize region event semaphore");
+ }
+}
+
+QWSLock::QWSLock(int id)
+{
+ semId = id;
+ QWSSignalHandler::instance()->addSemaphore(semId);
+ lockCount[0] = lockCount[1] = 0;
+}
+
+QWSLock::~QWSLock()
+{
+ if (semId == -1)
+ return;
+ QWSSignalHandler::instance()->removeSemaphore(semId);
+}
+
+static bool forceLock(int semId, int semNum, int)
+{
+ int ret;
+ do {
+ sembuf sops = { semNum, -1, 0 };
+
+ // As the BackingStore lock is a mutex, and only one process may own
+ // the lock, it's safe to use SEM_UNDO. On the other hand, the
+ // Communication lock is locked by the client but unlocked by the
+ // server and therefore can't use SEM_UNDO.
+ if (semNum == QWSLock::BackingStore)
+ sops.sem_flg |= SEM_UNDO;
+
+ ret = semop(semId, &sops, 1);
+ if (ret == -1 && errno != EINTR)
+ qDebug("QWSLock::lock: %s", strerror(errno));
+ } while (ret == -1 && errno == EINTR);
+
+ return (ret != -1);
+}
+
+static bool up(int semId, int semNum)
+{
+ int ret;
+ do {
+ sembuf sops = { semNum, 1, 0 };
+ ret = semop(semId, &sops, 1);
+ if (ret == -1 && errno != EINTR)
+ qDebug("QWSLock::up: %s", strerror(errno));
+ } while (ret == -1 && errno == EINTR);
+
+ return (ret != -1);
+}
+
+static bool down(int semId, int semNum)
+{
+ int ret;
+ do {
+ sembuf sops = { semNum, -1, 0 };
+ ret = semop(semId, &sops, 1);
+ if (ret == -1 && errno != EINTR)
+ qDebug("QWSLock::down: %s", strerror(errno));
+ } while (ret == -1 && errno == EINTR);
+
+ return (ret != -1);
+}
+
+static int getValue(int semId, int semNum)
+{
+ int ret;
+ do {
+ ret = semctl(semId, semNum, GETVAL, 0);
+ if (ret == -1 && errno != EINTR)
+ qDebug("QWSLock::getValue: %s", strerror(errno));
+ } while (ret == -1 && errno == EINTR);
+
+ return ret;
+}
+
+bool QWSLock::lock(LockType type, int timeout)
+{
+ if (type == RegionEvent)
+ return up(semId, RegionEvent);
+
+ if (hasLock(type)) {
+ ++lockCount[type];
+ return true;
+ }
+
+ if (!forceLock(semId, type, timeout))
+ return false;
+ ++lockCount[type];
+ return true;
+}
+
+bool QWSLock::hasLock(LockType type)
+{
+ if (type == RegionEvent)
+ return (getValue(semId, RegionEvent) == 0);
+
+ return (lockCount[type] > 0);
+}
+
+void QWSLock::unlock(LockType type)
+{
+ if (type == RegionEvent) {
+ down(semId, RegionEvent);
+ return;
+ }
+
+ if (hasLock(type)) {
+ --lockCount[type];
+ if (hasLock(type))
+ return;
+ }
+
+ const int semNum = type;
+ int ret;
+ do {
+ sembuf sops = {semNum, 1, 0};
+ if (semNum == QWSLock::BackingStore)
+ sops.sem_flg |= SEM_UNDO;
+
+ ret = semop(semId, &sops, 1);
+ if (ret == -1 && errno != EINTR)
+ qDebug("QWSLock::unlock: %s", strerror(errno));
+ } while (ret == -1 && errno == EINTR);
+}
+
+bool QWSLock::wait(LockType type, int timeout)
+{
+ bool ok = forceLock(semId, type, timeout);
+ if (ok)
+ unlock(type);
+ return ok;
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_QWS_MULTIPROCESS
diff --git a/src/gui/embedded/qwslock_p.h b/src/gui/embedded/qwslock_p.h
new file mode 100644
index 0000000000..4dc873248e
--- /dev/null
+++ b/src/gui/embedded/qwslock_p.h
@@ -0,0 +1,85 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QWSLOCK_P_H
+#define QWSLOCK_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. This header file may
+// change from version to version without notice, or even be
+// removed.
+//
+// We mean it.
+//
+
+#include <qglobal.h>
+
+QT_BEGIN_NAMESPACE
+
+#ifndef QT_NO_QWS_MULTIPROCESS
+
+class QWSLock
+{
+public:
+ enum LockType { BackingStore, Communication, RegionEvent };
+
+ QWSLock();
+ QWSLock(int lockId);
+ ~QWSLock();
+
+ bool lock(LockType type, int timeout = -1);
+ void unlock(LockType type);
+ bool wait(LockType type, int timeout = -1);
+ bool hasLock(LockType type);
+ int id() const { return semId; }
+
+private:
+ int semId;
+ int lockCount[2];
+};
+
+
+QT_END_NAMESPACE
+#endif // QT_NO_QWS_MULTIPROCESS
+#endif // QWSLOCK_P_H
diff --git a/src/gui/embedded/qwsmanager_p.h b/src/gui/embedded/qwsmanager_p.h
new file mode 100644
index 0000000000..a891a75484
--- /dev/null
+++ b/src/gui/embedded/qwsmanager_p.h
@@ -0,0 +1,122 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QWSMANAGER_P_H
+#define QWSMANAGER_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of the QLibrary class. This header file may change from
+// version to version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "QtGui/qregion.h"
+#include "QtGui/qdecoration_qws.h"
+
+#ifndef QT_NO_QWS_MANAGER
+
+#include "QtCore/qhash.h"
+
+QT_BEGIN_NAMESPACE
+
+class QWidget;
+class QMenu;
+
+class QWSManagerPrivate : public QObjectPrivate
+{
+ Q_DECLARE_PUBLIC(QWSManager)
+public:
+ QWSManagerPrivate();
+
+ int activeRegion;
+ QWidget *managed;
+ QMenu *popup;
+
+ enum MenuAction {
+ NormalizeAction,
+ TitleAction,
+ BottomRightAction,
+ MinimizeAction,
+ MaximizeAction,
+ CloseAction,
+ LastMenuAction
+ };
+ QAction *menuActions[LastMenuAction];
+
+ static QWidget *active;
+ static QPoint mousePos;
+
+ // Region caching to avoid getting a regiontype's
+ // QRegion for each mouse move event
+ int previousRegionType;
+ bool previousRegionRepainted; // Hover/Press handled
+ bool entireDecorationNeedsRepaint;
+ struct RegionCaching {
+ int regionType;
+ QRegion region;
+ Qt::WindowFlags windowFlags;
+ QRect windowGeometry;
+ } cached_region;
+
+ bool newCachedRegion(const QPoint &pos);
+ int cachedRegionAt()
+ { return cached_region.regionType; }
+
+ void dirtyRegion(int decorationRegion,
+ QDecoration::DecorationState state,
+ const QRegion &clip = QRegion());
+ void clearDirtyRegions();
+
+ QList<int> dirtyRegions;
+ QList<QDecoration::DecorationState> dirtyStates;
+ QRegion dirtyClip;
+};
+
+#endif // QT_NO_QWS_MANAGER
+
+QT_END_NAMESPACE
+
+#endif // QWSMANAGER_P_H
diff --git a/src/gui/embedded/qwsmanager_qws.cpp b/src/gui/embedded/qwsmanager_qws.cpp
new file mode 100644
index 0000000000..96abf3fdcb
--- /dev/null
+++ b/src/gui/embedded/qwsmanager_qws.cpp
@@ -0,0 +1,535 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qwsmanager_qws.h"
+
+#ifndef QT_NO_QWS_MANAGER
+
+#include "qdrawutil.h"
+#include "qapplication.h"
+#include "qstyle.h"
+#include "qwidget.h"
+#include "qmenu.h"
+#include "qpainter.h"
+#include "private/qpainter_p.h"
+#include "qregion.h"
+#include "qevent.h"
+#include "qcursor.h"
+#include "qwsdisplay_qws.h"
+#include "qdesktopwidget.h"
+
+#include <private/qapplication_p.h>
+#include <private/qwidget_p.h>
+#include <private/qbackingstore_p.h>
+#include <private/qwindowsurface_qws_p.h>
+#include "qdecorationfactory_qws.h"
+
+#include "qlayout.h"
+
+#include "qwsmanager_p.h"
+
+#include <qdebug.h>
+
+QT_BEGIN_NAMESPACE
+
+QWidget *QWSManagerPrivate::active = 0;
+QPoint QWSManagerPrivate::mousePos;
+
+
+QWSManagerPrivate::QWSManagerPrivate()
+ : QObjectPrivate(), activeRegion(QDecoration::None), managed(0), popup(0),
+ previousRegionType(0), previousRegionRepainted(false), entireDecorationNeedsRepaint(false)
+{
+ cached_region.regionType = 0;
+}
+
+QRegion &QWSManager::cachedRegion()
+{
+ return d_func()->cached_region.region;
+}
+
+/*!
+ \class QWSManager
+ \ingroup qws
+ \internal
+*/
+
+/*!
+
+*/
+QWSManager::QWSManager(QWidget *w)
+ : QObject(*new QWSManagerPrivate, (QObject*)0)
+{
+ d_func()->managed = w;
+
+}
+
+QWSManager::~QWSManager()
+{
+ Q_D(QWSManager);
+#ifndef QT_NO_MENU
+ if (d->popup)
+ delete d->popup;
+#endif
+ if (d->managed == QWSManagerPrivate::active)
+ QWSManagerPrivate::active = 0;
+}
+
+QWidget *QWSManager::widget()
+{
+ Q_D(QWSManager);
+ return d->managed;
+}
+
+QWidget *QWSManager::grabbedMouse()
+{
+ return QWSManagerPrivate::active;
+}
+
+QRegion QWSManager::region()
+{
+ Q_D(QWSManager);
+ return QApplication::qwsDecoration().region(d->managed, d->managed->geometry());
+}
+
+bool QWSManager::event(QEvent *e)
+{
+ if (QObject::event(e))
+ return true;
+
+ switch (e->type()) {
+ case QEvent::MouseMove:
+ mouseMoveEvent((QMouseEvent*)e);
+ break;
+
+ case QEvent::MouseButtonPress:
+ mousePressEvent((QMouseEvent*)e);
+ break;
+
+ case QEvent::MouseButtonRelease:
+ mouseReleaseEvent((QMouseEvent*)e);
+ break;
+
+ case QEvent::MouseButtonDblClick:
+ mouseDoubleClickEvent((QMouseEvent*)e);
+ break;
+
+ case QEvent::Paint:
+ paintEvent((QPaintEvent*)e);
+ break;
+
+ default:
+ return false;
+ break;
+ }
+
+ return true;
+}
+
+void QWSManager::mousePressEvent(QMouseEvent *e)
+{
+ Q_D(QWSManager);
+ d->mousePos = e->globalPos();
+ d->activeRegion = QApplication::qwsDecoration().regionAt(d->managed, d->mousePos);
+ if(d->cached_region.regionType)
+ d->previousRegionRepainted |= repaintRegion(d->cached_region.regionType, QDecoration::Pressed);
+
+ if (d->activeRegion == QDecoration::Menu) {
+ QPoint pos = (QApplication::layoutDirection() == Qt::LeftToRight
+ ? d->managed->geometry().topLeft()
+ : d->managed->geometry().topRight());
+ menu(pos);
+ }
+ if (d->activeRegion != QDecoration::None &&
+ d->activeRegion != QDecoration::Menu) {
+ d->active = d->managed;
+ d->managed->grabMouse();
+ }
+ if (d->activeRegion != QDecoration::None &&
+ d->activeRegion != QDecoration::Close &&
+ d->activeRegion != QDecoration::Minimize &&
+ d->activeRegion != QDecoration::Menu) {
+ d->managed->raise();
+ }
+
+ if (e->button() == Qt::RightButton) {
+ menu(e->globalPos());
+ }
+}
+
+void QWSManager::mouseReleaseEvent(QMouseEvent *e)
+{
+ Q_D(QWSManager);
+ d->managed->releaseMouse();
+ if (d->cached_region.regionType && d->previousRegionRepainted && QApplication::mouseButtons() == 0) {
+ bool doesHover = repaintRegion(d->cached_region.regionType, QDecoration::Hover);
+ if (!doesHover) {
+ repaintRegion(d->cached_region.regionType, QDecoration::Normal);
+ d->previousRegionRepainted = false;
+ }
+ }
+
+ if (e->button() == Qt::LeftButton) {
+ //handleMove();
+ int itm = QApplication::qwsDecoration().regionAt(d->managed, e->globalPos());
+ int activatedItem = d->activeRegion;
+ d->activeRegion = QDecoration::None;
+ d->active = 0;
+ if (activatedItem == itm)
+ QApplication::qwsDecoration().regionClicked(d->managed, itm);
+ } else if (d->activeRegion == QDecoration::None) {
+ d->active = 0;
+ }
+}
+
+void QWSManager::mouseDoubleClickEvent(QMouseEvent *e)
+{
+ Q_D(QWSManager);
+ if (e->button() == Qt::LeftButton)
+ QApplication::qwsDecoration().regionDoubleClicked(d->managed,
+ QApplication::qwsDecoration().regionAt(d->managed, e->globalPos()));
+}
+
+static inline Qt::CursorShape regionToShape(int region)
+{
+ if (region == QDecoration::None)
+ return Qt::ArrowCursor;
+
+ static const struct {
+ int region;
+ Qt::CursorShape shape;
+ } r2s[] = {
+ { QDecoration::TopLeft, Qt::SizeFDiagCursor },
+ { QDecoration::Top, Qt::SizeVerCursor},
+ { QDecoration::TopRight, Qt::SizeBDiagCursor},
+ { QDecoration::Left, Qt::SizeHorCursor},
+ { QDecoration::Right, Qt::SizeHorCursor},
+ { QDecoration::BottomLeft, Qt::SizeBDiagCursor},
+ { QDecoration::Bottom, Qt::SizeVerCursor},
+ { QDecoration::BottomRight, Qt::SizeFDiagCursor},
+ { QDecoration::None, Qt::ArrowCursor}
+ };
+
+ int i = 0;
+ while (region != r2s[i].region && r2s[i].region)
+ ++i;
+ return r2s[i].shape;
+}
+
+void QWSManager::mouseMoveEvent(QMouseEvent *e)
+{
+ Q_D(QWSManager);
+ if (d->newCachedRegion(e->globalPos())) {
+ if(d->previousRegionType && d->previousRegionRepainted)
+ repaintRegion(d->previousRegionType, QDecoration::Normal);
+ if(d->cached_region.regionType) {
+ d->previousRegionRepainted = repaintRegion(d->cached_region.regionType, QDecoration::Hover);
+ }
+ }
+
+
+#ifndef QT_NO_CURSOR
+ QWSDisplay *qwsd = QApplication::desktop()->qwsDisplay();
+ qwsd->selectCursor(d->managed, regionToShape(d->cachedRegionAt()));
+#endif //QT_NO_CURSOR
+
+ if (d->activeRegion)
+ handleMove(e->globalPos());
+}
+
+void QWSManager::handleMove(QPoint g)
+{
+ Q_D(QWSManager);
+
+ // don't allow dragging to where the user probably cannot click!
+ QApplicationPrivate *ap = QApplicationPrivate::instance();
+ const QRect maxWindowRect = ap->maxWindowRect(qt_screen);
+ if (maxWindowRect.isValid()) {
+ if (g.x() < maxWindowRect.x())
+ g.setX(maxWindowRect.x());
+ if (g.y() < maxWindowRect.y())
+ g.setY(maxWindowRect.y());
+ if (g.x() > maxWindowRect.right())
+ g.setX(maxWindowRect.right());
+ if (g.y() > maxWindowRect.bottom())
+ g.setY(maxWindowRect.bottom());
+ }
+
+ if (g == d->mousePos)
+ return;
+
+ if ( d->managed->isMaximized() )
+ return;
+
+ int x = d->managed->geometry().x();
+ int y = d->managed->geometry().y();
+ int w = d->managed->width();
+ int h = d->managed->height();
+
+ QRect geom(d->managed->geometry());
+
+ QPoint delta = g - d->mousePos;
+ d->mousePos = g;
+
+ if (d->activeRegion == QDecoration::Title) {
+ geom = QRect(x + delta.x(), y + delta.y(), w, h);
+ } else {
+ bool keepTop = true;
+ bool keepLeft = true;
+ switch (d->activeRegion) {
+ case QDecoration::Top:
+ geom.setTop(geom.top() + delta.y());
+ keepTop = false;
+ break;
+ case QDecoration::Bottom:
+ geom.setBottom(geom.bottom() + delta.y());
+ keepTop = true;
+ break;
+ case QDecoration::Left:
+ geom.setLeft(geom.left() + delta.x());
+ keepLeft = false;
+ break;
+ case QDecoration::Right:
+ geom.setRight(geom.right() + delta.x());
+ keepLeft = true;
+ break;
+ case QDecoration::TopRight:
+ geom.setTopRight(geom.topRight() + delta);
+ keepLeft = true;
+ keepTop = false;
+ break;
+ case QDecoration::TopLeft:
+ geom.setTopLeft(geom.topLeft() + delta);
+ keepLeft = false;
+ keepTop = false;
+ break;
+ case QDecoration::BottomLeft:
+ geom.setBottomLeft(geom.bottomLeft() + delta);
+ keepLeft = false;
+ keepTop = true;
+ break;
+ case QDecoration::BottomRight:
+ geom.setBottomRight(geom.bottomRight() + delta);
+ keepLeft = true;
+ keepTop = true;
+ break;
+ default:
+ return;
+ }
+
+ QSize newSize = QLayout::closestAcceptableSize(d->managed, geom.size());
+
+ int dx = newSize.width() - geom.width();
+ int dy = newSize.height() - geom.height();
+
+ if (keepTop) {
+ geom.setBottom(geom.bottom() + dy);
+ d->mousePos.ry() += dy;
+ } else {
+ geom.setTop(geom.top() - dy);
+ d->mousePos.ry() -= dy;
+ }
+ if (keepLeft) {
+ geom.setRight(geom.right() + dx);
+ d->mousePos.rx() += dx;
+ } else {
+ geom.setLeft(geom.left() - dx);
+ d->mousePos.rx() -= dx;
+ }
+ }
+ if (geom != d->managed->geometry()) {
+ QApplication::sendPostedEvents();
+ d->managed->setGeometry(geom);
+ }
+}
+
+void QWSManager::paintEvent(QPaintEvent *)
+{
+ Q_D(QWSManager);
+ d->dirtyRegion(QDecoration::All, QDecoration::Normal);
+}
+
+void QWSManagerPrivate::dirtyRegion(int decorationRegion,
+ QDecoration::DecorationState state,
+ const QRegion &clip)
+{
+ QTLWExtra *topextra = managed->d_func()->extra->topextra;
+ QWidgetBackingStore *bs = topextra->backingStore;
+ const bool pendingUpdateRequest = bs->isDirty();
+
+ if (decorationRegion == QDecoration::All) {
+ if (clip.isEmpty())
+ entireDecorationNeedsRepaint = true;
+ dirtyRegions.clear();
+ dirtyStates.clear();
+ }
+ int i = dirtyRegions.indexOf(decorationRegion);
+ if (i >= 0) {
+ dirtyRegions.removeAt(i);
+ dirtyStates.removeAt(i);
+ }
+
+ dirtyRegions.append(decorationRegion);
+ dirtyStates.append(state);
+ if (!entireDecorationNeedsRepaint)
+ dirtyClip += clip;
+
+ if (!pendingUpdateRequest)
+ QApplication::postEvent(managed, new QEvent(QEvent::UpdateRequest), Qt::LowEventPriority);
+}
+
+void QWSManagerPrivate::clearDirtyRegions()
+{
+ dirtyRegions.clear();
+ dirtyStates.clear();
+ dirtyClip = QRegion();
+ entireDecorationNeedsRepaint = false;
+}
+
+bool QWSManager::repaintRegion(int decorationRegion, QDecoration::DecorationState state)
+{
+ Q_D(QWSManager);
+
+ d->dirtyRegion(decorationRegion, state);
+ return true;
+}
+
+void QWSManager::menu(const QPoint &pos)
+{
+#ifdef QT_NO_MENU
+ Q_UNUSED(pos);
+#else
+ Q_D(QWSManager);
+ if (d->popup)
+ delete d->popup;
+
+ // Basic window operation menu
+ d->popup = new QMenu();
+ QApplication::qwsDecoration().buildSysMenu(d->managed, d->popup);
+ connect(d->popup, SIGNAL(triggered(QAction*)), SLOT(menuTriggered(QAction*)));
+
+ d->popup->popup(pos);
+ d->activeRegion = QDecoration::None;
+#endif // QT_NO_MENU
+}
+
+void QWSManager::menuTriggered(QAction *action)
+{
+#ifdef QT_NO_MENU
+ Q_UNUSED(action);
+#else
+ Q_D(QWSManager);
+ QApplication::qwsDecoration().menuTriggered(d->managed, action);
+ d->popup->deleteLater();
+ d->popup = 0;
+#endif
+}
+
+void QWSManager::startMove()
+{
+ Q_D(QWSManager);
+ d->mousePos = QCursor::pos();
+ d->activeRegion = QDecoration::Title;
+ d->active = d->managed;
+ d->managed->grabMouse();
+}
+
+void QWSManager::startResize()
+{
+ Q_D(QWSManager);
+ d->activeRegion = QDecoration::BottomRight;
+ d->active = d->managed;
+ d->managed->grabMouse();
+}
+
+void QWSManager::maximize()
+{
+ Q_D(QWSManager);
+ // find out how much space the decoration needs
+ const int screen = QApplication::desktop()->screenNumber(d->managed);
+ const QRect desk = QApplication::desktop()->availableGeometry(screen);
+ QRect dummy(0, 0, 1, 1);
+ QRect nr;
+ QRegion r = QApplication::qwsDecoration().region(d->managed, dummy);
+ if (r.isEmpty()) {
+ nr = desk;
+ } else {
+ r += dummy; // make sure we get the full window region in case of 0 width borders
+ QRect rect = r.boundingRect();
+ nr = QRect(desk.x()-rect.x(), desk.y()-rect.y(),
+ desk.width() - (rect.width()==1 ? 0 : rect.width()-1), // ==1 -> dummy
+ desk.height() - (rect.height()==1 ? 0 : rect.height()-1));
+ }
+ d->managed->setGeometry(nr);
+}
+
+bool QWSManagerPrivate::newCachedRegion(const QPoint &pos)
+{
+ // Check if anything has changed that would affect the region caching
+ if (managed->windowFlags() == cached_region.windowFlags
+ && managed->geometry() == cached_region.windowGeometry
+ && cached_region.region.contains(pos))
+ return false;
+
+ // Update the cached region
+ int reg = QApplication::qwsDecoration().regionAt(managed, pos);
+ if (QWidget::mouseGrabber())
+ reg = QDecoration::None;
+
+ previousRegionType = cached_region.regionType;
+ cached_region.regionType = reg;
+ cached_region.region = QApplication::qwsDecoration().region(managed, managed->geometry(),
+ reg);
+ // Make room for borders around the widget, even if the decoration doesn't have a frame.
+ if (reg && !(reg & int(QDecoration::Borders))) {
+ cached_region.region -= QApplication::qwsDecoration().region(managed, managed->geometry(), QDecoration::Borders);
+ }
+ cached_region.windowFlags = managed->windowFlags();
+ cached_region.windowGeometry = managed->geometry();
+// QRect rec = cached_region.region.boundingRect();
+// qDebug("Updated cached region: 0x%04x (%d, %d) (%d, %d, %d, %d)",
+// reg, pos.x(), pos.y(), rec.x(), rec.y(), rec.right(), rec.bottom());
+ return true;
+}
+
+QT_END_NAMESPACE
+
+#endif //QT_NO_QWS_MANAGER
diff --git a/src/gui/embedded/qwsmanager_qws.h b/src/gui/embedded/qwsmanager_qws.h
new file mode 100644
index 0000000000..d342ae085a
--- /dev/null
+++ b/src/gui/embedded/qwsmanager_qws.h
@@ -0,0 +1,122 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QWSMANAGER_QWS_H
+#define QWSMANAGER_QWS_H
+
+#include <QtGui/qpixmap.h>
+#include <QtCore/qobject.h>
+#include <QtGui/qdecoration_qws.h>
+#include <QtGui/qevent.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+#ifndef QT_NO_QWS_MANAGER
+
+class QAction;
+class QPixmap;
+class QWidget;
+class QPopupMenu;
+class QRegion;
+class QMouseEvent;
+class QWSManagerPrivate;
+
+class Q_GUI_EXPORT QWSManager : public QObject
+{
+ Q_OBJECT
+ Q_DECLARE_PRIVATE(QWSManager)
+public:
+ explicit QWSManager(QWidget *);
+ ~QWSManager();
+
+ static QDecoration *newDefaultDecoration();
+
+ QWidget *widget();
+ static QWidget *grabbedMouse();
+ void maximize();
+ void startMove();
+ void startResize();
+
+ QRegion region();
+ QRegion &cachedRegion();
+
+protected Q_SLOTS:
+ void menuTriggered(QAction *action);
+
+protected:
+ void handleMove(QPoint g);
+
+ virtual bool event(QEvent *e);
+ virtual void mouseMoveEvent(QMouseEvent *);
+ virtual void mousePressEvent(QMouseEvent *);
+ virtual void mouseReleaseEvent(QMouseEvent *);
+ virtual void mouseDoubleClickEvent(QMouseEvent *);
+ virtual void paintEvent(QPaintEvent *);
+ bool repaintRegion(int region, QDecoration::DecorationState state);
+
+ void menu(const QPoint &);
+
+private:
+ friend class QWidget;
+ friend class QETWidget;
+ friend class QWidgetPrivate;
+ friend class QApplication;
+ friend class QApplicationPrivate;
+ friend class QWidgetBackingStore;
+ friend class QWSWindowSurface;
+ friend class QGLDrawable;
+};
+
+QT_BEGIN_INCLUDE_NAMESPACE
+#include <QtGui/qdecorationdefault_qws.h>
+QT_END_INCLUDE_NAMESPACE
+
+#endif // QT_NO_QWS_MANAGER
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QWSMANAGER_QWS_H
diff --git a/src/gui/embedded/qwsproperty_qws.cpp b/src/gui/embedded/qwsproperty_qws.cpp
new file mode 100644
index 0000000000..1c44506101
--- /dev/null
+++ b/src/gui/embedded/qwsproperty_qws.cpp
@@ -0,0 +1,145 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qwsproperty_qws.h"
+
+#ifndef QT_NO_QWS_PROPERTIES
+#include "qwscommand_qws_p.h"
+#include "qwindowsystem_qws.h"
+#include "qhash.h"
+#include "qalgorithms.h"
+#include "qbytearray.h"
+
+#include <stdio.h>
+
+QT_BEGIN_NAMESPACE
+
+class QWSPropertyManager::Data {
+public:
+ QByteArray find(int winId, int property)
+ {
+ return properties.value(winId).value(property);
+ }
+
+ typedef QHash<int, QHash<int, QByteArray> > PropertyHash;
+ PropertyHash properties;
+};
+
+/*********************************************************************
+ *
+ * Class: QWSPropertyManager
+ *
+ *********************************************************************/
+
+QWSPropertyManager::QWSPropertyManager()
+{
+ d = new Data;
+}
+
+QWSPropertyManager::~QWSPropertyManager()
+{
+ delete d;
+}
+
+bool QWSPropertyManager::setProperty(int winId, int property, int mode, const char *data, int len)
+{
+ QHash<int, QByteArray> props = d->properties.value(winId);
+ QHash<int, QByteArray>::iterator it = props.find(property);
+ if (it == props.end())
+ return false;
+
+ switch (mode) {
+ case PropReplace:
+ d->properties[winId][property] = QByteArray(data, len);
+ break;
+ case PropAppend:
+ d->properties[winId][property].append(data);
+ break;
+ case PropPrepend:
+ d->properties[winId][property].prepend(data);
+ break;
+ }
+ return true;
+}
+
+bool QWSPropertyManager::hasProperty(int winId, int property)
+{
+ return d->properties.value(winId).contains(property);
+}
+
+bool QWSPropertyManager::removeProperty(int winId, int property)
+{
+ QWSPropertyManager::Data::PropertyHash::iterator it = d->properties.find(winId);
+ if (it == d->properties.end())
+ return false;
+ return d->properties[winId].remove( property );
+}
+
+bool QWSPropertyManager::addProperty(int winId, int property)
+{
+ if( !d->properties[winId].contains(property) )
+ d->properties[winId][property] = QByteArray(); // only add if it doesn't exist
+ return true;
+}
+
+bool QWSPropertyManager::getProperty(int winId, int property, const char *&data, int &len)
+{
+ QHash<int, QByteArray> props = d->properties.value(winId);
+ QHash<int, QByteArray>::iterator it = props.find(property);
+ if (it == props.end()) {
+ data = 0;
+ len = -1;
+ return false;
+ }
+ data = it.value().constData();
+ len = it.value().length();
+
+ return true;
+}
+
+bool QWSPropertyManager::removeProperties(int winId)
+{
+ return d->properties.remove(winId);
+}
+
+QT_END_NAMESPACE
+
+#endif //QT_NO_QWS_PROPERTIES
diff --git a/src/gui/embedded/qwsproperty_qws.h b/src/gui/embedded/qwsproperty_qws.h
new file mode 100644
index 0000000000..27f0c65a88
--- /dev/null
+++ b/src/gui/embedded/qwsproperty_qws.h
@@ -0,0 +1,96 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QWSPROPERTY_QWS_H
+#define QWSPROPERTY_QWS_H
+
+#include <QtCore/qglobal.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+/*********************************************************************
+ *
+ * Class: QWSPropertyManager
+ *
+ *********************************************************************/
+
+#ifndef QT_NO_QWS_PROPERTIES
+
+class QWSPropertyManager
+{
+public:
+ enum Mode {
+ PropReplace = 0,
+ PropPrepend,
+ PropAppend
+ };
+
+ // pre-defined properties
+ enum Atom {
+ PropSelection = 0
+ };
+
+ QWSPropertyManager();
+ ~QWSPropertyManager();
+
+ bool setProperty(int winId, int property, int mode, const char *data, int len);
+ bool hasProperty(int winId, int property);
+ bool removeProperty(int winId, int property);
+ bool addProperty(int winId, int property);
+ bool getProperty(int winId, int property, const char *&data, int &len);
+ bool removeProperties(int winId);
+
+private:
+ class Data;
+ Data* d;
+};
+
+#endif // QT_NO_QWS_PROPERTIES
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QWSPROPERTY_QWS_H
diff --git a/src/gui/embedded/qwsprotocolitem_qws.h b/src/gui/embedded/qwsprotocolitem_qws.h
new file mode 100644
index 0000000000..71f70ddf09
--- /dev/null
+++ b/src/gui/embedded/qwsprotocolitem_qws.h
@@ -0,0 +1,100 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QWSPROTOCOLITEM_QWS_H
+#define QWSPROTOCOLITEM_QWS_H
+
+/*********************************************************************
+ *
+ * QWSCommand base class - only use derived classes from that
+ *
+ *********************************************************************/
+
+#include <QtCore/qglobal.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+class QIODevice;
+
+struct QWSProtocolItem
+{
+ // ctor - dtor
+ QWSProtocolItem(int t, int len, char *ptr) : type(t),
+ simpleLen(len), rawLen(-1), deleteRaw(false), simpleDataPtr(ptr),
+ rawDataPtr(0), bytesRead(0) { }
+ virtual ~QWSProtocolItem();
+
+ // data
+ int type;
+ int simpleLen;
+ int rawLen;
+ bool deleteRaw;
+
+ // functions
+#ifndef QT_NO_QWS_MULTIPROCESS
+ void write(QIODevice *s);
+ bool read(QIODevice *s);
+#endif
+ void copyFrom(const QWSProtocolItem *item);
+
+ virtual void setData(const char *data, int len, bool allocateMem = true);
+
+ char *simpleDataPtr;
+ char *rawDataPtr;
+ // temp variables
+ int bytesRead;
+};
+
+// This should probably be a method on QWSProtocolItem, but this way avoids
+// changing the API of this apparently public header
+// size = (int)type + (int)rawLenSize + simpleLen + rawLen
+#define QWS_PROTOCOL_ITEM_SIZE( item ) \
+ (2 * sizeof(int)) + ((item).simpleDataPtr ? (item).simpleLen : 0) + ((item).rawDataPtr ? (item).rawLen : 0)
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QWSPROTOCOLITEM_QWS_H
diff --git a/src/gui/embedded/qwssharedmemory.cpp b/src/gui/embedded/qwssharedmemory.cpp
new file mode 100644
index 0000000000..8afbe9b5c7
--- /dev/null
+++ b/src/gui/embedded/qwssharedmemory.cpp
@@ -0,0 +1,185 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qwssharedmemory_p.h"
+
+#if !defined(QT_NO_QWS_MULTIPROCESS)
+
+#include <sys/shm.h>
+
+QT_BEGIN_NAMESPACE
+
+QWSSharedMemory::QWSSharedMemory()
+ : shmBase(0), shmSize(0), character(0), shmId(-1), key(-1)
+{
+}
+
+
+QWSSharedMemory::~QWSSharedMemory()
+{
+ detach();
+}
+
+/*
+ man page says:
+ On Linux, it is possible to attach a shared memory segment even if it
+ is already marked to be deleted. However, POSIX.1-2001 does not spec-
+ ify this behaviour and many other implementations do not support it.
+*/
+
+bool QWSSharedMemory::create(int size)
+{
+ if (shmId != -1)
+ detach();
+ shmId = shmget(IPC_PRIVATE, size, IPC_CREAT|0600);
+
+ if (shmId == -1) {
+#ifdef QT_SHM_DEBUG
+ perror("QWSSharedMemory::create allocating shared memory");
+ qWarning("Error allocating shared memory of size %d", size);
+#endif
+ return false;
+ }
+ shmBase = shmat(shmId,0,0);
+ shmctl(shmId, IPC_RMID, 0);
+ if (shmBase == (void*)-1) {
+#ifdef QT_SHM_DEBUG
+ perror("QWSSharedMemory::create attaching to shared memory");
+ qWarning("Error attaching to shared memory id %d", shmId);
+#endif
+ shmBase = 0;
+ return false;
+ }
+ return true;
+}
+
+bool QWSSharedMemory::attach(int id)
+{
+ if (shmId == id)
+ return id != -1;
+ if (shmId != -1)
+ detach();
+
+ shmBase = shmat(id,0,0);
+ if (shmBase == (void*)-1) {
+#ifdef QT_SHM_DEBUG
+ perror("QWSSharedMemory::attach attaching to shared memory");
+ qWarning("Error attaching to shared memory 0x%x of size %d",
+ id, size());
+#endif
+ shmBase = 0;
+ return false;
+ }
+ shmId = id;
+ return true;
+}
+
+
+void QWSSharedMemory::detach ()
+{
+ if (!shmBase)
+ return;
+ shmdt (shmBase);
+ shmBase = 0;
+ shmSize = 0;
+ shmId = -1;
+}
+
+void QWSSharedMemory::setPermissions (mode_t mode)
+{
+ struct shmid_ds shm;
+ shmctl (shmId, IPC_STAT, &shm);
+ shm.shm_perm.mode = mode;
+ shmctl (shmId, IPC_SET, &shm);
+}
+
+int QWSSharedMemory::size () const
+{
+ struct shmid_ds shm;
+ shmctl (shmId, IPC_STAT, &shm);
+ return shm.shm_segsz;
+}
+
+
+// old API
+
+
+
+QWSSharedMemory::QWSSharedMemory (int size, const QString &filename, char c)
+{
+ shmSize = size;
+ shmFile = filename;
+ shmBase = 0;
+ shmId = -1;
+ character = c;
+ key = ftok (shmFile.toLatin1().constData(), c);
+}
+
+
+
+bool QWSSharedMemory::create ()
+{
+ shmId = shmget (key, shmSize, IPC_CREAT | 0666);
+ return (shmId != -1);
+}
+
+void QWSSharedMemory::destroy ()
+{
+ if (shmId != -1)
+ shmctl(shmId, IPC_RMID, 0);
+}
+
+bool QWSSharedMemory::attach ()
+{
+ if (shmId == -1)
+ shmId = shmget (key, shmSize, 0);
+
+ shmBase = shmat (shmId, 0, 0);
+ if ((long)shmBase == -1)
+ shmBase = 0;
+
+ return (long)shmBase != 0;
+}
+
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_QWS_MULTIPROCESS
diff --git a/src/gui/embedded/qwssharedmemory_p.h b/src/gui/embedded/qwssharedmemory_p.h
new file mode 100644
index 0000000000..6b58605986
--- /dev/null
+++ b/src/gui/embedded/qwssharedmemory_p.h
@@ -0,0 +1,105 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QWSSHAREDMEMORY_P_H
+#define QWSSHAREDMEMORY_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 "qplatformdefs.h"
+#include "QtCore/qstring.h"
+
+QT_BEGIN_NAMESPACE
+
+#if !defined(QT_NO_QWS_MULTIPROCESS)
+
+class QWSSharedMemory {
+public:
+
+ QWSSharedMemory();
+ ~QWSSharedMemory();
+
+ void setPermissions(mode_t mode);
+ int size() const;
+ void *address() { return shmBase; };
+
+ int id() const { return shmId; }
+
+ void detach();
+
+ bool create(int size);
+ bool attach(int id);
+
+ //bool create(int size, const QString &filename, char c = 'Q');
+ //bool attach(const QString &filename, char c = 'Q');
+// old API
+
+ QWSSharedMemory(int, const QString &, char c = 'Q');
+ void * base() { return address(); };
+
+ bool create();
+ void destroy();
+
+ bool attach();
+
+private:
+ void *shmBase;
+ int shmSize;
+ QString shmFile;
+ char character;
+ int shmId;
+ key_t key;
+};
+
+#endif // QT_NO_QWS_MULTIPROCESS
+
+QT_END_NAMESPACE
+
+#endif // QWSSHAREDMEMORY_P_H
diff --git a/src/gui/embedded/qwssignalhandler.cpp b/src/gui/embedded/qwssignalhandler.cpp
new file mode 100644
index 0000000000..0946fb62bf
--- /dev/null
+++ b/src/gui/embedded/qwssignalhandler.cpp
@@ -0,0 +1,134 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qwssignalhandler_p.h"
+
+#ifndef QT_NO_QWS_SIGNALHANDLER
+
+#include <sys/types.h>
+#include <sys/ipc.h>
+#include <sys/sem.h>
+#include <signal.h>
+
+QT_BEGIN_NAMESPACE
+
+#ifndef Q_OS_BSD4
+union semun {
+ int val;
+ struct semid_ds *buf;
+ unsigned short *array;
+ struct seminfo *__buf;
+};
+#endif
+
+
+class QWSSignalHandlerPrivate : public QWSSignalHandler
+{
+public:
+ QWSSignalHandlerPrivate() : QWSSignalHandler() {}
+};
+
+
+Q_GLOBAL_STATIC(QWSSignalHandlerPrivate, signalHandlerInstance);
+
+
+QWSSignalHandler* QWSSignalHandler::instance()
+{
+ return signalHandlerInstance();
+}
+
+QWSSignalHandler::QWSSignalHandler()
+{
+ const int signums[] = { SIGHUP, SIGINT, SIGQUIT, SIGILL, SIGABRT, SIGFPE,
+ SIGSEGV, SIGTERM, SIGBUS };
+ const int n = sizeof(signums)/sizeof(int);
+
+ for (int i = 0; i < n; ++i) {
+ const int signum = signums[i];
+ qt_sighandler_t old = signal(signum, handleSignal);
+ if (old == SIG_IGN) // don't remove shm and semaphores when ignored
+ signal(signum, old);
+ else
+ oldHandlers[signum] = (old == SIG_ERR ? SIG_DFL : old);
+ }
+}
+
+QWSSignalHandler::~QWSSignalHandler()
+{
+#ifndef QT_NO_QWS_MULTIPROCESS
+ while (!semaphores.isEmpty())
+ removeSemaphore(semaphores.last());
+#endif
+}
+
+#ifndef QT_NO_QWS_MULTIPROCESS
+void QWSSignalHandler::removeSemaphore(int semno)
+{
+ const int index = semaphores.lastIndexOf(semno);
+ if (index != -1) {
+ semun semval;
+ semval.val = 0;
+ semctl(semaphores.at(index), 0, IPC_RMID, semval);
+ semaphores.remove(index);
+ }
+}
+#endif // QT_NO_QWS_MULTIPROCESS
+
+void QWSSignalHandler::handleSignal(int signum)
+{
+ QWSSignalHandler *h = instance();
+
+ signal(signum, h->oldHandlers[signum]);
+
+#ifndef QT_NO_QWS_MULTIPROCESS
+ semun semval;
+ semval.val = 0;
+ for (int i = 0; i < h->semaphores.size(); ++i)
+ semctl(h->semaphores.at(i), 0, IPC_RMID, semval);
+#endif
+
+ h->objects.clear();
+ raise(signum);
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_QWS_NO_SIGNALHANDLER
diff --git a/src/gui/embedded/qwssignalhandler_p.h b/src/gui/embedded/qwssignalhandler_p.h
new file mode 100644
index 0000000000..f2228e1461
--- /dev/null
+++ b/src/gui/embedded/qwssignalhandler_p.h
@@ -0,0 +1,99 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QWSSIGNALHANDLER_P_H
+#define QWSSIGNALHANDLER_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of the QLibrary class. This header file may change from
+// version to version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtCore/qglobal.h>
+
+#ifndef QT_NO_QWS_SIGNALHANDLER
+
+#include <QtCore/qmap.h>
+#include <QtCore/qvector.h>
+#include <QtCore/qobjectcleanuphandler.h>
+
+QT_BEGIN_NAMESPACE
+
+typedef void (*qt_sighandler_t)(int);
+
+class QWSSignalHandlerPrivate;
+
+class Q_GUI_EXPORT QWSSignalHandler
+{
+public:
+ static QWSSignalHandler* instance();
+
+ ~QWSSignalHandler();
+
+#ifndef QT_NO_QWS_MULTIPROCESS
+ inline void addSemaphore(int semno) { semaphores.append(semno); }
+ void removeSemaphore(int semno);
+#endif
+ inline void addObject(QObject *object) { (void)objects.add(object); }
+
+private:
+ QWSSignalHandler();
+ static void handleSignal(int signal);
+ QMap<int, qt_sighandler_t> oldHandlers;
+#ifndef QT_NO_QWS_MULTIPROCESS
+ QVector<int> semaphores;
+#endif
+ QObjectCleanupHandler objects;
+
+ friend class QWSSignalHandlerPrivate;
+};
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_QWS_SIGNALHANDLER
+
+#endif // QWSSIGNALHANDLER_P_H
diff --git a/src/gui/embedded/qwssocket_qws.cpp b/src/gui/embedded/qwssocket_qws.cpp
new file mode 100644
index 0000000000..bebd98e6b3
--- /dev/null
+++ b/src/gui/embedded/qwssocket_qws.cpp
@@ -0,0 +1,280 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qplatformdefs.h"
+#include "qwssocket_qws.h"
+
+#ifndef QT_NO_QWS_MULTIPROCESS
+
+#include <fcntl.h>
+#include <netdb.h>
+#include <errno.h>
+#include <stdio.h>
+#include <sys/file.h>
+#include <sys/time.h>
+#include <sys/un.h>
+
+#ifdef __MIPSEL__
+# ifndef SOCK_DGRAM
+# define SOCK_DGRAM 1
+# endif
+# ifndef SOCK_STREAM
+# define SOCK_STREAM 2
+# endif
+#endif
+
+#if defined(Q_OS_SOLARIS) || defined (QT_LINUXBASE)
+// uff-da apparently Solaris doesn't have the SUN_LEN macro, here is
+// an implementation of it...
+# ifndef SUN_LEN
+# define SUN_LEN(su) \
+ sizeof(*(su)) - sizeof((su)->sun_path) + strlen((su)->sun_path)
+# endif
+
+// nor the POSIX names of UNIX domain sockets *sigh*
+# ifndef AF_LOCAL
+# define AF_LOCAL AF_UNIX
+# endif
+# ifndef PF_LOCAL
+# define PF_LOCAL PF_UNIX
+# endif
+#endif // Q_OS_SOLARIS || QT_LINUXBASE
+
+QT_BEGIN_NAMESPACE
+
+/***********************************************************************
+ *
+ * QWSSocket
+ *
+ **********************************************************************/
+QWSSocket::QWSSocket(QObject *parent)
+ : QWS_SOCK_BASE(parent)
+{
+#ifndef QT_NO_SXE
+ QObject::connect( this, SIGNAL(stateChanged(SocketState)),
+ this, SLOT(forwardStateChange(SocketState)));
+#endif
+}
+
+QWSSocket::~QWSSocket()
+{
+}
+
+#ifndef QT_NO_SXE
+QString QWSSocket::errorString()
+{
+ switch (QUnixSocket::error()) {
+ case NoError:
+ return QString();
+ case InvalidPath:
+ case NonexistentPath:
+ return QLatin1String("Bad path"); // NO_TR
+ default:
+ return QLatin1String("Bad socket"); // NO TR
+ }
+}
+
+void QWSSocket::forwardStateChange(QUnixSocket::SocketState st )
+{
+ switch ( st )
+ {
+ case ConnectedState:
+ emit connected();
+ break;
+ case ClosingState:
+ break;
+ case UnconnectedState:
+ emit disconnected();
+ break;
+ default:
+ // nothing
+ break;
+ }
+ if ( QUnixSocket::error() != NoError )
+ emit error((QAbstractSocket::SocketError)0);
+}
+#endif
+
+bool QWSSocket::connectToLocalFile(const QString &file)
+{
+#ifndef QT_NO_SXE
+ bool result = QUnixSocket::connect( file.toLocal8Bit() );
+ if ( !result )
+ {
+ perror( "QWSSocketAuth::connectToLocalFile could not connect:" );
+ emit error(QAbstractSocket::ConnectionRefusedError);
+ return false;
+ }
+ return true;
+#else
+ // create socket
+ int s = ::socket(PF_LOCAL, SOCK_STREAM, 0);
+
+ // connect to socket
+ struct sockaddr_un a;
+ memset(&a, 0, sizeof(a));
+ a.sun_family = PF_LOCAL;
+ strncpy(a.sun_path, file.toLocal8Bit().constData(), sizeof(a.sun_path) - 1);
+ int r = ::connect(s, (struct sockaddr*)&a, SUN_LEN(&a));
+ if (r == 0) {
+ setSocketDescriptor(s);
+ } else {
+ perror("QWSSocket::connectToLocalFile could not connect:");
+ ::close(s);
+ emit error(ConnectionRefusedError);
+ return false;
+ }
+#endif
+ return true;
+}
+
+
+/***********************************************************************
+ *
+ * QWSServerSocket
+ *
+ **********************************************************************/
+QWSServerSocket::QWSServerSocket(const QString& file, QObject *parent)
+#ifndef QT_NO_SXE
+ : QUnixSocketServer(parent)
+#else
+ : QTcpServer(parent)
+#endif
+{
+ init(file);
+}
+
+void QWSServerSocket::init(const QString &file)
+{
+#ifndef QT_NO_SXE
+ QByteArray fn = file.toLocal8Bit();
+ bool result = QUnixSocketServer::listen( fn );
+ if ( !result )
+ {
+ QUnixSocketServer::ServerError err = serverError();
+ switch ( err )
+ {
+ case InvalidPath:
+ qWarning("QWSServerSocket:: invalid path %s", qPrintable(file));
+ break;
+ case ResourceError:
+ case BindError:
+ case ListenError:
+ qWarning("QWSServerSocket:: could not listen on path %s", qPrintable(file));
+ break;
+ default:
+ break;
+ }
+ }
+#else
+ int backlog = 16; //#####
+
+// create socket
+ int s = ::socket(PF_LOCAL, SOCK_STREAM, 0);
+ if (s == -1) {
+ perror("QWSServerSocket::init");
+ qWarning("QWSServerSocket: unable to create socket.");
+ return;
+ }
+
+ QByteArray fn = file.toLocal8Bit();
+ unlink(fn.constData()); // doesn't have to succeed
+
+ // bind socket
+ struct sockaddr_un a;
+ memset(&a, 0, sizeof(a));
+ a.sun_family = PF_LOCAL;
+ strncpy(a.sun_path, fn.constData(), sizeof(a.sun_path) - 1);
+ int r = ::bind(s, (struct sockaddr*)&a, SUN_LEN(&a));
+ if (r < 0) {
+ perror("QWSServerSocket::init");
+ qWarning("QWSServerSocket: could not bind to file %s", fn.constData());
+ ::close(s);
+ return;
+ }
+
+ if (chmod(fn.constData(), 0600) < 0) {
+ perror("QWSServerSocket::init");
+ qWarning("Could not set permissions of %s", fn.constData());
+ ::close(s);
+ return;
+ }
+
+ // listen
+ if (::listen(s, backlog) == 0) {
+ if (!setSocketDescriptor(s))
+ qWarning( "QWSServerSocket could not set descriptor %d : %s", s, errorString().toLatin1().constData());
+ } else {
+ perror("QWSServerSocket::init");
+ qWarning("QWSServerSocket: could not listen to file %s", fn.constData());
+ ::close(s);
+ }
+#endif
+}
+
+QWSServerSocket::~QWSServerSocket()
+{
+}
+
+#ifndef QT_NO_SXE
+
+void QWSServerSocket::incomingConnection(int socketDescriptor)
+{
+ inboundConnections.append( socketDescriptor );
+ emit newConnection();
+}
+
+
+QWSSocket *QWSServerSocket::nextPendingConnection()
+{
+ QMutexLocker locker( &ssmx );
+ if ( inboundConnections.count() == 0 )
+ return 0;
+ QWSSocket *s = new QWSSocket();
+ s->setSocketDescriptor( inboundConnections.takeFirst() );
+ return s;
+}
+
+#endif // QT_NO_SXE
+
+QT_END_NAMESPACE
+
+#endif //QT_NO_QWS_MULTIPROCESS
diff --git a/src/gui/embedded/qwssocket_qws.h b/src/gui/embedded/qwssocket_qws.h
new file mode 100644
index 0000000000..6b86d7c661
--- /dev/null
+++ b/src/gui/embedded/qwssocket_qws.h
@@ -0,0 +1,120 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QWSSOCKET_QWS_H
+#define QWSSOCKET_QWS_H
+
+#include <QtCore/qconfig.h>
+#include <QtGui/qwsutils_qws.h>
+
+#ifndef QT_NO_QWS_MULTIPROCESS
+
+#ifndef QT_NO_SXE
+#include <QtCore/qmutex.h>
+#include <QtGui/private/qunixsocketserver_p.h>
+#include <QtGui/private/qunixsocket_p.h>
+#else
+#include <QtNetwork/qtcpsocket.h>
+#include <QtNetwork/qtcpserver.h>
+#endif
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+
+class QWSSocket : public QWS_SOCK_BASE
+{
+ Q_OBJECT
+public:
+ explicit QWSSocket(QObject *parent=0);
+ ~QWSSocket();
+
+ bool connectToLocalFile(const QString &file);
+
+#ifndef QT_NO_SXE
+ QString errorString();
+Q_SIGNALS:
+ void connected();
+ void disconnected();
+ void error(QAbstractSocket::SocketError);
+private Q_SLOTS:
+ void forwardStateChange(SocketState);
+#endif
+
+private:
+ Q_DISABLE_COPY(QWSSocket)
+};
+
+
+class QWSServerSocket : public QWS_SOCK_SERVER_BASE
+{
+ Q_OBJECT
+public:
+ QWSServerSocket(const QString& file, QObject *parent=0);
+ ~QWSServerSocket();
+
+#ifndef QT_NO_SXE
+ QWSSocket *nextPendingConnection();
+Q_SIGNALS:
+ void newConnection();
+protected:
+ void incomingConnection(int socketDescriptor);
+private:
+ QMutex ssmx;
+ QList<int> inboundConnections;
+#endif
+
+private:
+ Q_DISABLE_COPY(QWSServerSocket)
+
+ void init(const QString &file);
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QT_NO_QWS_MULTIPROCESS
+
+#endif // QWSSOCKET_QWS_H
diff --git a/src/gui/embedded/qwsutils_qws.h b/src/gui/embedded/qwsutils_qws.h
new file mode 100644
index 0000000000..3aa96d184e
--- /dev/null
+++ b/src/gui/embedded/qwsutils_qws.h
@@ -0,0 +1,98 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QWSUTILS_QWS_H
+#define QWSUTILS_QWS_H
+
+#include <QtCore/QIODevice>
+
+#ifndef QT_NO_SXE
+#define QWS_SOCK_BASE QUnixSocket
+#define QWS_SOCK_SERVER_BASE QUnixSocketServer
+class QUnixSocket;
+class QUnixSocketServer;
+#else
+#define QWS_SOCK_BASE QTcpSocket
+#define QWS_SOCK_SERVER_BASE QTcpServer
+class QTcpSocket;
+class QTcpServer;
+#endif
+class QWSSocket;
+class QWSServerSocket;
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+/********************************************************************
+ *
+ * Convenient socket functions
+ *
+ ********************************************************************/
+#ifndef QT_NO_QWS_MULTIPROCESS
+inline int qws_read_uint(QIODevice *socket)
+{
+ if (!socket || socket->bytesAvailable() < (int)sizeof(int))
+ return -1;
+
+ int i;
+ socket->read(reinterpret_cast<char*>(&i), sizeof(i));
+
+ return i;
+}
+
+inline void qws_write_uint(QIODevice *socket, int i)
+{
+ if (!socket)
+ return;
+
+ socket->write(reinterpret_cast<char*>(&i), sizeof(i));
+}
+
+#endif // QT_NO_QWS_MULTIPROCESS
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QWSUTILS_QWS_H
diff --git a/src/gui/graphicsview/graphicsview.pri b/src/gui/graphicsview/graphicsview.pri
new file mode 100644
index 0000000000..02d9bb1a94
--- /dev/null
+++ b/src/gui/graphicsview/graphicsview.pri
@@ -0,0 +1,46 @@
+# Qt graphicsview module
+
+HEADERS += \
+ graphicsview/qgraphicsitem.h \
+ graphicsview/qgraphicsitem_p.h \
+ graphicsview/qgraphicsitemanimation.h \
+ graphicsview/qgraphicsscene.h \
+ graphicsview/qgraphicsscene_p.h \
+ graphicsview/qgraphicsscene_bsp_p.h \
+ graphicsview/qgraphicssceneevent.h \
+ graphicsview/qgraphicsview_p.h \
+ graphicsview/qgraphicsview.h
+
+SOURCES += \
+ graphicsview/qgraphicsitem.cpp \
+ graphicsview/qgraphicsitemanimation.cpp \
+ graphicsview/qgraphicsscene.cpp \
+ graphicsview/qgraphicsscene_bsp.cpp \
+ graphicsview/qgraphicssceneevent.cpp \
+ graphicsview/qgraphicsview.cpp
+
+# Widgets on the canvas
+
+HEADERS += \
+ graphicsview/qgraphicslayout.h \
+ graphicsview/qgraphicslayout_p.h \
+ graphicsview/qgraphicslayoutitem.h \
+ graphicsview/qgraphicslayoutitem_p.h \
+ graphicsview/qgraphicslinearlayout.h \
+ graphicsview/qgraphicswidget.h \
+ graphicsview/qgraphicswidget_p.h \
+ graphicsview/qgridlayoutengine_p.h \
+ graphicsview/qgraphicsproxywidget.h \
+ graphicsview/qgraphicsgridlayout.h
+
+SOURCES += \
+ graphicsview/qgraphicslayout.cpp \
+ graphicsview/qgraphicslayout_p.cpp \
+ graphicsview/qgraphicslayoutitem.cpp \
+ graphicsview/qgraphicslinearlayout.cpp \
+ graphicsview/qgraphicswidget.cpp \
+ graphicsview/qgraphicswidget_p.cpp \
+ graphicsview/qgridlayoutengine.cpp \
+ graphicsview/qgraphicsproxywidget.cpp \
+ graphicsview/qgraphicsgridlayout.cpp
+
diff --git a/src/gui/graphicsview/qgraphicsgridlayout.cpp b/src/gui/graphicsview/qgraphicsgridlayout.cpp
new file mode 100644
index 0000000000..1e21b5473a
--- /dev/null
+++ b/src/gui/graphicsview/qgraphicsgridlayout.cpp
@@ -0,0 +1,651 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*!
+ \class QGraphicsGridLayout
+ \brief The QGraphicsGridLayout class provides a grid layout for managing
+ widgets in Graphics View.
+ \since 4.4
+ \ingroup multimedia
+ \ingroup graphicsview-api
+
+ The most common way to use QGraphicsGridLayout is to construct an object
+ on the heap with no parent, add widgets and layouts by calling addItem(),
+ and finally assign the layout to a widget by calling
+ QGraphicsWidget::setLayout(). QGraphicsGridLayout automatically computes
+ the dimensions of the grid as you add items.
+
+ \snippet doc/src/snippets/code/src_gui_graphicsview_qgraphicsgridlayout.cpp 0
+
+ The layout takes ownership of the items. In some cases when the layout
+ item also inherits from QGraphicsItem (such as QGraphicsWidget) there will be a
+ ambiguity in ownership because the layout item belongs to two ownership hierarchies.
+ See the documentation of QGraphicsLayoutItem::setOwnedByLayout() how to handle
+ this.
+ You can access each item in the layout by calling count() and itemAt(). Calling
+ removeAt() will remove an item from the layout, without
+ destroying it.
+
+ \sa QGraphicsLinearLayout, QGraphicsWidget
+*/
+
+#include "qglobal.h"
+
+#ifndef QT_NO_GRAPHICSVIEW
+
+#include "qapplication.h"
+#include "qwidget.h"
+#include "qgraphicslayout_p.h"
+#include "qgraphicslayoutitem.h"
+#include "qgraphicsgridlayout.h"
+#include "qgraphicswidget.h"
+#include "qgridlayoutengine_p.h"
+#include <QtCore/qdebug.h>
+
+QT_BEGIN_NAMESPACE
+
+class QGraphicsGridLayoutPrivate : public QGraphicsLayoutPrivate
+{
+public:
+ QGraphicsGridLayoutPrivate() { }
+ QLayoutStyleInfo styleInfo() const;
+
+ QGridLayoutEngine engine;
+#ifdef QT_DEBUG
+ void dump(int indent) const;
+#endif
+};
+
+QLayoutStyleInfo QGraphicsGridLayoutPrivate::styleInfo() const
+{
+ static QWidget *wid = 0;
+ if (!wid)
+ wid = new QWidget;
+ QGraphicsItem *item = parentItem();
+ QStyle *style = (item && item->isWidget()) ? static_cast<QGraphicsWidget*>(item)->style() : qApp->style();
+ return QLayoutStyleInfo(style, wid);
+}
+
+/*!
+ Constructs a QGraphicsGridLayout instance. \a parent is passed to
+ QGraphicsLayout's constructor.
+*/
+QGraphicsGridLayout::QGraphicsGridLayout(QGraphicsLayoutItem *parent)
+ : QGraphicsLayout(*new QGraphicsGridLayoutPrivate(), parent)
+{
+}
+
+/*!
+ Destroys the QGraphicsGridLayout object.
+*/
+QGraphicsGridLayout::~QGraphicsGridLayout()
+{
+ for (int i = count() - 1; i >= 0; --i) {
+ QGraphicsLayoutItem *item = itemAt(i);
+ // The following lines can be removed, but this removes the item
+ // from the layout more efficiently than the implementation of
+ // ~QGraphicsLayoutItem.
+ removeAt(i);
+ if (item) {
+ item->setParentLayoutItem(0);
+ if (item->ownedByLayout())
+ delete item;
+ }
+ }
+}
+
+/*!
+ Adds \a item to the grid on \a row and \a column. You can specify a
+ \a rowSpan and \a columnSpan and an optional \a alignment.
+*/
+void QGraphicsGridLayout::addItem(QGraphicsLayoutItem *item, int row, int column,
+ int rowSpan, int columnSpan, Qt::Alignment alignment)
+{
+ Q_D(QGraphicsGridLayout);
+ if (row < 0 || column < 0) {
+ qWarning("QGraphicsGridLayout::addItem: invalid row/column: %d",
+ row < 0 ? row : column);
+ return;
+ }
+ if (columnSpan < 1 || rowSpan < 1) {
+ qWarning("QGraphicsGridLayout::addItem: invalid row span/column span: %d",
+ rowSpan < 1 ? rowSpan : columnSpan);
+ return;
+ }
+ if (!item) {
+ qWarning("QGraphicsGridLayout::addItem: cannot add null item");
+ return;
+ }
+
+ d->addChildLayoutItem(item);
+
+ new QGridLayoutItem(&d->engine, item, row, column, rowSpan, columnSpan, alignment);
+ invalidate();
+}
+
+/*!
+ \fn QGraphicsGridLayout::addItem(QGraphicsLayoutItem *item, int row, int column, Qt::Alignment alignment = 0)
+
+ Adds \a item to the grid on \a row and \a column. You can specify
+ an optional \a alignment for \a item.
+*/
+
+/*!
+ Sets the default horizontal spacing for the grid layout to \a spacing.
+*/
+void QGraphicsGridLayout::setHorizontalSpacing(qreal spacing)
+{
+ Q_D(QGraphicsGridLayout);
+ d->engine.setSpacing(spacing, Qt::Horizontal);
+ invalidate();
+}
+
+/*!
+ Returns the default horizontal spacing for the grid layout.
+*/
+qreal QGraphicsGridLayout::horizontalSpacing() const
+{
+ Q_D(const QGraphicsGridLayout);
+ return d->engine.spacing(d->styleInfo(), Qt::Horizontal);
+}
+
+/*!
+ Sets the default vertical spacing for the grid layout to \a spacing.
+*/
+void QGraphicsGridLayout::setVerticalSpacing(qreal spacing)
+{
+ Q_D(QGraphicsGridLayout);
+ d->engine.setSpacing(spacing, Qt::Vertical);
+ invalidate();
+}
+
+/*!
+ Returns the default vertical spacing for the grid layout.
+*/
+qreal QGraphicsGridLayout::verticalSpacing() const
+{
+ Q_D(const QGraphicsGridLayout);
+ return d->engine.spacing(d->styleInfo(), Qt::Vertical);
+}
+
+/*!
+ Sets the grid layout's default spacing, both vertical and
+ horizontal, to \a spacing.
+
+ \sa rowSpacing(), columnSpacing()
+*/
+void QGraphicsGridLayout::setSpacing(qreal spacing)
+{
+ Q_D(QGraphicsGridLayout);
+ d->engine.setSpacing(spacing, Qt::Horizontal | Qt::Vertical);
+ invalidate();
+}
+
+/*!
+ Sets the spacing for \a row to \a spacing.
+*/
+void QGraphicsGridLayout::setRowSpacing(int row, qreal spacing)
+{
+ Q_D(QGraphicsGridLayout);
+ d->engine.setRowSpacing(row, spacing, Qt::Vertical);
+ invalidate();
+}
+
+/*!
+ Returns the row spacing for \a row.
+*/
+qreal QGraphicsGridLayout::rowSpacing(int row) const
+{
+ Q_D(const QGraphicsGridLayout);
+ return d->engine.rowSpacing(row, Qt::Vertical);
+}
+
+/*!
+ Sets the spacing for \a column to \a spacing.
+*/
+void QGraphicsGridLayout::setColumnSpacing(int column, qreal spacing)
+{
+ Q_D(QGraphicsGridLayout);
+ d->engine.setRowSpacing(column, spacing, Qt::Horizontal);
+ invalidate();
+}
+
+/*!
+ Returns the column spacing for \a column.
+*/
+qreal QGraphicsGridLayout::columnSpacing(int column) const
+{
+ Q_D(const QGraphicsGridLayout);
+ return d->engine.rowSpacing(column, Qt::Horizontal);
+}
+
+/*!
+ Sets the stretch factor for \a row to \a stretch.
+*/
+void QGraphicsGridLayout::setRowStretchFactor(int row, int stretch)
+{
+ Q_D(QGraphicsGridLayout);
+ d->engine.setRowStretchFactor(row, stretch, Qt::Vertical);
+ invalidate();
+}
+
+/*!
+ Returns the stretch factor for \a row.
+*/
+int QGraphicsGridLayout::rowStretchFactor(int row) const
+{
+ Q_D(const QGraphicsGridLayout);
+ return d->engine.rowStretchFactor(row, Qt::Vertical);
+}
+
+/*!
+ Sets the stretch factor for \a column to \a stretch.
+*/
+void QGraphicsGridLayout::setColumnStretchFactor(int column, int stretch)
+{
+ Q_D(QGraphicsGridLayout);
+ d->engine.setRowStretchFactor(column, stretch, Qt::Horizontal);
+ invalidate();
+}
+
+/*!
+ Returns the stretch factor for \a column.
+*/
+int QGraphicsGridLayout::columnStretchFactor(int column) const
+{
+ Q_D(const QGraphicsGridLayout);
+ return d->engine.rowStretchFactor(column, Qt::Horizontal);
+}
+
+/*!
+ Sets the minimum height for row, \a row, to \a height.
+*/
+void QGraphicsGridLayout::setRowMinimumHeight(int row, qreal height)
+{
+ Q_D(QGraphicsGridLayout);
+ d->engine.setRowSizeHint(Qt::MinimumSize, row, height, Qt::Vertical);
+ invalidate();
+}
+
+/*!
+ Returns the minimum height for row, \a row.
+*/
+qreal QGraphicsGridLayout::rowMinimumHeight(int row) const
+{
+ Q_D(const QGraphicsGridLayout);
+ return d->engine.rowSizeHint(Qt::MinimumSize, row, Qt::Vertical);
+}
+
+/*!
+ Sets the preferred height for row, \a row, to \a height.
+*/
+void QGraphicsGridLayout::setRowPreferredHeight(int row, qreal height)
+{
+ Q_D(QGraphicsGridLayout);
+ d->engine.setRowSizeHint(Qt::PreferredSize, row, height, Qt::Vertical);
+ invalidate();
+}
+
+/*!
+ Returns the preferred height for row, \a row.
+*/
+qreal QGraphicsGridLayout::rowPreferredHeight(int row) const
+{
+ Q_D(const QGraphicsGridLayout);
+ return d->engine.rowSizeHint(Qt::PreferredSize, row, Qt::Vertical);
+}
+
+/*!
+ Sets the maximum height for row, \a row, to \a height.
+*/
+void QGraphicsGridLayout::setRowMaximumHeight(int row, qreal height)
+{
+ Q_D(QGraphicsGridLayout);
+ d->engine.setRowSizeHint(Qt::MaximumSize, row, height, Qt::Vertical);
+ invalidate();
+}
+
+/*!
+ Returns the maximum height for row, \a row.
+*/
+qreal QGraphicsGridLayout::rowMaximumHeight(int row) const
+{
+ Q_D(const QGraphicsGridLayout);
+ return d->engine.rowSizeHint(Qt::MaximumSize, row, Qt::Vertical);
+}
+
+/*!
+ Sets the fixed height for row, \a row, to \a height.
+*/
+void QGraphicsGridLayout::setRowFixedHeight(int row, qreal height)
+{
+ Q_D(QGraphicsGridLayout);
+ d->engine.setRowSizeHint(Qt::MinimumSize, row, height, Qt::Vertical);
+ d->engine.setRowSizeHint(Qt::MaximumSize, row, height, Qt::Vertical);
+ invalidate();
+}
+
+/*!
+ Sets the minimum width for \a column to \a width.
+*/
+void QGraphicsGridLayout::setColumnMinimumWidth(int column, qreal width)
+{
+ Q_D(QGraphicsGridLayout);
+ d->engine.setRowSizeHint(Qt::MinimumSize, column, width, Qt::Horizontal);
+ invalidate();
+}
+
+/*!
+ Returns the minimum width for \a column.
+*/
+qreal QGraphicsGridLayout::columnMinimumWidth(int column) const
+{
+ Q_D(const QGraphicsGridLayout);
+ return d->engine.rowSizeHint(Qt::MinimumSize, column, Qt::Horizontal);
+}
+
+/*!
+ Sets the preferred width for \a column to \a width.
+*/
+void QGraphicsGridLayout::setColumnPreferredWidth(int column, qreal width)
+{
+ Q_D(QGraphicsGridLayout);
+ d->engine.setRowSizeHint(Qt::PreferredSize, column, width, Qt::Horizontal);
+ invalidate();
+}
+
+/*!
+ Returns the preferred width for \a column.
+*/
+qreal QGraphicsGridLayout::columnPreferredWidth(int column) const
+{
+ Q_D(const QGraphicsGridLayout);
+ return d->engine.rowSizeHint(Qt::PreferredSize, column, Qt::Horizontal);
+}
+
+/*!
+ Sets the maximum width of \a column to \a width.
+*/
+void QGraphicsGridLayout::setColumnMaximumWidth(int column, qreal width)
+{
+ Q_D(QGraphicsGridLayout);
+ d->engine.setRowSizeHint(Qt::MaximumSize, column, width, Qt::Horizontal);
+ invalidate();
+}
+
+/*!
+ Returns the maximum width for \a column.
+*/
+qreal QGraphicsGridLayout::columnMaximumWidth(int column) const
+{
+ Q_D(const QGraphicsGridLayout);
+ return d->engine.rowSizeHint(Qt::MaximumSize, column, Qt::Horizontal);
+}
+
+/*!
+ Sets the fixed width of \a column to \a width.
+*/
+void QGraphicsGridLayout::setColumnFixedWidth(int column, qreal width)
+{
+ Q_D(QGraphicsGridLayout);
+ d->engine.setRowSizeHint(Qt::MinimumSize, column, width, Qt::Horizontal);
+ d->engine.setRowSizeHint(Qt::MaximumSize, column, width, Qt::Horizontal);
+ invalidate();
+}
+
+/*!
+ Sets the alignment of \a row to \a alignment.
+*/
+void QGraphicsGridLayout::setRowAlignment(int row, Qt::Alignment alignment)
+{
+ Q_D(QGraphicsGridLayout);
+ d->engine.setRowAlignment(row, alignment, Qt::Vertical);
+ invalidate();
+}
+
+/*!
+ Returns the alignment of \a row.
+*/
+Qt::Alignment QGraphicsGridLayout::rowAlignment(int row) const
+{
+ Q_D(const QGraphicsGridLayout);
+ return d->engine.rowAlignment(row, Qt::Vertical);
+}
+
+/*!
+ Sets the alignment for \a column to \a alignment.
+*/
+void QGraphicsGridLayout::setColumnAlignment(int column, Qt::Alignment alignment)
+{
+ Q_D(QGraphicsGridLayout);
+ d->engine.setRowAlignment(column, alignment, Qt::Horizontal);
+ invalidate();
+}
+
+/*!
+ Returns the alignment for \a column.
+*/
+Qt::Alignment QGraphicsGridLayout::columnAlignment(int column) const
+{
+ Q_D(const QGraphicsGridLayout);
+ return d->engine.rowAlignment(column, Qt::Horizontal);
+}
+
+/*!
+ Sets the alignment for \a item to \a alignment.
+*/
+void QGraphicsGridLayout::setAlignment(QGraphicsLayoutItem *item, Qt::Alignment alignment)
+{
+ Q_D(QGraphicsGridLayout);
+ d->engine.setAlignment(item, alignment);
+ invalidate();
+}
+
+/*!
+ Returns the alignment for \a item.
+*/
+Qt::Alignment QGraphicsGridLayout::alignment(QGraphicsLayoutItem *item) const
+{
+ Q_D(const QGraphicsGridLayout);
+ return d->engine.alignment(item);
+}
+
+/*!
+ Returns the number of rows in the grid layout. This is always one more
+ than the index of the last row that is occupied by a layout item (empty
+ rows are counted except for those at the end).
+*/
+int QGraphicsGridLayout::rowCount() const
+{
+ Q_D(const QGraphicsGridLayout);
+ return d->engine.effectiveLastRow(Qt::Vertical) + 1;
+}
+
+/*!
+ Returns the number of columns in the grid layout. This is always one more
+ than the index of the last column that is occupied by a layout item (empty
+ columns are counted except for those at the end).
+*/
+int QGraphicsGridLayout::columnCount() const
+{
+ Q_D(const QGraphicsGridLayout);
+ return d->engine.effectiveLastRow(Qt::Horizontal) + 1;
+}
+
+/*!
+ Returns a pointer to the layout item at (\a row, \a column).
+*/
+QGraphicsLayoutItem *QGraphicsGridLayout::itemAt(int row, int column) const
+{
+ Q_D(const QGraphicsGridLayout);
+ if (row < 0 || row >= rowCount() || column < 0 || column >= columnCount()) {
+ qWarning("QGraphicsGridLayout::itemAt: invalid row, column %d, %d", row, column);
+ return 0;
+ }
+ if (QGridLayoutItem *item = d->engine.itemAt(row, column))
+ return item->layoutItem();
+ return 0;
+}
+
+/*!
+ Returns the number of layout items in this grid layout.
+*/
+int QGraphicsGridLayout::count() const
+{
+ Q_D(const QGraphicsGridLayout);
+ return d->engine.itemCount();
+}
+
+/*!
+ Returns the layout item at \a index, or 0 if there is no layout item at
+ this index.
+*/
+QGraphicsLayoutItem *QGraphicsGridLayout::itemAt(int index) const
+{
+ Q_D(const QGraphicsGridLayout);
+ if (index < 0 || index >= d->engine.itemCount()) {
+ qWarning("QGraphicsGridLayout::itemAt: invalid index %d", index);
+ return 0;
+ }
+ QGraphicsLayoutItem *item = 0;
+ if (QGridLayoutItem *gridItem = d->engine.itemAt(index))
+ item = gridItem->layoutItem();
+ return item;
+}
+
+/*!
+ Removes the layout item at \a index without destroying it. Ownership of
+ the item is transferred to the caller.
+
+ \sa addItem()
+*/
+void QGraphicsGridLayout::removeAt(int index)
+{
+ Q_D(QGraphicsGridLayout);
+ if (index < 0 || index >= d->engine.itemCount()) {
+ qWarning("QGraphicsGridLayout::removeAt: invalid index %d", index);
+ return;
+ }
+ if (QGridLayoutItem *gridItem = d->engine.itemAt(index)) {
+ if (QGraphicsLayoutItem *layoutItem = gridItem->layoutItem())
+ layoutItem->setParentLayoutItem(0);
+ d->engine.removeItem(gridItem);
+ delete gridItem;
+ invalidate();
+ }
+}
+
+/*!
+ \reimp
+*/
+void QGraphicsGridLayout::invalidate()
+{
+ Q_D(QGraphicsGridLayout);
+ d->engine.invalidate();
+ QGraphicsLayout::invalidate();
+}
+
+#ifdef QT_DEBUG
+void QGraphicsGridLayoutPrivate::dump(int indent) const
+{
+ if (qt_graphicsLayoutDebug()) {
+ engine.dump(indent + 1);
+ }
+}
+#endif
+
+/*!
+ Sets the bounding geometry of the grid layout to \a rect.
+*/
+void QGraphicsGridLayout::setGeometry(const QRectF &rect)
+{
+ Q_D(QGraphicsGridLayout);
+ QGraphicsLayout::setGeometry(rect);
+ QRectF effectiveRect = geometry();
+ qreal left, top, right, bottom;
+ getContentsMargins(&left, &top, &right, &bottom);
+ Qt::LayoutDirection visualDir = d->visualDirection();
+ d->engine.setVisualDirection(visualDir);
+ if (visualDir == Qt::RightToLeft)
+ qSwap(left, right);
+ effectiveRect.adjust(+left, +top, -right, -bottom);
+ d->engine.setGeometries(d->styleInfo(), effectiveRect);
+#ifdef QT_DEBUG
+ if (qt_graphicsLayoutDebug()) {
+ static int counter = 0;
+ qDebug("==== BEGIN DUMP OF QGraphicsGridLayout (%d)====", counter++);
+ d->dump(1);
+ qDebug("==== END DUMP OF QGraphicsGridLayout ====");
+ }
+#endif
+}
+
+/*!
+ \reimp
+*/
+QSizeF QGraphicsGridLayout::sizeHint(Qt::SizeHint which, const QSizeF &constraint) const
+{
+ Q_D(const QGraphicsGridLayout);
+ qreal left, top, right, bottom;
+ getContentsMargins(&left, &top, &right, &bottom);
+ return d->engine.sizeHint(d->styleInfo(), which , constraint) + QSizeF(left + right, top + bottom);
+}
+
+
+#if 0
+// ### kill? (implement and kill?)
+QRect QGraphicsGridLayout::cellRect(int row, int column, int rowSpan, int columnSpan) const
+{
+ Q_D(const QGraphicsGridLayout);
+ return QRect();
+// return d->engine.cellRect(parentLayoutable(), contentsGeometry(), row, column, rowSpan, columnSpan);
+}
+
+QSizePolicy::ControlTypes QGraphicsGridLayout::controlTypes(LayoutSide side) const
+{
+ Q_D(const QGraphicsGridLayout);
+ return d->engine.controlTypes(side);
+}
+#endif
+
+QT_END_NAMESPACE
+
+#endif //QT_NO_GRAPHICSVIEW
diff --git a/src/gui/graphicsview/qgraphicsgridlayout.h b/src/gui/graphicsview/qgraphicsgridlayout.h
new file mode 100644
index 0000000000..5b40d6b561
--- /dev/null
+++ b/src/gui/graphicsview/qgraphicsgridlayout.h
@@ -0,0 +1,143 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QGRAPHICSGRIDLAYOUT_H
+#define QGRAPHICSGRIDLAYOUT_H
+
+#include <QtGui/qgraphicsitem.h>
+#include <QtGui/qgraphicslayout.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+#if !defined(QT_NO_GRAPHICSVIEW) || (QT_EDITION & QT_MODULE_GRAPHICSVIEW) != QT_MODULE_GRAPHICSVIEW
+
+class QGraphicsGridLayoutPrivate;
+
+class Q_GUI_EXPORT QGraphicsGridLayout : public QGraphicsLayout
+{
+public:
+ QGraphicsGridLayout(QGraphicsLayoutItem *parent = 0);
+ virtual ~QGraphicsGridLayout();
+
+ void addItem(QGraphicsLayoutItem *item, int row, int column, int rowSpan, int columnSpan,
+ Qt::Alignment alignment = 0);
+ inline void addItem(QGraphicsLayoutItem *item, int row, int column, Qt::Alignment alignment = 0);
+
+ void setHorizontalSpacing(qreal spacing);
+ qreal horizontalSpacing() const;
+ void setVerticalSpacing(qreal spacing);
+ qreal verticalSpacing() const;
+ void setSpacing(qreal spacing);
+
+ void setRowSpacing(int row, qreal spacing);
+ qreal rowSpacing(int row) const;
+ void setColumnSpacing(int column, qreal spacing);
+ qreal columnSpacing(int column) const;
+
+ void setRowStretchFactor(int row, int stretch);
+ int rowStretchFactor(int row) const;
+ void setColumnStretchFactor(int column, int stretch);
+ int columnStretchFactor(int column) const;
+
+ void setRowMinimumHeight(int row, qreal height);
+ qreal rowMinimumHeight(int row) const;
+ void setRowPreferredHeight(int row, qreal height);
+ qreal rowPreferredHeight(int row) const;
+ void setRowMaximumHeight(int row, qreal height);
+ qreal rowMaximumHeight(int row) const;
+ void setRowFixedHeight(int row, qreal height);
+
+ void setColumnMinimumWidth(int column, qreal width);
+ qreal columnMinimumWidth(int column) const;
+ void setColumnPreferredWidth(int column, qreal width);
+ qreal columnPreferredWidth(int column) const;
+ void setColumnMaximumWidth(int column, qreal width);
+ qreal columnMaximumWidth(int column) const;
+ void setColumnFixedWidth(int column, qreal width);
+
+ void setRowAlignment(int row, Qt::Alignment alignment);
+ Qt::Alignment rowAlignment(int row) const;
+ void setColumnAlignment(int column, Qt::Alignment alignment);
+ Qt::Alignment columnAlignment(int column) const;
+
+ void setAlignment(QGraphicsLayoutItem *item, Qt::Alignment alignment);
+ Qt::Alignment alignment(QGraphicsLayoutItem *item) const;
+
+ int rowCount() const;
+ int columnCount() const;
+
+ QGraphicsLayoutItem *itemAt(int row, int column) const;
+
+ // inherited from QGraphicsLayout
+ int count() const;
+ QGraphicsLayoutItem *itemAt(int index) const;
+ void removeAt(int index);
+
+ void invalidate();
+
+ // inherited from QGraphicsLayoutItem
+ void setGeometry(const QRectF &rect);
+ QSizeF sizeHint(Qt::SizeHint which, const QSizeF &constraint = QSizeF()) const;
+
+ // ####
+ //QRect cellRect(int row, int column, int rowSpan = 1, int columnSpan = 1) const;
+ //QSizePolicy::ControlTypes controlTypes(LayoutSide side) const;
+
+private:
+ Q_DISABLE_COPY(QGraphicsGridLayout)
+ Q_DECLARE_PRIVATE(QGraphicsGridLayout)
+};
+
+inline void QGraphicsGridLayout::addItem(QGraphicsLayoutItem *aitem, int arow, int acolumn, Qt::Alignment aalignment)
+{ addItem(aitem, arow, acolumn, 1, 1, aalignment); }
+
+#endif
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif
+
diff --git a/src/gui/graphicsview/qgraphicsitem.cpp b/src/gui/graphicsview/qgraphicsitem.cpp
new file mode 100644
index 0000000000..7b885df59f
--- /dev/null
+++ b/src/gui/graphicsview/qgraphicsitem.cpp
@@ -0,0 +1,8803 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*!
+ \class QGraphicsItem
+ \brief The QGraphicsItem class is the base class for all graphical
+ items in a QGraphicsScene.
+ \since 4.2
+ \ingroup multimedia
+ \ingroup graphicsview-api
+
+ It provides a light-weight foundation for writing your own custom items.
+ This includes defining the item's geometry, collision detection, its
+ painting implementation and item interaction through its event handlers.
+ QGraphicsItem is part of \l{The Graphics View Framework}
+
+ \img graphicsview-items.png
+
+ For convenience, Qt provides a set of standard graphics items for the most
+ common shapes. These are:
+
+ \list
+ \o QGraphicsEllipseItem provides an ellipse item
+ \o QGraphicsLineItem provides a line item
+ \o QGraphicsPathItem provides an arbitrary path item
+ \o QGraphicsPixmapItem provides a pixmap item
+ \o QGraphicsPolygonItem provides a polygon item
+ \o QGraphicsRectItem provides a rectangular item
+ \o QGraphicsSimpleTextItem provides a simple text label item
+ \o QGraphicsTextItem provides an advanced text browser item
+ \endlist
+
+ All of an item's geometric information is based on its local coordinate
+ system. The item's position, pos(), is the only function that does not
+ operate in local coordinates, as it returns a position in parent
+ coordinates. \l {The Graphics View Coordinate System} describes the coordinate
+ system in detail.
+
+ You can set whether an item should be visible (i.e., drawn, and accepting
+ events), by calling setVisible(). Hiding an item will also hide its
+ children. Similarly, you can enable or disable an item by calling
+ setEnabled(). If you disable an item, all its children will also be
+ disabled. By default, items are both visible and enabled. To toggle
+ whether an item is selected or not, first enable selection by setting
+ the ItemIsSelectable flag, and then call setSelected(). Normally,
+ selection is toggled by the scene, as a result of user interaction.
+
+ To write your own graphics item, you first create a subclass of
+ QGraphicsItem, and then start by implementing its two pure virtual public
+ functions: boundingRect(), which returns an estimate of the area painted
+ by the item, and paint(), which implements the actual painting. For
+ example:
+
+ \snippet doc/src/snippets/code/src_gui_graphicsview_qgraphicsitem.cpp 0
+
+ The boundingRect() function has many different purposes. QGraphicsScene
+ bases its item index on boundingRect(), and QGraphicsView uses it both for
+ culling invisible items, and for determining the area that needs to be
+ recomposed when drawing overlapping items. In addition, QGraphicsItem's
+ collision detection mechanisms use boundingRect() to provide an efficient
+ cut-off. The fine grained collision algorithm in collidesWithItem() is based
+ on calling shape(), which returns an accurate outline of the item's shape
+ as a QPainterPath.
+
+ QGraphicsScene expects all items boundingRect() and shape() to remain
+ unchanged unless it is notified. If you want to change an item's geometry
+ in any way, you must first call prepareGeometryChange() to allow
+ QGraphicsScene to update its bookkeeping.
+
+ Collision detection can be done in two ways:
+
+ \list 1
+
+ \o Reimplement shape() to return an accurate shape for your item, and rely
+ on the default implementation of collidesWithItem() to do shape-shape
+ intersection. This can be rather expensive if the shapes are complex.
+
+ \o Reimplement collidesWithItem() to provide your own custom item and shape
+ collision algorithm.
+
+ \endlist
+
+ The contains() function can be called to determine whether the item \e
+ contains a point or not. This function can also be reimplemented by the
+ item. The default behavior of contains() is based on calling shape().
+
+ Items can contain other items, and also be contained by other items. All
+ items can have a parent item and a list of children. Unless the item has
+ no parent, its position is in \e parent coordinates (i.e., the parent's
+ local coordinates). Parent items propagate both their position and their
+ transformation to all children.
+
+ \img graphicsview-parentchild.png
+
+ QGraphicsItem supports affine transformations in addition to its base
+ position, pos(). To change the item's transformation, you can either pass
+ a transformation matrix to setTransform(), or call one of the convenience
+ functions rotate(), scale(), translate(), or shear(). Item transformations
+ accumulate from parent to child, so if both a parent and child item are
+ rotated 90 degrees, the child's total transformation will be 180 degrees.
+ Similarly, if the item's parent is scaled to 2x its original size, its
+ children will also be twice as large. An item's transformation does not
+ affect its own local geometry; all geometry functions (e.g., contains(),
+ update(), and all the mapping functions) still operate in local
+ coordinates. For convenience, QGraphicsItem provides the functions
+ sceneTransform(), which returns the item's total transformation matrix
+ (including its position and all parents' positions and transformations),
+ and scenePos(), which returns its position in scene coordinates. To reset
+ an item's matrix, call resetTransform().
+
+ The paint() function is called by QGraphicsView to paint the item's
+ contents. The item has no background or default fill of its own; whatever
+ is behind the item will shine through all areas that are not explicitly
+ painted in this function. You can call update() to schedule a repaint,
+ optionally passing the rectangle that needs a repaint. Depending on
+ whether or not the item is visible in a view, the item may or may not be
+ repainted; there is no equivalent to QWidget::repaint() in QGraphicsItem.
+
+ Items are painted by the view, starting with the parent items and then
+ drawing children, in ascending stacking order. You can set an item's
+ stacking order by calling setZValue(), and test it by calling
+ zValue(), where items with low z-values are painted before items with
+ high z-values. Stacking order applies to sibling items; parents are always
+ drawn before their children.
+
+ QGraphicsItem receives events from QGraphicsScene through the virtual
+ function sceneEvent(). This function distributes the most common events
+ to a set of convenience event handlers:
+
+ \list
+ \o contextMenuEvent() handles context menu events
+ \o focusInEvent() and focusOutEvent() handle focus in and out events
+ \o hoverEnterEvent(), hoverMoveEvent(), and hoverLeaveEvent() handles
+ hover enter, move and leave events
+ \o inputMethodEvent() handles input events, for accessibility support
+ \o keyPressEvent() and keyReleaseEvent handle key press and release events
+ \o mousePressEvent(), mouseMoveEvent(), mouseReleaseEvent(), and
+ mouseDoubleClickEvent() handles mouse press, move, release, click and
+ doubleclick events
+ \endlist
+
+ You can filter events for any other item by installing event
+ filters. This functionaly is separate from from Qt's regular
+ event filters (see QObject::installEventFilter()), which only
+ work on subclasses of QObject. After installing your item as an
+ event filter for another item by calling
+ installSceneEventFilter(), the filtered events will be received
+ by the virtual function sceneEventFilter(). You can remove item
+ event filters by calling removeSceneEventFilter().
+
+ Sometimes it's useful to register custom data with an item, be it a custom
+ item, or a standard item. You can call setData() on any item to store data
+ in it using a key-value pair (the key being an integer, and the value is a
+ QVariant). To get custom data from an item, call data(). This
+ functionality is completely untouched by Qt itself; it is provided for the
+ user's convenience.
+
+ \sa QGraphicsScene, QGraphicsView, {The Graphics View Framework}
+*/
+
+/*!
+ \variable QGraphicsItem::UserType
+
+ The lowest permitted type value for custom items (subclasses
+ of QGraphicsItem or any of the standard items). This value is
+ used in conjunction with a reimplementation of QGraphicsItem::type()
+ and declaring a Type enum value. Example:
+
+ \snippet doc/src/snippets/code/src_gui_graphicsview_qgraphicsitem.cpp 1
+*/
+
+/*!
+ \enum QGraphicsItem::GraphicsItemFlag
+
+ This enum describes different flags that you can set on an item to
+ toggle different features in the item's behavior.
+
+ All flags are disabled by default.
+
+ \value ItemIsMovable The item supports interactive movement using
+ the mouse. By clicking on the item and then dragging, the item
+ will move together with the mouse cursor. If the item has
+ children, all children are also moved. If the item is part of a
+ selection, all selected items are also moved. This feature is
+ provided as a convenience through the base implementation of
+ QGraphicsItem's mouse event handlers.
+
+ \value ItemIsSelectable The item supports selection. Enabling this
+ feature will enable setSelected() to toggle selection for the
+ item. It will also let the item be selected automatically as a
+ result of calling QGraphicsScene::setSelectionArea(), by clicking
+ on an item, or by using rubber band selection in QGraphicsView.
+
+ \value ItemIsFocusable The item supports keyboard input focus (i.e., it is
+ an input item). Enabling this flag will allow the item to accept focus,
+ which again allows the delivery of key events to
+ QGraphicsItem::keyPressEvent() and QGraphicsItem::keyReleaseEvent().
+
+ \value ItemClipsToShape The item clips to its own shape. The item cannot
+ draw or receive mouse, tablet, drag and drop or hover events outside ts
+ shape. It is disabled by default. This behavior is enforced by
+ QGraphicsView::drawItems() or QGraphicsScene::drawItems(). This flag was
+ introduced in Qt 4.3.
+
+ \value ItemClipsChildrenToShape The item clips the painting of all its
+ descendants to its own shape. Items that are either direct or indirect
+ children of this item cannot draw outside this item's shape. By default,
+ this flag is disabled; children can draw anywhere. This behavior is
+ enforced by QGraphicsView::drawItems() or
+ QGraphicsScene::drawItems(). This flag was introduced in Qt 4.3.
+
+ \value ItemIgnoresTransformations The item ignores inherited
+ transformations (i.e., its position is still anchored to its parent, but
+ the parent or view rotation, zoom or shear transformations are ignored).
+ This flag is useful for keeping text label items horizontal and unscaled,
+ so they will still be readable if the view is transformed. When set, the
+ item's view geometry and scene geometry will be maintained separately. You
+ must call deviceTransform() to map coordinates and detect collisions in
+ the view. By default, this flag is disabled. This flag was introduced in
+ Qt 4.3. \note With this flag set you can still scale the item itself, and
+ that scale transformation will influence the item's children.
+
+ \value ItemIgnoresParentOpacity The item ignores its parent's opacity. The
+ item's effective opacity is the same as its own; it does not combine with
+ the parent's opacity. This flags allows your item to keep its absolute
+ opacity even if the parent is semitransparent. This flag was introduced in
+ Qt 4.5.
+
+ \value ItemDoesntPropagateOpacityToChildren The item doesn't propagate its
+ opacity to its children. This flag allows you to create a semitransparent
+ item that does not affect the opacity of its children. This flag was
+ introduced in Qt 4.5.
+
+ \value ItemStacksBehindParent The item is stacked behind its parent. By
+ default, child items are stacked on top of the parent item. But setting
+ this flag, the child will be stacked behind it. This flag is useful for
+ drop shadow effects and for decoration objects that follow the parent
+ item's geometry without drawing on top of it.
+*/
+
+/*!
+ \enum QGraphicsItem::GraphicsItemChange
+
+ ItemVisibleHasChanged,
+ ItemEnabledHasChanged,
+ ItemSelectedHasChanged,
+ ItemParentHasChanged,
+ ItemSceneHasChanged
+
+ This enum describes the state changes that are notified by
+ QGraphicsItem::itemChange(). The notifications are sent as the state
+ changes, and in some cases, adjustments can be made (see the documentation
+ for each change for details).
+
+ Note: Be careful with calling functions on the QGraphicsItem itself inside
+ itemChange(), as certain function calls can lead to unwanted
+ recursion. For example, you cannot call setPos() in itemChange() on an
+ ItemPositionChange notification, as the setPos() function will again call
+ itemChange(ItemPositionChange). Instead, you can return the new, adjusted
+ position from itemChange().
+
+ \value ItemEnabledChange The item's enabled state changes. If the item is
+ presently enabled, it will become disabled, and vice verca. The value
+ argument is the new enabled state (i.e., true or false). Do not call
+ setEnabled() in itemChange() as this notification is delivered. Instead,
+ you can return the new state from itemChange().
+
+ \value ItemEnabledHasChanged The item's enabled state has changed. The
+ value argument is the new enabled state (i.e., true or false). Do not call
+ setEnabled() in itemChange() as this notification is delivered. The return
+ value is ignored.
+
+ \value ItemMatrixChange The item's affine transformation matrix is
+ changing. This value is obsolete; you can use ItemTransformChange instead.
+
+ \value ItemPositionChange The item's position changes. This notification
+ is only sent when the item's local position changes, relative to its
+ parent, has changed (i.e., as a result of calling setPos() or
+ moveBy()). The value argument is the new position (i.e., a QPointF). You
+ can call pos() to get the original position. Do not call setPos() or
+ moveBy() in itemChange() as this notification is delivered; instead, you
+ can return the new, adjusted position from itemChange(). After this
+ notification, QGraphicsItem immediately sends the ItemPositionHasChanged
+ notification if the position changed.
+
+ \value ItemPositionHasChanged The item's position has changed. This
+ notification is only sent after the item's local position, relative to its
+ parent, has changed. The value argument is the new position (the same as
+ pos()), and QGraphicsItem ignores the return value for this notification
+ (i.e., a read-only notification).
+
+ \value ItemTransformChange The item's transformation matrix changes. This
+ notification is only sent when the item's local transformation matrix
+ changes (i.e., as a result of calling setTransform(), or one of the
+ convenience transformation functions, such as rotate()). The value
+ argument is the new matrix (i.e., a QTransform); to get the old matrix,
+ call transform(). Do not call setTransform() or any of the transformation
+ convenience functions in itemChange() as this notification is delivered;
+ instead, you can return the new matrix from itemChange().
+
+ \value ItemTransformHasChanged The item's transformation matrix has
+ changed. This notification is only sent after the item's local
+ trasformation matrix has changed. The value argument is the new matrix
+ (same as transform()), and QGraphicsItem ignores the return value for this
+ notification (i.e., a read-only notification).
+
+ \value ItemSelectedChange The item's selected state changes. If the item
+ is presently selected, it will become unselected, and vice verca. The
+ value argument is the new selected state (i.e., true or false). Do not
+ call setSelected() in itemChange() as this notification is delivered();
+ instead, you can return the new selected state from itemChange().
+
+ \value ItemSelectedHasChanged The item's selected state has changed. The
+ value argument is the new selected state (i.e., true or false). Do not
+ call setSelected() in itemChange() as this notification is delivered. The
+ return value is ignored.
+
+ \value ItemVisibleChange The item's visible state changes. If the item is
+ presently visible, it will become invisible, and vice verca. The value
+ argument is the new visible state (i.e., true or false). Do not call
+ setVisible() in itemChange() as this notification is delivered; instead,
+ you can return the new visible state from itemChange().
+
+ \value ItemVisibleHasChanged The item's visible state has changed. The
+ value argument is the new visible state (i.e., true or false). Do not call
+ setVisible() in itemChange() as this notification is delivered. The return
+ value is ignored.
+
+ \value ItemParentChange The item's parent changes. The value argument is
+ the new parent item (i.e., a QGraphicsItem pointer). Do not call
+ setParentItem() in itemChange() as this notification is delivered;
+ instead, you can return the new parent from itemChange().
+
+ \value ItemParentHasChanged The item's parent has changed. The value
+ argument is the new parent (i.e., a pointer to a QGraphicsItem). Do not
+ call setParentItem() in itemChange() as this notification is
+ delivered. The return value is ignored.
+
+ \value ItemChildAddedChange A child is added to this item. The value
+ argument is the new child item (i.e., a QGraphicsItem pointer). Do not
+ pass this item to any item's setParentItem() function as this notification
+ is delivered. The return value is unused; you cannot adjust anything in
+ this notification. Note that the new child might not be fully constructed
+ when this notification is sent; calling pure virtual functions on
+ the child can lead to a crash.
+
+ \value ItemChildRemovedChange A child is removed from this item. The value
+ argument is the child item that is about to be removed (i.e., a
+ QGraphicsItem pointer). The return value is unused; you cannot adjust
+ anything in this notification.
+
+ \value ItemSceneChange The item is moved to a new scene. This notification
+ is also sent when the item is added to its initial scene, and when it is
+ removed. The value argument is the new scene (i.e., a QGraphicsScene
+ pointer), or a null pointer if the item is removed from a scene. Do not
+ override this change by passing this item to QGraphicsScene::addItem() as
+ this notification is delivered; instead, you can return the new scene from
+ itemChange(). Use this feature with caution; objecting to a scene change can
+ quickly lead to unwanted recursion.
+
+ \value ItemSceneHasChanged The item's scene has changed. The value
+ argument is the new scene (i.e., a pointer to a QGraphicsScene). Do not
+ call setScene() in itemChange() as this notification is delivered. The
+ return value is ignored.
+
+ \value ItemCursorChange The item's cursor changes. The value argument is
+ the new cursor (i.e., a QCursor). Do not call setCursor() in itemChange()
+ as this notification is delivered. Instead, you can return a new cursor
+ from itemChange().
+
+ \value ItemCursorHasChanged The item's cursor has changed. The value
+ argument is the new cursor (i.e., a QCursor). Do not call setCursor() as
+ this notification is delivered. The return value is ignored.
+
+ \value ItemToolTipChange The item's tooltip changes. The value argument is
+ the new tooltip (i.e., a QToolTip). Do not call setToolTip() in
+ itemChange() as this notification is delivered. Instead, you can return a
+ new tooltip from itemChange().
+
+ \value ItemToolTipHasChanged The item's tooltip has changed. The value
+ argument is the new tooltip (i.e., a QToolTip). Do not call setToolTip()
+ as this notification is delivered. The return value is ignored.
+
+ \value ItemFlagsChange The item's flags change. The value argument is the
+ new flags (i.e., a quint32). Do not call setFlags() in itemChange() as
+ this notification is delivered. Instead, you can return the new flags from
+ itemChange().
+
+ \value ItemFlagsHaveChanged The item's flags have changed. The value
+ argument is the new flags (i.e., a quint32). Do not call setFlags() in
+ itemChange() as this notification is delivered. The return value is
+ ignored.
+
+ \value ItemZValueChange The item's Z-value changes. The value argument is
+ the new Z-value (i.e., a double). Do not call setZValue() in itemChange()
+ as this notification is delivered. Instead, you can return a new Z-value
+ from itemChange().
+
+ \value ItemZValueHasChanged The item's Z-value has changed. The value
+ argument is the new Z-value (i.e., a double). Do not call setZValue() as
+ this notification is delivered. The return value is ignored.
+
+ \value ItemOpacityChange The item's opacity changes. The value argument is
+ the new opacity (i.e., a double). Do not call setOpacity() in itemChange()
+ as this notification is delivered. Instead, you can return a new opacity
+ from itemChange().
+
+ \value ItemOpacityHasChanged The item's opacity has changed. The value
+ argument is the new opacity (i.e., a double). Do not call setOpacity() as
+ this notification is delivered. The return value is ignored.
+*/
+
+/*!
+ \enum QGraphicsItem::CacheMode
+ \since 4.4
+
+ This enum describes QGraphicsItem's cache modes. Caching is used to speed
+ up rendering by allocating and rendering to an off-screen pixel buffer,
+ which can be reused when the item requires redrawing. For some paint
+ devices, the cache is stored directly in graphics memory, which makes
+ rendering very quick.
+
+ \value NoCache The default; all item caching is
+ disabled. QGraphicsItem::paint() is called every time the item needs
+ redrawing.
+
+ \value ItemCoordinateCache Caching is enabled for the item's logical
+ (local) coordinate system. QGraphicsItem creates an off-screen pixel
+ buffer with a configurable size / resolution that you can pass to
+ QGraphicsItem::setCacheMode(). Rendering quality will typically degrade,
+ depending on the resolution of the cache and the item transformation. The
+ first time the item is redrawn, it will render itself into the cache, and
+ the cache is then reused for every subsequent expose. The cache is also
+ reused as the item is transformed. To adjust the resolution of the cache,
+ you can call setCacheMode() again.
+
+ \value DeviceCoordinateCache Caching is enabled at the paint device level,
+ in device coordinates. This mode is for items that can move, but are not
+ rotated, scaled or sheared. If the item is transformed directly or
+ indirectly, the cache will be regenerated automatically. Unlike
+ ItemCoordinateCacheMode, DeviceCoordinateCache always renders at maximum
+ quality.
+
+ \sa QGraphicsItem::setCacheMode()
+*/
+
+/*!
+ \enum QGraphicsItem::Extension
+ \internal
+
+ Note: This is provided as a hook to avoid future problems related
+ to adding virtual functions. See also extension(),
+ supportsExtension() and setExtension().
+*/
+
+#include "qgraphicsitem.h"
+
+#ifndef QT_NO_GRAPHICSVIEW
+
+#include "qgraphicsscene.h"
+#include "qgraphicsscene_p.h"
+#include "qgraphicssceneevent.h"
+#include "qgraphicsview.h"
+#include "qgraphicswidget.h"
+#include "qgraphicsproxywidget.h"
+#include <QtCore/qbitarray.h>
+#include <QtCore/qdebug.h>
+#include <QtCore/qpoint.h>
+#include <QtCore/qstack.h>
+#include <QtCore/qtimer.h>
+#include <QtCore/qvariant.h>
+#include <QtCore/qvarlengtharray.h>
+#include <QtGui/qapplication.h>
+#include <QtGui/qbitmap.h>
+#include <QtGui/qpainter.h>
+#include <QtGui/qpainterpath.h>
+#include <QtGui/qpixmapcache.h>
+#include <QtGui/qstyleoption.h>
+#include <QtGui/qevent.h>
+
+#include <private/qgraphicsitem_p.h>
+#include <private/qgraphicswidget_p.h>
+#include <private/qtextcontrol_p.h>
+#include <private/qtextdocumentlayout_p.h>
+#include <private/qtextengine_p.h>
+
+#include <math.h>
+
+QT_BEGIN_NAMESPACE
+
+// QRectF::intersects() returns false always if either the source or target
+// rectangle's width or height are 0. This works around that problem.
+static QRectF _q_adjustedRect(const QRectF &rect)
+{
+ static const qreal p = (qreal)0.00001;
+ QRectF r = rect;
+ if (!r.width())
+ r.adjust(-p, 0, p, 0);
+ if (!r.height())
+ r.adjust(0, -p, 0, p);
+ return r;
+}
+
+static QRect _q_adjustedRect(const QRect &rect)
+{
+ QRect r = rect;
+ if (!r.width())
+ r.adjust(0, 0, 1, 0);
+ if (!r.height())
+ r.adjust(0, 0, 0, 1);
+ return r;
+}
+
+/*
+ ### Move this into QGraphicsItemPrivate
+ */
+class QGraphicsItemCustomDataStore
+{
+public:
+ QMap<const QGraphicsItem *, QMap<int, QVariant> > data;
+};
+Q_GLOBAL_STATIC(QGraphicsItemCustomDataStore, qt_dataStore)
+
+/*!
+ \internal
+
+ Removes the first instance of \a child from \a children. This is a
+ heuristic approach that assumes that it's common to remove items from the
+ start or end of the list.
+*/
+static void qt_graphicsitem_removeChild(QGraphicsItem *child, QList<QGraphicsItem *> *children)
+{
+ const int n = children->size();
+ for (int i = 0; i < (n + 1) / 2; ++i) {
+ if (children->at(i) == child) {
+ children->removeAt(i);
+ return;
+ }
+ int j = n - i - 1;
+ if (children->at(j) == child) {
+ children->removeAt(j);
+ return;
+ }
+ }
+}
+
+/*!
+ \internal
+
+ Returns a QPainterPath of \a path when stroked with the \a pen.
+ Ignoring dash pattern.
+*/
+static QPainterPath qt_graphicsItem_shapeFromPath(const QPainterPath &path, const QPen &pen)
+{
+ // We unfortunately need this hack as QPainterPathStroker will set a width of 1.0
+ // if we pass a value of 0.0 to QPainterPathStroker::setWidth()
+ const qreal penWidthZero = qreal(0.00000001);
+
+ if (path == QPainterPath())
+ return path;
+ QPainterPathStroker ps;
+ ps.setCapStyle(pen.capStyle());
+ if (pen.widthF() <= 0.0)
+ ps.setWidth(penWidthZero);
+ else
+ ps.setWidth(pen.widthF());
+ ps.setJoinStyle(pen.joinStyle());
+ ps.setMiterLimit(pen.miterLimit());
+ QPainterPath p = ps.createStroke(path);
+ p.addPath(path);
+ return p;
+}
+
+/*!
+ \internal
+
+ Propagates the ancestor flag \a flag with value \a enabled to all this
+ item's children. If \a root is false, the flag is also set on this item
+ (default is true).
+*/
+void QGraphicsItemPrivate::updateAncestorFlag(QGraphicsItem::GraphicsItemFlag childFlag,
+ AncestorFlag flag, bool enabled, bool root)
+{
+ Q_Q(QGraphicsItem);
+ if (root) {
+ // For root items only. This is the item that has either enabled or
+ // disabled \a childFlag, or has been reparented.
+ switch (int(childFlag)) {
+ case -1:
+ flag = AncestorHandlesChildEvents;
+ enabled = q->handlesChildEvents();
+ break;
+ case QGraphicsItem::ItemClipsChildrenToShape:
+ flag = AncestorClipsChildren;
+ enabled = flags & QGraphicsItem::ItemClipsChildrenToShape;
+ break;
+ case QGraphicsItem::ItemIgnoresTransformations:
+ flag = AncestorIgnoresTransformations;
+ enabled = flags & QGraphicsItem::ItemIgnoresTransformations;
+ break;
+ default:
+ return;
+ }
+
+ // Inherit the enabled-state from our parents.
+ if ((parent && ((parent->d_ptr->ancestorFlags & flag)
+ || (int(parent->d_ptr->flags & childFlag) == childFlag)
+ || (childFlag == -1 && parent->d_ptr->handlesChildEvents)))) {
+ enabled = true;
+ ancestorFlags |= flag;
+ }
+
+ // Top-level root items don't have any ancestors, so there are no
+ // ancestor flags either.
+ if (!parent)
+ ancestorFlags = 0;
+ } else {
+ // Don't set or propagate the ancestor flag if it's already correct.
+ if (((ancestorFlags & flag) && enabled) || (!(ancestorFlags & flag) && !enabled))
+ return;
+
+ // Set the flag.
+ if (enabled)
+ ancestorFlags |= flag;
+ else
+ ancestorFlags &= ~flag;
+
+ // Don't process children if the item has the main flag set on itself.
+ if ((childFlag != -1 && int(flags & childFlag) == childFlag) || (int(childFlag) == -1 && handlesChildEvents))
+ return;
+ }
+
+ foreach (QGraphicsItem *child, children)
+ child->d_ptr->updateAncestorFlag(childFlag, flag, enabled, false);
+}
+
+/*!
+ \internal
+
+ Propagates item group membership.
+*/
+void QGraphicsItemPrivate::setIsMemberOfGroup(bool enabled)
+{
+ Q_Q(QGraphicsItem);
+ isMemberOfGroup = enabled;
+ if (!qgraphicsitem_cast<QGraphicsItemGroup *>(q)) {
+ foreach (QGraphicsItem *child, children)
+ child->d_func()->setIsMemberOfGroup(enabled);
+ }
+}
+
+/*!
+ \internal
+
+ Maps any item pos properties of \a event to \a item's coordinate system.
+*/
+void QGraphicsItemPrivate::remapItemPos(QEvent *event, QGraphicsItem *item)
+{
+ Q_Q(QGraphicsItem);
+ switch (event->type()) {
+ case QEvent::GraphicsSceneMouseMove:
+ case QEvent::GraphicsSceneMousePress:
+ case QEvent::GraphicsSceneMouseRelease:
+ case QEvent::GraphicsSceneMouseDoubleClick: {
+ QGraphicsSceneMouseEvent *mouseEvent = static_cast<QGraphicsSceneMouseEvent *>(event);
+ mouseEvent->setPos(item->mapFromItem(q, mouseEvent->pos()));
+ mouseEvent->setLastPos(item->mapFromItem(q, mouseEvent->pos()));
+ for (int i = 0x1; i <= 0x10; i <<= 1) {
+ if (mouseEvent->buttons() & i) {
+ Qt::MouseButton button = Qt::MouseButton(i);
+ mouseEvent->setButtonDownPos(button, item->mapFromItem(q, mouseEvent->buttonDownPos(button)));
+ }
+ }
+ break;
+ }
+ case QEvent::GraphicsSceneWheel: {
+ QGraphicsSceneWheelEvent *wheelEvent = static_cast<QGraphicsSceneWheelEvent *>(event);
+ wheelEvent->setPos(item->mapFromItem(q, wheelEvent->pos()));
+ break;
+ }
+ case QEvent::GraphicsSceneContextMenu: {
+ QGraphicsSceneContextMenuEvent *contextEvent = static_cast<QGraphicsSceneContextMenuEvent *>(event);
+ contextEvent->setPos(item->mapFromItem(q, contextEvent->pos()));
+ break;
+ }
+ case QEvent::GraphicsSceneHoverMove: {
+ QGraphicsSceneHoverEvent *hoverEvent = static_cast<QGraphicsSceneHoverEvent *>(event);
+ hoverEvent->setPos(item->mapFromItem(q, hoverEvent->pos()));
+ break;
+ }
+ default:
+ break;
+ }
+}
+
+/*!
+ \internal
+
+ Maps the point \a pos from scene to item coordinates. If \a view is passed and the item
+ is untransformable, this function will correctly map \a pos from the scene using the
+ view's transformation.
+*/
+QPointF QGraphicsItemPrivate::genericMapFromScene(const QPointF &pos,
+ const QWidget *viewport) const
+{
+ Q_Q(const QGraphicsItem);
+ if (!itemIsUntransformable())
+ return q->mapFromScene(pos);
+ QGraphicsView *view = 0;
+ if (viewport)
+ view = qobject_cast<QGraphicsView *>(viewport->parentWidget());
+ if (!view)
+ return q->mapFromScene(pos);
+ // ### More ping pong than needed.
+ return q->deviceTransform(view->viewportTransform()).inverted().map(view->mapFromScene(pos));
+}
+
+/*!
+ \internal
+
+ Returns true if this item or any of its ancestors are untransformable.
+*/
+bool QGraphicsItemPrivate::itemIsUntransformable() const
+{
+ return (flags & QGraphicsItem::ItemIgnoresTransformations)
+ || (ancestorFlags & AncestorIgnoresTransformations);
+}
+
+/*!
+ \internal
+
+ This helper function helped us add input method query support in
+ Qt 4.4.1 without having to reimplement the inputMethodQuery()
+ function in QGraphicsProxyWidget. ### Qt 5: Remove. We cannot
+ remove it in 4.5+ even if we do reimplement the function properly,
+ because apps compiled with 4.4 will not be able to call the
+ reimplementation.
+*/
+QVariant QGraphicsItemPrivate::inputMethodQueryHelper(Qt::InputMethodQuery query) const
+{
+ Q_UNUSED(query);
+ return QVariant();
+}
+
+/*!
+ \internal
+
+ Empty all cached pixmaps from the pixmap cache.
+*/
+void QGraphicsItemCache::purge()
+{
+ QPixmapCache::remove(key);
+ QMutableMapIterator<QPaintDevice *, DeviceData> it(deviceData);
+ while (it.hasNext()) {
+ DeviceData &data = it.next().value();
+ QPixmapCache::remove(data.key);
+ data.cacheIndent = QPoint();
+ }
+ deviceData.clear();
+ allExposed = true;
+ exposed.clear();
+}
+
+/*!
+ Constructs a QGraphicsItem with the given \a parent.
+
+ If \a parent is 0, you can add the item to a scene by calling
+ QGraphicsScene::addItem(). The item will then become a top-level item.
+
+ \sa QGraphicsScene::addItem(), setParentItem()
+*/
+QGraphicsItem::QGraphicsItem(QGraphicsItem *parent
+#ifndef Q_QDOC
+ // obsolete argument
+ , QGraphicsScene *scene
+#endif
+ )
+ : d_ptr(new QGraphicsItemPrivate)
+{
+ d_ptr->q_ptr = this;
+ setParentItem(parent);
+
+ if (scene && parent && parent->scene() != scene) {
+ qWarning("QGraphicsItem::QGraphicsItem: ignoring scene (%p), which is"
+ " different from parent's scene (%p)",
+ scene, parent->scene());
+ return;
+ }
+ if (scene && !parent)
+ scene->addItem(this);
+}
+
+/*!
+ \internal
+*/
+QGraphicsItem::QGraphicsItem(QGraphicsItemPrivate &dd, QGraphicsItem *parent,
+ QGraphicsScene *scene)
+ : d_ptr(&dd)
+{
+ d_ptr->q_ptr = this;
+ setParentItem(parent);
+
+ if (scene && parent && parent->scene() != scene) {
+ qWarning("QGraphicsItem::QGraphicsItem: ignoring scene (%p), which is"
+ " different from parent's scene (%p)",
+ scene, parent->scene());
+ return;
+ }
+ if (scene && !parent)
+ scene->addItem(this);
+}
+
+/*!
+ Destroys the QGraphicsItem and all its children. If this item is currently
+ associated with a scene, the item will be removed from the scene before it
+ is deleted.
+*/
+QGraphicsItem::~QGraphicsItem()
+{
+ clearFocus();
+ d_ptr->removeExtraItemCache();
+
+ QVariant variant;
+ foreach (QGraphicsItem *child, d_ptr->children) {
+ if (QGraphicsItem *parent = child->parentItem()) {
+ qVariantSetValue<QGraphicsItem *>(variant, child);
+ parent->itemChange(ItemChildRemovedChange, variant);
+ }
+ delete child;
+ }
+ d_ptr->children.clear();
+
+ if (QGraphicsItem *parent = parentItem()) {
+ qVariantSetValue<QGraphicsItem *>(variant, this);
+ parent->itemChange(ItemChildRemovedChange, variant);
+ qt_graphicsitem_removeChild(this, &parent->d_func()->children);
+ }
+ if (d_ptr->scene)
+ d_ptr->scene->d_func()->_q_removeItemLater(this);
+
+ delete d_ptr;
+
+ qt_dataStore()->data.remove(this);
+}
+
+/*!
+ Returns the current scene for the item, or 0 if the item is not stored in
+ a scene.
+
+ To add or move an item to a scene, call QGraphicsScene::addItem().
+*/
+QGraphicsScene *QGraphicsItem::scene() const
+{
+ return d_ptr->scene;
+}
+
+/*!
+ Returns a pointer to this item's item group, or 0 if this item is not
+ member of a group.
+
+ \sa QGraphicsItemGroup, QGraphicsScene::createItemGroup()
+*/
+QGraphicsItemGroup *QGraphicsItem::group() const
+{
+ if (!d_ptr->isMemberOfGroup)
+ return 0;
+ QGraphicsItem *parent = const_cast<QGraphicsItem *>(this);
+ while ((parent = parent->d_ptr->parent)) {
+ if (QGraphicsItemGroup *group = qgraphicsitem_cast<QGraphicsItemGroup *>(parent))
+ return group;
+ }
+ // Unreachable; if d_ptr->isMemberOfGroup is != 0, then one parent of this
+ // item is a group item.
+ return 0;
+}
+
+/*!
+ Adds this item to the item group \a group. If \a group is 0, this item is
+ removed from any current group and added as a child of the previous
+ group's parent.
+
+ \sa group(), QGraphicsScene::createItemGroup()
+*/
+void QGraphicsItem::setGroup(QGraphicsItemGroup *group)
+{
+ if (!group) {
+ if (QGraphicsItemGroup *group = this->group())
+ group->removeFromGroup(this);
+ } else {
+ group->addToGroup(this);
+ }
+}
+
+/*!
+ Returns a pointer to this item's parent item. If this item does not have a
+ parent, 0 is returned.
+
+ \sa setParentItem(), children()
+*/
+QGraphicsItem *QGraphicsItem::parentItem() const
+{
+ return d_ptr->parent;
+}
+
+/*!
+ Returns this item's top-level item. The top-level item is the item's
+ topmost ancestor item whose parent is 0. If an item has no parent, its own
+ pointer is returned (i.e., a top-level item is its own top-level item).
+
+ \sa parentItem()
+*/
+QGraphicsItem *QGraphicsItem::topLevelItem() const
+{
+ QGraphicsItem *parent = const_cast<QGraphicsItem *>(this);
+ while (QGraphicsItem *grandPa = parent->parentItem())
+ parent = grandPa;
+ return parent;
+}
+
+/*!
+ \since 4.4
+
+ Returns a pointer to the item's parent widget. The item's parent widget is
+ the closest parent item that is a widget.
+
+ \sa parentItem(), childItems()
+*/
+QGraphicsWidget *QGraphicsItem::parentWidget() const
+{
+ QGraphicsItem *p = parentItem();
+ while (p && !p->isWidget())
+ p = p->parentItem();
+ return (p && p->isWidget()) ? static_cast<QGraphicsWidget *>(p) : 0;
+}
+
+/*!
+ \since 4.4
+
+ Returns a pointer to the item's top level widget (i.e., the item's
+ ancestor whose parent is 0, or whose parent is not a widget), or 0 if this
+ item does not have a top level widget. If the item is its own top level
+ widget, this function returns a pointer to the item itself.
+*/
+QGraphicsWidget *QGraphicsItem::topLevelWidget() const
+{
+ if (const QGraphicsWidget *p = parentWidget())
+ return p->topLevelWidget();
+ return isWidget() ? static_cast<QGraphicsWidget *>(const_cast<QGraphicsItem *>(this)) : 0;
+}
+
+/*!
+ \since 4.4
+
+ Returns the item's window, or 0 if this item does not have a window. If
+ the item is a window, it will return itself. Otherwise it will return the
+ closest ancestor that is a window.
+
+ \sa QGraphicsWidget::isWindow()
+*/
+QGraphicsWidget *QGraphicsItem::window() const
+{
+ if (isWidget() && static_cast<const QGraphicsWidget *>(this)->isWindow())
+ return static_cast<QGraphicsWidget *>(const_cast<QGraphicsItem *>(this));
+ if (QGraphicsWidget *parent = parentWidget())
+ return parent->window();
+ return 0;
+}
+
+/*!
+ Sets this item's parent item to \a parent. If this item already has a
+ parent, it is first removed from the previous parent. If \a parent is 0,
+ this item will become a top-level item.
+
+ Note that this implicitly adds this graphics item to the scene of
+ the parent. You should not \l{QGraphicsScene::addItem()}{add} the
+ item to the scene yourself.
+
+ \sa parentItem(), children()
+*/
+void QGraphicsItem::setParentItem(QGraphicsItem *parent)
+{
+ if (parent == this) {
+ qWarning("QGraphicsItem::setParentItem: cannot assign %p as a parent of itself", this);
+ return;
+ }
+ if (parent == d_ptr->parent)
+ return;
+ QVariant variant;
+ qVariantSetValue<QGraphicsItem *>(variant, parent);
+ parent = qVariantValue<QGraphicsItem *>(itemChange(ItemParentChange, variant));
+ if (parent == d_ptr->parent)
+ return;
+
+ if (QGraphicsWidget *w = d_ptr->isWidget ? static_cast<QGraphicsWidget *>(this) : parentWidget()) {
+ // Update the child focus chain; when reparenting a widget that has a
+ // focus child, ensure that that focus child clears its focus child
+ // chain from our parents before it's reparented.
+ if (QGraphicsWidget *focusChild = w->focusWidget())
+ focusChild->clearFocus();
+ }
+
+ // We anticipate geometry changes
+ prepareGeometryChange();
+
+ if (d_ptr->parent) {
+ // Remove from current parent
+ qt_graphicsitem_removeChild(this, &d_ptr->parent->d_func()->children);
+ qVariantSetValue<QGraphicsItem *>(variant, this);
+ d_ptr->parent->itemChange(ItemChildRemovedChange, variant);
+ }
+
+ if ((d_ptr->parent = parent)) {
+ bool implicitUpdate = false;
+ if (parent->d_func()->scene && parent->d_func()->scene != d_ptr->scene) {
+ // Move this item to its new parent's scene
+ parent->d_func()->scene->addItem(this);
+ implicitUpdate = true;
+ } else if (!parent->d_func()->scene && d_ptr->scene) {
+ // Remove this item from its former scene
+ d_ptr->scene->removeItem(this);
+ }
+
+ d_ptr->parent->d_func()->children << this;
+ qVariantSetValue<QGraphicsItem *>(variant, this);
+ d_ptr->parent->itemChange(ItemChildAddedChange, variant);
+ if (!implicitUpdate)
+ d_ptr->updateHelper();
+
+ // Inherit ancestor flags from the new parent.
+ d_ptr->updateAncestorFlag(QGraphicsItem::GraphicsItemFlag(-1));
+ d_ptr->updateAncestorFlag(ItemClipsChildrenToShape);
+ d_ptr->updateAncestorFlag(ItemIgnoresTransformations);
+
+ // Update item visible / enabled.
+ if (d_ptr->parent->isVisible() != d_ptr->visible) {
+ if (!d_ptr->parent->isVisible() || !d_ptr->explicitlyHidden)
+ d_ptr->setVisibleHelper(d_ptr->parent->isVisible(), /* explicit = */ false, /* update = */ !implicitUpdate);
+ }
+ if (d_ptr->parent->isEnabled() != d_ptr->enabled) {
+ if (!d_ptr->parent->isEnabled() || !d_ptr->explicitlyDisabled)
+ d_ptr->setEnabledHelper(d_ptr->parent->isEnabled(), /* explicit = */ false, /* update = */ !implicitUpdate);
+ }
+
+ } else {
+ // Inherit ancestor flags from the new parent.
+ d_ptr->updateAncestorFlag(QGraphicsItem::GraphicsItemFlag(-1));
+ d_ptr->updateAncestorFlag(ItemClipsChildrenToShape);
+ d_ptr->updateAncestorFlag(ItemIgnoresTransformations);
+
+ // Update item visible / enabled.
+ if (!d_ptr->visible && !d_ptr->explicitlyHidden)
+ d_ptr->setVisibleHelper(true, /* explicit = */ false);
+ if (!d_ptr->enabled && !d_ptr->explicitlyDisabled)
+ d_ptr->setEnabledHelper(true, /* explicit = */ false);
+
+ d_ptr->updateHelper();
+ }
+
+ if (d_ptr->scene) {
+ // Invalidate any sort caching; arrival of a new item means we need to
+ // resort.
+ d_ptr->scene->d_func()->invalidateSortCache();
+ }
+
+ // Resolve opacity.
+ if (QGraphicsItem *p = d_ptr->parent)
+ d_ptr->resolveEffectiveOpacity(p->effectiveOpacity());
+ else
+ d_ptr->resolveEffectiveOpacity(1.0);
+
+ // Resolve depth.
+ d_ptr->resolveDepth(parent ? parent->d_ptr->depth : -1);
+
+ // Invalidate transform cache.
+ d_ptr->invalidateSceneTransformCache();
+
+ // Deliver post-change notification
+ itemChange(QGraphicsItem::ItemParentHasChanged, qVariantFromValue<QGraphicsItem *>(parent));
+}
+
+/*!
+ \obsolete
+
+ Use childItems() instead.
+
+ \sa setParentItem()
+*/
+QList<QGraphicsItem *> QGraphicsItem::children() const
+{
+ return childItems();
+}
+
+/*!
+ \since 4.4
+
+ Returns a list of this item's children. The items are returned in no
+ particular order.
+
+ \sa setParentItem()
+*/
+QList<QGraphicsItem *> QGraphicsItem::childItems() const
+{
+ return d_ptr->children;
+}
+
+/*!
+ \since 4.4
+ Returns true if this item is a widget (i.e., QGraphicsWidget); otherwise,
+ returns false.
+*/
+bool QGraphicsItem::isWidget() const
+{
+ return d_ptr->isWidget;
+}
+
+/*!
+ \since 4.4
+ Returns true if the item is a QGraphicsWidget window, otherwise returns
+ false.
+
+ \sa QGraphicsWidget::windowFlags()
+*/
+bool QGraphicsItem::isWindow() const
+{
+ return isWidget() && (static_cast<const QGraphicsWidget *>(this)->windowType() & Qt::Window);
+}
+
+/*!
+ Returns this item's flags. The flags describe what configurable features
+ of the item are enabled and not. For example, if the flags include
+ ItemIsFocusable, the item can accept input focus.
+
+ By default, no flags are enabled.
+
+ \sa setFlags(), setFlag()
+*/
+QGraphicsItem::GraphicsItemFlags QGraphicsItem::flags() const
+{
+ return GraphicsItemFlags(d_ptr->flags);
+}
+
+/*!
+ If \a enabled is true, the item flag \a flag is enabled; otherwise, it is
+ disabled.
+
+ \sa flags(), setFlags()
+*/
+void QGraphicsItem::setFlag(GraphicsItemFlag flag, bool enabled)
+{
+ if (enabled)
+ setFlags(flags() | flag);
+ else
+ setFlags(flags() & ~flag);
+}
+
+/*!
+ \internal
+
+ Sets the flag \a flag on \a item and all its children, to \a enabled.
+*/
+static void _q_qgraphicsItemSetFlag(QGraphicsItem *item, QGraphicsItem::GraphicsItemFlag flag,
+ bool enabled)
+{
+ if (item->flags() & flag) {
+ // If this item already has the correct flag set, we don't have to
+ // propagate it.
+ return;
+ }
+ item->setFlag(flag, enabled);
+ foreach (QGraphicsItem *child, item->children())
+ _q_qgraphicsItemSetFlag(child, flag, enabled);
+}
+
+/*!
+ Sets the item flags to \a flags. All flags in \a flags are enabled; all
+ flags not in \a flags are disabled.
+
+ If the item had focus and \a flags does not enable ItemIsFocusable, the
+ item loses focus as a result of calling this function. Similarly, if the
+ item was selected, and \a flags does not enabled ItemIsSelectable, the
+ item is automatically unselected.
+
+ By default, no flags are enabled.
+
+ \sa flags(), setFlag()
+*/
+void QGraphicsItem::setFlags(GraphicsItemFlags flags)
+{
+ // Notify change and check for adjustment.
+ if (quint32(d_ptr->flags) == quint32(flags))
+ return;
+ flags = GraphicsItemFlags(itemChange(ItemFlagsChange, quint32(flags)).toUInt());
+ if (quint32(d_ptr->flags) == quint32(flags))
+ return;
+
+ // Flags that alter the geometry of the item (or its children).
+ int geomChangeFlagsMask = (ItemClipsChildrenToShape | ItemClipsToShape | ItemIgnoresTransformations);
+ bool fullUpdate = (flags & geomChangeFlagsMask) != (d_ptr->flags & geomChangeFlagsMask);
+ if (fullUpdate)
+ d_ptr->fullUpdateHelper();
+
+ // Keep the old flags to compare the diff.
+ GraphicsItemFlags oldFlags = this->flags();
+
+ // Update flags.
+ d_ptr->flags = flags;
+
+ // Reresolve effective opacity if the opacity flags change.
+ static const quint32 opacityFlagsMask = ItemIgnoresParentOpacity | ItemDoesntPropagateOpacityToChildren;
+ if ((flags & opacityFlagsMask) != (oldFlags & opacityFlagsMask)) {
+ if (QGraphicsItem *p = d_ptr->parent)
+ d_ptr->resolveEffectiveOpacity(p->effectiveOpacity());
+ else
+ d_ptr->resolveEffectiveOpacity(1.0);
+ }
+
+ if (!(d_ptr->flags & ItemIsFocusable) && hasFocus()) {
+ // Clear focus on the item if it has focus when the focusable flag
+ // is unset.
+ clearFocus();
+ }
+
+ if (!(d_ptr->flags & ItemIsSelectable) && isSelected()) {
+ // Unselect the item if it is selected when the selectable flag is
+ // unset.
+ setSelected(false);
+ }
+
+ if ((flags & ItemClipsChildrenToShape) != (oldFlags & ItemClipsChildrenToShape)) {
+ // Item children clipping changes. Propagate the ancestor flag to
+ // all children.
+ d_ptr->updateAncestorFlag(ItemClipsChildrenToShape);
+ }
+
+ if ((flags & ItemIgnoresTransformations) != (oldFlags & ItemIgnoresTransformations)) {
+ // Item children clipping changes. Propagate the ancestor flag to
+ // all children.
+ d_ptr->updateAncestorFlag(ItemIgnoresTransformations);
+ }
+
+ // ### Why updateHelper?
+ d_ptr->updateHelper();
+
+ // Notify change.
+ itemChange(ItemFlagsHaveChanged, quint32(flags));
+}
+
+/*!
+ \since 4.4
+ Returns the cache mode for this item. The default mode is NoCache (i.e.,
+ cache is disabled and all painting is immediate).
+
+ \sa setCacheMode()
+*/
+QGraphicsItem::CacheMode QGraphicsItem::cacheMode() const
+{
+ return QGraphicsItem::CacheMode(d_ptr->cacheMode);
+}
+
+/*!
+ \since 4.4
+ Sets the item's cache mode to \a mode.
+
+ The optional \a logicalCacheSize argument is used only by
+ ItemCoordinateCache mode, and describes the resolution of the cache
+ buffer; if \a logicalCacheSize is (100, 100), QGraphicsItem will fit the
+ item into 100x100 pixels in graphics memory, regardless of the logical
+ size of the item itself. By default QGraphicsItem uses the size of
+ boundingRect(). For all other cache modes than ItemCoordinateCache, \a
+ logicalCacheSize is ignored.
+
+ Caching can speed up rendering if your item spends a significant time
+ redrawing itself. In some cases the cache can also slow down rendering, in
+ particular when the item spends less time redrawing than QGraphicsItem
+ spends redrawing from the cache. When enabled, the item's paint() function
+ will be called only once for each call to update(); for any subsequent
+ repaint requests, the Graphics View framework will redraw from the
+ cache. This approach works particularly well with QGLWidget, which stores
+ all the cache as OpenGL textures.
+
+ Be aware that QPixmapCache's cache limit may need to be changed to obtain
+ optimal performance.
+
+ You can read more about the different cache modes in the CacheMode
+ documentation.
+
+ \sa CacheMode, QPixmapCache::setCacheLimit()
+*/
+void QGraphicsItem::setCacheMode(CacheMode mode, const QSize &logicalCacheSize)
+{
+ CacheMode lastMode = CacheMode(d_ptr->cacheMode);
+ d_ptr->cacheMode = mode;
+ bool noVisualChange = (mode == NoCache && lastMode == NoCache)
+ || (mode == NoCache && lastMode == DeviceCoordinateCache)
+ || (mode == DeviceCoordinateCache && lastMode == NoCache);
+ if (mode == NoCache) {
+ d_ptr->removeExtraItemCache();
+ } else {
+ QGraphicsItemCache *cache = d_ptr->extraItemCache();
+
+ // Reset old cache
+ cache->purge();
+
+ if (mode == ItemCoordinateCache) {
+ if (cache->key.isEmpty()) {
+ // Generate new simple pixmap cache key.
+ QString tmp;
+ tmp.sprintf("qgv-%p", this);
+ cache->key = tmp;
+ }
+ if (lastMode == mode && cache->fixedSize == logicalCacheSize)
+ noVisualChange = true;
+ cache->fixedSize = logicalCacheSize;
+ }
+ }
+ if (!noVisualChange)
+ update();
+}
+
+#ifndef QT_NO_TOOLTIP
+/*!
+ Returns the item's tool tip, or an empty QString if no tool tip has been
+ set.
+
+ \sa setToolTip(), QToolTip
+*/
+QString QGraphicsItem::toolTip() const
+{
+ return d_ptr->extra(QGraphicsItemPrivate::ExtraToolTip).toString();
+}
+
+/*!
+ Sets the item's tool tip to \a toolTip. If \a toolTip is empty, the item's
+ tool tip is cleared.
+
+ \sa toolTip(), QToolTip
+*/
+void QGraphicsItem::setToolTip(const QString &toolTip)
+{
+ QString newCursor = itemChange(ItemToolTipChange, toolTip).toString();
+ d_ptr->setExtra(QGraphicsItemPrivate::ExtraToolTip, toolTip);
+ itemChange(ItemToolTipHasChanged, toolTip);
+}
+#endif // QT_NO_TOOLTIP
+
+#ifndef QT_NO_CURSOR
+/*!
+ Returns the current cursor shape for the item. The mouse cursor
+ will assume this shape when it's over this item. See the \link
+ Qt::CursorShape list of predefined cursor objects\endlink for a
+ range of useful shapes.
+
+ An editor item might want to use an I-beam cursor:
+
+ \snippet doc/src/snippets/code/src_gui_graphicsview_qgraphicsitem.cpp 2
+
+ If no cursor has been set, the parent's cursor is used.
+
+ \sa setCursor(), hasCursor(), unsetCursor(), QWidget::cursor,
+ QApplication::overrideCursor()
+*/
+QCursor QGraphicsItem::cursor() const
+{
+ return qVariantValue<QCursor>(d_ptr->extra(QGraphicsItemPrivate::ExtraCursor));
+}
+
+/*!
+ Sets the current cursor shape for the item to \a cursor. The mouse cursor
+ will assume this shape when it's over this item. See the \link
+ Qt::CursorShape list of predefined cursor objects\endlink for a range of
+ useful shapes.
+
+ An editor item might want to use an I-beam cursor:
+
+ \snippet doc/src/snippets/code/src_gui_graphicsview_qgraphicsitem.cpp 3
+
+ If no cursor has been set, the cursor of the item beneath is used.
+
+ \sa cursor(), hasCursor(), unsetCursor(), QWidget::cursor,
+ QApplication::overrideCursor()
+*/
+void QGraphicsItem::setCursor(const QCursor &cursor)
+{
+ QCursor newCursor = qVariantValue<QCursor>(itemChange(ItemCursorChange,
+ qVariantFromValue<QCursor>(cursor)));
+ d_ptr->setExtra(QGraphicsItemPrivate::ExtraCursor, newCursor);
+ d_ptr->hasCursor = 1;
+ if (d_ptr->scene) {
+ foreach (QGraphicsView *view, d_ptr->scene->views()) {
+ // Note: Some of this logic is duplicated in QGraphicsView's mouse events.
+ if (view->underMouse()) {
+ foreach (QGraphicsItem *itemUnderCursor, view->items(view->mapFromGlobal(QCursor::pos()))) {
+ if (itemUnderCursor->hasCursor()) {
+ QMetaObject::invokeMethod(view, "_q_setViewportCursor",
+ Q_ARG(QCursor, itemUnderCursor->cursor()));
+ break;
+ }
+ }
+ break;
+ }
+ }
+ }
+ itemChange(ItemCursorHasChanged, qVariantFromValue<QCursor>(newCursor));
+}
+
+/*!
+ Returns true if this item has a cursor set; otherwise, false is returned.
+
+ By default, items don't have any cursor set. cursor() will return a
+ standard pointing arrow cursor.
+
+ \sa unsetCursor()
+*/
+bool QGraphicsItem::hasCursor() const
+{
+ return d_ptr->hasCursor;
+}
+
+/*!
+ Clears the cursor from this item.
+
+ \sa hasCursor(), setCursor()
+*/
+void QGraphicsItem::unsetCursor()
+{
+ d_ptr->unsetExtra(QGraphicsItemPrivate::ExtraCursor);
+ d_ptr->hasCursor = 0;
+ if (d_ptr->scene) {
+ foreach (QGraphicsView *view, d_ptr->scene->views()) {
+ if (view->underMouse() && view->itemAt(view->mapFromGlobal(QCursor::pos())) == this) {
+ QMetaObject::invokeMethod(view, "_q_unsetViewportCursor");
+ break;
+ }
+ }
+ }
+}
+
+#endif // QT_NO_CURSOR
+
+/*!
+ Returns true if the item is visible; otherwise, false is returned.
+
+ Note that the item's general visibility is unrelated to whether or not it
+ is actually being visualized by a QGraphicsView.
+
+ \sa setVisible()
+*/
+bool QGraphicsItem::isVisible() const
+{
+ return d_ptr->visible;
+}
+
+/*!
+ \since 4.4
+ Returns true if the item is visible to \a parent; otherwise, false is
+ returned. \a parent can be 0, in which case this function will return
+ whether the item is visible to the scene or not.
+
+ An item may not be visible to its ancestors even if isVisible() is true. If
+ any ancestor is hidden, the item itself will be implicitly hidden, in which
+ case this function will return false.
+
+ \sa isVisible(), setVisible()
+*/
+bool QGraphicsItem::isVisibleTo(const QGraphicsItem *parent) const
+{
+ if (!d_ptr->visible)
+ return false;
+ if (parent == this)
+ return true;
+ if (parentItem() && parentItem()->isVisibleTo(parent))
+ return true;
+ if (!parent && !parentItem())
+ return true;
+ return false;
+}
+
+/*!
+ \internal
+
+ Sets this item's visibility to \a newVisible. If \a explicitly is true,
+ this item will be "explicitly" \a newVisible; otherwise, it.. will not be.
+*/
+void QGraphicsItemPrivate::setVisibleHelper(bool newVisible, bool explicitly, bool update)
+{
+ Q_Q(QGraphicsItem);
+
+ // Update explicit bit.
+ if (explicitly)
+ explicitlyHidden = newVisible ? 0 : 1;
+
+ // Check if there's nothing to do.
+ if (visible == quint32(newVisible))
+ return;
+
+ // Modify the property.
+ newVisible = q_ptr->itemChange(QGraphicsItem::ItemVisibleChange, quint32(newVisible)).toBool();
+ if (visible == quint32(newVisible))
+ return;
+ visible = newVisible;
+
+ // Schedule redrawing
+ if (update) {
+ QGraphicsItemCache *c = (QGraphicsItemCache *)qVariantValue<void *>(extra(ExtraCacheData));
+ if (c)
+ c->purge();
+ updateHelper(QRectF(), /* force = */ true);
+ }
+
+ // Certain properties are dropped as an item becomes invisible.
+ if (!newVisible) {
+ if (scene) {
+ if (scene->d_func()->mouseGrabberItems.contains(q))
+ q->ungrabMouse();
+ if (scene->d_func()->keyboardGrabberItems.contains(q))
+ q->ungrabKeyboard();
+ }
+ if (q_ptr->hasFocus() && scene) {
+ // Hiding the closest non-window ancestor of the focus item
+ QGraphicsItem *focusItem = scene->focusItem();
+ bool clear = true;
+ if (isWidget && !focusItem->isWindow()) {
+ do {
+ if (focusItem == q_ptr) {
+ clear = !static_cast<QGraphicsWidget *>(q_ptr)->focusNextPrevChild(true);
+ break;
+ }
+ } while ((focusItem = focusItem->parentWidget()) && !focusItem->isWindow());
+ }
+ if (clear)
+ q_ptr->clearFocus();
+ }
+ if (q_ptr->isSelected())
+ q_ptr->setSelected(false);
+ } else {
+ if (isWidget && scene) {
+ QGraphicsWidget *widget = static_cast<QGraphicsWidget *>(q_ptr);
+ if (widget->windowType() == Qt::Popup)
+ scene->d_func()->addPopup(widget);
+ }
+ }
+
+ // Update children with explicitly = false.
+ foreach (QGraphicsItem *child, children) {
+ if (!newVisible || !child->d_ptr->explicitlyHidden)
+ child->d_ptr->setVisibleHelper(newVisible, false);
+ }
+
+ // Enable subfocus
+ if (newVisible && isWidget) {
+ QGraphicsWidget *widget = static_cast<QGraphicsWidget *>(q_ptr);
+ QGraphicsWidget *fw = widget->focusWidget();
+ if (fw && fw != scene->focusItem())
+ scene->setFocusItem(fw);
+ }
+
+ // Deliver post-change notification.
+ q_ptr->itemChange(QGraphicsItem::ItemVisibleHasChanged, quint32(visible));
+}
+
+/*!
+ If \a visible is true, the item is made visible. Otherwise, the item is
+ made invisible. Invisible items are not painted, nor do they receive any
+ events. In particular, mouse events pass right through invisible items,
+ and are delivered to any item that may be behind. Invisible items are also
+ unselectable, they cannot take input focus, and are not detected by
+ QGraphicsScene's item location functions.
+
+ If an item becomes invisible while grabbing the mouse, (i.e., while it is
+ receiving mouse events,) it will automatically lose the mouse grab, and
+ the grab is not regained by making the item visible again; it must receive
+ a new mouse press to regain the mouse grab.
+
+ Similarly, an invisible item cannot have focus, so if the item has focus
+ when it becomes invisible, it will lose focus, and the focus is not
+ regained by simply making the item visible again.
+
+ If you hide a parent item, all its children will also be hidden. If you
+ show a parent item, all children will be shown, unless they have been
+ explicitly hidden (i.e., if you call setVisible(false) on a child, it will
+ not be reshown even if its parent is hidden, and then shown again).
+
+ Items are visible by default; it is unnecessary to call
+ setVisible() on a new item.
+
+ \sa isVisible(), show(), hide()
+*/
+void QGraphicsItem::setVisible(bool visible)
+{
+ d_ptr->setVisibleHelper(visible, /* explicit = */ true);
+}
+
+/*!
+ \fn void QGraphicsItem::hide()
+
+ Hides the item. (Items are visible by default.)
+
+ This convenience function is equivalent to calling \c setVisible(false).
+
+ \sa show(), setVisible()
+*/
+
+/*!
+ \fn void QGraphicsItem::show()
+
+ Shows the item. (Items are visible by default.)
+
+ This convenience function is equivalent to calling \c setVisible(true).
+
+ \sa hide(), setVisible()
+*/
+
+/*!
+ Returns true if the item is enabled; otherwise, false is returned.
+
+ \sa setEnabled()
+*/
+bool QGraphicsItem::isEnabled() const
+{
+ return d_ptr->enabled;
+}
+
+/*!
+ \internal
+
+ Sets this item's visibility to \a newEnabled. If \a explicitly is true,
+ this item will be "explicitly" \a newEnabled; otherwise, it.. will not be.
+*/
+void QGraphicsItemPrivate::setEnabledHelper(bool newEnabled, bool explicitly, bool update)
+{
+ // Update explicit bit.
+ if (explicitly)
+ explicitlyDisabled = newEnabled ? 0 : 1;
+
+ // Check if there's nothing to do.
+ if (enabled == quint32(newEnabled))
+ return;
+
+ // Certain properties are dropped when an item is disabled.
+ if (!newEnabled) {
+ if (scene && scene->mouseGrabberItem() == q_ptr)
+ q_ptr->ungrabMouse();
+ if (q_ptr->hasFocus()) {
+ // Disabling the closest non-window ancestor of the focus item
+ // causes focus to pop to the next item, otherwise it's cleared.
+ QGraphicsItem *focusItem = scene->focusItem();
+ bool clear = true;
+ if (isWidget && !focusItem->isWindow() && q_ptr->isAncestorOf(focusItem)) {
+ do {
+ if (focusItem == q_ptr) {
+ clear = !static_cast<QGraphicsWidget *>(q_ptr)->focusNextPrevChild(true);
+ break;
+ }
+ } while ((focusItem = focusItem->parentWidget()) && !focusItem->isWindow());
+ }
+ if (clear)
+ q_ptr->clearFocus();
+ }
+ if (q_ptr->isSelected())
+ q_ptr->setSelected(false);
+ }
+
+ // Modify the property.
+ enabled = q_ptr->itemChange(QGraphicsItem::ItemEnabledChange, quint32(newEnabled)).toBool();
+
+ // Schedule redraw.
+ if (update)
+ updateHelper();
+
+ foreach (QGraphicsItem *child, children) {
+ if (!newEnabled || !child->d_ptr->explicitlyDisabled)
+ child->d_ptr->setEnabledHelper(newEnabled, /* explicitly = */ false);
+ }
+
+ // Deliver post-change notification.
+ q_ptr->itemChange(QGraphicsItem::ItemEnabledHasChanged, quint32(enabled));
+}
+
+/*!
+ If \a enabled is true, the item is enabled; otherwise, it is disabled.
+
+ Disabled items are visible, but they do not receive any events, and cannot
+ take focus nor be selected. Mouse events are discarded; they are not
+ propagated unless the item is also invisible, or if it does not accept
+ mouse events (see acceptedMouseButtons()). A disabled item cannot become the
+ mouse grabber, and as a result of this, an item loses the grab if it
+ becomes disabled when grabbing the mouse, just like it loses focus if it
+ had focus when it was disabled.
+
+ Disabled items are traditionally drawn using grayed-out colors (see \l
+ QPalette::Disabled).
+
+ If you disable a parent item, all its children will also be disabled. If
+ you enable a parent item, all children will be enabled, unless they have
+ been explicitly disabled (i.e., if you call setEnabled(false) on a child,
+ it will not be reenabled if its parent is disabled, and then enabled
+ again).
+
+ Items are enabled by default.
+
+ \note If you install an event filter, you can still intercept events
+ before they are delivered to items; this mechanism disregards the item's
+ enabled state.
+
+ \sa isEnabled()
+*/
+void QGraphicsItem::setEnabled(bool enabled)
+{
+ d_ptr->setEnabledHelper(enabled, /* explicitly = */ true);
+}
+
+/*!
+ Returns true if this item is selected; otherwise, false is returned.
+
+ Items that are in a group inherit the group's selected state.
+
+ Items are not selected by default.
+
+ \sa setSelected(), QGraphicsScene::setSelectionArea()
+*/
+bool QGraphicsItem::isSelected() const
+{
+ if (QGraphicsItemGroup *group = this->group())
+ return group->isSelected();
+ return d_ptr->selected;
+}
+
+/*!
+ If \a selected is true and this item is selectable, this item is selected;
+ otherwise, it is unselected.
+
+ If the item is in a group, the whole group's selected state is toggled by
+ this function. If the group is selected, all items in the group are also
+ selected, and if the group is not selected, no item in the group is
+ selected.
+
+ Only visible, enabled, selectable items can be selected. If \a selected
+ is true and this item is either invisible or disabled or unselectable,
+ this function does nothing.
+
+ By default, items cannot be selected. To enable selection, set the
+ ItemIsSelectable flag.
+
+ This function is provided for convenience, allowing individual toggling of
+ the selected state of an item. However, a more common way of selecting
+ items is to call QGraphicsScene::setSelectionArea(), which will call this
+ function for all visible, enabled, and selectable items within a specified
+ area on the scene.
+
+ \sa isSelected(), QGraphicsScene::selectedItems()
+*/
+void QGraphicsItem::setSelected(bool selected)
+{
+ if (QGraphicsItemGroup *group = this->group()) {
+ group->setSelected(selected);
+ return;
+ }
+
+ if (!(d_ptr->flags & ItemIsSelectable) || !d_ptr->enabled || !d_ptr->visible)
+ selected = false;
+ if (d_ptr->selected == selected)
+ return;
+ bool newSelected = itemChange(ItemSelectedChange, quint32(selected)).toBool();
+ if (d_ptr->selected == newSelected)
+ return;
+ d_ptr->selected = newSelected;
+
+ d_ptr->updateHelper();
+
+ if (d_ptr->scene) {
+ QGraphicsScenePrivate *sceneD = d_ptr->scene->d_func();
+ if (selected) {
+ sceneD->selectedItems << this;
+ } else {
+ // QGraphicsScene::selectedItems() lazily pulls out all items that are
+ // no longer selected.
+ }
+ if (!sceneD->selectionChanging)
+ emit d_ptr->scene->selectionChanged();
+ }
+
+ // Deliver post-change notification.
+ itemChange(QGraphicsItem::ItemSelectedHasChanged, quint32(d_ptr->selected));
+}
+
+/*!
+ \since 4.5
+
+ Returns this item's local opacity, which is between 0.0 (transparent) and
+ 1.0 (opaque). This value is combined with parent and ancestor values into
+ the effectiveOpacity(). The effective opacity decides how the item is
+ rendered.
+
+ The opacity property decides the state of the painter passed to the
+ paint() function. If the item is cached, i.e., ItemCoordinateCache or
+ DeviceCoordinateCache, the effective property will be applied to the item's
+ cache as it is rendered.
+
+ The default opacity is 1.0; fully opaque.
+
+ \sa setOpacity(), paint(), ItemIgnoresParentOpacity,
+ ItemDoesntPropagateOpacityToChildren
+*/
+qreal QGraphicsItem::opacity() const
+{
+ if (d_ptr->hasOpacity) {
+ QVariant o = d_ptr->extra(QGraphicsItemPrivate::ExtraOpacity);
+ if (!o.isNull())
+ return o.toDouble();
+ }
+ return qreal(1.0);
+}
+
+/*!
+ \since 4.5
+
+ Returns this item's \e effective opacity, which is between 0.0
+ (transparent) and 1.0 (opaque). This value is a combination of this item's
+ local opacity, and its parent and ancestors' opacities. The effective
+ opacity decides how the item is rendered.
+
+ \sa opacity(), setOpacity(), paint(), ItemIgnoresParentOpacity,
+ ItemDoesntPropagateOpacityToChildren
+*/
+qreal QGraphicsItem::effectiveOpacity() const
+{
+ QVariant effectiveOpacity = d_ptr->extra(QGraphicsItemPrivate::ExtraEffectiveOpacity);
+ return effectiveOpacity.isNull() ? qreal(1.0) : qreal(effectiveOpacity.toDouble());
+}
+
+/*!
+ \since 4.5
+
+ Sets this item's local \a opacity, between 0.0 (transparent) and 1.0
+ (opaque). The item's local opacity is combined with parent and ancestor
+ opacities into the effectiveOpacity().
+
+ By default, opacity propagates from parent to child, so if a parent's
+ opacity is 0.5 and the child is also 0.5, the child's effective opacity
+ will be 0.25.
+
+ The opacity property decides the state of the painter passed to the
+ paint() function. If the item is cached, i.e., ItemCoordinateCache or
+ DeviceCoordinateCache, the effective property will be applied to the
+ item's cache as it is rendered.
+
+ There are two item flags that affect how the item's opacity is combined
+ with the parent: ItemIgnoresParentOpacity and
+ ItemDoesntPropagateOpacityToChildren.
+
+ \sa opacity(), effectiveOpacity()
+*/
+void QGraphicsItem::setOpacity(qreal opacity)
+{
+ // Notify change.
+ qreal newOpacity = itemChange(ItemOpacityChange, double(opacity)).toDouble();
+
+ // Normalize.
+ newOpacity = qBound<qreal>(0.0, newOpacity, 1.0);
+
+ // No change? Done.
+ if (qFuzzyCompare(newOpacity, this->opacity()))
+ return;
+
+ // Assign local opacity.
+ if (qFuzzyCompare(newOpacity, qreal(1.0))) {
+ // Opaque, unset opacity.
+ d_ptr->hasOpacity = 0;
+ d_ptr->unsetExtra(QGraphicsItemPrivate::ExtraOpacity);
+ } else {
+ d_ptr->hasOpacity = 1;
+ d_ptr->setExtra(QGraphicsItemPrivate::ExtraOpacity, double(newOpacity));
+ }
+
+ // Resolve effective opacity.
+ if (QGraphicsItem *p = d_ptr->parent)
+ d_ptr->resolveEffectiveOpacity(p->effectiveOpacity());
+ else
+ d_ptr->resolveEffectiveOpacity(1.0);
+
+ // Notify change.
+ itemChange(ItemOpacityHasChanged, newOpacity);
+
+ // Update.
+ d_ptr->fullUpdateHelper();
+}
+
+/*!
+ Returns true if this item can accept drag and drop events; otherwise,
+ returns false. By default, items do not accept drag and drop events; items
+ are transparent to drag and drop.
+
+ \sa setAcceptDrops()
+*/
+bool QGraphicsItem::acceptDrops() const
+{
+ return d_ptr->acceptDrops;
+}
+
+/*!
+ If \a on is true, this item will accept drag and drop events; otherwise,
+ it is transparent for drag and drop events. By default, items do not
+ accept drag and drop events.
+
+ \sa acceptDrops()
+*/
+void QGraphicsItem::setAcceptDrops(bool on)
+{
+ d_ptr->acceptDrops = on;
+}
+
+/*!
+ Returns the mouse buttons that this item accepts mouse events for. By
+ default, all mouse buttons are accepted.
+
+ If an item accepts a mouse button, it will become the mouse
+ grabber item when a mouse press event is delivered for that mouse
+ button. However, if the item does not accept the button,
+ QGraphicsScene will forward the mouse events to the first item
+ beneath it that does.
+
+ \sa setAcceptedMouseButtons(), mousePressEvent()
+*/
+Qt::MouseButtons QGraphicsItem::acceptedMouseButtons() const
+{
+ return Qt::MouseButtons(d_ptr->acceptedMouseButtons);
+}
+
+/*!
+ Sets the mouse \a buttons that this item accepts mouse events for.
+
+ By default, all mouse buttons are accepted. If an item accepts a
+ mouse button, it will become the mouse grabber item when a mouse
+ press event is delivered for that button. However, if the item
+ does not accept the mouse button, QGraphicsScene will forward the
+ mouse events to the first item beneath it that does.
+
+ To disable mouse events for an item (i.e., make it transparent for mouse
+ events), call setAcceptedMouseButtons(0).
+
+ \sa acceptedMouseButtons(), mousePressEvent()
+*/
+void QGraphicsItem::setAcceptedMouseButtons(Qt::MouseButtons buttons)
+{
+ if (Qt::MouseButtons(d_ptr->acceptedMouseButtons) != buttons) {
+ if (buttons == 0 && d_ptr->scene && d_ptr->scene->mouseGrabberItem() == this
+ && d_ptr->scene->d_func()->lastMouseGrabberItemHasImplicitMouseGrab) {
+ ungrabMouse();
+ }
+ d_ptr->acceptedMouseButtons = quint32(buttons);
+ }
+}
+
+/*!
+ \since 4.4
+
+ Returns true if an item accepts hover events
+ (QGraphicsSceneHoverEvent); otherwise, returns false. By default,
+ items do not accept hover events.
+
+ \sa setAcceptedMouseButtons()
+*/
+bool QGraphicsItem::acceptHoverEvents() const
+{
+ return d_ptr->acceptsHover;
+}
+
+/*!
+ \obsolete
+
+ Call acceptHoverEvents() instead.
+*/
+bool QGraphicsItem::acceptsHoverEvents() const
+{
+ return d_ptr->acceptsHover;
+}
+
+/*!
+ \since 4.4
+
+ If \a enabled is true, this item will accept hover events;
+ otherwise, it will ignore them. By default, items do not accept
+ hover events.
+
+ Hover events are delivered when there is no current mouse grabber
+ item. They are sent when the mouse cursor enters an item, when it
+ moves around inside the item, and when the cursor leaves an
+ item. Hover events are commonly used to highlight an item when
+ it's entered, and for tracking the mouse cursor as it hovers over
+ the item (equivalent to QWidget::mouseTracking).
+
+ Parent items receive hover enter events before their children, and
+ leave events after their children. The parent does not receive a
+ hover leave event if the cursor enters a child, though; the parent
+ stays "hovered" until the cursor leaves its area, including its
+ children's areas.
+
+ If a parent item handles child events (setHandlesChildEvents()), it will
+ receive hover move, drag move, and drop events as the cursor passes
+ through its children, but it does not receive hover enter and hover leave,
+ nor drag enter and drag leave events on behalf of its children.
+
+ A QGraphicsWidget with window decorations will accept hover events
+ regardless of the value of acceptHoverEvents().
+
+ \sa acceptHoverEvents(), hoverEnterEvent(), hoverMoveEvent(),
+ hoverLeaveEvent()
+*/
+void QGraphicsItem::setAcceptHoverEvents(bool enabled)
+{
+ d_ptr->acceptsHover = quint32(enabled);
+}
+
+/*!
+ \obsolete
+
+ Use setAcceptHoverEvents(\a enabled) instead.
+*/
+void QGraphicsItem::setAcceptsHoverEvents(bool enabled)
+{
+ d_ptr->acceptsHover = quint32(enabled);
+}
+
+/*!
+ Returns true if this item handles child events (i.e., all events
+ intended for any of its children are instead sent to this item);
+ otherwise, false is returned.
+
+ This property is useful for item groups; it allows one item to
+ handle events on behalf of its children, as opposed to its
+ children handling their events individually.
+
+ The default is to return false; children handle their own events.
+ The exception for this is if the item is a QGraphicsItemGroup, then
+ it defaults to return true.
+
+ \sa setHandlesChildEvents()
+*/
+bool QGraphicsItem::handlesChildEvents() const
+{
+ return d_ptr->handlesChildEvents;
+}
+
+/*!
+ If \a enabled is true, this item is set to handle all events for
+ all its children (i.e., all events intented for any of its
+ children are instead sent to this item); otherwise, if \a enabled
+ is false, this item will only handle its own events. The default
+ value is false.
+
+ This property is useful for item groups; it allows one item to
+ handle events on behalf of its children, as opposed to its
+ children handling their events individually.
+
+ If a child item accepts hover events, its parent will receive
+ hover move events as the cursor passes through the child, but it
+ does not receive hover enter and hover leave events on behalf of
+ its child.
+
+ \sa handlesChildEvents()
+*/
+void QGraphicsItem::setHandlesChildEvents(bool enabled)
+{
+ if (d_ptr->handlesChildEvents == enabled)
+ return;
+
+ d_ptr->handlesChildEvents = enabled;
+ d_ptr->updateAncestorFlag(QGraphicsItem::GraphicsItemFlag(-1));
+}
+
+/*!
+ Returns true if this item has keyboard input focus; otherwise, returns
+ false.
+
+ \sa QGraphicsScene::focusItem(), setFocus(), QGraphicsScene::setFocusItem()
+*/
+bool QGraphicsItem::hasFocus() const
+{
+ return (d_ptr->scene && d_ptr->scene->focusItem() == this);
+}
+
+/*!
+ Gives keyboard input focus to this item. The \a focusReason argument will
+ be passed into any focus event generated by this function; it is used to
+ give an explanation of what caused the item to get focus.
+
+ Only items that set the ItemIsFocusable flag can accept keyboard focus.
+
+ If this item is not visible (i.e., isVisible() returns false), not
+ enabled, not associated with a scene, or if it already has input focus,
+ this function will do nothing.
+
+ As a result of calling this function, this item will receive a focus in
+ event with \a focusReason. If another item already has focus, that item
+ will first receive a focus out event indicating that it has lost input
+ focus.
+
+ \sa clearFocus(), hasFocus()
+*/
+void QGraphicsItem::setFocus(Qt::FocusReason focusReason)
+{
+ if (!d_ptr->scene || !isEnabled() || hasFocus() || !(d_ptr->flags & ItemIsFocusable))
+ return;
+ if (isVisible()) {
+ // Visible items immediately gain focus from scene.
+ d_ptr->scene->setFocusItem(this, focusReason);
+ } else if (d_ptr->isWidget) {
+ // Just set up subfocus.
+ static_cast<QGraphicsWidget *>(this)->d_func()->setFocusWidget();
+ }
+}
+
+/*!
+ Takes keyboard input focus from the item.
+
+ If it has focus, a focus out event is sent to this item to tell it that it
+ is about to lose the focus.
+
+ Only items that set the ItemIsFocusable flag, or widgets that set an
+ appropriate focus policy, can accept keyboard focus.
+
+ \sa setFocus(), QGraphicsWidget::focusPolicy
+*/
+void QGraphicsItem::clearFocus()
+{
+ if (!d_ptr->scene)
+ return;
+ if (d_ptr->isWidget) {
+ // Invisible widget items with focus must explicitly clear subfocus.
+ static_cast<QGraphicsWidget *>(this)->d_func()->clearFocusWidget();
+ }
+ if (d_ptr->scene->focusItem() == this) {
+ // If this item has the scene's input focus, clear it.
+ d_ptr->scene->setFocusItem(0);
+ }
+}
+
+/*!
+ \since 4.4
+ Grabs the mouse input.
+
+ This item will receive all mouse events for the scene until any of the
+ following events occurs:
+
+ \list
+ \o The item becomes invisible
+ \o The item is removed from the scene
+ \o The item is deleted
+ \o The item call ungrabMouse()
+ \o Another item calls grabMouse(); the item will regain the mouse grab
+ when the other item calls ungrabMouse().
+ \endlist
+
+ When an item gains the mouse grab, it receives a QEvent::GrabMouse
+ event. When it loses the mouse grab, it receives a QEvent::UngrabMouse
+ event. These events can be used to detect when your item gains or loses
+ the mouse grab through other means than receiving mouse button events.
+
+ It is almost never necessary to explicitly grab the mouse in Qt, as Qt
+ grabs and releases it sensibly. In particular, Qt grabs the mouse when you
+ press a mouse button, and keeps the mouse grabbed until you release the
+ last mouse button. Also, Qt::Popup widgets implicitly call grabMouse()
+ when shown, and ungrabMouse() when hidden.
+
+ Note that only visible items can grab mouse input. Calling grabMouse() on
+ an invisible item has no effect.
+
+ Keyboard events are not affected.
+
+ \sa QGraphicsScene::mouseGrabberItem(), ungrabMouse(), grabKeyboard()
+*/
+void QGraphicsItem::grabMouse()
+{
+ if (!d_ptr->scene) {
+ qWarning("QGraphicsItem::grabMouse: cannot grab mouse without scene");
+ return;
+ }
+ if (!d_ptr->visible) {
+ qWarning("QGraphicsItem::grabMouse: cannot grab mouse while invisible");
+ return;
+ }
+ d_ptr->scene->d_func()->grabMouse(this);
+}
+
+/*!
+ \since 4.4
+ Releases the mouse grab.
+
+ \sa grabMouse(), ungrabKeyboard()
+*/
+void QGraphicsItem::ungrabMouse()
+{
+ if (!d_ptr->scene) {
+ qWarning("QGraphicsItem::ungrabMouse: cannot ungrab mouse without scene");
+ return;
+ }
+ d_ptr->scene->d_func()->ungrabMouse(this);
+}
+
+/*!
+ \since 4.4
+ Grabs the keyboard input.
+
+ The item will receive all keyboard input to the scene until one of the
+ following events occur:
+
+ \list
+ \o The item becomes invisible
+ \o The item is removed from the scene
+ \o The item is deleted
+ \o The item calls ungrabKeyboard()
+ \o Another item calls grabKeyboard(); the item will regain the keyboard grab
+ when the other item calls ungrabKeyboard().
+ \endlist
+
+ When an item gains the keyboard grab, it receives a QEvent::GrabKeyboard
+ event. When it loses the keyboard grab, it receives a
+ QEvent::UngrabKeyboard event. These events can be used to detect when your
+ item gains or loses the keyboard grab through other means than gaining
+ input focus.
+
+ It is almost never necessary to explicitly grab the keyboard in Qt, as Qt
+ grabs and releases it sensibly. In particular, Qt grabs the keyboard when
+ your item gains input focus, and releases it when your item loses input
+ focus, or when the item is hidden.
+
+ Note that only visible items can grab keyboard input. Calling
+ grabKeyboard() on an invisible item has no effect.
+
+ Keyboard events are not affected.
+
+ \sa ungrabKeyboard(), grabMouse(), setFocus()
+*/
+void QGraphicsItem::grabKeyboard()
+{
+ if (!d_ptr->scene) {
+ qWarning("QGraphicsItem::grabKeyboard: cannot grab keyboard without scene");
+ return;
+ }
+ if (!d_ptr->visible) {
+ qWarning("QGraphicsItem::grabKeyboard: cannot grab keyboard while invisible");
+ return;
+ }
+ d_ptr->scene->d_func()->grabKeyboard(this);
+}
+
+/*!
+ \since 4.4
+ Releases the keyboard grab.
+
+ \sa grabKeyboard(), ungrabMouse()
+*/
+void QGraphicsItem::ungrabKeyboard()
+{
+ if (!d_ptr->scene) {
+ qWarning("QGraphicsItem::ungrabKeyboard: cannot ungrab keyboard without scene");
+ return;
+ }
+ d_ptr->scene->d_func()->ungrabKeyboard(this);
+}
+
+/*!
+ Returns the position of the item in parent coordinates. If the item has no
+ parent, its position is given in scene coordinates.
+
+ The position of the item describes its origin (local coordinate
+ (0, 0)) in parent coordinates; this function returns the same as
+ mapToParent(0, 0).
+
+ For convenience, you can also call scenePos() to determine the
+ item's position in scene coordinates, regardless of its parent.
+
+ \sa x(), y(), setPos(), matrix(), {The Graphics View Coordinate System}
+*/
+QPointF QGraphicsItem::pos() const
+{
+ return d_ptr->pos;
+}
+
+/*!
+ \fn QGraphicsItem::x() const
+
+ This convenience function is equivalent to calling pos().x().
+
+ \sa y()
+*/
+
+/*!
+ \fn QGraphicsItem::y() const
+
+ This convenience function is equivalent to calling pos().y().
+
+ \sa x()
+*/
+
+/*!
+ Returns the item's position in scene coordinates. This is
+ equivalent to calling \c mapToScene(0, 0).
+
+ \sa pos(), sceneTransform(), {The Graphics View Coordinate System}
+*/
+QPointF QGraphicsItem::scenePos() const
+{
+ return mapToScene(0, 0);
+}
+
+/*!
+ \internal
+
+ Sets the position \a pos and notifies the change. If \a update is true,
+ the item is also updated; otherwise it is not updated before and after the
+ change.
+*/
+void QGraphicsItemPrivate::setPosHelper(const QPointF &pos, bool update)
+{
+ Q_Q(QGraphicsItem);
+ if (this->pos == pos)
+ return;
+
+ // Notify the item that the position is changing.
+ QPointF newPos = q->itemChange(QGraphicsItem::ItemPositionChange, pos).toPointF();
+ if (newPos == this->pos)
+ return;
+
+ // Update and repositition.
+ if (scene && update) {
+ fullUpdateHelper(true);
+ q->prepareGeometryChange();
+ }
+ this->pos = newPos;
+ invalidateSceneTransformCache();
+
+ // Send post-notification.
+ q->itemChange(QGraphicsItem::ItemPositionHasChanged, newPos);
+}
+
+/*!
+ Sets the position of the item to \a pos, which is in parent
+ coordinates. For items with no parent, \a pos is in scene
+ coordinates.
+
+ The position of the item describes its origin (local coordinate
+ (0, 0)) in parent coordinates.
+
+ \sa pos(), scenePos(), {The Graphics View Coordinate System}
+*/
+void QGraphicsItem::setPos(const QPointF &pos)
+{
+ d_ptr->setPosHelper(pos, /* update = */ true);
+}
+
+/*!
+ \fn void QGraphicsItem::setPos(qreal x, qreal y)
+ \overload
+
+ This convenience function is equivalent to calling setPos(QPointF(\a x, \a
+ y)).
+*/
+
+/*!
+ \fn void QGraphicsItem::moveBy(qreal dx, qreal dy)
+
+ Moves the item by \a dx points horizontally, and \a dy point
+ vertically. This function is equivalent to calling setPos(pos() +
+ QPointF(\a dx, \a dy)).
+*/
+
+/*!
+ If this item is part of a scene that is viewed by a QGraphicsView, this
+ convenience function will attempt to scroll the view to ensure that \a
+ rect is visible inside the view's viewport. If \a rect is a null rect (the
+ default), QGraphicsItem will default to the item's bounding rect. \a xmargin
+ and \a ymargin are the number of pixels the view should use for margins.
+
+ If the specified rect cannot be reached, the contents are scrolled to the
+ nearest valid position.
+
+ If this item is not viewed by a QGraphicsView, this function does nothing.
+
+ \sa QGraphicsView::ensureVisible()
+*/
+void QGraphicsItem::ensureVisible(const QRectF &rect, int xmargin, int ymargin)
+{
+ if (d_ptr->scene) {
+ QRectF sceneRect;
+ if (!rect.isNull())
+ sceneRect = sceneTransform().mapRect(rect);
+ else
+ sceneRect = sceneBoundingRect();
+ foreach (QGraphicsView *view, d_ptr->scene->d_func()->views)
+ view->ensureVisible(sceneRect, xmargin, ymargin);
+ }
+}
+
+/*!
+ \fn void QGraphicsItem::ensureVisible(qreal x, qreal y, qreal w, qreal h,
+ int xmargin = 50, int ymargin = 50)
+
+ This convenience function is equivalent to calling
+ ensureVisible(QRectF(\a x, \a y, \a w, \a h), \a xmargin, \a ymargin):
+*/
+
+/*!
+ \obsolete
+
+ Returns the item's affine transformation matrix. This is a subset or the
+ item's full transformation matrix, and might not represent the item's full
+ transformation.
+
+ Use transform() instead.
+
+ \sa setTransform(), sceneTransform()
+*/
+QMatrix QGraphicsItem::matrix() const
+{
+ return transform().toAffine();
+}
+
+/*!
+ \since 4.3
+
+ Returns this item's transformation matrix. If no matrix has been set, the
+ identity matrix is returned.
+
+ \sa setTransform(), sceneTransform()
+*/
+QTransform QGraphicsItem::transform() const
+{
+ if (!d_ptr->hasTransform)
+ return QTransform();
+ return qVariantValue<QTransform>(d_ptr->extra(QGraphicsItemPrivate::ExtraTransform));
+}
+
+/*!
+ \obsolete
+
+ Use sceneTransform() instead.
+
+ \sa transform(), setTransform(), scenePos(), {The Graphics View Coordinate System}
+*/
+QMatrix QGraphicsItem::sceneMatrix() const
+{
+ return sceneTransform().toAffine();
+}
+
+
+/*!
+ \since 4.3
+
+ Returns this item's scene transformation matrix. This matrix can be used
+ to map coordinates and geometrical shapes from this item's local
+ coordinate system to the scene's coordinate system. To map coordinates
+ from the scene, you must first invert the returned matrix.
+
+ Example:
+
+ \snippet doc/src/snippets/code/src_gui_graphicsview_qgraphicsitem.cpp 4
+
+ Unlike transform(), which returns only an item's local transformation, this
+ function includes the item's (and any parents') position.
+
+ \sa transform(), setTransform(), scenePos(), {The Graphics View Coordinate System}
+*/
+QTransform QGraphicsItem::sceneTransform() const
+{
+ // Check if there's any entry in the transform cache.
+ QGraphicsScenePrivate *sd = d_ptr->scene ? d_ptr->scene->d_func() : 0;
+ int index = d_ptr->sceneTransformIndex;
+ if (sd && index != -1 && sd->validTransforms.testBit(index))
+ return sd->sceneTransformCache[index];
+
+ // Calculate local transform.
+ QTransform m;
+ if (d_ptr->hasTransform) {
+ m = transform();
+ m *= QTransform::fromTranslate(d_ptr->pos.x(), d_ptr->pos.y());
+ } else {
+ // ### ? QTransform::fromTranslate(d_ptr->pos.x(), d_ptr->pos.y())
+ m.translate(d_ptr->pos.x(), d_ptr->pos.y());
+ }
+
+ // Combine with parent and add to cache.
+ if (d_ptr->parent) {
+ m *= d_ptr->parent->sceneTransform();
+ // Don't cache toplevels
+ if (sd) {
+ if (index == -1) {
+ if (!sd->freeSceneTransformSlots.isEmpty()) {
+ index = sd->freeSceneTransformSlots.last();
+ sd->freeSceneTransformSlots.pop_back();
+ } else {
+ index = sd->sceneTransformCache.size();
+ }
+ d_ptr->sceneTransformIndex = index;
+ if (index >= sd->validTransforms.size()) {
+ sd->validTransforms.resize(index + 1);
+ sd->sceneTransformCache.resize(index + 1);
+ }
+ }
+ sd->validTransforms.setBit(index, 1);
+ sd->sceneTransformCache[index] = m;
+ }
+ }
+ return m;
+}
+
+/*!
+ \since 4.3
+
+ Returns this item's device transformation matrix, using \a
+ viewportTransform to map from scene to device coordinates. This matrix can
+ be used to map coordinates and geometrical shapes from this item's local
+ coordinate system to the viewport's (or any device's) coordinate
+ system. To map coordinates from the viewport, you must first invert the
+ returned matrix.
+
+ Example:
+
+ \snippet doc/src/snippets/code/src_gui_graphicsview_qgraphicsitem.cpp 5
+
+ This function is the same as combining this item's scene transform with
+ the view's viewport transform, but it also understands the
+ ItemIgnoresTransformations flag. The device transform can be used to do
+ accurate coordinate mapping (and collision detection) for untransformable
+ items.
+
+ \sa transform(), setTransform(), scenePos(), {The Graphics View Coordinate
+ System}, itemTransform()
+*/
+QTransform QGraphicsItem::deviceTransform(const QTransform &viewportTransform) const
+{
+ // Find the topmost item that ignores view transformations.
+ const QGraphicsItem *untransformedAncestor = this;
+ QList<const QGraphicsItem *> parents;
+ while (untransformedAncestor && ((untransformedAncestor->d_ptr->ancestorFlags
+ & QGraphicsItemPrivate::AncestorIgnoresTransformations))) {
+ parents.prepend(untransformedAncestor);
+ untransformedAncestor = untransformedAncestor->parentItem();
+ }
+
+ if (!untransformedAncestor) {
+ // Assert in debug mode, continue in release.
+ Q_ASSERT_X(untransformedAncestor, "QGraphicsItem::deviceTransform",
+ "Invalid object structure!");
+ return QTransform();
+ }
+
+ // First translate the base untransformable item.
+ QPointF mappedPoint = (untransformedAncestor->sceneTransform() * viewportTransform).map(QPointF(0, 0));
+ QTransform matrix;
+ matrix.translate(mappedPoint.x(), mappedPoint.y());
+ matrix = untransformedAncestor->transform() * matrix;
+
+ // Then transform and translate all children.
+ for (int i = 0; i < parents.size(); ++i) {
+ const QGraphicsItem *parent = parents.at(i);
+ QPointF pos = parent->pos();
+ QTransform moveMatrix;
+ moveMatrix.translate(pos.x(), pos.y());
+ matrix = (parent->transform() * moveMatrix) * matrix;
+ }
+
+ return matrix;
+}
+
+/*!
+ \since 4.5
+
+ Returns a QTransform that maps coordinates from this item to \a other. If
+ \a ok is not null, and if there is no such transform, the boolean pointed
+ to by \a ok will be set to false; otherwise it will be set to true.
+
+ This transform provides an alternative to the mapToItem() or mapFromItem()
+ functions, by returning the appropriate transform so that you can map
+ shapes and coordinates yourself. It also helps you write more efficient
+ code when repeatedly mapping between the same two items.
+
+ \note In rare circumstances, there is no transform that maps between two
+ items.
+
+ \sa mapToItem(), mapFromItem(), deviceTransform()
+*/
+QTransform QGraphicsItem::itemTransform(const QGraphicsItem *other, bool *ok) const
+{
+ // Catch simple cases first.
+ if (other == 0) {
+ qWarning("QGraphicsItem::itemTransform: null pointer passed");
+ return QTransform();
+ }
+ if (other == this) {
+ if (ok)
+ *ok = true;
+ return QTransform();
+ }
+
+ QGraphicsItem *parent = d_ptr->parent;
+ const QGraphicsItem *otherParent = other->d_ptr->parent;
+
+ // This is other's child
+ if (parent == other) {
+ if (ok)
+ *ok = true;
+ const QPointF &itemPos = d_ptr->pos;
+ if (d_ptr->hasTransform)
+ return transform() * QTransform::fromTranslate(itemPos.x(), itemPos.y());
+ return QTransform::fromTranslate(itemPos.x(), itemPos.y());
+ }
+
+ // This is other's parent
+ if (otherParent == this) {
+ const QPointF &otherPos = other->d_ptr->pos;
+ if (other->d_ptr->hasTransform) {
+ QTransform otherToParent = other->transform();
+ otherToParent *= QTransform::fromTranslate(otherPos.x(), otherPos.y());
+ return otherToParent.inverted(ok);
+ } else {
+ if (ok)
+ *ok = true;
+ return QTransform::fromTranslate(-otherPos.x(), -otherPos.y());
+ }
+ }
+
+ // Siblings
+ if (parent == otherParent) {
+ bool hasTr = d_ptr->hasTransform;
+ bool otherHasTr = other->d_ptr->hasTransform;
+ const QPointF &itemPos = d_ptr->pos;
+ const QPointF &otherPos = other->d_ptr->pos;
+
+ if (!hasTr && !otherHasTr) {
+ QPointF delta = itemPos - otherPos;
+ if (ok)
+ *ok = true;
+ return QTransform::fromTranslate(delta.x(), delta.y());
+ }
+
+ QTransform itemToParent = QTransform::fromTranslate(itemPos.x(), itemPos.y());
+ if (hasTr)
+ itemToParent = transform() * itemToParent;
+
+ QTransform otherToParent = QTransform::fromTranslate(otherPos.x(), otherPos.y());
+ if (otherHasTr)
+ otherToParent = other->transform() * otherToParent;
+
+ return itemToParent * otherToParent.inverted(ok);
+ }
+
+ // Find the closest common ancestor. If the two items don't share an
+ // ancestor, then the only way is to combine their scene transforms.
+ const QGraphicsItem *commonAncestor = commonAncestorItem(other);
+ if (!commonAncestor)
+ return sceneTransform() * other->sceneTransform().inverted(ok);
+
+ // If the two items are cousins (in sibling branches), map both to the
+ // common ancestor, and combine the two transforms.
+ bool cousins = other != commonAncestor && this != commonAncestor;
+ if (cousins) {
+ bool good = false;
+ QTransform thisToScene;
+ QTransform otherToScene;
+ thisToScene = itemTransform(commonAncestor, &good);
+ if (good)
+ otherToScene = other->itemTransform(commonAncestor, &good);
+ if (!good) {
+ if (ok)
+ *ok = false;
+ return QTransform();
+ }
+ return thisToScene * otherToScene.inverted(ok);
+ }
+
+ // One is an ancestor of the other; walk the chain.
+ bool parentOfOther = isAncestorOf(other);
+ const QGraphicsItem *child = parentOfOther ? other : this;
+ const QGraphicsItem *root = parentOfOther ? this : other;
+
+ QTransform x;
+ const QGraphicsItem *p = child;
+ do {
+ const QGraphicsItemPrivate *pd = p->d_ptr;
+ if (pd->hasTransform)
+ x *= p->transform();
+ x *= QTransform::fromTranslate(pd->pos.x(), pd->pos.y());
+ } while ((p = p->d_ptr->parent) && p != root);
+ if (parentOfOther)
+ return x.inverted(ok);
+ if (ok)
+ *ok = true;
+ return x;
+}
+
+/*!
+ \obsolete
+
+ Sets the item's affine transformation matrix. This is a subset or the
+ item's full transformation matrix, and might not represent the item's full
+ transformation.
+
+ Use setTransform() instead.
+
+ \sa transform(), rotate(), scale(), shear(), translate(), {The Graphics View Coordinate System}
+*/
+void QGraphicsItem::setMatrix(const QMatrix &matrix, bool combine)
+{
+ QTransform oldTransform = this->transform();
+ QTransform newTransform;
+ if (!combine)
+ newTransform = QTransform(matrix);
+ else
+ newTransform = QTransform(matrix) * oldTransform;
+ if (oldTransform == newTransform)
+ return;
+
+ // Notify the item that the matrix is changing.
+ QVariant variant;
+ qVariantSetValue<QMatrix>(variant, newTransform.toAffine());
+ newTransform = QTransform(qVariantValue<QMatrix>(itemChange(ItemMatrixChange, variant)));
+ if (oldTransform == newTransform)
+ return;
+
+ // Update and set the new transformation.
+ d_ptr->fullUpdateHelper(true);
+ prepareGeometryChange();
+ d_ptr->hasTransform = !newTransform.isIdentity();
+ d_ptr->setExtra(QGraphicsItemPrivate::ExtraTransform, newTransform);
+ d_ptr->invalidateSceneTransformCache();
+
+ // Send post-notification.
+ itemChange(ItemTransformHasChanged, newTransform);
+}
+
+/*!
+ \since 4.3
+
+ Sets the item's current transformation matrix to \a matrix.
+
+ If \a combine is true, then \a matrix is combined with the current matrix;
+ otherwise, \a matrix \e replaces the current matrix. \a combine is false
+ by default.
+
+ To simplify interation with items using a transformed view, QGraphicsItem
+ provides mapTo... and mapFrom... functions that can translate between
+ items' and the scene's coordinates. For example, you can call mapToScene()
+ to map an item coordiate to a scene coordinate, or mapFromScene() to map
+ from scene coordinates to item coordinates.
+
+ \sa transform(), rotate(), scale(), shear(), translate(), {The Graphics View Coordinate System}
+*/
+void QGraphicsItem::setTransform(const QTransform &matrix, bool combine)
+{
+ QTransform oldTransform = this->transform();
+ QTransform newTransform;
+ if (!combine)
+ newTransform = matrix;
+ else
+ newTransform = matrix * oldTransform;
+ if (oldTransform == newTransform)
+ return;
+
+ // Notify the item that the transformation matrix is changing.
+ QVariant variant;
+ qVariantSetValue<QTransform>(variant, newTransform);
+ newTransform = qVariantValue<QTransform>(itemChange(ItemTransformChange, variant));
+ if (oldTransform == newTransform)
+ return;
+
+ // Update and set the new transformation.
+ d_ptr->fullUpdateHelper(true);
+ prepareGeometryChange();
+ d_ptr->hasTransform = !newTransform.isIdentity();
+ d_ptr->setExtra(QGraphicsItemPrivate::ExtraTransform, newTransform);
+ d_ptr->invalidateSceneTransformCache();
+
+ // Send post-notification.
+ itemChange(ItemTransformHasChanged, newTransform);
+}
+
+/*!
+ \obsolete
+
+ Use resetTransform() instead.
+*/
+void QGraphicsItem::resetMatrix()
+{
+ resetTransform();
+}
+
+/*!
+ \since 4.3
+
+ Resets this item's transformation matrix to the identity matrix. This is
+ equivalent to calling \c setTransform(QTransform()).
+
+ \sa setTransform(), transform()
+*/
+void QGraphicsItem::resetTransform()
+{
+ setTransform(QTransform(), false);
+}
+
+/*!
+ Rotates the current item transformation \a angle degrees clockwise around
+ its origin. To translate around an arbitrary point (x, y), you need to
+ combine translation and rotation with setTransform().
+
+ Example:
+
+ \snippet doc/src/snippets/code/src_gui_graphicsview_qgraphicsitem.cpp 6
+
+ \sa setTransform(), transform(), scale(), shear(), translate()
+*/
+void QGraphicsItem::rotate(qreal angle)
+{
+ setTransform(QTransform().rotate(angle), true);
+}
+
+/*!
+ Scales the current item transformation by (\a sx, \a sy) around its
+ origin. To scale from an arbitrary point (x, y), you need to combine
+ translation and scaling with setTransform().
+
+ Example:
+
+ \snippet doc/src/snippets/code/src_gui_graphicsview_qgraphicsitem.cpp 7
+
+ \sa setTransform(), transform(), rotate(), shear(), translate()
+*/
+void QGraphicsItem::scale(qreal sx, qreal sy)
+{
+ setTransform(QTransform::fromScale(sx, sy), true);
+}
+
+/*!
+ Shears the current item transformation by (\a sh, \a sv).
+
+ \sa setTransform(), transform(), rotate(), scale(), translate()
+*/
+void QGraphicsItem::shear(qreal sh, qreal sv)
+{
+ setTransform(QTransform().shear(sh, sv), true);
+}
+
+/*!
+ Translates the current item transformation by (\a dx, \a dy).
+
+ If all you want is to move an item, you should call moveBy() or
+ setPos() instead; this function changes the item's translation,
+ which is conceptually separate from its position.
+
+ \sa setTransform(), transform(), rotate(), scale(), shear()
+*/
+void QGraphicsItem::translate(qreal dx, qreal dy)
+{
+ setTransform(QTransform::fromTranslate(dx, dy), true);
+}
+
+/*!
+ This virtual function is called twice for all items by the
+ QGraphicsScene::advance() slot. In the first phase, all items are called
+ with \a phase == 0, indicating that items on the scene are about to
+ advance, and then all items are called with \a phase == 1. Reimplement
+ this function to update your item if you need simple scene-controlled
+ animation.
+
+ The default implementation does nothing.
+
+ For individual item animation, an alternative to this function is to
+ either use QGraphicsItemAnimation, or to multiple-inherit from QObject and
+ QGraphicsItem, and animate your item using QObject::startTimer() and
+ QObject::timerEvent().
+
+ \sa QGraphicsItemAnimation, QTimeLine
+*/
+void QGraphicsItem::advance(int phase)
+{
+ Q_UNUSED(phase);
+}
+
+/*!
+ Returns the Z-value, or the elevation, of the item. The Z-value decides
+ the stacking order of sibling (neighboring) items.
+
+ The default Z-value is 0.
+
+ \sa setZValue()
+*/
+qreal QGraphicsItem::zValue() const
+{
+ return d_ptr->z;
+}
+
+/*!
+ Sets the Z-value, or the elevation, of the item, to \a z. The elevation
+ decides the stacking order of sibling (neighboring) items. An item of high
+ Z-value will be drawn on top of an item with a lower Z-value if they
+ share the same parent item. In addition, children of an item will always be drawn
+ on top of the parent, regardless of the child's Z-value. Sibling items
+ that share the same Z-value will be drawn in an undefined order, although
+ the order will stay the same for as long as the items live.
+
+ \img graphicsview-zorder.png
+
+ Children of different parents are stacked according to the Z-value of
+ each item's ancestor item which is an immediate child of the two
+ items' closest common ancestor. For example, a robot item might
+ define a torso item as the parent of a head item, two arm items,
+ and two upper-leg items. The upper-leg items would each be parents
+ of one lower-leg item, and each lower-leg item would be parents of
+ one foot item. The stacking order of the feet is the same as the
+ stacking order of each foot's ancestor that is an immediate child
+ of the two feet's common ancestor (i.e., the torso item); so the
+ feet are stacked in the same order as the upper-leg items,
+ regardless of each foot's Z-value.
+
+ The Z-value does not affect the item's size in any way.
+
+ The default Z-value is 0.
+
+ \sa zValue()
+*/
+void QGraphicsItem::setZValue(qreal z)
+{
+ qreal newZ = qreal(itemChange(ItemZValueChange, double(z)).toDouble());
+ if (newZ == d_ptr->z)
+ return;
+ d_ptr->z = z;
+ d_ptr->fullUpdateHelper();
+
+ if (d_ptr->scene) {
+ // Invalidate any sort caching; arrival of a new item means we need to
+ // resort.
+ d_ptr->scene->d_func()->invalidateSortCache();
+ }
+
+ itemChange(ItemZValueHasChanged, double(newZ));
+}
+
+/*!
+ Returns the bounding rect of this item's descendants (i.e., its
+ children, their children, etc.) in local coordinates. The
+ rectangle will contain all descendants after they have been mapped
+ to local coordinates. If the item has no children, this function
+ returns an empty QRectF.
+
+ This does not include this item's own bounding rect; it only returns
+ its descendants' accumulated bounding rect. If you need to include this
+ item's bounding rect, you can add boundingRect() to childrenBoundingRect()
+ using QRectF::operator|().
+
+ This function is linear in complexity; it determines the size of the
+ returned bounding rect by iterating through all descendants.
+
+ \sa boundingRect(), sceneBoundingRect()
+*/
+QRectF QGraphicsItem::childrenBoundingRect() const
+{
+ QRectF childRect;
+ foreach (QGraphicsItem *child, children()) {
+ QPointF childPos = child->pos();
+ QTransform matrix = child->transform() * QTransform::fromTranslate(childPos.x(), childPos.y());
+ childRect |= matrix.mapRect(child->boundingRect() | child->childrenBoundingRect());
+ }
+ return childRect;
+}
+
+/*!
+ \fn virtual QRectF QGraphicsItem::boundingRect() const = 0
+
+ This pure virtual function defines the outer bounds of the item as
+ a rectangle; all painting must be restricted to inside an item's
+ bounding rect. QGraphicsView uses this to determine whether the
+ item requires redrawing.
+
+ Although the item's shape can be arbitrary, the bounding rect is
+ always rectangular, and it is unaffected by the items'
+ transformation (scale(), rotate(), etc.).
+
+ If you want to change the item's bounding rectangle, you must first call
+ prepareGeometryChange(). This notifies the scene of the imminent change,
+ so that its can update its item geometry index; otherwise, the scene will
+ be unaware of the item's new geometry, and the results are undefined
+ (typically, rendering artifacts are left around in the view).
+
+ Reimplement this function to let QGraphicsView determine what
+ parts of the widget, if any, need to be redrawn.
+
+ Note: For shapes that paint an outline / stroke, it is important
+ to include half the pen width in the bounding rect. It is not
+ necessary to compensate for antialiasing, though.
+
+ Example:
+
+ \snippet doc/src/snippets/code/src_gui_graphicsview_qgraphicsitem.cpp 8
+
+ \sa boundingRegion(), shape(), contains(), {The Graphics View Coordinate
+ System}, prepareGeometryChange()
+*/
+
+/*!
+ Returns the bounding rect of this item in scene coordinates, by combining
+ sceneTransform() with boundingRect().
+
+ \sa boundingRect(), {The Graphics View Coordinate System}
+*/
+QRectF QGraphicsItem::sceneBoundingRect() const
+{
+ // Find translate-only offset
+ QPointF offset;
+ const QGraphicsItem *parentItem = this;
+ const QGraphicsItemPrivate *itemd;
+ do {
+ itemd = parentItem->d_ptr;
+ if (itemd->hasTransform)
+ break;
+ offset += itemd->pos;
+ } while ((parentItem = itemd->parent));
+
+ QRectF br = boundingRect();
+ br.translate(offset);
+ return !parentItem ? br : parentItem->sceneTransform().mapRect(br);
+}
+
+/*!
+ Returns the shape of this item as a QPainterPath in local
+ coordinates. The shape is used for many things, including collision
+ detection, hit tests, and for the QGraphicsScene::items() functions.
+
+ The default implementation calls boundingRect() to return a simple
+ rectangular shape, but subclasses can reimplement this function to return
+ a more accurate shape for non-rectangular items. For example, a round item
+ may choose to return an elliptic shape for better collision detection. For
+ example:
+
+ \snippet doc/src/snippets/code/src_gui_graphicsview_qgraphicsitem.cpp 9
+
+ The outline of a shape can vary depending on the width and style of the
+ pen used when drawing. If you want to include this outline in the item's
+ shape, you can create a shape from the stroke using QPainterPathStroker.
+
+ This function is called by the default implementations of contains() and
+ collidesWithPath().
+
+ \sa boundingRect(), contains(), prepareGeometryChange(), QPainterPathStroker
+*/
+QPainterPath QGraphicsItem::shape() const
+{
+ QPainterPath path;
+ path.addRect(boundingRect());
+ return path;
+}
+
+/*!
+ Returns true if this item is clipped. An item is clipped if it has either
+ set the \l ItemClipsToShape flag, or if it or any of its ancestors has set
+ the \l ItemClipsChildrenToShape flag.
+
+ Clipping affects the item's appearance (i.e., painting), as well as mouse
+ and hover event delivery.
+
+ \sa clipPath(), shape(), setFlags()
+*/
+bool QGraphicsItem::isClipped() const
+{
+ Q_D(const QGraphicsItem);
+ return (d->ancestorFlags & QGraphicsItemPrivate::AncestorClipsChildren)
+ || (d->flags & QGraphicsItem::ItemClipsToShape);
+}
+
+/*!
+ \since 4.5
+
+ Returns this item's clip path, or an empty QPainterPath if this item is
+ not clipped. The clip path constrains the item's appearance and
+ interaction (i.e., restricts the area the item can draw, and it also
+ restricts the area that the item receives events).
+
+ You can enable clipping by setting the ItemClipsToShape or
+ ItemClipsChildrenToShape flags. The item's clip path is calculated by
+ intersecting all clipping ancestors' shapes. If the item sets
+ ItemClipsToShape, the final clip is intersected with the item's own shape.
+
+ \note Clipping introduces a performance penalty for all items involved;
+ you should generally avoid using clipping if you can (e.g., if your items
+ always draw inside boundingRect() or shape() boundaries, clipping is not
+ necessary).
+
+ \sa isClipped(), shape(), setFlags()
+*/
+QPainterPath QGraphicsItem::clipPath() const
+{
+ Q_D(const QGraphicsItem);
+ QPainterPath clip;
+ if (!isClipped())
+ return clip;
+
+ // Start with the item's bounding rect.
+ clip.addRect(boundingRect());
+
+ bool clipAway = false;
+ if (d->ancestorFlags & QGraphicsItemPrivate::AncestorClipsChildren) {
+ // Make list of parents up to the farthest ancestor that clips its
+ // children to its shape.
+ QVarLengthArray<const QGraphicsItem *, 32> clippingAncestors;
+ const QGraphicsItem *parent = parentItem();
+ const QGraphicsItem *clipOwner = 0;
+ do {
+ if (parent->d_ptr->flags & ItemClipsChildrenToShape) {
+ clippingAncestors.append(parent);
+ clipOwner = parent;
+ }
+ } while ((parent->d_ptr->ancestorFlags & QGraphicsItemPrivate::AncestorClipsChildren) && (parent = parent->parentItem()));
+
+ // Start with the topmost clip.
+ QPainterPath parentClip = clipOwner->shape();
+
+ // Intersect any in-between clips starting at the bottom and moving
+ // upwards.
+ for (int i = clippingAncestors.size() - 2; i >= 0; --i) {
+ const QGraphicsItem *item = clippingAncestors[i];
+ // ### what if itemtransform fails
+ if (clipOwner)
+ parentClip = clipOwner->itemTransform(item).map(parentClip);
+ parentClip = parentClip.intersected(item->shape());
+ if (parentClip.isEmpty()) {
+ clip = parentClip;
+ clipAway = true;
+ break;
+ }
+ clipOwner = item;
+ }
+
+ if (!clipAway) {
+ // ### what if itemtransform fails
+ clip = clip.intersected(clipOwner->itemTransform(this).map(parentClip));
+ if (clip.isEmpty())
+ clipAway = true;
+ }
+ }
+
+ if (!clipAway && d->flags & ItemClipsToShape)
+ clip = clip.intersected(shape());
+
+ return clip;
+}
+
+/*!
+ Returns true if this item contains \a point, which is in local
+ coordinates; otherwise, false is returned. It is most often called from
+ QGraphicsView to determine what item is under the cursor, and for that
+ reason, the implementation of this function should be as light-weight as
+ possible.
+
+ By default, this function calls shape(), but you can reimplement it in a
+ subclass to provide a (perhaps more efficient) implementation.
+
+ \sa shape(), boundingRect(), collidesWithPath()
+*/
+bool QGraphicsItem::contains(const QPointF &point) const
+{
+ return isClipped() ? clipPath().contains(point) : shape().contains(point);
+}
+
+/*!
+ Returns true if this item collides with \a other; otherwise returns false.
+ The ways items collide is determined by \a mode. The default value for \a
+ mode is Qt::IntersectsItemShape; \a other collides with this item if it
+ either intersects, contains, or is contained by this item's shape.
+
+ The default implementation is based on shape intersection, and it calls
+ shape() on both items. Because the complexity of arbitrary shape-shape
+ intersection grows with an order of magnitude when the shapes are complex,
+ this operation can be noticably time consuming. You have the option of
+ reimplementing this function in a subclass of QGraphicsItem to provide a
+ custom algorithm. This allows you to make use of natural constraints in
+ the shapes of your own items, in order to improve the performance of the
+ collision detection. For instance, two untransformed perfectly circular
+ items' collision can be determined very efficiently by comparing their
+ positions and radii.
+
+ Keep in mind that when reimplementing this function and calling shape() or
+ boundingRect() on \a other, the returned coordinates must be mapped to
+ this item's coordinate system before any intersection can take place.
+
+ \sa contains(), shape()
+*/
+bool QGraphicsItem::collidesWithItem(const QGraphicsItem *other, Qt::ItemSelectionMode mode) const
+{
+ if (other == this)
+ return true;
+ if (!other)
+ return false;
+ // The items share the same clip if their closest clipper is the same, or
+ // if one clips the other.
+ bool clips = (d_ptr->ancestorFlags & QGraphicsItemPrivate::AncestorClipsChildren);
+ bool otherClips = (other->d_ptr->ancestorFlags & QGraphicsItemPrivate::AncestorClipsChildren);
+ if (clips || otherClips) {
+ const QGraphicsItem *closestClipper = isAncestorOf(other) ? this : parentItem();
+ while (closestClipper && !(closestClipper->flags() & ItemClipsChildrenToShape))
+ closestClipper = closestClipper->parentItem();
+ const QGraphicsItem *otherClosestClipper = other->isAncestorOf(this) ? other : other->parentItem();
+ while (otherClosestClipper && !(otherClosestClipper->flags() & ItemClipsChildrenToShape))
+ otherClosestClipper = otherClosestClipper->parentItem();
+ if (closestClipper == otherClosestClipper) {
+ d_ptr->localCollisionHack = 1;
+ bool res = collidesWithPath(mapFromItem(other, other->shape()), mode);
+ d_ptr->localCollisionHack = 0;
+ return res;
+ }
+ }
+
+ QPainterPath otherShape = other->isClipped() ? other->clipPath() : other->shape();
+ return collidesWithPath(mapFromItem(other, otherShape), mode);
+}
+
+/*!
+ Returns true if this item collides with \a path.
+
+ The collision is determined by \a mode. The default value for \a mode is
+ Qt::IntersectsItemShape; \a path collides with this item if it either
+ intersects, contains, or is contained by this item's shape.
+
+ \sa collidesWithItem(), contains(), shape()
+*/
+bool QGraphicsItem::collidesWithPath(const QPainterPath &path, Qt::ItemSelectionMode mode) const
+{
+ if (path.isEmpty()) {
+ // No collision with empty paths.
+ return false;
+ }
+
+ QRectF rectA = _q_adjustedRect(boundingRect());
+ QRectF rectB = _q_adjustedRect(path.controlPointRect());
+ if (!rectA.intersects(rectB)) {
+ // This we can determine efficiently. If the two rects neither
+ // intersect nor contain eachother, then the two items do not collide.
+ return false;
+ }
+
+ // For further testing, we need this item's shape or bounding rect.
+ QPainterPath thisShape;
+ if (mode == Qt::IntersectsItemShape || mode == Qt::ContainsItemShape) {
+ thisShape = (isClipped() && !d_ptr->localCollisionHack) ? clipPath() : shape();
+ } else {
+ thisShape.addPolygon(_q_adjustedRect(boundingRect()));
+ thisShape.closeSubpath();
+ }
+ if (thisShape == QPainterPath()) {
+ // Empty shape? No collision.
+ return false;
+ }
+
+ // Use QPainterPath boolean operations to determine the collision, O(N*logN).
+ if (mode == Qt::IntersectsItemShape || mode == Qt::IntersectsItemBoundingRect)
+ return path.intersects(thisShape);
+ return path.contains(thisShape);
+}
+
+/*!
+ Returns a list of all items that collide with this item.
+
+ The way collisions are detected is determined by \a mode. The default
+ value for \a mode is Qt::IntersectsItemShape; All items whose shape
+ intersects or is contained by this item's shape are returned.
+
+ \sa QGraphicsScene::collidingItems(), collidesWithItem()
+*/
+QList<QGraphicsItem *> QGraphicsItem::collidingItems(Qt::ItemSelectionMode mode) const
+{
+ if (d_ptr->scene)
+ return d_ptr->scene->collidingItems(this, mode);
+ return QList<QGraphicsItem *>();
+}
+
+/*!
+ Returns true if this item's bounding rect is completely obscured by the
+ opaque shape of any of colliding items above it (i.e., with a higher Z
+ value than this item).
+
+ Its implementation is based on calling isObscuredBy(), which you can
+ reimplement to provide a custom obscurity algorithm.
+
+ \sa opaqueArea()
+*/
+bool QGraphicsItem::isObscured() const
+{
+ return isObscured(QRectF());
+}
+
+/*!
+ \internal
+
+ Item obscurity helper function.
+
+ Returns true if the subrect \a rect of \a item's bounding rect is obscured
+ by \a other (i.e., \a other's opaque area covers \a item's \a rect
+ completely. \a other is assumed to already be "on top of" \a item
+ wrt. stacking order.
+*/
+static bool qt_QGraphicsItem_isObscured(const QGraphicsItem *item,
+ const QGraphicsItem *other,
+ const QRectF &rect)
+{
+ return other->mapToItem(item, other->opaqueArea()).contains(rect);
+}
+
+/*!
+ \overload
+ \since 4.3
+
+ Returns true if \a rect is completely obscured by the opaque shape of any
+ of colliding items above it (i.e., with a higher Z value than this item).
+
+ Unlike the default isObscured() function, this function does not call
+ isObscuredBy().
+
+ \sa opaqueArea()
+*/
+bool QGraphicsItem::isObscured(const QRectF &rect) const
+{
+ Q_D(const QGraphicsItem);
+ if (!d->scene)
+ return false;
+
+ QRectF br = boundingRect();
+ QRectF testRect = rect.isNull() ? br : rect;
+
+ foreach (QGraphicsItem *item, d->scene->items(mapToScene(br), Qt::IntersectsItemBoundingRect)) {
+ if (item == this)
+ break;
+ if (qt_QGraphicsItem_isObscured(this, item, testRect))
+ return true;
+ }
+ return false;
+}
+
+/*!
+ \fn bool QGraphicsItem::isObscured(qreal x, qreal y, qreal w, qreal h) const
+ \since 4.3
+
+ This convenience function is equivalent to calling isObscured(QRectF(\a x, \a y, \a w, \a h)).
+*/
+
+/*!
+ Returns true if this item's bounding rect is completely obscured by the
+ opaque shape of \a item.
+
+ The base implementation maps \a item's opaqueArea() to this item's
+ coordinate system, and then checks if this item's boundingRect() is fully
+ contained within the mapped shape.
+
+ You can reimplement this function to provide a custom algorithm for
+ determining whether this item is obscured by \a item.
+
+ \sa opaqueArea(), isObscured()
+*/
+bool QGraphicsItem::isObscuredBy(const QGraphicsItem *item) const
+{
+ if (!item)
+ return false;
+ return QGraphicsScenePrivate::closestItemFirst_withoutCache(item, this)
+ && qt_QGraphicsItem_isObscured(this, item, boundingRect());
+}
+
+/*!
+ This virtual function returns a shape representing the area where this
+ item is opaque. An area is opaque if it is filled using an opaque brush or
+ color (i.e., not transparent).
+
+ This function is used by isObscuredBy(), which is called by underlying
+ items to determine if they are obscured by this item.
+
+ The default implementation returns an empty QPainterPath, indicating that
+ this item is completely transparent and does not obscure any other items.
+
+ \sa isObscuredBy(), isObscured(), shape()
+*/
+QPainterPath QGraphicsItem::opaqueArea() const
+{
+ return QPainterPath();
+}
+
+/*!
+ \since 4.4
+
+ Returns the bounding region for this item. The coordinate space of the
+ returned region depends on \a itemToDeviceTransform. If you pass an
+ identity QTransform as a parameter, this function will return a local
+ coordinate region.
+
+ The bounding region describes a coarse outline of the item's visual
+ contents. Although it's expensive to calculate, it's also more precise
+ than boundingRect(), and it can help to avoid unnecessary repainting when
+ an item is updated. This is particularily efficient for thin items (e.g.,
+ lines or simple polygons). You can tune the granularity for the bounding
+ region by calling setBoundingRegionGranularity(). The default granularity
+ is 0; in which the item's bounding region is the same as its bounding
+ rect.
+
+ \a itemToDeviceTransform is the transformation from item coordinates to
+ device coordinates. If you want this function to return a QRegion in scene
+ coordinates, you can pass sceneTransform() as an argument.
+
+ \sa boundingRegionGranularity()
+*/
+QRegion QGraphicsItem::boundingRegion(const QTransform &itemToDeviceTransform) const
+{
+ // ### Ideally we would have a better way to generate this region,
+ // preferably something in the lines of QPainterPath::toRegion(QTransform)
+ // coupled with a way to generate a painter path from a set of painter
+ // operations (e.g., QPicture::toPainterPath() or so). The current
+ // approach generates a bitmap with the size of the item's bounding rect
+ // in device coordinates, scaled by b.r.granularity, then paints the item
+ // into the bitmap, converts the result to a QRegion and scales the region
+ // back to device space with inverse granularity.
+ qreal granularity = boundingRegionGranularity();
+ QRect deviceRect = _q_adjustedRect(itemToDeviceTransform.mapRect(boundingRect()).toRect());
+ if (granularity == 0.0)
+ return QRegion(deviceRect);
+
+ int pad = 1;
+ QSize bitmapSize(qMax(1, int(deviceRect.width() * granularity) + pad * 2),
+ qMax(1, int(deviceRect.height() * granularity) + pad * 2));
+ QImage mask(bitmapSize, QImage::Format_ARGB32_Premultiplied);
+ mask.fill(0);
+ QPainter p(&mask);
+ p.setRenderHints(QPainter::Antialiasing);
+
+ // Transform painter (### this code is from QGraphicsScene::drawItemHelper
+ // and doesn't work properly with perspective transformations).
+ QPointF viewOrigo = itemToDeviceTransform.map(QPointF(0, 0));
+ QPointF offset = viewOrigo - deviceRect.topLeft();
+ p.scale(granularity, granularity);
+ p.translate(offset);
+ p.translate(pad, pad);
+ p.setWorldTransform(itemToDeviceTransform, true);
+ p.translate(itemToDeviceTransform.inverted().map(QPointF(0, 0)));
+
+ // Render
+ QStyleOptionGraphicsItem option;
+ const_cast<QGraphicsItem *>(this)->paint(&p, &option, 0);
+ p.end();
+
+ // Transform QRegion back to device space
+ QTransform unscale;
+ unscale.scale(1 / granularity, 1 / granularity);
+ QRegion r;
+ QBitmap colorMask = QBitmap::fromImage(mask.createMaskFromColor(0));
+ foreach (const QRect &rect, QRegion( colorMask ).rects()) {
+ QRect xrect = unscale.mapRect(rect).translated(deviceRect.topLeft() - QPoint(pad, pad));
+ r += xrect.adjusted(-1, -1, 1, 1) & deviceRect;
+ }
+ return r;
+}
+
+/*!
+ \since 4.4
+
+ Returns the item's bounding region granularity; a value between and
+ including 0 and 1. The default value is 0 (i.e., the lowest granularity,
+ where the bounding region corresponds to the item's bounding rectangle).
+
+\omit
+### NOTE
+\endomit
+
+ \sa setBoundingRegionGranularity()
+*/
+qreal QGraphicsItem::boundingRegionGranularity() const
+{
+ return d_ptr->hasBoundingRegionGranularity
+ ? qVariantValue<qreal>(d_ptr->extra(QGraphicsItemPrivate::ExtraBoundingRegionGranularity))
+ : 0;
+}
+
+/*!
+ \since 4.4
+ Sets the bounding region granularity to \a granularity; a value between
+ and including 0 and 1. The default value is 0 (i.e., the lowest
+ granularity, where the bounding region corresponds to the item's bounding
+ rectangle).
+
+ The granularity is used by boundingRegion() to calculate how fine the
+ bounding region of the item should be. The highest achievable granularity
+ is 1, where boundingRegion() will return the finest outline possible for
+ the respective device (e.g., for a QGraphicsView viewport, this gives you
+ a pixel-perfect bounding region). The lowest possible granularity is
+ 0. The value of \a granularity describes the ratio between device
+ resolution and the resolution of the bounding region (e.g., a value of
+ 0.25 will provide a region where each chunk corresponds to 4x4 device
+ units / pixels).
+
+ \sa boundingRegionGranularity()
+*/
+void QGraphicsItem::setBoundingRegionGranularity(qreal granularity)
+{
+ if (granularity < 0.0 || granularity > 1.0) {
+ qWarning("QGraphicsItem::setBoundingRegionGranularity: invalid granularity %g", granularity);
+ return;
+ }
+ if (granularity == 0.0) {
+ d_ptr->unsetExtra(QGraphicsItemPrivate::ExtraBoundingRegionGranularity);
+ d_ptr->hasBoundingRegionGranularity = 0;
+ return;
+ }
+ d_ptr->hasBoundingRegionGranularity = 1;
+ d_ptr->setExtra(QGraphicsItemPrivate::ExtraBoundingRegionGranularity,
+ qVariantFromValue<qreal>(granularity));
+}
+
+/*!
+ \fn virtual void QGraphicsItem::paint(QPainter *painter, const
+ QStyleOptionGraphicsItem *option, QWidget *widget = 0) = 0
+
+ This function, which is usually called by QGraphicsView, paints the
+ contents of an item in local coordinates.
+
+ Reimplement this function in a QGraphicsItem subclass to provide the
+ item's painting implementation, using \a painter. The \a option parameter
+ provides style options for the item, such as its state, exposed area and
+ its level-of-detail hints. The \a widget argument is optional. If
+ provided, it points to the widget that is being painted on; otherwise, it
+ is 0. For cached painting, \a widget is always 0.
+
+ \snippet doc/src/snippets/code/src_gui_graphicsview_qgraphicsitem.cpp 10
+
+ The painter's pen is 0-width by default, and its pen is initialized to the
+ QPalette::Text brush from the paint device's palette. The brush is
+ initialized to QPalette::Window.
+
+ Make sure to constrain all painting inside the boundaries of
+ boundingRect() to avoid rendering artifacts (as QGraphicsView does not
+ clip the painter for you). In particular, when QPainter renders the
+ outline of a shape using an assigned QPen, half of the outline will be
+ drawn outside, and half inside, the shape you're rendering (e.g., with a
+ pen width of 2 units, you must draw outlines 1 unit inside
+ boundingRect()). QGraphicsItem does not support use of cosmetic pens with
+ a non-zero width.
+
+ All painting is done in local coordinates.
+
+ \sa setCacheMode(), QPen::width(), {Item Coordinates}
+*/
+
+/*!
+ \internal
+
+ Asks the scene to mark this item's scene rect as dirty, requesting a
+ redraw. This does not invalidate any cache.
+
+ The \a force argument is for the update call in setVisible(), which is the
+ only case where the item's background should be marked as dirty even when
+ the item isn't visible.
+*/
+void QGraphicsItemPrivate::updateHelper(const QRectF &rect, bool force)
+{
+ // No scene, or if the scene is updating everything, means we have nothing
+ // to do. The only exception is if the scene tracks the growing scene rect.
+ if (dirty)
+ return;
+ if (!scene || (scene && scene->d_func()->updateAll && scene->d_func()->hasSceneRect))
+ return;
+ if (scene && (visible || force)) {
+ if (rect.isNull())
+ dirty = 1;
+ scene->itemUpdated(q_ptr, rect);
+ }
+}
+
+/*!
+ \internal
+
+ Propagates updates to \a item and all its children.
+*/
+void QGraphicsItemPrivate::fullUpdateHelper(bool childrenOnly)
+{
+ // No scene, or if the scene is updating everything, means we have nothing
+ // to do. The only exception is if the scene tracks the growing scene rect.
+ if (!scene || (scene && scene->d_func()->updateAll && scene->d_func()->hasSceneRect))
+ return;
+ if (!childrenOnly && !dirty)
+ updateHelper();
+ if (children.isEmpty() || dirtyChildren)
+ return;
+ if (flags & QGraphicsItem::ItemClipsChildrenToShape) {
+ // ### mark all children dirty?
+ // Unnecessary to update children as well.
+ return;
+ }
+ if (ancestorFlags & AncestorClipsChildren) {
+ Q_Q(QGraphicsItem);
+ // Check if we can avoid updating all children.
+ QGraphicsItem *p = parent;
+ QRectF br = q->boundingRect();
+ while (p) {
+ if (p->d_ptr->flags & QGraphicsItem::ItemClipsChildrenToShape) {
+ bool ok;
+ QTransform x = q->itemTransform(p, &ok);
+ if (!ok)
+ break;
+ if (x.mapRect(br).contains(p->boundingRect()))
+ return;
+ }
+ p = p->d_ptr->parent;
+ if (!p || !(p->d_ptr->ancestorFlags & AncestorClipsChildren))
+ break;
+ // ### check one level only
+ break;
+ }
+ }
+ foreach (QGraphicsItem *child, children)
+ child->d_ptr->fullUpdateHelper();
+ dirtyChildren = 1;
+}
+
+/*!
+ \internal
+
+ Resolves and propagates this item's effective opacity to its children.
+*/
+void QGraphicsItemPrivate::resolveEffectiveOpacity(qreal parentEffectiveOpacity)
+{
+ Q_Q(QGraphicsItem);
+ QGraphicsItem::GraphicsItemFlags myFlags = q->flags();
+ QGraphicsItem::GraphicsItemFlags parentFlags = parent ? parent->flags() : QGraphicsItem::GraphicsItemFlags(0);
+
+ // My local opacity is always part of my effective opacity.
+ qreal myEffectiveOpacity = q->opacity();
+
+ // If I have a parent, and I don't ignore my parent's opacity, and my
+ // parent propagates to me, then combine my local opacity with my parent's
+ // effective opacity into my effective opacity.
+ if (parent
+ && !(myFlags & QGraphicsItem::ItemIgnoresParentOpacity)
+ && !(parentFlags & QGraphicsItem::ItemDoesntPropagateOpacityToChildren)) {
+ myEffectiveOpacity *= parentEffectiveOpacity;
+ }
+
+ // Set this item's resolved opacity.
+ setExtra(ExtraEffectiveOpacity, myEffectiveOpacity);
+
+ // Resolve children always.
+ for (int i = 0; i < children.size(); ++i)
+ children.at(i)->d_ptr->resolveEffectiveOpacity(myEffectiveOpacity);
+}
+
+/*!
+ \internal
+
+ Resolves the stacking depth of this object and all its children.
+*/
+void QGraphicsItemPrivate::resolveDepth(int parentDepth)
+{
+ depth = parentDepth + 1;
+ for (int i = 0; i < children.size(); ++i)
+ children.at(i)->d_ptr->resolveDepth(depth);
+}
+
+/*!
+ \internal
+*/
+void QGraphicsItemPrivate::invalidateSceneTransformCache()
+{
+ if (!scene || (parent && sceneTransformIndex == -1))
+ return;
+ if (sceneTransformIndex != -1)
+ scene->d_func()->validTransforms.setBit(sceneTransformIndex, 0);
+ for (int i = 0; i < children.size(); ++i)
+ children.at(i)->d_ptr->invalidateSceneTransformCache();
+}
+
+QGraphicsItemCache *QGraphicsItemPrivate::extraItemCache() const
+{
+ QGraphicsItemCache *c = (QGraphicsItemCache *)qVariantValue<void *>(extra(ExtraCacheData));
+ if (!c) {
+ QGraphicsItemPrivate *that = const_cast<QGraphicsItemPrivate *>(this);
+ c = new QGraphicsItemCache;
+ that->setExtra(ExtraCacheData, qVariantFromValue<void *>(c));
+ }
+ return c;
+}
+
+void QGraphicsItemPrivate::removeExtraItemCache()
+{
+ QGraphicsItemCache *c = (QGraphicsItemCache *)qVariantValue<void *>(extra(ExtraCacheData));
+ if (c) {
+ c->purge();
+ delete c;
+ }
+ unsetExtra(ExtraCacheData);
+}
+
+/*!
+ \internal
+
+ Tells us if it is a proxy widget
+*/
+bool QGraphicsItemPrivate::isProxyWidget() const
+{
+ return false;
+}
+
+/*!
+ Schedules a redraw of the area covered by \a rect in this item. You can
+ call this function whenever your item needs to be redrawn, such as if it
+ changes appearance or size.
+
+ This function does not cause an immediate paint; instead it schedules a
+ paint request that is processed by QGraphicsView after control reaches the
+ event loop. The item will only be redrawn if it is visible in any
+ associated view.
+
+ As a side effect of the item being repainted, other items that overlap the
+ area \a rect may also be repainted.
+
+ If the item is invisible (i.e., isVisible() returns false), this function
+ does nothing.
+
+ \sa paint(), boundingRect()
+*/
+void QGraphicsItem::update(const QRectF &rect)
+{
+ if (d_ptr->dirty)
+ return;
+ if (d_ptr->scene && isVisible()) {
+ if (CacheMode(d_ptr->cacheMode) != NoCache) {
+ QGraphicsItemCache *cache = d_ptr->extraItemCache();
+ if (rect.isNull()) {
+ cache->allExposed = true;
+ cache->exposed.clear();
+ } else {
+ cache->exposed.append(rect);
+ }
+ }
+ d_ptr->updateHelper(rect);
+ }
+}
+
+
+/*!
+ \since 4.4
+ Scrolls the contents of \a rect by \a dx, \a dy. If \a rect is a null rect
+ (the default), the item's bounding rect is scrolled.
+
+ Scrolling provides a fast alternative to simply redrawing when the
+ contents of the item (or parts of the item) are shifted vertically or
+ horizontally. Depending on the current transformation and the capabilities
+ of the paint device (i.e., the viewport), this operation may consist of
+ simply moving pixels from one location to another using memmove(). In most
+ cases this is faster than rerendering the entire area.
+
+ After scrolling, the item will issue an update for the newly exposed
+ areas. If scrolling is not supported (e.g., you are rendering to an OpenGL
+ viewport, which does not benefit from scroll optimizations), this function
+ is equivalent to calling update(\a rect).
+
+ \sa boundingRect()
+*/
+void QGraphicsItem::scroll(qreal dx, qreal dy, const QRectF &rect)
+{
+ Q_D(QGraphicsItem);
+ if (dx == 0.0 && dy == 0.0)
+ return;
+ if (!d->scene)
+ return;
+ if (d->cacheMode != NoCache) {
+ // ### This is very slow, and can be done much better. If the cache is
+ // local and matches the below criteria for rotation and scaling, we
+ // can easily scroll. And if the cache is in device coordinates, we
+ // can scroll both the viewport and the cache.
+ update(rect);
+ return;
+ }
+
+ QRectF scrollRect = !rect.isNull() ? rect : boundingRect();
+ int couldntScroll = d->scene->views().size();
+ foreach (QGraphicsView *view, d->scene->views()) {
+ if (view->viewport()->inherits("QGLWidget")) {
+ // ### Please replace with a widget attribute; any widget that
+ // doesn't support partial updates / doesn't support scrolling
+ // should be skipped in this code. Qt::WA_NoPartialUpdates or so.
+ continue;
+ }
+
+ static const QLineF up(0, 0, 0, -1);
+ static const QLineF down(0, 0, 0, 1);
+ static const QLineF left(0, 0, -1, 0);
+ static const QLineF right(0, 0, 1, 0);
+
+ QTransform deviceTr;
+ if (d->itemIsUntransformable()) {
+ deviceTr = deviceTransform(view->viewportTransform());
+ } else {
+ deviceTr = sceneTransform() * view->viewportTransform();
+ }
+
+ QRect deviceScrollRect = deviceTr.mapRect(scrollRect).toRect();
+ QLineF v1 = deviceTr.map(right);
+ QLineF v2 = deviceTr.map(down);
+ QLineF u1 = v1.unitVector(); u1.translate(-v1.p1());
+ QLineF u2 = v2.unitVector(); u2.translate(-v2.p1());
+ bool noScroll = false;
+
+ // Check if the delta resolves to ints in device space.
+ QPointF deviceDelta = deviceTr.map(QPointF(dx, dy));
+ if ((deviceDelta.x() - int(deviceDelta.x()))
+ || (deviceDelta.y() - int(deviceDelta.y()))) {
+ noScroll = true;
+ } else {
+ // Check if the unit vectors have no fraction in device space.
+ qreal v1l = v1.length();
+ if (v1l - int(v1l)) {
+ noScroll = true;
+ } else {
+ dx *= v1.length();
+ }
+ qreal v2l = v2.length();
+ if (v2l - int(v2l)) {
+ noScroll = true;
+ } else {
+ dy *= v2.length();
+ }
+ }
+
+ if (!noScroll) {
+ if (u1 == right) {
+ if (u2 == up) {
+ // flipped
+ dy = -dy;
+ } else if (u2 == down) {
+ // normal
+ } else {
+ noScroll = true;
+ }
+ } else if (u1 == left) {
+ if (u2 == up) {
+ // mirrored & flipped / rotated 180 degrees
+ dx = -dx;
+ dy = -dy;
+ } else if (u2 == down) {
+ // mirrored
+ dx = -dx;
+ } else {
+ noScroll = true;
+ }
+ } else if (u1 == up) {
+ if (u2 == left) {
+ // rotated -90 & mirrored
+ qreal tmp = dy;
+ dy = -dx;
+ dx = -tmp;
+ } else if (u2 == right) {
+ // rotated -90
+ qreal tmp = dy;
+ dy = -dx;
+ dx = tmp;
+ } else {
+ noScroll = true;
+ }
+ } else if (u1 == down) {
+ if (u2 == left) {
+ // rotated 90
+ qreal tmp = dy;
+ dy = dx;
+ dx = -tmp;
+ } else if (u2 == right) {
+ // rotated 90 & mirrored
+ qreal tmp = dy;
+ dy = dx;
+ dx = tmp;
+ } else {
+ noScroll = true;
+ }
+ }
+ }
+
+ if (!noScroll) {
+ view->viewport()->scroll(int(dx), int(dy), deviceScrollRect);
+ --couldntScroll;
+ }
+ }
+ if (couldntScroll)
+ update(rect);
+}
+
+/*!
+ \fn void QGraphicsItem::update(qreal x, qreal y, qreal width, qreal height)
+ \overload
+
+ This convenience function is equivalent to calling update(QRectF(\a x, \a
+ y, \a width, \a height)).
+*/
+
+/*!
+ Maps the point \a point, which is in this item's coordinate system, to \a
+ item's coordinate system, and returns the mapped coordinate.
+
+ If \a item is 0, this function returns the same as mapToScene().
+
+ \sa itemTransform(), mapToParent(), mapToScene(), transform(), mapFromItem(), {The Graphics
+ View Coordinate System}
+*/
+QPointF QGraphicsItem::mapToItem(const QGraphicsItem *item, const QPointF &point) const
+{
+ if (item)
+ return itemTransform(item).map(point);
+ return mapToScene(point);
+}
+
+/*!
+ \fn QPointF QGraphicsItem::mapToItem(const QGraphicsItem *item, qreal x, qreal y) const
+ \overload
+
+ This convenience function is equivalent to calling mapToItem(\a item,
+ QPointF(\a x, \a y)).
+*/
+
+/*!
+ Maps the point \a point, which is in this item's coordinate system, to its
+ parent's coordinate system, and returns the mapped coordinate. If the item
+ has no parent, \a point will be mapped to the scene's coordinate system.
+
+ \sa mapToItem(), mapToScene(), transform(), mapFromParent(), {The Graphics
+ View Coordinate System}
+*/
+QPointF QGraphicsItem::mapToParent(const QPointF &point) const
+{
+ return d_ptr->pos + (d_ptr->hasTransform ? transform().map(point) : point);
+}
+
+/*!
+ \fn QPointF QGraphicsItem::mapToParent(qreal x, qreal y) const
+ \overload
+
+ This convenience function is equivalent to calling mapToParent(QPointF(\a
+ x, \a y)).
+*/
+
+/*!
+ Maps the point \a point, which is in this item's coordinate system, to the
+ scene's coordinate system, and returns the mapped coordinate.
+
+ \sa mapToItem(), mapToParent(), transform(), mapFromScene(), {The Graphics
+ View Coordinate System}
+*/
+QPointF QGraphicsItem::mapToScene(const QPointF &point) const
+{
+ return sceneTransform().map(point);
+}
+
+/*!
+ \fn QPointF QGraphicsItem::mapToScene(qreal x, qreal y) const
+ \overload
+
+ This convenience function is equivalent to calling mapToScene(QPointF(\a
+ x, \a y)).
+*/
+
+/*!
+ Maps the rectangle \a rect, which is in this item's coordinate system, to
+ \a item's coordinate system, and returns the mapped rectangle as a polygon.
+
+ If \a item is 0, this function returns the same as mapToScene().
+
+ \sa itemTransform(), mapToParent(), mapToScene(), mapFromItem(), {The
+ Graphics View Coordinate System}
+*/
+QPolygonF QGraphicsItem::mapToItem(const QGraphicsItem *item, const QRectF &rect) const
+{
+ if (item)
+ return itemTransform(item).map(rect);
+ return mapToScene(rect);
+}
+
+/*!
+ \fn QPolygonF QGraphicsItem::mapToItem(const QGraphicsItem *item, qreal x, qreal y, qreal w, qreal h) const
+ \since 4.3
+
+ This convenience function is equivalent to calling mapToItem(item, QRectF(\a x, \a y, \a w, \a h)).
+*/
+
+/*!
+ Maps the rectangle \a rect, which is in this item's coordinate system, to
+ its parent's coordinate system, and returns the mapped rectangle as a
+ polygon. If the item has no parent, \a rect will be mapped to the scene's
+ coordinate system.
+
+ \sa mapToScene(), mapToItem(), mapFromParent(), {The Graphics View
+ Coordinate System}
+*/
+QPolygonF QGraphicsItem::mapToParent(const QRectF &rect) const
+{
+ if (!d_ptr->hasTransform)
+ return QPolygonF(rect.translated(d_ptr->pos));
+ return transform().map(rect.translated(d_ptr->pos));
+}
+
+/*!
+ \fn QPolygonF QGraphicsItem::mapToParent(qreal x, qreal y, qreal w, qreal h) const
+ \since 4.3
+
+ This convenience function is equivalent to calling mapToParent(QRectF(\a x, \a y, \a w, \a h)).
+*/
+
+/*!
+ Maps the rectangle \a rect, which is in this item's coordinate system, to
+ the scene's coordinate system, and returns the mapped rectangle as a polygon.
+
+ \sa mapToParent(), mapToItem(), mapFromScene(), {The Graphics View
+ Coordinate System}
+*/
+QPolygonF QGraphicsItem::mapToScene(const QRectF &rect) const
+{
+ return sceneTransform().map(rect);
+}
+
+/*!
+ \fn QPolygonF QGraphicsItem::mapToScene(qreal x, qreal y, qreal w, qreal h) const
+ \since 4.3
+
+ This convenience function is equivalent to calling mapToScene(QRectF(\a x, \a y, \a w, \a h)).
+*/
+
+/*!
+ \since 4.5
+
+ Maps the rectangle \a rect, which is in this item's coordinate system, to
+ \a item's coordinate system, and returns the mapped rectangle as a new
+ rectangle (i.e., the bounding rectangle of the resulting polygon).
+
+ If \a item is 0, this function returns the same as mapRectToScene().
+
+ \sa itemTransform(), mapToParent(), mapToScene(), mapFromItem(), {The
+ Graphics View Coordinate System}
+*/
+QRectF QGraphicsItem::mapRectToItem(const QGraphicsItem *item, const QRectF &rect) const
+{
+ if (item)
+ return itemTransform(item).mapRect(rect);
+ return mapRectToScene(rect);
+}
+
+/*!
+ \fn QRectF QGraphicsItem::mapRectToItem(const QGraphicsItem *item, qreal x, qreal y, qreal w, qreal h) const
+ \since 4.5
+
+ This convenience function is equivalent to calling mapRectToItem(item, QRectF(\a x, \a y, \a w, \a h)).
+*/
+
+/*!
+ \since 4.5
+
+ Maps the rectangle \a rect, which is in this item's coordinate system, to
+ its parent's coordinate system, and returns the mapped rectangle as a new
+ rectangle (i.e., the bounding rectangle of the resulting polygon).
+
+ \sa itemTransform(), mapToParent(), mapToScene(), mapFromItem(), {The
+ Graphics View Coordinate System}
+*/
+QRectF QGraphicsItem::mapRectToParent(const QRectF &rect) const
+{
+ QRectF r = rect.translated(d_ptr->pos.x(), d_ptr->pos.y());
+ return !d_ptr->hasTransform ? r : transform().mapRect(r);
+}
+
+/*!
+ \fn QRectF QGraphicsItem::mapRectToParent(qreal x, qreal y, qreal w, qreal h) const
+ \since 4.5
+
+ This convenience function is equivalent to calling mapRectToParent(QRectF(\a x, \a y, \a w, \a h)).
+*/
+
+/*!
+ \since 4.5
+
+ Maps the rectangle \a rect, which is in this item's coordinate system, to
+ the scene coordinate system, and returns the mapped rectangle as a new
+ rectangle (i.e., the bounding rectangle of the resulting polygon).
+
+ \sa itemTransform(), mapToParent(), mapToScene(), mapFromItem(), {The
+ Graphics View Coordinate System}
+*/
+QRectF QGraphicsItem::mapRectToScene(const QRectF &rect) const
+{
+ return sceneTransform().mapRect(rect);
+}
+
+/*!
+ \fn QRectF QGraphicsItem::mapRectToScene(qreal x, qreal y, qreal w, qreal h) const
+ \since 4.5
+
+ This convenience function is equivalent to calling mapRectToScene(QRectF(\a x, \a y, \a w, \a h)).
+*/
+
+/*!
+ \since 4.5
+
+ Maps the rectangle \a rect, which is in \a item's coordinate system, to
+ this item's coordinate system, and returns the mapped rectangle as a new
+ rectangle (i.e., the bounding rectangle of the resulting polygon).
+
+ If \a item is 0, this function returns the same as mapRectFromScene().
+
+ \sa itemTransform(), mapToParent(), mapToScene(), mapFromItem(), {The
+ Graphics View Coordinate System}
+*/
+QRectF QGraphicsItem::mapRectFromItem(const QGraphicsItem *item, const QRectF &rect) const
+{
+ if (item)
+ return item->itemTransform(this).mapRect(rect);
+ return mapRectFromScene(rect);
+}
+
+/*!
+ \fn QRectF QGraphicsItem::mapRectFromItem(const QGraphicsItem *item, qreal x, qreal y, qreal w, qreal h) const
+ \since 4.5
+
+ This convenience function is equivalent to calling mapRectFromItem(item, QRectF(\a x, \a y, \a w, \a h)).
+*/
+
+/*!
+ \since 4.5
+
+ Maps the rectangle \a rect, which is in this item's parent's coordinate
+ system, to this item's coordinate system, and returns the mapped rectangle
+ as a new rectangle (i.e., the bounding rectangle of the resulting
+ polygon).
+
+ \sa itemTransform(), mapToParent(), mapToScene(), mapFromItem(), {The
+ Graphics View Coordinate System}
+*/
+QRectF QGraphicsItem::mapRectFromParent(const QRectF &rect) const
+{
+ QRectF r = rect.translated(-d_ptr->pos);
+ return d_ptr->hasTransform ? transform().inverted().mapRect(r) : r;
+}
+
+/*!
+ \fn QRectF QGraphicsItem::mapRectFromParent(qreal x, qreal y, qreal w, qreal h) const
+ \since 4.5
+
+ This convenience function is equivalent to calling mapRectFromParent(QRectF(\a x, \a y, \a w, \a h)).
+*/
+
+/*!
+ \since 4.5
+
+ Maps the rectangle \a rect, which is in scene coordinates, to this item's
+ coordinate system, and returns the mapped rectangle as a new rectangle
+ (i.e., the bounding rectangle of the resulting polygon).
+
+ \sa itemTransform(), mapToParent(), mapToScene(), mapFromItem(), {The
+ Graphics View Coordinate System}
+*/
+QRectF QGraphicsItem::mapRectFromScene(const QRectF &rect) const
+{
+ return sceneTransform().inverted().mapRect(rect);
+}
+
+/*!
+ \fn QRectF QGraphicsItem::mapRectFromScene(qreal x, qreal y, qreal w, qreal h) const
+ \since 4.5
+
+ This convenience function is equivalent to calling mapRectFromScene(QRectF(\a x, \a y, \a w, \a h)).
+*/
+
+/*!
+ Maps the polygon \a polygon, which is in this item's coordinate system, to
+ \a item's coordinate system, and returns the mapped polygon.
+
+ If \a item is 0, this function returns the same as mapToScene().
+
+ \sa itemTransform(), mapToParent(), mapToScene(), mapFromItem(), {The
+ Graphics View Coordinate System}
+*/
+QPolygonF QGraphicsItem::mapToItem(const QGraphicsItem *item, const QPolygonF &polygon) const
+{
+ if (item)
+ return itemTransform(item).map(polygon);
+ return mapToScene(polygon);
+}
+
+/*!
+ Maps the polygon \a polygon, which is in this item's coordinate system, to
+ its parent's coordinate system, and returns the mapped polygon. If the
+ item has no parent, \a polygon will be mapped to the scene's coordinate
+ system.
+
+ \sa mapToScene(), mapToItem(), mapFromParent(), {The Graphics View
+ Coordinate System}
+*/
+QPolygonF QGraphicsItem::mapToParent(const QPolygonF &polygon) const
+{
+ QPolygonF p = polygon;
+ p.translate(d_ptr->pos);
+ return d_ptr->hasTransform ? transform().map(p) : p;
+}
+
+/*!
+ Maps the polygon \a polygon, which is in this item's coordinate system, to
+ the scene's coordinate system, and returns the mapped polygon.
+
+ \sa mapToParent(), mapToItem(), mapFromScene(), {The Graphics View
+ Coordinate System}
+*/
+QPolygonF QGraphicsItem::mapToScene(const QPolygonF &polygon) const
+{
+ return sceneTransform().map(polygon);
+}
+
+/*!
+ Maps the path \a path, which is in this item's coordinate system, to
+ \a item's coordinate system, and returns the mapped path.
+
+ If \a item is 0, this function returns the same as mapToScene().
+
+ \sa itemTransform(), mapToParent(), mapToScene(), mapFromItem(), {The
+ Graphics View Coordinate System}
+*/
+QPainterPath QGraphicsItem::mapToItem(const QGraphicsItem *item, const QPainterPath &path) const
+{
+ if (item)
+ return itemTransform(item).map(path);
+ return mapToScene(path);
+}
+
+/*!
+ Maps the path \a path, which is in this item's coordinate system, to
+ its parent's coordinate system, and returns the mapped path. If the
+ item has no parent, \a path will be mapped to the scene's coordinate
+ system.
+
+ \sa mapToScene(), mapToItem(), mapFromParent(), {The Graphics View
+ Coordinate System}
+*/
+QPainterPath QGraphicsItem::mapToParent(const QPainterPath &path) const
+{
+ return d_ptr->parent ? itemTransform(d_ptr->parent).map(path) : mapToScene(path);
+}
+
+/*!
+ Maps the path \a path, which is in this item's coordinate system, to
+ the scene's coordinate system, and returns the mapped path.
+
+ \sa mapToParent(), mapToItem(), mapFromScene(), {The Graphics View
+ Coordinate System}
+*/
+QPainterPath QGraphicsItem::mapToScene(const QPainterPath &path) const
+{
+ return sceneTransform().map(path);
+}
+
+/*!
+ Maps the point \a point, which is in \a item's coordinate system, to this
+ item's coordinate system, and returns the mapped coordinate.
+
+ If \a item is 0, this function returns the same as mapFromScene().
+
+ \sa itemTransform(), mapFromParent(), mapFromScene(), transform(), mapToItem(), {The Graphics
+ View Coordinate System}
+*/
+QPointF QGraphicsItem::mapFromItem(const QGraphicsItem *item, const QPointF &point) const
+{
+ if (item)
+ return item->itemTransform(this).map(point);
+ return mapFromScene(point);
+}
+
+/*!
+ \fn QPointF QGraphicsItem::mapFromItem(const QGraphicsItem *item, qreal x, qreal y) const
+ \overload
+
+ This convenience function is equivalent to calling mapFromItem(\a item,
+ QPointF(\a x, \a y)).
+*/
+
+/*!
+ Maps the point \a point, which is in this item's parent's coordinate
+ system, to this item's coordinate system, and returns the mapped
+ coordinate.
+
+ \sa mapFromItem(), mapFromScene(), transform(), mapToParent(), {The Graphics
+ View Coordinate System}
+*/
+QPointF QGraphicsItem::mapFromParent(const QPointF &point) const
+{
+ if (d_ptr->hasTransform)
+ return transform().inverted().map(point - d_ptr->pos);
+ return point - d_ptr->pos;
+}
+
+/*!
+ \fn QPointF QGraphicsItem::mapFromParent(qreal x, qreal y) const
+ \overload
+
+ This convenience function is equivalent to calling
+ mapFromParent(QPointF(\a x, \a y)).
+*/
+
+/*!
+ Maps the point \a point, which is in this item's scene's coordinate
+ system, to this item's coordinate system, and returns the mapped
+ coordinate.
+
+ \sa mapFromItem(), mapFromParent(), transform(), mapToScene(), {The Graphics
+ View Coordinate System}
+*/
+QPointF QGraphicsItem::mapFromScene(const QPointF &point) const
+{
+ return sceneTransform().inverted().map(point);
+}
+
+/*!
+ \fn QPointF QGraphicsItem::mapFromScene(qreal x, qreal y) const
+ \overload
+
+ This convenience function is equivalent to calling mapFromScene(QPointF(\a
+ x, \a y)).
+*/
+
+/*!
+ Maps the rectangle \a rect, which is in \a item's coordinate system, to
+ this item's coordinate system, and returns the mapped rectangle as a
+ polygon.
+
+ If \a item is 0, this function returns the same as mapFromScene()
+
+ \sa itemTransform(), mapToItem(), mapFromParent(), transform(), {The Graphics View Coordinate
+ System}
+*/
+QPolygonF QGraphicsItem::mapFromItem(const QGraphicsItem *item, const QRectF &rect) const
+{
+ if (item)
+ return item->itemTransform(this).map(rect);
+ return mapFromScene(rect);
+}
+
+/*!
+ \fn QPolygonF QGraphicsItem::mapFromItem(const QGraphicsItem *item, qreal x, qreal y, qreal w, qreal h) const
+ \since 4.3
+
+ This convenience function is equivalent to calling mapFromItem(item, QRectF(\a x, \a y, \a w, \a h)).
+*/
+
+/*!
+ Maps the rectangle \a rect, which is in this item's parent's coordinate
+ system, to this item's coordinate system, and returns the mapped rectangle
+ as a polygon.
+
+ \sa mapToParent(), mapFromItem(), transform(), {The Graphics View Coordinate
+ System}
+*/
+QPolygonF QGraphicsItem::mapFromParent(const QRectF &rect) const
+{
+ QRectF r = rect.translated(-d_ptr->pos);
+ return d_ptr->hasTransform ? transform().inverted().map(r) : r;
+}
+
+/*!
+ \fn QPolygonF QGraphicsItem::mapFromParent(qreal x, qreal y, qreal w, qreal h) const
+ \since 4.3
+
+ This convenience function is equivalent to calling mapFromItem(QRectF(\a x, \a y, \a w, \a h)).
+*/
+
+/*!
+ Maps the rectangle \a rect, which is in this item's scene's coordinate
+ system, to this item's coordinate system, and returns the mapped rectangle
+ as a polygon.
+
+ \sa mapToScene(), mapFromItem(), transform(), {The Graphics View Coordinate
+ System}
+*/
+QPolygonF QGraphicsItem::mapFromScene(const QRectF &rect) const
+{
+ return sceneTransform().inverted().map(rect);
+}
+
+/*!
+ \fn QPolygonF QGraphicsItem::mapFromScene(qreal x, qreal y, qreal w, qreal h) const
+ \since 4.3
+
+ This convenience function is equivalent to calling mapFromScene(QRectF(\a x, \a y, \a w, \a h)).
+*/
+
+/*!
+ Maps the polygon \a polygon, which is in \a item's coordinate system, to
+ this item's coordinate system, and returns the mapped polygon.
+
+ If \a item is 0, this function returns the same as mapFromScene().
+
+ \sa itemTransform(), mapToItem(), mapFromParent(), transform(), {The
+ Graphics View Coordinate System}
+*/
+QPolygonF QGraphicsItem::mapFromItem(const QGraphicsItem *item, const QPolygonF &polygon) const
+{
+ if (item)
+ return item->itemTransform(this).map(polygon);
+ return mapFromScene(polygon);
+}
+
+/*!
+ Maps the polygon \a polygon, which is in this item's parent's coordinate
+ system, to this item's coordinate system, and returns the mapped polygon.
+
+ \sa mapToParent(), mapToItem(), transform(), {The Graphics View Coordinate
+ System}
+*/
+QPolygonF QGraphicsItem::mapFromParent(const QPolygonF &polygon) const
+{
+ QPolygonF p = polygon;
+ p.translate(-d_ptr->pos);
+ return d_ptr->hasTransform ? transform().inverted().map(p) : p;
+}
+
+/*!
+ Maps the polygon \a polygon, which is in this item's scene's coordinate
+ system, to this item's coordinate system, and returns the mapped polygon.
+
+ \sa mapToScene(), mapFromParent(), transform(), {The Graphics View Coordinate
+ System}
+*/
+QPolygonF QGraphicsItem::mapFromScene(const QPolygonF &polygon) const
+{
+ return sceneTransform().inverted().map(polygon);
+}
+
+/*!
+ Maps the path \a path, which is in \a item's coordinate system, to
+ this item's coordinate system, and returns the mapped path.
+
+ If \a item is 0, this function returns the same as mapFromScene().
+
+ \sa itemTransform(), mapFromParent(), mapFromScene(), mapToItem(), {The
+ Graphics View Coordinate System}
+*/
+QPainterPath QGraphicsItem::mapFromItem(const QGraphicsItem *item, const QPainterPath &path) const
+{
+ if (item)
+ return item->itemTransform(this).map(path);
+ return mapFromScene(path);
+}
+
+/*!
+ Maps the path \a path, which is in this item's parent's coordinate
+ system, to this item's coordinate system, and returns the mapped path.
+
+ \sa mapFromScene(), mapFromItem(), mapToParent(), {The Graphics View
+ Coordinate System}
+*/
+QPainterPath QGraphicsItem::mapFromParent(const QPainterPath &path) const
+{
+ if (d_ptr->parent)
+ return d_ptr->parent->itemTransform(this).map(path);
+ return mapFromScene(path);
+}
+
+/*!
+ Maps the path \a path, which is in this item's scene's coordinate
+ system, to this item's coordinate system, and returns the mapped path.
+
+ \sa mapFromParent(), mapFromItem(), mapToScene(), {The Graphics View
+ Coordinate System}
+*/
+QPainterPath QGraphicsItem::mapFromScene(const QPainterPath &path) const
+{
+ return sceneTransform().inverted().map(path);
+}
+
+/*!
+ Returns true if this item is an ancestor of \a child (i.e., if this item
+ is \a child's parent, or one of \a child's parent's ancestors).
+
+ \sa parentItem()
+*/
+bool QGraphicsItem::isAncestorOf(const QGraphicsItem *child) const
+{
+ if (!child || child == this)
+ return false;
+ const QGraphicsItem *ancestor = child;
+ while ((ancestor = ancestor->d_ptr->parent)) {
+ if (ancestor == this)
+ return true;
+ }
+ return false;
+}
+
+/*!
+ \since 4.4
+
+ Returns the closest common ancestor item of this item and \a other, or 0
+ if either \a other is 0, or there is no common ancestor.
+
+ \sa isAncestorOf()
+*/
+QGraphicsItem *QGraphicsItem::commonAncestorItem(const QGraphicsItem *other) const
+{
+ if (!other)
+ return 0;
+ if (other == this)
+ return const_cast<QGraphicsItem *>(this);
+ const QGraphicsItem *thisw = this;
+ const QGraphicsItem *otherw = other;
+ int thisDepth = d_ptr->depth;
+ int otherDepth = other->d_ptr->depth;
+ while (thisDepth > otherDepth) {
+ thisw = thisw->d_ptr->parent;
+ --thisDepth;
+ }
+ while (otherDepth > thisDepth) {
+ otherw = otherw->d_ptr->parent;
+ --otherDepth;
+ }
+ while (thisw && thisw != otherw) {
+ thisw = thisw->d_ptr->parent;
+ otherw = otherw->d_ptr->parent;
+ }
+ return const_cast<QGraphicsItem *>(thisw);
+}
+
+/*!
+ \since 4,4
+ Returns true if this item is currently under the mouse cursor in one of
+ the views; otherwise, false is returned.
+
+ \sa QGraphicsScene::views(), QCursor::pos()
+*/
+bool QGraphicsItem::isUnderMouse() const
+{
+ Q_D(const QGraphicsItem);
+ if (!d->scene)
+ return false;
+
+ QPoint cursorPos = QCursor::pos();
+ foreach (QGraphicsView *view, d->scene->views()) {
+ if (contains(mapFromScene(view->mapToScene(view->mapFromGlobal(cursorPos)))))
+ return true;
+ }
+ return false;
+}
+
+/*!
+ Returns this item's custom data for the key \a key as a QVariant.
+
+ Custom item data is useful for storing arbitrary properties in any
+ item. Example:
+
+ \snippet doc/src/snippets/code/src_gui_graphicsview_qgraphicsitem.cpp 11
+
+ Qt does not use this feature for storing data; it is provided solely
+ for the convenience of the user.
+
+ \sa setData()
+*/
+QVariant QGraphicsItem::data(int key) const
+{
+ QGraphicsItemCustomDataStore *store = qt_dataStore();
+ if (!store->data.contains(this))
+ return QVariant();
+ return store->data.value(this).value(key);
+}
+
+/*!
+ Sets this item's custom data for the key \a key to \a value.
+
+ Custom item data is useful for storing arbitrary properties for any
+ item. Qt does not use this feature for storing data; it is provided solely
+ for the convenience of the user.
+
+ \sa data()
+*/
+void QGraphicsItem::setData(int key, const QVariant &value)
+{
+ qt_dataStore()->data[this][key] = value;
+}
+
+/*!
+ \fn T qgraphicsitem_cast(QGraphicsItem *item)
+ \relates QGraphicsItem
+ \since 4.2
+
+ Returns the given \a item cast to type T if \a item is of type T;
+ otherwise, 0 is returned.
+
+ \note To make this function work correctly with custom items, reimplement
+ the \l{QGraphicsItem::}{type()} function for each custom QGraphicsItem
+ subclass.
+
+ \sa QGraphicsItem::type(), QGraphicsItem::UserType
+*/
+
+/*!
+ Returns the type of an item as an int. All standard graphicsitem classes
+ are associated with a unique value; see QGraphicsItem::Type. This type
+ information is used by qgraphicsitem_cast() to distinguish between types.
+
+ The default implementation (in QGraphicsItem) returns UserType.
+
+ To enable use of qgraphicsitem_cast() with a custom item, reimplement this
+ function and declare a Type enum value equal to your custom item's type.
+ Custom items must return a value larger than or equal to UserType (65536).
+
+ For example:
+
+ \snippet doc/src/snippets/code/src_gui_graphicsview_qgraphicsitem.cpp QGraphicsItem type
+
+ \sa UserType
+*/
+int QGraphicsItem::type() const
+{
+ return (int)UserType;
+}
+
+/*!
+ Installs an event filter for this item on \a filterItem, causing
+ all events for this item to first pass through \a filterItem's
+ sceneEventFilter() function.
+
+ To filter another item's events, install this item as an event filter
+ for the other item. Example:
+
+ \snippet doc/src/snippets/code/src_gui_graphicsview_qgraphicsitem.cpp 12
+
+ An item can only filter events for other items in the same
+ scene. Also, an item cannot filter its own events; instead, you
+ can reimplement sceneEvent() directly.
+
+ Items must belong to a scene for scene event filters to be installed and
+ used.
+
+ \sa removeSceneEventFilter(), sceneEventFilter(), sceneEvent()
+*/
+void QGraphicsItem::installSceneEventFilter(QGraphicsItem *filterItem)
+{
+ if (!d_ptr->scene) {
+ qWarning("QGraphicsItem::installSceneEventFilter: event filters can only be installed"
+ " on items in a scene.");
+ return;
+ }
+ if (d_ptr->scene != filterItem->scene()) {
+ qWarning("QGraphicsItem::installSceneEventFilter: event filters can only be installed"
+ " on items in the same scene.");
+ return;
+ }
+ d_ptr->scene->d_func()->installSceneEventFilter(this, filterItem);
+}
+
+/*!
+ Removes an event filter on this item from \a filterItem.
+
+ \sa installSceneEventFilter()
+*/
+void QGraphicsItem::removeSceneEventFilter(QGraphicsItem *filterItem)
+{
+ if (!d_ptr->scene || d_ptr->scene != filterItem->scene())
+ return;
+ d_ptr->scene->d_func()->removeSceneEventFilter(this, filterItem);
+}
+
+/*!
+ Filters events for the item \a watched. \a event is the filtered
+ event.
+
+ Reimplementing this function in a subclass makes it possible
+ for the item to be used as an event filter for other items,
+ intercepting all the events send to those items before they are
+ able to respond.
+
+ Reimplementations must return true to prevent further processing of
+ a given event, ensuring that it will not be delivered to the watched
+ item, or return false to indicate that the event should be propagated
+ further by the event system.
+
+ \sa installSceneEventFilter()
+*/
+bool QGraphicsItem::sceneEventFilter(QGraphicsItem *watched, QEvent *event)
+{
+ Q_UNUSED(watched);
+ Q_UNUSED(event);
+ return false;
+}
+
+/*!
+ This virtual function receives events to this item. Reimplement
+ this function to intercept events before they are dispatched to
+ the specialized event handlers contextMenuEvent(), focusInEvent(),
+ focusOutEvent(), hoverEnterEvent(), hoverMoveEvent(),
+ hoverLeaveEvent(), keyPressEvent(), keyReleaseEvent(),
+ mousePressEvent(), mouseReleaseEvent(), mouseMoveEvent(), and
+ mouseDoubleClickEvent().
+
+ Returns true if the event was recognized and handled; otherwise, (e.g., if
+ the event type was not recognized,) false is returned.
+
+ \a event is the intercepted event.
+*/
+bool QGraphicsItem::sceneEvent(QEvent *event)
+{
+ if (d_ptr->ancestorFlags & QGraphicsItemPrivate::AncestorHandlesChildEvents) {
+ if (event->type() == QEvent::HoverEnter || event->type() == QEvent::HoverLeave
+ || event->type() == QEvent::DragEnter || event->type() == QEvent::DragLeave) {
+ // Hover enter and hover leave events for children are ignored;
+ // hover move events are forwarded.
+ return true;
+ }
+
+ QGraphicsItem *handler = this;
+ do {
+ handler = handler->d_ptr->parent;
+ Q_ASSERT(handler);
+ } while (handler->d_ptr->ancestorFlags & QGraphicsItemPrivate::AncestorHandlesChildEvents);
+ // Forward the event to the closest parent that handles child
+ // events, mapping existing item-local coordinates to its
+ // coordinate system.
+ d_ptr->remapItemPos(event, handler);
+ handler->sceneEvent(event);
+ return true;
+ }
+
+ if (!d_ptr->visible) {
+ // Eaten
+ return true;
+ }
+
+ switch (event->type()) {
+ case QEvent::FocusIn:
+ focusInEvent(static_cast<QFocusEvent *>(event));
+ break;
+ case QEvent::FocusOut:
+ focusOutEvent(static_cast<QFocusEvent *>(event));
+ break;
+ case QEvent::GraphicsSceneContextMenu:
+ contextMenuEvent(static_cast<QGraphicsSceneContextMenuEvent *>(event));
+ break;
+ case QEvent::GraphicsSceneDragEnter:
+ dragEnterEvent(static_cast<QGraphicsSceneDragDropEvent *>(event));
+ break;
+ case QEvent::GraphicsSceneDragMove:
+ dragMoveEvent(static_cast<QGraphicsSceneDragDropEvent *>(event));
+ break;
+ case QEvent::GraphicsSceneDragLeave:
+ dragLeaveEvent(static_cast<QGraphicsSceneDragDropEvent *>(event));
+ break;
+ case QEvent::GraphicsSceneDrop:
+ dropEvent(static_cast<QGraphicsSceneDragDropEvent *>(event));
+ break;
+ case QEvent::GraphicsSceneHoverEnter:
+ hoverEnterEvent(static_cast<QGraphicsSceneHoverEvent *>(event));
+ break;
+ case QEvent::GraphicsSceneHoverMove:
+ hoverMoveEvent(static_cast<QGraphicsSceneHoverEvent *>(event));
+ break;
+ case QEvent::GraphicsSceneHoverLeave:
+ hoverLeaveEvent(static_cast<QGraphicsSceneHoverEvent *>(event));
+ break;
+ case QEvent::GraphicsSceneMouseMove:
+ mouseMoveEvent(static_cast<QGraphicsSceneMouseEvent *>(event));
+ break;
+ case QEvent::GraphicsSceneMousePress:
+ mousePressEvent(static_cast<QGraphicsSceneMouseEvent *>(event));
+ break;
+ case QEvent::GraphicsSceneMouseRelease:
+ mouseReleaseEvent(static_cast<QGraphicsSceneMouseEvent *>(event));
+ break;
+ case QEvent::GraphicsSceneMouseDoubleClick:
+ mouseDoubleClickEvent(static_cast<QGraphicsSceneMouseEvent *>(event));
+ break;
+ case QEvent::GraphicsSceneWheel:
+ wheelEvent(static_cast<QGraphicsSceneWheelEvent *>(event));
+ break;
+ case QEvent::KeyPress: {
+ QKeyEvent *k = static_cast<QKeyEvent *>(event);
+ if (k->key() == Qt::Key_Tab || k->key() == Qt::Key_Backtab) {
+ if (!(k->modifiers() & (Qt::ControlModifier | Qt::AltModifier))) { //### Add MetaModifier?
+ bool res = false;
+ if (k->key() == Qt::Key_Backtab
+ || (k->key() == Qt::Key_Tab && (k->modifiers() & Qt::ShiftModifier))) {
+ if (d_ptr->isWidget) {
+ res = static_cast<QGraphicsWidget *>(this)->focusNextPrevChild(false);
+ } else if (d_ptr->scene) {
+ res = d_ptr->scene->focusNextPrevChild(false);
+ }
+ } else if (k->key() == Qt::Key_Tab) {
+ if (d_ptr->isWidget) {
+ res = static_cast<QGraphicsWidget *>(this)->focusNextPrevChild(true);
+ } else if (d_ptr->scene) {
+ res = d_ptr->scene->focusNextPrevChild(true);
+ }
+ }
+ if (!res)
+ event->ignore();
+ return true;
+ }
+ }
+ keyPressEvent(static_cast<QKeyEvent *>(event));
+ break;
+ }
+ case QEvent::KeyRelease:
+ keyReleaseEvent(static_cast<QKeyEvent *>(event));
+ break;
+ case QEvent::InputMethod:
+ inputMethodEvent(static_cast<QInputMethodEvent *>(event));
+ break;
+ default:
+ return false;
+ }
+
+ return true;
+}
+
+/*!
+ This event handler can be reimplemented in a subclass to process context
+ menu events. The \a event parameter contains details about the event to
+ be handled.
+
+ If you ignore the event, (i.e., by calling QEvent::ignore(),) \a event
+ will propagate to any item beneath this item. If no items accept the
+ event, it will be ignored by the scene, and propagate to the view.
+
+ It's common to open a QMenu in response to receiving a context menu
+ event. Example:
+
+ \snippet doc/src/snippets/code/src_gui_graphicsview_qgraphicsitem.cpp 13
+
+ The default implementation ignores the event.
+
+ \sa sceneEvent()
+*/
+void QGraphicsItem::contextMenuEvent(QGraphicsSceneContextMenuEvent *event)
+{
+ event->ignore();
+}
+
+/*!
+ This event handler, for event \a event, can be reimplemented to receive
+ drag enter events for this item. Drag enter events are generated as the
+ cursor enters the item's area.
+
+ By accepting the event, (i.e., by calling QEvent::accept(),) the item will
+ accept drop events, in addition to receiving drag move and drag
+ leave. Otherwise, the event will be ignored and propagate to the item
+ beneath. If the event is accepted, the item will receive a drag move event
+ before control goes back to the event loop.
+
+ A common implementation of dragEnterEvent accepts or ignores \a event
+ depending on the associated mime data in \a event. Example:
+
+ \snippet doc/src/snippets/code/src_gui_graphicsview_qgraphicsitem.cpp 14
+
+ Items do not receive drag and drop events by default; to enable this
+ feature, call \c setAcceptDrops(true).
+
+ The default implementation does nothing.
+
+ \sa dropEvent(), dragMoveEvent(), dragLeaveEvent()
+*/
+void QGraphicsItem::dragEnterEvent(QGraphicsSceneDragDropEvent *event)
+{
+ Q_D(QGraphicsItem);
+ // binary compatibility workaround between 4.4 and 4.5
+ if (d->isProxyWidget())
+ static_cast<QGraphicsProxyWidget*>(this)->dragEnterEvent(event);
+}
+
+/*!
+ This event handler, for event \a event, can be reimplemented to receive
+ drag leave events for this item. Drag leave events are generated as the
+ cursor leaves the item's area. Most often you will not need to reimplement
+ this function, but it can be useful for resetting state in your item
+ (e.g., highlighting).
+
+ Calling QEvent::ignore() or QEvent::accept() on \a event has no effect.
+
+ Items do not receive drag and drop events by default; to enable this
+ feature, call \c setAcceptDrops(true).
+
+ The default implementation does nothing.
+
+ \sa dragEnterEvent(), dropEvent(), dragMoveEvent()
+*/
+void QGraphicsItem::dragLeaveEvent(QGraphicsSceneDragDropEvent *event)
+{
+ Q_D(QGraphicsItem);
+ // binary compatibility workaround between 4.4 and 4.5
+ if (d->isProxyWidget())
+ static_cast<QGraphicsProxyWidget*>(this)->dragLeaveEvent(event);
+}
+
+/*!
+ This event handler, for event \a event, can be reimplemented to receive
+ drag move events for this item. Drag move events are generated as the
+ cursor moves around inside the item's area. Most often you will not need
+ to reimplement this function; it is used to indicate that only parts of
+ the item can accept drops.
+
+ Calling QEvent::ignore() or QEvent::accept() on \a event toggles whether
+ or not the item will accept drops at the position from the event. By
+ default, \a event is accepted, indicating that the item allows drops at
+ the specified position.
+
+ Items do not receive drag and drop events by default; to enable this
+ feature, call \c setAcceptDrops(true).
+
+ The default implementation does nothing.
+
+ \sa dropEvent(), dragEnterEvent(), dragLeaveEvent()
+*/
+void QGraphicsItem::dragMoveEvent(QGraphicsSceneDragDropEvent *event)
+{
+ Q_D(QGraphicsItem);
+ // binary compatibility workaround between 4.4 and 4.5
+ if (d->isProxyWidget())
+ static_cast<QGraphicsProxyWidget*>(this)->dragMoveEvent(event);
+}
+
+/*!
+ This event handler, for event \a event, can be reimplemented to receive
+ drop events for this item. Items can only receive drop events if the last
+ drag move event was accepted.
+
+ Calling QEvent::ignore() or QEvent::accept() on \a event has no effect.
+
+ Items do not receive drag and drop events by default; to enable this
+ feature, call \c setAcceptDrops(true).
+
+ The default implementation does nothing.
+
+ \sa dragEnterEvent(), dragMoveEvent(), dragLeaveEvent()
+*/
+void QGraphicsItem::dropEvent(QGraphicsSceneDragDropEvent *event)
+{
+ Q_D(QGraphicsItem);
+ // binary compatibility workaround between 4.4 and 4.5
+ if (d->isProxyWidget())
+ static_cast<QGraphicsProxyWidget*>(this)->dropEvent(event);
+}
+
+/*!
+ This event handler, for event \a event, can be reimplemented to receive
+ focus in events for this item. The default implementation calls
+ ensureVisible().
+
+ \sa focusOutEvent(), sceneEvent()
+*/
+void QGraphicsItem::focusInEvent(QFocusEvent *event)
+{
+ Q_UNUSED(event);
+}
+
+/*!
+ This event handler, for event \a event, can be reimplemented to receive
+ focus out events for this item. The default implementation does nothing.
+
+ \sa focusInEvent(), sceneEvent()
+*/
+void QGraphicsItem::focusOutEvent(QFocusEvent *event)
+{
+ Q_UNUSED(event);
+}
+
+/*!
+ This event handler, for event \a event, can be reimplemented to receive
+ hover enter events for this item. The default implementation calls
+ update(); otherwise it does nothing.
+
+ Calling QEvent::ignore() or QEvent::accept() on \a event has no effect.
+
+ \sa hoverMoveEvent(), hoverLeaveEvent(), sceneEvent(), setAcceptHoverEvents()
+*/
+void QGraphicsItem::hoverEnterEvent(QGraphicsSceneHoverEvent *event)
+{
+ Q_UNUSED(event);
+ d_ptr->updateHelper();
+}
+
+/*!
+ This event handler, for event \a event, can be reimplemented to receive
+ hover move events for this item. The default implementation does nothing.
+
+ Calling QEvent::ignore() or QEvent::accept() on \a event has no effect.
+
+ \sa hoverEnterEvent(), hoverLeaveEvent(), sceneEvent(), setAcceptHoverEvents()
+*/
+void QGraphicsItem::hoverMoveEvent(QGraphicsSceneHoverEvent *event)
+{
+ Q_UNUSED(event);
+}
+
+/*!
+ This event handler, for event \a event, can be reimplemented to receive
+ hover leave events for this item. The default implementation calls
+ update(); otherwise it does nothing.
+
+ Calling QEvent::ignore() or QEvent::accept() on \a event has no effect.
+
+ \sa hoverEnterEvent(), hoverMoveEvent(), sceneEvent(), setAcceptHoverEvents()
+*/
+void QGraphicsItem::hoverLeaveEvent(QGraphicsSceneHoverEvent *event)
+{
+ Q_UNUSED(event);
+ d_ptr->updateHelper();
+}
+
+/*!
+ This event handler, for event \a event, can be reimplemented to
+ receive key press events for this item. The default implementation
+ ignores the event. If you reimplement this handler, the event will by
+ default be accepted.
+
+ Note that key events are only received for items that set the
+ ItemIsFocusable flag, and that have keyboard input focus.
+
+ \sa keyReleaseEvent(), setFocus(), QGraphicsScene::setFocusItem(),
+ sceneEvent()
+*/
+void QGraphicsItem::keyPressEvent(QKeyEvent *event)
+{
+ event->ignore();
+}
+
+/*!
+ This event handler, for event \a event, can be reimplemented to receive
+ key release events for this item. The default implementation
+ ignores the event. If you reimplement this handler, the event will by
+ default be accepted.
+
+ Note that key events are only received for items that set the
+ ItemIsFocusable flag, and that have keyboard input focus.
+
+ \sa keyPressEvent(), setFocus(), QGraphicsScene::setFocusItem(),
+ sceneEvent()
+*/
+void QGraphicsItem::keyReleaseEvent(QKeyEvent *event)
+{
+ event->ignore();
+}
+
+/*!
+ This event handler, for event \a event, can be reimplemented to
+ receive mouse press events for this item. Mouse press events are
+ only delivered to items that accept the mouse button that is
+ pressed. By default, an item accepts all mouse buttons, but you
+ can change this by calling setAcceptedMouseButtons().
+
+ The mouse press event decides which item should become the mouse
+ grabber (see QGraphicsScene::mouseGrabberItem()). If you do not
+ reimplement this function, the press event will propagate to any
+ topmost item beneath this item, and no other mouse events will be
+ delivered to this item.
+
+ If you do reimplement this function, \a event will by default be
+ accepted (see QEvent::accept()), and this item is then the mouse
+ grabber. This allows the item to receive future move, release and
+ doubleclick events. If you call QEvent::ignore() on \a event, this
+ item will lose the mouse grab, and \a event will propagate to any
+ topmost item beneath. No further mouse events will be delivered to
+ this item unless a new mouse press event is received.
+
+ The default implementation handles basic item interaction, such as
+ selection and moving. If you want to keep the base implementation
+ when reimplementing this function, call
+ QGraphicsItem::mousePressEvent() in your reimplementation.
+
+ The event is \l{QEvent::ignore()}d for items that are neither
+ \l{QGraphicsItem::ItemIsMovable}{movable} nor
+ \l{QGraphicsItem::ItemIsSelectable}{selectable}.
+
+ \sa mouseMoveEvent(), mouseReleaseEvent(),
+ mouseDoubleClickEvent(), sceneEvent()
+*/
+void QGraphicsItem::mousePressEvent(QGraphicsSceneMouseEvent *event)
+{
+ if (event->button() == Qt::LeftButton && (flags() & ItemIsSelectable)) {
+ bool multiSelect = (event->modifiers() & Qt::ControlModifier) != 0;
+ if (!multiSelect) {
+ if (!d_ptr->selected) {
+ if (QGraphicsScene *scene = d_ptr->scene) {
+ ++scene->d_func()->selectionChanging;
+ scene->clearSelection();
+ --scene->d_func()->selectionChanging;
+ }
+ setSelected(true);
+ }
+ }
+ } else if (!(flags() & ItemIsMovable)) {
+ event->ignore();
+ }
+ if (d_ptr->isWidget) {
+ // Qt::Popup closes when you click outside.
+ QGraphicsWidget *w = static_cast<QGraphicsWidget *>(this);
+ if (w->windowFlags() & Qt::Popup) {
+ event->accept();
+ if (!w->rect().contains(event->pos()))
+ w->close();
+ }
+ }
+}
+
+/*!
+ obsolete
+*/
+bool _qt_movableAncestorIsSelected(const QGraphicsItem *item)
+{
+ const QGraphicsItem *parent = item->parentItem();
+ return parent && (((parent->flags() & QGraphicsItem::ItemIsMovable) && parent->isSelected()) || _qt_movableAncestorIsSelected(parent));
+}
+
+bool QGraphicsItemPrivate::movableAncestorIsSelected(const QGraphicsItem *item)
+{
+ const QGraphicsItem *parent = item->d_ptr->parent;
+ return parent && (((parent->flags() & QGraphicsItem::ItemIsMovable) && parent->isSelected()) || _qt_movableAncestorIsSelected(parent));
+}
+
+/*!
+ This event handler, for event \a event, can be reimplemented to
+ receive mouse move events for this item. If you do receive this
+ event, you can be certain that this item also received a mouse
+ press event, and that this item is the current mouse grabber.
+
+ Calling QEvent::ignore() or QEvent::accept() on \a event has no
+ effect.
+
+ The default implementation handles basic item interaction, such as
+ selection and moving. If you want to keep the base implementation
+ when reimplementing this function, call
+ QGraphicsItem::mouseMoveEvent() in your reimplementation.
+
+ Please note that mousePressEvent() decides which graphics item it
+ is that receives mouse events. See the mousePressEvent()
+ description for details.
+
+ \sa mousePressEvent(), mouseReleaseEvent(),
+ mouseDoubleClickEvent(), sceneEvent()
+*/
+void QGraphicsItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
+{
+ if ((event->buttons() & Qt::LeftButton) && (flags() & ItemIsMovable)) {
+ // Determine the list of items that need to be moved.
+ QList<QGraphicsItem *> selectedItems;
+ QMap<QGraphicsItem *, QPointF> initialPositions;
+ if (d_ptr->scene) {
+ selectedItems = d_ptr->scene->selectedItems();
+ initialPositions = d_ptr->scene->d_func()->movingItemsInitialPositions;
+ if (initialPositions.isEmpty()) {
+ foreach (QGraphicsItem *item, selectedItems)
+ initialPositions[item] = item->pos();
+ initialPositions[this] = pos();
+ }
+ d_ptr->scene->d_func()->movingItemsInitialPositions = initialPositions;
+ }
+
+ // Find the active view.
+ QGraphicsView *view = 0;
+ if (event->widget())
+ view = qobject_cast<QGraphicsView *>(event->widget()->parentWidget());
+
+ // Move all selected items
+ int i = 0;
+ bool movedMe = false;
+ while (i <= selectedItems.size()) {
+ QGraphicsItem *item = 0;
+ if (i < selectedItems.size())
+ item = selectedItems.at(i);
+ else
+ item = this;
+ if (item == this) {
+ // Slightly clumsy-looking way to ensure that "this" is part
+ // of the list of items to move, this is to avoid allocations
+ // (appending this item to the list of selected items causes a
+ // detach).
+ if (movedMe)
+ break;
+ movedMe = true;
+ }
+
+ if ((item->flags() & ItemIsMovable) && !QGraphicsItemPrivate::movableAncestorIsSelected(item)) {
+ QPointF currentParentPos;
+ QPointF buttonDownParentPos;
+ if (item->d_ptr->ancestorFlags & QGraphicsItemPrivate::AncestorIgnoresTransformations) {
+ // Items whose ancestors ignore transformations need to
+ // map screen coordinates to local coordinates, then map
+ // those to the parent.
+ QTransform viewToItemTransform = (item->deviceTransform(view->viewportTransform())).inverted();
+ currentParentPos = mapToParent(viewToItemTransform.map(QPointF(view->mapFromGlobal(event->screenPos()))));
+ buttonDownParentPos = mapToParent(viewToItemTransform.map(QPointF(view->mapFromGlobal(event->buttonDownScreenPos(Qt::LeftButton)))));
+ } else if (item->flags() & ItemIgnoresTransformations) {
+ // Root items that ignore transformations need to
+ // calculate their diff by mapping viewport coordinates
+ // directly to parent coordinates.
+ QTransform viewToParentTransform = (item->transform().translate(item->d_ptr->pos.x(), item->d_ptr->pos.y()))
+ * (item->sceneTransform() * view->viewportTransform()).inverted();
+ currentParentPos = viewToParentTransform.map(QPointF(view->mapFromGlobal(event->screenPos())));
+ buttonDownParentPos = viewToParentTransform.map(QPointF(view->mapFromGlobal(event->buttonDownScreenPos(Qt::LeftButton))));
+ } else {
+ // All other items simply map from the scene.
+ currentParentPos = item->mapToParent(item->mapFromScene(event->scenePos()));
+ buttonDownParentPos = item->mapToParent(item->mapFromScene(event->buttonDownScenePos(Qt::LeftButton)));
+ }
+
+ item->setPos(initialPositions.value(item) + currentParentPos - buttonDownParentPos);
+
+ if (item->flags() & ItemIsSelectable)
+ item->setSelected(true);
+ }
+ ++i;
+ }
+
+ } else {
+ event->ignore();
+ }
+}
+
+/*!
+ This event handler, for event \a event, can be reimplemented to
+ receive mouse release events for this item.
+
+ Calling QEvent::ignore() or QEvent::accept() on \a event has no
+ effect.
+
+ The default implementation handles basic item interaction, such as
+ selection and moving. If you want to keep the base implementation
+ when reimplementing this function, call
+ QGraphicsItem::mouseReleaseEvent() in your reimplementation.
+
+ Please note that mousePressEvent() decides which graphics item it
+ is that receives mouse events. See the mousePressEvent()
+ description for details.
+
+ \sa mousePressEvent(), mouseMoveEvent(), mouseDoubleClickEvent(),
+ sceneEvent()
+*/
+void QGraphicsItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
+{
+ if (flags() & ItemIsSelectable) {
+ bool multiSelect = (event->modifiers() & Qt::ControlModifier) != 0;
+ if (event->scenePos() == event->buttonDownScenePos(Qt::LeftButton)) {
+ // The item didn't move
+ if (multiSelect) {
+ setSelected(!isSelected());
+ } else {
+ bool selectionChanged = false;
+ if (QGraphicsScene *scene = d_ptr->scene) {
+ ++scene->d_func()->selectionChanging;
+ // Clear everything but this item. Bypass
+ // QGraphicsScene::clearSelection()'s default behavior by
+ // temporarily removing this item from the selection list.
+ if (d_ptr->selected) {
+ scene->d_func()->selectedItems.remove(this);
+ foreach (QGraphicsItem *item, scene->d_func()->selectedItems) {
+ if (item->isSelected()) {
+ selectionChanged = true;
+ break;
+ }
+ }
+ }
+ scene->clearSelection();
+ if (d_ptr->selected)
+ scene->d_func()->selectedItems.insert(this);
+ --scene->d_func()->selectionChanging;
+ if (selectionChanged)
+ emit d_ptr->scene->selectionChanged();
+ }
+ setSelected(true);
+ }
+ }
+ }
+ if (d_ptr->scene && !event->buttons())
+ d_ptr->scene->d_func()->movingItemsInitialPositions.clear();
+}
+
+/*!
+ This event handler, for event \a event, can be reimplemented to
+ receive mouse doubleclick events for this item.
+
+ When doubleclicking an item, the item will first receive a mouse
+ press event, followed by a release event (i.e., a click), then a
+ doubleclick event, and finally a release event.
+
+ Calling QEvent::ignore() or QEvent::accept() on \a event has no
+ effect.
+
+ The default implementation calls mousePressEvent(). If you want to
+ keep the base implementation when reimplementing this function,
+ call QGraphicsItem::mouseDoubleClickEvent() in your
+ reimplementation.
+
+ Note that an item will not receive double click events if it is
+ neither \l {QGraphicsItem::ItemIsSelectable}{selectable} nor
+ \l{QGraphicsItem::ItemIsMovable}{movable} (single mouse clicks are
+ ignored in this case, and that stops the generation of double
+ clicks).
+
+ \sa mousePressEvent(), mouseMoveEvent(), mouseReleaseEvent(), sceneEvent()
+*/
+void QGraphicsItem::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event)
+{
+ mousePressEvent(event);
+}
+
+/*!
+ This event handler, for event \a event, can be reimplemented to receive
+ wheel events for this item. If you reimplement this function, \a event
+ will be accepted by default.
+
+ If you ignore the event, (i.e., by calling QEvent::ignore(),) it will
+ propagate to any item beneath this item. If no items accept the event, it
+ will be ignored by the scene, and propagate to the view (e.g., the view's
+ vertical scroll bar).
+
+ The default implementation ignores the event.
+
+ \sa sceneEvent()
+*/
+void QGraphicsItem::wheelEvent(QGraphicsSceneWheelEvent *event)
+{
+ event->ignore();
+}
+
+/*!
+ This event handler, for event \a event, can be reimplemented to receive
+ input method events for this item. The default implementation ignores the
+ event.
+
+ \sa inputMethodQuery(), sceneEvent()
+*/
+void QGraphicsItem::inputMethodEvent(QInputMethodEvent *event)
+{
+ event->ignore();
+}
+
+/*!
+ This method is only relevant for input items. It is used by the
+ input method to query a set of properties of the item to be able
+ to support complex input method operations, such as support for
+ surrounding text and reconversions. \a query specifies which
+ property is queried.
+
+ \sa inputMethodEvent()
+*/
+QVariant QGraphicsItem::inputMethodQuery(Qt::InputMethodQuery query) const
+{
+ if (isWidget()) {
+ // ### Qt 5: Remove. The reimplementation in
+ // QGraphicsProxyWidget solves this problem (but requires a
+ // recompile to take effect).
+ return d_ptr->inputMethodQueryHelper(query);
+ }
+
+ Q_UNUSED(query);
+ return QVariant();
+}
+
+/*!
+ This virtual function is called by QGraphicsItem to notify custom items
+ that some part of the item's state changes. By reimplementing this
+ function, your can react to a change, and in some cases, (depending on \a
+ change,) adjustments can be made.
+
+ \a change is the parameter of the item that is changing. \a value is the
+ new value; the type of the value depends on \a change.
+
+ Example:
+
+ \snippet doc/src/snippets/code/src_gui_graphicsview_qgraphicsitem.cpp 15
+
+ The default implementation does nothing, and returns \a value.
+
+ Note: Certain QGraphicsItem functions cannot be called in a
+ reimplementation of this function; see the GraphicsItemChange
+ documentation for details.
+
+ \sa GraphicsItemChange
+*/
+QVariant QGraphicsItem::itemChange(GraphicsItemChange change, const QVariant &value)
+{
+ Q_UNUSED(change);
+ return value;
+}
+
+/*!
+ \internal
+
+ Note: This is provided as a hook to avoid future problems related
+ to adding virtual functions.
+*/
+bool QGraphicsItem::supportsExtension(Extension extension) const
+{
+ Q_UNUSED(extension);
+ return false;
+}
+
+/*!
+ \internal
+
+ Note: This is provided as a hook to avoid future problems related
+ to adding virtual functions.
+*/
+void QGraphicsItem::setExtension(Extension extension, const QVariant &variant)
+{
+ Q_UNUSED(extension);
+ Q_UNUSED(variant);
+}
+
+/*!
+ \internal
+
+ Note: This is provided as a hook to avoid future problems related
+ to adding virtual functions.
+*/
+QVariant QGraphicsItem::extension(const QVariant &variant) const
+{
+ Q_UNUSED(variant);
+ return QVariant();
+}
+
+/*!
+ \internal
+
+ Adds this item to the scene's index. Called in conjunction with
+ removeFromIndex() to ensure the index bookkeeping is correct when
+ the item's position, transformation or shape changes.
+*/
+void QGraphicsItem::addToIndex()
+{
+ if (d_ptr->ancestorFlags & QGraphicsItemPrivate::AncestorClipsChildren) {
+ // ### add to child index only if applicable
+ return;
+ }
+ if (d_ptr->scene)
+ d_ptr->scene->d_func()->addToIndex(this);
+ d_ptr->updateHelper();
+}
+
+/*!
+ \internal
+
+ Removes this item from the scene's index. Called in conjunction
+ with addToIndex() to ensure the index bookkeeping is correct when
+ the item's position, transformation or shape changes.
+*/
+void QGraphicsItem::removeFromIndex()
+{
+ if (d_ptr->ancestorFlags & QGraphicsItemPrivate::AncestorClipsChildren) {
+ // ### remove from child index only if applicable
+ return;
+ }
+ d_ptr->updateHelper();
+ if (d_ptr->scene)
+ d_ptr->scene->d_func()->removeFromIndex(this);
+}
+
+/*!
+ Prepares the item for a geometry change. Call this function before
+ changing the bounding rect of an item to keep QGraphicsScene's index up to
+ date.
+
+ prepareGeometryChange() will call update() if this is necessary.
+
+ Example:
+
+ \snippet doc/src/snippets/code/src_gui_graphicsview_qgraphicsitem.cpp 16
+
+ \sa boundingRect()
+*/
+void QGraphicsItem::prepareGeometryChange()
+{
+ if (d_ptr->scene) {
+ d_ptr->updateHelper();
+
+ QGraphicsScenePrivate *scenePrivate = d_ptr->scene->d_func();
+ scenePrivate->removeFromIndex(this);
+ }
+}
+
+/*!
+ \internal
+
+ Highlights \a item as selected.
+
+ NOTE: This function is a duplicate of qt_graphicsItem_highlightSelected() in
+ qgraphicssvgitem.cpp!
+*/
+static void qt_graphicsItem_highlightSelected(
+ QGraphicsItem *item, QPainter *painter, const QStyleOptionGraphicsItem *option)
+{
+ const QRectF murect = painter->transform().mapRect(QRectF(0, 0, 1, 1));
+ if (qFuzzyCompare(qMax(murect.width(), murect.height()) + 1, 1))
+ return;
+
+ const QRectF mbrect = painter->transform().mapRect(item->boundingRect());
+ if (qMin(mbrect.width(), mbrect.height()) < qreal(1.0))
+ return;
+
+ qreal itemPenWidth;
+ switch (item->type()) {
+ case QGraphicsEllipseItem::Type:
+ itemPenWidth = static_cast<QGraphicsEllipseItem *>(item)->pen().widthF();
+ break;
+ case QGraphicsPathItem::Type:
+ itemPenWidth = static_cast<QGraphicsPathItem *>(item)->pen().widthF();
+ break;
+ case QGraphicsPolygonItem::Type:
+ itemPenWidth = static_cast<QGraphicsPolygonItem *>(item)->pen().widthF();
+ break;
+ case QGraphicsRectItem::Type:
+ itemPenWidth = static_cast<QGraphicsRectItem *>(item)->pen().widthF();
+ break;
+ case QGraphicsSimpleTextItem::Type:
+ itemPenWidth = static_cast<QGraphicsSimpleTextItem *>(item)->pen().widthF();
+ break;
+ case QGraphicsLineItem::Type:
+ itemPenWidth = static_cast<QGraphicsLineItem *>(item)->pen().widthF();
+ break;
+ default:
+ itemPenWidth = 1.0;
+ }
+ const qreal pad = itemPenWidth / 2;
+
+ const qreal penWidth = 0; // cosmetic pen
+
+ const QColor fgcolor = option->palette.windowText().color();
+ const QColor bgcolor( // ensure good contrast against fgcolor
+ fgcolor.red() > 127 ? 0 : 255,
+ fgcolor.green() > 127 ? 0 : 255,
+ fgcolor.blue() > 127 ? 0 : 255);
+
+ painter->setPen(QPen(bgcolor, penWidth, Qt::SolidLine));
+ painter->setBrush(Qt::NoBrush);
+ painter->drawRect(item->boundingRect().adjusted(pad, pad, -pad, -pad));
+
+ painter->setPen(QPen(option->palette.windowText(), 0, Qt::DashLine));
+ painter->setBrush(Qt::NoBrush);
+ painter->drawRect(item->boundingRect().adjusted(pad, pad, -pad, -pad));
+}
+
+/*!
+ \class QAbstractGraphicsShapeItem
+ \brief The QAbstractGraphicsShapeItem class provides a common base for
+ all path items.
+ \since 4.2
+ \ingroup multimedia
+
+ This class does not fully implement an item by itself; in particular, it
+ does not implement boundingRect() and paint(), which are inherited by
+ QGraphicsItem.
+
+ You can subclass this item to provide a simple base implementation of
+ accessors for the item's pen and brush.
+
+ \sa QGraphicsRectItem, QGraphicsEllipseItem, QGraphicsPathItem,
+ QGraphicsPolygonItem, QGraphicsTextItem, QGraphicsLineItem,
+ QGraphicsPixmapItem, {The Graphics View Framework}
+*/
+
+class QAbstractGraphicsShapeItemPrivate : public QGraphicsItemPrivate
+{
+ Q_DECLARE_PUBLIC(QAbstractGraphicsShapeItem)
+public:
+
+ QBrush brush;
+ QPen pen;
+
+ // Cached bounding rectangle
+ mutable QRectF boundingRect;
+};
+
+/*!
+ Constructs a QAbstractGraphicsShapeItem. \a parent is passed to
+ QGraphicsItem's constructor.
+*/
+QAbstractGraphicsShapeItem::QAbstractGraphicsShapeItem(QGraphicsItem *parent
+#ifndef Q_QDOC
+ // obsolete argument
+ , QGraphicsScene *scene
+#endif
+ )
+ : QGraphicsItem(*new QAbstractGraphicsShapeItemPrivate, parent, scene)
+{
+}
+
+/*!
+ \internal
+*/
+QAbstractGraphicsShapeItem::QAbstractGraphicsShapeItem(QAbstractGraphicsShapeItemPrivate &dd,
+ QGraphicsItem *parent,
+ QGraphicsScene *scene)
+ : QGraphicsItem(dd, parent, scene)
+{
+}
+
+/*!
+ Destroys a QAbstractGraphicsShapeItem.
+*/
+QAbstractGraphicsShapeItem::~QAbstractGraphicsShapeItem()
+{
+}
+
+/*!
+ Returns the item's pen. If no pen has been set, this function returns
+ QPen(), a default black solid line pen with 0 width.
+*/
+QPen QAbstractGraphicsShapeItem::pen() const
+{
+ Q_D(const QAbstractGraphicsShapeItem);
+ return d->pen;
+}
+
+/*!
+ Sets the pen for this item to \a pen.
+
+ The pen is used to draw the item's outline.
+
+ \sa pen()
+*/
+void QAbstractGraphicsShapeItem::setPen(const QPen &pen)
+{
+ Q_D(QAbstractGraphicsShapeItem);
+ prepareGeometryChange();
+ d->pen = pen;
+ d->boundingRect = QRectF();
+ update();
+}
+
+/*!
+ Returns the item's brush, or an empty brush if no brush has been set.
+
+ \sa setBrush()
+*/
+QBrush QAbstractGraphicsShapeItem::brush() const
+{
+ Q_D(const QAbstractGraphicsShapeItem);
+ return d->brush;
+}
+
+/*!
+ Sets the item's brush to \a brush.
+
+ The item's brush is used to fill the item.
+
+ If you use a brush with a QGradient, the gradient
+ is relative to the item's coordinate system.
+
+ \sa brush()
+*/
+void QAbstractGraphicsShapeItem::setBrush(const QBrush &brush)
+{
+ Q_D(QAbstractGraphicsShapeItem);
+ d->brush = brush;
+ update();
+}
+
+/*!
+ \reimp
+*/
+bool QAbstractGraphicsShapeItem::isObscuredBy(const QGraphicsItem *item) const
+{
+ return QGraphicsItem::isObscuredBy(item);
+}
+
+/*!
+ \reimp
+*/
+QPainterPath QAbstractGraphicsShapeItem::opaqueArea() const
+{
+ Q_D(const QAbstractGraphicsShapeItem);
+ if (d->brush.isOpaque())
+ return isClipped() ? clipPath() : shape();
+ return QGraphicsItem::opaqueArea();
+}
+
+/*!
+ \class QGraphicsPathItem
+ \brief The QGraphicsPathItem class provides a path item that you
+ can add to a QGraphicsScene.
+ \since 4.2
+ \ingroup multimedia
+ \ingroup graphicsview-api
+
+ To set the item's path, pass a QPainterPath to QGraphicsPathItem's
+ constructor, or call the setPath() function. The path() function
+ returns the current path.
+
+ \image graphicsview-pathitem.png
+
+ QGraphicsPathItem uses the path to provide a reasonable
+ implementation of boundingRect(), shape(), and contains(). The
+ paint() function draws the path using the item's associated pen
+ and brush, which you can set by calling the setPen() and
+ setBrush() functions.
+
+ \sa QGraphicsRectItem, QGraphicsEllipseItem, QGraphicsPolygonItem,
+ QGraphicsTextItem, QGraphicsLineItem, QGraphicsPixmapItem, {The Graphics
+ View Framework}
+*/
+
+class QGraphicsPathItemPrivate : public QAbstractGraphicsShapeItemPrivate
+{
+ Q_DECLARE_PUBLIC(QGraphicsPathItem)
+public:
+ QPainterPath path;
+};
+
+/*!
+ Constructs a QGraphicsPath item using \a path as the default path. \a
+ parent is passed to QAbstractGraphicsShapeItem's constructor.
+
+ \sa QGraphicsScene::addItem()
+*/
+QGraphicsPathItem::QGraphicsPathItem(const QPainterPath &path,
+ QGraphicsItem *parent
+#ifndef Q_QDOC
+ // obsolete argument
+ , QGraphicsScene *scene
+#endif
+ )
+ : QAbstractGraphicsShapeItem(*new QGraphicsPathItemPrivate, parent, scene)
+{
+ if (!path.isEmpty())
+ setPath(path);
+}
+
+/*!
+ Constructs a QGraphicsPath. \a parent is passed to
+ QAbstractGraphicsShapeItem's constructor.
+
+ \sa QGraphicsScene::addItem()
+*/
+QGraphicsPathItem::QGraphicsPathItem(QGraphicsItem *parent
+#ifndef Q_QDOC
+ // obsolete argument
+ , QGraphicsScene *scene
+#endif
+ )
+ : QAbstractGraphicsShapeItem(*new QGraphicsPathItemPrivate, parent, scene)
+{
+}
+
+/*!
+ Destroys the QGraphicsPathItem.
+*/
+QGraphicsPathItem::~QGraphicsPathItem()
+{
+}
+
+/*!
+ Returns the item's path as a QPainterPath. If no item has been set, an
+ empty QPainterPath is returned.
+
+ \sa setPath()
+*/
+QPainterPath QGraphicsPathItem::path() const
+{
+ Q_D(const QGraphicsPathItem);
+ return d->path;
+}
+
+/*!
+ Sets the item's path to be the given \a path.
+
+ \sa path()
+*/
+void QGraphicsPathItem::setPath(const QPainterPath &path)
+{
+ Q_D(QGraphicsPathItem);
+ if (d->path == path)
+ return;
+ prepareGeometryChange();
+ d->path = path;
+ d->boundingRect = QRectF();
+ update();
+}
+
+/*!
+ \reimp
+*/
+QRectF QGraphicsPathItem::boundingRect() const
+{
+ Q_D(const QGraphicsPathItem);
+ if (d->boundingRect.isNull()) {
+ qreal pw = pen().widthF();
+ if (pw == 0.0)
+ d->boundingRect = d->path.controlPointRect();
+ else {
+ d->boundingRect = shape().controlPointRect();
+ }
+ }
+ return d->boundingRect;
+}
+
+/*!
+ \reimp
+*/
+QPainterPath QGraphicsPathItem::shape() const
+{
+ Q_D(const QGraphicsPathItem);
+ return qt_graphicsItem_shapeFromPath(d->path, d->pen);
+}
+
+/*!
+ \reimp
+*/
+bool QGraphicsPathItem::contains(const QPointF &point) const
+{
+ return QAbstractGraphicsShapeItem::contains(point);
+}
+
+/*!
+ \reimp
+*/
+void QGraphicsPathItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option,
+ QWidget *widget)
+{
+ Q_D(QGraphicsPathItem);
+ Q_UNUSED(widget);
+ painter->setPen(d->pen);
+ painter->setBrush(d->brush);
+ painter->drawPath(d->path);
+
+ if (option->state & QStyle::State_Selected)
+ qt_graphicsItem_highlightSelected(this, painter, option);
+}
+
+/*!
+ \reimp
+*/
+bool QGraphicsPathItem::isObscuredBy(const QGraphicsItem *item) const
+{
+ return QAbstractGraphicsShapeItem::isObscuredBy(item);
+}
+
+/*!
+ \reimp
+*/
+QPainterPath QGraphicsPathItem::opaqueArea() const
+{
+ return QAbstractGraphicsShapeItem::opaqueArea();
+}
+
+/*!
+ \reimp
+*/
+int QGraphicsPathItem::type() const
+{
+ return Type;
+}
+
+/*!
+ \internal
+*/
+bool QGraphicsPathItem::supportsExtension(Extension extension) const
+{
+ Q_UNUSED(extension);
+ return false;
+}
+
+/*!
+ \internal
+*/
+void QGraphicsPathItem::setExtension(Extension extension, const QVariant &variant)
+{
+ Q_UNUSED(extension);
+ Q_UNUSED(variant);
+}
+
+/*!
+ \internal
+*/
+QVariant QGraphicsPathItem::extension(const QVariant &variant) const
+{
+ Q_UNUSED(variant);
+ return QVariant();
+}
+
+/*!
+ \class QGraphicsRectItem
+ \brief The QGraphicsRectItem class provides a rectangle item that you
+ can add to a QGraphicsScene.
+ \since 4.2
+ \ingroup multimedia
+ \ingroup graphicsview-api
+
+ To set the item's rectangle, pass a QRectF to QGraphicsRectItem's
+ constructor, or call the setRect() function. The rect() function
+ returns the current rectangle.
+
+ \image graphicsview-rectitem.png
+
+ QGraphicsRectItem uses the rectangle and the pen width to provide
+ a reasonable implementation of boundingRect(), shape(), and
+ contains(). The paint() function draws the rectangle using the
+ item's associated pen and brush, which you can set by calling the
+ setPen() and setBrush() functions.
+
+ \note The rendering of invalid rectangles, such as those with negative
+ widths or heights, is undefined. If you cannot be sure that you are
+ using valid rectangles (for example, if you are creating
+ rectangles using data from an unreliable source) then you should
+ use QRectF::normalized() to create normalized rectangles, and use
+ those instead.
+
+ \sa QGraphicsPathItem, QGraphicsEllipseItem, QGraphicsPolygonItem,
+ QGraphicsTextItem, QGraphicsLineItem, QGraphicsPixmapItem, {The Graphics
+ View Framework}
+*/
+
+class QGraphicsRectItemPrivate : public QAbstractGraphicsShapeItemPrivate
+{
+ Q_DECLARE_PUBLIC(QGraphicsRectItem)
+public:
+ QRectF rect;
+};
+
+/*!
+ Constructs a QGraphicsRectItem, using \a rect as the default rectangle.
+ \a parent is passed to QAbstractGraphicsShapeItem's constructor.
+
+ \sa QGraphicsScene::addItem()
+*/
+QGraphicsRectItem::QGraphicsRectItem(const QRectF &rect, QGraphicsItem *parent
+#ifndef Q_QDOC
+ // obsolete argument
+ , QGraphicsScene *scene
+#endif
+ )
+ : QAbstractGraphicsShapeItem(*new QGraphicsRectItemPrivate, parent, scene)
+{
+ setRect(rect);
+}
+
+/*!
+ \fn QGraphicsRectItem::QGraphicsRectItem(qreal x, qreal y, qreal width, qreal height,
+ QGraphicsItem *parent)
+
+ Constructs a QGraphicsRectItem with a default rectangle defined
+ by (\a x, \a y) and the given \a width and \a height.
+
+ \a parent is passed to QAbstractGraphicsShapeItem's constructor.
+
+ \sa QGraphicsScene::addItem()
+*/
+QGraphicsRectItem::QGraphicsRectItem(qreal x, qreal y, qreal w, qreal h,
+ QGraphicsItem *parent
+#ifndef Q_QDOC
+ // obsolete argument
+ , QGraphicsScene *scene
+#endif
+ )
+ : QAbstractGraphicsShapeItem(*new QGraphicsRectItemPrivate, parent, scene)
+{
+ setRect(QRectF(x, y, w, h));
+}
+
+/*!
+ Constructs a QGraphicsRectItem. \a parent is passed to
+ QAbstractGraphicsShapeItem's constructor.
+
+ \sa QGraphicsScene::addItem()
+*/
+QGraphicsRectItem::QGraphicsRectItem(QGraphicsItem *parent
+#ifndef Q_QDOC
+ // obsolete argument
+ , QGraphicsScene *scene
+#endif
+ )
+ : QAbstractGraphicsShapeItem(*new QGraphicsRectItemPrivate, parent, scene)
+{
+}
+
+/*!
+ Destroys the QGraphicsRectItem.
+*/
+QGraphicsRectItem::~QGraphicsRectItem()
+{
+}
+
+/*!
+ Returns the item's rectangle.
+
+ \sa setRect()
+*/
+QRectF QGraphicsRectItem::rect() const
+{
+ Q_D(const QGraphicsRectItem);
+ return d->rect;
+}
+
+/*!
+ \fn void QGraphicsRectItem::setRect(const QRectF &rectangle)
+
+ Sets the item's rectangle to be the given \a rectangle.
+
+ \sa rect()
+*/
+void QGraphicsRectItem::setRect(const QRectF &rect)
+{
+ Q_D(QGraphicsRectItem);
+ if (d->rect == rect)
+ return;
+ prepareGeometryChange();
+ d->rect = rect;
+ d->boundingRect = QRectF();
+ update();
+}
+
+/*!
+ \fn void QGraphicsRectItem::setRect(qreal x, qreal y, qreal width, qreal height)
+ \fn void QGraphicsEllipseItem::setRect(qreal x, qreal y, qreal width, qreal height)
+
+ Sets the item's rectangle to the rectangle defined by (\a x, \a y)
+ and the given \a width and \a height.
+
+ This convenience function is equivalent to calling \c
+ {setRect(QRectF(x, y, width, height))}
+
+ \sa rect()
+*/
+
+/*!
+ \reimp
+*/
+QRectF QGraphicsRectItem::boundingRect() const
+{
+ Q_D(const QGraphicsRectItem);
+ if (d->boundingRect.isNull()) {
+ qreal halfpw = pen().widthF() / 2;
+ d->boundingRect = d->rect;
+ if (halfpw > 0.0)
+ d->boundingRect.adjust(-halfpw, -halfpw, halfpw, halfpw);
+ }
+ return d->boundingRect;
+}
+
+/*!
+ \reimp
+*/
+QPainterPath QGraphicsRectItem::shape() const
+{
+ Q_D(const QGraphicsRectItem);
+ QPainterPath path;
+ path.addRect(d->rect);
+ return qt_graphicsItem_shapeFromPath(path, d->pen);
+}
+
+/*!
+ \reimp
+*/
+bool QGraphicsRectItem::contains(const QPointF &point) const
+{
+ return QAbstractGraphicsShapeItem::contains(point);
+}
+
+/*!
+ \reimp
+*/
+void QGraphicsRectItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option,
+ QWidget *widget)
+{
+ Q_D(QGraphicsRectItem);
+ Q_UNUSED(widget);
+ painter->setPen(d->pen);
+ painter->setBrush(d->brush);
+ painter->drawRect(d->rect);
+
+ if (option->state & QStyle::State_Selected)
+ qt_graphicsItem_highlightSelected(this, painter, option);
+}
+
+/*!
+ \reimp
+*/
+bool QGraphicsRectItem::isObscuredBy(const QGraphicsItem *item) const
+{
+ return QAbstractGraphicsShapeItem::isObscuredBy(item);
+}
+
+/*!
+ \reimp
+*/
+QPainterPath QGraphicsRectItem::opaqueArea() const
+{
+ return QAbstractGraphicsShapeItem::opaqueArea();
+}
+
+/*!
+ \reimp
+*/
+int QGraphicsRectItem::type() const
+{
+ return Type;
+}
+
+/*!
+ \internal
+*/
+bool QGraphicsRectItem::supportsExtension(Extension extension) const
+{
+ Q_UNUSED(extension);
+ return false;
+}
+
+/*!
+ \internal
+*/
+void QGraphicsRectItem::setExtension(Extension extension, const QVariant &variant)
+{
+ Q_UNUSED(extension);
+ Q_UNUSED(variant);
+}
+
+/*!
+ \internal
+*/
+QVariant QGraphicsRectItem::extension(const QVariant &variant) const
+{
+ Q_UNUSED(variant);
+ return QVariant();
+}
+
+/*!
+ \class QGraphicsEllipseItem
+ \brief The QGraphicsEllipseItem class provides an ellipse item that you
+ can add to a QGraphicsScene.
+ \since 4.2
+ \ingroup multimedia
+ \ingroup graphicsview-api
+
+ QGraphicsEllipseItem respresents an ellipse with a fill and an outline,
+ and you can also use it for ellipse segments (see startAngle(),
+ spanAngle()).
+
+ \table
+ \row
+ \o \inlineimage graphicsview-ellipseitem.png
+ \o \inlineimage graphicsview-ellipseitem-pie.png
+ \endtable
+
+ To set the item's ellipse, pass a QRectF to QGraphicsEllipseItem's
+ constructor, or call setRect(). The rect() function returns the
+ current ellipse geometry.
+
+ QGraphicsEllipseItem uses the rect and the pen width to provide a
+ reasonable implementation of boundingRect(), shape(), and contains(). The
+ paint() function draws the ellipse using the item's associated pen and
+ brush, which you can set by calling setPen() and setBrush().
+
+ \sa QGraphicsPathItem, QGraphicsRectItem, QGraphicsPolygonItem,
+ QGraphicsTextItem, QGraphicsLineItem, QGraphicsPixmapItem, {The Graphics
+ View Framework}
+*/
+
+class QGraphicsEllipseItemPrivate : public QAbstractGraphicsShapeItemPrivate
+{
+ Q_DECLARE_PUBLIC(QGraphicsEllipseItem)
+public:
+ inline QGraphicsEllipseItemPrivate()
+ : startAngle(0), spanAngle(360 * 16)
+ { }
+
+ QRectF rect;
+ int startAngle;
+ int spanAngle;
+};
+
+/*!
+ Constructs a QGraphicsEllipseItem using \a rect as the default rectangle.
+ \a parent is passed to QAbstractGraphicsShapeItem's constructor.
+
+ \sa QGraphicsScene::addItem()
+*/
+QGraphicsEllipseItem::QGraphicsEllipseItem(const QRectF &rect, QGraphicsItem *parent
+#ifndef Q_QDOC
+ // obsolete argument
+ , QGraphicsScene *scene
+#endif
+ )
+ : QAbstractGraphicsShapeItem(*new QGraphicsEllipseItemPrivate, parent, scene)
+{
+ setRect(rect);
+}
+
+/*!
+ \fn QGraphicsEllipseItem::QGraphicsEllipseItem(qreal x, qreal y, qreal width, qreal height, QGraphicsItem *parent)
+ \since 4.3
+
+ Constructs a QGraphicsEllipseItem using the rectangle defined by (\a x, \a
+ y) and the given \a width and \a height, as the default rectangle. \a
+ parent is passed to QAbstractGraphicsShapeItem's constructor.
+
+ \sa QGraphicsScene::addItem()
+*/
+QGraphicsEllipseItem::QGraphicsEllipseItem(qreal x, qreal y, qreal w, qreal h,
+ QGraphicsItem *parent
+#ifndef Q_QDOC
+ // obsolete argument
+ , QGraphicsScene *scene
+#endif
+ )
+ : QAbstractGraphicsShapeItem(*new QGraphicsEllipseItemPrivate, parent, scene)
+{
+ setRect(x,y,w,h);
+}
+
+
+
+/*!
+ Constructs a QGraphicsEllipseItem. \a parent is passed to
+ QAbstractGraphicsShapeItem's constructor.
+
+ \sa QGraphicsScene::addItem()
+*/
+QGraphicsEllipseItem::QGraphicsEllipseItem(QGraphicsItem *parent
+#ifndef Q_QDOC
+ // obsolete argument
+ , QGraphicsScene *scene
+#endif
+ )
+ : QAbstractGraphicsShapeItem(*new QGraphicsEllipseItemPrivate, parent, scene)
+{
+}
+
+/*!
+ Destroys the QGraphicsEllipseItem.
+*/
+QGraphicsEllipseItem::~QGraphicsEllipseItem()
+{
+}
+
+/*!
+ Returns the item's ellipse geometry as a QRectF.
+
+ \sa setRect(), QPainter::drawEllipse()
+*/
+QRectF QGraphicsEllipseItem::rect() const
+{
+ Q_D(const QGraphicsEllipseItem);
+ return d->rect;
+}
+
+/*!
+ Sets the item's ellipse geometry to \a rect. The rectangle's left edge
+ defines the left edge of the ellipse, and the rectangle's top edge
+ describes the top of the ellipse. The height and width of the rectangle
+ describe the height and width of the ellipse.
+
+ \sa rect(), QPainter::drawEllipse()
+*/
+void QGraphicsEllipseItem::setRect(const QRectF &rect)
+{
+ Q_D(QGraphicsEllipseItem);
+ if (d->rect == rect)
+ return;
+ prepareGeometryChange();
+ d->rect = rect;
+ d->boundingRect = QRectF();
+ update();
+}
+
+/*!
+ Returns the start angle for an ellipse segment in 16ths of a degree. This
+ angle is used together with spanAngle() for representing an ellipse
+ segment (a pie). By default, the start angle is 0.
+
+ \sa setStartAngle(), spanAngle()
+*/
+int QGraphicsEllipseItem::startAngle() const
+{
+ Q_D(const QGraphicsEllipseItem);
+ return d->startAngle;
+}
+
+/*!
+ Sets the start angle for an ellipse segment to \a angle, which is in 16ths
+ of a degree. This angle is used together with spanAngle() for representing
+ an ellipse segment (a pie). By default, the start angle is 0.
+
+ \sa startAngle(), setSpanAngle(), QPainter::drawPie()
+*/
+void QGraphicsEllipseItem::setStartAngle(int angle)
+{
+ Q_D(QGraphicsEllipseItem);
+ if (angle != d->startAngle) {
+ prepareGeometryChange();
+ d->boundingRect = QRectF();
+ d->startAngle = angle;
+ update();
+ }
+}
+
+/*!
+ Returns the span angle of an ellipse segment in 16ths of a degree. This
+ angle is used together with startAngle() for representing an ellipse
+ segment (a pie). By default, this function returns 5760 (360 * 16, a full
+ ellipse).
+
+ \sa setSpanAngle(), startAngle()
+*/
+int QGraphicsEllipseItem::spanAngle() const
+{
+ Q_D(const QGraphicsEllipseItem);
+ return d->spanAngle;
+}
+
+/*!
+ Sets the span angle for an ellipse segment to \a angle, which is in 16ths
+ of a degree. This angle is used together with startAngle() to represent an
+ ellipse segment (a pie). By default, the span angle is 5760 (360 * 16, a
+ full ellipse).
+
+ \sa spanAngle(), setStartAngle(), QPainter::drawPie()
+*/
+void QGraphicsEllipseItem::setSpanAngle(int angle)
+{
+ Q_D(QGraphicsEllipseItem);
+ if (angle != d->spanAngle) {
+ prepareGeometryChange();
+ d->boundingRect = QRectF();
+ d->spanAngle = angle;
+ update();
+ }
+}
+
+/*!
+ \reimp
+*/
+QRectF QGraphicsEllipseItem::boundingRect() const
+{
+ Q_D(const QGraphicsEllipseItem);
+ if (d->boundingRect.isNull()) {
+ qreal pw = pen().widthF();
+ if (pw == 0.0 && d->spanAngle == 360 * 16)
+ d->boundingRect = d->rect;
+ else
+ d->boundingRect = shape().controlPointRect();
+ }
+ return d->boundingRect;
+}
+
+/*!
+ \reimp
+*/
+QPainterPath QGraphicsEllipseItem::shape() const
+{
+ Q_D(const QGraphicsEllipseItem);
+ QPainterPath path;
+ if (d->rect.isNull())
+ return path;
+ if (d->spanAngle != 360 * 16) {
+ path.moveTo(d->rect.center());
+ path.arcTo(d->rect, d->startAngle / 16.0, d->spanAngle / 16.0);
+ } else {
+ path.addEllipse(d->rect);
+ }
+
+ return qt_graphicsItem_shapeFromPath(path, d->pen);
+}
+
+/*!
+ \reimp
+*/
+bool QGraphicsEllipseItem::contains(const QPointF &point) const
+{
+ return QAbstractGraphicsShapeItem::contains(point);
+}
+
+/*!
+ \reimp
+*/
+void QGraphicsEllipseItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option,
+ QWidget *widget)
+{
+ Q_D(QGraphicsEllipseItem);
+ Q_UNUSED(widget);
+ painter->setPen(d->pen);
+ painter->setBrush(d->brush);
+ if ((d->spanAngle != 0) && (qAbs(d->spanAngle) % (360 * 16) == 0))
+ painter->drawEllipse(d->rect);
+ else
+ painter->drawPie(d->rect, d->startAngle, d->spanAngle);
+
+ if (option->state & QStyle::State_Selected)
+ qt_graphicsItem_highlightSelected(this, painter, option);
+}
+
+/*!
+ \reimp
+*/
+bool QGraphicsEllipseItem::isObscuredBy(const QGraphicsItem *item) const
+{
+ return QAbstractGraphicsShapeItem::isObscuredBy(item);
+}
+
+/*!
+ \reimp
+*/
+QPainterPath QGraphicsEllipseItem::opaqueArea() const
+{
+ return QAbstractGraphicsShapeItem::opaqueArea();
+}
+
+/*!
+ \reimp
+*/
+int QGraphicsEllipseItem::type() const
+{
+ return Type;
+}
+
+
+/*!
+ \internal
+*/
+bool QGraphicsEllipseItem::supportsExtension(Extension extension) const
+{
+ Q_UNUSED(extension);
+ return false;
+}
+
+/*!
+ \internal
+*/
+void QGraphicsEllipseItem::setExtension(Extension extension, const QVariant &variant)
+{
+ Q_UNUSED(extension);
+ Q_UNUSED(variant);
+}
+
+/*!
+ \internal
+*/
+QVariant QGraphicsEllipseItem::extension(const QVariant &variant) const
+{
+ Q_UNUSED(variant);
+ return QVariant();
+}
+
+/*!
+ \class QGraphicsPolygonItem
+ \brief The QGraphicsPolygonItem class provides a polygon item that you
+ can add to a QGraphicsScene.
+ \since 4.2
+ \ingroup multimedia
+ \ingroup graphicsview-api
+
+ To set the item's polygon, pass a QPolygonF to
+ QGraphicsPolygonItem's constructor, or call the setPolygon()
+ function. The polygon() function returns the current polygon.
+
+ \image graphicsview-polygonitem.png
+
+ QGraphicsPolygonItem uses the polygon and the pen width to provide
+ a reasonable implementation of boundingRect(), shape(), and
+ contains(). The paint() function draws the polygon using the
+ item's associated pen and brush, which you can set by calling the
+ setPen() and setBrush() functions.
+
+ \sa QGraphicsPathItem, QGraphicsRectItem, QGraphicsEllipseItem,
+ QGraphicsTextItem, QGraphicsLineItem, QGraphicsPixmapItem, {The Graphics
+ View Framework}
+*/
+
+class QGraphicsPolygonItemPrivate : public QAbstractGraphicsShapeItemPrivate
+{
+ Q_DECLARE_PUBLIC(QGraphicsPolygonItem)
+public:
+ inline QGraphicsPolygonItemPrivate()
+ : fillRule(Qt::OddEvenFill)
+ { }
+
+ QPolygonF polygon;
+ Qt::FillRule fillRule;
+};
+
+/*!
+ Constructs a QGraphicsPolygonItem with \a polygon as the default
+ polygon. \a parent is passed to QAbstractGraphicsShapeItem's constructor.
+
+ \sa QGraphicsScene::addItem()
+*/
+QGraphicsPolygonItem::QGraphicsPolygonItem(const QPolygonF &polygon,
+ QGraphicsItem *parent
+#ifndef Q_QDOC
+ // obsolete argument
+ , QGraphicsScene *scene
+#endif
+ )
+ : QAbstractGraphicsShapeItem(*new QGraphicsPolygonItemPrivate, parent, scene)
+{
+ setPolygon(polygon);
+}
+
+/*!
+ Constructs a QGraphicsPolygonItem. \a parent is passed to
+ QAbstractGraphicsShapeItem's constructor.
+
+ \sa QGraphicsScene::addItem()
+*/
+QGraphicsPolygonItem::QGraphicsPolygonItem(QGraphicsItem *parent
+#ifndef Q_QDOC
+ // obsolete argument
+ , QGraphicsScene *scene
+#endif
+ )
+ : QAbstractGraphicsShapeItem(*new QGraphicsPolygonItemPrivate, parent, scene)
+{
+}
+
+/*!
+ Destroys the QGraphicsPolygonItem.
+*/
+QGraphicsPolygonItem::~QGraphicsPolygonItem()
+{
+}
+
+/*!
+ Returns the item's polygon, or an empty polygon if no polygon
+ has been set.
+
+ \sa setPolygon()
+*/
+QPolygonF QGraphicsPolygonItem::polygon() const
+{
+ Q_D(const QGraphicsPolygonItem);
+ return d->polygon;
+}
+
+/*!
+ Sets the item's polygon to be the given \a polygon.
+
+ \sa polygon()
+*/
+void QGraphicsPolygonItem::setPolygon(const QPolygonF &polygon)
+{
+ Q_D(QGraphicsPolygonItem);
+ if (d->polygon == polygon)
+ return;
+ prepareGeometryChange();
+ d->polygon = polygon;
+ d->boundingRect = QRectF();
+ update();
+}
+
+/*!
+ Returns the fill rule of the polygon. The default fill rule is
+ Qt::OddEvenFill.
+
+ \sa setFillRule(), QPainterPath::fillRule(), QPainter::drawPolygon()
+*/
+Qt::FillRule QGraphicsPolygonItem::fillRule() const
+{
+ Q_D(const QGraphicsPolygonItem);
+ return d->fillRule;
+}
+
+/*!
+ Sets the fill rule of the polygon to \a rule. The default fill rule is
+ Qt::OddEvenFill.
+
+ \sa fillRule(), QPainterPath::fillRule(), QPainter::drawPolygon()
+*/
+void QGraphicsPolygonItem::setFillRule(Qt::FillRule rule)
+{
+ Q_D(QGraphicsPolygonItem);
+ if (rule != d->fillRule) {
+ d->fillRule = rule;
+ update();
+ }
+}
+
+/*!
+ \reimp
+*/
+QRectF QGraphicsPolygonItem::boundingRect() const
+{
+ Q_D(const QGraphicsPolygonItem);
+ if (d->boundingRect.isNull()) {
+ qreal pw = pen().widthF();
+ if (pw == 0.0)
+ d->boundingRect = d->polygon.boundingRect();
+ else
+ d->boundingRect = shape().controlPointRect();
+ }
+ return d->boundingRect;
+}
+
+/*!
+ \reimp
+*/
+QPainterPath QGraphicsPolygonItem::shape() const
+{
+ Q_D(const QGraphicsPolygonItem);
+ QPainterPath path;
+ path.addPolygon(d->polygon);
+ return qt_graphicsItem_shapeFromPath(path, d->pen);
+}
+
+/*!
+ \reimp
+*/
+bool QGraphicsPolygonItem::contains(const QPointF &point) const
+{
+ return QAbstractGraphicsShapeItem::contains(point);
+}
+
+/*!
+ \reimp
+*/
+void QGraphicsPolygonItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
+{
+ Q_D(QGraphicsPolygonItem);
+ Q_UNUSED(widget);
+ painter->setPen(d->pen);
+ painter->setBrush(d->brush);
+ painter->drawPolygon(d->polygon, d->fillRule);
+
+ if (option->state & QStyle::State_Selected)
+ qt_graphicsItem_highlightSelected(this, painter, option);
+}
+
+/*!
+ \reimp
+*/
+bool QGraphicsPolygonItem::isObscuredBy(const QGraphicsItem *item) const
+{
+ return QAbstractGraphicsShapeItem::isObscuredBy(item);
+}
+
+/*!
+ \reimp
+*/
+QPainterPath QGraphicsPolygonItem::opaqueArea() const
+{
+ return QAbstractGraphicsShapeItem::opaqueArea();
+}
+
+/*!
+ \reimp
+*/
+int QGraphicsPolygonItem::type() const
+{
+ return Type;
+}
+
+/*!
+ \internal
+*/
+bool QGraphicsPolygonItem::supportsExtension(Extension extension) const
+{
+ Q_UNUSED(extension);
+ return false;
+}
+
+/*!
+ \internal
+*/
+void QGraphicsPolygonItem::setExtension(Extension extension, const QVariant &variant)
+{
+ Q_UNUSED(extension);
+ Q_UNUSED(variant);
+}
+
+/*!
+ \internal
+*/
+QVariant QGraphicsPolygonItem::extension(const QVariant &variant) const
+{
+ Q_UNUSED(variant);
+ return QVariant();
+}
+
+/*!
+ \class QGraphicsLineItem
+ \brief The QGraphicsLineItem class provides a line item that you can add to a
+ QGraphicsScene.
+ \since 4.2
+ \ingroup multimedia
+ \ingroup graphicsview-api
+
+ To set the item's line, pass a QLineF to QGraphicsLineItem's
+ constructor, or call the setLine() function. The line() function
+ returns the current line. By default the line is black with a
+ width of 0, but you can change this by calling setPen().
+
+ \img graphicsview-lineitem.png
+
+ QGraphicsLineItem uses the line and the pen width to provide a reasonable
+ implementation of boundingRect(), shape(), and contains(). The paint()
+ function draws the line using the item's associated pen.
+
+ \sa QGraphicsPathItem, QGraphicsRectItem, QGraphicsEllipseItem,
+ QGraphicsTextItem, QGraphicsPolygonItem, QGraphicsPixmapItem, {The
+ Graphics View Framework}
+*/
+
+class QGraphicsLineItemPrivate : public QGraphicsItemPrivate
+{
+ Q_DECLARE_PUBLIC(QGraphicsLineItem)
+public:
+ QLineF line;
+ QPen pen;
+};
+
+/*!
+ Constructs a QGraphicsLineItem, using \a line as the default line. \a
+ parent is passed to QGraphicsItem's constructor.
+
+ \sa QGraphicsScene::addItem()
+*/
+QGraphicsLineItem::QGraphicsLineItem(const QLineF &line, QGraphicsItem *parent
+#ifndef Q_QDOC
+ // obsolete argument
+ , QGraphicsScene *scene
+#endif
+ )
+ : QGraphicsItem(*new QGraphicsLineItemPrivate, parent, scene)
+{
+ setLine(line);
+}
+
+/*!
+ Constructs a QGraphicsLineItem, using the line between (\a x1, \a y1) and
+ (\a x2, \a y2) as the default line. \a parent is passed to
+ QGraphicsItem's constructor.
+
+ \sa QGraphicsScene::addItem()
+*/
+QGraphicsLineItem::QGraphicsLineItem(qreal x1, qreal y1, qreal x2, qreal y2, QGraphicsItem *parent
+#ifndef Q_QDOC
+ // obsolete argument
+ , QGraphicsScene *scene
+#endif
+ )
+ : QGraphicsItem(*new QGraphicsLineItemPrivate, parent, scene)
+{
+ setLine(x1, y1, x2, y2);
+}
+
+
+
+/*!
+ Constructs a QGraphicsLineItem. \a parent is passed to QGraphicsItem's
+ constructor.
+
+ \sa QGraphicsScene::addItem()
+*/
+QGraphicsLineItem::QGraphicsLineItem(QGraphicsItem *parent
+#ifndef Q_QDOC
+ // obsolete argument
+ , QGraphicsScene *scene
+#endif
+ )
+ : QGraphicsItem(*new QGraphicsLineItemPrivate, parent, scene)
+{
+}
+
+/*!
+ Destroys the QGraphicsLineItem.
+*/
+QGraphicsLineItem::~QGraphicsLineItem()
+{
+}
+
+/*!
+ Returns the item's pen, or a black solid 0-width pen if no pen has
+ been set.
+
+ \sa setPen()
+*/
+QPen QGraphicsLineItem::pen() const
+{
+ Q_D(const QGraphicsLineItem);
+ return d->pen;
+}
+
+/*!
+ Sets the item's pen to \a pen. If no pen is set, the line will be painted
+ using a black solid 0-width pen.
+
+ \sa pen()
+*/
+void QGraphicsLineItem::setPen(const QPen &pen)
+{
+ Q_D(QGraphicsLineItem);
+ prepareGeometryChange();
+ d->pen = pen;
+ update();
+}
+
+/*!
+ Returns the item's line, or a null line if no line has been set.
+
+ \sa setLine()
+*/
+QLineF QGraphicsLineItem::line() const
+{
+ Q_D(const QGraphicsLineItem);
+ return d->line;
+}
+
+/*!
+ Sets the item's line to be the given \a line.
+
+ \sa line()
+*/
+void QGraphicsLineItem::setLine(const QLineF &line)
+{
+ Q_D(QGraphicsLineItem);
+ if (d->line == line)
+ return;
+ prepareGeometryChange();
+ d->line = line;
+ update();
+}
+
+/*!
+ \fn void QGraphicsLineItem::setLine(qreal x1, qreal y1, qreal x2, qreal y2)
+ \overload
+
+ Sets the item's line to be the line between (\a x1, \a y1) and (\a
+ x2, \a y2).
+
+ This is the same as calling \c {setLine(QLineF(x1, y1, x2, y2))}.
+*/
+
+/*!
+ \reimp
+*/
+QRectF QGraphicsLineItem::boundingRect() const
+{
+ Q_D(const QGraphicsLineItem);
+ if (d->pen.widthF() == 0.0) {
+ const qreal x1 = d->line.p1().x();
+ const qreal x2 = d->line.p2().x();
+ const qreal y1 = d->line.p1().y();
+ const qreal y2 = d->line.p2().y();
+ qreal lx = qMin(x1, x2);
+ qreal rx = qMax(x1, x2);
+ qreal ty = qMin(y1, y2);
+ qreal by = qMax(y1, y2);
+ return QRectF(lx, ty, rx - lx, by - ty);
+ }
+ return shape().controlPointRect();
+}
+
+/*!
+ \reimp
+*/
+QPainterPath QGraphicsLineItem::shape() const
+{
+ Q_D(const QGraphicsLineItem);
+ QPainterPath path;
+ if (d->line == QLineF())
+ return path;
+
+ path.moveTo(d->line.p1());
+ path.lineTo(d->line.p2());
+ return qt_graphicsItem_shapeFromPath(path, d->pen);
+}
+
+/*!
+ \reimp
+*/
+bool QGraphicsLineItem::contains(const QPointF &point) const
+{
+ return QGraphicsItem::contains(point);
+}
+
+/*!
+ \reimp
+*/
+void QGraphicsLineItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
+{
+ Q_D(QGraphicsLineItem);
+ Q_UNUSED(widget);
+ painter->setPen(d->pen);
+ painter->drawLine(d->line);
+
+ if (option->state & QStyle::State_Selected)
+ qt_graphicsItem_highlightSelected(this, painter, option);
+}
+
+/*!
+ \reimp
+*/
+bool QGraphicsLineItem::isObscuredBy(const QGraphicsItem *item) const
+{
+ return QGraphicsItem::isObscuredBy(item);
+}
+
+/*!
+ \reimp
+*/
+QPainterPath QGraphicsLineItem::opaqueArea() const
+{
+ return QGraphicsItem::opaqueArea();
+}
+
+/*!
+ \reimp
+*/
+int QGraphicsLineItem::type() const
+{
+ return Type;
+}
+
+/*!
+ \internal
+*/
+bool QGraphicsLineItem::supportsExtension(Extension extension) const
+{
+ Q_UNUSED(extension);
+ return false;
+}
+
+/*!
+ \internal
+*/
+void QGraphicsLineItem::setExtension(Extension extension, const QVariant &variant)
+{
+ Q_UNUSED(extension);
+ Q_UNUSED(variant);
+}
+
+/*!
+ \internal
+*/
+QVariant QGraphicsLineItem::extension(const QVariant &variant) const
+{
+ Q_UNUSED(variant);
+ return QVariant();
+}
+
+/*!
+ \class QGraphicsPixmapItem
+ \brief The QGraphicsPixmapItem class provides a pixmap item that you can add to
+ a QGraphicsScene.
+ \since 4.2
+ \ingroup multimedia
+ \ingroup graphicsview-api
+
+ To set the item's pixmap, pass a QPixmap to QGraphicsPixmapItem's
+ constructor, or call the setPixmap() function. The pixmap()
+ function returns the current pixmap.
+
+ QGraphicsPixmapItem uses pixmap's optional alpha mask to provide a
+ reasonable implementation of boundingRect(), shape(), and contains().
+
+ \image graphicsview-pixmapitem.png
+
+ The pixmap is drawn at the item's (0, 0) coordinate, as returned by
+ offset(). You can change the drawing offset by calling setOffset().
+
+ You can set the pixmap's transformation mode by calling
+ setTransformationMode(). By default, Qt::FastTransformation is used, which
+ provides fast, non-smooth scaling. Qt::SmoothTransformation enables
+ QPainter::SmoothPixmapTransform on the painter, and the quality depends on
+ the platform and viewport. The result is usually not as good as calling
+ QPixmap::scale() directly. Call transformationMode() to get the current
+ transformation mode for the item.
+
+ \sa QGraphicsPathItem, QGraphicsRectItem, QGraphicsEllipseItem,
+ QGraphicsTextItem, QGraphicsPolygonItem, QGraphicsLineItem, {The
+ Graphics View Framework}
+*/
+
+/*!
+ \enum QGraphicsPixmapItem::ShapeMode
+
+ This enum describes how QGraphicsPixmapItem calculates its shape and
+ opaque area.
+
+ The default value is MaskShape.
+
+ \value MaskShape The shape is determined by calling QPixmap::mask().
+ This shape includes only the opaque pixels of the pixmap.
+ Because the shape is more complex, however, it can be slower than the other modes,
+ and uses more memory.
+
+ \value BoundingRectShape The shape is determined by tracing the outline of
+ the pixmap. This is the fastest shape mode, but it does not take into account
+ any transparent areas on the pixmap.
+
+ \value HeuristicMaskShape The shape is determine by calling
+ QPixmap::createHeuristicMask(). The performance and memory consumption
+ is similar to MaskShape.
+*/
+extern QPainterPath qt_regionToPath(const QRegion &region);
+
+class QGraphicsPixmapItemPrivate : public QGraphicsItemPrivate
+{
+ Q_DECLARE_PUBLIC(QGraphicsPixmapItem)
+public:
+ QGraphicsPixmapItemPrivate()
+ : transformationMode(Qt::FastTransformation),
+ shapeMode(QGraphicsPixmapItem::MaskShape),
+ hasShape(false)
+ {}
+
+ QPixmap pixmap;
+ Qt::TransformationMode transformationMode;
+ QPointF offset;
+ QGraphicsPixmapItem::ShapeMode shapeMode;
+ QPainterPath shape;
+ bool hasShape;
+
+ void updateShape()
+ {
+ shape = QPainterPath();
+ switch (shapeMode) {
+ case QGraphicsPixmapItem::MaskShape: {
+ QBitmap mask = pixmap.mask();
+ if (!mask.isNull()) {
+ shape = qt_regionToPath(QRegion(mask).translated(offset.toPoint()));
+ break;
+ }
+ // FALL THROUGH
+ }
+ case QGraphicsPixmapItem::BoundingRectShape:
+ shape.addRect(QRectF(offset.x(), offset.y(), pixmap.width(), pixmap.height()));
+ break;
+ case QGraphicsPixmapItem::HeuristicMaskShape:
+#ifndef QT_NO_IMAGE_HEURISTIC_MASK
+ shape = qt_regionToPath(QRegion(pixmap.createHeuristicMask()).translated(offset.toPoint()));
+#else
+ shape.addRect(QRectF(offset.x(), offset.y(), pixmap.width(), pixmap.height()));
+#endif
+ break;
+ }
+ }
+};
+
+/*!
+ Constructs a QGraphicsPixmapItem, using \a pixmap as the default pixmap.
+ \a parent is passed to QGraphicsItem's constructor.
+
+ \sa QGraphicsScene::addItem()
+*/
+QGraphicsPixmapItem::QGraphicsPixmapItem(const QPixmap &pixmap,
+ QGraphicsItem *parent
+#ifndef Q_QDOC
+ // obsolete argument
+ , QGraphicsScene *scene
+#endif
+ )
+ : QGraphicsItem(*new QGraphicsPixmapItemPrivate, parent, scene)
+{
+ setPixmap(pixmap);
+}
+
+/*!
+ Constructs a QGraphicsPixmapItem. \a parent is passed to QGraphicsItem's
+ constructor.
+
+ \sa QGraphicsScene::addItem()
+*/
+QGraphicsPixmapItem::QGraphicsPixmapItem(QGraphicsItem *parent
+#ifndef Q_QDOC
+ // obsolete argument
+ , QGraphicsScene *scene
+#endif
+ )
+ : QGraphicsItem(*new QGraphicsPixmapItemPrivate, parent, scene)
+{
+}
+
+/*!
+ Destroys the QGraphicsPixmapItem.
+*/
+QGraphicsPixmapItem::~QGraphicsPixmapItem()
+{
+}
+
+/*!
+ Sets the item's pixmap to \a pixmap.
+
+ \sa pixmap()
+*/
+void QGraphicsPixmapItem::setPixmap(const QPixmap &pixmap)
+{
+ Q_D(QGraphicsPixmapItem);
+ prepareGeometryChange();
+ d->pixmap = pixmap;
+ d->hasShape = false;
+ update();
+}
+
+/*!
+ Returns the item's pixmap, or an invalid QPixmap if no pixmap has been
+ set.
+
+ \sa setPixmap()
+*/
+QPixmap QGraphicsPixmapItem::pixmap() const
+{
+ Q_D(const QGraphicsPixmapItem);
+ return d->pixmap;
+}
+
+/*!
+ Returns the transformation mode of the pixmap. The default mode is
+ Qt::FastTransformation, which provides quick transformation with no
+ smoothing.
+
+ \sa setTransformationMode()
+*/
+Qt::TransformationMode QGraphicsPixmapItem::transformationMode() const
+{
+ Q_D(const QGraphicsPixmapItem);
+ return d->transformationMode;
+}
+
+/*!
+ Sets the pixmap item's transformation mode to \a mode, and toggles an
+ update of the item. The default mode is Qt::FastTransformation, which
+ provides quick transformation with no smoothing.
+
+ Qt::SmoothTransformation enables QPainter::SmoothPixmapTransform on the
+ painter, and the quality depends on the platform and viewport. The result
+ is usually not as good as calling QPixmap::scale() directly.
+
+ \sa transformationMode()
+*/
+void QGraphicsPixmapItem::setTransformationMode(Qt::TransformationMode mode)
+{
+ Q_D(QGraphicsPixmapItem);
+ if (mode != d->transformationMode) {
+ d_ptr->updateHelper();
+ d->transformationMode = mode;
+ update();
+ }
+}
+
+/*!
+ Returns the pixmap item's \e offset, which defines the point of the
+ top-left corner of the pixmap, in local coordinates.
+
+ \sa setOffset()
+*/
+QPointF QGraphicsPixmapItem::offset() const
+{
+ Q_D(const QGraphicsPixmapItem);
+ return d->offset;
+}
+
+/*!
+ Sets the pixmap item's offset to \a offset. QGraphicsPixmapItem will draw
+ its pixmap using \a offset for its top-left corner.
+
+ \sa offset()
+*/
+void QGraphicsPixmapItem::setOffset(const QPointF &offset)
+{
+ Q_D(QGraphicsPixmapItem);
+ if (d->offset == offset)
+ return;
+ prepareGeometryChange();
+ d->offset = offset;
+ d->hasShape = false;
+ update();
+}
+
+/*!
+ \fn void QGraphicsPixmapItem::setOffset(qreal x, qreal y)
+ \since 4.3
+
+ This convenience function is equivalent to calling setOffset(QPointF(\a x, \a y)).
+*/
+
+/*!
+ \reimp
+*/
+QRectF QGraphicsPixmapItem::boundingRect() const
+{
+ Q_D(const QGraphicsPixmapItem);
+ qreal pw = 1.0;
+ if (d->pixmap.isNull())
+ return QRectF();
+ return QRectF(d->offset, d->pixmap.size()).adjusted(-pw/2, -pw/2, pw/2, pw/2);
+}
+
+/*!
+ \reimp
+*/
+QPainterPath QGraphicsPixmapItem::shape() const
+{
+ Q_D(const QGraphicsPixmapItem);
+ if (!d->hasShape) {
+ QGraphicsPixmapItemPrivate *thatD = const_cast<QGraphicsPixmapItemPrivate *>(d);
+ thatD->updateShape();
+ thatD->hasShape = true;
+ }
+ return d_func()->shape;
+}
+
+/*!
+ \reimp
+*/
+bool QGraphicsPixmapItem::contains(const QPointF &point) const
+{
+ return QGraphicsItem::contains(point);
+}
+
+/*!
+ \reimp
+*/
+void QGraphicsPixmapItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option,
+ QWidget *widget)
+{
+ Q_D(QGraphicsPixmapItem);
+ Q_UNUSED(widget);
+
+ painter->setRenderHint(QPainter::SmoothPixmapTransform,
+ (d->transformationMode == Qt::SmoothTransformation));
+
+ QRectF exposed = option->exposedRect.adjusted(-1, -1, 1, 1);
+ exposed &= QRectF(d->offset.x(), d->offset.y(), d->pixmap.width(), d->pixmap.height());
+ painter->drawPixmap(exposed, d->pixmap, exposed.translated(-d->offset));
+
+ if (option->state & QStyle::State_Selected)
+ qt_graphicsItem_highlightSelected(this, painter, option);
+}
+
+/*!
+ \reimp
+*/
+bool QGraphicsPixmapItem::isObscuredBy(const QGraphicsItem *item) const
+{
+ return QGraphicsItem::isObscuredBy(item);
+}
+
+/*!
+ \reimp
+*/
+QPainterPath QGraphicsPixmapItem::opaqueArea() const
+{
+ return shape();
+}
+
+/*!
+ \reimp
+*/
+int QGraphicsPixmapItem::type() const
+{
+ return Type;
+}
+
+/*!
+ Returns the item's shape mode. The shape mode describes how
+ QGraphicsPixmapItem calculates its shape. The default mode is MaskShape.
+
+ \sa setShapeMode(), ShapeMode
+*/
+QGraphicsPixmapItem::ShapeMode QGraphicsPixmapItem::shapeMode() const
+{
+ return d_func()->shapeMode;
+}
+
+/*!
+ Sets the item's shape mode to \a mode. The shape mode describes how
+ QGraphicsPixmapItem calculates its shape. The default mode is MaskShape.
+
+ \sa shapeMode(), ShapeMode
+*/
+void QGraphicsPixmapItem::setShapeMode(ShapeMode mode)
+{
+ Q_D(QGraphicsPixmapItem);
+ if (d->shapeMode == mode)
+ return;
+ d->shapeMode = mode;
+ d->hasShape = false;
+}
+
+/*!
+ \internal
+*/
+bool QGraphicsPixmapItem::supportsExtension(Extension extension) const
+{
+ Q_UNUSED(extension);
+ return false;
+}
+
+/*!
+ \internal
+*/
+void QGraphicsPixmapItem::setExtension(Extension extension, const QVariant &variant)
+{
+ Q_UNUSED(extension);
+ Q_UNUSED(variant);
+}
+
+/*!
+ \internal
+*/
+QVariant QGraphicsPixmapItem::extension(const QVariant &variant) const
+{
+ Q_UNUSED(variant);
+ return QVariant();
+}
+
+/*!
+ \class QGraphicsTextItem
+ \brief The QGraphicsTextItem class provides a text item that you can add to
+ a QGraphicsScene to display formatted text.
+ \since 4.2
+ \ingroup multimedia
+ \ingroup graphicsview-api
+
+ If you only need to show plain text in an item, consider using QGraphicsSimpleTextItem
+ instead.
+
+ To set the item's text, pass a QString to QGraphicsTextItem's
+ constructor, or call setHtml()/setPlainText().
+
+ QGraphicsTextItem uses the text's formatted size and the associated font
+ to provide a reasonable implementation of boundingRect(), shape(),
+ and contains(). You can set the font by calling setFont().
+
+ It is possible to make the item editable by setting the Qt::TextEditorInteraction flag
+ using setTextInteractionFlags().
+
+ The item's preferred text width can be set using setTextWidth() and obtained
+ using textWidth().
+
+ \note In order to align HTML text in the center, the item's text width must be set.
+
+ \img graphicsview-textitem.png
+
+ \note QGraphicsTextItem accepts \l{QGraphicsItem::acceptHoverEvents()}{hover events}
+ by default. You can change this with \l{QGraphicsItem::}{setAcceptHoverEvents()}.
+
+ \sa QGraphicsSimpleTextItem, QGraphicsPathItem, QGraphicsRectItem,
+ QGraphicsEllipseItem, QGraphicsPixmapItem, QGraphicsPolygonItem,
+ QGraphicsLineItem, {The Graphics View Framework}
+*/
+
+class QGraphicsTextItemPrivate
+{
+public:
+ QGraphicsTextItemPrivate()
+ : control(0), pageNumber(0), useDefaultImpl(false), tabChangesFocus(false)
+ { }
+
+ mutable QTextControl *control;
+ QTextControl *textControl() const;
+
+ inline QPointF controlOffset() const
+ { return QPointF(0., pageNumber * control->document()->pageSize().height()); }
+ inline void sendControlEvent(QEvent *e)
+ { if (control) control->processEvent(e, controlOffset()); }
+
+ void _q_updateBoundingRect(const QSizeF &);
+ void _q_update(QRectF);
+ void _q_ensureVisible(QRectF);
+ bool _q_mouseOnEdge(QGraphicsSceneMouseEvent *);
+
+ QRectF boundingRect;
+ int pageNumber;
+ bool useDefaultImpl;
+ bool tabChangesFocus;
+
+ QGraphicsTextItem *qq;
+};
+
+/*!
+ Constructs a QGraphicsTextItem, using \a text as the default plain
+ text. \a parent is passed to QGraphicsItem's constructor.
+
+ \sa QGraphicsScene::addItem()
+*/
+QGraphicsTextItem::QGraphicsTextItem(const QString &text, QGraphicsItem *parent
+#ifndef Q_QDOC
+ // obsolete argument
+ , QGraphicsScene *scene
+#endif
+ )
+ : QGraphicsItem(parent, scene), dd(new QGraphicsTextItemPrivate)
+{
+ dd->qq = this;
+ if (!text.isEmpty())
+ setPlainText(text);
+ setAcceptDrops(true);
+ setAcceptHoverEvents(true);
+}
+
+/*!
+ Constructs a QGraphicsTextItem. \a parent is passed to QGraphicsItem's
+ constructor.
+
+ \sa QGraphicsScene::addItem()
+*/
+QGraphicsTextItem::QGraphicsTextItem(QGraphicsItem *parent
+#ifndef Q_QDOC
+ // obsolete argument
+ , QGraphicsScene *scene
+#endif
+ )
+ : QGraphicsItem(parent, scene), dd(new QGraphicsTextItemPrivate)
+{
+ dd->qq = this;
+ setAcceptDrops(true);
+ setAcceptHoverEvents(true);
+}
+
+/*!
+ Destroys the QGraphicsTextItem.
+*/
+QGraphicsTextItem::~QGraphicsTextItem()
+{
+ delete dd;
+}
+
+/*!
+ Returns the item's text converted to HTML, or an empty QString if no text has been set.
+
+ \sa setHtml()
+*/
+QString QGraphicsTextItem::toHtml() const
+{
+#ifndef QT_NO_TEXTHTMLPARSER
+ if (dd->control)
+ return dd->control->toHtml();
+#endif
+ return QString();
+}
+
+/*!
+ Sets the item's text to \a text, assuming that text is HTML formatted. If
+ the item has keyboard input focus, this function will also call
+ ensureVisible() to ensure that the text is visible in all viewports.
+
+ \sa toHtml(), hasFocus(), QGraphicsSimpleTextItem
+*/
+void QGraphicsTextItem::setHtml(const QString &text)
+{
+ dd->textControl()->setHtml(text);
+}
+
+/*!
+ Returns the item's text converted to plain text, or an empty QString if no text has been set.
+
+ \sa setPlainText()
+*/
+QString QGraphicsTextItem::toPlainText() const
+{
+ if (dd->control)
+ return dd->control->toPlainText();
+ return QString();
+}
+
+/*!
+ Sets the item's text to \a text. If the item has keyboard input focus,
+ this function will also call ensureVisible() to ensure that the text is
+ visible in all viewports.
+
+ \sa toHtml(), hasFocus()
+*/
+void QGraphicsTextItem::setPlainText(const QString &text)
+{
+ dd->textControl()->setPlainText(text);
+}
+
+/*!
+ Returns the item's font, which is used to render the text.
+
+ \sa setFont()
+*/
+QFont QGraphicsTextItem::font() const
+{
+ if (!dd->control)
+ return QFont();
+ return dd->control->document()->defaultFont();
+}
+
+/*!
+ Sets the font used to render the text item to \a font.
+
+ \sa font()
+*/
+void QGraphicsTextItem::setFont(const QFont &font)
+{
+ dd->textControl()->document()->setDefaultFont(font);
+}
+
+/*!
+ Sets the color for unformatted text to \a col.
+*/
+void QGraphicsTextItem::setDefaultTextColor(const QColor &col)
+{
+ QTextControl *c = dd->textControl();
+ QPalette pal = c->palette();
+ pal.setColor(QPalette::Text, col);
+ c->setPalette(pal);
+}
+
+/*!
+ Returns the default text color that is used to for unformatted text.
+*/
+QColor QGraphicsTextItem::defaultTextColor() const
+{
+ return dd->textControl()->palette().color(QPalette::Text);
+}
+
+/*!
+ \reimp
+*/
+QRectF QGraphicsTextItem::boundingRect() const
+{
+ return dd->boundingRect;
+}
+
+/*!
+ \reimp
+*/
+QPainterPath QGraphicsTextItem::shape() const
+{
+ if (!dd->control)
+ return QPainterPath();
+ QPainterPath path;
+ path.addRect(dd->boundingRect);
+ return path;
+}
+
+/*!
+ \reimp
+*/
+bool QGraphicsTextItem::contains(const QPointF &point) const
+{
+ return dd->boundingRect.contains(point);
+}
+
+/*!
+ \reimp
+*/
+void QGraphicsTextItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option,
+ QWidget *widget)
+{
+ Q_UNUSED(widget);
+ if (dd->control) {
+ painter->save();
+ QRectF r = option->exposedRect;
+ painter->translate(-dd->controlOffset());
+ r.translate(dd->controlOffset());
+
+ QTextDocument *doc = dd->control->document();
+ QTextDocumentLayout *layout = qobject_cast<QTextDocumentLayout *>(doc->documentLayout());
+
+ // the layout might need to expand the root frame to
+ // the viewport if NoWrap is set
+ if (layout)
+ layout->setViewport(dd->boundingRect);
+
+ dd->control->drawContents(painter, r);
+
+ if (layout)
+ layout->setViewport(QRect());
+
+ painter->restore();
+ }
+
+ if (option->state & (QStyle::State_Selected | QStyle::State_HasFocus))
+ qt_graphicsItem_highlightSelected(this, painter, option);
+}
+
+/*!
+ \reimp
+*/
+bool QGraphicsTextItem::isObscuredBy(const QGraphicsItem *item) const
+{
+ return QGraphicsItem::isObscuredBy(item);
+}
+
+/*!
+ \reimp
+*/
+QPainterPath QGraphicsTextItem::opaqueArea() const
+{
+ return QGraphicsItem::opaqueArea();
+}
+
+/*!
+ \reimp
+*/
+int QGraphicsTextItem::type() const
+{
+ return Type;
+}
+
+/*!
+ Sets the preferred width for the item's text. If the actual text
+ is wider than the specified width then it will be broken into
+ multiple lines.
+
+ If \a width is set to -1 then the text will not be broken into
+ multiple lines unless it is enforced through an explicit line
+ break or a new paragraph.
+
+ The default value is -1.
+
+ Note that QGraphicsTextItem keeps a QTextDocument internally,
+ which is used to calculate the text width.
+
+ \sa textWidth(), QTextDocument::setTextWidth()
+*/
+void QGraphicsTextItem::setTextWidth(qreal width)
+{
+ dd->textControl()->setTextWidth(width);
+}
+
+/*!
+ Returns the text width.
+
+ The width is calculated with the QTextDocument that
+ QGraphicsTextItem keeps internally.
+
+ \sa setTextWidth(), QTextDocument::textWidth()
+*/
+qreal QGraphicsTextItem::textWidth() const
+{
+ if (!dd->control)
+ return -1;
+ return dd->control->textWidth();
+}
+
+/*!
+ Adjusts the text item to a reasonable size.
+*/
+void QGraphicsTextItem::adjustSize()
+{
+ if (dd->control)
+ dd->control->adjustSize();
+}
+
+/*!
+ Sets the text document \a document on the item.
+*/
+void QGraphicsTextItem::setDocument(QTextDocument *document)
+{
+ dd->textControl()->setDocument(document);
+ dd->_q_updateBoundingRect(dd->control->size());
+}
+
+/*!
+ Returns the item's text document.
+*/
+QTextDocument *QGraphicsTextItem::document() const
+{
+ return dd->textControl()->document();
+}
+
+/*!
+ \reimp
+*/
+bool QGraphicsTextItem::sceneEvent(QEvent *event)
+{
+ QEvent::Type t = event->type();
+ if (!dd->tabChangesFocus && (t == QEvent::KeyPress || t == QEvent::KeyRelease)) {
+ int k = ((QKeyEvent *)event)->key();
+ if (k == Qt::Key_Tab || k == Qt::Key_Backtab) {
+ dd->sendControlEvent(event);
+ return true;
+ }
+ }
+ return QGraphicsItem::sceneEvent(event);
+}
+
+/*!
+ \reimp
+*/
+void QGraphicsTextItem::mousePressEvent(QGraphicsSceneMouseEvent *event)
+{
+ if ((QGraphicsItem::d_ptr->flags & (ItemIsSelectable | ItemIsMovable))
+ && (event->buttons() & Qt::LeftButton) && dd->_q_mouseOnEdge(event)) {
+ // User left-pressed on edge of selectable/movable item, use
+ // base impl.
+ dd->useDefaultImpl = true;
+ } else if (event->buttons() == event->button()
+ && dd->control->textInteractionFlags() == Qt::NoTextInteraction) {
+ // User pressed first button on non-interactive item.
+ dd->useDefaultImpl = true;
+ }
+ if (dd->useDefaultImpl) {
+ QGraphicsItem::mousePressEvent(event);
+ if (!event->isAccepted())
+ dd->useDefaultImpl = false;
+ return;
+ }
+ dd->sendControlEvent(event);
+}
+
+/*!
+ \reimp
+*/
+void QGraphicsTextItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
+{
+ if (dd->useDefaultImpl) {
+ QGraphicsItem::mouseMoveEvent(event);
+ return;
+ }
+ dd->sendControlEvent(event);
+}
+
+/*!
+ \reimp
+*/
+void QGraphicsTextItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
+{
+ if (dd->useDefaultImpl) {
+ QGraphicsItem::mouseReleaseEvent(event);
+ if (dd->control->textInteractionFlags() == Qt::NoTextInteraction
+ && !event->buttons()) {
+ // User released last button on non-interactive item.
+ dd->useDefaultImpl = false;
+ } else if ((event->buttons() & Qt::LeftButton) == 0) {
+ // User released the left button on an interactive item.
+ dd->useDefaultImpl = false;
+ }
+ return;
+ }
+ dd->sendControlEvent(event);
+}
+
+/*!
+ \reimp
+*/
+void QGraphicsTextItem::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event)
+{
+ if (dd->useDefaultImpl) {
+ QGraphicsItem::mouseDoubleClickEvent(event);
+ return;
+ }
+
+ if (!hasFocus()) {
+ QGraphicsItem::mouseDoubleClickEvent(event);
+ return;
+ }
+
+ dd->sendControlEvent(event);
+}
+
+/*!
+ \reimp
+*/
+void QGraphicsTextItem::contextMenuEvent(QGraphicsSceneContextMenuEvent *event)
+{
+ dd->sendControlEvent(event);
+}
+
+/*!
+ \reimp
+*/
+void QGraphicsTextItem::keyPressEvent(QKeyEvent *event)
+{
+ dd->sendControlEvent(event);
+}
+
+/*!
+ \reimp
+*/
+void QGraphicsTextItem::keyReleaseEvent(QKeyEvent *event)
+{
+ dd->sendControlEvent(event);
+}
+
+/*!
+ \reimp
+*/
+void QGraphicsTextItem::focusInEvent(QFocusEvent *event)
+{
+ dd->sendControlEvent(event);
+ update();
+}
+
+/*!
+ \reimp
+*/
+void QGraphicsTextItem::focusOutEvent(QFocusEvent *event)
+{
+ dd->sendControlEvent(event);
+ update();
+}
+
+/*!
+ \reimp
+*/
+void QGraphicsTextItem::dragEnterEvent(QGraphicsSceneDragDropEvent *event)
+{
+ dd->sendControlEvent(event);
+}
+
+/*!
+ \reimp
+*/
+void QGraphicsTextItem::dragLeaveEvent(QGraphicsSceneDragDropEvent *event)
+{
+ dd->sendControlEvent(event);
+}
+
+/*!
+ \reimp
+*/
+void QGraphicsTextItem::dragMoveEvent(QGraphicsSceneDragDropEvent *event)
+{
+ dd->sendControlEvent(event);
+}
+
+/*!
+ \reimp
+*/
+void QGraphicsTextItem::dropEvent(QGraphicsSceneDragDropEvent *event)
+{
+ dd->sendControlEvent(event);
+}
+
+/*!
+ \reimp
+*/
+void QGraphicsTextItem::inputMethodEvent(QInputMethodEvent *event)
+{
+ dd->sendControlEvent(event);
+}
+
+/*!
+ \reimp
+*/
+void QGraphicsTextItem::hoverEnterEvent(QGraphicsSceneHoverEvent *event)
+{
+ dd->sendControlEvent(event);
+}
+
+/*!
+ \reimp
+*/
+void QGraphicsTextItem::hoverMoveEvent(QGraphicsSceneHoverEvent *event)
+{
+ dd->sendControlEvent(event);
+}
+
+/*!
+ \reimp
+*/
+void QGraphicsTextItem::hoverLeaveEvent(QGraphicsSceneHoverEvent *event)
+{
+ dd->sendControlEvent(event);
+}
+
+/*!
+ \reimp
+*/
+QVariant QGraphicsTextItem::inputMethodQuery(Qt::InputMethodQuery query) const
+{
+ QVariant v;
+ if (dd->control)
+ v = dd->control->inputMethodQuery(query);
+ if (v.type() == QVariant::RectF)
+ v = v.toRectF().translated(-dd->controlOffset());
+ else if (v.type() == QVariant::PointF)
+ v = v.toPointF() - dd->controlOffset();
+ else if (v.type() == QVariant::Rect)
+ v = v.toRect().translated(-dd->controlOffset().toPoint());
+ else if (v.type() == QVariant::Point)
+ v = v.toPoint() - dd->controlOffset().toPoint();
+ return v;
+}
+
+/*!
+ \internal
+*/
+bool QGraphicsTextItem::supportsExtension(Extension extension) const
+{
+ Q_UNUSED(extension);
+ return false;
+}
+
+/*!
+ \internal
+*/
+void QGraphicsTextItem::setExtension(Extension extension, const QVariant &variant)
+{
+ Q_UNUSED(extension);
+ Q_UNUSED(variant);
+}
+
+/*!
+ \internal
+*/
+QVariant QGraphicsTextItem::extension(const QVariant &variant) const
+{
+ Q_UNUSED(variant);
+ return QVariant();
+}
+
+/*!
+ \internal
+*/
+void QGraphicsTextItemPrivate::_q_update(QRectF rect)
+{
+ if (rect.isValid()) {
+ rect.translate(-controlOffset());
+ } else {
+ rect = boundingRect;
+ }
+ if (rect.intersects(boundingRect))
+ qq->update(rect);
+}
+
+/*!
+ \internal
+*/
+void QGraphicsTextItemPrivate::_q_updateBoundingRect(const QSizeF &size)
+{
+ if (!control) return; // can't happen
+ const QSizeF pageSize = control->document()->pageSize();
+ // paged items have a constant (page) size
+ if (size == boundingRect.size() || pageSize.height() != -1)
+ return;
+ qq->prepareGeometryChange();
+ boundingRect.setSize(size);
+ qq->update();
+}
+
+/*!
+ \internal
+*/
+void QGraphicsTextItemPrivate::_q_ensureVisible(QRectF rect)
+{
+ if (qq->hasFocus()) {
+ rect.translate(-controlOffset());
+ qq->ensureVisible(rect, /*xmargin=*/0, /*ymargin=*/0);
+ }
+}
+
+QTextControl *QGraphicsTextItemPrivate::textControl() const
+{
+ if (!control) {
+ QGraphicsTextItem *that = const_cast<QGraphicsTextItem *>(qq);
+ control = new QTextControl(that);
+ control->setTextInteractionFlags(Qt::NoTextInteraction);
+
+ QObject::connect(control, SIGNAL(updateRequest(QRectF)),
+ qq, SLOT(_q_update(QRectF)));
+ QObject::connect(control, SIGNAL(documentSizeChanged(QSizeF)),
+ qq, SLOT(_q_updateBoundingRect(QSizeF)));
+ QObject::connect(control, SIGNAL(visibilityRequest(QRectF)),
+ qq, SLOT(_q_ensureVisible(QRectF)));
+ QObject::connect(control, SIGNAL(linkActivated(QString)),
+ qq, SIGNAL(linkActivated(QString)));
+ QObject::connect(control, SIGNAL(linkHovered(QString)),
+ qq, SIGNAL(linkHovered(QString)));
+
+ const QSizeF pgSize = control->document()->pageSize();
+ if (pgSize.height() != -1) {
+ qq->prepareGeometryChange();
+ that->dd->boundingRect.setSize(pgSize);
+ qq->update();
+ } else {
+ that->dd->_q_updateBoundingRect(control->size());
+ }
+ }
+ return control;
+}
+
+/*!
+ \internal
+*/
+bool QGraphicsTextItemPrivate::_q_mouseOnEdge(QGraphicsSceneMouseEvent *event)
+{
+ QPainterPath path;
+ path.addRect(qq->boundingRect());
+
+ QPainterPath docPath;
+ const QTextFrameFormat format = control->document()->rootFrame()->frameFormat();
+ docPath.addRect(
+ qq->boundingRect().adjusted(
+ format.leftMargin(),
+ format.topMargin(),
+ -format.rightMargin(),
+ -format.bottomMargin()));
+
+ return path.subtracted(docPath).contains(event->pos());
+}
+
+/*!
+ \fn QGraphicsTextItem::linkActivated(const QString &link)
+
+ This signal is emitted when the user clicks on a link on a text item
+ that enables Qt::LinksAccessibleByMouse or Qt::LinksAccessibleByKeyboard.
+ \a link is the link that was clicked.
+
+ \sa setTextInteractionFlags()
+*/
+
+/*!
+ \fn QGraphicsTextItem::linkHovered(const QString &link)
+
+ This signal is emitted when the user hovers over a link on a text item
+ that enables Qt::LinksAccessibleByMouse. \a link is
+ the link that was hovered over.
+
+ \sa setTextInteractionFlags()
+*/
+
+/*!
+ Sets the flags \a flags to specify how the text item should react to user
+ input.
+
+ The default for a QGraphicsTextItem is Qt::NoTextInteraction. Setting a
+ value different to Qt::NoTextInteraction will also set the ItemIsFocusable
+ QGraphicsItem flag.
+
+ By default, the text is read-only. To transform the item into an editor,
+ set the Qt::TextEditable flag.
+*/
+void QGraphicsTextItem::setTextInteractionFlags(Qt::TextInteractionFlags flags)
+{
+ if (flags == Qt::NoTextInteraction)
+ setFlags(this->flags() & ~QGraphicsItem::ItemIsFocusable);
+ else
+ setFlags(this->flags() | QGraphicsItem::ItemIsFocusable);
+ dd->textControl()->setTextInteractionFlags(flags);
+}
+
+/*!
+ Returns the current text interaction flags.
+
+ \sa setTextInteractionFlags()
+*/
+Qt::TextInteractionFlags QGraphicsTextItem::textInteractionFlags() const
+{
+ if (!dd->control)
+ return Qt::NoTextInteraction;
+ return dd->control->textInteractionFlags();
+}
+
+/*!
+ \since 4.5
+
+ If \a b is true, the \gui Tab key will cause the widget to change focus;
+ otherwise, the tab key will insert a tab into the document.
+
+ In some occasions text edits should not allow the user to input tabulators
+ or change indentation using the \gui Tab key, as this breaks the focus
+ chain. The default is false.
+
+ \sa tabChangesFocus(), ItemIsFocusable, textInteractionFlags()
+*/
+void QGraphicsTextItem::setTabChangesFocus(bool b)
+{
+ dd->tabChangesFocus = b;
+}
+
+/*!
+ \since 4.5
+
+ Returns true if the \gui Tab key will cause the widget to change focus;
+ otherwise, false is returned.
+
+ \sa setTabChangesFocus()
+*/
+bool QGraphicsTextItem::tabChangesFocus() const
+{
+ return dd->tabChangesFocus;
+}
+
+/*!
+ \property QGraphicsTextItem::openExternalLinks
+
+ Specifies whether QGraphicsTextItem should automatically open links using
+ QDesktopServices::openUrl() instead of emitting the
+ linkActivated signal.
+
+ The default value is false.
+*/
+void QGraphicsTextItem::setOpenExternalLinks(bool open)
+{
+ dd->textControl()->setOpenExternalLinks(open);
+}
+
+bool QGraphicsTextItem::openExternalLinks() const
+{
+ if (!dd->control)
+ return false;
+ return dd->control->openExternalLinks();
+}
+
+/*!
+ \property QGraphicsTextItem::textCursor
+
+ This property represents the visible text cursor in an editable
+ text item.
+
+ By default, if the item's text has not been set, this property
+ contains a null text cursor; otherwise it contains a text cursor
+ placed at the start of the item's document.
+*/
+void QGraphicsTextItem::setTextCursor(const QTextCursor &cursor)
+{
+ dd->textControl()->setTextCursor(cursor);
+}
+
+QTextCursor QGraphicsTextItem::textCursor() const
+{
+ if (!dd->control)
+ return QTextCursor();
+ return dd->control->textCursor();
+}
+
+class QGraphicsSimpleTextItemPrivate : public QAbstractGraphicsShapeItemPrivate
+{
+ Q_DECLARE_PUBLIC(QGraphicsSimpleTextItem)
+public:
+ inline QGraphicsSimpleTextItemPrivate() {
+ pen.setStyle(Qt::NoPen);
+ brush.setStyle(Qt::SolidPattern);
+ }
+ QString text;
+ QFont font;
+ QRectF boundingRect;
+
+ void updateBoundingRect();
+};
+
+static QRectF setupTextLayout(QTextLayout *layout)
+{
+ layout->setCacheEnabled(true);
+ layout->beginLayout();
+ while (layout->createLine().isValid())
+ ;
+ layout->endLayout();
+ qreal maxWidth = 0;
+ qreal y = 0;
+ for (int i = 0; i < layout->lineCount(); ++i) {
+ QTextLine line = layout->lineAt(i);
+ maxWidth = qMax(maxWidth, line.naturalTextWidth());
+ line.setPosition(QPointF(0, y));
+ y += line.height();
+ }
+ return QRectF(0, 0, maxWidth, y);
+}
+
+void QGraphicsSimpleTextItemPrivate::updateBoundingRect()
+{
+ Q_Q(QGraphicsSimpleTextItem);
+ QRectF br;
+ if (text.isEmpty()) {
+ br = QRectF();
+ } else {
+ QString tmp = text;
+ tmp.replace(QLatin1Char('\n'), QChar::LineSeparator);
+ QStackTextEngine engine(tmp, font);
+ QTextLayout layout(&engine);
+ br = setupTextLayout(&layout);
+ }
+ if (br != boundingRect) {
+ q->prepareGeometryChange();
+ boundingRect = br;
+ q->update();
+ }
+}
+
+/*!
+ \class QGraphicsSimpleTextItem
+ \brief The QGraphicsSimpleTextItem class provides a simple text path item
+ that you can add to a QGraphicsScene.
+ \since 4.2
+ \ingroup multimedia
+ \ingroup graphicsview-api
+
+ To set the item's text, you can either pass a QString to
+ QGraphicsSimpleTextItem's constructor, or call setText() to change the
+ text later. To set the text fill color, call setBrush().
+
+ The simple text item can have both a fill and an outline; setBrush() will
+ set the text fill (i.e., text color), and setPen() sets the pen that will
+ be used to draw the text outline. (The latter can be slow, especially for
+ complex pens, and items with long text content.) If all you want is to
+ draw a simple line of text, you should call setBrush() only, and leave the
+ pen unset; QGraphicsSimpleTextItem's pen is by default Qt::NoPen.
+
+ QGraphicsSimpleTextItem uses the text's formatted size and the associated
+ font to provide a reasonable implementation of boundingRect(), shape(),
+ and contains(). You can set the font by calling setFont().
+
+ QGraphicsSimpleText does not display rich text; instead, you can use
+ QGraphicsTextItem, which provides full text control capabilities.
+
+ \img graphicsview-simpletextitem.png
+
+ \sa QGraphicsTextItem, QGraphicsPathItem, QGraphicsRectItem, QGraphicsEllipseItem,
+ QGraphicsPixmapItem, QGraphicsPolygonItem, QGraphicsLineItem, {The
+ Graphics View Framework}
+*/
+
+/*!
+ Constructs a QGraphicsSimpleTextItem.
+
+ \a parent is passed to QGraphicsItem's constructor.
+
+ \sa QGraphicsScene::addItem()
+*/
+QGraphicsSimpleTextItem::QGraphicsSimpleTextItem(QGraphicsItem *parent
+#ifndef Q_QDOC
+ // obsolete argument
+ , QGraphicsScene *scene
+#endif
+ )
+ : QAbstractGraphicsShapeItem(*new QGraphicsSimpleTextItemPrivate, parent, scene)
+{
+}
+
+/*!
+ Constructs a QGraphicsSimpleTextItem, using \a text as the default plain text.
+
+ \a parent is passed to QGraphicsItem's constructor.
+
+ \sa QGraphicsScene::addItem()
+*/
+QGraphicsSimpleTextItem::QGraphicsSimpleTextItem(const QString &text, QGraphicsItem *parent
+#ifndef Q_QDOC
+ // obsolete argument
+ , QGraphicsScene *scene
+#endif
+ )
+ : QAbstractGraphicsShapeItem(*new QGraphicsSimpleTextItemPrivate, parent, scene)
+{
+ setText(text);
+}
+
+/*!
+ Destroys the QGraphicsSimpleTextItem.
+*/
+QGraphicsSimpleTextItem::~QGraphicsSimpleTextItem()
+{
+}
+
+/*!
+ Sets the item's text to \a text. The text will be displayed as
+ plain text. Newline characters ('\n') as well as characters of
+ type QChar::LineSeparator will cause item to break the text into
+ multiple lines.
+*/
+void QGraphicsSimpleTextItem::setText(const QString &text)
+{
+ Q_D(QGraphicsSimpleTextItem);
+ if (d->text == text)
+ return;
+ d->text = text;
+ d->updateBoundingRect();
+}
+
+/*!
+ Returns the item's text.
+*/
+QString QGraphicsSimpleTextItem::text() const
+{
+ Q_D(const QGraphicsSimpleTextItem);
+ return d->text;
+}
+
+/*!
+ Sets the font that is used to draw the item's text to \a font.
+*/
+void QGraphicsSimpleTextItem::setFont(const QFont &font)
+{
+ Q_D(QGraphicsSimpleTextItem);
+ d->font = font;
+ d->updateBoundingRect();
+}
+
+/*!
+ Returns the font that is used to draw the item's text.
+*/
+QFont QGraphicsSimpleTextItem::font() const
+{
+ Q_D(const QGraphicsSimpleTextItem);
+ return d->font;
+}
+
+/*!
+ \reimp
+*/
+QRectF QGraphicsSimpleTextItem::boundingRect() const
+{
+ Q_D(const QGraphicsSimpleTextItem);
+ return d->boundingRect;
+}
+
+/*!
+ \reimp
+*/
+QPainterPath QGraphicsSimpleTextItem::shape() const
+{
+ Q_D(const QGraphicsSimpleTextItem);
+ QPainterPath path;
+ path.addRect(d->boundingRect);
+ return path;
+}
+
+/*!
+ \reimp
+*/
+bool QGraphicsSimpleTextItem::contains(const QPointF &point) const
+{
+ Q_D(const QGraphicsSimpleTextItem);
+ return d->boundingRect.contains(point);
+}
+
+/*!
+ \reimp
+*/
+void QGraphicsSimpleTextItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
+{
+ Q_UNUSED(widget);
+ Q_D(QGraphicsSimpleTextItem);
+
+ painter->setFont(d->font);
+
+ QString tmp = d->text;
+ tmp.replace(QLatin1Char('\n'), QChar::LineSeparator);
+ QStackTextEngine engine(tmp, d->font);
+ QTextLayout layout(&engine);
+ setupTextLayout(&layout);
+
+ QPen p;
+ p.setBrush(d->brush);
+ painter->setPen(p);
+ if (d->pen.style() == Qt::NoPen && d->brush.style() == Qt::SolidPattern) {
+ painter->setBrush(Qt::NoBrush);
+ } else {
+ QTextLayout::FormatRange range;
+ range.start = 0;
+ range.length = layout.text().length();
+ range.format.setTextOutline(d->pen);
+ QList<QTextLayout::FormatRange> formats;
+ formats.append(range);
+ layout.setAdditionalFormats(formats);
+ }
+
+ layout.draw(painter, QPointF(0, 0));
+
+ if (option->state & (QStyle::State_Selected | QStyle::State_HasFocus))
+ qt_graphicsItem_highlightSelected(this, painter, option);
+}
+
+/*!
+ \reimp
+*/
+bool QGraphicsSimpleTextItem::isObscuredBy(const QGraphicsItem *item) const
+{
+ return QAbstractGraphicsShapeItem::isObscuredBy(item);
+}
+
+/*!
+ \reimp
+*/
+QPainterPath QGraphicsSimpleTextItem::opaqueArea() const
+{
+ return QAbstractGraphicsShapeItem::opaqueArea();
+}
+
+/*!
+ \reimp
+*/
+int QGraphicsSimpleTextItem::type() const
+{
+ return Type;
+}
+
+/*!
+ \internal
+*/
+bool QGraphicsSimpleTextItem::supportsExtension(Extension extension) const
+{
+ Q_UNUSED(extension);
+ return false;
+}
+
+/*!
+ \internal
+*/
+void QGraphicsSimpleTextItem::setExtension(Extension extension, const QVariant &variant)
+{
+ Q_UNUSED(extension);
+ Q_UNUSED(variant);
+}
+
+/*!
+ \internal
+*/
+QVariant QGraphicsSimpleTextItem::extension(const QVariant &variant) const
+{
+ Q_UNUSED(variant);
+ return QVariant();
+}
+
+/*!
+ \class QGraphicsItemGroup
+ \brief The QGraphicsItemGroup class provides treating a group of items as
+ one.
+ \since 4.2
+ \ingroup multimedia
+ \ingroup graphicsview-api
+
+ A QGraphicsItemGroup is a special type of compound item that
+ treats itself and all its children as one item (i.e., all events
+ and geometries for all children are merged together). It's common
+ to use item groups in presentation tools, when the user wants to
+ group several smaller items into one big item in order to simplify
+ moving and copying of items.
+
+ If all you want is to store items inside other items, you can use
+ any QGraphicsItem directly by passing a suitable parent to
+ setParentItem().
+
+ The boundingRect() function of QGraphicsItemGroup returns the
+ bounding rectangle of all items in the item group. In addition,
+ item groups have handlesChildEvents() enabled by default, so all
+ events sent to a member of the group go to the item group (i.e.,
+ selecting one item in a group will select them all).
+ QGraphicsItemGroup ignores the ItemIgnoresTransformations flag on its
+ children (i.e., with respect to the geometry of the group item, the
+ children are treated as if they were transformable).
+
+ There are two ways to construct an item group. The easiest and
+ most common approach is to pass a list of items (e.g., all
+ selected items) to QGraphicsScene::createItemGroup(), which
+ returns a new QGraphicsItemGroup item. The other approach is to
+ manually construct a QGraphicsItemGroup item, add it to the scene
+ calling QGraphicsScene::addItem(), and then add items to the group
+ manually, one at a time by calling addToGroup(). To dismantle
+ ("ungroup") an item group, you can either call
+ QGraphicsScene::destroyItemGroup(), or you can manually remove all
+ items from the group by calling removeFromGroup().
+
+ \snippet doc/src/snippets/code/src_gui_graphicsview_qgraphicsitem.cpp 17
+
+ The operation of adding and removing items preserves the items'
+ scene-relative position and transformation, as opposed to calling
+ setParentItem(), where only the child item's parent-relative
+ position and transformation are kept.
+
+ The addtoGroup() function reparents the target item to this item
+ group, keeping the item's position and transformation intact
+ relative to the scene. Visually, this means that items added via
+ addToGroup() will remain completely unchanged as a result of this
+ operation, regardless of the item or the group's current position
+ or transformation; although the item's position and matrix are
+ likely to change.
+
+ The removeFromGroup() function has similar semantics to
+ setParentItem(); it reparents the item to the parent item of the
+ item group. As with addToGroup(), the item's scene-relative
+ position and transformation remain intact.
+
+ \sa QGraphicsItem, {The Graphics View Framework}
+*/
+
+class QGraphicsItemGroupPrivate : public QGraphicsItemPrivate
+{
+public:
+ QRectF itemsBoundingRect;
+};
+
+/*!
+ Constructs a QGraphicsItemGroup. \a parent is passed to QGraphicsItem's
+ constructor.
+
+ \sa QGraphicsScene::addItem()
+*/
+QGraphicsItemGroup::QGraphicsItemGroup(QGraphicsItem *parent
+#ifndef Q_QDOC
+ // obsolete argument
+ , QGraphicsScene *scene
+#endif
+ )
+ : QGraphicsItem(*new QGraphicsItemGroupPrivate, parent, scene)
+{
+ setHandlesChildEvents(true);
+}
+
+/*!
+ Destroys the QGraphicsItemGroup.
+*/
+QGraphicsItemGroup::~QGraphicsItemGroup()
+{
+}
+
+/*!
+ Adds the given \a item to this item group. The item will be
+ reparented to this group, but its position and transformation
+ relative to the scene will stay intact.
+
+ \sa removeFromGroup(), QGraphicsScene::createItemGroup()
+*/
+void QGraphicsItemGroup::addToGroup(QGraphicsItem *item)
+{
+ Q_D(QGraphicsItemGroup);
+ if (!item) {
+ qWarning("QGraphicsItemGroup::addToGroup: cannot add null item");
+ return;
+ }
+ if (item == this) {
+ qWarning("QGraphicsItemGroup::addToGroup: cannot add a group to itself");
+ return;
+ }
+
+ QTransform oldSceneMatrix = item->sceneTransform();
+ item->setPos(mapFromItem(item, 0, 0));
+ item->setParentItem(this);
+ item->setTransform(oldSceneMatrix
+ * sceneTransform().inverted()
+ * QTransform::fromTranslate(-item->x(), -item->y()));
+ item->d_func()->setIsMemberOfGroup(true);
+ prepareGeometryChange();
+ d->itemsBoundingRect |= (item->transform() * QTransform::fromTranslate(item->x(), item->y()))
+ .mapRect(item->boundingRect() | item->childrenBoundingRect());
+ update();
+}
+
+/*!
+ Removes the specified \a item from this group. The item will be
+ reparented to this group's parent item, or to 0 if this group has
+ no parent. Its position and transformation relative to the scene
+ will stay intact.
+
+ \sa addToGroup(), QGraphicsScene::destroyItemGroup()
+*/
+void QGraphicsItemGroup::removeFromGroup(QGraphicsItem *item)
+{
+ Q_D(QGraphicsItemGroup);
+ if (!item) {
+ qWarning("QGraphicsItemGroup::removeFromGroup: cannot remove null item");
+ return;
+ }
+
+ QGraphicsItem *newParent = d_ptr->parent;
+ QPointF oldPos = item->mapToItem(newParent, 0, 0);
+ item->setParentItem(newParent);
+ // ### This function should remap the item's matrix to keep the item's
+ // transformation unchanged relative to the scene.
+ item->setPos(oldPos);
+ item->d_func()->setIsMemberOfGroup(item->group() != 0);
+
+ // ### Quite expensive. But removeFromGroup() isn't called very often.
+ prepareGeometryChange();
+ d->itemsBoundingRect = childrenBoundingRect();
+}
+
+/*!
+ \reimp
+
+ Returns the bounding rect of this group item, and all its children.
+*/
+QRectF QGraphicsItemGroup::boundingRect() const
+{
+ Q_D(const QGraphicsItemGroup);
+ return d->itemsBoundingRect;
+}
+
+/*!
+ \reimp
+*/
+void QGraphicsItemGroup::paint(QPainter *painter, const QStyleOptionGraphicsItem *option,
+ QWidget *widget)
+{
+ Q_UNUSED(widget);
+ if (option->state & QStyle::State_Selected) {
+ Q_D(QGraphicsItemGroup);
+ painter->setBrush(Qt::NoBrush);
+ painter->drawRect(d->itemsBoundingRect);
+ }
+}
+
+/*!
+ \reimp
+*/
+bool QGraphicsItemGroup::isObscuredBy(const QGraphicsItem *item) const
+{
+ return QGraphicsItem::isObscuredBy(item);
+}
+
+/*!
+ \reimp
+*/
+QPainterPath QGraphicsItemGroup::opaqueArea() const
+{
+ return QGraphicsItem::opaqueArea();
+}
+
+/*!
+ \reimp
+*/
+int QGraphicsItemGroup::type() const
+{
+ return Type;
+}
+
+#ifndef QT_NO_DEBUG_STREAM
+QDebug operator<<(QDebug debug, QGraphicsItem *item)
+{
+ if (!item) {
+ debug << "QGraphicsItem(0)";
+ return debug;
+ }
+
+ QStringList flags;
+ if (item->isVisible()) flags << QLatin1String("isVisible");
+ if (item->isEnabled()) flags << QLatin1String("isEnabled");
+ if (item->isSelected()) flags << QLatin1String("isSelected");
+ if (item->hasFocus()) flags << QLatin1String("HasFocus");
+
+ debug << "QGraphicsItem(this =" << ((void*)item)
+ << ", parent =" << ((void*)item->parentItem())
+ << ", pos =" << item->pos()
+ << ", z =" << item->zValue() << ", flags = {"
+ << flags.join(QLatin1String("|")) << " })";
+ return debug;
+}
+
+QDebug operator<<(QDebug debug, QGraphicsItem::GraphicsItemChange change)
+{
+ const char *str = "UnknownChange";
+ switch (change) {
+ case QGraphicsItem::ItemChildAddedChange:
+ str = "ItemChildAddedChange";
+ break;
+ case QGraphicsItem::ItemChildRemovedChange:
+ str = "ItemChildRemovedChange";
+ break;
+ case QGraphicsItem::ItemCursorChange:
+ str = "ItemCursorChange";
+ break;
+ case QGraphicsItem::ItemCursorHasChanged:
+ str = "ItemCursorHasChanged";
+ break;
+ case QGraphicsItem::ItemEnabledChange:
+ str = "ItemEnabledChange";
+ break;
+ case QGraphicsItem::ItemEnabledHasChanged:
+ str = "ItemEnabledHasChanged";
+ break;
+ case QGraphicsItem::ItemFlagsChange:
+ str = "ItemFlagsChange";
+ break;
+ case QGraphicsItem::ItemFlagsHaveChanged:
+ str = "ItemFlagsHaveChanged";
+ break;
+ case QGraphicsItem::ItemMatrixChange:
+ str = "ItemMatrixChange";
+ break;
+ case QGraphicsItem::ItemParentChange:
+ str = "ItemParentChange";
+ break;
+ case QGraphicsItem::ItemParentHasChanged:
+ str = "ItemParentHasChanged";
+ break;
+ case QGraphicsItem::ItemPositionChange:
+ str = "ItemPositionChange";
+ break;
+ case QGraphicsItem::ItemPositionHasChanged:
+ str = "ItemPositionHasChanged";
+ break;
+ case QGraphicsItem::ItemSceneChange:
+ str = "ItemSceneChange";
+ break;
+ case QGraphicsItem::ItemSceneHasChanged:
+ str = "ItemSceneHasChanged";
+ break;
+ case QGraphicsItem::ItemSelectedChange:
+ str = "ItemSelectedChange";
+ break;
+ case QGraphicsItem::ItemSelectedHasChanged:
+ str = "ItemSelectedHasChanged";
+ break;
+ case QGraphicsItem::ItemToolTipChange:
+ str = "ItemToolTipChange";
+ break;
+ case QGraphicsItem::ItemToolTipHasChanged:
+ str = "ItemToolTipHasChanged";
+ break;
+ case QGraphicsItem::ItemTransformChange:
+ str = "ItemTransformChange";
+ break;
+ case QGraphicsItem::ItemTransformHasChanged:
+ str = "ItemTransformHasChanged";
+ break;
+ case QGraphicsItem::ItemVisibleChange:
+ str = "ItemVisibleChange";
+ break;
+ case QGraphicsItem::ItemVisibleHasChanged:
+ str = "ItemVisibleHasChanged";
+ break;
+ case QGraphicsItem::ItemZValueChange:
+ str = "ItemZValueChange";
+ break;
+ case QGraphicsItem::ItemZValueHasChanged:
+ str = "ItemZValueHasChanged";
+ break;
+ case QGraphicsItem::ItemOpacityChange:
+ str = "ItemOpacityChange";
+ break;
+ case QGraphicsItem::ItemOpacityHasChanged:
+ str = "ItemOpacityHasChanged";
+ break;
+ }
+ debug << str;
+ return debug;
+}
+
+QDebug operator<<(QDebug debug, QGraphicsItem::GraphicsItemFlag flag)
+{
+ const char *str = "UnknownFlag";
+ switch (flag) {
+ case QGraphicsItem::ItemIsMovable:
+ str = "ItemIsMovable";
+ break;
+ case QGraphicsItem::ItemIsSelectable:
+ str = "ItemIsSelectable";
+ break;
+ case QGraphicsItem::ItemIsFocusable:
+ str = "ItemIsFocusable";
+ break;
+ case QGraphicsItem::ItemClipsToShape:
+ str = "ItemClipsToShape";
+ break;
+ case QGraphicsItem::ItemClipsChildrenToShape:
+ str = "ItemClipsChildrenToShape";
+ break;
+ case QGraphicsItem::ItemIgnoresTransformations:
+ str = "ItemIgnoresTransformations";
+ break;
+ case QGraphicsItem::ItemIgnoresParentOpacity:
+ str = "ItemIgnoresParentOpacity";
+ break;
+ case QGraphicsItem::ItemDoesntPropagateOpacityToChildren:
+ str = "ItemDoesntPropagateOpacityToChildren";
+ break;
+ case QGraphicsItem::ItemStacksBehindParent:
+ str = "ItemStacksBehindParent";
+ break;
+ }
+ debug << str;
+ return debug;
+}
+
+QDebug operator<<(QDebug debug, QGraphicsItem::GraphicsItemFlags flags)
+{
+ debug << "(";
+ bool f = false;
+ for (int i = 0; i < 9; ++i) {
+ if (flags & (1 << i)) {
+ if (f)
+ debug << "|";
+ f = true;
+ debug << QGraphicsItem::GraphicsItemFlag(int(flags & (1 << i)));
+ }
+ }
+ debug << ")";
+ return debug;
+}
+
+#endif
+
+QT_END_NAMESPACE
+
+#include "moc_qgraphicsitem.cpp"
+
+#endif // QT_NO_GRAPHICSVIEW
diff --git a/src/gui/graphicsview/qgraphicsitem.h b/src/gui/graphicsview/qgraphicsitem.h
new file mode 100644
index 0000000000..b98882d1d4
--- /dev/null
+++ b/src/gui/graphicsview/qgraphicsitem.h
@@ -0,0 +1,1016 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QGRAPHICSITEM_H
+#define QGRAPHICSITEM_H
+
+#include <QtCore/qglobal.h>
+#include <QtCore/qobject.h>
+#include <QtCore/qvariant.h>
+#include <QtCore/qrect.h>
+#include <QtGui/qpainterpath.h>
+#include <QtGui/qpixmap.h>
+
+class tst_QGraphicsItem;
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+#if !defined(QT_NO_GRAPHICSVIEW) || (QT_EDITION & QT_MODULE_GRAPHICSVIEW) != QT_MODULE_GRAPHICSVIEW
+
+class QBrush;
+class QCursor;
+class QFocusEvent;
+class QGraphicsItemGroup;
+class QGraphicsSceneContextMenuEvent;
+class QGraphicsSceneDragDropEvent;
+class QGraphicsSceneEvent;
+class QGraphicsSceneHoverEvent;
+class QGraphicsSceneMouseEvent;
+class QGraphicsSceneWheelEvent;
+class QGraphicsScene;
+class QGraphicsWidget;
+class QInputMethodEvent;
+class QKeyEvent;
+class QMatrix;
+class QMenu;
+class QPainter;
+class QPen;
+class QPointF;
+class QRectF;
+class QStyleOptionGraphicsItem;
+
+class QGraphicsItemPrivate;
+class Q_GUI_EXPORT QGraphicsItem
+{
+public:
+ enum GraphicsItemFlag {
+ ItemIsMovable = 0x1,
+ ItemIsSelectable = 0x2,
+ ItemIsFocusable = 0x4,
+ ItemClipsToShape = 0x8,
+ ItemClipsChildrenToShape = 0x10,
+ ItemIgnoresTransformations = 0x20,
+ ItemIgnoresParentOpacity = 0x40,
+ ItemDoesntPropagateOpacityToChildren = 0x80,
+ ItemStacksBehindParent = 0x100
+ };
+ Q_DECLARE_FLAGS(GraphicsItemFlags, GraphicsItemFlag)
+
+ enum GraphicsItemChange {
+ ItemPositionChange,
+ ItemMatrixChange,
+ ItemVisibleChange,
+ ItemEnabledChange,
+ ItemSelectedChange,
+ ItemParentChange,
+ ItemChildAddedChange,
+ ItemChildRemovedChange,
+ ItemTransformChange,
+ ItemPositionHasChanged,
+ ItemTransformHasChanged,
+ ItemSceneChange,
+ ItemVisibleHasChanged,
+ ItemEnabledHasChanged,
+ ItemSelectedHasChanged,
+ ItemParentHasChanged,
+ ItemSceneHasChanged,
+ ItemCursorChange,
+ ItemCursorHasChanged,
+ ItemToolTipChange,
+ ItemToolTipHasChanged,
+ ItemFlagsChange,
+ ItemFlagsHaveChanged,
+ ItemZValueChange,
+ ItemZValueHasChanged,
+ ItemOpacityChange,
+ ItemOpacityHasChanged
+ };
+
+ enum CacheMode {
+ NoCache,
+ ItemCoordinateCache,
+ DeviceCoordinateCache
+ };
+
+ QGraphicsItem(QGraphicsItem *parent = 0
+#ifndef Q_QDOC
+ // obsolete argument
+ , QGraphicsScene *scene = 0
+#endif
+ );
+ virtual ~QGraphicsItem();
+
+ QGraphicsScene *scene() const;
+
+ QGraphicsItem *parentItem() const;
+ QGraphicsItem *topLevelItem() const;
+ QGraphicsWidget *parentWidget() const;
+ QGraphicsWidget *topLevelWidget() const;
+ QGraphicsWidget *window() const;
+ void setParentItem(QGraphicsItem *parent);
+ QList<QGraphicsItem *> children() const; // ### obsolete
+ QList<QGraphicsItem *> childItems() const;
+ bool isWidget() const;
+ bool isWindow() const;
+
+ QGraphicsItemGroup *group() const;
+ void setGroup(QGraphicsItemGroup *group);
+
+ GraphicsItemFlags flags() const;
+ void setFlag(GraphicsItemFlag flag, bool enabled = true);
+ void setFlags(GraphicsItemFlags flags);
+
+ CacheMode cacheMode() const;
+ void setCacheMode(CacheMode mode, const QSize &cacheSize = QSize());
+
+#ifndef QT_NO_TOOLTIP
+ QString toolTip() const;
+ void setToolTip(const QString &toolTip);
+#endif
+
+#ifndef QT_NO_CURSOR
+ QCursor cursor() const;
+ void setCursor(const QCursor &cursor);
+ bool hasCursor() const;
+ void unsetCursor();
+#endif
+
+ bool isVisible() const;
+ bool isVisibleTo(const QGraphicsItem *parent) const;
+ void setVisible(bool visible);
+ inline void hide() { setVisible(false); }
+ inline void show() { setVisible(true); }
+
+ bool isEnabled() const;
+ void setEnabled(bool enabled);
+
+ bool isSelected() const;
+ void setSelected(bool selected);
+
+ bool acceptDrops() const;
+ void setAcceptDrops(bool on);
+
+ qreal opacity() const;
+ qreal effectiveOpacity() const;
+ void setOpacity(qreal opacity);
+
+ Qt::MouseButtons acceptedMouseButtons() const;
+ void setAcceptedMouseButtons(Qt::MouseButtons buttons);
+
+ bool acceptsHoverEvents() const; // obsolete
+ void setAcceptsHoverEvents(bool enabled); // obsolete
+ bool acceptHoverEvents() const;
+ void setAcceptHoverEvents(bool enabled);
+
+ bool handlesChildEvents() const;
+ void setHandlesChildEvents(bool enabled);
+
+ bool hasFocus() const;
+ void setFocus(Qt::FocusReason focusReason = Qt::OtherFocusReason);
+ void clearFocus();
+
+ void grabMouse();
+ void ungrabMouse();
+ void grabKeyboard();
+ void ungrabKeyboard();
+
+ // Positioning in scene coordinates
+ QPointF pos() const;
+ inline qreal x() const { return pos().x(); }
+ inline qreal y() const { return pos().y(); }
+ QPointF scenePos() const;
+ void setPos(const QPointF &pos);
+ inline void setPos(qreal x, qreal y);
+ inline void moveBy(qreal dx, qreal dy) { setPos(pos().x() + dx, pos().y() + dy); }
+
+ void ensureVisible(const QRectF &rect = QRectF(), int xmargin = 50, int ymargin = 50);
+ inline void ensureVisible(qreal x, qreal y, qreal w, qreal h, int xmargin = 50, int ymargin = 50);
+
+ // Local transformation
+ QMatrix matrix() const;
+ QMatrix sceneMatrix() const;
+ void setMatrix(const QMatrix &matrix, bool combine = false);
+ void resetMatrix();
+ QTransform transform() const;
+ QTransform sceneTransform() const;
+ QTransform deviceTransform(const QTransform &viewportTransform) const;
+ QTransform itemTransform(const QGraphicsItem *other, bool *ok = 0) const;
+ void setTransform(const QTransform &matrix, bool combine = false);
+ void resetTransform();
+
+ void rotate(qreal angle);
+ void scale(qreal sx, qreal sy);
+ void shear(qreal sh, qreal sv);
+ void translate(qreal dx, qreal dy);
+ virtual void advance(int phase);
+
+ // Stacking order
+ qreal zValue() const;
+ void setZValue(qreal z);
+
+ // Hit test
+ virtual QRectF boundingRect() const = 0;
+ QRectF childrenBoundingRect() const;
+ QRectF sceneBoundingRect() const;
+ virtual QPainterPath shape() const;
+ bool isClipped() const;
+ QPainterPath clipPath() const;
+ virtual bool contains(const QPointF &point) const;
+ virtual bool collidesWithItem(const QGraphicsItem *other, Qt::ItemSelectionMode mode = Qt::IntersectsItemShape) const;
+ virtual bool collidesWithPath(const QPainterPath &path, Qt::ItemSelectionMode mode = Qt::IntersectsItemShape) const;
+ QList<QGraphicsItem *> collidingItems(Qt::ItemSelectionMode mode = Qt::IntersectsItemShape) const;
+ bool isObscured() const;
+ bool isObscured(const QRectF &rect) const; // ### Qt 5: merge with isObscured(), add QRectF arg to isObscuredBy()
+ inline bool isObscured(qreal x, qreal y, qreal w, qreal h) const;
+ virtual bool isObscuredBy(const QGraphicsItem *item) const;
+ virtual QPainterPath opaqueArea() const;
+
+ QRegion boundingRegion(const QTransform &itemToDeviceTransform) const;
+ qreal boundingRegionGranularity() const;
+ void setBoundingRegionGranularity(qreal granularity);
+
+ // Drawing
+ virtual void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = 0) = 0;
+ void update(const QRectF &rect = QRectF());
+ inline void update(qreal x, qreal y, qreal width, qreal height);
+ void scroll(qreal dx, qreal dy, const QRectF &rect = QRectF());
+
+ // Coordinate mapping
+ QPointF mapToItem(const QGraphicsItem *item, const QPointF &point) const;
+ QPointF mapToParent(const QPointF &point) const;
+ QPointF mapToScene(const QPointF &point) const;
+ QPolygonF mapToItem(const QGraphicsItem *item, const QRectF &rect) const;
+ QPolygonF mapToParent(const QRectF &rect) const;
+ QPolygonF mapToScene(const QRectF &rect) const;
+ QRectF mapRectToItem(const QGraphicsItem *item, const QRectF &rect) const;
+ QRectF mapRectToParent(const QRectF &rect) const;
+ QRectF mapRectToScene(const QRectF &rect) const;
+ QPolygonF mapToItem(const QGraphicsItem *item, const QPolygonF &polygon) const;
+ QPolygonF mapToParent(const QPolygonF &polygon) const;
+ QPolygonF mapToScene(const QPolygonF &polygon) const;
+ QPainterPath mapToItem(const QGraphicsItem *item, const QPainterPath &path) const;
+ QPainterPath mapToParent(const QPainterPath &path) const;
+ QPainterPath mapToScene(const QPainterPath &path) const;
+ QPointF mapFromItem(const QGraphicsItem *item, const QPointF &point) const;
+ QPointF mapFromParent(const QPointF &point) const;
+ QPointF mapFromScene(const QPointF &point) const;
+ QPolygonF mapFromItem(const QGraphicsItem *item, const QRectF &rect) const;
+ QPolygonF mapFromParent(const QRectF &rect) const;
+ QPolygonF mapFromScene(const QRectF &rect) const;
+ QRectF mapRectFromItem(const QGraphicsItem *item, const QRectF &rect) const;
+ QRectF mapRectFromParent(const QRectF &rect) const;
+ QRectF mapRectFromScene(const QRectF &rect) const;
+ QPolygonF mapFromItem(const QGraphicsItem *item, const QPolygonF &polygon) const;
+ QPolygonF mapFromParent(const QPolygonF &polygon) const;
+ QPolygonF mapFromScene(const QPolygonF &polygon) const;
+ QPainterPath mapFromItem(const QGraphicsItem *item, const QPainterPath &path) const;
+ QPainterPath mapFromParent(const QPainterPath &path) const;
+ QPainterPath mapFromScene(const QPainterPath &path) const;
+
+ inline QPointF mapToItem(const QGraphicsItem *item, qreal x, qreal y) const;
+ inline QPointF mapToParent(qreal x, qreal y) const;
+ inline QPointF mapToScene(qreal x, qreal y) const;
+ inline QPolygonF mapToItem(const QGraphicsItem *item, qreal x, qreal y, qreal w, qreal h) const;
+ inline QPolygonF mapToParent(qreal x, qreal y, qreal w, qreal h) const;
+ inline QPolygonF mapToScene(qreal x, qreal y, qreal w, qreal h) const;
+ inline QRectF mapRectToItem(const QGraphicsItem *item, qreal x, qreal y, qreal w, qreal h) const;
+ inline QRectF mapRectToParent(qreal x, qreal y, qreal w, qreal h) const;
+ inline QRectF mapRectToScene(qreal x, qreal y, qreal w, qreal h) const;
+ inline QPointF mapFromItem(const QGraphicsItem *item, qreal x, qreal y) const;
+ inline QPointF mapFromParent(qreal x, qreal y) const;
+ inline QPointF mapFromScene(qreal x, qreal y) const;
+ inline QPolygonF mapFromItem(const QGraphicsItem *item, qreal x, qreal y, qreal w, qreal h) const;
+ inline QPolygonF mapFromParent(qreal x, qreal y, qreal w, qreal h) const;
+ inline QPolygonF mapFromScene(qreal x, qreal y, qreal w, qreal h) const;
+ inline QRectF mapRectFromItem(const QGraphicsItem *item, qreal x, qreal y, qreal w, qreal h) const;
+ inline QRectF mapRectFromParent(qreal x, qreal y, qreal w, qreal h) const;
+ inline QRectF mapRectFromScene(qreal x, qreal y, qreal w, qreal h) const;
+
+ bool isAncestorOf(const QGraphicsItem *child) const;
+ QGraphicsItem *commonAncestorItem(const QGraphicsItem *other) const;
+ bool isUnderMouse() const;
+
+ // Custom data
+ QVariant data(int key) const;
+ void setData(int key, const QVariant &value);
+
+ enum {
+ Type = 1,
+ UserType = 65536
+ };
+ virtual int type() const;
+
+ void installSceneEventFilter(QGraphicsItem *filterItem);
+ void removeSceneEventFilter(QGraphicsItem *filterItem);
+
+protected:
+ virtual bool sceneEventFilter(QGraphicsItem *watched, QEvent *event);
+ virtual bool sceneEvent(QEvent *event);
+ virtual void contextMenuEvent(QGraphicsSceneContextMenuEvent *event);
+ virtual void dragEnterEvent(QGraphicsSceneDragDropEvent *event);
+ virtual void dragLeaveEvent(QGraphicsSceneDragDropEvent *event);
+ virtual void dragMoveEvent(QGraphicsSceneDragDropEvent *event);
+ virtual void dropEvent(QGraphicsSceneDragDropEvent *event);
+ virtual void focusInEvent(QFocusEvent *event);
+ virtual void focusOutEvent(QFocusEvent *event);
+ virtual void hoverEnterEvent(QGraphicsSceneHoverEvent *event);
+ virtual void hoverMoveEvent(QGraphicsSceneHoverEvent *event);
+ virtual void hoverLeaveEvent(QGraphicsSceneHoverEvent *event);
+ virtual void keyPressEvent(QKeyEvent *event);
+ virtual void keyReleaseEvent(QKeyEvent *event);
+ virtual void mousePressEvent(QGraphicsSceneMouseEvent *event);
+ virtual void mouseMoveEvent(QGraphicsSceneMouseEvent *event);
+ virtual void mouseReleaseEvent(QGraphicsSceneMouseEvent *event);
+ virtual void mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event);
+ virtual void wheelEvent(QGraphicsSceneWheelEvent *event);
+ virtual void inputMethodEvent(QInputMethodEvent *event);
+ virtual QVariant inputMethodQuery(Qt::InputMethodQuery query) const;
+
+ virtual QVariant itemChange(GraphicsItemChange change, const QVariant &value);
+
+ enum Extension {
+ UserExtension = 0x80000000
+ };
+ virtual bool supportsExtension(Extension extension) const;
+ virtual void setExtension(Extension extension, const QVariant &variant);
+ virtual QVariant extension(const QVariant &variant) const;
+
+protected:
+ QGraphicsItem(QGraphicsItemPrivate &dd,
+ QGraphicsItem *parent, QGraphicsScene *scene);
+ QGraphicsItemPrivate *d_ptr;
+
+ void addToIndex();
+ void removeFromIndex();
+ void prepareGeometryChange();
+
+private:
+ Q_DISABLE_COPY(QGraphicsItem)
+ Q_DECLARE_PRIVATE(QGraphicsItem)
+ friend class QGraphicsItemGroup;
+ friend class QGraphicsScene;
+ friend class QGraphicsScenePrivate;
+ friend class QGraphicsSceneFindItemBspTreeVisitor;
+ friend class QGraphicsView;
+ friend class QGraphicsViewPrivate;
+ friend class QGraphicsWidget;
+ friend class QGraphicsWidgetPrivate;
+ friend class QGraphicsProxyWidgetPrivate;
+ friend class ::tst_QGraphicsItem;
+ friend bool qt_closestLeaf(const QGraphicsItem *, const QGraphicsItem *);
+ friend bool qt_closestItemFirst(const QGraphicsItem *, const QGraphicsItem *);
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(QGraphicsItem::GraphicsItemFlags)
+
+inline void QGraphicsItem::setPos(qreal ax, qreal ay)
+{ setPos(QPointF(ax, ay)); }
+inline void QGraphicsItem::ensureVisible(qreal ax, qreal ay, qreal w, qreal h, int xmargin, int ymargin)
+{ ensureVisible(QRectF(ax, ay, w, h), xmargin, ymargin); }
+inline void QGraphicsItem::update(qreal ax, qreal ay, qreal width, qreal height)
+{ update(QRectF(ax, ay, width, height)); }
+inline bool QGraphicsItem::isObscured(qreal ax, qreal ay, qreal w, qreal h) const
+{ return isObscured(QRectF(ax, ay, w, h)); }
+inline QPointF QGraphicsItem::mapToItem(const QGraphicsItem *item, qreal ax, qreal ay) const
+{ return mapToItem(item, QPointF(ax, ay)); }
+inline QPointF QGraphicsItem::mapToParent(qreal ax, qreal ay) const
+{ return mapToParent(QPointF(ax, ay)); }
+inline QPointF QGraphicsItem::mapToScene(qreal ax, qreal ay) const
+{ return mapToScene(QPointF(ax, ay)); }
+inline QPointF QGraphicsItem::mapFromItem(const QGraphicsItem *item, qreal ax, qreal ay) const
+{ return mapFromItem(item, QPointF(ax, ay)); }
+inline QPointF QGraphicsItem::mapFromParent(qreal ax, qreal ay) const
+{ return mapFromParent(QPointF(ax, ay)); }
+inline QPointF QGraphicsItem::mapFromScene(qreal ax, qreal ay) const
+{ return mapFromScene(QPointF(ax, ay)); }
+inline QPolygonF QGraphicsItem::mapToItem(const QGraphicsItem *item, qreal ax, qreal ay, qreal w, qreal h) const
+{ return mapToItem(item, QRectF(ax, ay, w, h)); }
+inline QPolygonF QGraphicsItem::mapToParent(qreal ax, qreal ay, qreal w, qreal h) const
+{ return mapToParent(QRectF(ax, ay, w, h)); }
+inline QPolygonF QGraphicsItem::mapToScene(qreal ax, qreal ay, qreal w, qreal h) const
+{ return mapToScene(QRectF(ax, ay, w, h)); }
+inline QRectF QGraphicsItem::mapRectToItem(const QGraphicsItem *item, qreal ax, qreal ay, qreal w, qreal h) const
+{ return mapRectToItem(item, QRectF(ax, ay, w, h)); }
+inline QRectF QGraphicsItem::mapRectToParent(qreal ax, qreal ay, qreal w, qreal h) const
+{ return mapRectToParent(QRectF(ax, ay, w, h)); }
+inline QRectF QGraphicsItem::mapRectToScene(qreal ax, qreal ay, qreal w, qreal h) const
+{ return mapRectToScene(QRectF(ax, ay, w, h)); }
+inline QPolygonF QGraphicsItem::mapFromItem(const QGraphicsItem *item, qreal ax, qreal ay, qreal w, qreal h) const
+{ return mapFromItem(item, QRectF(ax, ay, w, h)); }
+inline QPolygonF QGraphicsItem::mapFromParent(qreal ax, qreal ay, qreal w, qreal h) const
+{ return mapFromParent(QRectF(ax, ay, w, h)); }
+inline QPolygonF QGraphicsItem::mapFromScene(qreal ax, qreal ay, qreal w, qreal h) const
+{ return mapFromScene(QRectF(ax, ay, w, h)); }
+inline QRectF QGraphicsItem::mapRectFromItem(const QGraphicsItem *item, qreal ax, qreal ay, qreal w, qreal h) const
+{ return mapRectFromItem(item, QRectF(ax, ay, w, h)); }
+inline QRectF QGraphicsItem::mapRectFromParent(qreal ax, qreal ay, qreal w, qreal h) const
+{ return mapRectFromParent(QRectF(ax, ay, w, h)); }
+inline QRectF QGraphicsItem::mapRectFromScene(qreal ax, qreal ay, qreal w, qreal h) const
+{ return mapRectFromScene(QRectF(ax, ay, w, h)); }
+
+class QAbstractGraphicsShapeItemPrivate;
+class Q_GUI_EXPORT QAbstractGraphicsShapeItem : public QGraphicsItem
+{
+public:
+ QAbstractGraphicsShapeItem(QGraphicsItem *parent = 0
+#ifndef Q_QDOC
+ // obsolete argument
+ , QGraphicsScene *scene = 0
+#endif
+ );
+ ~QAbstractGraphicsShapeItem();
+
+ QPen pen() const;
+ void setPen(const QPen &pen);
+
+ QBrush brush() const;
+ void setBrush(const QBrush &brush);
+
+ bool isObscuredBy(const QGraphicsItem *item) const;
+ QPainterPath opaqueArea() const;
+
+protected:
+ QAbstractGraphicsShapeItem(QAbstractGraphicsShapeItemPrivate &dd,
+ QGraphicsItem *parent, QGraphicsScene *scene);
+
+private:
+ Q_DISABLE_COPY(QAbstractGraphicsShapeItem)
+ Q_DECLARE_PRIVATE(QAbstractGraphicsShapeItem)
+};
+
+class QGraphicsPathItemPrivate;
+class Q_GUI_EXPORT QGraphicsPathItem : public QAbstractGraphicsShapeItem
+{
+public:
+ QGraphicsPathItem(QGraphicsItem *parent = 0
+#ifndef Q_QDOC
+ // obsolete argument
+ , QGraphicsScene *scene = 0
+#endif
+ );
+ QGraphicsPathItem(const QPainterPath &path, QGraphicsItem *parent = 0
+#ifndef Q_QDOC
+ // obsolete argument
+ , QGraphicsScene *scene = 0
+#endif
+ );
+ ~QGraphicsPathItem();
+
+ QPainterPath path() const;
+ void setPath(const QPainterPath &path);
+
+ QRectF boundingRect() const;
+ QPainterPath shape() const;
+ bool contains(const QPointF &point) const;
+
+ void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = 0);
+
+ bool isObscuredBy(const QGraphicsItem *item) const;
+ QPainterPath opaqueArea() const;
+
+ enum { Type = 2 };
+ int type() const;
+
+protected:
+ bool supportsExtension(Extension extension) const;
+ void setExtension(Extension extension, const QVariant &variant);
+ QVariant extension(const QVariant &variant) const;
+
+private:
+ Q_DISABLE_COPY(QGraphicsPathItem)
+ Q_DECLARE_PRIVATE(QGraphicsPathItem)
+};
+
+class QGraphicsRectItemPrivate;
+class Q_GUI_EXPORT QGraphicsRectItem : public QAbstractGraphicsShapeItem
+{
+public:
+ QGraphicsRectItem(QGraphicsItem *parent = 0
+#ifndef Q_QDOC
+ // obsolete argument
+ , QGraphicsScene *scene = 0
+#endif
+ );
+ QGraphicsRectItem(const QRectF &rect, QGraphicsItem *parent = 0
+#ifndef Q_QDOC
+ // obsolete argument
+ , QGraphicsScene *scene = 0
+#endif
+ );
+ QGraphicsRectItem(qreal x, qreal y, qreal w, qreal h, QGraphicsItem *parent = 0
+#ifndef Q_QDOC
+ // obsolete argument
+ , QGraphicsScene *scene = 0
+#endif
+ );
+ ~QGraphicsRectItem();
+
+ QRectF rect() const;
+ void setRect(const QRectF &rect);
+ inline void setRect(qreal x, qreal y, qreal w, qreal h);
+
+ QRectF boundingRect() const;
+ QPainterPath shape() const;
+ bool contains(const QPointF &point) const;
+
+ void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = 0);
+
+ bool isObscuredBy(const QGraphicsItem *item) const;
+ QPainterPath opaqueArea() const;
+
+ enum { Type = 3 };
+ int type() const;
+
+protected:
+ bool supportsExtension(Extension extension) const;
+ void setExtension(Extension extension, const QVariant &variant);
+ QVariant extension(const QVariant &variant) const;
+
+private:
+ Q_DISABLE_COPY(QGraphicsRectItem)
+ Q_DECLARE_PRIVATE(QGraphicsRectItem)
+};
+
+inline void QGraphicsRectItem::setRect(qreal ax, qreal ay, qreal w, qreal h)
+{ setRect(QRectF(ax, ay, w, h)); }
+
+class QGraphicsEllipseItemPrivate;
+class Q_GUI_EXPORT QGraphicsEllipseItem : public QAbstractGraphicsShapeItem
+{
+public:
+ QGraphicsEllipseItem(QGraphicsItem *parent = 0
+#ifndef Q_QDOC
+ // obsolete argument
+ , QGraphicsScene *scene = 0
+#endif
+ );
+ QGraphicsEllipseItem(const QRectF &rect, QGraphicsItem *parent = 0
+#ifndef Q_QDOC
+ // obsolete argument
+ , QGraphicsScene *scene = 0
+#endif
+ );
+ QGraphicsEllipseItem(qreal x, qreal y, qreal w, qreal h, QGraphicsItem *parent = 0
+#ifndef Q_QDOC
+ // obsolete argument
+ , QGraphicsScene *scene = 0
+#endif
+ );
+ ~QGraphicsEllipseItem();
+
+ QRectF rect() const;
+ void setRect(const QRectF &rect);
+ inline void setRect(qreal x, qreal y, qreal w, qreal h);
+
+ int startAngle() const;
+ void setStartAngle(int angle);
+
+ int spanAngle() const;
+ void setSpanAngle(int angle);
+
+ QRectF boundingRect() const;
+ QPainterPath shape() const;
+ bool contains(const QPointF &point) const;
+
+ void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = 0);
+
+ bool isObscuredBy(const QGraphicsItem *item) const;
+ QPainterPath opaqueArea() const;
+
+ enum { Type = 4 };
+ int type() const;
+
+protected:
+ bool supportsExtension(Extension extension) const;
+ void setExtension(Extension extension, const QVariant &variant);
+ QVariant extension(const QVariant &variant) const;
+
+private:
+ Q_DISABLE_COPY(QGraphicsEllipseItem)
+ Q_DECLARE_PRIVATE(QGraphicsEllipseItem)
+};
+
+inline void QGraphicsEllipseItem::setRect(qreal ax, qreal ay, qreal w, qreal h)
+{ setRect(QRectF(ax, ay, w, h)); }
+
+class QGraphicsPolygonItemPrivate;
+class Q_GUI_EXPORT QGraphicsPolygonItem : public QAbstractGraphicsShapeItem
+{
+public:
+ QGraphicsPolygonItem(QGraphicsItem *parent = 0
+#ifndef Q_QDOC
+ // obsolete argument
+ , QGraphicsScene *scene = 0
+#endif
+ );
+ QGraphicsPolygonItem(const QPolygonF &polygon,
+ QGraphicsItem *parent = 0
+#ifndef Q_QDOC
+ // obsolete argument
+ , QGraphicsScene *scene = 0
+#endif
+ );
+ ~QGraphicsPolygonItem();
+
+ QPolygonF polygon() const;
+ void setPolygon(const QPolygonF &polygon);
+
+ Qt::FillRule fillRule() const;
+ void setFillRule(Qt::FillRule rule);
+
+ QRectF boundingRect() const;
+ QPainterPath shape() const;
+ bool contains(const QPointF &point) const;
+
+ void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = 0);
+
+ bool isObscuredBy(const QGraphicsItem *item) const;
+ QPainterPath opaqueArea() const;
+
+ enum { Type = 5 };
+ int type() const;
+
+protected:
+ bool supportsExtension(Extension extension) const;
+ void setExtension(Extension extension, const QVariant &variant);
+ QVariant extension(const QVariant &variant) const;
+
+private:
+ Q_DISABLE_COPY(QGraphicsPolygonItem)
+ Q_DECLARE_PRIVATE(QGraphicsPolygonItem)
+};
+
+class QGraphicsLineItemPrivate;
+class Q_GUI_EXPORT QGraphicsLineItem : public QGraphicsItem
+{
+public:
+ QGraphicsLineItem(QGraphicsItem *parent = 0
+#ifndef Q_QDOC
+ // obsolete argument
+ , QGraphicsScene *scene = 0
+#endif
+ );
+ QGraphicsLineItem(const QLineF &line, QGraphicsItem *parent = 0
+#ifndef Q_QDOC
+ // obsolete argument
+ , QGraphicsScene *scene = 0
+#endif
+ );
+ QGraphicsLineItem(qreal x1, qreal y1, qreal x2, qreal y2, QGraphicsItem *parent = 0
+#ifndef Q_QDOC
+ // obsolete argument
+ , QGraphicsScene *scene = 0
+#endif
+ );
+ ~QGraphicsLineItem();
+
+ QPen pen() const;
+ void setPen(const QPen &pen);
+
+ QLineF line() const;
+ void setLine(const QLineF &line);
+ inline void setLine(qreal x1, qreal y1, qreal x2, qreal y2)
+ { setLine(QLineF(x1, y1, x2, y2)); }
+
+ QRectF boundingRect() const;
+ QPainterPath shape() const;
+ bool contains(const QPointF &point) const;
+
+ void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = 0);
+
+ bool isObscuredBy(const QGraphicsItem *item) const;
+ QPainterPath opaqueArea() const;
+
+ enum { Type = 6 };
+ int type() const;
+
+protected:
+ bool supportsExtension(Extension extension) const;
+ void setExtension(Extension extension, const QVariant &variant);
+ QVariant extension(const QVariant &variant) const;
+
+private:
+ Q_DISABLE_COPY(QGraphicsLineItem)
+ Q_DECLARE_PRIVATE(QGraphicsLineItem)
+};
+
+class QGraphicsPixmapItemPrivate;
+class Q_GUI_EXPORT QGraphicsPixmapItem : public QGraphicsItem
+{
+public:
+ enum ShapeMode {
+ MaskShape,
+ BoundingRectShape,
+ HeuristicMaskShape
+ };
+
+ QGraphicsPixmapItem(QGraphicsItem *parent = 0
+#ifndef Q_QDOC
+ // obsolete argument
+ , QGraphicsScene *scene = 0
+#endif
+ );
+ QGraphicsPixmapItem(const QPixmap &pixmap, QGraphicsItem *parent = 0
+#ifndef Q_QDOC
+ // obsolete argument
+ , QGraphicsScene *scene = 0
+#endif
+ );
+ ~QGraphicsPixmapItem();
+
+ QPixmap pixmap() const;
+ void setPixmap(const QPixmap &pixmap);
+
+ Qt::TransformationMode transformationMode() const;
+ void setTransformationMode(Qt::TransformationMode mode);
+
+ QPointF offset() const;
+ void setOffset(const QPointF &offset);
+ inline void setOffset(qreal x, qreal y);
+
+ QRectF boundingRect() const;
+ QPainterPath shape() const;
+ bool contains(const QPointF &point) const;
+
+ void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget);
+
+ bool isObscuredBy(const QGraphicsItem *item) const;
+ QPainterPath opaqueArea() const;
+
+ enum { Type = 7 };
+ int type() const;
+
+ ShapeMode shapeMode() const;
+ void setShapeMode(ShapeMode mode);
+
+protected:
+ bool supportsExtension(Extension extension) const;
+ void setExtension(Extension extension, const QVariant &variant);
+ QVariant extension(const QVariant &variant) const;
+
+private:
+ Q_DISABLE_COPY(QGraphicsPixmapItem)
+ Q_DECLARE_PRIVATE(QGraphicsPixmapItem)
+};
+
+inline void QGraphicsPixmapItem::setOffset(qreal ax, qreal ay)
+{ setOffset(QPointF(ax, ay)); }
+
+class QGraphicsTextItemPrivate;
+class QTextDocument;
+class QTextCursor;
+class Q_GUI_EXPORT QGraphicsTextItem : public QObject, public QGraphicsItem
+{
+ Q_OBJECT
+ QDOC_PROPERTY(bool openExternalLinks READ openExternalLinks WRITE setOpenExternalLinks)
+ QDOC_PROPERTY(QTextCursor textCursor READ textCursor WRITE setTextCursor)
+
+public:
+ QGraphicsTextItem(QGraphicsItem *parent = 0
+#ifndef Q_QDOC
+ // obsolete argument
+ , QGraphicsScene *scene = 0
+#endif
+ );
+ QGraphicsTextItem(const QString &text, QGraphicsItem *parent = 0
+#ifndef Q_QDOC
+ // obsolete argument
+ , QGraphicsScene *scene = 0
+#endif
+ );
+ ~QGraphicsTextItem();
+
+ QString toHtml() const;
+ void setHtml(const QString &html);
+
+ QString toPlainText() const;
+ void setPlainText(const QString &text);
+
+ QFont font() const;
+ void setFont(const QFont &font);
+
+ void setDefaultTextColor(const QColor &c);
+ QColor defaultTextColor() const;
+
+ QRectF boundingRect() const;
+ QPainterPath shape() const;
+ bool contains(const QPointF &point) const;
+
+ void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget);
+
+ bool isObscuredBy(const QGraphicsItem *item) const;
+ QPainterPath opaqueArea() const;
+
+ enum { Type = 8 };
+ int type() const;
+
+ void setTextWidth(qreal width);
+ qreal textWidth() const;
+
+ void adjustSize();
+
+ void setDocument(QTextDocument *document);
+ QTextDocument *document() const;
+
+ void setTextInteractionFlags(Qt::TextInteractionFlags flags);
+ Qt::TextInteractionFlags textInteractionFlags() const;
+
+ void setTabChangesFocus(bool b);
+ bool tabChangesFocus() const;
+
+ void setOpenExternalLinks(bool open);
+ bool openExternalLinks() const;
+
+ void setTextCursor(const QTextCursor &cursor);
+ QTextCursor textCursor() const;
+
+Q_SIGNALS:
+ void linkActivated(const QString &);
+ void linkHovered(const QString &);
+
+protected:
+ bool sceneEvent(QEvent *event);
+ void mousePressEvent(QGraphicsSceneMouseEvent *event);
+ void mouseMoveEvent(QGraphicsSceneMouseEvent *event);
+ void mouseReleaseEvent(QGraphicsSceneMouseEvent *event);
+ void mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event);
+ void contextMenuEvent(QGraphicsSceneContextMenuEvent *event);
+ void keyPressEvent(QKeyEvent *event);
+ void keyReleaseEvent(QKeyEvent *event);
+ void focusInEvent(QFocusEvent *event);
+ void focusOutEvent(QFocusEvent *event);
+ void dragEnterEvent(QGraphicsSceneDragDropEvent *event);
+ void dragLeaveEvent(QGraphicsSceneDragDropEvent *event);
+ void dragMoveEvent(QGraphicsSceneDragDropEvent *event);
+ void dropEvent(QGraphicsSceneDragDropEvent *event);
+ void inputMethodEvent(QInputMethodEvent *event);
+ void hoverEnterEvent(QGraphicsSceneHoverEvent *event);
+ void hoverMoveEvent(QGraphicsSceneHoverEvent *event);
+ void hoverLeaveEvent(QGraphicsSceneHoverEvent *event);
+
+ QVariant inputMethodQuery(Qt::InputMethodQuery query) const;
+
+ bool supportsExtension(Extension extension) const;
+ void setExtension(Extension extension, const QVariant &variant);
+ QVariant extension(const QVariant &variant) const;
+
+private:
+ Q_DISABLE_COPY(QGraphicsTextItem)
+ Q_PRIVATE_SLOT(dd, void _q_updateBoundingRect(const QSizeF &))
+ Q_PRIVATE_SLOT(dd, void _q_update(QRectF))
+ Q_PRIVATE_SLOT(dd, void _q_ensureVisible(QRectF))
+ QGraphicsTextItemPrivate *dd;
+ friend class QGraphicsTextItemPrivate;
+};
+
+class QGraphicsSimpleTextItemPrivate;
+class Q_GUI_EXPORT QGraphicsSimpleTextItem : public QAbstractGraphicsShapeItem
+{
+public:
+ QGraphicsSimpleTextItem(QGraphicsItem *parent = 0
+#ifndef Q_QDOC
+ // obsolete argument
+ , QGraphicsScene *scene = 0
+#endif
+ );
+ QGraphicsSimpleTextItem(const QString &text, QGraphicsItem *parent = 0
+#ifndef Q_QDOC
+ // obsolete argument
+ , QGraphicsScene *scene = 0
+#endif
+ );
+ ~QGraphicsSimpleTextItem();
+
+ void setText(const QString &text);
+ QString text() const;
+
+ void setFont(const QFont &font);
+ QFont font() const;
+
+ QRectF boundingRect() const;
+ QPainterPath shape() const;
+ bool contains(const QPointF &point) const;
+
+ void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget);
+
+ bool isObscuredBy(const QGraphicsItem *item) const;
+ QPainterPath opaqueArea() const;
+
+ enum { Type = 9 };
+ int type() const;
+
+protected:
+ bool supportsExtension(Extension extension) const;
+ void setExtension(Extension extension, const QVariant &variant);
+ QVariant extension(const QVariant &variant) const;
+
+private:
+ Q_DISABLE_COPY(QGraphicsSimpleTextItem)
+ Q_DECLARE_PRIVATE(QGraphicsSimpleTextItem)
+};
+
+class QGraphicsItemGroupPrivate;
+class Q_GUI_EXPORT QGraphicsItemGroup : public QGraphicsItem
+{
+public:
+ QGraphicsItemGroup(QGraphicsItem *parent = 0
+#ifndef Q_QDOC
+ // obsolete argument
+ , QGraphicsScene *scene = 0
+#endif
+ );
+ ~QGraphicsItemGroup();
+
+ void addToGroup(QGraphicsItem *item);
+ void removeFromGroup(QGraphicsItem *item);
+
+ QRectF boundingRect() const;
+ void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = 0);
+
+ bool isObscuredBy(const QGraphicsItem *item) const;
+ QPainterPath opaqueArea() const;
+
+ enum { Type = 10 };
+ int type() const;
+
+private:
+ Q_DISABLE_COPY(QGraphicsItemGroup)
+ Q_DECLARE_PRIVATE(QGraphicsItemGroup)
+};
+
+template <class T> inline T qgraphicsitem_cast(QGraphicsItem *item)
+{
+ return int(static_cast<T>(0)->Type) == int(QGraphicsItem::Type)
+ || (item && int(static_cast<T>(0)->Type) == item->type()) ? static_cast<T>(item) : 0;
+}
+
+template <class T> inline T qgraphicsitem_cast(const QGraphicsItem *item)
+{
+ return int(static_cast<T>(0)->Type) == int(QGraphicsItem::Type)
+ || (item && int(static_cast<T>(0)->Type) == item->type()) ? static_cast<T>(item) : 0;
+}
+
+#ifndef QT_NO_DEBUG_STREAM
+Q_GUI_EXPORT QDebug operator<<(QDebug debug, QGraphicsItem *item);
+Q_GUI_EXPORT QDebug operator<<(QDebug debug, QGraphicsItem::GraphicsItemChange change);
+Q_GUI_EXPORT QDebug operator<<(QDebug debug, QGraphicsItem::GraphicsItemFlag flag);
+Q_GUI_EXPORT QDebug operator<<(QDebug debug, QGraphicsItem::GraphicsItemFlags flags);
+#endif
+
+QT_END_NAMESPACE
+
+Q_DECLARE_METATYPE(QGraphicsItem *)
+Q_DECLARE_METATYPE(QGraphicsScene *)
+
+QT_BEGIN_NAMESPACE
+
+#endif // QT_NO_GRAPHICSVIEW
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QGRAPHICSITEM_H
+
diff --git a/src/gui/graphicsview/qgraphicsitem_p.h b/src/gui/graphicsview/qgraphicsitem_p.h
new file mode 100644
index 0000000000..07f6958c26
--- /dev/null
+++ b/src/gui/graphicsview/qgraphicsitem_p.h
@@ -0,0 +1,281 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QGRAPHICSITEM_P_H
+#define QGRAPHICSITEM_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of qapplication_*.cpp, qwidget*.cpp and qfiledialog.cpp. This header
+// file may change from version to version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qgraphicsitem.h"
+
+#if !defined(QT_NO_GRAPHICSVIEW) || (QT_EDITION & QT_MODULE_GRAPHICSVIEW) != QT_MODULE_GRAPHICSVIEW
+
+QT_BEGIN_NAMESPACE
+
+class QGraphicsItemPrivate;
+
+class QGraphicsItemCache
+{
+public:
+ QGraphicsItemCache() : allExposed(false) { }
+
+ // ItemCoordinateCache only
+ QRect boundingRect;
+ QSize fixedSize;
+ QString key;
+
+ // DeviceCoordinateCache only
+ struct DeviceData {
+ QTransform lastTransform;
+ QPoint cacheIndent;
+ QString key;
+ };
+ QMap<QPaintDevice *, DeviceData> deviceData;
+
+ // List of logical exposed rects
+ QVector<QRectF> exposed;
+ bool allExposed;
+
+ // Empty cache
+ void purge();
+};
+
+class Q_AUTOTEST_EXPORT QGraphicsItemPrivate
+{
+ Q_DECLARE_PUBLIC(QGraphicsItem)
+public:
+ enum Extra {
+ ExtraTransform,
+ ExtraToolTip,
+ ExtraCursor,
+ ExtraCacheData,
+ ExtraMaxDeviceCoordCacheSize,
+ ExtraBoundingRegionGranularity,
+ ExtraOpacity,
+ ExtraEffectiveOpacity
+ };
+
+ enum AncestorFlag {
+ NoFlag = 0,
+ AncestorHandlesChildEvents = 0x1,
+ AncestorClipsChildren = 0x2,
+ AncestorIgnoresTransformations = 0x4
+ };
+
+ inline QGraphicsItemPrivate()
+ : z(0),
+ scene(0),
+ parent(0),
+ index(-1),
+ depth(0),
+ acceptedMouseButtons(0x1f),
+ visible(1),
+ explicitlyHidden(0),
+ enabled(1),
+ explicitlyDisabled(0),
+ selected(0),
+ acceptsHover(0),
+ acceptDrops(0),
+ isMemberOfGroup(0),
+ handlesChildEvents(0),
+ itemDiscovered(0),
+ hasTransform(0),
+ hasCursor(0),
+ ancestorFlags(0),
+ cacheMode(0),
+ hasBoundingRegionGranularity(0),
+ flags(0),
+ hasOpacity(0),
+ isWidget(0),
+ dirty(0),
+ dirtyChildren(0),
+ localCollisionHack(0),
+ globalStackingOrder(-1),
+ sceneTransformIndex(-1),
+ q_ptr(0)
+ {
+ }
+
+ inline virtual ~QGraphicsItemPrivate()
+ { }
+
+ void updateAncestorFlag(QGraphicsItem::GraphicsItemFlag childFlag,
+ AncestorFlag flag = NoFlag, bool enabled = false, bool root = true);
+ void setIsMemberOfGroup(bool enabled);
+ void remapItemPos(QEvent *event, QGraphicsItem *item);
+ QPointF genericMapFromScene(const QPointF &pos, const QWidget *viewport) const;
+ bool itemIsUntransformable() const;
+
+ // ### Qt 5: Remove. Workaround for reimplementation added after Qt 4.4.
+ virtual QVariant inputMethodQueryHelper(Qt::InputMethodQuery query) const;
+ static bool movableAncestorIsSelected(const QGraphicsItem *item);
+
+ void setPosHelper(const QPointF &pos, bool update);
+ void setVisibleHelper(bool newVisible, bool explicitly, bool update = true);
+ void setEnabledHelper(bool newEnabled, bool explicitly, bool update = true);
+ void updateHelper(const QRectF &rect = QRectF(), bool force = false);
+ void fullUpdateHelper(bool childrenOnly = false);
+ void resolveEffectiveOpacity(qreal effectiveParentOpacity);
+ void resolveDepth(int parentDepth);
+ void invalidateSceneTransformCache();
+
+ virtual void resolveFont(uint inheritedMask)
+ {
+ for (int i = 0; i < children.size(); ++i)
+ children.at(i)->d_ptr->resolveFont(inheritedMask);
+ }
+
+ virtual void resolvePalette(uint inheritedMask)
+ {
+ for (int i = 0; i < children.size(); ++i)
+ children.at(i)->d_ptr->resolveFont(inheritedMask);
+ }
+
+ virtual bool isProxyWidget() const;
+
+ inline QVariant extra(Extra type) const
+ {
+ for (int i = 0; i < extras.size(); ++i) {
+ const ExtraStruct &extra = extras.at(i);
+ if (extra.type == type)
+ return extra.value;
+ }
+ return QVariant();
+ }
+
+ inline void setExtra(Extra type, const QVariant &value)
+ {
+ int index = -1;
+ for (int i = 0; i < extras.size(); ++i) {
+ if (extras.at(i).type == type) {
+ index = i;
+ break;
+ }
+ }
+
+ if (index == -1) {
+ extras << ExtraStruct(type, value);
+ } else {
+ extras[index].value = value;
+ }
+ }
+
+ inline void unsetExtra(Extra type)
+ {
+ for (int i = 0; i < extras.size(); ++i) {
+ if (extras.at(i).type == type) {
+ extras.removeAt(i);
+ return;
+ }
+ }
+ }
+
+ struct ExtraStruct {
+ ExtraStruct(Extra type, QVariant value)
+ : type(type), value(value)
+ { }
+
+ Extra type;
+ QVariant value;
+
+ bool operator<(Extra extra) const
+ { return type < extra; }
+ };
+ QList<ExtraStruct> extras;
+
+ QGraphicsItemCache *extraItemCache() const;
+ void removeExtraItemCache();
+
+ QPointF pos;
+ qreal z;
+ QGraphicsScene *scene;
+ QGraphicsItem *parent;
+ QList<QGraphicsItem *> children;
+ int index;
+ int depth;
+
+ // Packed 32 bytes
+ quint32 acceptedMouseButtons : 5;
+ quint32 visible : 1;
+ quint32 explicitlyHidden : 1;
+ quint32 enabled : 1;
+ quint32 explicitlyDisabled : 1;
+ quint32 selected : 1;
+ quint32 acceptsHover : 1;
+ quint32 acceptDrops : 1;
+ quint32 isMemberOfGroup : 1;
+ quint32 handlesChildEvents : 1;
+ quint32 itemDiscovered : 1;
+ quint32 hasTransform : 1;
+ quint32 hasCursor : 1;
+ quint32 ancestorFlags : 3;
+ quint32 cacheMode : 2;
+ quint32 hasBoundingRegionGranularity : 1;
+ quint32 flags : 9;
+
+ // New 32 bytes
+ quint32 hasOpacity : 1;
+ quint32 isWidget : 1;
+ quint32 dirty : 1;
+ quint32 dirtyChildren : 1;
+ quint32 localCollisionHack : 1;
+
+ // Optional stacking order
+ int globalStackingOrder;
+ int sceneTransformIndex;
+
+ QGraphicsItem *q_ptr;
+};
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_GRAPHICSVIEW
+
+#endif
diff --git a/src/gui/graphicsview/qgraphicsitemanimation.cpp b/src/gui/graphicsview/qgraphicsitemanimation.cpp
new file mode 100644
index 0000000000..1b58b9c384
--- /dev/null
+++ b/src/gui/graphicsview/qgraphicsitemanimation.cpp
@@ -0,0 +1,599 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*!
+ \class QGraphicsItemAnimation
+ \brief The QGraphicsItemAnimation class provides simple animation
+ support for QGraphicsItem.
+ \since 4.2
+ \ingroup multimedia
+ \ingroup graphicsview-api
+
+ The QGraphicsItemAnimation class animates a QGraphicsItem. You can
+ schedule changes to the item's transformation matrix at
+ specified steps. The QGraphicsItemAnimation class has a
+ current step value. When this value changes the transformations
+ scheduled at that step are performed. The current step of the
+ animation is set with the \c setStep() function.
+
+ QGraphicsItemAnimation will do a simple linear interpolation
+ between the nearest adjacent scheduled changes to calculate the
+ matrix. For instance, if you set the position of an item at values
+ 0.0 and 1.0, the animation will show the item moving in a straight
+ line between these positions. The same is true for scaling and
+ rotation.
+
+ It is usual to use the class with a QTimeLine. The timeline's
+ \l{QTimeLine::}{valueChanged()} signal is then connected to the
+ \c setStep() slot. For example, you can set up an item for rotation
+ by calling \c setRotationAt() for different step values.
+ The animations timeline is set with the setTimeLine() function.
+
+ An example animation with a timeline follows:
+
+ \snippet doc/src/snippets/timeline/main.cpp 0
+
+ Note that steps lie between 0.0 and 1.0. It may be necessary to use
+ \l{QTimeLine::}{setUpdateInterval()}. The default update interval
+ is 40 ms. A scheduled transformation cannot be removed when set,
+ so scheduling several transformations of the same kind (e.g.,
+ rotations) at the same step is not recommended.
+
+ \sa QTimeLine, {The Graphics View Framework}
+*/
+
+#include "qgraphicsitemanimation.h"
+
+#ifndef QT_NO_GRAPHICSVIEW
+
+#include "qgraphicsitem.h"
+
+#include <QtCore/qtimeline.h>
+#include <QtCore/qpoint.h>
+#include <QtCore/qpointer.h>
+#include <QtCore/qpair.h>
+#include <QtGui/qmatrix.h>
+
+QT_BEGIN_NAMESPACE
+
+class QGraphicsItemAnimationPrivate
+{
+public:
+ inline QGraphicsItemAnimationPrivate()
+ : q(0), timeLine(0), item(0), step(0)
+ { }
+
+ QGraphicsItemAnimation *q;
+
+ QPointer<QTimeLine> timeLine;
+ QGraphicsItem *item;
+
+ QPointF startPos;
+ QMatrix startMatrix;
+
+ qreal step;
+
+ struct Pair {
+ Pair(qreal a, qreal b) : step(a), value(b) {}
+ bool operator <(const Pair &other) const
+ { return step < other.step; }
+ bool operator==(const Pair &other) const
+ { return step == other.step; }
+ qreal step;
+ qreal value;
+ };
+ QList<Pair> xPosition;
+ QList<Pair> yPosition;
+ QList<Pair> rotation;
+ QList<Pair> verticalScale;
+ QList<Pair> horizontalScale;
+ QList<Pair> verticalShear;
+ QList<Pair> horizontalShear;
+ QList<Pair> xTranslation;
+ QList<Pair> yTranslation;
+
+ qreal linearValueForStep(qreal step, QList<Pair> *source, qreal defaultValue = 0);
+ void insertUniquePair(qreal step, qreal value, QList<Pair> *binList, const char* method);
+};
+
+qreal QGraphicsItemAnimationPrivate::linearValueForStep(qreal step, QList<Pair> *source, qreal defaultValue)
+{
+ if (source->isEmpty())
+ return defaultValue;
+ step = qMin<qreal>(qMax<qreal>(step, 0), 1);
+
+ if (step == 1)
+ return source->last().value;
+
+ qreal stepBefore = 0;
+ qreal stepAfter = 1;
+ qreal valueBefore = source->first().step == 0 ? source->first().value : defaultValue;
+ qreal valueAfter = source->last().value;
+
+ // Find the closest step and value before the given step.
+ for (int i = 0; i < source->size() && step >= source->at(i).step; ++i) {
+ stepBefore = source->at(i).step;
+ valueBefore = source->at(i).value;
+ }
+
+ // Find the closest step and value after the given step.
+ for (int j = source->size() - 1; j >= 0 && step < source->at(j).step; --j) {
+ stepAfter = source->at(j).step;
+ valueAfter = source->at(j).value;
+ }
+
+ // Do a simple linear interpolation.
+ return valueBefore + (valueAfter - valueBefore) * ((step - stepBefore) / (stepAfter - stepBefore));
+}
+
+void QGraphicsItemAnimationPrivate::insertUniquePair(qreal step, qreal value, QList<Pair> *binList, const char* method)
+{
+ if (step < 0.0 || step > 1.0) {
+ qWarning("QGraphicsItemAnimation::%s: invalid step = %f", method, step);
+ return;
+ }
+
+ Pair pair(step, value);
+
+ QList<Pair>::iterator result = qBinaryFind(binList->begin(), binList->end(), pair);
+ if (result != binList->end())
+ result->value = value;
+ else {
+ *binList << pair;
+ qSort(binList->begin(), binList->end());
+ }
+}
+
+/*!
+ Constructs an animation object with the given \a parent.
+*/
+QGraphicsItemAnimation::QGraphicsItemAnimation(QObject *parent)
+ : QObject(parent), d(new QGraphicsItemAnimationPrivate)
+{
+ d->q = this;
+}
+
+/*!
+ Destroys the animation object.
+*/
+QGraphicsItemAnimation::~QGraphicsItemAnimation()
+{
+ delete d;
+}
+
+/*!
+ Returns the item on which the animation object operates.
+
+ \sa setItem()
+*/
+QGraphicsItem *QGraphicsItemAnimation::item() const
+{
+ return d->item;
+}
+
+/*!
+ Sets the specified \a item to be used in the animation.
+
+ \sa item()
+*/
+void QGraphicsItemAnimation::setItem(QGraphicsItem *item)
+{
+ d->item = item;
+ d->startPos = d->item->pos();
+}
+
+/*!
+ Returns the timeline object used to control the rate at which the animation
+ occurs.
+
+ \sa setTimeLine()
+*/
+QTimeLine *QGraphicsItemAnimation::timeLine() const
+{
+ return d->timeLine;
+}
+
+/*!
+ Sets the timeline object used to control the rate of animation to the \a timeLine
+ specified.
+
+ \sa timeLine()
+*/
+void QGraphicsItemAnimation::setTimeLine(QTimeLine *timeLine)
+{
+ if (d->timeLine == timeLine)
+ return;
+ if (d->timeLine)
+ delete d->timeLine;
+ if (!timeLine)
+ return;
+ d->timeLine = timeLine;
+ connect(timeLine, SIGNAL(valueChanged(qreal)), this, SLOT(setStep(qreal)));
+}
+
+/*!
+ Returns the position of the item at the given \a step value.
+
+ \sa setPosAt()
+*/
+QPointF QGraphicsItemAnimation::posAt(qreal step) const
+{
+ if (step < 0.0 || step > 1.0)
+ qWarning("QGraphicsItemAnimation::posAt: invalid step = %f", step);
+
+ return QPointF(d->linearValueForStep(step, &d->xPosition, d->startPos.x()),
+ d->linearValueForStep(step, &d->yPosition, d->startPos.y()));
+}
+
+/*!
+ \fn void QGraphicsItemAnimation::setPosAt(qreal step, const QPointF &point)
+
+ Sets the position of the item at the given \a step value to the \a point specified.
+
+ \sa posAt()
+*/
+void QGraphicsItemAnimation::setPosAt(qreal step, const QPointF &pos)
+{
+ d->insertUniquePair(step, pos.x(), &d->xPosition, "setPosAt");
+ d->insertUniquePair(step, pos.y(), &d->yPosition, "setPosAt");
+}
+
+/*!
+ Returns all explicitly inserted positions.
+
+ \sa posAt(), setPosAt()
+*/
+QList<QPair<qreal, QPointF> > QGraphicsItemAnimation::posList() const
+{
+ QList<QPair<qreal, QPointF> > list;
+ for (int i = 0; i < d->xPosition.size(); ++i)
+ list << QPair<qreal, QPointF>(d->xPosition.at(i).step, QPointF(d->xPosition.at(i).value, d->yPosition.at(i).value));
+
+ return list;
+}
+
+/*!
+ Returns the matrix used to transform the item at the specified \a step value.
+*/
+QMatrix QGraphicsItemAnimation::matrixAt(qreal step) const
+{
+ if (step < 0.0 || step > 1.0)
+ qWarning("QGraphicsItemAnimation::matrixAt: invalid step = %f", step);
+
+ QMatrix matrix;
+ if (!d->rotation.isEmpty())
+ matrix.rotate(rotationAt(step));
+ if (!d->verticalScale.isEmpty())
+ matrix.scale(horizontalScaleAt(step), verticalScaleAt(step));
+ if (!d->verticalShear.isEmpty())
+ matrix.shear(horizontalShearAt(step), verticalShearAt(step));
+ if (!d->xTranslation.isEmpty())
+ matrix.translate(xTranslationAt(step), yTranslationAt(step));
+ return matrix;
+}
+
+/*!
+ Returns the angle at which the item is rotated at the specified \a step value.
+
+ \sa setRotationAt()
+*/
+qreal QGraphicsItemAnimation::rotationAt(qreal step) const
+{
+ if (step < 0.0 || step > 1.0)
+ qWarning("QGraphicsItemAnimation::rotationAt: invalid step = %f", step);
+
+ return d->linearValueForStep(step, &d->rotation);
+}
+
+/*!
+ Sets the rotation of the item at the given \a step value to the \a angle specified.
+
+ \sa rotationAt()
+*/
+void QGraphicsItemAnimation::setRotationAt(qreal step, qreal angle)
+{
+ d->insertUniquePair(step, angle, &d->rotation, "setRotationAt");
+}
+
+/*!
+ Returns all explicitly inserted rotations.
+
+ \sa rotationAt(), setRotationAt()
+*/
+QList<QPair<qreal, qreal> > QGraphicsItemAnimation::rotationList() const
+{
+ QList<QPair<qreal, qreal> > list;
+ for (int i = 0; i < d->rotation.size(); ++i)
+ list << QPair<qreal, qreal>(d->rotation.at(i).step, d->rotation.at(i).value);
+
+ return list;
+}
+
+/*!
+ Returns the horizontal translation of the item at the specified \a step value.
+
+ \sa setTranslationAt()
+*/
+qreal QGraphicsItemAnimation::xTranslationAt(qreal step) const
+{
+ if (step < 0.0 || step > 1.0)
+ qWarning("QGraphicsItemAnimation::xTranslationAt: invalid step = %f", step);
+
+ return d->linearValueForStep(step, &d->xTranslation);
+}
+
+/*!
+ Returns the vertical translation of the item at the specified \a step value.
+
+ \sa setTranslationAt()
+*/
+qreal QGraphicsItemAnimation::yTranslationAt(qreal step) const
+{
+ if (step < 0.0 || step > 1.0)
+ qWarning("QGraphicsItemAnimation::yTranslationAt: invalid step = %f", step);
+
+ return d->linearValueForStep(step, &d->yTranslation);
+}
+
+/*!
+ Sets the translation of the item at the given \a step value using the horizontal
+ and vertical coordinates specified by \a dx and \a dy.
+
+ \sa xTranslationAt(), yTranslationAt()
+*/
+void QGraphicsItemAnimation::setTranslationAt(qreal step, qreal dx, qreal dy)
+{
+ d->insertUniquePair(step, dx, &d->xTranslation, "setTranslationAt");
+ d->insertUniquePair(step, dy, &d->yTranslation, "setTranslationAt");
+}
+
+/*!
+ Returns all explicitly inserted translations.
+
+ \sa xTranslationAt(), yTranslationAt(), setTranslationAt()
+*/
+QList<QPair<qreal, QPointF> > QGraphicsItemAnimation::translationList() const
+{
+ QList<QPair<qreal, QPointF> > list;
+ for (int i = 0; i < d->xTranslation.size(); ++i)
+ list << QPair<qreal, QPointF>(d->xTranslation.at(i).step, QPointF(d->xTranslation.at(i).value, d->yTranslation.at(i).value));
+
+ return list;
+}
+
+/*!
+ Returns the vertical scale for the item at the specified \a step value.
+
+ \sa setScaleAt()
+*/
+qreal QGraphicsItemAnimation::verticalScaleAt(qreal step) const
+{
+ if (step < 0.0 || step > 1.0)
+ qWarning("QGraphicsItemAnimation::verticalScaleAt: invalid step = %f", step);
+
+ return d->linearValueForStep(step, &d->verticalScale, 1);
+}
+
+/*!
+ Returns the horizontal scale for the item at the specified \a step value.
+
+ \sa setScaleAt()
+*/
+qreal QGraphicsItemAnimation::horizontalScaleAt(qreal step) const
+{
+ if (step < 0.0 || step > 1.0)
+ qWarning("QGraphicsItemAnimation::horizontalScaleAt: invalid step = %f", step);
+
+ return d->linearValueForStep(step, &d->horizontalScale, 1);
+}
+
+/*!
+ Sets the scale of the item at the given \a step value using the horizontal and
+ vertical scale factors specified by \a sx and \a sy.
+
+ \sa verticalScaleAt(), horizontalScaleAt()
+*/
+void QGraphicsItemAnimation::setScaleAt(qreal step, qreal sx, qreal sy)
+{
+ d->insertUniquePair(step, sx, &d->horizontalScale, "setScaleAt");
+ d->insertUniquePair(step, sy, &d->verticalScale, "setScaleAt");
+}
+
+/*!
+ Returns all explicitly inserted scales.
+
+ \sa verticalScaleAt(), horizontalScaleAt(), setScaleAt()
+*/
+QList<QPair<qreal, QPointF> > QGraphicsItemAnimation::scaleList() const
+{
+ QList<QPair<qreal, QPointF> > list;
+ for (int i = 0; i < d->horizontalScale.size(); ++i)
+ list << QPair<qreal, QPointF>(d->horizontalScale.at(i).step, QPointF(d->horizontalScale.at(i).value, d->verticalScale.at(i).value));
+
+ return list;
+}
+
+/*!
+ Returns the vertical shear for the item at the specified \a step value.
+
+ \sa setShearAt()
+*/
+qreal QGraphicsItemAnimation::verticalShearAt(qreal step) const
+{
+ if (step < 0.0 || step > 1.0)
+ qWarning("QGraphicsItemAnimation::verticalShearAt: invalid step = %f", step);
+
+ return d->linearValueForStep(step, &d->verticalShear, 0);
+}
+
+/*!
+ Returns the horizontal shear for the item at the specified \a step value.
+
+ \sa setShearAt()
+*/
+qreal QGraphicsItemAnimation::horizontalShearAt(qreal step) const
+{
+ if (step < 0.0 || step > 1.0)
+ qWarning("QGraphicsItemAnimation::horizontalShearAt: invalid step = %f", step);
+
+ return d->linearValueForStep(step, &d->horizontalShear, 0);
+}
+
+/*!
+ Sets the shear of the item at the given \a step value using the horizontal and
+ vertical shear factors specified by \a sh and \a sv.
+
+ \sa verticalShearAt(), horizontalShearAt()
+*/
+void QGraphicsItemAnimation::setShearAt(qreal step, qreal sh, qreal sv)
+{
+ d->insertUniquePair(step, sh, &d->horizontalShear, "setShearAt");
+ d->insertUniquePair(step, sv, &d->verticalShear, "setShearAt");
+}
+
+/*!
+ Returns all explicitly inserted shears.
+
+ \sa verticalShearAt(), horizontalShearAt(), setShearAt()
+*/
+QList<QPair<qreal, QPointF> > QGraphicsItemAnimation::shearList() const
+{
+ QList<QPair<qreal, QPointF> > list;
+ for (int i = 0; i < d->horizontalShear.size(); ++i)
+ list << QPair<qreal, QPointF>(d->horizontalShear.at(i).step, QPointF(d->horizontalShear.at(i).value, d->verticalShear.at(i).value));
+
+ return list;
+}
+
+/*!
+ Clears the scheduled transformations used for the animation, but
+ retains the item and timeline.
+*/
+void QGraphicsItemAnimation::clear()
+{
+ d->xPosition.clear();
+ d->yPosition.clear();
+ d->rotation.clear();
+ d->verticalScale.clear();
+ d->horizontalScale.clear();
+ d->verticalShear.clear();
+ d->horizontalShear.clear();
+ d->xTranslation.clear();
+ d->yTranslation.clear();
+}
+
+/*!
+ \fn void QGraphicsItemAnimation::setStep(qreal step)
+
+ Sets the current \a step value for the animation, causing the
+ transformations scheduled at this step to be performed.
+*/
+void QGraphicsItemAnimation::setStep(qreal x)
+{
+ if (x < 0.0 || x > 1.0) {
+ qWarning("QGraphicsItemAnimation::setStep: invalid step = %f", x);
+ return;
+ }
+
+ beforeAnimationStep(x);
+
+ d->step = x;
+ if (d->item) {
+ if (!d->xPosition.isEmpty() || !d->yPosition.isEmpty())
+ d->item->setPos(posAt(x));
+ if (!d->rotation.isEmpty()
+ || !d->verticalScale.isEmpty()
+ || !d->horizontalScale.isEmpty()
+ || !d->verticalShear.isEmpty()
+ || !d->horizontalShear.isEmpty()
+ || !d->xTranslation.isEmpty()
+ || !d->yTranslation.isEmpty()) {
+ d->item->setMatrix(d->startMatrix * matrixAt(x));
+ }
+ }
+
+ afterAnimationStep(x);
+}
+
+/*!
+ Resets the item to its starting position and transformation.
+
+ \obsolete
+
+ You can call setStep(0) instead.
+*/
+void QGraphicsItemAnimation::reset()
+{
+ if (!d->item)
+ return;
+ d->startPos = d->item->pos();
+ d->startMatrix = d->item->matrix();
+}
+
+/*!
+ \fn void QGraphicsItemAnimation::beforeAnimationStep(qreal step)
+
+ This method is meant to be overridden by subclassed that needs to
+ execute additional code before a new step takes place. The
+ animation \a step is provided for use in cases where the action
+ depends on its value.
+*/
+void QGraphicsItemAnimation::beforeAnimationStep(qreal step)
+{
+ Q_UNUSED(step);
+}
+
+/*!
+ \fn void QGraphicsItemAnimation::afterAnimationStep(qreal step)
+
+ This method is meant to be overridden in subclasses that need to
+ execute additional code after a new step has taken place. The
+ animation \a step is provided for use in cases where the action
+ depends on its value.
+*/
+void QGraphicsItemAnimation::afterAnimationStep(qreal step)
+{
+ Q_UNUSED(step);
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_GRAPHICSVIEW
diff --git a/src/gui/graphicsview/qgraphicsitemanimation.h b/src/gui/graphicsview/qgraphicsitemanimation.h
new file mode 100644
index 0000000000..c1312d4f64
--- /dev/null
+++ b/src/gui/graphicsview/qgraphicsitemanimation.h
@@ -0,0 +1,120 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QGRAPHICSITEMANIMATION_H
+#define QGRAPHICSITEMANIMATION_H
+
+#include <QtCore/qobject.h>
+
+#if !defined(QT_NO_GRAPHICSVIEW) || (QT_EDITION & QT_MODULE_GRAPHICSVIEW) != QT_MODULE_GRAPHICSVIEW
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+class QGraphicsItem;
+class QMatrix;
+class QPointF;
+class QTimeLine;
+template <class T1, class T2> struct QPair;
+
+class QGraphicsItemAnimationPrivate;
+class Q_GUI_EXPORT QGraphicsItemAnimation : public QObject
+{
+ Q_OBJECT
+public:
+ QGraphicsItemAnimation(QObject *parent = 0);
+ virtual ~QGraphicsItemAnimation();
+
+ QGraphicsItem *item() const;
+ void setItem(QGraphicsItem *item);
+
+ QTimeLine *timeLine() const;
+ void setTimeLine(QTimeLine *timeLine);
+
+ QPointF posAt(qreal step) const;
+ QList<QPair<qreal, QPointF> > posList() const;
+ void setPosAt(qreal step, const QPointF &pos);
+
+ QMatrix matrixAt(qreal step) const;
+
+ qreal rotationAt(qreal step) const;
+ QList<QPair<qreal, qreal> > rotationList() const;
+ void setRotationAt(qreal step, qreal angle);
+
+ qreal xTranslationAt(qreal step) const;
+ qreal yTranslationAt(qreal step) const;
+ QList<QPair<qreal, QPointF> > translationList() const;
+ void setTranslationAt(qreal step, qreal dx, qreal dy);
+
+ qreal verticalScaleAt(qreal step) const;
+ qreal horizontalScaleAt(qreal step) const;
+ QList<QPair<qreal, QPointF> > scaleList() const;
+ void setScaleAt(qreal step, qreal sx, qreal sy);
+
+ qreal verticalShearAt(qreal step) const;
+ qreal horizontalShearAt(qreal step) const;
+ QList<QPair<qreal, QPointF> > shearList() const;
+ void setShearAt(qreal step, qreal sh, qreal sv);
+
+ void clear();
+
+public Q_SLOTS:
+ void setStep(qreal x);
+ void reset();
+
+protected:
+ virtual void beforeAnimationStep(qreal step);
+ virtual void afterAnimationStep(qreal step);
+
+private:
+ Q_DISABLE_COPY(QGraphicsItemAnimation)
+ QGraphicsItemAnimationPrivate *d;
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QT_NO_GRAPHICSVIEW
+#endif
diff --git a/src/gui/graphicsview/qgraphicslayout.cpp b/src/gui/graphicsview/qgraphicslayout.cpp
new file mode 100644
index 0000000000..f78b8c81b6
--- /dev/null
+++ b/src/gui/graphicsview/qgraphicslayout.cpp
@@ -0,0 +1,423 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qapplication.h"
+
+#ifndef QT_NO_GRAPHICSVIEW
+#include "qgraphicslayout.h"
+#include "qgraphicslayout_p.h"
+#include "qgraphicslayoutitem.h"
+#include "qgraphicslayoutitem_p.h"
+#include "qgraphicswidget.h"
+#include "qgraphicswidget_p.h"
+#include "qgraphicsscene.h"
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QGraphicsLayout
+ \brief The QGraphicsLayout class provides the base class for all layouts
+ in Graphics View.
+ \since 4.4
+ \ingroup multimedia
+ \ingroup graphicsview-api
+
+ QGraphicsLayout is an abstract class that defines a virtual API for
+ arranging QGraphicsWidget children and other QGraphicsLayoutItem objects
+ for a QGraphicsWidget. QGraphicsWidget assigns responsibility to a
+ QGraphicsLayout through QGraphicsWidget::setLayout(). As the widget
+ is resized, the layout will automatically arrange the widget's children.
+ QGraphicsLayout inherits QGraphicsLayoutItem, so, it can be managed by
+ any layout, including its own subclasses.
+
+ \section1 Writing a Custom Layout
+
+ You can use QGraphicsLayout as a base to write your own custom layout
+ (e.g., a flowlayout), but it is more common to use one of its subclasses
+ instead - QGraphicsLinearLayout or QGraphicsGridLayout. When creating
+ a custom layout, the following functions must be reimplemented as a bare
+ minimum:
+
+ \table
+ \header \o Function \o Description
+ \row \o QGraphicsLayoutItem::setGeometry()
+ \o Notifies you when the geometry of the layout is set. You can
+ store the geometry in your own layout class in a reimplementation
+ of this function.
+ \row \o QGraphicsLayoutItem::sizeHint()
+ \o Returns the layout's size hints.
+ \row \o QGraphicsLayout::count()
+ \o Returns the number of items in your layout.
+ \row \o QGraphicsLayout::itemAt()
+ \o Returns a pointer to an item in your layout.
+ \row \o QGraphicsLayout::removeAt()
+ \o Removes an item from your layout without destroying it.
+ \endtable
+
+ For more details on how to implement each function, refer to the individual
+ function documentation.
+
+ Each layout defines its own API for arranging widgets and layout items.
+ For example, with a grid layout, you require a row and a
+ column index with optional row and column spans, alignment, spacing, and more.
+ A linear layout, however, requires a single row or column index to position its
+ items. For a grid layout, the order of insertion does not affect the layout in
+ any way, but for a linear layout, the order is essential. When writing your own
+ layout subclass, you are free to choose the API that best suits your layout.
+
+ \section1 Activating the Layout
+
+ When the layout's geometry changes, QGraphicsLayout immediately rearranges
+ all of its managed items by calling setGeometry() on each item. This
+ rearrangement is called \e activating the layout.
+
+ QGraphicsLayout updates its own geometry to match the contentsRect() of the
+ QGraphicsLayoutItem it is managing. Thus, it will automatically rearrange all
+ its items when the widget is resized. QGraphicsLayout caches the sizes of all
+ its managed items to avoid calling setGeometry() too often.
+
+ \note A QGraphicsLayout will have the same geometry as the contentsRect()
+ of the widget (not the layout) it is assigned to.
+
+ \section2 Activating the Layout Implicitly
+
+ The layout can be activated implicitly using one of two ways: by calling
+ activate() or by calling invalidate(). Calling activate() activates the layout
+ immediately. In contrast, calling invalidate() is delayed, as it posts a
+ \l{QEvent::LayoutRequest}{LayoutRequest} event to the managed widget. Due
+ to event compression, the activate() will only be called once after control has
+ returned to the event loop. This is referred to as \e invalidating the layout.
+ Invalidating the layout also invalidates any cached information. Also, the
+ invalidate() function is a virtual function. So, you can invalidate your own
+ cache in a subclass of QGraphicsLayout by reimplementing this function.
+
+ \section1 Event Handling
+
+ QGraphicsLayout listens to events for the widget it manages through the
+ virtual widgetEvent() event handler. When the layout is assigned to a
+ widget, all events delivered to the widget are first processed by
+ widgetEvent(). This allows the layout to be aware of any relevant state
+ changes on the widget such as visibility changes or layout direction changes.
+
+ \section1 Margin Handling
+
+ The margins of a QGraphicsLayout can be modified by reimplementing
+ setContentsMargins() and getContentsMargins().
+
+*/
+
+/*!
+ Contructs a QGraphicsLayout object.
+
+ \a parent is passed to QGraphicsLayoutItem's constructor and the
+ QGraphicsLayoutItem's isLayout argument is set to \e true.
+*/
+QGraphicsLayout::QGraphicsLayout(QGraphicsLayoutItem *parent)
+ : QGraphicsLayoutItem(*new QGraphicsLayoutPrivate)
+{
+ setParentLayoutItem(parent);
+ if (parent && !parent->isLayout()) {
+ // If a layout has a parent that is not a layout it must be a QGraphicsWidget.
+ QGraphicsItem *itemParent = parent->graphicsItem();
+ if (itemParent && itemParent->isWidget()) {
+ static_cast<QGraphicsWidget *>(itemParent)->d_func()->setLayout_helper(this);
+ } else {
+ qWarning("QGraphicsLayout::QGraphicsLayout: Attempt to create a layout with a parent that is"
+ " neither a QGraphicsWidget nor QGraphicsLayout");
+ }
+ }
+ setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding, QSizePolicy::DefaultType);
+ setOwnedByLayout(true);
+}
+
+/*!
+ \internal
+*/
+QGraphicsLayout::QGraphicsLayout(QGraphicsLayoutPrivate &dd, QGraphicsLayoutItem *parent)
+ : QGraphicsLayoutItem(dd)
+{
+ setParentLayoutItem(parent);
+ if (parent && !parent->isLayout()) {
+ // If a layout has a parent that is not a layout it must be a QGraphicsWidget.
+ QGraphicsItem *itemParent = parent->graphicsItem();
+ if (itemParent && itemParent->isWidget()) {
+ static_cast<QGraphicsWidget *>(itemParent)->d_func()->setLayout_helper(this);
+ } else {
+ qWarning("QGraphicsLayout::QGraphicsLayout: Attempt to create a layout with a parent that is"
+ " neither a QGraphicsWidget nor QGraphicsLayout");
+ }
+ }
+ setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding, QSizePolicy::DefaultType);
+ setOwnedByLayout(true);
+}
+
+/*!
+ Destroys the QGraphicsLayout object.
+*/
+QGraphicsLayout::~QGraphicsLayout()
+{
+}
+
+/*!
+ Sets the contents margins to \a left, \a top, \a right and \a bottom. The
+ default contents margins for toplevel layouts are style dependent
+ (by querying the pixelMetric for QStyle::PM_LayoutLeftMargin,
+ QStyle::PM_LayoutTopMargin, QStyle::PM_LayoutRightMargin and
+ QStyle::PM_LayoutBottomMargin).
+
+ For sublayouts the default margins are 0.
+
+ Changing the contents margins automatically invalidates the layout.
+
+ \sa invalidate()
+*/
+void QGraphicsLayout::setContentsMargins(qreal left, qreal top, qreal right, qreal bottom)
+{
+ Q_D(QGraphicsLayout);
+ if (d->left == left && d->top == top && d->right == right && d->bottom == bottom)
+ return;
+ d->left = left;
+ d->right = right;
+ d->top = top;
+ d->bottom = bottom;
+ invalidate();
+}
+
+/*!
+ \reimp
+*/
+void QGraphicsLayout::getContentsMargins(qreal *left, qreal *top, qreal *right, qreal *bottom) const
+{
+ Q_D(const QGraphicsLayout);
+ d->getMargin(left, d->left, QStyle::PM_LayoutLeftMargin);
+ d->getMargin(top, d->top, QStyle::PM_LayoutTopMargin);
+ d->getMargin(right, d->right, QStyle::PM_LayoutRightMargin);
+ d->getMargin(bottom, d->bottom, QStyle::PM_LayoutBottomMargin);
+}
+
+/*!
+ Activates the layout, causing all items in the layout to be immediately
+ rearranged. This function is based on calling count() and itemAt(), and
+ then calling setGeometry() on all items sequentially. When activated,
+ the layout will adjust its geometry to its parent's contentsRect().
+ The parent will then invalidate any layout of its own.
+
+ If called in sequence or recursively, e.g., by one of the arranged items
+ in response to being resized, this function will do nothing.
+
+ Note that the layout is free to use geometry caching to optimize this
+ process. To forcefully invalidate any such cache, you can call
+ invalidate() before calling activate().
+
+ \sa invalidate()
+*/
+void QGraphicsLayout::activate()
+{
+ Q_D(QGraphicsLayout);
+ if (d->activated)
+ return;
+
+ d->activateRecursive(this);
+
+ // we don't call activate on a sublayout, but somebody might.
+ // Therefore, we walk to the parentitem of the toplevel layout.
+ QGraphicsLayoutItem *parentItem = this;
+ while (parentItem && parentItem->isLayout())
+ parentItem = parentItem->parentLayoutItem();
+ if (!parentItem)
+ return;
+ Q_ASSERT(!parentItem->isLayout());
+
+ setGeometry(parentItem->contentsRect()); // relayout children
+
+ // ### bug, should be parentItem ?
+ parentLayoutItem()->updateGeometry(); // bubble up; will set activated to false
+ // ### too many resizes? maybe we should walk up the chain to the
+ // ### top-level layouted layoutItem and call activate there.
+}
+
+/*!
+ Returns true if the layout is currently being activated; otherwise,
+ returns false. If the layout is being activated, this means that it is
+ currently in the process of rearranging its items (i.e., the activate()
+ function has been called, and has not yet returned).
+
+ \sa activate(), invalidate()
+*/
+bool QGraphicsLayout::isActivated() const
+{
+ Q_D(const QGraphicsLayout);
+ return d->activated;
+}
+
+/*!
+ Clears any cached geometry and size hint information in the layout, and
+ posts a \l{QEvent::LayoutRequest}{LayoutRequest} event to the managed
+ parent QGraphicsLayoutItem.
+
+ \sa activate(), setGeometry()
+*/
+void QGraphicsLayout::invalidate()
+{
+ // only mark layouts as invalid (activated = false) if we can post a LayoutRequest event.
+ QGraphicsLayoutItem *layoutItem = this;
+ while (layoutItem && layoutItem->isLayout()) {
+ // we could call updateGeometry(), but what if that method
+ // does not call the base implementation? In addition, updateGeometry()
+ // does more than we need.
+ layoutItem->d_func()->sizeHintCacheDirty = true;
+ layoutItem = layoutItem->parentLayoutItem();
+ }
+ if (layoutItem)
+ layoutItem->d_func()->sizeHintCacheDirty = true;
+
+ bool postIt = layoutItem ? !layoutItem->isLayout() : false;
+ if (postIt) {
+ layoutItem = this;
+ while (layoutItem && layoutItem->isLayout()
+ && static_cast<QGraphicsLayout*>(layoutItem)->d_func()->activated) {
+ static_cast<QGraphicsLayout*>(layoutItem)->d_func()->activated = false;
+ layoutItem = layoutItem->parentLayoutItem();
+ }
+ if (layoutItem && !layoutItem->isLayout()) {
+ // If a layout has a parent that is not a layout it must be a QGraphicsWidget.
+ QApplication::postEvent(static_cast<QGraphicsWidget *>(layoutItem), new QEvent(QEvent::LayoutRequest));
+ }
+ }
+}
+
+/*!
+ \reimp
+*/
+void QGraphicsLayout::updateGeometry()
+{
+ QGraphicsLayoutItem::updateGeometry();
+ if (QGraphicsLayoutItem *parentItem = parentLayoutItem()) {
+ if (parentItem->isLayout()) {
+ parentItem->updateGeometry();
+ } else {
+ invalidate();
+ }
+ }
+}
+
+/*!
+ This virtual event handler receives all events for the managed
+ widget. QGraphicsLayout uses this event handler to listen for layout
+ related events such as geometry changes, layout changes or layout
+ direction changes.
+
+ \a e is a pointer to the event.
+
+ You can reimplement this event handler to track similar events for your
+ own custom layout.
+
+ \sa QGraphicsWidget::event(), QGraphicsItem::sceneEvent()
+*/
+void QGraphicsLayout::widgetEvent(QEvent *e)
+{
+ switch (e->type()) {
+ case QEvent::GraphicsSceneResize:
+ if (isActivated()) {
+ setGeometry(parentLayoutItem()->contentsRect());
+ } else {
+ activate(); // relies on that activate() will call updateGeometry()
+ }
+ break;
+ case QEvent::LayoutRequest:
+ activate();
+ break;
+ case QEvent::LayoutDirectionChange:
+ invalidate();
+ break;
+ default:
+ break;
+ }
+}
+
+/*!
+ \fn virtual int QGraphicsLayout::count() const = 0
+
+ This pure virtual function must be reimplemented in a subclass of
+ QGraphicsLayout to return the number of items in the layout.
+
+ The subclass is free to decide how to store the items.
+
+ \sa itemAt(), removeAt()
+*/
+
+/*!
+ \fn virtual QGraphicsLayoutItem *QGraphicsLayout::itemAt(int i) const = 0
+
+ This pure virtual function must be reimplemented in a subclass of
+ QGraphicsLayout to return a pointer to the item at index \a i. The
+ reimplementation can assume that \a i is valid (i.e., it respects the
+ value of count()).
+
+ The subclass is free to decide how to store the items.
+
+ \sa count(), removeAt()
+*/
+
+/*!
+ \fn virtual void QGraphicsLayout::removeAt(int index) = 0
+
+ This pure virtual function must be reimplemented in a subclass of
+ QGraphicsLayout to remove the item at \a index. The
+ reimplementation can assume that \a index is valid (i.e., it
+ respects the value of count()).
+
+ The implementation must ensure that the parentLayoutItem() of
+ the removed item does not point to this layout, since the item is
+ considered to be removed from the layout hierarchy.
+
+ If the layout is to be reused between applications, we recommend
+ that the layout deletes the item, but the graphics view framework
+ does not depend on this.
+
+ The subclass is free to decide how to store the items.
+
+ \sa itemAt(), count()
+*/
+
+QT_END_NAMESPACE
+
+#endif //QT_NO_GRAPHICSVIEW
diff --git a/src/gui/graphicsview/qgraphicslayout.h b/src/gui/graphicsview/qgraphicslayout.h
new file mode 100644
index 0000000000..fad6c3bb4e
--- /dev/null
+++ b/src/gui/graphicsview/qgraphicslayout.h
@@ -0,0 +1,95 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QGRAPHICSLAYOUT_H
+#define QGRAPHICSLAYOUT_H
+
+#include <QtGui/qgraphicslayoutitem.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+#if !defined(QT_NO_GRAPHICSVIEW) || (QT_EDITION & QT_MODULE_GRAPHICSVIEW) != QT_MODULE_GRAPHICSVIEW
+
+class QGraphicsLayoutPrivate;
+class QGraphicsLayoutItem;
+class QGraphicsWidget;
+
+class Q_GUI_EXPORT QGraphicsLayout : public QGraphicsLayoutItem
+{
+public:
+ QGraphicsLayout(QGraphicsLayoutItem *parent = 0);
+ ~QGraphicsLayout();
+
+ void setContentsMargins(qreal left, qreal top, qreal right, qreal bottom);
+ void getContentsMargins(qreal *left, qreal *top, qreal *right, qreal *bottom) const;
+
+ void activate();
+ bool isActivated() const;
+ virtual void invalidate();
+ virtual void updateGeometry();
+
+ virtual void widgetEvent(QEvent *e);
+
+ virtual int count() const = 0;
+ virtual QGraphicsLayoutItem *itemAt(int i) const = 0;
+ virtual void removeAt(int index) = 0;
+
+protected:
+ QGraphicsLayout(QGraphicsLayoutPrivate &, QGraphicsLayoutItem *);
+
+private:
+ Q_DISABLE_COPY(QGraphicsLayout)
+ Q_DECLARE_PRIVATE(QGraphicsLayout)
+ friend class QGraphicsWidget;
+};
+
+#endif
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif
+
diff --git a/src/gui/graphicsview/qgraphicslayout_p.cpp b/src/gui/graphicsview/qgraphicslayout_p.cpp
new file mode 100644
index 0000000000..f76f4dd750
--- /dev/null
+++ b/src/gui/graphicsview/qgraphicslayout_p.cpp
@@ -0,0 +1,198 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qglobal.h"
+
+#ifndef QT_NO_GRAPHICSVIEW
+
+#include "qgraphicslayout_p.h"
+#include "qgraphicslayout.h"
+#include "qgraphicswidget.h"
+#include "qapplication.h"
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \internal
+
+ \a mw is the new parent. all items in the layout will be a child of \a mw.
+ */
+void QGraphicsLayoutPrivate::reparentChildItems(QGraphicsItem *newParent)
+{
+ Q_Q(QGraphicsLayout);
+ int n = q->count();
+ //bool mwVisible = mw && mw->isVisible();
+ for (int i = 0; i < n; ++i) {
+ QGraphicsLayoutItem *layoutChild = q->itemAt(i);
+ if (!layoutChild) {
+ // Skip stretch items
+ continue;
+ }
+ if (layoutChild->isLayout()) {
+ QGraphicsLayout *l = static_cast<QGraphicsLayout*>(layoutChild);
+ l->d_func()->reparentChildItems(newParent);
+ } else if (QGraphicsItem *itemChild = layoutChild->graphicsItem()){
+ QGraphicsItem *childParent = itemChild->parentItem();
+#ifdef QT_DEBUG
+ if (childParent && childParent != newParent && itemChild->isWidget() && qt_graphicsLayoutDebug()) {
+ QGraphicsWidget *w = static_cast<QGraphicsWidget*>(layoutChild);
+ qWarning("QGraphicsLayout::addChildLayout: widget %s \"%s\" in wrong parent; moved to correct parent",
+ w->metaObject()->className(), w->objectName().toLocal8Bit().constData());
+ }
+#endif
+ if (childParent != newParent)
+ itemChild->setParentItem(newParent);
+ }
+ }
+}
+
+void QGraphicsLayoutPrivate::getMargin(qreal *result, qreal userMargin, QStyle::PixelMetric pm) const
+{
+ if (!result)
+ return;
+ Q_Q(const QGraphicsLayout);
+
+ QGraphicsLayoutItem *parent = q->parentLayoutItem();
+ if (userMargin >= 0.0) {
+ *result = userMargin;
+ } else if (!parent) {
+ *result = 0.0;
+ } else if (parent->isLayout()) { // sublayouts have 0 margin by default
+ *result = 0.0;
+ } else {
+ *result = 0.0;
+ if (QGraphicsItem *layoutParentItem = parentItem()) {
+ if (layoutParentItem->isWidget())
+ *result = (qreal)static_cast<QGraphicsWidget*>(layoutParentItem)->style()->pixelMetric(pm, 0);
+ }
+ }
+}
+
+Qt::LayoutDirection QGraphicsLayoutPrivate::visualDirection() const
+{
+ if (QGraphicsItem *maybeWidget = parentItem()) {
+ if (maybeWidget->isWidget())
+ return static_cast<QGraphicsWidget*>(maybeWidget)->layoutDirection();
+ }
+ return QApplication::layoutDirection();
+}
+
+static bool removeLayoutItemFromLayout(QGraphicsLayout *lay, QGraphicsLayoutItem *layoutItem)
+{
+ if (!lay)
+ return false;
+
+ QGraphicsLayoutItem *child;
+ for (int i = 0; (child = lay->itemAt(i)); ++i) {
+ if (child && child->isLayout()) {
+ if (removeLayoutItemFromLayout(static_cast<QGraphicsLayout*>(child), layoutItem))
+ return true;
+ } else if (child == layoutItem) {
+ lay->removeAt(i);
+ return true;
+ }
+ }
+ return false;
+}
+
+/*!
+ \internal
+
+ This function is called from subclasses to add a layout item \a layoutItem
+ to a layout.
+
+ It takes care of automatically reparenting graphics items, if needed.
+
+ If \a layoutItem is a is already in a layout, it will remove it from that layout.
+
+*/
+void QGraphicsLayoutPrivate::addChildLayoutItem(QGraphicsLayoutItem *layoutItem)
+{
+ Q_Q(QGraphicsLayout);
+ if (QGraphicsLayoutItem *maybeLayout = layoutItem->parentLayoutItem()) {
+ if (maybeLayout->isLayout())
+ removeLayoutItemFromLayout(static_cast<QGraphicsLayout*>(maybeLayout), layoutItem);
+ }
+ layoutItem->setParentLayoutItem(q);
+ if (layoutItem->isLayout()) {
+ if (QGraphicsItem *parItem = parentItem()) {
+ static_cast<QGraphicsLayout*>(layoutItem)->d_func()->reparentChildItems(parItem);
+ }
+ } else {
+ if (QGraphicsItem *item = layoutItem->graphicsItem()) {
+ QGraphicsItem *newParent = parentItem();
+ QGraphicsItem *oldParent = item->parentItem();
+ if (oldParent == newParent || !newParent)
+ return;
+
+#ifdef QT_DEBUG
+ if (oldParent && item->isWidget()) {
+ QGraphicsWidget *w = static_cast<QGraphicsWidget*>(item);
+ qWarning("QGraphicsLayout::addChildLayoutItem: %s \"%s\" in wrong parent; moved to correct parent",
+ w->metaObject()->className(), w->objectName().toLocal8Bit().constData());
+ }
+#endif
+
+ item->setParentItem(newParent);
+ }
+ }
+}
+
+void QGraphicsLayoutPrivate::activateRecursive(QGraphicsLayoutItem *item)
+{
+ if (item->isLayout()) {
+ QGraphicsLayout *layout = static_cast<QGraphicsLayout *>(item);
+ if (layout->d_func()->activated)
+ layout->invalidate();
+
+ for (int i = layout->count() - 1; i >= 0; --i) {
+ QGraphicsLayoutItem *childItem = layout->itemAt(i);
+ if (childItem)
+ activateRecursive(childItem);
+ }
+ layout->d_func()->activated = true;
+ }
+}
+
+
+QT_END_NAMESPACE
+
+#endif //QT_NO_GRAPHICSVIEW
diff --git a/src/gui/graphicsview/qgraphicslayout_p.h b/src/gui/graphicsview/qgraphicslayout_p.h
new file mode 100644
index 0000000000..a8895de0cc
--- /dev/null
+++ b/src/gui/graphicsview/qgraphicslayout_p.h
@@ -0,0 +1,103 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QGRAPHICSLAYOUT_P_H
+#define QGRAPHICSLAYOUT_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of qapplication_*.cpp, qwidget*.cpp and qfiledialog.cpp. This header
+// file may change from version to version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtCore/qglobal.h>
+
+#if !defined(QT_NO_GRAPHICSVIEW) || (QT_EDITION & QT_MODULE_GRAPHICSVIEW) != QT_MODULE_GRAPHICSVIEW
+
+#include "qgraphicslayout.h"
+#include "qgraphicslayoutitem_p.h"
+#include <QtGui/qstyle.h>
+
+QT_BEGIN_NAMESPACE
+
+class QGraphicsLayoutItem;
+class QGraphicsWidget;
+
+#ifdef QT_DEBUG
+inline bool qt_graphicsLayoutDebug()
+{
+ static int checked_env = -1;
+ if(checked_env == -1)
+ checked_env = !!qgetenv("QT_GRAPHICSLAYOUT_DEBUG").toInt();
+ return checked_env;
+}
+#endif
+
+class Q_AUTOTEST_EXPORT QGraphicsLayoutPrivate : public QGraphicsLayoutItemPrivate
+{
+ Q_DECLARE_PUBLIC(QGraphicsLayout)
+
+public:
+ QGraphicsLayoutPrivate() : QGraphicsLayoutItemPrivate(0, true), left(-1.0), top(-1.0), right(-1.0), bottom(-1.0),
+ activated(true) { }
+
+ void reparentChildItems(QGraphicsItem *newParent);
+ void getMargin(qreal *result, qreal userMargin, QStyle::PixelMetric pm) const;
+ Qt::LayoutDirection visualDirection() const;
+
+ void addChildLayoutItem(QGraphicsLayoutItem *item);
+ void activateRecursive(QGraphicsLayoutItem *item);
+
+ qreal left, top, right, bottom;
+ bool activated;
+};
+
+
+QT_END_NAMESPACE
+
+#endif //QT_NO_GRAPHICSVIEW
+
+#endif
diff --git a/src/gui/graphicsview/qgraphicslayoutitem.cpp b/src/gui/graphicsview/qgraphicslayoutitem.cpp
new file mode 100644
index 0000000000..0ea769292c
--- /dev/null
+++ b/src/gui/graphicsview/qgraphicslayoutitem.cpp
@@ -0,0 +1,852 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qglobal.h"
+
+#ifndef QT_NO_GRAPHICSVIEW
+
+#include "qgraphicslayout.h"
+#include "qgraphicsscene.h"
+#include "qgraphicslayoutitem.h"
+#include "qgraphicslayoutitem_p.h"
+#include "qwidget.h"
+
+#include <QtDebug>
+
+QT_BEGIN_NAMESPACE
+
+/*
+ COMBINE_SIZE() is identical to combineSize(), except that it
+ doesn't evaluate 'size' unless necessary.
+*/
+#define COMBINE_SIZE(result, size) \
+ do { \
+ if ((result).width() < 0 || (result).height() < 0) \
+ combineSize((result), (size)); \
+ } while (false)
+
+static void combineSize(QSizeF &result, const QSizeF &size)
+{
+ if (result.width() < 0)
+ result.setWidth(size.width());
+ if (result.height() < 0)
+ result.setHeight(size.height());
+}
+
+static void boundSize(QSizeF &result, const QSizeF &size)
+{
+ if (size.width() >= 0 && size.width() < result.width())
+ result.setWidth(size.width());
+ if (size.height() >= 0 && size.height() < result.height())
+ result.setHeight(size.height());
+}
+
+static void expandSize(QSizeF &result, const QSizeF &size)
+{
+ if (size.width() >= 0 && size.width() > result.width())
+ result.setWidth(size.width());
+ if (size.height() >= 0 && size.height() > result.height())
+ result.setHeight(size.height());
+}
+
+static void normalizeHints(qreal &minimum, qreal &preferred, qreal &maximum, qreal &descent)
+{
+ if (minimum >= 0 && maximum >= 0 && minimum > maximum)
+ minimum = maximum;
+
+ if (preferred >= 0) {
+ if (minimum >= 0 && preferred < minimum) {
+ preferred = minimum;
+ } else if (maximum >= 0 && preferred > maximum) {
+ preferred = maximum;
+ }
+ }
+
+ if (minimum >= 0 && descent > minimum)
+ descent = minimum;
+}
+
+/*!
+ \internal
+*/
+QGraphicsLayoutItemPrivate::QGraphicsLayoutItemPrivate(QGraphicsLayoutItem *par, bool layout)
+ : parent(par), isLayout(layout), ownedByLayout(false), graphicsItem(0)
+{
+}
+
+/*!
+ \internal
+*/
+void QGraphicsLayoutItemPrivate::init()
+{
+ sizeHintCacheDirty = true;
+ sizePolicy = QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
+}
+
+/*!
+ \internal
+*/
+QSizeF *QGraphicsLayoutItemPrivate::effectiveSizeHints(const QSizeF &constraint) const
+{
+ Q_Q(const QGraphicsLayoutItem);
+ if (!sizeHintCacheDirty && cachedConstraint == constraint)
+ return cachedSizeHints;
+
+ for (int i = 0; i < Qt::NSizeHints; ++i) {
+ cachedSizeHints[i] = constraint;
+ combineSize(cachedSizeHints[i], userSizeHints[i]);
+ }
+
+ QSizeF &minS = cachedSizeHints[Qt::MinimumSize];
+ QSizeF &prefS = cachedSizeHints[Qt::PreferredSize];
+ QSizeF &maxS = cachedSizeHints[Qt::MaximumSize];
+ QSizeF &descentS = cachedSizeHints[Qt::MinimumDescent];
+
+ normalizeHints(minS.rwidth(), prefS.rwidth(), maxS.rwidth(), descentS.rwidth());
+ normalizeHints(minS.rheight(), prefS.rheight(), maxS.rheight(), descentS.rheight());
+
+ // if the minimum, preferred and maximum sizes contradict each other
+ // (e.g. the minimum is larger than the maximum) we give priority to
+ // the maximum size, then the minimum size and finally the preferred size
+ COMBINE_SIZE(maxS, q->sizeHint(Qt::MaximumSize, maxS));
+ combineSize(maxS, QSizeF(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX));
+ expandSize(maxS, prefS);
+ expandSize(maxS, minS);
+ boundSize(maxS, QSizeF(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX));
+
+ COMBINE_SIZE(minS, q->sizeHint(Qt::MinimumSize, minS));
+ expandSize(minS, QSizeF(0, 0));
+ boundSize(minS, prefS);
+ boundSize(minS, maxS);
+
+ COMBINE_SIZE(prefS, q->sizeHint(Qt::PreferredSize, prefS));
+ expandSize(prefS, minS);
+ boundSize(prefS, maxS);
+
+ // Not supported yet
+ // COMBINE_SIZE(descentS, q->sizeHint(Qt::MinimumDescent, constraint));
+
+ cachedConstraint = constraint;
+ sizeHintCacheDirty = false;
+ return cachedSizeHints;
+}
+
+
+/*!
+ \internal
+
+ Returns the parent item of this layout, or 0 if this layout is
+ not installed on any widget.
+
+ If this is the item that the layout is installed on, it will return "itself".
+
+ If the layout is a sub-layout, this function returns the parent
+ widget of the parent layout.
+
+ Note that it will traverse up the layout item hierarchy instead of just calling
+ QGraphicsItem::parentItem(). This is on purpose.
+
+ \sa parent()
+*/
+QGraphicsItem *QGraphicsLayoutItemPrivate::parentItem() const
+{
+ Q_Q(const QGraphicsLayoutItem);
+
+ const QGraphicsLayoutItem *parent = q;
+ while (parent && parent->isLayout()) {
+ parent = parent->parentLayoutItem();
+ }
+ return parent ? parent->graphicsItem() : 0;
+}
+
+/*!
+ \class QGraphicsLayoutItem
+ \brief The QGraphicsLayoutItem class can be inherited to allow your custom
+ items to be managed by layouts.
+ \since 4.4
+ \ingroup multimedia
+ \ingroup graphicsview-api
+
+ QGraphicsLayoutItem is an abstract class that defines a set of virtual
+ functions describing sizes, size policies, and size hints for any object
+ arranged by QGraphicsLayout. The API contains functions relevant
+ for both the item itself and for the user of the item as most of
+ QGraphicsLayoutItem's functions are also part of the subclass' public API.
+
+ In most cases, existing layout-aware classes such as QGraphicsWidget and
+ QGraphicsLayout already provide the functionality you require. However,
+ subclassing these classes will enable you to create both graphical
+ elements that work well with layouts (QGraphicsWidget) or custom layouts
+ (QGraphicsLayout).
+
+ \section1 Subclassing QGraphicsLayoutItem
+
+ If you create a subclass of QGraphicsLayoutItem and reimplement its
+ virtual functions, you will enable the layout to resize and position your
+ item along with other QGraphicsLayoutItems including QGraphicsWidget
+ and QGraphicsLayout.
+
+ You can start by reimplementing important functions: the protected
+ sizeHint() function, as well as the public setGeometry()
+ function. If you want your items to be aware of immediate geometry
+ changes, you can also reimplement updateGeometry().
+
+ The geometry, size hint, and size policy affect the item's size and
+ position. Calling setGeometry() will always resize and reposition the item
+ immediately. Normally, this function is called by QGraphicsLayout after
+ the layout has been activated, but it can also be called by the item's user
+ at any time.
+
+ The sizeHint() function returns the item' minimum, preferred and maximum
+ size hints. You can override these properties by calling setMinimumSize(),
+ setPreferredSize() or setMaximumSize(). You can also use functions such as
+ setMinimumWidth() or setMaximumHeight() to set only the width or height
+ component if desired.
+
+ The effectiveSizeHint() function, on the other hand, returns a size hint
+ for any given Qt::SizeHint, and guarantees that the returned size is bound
+ to the minimum and maximum sizes and size hints. You can set the item's
+ vertical and horizontal size policy by calling setSizePolicy(). The
+ sizePolicy property is used by the layout system to describe how this item
+ prefers to grow or shrink.
+
+ \section1 Nesting QGraphicsLayoutItems
+
+ QGraphicsLayoutItems can be nested within other QGraphicsLayoutItems,
+ similar to layouts that can contain sublayouts. This is done either by
+ passing a QGraphicsLayoutItem pointer to QGraphicsLayoutItem's
+ protected constructor, or by calling setParentLayoutItem(). The
+ parentLayoutItem() function returns a pointer to the item's layoutItem
+ parent. If the item's parent is 0 or if the the parent does not inherit
+ from QGraphicsItem, the parentLayoutItem() function then returns 0.
+ isLayout() returns true if the QGraphicsLayoutItem subclass is itself a
+ layout, or false otherwise.
+
+ Qt uses QGraphicsLayoutItem to provide layout functionality in the
+ \l{The Graphics View Framework}, but in the future its use may spread
+ throughout Qt itself.
+
+ \sa QGraphicsWidget, QGraphicsLayout, QGraphicsLinearLayout,
+ QGraphicsGridLayout
+*/
+
+/*!
+ Constructs the QGraphicsLayoutItem object. \a parent becomes the object's
+ parent. If \a isLayout is true the item is a layout, otherwise
+ \a isLayout is false.
+*/
+QGraphicsLayoutItem::QGraphicsLayoutItem(QGraphicsLayoutItem *parent, bool isLayout)
+ : d_ptr(new QGraphicsLayoutItemPrivate(parent, isLayout))
+{
+ Q_D(QGraphicsLayoutItem);
+ d->init();
+ d->q_ptr = this;
+}
+
+/*!
+ \internal
+*/
+QGraphicsLayoutItem::QGraphicsLayoutItem(QGraphicsLayoutItemPrivate &dd)
+ : d_ptr(&dd)
+{
+ Q_D(QGraphicsLayoutItem);
+ d->q_ptr = this;
+}
+
+/*!
+ Destroys the QGraphicsLayoutItem object.
+*/
+QGraphicsLayoutItem::~QGraphicsLayoutItem()
+{
+ QGraphicsLayoutItem *parentLI = parentLayoutItem();
+ if (parentLI && parentLI->isLayout()) {
+ QGraphicsLayout *lay = static_cast<QGraphicsLayout*>(parentLI);
+ // this is not optimal
+ for (int i = lay->count() - 1; i >= 0; --i) {
+ if (lay->itemAt(i) == this) {
+ lay->removeAt(i);
+ break;
+ }
+ }
+ }
+ delete d_ptr;
+}
+
+/*!
+ \fn virtual QSizeF QGraphicsLayoutItem::sizeHint(Qt::SizeHint which, const QSizeF &constraint) const = 0;
+
+ This pure virtual function returns the size hint for \a which of the
+ QGraphicsLayoutItem, using the width or height of \a constraint to
+ constrain the output.
+
+ Reimplement this function in a subclass of QGraphicsLayoutItem to
+ provide the necessary size hints for your items.
+
+ \sa effectiveSizeHint()
+*/
+
+/*!
+ Sets the size policy to \a policy. The size policy describes how the item
+ should grow horizontally and vertically when arranged in a layout.
+
+ QGraphicsLayoutItem's default size policy is (QSizePolicy::Fixed,
+ QSizePolicy::Fixed, QSizePolicy::DefaultType), but it is common for
+ subclasses to change the default. For example, QGraphicsWidget defaults
+ to (QSizePolicy::Preferred, QSizePolicy::Preferred,
+ QSizePolicy::DefaultType).
+
+ \sa sizePolicy(), QWidget::sizePolicy()
+*/
+void QGraphicsLayoutItem::setSizePolicy(const QSizePolicy &policy)
+{
+ Q_D(QGraphicsLayoutItem);
+ if (d->sizePolicy == policy)
+ return;
+ d->sizePolicy = policy;
+ updateGeometry();
+}
+
+/*!
+ \overload
+
+ This function is equivalent to calling
+ setSizePolicy(QSizePolicy(\a hPolicy, \a vPolicy, \a controlType)).
+
+ \sa sizePolicy(), QWidget::sizePolicy()
+*/
+void QGraphicsLayoutItem::setSizePolicy(QSizePolicy::Policy hPolicy,
+ QSizePolicy::Policy vPolicy,
+ QSizePolicy::ControlType controlType)
+{
+ setSizePolicy(QSizePolicy(hPolicy, vPolicy, controlType));
+}
+
+/*!
+ Returns the current size policy.
+
+ \sa setSizePolicy(), QWidget::sizePolicy()
+*/
+QSizePolicy QGraphicsLayoutItem::sizePolicy() const
+{
+ Q_D(const QGraphicsLayoutItem);
+ return d->sizePolicy;
+}
+
+/*!
+ Sets the minimum size to \a size. This property overrides sizeHint() for
+ Qt::MinimumSize and ensures that effectiveSizeHint() will never return
+ a size smaller than \a size. In order to unset the minimum size, use an
+ invalid size.
+
+ \sa minimumSize(), maximumSize(), preferredSize(), Qt::MinimumSize,
+ sizeHint(), setMinimumWidth(), setMinimumHeight()
+*/
+void QGraphicsLayoutItem::setMinimumSize(const QSizeF &size)
+{
+ Q_D(QGraphicsLayoutItem);
+ if (size == d->userSizeHints[Qt::MinimumSize])
+ return;
+
+ d->userSizeHints[Qt::MinimumSize] = size;
+ updateGeometry();
+}
+
+/*!
+ \fn QGraphicsLayoutItem::setMinimumSize(qreal w, qreal h)
+
+ This convenience function is equivalent to calling
+ setMinimumSize(QSizeF(\a w, \a h)).
+
+ \sa minimumSize(), setMaximumSize(), setPreferredSize(), sizeHint()
+*/
+
+/*!
+ Returns the minimum size.
+
+ \sa setMinimumSize(), preferredSize(), maximumSize(), Qt::MinimumSize,
+ sizeHint()
+*/
+QSizeF QGraphicsLayoutItem::minimumSize() const
+{
+ return effectiveSizeHint(Qt::MinimumSize);
+}
+
+/*!
+ Sets the minimum width to \a width.
+
+ \sa minimumWidth(), setMinimumSize(), minimumSize()
+*/
+void QGraphicsLayoutItem::setMinimumWidth(qreal width)
+{
+ Q_D(QGraphicsLayoutItem);
+ qreal &userSizeHint = d->userSizeHints[Qt::MinimumSize].rwidth();
+ if (width == userSizeHint)
+ return;
+ userSizeHint = width;
+ updateGeometry();
+}
+
+/*!
+ Sets the minimum height to \a height.
+
+ \sa minimumHeight(), setMinimumSize(), minimumSize()
+*/
+void QGraphicsLayoutItem::setMinimumHeight(qreal height)
+{
+ Q_D(QGraphicsLayoutItem);
+ qreal &userSizeHint = d->userSizeHints[Qt::MinimumSize].rheight();
+ if (height == userSizeHint)
+ return;
+ userSizeHint = height;
+ updateGeometry();
+}
+
+
+/*!
+ Sets the preferred size to \a size. This property overrides sizeHint() for
+ Qt::PreferredSize and provides the default value for effectiveSizeHint().
+ In order to unset the preferred size, use an invalid size.
+
+ \sa preferredSize(), minimumSize(), maximumSize(), Qt::PreferredSize,
+ sizeHint()
+*/
+void QGraphicsLayoutItem::setPreferredSize(const QSizeF &size)
+{
+ Q_D(QGraphicsLayoutItem);
+ if (size == d->userSizeHints[Qt::PreferredSize])
+ return;
+
+ d->userSizeHints[Qt::PreferredSize] = size;
+ updateGeometry();
+}
+
+/*!
+ \fn QGraphicsLayoutItem::setPreferredSize(qreal w, qreal h)
+
+ This convenience function is equivalent to calling
+ setPreferredSize(QSizeF(\a w, \a h)).
+
+ \sa preferredSize(), setMaximumSize(), setMinimumSize(), sizeHint()
+*/
+
+/*!
+ Returns the preferred size.
+
+ \sa setPreferredSize(), minimumSize(), maximumSize(), Qt::PreferredSize,
+ sizeHint()
+*/
+QSizeF QGraphicsLayoutItem::preferredSize() const
+{
+ return effectiveSizeHint(Qt::PreferredSize);
+}
+
+/*!
+ Sets the preferred height to \a height.
+
+ \sa preferredWidth(), setPreferredSize(), preferredSize()
+*/
+void QGraphicsLayoutItem::setPreferredHeight(qreal height)
+{
+ Q_D(QGraphicsLayoutItem);
+ qreal &userSizeHint = d->userSizeHints[Qt::PreferredSize].rheight();
+ if (height == userSizeHint)
+ return;
+ userSizeHint = height;
+ updateGeometry();
+}
+
+/*!
+ Sets the preferred width to \a width.
+
+ \sa preferredHeight(), setPreferredSize(), preferredSize()
+*/
+void QGraphicsLayoutItem::setPreferredWidth(qreal width)
+{
+ Q_D(QGraphicsLayoutItem);
+ qreal &userSizeHint = d->userSizeHints[Qt::PreferredSize].rwidth();
+ if (width == userSizeHint)
+ return;
+ userSizeHint = width;
+ updateGeometry();
+}
+
+/*!
+ Sets the maximum size to \a size. This property overrides sizeHint() for
+ Qt::MaximumSize and ensures that effectiveSizeHint() will never return a
+ size larger than \a size. In order to unset the maximum size, use an
+ invalid size.
+
+ \sa maximumSize(), minimumSize(), preferredSize(), Qt::MaximumSize,
+ sizeHint()
+*/
+void QGraphicsLayoutItem::setMaximumSize(const QSizeF &size)
+{
+ Q_D(QGraphicsLayoutItem);
+ if (size == d->userSizeHints[Qt::MaximumSize])
+ return;
+
+ d->userSizeHints[Qt::MaximumSize] = size;
+ updateGeometry();
+}
+
+/*!
+ \fn QGraphicsLayoutItem::setMaximumSize(qreal w, qreal h)
+
+ This convenience function is equivalent to calling
+ setMaximumSize(QSizeF(\a w, \a h)).
+
+ \sa maximumSize(), setMinimumSize(), setPreferredSize(), sizeHint()
+*/
+
+/*!
+ Returns the maximum size.
+
+ \sa setMaximumSize(), minimumSize(), preferredSize(), Qt::MaximumSize,
+ sizeHint()
+*/
+QSizeF QGraphicsLayoutItem::maximumSize() const
+{
+ return effectiveSizeHint(Qt::MaximumSize);
+}
+
+/*!
+ Sets the maximum width to \a width.
+
+ \sa maximumWidth(), setMaximumSize(), maximumSize()
+*/
+void QGraphicsLayoutItem::setMaximumWidth(qreal width)
+{
+ Q_D(QGraphicsLayoutItem);
+ qreal &userSizeHint = d->userSizeHints[Qt::MaximumSize].rwidth();
+ if (width == userSizeHint)
+ return;
+ userSizeHint = width;
+ updateGeometry();
+}
+
+/*!
+ Sets the maximum height to \a height.
+
+ \sa maximumHeight(), setMaximumSize(), maximumSize()
+*/
+void QGraphicsLayoutItem::setMaximumHeight(qreal height)
+{
+ Q_D(QGraphicsLayoutItem);
+ qreal &userSizeHint = d->userSizeHints[Qt::MaximumSize].rheight();
+ if (height == userSizeHint)
+ return;
+ userSizeHint = height;
+ updateGeometry();
+}
+
+/*!
+ \fn qreal QGraphicsLayoutItem::minimumWidth() const
+
+ Returns the minimum width.
+
+ \sa setMinimumWidth(), setMinimumSize(), minimumSize()
+*/
+
+/*!
+ \fn qreal QGraphicsLayoutItem::minimumHeight() const
+
+ Returns the minimum height.
+
+ \sa setMinimumHeight(), setMinimumSize(), minimumSize()
+*/
+
+/*!
+ \fn qreal QGraphicsLayoutItem::preferredWidth() const
+
+ Returns the preferred width.
+
+ \sa setPreferredWidth(), setPreferredSize(), preferredSize()
+*/
+
+/*!
+ \fn qreal QGraphicsLayoutItem::preferredHeight() const
+
+ Returns the preferred height.
+
+ \sa setPreferredHeight(), setPreferredSize(), preferredSize()
+*/
+
+/*!
+ \fn qreal QGraphicsLayoutItem::maximumWidth() const
+
+ Returns the maximum width.
+
+ \sa setMaximumWidth(), setMaximumSize(), maximumSize()
+*/
+
+/*!
+ \fn qreal QGraphicsLayoutItem::maximumHeight() const
+
+ Returns the maximum height.
+
+ \sa setMaximumHeight(), setMaximumSize(), maximumSize()
+*/
+
+/*!
+ \fn virtual void QGraphicsLayoutItem::setGeometry(const QRectF &rect) = 0
+
+ This pure virtual function sets the geometry of the QGraphicsLayoutItem to
+ \a rect, which is in parent coordinates (e.g., the top-left corner of \a rect
+ is equivalent to the item's position in parent coordinates).
+
+ Reimplement this function in a subclass of QGraphicsLayoutItem to enable
+ your item to receive geometry updates.
+
+ If \a rect is outside of the bounds of minimumSize and maximumSize, it
+ will be adjusted to its closest size so that it is within the legal
+ bounds.
+
+ \sa geometry()
+*/
+void QGraphicsLayoutItem::setGeometry(const QRectF &rect)
+{
+ Q_D(QGraphicsLayoutItem);
+ QSizeF effectiveSize = rect.size().expandedTo(effectiveSizeHint(Qt::MinimumSize))
+ .boundedTo(effectiveSizeHint(Qt::MaximumSize));
+ d->geom = QRectF(rect.topLeft(), effectiveSize);
+}
+
+/*!
+ \fn QRectF QGraphicsLayoutItem::geometry() const
+
+ Returns the item's geometry (e.g., position and size) as a
+ QRectF. This function is equivalent to QRectF(pos(), size()).
+
+ \sa setGeometry()
+*/
+QRectF QGraphicsLayoutItem::geometry() const
+{
+ Q_D(const QGraphicsLayoutItem);
+ return d->geom;
+}
+
+/*!
+ This virtual function provides the \a left, \a top, \a right and \a bottom
+ contents margins for this QGraphicsLayoutItem. The default implementation
+ assumes all contents margins are 0. The parameters point to values stored
+ in qreals. If any of the pointers is 0, that value will not be updated.
+
+ \sa QGraphicsWidget::setContentsMargins()
+*/
+void QGraphicsLayoutItem::getContentsMargins(qreal *left, qreal *top, qreal *right, qreal *bottom) const
+{
+ if (left)
+ *left = 0;
+ if (top)
+ *top = 0;
+ if (right)
+ *right = 0;
+ if (bottom)
+ *bottom = 0;
+}
+
+/*!
+ Returns the contents rect in local coordinates.
+
+ The contents rect defines the subrectangle used by an associated layout
+ when arranging subitems. This function is a convenience function that
+ adjusts the item's geometry() by its contents margins. Note that
+ getContentsMargins() is a virtual function that you can reimplement to
+ return the item's contents margins.
+
+ \sa getContentsMargins(), geometry()
+*/
+QRectF QGraphicsLayoutItem::contentsRect() const
+{
+ qreal left, top, right, bottom;
+ getContentsMargins(&left, &top, &right, &bottom);
+ return QRectF(QPointF(), geometry().size()).adjusted(+left, +top, -right, -bottom);
+}
+
+/*!
+ Returns the effective size hint for this QGraphicsLayoutItem.
+
+ \a which is the size hint in question.
+ \a constraint is an optional argument that defines a special constrain
+ when calculating the effective size hint. By default, \a constraint is
+ QSizeF(-1, -1), which means there is no constraint to the size hint.
+
+ If you want to specify the widget's size hint for a given width or height,
+ you can provide the fixed dimension in \a constraint. This is useful for
+ widgets that can grow only either vertically or horizontally, and need to
+ set either their width or their height to a special value.
+
+ For example, a text paragraph item fit into a column width of 200 may
+ grow vertically. You can pass QSizeF(200, -1) as a constraint to get a
+ suitable minimum, preferred and maximum height).
+
+ You can adjust the effective size hint by reimplementing sizeHint()
+ in a QGraphicsLayoutItem subclass, or by calling one of the following
+ functions: setMinimumSize(), setPreferredSize, or setMaximumSize()
+ (or a combination of both).
+
+ This function caches each of the size hints and guarantees that
+ sizeHint() will be called only once for each value of \a which - unless
+ \a constraint is not specified and updateGeometry() has been called.
+
+ \sa sizeHint()
+*/
+QSizeF QGraphicsLayoutItem::effectiveSizeHint(Qt::SizeHint which, const QSizeF &constraint) const
+{
+ // ### should respect size policy???
+ return d_ptr->effectiveSizeHints(constraint)[which];
+}
+
+/*!
+ This virtual function discards any cached size hint information. You
+ should always call this function if you change the return value of the
+ sizeHint() function. Subclasses must always call the base implementation
+ when reimplementing this function.
+
+ \sa effectiveSizeHint()
+*/
+void QGraphicsLayoutItem::updateGeometry()
+{
+ Q_D(QGraphicsLayoutItem);
+ d->sizeHintCacheDirty = true;
+}
+
+/*!
+ Returns the parent of this QGraphicsLayoutItem, or 0 if there is no parent,
+ or if the parent does not inherit from QGraphicsLayoutItem
+ (QGraphicsLayoutItem is often used through multiple inheritance with
+ QObject-derived classes).
+
+ \sa setParentLayoutItem()
+*/
+QGraphicsLayoutItem *QGraphicsLayoutItem::parentLayoutItem() const
+{
+ return d_func()->parent;
+}
+
+/*!
+ Sets the parent of this QGraphicsLayoutItem to \a parent.
+
+ \sa parentLayoutItem()
+*/
+void QGraphicsLayoutItem::setParentLayoutItem(QGraphicsLayoutItem *parent)
+{
+ d_func()->parent = parent;
+}
+
+/*!
+ Returns true if this QGraphicsLayoutItem is a layout (e.g., is inherited
+ by an object that arranges other QGraphicsLayoutItem objects); otherwise
+ returns false.
+
+ \sa QGraphicsLayout
+*/
+bool QGraphicsLayoutItem::isLayout() const
+{
+ return d_func()->isLayout;
+}
+
+/*!
+ Returns whether a layout should delete this item in its destructor.
+ If its true, then the layout will delete it. If its false, then it is
+ assumed that another object has the ownership of it, and the layout won't
+ delete this item.
+
+ If the item inherits both QGraphicsItem and QGraphicsLayoutItem (such
+ as QGraphicsWidget does) the item is really part of two ownership
+ hierarchies. This property informs what the layout should do with its
+ child items when it is destructed. In the case of QGraphicsWidget, it
+ is preferred that when the layout is deleted it won't delete its children
+ (since they are also part of the graphics item hierarchy).
+
+ By default this value is initialized to false in QGraphicsLayoutItem,
+ but it is overridden by QGraphicsLayout to return true. This is because
+ QGraphicsLayout is not normally part of the QGraphicsItem hierarchy, so the
+ parent layout should delete it.
+ Subclasses might override this default behaviour by calling
+ setOwnedByLayout(true).
+
+ \sa setOwnedByLayout()
+*/
+bool QGraphicsLayoutItem::ownedByLayout() const
+{
+ return d_func()->ownedByLayout;
+}
+/*!
+ Sets whether a layout should delete this item in its destructor or not.
+ \a ownership must be true to in order for the layout to delete it.
+ \sa ownedByLayout()
+*/
+void QGraphicsLayoutItem::setOwnedByLayout(bool ownership)
+{
+ d_func()->ownedByLayout = ownership;
+}
+
+/*!
+ * Returns the QGraphicsItem that this layout item represents.
+ * For QGraphicsWidget it will return itself. For custom items it can return an
+ * aggregated value.
+ *
+ * \sa setGraphicsItem()
+ */
+QGraphicsItem *QGraphicsLayoutItem::graphicsItem() const
+{
+ return d_func()->graphicsItem;
+}
+
+/*!
+ * If the QGraphicsLayoutItem represents a QGraphicsItem, and it wants to take
+ * advantage of the automatic reparenting capabilities of QGraphicsLayout it
+ * should set this value.
+ * Note that if you delete \a item and not delete the layout item, you are
+ * responsible of calling setGraphicsItem(0) in order to avoid having a
+ * dangling pointer.
+ *
+ * \sa graphicsItem()
+ */
+void QGraphicsLayoutItem::setGraphicsItem(QGraphicsItem *item)
+{
+ d_func()->graphicsItem = item;
+}
+
+QT_END_NAMESPACE
+
+#endif //QT_NO_GRAPHICSVIEW
diff --git a/src/gui/graphicsview/qgraphicslayoutitem.h b/src/gui/graphicsview/qgraphicslayoutitem.h
new file mode 100644
index 0000000000..31f5d90747
--- /dev/null
+++ b/src/gui/graphicsview/qgraphicslayoutitem.h
@@ -0,0 +1,152 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QGRAPHICSLAYOUTITEM_H
+#define QGRAPHICSLAYOUTITEM_H
+
+#include <QtGui/qsizepolicy.h>
+#include <QtGui/qevent.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+#if !defined(QT_NO_GRAPHICSVIEW) || (QT_EDITION & QT_MODULE_GRAPHICSVIEW) != QT_MODULE_GRAPHICSVIEW
+
+class QGraphicsLayoutItemPrivate;
+class QGraphicsItem;
+class Q_GUI_EXPORT QGraphicsLayoutItem
+{
+public:
+ QGraphicsLayoutItem(QGraphicsLayoutItem *parent = 0, bool isLayout = false);
+ virtual ~QGraphicsLayoutItem();
+
+ void setSizePolicy(const QSizePolicy &policy);
+ void setSizePolicy(QSizePolicy::Policy hPolicy, QSizePolicy::Policy vPolicy, QSizePolicy::ControlType controlType = QSizePolicy::DefaultType);
+ QSizePolicy sizePolicy() const;
+
+ void setMinimumSize(const QSizeF &size);
+ inline void setMinimumSize(qreal w, qreal h);
+ QSizeF minimumSize() const;
+ void setMinimumWidth(qreal width);
+ inline qreal minimumWidth() const;
+ void setMinimumHeight(qreal height);
+ inline qreal minimumHeight() const;
+
+ void setPreferredSize(const QSizeF &size);
+ inline void setPreferredSize(qreal w, qreal h);
+ QSizeF preferredSize() const;
+ void setPreferredWidth(qreal width);
+ inline qreal preferredWidth() const;
+ void setPreferredHeight(qreal height);
+ inline qreal preferredHeight() const;
+
+ void setMaximumSize(const QSizeF &size);
+ inline void setMaximumSize(qreal w, qreal h);
+ QSizeF maximumSize() const;
+ void setMaximumWidth(qreal width);
+ inline qreal maximumWidth() const;
+ void setMaximumHeight(qreal height);
+ inline qreal maximumHeight() const;
+
+ virtual void setGeometry(const QRectF &rect);
+ QRectF geometry() const;
+ virtual void getContentsMargins(qreal *left, qreal *top, qreal *right, qreal *bottom) const;
+ QRectF contentsRect() const;
+
+ QSizeF effectiveSizeHint(Qt::SizeHint which, const QSizeF &constraint = QSizeF()) const;
+
+ virtual void updateGeometry(); //### rename to sizeHintChanged()
+
+ QGraphicsLayoutItem *parentLayoutItem() const;
+ void setParentLayoutItem(QGraphicsLayoutItem *parent);
+
+ bool isLayout() const;
+ // ###Qt5: Make automatic reparenting work regardless of item/object/widget type.
+ QGraphicsItem *graphicsItem() const;
+ bool ownedByLayout() const;
+
+protected:
+ void setGraphicsItem(QGraphicsItem *item);
+ void setOwnedByLayout(bool ownedByLayout);
+ QGraphicsLayoutItem(QGraphicsLayoutItemPrivate &dd);
+
+ virtual QSizeF sizeHint(Qt::SizeHint which, const QSizeF &constraint = QSizeF()) const = 0;
+ QGraphicsLayoutItemPrivate *d_ptr;
+
+private:
+ QSizeF *effectiveSizeHints(const QSizeF &constraint) const;
+ Q_DECLARE_PRIVATE(QGraphicsLayoutItem)
+
+ friend class QGraphicsLayout;
+};
+
+inline void QGraphicsLayoutItem::setMinimumSize(qreal aw, qreal ah)
+{ setMinimumSize(QSizeF(aw, ah)); }
+inline void QGraphicsLayoutItem::setPreferredSize(qreal aw, qreal ah)
+{ setPreferredSize(QSizeF(aw, ah)); }
+inline void QGraphicsLayoutItem::setMaximumSize(qreal aw, qreal ah)
+{ setMaximumSize(QSizeF(aw, ah)); }
+
+inline qreal QGraphicsLayoutItem::minimumWidth() const
+{ return effectiveSizeHint(Qt::MinimumSize).width(); }
+inline qreal QGraphicsLayoutItem::minimumHeight() const
+{ return effectiveSizeHint(Qt::MinimumSize).height(); }
+
+inline qreal QGraphicsLayoutItem::preferredWidth() const
+{ return effectiveSizeHint(Qt::PreferredSize).width(); }
+inline qreal QGraphicsLayoutItem::preferredHeight() const
+{ return effectiveSizeHint(Qt::PreferredSize).height(); }
+
+inline qreal QGraphicsLayoutItem::maximumWidth() const
+{ return effectiveSizeHint(Qt::MaximumSize).width(); }
+inline qreal QGraphicsLayoutItem::maximumHeight() const
+{ return effectiveSizeHint(Qt::MaximumSize).height(); }
+
+#endif
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif
diff --git a/src/gui/graphicsview/qgraphicslayoutitem_p.h b/src/gui/graphicsview/qgraphicslayoutitem_p.h
new file mode 100644
index 0000000000..fab0f3994b
--- /dev/null
+++ b/src/gui/graphicsview/qgraphicslayoutitem_p.h
@@ -0,0 +1,91 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QGRAPHICSLAYOUTITEM_P_H
+#define QGRAPHICSLAYOUTITEM_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of qapplication_*.cpp, qwidget*.cpp and qfiledialog.cpp. This header
+// file may change from version to version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtCore/QSizeF>
+#include <QtGui/QSizePolicy>
+
+QT_BEGIN_NAMESPACE
+
+class QGraphicsLayoutItem;
+class Q_AUTOTEST_EXPORT QGraphicsLayoutItemPrivate
+{
+ Q_DECLARE_PUBLIC(QGraphicsLayoutItem)
+public:
+ virtual ~QGraphicsLayoutItemPrivate() {}
+ QGraphicsLayoutItemPrivate(QGraphicsLayoutItem *parent, bool isLayout);
+ void init();
+ QSizeF *effectiveSizeHints(const QSizeF &constraint) const;
+ QGraphicsItem *parentItem() const;
+
+ QSizePolicy sizePolicy;
+ QGraphicsLayoutItem *parent;
+
+ QSizeF userSizeHints[Qt::NSizeHints];
+ mutable QSizeF cachedSizeHints[Qt::NSizeHints];
+ mutable QSizeF cachedConstraint;
+
+ mutable quint32 sizeHintCacheDirty : 1;
+ quint32 isLayout : 1;
+ quint32 ownedByLayout : 1;
+
+ QGraphicsLayoutItem *q_ptr;
+ QRectF geom;
+ QGraphicsItem *graphicsItem;
+};
+
+QT_END_NAMESPACE
+
+#endif //QGRAPHICSLAYOUTITEM_P_H
+
diff --git a/src/gui/graphicsview/qgraphicslinearlayout.cpp b/src/gui/graphicsview/qgraphicslinearlayout.cpp
new file mode 100644
index 0000000000..6a2d456b6c
--- /dev/null
+++ b/src/gui/graphicsview/qgraphicslinearlayout.cpp
@@ -0,0 +1,547 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*!
+ \class QGraphicsLinearLayout
+ \brief The QGraphicsLinearLayout class provides a horizontal or vertical
+ layout for managing widgets in Graphics View.
+ \since 4.4
+ \ingroup multimedia
+ \ingroup graphicsview-api
+
+ The default orientation for a linear layout is Qt::Horizontal. You can
+ choose a vertical orientation either by calling setOrientation(), or by
+ passing Qt::Vertical to QGraphicsLinearLayout's constructor.
+
+ The most common way to use QGraphicsLinearLayout is to construct an object
+ on the heap with no parent, add widgets and layouts by calling addItem(),
+ and finally assign the layout to a widget by calling
+ QGraphicsWidget::setLayout().
+
+ \snippet doc/src/snippets/code/src_gui_graphicsview_qgraphicslinearlayout.cpp 0
+
+ You can add widgets, layouts, stretches (addStretch(), insertStretch() or
+ setStretchFactor()), and spacings (setItemSpacing()) to a linear
+ layout. The layout takes ownership of the items. In some cases when the layout
+ item also inherits from QGraphicsItem (such as QGraphicsWidget) there will be a
+ ambiguity in ownership because the layout item belongs to two ownership hierarchies.
+ See the documentation of QGraphicsLayoutItem::setOwnedByLayout() how to handle
+ this.
+ You can access each item in the layout by calling count() and itemAt(). Calling
+ removeAt() or removeItem() will remove an item from the layout, without
+ destroying it.
+
+ \section1 Size Hints and Size Policies in QGraphicsLinearLayout
+
+ QGraphicsLinearLayout respects each item's size hints and size policies,
+ and when the layout contains more space than the items can fill, each item
+ is arranged according to the layout's alignment for that item. You can set
+ an alignment for each item by calling setAlignment(), and check the
+ alignment for any item by calling alignment(). By default, items are
+ centered both vertically and horizontally.
+
+ \section1 Spacing within QGraphicsLinearLayout
+
+ Between the items, the layout distributes some space. The actual amount of
+ space depends on the managed widget's current style, but the common
+ spacing is 4. You can also set your own spacing by calling setSpacing(),
+ and get the current spacing value by calling spacing(). If you want to
+ configure individual spacing for your items, you can call setItemSpacing().
+
+ \section1 Stretch Factor in QGraphicsLinearLayout
+
+ You can assign a stretch factor to each item to control how much space it
+ will get compared to the other items. By default, two identical widgets
+ arranged in a linear layout will have the same size, but if the first
+ widget has a stretch factor of 1 and the second widget has a stretch
+ factor of 2, the first widget will get 1/3 of the available space, and the
+ second will get 2/3.
+
+ QGraphicsLinearLayout calculates the distribution of sizes by adding up
+ the stretch factors of all items, and then dividing the available space
+ accordingly. The default stretch factor is 0 for all items; a factor of 0
+ means the item does not have any defined stretch factor; effectively this
+ is the same as setting the stretch factor to 1. The stretch factor only
+ applies to the available space in the lengthwise direction of the layout
+ (following its orientation). If you want to control both the item's
+ horizontal and vertical stretch, you can use QGraphicsGridLayout instead.
+
+ \section1 QGraphicsLinearLayout Compared to Other Layouts
+
+ QGraphicsLinearLayout is very similar to QVBoxLayout and QHBoxLayout, but
+ in contrast to these classes, it is used to manage QGraphicsWidget and
+ QGraphicsLayout instead of QWidget and QLayout.
+
+ \sa QGraphicsGridLayout, QGraphicsWidget
+*/
+
+#include "qapplication.h"
+
+#ifndef QT_NO_GRAPHICSVIEW
+
+#include "qwidget.h"
+#include "qgraphicslayout_p.h"
+#include "qgraphicslayoutitem.h"
+#include "qgraphicslinearlayout.h"
+#include "qgraphicswidget.h"
+#include "qgridlayoutengine_p.h"
+#ifdef QT_DEBUG
+#include <QtCore/qdebug.h>
+#endif
+
+QT_BEGIN_NAMESPACE
+
+class QGraphicsLinearLayoutPrivate : public QGraphicsLayoutPrivate
+{
+public:
+ QGraphicsLinearLayoutPrivate(Qt::Orientation orientation) : orientation(orientation) { }
+
+ void removeGridItem(QGridLayoutItem *gridItem);
+ QLayoutStyleInfo styleInfo() const;
+ void fixIndex(int *index) const;
+ int gridRow(int index) const;
+ int gridColumn(int index) const;
+
+ Qt::Orientation orientation;
+ QGridLayoutEngine engine;
+};
+
+void QGraphicsLinearLayoutPrivate::removeGridItem(QGridLayoutItem *gridItem)
+{
+ int index = gridItem->firstRow(orientation);
+ engine.removeItem(gridItem);
+ engine.removeRow(index, orientation);
+}
+
+void QGraphicsLinearLayoutPrivate::fixIndex(int *index) const
+{
+ int count = engine.rowCount(orientation);
+ if (uint(*index) > uint(count))
+ *index = count;
+}
+
+int QGraphicsLinearLayoutPrivate::gridRow(int index) const
+{
+ if (orientation == Qt::Horizontal)
+ return 0;
+ return int(qMin(uint(index), uint(engine.rowCount())));
+}
+
+int QGraphicsLinearLayoutPrivate::gridColumn(int index) const
+{
+ if (orientation == Qt::Vertical)
+ return 0;
+ return int(qMin(uint(index), uint(engine.columnCount())));
+}
+
+QLayoutStyleInfo QGraphicsLinearLayoutPrivate::styleInfo() const
+{
+ static QWidget *wid = 0;
+ if (!wid)
+ wid = new QWidget;
+ QGraphicsItem *item = parentItem();
+ QStyle *style = (item && item->isWidget()) ? static_cast<QGraphicsWidget*>(item)->style() : qApp->style();
+ return QLayoutStyleInfo(style, wid);
+}
+
+/*!
+ Constructs a QGraphicsLinearLayout instance. You can pass the
+ \a orientation for the layout, either horizontal or vertical, and
+ \a parent is passed to QGraphicsLayout's constructor.
+*/
+QGraphicsLinearLayout::QGraphicsLinearLayout(Qt::Orientation orientation, QGraphicsLayoutItem *parent)
+ : QGraphicsLayout(*new QGraphicsLinearLayoutPrivate(orientation), parent)
+{
+}
+
+/*!
+ Constructs a QGraphicsLinearLayout instance using Qt::Horizontal
+ orientation. \a parent is passed to QGraphicsLayout's constructor.
+*/
+QGraphicsLinearLayout::QGraphicsLinearLayout(QGraphicsLayoutItem *parent)
+ : QGraphicsLayout(*new QGraphicsLinearLayoutPrivate(Qt::Horizontal), parent)
+{
+}
+
+/*!
+ Destroys the QGraphicsLinearLayout object.
+*/
+QGraphicsLinearLayout::~QGraphicsLinearLayout()
+{
+ for (int i = count() - 1; i >= 0; --i) {
+ QGraphicsLayoutItem *item = itemAt(i);
+ // The following lines can be removed, but this removes the item
+ // from the layout more efficiently than the implementation of
+ // ~QGraphicsLayoutItem.
+ removeAt(i);
+ if (item) {
+ item->setParentLayoutItem(0);
+ if (item->ownedByLayout())
+ delete item;
+ }
+ }
+}
+
+/*!
+ Change the layout orientation to \a orientation. Changing the layout
+ orientation will automatically invalidate the layout.
+
+ \sa orientation()
+*/
+void QGraphicsLinearLayout::setOrientation(Qt::Orientation orientation)
+{
+ Q_D(QGraphicsLinearLayout);
+ if (orientation != d->orientation) {
+ d->engine.transpose();
+ d->orientation = orientation;
+ invalidate();
+ }
+}
+
+/*!
+ Returns the layout orientation.
+ \sa setOrientation()
+ */
+Qt::Orientation QGraphicsLinearLayout::orientation() const
+{
+ Q_D(const QGraphicsLinearLayout);
+ return d->orientation;
+}
+
+/*!
+ \fn void QGraphicsLinearLayout::addItem(QGraphicsLayoutItem *item)
+
+ This convenience function is equivalent to calling
+ insertItem(-1, \a item).
+*/
+
+/*!
+ \fn void QGraphicsLinearLayout::addStretch(int stretch)
+
+ This convenience function is equivalent to calling
+ insertStretch(-1, \a stretch).
+*/
+
+/*!
+ Inserts \a item into the layout at \a index, or before any item that is
+ currently at \a index.
+
+ \sa addItem(), itemAt(), insertStretch(), setItemSpacing()
+*/
+void QGraphicsLinearLayout::insertItem(int index, QGraphicsLayoutItem *item)
+{
+ Q_D(QGraphicsLinearLayout);
+ if (!item) {
+ qWarning("QGraphicsLinearLayout::insertItem: cannot insert null item");
+ return;
+ }
+ d->addChildLayoutItem(item);
+
+ Q_ASSERT(item);
+ d->fixIndex(&index);
+ d->engine.insertRow(index, d->orientation);
+ new QGridLayoutItem(&d->engine, item, d->gridRow(index), d->gridColumn(index));
+ invalidate();
+}
+
+/*!
+ Inserts a stretch of \a stretch at \a index, or before any item that is
+ currently at \a index.
+
+ \sa addStretch(), setStretchFactor(), setItemSpacing(), insertItem()
+*/
+void QGraphicsLinearLayout::insertStretch(int index, int stretch)
+{
+ Q_D(QGraphicsLinearLayout);
+ d->fixIndex(&index);
+ d->engine.insertRow(index, d->orientation);
+ d->engine.setRowStretchFactor(index, stretch, d->orientation);
+ invalidate();
+}
+
+/*!
+ Removes \a item from the layout without destroying it. Ownership of
+ \a item is transferred to the caller.
+
+ \sa removeAt(), insertItem()
+*/
+void QGraphicsLinearLayout::removeItem(QGraphicsLayoutItem *item)
+{
+ Q_D(QGraphicsLinearLayout);
+ if (QGridLayoutItem *gridItem = d->engine.findLayoutItem(item)) {
+ item->setParentLayoutItem(0);
+ d->removeGridItem(gridItem);
+ delete gridItem;
+ invalidate();
+ }
+}
+
+/*!
+ Removes the item at \a index without destroying it. Ownership of the item
+ is transferred to the caller.
+
+ \sa removeItem(), insertItem()
+*/
+void QGraphicsLinearLayout::removeAt(int index)
+{
+ Q_D(QGraphicsLinearLayout);
+ if (QGridLayoutItem *gridItem = d->engine.itemAt(d->gridRow(index), d->gridColumn(index))) {
+ if (QGraphicsLayoutItem *layoutItem = gridItem->layoutItem())
+ layoutItem->setParentLayoutItem(0);
+ d->removeGridItem(gridItem);
+ delete gridItem;
+ invalidate();
+ }
+}
+
+/*!
+ Sets the layout's spacing to \a spacing. Spacing refers to the
+ vertical and horizontal distances between items.
+
+ \sa setItemSpacing(), setStretchFactor(), QGraphicsGridLayout::setSpacing()
+*/
+void QGraphicsLinearLayout::setSpacing(qreal spacing)
+{
+ Q_D(QGraphicsLinearLayout);
+ if (spacing < 0) {
+ qWarning("QGraphicsLinearLayout::setSpacing: invalid spacing %g", spacing);
+ return;
+ }
+ d->engine.setSpacing(spacing, Qt::Horizontal | Qt::Vertical);
+ invalidate();
+}
+
+/*!
+ Returns the layout's spacing. Spacing refers to the
+ vertical and horizontal distances between items.
+
+ \sa setSpacing()
+ */
+qreal QGraphicsLinearLayout::spacing() const
+{
+ Q_D(const QGraphicsLinearLayout);
+ return d->engine.spacing(d->styleInfo(), d->orientation);
+}
+
+/*!
+ Sets the spacing after item at \a index to \a spacing.
+*/
+void QGraphicsLinearLayout::setItemSpacing(int index, qreal spacing)
+{
+ Q_D(QGraphicsLinearLayout);
+ d->engine.setRowSpacing(index, spacing, d->orientation);
+ invalidate();
+}
+/*!
+ Returns the spacing after item at \a index.
+*/
+qreal QGraphicsLinearLayout::itemSpacing(int index) const
+{
+ Q_D(const QGraphicsLinearLayout);
+ return d->engine.rowSpacing(index, d->orientation);
+}
+
+/*!
+ Sets the stretch factor for \a item to \a stretch. If an item's stretch
+ factor changes, this function will invalidate the layout.
+
+ Setting \a stretch to 0 removes the stretch factor from the item, and is
+ effectively equivalent to setting \a stretch to 1.
+
+ \sa stretchFactor()
+*/
+void QGraphicsLinearLayout::setStretchFactor(QGraphicsLayoutItem *item, int stretch)
+{
+ Q_D(QGraphicsLinearLayout);
+ if (!item) {
+ qWarning("QGraphicsLinearLayout::setStretchFactor: cannot assign"
+ " a stretch factor to a null item");
+ return;
+ }
+ if (stretchFactor(item) == stretch)
+ return;
+ d->engine.setStretchFactor(item, stretch, d->orientation);
+ invalidate();
+}
+
+/*!
+ Returns the stretch factor for \a item. The default stretch factor is 0,
+ meaning that the item has no assigned stretch factor.
+
+ \sa setStretchFactor()
+*/
+int QGraphicsLinearLayout::stretchFactor(QGraphicsLayoutItem *item) const
+{
+ Q_D(const QGraphicsLinearLayout);
+ if (!item) {
+ qWarning("QGraphicsLinearLayout::setStretchFactor: cannot return"
+ " a stretch factor for a null item");
+ return 0;
+ }
+ return d->engine.stretchFactor(item, d->orientation);
+}
+
+/*!
+ Sets the alignment of \a item to \a alignment. If \a item's alignment
+ changes, the layout is automatically invalidated.
+
+ \sa alignment(), invalidate()
+*/
+void QGraphicsLinearLayout::setAlignment(QGraphicsLayoutItem *item, Qt::Alignment alignment)
+{
+ Q_D(QGraphicsLinearLayout);
+ if (this->alignment(item) == alignment)
+ return;
+ d->engine.setAlignment(item, alignment);
+ invalidate();
+}
+
+/*!
+ Returns the alignment for \a item. The default alignment is
+ Qt::AlignCenter.
+
+ The alignment decides how the item is positioned within its assigned space
+ in the case where there's more space available in the layout than the
+ widgets can occupy.
+
+ \sa setAlignment()
+*/
+Qt::Alignment QGraphicsLinearLayout::alignment(QGraphicsLayoutItem *item) const
+{
+ Q_D(const QGraphicsLinearLayout);
+ return d->engine.alignment(item);
+}
+
+#if 0 // ###
+QSizePolicy::ControlTypes QGraphicsLinearLayout::controlTypes(LayoutSide side) const
+{
+ return d->engine.controlTypes(side);
+}
+#endif
+
+/*!
+ \reimp
+*/
+int QGraphicsLinearLayout::count() const
+{
+ Q_D(const QGraphicsLinearLayout);
+ return d->engine.rowCount(d->orientation);
+}
+
+/*!
+ \reimp
+*/
+QGraphicsLayoutItem *QGraphicsLinearLayout::itemAt(int index) const
+{
+ Q_D(const QGraphicsLinearLayout);
+ QGraphicsLayoutItem *item = 0;
+ if (QGridLayoutItem *gridItem = d->engine.itemAt(d->gridRow(index), d->gridColumn(index)))
+ item = gridItem->layoutItem();
+ return item;
+}
+
+/*!
+ \reimp
+*/
+void QGraphicsLinearLayout::setGeometry(const QRectF &rect)
+{
+ Q_D(QGraphicsLinearLayout);
+ QGraphicsLayout::setGeometry(rect);
+ QRectF effectiveRect = geometry();
+ qreal left, top, right, bottom;
+ getContentsMargins(&left, &top, &right, &bottom);
+ Qt::LayoutDirection visualDir = d->visualDirection();
+ d->engine.setVisualDirection(visualDir);
+ if (visualDir == Qt::RightToLeft)
+ qSwap(left, right);
+ effectiveRect.adjust(+left, +top, -right, -bottom);
+#ifdef QT_DEBUG
+ if (qt_graphicsLayoutDebug()) {
+ static int counter = 0;
+ qDebug() << counter++ << "QGraphicsLinearLayout::setGeometry - " << rect;
+ dump(1);
+ }
+#endif
+ d->engine.setGeometries(d->styleInfo(), effectiveRect);
+#ifdef QT_DEBUG
+ if (qt_graphicsLayoutDebug()) {
+ qDebug() << "post dump";
+ dump(1);
+ }
+#endif
+}
+
+/*!
+ \reimp
+*/
+QSizeF QGraphicsLinearLayout::sizeHint(Qt::SizeHint which, const QSizeF &constraint) const
+{
+ Q_D(const QGraphicsLinearLayout);
+ qreal left, top, right, bottom;
+ getContentsMargins(&left, &top, &right, &bottom);
+ return d->engine.sizeHint(d->styleInfo(), which , constraint) + QSizeF(left + right, top + bottom);
+}
+
+/*!
+ \reimp
+*/
+void QGraphicsLinearLayout::invalidate()
+{
+ Q_D(QGraphicsLinearLayout);
+ d->engine.invalidate();
+ QGraphicsLayout::invalidate();
+}
+
+#ifdef QT_DEBUG
+void QGraphicsLinearLayout::dump(int indent) const
+{
+ if (qt_graphicsLayoutDebug()) {
+ Q_D(const QGraphicsLinearLayout);
+ qDebug("%*s%s layout", indent, "",
+ d->orientation == Qt::Horizontal ? "Horizontal" : "Vertical");
+ d->engine.dump(indent + 1);
+ }
+}
+#endif
+
+QT_END_NAMESPACE
+
+#endif //QT_NO_GRAPHICSVIEW
diff --git a/src/gui/graphicsview/qgraphicslinearlayout.h b/src/gui/graphicsview/qgraphicslinearlayout.h
new file mode 100644
index 0000000000..05ad325ba8
--- /dev/null
+++ b/src/gui/graphicsview/qgraphicslinearlayout.h
@@ -0,0 +1,121 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QGRAPHICSLINEARLAYOUT_H
+#define QGRAPHICSLINEARLAYOUT_H
+
+#include <QtGui/qgraphicsitem.h>
+#include <QtGui/qgraphicslayout.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+#if !defined(QT_NO_GRAPHICSVIEW) || (QT_EDITION & QT_MODULE_GRAPHICSVIEW) != QT_MODULE_GRAPHICSVIEW
+
+class QGraphicsLinearLayoutPrivate;
+
+class Q_GUI_EXPORT QGraphicsLinearLayout : public QGraphicsLayout
+{
+public:
+ QGraphicsLinearLayout(QGraphicsLayoutItem *parent = 0);
+ QGraphicsLinearLayout(Qt::Orientation orientation, QGraphicsLayoutItem *parent = 0);
+ virtual ~QGraphicsLinearLayout();
+
+ void setOrientation(Qt::Orientation orientation);
+ Qt::Orientation orientation() const;
+
+ inline void addItem(QGraphicsLayoutItem *item) { insertItem(-1, item); }
+ inline void addStretch(int stretch = 1) { insertStretch(-1, stretch); }
+
+ void insertItem(int index, QGraphicsLayoutItem *item);
+ void insertStretch(int index, int stretch = 1);
+
+ void removeItem(QGraphicsLayoutItem *item);
+ void removeAt(int index);
+
+ void setSpacing(qreal spacing);
+ qreal spacing() const;
+ void setItemSpacing(int index, qreal spacing);
+ qreal itemSpacing(int index) const;
+
+ void setStretchFactor(QGraphicsLayoutItem *item, int stretch);
+ int stretchFactor(QGraphicsLayoutItem *item) const;
+
+ void setAlignment(QGraphicsLayoutItem *item, Qt::Alignment alignment);
+ Qt::Alignment alignment(QGraphicsLayoutItem *item) const;
+
+ void setGeometry(const QRectF &rect);
+
+ int count() const;
+ QGraphicsLayoutItem *itemAt(int index) const;
+
+ void invalidate();
+ QSizeF sizeHint(Qt::SizeHint which, const QSizeF &constraint = QSizeF()) const;
+
+#if 0 // ###
+ Q5SizePolicy::ControlTypes controlTypes(LayoutSide side) const;
+#endif
+
+#ifdef QT_DEBUG
+ void dump(int indent = 0) const;
+#endif
+
+protected:
+#if 0
+ QSize contentsSizeHint(Qt::SizeHint which, const QSize &constraint = QSize()) const;
+#endif
+
+private:
+ Q_DISABLE_COPY(QGraphicsLinearLayout)
+ Q_DECLARE_PRIVATE(QGraphicsLinearLayout)
+};
+
+#endif
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif
+
diff --git a/src/gui/graphicsview/qgraphicsproxywidget.cpp b/src/gui/graphicsview/qgraphicsproxywidget.cpp
new file mode 100644
index 0000000000..1d2721b2d5
--- /dev/null
+++ b/src/gui/graphicsview/qgraphicsproxywidget.cpp
@@ -0,0 +1,1494 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qglobal.h"
+
+#ifndef QT_NO_GRAPHICSVIEW
+
+#include "qgraphicslayout.h"
+#include "qgraphicsproxywidget.h"
+#include "private/qgraphicsproxywidget_p.h"
+#include "private/qwidget_p.h"
+#include "private/qapplication_p.h"
+
+#include <QtCore/qdebug.h>
+#include <QtGui/qevent.h>
+#include <QtGui/qgraphicsscene.h>
+#include <QtGui/qgraphicssceneevent.h>
+#include <QtGui/qlayout.h>
+#include <QtGui/qpainter.h>
+#include <QtGui/qstyleoption.h>
+#include <QtGui/qgraphicsview.h>
+
+QT_BEGIN_NAMESPACE
+
+//#define GRAPHICSPROXYWIDGET_DEBUG
+
+/*!
+ \class QGraphicsProxyWidget
+ \brief The QGraphicsProxyWidget class provides a proxy layer for embedding
+ a QWidget in a QGraphicsScene.
+ \since 4.4
+ \ingroup multimedia
+ \ingroup graphicsview-api
+
+ QGraphicsProxyWidget embeds QWidget-based widgets, for example, a
+ QPushButton, QFontComboBox, or even QFileDialog, into
+ QGraphicsScene. It forwards events between the two objects and
+ translates between QWidget's integer-based geometry and
+ QGraphicsWidget's qreal-based geometry. QGraphicsProxyWidget
+ supports all core features of QWidget, including tab focus,
+ keyboard input, Drag & Drop, and popups. You can also embed
+ complex widgets, e.g., widgets with subwidgets.
+
+ Example:
+
+ \snippet doc/src/snippets/code/src_gui_graphicsview_qgraphicsproxywidget.cpp 0
+
+ QGraphicsProxyWidget takes care of automatically embedding popup children
+ of embedded widgets through creating a child proxy for each popup. This
+ means that when an embedded QComboBox shows its popup list, a new
+ QGraphicsProxyWidget is created automatically, embedding the popup, and
+ positioning it correctly.
+
+ \section1 Embedding a Widget with QGraphicsProxyWidget
+
+ There are two ways to embed a widget using QGraphicsProxyWidget. The most
+ common way is to pass a widget pointer to QGraphicsScene::addWidget()
+ together with any relevant \l Qt::WindowFlags. This function returns a
+ pointer to a QGraphicsProxyWidget. You can then choose to reparent or
+ position either the proxy, or the embedded widget itself.
+
+ For example, in the code snippet below, we embed a group box into the proxy:
+
+ \snippet doc/src/snippets/code/src_gui_graphicsview_qgraphicsproxywidget.cpp 1
+
+ The image below is the output obtained with its contents margin and
+ contents rect labeled.
+
+ \image qgraphicsproxywidget-embed.png
+
+ Alternatively, you can start by creating a new QGraphicsProxyWidget item,
+ and then call setWidget() to embed a QWidget later. The widget() function
+ returns a pointer to the embedded widget. QGraphicsProxyWidget shares
+ ownership with QWidget, so if either of the two widgets are destroyed, the
+ other widget will be automatically destroyed as well.
+
+ \section1 Synchronizing Widget States
+
+ QGraphicsProxyWidget keeps its state in sync with the embedded widget. For
+ example, if the proxy is hidden or disabled, the embedded widget will be
+ hidden or disabled as well, and vice versa. When the widget is embedded by
+ calling addWidget(), QGraphicsProxyWidget copies the state from the widget
+ into the proxy, and after that, the two will stay synchronized where
+ possible. By default, when you embed a widget into a proxy, both the widget
+ and the proxy will be visible because a QGraphicsWidget is visible when
+ created (you do not have to call show()). If you explicitly hide the
+ embedded widget, the proxy will also become invisible.
+
+ Example:
+
+ \snippet doc/src/snippets/code/src_gui_graphicsview_qgraphicsproxywidget.cpp 2
+
+ QGraphicsProxyWidget maintains symmetry for the following states:
+
+ \table
+ \header \o QWidget state \o QGraphicsProxyWidget state \o Notes
+ \row \o QWidget::enabled
+ \o QGraphicsProxyWidget::enabled
+ \o
+ \row \o QWidget::visible
+ \o QGraphicsProxyWidget::visible
+ \o The explicit state is also symmetric.
+ \row \o QWidget::geometry
+ \o QGraphicsProxyWidget::geometry
+ \o Geometry is only guaranteed to be symmetric while
+ the embedded widget is visible.
+ \row \o QWidget::layoutDirection
+ \o QGraphicsProxyWidget::layoutDirection
+ \o
+ \row \o QWidget::style
+ \o QGraphicsProxyWidget::style
+ \o
+ \row \o QWidget::palette
+ \o QGraphicsProxyWidget::palette
+ \o
+ \row \o QWidget::font
+ \o QGraphicsProxyWidget::font
+ \o
+ \row \o QWidget::cursor
+ \o QGraphicsProxyWidget::cursor
+ \o The embedded widget overrides the proxy widget
+ cursor. The proxy cursor changes depending on
+ which embedded subwidget is currently under the
+ mouse.
+ \row \o QWidget::sizeHint()
+ \o QGraphicsProxyWidget::sizeHint()
+ \o All size hint functionality from the embedded
+ widget is forwarded by the proxy.
+ \row \o QWidget::getContentsMargins()
+ \o QGraphicsProxyWidget::getContentsMargins()
+ \o Updated once by setWidget().
+ \row \o QWidget::windowTitle
+ \o QGraphicsProxyWidget::windowTitle
+ \o Updated once by setWidget().
+ \endtable
+
+ \note QGraphicsScene keeps the embedded widget in a special state that
+ prevents it from disturbing other widgets (both embedded and not embedded)
+ while the widget is embedded. In this state, the widget may differ slightly
+ in behavior from when it is not embedded.
+
+ \sa QGraphicsScene::addWidget(), QGraphicsWidget
+*/
+
+extern bool qt_sendSpontaneousEvent(QObject *, QEvent *);
+
+/*!
+ \internal
+*/
+void QGraphicsProxyWidgetPrivate::init()
+{
+ Q_Q(QGraphicsProxyWidget);
+ q->setFocusPolicy(Qt::WheelFocus);
+ q->setAcceptDrops(true);
+}
+
+/*!
+ \internal
+*/
+void QGraphicsProxyWidgetPrivate::sendWidgetMouseEvent(QGraphicsSceneHoverEvent *event)
+{
+ QGraphicsSceneMouseEvent mouseEvent(QEvent::GraphicsSceneMouseMove);
+ mouseEvent.setPos(event->pos());
+ mouseEvent.setScreenPos(event->screenPos());
+ mouseEvent.setButton(Qt::NoButton);
+ mouseEvent.setButtons(0);
+ mouseEvent.setModifiers(event->modifiers());
+ sendWidgetMouseEvent(&mouseEvent);
+ event->setAccepted(mouseEvent.isAccepted());
+}
+
+/*!
+ \internal
+*/
+void QGraphicsProxyWidgetPrivate::sendWidgetMouseEvent(QGraphicsSceneMouseEvent *event)
+{
+ if (!event || !widget || !widget->isVisible())
+ return;
+ Q_Q(QGraphicsProxyWidget);
+
+ // Find widget position and receiver.
+ QPointF pos = event->pos();
+ QPointer<QWidget> alienWidget = widget->childAt(pos.toPoint());
+ QPointer<QWidget> receiver = alienWidget ? alienWidget : widget;
+
+ if (QWidgetPrivate::nearestGraphicsProxyWidget(receiver) != q)
+ return; //another proxywidget will handle the events
+
+ // Translate QGraphicsSceneMouse events to QMouseEvents.
+ QEvent::Type type = QEvent::None;
+ switch (event->type()) {
+ case QEvent::GraphicsSceneMousePress:
+ type = QEvent::MouseButtonPress;
+ if (!embeddedMouseGrabber)
+ embeddedMouseGrabber = receiver;
+ else
+ receiver = embeddedMouseGrabber;
+ break;
+ case QEvent::GraphicsSceneMouseRelease:
+ type = QEvent::MouseButtonRelease;
+ if (embeddedMouseGrabber)
+ receiver = embeddedMouseGrabber;
+ break;
+ case QEvent::GraphicsSceneMouseDoubleClick:
+ type = QEvent::MouseButtonDblClick;
+ if (!embeddedMouseGrabber)
+ embeddedMouseGrabber = receiver;
+ else
+ receiver = embeddedMouseGrabber;
+ break;
+ case QEvent::GraphicsSceneMouseMove:
+ type = QEvent::MouseMove;
+ if (embeddedMouseGrabber)
+ receiver = embeddedMouseGrabber;
+ break;
+ default:
+ Q_ASSERT_X(false, "QGraphicsProxyWidget", "internal error");
+ break;
+ }
+
+ if (!lastWidgetUnderMouse) {
+ QApplicationPrivate::dispatchEnterLeave(embeddedMouseGrabber ? embeddedMouseGrabber : widget, 0);
+ lastWidgetUnderMouse = widget;
+ }
+
+ // Map event position from us to the receiver
+ pos = mapToReceiver(pos, receiver);
+
+ // Send mouse event.
+ QMouseEvent *mouseEvent = QMouseEvent::createExtendedMouseEvent(type, pos,
+ receiver->mapToGlobal(pos.toPoint()), event->button(),
+ event->buttons(), event->modifiers());
+
+ QWidget *embeddedMouseGrabberPtr = (QWidget *)embeddedMouseGrabber;
+ QApplicationPrivate::sendMouseEvent(receiver, mouseEvent, alienWidget, widget,
+ &embeddedMouseGrabberPtr, lastWidgetUnderMouse);
+ embeddedMouseGrabber = embeddedMouseGrabberPtr;
+
+ // Handle enter/leave events when last button is released from mouse
+ // grabber child widget.
+ if (embeddedMouseGrabber && type == QEvent::MouseButtonRelease && !event->buttons()) {
+ Q_Q(QGraphicsProxyWidget);
+ if (q->rect().contains(event->pos()) && q->acceptsHoverEvents())
+ lastWidgetUnderMouse = alienWidget ? alienWidget : widget;
+ else // released on the frame our outside the item, or doesn't accept hover events.
+ lastWidgetUnderMouse = 0;
+
+ QApplicationPrivate::dispatchEnterLeave(lastWidgetUnderMouse, embeddedMouseGrabber);
+ embeddedMouseGrabber = 0;
+
+#ifndef QT_NO_CURSOR
+ // ### Restore the cursor, don't override it.
+ if (!lastWidgetUnderMouse)
+ q->unsetCursor();
+#endif
+ }
+
+ event->setAccepted(mouseEvent->isAccepted());
+ delete mouseEvent;
+}
+
+void QGraphicsProxyWidgetPrivate::sendWidgetKeyEvent(QKeyEvent *event)
+{
+ Q_Q(QGraphicsProxyWidget);
+ if (!event || !widget || !widget->isVisible())
+ return;
+
+ QPointer<QWidget> receiver = widget->focusWidget();
+ if (!receiver)
+ receiver = widget;
+ Q_ASSERT(receiver);
+
+ do {
+ bool res = QApplication::sendEvent(receiver, event);
+ if ((res && event->isAccepted()) || (q->isWindow() && receiver == widget))
+ break;
+ receiver = receiver->parentWidget();
+ } while (receiver);
+}
+
+/*!
+ \internal
+*/
+void QGraphicsProxyWidgetPrivate::removeSubFocusHelper(QWidget *widget, Qt::FocusReason reason)
+{
+ QFocusEvent event(QEvent::FocusOut, reason);
+ QPointer<QWidget> widgetGuard = widget;
+ QApplication::sendEvent(widget, &event);
+ if (widgetGuard && event.isAccepted())
+ QApplication::sendEvent(widget->style(), &event);
+}
+
+/*!
+ \internal
+
+ Reimplemented from QGraphicsItemPrivate. ### Qt 5: Move impl to
+ reimplementation QGraphicsProxyWidget::inputMethodQuery().
+*/
+QVariant QGraphicsProxyWidgetPrivate::inputMethodQueryHelper(Qt::InputMethodQuery query) const
+{
+ Q_Q(const QGraphicsProxyWidget);
+ if (!widget || !q->hasFocus())
+ return QVariant();
+
+ QWidget *focusWidget = widget->focusWidget();
+ if (!focusWidget)
+ focusWidget = widget;
+ QVariant v = focusWidget->inputMethodQuery(query);
+ QPointF focusWidgetPos = q->subWidgetRect(focusWidget).topLeft();
+ switch (v.type()) {
+ case QVariant::RectF:
+ v = v.toRectF().translated(focusWidgetPos);
+ break;
+ case QVariant::PointF:
+ v = v.toPointF() + focusWidgetPos;
+ break;
+ case QVariant::Rect:
+ v = v.toRect().translated(focusWidgetPos.toPoint());
+ break;
+ case QVariant::Point:
+ v = v.toPoint() + focusWidgetPos.toPoint();
+ break;
+ default:
+ break;
+ }
+ return v;
+}
+
+/*!
+ \internal
+*/
+QWidget *QGraphicsProxyWidgetPrivate::findFocusChild(QWidget *child, bool next) const
+{
+ if (!widget)
+ return 0;
+
+ // Run around the focus chain until we find a widget that can take tab focus.
+ if (!child) {
+ child = next ? (QWidget *)widget : widget->d_func()->focus_prev;
+ } else {
+ child = next ? child->d_func()->focus_next : child->d_func()->focus_prev;
+ if ((next && child == widget) || (!next && child == widget->d_func()->focus_prev)) {
+ return 0;
+ }
+ }
+
+ QWidget *oldChild = child;
+ do {
+ if (child->isEnabled()
+ && child->isVisibleTo(widget)
+ && (child->focusPolicy() & Qt::TabFocus)) {
+ return child;
+ }
+ child = next ? child->d_func()->focus_next : child->d_func()->focus_prev;
+ } while (child != oldChild && !(next && child == widget) && !(!next && child == widget->d_func()->focus_prev));
+ return 0;
+}
+
+/*!
+ \internal
+*/
+void QGraphicsProxyWidgetPrivate::_q_removeWidgetSlot()
+{
+ Q_Q(QGraphicsProxyWidget);
+ widget = 0;
+ delete q;
+}
+
+/*!
+ \internal
+*/
+void QGraphicsProxyWidgetPrivate::updateWidgetGeometryFromProxy()
+{
+}
+
+/*!
+ \internal
+*/
+void QGraphicsProxyWidgetPrivate::updateProxyGeometryFromWidget()
+{
+ Q_Q(QGraphicsProxyWidget);
+ if (!widget)
+ return;
+
+ QRectF widgetGeometry = widget->geometry();
+ QWidget *parentWidget = widget->parentWidget();
+ if (widget->isWindow()) {
+ QGraphicsProxyWidget *proxyParent = 0;
+ if (parentWidget && (proxyParent = qobject_cast<QGraphicsProxyWidget *>(q->parentWidget()))) {
+ // Nested window proxy (e.g., combobox popup), map widget to the
+ // parent widget's global coordinates, and map that to the parent
+ // proxy's child coordinates.
+ widgetGeometry.moveTo(proxyParent->subWidgetRect(parentWidget).topLeft()
+ + parentWidget->mapFromGlobal(widget->pos()));
+ }
+ }
+
+ // Adjust to size hint if the widget has never been resized.
+ if (!widget->size().isValid())
+ widgetGeometry.setSize(widget->sizeHint());
+
+ // Assign new geometry.
+ posChangeMode = QGraphicsProxyWidgetPrivate::WidgetToProxyMode;
+ sizeChangeMode = QGraphicsProxyWidgetPrivate::WidgetToProxyMode;
+ q->setGeometry(widgetGeometry);
+ posChangeMode = QGraphicsProxyWidgetPrivate::NoMode;
+ sizeChangeMode = QGraphicsProxyWidgetPrivate::NoMode;
+}
+
+/*!
+ \internal
+
+ Embeds \a subWin as a subwindow of this proxy widget. \a subWin must be a top-level
+ widget and a descendant of the widget managed by this proxy. A separate subproxy
+ will be created as a child of this proxy widget to manage \a subWin.
+*/
+void QGraphicsProxyWidgetPrivate::embedSubWindow(QWidget *subWin)
+{
+ QWExtra *extra;
+ if (!((extra = subWin->d_func()->extra) && extra->proxyWidget)) {
+ QGraphicsProxyWidget *subProxy = new QGraphicsProxyWidget(q_func());
+ subProxy->d_func()->setWidget_helper(subWin, false);
+ }
+}
+
+/*!
+ \internal
+
+ Removes ("unembeds") \a subWin and deletes the proxy holder item. This can
+ happen when QWidget::setParent() reparents the embedded window out of
+ "embedded space".
+*/
+void QGraphicsProxyWidgetPrivate::unembedSubWindow(QWidget *subWin)
+{
+ foreach (QGraphicsItem *child, children) {
+ if (child->isWidget()) {
+ if (QGraphicsProxyWidget *proxy = qobject_cast<QGraphicsProxyWidget *>(static_cast<QGraphicsWidget *>(child))) {
+ if (proxy->widget() == subWin) {
+ proxy->setWidget(0);
+ scene->removeItem(proxy);
+ delete proxy;
+ return;
+ }
+ }
+ }
+ }
+}
+
+bool QGraphicsProxyWidgetPrivate::isProxyWidget() const
+{
+ return true;
+}
+
+/*!
+ \internal
+*/
+QPointF QGraphicsProxyWidgetPrivate::mapToReceiver(const QPointF &pos, const QWidget *receiver) const
+{
+ QPointF p = pos;
+ // Map event position from us to the receiver, preserving its
+ // precision (don't use QWidget::mapFrom here).
+ while (receiver && receiver != widget) {
+ p -= QPointF(receiver->pos());
+ receiver = receiver->parentWidget();
+ }
+ return p;
+}
+
+/*!
+ Constructs a new QGraphicsProxy widget. \a parent and \a wFlags are passed
+ to QGraphicsItem's constructor.
+*/
+QGraphicsProxyWidget::QGraphicsProxyWidget(QGraphicsItem *parent, Qt::WindowFlags wFlags)
+ : QGraphicsWidget(*new QGraphicsProxyWidgetPrivate, parent, 0, wFlags)
+{
+ Q_D(QGraphicsProxyWidget);
+ d->init();
+}
+
+/*!
+ Destroys the proxy widget and any embedded widget.
+*/
+QGraphicsProxyWidget::~QGraphicsProxyWidget()
+{
+ Q_D(QGraphicsProxyWidget);
+ if (d->widget) {
+ QObject::disconnect(d->widget, SIGNAL(destroyed()), this, SLOT(_q_removeWidgetSlot()));
+ delete d->widget;
+ }
+}
+
+/*!
+ Embeds \a widget into this proxy widget. The embedded widget must reside
+ exclusively either inside or outside of Graphics View. You cannot embed a
+ widget as long as it is is visible elsewhere in the UI, at the same time.
+
+ \a widget must be a top-level widget whose parent is 0.
+
+ When the widget is embedded, its state (e.g., visible, enabled, geometry,
+ size hints) is copied into the proxy widget. If the embedded widget is
+ explicitly hidden or disabled, the proxy widget will become explicitly
+ hidden or disabled after embedding is complete. The class documentation
+ has a full overview over the shared state.
+
+ After this function returns, QGraphicsProxyWidget will keep its state
+ synchronized with that of \a widget whenever possible.
+
+ If a widget is already embedded by this proxy when this function is
+ called, that widget will first be automatically unembedded. Passing 0 for
+ the \a widget argument will only unembed the widget, and the ownership of
+ the currently embedded widget will be passed on to the caller.
+ Every child widget that are embedded will also be embedded and their proxy
+ widget destroyed.
+
+ Note that widgets with the Qt::WA_PaintOnScreen widget attribute
+ set and widgets that wrap an external application or controller
+ cannot be embedded. Examples are QGLWidget and QAxWidget.
+
+ \sa widget()
+*/
+void QGraphicsProxyWidget::setWidget(QWidget *widget)
+{
+ Q_D(QGraphicsProxyWidget);
+ d->setWidget_helper(widget, true);
+}
+
+void QGraphicsProxyWidgetPrivate::setWidget_helper(QWidget *newWidget, bool autoShow)
+{
+ Q_Q(QGraphicsProxyWidget);
+ if (newWidget == widget)
+ return;
+ if (widget) {
+ QObject::disconnect(widget, SIGNAL(destroyed()), q, SLOT(_q_removeWidgetSlot()));
+ widget->removeEventFilter(q);
+ widget->setAttribute(Qt::WA_DontShowOnScreen, false);
+ widget->d_func()->extra->proxyWidget = 0;
+ resolveFont(inheritedFontResolveMask);
+ resolvePalette(inheritedPaletteResolveMask);
+ widget->update();
+
+ foreach (QGraphicsItem *child, q->childItems()) {
+ if (child->d_ptr->isProxyWidget()) {
+ QGraphicsProxyWidget *childProxy = static_cast<QGraphicsProxyWidget *>(child);
+ QWidget * parent = childProxy->widget();
+ while (parent->parentWidget() != 0) {
+ if (parent == widget)
+ break;
+ parent = parent->parentWidget();
+ }
+ if (!childProxy->widget() || parent != widget)
+ continue;
+ childProxy->setWidget(0);
+ delete childProxy;
+ }
+ }
+
+ widget = 0;
+#ifndef QT_NO_CURSOR
+ q->unsetCursor();
+#endif
+ q->setAcceptHoverEvents(false);
+ if (!newWidget)
+ q->update();
+ }
+ if (!newWidget)
+ return;
+ if (!newWidget->isWindow()) {
+ QWExtra *extra = newWidget->parentWidget()->d_func()->extra;
+ if (!extra || !extra->proxyWidget) {
+ qWarning("QGraphicsProxyWidget::setWidget: cannot embed widget %p "
+ "which is not a toplevel widget, and is not a child of an embedded widget", newWidget);
+ return;
+ }
+ }
+
+ // Register this proxy within the widget's private.
+ // ### This is a bit backdoorish
+ QWExtra *extra = newWidget->d_func()->extra;
+ if (!extra) {
+ newWidget->d_func()->createExtra();
+ extra = newWidget->d_func()->extra;
+ }
+ QGraphicsProxyWidget **proxyWidget = &extra->proxyWidget;
+ if (*proxyWidget) {
+ if (*proxyWidget != q) {
+ qWarning("QGraphicsProxyWidget::setWidget: cannot embed widget %p"
+ "; already embedded", newWidget);
+ }
+ return;
+ }
+ *proxyWidget = q;
+
+ newWidget->setAttribute(Qt::WA_DontShowOnScreen);
+ newWidget->ensurePolished();
+ // Do not wait for this widget to close before the app closes ###
+ // shouldn't this widget inherit the attribute?
+ newWidget->setAttribute(Qt::WA_QuitOnClose, false);
+ q->setAcceptHoverEvents(true);
+
+ if (newWidget->testAttribute(Qt::WA_NoSystemBackground))
+ q->setAttribute(Qt::WA_NoSystemBackground);
+ if (newWidget->testAttribute(Qt::WA_OpaquePaintEvent))
+ q->setAttribute(Qt::WA_OpaquePaintEvent);
+
+ widget = newWidget;
+
+ // Changes only go from the widget to the proxy.
+ enabledChangeMode = QGraphicsProxyWidgetPrivate::WidgetToProxyMode;
+ visibleChangeMode = QGraphicsProxyWidgetPrivate::WidgetToProxyMode;
+ posChangeMode = QGraphicsProxyWidgetPrivate::WidgetToProxyMode;
+ sizeChangeMode = QGraphicsProxyWidgetPrivate::WidgetToProxyMode;
+
+ if ((autoShow && !newWidget->testAttribute(Qt::WA_WState_ExplicitShowHide)) || !newWidget->testAttribute(Qt::WA_WState_Hidden)) {
+ newWidget->show();
+ }
+
+ // Copy the state from the widget onto the proxy.
+#ifndef QT_NO_CURSOR
+ if (newWidget->testAttribute(Qt::WA_SetCursor))
+ q->setCursor(widget->cursor());
+#endif
+ Qt::WFlags flags = newWidget->windowFlags();
+ if (newWidget->windowType() == Qt::Window)
+ flags &= ~Qt::Window;
+ q->setWindowFlags(flags);
+ q->setEnabled(newWidget->isEnabled());
+ q->setVisible(newWidget->isVisible());
+ q->setLayoutDirection(newWidget->layoutDirection());
+ if (newWidget->testAttribute(Qt::WA_SetStyle))
+ q->setStyle(widget->style());
+
+ resolveFont(inheritedFontResolveMask);
+ resolvePalette(inheritedPaletteResolveMask);
+
+ if (!newWidget->testAttribute(Qt::WA_Resized))
+ newWidget->adjustSize();
+
+ int left, top, right, bottom;
+ newWidget->getContentsMargins(&left, &top, &right, &bottom);
+ q->setContentsMargins(left, top, right, bottom);
+ q->setWindowTitle(newWidget->windowTitle());
+
+ // size policies and constraints..
+ q->setSizePolicy(newWidget->sizePolicy());
+ QSize sz = newWidget->minimumSize();
+ q->setMinimumSize(sz.isNull() ? QSizeF() : QSizeF(sz));
+ sz = newWidget->maximumSize();
+ q->setMaximumSize(sz.isNull() ? QSizeF() : QSizeF(sz));
+
+ updateProxyGeometryFromWidget();
+
+ // Hook up the event filter to keep the state up to date.
+ newWidget->installEventFilter(q);
+ QObject::connect(newWidget, SIGNAL(destroyed()), q, SLOT(_q_removeWidgetSlot()));
+
+ // Changes no longer go only from the widget to the proxy.
+ enabledChangeMode = QGraphicsProxyWidgetPrivate::NoMode;
+ visibleChangeMode = QGraphicsProxyWidgetPrivate::NoMode;
+ posChangeMode = QGraphicsProxyWidgetPrivate::NoMode;
+ sizeChangeMode = QGraphicsProxyWidgetPrivate::NoMode;
+}
+
+/*!
+ Returns a pointer to the embedded widget.
+
+ \sa setWidget()
+*/
+QWidget *QGraphicsProxyWidget::widget() const
+{
+ Q_D(const QGraphicsProxyWidget);
+ return d->widget;
+}
+
+/*!
+ Returns the rectangle for \a widget, which must be a descendant of
+ widget(), or widget() itself, in this proxy item's local coordinates.
+
+ If no widget is embedded, \a widget is 0, or \a widget is not a
+ descendant of the embedded widget, this function returns an empty QRectF.
+
+ \sa widget()
+*/
+QRectF QGraphicsProxyWidget::subWidgetRect(const QWidget *widget) const
+{
+ Q_D(const QGraphicsProxyWidget);
+ if (!widget || !d->widget)
+ return QRectF();
+ if (d->widget == widget || d->widget->isAncestorOf(widget))
+ return QRectF(widget->mapTo(d->widget, QPoint(0, 0)), widget->size());
+ return QRectF();
+}
+
+/*!
+ \reimp
+*/
+void QGraphicsProxyWidget::setGeometry(const QRectF &rect)
+{
+ Q_D(QGraphicsProxyWidget);
+ bool proxyResizesWidget = !d->posChangeMode && !d->sizeChangeMode;
+ if (proxyResizesWidget) {
+ d->posChangeMode = QGraphicsProxyWidgetPrivate::ProxyToWidgetMode;
+ d->sizeChangeMode = QGraphicsProxyWidgetPrivate::ProxyToWidgetMode;
+ }
+ QGraphicsWidget::setGeometry(rect);
+ if (proxyResizesWidget) {
+ d->posChangeMode = QGraphicsProxyWidgetPrivate::NoMode;
+ d->sizeChangeMode = QGraphicsProxyWidgetPrivate::NoMode;
+ }
+}
+
+/*!
+ \reimp
+*/
+QVariant QGraphicsProxyWidget::itemChange(GraphicsItemChange change,
+ const QVariant &value)
+{
+ Q_D(QGraphicsProxyWidget);
+
+ switch (change) {
+ case ItemPositionChange:
+ // The item's position is either changed directly on the proxy, in
+ // which case the position change should propagate to the widget,
+ // otherwise it happens as a side effect when filtering QEvent::Move.
+ if (!d->posChangeMode)
+ d->posChangeMode = QGraphicsProxyWidgetPrivate::ProxyToWidgetMode;
+ break;
+ case ItemPositionHasChanged:
+ // Move the internal widget if we're in widget-to-proxy
+ // mode. Otherwise the widget has already moved.
+ if (d->widget && d->posChangeMode != QGraphicsProxyWidgetPrivate::WidgetToProxyMode)
+ d->widget->move(value.toPoint());
+ if (d->posChangeMode == QGraphicsProxyWidgetPrivate::ProxyToWidgetMode)
+ d->posChangeMode = QGraphicsProxyWidgetPrivate::NoMode;
+ break;
+ case ItemVisibleChange:
+ if (!d->visibleChangeMode)
+ d->visibleChangeMode = QGraphicsProxyWidgetPrivate::ProxyToWidgetMode;
+ break;
+ case ItemVisibleHasChanged:
+ if (d->widget && d->visibleChangeMode != QGraphicsProxyWidgetPrivate::WidgetToProxyMode)
+ d->widget->setVisible(isVisible());
+ if (d->visibleChangeMode == QGraphicsProxyWidgetPrivate::ProxyToWidgetMode)
+ d->visibleChangeMode = QGraphicsProxyWidgetPrivate::NoMode;
+ break;
+ case ItemEnabledChange:
+ if (!d->enabledChangeMode)
+ d->enabledChangeMode = QGraphicsProxyWidgetPrivate::ProxyToWidgetMode;
+ break;
+ case ItemEnabledHasChanged:
+ if (d->widget && d->enabledChangeMode != QGraphicsProxyWidgetPrivate::WidgetToProxyMode)
+ d->widget->setEnabled(isEnabled());
+ if (d->enabledChangeMode == QGraphicsProxyWidgetPrivate::ProxyToWidgetMode)
+ d->enabledChangeMode = QGraphicsProxyWidgetPrivate::NoMode;
+ break;
+ default:
+ break;
+ }
+ return QGraphicsWidget::itemChange(change, value);
+}
+
+/*!
+ \reimp
+*/
+bool QGraphicsProxyWidget::event(QEvent *event)
+{
+ Q_D(QGraphicsProxyWidget);
+ if (!d->widget)
+ return QGraphicsWidget::event(event);
+
+ switch (event->type()) {
+ case QEvent::StyleChange:
+ // Propagate style changes to the embedded widget.
+ if (!d->styleChangeMode) {
+ d->styleChangeMode = QGraphicsProxyWidgetPrivate::ProxyToWidgetMode;
+ d->widget->setStyle(style());
+ d->styleChangeMode = QGraphicsProxyWidgetPrivate::NoMode;
+ }
+ break;
+ case QEvent::FontChange: {
+ // Propagate to widget.
+ QWidgetPrivate *wd = d->widget->d_func();
+ int mask = d->font.resolve() | d->inheritedFontResolveMask;
+ wd->inheritedFontResolveMask = mask;
+ wd->resolveFont();
+ break;
+ }
+ case QEvent::PaletteChange: {
+ // Propagate to widget.
+ QWidgetPrivate *wd = d->widget->d_func();
+ int mask = d->palette.resolve() | d->inheritedPaletteResolveMask;
+ wd->inheritedPaletteResolveMask = mask;
+ wd->resolvePalette();
+ break;
+ }
+ case QEvent::InputMethod: {
+ // Forward input method events if the focus widget enables
+ // input methods.
+ // ### Qt 4.5: this code must also go into a reimplementation
+ // of inputMethodEvent().
+ QWidget *focusWidget = d->widget->focusWidget();
+ if (focusWidget->testAttribute(Qt::WA_InputMethodEnabled))
+ QApplication::sendEvent(focusWidget, event);
+ break;
+ }
+ case QEvent::ShortcutOverride: {
+ QWidget *focusWidget = d->widget->focusWidget();
+ while (focusWidget) {
+ QApplication::sendEvent(focusWidget, event);
+ if (event->isAccepted())
+ return true;
+ focusWidget = focusWidget->parentWidget();
+ }
+ return false;
+ }
+ case QEvent::KeyPress: {
+ QKeyEvent *k = static_cast<QKeyEvent *>(event);
+ if (k->key() == Qt::Key_Tab || k->key() == Qt::Key_Backtab) {
+ if (!(k->modifiers() & (Qt::ControlModifier | Qt::AltModifier))) { //### Add MetaModifier?
+ QWidget *focusWidget = d->widget->focusWidget();
+ while (focusWidget) {
+ bool res = QApplication::sendEvent(focusWidget, event);
+ if ((res && event->isAccepted()) || (isWindow() && focusWidget == d->widget)) {
+ event->accept();
+ break;
+ }
+ focusWidget = focusWidget->parentWidget();
+ }
+ return true;
+ }
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ return QGraphicsWidget::event(event);
+}
+
+/*!
+ \reimp
+*/
+bool QGraphicsProxyWidget::eventFilter(QObject *object, QEvent *event)
+{
+ Q_D(QGraphicsProxyWidget);
+
+ if (object == d->widget) {
+ switch (event->type()) {
+ case QEvent::LayoutRequest:
+ updateGeometry();
+ break;
+ case QEvent::Resize:
+ // If the widget resizes itself, we resize the proxy too.
+ // Prevent feed-back by checking the geometry change mode.
+ if (!d->sizeChangeMode)
+ d->updateProxyGeometryFromWidget();
+ break;
+ case QEvent::Move:
+ // If the widget moves itself, we move the proxy too. Prevent
+ // feed-back by checking the geometry change mode.
+ if (!d->posChangeMode)
+ d->updateProxyGeometryFromWidget();
+ break;
+ case QEvent::Hide:
+ case QEvent::Show:
+ // If the widget toggles its visible state, the proxy will follow.
+ if (!d->visibleChangeMode) {
+ d->visibleChangeMode = QGraphicsProxyWidgetPrivate::WidgetToProxyMode;
+ setVisible(event->type() == QEvent::Show);
+ d->visibleChangeMode = QGraphicsProxyWidgetPrivate::NoMode;
+ }
+ break;
+ case QEvent::EnabledChange:
+ // If the widget toggles its enabled state, the proxy will follow.
+ if (!d->enabledChangeMode) {
+ d->enabledChangeMode = QGraphicsProxyWidgetPrivate::WidgetToProxyMode;
+ setEnabled(d->widget->isEnabled());
+ d->enabledChangeMode = QGraphicsProxyWidgetPrivate::NoMode;
+ }
+ break;
+ case QEvent::StyleChange:
+ // Propagate style changes to the proxy.
+ if (!d->styleChangeMode) {
+ d->styleChangeMode = QGraphicsProxyWidgetPrivate::WidgetToProxyMode;
+ setStyle(d->widget->style());
+ d->styleChangeMode = QGraphicsProxyWidgetPrivate::NoMode;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ return QGraphicsWidget::eventFilter(object, event);
+}
+
+/*!
+ \reimp
+*/
+void QGraphicsProxyWidget::showEvent(QShowEvent *event)
+{
+ Q_UNUSED(event);
+}
+
+/*!
+ \reimp
+*/
+void QGraphicsProxyWidget::hideEvent(QHideEvent *event)
+{
+ Q_UNUSED(event);
+}
+
+#ifndef QT_NO_CONTEXTMENU
+/*!
+ \reimp
+*/
+void QGraphicsProxyWidget::contextMenuEvent(QGraphicsSceneContextMenuEvent *event)
+{
+ Q_D(QGraphicsProxyWidget);
+ if (!event || !d->widget || !d->widget->isVisible())
+ return;
+
+ // Find widget position and receiver.
+ QPointF pos = event->pos();
+ QPointer<QWidget> alienWidget = d->widget->childAt(pos.toPoint());
+ QPointer<QWidget> receiver = alienWidget ? alienWidget : d->widget;
+
+ // Map event position from us to the receiver
+ pos = d->mapToReceiver(pos, receiver);
+
+ // Send mouse event. ### Doesn't propagate the event.
+ QContextMenuEvent contextMenuEvent(QContextMenuEvent::Reason(event->reason()),
+ pos.toPoint(), receiver->mapToGlobal(pos.toPoint()), event->modifiers());
+ QApplication::sendEvent(receiver, &contextMenuEvent);
+
+ event->setAccepted(contextMenuEvent.isAccepted());
+}
+#endif // QT_NO_CONTEXTMENU
+
+/*!
+ \reimp
+*/
+void QGraphicsProxyWidget::dragEnterEvent(QGraphicsSceneDragDropEvent *event)
+{
+#ifdef QT_NO_DRAGANDDROP
+ Q_UNUSED(event);
+#else
+ Q_D(QGraphicsProxyWidget);
+ if (!d->widget)
+ return;
+
+ QDragEnterEvent proxyDragEnter(event->pos().toPoint(), event->dropAction(), event->mimeData(), event->buttons(), event->modifiers());
+ proxyDragEnter.setAccepted(event->isAccepted());
+ QApplication::sendEvent(d->widget, &proxyDragEnter);
+ event->setAccepted(proxyDragEnter.isAccepted());
+ if (proxyDragEnter.isAccepted()) // we discard answerRect
+ event->setDropAction(proxyDragEnter.dropAction());
+#endif
+}
+/*!
+ \reimp
+*/
+void QGraphicsProxyWidget::dragLeaveEvent(QGraphicsSceneDragDropEvent *event)
+{
+ Q_UNUSED(event);
+#ifndef QT_NO_DRAGANDDROP
+ Q_D(QGraphicsProxyWidget);
+ if (!d->widget || !d->dragDropWidget)
+ return;
+ QDragLeaveEvent proxyDragLeave;
+ QApplication::sendEvent(d->dragDropWidget, &proxyDragLeave);
+ d->dragDropWidget = 0;
+#endif
+}
+
+/*!
+ \reimp
+*/
+void QGraphicsProxyWidget::dragMoveEvent(QGraphicsSceneDragDropEvent *event)
+{
+#ifdef QT_NO_DRAGANDDROP
+ Q_UNUSED(event);
+#else
+ Q_D(QGraphicsProxyWidget);
+ if (!d->widget)
+ return;
+ QPointF p = event->pos();
+ event->ignore();
+ QPointer<QWidget> subWidget = d->widget->childAt(p.toPoint());
+ QPointer<QWidget> receiver = subWidget ? subWidget : d->widget;
+ bool eventDelivered = false;
+ for (; receiver; receiver = receiver->parentWidget()) {
+ if (!receiver->isEnabled() || !receiver->acceptDrops())
+ continue;
+ // Map event position from us to the receiver
+ QPoint receiverPos = d->mapToReceiver(p, receiver).toPoint();
+ if (receiver != d->dragDropWidget) {
+ // Try to enter before we leave
+ QDragEnterEvent dragEnter(receiverPos, event->possibleActions(), event->mimeData(), event->buttons(), event->modifiers());
+ dragEnter.setDropAction(event->proposedAction());
+ QApplication::sendEvent(receiver, &dragEnter);
+ event->setAccepted(dragEnter.isAccepted());
+ event->setDropAction(dragEnter.dropAction());
+ if (!event->isAccepted()) {
+ // propagate to the parent widget
+ continue;
+ }
+
+ d->lastDropAction = event->dropAction();
+
+ if (d->dragDropWidget) {
+ QDragLeaveEvent dragLeave;
+ QApplication::sendEvent(d->dragDropWidget, &dragLeave);
+ }
+ d->dragDropWidget = receiver;
+ }
+
+ QDragMoveEvent dragMove(receiverPos, event->possibleActions(), event->mimeData(), event->buttons(), event->modifiers());
+ event->setDropAction(d->lastDropAction);
+ QApplication::sendEvent(receiver, &dragMove);
+ event->setAccepted(dragMove.isAccepted());
+ event->setDropAction(dragMove.dropAction());
+ if (event->isAccepted())
+ d->lastDropAction = event->dropAction();
+ eventDelivered = true;
+ break;
+ }
+
+ if (!eventDelivered) {
+ if (d->dragDropWidget) {
+ // Leave the last drag drop item
+ QDragLeaveEvent dragLeave;
+ QApplication::sendEvent(d->dragDropWidget, &dragLeave);
+ d->dragDropWidget = 0;
+ }
+ // Propagate
+ event->setDropAction(Qt::IgnoreAction);
+ }
+#endif
+}
+
+/*!
+ \reimp
+*/
+void QGraphicsProxyWidget::dropEvent(QGraphicsSceneDragDropEvent *event)
+{
+#ifdef QT_NO_DRAGANDDROP
+ Q_UNUSED(event);
+#else
+ Q_D(QGraphicsProxyWidget);
+ if (d->widget && d->dragDropWidget) {
+ QPoint widgetPos = d->mapToReceiver(event->pos(), d->dragDropWidget).toPoint();
+ QDropEvent dropEvent(widgetPos, event->possibleActions(), event->mimeData(), event->buttons(), event->modifiers());
+ QApplication::sendEvent(d->dragDropWidget, &dropEvent);
+ event->setAccepted(dropEvent.isAccepted());
+ d->dragDropWidget = 0;
+ }
+#endif
+}
+
+/*!
+ \reimp
+*/
+void QGraphicsProxyWidget::hoverEnterEvent(QGraphicsSceneHoverEvent *event)
+{
+ Q_UNUSED(event);
+}
+
+/*!
+ \reimp
+*/
+void QGraphicsProxyWidget::hoverLeaveEvent(QGraphicsSceneHoverEvent *event)
+{
+ Q_UNUSED(event);
+ Q_D(QGraphicsProxyWidget);
+ // If hoverMove was compressed away, make sure we update properly here.
+ if (d->lastWidgetUnderMouse) {
+ QApplicationPrivate::dispatchEnterLeave(0, d->lastWidgetUnderMouse);
+ d->lastWidgetUnderMouse = 0;
+ }
+}
+
+/*!
+ \reimp
+*/
+void QGraphicsProxyWidget::hoverMoveEvent(QGraphicsSceneHoverEvent *event)
+{
+ Q_D(QGraphicsProxyWidget);
+#ifdef GRAPHICSPROXYWIDGET_DEBUG
+ qDebug() << "QGraphicsProxyWidget::hoverMoveEvent";
+#endif
+ // Ignore events on the window frame.
+ if (!d->widget || !rect().contains(event->pos())) {
+ if (d->lastWidgetUnderMouse) {
+ QApplicationPrivate::dispatchEnterLeave(0, d->lastWidgetUnderMouse);
+ d->lastWidgetUnderMouse = 0;
+ }
+ return;
+ }
+
+ d->embeddedMouseGrabber = 0;
+ d->sendWidgetMouseEvent(event);
+}
+
+/*!
+ \reimp
+*/
+void QGraphicsProxyWidget::grabMouseEvent(QEvent *event)
+{
+ Q_UNUSED(event);
+}
+
+/*!
+ \reimp
+*/
+void QGraphicsProxyWidget::ungrabMouseEvent(QEvent *event)
+{
+ Q_D(QGraphicsProxyWidget);
+ Q_UNUSED(event);
+ d->embeddedMouseGrabber = 0;
+}
+
+/*!
+ \reimp
+*/
+void QGraphicsProxyWidget::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
+{
+ Q_D(QGraphicsProxyWidget);
+#ifdef GRAPHICSPROXYWIDGET_DEBUG
+ qDebug() << "QGraphicsProxyWidget::mouseMoveEvent";
+#endif
+ d->sendWidgetMouseEvent(event);
+}
+
+/*!
+ \reimp
+*/
+void QGraphicsProxyWidget::mousePressEvent(QGraphicsSceneMouseEvent *event)
+{
+ Q_D(QGraphicsProxyWidget);
+#ifdef GRAPHICSPROXYWIDGET_DEBUG
+ qDebug() << "QGraphicsProxyWidget::mousePressEvent";
+#endif
+ d->sendWidgetMouseEvent(event);
+}
+
+/*!
+ \reimp
+*/
+void QGraphicsProxyWidget::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event)
+{
+ Q_D(QGraphicsProxyWidget);
+#ifdef GRAPHICSPROXYWIDGET_DEBUG
+ qDebug() << "QGraphicsProxyWidget::mouseDoubleClickEvent";
+#endif
+ d->sendWidgetMouseEvent(event);
+}
+
+/*!
+ \reimp
+*/
+#ifndef QT_NO_WHEELEVENT
+void QGraphicsProxyWidget::wheelEvent(QGraphicsSceneWheelEvent *event)
+{
+ Q_D(QGraphicsProxyWidget);
+#ifdef GRAPHICSPROXYWIDGET_DEBUG
+ qDebug() << "QGraphicsProxyWidget::wheelEvent";
+#endif
+ if (!d->widget)
+ return;
+
+ QPointF pos = event->pos();
+ QPointer<QWidget> receiver = d->widget->childAt(pos.toPoint());
+ if (!receiver)
+ receiver = d->widget;
+
+ // Map event position from us to the receiver
+ pos = d->mapToReceiver(pos, receiver);
+
+ // Send mouse event.
+ QWheelEvent wheelEvent(pos.toPoint(), event->screenPos(), event->delta(),
+ event->buttons(), event->modifiers(), event->orientation());
+ QPointer<QWidget> focusWidget = d->widget->focusWidget();
+ extern bool qt_sendSpontaneousEvent(QObject *, QEvent *);
+ qt_sendSpontaneousEvent(receiver, &wheelEvent);
+ event->setAccepted(wheelEvent.isAccepted());
+
+ // ### Remove, this should be done by proper focusIn/focusOut events.
+ if (focusWidget && !focusWidget->hasFocus()) {
+ focusWidget->update();
+ focusWidget = d->widget->focusWidget();
+ if (focusWidget && focusWidget->hasFocus())
+ focusWidget->update();
+ }
+}
+#endif
+
+/*!
+ \reimp
+*/
+void QGraphicsProxyWidget::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
+{
+ Q_D(QGraphicsProxyWidget);
+#ifdef GRAPHICSPROXYWIDGET_DEBUG
+ qDebug() << "QGraphicsProxyWidget::mouseReleaseEvent";
+#endif
+ d->sendWidgetMouseEvent(event);
+}
+
+/*!
+ \reimp
+*/
+void QGraphicsProxyWidget::keyPressEvent(QKeyEvent *event)
+{
+ Q_D(QGraphicsProxyWidget);
+#ifdef GRAPHICSPROXYWIDGET_DEBUG
+ qDebug() << "QGraphicsProxyWidget::keyPressEvent";
+#endif
+ d->sendWidgetKeyEvent(event);
+}
+
+/*!
+ \reimp
+*/
+void QGraphicsProxyWidget::keyReleaseEvent(QKeyEvent *event)
+{
+ Q_D(QGraphicsProxyWidget);
+#ifdef GRAPHICSPROXYWIDGET_DEBUG
+ qDebug() << "QGraphicsProxyWidget::keyReleaseEvent";
+#endif
+ d->sendWidgetKeyEvent(event);
+}
+
+/*!
+ \reimp
+*/
+void QGraphicsProxyWidget::focusInEvent(QFocusEvent *event)
+{
+#ifdef GRAPHICSPROXYWIDGET_DEBUG
+ qDebug() << "QGraphicsProxyWidget::focusInEvent";
+#endif
+ Q_D(QGraphicsProxyWidget);
+
+ if (d->focusFromWidgetToProxy) {
+ // Prevent recursion when the proxy autogains focus through the
+ // embedded widget calling setFocus(). ### Could be done with event
+ // filter on FocusIn instead?
+ return;
+ }
+
+ switch (event->reason()) {
+ case Qt::TabFocusReason: {
+ if (QWidget *focusChild = d->findFocusChild(0, true))
+ focusChild->setFocus(event->reason());
+ break;
+ }
+ case Qt::BacktabFocusReason:
+ if (QWidget *focusChild = d->findFocusChild(0, false))
+ focusChild->setFocus(event->reason());
+ break;
+ default:
+ if (d->widget && d->widget->focusWidget()) {
+ d->widget->focusWidget()->setFocus(event->reason());
+ return;
+ }
+ break;
+ }
+}
+
+/*!
+ \reimp
+*/
+void QGraphicsProxyWidget::focusOutEvent(QFocusEvent *event)
+{
+#ifdef GRAPHICSPROXYWIDGET_DEBUG
+ qDebug() << "QGraphicsProxyWidget::focusOutEvent";
+#endif
+ Q_D(QGraphicsProxyWidget);
+ if (d->widget) {
+ // We need to explicitly remove subfocus from the embedded widget's
+ // focus widget.
+ if (QWidget *focusWidget = d->widget->focusWidget())
+ d->removeSubFocusHelper(focusWidget, event->reason());
+ }
+}
+
+/*!
+ \reimp
+*/
+bool QGraphicsProxyWidget::focusNextPrevChild(bool next)
+{
+ Q_D(QGraphicsProxyWidget);
+ if (!d->widget || !d->scene)
+ return QGraphicsWidget::focusNextPrevChild(next);
+
+ Qt::FocusReason reason = next ? Qt::TabFocusReason : Qt::BacktabFocusReason;
+ QWidget *lastFocusChild = d->widget->focusWidget();
+ if (QWidget *newFocusChild = d->findFocusChild(lastFocusChild, next)) {
+ newFocusChild->setFocus(reason);
+ return true;
+ }
+
+ return QGraphicsWidget::focusNextPrevChild(next);
+}
+
+/*!
+ \reimp
+*/
+QSizeF QGraphicsProxyWidget::sizeHint(Qt::SizeHint which, const QSizeF &constraint) const
+{
+ Q_D(const QGraphicsProxyWidget);
+ if (!d->widget)
+ return QGraphicsWidget::sizeHint(which, constraint);
+
+ QSizeF sh;
+ switch (which) {
+ case Qt::PreferredSize:
+ if (QLayout *l = d->widget->layout())
+ sh = l->sizeHint();
+ else
+ sh = d->widget->sizeHint();
+ break;
+ case Qt::MinimumSize:
+ if (QLayout *l = d->widget->layout())
+ sh = l->minimumSize();
+ else
+ sh = d->widget->minimumSizeHint();
+ break;
+ case Qt::MaximumSize:
+ if (QLayout *l = d->widget->layout())
+ sh = l->maximumSize();
+ else
+ sh = QSizeF(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX);
+ break;
+ case Qt::MinimumDescent:
+ sh = constraint;
+ break;
+ default:
+ break;
+ }
+ return sh;
+}
+
+/*!
+ \reimp
+*/
+void QGraphicsProxyWidget::resizeEvent(QGraphicsSceneResizeEvent *event)
+{
+ Q_D(QGraphicsProxyWidget);
+ if (d->widget) {
+ if (d->sizeChangeMode != QGraphicsProxyWidgetPrivate::WidgetToProxyMode)
+ d->widget->resize(event->newSize().toSize());
+ }
+ QGraphicsWidget::resizeEvent(event);
+}
+
+/*!
+ \reimp
+*/
+void QGraphicsProxyWidget::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
+{
+ Q_D(QGraphicsProxyWidget);
+ Q_UNUSED(widget);
+ if (!d->widget || !d->widget->isVisible())
+ return;
+
+ // Filter out repaints on the window frame.
+ const QRect exposedWidgetRect = (option->exposedRect & rect()).toRect();
+ if (exposedWidgetRect.isEmpty())
+ return;
+
+ // Disable QPainter's default pen being cosmetic. This allows widgets and
+ // styles to follow Qt's existing defaults without getting ugly cosmetic
+ // lines when scaled.
+ bool restore = !(painter->renderHints() & QPainter::NonCosmeticDefaultPen);
+ painter->setRenderHints(QPainter::NonCosmeticDefaultPen, true);
+
+ d->widget->render(painter, exposedWidgetRect.topLeft(), exposedWidgetRect);
+
+ // Restore the render hints if necessary.
+ if (restore)
+ painter->setRenderHints(QPainter::NonCosmeticDefaultPen, false);
+}
+
+/*!
+ \reimp
+*/
+int QGraphicsProxyWidget::type() const
+{
+ return Type;
+}
+
+/*!
+ \since 4.5
+
+ Creates a proxy widget for the given \a child of the widget
+ contained in this proxy.
+
+ This function makes it possible to aquire proxies for
+ non top-level widgets. For instance, you can embed a dialog,
+ and then transform only one of its widgets.
+
+ If the widget is already embedded, return the existing proxy widget.
+
+ \sa newProxyWidget(), QGraphicsScene::addWidget()
+*/
+QGraphicsProxyWidget *QGraphicsProxyWidget::createProxyForChildWidget(QWidget *child)
+{
+ QGraphicsProxyWidget *proxy = child->graphicsProxyWidget();
+ if (proxy)
+ return proxy;
+ if (!child->parentWidget()) {
+ qWarning("QGraphicsProxyWidget::createProxyForChildWidget: top-level widget not in a QGraphicsScene");
+ return 0;
+ }
+
+ QGraphicsProxyWidget *parentProxy = createProxyForChildWidget(child->parentWidget());
+ if (!parentProxy)
+ return 0;
+
+ if (!QMetaObject::invokeMethod(parentProxy, "newProxyWidget", Qt::DirectConnection,
+ Q_RETURN_ARG(QGraphicsProxyWidget*, proxy), Q_ARG(const QWidget*, child)))
+ return 0;
+ proxy->setParent(parentProxy);
+ proxy->setWidget(child);
+ return proxy;
+}
+
+/*!
+ \fn QGraphicsProxyWidget *QGraphicsProxyWidget::newProxyWidget(const QWidget *child)
+ \since 4.5
+
+ Creates a proxy widget for the given \a child of the widget contained in this
+ proxy.
+
+ You should not call this function directly; use
+ QGraphicsProxyWidget::createProxyForChildWidget() instead.
+
+ This function is a fake virtual slot that you can reimplement in
+ your subclass in order to control how new proxy widgets are
+ created. The default implementation returns a proxy created with
+ the QGraphicsProxyWidget() constructor with this proxy widget as
+ the parent.
+
+ \sa createProxyForChildWidget()
+*/
+QGraphicsProxyWidget *QGraphicsProxyWidget::newProxyWidget(const QWidget *)
+{
+ return new QGraphicsProxyWidget(this);
+}
+
+
+
+QT_END_NAMESPACE
+
+#include "moc_qgraphicsproxywidget.cpp"
+
+#endif //QT_NO_GRAPHICSVIEW
diff --git a/src/gui/graphicsview/qgraphicsproxywidget.h b/src/gui/graphicsview/qgraphicsproxywidget.h
new file mode 100644
index 0000000000..b2c3c8f8dd
--- /dev/null
+++ b/src/gui/graphicsview/qgraphicsproxywidget.h
@@ -0,0 +1,145 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QGRAPHICSPROXYWIDGET_H
+#define QGRAPHICSPROXYWIDGET_H
+
+#include <QtGui/qgraphicswidget.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+#if !defined(QT_NO_GRAPHICSVIEW) || (QT_EDITION & QT_MODULE_GRAPHICSVIEW) != QT_MODULE_GRAPHICSVIEW
+
+class QGraphicsProxyWidgetPrivate;
+
+class Q_GUI_EXPORT QGraphicsProxyWidget : public QGraphicsWidget
+{
+ Q_OBJECT
+public:
+ QGraphicsProxyWidget(QGraphicsItem *parent = 0, Qt::WindowFlags wFlags = 0);
+ ~QGraphicsProxyWidget();
+
+ void setWidget(QWidget *widget);
+ QWidget *widget() const;
+
+ QRectF subWidgetRect(const QWidget *widget) const;
+
+ void setGeometry(const QRectF &rect);
+
+ void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget);
+
+ enum {
+ Type = 12
+ };
+ int type() const;
+
+ QGraphicsProxyWidget *createProxyForChildWidget(QWidget *child);
+
+protected:
+ QVariant itemChange(GraphicsItemChange change, const QVariant &value);
+
+ bool event(QEvent *event);
+ bool eventFilter(QObject *object, QEvent *event);
+
+ void showEvent(QShowEvent *event);
+ void hideEvent(QHideEvent *event);
+
+#ifndef QT_NO_CONTEXTMENU
+ void contextMenuEvent(QGraphicsSceneContextMenuEvent *event);
+#endif
+
+ void dragEnterEvent(QGraphicsSceneDragDropEvent *event);
+ void dragLeaveEvent(QGraphicsSceneDragDropEvent *event);
+ void dragMoveEvent(QGraphicsSceneDragDropEvent *event);
+ void dropEvent(QGraphicsSceneDragDropEvent *event);
+
+ void hoverEnterEvent(QGraphicsSceneHoverEvent *event);
+ void hoverLeaveEvent(QGraphicsSceneHoverEvent *event);
+ void hoverMoveEvent(QGraphicsSceneHoverEvent *event);
+ void grabMouseEvent(QEvent *event);
+ void ungrabMouseEvent(QEvent *event);
+
+ void mouseMoveEvent(QGraphicsSceneMouseEvent *event);
+ void mousePressEvent(QGraphicsSceneMouseEvent *event);
+ void mouseReleaseEvent(QGraphicsSceneMouseEvent *event);
+ void mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event);
+#ifndef QT_NO_WHEELEVENT
+ void wheelEvent(QGraphicsSceneWheelEvent *event);
+#endif
+
+ void keyPressEvent(QKeyEvent *event);
+ void keyReleaseEvent(QKeyEvent *event);
+
+ void focusInEvent(QFocusEvent *event);
+ void focusOutEvent(QFocusEvent *event);
+ bool focusNextPrevChild(bool next);
+ // ### Qt 4.5:
+ // QVariant inputMethodQuery(Qt::InputMethodQuery query) const;
+ // void inputMethodEvent(QInputMethodEvent *event);
+
+ QSizeF sizeHint(Qt::SizeHint which, const QSizeF &constraint = QSizeF()) const;
+ void resizeEvent(QGraphicsSceneResizeEvent *event);
+
+protected Q_SLOTS:
+ QGraphicsProxyWidget *newProxyWidget(const QWidget *);
+
+private:
+ Q_DISABLE_COPY(QGraphicsProxyWidget)
+ Q_DECLARE_PRIVATE_D(QGraphicsItem::d_ptr, QGraphicsProxyWidget)
+ Q_PRIVATE_SLOT(d_func(), void _q_removeWidgetSlot())
+
+ friend class QWidget;
+ friend class QWidgetPrivate;
+ friend class QGraphicsItem;
+};
+
+#endif
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif
+
diff --git a/src/gui/graphicsview/qgraphicsproxywidget_p.h b/src/gui/graphicsview/qgraphicsproxywidget_p.h
new file mode 100644
index 0000000000..5985eedc5b
--- /dev/null
+++ b/src/gui/graphicsview/qgraphicsproxywidget_p.h
@@ -0,0 +1,125 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QGRAPHICSPROXYWIDGET_P_H
+#define QGRAPHICSPROXYWIDGET_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of qapplication_*.cpp, qwidget*.cpp and qfiledialog.cpp. This header
+// file may change from version to version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qgraphicsproxywidget.h"
+#include "private/qgraphicswidget_p.h"
+
+#if !defined(QT_NO_GRAPHICSVIEW) || (QT_EDITION & QT_MODULE_GRAPHICSVIEW) != QT_MODULE_GRAPHICSVIEW
+
+QT_BEGIN_NAMESPACE
+
+class QGraphicsProxyWidgetPrivate : public QGraphicsWidgetPrivate
+{
+ Q_DECLARE_PUBLIC(QGraphicsProxyWidget)
+public:
+ QGraphicsProxyWidgetPrivate()
+ : dragDropWidget(0),
+ posChangeMode(NoMode),
+ sizeChangeMode(NoMode),
+ visibleChangeMode(NoMode),
+ enabledChangeMode(NoMode),
+ styleChangeMode(NoMode),
+ paletteChangeMode(NoMode),
+ focusFromWidgetToProxy(0)
+ { }
+ void init();
+ void sendWidgetMouseEvent(QGraphicsSceneMouseEvent *event);
+ void sendWidgetMouseEvent(QGraphicsSceneHoverEvent *event);
+ void sendWidgetKeyEvent(QKeyEvent *event);
+ void setWidget_helper(QWidget *widget, bool autoShow);
+
+ QWidget *findFocusChild(QWidget *child, bool next) const;
+ void removeSubFocusHelper(QWidget *widget, Qt::FocusReason reason);
+
+ // ### Qt 5: Remove. Workaround for reimplementation added after Qt 4.4.
+ QVariant inputMethodQueryHelper(Qt::InputMethodQuery query) const;
+
+ void _q_removeWidgetSlot();
+
+ void embedSubWindow(QWidget *);
+ void unembedSubWindow(QWidget *);
+
+ bool isProxyWidget() const;
+
+ QPointer<QWidget> widget;
+ QPointer<QWidget> lastWidgetUnderMouse;
+ QPointer<QWidget> embeddedMouseGrabber;
+ QWidget *dragDropWidget;
+ Qt::DropAction lastDropAction;
+
+ void updateWidgetGeometryFromProxy();
+ void updateProxyGeometryFromWidget();
+
+ QPointF mapToReceiver(const QPointF &pos, const QWidget *receiver) const;
+
+ enum ChangeMode {
+ NoMode,
+ ProxyToWidgetMode,
+ WidgetToProxyMode
+ };
+ quint32 posChangeMode : 2;
+ quint32 sizeChangeMode : 2;
+ quint32 visibleChangeMode : 2;
+ quint32 enabledChangeMode : 2;
+ quint32 styleChangeMode : 2;
+ quint32 paletteChangeMode : 2;
+ quint32 focusFromWidgetToProxy : 1;
+};
+
+QT_END_NAMESPACE
+
+#endif
+
+#endif
diff --git a/src/gui/graphicsview/qgraphicsscene.cpp b/src/gui/graphicsview/qgraphicsscene.cpp
new file mode 100644
index 0000000000..1f78a18d32
--- /dev/null
+++ b/src/gui/graphicsview/qgraphicsscene.cpp
@@ -0,0 +1,5360 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+static const int QGRAPHICSSCENE_INDEXTIMER_TIMEOUT = 2000;
+
+/*!
+ \class QGraphicsScene
+ \brief The QGraphicsScene class provides a surface for managing a large
+ number of 2D graphical items.
+ \since 4.2
+ \ingroup multimedia
+ \ingroup graphicsview-api
+ \mainclass
+
+ The class serves as a container for QGraphicsItems. It is used together
+ with QGraphicsView for visualizing graphical items, such as lines,
+ rectangles, text, or even custom items, on a 2D surface. QGraphicsScene is
+ part of \l{The Graphics View Framework}.
+
+ QGraphicsScene also provides functionality that lets you efficiently
+ determine both the location of items, and for determining what items are
+ visible within an arbitrary area on the scene. With the QGraphicsView
+ widget, you can either visualize the whole scene, or zoom in and view only
+ parts of the scene.
+
+ Example:
+
+ \snippet doc/src/snippets/code/src_gui_graphicsview_qgraphicsscene.cpp 0
+
+ Note that QGraphicsScene has no visual appearance of its own; it only
+ manages the items. You need to create a QGraphicsView widget to visualize
+ the scene.
+
+ To add items to a scene, you start off by constructing a QGraphicsScene
+ object. Then, you have two options: either add your existing QGraphicsItem
+ objects by calling addItem(), or you can call one of the convenience
+ functions addEllipse(), addLine(), addPath(), addPixmap(), addPolygon(),
+ addRect(), or addText(), which all return a pointer to the newly added item.
+ The dimensions of the items added with these functions are relative to the
+ item's coordinate system, and the items position is initialized to (0,
+ 0) in the scene.
+
+ You can then visualize the scene using QGraphicsView. When the scene
+ changes, (e.g., when an item moves or is transformed) QGraphicsScene
+ emits the changed() signal. To remove an item, call removeItem().
+
+ QGraphicsScene uses an indexing algorithm to manage the location of items
+ efficiently. By default, a BSP (Binary Space Partitioning) tree is used; an
+ algorithm suitable for large scenes where most items remain static (i.e.,
+ do not move around). You can choose to disable this index by calling
+ setItemIndexMethod(). For more information about the available indexing
+ algorithms, see the itemIndexMethod property.
+
+ The scene's bounding rect is set by calling setSceneRect(). Items can be
+ placed at any position on the scene, and the size of the scene is by
+ default unlimited. The scene rect is used only for internal bookkeeping,
+ maintaining the scene's item index. If the scene rect is unset,
+ QGraphicsScene will use the bounding area of all items, as returned by
+ itemsBoundingRect(), as the scene rect. However, itemsBoundingRect() is a
+ relatively time consuming function, as it operates by collecting
+ positional information for every item on the scene. Because of this, you
+ should always set the scene rect when operating on large scenes.
+
+ One of QGraphicsScene's greatest strengths is its ability to efficiently
+ determine the location of items. Even with millions of items on the scene,
+ the items() functions can determine the location of an item within few
+ milliseconds. There are several overloads to items(): one that finds items
+ at a certain position, one that finds items inside or intersecting with a
+ polygon or a rectangle, and more. The list of returned items is sorted by
+ stacking order, with the topmost item being the first item in the list.
+ For convenience, there is also an itemAt() function that returns the
+ topmost item at a given position.
+
+ QGraphicsScene maintains selection information for the scene. To select
+ items, call setSelectionArea(), and to clear the current selection, call
+ clearSelection(). Call selectedItems() to get the list of all selected
+ items.
+
+ \section1 Event Handling and Propagation
+
+ Another responsibility that QGraphicsScene has, is to propagate events
+ from QGraphicsView. To send an event to a scene, you construct an event
+ that inherits QEvent, and then send it using, for example,
+ QApplication::sendEvent(). event() is responsible for dispatching
+ the event to the individual items. Some common events are handled by
+ convenience event handlers. For example, key press events are handled by
+ keyPressEvent(), and mouse press events are handled by mousePressEvent().
+
+ Key events are delivered to the \e {focus item}. To set the focus item,
+ you can either call setFocusItem(), passing an item that accepts focus, or
+ the item itself can call QGraphicsItem::setFocus(). Call focusItem() to
+ get the current focus item. For compatibility with widgets, the scene also
+ maintains its own focus information. By default, the scene does not have
+ focus, and all key events are discarded. If setFocus() is called, or if an
+ item on the scene gains focus, the scene automatically gains focus. If the
+ scene has focus, hasFocus() will return true, and key events will be
+ forwarded to the focus item, if any. If the scene loses focus, (i.e.,
+ someone calls clearFocus(),) while an item has focus, the scene will
+ maintain its item focus information, and once the scene regains focus, it
+ will make sure the last focus item regains focus.
+
+ For mouse-over effects, QGraphicsScene dispatches \e {hover
+ events}. If an item accepts hover events (see
+ QGraphicsItem::acceptHoverEvents()), it will receive a \l
+ {QEvent::}{GraphicsSceneHoverEnter} event when the mouse enters
+ its area. As the mouse continues moving inside the item's area,
+ QGraphicsScene will send it \l {QEvent::}{GraphicsSceneHoverMove}
+ events. When the mouse leaves the item's area, the item will
+ receive a \l {QEvent::}{GraphicsSceneHoverLeave} event.
+
+ All mouse events are delivered to the current \e {mouse grabber}
+ item. An item becomes the scene's mouse grabber if it accepts
+ mouse events (see QGraphicsItem::acceptedMouseButtons()) and it
+ receives a mouse press. It stays the mouse grabber until it
+ receives a mouse release when no other mouse buttons are
+ pressed. You can call mouseGrabberItem() to determine what item is
+ currently grabbing the mouse.
+
+ \sa QGraphicsItem, QGraphicsView
+*/
+
+/*!
+ \enum QGraphicsScene::SceneLayer
+ \since 4.3
+
+ This enum describes the rendering layers in a QGraphicsScene. When
+ QGraphicsScene draws the scene contents, it renders each of these layers
+ separately, in order.
+
+ Each layer represents a flag that can be OR'ed together when calling
+ functions such as invalidate() or QGraphicsView::invalidateScene().
+
+ \value ItemLayer The item layer. QGraphicsScene renders all items are in
+ this layer by calling the virtual function drawItems(). The item layer is
+ drawn after the background layer, but before the foreground layer.
+
+ \value BackgroundLayer The background layer. QGraphicsScene renders the
+ scene's background in this layer by calling the virtual function
+ drawBackground(). The background layer is drawn first of all layers.
+
+ \value ForegroundLayer The foreground layer. QGraphicsScene renders the
+ scene's foreground in this layer by calling the virtual function
+ drawForeground(). The foreground layer is drawn last of all layers.
+
+ \value AllLayers All layers; this value represents a combination of all
+ three layers.
+
+ \sa invalidate(), QGraphicsView::invalidateScene()
+*/
+
+/*!
+ \enum QGraphicsScene::ItemIndexMethod
+
+ This enum describes the indexing algorithms QGraphicsScene provides for
+ managing positional information about items on the scene.
+
+ \value BspTreeIndex A Binary Space Partitioning tree is applied. All
+ QGraphicsScene's item location algorithms are of an order close to
+ logarithmic complexity, by making use of binary search. Adding, moving and
+ removing items is logarithmic. This approach is best for static scenes
+ (i.e., scenes where most items do not move).
+
+ \value NoIndex No index is applied. Item location is of linear complexity,
+ as all items on the scene are searched. Adding, moving and removing items,
+ however, is done in constant time. This approach is ideal for dynamic
+ scenes, where many items are added, moved or removed continuously.
+
+ \sa setItemIndexMethod(), bspTreeDepth
+*/
+
+#include "qgraphicsscene.h"
+
+#ifndef QT_NO_GRAPHICSVIEW
+
+#include "qgraphicsitem.h"
+#include "qgraphicsitem_p.h"
+#include "qgraphicslayout.h"
+#include "qgraphicsscene_p.h"
+#include "qgraphicssceneevent.h"
+#include "qgraphicsview.h"
+#include "qgraphicsview_p.h"
+#include "qgraphicswidget.h"
+#include "qgraphicswidget_p.h"
+
+#include <QtCore/qdebug.h>
+#include <QtCore/qlist.h>
+#include <QtCore/qmath.h>
+#include <QtCore/qrect.h>
+#include <QtCore/qset.h>
+#include <QtCore/qstack.h>
+#include <QtCore/qtimer.h>
+#include <QtCore/qvarlengtharray.h>
+#include <QtGui/qapplication.h>
+#include <QtGui/qdesktopwidget.h>
+#include <QtGui/qevent.h>
+#include <QtGui/qgraphicslayout.h>
+#include <QtGui/qgraphicsproxywidget.h>
+#include <QtGui/qgraphicswidget.h>
+#include <QtGui/qmatrix.h>
+#include <QtGui/qpaintengine.h>
+#include <QtGui/qpainter.h>
+#include <QtGui/qpixmapcache.h>
+#include <QtGui/qpolygon.h>
+#include <QtGui/qstyleoption.h>
+#include <QtGui/qtooltip.h>
+#include <QtGui/qtransform.h>
+#include <private/qapplication_p.h>
+#include <private/qobject_p.h>
+#ifdef Q_WS_X11
+#include <private/qt_x11_p.h>
+#endif
+
+QT_BEGIN_NAMESPACE
+
+static inline bool QRectF_intersects(const QRectF &s, const QRectF &r)
+{
+ qreal xp = s.left();
+ qreal yp = s.top();
+ qreal w = s.width();
+ qreal h = s.height();
+ qreal l1 = xp;
+ qreal r1 = xp;
+ if (w < 0)
+ l1 += w;
+ else
+ r1 += w;
+
+ qreal l2 = r.left();
+ qreal r2 = r.left();
+ if (w < 0)
+ l2 += r.width();
+ else
+ r2 += r.width();
+
+ if (l1 >= r2 || l2 >= r1)
+ return false;
+
+ qreal t1 = yp;
+ qreal b1 = yp;
+ if (h < 0)
+ t1 += h;
+ else
+ b1 += h;
+
+ qreal t2 = r.top();
+ qreal b2 = r.top();
+ if (r.height() < 0)
+ t2 += r.height();
+ else
+ b2 += r.height();
+
+ return !(t1 >= b2 || t2 >= b1);
+}
+
+// QRectF::intersects() returns false always if either the source or target
+// rectangle's width or height are 0. This works around that problem.
+static QRectF _q_adjustedRect(const QRectF &rect)
+{
+ static const qreal p = (qreal)0.00001;
+ QRectF r = rect;
+ if (!r.width())
+ r.adjust(-p, 0, p, 0);
+ if (!r.height())
+ r.adjust(0, -p, 0, p);
+ return r;
+}
+
+static void _q_hoverFromMouseEvent(QGraphicsSceneHoverEvent *hover, const QGraphicsSceneMouseEvent *mouseEvent)
+{
+ hover->setWidget(mouseEvent->widget());
+ hover->setPos(mouseEvent->pos());
+ hover->setScenePos(mouseEvent->scenePos());
+ hover->setScreenPos(mouseEvent->screenPos());
+ hover->setLastPos(mouseEvent->lastPos());
+ hover->setLastScenePos(mouseEvent->lastScenePos());
+ hover->setLastScreenPos(mouseEvent->lastScreenPos());
+ hover->setModifiers(mouseEvent->modifiers());
+ hover->setAccepted(mouseEvent->isAccepted());
+}
+
+/*!
+ \internal
+*/
+QGraphicsScenePrivate::QGraphicsScenePrivate()
+ : changedSignalMask(0),
+ indexMethod(QGraphicsScene::BspTreeIndex),
+ bspTreeDepth(0),
+ lastItemCount(0),
+ hasSceneRect(false),
+ updateAll(false),
+ calledEmitUpdated(false),
+ selectionChanging(0),
+ dirtyItemResetPending(false),
+ regenerateIndex(true),
+ purgePending(false),
+ indexTimerId(0),
+ restartIndexTimer(false),
+ stickyFocus(false),
+ hasFocus(false),
+ focusItem(0),
+ lastFocusItem(0),
+ tabFocusFirst(0),
+ activeWindow(0),
+ activationRefCount(0),
+ lastMouseGrabberItem(0),
+ lastMouseGrabberItemHasImplicitMouseGrab(false),
+ dragDropItem(0),
+ enterWidget(0),
+ lastDropAction(Qt::IgnoreAction),
+ painterStateProtection(true),
+ sortCacheEnabled(false),
+ updatingSortCache(false),
+ style(0)
+{
+}
+
+/*!
+ \internal
+*/
+void QGraphicsScenePrivate::init()
+{
+ Q_Q(QGraphicsScene);
+
+ // Keep this index so we can check for connected slots later on.
+ changedSignalMask = (1 << q->metaObject()->indexOfSignal("changed(QList<QRectF>)"));
+ qApp->d_func()->scene_list.append(q);
+ q->update();
+}
+
+/*!
+ \internal
+*/
+QList<QGraphicsItem *> QGraphicsScenePrivate::estimateItemsInRect(const QRectF &rect) const
+{
+ const_cast<QGraphicsScenePrivate *>(this)->purgeRemovedItems();
+ const_cast<QGraphicsScenePrivate *>(this)->_q_updateSortCache();
+
+ if (indexMethod == QGraphicsScene::BspTreeIndex) {
+ // ### Only do this once in a while.
+ QGraphicsScenePrivate *that = const_cast<QGraphicsScenePrivate *>(this);
+
+ // Get items from BSP tree
+ QList<QGraphicsItem *> items = that->bspTree.items(rect);
+
+ // Fill in with any unindexed items
+ for (int i = 0; i < unindexedItems.size(); ++i) {
+ if (QGraphicsItem *item = unindexedItems.at(i)) {
+ if (!item->d_ptr->itemDiscovered && item->d_ptr->visible && !(item->d_ptr->ancestorFlags & QGraphicsItemPrivate::AncestorClipsChildren)) {
+ QRectF boundingRect = item->sceneBoundingRect();
+ if (QRectF_intersects(boundingRect, rect)) {
+ item->d_ptr->itemDiscovered = 1;
+ items << item;
+ }
+ }
+ }
+ }
+
+ // Reset the discovered state of all discovered items
+ for (int i = 0; i < items.size(); ++i)
+ items.at(i)->d_func()->itemDiscovered = 0;
+ return items;
+ }
+
+ QList<QGraphicsItem *> itemsInRect;
+ for (int i = 0; i < unindexedItems.size(); ++i) {
+ if (QGraphicsItem *item = unindexedItems.at(i)) {
+ if (item->d_ptr->ancestorFlags & QGraphicsItemPrivate::AncestorClipsChildren)
+ continue;
+ if (item->d_ptr->visible && item->effectiveOpacity() > qreal(0.0))
+ itemsInRect << item;
+ }
+ }
+ for (int i = 0; i < indexedItems.size(); ++i) {
+ if (QGraphicsItem *item = indexedItems.at(i)) {
+ if (item->d_ptr->ancestorFlags & QGraphicsItemPrivate::AncestorClipsChildren)
+ continue;
+ if (item->d_ptr->visible && item->effectiveOpacity() > qreal(0.0))
+ itemsInRect << item;
+ }
+ }
+
+ return itemsInRect;
+}
+
+/*!
+ \internal
+*/
+void QGraphicsScenePrivate::addToIndex(QGraphicsItem *item)
+{
+ if (indexMethod == QGraphicsScene::BspTreeIndex) {
+ if (item->d_func()->index != -1) {
+ bspTree.insertItem(item, item->sceneBoundingRect());
+ foreach (QGraphicsItem *child, item->children())
+ child->addToIndex();
+ } else {
+ // The BSP tree is regenerated if the number of items grows to a
+ // certain threshold, or if the bounding rect of the graph doubles in
+ // size.
+ startIndexTimer();
+ }
+ }
+}
+
+/*!
+ \internal
+*/
+void QGraphicsScenePrivate::removeFromIndex(QGraphicsItem *item)
+{
+ if (indexMethod == QGraphicsScene::BspTreeIndex) {
+ int index = item->d_func()->index;
+ if (index != -1) {
+ bspTree.removeItem(item, item->sceneBoundingRect());
+ freeItemIndexes << index;
+ indexedItems[index] = 0;
+ item->d_func()->index = -1;
+ unindexedItems << item;
+
+ foreach (QGraphicsItem *child, item->children())
+ child->removeFromIndex();
+ }
+
+ startIndexTimer();
+ }
+}
+
+/*!
+ \internal
+*/
+void QGraphicsScenePrivate::resetIndex()
+{
+ purgeRemovedItems();
+ if (indexMethod == QGraphicsScene::BspTreeIndex) {
+ for (int i = 0; i < indexedItems.size(); ++i) {
+ if (QGraphicsItem *item = indexedItems.at(i)) {
+ item->d_ptr->index = -1;
+ unindexedItems << item;
+ }
+ }
+ indexedItems.clear();
+ freeItemIndexes.clear();
+ regenerateIndex = true;
+ startIndexTimer();
+ }
+}
+
+static inline int intmaxlog(int n)
+{
+ return (n > 0 ? qMax(qCeil(qLn(qreal(n)) / qLn(qreal(2))), 5) : 0);
+}
+
+/*!
+ \internal
+*/
+void QGraphicsScenePrivate::_q_updateIndex()
+{
+ if (!indexTimerId)
+ return;
+
+ Q_Q(QGraphicsScene);
+ q->killTimer(indexTimerId);
+ indexTimerId = 0;
+
+ purgeRemovedItems();
+
+ // Add unindexedItems to indexedItems
+ QRectF unindexedItemsBoundingRect;
+ for (int i = 0; i < unindexedItems.size(); ++i) {
+ if (QGraphicsItem *item = unindexedItems.at(i)) {
+ unindexedItemsBoundingRect |= item->sceneBoundingRect();
+ if (!freeItemIndexes.isEmpty()) {
+ int freeIndex = freeItemIndexes.takeFirst();
+ item->d_func()->index = freeIndex;
+ indexedItems[freeIndex] = item;
+ } else {
+ item->d_func()->index = indexedItems.size();
+ indexedItems << item;
+ }
+ }
+ }
+
+ // Update growing scene rect.
+ QRectF oldGrowingItemsBoundingRect = growingItemsBoundingRect;
+ growingItemsBoundingRect |= unindexedItemsBoundingRect;
+
+ // Determine whether we should regenerate the BSP tree.
+ if (indexMethod == QGraphicsScene::BspTreeIndex) {
+ int depth = bspTreeDepth;
+ if (depth == 0) {
+ int oldDepth = intmaxlog(lastItemCount);
+ depth = intmaxlog(indexedItems.size());
+ static const int slack = 100;
+ if (bspTree.leafCount() == 0 || (oldDepth != depth && qAbs(lastItemCount - indexedItems.size()) > slack)) {
+ // ### Crude algorithm.
+ regenerateIndex = true;
+ }
+ }
+
+ // Regenerate the tree.
+ if (regenerateIndex) {
+ regenerateIndex = false;
+ bspTree.initialize(q->sceneRect(), depth);
+ unindexedItems = indexedItems;
+ lastItemCount = indexedItems.size();
+ q->update();
+
+ // Take this opportunity to reset our largest-item counter for
+ // untransformable items. When the items are inserted into the BSP
+ // tree, we'll get an accurate calculation.
+ largestUntransformableItem = QRectF();
+ }
+ }
+
+ // Insert all unindexed items into the tree.
+ for (int i = 0; i < unindexedItems.size(); ++i) {
+ if (QGraphicsItem *item = unindexedItems.at(i)) {
+ QRectF rect = item->sceneBoundingRect();
+ if (item->d_ptr->ancestorFlags & QGraphicsItemPrivate::AncestorClipsChildren)
+ continue;
+ if (indexMethod == QGraphicsScene::BspTreeIndex)
+ bspTree.insertItem(item, rect);
+
+ // If the item ignores view transformations, update our
+ // largest-item-counter to ensure that the view can accurately
+ // discover untransformable items when drawing.
+ if (item->d_ptr->itemIsUntransformable()) {
+ QGraphicsItem *topmostUntransformable = item;
+ while (topmostUntransformable && (topmostUntransformable->d_ptr->ancestorFlags
+ & QGraphicsItemPrivate::AncestorIgnoresTransformations)) {
+ topmostUntransformable = topmostUntransformable->parentItem();
+ }
+ // ### Verify that this is the correct largest untransformable rectangle.
+ largestUntransformableItem |= item->mapToItem(topmostUntransformable, item->boundingRect()).boundingRect();
+ }
+ }
+ }
+ unindexedItems.clear();
+
+ // Notify scene rect changes.
+ if (!hasSceneRect && growingItemsBoundingRect != oldGrowingItemsBoundingRect)
+ emit q->sceneRectChanged(growingItemsBoundingRect);
+}
+
+/*!
+ \internal
+*/
+void QGraphicsScenePrivate::_q_emitUpdated()
+{
+ Q_Q(QGraphicsScene);
+ calledEmitUpdated = false;
+
+ // Ensure all views are connected if anything is connected. This disables
+ // the optimization that items send updates directly to the views, but it
+ // needs to happen in order to keep compatibility with the behavior from
+ // Qt 4.4 and backward.
+ if (!views.isEmpty() && (connectedSignals & changedSignalMask)) {
+ for (int i = 0; i < views.size(); ++i) {
+ QGraphicsView *view = views.at(i);
+ if (!view->d_func()->connectedToScene) {
+ view->d_func()->connectedToScene = true;
+ q->connect(q, SIGNAL(changed(QList<QRectF>)),
+ views.at(i), SLOT(updateScene(QList<QRectF>)));
+ }
+ }
+ }
+
+ // Ensure all dirty items's current positions are recorded in the list of
+ // updated rects.
+ for (int i = 0; i < dirtyItems.size(); ++i)
+ updatedRects += dirtyItems.at(i)->sceneBoundingRect();
+
+ // Notify the changes to anybody interested.
+ QList<QRectF> oldUpdatedRects;
+ oldUpdatedRects = updateAll ? (QList<QRectF>() << q->sceneRect()) : updatedRects;
+ updateAll = false;
+ updatedRects.clear();
+ emit q->changed(oldUpdatedRects);
+}
+
+/*!
+ \internal
+
+ Updates all items in the pending update list. At this point, the list is
+ unlikely to contain partially constructed items.
+*/
+void QGraphicsScenePrivate::_q_updateLater()
+{
+ foreach (QGraphicsItem *item, pendingUpdateItems)
+ item->update();
+ pendingUpdateItems.clear();
+}
+
+/*!
+ \internal
+*/
+void QGraphicsScenePrivate::_q_polishItems()
+{
+ foreach (QGraphicsItem *item, unpolishedItems) {
+ if (!item->d_ptr->explicitlyHidden) {
+ item->itemChange(QGraphicsItem::ItemVisibleChange, true);
+ item->itemChange(QGraphicsItem::ItemVisibleHasChanged, true);
+ }
+ if (item->isWidget()) {
+ QEvent event(QEvent::Polish);
+ QApplication::sendEvent((QGraphicsWidget *)item, &event);
+ }
+ }
+ unpolishedItems.clear();
+}
+
+/*!
+ \internal
+*/
+void QGraphicsScenePrivate::_q_resetDirtyItems()
+{
+ for (int i = 0; i < dirtyItems.size(); ++i) {
+ QGraphicsItem *item = dirtyItems.at(i);
+ item->d_ptr->dirty = 0;
+ item->d_ptr->dirtyChildren = 0;
+ }
+ dirtyItems.clear();
+ dirtyItemResetPending = false;
+}
+
+/*!
+ \internal
+*/
+void QGraphicsScenePrivate::resetDirtyItemsLater()
+{
+ Q_Q(QGraphicsScene);
+ if (dirtyItemResetPending)
+ return;
+ // dirtyItems.reserve(indexedItems.size() + unindexedItems.size());
+ dirtyItemResetPending = true;
+ QMetaObject::invokeMethod(q, "_q_resetDirtyItems", Qt::QueuedConnection);
+}
+
+/*!
+ \internal
+
+ Schedules an item for removal. This function leaves some stale indexes
+ around in the BSP tree; these will be cleaned up the next time someone
+ triggers purgeRemovedItems().
+
+ Note: This function is called from QGraphicsItem's destructor. \a item is
+ being destroyed, so we cannot call any pure virtual functions on it (such
+ as boundingRect()). Also, it is unnecessary to update the item's own state
+ in any way.
+
+ ### Refactoring: This function shares much functionality with removeItem()
+*/
+void QGraphicsScenePrivate::_q_removeItemLater(QGraphicsItem *item)
+{
+ Q_Q(QGraphicsScene);
+
+ if (QGraphicsItem *parent = item->d_func()->parent) {
+ QVariant variant;
+ qVariantSetValue<QGraphicsItem *>(variant, item);
+ parent->itemChange(QGraphicsItem::ItemChildRemovedChange, variant);
+ parent->d_func()->children.removeAll(item);
+ }
+
+ // Clear focus on the item to remove any reference in the focusWidget
+ // chain.
+ item->clearFocus();
+
+ int index = item->d_func()->index;
+ if (index != -1) {
+ // Important: The index is useless until purgeRemovedItems() is
+ // called.
+ indexedItems[index] = (QGraphicsItem *)0;
+ if (!purgePending) {
+ purgePending = true;
+ q->update();
+ }
+ removedItems << item;
+ } else {
+ // Recently added items are purged immediately. unindexedItems() never
+ // contains stale items.
+ unindexedItems.removeAll(item);
+ q->update();
+ }
+
+ // Reset the mouse grabber and focus item data.
+ if (item == focusItem)
+ focusItem = 0;
+ if (item == lastFocusItem)
+ lastFocusItem = 0;
+ if (item == activeWindow) {
+ // ### deactivate...
+ activeWindow = 0;
+ }
+
+ // Disable selectionChanged() for individual items
+ ++selectionChanging;
+ int oldSelectedItemsSize = selectedItems.size();
+
+ // Update selected & hovered item bookkeeping
+ selectedItems.remove(item);
+ hoverItems.removeAll(item);
+ pendingUpdateItems.removeAll(item);
+ cachedItemsUnderMouse.removeAll(item);
+ unpolishedItems.removeAll(item);
+ dirtyItems.removeAll(item);
+
+ // Remove from scene transform cache
+ int transformIndex = item->d_func()->sceneTransformIndex;
+ if (transformIndex != -1) {
+ validTransforms.setBit(transformIndex, 0);
+ freeSceneTransformSlots.append(transformIndex);
+ }
+
+ // Remove all children recursively.
+ foreach (QGraphicsItem *child, item->children())
+ _q_removeItemLater(child);
+
+ // Reset the mouse grabber
+ if (mouseGrabberItems.contains(item))
+ ungrabMouse(item, /* item is dying */ true);
+
+ // Reset the keyboard grabber
+ if (keyboardGrabberItems.contains(item))
+ ungrabKeyboard(item, /* item is dying */ true);
+
+ // Reset the last mouse grabber item
+ if (item == lastMouseGrabberItem)
+ lastMouseGrabberItem = 0;
+
+ // Reenable selectionChanged() for individual items
+ --selectionChanging;
+ if (!selectionChanging && selectedItems.size() != oldSelectedItemsSize)
+ emit q->selectionChanged();
+}
+
+/*!
+ \internal
+
+ Removes stale pointers from all data structures.
+*/
+void QGraphicsScenePrivate::purgeRemovedItems()
+{
+ Q_Q(QGraphicsScene);
+
+ if (!purgePending && removedItems.isEmpty())
+ return;
+
+ // Remove stale items from the BSP tree.
+ if (indexMethod != QGraphicsScene::NoIndex)
+ bspTree.removeItems(removedItems);
+
+ // Purge this list.
+ removedItems.clear();
+ freeItemIndexes.clear();
+ for (int i = 0; i < indexedItems.size(); ++i) {
+ if (!indexedItems.at(i))
+ freeItemIndexes << i;
+ }
+ purgePending = false;
+
+ // No locality info for the items; update the whole scene.
+ q->update();
+}
+
+/*!
+ \internal
+
+ Starts or restarts the timer used for reindexing unindexed items.
+*/
+void QGraphicsScenePrivate::startIndexTimer()
+{
+ Q_Q(QGraphicsScene);
+ if (indexTimerId) {
+ restartIndexTimer = true;
+ } else {
+ indexTimerId = q->startTimer(QGRAPHICSSCENE_INDEXTIMER_TIMEOUT);
+ }
+}
+
+/*!
+ \internal
+*/
+void QGraphicsScenePrivate::addPopup(QGraphicsWidget *widget)
+{
+ Q_ASSERT(widget);
+ Q_ASSERT(!popupWidgets.contains(widget));
+ popupWidgets << widget;
+ if (QGraphicsWidget *focusWidget = widget->focusWidget()) {
+ focusWidget->setFocus(Qt::PopupFocusReason);
+ } else {
+ grabKeyboard((QGraphicsItem *)widget);
+ if (focusItem && popupWidgets.size() == 1) {
+ QFocusEvent event(QEvent::FocusOut, Qt::PopupFocusReason);
+ sendEvent(focusItem, &event);
+ }
+ }
+ grabMouse((QGraphicsItem *)widget);
+}
+
+/*!
+ \internal
+
+ Remove \a widget from the popup list. Important notes:
+
+ \a widget is guaranteed to be in the list of popups, but it might not be
+ the last entry; you can hide any item in the pop list before the others,
+ and this must cause all later mouse grabbers to lose the grab.
+*/
+void QGraphicsScenePrivate::removePopup(QGraphicsWidget *widget, bool itemIsDying)
+{
+ Q_ASSERT(widget);
+ int index = popupWidgets.indexOf(widget);
+ Q_ASSERT(index != -1);
+
+ for (int i = popupWidgets.size() - 1; i >= index; --i) {
+ QGraphicsWidget *widget = popupWidgets.takeLast();
+ ungrabMouse(widget, itemIsDying);
+ if (focusItem && popupWidgets.isEmpty()) {
+ QFocusEvent event(QEvent::FocusIn, Qt::PopupFocusReason);
+ sendEvent(focusItem, &event);
+ } else {
+ ungrabKeyboard((QGraphicsItem *)widget, itemIsDying);
+ }
+ if (!itemIsDying && widget->isVisible()) {
+ widget->hide();
+ widget->QGraphicsItem::d_ptr->explicitlyHidden = 0;
+ }
+ }
+}
+
+/*!
+ \internal
+*/
+void QGraphicsScenePrivate::grabMouse(QGraphicsItem *item, bool implicit)
+{
+ // Append to list of mouse grabber items, and send a mouse grab event.
+ if (mouseGrabberItems.contains(item)) {
+ if (mouseGrabberItems.last() == item)
+ qWarning("QGraphicsItem::grabMouse: already a mouse grabber");
+ else
+ qWarning("QGraphicsItem::grabMouse: already blocked by mouse grabber: %p",
+ mouseGrabberItems.last());
+ return;
+ }
+
+ // Send ungrab event to the last grabber.
+ if (!mouseGrabberItems.isEmpty()) {
+ QGraphicsItem *last = mouseGrabberItems.last();
+ if (lastMouseGrabberItemHasImplicitMouseGrab) {
+ // Implicit mouse grab is immediately lost.
+ last->ungrabMouse();
+ } else {
+ // Just send ungrab event to current grabber.
+ QEvent ungrabEvent(QEvent::UngrabMouse);
+ sendEvent(last, &ungrabEvent);
+ }
+ }
+
+ mouseGrabberItems << item;
+ lastMouseGrabberItemHasImplicitMouseGrab = implicit;
+
+ // Send grab event to current grabber.
+ QEvent grabEvent(QEvent::GrabMouse);
+ sendEvent(item, &grabEvent);
+}
+
+/*!
+ \internal
+*/
+void QGraphicsScenePrivate::ungrabMouse(QGraphicsItem *item, bool itemIsDying)
+{
+ int index = mouseGrabberItems.indexOf(item);
+ if (index == -1) {
+ qWarning("QGraphicsItem::ungrabMouse: not a mouse grabber");
+ return;
+ }
+
+ if (item != mouseGrabberItems.last()) {
+ // Recursively ungrab the next mouse grabber until we reach this item
+ // to ensure state consistency.
+ ungrabMouse(mouseGrabberItems.at(index + 1), itemIsDying);
+ }
+ if (!popupWidgets.isEmpty() && item == popupWidgets.last()) {
+ // If the item is a popup, go via removePopup to ensure state
+ // consistency and that it gets hidden correctly - beware that
+ // removePopup() reenters this function to continue removing the grab.
+ removePopup((QGraphicsWidget *)item, itemIsDying);
+ return;
+ }
+
+ // Send notification about mouse ungrab.
+ if (!itemIsDying) {
+ QEvent event(QEvent::UngrabMouse);
+ sendEvent(item, &event);
+ }
+
+ // Remove the item from the list of grabbers. Whenever this happens, we
+ // reset the implicitGrab (there can be only ever be one implicit grabber
+ // in a scene, and it is always the latest grabber; if the implicit grab
+ // is lost, it is not automatically regained.
+ mouseGrabberItems.takeLast();
+ lastMouseGrabberItemHasImplicitMouseGrab = false;
+
+ // Send notification about mouse regrab. ### It's unfortunate that all the
+ // items get a GrabMouse event, but this is a rare case with a simple
+ // implementation and it does ensure a consistent state.
+ if (!itemIsDying && !mouseGrabberItems.isEmpty()) {
+ QGraphicsItem *last = mouseGrabberItems.last();
+ QEvent event(QEvent::GrabMouse);
+ sendEvent(last, &event);
+ }
+}
+
+/*!
+ \internal
+*/
+void QGraphicsScenePrivate::clearMouseGrabber()
+{
+ if (!mouseGrabberItems.isEmpty())
+ mouseGrabberItems.first()->ungrabMouse();
+ lastMouseGrabberItem = 0;
+}
+
+/*!
+ \internal
+*/
+void QGraphicsScenePrivate::grabKeyboard(QGraphicsItem *item)
+{
+ if (keyboardGrabberItems.contains(item)) {
+ if (keyboardGrabberItems.last() == item)
+ qWarning("QGraphicsItem::grabKeyboard: already a keyboard grabber");
+ else
+ qWarning("QGraphicsItem::grabKeyboard: already blocked by keyboard grabber: %p",
+ keyboardGrabberItems.last());
+ return;
+ }
+
+ // Send ungrab event to the last grabber.
+ if (!keyboardGrabberItems.isEmpty()) {
+ // Just send ungrab event to current grabber.
+ QEvent ungrabEvent(QEvent::UngrabKeyboard);
+ sendEvent(keyboardGrabberItems.last(), &ungrabEvent);
+ }
+
+ keyboardGrabberItems << item;
+
+ // Send grab event to current grabber.
+ QEvent grabEvent(QEvent::GrabKeyboard);
+ sendEvent(item, &grabEvent);
+}
+
+/*!
+ \internal
+*/
+void QGraphicsScenePrivate::ungrabKeyboard(QGraphicsItem *item, bool itemIsDying)
+{
+ int index = keyboardGrabberItems.lastIndexOf(item);
+ if (index == -1) {
+ qWarning("QGraphicsItem::ungrabKeyboard: not a keyboard grabber");
+ return;
+ }
+ if (item != keyboardGrabberItems.last()) {
+ // Recursively ungrab the topmost keyboard grabber until we reach this
+ // item to ensure state consistency.
+ ungrabKeyboard(keyboardGrabberItems.at(index + 1), itemIsDying);
+ }
+
+ // Send notification about keyboard ungrab.
+ if (!itemIsDying) {
+ QEvent event(QEvent::UngrabKeyboard);
+ sendEvent(item, &event);
+ }
+
+ // Remove the item from the list of grabbers.
+ keyboardGrabberItems.takeLast();
+
+ // Send notification about mouse regrab.
+ if (!itemIsDying && !keyboardGrabberItems.isEmpty()) {
+ QGraphicsItem *last = keyboardGrabberItems.last();
+ QEvent event(QEvent::GrabKeyboard);
+ sendEvent(last, &event);
+ }
+}
+
+/*!
+ \internal
+*/
+void QGraphicsScenePrivate::clearKeyboardGrabber()
+{
+ if (!keyboardGrabberItems.isEmpty())
+ ungrabKeyboard(keyboardGrabberItems.first());
+}
+
+/*!
+ Returns all items for the screen position in \a event.
+*/
+QList<QGraphicsItem *> QGraphicsScenePrivate::itemsAtPosition(const QPoint &screenPos,
+ const QPointF &scenePos,
+ QWidget *widget) const
+{
+ Q_Q(const QGraphicsScene);
+ QGraphicsView *view = widget ? qobject_cast<QGraphicsView *>(widget->parentWidget()) : 0;
+ QList<QGraphicsItem *> items;
+ if (view)
+ items = view->items(view->viewport()->mapFromGlobal(screenPos));
+ else
+ items = q->items(scenePos);
+ return items;
+}
+
+/*!
+ \internal
+
+ Checks if item collides with the path and mode, but also checks that if it
+ doesn't collide, maybe its frame rect will.
+*/
+bool QGraphicsScenePrivate::itemCollidesWithPath(QGraphicsItem *item,
+ const QPainterPath &path,
+ Qt::ItemSelectionMode mode)
+{
+ if (item->collidesWithPath(path, mode))
+ return true;
+ if (item->isWidget()) {
+ // Check if this is a window, and if its frame rect collides.
+ QGraphicsWidget *widget = static_cast<QGraphicsWidget *>(item);
+ if (widget->isWindow()) {
+ QRectF frameRect = widget->windowFrameRect();
+ QPainterPath framePath;
+ framePath.addRect(frameRect);
+ bool intersects = path.intersects(frameRect);
+ if (mode == Qt::IntersectsItemShape || mode == Qt::IntersectsItemBoundingRect)
+ return intersects || path.contains(frameRect.topLeft())
+ || framePath.contains(path.elementAt(0));
+ return !intersects && path.contains(frameRect.topLeft());
+ }
+ }
+ return false;
+}
+
+/*!
+ \internal
+*/
+void QGraphicsScenePrivate::storeMouseButtonsForMouseGrabber(QGraphicsSceneMouseEvent *event)
+{
+ for (int i = 0x1; i <= 0x10; i <<= 1) {
+ if (event->buttons() & i) {
+ mouseGrabberButtonDownPos.insert(Qt::MouseButton(i),
+ mouseGrabberItems.last()->d_ptr->genericMapFromScene(event->scenePos(),
+ event->widget()));
+ mouseGrabberButtonDownScenePos.insert(Qt::MouseButton(i), event->scenePos());
+ mouseGrabberButtonDownScreenPos.insert(Qt::MouseButton(i), event->screenPos());
+ }
+ }
+}
+
+/*!
+ \internal
+*/
+void QGraphicsScenePrivate::installSceneEventFilter(QGraphicsItem *watched, QGraphicsItem *filter)
+{
+ sceneEventFilters.insert(watched, filter);
+}
+
+/*!
+ \internal
+*/
+void QGraphicsScenePrivate::removeSceneEventFilter(QGraphicsItem *watched, QGraphicsItem *filter)
+{
+ if (!sceneEventFilters.contains(watched))
+ return;
+
+ QMultiMap<QGraphicsItem *, QGraphicsItem *>::Iterator it = sceneEventFilters.lowerBound(watched);
+ QMultiMap<QGraphicsItem *, QGraphicsItem *>::Iterator end = sceneEventFilters.upperBound(watched);
+ do {
+ if (it.value() == filter)
+ it = sceneEventFilters.erase(it);
+ else
+ ++it;
+ } while (it != end);
+}
+
+/*!
+ \internal
+*/
+bool QGraphicsScenePrivate::filterEvent(QGraphicsItem *item, QEvent *event)
+{
+ if (item && !sceneEventFilters.contains(item))
+ return false;
+
+ QMultiMap<QGraphicsItem *, QGraphicsItem *>::Iterator it = sceneEventFilters.lowerBound(item);
+ QMultiMap<QGraphicsItem *, QGraphicsItem *>::Iterator end = sceneEventFilters.upperBound(item);
+ while (it != end) {
+ // ### The filterer and filteree might both be deleted.
+ if (it.value()->sceneEventFilter(it.key(), event))
+ return true;
+ ++it;
+ }
+ return false;
+}
+
+/*!
+ \internal
+
+ This is the final dispatch point for any events from the scene to the
+ item. It filters the event first - if the filter returns true, the event
+ is considered to have been eaten by the filter, and is therefore stopped
+ (the default filter returns false). Then/otherwise, if the item is
+ enabled, the event is sent; otherwise it is stopped.
+*/
+bool QGraphicsScenePrivate::sendEvent(QGraphicsItem *item, QEvent *event)
+{
+ if (filterEvent(item, event))
+ return false;
+ return (item && item->isEnabled()) ? item->sceneEvent(event) : false;
+}
+
+/*!
+ \internal
+*/
+void QGraphicsScenePrivate::cloneDragDropEvent(QGraphicsSceneDragDropEvent *dest,
+ QGraphicsSceneDragDropEvent *source)
+{
+ dest->setWidget(source->widget());
+ dest->setPos(source->pos());
+ dest->setScenePos(source->scenePos());
+ dest->setScreenPos(source->screenPos());
+ dest->setButtons(source->buttons());
+ dest->setModifiers(source->modifiers());
+ dest->setPossibleActions(source->possibleActions());
+ dest->setProposedAction(source->proposedAction());
+ dest->setDropAction(source->dropAction());
+ dest->setSource(source->source());
+ dest->setMimeData(source->mimeData());
+}
+
+/*!
+ \internal
+*/
+void QGraphicsScenePrivate::sendDragDropEvent(QGraphicsItem *item,
+ QGraphicsSceneDragDropEvent *dragDropEvent)
+{
+ dragDropEvent->setPos(item->d_ptr->genericMapFromScene(dragDropEvent->scenePos(), dragDropEvent->widget()));
+ sendEvent(item, dragDropEvent);
+}
+
+/*!
+ \internal
+*/
+void QGraphicsScenePrivate::sendHoverEvent(QEvent::Type type, QGraphicsItem *item,
+ QGraphicsSceneHoverEvent *hoverEvent)
+{
+ QGraphicsSceneHoverEvent event(type);
+ event.setWidget(hoverEvent->widget());
+ event.setPos(item->d_ptr->genericMapFromScene(hoverEvent->scenePos(), hoverEvent->widget()));
+ event.setScenePos(hoverEvent->scenePos());
+ event.setScreenPos(hoverEvent->screenPos());
+ event.setLastPos(item->d_ptr->genericMapFromScene(hoverEvent->lastScenePos(), hoverEvent->widget()));
+ event.setLastScenePos(hoverEvent->lastScenePos());
+ event.setLastScreenPos(hoverEvent->lastScreenPos());
+ event.setModifiers(hoverEvent->modifiers());
+ sendEvent(item, &event);
+}
+
+/*!
+ \internal
+*/
+void QGraphicsScenePrivate::sendMouseEvent(QGraphicsSceneMouseEvent *mouseEvent)
+{
+ if (mouseEvent->button() == 0 && mouseEvent->buttons() == 0 && lastMouseGrabberItemHasImplicitMouseGrab) {
+ // ### This is a temporary fix for until we get proper mouse
+ // grab events.
+ clearMouseGrabber();
+ return;
+ }
+
+ QGraphicsItem *item = mouseGrabberItems.last();
+ for (int i = 0x1; i <= 0x10; i <<= 1) {
+ Qt::MouseButton button = Qt::MouseButton(i);
+ mouseEvent->setButtonDownPos(button, mouseGrabberButtonDownPos.value(button, item->d_ptr->genericMapFromScene(mouseEvent->scenePos(), mouseEvent->widget())));
+ mouseEvent->setButtonDownScenePos(button, mouseGrabberButtonDownScenePos.value(button, mouseEvent->scenePos()));
+ mouseEvent->setButtonDownScreenPos(button, mouseGrabberButtonDownScreenPos.value(button, mouseEvent->screenPos()));
+ }
+ mouseEvent->setPos(item->d_ptr->genericMapFromScene(mouseEvent->scenePos(), mouseEvent->widget()));
+ mouseEvent->setLastPos(item->d_ptr->genericMapFromScene(mouseEvent->lastScenePos(), mouseEvent->widget()));
+ sendEvent(item, mouseEvent);
+}
+
+/*!
+ \internal
+*/
+void QGraphicsScenePrivate::mousePressEventHandler(QGraphicsSceneMouseEvent *mouseEvent)
+{
+ Q_Q(QGraphicsScene);
+
+ // Ignore by default, unless we find a mouse grabber that accepts it.
+ mouseEvent->ignore();
+
+ // Deliver to any existing mouse grabber.
+ if (!mouseGrabberItems.isEmpty()) {
+ // The event is ignored by default, but we disregard the event's
+ // accepted state after delivery; the mouse is grabbed, after all.
+ sendMouseEvent(mouseEvent);
+ return;
+ }
+
+ // Start by determining the number of items at the current position.
+ // Reuse value from earlier calculations if possible.
+ if (cachedItemsUnderMouse.isEmpty()) {
+ cachedItemsUnderMouse = itemsAtPosition(mouseEvent->screenPos(),
+ mouseEvent->scenePos(),
+ mouseEvent->widget());
+ }
+
+ // Update window activation.
+ QGraphicsWidget *newActiveWindow = windowForItem(cachedItemsUnderMouse.value(0));
+ if (newActiveWindow != activeWindow)
+ q->setActiveWindow(newActiveWindow);
+
+ // Set focus on the topmost enabled item that can take focus.
+ bool setFocus = false;
+ foreach (QGraphicsItem *item, cachedItemsUnderMouse) {
+ if (item->isEnabled() && (item->flags() & QGraphicsItem::ItemIsFocusable)) {
+ if (!item->isWidget() || ((QGraphicsWidget *)item)->focusPolicy() & Qt::ClickFocus) {
+ setFocus = true;
+ if (item != q->focusItem())
+ q->setFocusItem(item, Qt::MouseFocusReason);
+ break;
+ }
+ }
+ }
+
+ // If nobody could take focus, clear it.
+ if (!stickyFocus && !setFocus)
+ q->setFocusItem(0, Qt::MouseFocusReason);
+
+ // Find a mouse grabber by sending mouse press events to all mouse grabber
+ // candidates one at a time, until the event is accepted. It's accepted by
+ // default, so the receiver has to explicitly ignore it for it to pass
+ // through.
+ foreach (QGraphicsItem *item, cachedItemsUnderMouse) {
+ if (!(item->acceptedMouseButtons() & mouseEvent->button())) {
+ // Skip items that don't accept the event's mouse button.
+ continue;
+ }
+
+ grabMouse(item, /* implicit = */ true);
+ mouseEvent->accept();
+
+ // check if the item we are sending to are disabled (before we send the event)
+ bool disabled = !item->isEnabled();
+ bool isWindow = item->isWindow();
+ if (mouseEvent->type() == QEvent::GraphicsSceneMouseDoubleClick && item != lastMouseGrabberItem) {
+ // If this item is different from the item that received the last
+ // mouse event, and mouseEvent is a doubleclick event, then the
+ // event is converted to a press. Known limitation:
+ // Triple-clicking will not generate a doubleclick, though.
+ QGraphicsSceneMouseEvent mousePress(QEvent::GraphicsSceneMousePress);
+ mousePress.accept();
+ mousePress.setButton(mouseEvent->button());
+ mousePress.setButtons(mouseEvent->buttons());
+ mousePress.setScreenPos(mouseEvent->screenPos());
+ mousePress.setScenePos(mouseEvent->scenePos());
+ mousePress.setModifiers(mouseEvent->modifiers());
+ mousePress.setWidget(mouseEvent->widget());
+ mousePress.setButtonDownPos(mouseEvent->button(),
+ mouseEvent->buttonDownPos(mouseEvent->button()));
+ mousePress.setButtonDownScenePos(mouseEvent->button(),
+ mouseEvent->buttonDownScenePos(mouseEvent->button()));
+ mousePress.setButtonDownScreenPos(mouseEvent->button(),
+ mouseEvent->buttonDownScreenPos(mouseEvent->button()));
+ sendMouseEvent(&mousePress);
+ mouseEvent->setAccepted(mousePress.isAccepted());
+ } else {
+ sendMouseEvent(mouseEvent);
+ }
+
+ bool dontSendUngrabEvents = mouseGrabberItems.isEmpty() || mouseGrabberItems.last() != item;
+ if (disabled) {
+ ungrabMouse(item, /* itemIsDying = */ dontSendUngrabEvents);
+ break;
+ }
+ if (mouseEvent->isAccepted()) {
+ if (!mouseGrabberItems.isEmpty())
+ storeMouseButtonsForMouseGrabber(mouseEvent);
+ lastMouseGrabberItem = item;
+ return;
+ }
+ ungrabMouse(item, /* itemIsDying = */ dontSendUngrabEvents);
+
+ // Don't propagate through windows.
+ if (isWindow)
+ break;
+ }
+
+ // Is the event still ignored? Then the mouse press goes to the scene.
+ // Reset the mouse grabber, clear the selection, clear focus, and leave
+ // the event ignored so that it can propagate through the originating
+ // view.
+ if (!mouseEvent->isAccepted()) {
+ clearMouseGrabber();
+
+ QGraphicsView *view = mouseEvent->widget() ? qobject_cast<QGraphicsView *>(mouseEvent->widget()->parentWidget()) : 0;
+ bool dontClearSelection = view && view->dragMode() == QGraphicsView::ScrollHandDrag;
+ if (!dontClearSelection) {
+ // Clear the selection if the originating view isn't in scroll
+ // hand drag mode. The view will clear the selection if no drag
+ // happened.
+ q->clearSelection();
+ }
+ }
+}
+
+QGraphicsWidget *QGraphicsScenePrivate::windowForItem(const QGraphicsItem *item) const
+{
+ if (!item)
+ return 0;
+ do {
+ if (item->isWidget())
+ return static_cast<const QGraphicsWidget *>(item)->window();
+ item = item->parentItem();
+ } while (item);
+ return 0;
+}
+
+QList<QGraphicsItem *> QGraphicsScenePrivate::items_helper(const QRectF &rect,
+ Qt::ItemSelectionMode mode,
+ Qt::SortOrder order) const
+{
+ QList<QGraphicsItem *> items;
+
+ QPainterPath path;
+
+ // The index returns a rough estimate of what items are inside the rect.
+ // Refine it by iterating through all returned items.
+ QRectF adjustedRect = _q_adjustedRect(rect);
+ foreach (QGraphicsItem *item, estimateItemsInRect(adjustedRect)) {
+ // Find the item's scene transform in a clever way.
+ QTransform x = item->sceneTransform();
+ bool keep = false;
+
+ // ### _q_adjustedRect is only needed because QRectF::intersects,
+ // QRectF::contains and QTransform::map() and friends don't work with
+ // flat rectangles.
+ QRectF br = _q_adjustedRect(item->boundingRect());
+ if (mode >= Qt::ContainsItemBoundingRect) {
+ // Rect intersects/contains item's bounding rect
+ QRectF mbr = x.mapRect(br);
+ if ((mode == Qt::IntersectsItemBoundingRect && QRectF_intersects(rect, mbr))
+ || (mode == Qt::ContainsItemBoundingRect && rect != mbr && rect.contains(mbr))) {
+ items << item;
+ keep = true;
+ }
+ } else {
+ // Rect intersects/contains item's shape
+ if (QRectF_intersects(adjustedRect, x.mapRect(br))) {
+ bool ok;
+ QTransform xinv = x.inverted(&ok);
+ if (ok) {
+ if (path == QPainterPath())
+ path.addRect(rect);
+ if (itemCollidesWithPath(item, xinv.map(path), mode)) {
+ items << item;
+ keep = true;
+ }
+ }
+ }
+ }
+
+ if (keep && (item->flags() & QGraphicsItem::ItemClipsChildrenToShape)) {
+ // Recurse into children that clip children.
+ bool ok;
+ QTransform xinv = x.inverted(&ok);
+ if (ok) {
+ if (x.type() <= QTransform::TxScale) {
+ // Rect
+ childItems_helper(&items, item, xinv.mapRect(rect), mode);
+ } else {
+ // Polygon
+ childItems_helper(&items, item, xinv.map(rect), mode);
+ }
+ }
+ }
+ }
+
+ if (order != Qt::SortOrder(-1))
+ sortItems(&items, order, sortCacheEnabled);
+ return items;
+}
+
+QList<QGraphicsItem *> QGraphicsScenePrivate::items_helper(const QPolygonF &polygon,
+ Qt::ItemSelectionMode mode,
+ Qt::SortOrder order) const
+{
+ QList<QGraphicsItem *> items;
+
+ QRectF polyRect = _q_adjustedRect(polygon.boundingRect());
+ QPainterPath path;
+
+ // The index returns a rough estimate of what items are inside the rect.
+ // Refine it by iterating through all returned items.
+ foreach (QGraphicsItem *item, estimateItemsInRect(polyRect)) {
+ // Find the item's scene transform in a clever way.
+ QTransform x = item->sceneTransform();
+ bool keep = false;
+
+ // ### _q_adjustedRect is only needed because QRectF::intersects,
+ // QRectF::contains and QTransform::map() and friends don't work with
+ // flat rectangles.
+ QRectF br = _q_adjustedRect(item->boundingRect());
+ if (mode >= Qt::ContainsItemBoundingRect) {
+ // Polygon contains/intersects item's bounding rect
+ if (path == QPainterPath())
+ path.addPolygon(polygon);
+ if ((mode == Qt::IntersectsItemBoundingRect && path.intersects(x.mapRect(br)))
+ || (mode == Qt::ContainsItemBoundingRect && path.contains(x.mapRect(br)))) {
+ items << item;
+ keep = true;
+ }
+ } else {
+ // Polygon contains/intersects item's shape
+ if (QRectF_intersects(polyRect, x.mapRect(br))) {
+ bool ok;
+ QTransform xinv = x.inverted(&ok);
+ if (ok) {
+ if (path == QPainterPath())
+ path.addPolygon(polygon);
+ if (itemCollidesWithPath(item, xinv.map(path), mode)) {
+ items << item;
+ keep = true;
+ }
+ }
+ }
+ }
+
+ if (keep && (item->flags() & QGraphicsItem::ItemClipsChildrenToShape)) {
+ // Recurse into children that clip children.
+ bool ok;
+ QTransform xinv = x.inverted(&ok);
+ if (ok)
+ childItems_helper(&items, item, xinv.map(polygon), mode);
+ }
+ }
+
+ if (order != Qt::SortOrder(-1))
+ sortItems(&items, order, sortCacheEnabled);
+ return items;
+}
+
+QList<QGraphicsItem *> QGraphicsScenePrivate::items_helper(const QPainterPath &path,
+ Qt::ItemSelectionMode mode,
+ Qt::SortOrder order) const
+{
+ QList<QGraphicsItem *> items;
+ // The index returns a rough estimate of what items are inside the rect.
+ // Refine it by iterating through all returned items.
+ foreach (QGraphicsItem *item, estimateItemsInRect(_q_adjustedRect(path.controlPointRect()))) {
+ // Find the item's scene transform in a clever way.
+ QTransform x = item->sceneTransform();
+ bool ok;
+ QTransform xinv = x.inverted(&ok);
+ if (ok) {
+ QPainterPath mappedPath = xinv.map(path);
+ if (itemCollidesWithPath(item, mappedPath, mode)) {
+ items << item;
+ if (item->flags() & QGraphicsItem::ItemClipsChildrenToShape)
+ childItems_helper(&items, item, mappedPath, mode);
+ }
+ }
+ }
+
+ if (order != Qt::SortOrder(-1))
+ sortItems(&items, order, sortCacheEnabled);
+ return items;
+}
+
+void QGraphicsScenePrivate::childItems_helper(QList<QGraphicsItem *> *items,
+ const QGraphicsItem *parent,
+ const QRectF &rect,
+ Qt::ItemSelectionMode mode) const
+{
+ QPainterPath path;
+ bool parentClip = (parent->flags() & QGraphicsItem::ItemClipsChildrenToShape);
+ QRectF r = !parentClip ? _q_adjustedRect(rect) : _q_adjustedRect(rect).intersected(_q_adjustedRect(parent->boundingRect()));
+ if (r.isEmpty())
+ return;
+
+ QList<QGraphicsItem *> &children = parent->d_ptr->children;
+ for (int i = 0; i < children.size(); ++i) {
+ QGraphicsItem *item = children.at(i);
+ if (item->d_ptr->hasTransform && !item->transform().isInvertible())
+ continue;
+
+ // Skip invisible items and all their children.
+ if (!item->d_ptr->visible || qFuzzyCompare(item->effectiveOpacity(), qreal(0.0)))
+ continue;
+
+ // ### _q_adjustedRect is only needed because QRectF::intersects,
+ // QRectF::contains and QTransform::map() and friends don't work with
+ // flat rectangles.
+ QRectF br = _q_adjustedRect(item->boundingRect());
+ QRectF mbr = item->mapRectToParent(br);
+ bool keep = false;
+ if (mode >= Qt::ContainsItemBoundingRect) {
+ // Rect intersects/contains item's bounding rect
+ if ((mode == Qt::IntersectsItemBoundingRect && QRectF_intersects(rect, mbr))
+ || (mode == Qt::ContainsItemBoundingRect && rect != mbr && rect.contains(br))) {
+ items->append(item);
+ keep = true;
+ }
+ } else {
+ // Rect intersects/contains item's shape
+ if (QRectF_intersects(rect, mbr)) {
+ if (path == QPainterPath())
+ path.addRect(rect);
+ if (itemCollidesWithPath(item, item->mapFromParent(path), mode)) {
+ items->append(item);
+ keep = true;
+ }
+ }
+ }
+
+ if ((keep || !(item->flags() & QGraphicsItem::ItemClipsChildrenToShape)) && !item->d_ptr->children.isEmpty()) {
+ // Recurse into children.
+ if (!item->d_ptr->hasTransform || item->transform().type() <= QTransform::TxScale) {
+ // Rect
+ childItems_helper(items, item, item->mapRectFromParent(rect), mode);
+ } else {
+ // Polygon
+ childItems_helper(items, item, item->mapFromParent(rect), mode);
+ }
+ }
+ }
+}
+
+void QGraphicsScenePrivate::childItems_helper(QList<QGraphicsItem *> *items,
+ const QGraphicsItem *parent,
+ const QPolygonF &polygon,
+ Qt::ItemSelectionMode mode) const
+{
+ QPainterPath path;
+ bool parentClip = (parent->flags() & QGraphicsItem::ItemClipsChildrenToShape);
+ QRectF polyRect = _q_adjustedRect(polygon.boundingRect());
+ QRectF r = !parentClip ? polyRect : polyRect.intersected(_q_adjustedRect(parent->boundingRect()));
+ if (r.isEmpty())
+ return;
+
+ QList<QGraphicsItem *> &children = parent->d_ptr->children;
+ for (int i = 0; i < children.size(); ++i) {
+ QGraphicsItem *item = children.at(i);
+ if (item->d_ptr->hasTransform && !item->transform().isInvertible())
+ continue;
+
+ // Skip invisible items.
+ if (!item->d_ptr->visible || qFuzzyCompare(item->effectiveOpacity() + 1, qreal(1.0)))
+ continue;
+
+ // ### _q_adjustedRect is only needed because QRectF::intersects,
+ // QRectF::contains and QTransform::map() and friends don't work with
+ // flat rectangles.
+ QRectF br = _q_adjustedRect(item->boundingRect());
+ bool keep = false;
+ if (mode >= Qt::ContainsItemBoundingRect) {
+ // Polygon contains/intersects item's bounding rect
+ if (path == QPainterPath())
+ path.addPolygon(polygon);
+ if ((mode == Qt::IntersectsItemBoundingRect && path.intersects(item->mapRectToParent(br)))
+ || (mode == Qt::ContainsItemBoundingRect && path.contains(item->mapRectToParent(br)))) {
+ items->append(item);
+ keep = true;
+ }
+ } else {
+ // Polygon contains/intersects item's shape
+ if (QRectF_intersects(polyRect, item->mapRectToParent(br))) {
+ if (path == QPainterPath())
+ path.addPolygon(polygon);
+ if (itemCollidesWithPath(item, item->mapFromParent(path), mode)) {
+ items->append(item);
+ keep = true;
+ }
+ }
+ }
+
+ if ((keep || !(item->flags() & QGraphicsItem::ItemClipsChildrenToShape)) && !item->d_ptr->children.isEmpty()) {
+ // Recurse into children that clip children.
+ childItems_helper(items, item, item->mapFromParent(polygon), mode);
+ }
+ }
+}
+
+void QGraphicsScenePrivate::childItems_helper(QList<QGraphicsItem *> *items,
+ const QGraphicsItem *parent,
+ const QPainterPath &path,
+ Qt::ItemSelectionMode mode) const
+{
+ bool parentClip = (parent->flags() & QGraphicsItem::ItemClipsChildrenToShape);
+ QPainterPath intersectedPath = !parentClip ? path : path.intersected(parent->shape());
+ if (intersectedPath.isEmpty())
+ return;
+
+ QList<QGraphicsItem *> &children = parent->d_ptr->children;
+ for (int i = 0; i < children.size(); ++i) {
+ QGraphicsItem *item = children.at(i);
+
+ // Skip invisible items.
+ if (!item->d_ptr->visible || qFuzzyCompare(item->effectiveOpacity(), qreal(0.0)))
+ continue;
+
+ QTransform x = item->sceneTransform();
+
+ bool ok;
+ QTransform xinv = x.inverted(&ok);
+ if (ok) {
+ QPainterPath mappedPath = xinv.map(path);
+ if (itemCollidesWithPath(item, mappedPath, mode)) {
+ items->append(item);
+ if (!item->d_ptr->children.isEmpty())
+ childItems_helper(items, item, mappedPath, mode);
+ }
+ }
+ }
+}
+
+void QGraphicsScenePrivate::invalidateSortCache()
+{
+ Q_Q(QGraphicsScene);
+ if (!sortCacheEnabled || updatingSortCache)
+ return;
+
+ updatingSortCache = true;
+ QMetaObject::invokeMethod(q, "_q_updateSortCache", Qt::QueuedConnection);
+}
+
+/*!
+ \internal
+
+ Should not be exported, but we can't change that now.
+ ### Qt 5: Remove symbol / make static
+*/
+inline bool qt_closestLeaf(const QGraphicsItem *item1, const QGraphicsItem *item2)
+{
+ // Return true if sibling item1 is on top of item2.
+ const QGraphicsItemPrivate *d1 = item1->d_ptr;
+ const QGraphicsItemPrivate *d2 = item2->d_ptr;
+ bool f1 = d1->flags & QGraphicsItem::ItemStacksBehindParent;
+ bool f2 = d2->flags & QGraphicsItem::ItemStacksBehindParent;
+ if (f1 != f2) return f2;
+ qreal z1 = d1->z;
+ qreal z2 = d2->z;
+ return z1 != z2 ? z1 > z2 : item1 > item2;
+}
+
+/*!
+ \internal
+
+ Should not be exported, but we can't change that now.
+*/
+inline bool qt_closestItemFirst(const QGraphicsItem *item1, const QGraphicsItem *item2)
+{
+ return QGraphicsScenePrivate::closestItemFirst_withoutCache(item1, item2);
+}
+
+/*!
+ Returns true if \a item1 is on top of \a item2.
+
+ \internal
+*/
+bool QGraphicsScenePrivate::closestItemFirst_withoutCache(const QGraphicsItem *item1, const QGraphicsItem *item2)
+{
+ // Siblings? Just check their z-values.
+ const QGraphicsItemPrivate *d1 = item1->d_ptr;
+ const QGraphicsItemPrivate *d2 = item2->d_ptr;
+ if (d1->parent == d2->parent)
+ return qt_closestLeaf(item1, item2);
+
+ // Find common ancestor, and each item's ancestor closest to the common
+ // ancestor.
+ int item1Depth = d1->depth;
+ int item2Depth = d2->depth;
+ const QGraphicsItem *p = item1;
+ const QGraphicsItem *t1 = item1;
+ while (item1Depth > item2Depth && (p = p->d_ptr->parent)) {
+ if (p == item2) {
+ // item2 is one of item1's ancestors; item1 is on top
+ return !(t1->d_ptr->flags & QGraphicsItem::ItemStacksBehindParent);
+ }
+ t1 = p;
+ --item1Depth;
+ }
+ p = item2;
+ const QGraphicsItem *t2 = item2;
+ while (item2Depth > item1Depth && (p = p->d_ptr->parent)) {
+ if (p == item1) {
+ // item1 is one of item2's ancestors; item1 is not on top
+ return (t2->d_ptr->flags & QGraphicsItem::ItemStacksBehindParent);
+ }
+ t2 = p;
+ --item2Depth;
+ }
+
+ // item1Ancestor is now at the same level as item2Ancestor, but not the same.
+ const QGraphicsItem *a1 = t1;
+ const QGraphicsItem *a2 = t2;
+ while (a1) {
+ const QGraphicsItem *p1 = a1;
+ const QGraphicsItem *p2 = a2;
+ a1 = a1->parentItem();
+ a2 = a2->parentItem();
+ if (a1 && a1 == a2)
+ return qt_closestLeaf(p1, p2);
+ }
+
+ // No common ancestor? Then just compare the items' toplevels directly.
+ return qt_closestLeaf(t1->topLevelItem(), t2->topLevelItem());
+}
+
+/*!
+ Returns true if \a item2 is on top of \a item1.
+
+ \internal
+*/
+bool QGraphicsScenePrivate::closestItemLast_withoutCache(const QGraphicsItem *item1, const QGraphicsItem *item2)
+{
+ return closestItemFirst_withoutCache(item2, item1);
+}
+
+void QGraphicsScenePrivate::climbTree(QGraphicsItem *item, int *stackingOrder)
+{
+ if (!item->d_ptr->children.isEmpty()) {
+ QList<QGraphicsItem *> childList = item->d_ptr->children;
+ qSort(childList.begin(), childList.end(), qt_closestLeaf);
+ for (int i = 0; i < childList.size(); ++i) {
+ QGraphicsItem *item = childList.at(i);
+ if (!(item->flags() & QGraphicsItem::ItemStacksBehindParent))
+ climbTree(childList.at(i), stackingOrder);
+ }
+ item->d_ptr->globalStackingOrder = (*stackingOrder)++;
+ for (int i = 0; i < childList.size(); ++i) {
+ QGraphicsItem *item = childList.at(i);
+ if (item->flags() & QGraphicsItem::ItemStacksBehindParent)
+ climbTree(childList.at(i), stackingOrder);
+ }
+ } else {
+ item->d_ptr->globalStackingOrder = (*stackingOrder)++;
+ }
+}
+
+void QGraphicsScenePrivate::_q_updateSortCache()
+{
+ _q_updateIndex();
+
+ if (!sortCacheEnabled || !updatingSortCache)
+ return;
+
+ updatingSortCache = false;
+ int stackingOrder = 0;
+
+ QList<QGraphicsItem *> topLevels;
+
+ for (int i = 0; i < indexedItems.size(); ++i) {
+ QGraphicsItem *item = indexedItems.at(i);
+ if (item && item->parentItem() == 0)
+ topLevels << item;
+ }
+ for (int i = 0; i < unindexedItems.size(); ++i) {
+ QGraphicsItem *item = unindexedItems.at(i);
+ if (item->parentItem() == 0)
+ topLevels << item;
+ }
+
+ qSort(topLevels.begin(), topLevels.end(), qt_closestLeaf);
+ for (int i = 0; i < topLevels.size(); ++i)
+ climbTree(topLevels.at(i), &stackingOrder);
+}
+
+void QGraphicsScenePrivate::sortItems(QList<QGraphicsItem *> *itemList, Qt::SortOrder order,
+ bool sortCacheEnabled)
+{
+ if (sortCacheEnabled) {
+ if (order == Qt::AscendingOrder) {
+ qSort(itemList->begin(), itemList->end(), closestItemFirst_withCache);
+ } else if (order == Qt::DescendingOrder) {
+ qSort(itemList->begin(), itemList->end(), closestItemLast_withCache);
+ }
+ } else {
+ if (order == Qt::AscendingOrder) {
+ qSort(itemList->begin(), itemList->end(), closestItemFirst_withoutCache);
+ } else if (order == Qt::DescendingOrder) {
+ qSort(itemList->begin(), itemList->end(), closestItemLast_withoutCache);
+ }
+ }
+}
+
+/*!
+ \internal
+
+ Set the font and propagate the changes if the font is different from the
+ current font.
+*/
+void QGraphicsScenePrivate::setFont_helper(const QFont &font)
+{
+ if (this->font == font && this->font.resolve() == font.resolve())
+ return;
+ updateFont(font);
+}
+
+/*!
+ \internal
+
+ Resolve the scene's font against the application font, and propagate the
+ changes too all items in the scene.
+*/
+void QGraphicsScenePrivate::resolveFont()
+{
+ QFont naturalFont = qApp->font();
+ naturalFont.resolve(0);
+ QFont resolvedFont = font.resolve(naturalFont);
+ updateFont(resolvedFont);
+}
+
+/*!
+ \internal
+
+ Update the font, and whether or not it has changed, reresolve all fonts in
+ the scene.
+*/
+void QGraphicsScenePrivate::updateFont(const QFont &font)
+{
+ Q_Q(QGraphicsScene);
+
+ // Update local font setting.
+ this->font = font;
+
+ // Resolve the fonts of all top-level widget items, or widget items
+ // whose parent is not a widget.
+ foreach (QGraphicsItem *item, q->items()) {
+ if (!item->parentItem()) {
+ // Resolvefont for an item is a noop operation, but
+ // every item can be a widget, or can have a widget
+ // childre.
+ item->d_ptr->resolveFont(font.resolve());
+ }
+ }
+
+ // Send the scene a FontChange event.
+ QEvent event(QEvent::FontChange);
+ QApplication::sendEvent(q, &event);
+}
+
+/*!
+ \internal
+
+ Set the palette and propagate the changes if the palette is different from
+ the current palette.
+*/
+void QGraphicsScenePrivate::setPalette_helper(const QPalette &palette)
+{
+ if (this->palette == palette && this->palette.resolve() == palette.resolve())
+ return;
+ updatePalette(palette);
+}
+
+/*!
+ \internal
+
+ Resolve the scene's palette against the application palette, and propagate
+ the changes too all items in the scene.
+*/
+void QGraphicsScenePrivate::resolvePalette()
+{
+ QPalette naturalPalette = qApp->palette();
+ naturalPalette.resolve(0);
+ QPalette resolvedPalette = palette.resolve(naturalPalette);
+ updatePalette(resolvedPalette);
+}
+
+/*!
+ \internal
+
+ Update the palette, and whether or not it has changed, reresolve all
+ palettes in the scene.
+*/
+void QGraphicsScenePrivate::updatePalette(const QPalette &palette)
+{
+ Q_Q(QGraphicsScene);
+
+ // Update local palette setting.
+ this->palette = palette;
+
+ // Resolve the palettes of all top-level widget items, or widget items
+ // whose parent is not a widget.
+ foreach (QGraphicsItem *item, q->items()) {
+ if (!item->parentItem()) {
+ // Resolvefont for an item is a noop operation, but
+ // every item can be a widget, or can have a widget
+ // childre.
+ item->d_ptr->resolvePalette(palette.resolve());
+ }
+ }
+
+ // Send the scene a PaletteChange event.
+ QEvent event(QEvent::PaletteChange);
+ QApplication::sendEvent(q, &event);
+}
+
+/*!
+ Constructs a QGraphicsScene object. The \a parent parameter is
+ passed to QObject's constructor.
+*/
+QGraphicsScene::QGraphicsScene(QObject *parent)
+ : QObject(*new QGraphicsScenePrivate, parent)
+{
+ d_func()->init();
+}
+
+/*!
+ Constructs a QGraphicsScene object, using \a sceneRect for its
+ scene rectangle. The \a parent parameter is passed to QObject's
+ constructor.
+
+ \sa sceneRect
+*/
+QGraphicsScene::QGraphicsScene(const QRectF &sceneRect, QObject *parent)
+ : QObject(*new QGraphicsScenePrivate, parent)
+{
+ setSceneRect(sceneRect);
+ d_func()->init();
+}
+
+/*!
+ Constructs a QGraphicsScene object, using the rectangle specified
+ by (\a x, \a y), and the given \a width and \a height for its
+ scene rectangle. The \a parent parameter is passed to QObject's
+ constructor.
+
+ \sa sceneRect
+*/
+QGraphicsScene::QGraphicsScene(qreal x, qreal y, qreal width, qreal height, QObject *parent)
+ : QObject(*new QGraphicsScenePrivate, parent)
+{
+ setSceneRect(x, y, width, height);
+ d_func()->init();
+}
+
+/*!
+ Destroys the QGraphicsScene object.
+*/
+QGraphicsScene::~QGraphicsScene()
+{
+ Q_D(QGraphicsScene);
+ // Remove this scene from qApp's global scene list.
+ qApp->d_func()->scene_list.removeAll(this);
+
+ clear();
+
+ // Remove this scene from all associated views.
+ for (int j = 0; j < d->views.size(); ++j)
+ d->views.at(j)->setScene(0);
+}
+
+/*!
+ \property QGraphicsScene::sceneRect
+ \brief the scene rectangle; the bounding rectangle of the scene
+
+ The scene rectangle defines the extent of the scene. It is
+ primarily used by QGraphicsView to determine the view's default
+ scrollable area, and by QGraphicsScene to manage item indexing.
+
+ If unset, or if set to a null QRectF, sceneRect() will return the largest
+ bounding rect of all items on the scene since the scene was created (i.e.,
+ a rectangle that grows when items are added to or moved in the scene, but
+ never shrinks).
+
+ \sa width(), height(), QGraphicsView::sceneRect
+*/
+QRectF QGraphicsScene::sceneRect() const
+{
+ Q_D(const QGraphicsScene);
+ const_cast<QGraphicsScenePrivate *>(d)->_q_updateIndex();
+ return d->hasSceneRect ? d->sceneRect : d->growingItemsBoundingRect;
+}
+void QGraphicsScene::setSceneRect(const QRectF &rect)
+{
+ Q_D(QGraphicsScene);
+ if (rect != d->sceneRect) {
+ d->hasSceneRect = !rect.isNull();
+ d->sceneRect = rect;
+ d->resetIndex();
+ emit sceneRectChanged(rect);
+ }
+}
+
+/*!
+ \fn qreal QGraphicsScene::width() const
+
+ This convenience function is equivalent to calling sceneRect().width().
+
+ \sa height()
+*/
+
+/*!
+ \fn qreal QGraphicsScene::height() const
+
+ This convenience function is equivalent to calling \c sceneRect().height().
+
+ \sa width()
+*/
+
+/*!
+ Renders the \a source rect from scene into \a target, using \a painter. This
+ function is useful for capturing the contents of the scene onto a paint
+ device, such as a QImage (e.g., to take a screenshot), or for printing
+ with QPrinter. For example:
+
+ \snippet doc/src/snippets/code/src_gui_graphicsview_qgraphicsscene.cpp 1
+
+ If \a source is a null rect, this function will use sceneRect() to
+ determine what to render. If \a target is a null rect, the dimensions of \a
+ painter's paint device will be used.
+
+ The source rect contents will be transformed according to \a
+ aspectRatioMode to fit into the target rect. By default, the aspect ratio
+ is kept, and \a source is scaled to fit in \a target.
+
+ \sa QGraphicsView::render()
+*/
+void QGraphicsScene::render(QPainter *painter, const QRectF &target, const QRectF &source,
+ Qt::AspectRatioMode aspectRatioMode)
+{
+ Q_D(QGraphicsScene);
+
+ // Default source rect = scene rect
+ QRectF sourceRect = source;
+ if (sourceRect.isNull())
+ sourceRect = sceneRect();
+
+ // Default target rect = device rect
+ QRectF targetRect = target;
+ if (targetRect.isNull()) {
+ if (painter->device()->devType() == QInternal::Picture)
+ targetRect = sourceRect;
+ else
+ targetRect.setRect(0, 0, painter->device()->width(), painter->device()->height());
+ }
+
+ // Find the ideal x / y scaling ratio to fit \a source into \a target.
+ qreal xratio = targetRect.width() / sourceRect.width();
+ qreal yratio = targetRect.height() / sourceRect.height();
+
+ // Scale according to the aspect ratio mode.
+ switch (aspectRatioMode) {
+ case Qt::KeepAspectRatio:
+ xratio = yratio = qMin(xratio, yratio);
+ break;
+ case Qt::KeepAspectRatioByExpanding:
+ xratio = yratio = qMax(xratio, yratio);
+ break;
+ case Qt::IgnoreAspectRatio:
+ break;
+ }
+
+ // Find all items to draw, and reverse the list (we want to draw
+ // in reverse order).
+ QList<QGraphicsItem *> itemList = items(sourceRect, Qt::IntersectsItemBoundingRect);
+ QGraphicsItem **itemArray = new QGraphicsItem *[itemList.size()];
+ int numItems = itemList.size();
+ for (int i = 0; i < numItems; ++i)
+ itemArray[numItems - i - 1] = itemList.at(i);
+ itemList.clear();
+
+ painter->save();
+
+ // Transform the painter.
+ painter->setClipRect(targetRect);
+ QTransform painterTransform;
+ painterTransform *= QTransform()
+ .translate(targetRect.left(), targetRect.top())
+ .scale(xratio, yratio)
+ .translate(-sourceRect.left(), -sourceRect.top());
+ painter->setWorldTransform(painterTransform, true);
+
+ // Two unit vectors.
+ QLineF v1(0, 0, 1, 0);
+ QLineF v2(0, 0, 0, 1);
+
+ // Generate the style options
+ QStyleOptionGraphicsItem *styleOptionArray = new QStyleOptionGraphicsItem[numItems];
+ for (int i = 0; i < numItems; ++i) {
+ QGraphicsItem *item = itemArray[i];
+
+ QStyleOptionGraphicsItem option;
+ option.state = QStyle::State_None;
+ option.rect = item->boundingRect().toRect();
+ if (item->isSelected())
+ option.state |= QStyle::State_Selected;
+ if (item->isEnabled())
+ option.state |= QStyle::State_Enabled;
+ if (item->hasFocus())
+ option.state |= QStyle::State_HasFocus;
+ if (d->hoverItems.contains(item))
+ option.state |= QStyle::State_MouseOver;
+ if (item == mouseGrabberItem())
+ option.state |= QStyle::State_Sunken;
+
+ // Calculate a simple level-of-detail metric.
+ // ### almost identical code in QGraphicsView::paintEvent()
+ // and QGraphicsView::render() - consider refactoring
+ QTransform itemToDeviceTransform;
+ if (item->d_ptr->itemIsUntransformable()) {
+ itemToDeviceTransform = item->deviceTransform(painterTransform);
+ } else {
+ itemToDeviceTransform = item->sceneTransform() * painterTransform;
+ }
+
+ option.levelOfDetail = qSqrt(itemToDeviceTransform.map(v1).length() * itemToDeviceTransform.map(v2).length());
+ option.matrix = itemToDeviceTransform.toAffine(); //### discards perspective
+
+ option.exposedRect = item->boundingRect();
+ option.exposedRect &= itemToDeviceTransform.inverted().mapRect(targetRect);
+
+ styleOptionArray[i] = option;
+ }
+
+ // Render the scene.
+ drawBackground(painter, sourceRect);
+ drawItems(painter, numItems, itemArray, styleOptionArray);
+ drawForeground(painter, sourceRect);
+
+ delete [] itemArray;
+ delete [] styleOptionArray;
+
+ painter->restore();
+}
+
+/*!
+ \property QGraphicsScene::itemIndexMethod
+ \brief the item indexing method.
+
+ QGraphicsScene applies an indexing algorithm to the scene, to speed up
+ item discovery functions like items() and itemAt(). Indexing is most
+ efficient for static scenes (i.e., where items don't move around). For
+ dynamic scenes, or scenes with many animated items, the index bookkeeping
+ can outweight the fast lookup speeds.
+
+ For the common case, the default index method BspTreeIndex works fine. If
+ your scene uses many animations and you are experiencing slowness, you can
+ disable indexing by calling \c setItemIndexMethod(NoIndex).
+
+ \sa bspTreeDepth
+*/
+QGraphicsScene::ItemIndexMethod QGraphicsScene::itemIndexMethod() const
+{
+ Q_D(const QGraphicsScene);
+ return d->indexMethod;
+}
+void QGraphicsScene::setItemIndexMethod(ItemIndexMethod method)
+{
+ Q_D(QGraphicsScene);
+ d->resetIndex();
+ d->indexMethod = method;
+}
+
+/*!
+ \property QGraphicsScene::bspTreeDepth
+ \brief the depth of QGraphicsScene's BSP index tree
+ \since 4.3
+
+ This property has no effect when NoIndex is used.
+
+ This value determines the depth of QGraphicsScene's BSP tree. The depth
+ directly affects QGraphicsScene's performance and memory usage; the latter
+ growing exponentially with the depth of the tree. With an optimal tree
+ depth, QGraphicsScene can instantly determine the locality of items, even
+ for scenes with thousands or millions of items. This also greatly improves
+ rendering performance.
+
+ By default, the value is 0, in which case Qt will guess a reasonable
+ default depth based on the size, location and number of items in the
+ scene. If these parameters change frequently, however, you may experience
+ slowdowns as QGraphicsScene retunes the depth internally. You can avoid
+ potential slowdowns by fixating the tree depth through setting this
+ property.
+
+ The depth of the tree and the size of the scene rectangle decide the
+ granularity of the scene's partitioning. The size of each scene segment is
+ determined by the following algorithm:
+
+ \snippet doc/src/snippets/code/src_gui_graphicsview_qgraphicsscene.cpp 2
+
+ The BSP tree has an optimal size when each segment contains between 0 and
+ 10 items.
+
+ \sa itemIndexMethod
+*/
+int QGraphicsScene::bspTreeDepth() const
+{
+ Q_D(const QGraphicsScene);
+ return d->bspTreeDepth;
+}
+void QGraphicsScene::setBspTreeDepth(int depth)
+{
+ Q_D(QGraphicsScene);
+ if (d->bspTreeDepth == depth)
+ return;
+
+ if (depth < 0) {
+ qWarning("QGraphicsScene::setBspTreeDepth: invalid depth %d ignored; must be >= 0", depth);
+ return;
+ }
+
+ d->bspTreeDepth = depth;
+ d->resetIndex();
+}
+
+/*!
+ \property QGraphicsScene::sortCacheEnabled
+ \brief whether sort caching is enabled
+ \since 4.5
+
+ When enabled, this property adds a cache that speeds up sorting and
+ transformations for scenes with deep hierarchies (i.e., items with many
+ levels of descendents), at the cost of using more memory (approx. 100 more
+ bytes of memory per item).
+
+ Items that are not part of a deep hierarchy suffer no penalty from this
+ cache.
+*/
+bool QGraphicsScene::isSortCacheEnabled() const
+{
+ Q_D(const QGraphicsScene);
+ return d->sortCacheEnabled;
+}
+void QGraphicsScene::setSortCacheEnabled(bool enabled)
+{
+ Q_D(QGraphicsScene);
+ if (enabled == d->sortCacheEnabled)
+ return;
+ if ((d->sortCacheEnabled = enabled))
+ d->invalidateSortCache();
+}
+
+/*!
+ Calculates and returns the bounding rect of all items on the scene. This
+ function works by iterating over all items, and because if this, it can
+ be slow for large scenes.
+
+ \sa sceneRect()
+*/
+QRectF QGraphicsScene::itemsBoundingRect() const
+{
+ QRectF boundingRect;
+ foreach (QGraphicsItem *item, items())
+ boundingRect |= item->sceneBoundingRect();
+ return boundingRect;
+}
+
+/*!
+ Returns a list of all items on the scene, in no particular order.
+
+ \sa addItem(), removeItem()
+*/
+QList<QGraphicsItem *> QGraphicsScene::items() const
+{
+ Q_D(const QGraphicsScene);
+ const_cast<QGraphicsScenePrivate *>(d)->purgeRemovedItems();
+
+ // If freeItemIndexes is empty, we know there are no holes in indexedItems and
+ // unindexedItems.
+ if (d->freeItemIndexes.isEmpty()) {
+ if (d->unindexedItems.isEmpty())
+ return d->indexedItems;
+ return d->indexedItems + d->unindexedItems;
+ }
+
+ // Rebuild the list of items to avoid holes. ### We could also just
+ // compress the item lists at this point.
+ QList<QGraphicsItem *> itemList;
+ foreach (QGraphicsItem *item, d->indexedItems + d->unindexedItems) {
+ if (item)
+ itemList << item;
+ }
+ return itemList;
+}
+
+/*!
+ Returns all visible items at position \a pos in the scene. The items are
+ listed in descending Z order (i.e., the first item in the list is the
+ top-most item, and the last item is the bottom-most item).
+
+ \sa itemAt()
+*/
+QList<QGraphicsItem *> QGraphicsScene::items(const QPointF &pos) const
+{
+ QList<QGraphicsItem *> itemsAtPoint;
+
+ // Find all items within a 1x1 rect area starting at pos. This can be
+ // inefficient for scenes that use small coordinates (like unity
+ // coordinates), or for detailed graphs. ### The index should support
+ // fetching items at a pos to avoid this limitation.
+ foreach (QGraphicsItem *item, items(QRectF(pos, QSizeF(1, 1)), Qt::IntersectsItemBoundingRect)) {
+ if (item->contains(item->mapFromScene(pos)))
+ itemsAtPoint << item;
+ }
+ return itemsAtPoint;
+}
+
+
+/*!
+ \fn QList<QGraphicsItem *> QGraphicsScene::items(const QRectF &rectangle, Qt::ItemSelectionMode mode) const
+
+ \overload
+
+ Returns all visible items that, depending on \a mode, are either inside or
+ intersect with the specified \a rectangle.
+
+ The default value for \a mode is Qt::IntersectsItemShape; all items whose
+ exact shape intersects with or is contained by \a rectangle are returned.
+
+ \sa itemAt()
+*/
+QList<QGraphicsItem *> QGraphicsScene::items(const QRectF &rect, Qt::ItemSelectionMode mode) const
+{
+ Q_D(const QGraphicsScene);
+ return d->items_helper(rect, mode, Qt::AscendingOrder);
+}
+
+/*!
+ \fn QList<QGraphicsItem *> QGraphicsScene::items(qreal x, qreal y, qreal w, qreal h, Qt::ItemSelectionMode mode) const
+ \since 4.3
+
+ This convenience function is equivalent to calling items(QRectF(\a x, \a y, \a w, \a h), \a mode).
+*/
+
+/*!
+ \overload
+
+ Returns all visible items that, depending on \a mode, are either inside or
+ intersect with the polygon \a polygon.
+
+ The default value for \a mode is Qt::IntersectsItemShape; all items whose
+ exact shape intersects with or is contained by \a polygon are returned.
+
+ \sa itemAt()
+*/
+QList<QGraphicsItem *> QGraphicsScene::items(const QPolygonF &polygon, Qt::ItemSelectionMode mode) const
+{
+ Q_D(const QGraphicsScene);
+ return d->items_helper(polygon, mode, Qt::AscendingOrder);
+}
+
+/*!
+ \overload
+
+ Returns all visible items that, depending on \a path, are either inside or
+ intersect with the path \a path.
+
+ The default value for \a mode is Qt::IntersectsItemShape; all items whose
+ exact shape intersects with or is contained by \a path are returned.
+
+ \sa itemAt()
+*/
+QList<QGraphicsItem *> QGraphicsScene::items(const QPainterPath &path, Qt::ItemSelectionMode mode) const
+{
+ Q_D(const QGraphicsScene);
+ return d->items_helper(path, mode, Qt::AscendingOrder);
+}
+
+/*!
+ Returns a list of all items that collide with \a item. Collisions are
+ determined by calling QGraphicsItem::collidesWithItem(); the collision
+ detection is determined by \a mode. By default, all items whose shape
+ intersects \a item or is contained inside \a item's shape are returned.
+
+ The items are returned in descending Z order (i.e., the first item in the
+ list is the top-most item, and the last item is the bottom-most item).
+
+ \sa items(), itemAt(), QGraphicsItem::collidesWithItem()
+*/
+QList<QGraphicsItem *> QGraphicsScene::collidingItems(const QGraphicsItem *item,
+ Qt::ItemSelectionMode mode) const
+{
+ Q_D(const QGraphicsScene);
+ if (!item) {
+ qWarning("QGraphicsScene::collidingItems: cannot find collisions for null item");
+ return QList<QGraphicsItem *>();
+ }
+
+ QList<QGraphicsItem *> tmp;
+ foreach (QGraphicsItem *itemInVicinity, d->estimateItemsInRect(item->sceneBoundingRect())) {
+ if (item != itemInVicinity && item->collidesWithItem(itemInVicinity, mode))
+ tmp << itemInVicinity;
+ }
+ d->sortItems(&tmp, Qt::AscendingOrder, d->sortCacheEnabled);
+ return tmp;
+}
+
+/*!
+ \fn QGraphicsItem *QGraphicsScene::itemAt(const QPointF &position) const
+
+ Returns the topmost visible item at the specified \a position, or 0 if
+ there are no items at this position.
+
+ \note The topmost item is the one with the highest Z-value.
+
+ \sa items(), collidingItems(), QGraphicsItem::setZValue()
+*/
+QGraphicsItem *QGraphicsScene::itemAt(const QPointF &pos) const
+{
+ QList<QGraphicsItem *> itemsAtPoint = items(pos);
+ return itemsAtPoint.isEmpty() ? 0 : itemsAtPoint.first();
+}
+
+/*!
+ \fn QGraphicsScene::itemAt(qreal x, qreal y) const
+ \overload
+
+ Returns the topmost item at the position specified by (\a x, \a
+ y), or 0 if there are no items at this position.
+
+ This convenience function is equivalent to calling \c
+ {itemAt(QPointF(x, y))}.
+
+ \note The topmost item is the one with the highest Z-value.
+*/
+
+/*!
+ Returns a list of all currently selected items. The items are
+ returned in no particular order.
+
+ \sa setSelectionArea()
+*/
+QList<QGraphicsItem *> QGraphicsScene::selectedItems() const
+{
+ Q_D(const QGraphicsScene);
+
+ // Optimization: Lazily removes items that are not selected.
+ QGraphicsScene *that = const_cast<QGraphicsScene *>(this);
+ QSet<QGraphicsItem *> actuallySelectedSet;
+ foreach (QGraphicsItem *item, that->d_func()->selectedItems) {
+ if (item->isSelected())
+ actuallySelectedSet << item;
+ }
+
+ that->d_func()->selectedItems = actuallySelectedSet;
+
+ return d->selectedItems.values();
+}
+
+/*!
+ Returns the selection area that was previously set with
+ setSelectionArea(), or an empty QPainterPath if no selection area has been
+ set.
+
+ \sa setSelectionArea()
+*/
+QPainterPath QGraphicsScene::selectionArea() const
+{
+ Q_D(const QGraphicsScene);
+ return d->selectionArea;
+}
+
+/*!
+ Sets the selection area to \a path. All items within this area are
+ immediately selected, and all items outside are unselected. You can get
+ the list of all selected items by calling selectedItems().
+
+ For an item to be selected, it must be marked as \e selectable
+ (QGraphicsItem::ItemIsSelectable).
+
+ \sa clearSelection(), selectionArea()
+*/
+void QGraphicsScene::setSelectionArea(const QPainterPath &path)
+{
+ setSelectionArea(path, Qt::IntersectsItemShape);
+}
+
+/*!
+ \overload
+ \since 4.3
+
+ Sets the selection area to \a path using \a mode to determine if items are
+ included in the selection area.
+
+ \sa clearSelection(), selectionArea()
+*/
+void QGraphicsScene::setSelectionArea(const QPainterPath &path, Qt::ItemSelectionMode mode)
+{
+ Q_D(QGraphicsScene);
+
+ // Note: with boolean path operations, we can improve performance here
+ // quite a lot by "growing" the old path instead of replacing it. That
+ // allows us to only check the intersect area for changes, instead of
+ // reevaluating the whole path over again.
+ d->selectionArea = path;
+
+ QSet<QGraphicsItem *> unselectItems = d->selectedItems;
+
+ // Disable emitting selectionChanged() for individual items.
+ ++d->selectionChanging;
+ bool changed = false;
+
+ // Set all items in path to selected.
+ foreach (QGraphicsItem *item, items(path, mode)) {
+ if (item->flags() & QGraphicsItem::ItemIsSelectable) {
+ if (!item->isSelected())
+ changed = true;
+ unselectItems.remove(item);
+ item->setSelected(true);
+ }
+ }
+
+ // Unselect all items outside path.
+ foreach (QGraphicsItem *item, unselectItems) {
+ item->setSelected(false);
+ changed = true;
+ }
+
+ // Reenable emitting selectionChanged() for individual items.
+ --d->selectionChanging;
+
+ if (!d->selectionChanging && changed)
+ emit selectionChanged();
+}
+
+/*!
+ Clears the current selection.
+
+ \sa setSelectionArea(), selectedItems()
+*/
+void QGraphicsScene::clearSelection()
+{
+ Q_D(QGraphicsScene);
+
+ // Disable emitting selectionChanged
+ ++d->selectionChanging;
+ bool changed = !d->selectedItems.isEmpty();
+
+ foreach (QGraphicsItem *item, d->selectedItems)
+ item->setSelected(false);
+ d->selectedItems.clear();
+
+ // Reenable emitting selectionChanged() for individual items.
+ --d->selectionChanging;
+
+ if (!d->selectionChanging && changed)
+ emit selectionChanged();
+}
+
+/*!
+ \since 4.4
+
+ Removes and deletes all items from the scene, but otherwise leaves the
+ state of the scene unchanged.
+
+ \sa addItem()
+*/
+void QGraphicsScene::clear()
+{
+ Q_D(QGraphicsScene);
+ // Recursive descent delete
+ for (int i = 0; i < d->indexedItems.size(); ++i) {
+ if (QGraphicsItem *item = d->indexedItems.at(i)) {
+ if (!item->parentItem())
+ delete item;
+ }
+ }
+ QList<QGraphicsItem *> unindexedParents;
+ for (int i = 0; i < d->unindexedItems.size(); ++i) {
+ QGraphicsItem *item = d->unindexedItems.at(i);
+ if (!item->parentItem())
+ unindexedParents << item;
+ }
+ d->unindexedItems.clear();
+ qDeleteAll(unindexedParents);
+
+ d->indexedItems.clear();
+ d->freeItemIndexes.clear();
+ d->lastItemCount = 0;
+ d->bspTree.clear();
+ d->largestUntransformableItem = QRectF();
+}
+
+/*!
+ Groups all items in \a items into a new QGraphicsItemGroup, and returns a
+ pointer to the group. The group is created with the common ancestor of \a
+ items as its parent, and with position (0, 0). The items are all
+ reparented to the group, and their positions and transformations are
+ mapped to the group. If \a items is empty, this function will return an
+ empty top-level QGraphicsItemGroup.
+
+ QGraphicsScene has ownership of the group item; you do not need to delete
+ it. To dismantle (ungroup) a group, call destroyItemGroup().
+
+ \sa destroyItemGroup(), QGraphicsItemGroup::addToGroup()
+*/
+QGraphicsItemGroup *QGraphicsScene::createItemGroup(const QList<QGraphicsItem *> &items)
+{
+ // Build a list of the first item's ancestors
+ QList<QGraphicsItem *> ancestors;
+ int n = 0;
+ if (!items.isEmpty()) {
+ QGraphicsItem *parent = items.at(n++);
+ while ((parent = parent->parentItem()))
+ ancestors.append(parent);
+ }
+
+ // Find the common ancestor for all items
+ QGraphicsItem *commonAncestor = 0;
+ if (!ancestors.isEmpty()) {
+ while (n < items.size()) {
+ int commonIndex = -1;
+ QGraphicsItem *parent = items.at(n++);
+ do {
+ int index = ancestors.indexOf(parent, qMax(0, commonIndex));
+ if (index != -1) {
+ commonIndex = index;
+ break;
+ }
+ } while ((parent = parent->parentItem()));
+
+ if (commonIndex == -1) {
+ commonAncestor = 0;
+ break;
+ }
+
+ commonAncestor = ancestors.at(commonIndex);
+ }
+ }
+
+ // Create a new group at that level
+ QGraphicsItemGroup *group = new QGraphicsItemGroup(commonAncestor);
+ if (!commonAncestor)
+ addItem(group);
+ foreach (QGraphicsItem *item, items)
+ group->addToGroup(item);
+ return group;
+}
+
+/*!
+ Reparents all items in \a group to \a group's parent item, then removes \a
+ group from the scene, and finally deletes it. The items' positions and
+ transformations are mapped from the group to the group's parent.
+
+ \sa createItemGroup(), QGraphicsItemGroup::removeFromGroup()
+*/
+void QGraphicsScene::destroyItemGroup(QGraphicsItemGroup *group)
+{
+ foreach (QGraphicsItem *item, group->children())
+ group->removeFromGroup(item);
+ removeItem(group);
+ delete group;
+}
+
+/*!
+ Adds or moves the item \a item and all its childen to the scene.
+
+ If the item is visible (i.e., QGraphicsItem::isVisible() returns
+ true), QGraphicsScene will emit changed() once control goes back
+ to the event loop.
+
+ If the item is already in a different scene, it will first be removed from
+ its old scene, and then added to this scene as a top-level.
+
+ QGraphicsScene will send ItemSceneChange notifications to \a item while
+ it is added to the scene. If item does not currently belong to a scene, only one
+ notification is sent. If it does belong to scene already (i.e., it is
+ moved to this scene), QGraphicsScene will send an addition notification as
+ the item is removed from its previous scene.
+
+ \sa removeItem(), addEllipse(), addLine(), addPath(), addPixmap(),
+ addRect(), addText(), addWidget()
+*/
+void QGraphicsScene::addItem(QGraphicsItem *item)
+{
+ Q_D(QGraphicsScene);
+ if (!item) {
+ qWarning("QGraphicsScene::addItem: cannot add null item");
+ return;
+ }
+ if (item->scene() == this) {
+ qWarning("QGraphicsScene::addItem: item has already been added to this scene");
+ return;
+ }
+
+ // Remove this item from its existing scene
+ if (QGraphicsScene *oldScene = item->scene())
+ oldScene->removeItem(item);
+
+ // Notify the item that its scene is changing, and allow the item to
+ // react.
+ QGraphicsScene *targetScene = qVariantValue<QGraphicsScene *>(item->itemChange(QGraphicsItem::ItemSceneChange,
+ qVariantFromValue<QGraphicsScene *>(this)));
+ if (targetScene != this) {
+ if (targetScene && item->scene() != targetScene)
+ targetScene->addItem(item);
+ return;
+ }
+
+ // Prevent reusing a recently deleted pointer: purge all removed items
+ // from our lists.
+ d->purgeRemovedItems();
+
+ // Invalidate any sort caching; arrival of a new item means we need to
+ // resort.
+ d->invalidateSortCache();
+
+ // Detach this item from its parent if the parent's scene is different
+ // from this scene.
+ if (QGraphicsItem *itemParent = item->parentItem()) {
+ if (itemParent->scene() != this)
+ item->setParentItem(0);
+ }
+
+ // Add the item to this scene
+ item->d_func()->scene = targetScene;
+
+ // Indexing requires sceneBoundingRect(), but because \a item might
+ // not be completely constructed at this point, we need to store it in
+ // a temporary list and schedule an indexing for later.
+ d->unindexedItems << item;
+ item->d_func()->index = -1;
+ d->startIndexTimer();
+
+ // Update the scene's sort cache settings.
+ item->d_ptr->globalStackingOrder = -1;
+ d->invalidateSortCache();
+
+ // Add to list of items that require an update. We cannot assume that the
+ // item is fully constructed, so calling item->update() can lead to a pure
+ // virtual function call to boundingRect().
+ if (!d->updateAll) {
+ if (d->pendingUpdateItems.isEmpty())
+ QMetaObject::invokeMethod(this, "_q_updateLater", Qt::QueuedConnection);
+ d->pendingUpdateItems << item;
+ }
+
+ // Disable selectionChanged() for individual items
+ ++d->selectionChanging;
+ int oldSelectedItemSize = d->selectedItems.size();
+
+ // Update selection lists
+ if (item->isSelected())
+ d->selectedItems << item;
+ if (item->isWidget() && item->isVisible() && static_cast<QGraphicsWidget *>(item)->windowType() == Qt::Popup)
+ d->addPopup(static_cast<QGraphicsWidget *>(item));
+
+ // Update creation order focus chain. Make sure to leave the widget's
+ // internal tab order intact.
+ if (item->isWidget()) {
+ QGraphicsWidget *widget = static_cast<QGraphicsWidget *>(item);
+ if (!d->tabFocusFirst) {
+ // No first tab focus widget - make this the first tab focus
+ // widget.
+ d->tabFocusFirst = widget;
+ } else if (!widget->parentWidget()) {
+ // Adding a widget that is not part of a tab focus chain.
+ QGraphicsWidget *last = d->tabFocusFirst->d_func()->focusPrev;
+ QGraphicsWidget *lastNew = widget->d_func()->focusPrev;
+ last->d_func()->focusNext = widget;
+ widget->d_func()->focusPrev = last;
+ d->tabFocusFirst->d_func()->focusPrev = lastNew;
+ lastNew->d_func()->focusNext = d->tabFocusFirst;
+ }
+ }
+
+ // Add all children recursively
+ foreach (QGraphicsItem *child, item->children())
+ addItem(child);
+
+ // Resolve font and palette.
+ item->d_ptr->resolveFont(d->font.resolve());
+ item->d_ptr->resolvePalette(d->palette.resolve());
+
+ if (!item->d_ptr->explicitlyHidden) {
+ if (d->unpolishedItems.isEmpty())
+ QMetaObject::invokeMethod(this, "_q_polishItems", Qt::QueuedConnection);
+ d->unpolishedItems << item;
+ }
+
+ // Reenable selectionChanged() for individual items
+ --d->selectionChanging;
+ if (!d->selectionChanging && d->selectedItems.size() != oldSelectedItemSize)
+ emit selectionChanged();
+
+ // Deliver post-change notification
+ item->itemChange(QGraphicsItem::ItemSceneHasChanged, qVariantFromValue<QGraphicsScene *>(this));
+}
+
+/*!
+ Creates and adds an ellipse item to the scene, and returns the item
+ pointer. The geometry of the ellipse is defined by \a rect, and its pen
+ and brush are initialized to \a pen and \a brush.
+
+ Note that the item's geometry is provided in item coordinates, and its
+ position is initialized to (0, 0).
+
+ If the item is visible (i.e., QGraphicsItem::isVisible() returns true),
+ QGraphicsScene will emit changed() once control goes back to the event
+ loop.
+
+ \sa addLine(), addPath(), addPixmap(), addRect(), addText(), addItem(),
+ addWidget()
+*/
+QGraphicsEllipseItem *QGraphicsScene::addEllipse(const QRectF &rect, const QPen &pen, const QBrush &brush)
+{
+ QGraphicsEllipseItem *item = new QGraphicsEllipseItem(rect);
+ item->setPen(pen);
+ item->setBrush(brush);
+ addItem(item);
+ return item;
+}
+
+/*!
+ \fn QGraphicsEllipseItem *QGraphicsScene::addEllipse(qreal x, qreal y, qreal w, qreal h, const QPen &pen, const QBrush &brush)
+ \since 4.3
+
+ This convenience function is equivalent to calling addEllipse(QRectF(\a x,
+ \a y, \a w, \a h), \a pen, \a brush).
+*/
+
+/*!
+ Creates and adds a line item to the scene, and returns the item
+ pointer. The geometry of the line is defined by \a line, and its pen
+ is initialized to \a pen.
+
+ Note that the item's geometry is provided in item coordinates, and its
+ position is initialized to (0, 0).
+
+ If the item is visible (i.e., QGraphicsItem::isVisible() returns true),
+ QGraphicsScene will emit changed() once control goes back to the event
+ loop.
+
+ \sa addEllipse(), addPath(), addPixmap(), addRect(), addText(), addItem(),
+ addWidget()
+*/
+QGraphicsLineItem *QGraphicsScene::addLine(const QLineF &line, const QPen &pen)
+{
+ QGraphicsLineItem *item = new QGraphicsLineItem(line);
+ item->setPen(pen);
+ addItem(item);
+ return item;
+}
+
+/*!
+ \fn QGraphicsLineItem *QGraphicsScene::addLine(qreal x1, qreal y1, qreal x2, qreal y2, const QPen &pen)
+ \since 4.3
+
+ This convenience function is equivalent to calling addLine(QLineF(\a x1,
+ \a y1, \a x2, \a y2), \a pen).
+*/
+
+/*!
+ Creates and adds a path item to the scene, and returns the item
+ pointer. The geometry of the path is defined by \a path, and its pen and
+ brush are initialized to \a pen and \a brush.
+
+ Note that the item's geometry is provided in item coordinates, and its
+ position is initialized to (0, 0).
+
+ If the item is visible (i.e., QGraphicsItem::isVisible() returns true),
+ QGraphicsScene will emit changed() once control goes back to the event
+ loop.
+
+ \sa addEllipse(), addLine(), addPixmap(), addRect(), addText(), addItem(),
+ addWidget()
+*/
+QGraphicsPathItem *QGraphicsScene::addPath(const QPainterPath &path, const QPen &pen, const QBrush &brush)
+{
+ QGraphicsPathItem *item = new QGraphicsPathItem(path);
+ item->setPen(pen);
+ item->setBrush(brush);
+ addItem(item);
+ return item;
+}
+
+/*!
+ Creates and adds a pixmap item to the scene, and returns the item
+ pointer. The pixmap is defined by \a pixmap.
+
+ Note that the item's geometry is provided in item coordinates, and its
+ position is initialized to (0, 0).
+
+ If the item is visible (i.e., QGraphicsItem::isVisible() returns true),
+ QGraphicsScene will emit changed() once control goes back to the event
+ loop.
+
+ \sa addEllipse(), addLine(), addPath(), addRect(), addText(), addItem(),
+ addWidget()
+*/
+QGraphicsPixmapItem *QGraphicsScene::addPixmap(const QPixmap &pixmap)
+{
+ QGraphicsPixmapItem *item = new QGraphicsPixmapItem(pixmap);
+ addItem(item);
+ return item;
+}
+
+/*!
+ Creates and adds a polygon item to the scene, and returns the item
+ pointer. The polygon is defined by \a polygon, and its pen and
+ brush are initialized to \a pen and \a brush.
+
+ Note that the item's geometry is provided in item coordinates, and its
+ position is initialized to (0, 0).
+
+ If the item is visible (i.e., QGraphicsItem::isVisible() returns true),
+ QGraphicsScene will emit changed() once control goes back to the event
+ loop.
+
+ \sa addEllipse(), addLine(), addPath(), addRect(), addText(), addItem(),
+ addWidget()
+*/
+QGraphicsPolygonItem *QGraphicsScene::addPolygon(const QPolygonF &polygon,
+ const QPen &pen, const QBrush &brush)
+{
+ QGraphicsPolygonItem *item = new QGraphicsPolygonItem(polygon);
+ item->setPen(pen);
+ item->setBrush(brush);
+ addItem(item);
+ return item;
+}
+
+/*!
+ Creates and adds a rectangle item to the scene, and returns the item
+ pointer. The geometry of the rectangle is defined by \a rect, and its pen
+ and brush are initialized to \a pen and \a brush.
+
+ Note that the item's geometry is provided in item coordinates, and its
+ position is initialized to (0, 0). For example, if a QRect(50, 50, 100,
+ 100) is added, its top-left corner will be at (50, 50) relative to the
+ origin in the items coordinate system.
+
+ If the item is visible (i.e., QGraphicsItem::isVisible() returns true),
+ QGraphicsScene will emit changed() once control goes back to the event
+ loop.
+
+ \sa addEllipse(), addLine(), addPixmap(), addPixmap(), addText(),
+ addItem(), addWidget()
+*/
+QGraphicsRectItem *QGraphicsScene::addRect(const QRectF &rect, const QPen &pen, const QBrush &brush)
+{
+ QGraphicsRectItem *item = new QGraphicsRectItem(rect);
+ item->setPen(pen);
+ item->setBrush(brush);
+ addItem(item);
+ return item;
+}
+
+/*!
+ \fn QGraphicsRectItem *QGraphicsScene::addRect(qreal x, qreal y, qreal w, qreal h, const QPen &pen, const QBrush &brush)
+ \since 4.3
+
+ This convenience function is equivalent to calling addRect(QRectF(\a x,
+ \a y, \a w, \a h), \a pen, \a brush).
+*/
+
+/*!
+ Creates and adds a text item to the scene, and returns the item
+ pointer. The text string is initialized to \a text, and its font
+ is initialized to \a font.
+
+ The item's position is initialized to (0, 0).
+
+ If the item is visible (i.e., QGraphicsItem::isVisible() returns true),
+ QGraphicsScene will emit changed() once control goes back to the event
+ loop.
+
+ \sa addEllipse(), addLine(), addPixmap(), addPixmap(), addRect(),
+ addItem(), addWidget()
+*/
+QGraphicsTextItem *QGraphicsScene::addText(const QString &text, const QFont &font)
+{
+ QGraphicsTextItem *item = new QGraphicsTextItem(text);
+ item->setFont(font);
+ addItem(item);
+ return item;
+}
+
+/*!
+ Creates and adds a QGraphicsSimpleTextItem to the scene, and returns the
+ item pointer. The text string is initialized to \a text, and its font is
+ initialized to \a font.
+
+ The item's position is initialized to (0, 0).
+
+ If the item is visible (i.e., QGraphicsItem::isVisible() returns true),
+ QGraphicsScene will emit changed() once control goes back to the event
+ loop.
+
+ \sa addEllipse(), addLine(), addPixmap(), addPixmap(), addRect(),
+ addItem(), addWidget()
+*/
+QGraphicsSimpleTextItem *QGraphicsScene::addSimpleText(const QString &text, const QFont &font)
+{
+ QGraphicsSimpleTextItem *item = new QGraphicsSimpleTextItem(text);
+ item->setFont(font);
+ addItem(item);
+ return item;
+}
+
+/*!
+ Creates a new QGraphicsProxyWidget for \a widget, adds it to the scene,
+ and returns a pointer to the proxy. \a wFlags set the default window flags
+ for the embedding proxy widget.
+
+ The item's position is initialized to (0, 0).
+
+ If the item is visible (i.e., QGraphicsItem::isVisible() returns true),
+ QGraphicsScene will emit changed() once control goes back to the event
+ loop.
+
+ Note that widgets with the Qt::WA_PaintOnScreen widget attribute
+ set and widgets that wrap an external application or controller
+ are not supported. Examples are QGLWidget and QAxWidget.
+
+ \sa addEllipse(), addLine(), addPixmap(), addPixmap(), addRect(),
+ addText(), addSimpleText(), addItem()
+*/
+QGraphicsProxyWidget *QGraphicsScene::addWidget(QWidget *widget, Qt::WindowFlags wFlags)
+{
+ QGraphicsProxyWidget *proxy = new QGraphicsProxyWidget(0, wFlags);
+ proxy->setWidget(widget);
+ addItem(proxy);
+ return proxy;
+}
+
+/*!
+ Removes the item \a item and all its children from the scene. The
+ ownership of \a item is passed on to the caller (i.e.,
+ QGraphicsScene will no longer delete \a item when destroyed).
+
+ \sa addItem()
+*/
+void QGraphicsScene::removeItem(QGraphicsItem *item)
+{
+ // ### Refactoring: This function shares much functionality with _q_removeItemLater()
+ Q_D(QGraphicsScene);
+ if (!item) {
+ qWarning("QGraphicsScene::removeItem: cannot remove 0-item");
+ return;
+ }
+ if (item->scene() != this) {
+ qWarning("QGraphicsScene::removeItem: item %p's scene (%p)"
+ " is different from this scene (%p)",
+ item, item->scene(), this);
+ return;
+ }
+
+ // Notify the item that it's scene is changing to 0, allowing the item to
+ // react.
+ QGraphicsScene *targetScene = qVariantValue<QGraphicsScene *>(item->itemChange(QGraphicsItem::ItemSceneChange,
+ qVariantFromValue<QGraphicsScene *>(0)));
+ if (targetScene != 0 && targetScene != this) {
+ targetScene->addItem(item);
+ return;
+ }
+
+ // If the item has focus, remove it (and any focusWidget reference).
+ item->clearFocus();
+
+ // Clear its background
+ item->update();
+
+ // Note: This will access item's sceneBoundingRect(), which (as this is
+ // C++) is why we cannot call removeItem() from QGraphicsItem's
+ // destructor.
+ d->removeFromIndex(item);
+
+ if (item == d->tabFocusFirst) {
+ QGraphicsWidget *widget = static_cast<QGraphicsWidget *>(item);
+ widget->d_func()->fixFocusChainBeforeReparenting(0, 0);
+ }
+ // Set the item's scene ptr to 0.
+ item->d_func()->scene = 0;
+
+ // Detach the item from its parent.
+ if (QGraphicsItem *parentItem = item->parentItem()) {
+ if (parentItem->scene()) {
+ Q_ASSERT_X(parentItem->scene() == this, "QGraphicsScene::removeItem",
+ "Parent item's scene is different from this item's scene");
+ item->setParentItem(0);
+ }
+ }
+
+ // Remove from our item lists.
+ int index = item->d_func()->index;
+ if (index != -1) {
+ d->freeItemIndexes << index;
+ d->indexedItems[index] = 0;
+ } else {
+ d->unindexedItems.removeAll(item);
+ }
+
+ // Remove from scene transform cache
+ int transformIndex = item->d_func()->sceneTransformIndex;
+ if (transformIndex != -1) {
+ d->validTransforms.setBit(transformIndex, 0);
+ d->freeSceneTransformSlots.append(transformIndex);
+ item->d_func()->sceneTransformIndex = -1;
+ }
+
+ if (item == d->focusItem)
+ d->focusItem = 0;
+ if (item == d->lastFocusItem)
+ d->lastFocusItem = 0;
+ if (item == d->activeWindow) {
+ // ### deactivate...
+ d->activeWindow = 0;
+ }
+
+ // Disable selectionChanged() for individual items
+ ++d->selectionChanging;
+ int oldSelectedItemsSize = d->selectedItems.size();
+
+ // Update selected & hovered item bookkeeping
+ d->selectedItems.remove(item);
+ d->hoverItems.removeAll(item);
+ d->pendingUpdateItems.removeAll(item);
+ d->cachedItemsUnderMouse.removeAll(item);
+ d->unpolishedItems.removeAll(item);
+ d->dirtyItems.removeAll(item);
+
+ //Ensure dirty flag have the correct default value so the next time it will be added it will receive updates
+ item->d_func()->dirty = 0;
+ item->d_func()->dirtyChildren = 0;
+
+ // Remove all children recursively
+ foreach (QGraphicsItem *child, item->children())
+ removeItem(child);
+
+ // Reset the mouse grabber and focus item data.
+ if (d->mouseGrabberItems.contains(item))
+ d->ungrabMouse(item);
+
+ // Reset the keyboard grabber
+ if (d->keyboardGrabberItems.contains(item))
+ item->ungrabKeyboard();
+
+ // Reset the last mouse grabber item
+ if (item == d->lastMouseGrabberItem)
+ d->lastMouseGrabberItem = 0;
+
+ // Reenable selectionChanged() for individual items
+ --d->selectionChanging;
+
+ if (!d->selectionChanging && d->selectedItems.size() != oldSelectedItemsSize)
+ emit selectionChanged();
+
+ // Deliver post-change notification
+ item->itemChange(QGraphicsItem::ItemSceneHasChanged, qVariantFromValue<QGraphicsScene *>(0));
+}
+
+/*!
+ Returns the scene's current focus item, or 0 if no item currently has
+ focus.
+
+ The focus item receives keyboard input when the scene receives a
+ key event.
+
+ \sa setFocusItem(), QGraphicsItem::hasFocus()
+*/
+QGraphicsItem *QGraphicsScene::focusItem() const
+{
+ Q_D(const QGraphicsScene);
+ return d->focusItem;
+}
+
+/*!
+ Sets the scene's focus item to \a item, with the focus reason \a
+ focusReason, after removing focus from any previous item that may have had
+ focus.
+
+ If \a item is 0, or if it either does not accept focus (i.e., it does not
+ have the QGraphicsItem::ItemIsFocusable flag enabled), or is not visible
+ or not enabled, this function only removes focus from any previous
+ focusitem.
+
+ If item is not 0, and the scene does not currently have focus (i.e.,
+ hasFocus() returns false), this function will call setFocus()
+ automatically.
+
+ \sa focusItem(), hasFocus(), setFocus()
+*/
+void QGraphicsScene::setFocusItem(QGraphicsItem *item, Qt::FocusReason focusReason)
+{
+ Q_D(QGraphicsScene);
+ if (item == d->focusItem)
+ return;
+ if (item && (!(item->flags() & QGraphicsItem::ItemIsFocusable)
+ || !item->isVisible() || !item->isEnabled())) {
+ item = 0;
+ }
+
+ if (item) {
+ setFocus(focusReason);
+ if (item == d->focusItem)
+ return;
+ }
+
+ if (d->focusItem) {
+ QFocusEvent event(QEvent::FocusOut, focusReason);
+ d->lastFocusItem = d->focusItem;
+ d->focusItem = 0;
+ d->sendEvent(d->lastFocusItem, &event);
+ }
+
+ if (item) {
+ if (item->isWidget()) {
+ // Update focus child chain.
+ static_cast<QGraphicsWidget *>(item)->d_func()->setFocusWidget();
+ }
+
+ d->focusItem = item;
+ QFocusEvent event(QEvent::FocusIn, focusReason);
+ d->sendEvent(item, &event);
+ }
+}
+
+/*!
+ Returns true if the scene has focus; otherwise returns false. If the scene
+ has focus, it will will forward key events from QKeyEvent to any item that
+ has focus.
+
+ \sa setFocus(), setFocusItem()
+*/
+bool QGraphicsScene::hasFocus() const
+{
+ Q_D(const QGraphicsScene);
+ return d->hasFocus;
+}
+
+/*!
+ Sets focus on the scene by sending a QFocusEvent to the scene, passing \a
+ focusReason as the reason. If the scene regains focus after having
+ previously lost it while an item had focus, the last focus item will
+ receive focus with \a focusReason as the reason.
+
+ If the scene already has focus, this function does nothing.
+
+ \sa hasFocus(), clearFocus(), setFocusItem()
+*/
+void QGraphicsScene::setFocus(Qt::FocusReason focusReason)
+{
+ Q_D(QGraphicsScene);
+ if (d->hasFocus)
+ return;
+ QFocusEvent event(QEvent::FocusIn, focusReason);
+ QCoreApplication::sendEvent(this, &event);
+}
+
+/*!
+ Clears focus from the scene. If any item has focus when this function is
+ called, it will lose focus, and regain focus again once the scene regains
+ focus.
+
+ A scene that does not have focus ignores key events.
+
+ \sa hasFocus(), setFocus(), setFocusItem()
+*/
+void QGraphicsScene::clearFocus()
+{
+ Q_D(QGraphicsScene);
+ if (d->hasFocus) {
+ d->hasFocus = false;
+ setFocusItem(0, Qt::OtherFocusReason);
+ }
+}
+
+/*!
+ \property QGraphicsScene::stickyFocus
+ \brief whether or not clicking the scene will clear focus
+
+ If this property is false (the default), then clicking on the scene
+ background or on an item that does not accept focus, will clear
+ focus. Otherwise, focus will remain unchanged.
+
+ The focus change happens in response to a mouse press. You can reimplement
+ mousePressEvent() in a subclass of QGraphicsScene to toggle this property
+ based on where the user has clicked.
+
+ \sa clearFocus(), setFocusItem()
+*/
+void QGraphicsScene::setStickyFocus(bool enabled)
+{
+ Q_D(QGraphicsScene);
+ d->stickyFocus = enabled;
+}
+bool QGraphicsScene::stickyFocus() const
+{
+ Q_D(const QGraphicsScene);
+ return d->stickyFocus;
+}
+
+/*!
+ Returns the current mouse grabber item, or 0 if no item is currently
+ grabbing the mouse. The mouse grabber item is the item that receives all
+ mouse events sent to the scene.
+
+ An item becomes a mouse grabber when it receives and accepts a
+ mouse press event, and it stays the mouse grabber until either of
+ the following events occur:
+
+ \list
+ \o If the item receives a mouse release event when there are no other
+ buttons pressed, it loses the mouse grab.
+ \o If the item becomes invisible (i.e., someone calls \c {item->setVisible(false))},
+ or if it becomes disabled (i.e., someone calls \c {item->setEnabled(false))},
+ it loses the mouse grab.
+ \o If the item is removed from the scene, it loses the mouse grab.
+ \endlist
+
+ If the item loses its mouse grab, the scene will ignore all mouse events
+ until a new item grabs the mouse (i.e., until a new item receives a mouse
+ press event).
+*/
+QGraphicsItem *QGraphicsScene::mouseGrabberItem() const
+{
+ Q_D(const QGraphicsScene);
+ return !d->mouseGrabberItems.isEmpty() ? d->mouseGrabberItems.last() : 0;
+}
+
+/*!
+ \property QGraphicsScene::backgroundBrush
+ \brief the background brush of the scene.
+
+ Set this property to changes the scene's background to a different color,
+ gradient or texture. The default background brush is Qt::NoBrush. The
+ background is drawn before (behind) the items.
+
+ Example:
+
+ \snippet doc/src/snippets/code/src_gui_graphicsview_qgraphicsscene.cpp 3
+
+ QGraphicsScene::render() calls drawBackground() to draw the scene
+ background. For more detailed control over how the background is drawn,
+ you can reimplement drawBackground() in a subclass of QGraphicsScene.
+*/
+QBrush QGraphicsScene::backgroundBrush() const
+{
+ Q_D(const QGraphicsScene);
+ return d->backgroundBrush;
+}
+void QGraphicsScene::setBackgroundBrush(const QBrush &brush)
+{
+ Q_D(QGraphicsScene);
+ d->backgroundBrush = brush;
+ foreach (QGraphicsView *view, d->views) {
+ view->resetCachedContent();
+ view->viewport()->update();
+ }
+ update();
+}
+
+/*!
+ \property QGraphicsScene::foregroundBrush
+ \brief the foreground brush of the scene.
+
+ Change this property to set the scene's foreground to a different
+ color, gradient or texture.
+
+ The foreground is drawn after (on top of) the items. The default
+ foreground brush is Qt::NoBrush ( i.e. the foreground is not
+ drawn).
+
+ Example:
+
+ \snippet doc/src/snippets/code/src_gui_graphicsview_qgraphicsscene.cpp 4
+
+ QGraphicsScene::render() calls drawForeground() to draw the scene
+ foreground. For more detailed control over how the foreground is
+ drawn, you can reimplement the drawForeground() function in a
+ QGraphicsScene subclass.
+*/
+QBrush QGraphicsScene::foregroundBrush() const
+{
+ Q_D(const QGraphicsScene);
+ return d->foregroundBrush;
+}
+void QGraphicsScene::setForegroundBrush(const QBrush &brush)
+{
+ Q_D(QGraphicsScene);
+ d->foregroundBrush = brush;
+ foreach (QGraphicsView *view, views())
+ view->viewport()->update();
+ update();
+}
+
+/*!
+ This method is used by input methods to query a set of properties of
+ the scene to be able to support complex input method operations as support
+ for surrounding text and reconversions.
+
+ The \a query parameter specifies which property is queried.
+
+ \sa QWidget::inputMethodQuery()
+*/
+QVariant QGraphicsScene::inputMethodQuery(Qt::InputMethodQuery query) const
+{
+ Q_D(const QGraphicsScene);
+ if (!d->focusItem)
+ return QVariant();
+ const QTransform matrix = d->focusItem->sceneTransform();
+ QVariant value = d->focusItem->inputMethodQuery(query);
+ if (value.type() == QVariant::RectF)
+ value = matrix.mapRect(value.toRectF());
+ else if (value.type() == QVariant::PointF)
+ value = matrix.map(value.toPointF());
+ else if (value.type() == QVariant::Rect)
+ value = matrix.mapRect(value.toRect());
+ else if (value.type() == QVariant::Point)
+ value = matrix.map(value.toPoint());
+ return value;
+}
+
+/*!
+ \fn void QGraphicsScene::update(const QRectF &rect)
+ Schedules a redraw of the area \a rect on the scene.
+
+ \sa sceneRect(), changed()
+*/
+void QGraphicsScene::update(const QRectF &rect)
+{
+ Q_D(QGraphicsScene);
+ if (d->updateAll)
+ return;
+
+ // Check if anyone's connected; if not, we can send updates directly to
+ // the views. Otherwise or if there are no views, use old behavior.
+ bool directUpdates = !(d->connectedSignals & d->changedSignalMask) && !d->views.isEmpty();
+ if (rect.isNull()) {
+ d->updateAll = true;
+ d->updatedRects.clear();
+ if (directUpdates) {
+ // Update all views.
+ for (int i = 0; i < d->views.size(); ++i)
+ d->views.at(i)->d_func()->updateAll();
+ }
+ } else {
+ if (directUpdates) {
+ // Update all views.
+ for (int i = 0; i < d->views.size(); ++i) {
+ QGraphicsView *view = d->views.at(i);
+ view->d_func()->updateRegion(QRegion(view->mapFromScene(rect).boundingRect()));
+ }
+ } else {
+ d->updatedRects << rect;
+ }
+ }
+
+ if (!directUpdates && !d->calledEmitUpdated) {
+ d->calledEmitUpdated = true;
+ QMetaObject::invokeMethod(this, "_q_emitUpdated", Qt::QueuedConnection);
+ }
+}
+
+/*!
+ \fn void QGraphicsScene::update(qreal x, qreal y, qreal w, qreal h)
+ \overload
+ \since 4.3
+
+ This function is equivalent to calling update(QRectF(\a x, \a y, \a w,
+ \a h));
+*/
+
+/*!
+ Invalidates and schedules a redraw of the \a layers in \a rect on the
+ scene. Any cached content in \a layers is unconditionally invalidated and
+ redrawn.
+
+ You can use this function overload to notify QGraphicsScene of changes to
+ the background or the foreground of the scene. This function is commonly
+ used for scenes with tile-based backgrounds to notify changes when
+ QGraphicsView has enabled
+ \l{QGraphicsView::CacheBackground}{CacheBackground}.
+
+ Example:
+
+ \snippet doc/src/snippets/code/src_gui_graphicsview_qgraphicsscene.cpp 5
+
+ Note that QGraphicsView currently supports background caching only (see
+ QGraphicsView::CacheBackground). This function is equivalent to calling
+ update() if any layer but BackgroundLayer is passed.
+
+ \sa QGraphicsView::resetCachedContent()
+*/
+void QGraphicsScene::invalidate(const QRectF &rect, SceneLayers layers)
+{
+ foreach (QGraphicsView *view, views())
+ view->invalidateScene(rect, layers);
+ update(rect);
+}
+
+/*!
+ \fn void QGraphicsScene::invalidate(qreal x, qreal y, qreal w, qreal h, SceneLayers layers)
+ \overload
+ \since 4.3
+
+ This convenience function is equivalent to calling invalidate(QRectF(\a x, \a
+ y, \a w, \a h), \a layers);
+*/
+
+/*!
+ Returns a list of all the views that display this scene.
+
+ \sa QGraphicsView::scene()
+*/
+QList <QGraphicsView *> QGraphicsScene::views() const
+{
+ Q_D(const QGraphicsScene);
+ return d->views;
+}
+
+/*!
+ This slot \e advances the scene by one step, by calling
+ QGraphicsItem::advance() for all items on the scene. This is done in two
+ phases: in the first phase, all items are notified that the scene is about
+ to change, and in the second phase all items are notified that they can
+ move. In the first phase, QGraphicsItem::advance() is called passing a
+ value of 0 as an argument, and 1 is passed in the second phase.
+
+ \sa QGraphicsItem::advance(), QGraphicsItemAnimation, QTimeLine
+*/
+void QGraphicsScene::advance()
+{
+ for (int i = 0; i < 2; ++i) {
+ foreach (QGraphicsItem *item, items())
+ item->advance(i);
+ }
+}
+
+/*!
+ Processes the event \a event, and dispatches it to the respective
+ event handlers.
+
+ In addition to calling the convenience event handlers, this
+ function is responsible for converting mouse move events to hover
+ events for when there is no mouse grabber item. Hover events are
+ delivered directly to items; there is no convenience function for
+ them.
+
+ Unlike QWidget, QGraphicsScene does not have the convenience functions
+ \l{QWidget::}{enterEvent()} and \l{QWidget::}{leaveEvent()}. Use this
+ function to obtain those events instead.
+
+ \sa contextMenuEvent(), keyPressEvent(), keyReleaseEvent(),
+ mousePressEvent(), mouseMoveEvent(), mouseReleaseEvent(),
+ mouseDoubleClickEvent(), focusInEvent(), focusOutEvent()
+*/
+bool QGraphicsScene::event(QEvent *event)
+{
+ Q_D(QGraphicsScene);
+
+ switch (event->type()) {
+ case QEvent::GraphicsSceneMousePress:
+ case QEvent::GraphicsSceneMouseMove:
+ case QEvent::GraphicsSceneMouseRelease:
+ case QEvent::GraphicsSceneMouseDoubleClick:
+ case QEvent::GraphicsSceneHoverEnter:
+ case QEvent::GraphicsSceneHoverLeave:
+ case QEvent::GraphicsSceneHoverMove:
+ // Reset the under-mouse list to ensure that this event gets fresh
+ // item-under-mouse data. Be careful about this list; if people delete
+ // items from inside event handlers, this list can quickly end up
+ // having stale pointers in it. We need to clear it before dispatching
+ // events that use it.
+ d->cachedItemsUnderMouse.clear();
+ default:
+ break;
+ }
+
+ switch (event->type()) {
+ case QEvent::GraphicsSceneDragEnter:
+ dragEnterEvent(static_cast<QGraphicsSceneDragDropEvent *>(event));
+ break;
+ case QEvent::GraphicsSceneDragMove:
+ dragMoveEvent(static_cast<QGraphicsSceneDragDropEvent *>(event));
+ break;
+ case QEvent::GraphicsSceneDragLeave:
+ dragLeaveEvent(static_cast<QGraphicsSceneDragDropEvent *>(event));
+ break;
+ case QEvent::GraphicsSceneDrop:
+ dropEvent(static_cast<QGraphicsSceneDragDropEvent *>(event));
+ break;
+ case QEvent::GraphicsSceneContextMenu:
+ contextMenuEvent(static_cast<QGraphicsSceneContextMenuEvent *>(event));
+ break;
+ case QEvent::KeyPress:
+ if (!d->focusItem) {
+ QKeyEvent *k = static_cast<QKeyEvent *>(event);
+ if (k->key() == Qt::Key_Tab || k->key() == Qt::Key_Backtab) {
+ if (!(k->modifiers() & (Qt::ControlModifier | Qt::AltModifier))) { //### Add MetaModifier?
+ bool res = false;
+ if (k->key() == Qt::Key_Backtab
+ || (k->key() == Qt::Key_Tab && (k->modifiers() & Qt::ShiftModifier))) {
+ res = focusNextPrevChild(false);
+ } else if (k->key() == Qt::Key_Tab) {
+ res = focusNextPrevChild(true);
+ }
+ if (!res)
+ event->ignore();
+ return true;
+ }
+ }
+ }
+ keyPressEvent(static_cast<QKeyEvent *>(event));
+ break;
+ case QEvent::KeyRelease:
+ keyReleaseEvent(static_cast<QKeyEvent *>(event));
+ break;
+ case QEvent::ShortcutOverride: {
+ QGraphicsItem *parent = focusItem();
+ while (parent) {
+ d->sendEvent(parent, event);
+ if (event->isAccepted())
+ return true;
+ parent = parent->parentItem();
+ }
+ }
+ return false;
+ case QEvent::GraphicsSceneMouseMove:
+ mouseMoveEvent(static_cast<QGraphicsSceneMouseEvent *>(event));
+ break;
+ case QEvent::GraphicsSceneMousePress:
+ mousePressEvent(static_cast<QGraphicsSceneMouseEvent *>(event));
+ break;
+ case QEvent::GraphicsSceneMouseRelease:
+ mouseReleaseEvent(static_cast<QGraphicsSceneMouseEvent *>(event));
+ break;
+ case QEvent::GraphicsSceneMouseDoubleClick:
+ mouseDoubleClickEvent(static_cast<QGraphicsSceneMouseEvent *>(event));
+ break;
+ case QEvent::GraphicsSceneWheel:
+ wheelEvent(static_cast<QGraphicsSceneWheelEvent *>(event));
+ break;
+ case QEvent::FocusIn:
+ focusInEvent(static_cast<QFocusEvent *>(event));
+ break;
+ case QEvent::FocusOut:
+ focusOutEvent(static_cast<QFocusEvent *>(event));
+ break;
+ case QEvent::GraphicsSceneHoverEnter:
+ case QEvent::GraphicsSceneHoverLeave:
+ case QEvent::GraphicsSceneHoverMove:
+ d->dispatchHoverEvent(static_cast<QGraphicsSceneHoverEvent *>(event));
+ break;
+ case QEvent::Leave:
+ d->leaveScene();
+ break;
+ case QEvent::GraphicsSceneHelp:
+ helpEvent(static_cast<QGraphicsSceneHelpEvent *>(event));
+ break;
+ case QEvent::InputMethod:
+ inputMethodEvent(static_cast<QInputMethodEvent *>(event));
+ break;
+ case QEvent::WindowActivate: {
+ if (!d->activationRefCount++) {
+ // Notify all non-window widgets.
+ foreach (QGraphicsItem *item, items()) {
+ if (item->isWidget() && item->isVisible() && !item->isWindow() && !item->parentWidget()) {
+ QEvent event(QEvent::WindowActivate);
+ QApplication::sendEvent(static_cast<QGraphicsWidget *>(item), &event);
+ }
+ }
+
+ // Restore window activation.
+ QGraphicsItem *nextFocusItem = d->focusItem ? d->focusItem : d->lastFocusItem;
+ if (nextFocusItem && nextFocusItem->window())
+ setActiveWindow(static_cast<QGraphicsWidget *>(nextFocusItem));
+ else if (d->tabFocusFirst && d->tabFocusFirst->isWindow())
+ setActiveWindow(d->tabFocusFirst);
+ }
+ break;
+ }
+ case QEvent::WindowDeactivate: {
+ if (!--d->activationRefCount) {
+ // Remove window activation.
+ setActiveWindow(0);
+
+ // Notify all non-window widgets.
+ foreach (QGraphicsItem *item, items()) {
+ if (item->isWidget() && item->isVisible() && !item->isWindow() && !item->parentWidget()) {
+ QEvent event(QEvent::WindowDeactivate);
+ QApplication::sendEvent(static_cast<QGraphicsWidget *>(item), &event);
+ }
+ }
+ }
+ break;
+ }
+ case QEvent::ApplicationFontChange: {
+ // Resolve the existing scene font.
+ d->resolveFont();
+ break;
+ }
+ case QEvent::FontChange:
+ // Update the entire scene when the font changes.
+ update();
+ break;
+ case QEvent::ApplicationPaletteChange: {
+ // Resolve the existing scene palette.
+ d->resolvePalette();
+ break;
+ }
+ case QEvent::PaletteChange:
+ // Update the entire scene when the palette changes.
+ update();
+ break;
+ case QEvent::StyleChange:
+ // Reresolve all widgets' styles. Update all top-level widgets'
+ // geometries that do not have an explicit style set.
+ update();
+ break;
+ case QEvent::Timer:
+ if (d->indexTimerId && static_cast<QTimerEvent *>(event)->timerId() == d->indexTimerId) {
+ if (d->restartIndexTimer) {
+ d->restartIndexTimer = false;
+ } else {
+ // this call will kill the timer
+ d->_q_updateIndex();
+ }
+ }
+ // Fallthrough intended - support timers in subclasses.
+ default:
+ return QObject::event(event);
+ }
+ return true;
+}
+
+/*!
+ \reimp
+
+ QGraphicsScene filters QApplication's events to detect palette and font
+ changes.
+*/
+bool QGraphicsScene::eventFilter(QObject *watched, QEvent *event)
+{
+ if (watched != qApp)
+ return false;
+
+ switch (event->type()) {
+ case QEvent::ApplicationPaletteChange:
+ qApp->postEvent(this, new QEvent(QEvent::ApplicationPaletteChange));
+ break;
+ case QEvent::ApplicationFontChange:
+ qApp->postEvent(this, new QEvent(QEvent::ApplicationFontChange));
+ break;
+ default:
+ break;
+ }
+ return false;
+}
+
+/*!
+ This event handler, for event \a contextMenuEvent, can be reimplemented in
+ a subclass to receive context menu events. The default implementation
+ forwards the event to the topmost item that accepts context menu events at
+ the position of the event. If no items accept context menu events at this
+ position, the event is ignored.
+
+ \sa QGraphicsItem::contextMenuEvent()
+*/
+void QGraphicsScene::contextMenuEvent(QGraphicsSceneContextMenuEvent *contextMenuEvent)
+{
+ Q_D(QGraphicsScene);
+ // Ignore by default.
+ contextMenuEvent->ignore();
+
+ // Send the event to all items at this position until one item accepts the
+ // event.
+ foreach (QGraphicsItem *item, d->itemsAtPosition(contextMenuEvent->screenPos(),
+ contextMenuEvent->scenePos(),
+ contextMenuEvent->widget())) {
+ contextMenuEvent->setPos(item->d_ptr->genericMapFromScene(contextMenuEvent->scenePos(),
+ contextMenuEvent->widget()));
+ contextMenuEvent->accept();
+ if (!d->sendEvent(item, contextMenuEvent))
+ break;
+
+ if (contextMenuEvent->isAccepted())
+ break;
+ }
+}
+
+/*!
+ This event handler, for event \a event, can be reimplemented in a subclass
+ to receive drag enter events for the scene.
+
+ The default implementation accepts the event and prepares the scene to
+ accept drag move events.
+
+ \sa QGraphicsItem::dragEnterEvent(), dragMoveEvent(), dragLeaveEvent(),
+ dropEvent()
+*/
+void QGraphicsScene::dragEnterEvent(QGraphicsSceneDragDropEvent *event)
+{
+ Q_D(QGraphicsScene);
+ d->dragDropItem = 0;
+ d->lastDropAction = Qt::IgnoreAction;
+ event->accept();
+}
+
+/*!
+ This event handler, for event \a event, can be reimplemented in a subclass
+ to receive drag move events for the scene.
+
+ \sa QGraphicsItem::dragMoveEvent(), dragEnterEvent(), dragLeaveEvent(),
+ dropEvent()
+*/
+void QGraphicsScene::dragMoveEvent(QGraphicsSceneDragDropEvent *event)
+{
+ Q_D(QGraphicsScene);
+ event->ignore();
+
+ if (!d->mouseGrabberItems.isEmpty()) {
+ // Mouse grabbers that start drag events lose the mouse grab.
+ d->clearMouseGrabber();
+ d->mouseGrabberButtonDownPos.clear();
+ d->mouseGrabberButtonDownScenePos.clear();
+ d->mouseGrabberButtonDownScreenPos.clear();
+ }
+
+ bool eventDelivered = false;
+
+ // Find the topmost enabled items under the cursor. They are all
+ // candidates for accepting drag & drop events.
+ foreach (QGraphicsItem *item, d->itemsAtPosition(event->screenPos(),
+ event->scenePos(),
+ event->widget())) {
+ if (!item->isEnabled() || !item->acceptDrops())
+ continue;
+
+ if (item != d->dragDropItem) {
+ // Enter the new drag drop item. If it accepts the event, we send
+ // the leave to the parent item.
+ QGraphicsSceneDragDropEvent dragEnter(QEvent::GraphicsSceneDragEnter);
+ d->cloneDragDropEvent(&dragEnter, event);
+ dragEnter.setDropAction(event->proposedAction());
+ d->sendDragDropEvent(item, &dragEnter);
+ event->setAccepted(dragEnter.isAccepted());
+ event->setDropAction(dragEnter.dropAction());
+ if (!event->isAccepted()) {
+ // Propagate to the item under
+ continue;
+ }
+
+ d->lastDropAction = event->dropAction();
+
+ if (d->dragDropItem) {
+ // Leave the last drag drop item. A perfect implementation
+ // would set the position of this event to the point where
+ // this event and the last event intersect with the item's
+ // shape, but that's not easy to do. :-)
+ QGraphicsSceneDragDropEvent dragLeave(QEvent::GraphicsSceneDragLeave);
+ d->cloneDragDropEvent(&dragLeave, event);
+ d->sendDragDropEvent(d->dragDropItem, &dragLeave);
+ }
+
+ // We've got a new drag & drop item
+ d->dragDropItem = item;
+ }
+
+ // Send the move event.
+ event->setDropAction(d->lastDropAction);
+ event->accept();
+ d->sendDragDropEvent(item, event);
+ if (event->isAccepted())
+ d->lastDropAction = event->dropAction();
+ eventDelivered = true;
+ break;
+ }
+
+ if (!eventDelivered) {
+ if (d->dragDropItem) {
+ // Leave the last drag drop item
+ QGraphicsSceneDragDropEvent dragLeave(QEvent::GraphicsSceneDragLeave);
+ d->cloneDragDropEvent(&dragLeave, event);
+ d->sendDragDropEvent(d->dragDropItem, &dragLeave);
+ d->dragDropItem = 0;
+ }
+ // Propagate
+ event->setDropAction(Qt::IgnoreAction);
+ }
+}
+
+/*!
+ This event handler, for event \a event, can be reimplemented in a subclass
+ to receive drag leave events for the scene.
+
+ \sa QGraphicsItem::dragLeaveEvent(), dragEnterEvent(), dragMoveEvent(),
+ dropEvent()
+*/
+void QGraphicsScene::dragLeaveEvent(QGraphicsSceneDragDropEvent *event)
+{
+ Q_D(QGraphicsScene);
+ if (d->dragDropItem) {
+ // Leave the last drag drop item
+ d->sendDragDropEvent(d->dragDropItem, event);
+ d->dragDropItem = 0;
+ }
+}
+
+/*!
+ This event handler, for event \a event, can be reimplemented in a subclass
+ to receive drop events for the scene.
+
+ \sa QGraphicsItem::dropEvent(), dragEnterEvent(), dragMoveEvent(),
+ dragLeaveEvent()
+*/
+void QGraphicsScene::dropEvent(QGraphicsSceneDragDropEvent *event)
+{
+ Q_UNUSED(event);
+ Q_D(QGraphicsScene);
+ if (d->dragDropItem) {
+ // Drop on the last drag drop item
+ d->sendDragDropEvent(d->dragDropItem, event);
+ d->dragDropItem = 0;
+ }
+}
+
+/*!
+ This event handler, for event \a focusEvent, can be reimplemented in a
+ subclass to receive focus in events.
+
+ The default implementation sets focus on the scene, and then on the last
+ focus item.
+
+ \sa QGraphicsItem::focusOutEvent()
+*/
+void QGraphicsScene::focusInEvent(QFocusEvent *focusEvent)
+{
+ Q_D(QGraphicsScene);
+
+ d->hasFocus = true;
+ switch (focusEvent->reason()) {
+ case Qt::TabFocusReason:
+ if (!focusNextPrevChild(true))
+ focusEvent->ignore();
+ break;
+ case Qt::BacktabFocusReason:
+ if (!focusNextPrevChild(false))
+ focusEvent->ignore();
+ break;
+ default:
+ if (d->lastFocusItem) {
+ // Set focus on the last focus item
+ setFocusItem(d->lastFocusItem, focusEvent->reason());
+ }
+ break;
+ }
+}
+
+/*!
+ This event handler, for event \a focusEvent, can be reimplemented in a
+ subclass to receive focus out events.
+
+ The default implementation removes focus from any focus item, then removes
+ focus from the scene.
+
+ \sa QGraphicsItem::focusInEvent()
+*/
+void QGraphicsScene::focusOutEvent(QFocusEvent *focusEvent)
+{
+ Q_D(QGraphicsScene);
+ d->hasFocus = false;
+ setFocusItem(0, focusEvent->reason());
+
+ // Remove all popups when the scene loses focus.
+ if (!d->popupWidgets.isEmpty())
+ d->removePopup(d->popupWidgets.first());
+}
+
+/*!
+ This event handler, for event \a helpEvent, can be
+ reimplemented in a subclass to receive help events. The events
+ are of type QEvent::ToolTip, which are created when a tooltip is
+ requested.
+
+ The default implementation shows the tooltip of the topmost
+ item, i.e., the item with the highest z-value, at the mouse
+ cursor position. If no item has a tooltip set, this function
+ does nothing.
+
+ \sa QGraphicsItem::toolTip(), QGraphicsSceneHelpEvent
+*/
+void QGraphicsScene::helpEvent(QGraphicsSceneHelpEvent *helpEvent)
+{
+#ifdef QT_NO_TOOLTIP
+ Q_UNUSED(helpEvent);
+#else
+ // Find the first item that does tooltips
+ Q_D(QGraphicsScene);
+ QList<QGraphicsItem *> itemsAtPos = d->itemsAtPosition(helpEvent->screenPos(),
+ helpEvent->scenePos(),
+ helpEvent->widget());
+ QGraphicsItem *toolTipItem = 0;
+ for (int i = 0; i < itemsAtPos.size(); ++i) {
+ QGraphicsItem *tmp = itemsAtPos.at(i);
+ if (!tmp->toolTip().isEmpty()) {
+ toolTipItem = tmp;
+ break;
+ }
+ }
+
+ // Show or hide the tooltip
+ QString text;
+ QPoint point;
+ if (toolTipItem && !toolTipItem->toolTip().isEmpty()) {
+ text = toolTipItem->toolTip();
+ point = helpEvent->screenPos();
+ }
+ QToolTip::showText(point, text);
+ helpEvent->setAccepted(!text.isEmpty());
+#endif
+}
+
+bool QGraphicsScenePrivate::itemAcceptsHoverEvents_helper(const QGraphicsItem *item) const
+{
+ return item->acceptHoverEvents()
+ || (item->isWidget() && static_cast<const QGraphicsWidget *>(item)->d_func()->hasDecoration());
+}
+
+/*!
+ This event handler, for event \a hoverEvent, can be reimplemented in a
+ subclass to receive hover enter events. The default implementation
+ forwards the event to the topmost item that accepts hover events at the
+ scene position from the event.
+
+ \sa QGraphicsItem::hoverEvent(), QGraphicsItem::setAcceptHoverEvents()
+*/
+bool QGraphicsScenePrivate::dispatchHoverEvent(QGraphicsSceneHoverEvent *hoverEvent)
+{
+ // Find the first item that accepts hover events, reusing earlier
+ // calculated data is possible.
+ if (cachedItemsUnderMouse.isEmpty()) {
+ cachedItemsUnderMouse = itemsAtPosition(hoverEvent->screenPos(),
+ hoverEvent->scenePos(),
+ hoverEvent->widget());
+ }
+
+ QGraphicsItem *item = 0;
+ for (int i = 0; i < cachedItemsUnderMouse.size(); ++i) {
+ QGraphicsItem *tmp = cachedItemsUnderMouse.at(i);
+ if (itemAcceptsHoverEvents_helper(tmp)) {
+ item = tmp;
+ break;
+ }
+ }
+
+ // Find the common ancestor item for the new topmost hoverItem and the
+ // last item in the hoverItem list.
+ QGraphicsItem *commonAncestorItem = (item && !hoverItems.isEmpty()) ? item->commonAncestorItem(hoverItems.last()) : 0;
+ while (commonAncestorItem && !itemAcceptsHoverEvents_helper(commonAncestorItem))
+ commonAncestorItem = commonAncestorItem->parentItem();
+ if (commonAncestorItem && commonAncestorItem->window() != item->window()) {
+ // The common ancestor isn't in the same window as the two hovered
+ // items.
+ commonAncestorItem = 0;
+ }
+
+ // Check if the common ancestor item is known.
+ int index = commonAncestorItem ? hoverItems.indexOf(commonAncestorItem) : -1;
+ // Send hover leaves to any existing hovered children of the common
+ // ancestor item.
+ for (int i = hoverItems.size() - 1; i > index; --i) {
+ QGraphicsItem *lastItem = hoverItems.takeLast();
+ if (itemAcceptsHoverEvents_helper(lastItem))
+ sendHoverEvent(QEvent::GraphicsSceneHoverLeave, lastItem, hoverEvent);
+ }
+
+ // Item is a child of a known item. Generate enter events for the
+ // missing links.
+ QList<QGraphicsItem *> parents;
+ QGraphicsItem *parent = item;
+ while (parent && parent != commonAncestorItem) {
+ parents.prepend(parent);
+ if (parent->isWindow()) {
+ // Stop at the window - we don't deliver beyond this point.
+ break;
+ }
+ parent = parent->parentItem();
+ }
+ for (int i = 0; i < parents.size(); ++i) {
+ parent = parents.at(i);
+ hoverItems << parent;
+ if (itemAcceptsHoverEvents_helper(parent))
+ sendHoverEvent(QEvent::GraphicsSceneHoverEnter, parent, hoverEvent);
+ }
+
+ // Generate a move event for the item itself
+ if (item && !hoverItems.isEmpty() && item == hoverItems.last()) {
+ sendHoverEvent(QEvent::GraphicsSceneHoverMove, item, hoverEvent);
+ return true;
+ }
+ return false;
+}
+
+/*!
+ \internal
+
+ Handles all actions necessary to clean up the scene when the mouse leaves
+ the view.
+*/
+void QGraphicsScenePrivate::leaveScene()
+{
+ Q_Q(QGraphicsScene);
+#ifndef QT_NO_TOOLTIP
+ // Remove any tooltips
+ QToolTip::showText(QPoint(), QString());
+#endif
+ // Send HoverLeave events to all existing hover items, topmost first.
+ QGraphicsView *senderWidget = qobject_cast<QGraphicsView *>(q->sender());
+ QGraphicsSceneHoverEvent hoverEvent;
+ hoverEvent.setWidget(senderWidget);
+
+ if (senderWidget) {
+ QPoint cursorPos = QCursor::pos();
+ hoverEvent.setScenePos(senderWidget->mapToScene(senderWidget->mapFromGlobal(cursorPos)));
+ hoverEvent.setLastScenePos(hoverEvent.scenePos());
+ hoverEvent.setScreenPos(cursorPos);
+ hoverEvent.setLastScreenPos(hoverEvent.screenPos());
+ }
+
+ while (!hoverItems.isEmpty()) {
+ QGraphicsItem *lastItem = hoverItems.takeLast();
+ if (lastItem->acceptHoverEvents()
+ || (lastItem->isWidget() && static_cast<QGraphicsWidget*>(lastItem)->d_func()->hasDecoration()))
+ sendHoverEvent(QEvent::GraphicsSceneHoverLeave, lastItem, &hoverEvent);
+ }
+}
+
+/*!
+ This event handler, for event \a keyEvent, can be reimplemented in a
+ subclass to receive keypress events. The default implementation forwards
+ the event to current focus item.
+
+ \sa QGraphicsItem::keyPressEvent(), focusItem()
+*/
+void QGraphicsScene::keyPressEvent(QKeyEvent *keyEvent)
+{
+ // ### Merge this function with keyReleaseEvent; they are identical
+ // ### (except this comment).
+ Q_D(QGraphicsScene);
+ QGraphicsItem *item = !d->keyboardGrabberItems.isEmpty() ? d->keyboardGrabberItems.last() : 0;
+ if (!item)
+ item = focusItem();
+ if (item) {
+ QGraphicsItem *p = item;
+ do {
+ // Accept the event by default
+ keyEvent->accept();
+ // Send it; QGraphicsItem::keyPressEvent ignores it. If the event
+ // is filtered out, stop propagating it.
+ if (!d->sendEvent(p, keyEvent))
+ break;
+ } while (!keyEvent->isAccepted() && !p->isWindow() && (p = p->parentItem()));
+ } else {
+ keyEvent->ignore();
+ }
+}
+
+/*!
+ This event handler, for event \a keyEvent, can be reimplemented in a
+ subclass to receive key release events. The default implementation
+ forwards the event to current focus item.
+
+ \sa QGraphicsItem::keyReleaseEvent(), focusItem()
+*/
+void QGraphicsScene::keyReleaseEvent(QKeyEvent *keyEvent)
+{
+ // ### Merge this function with keyPressEvent; they are identical (except
+ // ### this comment).
+ Q_D(QGraphicsScene);
+ QGraphicsItem *item = !d->keyboardGrabberItems.isEmpty() ? d->keyboardGrabberItems.last() : 0;
+ if (!item)
+ item = focusItem();
+ if (item) {
+ QGraphicsItem *p = item;
+ do {
+ // Accept the event by default
+ keyEvent->accept();
+ // Send it; QGraphicsItem::keyPressEvent ignores it. If the event
+ // is filtered out, stop propagating it.
+ if (!d->sendEvent(p, keyEvent))
+ break;
+ } while (!keyEvent->isAccepted() && !p->isWindow() && (p = p->parentItem()));
+ } else {
+ keyEvent->ignore();
+ }
+}
+
+/*!
+ This event handler, for event \a mouseEvent, can be reimplemented
+ in a subclass to receive mouse press events for the scene.
+
+ The default implementation depends on the state of the scene. If
+ there is a mouse grabber item, then the event is sent to the mouse
+ grabber. Otherwise, it is forwarded to the topmost item that
+ accepts mouse events at the scene position from the event, and
+ that item promptly becomes the mouse grabber item.
+
+ If there is no item at the given position on the scene, the
+ selection area is reset, any focus item loses its input focus, and
+ the event is then ignored.
+
+ \sa QGraphicsItem::mousePressEvent(),
+ QGraphicsItem::setAcceptedMouseButtons()
+*/
+void QGraphicsScene::mousePressEvent(QGraphicsSceneMouseEvent *mouseEvent)
+{
+ Q_D(QGraphicsScene);
+ d->mousePressEventHandler(mouseEvent);
+}
+
+/*!
+ This event handler, for event \a mouseEvent, can be reimplemented
+ in a subclass to receive mouse move events for the scene.
+
+ The default implementation depends on the mouse grabber state. If there is
+ a mouse grabber item, the event is sent to the mouse grabber. If there
+ are any items that accept hover events at the current position, the event
+ is translated into a hover event and accepted; otherwise it's ignored.
+
+ \sa QGraphicsItem::mousePressEvent(), QGraphicsItem::mouseReleaseEvent(),
+ QGraphicsItem::mouseDoubleClickEvent(), QGraphicsItem::setAcceptedMouseButtons()
+*/
+void QGraphicsScene::mouseMoveEvent(QGraphicsSceneMouseEvent *mouseEvent)
+{
+ Q_D(QGraphicsScene);
+ if (d->mouseGrabberItems.isEmpty()) {
+ if (mouseEvent->buttons())
+ return;
+ QGraphicsSceneHoverEvent hover;
+ _q_hoverFromMouseEvent(&hover, mouseEvent);
+ mouseEvent->setAccepted(d->dispatchHoverEvent(&hover));
+ return;
+ }
+
+ // Forward the event to the mouse grabber
+ d->sendMouseEvent(mouseEvent);
+ mouseEvent->accept();
+}
+
+/*!
+ This event handler, for event \a mouseEvent, can be reimplemented
+ in a subclass to receive mouse release events for the scene.
+
+ The default implementation depends on the mouse grabber state. If
+ there is no mouse grabber, the event is ignored. Otherwise, if
+ there is a mouse grabber item, the event is sent to the mouse
+ grabber. If this mouse release represents the last pressed button
+ on the mouse, the mouse grabber item then loses the mouse grab.
+
+ \sa QGraphicsItem::mousePressEvent(), QGraphicsItem::mouseMoveEvent(),
+ QGraphicsItem::mouseDoubleClickEvent(), QGraphicsItem::setAcceptedMouseButtons()
+*/
+void QGraphicsScene::mouseReleaseEvent(QGraphicsSceneMouseEvent *mouseEvent)
+{
+ Q_D(QGraphicsScene);
+ if (d->mouseGrabberItems.isEmpty()) {
+ mouseEvent->ignore();
+ return;
+ }
+
+ // Forward the event to the mouse grabber
+ d->sendMouseEvent(mouseEvent);
+ mouseEvent->accept();
+
+ // Reset the mouse grabber when the last mouse button has been released.
+ if (!mouseEvent->buttons()) {
+ if (!d->mouseGrabberItems.isEmpty()) {
+ d->lastMouseGrabberItem = d->mouseGrabberItems.last();
+ if (d->lastMouseGrabberItemHasImplicitMouseGrab)
+ d->mouseGrabberItems.last()->ungrabMouse();
+ } else {
+ d->lastMouseGrabberItem = 0;
+ }
+
+ // Generate a hoverevent
+ QGraphicsSceneHoverEvent hoverEvent;
+ _q_hoverFromMouseEvent(&hoverEvent, mouseEvent);
+ d->dispatchHoverEvent(&hoverEvent);
+ }
+}
+
+/*!
+ This event handler, for event \a mouseEvent, can be reimplemented
+ in a subclass to receive mouse doubleclick events for the scene.
+
+ If someone doubleclicks on the scene, the scene will first receive
+ a mouse press event, followed by a release event (i.e., a click),
+ then a doubleclick event, and finally a release event. If the
+ doubleclick event is delivered to a different item than the one
+ that received the first press and release, it will be delivered as
+ a press event. However, tripleclick events are not delivered as
+ doubleclick events in this case.
+
+ The default implementation is similar to mousePressEvent().
+
+ \sa QGraphicsItem::mousePressEvent(), QGraphicsItem::mouseMoveEvent(),
+ QGraphicsItem::mouseReleaseEvent(), QGraphicsItem::setAcceptedMouseButtons()
+*/
+void QGraphicsScene::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *mouseEvent)
+{
+ Q_D(QGraphicsScene);
+ d->mousePressEventHandler(mouseEvent);
+}
+
+/*!
+ This event handler, for event \a wheelEvent, can be reimplemented in a
+ subclass to receive mouse wheel events for the scene.
+
+ By default, the event is delivered to the topmost visible item under the
+ cursor. If ignored, the event propagates to the item beneath, and again
+ until the event is accepted, or it reaches the scene. If no items accept
+ the event, it is ignored.
+
+ \sa QGraphicsItem::wheelEvent()
+*/
+void QGraphicsScene::wheelEvent(QGraphicsSceneWheelEvent *wheelEvent)
+{
+ Q_D(QGraphicsScene);
+ QList<QGraphicsItem *> wheelCandidates = d->itemsAtPosition(wheelEvent->screenPos(),
+ wheelEvent->scenePos(),
+ wheelEvent->widget());
+
+ bool hasSetFocus = false;
+ foreach (QGraphicsItem *item, wheelCandidates) {
+ if (!hasSetFocus && item->isEnabled() && (item->flags() & QGraphicsItem::ItemIsFocusable)) {
+ if (item->isWidget() && static_cast<QGraphicsWidget *>(item)->focusPolicy() == Qt::WheelFocus) {
+ hasSetFocus = true;
+ if (item != focusItem())
+ setFocusItem(item, Qt::MouseFocusReason);
+ }
+ }
+
+ wheelEvent->setPos(item->d_ptr->genericMapFromScene(wheelEvent->scenePos(),
+ wheelEvent->widget()));
+ wheelEvent->accept();
+ bool isWindow = item->isWindow();
+ d->sendEvent(item, wheelEvent);
+ if (isWindow || wheelEvent->isAccepted())
+ break;
+ }
+}
+
+/*!
+ This event handler, for event \a event, can be reimplemented in a
+ subclass to receive input method events for the scene.
+
+ The default implementation forwards the event to the focusItem().
+ If no item currently has focus, this function does nothing.
+
+ \sa QGraphicsItem::inputMethodEvent()
+*/
+void QGraphicsScene::inputMethodEvent(QInputMethodEvent *event)
+{
+ Q_D(QGraphicsScene);
+ if (!d->focusItem)
+ return;
+ d->sendEvent(d->focusItem, event);
+}
+
+/*!
+ Draws the background of the scene using \a painter, before any items and
+ the foreground are drawn. Reimplement this function to provide a custom
+ background for the scene.
+
+ All painting is done in \e scene coordinates. The \a rect
+ parameter is the exposed rectangle.
+
+ If all you want is to define a color, texture, or gradient for the
+ background, you can call setBackgroundBrush() instead.
+
+ \sa drawForeground(), drawItems()
+*/
+void QGraphicsScene::drawBackground(QPainter *painter, const QRectF &rect)
+{
+ Q_D(QGraphicsScene);
+
+ if (d->backgroundBrush.style() != Qt::NoBrush) {
+ if (d->painterStateProtection)
+ painter->save();
+ painter->setBrushOrigin(0, 0);
+ painter->fillRect(rect, backgroundBrush());
+ if (d->painterStateProtection)
+ painter->restore();
+ }
+}
+
+/*!
+ Draws the foreground of the scene using \a painter, after the background
+ and all items have been drawn. Reimplement this function to provide a
+ custom foreground for the scene.
+
+ All painting is done in \e scene coordinates. The \a rect
+ parameter is the exposed rectangle.
+
+ If all you want is to define a color, texture or gradient for the
+ foreground, you can call setForegroundBrush() instead.
+
+ \sa drawBackground(), drawItems()
+*/
+void QGraphicsScene::drawForeground(QPainter *painter, const QRectF &rect)
+{
+ Q_D(QGraphicsScene);
+
+ if (d->foregroundBrush.style() != Qt::NoBrush) {
+ if (d->painterStateProtection)
+ painter->save();
+ painter->setBrushOrigin(0, 0);
+ painter->fillRect(rect, foregroundBrush());
+ if (d->painterStateProtection)
+ painter->restore();
+ }
+}
+
+static void _q_paintItem(QGraphicsItem *item, QPainter *painter,
+ const QStyleOptionGraphicsItem *option, QWidget *widget,
+ bool useWindowOpacity, bool painterStateProtection)
+{
+ if (!item->isWidget()) {
+ item->paint(painter, option, widget);
+ return;
+ }
+ QGraphicsWidget *widgetItem = static_cast<QGraphicsWidget *>(item);
+ QGraphicsProxyWidget *proxy = qobject_cast<QGraphicsProxyWidget *>(widgetItem);
+ const qreal windowOpacity = (proxy && proxy->widget() && useWindowOpacity)
+ ? proxy->widget()->windowOpacity() : 1.0;
+ const qreal oldPainterOpacity = painter->opacity();
+
+ if (qFuzzyCompare(windowOpacity + 1, qreal(1.0)))
+ return;
+ // Set new painter opacity.
+ if (windowOpacity < 1.0)
+ painter->setOpacity(oldPainterOpacity * windowOpacity);
+
+ // set layoutdirection on the painter
+ Qt::LayoutDirection oldLayoutDirection = painter->layoutDirection();
+ painter->setLayoutDirection(widgetItem->layoutDirection());
+
+ if (widgetItem->isWindow() && widgetItem->windowType() != Qt::Popup && widgetItem->windowType() != Qt::ToolTip
+ && !(widgetItem->windowFlags() & Qt::FramelessWindowHint)) {
+ if (painterStateProtection)
+ painter->save();
+ widgetItem->paintWindowFrame(painter, option, widget);
+ if (painterStateProtection)
+ painter->restore();
+ }
+
+ widgetItem->paint(painter, option, widget);
+
+ // Restore layoutdirection on the painter.
+ painter->setLayoutDirection(oldLayoutDirection);
+ // Restore painter opacity.
+ if (windowOpacity < 1.0)
+ painter->setOpacity(oldPainterOpacity);
+}
+
+static void _q_paintIntoCache(QPixmap *pix, QGraphicsItem *item, const QRegion &pixmapExposed,
+ const QTransform &itemToPixmap, QPainter::RenderHints renderHints,
+ const QStyleOptionGraphicsItem *option, bool painterStateProtection)
+{
+ QPixmap subPix;
+ QPainter pixmapPainter;
+ QRect br = pixmapExposed.boundingRect();
+
+ // Don't use subpixmap if we get a full update.
+ if (pixmapExposed.isEmpty() || (pixmapExposed.numRects() == 1 && br.contains(pix->rect()))) {
+ pix->fill(Qt::transparent);
+ pixmapPainter.begin(pix);
+ } else {
+ subPix = QPixmap(br.size());
+ subPix.fill(Qt::transparent);
+ pixmapPainter.begin(&subPix);
+ pixmapPainter.translate(-br.topLeft());
+ if (!pixmapExposed.isEmpty()) {
+ // Applied to subPix; paint is adjusted to the coordinate space is
+ // correct.
+ pixmapPainter.setClipRegion(pixmapExposed);
+ }
+ }
+
+ pixmapPainter.setRenderHints(pixmapPainter.renderHints(), false);
+ pixmapPainter.setRenderHints(renderHints, true);
+ pixmapPainter.setWorldTransform(itemToPixmap, true);
+
+ // Render.
+ _q_paintItem(item, &pixmapPainter, option, 0, false, painterStateProtection);
+ pixmapPainter.end();
+
+ if (!subPix.isNull()) {
+ // Blit the subpixmap into the main pixmap.
+ pixmapPainter.begin(pix);
+ pixmapPainter.setCompositionMode(QPainter::CompositionMode_Source);
+ pixmapPainter.setClipRegion(pixmapExposed);
+ pixmapPainter.drawPixmap(br.topLeft(), subPix);
+ pixmapPainter.end();
+ }
+}
+
+/*!
+ \internal
+
+ Draws items directly, or using cache.
+*/
+void QGraphicsScenePrivate::drawItemHelper(QGraphicsItem *item, QPainter *painter,
+ const QStyleOptionGraphicsItem *option, QWidget *widget,
+ bool painterStateProtection)
+{
+ QGraphicsItemPrivate *itemd = item->d_ptr;
+ QGraphicsItem::CacheMode cacheMode = QGraphicsItem::CacheMode(itemd->cacheMode);
+
+ // Render directly, using no cache.
+ if (cacheMode == QGraphicsItem::NoCache
+#ifdef Q_WS_X11
+ || !X11->use_xrender
+#endif
+ ) {
+ _q_paintItem(static_cast<QGraphicsWidget *>(item), painter, option, widget, true, painterStateProtection);
+ return;
+ }
+
+ const qreal oldPainterOpacity = painter->opacity();
+ qreal newPainterOpacity = oldPainterOpacity;
+ QGraphicsProxyWidget *proxy = item->isWidget() ? qobject_cast<QGraphicsProxyWidget *>(static_cast<QGraphicsWidget *>(item)) : 0;
+ if (proxy && proxy->widget()) {
+ const qreal windowOpacity = proxy->widget()->windowOpacity();
+ if (windowOpacity < 1.0)
+ newPainterOpacity *= windowOpacity;
+ }
+
+ // Item's (local) bounding rect
+ QRectF brect = item->boundingRect();
+ if (_q_adjustedRect(brect).isEmpty())
+ return;
+
+ // Fetch the off-screen transparent buffer and exposed area info.
+ QString pixmapKey;
+ QPixmap pix;
+ QGraphicsItemCache *itemCache = itemd->extraItemCache();
+ if (cacheMode == QGraphicsItem::ItemCoordinateCache) {
+ if (itemCache->boundingRect != brect.toRect()) {
+ itemCache->boundingRect = brect.toRect();
+ itemCache->allExposed = true;
+ itemCache->exposed.clear();
+ }
+ pixmapKey = itemCache->key;
+ } else {
+ if ((pixmapKey = itemCache->deviceData.value(widget).key).isEmpty()) {
+ pixmapKey.sprintf("qgv-%p-%p", item, widget);
+ QGraphicsItemCache::DeviceData data;
+ data.key = pixmapKey;
+ itemCache->deviceData.insert(widget, data);
+ }
+ }
+
+ // Find pixmap in cache.
+ if (!itemCache->allExposed)
+ QPixmapCache::find(pixmapKey, pix);
+
+ // Render using item coordinate cache mode.
+ if (cacheMode == QGraphicsItem::ItemCoordinateCache) {
+ QSize pixmapSize;
+ bool fixedCacheSize = false;
+ if ((fixedCacheSize = itemCache->fixedSize.isValid())) {
+ pixmapSize = itemCache->fixedSize;
+ } else {
+ pixmapSize = brect.toAlignedRect().size();
+ }
+
+ // Create or recreate the pixmap.
+ int adjust = itemCache->fixedSize.isValid() ? 0 : 2;
+ QSize adjustSize(adjust*2, adjust*2);
+ QRectF br = brect.adjusted(-adjust, -adjust, adjust, adjust);
+ if (pix.isNull() || (!fixedCacheSize && (pixmapSize + adjustSize) != pix.size())) {
+ pix = QPixmap(pixmapSize + adjustSize);
+ itemCache->exposed.clear();
+ itemCache->allExposed = true;
+ }
+
+ // Redraw any newly exposed areas.
+ if (itemCache->allExposed || !itemCache->exposed.isEmpty()) {
+ // Fit the item's bounding rect into the pixmap's coordinates.
+ const QPointF scale(pixmapSize.width() / brect.width(), pixmapSize.height() / brect.height());
+ QTransform itemToPixmap;
+ itemToPixmap.scale(scale.x(), scale.y());
+ itemToPixmap.translate(-br.x(), -br.y());
+
+ // Generate the item's exposedRect and map its list of expose
+ // rects to device coordinates.
+ QStyleOptionGraphicsItem cacheOption = *option;
+ QRegion pixmapExposed;
+ QRectF exposedRect;
+ if (!itemCache->allExposed) {
+ for (int i = 0; i < itemCache->exposed.size(); ++i) {
+ QRectF r = itemCache->exposed.at(i);
+ exposedRect |= r;
+ pixmapExposed += itemToPixmap.mapRect(r).toAlignedRect();
+ }
+ } else {
+ exposedRect = brect;
+ }
+ cacheOption.exposedRect = exposedRect;
+
+ // Render.
+ _q_paintIntoCache(&pix, item, pixmapExposed, itemToPixmap, painter->renderHints(),
+ &cacheOption, painterStateProtection);
+
+ // Reinsert this pixmap into the cache.
+ QPixmapCache::insert(pixmapKey, pix);
+
+ // Reset expose data.
+ itemCache->allExposed = false;
+ itemCache->exposed.clear();
+ }
+
+ // Redraw the exposed area using the transformed painter. Depending on
+ // the hardware, this may be a server-side operation, or an expensive
+ // qpixmap-image-transform-pixmap roundtrip.
+ if (newPainterOpacity != oldPainterOpacity) {
+ painter->setOpacity(newPainterOpacity);
+ painter->drawPixmap(br, pix, QRectF(QPointF(), pix.size()));
+ painter->setOpacity(oldPainterOpacity);
+ } else {
+ painter->drawPixmap(br, pix, QRectF(QPointF(), pix.size()));
+ }
+ return;
+ }
+
+ // Render using device coordinate cache mode.
+ if (cacheMode == QGraphicsItem::DeviceCoordinateCache) {
+ // Find the item's bounds in device coordinates.
+ QRectF deviceBounds = painter->worldTransform().mapRect(brect);
+ QRect deviceRect = deviceBounds.toRect().adjusted(-1, -1, 1, 1);
+ if (deviceRect.isEmpty())
+ return;
+ QRect viewRect = widget ? widget->rect() : QRect();
+ if (widget && !viewRect.intersects(deviceRect))
+ return;
+
+ // Resort to direct rendering if the device rect exceeds the
+ // (optional) maximum bounds. (QGraphicsSvgItem uses this).
+ QSize maximumCacheSize =
+ itemd->extra(QGraphicsItemPrivate::ExtraMaxDeviceCoordCacheSize).toSize();
+ if (!maximumCacheSize.isEmpty()
+ && (deviceRect.width() > maximumCacheSize.width()
+ || deviceRect.height() > maximumCacheSize.height())) {
+ _q_paintItem(static_cast<QGraphicsWidget *>(item), painter, option, widget,
+ oldPainterOpacity != newPainterOpacity, painterStateProtection);
+ return;
+ }
+
+ // Create or reuse offscreen pixmap, possibly scroll/blit from the old one.
+ bool pixModified = false;
+ QGraphicsItemCache::DeviceData *deviceData = &itemCache->deviceData[widget];
+ bool invertable = true;
+ QTransform diff = deviceData->lastTransform.inverted(&invertable);
+ if (invertable)
+ diff *= painter->worldTransform();
+ deviceData->lastTransform = painter->worldTransform();
+ if (!invertable || diff.type() > QTransform::TxTranslate) {
+ pixModified = true;
+ itemCache->allExposed = true;
+ itemCache->exposed.clear();
+ pix = QPixmap();
+ }
+
+ // ### This is a pretty bad way to determine when to start partial
+ // exposure for DeviceCoordinateCache but it's the least intrusive
+ // approach for now.
+#if 0
+ // Only if the device rect isn't fully contained.
+ bool allowPartialCacheExposure = !viewRect.contains(deviceRect);
+#else
+ // Only if deviceRect is 20% taller or wider than the desktop.
+ QRect desktopRect = qApp->desktop()->availableGeometry(widget);
+ bool allowPartialCacheExposure = (desktopRect.width() * 1.2 < deviceRect.width()
+ || desktopRect.height() * 1.2 < deviceRect.height());
+#endif
+ QRegion scrollExposure;
+ if (deviceData->cacheIndent != QPoint() || allowPartialCacheExposure) {
+ // Part of pixmap is drawn. Either device contains viewrect (big
+ // item covers whole screen) or parts of device are outside the
+ // viewport. In either case the device rect must be the intersect
+ // between the two.
+ int dx = deviceRect.left() < viewRect.left() ? viewRect.left() - deviceRect.left() : 0;
+ int dy = deviceRect.top() < viewRect.top() ? viewRect.top() - deviceRect.top() : 0;
+ QPoint newCacheIndent(dx, dy);
+ deviceRect &= viewRect;
+
+ if (pix.isNull()) {
+ deviceData->cacheIndent = QPoint();
+ itemCache->allExposed = true;
+ itemCache->exposed.clear();
+ pixModified = true;
+ }
+
+ // Copy / "scroll" the old pixmap onto the new ole and calculate
+ // scrolled exposure.
+ if (newCacheIndent != deviceData->cacheIndent || deviceRect.size() != pix.size()) {
+ QPoint diff = newCacheIndent - deviceData->cacheIndent;
+ QPixmap newPix(deviceRect.size());
+ // ### Investigate removing this fill (test with Plasma and
+ // graphicssystem raster).
+ newPix.fill(Qt::transparent);
+ if (!pix.isNull()) {
+ QPainter newPixPainter(&newPix);
+ newPixPainter.drawPixmap(-diff, pix);
+ newPixPainter.end();
+ }
+ QRegion exposed;
+ exposed += newPix.rect();
+ if (!pix.isNull())
+ exposed -= QRect(-diff, pix.size());
+ scrollExposure = exposed;
+
+ pix = newPix;
+ pixModified = true;
+ }
+ deviceData->cacheIndent = newCacheIndent;
+ } else {
+ // Full pixmap is drawn.
+ deviceData->cacheIndent = QPoint();
+
+ // Auto-adjust the pixmap size.
+ if (deviceRect.size() != pix.size()) {
+ // exposed needs to cover the whole pixmap
+ pix = QPixmap(deviceRect.size());
+ pixModified = true;
+ itemCache->allExposed = true;
+ itemCache->exposed.clear();
+ }
+ }
+
+ // Check for newly invalidated areas.
+ if (itemCache->allExposed || !itemCache->exposed.isEmpty() || !scrollExposure.isEmpty()) {
+ // Construct an item-to-pixmap transform.
+ QPointF p = deviceRect.topLeft();
+ QTransform itemToPixmap = QTransform::fromTranslate(-p.x(), -p.y());
+ itemToPixmap = painter->worldTransform() * itemToPixmap;
+
+ // Map the item's logical expose to pixmap coordinates.
+ QRegion pixmapExposed = scrollExposure;
+ if (!itemCache->allExposed) {
+ const QVector<QRectF> &exposed = itemCache->exposed;
+ for (int i = 0; i < exposed.size(); ++i)
+ pixmapExposed += itemToPixmap.mapRect(exposed.at(i)).toRect().adjusted(-1, -1, 1, 1);
+ }
+
+ // Calculate the style option's exposedRect.
+ QRectF br;
+ if (itemCache->allExposed) {
+ br = item->boundingRect();
+ } else {
+ const QVector<QRectF> &exposed = itemCache->exposed;
+ for (int i = 0; i < exposed.size(); ++i)
+ br |= exposed.at(i);
+ QTransform pixmapToItem = itemToPixmap.inverted();
+ foreach (QRect r, scrollExposure.rects())
+ br |= pixmapToItem.mapRect(r);
+ }
+ QStyleOptionGraphicsItem cacheOption = *option;
+ cacheOption.exposedRect = br.adjusted(-1, -1, 1, 1);
+
+ // Render the exposed areas.
+ _q_paintIntoCache(&pix, item, pixmapExposed, itemToPixmap, painter->renderHints(),
+ &cacheOption, painterStateProtection);
+
+ // Reset expose data.
+ pixModified = true;
+ itemCache->allExposed = false;
+ itemCache->exposed.clear();
+ }
+
+ if (pixModified) {
+ // Reinsert this pixmap into the cache
+ QPixmapCache::insert(pixmapKey, pix);
+ }
+
+ // Redraw the exposed area using an untransformed painter. This
+ // effectively becomes a bitblit that does not transform the cache.
+ QTransform restoreTransform = painter->worldTransform();
+ painter->setWorldTransform(QTransform());
+ if (newPainterOpacity != oldPainterOpacity) {
+ painter->setOpacity(newPainterOpacity);
+ painter->drawPixmap(deviceRect.topLeft(), pix);
+ painter->setOpacity(oldPainterOpacity);
+ } else {
+ painter->drawPixmap(deviceRect.topLeft(), pix);
+ }
+ painter->setWorldTransform(restoreTransform);
+ return;
+ }
+}
+
+/*!
+ Paints the given \a items using the provided \a painter, after the
+ background has been drawn, and before the foreground has been
+ drawn. All painting is done in \e scene coordinates. Before
+ drawing each item, the painter must be transformed using
+ QGraphicsItem::sceneMatrix().
+
+ The \a options parameter is the list of style option objects for
+ each item in \a items. The \a numItems parameter is the number of
+ items in \a items and options in \a options. The \a widget
+ parameter is optional; if specified, it should point to the widget
+ that is being painted on.
+
+ The default implementation prepares the painter matrix, and calls
+ QGraphicsItem::paint() on all items. Reimplement this function to
+ provide custom painting of all items for the scene; gaining
+ complete control over how each item is drawn. In some cases this
+ can increase drawing performance significantly.
+
+ Example:
+
+ \snippet doc/src/snippets/graphicssceneadditemsnippet.cpp 0
+
+ \sa drawBackground(), drawForeground()
+*/
+void QGraphicsScene::drawItems(QPainter *painter,
+ int numItems,
+ QGraphicsItem *items[],
+ const QStyleOptionGraphicsItem options[], QWidget *widget)
+{
+ Q_D(QGraphicsScene);
+
+ // Detect if painter state protection is disabled.
+ QTransform viewTransform = painter->worldTransform();
+ QVarLengthArray<QGraphicsItem *, 16> childClippers;
+
+ for (int i = 0; i < numItems; ++i) {
+ QGraphicsItem *item = items[i];
+ if (!(item->d_ptr->ancestorFlags & QGraphicsItemPrivate::AncestorClipsChildren)) {
+ if (!childClippers.isEmpty()) {
+ // Item is not clipped to any ancestor: pop all current clippers.
+ for (int i = 0; i < childClippers.size(); ++i)
+ painter->restore();
+ childClippers.clear();
+ }
+ } else {
+ // Item is clipped to an ancestor, which may or may not be in our
+ // child clipper list. Let's start by finding the item's closest
+ // clipping ancestor.
+ QGraphicsItem *clipParent = item->parentItem();
+ while (clipParent && !(clipParent->d_ptr->flags & QGraphicsItem::ItemClipsChildrenToShape))
+ clipParent = clipParent->parentItem();
+
+ // Pop any in-between clippers. If the clipper is unknown, pop
+ // them all. ### QVarLengthArray::lastIndexOf().
+ int index = -1;
+ for (int n = childClippers.size() - 1; n >= 0; --n) {
+ if (childClippers[n] == clipParent) {
+ index = n;
+ break;
+ }
+ }
+ if (index != -1) {
+ int toPop = childClippers.size() - index - 1;
+ if (toPop > 0) {
+ for (int i = 0; i < toPop; ++i)
+ painter->restore();
+ childClippers.resize(index + 1);
+ }
+ }
+
+ // Sanity check
+ if (!childClippers.isEmpty())
+ Q_ASSERT(childClippers[childClippers.size() - 1] == clipParent);
+
+ // If the clipper list is empty at this point, but we're still
+ // clipped to an ancestor, then we need to build the clip chain
+ // ourselves. There is only one case that can produce this issue:
+ // This item is stacked behind an ancestor:
+ // ItemStacksBehindParent.
+ if (childClippers.isEmpty()) {
+ Q_ASSERT(clipParent != 0);
+ // Build a stack of clippers.
+ QVarLengthArray<QGraphicsItem *, 16> clippers;
+ QGraphicsItem *p = clipParent;
+ do {
+ if (p->d_ptr->flags & QGraphicsItem::ItemClipsChildrenToShape)
+ clippers.append(p);
+ } while ((p = p->parentItem()) && (p->d_ptr->ancestorFlags & QGraphicsItemPrivate::AncestorClipsChildren));
+
+ // ### This code path can also use the itemTransform
+ // optimization, but it's hit very rarely.
+ for (int i = clippers.size() - 1; i >= 0; --i) {
+ QGraphicsItem *clipper = clippers[i];
+ if (clipper->d_ptr->itemIsUntransformable()) {
+ painter->setWorldTransform(clipper->deviceTransform(viewTransform), false);
+ } else {
+ painter->setWorldTransform(clipper->sceneTransform() * viewTransform, false);
+ }
+
+ childClippers.append(clipper);
+ painter->save();
+ painter->setClipPath(clipper->shape(), Qt::IntersectClip);
+ }
+ Q_ASSERT(childClippers[childClippers.size() - 1] == clipParent);
+ }
+ }
+
+ // Set up the painter transform
+ if (item->d_ptr->itemIsUntransformable()) {
+ painter->setWorldTransform(item->deviceTransform(viewTransform), false);
+ } else {
+ painter->setWorldTransform(item->sceneTransform() * viewTransform, false);
+ }
+
+ // Save painter
+ bool saveState = (d->painterStateProtection || (item->flags() & QGraphicsItem::ItemClipsToShape));
+ if (saveState)
+ painter->save();
+
+ // Set local clip
+ if (item->flags() & QGraphicsItem::ItemClipsToShape)
+ painter->setClipPath(item->shape(), Qt::IntersectClip);
+
+ // Setup opacity
+ painter->setOpacity(item->effectiveOpacity());
+
+ // Draw the item
+ d->drawItemHelper(item, painter, &options[i], widget, d->painterStateProtection);
+
+ if (saveState)
+ painter->restore();
+
+ if (item->flags() & QGraphicsItem::ItemClipsChildrenToShape) {
+ // Clip descendents to this item's shape, and keep the painter
+ // saved.
+ childClippers.append(item);
+ painter->save();
+ painter->setClipPath(item->shape(), Qt::IntersectClip);
+ }
+ }
+
+ for (int i = 0; i < childClippers.size(); ++i)
+ painter->restore();
+
+ painter->setWorldTransform(viewTransform);
+}
+
+/*!
+ \since 4.4
+
+ Finds a new widget to give the keyboard focus to, as appropriate for Tab
+ and Shift+Tab, and returns true if it can find a new widget, or false if
+ it cannot. If \a next is true, this function searches forward; if \a next
+ is false, it searches backward.
+
+ You can reimplement this function in a subclass of QGraphicsScene to
+ provide fine-grained control over how tab focus passes inside your
+ scene. The default implementation is based on the tab focus chain defined
+ by QGraphicsWidget::setTabOrder().
+*/
+bool QGraphicsScene::focusNextPrevChild(bool next)
+{
+ Q_D(QGraphicsScene);
+
+ QGraphicsItem *item = focusItem();
+ if (item && !item->isWidget()) {
+ // Tab out of the scene.
+ return false;
+ }
+ if (!item) {
+ if (d->lastFocusItem && !d->lastFocusItem->isWidget()) {
+ // Restore focus to the last focusable non-widget item that had
+ // focus.
+ setFocusItem(d->lastFocusItem, next ? Qt::TabFocusReason : Qt::BacktabFocusReason);
+ return true;
+ }
+ }
+ if (!d->tabFocusFirst) {
+ // No widgets...
+ return false;
+ }
+
+ // The item must be a widget.
+ QGraphicsWidget *widget = 0;
+ if (!item) {
+ widget = next ? d->tabFocusFirst : d->tabFocusFirst->d_func()->focusPrev;
+ } else {
+ QGraphicsWidget *test = static_cast<QGraphicsWidget *>(item);
+ widget = next ? test->d_func()->focusNext : test->d_func()->focusPrev;
+ if ((next && widget == d->tabFocusFirst) || (!next && widget == d->tabFocusFirst->d_func()->focusPrev))
+ return false;
+ }
+ QGraphicsWidget *widgetThatHadFocus = widget;
+
+ // Run around the focus chain until we find a widget that can take tab focus.
+ do {
+ if (widget->flags() & QGraphicsItem::ItemIsFocusable
+ && widget->isEnabled() && widget->isVisibleTo(0)
+ && (widget->focusPolicy() & Qt::TabFocus)
+ && (!item || !item->isWindow() || item->isAncestorOf(widget))
+ ) {
+ setFocusItem(widget, next ? Qt::TabFocusReason : Qt::BacktabFocusReason);
+ return true;
+ }
+ widget = next ? widget->d_func()->focusNext : widget->d_func()->focusPrev;
+ if ((next && widget == d->tabFocusFirst) || (!next && widget == d->tabFocusFirst->d_func()->focusPrev))
+ return false;
+ } while (widget != widgetThatHadFocus);
+
+ return false;
+}
+
+/*!
+ \fn QGraphicsScene::changed(const QList<QRectF> &region)
+
+ This signal is emitted by QGraphicsScene when control reaches the
+ event loop, if the scene content changes. The \a region parameter
+ contains a list of scene rectangles that indicate the area that
+ has been changed.
+
+ \sa QGraphicsView::updateScene()
+*/
+
+/*!
+ \fn QGraphicsScene::sceneRectChanged(const QRectF &rect)
+
+ This signal is emitted by QGraphicsScene whenever the scene rect changes.
+ The \a rect parameter is the new scene rectangle.
+
+ \sa QGraphicsView::updateSceneRect()
+*/
+
+/*!
+ \fn QGraphicsScene::selectionChanged()
+ \since 4.3
+
+ This signal is emitted by QGraphicsScene whenever the selection
+ changes. You can call selectedItems() to get the new list of selected
+ items.
+
+ The selection changes whenever an item is selected or unselected, a
+ selection area is set, cleared or otherwise changed, if a preselected item
+ is added to the scene, or if a selected item is removed from the scene.
+
+ QGraphicsScene emits this signal only once for group selection operations.
+ For example, if you set a selection area, select or unselect a
+ QGraphicsItemGroup, or if you add or remove from the scene a parent item
+ that contains several selected items, selectionChanged() is emitted only
+ once after the operation has completed (instead of once for each item).
+
+ \sa setSelectionArea(), selectedItems(), QGraphicsItem::setSelected()
+*/
+
+/*!
+ \internal
+
+ This private function is called by QGraphicsItem, which is a friend of
+ QGraphicsScene. It is used by QGraphicsScene to record the rectangles that
+ need updating. It also launches a single-shot timer to ensure that
+ updated() will be emitted later.
+
+ The \a item parameter is the item that changed, and \a rect is the
+ area of the item that changed given in item coordinates.
+*/
+void QGraphicsScene::itemUpdated(QGraphicsItem *item, const QRectF &rect)
+{
+ Q_D(QGraphicsScene);
+ // Deliver the actual update.
+ if (!d->updateAll) {
+ if (d->views.isEmpty() || ((d->connectedSignals & d->changedSignalMask) && !item->d_ptr->itemIsUntransformable()
+ && qFuzzyCompare(item->boundingRegionGranularity(), qreal(0.0)))) {
+ // This block of code is kept for compatibility. Since 4.5, by default
+ // QGraphicsView does not connect the signal and we use the below
+ // method of delivering updates.
+ update(item->sceneBoundingRect());
+ } else {
+ // ### Remove _q_adjustedRects().
+ QRectF boundingRect = _q_adjustedRect(item->boundingRect());
+ if (!rect.isNull())
+ boundingRect &= _q_adjustedRect(rect);
+
+ // Update each view directly.
+ for (int i = 0; i < d->views.size(); ++i)
+ d->views.at(i)->d_func()->itemUpdated(item, boundingRect);
+ }
+ }
+ if (item->d_ptr->dirty) {
+ d->dirtyItems << item;
+ d->resetDirtyItemsLater();
+ }
+
+ // Update d->largestUntransformableItem by mapping this item's bounding
+ // rect back to the topmost untransformable item's untransformed
+ // coordinate system (which sort of equals the 1:1 coordinate system of an
+ // untransformed view).
+ if (item->d_ptr->itemIsUntransformable()) {
+ QGraphicsItem *parent = item;
+ while (parent && (parent->d_ptr->ancestorFlags & QGraphicsItemPrivate::AncestorIgnoresTransformations))
+ parent = parent->parentItem();
+ d->largestUntransformableItem |= item->mapToItem(parent, item->boundingRect()).boundingRect();
+ }
+
+ // Only track the automatically growing scene rect if the scene has no
+ // defined scene rect.
+ if (!d->hasSceneRect) {
+ QRectF oldGrowingItemsBoundingRect = d->growingItemsBoundingRect;
+ d->growingItemsBoundingRect |= _q_adjustedRect(item->sceneBoundingRect());
+ if (d->growingItemsBoundingRect != oldGrowingItemsBoundingRect)
+ emit sceneRectChanged(d->growingItemsBoundingRect);
+ }
+}
+
+/*!
+ \since 4.4
+
+ Returns the scene's style, or the same as QApplication::style() if the
+ scene has not been explicitly assigned a style.
+
+ \sa setStyle()
+*/
+QStyle *QGraphicsScene::style() const
+{
+ Q_D(const QGraphicsScene);
+ // ### This function, and the use of styles in general, is non-reentrant.
+ return d->style ? d->style : qApp->style();
+}
+
+/*!
+ \since 4.4
+
+ Sets or replaces the style of the scene to \a style, and reparents the
+ style to this scene. Any previously assigned style is deleted. The scene's
+ style defaults to QApplication::style(), and serves as the default for all
+ QGraphicsWidget items in the scene.
+
+ Changing the style, either directly by calling this function, or
+ indirectly by calling QApplication::setStyle(), will automatically update
+ the style for all widgets in the scene that do not have a style explicitly
+ assigned to them.
+
+ If \a style is 0, QGraphicsScene will revert to QApplication::style().
+
+ \sa style()
+*/
+void QGraphicsScene::setStyle(QStyle *style)
+{
+ Q_D(QGraphicsScene);
+ // ### This function, and the use of styles in general, is non-reentrant.
+ if (style == d->style)
+ return;
+
+ // Delete the old style,
+ delete d->style;
+ if ((d->style = style))
+ d->style->setParent(this);
+
+ // Notify the scene.
+ QEvent event(QEvent::StyleChange);
+ QApplication::sendEvent(this, &event);
+
+ // Notify all widgets that don't have a style explicitly set.
+ foreach (QGraphicsItem *item, items()) {
+ if (item->isWidget()) {
+ QGraphicsWidget *widget = static_cast<QGraphicsWidget *>(item);
+ if (!widget->testAttribute(Qt::WA_SetStyle))
+ QApplication::sendEvent(widget, &event);
+ }
+ }
+}
+
+/*!
+ \property QGraphicsScene::font
+ \since 4.4
+ \brief the scene's default font
+
+ This property provides the scene's font. The scene font defaults to,
+ and resolves all its entries from, QApplication::font.
+
+ If the scene's font changes, either directly through setFont() or
+ indirectly when the application font changes, QGraphicsScene first
+ sends itself a \l{QEvent::FontChange}{FontChange} event, and it then
+ sends \l{QEvent::FontChange}{FontChange} events to all top-level
+ widget items in the scene. These items respond by resolving their own
+ fonts to the scene, and they then notify their children, who again
+ notify their children, and so on, until all widget items have updated
+ their fonts.
+
+ Changing the scene font, (directly or indirectly through
+ QApplication::setFont(),) automatically schedules a redraw the entire
+ scene.
+
+ \sa QWidget::font, QApplication::setFont(), palette, style()
+*/
+QFont QGraphicsScene::font() const
+{
+ Q_D(const QGraphicsScene);
+ return d->font;
+}
+void QGraphicsScene::setFont(const QFont &font)
+{
+ Q_D(QGraphicsScene);
+ QFont naturalFont = qApp->font();
+ naturalFont.resolve(0);
+ QFont resolvedFont = font.resolve(naturalFont);
+ d->setFont_helper(resolvedFont);
+}
+
+/*!
+ \property QGraphicsScene::palette
+ \since 4.4
+ \brief the scene's default palette
+
+ This property provides the scene's palette. The scene palette defaults to,
+ and resolves all its entries from, QApplication::palette.
+
+ If the scene's palette changes, either directly through setPalette() or
+ indirectly when the application palette changes, QGraphicsScene first
+ sends itself a \l{QEvent::PaletteChange}{PaletteChange} event, and it then
+ sends \l{QEvent::PaletteChange}{PaletteChange} events to all top-level
+ widget items in the scene. These items respond by resolving their own
+ palettes to the scene, and they then notify their children, who again
+ notify their children, and so on, until all widget items have updated
+ their palettes.
+
+ Changing the scene palette, (directly or indirectly through
+ QApplication::setPalette(),) automatically schedules a redraw the entire
+ scene.
+
+ \sa QWidget::palette, QApplication::setPalette(), font, style()
+*/
+QPalette QGraphicsScene::palette() const
+{
+ Q_D(const QGraphicsScene);
+ return d->palette;
+}
+void QGraphicsScene::setPalette(const QPalette &palette)
+{
+ Q_D(QGraphicsScene);
+ QPalette naturalPalette = qApp->palette();
+ naturalPalette.resolve(0);
+ QPalette resolvedPalette = palette.resolve(naturalPalette);
+ d->setPalette_helper(resolvedPalette);
+}
+
+/*!
+ \since 4.4
+
+ Returns the current active window, or 0 if there is no window is currently
+ active.
+
+ \sa QGraphicsScene::setActiveWindow()
+*/
+QGraphicsWidget *QGraphicsScene::activeWindow() const
+{
+ Q_D(const QGraphicsScene);
+ return d->activeWindow;
+}
+
+/*!
+ \since 4.4
+ Activates \a widget, which must be a widget in this scene. You can also
+ pass 0 for \a widget, in which case QGraphicsScene will deactivate any
+ currently active window.
+
+ \sa activeWindow(), QGraphicsWidget::isActiveWindow()
+*/
+void QGraphicsScene::setActiveWindow(QGraphicsWidget *widget)
+{
+ Q_D(QGraphicsScene);
+ if (widget && widget->scene() != this) {
+ qWarning("QGraphicsScene::setActiveWindow: widget %p must be part of this scene",
+ widget);
+ return;
+ }
+
+ // Activate the widget's window.
+ QGraphicsWidget *window = widget ? widget->window() : 0;
+ if (window == d->activeWindow)
+ return;
+
+ // Deactivate the last active window.
+ if (d->activeWindow) {
+ if (QGraphicsWidget *fw = d->activeWindow->focusWidget()) {
+ // Remove focus from the current focus item.
+ if (fw == focusItem())
+ setFocusItem(0, Qt::ActiveWindowFocusReason);
+ }
+
+ QEvent event(QEvent::WindowDeactivate);
+ QApplication::sendEvent(d->activeWindow, &event);
+ }
+
+ // Update activate state.
+ d->activeWindow = window;
+ QEvent event(QEvent::ActivationChange);
+ QApplication::sendEvent(this, &event);
+
+ // Activate
+ if (window) {
+ QEvent event(QEvent::WindowActivate);
+ QApplication::sendEvent(window, &event);
+
+ QList<QGraphicsItem *> siblingWindows;
+ QGraphicsItem *parent = window->parentItem();
+ // Raise ### inefficient for toplevels
+ foreach (QGraphicsItem *sibling, parent ? parent->children() : items()) {
+ if (sibling != window && sibling->isWidget()
+ && static_cast<QGraphicsWidget *>(sibling)->isWindow()) {
+ siblingWindows << sibling;
+ }
+ }
+
+ // Find the highest z value.
+ qreal z = window->zValue();
+ for (int i = 0; i < siblingWindows.size(); ++i)
+ z = qMax(z, siblingWindows.at(i)->zValue());
+
+ // This will probably never overflow.
+ const qreal litt = qreal(0.001);
+ window->setZValue(z + litt);
+
+ if (QGraphicsWidget *focusChild = window->focusWidget())
+ focusChild->setFocus(Qt::ActiveWindowFocusReason);
+ }
+}
+
+QT_END_NAMESPACE
+
+#include "moc_qgraphicsscene.cpp"
+
+#endif // QT_NO_GRAPHICSVIEW
diff --git a/src/gui/graphicsview/qgraphicsscene.h b/src/gui/graphicsview/qgraphicsscene.h
new file mode 100644
index 0000000000..9802f8755b
--- /dev/null
+++ b/src/gui/graphicsview/qgraphicsscene.h
@@ -0,0 +1,301 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QGRAPHICSSCENE_H
+#define QGRAPHICSSCENE_H
+
+#include <QtCore/qobject.h>
+#include <QtCore/qpoint.h>
+#include <QtCore/qrect.h>
+#include <QtGui/qbrush.h>
+#include <QtGui/qfont.h>
+#include <QtGui/qtransform.h>
+#include <QtGui/qmatrix.h>
+#include <QtGui/qpen.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+#if !defined(QT_NO_GRAPHICSVIEW) || (QT_EDITION & QT_MODULE_GRAPHICSVIEW) != QT_MODULE_GRAPHICSVIEW
+
+template<typename T> class QList;
+class QFocusEvent;
+class QFont;
+class QFontMetrics;
+class QGraphicsEllipseItem;
+class QGraphicsItem;
+class QGraphicsItemGroup;
+class QGraphicsLineItem;
+class QGraphicsPathItem;
+class QGraphicsPixmapItem;
+class QGraphicsPolygonItem;
+class QGraphicsProxyWidget;
+class QGraphicsRectItem;
+class QGraphicsSceneContextMenuEvent;
+class QGraphicsSceneDragDropEvent;
+class QGraphicsSceneEvent;
+class QGraphicsSceneHelpEvent;
+class QGraphicsSceneHoverEvent;
+class QGraphicsSceneMouseEvent;
+class QGraphicsSceneWheelEvent;
+class QGraphicsSimpleTextItem;
+class QGraphicsTextItem;
+class QGraphicsView;
+class QGraphicsWidget;
+class QHelpEvent;
+class QInputMethodEvent;
+class QKeyEvent;
+class QLineF;
+class QPainterPath;
+class QPixmap;
+class QPointF;
+class QPolygonF;
+class QRectF;
+class QSizeF;
+class QStyle;
+class QStyleOptionGraphicsItem;
+
+class QGraphicsScenePrivate;
+class Q_GUI_EXPORT QGraphicsScene : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(QBrush backgroundBrush READ backgroundBrush WRITE setBackgroundBrush)
+ Q_PROPERTY(QBrush foregroundBrush READ foregroundBrush WRITE setForegroundBrush)
+ Q_PROPERTY(ItemIndexMethod itemIndexMethod READ itemIndexMethod WRITE setItemIndexMethod)
+ Q_PROPERTY(QRectF sceneRect READ sceneRect WRITE setSceneRect)
+ Q_PROPERTY(int bspTreeDepth READ bspTreeDepth WRITE setBspTreeDepth)
+ Q_PROPERTY(QPalette palette READ palette WRITE setPalette)
+ Q_PROPERTY(QFont font READ font WRITE setFont)
+ Q_PROPERTY(bool sortCacheEnabled READ isSortCacheEnabled WRITE setSortCacheEnabled)
+ Q_PROPERTY(bool stickyFocus READ stickyFocus WRITE setStickyFocus)
+
+public:
+ enum ItemIndexMethod {
+ BspTreeIndex,
+ NoIndex = -1
+ };
+
+ enum SceneLayer {
+ ItemLayer = 0x1,
+ BackgroundLayer = 0x2,
+ ForegroundLayer = 0x4,
+ AllLayers = 0xffff
+ };
+ Q_DECLARE_FLAGS(SceneLayers, SceneLayer)
+
+ QGraphicsScene(QObject *parent = 0);
+ QGraphicsScene(const QRectF &sceneRect, QObject *parent = 0);
+ QGraphicsScene(qreal x, qreal y, qreal width, qreal height, QObject *parent = 0);
+ virtual ~QGraphicsScene();
+
+ QRectF sceneRect() const;
+ inline qreal width() const { return sceneRect().width(); }
+ inline qreal height() const { return sceneRect().height(); }
+ void setSceneRect(const QRectF &rect);
+ inline void setSceneRect(qreal x, qreal y, qreal w, qreal h)
+ { setSceneRect(QRectF(x, y, w, h)); }
+
+ void render(QPainter *painter,
+ const QRectF &target = QRectF(), const QRectF &source = QRectF(),
+ Qt::AspectRatioMode aspectRatioMode = Qt::KeepAspectRatio);
+
+ ItemIndexMethod itemIndexMethod() const;
+ void setItemIndexMethod(ItemIndexMethod method);
+
+ bool isSortCacheEnabled() const;
+ void setSortCacheEnabled(bool enabled);
+
+ int bspTreeDepth() const;
+ void setBspTreeDepth(int depth);
+
+ QRectF itemsBoundingRect() const;
+
+ QList<QGraphicsItem *> items() const;
+ QList<QGraphicsItem *> items(const QPointF &pos) const;
+ QList<QGraphicsItem *> items(const QRectF &rect, Qt::ItemSelectionMode mode = Qt::IntersectsItemShape) const;
+ QList<QGraphicsItem *> items(const QPolygonF &polygon, Qt::ItemSelectionMode mode = Qt::IntersectsItemShape) const;
+ QList<QGraphicsItem *> items(const QPainterPath &path, Qt::ItemSelectionMode mode = Qt::IntersectsItemShape) const;
+ QList<QGraphicsItem *> collidingItems(const QGraphicsItem *item, Qt::ItemSelectionMode mode = Qt::IntersectsItemShape) const;
+ QGraphicsItem *itemAt(const QPointF &pos) const;
+
+ inline QList<QGraphicsItem *> items(qreal x, qreal y, qreal w, qreal h, Qt::ItemSelectionMode mode = Qt::IntersectsItemShape) const
+ { return items(QRectF(x, y, w, h), mode); }
+ inline QGraphicsItem *itemAt(qreal x, qreal y) const
+ { return itemAt(QPointF(x, y)); }
+
+ QList<QGraphicsItem *> selectedItems() const;
+ QPainterPath selectionArea() const;
+ void setSelectionArea(const QPainterPath &path);
+ void setSelectionArea(const QPainterPath &path, Qt::ItemSelectionMode);
+
+ QGraphicsItemGroup *createItemGroup(const QList<QGraphicsItem *> &items);
+ void destroyItemGroup(QGraphicsItemGroup *group);
+
+ void addItem(QGraphicsItem *item);
+ QGraphicsEllipseItem *addEllipse(const QRectF &rect, const QPen &pen = QPen(), const QBrush &brush = QBrush());
+ QGraphicsLineItem *addLine(const QLineF &line, const QPen &pen = QPen());
+ QGraphicsPathItem *addPath(const QPainterPath &path, const QPen &pen = QPen(), const QBrush &brush = QBrush());
+ QGraphicsPixmapItem *addPixmap(const QPixmap &pixmap);
+ QGraphicsPolygonItem *addPolygon(const QPolygonF &polygon, const QPen &pen = QPen(), const QBrush &brush = QBrush());
+ QGraphicsRectItem *addRect(const QRectF &rect, const QPen &pen = QPen(), const QBrush &brush = QBrush());
+ QGraphicsTextItem *addText(const QString &text, const QFont &font = QFont());
+ QGraphicsSimpleTextItem *addSimpleText(const QString &text, const QFont &font = QFont());
+ QGraphicsProxyWidget *addWidget(QWidget *widget, Qt::WindowFlags wFlags = 0);
+ inline QGraphicsEllipseItem *addEllipse(qreal x, qreal y, qreal w, qreal h, const QPen &pen = QPen(), const QBrush &brush = QBrush())
+ { return addEllipse(QRectF(x, y, w, h), pen, brush); }
+ inline QGraphicsLineItem *addLine(qreal x1, qreal y1, qreal x2, qreal y2, const QPen &pen = QPen())
+ { return addLine(QLineF(x1, y1, x2, y2), pen); }
+ inline QGraphicsRectItem *addRect(qreal x, qreal y, qreal w, qreal h, const QPen &pen = QPen(), const QBrush &brush = QBrush())
+ { return addRect(QRectF(x, y, w, h), pen, brush); }
+ void removeItem(QGraphicsItem *item);
+
+ QGraphicsItem *focusItem() const;
+ void setFocusItem(QGraphicsItem *item, Qt::FocusReason focusReason = Qt::OtherFocusReason);
+ bool hasFocus() const;
+ void setFocus(Qt::FocusReason focusReason = Qt::OtherFocusReason);
+ void clearFocus();
+
+ void setStickyFocus(bool enabled);
+ bool stickyFocus() const;
+
+ QGraphicsItem *mouseGrabberItem() const;
+
+ QBrush backgroundBrush() const;
+ void setBackgroundBrush(const QBrush &brush);
+
+ QBrush foregroundBrush() const;
+ void setForegroundBrush(const QBrush &brush);
+
+ virtual QVariant inputMethodQuery(Qt::InputMethodQuery query) const;
+
+ QList <QGraphicsView *> views() const;
+
+ inline void update(qreal x, qreal y, qreal w, qreal h)
+ { update(QRectF(x, y, w, h)); }
+ inline void invalidate(qreal x, qreal y, qreal w, qreal h, SceneLayers layers = AllLayers)
+ { invalidate(QRectF(x, y, w, h), layers); }
+
+ QStyle *style() const;
+ void setStyle(QStyle *style);
+
+ QFont font() const;
+ void setFont(const QFont &font);
+
+ QPalette palette() const;
+ void setPalette(const QPalette &palette);
+
+ QGraphicsWidget *activeWindow() const;
+ void setActiveWindow(QGraphicsWidget *widget);
+
+public Q_SLOTS:
+ void update(const QRectF &rect = QRectF());
+ void invalidate(const QRectF &rect = QRectF(), SceneLayers layers = AllLayers);
+ void advance();
+ void clearSelection();
+ void clear();
+
+protected:
+ bool event(QEvent *event);
+ bool eventFilter(QObject *watched, QEvent *event);
+ virtual void contextMenuEvent(QGraphicsSceneContextMenuEvent *event);
+ virtual void dragEnterEvent(QGraphicsSceneDragDropEvent *event);
+ virtual void dragMoveEvent(QGraphicsSceneDragDropEvent *event);
+ virtual void dragLeaveEvent(QGraphicsSceneDragDropEvent *event);
+ virtual void dropEvent(QGraphicsSceneDragDropEvent *event);
+ virtual void focusInEvent(QFocusEvent *event);
+ virtual void focusOutEvent(QFocusEvent *event);
+ virtual void helpEvent(QGraphicsSceneHelpEvent *event);
+ virtual void keyPressEvent(QKeyEvent *event);
+ virtual void keyReleaseEvent(QKeyEvent *event);
+ virtual void mousePressEvent(QGraphicsSceneMouseEvent *event);
+ virtual void mouseMoveEvent(QGraphicsSceneMouseEvent *event);
+ virtual void mouseReleaseEvent(QGraphicsSceneMouseEvent *event);
+ virtual void mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event);
+ virtual void wheelEvent(QGraphicsSceneWheelEvent *event);
+ virtual void inputMethodEvent(QInputMethodEvent *event);
+
+ virtual void drawBackground(QPainter *painter, const QRectF &rect);
+ virtual void drawForeground(QPainter *painter, const QRectF &rect);
+ virtual void drawItems(QPainter *painter, int numItems,
+ QGraphicsItem *items[],
+ const QStyleOptionGraphicsItem options[],
+ QWidget *widget = 0);
+
+protected Q_SLOTS:
+ bool focusNextPrevChild(bool next);
+
+Q_SIGNALS:
+ void changed(const QList<QRectF> &region);
+ void sceneRectChanged(const QRectF &rect);
+ void selectionChanged();
+
+private:
+ void itemUpdated(QGraphicsItem *item, const QRectF &rect);
+
+ Q_DECLARE_PRIVATE(QGraphicsScene)
+ Q_DISABLE_COPY(QGraphicsScene)
+ Q_PRIVATE_SLOT(d_func(), void _q_updateIndex())
+ Q_PRIVATE_SLOT(d_func(), void _q_emitUpdated())
+ Q_PRIVATE_SLOT(d_func(), void _q_removeItemLater(QGraphicsItem *item))
+ Q_PRIVATE_SLOT(d_func(), void _q_updateLater())
+ Q_PRIVATE_SLOT(d_func(), void _q_polishItems())
+ Q_PRIVATE_SLOT(d_func(), void _q_updateSortCache())
+ Q_PRIVATE_SLOT(d_func(), void _q_resetDirtyItems())
+ friend class QGraphicsItem;
+ friend class QGraphicsItemPrivate;
+ friend class QGraphicsView;
+ friend class QGraphicsViewPrivate;
+ friend class QGraphicsWidget;
+ friend class QGraphicsWidgetPrivate;
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(QGraphicsScene::SceneLayers)
+
+#endif // QT_NO_GRAPHICSVIEW
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif
diff --git a/src/gui/graphicsview/qgraphicsscene_bsp.cpp b/src/gui/graphicsview/qgraphicsscene_bsp.cpp
new file mode 100644
index 0000000000..f8fa4500c8
--- /dev/null
+++ b/src/gui/graphicsview/qgraphicsscene_bsp.cpp
@@ -0,0 +1,328 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qgraphicsscene_bsp_p.h"
+
+#ifndef QT_NO_GRAPHICSVIEW
+
+#include <QtCore/qstring.h>
+#include <private/qgraphicsitem_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QGraphicsSceneInsertItemBspTreeVisitor : public QGraphicsSceneBspTreeVisitor
+{
+public:
+ QGraphicsItem *item;
+
+ void visit(QList<QGraphicsItem *> *items)
+ { items->prepend(item); }
+};
+
+class QGraphicsSceneRemoveItemBspTreeVisitor : public QGraphicsSceneBspTreeVisitor
+{
+public:
+ QGraphicsItem *item;
+
+ void visit(QList<QGraphicsItem *> *items)
+ { items->removeAll(item); }
+};
+
+class QGraphicsSceneFindItemBspTreeVisitor : public QGraphicsSceneBspTreeVisitor
+{
+public:
+ QList<QGraphicsItem *> *foundItems;
+
+ void visit(QList<QGraphicsItem *> *items)
+ {
+ for (int i = 0; i < items->size(); ++i) {
+ QGraphicsItem *item = items->at(i);
+ if (!item->d_func()->itemDiscovered && item->isVisible()) {
+ item->d_func()->itemDiscovered = 1;
+ foundItems->prepend(item);
+ }
+ }
+ }
+};
+
+QGraphicsSceneBspTree::QGraphicsSceneBspTree()
+ : leafCnt(0)
+{
+ insertVisitor = new QGraphicsSceneInsertItemBspTreeVisitor;
+ removeVisitor = new QGraphicsSceneRemoveItemBspTreeVisitor;
+ findVisitor = new QGraphicsSceneFindItemBspTreeVisitor;
+}
+
+QGraphicsSceneBspTree::~QGraphicsSceneBspTree()
+{
+ delete insertVisitor;
+ delete removeVisitor;
+ delete findVisitor;
+}
+
+void QGraphicsSceneBspTree::initialize(const QRectF &rect, int depth)
+{
+ this->rect = rect;
+ leafCnt = 0;
+ nodes.resize((1 << (depth + 1)) - 1);
+ nodes.fill(Node());
+ leaves.resize(1 << depth);
+ leaves.fill(QList<QGraphicsItem *>());
+
+ initialize(rect, depth, 0);
+}
+
+void QGraphicsSceneBspTree::clear()
+{
+ leafCnt = 0;
+ nodes.clear();
+ leaves.clear();
+}
+
+void QGraphicsSceneBspTree::insertItem(QGraphicsItem *item, const QRectF &rect)
+{
+ insertVisitor->item = item;
+ climbTree(insertVisitor, rect);
+}
+
+void QGraphicsSceneBspTree::removeItem(QGraphicsItem *item, const QRectF &rect)
+{
+ removeVisitor->item = item;
+ climbTree(removeVisitor, rect);
+}
+
+void QGraphicsSceneBspTree::removeItems(const QSet<QGraphicsItem *> &items)
+{
+ for (int i = 0; i < leaves.size(); ++i) {
+ QList<QGraphicsItem *> newItemList;
+ const QList<QGraphicsItem *> &oldItemList = leaves[i];
+ for (int j = 0; j < oldItemList.size(); ++j) {
+ QGraphicsItem *item = oldItemList.at(j);
+ if (!items.contains(item))
+ newItemList << item;
+ }
+ leaves[i] = newItemList;
+ }
+}
+
+QList<QGraphicsItem *> QGraphicsSceneBspTree::items(const QRectF &rect)
+{
+ QList<QGraphicsItem *> tmp;
+ findVisitor->foundItems = &tmp;
+ climbTree(findVisitor, rect);
+ return tmp;
+}
+
+QList<QGraphicsItem *> QGraphicsSceneBspTree::items(const QPointF &pos)
+{
+ QList<QGraphicsItem *> tmp;
+ findVisitor->foundItems = &tmp;
+ climbTree(findVisitor, pos);
+ return tmp;
+}
+
+int QGraphicsSceneBspTree::leafCount() const
+{
+ return leafCnt;
+}
+
+QString QGraphicsSceneBspTree::debug(int index) const
+{
+ const Node *node = &nodes.at(index);
+
+ QString tmp;
+ if (node->type == Node::Leaf) {
+ QRectF rect = rectForIndex(index);
+ if (!leaves[node->leafIndex].isEmpty()) {
+ tmp += QString::fromLatin1("[%1, %2, %3, %4] contains %5 items\n")
+ .arg(rect.left()).arg(rect.top())
+ .arg(rect.width()).arg(rect.height())
+ .arg(leaves[node->leafIndex].size());
+ }
+ } else {
+ if (node->type == Node::Horizontal) {
+ tmp += debug(firstChildIndex(index));
+ tmp += debug(firstChildIndex(index) + 1);
+ } else {
+ tmp += debug(firstChildIndex(index));
+ tmp += debug(firstChildIndex(index) + 1);
+ }
+ }
+
+ return tmp;
+}
+
+void QGraphicsSceneBspTree::initialize(const QRectF &rect, int depth, int index)
+{
+ Node *node = &nodes[index];
+ if (index == 0) {
+ node->type = Node::Horizontal;
+ node->offset = rect.center().x();
+ }
+
+ if (depth) {
+ Node::Type type;
+ QRectF rect1, rect2;
+ qreal offset1, offset2;
+
+ if (node->type == Node::Horizontal) {
+ type = Node::Vertical;
+ rect1.setRect(rect.left(), rect.top(), rect.width(), rect.height() / 2);
+ rect2.setRect(rect1.left(), rect1.bottom(), rect1.width(), rect.height() - rect1.height());
+ offset1 = rect1.center().x();
+ offset2 = rect2.center().x();
+ } else {
+ type = Node::Horizontal;
+ rect1.setRect(rect.left(), rect.top(), rect.width() / 2, rect.height());
+ rect2.setRect(rect1.right(), rect1.top(), rect.width() - rect1.width(), rect1.height());
+ offset1 = rect1.center().y();
+ offset2 = rect2.center().y();
+ }
+
+ int childIndex = firstChildIndex(index);
+
+ Node *child = &nodes[childIndex];
+ child->offset = offset1;
+ child->type = type;
+
+ child = &nodes[childIndex + 1];
+ child->offset = offset2;
+ child->type = type;
+
+ initialize(rect1, depth - 1, childIndex);
+ initialize(rect2, depth - 1, childIndex + 1);
+ } else {
+ node->type = Node::Leaf;
+ node->leafIndex = leafCnt++;
+ }
+}
+
+void QGraphicsSceneBspTree::climbTree(QGraphicsSceneBspTreeVisitor *visitor, const QPointF &pos, int index)
+{
+ if (nodes.isEmpty())
+ return;
+
+ const Node &node = nodes.at(index);
+ int childIndex = firstChildIndex(index);
+
+ switch (node.type) {
+ case Node::Leaf: {
+ visitor->visit(&leaves[node.leafIndex]);
+ break;
+ }
+ case Node::Vertical:
+ if (pos.x() < node.offset) {
+ climbTree(visitor, pos, childIndex);
+ } else {
+ climbTree(visitor, pos, childIndex + 1);
+ }
+ break;
+ case Node::Horizontal:
+ if (pos.y() < node.offset) {
+ climbTree(visitor, pos, childIndex);
+ } else {
+ climbTree(visitor, pos, childIndex + 1);
+ }
+ break;
+ }
+}
+
+void QGraphicsSceneBspTree::climbTree(QGraphicsSceneBspTreeVisitor *visitor, const QRectF &rect, int index)
+{
+ if (nodes.isEmpty())
+ return;
+
+ const Node &node = nodes.at(index);
+ int childIndex = firstChildIndex(index);
+
+ switch (node.type) {
+ case Node::Leaf: {
+ visitor->visit(&leaves[node.leafIndex]);
+ break;
+ }
+ case Node::Vertical:
+ if (rect.left() < node.offset) {
+ climbTree(visitor, rect, childIndex);
+ if (rect.right() >= node.offset)
+ climbTree(visitor, rect, childIndex + 1);
+ } else {
+ climbTree(visitor, rect, childIndex + 1);
+ }
+ break;
+ case Node::Horizontal:
+ int childIndex = firstChildIndex(index);
+ if (rect.top() < node.offset) {
+ climbTree(visitor, rect, childIndex);
+ if (rect.bottom() >= node.offset)
+ climbTree(visitor, rect, childIndex + 1);
+ } else {
+ climbTree(visitor, rect, childIndex + 1);
+ }
+ }
+}
+
+QRectF QGraphicsSceneBspTree::rectForIndex(int index) const
+{
+ if (index <= 0)
+ return rect;
+
+ int parentIdx = parentIndex(index);
+ QRectF rect = rectForIndex(parentIdx);
+ const Node *parent = &nodes.at(parentIdx);
+
+ if (parent->type == Node::Horizontal) {
+ if (index & 1)
+ rect.setRight(parent->offset);
+ else
+ rect.setLeft(parent->offset);
+ } else {
+ if (index & 1)
+ rect.setBottom(parent->offset);
+ else
+ rect.setTop(parent->offset);
+ }
+
+ return rect;
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_GRAPHICSVIEW
diff --git a/src/gui/graphicsview/qgraphicsscene_bsp_p.h b/src/gui/graphicsview/qgraphicsscene_bsp_p.h
new file mode 100644
index 0000000000..e6ceb78f71
--- /dev/null
+++ b/src/gui/graphicsview/qgraphicsscene_bsp_p.h
@@ -0,0 +1,137 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QGRAPHICSSCENEBSPTREE_P_H
+#define QGRAPHICSSCENEBSPTREE_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/qlist.h>
+
+#if !defined(QT_NO_GRAPHICSVIEW) || (QT_EDITION & QT_MODULE_GRAPHICSVIEW) != QT_MODULE_GRAPHICSVIEW
+
+#include <QtCore/qrect.h>
+#include <QtCore/qset.h>
+#include <QtCore/qvector.h>
+
+QT_BEGIN_NAMESPACE
+
+class QGraphicsItem;
+class QGraphicsSceneBspTreeVisitor;
+class QGraphicsSceneInsertItemBspTreeVisitor;
+class QGraphicsSceneRemoveItemBspTreeVisitor;
+class QGraphicsSceneFindItemBspTreeVisitor;
+
+class QGraphicsSceneBspTree
+{
+public:
+ struct Node
+ {
+ enum Type { Horizontal, Vertical, Leaf };
+ union {
+ qreal offset;
+ int leafIndex;
+ };
+ Type type;
+ };
+
+ QGraphicsSceneBspTree();
+ ~QGraphicsSceneBspTree();
+
+ void initialize(const QRectF &rect, int depth);
+ void clear();
+
+ void insertItem(QGraphicsItem *item, const QRectF &rect);
+ void removeItem(QGraphicsItem *item, const QRectF &rect);
+ void removeItems(const QSet<QGraphicsItem *> &items);
+
+ QList<QGraphicsItem *> items(const QRectF &rect);
+ QList<QGraphicsItem *> items(const QPointF &pos);
+ int leafCount() const;
+
+ inline int firstChildIndex(int index) const
+ { return index * 2 + 1; }
+
+ inline int parentIndex(int index) const
+ { return index > 0 ? ((index & 1) ? ((index - 1) / 2) : ((index - 2) / 2)) : -1; }
+
+ QString debug(int index) const;
+
+private:
+ void initialize(const QRectF &rect, int depth, int index);
+ void climbTree(QGraphicsSceneBspTreeVisitor *visitor, const QPointF &pos, int index = 0);
+ void climbTree(QGraphicsSceneBspTreeVisitor *visitor, const QRectF &rect, int index = 0);
+
+ void findItems(QList<QGraphicsItem *> *foundItems, const QRectF &rect, int index);
+ void findItems(QList<QGraphicsItem *> *foundItems, const QPointF &pos, int index);
+ QRectF rectForIndex(int index) const;
+
+ QVector<Node> nodes;
+ QVector<QList<QGraphicsItem *> > leaves;
+ int leafCnt;
+ QRectF rect;
+
+ QGraphicsSceneInsertItemBspTreeVisitor *insertVisitor;
+ QGraphicsSceneRemoveItemBspTreeVisitor *removeVisitor;
+ QGraphicsSceneFindItemBspTreeVisitor *findVisitor;
+};
+
+class QGraphicsSceneBspTreeVisitor
+{
+public:
+ virtual ~QGraphicsSceneBspTreeVisitor() { }
+ virtual void visit(QList<QGraphicsItem *> *items) = 0;
+};
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_GRAPHICSVIEW
+
+#endif // QGRAPHICSSCENEBSPTREE_P_H
diff --git a/src/gui/graphicsview/qgraphicsscene_p.h b/src/gui/graphicsview/qgraphicsscene_p.h
new file mode 100644
index 0000000000..9c165d150d
--- /dev/null
+++ b/src/gui/graphicsview/qgraphicsscene_p.h
@@ -0,0 +1,265 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QGRAPHICSSCENE_P_H
+#define QGRAPHICSSCENE_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of qapplication_*.cpp, qwidget*.cpp and qfiledialog.cpp. This header
+// file may change from version to version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qgraphicsscene.h"
+
+#if !defined(QT_NO_GRAPHICSVIEW) || (QT_EDITION & QT_MODULE_GRAPHICSVIEW) != QT_MODULE_GRAPHICSVIEW
+
+#include "qgraphicsscene_bsp_p.h"
+#include "qgraphicsitem_p.h"
+
+#include <private/qobject_p.h>
+#include <QtCore/qbitarray.h>
+#include <QtCore/qlist.h>
+#include <QtCore/qmap.h>
+#include <QtCore/qset.h>
+#include <QtGui/qfont.h>
+#include <QtGui/qpalette.h>
+#include <QtGui/qstyle.h>
+
+QT_BEGIN_NAMESPACE
+
+class QGraphicsView;
+class QGraphicsWidget;
+
+class QGraphicsScenePrivate : public QObjectPrivate
+{
+ Q_DECLARE_PUBLIC(QGraphicsScene)
+public:
+ QGraphicsScenePrivate();
+ void init();
+
+ quint32 changedSignalMask;
+
+ QGraphicsScene::ItemIndexMethod indexMethod;
+ int bspTreeDepth;
+
+ QList<QGraphicsItem *> estimateItemsInRect(const QRectF &rect) const;
+ void addToIndex(QGraphicsItem *item);
+ void removeFromIndex(QGraphicsItem *item);
+ void resetIndex();
+
+ QGraphicsSceneBspTree bspTree;
+ void _q_updateIndex();
+ int lastItemCount;
+
+ QRectF sceneRect;
+ bool hasSceneRect;
+ QRectF growingItemsBoundingRect;
+ QRectF largestUntransformableItem;
+
+ void _q_emitUpdated();
+ QList<QRectF> updatedRects;
+ bool updateAll;
+ bool calledEmitUpdated;
+
+ QPainterPath selectionArea;
+ int selectionChanging;
+ QSet<QGraphicsItem *> selectedItems;
+ QList<QGraphicsItem *> unindexedItems;
+ QList<QGraphicsItem *> indexedItems;
+ QList<QGraphicsItem *> dirtyItems;
+ QList<QGraphicsItem *> pendingUpdateItems;
+ QList<QGraphicsItem *> unpolishedItems;
+ QMap<QGraphicsItem *, QPointF> movingItemsInitialPositions;
+ void _q_updateLater();
+ void _q_polishItems();
+
+ void _q_resetDirtyItems();
+ void resetDirtyItemsLater();
+ bool dirtyItemResetPending;
+
+ QList<int> freeItemIndexes;
+ bool regenerateIndex;
+
+ bool purgePending;
+ void _q_removeItemLater(QGraphicsItem *item);
+ QSet<QGraphicsItem *> removedItems;
+ void purgeRemovedItems();
+
+ QBrush backgroundBrush;
+ QBrush foregroundBrush;
+
+ int indexTimerId;
+ bool restartIndexTimer;
+ void startIndexTimer();
+
+ bool stickyFocus;
+ bool hasFocus;
+ QGraphicsItem *focusItem;
+ QGraphicsItem *lastFocusItem;
+ QGraphicsWidget *tabFocusFirst;
+ QGraphicsWidget *activeWindow;
+ int activationRefCount;
+
+ QList<QGraphicsWidget *> popupWidgets;
+ void addPopup(QGraphicsWidget *widget);
+ void removePopup(QGraphicsWidget *widget, bool itemIsDying = false);
+
+ QGraphicsItem *lastMouseGrabberItem;
+ bool lastMouseGrabberItemHasImplicitMouseGrab;
+ QList<QGraphicsItem *> mouseGrabberItems;
+ void grabMouse(QGraphicsItem *item, bool implicit = false);
+ void ungrabMouse(QGraphicsItem *item, bool itemIsDying = false);
+ void clearMouseGrabber();
+
+ QList<QGraphicsItem *> keyboardGrabberItems;
+ void grabKeyboard(QGraphicsItem *item);
+ void ungrabKeyboard(QGraphicsItem *item, bool itemIsDying = false);
+ void clearKeyboardGrabber();
+
+ QGraphicsItem *dragDropItem;
+ QGraphicsWidget *enterWidget;
+ Qt::DropAction lastDropAction;
+ QList<QGraphicsItem *> cachedItemsUnderMouse;
+ QList<QGraphicsItem *> hoverItems;
+ QMap<Qt::MouseButton, QPointF> mouseGrabberButtonDownPos;
+ QMap<Qt::MouseButton, QPointF> mouseGrabberButtonDownScenePos;
+ QMap<Qt::MouseButton, QPoint> mouseGrabberButtonDownScreenPos;
+ QList<QGraphicsItem *> itemsAtPosition(const QPoint &screenPos,
+ const QPointF &scenePos,
+ QWidget *widget) const;
+ static bool itemCollidesWithPath(QGraphicsItem *item, const QPainterPath &path, Qt::ItemSelectionMode mode);
+ void storeMouseButtonsForMouseGrabber(QGraphicsSceneMouseEvent *event);
+
+ QList<QGraphicsView *> views;
+ bool painterStateProtection;
+
+ QMultiMap<QGraphicsItem *, QGraphicsItem *> sceneEventFilters;
+ void installSceneEventFilter(QGraphicsItem *watched, QGraphicsItem *filter);
+ void removeSceneEventFilter(QGraphicsItem *watched, QGraphicsItem *filter);
+ bool filterEvent(QGraphicsItem *item, QEvent *event);
+ bool sendEvent(QGraphicsItem *item, QEvent *event);
+
+ bool dispatchHoverEvent(QGraphicsSceneHoverEvent *hoverEvent);
+ bool itemAcceptsHoverEvents_helper(const QGraphicsItem *item) const;
+ void leaveScene();
+
+ void cloneDragDropEvent(QGraphicsSceneDragDropEvent *dest,
+ QGraphicsSceneDragDropEvent *source);
+ void sendDragDropEvent(QGraphicsItem *item,
+ QGraphicsSceneDragDropEvent *dragDropEvent);
+ void sendHoverEvent(QEvent::Type type, QGraphicsItem *item,
+ QGraphicsSceneHoverEvent *hoverEvent);
+ void sendMouseEvent(QGraphicsSceneMouseEvent *mouseEvent);
+ void mousePressEventHandler(QGraphicsSceneMouseEvent *mouseEvent);
+ QGraphicsWidget *windowForItem(const QGraphicsItem *item) const;
+
+ QList<QGraphicsItem *> items_helper(const QRectF &rect,
+ Qt::ItemSelectionMode mode,
+ Qt::SortOrder order) const;
+ QList<QGraphicsItem *> items_helper(const QPolygonF &rect,
+ Qt::ItemSelectionMode mode,
+ Qt::SortOrder order) const;
+ QList<QGraphicsItem *> items_helper(const QPainterPath &rect,
+ Qt::ItemSelectionMode mode,
+ Qt::SortOrder order) const;
+ void childItems_helper(QList<QGraphicsItem *> *items,
+ const QGraphicsItem *parent,
+ const QRectF &rect,
+ Qt::ItemSelectionMode mode) const;
+ void childItems_helper(QList<QGraphicsItem *> *items,
+ const QGraphicsItem *parent,
+ const QPolygonF &polygon,
+ Qt::ItemSelectionMode mode) const;
+ void childItems_helper(QList<QGraphicsItem *> *items,
+ const QGraphicsItem *parent,
+ const QPainterPath &path,
+ Qt::ItemSelectionMode mode) const;
+
+ bool sortCacheEnabled;
+ bool updatingSortCache;
+ void invalidateSortCache();
+ static void climbTree(QGraphicsItem *item, int *stackingOrder);
+ void _q_updateSortCache();
+
+ static bool closestItemFirst_withoutCache(const QGraphicsItem *item1, const QGraphicsItem *item2);
+ static bool closestItemLast_withoutCache(const QGraphicsItem *item1, const QGraphicsItem *item2);
+
+ static inline bool closestItemFirst_withCache(const QGraphicsItem *item1, const QGraphicsItem *item2)
+ {
+ return item1->d_ptr->globalStackingOrder < item2->d_ptr->globalStackingOrder;
+ }
+ static inline bool closestItemLast_withCache(const QGraphicsItem *item1, const QGraphicsItem *item2)
+ {
+ return item1->d_ptr->globalStackingOrder >= item2->d_ptr->globalStackingOrder;
+ }
+
+ static void sortItems(QList<QGraphicsItem *> *itemList, Qt::SortOrder order, bool cached);
+
+ void drawItemHelper(QGraphicsItem *item, QPainter *painter,
+ const QStyleOptionGraphicsItem *option, QWidget *widget,
+ bool painterStateProtection);
+
+ QStyle *style;
+ QFont font;
+ void setFont_helper(const QFont &font);
+ void resolveFont();
+ void updateFont(const QFont &font);
+ QPalette palette;
+ void setPalette_helper(const QPalette &palette);
+ void resolvePalette();
+ void updatePalette(const QPalette &palette);
+
+ mutable QVector<QTransform> sceneTransformCache;
+ mutable QBitArray validTransforms;
+ mutable QVector<int> freeSceneTransformSlots;
+};
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_GRAPHICSVIEW
+
+#endif
diff --git a/src/gui/graphicsview/qgraphicssceneevent.cpp b/src/gui/graphicsview/qgraphicssceneevent.cpp
new file mode 100644
index 0000000000..b819c2c82e
--- /dev/null
+++ b/src/gui/graphicsview/qgraphicssceneevent.cpp
@@ -0,0 +1,1678 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*!
+ \class QGraphicsSceneEvent
+ \brief The QGraphicsSceneEvent class provides a base class for all
+ graphics view related events.
+ \since 4.2
+ \ingroup multimedia
+ \ingroup graphicsview-api
+
+ When a QGraphicsView receives Qt mouse, keyboard, and drag and
+ drop events (QMouseEvent, QKeyEvent, QDragEvent, etc.), it
+ translates them into instances of QGraphicsSceneEvent subclasses
+ and forwards them to the QGraphicsScene it displays. The scene
+ then forwards the events to the relevant items.
+
+ For example, when a QGraphicsView receives a QMouseEvent of type
+ MousePress as a response to a user click, the view sends a
+ QGraphicsSceneMouseEvent of type GraphicsSceneMousePress to the
+ underlying QGraphicsScene through its
+ \l{QGraphicsScene::}{mousePressEvent()} function. The default
+ QGraphicsScene::mousePressEvent() implementation determines which
+ item was clicked and forwards the event to
+ QGraphicsItem::mousePressEvent().
+
+ \omit ### Beskrive widget() \endomit
+
+ Subclasses such as QGraphicsSceneMouseEvent and
+ QGraphicsSceneContextMenuEvent provide the coordinates from the
+ original QEvent in screen, scene, and item coordinates (see
+ \l{QGraphicsSceneMouseEvent::}{screenPos()},
+ \l{QGraphicsSceneMouseEvent::}{scenePos()}, and
+ \l{QGraphicsSceneMouseEvent::}{pos()}). The item coordinates are
+ set by the QGraphicsScene before it forwards the event to the
+ event to a QGraphicsItem. The mouse events also add the
+ possibility to retrieve the coordinates from the last event
+ received by the view (see
+ \l{QGraphicsSceneMouseEvent::}{lastScreenPos()},
+ \l{QGraphicsSceneMouseEvent::}{lastScenePos()}, and
+ \l{QGraphicsSceneMouseEvent::}{lastPos()}).
+
+ \sa QEvent
+*/
+
+/*!
+ \class QGraphicsSceneMouseEvent
+ \brief The QGraphicsSceneMouseEvent class provides mouse events
+ in the graphics view framework.
+ \since 4.2
+ \ingroup multimedia
+ \ingroup graphicsview-api
+
+ When a QGraphicsView receives a QMouseEvent, it translates it to
+ a QGraphicsSceneMouseEvent. The event is then forwarded to the
+ QGraphicsScene associated with the view.
+
+ In addition to containing the item, scene, and screen coordinates
+ of the event (as pos(), scenePos(), and screenPos()), mouse
+ events also contain the coordinates of the previous mouse
+ event received by the view. These can be retrieved with
+ lastPos(), lastScreenPos(), and lastScenePos().
+
+ \sa QGraphicsSceneContextMenuEvent,
+ QGraphicsSceneHoverEvent, QGraphicsSceneWheelEvent,
+ QMouseEvent
+*/
+
+/*!
+ \class QGraphicsSceneWheelEvent
+ \brief The QGraphicsSceneWheelEvent class provides wheel events
+ in the graphics view framework.
+ \brief The QGraphicsSceneWheelEvent class provides wheel events in the
+ graphics view framework.
+ \since 4.2
+ \ingroup multimedia
+ \ingroup graphicsview-api
+
+ \l{QWheelEvent}{QWheelEvent}s received by a QGraphicsView are translated
+ into QGraphicsSceneWheelEvents; it translates the QWheelEvent::globalPos()
+ into item, scene, and screen coordinates (pos(), scenePos(), and
+ screenPos()).
+
+ \sa QGraphicsSceneMouseEvent, QGraphicsSceneContextMenuEvent,
+ QGraphicsSceneHoverEvent, QWheelEvent
+*/
+
+/*!
+ \class QGraphicsSceneContextMenuEvent
+ \brief The QGraphicsSceneContextMenuEvent class provides context
+ menu events in the graphics view framework.
+ \since 4.2
+ \ingroup multimedia
+ \ingroup graphicsview-api
+
+ A QContextMenuEvent received by a QGraphicsView is translated
+ into a QGraphicsSceneContextMenuEvent. The
+ QContextMenuEvent::globalPos() is translated into item, scene, and
+ screen coordinates (pos(), scenePos(), and screenPos()).
+
+ \sa QGraphicsSceneMouseEvent, QGraphicsSceneWheelEvent,
+ QContextMenuEvent
+*/
+
+/*!
+ \enum QGraphicsSceneContextMenuEvent::Reason
+
+ This enum describes the reason why the context event was sent.
+
+ \value Mouse The mouse caused the event to be sent. On most
+ platforms, this means the right mouse button was clicked.
+
+ \value Keyboard The keyboard caused this event to be sent. On
+ Windows and Mac OS X, this means the menu button was pressed.
+
+ \value Other The event was sent by some other means (i.e. not
+ by the mouse or keyboard).
+*/
+
+/*!
+ \class QGraphicsSceneHoverEvent
+ \brief The QGraphicsSceneHoverEvent class provides hover events
+ in the graphics view framework.
+ \since 4.2
+ \ingroup multimedia
+ \ingroup graphicsview-api
+
+ When a QGraphicsView receives a QHoverEvent event, it translates
+ it into QGraphicsSceneHoverEvent. The event is then forwarded to
+ the QGraphicsScene associated with the view.
+
+ \sa QGraphicsSceneMouseEvent, QGraphicsSceneContextMenuEvent,
+ QGraphicsSceneWheelEvent, QHoverEvent
+*/
+
+/*!
+ \class QGraphicsSceneHelpEvent
+ \brief The QGraphicsSceneHelpEvent class provides events when a
+ tooltip is requested.
+ \since 4.2
+ \ingroup multimedia
+ \ingroup graphicsview-api
+
+ When a QGraphicsView receives a QEvent of type
+ QEvent::ToolTip, it creates a QGraphicsSceneHelpEvent, which is
+ forwarded to the scene. You can set a tooltip on a QGraphicsItem
+ with \l{QGraphicsItem::}{setToolTip()}; by default QGraphicsScene
+ displays the tooltip of the QGraphicsItem with the highest
+ z-value (i.e, the top-most item) under the mouse position.
+
+ QGraphicsView does not forward events when
+ \l{QWhatsThis}{"What's This"} and \l{QStatusTipEvent}{status tip}
+ help is requested. If you need this, you can reimplement
+ QGraphicsView::viewportEvent() and forward QStatusTipEvent
+ events and \l{QEvent}{QEvents} of type QEvent::WhatsThis to the
+ scene.
+
+ \sa QEvent
+*/
+
+/*!
+ \class QGraphicsSceneDragDropEvent
+ \brief The QGraphicsSceneDragDropEvent class provides events for
+ drag and drop in the graphics view framework.
+ \since 4.2
+ \ingroup multimedia
+ \ingroup graphicsview-api
+
+ QGraphicsView inherits the drag and drop functionality provided
+ by QWidget. When it receives a drag and drop event, it translates
+ it to a QGraphicsSceneDragDropEvent.
+
+ QGraphicsSceneDragDropEvent stores events of type
+ GraphicsSceneDragEnter, GraphicsSceneDragLeave,
+ GraphicsSceneDragMove, or GraphicsSceneDrop.
+
+ QGraphicsSceneDragDropEvent contains the position of the mouse
+ cursor in both item, scene, and screen coordinates; this can be
+ retrieved with pos(), scenePos(), and screenPos().
+
+ The scene sends the event to the first QGraphicsItem under the
+ mouse cursor that accepts drops; a graphics item is set to accept
+ drops with \l{QGraphicsItem::}{setAcceptDrops()}.
+*/
+
+/*!
+ \class QGraphicsSceneResizeEvent
+ \brief The QGraphicsSceneResizeEvent class provides events for widget
+ resizing in the graphics view framework.
+ \since 4.4
+ \ingroup multimedia
+ \ingroup graphicsview-api
+
+ A QGraphicsWidget sends itself a QGraphicsSceneResizeEvent immediately
+ when its geometry changes.
+
+ It's similar to QResizeEvent, but its sizes, oldSize() and newSize(), use
+ QSizeF instead of QSize.
+
+ \sa QGraphicsWidget::setGeometry(), QGraphicsWidget::resize()
+*/
+
+/*!
+ \class QGraphicsSceneMoveEvent
+ \brief The QGraphicsSceneMoveEvent class provides events for widget
+ moving in the graphics view framework.
+ \since 4.4
+ \ingroup multimedia
+ \ingroup graphicsview-api
+
+ A QGraphicsWidget sends itself a QGraphicsSceneMoveEvent immediately when
+ its local position changes. The delivery is implemented as part of
+ QGraphicsItem::itemChange().
+
+ It's similar to QMoveEvent, but its positions, oldPos() and newPos(), use
+ QPointF instead of QPoint.
+
+ \sa QGraphicsItem::setPos(), QGraphicsItem::ItemPositionChange,
+ QGraphicsItem::ItemPositionHasChanged
+*/
+
+#include "qgraphicssceneevent.h"
+
+#ifndef QT_NO_GRAPHICSVIEW
+
+#ifndef QT_NO_DEBUG
+#include <QtCore/qdebug.h>
+#endif
+#include <QtCore/qmap.h>
+#include <QtCore/qpoint.h>
+#include <QtCore/qsize.h>
+#include <QtCore/qstring.h>
+
+QT_BEGIN_NAMESPACE
+
+class QGraphicsSceneEventPrivate
+{
+public:
+ inline QGraphicsSceneEventPrivate()
+ : widget(0),
+ q_ptr(0)
+ { }
+
+ inline virtual ~QGraphicsSceneEventPrivate()
+ { }
+
+ QWidget *widget;
+ QGraphicsSceneEvent *q_ptr;
+};
+
+/*!
+ \internal
+
+ Constructs a generic graphics scene event of the specified \a type.
+*/
+QGraphicsSceneEvent::QGraphicsSceneEvent(Type type)
+ : QEvent(type), d_ptr(new QGraphicsSceneEventPrivate)
+{
+ d_ptr->q_ptr = this;
+}
+
+/*!
+ \internal
+
+ Constructs a generic graphics scene event.
+*/
+QGraphicsSceneEvent::QGraphicsSceneEvent(QGraphicsSceneEventPrivate &dd, Type type)
+ : QEvent(type), d_ptr(&dd)
+{
+ d_ptr->q_ptr = this;
+}
+
+/*!
+ Destroys the event.
+*/
+QGraphicsSceneEvent::~QGraphicsSceneEvent()
+{
+ delete d_ptr;
+}
+
+/*!
+ Returns the widget where the event originated, or 0 if the event
+ originates from another application.
+*/
+QWidget *QGraphicsSceneEvent::widget() const
+{
+ return d_ptr->widget;
+}
+
+/*!
+ \internal
+
+ Sets the \a widget related to this event.
+
+ \sa widget()
+*/
+void QGraphicsSceneEvent::setWidget(QWidget *widget)
+{
+ d_ptr->widget = widget;
+}
+
+class QGraphicsSceneMouseEventPrivate : public QGraphicsSceneEventPrivate
+{
+ Q_DECLARE_PUBLIC(QGraphicsSceneMouseEvent)
+public:
+ inline QGraphicsSceneMouseEventPrivate()
+ : button(Qt::NoButton),
+ buttons(0), modifiers(0)
+ { }
+
+ QPointF pos;
+ QPointF scenePos;
+ QPoint screenPos;
+ QPointF lastPos;
+ QPointF lastScenePos;
+ QPoint lastScreenPos;
+ QMap<Qt::MouseButton, QPointF> buttonDownPos;
+ QMap<Qt::MouseButton, QPointF> buttonDownScenePos;
+ QMap<Qt::MouseButton, QPoint> buttonDownScreenPos;
+ Qt::MouseButton button;
+ Qt::MouseButtons buttons;
+ Qt::KeyboardModifiers modifiers;
+};
+
+/*!
+ \internal
+
+ Constructs a generic graphics scene mouse event of the specified \a type.
+*/
+QGraphicsSceneMouseEvent::QGraphicsSceneMouseEvent(Type type)
+ : QGraphicsSceneEvent(*new QGraphicsSceneMouseEventPrivate, type)
+{
+}
+
+/*!
+ Destroys the event.
+*/
+QGraphicsSceneMouseEvent::~QGraphicsSceneMouseEvent()
+{
+}
+
+/*!
+ Returns the mouse cursor position in item coordinates.
+
+ \sa scenePos(), screenPos(), lastPos()
+*/
+QPointF QGraphicsSceneMouseEvent::pos() const
+{
+ Q_D(const QGraphicsSceneMouseEvent);
+ return d->pos;
+}
+
+/*!
+ \internal
+*/
+void QGraphicsSceneMouseEvent::setPos(const QPointF &pos)
+{
+ Q_D(QGraphicsSceneMouseEvent);
+ d->pos = pos;
+}
+
+/*!
+ Returns the mouse cursor position in scene coordinates.
+
+ \sa pos(), screenPos(), lastScenePos()
+*/
+QPointF QGraphicsSceneMouseEvent::scenePos() const
+{
+ Q_D(const QGraphicsSceneMouseEvent);
+ return d->scenePos;
+}
+
+/*!
+ \internal
+*/
+void QGraphicsSceneMouseEvent::setScenePos(const QPointF &pos)
+{
+ Q_D(QGraphicsSceneMouseEvent);
+ d->scenePos = pos;
+}
+
+/*!
+ Returns the mouse cursor position in screen coordinates.
+
+ \sa pos(), scenePos(), lastScreenPos()
+*/
+QPoint QGraphicsSceneMouseEvent::screenPos() const
+{
+ Q_D(const QGraphicsSceneMouseEvent);
+ return d->screenPos;
+}
+
+/*!
+ \internal
+*/
+void QGraphicsSceneMouseEvent::setScreenPos(const QPoint &pos)
+{
+ Q_D(QGraphicsSceneMouseEvent);
+ d->screenPos = pos;
+}
+
+/*!
+ Returns the mouse cursor position in item coordinates where the specified
+ \a button was clicked.
+
+ \sa buttonDownScenePos(), buttonDownScreenPos(), pos()
+*/
+QPointF QGraphicsSceneMouseEvent::buttonDownPos(Qt::MouseButton button) const
+{
+ Q_D(const QGraphicsSceneMouseEvent);
+ return d->buttonDownPos.value(button);
+}
+
+/*!
+ \internal
+*/
+void QGraphicsSceneMouseEvent::setButtonDownPos(Qt::MouseButton button, const QPointF &pos)
+{
+ Q_D(QGraphicsSceneMouseEvent);
+ d->buttonDownPos.insert(button, pos);
+}
+
+/*!
+ Returns the mouse cursor position in scene coordinates where the
+ specified \a button was clicked.
+
+ \sa buttonDownPos(), buttonDownScreenPos(), scenePos()
+*/
+QPointF QGraphicsSceneMouseEvent::buttonDownScenePos(Qt::MouseButton button) const
+{
+ Q_D(const QGraphicsSceneMouseEvent);
+ return d->buttonDownScenePos.value(button);
+}
+
+/*!
+ \internal
+*/
+void QGraphicsSceneMouseEvent::setButtonDownScenePos(Qt::MouseButton button, const QPointF &pos)
+{
+ Q_D(QGraphicsSceneMouseEvent);
+ d->buttonDownScenePos.insert(button, pos);
+}
+
+/*!
+ Returns the mouse cursor position in screen coordinates where the
+ specified \a button was clicked.
+
+ \sa screenPos(), buttonDownPos(), buttonDownScenePos()
+*/
+QPoint QGraphicsSceneMouseEvent::buttonDownScreenPos(Qt::MouseButton button) const
+{
+ Q_D(const QGraphicsSceneMouseEvent);
+ return d->buttonDownScreenPos.value(button);
+}
+
+/*!
+ \internal
+*/
+void QGraphicsSceneMouseEvent::setButtonDownScreenPos(Qt::MouseButton button, const QPoint &pos)
+{
+ Q_D(QGraphicsSceneMouseEvent);
+ d->buttonDownScreenPos.insert(button, pos);
+}
+
+/*!
+ Returns the last recorded mouse cursor position in item
+ coordinates.
+
+ \sa lastScenePos(), lastScreenPos(), pos()
+*/
+QPointF QGraphicsSceneMouseEvent::lastPos() const
+{
+ Q_D(const QGraphicsSceneMouseEvent);
+ return d->lastPos;
+}
+
+/*!
+ \internal
+*/
+void QGraphicsSceneMouseEvent::setLastPos(const QPointF &pos)
+{
+ Q_D(QGraphicsSceneMouseEvent);
+ d->lastPos = pos;
+}
+
+/*!
+ Returns the last recorded mouse cursor position in scene
+ coordinates. The last recorded position is the position of
+ the previous mouse event received by the view that created
+ the event.
+
+ \sa lastPos(), lastScreenPos(), scenePos()
+*/
+QPointF QGraphicsSceneMouseEvent::lastScenePos() const
+{
+ Q_D(const QGraphicsSceneMouseEvent);
+ return d->lastScenePos;
+}
+
+/*!
+ \internal
+*/
+void QGraphicsSceneMouseEvent::setLastScenePos(const QPointF &pos)
+{
+ Q_D(QGraphicsSceneMouseEvent);
+ d->lastScenePos = pos;
+}
+
+/*!
+ Returns the last recorded mouse cursor position in screen
+ coordinates. The last recorded position is the position of
+ the previous mouse event received by the view that created
+ the event.
+
+ \sa lastPos(), lastScenePos(), screenPos()
+*/
+QPoint QGraphicsSceneMouseEvent::lastScreenPos() const
+{
+ Q_D(const QGraphicsSceneMouseEvent);
+ return d->lastScreenPos;
+}
+
+/*!
+ \internal
+*/
+void QGraphicsSceneMouseEvent::setLastScreenPos(const QPoint &pos)
+{
+ Q_D(QGraphicsSceneMouseEvent);
+ d->lastScreenPos = pos;
+}
+
+/*!
+ Returns the combination of mouse buttons that were pressed at the
+ time the event was sent.
+
+ \sa button(), modifiers()
+*/
+Qt::MouseButtons QGraphicsSceneMouseEvent::buttons() const
+{
+ Q_D(const QGraphicsSceneMouseEvent);
+ return d->buttons;
+}
+
+/*!
+ \internal
+*/
+void QGraphicsSceneMouseEvent::setButtons(Qt::MouseButtons buttons)
+{
+ Q_D(QGraphicsSceneMouseEvent);
+ d->buttons = buttons;
+}
+
+/*!
+ Returns the mouse button (if any) that caused the event.
+
+ \sa buttons(), modifiers()
+*/
+Qt::MouseButton QGraphicsSceneMouseEvent::button() const
+{
+ Q_D(const QGraphicsSceneMouseEvent);
+ return d->button;
+}
+
+/*!
+ \internal
+*/
+void QGraphicsSceneMouseEvent::setButton(Qt::MouseButton button)
+{
+ Q_D(QGraphicsSceneMouseEvent);
+ d->button = button;
+}
+
+/*!
+ Returns the keyboard modifiers in use at the time the event was
+ sent.
+
+ \sa buttons(), button()
+*/
+Qt::KeyboardModifiers QGraphicsSceneMouseEvent::modifiers() const
+{
+ Q_D(const QGraphicsSceneMouseEvent);
+ return d->modifiers;
+}
+
+/*!
+ \internal
+*/
+void QGraphicsSceneMouseEvent::setModifiers(Qt::KeyboardModifiers modifiers)
+{
+ Q_D(QGraphicsSceneMouseEvent);
+ d->modifiers = modifiers;
+}
+
+class QGraphicsSceneWheelEventPrivate : public QGraphicsSceneEventPrivate
+{
+ Q_DECLARE_PUBLIC(QGraphicsSceneWheelEvent)
+public:
+ inline QGraphicsSceneWheelEventPrivate()
+ : buttons(0), modifiers(0), delta(0), orientation(Qt::Horizontal)
+ { }
+
+ QPointF pos;
+ QPointF scenePos;
+ QPoint screenPos;
+ Qt::MouseButtons buttons;
+ Qt::KeyboardModifiers modifiers;
+ int delta;
+ Qt::Orientation orientation;
+};
+
+/*!
+ \internal
+
+ Constructs a QGraphicsSceneWheelEvent of type \a type, which
+ is always QEvent::GraphicsSceneWheel.
+*/
+QGraphicsSceneWheelEvent::QGraphicsSceneWheelEvent(Type type)
+ : QGraphicsSceneEvent(*new QGraphicsSceneWheelEventPrivate, type)
+{
+}
+
+/*!
+ Destroys the QGraphicsSceneWheelEvent.
+*/
+QGraphicsSceneWheelEvent::~QGraphicsSceneWheelEvent()
+{
+}
+
+/*!
+ Returns the position of the cursor in item coordinates when the
+ wheel event occurred.
+
+ \sa scenePos(), screenPos()
+*/
+QPointF QGraphicsSceneWheelEvent::pos() const
+{
+ Q_D(const QGraphicsSceneWheelEvent);
+ return d->pos;
+}
+
+/*!
+ \internal
+*/
+void QGraphicsSceneWheelEvent::setPos(const QPointF &pos)
+{
+ Q_D(QGraphicsSceneWheelEvent);
+ d->pos = pos;
+}
+
+/*!
+ Returns the position of the cursor in scene coordinates when the wheel
+ event occurred.
+
+ \sa pos(), screenPos()
+*/
+QPointF QGraphicsSceneWheelEvent::scenePos() const
+{
+ Q_D(const QGraphicsSceneWheelEvent);
+ return d->scenePos;
+}
+
+/*!
+ \internal
+*/
+void QGraphicsSceneWheelEvent::setScenePos(const QPointF &pos)
+{
+ Q_D(QGraphicsSceneWheelEvent);
+ d->scenePos = pos;
+}
+
+/*!
+ Returns the position of the cursor in screen coordinates when the wheel
+ event occurred.
+
+ \sa pos(), scenePos()
+*/
+QPoint QGraphicsSceneWheelEvent::screenPos() const
+{
+ Q_D(const QGraphicsSceneWheelEvent);
+ return d->screenPos;
+}
+
+/*!
+ \internal
+*/
+void QGraphicsSceneWheelEvent::setScreenPos(const QPoint &pos)
+{
+ Q_D(QGraphicsSceneWheelEvent);
+ d->screenPos = pos;
+}
+
+/*!
+ Returns the mouse buttons that were pressed when the wheel event occurred.
+
+ \sa modifiers()
+*/
+Qt::MouseButtons QGraphicsSceneWheelEvent::buttons() const
+{
+ Q_D(const QGraphicsSceneWheelEvent);
+ return d->buttons;
+}
+
+/*!
+ \internal
+*/
+void QGraphicsSceneWheelEvent::setButtons(Qt::MouseButtons buttons)
+{
+ Q_D(QGraphicsSceneWheelEvent);
+ d->buttons = buttons;
+}
+
+/*!
+ Returns the keyboard modifiers that were active when the wheel event
+ occurred.
+
+ \sa buttons()
+*/
+Qt::KeyboardModifiers QGraphicsSceneWheelEvent::modifiers() const
+{
+ Q_D(const QGraphicsSceneWheelEvent);
+ return d->modifiers;
+}
+
+/*!
+ \internal
+*/
+void QGraphicsSceneWheelEvent::setModifiers(Qt::KeyboardModifiers modifiers)
+{
+ Q_D(QGraphicsSceneWheelEvent);
+ d->modifiers = modifiers;
+}
+
+/*!
+ Returns the distance that the wheel is rotated, in eighths (1/8s)
+ of a degree. A positive value indicates that the wheel was
+ rotated forwards away from the user; a negative value indicates
+ that the wheel was rotated backwards toward the user.
+
+ Most mouse types work in steps of 15 degrees, in which case the delta
+ value is a multiple of 120 (== 15 * 8).
+*/
+int QGraphicsSceneWheelEvent::delta() const
+{
+ Q_D(const QGraphicsSceneWheelEvent);
+ return d->delta;
+}
+
+/*!
+ \internal
+*/
+void QGraphicsSceneWheelEvent::setDelta(int delta)
+{
+ Q_D(QGraphicsSceneWheelEvent);
+ d->delta = delta;
+}
+
+/*!
+ Returns the wheel orientation.
+*/
+Qt::Orientation QGraphicsSceneWheelEvent::orientation() const
+{
+ Q_D(const QGraphicsSceneWheelEvent);
+ return d->orientation;
+}
+
+/*!
+ \internal
+*/
+void QGraphicsSceneWheelEvent::setOrientation(Qt::Orientation orientation)
+{
+ Q_D(QGraphicsSceneWheelEvent);
+ d->orientation = orientation;
+}
+
+class QGraphicsSceneContextMenuEventPrivate : public QGraphicsSceneEventPrivate
+{
+ Q_DECLARE_PUBLIC(QGraphicsSceneContextMenuEvent)
+ public:
+ inline QGraphicsSceneContextMenuEventPrivate()
+ : modifiers(0), reason(QGraphicsSceneContextMenuEvent::Other)
+ { }
+
+ QPointF pos;
+ QPointF scenePos;
+ QPoint screenPos;
+ Qt::KeyboardModifiers modifiers;
+ QGraphicsSceneContextMenuEvent::Reason reason;
+};
+
+/*!
+ \internal
+
+ Constructs a graphics scene context menu event of the specified \a type.
+*/
+QGraphicsSceneContextMenuEvent::QGraphicsSceneContextMenuEvent(Type type)
+ : QGraphicsSceneEvent(*new QGraphicsSceneContextMenuEventPrivate, type)
+{
+}
+
+/*!
+ Destroys the event.
+*/
+QGraphicsSceneContextMenuEvent::~QGraphicsSceneContextMenuEvent()
+{
+}
+
+/*!
+ Returns the position of the mouse cursor in item coordinates at the moment
+ the the context menu was requested.
+
+ \sa scenePos(), screenPos()
+*/
+QPointF QGraphicsSceneContextMenuEvent::pos() const
+{
+ Q_D(const QGraphicsSceneContextMenuEvent);
+ return d->pos;
+}
+
+/*!
+ \fn void QGraphicsSceneContextMenuEvent::setPos(const QPointF &point)
+ \internal
+
+ Sets the position associated with the context menu to the given \a point
+ in item coordinates.
+*/
+void QGraphicsSceneContextMenuEvent::setPos(const QPointF &pos)
+{
+ Q_D(QGraphicsSceneContextMenuEvent);
+ d->pos = pos;
+}
+
+/*!
+ Returns the position of the mouse cursor in scene coordinates at the moment the
+ the context menu was requested.
+
+ \sa pos(), screenPos()
+*/
+QPointF QGraphicsSceneContextMenuEvent::scenePos() const
+{
+ Q_D(const QGraphicsSceneContextMenuEvent);
+ return d->scenePos;
+}
+
+/*!
+ \fn void QGraphicsSceneContextMenuEvent::setScenePos(const QPointF &point)
+ \internal
+
+ Sets the position associated with the context menu to the given \a point
+ in scene coordinates.
+*/
+void QGraphicsSceneContextMenuEvent::setScenePos(const QPointF &pos)
+{
+ Q_D(QGraphicsSceneContextMenuEvent);
+ d->scenePos = pos;
+}
+
+/*!
+ Returns the position of the mouse cursor in screen coordinates at the moment the
+ the context menu was requested.
+
+ \sa pos(), scenePos()
+*/
+QPoint QGraphicsSceneContextMenuEvent::screenPos() const
+{
+ Q_D(const QGraphicsSceneContextMenuEvent);
+ return d->screenPos;
+}
+
+/*!
+ \fn void QGraphicsSceneContextMenuEvent::setScreenPos(const QPoint &point)
+ \internal
+
+ Sets the position associated with the context menu to the given \a point
+ in screen coordinates.
+*/
+void QGraphicsSceneContextMenuEvent::setScreenPos(const QPoint &pos)
+{
+ Q_D(QGraphicsSceneContextMenuEvent);
+ d->screenPos = pos;
+}
+
+/*!
+ Returns the keyboard modifiers in use when the context menu was requested.
+*/
+Qt::KeyboardModifiers QGraphicsSceneContextMenuEvent::modifiers() const
+{
+ Q_D(const QGraphicsSceneContextMenuEvent);
+ return d->modifiers;
+}
+
+/*!
+ \internal
+
+ Sets the keyboard modifiers associated with the context menu to the \a
+ modifiers specified.
+*/
+void QGraphicsSceneContextMenuEvent::setModifiers(Qt::KeyboardModifiers modifiers)
+{
+ Q_D(QGraphicsSceneContextMenuEvent);
+ d->modifiers = modifiers;
+}
+
+/*!
+ Returns the reason for the context menu event.
+
+ \sa QGraphicsSceneContextMenuEvent::Reason
+*/
+QGraphicsSceneContextMenuEvent::Reason QGraphicsSceneContextMenuEvent::reason() const
+{
+ Q_D(const QGraphicsSceneContextMenuEvent);
+ return d->reason;
+}
+
+/*!
+ \internal
+ Sets the reason for the context menu event to \a reason.
+
+ \sa reason()
+*/
+void QGraphicsSceneContextMenuEvent::setReason(Reason reason)
+{
+ Q_D(QGraphicsSceneContextMenuEvent);
+ d->reason = reason;
+}
+
+class QGraphicsSceneHoverEventPrivate : public QGraphicsSceneEventPrivate
+{
+public:
+ QPointF pos;
+ QPointF scenePos;
+ QPoint screenPos;
+ QPointF lastPos;
+ QPointF lastScenePos;
+ QPoint lastScreenPos;
+ Qt::KeyboardModifiers modifiers;
+};
+
+/*!
+ \internal
+
+ Constructs a graphics scene hover event of the specified \a type.
+*/
+QGraphicsSceneHoverEvent::QGraphicsSceneHoverEvent(Type type)
+ : QGraphicsSceneEvent(*new QGraphicsSceneHoverEventPrivate, type)
+{
+}
+
+/*!
+ Destroys the event.
+*/
+QGraphicsSceneHoverEvent::~QGraphicsSceneHoverEvent()
+{
+}
+
+/*!
+ Returns the position of the mouse cursor in item coordinates at the moment
+ the the hover event was sent.
+
+ \sa scenePos(), screenPos()
+*/
+QPointF QGraphicsSceneHoverEvent::pos() const
+{
+ Q_D(const QGraphicsSceneHoverEvent);
+ return d->pos;
+}
+
+/*!
+ \fn void QGraphicsSceneHoverEvent::setPos(const QPointF &point)
+ \internal
+
+ Sets the position associated with the hover event to the given \a point in
+ item coordinates.
+*/
+void QGraphicsSceneHoverEvent::setPos(const QPointF &pos)
+{
+ Q_D(QGraphicsSceneHoverEvent);
+ d->pos = pos;
+}
+
+/*!
+ Returns the position of the mouse cursor in scene coordinates at the
+ moment the the hover event was sent.
+
+ \sa pos(), screenPos()
+*/
+QPointF QGraphicsSceneHoverEvent::scenePos() const
+{
+ Q_D(const QGraphicsSceneHoverEvent);
+ return d->scenePos;
+}
+
+/*!
+ \fn void QGraphicsSceneHoverEvent::setScenePos(const QPointF &point)
+ \internal
+
+ Sets the position associated with the hover event to the given \a point in
+ scene coordinates.
+*/
+void QGraphicsSceneHoverEvent::setScenePos(const QPointF &pos)
+{
+ Q_D(QGraphicsSceneHoverEvent);
+ d->scenePos = pos;
+}
+
+/*!
+ Returns the position of the mouse cursor in screen coordinates at the
+ moment the the hover event was sent.
+
+ \sa pos(), scenePos()
+*/
+QPoint QGraphicsSceneHoverEvent::screenPos() const
+{
+ Q_D(const QGraphicsSceneHoverEvent);
+ return d->screenPos;
+}
+
+/*!
+ \fn void QGraphicsSceneHoverEvent::setScreenPos(const QPoint &point)
+ \internal
+
+ Sets the position associated with the hover event to the given \a point in
+ screen coordinates.
+*/
+void QGraphicsSceneHoverEvent::setScreenPos(const QPoint &pos)
+{
+ Q_D(QGraphicsSceneHoverEvent);
+ d->screenPos = pos;
+}
+
+/*!
+ \since 4.4
+
+ Returns the last recorded mouse cursor position in item coordinates.
+
+ \sa lastScenePos(), lastScreenPos(), pos()
+*/
+QPointF QGraphicsSceneHoverEvent::lastPos() const
+{
+ Q_D(const QGraphicsSceneHoverEvent);
+ return d->lastPos;
+}
+
+/*!
+ \internal
+*/
+void QGraphicsSceneHoverEvent::setLastPos(const QPointF &pos)
+{
+ Q_D(QGraphicsSceneHoverEvent);
+ d->lastPos = pos;
+}
+
+/*!
+ \since 4.4
+
+ Returns the last recorded, the scene coordinates of the previous mouse or
+ hover event received by the view, that created the event mouse cursor
+ position in scene coordinates.
+
+ \sa lastPos(), lastScreenPos(), scenePos()
+*/
+QPointF QGraphicsSceneHoverEvent::lastScenePos() const
+{
+ Q_D(const QGraphicsSceneHoverEvent);
+ return d->lastScenePos;
+}
+
+/*!
+ \internal
+*/
+void QGraphicsSceneHoverEvent::setLastScenePos(const QPointF &pos)
+{
+ Q_D(QGraphicsSceneHoverEvent);
+ d->lastScenePos = pos;
+}
+
+/*!
+ \since 4.4
+
+ Returns the last recorded mouse cursor position in screen coordinates. The
+ last recorded position is the position of the previous mouse or hover
+ event received by the view that created the event.
+
+ \sa lastPos(), lastScenePos(), screenPos()
+*/
+QPoint QGraphicsSceneHoverEvent::lastScreenPos() const
+{
+ Q_D(const QGraphicsSceneHoverEvent);
+ return d->lastScreenPos;
+}
+
+/*!
+ \internal
+*/
+void QGraphicsSceneHoverEvent::setLastScreenPos(const QPoint &pos)
+{
+ Q_D(QGraphicsSceneHoverEvent);
+ d->lastScreenPos = pos;
+}
+
+/*!
+ \since 4.4
+
+ Returns the keyboard modifiers at the moment the the hover event was sent.
+*/
+Qt::KeyboardModifiers QGraphicsSceneHoverEvent::modifiers() const
+{
+ Q_D(const QGraphicsSceneHoverEvent);
+ return d->modifiers;
+}
+
+/*!
+ \fn void QGraphicsSceneHoverEvent::setModifiers(Qt::KeyboardModifiers modifiers)
+ \internal
+
+ Sets the modifiers for the current hover event to \a modifiers.
+*/
+void QGraphicsSceneHoverEvent::setModifiers(Qt::KeyboardModifiers modifiers)
+{
+ Q_D(QGraphicsSceneHoverEvent);
+ d->modifiers = modifiers;
+}
+
+class QGraphicsSceneHelpEventPrivate : public QGraphicsSceneEventPrivate
+{
+public:
+ QPointF scenePos;
+ QPoint screenPos;
+};
+
+/*!
+ \internal
+
+ Constructs a graphics scene help event of the specified \a type.
+*/
+QGraphicsSceneHelpEvent::QGraphicsSceneHelpEvent(Type type)
+ : QGraphicsSceneEvent(*new QGraphicsSceneHelpEventPrivate, type)
+{
+}
+
+/*!
+ Destroys the event.
+*/
+QGraphicsSceneHelpEvent::~QGraphicsSceneHelpEvent()
+{
+}
+
+/*!
+ Returns the position of the mouse cursor in scene coordinates at the
+ moment the the help event was sent.
+
+ \sa screenPos()
+*/
+QPointF QGraphicsSceneHelpEvent::scenePos() const
+{
+ Q_D(const QGraphicsSceneHelpEvent);
+ return d->scenePos;
+}
+
+/*!
+ \fn void QGraphicsSceneHelpEvent::setScenePos(const QPointF &point)
+ \internal
+
+ Sets the position associated with the context menu to the given \a point
+ in scene coordinates.
+*/
+void QGraphicsSceneHelpEvent::setScenePos(const QPointF &pos)
+{
+ Q_D(QGraphicsSceneHelpEvent);
+ d->scenePos = pos;
+}
+
+/*!
+ Returns the position of the mouse cursor in screen coordinates at the
+ moment the the help event was sent.
+
+ \sa scenePos()
+*/
+QPoint QGraphicsSceneHelpEvent::screenPos() const
+{
+ Q_D(const QGraphicsSceneHelpEvent);
+ return d->screenPos;
+}
+
+/*!
+ \fn void QGraphicsSceneHelpEvent::setScreenPos(const QPoint &point)
+ \internal
+
+ Sets the position associated with the context menu to the given \a point
+ in screen coordinates.
+*/
+void QGraphicsSceneHelpEvent::setScreenPos(const QPoint &pos)
+{
+ Q_D(QGraphicsSceneHelpEvent);
+ d->screenPos = pos;
+}
+
+class QGraphicsSceneDragDropEventPrivate : public QGraphicsSceneEventPrivate
+{
+ Q_DECLARE_PUBLIC(QGraphicsSceneDragDropEvent)
+public:
+ inline QGraphicsSceneDragDropEventPrivate()
+ : source(0), mimeData(0)
+ { }
+
+ QPointF pos;
+ QPointF scenePos;
+ QPoint screenPos;
+ Qt::MouseButtons buttons;
+ Qt::KeyboardModifiers modifiers;
+ Qt::DropActions possibleActions;
+ Qt::DropAction proposedAction;
+ Qt::DropAction dropAction;
+ QWidget *source;
+ const QMimeData *mimeData;
+};
+
+/*!
+ \internal
+
+ Constructs a new QGraphicsSceneDragDropEvent of the
+ specified \a type. The type can be either
+ QEvent::GraphicsSceneDragEnter, QEvent::GraphicsSceneDragLeave,
+ QEvent::GraphicsSceneDragMove, or QEvent::GraphicsSceneDrop.
+*/
+QGraphicsSceneDragDropEvent::QGraphicsSceneDragDropEvent(Type type)
+ : QGraphicsSceneEvent(*new QGraphicsSceneDragDropEventPrivate, type)
+{
+}
+
+/*!
+ Destroys the object.
+*/
+QGraphicsSceneDragDropEvent::~QGraphicsSceneDragDropEvent()
+{
+}
+
+/*!
+ Returns the mouse position of the event relative to the
+ view that sent the event.
+
+ \sa QGraphicsView, screenPos(), scenePos()
+*/
+QPointF QGraphicsSceneDragDropEvent::pos() const
+{
+ Q_D(const QGraphicsSceneDragDropEvent);
+ return d->pos;
+}
+
+/*!
+ \internal
+ Sets the position of the mouse to \a pos; this should be
+ relative to the widget that generated the event, which normally
+ is a QGraphicsView.
+
+ \sa pos(), setScenePos(), setScreenPos()
+*/
+
+void QGraphicsSceneDragDropEvent::setPos(const QPointF &pos)
+{
+ Q_D(QGraphicsSceneDragDropEvent);
+ d->pos = pos;
+}
+
+/*!
+ Returns the position of the mouse in scene coordinates.
+
+ \sa pos(), screenPos()
+*/
+QPointF QGraphicsSceneDragDropEvent::scenePos() const
+{
+ Q_D(const QGraphicsSceneDragDropEvent);
+ return d->scenePos;
+}
+
+/*!
+ \internal
+ Sets the scene position of the mouse to \a pos.
+
+ \sa scenePos(), setScreenPos(), setPos()
+*/
+void QGraphicsSceneDragDropEvent::setScenePos(const QPointF &pos)
+{
+ Q_D(QGraphicsSceneDragDropEvent);
+ d->scenePos = pos;
+}
+
+/*!
+ Returns the position of the mouse relative to the screen.
+
+ \sa pos(), scenePos()
+*/
+QPoint QGraphicsSceneDragDropEvent::screenPos() const
+{
+ Q_D(const QGraphicsSceneDragDropEvent);
+ return d->screenPos;
+}
+
+/*!
+ \internal
+ Sets the mouse position relative to the screen to \a pos.
+
+ \sa screenPos(), setScenePos(), setPos()
+*/
+void QGraphicsSceneDragDropEvent::setScreenPos(const QPoint &pos)
+{
+ Q_D(QGraphicsSceneDragDropEvent);
+ d->screenPos = pos;
+}
+
+/*!
+ Returns a Qt::MouseButtons value indicating which buttons
+ were pressed on the mouse when this mouse event was
+ generated.
+
+ \sa Qt::MouseButtons
+*/
+Qt::MouseButtons QGraphicsSceneDragDropEvent::buttons() const
+{
+ Q_D(const QGraphicsSceneDragDropEvent);
+ return d->buttons;
+}
+
+/*!
+ \internal
+ Sets the mouse buttons that were pressed when the event was
+ created to \a buttons.
+
+ \sa Qt::MouseButtons, buttons()
+*/
+void QGraphicsSceneDragDropEvent::setButtons(Qt::MouseButtons buttons)
+{
+ Q_D(QGraphicsSceneDragDropEvent);
+ d->buttons = buttons;
+}
+
+/*!
+ Returns the keyboard modifiers that were pressed when the drag
+ and drop event was created.
+
+ \sa Qt::KeyboardModifiers
+*/
+Qt::KeyboardModifiers QGraphicsSceneDragDropEvent::modifiers() const
+{
+ Q_D(const QGraphicsSceneDragDropEvent);
+ return d->modifiers;
+}
+
+/*!
+ \internal
+ Sets the keyboard modifiers that were pressed when the event
+ was created to \a modifiers.
+
+ \sa Qt::KeyboardModifiers, modifiers()
+*/
+
+void QGraphicsSceneDragDropEvent::setModifiers(Qt::KeyboardModifiers modifiers)
+{
+ Q_D(QGraphicsSceneDragDropEvent);
+ d->modifiers = modifiers;
+}
+
+/*!
+ Returns the possible drop actions that the drag and
+ drop can result in.
+
+ \sa Qt::DropActions
+*/
+
+Qt::DropActions QGraphicsSceneDragDropEvent::possibleActions() const
+{
+ Q_D(const QGraphicsSceneDragDropEvent);
+ return d->possibleActions;
+}
+
+/*!
+ \internal
+ Sets the possible drop actions that the drag can
+ result in to \a actions.
+
+ \sa Qt::DropActions, possibleActions()
+*/
+void QGraphicsSceneDragDropEvent::setPossibleActions(Qt::DropActions actions)
+{
+ Q_D(QGraphicsSceneDragDropEvent);
+ d->possibleActions = actions;
+}
+
+/*!
+ Returns the drop action that is proposed, i.e., preferred.
+ The action must be one of the possible actions as defined by
+ \c possibleActions().
+
+ \sa Qt::DropAction, possibleActions()
+*/
+
+Qt::DropAction QGraphicsSceneDragDropEvent::proposedAction() const
+{
+ Q_D(const QGraphicsSceneDragDropEvent);
+ return d->proposedAction;
+}
+
+/*!
+ \internal
+ Sets the proposed action to \a action. The proposed action
+ is a Qt::DropAction that is one of the possible actions as
+ given by \c possibleActions().
+
+ \sa proposedAction(), Qt::DropAction, possibleActions()
+*/
+
+void QGraphicsSceneDragDropEvent::setProposedAction(Qt::DropAction action)
+{
+ Q_D(QGraphicsSceneDragDropEvent);
+ d->proposedAction = action;
+}
+
+/*!
+ Sets the proposed action as accepted, i.e, the drop action
+ is set to the proposed action. This is equal to:
+
+ \snippet doc/src/snippets/code/src_gui_graphicsview_qgraphicssceneevent.cpp 0
+
+ When using this function, one should not call \c accept().
+
+ \sa dropAction(), setDropAction(), proposedAction()
+*/
+
+void QGraphicsSceneDragDropEvent::acceptProposedAction()
+{
+ Q_D(QGraphicsSceneDragDropEvent);
+ d->dropAction = d->proposedAction;
+}
+
+/*!
+ Returns the action that was performed in this drag and drop.
+ This should be set by the receiver of the drop and is
+ returned by QDrag::start().
+
+ \sa setDropAction(), acceptProposedAction()
+*/
+
+Qt::DropAction QGraphicsSceneDragDropEvent::dropAction() const
+{
+ Q_D(const QGraphicsSceneDragDropEvent);
+ return d->dropAction;
+}
+
+/*!
+ This function lets the receiver of the drop set the drop
+ action that was performed to \a action, which should be one
+ of the
+ \l{QGraphicsSceneDragDropEvent::possibleActions()}{possible
+ actions}. Call \c accept() in stead of \c
+ acceptProposedAction() if you use this function.
+
+ \sa dropAction(), accept(), possibleActions()
+*/
+void QGraphicsSceneDragDropEvent::setDropAction(Qt::DropAction action)
+{
+ Q_D(QGraphicsSceneDragDropEvent);
+ d->dropAction = action;
+}
+
+/*!
+ This function returns the QGraphicsView that created the
+ QGraphicsSceneDragDropEvent.
+*/
+QWidget *QGraphicsSceneDragDropEvent::source() const
+{
+ Q_D(const QGraphicsSceneDragDropEvent);
+ return d->source;
+}
+
+/*!
+ \internal
+ This function set the source widget, i.e., the widget that
+ created the drop event, to \a source.
+*/
+void QGraphicsSceneDragDropEvent::setSource(QWidget *source)
+{
+ Q_D(QGraphicsSceneDragDropEvent);
+ d->source = source;
+}
+
+/*!
+ This function returns the MIME data of the event.
+*/
+const QMimeData *QGraphicsSceneDragDropEvent::mimeData() const
+{
+ Q_D(const QGraphicsSceneDragDropEvent);
+ return d->mimeData;
+}
+
+/*!
+ \internal
+ This function sets the MIME data for the event.
+*/
+void QGraphicsSceneDragDropEvent::setMimeData(const QMimeData *data)
+{
+ Q_D(QGraphicsSceneDragDropEvent);
+ d->mimeData = data;
+}
+
+class QGraphicsSceneResizeEventPrivate : public QGraphicsSceneEventPrivate
+{
+ Q_DECLARE_PUBLIC(QGraphicsSceneResizeEvent)
+public:
+ inline QGraphicsSceneResizeEventPrivate()
+ { }
+
+ QSizeF oldSize;
+ QSizeF newSize;
+};
+
+/*!
+ Constructs a QGraphicsSceneResizeEvent.
+*/
+QGraphicsSceneResizeEvent::QGraphicsSceneResizeEvent()
+ : QGraphicsSceneEvent(*new QGraphicsSceneResizeEventPrivate, QEvent::GraphicsSceneResize)
+{
+}
+
+/*!
+ Destroys the QGraphicsSceneResizeEvent.
+*/
+QGraphicsSceneResizeEvent::~QGraphicsSceneResizeEvent()
+{
+}
+
+/*!
+ Returns the old size (i.e., the size immediately before the widget was
+ resized).
+
+ \sa newSize(), QGraphicsWidget::resize()
+*/
+QSizeF QGraphicsSceneResizeEvent::oldSize() const
+{
+ Q_D(const QGraphicsSceneResizeEvent);
+ return d->oldSize;
+}
+
+/*!
+ \internal
+*/
+void QGraphicsSceneResizeEvent::setOldSize(const QSizeF &size)
+{
+ Q_D(QGraphicsSceneResizeEvent);
+ d->oldSize = size;
+}
+
+/*!
+ Returns the new size (i.e., the current size).
+
+ \sa oldSize(), QGraphicsWidget::resize()
+*/
+QSizeF QGraphicsSceneResizeEvent::newSize() const
+{
+ Q_D(const QGraphicsSceneResizeEvent);
+ return d->newSize;
+}
+
+/*!
+ \internal
+*/
+void QGraphicsSceneResizeEvent::setNewSize(const QSizeF &size)
+{
+ Q_D(QGraphicsSceneResizeEvent);
+ d->newSize = size;
+}
+
+class QGraphicsSceneMoveEventPrivate : public QGraphicsSceneEventPrivate
+{
+ Q_DECLARE_PUBLIC(QGraphicsSceneMoveEvent)
+public:
+ inline QGraphicsSceneMoveEventPrivate()
+ { }
+
+ QPointF oldPos;
+ QPointF newPos;
+};
+
+/*!
+ Constructs a QGraphicsSceneMoveEvent.
+*/
+QGraphicsSceneMoveEvent::QGraphicsSceneMoveEvent()
+ : QGraphicsSceneEvent(*new QGraphicsSceneMoveEventPrivate, QEvent::GraphicsSceneMove)
+{
+}
+
+/*!
+ Destroys the QGraphicsSceneMoveEvent.
+*/
+QGraphicsSceneMoveEvent::~QGraphicsSceneMoveEvent()
+{
+}
+
+/*!
+ Returns the old position (i.e., the position immediatly before the widget
+ was moved).
+
+ \sa newPos(), QGraphicsItem::setPos()
+*/
+QPointF QGraphicsSceneMoveEvent::oldPos() const
+{
+ Q_D(const QGraphicsSceneMoveEvent);
+ return d->oldPos;
+}
+
+/*!
+ \internal
+*/
+void QGraphicsSceneMoveEvent::setOldPos(const QPointF &pos)
+{
+ Q_D(QGraphicsSceneMoveEvent);
+ d->oldPos = pos;
+}
+
+/*!
+ Returns the new position (i.e., the current position).
+
+ \sa oldPos(), QGraphicsItem::setPos()
+*/
+QPointF QGraphicsSceneMoveEvent::newPos() const
+{
+ Q_D(const QGraphicsSceneMoveEvent);
+ return d->newPos;
+}
+
+/*!
+ \internal
+*/
+void QGraphicsSceneMoveEvent::setNewPos(const QPointF &pos)
+{
+ Q_D(QGraphicsSceneMoveEvent);
+ d->newPos = pos;
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_GRAPHICSVIEW
diff --git a/src/gui/graphicsview/qgraphicssceneevent.h b/src/gui/graphicsview/qgraphicssceneevent.h
new file mode 100644
index 0000000000..be50e96b39
--- /dev/null
+++ b/src/gui/graphicsview/qgraphicssceneevent.h
@@ -0,0 +1,311 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QGRAPHICSSCENEEVENT_H
+#define QGRAPHICSSCENEEVENT_H
+
+#include <QtCore/qcoreevent.h>
+#include <QtCore/qpoint.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+#if !defined(QT_NO_GRAPHICSVIEW) || (QT_EDITION & QT_MODULE_GRAPHICSVIEW) != QT_MODULE_GRAPHICSVIEW
+
+class QMimeData;
+class QPointF;
+class QSizeF;
+class QWidget;
+
+class QGraphicsSceneEventPrivate;
+class Q_GUI_EXPORT QGraphicsSceneEvent : public QEvent
+{
+public:
+ QGraphicsSceneEvent(Type type);
+ ~QGraphicsSceneEvent();
+
+ QWidget *widget() const;
+ void setWidget(QWidget *widget);
+
+protected:
+ QGraphicsSceneEvent(QGraphicsSceneEventPrivate &dd, Type type = None);
+ QGraphicsSceneEventPrivate *d_ptr;
+ Q_DECLARE_PRIVATE(QGraphicsSceneEvent)
+};
+
+class QGraphicsSceneMouseEventPrivate;
+class Q_GUI_EXPORT QGraphicsSceneMouseEvent : public QGraphicsSceneEvent
+{
+public:
+ QGraphicsSceneMouseEvent(Type type = None);
+ ~QGraphicsSceneMouseEvent();
+
+ QPointF pos() const;
+ void setPos(const QPointF &pos);
+
+ QPointF scenePos() const;
+ void setScenePos(const QPointF &pos);
+
+ QPoint screenPos() const;
+ void setScreenPos(const QPoint &pos);
+
+ QPointF buttonDownPos(Qt::MouseButton button) const;
+ void setButtonDownPos(Qt::MouseButton button, const QPointF &pos);
+
+ QPointF buttonDownScenePos(Qt::MouseButton button) const;
+ void setButtonDownScenePos(Qt::MouseButton button, const QPointF &pos);
+
+ QPoint buttonDownScreenPos(Qt::MouseButton button) const;
+ void setButtonDownScreenPos(Qt::MouseButton button, const QPoint &pos);
+
+ QPointF lastPos() const;
+ void setLastPos(const QPointF &pos);
+
+ QPointF lastScenePos() const;
+ void setLastScenePos(const QPointF &pos);
+
+ QPoint lastScreenPos() const;
+ void setLastScreenPos(const QPoint &pos);
+
+ Qt::MouseButtons buttons() const;
+ void setButtons(Qt::MouseButtons buttons);
+
+ Qt::MouseButton button() const;
+ void setButton(Qt::MouseButton button);
+
+ Qt::KeyboardModifiers modifiers() const;
+ void setModifiers(Qt::KeyboardModifiers modifiers);
+
+private:
+ Q_DECLARE_PRIVATE(QGraphicsSceneMouseEvent)
+};
+
+class QGraphicsSceneWheelEventPrivate;
+class Q_GUI_EXPORT QGraphicsSceneWheelEvent : public QGraphicsSceneEvent
+{
+public:
+ QGraphicsSceneWheelEvent(Type type = None);
+ ~QGraphicsSceneWheelEvent();
+
+ QPointF pos() const;
+ void setPos(const QPointF &pos);
+
+ QPointF scenePos() const;
+ void setScenePos(const QPointF &pos);
+
+ QPoint screenPos() const;
+ void setScreenPos(const QPoint &pos);
+
+ Qt::MouseButtons buttons() const;
+ void setButtons(Qt::MouseButtons buttons);
+
+ Qt::KeyboardModifiers modifiers() const;
+ void setModifiers(Qt::KeyboardModifiers modifiers);
+
+ int delta() const;
+ void setDelta(int delta);
+
+ Qt::Orientation orientation() const;
+ void setOrientation(Qt::Orientation orientation);
+
+private:
+ Q_DECLARE_PRIVATE(QGraphicsSceneWheelEvent)
+};
+
+class QGraphicsSceneContextMenuEventPrivate;
+class Q_GUI_EXPORT QGraphicsSceneContextMenuEvent : public QGraphicsSceneEvent
+{
+public:
+ enum Reason { Mouse, Keyboard, Other };
+
+ QGraphicsSceneContextMenuEvent(Type type = None);
+ ~QGraphicsSceneContextMenuEvent();
+
+ QPointF pos() const;
+ void setPos(const QPointF &pos);
+
+ QPointF scenePos() const;
+ void setScenePos(const QPointF &pos);
+
+ QPoint screenPos() const;
+ void setScreenPos(const QPoint &pos);
+
+ Qt::KeyboardModifiers modifiers() const;
+ void setModifiers(Qt::KeyboardModifiers modifiers);
+
+ Reason reason() const;
+ void setReason(Reason reason);
+
+private:
+ Q_DECLARE_PRIVATE(QGraphicsSceneContextMenuEvent)
+};
+
+class QGraphicsSceneHoverEventPrivate;
+class Q_GUI_EXPORT QGraphicsSceneHoverEvent : public QGraphicsSceneEvent
+{
+public:
+ QGraphicsSceneHoverEvent(Type type = None);
+ ~QGraphicsSceneHoverEvent();
+
+ QPointF pos() const;
+ void setPos(const QPointF &pos);
+
+ QPointF scenePos() const;
+ void setScenePos(const QPointF &pos);
+
+ QPoint screenPos() const;
+ void setScreenPos(const QPoint &pos);
+
+ QPointF lastPos() const;
+ void setLastPos(const QPointF &pos);
+
+ QPointF lastScenePos() const;
+ void setLastScenePos(const QPointF &pos);
+
+ QPoint lastScreenPos() const;
+ void setLastScreenPos(const QPoint &pos);
+
+ Qt::KeyboardModifiers modifiers() const;
+ void setModifiers(Qt::KeyboardModifiers modifiers);
+
+private:
+ Q_DECLARE_PRIVATE(QGraphicsSceneHoverEvent)
+};
+
+class QGraphicsSceneHelpEventPrivate;
+class Q_GUI_EXPORT QGraphicsSceneHelpEvent : public QGraphicsSceneEvent
+{
+public:
+ QGraphicsSceneHelpEvent(Type type = None);
+ ~QGraphicsSceneHelpEvent();
+
+ QPointF scenePos() const;
+ void setScenePos(const QPointF &pos);
+
+ QPoint screenPos() const;
+ void setScreenPos(const QPoint &pos);
+
+private:
+ Q_DECLARE_PRIVATE(QGraphicsSceneHelpEvent)
+};
+
+class QGraphicsSceneDragDropEventPrivate;
+class Q_GUI_EXPORT QGraphicsSceneDragDropEvent : public QGraphicsSceneEvent
+{
+public:
+ QGraphicsSceneDragDropEvent(Type type = None);
+ ~QGraphicsSceneDragDropEvent();
+
+ QPointF pos() const;
+ void setPos(const QPointF &pos);
+
+ QPointF scenePos() const;
+ void setScenePos(const QPointF &pos);
+
+ QPoint screenPos() const;
+ void setScreenPos(const QPoint &pos);
+
+ Qt::MouseButtons buttons() const;
+ void setButtons(Qt::MouseButtons buttons);
+
+ Qt::KeyboardModifiers modifiers() const;
+ void setModifiers(Qt::KeyboardModifiers modifiers);
+
+ Qt::DropActions possibleActions() const;
+ void setPossibleActions(Qt::DropActions actions);
+
+ Qt::DropAction proposedAction() const;
+ void setProposedAction(Qt::DropAction action);
+ void acceptProposedAction();
+
+ Qt::DropAction dropAction() const;
+ void setDropAction(Qt::DropAction action);
+
+ QWidget *source() const;
+ void setSource(QWidget *source);
+
+ const QMimeData *mimeData() const;
+ void setMimeData(const QMimeData *data);
+
+private:
+ Q_DECLARE_PRIVATE(QGraphicsSceneDragDropEvent)
+};
+
+class QGraphicsSceneResizeEventPrivate;
+class Q_GUI_EXPORT QGraphicsSceneResizeEvent : public QGraphicsSceneEvent
+{
+ Q_DECLARE_PRIVATE(QGraphicsSceneResizeEvent)
+public:
+ QGraphicsSceneResizeEvent();
+ ~QGraphicsSceneResizeEvent();
+
+ QSizeF oldSize() const;
+ void setOldSize(const QSizeF &size);
+
+ QSizeF newSize() const;
+ void setNewSize(const QSizeF &size);
+};
+
+class QGraphicsSceneMoveEventPrivate;
+class Q_GUI_EXPORT QGraphicsSceneMoveEvent : public QGraphicsSceneEvent
+{
+ Q_DECLARE_PRIVATE(QGraphicsSceneMoveEvent)
+public:
+ QGraphicsSceneMoveEvent();
+ ~QGraphicsSceneMoveEvent();
+
+ QPointF oldPos() const;
+ void setOldPos(const QPointF &pos);
+
+ QPointF newPos() const;
+ void setNewPos(const QPointF &pos);
+};
+
+#endif // QT_NO_GRAPHICSVIEW
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif
diff --git a/src/gui/graphicsview/qgraphicsview.cpp b/src/gui/graphicsview/qgraphicsview.cpp
new file mode 100644
index 0000000000..2f7f57aac8
--- /dev/null
+++ b/src/gui/graphicsview/qgraphicsview.cpp
@@ -0,0 +1,3887 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//#define QGRAPHICSVIEW_DEBUG
+
+static const int QGRAPHICSVIEW_REGION_RECT_THRESHOLD = 50;
+
+static const int QGRAPHICSVIEW_PREALLOC_STYLE_OPTIONS = 503; // largest prime < 2^9
+
+/*!
+ \class QGraphicsView
+ \brief The QGraphicsView class provides a widget for displaying the
+ contents of a QGraphicsScene.
+ \since 4.2
+ \ingroup multimedia
+ \ingroup graphicsview-api
+ \mainclass
+
+ QGraphicsView visualizes the contents of a QGraphicsScene in a scrollable
+ viewport. To create a scene with geometrical items, see QGraphicsScene's
+ documentation. QGraphicsView is part of \l{The Graphics View Framework}.
+
+ To visualize a scene, you start by constructing a QGraphicsView object,
+ passing the address of the scene you want to visualize to QGraphicsView's
+ constructor. Alternatively, you can call setScene() to set the scene at a
+ later point. After you call show(), the view will by default scroll to the
+ center of the scene and display any items that are visible at this
+ point. For example:
+
+ \snippet doc/src/snippets/code/src_gui_graphicsview_qgraphicsview.cpp 0
+
+ You can explicitly scroll to any position on the scene by using the
+ scroll bars, or by calling centerOn(). By passing a point to centerOn(),
+ QGraphicsView will scroll its viewport to ensure that the point is
+ centered in the view. An overload is provided for scrolling to a
+ QGraphicsItem, in which case QGraphicsView will see to that the center of
+ the item is centered in the view. If all you want is to ensure that a
+ certain area is visible, (but not necessarily centered,) you can call
+ ensureVisible() instead.
+
+ QGraphicsView can be used to visualize a whole scene, or only parts of it.
+ The visualized area is by default detected automatically when the view is
+ displayed for the first time (by calling
+ QGraphicsScene::itemsBoundingRect()). To set the visualized area rectangle
+ yourself, you can call setSceneRect(). This will adjust the scroll bars'
+ ranges appropriately. Note that although the scene supports a virtually
+ unlimited size, the range of the scroll bars will never exceed the range of
+ an integer (INT_MIN, INT_MAX). When the scene is larger than the scroll
+ bars' values, you can choose to use translate() to navigate the scene
+ instead.
+
+ QGraphicsView visualizes the scene by calling render(). By default, the
+ items are drawn onto the viewport by using a regular QPainter, and using
+ default render hints. To change the default render hints that
+ QGraphicsView passes to QPainter when painting items, you can call
+ setRenderHints().
+
+ By default, QGraphicsView provides a regular QWidget for the viewport
+ widget. You can access this widget by calling viewport(), or you can
+ replace it by calling setViewport(). To render using OpenGL, simply call
+ setViewport(new QGLWidget). QGraphicsView takes ownership of the viewport
+ widget.
+
+ QGraphicsView supports affine transformations, using QMatrix. You can
+ either pass a matrix to setMatrix(), or you can call one of the
+ convenience functions rotate(), scale(), translate() or shear(). The most
+ two common transformations are scaling, which is used to implement
+ zooming, and rotation. QGraphicsView keeps the center of the view fixed
+ during a transformation.
+
+ You can interact with the items on the scene by using the mouse and
+ keyboard. QGraphicsView translates the mouse and key events into \e scene
+ events, (events that inherit QGraphicsSceneEvent,), and forward them to
+ the visualized scene. In the end, it's the individual item that handles
+ the events and reacts to them. For example, if you click on a selectable
+ item, the item will typically let the scene know that it has been
+ selected, and it will also redraw itself to display a selection
+ rectangle. Similiary, if you click and drag the mouse to move a movable
+ item, it's the item that handles the mouse moves and moves itself. Item
+ interaction is enabled by default, and you can toggle it by calling
+ setInteractive().
+
+ You can also provide your own custom scene interaction, by creating a
+ subclass of QGraphicsView, and reimplementing the mouse and key event
+ handlers. To simplify how you programmatically interact with items in the
+ view, QGraphicsView provides the mapping functions mapToScene() and
+ mapFromScene(), and the item accessors items() and itemAt(). These
+ functions allow you to map points, rectangles, polygons and paths between
+ view coordinates and scene coordinates, and to find items on the scene
+ using view coordinates.
+
+ \img graphicsview-view.png
+
+ \sa QGraphicsScene, QGraphicsItem, QGraphicsSceneEvent
+*/
+
+/*!
+ \enum QGraphicsView::ViewportAnchor
+
+ This enums describe the possible anchors that QGraphicsView can
+ use when the user resizes the view or when the view is
+ transformed.
+
+ \value NoAnchor No anchor, i.e. the view leaves the scene's
+ position unchanged.
+ \value AnchorViewCenter The scene point at the center of the view
+ is used as the anchor.
+ \value AnchorUnderMouse The point under the mouse is used as the anchor.
+
+ \sa resizeAnchor, transformationAnchor
+*/
+
+/*!
+ \enum QGraphicsView::ViewportUpdateMode
+
+ \since 4.3
+
+ This enum describes how QGraphicsView updates its viewport when the scene
+ contents change or are exposed.
+
+ \value FullViewportUpdate When any visible part of the scene changes or is
+ reexposed, QGraphicsView will update the entire viewport. This approach is
+ fastest when QGraphicsView spends more time figuring out what to draw than
+ it would spend drawing (e.g., when very many small items are repeatedly
+ updated). This is the preferred update mode for viewports that do not
+ support partial updates, such as QGLWidget, and for viewports that need to
+ disable scroll optimization.
+
+ \value MinimalViewportUpdate QGraphicsView will determine the minimal
+ viewport region that requires a redraw, minimizing the time spent drawing
+ by avoiding a redraw of areas that have not changed. This is
+ QGraphicsView's default mode. Although this approach provides the best
+ performance in general, if there are many small visible changes on the
+ scene, QGraphicsView might end up spending more time finding the minimal
+ approach than it will spend drawing.
+
+ \value SmartViewportUpdate QGraphicsView will attempt to find an optimal
+ update mode by analyzing the areas that require a redraw.
+
+ \value BoundingRectViewportUpdate The bounding rectangle of all changes in
+ the viewport will be redrawn. This mode has the advantage that
+ QGraphicsView searches only one region for changes, minimizing time spent
+ determining what needs redrawing. The disadvantage is that areas that have
+ not changed also need to be redrawn.
+
+ \value NoViewportUpdate QGraphicsView will never update its viewport when
+ the scene changes; the user is expected to control all updates. This mode
+ disables all (potentially slow) item visibility testing in QGraphicsView,
+ and is suitable for scenes that either require a fixed frame rate, or where
+ the viewport is otherwise updated externally.
+
+ \sa viewportUpdateMode
+*/
+
+/*!
+ \enum QGraphicsView::OptimizationFlag
+
+ \since 4.3
+
+ This enum describes flags that you can enable to improve rendering
+ performance in QGraphicsView. By default, none of these flags are set.
+ Note that setting a flag usually imposes a side effect, and this effect
+ can vary between paint devices and platforms.
+
+ \value DontClipPainter QGraphicsView sometimes clips the painter when
+ rendering the scene contents. This can generally improve performance
+ (e.g., rendering only small parts of a large pixmap), and protects against
+ rendering mistakes (e.g., drawing outside bounding rectangles, or outside
+ the exposed area). In some situations, however, the painter clip can slow
+ down rendering; especially when all painting is restricted to inside
+ exposed areas. By enabling this flag, QGraphicsView will completely
+ disable its implicit clipping. Note that rendering artifacts from using a
+ semi-transparent foreground or background brush can occur if clipping is
+ disabled.
+
+ \value DontSavePainterState When rendering, QGraphicsView protects the
+ painter state (see QPainter::save()) when rendering the background or
+ foreground, and when rendering each item. This allows you to leave the
+ painter in an altered state (i.e., you can call QPainter::setPen() or
+ QPainter::setBrush() without restoring the state after painting). However,
+ if the items consistently do restore the state, you should enable this
+ flag to prevent QGraphicsView from doing the same.
+
+ \value DontAdjustForAntialiasing Disables QGraphicsView's antialiasing
+ auto-adjustment of exposed areas. Items that render antialiased lines on
+ the boundaries of their QGraphicsItem::boundingRect() can end up rendering
+ parts of the line outside. To prevent rendering artifacts, QGraphicsView
+ expands all exposed regions by 2 pixels in all directions. If you enable
+ this flag, QGraphicsView will no longer perform these adjustments,
+ minimizing the areas that require redrawing, which improves performance. A
+ common side effect is that items that do draw with antialiasing can leave
+ painting traces behind on the scene as they are moved.
+*/
+
+/*!
+ \enum QGraphicsView::CacheModeFlag
+
+ This enum describes the flags that you can set for a QGraphicsView's cache
+ mode.
+
+ \value CacheNone All painting is done directly onto the viewport.
+
+ \value CacheBackground The background is cached. This affects both custom
+ backgrounds, and backgrounds based on the backgroundBrush property. When
+ this flag is enabled, QGraphicsView will allocate one pixmap with the full
+ size of the viewport.
+
+ \sa cacheMode
+*/
+
+/*!
+ \enum QGraphicsView::DragMode
+
+ This enum describes the default action for the view when pressing and
+ dragging the mouse over the viewport.
+
+ \value NoDrag Nothing happens; the mouse event is ignored.
+
+ \value ScrollHandDrag The cursor changes into a pointing hand, and
+ dragging the mouse around will scroll the scrolbars. This mode works both
+ in \l{QGraphicsView::interactive}{interactive} and non-interactive mode.
+
+ \value RubberBandDrag A rubber band will appear. Dragging the mouse will
+ set the rubber band geometry, and all items covered by the rubber band are
+ selected. This mode is disabled for non-interactive views.
+
+ \sa dragMode, QGraphicsScene::setSelectionArea()
+*/
+
+#include "qgraphicsview.h"
+#include "qgraphicsview_p.h"
+
+#ifndef QT_NO_GRAPHICSVIEW
+
+#include "qgraphicsitem.h"
+#include "qgraphicsitem_p.h"
+#include "qgraphicsscene.h"
+#include "qgraphicsscene_p.h"
+#include "qgraphicssceneevent.h"
+#include "qgraphicswidget.h"
+
+#include <QtCore/qdatetime.h>
+#include <QtCore/qdebug.h>
+#include <QtCore/qmath.h>
+#include <QtGui/qapplication.h>
+#include <QtGui/qdesktopwidget.h>
+#include <QtGui/qevent.h>
+#include <QtGui/qlayout.h>
+#include <QtGui/qtransform.h>
+#include <QtGui/qmatrix.h>
+#include <QtGui/qpainter.h>
+#include <QtGui/qscrollbar.h>
+#include <QtGui/qstyleoption.h>
+#ifdef Q_WS_X11
+#include <private/qt_x11_p.h>
+#endif
+
+QT_BEGIN_NAMESPACE
+
+inline int q_round_bound(qreal d) //### (int)(qreal) INT_MAX != INT_MAX for single precision
+{
+ if (d <= (qreal) INT_MIN)
+ return INT_MIN;
+ else if (d >= (qreal) INT_MAX)
+ return INT_MAX;
+ return d >= 0.0 ? int(d + 0.5) : int(d - int(d-1) + 0.5) + int(d-1);
+}
+
+/*!
+ \internal
+*/
+QGraphicsViewPrivate::QGraphicsViewPrivate()
+ : renderHints(QPainter::TextAntialiasing),
+ dragMode(QGraphicsView::NoDrag),
+ sceneInteractionAllowed(true), hasSceneRect(false),
+ connectedToScene(false),
+ mousePressButton(Qt::NoButton),
+ identityMatrix(true),
+ dirtyScroll(true),
+ accelerateScrolling(true),
+ leftIndent(0), topIndent(0),
+ lastMouseEvent(QEvent::None, QPoint(), Qt::NoButton, 0, 0),
+ useLastMouseEvent(false),
+ keepLastCenterPoint(true),
+ alignment(Qt::AlignCenter),
+ transforming(false),
+ transformationAnchor(QGraphicsView::AnchorViewCenter), resizeAnchor(QGraphicsView::NoAnchor),
+ viewportUpdateMode(QGraphicsView::MinimalViewportUpdate),
+ optimizationFlags(0),
+ scene(0),
+#ifndef QT_NO_RUBBERBAND
+ rubberBanding(false),
+ rubberBandSelectionMode(Qt::IntersectsItemShape),
+#endif
+ handScrolling(false), handScrollMotions(0), cacheMode(0),
+ mustAllocateStyleOptions(false),
+ mustResizeBackgroundPixmap(true),
+#ifndef QT_NO_CURSOR
+ hasStoredOriginalCursor(false),
+#endif
+ lastDragDropEvent(0),
+ fullUpdatePending(true),
+ dirtyRectCount(0),
+ updatingLater(false),
+ updateSceneSlotReimplementedChecked(false)
+{
+ styleOptions.reserve(QGRAPHICSVIEW_PREALLOC_STYLE_OPTIONS);
+}
+
+/*!
+ \internal
+*/
+void QGraphicsViewPrivate::recalculateContentSize()
+{
+ Q_Q(QGraphicsView);
+
+ QSize maxSize = q->maximumViewportSize();
+ int width = maxSize.width();
+ int height = maxSize.height();
+ QRectF viewRect = matrix.mapRect(q->sceneRect());
+
+ bool frameOnlyAround = (q->style()->styleHint(QStyle::SH_ScrollView_FrameOnlyAroundContents, 0, q));
+ if (frameOnlyAround) {
+ if (hbarpolicy == Qt::ScrollBarAlwaysOn)
+ height -= frameWidth * 2;
+ if (vbarpolicy == Qt::ScrollBarAlwaysOn)
+ width -= frameWidth * 2;
+ }
+
+ // Adjust the maximum width and height of the viewport based on the width
+ // of visible scroll bars.
+ int scrollBarExtent = q->style()->pixelMetric(QStyle::PM_ScrollBarExtent, 0, q);
+ if (frameOnlyAround)
+ scrollBarExtent += frameWidth * 2;
+
+ bool useHorizontalScrollBar = (viewRect.width() > width) && hbarpolicy != Qt::ScrollBarAlwaysOff;
+ bool useVerticalScrollBar = (viewRect.height() > height) && vbarpolicy != Qt::ScrollBarAlwaysOff;
+ if (useHorizontalScrollBar && !useVerticalScrollBar) {
+ if (viewRect.height() > height - scrollBarExtent)
+ useVerticalScrollBar = true;
+ }
+ if (useVerticalScrollBar && !useHorizontalScrollBar) {
+ if (viewRect.width() > width - scrollBarExtent)
+ useHorizontalScrollBar = true;
+ }
+ if (useHorizontalScrollBar && hbarpolicy != Qt::ScrollBarAlwaysOn)
+ height -= scrollBarExtent;
+ if (useVerticalScrollBar && vbarpolicy != Qt::ScrollBarAlwaysOn)
+ width -= scrollBarExtent;
+
+ // Setting the ranges of these scroll bars can/will cause the values to
+ // change, and scrollContentsBy() will be called correspondingly. This
+ // will reset the last center point.
+ QPointF savedLastCenterPoint = lastCenterPoint;
+
+ // Remember the former indent settings
+ qreal oldLeftIndent = leftIndent;
+ qreal oldTopIndent = topIndent;
+
+ // If the whole scene fits horizontally, we center the scene horizontally,
+ // and ignore the horizontal scroll bars.
+ int left = q_round_bound(viewRect.left());
+ int right = q_round_bound(viewRect.right() - width);
+ if (left >= right) {
+ q->horizontalScrollBar()->setRange(0, 0);
+
+ switch (alignment & Qt::AlignHorizontal_Mask) {
+ case Qt::AlignLeft:
+ leftIndent = -viewRect.left();
+ break;
+ case Qt::AlignRight:
+ leftIndent = width - viewRect.width() - viewRect.left() - 1;
+ break;
+ case Qt::AlignHCenter:
+ default:
+ leftIndent = width / 2 - (viewRect.left() + viewRect.right()) / 2;
+ break;
+ }
+ } else {
+ q->horizontalScrollBar()->setRange(left, right);
+ q->horizontalScrollBar()->setPageStep(width);
+ q->horizontalScrollBar()->setSingleStep(width / 20);
+ leftIndent = 0;
+ }
+
+ // If the whole scene fits vertically, we center the scene vertically, and
+ // ignore the vertical scroll bars.
+ int top = q_round_bound(viewRect.top());
+ int bottom = q_round_bound(viewRect.bottom() - height);
+ if (top >= bottom) {
+ q->verticalScrollBar()->setRange(0, 0);
+
+ switch (alignment & Qt::AlignVertical_Mask) {
+ case Qt::AlignTop:
+ topIndent = -viewRect.top();
+ break;
+ case Qt::AlignBottom:
+ topIndent = height - viewRect.height() - viewRect.top() - 1;
+ break;
+ case Qt::AlignVCenter:
+ default:
+ topIndent = height / 2 - (viewRect.top() + viewRect.bottom()) / 2;
+ break;
+ }
+ } else {
+ q->verticalScrollBar()->setRange(top, bottom);
+ q->verticalScrollBar()->setPageStep(height);
+ q->verticalScrollBar()->setSingleStep(height / 20);
+ topIndent = 0;
+ }
+
+ // Restorethe center point from before the ranges changed.
+ lastCenterPoint = savedLastCenterPoint;
+
+ // Issue a full update if the indents change.
+ // ### If the transform is still the same, we can get away with just a
+ // scroll instead.
+ if (oldLeftIndent != leftIndent || oldTopIndent != topIndent) {
+ dirtyScroll = true;
+ q->viewport()->update();
+ } else if (q->isRightToLeft() && !leftIndent) {
+ // In reverse mode, the horizontal scroll always changes after the content
+ // size has changed, as the scroll is calculated by summing the min and
+ // max values of the range and subtracting the current value. In normal
+ // mode the scroll remains unchanged unless the indent has changed.
+ dirtyScroll = true;
+ }
+
+ if (cacheMode & QGraphicsView::CacheBackground) {
+ // Invalidate the background pixmap
+ mustResizeBackgroundPixmap = true;
+ }
+}
+
+/*!
+ \internal
+*/
+void QGraphicsViewPrivate::centerView(QGraphicsView::ViewportAnchor anchor)
+{
+ Q_Q(QGraphicsView);
+ switch (anchor) {
+ case QGraphicsView::AnchorUnderMouse: {
+ if (q->underMouse()) {
+ // Last scene pos: lastMouseMoveScenePoint
+ // Current mouse pos:
+ QPointF transformationDiff = q->mapToScene(q->viewport()->rect().center())
+ - q->mapToScene(q->mapFromGlobal(QCursor::pos()));
+ q->centerOn(lastMouseMoveScenePoint + transformationDiff);;
+ } else {
+ q->centerOn(lastCenterPoint);
+ }
+ break;
+ }
+ case QGraphicsView::AnchorViewCenter:
+ q->centerOn(lastCenterPoint);
+ break;
+ case QGraphicsView::NoAnchor:
+ break;
+ }
+}
+
+/*!
+ \internal
+*/
+void QGraphicsViewPrivate::updateLastCenterPoint()
+{
+ Q_Q(QGraphicsView);
+ lastCenterPoint = q->mapToScene(q->viewport()->rect().center());
+}
+
+/*!
+ \internal
+
+ Returns the horizontal scroll value (the X value of the left edge of the
+ viewport).
+*/
+qint64 QGraphicsViewPrivate::horizontalScroll() const
+{
+ if (dirtyScroll)
+ const_cast<QGraphicsViewPrivate *>(this)->updateScroll();
+ return scrollX;
+}
+
+/*!
+ \internal
+
+ Returns the vertical scroll value (the X value of the top edge of the
+ viewport).
+*/
+qint64 QGraphicsViewPrivate::verticalScroll() const
+{
+ if (dirtyScroll)
+ const_cast<QGraphicsViewPrivate *>(this)->updateScroll();
+ return scrollY;
+}
+
+/*!
+ \internal
+*/
+void QGraphicsViewPrivate::updateScroll()
+{
+ Q_Q(QGraphicsView);
+ scrollX = qint64(-leftIndent);
+ if (q->isRightToLeft()) {
+ if (!leftIndent) {
+ scrollX += q->horizontalScrollBar()->minimum();
+ scrollX += q->horizontalScrollBar()->maximum();
+ scrollX -= q->horizontalScrollBar()->value();
+ }
+ } else {
+ scrollX += q->horizontalScrollBar()->value();
+ }
+
+ scrollY = qint64(q->verticalScrollBar()->value() - topIndent);
+
+ dirtyScroll = false;
+}
+
+/*!
+ \internal
+*/
+void QGraphicsViewPrivate::replayLastMouseEvent()
+{
+ if (!useLastMouseEvent || !scene)
+ return;
+ mouseMoveEventHandler(&lastMouseEvent);
+}
+
+/*!
+ \internal
+*/
+void QGraphicsViewPrivate::storeMouseEvent(QMouseEvent *event)
+{
+ useLastMouseEvent = true;
+ lastMouseEvent = QMouseEvent(QEvent::MouseMove, event->pos(), event->globalPos(),
+ event->button(), event->buttons(), event->modifiers());
+}
+
+void QGraphicsViewPrivate::mouseMoveEventHandler(QMouseEvent *event)
+{
+ Q_Q(QGraphicsView);
+
+ storeMouseEvent(event);
+ lastMouseEvent.setAccepted(false);
+
+ if (!sceneInteractionAllowed)
+ return;
+ if (handScrolling)
+ return;
+ if (!scene)
+ return;
+
+ QGraphicsSceneMouseEvent mouseEvent(QEvent::GraphicsSceneMouseMove);
+ mouseEvent.setWidget(q->viewport());
+ mouseEvent.setButtonDownScenePos(mousePressButton, mousePressScenePoint);
+ mouseEvent.setButtonDownScreenPos(mousePressButton, mousePressScreenPoint);
+ mouseEvent.setScenePos(q->mapToScene(event->pos()));
+ mouseEvent.setScreenPos(event->globalPos());
+ mouseEvent.setLastScenePos(lastMouseMoveScenePoint);
+ mouseEvent.setLastScreenPos(lastMouseMoveScreenPoint);
+ mouseEvent.setButtons(event->buttons());
+ mouseEvent.setButton(event->button());
+ mouseEvent.setModifiers(event->modifiers());
+ lastMouseMoveScenePoint = mouseEvent.scenePos();
+ lastMouseMoveScreenPoint = mouseEvent.screenPos();
+ mouseEvent.setAccepted(false);
+ QApplication::sendEvent(scene, &mouseEvent);
+
+ // Remember whether the last event was accepted or not.
+ lastMouseEvent.setAccepted(mouseEvent.isAccepted());
+
+ if (mouseEvent.isAccepted() && mouseEvent.buttons() != 0) {
+ // The event was delivered to a mouse grabber; the press is likely to
+ // have set a cursor, and we must not change it.
+ return;
+ }
+
+#ifndef QT_NO_CURSOR
+ // Find the topmost item under the mouse with a cursor.
+ foreach (QGraphicsItem *item, scene->d_func()->cachedItemsUnderMouse) {
+ if (item->hasCursor()) {
+ _q_setViewportCursor(item->cursor());
+ return;
+ }
+ }
+
+ // No items with cursors found; revert to the view cursor.
+ if (hasStoredOriginalCursor) {
+ // Restore the original viewport cursor.
+ hasStoredOriginalCursor = false;
+ q->viewport()->setCursor(originalCursor);
+ }
+#endif
+}
+
+/*!
+ \internal
+*/
+#ifndef QT_NO_RUBBERBAND
+QRegion QGraphicsViewPrivate::rubberBandRegion(const QWidget *widget, const QRect &rect) const
+{
+ QStyleHintReturnMask mask;
+ QStyleOptionRubberBand option;
+ option.initFrom(widget);
+ option.rect = rect;
+ option.opaque = false;
+ option.shape = QRubberBand::Rectangle;
+
+ QRegion tmp;
+ tmp += rect;
+ if (widget->style()->styleHint(QStyle::SH_RubberBand_Mask, &option, widget, &mask))
+ tmp &= mask.region;
+ return tmp;
+}
+#endif
+
+/*!
+ \internal
+*/
+#ifndef QT_NO_CURSOR
+void QGraphicsViewPrivate::_q_setViewportCursor(const QCursor &cursor)
+{
+ Q_Q(QGraphicsView);
+ QWidget *viewport = q->viewport();
+ if (!hasStoredOriginalCursor) {
+ hasStoredOriginalCursor = true;
+ originalCursor = viewport->cursor();
+ }
+ viewport->setCursor(cursor);
+}
+#endif
+
+/*!
+ \internal
+*/
+#ifndef QT_NO_CURSOR
+void QGraphicsViewPrivate::_q_unsetViewportCursor()
+{
+ Q_Q(QGraphicsView);
+ foreach (QGraphicsItem *item, q->items(lastMouseEvent.pos())) {
+ if (item->hasCursor()) {
+ _q_setViewportCursor(item->cursor());
+ return;
+ }
+ }
+
+ // Restore the original viewport cursor.
+ hasStoredOriginalCursor = false;
+ if (dragMode == QGraphicsView::ScrollHandDrag)
+ q->viewport()->setCursor(Qt::OpenHandCursor);
+ else
+ q->viewport()->setCursor(originalCursor);
+}
+#endif
+
+/*!
+ \internal
+*/
+void QGraphicsViewPrivate::storeDragDropEvent(const QGraphicsSceneDragDropEvent *event)
+{
+ delete lastDragDropEvent;
+ lastDragDropEvent = new QGraphicsSceneDragDropEvent(event->type());
+ lastDragDropEvent->setScenePos(event->scenePos());
+ lastDragDropEvent->setScreenPos(event->screenPos());
+ lastDragDropEvent->setButtons(event->buttons());
+ lastDragDropEvent->setModifiers(event->modifiers());
+ lastDragDropEvent->setPossibleActions(event->possibleActions());
+ lastDragDropEvent->setProposedAction(event->proposedAction());
+ lastDragDropEvent->setDropAction(event->dropAction());
+ lastDragDropEvent->setMimeData(event->mimeData());
+ lastDragDropEvent->setWidget(event->widget());
+ lastDragDropEvent->setSource(event->source());
+}
+
+/*!
+ \internal
+*/
+void QGraphicsViewPrivate::populateSceneDragDropEvent(QGraphicsSceneDragDropEvent *dest,
+ QDropEvent *source)
+{
+#ifndef QT_NO_DRAGANDDROP
+ Q_Q(QGraphicsView);
+ dest->setScenePos(q->mapToScene(source->pos()));
+ dest->setScreenPos(q->mapToGlobal(source->pos()));
+ dest->setButtons(source->mouseButtons());
+ dest->setModifiers(source->keyboardModifiers());
+ dest->setPossibleActions(source->possibleActions());
+ dest->setProposedAction(source->proposedAction());
+ dest->setDropAction(source->dropAction());
+ dest->setMimeData(source->mimeData());
+ dest->setWidget(q->viewport());
+ dest->setSource(source->source());
+#else
+ Q_UNUSED(dest)
+ Q_UNUSED(source)
+#endif
+}
+
+/*!
+ \internal
+*/
+QRect QGraphicsViewPrivate::mapToViewRect(const QGraphicsItem *item, const QRectF &rect) const
+{
+ Q_Q(const QGraphicsView);
+ if (dirtyScroll)
+ const_cast<QGraphicsViewPrivate *>(this)->updateScroll();
+
+ if (item->d_ptr->itemIsUntransformable()) {
+ QTransform itv = item->deviceTransform(q->viewportTransform());
+ return itv.mapRect(rect).toAlignedRect();
+ }
+
+ // Translate-only
+ QPointF offset;
+ const QGraphicsItem *parentItem = item;
+ const QGraphicsItemPrivate *itemd;
+ do {
+ itemd = parentItem->d_ptr;
+ if (itemd->hasTransform)
+ break;
+ offset += itemd->pos;
+ } while ((parentItem = itemd->parent));
+
+ QRectF baseRect = rect.translated(offset.x(), offset.y());
+ if (!parentItem) {
+ if (identityMatrix) {
+ baseRect.translate(-scrollX, -scrollY);
+ return baseRect.toAlignedRect();
+ }
+ return matrix.mapRect(baseRect).translated(-scrollX, -scrollY).toAlignedRect();
+ }
+
+ QTransform tr = parentItem->sceneTransform();
+ if (!identityMatrix)
+ tr *= matrix;
+ QRectF r = tr.mapRect(baseRect);
+ r.translate(-scrollX, -scrollY);
+ return r.toAlignedRect();
+}
+
+/*!
+ \internal
+*/
+QRegion QGraphicsViewPrivate::mapToViewRegion(const QGraphicsItem *item, const QRectF &rect) const
+{
+ Q_Q(const QGraphicsView);
+ if (dirtyScroll)
+ const_cast<QGraphicsViewPrivate *>(this)->updateScroll();
+
+ // Accurate bounding region
+ QTransform itv = item->sceneTransform() * q->viewportTransform();
+ return item->boundingRegion(itv) & itv.mapRect(rect).toAlignedRect();
+}
+
+/*!
+ \internal
+*/
+void QGraphicsViewPrivate::itemUpdated(QGraphicsItem *item, const QRectF &rect)
+{
+ if (fullUpdatePending || viewportUpdateMode == QGraphicsView::NoViewportUpdate)
+ return;
+ if (item->d_ptr->dirty)
+ updateLater();
+
+ QRectF updateRect = rect;
+ if (item->isClipped()) {
+ // Minimize unnecessary redraw.
+ QGraphicsItem *p = item;
+ while ((p = p->d_ptr->parent)) {
+ if (p->flags() & QGraphicsItem::ItemClipsChildrenToShape) {
+ updateRect &= p->itemTransform(item).mapRect(p->boundingRect());
+ if (updateRect.isNull())
+ return;
+ }
+
+ if (!(p->d_ptr->ancestorFlags & QGraphicsItemPrivate::AncestorClipsChildren))
+ break;
+ }
+
+ if (updateRect.isNull())
+ return;
+ }
+
+ // Map the rect to view coordinates.
+ QRect vr = viewport->rect();
+
+ if (!item->d_ptr->hasBoundingRegionGranularity) {
+ QRect r = mapToViewRect(item, updateRect) & vr;
+ if (r.isNull())
+ return;
+ this->updateRect(r);
+ } else {
+ QRegion r = mapToViewRegion(item, updateRect) & vr;
+ if (r.isEmpty())
+ return;
+ updateRegion(r);
+ }
+}
+
+void QGraphicsViewPrivate::updateLater()
+{
+ Q_Q(QGraphicsView);
+ if (updatingLater)
+ return;
+ updatingLater = true;
+ QMetaObject::invokeMethod(q, "_q_updateLaterSlot", Qt::QueuedConnection);
+}
+
+void QGraphicsViewPrivate::_q_updateLaterSlot()
+{
+ Q_Q(QGraphicsView);
+ if (!scene)
+ return;
+
+ QRect vr = viewport->rect();
+ QTransform viewTransform = q->viewportTransform();
+ const QList<QGraphicsItem *> &dirtyItems = scene->d_func()->dirtyItems;
+ for (int i = 0; i < dirtyItems.size(); ++i) {
+ const QGraphicsItem *item = dirtyItems.at(i);
+ QTransform x = item->sceneTransform() * viewTransform;
+ QRect viewRect = x.mapRect(item->boundingRect()).toAlignedRect() & vr;
+ if (!viewRect.isNull())
+ updateRect(viewRect);
+ }
+
+ dirtyRectCount += dirtyRects.size();
+
+ bool noUpdate = !fullUpdatePending && viewportUpdateMode == QGraphicsView::FullViewportUpdate;
+ if ((dirtyRectCount > 0 || !dirtyBoundingRect.isNull()) && !fullUpdatePending && !noUpdate) {
+ if (viewportUpdateMode == QGraphicsView::BoundingRectViewportUpdate
+ || (viewportUpdateMode == QGraphicsView::SmartViewportUpdate
+ && dirtyRectCount >= QGRAPHICSVIEW_REGION_RECT_THRESHOLD)) {
+ if (!(optimizationFlags & QGraphicsView::DontAdjustForAntialiasing)) {
+ viewport->update(dirtyBoundingRect.adjusted(-2, -2, 2, 2));
+ } else {
+ viewport->update(dirtyBoundingRect);
+ }
+ } else {
+ // ### Improve this block, which is very slow for complex regions. We
+ // need to strike the balance between having an accurate update
+ // region, and running fast. The below approach is the simplest way to
+ // create a region from a bunch of rects, but we might want to use
+ // other approaches; e.g., a grid of a fixed size representing
+ // quadrants of the viewport, which we mark as dirty depending on the
+ // rectangles in the list. Perhaps this should go into a
+ // QRegion::fromRects(rects, how) function.
+ QRegion region;
+ if (!(optimizationFlags & QGraphicsView::DontAdjustForAntialiasing)) {
+ for (int i = 0; i < dirtyRegions.size(); ++i) {
+ QVector<QRect> rects = dirtyRegions.at(i).rects();
+ for (int j = 0; j < rects.size(); ++j)
+ region += rects.at(j).adjusted(-2, -2, 2, 2);
+ }
+ for (int i = 0; i < dirtyRects.size(); ++i)
+ region += dirtyRects.at(i).adjusted(-2, -2, 2, 2);
+ } else {
+ for (int i = 0; i < dirtyRegions.size(); ++i)
+ region += dirtyRegions.at(i);
+ for (int i = 0; i < dirtyRects.size(); ++i)
+ region += dirtyRects.at(i);
+ }
+
+ viewport->update(region);
+ }
+ }
+
+ dirtyRegions.clear();
+ dirtyRects.clear();
+ dirtyRectCount = 0;
+ dirtyBoundingRect = QRect();
+ updatingLater = false;
+}
+
+void QGraphicsViewPrivate::updateAll()
+{
+ Q_Q(QGraphicsView);
+ q->viewport()->update();
+ fullUpdatePending = true;
+ dirtyRectCount = 0;
+ dirtyBoundingRect = QRect();
+ updatingLater = false;
+}
+
+void QGraphicsViewPrivate::updateRegion(const QRegion &r)
+{
+ Q_Q(QGraphicsView);
+
+ // Rect intersects viewport - update everything?
+ switch (viewportUpdateMode) {
+ case QGraphicsView::FullViewportUpdate:
+ fullUpdatePending = true;
+ q->viewport()->update();
+ break;
+ case QGraphicsView::BoundingRectViewportUpdate:
+ dirtyBoundingRect |= r.boundingRect();
+ if (dirtyBoundingRect == q->viewport()->rect()) {
+ fullUpdatePending = true;
+ q->viewport()->update();
+ } else {
+ updateLater();
+ }
+ break;
+ case QGraphicsView::SmartViewportUpdate:
+ dirtyBoundingRect |= r.boundingRect();
+ if ((dirtyRectCount + r.numRects()) < QGRAPHICSVIEW_REGION_RECT_THRESHOLD)
+ dirtyRegions << r;
+ dirtyRectCount += r.numRects();
+ updateLater();
+ break;
+ case QGraphicsView::MinimalViewportUpdate:
+ dirtyRegions << r;
+ dirtyRectCount += r.numRects();
+ updateLater();
+ break;
+ case QGraphicsView::NoViewportUpdate:
+ // Unreachable
+ break;
+ }
+
+ // Compress the regions...
+ if (dirtyRectCount > QGRAPHICSVIEW_REGION_RECT_THRESHOLD && dirtyRegions.size() > 1) {
+ QRegion masterRegion;
+ for (int i=0; i<dirtyRegions.size(); ++i) {
+ masterRegion |= dirtyRegions.at(i);
+ }
+ dirtyRectCount = masterRegion.numRects();
+ dirtyRegions.clear();
+ dirtyRegions << masterRegion;
+ }
+}
+
+void QGraphicsViewPrivate::updateRect(const QRect &r)
+{
+ Q_Q(QGraphicsView);
+
+ // Rect intersects viewport - update everything?
+ switch (viewportUpdateMode) {
+ case QGraphicsView::FullViewportUpdate:
+ fullUpdatePending = true;
+ q->viewport()->update();
+ break;
+ case QGraphicsView::BoundingRectViewportUpdate:
+ dirtyBoundingRect |= r;
+ if (dirtyBoundingRect == q->viewport()->rect()) {
+ fullUpdatePending = true;
+ q->viewport()->update();
+ } else {
+ updateLater();
+ }
+ break;
+ case QGraphicsView::SmartViewportUpdate:
+ dirtyBoundingRect |= r;
+ if ((dirtyRectCount + dirtyRects.size()) < QGRAPHICSVIEW_REGION_RECT_THRESHOLD)
+ dirtyRects << r;
+ updateLater();
+ break;
+ case QGraphicsView::MinimalViewportUpdate:
+ dirtyRects << r;
+ updateLater();
+ break;
+ case QGraphicsView::NoViewportUpdate:
+ // Unreachable
+ break;
+ }
+}
+
+QStyleOptionGraphicsItem *QGraphicsViewPrivate::allocStyleOptionsArray(int numItems)
+{
+ if (mustAllocateStyleOptions || (numItems > styleOptions.capacity()))
+ // too many items, let's allocate on-the-fly
+ return new QStyleOptionGraphicsItem[numItems];
+
+ // expand only whenever necessary
+ if (numItems > styleOptions.size())
+ styleOptions.resize(numItems);
+
+ mustAllocateStyleOptions = true;
+ return styleOptions.data();
+}
+
+void QGraphicsViewPrivate::freeStyleOptionsArray(QStyleOptionGraphicsItem *array)
+{
+ mustAllocateStyleOptions = false;
+ if (array != styleOptions.data())
+ delete [] array;
+}
+
+extern QPainterPath qt_regionToPath(const QRegion &region);
+
+QList<QGraphicsItem *> QGraphicsViewPrivate::findItems(const QRegion &exposedRegion,
+ const QTransform &worldTransform,
+ bool *allItems) const
+{
+ Q_Q(const QGraphicsView);
+ QList<QGraphicsItem *> itemList;
+ QSet<QGraphicsItem *> tmp;
+ bool simpleTransform = worldTransform.type() <= QTransform::TxScale;
+
+ QPainterPath path = qt_regionToPath(exposedRegion);
+ *allItems = path.contains(q->mapFromScene(scene->d_func()->growingItemsBoundingRect).boundingRect());
+ QList<QRectF> exposedRects;
+ QList<QPolygonF> exposedPolys;
+
+ // Transform the exposed viewport rects to scene rects or polygons
+ foreach (const QRect &rect, exposedRegion.rects()) {
+ QPolygonF exposedPoly = q->mapToScene(rect.adjusted(-1, -1, 1, 1));
+ QRectF exposedRect = exposedPoly.boundingRect();
+ if (!simpleTransform)
+ exposedPolys << exposedPoly;
+ exposedRects << exposedRect;
+ }
+
+ // Find which items need to be drawn.
+ if (*allItems) {
+ // All items are guaranteed within the exposed region, don't bother using the index.
+ foreach (QGraphicsItem *item, scene->items()) {
+ // But we only want to include items that are visible
+ if (item->isVisible())
+ itemList << item;
+ }
+ } else if (simpleTransform) {
+ // Simple rect lookups will do.
+ if (exposedRects.size() > 1) {
+ foreach (const QRectF &rect, exposedRects) {
+ foreach (QGraphicsItem *item, scene->d_func()->items_helper(rect, Qt::IntersectsItemBoundingRect, Qt::SortOrder(-1) /* don't sort */)) {
+ if (!tmp.contains(item)) {
+ tmp << item;
+ itemList << item;
+ }
+ }
+ }
+ } else {
+ itemList += scene->d_func()->items_helper(exposedRects[0], Qt::IntersectsItemBoundingRect, Qt::SortOrder(-1) /* don't sort */);
+ }
+ } else {
+ // Polygon lookup is necessary.
+ if (exposedRects.size() > 1) {
+ foreach (const QPolygonF &poly, exposedPolys) {
+ foreach (QGraphicsItem *item, scene->d_func()->items_helper(poly, Qt::IntersectsItemBoundingRect, Qt::SortOrder(-1) /* don't sort */)) {
+ if (!tmp.contains(item)) {
+ tmp << item;
+ itemList << item;
+ }
+ }
+ }
+ } else {
+ itemList += scene->d_func()->items_helper(exposedPolys[0], Qt::IntersectsItemBoundingRect, Qt::SortOrder(-1) /* don't sort */);
+ }
+ }
+
+ // Check for items that ignore inherited transformations, and add them if
+ // necessary.
+ QRectF untr = scene->d_func()->largestUntransformableItem;
+ if (!*allItems && !untr.isNull()) {
+ // Map the largest untransformable item subtree boundingrect from view
+ // to scene coordinates, and use this to expand all exposed rects in
+ // search for untransformable items.
+ QRectF ltri = matrix.inverted().mapRect(untr);
+ ltri.adjust(-untr.width(), -untr.height(), untr.width(), untr.height());
+
+ foreach (const QRect &rect, exposedRegion.rects()) {
+ QRectF exposed = q->mapToScene(rect.adjusted(-1, -1, 1, 1)).boundingRect();
+ exposed.adjust(-ltri.width(), -ltri.height(), ltri.width(), ltri.height());
+
+ foreach (QGraphicsItem *item, scene->d_func()->estimateItemsInRect(exposed)) {
+ if (item->d_ptr->itemIsUntransformable()) {
+ if (!tmp.contains(item)) {
+ QPainterPath rectPath;
+ rectPath.addRect(rect);
+ QPainterPath path = item->deviceTransform(q->viewportTransform()).inverted().map(rectPath);
+ if (item->collidesWithPath(path, Qt::IntersectsItemBoundingRect)) {
+ itemList << item;
+ tmp << item;
+ }
+ }
+ }
+ }
+ }
+ }
+ tmp.clear();
+
+ // Sort the items.
+ QGraphicsScenePrivate::sortItems(&itemList, Qt::DescendingOrder,
+ scene->d_func()->sortCacheEnabled);
+
+ return itemList;
+}
+
+void QGraphicsViewPrivate::generateStyleOptions(const QList<QGraphicsItem *> &itemList,
+ QGraphicsItem **itemArray,
+ QStyleOptionGraphicsItem *styleOptionArray,
+ const QTransform &worldTransform,
+ bool allItems,
+ const QRegion &exposedRegion) const
+{
+ // Two unit vectors.
+ QLineF v1(0, 0, 1, 0);
+ QLineF v2(0, 0, 0, 1);
+ QTransform itemToViewportTransform;
+ QRectF brect;
+ QTransform reverseMap;
+
+ for (int i = 0; i < itemList.size(); ++i) {
+ QGraphicsItem *item = itemArray[i] = itemList[i];
+
+ QStyleOptionGraphicsItem &option = styleOptionArray[i];
+ brect = item->boundingRect();
+ option.state = QStyle::State_None;
+ option.rect = brect.toRect();
+ option.exposedRect = QRectF();
+ if (item->d_ptr->selected)
+ option.state |= QStyle::State_Selected;
+ if (item->d_ptr->enabled)
+ option.state |= QStyle::State_Enabled;
+ if (item->hasFocus())
+ option.state |= QStyle::State_HasFocus;
+ if (scene->d_func()->hoverItems.contains(item))
+ option.state |= QStyle::State_MouseOver;
+ if (item == scene->mouseGrabberItem())
+ option.state |= QStyle::State_Sunken;
+
+ // Calculate a simple level-of-detail metric.
+ // ### almost identical code in QGraphicsScene::render()
+ // and QGraphicsView::render() - consider refactoring
+ if (item->d_ptr->itemIsUntransformable()) {
+ itemToViewportTransform = item->deviceTransform(worldTransform);
+ } else {
+ itemToViewportTransform = item->sceneTransform() * worldTransform;
+ }
+
+ if (itemToViewportTransform.type() <= QTransform::TxTranslate) {
+ // Translation and rotation only? The LOD is 1.
+ option.levelOfDetail = 1;
+ } else {
+ // LOD is the transformed area of a 1x1 rectangle.
+ option.levelOfDetail = qSqrt(itemToViewportTransform.map(v1).length() * itemToViewportTransform.map(v2).length());
+ }
+ option.matrix = itemToViewportTransform.toAffine(); //### discards perspective
+
+ if (!allItems) {
+ // Determine the item's exposed area
+ reverseMap = itemToViewportTransform.inverted();
+ foreach (const QRect &rect, exposedRegion.rects()) {
+ option.exposedRect |= reverseMap.mapRect(QRectF(rect.adjusted(-1, -1, 1, 1)));
+ if (option.exposedRect.contains(brect))
+ break;
+ }
+ option.exposedRect &= brect;
+ } else {
+ // The whole item is exposed
+ option.exposedRect = brect;
+ }
+ }
+}
+
+/*!
+ Constructs a QGraphicsView. \a parent is passed to QWidget's constructor.
+*/
+QGraphicsView::QGraphicsView(QWidget *parent)
+ : QAbstractScrollArea(*new QGraphicsViewPrivate, parent)
+{
+ setViewport(0);
+ setAcceptDrops(true);
+ setBackgroundRole(QPalette::Base);
+
+ // ### Ideally this would be enabled/disabled depending on whether any
+ // widgets in the current scene enabled input methods. We could do that
+ // using a simple reference count. The same goes for acceptDrops and mouse
+ // tracking.
+ setAttribute(Qt::WA_InputMethodEnabled);
+}
+
+/*!
+ Constructs a QGraphicsView and sets the visualized scene to \a
+ scene. \a parent is passed to QWidget's constructor.
+*/
+QGraphicsView::QGraphicsView(QGraphicsScene *scene, QWidget *parent)
+ : QAbstractScrollArea(*new QGraphicsViewPrivate, parent)
+{
+ setScene(scene);
+ setViewport(0);
+ setAcceptDrops(true);
+ setBackgroundRole(QPalette::Base);
+ setAttribute(Qt::WA_InputMethodEnabled);
+}
+
+/*!
+ \internal
+ */
+QGraphicsView::QGraphicsView(QGraphicsViewPrivate &dd, QWidget *parent)
+ : QAbstractScrollArea(dd, parent)
+{
+ setViewport(0);
+ setAcceptDrops(true);
+ setBackgroundRole(QPalette::Base);
+ setAttribute(Qt::WA_InputMethodEnabled);
+}
+
+/*!
+ Destructs the QGraphicsView object.
+*/
+QGraphicsView::~QGraphicsView()
+{
+ Q_D(QGraphicsView);
+ if (d->scene)
+ d->scene->d_func()->views.removeAll(this);
+ delete d->lastDragDropEvent;
+}
+
+/*!
+ \reimp
+*/
+QSize QGraphicsView::sizeHint() const
+{
+ Q_D(const QGraphicsView);
+ if (d->scene) {
+ QSizeF baseSize = d->matrix.mapRect(sceneRect()).size();
+ baseSize += QSizeF(d->frameWidth * 2, d->frameWidth * 2);
+ return baseSize.boundedTo((3 * QApplication::desktop()->size()) / 4).toSize();
+ }
+ return QAbstractScrollArea::sizeHint();
+}
+
+/*!
+ \property QGraphicsView::renderHints
+ \brief the default render hints for the view
+
+ These hints are
+ used to initialize QPainter before each visible item is drawn. QPainter
+ uses render hints to toggle rendering features such as antialiasing and
+ smooth pixmap transformation.
+
+ QPainter::TextAntialiasing is enabled by default.
+
+ Example:
+
+ \snippet doc/src/snippets/code/src_gui_graphicsview_qgraphicsview.cpp 1
+*/
+QPainter::RenderHints QGraphicsView::renderHints() const
+{
+ Q_D(const QGraphicsView);
+ return d->renderHints;
+}
+void QGraphicsView::setRenderHints(QPainter::RenderHints hints)
+{
+ Q_D(QGraphicsView);
+ if (hints == d->renderHints)
+ return;
+ d->renderHints = hints;
+ viewport()->update();
+}
+
+/*!
+ If \a enabled is true, the render hint \a hint is enabled; otherwise it
+ is disabled.
+
+ \sa renderHints
+*/
+void QGraphicsView::setRenderHint(QPainter::RenderHint hint, bool enabled)
+{
+ Q_D(QGraphicsView);
+ QPainter::RenderHints oldHints = d->renderHints;
+ if (enabled)
+ d->renderHints |= hint;
+ else
+ d->renderHints &= ~hint;
+ if (oldHints != d->renderHints)
+ viewport()->update();
+}
+
+/*!
+ \property QGraphicsView::alignment
+ \brief the alignment of the scene in the view when the whole
+ scene is visible.
+
+ If the whole scene is visible in the view, (i.e., there are no visible
+ scroll bars,) the view's alignment will decide where the scene will be
+ rendered in the view. For example, if the alignment is Qt::AlignCenter,
+ which is default, the scene will be centered in the view, and if the
+ alignment is (Qt::AlignLeft | Qt::AlignTop), the scene will be rendered in
+ the top-left corner of the view.
+*/
+Qt::Alignment QGraphicsView::alignment() const
+{
+ Q_D(const QGraphicsView);
+ return d->alignment;
+}
+void QGraphicsView::setAlignment(Qt::Alignment alignment)
+{
+ Q_D(QGraphicsView);
+ if (d->alignment != alignment) {
+ d->alignment = alignment;
+ d->recalculateContentSize();
+ }
+}
+
+/*!
+ \property QGraphicsView::transformationAnchor
+ \brief how the view should position the scene during transformations.
+
+ QGraphicsView uses this property to decide how to position the scene in
+ the viewport when the transformation matrix changes, and the coordinate
+ system of the view is transformed. The default behavior, AnchorViewCenter,
+ ensures that the scene point at the center of the view remains unchanged
+ during transformations (e.g., when rotating, the scene will appear to
+ rotate around the center of the view).
+
+ Note that the effect of this property is noticeable when only a part of the
+ scene is visible (i.e., when there are scroll bars). Otherwise, if the
+ whole scene fits in the view, QGraphicsScene uses the view \l alignment to
+ position the scene in the view.
+
+ \sa alignment, resizeAnchor
+*/
+QGraphicsView::ViewportAnchor QGraphicsView::transformationAnchor() const
+{
+ Q_D(const QGraphicsView);
+ return d->transformationAnchor;
+}
+void QGraphicsView::setTransformationAnchor(ViewportAnchor anchor)
+{
+ Q_D(QGraphicsView);
+ d->transformationAnchor = anchor;
+}
+
+/*!
+ \property QGraphicsView::resizeAnchor
+ \brief how the view should position the scene when the view is resized.
+
+ QGraphicsView uses this property to decide how to position the scene in
+ the viewport when the viewport widget's size changes. The default
+ behavior, NoAnchor, leaves the scene's position unchanged during a resize;
+ the top-left corner of the view will appear to be anchored while resizing.
+
+ Note that the effect of this property is noticeable when only a part of the
+ scene is visible (i.e., when there are scroll bars). Otherwise, if the
+ whole scene fits in the view, QGraphicsScene uses the view \l alignment to
+ position the scene in the view.
+
+ \sa alignment, transformationAnchor, Qt::WNorthWestGravity
+*/
+QGraphicsView::ViewportAnchor QGraphicsView::resizeAnchor() const
+{
+ Q_D(const QGraphicsView);
+ return d->resizeAnchor;
+}
+void QGraphicsView::setResizeAnchor(ViewportAnchor anchor)
+{
+ Q_D(QGraphicsView);
+ d->resizeAnchor = anchor;
+}
+
+/*!
+ \property QGraphicsView::viewportUpdateMode
+ \brief how the viewport should update its contents.
+
+ \since 4.3
+
+ QGraphicsView uses this property to decide how to update areas of the
+ scene that have been reexposed or changed. Usually you do not need to
+ modify this property, but there are some cases where doing so can improve
+ rendering performance. See the ViewportUpdateMode documentation for
+ specific details.
+
+ The default value is MinimalViewportUpdate, where QGraphicsView will
+ update as small an area of the viewport as possible when the contents
+ change.
+
+ \sa ViewportUpdateMode, cacheMode
+*/
+QGraphicsView::ViewportUpdateMode QGraphicsView::viewportUpdateMode() const
+{
+ Q_D(const QGraphicsView);
+ return d->viewportUpdateMode;
+}
+void QGraphicsView::setViewportUpdateMode(ViewportUpdateMode mode)
+{
+ Q_D(QGraphicsView);
+ d->viewportUpdateMode = mode;
+}
+
+/*!
+ \property QGraphicsView::optimizationFlags
+ \brief flags that can be used to tune QGraphicsView's performance.
+
+ \since 4.3
+
+ QGraphicsView uses clipping, extra bounding rect adjustments, and certain
+ other aids to improve rendering quality and performance for the common
+ case graphics scene. However, depending on the target platform, the scene,
+ and the viewport in use, some of these operations can degrade performance.
+
+ The effect varies from flag to flag; see the OptimizationFlags
+ documentation for details.
+
+ By default, no optimization flags are enabled.
+
+ \sa setOptimizationFlag()
+*/
+QGraphicsView::OptimizationFlags QGraphicsView::optimizationFlags() const
+{
+ Q_D(const QGraphicsView);
+ return d->optimizationFlags;
+}
+void QGraphicsView::setOptimizationFlags(OptimizationFlags flags)
+{
+ Q_D(QGraphicsView);
+ d->optimizationFlags = flags;
+}
+
+/*!
+ Enables \a flag if \a enabled is true; otherwise disables \a flag.
+
+ \sa optimizationFlags
+*/
+void QGraphicsView::setOptimizationFlag(OptimizationFlag flag, bool enabled)
+{
+ Q_D(QGraphicsView);
+ if (enabled)
+ d->optimizationFlags |= flag;
+ else
+ d->optimizationFlags &= ~flag;
+}
+
+/*!
+ \property QGraphicsView::dragMode
+ \brief the behavior for dragging the mouse over the scene while
+ the left mouse button is pressed.
+
+ This property defines what should happen when the user clicks on the scene
+ background and drags the mouse (e.g., scrolling the viewport contents
+ using a pointing hand cursor, or selecting multiple items with a rubber
+ band). The default value, NoDrag, does nothing.
+
+ This behavior only affects mouse clicks that are not handled by any item.
+ You can define a custom behavior by creating a subclass of QGraphicsView
+ and reimplementing mouseMoveEvent().
+*/
+QGraphicsView::DragMode QGraphicsView::dragMode() const
+{
+ Q_D(const QGraphicsView);
+ return d->dragMode;
+}
+void QGraphicsView::setDragMode(DragMode mode)
+{
+ Q_D(QGraphicsView);
+ if (d->dragMode == mode)
+ return;
+
+#ifndef QT_NO_CURSOR
+ if (d->dragMode == ScrollHandDrag)
+ viewport()->unsetCursor();
+#endif
+
+ // If dragMode is unset while dragging, e.g. via a keyEvent, we
+ // don't unset the handScrolling state. When enabling scrolling
+ // again the mouseMoveEvent will automatically start scrolling,
+ // without a mousePress
+ if (d->dragMode == ScrollHandDrag && mode == NoDrag && d->handScrolling)
+ d->handScrolling = false;
+
+ d->dragMode = mode;
+
+#ifndef QT_NO_CURSOR
+ if (d->dragMode == ScrollHandDrag) {
+ // Forget the stored viewport cursor when we enter scroll hand drag mode.
+ d->hasStoredOriginalCursor = false;
+ viewport()->setCursor(Qt::OpenHandCursor);
+ }
+#endif
+}
+
+#ifndef QT_NO_RUBBERBAND
+/*!
+ \property QGraphicsView::rubberBandSelectionMode
+ \brief the behavior for selecting items with a rubber band selection rectangle.
+ \since 4.3
+
+ This property defines how items are selected when using the RubberBandDrag
+ drag mode.
+
+ The default value is Qt::IntersectsItemShape; all items whose shape
+ intersects with or is contained by the rubber band are selected.
+
+ \sa dragMode, items()
+*/
+Qt::ItemSelectionMode QGraphicsView::rubberBandSelectionMode() const
+{
+ Q_D(const QGraphicsView);
+ return d->rubberBandSelectionMode;
+}
+void QGraphicsView::setRubberBandSelectionMode(Qt::ItemSelectionMode mode)
+{
+ Q_D(QGraphicsView);
+ d->rubberBandSelectionMode = mode;
+}
+#endif
+
+/*!
+ \property QGraphicsView::cacheMode
+ \brief which parts of the view are cached
+
+ QGraphicsView can cache pre-rendered content in a QPixmap, which is then
+ drawn onto the viewport. The purpose of such caching is to speed up the
+ total rendering time for areas that are slow to render. Texture, gradient
+ and alpha blended backgrounds, for example, can be notibly slow to render;
+ especially with a transformed view. The CacheBackground flag enables
+ caching of the view's background. For example:
+
+ \snippet doc/src/snippets/code/src_gui_graphicsview_qgraphicsview.cpp 2
+
+ The cache is invalidated every time the view is transformed. However, when
+ scrolling, only partial invalidation is required.
+
+ By default, nothing is cached.
+
+ \sa resetCachedContent(), QPixmapCache
+*/
+QGraphicsView::CacheMode QGraphicsView::cacheMode() const
+{
+ Q_D(const QGraphicsView);
+ return d->cacheMode;
+}
+void QGraphicsView::setCacheMode(CacheMode mode)
+{
+ Q_D(QGraphicsView);
+ if (mode == d->cacheMode)
+ return;
+ d->cacheMode = mode;
+ resetCachedContent();
+}
+
+/*!
+ Resets any cached content. Calling this function will clear
+ QGraphicsView's cache. If the current cache mode is \l CacheNone, this
+ function does nothing.
+
+ This function is called automatically for you when the backgroundBrush or
+ QGraphicsScene::backgroundBrush properties change; you only need to call
+ this function if you have reimplemented QGraphicsScene::drawBackground()
+ or QGraphicsView::drawBackground() to draw a custom background, and need
+ to trigger a full redraw.
+
+ \sa cacheMode()
+*/
+void QGraphicsView::resetCachedContent()
+{
+ Q_D(QGraphicsView);
+ if (d->cacheMode == CacheNone)
+ return;
+
+ if (d->cacheMode & CacheBackground) {
+ // Background caching is enabled.
+ d->mustResizeBackgroundPixmap = true;
+ viewport()->update();
+ } else if (d->mustResizeBackgroundPixmap) {
+ // Background caching is disabled.
+ // Cleanup, free some resources.
+ d->mustResizeBackgroundPixmap = false;
+ d->backgroundPixmap = QPixmap();
+ d->backgroundPixmapExposed = QRegion();
+ }
+}
+
+/*!
+ Invalidates and schedules a redraw of \a layers inside \a rect. \a rect is
+ in scene coordinates. Any cached content for \a layers inside \a rect is
+ unconditionally invalidated and redrawn.
+
+ You can call this function to notify QGraphicsView of changes to the
+ background or the foreground of the scene. It is commonly used for scenes
+ with tile-based backgrounds to notify changes when QGraphicsView has
+ enabled background caching.
+
+ Note that QGraphicsView currently supports background caching only (see
+ QGraphicsView::CacheBackground). This function is equivalent to calling update() if any
+ layer but QGraphicsScene::BackgroundLayer is passed.
+
+ \sa QGraphicsScene::invalidate(), update()
+*/
+void QGraphicsView::invalidateScene(const QRectF &rect, QGraphicsScene::SceneLayers layers)
+{
+ Q_D(QGraphicsView);
+ if ((layers & QGraphicsScene::BackgroundLayer) && !d->mustResizeBackgroundPixmap) {
+ QRect viewRect = mapFromScene(rect).boundingRect();
+ if (viewport()->rect().intersects(viewRect)) {
+ // The updated background area is exposed; schedule this area for
+ // redrawing.
+ d->backgroundPixmapExposed += viewRect;
+ if (d->scene)
+ d->scene->update(rect);
+ }
+ }
+}
+
+/*!
+ \property QGraphicsView::interactive
+ \brief whether the view allowed scene interaction.
+
+ If enabled, this view is set to allow scene interaction. Otherwise, this
+ view will not allow interaction, and any mouse or key events are ignored
+ (i.e., it will act as a read-only view).
+
+ By default, this property is true.
+*/
+bool QGraphicsView::isInteractive() const
+{
+ Q_D(const QGraphicsView);
+ return d->sceneInteractionAllowed;
+}
+void QGraphicsView::setInteractive(bool allowed)
+{
+ Q_D(QGraphicsView);
+ d->sceneInteractionAllowed = allowed;
+}
+
+/*!
+ Returns a pointer to the scene that is currently visualized in the
+ view. If no scene is currently visualized, 0 is returned.
+
+ \sa setScene()
+*/
+QGraphicsScene *QGraphicsView::scene() const
+{
+ Q_D(const QGraphicsView);
+ return d->scene;
+}
+
+/*!
+ Sets the current scene to \a scene. If \a scene is already being
+ viewed, this function does nothing.
+
+ When a scene is set on a view, the QGraphicsScene::changed() signal
+ is automatically connected to this view's updateScene() slot, and the
+ view's scroll bars are adjusted to fit the size of the scene.
+*/
+void QGraphicsView::setScene(QGraphicsScene *scene)
+{
+ Q_D(QGraphicsView);
+ if (d->scene == scene)
+ return;
+
+ // Always update the viewport when the scene changes.
+ viewport()->update();
+
+ // Remove the previously assigned scene.
+ if (d->scene) {
+ disconnect(d->scene, SIGNAL(changed(QList<QRectF>)),
+ this, SLOT(updateScene(QList<QRectF>)));
+ disconnect(d->scene, SIGNAL(sceneRectChanged(QRectF)),
+ this, SLOT(updateSceneRect(QRectF)));
+ d->scene->d_func()->views.removeAll(this);
+ }
+
+ // Assign the new scene and update the contents (scrollbars, etc.)).
+ if ((d->scene = scene)) {
+ connect(d->scene, SIGNAL(sceneRectChanged(QRectF)),
+ this, SLOT(updateSceneRect(QRectF)));
+ d->updateSceneSlotReimplementedChecked = false;
+ d->scene->d_func()->views << this;
+ d->recalculateContentSize();
+ d->lastCenterPoint = sceneRect().center();
+ d->keepLastCenterPoint = true;
+ } else {
+ d->recalculateContentSize();
+ }
+}
+
+/*!
+ \property QGraphicsView::sceneRect
+ \brief the area of the scene visualized by this view.
+
+ The scene rectangle defines the extent of the scene, and in the view's case,
+ this means the area of the scene that you can navigate using the scroll
+ bars.
+
+ If unset, or if a null QRectF is set, this property has the same value as
+ QGraphicsScene::sceneRect, and it changes with
+ QGraphicsScene::sceneRect. Otherwise, the view's scene rect is unaffected
+ by the scene.
+
+ Note that, although the scene supports a virtually unlimited size, the
+ range of the scroll bars will never exceed the range of an integer
+ (INT_MIN, INT_MAX). When the scene is larger than the scroll bars' values,
+ you can choose to use translate() to navigate the scene instead.
+
+ By default, this property contains a rectangle at the origin with zero
+ width and height.
+
+ \sa QGraphicsScene::sceneRect
+*/
+QRectF QGraphicsView::sceneRect() const
+{
+ Q_D(const QGraphicsView);
+ if (d->hasSceneRect)
+ return d->sceneRect;
+ if (d->scene)
+ return d->scene->sceneRect();
+ return QRectF();
+}
+void QGraphicsView::setSceneRect(const QRectF &rect)
+{
+ Q_D(QGraphicsView);
+ d->hasSceneRect = !rect.isNull();
+ d->sceneRect = rect;
+ d->recalculateContentSize();
+}
+
+/*!
+ Returns the current transformation matrix for the view. If no current
+ transformation is set, the identity matrix is returned.
+
+ \sa setMatrix(), rotate(), scale(), shear(), translate()
+*/
+QMatrix QGraphicsView::matrix() const
+{
+ Q_D(const QGraphicsView);
+ return d->matrix.toAffine();
+}
+
+/*!
+ Sets the view's current transformation matrix to \a matrix.
+
+ If \a combine is true, then \a matrix is combined with the current matrix;
+ otherwise, \a matrix \e replaces the current matrix. \a combine is false
+ by default.
+
+ The transformation matrix tranforms the scene into view coordinates. Using
+ the default transformation, provided by the identity matrix, one pixel in
+ the view represents one unit in the scene (e.g., a 10x10 rectangular item
+ is drawn using 10x10 pixels in the view). If a 2x2 scaling matrix is
+ applied, the scene will be drawn in 1:2 (e.g., a 10x10 rectangular item is
+ then drawn using 20x20 pixels in the view).
+
+ Example:
+
+ \snippet doc/src/snippets/code/src_gui_graphicsview_qgraphicsview.cpp 3
+
+ To simplify interation with items using a transformed view, QGraphicsView
+ provides mapTo... and mapFrom... functions that can translate between
+ scene and view coordinates. For example, you can call mapToScene() to map
+ a view coordinate to a floating point scene coordinate, or mapFromScene()
+ to map from floating point scene coordinates to view coordinates.
+
+ \sa matrix(), rotate(), scale(), shear(), translate()
+*/
+void QGraphicsView::setMatrix(const QMatrix &matrix, bool combine)
+{
+ setTransform(QTransform(matrix), combine);
+}
+
+/*!
+ Resets the view transformation matrix to the identity matrix.
+*/
+void QGraphicsView::resetMatrix()
+{
+ resetTransform();
+}
+
+/*!
+ Rotates the current view transformation \a angle degrees clockwise.
+
+ \sa setMatrix(), matrix(), scale(), shear(), translate()
+*/
+void QGraphicsView::rotate(qreal angle)
+{
+ Q_D(QGraphicsView);
+ QTransform matrix = d->matrix;
+ matrix.rotate(angle);
+ setTransform(matrix);
+}
+
+/*!
+ Scales the current view transformation by (\a sx, \a sy).
+
+ \sa setMatrix(), matrix(), rotate(), shear(), translate()
+*/
+void QGraphicsView::scale(qreal sx, qreal sy)
+{
+ Q_D(QGraphicsView);
+ QTransform matrix = d->matrix;
+ matrix.scale(sx, sy);
+ setTransform(matrix);
+}
+
+/*!
+ Shears the current view transformation by (\a sh, \a sv).
+
+ \sa setMatrix(), matrix(), rotate(), scale(), translate()
+*/
+void QGraphicsView::shear(qreal sh, qreal sv)
+{
+ Q_D(QGraphicsView);
+ QTransform matrix = d->matrix;
+ matrix.shear(sh, sv);
+ setTransform(matrix);
+}
+
+/*!
+ Translates the current view transformation by (\a dx, \a dy).
+
+ \sa setMatrix(), matrix(), rotate(), shear()
+*/
+void QGraphicsView::translate(qreal dx, qreal dy)
+{
+ Q_D(QGraphicsView);
+ QTransform matrix = d->matrix;
+ matrix.translate(dx, dy);
+ setTransform(matrix);
+}
+
+/*!
+ Scrolls the contents of the viewport to ensure that the scene
+ coordinate \a pos, is centered in the view.
+
+ Because \a pos is a floating point coordinate, and the scroll bars operate
+ on integer coordinates, the centering is only an approximation.
+
+ \note If the item is close to or outside the border, it will be visible
+ in the view, but not centered.
+
+ \sa ensureVisible()
+*/
+void QGraphicsView::centerOn(const QPointF &pos)
+{
+ Q_D(QGraphicsView);
+ qreal width = viewport()->width();
+ qreal height = viewport()->height();
+ QPointF viewPoint = d->matrix.map(pos);
+ QPointF oldCenterPoint = pos;
+
+ if (!d->leftIndent) {
+ if (isRightToLeft()) {
+ qint64 horizontal = 0;
+ horizontal += horizontalScrollBar()->minimum();
+ horizontal += horizontalScrollBar()->maximum();
+ horizontal -= int(viewPoint.x() - width / 2.0);
+ horizontalScrollBar()->setValue(horizontal);
+ } else {
+ horizontalScrollBar()->setValue(int(viewPoint.x() - width / 2.0));
+ }
+ }
+ if (!d->topIndent)
+ verticalScrollBar()->setValue(int(viewPoint.y() - height / 2.0));
+ d->lastCenterPoint = oldCenterPoint;
+}
+
+/*!
+ \fn QGraphicsView::centerOn(qreal x, qreal y)
+ \overload
+
+ This function is provided for convenience. It's equivalent to calling
+ centerOn(QPointF(\a x, \a y)).
+*/
+
+/*!
+ \overload
+
+ Scrolls the contents of the viewport to ensure that \a item
+ is centered in the view.
+
+ \sa ensureVisible()
+*/
+void QGraphicsView::centerOn(const QGraphicsItem *item)
+{
+ centerOn(item->sceneBoundingRect().center());
+}
+
+/*!
+ Scrolls the contents of the viewport so that the scene rectangle \a rect
+ is visible, with margins specified in pixels by \a xmargin and \a
+ ymargin. If the specified rect cannot be reached, the contents are
+ scrolled to the nearest valid position. The default value for both margins
+ is 50 pixels.
+
+ \sa centerOn()
+*/
+void QGraphicsView::ensureVisible(const QRectF &rect, int xmargin, int ymargin)
+{
+ Q_D(QGraphicsView);
+ Q_UNUSED(xmargin);
+ Q_UNUSED(ymargin);
+ qreal width = viewport()->width();
+ qreal height = viewport()->height();
+ QRectF viewRect = d->matrix.mapRect(rect);
+
+ qreal left = d->horizontalScroll();
+ qreal right = left + width;
+ qreal top = d->verticalScroll();
+ qreal bottom = top + height;
+
+ if (viewRect.left() <= left + xmargin) {
+ // need to scroll from the left
+ if (!d->leftIndent)
+ horizontalScrollBar()->setValue(int(viewRect.left() - xmargin - 0.5));
+ }
+ if (viewRect.right() >= right - xmargin) {
+ // need to scroll from the right
+ if (!d->leftIndent)
+ horizontalScrollBar()->setValue(int(viewRect.right() - width + xmargin + 0.5));
+ }
+ if (viewRect.top() <= top + ymargin) {
+ // need to scroll from the top
+ if (!d->topIndent)
+ verticalScrollBar()->setValue(int(viewRect.top() - ymargin - 0.5));
+ }
+ if (viewRect.bottom() >= bottom - ymargin) {
+ // need to scroll from the bottom
+ if (!d->topIndent)
+ verticalScrollBar()->setValue(int(viewRect.bottom() - height + ymargin + 0.5));
+ }
+}
+
+/*!
+ \fn QGraphicsView::ensureVisible(qreal x, qreal y, qreal w, qreal h,
+ int xmargin, int ymargin)
+ \overload
+
+ This function is provided for convenience. It's equivalent to calling
+ ensureVisible(QRectF(\a x, \a y, \a w, \a h), \a xmargin, \a ymargin).
+*/
+
+/*!
+ \overload
+
+ Scrolls the contents of the viewport so that the center of item \a item is
+ visible, with margins specified in pixels by \a xmargin and \a ymargin. If
+ the specified point cannot be reached, the contents are scrolled to the
+ nearest valid position. The default value for both margins is 50 pixels.
+
+ \sa centerOn()
+*/
+void QGraphicsView::ensureVisible(const QGraphicsItem *item, int xmargin, int ymargin)
+{
+ ensureVisible(item->sceneBoundingRect(), xmargin, ymargin);
+}
+
+/*!
+ Scales the view matrix and scrolls the scroll bars to ensure that the
+ scene rectangle \a rect fits inside the viewport. \a rect must be inside
+ the scene rect; otherwise, fitInView() cannot guarantee that the whole
+ rect is visible.
+
+ This function keeps the view's rotation, translation, or shear. The view
+ is scaled according to \a aspectRatioMode. \a rect will be centered in the
+ view if it does not fit tightly.
+
+ It's common to call fitInView() from inside a reimplementation of
+ resizeEvent(), to ensure that the whole scene, or parts of the scene,
+ scales automatically to fit the new size of the viewport as the view is
+ resized. Note though, that calling fitInView() from inside resizeEvent()
+ can lead to unwanted resize recursion, if the new transformation toggles
+ the automatic state of the scrollbars. You can toggle the scrollbar
+ policies to always on or always off to prevent this (see
+ horizontalScrollBarPolicy() and verticalScrollBarPolicy()).
+
+ If \a rect is empty, or if the viewport is too small, this
+ function will do nothing.
+
+ \sa setMatrix(), ensureVisible(), centerOn()
+*/
+void QGraphicsView::fitInView(const QRectF &rect, Qt::AspectRatioMode aspectRatioMode)
+{
+ Q_D(QGraphicsView);
+ if (!d->scene || rect.isNull())
+ return;
+
+ // Reset the view scale to 1:1.
+ QRectF unity = d->matrix.mapRect(QRectF(0, 0, 1, 1));
+ if (unity.isEmpty())
+ return;
+ scale(1 / unity.width(), 1 / unity.height());
+
+ // Find the ideal x / y scaling ratio to fit \a rect in the view.
+ int margin = 2;
+ QRectF viewRect = viewport()->rect().adjusted(margin, margin, -margin, -margin);
+ if (viewRect.isEmpty())
+ return;
+ QRectF sceneRect = d->matrix.mapRect(rect);
+ if (sceneRect.isEmpty())
+ return;
+ qreal xratio = viewRect.width() / sceneRect.width();
+ qreal yratio = viewRect.height() / sceneRect.height();
+
+ // Respect the aspect ratio mode.
+ switch (aspectRatioMode) {
+ case Qt::KeepAspectRatio:
+ xratio = yratio = qMin(xratio, yratio);
+ break;
+ case Qt::KeepAspectRatioByExpanding:
+ xratio = yratio = qMax(xratio, yratio);
+ break;
+ case Qt::IgnoreAspectRatio:
+ break;
+ }
+
+ // Scale and center on the center of \a rect.
+ scale(xratio, yratio);
+ centerOn(rect.center());
+}
+
+/*!
+ \fn void QGraphicsView::fitInView(qreal x, qreal y, qreal w, qreal h,
+ Qt::AspectRatioMode aspectRatioMode = Qt::IgnoreAspectRatio)
+
+ \overload
+
+ This convenience function is equivalent to calling
+ fitInView(QRectF(\a x, \a y, \a w, \a h), \a aspectRatioMode).
+
+ \sa ensureVisible(), centerOn()
+*/
+
+/*!
+ \overload
+
+ Ensures that \a item fits tightly inside the view, scaling the view
+ according to \a aspectRatioMode.
+
+ \sa ensureVisible(), centerOn()
+*/
+void QGraphicsView::fitInView(const QGraphicsItem *item, Qt::AspectRatioMode aspectRatioMode)
+{
+ QPainterPath path = item->isClipped() ? item->clipPath() : item->shape();
+ fitInView(item->sceneTransform().map(path).boundingRect(), aspectRatioMode);
+}
+
+/*!
+ Renders the \a source rect, which is in view coordinates, from the scene
+ into \a target, which is in paint device coordinates, using \a
+ painter. This function is useful for capturing the contents of the view
+ onto a paint device, such as a QImage (e.g., to take a screenshot), or for
+ printing to QPrinter. For example:
+
+ \snippet doc/src/snippets/code/src_gui_graphicsview_qgraphicsview.cpp 4
+
+ If \a source is a null rect, this function will use viewport()->rect() to
+ determine what to draw. If \a target is a null rect, the full dimensions
+ of \a painter's paint device (e.g., for a QPrinter, the page size) will be
+ used.
+
+ The source rect contents will be transformed according to \a
+ aspectRatioMode to fit into the target rect. By default, the aspect ratio
+ is kept, and \a source is scaled to fit in \a target.
+
+ \sa QGraphicsScene::render()
+*/
+void QGraphicsView::render(QPainter *painter, const QRectF &target, const QRect &source,
+ Qt::AspectRatioMode aspectRatioMode)
+{
+ Q_D(QGraphicsView);
+ if (!d->scene || !(painter && painter->isActive()))
+ return;
+
+ // Default source rect = viewport rect
+ QRect sourceRect = source;
+ if (source.isNull())
+ sourceRect = viewport()->rect();
+
+ // Default target rect = device rect
+ QRectF targetRect = target;
+ if (target.isNull()) {
+ if (painter->device()->devType() == QInternal::Picture)
+ targetRect = sourceRect;
+ else
+ targetRect.setRect(0, 0, painter->device()->width(), painter->device()->height());
+ }
+
+ // Find the ideal x / y scaling ratio to fit \a source into \a target.
+ qreal xratio = targetRect.width() / sourceRect.width();
+ qreal yratio = targetRect.height() / sourceRect.height();
+
+ // Scale according to the aspect ratio mode.
+ switch (aspectRatioMode) {
+ case Qt::KeepAspectRatio:
+ xratio = yratio = qMin(xratio, yratio);
+ break;
+ case Qt::KeepAspectRatioByExpanding:
+ xratio = yratio = qMax(xratio, yratio);
+ break;
+ case Qt::IgnoreAspectRatio:
+ break;
+ }
+
+ // Find all items to draw, and reverse the list (we want to draw
+ // in reverse order).
+ QPolygonF sourceScenePoly = mapToScene(sourceRect.adjusted(-1, -1, 1, 1));
+ QList<QGraphicsItem *> itemList = d->scene->items(sourceScenePoly,
+ Qt::IntersectsItemBoundingRect);
+ QGraphicsItem **itemArray = new QGraphicsItem *[itemList.size()];
+ int numItems = itemList.size();
+ for (int i = 0; i < numItems; ++i)
+ itemArray[numItems - i - 1] = itemList.at(i);
+ itemList.clear();
+
+ // Setup painter matrix.
+ QTransform moveMatrix;
+ moveMatrix.translate(-d->horizontalScroll(), -d->verticalScroll());
+ QTransform painterMatrix = d->matrix * moveMatrix;
+ painterMatrix *= QTransform()
+ .translate(targetRect.left(), targetRect.top())
+ .scale(xratio, yratio)
+ .translate(-sourceRect.left(), -sourceRect.top());
+
+ // Two unit vectors.
+ QLineF v1(0, 0, 1, 0);
+ QLineF v2(0, 0, 0, 1);
+
+ // Generate the style options
+ QStyleOptionGraphicsItem *styleOptionArray = d->allocStyleOptionsArray(numItems);
+ QStyleOptionGraphicsItem* option = styleOptionArray;
+ for (int i = 0; i < numItems; ++i, ++option) {
+ QGraphicsItem *item = itemArray[i];
+
+ option->state = QStyle::State_None;
+ option->rect = item->boundingRect().toRect();
+ if (item->isSelected())
+ option->state |= QStyle::State_Selected;
+ if (item->isEnabled())
+ option->state |= QStyle::State_Enabled;
+ if (item->hasFocus())
+ option->state |= QStyle::State_HasFocus;
+ if (d->scene->d_func()->hoverItems.contains(item))
+ option->state |= QStyle::State_MouseOver;
+ if (item == d->scene->mouseGrabberItem())
+ option->state |= QStyle::State_Sunken;
+
+ // Calculate a simple level-of-detail metric.
+ // ### almost identical code in QGraphicsScene::render()
+ // and QGraphicsView::paintEvent() - consider refactoring
+ QTransform itemToViewportTransform;
+ if (item->d_ptr->itemIsUntransformable()) {
+ itemToViewportTransform = item->deviceTransform(painterMatrix);
+ } else {
+ itemToViewportTransform = item->sceneTransform() * painterMatrix;
+ }
+
+ option->levelOfDetail = qSqrt(itemToViewportTransform.map(v1).length() * itemToViewportTransform.map(v2).length());
+ option->matrix = itemToViewportTransform.toAffine();
+
+ option->exposedRect = item->boundingRect();
+ option->exposedRect &= itemToViewportTransform.inverted().mapRect(targetRect);
+ }
+
+ painter->save();
+
+ // Clip in device coordinates to avoid QRegion transformations.
+ painter->setClipRect(targetRect);
+ QPainterPath path;
+ path.addPolygon(sourceScenePoly);
+ path.closeSubpath();
+ painter->setClipPath(painterMatrix.map(path), Qt::IntersectClip);
+
+ // Transform the painter.
+ painter->setTransform(painterMatrix, true);
+
+ // Render the scene.
+ QRectF sourceSceneRect = sourceScenePoly.boundingRect();
+ drawBackground(painter, sourceSceneRect);
+ drawItems(painter, numItems, itemArray, styleOptionArray);
+ drawForeground(painter, sourceSceneRect);
+
+ delete [] itemArray;
+ d->freeStyleOptionsArray(styleOptionArray);
+
+ painter->restore();
+}
+
+/*!
+ Returns a list of all the items in the associated scene.
+
+ \sa QGraphicsScene::items()
+*/
+QList<QGraphicsItem *> QGraphicsView::items() const
+{
+ Q_D(const QGraphicsView);
+ if (!d->scene)
+ return QList<QGraphicsItem *>();
+ return d->scene->items();
+}
+
+/*!
+ Returns all items in the area \a path, which is in viewport coordinates,
+ also taking untransformable items into consideration. This function is
+ considerably slower than just checking the scene directly. There is
+ certainly room for improvement.
+*/
+QList<QGraphicsItem *> QGraphicsViewPrivate::itemsInArea(const QPainterPath &path,
+ Qt::ItemSelectionMode mode) const
+{
+ Q_Q(const QGraphicsView);
+
+ // Determine the size of the largest untransformable subtree of children
+ // mapped to scene coordinates.
+ QRectF untr = scene->d_func()->largestUntransformableItem;
+ QRectF ltri = matrix.inverted().mapRect(untr);
+ ltri.adjust(-untr.width(), -untr.height(), untr.width(), untr.height());
+
+ QRectF rect = path.controlPointRect();
+
+ // Find all possible items in the relevant area.
+ // ### Improve this algorithm; it might be searching a too large area.
+ QRectF adjustedRect = q->mapToScene(rect.adjusted(-1, -1, 1, 1).toRect()).boundingRect();
+ adjustedRect.adjust(-ltri.width(), -ltri.height(), ltri.width(), ltri.height());
+
+ // First build a (potentially large) list of all items in the vicinity
+ // that might be untransformable.
+ QList<QGraphicsItem *> allCandidates = scene->d_func()->estimateItemsInRect(adjustedRect);
+
+ // Then find the minimal list of items that are inside \a path, and
+ // convert it to a set.
+ QList<QGraphicsItem *> regularCandidates = scene->items(q->mapToScene(path), mode);
+ QSet<QGraphicsItem *> candSet = QSet<QGraphicsItem *>::fromList(regularCandidates);
+
+ QTransform viewMatrix = q->viewportTransform();
+
+ QList<QGraphicsItem *> result;
+
+ // Run through all candidates and keep all items that are in candSet, or
+ // are untransformable and collide with \a path. ### We can improve this
+ // algorithm.
+ QList<QGraphicsItem *>::Iterator it = allCandidates.begin();
+ while (it != allCandidates.end()) {
+ QGraphicsItem *item = *it;
+ if (item->d_ptr->itemIsUntransformable()) {
+ // Check if this untransformable item collides with the
+ // original selection rect.
+ QTransform itemTransform = item->deviceTransform(viewMatrix);
+ if (QGraphicsScenePrivate::itemCollidesWithPath(item, itemTransform.inverted().map(path), mode))
+ result << item;
+ } else {
+ if (candSet.contains(item))
+ result << item;
+ }
+ ++it;
+ }
+
+ // ### Insertion sort would be faster.
+ QGraphicsScenePrivate::sortItems(&result, Qt::AscendingOrder,
+ scene->d_func()->sortCacheEnabled);
+ return result;
+}
+
+/*!
+ Returns a list of all the items at the position \a pos in the view. The
+ items are listed in descending Z order (i.e., the first item in the list
+ is the top-most item, and the last item is the bottom-most item). \a pos
+ is in viewport coordinates.
+
+ This function is most commonly called from within mouse event handlers in
+ a subclass in QGraphicsView. \a pos is in untransformed viewport
+ coordinates, just like QMouseEvent::pos().
+
+ \snippet doc/src/snippets/code/src_gui_graphicsview_qgraphicsview.cpp 5
+
+ \sa QGraphicsScene::items(), QGraphicsItem::zValue()
+*/
+QList<QGraphicsItem *> QGraphicsView::items(const QPoint &pos) const
+{
+ Q_D(const QGraphicsView);
+ if (!d->scene)
+ return QList<QGraphicsItem *>();
+ if (d->scene->d_func()->largestUntransformableItem.isNull()) {
+ if ((d->identityMatrix || d->matrix.type() <= QTransform::TxScale)) {
+ QTransform xinv = viewportTransform().inverted();
+ return d->scene->items(xinv.mapRect(QRectF(pos.x(), pos.y(), 1, 1)));
+ }
+ return d->scene->items(mapToScene(pos.x(), pos.y(), 2, 2));
+ }
+
+ QPainterPath path;
+ path.addRect(QRectF(pos.x(), pos.y(), 1, 1));
+ return d->itemsInArea(path);
+}
+
+/*!
+ \fn QGraphicsView::items(int x, int y) const
+
+ This function is provided for convenience. It's equivalent to calling
+ items(QPoint(\a x, \a y)).
+*/
+
+/*!
+ \overload
+
+ Returns a list of all the items that, depending on \a mode, are either
+ contained by or intersect with \a rect. \a rect is in viewport
+ coordinates.
+
+ The default value for \a mode is Qt::IntersectsItemShape; all items whose
+ exact shape intersects with or is contained by \a rect are returned.
+
+ \sa itemAt(), items(), mapToScene()
+*/
+QList<QGraphicsItem *> QGraphicsView::items(const QRect &rect, Qt::ItemSelectionMode mode) const
+{
+ Q_D(const QGraphicsView);
+ if (!d->scene)
+ return QList<QGraphicsItem *>();
+ if (d->scene->d_func()->largestUntransformableItem.isNull())
+ return d->scene->items(mapToScene(rect), mode);
+
+ QPainterPath path;
+ path.addRect(rect);
+ return d->itemsInArea(path);
+}
+
+/*!
+ \fn QList<QGraphicsItem *> QGraphicsView::items(int x, int y, int w, int h, Qt::ItemSelectionMode mode) const
+ \since 4.3
+
+ This convenience function is equivalent to calling items(QRectF(\a x, \a
+ y, \a w, \a h), \a mode).
+*/
+
+/*!
+ \overload
+
+ Returns a list of all the items that, depending on \a mode, are either
+ contained by or intersect with \a polygon. \a polygon is in viewport
+ coordinates.
+
+ The default value for \a mode is Qt::IntersectsItemShape; all items whose
+ exact shape intersects with or is contained by \a polygon are returned.
+
+ \sa itemAt(), items(), mapToScene()
+*/
+QList<QGraphicsItem *> QGraphicsView::items(const QPolygon &polygon, Qt::ItemSelectionMode mode) const
+{
+ Q_D(const QGraphicsView);
+ if (!d->scene)
+ return QList<QGraphicsItem *>();
+ if (d->scene->d_func()->largestUntransformableItem.isNull())
+ return d->scene->items(mapToScene(polygon), mode);
+
+ QPainterPath path;
+ path.addPolygon(polygon);
+ path.closeSubpath();
+ return d->itemsInArea(path);
+}
+
+/*!
+ \overload
+
+ Returns a list of all the items that, depending on \a mode, are either
+ contained by or intersect with \a path. \a path is in viewport
+ coordinates.
+
+ The default value for \a mode is Qt::IntersectsItemShape; all items whose
+ exact shape intersects with or is contained by \a path are returned.
+
+ \sa itemAt(), items(), mapToScene()
+*/
+QList<QGraphicsItem *> QGraphicsView::items(const QPainterPath &path, Qt::ItemSelectionMode mode) const
+{
+ Q_D(const QGraphicsView);
+ if (!d->scene)
+ return QList<QGraphicsItem *>();
+ if (d->scene->d_func()->largestUntransformableItem.isNull())
+ return d->scene->items(mapToScene(path), mode);
+ return d->itemsInArea(path);
+}
+
+/*!
+ Returns the item at position \a pos, which is in viewport coordinates.
+ If there are several items at this position, this function returns
+ the topmost item.
+
+ Example:
+
+ \snippet doc/src/snippets/code/src_gui_graphicsview_qgraphicsview.cpp 6
+
+ \sa items()
+*/
+QGraphicsItem *QGraphicsView::itemAt(const QPoint &pos) const
+{
+ Q_D(const QGraphicsView);
+ if (!d->scene)
+ return 0;
+ QList<QGraphicsItem *> itemsAtPos = items(pos);
+ return itemsAtPos.isEmpty() ? 0 : itemsAtPos.first();
+}
+
+/*!
+ \overload
+ \fn QGraphicsItem *QGraphicsView::itemAt(int x, int y) const
+
+ This function is provided for convenience. It's equivalent to
+ calling itemAt(QPoint(\a x, \a y)).
+*/
+
+/*!
+ Returns the viewport coordinate \a point mapped to scene coordinates.
+
+ Note: It can be useful to map the whole rectangle covered by the pixel at
+ \a point instead of the point itself. To do this, you can call
+ mapToScene(QRect(\a point, QSize(2, 2))).
+
+ \sa mapFromScene()
+*/
+QPointF QGraphicsView::mapToScene(const QPoint &point) const
+{
+ Q_D(const QGraphicsView);
+ QPointF p = point;
+ p.rx() += d->horizontalScroll();
+ p.ry() += d->verticalScroll();
+ return d->identityMatrix ? p : d->matrix.inverted().map(p);
+}
+
+/*!
+ \fn QGraphicsView::mapToScene(int x, int y) const
+
+ This function is provided for convenience. It's equivalent to calling
+ mapToScene(QPoint(\a x, \a y)).
+*/
+
+/*!
+ Returns the viewport rectangle \a rect mapped to a scene coordinate
+ polygon.
+
+ \sa mapFromScene()
+*/
+QPolygonF QGraphicsView::mapToScene(const QRect &rect) const
+{
+ Q_D(const QGraphicsView);
+ if (!rect.isValid())
+ return QPolygonF();
+
+ QPointF scrollOffset(d->horizontalScroll(), d->verticalScroll());
+ QPointF tl = scrollOffset + rect.topLeft();
+ QPointF tr = scrollOffset + rect.topRight();
+ QPointF br = scrollOffset + rect.bottomRight();
+ QPointF bl = scrollOffset + rect.bottomLeft();
+
+ QPolygonF poly;
+ poly.resize(4);
+ if (!d->identityMatrix) {
+ QTransform x = d->matrix.inverted();
+ poly[0] = x.map(tl);
+ poly[1] = x.map(tr);
+ poly[2] = x.map(br);
+ poly[3] = x.map(bl);
+ } else {
+ poly[0] = tl;
+ poly[1] = tr;
+ poly[2] = br;
+ poly[3] = bl;
+ }
+ return poly;
+}
+
+/*!
+ \fn QGraphicsView::mapToScene(int x, int y, int w, int h) const
+
+ This function is provided for convenience. It's equivalent to calling
+ mapToScene(QRect(\a x, \a y, \a w, \a h)).
+*/
+
+/*!
+ Returns the viewport polygon \a polygon mapped to a scene coordinate
+ polygon.
+
+ \sa mapFromScene()
+*/
+QPolygonF QGraphicsView::mapToScene(const QPolygon &polygon) const
+{
+ QPolygonF poly;
+ foreach (const QPoint &point, polygon)
+ poly << mapToScene(point);
+ return poly;
+}
+
+/*!
+ Returns the viewport painter path \a path mapped to a scene coordinate
+ painter path.
+
+ \sa mapFromScene()
+*/
+QPainterPath QGraphicsView::mapToScene(const QPainterPath &path) const
+{
+ Q_D(const QGraphicsView);
+ QTransform moveMatrix;
+ moveMatrix.translate(d->horizontalScroll(), d->verticalScroll());
+ return (moveMatrix * d->matrix.inverted()).map(path);
+}
+
+/*!
+ Returns the scene coordinate \a point to viewport coordinates.
+
+ \sa mapToScene()
+*/
+QPoint QGraphicsView::mapFromScene(const QPointF &point) const
+{
+ Q_D(const QGraphicsView);
+ QPointF p = d->identityMatrix ? point : d->matrix.map(point);
+ p.rx() -= d->horizontalScroll();
+ p.ry() -= d->verticalScroll();
+ return p.toPoint();
+}
+
+/*!
+ \fn QGraphicsView::mapFromScene(qreal x, qreal y) const
+
+ This function is provided for convenience. It's equivalent to
+ calling mapFromScene(QPointF(\a x, \a y)).
+*/
+
+/*!
+ Returns the scene rectangle \a rect to a viewport coordinate
+ polygon.
+
+ \sa mapToScene()
+*/
+QPolygon QGraphicsView::mapFromScene(const QRectF &rect) const
+{
+ Q_D(const QGraphicsView);
+ QPointF tl;
+ QPointF tr;
+ QPointF br;
+ QPointF bl;
+ if (!d->identityMatrix) {
+ const QTransform &x = d->matrix;
+ tl = x.map(rect.topLeft());
+ tr = x.map(rect.topRight());
+ br = x.map(rect.bottomRight());
+ bl = x.map(rect.bottomLeft());
+ } else {
+ tl = rect.topLeft();
+ tr = rect.topRight();
+ br = rect.bottomRight();
+ bl = rect.bottomLeft();
+ }
+ QPointF scrollOffset(d->horizontalScroll(), d->verticalScroll());
+ tl -= scrollOffset;
+ tr -= scrollOffset;
+ br -= scrollOffset;
+ bl -= scrollOffset;
+
+ QPolygon poly;
+ poly.resize(4);
+ poly[0] = tl.toPoint();
+ poly[1] = tr.toPoint();
+ poly[2] = br.toPoint();
+ poly[3] = bl.toPoint();
+ return poly;
+}
+
+/*!
+ \fn QGraphicsView::mapFromScene(qreal x, qreal y, qreal w, qreal h) const
+
+ This function is provided for convenience. It's equivalent to
+ calling mapFromScene(QRectF(\a x, \a y, \a w, \a h)).
+*/
+
+/*!
+ Returns the scene coordinate polygon \a polygon to a viewport coordinate
+ polygon.
+
+ \sa mapToScene()
+*/
+QPolygon QGraphicsView::mapFromScene(const QPolygonF &polygon) const
+{
+ QPolygon poly;
+ foreach (const QPointF &point, polygon)
+ poly << mapFromScene(point);
+ return poly;
+}
+
+/*!
+ Returns the scene coordinate painter path \a path to a viewport coordinate
+ painter path.
+
+ \sa mapToScene()
+*/
+QPainterPath QGraphicsView::mapFromScene(const QPainterPath &path) const
+{
+ Q_D(const QGraphicsView);
+ QTransform moveMatrix;
+ moveMatrix.translate(-d->horizontalScroll(), -d->verticalScroll());
+ return (d->matrix * moveMatrix).map(path);
+}
+
+/*!
+ \reimp
+*/
+QVariant QGraphicsView::inputMethodQuery(Qt::InputMethodQuery query) const
+{
+ Q_D(const QGraphicsView);
+ if (!d->scene)
+ return QVariant();
+
+ QVariant value = d->scene->inputMethodQuery(query);
+ if (value.type() == QVariant::RectF)
+ value = mapFromScene(value.toRectF()).boundingRect();
+ else if (value.type() == QVariant::PointF)
+ value = mapFromScene(value.toPointF());
+ else if (value.type() == QVariant::Rect)
+ value = mapFromScene(value.toRect()).boundingRect();
+ else if (value.type() == QVariant::Point)
+ value = mapFromScene(value.toPoint());
+ return value;
+}
+
+/*!
+ \property QGraphicsView::backgroundBrush
+ \brief the background brush of the scene.
+
+ This property sets the background brush for the scene in this view. It is
+ used to override the scene's own background, and defines the behavior of
+ drawBackground(). To provide custom background drawing for this view, you
+ can reimplement drawBackground() instead.
+
+ By default, this property contains a brush with the Qt::NoBrush pattern.
+
+ \sa QGraphicsScene::backgroundBrush, foregroundBrush
+*/
+QBrush QGraphicsView::backgroundBrush() const
+{
+ Q_D(const QGraphicsView);
+ return d->backgroundBrush;
+}
+void QGraphicsView::setBackgroundBrush(const QBrush &brush)
+{
+ Q_D(QGraphicsView);
+ d->backgroundBrush = brush;
+ viewport()->update();
+
+ if (d->cacheMode & CacheBackground) {
+ // Invalidate the background pixmap
+ d->mustResizeBackgroundPixmap = true;
+ }
+}
+
+/*!
+ \property QGraphicsView::foregroundBrush
+ \brief the foreground brush of the scene.
+
+ This property sets the foreground brush for the scene in this view. It is
+ used to override the scene's own foreground, and defines the behavior of
+ drawForeground(). To provide custom foreground drawing for this view, you
+ can reimplement drawForeground() instead.
+
+ By default, this property contains a brush with the Qt::NoBrush pattern.
+
+ \sa QGraphicsScene::foregroundBrush, backgroundBrush
+*/
+QBrush QGraphicsView::foregroundBrush() const
+{
+ Q_D(const QGraphicsView);
+ return d->foregroundBrush;
+}
+void QGraphicsView::setForegroundBrush(const QBrush &brush)
+{
+ Q_D(QGraphicsView);
+ d->foregroundBrush = brush;
+ viewport()->update();
+}
+
+/*!
+ Schedules an update of the scene rectangles \a rects.
+
+ \sa QGraphicsScene::changed()
+*/
+void QGraphicsView::updateScene(const QList<QRectF> &rects)
+{
+ // ### Note: Since 4.5, this slot is only called if the user explicitly
+ // establishes a connection between the scene and the view, as the scene
+ // and view are no longer connected. We need to keep it working (basically
+ // leave it as it is), but the new delivery path is through
+ // QGraphicsScenePrivate::itemUpdate().
+ Q_D(QGraphicsView);
+ if (d->fullUpdatePending || d->viewportUpdateMode == QGraphicsView::NoViewportUpdate)
+ return;
+
+ // Extract and reset dirty scene rect info.
+ QVector<QRect> dirtyViewportRects;
+ for (int i = 0; i < d->dirtyRegions.size(); ++i)
+ dirtyViewportRects += d->dirtyRegions.at(i).rects();
+ d->dirtyRegions.clear();
+
+ bool fullUpdate = !d->accelerateScrolling || d->viewportUpdateMode == QGraphicsView::FullViewportUpdate;
+ bool boundingRectUpdate = (d->viewportUpdateMode == QGraphicsView::BoundingRectViewportUpdate)
+ || (d->viewportUpdateMode == QGraphicsView::SmartViewportUpdate
+ && ((dirtyViewportRects.size() + rects.size()) >= QGRAPHICSVIEW_REGION_RECT_THRESHOLD));
+
+ QRegion updateRegion;
+ QRect boundingRect;
+ QRect viewportRect = viewport()->rect();
+ bool redraw = false;
+ QTransform transform = viewportTransform();
+
+ // Convert scene rects to viewport rects.
+ foreach (const QRectF &rect, rects) {
+ QRect xrect = transform.mapRect(rect).toRect();
+ if (!(d->optimizationFlags & DontAdjustForAntialiasing))
+ xrect.adjust(-2, -2, 2, 2);
+ if (!viewportRect.intersects(xrect))
+ continue;
+ dirtyViewportRects << xrect;
+ }
+
+ foreach (const QRect &rect, dirtyViewportRects) {
+ // Add the exposed rect to the update region. In rect update
+ // mode, we only count the bounding rect of items.
+ if (!boundingRectUpdate) {
+ updateRegion += rect;
+ } else {
+ boundingRect |= rect;
+ }
+ redraw = true;
+ if (fullUpdate) {
+ // If fullUpdate is true and we found a visible dirty rect,
+ // we're done.
+ break;
+ }
+ }
+
+ if (!redraw)
+ return;
+
+ if (fullUpdate)
+ viewport()->update();
+ else if (boundingRectUpdate)
+ viewport()->update(boundingRect);
+ else
+ viewport()->update(updateRegion);
+}
+
+/*!
+ Notifies QGraphicsView that the scene's scene rect has changed. \a rect
+ is the new scene rect. If the view already has an explicitly set scene
+ rect, this function does nothing.
+
+ \sa sceneRect, QGraphicsScene::sceneRectChanged()
+*/
+void QGraphicsView::updateSceneRect(const QRectF &rect)
+{
+ Q_D(QGraphicsView);
+ if (!d->hasSceneRect) {
+ d->sceneRect = rect;
+ d->recalculateContentSize();
+ }
+}
+
+/*!
+ This slot is called by QAbstractScrollArea after setViewport() has been
+ called. Reimplement this function in a subclass of QGraphicsView to
+ initialize the new viewport \a widget before it is used.
+
+ \sa setViewport()
+*/
+void QGraphicsView::setupViewport(QWidget *widget)
+{
+ Q_D(QGraphicsView);
+
+ if (!widget) {
+ qWarning("QGraphicsView::setupViewport: cannot initialize null widget");
+ return;
+ }
+
+ const bool isGLWidget = widget->inherits("QGLWidget");
+
+ d->accelerateScrolling = !(isGLWidget
+ || widget->testAttribute(Qt::WA_MSWindowsUseDirect3D)
+ || qApp->testAttribute(Qt::AA_MSWindowsUseDirect3DByDefault));
+
+ widget->setFocusPolicy(Qt::StrongFocus);
+
+ if (!isGLWidget) {
+ // autoFillBackground enables scroll acceleration.
+ widget->setAutoFillBackground(true);
+ }
+
+ widget->setMouseTracking(true);
+ widget->setAcceptDrops(acceptDrops());
+}
+
+/*!
+ \reimp
+*/
+bool QGraphicsView::event(QEvent *event)
+{
+ Q_D(QGraphicsView);
+
+ if (d->sceneInteractionAllowed) {
+ switch (event->type()) {
+ case QEvent::ShortcutOverride:
+ if (d->scene)
+ return QApplication::sendEvent(d->scene, event);
+ break;
+ case QEvent::KeyPress:
+ if (d->scene) {
+ QKeyEvent *k = static_cast<QKeyEvent *>(event);
+ if (k->key() == Qt::Key_Tab || k->key() == Qt::Key_Backtab) {
+ // Send the key events to the scene. This will invoke the
+ // scene's tab focus handling, and if the event is
+ // accepted, we return (prevent further event delivery),
+ // and the base implementation will call QGraphicsView's
+ // focusNextPrevChild() function. If the event is ignored,
+ // we fall back to standard tab focus handling.
+ QApplication::sendEvent(d->scene, event);
+ if (event->isAccepted())
+ return true;
+ // Ensure the event doesn't propagate just because the
+ // scene ignored it. If the event propagates, then tab
+ // handling will be called twice (this and parent).
+ event->accept();
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ return QAbstractScrollArea::event(event);
+}
+
+/*!
+ \reimp
+*/
+bool QGraphicsView::viewportEvent(QEvent *event)
+{
+ Q_D(QGraphicsView);
+
+ if (!d->scene)
+ return QAbstractScrollArea::viewportEvent(event);
+
+ switch (event->type()) {
+ case QEvent::Enter:
+ QApplication::sendEvent(d->scene, event);
+ break;
+ case QEvent::WindowActivate:
+ QApplication::sendEvent(d->scene, event);
+ break;
+ case QEvent::WindowDeactivate:
+ // ### This is a temporary fix for until we get proper mouse
+ // grab events. mouseGrabberItem should be set to 0 if we lose
+ // the mouse grab.
+ // Remove all popups when the scene loses focus.
+ if (!d->scene->d_func()->popupWidgets.isEmpty())
+ d->scene->d_func()->removePopup(d->scene->d_func()->popupWidgets.first());
+ QApplication::sendEvent(d->scene, event);
+ break;
+ case QEvent::Leave:
+ // ### This is a temporary fix for until we get proper mouse grab
+ // events. activeMouseGrabberItem should be set to 0 if we lose the
+ // mouse grab.
+ if ((QApplication::activePopupWidget() && QApplication::activePopupWidget() != window())
+ || (QApplication::activeModalWidget() && QApplication::activeModalWidget() != window())
+ || (QApplication::activeWindow() != window())) {
+ if (!d->scene->d_func()->popupWidgets.isEmpty())
+ d->scene->d_func()->removePopup(d->scene->d_func()->popupWidgets.first());
+ }
+ d->useLastMouseEvent = false;
+ QApplication::sendEvent(d->scene, event);
+ break;
+#ifndef QT_NO_TOOLTIP
+ case QEvent::ToolTip: {
+ QHelpEvent *toolTip = static_cast<QHelpEvent *>(event);
+ QGraphicsSceneHelpEvent helpEvent(QEvent::GraphicsSceneHelp);
+ helpEvent.setWidget(viewport());
+ helpEvent.setScreenPos(toolTip->globalPos());
+ helpEvent.setScenePos(mapToScene(toolTip->pos()));
+ QApplication::sendEvent(d->scene, &helpEvent);
+ toolTip->setAccepted(helpEvent.isAccepted());
+ return true;
+ }
+#endif
+ case QEvent::Paint:
+ // Reset full update
+ d->fullUpdatePending = false;
+ if (d->scene) {
+ // Check if this view reimplements the updateScene slot; if it
+ // does, we can't do direct update delivery and have to fall back
+ // to connecting the changed signal.
+ if (!d->updateSceneSlotReimplementedChecked) {
+ d->updateSceneSlotReimplementedChecked = true;
+ const QMetaObject *mo = metaObject();
+ if (mo != &QGraphicsView::staticMetaObject) {
+ if (mo->indexOfSlot("updateScene(QList<QRectF>)")
+ != QGraphicsView::staticMetaObject.indexOfSlot("updateScene(QList<QRectF>)")) {
+ connect(d->scene, SIGNAL(changed(QList<QRectF>)),
+ this, SLOT(updateScene(QList<QRectF>)));
+ }
+ }
+ }
+ d->scene->d_func()->updateAll = false;
+ }
+ break;
+ default:
+ break;
+ }
+
+ return QAbstractScrollArea::viewportEvent(event);
+}
+
+#ifndef QT_NO_CONTEXTMENU
+/*!
+ \reimp
+*/
+void QGraphicsView::contextMenuEvent(QContextMenuEvent *event)
+{
+ Q_D(QGraphicsView);
+ if (!d->scene || !d->sceneInteractionAllowed)
+ return;
+
+ d->mousePressViewPoint = event->pos();
+ d->mousePressScenePoint = mapToScene(d->mousePressViewPoint);
+ d->mousePressScreenPoint = event->globalPos();
+ d->lastMouseMoveScenePoint = d->mousePressScenePoint;
+ d->lastMouseMoveScreenPoint = d->mousePressScreenPoint;
+
+ QGraphicsSceneContextMenuEvent contextEvent(QEvent::GraphicsSceneContextMenu);
+ contextEvent.setWidget(viewport());
+ contextEvent.setScenePos(d->mousePressScenePoint);
+ contextEvent.setScreenPos(d->mousePressScreenPoint);
+ contextEvent.setModifiers(event->modifiers());
+ contextEvent.setReason((QGraphicsSceneContextMenuEvent::Reason)(event->reason()));
+ contextEvent.setAccepted(event->isAccepted());
+ QApplication::sendEvent(d->scene, &contextEvent);
+ event->setAccepted(contextEvent.isAccepted());
+}
+#endif // QT_NO_CONTEXTMENU
+
+/*!
+ \reimp
+*/
+void QGraphicsView::dropEvent(QDropEvent *event)
+{
+#ifndef QT_NO_DRAGANDDROP
+ Q_D(QGraphicsView);
+ if (!d->scene || !d->sceneInteractionAllowed)
+ return;
+
+ // Generate a scene event.
+ QGraphicsSceneDragDropEvent sceneEvent(QEvent::GraphicsSceneDrop);
+ d->populateSceneDragDropEvent(&sceneEvent, event);
+
+ // Send it to the scene.
+ QApplication::sendEvent(d->scene, &sceneEvent);
+
+ // Accept the originating event if the scene accepted the scene event.
+ event->setAccepted(sceneEvent.isAccepted());
+ if (sceneEvent.isAccepted())
+ event->setDropAction(sceneEvent.dropAction());
+
+ delete d->lastDragDropEvent;
+ d->lastDragDropEvent = 0;
+
+#else
+ Q_UNUSED(event)
+#endif
+}
+
+/*!
+ \reimp
+*/
+void QGraphicsView::dragEnterEvent(QDragEnterEvent *event)
+{
+#ifndef QT_NO_DRAGANDDROP
+ Q_D(QGraphicsView);
+ if (!d->scene || !d->sceneInteractionAllowed)
+ return;
+
+ // Disable replaying of mouse move events.
+ d->useLastMouseEvent = false;
+
+ // Generate a scene event.
+ QGraphicsSceneDragDropEvent sceneEvent(QEvent::GraphicsSceneDragEnter);
+ d->populateSceneDragDropEvent(&sceneEvent, event);
+
+ // Store it for later use.
+ d->storeDragDropEvent(&sceneEvent);
+
+ // Send it to the scene.
+ QApplication::sendEvent(d->scene, &sceneEvent);
+
+ // Accept the originating event if the scene accepted the scene event.
+ if (sceneEvent.isAccepted()) {
+ event->setAccepted(true);
+ event->setDropAction(sceneEvent.dropAction());
+ }
+#else
+ Q_UNUSED(event)
+#endif
+}
+
+/*!
+ \reimp
+*/
+void QGraphicsView::dragLeaveEvent(QDragLeaveEvent *event)
+{
+#ifndef QT_NO_DRAGANDDROP
+ Q_D(QGraphicsView);
+ if (!d->scene || !d->sceneInteractionAllowed)
+ return;
+ if (!d->lastDragDropEvent) {
+ qWarning("QGraphicsView::dragLeaveEvent: drag leave received before drag enter");
+ return;
+ }
+
+ // Generate a scene event.
+ QGraphicsSceneDragDropEvent sceneEvent(QEvent::GraphicsSceneDragLeave);
+ sceneEvent.setScenePos(d->lastDragDropEvent->scenePos());
+ sceneEvent.setScreenPos(d->lastDragDropEvent->screenPos());
+ sceneEvent.setButtons(d->lastDragDropEvent->buttons());
+ sceneEvent.setModifiers(d->lastDragDropEvent->modifiers());
+ sceneEvent.setPossibleActions(d->lastDragDropEvent->possibleActions());
+ sceneEvent.setProposedAction(d->lastDragDropEvent->proposedAction());
+ sceneEvent.setDropAction(d->lastDragDropEvent->dropAction());
+ sceneEvent.setMimeData(d->lastDragDropEvent->mimeData());
+ sceneEvent.setWidget(d->lastDragDropEvent->widget());
+ sceneEvent.setSource(d->lastDragDropEvent->source());
+ delete d->lastDragDropEvent;
+ d->lastDragDropEvent = 0;
+
+ // Send it to the scene.
+ QApplication::sendEvent(d->scene, &sceneEvent);
+
+ // Accept the originating event if the scene accepted the scene event.
+ if (sceneEvent.isAccepted())
+ event->setAccepted(true);
+#else
+ Q_UNUSED(event)
+#endif
+}
+
+/*!
+ \reimp
+*/
+void QGraphicsView::dragMoveEvent(QDragMoveEvent *event)
+{
+#ifndef QT_NO_DRAGANDDROP
+ Q_D(QGraphicsView);
+ if (!d->scene || !d->sceneInteractionAllowed)
+ return;
+
+ // Generate a scene event.
+ QGraphicsSceneDragDropEvent sceneEvent(QEvent::GraphicsSceneDragMove);
+ d->populateSceneDragDropEvent(&sceneEvent, event);
+
+ // Store it for later use.
+ d->storeDragDropEvent(&sceneEvent);
+
+ // Send it to the scene.
+ QApplication::sendEvent(d->scene, &sceneEvent);
+
+ // Ignore the originating event if the scene ignored the scene event.
+ event->setAccepted(sceneEvent.isAccepted());
+ if (sceneEvent.isAccepted())
+ event->setDropAction(sceneEvent.dropAction());
+#else
+ Q_UNUSED(event)
+#endif
+}
+
+/*!
+ \reimp
+*/
+void QGraphicsView::focusInEvent(QFocusEvent *event)
+{
+ Q_D(QGraphicsView);
+ QAbstractScrollArea::focusInEvent(event);
+ if (d->scene)
+ QApplication::sendEvent(d->scene, event);
+ // Pass focus on if the scene cannot accept focus.
+ if (!d->scene || !event->isAccepted())
+ QAbstractScrollArea::focusInEvent(event);
+}
+
+/*!
+ \reimp
+*/
+bool QGraphicsView::focusNextPrevChild(bool next)
+{
+ return QAbstractScrollArea::focusNextPrevChild(next);
+}
+
+/*!
+ \reimp
+*/
+void QGraphicsView::focusOutEvent(QFocusEvent *event)
+{
+ Q_D(QGraphicsView);
+ QAbstractScrollArea::focusOutEvent(event);
+ if (d->scene)
+ QApplication::sendEvent(d->scene, event);
+}
+
+/*!
+ \reimp
+*/
+void QGraphicsView::keyPressEvent(QKeyEvent *event)
+{
+ Q_D(QGraphicsView);
+ if (!d->scene || !d->sceneInteractionAllowed) {
+ QAbstractScrollArea::keyPressEvent(event);
+ return;
+ }
+ QApplication::sendEvent(d->scene, event);
+ if (!event->isAccepted())
+ QAbstractScrollArea::keyPressEvent(event);
+}
+
+/*!
+ \reimp
+*/
+void QGraphicsView::keyReleaseEvent(QKeyEvent *event)
+{
+ Q_D(QGraphicsView);
+ if (!d->scene || !d->sceneInteractionAllowed)
+ return;
+ QApplication::sendEvent(d->scene, event);
+ if (!event->isAccepted())
+ QAbstractScrollArea::keyReleaseEvent(event);
+}
+
+/*!
+ \reimp
+*/
+void QGraphicsView::mouseDoubleClickEvent(QMouseEvent *event)
+{
+ Q_D(QGraphicsView);
+ if (!d->scene || !d->sceneInteractionAllowed)
+ return;
+
+ d->storeMouseEvent(event);
+ d->mousePressViewPoint = event->pos();
+ d->mousePressScenePoint = mapToScene(d->mousePressViewPoint);
+ d->mousePressScreenPoint = event->globalPos();
+ d->lastMouseMoveScenePoint = d->mousePressScenePoint;
+ d->lastMouseMoveScreenPoint = d->mousePressScreenPoint;
+ d->mousePressButton = event->button();
+
+ QGraphicsSceneMouseEvent mouseEvent(QEvent::GraphicsSceneMouseDoubleClick);
+ mouseEvent.setWidget(viewport());
+ mouseEvent.setButtonDownScenePos(d->mousePressButton, d->mousePressScenePoint);
+ mouseEvent.setButtonDownScreenPos(d->mousePressButton, d->mousePressScreenPoint);
+ mouseEvent.setScenePos(mapToScene(d->mousePressViewPoint));
+ mouseEvent.setScreenPos(d->mousePressScreenPoint);
+ mouseEvent.setLastScenePos(d->lastMouseMoveScenePoint);
+ mouseEvent.setLastScreenPos(d->lastMouseMoveScreenPoint);
+ mouseEvent.setButtons(event->buttons());
+ mouseEvent.setButtons(event->buttons());
+ mouseEvent.setAccepted(false);
+ mouseEvent.setButton(event->button());
+ mouseEvent.setModifiers(event->modifiers());
+ QApplication::sendEvent(d->scene, &mouseEvent);
+}
+
+/*!
+ \reimp
+*/
+void QGraphicsView::mousePressEvent(QMouseEvent *event)
+{
+ Q_D(QGraphicsView);
+
+ // Store this event for replaying, finding deltas, and for
+ // scroll-dragging; even in non-interactive mode, scroll hand dragging is
+ // allowed, so we store the event at the very top of this function.
+ d->storeMouseEvent(event);
+ d->lastMouseEvent.setAccepted(false);
+
+ if (d->sceneInteractionAllowed) {
+ // Store some of the event's button-down data.
+ d->mousePressViewPoint = event->pos();
+ d->mousePressScenePoint = mapToScene(d->mousePressViewPoint);
+ d->mousePressScreenPoint = event->globalPos();
+ d->lastMouseMoveScenePoint = d->mousePressScenePoint;
+ d->lastMouseMoveScreenPoint = d->mousePressScreenPoint;
+ d->mousePressButton = event->button();
+
+ if (d->scene) {
+ // Convert and deliver the mouse event to the scene.
+ QGraphicsSceneMouseEvent mouseEvent(QEvent::GraphicsSceneMousePress);
+ mouseEvent.setWidget(viewport());
+ mouseEvent.setButtonDownScenePos(d->mousePressButton, d->mousePressScenePoint);
+ mouseEvent.setButtonDownScreenPos(d->mousePressButton, d->mousePressScreenPoint);
+ mouseEvent.setScenePos(d->mousePressScenePoint);
+ mouseEvent.setScreenPos(d->mousePressScreenPoint);
+ mouseEvent.setLastScenePos(d->lastMouseMoveScenePoint);
+ mouseEvent.setLastScreenPos(d->lastMouseMoveScreenPoint);
+ mouseEvent.setButtons(event->buttons());
+ mouseEvent.setButton(event->button());
+ mouseEvent.setModifiers(event->modifiers());
+ mouseEvent.setAccepted(false);
+ QApplication::sendEvent(d->scene, &mouseEvent);
+
+ // Update the original mouse event accepted state.
+ bool isAccepted = mouseEvent.isAccepted();
+ event->setAccepted(isAccepted);
+
+ // Update the last mouse event accepted state.
+ d->lastMouseEvent.setAccepted(isAccepted);
+
+ if (isAccepted)
+ return;
+ }
+ }
+
+#ifndef QT_NO_RUBBERBAND
+ if (d->dragMode == QGraphicsView::RubberBandDrag && !d->rubberBanding) {
+ if (d->sceneInteractionAllowed) {
+ // Rubberbanding is only allowed in interactive mode.
+ event->accept();
+ d->rubberBanding = true;
+ d->rubberBandRect = QRect();
+ if (d->scene) {
+ // Initiating a rubber band always clears the selection.
+ d->scene->clearSelection();
+ }
+ }
+ } else
+#endif
+ if (d->dragMode == QGraphicsView::ScrollHandDrag && event->button() == Qt::LeftButton) {
+ // Left-button press in scroll hand mode initiates hand scrolling.
+ event->accept();
+ d->handScrolling = true;
+ d->handScrollMotions = 0;
+#ifndef QT_NO_CURSOR
+ viewport()->setCursor(Qt::ClosedHandCursor);
+#endif
+ }
+}
+
+/*!
+ \reimp
+*/
+void QGraphicsView::mouseMoveEvent(QMouseEvent *event)
+{
+ Q_D(QGraphicsView);
+
+#ifndef QT_NO_RUBBERBAND
+ if (d->dragMode == QGraphicsView::RubberBandDrag && d->sceneInteractionAllowed) {
+ d->storeMouseEvent(event);
+ if (d->rubberBanding) {
+ // Check for enough drag distance
+ if ((d->mousePressViewPoint - event->pos()).manhattanLength()
+ < QApplication::startDragDistance()) {
+ return;
+ }
+
+ // Update old rubberband
+ if (d->viewportUpdateMode != QGraphicsView::NoViewportUpdate && !d->rubberBandRect.isNull()) {
+ if (d->viewportUpdateMode != FullViewportUpdate)
+ viewport()->update(d->rubberBandRegion(viewport(), d->rubberBandRect));
+ else
+ viewport()->update();
+ }
+
+ // Stop rubber banding if the user has let go of all buttons (even
+ // if we didn't get the release events).
+ if (!event->buttons()) {
+ d->rubberBanding = false;
+ d->rubberBandRect = QRect();
+ return;
+ }
+
+ // Update rubberband position
+ const QPoint &mp = d->mousePressViewPoint;
+ QPoint ep = event->pos();
+ d->rubberBandRect = QRect(qMin(mp.x(), ep.x()), qMin(mp.y(), ep.y()),
+ qAbs(mp.x() - ep.x()) + 1, qAbs(mp.y() - ep.y()) + 1);
+
+ // Update new rubberband
+ if (d->viewportUpdateMode != QGraphicsView::NoViewportUpdate){
+ if (d->viewportUpdateMode != FullViewportUpdate)
+ viewport()->update(d->rubberBandRegion(viewport(), d->rubberBandRect));
+ else
+ viewport()->update();
+ }
+ // Set the new selection area
+ QPainterPath selectionArea;
+ selectionArea.addPolygon(mapToScene(d->rubberBandRect));
+ selectionArea.closeSubpath();
+ if (d->scene)
+ d->scene->setSelectionArea(selectionArea, d->rubberBandSelectionMode);
+ return;
+ }
+ } else
+#endif // QT_NO_RUBBERBAND
+ if (d->dragMode == QGraphicsView::ScrollHandDrag) {
+ if (d->handScrolling) {
+ QScrollBar *hBar = horizontalScrollBar();
+ QScrollBar *vBar = verticalScrollBar();
+ QPoint delta = event->pos() - d->lastMouseEvent.pos();
+ hBar->setValue(hBar->value() + (isRightToLeft() ? delta.x() : -delta.x()));
+ vBar->setValue(vBar->value() - delta.y());
+
+ // Detect how much we've scrolled to disambiguate scrolling from
+ // clicking.
+ ++d->handScrollMotions;
+ }
+ }
+
+ d->mouseMoveEventHandler(event);
+}
+
+/*!
+ \reimp
+*/
+void QGraphicsView::mouseReleaseEvent(QMouseEvent *event)
+{
+ Q_D(QGraphicsView);
+
+#ifndef QT_NO_RUBBERBAND
+ if (d->dragMode == QGraphicsView::RubberBandDrag && d->sceneInteractionAllowed && !event->buttons()) {
+ if (d->rubberBanding) {
+ if (d->viewportUpdateMode != QGraphicsView::NoViewportUpdate){
+ if (d->viewportUpdateMode != FullViewportUpdate)
+ viewport()->update(d->rubberBandRegion(viewport(), d->rubberBandRect));
+ else
+ viewport()->update();
+ }
+ d->rubberBanding = false;
+ d->rubberBandRect = QRect();
+ }
+ } else
+#endif
+ if (d->dragMode == QGraphicsView::ScrollHandDrag) {
+#ifndef QT_NO_CURSOR
+ // Restore the open hand cursor. ### There might be items
+ // under the mouse that have a valid cursor at this time, so
+ // we could repeat the steps from mouseMoveEvent().
+ viewport()->setCursor(Qt::OpenHandCursor);
+#endif
+ d->handScrolling = false;
+
+ if (d->scene && d->sceneInteractionAllowed && !d->lastMouseEvent.isAccepted() && d->handScrollMotions <= 6) {
+ // If we've detected very little motion during the hand drag, and
+ // no item accepted the last event, we'll interpret that as a
+ // click to the scene, and reset the selection.
+ d->scene->clearSelection();
+ }
+ }
+
+ d->storeMouseEvent(event);
+
+ if (!d->sceneInteractionAllowed)
+ return;
+
+ if (!d->scene)
+ return;
+
+ QGraphicsSceneMouseEvent mouseEvent(QEvent::GraphicsSceneMouseRelease);
+ mouseEvent.setWidget(viewport());
+ mouseEvent.setButtonDownScenePos(d->mousePressButton, d->mousePressScenePoint);
+ mouseEvent.setButtonDownScreenPos(d->mousePressButton, d->mousePressScreenPoint);
+ mouseEvent.setScenePos(mapToScene(event->pos()));
+ mouseEvent.setScreenPos(event->globalPos());
+ mouseEvent.setLastScenePos(d->lastMouseMoveScenePoint);
+ mouseEvent.setLastScreenPos(d->lastMouseMoveScreenPoint);
+ mouseEvent.setButtons(event->buttons());
+ mouseEvent.setButton(event->button());
+ mouseEvent.setModifiers(event->modifiers());
+ mouseEvent.setAccepted(false);
+ QApplication::sendEvent(d->scene, &mouseEvent);
+
+ // Update the last mouse event selected state.
+ d->lastMouseEvent.setAccepted(mouseEvent.isAccepted());
+
+#ifndef QT_NO_CURSOR
+ if (mouseEvent.isAccepted() && mouseEvent.buttons() == 0 && viewport()->testAttribute(Qt::WA_SetCursor)) {
+ // The last mouse release on the viewport will trigger clearing the cursor.
+ d->_q_unsetViewportCursor();
+ }
+#endif
+}
+
+#ifndef QT_NO_WHEELEVENT
+/*!
+ \reimp
+*/
+void QGraphicsView::wheelEvent(QWheelEvent *event)
+{
+ Q_D(QGraphicsView);
+ if (!d->scene || !d->sceneInteractionAllowed) {
+ QAbstractScrollArea::wheelEvent(event);
+ return;
+ }
+
+ event->ignore();
+
+ QGraphicsSceneWheelEvent wheelEvent(QEvent::GraphicsSceneWheel);
+ wheelEvent.setWidget(viewport());
+ wheelEvent.setScenePos(mapToScene(event->pos()));
+ wheelEvent.setScreenPos(event->globalPos());
+ wheelEvent.setButtons(event->buttons());
+ wheelEvent.setModifiers(event->modifiers());
+ wheelEvent.setDelta(event->delta());
+ wheelEvent.setOrientation(event->orientation());
+ wheelEvent.setAccepted(false);
+ QApplication::sendEvent(d->scene, &wheelEvent);
+ event->setAccepted(wheelEvent.isAccepted());
+ if (!event->isAccepted())
+ QAbstractScrollArea::wheelEvent(event);
+}
+#endif // QT_NO_WHEELEVENT
+
+/*!
+ \reimp
+*/
+void QGraphicsView::paintEvent(QPaintEvent *event)
+{
+ Q_D(QGraphicsView);
+ if (!d->scene) {
+ QAbstractScrollArea::paintEvent(event);
+ return;
+ }
+
+ // Set up painter state protection.
+ d->scene->d_func()->painterStateProtection = !(d->optimizationFlags & DontSavePainterState);
+
+ // Determine the exposed region
+ QRegion exposedRegion = event->region();
+ if (!d->accelerateScrolling)
+ exposedRegion = viewport()->rect();
+ else if (d->viewportUpdateMode == BoundingRectViewportUpdate)
+ exposedRegion = event->rect();
+ QRectF exposedSceneRect = mapToScene(exposedRegion.boundingRect().adjusted(0, 0, 1, 1)).boundingRect();
+
+ // Set up the painter
+ QPainter painter(viewport());
+ QTransform original = painter.worldTransform();
+#ifndef QT_NO_RUBBERBAND
+ if (d->rubberBanding && !d->rubberBandRect.isNull())
+ painter.save();
+#endif
+ // Set up render hints
+ painter.setRenderHints(painter.renderHints(), false);
+ painter.setRenderHints(d->renderHints, true);
+
+ // Set up viewport transform
+ const QTransform viewTransform = viewportTransform();
+ painter.setTransform(viewTransform, true);
+
+#ifdef QGRAPHICSVIEW_DEBUG
+ QTime stopWatch;
+ stopWatch.start();
+ qDebug() << "QGraphicsView::paintEvent(" << exposedRegion << ")";
+#endif
+
+ // Find all exposed items
+ bool allItems = false;
+ QList<QGraphicsItem *> itemList = d->findItems(exposedRegion, viewTransform, &allItems);
+
+#ifdef QGRAPHICSVIEW_DEBUG
+ int exposedTime = stopWatch.elapsed();
+#endif
+
+ if ((d->cacheMode & CacheBackground)
+#ifdef Q_WS_X11
+ && X11->use_xrender
+#endif
+ ) {
+ // Recreate the background pixmap, and flag the whole background as
+ // exposed.
+ if (d->mustResizeBackgroundPixmap) {
+ d->backgroundPixmap = QPixmap(viewport()->size());
+ QBrush bgBrush = viewport()->palette().brush(viewport()->backgroundRole());
+ if (!bgBrush.isOpaque())
+ d->backgroundPixmap.fill(Qt::transparent);
+ QPainter p(&d->backgroundPixmap);
+ p.fillRect(0, 0, d->backgroundPixmap.width(), d->backgroundPixmap.height(), bgBrush);
+ d->backgroundPixmapExposed = QRegion(viewport()->rect());
+ d->mustResizeBackgroundPixmap = false;
+ }
+
+ // Redraw exposed areas
+ if (!d->backgroundPixmapExposed.isEmpty()) {
+ QPainter backgroundPainter(&d->backgroundPixmap);
+ backgroundPainter.setClipRegion(d->backgroundPixmapExposed, Qt::ReplaceClip);
+ backgroundPainter.setTransform(viewportTransform());
+ drawBackground(&backgroundPainter, exposedSceneRect);
+ d->backgroundPixmapExposed = QRegion();
+ }
+
+ // Blit the background from the background pixmap
+ QTransform oldMatrix = painter.worldTransform();
+ painter.setWorldTransform(original);
+ painter.drawPixmap(QPoint(), d->backgroundPixmap);
+ painter.setWorldTransform(oldMatrix);
+ } else {
+ if (!(d->optimizationFlags & DontSavePainterState))
+ painter.save();
+ drawBackground(&painter, exposedSceneRect);
+ if (!(d->optimizationFlags & DontSavePainterState))
+ painter.restore();
+ }
+
+#ifdef QGRAPHICSVIEW_DEBUG
+ int backgroundTime = stopWatch.elapsed() - exposedTime;
+#endif
+
+ // Generate the style options
+ QGraphicsItem **itemArray = new QGraphicsItem *[itemList.size()];
+ QStyleOptionGraphicsItem *styleOptionArray = d->allocStyleOptionsArray(itemList.size());
+
+ d->generateStyleOptions(itemList, itemArray, styleOptionArray, viewTransform,
+ allItems, exposedRegion);
+
+ // Items
+ drawItems(&painter, itemList.size(), itemArray, styleOptionArray);
+
+#ifdef QGRAPHICSVIEW_DEBUG
+ int itemsTime = stopWatch.elapsed() - exposedTime - backgroundTime;
+#endif
+
+ // Foreground
+ drawForeground(&painter, exposedSceneRect);
+
+ delete [] itemArray;
+ d->freeStyleOptionsArray(styleOptionArray);
+
+#ifdef QGRAPHICSVIEW_DEBUG
+ int foregroundTime = stopWatch.elapsed() - exposedTime - backgroundTime - itemsTime;
+#endif
+
+#ifndef QT_NO_RUBBERBAND
+ // Rubberband
+ if (d->rubberBanding && !d->rubberBandRect.isNull()) {
+ painter.restore();
+ QStyleOptionRubberBand option;
+ option.initFrom(viewport());
+ option.rect = d->rubberBandRect;
+ option.shape = QRubberBand::Rectangle;
+
+ QStyleHintReturnMask mask;
+ if (viewport()->style()->styleHint(QStyle::SH_RubberBand_Mask, &option, viewport(), &mask)) {
+ // painter clipping for masked rubberbands
+ painter.setClipRegion(mask.region, Qt::IntersectClip);
+ }
+
+ viewport()->style()->drawControl(QStyle::CE_RubberBand, &option, &painter, viewport());
+ }
+#endif
+
+ painter.end();
+
+#ifdef QGRAPHICSVIEW_DEBUG
+ qDebug() << "\tItem discovery....... " << exposedTime << "msecs (" << itemList.size() << "items,"
+ << (exposedTime > 0 ? (itemList.size() * 1000.0 / exposedTime) : -1) << "/ sec )";
+ qDebug() << "\tDrawing background... " << backgroundTime << "msecs (" << exposedRegion.numRects() << "segments )";
+ qDebug() << "\tDrawing items........ " << itemsTime << "msecs ("
+ << (itemsTime > 0 ? (itemList.size() * 1000.0 / itemsTime) : -1) << "/ sec )";
+ qDebug() << "\tDrawing foreground... " << foregroundTime << "msecs (" << exposedRegion.numRects() << "segments )";
+ qDebug() << "\tTotal rendering time: " << stopWatch.elapsed() << "msecs ("
+ << (stopWatch.elapsed() > 0 ? (1000.0 / stopWatch.elapsed()) : -1.0) << "fps )";
+#endif
+
+ // Restore painter state protection.
+ d->scene->d_func()->painterStateProtection = true;
+}
+
+/*!
+ \reimp
+*/
+void QGraphicsView::resizeEvent(QResizeEvent *event)
+{
+ Q_D(QGraphicsView);
+ // Save the last center point - the resize may scroll the view, which
+ // changes the center point.
+ QPointF oldLastCenterPoint = d->lastCenterPoint;
+
+ QAbstractScrollArea::resizeEvent(event);
+ d->recalculateContentSize();
+
+ // Restore the center point again.
+ if (d->resizeAnchor == NoAnchor && !d->keepLastCenterPoint) {
+ d->updateLastCenterPoint();
+ } else {
+ d->lastCenterPoint = oldLastCenterPoint;
+ }
+ d->centerView(d->resizeAnchor);
+ d->keepLastCenterPoint = false;
+
+ if (d->cacheMode & CacheBackground) {
+ // Invalidate the background pixmap
+ d->mustResizeBackgroundPixmap = true;
+ }
+}
+
+/*!
+ \reimp
+*/
+void QGraphicsView::scrollContentsBy(int dx, int dy)
+{
+ Q_D(QGraphicsView);
+ d->dirtyScroll = true;
+ if (d->transforming)
+ return;
+ if (isRightToLeft())
+ dx = -dx;
+
+ if (d->viewportUpdateMode != QGraphicsView::NoViewportUpdate
+ && d->viewportUpdateMode != QGraphicsView::FullViewportUpdate) {
+ for (int i = 0; i < d->dirtyRects.size(); ++i)
+ d->dirtyRects[i].translate(dx, dy);
+ for (int i = 0; i < d->dirtyRegions.size(); ++i)
+ d->dirtyRegions[i].translate(dx, dy);
+ }
+
+#ifndef QT_NO_RUBBERBAND
+ // Update old rubberband
+ if (d->viewportUpdateMode != QGraphicsView::NoViewportUpdate && !d->rubberBandRect.isNull()) {
+ if (d->viewportUpdateMode != FullViewportUpdate)
+ viewport()->update(d->rubberBandRegion(viewport(), d->rubberBandRect));
+ else
+ viewport()->update();
+ }
+#endif
+
+ if (d->viewportUpdateMode != QGraphicsView::NoViewportUpdate){
+ if (d->accelerateScrolling && d->viewportUpdateMode != FullViewportUpdate)
+ viewport()->scroll(dx, dy);
+ else
+ viewport()->update();
+ }
+ d->updateLastCenterPoint();
+
+ if ((d->cacheMode & CacheBackground)
+#ifdef Q_WS_X11
+ && X11->use_xrender
+#endif
+ ) {
+ // Invalidate the background pixmap
+ d->backgroundPixmapExposed.translate(dx, 0);
+ if (dx > 0) {
+ d->backgroundPixmapExposed += QRect(0, 0, dx, viewport()->height());
+ } else if (dx < 0) {
+ d->backgroundPixmapExposed += QRect(viewport()->width() + dx, 0,
+ -dx, viewport()->height());
+ }
+ d->backgroundPixmapExposed.translate(0, dy);
+ if (dy > 0) {
+ d->backgroundPixmapExposed += QRect(0, 0, viewport()->width(), dy);
+ } else if (dy < 0) {
+ d->backgroundPixmapExposed += QRect(0, viewport()->height() + dy,
+ viewport()->width(), -dy);
+ }
+
+ // Scroll the background pixmap
+ if (!d->backgroundPixmap.isNull()) {
+ QPixmap tmp = d->backgroundPixmap.copy();
+ QBrush bgBrush = viewport()->palette().brush(viewport()->backgroundRole());
+ if (!bgBrush.isOpaque())
+ d->backgroundPixmap.fill(Qt::transparent);
+ QPainter painter(&d->backgroundPixmap);
+ painter.drawPixmap(dx, dy, tmp);
+ }
+ }
+
+ // Always replay on scroll.
+ if (d->sceneInteractionAllowed)
+ d->replayLastMouseEvent();
+}
+
+/*!
+ \reimp
+*/
+void QGraphicsView::showEvent(QShowEvent *event)
+{
+ Q_D(QGraphicsView);
+ d->recalculateContentSize();
+ d->centerView(d->transformationAnchor);
+ QAbstractScrollArea::showEvent(event);
+}
+
+/*!
+ \reimp
+*/
+void QGraphicsView::inputMethodEvent(QInputMethodEvent *event)
+{
+ Q_D(QGraphicsView);
+ if (d->scene)
+ QApplication::sendEvent(d->scene, event);
+}
+
+/*!
+ Draws the background of the scene using \a painter, before any items and
+ the foreground are drawn. Reimplement this function to provide a custom
+ background for this view.
+
+ If all you want is to define a color, texture or gradient for the
+ background, you can call setBackgroundBrush() instead.
+
+ All painting is done in \e scene coordinates. \a rect is the exposed
+ rectangle.
+
+ The default implementation fills \a rect using the view's backgroundBrush.
+ If no such brush is defined (the default), the scene's drawBackground()
+ function is called instead.
+
+ \sa drawForeground(), QGraphicsScene::drawBackground()
+*/
+void QGraphicsView::drawBackground(QPainter *painter, const QRectF &rect)
+{
+ Q_D(QGraphicsView);
+ if (d->scene && d->backgroundBrush.style() == Qt::NoBrush) {
+ d->scene->drawBackground(painter, rect);
+ return;
+ }
+
+ painter->fillRect(rect, d->backgroundBrush);
+}
+
+/*!
+ Draws the foreground of the scene using \a painter, after the background
+ and all items are drawn. Reimplement this function to provide a custom
+ foreground for this view.
+
+ If all you want is to define a color, texture or gradient for the
+ foreground, you can call setForegroundBrush() instead.
+
+ All painting is done in \e scene coordinates. \a rect is the exposed
+ rectangle.
+
+ The default implementation fills \a rect using the view's foregroundBrush.
+ If no such brush is defined (the default), the scene's drawForeground()
+ function is called instead.
+
+ \sa drawBackground(), QGraphicsScene::drawForeground()
+*/
+void QGraphicsView::drawForeground(QPainter *painter, const QRectF &rect)
+{
+ Q_D(QGraphicsView);
+ if (d->scene && d->foregroundBrush.style() == Qt::NoBrush) {
+ d->scene->drawForeground(painter, rect);
+ return;
+ }
+
+ painter->fillRect(rect, d->foregroundBrush);
+}
+
+/*!
+ Draws the items \a items in the scene using \a painter, after the
+ background and before the foreground are drawn. \a numItems is the number
+ of items in \a items and options in \a options. \a options is a list of
+ styleoptions; one for each item. Reimplement this function to provide
+ custom item drawing for this view.
+
+ The default implementation calls the scene's drawItems() function.
+
+ \sa drawForeground(), drawBackground(), QGraphicsScene::drawItems()
+*/
+void QGraphicsView::drawItems(QPainter *painter, int numItems,
+ QGraphicsItem *items[],
+ const QStyleOptionGraphicsItem options[])
+{
+ Q_D(QGraphicsView);
+ if (d->scene)
+ d->scene->drawItems(painter, numItems, items, options, viewport());
+}
+
+/*!
+ Returns the current transformation matrix for the view. If no current
+ transformation is set, the identity matrix is returned.
+
+ \sa setTransform(), rotate(), scale(), shear(), translate()
+*/
+QTransform QGraphicsView::transform() const
+{
+ Q_D(const QGraphicsView);
+ return d->matrix;
+}
+
+/*!
+ Returns a matrix that maps viewport coordinates to scene coordinates.
+
+ \sa mapToScene(), mapFromScene()
+*/
+QTransform QGraphicsView::viewportTransform() const
+{
+ Q_D(const QGraphicsView);
+ QTransform moveMatrix;
+ moveMatrix.translate(-d->horizontalScroll(), -d->verticalScroll());
+ return d->identityMatrix ? moveMatrix : d->matrix * moveMatrix;
+}
+
+/*!
+ Sets the view's current transformation matrix to \a matrix.
+
+ If \a combine is true, then \a matrix is combined with the current matrix;
+ otherwise, \a matrix \e replaces the current matrix. \a combine is false
+ by default.
+
+ The transformation matrix tranforms the scene into view coordinates. Using
+ the default transformation, provided by the identity matrix, one pixel in
+ the view represents one unit in the scene (e.g., a 10x10 rectangular item
+ is drawn using 10x10 pixels in the view). If a 2x2 scaling matrix is
+ applied, the scene will be drawn in 1:2 (e.g., a 10x10 rectangular item is
+ then drawn using 20x20 pixels in the view).
+
+ Example:
+
+ \snippet doc/src/snippets/code/src_gui_graphicsview_qgraphicsview.cpp 7
+
+ To simplify interation with items using a transformed view, QGraphicsView
+ provides mapTo... and mapFrom... functions that can translate between
+ scene and view coordinates. For example, you can call mapToScene() to map
+ a view coordiate to a floating point scene coordinate, or mapFromScene()
+ to map from floating point scene coordinates to view coordinates.
+
+ \sa transform(), rotate(), scale(), shear(), translate()
+*/
+void QGraphicsView::setTransform(const QTransform &matrix, bool combine )
+{
+ Q_D(QGraphicsView);
+ QTransform oldMatrix = d->matrix;
+ if (!combine)
+ d->matrix = matrix;
+ else
+ d->matrix = matrix * d->matrix;
+ if (oldMatrix == d->matrix)
+ return;
+
+ d->identityMatrix = d->matrix.isIdentity();
+ d->transforming = true;
+ if (d->scene) {
+ d->recalculateContentSize();
+ d->centerView(d->transformationAnchor);
+ } else {
+ d->updateLastCenterPoint();
+ }
+
+ if (d->sceneInteractionAllowed)
+ d->replayLastMouseEvent();
+ d->transforming = false;
+
+ // Any matrix operation requires a full update.
+ viewport()->update();
+}
+
+/*!
+ Resets the view transformation to the identity matrix.
+
+ \sa transform(), setTransform()
+*/
+void QGraphicsView::resetTransform()
+{
+ setTransform(QTransform());
+}
+
+QT_END_NAMESPACE
+
+#include "moc_qgraphicsview.cpp"
+
+#endif // QT_NO_GRAPHICSVIEW
diff --git a/src/gui/graphicsview/qgraphicsview.h b/src/gui/graphicsview/qgraphicsview.h
new file mode 100644
index 0000000000..e77e45c1bb
--- /dev/null
+++ b/src/gui/graphicsview/qgraphicsview.h
@@ -0,0 +1,314 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QGRAPHICSVIEW_H
+#define QGRAPHICSVIEW_H
+
+#include <QtCore/qmetatype.h>
+#include <QtGui/qpainter.h>
+#include <QtGui/qscrollarea.h>
+#include <QtGui/qgraphicsscene.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+#if !defined(QT_NO_GRAPHICSVIEW) || (QT_EDITION & QT_MODULE_GRAPHICSVIEW) != QT_MODULE_GRAPHICSVIEW
+
+class QGraphicsItem;
+class QPainterPath;
+class QPolygonF;
+class QStyleOptionGraphicsItem;
+
+class QGraphicsViewPrivate;
+class Q_GUI_EXPORT QGraphicsView : public QAbstractScrollArea
+{
+ Q_OBJECT
+ Q_FLAGS(QPainter::RenderHints CacheMode OptimizationFlags)
+ Q_ENUMS(ViewportAnchor DragMode ViewportUpdateMode)
+ Q_PROPERTY(QBrush backgroundBrush READ backgroundBrush WRITE setBackgroundBrush)
+ Q_PROPERTY(QBrush foregroundBrush READ foregroundBrush WRITE setForegroundBrush)
+ Q_PROPERTY(bool interactive READ isInteractive WRITE setInteractive)
+ Q_PROPERTY(QRectF sceneRect READ sceneRect WRITE setSceneRect)
+ Q_PROPERTY(Qt::Alignment alignment READ alignment WRITE setAlignment)
+ Q_PROPERTY(QPainter::RenderHints renderHints READ renderHints WRITE setRenderHints)
+ Q_PROPERTY(DragMode dragMode READ dragMode WRITE setDragMode)
+ Q_PROPERTY(CacheMode cacheMode READ cacheMode WRITE setCacheMode)
+ Q_PROPERTY(ViewportAnchor transformationAnchor READ transformationAnchor WRITE setTransformationAnchor)
+ Q_PROPERTY(ViewportAnchor resizeAnchor READ resizeAnchor WRITE setResizeAnchor)
+ Q_PROPERTY(ViewportUpdateMode viewportUpdateMode READ viewportUpdateMode WRITE setViewportUpdateMode)
+#ifndef QT_NO_RUBBERBAND
+ Q_PROPERTY(Qt::ItemSelectionMode rubberBandSelectionMode READ rubberBandSelectionMode WRITE setRubberBandSelectionMode)
+#endif
+ Q_PROPERTY(OptimizationFlags optimizationFlags READ optimizationFlags WRITE setOptimizationFlags)
+
+public:
+ enum ViewportAnchor {
+ NoAnchor,
+ AnchorViewCenter,
+ AnchorUnderMouse
+ };
+
+ enum CacheModeFlag {
+ CacheNone = 0x0,
+ CacheBackground = 0x1
+ };
+ Q_DECLARE_FLAGS(CacheMode, CacheModeFlag)
+
+ enum DragMode {
+ NoDrag,
+ ScrollHandDrag,
+ RubberBandDrag
+ };
+
+ enum ViewportUpdateMode {
+ FullViewportUpdate,
+ MinimalViewportUpdate,
+ SmartViewportUpdate,
+ NoViewportUpdate,
+ BoundingRectViewportUpdate
+ };
+
+ enum OptimizationFlag {
+ DontClipPainter = 0x1,
+ DontSavePainterState = 0x2,
+ DontAdjustForAntialiasing = 0x4
+ };
+ Q_DECLARE_FLAGS(OptimizationFlags, OptimizationFlag)
+
+ QGraphicsView(QWidget *parent = 0);
+ QGraphicsView(QGraphicsScene *scene, QWidget *parent = 0);
+ ~QGraphicsView();
+
+ QSize sizeHint() const;
+
+ QPainter::RenderHints renderHints() const;
+ void setRenderHint(QPainter::RenderHint hint, bool enabled = true);
+ void setRenderHints(QPainter::RenderHints hints);
+
+ Qt::Alignment alignment() const;
+ void setAlignment(Qt::Alignment alignment);
+
+ ViewportAnchor transformationAnchor() const;
+ void setTransformationAnchor(ViewportAnchor anchor);
+
+ ViewportAnchor resizeAnchor() const;
+ void setResizeAnchor(ViewportAnchor anchor);
+
+ ViewportUpdateMode viewportUpdateMode() const;
+ void setViewportUpdateMode(ViewportUpdateMode mode);
+
+ OptimizationFlags optimizationFlags() const;
+ void setOptimizationFlag(OptimizationFlag flag, bool enabled = true);
+ void setOptimizationFlags(OptimizationFlags flags);
+
+ DragMode dragMode() const;
+ void setDragMode(DragMode mode);
+
+#ifndef QT_NO_RUBBERBAND
+ Qt::ItemSelectionMode rubberBandSelectionMode() const;
+ void setRubberBandSelectionMode(Qt::ItemSelectionMode mode);
+#endif
+
+ CacheMode cacheMode() const;
+ void setCacheMode(CacheMode mode);
+ void resetCachedContent();
+
+ bool isInteractive() const;
+ void setInteractive(bool allowed);
+
+ QGraphicsScene *scene() const;
+ void setScene(QGraphicsScene *scene);
+
+ QRectF sceneRect() const;
+ void setSceneRect(const QRectF &rect);
+ inline void setSceneRect(qreal x, qreal y, qreal w, qreal h);
+
+ QMatrix matrix() const;
+ void setMatrix(const QMatrix &matrix, bool combine = false);
+ void resetMatrix();
+ QTransform transform() const;
+ QTransform viewportTransform() const;
+ void setTransform(const QTransform &matrix, bool combine = false);
+ void resetTransform();
+ void rotate(qreal angle);
+ void scale(qreal sx, qreal sy);
+ void shear(qreal sh, qreal sv);
+ void translate(qreal dx, qreal dy);
+
+ void centerOn(const QPointF &pos);
+ inline void centerOn(qreal x, qreal y);
+ void centerOn(const QGraphicsItem *item);
+ void ensureVisible(const QRectF &rect, int xmargin = 50, int ymargin = 50);
+ inline void ensureVisible(qreal x, qreal y, qreal w, qreal h, int xmargin = 50, int ymargin = 50);
+ void ensureVisible(const QGraphicsItem *item, int xmargin = 50, int ymargin = 50);
+ void fitInView(const QRectF &rect, Qt::AspectRatioMode aspectRadioMode = Qt::IgnoreAspectRatio);
+ inline void fitInView(qreal x, qreal y, qreal w, qreal h,
+ Qt::AspectRatioMode aspectRadioMode = Qt::IgnoreAspectRatio);
+ void fitInView(const QGraphicsItem *item,
+ Qt::AspectRatioMode aspectRadioMode = Qt::IgnoreAspectRatio);
+
+ void render(QPainter *painter, const QRectF &target = QRectF(), const QRect &source = QRect(),
+ Qt::AspectRatioMode aspectRatioMode = Qt::KeepAspectRatio);
+
+ QList<QGraphicsItem *> items() const;
+ QList<QGraphicsItem *> items(const QPoint &pos) const;
+ inline QList<QGraphicsItem *> items(int x, int y) const;
+ QList<QGraphicsItem *> items(const QRect &rect, Qt::ItemSelectionMode mode = Qt::IntersectsItemShape) const;
+ inline QList<QGraphicsItem *> items(int x, int y, int w, int h, Qt::ItemSelectionMode mode = Qt::IntersectsItemShape) const;
+ QList<QGraphicsItem *> items(const QPolygon &polygon, Qt::ItemSelectionMode mode = Qt::IntersectsItemShape) const;
+ QList<QGraphicsItem *> items(const QPainterPath &path, Qt::ItemSelectionMode mode = Qt::IntersectsItemShape) const;
+ QGraphicsItem *itemAt(const QPoint &pos) const;
+ inline QGraphicsItem *itemAt(int x, int y) const;
+
+ QPointF mapToScene(const QPoint &point) const;
+ QPolygonF mapToScene(const QRect &rect) const;
+ QPolygonF mapToScene(const QPolygon &polygon) const;
+ QPainterPath mapToScene(const QPainterPath &path) const;
+ QPoint mapFromScene(const QPointF &point) const;
+ QPolygon mapFromScene(const QRectF &rect) const;
+ QPolygon mapFromScene(const QPolygonF &polygon) const;
+ QPainterPath mapFromScene(const QPainterPath &path) const;
+ inline QPointF mapToScene(int x, int y) const;
+ inline QPolygonF mapToScene(int x, int y, int w, int h) const;
+ inline QPoint mapFromScene(qreal x, qreal y) const;
+ inline QPolygon mapFromScene(qreal x, qreal y, qreal w, qreal h) const;
+
+ QVariant inputMethodQuery(Qt::InputMethodQuery query) const;
+
+ QBrush backgroundBrush() const;
+ void setBackgroundBrush(const QBrush &brush);
+
+ QBrush foregroundBrush() const;
+ void setForegroundBrush(const QBrush &brush);
+
+public Q_SLOTS:
+ void updateScene(const QList<QRectF> &rects);
+ void invalidateScene(const QRectF &rect = QRectF(), QGraphicsScene::SceneLayers layers = QGraphicsScene::AllLayers);
+ void updateSceneRect(const QRectF &rect);
+
+protected Q_SLOTS:
+ void setupViewport(QWidget *widget);
+
+protected:
+ QGraphicsView(QGraphicsViewPrivate &, QWidget *parent = 0);
+ bool event(QEvent *event);
+ bool viewportEvent(QEvent *event);
+
+#ifndef QT_NO_CONTEXTMENU
+ void contextMenuEvent(QContextMenuEvent *event);
+#endif
+ void dragEnterEvent(QDragEnterEvent *event);
+ void dragLeaveEvent(QDragLeaveEvent *event);
+ void dragMoveEvent(QDragMoveEvent *event);
+ void dropEvent(QDropEvent *event);
+ void focusInEvent(QFocusEvent *event);
+ bool focusNextPrevChild(bool next);
+ void focusOutEvent(QFocusEvent *event);
+ void keyPressEvent(QKeyEvent *event);
+ void keyReleaseEvent(QKeyEvent *event);
+ void mouseDoubleClickEvent(QMouseEvent *event);
+ void mousePressEvent(QMouseEvent *event);
+ void mouseMoveEvent(QMouseEvent *event);
+ void mouseReleaseEvent(QMouseEvent *event);
+#ifndef QT_NO_WHEELEVENT
+ void wheelEvent(QWheelEvent *event);
+#endif
+ void paintEvent(QPaintEvent *event);
+ void resizeEvent(QResizeEvent *event);
+ void scrollContentsBy(int dx, int dy);
+ void showEvent(QShowEvent *event);
+ void inputMethodEvent(QInputMethodEvent *event);
+
+ virtual void drawBackground(QPainter *painter, const QRectF &rect);
+ virtual void drawForeground(QPainter *painter, const QRectF &rect);
+ virtual void drawItems(QPainter *painter, int numItems,
+ QGraphicsItem *items[],
+ const QStyleOptionGraphicsItem options[]);
+
+private:
+ Q_DECLARE_PRIVATE(QGraphicsView)
+ Q_DISABLE_COPY(QGraphicsView)
+#ifndef QT_NO_CURSOR
+ Q_PRIVATE_SLOT(d_func(), void _q_setViewportCursor(const QCursor &))
+ Q_PRIVATE_SLOT(d_func(), void _q_unsetViewportCursor())
+#endif
+ Q_PRIVATE_SLOT(d_func(), void _q_updateLaterSlot())
+ friend class QGraphicsSceneWidget;
+ friend class QGraphicsScene;
+ friend class QGraphicsScenePrivate;
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(QGraphicsView::CacheMode)
+Q_DECLARE_OPERATORS_FOR_FLAGS(QGraphicsView::OptimizationFlags)
+
+inline void QGraphicsView::setSceneRect(qreal ax, qreal ay, qreal aw, qreal ah)
+{ setSceneRect(QRectF(ax, ay, aw, ah)); }
+inline void QGraphicsView::centerOn(qreal ax, qreal ay)
+{ centerOn(QPointF(ax, ay)); }
+inline void QGraphicsView::ensureVisible(qreal ax, qreal ay, qreal aw, qreal ah, int xmargin, int ymargin)
+{ ensureVisible(QRectF(ax, ay, aw, ah), xmargin, ymargin); }
+inline void QGraphicsView::fitInView(qreal ax, qreal ay, qreal w, qreal h, Qt::AspectRatioMode mode)
+{ fitInView(QRectF(ax, ay, w, h), mode); }
+inline QList<QGraphicsItem *> QGraphicsView::items(int ax, int ay) const
+{ return items(QPoint(ax, ay)); }
+inline QList<QGraphicsItem *> QGraphicsView::items(int ax, int ay, int w, int h, Qt::ItemSelectionMode mode) const
+{ return items(QRect(ax, ay, w, h), mode); }
+inline QGraphicsItem *QGraphicsView::itemAt(int ax, int ay) const
+{ return itemAt(QPoint(ax, ay)); }
+inline QPointF QGraphicsView::mapToScene(int ax, int ay) const
+{ return mapToScene(QPoint(ax, ay)); }
+inline QPolygonF QGraphicsView::mapToScene(int ax, int ay, int w, int h) const
+{ return mapToScene(QRect(ax, ay, w, h)); }
+inline QPoint QGraphicsView::mapFromScene(qreal ax, qreal ay) const
+{ return mapFromScene(QPointF(ax, ay)); }
+inline QPolygon QGraphicsView::mapFromScene(qreal ax, qreal ay, qreal w, qreal h) const
+{ return mapFromScene(QRectF(ax, ay, w, h)); }
+
+#endif // QT_NO_GRAPHICSVIEW
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QGRAPHICSVIEW_H
diff --git a/src/gui/graphicsview/qgraphicsview_p.h b/src/gui/graphicsview/qgraphicsview_p.h
new file mode 100644
index 0000000000..21096732d6
--- /dev/null
+++ b/src/gui/graphicsview/qgraphicsview_p.h
@@ -0,0 +1,191 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QGRAPHICSVIEW_P_H
+#define QGRAPHICSVIEW_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of qapplication_*.cpp, qwidget*.cpp and qfiledialog.cpp. This header
+// file may change from version to version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qgraphicsview.h"
+
+#if !defined(QT_NO_GRAPHICSVIEW) || (QT_EDITION & QT_MODULE_GRAPHICSVIEW) != QT_MODULE_GRAPHICSVIEW
+
+#include <QtGui/qevent.h>
+#include "qgraphicssceneevent.h"
+#include <private/qabstractscrollarea_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class Q_GUI_EXPORT QGraphicsViewPrivate : public QAbstractScrollAreaPrivate
+{
+ Q_DECLARE_PUBLIC(QGraphicsView)
+public:
+ QGraphicsViewPrivate();
+
+ void recalculateContentSize();
+ void centerView(QGraphicsView::ViewportAnchor anchor);
+
+ QPainter::RenderHints renderHints;
+
+ QGraphicsView::DragMode dragMode;
+ bool sceneInteractionAllowed;
+ QRectF sceneRect;
+ bool hasSceneRect;
+ void updateLastCenterPoint();
+ bool connectedToScene;
+
+ qint64 horizontalScroll() const;
+ qint64 verticalScroll() const;
+
+ QList<QGraphicsItem *> itemsInArea(const QPainterPath &path,
+ Qt::ItemSelectionMode mode = Qt::IntersectsItemShape) const;
+
+ QPointF mousePressItemPoint;
+ QPointF mousePressScenePoint;
+ QPoint mousePressViewPoint;
+ QPoint mousePressScreenPoint;
+ QPointF lastMouseMoveScenePoint;
+ QPoint lastMouseMoveScreenPoint;
+ Qt::MouseButton mousePressButton;
+ QTransform matrix;
+ bool identityMatrix;
+ qint64 scrollX, scrollY;
+ bool dirtyScroll;
+ void updateScroll();
+
+ bool accelerateScrolling;
+ qreal leftIndent;
+ qreal topIndent;
+
+ // Replaying mouse events
+ QMouseEvent lastMouseEvent;
+ bool useLastMouseEvent;
+ void replayLastMouseEvent();
+ void storeMouseEvent(QMouseEvent *event);
+ void mouseMoveEventHandler(QMouseEvent *event);
+
+ QPointF lastCenterPoint;
+ bool keepLastCenterPoint;
+ Qt::Alignment alignment;
+ bool transforming;
+
+ QGraphicsView::ViewportAnchor transformationAnchor;
+ QGraphicsView::ViewportAnchor resizeAnchor;
+ QGraphicsView::ViewportUpdateMode viewportUpdateMode;
+ QGraphicsView::OptimizationFlags optimizationFlags;
+
+ QPointer<QGraphicsScene> scene;
+#ifndef QT_NO_RUBBERBAND
+ QRect rubberBandRect;
+ QRegion rubberBandRegion(const QWidget *widget, const QRect &rect) const;
+ bool rubberBanding;
+ Qt::ItemSelectionMode rubberBandSelectionMode;
+#endif
+ bool handScrolling;
+ int handScrollMotions;
+
+ QGraphicsView::CacheMode cacheMode;
+
+ QVector<QStyleOptionGraphicsItem> styleOptions;
+ bool mustAllocateStyleOptions;
+ QStyleOptionGraphicsItem *allocStyleOptionsArray(int numItems);
+ void freeStyleOptionsArray(QStyleOptionGraphicsItem *array);
+
+ QBrush backgroundBrush;
+ QBrush foregroundBrush;
+ QPixmap backgroundPixmap;
+ bool mustResizeBackgroundPixmap;
+ QRegion backgroundPixmapExposed;
+
+#ifndef QT_NO_CURSOR
+ QCursor originalCursor;
+ bool hasStoredOriginalCursor;
+ void _q_setViewportCursor(const QCursor &cursor);
+ void _q_unsetViewportCursor();
+#endif
+
+ QGraphicsSceneDragDropEvent *lastDragDropEvent;
+ void storeDragDropEvent(const QGraphicsSceneDragDropEvent *event);
+ void populateSceneDragDropEvent(QGraphicsSceneDragDropEvent *dest,
+ QDropEvent *source);
+
+ QRect mapToViewRect(const QGraphicsItem *item, const QRectF &rect) const;
+ QRegion mapToViewRegion(const QGraphicsItem *item, const QRectF &rect) const;
+ void itemUpdated(QGraphicsItem *item, const QRectF &rect);
+ bool fullUpdatePending;
+ QList<QRect> dirtyRects;
+ QList<QRegion> dirtyRegions;
+ int dirtyRectCount;
+ QRect dirtyBoundingRect;
+ void updateLater();
+ bool updatingLater;
+ void _q_updateLaterSlot();
+ void updateAll();
+ void updateRect(const QRect &rect);
+ void updateRegion(const QRegion &region);
+ bool updateSceneSlotReimplementedChecked;
+
+ QList<QGraphicsItem *> findItems(const QRegion &exposedRegion,
+ const QTransform &worldTransform,
+ bool *allItems) const;
+
+ void generateStyleOptions(const QList<QGraphicsItem *> &itemList,
+ QGraphicsItem **itemArray,
+ QStyleOptionGraphicsItem *styleOptionArray,
+ const QTransform &worldTransform,
+ bool allItems,
+ const QRegion &exposedRegion) const;
+};
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_GRAPHICSVIEW
+
+#endif
diff --git a/src/gui/graphicsview/qgraphicswidget.cpp b/src/gui/graphicsview/qgraphicswidget.cpp
new file mode 100644
index 0000000000..5cc18f93f3
--- /dev/null
+++ b/src/gui/graphicsview/qgraphicswidget.cpp
@@ -0,0 +1,2273 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qglobal.h"
+
+#ifndef QT_NO_GRAPHICSVIEW
+
+#include "qgraphicswidget.h"
+#include "qgraphicswidget_p.h"
+#include "qgraphicslayout.h"
+#include "qgraphicslayout_p.h"
+#include "qgraphicsscene.h"
+#include "qgraphicssceneevent.h"
+
+#ifndef QT_NO_ACTION
+#include <private/qaction_p.h>
+#endif
+#include <private/qapplication_p.h>
+#include <private/qgraphicsscene_p.h>
+#ifndef QT_NO_SHORTCUT
+#include <private/qshortcutmap_p.h>
+#endif
+#include <QtCore/qmutex.h>
+#include <QtGui/qapplication.h>
+#include <QtGui/qgraphicsview.h>
+#include <QtGui/qgraphicsproxywidget.h>
+#include <QtGui/qpalette.h>
+#include <QtGui/qstyleoption.h>
+
+#include <qdebug.h>
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QGraphicsWidget
+ \brief The QGraphicsWidget class is the base class for all widget
+ items in a QGraphicsScene.
+ \since 4.4
+ \ingroup multimedia
+ \ingroup graphicsview-api
+
+ QGraphicsWidget is an extended base item that provides extra functionality
+ over QGraphicsItem. It is similar to QWidget in many ways:
+
+ \list
+ \o Provides a \l palette, a \l font and a \l style().
+ \o Has a defined geometry().
+ \o Supports layouts with setLayout() and layout().
+ \o Supports shortcuts and actions with grabShortcut() and insertAction()
+ \endlist
+
+ Unlike QGraphicsItem, QGraphicsWidget is not an abstract class; you can
+ create instances of a QGraphicsWidget without having to subclass it.
+ This approach is useful for widgets that only serve the purpose of
+ organizing child widgets into a layout.
+
+ QGraphicsWidget can be used as a base item for your own custom item if
+ you require advanced input focus handling, e.g., tab focus and activation, or
+ layouts.
+
+ Since QGraphicsWidget resembles QWidget and has similar API, it is
+ easier to port a widget from QWidget to QGraphicsWidget, instead of
+ QGraphicsItem.
+
+ \note QWidget-based widgets can be directly embedded into a
+ QGraphicsScene using QGraphicsProxyWidget.
+
+ Noticeable differences between QGraphicsWidget and QWidget are:
+
+ \table
+ \header \o QGraphicsWidget
+ \o QWidget
+ \row \o Coordinates and geometry are defined with qreals (doubles or
+ floats, depending on the platform).
+ \o QWidget uses integer geometry (QPoint, QRect).
+ \row \o The widget is already visible by default; you do not have to
+ call show() to display the widget.
+ \o QWidget is hidden by default until you call show().
+ \row \o A subset of widget attributes are supported.
+ \o All widget attributes are supported.
+ \row \o A top-level item's style defaults to QGraphicsScene::style
+ \o A top-level widget's style defaults to QApplication::style
+ \row \o Graphics View provides a custom drag and drop framework, different
+ from QWidget.
+ \o Standard drag and drop framework.
+ \row \o Widget items do not support modality.
+ \o Full modality support.
+ \endtable
+
+ QGraphicsWidget supports a subset of Qt's widget attributes,
+ (Qt::WidgetAttribute), as shown in the table below. Any attributes not
+ listed in this table are unsupported, or otherwise unused.
+
+ \table
+ \header \o Widget Attribute \o Usage
+ \row \o Qt::WA_SetLayoutDirection
+ \o Set by setLayoutDirection(), cleared by
+ unsetLayoutDirection(). You can test this attribute to
+ check if the widget has been explicitly assigned a
+ \l{QGraphicsWidget::layoutDirection()}
+ {layoutDirection}. If the attribute is not set, the
+ \l{QGraphicsWidget::layoutDirection()}
+ {layoutDirection()} is inherited.
+ \row \o Qt::WA_RightToLeft
+ \o Toggled by setLayoutDirection(). Inherited from the
+ parent/scene. If set, the widget's layout will order
+ horizontally arranged widgets from right to left.
+ \row \o Qt::WA_SetStyle
+ \o Set and cleared by setStyle(). If this attribute is
+ set, the widget has been explicitly assigned a style.
+ If it is unset, the widget will use the scene's or the
+ application's style.
+ \row \o Qt::WA_Resized
+ \o Set by setGeometry() and resize().
+ \row \o Qt::WA_SetPalette
+ \o Set by setPalette().
+ \row \o Qt::WA_SetFont
+ \o Set by setPalette().
+ \row \o Qt::WA_WindowPropagation
+ \o Enables propagation to window widgets.
+ \endtable
+
+ Although QGraphicsWidget inherits from both QObject and QGraphicsItem,
+ you should use the functions provided by QGraphicsItem, \e not QObject, to
+ manage the relationships between parent and child items. These functions
+ control the stacking order of items as well as their ownership.
+
+ \note The QObject::parent() should always return 0 for QGraphicsWidgets,
+ but this policy is not strictly defined.
+
+ \sa QGraphicsProxyWidget, QGraphicsItem, {Widgets and Layouts}
+*/
+
+/*!
+ \property QGraphicsWidget::enabled
+ \brief whether the item is enabled or not
+
+ This property is declared in QGraphicsItem.
+
+ By default, this property is true.
+
+ \sa QGraphicsItem::isEnabled(), QGraphicsItem::setEnabled()
+*/
+
+/*!
+ \property QGraphicsWidget::visible
+ \brief whether the item is visible or not
+
+ This property is declared in QGraphicsItem.
+
+ By default, this property is true.
+
+ \sa QGraphicsItem::isVisible(), QGraphicsItem::setVisible(), show(),
+ hide()
+*/
+
+/*!
+ \property QGraphicsWidget::opacity
+ \brief the opacity of the widget
+*/
+
+/*!
+ \property QGraphicsWidget::pos
+ \brief the position of the widget
+*/
+
+/*!
+ Constructs a QGraphicsWidget instance. The optional \a parent argument is
+ passed to QGraphicsItem's constructor. The optional \a wFlags argument
+ specifies the widget's window flags (e.g., whether the widget should be a
+ window, a tool, a popup, etc).
+*/
+QGraphicsWidget::QGraphicsWidget(QGraphicsItem *parent, Qt::WindowFlags wFlags)
+ : QGraphicsItem(*new QGraphicsWidgetPrivate, 0, 0), QGraphicsLayoutItem(0, false)
+{
+ Q_D(QGraphicsWidget);
+ d->init(parent, wFlags);
+}
+
+/*!
+ \internal
+
+ Constructs a new QGraphicsWidget, using \a dd as parent.
+*/
+QGraphicsWidget::QGraphicsWidget(QGraphicsWidgetPrivate &dd, QGraphicsItem *parent, QGraphicsScene *scene, Qt::WindowFlags wFlags)
+ : QGraphicsItem(dd, 0, scene), QGraphicsLayoutItem(0, false)
+{
+ Q_D(QGraphicsWidget);
+ d->init(parent, wFlags);
+}
+
+/*
+ \internal
+ \class QGraphicsWidgetStyles
+
+ We use this thread-safe class to maintain a hash of styles for widgets
+ styles. Note that QApplication::style() itself isn't thread-safe, QStyle
+ isn't thread-safe, and we don't have a thread-safe factory for creating
+ the default style, nor cloning a style.
+*/
+class QGraphicsWidgetStyles
+{
+public:
+ QStyle *styleForWidget(const QGraphicsWidget *widget) const
+ {
+ QMutexLocker locker(&mutex);
+ return styles.value(widget, 0);
+ }
+
+ void setStyleForWidget(QGraphicsWidget *widget, QStyle *style)
+ {
+ QMutexLocker locker(&mutex);
+ if (style)
+ styles[widget] = style;
+ else
+ styles.remove(widget);
+ }
+
+private:
+ QMap<const QGraphicsWidget *, QStyle *> styles;
+ mutable QMutex mutex;
+};
+Q_GLOBAL_STATIC(QGraphicsWidgetStyles, widgetStyles)
+
+/*!
+ Destroys the QGraphicsWidget instance.
+*/
+QGraphicsWidget::~QGraphicsWidget()
+{
+ Q_D(QGraphicsWidget);
+#ifndef QT_NO_ACTION
+ // Remove all actions from this widget
+ for (int i = 0; i < d->actions.size(); ++i) {
+ QActionPrivate *apriv = d->actions.at(i)->d_func();
+ apriv->graphicsWidgets.removeAll(this);
+ }
+ d->actions.clear();
+#endif
+
+ if (QGraphicsScene *scn = scene()) {
+ QGraphicsScenePrivate *sceneD = scn->d_func();
+ if (sceneD->tabFocusFirst == this)
+ sceneD->tabFocusFirst = (d->focusNext == this ? 0 : d->focusNext);
+ }
+ d->focusPrev->d_func()->focusNext = d->focusNext;
+ d->focusNext->d_func()->focusPrev = d->focusPrev;
+
+ // Play it really safe
+ d->focusNext = this;
+ d->focusPrev = this;
+
+ clearFocus();
+
+ //we check if we have a layout previously
+ if (d->layout) {
+ delete d->layout;
+ foreach (QGraphicsItem * item, childItems()) {
+ // In case of a custom layout which doesn't remove and delete items, we ensure that
+ // the parent layout item does not point to the deleted layout. This code is here to
+ // avoid regression from 4.4 to 4.5, because according to 4.5 docs it is not really needed.
+ if (item->isWidget()) {
+ QGraphicsWidget *widget = static_cast<QGraphicsWidget *>(item);
+ if (widget->parentLayoutItem() == d->layout)
+ widget->setParentLayoutItem(0);
+ }
+ }
+ }
+
+ // Remove this graphics widget from widgetStyles
+ widgetStyles()->setStyleForWidget(this, 0);
+}
+
+/*!
+ \property QGraphicsWidget::size
+ \brief the size of the widget
+
+ Calling resize() resizes the widget to a \a size bounded by minimumSize()
+ and maximumSize(). This property only affects the widget's width and
+ height (e.g., its right and bottom edges); the widget's position and
+ top-left corner remains unaffected.
+
+ Resizing a widget triggers the widget to immediately receive a
+ \l{QEvent::GraphicsSceneResize}{GraphicsSceneResize} event with the
+ widget's old and new size. If the widget has a layout assigned when this
+ event arrives, the layout will be activated and it will automatically
+ update any child widgets's geometry.
+
+ This property does not affect any layout of the parent widget. If the
+ widget itself is managed by a parent layout; e.g., it has a parent widget
+ with a layout assigned, that layout will not activate.
+
+ By default, this property contains a size with zero width and height.
+
+ \sa setGeometry(), QGraphicsSceneResizeEvent, QGraphicsLayout
+*/
+QSizeF QGraphicsWidget::size() const
+{
+ return QGraphicsLayoutItem::geometry().size();
+}
+
+void QGraphicsWidget::resize(const QSizeF &size)
+{
+ setGeometry(QRectF(pos(), size));
+}
+
+/*!
+ \fn void QGraphicsWidget::resize(qreal w, qreal h)
+
+ This convenience function is equivalent to calling resize(QSizeF(w, h)).
+
+ \sa setGeometry(), setTransform()
+*/
+
+/*!
+ \property QGraphicsWidget::geometry
+ \brief the geometry of the widget
+
+ Sets the item's geometry to \a rect. The item's position and size are
+ modified as a result of calling this function. The item is first moved,
+ then resized.
+
+ A side effect of calling this function is that the widget will receive
+ a move event and a resize event. Also, if the widget has a layout
+ assigned, the layout will activate.
+
+ \sa geometry(), resize()
+*/
+void QGraphicsWidget::setGeometry(const QRectF &rect)
+{
+ QGraphicsWidgetPrivate *wd = QGraphicsWidget::d_func();
+ const QGraphicsLayoutItemPrivate *d = QGraphicsLayoutItem::d_ptr;
+ setAttribute(Qt::WA_Resized);
+ QRectF newGeom = rect;
+ newGeom.setSize(rect.size().expandedTo(effectiveSizeHint(Qt::MinimumSize))
+ .boundedTo(effectiveSizeHint(Qt::MaximumSize)));
+ if (newGeom == d->geom)
+ return;
+
+ // Update and prepare to change the geometry (remove from index).
+ if (wd->scene) {
+ if (rect.topLeft() != d->geom.topLeft())
+ wd->fullUpdateHelper(true);
+ else
+ update();
+ }
+ prepareGeometryChange();
+
+ // setPos triggers ItemPositionChange, which can adjust position
+ QPointF oldPos = d->geom.topLeft();
+ wd->inSetGeometry = 1;
+ wd->setPosHelper(newGeom.topLeft(), /* update = */ false);
+ wd->inSetGeometry = 0;
+ newGeom.moveTopLeft(pos());
+
+ if (newGeom == d->geom)
+ return;
+
+ // Update the layout item geometry
+ bool moved = oldPos != pos();
+ if (moved) {
+ // Send move event.
+ QGraphicsSceneMoveEvent event;
+ event.setOldPos(oldPos);
+ event.setNewPos(pos());
+ QApplication::sendEvent(this, &event);
+ }
+ QSizeF oldSize = size();
+ QGraphicsLayoutItem::setGeometry(newGeom);
+
+ // Send resize event
+ bool resized = newGeom.size() != oldSize;
+ if (resized) {
+ QGraphicsSceneResizeEvent re;
+ re.setOldSize(oldSize);
+ re.setNewSize(newGeom.size());
+ QApplication::sendEvent(this, &re);
+ }
+}
+
+/*!
+ \fn QRectF QGraphicsWidget::rect() const
+
+ Returns the item's local rect as a QRectF. This function is equivalent
+ to QRectF(QPointF(), size()).
+
+ \sa setGeometry(), resize()
+*/
+
+/*!
+ \fn void QGraphicsWidget::setGeometry(qreal x, qreal y, qreal w, qreal h)
+
+ This convenience function is equivalent to calling setGeometry(QRectF(
+ \a x, \a y, \a w, \a h)).
+
+ \sa geometry(), resize()
+*/
+
+/*!
+ Sets the widget's contents margins to \a left, \a top, \a right and \a
+ bottom.
+
+ Contents margins are used by the assigned layout to define the placement
+ of subwidgets and layouts. Margins are particularily useful for widgets
+ that constrain subwidgets to only a section of its own geometry. For
+ example, a group box with a layout will place subwidgets inside its frame,
+ but below the title.
+
+ Changing a widget's contents margins will always trigger an update(), and
+ any assigned layout will be activated automatically. The widget will then
+ receive a \l{QEvent::ContentsRectChange}{ContentsRectChange} event.
+
+ \sa getContentsMargins(), setGeometry()
+*/
+void QGraphicsWidget::setContentsMargins(qreal left, qreal top, qreal right, qreal bottom)
+{
+ Q_D(QGraphicsWidget);
+
+ if (left == d->leftMargin
+ && top == d->topMargin
+ && right == d->rightMargin
+ && bottom == d->bottomMargin) {
+ return;
+ }
+
+ d->leftMargin = left;
+ d->topMargin = top;
+ d->rightMargin = right;
+ d->bottomMargin = bottom;
+
+ if (QGraphicsLayout *l = d->layout)
+ l->invalidate();
+ else
+ updateGeometry();
+
+ QEvent e(QEvent::ContentsRectChange);
+ QApplication::sendEvent(this, &e);
+}
+
+/*!
+ Gets the widget's contents margins. The margins are stored in \a left, \a
+ top, \a right and \a bottom, as pointers to qreals. Each argument can
+ be \e {omitted} by passing 0.
+
+ \sa setContentsMargins()
+*/
+void QGraphicsWidget::getContentsMargins(qreal *left, qreal *top, qreal *right, qreal *bottom) const
+{
+ Q_D(const QGraphicsWidget);
+ if (left)
+ *left = d->leftMargin;
+ if (top)
+ *top = d->topMargin;
+ if (right)
+ *right = d->rightMargin;
+ if (bottom)
+ *bottom = d->bottomMargin;
+}
+
+/*!
+ Sets the widget's window frame margins to \a left, \a top, \a right and
+ \a bottom. The default frame margins are provided by the style, and they
+ depend on the current window flags.
+
+ If you would like to draw your own window decoration, you can set your
+ own frame margins to override the default margins.
+
+ \sa unsetWindowFrameMargins(), getWindowFrameMargins(), windowFrameRect()
+*/
+void QGraphicsWidget::setWindowFrameMargins(qreal left, qreal top, qreal right, qreal bottom)
+{
+ Q_D(QGraphicsWidget);
+ bool unchanged = left == d->leftWindowFrameMargin && top == d->topWindowFrameMargin
+ && right == d->rightWindowFrameMargin && bottom == d->bottomWindowFrameMargin;
+ if (d->setWindowFrameMargins && unchanged)
+ return;
+ if (!unchanged)
+ prepareGeometryChange();
+ d->leftWindowFrameMargin = left;
+ d->topWindowFrameMargin = top;
+ d->rightWindowFrameMargin = right;
+ d->bottomWindowFrameMargin = bottom;
+ d->setWindowFrameMargins = true;
+}
+
+/*!
+ Gets the widget's window frame margins. The margins are stored in \a left,
+ \a top, \a right and \a bottom as pointers to qreals. Each argument can
+ be \e {omitted} by passing 0.
+
+ \sa setWindowFrameMargins(), windowFrameRect()
+*/
+void QGraphicsWidget::getWindowFrameMargins(qreal *left, qreal *top, qreal *right, qreal *bottom) const
+{
+ Q_D(const QGraphicsWidget);
+ if (left)
+ *left = d->leftWindowFrameMargin;
+ if (top)
+ *top = d->topWindowFrameMargin;
+ if (right)
+ *right = d->rightWindowFrameMargin;
+ if (bottom)
+ *bottom = d->bottomWindowFrameMargin;
+}
+
+/*!
+ Resets the window frame margins to the default value, provided by the style.
+
+ \sa setWindowFrameMargins(), getWindowFrameMargins(), windowFrameRect()
+*/
+void QGraphicsWidget::unsetWindowFrameMargins()
+{
+ Q_D(QGraphicsWidget);
+ if ((d->windowFlags & Qt::Window) && (d->windowFlags & Qt::WindowType_Mask) != Qt::Popup &&
+ (d->windowFlags & Qt::WindowType_Mask) != Qt::ToolTip && !(d->windowFlags & Qt::FramelessWindowHint)) {
+ QStyleOptionTitleBar bar;
+ d->initStyleOptionTitleBar(&bar);
+ QStyle *style = this->style();
+ qreal margin = style->pixelMetric(QStyle::PM_MdiSubWindowFrameWidth);
+ qreal titleBarHeight = d->titleBarHeight(bar);
+ setWindowFrameMargins(margin, titleBarHeight, margin, margin);
+ } else {
+ setWindowFrameMargins(0, 0, 0, 0);
+ }
+ d->setWindowFrameMargins = false;
+}
+
+/*!
+ Returns the widget's geometry in parent coordinates including any window
+ frame.
+
+ \sa windowFrameRect(), getWindowFrameMargins(), setWindowFrameMargins()
+*/
+QRectF QGraphicsWidget::windowFrameGeometry() const
+{
+ Q_D(const QGraphicsWidget);
+ return geometry().adjusted(-d->leftWindowFrameMargin, -d->topWindowFrameMargin,
+ d->rightWindowFrameMargin, d->bottomWindowFrameMargin);
+}
+
+/*!
+ Returns the widget's local rect including any window frame.
+
+ \sa windowFrameGeometry(), getWindowFrameMargins(), setWindowFrameMargins()
+*/
+QRectF QGraphicsWidget::windowFrameRect() const
+{
+ Q_D(const QGraphicsWidget);
+ return rect().adjusted(-d->leftWindowFrameMargin, -d->topWindowFrameMargin,
+ d->rightWindowFrameMargin, d->bottomWindowFrameMargin);
+}
+
+/*!
+ Populates a style option object for this widget based on its current
+ state, and stores the output in \a option. The default implementation
+ populates \a option with the following properties.
+
+ \table
+ \header
+ \o Style Option Property
+ \o Value
+ \row
+ \o state & QStyle::State_Enabled
+ \o Corresponds to QGraphicsItem::isEnabled().
+ \row
+ \o state & QStyle::State_HasFocus
+ \o Corresponds to QGraphicsItem::hasFocus().
+ \row
+ \o state & QStyle::State_MouseOver
+ \o Corresponds to QGraphicsItem::isUnderMouse().
+ \row
+ \o direction
+ \o Corresponds to QGraphicsWidget::layoutDirection().
+ \row
+ \o rect
+ \o Corresponds to QGraphicsWidget::rect().toRect().
+ \row
+ \o palette
+ \o Corresponds to QGraphicsWidget::palette().
+ \row
+ \o fontMetrics
+ \o Corresponds to QFontMetrics(QGraphicsWidget::font()).
+ \endtable
+
+ Subclasses of QGraphicsWidget should call the base implementation, and
+ then test the type of \a option using qstyleoption_cast<>() or test
+ QStyleOption::Type before storing widget-specific options.
+
+ For example:
+
+ \snippet doc/src/snippets/code/src_gui_graphicsview_qgraphicswidget.cpp 0
+
+ \sa QStyleOption::initFrom()
+*/
+void QGraphicsWidget::initStyleOption(QStyleOption *option) const
+{
+ Q_ASSERT(option);
+
+ option->state = QStyle::State_None;
+ if (isEnabled())
+ option->state |= QStyle::State_Enabled;
+ if (hasFocus())
+ option->state |= QStyle::State_HasFocus;
+ // if (window->testAttribute(Qt::WA_KeyboardFocusChange)) // ### Window
+ // option->state |= QStyle::State_KeyboardFocusChange;
+ if (isUnderMouse())
+ option->state |= QStyle::State_MouseOver;
+ if (QGraphicsWidget *w = window()) {
+ if (w->isActiveWindow())
+ option->state |= QStyle::State_Active;
+ }
+ if (isWindow())
+ option->state |= QStyle::State_Window;
+ /*
+ ###
+#ifdef Q_WS_MAC
+ extern bool qt_mac_can_clickThrough(const QGraphicsWidget *w); //qwidget_mac.cpp
+ if (!(option->state & QStyle::State_Active) && !qt_mac_can_clickThrough(widget))
+ option->state &= ~QStyle::State_Enabled;
+
+ switch (QMacStyle::widgetSizePolicy(widget)) {
+ case QMacStyle::SizeSmall:
+ option->state |= QStyle::State_Small;
+ break;
+ case QMacStyle::SizeMini:
+ option->state |= QStyle::State_Mini;
+ break;
+ default:
+ ;
+ }
+#endif
+#ifdef QT_KEYPAD_NAVIGATION
+ if (widget->hasEditFocus())
+ state |= QStyle::State_HasEditFocus;
+#endif
+ */
+ option->direction = layoutDirection();
+ option->rect = rect().toRect(); // ### truncation!
+ option->palette = palette();
+ if (!isEnabled()) {
+ option->palette.setCurrentColorGroup(QPalette::Disabled);
+ } else if (isActiveWindow()) {
+ option->palette.setCurrentColorGroup(QPalette::Active);
+ } else {
+ option->palette.setCurrentColorGroup(QPalette::Inactive);
+ }
+ option->fontMetrics = QFontMetrics(font());
+}
+
+/*!
+ \reimp
+*/
+QSizeF QGraphicsWidget::sizeHint(Qt::SizeHint which, const QSizeF &constraint) const
+{
+ Q_D(const QGraphicsWidget);
+ QSizeF sh;
+ if (d->layout) {
+ sh = d->layout->effectiveSizeHint(which, constraint);
+ sh += QSizeF(d->leftMargin + d->rightMargin, d->topMargin + d->bottomMargin);
+ } else {
+ switch (which) {
+ case Qt::MinimumSize:
+ sh = QSizeF(0, 0);
+ break;
+ case Qt::PreferredSize:
+ sh = QSizeF(50, 50); //rather arbitrary
+ break;
+ case Qt::MaximumSize:
+ sh = QSizeF(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX);
+ break;
+ default:
+ qWarning("QGraphicsWidget::sizeHint(): Don't know how to handle the value of 'which'");
+ break;
+ }
+ }
+ return sh;
+}
+
+/*!
+ Returns this widget's layout, or 0 if no layout is currently managing this
+ widget.
+
+ \sa setLayout()
+*/
+QGraphicsLayout *QGraphicsWidget::layout() const
+{
+ Q_D(const QGraphicsWidget);
+ return d->layout;
+}
+
+/*!
+ \fn void QGraphicsWidget::setLayout(QGraphicsLayout *layout)
+
+ Sets the layout for this widget to \a layout. Any existing layout manager
+ is deleted before the new layout is assigned. If \a layout is 0, the
+ widget is left without a layout. Existing subwidgets' geometries will
+ remain unaffected.
+
+ All widgets that are currently managed by \a layout or all of its
+ sublayouts, are automatically reparented to this item. The layout is then
+ invalidated, and the child widget geometries are adjusted according to
+ this item's geometry() and contentsMargins(). Children who are not
+ explicitly managed by \a layout remain unaffected by the layout after
+ it has been assigned to this widget.
+
+ QGraphicsWidget takes ownership of \a layout.
+
+ \sa layout(), QGraphicsLinearLayout::addItem(), QGraphicsLayout::invalidate()
+*/
+void QGraphicsWidget::setLayout(QGraphicsLayout *l)
+{
+ Q_D(QGraphicsWidget);
+ if (d->layout == l)
+ return;
+ d->setLayout_helper(l);
+ if (!l)
+ return;
+
+ // Prevent assigning a layout that is already assigned to another widget.
+ QGraphicsLayoutItem *oldParent = l->parentLayoutItem();
+ if (oldParent && oldParent != this) {
+ qWarning("QGraphicsWidget::setLayout: Attempting to set a layout on %s"
+ " \"%s\", when the layout already has a parent",
+ metaObject()->className(), qPrintable(objectName()));
+ return;
+ }
+
+ // Install and activate the layout.
+ l->setParentLayoutItem(this);
+ l->d_func()->reparentChildItems(this);
+ l->invalidate();
+}
+
+/*!
+ Adjusts the size of the widget to its effective preferred size hint.
+
+ This function is called implicitly when the item is shown for the first
+ time.
+
+ \sa effectiveSizeHint(), Qt::MinimumSize
+*/
+void QGraphicsWidget::adjustSize()
+{
+ QSizeF sz = effectiveSizeHint(Qt::PreferredSize);
+ // What if sz is not valid?!
+ if (sz.isValid())
+ resize(sz);
+}
+
+/*!
+ \property QGraphicsWidget::layoutDirection
+ \brief the layout direction for this widget.
+
+ This property modifies this widget's and all of its descendants'
+ Qt::WA_RightToLeft attribute. It also sets this widget's
+ Qt::WA_SetLayoutDirection attribute.
+
+ The widget's layout direction determines the order in which the layout
+ manager horizontally arranges subwidgets of this widget. The default
+ value depends on the language and locale of the application, and is
+ typically in the same direction as words are read and written. With
+ Qt::LeftToRight, the layout starts placing subwidgets from the left
+ side of this widget towards the right. Qt::RightToLeft does the opposite -
+ the layout will place widgets starting from the right edge moving towards
+ the left.
+
+ Subwidgets inherit their layout direction from the parent. Top-level
+ widget items inherit their layout direction from
+ QGraphicsScene::layoutDirection. If you change a widget's layout direction
+ by calling setLayoutDirection(), the widget will send itself a
+ \l{QEvent::LayoutDirectionChange}{LayoutDirectionChange} event, and then
+ propagate the new layout direction to all its descendants.
+
+ \sa QWidget::layoutDirection, QApplication::layoutDirection
+*/
+Qt::LayoutDirection QGraphicsWidget::layoutDirection() const
+{
+ return testAttribute(Qt::WA_RightToLeft) ? Qt::RightToLeft : Qt::LeftToRight;
+}
+void QGraphicsWidget::setLayoutDirection(Qt::LayoutDirection direction)
+{
+ Q_D(QGraphicsWidget);
+ setAttribute(Qt::WA_SetLayoutDirection, true);
+ d->setLayoutDirection_helper(direction);
+}
+void QGraphicsWidget::unsetLayoutDirection()
+{
+ Q_D(QGraphicsWidget);
+ setAttribute(Qt::WA_SetLayoutDirection, false);
+ d->resolveLayoutDirection();
+}
+
+/*!
+ Returns a pointer to the widget's style. If this widget does not have any
+ explicitly assigned style, the scene's style is returned instead. In turn,
+ if the scene does not have any assigned style, this function returns
+ QApplication::style().
+
+ \sa setStyle()
+*/
+QStyle *QGraphicsWidget::style() const
+{
+ if (QStyle *style = widgetStyles()->styleForWidget(this))
+ return style;
+ // ### This is not thread-safe. QApplication::style() is not thread-safe.
+ return scene() ? scene()->style() : QApplication::style();
+}
+
+/*!
+ Sets the widget's style to \a style. QGraphicsWidget does \e not take
+ ownership of \a style.
+
+ If no style is assigned, or \a style is 0, the widget will use
+ QGraphicsScene::style() (if this has been set). Otherwise the widget will
+ use QApplication::style().
+
+ This function sets the Qt::WA_SetStyle attribute if \a style is not 0;
+ otherwise it clears the attribute.
+
+ \sa style()
+*/
+void QGraphicsWidget::setStyle(QStyle *style)
+{
+ setAttribute(Qt::WA_SetStyle, style != 0);
+ widgetStyles()->setStyleForWidget(this, style);
+
+ // Deliver StyleChange to the widget itself (doesn't propagate).
+ QEvent event(QEvent::StyleChange);
+ QApplication::sendEvent(this, &event);
+}
+
+/*!
+ \property QGraphicsWidget::font
+ \brief the widgets' font
+
+ This property provides the widget's font.
+
+ QFont consists of font properties that have been explicitly defined and
+ properties implicitly inherited from the widget's parent. Hence, font()
+ can return a different font compared to the one set with setFont().
+ This scheme allows you to define single entries in a font without
+ affecting the font's inherited entries.
+
+ When a widget's font changes, it resolves its entries against its
+ parent widget. If the widget does not have a parent widget, it resolves
+ its entries against the scene. The widget then sends itself a
+ \l{QEvent::FontChange}{FontChange} event and notifies all its
+ descendants so that they can resolve their fonts as well.
+
+ By default, this property contains the application's default font.
+
+ \sa QApplication::font(), QGraphicsScene::font, QFont::resolve()
+*/
+QFont QGraphicsWidget::font() const
+{
+ Q_D(const QGraphicsWidget);
+ return d->font;
+}
+void QGraphicsWidget::setFont(const QFont &font)
+{
+ Q_D(QGraphicsWidget);
+ setAttribute(Qt::WA_SetFont, font.resolve() != 0);
+
+ QFont naturalFont = d->naturalWidgetFont();
+ QFont resolvedFont = font.resolve(naturalFont);
+ d->setFont_helper(resolvedFont);
+}
+
+/*!
+ \property QGraphicsWidget::palette
+ \brief the widget's palette
+
+ This property provides the widget's palette. The palette provides colors
+ and brushes for color groups (e.g., QPalette::Button) and states (e.g.,
+ QPalette::Inactive), loosely defining the general look of the widget and
+ its children.
+
+ QPalette consists of color groups that have been explicitly defined, and
+ groups that are implicitly inherited from the widget's parent. Because of
+ this, palette() can return a different palette than what has been set with
+ setPalette(). This scheme allows you to define single entries in a palette
+ without affecting the palette's inherited entries.
+
+ When a widget's palette changes, it resolves its entries against its
+ parent widget, or if it doesn't have a parent widget, it resolves against
+ the scene. It then sends itself a \l{QEvent::PaletteChange}{PaletteChange}
+ event, and notifies all its descendants so they can resolve their palettes
+ as well.
+
+ By default, this property contains the application's default palette.
+
+ \sa QApplication::palette(), QGraphicsScene::palette, QPalette::resolve()
+*/
+QPalette QGraphicsWidget::palette() const
+{
+ Q_D(const QGraphicsWidget);
+ return d->palette;
+}
+void QGraphicsWidget::setPalette(const QPalette &palette)
+{
+ Q_D(QGraphicsWidget);
+ setAttribute(Qt::WA_SetPalette, palette.resolve() != 0);
+
+ QPalette naturalPalette = d->naturalWidgetPalette();
+ QPalette resolvedPalette = palette.resolve(naturalPalette);
+ d->setPalette_helper(resolvedPalette);
+}
+
+/*!
+ If this widget is currently managed by a layout, this function notifies
+ the layout that the widget's size hints have changed and the layout
+ may need to resize and reposition the widget accordingly.
+
+ Call this function if the widget's sizeHint() has changed.
+
+ \sa QGraphicsLayout::invalidate()
+*/
+void QGraphicsWidget::updateGeometry()
+{
+ QGraphicsLayoutItem::updateGeometry();
+ QGraphicsLayoutItem *parentItem = parentLayoutItem();
+
+ if (parentItem && parentItem->isLayout()) {
+ parentItem->updateGeometry();
+ } else {
+ if (parentItem) {
+ QGraphicsWidget *parentWid = parentWidget(); //###
+ if (parentWid->isVisible())
+ QApplication::postEvent(parentWid, new QEvent(QEvent::LayoutRequest));
+ }
+ bool wasResized = testAttribute(Qt::WA_Resized);
+ resize(size()); // this will restrict the size
+ setAttribute(Qt::WA_Resized, wasResized);
+ }
+}
+
+/*!
+ \reimp
+
+ QGraphicsWidget uses the base implementation of this function to catch and
+ deliver events related to state changes in the item. Because of this, it is
+ very important that subclasses call the base implementation.
+
+ For example, QGraphicsWidget uses ItemVisibleChange to deliver \l Show and
+ \l Hide events, ItemPositionHasChanged to deliver \l Move events, and
+ ItemParentChange both to deliver \l ParentChange events, and for managing
+ the focus chain.
+
+ \sa propertyChange()
+*/
+QVariant QGraphicsWidget::itemChange(GraphicsItemChange change, const QVariant &value)
+{
+ Q_D(QGraphicsWidget);
+ switch (change) {
+ case ItemEnabledHasChanged: {
+ // Send EnabledChange after the enabled state has changed.
+ QEvent event(QEvent::EnabledChange);
+ QApplication::sendEvent(this, &event);
+ break;
+ }
+ case ItemVisibleChange:
+ if (value.toBool()) {
+ // Send Show event before the item has been shown.
+ QShowEvent event;
+ QApplication::sendEvent(this, &event);
+ bool resized = testAttribute(Qt::WA_Resized);
+ if (!resized) {
+ adjustSize();
+ setAttribute(Qt::WA_Resized, false);
+ }
+ }
+ break;
+ case ItemVisibleHasChanged:
+ if (!value.toBool()) {
+ // Send Hide event after the item has been hidden.
+ QHideEvent event;
+ QApplication::sendEvent(this, &event);
+ }
+ break;
+ case ItemPositionHasChanged:
+ if (!d->inSetGeometry) {
+ // Ensure setGeometry is called (avoid recursion when setPos is
+ // called from within setGeometry).
+ setGeometry(QRectF(pos(), size()));
+ }
+ break;
+ case ItemParentChange: {
+ QGraphicsItem *parent = qVariantValue<QGraphicsItem *>(value);
+ d->fixFocusChainBeforeReparenting((parent && parent->isWidget()) ? static_cast<QGraphicsWidget *>(parent) : 0);
+
+ // Deliver ParentAboutToChange.
+ QEvent event(QEvent::ParentAboutToChange);
+ QApplication::sendEvent(this, &event);
+ break;
+ }
+ case ItemParentHasChanged: {
+ // reset window type on parent change in order to automagically remove decorations etc.
+ Qt::WindowFlags wflags = d->windowFlags & ~Qt::WindowType_Mask;
+ d->adjustWindowFlags(&wflags);
+ setWindowFlags(wflags);
+ // Deliver ParentChange.
+ QEvent event(QEvent::ParentChange);
+ QApplication::sendEvent(this, &event);
+ break;
+ }
+ case ItemCursorChange: {
+ // Deliver CursorChange.
+ QEvent event(QEvent::CursorChange);
+ QApplication::sendEvent(this, &event);
+ break;
+ }
+ case ItemToolTipChange: {
+ // Deliver ToolTipChange.
+ QEvent event(QEvent::ToolTipChange);
+ QApplication::sendEvent(this, &event);
+ break;
+ }
+ default:
+ break;
+ }
+ return QGraphicsItem::itemChange(change, value);
+}
+
+/*!
+ \internal
+
+ This virtual function is used to notify changes to any property (both
+ dynamic properties, and registered with Q_PROPERTY) in the
+ widget. Depending on the property itself, the notification can be
+ delivered before or after the value has changed.
+
+ \a propertyName is the name of the property (e.g., "size" or "font"), and
+ \a value is the (proposed) new value of the property. The function returns
+ the new value, which may be different from \a value if the notification
+ supports adjusting the property value. The base implementation simply
+ returns \a value for any \a propertyName.
+
+ QGraphicsWidget delivers notifications for the following properties:
+
+ \table \o propertyName \o Property
+ \row \o layoutDirection \o QGraphicsWidget::layoutDirection
+ \row \o size \o QGraphicsWidget::size
+ \row \o font \o QGraphicsWidget::font
+ \row \o palette \o QGraphicsWidget::palette
+ \endtable
+
+ \sa itemChange()
+*/
+QVariant QGraphicsWidget::propertyChange(const QString &propertyName, const QVariant &value)
+{
+ Q_UNUSED(propertyName);
+ return value;
+}
+
+/*!
+ QGraphicsWidget's implementation of sceneEvent() simply passes \a event to
+ QGraphicsWidget::event(). You can handle all events for your widget in
+ event() or in any of the convenience functions; you should not have to
+ reimplement this function in a subclass of QGraphicsWidget.
+
+ \sa QGraphicsItem::sceneEvent()
+*/
+bool QGraphicsWidget::sceneEvent(QEvent *event)
+{
+ return QCoreApplication::sendEvent(this, event) || QGraphicsItem::sceneEvent(event);
+}
+
+/*!
+ This event handler, for \a event, receives events for the window frame if
+ this widget is a window. Its base implementation provides support for
+ default window frame interaction such as moving, resizing, etc.
+
+ You can reimplement this handler in a subclass of QGraphicsWidget to
+ provide your own custom window frame interaction support.
+
+ Returns true if \a event has been recognized and processed; otherwise,
+ returns false.
+
+ \sa event()
+*/
+bool QGraphicsWidget::windowFrameEvent(QEvent *event)
+{
+ Q_D(QGraphicsWidget);
+ switch (event->type()) {
+ case QEvent::GraphicsSceneMousePress:
+ d->windowFrameMousePressEvent(static_cast<QGraphicsSceneMouseEvent *>(event));
+ break;
+ case QEvent::GraphicsSceneMouseMove:
+ if (d->grabbedSection != Qt::NoSection) {
+ d->windowFrameMouseMoveEvent(static_cast<QGraphicsSceneMouseEvent *>(event));
+ event->accept();
+ }
+ break;
+ case QEvent::GraphicsSceneMouseRelease:
+ d->windowFrameMouseReleaseEvent(static_cast<QGraphicsSceneMouseEvent *>(event));
+ break;
+ case QEvent::GraphicsSceneHoverMove:
+ d->windowFrameHoverMoveEvent(static_cast<QGraphicsSceneHoverEvent *>(event));
+ break;
+ case QEvent::GraphicsSceneHoverLeave:
+ d->windowFrameHoverLeaveEvent(static_cast<QGraphicsSceneHoverEvent *>(event));
+ break;
+ default:
+ break;
+ }
+ return event->isAccepted();
+}
+
+/*!
+ \since 4.4
+
+ Returns the window frame section at position \a pos, or
+ Qt::NoSection if there is no window frame section at this
+ position.
+
+ This function is used in QGraphicsWidget's base implementation for window
+ frame interaction.
+
+ You can reimplement this function if you want to customize how a window
+ can be interactively moved or resized. For instance, if you only want to
+ allow a window to be resized by the bottom right corner, you can
+ reimplement this function to return Qt::NoSection for all sections except
+ Qt::BottomRightSection.
+
+ \sa windowFrameEvent(), paintWindowFrame(), windowFrameGeometry()
+*/
+Qt::WindowFrameSection QGraphicsWidget::windowFrameSectionAt(const QPointF &pos) const
+{
+ Q_D(const QGraphicsWidget);
+
+ const QRectF r = windowFrameRect();
+ if (!r.contains(pos))
+ return Qt::NoSection;
+
+ const qreal left = r.left();
+ const qreal top = r.top();
+ const qreal right = r.right();
+ const qreal bottom = r.bottom();
+ const qreal x = pos.x();
+ const qreal y = pos.y();
+
+ const qreal cornerMargin = 20;
+ //### Not sure of this one, it should be the same value for all edges.
+ const qreal windowFrameWidth = d->leftWindowFrameMargin;
+
+ Qt::WindowFrameSection s = Qt::NoSection;
+ if (x <= left + cornerMargin) {
+ if (y <= top + windowFrameWidth || (x <= left + windowFrameWidth && y <= top + cornerMargin)) {
+ s = Qt::TopLeftSection;
+ } else if (y >= bottom - windowFrameWidth || (x <= left + windowFrameWidth && y >= bottom - windowFrameWidth)) {
+ s = Qt::BottomLeftSection;
+ } else if (x <= left + windowFrameWidth) {
+ s = Qt::LeftSection;
+ }
+ } else if (x >= right - cornerMargin) {
+ if (y <= top + windowFrameWidth || (x >= right - windowFrameWidth && y <= top + cornerMargin)) {
+ s = Qt::TopRightSection;
+ } else if (y >= bottom - windowFrameWidth || (x >= right - windowFrameWidth && y >= bottom - windowFrameWidth)) {
+ s = Qt::BottomRightSection;
+ } else if (x >= right - windowFrameWidth) {
+ s = Qt::RightSection;
+ }
+ } else if (y <= top + windowFrameWidth) {
+ s = Qt::TopSection;
+ } else if (y >= bottom - windowFrameWidth) {
+ s = Qt::BottomSection;
+ }
+ if (s == Qt::NoSection) {
+ QRectF r1 = r;
+ r1.setHeight(d->topWindowFrameMargin);
+ if (r1.contains(pos))
+ s = Qt::TitleBarArea;
+ }
+ return s;
+}
+
+/*!
+ \reimp
+
+ QGraphicsWidget handles the following events:
+
+ \table \o Event \o Usage
+ \row \o Polish
+ \o Delivered to the widget some time after it has been
+ shown.
+ \row \o GraphicsSceneMove
+ \o Delivered to the widget after its local position has
+ changed.
+ \row \o GraphicsSceneResize
+ \o Delivered to the widget after its size has changed.
+ \row \o Show
+ \o Delivered to the widget before it has been shown.
+ \row \o Hide
+ \o Delivered to the widget after it has been hidden.
+ \row \o PaletteChange
+ \o Delivered to the widget after its palette has changed.
+ \row \o FontChange
+ \o Delivered to the widget after its font has changed.
+ \row \o EnabledChange
+ \o Delivered to the widget after its enabled state has
+ changed.
+ \row \o StyleChange
+ \o Delivered to the widget after its style has changed.
+ \row \o LayoutDirectionChange
+ \o Delivered to the widget after its layout direction has
+ changed.
+ \row \o ContentsRectChange
+ \o Delivered to the widget after its contents margins/
+ contents rect has changed.
+ \endtable
+*/
+bool QGraphicsWidget::event(QEvent *event)
+{
+ Q_D(QGraphicsWidget);
+ // Forward the event to the layout first.
+ if (d->layout)
+ d->layout->widgetEvent(event);
+
+ // Handle the event itself.
+ switch (event->type()) {
+ case QEvent::GraphicsSceneMove:
+ moveEvent(static_cast<QGraphicsSceneMoveEvent *>(event));
+ break;
+ case QEvent::GraphicsSceneResize:
+ resizeEvent(static_cast<QGraphicsSceneResizeEvent *>(event));
+ break;
+ case QEvent::Show:
+ showEvent(static_cast<QShowEvent *>(event));
+ break;
+ case QEvent::Hide:
+ hideEvent(static_cast<QHideEvent *>(event));
+ break;
+ case QEvent::Polish:
+ polishEvent();
+ break;
+ case QEvent::WindowActivate:
+ case QEvent::WindowDeactivate:
+ update();
+ foreach (QGraphicsItem *child, childItems()) {
+ if (child->isWidget())
+ QApplication::sendEvent(static_cast<QGraphicsWidget *>(child), event);
+ }
+ break;
+ // Taken from QWidget::event
+ case QEvent::ActivationChange:
+ case QEvent::EnabledChange:
+ case QEvent::FontChange:
+ case QEvent::StyleChange:
+ case QEvent::PaletteChange:
+ case QEvent::ParentChange:
+ case QEvent::ContentsRectChange:
+ case QEvent::LayoutDirectionChange:
+ changeEvent(event);
+ break;
+ case QEvent::Close:
+ closeEvent((QCloseEvent *)event);
+ break;
+ case QEvent::GrabMouse:
+ grabMouseEvent(event);
+ break;
+ case QEvent::UngrabMouse:
+ ungrabMouseEvent(event);
+ break;
+ case QEvent::GrabKeyboard:
+ grabKeyboardEvent(event);
+ break;
+ case QEvent::UngrabKeyboard:
+ ungrabKeyboardEvent(event);
+ break;
+ case QEvent::GraphicsSceneMousePress:
+ if (d->hasDecoration() && windowFrameEvent(event))
+ return true;
+ case QEvent::GraphicsSceneMouseMove:
+ case QEvent::GraphicsSceneMouseRelease:
+ case QEvent::GraphicsSceneMouseDoubleClick:
+ if (d->hasDecoration() && d->grabbedSection != Qt::NoSection)
+ return windowFrameEvent(event);
+ break;
+ case QEvent::GraphicsSceneHoverEnter:
+ case QEvent::GraphicsSceneHoverMove:
+ case QEvent::GraphicsSceneHoverLeave:
+ if (d->hasDecoration()) {
+ windowFrameEvent(event);
+ // Filter out hover events if they were sent to us only because of the
+ // decoration (special case in QGraphicsScenePrivate::dispatchHoverEvent).
+ if (!acceptsHoverEvents())
+ return true;
+ }
+ break;
+ default:
+ break;
+ }
+ return QObject::event(event);
+}
+
+/*!
+ This event handler can be reimplemented to handle state changes.
+
+ The state being changed in this event can be retrieved through \a event.
+
+ Change events include: QEvent::ActivationChange, QEvent::EnabledChange,
+ QEvent::FontChange, QEvent::StyleChange, QEvent::PaletteChange,
+ QEvent::ParentChange, QEvent::LayoutDirectionChange, and
+ QEvent::ContentsRectChange.
+*/
+void QGraphicsWidget::changeEvent(QEvent *event)
+{
+ Q_D(QGraphicsWidget);
+ switch (event->type()) {
+ case QEvent::StyleChange:
+ // ### Don't unset if the margins are explicitly set.
+ unsetWindowFrameMargins();
+ case QEvent::FontChange:
+ update();
+ updateGeometry();
+ break;
+ case QEvent::PaletteChange:
+ update();
+ break;
+ case QEvent::ParentChange:
+ d->resolveFont(d->inheritedFontResolveMask);
+ d->resolvePalette(d->inheritedPaletteResolveMask);
+ break;
+ default:
+ break;
+ }
+}
+
+/*!
+ This event handler, for \a event, can be reimplemented in a subclass to
+ receive widget close events. The default implementation accepts the
+ event.
+
+ \sa close(), QCloseEvent
+*/
+void QGraphicsWidget::closeEvent(QCloseEvent *event)
+{
+ event->accept();
+}
+
+/*!
+ \reimp
+*/
+void QGraphicsWidget::focusInEvent(QFocusEvent *event)
+{
+ Q_UNUSED(event);
+ if (focusPolicy() != Qt::NoFocus)
+ update();
+}
+
+/*!
+ Finds a new widget to give the keyboard focus to, as appropriate for Tab
+ and Shift+Tab, and returns true if it can find a new widget; returns false
+ otherwise. If \a next is true, this function searches forward; if \a next
+ is false, it searches backward.
+
+ Sometimes, you will want to reimplement this function to provide special
+ focus handling for your widget and its subwidgets. For example, a web
+ browser might reimplement it to move its current active link forward or
+ backward, and call the base implementation only when it reaches the last
+ or first link on the page.
+
+ Child widgets call focusNextPrevChild() on their parent widgets, but only
+ the window that contains the child widgets decides where to redirect
+ focus. By reimplementing this function for an object, you gain control of
+ focus traversal for all child widgets.
+
+ \sa focusPolicy()
+*/
+bool QGraphicsWidget::focusNextPrevChild(bool next)
+{
+ Q_D(QGraphicsWidget);
+ // Let the parent's focusNextPrevChild implementation decide what to do.
+ QGraphicsWidget *parent = 0;
+ if (!isWindow() && (parent = parentWidget()))
+ return parent->focusNextPrevChild(next);
+ if (!d->scene)
+ return false;
+ if (d->scene->focusNextPrevChild(next))
+ return true;
+ if (isWindow()) {
+ setFocus(next ? Qt::TabFocusReason : Qt::BacktabFocusReason);
+ if (hasFocus())
+ return true;
+ }
+ return false;
+}
+
+/*!
+ \reimp
+*/
+void QGraphicsWidget::focusOutEvent(QFocusEvent *event)
+{
+ Q_UNUSED(event);
+ if (focusPolicy() != Qt::NoFocus)
+ update();
+}
+
+/*!
+ This event handler, for \l{QEvent::Hide}{Hide} events, is delivered after
+ the widget has been hidden, for example, setVisible(false) has been called
+ for the widget or one of its ancestors when the widget was previously
+ shown.
+
+ You can reimplement this event handler to detect when your widget is
+ hidden. Calling QEvent::accept() or QEvent::ignore() on \a event has no
+ effect.
+
+ \sa showEvent(), QWidget::hideEvent(), ItemVisibleChange
+*/
+void QGraphicsWidget::hideEvent(QHideEvent *event)
+{
+ ///### focusNextPrevChild(true), don't lose focus when the focus widget
+ // is hidden.
+ Q_UNUSED(event);
+}
+
+/*!
+ This event handler, for \l{QEvent::GraphicsSceneMove}{GraphicsSceneMove}
+ events, is delivered after the widget has moved (e.g., its local position
+ has changed).
+
+ This event is only delivered when the item is moved locally. Calling
+ setTransform() or moving any of the item's ancestors does not affect the
+ item's local position.
+
+ You can reimplement this event handler to detect when your widget has
+ moved. Calling QEvent::accept() or QEvent::ignore() on \a event has no
+ effect.
+
+ \sa ItemPositionChange, ItemPositionHasChanged
+*/
+void QGraphicsWidget::moveEvent(QGraphicsSceneMoveEvent *event)
+{
+ // ### Last position is always == current position
+ Q_UNUSED(event);
+}
+
+/*!
+ This event is delivered to the item by the scene at some point after it
+ has been constructed, but before it is shown or otherwise accessed through
+ the scene. You can use this event handler to do last-minute initializations
+ of the widget which require the item to be fully constructed.
+
+ The base implementation does nothing.
+*/
+void QGraphicsWidget::polishEvent()
+{
+}
+
+/*!
+ This event handler, for
+ \l{QEvent::GraphicsSceneResize}{GraphicsSceneResize} events, is
+ delivered after the widget has been resized (i.e., its local size has
+ changed). \a event contains both the old and the new size.
+
+ This event is only delivered when the widget is resized locally; calling
+ setTransform() on the widget or any of its ancestors or view, does not
+ affect the widget's local size.
+
+ You can reimplement this event handler to detect when your widget has been
+ resized. Calling QEvent::accept() or QEvent::ignore() on \a event has no
+ effect.
+
+ \sa geometry(), setGeometry()
+*/
+void QGraphicsWidget::resizeEvent(QGraphicsSceneResizeEvent *event)
+{
+ Q_UNUSED(event);
+}
+
+/*!
+ This event handler, for \l{QEvent::Show}{Show} events, is delivered before
+ the widget has been shown, for example, setVisible(true) has been called
+ for the widget or one of its ancestors when the widget was previously
+ hidden.
+
+ You can reimplement this event handler to detect when your widget is
+ shown. Calling QEvent::accept() or QEvent::ignore() on \a event has no
+ effect.
+
+ \sa hideEvent(), QWidget::showEvent(), ItemVisibleChange
+*/
+void QGraphicsWidget::showEvent(QShowEvent *event)
+{
+ Q_UNUSED(event);
+}
+
+/*!
+ \reimp
+*/
+void QGraphicsWidget::hoverMoveEvent(QGraphicsSceneHoverEvent *event)
+{
+ Q_UNUSED(event);
+}
+
+/*!
+ \reimp
+*/
+void QGraphicsWidget::hoverLeaveEvent(QGraphicsSceneHoverEvent *event)
+{
+ Q_UNUSED(event);
+}
+
+/*!
+ This event handler, for \a event, can be reimplemented in a subclass to
+ receive notifications for Qt::GrabMouse events.
+
+ \sa grabMouse(), grabKeyboard()
+*/
+void QGraphicsWidget::grabMouseEvent(QEvent *event)
+{
+ Q_UNUSED(event);
+}
+
+/*!
+ This event handler, for \a event, can be reimplemented in a subclass to
+ receive notifications for Qt::UngrabMouse events.
+
+ \sa ungrabMouse(), ungrabKeyboard()
+*/
+void QGraphicsWidget::ungrabMouseEvent(QEvent *event)
+{
+ Q_UNUSED(event);
+}
+
+/*!
+ This event handler, for \a event, can be reimplemented in a subclass to
+ receive notifications for Qt::GrabKeyboard events.
+
+ \sa grabKeyboard(), grabMouse()
+*/
+void QGraphicsWidget::grabKeyboardEvent(QEvent *event)
+{
+ Q_UNUSED(event);
+}
+
+/*!
+ This event handler, for \a event, can be reimplemented in a subclass to
+ receive notifications for Qt::UngrabKeyboard events.
+
+ \sa ungrabKeyboard(), ungrabMouse()
+*/
+void QGraphicsWidget::ungrabKeyboardEvent(QEvent *event)
+{
+ Q_UNUSED(event);
+}
+
+/*!
+ Returns the widgets window type.
+*/
+Qt::WindowType QGraphicsWidget::windowType() const
+{
+ return Qt::WindowType(int(windowFlags()) & Qt::WindowType_Mask);
+}
+
+/*!
+ \property QGraphicsWidget::windowFlags
+ \brief the widget's window flags
+
+ Window flags are a combination of a window type (e.g., Qt::Dialog) and
+ several flags giving hints on the behavior of the window. The behavior
+ is platform-dependent.
+
+ By default, this property contains no window flags.
+*/
+Qt::WindowFlags QGraphicsWidget::windowFlags() const
+{
+ Q_D(const QGraphicsWidget);
+ return d->windowFlags;
+}
+void QGraphicsWidget::setWindowFlags(Qt::WindowFlags wFlags)
+{
+ Q_D(QGraphicsWidget);
+ if (d->windowFlags == wFlags)
+ return;
+ bool wasPopup = (d->windowFlags & Qt::WindowType_Mask) == Qt::Popup;
+
+ d->windowFlags = wFlags;
+ if (!d->setWindowFrameMargins)
+ unsetWindowFrameMargins();
+
+ bool isPopup = (d->windowFlags & Qt::WindowType_Mask) == Qt::Popup;
+ if (d->scene && isVisible() && wasPopup != isPopup) {
+ // Popup state changed; update implicit mouse grab.
+ if (!isPopup)
+ d->scene->d_func()->removePopup(this);
+ else
+ d->scene->d_func()->addPopup(this);
+ }
+}
+
+/*!
+ Returns true if this widget's window is in the active window, or if the
+ widget does not have a window but is in an active scene (i.e., a scene
+ that currently has focus).
+
+ The active window is the window that either contains a child widget that
+ currently has input focus, or that itself has input focus.
+
+ \sa QGraphicsScene::activeWindow(), QGraphicsScene::setActiveWindow()
+*/
+bool QGraphicsWidget::isActiveWindow() const
+{
+ Q_D(const QGraphicsWidget);
+ if (!d->scene)
+ return false;
+ const QGraphicsWidget *w = window();
+ return (!w && d->scene->d_func()->activationRefCount) || (w && d->scene->activeWindow() == w);
+}
+
+/*!
+ \property QGraphicsWidget::windowTitle
+ \brief This property holds the window title (caption).
+
+ This property is only used for windows.
+
+ By default, if no title has been set, this property contains an empty string.
+*/
+void QGraphicsWidget::setWindowTitle(const QString &title)
+{
+ Q_D(QGraphicsWidget);
+ d->windowTitle = title;
+}
+QString QGraphicsWidget::windowTitle() const
+{
+ Q_D(const QGraphicsWidget);
+ return d->windowTitle;
+}
+
+/*!
+ \property QGraphicsWidget::focusPolicy
+ \brief the way the widget accepts keyboard focus
+
+ The focus policy is Qt::TabFocus if the widget accepts keyboard focus by
+ tabbing, Qt::ClickFocus if the widget accepts focus by clicking,
+ Qt::StrongFocus if it accepts both, and Qt::NoFocus (the default) if it
+ does not accept focus at all.
+
+ You must enable keyboard focus for a widget if it processes keyboard
+ events. This is normally done from the widget's constructor. For instance,
+ the QLineEdit constructor calls setFocusPolicy(Qt::StrongFocus).
+
+ If you enable a focus policy (i.e., not Qt::NoFocus), QGraphicsWidget will
+ automatically enable the ItemIsFocusable flag. Setting Qt::NoFocus on a
+ widget will clear the ItemIsFocusable flag. If the widget currently has
+ keyboard focus, the widget will automatically lose focus.
+
+ \sa focusInEvent(), focusOutEvent(), keyPressEvent(), keyReleaseEvent(), enabled
+*/
+Qt::FocusPolicy QGraphicsWidget::focusPolicy() const
+{
+ Q_D(const QGraphicsWidget);
+ return d->focusPolicy;
+}
+void QGraphicsWidget::setFocusPolicy(Qt::FocusPolicy policy)
+{
+ Q_D(QGraphicsWidget);
+ if (d->focusPolicy == policy)
+ return;
+ d->focusPolicy = policy;
+ if (hasFocus() && policy == Qt::NoFocus)
+ clearFocus();
+ setFlag(ItemIsFocusable, policy != Qt::NoFocus);
+}
+
+/*!
+ If this widget, a child or descendant of this widget currently has input
+ focus, this function will return a pointer to that widget. If
+ no descendant has input focus, 0 is returned.
+
+ \sa QWidget::focusWidget()
+*/
+QGraphicsWidget *QGraphicsWidget::focusWidget() const
+{
+ Q_D(const QGraphicsWidget);
+ return d->focusChild;
+}
+
+
+#ifndef QT_NO_SHORTCUT
+/*!
+ \since 4.5
+
+ Adds a shortcut to Qt's shortcut system that watches for the given key \a
+ sequence in the given \a context. If the \a context is
+ Qt::ApplicationShortcut, the shortcut applies to the application as a
+ whole. Otherwise, it is either local to this widget, Qt::WidgetShortcut,
+ or to the window itself, Qt::WindowShortcut. For widgets that are not part
+ of a window (i.e., top-level widgets and their children),
+ Qt::WindowShortcut shortcuts apply to the scene.
+
+ If the same key \a sequence has been grabbed by several widgets,
+ when the key \a sequence occurs a QEvent::Shortcut event is sent
+ to all the widgets to which it applies in a non-deterministic
+ order, but with the ``ambiguous'' flag set to true.
+
+ \warning You should not normally need to use this function;
+ instead create \l{QAction}s with the shortcut key sequences you
+ require (if you also want equivalent menu options and toolbar
+ buttons), or create \l{QShortcut}s if you just need key sequences.
+ Both QAction and QShortcut handle all the event filtering for you,
+ and provide signals which are triggered when the user triggers the
+ key sequence, so are much easier to use than this low-level
+ function.
+
+ \sa releaseShortcut() setShortcutEnabled() QWidget::grabShortcut()
+*/
+int QGraphicsWidget::grabShortcut(const QKeySequence &sequence, Qt::ShortcutContext context)
+{
+ Q_ASSERT(qApp);
+ if (sequence.isEmpty())
+ return 0;
+ // ### setAttribute(Qt::WA_GrabbedShortcut);
+ return qApp->d_func()->shortcutMap.addShortcut(this, sequence, context);
+}
+
+/*!
+ \since 4.5
+
+ Removes the shortcut with the given \a id from Qt's shortcut
+ system. The widget will no longer receive QEvent::Shortcut events
+ for the shortcut's key sequence (unless it has other shortcuts
+ with the same key sequence).
+
+ \warning You should not normally need to use this function since
+ Qt's shortcut system removes shortcuts automatically when their
+ parent widget is destroyed. It is best to use QAction or
+ QShortcut to handle shortcuts, since they are easier to use than
+ this low-level function. Note also that this is an expensive
+ operation.
+
+ \sa grabShortcut() setShortcutEnabled() , QWidget::releaseShortcut()
+*/
+void QGraphicsWidget::releaseShortcut(int id)
+{
+ Q_ASSERT(qApp);
+ if (id)
+ qApp->d_func()->shortcutMap.removeShortcut(id, this, 0);
+}
+
+/*!
+ \since 4.5
+
+ If \a enabled is true, the shortcut with the given \a id is
+ enabled; otherwise the shortcut is disabled.
+
+ \warning You should not normally need to use this function since
+ Qt's shortcut system enables/disables shortcuts automatically as
+ widgets become hidden/visible and gain or lose focus. It is best
+ to use QAction or QShortcut to handle shortcuts, since they are
+ easier to use than this low-level function.
+
+ \sa grabShortcut() releaseShortcut(), QWidget::setShortcutEnabled()
+*/
+void QGraphicsWidget::setShortcutEnabled(int id, bool enabled)
+{
+ Q_ASSERT(qApp);
+ if (id)
+ qApp->d_func()->shortcutMap.setShortcutEnabled(enabled, id, this, 0);
+}
+
+/*!
+ \since 4.5
+
+ If \a enabled is true, auto repeat of the shortcut with the
+ given \a id is enabled; otherwise it is disabled.
+
+ \sa grabShortcut() releaseShortcut() QWidget::setShortcutAutoRepeat()
+*/
+void QGraphicsWidget::setShortcutAutoRepeat(int id, bool enabled)
+{
+ Q_ASSERT(qApp);
+ if (id)
+ qApp->d_func()->shortcutMap.setShortcutAutoRepeat(enabled, id, this, 0);
+}
+#endif
+
+#ifndef QT_NO_ACTION
+/*!
+ \since 4.5
+
+ Appends the action \a action to this widget's list of actions.
+
+ All QGraphicsWidgets have a list of \l{QAction}s, however they can be
+ represented graphically in many different ways. The default use of the
+ QAction list (as returned by actions()) is to create a context QMenu.
+
+ A QGraphicsWidget should only have one of each action and adding an action
+ it already has will not cause the same action to be in the widget twice.
+
+ \sa removeAction(), insertAction(), actions(), QWidget::addAction()
+*/
+void QGraphicsWidget::addAction(QAction *action)
+{
+ insertAction(0, action);
+}
+
+/*!
+ \since 4.5
+
+ Appends the actions \a actions to this widget's list of actions.
+
+ \sa removeAction(), QMenu, addAction(), QWidget::addActions()
+*/
+void QGraphicsWidget::addActions(QList<QAction *> actions)
+{
+ for (int i = 0; i < actions.count(); ++i)
+ insertAction(0, actions.at(i));
+}
+
+/*!
+ \since 4.5
+
+ Inserts the action \a action to this widget's list of actions,
+ before the action \a before. It appends the action if \a before is 0 or
+ \a before is not a valid action for this widget.
+
+ A QGraphicsWidget should only have one of each action.
+
+ \sa removeAction(), addAction(), QMenu, actions(),
+ QWidget::insertActions()
+*/
+void QGraphicsWidget::insertAction(QAction *before, QAction *action)
+{
+ if (!action) {
+ qWarning("QWidget::insertAction: Attempt to insert null action");
+ return;
+ }
+
+ Q_D(QGraphicsWidget);
+ int index = d->actions.indexOf(action);
+ if (index != -1)
+ d->actions.removeAt(index);
+
+ int pos = d->actions.indexOf(before);
+ if (pos < 0) {
+ before = 0;
+ pos = d->actions.size();
+ }
+ d->actions.insert(pos, action);
+
+ QActionPrivate *apriv = action->d_func();
+ apriv->graphicsWidgets.append(this);
+
+ QActionEvent e(QEvent::ActionAdded, action, before);
+ QApplication::sendEvent(this, &e);
+}
+
+/*!
+ \since 4.5
+
+ Inserts the actions \a actions to this widget's list of actions,
+ before the action \a before. It appends the action if \a before is 0 or
+ \a before is not a valid action for this widget.
+
+ A QGraphicsWidget can have at most one of each action.
+
+ \sa removeAction(), QMenu, insertAction(), QWidget::insertActions()
+*/
+void QGraphicsWidget::insertActions(QAction *before, QList<QAction *> actions)
+{
+ for (int i = 0; i < actions.count(); ++i)
+ insertAction(before, actions.at(i));
+}
+
+/*!
+ \since 4.5
+
+ Removes the action \a action from this widget's list of actions.
+
+ \sa insertAction(), actions(), insertAction(), QWidget::removeAction()
+*/
+void QGraphicsWidget::removeAction(QAction *action)
+{
+ if (!action)
+ return;
+
+ Q_D(QGraphicsWidget);
+
+ QActionPrivate *apriv = action->d_func();
+ apriv->graphicsWidgets.removeAll(this);
+
+ if (d->actions.removeAll(action)) {
+ QActionEvent e(QEvent::ActionRemoved, action);
+ QApplication::sendEvent(this, &e);
+ }
+}
+
+/*!
+ \since 4.5
+
+ Returns the (possibly empty) list of this widget's actions.
+
+ \sa insertAction(), removeAction(), QWidget::actions(),
+ QAction::associatedWidgets(), QAction::associatedGraphicsWidgets()
+*/
+QList<QAction *> QGraphicsWidget::actions() const
+{
+ Q_D(const QGraphicsWidget);
+ return d->actions;
+}
+#endif
+
+/*!
+ Moves the \a second widget around the ring of focus widgets so that
+ keyboard focus moves from the \a first widget to the \a second widget when
+ the Tab key is pressed.
+
+ Note that since the tab order of the \a second widget is changed, you
+ should order a chain like this:
+
+ \snippet doc/src/snippets/code/src_gui_graphicsview_qgraphicswidget.cpp 1
+
+ \e not like this:
+
+ \snippet doc/src/snippets/code/src_gui_graphicsview_qgraphicswidget.cpp 2
+
+ If \a first is 0, this indicates that \a second should be the first widget
+ to receive input focus should the scene gain Tab focus (i.e., the user
+ hits Tab so that focus passes into the scene). If \a second is 0, this
+ indicates that \a first should be the first widget to gain focus if the
+ scene gained BackTab focus.
+
+ By default, tab order is defined implicitly using widget creation order.
+
+ \sa focusPolicy, {Keyboard Focus}
+*/
+void QGraphicsWidget::setTabOrder(QGraphicsWidget *first, QGraphicsWidget *second)
+{
+ if (!first && !second) {
+ qWarning("QGraphicsWidget::setTabOrder(0, 0) is undefined");
+ return;
+ }
+ if ((first && second) && first->scene() != second->scene()) {
+ qWarning("QGraphicsWidget::setTabOrder: scenes %p and %p are different",
+ first->scene(), second->scene());
+ return;
+ }
+ QGraphicsScene *scene = first ? first->scene() : second->scene();
+ if (!scene && (!first || !second)) {
+ qWarning("QGraphicsWidget::setTabOrder: assigning tab order from/to the"
+ " scene requires the item to be in a scene.");
+ return;
+ }
+
+ // If either first or second are 0, the scene's tabFocusFirst is updated
+ // to point to the first item in the scene's focus chain. Then first or
+ // second are set to point to tabFocusFirst.
+ QGraphicsScenePrivate *sceneD = scene->d_func();
+ if (!first) {
+ sceneD->tabFocusFirst = second;
+ return;
+ }
+ if (!second) {
+ sceneD->tabFocusFirst = first->d_func()->focusNext;
+ return;
+ }
+
+ // Both first and second are != 0.
+ QGraphicsWidget *firstFocusNext = first->d_func()->focusNext;
+ if (firstFocusNext == second) {
+ // Nothing to do.
+ return;
+ }
+
+ // Update the focus chain.
+ QGraphicsWidget *secondFocusPrev = second->d_func()->focusPrev;
+ QGraphicsWidget *secondFocusNext = second->d_func()->focusNext;
+ firstFocusNext->d_func()->focusPrev = second;
+ first->d_func()->focusNext = second;
+ second->d_func()->focusNext = firstFocusNext;
+ second->d_func()->focusPrev = first;
+ secondFocusPrev->d_func()->focusNext = secondFocusNext;
+ secondFocusNext->d_func()->focusPrev = secondFocusPrev;
+
+ Q_ASSERT(first->d_func()->focusNext->d_func()->focusPrev == first);
+ Q_ASSERT(first->d_func()->focusPrev->d_func()->focusNext == first);
+
+ Q_ASSERT(second->d_func()->focusNext->d_func()->focusPrev == second);
+ Q_ASSERT(second->d_func()->focusPrev->d_func()->focusNext == second);
+
+}
+
+/*!
+ If \a on is true, this function enables \a attribute; otherwise
+ \a attribute is disabled.
+
+ See the class documentation for QGraphicsWidget for a complete list of
+ which attributes are supported, and what they are for.
+
+ \sa testAttribute(), QWidget::setAttribute()
+*/
+void QGraphicsWidget::setAttribute(Qt::WidgetAttribute attribute, bool on)
+{
+ Q_D(QGraphicsWidget);
+ // ### most flags require some immediate action
+ // ### we might want to qWarn use of unsupported attributes
+ // ### we might want to not use Qt::WidgetAttribute, but roll our own instead
+ d->setAttribute(attribute, on);
+}
+
+/*!
+ Returns true if \a attribute is enabled for this widget; otherwise,
+ returns false.
+
+ \sa setAttribute()
+*/
+bool QGraphicsWidget::testAttribute(Qt::WidgetAttribute attribute) const
+{
+ Q_D(const QGraphicsWidget);
+ return d->testAttribute(attribute);
+}
+
+/*!
+ \reimp
+*/
+int QGraphicsWidget::type() const
+{
+ return Type;
+}
+
+/*!
+ \reimp
+*/
+void QGraphicsWidget::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
+{
+ Q_UNUSED(painter);
+ Q_UNUSED(option);
+ Q_UNUSED(widget);
+}
+
+/*!
+ This virtual function is called by QGraphicsScene to draw the window frame
+ for windows using \a painter, \a option, and \a widget, in local
+ coordinates. The base implementation uses the current style to render the
+ frame and title bar.
+
+ You can reimplement this function in a subclass of QGraphicsWidget to
+ provide custom rendering of the widget's window frame.
+
+ \sa QGraphicsItem::paint()
+*/
+void QGraphicsWidget::paintWindowFrame(QPainter *painter, const QStyleOptionGraphicsItem *option,
+ QWidget *widget)
+{
+ const bool fillBackground = !testAttribute(Qt::WA_OpaquePaintEvent)
+ && !testAttribute(Qt::WA_NoSystemBackground);
+ QGraphicsProxyWidget *proxy = qobject_cast<QGraphicsProxyWidget *>(this);
+ const bool embeddedWidgetFillsOwnBackground = proxy && proxy->widget();
+
+ if (rect().contains(option->exposedRect)) {
+ if (fillBackground && !embeddedWidgetFillsOwnBackground)
+ painter->fillRect(option->exposedRect, palette().window());
+ return;
+ }
+
+ Q_D(QGraphicsWidget);
+
+ QRect windowFrameRect = QRect(QPoint(), windowFrameGeometry().size().toSize());
+ QStyleOptionTitleBar bar;
+ bar.QStyleOption::operator=(*option);
+ d->initStyleOptionTitleBar(&bar); // this clear flags in bar.state
+ if (d->buttonMouseOver)
+ bar.state |= QStyle::State_MouseOver;
+ else
+ bar.state &= ~QStyle::State_MouseOver;
+ if (d->buttonSunken)
+ bar.state |= QStyle::State_Sunken;
+ else
+ bar.state &= ~QStyle::State_Sunken;
+
+ bar.rect = windowFrameRect;
+
+ // translate painter to make the style happy
+ const QPointF styleOrigin = this->windowFrameRect().topLeft();
+ painter->translate(styleOrigin);
+
+#ifdef Q_WS_MAC
+ const QSize pixmapSize = windowFrameRect.size();
+ if (pixmapSize.width() <= 0 || pixmapSize.height() <= 0)
+ return;
+ QPainter *realPainter = painter;
+ QPixmap pm(pixmapSize);
+ painter = new QPainter(&pm);
+#endif
+
+ // Fill background
+ QStyleHintReturnMask mask;
+ bool setMask = style()->styleHint(QStyle::SH_WindowFrame_Mask, &bar, widget, &mask) && !mask.region.isEmpty();
+ bool hasBorder = !style()->styleHint(QStyle::SH_TitleBar_NoBorder, &bar, widget);
+ int frameWidth = style()->pixelMetric(QStyle::PM_MDIFrameWidth, &bar, widget);
+ if (setMask) {
+ painter->save();
+ painter->setClipRegion(mask.region, Qt::IntersectClip);
+ }
+ if (fillBackground) {
+ if (embeddedWidgetFillsOwnBackground) {
+ // Don't fill the background twice.
+ QPainterPath windowFrameBackground;
+ windowFrameBackground.addRect(windowFrameRect);
+ // Adjust with 0.5 to avoid border artifacts between
+ // widget background and frame background.
+ windowFrameBackground.addRect(rect().translated(-styleOrigin).adjusted(0.5, 0.5, -0.5, -0.5));
+ painter->fillPath(windowFrameBackground, palette().window());
+ } else {
+ painter->fillRect(windowFrameRect, palette().window());
+ }
+ }
+ painter->setRenderHint(QPainter::NonCosmeticDefaultPen);
+
+ // Draw title
+ int height = (int)d->titleBarHeight(bar);
+ bar.rect.setHeight(height);
+ if (hasBorder) // Frame is painted by PE_FrameWindow
+ bar.rect.adjust(frameWidth, frameWidth, -frameWidth, 0);
+
+ painter->save();
+ painter->setFont(QApplication::font("QWorkspaceTitleBar"));
+ style()->drawComplexControl(QStyle::CC_TitleBar, &bar, painter, widget);
+ painter->restore();
+ if (setMask)
+ painter->restore();
+ // Draw window frame
+ QStyleOptionFrame frameOptions;
+ frameOptions.QStyleOption::operator=(*option);
+ initStyleOption(&frameOptions);
+ if (!hasBorder)
+ painter->setClipRect(windowFrameRect.adjusted(0, +height, 0, 0), Qt::IntersectClip);
+ if (hasFocus()) {
+ frameOptions.state |= QStyle::State_HasFocus;
+ } else {
+ frameOptions.state &= ~QStyle::State_HasFocus;
+ }
+ bool isActive = isActiveWindow();
+ if (isActive) {
+ frameOptions.state |= QStyle::State_Active;
+ } else {
+ frameOptions.state &= ~QStyle::State_Active;
+ }
+
+ frameOptions.palette.setCurrentColorGroup(isActive ? QPalette::Active : QPalette::Normal);
+ frameOptions.rect = windowFrameRect;
+ frameOptions.lineWidth = style()->pixelMetric(QStyle::PM_MdiSubWindowFrameWidth, 0, widget);
+ frameOptions.midLineWidth = 1;
+ style()->drawPrimitive(QStyle::PE_FrameWindow, &frameOptions, painter, widget);
+
+#ifdef Q_WS_MAC
+ realPainter->drawPixmap(QPoint(), pm);
+ delete painter;
+#endif
+}
+
+/*!
+ \reimp
+*/
+QRectF QGraphicsWidget::boundingRect() const
+{
+ return windowFrameRect();
+}
+
+/*!
+ \reimp
+*/
+QPainterPath QGraphicsWidget::shape() const
+{
+ QPainterPath path;
+ path.addRect(rect());
+ return path;
+}
+
+/*!
+ Call this function to close the widget.
+
+ Returns true if the widget was closed; otherwise returns false.
+ This slot will first send a QCloseEvent to the widget, which may or may
+ not accept the event. If the event was ignored, nothing happens. If the
+ event was accepted, it will hide() the widget.
+
+ If the widget has the Qt::WA_DeleteOnClose attribute set it will be
+ deleted.
+*/
+bool QGraphicsWidget::close()
+{
+ QCloseEvent closeEvent;
+ QApplication::sendEvent(this, &closeEvent);
+ if (!closeEvent.isAccepted()) {
+ return false;
+ }
+ // hide
+ if (isVisible()) {
+ hide();
+ }
+ if (testAttribute(Qt::WA_DeleteOnClose)) {
+ deleteLater();
+ }
+ return true;
+}
+
+#ifdef Q_NO_USING_KEYWORD
+/*!
+ \fn const QObjectList &QGraphicsWidget::children() const
+
+ This function returns the same value as QObject::children(). It's
+ provided to differentiate between the obsolete member
+ QGraphicsItem::children() and QObject::children(). QGraphicsItem now
+ provides childItems() instead.
+*/
+#endif
+
+#if 0
+void QGraphicsWidget::dumpFocusChain()
+{
+ qDebug() << "=========== Dumping focus chain ==============";
+ int i = 0;
+ QGraphicsWidget *next = this;
+ QSet<QGraphicsWidget*> visited;
+ do {
+ if (!next) {
+ qWarning("Found a focus chain that is not circular, (next == 0)");
+ break;
+ }
+ qDebug() << i++ << QString::number(uint(next), 16) << next->className() << next->data(0) << QString::fromAscii("focusItem:%1").arg(next->hasFocus() ? "1" : "0") << QLatin1String("next:") << next->d_func()->focusNext->data(0) << QLatin1String("prev:") << next->d_func()->focusPrev->data(0);
+ if (visited.contains(next)) {
+ qWarning("Already visited this node. However, I expected to dump until I found myself.");
+ break;
+ }
+ visited << next;
+ next = next->d_func()->focusNext;
+ } while (next != this);
+}
+#endif
+
+QT_END_NAMESPACE
+
+#endif //QT_NO_GRAPHICSVIEW
diff --git a/src/gui/graphicsview/qgraphicswidget.h b/src/gui/graphicsview/qgraphicswidget.h
new file mode 100644
index 0000000000..34f1c5fa83
--- /dev/null
+++ b/src/gui/graphicsview/qgraphicswidget.h
@@ -0,0 +1,249 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QGRAPHICSWIDGET_H
+#define QGRAPHICSWIDGET_H
+
+#include <QtGui/qfont.h>
+#include <QtGui/qgraphicslayoutitem.h>
+#include <QtGui/qgraphicsitem.h>
+#include <QtGui/qpalette.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+class QFont;
+class QFontMetrics;
+class QGraphicsLayout;
+class QGraphicsSceneMoveEvent;
+class QGraphicsWidgetPrivate;
+class QGraphicsSceneResizeEvent;
+class QStyle;
+class QStyleOption;
+
+#if !defined(QT_NO_GRAPHICSVIEW) || (QT_EDITION & QT_MODULE_GRAPHICSVIEW) != QT_MODULE_GRAPHICSVIEW
+
+class QGraphicsWidgetPrivate;
+
+class Q_GUI_EXPORT QGraphicsWidget : public QObject, public QGraphicsItem, public QGraphicsLayoutItem
+{
+ Q_OBJECT
+ Q_PROPERTY(QPalette palette READ palette WRITE setPalette)
+ Q_PROPERTY(QFont font READ font WRITE setFont)
+ Q_PROPERTY(Qt::LayoutDirection layoutDirection READ layoutDirection WRITE setLayoutDirection RESET unsetLayoutDirection)
+ Q_PROPERTY(QSizeF size READ size WRITE resize)
+ Q_PROPERTY(Qt::FocusPolicy focusPolicy READ focusPolicy WRITE setFocusPolicy)
+ Q_PROPERTY(bool enabled READ isEnabled WRITE setEnabled)
+ Q_PROPERTY(bool visible READ isVisible WRITE setVisible)
+ Q_PROPERTY(Qt::WindowFlags windowFlags READ windowFlags WRITE setWindowFlags)
+ Q_PROPERTY(QString windowTitle READ windowTitle WRITE setWindowTitle)
+ Q_PROPERTY(qreal opacity READ opacity WRITE setOpacity)
+ Q_PROPERTY(QPointF pos READ pos WRITE setPos)
+ Q_PROPERTY(QRectF geometry READ geometry WRITE setGeometry)
+
+public:
+ QGraphicsWidget(QGraphicsItem *parent = 0, Qt::WindowFlags wFlags = 0);
+ ~QGraphicsWidget();
+
+ QGraphicsLayout *layout() const;
+ void setLayout(QGraphicsLayout *layout);
+ void adjustSize();
+
+ Qt::LayoutDirection layoutDirection() const;
+ void setLayoutDirection(Qt::LayoutDirection direction);
+ void unsetLayoutDirection();
+
+ QStyle *style() const;
+ void setStyle(QStyle *style);
+
+ QFont font() const;
+ void setFont(const QFont &font);
+
+ QPalette palette() const;
+ void setPalette(const QPalette &palette);
+
+ void resize(const QSizeF &size);
+ inline void resize(qreal w, qreal h) { resize(QSizeF(w, h)); }
+ QSizeF size() const;
+
+ void setGeometry(const QRectF &rect);
+ inline void setGeometry(qreal x, qreal y, qreal w, qreal h);
+ inline QRectF rect() const { return QRectF(QPointF(), size()); }
+
+ void setContentsMargins(qreal left, qreal top, qreal right, qreal bottom);
+ void getContentsMargins(qreal *left, qreal *top, qreal *right, qreal *bottom) const;
+
+ void setWindowFrameMargins(qreal left, qreal top, qreal right, qreal bottom);
+ void getWindowFrameMargins(qreal *left, qreal *top, qreal *right, qreal *bottom) const;
+ void unsetWindowFrameMargins();
+ QRectF windowFrameGeometry() const;
+ QRectF windowFrameRect() const;
+
+ // Window handling
+ Qt::WindowFlags windowFlags() const;
+ Qt::WindowType windowType() const;
+ void setWindowFlags(Qt::WindowFlags wFlags);
+ bool isActiveWindow() const;
+ void setWindowTitle(const QString &title);
+ QString windowTitle() const;
+
+ // Focus handling
+ Qt::FocusPolicy focusPolicy() const;
+ void setFocusPolicy(Qt::FocusPolicy policy);
+ static void setTabOrder(QGraphicsWidget *first, QGraphicsWidget *second);
+ QGraphicsWidget *focusWidget() const;
+
+#ifndef QT_NO_SHORTCUT
+ int grabShortcut(const QKeySequence &sequence, Qt::ShortcutContext context = Qt::WindowShortcut);
+ void releaseShortcut(int id);
+ void setShortcutEnabled(int id, bool enabled = true);
+ void setShortcutAutoRepeat(int id, bool enabled = true);
+#endif
+
+#ifndef QT_NO_ACTION
+ //actions
+ void addAction(QAction *action);
+ void addActions(QList<QAction*> actions);
+ void insertAction(QAction *before, QAction *action);
+ void insertActions(QAction *before, QList<QAction*> actions);
+ void removeAction(QAction *action);
+ QList<QAction*> actions() const;
+#endif
+
+ void setAttribute(Qt::WidgetAttribute attribute, bool on = true);
+ bool testAttribute(Qt::WidgetAttribute attribute) const;
+
+ enum {
+ Type = 11
+ };
+ int type() const;
+
+ void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = 0);
+ virtual void paintWindowFrame(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = 0);
+ QRectF boundingRect() const;
+ QPainterPath shape() const;
+
+#if 0
+ void dumpFocusChain();
+#endif
+
+ // ### Qt 5: Disambiguate
+#ifdef Q_NO_USING_KEYWORD
+ const QObjectList &children() const { return QObject::children(); }
+#else
+ using QObject::children;
+#endif
+
+public Q_SLOTS:
+ bool close();
+
+protected:
+ virtual void initStyleOption(QStyleOption *option) const;
+
+ QSizeF sizeHint(Qt::SizeHint which, const QSizeF &constraint = QSizeF()) const;
+ void updateGeometry();
+
+ // Notification
+ QVariant itemChange(GraphicsItemChange change, const QVariant &value);
+ virtual QVariant propertyChange(const QString &propertyName, const QVariant &value);
+
+ // Scene events
+ bool sceneEvent(QEvent *event);
+ virtual bool windowFrameEvent(QEvent *e);
+ virtual Qt::WindowFrameSection windowFrameSectionAt(const QPointF& pos) const;
+
+ // Base event handlers
+ bool event(QEvent *event);
+ //virtual void actionEvent(QActionEvent *event);
+ virtual void changeEvent(QEvent *event);
+ virtual void closeEvent(QCloseEvent *event);
+ //void create(WId window = 0, bool initializeWindow = true, bool destroyOldWindow = true);
+ //void destroy(bool destroyWindow = true, bool destroySubWindows = true);
+ void focusInEvent(QFocusEvent *event);
+ virtual bool focusNextPrevChild(bool next);
+ void focusOutEvent(QFocusEvent *event);
+ virtual void hideEvent(QHideEvent *event);
+ //virtual bool macEvent(EventHandlerCallRef caller, EventRef event);
+ //virtual int metric(PaintDeviceMetric m ) const;
+ virtual void moveEvent(QGraphicsSceneMoveEvent *event);
+ virtual void polishEvent();
+ //virtual bool qwsEvent(QWSEvent *event);
+ //void resetInputContext ();
+ virtual void resizeEvent(QGraphicsSceneResizeEvent *event);
+ virtual void showEvent(QShowEvent *event);
+ //virtual void tabletEvent(QTabletEvent *event);
+ //virtual bool winEvent(MSG *message, long *result);
+ //virtual bool x11Event(XEvent *event);
+ virtual void hoverMoveEvent(QGraphicsSceneHoverEvent *event);
+ virtual void hoverLeaveEvent(QGraphicsSceneHoverEvent *event);
+ virtual void grabMouseEvent(QEvent *event);
+ virtual void ungrabMouseEvent(QEvent *event);
+ virtual void grabKeyboardEvent(QEvent *event);
+ virtual void ungrabKeyboardEvent(QEvent *event);
+ QGraphicsWidget(QGraphicsWidgetPrivate &, QGraphicsItem *parent, QGraphicsScene *, Qt::WindowFlags wFlags = 0);
+
+private:
+ Q_DISABLE_COPY(QGraphicsWidget)
+ Q_DECLARE_PRIVATE_D(QGraphicsItem::d_ptr, QGraphicsWidget)
+ friend class QGraphicsScene;
+ friend class QGraphicsScenePrivate;
+ friend class QGraphicsView;
+ friend class QGraphicsItem;
+ friend class QGraphicsItemPrivate;
+ friend class QGraphicsLayout;
+ friend class QWidget;
+ friend class QApplication;
+};
+
+inline void QGraphicsWidget::setGeometry(qreal ax, qreal ay, qreal aw, qreal ah)
+{ setGeometry(QRectF(ax, ay, aw, ah)); }
+
+#endif
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif
+
diff --git a/src/gui/graphicsview/qgraphicswidget_p.cpp b/src/gui/graphicsview/qgraphicswidget_p.cpp
new file mode 100644
index 0000000000..641409d5ed
--- /dev/null
+++ b/src/gui/graphicsview/qgraphicswidget_p.cpp
@@ -0,0 +1,740 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qglobal.h"
+
+#ifndef QT_NO_GRAPHICSVIEW
+
+#include <QtCore/qdebug.h>
+#include "qgraphicswidget_p.h"
+#include "qgraphicslayout.h"
+#include "qgraphicsscene_p.h"
+#include <QtGui/qapplication.h>
+#include <QtGui/qgraphicsscene.h>
+#include <QtGui/qstyleoption.h>
+#include <QtGui/QStyleOptionTitleBar>
+#include <QtGui/QGraphicsSceneMouseEvent>
+#if defined(Q_WS_MAC) && !defined(QT_NO_STYLE_MAC)
+# include <QMacStyle>
+#endif
+
+QT_BEGIN_NAMESPACE
+
+void QGraphicsWidgetPrivate::init(QGraphicsItem *parentItem, Qt::WindowFlags wFlags)
+{
+ Q_Q(QGraphicsWidget);
+
+ attributes = 0;
+ isWidget = 1; // QGraphicsItem::isWidget() returns true.
+ focusNext = focusPrev = q;
+ focusPolicy = Qt::NoFocus;
+ q->setParentItem(parentItem);
+ q->setSizePolicy(QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred, QSizePolicy::DefaultType));
+ q->setGraphicsItem(q);
+
+ resolveLayoutDirection();
+
+ if (!parentItem)
+ adjustWindowFlags(&wFlags);
+ windowFlags = wFlags;
+ q->unsetWindowFrameMargins();
+}
+qreal QGraphicsWidgetPrivate::titleBarHeight(const QStyleOptionTitleBar &options) const
+{
+ Q_Q(const QGraphicsWidget);
+ int height = q->style()->pixelMetric(QStyle::PM_TitleBarHeight, &options);
+#if defined(Q_WS_MAC) && !defined(QT_NO_STYLE_MAC)
+ if (qobject_cast<QMacStyle*>(q->style())) {
+ height -=4;
+ }
+#endif
+ return (qreal)height;
+}
+
+void QGraphicsWidgetPrivate::getLayoutItemMargins(qreal *left, qreal *top, qreal *right, qreal *bottom) const
+{
+ if (left)
+ *left = leftLayoutItemMargin;
+ if (top)
+ *top = topLayoutItemMargin;
+ if (right)
+ *right = rightLayoutItemMargin;
+ if (bottom)
+ *bottom = bottomLayoutItemMargin;
+}
+
+void QGraphicsWidgetPrivate::setLayoutItemMargins(qreal left, qreal top, qreal right, qreal bottom)
+{
+ if (leftLayoutItemMargin == left
+ && topLayoutItemMargin == top
+ && rightLayoutItemMargin == right
+ && bottomLayoutItemMargin == bottom)
+ return;
+
+ Q_Q(QGraphicsWidget);
+ leftLayoutItemMargin = left;
+ topLayoutItemMargin = top;
+ rightLayoutItemMargin = right;
+ bottomLayoutItemMargin = bottom;
+ q->updateGeometry();
+}
+
+void QGraphicsWidgetPrivate::setLayoutItemMargins(QStyle::SubElement element, const QStyleOption *opt)
+{
+ Q_Q(QGraphicsWidget);
+ QStyleOption myOpt;
+ if (!opt) {
+ q->initStyleOption(&myOpt);
+ myOpt.rect.setRect(0, 0, 32768, 32768); // arbitrary
+ opt = &myOpt;
+ }
+
+ QRect liRect = q->style()->subElementRect(element, opt, /* q */ 0);
+ if (liRect.isValid()) {
+ leftLayoutItemMargin = (opt->rect.left() - liRect.left());
+ topLayoutItemMargin = (opt->rect.top() - liRect.top());
+ rightLayoutItemMargin = (liRect.right() - opt->rect.right());
+ bottomLayoutItemMargin = (liRect.bottom() - opt->rect.bottom());
+ } else {
+ leftLayoutItemMargin = 0;
+ topLayoutItemMargin = 0;
+ rightLayoutItemMargin = 0;
+ bottomLayoutItemMargin = 0;
+ }
+}
+
+void QGraphicsWidgetPrivate::setPalette_helper(const QPalette &palette)
+{
+ if (this->palette == palette && this->palette.resolve() == palette.resolve())
+ return;
+ updatePalette(palette);
+}
+
+void QGraphicsWidgetPrivate::resolvePalette(uint inheritedMask)
+{
+ inheritedPaletteResolveMask = inheritedMask;
+ QPalette naturalPalette = naturalWidgetPalette();
+ QPalette resolvedPalette = palette.resolve(naturalPalette);
+ updatePalette(resolvedPalette);
+}
+
+void QGraphicsWidgetPrivate::updatePalette(const QPalette &palette)
+{
+ Q_Q(QGraphicsWidget);
+ // Update local palette setting.
+ this->palette = palette;
+
+ // Calculate new mask.
+ if (q->isWindow() && !q->testAttribute(Qt::WA_WindowPropagation))
+ inheritedPaletteResolveMask = 0;
+ int mask = palette.resolve() | inheritedPaletteResolveMask;
+
+ // Propagate to children.
+ for (int i = 0; i < children.size(); ++i) {
+ QGraphicsItem *item = children.at(i);
+ if (item->isWidget()) {
+ QGraphicsWidget *w = static_cast<QGraphicsWidget *>(item);
+ if (!w->isWindow() || w->testAttribute(Qt::WA_WindowPropagation))
+ w->d_func()->resolvePalette(mask);
+ } else {
+ item->d_ptr->resolvePalette(mask);
+ }
+ }
+
+ // Notify change.
+ QEvent event(QEvent::PaletteChange);
+ QApplication::sendEvent(q, &event);
+}
+
+void QGraphicsWidgetPrivate::setLayoutDirection_helper(Qt::LayoutDirection direction)
+{
+ Q_Q(QGraphicsWidget);
+ if ((direction == Qt::RightToLeft) == (testAttribute(Qt::WA_RightToLeft)))
+ return;
+ q->setAttribute(Qt::WA_RightToLeft, (direction == Qt::RightToLeft));
+
+ // Propagate this change to all children.
+ for (int i = 0; i < children.size(); ++i) {
+ QGraphicsItem *item = children.at(i);
+ if (item->isWidget()) {
+ QGraphicsWidget *widget = static_cast<QGraphicsWidget *>(item);
+ if (widget->parentWidget() && !widget->testAttribute(Qt::WA_SetLayoutDirection))
+ widget->d_func()->setLayoutDirection_helper(direction);
+ }
+ }
+
+ // Send the notification event to this widget item.
+ QEvent e(QEvent::LayoutDirectionChange);
+ QApplication::sendEvent(q, &e);
+}
+
+void QGraphicsWidgetPrivate::resolveLayoutDirection()
+{
+ Q_Q(QGraphicsWidget);
+ if (q->testAttribute(Qt::WA_SetLayoutDirection)) {
+ return;
+ }
+ if (QGraphicsWidget *parentWidget = q->parentWidget()) {
+ setLayoutDirection_helper(parentWidget->layoutDirection());
+ } else if (scene) {
+ // ### shouldn't the scene have a layoutdirection really? how does
+ // ### QGraphicsWidget get changes from QApplication::layoutDirection?
+ setLayoutDirection_helper(QApplication::layoutDirection());
+ } else {
+ setLayoutDirection_helper(QApplication::layoutDirection());
+ }
+}
+
+QPalette QGraphicsWidgetPrivate::naturalWidgetPalette() const
+{
+ Q_Q(const QGraphicsWidget);
+ QPalette palette;
+ if (QGraphicsWidget *parent = q->parentWidget()) {
+ palette = parent->palette();
+ } else if (scene) {
+ palette = scene->palette();
+ }
+ palette.resolve(0);
+ return palette;
+}
+
+void QGraphicsWidgetPrivate::setFont_helper(const QFont &font)
+{
+ if (this->font == font && this->font.resolve() == font.resolve())
+ return;
+ updateFont(font);
+}
+
+void QGraphicsWidgetPrivate::resolveFont(uint inheritedMask)
+{
+ inheritedFontResolveMask = inheritedMask;
+ QFont naturalFont = naturalWidgetFont();
+ QFont resolvedFont = font.resolve(naturalFont);
+ updateFont(resolvedFont);
+}
+
+void QGraphicsWidgetPrivate::updateFont(const QFont &font)
+{
+ Q_Q(QGraphicsWidget);
+ // Update the local font setting.
+ this->font = font;
+
+ // Calculate new mask.
+ if (q->isWindow() && !q->testAttribute(Qt::WA_WindowPropagation))
+ inheritedFontResolveMask = 0;
+ int mask = font.resolve() | inheritedFontResolveMask;
+
+ // Propagate to children.
+ for (int i = 0; i < children.size(); ++i) {
+ QGraphicsItem *item = children.at(i);
+ if (item->isWidget()) {
+ QGraphicsWidget *w = static_cast<QGraphicsWidget *>(item);
+ if (!w->isWindow() || w->testAttribute(Qt::WA_WindowPropagation))
+ w->d_func()->resolveFont(mask);
+ } else {
+ item->d_ptr->resolveFont(mask);
+ }
+ }
+
+ // Notify change.
+ QEvent event(QEvent::FontChange);
+ QApplication::sendEvent(q, &event);
+}
+
+QFont QGraphicsWidgetPrivate::naturalWidgetFont() const
+{
+ Q_Q(const QGraphicsWidget);
+ QFont naturalFont; // ### no application font support
+ if (QGraphicsWidget *parent = q->parentWidget()) {
+ naturalFont = parent->font();
+ } else if (scene) {
+ naturalFont = scene->font();
+ }
+ naturalFont.resolve(0);
+ return naturalFont;
+}
+
+void QGraphicsWidgetPrivate::initStyleOptionTitleBar(QStyleOptionTitleBar *option)
+{
+ Q_Q(QGraphicsWidget);
+ q->initStyleOption(option);
+ option->rect.setHeight(titleBarHeight(*option));
+ option->titleBarFlags = windowFlags;
+ option->subControls = QStyle::SC_TitleBarCloseButton | QStyle::SC_TitleBarLabel | QStyle::SC_TitleBarSysMenu;
+ option->activeSubControls = hoveredSubControl;
+ bool isActive = q->isActiveWindow();
+ if (isActive) {
+ option->state |= QStyle::State_Active;
+ option->titleBarState = Qt::WindowActive;
+ option->titleBarState |= QStyle::State_Active;
+ } else {
+ option->state &= ~QStyle::State_Active;
+ option->titleBarState = Qt::WindowNoState;
+ }
+ QFont windowTitleFont = QApplication::font("QWorkspaceTitleBar");
+ QRect textRect = q->style()->subControlRect(QStyle::CC_TitleBar, option, QStyle::SC_TitleBarLabel, 0);
+ option->text = QFontMetrics(windowTitleFont).elidedText(windowTitle, Qt::ElideRight, textRect.width());
+}
+
+void QGraphicsWidgetPrivate::adjustWindowFlags(Qt::WindowFlags *flags)
+{
+ bool customize = (*flags & (Qt::CustomizeWindowHint
+ | Qt::FramelessWindowHint
+ | Qt::WindowTitleHint
+ | Qt::WindowSystemMenuHint
+ | Qt::WindowMinimizeButtonHint
+ | Qt::WindowMaximizeButtonHint
+ | Qt::WindowContextHelpButtonHint));
+
+ uint type = (*flags & Qt::WindowType_Mask);
+ if (customize)
+ ;
+ else if (type == Qt::Dialog || type == Qt::Sheet)
+ *flags |= Qt::WindowTitleHint | Qt::WindowSystemMenuHint | Qt::WindowContextHelpButtonHint;
+ else if (type == Qt::Tool)
+ *flags |= Qt::WindowTitleHint | Qt::WindowSystemMenuHint;
+ else if (type == Qt::Window || type == Qt::SubWindow)
+ *flags |= Qt::WindowTitleHint | Qt::WindowSystemMenuHint
+ | Qt::WindowMinimizeButtonHint | Qt::WindowMaximizeButtonHint;
+}
+
+void QGraphicsWidgetPrivate::windowFrameMouseReleaseEvent(QGraphicsSceneMouseEvent *event)
+{
+ Q_Q(QGraphicsWidget);
+ if (grabbedSection != Qt::NoSection) {
+ if (grabbedSection == Qt::TitleBarArea) {
+ buttonSunken = false;
+ QStyleOptionTitleBar bar;
+ initStyleOptionTitleBar(&bar);
+ // make sure that the coordinates (rect and pos) we send to the style are positive.
+ bar.rect = q->windowFrameRect().toRect();
+ bar.rect.moveTo(0,0);
+ bar.rect.setHeight(q->style()->pixelMetric(QStyle::PM_TitleBarHeight, &bar));
+ QPointF pos = event->pos();
+ pos.rx() += leftWindowFrameMargin;
+ pos.ry() += topWindowFrameMargin;
+ bar.subControls = QStyle::SC_TitleBarCloseButton;
+ if (q->style()->subControlRect(QStyle::CC_TitleBar, &bar,
+ QStyle::SC_TitleBarCloseButton,
+ event->widget()).contains(pos.toPoint())) {
+ q->close();
+ }
+ }
+ if (!(static_cast<QGraphicsSceneMouseEvent *>(event)->buttons()))
+ grabbedSection = Qt::NoSection;
+ event->accept();
+ }
+}
+
+void QGraphicsWidgetPrivate::windowFrameMousePressEvent(QGraphicsSceneMouseEvent *event)
+{
+ Q_Q(QGraphicsWidget);
+ if (event->button() != Qt::LeftButton)
+ return;
+
+ startGeometry = q->geometry();
+ grabbedSection = q->windowFrameSectionAt(event->pos());
+ switch (grabbedSection) {
+ case Qt::LeftSection:
+ case Qt::TopLeftSection:
+ mouseDelta = event->pos() - q->rect().topLeft();
+ break;
+ case Qt::TopSection:
+ case Qt::TopRightSection:
+ mouseDelta = event->pos() - q->rect().topRight();
+ break;
+ case Qt::RightSection:
+ case Qt::BottomRightSection:
+ mouseDelta = event->pos() - q->rect().bottomRight();
+ break;
+ case Qt::BottomSection:
+ case Qt::BottomLeftSection:
+ mouseDelta = event->pos() - q->rect().bottomLeft();
+ break;
+ case Qt::TitleBarArea:
+ if (hoveredSubControl == QStyle::SC_TitleBarCloseButton) {
+ buttonSunken = true;
+ q->update();
+ }
+ break;
+ case Qt::NoSection:
+ break;
+ }
+ event->setAccepted(grabbedSection != Qt::NoSection);
+}
+
+static void _q_boundGeometryToSizeConstraints(const QRectF &startGeometry,
+ QRectF *rect, Qt::WindowFrameSection section,
+ const QSizeF &min, const QSizeF &max)
+{
+ int height;
+ int width;
+ switch (section) {
+ case Qt::LeftSection:
+ width = qRound(qBound(min.width(), rect->width(), max.width()));
+ rect->setRect(startGeometry.right() - width, startGeometry.top(),
+ width, startGeometry.height());
+ break;
+ case Qt::TopLeftSection:
+ width = qRound(qBound(min.width(), rect->width(), max.width()));
+ height = qRound(qBound(min.height(), rect->height(), max.height()));
+ rect->setRect(startGeometry.right() - width, startGeometry.bottom() - height,
+ width, height);
+ break;
+ case Qt::TopSection:
+ height = qRound(qBound(min.height(), rect->height(), max.height()));
+ rect->setRect(startGeometry.left(), startGeometry.bottom() - height,
+ startGeometry.width(), height);
+ break;
+ case Qt::TopRightSection:
+ height = qRound(qBound(min.height(), rect->height(), max.height()));
+ rect->setTop(rect->bottom() - height);
+ rect->setWidth(qBound(min.width(), rect->width(), max.width()));
+ break;
+ case Qt::RightSection:
+ rect->setWidth(qBound(min.width(), rect->width(), max.width()));
+ break;
+ case Qt::BottomRightSection:
+ rect->setWidth(qBound(min.width(), rect->width(), max.width()));
+ rect->setHeight(qBound(min.height(), rect->height(), max.height()));
+ break;
+ case Qt::BottomSection:
+ rect->setHeight(qBound(min.height(), rect->height(), max.height()));
+ break;
+ case Qt::BottomLeftSection:
+ height = qRound(qBound(min.height(), rect->height(), max.height()));
+ width = qRound(qBound(min.width(), rect->width(), max.width()));
+ rect->setRect(startGeometry.right() - width, startGeometry.top(),
+ width, height);
+ break;
+ default:
+ break;
+ }
+}
+
+void QGraphicsWidgetPrivate::windowFrameMouseMoveEvent(QGraphicsSceneMouseEvent *event)
+{
+ Q_Q(QGraphicsWidget);
+ if (!(event->buttons() & Qt::LeftButton) || hoveredSubControl != QStyle::SC_TitleBarLabel)
+ return;
+
+ QLineF delta(q->mapFromScene(event->buttonDownScenePos(Qt::LeftButton)), event->pos());
+ QLineF parentDelta(q->mapToParent(delta.p1()), q->mapToParent(delta.p2()));
+ QLineF parentXDelta(q->mapToParent(QPointF(delta.p1().x(), 0)), q->mapToParent(QPointF(delta.p2().x(), 0)));
+ QLineF parentYDelta(q->mapToParent(QPointF(0, delta.p1().y())), q->mapToParent(QPointF(0, delta.p2().y())));
+
+ QRectF newGeometry;
+ switch (grabbedSection) {
+ case Qt::LeftSection:
+ newGeometry = QRectF(startGeometry.topLeft() + QPointF(parentXDelta.dx(), parentXDelta.dy()),
+ startGeometry.size() - QSizeF(delta.dx(), delta.dy()));
+ break;
+ case Qt::TopLeftSection:
+ newGeometry = QRectF(startGeometry.topLeft() + QPointF(parentDelta.dx(), parentDelta.dy()),
+ startGeometry.size() - QSizeF(delta.dx(), delta.dy()));
+ break;
+ case Qt::TopSection:
+ newGeometry = QRectF(startGeometry.topLeft() + QPointF(parentYDelta.dx(), parentYDelta.dy()),
+ startGeometry.size() - QSizeF(0, delta.dy()));
+ break;
+ case Qt::TopRightSection:
+ newGeometry = QRectF(startGeometry.topLeft() + QPointF(parentYDelta.dx(), parentYDelta.dy()),
+ startGeometry.size() - QSizeF(-delta.dx(), delta.dy()));
+ break;
+ case Qt::RightSection:
+ newGeometry = QRectF(startGeometry.topLeft(),
+ startGeometry.size() + QSizeF(delta.dx(), 0));
+ break;
+ case Qt::BottomRightSection:
+ newGeometry = QRectF(startGeometry.topLeft(),
+ startGeometry.size() + QSizeF(delta.dx(), delta.dy()));
+ break;
+ case Qt::BottomSection:
+ newGeometry = QRectF(startGeometry.topLeft(),
+ startGeometry.size() + QSizeF(0, delta.dy()));
+ break;
+ case Qt::BottomLeftSection:
+ newGeometry = QRectF(startGeometry.topLeft() + QPointF(parentXDelta.dx(), parentXDelta.dy()),
+ startGeometry.size() - QSizeF(delta.dx(), -delta.dy()));
+ break;
+ case Qt::TitleBarArea:
+ newGeometry = QRectF(startGeometry.topLeft() + QPointF(parentDelta.dx(), parentDelta.dy()),
+ startGeometry.size());
+ break;
+ case Qt::NoSection:
+ break;
+ }
+
+ if (grabbedSection != Qt::NoSection) {
+ _q_boundGeometryToSizeConstraints(startGeometry, &newGeometry, grabbedSection,
+ q->effectiveSizeHint(Qt::MinimumSize),
+ q->effectiveSizeHint(Qt::MaximumSize));
+ q->setGeometry(newGeometry);
+ }
+}
+
+void QGraphicsWidgetPrivate::windowFrameHoverMoveEvent(QGraphicsSceneHoverEvent *event)
+{
+ Q_Q(QGraphicsWidget);
+ if (!hasDecoration())
+ return;
+
+ if (q->rect().contains(event->pos())) {
+ if (buttonMouseOver || hoveredSubControl != QStyle::SC_None)
+ windowFrameHoverLeaveEvent(event);
+ return;
+ }
+
+ bool wasMouseOver = buttonMouseOver;
+ QRect oldButtonRect = buttonRect;
+ buttonRect = QRect();
+ buttonMouseOver = false;
+ QPointF pos = event->pos();
+ QStyleOptionTitleBar bar;
+ // make sure that the coordinates (rect and pos) we send to the style are positive.
+ pos.rx() += leftWindowFrameMargin;
+ pos.ry() += topWindowFrameMargin;
+ initStyleOptionTitleBar(&bar);
+ bar.rect = q->windowFrameRect().toRect();
+ bar.rect.moveTo(0,0);
+ bar.rect.setHeight(int(titleBarHeight(bar)));
+
+ Qt::CursorShape cursorShape = Qt::ArrowCursor;
+ bool needsSetCursorCall = true;
+ switch (q->windowFrameSectionAt(event->pos())) {
+ case Qt::TopLeftSection:
+ case Qt::BottomRightSection:
+ cursorShape = Qt::SizeFDiagCursor;
+ break;
+ case Qt::TopRightSection:
+ case Qt::BottomLeftSection:
+ cursorShape = Qt::SizeBDiagCursor;
+ break;
+ case Qt::LeftSection:
+ case Qt::RightSection:
+ cursorShape = Qt::SizeHorCursor;
+ break;
+ case Qt::TopSection:
+ case Qt::BottomSection:
+ cursorShape = Qt::SizeVerCursor;
+ break;
+ case Qt::TitleBarArea:
+ buttonRect = q->style()->subControlRect(QStyle::CC_TitleBar, &bar, QStyle::SC_TitleBarCloseButton, 0);
+#ifdef Q_WS_MAC
+ // On mac we should hover if we are in the 'area' of the buttons
+ buttonRect |= q->style()->subControlRect(QStyle::CC_TitleBar, &bar, QStyle::SC_TitleBarMinButton, 0);
+ buttonRect |= q->style()->subControlRect(QStyle::CC_TitleBar, &bar, QStyle::SC_TitleBarMaxButton, 0);
+#endif
+ if (buttonRect.contains(pos.toPoint()))
+ buttonMouseOver = true;
+ event->ignore();
+ break;
+ default:
+ needsSetCursorCall = false;
+ event->ignore();
+ }
+#ifndef QT_NO_CURSOR
+ if (needsSetCursorCall)
+ q->setCursor(cursorShape);
+#endif
+ // update buttons if we hover over them
+ hoveredSubControl = q->style()->hitTestComplexControl(QStyle::CC_TitleBar, &bar, pos.toPoint(), 0);
+ if (hoveredSubControl != QStyle::SC_TitleBarCloseButton)
+ hoveredSubControl = QStyle::SC_TitleBarLabel;
+
+ if (buttonMouseOver != wasMouseOver) {
+ if (!oldButtonRect.isNull())
+ q->update(QRectF(oldButtonRect).translated(q->windowFrameRect().topLeft()));
+ if (!buttonRect.isNull())
+ q->update(QRectF(buttonRect).translated(q->windowFrameRect().topLeft()));
+ }
+}
+
+void QGraphicsWidgetPrivate::windowFrameHoverLeaveEvent(QGraphicsSceneHoverEvent *event)
+{
+ Q_UNUSED(event);
+ Q_Q(QGraphicsWidget);
+ if (hasDecoration()) {
+ // ### restore the cursor, don't override it
+#ifndef QT_NO_CURSOR
+ q->unsetCursor();
+#endif
+
+ bool needsUpdate = false;
+ if (hoveredSubControl == QStyle::SC_TitleBarCloseButton || buttonMouseOver)
+ needsUpdate = true;
+
+ // update the hover state (of buttons etc...)
+ hoveredSubControl = QStyle::SC_None;
+ buttonMouseOver = false;
+ buttonRect = QRect();
+ if (needsUpdate)
+ q->update(buttonRect);
+ }
+}
+
+bool QGraphicsWidgetPrivate::hasDecoration() const
+{
+ return (windowFlags & Qt::Window) && (windowFlags & Qt::WindowTitleHint);
+}
+
+/*!
+ \internal
+*/
+void QGraphicsWidgetPrivate::setFocusWidget()
+{
+ // Update focus child chain.
+ QGraphicsWidget *widget = static_cast<QGraphicsWidget *>(q_ptr);
+ QGraphicsWidget *parent = widget;
+ bool hidden = !visible;
+ do {
+ parent->d_func()->focusChild = widget;
+ } while (!parent->isWindow() && (parent = parent->parentWidget()) && (!hidden || !parent->d_func()->visible));
+}
+
+/*!
+ \internal
+*/
+void QGraphicsWidgetPrivate::clearFocusWidget()
+{
+ // Reset focus child chain.
+ QGraphicsWidget *parent = static_cast<QGraphicsWidget *>(q_ptr);
+ do {
+ if (parent->d_func()->focusChild != q_ptr)
+ break;
+ parent->d_func()->focusChild = 0;
+ } while (!parent->isWindow() && (parent = parent->parentWidget()));
+}
+
+/**
+ * is called after a reparent has taken place to fix up the focus chain(s)
+ */
+void QGraphicsWidgetPrivate::fixFocusChainBeforeReparenting(QGraphicsWidget *newParent, QGraphicsScene *newScene)
+{
+ Q_Q(QGraphicsWidget);
+
+ Q_ASSERT(focusNext && focusPrev);
+
+ QGraphicsWidget *n = q; //last one in 'new' list
+ QGraphicsWidget *o = 0; //last one in 'old' list
+
+ QGraphicsWidget *w = focusNext;
+
+ QGraphicsWidget *firstOld = 0;
+ bool wasPreviousNew = true;
+
+ if (focusChild) {
+ // Ensure that the current focus child doesn't leave pointers around
+ // before reparenting.
+ focusChild->clearFocus();
+ }
+
+ while (w != q) {
+ bool isCurrentNew = q->isAncestorOf(w);
+ if (isCurrentNew) {
+ if (!wasPreviousNew) {
+ n->d_func()->focusNext = w;
+ w->d_func()->focusPrev = n;
+ }
+ n = w;
+ } else /*if (!isCurrentNew)*/ {
+ if (wasPreviousNew) {
+ if (o) {
+ o->d_func()->focusNext = w;
+ w->d_func()->focusPrev = o;
+ } else {
+ firstOld = w;
+ }
+ }
+ o = w;
+ }
+ w = w->d_func()->focusNext;
+ wasPreviousNew = isCurrentNew;
+ }
+
+ // repair the 'old' chain
+ if (firstOld) {
+ o->d_func()->focusNext = firstOld;
+ firstOld->d_func()->focusPrev = o;
+ }
+
+ // update tabFocusFirst for oldScene if the item is going to be removed from oldScene
+ if (newParent)
+ newScene = newParent->scene();
+ QGraphicsScene *oldScene = q->scene();
+ if (oldScene && newScene != oldScene)
+ oldScene->d_func()->tabFocusFirst = firstOld;
+
+ QGraphicsItem *topLevelItem = newParent ? newParent->topLevelItem() : 0;
+ QGraphicsWidget *topLevel = 0;
+ if (topLevelItem && topLevelItem->isWidget())
+ topLevel = static_cast<QGraphicsWidget *>(topLevelItem);
+
+ if (topLevel && newParent) {
+ QGraphicsWidget *last = topLevel->d_func()->focusPrev;
+ // link last with new chain
+ last->d_func()->focusNext = q;
+ focusPrev = last;
+
+ // link last in chain with
+ topLevel->d_func()->focusPrev = n;
+ n->d_func()->focusNext = topLevel;
+ } else {
+ // q is the start of the focus chain
+ n->d_func()->focusNext = q;
+ focusPrev = n;
+ }
+
+}
+
+void QGraphicsWidgetPrivate::setLayout_helper(QGraphicsLayout *l)
+{
+ delete (this->layout);
+ layout = l;
+ if (!l) {
+ Q_Q(QGraphicsWidget);
+ q->updateGeometry();
+ }
+}
+
+QT_END_NAMESPACE
+
+#endif //QT_NO_GRAPHICSVIEW
diff --git a/src/gui/graphicsview/qgraphicswidget_p.h b/src/gui/graphicsview/qgraphicswidget_p.h
new file mode 100644
index 0000000000..afa6812904
--- /dev/null
+++ b/src/gui/graphicsview/qgraphicswidget_p.h
@@ -0,0 +1,232 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QGRAPHICSWIDGET_P_H
+#define QGRAPHICSWIDGET_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of qapplication_*.cpp, qwidget*.cpp and qfiledialog.cpp. This header
+// file may change from version to version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <private/qobject_p.h>
+#include "qgraphicsitem_p.h"
+#include "qgraphicswidget.h"
+#include <QtGui/qfont.h>
+#include <QtGui/qpalette.h>
+#include <QtGui/qsizepolicy.h>
+#include <QtGui/qstyle.h>
+
+QT_BEGIN_NAMESPACE
+
+class QGraphicsLayout;
+class QStyleOptionTitleBar;
+
+#if !defined(QT_NO_GRAPHICSVIEW) || (QT_EDITION & QT_MODULE_GRAPHICSVIEW) != QT_MODULE_GRAPHICSVIEW
+
+class Q_GUI_EXPORT QGraphicsWidgetPrivate : public QGraphicsItemPrivate
+{
+ Q_DECLARE_PUBLIC(QGraphicsWidget)
+public:
+ QGraphicsWidgetPrivate()
+ : leftMargin(0),
+ topMargin(0),
+ rightMargin(0),
+ bottomMargin(0),
+ leftLayoutItemMargin(0),
+ topLayoutItemMargin(0),
+ rightLayoutItemMargin(0),
+ bottomLayoutItemMargin(0),
+ layout(0),
+ inheritedPaletteResolveMask(0),
+ inheritedFontResolveMask(0),
+ inSetGeometry(0),
+ focusPolicy(Qt::NoFocus),
+ focusNext(0),
+ focusPrev(0),
+ focusChild(0),
+ windowFlags(0),
+ hoveredSubControl(QStyle::SC_None),
+ grabbedSection(Qt::NoSection),
+ buttonMouseOver(false),
+ buttonSunken(false),
+ setWindowFrameMargins(false),
+ leftWindowFrameMargin(0),
+ topWindowFrameMargin(0),
+ rightWindowFrameMargin(0),
+ bottomWindowFrameMargin(0)
+ { }
+
+ void init(QGraphicsItem *parentItem, Qt::WindowFlags wFlags);
+ qreal titleBarHeight(const QStyleOptionTitleBar &options) const;
+
+ // Margins
+ qreal leftMargin;
+ qreal topMargin;
+ qreal rightMargin;
+ qreal bottomMargin;
+ QRectF contentsRect;
+
+ // Layout item margins
+ void getLayoutItemMargins(qreal *left, qreal *top, qreal *right, qreal *bottom) const;
+ void setLayoutItemMargins(qreal left, qreal top, qreal right, qreal bottom);
+ void setLayoutItemMargins(QStyle::SubElement element, const QStyleOption *opt = 0);
+
+ void fixFocusChainBeforeReparenting(QGraphicsWidget *newParent, QGraphicsScene *newScene = 0);
+ void setLayout_helper(QGraphicsLayout *l);
+
+ qreal leftLayoutItemMargin;
+ qreal topLayoutItemMargin;
+ qreal rightLayoutItemMargin;
+ qreal bottomLayoutItemMargin;
+
+ // Layouts
+ QGraphicsLayout *layout;
+ void setLayoutDirection_helper(Qt::LayoutDirection direction);
+ void resolveLayoutDirection();
+
+ // Style
+ QPalette palette;
+ uint inheritedPaletteResolveMask;
+ void setPalette_helper(const QPalette &palette);
+ void resolvePalette(uint inheritedMask);
+ void updatePalette(const QPalette &palette);
+ QPalette naturalWidgetPalette() const;
+ QFont font;
+ uint inheritedFontResolveMask;
+ void setFont_helper(const QFont &font);
+ void resolveFont(uint inheritedMask);
+ void updateFont(const QFont &font);
+ QFont naturalWidgetFont() const;
+
+ // Window specific
+ void initStyleOptionTitleBar(QStyleOptionTitleBar *option);
+ void adjustWindowFlags(Qt::WindowFlags *wFlags);
+ void windowFrameMouseReleaseEvent(QGraphicsSceneMouseEvent *event);
+ void windowFrameMousePressEvent(QGraphicsSceneMouseEvent *event);
+ void windowFrameMouseMoveEvent(QGraphicsSceneMouseEvent *event);
+ void windowFrameHoverMoveEvent(QGraphicsSceneHoverEvent *event);
+ void windowFrameHoverLeaveEvent(QGraphicsSceneHoverEvent *event);
+ bool hasDecoration() const;
+
+ // State
+ inline int attributeToBitIndex(Qt::WidgetAttribute att) const
+ {
+ int bit = -1;
+ switch (att) {
+ case Qt::WA_SetLayoutDirection: bit = 0; break;
+ case Qt::WA_RightToLeft: bit = 1; break;
+ case Qt::WA_SetStyle: bit = 2; break;
+ case Qt::WA_Resized: bit = 3; break;
+ case Qt::WA_DeleteOnClose: bit = 4; break;
+ case Qt::WA_NoSystemBackground: bit = 5; break;
+ case Qt::WA_OpaquePaintEvent: bit = 6; break;
+ case Qt::WA_SetPalette: bit = 7; break;
+ case Qt::WA_SetFont: bit = 8; break;
+ case Qt::WA_WindowPropagation: bit = 9; break;
+ default: break;
+ }
+ return bit;
+ }
+ inline void setAttribute(Qt::WidgetAttribute att, bool value)
+ {
+ int bit = attributeToBitIndex(att);
+ if (bit == -1) {
+ qWarning("QGraphicsWidget::setAttribute: unsupported attribute %d", int(att));
+ return;
+ }
+ if (value)
+ attributes |= (1 << bit);
+ else
+ attributes &= ~(1 << bit);
+ }
+ inline bool testAttribute(Qt::WidgetAttribute att) const
+ {
+ int bit = attributeToBitIndex(att);
+ if (bit == -1)
+ return false;
+ return (attributes & (1 << bit)) != 0;
+ }
+ quint32 attributes : 10;
+ quint32 inSetGeometry : 1;
+
+ // Focus
+ Qt::FocusPolicy focusPolicy;
+ QGraphicsWidget *focusNext;
+ QGraphicsWidget *focusPrev;
+ QGraphicsWidget *focusChild;
+ void setFocusWidget();
+ void clearFocusWidget();
+
+ // Windows
+ Qt::WindowFlags windowFlags;
+ QString windowTitle;
+ QStyle::SubControl hoveredSubControl;
+ Qt::WindowFrameSection grabbedSection;
+ uint buttonMouseOver : 1;
+ uint buttonSunken : 1;
+ QPointF mouseDelta; // to compensate for small error when interactively resizing
+ QRectF startGeometry;
+ QRect buttonRect;
+
+ bool setWindowFrameMargins;
+ qreal leftWindowFrameMargin;
+ qreal topWindowFrameMargin;
+ qreal rightWindowFrameMargin;
+ qreal bottomWindowFrameMargin;
+
+#ifndef QT_NO_ACTION
+ QList<QAction *> actions;
+#endif
+};
+
+#endif //!defined(QT_NO_GRAPHICSVIEW) || (QT_EDITION & QT_MODULE_GRAPHICSVIEW) != QT_MODULE_GRAPHICSVIEW
+
+QT_END_NAMESPACE
+
+#endif //QGRAPHICSWIDGET_P_H
+
diff --git a/src/gui/graphicsview/qgridlayoutengine.cpp b/src/gui/graphicsview/qgridlayoutengine.cpp
new file mode 100644
index 0000000000..dc5ca219a6
--- /dev/null
+++ b/src/gui/graphicsview/qgridlayoutengine.cpp
@@ -0,0 +1,1542 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qglobal.h"
+
+#ifndef QT_NO_GRAPHICSVIEW
+
+#include <math.h>
+
+#include "qgraphicslayoutitem.h"
+#include "qgridlayoutengine_p.h"
+#include "qstyleoption.h"
+#include "qvarlengtharray.h"
+
+#include <QtDebug>
+
+QT_BEGIN_NAMESPACE
+
+template <typename T>
+static void insertOrRemoveItems(QVector<T> &items, int index, int delta)
+{
+ int count = items.count();
+ if (index < count) {
+ if (delta > 0) {
+ items.insert(index, delta, T());
+ } else if (delta < 0) {
+ items.remove(index, qMin(-delta, count - index));
+ }
+ }
+}
+
+static qreal growthFactorBelowPreferredSize(qreal desired, qreal sumAvailable, qreal sumDesired)
+{
+ Q_ASSERT(sumDesired != 0.0);
+ return desired * ::pow(sumAvailable / sumDesired, desired / sumDesired);
+}
+
+static qreal fixedDescent(qreal descent, qreal ascent, qreal targetSize)
+{
+ if (descent < 0.0)
+ return -1.0;
+
+ Q_ASSERT(descent >= 0.0);
+ Q_ASSERT(ascent >= 0.0);
+ Q_ASSERT(targetSize >= ascent + descent);
+
+ qreal extra = targetSize - (ascent + descent);
+ return descent + (extra / 2.0);
+}
+
+static qreal compare(const QGridLayoutBox &box1, const QGridLayoutBox &box2, int which)
+{
+ qreal size1 = box1.q_sizes(which);
+ qreal size2 = box2.q_sizes(which);
+
+ if (which == MaximumSize) {
+ return size2 - size1;
+ } else {
+ return size1 - size2;
+ }
+}
+
+void QGridLayoutBox::add(const QGridLayoutBox &other, int stretch, qreal spacing)
+{
+ Q_ASSERT(q_minimumDescent < 0.0);
+
+ q_minimumSize += other.q_minimumSize + spacing;
+ q_preferredSize += other.q_preferredSize + spacing;
+ q_maximumSize += ((stretch == 0) ? other.q_preferredSize : other.q_maximumSize) + spacing;
+}
+
+void QGridLayoutBox::combine(const QGridLayoutBox &other)
+{
+ q_minimumDescent = qMax(q_minimumDescent, other.q_minimumDescent);
+ q_minimumAscent = qMax(q_minimumAscent, other.q_minimumAscent);
+
+ q_minimumSize = qMax(q_minimumAscent + q_minimumDescent,
+ qMax(q_minimumSize, other.q_minimumSize));
+ qreal maxMax;
+ if (q_maximumSize == FLT_MAX && other.q_maximumSize != FLT_MAX)
+ maxMax = other.q_maximumSize;
+ else if (other.q_maximumSize == FLT_MAX && q_maximumSize != FLT_MAX)
+ maxMax = q_maximumSize;
+ else
+ maxMax = qMax(q_maximumSize, other.q_maximumSize);
+
+ q_maximumSize = qMax(q_minimumSize, maxMax);
+ q_preferredSize = qBound(q_minimumSize, qMax(q_preferredSize, other.q_preferredSize),
+ q_maximumSize);
+}
+
+void QGridLayoutBox::normalize()
+{
+ q_maximumSize = qMax(qreal(0.0), q_maximumSize);
+ q_minimumSize = qBound(qreal(0.0), q_minimumSize, q_maximumSize);
+ q_preferredSize = qBound(q_minimumSize, q_preferredSize, q_maximumSize);
+ q_minimumDescent = qMin(q_minimumDescent, q_minimumSize);
+
+ Q_ASSERT((q_minimumDescent < 0.0) == (q_minimumAscent < 0.0));
+}
+
+#ifdef QT_DEBUG
+void QGridLayoutBox::dump(int indent) const
+{
+ qDebug("%*sBox (%g <= %g <= %g [%g/%g])", indent, "", q_minimumSize, q_preferredSize,
+ q_maximumSize, q_minimumAscent, q_minimumDescent);
+}
+#endif
+
+bool operator==(const QGridLayoutBox &box1, const QGridLayoutBox &box2)
+{
+ for (int i = 0; i < NSizes; ++i) {
+ if (box1.q_sizes(i) != box2.q_sizes(i))
+ return false;
+ }
+ return box1.q_minimumDescent == box2.q_minimumDescent
+ && box1.q_minimumAscent == box2.q_minimumAscent;
+}
+
+void QGridLayoutRowData::reset(int count)
+{
+ ignore.fill(false, count);
+ boxes.fill(QGridLayoutBox(), count);
+ multiCellMap.clear();
+ stretches.fill(0, count);
+ spacings.fill(0.0, count);
+ hasIgnoreFlag = false;
+}
+
+void QGridLayoutRowData::distributeMultiCells()
+{
+ MultiCellMap::const_iterator i = multiCellMap.constBegin();
+ for (; i != multiCellMap.constEnd(); ++i) {
+ int start = i.key().first;
+ int span = i.key().second;
+ int end = start + span;
+ const QGridLayoutBox &box = i.value().q_box;
+ int stretch = i.value().q_stretch;
+
+ QGridLayoutBox totalBox = this->totalBox(start, end);
+ QVarLengthArray<QGridLayoutBox> extras(span);
+ QVarLengthArray<qreal> dummy(span);
+ QVarLengthArray<qreal> newSizes(span);
+
+ for (int j = 0; j < NSizes; ++j) {
+ qreal extra = compare(totalBox, box, j);
+ if (extra > 0.0) {
+ calculateGeometries(start, end, totalBox.q_sizes(j), dummy.data(), newSizes.data(),
+ 0, totalBox);
+
+ for (int k = 0; k < span; ++k)
+ extras[k].q_sizes(j) = newSizes[k];
+ }
+ }
+
+ for (int k = 0; k < span; ++k) {
+ boxes[start + k].combine(extras[k]);
+ stretches[start + k] = qMax(stretches[start + k], stretch);
+ }
+ }
+ multiCellMap.clear();
+}
+
+void QGridLayoutRowData::calculateGeometries(int start, int end, qreal targetSize, qreal *positions,
+ qreal *sizes, qreal *descents,
+ const QGridLayoutBox &totalBox)
+{
+ Q_ASSERT(end > start);
+
+ targetSize = qBound(totalBox.q_minimumSize, targetSize, totalBox.q_maximumSize);
+
+ int n = end - start;
+ QVarLengthArray<qreal> newSizes(n);
+ QVarLengthArray<qreal> factors(n);
+ qreal sumFactors = 0.0;
+ int sumStretches = 0;
+ qreal sumAvailable;
+
+ for (int i = 0; i < n; ++i) {
+ if (stretches[start + i] > 0)
+ sumStretches += stretches[start + i];
+ }
+
+ if (targetSize < totalBox.q_preferredSize) {
+ stealBox(start, end, MinimumSize, positions, sizes);
+
+ sumAvailable = targetSize - totalBox.q_minimumSize;
+ if (sumAvailable > 0.0) {
+ qreal sumDesired = totalBox.q_preferredSize - totalBox.q_minimumSize;
+
+ for (int i = 0; i < n; ++i) {
+ if (ignore.testBit(start + i)) {
+ factors[i] = 0.0;
+ continue;
+ }
+
+ const QGridLayoutBox &box = boxes.at(start + i);
+ qreal desired = box.q_preferredSize - box.q_minimumSize;
+ factors[i] = growthFactorBelowPreferredSize(desired, sumAvailable, sumDesired);
+ sumFactors += factors[i];
+ }
+
+ for (int i = 0; i < n; ++i) {
+ Q_ASSERT(sumFactors > 0.0);
+ qreal delta = sumAvailable * factors[i] / sumFactors;
+ newSizes[i] = sizes[i] + delta;
+ }
+ }
+ } else {
+ stealBox(start, end, PreferredSize, positions, sizes);
+
+ sumAvailable = targetSize - totalBox.q_preferredSize;
+ if (sumAvailable > 0.0) {
+ bool somethingHasAMaximumSize = false;
+
+ qreal sumPreferredSizes = 0.0;
+ for (int i = 0; i < n; ++i)
+ sumPreferredSizes += sizes[i];
+
+ for (int i = 0; i < n; ++i) {
+ if (ignore.testBit(start + i)) {
+ newSizes[i] = 0.0;
+ factors[i] = 0.0;
+ continue;
+ }
+
+ const QGridLayoutBox &box = boxes.at(start + i);
+ qreal desired = box.q_maximumSize - box.q_preferredSize;
+ if (desired == 0.0) {
+ newSizes[i] = sizes[i];
+ factors[i] = 0.0;
+ } else {
+ Q_ASSERT(desired > 0.0);
+
+ int stretch = stretches[start + i];
+ if (sumStretches == 0) {
+ if (hasIgnoreFlag) {
+ factors[i] = (stretch < 0) ? 1.0 : 0.0;
+ } else {
+ factors[i] = (stretch < 0) ? sizes[i] : 0.0;
+ }
+ } else if (stretch == sumStretches) {
+ factors[i] = 1.0;
+ } else if (stretch <= 0) {
+ factors[i] = 0.0;
+ } else {
+ qreal ultimatePreferredSize;
+ qreal ultimateSumPreferredSizes;
+ qreal x = ((stretch * sumPreferredSizes)
+ - (sumStretches * box.q_preferredSize))
+ / (sumStretches - stretch);
+ if (x >= 0.0) {
+ ultimatePreferredSize = box.q_preferredSize + x;
+ ultimateSumPreferredSizes = sumPreferredSizes + x;
+ } else {
+ ultimatePreferredSize = box.q_preferredSize;
+ ultimateSumPreferredSizes = (sumStretches * box.q_preferredSize)
+ / stretch;
+ }
+
+ /*
+ We multiply these by 1.5 to give some space for a smooth transition
+ (at the expense of the stretch factors, which are not fully respected
+ during the transition).
+ */
+ ultimatePreferredSize = ultimatePreferredSize * 3 / 2;
+ ultimateSumPreferredSizes = ultimateSumPreferredSizes * 3 / 2;
+
+ qreal ultimateFactor = (stretch * ultimateSumPreferredSizes
+ / sumStretches)
+ - (box.q_preferredSize);
+ qreal transitionalFactor = sumAvailable
+ * (ultimatePreferredSize - box.q_preferredSize)
+ / (ultimateSumPreferredSizes
+ - sumPreferredSizes);
+
+ qreal alpha = qMin(sumAvailable,
+ ultimateSumPreferredSizes - sumPreferredSizes);
+ qreal beta = ultimateSumPreferredSizes - sumPreferredSizes;
+
+ factors[i] = ((alpha * ultimateFactor)
+ + ((beta - alpha) * transitionalFactor)) / beta;
+ }
+ sumFactors += factors[i];
+ if (desired < sumAvailable)
+ somethingHasAMaximumSize = true;
+
+ newSizes[i] = -1.0;
+ }
+ }
+
+ bool keepGoing = somethingHasAMaximumSize;
+ while (keepGoing) {
+ keepGoing = false;
+
+ for (int i = 0; i < n; ++i) {
+ if (newSizes[i] >= 0.0)
+ continue;
+
+ const QGridLayoutBox &box = boxes.at(start + i);
+ qreal avail = sumAvailable * factors[i] / sumFactors;
+ if (sizes[i] + avail >= box.q_maximumSize) {
+ newSizes[i] = box.q_maximumSize;
+ sumAvailable -= box.q_maximumSize - sizes[i];
+ sumFactors -= factors[i];
+ keepGoing = (sumAvailable > 0.0);
+ if (!keepGoing)
+ break;
+ }
+ }
+ }
+
+ for (int i = 0; i < n; ++i) {
+ if (newSizes[i] < 0.0) {
+ qreal delta = (sumFactors == 0.0) ? 0.0
+ : sumAvailable * factors[i] / sumFactors;
+ newSizes[i] = sizes[i] + delta;
+ }
+ }
+ }
+ }
+
+ if (sumAvailable > 0) {
+ qreal offset = 0;
+ for (int i = 0; i < n; ++i) {
+ qreal delta = newSizes[i] - sizes[i];
+ positions[i] += offset;
+ sizes[i] += delta;
+ offset += delta;
+ }
+
+#if 0 // some "pixel allocation"
+ int surplus = targetSize - (positions[n - 1] + sizes[n - 1]);
+ Q_ASSERT(surplus >= 0 && surplus <= n);
+
+ int prevSurplus = -1;
+ while (surplus > 0 && surplus != prevSurplus) {
+ prevSurplus = surplus;
+
+ int offset = 0;
+ for (int i = 0; i < n; ++i) {
+ const QGridLayoutBox &box = boxes.at(start + i);
+ int delta = (!ignore.testBit(start + i) && surplus > 0
+ && factors[i] > 0 && sizes[i] < box.q_maximumSize)
+ ? 1 : 0;
+
+ positions[i] += offset;
+ sizes[i] += delta;
+ offset += delta;
+ surplus -= delta;
+ }
+ }
+ Q_ASSERT(surplus == 0);
+#endif
+ }
+
+ if (descents) {
+ for (int i = 0; i < n; ++i) {
+ if (ignore.testBit(start + i))
+ continue;
+ const QGridLayoutBox &box = boxes.at(start + i);
+ descents[i] = fixedDescent(box.q_minimumDescent, box.q_minimumAscent, sizes[i]);
+ }
+ }
+}
+
+QGridLayoutBox QGridLayoutRowData::totalBox(int start, int end) const
+{
+ QGridLayoutBox result;
+ if (start < end) {
+ result.q_maximumSize = 0.0;
+ qreal nextSpacing = 0.0;
+ for (int i = start; i < end; ++i) {
+ result.add(boxes.at(i), stretches.at(i), nextSpacing);
+ nextSpacing = spacings.at(i);
+ }
+ }
+ return result;
+}
+
+void QGridLayoutRowData::stealBox(int start, int end, int which, qreal *positions, qreal *sizes)
+{
+ qreal offset = 0.0;
+ qreal nextSpacing = 0.0;
+
+ for (int i = start; i < end; ++i) {
+ qreal avail = 0.0;
+
+ if (!ignore.testBit(i)) {
+ const QGridLayoutBox &box = boxes.at(i);
+ avail = box.q_sizes(which);
+ offset += nextSpacing;
+ nextSpacing = spacings.at(i);
+ }
+
+ *positions++ = offset;
+ *sizes++ = avail;
+ offset += avail;
+ }
+}
+
+#ifdef QT_DEBUG
+void QGridLayoutRowData::dump(int indent) const
+{
+ qDebug("%*sData", indent, "");
+
+ for (int i = 0; i < ignore.count(); ++i) {
+ qDebug("%*s Row %d (stretch %d, spacing %g)", indent, "", i, stretches.at(i),
+ spacings.at(i));
+ if (ignore.testBit(i))
+ qDebug("%*s Ignored", indent, "");
+ boxes.at(i).dump(indent + 2);
+ }
+
+ MultiCellMap::const_iterator it = multiCellMap.constBegin();
+ while (it != multiCellMap.constEnd()) {
+ qDebug("%*s Multi-cell entry <%d, %d> (stretch %d)", indent, "", it.key().first,
+ it.key().second, it.value().q_stretch);
+ it.value().q_box.dump(indent + 2);
+ }
+}
+#endif
+
+QGridLayoutItem::QGridLayoutItem(QGridLayoutEngine *engine, QGraphicsLayoutItem *layoutItem,
+ int row, int column, int rowSpan, int columnSpan,
+ Qt::Alignment alignment)
+ : q_engine(engine), q_layoutItem(layoutItem), q_alignment(alignment)
+{
+ q_firstRows[Hor] = column;
+ q_firstRows[Ver] = row;
+ q_rowSpans[Hor] = columnSpan;
+ q_rowSpans[Ver] = rowSpan;
+ q_stretches[Hor] = -1;
+ q_stretches[Ver] = -1;
+
+ q_engine->addItem(this);
+}
+
+int QGridLayoutItem::firstRow(Qt::Orientation orientation) const
+{
+ return q_firstRows[orientation == Qt::Vertical];
+}
+
+int QGridLayoutItem::firstColumn(Qt::Orientation orientation) const
+{
+ return q_firstRows[orientation == Qt::Horizontal];
+}
+
+int QGridLayoutItem::lastRow(Qt::Orientation orientation) const
+{
+ return firstRow(orientation) + rowSpan(orientation) - 1;
+}
+
+int QGridLayoutItem::lastColumn(Qt::Orientation orientation) const
+{
+ return firstColumn(orientation) + columnSpan(orientation) - 1;
+}
+
+int QGridLayoutItem::rowSpan(Qt::Orientation orientation) const
+{
+ return q_rowSpans[orientation == Qt::Vertical];
+}
+
+int QGridLayoutItem::columnSpan(Qt::Orientation orientation) const
+{
+ return q_rowSpans[orientation == Qt::Horizontal];
+}
+
+void QGridLayoutItem::setFirstRow(int row, Qt::Orientation orientation)
+{
+ q_firstRows[orientation == Qt::Vertical] = row;
+}
+
+void QGridLayoutItem::setRowSpan(int rowSpan, Qt::Orientation orientation)
+{
+ q_rowSpans[orientation == Qt::Vertical] = rowSpan;
+}
+
+int QGridLayoutItem::stretchFactor(Qt::Orientation orientation) const
+{
+ int stretch = q_stretches[orientation == Qt::Vertical];
+ if (stretch >= 0)
+ return stretch;
+
+ QSizePolicy::Policy policy = sizePolicy(orientation);
+
+ if (policy & QSizePolicy::ExpandFlag) {
+ return 1;
+ } else if (policy & QSizePolicy::GrowFlag) {
+ return -1; // because we max it up
+ } else {
+ return 0;
+ }
+}
+
+void QGridLayoutItem::setStretchFactor(int stretch, Qt::Orientation orientation)
+{
+ Q_ASSERT(stretch >= 0); // ### deal with too big stretches
+ q_stretches[orientation == Qt::Vertical] = stretch;
+}
+
+QSizePolicy::Policy QGridLayoutItem::sizePolicy(Qt::Orientation orientation) const
+{
+ QSizePolicy sizePolicy(q_layoutItem->sizePolicy());
+ return (orientation == Qt::Horizontal) ? sizePolicy.horizontalPolicy()
+ : sizePolicy.verticalPolicy();
+}
+
+QSizePolicy::ControlTypes QGridLayoutItem::controlTypes(LayoutSide /* side */) const
+{
+ return q_layoutItem->sizePolicy().controlType();
+}
+
+QSizeF QGridLayoutItem::sizeHint(Qt::SizeHint which, const QSizeF &constraint) const
+{
+ return q_layoutItem->effectiveSizeHint(which, constraint);
+}
+
+QGridLayoutBox QGridLayoutItem::box(Qt::Orientation orientation, qreal constraint) const
+{
+ QGridLayoutBox result;
+ QSizePolicy::Policy policy = sizePolicy(orientation);
+
+ if (orientation == Qt::Horizontal) {
+ QSizeF constraintSize(-1.0, constraint);
+
+ result.q_preferredSize = sizeHint(Qt::PreferredSize, constraintSize).width();
+
+ if (policy & QSizePolicy::ShrinkFlag) {
+ result.q_minimumSize = sizeHint(Qt::MinimumSize, constraintSize).width();
+ } else {
+ result.q_minimumSize = result.q_preferredSize;
+ }
+
+ if (policy & (QSizePolicy::GrowFlag | QSizePolicy::ExpandFlag)) {
+ result.q_maximumSize = sizeHint(Qt::MaximumSize, constraintSize).width();
+ } else {
+ result.q_maximumSize = result.q_preferredSize;
+ }
+ } else {
+ QSizeF constraintSize(constraint, -1.0);
+
+ result.q_preferredSize = sizeHint(Qt::PreferredSize, constraintSize).height();
+
+ if (policy & QSizePolicy::ShrinkFlag) {
+ result.q_minimumSize = sizeHint(Qt::MinimumSize, constraintSize).height();
+ } else {
+ result.q_minimumSize = result.q_preferredSize;
+ }
+
+ if (policy & (QSizePolicy::GrowFlag | QSizePolicy::ExpandFlag)) {
+ result.q_maximumSize = sizeHint(Qt::MaximumSize, constraintSize).height();
+ } else {
+ result.q_maximumSize = result.q_preferredSize;
+ }
+
+ result.q_minimumDescent = sizeHint(Qt::MinimumDescent, constraintSize).height();
+ if (result.q_minimumDescent >= 0.0)
+ result.q_minimumAscent = result.q_minimumSize - result.q_minimumDescent;
+ }
+ if (policy & QSizePolicy::IgnoreFlag)
+ result.q_preferredSize = result.q_minimumSize;
+
+ return result;
+}
+
+QRectF QGridLayoutItem::geometryWithin(qreal x, qreal y, qreal width, qreal height,
+ qreal rowDescent) const
+{
+ rowDescent = -1.0; // ### This disables the descent
+
+ QGridLayoutBox vBox = box(Qt::Vertical);
+ if (vBox.q_minimumDescent < 0.0 || rowDescent < 0.0) {
+ qreal cellWidth = width;
+ qreal cellHeight = height;
+
+ QSizeF size = effectiveMaxSize().boundedTo(QSizeF(cellWidth, cellHeight));
+ width = size.width();
+ height = size.height();
+
+ Qt::Alignment align = q_engine->effectiveAlignment(this);
+ switch (align & Qt::AlignHorizontal_Mask) {
+ case Qt::AlignHCenter:
+ x += (cellWidth - width)/2;
+ break;
+ case Qt::AlignRight:
+ x += cellWidth - width;
+ break;
+ default:
+ break;
+ }
+ switch (align & Qt::AlignVertical_Mask) {
+ case Qt::AlignVCenter:
+ y += (cellHeight - height)/2;
+ break;
+ case Qt::AlignBottom:
+ y += cellHeight - height;
+ break;
+ default:
+ break;
+ }
+ return QRectF(x, y, width, height);
+ } else {
+ qreal descent = vBox.q_minimumDescent;
+ qreal ascent = vBox.q_minimumSize - descent;
+ return QRectF(x, y + height - rowDescent - ascent, width, ascent + descent);
+ }
+}
+
+void QGridLayoutItem::setGeometry(const QRectF &rect)
+{
+ q_layoutItem->setGeometry(rect);
+}
+
+void QGridLayoutItem::transpose()
+{
+ qSwap(q_firstRows[Hor], q_firstRows[Ver]);
+ qSwap(q_rowSpans[Hor], q_rowSpans[Ver]);
+ qSwap(q_stretches[Hor], q_stretches[Ver]);
+}
+
+void QGridLayoutItem::insertOrRemoveRows(int row, int delta, Qt::Orientation orientation)
+{
+ int oldFirstRow = firstRow(orientation);
+ if (oldFirstRow >= row) {
+ setFirstRow(oldFirstRow + delta, orientation);
+ } else if (lastRow(orientation) >= row) {
+ setRowSpan(rowSpan(orientation) + delta, orientation);
+ }
+}
+/*!
+ \internal
+ returns the effective maximumSize, will take the sizepolicy into
+ consideration. (i.e. if sizepolicy does not have QSizePolicy::Grow, then
+ maxSizeHint will be the preferredSize)
+ Note that effectiveSizeHint does not take sizePolicy into consideration,
+ (since it only evaluates the hints, as the name implies)
+*/
+QSizeF QGridLayoutItem::effectiveMaxSize() const
+{
+ QSizeF size;
+ bool vGrow = (sizePolicy(Qt::Vertical) & QSizePolicy::GrowFlag) == QSizePolicy::GrowFlag;
+ bool hGrow = (sizePolicy(Qt::Horizontal) & QSizePolicy::GrowFlag) == QSizePolicy::GrowFlag;
+ if (!vGrow || !hGrow) {
+ QSizeF pref = layoutItem()->effectiveSizeHint(Qt::PreferredSize);
+ if (!vGrow)
+ size.setHeight(pref.height());
+ if (!hGrow)
+ size.setWidth(pref.width());
+ }
+
+ if (!size.isValid()) {
+ QSizeF maxSize = layoutItem()->effectiveSizeHint(Qt::MaximumSize);
+ if (size.width() == -1)
+ size.setWidth(maxSize.width());
+ if (size.height() == -1)
+ size.setHeight(maxSize.height());
+ }
+ return size;
+}
+
+#ifdef QT_DEBUG
+void QGridLayoutItem::dump(int indent) const
+{
+ qDebug("%*s%p (%d, %d) %d x %d", indent, "", q_layoutItem, firstRow(), firstColumn(),
+ rowSpan(), columnSpan());
+
+ if (q_stretches[Hor] >= 0)
+ qDebug("%*s Horizontal stretch: %d", indent, "", q_stretches[Hor]);
+ if (q_stretches[Ver] >= 0)
+ qDebug("%*s Vertical stretch: %d", indent, "", q_stretches[Ver]);
+ if (q_alignment != 0)
+ qDebug("%*s Alignment: %x", indent, "", uint(q_alignment));
+ qDebug("%*s Horizontal size policy: %x Vertical size policy: %x",
+ indent, "", sizePolicy(Qt::Horizontal), sizePolicy(Qt::Vertical));
+}
+#endif
+
+void QGridLayoutRowInfo::insertOrRemoveRows(int row, int delta)
+{
+ count += delta;
+
+ insertOrRemoveItems(stretches, row, delta);
+ insertOrRemoveItems(spacings, row, delta);
+ insertOrRemoveItems(alignments, row, delta);
+ insertOrRemoveItems(boxes, row, delta);
+}
+
+#ifdef QT_DEBUG
+void QGridLayoutRowInfo::dump(int indent) const
+{
+ qDebug("%*sInfo (count: %d)", indent, "", count);
+ for (int i = 0; i < count; ++i) {
+ QString message;
+
+ if (stretches.value(i).value() >= 0)
+ message += QString::fromAscii(" stretch %1").arg(stretches.value(i).value());
+ if (spacings.value(i).value() >= 0.0)
+ message += QString::fromAscii(" spacing %1").arg(spacings.value(i).value());
+ if (alignments.value(i) != 0)
+ message += QString::fromAscii(" alignment %1").arg(int(alignments.value(i)), 16);
+
+ if (!message.isEmpty() || boxes.value(i) != QGridLayoutBox()) {
+ qDebug("%*s Row %d:%s", indent, "", i, qPrintable(message));
+ if (boxes.value(i) != QGridLayoutBox())
+ boxes.value(i).dump(indent + 1);
+ }
+ }
+}
+#endif
+
+QGridLayoutEngine::QGridLayoutEngine()
+{
+ m_visualDirection = Qt::LeftToRight;
+ invalidate();
+}
+
+int QGridLayoutEngine::rowCount(Qt::Orientation orientation) const
+{
+ return q_infos[orientation == Qt::Vertical].count;
+}
+
+int QGridLayoutEngine::columnCount(Qt::Orientation orientation) const
+{
+ return q_infos[orientation == Qt::Horizontal].count;
+}
+
+int QGridLayoutEngine::itemCount() const
+{
+ return q_items.count();
+}
+
+QGridLayoutItem *QGridLayoutEngine::itemAt(int index) const
+{
+ Q_ASSERT(index >= 0 && index < itemCount());
+ return q_items.at(index);
+}
+
+int QGridLayoutEngine::effectiveFirstRow(Qt::Orientation orientation) const
+{
+ ensureEffectiveFirstAndLastRows();
+ return q_cachedEffectiveFirstRows[orientation == Qt::Vertical];
+}
+
+int QGridLayoutEngine::effectiveLastRow(Qt::Orientation orientation) const
+{
+ ensureEffectiveFirstAndLastRows();
+ return q_cachedEffectiveLastRows[orientation == Qt::Vertical];
+}
+
+void QGridLayoutEngine::setSpacing(qreal spacing, Qt::Orientations orientations)
+{
+ Q_ASSERT(spacing >= 0.0);
+ if (orientations & Qt::Horizontal)
+ q_defaultSpacings[Hor].setUserValue(spacing);
+ if (orientations & Qt::Vertical)
+ q_defaultSpacings[Ver].setUserValue(spacing);
+
+ invalidate();
+}
+
+qreal QGridLayoutEngine::spacing(const QLayoutStyleInfo &styleInfo, Qt::Orientation orientation) const
+{
+ if (q_defaultSpacings[orientation == Qt::Vertical].isDefault()) {
+ QStyle *style = styleInfo.style();
+ QStyleOption option;
+ option.initFrom(styleInfo.widget());
+ qreal defaultSpacing = (qreal)style->pixelMetric(orientation == Qt::Vertical ? QStyle::PM_LayoutVerticalSpacing
+ : QStyle::PM_LayoutHorizontalSpacing, &option, styleInfo.widget());
+ q_defaultSpacings[orientation == Qt::Vertical].setCachedValue(defaultSpacing);
+ }
+ return q_defaultSpacings[orientation == Qt::Vertical].value();
+}
+
+void QGridLayoutEngine::setRowSpacing(int row, qreal spacing, Qt::Orientation orientation)
+{
+ Q_ASSERT(row >= 0);
+
+ QGridLayoutRowInfo &rowInfo = q_infos[orientation == Qt::Vertical];
+ if (row >= rowInfo.spacings.count())
+ rowInfo.spacings.resize(row + 1);
+ if (spacing >= 0)
+ rowInfo.spacings[row].setUserValue(spacing);
+ else
+ rowInfo.spacings[row] = QLayoutParameter<qreal>();
+ invalidate();
+}
+
+qreal QGridLayoutEngine::rowSpacing(int row, Qt::Orientation orientation) const
+{
+ QLayoutParameter<qreal> spacing = q_infos[orientation == Qt::Vertical].spacings.value(row);
+ if (!spacing.isDefault())
+ return spacing.value();
+ return q_defaultSpacings[orientation == Qt::Vertical].value();
+}
+
+void QGridLayoutEngine::setRowStretchFactor(int row, int stretch, Qt::Orientation orientation)
+{
+ Q_ASSERT(row >= 0);
+ Q_ASSERT(stretch >= 0);
+
+ maybeExpandGrid(row, -1, orientation);
+
+ QGridLayoutRowInfo &rowInfo = q_infos[orientation == Qt::Vertical];
+ if (row >= rowInfo.stretches.count())
+ rowInfo.stretches.resize(row + 1);
+ rowInfo.stretches[row].setUserValue(stretch);
+}
+
+int QGridLayoutEngine::rowStretchFactor(int row, Qt::Orientation orientation) const
+{
+ QStretchParameter stretch = q_infos[orientation == Qt::Vertical].stretches.value(row);
+ if (!stretch.isDefault())
+ return stretch.value();
+ return 0;
+}
+
+void QGridLayoutEngine::setStretchFactor(QGraphicsLayoutItem *layoutItem, int stretch,
+ Qt::Orientation orientation)
+{
+ Q_ASSERT(stretch >= 0);
+
+ if (QGridLayoutItem *item = findLayoutItem(layoutItem))
+ item->setStretchFactor(stretch, orientation);
+}
+
+int QGridLayoutEngine::stretchFactor(QGraphicsLayoutItem *layoutItem, Qt::Orientation orientation) const
+{
+ if (QGridLayoutItem *item = findLayoutItem(layoutItem))
+ return item->stretchFactor(orientation);
+ return 0;
+}
+
+void QGridLayoutEngine::setRowSizeHint(Qt::SizeHint which, int row, qreal size,
+ Qt::Orientation orientation)
+{
+ Q_ASSERT(row >= 0);
+ Q_ASSERT(size >= 0.0);
+
+ maybeExpandGrid(row, -1, orientation);
+
+ QGridLayoutRowInfo &rowInfo = q_infos[orientation == Qt::Vertical];
+ if (row >= rowInfo.boxes.count())
+ rowInfo.boxes.resize(row + 1);
+ rowInfo.boxes[row].q_sizes(which) = size;
+}
+
+qreal QGridLayoutEngine::rowSizeHint(Qt::SizeHint which, int row, Qt::Orientation orientation) const
+{
+ return q_infos[orientation == Qt::Vertical].boxes.value(row).q_sizes(which);
+}
+
+void QGridLayoutEngine::setRowAlignment(int row, Qt::Alignment alignment,
+ Qt::Orientation orientation)
+{
+ Q_ASSERT(row >= 0);
+
+ maybeExpandGrid(row, -1, orientation);
+
+ QGridLayoutRowInfo &rowInfo = q_infos[orientation == Qt::Vertical];
+ if (row >= rowInfo.alignments.count())
+ rowInfo.alignments.resize(row + 1);
+ rowInfo.alignments[row] = alignment;
+}
+
+Qt::Alignment QGridLayoutEngine::rowAlignment(int row, Qt::Orientation orientation) const
+{
+ Q_ASSERT(row >= 0);
+ return q_infos[orientation == Qt::Vertical].alignments.value(row);
+}
+
+void QGridLayoutEngine::setAlignment(QGraphicsLayoutItem *layoutItem, Qt::Alignment alignment)
+{
+ if (QGridLayoutItem *item = findLayoutItem(layoutItem))
+ item->setAlignment(alignment);
+ invalidate();
+}
+
+Qt::Alignment QGridLayoutEngine::alignment(QGraphicsLayoutItem *layoutItem) const
+{
+ if (QGridLayoutItem *item = findLayoutItem(layoutItem))
+ return item->alignment();
+ return 0;
+}
+
+Qt::Alignment QGridLayoutEngine::effectiveAlignment(const QGridLayoutItem *layoutItem) const
+{
+ Qt::Alignment align = layoutItem->alignment();
+ if (!(align & Qt::AlignVertical_Mask)) {
+ // no vertical alignment, respect the row alignment
+ int y = layoutItem->firstRow();
+ align |= (rowAlignment(y, Qt::Vertical) & Qt::AlignVertical_Mask);
+ }
+ if (!(align & Qt::AlignHorizontal_Mask)) {
+ // no horizontal alignment, respect the column alignment
+ int x = layoutItem->firstColumn();
+ align |= (rowAlignment(x, Qt::Horizontal) & Qt::AlignHorizontal_Mask);
+ }
+ return align;
+}
+
+void QGridLayoutEngine::addItem(QGridLayoutItem *item)
+{
+ maybeExpandGrid(item->lastRow(), item->lastColumn());
+
+ q_items.append(item);
+
+ for (int i = item->firstRow(); i <= item->lastRow(); ++i) {
+ for (int j = item->firstColumn(); j <= item->lastColumn(); ++j) {
+ if (itemAt(i, j))
+ qWarning("QGridLayoutEngine::addItem: Cell (%d, %d) already taken", i, j);
+ setItemAt(i, j, item);
+ }
+ }
+}
+
+void QGridLayoutEngine::removeItem(QGridLayoutItem *item)
+{
+ Q_ASSERT(q_items.contains(item));
+
+ invalidate();
+
+ for (int i = item->firstRow(); i <= item->lastRow(); ++i) {
+ for (int j = item->firstColumn(); j <= item->lastColumn(); ++j) {
+ if (itemAt(i, j) == item)
+ setItemAt(i, j, 0);
+ }
+ }
+
+ q_items.removeAll(item);
+}
+
+QGridLayoutItem *QGridLayoutEngine::findLayoutItem(QGraphicsLayoutItem *layoutItem) const
+{
+ for (int i = q_items.count() - 1; i >= 0; --i) {
+ QGridLayoutItem *item = q_items.at(i);
+ if (item->layoutItem() == layoutItem)
+ return item;
+ }
+ return 0;
+}
+
+QGridLayoutItem *QGridLayoutEngine::itemAt(int row, int column, Qt::Orientation orientation) const
+{
+ if (orientation == Qt::Horizontal)
+ qSwap(row, column);
+ if (uint(row) >= uint(rowCount()) || uint(column) >= uint(columnCount()))
+ return 0;
+ return q_grid.at((row * internalGridColumnCount()) + column);
+}
+
+void QGridLayoutEngine::invalidate()
+{
+ q_cachedEffectiveFirstRows[Hor] = -1;
+ q_cachedEffectiveFirstRows[Ver] = -1;
+ q_cachedEffectiveLastRows[Hor] = -1;
+ q_cachedEffectiveLastRows[Ver] = -1;
+ q_cachedDataForStyleInfo.invalidate();
+ q_cachedSize = QSizeF();
+}
+
+static void visualRect(QRectF *geom, Qt::LayoutDirection dir, const QRectF &contentsRect)
+{
+ if (dir == Qt::RightToLeft)
+ geom->moveRight(contentsRect.right() - (geom->left() - contentsRect.left()));
+}
+
+void QGridLayoutEngine::setGeometries(const QLayoutStyleInfo &styleInfo,
+ const QRectF &contentsGeometry)
+{
+ if (rowCount() < 1 || columnCount() < 1)
+ return;
+
+ ensureGeometries(styleInfo, contentsGeometry.size());
+
+ for (int i = q_items.count() - 1; i >= 0; --i) {
+ QGridLayoutItem *item = q_items.at(i);
+
+ qreal x = q_xx[item->firstColumn()];
+ qreal y = q_yy[item->firstRow()];
+ qreal width = q_widths[item->lastColumn()];
+ qreal height = q_heights[item->lastRow()];
+
+ if (item->columnSpan() != 1)
+ width += q_xx[item->lastColumn()] - x;
+ if (item->rowSpan() != 1)
+ height += q_yy[item->lastRow()] - y;
+
+ QRectF geom = item->geometryWithin(contentsGeometry.x() + x, contentsGeometry.y() + y,
+ width, height, q_descents[item->lastRow()]);
+ visualRect(&geom, visualDirection(), contentsGeometry);
+ item->setGeometry(geom);
+ }
+}
+
+// ### candidate for deletion
+QRectF QGridLayoutEngine::cellRect(const QLayoutStyleInfo &styleInfo,
+ const QRectF &contentsGeometry, int row, int column, int rowSpan,
+ int columnSpan) const
+{
+ if (uint(row) >= uint(rowCount()) || uint(column) >= uint(columnCount())
+ || rowSpan < 1 || columnSpan < 1)
+ return QRectF();
+
+ ensureGeometries(styleInfo, contentsGeometry.size());
+
+ int lastColumn = qMax(column + columnSpan, columnCount()) - 1;
+ int lastRow = qMax(row + rowSpan, rowCount()) - 1;
+
+ qreal x = q_xx[column];
+ qreal y = q_yy[row];
+ qreal width = q_widths[lastColumn];
+ qreal height = q_heights[lastRow];
+
+ if (columnSpan != 1)
+ width += q_xx[lastColumn] - x;
+ if (rowSpan != 1)
+ height += q_yy[lastRow] - y;
+
+ return QRectF(contentsGeometry.x() + x, contentsGeometry.y() + y, width, height);
+}
+
+QSizeF QGridLayoutEngine::sizeHint(const QLayoutStyleInfo &styleInfo, Qt::SizeHint which,
+ const QSizeF & /* constraint */) const
+{
+ ensureColumnAndRowData(styleInfo);
+
+ switch (which) {
+ case Qt::MinimumSize:
+ return QSizeF(q_totalBoxes[Hor].q_minimumSize, q_totalBoxes[Ver].q_minimumSize);
+ case Qt::PreferredSize:
+ return QSizeF(q_totalBoxes[Hor].q_preferredSize, q_totalBoxes[Ver].q_preferredSize);
+ case Qt::MaximumSize:
+ return QSizeF(q_totalBoxes[Hor].q_maximumSize, q_totalBoxes[Ver].q_maximumSize);
+ case Qt::MinimumDescent:
+ return QSizeF(-1.0, q_totalBoxes[Hor].q_minimumDescent); // ### doesn't work
+ default:
+ break;
+ }
+ return QSizeF();
+}
+
+QSizePolicy::ControlTypes QGridLayoutEngine::controlTypes(LayoutSide side) const
+{
+ Qt::Orientation orientation = (side == Top || side == Bottom) ? Qt::Vertical : Qt::Horizontal;
+ int row = (side == Top || side == Left) ? effectiveFirstRow(orientation)
+ : effectiveLastRow(orientation);
+ QSizePolicy::ControlTypes result = 0;
+
+ for (int column = columnCount(orientation) - 1; column >= 0; --column) {
+ if (QGridLayoutItem *item = itemAt(row, column, orientation))
+ result |= item->controlTypes(side);
+ }
+ return result;
+}
+
+void QGridLayoutEngine::transpose()
+{
+ invalidate();
+
+ for (int i = q_items.count() - 1; i >= 0; --i)
+ q_items.at(i)->transpose();
+
+ qSwap(q_defaultSpacings[Hor], q_defaultSpacings[Ver]);
+ qSwap(q_infos[Hor], q_infos[Ver]);
+
+ regenerateGrid();
+}
+
+void QGridLayoutEngine::setVisualDirection(Qt::LayoutDirection direction)
+{
+ m_visualDirection = direction;
+}
+
+Qt::LayoutDirection QGridLayoutEngine::visualDirection() const
+{
+ return m_visualDirection;
+}
+
+#ifdef QT_DEBUG
+void QGridLayoutEngine::dump(int indent) const
+{
+ qDebug("%*sEngine", indent, "");
+
+ qDebug("%*s Items (%d)", indent, "", q_items.count());
+ int i;
+ for (i = 0; i < q_items.count(); ++i)
+ q_items.at(i)->dump(indent + 2);
+
+ qDebug("%*s Grid (%d x %d)", indent, "", internalGridRowCount(),
+ internalGridColumnCount());
+ for (int row = 0; row < internalGridRowCount(); ++row) {
+ QString message = QLatin1String("[ ");
+ for (int column = 0; column < internalGridColumnCount(); ++column) {
+ message += QString::number(q_items.indexOf(itemAt(row, column))).rightJustified(3);
+ message += QLatin1String(" ");
+ }
+ message += QLatin1String("]");
+ qDebug("%*s %s", indent, "", qPrintable(message));
+ }
+
+ if (q_defaultSpacings[Hor].value() >= 0.0 || q_defaultSpacings[Ver].value() >= 0.0)
+ qDebug("%*s Default spacings: %g %g", indent, "", q_defaultSpacings[Hor].value(),
+ q_defaultSpacings[Ver].value());
+
+ qDebug("%*s Column and row info", indent, "");
+ q_infos[Hor].dump(indent + 2);
+ q_infos[Ver].dump(indent + 2);
+
+ qDebug("%*s Column and row data", indent, "");
+ q_columnData.dump(indent + 2);
+ q_rowData.dump(indent + 2);
+
+ qDebug("%*s Geometries output", indent, "");
+ for (int pass = 0; pass < 2; ++pass) {
+ QVector<qreal> &cellPos = q_yy;
+ QString message;
+ for (i = 0; i < cellPos.count(); ++i) {
+ message += QLatin1String((message.isEmpty() ? "[" : ", "));
+ message += QString::number(cellPos.at(i));
+ }
+ message += QLatin1String("]");
+ qDebug("%*s %s %s", indent, "", (pass == 0 ? "rows:" : "columns:"), qPrintable(message));
+ cellPos = q_xx;
+ }
+}
+#endif
+
+void QGridLayoutEngine::maybeExpandGrid(int row, int column, Qt::Orientation orientation)
+{
+ invalidate(); // ### move out of here?
+
+ if (orientation == Qt::Horizontal)
+ qSwap(row, column);
+
+ if (row < rowCount() && column < columnCount())
+ return;
+
+ int oldGridRowCount = internalGridRowCount();
+ int oldGridColumnCount = internalGridColumnCount();
+
+ q_infos[Ver].count = qMax(row + 1, rowCount());
+ q_infos[Hor].count = qMax(column + 1, columnCount());
+
+ int newGridRowCount = internalGridRowCount();
+ int newGridColumnCount = internalGridColumnCount();
+
+ int newGridSize = newGridRowCount * newGridColumnCount;
+ if (newGridSize != q_grid.count()) {
+ q_grid.resize(newGridSize);
+
+ if (newGridColumnCount != oldGridColumnCount) {
+ for (int i = oldGridRowCount - 1; i >= 1; --i) {
+ for (int j = oldGridColumnCount - 1; j >= 0; --j) {
+ int oldIndex = (i * oldGridColumnCount) + j;
+ int newIndex = (i * newGridColumnCount) + j;
+
+ Q_ASSERT(newIndex > oldIndex);
+ q_grid[newIndex] = q_grid[oldIndex];
+ q_grid[oldIndex] = 0;
+ }
+ }
+ }
+ }
+}
+
+void QGridLayoutEngine::regenerateGrid()
+{
+ q_grid.fill(0);
+
+ for (int i = q_items.count() - 1; i >= 0; --i) {
+ QGridLayoutItem *item = q_items.at(i);
+
+ for (int j = item->firstRow(); j <= item->lastRow(); ++j) {
+ for (int k = item->firstColumn(); k <= item->lastColumn(); ++k) {
+ setItemAt(j, k, item);
+ }
+ }
+ }
+}
+
+void QGridLayoutEngine::setItemAt(int row, int column, QGridLayoutItem *item)
+{
+ Q_ASSERT(row >= 0 && row < rowCount());
+ Q_ASSERT(column >= 0 && column < columnCount());
+ q_grid[(row * internalGridColumnCount()) + column] = item;
+}
+
+void QGridLayoutEngine::insertOrRemoveRows(int row, int delta, Qt::Orientation orientation)
+{
+ int oldRowCount = rowCount(orientation);
+ Q_ASSERT(uint(row) <= uint(oldRowCount));
+
+ invalidate();
+
+ // appending rows (or columns) is easy
+ if (row == oldRowCount && delta > 0) {
+ maybeExpandGrid(oldRowCount + delta - 1, -1, orientation);
+ return;
+ }
+
+ q_infos[orientation == Qt::Vertical].insertOrRemoveRows(row, delta);
+
+ for (int i = q_items.count() - 1; i >= 0; --i)
+ q_items.at(i)->insertOrRemoveRows(row, delta, orientation);
+
+ q_grid.resize(internalGridRowCount() * internalGridColumnCount());
+ regenerateGrid();
+}
+
+void QGridLayoutEngine::fillRowData(QGridLayoutRowData *rowData, const QLayoutStyleInfo &styleInfo,
+ Qt::Orientation orientation) const
+{
+ const int ButtonMask = QSizePolicy::ButtonBox | QSizePolicy::PushButton;
+ const QGridLayoutRowInfo &rowInfo = q_infos[orientation == Qt::Vertical];
+ const QGridLayoutRowInfo &columnInfo = q_infos[orientation == Qt::Horizontal];
+ LayoutSide top = (orientation == Qt::Vertical) ? Top : Left;
+ LayoutSide bottom = (orientation == Qt::Vertical) ? Bottom : Right;
+
+ QStyle *style = styleInfo.style();
+ QStyleOption option;
+ option.initFrom(styleInfo.widget());
+
+ const QLayoutParameter<qreal> &defaultSpacing = q_defaultSpacings[orientation == Qt::Vertical];
+ qreal innerSpacing = 0.0;
+ if (style)
+ innerSpacing = (qreal)style->pixelMetric(orientation == Qt::Vertical ? QStyle::PM_LayoutVerticalSpacing
+ : QStyle::PM_LayoutHorizontalSpacing,
+ &option, styleInfo.widget());
+ if (innerSpacing >= 0.0)
+ defaultSpacing.setCachedValue(innerSpacing);
+
+ for (int row = 0; row < rowInfo.count; ++row) {
+ bool rowIsEmpty = true;
+ bool rowIsIdenticalToPrevious = (row > 0);
+
+ for (int column = 0; column < columnInfo.count; ++column) {
+ QGridLayoutItem *item = itemAt(row, column, orientation);
+
+ if (rowIsIdenticalToPrevious && item != itemAt(row - 1, column, orientation))
+ rowIsIdenticalToPrevious = false;
+
+ if (item)
+ rowIsEmpty = false;
+ }
+
+ if ((rowIsEmpty || rowIsIdenticalToPrevious)
+ && rowInfo.spacings.value(row).isDefault()
+ && rowInfo.stretches.value(row).isDefault()
+ && rowInfo.boxes.value(row) == QGridLayoutBox())
+ rowData->ignore.setBit(row, true);
+
+ if (rowInfo.spacings.value(row).isUser()) {
+ rowData->spacings[row] = rowInfo.spacings.at(row).value();
+ } else if (!defaultSpacing.isDefault()) {
+ rowData->spacings[row] = defaultSpacing.value();
+ }
+
+ rowData->stretches[row] = rowInfo.stretches.value(row).value();
+ }
+
+ struct RowAdHocData {
+ int q_row;
+ unsigned int q_hasButtons : 8;
+ unsigned int q_hasNonButtons : 8;
+
+ inline RowAdHocData() : q_row(-1), q_hasButtons(false), q_hasNonButtons(false) {}
+ inline void init(int row) {
+ this->q_row = row;
+ q_hasButtons = false;
+ q_hasNonButtons = false;
+ }
+ inline bool hasOnlyButtons() const { return q_hasButtons && !q_hasNonButtons; }
+ inline bool hasOnlyNonButtons() const { return q_hasNonButtons && !q_hasButtons; }
+ };
+ RowAdHocData lastRowAdHocData;
+ RowAdHocData nextToLastRowAdHocData;
+ RowAdHocData nextToNextToLastRowAdHocData;
+
+ rowData->hasIgnoreFlag = false;
+ for (int row = 0; row < rowInfo.count; ++row) {
+ if (rowData->ignore.testBit(row))
+ continue;
+
+ QGridLayoutBox &rowBox = rowData->boxes[row];
+ if (option.state & QStyle::State_Window) {
+ nextToNextToLastRowAdHocData = nextToLastRowAdHocData;
+ nextToLastRowAdHocData = lastRowAdHocData;
+ lastRowAdHocData.init(row);
+ }
+
+ bool userRowStretch = rowInfo.stretches.value(row).isUser();
+ int &rowStretch = rowData->stretches[row];
+
+ bool hasIgnoreFlag = true;
+ for (int column = 0; column < columnInfo.count; ++column) {
+ QGridLayoutItem *item = itemAt(row, column, orientation);
+ if (item) {
+ int itemRow = item->firstRow(orientation);
+ int itemColumn = item->firstColumn(orientation);
+
+ if (itemRow == row && itemColumn == column) {
+ int itemStretch = item->stretchFactor(orientation);
+ if (!(item->sizePolicy(orientation) & QSizePolicy::IgnoreFlag))
+ hasIgnoreFlag = false;
+ int itemRowSpan = item->rowSpan(orientation);
+
+ int effectiveRowSpan = 1;
+ for (int i = 1; i < itemRowSpan; ++i) {
+ if (!rowData->ignore.testBit(i))
+ ++effectiveRowSpan;
+ }
+
+ QGridLayoutBox *box;
+ if (effectiveRowSpan == 1) {
+ box = &rowBox;
+ if (!userRowStretch)
+ rowStretch = qMax(rowStretch, itemStretch);
+ } else {
+ QGridLayoutMultiCellData &multiCell =
+ rowData->multiCellMap[qMakePair(row, effectiveRowSpan)];
+ box = &multiCell.q_box;
+ multiCell.q_stretch = itemStretch;
+ }
+ box->combine(item->box(orientation));
+
+ if (effectiveRowSpan == 1) {
+ QSizePolicy::ControlTypes controls = item->controlTypes(top);
+ if (controls & ButtonMask)
+ lastRowAdHocData.q_hasButtons = true;
+ if (controls & ~ButtonMask)
+ lastRowAdHocData.q_hasNonButtons = true;
+ }
+ }
+ }
+ }
+ if (row < rowInfo.boxes.count()) {
+ QGridLayoutBox rowBoxInfo = rowInfo.boxes.at(row);
+ rowBoxInfo.normalize();
+ rowBox.q_minimumSize = qMax(rowBox.q_minimumSize, rowBoxInfo.q_minimumSize);
+ rowBox.q_maximumSize = qMax(rowBox.q_minimumSize,
+ (rowBoxInfo.q_maximumSize != FLT_MAX ?
+ rowBoxInfo.q_maximumSize : rowBox.q_maximumSize));
+ rowBox.q_preferredSize = qBound(rowBox.q_minimumSize,
+ qMax(rowBox.q_preferredSize, rowBoxInfo.q_preferredSize),
+ rowBox.q_maximumSize);
+ }
+ if (hasIgnoreFlag)
+ rowData->hasIgnoreFlag = true;
+ }
+
+ /*
+ Heuristic: Detect button boxes that don't use QSizePolicy::ButtonBox.
+ This is somewhat ad hoc but it usually does the trick.
+ */
+ bool lastRowIsButtonBox = (lastRowAdHocData.hasOnlyButtons()
+ && nextToLastRowAdHocData.hasOnlyNonButtons());
+ bool lastTwoRowsIsButtonBox = (lastRowAdHocData.hasOnlyButtons()
+ && nextToLastRowAdHocData.hasOnlyButtons()
+ && nextToNextToLastRowAdHocData.hasOnlyNonButtons()
+ && orientation == Qt::Vertical);
+
+ if (defaultSpacing.isDefault()) {
+ int prevRow = -1;
+ for (int row = 0; row < rowInfo.count; ++row) {
+ if (rowData->ignore.testBit(row))
+ continue;
+
+ if (prevRow != -1 && !rowInfo.spacings.value(prevRow).isUser()) {
+ qreal &rowSpacing = rowData->spacings[prevRow];
+ for (int column = 0; column < columnInfo.count; ++column) {
+ QGridLayoutItem *item1 = itemAt(prevRow, column, orientation);
+ QGridLayoutItem *item2 = itemAt(row, column, orientation);
+
+ if (item1 && item2 && item1 != item2) {
+ QSizePolicy::ControlTypes controls1 = item1->controlTypes(bottom);
+ QSizePolicy::ControlTypes controls2 = item2->controlTypes(top);
+
+ if (controls2 & QSizePolicy::PushButton) {
+ if ((row == nextToLastRowAdHocData.q_row && lastTwoRowsIsButtonBox)
+ || (row == lastRowAdHocData.q_row && lastRowIsButtonBox)) {
+ controls2 &= ~QSizePolicy::PushButton;
+ controls2 |= QSizePolicy::ButtonBox;
+ }
+ }
+
+ qreal spacing = style->combinedLayoutSpacing(controls1, controls2,
+ orientation, &option,
+ styleInfo.widget());
+ if (orientation == Qt::Horizontal) {
+ qreal width1 = rowData->boxes.at(prevRow).q_minimumSize;
+ qreal width2 = rowData->boxes.at(row).q_minimumSize;
+ QRectF rect1 = item1->geometryWithin(0.0, 0.0, width1, FLT_MAX, -1.0);
+ QRectF rect2 = item2->geometryWithin(0.0, 0.0, width2, FLT_MAX, -1.0);
+ spacing -= (width1 - (rect1.x() + rect1.width())) + rect2.x();
+ } else {
+ const QGridLayoutBox &box1 = rowData->boxes.at(prevRow);
+ const QGridLayoutBox &box2 = rowData->boxes.at(row);
+ qreal height1 = box1.q_minimumSize;
+ qreal height2 = box2.q_minimumSize;
+ qreal rowDescent1 = fixedDescent(box1.q_minimumDescent,
+ box1.q_minimumAscent, height1);
+ qreal rowDescent2 = fixedDescent(box2.q_minimumDescent,
+ box2.q_minimumAscent, height2);
+ QRectF rect1 = item1->geometryWithin(0.0, 0.0, FLT_MAX, height1,
+ rowDescent1);
+ QRectF rect2 = item2->geometryWithin(0.0, 0.0, FLT_MAX, height2,
+ rowDescent2);
+ spacing -= (height1 - (rect1.y() + rect1.height())) + rect2.y();
+ }
+ rowSpacing = qMax(spacing, rowSpacing);
+ }
+ }
+ }
+ prevRow = row;
+ }
+ } else if (lastRowIsButtonBox || lastTwoRowsIsButtonBox) {
+ /*
+ Even for styles that define a uniform spacing, we cheat a
+ bit and use the window margin as the spacing. This
+ significantly improves the look of dialogs.
+ */
+ int prevRow = lastRowIsButtonBox ? nextToLastRowAdHocData.q_row
+ : nextToNextToLastRowAdHocData.q_row;
+ if (!defaultSpacing.isUser() && !rowInfo.spacings.value(prevRow).isUser()) {
+ qreal windowMargin = style->pixelMetric(orientation == Qt::Vertical
+ ? QStyle::PM_LayoutBottomMargin
+ : QStyle::PM_LayoutRightMargin,
+ &option, styleInfo.widget());
+
+ qreal &rowSpacing = rowData->spacings[prevRow];
+ rowSpacing = qMax(windowMargin, rowSpacing);
+ }
+ }
+}
+
+void QGridLayoutEngine::ensureEffectiveFirstAndLastRows() const
+{
+ if (q_cachedEffectiveFirstRows[Hor] == -1 && !q_items.isEmpty()) {
+ int rowCount = this->rowCount();
+ int columnCount = this->columnCount();
+
+ q_cachedEffectiveFirstRows[Ver] = rowCount;
+ q_cachedEffectiveFirstRows[Hor] = columnCount;
+ q_cachedEffectiveLastRows[Ver] = -1;
+ q_cachedEffectiveLastRows[Hor] = -1;
+
+ for (int i = q_items.count() - 1; i >= 0; --i) {
+ const QGridLayoutItem *item = q_items.at(i);
+
+ for (int j = 0; j < NOrientations; ++j) {
+ Qt::Orientation orientation = (j == Hor) ? Qt::Horizontal : Qt::Vertical;
+ if (item->firstRow(orientation) < q_cachedEffectiveFirstRows[j])
+ q_cachedEffectiveFirstRows[j] = item->firstRow(orientation);
+ if (item->lastRow(orientation) > q_cachedEffectiveLastRows[j])
+ q_cachedEffectiveLastRows[j] = item->lastRow(orientation);
+ }
+ }
+ }
+}
+
+void QGridLayoutEngine::ensureColumnAndRowData(const QLayoutStyleInfo &styleInfo) const
+{
+ if (q_cachedDataForStyleInfo == styleInfo)
+ return;
+
+ q_columnData.reset(columnCount());
+ q_rowData.reset(rowCount());
+
+ fillRowData(&q_columnData, styleInfo, Qt::Horizontal);
+ fillRowData(&q_rowData, styleInfo, Qt::Vertical);
+
+ q_columnData.distributeMultiCells();
+ q_rowData.distributeMultiCells();
+
+ q_totalBoxes[Hor] = q_columnData.totalBox(0, columnCount());
+ q_totalBoxes[Ver] = q_rowData.totalBox(0, rowCount());
+
+ q_cachedDataForStyleInfo = styleInfo;
+}
+
+void QGridLayoutEngine::ensureGeometries(const QLayoutStyleInfo &styleInfo,
+ const QSizeF &size) const
+{
+ ensureColumnAndRowData(styleInfo);
+ if (q_cachedSize == size)
+ return;
+
+ q_xx.resize(columnCount());
+ q_yy.resize(rowCount());
+ q_widths.resize(columnCount());
+ q_heights.resize(rowCount());
+ q_descents.resize(rowCount());
+ q_columnData.calculateGeometries(0, columnCount(), size.width(), q_xx.data(), q_widths.data(),
+ 0, q_totalBoxes[Hor]);
+ q_rowData.calculateGeometries(0, rowCount(), size.height(), q_yy.data(), q_heights.data(),
+ q_descents.data(), q_totalBoxes[Ver]);
+
+ q_cachedSize = size;
+}
+
+QT_END_NAMESPACE
+
+#endif //QT_NO_GRAPHICSVIEW
diff --git a/src/gui/graphicsview/qgridlayoutengine_p.h b/src/gui/graphicsview/qgridlayoutengine_p.h
new file mode 100644
index 0000000000..acc96deda0
--- /dev/null
+++ b/src/gui/graphicsview/qgridlayoutengine_p.h
@@ -0,0 +1,449 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QGRIDLAYOUTENGINE_P_H
+#define QGRIDLAYOUTENGINE_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of the graphics view layout classes. This header
+// file may change from version to version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qalgorithms.h"
+#include "qbitarray.h"
+#include "qlist.h"
+#include "qmap.h"
+#include "qpair.h"
+#include "qvector.h"
+
+#include <float.h>
+
+QT_BEGIN_NAMESPACE
+
+class QGraphicsLayoutItem;
+class QStyle;
+class QWidget;
+
+// ### deal with Descent in a similar way
+enum {
+ MinimumSize = Qt::MinimumSize,
+ PreferredSize = Qt::PreferredSize,
+ MaximumSize = Qt::MaximumSize,
+ NSizes
+};
+
+// do not reorder
+enum {
+ Hor,
+ Ver,
+ NOrientations
+};
+
+// do not reorder
+enum LayoutSide {
+ Left,
+ Top,
+ Right,
+ Bottom
+};
+
+template <typename T>
+class QLayoutParameter
+{
+public:
+ enum State { Default, User, Cached };
+
+ inline QLayoutParameter() : q_value(T()), q_state(Default) {}
+ inline QLayoutParameter(T value, State state = Default) : q_value(value), q_state(state) {}
+
+ inline void setUserValue(T value) {
+ q_value = value;
+ q_state = User;
+ }
+ inline void setCachedValue(T value) const {
+ if (q_state != User) {
+ q_value = value;
+ q_state = Cached;
+ }
+ }
+ inline T value() const { return q_value; }
+ inline T value(T defaultValue) const { return isUser() ? q_value : defaultValue; }
+ inline bool isDefault() const { return q_state == Default; }
+ inline bool isUser() const { return q_state == User; }
+ inline bool isCached() const { return q_state == Cached; }
+
+private:
+ mutable T q_value;
+ mutable State q_state;
+};
+
+class QStretchParameter : public QLayoutParameter<int>
+{
+public:
+ QStretchParameter() : QLayoutParameter<int>(-1) {}
+
+};
+
+class QLayoutStyleInfo
+{
+public:
+ inline QLayoutStyleInfo() { invalidate(); }
+ inline QLayoutStyleInfo(QStyle *style, QWidget *widget)
+ : q_valid(true), q_style(style), q_widget(widget) {}
+
+ inline void invalidate() { q_valid = false; q_style = 0; q_widget = 0; }
+
+ inline QStyle *style() const { return q_style; }
+ inline QWidget *widget() const { return q_widget; }
+
+ inline bool operator==(const QLayoutStyleInfo &other)
+ { return q_style == other.q_style && q_widget == other.q_widget; }
+ inline bool operator!=(const QLayoutStyleInfo &other)
+ { return !(*this == other); }
+
+private:
+ bool q_valid;
+ QStyle *q_style;
+ QWidget *q_widget;
+};
+
+class QGridLayoutBox
+{
+public:
+ inline QGridLayoutBox()
+ : q_minimumSize(0), q_preferredSize(0), q_maximumSize(FLT_MAX),
+ q_minimumDescent(-1), q_minimumAscent(-1) {}
+
+ void add(const QGridLayoutBox &other, int stretch, qreal spacing);
+ void combine(const QGridLayoutBox &other);
+ void normalize();
+
+#ifdef QT_DEBUG
+ void dump(int indent = 0) const;
+#endif
+ // This code could use the union-struct-array trick, but a compiler
+ // bug prevents this from working.
+ qreal q_minimumSize;
+ qreal q_preferredSize;
+ qreal q_maximumSize;
+ qreal q_minimumDescent;
+ qreal q_minimumAscent;
+ inline qreal &q_sizes(int which)
+ {
+ qreal *t;
+ switch (which) {
+ case Qt::MinimumSize:
+ t = &q_minimumSize;
+ break;
+ case Qt::PreferredSize:
+ t = &q_preferredSize;
+ break;
+ case Qt::MaximumSize:
+ t = &q_maximumSize;
+ break;
+ case Qt::MinimumDescent:
+ t = &q_minimumDescent;
+ break;
+ case (Qt::MinimumDescent + 1):
+ t = &q_minimumAscent;
+ break;
+ default:
+ t = 0;
+ break;
+ }
+ return *t;
+ }
+ inline const qreal &q_sizes(int which) const
+ {
+ const qreal *t;
+ switch (which) {
+ case Qt::MinimumSize:
+ t = &q_minimumSize;
+ break;
+ case Qt::PreferredSize:
+ t = &q_preferredSize;
+ break;
+ case Qt::MaximumSize:
+ t = &q_maximumSize;
+ break;
+ case Qt::MinimumDescent:
+ t = &q_minimumDescent;
+ break;
+ case (Qt::MinimumDescent + 1):
+ t = &q_minimumAscent;
+ break;
+ default:
+ t = 0;
+ break;
+ }
+ return *t;
+ }
+};
+
+bool operator==(const QGridLayoutBox &box1, const QGridLayoutBox &box2);
+inline bool operator!=(const QGridLayoutBox &box1, const QGridLayoutBox &box2)
+ { return !operator==(box1, box2); }
+
+class QGridLayoutMultiCellData
+{
+public:
+ inline QGridLayoutMultiCellData() : q_stretch(-1) {}
+
+ QGridLayoutBox q_box;
+ int q_stretch;
+};
+
+typedef QMap<QPair<int, int>, QGridLayoutMultiCellData> MultiCellMap;
+
+class QGridLayoutRowData
+{
+public:
+ void reset(int count);
+ void distributeMultiCells();
+ void calculateGeometries(int start, int end, qreal targetSize, qreal *positions, qreal *sizes,
+ qreal *descents, const QGridLayoutBox &totalBox);
+ QGridLayoutBox totalBox(int start, int end) const;
+ void stealBox(int start, int end, int which, qreal *positions, qreal *sizes);
+
+#ifdef QT_DEBUG
+ void dump(int indent = 0) const;
+#endif
+
+ QBitArray ignore; // ### rename q_
+ QVector<QGridLayoutBox> boxes;
+ MultiCellMap multiCellMap;
+ QVector<int> stretches;
+ QVector<qreal> spacings;
+ bool hasIgnoreFlag;
+};
+
+class QGridLayoutEngine;
+
+class QGridLayoutItem
+{
+public:
+ QGridLayoutItem(QGridLayoutEngine *engine, QGraphicsLayoutItem *layoutItem, int row, int column,
+ int rowSpan = 1, int columnSpan = 1, Qt::Alignment alignment = 0);
+
+ inline int firstRow() const { return q_firstRows[Ver]; }
+ inline int firstColumn() const { return q_firstRows[Hor]; }
+ inline int rowSpan() const { return q_rowSpans[Ver]; }
+ inline int columnSpan() const { return q_rowSpans[Hor]; }
+ inline int lastRow() const { return firstRow() + rowSpan() - 1; }
+ inline int lastColumn() const { return firstColumn() + columnSpan() - 1; }
+
+ int firstRow(Qt::Orientation orientation) const;
+ int firstColumn(Qt::Orientation orientation) const;
+ int lastRow(Qt::Orientation orientation) const;
+ int lastColumn(Qt::Orientation orientation) const;
+ int rowSpan(Qt::Orientation orientation) const;
+ int columnSpan(Qt::Orientation orientation) const;
+ void setFirstRow(int row, Qt::Orientation orientation = Qt::Vertical);
+ void setRowSpan(int rowSpan, Qt::Orientation orientation = Qt::Vertical);
+
+ int stretchFactor(Qt::Orientation orientation) const;
+ void setStretchFactor(int stretch, Qt::Orientation orientation);
+
+ inline Qt::Alignment alignment() const { return q_alignment; }
+ inline void setAlignment(Qt::Alignment alignment) { q_alignment = alignment; }
+
+ QSizePolicy::Policy sizePolicy(Qt::Orientation orientation) const;
+ QSizePolicy::ControlTypes controlTypes(LayoutSide side) const;
+ QSizeF sizeHint(Qt::SizeHint which, const QSizeF &constraint = QSizeF()) const;
+ QGridLayoutBox box(Qt::Orientation orientation, qreal constraint = -1.0) const;
+ QRectF geometryWithin(qreal x, qreal y, qreal width, qreal height, qreal rowDescent) const;
+
+ QGraphicsLayoutItem *layoutItem() const { return q_layoutItem; }
+
+ void setGeometry(const QRectF &rect);
+ void transpose();
+ void insertOrRemoveRows(int row, int delta, Qt::Orientation orientation = Qt::Vertical);
+ QSizeF effectiveMaxSize() const;
+
+#ifdef QT_DEBUG
+ void dump(int indent = 0) const;
+#endif
+
+private:
+ QGridLayoutEngine *q_engine; // ### needed?
+ QGraphicsLayoutItem *q_layoutItem;
+ int q_firstRows[NOrientations];
+ int q_rowSpans[NOrientations];
+ int q_stretches[NOrientations];
+ Qt::Alignment q_alignment;
+};
+
+class QGridLayoutRowInfo
+{
+public:
+ inline QGridLayoutRowInfo() : count(0) {}
+
+ void insertOrRemoveRows(int row, int delta);
+
+#ifdef QT_DEBUG
+ void dump(int indent = 0) const;
+#endif
+
+ int count;
+ QVector<QStretchParameter> stretches;
+ QVector<QLayoutParameter<qreal> > spacings;
+ QVector<Qt::Alignment> alignments;
+ QVector<QGridLayoutBox> boxes;
+};
+
+class QGridLayoutEngine
+{
+public:
+ QGridLayoutEngine();
+ inline ~QGridLayoutEngine() { qDeleteAll(q_items); }
+
+ int rowCount(Qt::Orientation orientation) const;
+ int columnCount(Qt::Orientation orientation) const;
+ inline int rowCount() const { return q_infos[Ver].count; }
+ inline int columnCount() const { return q_infos[Hor].count; }
+ // returns the number of items inserted, which may be less than (rowCount * columnCount)
+ int itemCount() const;
+ QGridLayoutItem *itemAt(int index) const;
+
+ int effectiveFirstRow(Qt::Orientation orientation = Qt::Vertical) const;
+ int effectiveLastRow(Qt::Orientation orientation = Qt::Vertical) const;
+
+ void setSpacing(qreal spacing, Qt::Orientations orientations);
+ qreal spacing(const QLayoutStyleInfo &styleInfo, Qt::Orientation orientation) const;
+ // ### setSpacingAfterRow(), spacingAfterRow()
+ void setRowSpacing(int row, qreal spacing, Qt::Orientation orientation = Qt::Vertical);
+ qreal rowSpacing(int row, Qt::Orientation orientation = Qt::Vertical) const;
+
+ void setRowStretchFactor(int row, int stretch, Qt::Orientation orientation = Qt::Vertical);
+ int rowStretchFactor(int row, Qt::Orientation orientation = Qt::Vertical) const;
+
+ void setStretchFactor(QGraphicsLayoutItem *layoutItem, int stretch,
+ Qt::Orientation orientation);
+ int stretchFactor(QGraphicsLayoutItem *layoutItem, Qt::Orientation orientation) const;
+
+ void setRowSizeHint(Qt::SizeHint which, int row, qreal size,
+ Qt::Orientation orientation = Qt::Vertical);
+ qreal rowSizeHint(Qt::SizeHint which, int row,
+ Qt::Orientation orientation = Qt::Vertical) const;
+
+ void setRowAlignment(int row, Qt::Alignment alignment, Qt::Orientation orientation);
+ Qt::Alignment rowAlignment(int row, Qt::Orientation orientation) const;
+
+ void setAlignment(QGraphicsLayoutItem *layoutItem, Qt::Alignment alignment);
+ Qt::Alignment alignment(QGraphicsLayoutItem *layoutItem) const;
+ Qt::Alignment effectiveAlignment(const QGridLayoutItem *layoutItem) const;
+
+
+ void addItem(QGridLayoutItem *item);
+ void removeItem(QGridLayoutItem *item);
+ QGridLayoutItem *findLayoutItem(QGraphicsLayoutItem *layoutItem) const;
+ QGridLayoutItem *itemAt(int row, int column, Qt::Orientation orientation = Qt::Vertical) const;
+ inline void insertRow(int row, Qt::Orientation orientation = Qt::Vertical)
+ { insertOrRemoveRows(row, +1, orientation); }
+ inline void removeRow(int row, Qt::Orientation orientation = Qt::Vertical)
+ { insertOrRemoveRows(row, -1, orientation); }
+
+ void invalidate();
+ void setGeometries(const QLayoutStyleInfo &styleInfo, const QRectF &contentsGeometry);
+ QRectF cellRect(const QLayoutStyleInfo &styleInfo, const QRectF &contentsGeometry, int row,
+ int column, int rowSpan, int columnSpan) const;
+ QSizeF sizeHint(const QLayoutStyleInfo &styleInfo, Qt::SizeHint which,
+ const QSizeF &constraint) const;
+ QSizePolicy::ControlTypes controlTypes(LayoutSide side) const;
+ void transpose();
+ void setVisualDirection(Qt::LayoutDirection direction);
+ Qt::LayoutDirection visualDirection() const;
+#ifdef QT_DEBUG
+ void dump(int indent = 0) const;
+#endif
+
+private:
+ static int grossRoundUp(int n) { return ((n + 2) | 0x3) - 2; }
+
+ void maybeExpandGrid(int row, int column, Qt::Orientation orientation = Qt::Vertical);
+ void regenerateGrid();
+ inline int internalGridRowCount() const { return grossRoundUp(rowCount()); }
+ inline int internalGridColumnCount() const { return grossRoundUp(columnCount()); }
+ void setItemAt(int row, int column, QGridLayoutItem *item);
+ void insertOrRemoveRows(int row, int delta, Qt::Orientation orientation = Qt::Vertical);
+ void fillRowData(QGridLayoutRowData *rowData, const QLayoutStyleInfo &styleInfo,
+ Qt::Orientation orientation = Qt::Vertical) const;
+ void ensureEffectiveFirstAndLastRows() const;
+ void ensureColumnAndRowData(const QLayoutStyleInfo &styleInfo) const;
+ void ensureGeometries(const QLayoutStyleInfo &styleInfo, const QSizeF &size) const;
+
+ // User input
+ QVector<QGridLayoutItem *> q_grid;
+ QList<QGridLayoutItem *> q_items;
+ QLayoutParameter<qreal> q_defaultSpacings[NOrientations];
+ QGridLayoutRowInfo q_infos[NOrientations];
+ Qt::LayoutDirection m_visualDirection;
+
+ // Lazily computed from the above user input
+ mutable int q_cachedEffectiveFirstRows[NOrientations];
+ mutable int q_cachedEffectiveLastRows[NOrientations];
+
+ // Layout item input
+ mutable QLayoutStyleInfo q_cachedDataForStyleInfo;
+ mutable QGridLayoutRowData q_columnData;
+ mutable QGridLayoutRowData q_rowData;
+ mutable QGridLayoutBox q_totalBoxes[NOrientations];
+
+ // Output
+ mutable QSizeF q_cachedSize;
+ mutable QVector<qreal> q_xx;
+ mutable QVector<qreal> q_yy;
+ mutable QVector<qreal> q_widths;
+ mutable QVector<qreal> q_heights;
+ mutable QVector<qreal> q_descents;
+
+ friend class QGridLayoutItem;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/gui/gui.pro b/src/gui/gui.pro
new file mode 100644
index 0000000000..f224e67d05
--- /dev/null
+++ b/src/gui/gui.pro
@@ -0,0 +1,44 @@
+TARGET = QtGui
+QPRO_PWD = $$PWD
+QT = core
+DEFINES += QT_BUILD_GUI_LIB QT_NO_USING_NAMESPACE
+win32-msvc*|win32-icc:QMAKE_LFLAGS += /BASE:0x65000000
+
+!win32:!embedded:!mac:CONFIG += x11
+
+unix:QMAKE_PKGCONFIG_REQUIRES = QtCore
+
+include(../qbase.pri)
+
+contains(QT_CONFIG, x11sm):CONFIG += x11sm
+
+#platforms
+x11:include(kernel/x11.pri)
+mac:include(kernel/mac.pri)
+win32:include(kernel/win.pri)
+embedded:include(embedded/embedded.pri)
+
+#modules
+include(kernel/kernel.pri)
+include(image/image.pri)
+include(painting/painting.pri)
+include(text/text.pri)
+include(styles/styles.pri)
+include(widgets/widgets.pri)
+include(dialogs/dialogs.pri)
+include(accessible/accessible.pri)
+include(itemviews/itemviews.pri)
+include(inputmethod/inputmethod.pri)
+include(graphicsview/graphicsview.pri)
+include(util/util.pri)
+
+embedded: QT += network
+
+QMAKE_LIBS += $$QMAKE_LIBS_GUI
+
+contains(DEFINES,QT_EVAL):include($$QT_SOURCE_TREE/src/corelib/eval.pri)
+
+QMAKE_DYNAMIC_LIST_FILE = $$PWD/QtGui.dynlist
+
+DEFINES += Q_INTERNAL_QAPP_SRC
+
diff --git a/src/gui/image/image.pri b/src/gui/image/image.pri
new file mode 100644
index 0000000000..ca529743f5
--- /dev/null
+++ b/src/gui/image/image.pri
@@ -0,0 +1,108 @@
+# -*-mode:sh-*-
+# Qt image handling
+
+# Qt kernel module
+
+HEADERS += \
+ image/qbitmap.h \
+ image/qicon.h \
+ image/qiconengine.h \
+ image/qiconengineplugin.h \
+ image/qimage.h \
+ image/qimage_p.h \
+ image/qimageiohandler.h \
+ image/qimagereader.h \
+ image/qimagewriter.h \
+ image/qmovie.h \
+ image/qnativeimage_p.h \
+ image/qpaintengine_pic_p.h \
+ image/qpicture.h \
+ image/qpicture_p.h \
+ image/qpictureformatplugin.h \
+ image/qpixmap.h \
+ image/qpixmap_raster_p.h \
+ image/qpixmapcache.h \
+ image/qpixmapdata_p.h \
+ image/qpixmapdatafactory_p.h \
+ image/qpixmapfilter_p.h
+
+SOURCES += \
+ image/qbitmap.cpp \
+ image/qicon.cpp \
+ image/qimage.cpp \
+ image/qimageiohandler.cpp \
+ image/qimagereader.cpp \
+ image/qimagewriter.cpp \
+ image/qpaintengine_pic.cpp \
+ image/qpicture.cpp \
+ image/qpictureformatplugin.cpp \
+ image/qpixmap.cpp \
+ image/qpixmapcache.cpp \
+ image/qpixmapdata.cpp \
+ image/qpixmapdatafactory.cpp \
+ image/qpixmapfilter.cpp \
+ image/qiconengine.cpp \
+ image/qiconengineplugin.cpp \
+ image/qmovie.cpp \
+ image/qpixmap_raster.cpp \
+ image/qnativeimage.cpp \
+
+win32 {
+ SOURCES += image/qpixmap_win.cpp
+}
+embedded {
+ SOURCES += image/qpixmap_qws.cpp
+}
+x11 {
+ HEADERS += image/qpixmap_x11_p.h
+ SOURCES += image/qpixmap_x11.cpp
+}
+mac {
+ HEADERS += image/qpixmap_mac_p.h
+ SOURCES += image/qpixmap_mac.cpp
+}
+
+# Built-in image format support
+HEADERS += \
+ image/qbmphandler_p.h \
+ image/qppmhandler_p.h \
+ image/qxbmhandler_p.h \
+ image/qxpmhandler_p.h
+
+SOURCES += \
+ image/qbmphandler.cpp \
+ image/qppmhandler.cpp \
+ image/qxbmhandler.cpp \
+ image/qxpmhandler.cpp
+
+# 3rd party / system PNG support
+!contains(QT_CONFIG, no-png) {
+ HEADERS += image/qpnghandler_p.h
+ SOURCES += image/qpnghandler.cpp
+
+ contains(QT_CONFIG, system-png) {
+ unix:LIBS += -lpng
+ win32:LIBS += libpng.lib
+ } else {
+ !isEqual(QT_ARCH, i386):!isEqual(QT_ARCH, x86_64):DEFINES += PNG_NO_ASSEMBLER_CODE
+ INCLUDEPATH += ../3rdparty/libpng ../3rdparty/zlib
+ SOURCES += ../3rdparty/libpng/png.c \
+ ../3rdparty/libpng/pngerror.c \
+ ../3rdparty/libpng/pngget.c \
+ ../3rdparty/libpng/pngmem.c \
+ ../3rdparty/libpng/pngpread.c \
+ ../3rdparty/libpng/pngread.c \
+ ../3rdparty/libpng/pngrio.c \
+ ../3rdparty/libpng/pngrtran.c \
+ ../3rdparty/libpng/pngrutil.c \
+ ../3rdparty/libpng/pngset.c \
+ ../3rdparty/libpng/pngtrans.c \
+ ../3rdparty/libpng/pngwio.c \
+ ../3rdparty/libpng/pngwrite.c \
+ ../3rdparty/libpng/pngwtran.c \
+ ../3rdparty/libpng/pngwutil.c \
+ ../3rdparty/libpng/pnggccrd.c
+ }
+} else {
+ DEFINES *= QT_NO_IMAGEFORMAT_PNG
+}
diff --git a/src/gui/image/qbitmap.cpp b/src/gui/image/qbitmap.cpp
new file mode 100644
index 0000000000..3805b6e6fe
--- /dev/null
+++ b/src/gui/image/qbitmap.cpp
@@ -0,0 +1,403 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qbitmap.h"
+#include "qpixmapdata_p.h"
+#include "qimage.h"
+#include "qvariant.h"
+#include <qpainter.h>
+#include <private/qgraphicssystem_p.h>
+#include <private/qapplication_p.h>
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QBitmap
+ \brief The QBitmap class provides monochrome (1-bit depth) pixmaps.
+
+ \ingroup multimedia
+ \ingroup shared
+
+ The QBitmap class is a monochrome off-screen paint device used
+ mainly for creating custom QCursor and QBrush objects,
+ constructing QRegion objects, and for setting masks for pixmaps
+ and widgets.
+
+ QBitmap is a QPixmap subclass ensuring a depth of 1, except for
+ null objects which have a depth of 0. If a pixmap with a depth
+ greater than 1 is assigned to a bitmap, the bitmap will be
+ dithered automatically.
+
+ Use the QColor objects Qt::color0 and Qt::color1 when drawing on a
+ QBitmap object (or a QPixmap object with depth 1).
+
+ Painting with Qt::color0 sets the bitmap bits to 0, and painting
+ with Qt::color1 sets the bits to 1. For a bitmap, 0-bits indicate
+ background (or transparent pixels) and 1-bits indicate foreground
+ (or opaque pixels). Use the clear() function to set all the bits
+ to Qt::color0. Note that using the Qt::black and Qt::white colors
+ make no sense because the QColor::pixel() value is not necessarily
+ 0 for black and 1 for white.
+
+ The QBitmap class provides the transformed() function returning a
+ transformed copy of the bitmap; use the QMatrix argument to
+ translate, scale, shear, and rotate the bitmap. In addition,
+ QBitmap provides the static fromData() function which returns a
+ bitmap constructed from the given \c uchar data, and the static
+ fromImage() function returning a converted copy of a QImage
+ object.
+
+ Just like the QPixmap class, QBitmap is optimized by the use of
+ implicit data sharing. For more information, see the {Implicit
+ Data Sharing} documentation.
+
+ \sa QPixmap, QImage, QImageReader, QImageWriter
+*/
+
+
+/*!
+ Constructs a null bitmap.
+
+ \sa QPixmap::isNull()
+*/
+
+QBitmap::QBitmap()
+ : QPixmap(QSize(0, 0), QPixmapData::BitmapType)
+{
+}
+
+/*!
+ \fn QBitmap::QBitmap(int width, int height)
+
+ Constructs a bitmap with the given \a width and \a height. The pixels
+ inside are uninitialized.
+
+ \sa clear()
+*/
+
+QBitmap::QBitmap(int w, int h)
+ : QPixmap(QSize(w, h), QPixmapData::BitmapType)
+{
+}
+
+/*!
+ Constructs a bitmap with the given \a size. The pixels in the
+ bitmap are uninitialized.
+
+ \sa clear()
+*/
+
+QBitmap::QBitmap(const QSize &size)
+ : QPixmap(size, QPixmapData::BitmapType)
+{
+}
+
+/*!
+ \fn QBitmap::clear()
+
+ Clears the bitmap, setting all its bits to Qt::color0.
+*/
+
+/*!
+ Constructs a bitmap that is a copy of the given \a pixmap.
+
+ If the pixmap has a depth greater than 1, the resulting bitmap
+ will be dithered automatically.
+
+ \sa QPixmap::depth(), fromImage(), fromData()
+*/
+
+QBitmap::QBitmap(const QPixmap &pixmap)
+{
+ QBitmap::operator=(pixmap);
+}
+
+/*!
+ \fn QBitmap::QBitmap(const QImage &image)
+
+ Constructs a bitmap that is a copy of the given \a image.
+
+ Use the static fromImage() function instead.
+*/
+
+/*!
+ Constructs a bitmap from the file specified by the given \a
+ fileName. If the file does not exist, or has an unknown format,
+ the bitmap becomes a null bitmap.
+
+ The \a fileName and \a format parameters are passed on to the
+ QPixmap::load() function. If the file format uses more than 1 bit
+ per pixel, the resulting bitmap will be dithered automatically.
+
+ \sa QPixmap::isNull(), QImageReader::imageFormat()
+*/
+
+QBitmap::QBitmap(const QString& fileName, const char *format)
+ : QPixmap(QSize(0, 0), QPixmapData::BitmapType)
+{
+ load(fileName, format, Qt::MonoOnly);
+}
+
+/*!
+ \overload
+
+ Assigns the given \a pixmap to this bitmap and returns a reference
+ to this bitmap.
+
+ If the pixmap has a depth greater than 1, the resulting bitmap
+ will be dithered automatically.
+
+ \sa QPixmap::depth()
+ */
+
+QBitmap &QBitmap::operator=(const QPixmap &pixmap)
+{
+ if (pixmap.isNull()) { // a null pixmap
+ QBitmap bm(0, 0);
+ QBitmap::operator=(bm);
+ } else if (pixmap.depth() == 1) { // 1-bit pixmap
+ QPixmap::operator=(pixmap); // shallow assignment
+ } else { // n-bit depth pixmap
+ QImage image;
+ image = pixmap.toImage(); // convert pixmap to image
+ *this = fromImage(image); // will dither image
+ }
+ return *this;
+}
+
+
+#ifdef QT3_SUPPORT
+QBitmap::QBitmap(int w, int h, const uchar *bits, bool isXbitmap)
+{
+ *this = fromData(QSize(w, h), bits, isXbitmap ? QImage::Format_MonoLSB : QImage::Format_Mono);
+}
+
+
+QBitmap::QBitmap(const QSize &size, const uchar *bits, bool isXbitmap)
+{
+ *this = fromData(size, bits, isXbitmap ? QImage::Format_MonoLSB : QImage::Format_Mono);
+}
+#endif
+
+/*!
+ Destroys the bitmap.
+*/
+QBitmap::~QBitmap()
+{
+}
+
+/*!
+ Returns the bitmap as a QVariant.
+*/
+QBitmap::operator QVariant() const
+{
+ return QVariant(QVariant::Bitmap, this);
+}
+
+/*!
+ \fn QBitmap &QBitmap::operator=(const QImage &image)
+ \overload
+
+ Converts the given \a image to a bitmap, and assigns the result to
+ this bitmap. Returns a reference to the bitmap.
+
+ Use the static fromImage() function instead.
+*/
+
+/*!
+ Returns a copy of the given \a image converted to a bitmap using
+ the specified image conversion \a flags.
+
+ \sa fromData()
+*/
+QBitmap QBitmap::fromImage(const QImage &image, Qt::ImageConversionFlags flags)
+{
+ if (image.isNull())
+ return QBitmap();
+
+ QImage img = image.convertToFormat(QImage::Format_MonoLSB, flags);
+
+ // make sure image.color(0) == Qt::color0 (white)
+ // and image.color(1) == Qt::color1 (black)
+ const QRgb c0 = QColor(Qt::black).rgb();
+ const QRgb c1 = QColor(Qt::white).rgb();
+ if (img.color(0) == c0 && img.color(1) == c1) {
+ img.invertPixels();
+ img.setColor(0, c1);
+ img.setColor(1, c0);
+ }
+
+ QPixmapData *d;
+ QGraphicsSystem* gs = QApplicationPrivate::graphicsSystem();
+ if (gs)
+ d = gs->createPixmapData(QPixmapData::BitmapType);
+ else
+ d = QGraphicsSystem::createDefaultPixmapData(QPixmapData::BitmapType);
+
+ d->fromImage(img, flags | Qt::MonoOnly);
+ return QPixmap(d);
+}
+
+/*!
+ Constructs a bitmap with the given \a size, and sets the contents to
+ the \a bits supplied.
+
+ The bitmap data has to be byte aligned and provided in in the bit
+ order specified by \a monoFormat. The mono format must be either
+ QImage::Format_Mono or QImage::Format_MonoLSB. Use
+ QImage::Format_Mono to specify data on the XBM format.
+
+ \sa fromImage()
+
+*/
+QBitmap QBitmap::fromData(const QSize &size, const uchar *bits, QImage::Format monoFormat)
+{
+ Q_ASSERT(monoFormat == QImage::Format_Mono || monoFormat == QImage::Format_MonoLSB);
+
+ QImage image(size, monoFormat);
+ image.setColor(0, QColor(Qt::color0).rgb());
+ image.setColor(1, QColor(Qt::color1).rgb());
+
+ // Need to memcpy each line separatly since QImage is 32bit aligned and
+ // this data is only byte aligned...
+ int bytesPerLine = (size.width() + 7) / 8;
+ for (int y = 0; y < size.height(); ++y)
+ memcpy(image.scanLine(y), bits + bytesPerLine * y, bytesPerLine);
+ return QBitmap::fromImage(image);
+}
+
+/*!
+ Returns a copy of this bitmap, transformed according to the given
+ \a matrix.
+
+ \sa QPixmap::transformed()
+ */
+QBitmap QBitmap::transformed(const QTransform &matrix) const
+{
+ QBitmap bm = QPixmap::transformed(matrix);
+ return bm;
+}
+
+/*!
+ \overload
+
+ This convenience function converts the \a matrix to a QTransform
+ and calls the overloaded function.
+*/
+QBitmap QBitmap::transformed(const QMatrix &matrix) const
+{
+ return transformed(QTransform(matrix));
+}
+
+#ifdef QT3_SUPPORT
+/*!
+ \fn QBitmap QBitmap::xForm(const QMatrix &matrix) const
+
+ Returns a copy of this bitmap, transformed according to the given
+ \a matrix.
+
+ Use transformed() instead.
+*/
+
+/*!
+ \fn QBitmap::QBitmap(const QSize &size, bool clear)
+
+ Constructs a bitmap with the given \a size. If \a clear is true,
+ the bits are initialized to Qt::color0.
+
+ Use the corresponding QBitmap() constructor instead, and then call
+ the clear() function if the \a clear parameter is true.
+*/
+
+/*!
+ \fn QBitmap::QBitmap(int width, int height, bool clear)
+
+ Constructs a bitmap with the given \a width and \a height. If \a
+ clear is true, the bits are initialized to Qt::color0.
+
+ Use the corresponding QBitmap() constructor instead, and then call
+ the clear() function if the \a clear parameter is true.
+*/
+
+/*!
+ \fn QBitmap::QBitmap(int width, int height, const uchar *bits, bool isXbitmap)
+
+ Constructs a bitmap with the given \a width and \a height, and
+ sets the contents to the \a bits supplied. The \a isXbitmap flag
+ should be true if \a bits was generated by the X11 bitmap
+ program.
+
+ Use the static fromData() function instead. If \a isXbitmap is
+ true, use the default bit order(QImage_FormatMonoLSB) otherwise
+ use QImage::Format_Mono.
+
+ \omit
+ The X bitmap bit order is little endian. The QImage
+ documentation discusses bit order of monochrome images. Opposed to
+ QImage, the data has to be byte aligned.
+
+ Example (creates an arrow bitmap):
+ \snippet doc/src/snippets/code/src_gui_image_qbitmap.cpp 0
+ \endomit
+*/
+
+
+/*!
+ \fn QBitmap::QBitmap(const QSize &size, const uchar *bits, bool isXbitmap)
+
+ \overload
+
+ Constructs a bitmap with the given \a size, and sets the contents
+ to the \a bits supplied. The \a isXbitmap flag should be true if
+ \a bits was generated by the X11 bitmap program.
+
+ \omit
+ The X bitmap bit order is little endian. The QImage documentation
+ discusses bit order of monochrome images.
+ \endomit
+
+ Use the static fromData() function instead. If \a isXbitmap is
+ true, use the default bit order(QImage_FormatMonoLSB) otherwise
+ use QImage::Format_Mono.
+*/
+#endif
+
+QT_END_NAMESPACE
diff --git a/src/gui/image/qbitmap.h b/src/gui/image/qbitmap.h
new file mode 100644
index 0000000000..b17e4ac479
--- /dev/null
+++ b/src/gui/image/qbitmap.h
@@ -0,0 +1,106 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QBITMAP_H
+#define QBITMAP_H
+
+#include <QtGui/qpixmap.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+class QVariant;
+
+class Q_GUI_EXPORT QBitmap : public QPixmap
+{
+public:
+ QBitmap();
+ QBitmap(const QPixmap &);
+ QBitmap(int w, int h);
+ explicit QBitmap(const QSize &);
+ explicit QBitmap(const QString &fileName, const char *format=0);
+ ~QBitmap();
+
+ QBitmap &operator=(const QPixmap &);
+ operator QVariant() const;
+
+ inline void clear() { fill(Qt::color0); }
+
+ static QBitmap fromImage(const QImage &image, Qt::ImageConversionFlags flags = Qt::AutoColor);
+ static QBitmap fromData(const QSize &size, const uchar *bits,
+ QImage::Format monoFormat = QImage::Format_MonoLSB);
+
+ QBitmap transformed(const QMatrix &) const;
+ QBitmap transformed(const QTransform &matrix) const;
+#ifdef QT3_SUPPORT
+ inline QT3_SUPPORT_CONSTRUCTOR QBitmap(int w, int h, bool clear);
+ inline QT3_SUPPORT_CONSTRUCTOR QBitmap(const QSize &, bool clear);
+ QT3_SUPPORT_CONSTRUCTOR QBitmap(int w, int h, const uchar *bits, bool isXbitmap=false);
+ QT3_SUPPORT_CONSTRUCTOR QBitmap(const QSize &, const uchar *bits, bool isXbitmap=false);
+ inline QT3_SUPPORT QBitmap xForm(const QMatrix &matrix) const { return transformed(QTransform(matrix)); }
+ QT3_SUPPORT_CONSTRUCTOR QBitmap(const QImage &image) { *this = fromImage(image); }
+ QT3_SUPPORT QBitmap &operator=(const QImage &image) { *this = fromImage(image); return *this; }
+#endif
+};
+Q_DECLARE_SHARED(QBitmap)
+
+#ifdef QT3_SUPPORT
+inline QBitmap::QBitmap(int w, int h, bool clear)
+ : QPixmap(QSize(w, h), 1)
+{
+ if (clear) this->clear();
+}
+
+inline QBitmap::QBitmap(const QSize &size, bool clear)
+ : QPixmap(size, 1)
+{
+ if (clear) this->clear();
+}
+#endif
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QBITMAP_H
diff --git a/src/gui/image/qbmphandler.cpp b/src/gui/image/qbmphandler.cpp
new file mode 100644
index 0000000000..6734e02b4b
--- /dev/null
+++ b/src/gui/image/qbmphandler.cpp
@@ -0,0 +1,833 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "private/qbmphandler_p.h"
+
+#ifndef QT_NO_IMAGEFORMAT_BMP
+
+#include <qimage.h>
+#include <qvariant.h>
+#include <qvector.h>
+
+QT_BEGIN_NAMESPACE
+
+static void swapPixel01(QImage *image) // 1-bpp: swap 0 and 1 pixels
+{
+ int i;
+ if (image->depth() == 1 && image->numColors() == 2) {
+ register uint *p = (uint *)image->bits();
+ int nbytes = image->numBytes();
+ for (i=0; i<nbytes/4; i++) {
+ *p = ~*p;
+ p++;
+ }
+ uchar *p2 = (uchar *)p;
+ for (i=0; i<(nbytes&3); i++) {
+ *p2 = ~*p2;
+ p2++;
+ }
+ QRgb t = image->color(0); // swap color 0 and 1
+ image->setColor(0, image->color(1));
+ image->setColor(1, t);
+ }
+}
+
+/*
+ QImageIO::defineIOHandler("BMP", "^BM", 0,
+ read_bmp_image, write_bmp_image);
+*/
+
+/*****************************************************************************
+ BMP (DIB) image read/write functions
+ *****************************************************************************/
+
+const int BMP_FILEHDR_SIZE = 14; // size of BMP_FILEHDR data
+
+QDataStream &operator>>(QDataStream &s, BMP_FILEHDR &bf)
+{ // read file header
+ s.readRawData(bf.bfType, 2);
+ s >> bf.bfSize >> bf.bfReserved1 >> bf.bfReserved2 >> bf.bfOffBits;
+ return s;
+}
+
+QDataStream &operator<<(QDataStream &s, const BMP_FILEHDR &bf)
+{ // write file header
+ s.writeRawData(bf.bfType, 2);
+ s << bf.bfSize << bf.bfReserved1 << bf.bfReserved2 << bf.bfOffBits;
+ return s;
+}
+
+
+const int BMP_OLD = 12; // old Windows/OS2 BMP size
+const int BMP_WIN = 40; // new Windows BMP size
+const int BMP_OS2 = 64; // new OS/2 BMP size
+
+const int BMP_RGB = 0; // no compression
+const int BMP_RLE8 = 1; // run-length encoded, 8 bits
+const int BMP_RLE4 = 2; // run-length encoded, 4 bits
+const int BMP_BITFIELDS = 3; // RGB values encoded in data as bit-fields
+
+
+QDataStream &operator>>(QDataStream &s, BMP_INFOHDR &bi)
+{
+ s >> bi.biSize;
+ if (bi.biSize == BMP_WIN || bi.biSize == BMP_OS2) {
+ s >> bi.biWidth >> bi.biHeight >> bi.biPlanes >> bi.biBitCount;
+ s >> bi.biCompression >> bi.biSizeImage;
+ s >> bi.biXPelsPerMeter >> bi.biYPelsPerMeter;
+ s >> bi.biClrUsed >> bi.biClrImportant;
+ }
+ else { // probably old Windows format
+ qint16 w, h;
+ s >> w >> h >> bi.biPlanes >> bi.biBitCount;
+ bi.biWidth = w;
+ bi.biHeight = h;
+ bi.biCompression = BMP_RGB; // no compression
+ bi.biSizeImage = 0;
+ bi.biXPelsPerMeter = bi.biYPelsPerMeter = 0;
+ bi.biClrUsed = bi.biClrImportant = 0;
+ }
+ return s;
+}
+
+QDataStream &operator<<(QDataStream &s, const BMP_INFOHDR &bi)
+{
+ s << bi.biSize;
+ s << bi.biWidth << bi.biHeight;
+ s << bi.biPlanes;
+ s << bi.biBitCount;
+ s << bi.biCompression;
+ s << bi.biSizeImage;
+ s << bi.biXPelsPerMeter << bi.biYPelsPerMeter;
+ s << bi.biClrUsed << bi.biClrImportant;
+ return s;
+}
+
+static int calc_shift(int mask)
+{
+ int result = 0;
+ while (!(mask & 1)) {
+ result++;
+ mask >>= 1;
+ }
+ return result;
+}
+
+static bool read_dib_fileheader(QDataStream &s, BMP_FILEHDR &bf)
+{
+ // read BMP file header
+ s >> bf;
+ if (s.status() != QDataStream::Ok)
+ return false;
+
+ // check header
+ if (qstrncmp(bf.bfType,"BM",2) != 0)
+ return false;
+
+ return true;
+}
+
+static bool read_dib_infoheader(QDataStream &s, BMP_INFOHDR &bi)
+{
+ s >> bi; // read BMP info header
+ if (s.status() != QDataStream::Ok)
+ return false;
+
+ int nbits = bi.biBitCount;
+ int comp = bi.biCompression;
+ if (!(nbits == 1 || nbits == 4 || nbits == 8 || nbits == 16 || nbits == 24 || nbits == 32) ||
+ bi.biPlanes != 1 || comp > BMP_BITFIELDS)
+ return false; // weird BMP image
+ if (!(comp == BMP_RGB || (nbits == 4 && comp == BMP_RLE4) ||
+ (nbits == 8 && comp == BMP_RLE8) || ((nbits == 16 || nbits == 32) && comp == BMP_BITFIELDS)))
+ return false; // weird compression type
+
+ return true;
+}
+
+static bool read_dib_body(QDataStream &s, const BMP_INFOHDR &bi, int offset, int startpos, QImage &image)
+{
+ QIODevice* d = s.device();
+ if (d->atEnd()) // end of stream/file
+ return false;
+#if 0
+ qDebug("offset...........%d", offset);
+ qDebug("startpos.........%d", startpos);
+ qDebug("biSize...........%d", bi.biSize);
+ qDebug("biWidth..........%d", bi.biWidth);
+ qDebug("biHeight.........%d", bi.biHeight);
+ qDebug("biPlanes.........%d", bi.biPlanes);
+ qDebug("biBitCount.......%d", bi.biBitCount);
+ qDebug("biCompression....%d", bi.biCompression);
+ qDebug("biSizeImage......%d", bi.biSizeImage);
+ qDebug("biXPelsPerMeter..%d", bi.biXPelsPerMeter);
+ qDebug("biYPelsPerMeter..%d", bi.biYPelsPerMeter);
+ qDebug("biClrUsed........%d", bi.biClrUsed);
+ qDebug("biClrImportant...%d", bi.biClrImportant);
+#endif
+ int w = bi.biWidth, h = bi.biHeight, nbits = bi.biBitCount;
+ int t = bi.biSize, comp = bi.biCompression;
+ int red_mask = 0;
+ int green_mask = 0;
+ int blue_mask = 0;
+ int red_shift = 0;
+ int green_shift = 0;
+ int blue_shift = 0;
+ int red_scale = 0;
+ int green_scale = 0;
+ int blue_scale = 0;
+
+ int ncols = 0;
+ int depth = 0;
+ QImage::Format format;
+ switch (nbits) {
+ case 32:
+ case 24:
+ case 16:
+ depth = 32;
+ format = QImage::Format_RGB32;
+ break;
+ case 8:
+ case 4:
+ depth = 8;
+ format = QImage::Format_Indexed8;
+ break;
+ default:
+ depth = 1;
+ format = QImage::Format_Mono;
+ }
+
+ if (bi.biHeight < 0)
+ h = -h; // support images with negative height
+
+ if (image.size() != QSize(w, h) || image.format() != format) {
+ image = QImage(w, h, format);
+ if (image.isNull()) // could not create image
+ return false;
+ }
+
+ if (depth != 32) {
+ ncols = bi.biClrUsed ? bi.biClrUsed : 1 << nbits;
+ image.setNumColors(ncols);
+ }
+
+ image.setDotsPerMeterX(bi.biXPelsPerMeter);
+ image.setDotsPerMeterY(bi.biYPelsPerMeter);
+
+ if (!d->isSequential())
+ d->seek(startpos + BMP_FILEHDR_SIZE + bi.biSize); // goto start of colormap
+
+ if (ncols > 0) { // read color table
+ uchar rgb[4];
+ int rgb_len = t == BMP_OLD ? 3 : 4;
+ for (int i=0; i<ncols; i++) {
+ if (d->read((char *)rgb, rgb_len) != rgb_len)
+ return false;
+ image.setColor(i, qRgb(rgb[2],rgb[1],rgb[0]));
+ if (d->atEnd()) // truncated file
+ return false;
+ }
+ } else if (comp == BMP_BITFIELDS && (nbits == 16 || nbits == 32)) {
+ if (d->read((char *)&red_mask, sizeof(red_mask)) != sizeof(red_mask))
+ return false;
+ if (d->read((char *)&green_mask, sizeof(green_mask)) != sizeof(green_mask))
+ return false;
+ if (d->read((char *)&blue_mask, sizeof(blue_mask)) != sizeof(blue_mask))
+ return false;
+ red_shift = calc_shift(red_mask);
+ red_scale = 256 / ((red_mask >> red_shift) + 1);
+ green_shift = calc_shift(green_mask);
+ green_scale = 256 / ((green_mask >> green_shift) + 1);
+ blue_shift = calc_shift(blue_mask);
+ blue_scale = 256 / ((blue_mask >> blue_shift) + 1);
+ } else if (comp == BMP_RGB && (nbits == 24 || nbits == 32)) {
+ blue_mask = 0x000000ff;
+ green_mask = 0x0000ff00;
+ red_mask = 0x00ff0000;
+ blue_shift = 0;
+ green_shift = 8;
+ red_shift = 16;
+ blue_scale = green_scale = red_scale = 1;
+ } else if (comp == BMP_RGB && nbits == 16) {
+ blue_mask = 0x001f;
+ green_mask = 0x03e0;
+ red_mask = 0x7c00;
+ blue_shift = 0;
+ green_shift = 2;
+ red_shift = 7;
+ red_scale = 1;
+ green_scale = 1;
+ blue_scale = 8;
+ }
+
+ // offset can be bogus, be careful
+ if (offset>=0 && startpos + offset > d->pos()) {
+ if (!d->isSequential())
+ d->seek(startpos + offset); // start of image data
+ }
+
+ int bpl = image.bytesPerLine();
+ uchar *data = image.bits();
+
+ if (nbits == 1) { // 1 bit BMP image
+ while (--h >= 0) {
+ if (d->read((char*)(data + h*bpl), bpl) != bpl)
+ break;
+ }
+ if (ncols == 2 && qGray(image.color(0)) < qGray(image.color(1)))
+ swapPixel01(&image); // pixel 0 is white!
+ }
+
+ else if (nbits == 4) { // 4 bit BMP image
+ int buflen = ((w+7)/8)*4;
+ uchar *buf = new uchar[buflen];
+ if (comp == BMP_RLE4) { // run length compression
+ int x=0, y=0, c, i;
+ quint8 b;
+ register uchar *p = data + (h-1)*bpl;
+ const uchar *endp = p + w;
+ while (y < h) {
+ if (!d->getChar((char *)&b))
+ break;
+ if (b == 0) { // escape code
+ if (!d->getChar((char *)&b) || b == 1) {
+ y = h; // exit loop
+ } else switch (b) {
+ case 0: // end of line
+ x = 0;
+ y++;
+ p = data + (h-y-1)*bpl;
+ break;
+ case 2: // delta (jump)
+ {
+ quint8 tmp;
+ d->getChar((char *)&tmp);
+ x += tmp;
+ d->getChar((char *)&tmp);
+ y += tmp;
+ }
+
+ // Protection
+ if ((uint)x >= (uint)w)
+ x = w-1;
+ if ((uint)y >= (uint)h)
+ y = h-1;
+
+ p = data + (h-y-1)*bpl + x;
+ break;
+ default: // absolute mode
+ // Protection
+ if (p + b > endp)
+ b = endp-p;
+
+ i = (c = b)/2;
+ while (i--) {
+ d->getChar((char *)&b);
+ *p++ = b >> 4;
+ *p++ = b & 0x0f;
+ }
+ if (c & 1) {
+ unsigned char tmp;
+ d->getChar((char *)&tmp);
+ *p++ = tmp >> 4;
+ }
+ if ((((c & 3) + 1) & 2) == 2)
+ d->getChar(0); // align on word boundary
+ x += c;
+ }
+ } else { // encoded mode
+ // Protection
+ if (p + b > endp)
+ b = endp-p;
+
+ i = (c = b)/2;
+ d->getChar((char *)&b); // 2 pixels to be repeated
+ while (i--) {
+ *p++ = b >> 4;
+ *p++ = b & 0x0f;
+ }
+ if (c & 1)
+ *p++ = b >> 4;
+ x += c;
+ }
+ }
+ } else if (comp == BMP_RGB) { // no compression
+ memset(data, 0, h*bpl);
+ while (--h >= 0) {
+ if (d->read((char*)buf,buflen) != buflen)
+ break;
+ register uchar *p = data + h*bpl;
+ uchar *b = buf;
+ for (int i=0; i<w/2; i++) { // convert nibbles to bytes
+ *p++ = *b >> 4;
+ *p++ = *b++ & 0x0f;
+ }
+ if (w & 1) // the last nibble
+ *p = *b >> 4;
+ }
+ }
+ delete [] buf;
+ }
+
+ else if (nbits == 8) { // 8 bit BMP image
+ if (comp == BMP_RLE8) { // run length compression
+ int x=0, y=0;
+ quint8 b;
+ register uchar *p = data + (h-1)*bpl;
+ const uchar *endp = p + w;
+ while (y < h) {
+ if (!d->getChar((char *)&b))
+ break;
+ if (b == 0) { // escape code
+ if (!d->getChar((char *)&b) || b == 1) {
+ y = h; // exit loop
+ } else switch (b) {
+ case 0: // end of line
+ x = 0;
+ y++;
+ p = data + (h-y-1)*bpl;
+ break;
+ case 2: // delta (jump)
+ // Protection
+ if ((uint)x >= (uint)w)
+ x = w-1;
+ if ((uint)y >= (uint)h)
+ y = h-1;
+
+ {
+ quint8 tmp;
+ d->getChar((char *)&tmp);
+ x += tmp;
+ d->getChar((char *)&tmp);
+ y += tmp;
+ }
+ p = data + (h-y-1)*bpl + x;
+ break;
+ default: // absolute mode
+ // Protection
+ if (p + b > endp)
+ b = endp-p;
+
+ if (d->read((char *)p, b) != b)
+ return false;
+ if ((b & 1) == 1)
+ d->getChar(0); // align on word boundary
+ x += b;
+ p += b;
+ }
+ } else { // encoded mode
+ // Protection
+ if (p + b > endp)
+ b = endp-p;
+
+ char tmp;
+ d->getChar(&tmp);
+ memset(p, tmp, b); // repeat pixel
+ x += b;
+ p += b;
+ }
+ }
+ } else if (comp == BMP_RGB) { // uncompressed
+ while (--h >= 0) {
+ if (d->read((char *)data + h*bpl, bpl) != bpl)
+ break;
+ }
+ }
+ }
+
+ else if (nbits == 16 || nbits == 24 || nbits == 32) { // 16,24,32 bit BMP image
+ register QRgb *p;
+ QRgb *end;
+ uchar *buf24 = new uchar[bpl];
+ int bpl24 = ((w*nbits+31)/32)*4;
+ uchar *b;
+ int c;
+
+ while (--h >= 0) {
+ p = (QRgb *)(data + h*bpl);
+ end = p + w;
+ if (d->read((char *)buf24,bpl24) != bpl24)
+ break;
+ b = buf24;
+ while (p < end) {
+ c = *(uchar*)b | (*(uchar*)(b+1)<<8);
+ if (nbits != 16)
+ c |= *(uchar*)(b+2)<<16;
+ *p++ = qRgb(((c & red_mask) >> red_shift) * red_scale,
+ ((c & green_mask) >> green_shift) * green_scale,
+ ((c & blue_mask) >> blue_shift) * blue_scale);
+ b += nbits/8;
+ }
+ }
+ delete[] buf24;
+ }
+
+ if (bi.biHeight < 0) {
+ // Flip the image
+ uchar *buf = new uchar[bpl];
+ h = -bi.biHeight;
+ for (int y = 0; y < h/2; ++y) {
+ memcpy(buf, data + y*bpl, bpl);
+ memcpy(data + y*bpl, data + (h-y-1)*bpl, bpl);
+ memcpy(data + (h-y-1)*bpl, buf, bpl);
+ }
+ delete [] buf;
+ }
+
+ return true;
+}
+
+// this is also used in qmime_win.cpp
+bool qt_write_dib(QDataStream &s, QImage image)
+{
+ int nbits;
+ int bpl_bmp;
+ int bpl = image.bytesPerLine();
+
+ QIODevice* d = s.device();
+ if (!d->isWritable())
+ return false;
+
+ if (image.depth() == 8 && image.numColors() <= 16) {
+ bpl_bmp = (((bpl+1)/2+3)/4)*4;
+ nbits = 4;
+ } else if (image.depth() == 32) {
+ bpl_bmp = ((image.width()*24+31)/32)*4;
+ nbits = 24;
+#ifdef Q_WS_QWS
+ } else if (image.depth() == 1 || image.depth() == 8) {
+ // Qt for Embedded Linux doesn't word align.
+ bpl_bmp = ((image.width()*image.depth()+31)/32)*4;
+ nbits = image.depth();
+#endif
+ } else {
+ bpl_bmp = bpl;
+ nbits = image.depth();
+ }
+
+ BMP_INFOHDR bi;
+ bi.biSize = BMP_WIN; // build info header
+ bi.biWidth = image.width();
+ bi.biHeight = image.height();
+ bi.biPlanes = 1;
+ bi.biBitCount = nbits;
+ bi.biCompression = BMP_RGB;
+ bi.biSizeImage = bpl_bmp*image.height();
+ bi.biXPelsPerMeter = image.dotsPerMeterX() ? image.dotsPerMeterX()
+ : 2834; // 72 dpi default
+ bi.biYPelsPerMeter = image.dotsPerMeterY() ? image.dotsPerMeterY() : 2834;
+ bi.biClrUsed = image.numColors();
+ bi.biClrImportant = image.numColors();
+ s << bi; // write info header
+ if (s.status() != QDataStream::Ok)
+ return false;
+
+ if (image.depth() != 32) { // write color table
+ uchar *color_table = new uchar[4*image.numColors()];
+ uchar *rgb = color_table;
+ QVector<QRgb> c = image.colorTable();
+ for (int i=0; i<image.numColors(); i++) {
+ *rgb++ = qBlue (c[i]);
+ *rgb++ = qGreen(c[i]);
+ *rgb++ = qRed (c[i]);
+ *rgb++ = 0;
+ }
+ if (d->write((char *)color_table, 4*image.numColors()) == -1) {
+ delete [] color_table;
+ return false;
+ }
+ delete [] color_table;
+ }
+
+ if (image.format() == QImage::Format_MonoLSB)
+ image = image.convertToFormat(QImage::Format_Mono);
+
+ int y;
+
+ if (nbits == 1 || nbits == 8) { // direct output
+#ifdef Q_WS_QWS
+ // Qt for Embedded Linux doesn't word align.
+ int pad = bpl_bmp - bpl;
+ char padding[4];
+#endif
+ for (y=image.height()-1; y>=0; y--) {
+ if (d->write((char*)image.scanLine(y), bpl) == -1)
+ return false;
+#ifdef Q_WS_QWS
+ if (d->write(padding, pad) == -1)
+ return false;
+#endif
+ }
+ return true;
+ }
+
+ uchar *buf = new uchar[bpl_bmp];
+ uchar *b, *end;
+ register uchar *p;
+
+ memset(buf, 0, bpl_bmp);
+ for (y=image.height()-1; y>=0; y--) { // write the image bits
+ if (nbits == 4) { // convert 8 -> 4 bits
+ p = image.scanLine(y);
+ b = buf;
+ end = b + image.width()/2;
+ while (b < end) {
+ *b++ = (*p << 4) | (*(p+1) & 0x0f);
+ p += 2;
+ }
+ if (image.width() & 1)
+ *b = *p << 4;
+ } else { // 32 bits
+ QRgb *p = (QRgb *)image.scanLine(y);
+ QRgb *end = p + image.width();
+ b = buf;
+ while (p < end) {
+ *b++ = qBlue(*p);
+ *b++ = qGreen(*p);
+ *b++ = qRed(*p);
+ p++;
+ }
+ }
+ if (bpl_bmp != d->write((char*)buf, bpl_bmp)) {
+ delete[] buf;
+ return false;
+ }
+ }
+ delete[] buf;
+ return true;
+}
+
+// this is also used in qmime_win.cpp
+bool qt_read_dib(QDataStream &s, QImage &image)
+{
+ BMP_INFOHDR bi;
+ if (!read_dib_infoheader(s, bi))
+ return false;
+ return read_dib_body(s, bi, -1, -BMP_FILEHDR_SIZE, image);
+}
+
+QBmpHandler::QBmpHandler()
+ : state(Ready)
+{
+}
+
+bool QBmpHandler::readHeader()
+{
+ state = Error;
+
+ QIODevice *d = device();
+ QDataStream s(d);
+ startpos = d->pos();
+
+ // Intel byte order
+ s.setByteOrder(QDataStream::LittleEndian);
+
+ // read BMP file header
+ if (!read_dib_fileheader(s, fileHeader))
+ return false;
+
+ // read BMP info header
+ if (!read_dib_infoheader(s, infoHeader))
+ return false;
+
+ state = ReadHeader;
+ return true;
+}
+
+bool QBmpHandler::canRead() const
+{
+ if (state == Ready) {
+ if (!canRead(device()))
+ return false;
+ setFormat("bmp");
+ return true;
+ }
+ return state != Error;
+}
+
+bool QBmpHandler::canRead(QIODevice *device)
+{
+ if (!device) {
+ qWarning("QBmpHandler::canRead() called with 0 pointer");
+ return false;
+ }
+
+ char head[2];
+ if (device->peek(head, sizeof(head)) != sizeof(head))
+ return false;
+
+ return (qstrncmp(head, "BM", 2) == 0);
+}
+
+bool QBmpHandler::read(QImage *image)
+{
+ if (state == Error)
+ return false;
+
+ if (!image) {
+ qWarning("QBmpHandler::read: cannot read into null pointer");
+ return false;
+ }
+
+ if (state == Ready && !readHeader()) {
+ state = Error;
+ return false;
+ }
+
+ QIODevice *d = device();
+ QDataStream s(d);
+
+ // Intel byte order
+ s.setByteOrder(QDataStream::LittleEndian);
+
+ // read image
+ if (!read_dib_body(s, infoHeader, fileHeader.bfOffBits, startpos, *image))
+ return false;
+
+ state = Ready;
+ return true;
+}
+
+bool QBmpHandler::write(const QImage &img)
+{
+ QImage image;
+ switch (img.format()) {
+ case QImage::Format_ARGB8565_Premultiplied:
+ case QImage::Format_ARGB8555_Premultiplied:
+ case QImage::Format_ARGB6666_Premultiplied:
+ case QImage::Format_ARGB4444_Premultiplied:
+ image = img.convertToFormat(QImage::Format_ARGB32);
+ break;
+ case QImage::Format_RGB16:
+ case QImage::Format_RGB888:
+ case QImage::Format_RGB666:
+ case QImage::Format_RGB555:
+ case QImage::Format_RGB444:
+ image = img.convertToFormat(QImage::Format_RGB32);
+ break;
+ default:
+ image = img;
+ }
+
+ QIODevice *d = device();
+ QDataStream s(d);
+ BMP_FILEHDR bf;
+ int bpl_bmp;
+ int bpl = image.bytesPerLine();
+
+ // Code partially repeated in qt_write_dib
+ if (image.depth() == 8 && image.numColors() <= 16) {
+ bpl_bmp = (((bpl+1)/2+3)/4)*4;
+ } else if (image.depth() == 32) {
+ bpl_bmp = ((image.width()*24+31)/32)*4;
+ } else {
+ bpl_bmp = bpl;
+ }
+
+ // Intel byte order
+ s.setByteOrder(QDataStream::LittleEndian);
+
+ // build file header
+ memcpy(bf.bfType, "BM", 2);
+
+ // write file header
+ bf.bfReserved1 = 0;
+ bf.bfReserved2 = 0;
+ bf.bfOffBits = BMP_FILEHDR_SIZE + BMP_WIN + image.numColors() * 4;
+ bf.bfSize = bf.bfOffBits + bpl_bmp*image.height();
+ s << bf;
+
+ // write image
+ return qt_write_dib(s, image);
+}
+
+bool QBmpHandler::supportsOption(ImageOption option) const
+{
+ return option == Size
+ || option == ImageFormat;
+}
+
+QVariant QBmpHandler::option(ImageOption option) const
+{
+ if (option == Size) {
+ if (state == Error)
+ return QVariant();
+ if (state == Ready && !const_cast<QBmpHandler*>(this)->readHeader())
+ return QVariant();
+ return QSize(infoHeader.biWidth, infoHeader.biHeight);
+ } else if (option == ImageFormat) {
+ if (state == Error)
+ return QVariant();
+ if (state == Ready && !const_cast<QBmpHandler*>(this)->readHeader())
+ return QVariant();
+ QImage::Format format;
+ switch (infoHeader.biBitCount) {
+ case 32:
+ case 24:
+ case 16:
+ format = QImage::Format_RGB32;
+ break;
+ case 8:
+ case 4:
+ format = QImage::Format_Indexed8;
+ break;
+ default:
+ format = QImage::Format_Mono;
+ }
+ return format;
+ }
+ return QVariant();
+}
+
+void QBmpHandler::setOption(ImageOption option, const QVariant &value)
+{
+ Q_UNUSED(option);
+ Q_UNUSED(value);
+}
+
+QByteArray QBmpHandler::name() const
+{
+ return "bmp";
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_IMAGEFORMAT_BMP
diff --git a/src/gui/image/qbmphandler_p.h b/src/gui/image/qbmphandler_p.h
new file mode 100644
index 0000000000..6e953a52a8
--- /dev/null
+++ b/src/gui/image/qbmphandler_p.h
@@ -0,0 +1,117 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QBMPHANDLER_P_H
+#define QBMPHANDLER_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 "QtGui/qimageiohandler.h"
+
+#ifndef QT_NO_IMAGEFORMAT_BMP
+
+QT_BEGIN_NAMESPACE
+
+struct BMP_FILEHDR { // BMP file header
+ char bfType[2]; // "BM"
+ qint32 bfSize; // size of file
+ qint16 bfReserved1;
+ qint16 bfReserved2;
+ qint32 bfOffBits; // pointer to the pixmap bits
+};
+
+struct BMP_INFOHDR { // BMP information header
+ qint32 biSize; // size of this struct
+ qint32 biWidth; // pixmap width
+ qint32 biHeight; // pixmap height
+ qint16 biPlanes; // should be 1
+ qint16 biBitCount; // number of bits per pixel
+ qint32 biCompression; // compression method
+ qint32 biSizeImage; // size of image
+ qint32 biXPelsPerMeter; // horizontal resolution
+ qint32 biYPelsPerMeter; // vertical resolution
+ qint32 biClrUsed; // number of colors used
+ qint32 biClrImportant; // number of important colors
+};
+
+class QBmpHandler : public QImageIOHandler
+{
+public:
+ QBmpHandler();
+ bool canRead() const;
+ bool read(QImage *image);
+ bool write(const QImage &image);
+
+ QByteArray name() const;
+
+ static bool canRead(QIODevice *device);
+
+ QVariant option(ImageOption option) const;
+ void setOption(ImageOption option, const QVariant &value);
+ bool supportsOption(ImageOption option) const;
+
+private:
+ bool readHeader();
+ enum State {
+ Ready,
+ ReadHeader,
+ Error
+ };
+ State state;
+ BMP_FILEHDR fileHeader;
+ BMP_INFOHDR infoHeader;
+ int startpos;
+};
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_IMAGEFORMAT_BMP
+
+#endif // QBMPHANDLER_P_H
diff --git a/src/gui/image/qicon.cpp b/src/gui/image/qicon.cpp
new file mode 100644
index 0000000000..3c71f15b4d
--- /dev/null
+++ b/src/gui/image/qicon.cpp
@@ -0,0 +1,1128 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qicon.h"
+#include "qiconengine.h"
+#include "qiconengineplugin.h"
+#include "private/qfactoryloader_p.h"
+#include "qapplication.h"
+#include "qstyleoption.h"
+#include "qpainter.h"
+#include "qfileinfo.h"
+#include "qstyle.h"
+#include "qpixmapcache.h"
+#include "qvariant.h"
+#include "qdebug.h"
+
+#ifdef Q_WS_MAC
+#include <private/qt_mac_p.h>
+#include <Carbon/Carbon.h>
+#endif
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \enum QIcon::Mode
+
+ This enum type describes the mode for which a pixmap is intended
+ to be used. The currently defined modes are:
+
+ \value Normal
+ Display the pixmap when the user is
+ not interacting with the icon, but the
+ functionality represented by the icon is available.
+ \value Disabled
+ Display the pixmap when the
+ functionality represented by the icon is not available.
+ \value Active
+ Display the pixmap when the
+ functionality represented by the icon is available and
+ the user is interacting with the icon, for example, moving the
+ mouse over it or clicking it.
+ \value Selected
+ Display the pixmap when the item represented by the icon is
+ selected.
+*/
+
+/*!
+ \enum QIcon::State
+
+ This enum describes the state for which a pixmap is intended to be
+ used. The \e state can be:
+
+ \value Off Display the pixmap when the widget is in an "off" state
+ \value On Display the pixmap when the widget is in an "on" state
+*/
+
+static QBasicAtomicInt serialNumCounter = Q_BASIC_ATOMIC_INITIALIZER(1);
+
+class QIconPrivate
+{
+public:
+ QIconPrivate(): engine(0), ref(1), serialNum(serialNumCounter.fetchAndAddRelaxed(1)), detach_no(0), engine_version(2), v1RefCount(0) {}
+
+ ~QIconPrivate() {
+ if (engine_version == 1) {
+ if (!v1RefCount->deref()) {
+ delete engine;
+ delete v1RefCount;
+ }
+ } else if (engine_version == 2) {
+ delete engine;
+ }
+ }
+
+ QIconEngine *engine;
+
+ QAtomicInt ref;
+ int serialNum;
+ int detach_no;
+ int engine_version;
+
+ QAtomicInt *v1RefCount;
+};
+
+
+struct QPixmapIconEngineEntry
+{
+ QPixmapIconEngineEntry():mode(QIcon::Normal), state(QIcon::Off){}
+ QPixmapIconEngineEntry(const QPixmap &pm, QIcon::Mode m = QIcon::Normal, QIcon::State s = QIcon::Off)
+ :pixmap(pm), size(pm.size()), mode(m), state(s){}
+ QPixmapIconEngineEntry(const QString &file, const QSize &sz = QSize(), QIcon::Mode m = QIcon::Normal, QIcon::State s = QIcon::Off)
+ :fileName(file), size(sz), mode(m), state(s){}
+ QPixmap pixmap;
+ QString fileName;
+ QSize size;
+ QIcon::Mode mode;
+ QIcon::State state;
+ bool isNull() const {return (fileName.isEmpty() && pixmap.isNull()); }
+};
+
+class QPixmapIconEngine : public QIconEngineV2 {
+public:
+ QPixmapIconEngine();
+ QPixmapIconEngine(const QPixmapIconEngine &);
+ ~QPixmapIconEngine();
+ void paint(QPainter *painter, const QRect &rect, QIcon::Mode mode, QIcon::State state);
+ QPixmap pixmap(const QSize &size, QIcon::Mode mode, QIcon::State state);
+ QPixmapIconEngineEntry *bestMatch(const QSize &size, QIcon::Mode mode, QIcon::State state, bool sizeOnly);
+ QSize actualSize(const QSize &size, QIcon::Mode mode, QIcon::State state);
+ void addPixmap(const QPixmap &pixmap, QIcon::Mode mode, QIcon::State state);
+ void addFile(const QString &fileName, const QSize &size, QIcon::Mode mode, QIcon::State state);
+
+ // v2 functions
+ QString key() const;
+ QIconEngineV2 *clone() const;
+ bool read(QDataStream &in);
+ bool write(QDataStream &out) const;
+ void virtual_hook(int id, void *data);
+
+private:
+ QPixmapIconEngineEntry *tryMatch(const QSize &size, QIcon::Mode mode, QIcon::State state);
+ QVector<QPixmapIconEngineEntry> pixmaps;
+
+ friend QDataStream &operator<<(QDataStream &s, const QIcon &icon);
+};
+
+QPixmapIconEngine::QPixmapIconEngine()
+{
+}
+
+QPixmapIconEngine::QPixmapIconEngine(const QPixmapIconEngine &other)
+ : QIconEngineV2(other), pixmaps(other.pixmaps)
+{
+}
+
+QPixmapIconEngine::~QPixmapIconEngine()
+{
+}
+
+void QPixmapIconEngine::paint(QPainter *painter, const QRect &rect, QIcon::Mode mode, QIcon::State state)
+{
+ QSize pixmapSize = rect.size();
+#if defined(Q_WS_MAC) && !defined(Q_WS_MAC64)
+ pixmapSize *= (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_4) ? HIGetScaleFactor() : 1;
+#endif
+ painter->drawPixmap(rect, pixmap(pixmapSize, mode, state));
+}
+
+static inline int area(const QSize &s) { return s.width() * s.height(); }
+
+// returns the smallest of the two that is still larger than or equal to size.
+static QPixmapIconEngineEntry *bestSizeMatch( const QSize &size, QPixmapIconEngineEntry *pa, QPixmapIconEngineEntry *pb)
+{
+ int s = area(size);
+ if (pa->size == QSize() && pa->pixmap.isNull()) {
+ pa->pixmap = QPixmap(pa->fileName);
+ pa->size = pa->pixmap.size();
+ }
+ int a = area(pa->size);
+ if (pb->size == QSize() && pb->pixmap.isNull()) {
+ pb->pixmap = QPixmap(pb->fileName);
+ pb->size = pb->pixmap.size();
+ }
+ int b = area(pb->size);
+ int res = a;
+ if (qMin(a,b) >= s)
+ res = qMin(a,b);
+ else
+ res = qMax(a,b);
+ if (res == a)
+ return pa;
+ return pb;
+}
+
+QPixmapIconEngineEntry *QPixmapIconEngine::tryMatch(const QSize &size, QIcon::Mode mode, QIcon::State state)
+{
+ QPixmapIconEngineEntry *pe = 0;
+ for (int i = 0; i < pixmaps.count(); ++i)
+ if (pixmaps.at(i).mode == mode && pixmaps.at(i).state == state) {
+ if (pe)
+ pe = bestSizeMatch(size, &pixmaps[i], pe);
+ else
+ pe = &pixmaps[i];
+ }
+ return pe;
+}
+
+
+QPixmapIconEngineEntry *QPixmapIconEngine::bestMatch(const QSize &size, QIcon::Mode mode, QIcon::State state, bool sizeOnly)
+{
+ QPixmapIconEngineEntry *pe = tryMatch(size, mode, state);
+ while (!pe){
+ QIcon::State oppositeState = (state == QIcon::On) ? QIcon::Off : QIcon::On;
+ if (mode == QIcon::Disabled || mode == QIcon::Selected) {
+ QIcon::Mode oppositeMode = (mode == QIcon::Disabled) ? QIcon::Selected : QIcon::Disabled;
+ if ((pe = tryMatch(size, QIcon::Normal, state)))
+ break;
+ if ((pe = tryMatch(size, QIcon::Active, state)))
+ break;
+ if ((pe = tryMatch(size, mode, oppositeState)))
+ break;
+ if ((pe = tryMatch(size, QIcon::Normal, oppositeState)))
+ break;
+ if ((pe = tryMatch(size, QIcon::Active, oppositeState)))
+ break;
+ if ((pe = tryMatch(size, oppositeMode, state)))
+ break;
+ if ((pe = tryMatch(size, oppositeMode, oppositeState)))
+ break;
+ } else {
+ QIcon::Mode oppositeMode = (mode == QIcon::Normal) ? QIcon::Active : QIcon::Normal;
+ if ((pe = tryMatch(size, oppositeMode, state)))
+ break;
+ if ((pe = tryMatch(size, mode, oppositeState)))
+ break;
+ if ((pe = tryMatch(size, oppositeMode, oppositeState)))
+ break;
+ if ((pe = tryMatch(size, QIcon::Disabled, state)))
+ break;
+ if ((pe = tryMatch(size, QIcon::Selected, state)))
+ break;
+ if ((pe = tryMatch(size, QIcon::Disabled, oppositeState)))
+ break;
+ if ((pe = tryMatch(size, QIcon::Selected, oppositeState)))
+ break;
+ }
+
+ if (!pe)
+ return pe;
+ }
+
+ if (sizeOnly ? (pe->size.isNull() || !pe->size.isValid()) : pe->pixmap.isNull()) {
+ pe->pixmap = QPixmap(pe->fileName);
+ if (!pe->pixmap.isNull())
+ pe->size = pe->pixmap.size();
+ }
+
+ return pe;
+}
+
+QPixmap QPixmapIconEngine::pixmap(const QSize &size, QIcon::Mode mode, QIcon::State state)
+{
+ QPixmap pm;
+ QPixmapIconEngineEntry *pe = bestMatch(size, mode, state, false);
+ if (pe)
+ pm = pe->pixmap;
+
+ if (pm.isNull()) {
+ int idx = pixmaps.count();
+ while (--idx >= 0) {
+ if (pe == &pixmaps[idx]) {
+ pixmaps.remove(idx);
+ break;
+ }
+ }
+ if (pixmaps.isEmpty())
+ return pm;
+ else
+ return pixmap(size, mode, state);
+ }
+
+ QSize actualSize = pm.size();
+ if (!actualSize.isNull() && (actualSize.width() > size.width() || actualSize.height() > size.height()))
+ actualSize.scale(size, Qt::KeepAspectRatio);
+
+ QString key = QLatin1String("$qt_icon_")
+ + QString::number(pm.cacheKey())
+ + QString::number(pe->mode)
+ + QString::number(actualSize.width())
+ + QLatin1Char('_')
+ + QString::number(actualSize.height())
+ + QLatin1Char('_');
+
+
+ if (mode == QIcon::Active) {
+ if (QPixmapCache::find(key + QString::number(mode), pm))
+ return pm; // horray
+ if (QPixmapCache::find(key + QString::number(QIcon::Normal), pm)) {
+ QStyleOption opt(0);
+ opt.palette = QApplication::palette();
+ QPixmap active = QApplication::style()->generatedIconPixmap(QIcon::Active, pm, &opt);
+ if (pm.cacheKey() == active.cacheKey())
+ return pm;
+ }
+ }
+
+ if (!QPixmapCache::find(key + QString::number(mode), pm)) {
+ if (pm.size() != actualSize)
+ pm = pm.scaled(actualSize, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
+ if (pe->mode != mode && mode != QIcon::Normal) {
+ QStyleOption opt(0);
+ opt.palette = QApplication::palette();
+ QPixmap generated = QApplication::style()->generatedIconPixmap(mode, pm, &opt);
+ if (!generated.isNull())
+ pm = generated;
+ }
+ QPixmapCache::insert(key + QString::number(mode), pm);
+ }
+ return pm;
+}
+
+QSize QPixmapIconEngine::actualSize(const QSize &size, QIcon::Mode mode, QIcon::State state)
+{
+ QSize actualSize;
+ if (QPixmapIconEngineEntry *pe = bestMatch(size, mode, state, true))
+ actualSize = pe->size;
+
+ if (actualSize.isNull())
+ return actualSize;
+
+ if (!actualSize.isNull() && (actualSize.width() > size.width() || actualSize.height() > size.height()))
+ actualSize.scale(size, Qt::KeepAspectRatio);
+ return actualSize;
+}
+
+void QPixmapIconEngine::addPixmap(const QPixmap &pixmap, QIcon::Mode mode, QIcon::State state)
+{
+ if (!pixmap.isNull()) {
+ QPixmapIconEngineEntry *pe = tryMatch(pixmap.size(), mode, state);
+ if(pe && pe->size == pixmap.size()) {
+ pe->pixmap = pixmap;
+ pe->fileName.clear();
+ } else {
+ pixmaps += QPixmapIconEngineEntry(pixmap, mode, state);
+ }
+ }
+}
+
+void QPixmapIconEngine::addFile(const QString &fileName, const QSize &_size, QIcon::Mode mode, QIcon::State state)
+{
+ if (!fileName.isEmpty()) {
+ QSize size = _size;
+ QPixmap pixmap;
+
+ QString abs = fileName;
+ if (fileName.at(0) != QLatin1Char(':'))
+ abs = QFileInfo(fileName).absoluteFilePath();
+
+ for (int i = 0; i < pixmaps.count(); ++i) {
+ if (pixmaps.at(i).mode == mode && pixmaps.at(i).state == state) {
+ QPixmapIconEngineEntry *pe = &pixmaps[i];
+ if(size == QSize()) {
+ pixmap = QPixmap(abs);
+ size = pixmap.size();
+ }
+ if (pe->size == QSize() && pe->pixmap.isNull()) {
+ pe->pixmap = QPixmap(pe->fileName);
+ pe->size = pe->pixmap.size();
+ }
+ if(pe->size == size) {
+ pe->pixmap = pixmap;
+ pe->fileName = abs;
+ return;
+ }
+ }
+ }
+ QPixmapIconEngineEntry e(abs, size, mode, state);
+ e.pixmap = pixmap;
+ pixmaps += e;
+ }
+}
+
+QString QPixmapIconEngine::key() const
+{
+ return QLatin1String("QPixmapIconEngine");
+}
+
+QIconEngineV2 *QPixmapIconEngine::clone() const
+{
+ return new QPixmapIconEngine(*this);
+}
+
+bool QPixmapIconEngine::read(QDataStream &in)
+{
+ int num_entries;
+ QPixmap pm;
+ QString fileName;
+ QSize sz;
+ uint mode;
+ uint state;
+
+ in >> num_entries;
+ for (int i=0; i < num_entries; ++i) {
+ if (in.atEnd()) {
+ pixmaps.clear();
+ return false;
+ }
+ in >> pm;
+ in >> fileName;
+ in >> sz;
+ in >> mode;
+ in >> state;
+ if (pm.isNull())
+ addFile(fileName, sz, QIcon::Mode(mode), QIcon::State(state));
+ else
+ addPixmap(pm, QIcon::Mode(mode), QIcon::State(state));
+ }
+ return true;
+}
+
+bool QPixmapIconEngine::write(QDataStream &out) const
+{
+ int num_entries = pixmaps.size();
+ out << num_entries;
+ for (int i=0; i < num_entries; ++i) {
+ if (pixmaps.at(i).pixmap.isNull())
+ out << QPixmap(pixmaps.at(i).fileName);
+ else
+ out << pixmaps.at(i).pixmap;
+ out << pixmaps.at(i).fileName;
+ out << pixmaps.at(i).size;
+ out << (uint) pixmaps.at(i).mode;
+ out << (uint) pixmaps.at(i).state;
+ }
+ return true;
+}
+
+void QPixmapIconEngine::virtual_hook(int id, void *data)
+{
+ switch (id) {
+ case QIconEngineV2::AvailableSizesHook: {
+ QIconEngineV2::AvailableSizesArgument &arg =
+ *reinterpret_cast<QIconEngineV2::AvailableSizesArgument*>(data);
+ arg.sizes.clear();
+ for (int i = 0; i < pixmaps.size(); ++i) {
+ QPixmapIconEngineEntry &pe = pixmaps[i];
+ if (pe.size == QSize() && pe.pixmap.isNull()) {
+ pe.pixmap = QPixmap(pe.fileName);
+ pe.size = pe.pixmap.size();
+ }
+ if (pe.mode == arg.mode && pe.state == arg.state && !pe.size.isEmpty())
+ arg.sizes.push_back(pe.size);
+ }
+ break;
+ }
+ default:
+ QIconEngineV2::virtual_hook(id, data);
+ }
+}
+
+#if !defined (QT_NO_LIBRARY) && !defined(QT_NO_SETTINGS)
+Q_GLOBAL_STATIC_WITH_ARGS(QFactoryLoader, loader,
+ (QIconEngineFactoryInterface_iid, QLatin1String("/iconengines"), Qt::CaseInsensitive))
+Q_GLOBAL_STATIC_WITH_ARGS(QFactoryLoader, loaderV2,
+ (QIconEngineFactoryInterfaceV2_iid, QLatin1String("/iconengines"), Qt::CaseInsensitive))
+#endif
+
+
+
+/*!
+ \class QIcon
+
+ \brief The QIcon class provides scalable icons in different modes
+ and states.
+
+ \ingroup multimedia
+ \ingroup shared
+ \mainclass
+
+ A QIcon can generate smaller, larger, active, and disabled pixmaps
+ from the set of pixmaps it is given. Such pixmaps are used by Qt
+ widgets to show an icon representing a particular action.
+
+ The simplest use of QIcon is to create one from a QPixmap file or
+ resource, and then use it, allowing Qt to work out all the required
+ icon styles and sizes. For example:
+
+ \snippet doc/src/snippets/code/src_gui_image_qicon.cpp 0
+
+ To undo a QIcon, simply set a null icon in its place:
+
+ \snippet doc/src/snippets/code/src_gui_image_qicon.cpp 1
+
+ Use the QImageReader::supportedImageFormats() and
+ QImageWriter::supportedImageFormats() functions to retrieve a
+ complete list of the supported file formats.
+
+ When you retrieve a pixmap using pixmap(QSize, Mode, State), and no
+ pixmap for this given size, mode and state has been added with
+ addFile() or addPixmap(), then QIcon will generate one on the
+ fly. This pixmap generation happens in a QIconEngineV2. The default
+ engine scales pixmaps down if required, but never up, and it uses
+ the current style to calculate a disabled appearance. By using
+ custom icon engines, you can customize every aspect of generated
+ icons. With QIconEnginePluginV2 it is possible to register different
+ icon engines for different file suffixes, making it possible for
+ third parties to provide additional icon engines to those included
+ with Qt.
+
+ \note Since Qt 4.2, an icon engine that supports SVG is included.
+
+ \section1 Making Classes that Use QIcon
+
+ If you write your own widgets that have an option to set a small
+ pixmap, consider allowing a QIcon to be set for that pixmap. The
+ Qt class QToolButton is an example of such a widget.
+
+ Provide a method to set a QIcon, and when you draw the icon, choose
+ whichever pixmap is appropriate for the current state of your widget.
+ For example:
+ \snippet doc/src/snippets/code/src_gui_image_qicon.cpp 2
+
+ You might also make use of the \c Active mode, perhaps making your
+ widget \c Active when the mouse is over the widget (see \l
+ QWidget::enterEvent()), while the mouse is pressed pending the
+ release that will activate the function, or when it is the currently
+ selected item. If the widget can be toggled, the "On" mode might be
+ used to draw a different icon.
+
+ \img icon.png QIcon
+
+ \sa {fowler}{GUI Design Handbook: Iconic Label}, {Icons Example}
+*/
+
+
+/*!
+ Constructs a null icon.
+*/
+QIcon::QIcon()
+ : d(0)
+{
+}
+
+/*!
+ Constructs an icon from a \a pixmap.
+ */
+QIcon::QIcon(const QPixmap &pixmap)
+ :d(0)
+{
+ addPixmap(pixmap);
+}
+
+/*!
+ Constructs a copy of \a other. This is very fast.
+*/
+QIcon::QIcon(const QIcon &other)
+ :d(other.d)
+{
+ if (d)
+ d->ref.ref();
+}
+
+/*!
+ Constructs an icon from the file with the given \a fileName. The
+ file will be loaded on demand.
+
+ If \a fileName contains a relative path (e.g. the filename only)
+ the relevant file must be found relative to the runtime working
+ directory.
+
+ The file name can be either refer to an actual file on disk or to
+ one of the application's embedded resources. See the
+ \l{resources.html}{Resource System} overview for details on how to
+ embed images and other resource files in the application's
+ executable.
+
+ Use the QImageReader::supportedImageFormats() and
+ QImageWriter::supportedImageFormats() functions to retrieve a
+ complete list of the supported file formats.
+*/
+QIcon::QIcon(const QString &fileName)
+ : d(0)
+{
+ addFile(fileName);
+}
+
+
+/*!
+ Creates an icon with a specific icon \a engine. The icon takes
+ ownership of the engine.
+*/
+QIcon::QIcon(QIconEngine *engine)
+ :d(new QIconPrivate)
+{
+ d->engine_version = 1;
+ d->engine = engine;
+ d->v1RefCount = new QAtomicInt(1);
+}
+
+/*!
+ Creates an icon with a specific icon \a engine. The icon takes
+ ownership of the engine.
+*/
+QIcon::QIcon(QIconEngineV2 *engine)
+ :d(new QIconPrivate)
+{
+ d->engine_version = 2;
+ d->engine = engine;
+}
+
+/*!
+ Destroys the icon.
+*/
+QIcon::~QIcon()
+{
+ if (d && !d->ref.deref())
+ delete d;
+}
+
+/*!
+ Assigns the \a other icon to this icon and returns a reference to
+ this icon.
+*/
+QIcon &QIcon::operator=(const QIcon &other)
+{
+ if (other.d)
+ other.d->ref.ref();
+ if (d && !d->ref.deref())
+ delete d;
+ d = other.d;
+ return *this;
+}
+
+/*!
+ Returns the icon as a QVariant.
+*/
+QIcon::operator QVariant() const
+{
+ return QVariant(QVariant::Icon, this);
+}
+
+/*! \obsolete
+
+ Returns a number that identifies the contents of this
+ QIcon object. Distinct QIcon objects can have
+ the same serial number if they refer to the same contents
+ (but they don't have to). Also, the serial number of
+ a QIcon object may change during its lifetime.
+
+ Use cacheKey() instead.
+
+ A null icon always has a serial number of 0.
+
+ Serial numbers are mostly useful in conjunction with caching.
+
+ \sa QPixmap::serialNumber()
+*/
+
+int QIcon::serialNumber() const
+{
+ return d ? d->serialNum : 0;
+}
+
+/*!
+ Returns a number that identifies the contents of this QIcon
+ object. Distinct QIcon objects can have the same key if
+ they refer to the same contents.
+ \since 4.3
+
+ The cacheKey() will change when the icon is altered via
+ addPixmap() or addFile().
+
+ Cache keys are mostly useful in conjunction with caching.
+
+ \sa QPixmap::cacheKey()
+*/
+qint64 QIcon::cacheKey() const
+{
+ if (!d)
+ return 0;
+ return (((qint64) d->serialNum) << 32) | ((qint64) (d->detach_no));
+}
+
+/*!
+ Returns a pixmap with the requested \a size, \a mode, and \a
+ state, generating one if necessary. The pixmap might be smaller than
+ requested, but never larger.
+
+ \sa actualSize(), paint()
+*/
+QPixmap QIcon::pixmap(const QSize &size, Mode mode, State state) const
+{
+ if (!d)
+ return QPixmap();
+ return d->engine->pixmap(size, mode, state);
+}
+
+/*!
+ \fn QPixmap QIcon::pixmap(int w, int h, Mode mode = Normal, State state = Off) const
+
+ \overload
+
+ Returns a pixmap of size QSize(\a w, \a h). The pixmap might be smaller than
+ requested, but never larger.
+*/
+
+/*!
+ \fn QPixmap QIcon::pixmap(int extent, Mode mode = Normal, State state = Off) const
+
+ \overload
+
+ Returns a pixmap of size QSize(\a extent, \a extent). The pixmap might be smaller
+ than requested, but never larger.
+*/
+
+/*! Returns the actual size of the icon for the requested \a size, \a
+ mode, and \a state. The result might be smaller than requested, but
+ never larger.
+
+ \sa pixmap(), paint()
+*/
+QSize QIcon::actualSize(const QSize &size, Mode mode, State state) const
+{
+ if (!d)
+ return QSize();
+ return d->engine->actualSize(size, mode, state);
+}
+
+
+/*!
+ Uses the \a painter to paint the icon with specified \a alignment,
+ required \a mode, and \a state into the rectangle \a rect.
+
+ \sa actualSize(), pixmap()
+*/
+void QIcon::paint(QPainter *painter, const QRect &rect, Qt::Alignment alignment, Mode mode, State state) const
+{
+ if (!d || !painter)
+ return;
+ QRect alignedRect = QStyle::alignedRect(painter->layoutDirection(), alignment, d->engine->actualSize(rect.size(), mode, state), rect);
+ d->engine->paint(painter, alignedRect, mode, state);
+}
+
+/*!
+ \fn void QIcon::paint(QPainter *painter, int x, int y, int w, int h, Qt::Alignment alignment,
+ Mode mode, State state) const
+
+ \overload
+
+ Paints the icon into the rectangle QRect(\a x, \a y, \a w, \a h).
+*/
+
+/*!
+ Returns true if the icon is empty; otherwise returns false.
+
+ An icon is empty if it has neither a pixmap nor a filename.
+
+ Note: Even a non-null icon might not be able to create valid
+ pixmaps, eg. if the file does not exist or cannot be read.
+*/
+bool QIcon::isNull() const
+{
+ return !d;
+}
+
+/*!\internal
+ */
+bool QIcon::isDetached() const
+{
+ return !d || d->ref == 1;
+}
+
+/*! \internal
+ */
+void QIcon::detach()
+{
+ if (d) {
+ if (d->ref != 1) {
+ QIconPrivate *x = new QIconPrivate;
+ if (d->engine_version > 1) {
+ QIconEngineV2 *engine = static_cast<QIconEngineV2 *>(d->engine);
+ x->engine = engine->clone();
+ } else {
+ x->engine = d->engine;
+ x->v1RefCount = d->v1RefCount;
+ x->v1RefCount->ref();
+ }
+ x->engine_version = d->engine_version;
+ if (!d->ref.deref())
+ delete d;
+ d = x;
+ }
+ ++d->detach_no;
+ }
+}
+
+/*!
+ Adds \a pixmap to the icon, as a specialization for \a mode and
+ \a state.
+
+ Custom icon engines are free to ignore additionally added
+ pixmaps.
+
+ \sa addFile()
+*/
+void QIcon::addPixmap(const QPixmap &pixmap, Mode mode, State state)
+{
+ if (pixmap.isNull())
+ return;
+ if (!d) {
+ d = new QIconPrivate;
+ d->engine = new QPixmapIconEngine;
+ } else {
+ detach();
+ }
+ d->engine->addPixmap(pixmap, mode, state);
+}
+
+
+/*! Adds an image from the file with the given \a fileName to the
+ icon, as a specialization for \a size, \a mode and \a state. The
+ file will be loaded on demand. Note: custom icon engines are free
+ to ignore additionally added pixmaps.
+
+ If \a fileName contains a relative path (e.g. the filename only)
+ the relevant file must be found relative to the runtime working
+ directory.
+
+ The file name can be either refer to an actual file on disk or to
+ one of the application's embedded resources. See the
+ \l{resources.html}{Resource System} overview for details on how to
+ embed images and other resource files in the application's
+ executable.
+
+ Use the QImageReader::supportedImageFormats() and
+ QImageWriter::supportedImageFormats() functions to retrieve a
+ complete list of the supported file formats.
+
+ \sa addPixmap()
+ */
+void QIcon::addFile(const QString &fileName, const QSize &size, Mode mode, State state)
+{
+ if (fileName.isEmpty())
+ return;
+ if (!d) {
+#if !defined (QT_NO_LIBRARY) && !defined(QT_NO_SETTINGS)
+ QFileInfo info(fileName);
+ QString suffix = info.suffix();
+ if (!suffix.isEmpty()) {
+ // first try version 2 engines..
+ if (QIconEngineFactoryInterfaceV2 *factory = qobject_cast<QIconEngineFactoryInterfaceV2*>(loaderV2()->instance(suffix))) {
+ if (QIconEngine *engine = factory->create(fileName)) {
+ d = new QIconPrivate;
+ d->engine = engine;
+ }
+ }
+ // ..then fall back and try to load version 1 engines
+ if (!d) {
+ if (QIconEngineFactoryInterface *factory = qobject_cast<QIconEngineFactoryInterface*>(loader()->instance(suffix))) {
+ if (QIconEngine *engine = factory->create(fileName)) {
+ d = new QIconPrivate;
+ d->engine = engine;
+ d->engine_version = 1;
+ d->v1RefCount = new QAtomicInt(1);
+ }
+ }
+ }
+ }
+#endif
+ // ...then fall back to the default engine
+ if (!d) {
+ d = new QIconPrivate;
+ d->engine = new QPixmapIconEngine;
+ }
+ } else {
+ detach();
+ }
+ d->engine->addFile(fileName, size, mode, state);
+}
+
+/*!
+ \since 4.5
+
+ Returns a list of available icon sizes for the specified \a mode and
+ \a state.
+*/
+QList<QSize> QIcon::availableSizes(Mode mode, State state) const
+{
+ if (!d || !d->engine || d->engine_version < 2)
+ return QList<QSize>();
+ QIconEngineV2 *engine = static_cast<QIconEngineV2*>(d->engine);
+ return engine->availableSizes(mode, state);
+}
+
+/*****************************************************************************
+ QIcon stream functions
+ *****************************************************************************/
+#if !defined(QT_NO_DATASTREAM)
+/*!
+ \fn QDataStream &operator<<(QDataStream &stream, const QIcon &icon)
+ \relates QIcon
+ \since 4.2
+
+ Writes the given \a icon to the the given \a stream as a PNG
+ image. If the icon contains more than one image, all images will
+ be written to the stream. Note that writing the stream to a file
+ will not produce a valid image file.
+*/
+
+QDataStream &operator<<(QDataStream &s, const QIcon &icon)
+{
+ if (s.version() >= QDataStream::Qt_4_3) {
+ if (icon.isNull()) {
+ s << QString();
+ } else {
+ if (icon.d->engine_version > 1) {
+ QIconEngineV2 *engine = static_cast<QIconEngineV2 *>(icon.d->engine);
+ s << engine->key();
+ engine->write(s);
+ } else {
+ // not really supported
+ qWarning("QIcon: Cannot stream QIconEngine. Use QIconEngineV2 instead.");
+ }
+ }
+ } else if (s.version() == QDataStream::Qt_4_2) {
+ if (icon.isNull()) {
+ s << 0;
+ } else {
+ QPixmapIconEngine *engine = static_cast<QPixmapIconEngine *>(icon.d->engine);
+ int num_entries = engine->pixmaps.size();
+ s << num_entries;
+ for (int i=0; i < num_entries; ++i) {
+ s << engine->pixmaps.at(i).pixmap;
+ s << engine->pixmaps.at(i).fileName;
+ s << engine->pixmaps.at(i).size;
+ s << (uint) engine->pixmaps.at(i).mode;
+ s << (uint) engine->pixmaps.at(i).state;
+ }
+ }
+ } else {
+ s << QPixmap(icon.pixmap(22,22));
+ }
+ return s;
+}
+
+/*!
+ \fn QDataStream &operator>>(QDataStream &stream, QIcon &icon)
+ \relates QIcon
+ \since 4.2
+
+ Reads an image, or a set of images, from the given \a stream into
+ the given \a icon.
+*/
+
+QDataStream &operator>>(QDataStream &s, QIcon &icon)
+{
+ if (s.version() >= QDataStream::Qt_4_3) {
+ icon = QIcon();
+ QString key;
+ s >> key;
+ if (key == QLatin1String("QPixmapIconEngine")) {
+ icon.d = new QIconPrivate;
+ QIconEngineV2 *engine = new QPixmapIconEngine;
+ icon.d->engine = engine;
+ engine->read(s);
+#if !defined (QT_NO_LIBRARY) && !defined(QT_NO_SETTINGS)
+ } else if (QIconEngineFactoryInterfaceV2 *factory = qobject_cast<QIconEngineFactoryInterfaceV2*>(loaderV2()->instance(key))) {
+ if (QIconEngineV2 *engine= factory->create()) {
+ icon.d = new QIconPrivate;
+ icon.d->engine = engine;
+ engine->read(s);
+ }
+#endif
+ }
+ } else if (s.version() == QDataStream::Qt_4_2) {
+ icon = QIcon();
+ int num_entries;
+ QPixmap pm;
+ QString fileName;
+ QSize sz;
+ uint mode;
+ uint state;
+
+ s >> num_entries;
+ for (int i=0; i < num_entries; ++i) {
+ s >> pm;
+ s >> fileName;
+ s >> sz;
+ s >> mode;
+ s >> state;
+ if (pm.isNull())
+ icon.addFile(fileName, sz, QIcon::Mode(mode), QIcon::State(state));
+ else
+ icon.addPixmap(pm, QIcon::Mode(mode), QIcon::State(state));
+ }
+ } else {
+ QPixmap pm;
+ s >> pm;
+ icon.addPixmap(pm);
+ }
+ return s;
+}
+
+#endif //QT_NO_DATASTREAM
+
+
+#ifdef QT3_SUPPORT
+
+static int widths[2] = { 22, 32 };
+static int heights[2] = { 22, 32 };
+
+static QSize pixmapSizeHelper(QIcon::Size which)
+{
+ int i = 0;
+ if (which == QIcon::Large)
+ i = 1;
+ return QSize(widths[i], heights[i]);
+}
+
+/*!
+ \enum QIcon::Size
+ \compat
+
+ \value Small Use QStyle::pixelMetric(QStyle::PM_SmallIconSize) instead.
+ \value Large Use QStyle::pixelMetric(QStyle::PM_LargeIconSize) instead.
+ \value Automatic N/A.
+*/
+
+/*!
+ Use pixmap(QSize(...), \a mode, \a state), where the first
+ argument is an appropriate QSize instead of a \l Size value.
+
+ \sa pixmapSize()
+*/
+QPixmap QIcon::pixmap(Size size, Mode mode, State state) const
+{ return pixmap(pixmapSizeHelper(size), mode, state); }
+
+/*!
+ Use pixmap(QSize(...), mode, \a state), where the first argument
+ is an appropriate QSize instead of a \l Size value, and the
+ second argument is QIcon::Normal or QIcon::Disabled, depending on
+ the value of \a enabled.
+
+ \sa pixmapSize()
+*/
+QPixmap QIcon::pixmap(Size size, bool enabled, State state) const
+{ return pixmap(pixmapSizeHelper(size), enabled ? Normal : Disabled, state); }
+
+/*!
+ Use one of the other pixmap() overloads.
+*/
+QPixmap QIcon::pixmap() const
+{ return pixmap(pixmapSizeHelper(Small), Normal, Off); }
+
+/*!
+ The pixmap() function now takes a QSize instead of a QIcon::Size,
+ so there is no need for this function in new code.
+*/
+void QIcon::setPixmapSize(Size which, const QSize &size)
+{
+ int i = 0;
+ if (which == Large)
+ i = 1;
+ widths[i] = size.width();
+ heights[i] = size.height();
+}
+
+/*!
+ Use QStyle::pixelMetric() with QStyle::PM_SmallIconSize or
+ QStyle::PM_LargeIconSize as the first argument, depending on \a
+ which.
+*/
+QSize QIcon::pixmapSize(Size which)
+{
+ return pixmapSizeHelper(which);
+}
+
+/*!
+ \fn void QIcon::reset(const QPixmap &pixmap, Size size)
+
+ Use the constructor that takes a QPixmap and operator=().
+*/
+
+/*!
+ \fn void QIcon::setPixmap(const QPixmap &pixmap, Size size, Mode mode, State state)
+
+ Use addPixmap(\a pixmap, \a mode, \a state) instead. The \a size
+ parameter is ignored.
+*/
+
+/*!
+ \fn void QIcon::setPixmap(const QString &fileName, Size size, Mode mode, State state)
+
+ Use addFile(\a fileName, \a mode, \a state) instead. The \a size
+ parameter is ignored.
+*/
+
+#endif // QT3_SUPPORT
+
+/*!
+ \fn DataPtr &QIcon::data_ptr()
+ \internal
+*/
+
+/*!
+ \typedef QIcon::DataPtr
+ \internal
+*/
+
+QT_END_NAMESPACE
diff --git a/src/gui/image/qicon.h b/src/gui/image/qicon.h
new file mode 100644
index 0000000000..5a606d4c07
--- /dev/null
+++ b/src/gui/image/qicon.h
@@ -0,0 +1,144 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QICON_H
+#define QICON_H
+
+#include <QtCore/qglobal.h>
+#include <QtCore/qsize.h>
+#include <QtCore/qlist.h>
+#include <QtGui/qpixmap.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+class QIconPrivate;
+class QIconEngine;
+class QIconEngineV2;
+
+class Q_GUI_EXPORT QIcon
+{
+public:
+ enum Mode { Normal, Disabled, Active, Selected };
+ enum State { On, Off };
+
+ QIcon();
+ QIcon(const QPixmap &pixmap);
+ QIcon(const QIcon &other);
+ explicit QIcon(const QString &fileName); // file or resource name
+ explicit QIcon(QIconEngine *engine);
+ explicit QIcon(QIconEngineV2 *engine);
+ ~QIcon();
+ QIcon &operator=(const QIcon &other);
+ operator QVariant() const;
+
+ QPixmap pixmap(const QSize &size, Mode mode = Normal, State state = Off) const;
+ inline QPixmap pixmap(int w, int h, Mode mode = Normal, State state = Off) const
+ { return pixmap(QSize(w, h), mode, state); }
+ inline QPixmap pixmap(int extent, Mode mode = Normal, State state = Off) const
+ { return pixmap(QSize(extent, extent), mode, state); }
+
+ QSize actualSize(const QSize &size, Mode mode = Normal, State state = Off) const;
+
+ void paint(QPainter *painter, const QRect &rect, Qt::Alignment alignment = Qt::AlignCenter, Mode mode = Normal, State state = Off) const;
+ inline void paint(QPainter *painter, int x, int y, int w, int h, Qt::Alignment alignment = Qt::AlignCenter, Mode mode = Normal, State state = Off) const
+ { paint(painter, QRect(x, y, w, h), alignment, mode, state); }
+
+ bool isNull() const;
+ bool isDetached() const;
+ void detach();
+
+ int serialNumber() const;
+ qint64 cacheKey() const;
+
+ void addPixmap(const QPixmap &pixmap, Mode mode = Normal, State state = Off);
+ void addFile(const QString &fileName, const QSize &size = QSize(), Mode mode = Normal, State state = Off);
+
+ QList<QSize> availableSizes(Mode mode = Normal, State state = Off) const;
+
+#ifdef QT3_SUPPORT
+ enum Size { Small, Large, Automatic = Small };
+ static QT3_SUPPORT void setPixmapSize(Size which, const QSize &size);
+ static QT3_SUPPORT QSize pixmapSize(Size which);
+ inline QT3_SUPPORT void reset(const QPixmap &pixmap, Size /*size*/) { *this = QIcon(pixmap); }
+ inline QT3_SUPPORT void setPixmap(const QPixmap &pixmap, Size, Mode mode = Normal, State state = Off)
+ { addPixmap(pixmap, mode, state); }
+ inline QT3_SUPPORT void setPixmap(const QString &fileName, Size, Mode mode = Normal, State state = Off)
+ { addPixmap(QPixmap(fileName), mode, state); }
+ QT3_SUPPORT QPixmap pixmap(Size size, Mode mode, State state = Off) const;
+ QT3_SUPPORT QPixmap pixmap(Size size, bool enabled, State state = Off) const;
+ QT3_SUPPORT QPixmap pixmap() const;
+#endif
+
+ Q_DUMMY_COMPARISON_OPERATOR(QIcon)
+
+private:
+ QIconPrivate *d;
+#if !defined(QT_NO_DATASTREAM)
+ friend Q_GUI_EXPORT QDataStream &operator<<(QDataStream &, const QIcon &);
+ friend Q_GUI_EXPORT QDataStream &operator>>(QDataStream &, QIcon &);
+#endif
+
+public:
+ typedef QIconPrivate * DataPtr;
+ inline DataPtr &data_ptr() { return d; }
+};
+
+Q_DECLARE_SHARED(QIcon)
+Q_DECLARE_TYPEINFO(QIcon, Q_MOVABLE_TYPE);
+
+#if !defined(QT_NO_DATASTREAM)
+Q_GUI_EXPORT QDataStream &operator<<(QDataStream &, const QIcon &);
+Q_GUI_EXPORT QDataStream &operator>>(QDataStream &, QIcon &);
+#endif
+
+#ifdef QT3_SUPPORT
+typedef QIcon QIconSet;
+#endif
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QICON_H
diff --git a/src/gui/image/qiconengine.cpp b/src/gui/image/qiconengine.cpp
new file mode 100644
index 0000000000..866a82e506
--- /dev/null
+++ b/src/gui/image/qiconengine.cpp
@@ -0,0 +1,304 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qiconengine.h"
+#include "qpainter.h"
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QIconEngine
+
+ \brief The QIconEngine class provides an abstract base class for QIcon renderers.
+
+ \ingroup multimedia
+
+ \bold {Use QIconEngineV2 instead.}
+
+ An icon engine provides the rendering functions for a QIcon. Each icon has a
+ corresponding icon engine that is responsible for drawing the icon with a
+ requested size, mode and state.
+
+ The icon is rendered by the paint() function, and the icon can additionally be
+ obtained as a pixmap with the pixmap() function (the default implementation
+ simply uses paint() to achieve this). The addPixmap() function can be used to
+ add new pixmaps to the icon engine, and is used by QIcon to add specialized
+ custom pixmaps.
+
+ The paint(), pixmap(), and addPixmap() functions are all virtual, and can
+ therefore be reimplemented in subclasses of QIconEngine.
+
+ \sa QIconEngineV2, QIconEnginePlugin
+
+*/
+
+/*!
+ \fn virtual void QIconEngine::paint(QPainter *painter, const QRect &rect, QIcon::Mode mode, QIcon::State state) = 0;
+
+ Uses the given \a painter to paint the icon with the required \a mode and
+ \a state into the rectangle \a rect.
+*/
+
+/*! Returns the actual size of the icon the engine provides for the
+ requested \a size, \a mode and \a state. The default implementation
+ returns the given \a size.
+ */
+QSize QIconEngine::actualSize(const QSize &size, QIcon::Mode /*mode*/, QIcon::State /*state*/)
+{
+ return size;
+}
+
+
+/*!
+ Destroys the icon engine.
+ */
+QIconEngine::~QIconEngine()
+{
+}
+
+
+/*!
+ Returns the icon as a pixmap with the required \a size, \a mode,
+ and \a state. The default implementation creates a new pixmap and
+ calls paint() to fill it.
+*/
+QPixmap QIconEngine::pixmap(const QSize &size, QIcon::Mode mode, QIcon::State state)
+{
+ QPixmap pm(size);
+ {
+ QPainter p(&pm);
+ paint(&p, QRect(QPoint(0,0),size), mode, state);
+ }
+ return pm;
+}
+
+/*!
+ Called by QIcon::addPixmap(). Adds a specialized \a pixmap for the given
+ \a mode and \a state. The default pixmap-based engine stores any supplied
+ pixmaps, and it uses them instead of scaled pixmaps if the size of a pixmap
+ matches the size of icon requested. Custom icon engines that implement
+ scalable vector formats are free to ignores any extra pixmaps.
+ */
+void QIconEngine::addPixmap(const QPixmap &/*pixmap*/, QIcon::Mode /*mode*/, QIcon::State /*state*/)
+{
+}
+
+
+/*! Called by QIcon::addFile(). Adds a specialized pixmap from the
+ file with the given \a fileName, \a size, \a mode and \a state. The
+ default pixmap-based engine stores any supplied file names, and it
+ loads the pixmaps on demand instead of using scaled pixmaps if the
+ size of a pixmap matches the size of icon requested. Custom icon
+ engines that implement scalable vector formats are free to ignores
+ any extra files.
+ */
+void QIconEngine::addFile(const QString &/*fileName*/, const QSize &/*size*/, QIcon::Mode /*mode*/, QIcon::State /*state*/)
+{
+}
+
+
+
+// version 2 functions
+
+
+/*!
+ \class QIconEngineV2
+
+ \brief The QIconEngineV2 class provides an abstract base class for QIcon renderers.
+
+ \ingroup multimedia
+ \since 4.3
+
+ An icon engine renders \l{QIcon}s. With icon engines, you can
+ customize icons. Qt provides a default engine that makes icons
+ adhere to the current style by scaling the icons and providing a
+ disabled appearance.
+
+ An engine is installed on an icon either through a QIcon
+ constructor or through a QIconEnginePluginV2. The plugins are used
+ by Qt if a specific engine is not given when the icon is created.
+ See the QIconEngineV2 class description to learn how to create
+ icon engine plugins.
+
+ An icon engine provides the rendering functions for a QIcon. Each
+ icon has a corresponding icon engine that is responsible for drawing
+ the icon with a requested size, mode and state.
+
+ QIconEngineV2 extends the API of QIconEngine to allow streaming of
+ the icon engine contents, and should be used instead of QIconEngine
+ for implementing new icon engines.
+
+ \sa QIconEnginePluginV2
+
+*/
+
+/*!
+ \enum QIconEngineV2::IconEngineHook
+ \since 4.5
+
+ These enum values are used for virtual_hook() to allow additional
+ queries to icon engine without breaking binary compatibility.
+
+ \value AvailableSizesHook Allows to query the sizes of the
+ contained pixmaps for pixmap-based engines. The \a data argument
+ of the virtual_hook() function is a AvailableSizesArgument pointer
+ that should be filled with icon sizes. Engines that work in terms
+ of a scalable, vectorial format normally return an empty list.
+
+ \sa virtual_hook()
+ */
+
+/*!
+ \class QIconEngineV2::AvailableSizesArgument
+ \since 4.5
+
+ This struct represents arguments to virtual_hook() function when
+ \a id parameter is QIconEngineV2::AvailableSizesHook.
+
+ \sa virtual_hook(), QIconEngineV2::IconEngineHook
+ */
+
+/*!
+ \variable QIconEngineV2::AvailableSizesArgument::mode
+ \brief the requested mode of an image.
+
+ \sa QIcon::Mode
+*/
+
+/*!
+ \variable QIconEngineV2::AvailableSizesArgument::state
+ \brief the requested state of an image.
+
+ \sa QIcon::State
+*/
+
+/*!
+ \variable QIconEngineV2::AvailableSizesArgument::sizes
+
+ \brief image sizes that are available with specified \a mode and
+ \a state. This is an output parameter and is filled after call to
+ virtual_hook(). Engines that work in terms of a scalable,
+ vectorial format normally return an empty list.
+*/
+
+
+/*!
+ Returns a key that identifies this icon engine.
+ */
+QString QIconEngineV2::key() const
+{
+ return QString();
+}
+
+/*!
+ Returns a clone of this icon engine.
+ */
+QIconEngineV2 *QIconEngineV2::clone() const
+{
+ return 0;
+}
+
+/*!
+ Reads icon engine contents from the QDataStream \a in. Returns
+ true if the contents were read; otherwise returns false.
+
+ QIconEngineV2's default implementation always return false.
+ */
+bool QIconEngineV2::read(QDataStream &)
+{
+ return false;
+}
+
+/*!
+ Writes the contents of this engine to the QDataStream \a out.
+ Returns true if the contents were written; otherwise returns false.
+
+ QIconEngineV2's default implementation always return false.
+ */
+bool QIconEngineV2::write(QDataStream &) const
+{
+ return false;
+}
+
+/*!
+ \since 4.5
+
+ Additional method to allow extending QIconEngineV2 without
+ adding new virtual methods (and without breaking binary compatibility).
+ The actual action and format of \a data depends on \a id argument
+ which is in fact a constant from IconEngineHook enum.
+
+ \sa IconEngineHook
+*/
+void QIconEngineV2::virtual_hook(int id, void *data)
+{
+ switch (id) {
+ case QIconEngineV2::AvailableSizesHook: {
+ QIconEngineV2::AvailableSizesArgument &arg =
+ *reinterpret_cast<QIconEngineV2::AvailableSizesArgument*>(data);
+ arg.sizes.clear();
+ break;
+ }
+ default:
+ break;
+ }
+}
+
+/*!
+ \since 4.5
+
+ Returns sizes of all images that are contained in the engine for the
+ specific \a mode and \a state.
+
+ \note This is a helper method and the actual work is done by
+ virtual_hook() method, hence this method depends on icon engine support
+ and may not work with all icon engines.
+ */
+QList<QSize> QIconEngineV2::availableSizes(QIcon::Mode mode, QIcon::State state)
+{
+ AvailableSizesArgument arg;
+ arg.mode = mode;
+ arg.state = state;
+ virtual_hook(QIconEngineV2::AvailableSizesHook, reinterpret_cast<void*>(&arg));
+ return arg.sizes;
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/image/qiconengine.h b/src/gui/image/qiconengine.h
new file mode 100644
index 0000000000..71c89277f6
--- /dev/null
+++ b/src/gui/image/qiconengine.h
@@ -0,0 +1,101 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QICONENGINE_H
+#define QICONENGINE_H
+
+#include <QtCore/qglobal.h>
+#include <QtCore/qlist.h>
+#include <QtGui/qicon.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+class Q_GUI_EXPORT QIconEngine
+{
+public:
+ virtual ~QIconEngine();
+ virtual void paint(QPainter *painter, const QRect &rect, QIcon::Mode mode, QIcon::State state) = 0;
+ virtual QSize actualSize(const QSize &size, QIcon::Mode mode, QIcon::State state);
+ virtual QPixmap pixmap(const QSize &size, QIcon::Mode mode, QIcon::State state);
+
+ virtual void addPixmap(const QPixmap &pixmap, QIcon::Mode mode, QIcon::State state);
+ virtual void addFile(const QString &fileName, const QSize &size, QIcon::Mode mode, QIcon::State state);
+
+#if 0
+ virtual int frameCount(QIcon::Mode fromMode, QIcon::State fromState, QIcon::Mode toMode, QIcon::State toState);
+ virtual void paintFrame(QPainter *painter, const QRect &rect, int frameNumber, QIcon::Mode fromMode, QIcon::State fromState, QIcon::Mode toMode, QIcon::State toState);
+#endif
+};
+
+// ### Qt 5: move the below into QIconEngine
+class Q_GUI_EXPORT QIconEngineV2 : public QIconEngine
+{
+public:
+ virtual QString key() const;
+ virtual QIconEngineV2 *clone() const;
+ virtual bool read(QDataStream &in);
+ virtual bool write(QDataStream &out) const;
+ virtual void virtual_hook(int id, void *data);
+
+public:
+ enum IconEngineHook { AvailableSizesHook = 1 };
+
+ struct AvailableSizesArgument
+ {
+ QIcon::Mode mode;
+ QIcon::State state;
+ QList<QSize> sizes;
+ };
+
+ // ### Qt 5: make this function const and virtual.
+ QList<QSize> availableSizes(QIcon::Mode mode = QIcon::Normal,
+ QIcon::State state = QIcon::Off);
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QICONENGINE_H
diff --git a/src/gui/image/qiconengineplugin.cpp b/src/gui/image/qiconengineplugin.cpp
new file mode 100644
index 0000000000..22f62cf254
--- /dev/null
+++ b/src/gui/image/qiconengineplugin.cpp
@@ -0,0 +1,171 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qiconengineplugin.h"
+#include "qiconengine.h"
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QIconEnginePlugin
+ \brief The QIconEnginePlugin class provides an abstract base for custom QIconEngine plugins.
+
+ \ingroup plugins
+
+ \bold {Use QIconEnginePluginV2 instead.}
+
+ The icon engine plugin is a simple plugin interface that makes it easy to
+ create custom icon engines that can be loaded dynamically into applications
+ through QIcon. QIcon uses the file or resource name's suffix to determine
+ what icon engine to use.
+
+ Writing a icon engine plugin is achieved by subclassing this base class,
+ reimplementing the pure virtual functions keys() and create(), and
+ exporting the class with the Q_EXPORT_PLUGIN2() macro.
+
+ \sa {How to Create Qt Plugins}
+*/
+
+/*!
+ \fn QStringList QIconEnginePlugin::keys() const
+
+ Returns a list of icon engine keys that this plugin supports. The keys correspond
+ to the suffix of the file or resource name used when the plugin was created.
+ Keys are case insensitive.
+
+ \sa create()
+*/
+
+/*!
+ \fn QIconEngine* QIconEnginePlugin::create(const QString& filename)
+
+ Creates and returns a QIconEngine object for the icon with the given
+ \a filename.
+
+ \sa keys()
+*/
+
+/*!
+ Constructs a icon engine plugin with the given \a parent. This is invoked
+ automatically by the Q_EXPORT_PLUGIN2() macro.
+*/
+QIconEnginePlugin::QIconEnginePlugin(QObject *parent)
+ : QObject(parent)
+{
+}
+
+/*!
+ Destroys the icon engine plugin.
+
+ You never have to call this explicitly. Qt destroys a plugin
+ automatically when it is no longer used.
+*/
+QIconEnginePlugin::~QIconEnginePlugin()
+{
+}
+
+// version 2
+
+/*!
+ \class QIconEnginePluginV2
+ \brief The QIconEnginePluginV2 class provides an abstract base for custom QIconEngineV2 plugins.
+
+ \ingroup plugins
+ \since 4.3
+
+ Icon engine plugins produces \l{QIconEngine}s for \l{QIcon}s; an
+ icon engine is used to render the icon. The keys that identifies
+ the engines the plugin can create are suffixes of
+ icon filenames; they are returned by keys(). The create() function
+ receives the icon filename to return an engine for; it should
+ return 0 if it cannot produce an engine for the file.
+
+ Writing an icon engine plugin is achieved by inheriting
+ QIconEnginePluginV2, reimplementing keys() and create(), and
+ adding the Q_EXPORT_PLUGIN2() macro.
+
+ You should ensure that you do not duplicate keys. Qt will query
+ the plugins for icon engines in the order in which the plugins are
+ found during plugin search (see the plugins \l{How to Create Qt
+ Plugins}{overview document}).
+
+ \sa {How to Create Qt Plugins}
+*/
+
+/*!
+ \fn QStringList QIconEnginePluginV2::keys() const
+
+ Returns a list of icon engine keys that this plugin supports. The keys correspond
+ to the suffix of the file or resource name used when the plugin was created.
+ Keys are case insensitive.
+
+ \sa create()
+*/
+
+/*!
+ \fn QIconEngineV2* QIconEnginePluginV2::create(const QString& filename = QString())
+
+ Creates and returns a QIconEngine object for the icon with the given
+ \a filename.
+
+ \sa keys()
+*/
+
+/*!
+ Constructs a icon engine plugin with the given \a parent. This is invoked
+ automatically by the Q_EXPORT_PLUGIN2() macro.
+*/
+QIconEnginePluginV2::QIconEnginePluginV2(QObject *parent)
+ : QObject(parent)
+{
+}
+
+/*!
+ Destroys the icon engine plugin.
+
+ You never have to call this explicitly. Qt destroys a plugin
+ automatically when it is no longer used.
+*/
+QIconEnginePluginV2::~QIconEnginePluginV2()
+{
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/image/qiconengineplugin.h b/src/gui/image/qiconengineplugin.h
new file mode 100644
index 0000000000..5c7f8b54bc
--- /dev/null
+++ b/src/gui/image/qiconengineplugin.h
@@ -0,0 +1,104 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QICONENGINEPLUGIN_H
+#define QICONENGINEPLUGIN_H
+
+#include <QtCore/qplugin.h>
+#include <QtCore/qfactoryinterface.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+class QIconEngine;
+class QIconEngineV2;
+
+struct Q_GUI_EXPORT QIconEngineFactoryInterface : public QFactoryInterface
+{
+ virtual QIconEngine *create(const QString &filename) = 0;
+};
+
+#define QIconEngineFactoryInterface_iid \
+ "com.trolltech.Qt.QIconEngineFactoryInterface"
+Q_DECLARE_INTERFACE(QIconEngineFactoryInterface, QIconEngineFactoryInterface_iid)
+
+class Q_GUI_EXPORT QIconEnginePlugin : public QObject, public QIconEngineFactoryInterface
+{
+ Q_OBJECT
+ Q_INTERFACES(QIconEngineFactoryInterface:QFactoryInterface)
+public:
+ QIconEnginePlugin(QObject *parent = 0);
+ ~QIconEnginePlugin();
+
+ virtual QStringList keys() const = 0;
+ virtual QIconEngine *create(const QString &filename) = 0;
+};
+
+// ### Qt 5: remove version 2
+struct Q_GUI_EXPORT QIconEngineFactoryInterfaceV2 : public QFactoryInterface
+{
+ virtual QIconEngineV2 *create(const QString &filename = QString()) = 0;
+};
+
+#define QIconEngineFactoryInterfaceV2_iid \
+ "com.trolltech.Qt.QIconEngineFactoryInterfaceV2"
+Q_DECLARE_INTERFACE(QIconEngineFactoryInterfaceV2, QIconEngineFactoryInterfaceV2_iid)
+
+class Q_GUI_EXPORT QIconEnginePluginV2 : public QObject, public QIconEngineFactoryInterfaceV2
+{
+ Q_OBJECT
+ Q_INTERFACES(QIconEngineFactoryInterfaceV2:QFactoryInterface)
+public:
+ QIconEnginePluginV2(QObject *parent = 0);
+ ~QIconEnginePluginV2();
+
+ virtual QStringList keys() const = 0;
+ virtual QIconEngineV2 *create(const QString &filename = QString()) = 0;
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QICONENGINEPLUGIN_H
diff --git a/src/gui/image/qimage.cpp b/src/gui/image/qimage.cpp
new file mode 100644
index 0000000000..558d5745f2
--- /dev/null
+++ b/src/gui/image/qimage.cpp
@@ -0,0 +1,6119 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qimage.h"
+#include "qdatastream.h"
+#include "qbuffer.h"
+#include "qmap.h"
+#include "qmatrix.h"
+#include "qtransform.h"
+#include "qimagereader.h"
+#include "qimagewriter.h"
+#include "qstringlist.h"
+#include "qvariant.h"
+#include <ctype.h>
+#include <stdlib.h>
+#include <limits.h>
+#include <math.h>
+#include <private/qdrawhelper_p.h>
+#include <private/qmemrotate_p.h>
+#include <private/qpixmapdata_p.h>
+#include <private/qimagescale_p.h>
+
+#include <qhash.h>
+
+#ifdef QT_RASTER_IMAGEENGINE
+#include <private/qpaintengine_raster_p.h>
+#else
+#include <qpaintengine.h>
+#endif
+
+#include <private/qimage_p.h>
+
+QT_BEGIN_NAMESPACE
+
+static inline bool checkPixelSize(const QImage::Format format)
+{
+ switch (format) {
+ case QImage::Format_ARGB8565_Premultiplied:
+ return (sizeof(qargb8565) == 3);
+ case QImage::Format_RGB666:
+ return (sizeof(qrgb666) == 3);
+ case QImage::Format_ARGB6666_Premultiplied:
+ return (sizeof(qargb6666) == 3);
+ case QImage::Format_RGB555:
+ return (sizeof(qrgb555) == 2);
+ case QImage::Format_ARGB8555_Premultiplied:
+ return (sizeof(qargb8555) == 3);
+ case QImage::Format_RGB888:
+ return (sizeof(qrgb888) == 3);
+ case QImage::Format_RGB444:
+ return (sizeof(qrgb444) == 2);
+ case QImage::Format_ARGB4444_Premultiplied:
+ return (sizeof(qargb4444) == 2);
+ default:
+ return true;
+ }
+}
+
+#if defined(Q_CC_DEC) && defined(__alpha) && (__DECCXX_VER-0 >= 50190001)
+#pragma message disable narrowptr
+#endif
+
+
+#define QIMAGE_SANITYCHECK_MEMORY(image) \
+ if ((image).isNull()) { \
+ qWarning("QImage: out of memory, returning null image"); \
+ return QImage(); \
+ }
+
+
+// ### Qt 5: remove
+typedef void (*_qt_image_cleanup_hook)(int);
+Q_GUI_EXPORT _qt_image_cleanup_hook qt_image_cleanup_hook = 0;
+
+// ### Qt 5: rename
+typedef void (*_qt_image_cleanup_hook_64)(qint64);
+Q_GUI_EXPORT _qt_image_cleanup_hook_64 qt_image_cleanup_hook_64 = 0;
+
+static QImage rotated90(const QImage &src);
+static QImage rotated180(const QImage &src);
+static QImage rotated270(const QImage &src);
+
+// ### Qt 5: remove
+Q_GUI_EXPORT qint64 qt_image_id(const QImage &image)
+{
+ return image.cacheKey();
+}
+
+const QVector<QRgb> *qt_image_colortable(const QImage &image)
+{
+ return &image.d->colortable;
+}
+
+extern int qt_defaultDpiX();
+extern int qt_defaultDpiY();
+
+QBasicAtomicInt qimage_serial_number = Q_BASIC_ATOMIC_INITIALIZER(1);
+
+QImageData::QImageData()
+ : ref(0), width(0), height(0), depth(0), nbytes(0), data(0),
+#ifdef QT3_SUPPORT
+ jumptable(0),
+#endif
+ format(QImage::Format_ARGB32), bytes_per_line(0),
+ ser_no(qimage_serial_number.fetchAndAddRelaxed(1)),
+ detach_no(0),
+ dpmx(qt_defaultDpiX() * 100 / qreal(2.54)),
+ dpmy(qt_defaultDpiY() * 100 / qreal(2.54)),
+ offset(0, 0), own_data(true), ro_data(false), has_alpha_clut(false),
+ is_cached(false), paintEngine(0)
+{
+}
+
+static int depthForFormat(QImage::Format format)
+{
+ int depth = 0;
+ switch(format) {
+ case QImage::Format_Invalid:
+ case QImage::NImageFormats:
+ Q_ASSERT(false);
+ case QImage::Format_Mono:
+ case QImage::Format_MonoLSB:
+ depth = 1;
+ break;
+ case QImage::Format_Indexed8:
+ depth = 8;
+ break;
+ case QImage::Format_RGB32:
+ case QImage::Format_ARGB32:
+ case QImage::Format_ARGB32_Premultiplied:
+ depth = 32;
+ break;
+ case QImage::Format_RGB555:
+ case QImage::Format_RGB16:
+ case QImage::Format_RGB444:
+ case QImage::Format_ARGB4444_Premultiplied:
+ depth = 16;
+ break;
+ case QImage::Format_RGB666:
+ case QImage::Format_ARGB6666_Premultiplied:
+ case QImage::Format_ARGB8565_Premultiplied:
+ case QImage::Format_ARGB8555_Premultiplied:
+ case QImage::Format_RGB888:
+ depth = 24;
+ break;
+ }
+ return depth;
+}
+
+QImageData * QImageData::create(const QSize &size, QImage::Format format, int numColors)
+{
+ if (!size.isValid() || numColors < 0 || format == QImage::Format_Invalid)
+ return 0; // invalid parameter(s)
+
+ if (!checkPixelSize(format)) {
+ qWarning("QImageData::create(): Invalid pixel size for format %i",
+ format);
+ return 0;
+ }
+
+ uint width = size.width();
+ uint height = size.height();
+ uint depth = depthForFormat(format);
+
+ switch (format) {
+ case QImage::Format_Mono:
+ case QImage::Format_MonoLSB:
+ numColors = 2;
+ break;
+ case QImage::Format_Indexed8:
+ numColors = qBound(0, numColors, 256);
+ break;
+ default:
+ numColors = 0;
+ break;
+ }
+
+ const int bytes_per_line = ((width * depth + 31) >> 5) << 2; // bytes per scanline (must be multiple of 8)
+
+ // sanity check for potential overflows
+ if (INT_MAX/depth < width
+ || bytes_per_line <= 0
+ || height <= 0
+ || INT_MAX/uint(bytes_per_line) < height
+ || INT_MAX/sizeof(uchar *) < uint(height))
+ return 0;
+
+ QImageData *d = new QImageData;
+ d->colortable.resize(numColors);
+ if (depth == 1) {
+ d->colortable[0] = QColor(Qt::black).rgba();
+ d->colortable[1] = QColor(Qt::white).rgba();
+ } else {
+ for (int i = 0; i < numColors; ++i)
+ d->colortable[i] = 0;
+ }
+
+ d->width = width;
+ d->height = height;
+ d->depth = depth;
+ d->format = format;
+ d->has_alpha_clut = false;
+ d->is_cached = false;
+
+ d->bytes_per_line = bytes_per_line;
+
+ d->nbytes = d->bytes_per_line*height;
+ d->data = (uchar *)malloc(d->nbytes);
+
+ if (!d->data) {
+ delete d;
+ return 0;
+ }
+
+ d->ref.ref();
+ return d;
+
+}
+
+QImageData::~QImageData()
+{
+ if (is_cached && qt_image_cleanup_hook_64)
+ qt_image_cleanup_hook_64((((qint64) ser_no) << 32) | ((qint64) detach_no));
+ delete paintEngine;
+ if (data && own_data)
+ free(data);
+#ifdef QT3_SUPPORT
+ if (jumptable)
+ free(jumptable);
+ jumptable = 0;
+#endif
+ data = 0;
+}
+
+
+bool QImageData::checkForAlphaPixels() const
+{
+ bool has_alpha_pixels = false;
+
+ switch (format) {
+
+ case QImage::Format_Indexed8:
+ has_alpha_pixels = has_alpha_clut;
+ break;
+
+ case QImage::Format_ARGB32:
+ case QImage::Format_ARGB32_Premultiplied: {
+ uchar *bits = data;
+ for (int y=0; y<height && !has_alpha_pixels; ++y) {
+ for (int x=0; x<width; ++x)
+ has_alpha_pixels |= (((uint *)bits)[x] & 0xff000000) != 0xff000000;
+ bits += bytes_per_line;
+ }
+ } break;
+
+ case QImage::Format_ARGB8555_Premultiplied:
+ case QImage::Format_ARGB8565_Premultiplied: {
+ uchar *bits = data;
+ uchar *end_bits = data + bytes_per_line;
+
+ for (int y=0; y<height && !has_alpha_pixels; ++y) {
+ while (bits < end_bits) {
+ has_alpha_pixels |= bits[0] != 0;
+ bits += 3;
+ }
+ bits = end_bits;
+ end_bits += bytes_per_line;
+ }
+ } break;
+
+ case QImage::Format_ARGB6666_Premultiplied: {
+ uchar *bits = data;
+ uchar *end_bits = data + bytes_per_line;
+
+ for (int y=0; y<height && !has_alpha_pixels; ++y) {
+ while (bits < end_bits) {
+ has_alpha_pixels |= (bits[0] & 0xfc) != 0;
+ bits += 3;
+ }
+ bits = end_bits;
+ end_bits += bytes_per_line;
+ }
+ } break;
+
+ case QImage::Format_ARGB4444_Premultiplied: {
+ uchar *bits = data;
+ uchar *end_bits = data + bytes_per_line;
+
+ for (int y=0; y<height && !has_alpha_pixels; ++y) {
+ while (bits < end_bits) {
+ has_alpha_pixels |= (bits[0] & 0xf0) != 0;
+ bits += 2;
+ }
+ bits = end_bits;
+ end_bits += bytes_per_line;
+ }
+ } break;
+
+ default:
+ break;
+ }
+
+ return has_alpha_pixels;
+}
+
+/*!
+ \class QImage
+
+ \ingroup multimedia
+ \ingroup shared
+ \mainclass
+ \reentrant
+
+ \brief The QImage class provides a hardware-independent image
+ representation that allows direct access to the pixel data, and
+ can be used as a paint device.
+
+ Qt provides four classes for handling image data: QImage, QPixmap,
+ QBitmap and QPicture. QImage is designed and optimized for I/O,
+ and for direct pixel access and manipulation, while QPixmap is
+ designed and optimized for showing images on screen. QBitmap is
+ only a convenience class that inherits QPixmap, ensuring a
+ depth of 1. Finally, the QPicture class is a paint device that
+ records and replays QPainter commands.
+
+ Because QImage is a QPaintDevice subclass, QPainter can be used to
+ draw directly onto images. When using QPainter on a QImage, the
+ painting can be performed in another thread than the current GUI
+ thread.
+
+ The QImage class supports several image formats described by the
+ \l Format enum. These include monochrome, 8-bit, 32-bit and
+ alpha-blended images which are available in all versions of Qt
+ 4.x.
+
+ QImage provides a collection of functions that can be used to
+ obtain a variety of information about the image. There are also
+ several functions that enables transformation of the image.
+
+ QImage objects can be passed around by value since the QImage
+ class uses \l{Implicit Data Sharing}{implicit data
+ sharing}. QImage objects can also be streamed and compared.
+
+ \note If you would like to load QImage objects in a static build of Qt,
+ refer to the \l{How To Create Qt Plugins#Static Plugins}{Plugin HowTo}.
+
+ \tableofcontents
+
+ \section1 Reading and Writing Image Files
+
+ QImage provides several ways of loading an image file: The file
+ can be loaded when constructing the QImage object, or by using the
+ load() or loadFromData() functions later on. QImage also provides
+ the static fromData() function, constructing a QImage from the
+ given data. When loading an image, the file name can either refer
+ to an actual file on disk or to one of the application's embedded
+ resources. See \l{The Qt Resource System} overview for details
+ on how to embed images and other resource files in the
+ application's executable.
+
+ Simply call the save() function to save a QImage object.
+
+ The complete list of supported file formats are available through
+ the QImageReader::supportedImageFormats() and
+ QImageWriter::supportedImageFormats() functions. New file formats
+ can be added as plugins. By default, Qt supports the following
+ formats:
+
+ \table
+ \header \o Format \o Description \o Qt's support
+ \row \o BMP \o Windows Bitmap \o Read/write
+ \row \o GIF \o Graphic Interchange Format (optional) \o Read
+ \row \o JPG \o Joint Photographic Experts Group \o Read/write
+ \row \o JPEG \o Joint Photographic Experts Group \o Read/write
+ \row \o PNG \o Portable Network Graphics \o Read/write
+ \row \o PBM \o Portable Bitmap \o Read
+ \row \o PGM \o Portable Graymap \o Read
+ \row \o PPM \o Portable Pixmap \o Read/write
+ \row \o TIFF \o Tagged Image File Format \o Read/write
+ \row \o XBM \o X11 Bitmap \o Read/write
+ \row \o XPM \o X11 Pixmap \o Read/write
+ \endtable
+
+ \section1 Image Information
+
+ QImage provides a collection of functions that can be used to
+ obtain a variety of information about the image:
+
+ \table
+ \header
+ \o \o Available Functions
+
+ \row
+ \o Geometry
+ \o
+
+ The size(), width(), height(), dotsPerMeterX(), and
+ dotsPerMeterY() functions provide information about the image size
+ and aspect ratio.
+
+ The rect() function returns the image's enclosing rectangle. The
+ valid() function tells if a given pair of coordinates is within
+ this rectangle. The offset() function returns the number of pixels
+ by which the image is intended to be offset by when positioned
+ relative to other images, which also can be manipulated using the
+ setOffset() function.
+
+ \row
+ \o Colors
+ \o
+
+ The color of a pixel can be retrieved by passing its coordinates
+ to the pixel() function. The pixel() function returns the color
+ as a QRgb value indepedent of the image's format.
+
+ In case of monochrome and 8-bit images, the numColors() and
+ colorTable() functions provide information about the color
+ components used to store the image data: The colorTable() function
+ returns the image's entire color table. To obtain a single entry,
+ use the pixelIndex() function to retrieve the pixel index for a
+ given pair of coordinates, then use the color() function to
+ retrieve the color. Note that if you create an 8-bit image
+ manually, you have to set a valid color table on the image as
+ well.
+
+ The hasAlphaChannel() function tells if the image's format
+ respects the alpha channel, or not. The allGray() and
+ isGrayscale() functions tell whether an image's colors are all
+ shades of gray.
+
+ See also the \l {QImage#Pixel Manipulation}{Pixel Manipulation}
+ and \l {QImage#Image Transformations}{Image Transformations}
+ sections.
+
+ \row
+ \o Text
+ \o
+
+ The text() function returns the image text associated with the
+ given text key. An image's text keys can be retrieved using the
+ textKeys() function. Use the setText() function to alter an
+ image's text.
+
+ \row
+ \o Low-level information
+ \o
+ The depth() function returns the depth of the image. The supported
+ depths are 1 (monochrome), 8 and 32 (for more information see the
+ \l {QImage#Image Formats}{Image Formats} section).
+
+ The format(), bytesPerLine(), and numBytes() functions provide
+ low-level information about the data stored in the image.
+
+ The cacheKey() function returns a number that uniquely
+ identifies the contents of this QImage object.
+ \endtable
+
+ \section1 Pixel Manipulation
+
+ The functions used to manipulate an image's pixels depend on the
+ image format. The reason is that monochrome and 8-bit images are
+ index-based and use a color lookup table, while 32-bit images
+ store ARGB values directly. For more information on image formats,
+ see the \l {Image Formats} section.
+
+ In case of a 32-bit image, the setPixel() function can be used to
+ alter the color of the pixel at the given coordinates to any other
+ color specified as an ARGB quadruplet. To make a suitable QRgb
+ value, use the qRgb() (adding a default alpha component to the
+ given RGB values, i.e. creating an opaque color) or qRgba()
+ function. For example:
+
+ \table
+ \row
+ \o \inlineimage qimage-32bit_scaled.png
+ \o
+ \snippet doc/src/snippets/code/src_gui_image_qimage.cpp 0
+ \header
+ \o {2,1}32-bit
+ \endtable
+
+ In case of a 8-bit and monchrome images, the pixel value is only
+ an index from the image's color table. So the setPixel() function
+ can only be used to alter the color of the pixel at the given
+ coordinates to a predefined color from the image's color table,
+ i.e. it can only change the pixel's index value. To alter or add a
+ color to an image's color table, use the setColor() function.
+
+ An entry in the color table is an ARGB quadruplet encoded as an
+ QRgb value. Use the qRgb() and qRgba() functions to make a
+ suitable QRgb value for use with the setColor() function. For
+ example:
+
+ \table
+ \row
+ \o \inlineimage qimage-8bit_scaled.png
+ \o
+ \snippet doc/src/snippets/code/src_gui_image_qimage.cpp 1
+ \header
+ \o {2,1} 8-bit
+ \endtable
+
+ QImage also provide the scanLine() function which returns a
+ pointer to the pixel data at the scanline with the given index,
+ and the bits() function which returns a pointer to the first pixel
+ data (this is equivalent to \c scanLine(0)).
+
+ \section1 Image Formats
+
+ Each pixel stored in a QImage is represented by an integer. The
+ size of the integer varies depending on the format. QImage
+ supports several image formats described by the \l Format
+ enum. The monochrome (1-bit), 8-bit and 32-bit images are
+ available in all versions of Qt. In addition Qt for Embedded Linux
+ also supports 2-bit, 4-bit, and 16-bit images. For more information
+ about the Qt Extended specific formats, see the documentation of the \l
+ Format enum.
+
+ Monochrome images are stored using 1-bit indexes into a color table
+ with at most two colors. There are two different types of
+ monochrome images: big endian (MSB first) or little endian (LSB
+ first) bit order.
+
+ 8-bit images are stored using 8-bit indexes into a color table,
+ i.e. they have a single byte per pixel. The color table is a
+ QVector<QRgb>, and the QRgb typedef is equivalent to an unsigned
+ int containing an ARGB quadruplet on the format 0xAARRGGBB.
+
+ 32-bit images have no color table; instead, each pixel contains an
+ QRgb value. There are three different types of 32-bit images
+ storing RGB (i.e. 0xffRRGGBB), ARGB and premultiplied ARGB
+ values respectively. In the premultiplied format the red, green,
+ and blue channels are multiplied by the alpha component divided by
+ 255.
+
+ An image's format can be retrieved using the format()
+ function. Use the convertToFormat() functions to convert an image
+ into another format. The allGray() and isGrayscale() functions
+ tell whether a color image can safely be converted to a grayscale
+ image.
+
+ \section1 Image Transformations
+
+ QImage supports a number of functions for creating a new image
+ that is a transformed version of the original: The
+ createAlphaMask() function builds and returns a 1-bpp mask from
+ the alpha buffer in this image, and the createHeuristicMask()
+ function creates and returns a 1-bpp heuristic mask for this
+ image. The latter function works by selecting a color from one of
+ the corners, then chipping away pixels of that color starting at
+ all the edges.
+
+ The mirrored() function returns a mirror of the image in the
+ desired direction, the scaled() returns a copy of the image scaled
+ to a rectangle of the desired measures, the rgbSwapped() function
+ constructs a BGR image from a RGB image, and the alphaChannel()
+ function constructs an image from this image's alpha channel.
+
+ The scaledToWidth() and scaledToHeight() functions return scaled
+ copies of the image.
+
+ The transformed() function returns a copy of the image that is
+ transformed with the given transformation matrix and
+ transformation mode: Internally, the transformation matrix is
+ adjusted to compensate for unwanted translation,
+ i.e. transformed() returns the smallest image containing all
+ transformed points of the original image. The static trueMatrix()
+ function returns the actual matrix used for transforming the
+ image.
+
+ There are also functions for changing attributes of an image
+ in-place:
+
+ \table
+ \header \o Function \o Description
+ \row
+ \o setAlphaChannel()
+ \o Sets the alpha channel of the image.
+ \row
+ \o setDotsPerMeterX()
+ \o Defines the aspect ratio by setting the number of pixels that fit
+ horizontally in a physical meter.
+ \row
+ \o setDotsPerMeterY()
+ \o Defines the aspect ratio by setting the number of pixels that fit
+ vertically in a physical meter.
+ \row
+ \o fill()
+ \o Fills the entire image with the given pixel value.
+ \row
+ \o invertPixels()
+ \o Inverts all pixel values in the image using the given InvertMode value.
+ \row
+ \o setColorTable()
+ \o Sets the color table used to translate color indexes. Only
+ monochrome and 8-bit formats.
+ \row
+ \o setNumColors()
+ \o Resizes the color table. Only monochrome and 8-bit formats.
+
+ \endtable
+
+ \section1 Legal Information
+
+ For smooth scaling, the transformed() functions use code based on
+ smooth scaling algorithm by Daniel M. Duley.
+
+ \legalese
+ Copyright (C) 2004, 2005 Daniel M. Duley
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ \endlegalese
+
+ \sa QImageReader, QImageWriter, QPixmap, QSvgRenderer, {Image Composition Example},
+ {Image Viewer Example}, {Scribble Example}, {Pixelator Example}
+*/
+
+/*!
+ \enum QImage::Endian
+ \compat
+
+ This enum type is used to describe the endianness of the CPU and
+ graphics hardware. It is provided here for compatibility with earlier versions of Qt.
+
+ Use the \l Format enum instead. The \l Format enum specify the
+ endianess for monchrome formats, but for other formats the
+ endianess is not relevant.
+
+ \value IgnoreEndian Endianness does not matter. Useful for some
+ operations that are independent of endianness.
+ \value BigEndian Most significant bit first or network byte order, as on SPARC, PowerPC, and Motorola CPUs.
+ \value LittleEndian Least significant bit first or little endian byte order, as on Intel x86.
+*/
+
+/*!
+ \enum QImage::InvertMode
+
+ This enum type is used to describe how pixel values should be
+ inverted in the invertPixels() function.
+
+ \value InvertRgb Invert only the RGB values and leave the alpha
+ channel unchanged.
+
+ \value InvertRgba Invert all channels, including the alpha channel.
+
+ \sa invertPixels()
+*/
+
+/*!
+ \enum QImage::Format
+
+ The following image formats are available in all versions of Qt:
+
+ \value Format_Invalid The image is invalid.
+ \value Format_Mono The image is stored using 1-bit per pixel. Bytes are
+ packed with the most significant bit (MSB) first.
+ \value Format_MonoLSB The image is stored using 1-bit per pixel. Bytes are
+ packed with the less significant bit (LSB) first.
+ \value Format_Indexed8 The image is stored using 8-bit indexes into a colormap.
+ \value Format_RGB32 The image is stored using a 32-bit RGB format (0xffRRGGBB).
+ \value Format_ARGB32 The image is stored using a 32-bit ARGB format (0xAARRGGBB).
+ \value Format_ARGB32_Premultiplied The image is stored using a premultiplied 32-bit
+ ARGB format (0xAARRGGBB), i.e. the red,
+ green, and blue channels are multiplied
+ by the alpha component divided by 255. (If RR, GG, or BB
+ has a higher value than the alpha channel, the results are
+ undefined.) Certain operations (such as image composition
+ using alpha blending) are faster using premultiplied ARGB32
+ than with plain ARGB32.
+ \value Format_RGB16 The image is stored using a 16-bit RGB format (5-6-5).
+ \value Format_ARGB8565_Premultiplied The image is stored using a
+ premultiplied 24-bit ARGB format (8-5-6-5).
+ \value Format_RGB666 The image is stored using a 24-bit RGB format (6-6-6).
+ The unused most significant bits is always zero.
+ \value Format_ARGB6666_Premultiplied The image is stored using a
+ premultiplied 24-bit ARGB format (6-6-6-6).
+ \value Format_RGB555 The image is stored using a 16-bit RGB format (5-5-5).
+ The unused most significant bit is always zero.
+ \value Format_ARGB8555_Premultiplied The image is stored using a
+ premultiplied 24-bit ARGB format (8-5-5-5).
+ \value Format_RGB888 The image is stored using a 24-bit RGB format (8-8-8).
+ \value Format_RGB444 The image is stored using a 16-bit RGB format (4-4-4).
+ The unused bits are always zero.
+ \value Format_ARGB4444_Premultiplied The image is stored using a
+ premultiplied 16-bit ARGB format (4-4-4-4).
+
+ \sa format(), convertToFormat()
+*/
+
+/*****************************************************************************
+ QImage member functions
+ *****************************************************************************/
+
+// table to flip bits
+static const uchar bitflip[256] = {
+ /*
+ open OUT, "| fmt";
+ for $i (0..255) {
+ print OUT (($i >> 7) & 0x01) | (($i >> 5) & 0x02) |
+ (($i >> 3) & 0x04) | (($i >> 1) & 0x08) |
+ (($i << 7) & 0x80) | (($i << 5) & 0x40) |
+ (($i << 3) & 0x20) | (($i << 1) & 0x10), ", ";
+ }
+ close OUT;
+ */
+ 0, 128, 64, 192, 32, 160, 96, 224, 16, 144, 80, 208, 48, 176, 112, 240,
+ 8, 136, 72, 200, 40, 168, 104, 232, 24, 152, 88, 216, 56, 184, 120, 248,
+ 4, 132, 68, 196, 36, 164, 100, 228, 20, 148, 84, 212, 52, 180, 116, 244,
+ 12, 140, 76, 204, 44, 172, 108, 236, 28, 156, 92, 220, 60, 188, 124, 252,
+ 2, 130, 66, 194, 34, 162, 98, 226, 18, 146, 82, 210, 50, 178, 114, 242,
+ 10, 138, 74, 202, 42, 170, 106, 234, 26, 154, 90, 218, 58, 186, 122, 250,
+ 6, 134, 70, 198, 38, 166, 102, 230, 22, 150, 86, 214, 54, 182, 118, 246,
+ 14, 142, 78, 206, 46, 174, 110, 238, 30, 158, 94, 222, 62, 190, 126, 254,
+ 1, 129, 65, 193, 33, 161, 97, 225, 17, 145, 81, 209, 49, 177, 113, 241,
+ 9, 137, 73, 201, 41, 169, 105, 233, 25, 153, 89, 217, 57, 185, 121, 249,
+ 5, 133, 69, 197, 37, 165, 101, 229, 21, 149, 85, 213, 53, 181, 117, 245,
+ 13, 141, 77, 205, 45, 173, 109, 237, 29, 157, 93, 221, 61, 189, 125, 253,
+ 3, 131, 67, 195, 35, 163, 99, 227, 19, 147, 83, 211, 51, 179, 115, 243,
+ 11, 139, 75, 203, 43, 171, 107, 235, 27, 155, 91, 219, 59, 187, 123, 251,
+ 7, 135, 71, 199, 39, 167, 103, 231, 23, 151, 87, 215, 55, 183, 119, 247,
+ 15, 143, 79, 207, 47, 175, 111, 239, 31, 159, 95, 223, 63, 191, 127, 255
+};
+
+const uchar *qt_get_bitflip_array() // called from QPixmap code
+{
+ return bitflip;
+}
+
+#if defined(QT3_SUPPORT)
+static QImage::Format formatFor(int depth, QImage::Endian bitOrder)
+{
+ QImage::Format format;
+ if (depth == 1) {
+ format = bitOrder == QImage::BigEndian ? QImage::Format_Mono : QImage::Format_MonoLSB;
+ } else if (depth == 8) {
+ format = QImage::Format_Indexed8;
+ } else if (depth == 32) {
+ format = QImage::Format_RGB32;
+ } else if (depth == 24) {
+ format = QImage::Format_RGB888;
+ } else if (depth == 16) {
+ format = QImage::Format_RGB16;
+ } else {
+ qWarning("QImage: Depth %d not supported", depth);
+ format = QImage::Format_Invalid;
+ }
+ return format;
+}
+#endif
+
+/*!
+ Constructs a null image.
+
+ \sa isNull()
+*/
+
+QImage::QImage()
+ : QPaintDevice()
+{
+ d = 0;
+}
+
+/*!
+ Constructs an image with the given \a width, \a height and \a
+ format.
+
+ \warning This will create a QImage with uninitialized data. Call
+ fill() to fill the image with an appropriate pixel value before
+ drawing onto it with QPainter.
+*/
+QImage::QImage(int width, int height, Format format)
+ : QPaintDevice()
+{
+ d = QImageData::create(QSize(width, height), format, 0);
+}
+
+/*!
+ Constructs an image with the given \a size and \a format.
+
+ \warning This will create a QImage with uninitialized data. Call
+ fill() to fill the image with an appropriate pixel value before
+ drawing onto it with QPainter.
+*/
+QImage::QImage(const QSize &size, Format format)
+ : QPaintDevice()
+{
+ d = QImageData::create(size, format, 0);
+}
+
+
+
+QImageData *QImageData::create(uchar *data, int width, int height, int bpl, QImage::Format format, bool readOnly)
+{
+ QImageData *d = 0;
+
+ if (format == QImage::Format_Invalid)
+ return d;
+
+ if (!checkPixelSize(format)) {
+ qWarning("QImageData::create(): Invalid pixel size for format %i",
+ format);
+ return 0;
+ }
+
+ const int depth = depthForFormat(format);
+ const int calc_bytes_per_line = ((width * depth + 31)/32) * 4;
+ const int min_bytes_per_line = (width * depth + 7)/8;
+
+ if (bpl <= 0)
+ bpl = calc_bytes_per_line;
+
+ if (width <= 0 || height <= 0 || !data
+ || INT_MAX/sizeof(uchar *) < uint(height)
+ || INT_MAX/uint(depth) < uint(width)
+ || bpl <= 0
+ || height <= 0
+ || bpl < min_bytes_per_line
+ || INT_MAX/uint(bpl) < uint(height))
+ return d; // invalid parameter(s)
+
+ d = new QImageData;
+ d->ref.ref();
+
+ d->own_data = false;
+ d->ro_data = readOnly;
+ d->data = data;
+ d->width = width;
+ d->height = height;
+ d->depth = depth;
+ d->format = format;
+
+ d->bytes_per_line = bpl;
+ d->nbytes = d->bytes_per_line * height;
+
+ return d;
+}
+
+/*!
+ Constructs an image with the given \a width, \a height and \a
+ format, that uses an existing memory buffer, \a data. The \a width
+ and \a height must be specified in pixels, \a data must be 32-bit aligned,
+ and each scanline of data in the image must also be 32-bit aligned.
+
+ The buffer must remain valid throughout the life of the
+ QImage. The image does not delete the buffer at destruction.
+
+ If \a format is an indexed color format, the image color table is
+ initially empty and must be sufficiently expanded with
+ setNumColors() or setColorTable() before the image is used.
+*/
+QImage::QImage(uchar* data, int width, int height, Format format)
+ : QPaintDevice()
+{
+ d = QImageData::create(data, width, height, 0, format, false);
+}
+
+/*!
+ Constructs an image with the given \a width, \a height and \a
+ format, that uses an existing read-only memory buffer, \a
+ data. The \a width and \a height must be specified in pixels, \a
+ data must be 32-bit aligned, and each scanline of data in the
+ image must also be 32-bit aligned.
+
+ The buffer must remain valid throughout the life of the QImage and
+ all copies that have not been modified or otherwise detached from
+ the original buffer. The image does not delete the buffer at
+ destruction.
+
+ If \a format is an indexed color format, the image color table is
+ initially empty and must be sufficiently expanded with
+ setNumColors() or setColorTable() before the image is used.
+
+ Unlike the similar QImage constructor that takes a non-const data buffer,
+ this version will never alter the contents of the buffer. For example,
+ calling QImage::bits() will return a deep copy of the image, rather than
+ the buffer passed to the constructor. This allows for the efficiency of
+ constructing a QImage from raw data, without the possibility of the raw
+ data being changed.
+*/
+QImage::QImage(const uchar* data, int width, int height, Format format)
+ : QPaintDevice()
+{
+ d = QImageData::create(const_cast<uchar*>(data), width, height, 0, format, true);
+}
+
+/*!
+ Constructs an image with the given \a width, \a height and \a
+ format, that uses an existing memory buffer, \a data. The \a width
+ and \a height must be specified in pixels. \a bytesPerLine
+ specifies the number of bytes per line (stride).
+
+ The buffer must remain valid throughout the life of the
+ QImage. The image does not delete the buffer at destruction.
+
+ If \a format is an indexed color format, the image color table is
+ initially empty and must be sufficiently expanded with
+ setNumColors() or setColorTable() before the image is used.
+*/
+QImage::QImage(uchar *data, int width, int height, int bytesPerLine, Format format)
+ :QPaintDevice()
+{
+ d = QImageData::create(data, width, height, bytesPerLine, format, false);
+}
+
+
+/*!
+ Constructs an image with the given \a width, \a height and \a
+ format, that uses an existing memory buffer, \a data. The \a width
+ and \a height must be specified in pixels. \a bytesPerLine
+ specifies the number of bytes per line (stride).
+
+ The buffer must remain valid throughout the life of the
+ QImage. The image does not delete the buffer at destruction.
+
+ If \a format is an indexed color format, the image color table is
+ initially empty and must be sufficiently expanded with
+ setNumColors() or setColorTable() before the image is used.
+
+ Unlike the similar QImage constructor that takes a non-const data buffer,
+ this version will never alter the contents of the buffer. For example,
+ calling QImage::bits() will return a deep copy of the image, rather than
+ the buffer passed to the constructor. This allows for the efficiency of
+ constructing a QImage from raw data, without the possibility of the raw
+ data being changed.
+*/
+
+QImage::QImage(const uchar *data, int width, int height, int bytesPerLine, Format format)
+ :QPaintDevice()
+{
+ d = QImageData::create(const_cast<uchar*>(data), width, height, bytesPerLine, format, true);
+}
+
+/*!
+ Constructs an image and tries to load the image from the file with
+ the given \a fileName.
+
+ The loader attempts to read the image using the specified \a
+ format. If the \a format is not specified (which is the default),
+ the loader probes the file for a header to guess the file format.
+
+ If the loading of the image failed, this object is a null image.
+
+ The file name can either refer to an actual file on disk or to one
+ of the application's embedded resources. See the
+ \l{resources.html}{Resource System} overview for details on how to
+ embed images and other resource files in the application's
+ executable.
+
+ \sa isNull(), {QImage#Reading and Writing Image Files}{Reading and Writing Image Files}
+*/
+
+QImage::QImage(const QString &fileName, const char *format)
+ : QPaintDevice()
+{
+ d = 0;
+ load(fileName, format);
+}
+
+/*!
+ Constructs an image and tries to load the image from the file with
+ the given \a fileName.
+
+ The loader attempts to read the image using the specified \a
+ format. If the \a format is not specified (which is the default),
+ the loader probes the file for a header to guess the file format.
+
+ If the loading of the image failed, this object is a null image.
+
+ The file name can either refer to an actual file on disk or to one
+ of the application's embedded resources. See the
+ \l{resources.html}{Resource System} overview for details on how to
+ embed images and other resource files in the application's
+ executable.
+
+ You can disable this constructor by defining \c
+ QT_NO_CAST_FROM_ASCII when you compile your applications. This can
+ be useful, for example, if you want to ensure that all
+ user-visible strings go through QObject::tr().
+
+ \sa QString::fromAscii(), isNull(), {QImage#Reading and Writing
+ Image Files}{Reading and Writing Image Files}
+*/
+#ifndef QT_NO_CAST_FROM_ASCII
+QImage::QImage(const char *fileName, const char *format)
+ : QPaintDevice()
+{
+ // ### Qt 5: if you remove the QImage(const QByteArray &) QT3_SUPPORT
+ // constructor, remove this constructor as well. The constructor here
+ // exists so that QImage("foo.png") compiles without ambiguity.
+ d = 0;
+ load(QString::fromAscii(fileName), format);
+}
+#endif
+
+#ifndef QT_NO_IMAGEFORMAT_XPM
+extern bool qt_read_xpm_image_or_array(QIODevice *device, const char * const *source, QImage &image);
+
+/*!
+ Constructs an image from the given \a xpm image.
+
+ Make sure that the image is a valid XPM image. Errors are silently
+ ignored.
+
+ Note that it's possible to squeeze the XPM variable a little bit
+ by using an unusual declaration:
+
+ \snippet doc/src/snippets/code/src_gui_image_qimage.cpp 2
+
+ The extra \c const makes the entire definition read-only, which is
+ slightly more efficient (e.g., when the code is in a shared
+ library) and able to be stored in ROM with the application.
+*/
+
+QImage::QImage(const char * const xpm[])
+ : QPaintDevice()
+{
+ d = 0;
+ if (!xpm)
+ return;
+ if (!qt_read_xpm_image_or_array(0, xpm, *this))
+ // Issue: Warning because the constructor may be ambigious
+ qWarning("QImage::QImage(), XPM is not supported");
+}
+#endif // QT_NO_IMAGEFORMAT_XPM
+
+/*!
+ \fn QImage::QImage(const QByteArray &data)
+
+ Use the static fromData() function instead.
+
+ \oldcode
+ QByteArray data;
+ ...
+ QImage image(data);
+ \newcode
+ QByteArray data;
+ ...
+ QImage image = QImage::fromData(data);
+ \endcode
+*/
+
+
+/*!
+ Constructs a shallow copy of the given \a image.
+
+ For more information about shallow copies, see the \l {Implicit
+ Data Sharing} documentation.
+
+ \sa copy()
+*/
+
+QImage::QImage(const QImage &image)
+ : QPaintDevice()
+{
+ d = image.d;
+ if (d)
+ d->ref.ref();
+}
+
+#ifdef QT3_SUPPORT
+/*!
+ \fn QImage::QImage(int width, int height, int depth, int numColors, Endian bitOrder)
+
+ Constructs an image with the given \a width, \a height, \a depth,
+ \a numColors colors and \a bitOrder.
+
+ Use the constructor that accepts a width, a height and a format
+ (i.e. specifying the depth and bit order), in combination with the
+ setNumColors() function, instead.
+
+ \oldcode
+ QImage image(width, height, depth, numColors);
+ \newcode
+ QImage image(width, height, format);
+
+ // For 8 bit images the default number of colors is 256. If
+ // another number of colors is required it can be specified
+ // using the setNumColors() function.
+ image.setNumColors(numColors);
+ \endcode
+*/
+
+QImage::QImage(int w, int h, int depth, int numColors, Endian bitOrder)
+ : QPaintDevice()
+{
+ d = QImageData::create(QSize(w, h), formatFor(depth, bitOrder), numColors);
+}
+
+/*!
+ Constructs an image with the given \a size, \a depth, \a numColors
+ and \a bitOrder.
+
+ Use the constructor that accepts a size and a format
+ (i.e. specifying the depth and bit order), in combination with the
+ setNumColors() function, instead.
+
+ \oldcode
+ QSize mySize(width, height);
+ QImage image(mySize, depth, numColors);
+ \newcode
+ QSize mySize(width, height);
+ QImage image(mySize, format);
+
+ // For 8 bit images the default number of colors is 256. If
+ // another number of colors is required it can be specified
+ // using the setNumColors() function.
+ image.setNumColors(numColors);
+ \endcode
+*/
+QImage::QImage(const QSize& size, int depth, int numColors, Endian bitOrder)
+ : QPaintDevice()
+{
+ d = QImageData::create(size, formatFor(depth, bitOrder), numColors);
+}
+
+/*!
+ \fn QImage::QImage(uchar* data, int width, int height, int depth, const QRgb* colortable, int numColors, Endian bitOrder)
+
+ Constructs an image with the given \a width, \a height, depth, \a
+ colortable, \a numColors and \a bitOrder, that uses an existing
+ memory buffer, \a data.
+
+ Use the constructor that accepts a uchar pointer, a width, a
+ height and a format (i.e. specifying the depth and bit order), in
+ combination with the setColorTable() function, instead.
+
+ \oldcode
+ uchar *myData;
+ QRgb *myColorTable;
+
+ QImage image(myData, width, height, depth,
+ myColorTable, numColors, IgnoreEndian);
+ \newcode
+ uchar *myData;
+ QVector<QRgb> myColorTable;
+
+ QImage image(myData, width, height, format);
+ image.setColorTable(myColorTable);
+ \endcode
+*/
+QImage::QImage(uchar* data, int w, int h, int depth, const QRgb* colortable, int numColors, Endian bitOrder)
+ : QPaintDevice()
+{
+ d = 0;
+ Format f = formatFor(depth, bitOrder);
+ if (f == Format_Invalid)
+ return;
+
+ const int bytes_per_line = ((w*depth+31)/32)*4; // bytes per scanline
+ if (w <= 0 || h <= 0 || numColors < 0 || !data
+ || INT_MAX/sizeof(uchar *) < uint(h)
+ || INT_MAX/uint(depth) < uint(w)
+ || bytes_per_line <= 0
+ || INT_MAX/uint(bytes_per_line) < uint(h))
+ return; // invalid parameter(s)
+ d = new QImageData;
+ d->ref.ref();
+
+ d->own_data = false;
+ d->data = data;
+ d->width = w;
+ d->height = h;
+ d->depth = depth;
+ d->format = f;
+ if (depth == 32)
+ numColors = 0;
+
+ d->bytes_per_line = bytes_per_line;
+ d->nbytes = d->bytes_per_line * h;
+ if (colortable) {
+ d->colortable.resize(numColors);
+ for (int i = 0; i < numColors; ++i)
+ d->colortable[i] = colortable[i];
+ } else if (numColors) {
+ setNumColors(numColors);
+ }
+}
+
+#ifdef Q_WS_QWS
+
+/*!
+ \fn QImage::QImage(uchar* data, int width, int height, int depth, int bytesPerLine, const QRgb* colortable, int numColors, Endian bitOrder)
+
+ Constructs an image with the given \a width, \a height, \a depth,
+ \a bytesPerLine, \a colortable, \a numColors and \a bitOrder, that
+ uses an existing memory buffer, \a data. The image does not delete
+ the buffer at destruction.
+
+ \warning This constructor is only available in Qt for Embedded Linux.
+
+ The data has to be 32-bit aligned, and each scanline of data in the image
+ must also be 32-bit aligned, so it's no longer possible to specify a custom
+ \a bytesPerLine value.
+*/
+QImage::QImage(uchar* data, int w, int h, int depth, int bpl, const QRgb* colortable, int numColors, Endian bitOrder)
+ : QPaintDevice()
+{
+ d = 0;
+ Format f = formatFor(depth, bitOrder);
+ if (f == Format_Invalid)
+ return;
+ if (!data || w <= 0 || h <= 0 || depth <= 0 || numColors < 0
+ || INT_MAX/sizeof(uchar *) < uint(h)
+ || INT_MAX/uint(depth) < uint(w)
+ || bpl <= 0
+ || INT_MAX/uint(bpl) < uint(h))
+ return; // invalid parameter(s)
+
+ d = new QImageData;
+ d->ref.ref();
+ d->own_data = false;
+ d->data = data;
+ d->width = w;
+ d->height = h;
+ d->depth = depth;
+ d->format = f;
+ if (depth == 32)
+ numColors = 0;
+ d->bytes_per_line = bpl;
+ d->nbytes = d->bytes_per_line * h;
+ if (colortable) {
+ d->colortable.resize(numColors);
+ for (int i = 0; i < numColors; ++i)
+ d->colortable[i] = colortable[i];
+ } else if (numColors) {
+ setNumColors(numColors);
+ }
+}
+#endif // Q_WS_QWS
+#endif // QT3_SUPPORT
+
+/*!
+ Destroys the image and cleans up.
+*/
+
+QImage::~QImage()
+{
+ if (d && !d->ref.deref())
+ delete d;
+}
+
+/*!
+ Assigns a shallow copy of the given \a image to this image and
+ returns a reference to this image.
+
+ For more information about shallow copies, see the \l {Implicit
+ Data Sharing} documentation.
+
+ \sa copy(), QImage()
+*/
+
+QImage &QImage::operator=(const QImage &image)
+{
+ if (image.d)
+ image.d->ref.ref();
+ if (d && !d->ref.deref())
+ delete d;
+ d = image.d;
+ return *this;
+}
+
+/*!
+ \internal
+*/
+int QImage::devType() const
+{
+ return QInternal::Image;
+}
+
+/*!
+ Returns the image as a QVariant.
+*/
+QImage::operator QVariant() const
+{
+ return QVariant(QVariant::Image, this);
+}
+
+/*!
+ \internal
+
+ If multiple images share common data, this image makes a copy of
+ the data and detaches itself from the sharing mechanism, making
+ sure that this image is the only one referring to the data.
+
+ Nothing is done if there is just a single reference.
+
+ \sa copy(), isDetached(), {Implicit Data Sharing}
+*/
+void QImage::detach()
+{
+ if (d) {
+ if (d->is_cached && qt_image_cleanup_hook_64 && d->ref == 1)
+ qt_image_cleanup_hook_64(cacheKey());
+
+ if (d->ref != 1 || d->ro_data)
+ *this = copy();
+
+ if (d)
+ ++d->detach_no;
+ }
+}
+
+
+/*!
+ \fn QImage QImage::copy(int x, int y, int width, int height) const
+ \overload
+
+ The returned image is copied from the position (\a x, \a y) in
+ this image, and will always have the given \a width and \a height.
+ In areas beyond this image, pixels are set to 0.
+
+*/
+
+/*!
+ \fn QImage QImage::copy(const QRect& rectangle) const
+
+ Returns a sub-area of the image as a new image.
+
+ The returned image is copied from the position (\a
+ {rectangle}.x(), \a{rectangle}.y()) in this image, and will always
+ have the size of the given \a rectangle.
+
+ In areas beyond this image, pixels are set to 0. For 32-bit RGB
+ images, this means black; for 32-bit ARGB images, this means
+ transparent black; for 8-bit images, this means the color with
+ index 0 in the color table which can be anything; for 1-bit
+ images, this means Qt::color0.
+
+ If the given \a rectangle is a null rectangle the entire image is
+ copied.
+
+ \sa QImage()
+*/
+QImage QImage::copy(const QRect& r) const
+{
+ if (!d)
+ return QImage();
+
+ if (r.isNull()) {
+ QImage image(d->width, d->height, d->format);
+ if (image.isNull())
+ return image;
+
+ // Qt for Embedded Linux can create images with non-default bpl
+ // make sure we don't crash.
+ if (image.d->nbytes != d->nbytes) {
+ int bpl = image.bytesPerLine();
+ for (int i = 0; i < height(); i++)
+ memcpy(image.scanLine(i), scanLine(i), bpl);
+ } else
+ memcpy(image.bits(), bits(), d->nbytes);
+ image.d->colortable = d->colortable;
+ image.d->dpmx = d->dpmx;
+ image.d->dpmy = d->dpmy;
+ image.d->offset = d->offset;
+ image.d->has_alpha_clut = d->has_alpha_clut;
+#ifndef QT_NO_IMAGE_TEXT
+ image.d->text = d->text;
+#endif
+ return image;
+ }
+
+ int x = r.x();
+ int y = r.y();
+ int w = r.width();
+ int h = r.height();
+
+ int dx = 0;
+ int dy = 0;
+ if (w <= 0 || h <= 0)
+ return QImage();
+
+ QImage image(w, h, d->format);
+ if (image.isNull())
+ return image;
+
+ if (x < 0 || y < 0 || x + w > d->width || y + h > d->height) {
+ // bitBlt will not cover entire image - clear it.
+ image.fill(0);
+ if (x < 0) {
+ dx = -x;
+ x = 0;
+ }
+ if (y < 0) {
+ dy = -y;
+ y = 0;
+ }
+ }
+
+ image.d->colortable = d->colortable;
+
+ int pixels_to_copy = qMax(w - dx, 0);
+ if (x > d->width)
+ pixels_to_copy = 0;
+ else if (pixels_to_copy > d->width - x)
+ pixels_to_copy = d->width - x;
+ int lines_to_copy = qMax(h - dy, 0);
+ if (y > d->height)
+ lines_to_copy = 0;
+ else if (lines_to_copy > d->height - y)
+ lines_to_copy = d->height - y;
+
+ bool byteAligned = true;
+ if (d->format == Format_Mono || d->format == Format_MonoLSB)
+ byteAligned = !(dx & 7) && !(x & 7) && !(pixels_to_copy & 7);
+
+ if (byteAligned) {
+ const uchar *src = d->data + ((x * d->depth) >> 3) + y * d->bytes_per_line;
+ uchar *dest = image.d->data + ((dx * d->depth) >> 3) + dy * image.d->bytes_per_line;
+ const int bytes_to_copy = (pixels_to_copy * d->depth) >> 3;
+ for (int i = 0; i < lines_to_copy; ++i) {
+ memcpy(dest, src, bytes_to_copy);
+ src += d->bytes_per_line;
+ dest += image.d->bytes_per_line;
+ }
+ } else if (d->format == Format_Mono) {
+ const uchar *src = d->data + y * d->bytes_per_line;
+ uchar *dest = image.d->data + dy * image.d->bytes_per_line;
+ for (int i = 0; i < lines_to_copy; ++i) {
+ for (int j = 0; j < pixels_to_copy; ++j) {
+ if (src[(x + j) >> 3] & (0x80 >> ((x + j) & 7)))
+ dest[(dx + j) >> 3] |= (0x80 >> ((dx + j) & 7));
+ else
+ dest[(dx + j) >> 3] &= ~(0x80 >> ((dx + j) & 7));
+ }
+ src += d->bytes_per_line;
+ dest += image.d->bytes_per_line;
+ }
+ } else { // Format_MonoLSB
+ Q_ASSERT(d->format == Format_MonoLSB);
+ const uchar *src = d->data + y * d->bytes_per_line;
+ uchar *dest = image.d->data + dy * image.d->bytes_per_line;
+ for (int i = 0; i < lines_to_copy; ++i) {
+ for (int j = 0; j < pixels_to_copy; ++j) {
+ if (src[(x + j) >> 3] & (0x1 << ((x + j) & 7)))
+ dest[(dx + j) >> 3] |= (0x1 << ((dx + j) & 7));
+ else
+ dest[(dx + j) >> 3] &= ~(0x1 << ((dx + j) & 7));
+ }
+ src += d->bytes_per_line;
+ dest += image.d->bytes_per_line;
+ }
+ }
+
+ image.d->dpmx = dotsPerMeterX();
+ image.d->dpmy = dotsPerMeterY();
+ image.d->offset = offset();
+ image.d->has_alpha_clut = d->has_alpha_clut;
+#ifndef QT_NO_IMAGE_TEXT
+ image.d->text = d->text;
+#endif
+ return image;
+}
+
+
+/*!
+ \fn bool QImage::isNull() const
+
+ Returns true if it is a null image, otherwise returns false.
+
+ A null image has all parameters set to zero and no allocated data.
+*/
+bool QImage::isNull() const
+{
+ return !d;
+}
+
+/*!
+ \fn int QImage::width() const
+
+ Returns the width of the image.
+
+ \sa {QImage#Image Information}{Image Information}
+*/
+int QImage::width() const
+{
+ return d ? d->width : 0;
+}
+
+/*!
+ \fn int QImage::height() const
+
+ Returns the height of the image.
+
+ \sa {QImage#Image Information}{Image Information}
+*/
+int QImage::height() const
+{
+ return d ? d->height : 0;
+}
+
+/*!
+ \fn QSize QImage::size() const
+
+ Returns the size of the image, i.e. its width() and height().
+
+ \sa {QImage#Image Information}{Image Information}
+*/
+QSize QImage::size() const
+{
+ return d ? QSize(d->width, d->height) : QSize(0, 0);
+}
+
+/*!
+ \fn QRect QImage::rect() const
+
+ Returns the enclosing rectangle (0, 0, width(), height()) of the
+ image.
+
+ \sa {QImage#Image Information}{Image Information}
+*/
+QRect QImage::rect() const
+{
+ return d ? QRect(0, 0, d->width, d->height) : QRect();
+}
+
+/*!
+ Returns the depth of the image.
+
+ The image depth is the number of bits used to encode a single
+ pixel, also called bits per pixel (bpp).
+
+ The supported depths are 1, 8, 16, 24 and 32.
+
+ \sa convertToFormat(), {QImage#Image Formats}{Image Formats},
+ {QImage#Image Information}{Image Information}
+
+*/
+int QImage::depth() const
+{
+ return d ? d->depth : 0;
+}
+
+/*!
+ \fn int QImage::numColors() const
+
+ Returns the size of the color table for the image.
+
+ Notice that numColors() returns 0 for 32-bpp images because these
+ images do not use color tables, but instead encode pixel values as
+ ARGB quadruplets.
+
+ \sa setNumColors(), {QImage#Image Information}{Image Information}
+*/
+int QImage::numColors() const
+{
+ return d ? d->colortable.size() : 0;
+}
+
+
+#ifdef QT3_SUPPORT
+/*!
+ \fn QImage::Endian QImage::bitOrder() const
+
+ Returns the bit order for the image. If it is a 1-bpp image, this
+ function returns either QImage::BigEndian or
+ QImage::LittleEndian. Otherwise, this function returns
+ QImage::IgnoreEndian.
+
+ Use the format() function instead for the monochrome formats. For
+ non-monochrome formats the bit order is irrelevant.
+*/
+
+/*!
+ Returns a pointer to the scanline pointer table. This is the
+ beginning of the data block for the image.
+
+ Use the bits() or scanLine() function instead.
+*/
+uchar **QImage::jumpTable()
+{
+ if (!d)
+ return 0;
+ detach();
+
+ // in case detach() ran out of memory..
+ if (!d)
+ return 0;
+
+ if (!d->jumptable) {
+ d->jumptable = (uchar **)malloc(d->height*sizeof(uchar *));
+ uchar *data = d->data;
+ int height = d->height;
+ uchar **p = d->jumptable;
+ while (height--) {
+ *p++ = data;
+ data += d->bytes_per_line;
+ }
+ }
+ return d->jumptable;
+}
+
+/*!
+ \overload
+*/
+const uchar * const *QImage::jumpTable() const
+{
+ if (!d)
+ return 0;
+ if (!d->jumptable) {
+ d->jumptable = (uchar **)malloc(d->height*sizeof(uchar *));
+ uchar *data = d->data;
+ int height = d->height;
+ uchar **p = d->jumptable;
+ while (height--) {
+ *p++ = data;
+ data += d->bytes_per_line;
+ }
+ }
+ return d->jumptable;
+}
+#endif
+
+/*!
+ Sets the color table used to translate color indexes to QRgb
+ values, to the specified \a colors.
+
+ When the image is used, the color table must be large enough to
+ have entries for all the pixel/index values present in the image,
+ otherwise the results are undefined.
+
+ \sa colorTable(), setColor(), {QImage#Image Transformations}{Image
+ Transformations}
+*/
+void QImage::setColorTable(const QVector<QRgb> colors)
+{
+ if (!d)
+ return;
+ detach();
+
+ // In case detach() ran out of memory
+ if (!d)
+ return;
+
+ d->colortable = colors;
+ d->has_alpha_clut = false;
+ for (int i = 0; i < d->colortable.size(); ++i)
+ d->has_alpha_clut |= (qAlpha(d->colortable.at(i)) != 255);
+}
+
+/*!
+ Returns a list of the colors contained in the image's color table,
+ or an empty list if the image does not have a color table
+
+ \sa setColorTable(), numColors(), color()
+*/
+QVector<QRgb> QImage::colorTable() const
+{
+ return d ? d->colortable : QVector<QRgb>();
+}
+
+
+/*!
+ Returns the number of bytes occupied by the image data.
+
+ \sa bytesPerLine(), bits(), {QImage#Image Information}{Image
+ Information}
+*/
+int QImage::numBytes() const
+{
+ return d ? d->nbytes : 0;
+}
+
+/*!
+ Returns the number of bytes per image scanline.
+
+ This is equivalent to numBytes()/ height().
+
+ \sa scanLine()
+*/
+int QImage::bytesPerLine() const
+{
+ return (d && d->height) ? d->nbytes / d->height : 0;
+}
+
+
+/*!
+ Returns the color in the color table at index \a i. The first
+ color is at index 0.
+
+ The colors in an image's color table are specified as ARGB
+ quadruplets (QRgb). Use the qAlpha(), qRed(), qGreen(), and
+ qBlue() functions to get the color value components.
+
+ \sa setColor(), pixelIndex(), {QImage#Pixel Manipulation}{Pixel
+ Manipulation}
+*/
+QRgb QImage::color(int i) const
+{
+ Q_ASSERT(i < numColors());
+ return d ? d->colortable.at(i) : QRgb(uint(-1));
+}
+
+/*!
+ \fn void QImage::setColor(int index, QRgb colorValue)
+
+ Sets the color at the given \a index in the color table, to the
+ given to \a colorValue. The color value is an ARGB quadruplet.
+
+ If \a index is outside the current size of the color table, it is
+ expanded with setNumColors().
+
+ \sa color(), numColors(), setColorTable(), {QImage#Pixel Manipulation}{Pixel
+ Manipulation}
+*/
+void QImage::setColor(int i, QRgb c)
+{
+ if (!d)
+ return;
+ if (i < 0 || d->depth > 8 || i >= 1<<d->depth) {
+ qWarning("QImage::setColor: Index out of bound %d", i);
+ return;
+ }
+ detach();
+
+ // In case detach() run out of memory
+ if (!d)
+ return;
+
+ if (i >= d->colortable.size())
+ setNumColors(i+1);
+ d->colortable[i] = c;
+ d->has_alpha_clut |= (qAlpha(c) != 255);
+}
+
+/*!
+ Returns a pointer to the pixel data at the scanline with index \a
+ i. The first scanline is at index 0.
+
+ The scanline data is aligned on a 32-bit boundary.
+
+ \warning If you are accessing 32-bpp image data, cast the returned
+ pointer to \c{QRgb*} (QRgb has a 32-bit size) and use it to
+ read/write the pixel value. You cannot use the \c{uchar*} pointer
+ directly, because the pixel format depends on the byte order on
+ the underlying platform. Use qRed(), qGreen(), qBlue(), and
+ qAlpha() to access the pixels.
+
+ \sa bytesPerLine(), bits(), {QImage#Pixel Manipulation}{Pixel
+ Manipulation}
+*/
+uchar *QImage::scanLine(int i)
+{
+ if (!d)
+ return 0;
+
+ detach();
+
+ // In case detach() ran out of memory
+ if (!d)
+ return 0;
+
+ return d->data + i * d->bytes_per_line;
+}
+
+/*!
+ \overload
+*/
+const uchar *QImage::scanLine(int i) const
+{
+ if (!d)
+ return 0;
+
+ Q_ASSERT(i >= 0 && i < height());
+ return d->data + i * d->bytes_per_line;
+}
+
+
+/*!
+ Returns a pointer to the first pixel data. This is equivalent to
+ scanLine(0).
+
+ Note that QImage uses \l{Implicit Data Sharing} {implicit data
+ sharing}. This function performs a deep copy of the shared pixel
+ data, thus ensuring that this QImage is the only one using the
+ current return value.
+
+ \sa scanLine(), numBytes()
+*/
+uchar *QImage::bits()
+{
+ if (!d)
+ return 0;
+ detach();
+
+ // In case detach ran out of memory...
+ if (!d)
+ return 0;
+
+ return d->data;
+}
+
+/*!
+ \overload
+
+ Note that QImage uses \l{Implicit Data Sharing} {implicit data
+ sharing}, but this function does \e not perform a deep copy of the
+ shared pixel data, because the returned data is const.
+*/
+const uchar *QImage::bits() const
+{
+ return d ? d->data : 0;
+}
+
+
+
+/*!
+ \fn void QImage::reset()
+
+ Resets all image parameters and deallocates the image data.
+
+ Assign a null image instead.
+
+ \oldcode
+ QImage image;
+ image.reset();
+ \newcode
+ QImage image;
+ image = QImage();
+ \endcode
+*/
+
+/*!
+ \fn void QImage::fill(uint pixelValue)
+
+ Fills the entire image with the given \a pixelValue.
+
+ If the depth of this image is 1, only the lowest bit is used. If
+ you say fill(0), fill(2), etc., the image is filled with 0s. If
+ you say fill(1), fill(3), etc., the image is filled with 1s. If
+ the depth is 8, the lowest 8 bits are used and if the depth is 16
+ the lowest 16 bits are used.
+
+ Note: QImage::pixel() returns the color of the pixel at the given
+ coordinates while QColor::pixel() returns the pixel value of the
+ underlying window system (essentially an index value), so normally
+ you will want to use QImage::pixel() to use a color from an
+ existing image or QColor::rgb() to use a specific color.
+
+ \sa depth(), {QImage#Image Transformations}{Image Transformations}
+*/
+
+void QImage::fill(uint pixel)
+{
+ if (!d)
+ return;
+
+ detach();
+
+ // In case detach() ran out of memory
+ if (!d)
+ return;
+
+ if (d->depth == 1 || d->depth == 8) {
+ int w = d->width;
+ if (d->depth == 1) {
+ if (pixel & 1)
+ pixel = 0xffffffff;
+ else
+ pixel = 0;
+ w = (w + 7) / 8;
+ } else {
+ pixel &= 0xff;
+ }
+ qt_rectfill<quint8>(d->data, pixel, 0, 0,
+ w, d->height, d->bytes_per_line);
+ return;
+ } else if (d->depth == 16) {
+ qt_rectfill<quint16>(reinterpret_cast<quint16*>(d->data), pixel,
+ 0, 0, d->width, d->height, d->bytes_per_line);
+ return;
+ } else if (d->depth == 24) {
+ qt_rectfill<quint24>(reinterpret_cast<quint24*>(d->data), pixel,
+ 0, 0, d->width, d->height, d->bytes_per_line);
+ return;
+ }
+
+ if (d->format == Format_RGB32)
+ pixel |= 0xff000000;
+
+ qt_rectfill<uint>(reinterpret_cast<uint*>(d->data), pixel,
+ 0, 0, d->width, d->height, d->bytes_per_line);
+}
+
+/*!
+ Inverts all pixel values in the image.
+
+ The given invert \a mode only have a meaning when the image's
+ depth is 32. The default \a mode is InvertRgb, which leaves the
+ alpha channel unchanged. If the \a mode is InvertRgba, the alpha
+ bits are also inverted.
+
+ Inverting an 8-bit image means to replace all pixels using color
+ index \e i with a pixel using color index 255 minus \e i. The same
+ is the case for a 1-bit image. Note that the color table is \e not
+ changed.
+
+ \sa {QImage#Image Transformations}{Image Transformations}
+*/
+
+void QImage::invertPixels(InvertMode mode)
+{
+ if (!d)
+ return;
+
+ detach();
+
+ // In case detach() ran out of memory
+ if (!d)
+ return;
+
+ if (depth() != 32) {
+ // number of used bytes pr line
+ int bpl = (d->width * d->depth + 7) / 8;
+ int pad = d->bytes_per_line - bpl;
+ uchar *sl = d->data;
+ for (int y=0; y<d->height; ++y) {
+ for (int x=0; x<bpl; ++x)
+ *sl++ ^= 0xff;
+ sl += pad;
+ }
+ } else {
+ quint32 *p = (quint32*)d->data;
+ quint32 *end = (quint32*)(d->data + d->nbytes);
+ uint xorbits = (mode == InvertRgba) ? 0xffffffff : 0x00ffffff;
+ while (p < end)
+ *p++ ^= xorbits;
+ }
+}
+
+/*!
+ \fn void QImage::invertPixels(bool invertAlpha)
+
+ Use the invertPixels() function that takes a QImage::InvertMode
+ parameter instead.
+*/
+
+/*! \fn QImage::Endian QImage::systemByteOrder()
+
+ Determines the host computer byte order. Returns
+ QImage::LittleEndian (LSB first) or QImage::BigEndian (MSB first).
+
+ This function is no longer relevant for QImage. Use QSysInfo
+ instead.
+*/
+
+// Windows defines these
+#if defined(write)
+# undef write
+#endif
+#if defined(close)
+# undef close
+#endif
+#if defined(read)
+# undef read
+#endif
+
+/*!
+ Resizes the color table to contain \a numColors entries.
+
+ If the color table is expanded, all the extra colors will be set to
+ transparent (i.e qRgba(0, 0, 0, 0)).
+
+ When the image is used, the color table must be large enough to
+ have entries for all the pixel/index values present in the image,
+ otherwise the results are undefined.
+
+ \sa numColors(), colorTable(), setColor(), {QImage#Image
+ Transformations}{Image Transformations}
+*/
+
+void QImage::setNumColors(int numColors)
+{
+ if (!d) {
+ qWarning("QImage::setNumColors: null image");
+ return;
+ }
+
+ detach();
+
+ // In case detach() ran out of memory
+ if (!d)
+ return;
+
+ if (numColors == d->colortable.size())
+ return;
+ if (numColors <= 0) { // use no color table
+ d->colortable = QVector<QRgb>();
+ return;
+ }
+ int nc = d->colortable.size();
+ d->colortable.resize(numColors);
+ for (int i = nc; i < numColors; ++i)
+ d->colortable[i] = 0;
+
+}
+
+/*!
+ Returns the format of the image.
+
+ \sa {QImage#Image Formats}{Image Formats}
+*/
+QImage::Format QImage::format() const
+{
+ return d ? d->format : Format_Invalid;
+}
+
+
+#ifdef QT3_SUPPORT
+/*!
+ Returns true if alpha buffer mode is enabled; otherwise returns
+ false.
+
+ Use the hasAlphaChannel() function instead.
+
+*/
+bool QImage::hasAlphaBuffer() const
+{
+ if (!d)
+ return false;
+
+ switch (d->format) {
+ case Format_ARGB32:
+ case Format_ARGB32_Premultiplied:
+ case Format_ARGB8565_Premultiplied:
+ case Format_ARGB8555_Premultiplied:
+ case Format_ARGB6666_Premultiplied:
+ case Format_ARGB4444_Premultiplied:
+ return true;
+ default:
+ return false;
+ }
+}
+
+/*!
+ Enables alpha buffer mode if \a enable is true, otherwise disables
+ it. The alpha buffer is used to set a mask when a QImage is
+ translated to a QPixmap.
+
+ If a monochrome or indexed 8-bit image has alpha channels in their
+ color tables they will automatically detect that they have an
+ alpha channel, so this function is not required. To force alpha
+ channels on 32-bit images, use the convertToFormat() function.
+*/
+
+void QImage::setAlphaBuffer(bool enable)
+{
+ if (!d
+ || d->format == Format_Mono
+ || d->format == Format_MonoLSB
+ || d->format == Format_Indexed8)
+ return;
+ if (enable && (d->format == Format_ARGB32 ||
+ d->format == Format_ARGB32_Premultiplied ||
+ d->format == Format_ARGB8565_Premultiplied ||
+ d->format == Format_ARGB6666_Premultiplied ||
+ d->format == Format_ARGB8555_Premultiplied ||
+ d->format == Format_ARGB4444_Premultiplied))
+ {
+ return;
+ }
+ if (!enable && (d->format == Format_RGB32 ||
+ d->format == Format_RGB555 ||
+ d->format == Format_RGB666 ||
+ d->format == Format_RGB888 ||
+ d->format == Format_RGB444))
+ {
+ return;
+ }
+ detach();
+ d->format = (enable ? Format_ARGB32 : Format_RGB32);
+}
+
+
+/*!
+ \fn bool QImage::create(int width, int height, int depth, int numColors, Endian bitOrder)
+
+ Sets the image \a width, \a height, \a depth, its number of colors
+ (in \a numColors), and bit order. Returns true if successful, or
+ false if the parameters are incorrect or if memory cannot be
+ allocated.
+
+ The \a width and \a height is limited to 32767. \a depth must be
+ 1, 8, or 32. If \a depth is 1, \a bitOrder must be set to
+ either QImage::LittleEndian or QImage::BigEndian. For other depths
+ \a bitOrder must be QImage::IgnoreEndian.
+
+ This function allocates a color table and a buffer for the image
+ data. The image data is not initialized. The image buffer is
+ allocated as a single block that consists of a table of scanLine()
+ pointers (jumpTable()) and the image data (bits()).
+
+ Use a QImage constructor instead.
+*/
+bool QImage::create(int width, int height, int depth, int numColors, Endian bitOrder)
+{
+ if (d && !d->ref.deref())
+ delete d;
+ d = QImageData::create(QSize(width, height), formatFor(depth, bitOrder), numColors);
+ return true;
+}
+
+/*!
+ \fn bool QImage::create(const QSize& size, int depth, int numColors, Endian bitOrder)
+ \overload
+
+ The width and height are specified in the \a size argument.
+
+ Use a QImage constructor instead.
+*/
+bool QImage::create(const QSize& size, int depth, int numColors, QImage::Endian bitOrder)
+{
+ if (d && !d->ref.deref())
+ delete d;
+ d = QImageData::create(size, formatFor(depth, bitOrder), numColors);
+ return true;
+}
+#endif // QT3_SUPPORT
+
+/*****************************************************************************
+ Internal routines for converting image depth.
+ *****************************************************************************/
+
+typedef void (*Image_Converter)(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags);
+
+static void convert_ARGB_to_ARGB_PM(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags)
+{
+ Q_ASSERT(src->format == QImage::Format_ARGB32);
+ Q_ASSERT(dest->format == QImage::Format_ARGB32_Premultiplied);
+ Q_ASSERT(src->width == dest->width);
+ Q_ASSERT(src->height == dest->height);
+
+ const int src_pad = (src->bytes_per_line >> 2) - src->width;
+ const int dest_pad = (dest->bytes_per_line >> 2) - dest->width;
+ const QRgb *src_data = (QRgb *) src->data;
+ QRgb *dest_data = (QRgb *) dest->data;
+
+ for (int i = 0; i < src->height; ++i) {
+ const QRgb *end = src_data + src->width;
+ while (src_data < end) {
+ *dest_data = PREMUL(*src_data);
+ ++src_data;
+ ++dest_data;
+ }
+ src_data += src_pad;
+ dest_data += dest_pad;
+ }
+}
+
+static void convert_ARGB_PM_to_ARGB(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags)
+{
+ Q_ASSERT(src->format == QImage::Format_ARGB32_Premultiplied);
+ Q_ASSERT(dest->format == QImage::Format_ARGB32);
+ Q_ASSERT(src->width == dest->width);
+ Q_ASSERT(src->height == dest->height);
+
+ const int src_pad = (src->bytes_per_line >> 2) - src->width;
+ const int dest_pad = (dest->bytes_per_line >> 2) - dest->width;
+ const QRgb *src_data = (QRgb *) src->data;
+ QRgb *dest_data = (QRgb *) dest->data;
+
+ for (int i = 0; i < src->height; ++i) {
+ const QRgb *end = src_data + src->width;
+ while (src_data < end) {
+ *dest_data = INV_PREMUL(*src_data);
+ ++src_data;
+ ++dest_data;
+ }
+ src_data += src_pad;
+ dest_data += dest_pad;
+ }
+}
+
+static void convert_ARGB_PM_to_RGB(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags)
+{
+ Q_ASSERT(src->format == QImage::Format_ARGB32_Premultiplied);
+ Q_ASSERT(dest->format == QImage::Format_RGB32);
+ Q_ASSERT(src->width == dest->width);
+ Q_ASSERT(src->height == dest->height);
+
+ const int src_pad = (src->bytes_per_line >> 2) - src->width;
+ const int dest_pad = (dest->bytes_per_line >> 2) - dest->width;
+ const QRgb *src_data = (QRgb *) src->data;
+ QRgb *dest_data = (QRgb *) dest->data;
+
+ for (int i = 0; i < src->height; ++i) {
+ const QRgb *end = src_data + src->width;
+ while (src_data < end) {
+ *dest_data = 0xff000000 | INV_PREMUL(*src_data);
+ ++src_data;
+ ++dest_data;
+ }
+ src_data += src_pad;
+ dest_data += dest_pad;
+ }
+}
+
+static void swap_bit_order(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags)
+{
+ Q_ASSERT(src->format == QImage::Format_Mono || src->format == QImage::Format_MonoLSB);
+ Q_ASSERT(dest->format == QImage::Format_Mono || dest->format == QImage::Format_MonoLSB);
+ Q_ASSERT(src->width == dest->width);
+ Q_ASSERT(src->height == dest->height);
+ Q_ASSERT(src->nbytes == dest->nbytes);
+ Q_ASSERT(src->bytes_per_line == dest->bytes_per_line);
+
+ dest->colortable = src->colortable;
+
+ const uchar *src_data = src->data;
+ const uchar *end = src->data + src->nbytes;
+ uchar *dest_data = dest->data;
+ while (src_data < end) {
+ *dest_data = bitflip[*src_data];
+ ++src_data;
+ ++dest_data;
+ }
+}
+
+static void mask_alpha_converter(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags)
+{
+ Q_ASSERT(src->width == dest->width);
+ Q_ASSERT(src->height == dest->height);
+
+ const int src_pad = (src->bytes_per_line >> 2) - src->width;
+ const int dest_pad = (dest->bytes_per_line >> 2) - dest->width;
+ const uint *src_data = (const uint *)src->data;
+ uint *dest_data = (uint *)dest->data;
+
+ for (int i = 0; i < src->height; ++i) {
+ const uint *end = src_data + src->width;
+ while (src_data < end) {
+ *dest_data = *src_data | 0xff000000;
+ ++src_data;
+ ++dest_data;
+ }
+ src_data += src_pad;
+ dest_data += dest_pad;
+ }
+}
+
+static QVector<QRgb> fix_color_table(const QVector<QRgb> &ctbl, QImage::Format format)
+{
+ QVector<QRgb> colorTable = ctbl;
+ if (format == QImage::Format_RGB32) {
+ // check if the color table has alpha
+ for (int i = 0; i < colorTable.size(); ++i)
+ if (qAlpha(colorTable.at(i) != 0xff))
+ colorTable[i] = colorTable.at(i) | 0xff000000;
+ } else if (format == QImage::Format_ARGB32_Premultiplied) {
+ // check if the color table has alpha
+ for (int i = 0; i < colorTable.size(); ++i)
+ colorTable[i] = PREMUL(colorTable.at(i));
+ }
+ return colorTable;
+}
+
+//
+// dither_to_1: Uses selected dithering algorithm.
+//
+
+static void dither_to_Mono(QImageData *dst, const QImageData *src,
+ Qt::ImageConversionFlags flags, bool fromalpha)
+{
+ Q_ASSERT(src->width == dst->width);
+ Q_ASSERT(src->height == dst->height);
+ Q_ASSERT(dst->format == QImage::Format_Mono || dst->format == QImage::Format_MonoLSB);
+
+ dst->colortable.clear();
+ dst->colortable.append(0xffffffff);
+ dst->colortable.append(0xff000000);
+
+ enum { Threshold, Ordered, Diffuse } dithermode;
+
+ if (fromalpha) {
+ if ((flags & Qt::AlphaDither_Mask) == Qt::DiffuseAlphaDither)
+ dithermode = Diffuse;
+ else if ((flags & Qt::AlphaDither_Mask) == Qt::OrderedAlphaDither)
+ dithermode = Ordered;
+ else
+ dithermode = Threshold;
+ } else {
+ if ((flags & Qt::Dither_Mask) == Qt::ThresholdDither)
+ dithermode = Threshold;
+ else if ((flags & Qt::Dither_Mask) == Qt::OrderedDither)
+ dithermode = Ordered;
+ else
+ dithermode = Diffuse;
+ }
+
+ int w = src->width;
+ int h = src->height;
+ int d = src->depth;
+ uchar gray[256]; // gray map for 8 bit images
+ bool use_gray = (d == 8);
+ if (use_gray) { // make gray map
+ if (fromalpha) {
+ // Alpha 0x00 -> 0 pixels (white)
+ // Alpha 0xFF -> 1 pixels (black)
+ for (int i = 0; i < src->colortable.size(); i++)
+ gray[i] = (255 - (src->colortable.at(i) >> 24));
+ } else {
+ // Pixel 0x00 -> 1 pixels (black)
+ // Pixel 0xFF -> 0 pixels (white)
+ for (int i = 0; i < src->colortable.size(); i++)
+ gray[i] = qGray(src->colortable.at(i));
+ }
+ }
+
+ uchar *dst_data = dst->data;
+ int dst_bpl = dst->bytes_per_line;
+ const uchar *src_data = src->data;
+ int src_bpl = src->bytes_per_line;
+
+ switch (dithermode) {
+ case Diffuse: {
+ int *line1 = new int[w];
+ int *line2 = new int[w];
+ int bmwidth = (w+7)/8;
+
+ int *b1, *b2;
+ int wbytes = w * (d/8);
+ register const uchar *p = src->data;
+ const uchar *end = p + wbytes;
+ b2 = line2;
+ if (use_gray) { // 8 bit image
+ while (p < end)
+ *b2++ = gray[*p++];
+ } else { // 32 bit image
+ if (fromalpha) {
+ while (p < end) {
+ *b2++ = 255 - (*(uint*)p >> 24);
+ p += 4;
+ }
+ } else {
+ while (p < end) {
+ *b2++ = qGray(*(uint*)p);
+ p += 4;
+ }
+ }
+ }
+ for (int y=0; y<h; y++) { // for each scan line...
+ int *tmp = line1; line1 = line2; line2 = tmp;
+ bool not_last_line = y < h - 1;
+ if (not_last_line) { // calc. grayvals for next line
+ p = src->data + (y+1)*src->bytes_per_line;
+ end = p + wbytes;
+ b2 = line2;
+ if (use_gray) { // 8 bit image
+ while (p < end)
+ *b2++ = gray[*p++];
+ } else { // 24 bit image
+ if (fromalpha) {
+ while (p < end) {
+ *b2++ = 255 - (*(uint*)p >> 24);
+ p += 4;
+ }
+ } else {
+ while (p < end) {
+ *b2++ = qGray(*(uint*)p);
+ p += 4;
+ }
+ }
+ }
+ }
+
+ int err;
+ uchar *p = dst->data + y*dst->bytes_per_line;
+ memset(p, 0, bmwidth);
+ b1 = line1;
+ b2 = line2;
+ int bit = 7;
+ for (int x=1; x<=w; x++) {
+ if (*b1 < 128) { // black pixel
+ err = *b1++;
+ *p |= 1 << bit;
+ } else { // white pixel
+ err = *b1++ - 255;
+ }
+ if (bit == 0) {
+ p++;
+ bit = 7;
+ } else {
+ bit--;
+ }
+ if (x < w)
+ *b1 += (err*7)>>4; // spread error to right pixel
+ if (not_last_line) {
+ b2[0] += (err*5)>>4; // pixel below
+ if (x > 1)
+ b2[-1] += (err*3)>>4; // pixel below left
+ if (x < w)
+ b2[1] += err>>4; // pixel below right
+ }
+ b2++;
+ }
+ }
+ delete [] line1;
+ delete [] line2;
+ } break;
+ case Ordered: {
+
+ memset(dst->data, 0, dst->nbytes);
+ if (d == 32) {
+ for (int i=0; i<h; i++) {
+ const uint *p = (const uint *)src_data;
+ const uint *end = p + w;
+ uchar *m = dst_data;
+ int bit = 7;
+ int j = 0;
+ if (fromalpha) {
+ while (p < end) {
+ if ((*p++ >> 24) >= qt_bayer_matrix[j++&15][i&15])
+ *m |= 1 << bit;
+ if (bit == 0) {
+ m++;
+ bit = 7;
+ } else {
+ bit--;
+ }
+ }
+ } else {
+ while (p < end) {
+ if ((uint)qGray(*p++) < qt_bayer_matrix[j++&15][i&15])
+ *m |= 1 << bit;
+ if (bit == 0) {
+ m++;
+ bit = 7;
+ } else {
+ bit--;
+ }
+ }
+ }
+ dst_data += dst_bpl;
+ src_data += src_bpl;
+ }
+ } else
+ /* (d == 8) */ {
+ for (int i=0; i<h; i++) {
+ const uchar *p = src_data;
+ const uchar *end = p + w;
+ uchar *m = dst_data;
+ int bit = 7;
+ int j = 0;
+ while (p < end) {
+ if ((uint)gray[*p++] < qt_bayer_matrix[j++&15][i&15])
+ *m |= 1 << bit;
+ if (bit == 0) {
+ m++;
+ bit = 7;
+ } else {
+ bit--;
+ }
+ }
+ dst_data += dst_bpl;
+ src_data += src_bpl;
+ }
+ }
+ } break;
+ default: { // Threshold:
+ memset(dst->data, 0, dst->nbytes);
+ if (d == 32) {
+ for (int i=0; i<h; i++) {
+ const uint *p = (const uint *)src_data;
+ const uint *end = p + w;
+ uchar *m = dst_data;
+ int bit = 7;
+ if (fromalpha) {
+ while (p < end) {
+ if ((*p++ >> 24) >= 128)
+ *m |= 1 << bit; // Set mask "on"
+ if (bit == 0) {
+ m++;
+ bit = 7;
+ } else {
+ bit--;
+ }
+ }
+ } else {
+ while (p < end) {
+ if (qGray(*p++) < 128)
+ *m |= 1 << bit; // Set pixel "black"
+ if (bit == 0) {
+ m++;
+ bit = 7;
+ } else {
+ bit--;
+ }
+ }
+ }
+ dst_data += dst_bpl;
+ src_data += src_bpl;
+ }
+ } else
+ if (d == 8) {
+ for (int i=0; i<h; i++) {
+ const uchar *p = src_data;
+ const uchar *end = p + w;
+ uchar *m = dst_data;
+ int bit = 7;
+ while (p < end) {
+ if (gray[*p++] < 128)
+ *m |= 1 << bit; // Set mask "on"/ pixel "black"
+ if (bit == 0) {
+ m++;
+ bit = 7;
+ } else {
+ bit--;
+ }
+ }
+ dst_data += dst_bpl;
+ src_data += src_bpl;
+ }
+ }
+ }
+ }
+
+ if (dst->format == QImage::Format_MonoLSB) {
+ // need to swap bit order
+ uchar *sl = dst->data;
+ int bpl = (dst->width + 7) * dst->depth / 8;
+ int pad = dst->bytes_per_line - bpl;
+ for (int y=0; y<dst->height; ++y) {
+ for (int x=0; x<bpl; ++x) {
+ *sl = bitflip[*sl];
+ ++sl;
+ }
+ sl += pad;
+ }
+ }
+}
+
+static void convert_X_to_Mono(QImageData *dst, const QImageData *src, Qt::ImageConversionFlags flags)
+{
+ dither_to_Mono(dst, src, flags, false);
+}
+
+static void convert_ARGB_PM_to_Mono(QImageData *dst, const QImageData *src, Qt::ImageConversionFlags flags)
+{
+ QImageData *tmp = QImageData::create(QSize(src->width, src->height), QImage::Format_ARGB32);
+ convert_ARGB_PM_to_ARGB(tmp, src, flags);
+ dither_to_Mono(dst, tmp, flags, false);
+ delete tmp;
+}
+
+//
+// convert_32_to_8: Converts a 32 bits depth (true color) to an 8 bit
+// image with a colormap. If the 32 bit image has more than 256 colors,
+// we convert the red,green and blue bytes into a single byte encoded
+// as 6 shades of each of red, green and blue.
+//
+// if dithering is needed, only 1 color at most is available for alpha.
+//
+struct QRgbMap {
+ inline QRgbMap() : used(0) { }
+ uchar pix;
+ uchar used;
+ QRgb rgb;
+};
+
+static void convert_RGB_to_Indexed8(QImageData *dst, const QImageData *src, Qt::ImageConversionFlags flags)
+{
+ Q_ASSERT(src->format == QImage::Format_RGB32 || src->format == QImage::Format_ARGB32);
+ Q_ASSERT(dst->format == QImage::Format_Indexed8);
+ Q_ASSERT(src->width == dst->width);
+ Q_ASSERT(src->height == dst->height);
+
+ bool do_quant = (flags & Qt::DitherMode_Mask) == Qt::PreferDither
+ || src->format == QImage::Format_ARGB32;
+ uint alpha_mask = src->format == QImage::Format_RGB32 ? 0xff000000 : 0;
+
+ const int tablesize = 997; // prime
+ QRgbMap table[tablesize];
+ int pix=0;
+
+ if (!dst->colortable.isEmpty()) {
+ QVector<QRgb> ctbl = dst->colortable;
+ dst->colortable.resize(256);
+ // Preload palette into table.
+ // Almost same code as pixel insertion below
+ for (int i = 0; i < dst->colortable.size(); ++i) {
+ // Find in table...
+ QRgb p = ctbl.at(i) | alpha_mask;
+ int hash = p % tablesize;
+ for (;;) {
+ if (table[hash].used) {
+ if (table[hash].rgb == p) {
+ // Found previous insertion - use it
+ break;
+ } else {
+ // Keep searching...
+ if (++hash == tablesize) hash = 0;
+ }
+ } else {
+ // Cannot be in table
+ Q_ASSERT (pix != 256); // too many colors
+ // Insert into table at this unused position
+ dst->colortable[pix] = p;
+ table[hash].pix = pix++;
+ table[hash].rgb = p;
+ table[hash].used = 1;
+ break;
+ }
+ }
+ }
+ }
+
+ if ((flags & Qt::DitherMode_Mask) != Qt::PreferDither) {
+ dst->colortable.resize(256);
+ const uchar *src_data = src->data;
+ uchar *dest_data = dst->data;
+ for (int y = 0; y < src->height; y++) { // check if <= 256 colors
+ const QRgb *s = (const QRgb *)src_data;
+ uchar *b = dest_data;
+ for (int x = 0; x < src->width; ++x) {
+ QRgb p = s[x] | alpha_mask;
+ int hash = p % tablesize;
+ for (;;) {
+ if (table[hash].used) {
+ if (table[hash].rgb == (p)) {
+ // Found previous insertion - use it
+ break;
+ } else {
+ // Keep searching...
+ if (++hash == tablesize) hash = 0;
+ }
+ } else {
+ // Cannot be in table
+ if (pix == 256) { // too many colors
+ do_quant = true;
+ // Break right out
+ x = src->width;
+ y = src->height;
+ } else {
+ // Insert into table at this unused position
+ dst->colortable[pix] = p;
+ table[hash].pix = pix++;
+ table[hash].rgb = p;
+ table[hash].used = 1;
+ }
+ break;
+ }
+ }
+ *b++ = table[hash].pix; // May occur once incorrectly
+ }
+ src_data += src->bytes_per_line;
+ dest_data += dst->bytes_per_line;
+ }
+ }
+ int numColors = do_quant ? 256 : pix;
+
+ dst->colortable.resize(numColors);
+
+ if (do_quant) { // quantization needed
+
+#define MAX_R 5
+#define MAX_G 5
+#define MAX_B 5
+#define INDEXOF(r,g,b) (((r)*(MAX_G+1)+(g))*(MAX_B+1)+(b))
+
+ for (int rc=0; rc<=MAX_R; rc++) // build 6x6x6 color cube
+ for (int gc=0; gc<=MAX_G; gc++)
+ for (int bc=0; bc<=MAX_B; bc++)
+ dst->colortable[INDEXOF(rc,gc,bc)] = 0xff000000 | qRgb(rc*255/MAX_R, gc*255/MAX_G, bc*255/MAX_B);
+
+ const uchar *src_data = src->data;
+ uchar *dest_data = dst->data;
+ if ((flags & Qt::Dither_Mask) == Qt::ThresholdDither) {
+ for (int y = 0; y < src->height; y++) {
+ const QRgb *p = (const QRgb *)src_data;
+ const QRgb *end = p + src->width;
+ uchar *b = dest_data;
+
+ while (p < end) {
+#define DITHER(p,m) ((uchar) ((p * (m) + 127) / 255))
+ *b++ =
+ INDEXOF(
+ DITHER(qRed(*p), MAX_R),
+ DITHER(qGreen(*p), MAX_G),
+ DITHER(qBlue(*p), MAX_B)
+ );
+#undef DITHER
+ p++;
+ }
+ src_data += src->bytes_per_line;
+ dest_data += dst->bytes_per_line;
+ }
+ } else if ((flags & Qt::Dither_Mask) == Qt::DiffuseDither) {
+ int* line1[3];
+ int* line2[3];
+ int* pv[3];
+ line1[0] = new int[src->width];
+ line2[0] = new int[src->width];
+ line1[1] = new int[src->width];
+ line2[1] = new int[src->width];
+ line1[2] = new int[src->width];
+ line2[2] = new int[src->width];
+ pv[0] = new int[src->width];
+ pv[1] = new int[src->width];
+ pv[2] = new int[src->width];
+
+ int endian = (QSysInfo::ByteOrder == QSysInfo::BigEndian);
+ for (int y = 0; y < src->height; y++) {
+ const uchar* q = src_data;
+ const uchar* q2 = y < src->height - 1 ? q + src->bytes_per_line : src->data;
+ uchar *b = dest_data;
+ for (int chan = 0; chan < 3; chan++) {
+ int *l1 = (y&1) ? line2[chan] : line1[chan];
+ int *l2 = (y&1) ? line1[chan] : line2[chan];
+ if (y == 0) {
+ for (int i = 0; i < src->width; i++)
+ l1[i] = q[i*4+chan+endian];
+ }
+ if (y+1 < src->height) {
+ for (int i = 0; i < src->width; i++)
+ l2[i] = q2[i*4+chan+endian];
+ }
+ // Bi-directional error diffusion
+ if (y&1) {
+ for (int x = 0; x < src->width; x++) {
+ int pix = qMax(qMin(5, (l1[x] * 5 + 128)/ 255), 0);
+ int err = l1[x] - pix * 255 / 5;
+ pv[chan][x] = pix;
+
+ // Spread the error around...
+ if (x + 1< src->width) {
+ l1[x+1] += (err*7)>>4;
+ l2[x+1] += err>>4;
+ }
+ l2[x]+=(err*5)>>4;
+ if (x>1)
+ l2[x-1]+=(err*3)>>4;
+ }
+ } else {
+ for (int x = src->width; x-- > 0;) {
+ int pix = qMax(qMin(5, (l1[x] * 5 + 128)/ 255), 0);
+ int err = l1[x] - pix * 255 / 5;
+ pv[chan][x] = pix;
+
+ // Spread the error around...
+ if (x > 0) {
+ l1[x-1] += (err*7)>>4;
+ l2[x-1] += err>>4;
+ }
+ l2[x]+=(err*5)>>4;
+ if (x + 1 < src->width)
+ l2[x+1]+=(err*3)>>4;
+ }
+ }
+ }
+ if (endian) {
+ for (int x = 0; x < src->width; x++) {
+ *b++ = INDEXOF(pv[0][x],pv[1][x],pv[2][x]);
+ }
+ } else {
+ for (int x = 0; x < src->width; x++) {
+ *b++ = INDEXOF(pv[2][x],pv[1][x],pv[0][x]);
+ }
+ }
+ src_data += src->bytes_per_line;
+ dest_data += dst->bytes_per_line;
+ }
+ delete [] line1[0];
+ delete [] line2[0];
+ delete [] line1[1];
+ delete [] line2[1];
+ delete [] line1[2];
+ delete [] line2[2];
+ delete [] pv[0];
+ delete [] pv[1];
+ delete [] pv[2];
+ } else { // OrderedDither
+ for (int y = 0; y < src->height; y++) {
+ const QRgb *p = (const QRgb *)src_data;
+ const QRgb *end = p + src->width;
+ uchar *b = dest_data;
+
+ int x = 0;
+ while (p < end) {
+ uint d = qt_bayer_matrix[y & 15][x & 15] << 8;
+
+#define DITHER(p, d, m) ((uchar) ((((256 * (m) + (m) + 1)) * (p) + (d)) >> 16))
+ *b++ =
+ INDEXOF(
+ DITHER(qRed(*p), d, MAX_R),
+ DITHER(qGreen(*p), d, MAX_G),
+ DITHER(qBlue(*p), d, MAX_B)
+ );
+#undef DITHER
+
+ p++;
+ x++;
+ }
+ src_data += src->bytes_per_line;
+ dest_data += dst->bytes_per_line;
+ }
+ }
+
+ if (src->format != QImage::Format_RGB32
+ && src->format != QImage::Format_RGB16) {
+ const int trans = 216;
+ Q_ASSERT(dst->colortable.size() > trans);
+ dst->colortable[trans] = 0;
+ QImageData *mask = QImageData::create(QSize(src->width, src->height), QImage::Format_Mono);
+ dither_to_Mono(mask, src, flags, true);
+ uchar *dst_data = dst->data;
+ const uchar *mask_data = mask->data;
+ for (int y = 0; y < src->height; y++) {
+ for (int x = 0; x < src->width ; x++) {
+ if (!(mask_data[x>>3] & (0x80 >> (x & 7))))
+ dst_data[x] = trans;
+ }
+ mask_data += mask->bytes_per_line;
+ dst_data += dst->bytes_per_line;
+ }
+ dst->has_alpha_clut = true;
+ delete mask;
+ }
+
+#undef MAX_R
+#undef MAX_G
+#undef MAX_B
+#undef INDEXOF
+
+ }
+}
+
+static void convert_ARGB_PM_to_Indexed8(QImageData *dst, const QImageData *src, Qt::ImageConversionFlags flags)
+{
+ QImageData *tmp = QImageData::create(QSize(src->width, src->height), QImage::Format_ARGB32);
+ convert_ARGB_PM_to_ARGB(tmp, src, flags);
+ convert_RGB_to_Indexed8(dst, tmp, flags);
+ delete tmp;
+}
+
+static void convert_ARGB_to_Indexed8(QImageData *dst, const QImageData *src, Qt::ImageConversionFlags flags)
+{
+ convert_RGB_to_Indexed8(dst, src, flags);
+}
+
+static void convert_Indexed8_to_X32(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags)
+{
+ Q_ASSERT(src->format == QImage::Format_Indexed8);
+ Q_ASSERT(dest->format == QImage::Format_RGB32
+ || dest->format == QImage::Format_ARGB32
+ || dest->format == QImage::Format_ARGB32_Premultiplied);
+ Q_ASSERT(src->width == dest->width);
+ Q_ASSERT(src->height == dest->height);
+
+ QVector<QRgb> colorTable = fix_color_table(src->colortable, dest->format);
+
+ int w = src->width;
+ const uchar *src_data = src->data;
+ uchar *dest_data = dest->data;
+ for (int y = 0; y < src->height; y++) {
+ uint *p = (uint *)dest_data;
+ const uchar *b = src_data;
+ uint *end = p + w;
+
+ while (p < end)
+ *p++ = colorTable.at(*b++);
+
+ src_data += src->bytes_per_line;
+ dest_data += dest->bytes_per_line;
+ }
+}
+
+static void convert_Mono_to_X32(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags)
+{
+ Q_ASSERT(src->format == QImage::Format_Mono || src->format == QImage::Format_MonoLSB);
+ Q_ASSERT(dest->format == QImage::Format_RGB32
+ || dest->format == QImage::Format_ARGB32
+ || dest->format == QImage::Format_ARGB32_Premultiplied);
+ Q_ASSERT(src->width == dest->width);
+ Q_ASSERT(src->height == dest->height);
+
+ QVector<QRgb> colorTable = fix_color_table(src->colortable, dest->format);
+
+ // Default to black / white colors
+ if (colorTable.size() < 2) {
+ if (colorTable.size() == 0)
+ colorTable << 0xff000000;
+ colorTable << 0xffffffff;
+ }
+
+ const uchar *src_data = src->data;
+ uchar *dest_data = dest->data;
+ if (src->format == QImage::Format_Mono) {
+ for (int y = 0; y < dest->height; y++) {
+ register uint *p = (uint *)dest_data;
+ for (int x = 0; x < dest->width; x++)
+ *p++ = colorTable.at((src_data[x>>3] >> (7 - (x & 7))) & 1);
+
+ src_data += src->bytes_per_line;
+ dest_data += dest->bytes_per_line;
+ }
+ } else {
+ for (int y = 0; y < dest->height; y++) {
+ register uint *p = (uint *)dest_data;
+ for (int x = 0; x < dest->width; x++)
+ *p++ = colorTable.at((src_data[x>>3] >> (x & 7)) & 1);
+
+ src_data += src->bytes_per_line;
+ dest_data += dest->bytes_per_line;
+ }
+ }
+}
+
+
+static void convert_Mono_to_Indexed8(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags)
+{
+ Q_ASSERT(src->format == QImage::Format_Mono || src->format == QImage::Format_MonoLSB);
+ Q_ASSERT(dest->format == QImage::Format_Indexed8);
+ Q_ASSERT(src->width == dest->width);
+ Q_ASSERT(src->height == dest->height);
+
+ QVector<QRgb> ctbl = src->colortable;
+ if (ctbl.size() > 2) {
+ ctbl.resize(2);
+ } else if (ctbl.size() < 2) {
+ if (ctbl.size() == 0)
+ ctbl << 0xff000000;
+ ctbl << 0xffffffff;
+ }
+ dest->colortable = ctbl;
+ dest->has_alpha_clut = src->has_alpha_clut;
+
+
+ const uchar *src_data = src->data;
+ uchar *dest_data = dest->data;
+ if (src->format == QImage::Format_Mono) {
+ for (int y = 0; y < dest->height; y++) {
+ register uchar *p = dest_data;
+ for (int x = 0; x < dest->width; x++)
+ *p++ = (src_data[x>>3] >> (7 - (x & 7))) & 1;
+ src_data += src->bytes_per_line;
+ dest_data += dest->bytes_per_line;
+ }
+ } else {
+ for (int y = 0; y < dest->height; y++) {
+ register uchar *p = dest_data;
+ for (int x = 0; x < dest->width; x++)
+ *p++ = (src_data[x>>3] >> (x & 7)) & 1;
+ src_data += src->bytes_per_line;
+ dest_data += dest->bytes_per_line;
+ }
+ }
+}
+
+#define CONVERT_DECL(DST, SRC) \
+ static void convert_##SRC##_to_##DST(QImageData *dest, \
+ const QImageData *src, \
+ Qt::ImageConversionFlags) \
+ { \
+ qt_rectconvert<DST, SRC>(reinterpret_cast<DST*>(dest->data), \
+ reinterpret_cast<const SRC*>(src->data), \
+ 0, 0, src->width, src->height, \
+ dest->bytes_per_line, src->bytes_per_line); \
+ }
+
+CONVERT_DECL(quint32, quint16)
+CONVERT_DECL(quint16, quint32)
+CONVERT_DECL(quint32, qargb8565)
+CONVERT_DECL(qargb8565, quint32)
+CONVERT_DECL(quint32, qrgb555)
+CONVERT_DECL(qrgb666, quint32)
+CONVERT_DECL(quint32, qrgb666)
+CONVERT_DECL(qargb6666, quint32)
+CONVERT_DECL(quint32, qargb6666)
+CONVERT_DECL(qrgb555, quint32)
+#if !defined(Q_WS_QWS) || (defined(QT_QWS_DEPTH_15) && defined(QT_QWS_DEPTH_16))
+CONVERT_DECL(quint16, qrgb555)
+CONVERT_DECL(qrgb555, quint16)
+#endif
+CONVERT_DECL(quint32, qrgb888)
+CONVERT_DECL(qrgb888, quint32)
+CONVERT_DECL(quint32, qargb8555)
+CONVERT_DECL(qargb8555, quint32)
+CONVERT_DECL(quint32, qrgb444)
+CONVERT_DECL(qrgb444, quint32)
+CONVERT_DECL(quint32, qargb4444)
+CONVERT_DECL(qargb4444, quint32)
+#undef CONVERT_DECL
+#define CONVERT_PTR(DST, SRC) convert_##SRC##_to_##DST
+
+/*
+ Format_Invalid,
+ Format_Mono,
+ Format_MonoLSB,
+ Format_Indexed8,
+ Format_RGB32,
+ Format_ARGB32,
+ Format_ARGB32_Premultiplied,
+ Format_RGB16,
+ Format_ARGB8565_Premultiplied,
+ Format_RGB666,
+ Format_ARGB6666_Premultiplied,
+ Format_RGB555,
+ Format_ARGB8555_Premultiplied,
+ Format_RGB888
+ Format_RGB444
+ Format_ARGB4444_Premultiplied
+*/
+
+
+// first index source, second dest
+static const Image_Converter converter_map[QImage::NImageFormats][QImage::NImageFormats] =
+{
+ {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ },
+ {
+ 0,
+ 0,
+ swap_bit_order,
+ convert_Mono_to_Indexed8,
+ convert_Mono_to_X32,
+ convert_Mono_to_X32,
+ convert_Mono_to_X32,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0
+ }, // Format_Mono
+
+ {
+ 0,
+ swap_bit_order,
+ 0,
+ convert_Mono_to_Indexed8,
+ convert_Mono_to_X32,
+ convert_Mono_to_X32,
+ convert_Mono_to_X32,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0
+ }, // Format_MonoLSB
+
+ {
+ 0,
+ convert_X_to_Mono,
+ convert_X_to_Mono,
+ 0,
+ convert_Indexed8_to_X32,
+ convert_Indexed8_to_X32,
+ convert_Indexed8_to_X32,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0
+ }, // Format_Indexed8
+
+ {
+ 0,
+ convert_X_to_Mono,
+ convert_X_to_Mono,
+ convert_RGB_to_Indexed8,
+ 0,
+ mask_alpha_converter,
+ mask_alpha_converter,
+ CONVERT_PTR(quint16, quint32),
+ CONVERT_PTR(qargb8565, quint32),
+ CONVERT_PTR(qrgb666, quint32),
+ CONVERT_PTR(qargb6666, quint32),
+ CONVERT_PTR(qrgb555, quint32),
+ CONVERT_PTR(qargb8555, quint32),
+ CONVERT_PTR(qrgb888, quint32),
+ CONVERT_PTR(qrgb444, quint32),
+ CONVERT_PTR(qargb4444, quint32)
+ }, // Format_RGB32
+
+ {
+ 0,
+ convert_X_to_Mono,
+ convert_X_to_Mono,
+ convert_ARGB_to_Indexed8,
+ mask_alpha_converter,
+ 0,
+ convert_ARGB_to_ARGB_PM,
+ CONVERT_PTR(quint16, quint32),
+ CONVERT_PTR(qargb8565, quint32),
+ CONVERT_PTR(qrgb666, quint32),
+ CONVERT_PTR(qargb6666, quint32),
+ CONVERT_PTR(qrgb555, quint32),
+ CONVERT_PTR(qargb8555, quint32),
+ CONVERT_PTR(qrgb888, quint32),
+ CONVERT_PTR(qrgb444, quint32),
+ CONVERT_PTR(qargb4444, quint32)
+ }, // Format_ARGB32
+
+ {
+ 0,
+ convert_ARGB_PM_to_Mono,
+ convert_ARGB_PM_to_Mono,
+ convert_ARGB_PM_to_Indexed8,
+ convert_ARGB_PM_to_RGB,
+ convert_ARGB_PM_to_ARGB,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0
+ }, // Format_ARGB32_Premultiplied
+
+ {
+ 0,
+ 0,
+ 0,
+ 0,
+ CONVERT_PTR(quint32, quint16),
+ CONVERT_PTR(quint32, quint16),
+ CONVERT_PTR(quint32, quint16),
+ 0,
+ 0,
+ 0,
+ 0,
+#if !defined(Q_WS_QWS) || (defined(QT_QWS_DEPTH_15) && defined(QT_QWS_DEPTH_16))
+ CONVERT_PTR(qrgb555, quint16),
+#else
+ 0,
+#endif
+ 0,
+ 0,
+ 0,
+ 0
+ }, // Format_RGB16
+
+ {
+ 0,
+ 0,
+ 0,
+ 0,
+ CONVERT_PTR(quint32, qargb8565),
+ CONVERT_PTR(quint32, qargb8565),
+ CONVERT_PTR(quint32, qargb8565),
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0
+ }, // Format_ARGB8565_Premultiplied
+
+ {
+ 0,
+ 0,
+ 0,
+ 0,
+ CONVERT_PTR(quint32, qrgb666),
+ CONVERT_PTR(quint32, qrgb666),
+ CONVERT_PTR(quint32, qrgb666),
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0
+ }, // Format_RGB666
+
+ {
+ 0,
+ 0,
+ 0,
+ 0,
+ CONVERT_PTR(quint32, qargb6666),
+ CONVERT_PTR(quint32, qargb6666),
+ CONVERT_PTR(quint32, qargb6666),
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0
+ }, // Format_ARGB6666_Premultiplied
+
+ {
+ 0,
+ 0,
+ 0,
+ 0,
+ CONVERT_PTR(quint32, qrgb555),
+ CONVERT_PTR(quint32, qrgb555),
+ CONVERT_PTR(quint32, qrgb555),
+#if !defined(Q_WS_QWS) || (defined(QT_QWS_DEPTH_15) && defined(QT_QWS_DEPTH_16))
+ CONVERT_PTR(quint16, qrgb555),
+#else
+ 0,
+#endif
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0
+ }, // Format_RGB555
+
+ {
+ 0,
+ 0,
+ 0,
+ 0,
+ CONVERT_PTR(quint32, qargb8555),
+ CONVERT_PTR(quint32, qargb8555),
+ CONVERT_PTR(quint32, qargb8555),
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0
+ }, // Format_ARGB8555_Premultiplied
+
+ {
+ 0,
+ 0,
+ 0,
+ 0,
+ CONVERT_PTR(quint32, qrgb888),
+ CONVERT_PTR(quint32, qrgb888),
+ CONVERT_PTR(quint32, qrgb888),
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0
+ }, // Format_RGB888
+
+ {
+ 0,
+ 0,
+ 0,
+ 0,
+ CONVERT_PTR(quint32, qrgb444),
+ CONVERT_PTR(quint32, qrgb444),
+ CONVERT_PTR(quint32, qrgb444),
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0
+ }, // Format_RGB444
+
+ {
+ 0,
+ 0,
+ 0,
+ 0,
+ CONVERT_PTR(quint32, qargb4444),
+ CONVERT_PTR(quint32, qargb4444),
+ CONVERT_PTR(quint32, qargb4444),
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0
+ } // Format_ARGB4444_Premultiplied
+};
+
+/*!
+ Returns a copy of the image in the given \a format.
+
+ The specified image conversion \a flags control how the image data
+ is handled during the conversion process.
+
+ \sa {QImage#Image Format}{Image Format}
+*/
+QImage QImage::convertToFormat(Format format, Qt::ImageConversionFlags flags) const
+{
+ if (!d || d->format == format)
+ return *this;
+
+ if (format == Format_Invalid || d->format == Format_Invalid)
+ return QImage();
+
+ const Image_Converter *converterPtr = &converter_map[d->format][format];
+ Image_Converter converter = *converterPtr;
+ if (converter) {
+ QImage image(d->width, d->height, format);
+
+ QIMAGE_SANITYCHECK_MEMORY(image);
+
+ image.setDotsPerMeterY(dotsPerMeterY());
+ image.setDotsPerMeterX(dotsPerMeterX());
+
+#if !defined(QT_NO_IMAGE_TEXT)
+ image.d->text = d->text;
+#endif // !QT_NO_IMAGE_TEXT
+
+ converter(image.d, d, flags);
+ return image;
+ }
+
+ Q_ASSERT(format != QImage::Format_ARGB32);
+ Q_ASSERT(d->format != QImage::Format_ARGB32);
+
+ QImage image = convertToFormat(Format_ARGB32, flags);
+ return image.convertToFormat(format, flags);
+}
+
+
+
+static inline int pixel_distance(QRgb p1, QRgb p2) {
+ int r1 = qRed(p1);
+ int g1 = qGreen(p1);
+ int b1 = qBlue(p1);
+ int a1 = qAlpha(p1);
+
+ int r2 = qRed(p2);
+ int g2 = qGreen(p2);
+ int b2 = qBlue(p2);
+ int a2 = qAlpha(p2);
+
+ return abs(r1 - r2) + abs(g1 - g2) + abs(b1 - b2) + abs(a1 - a2);
+}
+
+static inline int closestMatch(QRgb pixel, const QVector<QRgb> &clut) {
+ int idx = 0;
+ int current_distance = INT_MAX;
+ for (int i=0; i<clut.size(); ++i) {
+ int dist = pixel_distance(pixel, clut.at(i));
+ if (dist < current_distance) {
+ current_distance = dist;
+ idx = i;
+ }
+ }
+ return idx;
+}
+
+static QImage convertWithPalette(const QImage &src, QImage::Format format,
+ const QVector<QRgb> &clut) {
+ QImage dest(src.size(), format);
+ dest.setColorTable(clut);
+
+#if !defined(QT_NO_IMAGE_TEXT)
+ QString textsKeys = src.text();
+ QStringList textKeyList = textsKeys.split(QLatin1Char('\n'), QString::SkipEmptyParts);
+ foreach (const QString &textKey, textKeyList) {
+ QStringList textKeySplitted = textKey.split(QLatin1String(": "));
+ dest.setText(textKeySplitted[0], textKeySplitted[1]);
+ }
+#endif // !QT_NO_IMAGE_TEXT
+
+ int h = src.height();
+ int w = src.width();
+
+ QHash<QRgb, int> cache;
+
+ if (format == QImage::Format_Indexed8) {
+ for (int y=0; y<h; ++y) {
+ QRgb *src_pixels = (QRgb *) src.scanLine(y);
+ uchar *dest_pixels = (uchar *) dest.scanLine(y);
+ for (int x=0; x<w; ++x) {
+ int src_pixel = src_pixels[x];
+ int value = cache.value(src_pixel, -1);
+ if (value == -1) {
+ value = closestMatch(src_pixel, clut);
+ cache.insert(src_pixel, value);
+ }
+ dest_pixels[x] = (uchar) value;
+ }
+ }
+ } else {
+ QVector<QRgb> table = clut;
+ table.resize(2);
+ for (int y=0; y<h; ++y) {
+ QRgb *src_pixels = (QRgb *) src.scanLine(y);
+ for (int x=0; x<w; ++x) {
+ int src_pixel = src_pixels[x];
+ int value = cache.value(src_pixel, -1);
+ if (value == -1) {
+ value = closestMatch(src_pixel, table);
+ cache.insert(src_pixel, value);
+ }
+ dest.setPixel(x, y, value);
+ }
+ }
+ }
+
+ return dest;
+}
+
+/*!
+ \overload
+
+ Returns a copy of the image converted to the given \a format,
+ using the specified \a colorTable.
+
+ Conversion from 32 bit to 8 bit indexed is a slow operation and
+ will use a straightforward nearest color approach, with no
+ dithering.
+*/
+QImage QImage::convertToFormat(Format format, const QVector<QRgb> &colorTable, Qt::ImageConversionFlags flags) const
+{
+ if (d->format == format)
+ return *this;
+
+ if (format <= QImage::Format_Indexed8 && depth() == 32) {
+ return convertWithPalette(*this, format, colorTable);
+ }
+
+ const Image_Converter *converterPtr = &converter_map[d->format][format];
+ Image_Converter converter = *converterPtr;
+ if (!converter)
+ return QImage();
+
+ QImage image(d->width, d->height, format);
+ QIMAGE_SANITYCHECK_MEMORY(image);
+
+#if !defined(QT_NO_IMAGE_TEXT)
+ image.d->text = d->text;
+#endif // !QT_NO_IMAGE_TEXT
+
+ converter(image.d, d, flags);
+ return image;
+}
+
+#ifdef QT3_SUPPORT
+/*!
+ Converts the depth (bpp) of the image to the given \a depth and
+ returns the converted image. The original image is not changed.
+ Returns this image if \a depth is equal to the image depth, or a
+ null image if this image cannot be converted. The \a depth
+ argument must be 1, 8 or 32. If the image needs to be modified to
+ fit in a lower-resolution result (e.g. converting from 32-bit to
+ 8-bit), use the \a flags to specify how you'd prefer this to
+ happen.
+
+ Use the convertToFormat() function instead.
+*/
+
+QImage QImage::convertDepth(int depth, Qt::ImageConversionFlags flags) const
+{
+ if (!d || d->depth == depth)
+ return *this;
+
+ Format format = formatFor (depth, QImage::LittleEndian);
+ return convertToFormat(format, flags);
+}
+#endif
+
+/*!
+ \fn bool QImage::valid(const QPoint &pos) const
+
+ Returns true if \a pos is a valid coordinate pair within the
+ image; otherwise returns false.
+
+ \sa rect(), QRect::contains()
+*/
+
+/*!
+ \overload
+
+ Returns true if QPoint(\a x, \a y) is a valid coordinate pair
+ within the image; otherwise returns false.
+*/
+bool QImage::valid(int x, int y) const
+{
+ return d
+ && x >= 0 && x < d->width
+ && y >= 0 && y < d->height;
+}
+
+/*!
+ \fn int QImage::pixelIndex(const QPoint &position) const
+
+ Returns the pixel index at the given \a position.
+
+ If \a position is not valid, or if the image is not a paletted
+ image (depth() > 8), the results are undefined.
+
+ \sa valid(), depth(), {QImage#Pixel Manipulation}{Pixel Manipulation}
+*/
+
+/*!
+ \overload
+
+ Returns the pixel index at (\a x, \a y).
+*/
+int QImage::pixelIndex(int x, int y) const
+{
+ if (!d || x < 0 || x >= d->width || y < 0 || y >= height()) {
+ qWarning("QImage::pixelIndex: coordinate (%d,%d) out of range", x, y);
+ return -12345;
+ }
+ const uchar * s = scanLine(y);
+ switch(d->format) {
+ case Format_Mono:
+ return (*(s + (x >> 3)) >> (7- (x & 7))) & 1;
+ case Format_MonoLSB:
+ return (*(s + (x >> 3)) >> (x & 7)) & 1;
+ case Format_Indexed8:
+ return (int)s[x];
+ default:
+ qWarning("QImage::pixelIndex: Not applicable for %d-bpp images (no palette)", d->depth);
+ }
+ return 0;
+}
+
+
+/*!
+ \fn QRgb QImage::pixel(const QPoint &position) const
+
+ Returns the color of the pixel at the given \a position.
+
+ If the \a position is not valid, the results are undefined.
+
+ \sa setPixel(), valid(), {QImage#Pixel Manipulation}{Pixel
+ Manipulation}
+*/
+
+/*!
+ \overload
+
+ Returns the color of the pixel at coordinates (\a x, \a y).
+*/
+QRgb QImage::pixel(int x, int y) const
+{
+ if (!d || x < 0 || x >= d->width || y < 0 || y >= height()) {
+ qWarning("QImage::pixel: coordinate (%d,%d) out of range", x, y);
+ return 12345;
+ }
+ const uchar * s = scanLine(y);
+ switch(d->format) {
+ case Format_Mono:
+ return d->colortable.at((*(s + (x >> 3)) >> (7- (x & 7))) & 1);
+ case Format_MonoLSB:
+ return d->colortable.at((*(s + (x >> 3)) >> (x & 7)) & 1);
+ case Format_Indexed8:
+ return d->colortable.at((int)s[x]);
+ case Format_ARGB8565_Premultiplied:
+ return qt_colorConvert<quint32, qargb8565>(reinterpret_cast<const qargb8565*>(s)[x], 0);
+ case Format_RGB666:
+ return qt_colorConvert<quint32, qrgb666>(reinterpret_cast<const qrgb666*>(s)[x], 0);
+ case Format_ARGB6666_Premultiplied:
+ return qt_colorConvert<quint32, qargb6666>(reinterpret_cast<const qargb6666*>(s)[x], 0);
+ case Format_RGB555:
+ return qt_colorConvert<quint32, qrgb555>(reinterpret_cast<const qrgb555*>(s)[x], 0);
+ case Format_ARGB8555_Premultiplied:
+ return qt_colorConvert<quint32, qargb8555>(reinterpret_cast<const qargb8555*>(s)[x], 0);
+ case Format_RGB888:
+ return qt_colorConvert<quint32, qrgb888>(reinterpret_cast<const qrgb888*>(s)[x], 0);
+ case Format_RGB444:
+ return qt_colorConvert<quint32, qrgb444>(reinterpret_cast<const qrgb444*>(s)[x], 0);
+ case Format_ARGB4444_Premultiplied:
+ return qt_colorConvert<quint32, qargb4444>(reinterpret_cast<const qargb4444*>(s)[x], 0);
+ case Format_RGB16:
+ return qt_colorConvert<quint32, quint16>(reinterpret_cast<const quint16*>(s)[x], 0);
+ default:
+ return ((QRgb*)s)[x];
+ }
+}
+
+
+/*!
+ \fn void QImage::setPixel(const QPoint &position, uint index_or_rgb)
+
+ Sets the pixel index or color at the given \a position to \a
+ index_or_rgb.
+
+ If the image's format is either monochrome or 8-bit, the given \a
+ index_or_rgb value must be an index in the image's color table,
+ otherwise the parameter must be a QRgb value.
+
+ If \a position is not a valid coordinate pair in the image, or if
+ \a index_or_rgb >= numColors() in the case of monochrome and
+ 8-bit images, the result is undefined.
+
+ \warning This function is expensive due to the call of the internal
+ \c{detach()} function called within; if performance is a concern, we
+ recommend the use of \l{QImage::}{scanLine()} to access pixel data
+ directly.
+
+ \sa pixel(), {QImage#Pixel Manipulation}{Pixel Manipulation}
+*/
+
+/*!
+ \overload
+
+ Sets the pixel index or color at (\a x, \a y) to \a index_or_rgb.
+*/
+void QImage::setPixel(int x, int y, uint index_or_rgb)
+{
+ if (!d || x < 0 || x >= width() || y < 0 || y >= height()) {
+ qWarning("QImage::setPixel: coordinate (%d,%d) out of range", x, y);
+ return;
+ }
+ // detach is called from within scanLine
+ uchar * s = scanLine(y);
+ const quint32p p = quint32p::fromRawData(index_or_rgb);
+ switch(d->format) {
+ case Format_Mono:
+ case Format_MonoLSB:
+ if (index_or_rgb > 1) {
+ qWarning("QImage::setPixel: Index %d out of range", index_or_rgb);
+ } else if (format() == Format_MonoLSB) {
+ if (index_or_rgb==0)
+ *(s + (x >> 3)) &= ~(1 << (x & 7));
+ else
+ *(s + (x >> 3)) |= (1 << (x & 7));
+ } else {
+ if (index_or_rgb==0)
+ *(s + (x >> 3)) &= ~(1 << (7-(x & 7)));
+ else
+ *(s + (x >> 3)) |= (1 << (7-(x & 7)));
+ }
+ break;
+ case Format_Indexed8:
+ if (index_or_rgb > (uint)d->colortable.size()) {
+ qWarning("QImage::setPixel: Index %d out of range", index_or_rgb);
+ return;
+ }
+ s[x] = index_or_rgb;
+ break;
+ case Format_RGB32:
+ //make sure alpha is 255, we depend on it in qdrawhelper for cases
+ // when image is set as a texture pattern on a qbrush
+ ((uint *)s)[x] = uint(255 << 24) | index_or_rgb;
+ break;
+ case Format_ARGB32:
+ case Format_ARGB32_Premultiplied:
+ ((uint *)s)[x] = index_or_rgb;
+ break;
+ case Format_RGB16:
+ ((quint16 *)s)[x] = qt_colorConvert<quint16, quint32p>(p, 0);
+ break;
+ case Format_ARGB8565_Premultiplied:
+ ((qargb8565*)s)[x] = qt_colorConvert<qargb8565, quint32p>(p, 0);
+ break;
+ case Format_RGB666:
+ ((qrgb666*)s)[x] = qt_colorConvert<qrgb666, quint32p>(p, 0);
+ break;
+ case Format_ARGB6666_Premultiplied:
+ ((qargb6666*)s)[x] = qt_colorConvert<qargb6666, quint32p>(p, 0);
+ break;
+ case Format_RGB555:
+ ((qrgb555*)s)[x] = qt_colorConvert<qrgb555, quint32p>(p, 0);
+ break;
+ case Format_ARGB8555_Premultiplied:
+ ((qargb8555*)s)[x] = qt_colorConvert<qargb8555, quint32p>(p, 0);
+ break;
+ case Format_RGB888:
+ ((qrgb888*)s)[x] = qt_colorConvert<qrgb888, quint32p>(p, 0);
+ break;
+ case Format_RGB444:
+ ((qrgb444*)s)[x] = qt_colorConvert<qrgb444, quint32p>(p, 0);
+ break;
+ case Format_ARGB4444_Premultiplied:
+ ((qargb4444*)s)[x] = qt_colorConvert<qargb4444, quint32p>(p, 0);
+ break;
+ case Format_Invalid:
+ case NImageFormats:
+ Q_ASSERT(false);
+ }
+}
+
+#ifdef QT3_SUPPORT
+/*!
+ Converts the bit order of the image to the given \a bitOrder and
+ returns the converted image. The original image is not changed.
+ Returns this image if the given \a bitOrder is equal to the image
+ current bit order, or a null image if this image cannot be
+ converted.
+
+ Use convertToFormat() instead.
+*/
+
+QImage QImage::convertBitOrder(Endian bitOrder) const
+{
+ if (!d || isNull() || d->depth != 1 || !(bitOrder == BigEndian || bitOrder == LittleEndian))
+ return QImage();
+
+ if ((d->format == Format_Mono && bitOrder == BigEndian)
+ || (d->format == Format_MonoLSB && bitOrder == LittleEndian))
+ return *this;
+
+ QImage image(d->width, d->height, d->format == Format_Mono ? Format_MonoLSB : Format_Mono);
+
+ const uchar *data = d->data;
+ const uchar *end = data + d->nbytes;
+ uchar *ndata = image.d->data;
+ while (data < end)
+ *ndata++ = bitflip[*data++];
+
+ image.setDotsPerMeterX(dotsPerMeterX());
+ image.setDotsPerMeterY(dotsPerMeterY());
+
+ image.d->colortable = d->colortable;
+ return image;
+}
+#endif
+/*!
+ Returns true if all the colors in the image are shades of gray
+ (i.e. their red, green and blue components are equal); otherwise
+ false.
+
+ Note that this function is slow for images without color table.
+
+ \sa isGrayscale()
+*/
+bool QImage::allGray() const
+{
+ if (!d)
+ return true;
+
+ if (d->depth == 32) {
+ int p = width()*height();
+ const QRgb* b = (const QRgb*)bits();
+ while (p--)
+ if (!qIsGray(*b++))
+ return false;
+ } else if (d->depth == 16) {
+ int p = width()*height();
+ const ushort* b = (const ushort *)bits();
+ while (p--)
+ if (!qIsGray(qt_colorConvert<quint32, quint16>(*b++, 0)))
+ return false;
+ } else if (d->format == QImage::Format_RGB888) {
+ int p = width()*height();
+ const qrgb888* b = (const qrgb888 *)bits();
+ while (p--)
+ if (!qIsGray(qt_colorConvert<quint32, qrgb888>(*b++, 0)))
+ return false;
+ } else {
+ if (d->colortable.isEmpty())
+ return true;
+ for (int i = 0; i < numColors(); i++)
+ if (!qIsGray(d->colortable.at(i)))
+ return false;
+ }
+ return true;
+}
+
+/*!
+ For 32-bit images, this function is equivalent to allGray().
+
+ For 8-bpp images, this function returns true if color(i) is
+ QRgb(i, i, i) for all indexes of the color table; otherwise
+ returns false.
+
+ \sa allGray(), {QImage#Image Formats}{Image Formats}
+*/
+bool QImage::isGrayscale() const
+{
+ if (!d)
+ return false;
+
+ switch (depth()) {
+ case 32:
+ case 24:
+ case 16:
+ return allGray();
+ case 8: {
+ for (int i = 0; i < numColors(); i++)
+ if (d->colortable.at(i) != qRgb(i,i,i))
+ return false;
+ return true;
+ }
+ }
+ return false;
+}
+
+
+/*!
+ \fn QImage QImage::smoothScale(int width, int height, Qt::AspectRatioMode mode) const
+
+ Use scaled() instead.
+
+ \oldcode
+ QImage image;
+ image.smoothScale(width, height, mode);
+ \newcode
+ QImage image;
+ image.scaled(width, height, mode, Qt::SmoothTransformation);
+ \endcode
+*/
+
+/*!
+ \fn QImage QImage::smoothScale(const QSize &size, Qt::AspectRatioMode mode) const
+ \overload
+
+ Use scaled() instead.
+
+ \oldcode
+ QImage image;
+ image.smoothScale(size, mode);
+ \newcode
+ QImage image;
+ image.scaled(size, mode, Qt::SmoothTransformation);
+ \endcode
+*/
+
+/*!
+ \fn QImage QImage::scaled(int width, int height, Qt::AspectRatioMode aspectRatioMode,
+ Qt::TransformationMode transformMode) const
+ \overload
+
+ Returns a copy of the image scaled to a rectangle with the given
+ \a width and \a height according to the given \a aspectRatioMode
+ and \a transformMode.
+
+ If either the \a width or the \a height is zero or negative, this
+ function returns a null image.
+*/
+
+/*!
+ \fn QImage QImage::scaled(const QSize &size, Qt::AspectRatioMode aspectRatioMode,
+ Qt::TransformationMode transformMode) const
+
+ Returns a copy of the image scaled to a rectangle defined by the
+ given \a size according to the given \a aspectRatioMode and \a
+ transformMode.
+
+ \image qimage-scaling.png
+
+ \list
+ \i If \a aspectRatioMode is Qt::IgnoreAspectRatio, the image
+ is scaled to \a size.
+ \i If \a aspectRatioMode is Qt::KeepAspectRatio, the image is
+ scaled to a rectangle as large as possible inside \a size, preserving the aspect ratio.
+ \i If \a aspectRatioMode is Qt::KeepAspectRatioByExpanding,
+ the image is scaled to a rectangle as small as possible
+ outside \a size, preserving the aspect ratio.
+ \endlist
+
+ If the given \a size is empty, this function returns a null image.
+
+ \sa isNull(), {QImage#Image Transformations}{Image
+ Transformations}
+*/
+QImage QImage::scaled(const QSize& s, Qt::AspectRatioMode aspectMode, Qt::TransformationMode mode) const
+{
+ if (!d) {
+ qWarning("QImage::scaled: Image is a null image");
+ return QImage();
+ }
+ if (s.isEmpty())
+ return QImage();
+
+ QSize newSize = size();
+ newSize.scale(s, aspectMode);
+ if (newSize == size())
+ return copy();
+
+ QImage img;
+ QTransform wm;
+ wm.scale((qreal)newSize.width() / width(), (qreal)newSize.height() / height());
+ img = transformed(wm, mode);
+ return img;
+}
+
+/*!
+ \fn QImage QImage::scaledToWidth(int width, Qt::TransformationMode mode) const
+
+ Returns a scaled copy of the image. The returned image is scaled
+ to the given \a width using the specified transformation \a
+ mode.
+
+ This function automatically calculates the height of the image so
+ that its aspect ratio is preserved.
+
+ If the given \a width is 0 or negative, a null image is returned.
+
+ \sa {QImage#Image Transformations}{Image Transformations}
+*/
+QImage QImage::scaledToWidth(int w, Qt::TransformationMode mode) const
+{
+ if (!d) {
+ qWarning("QImage::scaleWidth: Image is a null image");
+ return QImage();
+ }
+ if (w <= 0)
+ return QImage();
+
+ QTransform wm;
+ qreal factor = (qreal) w / width();
+ wm.scale(factor, factor);
+ return transformed(wm, mode);
+}
+
+/*!
+ \fn QImage QImage::scaledToHeight(int height, Qt::TransformationMode mode) const
+
+ Returns a scaled copy of the image. The returned image is scaled
+ to the given \a height using the specified transformation \a
+ mode.
+
+ This function automatically calculates the width of the image so that
+ the ratio of the image is preserved.
+
+ If the given \a height is 0 or negative, a null image is returned.
+
+ \sa {QImage#Image Transformations}{Image Transformations}
+*/
+QImage QImage::scaledToHeight(int h, Qt::TransformationMode mode) const
+{
+ if (!d) {
+ qWarning("QImage::scaleHeight: Image is a null image");
+ return QImage();
+ }
+ if (h <= 0)
+ return QImage();
+
+ QTransform wm;
+ qreal factor = (qreal) h / height();
+ wm.scale(factor, factor);
+ return transformed(wm, mode);
+}
+
+
+/*!
+ \fn QMatrix QImage::trueMatrix(const QMatrix &matrix, int width, int height)
+
+ Returns the actual matrix used for transforming an image with the
+ given \a width, \a height and \a matrix.
+
+ When transforming an image using the transformed() function, the
+ transformation matrix is internally adjusted to compensate for
+ unwanted translation, i.e. transformed() returns the smallest
+ image containing all transformed points of the original image.
+ This function returns the modified matrix, which maps points
+ correctly from the original image into the new image.
+
+ \sa transformed(), {QImage#Image Transformations}{Image
+ Transformations}
+*/
+QMatrix QImage::trueMatrix(const QMatrix &matrix, int w, int h)
+{
+ return trueMatrix(QTransform(matrix), w, h).toAffine();
+}
+
+/*!
+ Returns a copy of the image that is transformed using the given
+ transformation \a matrix and transformation \a mode.
+
+ The transformation \a matrix is internally adjusted to compensate
+ for unwanted translation; i.e. the image produced is the smallest
+ image that contains all the transformed points of the original
+ image. Use the trueMatrix() function to retrieve the actual matrix
+ used for transforming an image.
+
+ \sa trueMatrix(), {QImage#Image Transformations}{Image
+ Transformations}
+*/
+QImage QImage::transformed(const QMatrix &matrix, Qt::TransformationMode mode) const
+{
+ return transformed(QTransform(matrix), mode);
+}
+
+/*!
+ Builds and returns a 1-bpp mask from the alpha buffer in this
+ image. Returns a null image if the image's format is
+ QImage::Format_RGB32.
+
+ The \a flags argument is a bitwise-OR of the
+ Qt::ImageConversionFlags, and controls the conversion
+ process. Passing 0 for flags sets all the default options.
+
+ The returned image has little-endian bit order (i.e. the image's
+ format is QImage::Format_MonoLSB), which you can convert to
+ big-endian (QImage::Format_Mono) using the convertToFormat()
+ function.
+
+ \sa createHeuristicMask(), {QImage#Image Transformations}{Image
+ Transformations}
+*/
+QImage QImage::createAlphaMask(Qt::ImageConversionFlags flags) const
+{
+ if (!d || d->format == QImage::Format_RGB32)
+ return QImage();
+
+ if (d->depth == 1) {
+ // A monochrome pixmap, with alpha channels on those two colors.
+ // Pretty unlikely, so use less efficient solution.
+ return convertToFormat(Format_Indexed8, flags).createAlphaMask(flags);
+ }
+
+ QImage mask(d->width, d->height, Format_MonoLSB);
+ dither_to_Mono(mask.d, d, flags, true);
+ return mask;
+}
+
+#ifndef QT_NO_IMAGE_HEURISTIC_MASK
+/*!
+ Creates and returns a 1-bpp heuristic mask for this image.
+
+ The function works by selecting a color from one of the corners,
+ then chipping away pixels of that color starting at all the edges.
+ The four corners vote for which color is to be masked away. In
+ case of a draw (this generally means that this function is not
+ applicable to the image), the result is arbitrary.
+
+ The returned image has little-endian bit order (i.e. the image's
+ format is QImage::Format_MonoLSB), which you can convert to
+ big-endian (QImage::Format_Mono) using the convertToFormat()
+ function.
+
+ If \a clipTight is true (the default) the mask is just large
+ enough to cover the pixels; otherwise, the mask is larger than the
+ data pixels.
+
+ Note that this function disregards the alpha buffer.
+
+ \sa createAlphaMask(), {QImage#Image Transformations}{Image
+ Transformations}
+*/
+
+QImage QImage::createHeuristicMask(bool clipTight) const
+{
+ if (!d)
+ return QImage();
+
+ if (d->depth != 32) {
+ QImage img32 = convertToFormat(Format_RGB32);
+ return img32.createHeuristicMask(clipTight);
+ }
+
+#define PIX(x,y) (*((QRgb*)scanLine(y)+x) & 0x00ffffff)
+
+ int w = width();
+ int h = height();
+ QImage m(w, h, Format_MonoLSB);
+ m.setNumColors(2);
+ m.setColor(0, QColor(Qt::color0).rgba());
+ m.setColor(1, QColor(Qt::color1).rgba());
+ m.fill(0xff);
+
+ QRgb background = PIX(0,0);
+ if (background != PIX(w-1,0) &&
+ background != PIX(0,h-1) &&
+ background != PIX(w-1,h-1)) {
+ background = PIX(w-1,0);
+ if (background != PIX(w-1,h-1) &&
+ background != PIX(0,h-1) &&
+ PIX(0,h-1) == PIX(w-1,h-1)) {
+ background = PIX(w-1,h-1);
+ }
+ }
+
+ int x,y;
+ bool done = false;
+ uchar *ypp, *ypc, *ypn;
+ while(!done) {
+ done = true;
+ ypn = m.scanLine(0);
+ ypc = 0;
+ for (y = 0; y < h; y++) {
+ ypp = ypc;
+ ypc = ypn;
+ ypn = (y == h-1) ? 0 : m.scanLine(y+1);
+ QRgb *p = (QRgb *)scanLine(y);
+ for (x = 0; x < w; x++) {
+ // slowness here - it's possible to do six of these tests
+ // together in one go. oh well.
+ if ((x == 0 || y == 0 || x == w-1 || y == h-1 ||
+ !(*(ypc + ((x-1) >> 3)) & (1 << ((x-1) & 7))) ||
+ !(*(ypc + ((x+1) >> 3)) & (1 << ((x+1) & 7))) ||
+ !(*(ypp + (x >> 3)) & (1 << (x & 7))) ||
+ !(*(ypn + (x >> 3)) & (1 << (x & 7)))) &&
+ ( (*(ypc + (x >> 3)) & (1 << (x & 7)))) &&
+ ((*p & 0x00ffffff) == background)) {
+ done = false;
+ *(ypc + (x >> 3)) &= ~(1 << (x & 7));
+ }
+ p++;
+ }
+ }
+ }
+
+ if (!clipTight) {
+ ypn = m.scanLine(0);
+ ypc = 0;
+ for (y = 0; y < h; y++) {
+ ypp = ypc;
+ ypc = ypn;
+ ypn = (y == h-1) ? 0 : m.scanLine(y+1);
+ QRgb *p = (QRgb *)scanLine(y);
+ for (x = 0; x < w; x++) {
+ if ((*p & 0x00ffffff) != background) {
+ if (x > 0)
+ *(ypc + ((x-1) >> 3)) |= (1 << ((x-1) & 7));
+ if (x < w-1)
+ *(ypc + ((x+1) >> 3)) |= (1 << ((x+1) & 7));
+ if (y > 0)
+ *(ypp + (x >> 3)) |= (1 << (x & 7));
+ if (y < h-1)
+ *(ypn + (x >> 3)) |= (1 << (x & 7));
+ }
+ p++;
+ }
+ }
+ }
+
+#undef PIX
+
+ return m;
+}
+#endif //QT_NO_IMAGE_HEURISTIC_MASK
+
+/*!
+ Creates and returns a mask for this image based on the given \a
+ color value. If the \a mode is MaskInColor (the default value),
+ all pixels matching \a color will be opaque pixels in the mask. If
+ \a mode is MaskOutColor, all pixels matching the given color will
+ be transparent.
+
+ \sa createAlphaMask(), createHeuristicMask()
+*/
+
+QImage QImage::createMaskFromColor(QRgb color, Qt::MaskMode mode) const
+{
+ if (!d)
+ return QImage();
+ QImage maskImage(size(), QImage::Format_MonoLSB);
+ maskImage.fill(0);
+ uchar *s = maskImage.bits();
+
+ if (depth() == 32) {
+ for (int h = 0; h < d->height; h++) {
+ const uint *sl = (uint *) scanLine(h);
+ for (int w = 0; w < d->width; w++) {
+ if (sl[w] == color)
+ *(s + (w >> 3)) |= (1 << (w & 7));
+ }
+ s += maskImage.bytesPerLine();
+ }
+ } else {
+ for (int h = 0; h < d->height; h++) {
+ for (int w = 0; w < d->width; w++) {
+ if ((uint) pixel(w, h) == color)
+ *(s + (w >> 3)) |= (1 << (w & 7));
+ }
+ s += maskImage.bytesPerLine();
+ }
+ }
+ if (mode == Qt::MaskOutColor)
+ maskImage.invertPixels();
+ return maskImage;
+}
+
+
+/*
+ This code is contributed by Philipp Lang,
+ GeneriCom Software Germany (www.generi.com)
+ under the terms of the QPL, Version 1.0
+*/
+
+/*!
+ \fn QImage QImage::mirror(bool horizontal, bool vertical) const
+
+ Use mirrored() instead.
+*/
+
+/*!
+ Returns a mirror of the image, mirrored in the horizontal and/or
+ the vertical direction depending on whether \a horizontal and \a
+ vertical are set to true or false.
+
+ Note that the original image is not changed.
+
+ \sa {QImage#Image Transformations}{Image Transformations}
+*/
+QImage QImage::mirrored(bool horizontal, bool vertical) const
+{
+ if (!d)
+ return QImage();
+
+ if ((d->width <= 1 && d->height <= 1) || (!horizontal && !vertical))
+ return *this;
+
+ int w = d->width;
+ int h = d->height;
+ // Create result image, copy colormap
+ QImage result(d->width, d->height, d->format);
+ result.d->colortable = d->colortable;
+ result.d->has_alpha_clut = d->has_alpha_clut;
+
+ if (depth() == 1)
+ w = (w+7)/8;
+ int dxi = horizontal ? -1 : 1;
+ int dxs = horizontal ? w-1 : 0;
+ int dyi = vertical ? -1 : 1;
+ int dy = vertical ? h-1: 0;
+
+ // 1 bit, 8 bit
+ if (d->depth == 1 || d->depth == 8) {
+ for (int sy = 0; sy < h; sy++, dy += dyi) {
+ quint8* ssl = (quint8*)(d->data + sy*d->bytes_per_line);
+ quint8* dsl = (quint8*)(result.d->data + dy*result.d->bytes_per_line);
+ int dx = dxs;
+ for (int sx = 0; sx < w; sx++, dx += dxi)
+ dsl[dx] = ssl[sx];
+ }
+ }
+ // 16 bit
+ else if (d->depth == 16) {
+ for (int sy = 0; sy < h; sy++, dy += dyi) {
+ quint16* ssl = (quint16*)(d->data + sy*d->bytes_per_line);
+ quint16* dsl = (quint16*)(result.d->data + dy*result.d->bytes_per_line);
+ int dx = dxs;
+ for (int sx = 0; sx < w; sx++, dx += dxi)
+ dsl[dx] = ssl[sx];
+ }
+ }
+ // 24 bit
+ else if (d->depth == 24) {
+ for (int sy = 0; sy < h; sy++, dy += dyi) {
+ quint24* ssl = (quint24*)(d->data + sy*d->bytes_per_line);
+ quint24* dsl = (quint24*)(result.d->data + dy*result.d->bytes_per_line);
+ int dx = dxs;
+ for (int sx = 0; sx < w; sx++, dx += dxi)
+ dsl[dx] = ssl[sx];
+ }
+ }
+ // 32 bit
+ else if (d->depth == 32) {
+ for (int sy = 0; sy < h; sy++, dy += dyi) {
+ quint32* ssl = (quint32*)(d->data + sy*d->bytes_per_line);
+ quint32* dsl = (quint32*)(result.d->data + dy*result.d->bytes_per_line);
+ int dx = dxs;
+ for (int sx = 0; sx < w; sx++, dx += dxi)
+ dsl[dx] = ssl[sx];
+ }
+ }
+
+ // special handling of 1 bit images for horizontal mirroring
+ if (horizontal && d->depth == 1) {
+ int shift = width() % 8;
+ for (int y = h-1; y >= 0; y--) {
+ quint8* a0 = (quint8*)(result.d->data + y*d->bytes_per_line);
+ // Swap bytes
+ quint8* a = a0+dxs;
+ while (a >= a0) {
+ *a = bitflip[*a];
+ a--;
+ }
+ // Shift bits if unaligned
+ if (shift != 0) {
+ a = a0+dxs;
+ quint8 c = 0;
+ if (format() == Format_MonoLSB) {
+ while (a >= a0) {
+ quint8 nc = *a << shift;
+ *a = (*a >> (8-shift)) | c;
+ --a;
+ c = nc;
+ }
+ } else {
+ while (a >= a0) {
+ quint8 nc = *a >> shift;
+ *a = (*a << (8-shift)) | c;
+ --a;
+ c = nc;
+ }
+ }
+ }
+ }
+ }
+
+ return result;
+}
+
+/*!
+ \fn QImage QImage::swapRGB() const
+
+ Use rgbSwapped() instead.
+
+ \omit
+ Returns a QImage in which the values of the red and blue
+ components of all pixels have been swapped, effectively converting
+ an RGB image to an BGR image. The original QImage is not changed.
+ \endomit
+*/
+
+/*!
+ Returns a QImage in which the values of the red and blue
+ components of all pixels have been swapped, effectively converting
+ an RGB image to an BGR image.
+
+ The original QImage is not changed.
+
+ \sa {QImage#Image Transformations}{Image Transformations}
+*/
+QImage QImage::rgbSwapped() const
+{
+ if (isNull())
+ return *this;
+ QImage res;
+ switch (d->format) {
+ case Format_Invalid:
+ case NImageFormats:
+ Q_ASSERT(false);
+ break;
+ case Format_Mono:
+ case Format_MonoLSB:
+ case Format_Indexed8:
+ res = copy();
+ for (int i = 0; i < res.d->colortable.size(); i++) {
+ QRgb c = res.d->colortable.at(i);
+ res.d->colortable[i] = QRgb(((c << 16) & 0xff0000) | ((c >> 16) & 0xff) | (c & 0xff00ff00));
+ }
+ break;
+ case Format_RGB32:
+ case Format_ARGB32:
+ case Format_ARGB32_Premultiplied:
+ res = QImage(d->width, d->height, d->format);
+ for (int i = 0; i < d->height; i++) {
+ uint *q = (uint*)res.scanLine(i);
+ uint *p = (uint*)scanLine(i);
+ uint *end = p + d->width;
+ while (p < end) {
+ *q = ((*p << 16) & 0xff0000) | ((*p >> 16) & 0xff) | (*p & 0xff00ff00);
+ p++;
+ q++;
+ }
+ }
+ break;
+ case Format_RGB16:
+ res = QImage(d->width, d->height, d->format);
+ for (int i = 0; i < d->height; i++) {
+ ushort *q = (ushort*)res.scanLine(i);
+ const ushort *p = (const ushort*)scanLine(i);
+ const ushort *end = p + d->width;
+ while (p < end) {
+ *q = ((*p << 11) & 0xf800) | ((*p >> 11) & 0x1f) | (*p & 0x07e0);
+ p++;
+ q++;
+ }
+ }
+ break;
+ case Format_ARGB8565_Premultiplied:
+ res = QImage(d->width, d->height, d->format);
+ for (int i = 0; i < d->height; i++) {
+ quint8 *p = (quint8*)scanLine(i);
+ const quint8 *end = p + d->width * sizeof(qargb8565);
+ while (p < end) {
+ quint16 *q = reinterpret_cast<quint16*>(p + 1);
+ *q = ((*q << 11) & 0xf800) | ((*q >> 11) & 0x1f) | (*q & 0x07e0);
+ p += sizeof(qargb8565);
+ }
+ }
+ break;
+ case Format_RGB666:
+ res = QImage(d->width, d->height, d->format);
+ for (int i = 0; i < d->height; i++) {
+ qrgb666 *q = reinterpret_cast<qrgb666*>(res.scanLine(i));
+ const qrgb666 *p = reinterpret_cast<const qrgb666*>(scanLine(i));
+ const qrgb666 *end = p + d->width;
+ while (p < end) {
+ const QRgb rgb = quint32(*p++);
+ *q++ = qRgb(qBlue(rgb), qGreen(rgb), qRed(rgb));
+ }
+ }
+ break;
+ case Format_ARGB6666_Premultiplied:
+ res = QImage(d->width, d->height, d->format);
+ for (int i = 0; i < d->height; i++) {
+ qargb6666 *q = reinterpret_cast<qargb6666*>(res.scanLine(i));
+ const qargb6666 *p = reinterpret_cast<const qargb6666*>(scanLine(i));
+ const qargb6666 *end = p + d->width;
+ while (p < end) {
+ const QRgb rgb = quint32(*p++);
+ *q++ = qRgba(qBlue(rgb), qGreen(rgb), qRed(rgb), qAlpha(rgb));
+ }
+ }
+ break;
+ case Format_RGB555:
+ res = QImage(d->width, d->height, d->format);
+ for (int i = 0; i < d->height; i++) {
+ ushort *q = (ushort*)res.scanLine(i);
+ const ushort *p = (const ushort*)scanLine(i);
+ const ushort *end = p + d->width;
+ while (p < end) {
+ *q = ((*p << 10) & 0x7800) | ((*p >> 10) & 0x1f) | (*p & 0x83e0);
+ p++;
+ q++;
+ }
+ }
+ break;
+ case Format_ARGB8555_Premultiplied:
+ res = QImage(d->width, d->height, d->format);
+ for (int i = 0; i < d->height; i++) {
+ quint8 *p = (quint8*)scanLine(i);
+ const quint8 *end = p + d->width * sizeof(qargb8555);
+ while (p < end) {
+ quint16 *q = reinterpret_cast<quint16*>(p + 1);
+ *q = ((*q << 10) & 0x7800) | ((*q >> 10) & 0x1f) | (*q & 0x83e0);
+ p += sizeof(qargb8555);
+ }
+ }
+ break;
+ case Format_RGB888:
+ res = QImage(d->width, d->height, d->format);
+ for (int i = 0; i < d->height; i++) {
+ quint8 *q = reinterpret_cast<quint8*>(res.scanLine(i));
+ const quint8 *p = reinterpret_cast<const quint8*>(scanLine(i));
+ const quint8 *end = p + d->width * sizeof(qrgb888);
+ while (p < end) {
+ q[0] = p[2];
+ q[1] = p[1];
+ q[2] = p[0];
+ q += sizeof(qrgb888);
+ p += sizeof(qrgb888);
+ }
+ }
+ break;
+ case Format_RGB444:
+ res = QImage(d->width, d->height, d->format);
+ for (int i = 0; i < d->height; i++) {
+ quint8 *q = reinterpret_cast<quint8*>(res.scanLine(i));
+ const quint8 *p = reinterpret_cast<const quint8*>(scanLine(i));
+ const quint8 *end = p + d->width * sizeof(qrgb444);
+ while (p < end) {
+ q[0] = (p[0] & 0xf0) | ((p[1] & 0x0f) << 8);
+ q[1] = ((p[0] & 0x0f) >> 8) | (p[1] & 0xf0);
+ q += sizeof(qrgb444);
+ p += sizeof(qrgb444);
+ }
+ }
+ break;
+ case Format_ARGB4444_Premultiplied:
+ res = QImage(d->width, d->height, d->format);
+ for (int i = 0; i < d->height; i++) {
+ quint8 *q = reinterpret_cast<quint8*>(res.scanLine(i));
+ const quint8 *p = reinterpret_cast<const quint8*>(scanLine(i));
+ const quint8 *end = p + d->width * sizeof(qargb4444);
+ while (p < end) {
+ q[0] = (p[0] & 0xf0) | ((p[1] & 0x0f) << 8);
+ q[1] = ((p[0] & 0x0f) >> 8) | (p[1] & 0xf0);
+ q += sizeof(qargb4444);
+ p += sizeof(qargb4444);
+ }
+ }
+ break;
+ }
+ return res;
+}
+
+/*!
+ Loads an image from the file with the given \a fileName. Returns true if
+ the image was successfully loaded; otherwise returns false.
+
+ The loader attempts to read the image using the specified \a format, e.g.,
+ PNG or JPG. If \a format is not specified (which is the default), the
+ loader probes the file for a header to guess the file format.
+
+ The file name can either refer to an actual file on disk or to one
+ of the application's embedded resources. See the
+ \l{resources.html}{Resource System} overview for details on how to
+ embed images and other resource files in the application's
+ executable.
+
+ \sa {QImage#Reading and Writing Image Files}{Reading and Writing Image Files}
+*/
+
+bool QImage::load(const QString &fileName, const char* format)
+{
+ if (fileName.isEmpty())
+ return false;
+
+ QImage image = QImageReader(fileName, format).read();
+ if (!image.isNull()) {
+ operator=(image);
+ return true;
+ }
+ return false;
+}
+
+/*!
+ \overload
+
+ This function reads a QImage from the given \a device. This can,
+ for example, be used to load an image directly into a QByteArray.
+*/
+
+bool QImage::load(QIODevice* device, const char* format)
+{
+ QImage image = QImageReader(device, format).read();
+ if(!image.isNull()) {
+ operator=(image);
+ return true;
+ }
+ return false;
+}
+
+/*!
+ \fn bool QImage::loadFromData(const uchar *data, int len, const char *format)
+
+ Loads an image from the first \a len bytes of the given binary \a
+ data. Returns true if the image was successfully loaded; otherwise
+ returns false.
+
+ The loader attempts to read the image using the specified \a format, e.g.,
+ PNG or JPG. If \a format is not specified (which is the default), the
+ loader probes the file for a header to guess the file format.
+
+ \sa {QImage#Reading and Writing Image Files}{Reading and Writing Image Files}
+*/
+
+bool QImage::loadFromData(const uchar *data, int len, const char *format)
+{
+ QImage image = fromData(data, len, format);
+ if (!image.isNull()) {
+ operator=(image);
+ return true;
+ }
+ return false;
+}
+
+/*!
+ \fn bool QImage::loadFromData(const QByteArray &data, const char *format)
+
+ \overload
+
+ Loads an image from the given QByteArray \a data.
+*/
+
+/*!
+ \fn QImage QImage::fromData(const uchar *data, int size, const char *format)
+
+ Constructs a QImage from the first \a size bytes of the given
+ binary \a data. The loader attempts to read the image using the
+ specified \a format. If \a format is not specified (which is the default),
+ the loader probes the file for a header to guess the file format.
+
+ If the loading of the image failed, this object is a null image.
+
+ \sa load(), save(), {QImage#Reading and Writing Image
+ Files}{Reading and Writing Image Files}
+*/
+QImage QImage::fromData(const uchar *data, int size, const char *format)
+{
+ QByteArray a = QByteArray::fromRawData(reinterpret_cast<const char *>(data), size);
+ QBuffer b;
+ b.setData(a);
+ b.open(QIODevice::ReadOnly);
+ return QImageReader(&b, format).read();
+}
+
+/*!
+ \fn QImage QImage::fromData(const QByteArray &data, const char *format)
+
+ \overload
+
+ Loads an image from the given QByteArray \a data.
+*/
+
+/*!
+ Saves the image to the file with the given \a fileName, using the
+ given image file \a format and \a quality factor. If \a format is
+ 0, QImage will attempt to guess the format by looking at \a fileName's
+ suffix.
+
+ The \a quality factor must be in the range 0 to 100 or -1. Specify
+ 0 to obtain small compressed files, 100 for large uncompressed
+ files, and -1 (the default) to use the default settings.
+
+ Returns true if the image was successfully saved; otherwise
+ returns false.
+
+ \sa {QImage#Reading and Writing Image Files}{Reading and Writing
+ Image Files}
+*/
+bool QImage::save(const QString &fileName, const char *format, int quality) const
+{
+ if (isNull())
+ return false;
+ QImageWriter writer(fileName, format);
+ return d->doImageIO(this, &writer, quality);
+}
+
+/*!
+ \overload
+
+ This function writes a QImage to the given \a device.
+
+ This can, for example, be used to save an image directly into a
+ QByteArray:
+
+ \snippet doc/src/snippets/image/image.cpp 0
+*/
+
+bool QImage::save(QIODevice* device, const char* format, int quality) const
+{
+ if (isNull())
+ return false; // nothing to save
+ QImageWriter writer(device, format);
+ return d->doImageIO(this, &writer, quality);
+}
+
+/* \internal
+*/
+
+bool QImageData::doImageIO(const QImage *image, QImageWriter *writer, int quality) const
+{
+ if (quality > 100 || quality < -1)
+ qWarning("QPixmap::save: Quality out of range [-1, 100]");
+ if (quality >= 0)
+ writer->setQuality(qMin(quality,100));
+ return writer->write(*image);
+}
+
+/*****************************************************************************
+ QImage stream functions
+ *****************************************************************************/
+#if !defined(QT_NO_DATASTREAM)
+/*!
+ \fn QDataStream &operator<<(QDataStream &stream, const QImage &image)
+ \relates QImage
+
+ Writes the given \a image to the given \a stream as a PNG image,
+ or as a BMP image if the stream's version is 1. Note that writing
+ the stream to a file will not produce a valid image file.
+
+ \sa QImage::save(), {Format of the QDataStream Operators}
+*/
+
+QDataStream &operator<<(QDataStream &s, const QImage &image)
+{
+ if (s.version() >= 5) {
+ if (image.isNull()) {
+ s << (qint32) 0; // null image marker
+ return s;
+ } else {
+ s << (qint32) 1;
+ // continue ...
+ }
+ }
+ QImageWriter writer(s.device(), s.version() == 1 ? "bmp" : "png");
+ writer.write(image);
+ return s;
+}
+
+/*!
+ \fn QDataStream &operator>>(QDataStream &stream, QImage &image)
+ \relates QImage
+
+ Reads an image from the given \a stream and stores it in the given
+ \a image.
+
+ \sa QImage::load(), {Format of the QDataStream Operators}
+*/
+
+QDataStream &operator>>(QDataStream &s, QImage &image)
+{
+ if (s.version() >= 5) {
+ qint32 nullMarker;
+ s >> nullMarker;
+ if (!nullMarker) {
+ image = QImage(); // null image
+ return s;
+ }
+ }
+ image = QImageReader(s.device(), 0).read();
+ return s;
+}
+#endif
+
+
+#ifdef QT3_SUPPORT
+/*!
+ \fn QImage QImage::convertDepthWithPalette(int depth, QRgb* palette, int palette_count, Qt::ImageConversionFlags flags) const
+
+ Returns an image with the given \a depth, using the \a
+ palette_count colors pointed to by \a palette. If \a depth is 1 or
+ 8, the returned image will have its color table ordered in the
+ same way as \a palette.
+
+ If the image needs to be modified to fit in a lower-resolution
+ result (e.g. converting from 32-bit to 8-bit), use the \a flags to
+ specify how you'd prefer this to happen.
+
+ Note: currently no closest-color search is made. If colors are
+ found that are not in the palette, the palette may not be used at
+ all. This result should not be considered valid because it may
+ change in future implementations.
+
+ Currently inefficient for non-32-bit images.
+
+ Use the convertToFormat() function in combination with the
+ setColorTable() function instead.
+*/
+QImage QImage::convertDepthWithPalette(int d, QRgb* palette, int palette_count, Qt::ImageConversionFlags flags) const
+{
+ Format f = formatFor(d, QImage::LittleEndian);
+ QVector<QRgb> colortable;
+ for (int i = 0; i < palette_count; ++i)
+ colortable.append(palette[i]);
+ return convertToFormat(f, colortable, flags);
+}
+
+/*!
+ \relates QImage
+
+ Copies a block of pixels from \a src to \a dst. The pixels
+ copied from source (src) are converted according to
+ \a flags if it is incompatible with the destination
+ (\a dst).
+
+ \a sx, \a sy is the top-left pixel in \a src, \a dx, \a dy is the
+ top-left position in \a dst and \a sw, \a sh is the size of the
+ copied block. The copying is clipped if areas outside \a src or \a
+ dst are specified. If \a sw is -1, it is adjusted to
+ src->width(). Similarly, if \a sh is -1, it is adjusted to
+ src->height().
+
+ Currently inefficient for non 32-bit images.
+
+ Use copy() or QPainter::drawImage() instead.
+*/
+void bitBlt(QImage *dst, int dx, int dy, const QImage *src, int sx, int sy, int sw, int sh,
+ Qt::ImageConversionFlags flags)
+{
+ if (dst->isNull() || src->isNull())
+ return;
+ QPainter p(dst);
+ p.drawImage(QPoint(dx, dy), *src, QRect(sx, sy, sw, sh), flags);
+}
+#endif
+
+/*!
+ \fn bool QImage::operator==(const QImage & image) const
+
+ Returns true if this image and the given \a image have the same
+ contents; otherwise returns false.
+
+ The comparison can be slow, unless there is some obvious
+ difference (e.g. different size or format), in which case the
+ function will return quickly.
+
+ \sa operator=()
+*/
+
+bool QImage::operator==(const QImage & i) const
+{
+ // same object, or shared?
+ if (i.d == d)
+ return true;
+ if (!i.d || !d)
+ return false;
+
+ // obviously different stuff?
+ if (i.d->height != d->height || i.d->width != d->width || i.d->format != d->format)
+ return false;
+
+ if (d->format != Format_RGB32) {
+ if (d->colortable != i.d->colortable)
+ return false;
+ if (d->format >= Format_ARGB32) { // all bits defined
+ const int n = d->width * d->depth / 8;
+ if (n == d->bytes_per_line && n == i.d->bytes_per_line) {
+ if (memcmp(bits(), i.bits(), d->nbytes))
+ return false;
+ } else {
+ for (int y = 0; y < d->height; ++y) {
+ if (memcmp(scanLine(y), i.scanLine(y), n))
+ return false;
+ }
+ }
+ } else {
+ int w = width();
+ int h = height();
+ for (int y=0; y<h; ++y) {
+ for (int x=0; x<w; ++x) {
+ if (pixelIndex(x, y) != i.pixelIndex(x, y))
+ return false;
+ }
+ }
+ }
+ } else {
+ //alpha channel undefined, so we must mask it out
+ for(int l = 0; l < d->height; l++) {
+ int w = d->width;
+ const uint *p1 = reinterpret_cast<const uint*>(scanLine(l));
+ const uint *p2 = reinterpret_cast<const uint*>(i.scanLine(l));
+ while (w--) {
+ if ((*p1++ & 0x00ffffff) != (*p2++ & 0x00ffffff))
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+
+/*!
+ \fn bool QImage::operator!=(const QImage & image) const
+
+ Returns true if this image and the given \a image have different
+ contents; otherwise returns false.
+
+ The comparison can be slow, unless there is some obvious
+ difference, such as different widths, in which case the function
+ will return quickly.
+
+ \sa operator=()
+*/
+
+bool QImage::operator!=(const QImage & i) const
+{
+ return !(*this == i);
+}
+
+
+
+
+/*!
+ Returns the number of pixels that fit horizontally in a physical
+ meter. Together with dotsPerMeterY(), this number defines the
+ intended scale and aspect ratio of the image.
+
+ \sa setDotsPerMeterX(), {QImage#Image Information}{Image
+ Information}
+*/
+int QImage::dotsPerMeterX() const
+{
+ return d ? qRound(d->dpmx) : 0;
+}
+
+/*!
+ Returns the number of pixels that fit vertically in a physical
+ meter. Together with dotsPerMeterX(), this number defines the
+ intended scale and aspect ratio of the image.
+
+ \sa setDotsPerMeterY(), {QImage#Image Information}{Image
+ Information}
+*/
+int QImage::dotsPerMeterY() const
+{
+ return d ? qRound(d->dpmy) : 0;
+}
+
+/*!
+ Sets the number of pixels that fit horizontally in a physical
+ meter, to \a x.
+
+ Together with dotsPerMeterY(), this number defines the intended
+ scale and aspect ratio of the image.
+
+ \sa dotsPerMeterX(), {QImage#Image Information}{Image
+ Information}
+*/
+void QImage::setDotsPerMeterX(int x)
+{
+ if (!d || !x)
+ return;
+ detach();
+
+ if (d)
+ d->dpmx = x;
+}
+
+/*!
+ Sets the number of pixels that fit vertically in a physical meter,
+ to \a y.
+
+ Together with dotsPerMeterX(), this number defines the intended
+ scale and aspect ratio of the image.
+
+ \sa dotsPerMeterY(), {QImage#Image Information}{Image
+ Information}
+*/
+void QImage::setDotsPerMeterY(int y)
+{
+ if (!d || !y)
+ return;
+ detach();
+
+ if (d)
+ d->dpmy = y;
+}
+
+/*!
+ \fn QPoint QImage::offset() const
+
+ Returns the number of pixels by which the image is intended to be
+ offset by when positioning relative to other images.
+
+ \sa setOffset(), {QImage#Image Information}{Image Information}
+*/
+QPoint QImage::offset() const
+{
+ return d ? d->offset : QPoint();
+}
+
+
+/*!
+ \fn void QImage::setOffset(const QPoint& offset)
+
+ Sets the the number of pixels by which the image is intended to be
+ offset by when positioning relative to other images, to \a offset.
+
+ \sa offset(), {QImage#Image Information}{Image Information}
+*/
+void QImage::setOffset(const QPoint& p)
+{
+ if (!d)
+ return;
+ detach();
+
+ if (d)
+ d->offset = p;
+}
+#ifndef QT_NO_IMAGE_TEXT
+
+/*!
+ Returns the text keys for this image.
+
+ You can use these keys with text() to list the image text for a
+ certain key.
+
+ \sa text()
+*/
+QStringList QImage::textKeys() const
+{
+ return d ? QStringList(d->text.keys()) : QStringList();
+}
+
+/*!
+ Returns the image text associated with the given \a key. If the
+ specified \a key is an empty string, the whole image text is
+ returned, with each key-text pair separated by a newline.
+
+ \sa setText(), textKeys()
+*/
+QString QImage::text(const QString &key) const
+{
+ if (!d)
+ return QString();
+
+ if (!key.isEmpty())
+ return d->text.value(key);
+
+ QString tmp;
+ foreach (const QString &key, d->text.keys()) {
+ if (!tmp.isEmpty())
+ tmp += QLatin1String("\n\n");
+ tmp += key + QLatin1String(": ") + d->text.value(key).simplified();
+ }
+ return tmp;
+}
+
+/*!
+ \fn void QImage::setText(const QString &key, const QString &text)
+
+ Sets the image text to the given \a text and associate it with the
+ given \a key.
+
+ If you just want to store a single text block (i.e., a "comment"
+ or just a description), you can either pass an empty key, or use a
+ generic key like "Description".
+
+ The image text is embedded into the image data when you
+ call save() or QImageWriter::write().
+
+ Not all image formats support embedded text. You can find out
+ if a specific image or format supports embedding text
+ by using QImageWriter::supportsOption(). We give an example:
+
+ \snippet doc/src/snippets/image/supportedformat.cpp 0
+
+ You can use QImageWriter::supportedImageFormats() to find out
+ which image formats are available to you.
+
+ \sa text(), textKeys()
+*/
+void QImage::setText(const QString &key, const QString &value)
+{
+ if (!d)
+ return;
+ detach();
+
+ if (d)
+ d->text.insert(key, value);
+}
+
+/*!
+ \fn QString QImage::text(const char* key, const char* language) const
+ \obsolete
+
+ Returns the text recorded for the given \a key in the given \a
+ language, or in a default language if \a language is 0.
+
+ Use text() instead.
+
+ The language the text is recorded in is no longer relevant since
+ the text is always set using QString and UTF-8 representation.
+*/
+QString QImage::text(const char* key, const char* lang) const
+{
+ if (!d)
+ return QString();
+ QString k = QString::fromAscii(key);
+ if (lang && *lang)
+ k += QLatin1Char('/') + QString::fromAscii(lang);
+ return d->text.value(k);
+}
+
+/*!
+ \fn QString QImage::text(const QImageTextKeyLang& keywordAndLanguage) const
+ \overload
+ \obsolete
+
+ Returns the text recorded for the given \a keywordAndLanguage.
+
+ Use text() instead.
+
+ The language the text is recorded in is no longer relevant since
+ the text is always set using QString and UTF-8 representation.
+*/
+QString QImage::text(const QImageTextKeyLang& kl) const
+{
+ if (!d)
+ return QString();
+ QString k = QString::fromAscii(kl.key);
+ if (!kl.lang.isEmpty())
+ k += QLatin1Char('/') + QString::fromAscii(kl.lang);
+ return d->text.value(k);
+}
+
+/*!
+ \obsolete
+
+ Returns the language identifiers for which some texts are
+ recorded. Note that if you want to iterate over the list, you
+ should iterate over a copy.
+
+ The language the text is recorded in is no longer relevant since
+ the text is always set using QString and UTF-8 representation.
+*/
+QStringList QImage::textLanguages() const
+{
+ if (!d)
+ return QStringList();
+ QStringList keys = textKeys();
+ QStringList languages;
+ for (int i = 0; i < keys.size(); ++i) {
+ int index = keys.at(i).indexOf(QLatin1Char('/'));
+ if (index > 0)
+ languages += keys.at(i).mid(index+1);
+ }
+
+ return languages;
+}
+
+/*!
+ \obsolete
+
+ Returns a list of QImageTextKeyLang objects that enumerate all the
+ texts key/language pairs set for this image.
+
+ Use textKeys() instead.
+
+ The language the text is recorded in is no longer relevant since
+ the text is always set using QString and UTF-8 representation.
+*/
+QList<QImageTextKeyLang> QImage::textList() const
+{
+ QList<QImageTextKeyLang> imageTextKeys;
+ if (!d)
+ return imageTextKeys;
+ QStringList keys = textKeys();
+ for (int i = 0; i < keys.size(); ++i) {
+ int index = keys.at(i).indexOf(QLatin1Char('/'));
+ if (index > 0) {
+ QImageTextKeyLang tkl;
+ tkl.key = keys.at(i).left(index).toAscii();
+ tkl.lang = keys.at(i).mid(index+1).toAscii();
+ imageTextKeys += tkl;
+ }
+ }
+
+ return imageTextKeys;
+}
+
+/*!
+ \fn void QImage::setText(const char* key, const char* language, const QString& text)
+ \obsolete
+
+ Sets the image text to the given \a text and associate it with the
+ given \a key. The text is recorded in the specified \a language,
+ or in a default language if \a language is 0.
+
+ Use setText() instead.
+
+ The language the text is recorded in is no longer relevant since
+ the text is always set using QString and UTF-8 representation.
+
+ \omit
+ Records string \a for the keyword \a key. The \a key should be
+ a portable keyword recognizable by other software - some suggested
+ values can be found in
+ \l{http://www.libpng.org/pub/png/spec/1.2/png-1.2-pdg.html#C.Anc-text}
+ {the PNG specification}. \a s can be any text. \a lang should
+ specify the language code (see
+ \l{http://www.rfc-editor.org/rfc/rfc1766.txt}{RFC 1766}) or 0.
+ \endomit
+*/
+void QImage::setText(const char* key, const char* lang, const QString& s)
+{
+ if (!d)
+ return;
+ detach();
+
+ // In case detach() ran out of memory
+ if (!d)
+ return;
+
+ QString k = QString::fromAscii(key);
+ if (lang && *lang)
+ k += QLatin1Char('/') + QString::fromAscii(lang);
+ d->text.insert(k, s);
+}
+
+#endif // QT_NO_IMAGE_TEXT
+
+/*
+ Sets the image bits to the \a pixmap contents and returns a
+ reference to the image.
+
+ If the image shares data with other images, it will first
+ dereference the shared data.
+
+ Makes a call to QPixmap::convertToImage().
+*/
+
+/*! \fn QImage::Endian QImage::systemBitOrder()
+
+ Determines the bit order of the display hardware. Returns
+ QImage::LittleEndian (LSB first) or QImage::BigEndian (MSB first).
+
+ This function is no longer relevant for QImage. Use QSysInfo
+ instead.
+*/
+
+
+/*!
+ \internal
+
+ Used by QPainter to retrieve a paint engine for the image.
+*/
+
+QPaintEngine *QImage::paintEngine() const
+{
+ if (!d)
+ return 0;
+
+#ifdef QT_RASTER_IMAGEENGINE
+ if (!d->paintEngine) {
+ d->paintEngine = new QRasterPaintEngine(const_cast<QImage *>(this));
+ }
+#endif
+ return d->paintEngine;
+}
+
+
+/*!
+ \reimp
+
+ Returns the size for the specified \a metric on the device.
+*/
+int QImage::metric(PaintDeviceMetric metric) const
+{
+ if (!d)
+ return 0;
+
+ switch (metric) {
+ case PdmWidth:
+ return d->width;
+ break;
+
+ case PdmHeight:
+ return d->height;
+ break;
+
+ case PdmWidthMM:
+ return qRound(d->width * 1000 / d->dpmx);
+ break;
+
+ case PdmHeightMM:
+ return qRound(d->height * 1000 / d->dpmy);
+ break;
+
+ case PdmNumColors:
+ return d->colortable.size();
+ break;
+
+ case PdmDepth:
+ return d->depth;
+ break;
+
+ case PdmDpiX:
+ return qRound(d->dpmx * 0.0254);
+ break;
+
+ case PdmDpiY:
+ return qRound(d->dpmy * 0.0254);
+ break;
+
+ case PdmPhysicalDpiX:
+ return qRound(d->dpmx * 0.0254);
+ break;
+
+ case PdmPhysicalDpiY:
+ return qRound(d->dpmy * 0.0254);
+ break;
+
+ default:
+ qWarning("QImage::metric(): Unhandled metric type %d", metric);
+ break;
+ }
+ return 0;
+}
+
+
+
+/*****************************************************************************
+ QPixmap (and QImage) helper functions
+ *****************************************************************************/
+/*
+ This internal function contains the common (i.e. platform independent) code
+ to do a transformation of pixel data. It is used by QPixmap::transform() and by
+ QImage::transform().
+
+ \a trueMat is the true transformation matrix (see QPixmap::trueMatrix()) and
+ \a xoffset is an offset to the matrix.
+
+ \a msbfirst specifies for 1bpp images, if the MSB or LSB comes first and \a
+ depth specifies the colordepth of the data.
+
+ \a dptr is a pointer to the destination data, \a dbpl specifies the bits per
+ line for the destination data, \a p_inc is the offset that we advance for
+ every scanline and \a dHeight is the height of the destination image.
+
+ \a sprt is the pointer to the source data, \a sbpl specifies the bits per
+ line of the source data, \a sWidth and \a sHeight are the width and height of
+ the source data.
+*/
+
+#undef IWX_MSB
+#define IWX_MSB(b) if (trigx < maxws && trigy < maxhs) { \
+ if (*(sptr+sbpl*(trigy>>12)+(trigx>>15)) & \
+ (1 << (7-((trigx>>12)&7)))) \
+ *dptr |= b; \
+ } \
+ trigx += m11; \
+ trigy += m12;
+ // END OF MACRO
+#undef IWX_LSB
+#define IWX_LSB(b) if (trigx < maxws && trigy < maxhs) { \
+ if (*(sptr+sbpl*(trigy>>12)+(trigx>>15)) & \
+ (1 << ((trigx>>12)&7))) \
+ *dptr |= b; \
+ } \
+ trigx += m11; \
+ trigy += m12;
+ // END OF MACRO
+#undef IWX_PIX
+#define IWX_PIX(b) if (trigx < maxws && trigy < maxhs) { \
+ if ((*(sptr+sbpl*(trigy>>12)+(trigx>>15)) & \
+ (1 << (7-((trigx>>12)&7)))) == 0) \
+ *dptr &= ~b; \
+ } \
+ trigx += m11; \
+ trigy += m12;
+ // END OF MACRO
+bool qt_xForm_helper(const QTransform &trueMat, int xoffset, int type, int depth,
+ uchar *dptr, int dbpl, int p_inc, int dHeight,
+ const uchar *sptr, int sbpl, int sWidth, int sHeight)
+{
+ int m11 = int(trueMat.m11()*4096.0);
+ int m12 = int(trueMat.m12()*4096.0);
+ int m21 = int(trueMat.m21()*4096.0);
+ int m22 = int(trueMat.m22()*4096.0);
+ int dx = qRound(trueMat.dx()*4096.0);
+ int dy = qRound(trueMat.dy()*4096.0);
+
+ int m21ydx = dx + (xoffset<<16) + (m11 + m21) / 2;
+ int m22ydy = dy + (m12 + m22) / 2;
+ uint trigx;
+ uint trigy;
+ uint maxws = sWidth<<12;
+ uint maxhs = sHeight<<12;
+
+ for (int y=0; y<dHeight; y++) { // for each target scanline
+ trigx = m21ydx;
+ trigy = m22ydy;
+ uchar *maxp = dptr + dbpl;
+ if (depth != 1) {
+ switch (depth) {
+ case 8: // 8 bpp transform
+ while (dptr < maxp) {
+ if (trigx < maxws && trigy < maxhs)
+ *dptr = *(sptr+sbpl*(trigy>>12)+(trigx>>12));
+ trigx += m11;
+ trigy += m12;
+ dptr++;
+ }
+ break;
+
+ case 16: // 16 bpp transform
+ while (dptr < maxp) {
+ if (trigx < maxws && trigy < maxhs)
+ *((ushort*)dptr) = *((ushort *)(sptr+sbpl*(trigy>>12) +
+ ((trigx>>12)<<1)));
+ trigx += m11;
+ trigy += m12;
+ dptr++;
+ dptr++;
+ }
+ break;
+
+ case 24: // 24 bpp transform
+ while (dptr < maxp) {
+ if (trigx < maxws && trigy < maxhs) {
+ const uchar *p2 = sptr+sbpl*(trigy>>12) + ((trigx>>12)*3);
+ dptr[0] = p2[0];
+ dptr[1] = p2[1];
+ dptr[2] = p2[2];
+ }
+ trigx += m11;
+ trigy += m12;
+ dptr += 3;
+ }
+ break;
+
+ case 32: // 32 bpp transform
+ while (dptr < maxp) {
+ if (trigx < maxws && trigy < maxhs)
+ *((uint*)dptr) = *((uint *)(sptr+sbpl*(trigy>>12) +
+ ((trigx>>12)<<2)));
+ trigx += m11;
+ trigy += m12;
+ dptr += 4;
+ }
+ break;
+
+ default: {
+ return false;
+ }
+ }
+ } else {
+ switch (type) {
+ case QT_XFORM_TYPE_MSBFIRST:
+ while (dptr < maxp) {
+ IWX_MSB(128);
+ IWX_MSB(64);
+ IWX_MSB(32);
+ IWX_MSB(16);
+ IWX_MSB(8);
+ IWX_MSB(4);
+ IWX_MSB(2);
+ IWX_MSB(1);
+ dptr++;
+ }
+ break;
+ case QT_XFORM_TYPE_LSBFIRST:
+ while (dptr < maxp) {
+ IWX_LSB(1);
+ IWX_LSB(2);
+ IWX_LSB(4);
+ IWX_LSB(8);
+ IWX_LSB(16);
+ IWX_LSB(32);
+ IWX_LSB(64);
+ IWX_LSB(128);
+ dptr++;
+ }
+ break;
+# if defined(Q_WS_WIN)
+ case QT_XFORM_TYPE_WINDOWSPIXMAP:
+ while (dptr < maxp) {
+ IWX_PIX(128);
+ IWX_PIX(64);
+ IWX_PIX(32);
+ IWX_PIX(16);
+ IWX_PIX(8);
+ IWX_PIX(4);
+ IWX_PIX(2);
+ IWX_PIX(1);
+ dptr++;
+ }
+ break;
+# endif
+ }
+ }
+ m21ydx += m21;
+ m22ydy += m22;
+ dptr += p_inc;
+ }
+ return true;
+}
+#undef IWX_MSB
+#undef IWX_LSB
+#undef IWX_PIX
+
+/*!
+ \fn QImage QImage::xForm(const QMatrix &matrix) const
+
+ Use transformed() instead.
+
+ \oldcode
+ QImage image;
+ ...
+ image.xForm(matrix);
+ \newcode
+ QImage image;
+ ...
+ image.transformed(matrix);
+ \endcode
+*/
+
+/*! \obsolete
+ Returns a number that identifies the contents of this
+ QImage object. Distinct QImage objects can only have the same
+ serial number if they refer to the same contents (but they don't
+ have to).
+
+ Use cacheKey() instead.
+
+ \warning The serial number doesn't necessarily change when the
+ image is altered. This means that it may be dangerous to use
+ it as a cache key.
+
+ \sa operator==()
+*/
+
+int QImage::serialNumber() const
+{
+ if (!d)
+ return 0;
+ else
+ return d->ser_no;
+}
+
+/*!
+ Returns a number that identifies the contents of this QImage
+ object. Distinct QImage objects can only have the same key if they
+ refer to the same contents.
+
+ The key will change when the image is altered.
+*/
+qint64 QImage::cacheKey() const
+{
+ if (!d)
+ return 0;
+ else
+ return (((qint64) d->ser_no) << 32) | ((qint64) d->detach_no);
+}
+
+/*!
+ \internal
+
+ Returns true if the image is detached; otherwise returns false.
+
+ \sa detach(), {Implicit Data Sharing}
+*/
+
+bool QImage::isDetached() const
+{
+ return d && d->ref == 1;
+}
+
+
+/*!
+ \obsolete
+ Sets the alpha channel of this image to the given \a alphaChannel.
+
+ If \a alphaChannel is an 8 bit grayscale image, the intensity values are
+ written into this buffer directly. Otherwise, \a alphaChannel is converted
+ to 32 bit and the intensity of the RGB pixel values is used.
+
+ Note that the image will be converted to the Format_ARGB32_Premultiplied
+ format if the function succeeds.
+
+ Use one of the composition mods in QPainter::CompositionMode instead.
+
+ \sa alphaChannel(), {QImage#Image Transformations}{Image
+ Transformations}, {QImage#Image Formats}{Image Formats}
+*/
+
+void QImage::setAlphaChannel(const QImage &alphaChannel)
+{
+ if (!d)
+ return;
+
+ int w = d->width;
+ int h = d->height;
+
+ if (w != alphaChannel.d->width || h != alphaChannel.d->height) {
+ qWarning("QImage::setAlphaChannel: "
+ "Alpha channel must have same dimensions as the target image");
+ return;
+ }
+
+ if (d->paintEngine && d->paintEngine->isActive()) {
+ qWarning("QImage::setAlphaChannel: "
+ "Unable to set alpha channel while image is being painted on");
+ return;
+ }
+
+ detach();
+
+ *this = convertToFormat(QImage::Format_ARGB32_Premultiplied);
+
+ // Slight optimization since alphachannels are returned as 8-bit grays.
+ if (alphaChannel.d->depth == 8 && alphaChannel.isGrayscale()) {
+ const uchar *src_data = alphaChannel.d->data;
+ const uchar *dest_data = d->data;
+ for (int y=0; y<h; ++y) {
+ const uchar *src = src_data;
+ QRgb *dest = (QRgb *)dest_data;
+ for (int x=0; x<w; ++x) {
+ int alpha = *src;
+ int destAlpha = qt_div_255(alpha * qAlpha(*dest));
+ *dest = ((destAlpha << 24)
+ | (qt_div_255(qRed(*dest) * alpha) << 16)
+ | (qt_div_255(qGreen(*dest) * alpha) << 8)
+ | (qt_div_255(qBlue(*dest) * alpha)));
+ ++dest;
+ ++src;
+ }
+ src_data += alphaChannel.d->bytes_per_line;
+ dest_data += d->bytes_per_line;
+ }
+
+ } else {
+ const QImage sourceImage = alphaChannel.convertToFormat(QImage::Format_RGB32);
+ const uchar *src_data = sourceImage.d->data;
+ const uchar *dest_data = d->data;
+ for (int y=0; y<h; ++y) {
+ const QRgb *src = (const QRgb *) src_data;
+ QRgb *dest = (QRgb *) dest_data;
+ for (int x=0; x<w; ++x) {
+ int alpha = qGray(*src);
+ int destAlpha = qt_div_255(alpha * qAlpha(*dest));
+ *dest = ((destAlpha << 24)
+ | (qt_div_255(qRed(*dest) * alpha) << 16)
+ | (qt_div_255(qGreen(*dest) * alpha) << 8)
+ | (qt_div_255(qBlue(*dest) * alpha)));
+ ++dest;
+ ++src;
+ }
+ src_data += sourceImage.d->bytes_per_line;
+ dest_data += d->bytes_per_line;
+ }
+ }
+}
+
+
+/*!
+ Returns the alpha channel of the image as a new grayscale QImage in which
+ each pixel's red, green, and blue values are given the alpha value of the
+ original image. The color depth of the returned image is 8-bit.
+
+ You can see an example of use of this function in QPixmap's
+ \l{QPixmap::}{alphaChannel()}, which works in the same way as
+ this function on QPixmaps.
+
+ \sa setAlphaChannel(), hasAlphaChannel(),
+ {QPixmap#Pixmap Information}{Pixmap},
+ {QImage#Image Transformations}{Image Transformations}
+*/
+
+QImage QImage::alphaChannel() const
+{
+ if (!d)
+ return QImage();
+
+ int w = d->width;
+ int h = d->height;
+
+ QImage image(w, h, Format_Indexed8);
+ image.setNumColors(256);
+
+ // set up gray scale table.
+ for (int i=0; i<256; ++i)
+ image.setColor(i, qRgb(i, i, i));
+
+ if (!hasAlphaChannel()) {
+ image.fill(255);
+ return image;
+ }
+
+ if (d->format == Format_Indexed8) {
+ const uchar *src_data = d->data;
+ uchar *dest_data = image.d->data;
+ for (int y=0; y<h; ++y) {
+ const uchar *src = src_data;
+ uchar *dest = dest_data;
+ for (int x=0; x<w; ++x) {
+ *dest = qAlpha(d->colortable.at(*src));
+ ++dest;
+ ++src;
+ }
+ src_data += d->bytes_per_line;
+ dest_data += image.d->bytes_per_line;
+ }
+ } else {
+ QImage alpha32 = *this;
+ if (d->format != Format_ARGB32 && d->format != Format_ARGB32_Premultiplied)
+ alpha32 = convertToFormat(Format_ARGB32);
+
+ const uchar *src_data = alpha32.d->data;
+ uchar *dest_data = image.d->data;
+ for (int y=0; y<h; ++y) {
+ const QRgb *src = (const QRgb *) src_data;
+ uchar *dest = dest_data;
+ for (int x=0; x<w; ++x) {
+ *dest = qAlpha(*src);
+ ++dest;
+ ++src;
+ }
+ src_data += alpha32.d->bytes_per_line;
+ dest_data += image.d->bytes_per_line;
+ }
+ }
+
+ return image;
+}
+
+/*!
+ Returns true if the image has a format that respects the alpha
+ channel, otherwise returns false.
+
+ \sa alphaChannel(), {QImage#Image Information}{Image Information}
+*/
+bool QImage::hasAlphaChannel() const
+{
+ return d && (d->format == Format_ARGB32_Premultiplied
+ || d->format == Format_ARGB32
+ || d->format == Format_ARGB8565_Premultiplied
+ || d->format == Format_ARGB8555_Premultiplied
+ || d->format == Format_ARGB6666_Premultiplied
+ || d->format == Format_ARGB4444_Premultiplied
+ || (d->has_alpha_clut && (d->format == Format_Indexed8
+ || d->format == Format_Mono
+ || d->format == Format_MonoLSB)));
+}
+
+
+#ifdef QT3_SUPPORT
+#if defined(Q_WS_X11)
+QT_BEGIN_INCLUDE_NAMESPACE
+#include <private/qt_x11_p.h>
+QT_END_INCLUDE_NAMESPACE
+#endif
+
+QImage::Endian QImage::systemBitOrder()
+{
+#if defined(Q_WS_X11)
+ return BitmapBitOrder(X11->display) == MSBFirst ? BigEndian : LittleEndian;
+#else
+ return BigEndian;
+#endif
+}
+#endif
+
+/*!
+ \fn QImage QImage::copy(const QRect &rect, Qt::ImageConversionFlags flags) const
+ \compat
+
+ Use copy() instead.
+*/
+
+/*!
+ \fn QImage QImage::copy(int x, int y, int w, int h, Qt::ImageConversionFlags flags) const
+ \compat
+
+ Use copy() instead.
+*/
+
+/*!
+ \fn QImage QImage::scaleWidth(int w) const
+ \compat
+
+ Use scaledToWidth() instead.
+*/
+
+/*!
+ \fn QImage QImage::scaleHeight(int h) const
+ \compat
+
+ Use scaledToHeight() instead.
+*/
+
+static QImage smoothScaled(const QImage &source, int w, int h) {
+ QImage src = source;
+ if (src.format() == QImage::Format_ARGB32)
+ src = src.convertToFormat(QImage::Format_ARGB32_Premultiplied);
+ else if (src.depth() < 32) {
+ if (src.hasAlphaChannel())
+ src = src.convertToFormat(QImage::Format_ARGB32_Premultiplied);
+ else
+ src = src.convertToFormat(QImage::Format_RGB32);
+ }
+
+ return qSmoothScaleImage(src, w, h);
+}
+
+
+static QImage rotated90(const QImage &image) {
+ QImage out(image.height(), image.width(), image.format());
+ if (image.numColors() > 0)
+ out.setColorTable(image.colorTable());
+ int w = image.width();
+ int h = image.height();
+ switch (image.format()) {
+ case QImage::Format_RGB32:
+ case QImage::Format_ARGB32:
+ case QImage::Format_ARGB32_Premultiplied:
+ qt_memrotate270(reinterpret_cast<const quint32*>(image.bits()),
+ w, h, image.bytesPerLine(),
+ reinterpret_cast<quint32*>(out.bits()),
+ out.bytesPerLine());
+ break;
+ case QImage::Format_RGB666:
+ case QImage::Format_ARGB6666_Premultiplied:
+ case QImage::Format_ARGB8565_Premultiplied:
+ case QImage::Format_ARGB8555_Premultiplied:
+ case QImage::Format_RGB888:
+ qt_memrotate270(reinterpret_cast<const quint24*>(image.bits()),
+ w, h, image.bytesPerLine(),
+ reinterpret_cast<quint24*>(out.bits()),
+ out.bytesPerLine());
+ break;
+ case QImage::Format_RGB555:
+ case QImage::Format_RGB16:
+ case QImage::Format_ARGB4444_Premultiplied:
+ qt_memrotate270(reinterpret_cast<const quint16*>(image.bits()),
+ w, h, image.bytesPerLine(),
+ reinterpret_cast<quint16*>(out.bits()),
+ out.bytesPerLine());
+ break;
+ case QImage::Format_Indexed8:
+ qt_memrotate270(reinterpret_cast<const quint8*>(image.bits()),
+ w, h, image.bytesPerLine(),
+ reinterpret_cast<quint8*>(out.bits()),
+ out.bytesPerLine());
+ break;
+ default:
+ for (int y=0; y<h; ++y) {
+ if (image.numColors())
+ for (int x=0; x<w; ++x)
+ out.setPixel(h-y-1, x, image.pixelIndex(x, y));
+ else
+ for (int x=0; x<w; ++x)
+ out.setPixel(h-y-1, x, image.pixel(x, y));
+ }
+ break;
+ }
+ return out;
+}
+
+
+static QImage rotated180(const QImage &image) {
+ return image.mirrored(true, true);
+}
+
+
+static QImage rotated270(const QImage &image) {
+ QImage out(image.height(), image.width(), image.format());
+ if (image.numColors() > 0)
+ out.setColorTable(image.colorTable());
+ int w = image.width();
+ int h = image.height();
+ switch (image.format()) {
+ case QImage::Format_RGB32:
+ case QImage::Format_ARGB32:
+ case QImage::Format_ARGB32_Premultiplied:
+ qt_memrotate90(reinterpret_cast<const quint32*>(image.bits()),
+ w, h, image.bytesPerLine(),
+ reinterpret_cast<quint32*>(out.bits()),
+ out.bytesPerLine());
+ break;
+ case QImage::Format_RGB666:
+ case QImage::Format_ARGB6666_Premultiplied:
+ case QImage::Format_ARGB8565_Premultiplied:
+ case QImage::Format_ARGB8555_Premultiplied:
+ case QImage::Format_RGB888:
+ qt_memrotate90(reinterpret_cast<const quint24*>(image.bits()),
+ w, h, image.bytesPerLine(),
+ reinterpret_cast<quint24*>(out.bits()),
+ out.bytesPerLine());
+ break;
+ case QImage::Format_RGB555:
+ case QImage::Format_RGB16:
+ case QImage::Format_ARGB4444_Premultiplied:
+ qt_memrotate90(reinterpret_cast<const quint16*>(image.bits()),
+ w, h, image.bytesPerLine(),
+ reinterpret_cast<quint16*>(out.bits()),
+ out.bytesPerLine());
+ break;
+ case QImage::Format_Indexed8:
+ qt_memrotate90(reinterpret_cast<const quint8*>(image.bits()),
+ w, h, image.bytesPerLine(),
+ reinterpret_cast<quint8*>(out.bits()),
+ out.bytesPerLine());
+ break;
+ default:
+ for (int y=0; y<h; ++y) {
+ if (image.numColors())
+ for (int x=0; x<w; ++x)
+ out.setPixel(y, w-x-1, image.pixelIndex(x, y));
+ else
+ for (int x=0; x<w; ++x)
+ out.setPixel(y, w-x-1, image.pixel(x, y));
+ }
+ break;
+ }
+ return out;
+}
+
+/*!
+ Returns a copy of the image that is transformed using the given
+ transformation \a matrix and transformation \a mode.
+
+ The transformation \a matrix is internally adjusted to compensate
+ for unwanted translation; i.e. the image produced is the smallest
+ image that contains all the transformed points of the original
+ image. Use the trueMatrix() function to retrieve the actual matrix
+ used for transforming an image.
+
+ Unlike the other overload, this function can be used to perform perspective
+ transformations on images.
+
+ \sa trueMatrix(), {QImage#Image Transformations}{Image
+ Transformations}
+*/
+
+QImage QImage::transformed(const QTransform &matrix, Qt::TransformationMode mode ) const
+{
+ if (!d)
+ return QImage();
+
+ // source image data
+ int ws = width();
+ int hs = height();
+
+ // target image data
+ int wd;
+ int hd;
+
+ // compute size of target image
+ QTransform mat = trueMatrix(matrix, ws, hs);
+ bool complex_xform = false;
+ bool scale_xform = false;
+ if (mat.type() <= QTransform::TxScale) {
+ if (mat.type() == QTransform::TxNone) // identity matrix
+ return *this;
+ else if (mat.m11() == -1. && mat.m22() == -1.)
+ return rotated180(*this);
+
+ if (mode == Qt::FastTransformation) {
+ hd = qRound(qAbs(mat.m22()) * hs);
+ wd = qRound(qAbs(mat.m11()) * ws);
+ } else {
+ hd = int(qAbs(mat.m22()) * hs + 0.9999);
+ wd = int(qAbs(mat.m11()) * ws + 0.9999);
+ }
+ scale_xform = true;
+ } else {
+ if (mat.type() <= QTransform::TxRotate && mat.m11() == 0 && mat.m22() == 0) {
+ if (mat.m12() == 1. && mat.m21() == -1.)
+ return rotated90(*this);
+ else if (mat.m12() == -1. && mat.m21() == 1.)
+ return rotated270(*this);
+ }
+
+ QPolygonF a(QRectF(0, 0, ws, hs));
+ a = mat.map(a);
+ QRect r = a.boundingRect().toAlignedRect();
+ wd = r.width();
+ hd = r.height();
+ complex_xform = true;
+ }
+
+ if (wd == 0 || hd == 0)
+ return QImage();
+
+ // Make use of the optimized algorithm when we're scaling
+ if (scale_xform && mode == Qt::SmoothTransformation) {
+ if (mat.m11() < 0.0F && mat.m22() < 0.0F) { // horizontal/vertical flip
+ return smoothScaled(mirrored(true, true), wd, hd);
+ } else if (mat.m11() < 0.0F) { // horizontal flip
+ return smoothScaled(mirrored(true, false), wd, hd);
+ } else if (mat.m22() < 0.0F) { // vertical flip
+ return smoothScaled(mirrored(false, true), wd, hd);
+ } else { // no flipping
+ return smoothScaled(*this, wd, hd);
+ }
+ }
+
+ int bpp = depth();
+
+ int sbpl = bytesPerLine();
+ const uchar *sptr = bits();
+
+ QImage::Format target_format = d->format;
+
+ if (complex_xform || mode == Qt::SmoothTransformation) {
+ if (d->format < QImage::Format_RGB32 || !hasAlphaChannel()) {
+ switch(d->format) {
+ case QImage::Format_RGB16:
+ target_format = Format_ARGB8565_Premultiplied;
+ break;
+ case QImage::Format_RGB555:
+ target_format = Format_ARGB8555_Premultiplied;
+ break;
+ case QImage::Format_RGB666:
+ target_format = Format_ARGB6666_Premultiplied;
+ break;
+ case QImage::Format_RGB444:
+ target_format = Format_ARGB4444_Premultiplied;
+ break;
+ default:
+ target_format = Format_ARGB32_Premultiplied;
+ break;
+ }
+ }
+ }
+
+ QImage dImage(wd, hd, target_format);
+ QIMAGE_SANITYCHECK_MEMORY(dImage);
+
+ if (target_format == QImage::Format_MonoLSB
+ || target_format == QImage::Format_Mono
+ || target_format == QImage::Format_Indexed8) {
+ dImage.d->colortable = d->colortable;
+ dImage.d->has_alpha_clut = d->has_alpha_clut | complex_xform;
+ }
+
+ dImage.d->dpmx = dotsPerMeterX();
+ dImage.d->dpmy = dotsPerMeterY();
+
+ switch (bpp) {
+ // initizialize the data
+ case 8:
+ if (dImage.d->colortable.size() < 256) {
+ // colors are left in the color table, so pick that one as transparent
+ dImage.d->colortable.append(0x0);
+ memset(dImage.bits(), dImage.d->colortable.size() - 1, dImage.numBytes());
+ } else {
+ memset(dImage.bits(), 0, dImage.numBytes());
+ }
+ break;
+ case 1:
+ case 16:
+ case 24:
+ case 32:
+ memset(dImage.bits(), 0x00, dImage.numBytes());
+ break;
+ }
+
+ if (target_format >= QImage::Format_RGB32) {
+ QPainter p(&dImage);
+ if (mode == Qt::SmoothTransformation) {
+ p.setRenderHint(QPainter::Antialiasing);
+ p.setRenderHint(QPainter::SmoothPixmapTransform);
+ }
+ p.setTransform(mat);
+ p.drawImage(QPoint(0, 0), *this);
+ } else {
+ bool invertible;
+ mat = mat.inverted(&invertible); // invert matrix
+ if (!invertible) // error, return null image
+ return QImage();
+
+ // create target image (some of the code is from QImage::copy())
+ int type = format() == Format_Mono ? QT_XFORM_TYPE_MSBFIRST : QT_XFORM_TYPE_LSBFIRST;
+ int dbpl = dImage.bytesPerLine();
+ qt_xForm_helper(mat, 0, type, bpp, dImage.bits(), dbpl, 0, hd, sptr, sbpl, ws, hs);
+ }
+ return dImage;
+}
+
+/*!
+ \fn QTransform QImage::trueMatrix(const QTransform &matrix, int width, int height)
+
+ Returns the actual matrix used for transforming an image with the
+ given \a width, \a height and \a matrix.
+
+ When transforming an image using the transformed() function, the
+ transformation matrix is internally adjusted to compensate for
+ unwanted translation, i.e. transformed() returns the smallest
+ image containing all transformed points of the original image.
+ This function returns the modified matrix, which maps points
+ correctly from the original image into the new image.
+
+ Unlike the other overload, this function creates transformation
+ matrices that can be used to perform perspective
+ transformations on images.
+
+ \sa transformed(), {QImage#Image Transformations}{Image
+ Transformations}
+*/
+
+QTransform QImage::trueMatrix(const QTransform &matrix, int w, int h)
+{
+ const QRectF rect(0, 0, w, h);
+ const QRect mapped = matrix.mapRect(rect).toAlignedRect();
+ const QPoint delta = mapped.topLeft();
+ return matrix * QTransform().translate(-delta.x(), -delta.y());
+}
+
+
+/*!
+ \typedef QImage::DataPtr
+ \internal
+*/
+
+/*!
+ \fn DataPtr & QImage::data_ptr()
+ \internal
+*/
+
+QT_END_NAMESPACE
diff --git a/src/gui/image/qimage.h b/src/gui/image/qimage.h
new file mode 100644
index 0000000000..08c8d7c357
--- /dev/null
+++ b/src/gui/image/qimage.h
@@ -0,0 +1,352 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QIMAGE_H
+#define QIMAGE_H
+
+#include <QtGui/qtransform.h>
+#include <QtGui/qpaintdevice.h>
+#include <QtGui/qrgb.h>
+#include <QtCore/qbytearray.h>
+#include <QtCore/qrect.h>
+#include <QtCore/qstring.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+class QIODevice;
+class QStringList;
+class QMatrix;
+class QTransform;
+class QVariant;
+template <class T> class QList;
+template <class T> class QVector;
+
+struct QImageData;
+class QImageDataMisc; // internal
+#ifndef QT_NO_IMAGE_TEXT
+class Q_GUI_EXPORT QImageTextKeyLang {
+public:
+ QImageTextKeyLang(const char* k, const char* l) : key(k), lang(l) { }
+ QImageTextKeyLang() { }
+
+ QByteArray key;
+ QByteArray lang;
+
+ bool operator< (const QImageTextKeyLang& other) const
+ { return key < other.key || (key==other.key && lang < other.lang); }
+ bool operator== (const QImageTextKeyLang& other) const
+ { return key==other.key && lang==other.lang; }
+ inline bool operator!= (const QImageTextKeyLang &other) const
+ { return !operator==(other); }
+};
+#endif //QT_NO_IMAGE_TEXT
+
+
+class Q_GUI_EXPORT QImage : public QPaintDevice
+{
+public:
+ enum InvertMode { InvertRgb, InvertRgba };
+ enum Format {
+ Format_Invalid,
+ Format_Mono,
+ Format_MonoLSB,
+ Format_Indexed8,
+ Format_RGB32,
+ Format_ARGB32,
+ Format_ARGB32_Premultiplied,
+ Format_RGB16,
+ Format_ARGB8565_Premultiplied,
+ Format_RGB666,
+ Format_ARGB6666_Premultiplied,
+ Format_RGB555,
+ Format_ARGB8555_Premultiplied,
+ Format_RGB888,
+ Format_RGB444,
+ Format_ARGB4444_Premultiplied,
+#if 0
+ // reserved for future use
+ Format_RGB15,
+ Format_Grayscale16,
+ Format_Grayscale8,
+ Format_Grayscale4,
+ Format_Grayscale4LSB,
+ Format_Grayscale2,
+ Format_Grayscale2LSB
+#endif
+#ifndef qdoc
+ NImageFormats
+#endif
+ };
+
+ QImage();
+ QImage(const QSize &size, Format format);
+ QImage(int width, int height, Format format);
+ QImage(uchar *data, int width, int height, Format format);
+ QImage(const uchar *data, int width, int height, Format format);
+ QImage(uchar *data, int width, int height, int bytesPerLine, Format format);
+ QImage(const uchar *data, int width, int height, int bytesPerLine, Format format);
+
+#ifndef QT_NO_IMAGEFORMAT_XPM
+ explicit QImage(const char * const xpm[]);
+#endif
+ explicit QImage(const QString &fileName, const char *format = 0);
+#ifndef QT_NO_CAST_FROM_ASCII
+ explicit QImage(const char *fileName, const char *format = 0);
+#endif
+
+ QImage(const QImage &);
+ ~QImage();
+
+ QImage &operator=(const QImage &);
+ bool isNull() const;
+
+ int devType() const;
+
+ bool operator==(const QImage &) const;
+ bool operator!=(const QImage &) const;
+ operator QVariant() const;
+ void detach();
+ bool isDetached() const;
+
+ QImage copy(const QRect &rect = QRect()) const;
+ inline QImage copy(int x, int y, int w, int h) const
+ { return copy(QRect(x, y, w, h)); }
+
+ Format format() const;
+
+ QImage convertToFormat(Format f, Qt::ImageConversionFlags flags = Qt::AutoColor) const Q_REQUIRED_RESULT;
+ QImage convertToFormat(Format f, const QVector<QRgb> &colorTable, Qt::ImageConversionFlags flags = Qt::AutoColor) const Q_REQUIRED_RESULT;
+
+ int width() const;
+ int height() const;
+ QSize size() const;
+ QRect rect() const;
+
+ int depth() const;
+ int numColors() const;
+
+ QRgb color(int i) const;
+ void setColor(int i, QRgb c);
+ void setNumColors(int);
+
+ bool allGray() const;
+ bool isGrayscale() const;
+
+ uchar *bits();
+ const uchar *bits() const;
+ int numBytes() const;
+
+ uchar *scanLine(int);
+ const uchar *scanLine(int) const;
+ int bytesPerLine() const;
+
+ bool valid(int x, int y) const;
+ bool valid(const QPoint &pt) const;
+
+ int pixelIndex(int x, int y) const;
+ int pixelIndex(const QPoint &pt) const;
+
+ QRgb pixel(int x, int y) const;
+ QRgb pixel(const QPoint &pt) const;
+
+ void setPixel(int x, int y, uint index_or_rgb);
+ void setPixel(const QPoint &pt, uint index_or_rgb);
+
+ QVector<QRgb> colorTable() const;
+ void setColorTable(const QVector<QRgb> colors);
+
+ void fill(uint pixel);
+
+ bool hasAlphaChannel() const;
+ void setAlphaChannel(const QImage &alphaChannel);
+ QImage alphaChannel() const;
+ QImage createAlphaMask(Qt::ImageConversionFlags flags = Qt::AutoColor) const;
+#ifndef QT_NO_IMAGE_HEURISTIC_MASK
+ QImage createHeuristicMask(bool clipTight = true) const;
+#endif
+ QImage createMaskFromColor(QRgb color, Qt::MaskMode mode = Qt::MaskInColor) const;
+
+ inline QImage scaled(int w, int h, Qt::AspectRatioMode aspectMode = Qt::IgnoreAspectRatio,
+ Qt::TransformationMode mode = Qt::FastTransformation) const
+ { return scaled(QSize(w, h), aspectMode, mode); }
+ QImage scaled(const QSize &s, Qt::AspectRatioMode aspectMode = Qt::IgnoreAspectRatio,
+ Qt::TransformationMode mode = Qt::FastTransformation) const;
+ QImage scaledToWidth(int w, Qt::TransformationMode mode = Qt::FastTransformation) const;
+ QImage scaledToHeight(int h, Qt::TransformationMode mode = Qt::FastTransformation) const;
+ QImage transformed(const QMatrix &matrix, Qt::TransformationMode mode = Qt::FastTransformation) const;
+ static QMatrix trueMatrix(const QMatrix &, int w, int h);
+ QImage transformed(const QTransform &matrix, Qt::TransformationMode mode = Qt::FastTransformation) const;
+ static QTransform trueMatrix(const QTransform &, int w, int h);
+ QImage mirrored(bool horizontally = false, bool vertically = true) const;
+ QImage rgbSwapped() const;
+ void invertPixels(InvertMode = InvertRgb);
+
+
+ bool load(QIODevice *device, const char* format);
+ bool load(const QString &fileName, const char* format=0);
+ bool loadFromData(const uchar *buf, int len, const char *format = 0);
+ inline bool loadFromData(const QByteArray &data, const char* aformat=0)
+ { return loadFromData(reinterpret_cast<const uchar *>(data.constData()), data.size(), aformat); }
+
+ bool save(const QString &fileName, const char* format=0, int quality=-1) const;
+ bool save(QIODevice *device, const char* format=0, int quality=-1) const;
+
+ static QImage fromData(const uchar *data, int size, const char *format = 0);
+ inline static QImage fromData(const QByteArray &data, const char *format = 0)
+ { return fromData(reinterpret_cast<const uchar *>(data.constData()), data.size(), format); }
+
+ int serialNumber() const;
+ qint64 cacheKey() const;
+
+ QPaintEngine *paintEngine() const;
+
+ // Auxiliary data
+ int dotsPerMeterX() const;
+ int dotsPerMeterY() const;
+ void setDotsPerMeterX(int);
+ void setDotsPerMeterY(int);
+ QPoint offset() const;
+ void setOffset(const QPoint&);
+#ifndef QT_NO_IMAGE_TEXT
+ QStringList textKeys() const;
+ QString text(const QString &key = QString()) const;
+ void setText(const QString &key, const QString &value);
+
+ // The following functions are obsolete as of 4.1
+ QString text(const char* key, const char* lang=0) const;
+ QList<QImageTextKeyLang> textList() const;
+ QStringList textLanguages() const;
+ QString text(const QImageTextKeyLang&) const;
+ void setText(const char* key, const char* lang, const QString&);
+#endif
+
+#ifdef QT3_SUPPORT
+ enum Endian { BigEndian, LittleEndian, IgnoreEndian };
+ QT3_SUPPORT_CONSTRUCTOR QImage(int width, int height, int depth, int numColors=0, Endian bitOrder=IgnoreEndian);
+ QT3_SUPPORT_CONSTRUCTOR QImage(const QSize&, int depth, int numColors=0, Endian bitOrder=IgnoreEndian);
+ QT3_SUPPORT_CONSTRUCTOR QImage(uchar *data, int w, int h, int depth, const QRgb *colortable, int numColors, Endian bitOrder);
+#ifdef Q_WS_QWS
+ QT3_SUPPORT_CONSTRUCTOR QImage(uchar *data, int w, int h, int depth, int pbl, const QRgb *colortable, int numColors, Endian bitOrder);
+#endif
+ inline QT3_SUPPORT Endian bitOrder() const {
+ Format f = format();
+ return f == Format_Mono ? BigEndian : (f == Format_MonoLSB ? LittleEndian : IgnoreEndian);
+ }
+ QT3_SUPPORT QImage convertDepth(int, Qt::ImageConversionFlags flags = Qt::AutoColor) const;
+ QT3_SUPPORT QImage convertDepthWithPalette(int, QRgb* p, int pc, Qt::ImageConversionFlags flags = Qt::AutoColor) const;
+ QT3_SUPPORT QImage convertBitOrder(Endian) const;
+ QT3_SUPPORT bool hasAlphaBuffer() const;
+ QT3_SUPPORT void setAlphaBuffer(bool);
+ QT3_SUPPORT uchar **jumpTable();
+ QT3_SUPPORT const uchar * const *jumpTable() const;
+ inline QT3_SUPPORT void reset() { *this = QImage(); }
+ static inline QT3_SUPPORT Endian systemByteOrder()
+ { return QSysInfo::ByteOrder == QSysInfo::BigEndian ? BigEndian : LittleEndian; }
+ inline QT3_SUPPORT QImage swapRGB() const { return rgbSwapped(); }
+ inline QT3_SUPPORT QImage mirror(bool horizontally = false, bool vertically = true) const
+ { return mirrored(horizontally, vertically); }
+ QT3_SUPPORT bool create(const QSize&, int depth, int numColors=0, Endian bitOrder=IgnoreEndian);
+ QT3_SUPPORT bool create(int width, int height, int depth, int numColors=0, Endian bitOrder=IgnoreEndian);
+ inline QT3_SUPPORT QImage xForm(const QMatrix &matrix) const { return transformed(QTransform(matrix)); }
+ inline QT3_SUPPORT QImage smoothScale(int w, int h, Qt::AspectRatioMode mode = Qt::IgnoreAspectRatio) const
+ { return scaled(QSize(w, h), mode, Qt::SmoothTransformation); }
+ inline QImage QT3_SUPPORT smoothScale(const QSize &s, Qt::AspectRatioMode mode = Qt::IgnoreAspectRatio) const
+ { return scaled(s, mode, Qt::SmoothTransformation); }
+ inline QT3_SUPPORT QImage scaleWidth(int w) const { return scaledToWidth(w); }
+ inline QT3_SUPPORT QImage scaleHeight(int h) const { return scaledToHeight(h); }
+ inline QT3_SUPPORT void invertPixels(bool invertAlpha) { invertAlpha ? invertPixels(InvertRgba) : invertPixels(InvertRgb); }
+ inline QT3_SUPPORT QImage copy(int x, int y, int w, int h, Qt::ImageConversionFlags) const
+ { return copy(QRect(x, y, w, h)); }
+ inline QT3_SUPPORT QImage copy(const QRect &rect, Qt::ImageConversionFlags) const
+ { return copy(rect); }
+ static QT3_SUPPORT Endian systemBitOrder();
+ inline QT3_SUPPORT_CONSTRUCTOR QImage(const QByteArray &data)
+ { d = 0; *this = QImage::fromData(data); }
+#endif
+
+protected:
+ virtual int metric(PaintDeviceMetric metric) const;
+
+private:
+ friend class QWSOnScreenSurface;
+ QImageData *d;
+
+ friend class QRasterPixmapData;
+ friend class QDetachedPixmap;
+ friend Q_GUI_EXPORT qint64 qt_image_id(const QImage &image);
+ friend const QVector<QRgb> *qt_image_colortable(const QImage &image);
+
+public:
+ typedef QImageData * DataPtr;
+ inline DataPtr &data_ptr() { return d; }
+};
+
+Q_DECLARE_SHARED(QImage)
+Q_DECLARE_TYPEINFO(QImage, Q_MOVABLE_TYPE);
+
+// Inline functions...
+
+Q_GUI_EXPORT_INLINE bool QImage::valid(const QPoint &pt) const { return valid(pt.x(), pt.y()); }
+Q_GUI_EXPORT_INLINE int QImage::pixelIndex(const QPoint &pt) const { return pixelIndex(pt.x(), pt.y());}
+Q_GUI_EXPORT_INLINE QRgb QImage::pixel(const QPoint &pt) const { return pixel(pt.x(), pt.y()); }
+Q_GUI_EXPORT_INLINE void QImage::setPixel(const QPoint &pt, uint index_or_rgb) { setPixel(pt.x(), pt.y(), index_or_rgb); }
+
+// QImage stream functions
+
+#if !defined(QT_NO_DATASTREAM)
+Q_GUI_EXPORT QDataStream &operator<<(QDataStream &, const QImage &);
+Q_GUI_EXPORT QDataStream &operator>>(QDataStream &, QImage &);
+#endif
+
+#ifdef QT3_SUPPORT
+Q_GUI_EXPORT QT3_SUPPORT void bitBlt(QImage* dst, int dx, int dy, const QImage* src,
+ int sx=0, int sy=0, int sw=-1, int sh=-1, Qt::ImageConversionFlags flags = Qt::AutoColor);
+#endif
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QIMAGE_H
diff --git a/src/gui/image/qimage_p.h b/src/gui/image/qimage_p.h
new file mode 100644
index 0000000000..9b47c5c9e9
--- /dev/null
+++ b/src/gui/image/qimage_p.h
@@ -0,0 +1,110 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QIMAGE_P_H
+#define QIMAGE_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 <QtCore/qglobal.h>
+
+#include <QVector>
+
+#ifndef QT_NO_IMAGE_TEXT
+#include <QMap>
+#endif
+
+QT_BEGIN_NAMESPACE
+
+struct QImageData { // internal image data
+ QImageData();
+ ~QImageData();
+ static QImageData *create(const QSize &size, QImage::Format format, int numColors = 0);
+ static QImageData *create(uchar *data, int w, int h, int bpl, QImage::Format format, bool readOnly);
+
+ QAtomicInt ref;
+
+ int width;
+ int height;
+ int depth;
+ int nbytes; // number of bytes data
+ QVector<QRgb> colortable;
+ uchar *data;
+#ifdef QT3_SUPPORT
+ uchar **jumptable;
+#endif
+ QImage::Format format;
+ int bytes_per_line;
+ int ser_no; // serial number
+ int detach_no;
+
+ qreal dpmx; // dots per meter X (or 0)
+ qreal dpmy; // dots per meter Y (or 0)
+ QPoint offset; // offset in pixels
+
+ uint own_data : 1;
+ uint ro_data : 1;
+ uint has_alpha_clut : 1;
+ uint is_cached : 1;
+
+ bool checkForAlphaPixels() const;
+
+
+#ifndef QT_NO_IMAGE_TEXT
+ QMap<QString, QString> text;
+#endif
+ bool doImageIO(const QImage *image, QImageWriter* io, int quality) const;
+
+ QPaintEngine *paintEngine;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/gui/image/qimageiohandler.cpp b/src/gui/image/qimageiohandler.cpp
new file mode 100644
index 0000000000..a47c69e2d7
--- /dev/null
+++ b/src/gui/image/qimageiohandler.cpp
@@ -0,0 +1,571 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*!
+ \class QImageIOHandler
+ \brief The QImageIOHandler class defines the common image I/O
+ interface for all image formats in Qt.
+ \reentrant
+
+ Qt uses QImageIOHandler for reading and writing images through
+ QImageReader and QImageWriter. You can also derive from this class
+ to write your own image format handler using Qt's plugin mechanism.
+
+ Call setDevice() to assign a device to the handler, and
+ setFormat() to assign a format to it. One QImageIOHandler may
+ support more than one image format. canRead() returns true if an
+ image can be read from the device, and read() and write() return
+ true if reading or writing an image was completed successfully.
+
+ QImageIOHandler also has support for animations formats, through
+ the functions loopCount(), imageCount(), nextImageDelay() and
+ currentImageNumber().
+
+ In order to determine what options an image handler supports, Qt
+ will call supportsOption() and setOption(). Make sure to
+ reimplement these functions if you can provide support for any of
+ the options in the ImageOption enum.
+
+ To write your own image handler, you must at least reimplement
+ canRead() and read(). Then create a QImageIOPlugin that
+ can create the handler. Finally, install your plugin, and
+ QImageReader and QImageWriter will then automatically load the
+ plugin, and start using it.
+
+ \sa QImageIOPlugin, QImageReader, QImageWriter
+*/
+
+/*! \enum QImageIOHandler::ImageOption
+
+ This enum describes the different options supported by
+ QImageIOHandler. Some options are used to query an image for
+ properties, and others are used to toggle the way in which an
+ image should be written.
+
+ \value Size The original size of an image. A handler that supports
+ this option is expected to read the size of the image from the
+ image metadata, and return this size from option() as a QSize.
+
+ \value ClipRect The clip rect, or ROI (Region Of Interest). A
+ handler that supports this option is expected to only read the
+ provided QRect area from the original image in read(), before any
+ other transformation is applied.
+
+ \value ScaledSize The scaled size of the image. A handler that
+ supports this option is expected to scale the image to the
+ provided size (a QSize), after applying any clip rect
+ transformation (ClipRect). If the handler does not support this
+ option, QImageReader will perform the scaling after the image has
+ been read.
+
+ \value ScaledClipRect The scaled clip rect (or ROI, Region Of
+ Interest) of the image. A handler that supports this option is
+ expected to apply the provided clip rect (a QRect), after applying
+ any scaling (ScaleSize) or regular clipping (ClipRect). If the
+ handler does not support this option, QImageReader will apply the
+ scaled clip rect after the image has been read.
+
+ \value Description The image description. Some image formats,
+ such as GIF and PNG, allow embedding of text
+ or comments into the image data (e.g., for storing copyright
+ information). It's common that the text is stored in key-value
+ pairs, but some formats store all text in one continuous block.
+ QImageIOHandler returns the text as one
+ QString, where keys and values are separated by a ':', and
+ keys-value pairs are separated by two newlines (\\n\\n). For example,
+ "Title: Sunset\\n\\nAuthor: Jim Smith\\nSarah Jones\\n\\n". Formats that
+ store text in a single block can use "Description" as the key.
+
+ \value CompressionRatio The compression ratio of the image data. A
+ handler that supports this option is expected to set its
+ compression rate depending on the value of this option (an int)
+ when writing.
+
+ \value Gamma The gamma level of the image. A handler that supports
+ this option is expected to set the image gamma level depending on
+ the value of this option (a float) when writing.
+
+ \value Quality The quality level of the image. A handler that
+ supports this option is expected to set the image quality level
+ depending on the value of this option (an int) when writing.
+
+ \value Name The name of the image. A handler that supports this
+ option is expected to read the name from the image metadata and
+ return this as a QString, or when writing an image it is expected
+ to store the name in the image metadata.
+
+ \value SubType The subtype of the image. A handler that supports
+ this option can use the subtype value to help when reading and
+ writing images. For example, a PPM handler may have a subtype
+ value of "ppm" or "ppmraw".
+
+ \value IncrementalReading A handler that supports this option is
+ expected to read the image in several passes, as if it was an
+ animation. QImageReader will treat the image as an animation.
+
+ \value Endianness The endianness of the image. Certain image
+ formats can be stored as BigEndian or LittleEndian. A handler that
+ supports Endianness uses the value of this option to determine how
+ the image should be stored.
+
+ \value Animation Image formats that support animation return
+ true for this value in supportsOption(); otherwise, false is returned.
+
+ \value BackgroundColor Certain image formats allow the
+ background color to be specified. A handler that supports
+ BackgroundColor initializes the background color to this option
+ (a QColor) when reading an image.
+
+ \value ImageFormat The image's data format returned by the handler.
+ This can be any of the formats listed in QImage::Format.
+*/
+
+/*!
+ \class QImageIOPlugin
+ \brief The QImageIOPlugin class defines an interface for writing
+ an image format plugin.
+ \reentrant
+
+ \ingroup plugins
+
+ QImageIOPlugin is a factory for creating QImageIOHandler objects,
+ which are used internally by QImageReader and QImageWriter to add
+ support for different image formats to Qt.
+
+ Writing an image I/O plugin is achieved by subclassing this
+ base class, reimplementing the pure virtual functions capabilities(),
+ create(), and keys(), and exporting the class with the
+ Q_EXPORT_PLUGIN2() macro. See \l{How to Create Qt Plugins} for details.
+
+ An image format plugin can support three capabilities: reading (\l
+ CanRead), writing (\l CanWrite) and \e incremental reading (\l
+ CanReadIncremental). Reimplement capabilities() in you subclass to
+ expose the capabilities of your image format.
+
+ create() should create an instance of your QImageIOHandler
+ subclass, with the provided device and format properly set, and
+ return this handler. You must also reimplement keys() so that Qt
+ knows which image formats your plugin supports.
+
+ Different plugins can support different capabilities. For example,
+ you may have one plugin that supports reading the GIF format, and
+ another that supports writing. Qt will select the correct plugin
+ for the job, depending on the return value of capabilities(). If
+ several plugins support the same capability, Qt will select one
+ arbitrarily.
+
+ \sa QImageIOHandler, {How to Create Qt Plugins}
+*/
+
+/*!
+ \enum QImageIOPlugin::Capability
+
+ This enum describes the capabilities of a QImageIOPlugin.
+
+ \value CanRead The plugin can read images.
+ \value CanWrite The plugin can write images.
+ \value CanReadIncremental The plugin can read images incrementally.
+*/
+
+/*!
+ \class QImageIOHandlerFactoryInterface
+ \brief The QImageIOHandlerFactoryInterface class provides the factory
+ interface for QImageIOPlugin.
+ \reentrant
+
+ \internal
+
+ \sa QImageIOPlugin
+*/
+
+#include "qimageiohandler.h"
+
+#include <qbytearray.h>
+#include <qimage.h>
+#include <qvariant.h>
+
+QT_BEGIN_NAMESPACE
+
+class QIODevice;
+
+class QImageIOHandlerPrivate
+{
+ Q_DECLARE_PUBLIC(QImageIOHandler)
+public:
+ QImageIOHandlerPrivate(QImageIOHandler *q);
+ virtual ~QImageIOHandlerPrivate();
+
+ QIODevice *device;
+ mutable QByteArray format;
+
+ QImageIOHandler *q_ptr;
+};
+
+QImageIOHandlerPrivate::QImageIOHandlerPrivate(QImageIOHandler *q)
+{
+ device = 0;
+ q_ptr = q;
+}
+
+QImageIOHandlerPrivate::~QImageIOHandlerPrivate()
+{
+}
+
+/*!
+ Constructs a QImageIOHandler object.
+*/
+QImageIOHandler::QImageIOHandler()
+ : d_ptr(new QImageIOHandlerPrivate(this))
+{
+}
+
+/*! \internal
+
+ Constructs a QImageIOHandler object, using the private member \a
+ dd.
+*/
+QImageIOHandler::QImageIOHandler(QImageIOHandlerPrivate &dd)
+ : d_ptr(&dd)
+{
+}
+
+/*!
+ Destructs the QImageIOHandler object.
+*/
+QImageIOHandler::~QImageIOHandler()
+{
+ delete d_ptr;
+}
+
+/*!
+ Sets the device of the QImageIOHandler to \a device. The image
+ handler will use this device when reading and writing images.
+
+ The device can only be set once and must be set before calling
+ canRead(), read(), write(), etc. If you need to read multiple
+ files, construct multiple instances of the appropriate
+ QImageIOHandler subclass.
+
+ \sa device()
+*/
+void QImageIOHandler::setDevice(QIODevice *device)
+{
+ Q_D(QImageIOHandler);
+ d->device = device;
+}
+
+/*!
+ Returns the device currently assigned to the QImageIOHandler. If
+ not device has been assigned, 0 is returned.
+*/
+QIODevice *QImageIOHandler::device() const
+{
+ Q_D(const QImageIOHandler);
+ return d->device;
+}
+
+/*!
+ Sets the format of the QImageIOHandler to \a format. The format is
+ most useful for handlers that support multiple image formats.
+
+ \sa format()
+*/
+void QImageIOHandler::setFormat(const QByteArray &format)
+{
+ Q_D(QImageIOHandler);
+ d->format = format;
+}
+
+/*!
+ Sets the format of the QImageIOHandler to \a format. The format is
+ most useful for handlers that support multiple image formats.
+
+ This function is declared const so that it can be called from canRead().
+
+ \sa format()
+*/
+void QImageIOHandler::setFormat(const QByteArray &format) const
+{
+ Q_D(const QImageIOHandler);
+ d->format = format;
+}
+
+/*!
+ Returns the format that is currently assigned to
+ QImageIOHandler. If no format has been assigned, an empty string
+ is returned.
+
+ \sa setFormat()
+*/
+QByteArray QImageIOHandler::format() const
+{
+ Q_D(const QImageIOHandler);
+ return d->format;
+}
+
+/*!
+ \fn bool QImageIOHandler::read(QImage *image)
+
+ Read an image from the device, and stores it in \a image.
+ Returns true if the image is successfully read; otherwise returns
+ false.
+
+ For image formats that support incremental loading, and for animation
+ formats, the image handler can assume that \a image points to the
+ previous frame.
+
+ \sa canRead()
+*/
+
+/*!
+ \fn bool QImageIOHandler::canRead() const
+
+ Returns true if an image can be read from the device (i.e., the
+ image format is supported, the device can be read from and the
+ initial header information suggests that the image can be read);
+ otherwise returns false.
+
+ When reimplementing canRead(), make sure that the I/O device
+ (device()) is left in its original state (e.g., by using peek()
+ rather than read()).
+
+ \sa read(), QIODevice::peek()
+*/
+
+/*!
+ \obsolete
+
+ Use format() instead.
+*/
+
+QByteArray QImageIOHandler::name() const
+{
+ return format();
+}
+
+/*!
+ Writes the image \a image to the assigned device. Returns true on
+ success; otherwise returns false.
+
+ The default implementation does nothing, and simply returns false.
+*/
+bool QImageIOHandler::write(const QImage &image)
+{
+ Q_UNUSED(image);
+ return false;
+}
+
+/*!
+ Sets the option \a option with the value \a value.
+
+ \sa option(), ImageOption
+*/
+void QImageIOHandler::setOption(ImageOption option, const QVariant &value)
+{
+ Q_UNUSED(option);
+ Q_UNUSED(value);
+}
+
+/*!
+ Returns the value assigned to \a option as a QVariant. The type of
+ the value depends on the option. For example, option(Size) returns
+ a QSize variant.
+
+ \sa setOption(), supportsOption()
+*/
+QVariant QImageIOHandler::option(ImageOption option) const
+{
+ Q_UNUSED(option);
+ return QVariant();
+}
+
+/*!
+ Returns true if the QImageIOHandler supports the option \a option;
+ otherwise returns false. For example, if the QImageIOHandler
+ supports the \l Size option, supportsOption(Size) must return
+ true.
+
+ \sa setOption(), option()
+*/
+bool QImageIOHandler::supportsOption(ImageOption option) const
+{
+ Q_UNUSED(option);
+ return false;
+}
+
+/*!
+ For image formats that support animation, this function returns
+ the sequence number of the current image in the animation. If
+ this function is called before any image is read(), -1 is
+ returned. The number of the first image in the sequence is 0.
+
+ If the image format does not support animation, 0 is returned.
+
+ \sa read()
+*/
+int QImageIOHandler::currentImageNumber() const
+{
+ return 0;
+}
+
+/*!
+ Returns the rect of the current image. If no rect is defined for the
+ image, and empty QRect() is returned.
+
+ This function is useful for animations, where only parts of the frame
+ may be updated at a time.
+*/
+QRect QImageIOHandler::currentImageRect() const
+{
+ return QRect();
+}
+
+/*!
+ For image formats that support animation, this function returns
+ the number of images in the animation. If the image format does
+ not support animation, or if it is unable to determine the number
+ of images, 0 is returned.
+
+ The default implementation returns 1 if canRead() returns true;
+ otherwise 0 is returned.
+*/
+int QImageIOHandler::imageCount() const
+{
+ return canRead() ? 1 : 0;
+}
+
+/*!
+ For image formats that support animation, this function jumps to the
+ next image.
+
+ The default implementation does nothing, and returns false.
+*/
+bool QImageIOHandler::jumpToNextImage()
+{
+ return false;
+}
+
+/*!
+ For image formats that support animation, this function jumps to the image
+ whose sequence number is \a imageNumber. The next call to read() will
+ attempt to read this image.
+
+ The default implementation does nothing, and returns false.
+*/
+bool QImageIOHandler::jumpToImage(int imageNumber)
+{
+ Q_UNUSED(imageNumber);
+ return false;
+}
+
+/*!
+ For image formats that support animation, this function returns
+ the number of times the animation should loop. If the image format
+ does not support animation, 0 is returned.
+*/
+int QImageIOHandler::loopCount() const
+{
+ return 0;
+}
+
+/*!
+ For image formats that support animation, this function returns
+ the number of milliseconds to wait until reading the next
+ image. If the image format does not support animation, 0 is
+ returned.
+*/
+int QImageIOHandler::nextImageDelay() const
+{
+ return 0;
+}
+
+/*!
+ Constructs an image plugin with the given \a parent. This is
+ invoked automatically by the Q_EXPORT_PLUGIN2() macro.
+*/
+QImageIOPlugin::QImageIOPlugin(QObject *parent)
+ : QObject(parent)
+{
+}
+
+/*!
+ Destroys the picture format plugin.
+
+ You never have to call this explicitly. Qt destroys a plugin
+ automatically when it is no longer used.
+*/
+QImageIOPlugin::~QImageIOPlugin()
+{
+}
+
+/*! \fn QImageIOPlugin::capabilities(QIODevice *device, const QByteArray &format) const
+
+ Returns the capabilities on the plugin, based on the data in \a
+ device and the format \a format. For example, if the
+ QImageIOHandler supports the BMP format, and the data in the
+ device starts with the characters "BM", this function should
+ return \l CanRead. If \a format is "bmp" and the handler supports
+ both reading and writing, this function should return \l CanRead |
+ \l CanWrite.
+*/
+
+/*!
+ \fn QImageIOPlugin::keys() const
+
+ Returns the list of image keys this plugin supports.
+
+ These keys are usually the names of the image formats that are implemented
+ in the plugin (e.g., "jpg" or "gif").
+
+ \sa capabilities()
+*/
+
+/*!
+ \fn QImageIOHandler *QImageIOPlugin::create(QIODevice *device, const QByteArray &format) const
+
+ Creates and returns a QImageIOHandler subclass, with \a device
+ and \a format set. The \a format must come from the list returned by keys().
+ Format names are case sensitive.
+
+ \sa keys()
+*/
+
+QT_END_NAMESPACE
diff --git a/src/gui/image/qimageiohandler.h b/src/gui/image/qimageiohandler.h
new file mode 100644
index 0000000000..3b654f34ec
--- /dev/null
+++ b/src/gui/image/qimageiohandler.h
@@ -0,0 +1,151 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QIMAGEIOHANDLER_H
+#define QIMAGEIOHANDLER_H
+
+#include <QtCore/qplugin.h>
+#include <QtCore/qfactoryinterface.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+class QImage;
+class QRect;
+class QSize;
+class QVariant;
+
+class QImageIOHandlerPrivate;
+class Q_GUI_EXPORT QImageIOHandler
+{
+ Q_DECLARE_PRIVATE(QImageIOHandler)
+public:
+ QImageIOHandler();
+ virtual ~QImageIOHandler();
+
+ void setDevice(QIODevice *device);
+ QIODevice *device() const;
+
+ void setFormat(const QByteArray &format);
+ void setFormat(const QByteArray &format) const;
+ QByteArray format() const;
+
+ virtual QByteArray name() const;
+
+ virtual bool canRead() const = 0;
+ virtual bool read(QImage *image) = 0;
+ virtual bool write(const QImage &image);
+
+ enum ImageOption {
+ Size,
+ ClipRect,
+ Description,
+ ScaledClipRect,
+ ScaledSize,
+ CompressionRatio,
+ Gamma,
+ Quality,
+ Name,
+ SubType,
+ IncrementalReading,
+ Endianness,
+ Animation,
+ BackgroundColor,
+ ImageFormat
+ };
+ virtual QVariant option(ImageOption option) const;
+ virtual void setOption(ImageOption option, const QVariant &value);
+ virtual bool supportsOption(ImageOption option) const;
+
+ // incremental loading
+ virtual bool jumpToNextImage();
+ virtual bool jumpToImage(int imageNumber);
+ virtual int loopCount() const;
+ virtual int imageCount() const;
+ virtual int nextImageDelay() const;
+ virtual int currentImageNumber() const;
+ virtual QRect currentImageRect() const;
+
+protected:
+ QImageIOHandler(QImageIOHandlerPrivate &dd);
+ QImageIOHandlerPrivate *d_ptr;
+private:
+ Q_DISABLE_COPY(QImageIOHandler)
+};
+
+struct Q_GUI_EXPORT QImageIOHandlerFactoryInterface : public QFactoryInterface
+{
+ virtual QImageIOHandler *create(QIODevice *device, const QByteArray &format = QByteArray()) const = 0;
+};
+
+#define QImageIOHandlerFactoryInterface_iid "com.trolltech.Qt.QImageIOHandlerFactoryInterface"
+Q_DECLARE_INTERFACE(QImageIOHandlerFactoryInterface, QImageIOHandlerFactoryInterface_iid)
+
+class Q_GUI_EXPORT QImageIOPlugin : public QObject, public QImageIOHandlerFactoryInterface
+{
+ Q_OBJECT
+ Q_INTERFACES(QImageIOHandlerFactoryInterface:QFactoryInterface)
+public:
+ explicit QImageIOPlugin(QObject *parent = 0);
+ virtual ~QImageIOPlugin();
+
+ enum Capability {
+ CanRead = 0x1,
+ CanWrite = 0x2,
+ CanReadIncremental = 0x4
+ };
+ Q_DECLARE_FLAGS(Capabilities, Capability)
+
+ virtual Capabilities capabilities(QIODevice *device, const QByteArray &format) const = 0;
+ virtual QStringList keys() const = 0;
+ virtual QImageIOHandler *create(QIODevice *device, const QByteArray &format = QByteArray()) const = 0;
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(QImageIOPlugin::Capabilities)
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QIMAGEIOHANDLER_H
diff --git a/src/gui/image/qimagereader.cpp b/src/gui/image/qimagereader.cpp
new file mode 100644
index 0000000000..5de39d94e3
--- /dev/null
+++ b/src/gui/image/qimagereader.cpp
@@ -0,0 +1,1376 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//#define QIMAGEREADER_DEBUG
+
+/*!
+ \class QImageReader
+ \brief The QImageReader class provides a format independent interface
+ for reading images from files or other devices.
+
+ \reentrant
+ \ingroup multimedia
+ \ingroup io
+
+ The most common way to read images is through QImage and QPixmap's
+ constructors, or by calling QImage::load() and
+ QPixmap::load(). QImageReader is a specialized class which gives
+ you more control when reading images. For example, you can read an
+ image into a specific size by calling setScaledSize(), and you can
+ select a clip rect, effectively loading only parts of an image, by
+ calling setClipRect(). Depending on the underlying support in the
+ image format, this can save memory and speed up loading of images.
+
+ To read an image, you start by constructing a QImageReader object.
+ Pass either a file name or a device pointer, and the image format
+ to QImageReader's constructor. You can then set several options,
+ such as the clip rect (by calling setClipRect()) and scaled size
+ (by calling setScaledSize()). canRead() returns the image if the
+ QImageReader can read the image (i.e., the image format is
+ supported and the device is open for reading). Call read() to read
+ the image.
+
+ If any error occurs when reading the image, read() will return a
+ null QImage. You can then call error() to find the type of error
+ that occurred, or errorString() to get a human readable
+ description of what went wrong.
+
+ Call supportedImageFormats() for a list of formats that
+ QImageReader can read. QImageReader supports all built-in image
+ formats, in addition to any image format plugins that support
+ reading.
+
+ QImageReader autodetects the image format by default, by looking at the
+ provided (optional) format string, the file name suffix, and the data
+ stream contents. You can enable or disable this feature, by calling
+ setAutoDetectImageFormat().
+
+ \sa QImageWriter, QImageIOHandler, QImageIOPlugin
+*/
+
+/*!
+ \enum QImageReader::ImageReaderError
+
+ This enum describes the different types of errors that can occur
+ when reading images with QImageReader.
+
+ \value FileNotFoundError QImageReader was used with a file name,
+ but not file was found with that name. This can also happen if the
+ file name contained no extension, and the file with the correct
+ extension is not supported by Qt.
+
+ \value DeviceError QImageReader encountered a device error when
+ reading the image. You can consult your particular device for more
+ details on what went wrong.
+
+ \value UnsupportedFormatError Qt does not support the requested
+ image format.
+
+ \value InvalidDataError The image data was invalid, and
+ QImageReader was unable to read an image from it. The can happen
+ if the image file is damaged.
+
+ \value UnknownError An unknown error occurred. If you get this
+ value after calling read(), it is most likely caused by a bug in
+ QImageReader.
+*/
+#include "qimagereader.h"
+
+#include <qbytearray.h>
+#ifdef QIMAGEREADER_DEBUG
+#include <qdebug.h>
+#endif
+#include <qfile.h>
+#include <qfileinfo.h>
+#include <qimage.h>
+#include <qimageiohandler.h>
+#include <qlist.h>
+#include <qrect.h>
+#include <qset.h>
+#include <qsize.h>
+#include <qcolor.h>
+#include <qvariant.h>
+
+// factory loader
+#include <qcoreapplication.h>
+#include <private/qfactoryloader_p.h>
+
+// image handlers
+#include <private/qbmphandler_p.h>
+#include <private/qppmhandler_p.h>
+#include <private/qxbmhandler_p.h>
+#include <private/qxpmhandler_p.h>
+#ifndef QT_NO_IMAGEFORMAT_PNG
+#include <private/qpnghandler_p.h>
+#endif
+
+QT_BEGIN_NAMESPACE
+
+#if !defined (QT_NO_LIBRARY) && !defined(QT_NO_SETTINGS)
+Q_GLOBAL_STATIC_WITH_ARGS(QFactoryLoader, loader,
+ (QImageIOHandlerFactoryInterface_iid, QLatin1String("/imageformats")))
+#endif
+
+enum _qt_BuiltInFormatType {
+#ifndef QT_NO_IMAGEFORMAT_PNG
+ _qt_PngFormat,
+#endif
+ _qt_BmpFormat,
+#ifndef QT_NO_IMAGEFORMAT_PPM
+ _qt_PpmFormat,
+ _qt_PgmFormat,
+ _qt_PbmFormat,
+#endif
+#ifndef QT_NO_IMAGEFORMAT_XBM
+ _qt_XbmFormat,
+#endif
+#ifndef QT_NO_IMAGEFORMAT_XPM
+ _qt_XpmFormat,
+#endif
+ _qt_NumFormats,
+ _qt_NoFormat = -1
+};
+
+struct _qt_BuiltInFormatStruct
+{
+ _qt_BuiltInFormatType type;
+ const char *extension;
+};
+
+static const _qt_BuiltInFormatStruct _qt_BuiltInFormats[] = {
+#ifndef QT_NO_IMAGEFORMAT_PNG
+ {_qt_PngFormat, "png"},
+#endif
+ {_qt_BmpFormat, "bmp"},
+#ifndef QT_NO_IMAGEFORMAT_PPM
+ {_qt_PpmFormat, "ppm"},
+ {_qt_PgmFormat, "pgm"},
+ {_qt_PbmFormat, "pbm"},
+#endif
+#ifndef QT_NO_IMAGEFORMAT_XBM
+ {_qt_XbmFormat, "xbm"},
+#endif
+#ifndef QT_NO_IMAGEFORMAT_XPM
+ {_qt_XpmFormat, "xpm"},
+#endif
+ {_qt_NoFormat, ""}
+};
+
+static QImageIOHandler *createReadHandlerHelper(QIODevice *device,
+ const QByteArray &format, bool autoDetectImageFormat)
+{
+ if (!autoDetectImageFormat && format.isEmpty())
+ return 0;
+
+ QByteArray form = format.toLower();
+ QImageIOHandler *handler = 0;
+
+#if !defined (QT_NO_LIBRARY) && !defined(QT_NO_SETTINGS)
+ // check if we have plugins that support the image format
+ QFactoryLoader *l = loader();
+ QStringList keys = l->keys();
+#endif
+ QByteArray suffix;
+
+#ifdef QIMAGEREADER_DEBUG
+ qDebug() << "QImageReader::createReadHandler( device =" << (void *)device << ", format =" << format << "),"
+ << keys.size() << "plugins available: " << keys;
+#endif
+
+#if !defined (QT_NO_LIBRARY) && !defined(QT_NO_SETTINGS)
+ int suffixPluginIndex = -1;
+ if (device && format.isEmpty() && autoDetectImageFormat) {
+ // if there's no format, see if \a device is a file, and if so, find
+ // the file suffix and find support for that format among our plugins.
+ // this allows plugins to override our built-in handlers.
+ if (QFile *file = qobject_cast<QFile *>(device)) {
+#ifdef QIMAGEREADER_DEBUG
+ qDebug() << "QImageReader::createReadHandler: device is a file:" << file->fileName();
+#endif
+ if (!(suffix = QFileInfo(file->fileName()).suffix().toLower().toLatin1()).isEmpty()) {
+ int index = keys.indexOf(QString::fromLatin1(suffix));
+ if (index != -1) {
+#ifdef QIMAGEREADER_DEBUG
+ qDebug() << "QImageReader::createReadHandler: suffix recognized; the"
+ << suffix << "plugin might be able to read this";
+#endif
+ suffixPluginIndex = index;
+ }
+ }
+ }
+ }
+#endif // QT_NO_LIBRARY
+
+ QByteArray testFormat = !form.isEmpty() ? form : suffix;
+
+#if !defined (QT_NO_LIBRARY) && !defined(QT_NO_SETTINGS)
+ if (suffixPluginIndex != -1) {
+ // check if the plugin that claims support for this format can load
+ // from this device with this format.
+ const qint64 pos = device ? device->pos() : 0;
+ QImageIOPlugin *plugin = qobject_cast<QImageIOPlugin *>(l->instance(QString::fromLatin1(suffix)));
+ if (plugin && plugin->capabilities(device, testFormat) & QImageIOPlugin::CanRead) {
+ handler = plugin->create(device, testFormat);
+#ifdef QIMAGEREADER_DEBUG
+ qDebug() << "QImageReader::createReadHandler: using the" << suffix
+ << "plugin";
+#endif
+ }
+ if (device && !device->isSequential())
+ device->seek(pos);
+ }
+
+ if (!handler && !testFormat.isEmpty() && autoDetectImageFormat) {
+ // check if any plugin supports the format (they are not allowed to
+ // read from the device yet).
+ const qint64 pos = device ? device->pos() : 0;
+ for (int i = 0; i < keys.size(); ++i) {
+ if (i != suffixPluginIndex) {
+ QImageIOPlugin *plugin = qobject_cast<QImageIOPlugin *>(l->instance(keys.at(i)));
+ if (plugin && plugin->capabilities(device, testFormat) & QImageIOPlugin::CanRead) {
+#ifdef QIMAGEREADER_DEBUG
+ qDebug() << "QImageReader::createReadHandler: the" << keys.at(i) << "plugin can read this format";
+#endif
+ handler = plugin->create(device, testFormat);
+ break;
+ }
+ }
+ }
+ if (device && !device->isSequential())
+ device->seek(pos);
+ }
+#endif // QT_NO_LIBRARY
+
+ // if we don't have a handler yet, check if we have built-in support for
+ // the format
+ if (!handler && !testFormat.isEmpty()) {
+ if (false) {
+#ifndef QT_NO_IMAGEFORMAT_PNG
+ } else if (testFormat == "png") {
+ handler = new QPngHandler;
+#endif
+#ifndef QT_NO_IMAGEFORMAT_BMP
+ } else if (testFormat == "bmp") {
+ handler = new QBmpHandler;
+#endif
+#ifndef QT_NO_IMAGEFORMAT_XPM
+ } else if (testFormat == "xpm") {
+ handler = new QXpmHandler;
+#endif
+#ifndef QT_NO_IMAGEFORMAT_XBM
+ } else if (testFormat == "xbm") {
+ handler = new QXbmHandler;
+ handler->setOption(QImageIOHandler::SubType, testFormat);
+#endif
+#ifndef QT_NO_IMAGEFORMAT_PPM
+ } else if (testFormat == "pbm" || testFormat == "pbmraw" || testFormat == "pgm"
+ || testFormat == "pgmraw" || testFormat == "ppm" || testFormat == "ppmraw") {
+ handler = new QPpmHandler;
+ handler->setOption(QImageIOHandler::SubType, testFormat);
+#endif
+ }
+
+#ifdef QIMAGEREADER_DEBUG
+ if (handler)
+ qDebug() << "QImageReader::createReadHandler: using the built-in handler for" << testFormat;
+#endif
+ }
+
+#if !defined (QT_NO_LIBRARY) && !defined(QT_NO_SETTINGS)
+ if (!handler && autoDetectImageFormat) {
+ // check if any of our plugins recognize the file from its contents.
+ const qint64 pos = device ? device->pos() : 0;
+ for (int i = 0; i < keys.size(); ++i) {
+ if (i != suffixPluginIndex) {
+ QImageIOPlugin *plugin = qobject_cast<QImageIOPlugin *>(l->instance(keys.at(i)));
+ if (plugin && plugin->capabilities(device, QByteArray()) & QImageIOPlugin::CanRead) {
+ handler = plugin->create(device, testFormat);
+#ifdef QIMAGEREADER_DEBUG
+ qDebug() << "QImageReader::createReadHandler: the" << keys.at(i) << "plugin can read this data";
+#endif
+ break;
+ }
+ }
+ }
+ if (device && !device->isSequential())
+ device->seek(pos);
+ }
+#endif
+
+ if (!handler && autoDetectImageFormat) {
+ // check if any of our built-in handlers recognize the file from its
+ // contents.
+ int currentFormat = 0;
+ if (!suffix.isEmpty()) {
+ // If reading from a file with a suffix, start testing our
+ // built-in handler for that suffix first.
+ for (int i = 0; i < _qt_NumFormats; ++i) {
+ if (_qt_BuiltInFormats[i].extension == suffix) {
+ currentFormat = i;
+ break;
+ }
+ }
+ }
+
+ QByteArray subType;
+ int numFormats = _qt_NumFormats;
+ while (device && numFormats >= 0) {
+ const _qt_BuiltInFormatStruct *formatStruct = &_qt_BuiltInFormats[currentFormat];
+
+ const qint64 pos = device->pos();
+ switch (formatStruct->type) {
+#ifndef QT_NO_IMAGEFORMAT_PNG
+ case _qt_PngFormat:
+ if (QPngHandler::canRead(device))
+ handler = new QPngHandler;
+ break;
+#endif
+#ifndef QT_NO_IMAGEFORMAT_BMP
+ case _qt_BmpFormat:
+ if (QBmpHandler::canRead(device))
+ handler = new QBmpHandler;
+ break;
+#endif
+#ifndef QT_NO_IMAGEFORMAT_XPM
+ case _qt_XpmFormat:
+ if (QXpmHandler::canRead(device))
+ handler = new QXpmHandler;
+ break;
+#endif
+#ifndef QT_NO_IMAGEFORMAT_PPM
+ case _qt_PbmFormat:
+ case _qt_PgmFormat:
+ case _qt_PpmFormat:
+ if (QPpmHandler::canRead(device, &subType)) {
+ handler = new QPpmHandler;
+ handler->setOption(QImageIOHandler::SubType, subType);
+ }
+ break;
+#endif
+#ifndef QT_NO_IMAGEFORMAT_XBM
+ case _qt_XbmFormat:
+ if (QXbmHandler::canRead(device))
+ handler = new QXbmHandler;
+ break;
+#endif
+ default:
+ break;
+ }
+ if (!device->isSequential())
+ device->seek(pos);
+
+ if (handler) {
+#ifdef QIMAGEREADER_DEBUG
+ qDebug() << "QImageReader::createReadHandler: the" << formatStruct->extension
+ << "built-in handler can read this data";
+#endif
+ break;
+ }
+
+ --numFormats;
+ ++currentFormat;
+ currentFormat %= _qt_NumFormats;
+ }
+ }
+
+ if (!handler) {
+#ifdef QIMAGEREADER_DEBUG
+ qDebug() << "QImageReader::createReadHandler: no handlers found. giving up.";
+#endif
+ // no handler: give up.
+ return 0;
+ }
+
+ handler->setDevice(device);
+ if (!form.isEmpty())
+ handler->setFormat(form);
+ return handler;
+}
+
+class QImageReaderPrivate
+{
+public:
+ QImageReaderPrivate(QImageReader *qq);
+ ~QImageReaderPrivate();
+
+ // device
+ QByteArray format;
+ bool autoDetectImageFormat;
+ QIODevice *device;
+ bool deleteDevice;
+ QImageIOHandler *handler;
+ bool initHandler();
+
+ // image options
+ QRect clipRect;
+ QSize scaledSize;
+ QRect scaledClipRect;
+ int quality;
+ QMap<QString, QString> text;
+ void getText();
+
+ // error
+ QImageReader::ImageReaderError imageReaderError;
+ QString errorString;
+
+ QImageReader *q;
+};
+
+/*!
+ \internal
+*/
+QImageReaderPrivate::QImageReaderPrivate(QImageReader *qq)
+ : autoDetectImageFormat(true)
+{
+ device = 0;
+ deleteDevice = false;
+ handler = 0;
+ quality = -1;
+ imageReaderError = QImageReader::UnknownError;
+ errorString = QLatin1String(QT_TRANSLATE_NOOP(QImageReader, "Unknown error"));
+
+ q = qq;
+}
+
+/*!
+ \internal
+*/
+QImageReaderPrivate::~QImageReaderPrivate()
+{
+ if (deleteDevice)
+ delete device;
+ delete handler;
+}
+
+/*!
+ \internal
+*/
+bool QImageReaderPrivate::initHandler()
+{
+ // check some preconditions
+ if (!device || (!deleteDevice && !device->isOpen())) {
+ imageReaderError = QImageReader::DeviceError;
+ errorString = QLatin1String(QT_TRANSLATE_NOOP(QImageReader, "Invalid device"));
+ return false;
+ }
+
+ // probe the file extension
+ if (deleteDevice && !device->isOpen() && !device->open(QIODevice::ReadOnly) && autoDetectImageFormat) {
+ QList<QByteArray> extensions = QImageReader::supportedImageFormats();
+ if (!format.isEmpty()) {
+ // Try the most probable extension first
+ int currentFormatIndex = extensions.indexOf(format.toLower());
+ if (currentFormatIndex > 0)
+ extensions.swap(0, currentFormatIndex);
+ }
+
+ int currentExtension = 0;
+
+ QFile *file = static_cast<QFile *>(device);
+ QString fileName = file->fileName();
+
+ do {
+ file->setFileName(fileName + QLatin1Char('.')
+ + QString::fromLatin1(extensions.at(currentExtension++).constData()));
+ file->open(QIODevice::ReadOnly);
+ } while (!file->isOpen() && currentExtension < extensions.size());
+
+ if (!device->isOpen()) {
+ imageReaderError = QImageReader::FileNotFoundError;
+ errorString = QLatin1String(QT_TRANSLATE_NOOP(QImageReader, "File not found"));
+ file->setFileName(fileName); // restore the old file name
+ return false;
+ }
+ }
+
+ // assign a handler
+ if (!handler && (handler = createReadHandlerHelper(device, format, autoDetectImageFormat)) == 0) {
+ imageReaderError = QImageReader::UnsupportedFormatError;
+ errorString = QLatin1String(QT_TRANSLATE_NOOP(QImageReader, "Unsupported image format"));
+ return false;
+ }
+ return true;
+}
+
+/*!
+ \internal
+*/
+void QImageReaderPrivate::getText()
+{
+ if (!text.isEmpty() || (!handler && !initHandler()) || !handler->supportsOption(QImageIOHandler::Description))
+ return;
+ foreach (QString pair, handler->option(QImageIOHandler::Description).toString().split(
+ QLatin1String("\n\n"))) {
+ int index = pair.indexOf(QLatin1Char(':'));
+ if (index >= 0 && pair.indexOf(QLatin1Char(' ')) < index) {
+ text.insert(QLatin1String("Description"), pair.simplified());
+ } else {
+ QString key = pair.left(index);
+ text.insert(key, pair.mid(index + 2).simplified());
+ }
+ }
+}
+
+/*!
+ Constructs an empty QImageReader object. Before reading an image,
+ call setDevice() or setFileName().
+*/
+QImageReader::QImageReader()
+ : d(new QImageReaderPrivate(this))
+{
+}
+
+/*!
+ Constructs a QImageReader object with the device \a device and the
+ image format \a format.
+*/
+QImageReader::QImageReader(QIODevice *device, const QByteArray &format)
+ : d(new QImageReaderPrivate(this))
+{
+ d->device = device;
+ d->format = format;
+}
+
+/*!
+ Constructs a QImageReader object with the file name \a fileName
+ and the image format \a format.
+
+ \sa setFileName()
+*/
+QImageReader::QImageReader(const QString &fileName, const QByteArray &format)
+ : d(new QImageReaderPrivate(this))
+{
+ QFile *file = new QFile(fileName);
+ d->device = file;
+ d->deleteDevice = true;
+ d->format = format;
+}
+
+/*!
+ Destructs the QImageReader object.
+*/
+QImageReader::~QImageReader()
+{
+ delete d;
+}
+
+/*!
+ Sets the format QImageReader will use when reading images, to \a
+ format. \a format is a case insensitive text string. Example:
+
+ \snippet doc/src/snippets/code/src_gui_image_qimagereader.cpp 0
+
+ You can call supportedImageFormats() for the full list of formats
+ QImageReader supports.
+
+ \sa format()
+*/
+void QImageReader::setFormat(const QByteArray &format)
+{
+ d->format = format;
+}
+
+/*!
+ Returns the format QImageReader uses for reading images.
+
+ You can call this function after assigning a device to the
+ reader to determine the format of the device. For example:
+
+ \snippet doc/src/snippets/code/src_gui_image_qimagereader.cpp 1
+
+ If the reader cannot read any image from the device (e.g., there is no
+ image there, or the image has already been read), or if the format is
+ unsupported, this function returns an empty QByteArray().
+
+ \sa setFormat(), supportedImageFormats()
+*/
+QByteArray QImageReader::format() const
+{
+ if (d->format.isEmpty()) {
+ if (!d->initHandler())
+ return QByteArray();
+ return d->handler->canRead() ? d->handler->format() : QByteArray();
+ }
+
+ return d->format;
+}
+
+/*!
+ If \a enabled is true, image format autodetection is enabled; otherwise,
+ it is disabled. By default, autodetection is enabled.
+
+ QImageReader uses an extensive approach to detecting the image format;
+ firstly, if you pass a file name to QImageReader, it will attempt to
+ detect the file extension if the given file name does not point to an
+ existing file, by appending supported default extensions to the given file
+ name, one at a time. It then uses the following approach to detect the
+ image format:
+
+ \list
+
+ \o Image plugins are queried first, based on either the optional format
+ string, or the file name suffix (if the source device is a file). No
+ content detection is done at this stage. QImageReader will choose the
+ first plugin that supports reading for this format.
+
+ \o If no plugin supports the image format, Qt's built-in handlers are
+ checked based on either the optional format string, or the file name
+ suffix.
+
+ \o If no capable plugins or built-in handlers are found, each plugin is
+ tested by inspecting the content of the data stream.
+
+ \o If no plugins could detect the image format based on data contents,
+ each built-in image handler is tested by inspecting the contents.
+
+ \o Finally, if all above approaches fail, QImageReader will report failure
+ when trying to read the image.
+
+ \endlist
+
+ By disabling image format autodetection, QImageReader will only query the
+ plugins and built-in handlers based on the format string (i.e., no file
+ name extensions are tested).
+
+ \sa QImageIOHandler::canRead(), QImageIOPlugin::capabilities()
+*/
+void QImageReader::setAutoDetectImageFormat(bool enabled)
+{
+ d->autoDetectImageFormat = enabled;
+}
+
+/*!
+ Returns true if image format autodetection is enabled on this image
+ reader; otherwise returns false. By default, autodetection is enabled.
+
+ \sa setAutoDetectImageFormat()
+*/
+bool QImageReader::autoDetectImageFormat() const
+{
+ return d->autoDetectImageFormat;
+}
+
+/*!
+ Sets QImageReader's device to \a device. If a device has already
+ been set, the old device is removed from QImageReader and is
+ otherwise left unchanged.
+
+ If the device is not already open, QImageReader will attempt to
+ open the device in \l QIODevice::ReadOnly mode by calling
+ open(). Note that this does not work for certain devices, such as
+ QProcess, QTcpSocket and QUdpSocket, where more logic is required
+ to open the device.
+
+ \sa device(), setFileName()
+*/
+void QImageReader::setDevice(QIODevice *device)
+{
+ if (d->device && d->deleteDevice)
+ delete d->device;
+ d->device = device;
+ d->deleteDevice = false;
+ delete d->handler;
+ d->handler = 0;
+ d->text.clear();
+}
+
+/*!
+ Returns the device currently assigned to QImageReader, or 0 if no
+ device has been assigned.
+*/
+QIODevice *QImageReader::device() const
+{
+ return d->device;
+}
+
+/*!
+ Sets the file name of QImageReader to \a fileName. Internally,
+ QImageReader will create a QFile object and open it in \l
+ QIODevice::ReadOnly mode, and use this when reading images.
+
+ If \a fileName does not include a file extension (e.g., .png or .bmp),
+ QImageReader will cycle through all supported extensions until it finds
+ a matching file.
+
+ \sa fileName(), setDevice(), supportedImageFormats()
+*/
+void QImageReader::setFileName(const QString &fileName)
+{
+ setDevice(new QFile(fileName));
+ d->deleteDevice = true;
+}
+
+/*!
+ If the currently assigned device is a QFile, or if setFileName()
+ has been called, this function returns the name of the file
+ QImageReader reads from. Otherwise (i.e., if no device has been
+ assigned or the device is not a QFile), an empty QString is
+ returned.
+
+ \sa setFileName(), setDevice()
+*/
+QString QImageReader::fileName() const
+{
+ QFile *file = qobject_cast<QFile *>(d->device);
+ return file ? file->fileName() : QString();
+}
+
+/*!
+ \since 4.2
+
+ This is an image format specific function that sets the quality
+ level of the image to \a quality. For image formats that do not
+ support setting the quality, this value is ignored.
+
+ The value range of \a quality depends on the image format. For
+ example, the "jpeg" format supports a quality range from 0 (low
+ quality, high compression) to 100 (high quality, low compression).
+
+ \sa quality()
+*/
+void QImageReader::setQuality(int quality)
+{
+ d->quality = quality;
+}
+
+/*!
+ \since 4.2
+
+ Returns the quality level of the image.
+
+ \sa setQuality()
+*/
+int QImageReader::quality() const
+{
+ return d->quality;
+}
+
+
+/*!
+ Returns the size of the image, without actually reading the image
+ contents.
+
+ If the image format does not support this feature, this function returns
+ an invalid size. Qt's built-in image handlers all support this feature,
+ but custom image format plugins are not required to do so.
+
+ \sa QImageIOHandler::ImageOption, QImageIOHandler::option(), QImageIOHandler::supportsOption()
+*/
+QSize QImageReader::size() const
+{
+ if (!d->initHandler())
+ return QSize();
+
+ if (d->handler->supportsOption(QImageIOHandler::Size))
+ return d->handler->option(QImageIOHandler::Size).toSize();
+
+ return QSize();
+}
+
+/*!
+ \since 4.5
+
+ Returns the format of the image, without actually reading the image
+ contents. The format describes the image format \l QImageReader::read()
+ returns, not the format of the actual image.
+
+ If the image format does not support this feature, this function returns
+ an invalid format.
+
+ \sa QImageIOHandler::ImageOption, QImageIOHandler::option(), QImageIOHandler::supportsOption()
+*/
+QImage::Format QImageReader::imageFormat() const
+{
+ if (!d->initHandler())
+ return QImage::Format_Invalid;
+
+ if (d->handler->supportsOption(QImageIOHandler::ImageFormat))
+ return (QImage::Format)d->handler->option(QImageIOHandler::ImageFormat).toInt();
+
+ return QImage::Format_Invalid;
+}
+
+/*!
+ \since 4.1
+
+ Returns the text keys for this image. You can use
+ these keys with text() to list the image text for
+ a certain key.
+
+ Support for this option is implemented through
+ QImageIOHandler::Description.
+
+ \sa text(), QImageWriter::setText(), QImage::textKeys()
+*/
+QStringList QImageReader::textKeys() const
+{
+ d->getText();
+ return d->text.keys();
+}
+
+/*!
+ \since 4.1
+
+ Returns the image text associated with \a key.
+
+ Support for this option is implemented through
+ QImageIOHandler::Description.
+
+ \sa textKeys(), QImageWriter::setText()
+*/
+QString QImageReader::text(const QString &key) const
+{
+ d->getText();
+ return d->text.value(key);
+}
+
+/*!
+ Sets the image clip rect (also known as the ROI, or Region Of
+ Interest) to \a rect. The coordinates of \a rect are relative to
+ the untransformed image size, as returned by size().
+
+ \sa clipRect(), setScaledSize(), setScaledClipRect()
+*/
+void QImageReader::setClipRect(const QRect &rect)
+{
+ d->clipRect = rect;
+}
+
+/*!
+ Returns the clip rect (also known as the ROI, or Region Of
+ Interest) of the image. If no clip rect has been set, an invalid
+ QRect is returned.
+
+ \sa setClipRect()
+*/
+QRect QImageReader::clipRect() const
+{
+ return d->clipRect;
+}
+
+/*!
+ Sets the scaled size of the image to \a size. The scaling is
+ performed after the initial clip rect, but before the scaled clip
+ rect is applied. The algorithm used for scaling depends on the
+ image format. By default (i.e., if the image format does not
+ support scaling), QImageReader will use QImage::scale() with
+ Qt::SmoothScaling.
+
+ \sa scaledSize(), setClipRect(), setScaledClipRect()
+*/
+void QImageReader::setScaledSize(const QSize &size)
+{
+ d->scaledSize = size;
+}
+
+/*!
+ Returns the scaled size of the image.
+
+ \sa setScaledSize()
+*/
+QSize QImageReader::scaledSize() const
+{
+ return d->scaledSize;
+}
+
+/*!
+ Sets the scaled clip rect to \a rect. The scaled clip rect is the
+ clip rect (also known as ROI, or Region Of Interest) that is
+ applied after the image has been scaled.
+
+ \sa scaledClipRect(), setScaledSize()
+*/
+void QImageReader::setScaledClipRect(const QRect &rect)
+{
+ d->scaledClipRect = rect;
+}
+
+/*!
+ Returns the scaled clip rect of the image.
+
+ \sa setScaledClipRect()
+*/
+QRect QImageReader::scaledClipRect() const
+{
+ return d->scaledClipRect;
+}
+
+/*!
+ \since 4.1
+
+ Sets the background color to \a color.
+ Image formats that support this operation are expected to
+ initialize the background to \a color before reading an image.
+
+ \sa backgroundColor(), read()
+*/
+void QImageReader::setBackgroundColor(const QColor &color)
+{
+ if (!d->initHandler())
+ return;
+ if (d->handler->supportsOption(QImageIOHandler::BackgroundColor))
+ d->handler->setOption(QImageIOHandler::BackgroundColor, color);
+}
+
+/*!
+ \since 4.1
+
+ Returns the background color that's used when reading an image.
+ If the image format does not support setting the background color
+ an invalid color is returned.
+
+ \sa setBackgroundColor(), read()
+*/
+QColor QImageReader::backgroundColor() const
+{
+ if (!d->initHandler())
+ return QColor();
+ if (d->handler->supportsOption(QImageIOHandler::BackgroundColor))
+ return qVariantValue<QColor>(d->handler->option(QImageIOHandler::BackgroundColor));
+ return QColor();
+}
+
+/*!
+ \since 4.1
+
+ Returns true if the image format supports animation;
+ otherwise, false is returned.
+
+ \sa QMovie::supportedFormats()
+*/
+bool QImageReader::supportsAnimation() const
+{
+ if (!d->initHandler())
+ return false;
+ if (d->handler->supportsOption(QImageIOHandler::Animation))
+ return d->handler->option(QImageIOHandler::Animation).toBool();
+ return false;
+}
+
+/*!
+ Returns true if an image can be read for the device (i.e., the
+ image format is supported, and the device seems to contain valid
+ data); otherwise returns false.
+
+ canRead() is a lightweight function that only does a quick test to
+ see if the image data is valid. read() may still return false
+ after canRead() returns true, if the image data is corrupt.
+
+ For images that support animation, canRead() returns false when
+ all frames have been read.
+
+ \sa read(), supportedImageFormats()
+*/
+bool QImageReader::canRead() const
+{
+ if (!d->initHandler())
+ return false;
+
+ return d->handler->canRead();
+}
+
+/*!
+ Reads an image from the device. On success, the image that was
+ read is returned; otherwise, a null QImage is returned. You can
+ then call error() to find the type of error that occurred, or
+ errorString() to get a human readable description of the error.
+
+ For image formats that support animation, calling read()
+ repeatedly will return the next frame. When all frames have been
+ read, a null image will be returned.
+
+ \sa canRead(), supportedImageFormats(), supportsAnimation(), QMovie
+*/
+QImage QImageReader::read()
+{
+ // Because failed image reading might have side effects, we explicitly
+ // return a null image instead of the image we've just created.
+ QImage image;
+ return read(&image) ? image : QImage();
+}
+
+/*!
+ \overload
+
+ Reads an image from the device into \a image, which must point to a
+ QImage. Returns true on success; otherwise, returns false.
+
+ If \a image has same format and size as the image data that is about to be
+ read, this function may not need to allocate a new image before
+ reading. Because of this, it can be faster than the other read() overload,
+ which always constructs a new image; especially when reading several
+ images with the same format and size.
+
+ \snippet doc/src/snippets/code/src_gui_image_qimagereader.cpp 2
+
+ For image formats that support animation, calling read() repeatedly will
+ return the next frame. When all frames have been read, a null image will
+ be returned.
+
+ \sa canRead(), supportedImageFormats(), supportsAnimation(), QMovie
+*/
+bool QImageReader::read(QImage *image)
+{
+ if (!image) {
+ qWarning("QImageReader::read: cannot read into null pointer");
+ return false;
+ }
+
+ if (!d->handler && !d->initHandler())
+ return false;
+
+ // set the handler specific options.
+ if (d->handler->supportsOption(QImageIOHandler::ScaledSize) && d->scaledSize.isValid()) {
+ if ((d->handler->supportsOption(QImageIOHandler::ClipRect) && !d->clipRect.isNull())
+ || d->clipRect.isNull()) {
+ // Only enable the ScaledSize option if there is no clip rect, or
+ // if the handler also supports ClipRect.
+ d->handler->setOption(QImageIOHandler::ScaledSize, d->scaledSize);
+ }
+ }
+ if (d->handler->supportsOption(QImageIOHandler::ClipRect) && !d->clipRect.isNull())
+ d->handler->setOption(QImageIOHandler::ClipRect, d->clipRect);
+ if (d->handler->supportsOption(QImageIOHandler::ScaledClipRect) && !d->scaledClipRect.isNull())
+ d->handler->setOption(QImageIOHandler::ScaledClipRect, d->scaledClipRect);
+ if (d->handler->supportsOption(QImageIOHandler::Quality))
+ d->handler->setOption(QImageIOHandler::Quality, d->quality);
+
+ // read the image
+ if (!d->handler->read(image)) {
+ d->imageReaderError = InvalidDataError;
+ d->errorString = QLatin1String(QT_TRANSLATE_NOOP(QImageReader, "Unable to read image data"));
+ return false;
+ }
+
+ // provide default implementations for any unsupported image
+ // options
+ if (d->handler->supportsOption(QImageIOHandler::ClipRect) && !d->clipRect.isNull()) {
+ if (d->handler->supportsOption(QImageIOHandler::ScaledSize) && d->scaledSize.isValid()) {
+ if (d->handler->supportsOption(QImageIOHandler::ScaledClipRect) && !d->scaledClipRect.isNull()) {
+ // all features are supported by the handler; nothing to do.
+ } else {
+ // the image is already scaled, so apply scaled clipping.
+ if (!d->scaledClipRect.isNull())
+ *image = image->copy(d->scaledClipRect);
+ }
+ } else {
+ if (d->handler->supportsOption(QImageIOHandler::ScaledClipRect) && !d->scaledClipRect.isNull()) {
+ // supports scaled clipping but not scaling, most
+ // likely a broken handler.
+ } else {
+ if (d->scaledSize.isValid()) {
+ *image = image->scaled(d->scaledSize, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
+ }
+ if (d->scaledClipRect.isValid()) {
+ *image = image->copy(d->scaledClipRect);
+ }
+ }
+ }
+ } else {
+ if (d->handler->supportsOption(QImageIOHandler::ScaledSize) && d->scaledSize.isValid()) {
+ // in this case, there's nothing we can do. if the
+ // plugin supports scaled size but not ClipRect, then
+ // we have to ignore ClipRect."
+
+ if (d->handler->supportsOption(QImageIOHandler::ScaledClipRect) && !d->scaledClipRect.isNull()) {
+ // nothing to do (ClipRect is ignored!)
+ } else {
+ // provide all workarounds.
+ if (d->scaledClipRect.isValid()) {
+ *image = image->copy(d->scaledClipRect);
+ }
+ }
+ } else {
+ if (d->handler->supportsOption(QImageIOHandler::ScaledClipRect) && !d->scaledClipRect.isNull()) {
+ // this makes no sense; a handler that supports
+ // ScaledClipRect but not ScaledSize is broken, and we
+ // can't work around it.
+ } else {
+ // provide all workarounds.
+ if (d->clipRect.isValid())
+ *image = image->copy(d->clipRect);
+ if (d->scaledSize.isValid())
+ *image = image->scaled(d->scaledSize, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
+ if (d->scaledClipRect.isValid())
+ *image = image->copy(d->scaledClipRect);
+ }
+ }
+ }
+
+ return true;
+}
+
+/*!
+ For image formats that support animation, this function steps over the
+ current image, returning true if successful or false if there is no
+ following image in the animation.
+
+ The default implementation calls read(), then discards the resulting
+ image, but the image handler may have a more efficient way of implementing
+ this operation.
+
+ \sa jumpToImage(), QImageIOHandler::jumpToNextImage()
+*/
+bool QImageReader::jumpToNextImage()
+{
+ if (!d->initHandler())
+ return false;
+ return d->handler->jumpToNextImage();
+}
+
+/*!
+ For image formats that support animation, this function skips to the image
+ whose sequence number is \a imageNumber, returning true if successful
+ or false if the corresponding image cannot be found.
+
+ The next call to read() will attempt to read this image.
+
+ \sa jumpToNextImage(), QImageIOHandler::jumpToImage()
+*/
+bool QImageReader::jumpToImage(int imageNumber)
+{
+ if (!d->initHandler())
+ return false;
+ return d->handler->jumpToImage(imageNumber);
+}
+
+/*!
+ For image formats that support animation, this function returns
+ the number of times the animation should loop. Otherwise, it
+ returns -1.
+
+ \sa supportsAnimation(), QImageIOHandler::loopCount()
+*/
+int QImageReader::loopCount() const
+{
+ if (!d->initHandler())
+ return -1;
+ return d->handler->loopCount();
+}
+
+/*!
+ For image formats that support animation, this function returns
+ the total number of images in the animation.
+
+ Certain animation formats do not support this feature, in which
+ case 0 is returned.
+
+ \sa supportsAnimation(), QImageIOHandler::imageCount()
+*/
+int QImageReader::imageCount() const
+{
+ if (!d->initHandler())
+ return -1;
+ return d->handler->imageCount();
+}
+
+/*!
+ For image formats that support animation, this function returns
+ the number of milliseconds to wait until displaying the next frame
+ in the animation. Otherwise, 0 is returned.
+
+ \sa supportsAnimation(), QImageIOHandler::nextImageDelay()
+*/
+int QImageReader::nextImageDelay() const
+{
+ if (!d->initHandler())
+ return -1;
+ return d->handler->nextImageDelay();
+}
+
+/*!
+ For image formats that support animation, this function returns
+ the sequence number of the current frame. Otherwise, -1 is
+ returned.
+
+ \sa supportsAnimation(), QImageIOHandler::currentImageNumber()
+*/
+int QImageReader::currentImageNumber() const
+{
+ if (!d->initHandler())
+ return -1;
+ return d->handler->currentImageNumber();
+}
+
+/*!
+ For image formats that support animation, this function returns
+ the rect for the current frame. Otherwise, a null rect is returned.
+
+ \sa supportsAnimation(), QImageIOHandler::currentImageRect()
+*/
+QRect QImageReader::currentImageRect() const
+{
+ if (!d->initHandler())
+ return QRect();
+ return d->handler->currentImageRect();
+}
+
+/*!
+ Returns the type of error that occurred last.
+
+ \sa ImageReaderError, errorString()
+*/
+QImageReader::ImageReaderError QImageReader::error() const
+{
+ return d->imageReaderError;
+}
+
+/*!
+ Returns a human readable description of the last error that
+ occurred.
+
+ \sa error()
+*/
+QString QImageReader::errorString() const
+{
+ return d->errorString;
+}
+
+/*!
+ \since 4.2
+
+ Returns true if the reader supports \a option; otherwise returns
+ false.
+
+ Different image formats support different options. Call this function to
+ determine whether a certain option is supported by the current format. For
+ example, the PNG format allows you to embed text into the image's metadata
+ (see text()), and the BMP format allows you to determine the image's size
+ without loading the whole image into memory (see size()).
+
+ \snippet doc/src/snippets/code/src_gui_image_qimagereader.cpp 3
+
+ \sa QImageWriter::supportsOption()
+*/
+bool QImageReader::supportsOption(QImageIOHandler::ImageOption option) const
+{
+ if (!d->initHandler())
+ return false;
+ return d->handler->supportsOption(option);
+}
+
+/*!
+ If supported, this function returns the image format of the file
+ \a fileName. Otherwise, an empty string is returned.
+*/
+QByteArray QImageReader::imageFormat(const QString &fileName)
+{
+ QFile file(fileName);
+ if (!file.open(QFile::ReadOnly))
+ return QByteArray();
+
+ return imageFormat(&file);
+}
+
+/*!
+ If supported, this function returns the image format of the device
+ \a device. Otherwise, an empty string is returned.
+
+ \sa QImageReader::autoDetectImageFormat()
+*/
+QByteArray QImageReader::imageFormat(QIODevice *device)
+{
+ QByteArray format;
+ QImageIOHandler *handler = createReadHandlerHelper(device, format, /* autoDetectImageFormat = */ true);
+ if (handler) {
+ if (handler->canRead())
+ format = handler->format();
+ delete handler;
+ }
+ return format;
+}
+
+/*!
+ Returns the list of image formats supported by QImageReader.
+
+ By default, Qt can read the following formats:
+
+ \table
+ \header \o Format \o Description
+ \row \o BMP \o Windows Bitmap
+ \row \o GIF \o Graphic Interchange Format (optional)
+ \row \o JPG \o Joint Photographic Experts Group
+ \row \o JPEG \o Joint Photographic Experts Group
+ \row \o MNG \o Multiple-image Network Graphics
+ \row \o PNG \o Portable Network Graphics
+ \row \o PBM \o Portable Bitmap
+ \row \o PGM \o Portable Graymap
+ \row \o PPM \o Portable Pixmap
+ \row \o TIFF \o Tagged Image File Format
+ \row \o XBM \o X11 Bitmap
+ \row \o XPM \o X11 Pixmap
+ \endtable
+
+ Reading and writing SVG files is supported through Qt's
+ \l{QtSvg Module}{SVG Module}.
+
+ To configure Qt with GIF support, pass \c -qt-gif to the \c
+ configure script or check the appropriate option in the graphical
+ installer.
+
+ \sa setFormat(), QImageWriter::supportedImageFormats(), QImageIOPlugin
+*/
+QList<QByteArray> QImageReader::supportedImageFormats()
+{
+ QSet<QByteArray> formats;
+ for (int i = 0; i < _qt_NumFormats; ++i)
+ formats << _qt_BuiltInFormats[i].extension;
+
+#if !defined (QT_NO_LIBRARY) && !defined(QT_NO_SETTINGS)
+ QFactoryLoader *l = loader();
+ QStringList keys = l->keys();
+
+ for (int i = 0; i < keys.count(); ++i) {
+ QImageIOPlugin *plugin = qobject_cast<QImageIOPlugin *>(l->instance(keys.at(i)));
+ if (plugin && plugin->capabilities(0, keys.at(i).toLatin1()) & QImageIOPlugin::CanRead)
+ formats << keys.at(i).toLatin1();
+ }
+#endif // QT_NO_LIBRARY
+
+ QList<QByteArray> sortedFormats;
+ for (QSet<QByteArray>::ConstIterator it = formats.constBegin(); it != formats.constEnd(); ++it)
+ sortedFormats << *it;
+
+ qSort(sortedFormats);
+ return sortedFormats;
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/image/qimagereader.h b/src/gui/image/qimagereader.h
new file mode 100644
index 0000000000..95d4b9a809
--- /dev/null
+++ b/src/gui/image/qimagereader.h
@@ -0,0 +1,144 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QIMAGEREADER_H
+#define QIMAGEREADER_H
+
+#include <QtCore/qbytearray.h>
+#include <QtGui/qimage.h>
+#include <QtGui/qimageiohandler.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+class QColor;
+class QIODevice;
+class QRect;
+class QSize;
+class QStringList;
+
+class QImageReaderPrivate;
+class Q_GUI_EXPORT QImageReader
+{
+public:
+ enum ImageReaderError {
+ UnknownError,
+ FileNotFoundError,
+ DeviceError,
+ UnsupportedFormatError,
+ InvalidDataError
+ };
+
+ QImageReader();
+ explicit QImageReader(QIODevice *device, const QByteArray &format = QByteArray());
+ explicit QImageReader(const QString &fileName, const QByteArray &format = QByteArray());
+ ~QImageReader();
+
+ void setFormat(const QByteArray &format);
+ QByteArray format() const;
+
+ void setAutoDetectImageFormat(bool enabled);
+ bool autoDetectImageFormat() const;
+
+ void setDevice(QIODevice *device);
+ QIODevice *device() const;
+
+ void setFileName(const QString &fileName);
+ QString fileName() const;
+
+ QSize size() const;
+
+ QImage::Format imageFormat() const;
+
+ QStringList textKeys() const;
+ QString text(const QString &key) const;
+
+ void setClipRect(const QRect &rect);
+ QRect clipRect() const;
+
+ void setScaledSize(const QSize &size);
+ QSize scaledSize() const;
+
+ void setQuality(int quality);
+ int quality() const;
+
+ void setScaledClipRect(const QRect &rect);
+ QRect scaledClipRect() const;
+
+ void setBackgroundColor(const QColor &color);
+ QColor backgroundColor() const;
+
+ bool supportsAnimation() const;
+
+ bool canRead() const;
+ QImage read();
+ bool read(QImage *image);
+
+ bool jumpToNextImage();
+ bool jumpToImage(int imageNumber);
+ int loopCount() const;
+ int imageCount() const;
+ int nextImageDelay() const;
+ int currentImageNumber() const;
+ QRect currentImageRect() const;
+
+ ImageReaderError error() const;
+ QString errorString() const;
+
+ bool supportsOption(QImageIOHandler::ImageOption option) const;
+
+ static QByteArray imageFormat(const QString &fileName);
+ static QByteArray imageFormat(QIODevice *device);
+ static QList<QByteArray> supportedImageFormats();
+
+private:
+ Q_DISABLE_COPY(QImageReader)
+ QImageReaderPrivate *d;
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QIMAGEREADER_H
diff --git a/src/gui/image/qimagewriter.cpp b/src/gui/image/qimagewriter.cpp
new file mode 100644
index 0000000000..c24bbda696
--- /dev/null
+++ b/src/gui/image/qimagewriter.cpp
@@ -0,0 +1,690 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*!
+ \class QImageWriter
+ \brief The QImageWriter class provides a format independent interface
+ for writing images to files or other devices.
+
+ \reentrant
+ \ingroup multimedia
+ \ingroup io
+
+ QImageWriter supports setting format specific options, such as the
+ gamma level, compression level and quality, prior to storing the
+ image. If you do not need such options, you can use QImage::save()
+ or QPixmap::save() instead.
+
+ To store an image, you start by constructing a QImageWriter
+ object. Pass either a file name or a device pointer, and the
+ image format to QImageWriter's constructor. You can then set
+ several options, such as the gamma level (by calling setGamma())
+ and quality (by calling setQuality()). canWrite() returns true if
+ QImageWriter can write the image (i.e., the image format is
+ supported and the device is open for writing). Call write() to
+ write the image to the device.
+
+ If any error occurs when writing the image, write() will return
+ false. You can then call error() to find the type of error that
+ occurred, or errorString() to get a human readable description of
+ what went wrong.
+
+ Call supportedImageFormats() for a list of formats that
+ QImageWriter can write. QImageWriter supports all built-in image
+ formats, in addition to any image format plugins that support
+ writing.
+
+ \sa QImageReader, QImageIOHandler, QImageIOPlugin
+*/
+
+/*!
+ \enum QImageWriter::ImageWriterError
+
+ This enum describes errors that can occur when writing images with
+ QImageWriter.
+
+ \value DeviceError QImageWriter encountered a device error when
+ writing the image data. Consult your device for more details on
+ what went wrong.
+
+ \value UnsupportedFormatError Qt does not support the requested
+ image format.
+
+ \value UnknownError An unknown error occurred. If you get this
+ value after calling write(), it is most likely caused by a bug in
+ QImageWriter.
+*/
+
+#include "qimagewriter.h"
+
+#include <qbytearray.h>
+#include <qfile.h>
+#include <qfileinfo.h>
+#include <qimageiohandler.h>
+#include <qset.h>
+#include <qvariant.h>
+
+// factory loader
+#include <qcoreapplication.h>
+#include <private/qfactoryloader_p.h>
+
+// image handlers
+#include <private/qbmphandler_p.h>
+#include <private/qppmhandler_p.h>
+#include <private/qxbmhandler_p.h>
+#include <private/qxpmhandler_p.h>
+#ifndef QT_NO_IMAGEFORMAT_PNG
+#include <private/qpnghandler_p.h>
+#endif
+
+QT_BEGIN_NAMESPACE
+
+#if !defined (QT_NO_LIBRARY) && !defined(QT_NO_SETTINGS)
+Q_GLOBAL_STATIC_WITH_ARGS(QFactoryLoader, loader,
+ (QImageIOHandlerFactoryInterface_iid, QLatin1String("/imageformats")))
+#endif
+
+static QImageIOHandler *createWriteHandlerHelper(QIODevice *device,
+ const QByteArray &format)
+{
+ QByteArray form = format.toLower();
+ QByteArray suffix;
+ QImageIOHandler *handler = 0;
+
+#if !defined (QT_NO_LIBRARY) && !defined(QT_NO_SETTINGS)
+ // check if any plugins can write the image
+ QFactoryLoader *l = loader();
+ QStringList keys = l->keys();
+ int suffixPluginIndex = -1;
+#endif
+
+ if (device && format.isEmpty()) {
+ // if there's no format, see if \a device is a file, and if so, find
+ // the file suffix and find support for that format among our plugins.
+ // this allows plugins to override our built-in handlers.
+ if (QFile *file = qobject_cast<QFile *>(device)) {
+ if (!(suffix = QFileInfo(file->fileName()).suffix().toLower().toLatin1()).isEmpty()) {
+#if !defined (QT_NO_LIBRARY) && !defined(QT_NO_SETTINGS)
+ int index = keys.indexOf(QString::fromLatin1(suffix));
+ if (index != -1)
+ suffixPluginIndex = index;
+#endif
+ }
+ }
+ }
+
+ QByteArray testFormat = !form.isEmpty() ? form : suffix;
+
+#if !defined (QT_NO_LIBRARY) && !defined(QT_NO_SETTINGS)
+ if (suffixPluginIndex != -1) {
+ // when format is missing, check if we can find a plugin for the
+ // suffix.
+ QImageIOPlugin *plugin = qobject_cast<QImageIOPlugin *>(l->instance(QString::fromLatin1(suffix)));
+ if (plugin && (plugin->capabilities(device, suffix) & QImageIOPlugin::CanWrite))
+ handler = plugin->create(device, suffix);
+ }
+#endif // Q_NO_LIBRARY
+
+ // check if any built-in handlers can write the image
+ if (!handler && !testFormat.isEmpty()) {
+ if (false) {
+#ifndef QT_NO_IMAGEFORMAT_PNG
+ } else if (testFormat == "png") {
+ handler = new QPngHandler;
+#endif
+#ifndef QT_NO_IMAGEFORMAT_BMP
+ } else if (testFormat == "bmp") {
+ handler = new QBmpHandler;
+#endif
+#ifndef QT_NO_IMAGEFORMAT_XPM
+ } else if (testFormat == "xpm") {
+ handler = new QXpmHandler;
+#endif
+#ifndef QT_NO_IMAGEFORMAT_XBM
+ } else if (testFormat == "xbm") {
+ handler = new QXbmHandler;
+ handler->setOption(QImageIOHandler::SubType, testFormat);
+#endif
+#ifndef QT_NO_IMAGEFORMAT_PPM
+ } else if (testFormat == "pbm" || testFormat == "pbmraw" || testFormat == "pgm"
+ || testFormat == "pgmraw" || testFormat == "ppm" || testFormat == "ppmraw") {
+ handler = new QPpmHandler;
+ handler->setOption(QImageIOHandler::SubType, testFormat);
+#endif
+ }
+ }
+
+#if !defined (QT_NO_LIBRARY) && !defined(QT_NO_SETTINGS)
+ if (!testFormat.isEmpty()) {
+ for (int i = 0; i < keys.size(); ++i) {
+ QImageIOPlugin *plugin = qobject_cast<QImageIOPlugin *>(l->instance(keys.at(i)));
+ if (plugin && (plugin->capabilities(device, testFormat) & QImageIOPlugin::CanWrite)) {
+ handler = plugin->create(device, testFormat);
+ break;
+ }
+ }
+ }
+#endif
+
+ if (!handler)
+ return 0;
+
+ handler->setDevice(device);
+ if (!testFormat.isEmpty())
+ handler->setFormat(testFormat);
+ return handler;
+}
+
+class QImageWriterPrivate
+{
+public:
+ QImageWriterPrivate(QImageWriter *qq);
+
+ // device
+ QByteArray format;
+ QIODevice *device;
+ bool deleteDevice;
+ QImageIOHandler *handler;
+
+ // image options
+ int quality;
+ int compression;
+ float gamma;
+ QString description;
+ QString text;
+
+ // error
+ QImageWriter::ImageWriterError imageWriterError;
+ QString errorString;
+
+ QImageWriter *q;
+};
+
+/*!
+ \internal
+*/
+QImageWriterPrivate::QImageWriterPrivate(QImageWriter *qq)
+{
+ device = 0;
+ deleteDevice = false;
+ handler = 0;
+ quality = -1;
+ compression = 0;
+ gamma = 0.0;
+ imageWriterError = QImageWriter::UnknownError;
+ errorString = QT_TRANSLATE_NOOP(QImageWriter, QLatin1String("Unknown error"));
+
+ q = qq;
+}
+
+/*!
+ Constructs an empty QImageWriter object. Before writing, you must
+ call setFormat() to set an image format, then setDevice() or
+ setFileName().
+*/
+QImageWriter::QImageWriter()
+ : d(new QImageWriterPrivate(this))
+{
+}
+
+/*!
+ Constructs a QImageWriter object using the device \a device and
+ image format \a format.
+*/
+QImageWriter::QImageWriter(QIODevice *device, const QByteArray &format)
+ : d(new QImageWriterPrivate(this))
+{
+ d->device = device;
+ d->format = format;
+}
+
+/*!
+ Constructs a QImageWriter objects that will write to a file with
+ the name \a fileName, using the image format \a format. If \a
+ format is not provided, QImageWriter will detect the image format
+ by inspecting the extension of \a fileName.
+*/
+QImageWriter::QImageWriter(const QString &fileName, const QByteArray &format)
+ : d(new QImageWriterPrivate(this))
+{
+ QFile *file = new QFile(fileName);
+ d->device = file;
+ d->deleteDevice = true;
+ d->format = format;
+}
+
+/*!
+ Destructs the QImageWriter object.
+*/
+QImageWriter::~QImageWriter()
+{
+ if (d->deleteDevice)
+ delete d->device;
+ delete d->handler;
+ delete d;
+}
+
+/*!
+ Sets the format QImageWriter will use when writing images, to \a
+ format. \a format is a case insensitive text string. Example:
+
+ \snippet doc/src/snippets/code/src_gui_image_qimagewriter.cpp 0
+
+ You can call supportedImageFormats() for the full list of formats
+ QImageWriter supports.
+
+ \sa format()
+*/
+void QImageWriter::setFormat(const QByteArray &format)
+{
+ d->format = format;
+}
+
+/*!
+ Returns the format QImageWriter uses for writing images.
+
+ \sa setFormat()
+*/
+QByteArray QImageWriter::format() const
+{
+ return d->format;
+}
+
+/*!
+ Sets QImageWriter's device to \a device. If a device has already
+ been set, the old device is removed from QImageWriter and is
+ otherwise left unchanged.
+
+ If the device is not already open, QImageWriter will attempt to
+ open the device in \l QIODevice::WriteOnly mode by calling
+ open(). Note that this does not work for certain devices, such as
+ QProcess, QTcpSocket and QUdpSocket, where more logic is required
+ to open the device.
+
+ \sa device(), setFileName()
+*/
+void QImageWriter::setDevice(QIODevice *device)
+{
+ if (d->device && d->deleteDevice)
+ delete d->device;
+
+ d->device = device;
+ d->deleteDevice = false;
+ delete d->handler;
+ d->handler = 0;
+}
+
+/*!
+ Returns the device currently assigned to QImageWriter, or 0 if no
+ device has been assigned.
+*/
+QIODevice *QImageWriter::device() const
+{
+ return d->device;
+}
+
+/*!
+ Sets the file name of QImageWriter to \a fileName. Internally,
+ QImageWriter will create a QFile and open it in \l
+ QIODevice::WriteOnly mode, and use this file when writing images.
+
+ \sa fileName(), setDevice()
+*/
+void QImageWriter::setFileName(const QString &fileName)
+{
+ setDevice(new QFile(fileName));
+ d->deleteDevice = true;
+}
+
+/*!
+ If the currently assigned device is a QFile, or if setFileName()
+ has been called, this function returns the name of the file
+ QImageWriter writes to. Otherwise (i.e., if no device has been
+ assigned or the device is not a QFile), an empty QString is
+ returned.
+
+ \sa setFileName(), setDevice()
+*/
+QString QImageWriter::fileName() const
+{
+ QFile *file = qobject_cast<QFile *>(d->device);
+ return file ? file->fileName() : QString();
+}
+
+/*!
+ This is an image format specific function that sets the quality
+ level of the image to \a quality. For image formats that do not
+ support setting the quality, this value is ignored.
+
+ The value range of \a quality depends on the image format. For
+ example, the "jpeg" format supports a quality range from 0 (low
+ quality, high compression) to 100 (high quality, low compression).
+
+ \sa quality()
+*/
+void QImageWriter::setQuality(int quality)
+{
+ d->quality = quality;
+}
+
+/*!
+ Returns the quality level of the image.
+
+ \sa setQuality()
+*/
+int QImageWriter::quality() const
+{
+ return d->quality;
+}
+
+/*!
+ This is an image format specific function that set the compression
+ of an image. For image formats that do not support setting the
+ compression, this value is ignored.
+
+ The value range of \a compression depends on the image format. For
+ example, the "tiff" format supports two values, 0(no compression) and
+ 1(LZW-compression).
+
+ \sa compression()
+*/
+void QImageWriter::setCompression(int compression)
+{
+ d->compression = compression;
+}
+
+/*!
+ Returns the compression of the image.
+
+ \sa setCompression()
+*/
+int QImageWriter::compression() const
+{
+ return d->compression;
+}
+
+/*!
+ This is an image format specific function that sets the gamma
+ level of the image to \a gamma. For image formats that do not
+ support setting the gamma level, this value is ignored.
+
+ The value range of \a gamma depends on the image format. For
+ example, the "png" format supports a gamma range from 0.0 to 1.0.
+
+ \sa quality()
+*/
+void QImageWriter::setGamma(float gamma)
+{
+ d->gamma = gamma;
+}
+
+/*!
+ Returns the gamma level of the image.
+
+ \sa setGamma()
+*/
+float QImageWriter::gamma() const
+{
+ return d->gamma;
+}
+
+/*!
+ \obsolete
+
+ Use setText() instead.
+
+ This is an image format specific function that sets the
+ description of the image to \a description. For image formats that
+ do not support setting the description, this value is ignored.
+
+ The contents of \a description depends on the image format.
+
+ \sa description()
+*/
+void QImageWriter::setDescription(const QString &description)
+{
+ d->description = description;
+}
+
+/*!
+ \obsolete
+
+ Use QImageReader::text() instead.
+
+ Returns the description of the image.
+
+ \sa setDescription()
+*/
+QString QImageWriter::description() const
+{
+ return d->description;
+}
+
+/*!
+ \since 4.1
+
+ Sets the image text associated with the key \a key to
+ \a text. This is useful for storing copyright information
+ or other information about the image. Example:
+
+ \snippet doc/src/snippets/code/src_gui_image_qimagewriter.cpp 1
+
+ If you want to store a single block of data
+ (e.g., a comment), you can pass an empty key, or use
+ a generic key like "Description".
+
+ The key and text will be embedded into the
+ image data after calling write().
+
+ Support for this option is implemented through
+ QImageIOHandler::Description.
+
+ \sa QImage::setText(), QImageReader::text()
+*/
+void QImageWriter::setText(const QString &key, const QString &text)
+{
+ if (!d->description.isEmpty())
+ d->description += QLatin1String("\n\n");
+ d->description += key.simplified() + QLatin1String(": ") + text.simplified();
+}
+
+/*!
+ Returns true if QImageWriter can write the image; i.e., the image
+ format is supported and the assigned device is open for reading.
+
+ \sa write(), setDevice(), setFormat()
+*/
+bool QImageWriter::canWrite() const
+{
+ if (d->device && !d->handler && (d->handler = createWriteHandlerHelper(d->device, d->format)) == 0) {
+ d->imageWriterError = QImageWriter::UnsupportedFormatError;
+ d->errorString = QT_TRANSLATE_NOOP(QImageWriter,
+ QLatin1String("Unsupported image format"));
+ return false;
+ }
+ if (d->device && !d->device->isOpen())
+ d->device->open(QIODevice::WriteOnly);
+ if (!d->device || !d->device->isWritable()) {
+ d->imageWriterError = QImageWriter::DeviceError;
+ d->errorString = QT_TRANSLATE_NOOP(QImageWriter,
+ QLatin1String("Device not writable"));
+ return false;
+ }
+ return true;
+}
+
+/*!
+ Writes the image \a image to the assigned device or file
+ name. Returns true on success; otherwise returns false. If the
+ operation fails, you can call error() to find the type of error
+ that occurred, or errorString() to get a human readable
+ description of the error.
+
+ \sa canWrite(), error(), errorString()
+*/
+bool QImageWriter::write(const QImage &image)
+{
+ if (!canWrite())
+ return false;
+
+ if (d->handler->supportsOption(QImageIOHandler::Quality))
+ d->handler->setOption(QImageIOHandler::Quality, d->quality);
+ if (d->handler->supportsOption(QImageIOHandler::CompressionRatio))
+ d->handler->setOption(QImageIOHandler::CompressionRatio, d->compression);
+ if (d->handler->supportsOption(QImageIOHandler::Gamma))
+ d->handler->setOption(QImageIOHandler::Gamma, d->gamma);
+ if (!d->description.isEmpty() && d->handler->supportsOption(QImageIOHandler::Description))
+ d->handler->setOption(QImageIOHandler::Description, d->description);
+
+ if (!d->handler->write(image))
+ return false;
+ if (QFile *file = qobject_cast<QFile *>(d->device))
+ file->flush();
+ return true;
+}
+
+/*!
+ Returns the type of error that last occurred.
+
+ \sa ImageWriterError, errorString()
+*/
+QImageWriter::ImageWriterError QImageWriter::error() const
+{
+ return d->imageWriterError;
+}
+
+/*!
+ Returns a human readable description of the last error that occurred.
+
+ \sa error()
+*/
+QString QImageWriter::errorString() const
+{
+ return d->errorString;
+}
+
+/*!
+ \since 4.2
+
+ Returns true if the writer supports \a option; otherwise returns
+ false.
+
+ Different image formats support different options. Call this function to
+ determine whether a certain option is supported by the current format. For
+ example, the PNG format allows you to embed text into the image's metadata
+ (see text()).
+
+ \snippet doc/src/snippets/code/src_gui_image_qimagewriter.cpp 2
+
+ Options can be tested after the writer has been associated with a format.
+
+ \sa QImageReader::supportsOption(), setFormat()
+*/
+bool QImageWriter::supportsOption(QImageIOHandler::ImageOption option) const
+{
+ if (!d->handler && (d->handler = createWriteHandlerHelper(d->device, d->format)) == 0) {
+ d->imageWriterError = QImageWriter::UnsupportedFormatError;
+ d->errorString = QT_TRANSLATE_NOOP(QImageWriter,
+ QLatin1String("Unsupported image format"));
+ return false;
+ }
+
+ return d->handler->supportsOption(option);
+}
+
+/*!
+ Returns the list of image formats supported by QImageWriter.
+
+ By default, Qt can write the following formats:
+
+ \table
+ \header \o Format \o Description
+ \row \o BMP \o Windows Bitmap
+ \row \o JPG \o Joint Photographic Experts Group
+ \row \o JPEG \o Joint Photographic Experts Group
+ \row \o PNG \o Portable Network Graphics
+ \row \o PPM \o Portable Pixmap
+ \row \o TIFF \o Tagged Image File Format
+ \row \o XBM \o X11 Bitmap
+ \row \o XPM \o X11 Pixmap
+ \endtable
+
+ Reading and writing SVG files is supported through Qt's
+ \l{QtSvg Module}{SVG Module}.
+
+ \sa setFormat(), QImageReader::supportedImageFormats(), QImageIOPlugin
+*/
+QList<QByteArray> QImageWriter::supportedImageFormats()
+{
+ QSet<QByteArray> formats;
+ formats << "bmp";
+#ifndef QT_NO_IMAGEFORMAT_PPM
+ formats << "ppm";
+#endif
+#ifndef QT_NO_IMAGEFORMAT_XBM
+ formats << "xbm";
+#endif
+#ifndef QT_NO_IMAGEFORMAT_XPM
+ formats << "xpm";
+#endif
+#ifndef QT_NO_IMAGEFORMAT_PNG
+ formats << "png";
+#endif
+
+#if !defined (QT_NO_LIBRARY) && !defined(QT_NO_SETTINGS)
+ QFactoryLoader *l = loader();
+ QStringList keys = l->keys();
+ for (int i = 0; i < keys.count(); ++i) {
+ QImageIOPlugin *plugin = qobject_cast<QImageIOPlugin *>(l->instance(keys.at(i)));
+ if (plugin && (plugin->capabilities(0, keys.at(i).toLatin1()) & QImageIOPlugin::CanWrite) != 0)
+ formats << keys.at(i).toLatin1();
+ }
+#endif // QT_NO_LIBRARY
+
+ QList<QByteArray> sortedFormats;
+ for (QSet<QByteArray>::ConstIterator it = formats.constBegin(); it != formats.constEnd(); ++it)
+ sortedFormats << *it;
+
+ qSort(sortedFormats);
+ return sortedFormats;
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/image/qimagewriter.h b/src/gui/image/qimagewriter.h
new file mode 100644
index 0000000000..57566181e2
--- /dev/null
+++ b/src/gui/image/qimagewriter.h
@@ -0,0 +1,116 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QIMAGEWRITER_H
+#define QIMAGEWRITER_H
+
+#include <QtCore/qbytearray.h>
+#include <QtCore/qlist.h>
+#include <QtGui/qimageiohandler.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+class QIODevice;
+class QImage;
+
+class QImageWriterPrivate;
+class Q_GUI_EXPORT QImageWriter
+{
+public:
+ enum ImageWriterError {
+ UnknownError,
+ DeviceError,
+ UnsupportedFormatError
+ };
+
+ QImageWriter();
+ explicit QImageWriter(QIODevice *device, const QByteArray &format);
+ explicit QImageWriter(const QString &fileName, const QByteArray &format = QByteArray());
+ ~QImageWriter();
+
+ void setFormat(const QByteArray &format);
+ QByteArray format() const;
+
+ void setDevice(QIODevice *device);
+ QIODevice *device() const;
+
+ void setFileName(const QString &fileName);
+ QString fileName() const;
+
+ void setQuality(int quality);
+ int quality() const;
+
+ void setCompression(int compression);
+ int compression() const;
+
+ void setGamma(float gamma);
+ float gamma() const;
+
+ // Obsolete as of 4.1
+ void setDescription(const QString &description);
+ QString description() const;
+
+ void setText(const QString &key, const QString &text);
+
+ bool canWrite() const;
+ bool write(const QImage &image);
+
+ ImageWriterError error() const;
+ QString errorString() const;
+
+ bool supportsOption(QImageIOHandler::ImageOption option) const;
+
+ static QList<QByteArray> supportedImageFormats();
+
+private:
+ Q_DISABLE_COPY(QImageWriter)
+ QImageWriterPrivate *d;
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QIMAGEWRITER_H
diff --git a/src/gui/image/qmovie.cpp b/src/gui/image/qmovie.cpp
new file mode 100644
index 0000000000..ca69cab9b7
--- /dev/null
+++ b/src/gui/image/qmovie.cpp
@@ -0,0 +1,1081 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*!
+ \class QMovie
+
+ \brief The QMovie class is a convenience class for playing movies
+ with QImageReader.
+
+ \ingroup multimedia
+
+ First, create a QMovie object by passing either the name of a file or a
+ pointer to a QIODevice containing an animated image format to QMovie's
+ constructor. You can call isValid() to check if the image data is valid,
+ before starting the movie. To start the movie, call start(). QMovie will
+ enter \l Running state, and emit started() and stateChanged(). To get the
+ current state of the movie, call state().
+
+ To display the movie in your application, you can pass your QMovie object
+ to QLabel::setMovie(). Example:
+
+ \snippet doc/src/snippets/code/src_gui_image_qmovie.cpp 0
+
+ Whenever a new frame is available in the movie, QMovie will emit
+ updated(). If the size of the frame changes, resized() is emitted. You can
+ call currentImage() or currentPixmap() to get a copy of the current
+ frame. When the movie is done, QMovie emits finished(). If any error
+ occurs during playback (i.e, the image file is corrupt), QMovie will emit
+ error().
+
+ You can control the speed of the movie playback by calling setSpeed(),
+ which takes the percentage of the original speed as an argument. Pause the
+ movie by calling setPaused(true). QMovie will then enter \l Paused state
+ and emit stateChanged(). If you call setPaused(false), QMovie will reenter
+ \l Running state and start the movie again. To stop the movie, call
+ stop().
+
+ Certain animation formats allow you to set the background color. You can
+ call setBackgroundColor() to set the color, or backgroundColor() to
+ retrieve the current background color.
+
+ currentFrameNumber() returns the sequence number of the current frame. The
+ first frame in the animation has the sequence number 0. frameCount()
+ returns the total number of frames in the animation, if the image format
+ supports this. You can call loopCount() to get the number of times the
+ movie should loop before finishing. nextFrameDelay() returns the number of
+ milliseconds the current frame should be displayed.
+
+ QMovie can be instructed to cache frames of an animation by calling
+ setCacheMode().
+
+ Call supportedFormats() for a list of formats that QMovie supports.
+
+ \sa QLabel, QImageReader, {Movie Example}
+*/
+
+/*! \enum QMovie::MovieState
+
+ This enum describes the different states of QMovie.
+
+ \value NotRunning The movie is not running. This is QMovie's initial
+ state, and the state it enters after stop() has been called or the movie
+ is finished.
+
+ \value Paused The movie is paused, and QMovie stops emitting updated() or
+ resized(). This state is entered after calling pause() or
+ setPaused(true). The current frame number it kept, and the movie will
+ continue with the next frame when unpause() or setPaused(false) is called.
+
+ \value Running The movie is running.
+*/
+
+/*! \enum QMovie::CacheMode
+
+ This enum describes the different cache modes of QMovie.
+
+ \value CacheNone No frames are cached (the default).
+
+ \value CacheAll All frames are cached.
+*/
+
+/*! \fn void QMovie::started()
+
+ This signal is emitted after QMovie::start() has been called, and QMovie
+ has entered QMovie::Running state.
+*/
+
+/*! \fn void QMovie::resized(const QSize &size)
+
+ This signal is emitted when the current frame has been resized to \a
+ size. This effect is sometimes used in animations as an alternative to
+ replacing the frame. You can call currentImage() or currentPixmap() to get a
+ copy of the updated frame.
+*/
+
+/*! \fn void QMovie::updated(const QRect &rect)
+
+ This signal is emitted when the rect \a rect in the current frame has been
+ updated. You can call currentImage() or currentPixmap() to get a copy of the
+ updated frame.
+*/
+
+/*! \fn void QMovie::frameChanged(int frameNumber)
+ \since 4.1
+
+ This signal is emitted when the frame number has changed to
+ \a frameNumber. You can call currentImage() or currentPixmap() to get a
+ copy of the frame.
+*/
+
+/*!
+ \fn void QMovie::stateChanged(QMovie::MovieState state)
+
+ This signal is emitted every time the state of the movie changes. The new
+ state is specified by \a state.
+
+ \sa QMovie::state()
+*/
+
+/*! \fn void QMovie::error(QImageReader::ImageReaderError error)
+
+ This signal is emitted by QMovie when the error \a error occurred during
+ playback. QMovie will stop the movie, and enter QMovie::NotRunning state.
+*/
+
+/*! \fn void QMovie::finished()
+
+ This signal is emitted when the movie has finished.
+
+ \sa QMovie::stop()
+*/
+
+#include "qglobal.h"
+
+#ifndef QT_NO_MOVIE
+
+#include "qmovie.h"
+#include "qimage.h"
+#include "qimagereader.h"
+#include "qpixmap.h"
+#include "qrect.h"
+#include "qdatetime.h"
+#include "qtimer.h"
+#include "qpair.h"
+#include "qmap.h"
+#include "qlist.h"
+#include "qbuffer.h"
+#include "qdir.h"
+#include "private/qobject_p.h"
+
+#define QMOVIE_INVALID_DELAY -1
+
+QT_BEGIN_NAMESPACE
+
+class QFrameInfo
+{
+public:
+ QPixmap pixmap;
+ int delay;
+ bool endMark;
+ inline QFrameInfo(bool endMark)
+ : pixmap(QPixmap()), delay(QMOVIE_INVALID_DELAY), endMark(endMark)
+ { }
+
+ inline QFrameInfo()
+ : pixmap(QPixmap()), delay(QMOVIE_INVALID_DELAY), endMark(false)
+ { }
+
+ inline QFrameInfo(const QPixmap &pixmap, int delay)
+ : pixmap(pixmap), delay(delay), endMark(false)
+ { }
+
+ inline bool isValid()
+ {
+ return endMark || !(pixmap.isNull() && (delay == QMOVIE_INVALID_DELAY));
+ }
+
+ inline bool isEndMarker()
+ { return endMark; }
+
+ static inline QFrameInfo endMarker()
+ { return QFrameInfo(true); }
+};
+
+class QMoviePrivate : public QObjectPrivate
+{
+ Q_DECLARE_PUBLIC(QMovie)
+
+public:
+ QMoviePrivate(QMovie *qq);
+ bool isDone();
+ bool next();
+ int speedAdjustedDelay(int delay) const;
+ bool isValid() const;
+ bool jumpToFrame(int frameNumber);
+ int frameCount() const;
+ bool jumpToNextFrame();
+ QFrameInfo infoForFrame(int frameNumber);
+ void reset();
+
+ inline void enterState(QMovie::MovieState newState) {
+ movieState = newState;
+ emit q_func()->stateChanged(newState);
+ }
+
+ // private slots
+ void _q_loadNextFrame();
+ void _q_loadNextFrame(bool starting);
+
+ QImageReader *reader;
+ int speed;
+ QMovie::MovieState movieState;
+ QRect frameRect;
+ QPixmap currentPixmap;
+ int currentFrameNumber;
+ int nextFrameNumber;
+ int greatestFrameNumber;
+ int nextDelay;
+ int playCounter;
+ qint64 initialDevicePos;
+ QMovie::CacheMode cacheMode;
+ bool haveReadAll;
+ bool isFirstIteration;
+ QMap<int, QFrameInfo> frameMap;
+ QString absoluteFilePath;
+
+ QTimer nextImageTimer;
+};
+
+/*! \internal
+ */
+QMoviePrivate::QMoviePrivate(QMovie *qq)
+ : reader(0), speed(100), movieState(QMovie::NotRunning),
+ currentFrameNumber(-1), nextFrameNumber(0), greatestFrameNumber(-1),
+ nextDelay(0), playCounter(-1),
+ cacheMode(QMovie::CacheNone), haveReadAll(false), isFirstIteration(true)
+{
+ q_ptr = qq;
+ nextImageTimer.setSingleShot(true);
+}
+
+/*! \internal
+ */
+void QMoviePrivate::reset()
+{
+ nextImageTimer.stop();
+ if (reader->device())
+ initialDevicePos = reader->device()->pos();
+ currentFrameNumber = -1;
+ nextFrameNumber = 0;
+ greatestFrameNumber = -1;
+ nextDelay = 0;
+ playCounter = -1;
+ haveReadAll = false;
+ isFirstIteration = true;
+ frameMap.clear();
+}
+
+/*! \internal
+ */
+bool QMoviePrivate::isDone()
+{
+ return (playCounter == 0);
+}
+
+/*!
+ \internal
+
+ Given the original \a delay, this function returns the
+ actual number of milliseconds to delay according to
+ the current speed. E.g. if the speed is 200%, the
+ result will be half of the original delay.
+*/
+int QMoviePrivate::speedAdjustedDelay(int delay) const
+{
+ return int( (qint64(delay) * qint64(100) ) / qint64(speed) );
+}
+
+/*!
+ \internal
+
+ Returns the QFrameInfo for the given \a frameNumber.
+
+ If the frame number is invalid, an invalid QFrameInfo is
+ returned.
+
+ If the end of the animation has been reached, a
+ special end marker QFrameInfo is returned.
+
+*/
+QFrameInfo QMoviePrivate::infoForFrame(int frameNumber)
+{
+ if (frameNumber < 0)
+ return QFrameInfo(); // Invalid
+
+ if (haveReadAll && (frameNumber > greatestFrameNumber)) {
+ if (frameNumber == greatestFrameNumber+1)
+ return QFrameInfo::endMarker();
+ return QFrameInfo(); // Invalid
+ }
+
+ if (cacheMode == QMovie::CacheNone) {
+ if (frameNumber != currentFrameNumber+1) {
+ // Non-sequential frame access
+ if (!reader->jumpToImage(frameNumber)) {
+ if (frameNumber == 0) {
+ // Special case: Attempt to "rewind" so we can loop
+ // ### This could be implemented as QImageReader::rewind()
+ if (reader->device()->isSequential())
+ return QFrameInfo(); // Invalid
+ QString fileName = reader->fileName();
+ QByteArray format = reader->format();
+ QIODevice *device = reader->device();
+ QColor bgColor = reader->backgroundColor();
+ QSize scaledSize = reader->scaledSize();
+ delete reader;
+ if (fileName.isEmpty())
+ reader = new QImageReader(device, format);
+ else
+ reader = new QImageReader(absoluteFilePath, format);
+ reader->canRead(); // Provoke a device->open() call
+ reader->device()->seek(initialDevicePos);
+ reader->setBackgroundColor(bgColor);
+ reader->setScaledSize(scaledSize);
+ } else {
+ return QFrameInfo(); // Invalid
+ }
+ }
+ }
+ if (reader->canRead()) {
+ // reader says we can read. Attempt to actually read image
+ QImage anImage = reader->read();
+ if (anImage.isNull()) {
+ // Reading image failed.
+ return QFrameInfo(); // Invalid
+ }
+ if (frameNumber > greatestFrameNumber)
+ greatestFrameNumber = frameNumber;
+ QPixmap aPixmap = QPixmap::fromImage(anImage);
+ int aDelay = reader->nextImageDelay();
+ return QFrameInfo(aPixmap, aDelay);
+ } else {
+ // We've read all frames now. Return an end marker
+ haveReadAll = true;
+ return QFrameInfo::endMarker();
+ }
+ }
+
+ // CacheMode == CacheAll
+ if (frameNumber > greatestFrameNumber) {
+ // Frame hasn't been read from file yet. Try to do it
+ for (int i = greatestFrameNumber + 1; i <= frameNumber; ++i) {
+ if (reader->canRead()) {
+ // reader says we can read. Attempt to actually read image
+ QImage anImage = reader->read();
+ if (anImage.isNull()) {
+ // Reading image failed.
+ return QFrameInfo(); // Invalid
+ }
+ greatestFrameNumber = i;
+ QPixmap aPixmap = QPixmap::fromImage(anImage);
+ int aDelay = reader->nextImageDelay();
+ QFrameInfo info(aPixmap, aDelay);
+ // Cache it!
+ frameMap.insert(i, info);
+ if (i == frameNumber) {
+ return info;
+ }
+ } else {
+ // We've read all frames now. Return an end marker
+ haveReadAll = true;
+ return QFrameInfo::endMarker();
+ }
+ }
+ }
+ // Return info for requested (cached) frame
+ return frameMap.value(frameNumber);
+}
+
+/*!
+ \internal
+
+ Attempts to advance the animation to the next frame.
+ If successful, currentFrameNumber, currentPixmap and
+ nextDelay are updated accordingly, and true is returned.
+ Otherwise, false is returned.
+ When false is returned, isDone() can be called to
+ determine whether the animation ended gracefully or
+ an error occurred when reading the frame.
+*/
+bool QMoviePrivate::next()
+{
+ QTime time;
+ time.start();
+ QFrameInfo info = infoForFrame(nextFrameNumber);
+ if (!info.isValid())
+ return false;
+ if (info.isEndMarker()) {
+ // We reached the end of the animation.
+ if (isFirstIteration) {
+ if (nextFrameNumber == 0) {
+ // No frames could be read at all (error).
+ return false;
+ }
+ // End of first iteration. Initialize play counter
+ playCounter = reader->loopCount();
+ isFirstIteration = false;
+ }
+ // Loop as appropriate
+ if (playCounter != 0) {
+ if (playCounter != -1) // Infinite?
+ playCounter--; // Nope
+ nextFrameNumber = 0;
+ return next();
+ }
+ // Loop no more. Done
+ return false;
+ }
+ // Image and delay OK, update internal state
+ currentFrameNumber = nextFrameNumber++;
+ QSize scaledSize = reader->scaledSize();
+ if (scaledSize.isValid() && (scaledSize != info.pixmap.size()))
+ currentPixmap = QPixmap::fromImage( info.pixmap.toImage().scaled(scaledSize) );
+ else
+ currentPixmap = info.pixmap;
+ nextDelay = speedAdjustedDelay(info.delay);
+ // Adjust delay according to the time it took to read the frame
+ int processingTime = time.elapsed();
+ if (processingTime > nextDelay)
+ nextDelay = 0;
+ else
+ nextDelay = nextDelay - processingTime;
+ return true;
+}
+
+/*! \internal
+ */
+void QMoviePrivate::_q_loadNextFrame()
+{
+ _q_loadNextFrame(false);
+}
+
+void QMoviePrivate::_q_loadNextFrame(bool starting)
+{
+ Q_Q(QMovie);
+ if (next()) {
+ if (starting && movieState == QMovie::NotRunning) {
+ enterState(QMovie::Running);
+ emit q->started();
+ }
+
+ if (frameRect.size() != currentPixmap.rect().size()) {
+ frameRect = currentPixmap.rect();
+ emit q->resized(frameRect.size());
+ }
+
+ emit q->updated(frameRect);
+ emit q->frameChanged(currentFrameNumber);
+
+ if (movieState == QMovie::Running)
+ nextImageTimer.start(nextDelay);
+ } else {
+ // Could not read another frame
+ if (!isDone()) {
+ emit q->error(reader->error());
+ }
+
+ // Graceful finish
+ if (movieState != QMovie::Paused) {
+ nextFrameNumber = 0;
+ isFirstIteration = true;
+ playCounter = -1;
+ enterState(QMovie::NotRunning);
+ emit q->finished();
+ }
+ }
+}
+
+/*!
+ \internal
+*/
+bool QMoviePrivate::isValid() const
+{
+ return (greatestFrameNumber >= 0) // have we seen valid data
+ || reader->canRead(); // or does the reader see valid data
+}
+
+/*!
+ \internal
+*/
+bool QMoviePrivate::jumpToFrame(int frameNumber)
+{
+ if (frameNumber < 0)
+ return false;
+ if (currentFrameNumber == frameNumber)
+ return true;
+ nextFrameNumber = frameNumber;
+ if (movieState == QMovie::Running)
+ nextImageTimer.stop();
+ _q_loadNextFrame();
+ return (nextFrameNumber == currentFrameNumber+1);
+}
+
+/*!
+ \internal
+*/
+int QMoviePrivate::frameCount() const
+{
+ int result;
+ if ((result = reader->imageCount()) != 0)
+ return result;
+ if (haveReadAll)
+ return greatestFrameNumber+1;
+ return 0; // Don't know
+}
+
+/*!
+ \internal
+*/
+bool QMoviePrivate::jumpToNextFrame()
+{
+ return jumpToFrame(currentFrameNumber+1);
+}
+
+/*!
+ Constructs a QMovie object, passing the \a parent object to QObject's
+ constructor.
+
+ \sa setFileName(), setDevice(), setFormat()
+ */
+QMovie::QMovie(QObject *parent)
+ : QObject(*new QMoviePrivate(this), parent)
+{
+ Q_D(QMovie);
+ d->reader = new QImageReader;
+ connect(&d->nextImageTimer, SIGNAL(timeout()), this, SLOT(_q_loadNextFrame()));
+}
+
+/*!
+ Constructs a QMovie object. QMovie will use read image data from \a
+ device, which it assumes is open and readable. If \a format is not empty,
+ QMovie will use the image format \a format for decoding the image
+ data. Otherwise, QMovie will attempt to guess the format.
+
+ The \a parent object is passed to QObject's constructor.
+ */
+QMovie::QMovie(QIODevice *device, const QByteArray &format, QObject *parent)
+ : QObject(*new QMoviePrivate(this), parent)
+{
+ Q_D(QMovie);
+ d->reader = new QImageReader(device, format);
+ d->initialDevicePos = device->pos();
+ connect(&d->nextImageTimer, SIGNAL(timeout()), this, SLOT(_q_loadNextFrame()));
+}
+
+/*!
+ Constructs a QMovie object. QMovie will use read image data from \a
+ fileName. If \a format is not empty, QMovie will use the image format \a
+ format for decoding the image data. Otherwise, QMovie will attempt to
+ guess the format.
+
+ The \a parent object is passed to QObject's constructor.
+ */
+QMovie::QMovie(const QString &fileName, const QByteArray &format, QObject *parent)
+ : QObject(*new QMoviePrivate(this), parent)
+{
+ Q_D(QMovie);
+ d->absoluteFilePath = QDir(fileName).absolutePath();
+ d->reader = new QImageReader(fileName, format);
+ if (d->reader->device())
+ d->initialDevicePos = d->reader->device()->pos();
+ connect(&d->nextImageTimer, SIGNAL(timeout()), this, SLOT(_q_loadNextFrame()));
+}
+
+/*!
+ Destructs the QMovie object.
+*/
+QMovie::~QMovie()
+{
+ Q_D(QMovie);
+ delete d->reader;
+}
+
+/*!
+ Sets the current device to \a device. QMovie will read image data from
+ this device when the movie is running.
+
+ \sa device(), setFormat()
+*/
+void QMovie::setDevice(QIODevice *device)
+{
+ Q_D(QMovie);
+ d->reader->setDevice(device);
+ d->reset();
+}
+
+/*!
+ Returns the device QMovie reads image data from. If no device has
+ currently been assigned, 0 is returned.
+
+ \sa setDevice(), fileName()
+*/
+QIODevice *QMovie::device() const
+{
+ Q_D(const QMovie);
+ return d->reader->device();
+}
+
+/*!
+ Sets the name of the file that QMovie reads image data from, to \a
+ fileName.
+
+ \sa fileName(), setDevice(), setFormat()
+*/
+void QMovie::setFileName(const QString &fileName)
+{
+ Q_D(QMovie);
+ d->absoluteFilePath = QDir(fileName).absolutePath();
+ d->reader->setFileName(fileName);
+ d->reset();
+}
+
+/*!
+ Returns the name of the file that QMovie reads image data from. If no file
+ name has been assigned, or if the assigned device is not a file, an empty
+ QString is returned.
+
+ \sa setFileName(), device()
+*/
+QString QMovie::fileName() const
+{
+ Q_D(const QMovie);
+ return d->reader->fileName();
+}
+
+/*!
+ Sets the format that QMovie will use when decoding image data, to \a
+ format. By default, QMovie will attempt to guess the format of the image
+ data.
+
+ You can call supportedFormats() for the full list of formats
+ QMovie supports.
+
+ \sa QImageReader::supportedImageFormats()
+*/
+void QMovie::setFormat(const QByteArray &format)
+{
+ Q_D(QMovie);
+ d->reader->setFormat(format);
+}
+
+/*!
+ Returns the format that QMovie uses when decoding image data. If no format
+ has been assigned, an empty QByteArray() is returned.
+
+ \sa setFormat()
+*/
+QByteArray QMovie::format() const
+{
+ Q_D(const QMovie);
+ return d->reader->format();
+}
+
+/*!
+ For image formats that support it, this function sets the background color
+ to \a color.
+
+ \sa backgroundColor()
+*/
+void QMovie::setBackgroundColor(const QColor &color)
+{
+ Q_D(QMovie);
+ d->reader->setBackgroundColor(color);
+}
+
+/*!
+ Returns the background color of the movie. If no background color has been
+ assigned, an invalid QColor is returned.
+
+ \sa setBackgroundColor()
+*/
+QColor QMovie::backgroundColor() const
+{
+ Q_D(const QMovie);
+ return d->reader->backgroundColor();
+}
+
+/*!
+ Returns the current state of QMovie.
+
+ \sa MovieState, stateChanged()
+*/
+QMovie::MovieState QMovie::state() const
+{
+ Q_D(const QMovie);
+ return d->movieState;
+}
+
+/*!
+ Returns the rect of the last frame. If no frame has yet been updated, an
+ invalid QRect is returned.
+
+ \sa currentImage(), currentPixmap()
+*/
+QRect QMovie::frameRect() const
+{
+ Q_D(const QMovie);
+ return d->frameRect;
+}
+
+/*! \fn QImage QMovie::framePixmap() const
+
+ Use currentPixmap() instead.
+*/
+
+/*! \fn void QMovie::pause()
+
+ Use setPaused(true) instead.
+*/
+
+/*! \fn void QMovie::unpause()
+
+ Use setPaused(false) instead.
+*/
+
+/*!
+ Returns the current frame as a QPixmap.
+
+ \sa currentImage(), updated()
+*/
+QPixmap QMovie::currentPixmap() const
+{
+ Q_D(const QMovie);
+ return d->currentPixmap;
+}
+
+/*! \fn QImage QMovie::frameImage() const
+
+ Use currentImage() instead.
+*/
+
+/*!
+ Returns the current frame as a QImage.
+
+ \sa currentPixmap(), updated()
+*/
+QImage QMovie::currentImage() const
+{
+ Q_D(const QMovie);
+ return d->currentPixmap.toImage();
+}
+
+/*!
+ Returns true if the movie is valid (e.g., the image data is readable and
+ the image format is supported); otherwise returns false.
+*/
+bool QMovie::isValid() const
+{
+ Q_D(const QMovie);
+ return d->isValid();
+}
+
+/*! \fn bool QMovie::running() const
+
+ Use state() instead.
+*/
+
+/*! \fn bool QMovie::isNull() const
+
+ Use isValid() instead.
+*/
+
+/*! \fn int QMovie::frameNumber() const
+
+ Use currentFrameNumber() instead.
+*/
+
+/*! \fn bool QMovie::paused() const
+
+ Use state() instead.
+*/
+
+/*! \fn bool QMovie::finished() const
+
+ Use state() instead.
+*/
+
+/*! \fn void QMovie::restart()
+
+ Use stop() and start() instead.
+*/
+
+/*!
+ \fn void QMovie::step()
+
+ Use jumpToNextFrame() instead.
+*/
+
+/*!
+ Returns the number of frames in the movie.
+
+ Certain animation formats do not support this feature, in which
+ case 0 is returned.
+*/
+int QMovie::frameCount() const
+{
+ Q_D(const QMovie);
+ return d->frameCount();
+}
+
+/*!
+ Returns the number of milliseconds QMovie will wait before updating the
+ next frame in the animation.
+*/
+int QMovie::nextFrameDelay() const
+{
+ Q_D(const QMovie);
+ return d->nextDelay;
+}
+
+/*!
+ Returns the sequence number of the current frame. The number of the first
+ frame in the movie is 0.
+*/
+int QMovie::currentFrameNumber() const
+{
+ Q_D(const QMovie);
+ return d->currentFrameNumber;
+}
+
+/*!
+ Jumps to the next frame. Returns true on success; otherwise returns false.
+*/
+bool QMovie::jumpToNextFrame()
+{
+ Q_D(QMovie);
+ return d->jumpToNextFrame();
+}
+
+/*!
+ Jumps to frame number \a frameNumber. Returns true on success; otherwise
+ returns false.
+*/
+bool QMovie::jumpToFrame(int frameNumber)
+{
+ Q_D(QMovie);
+ return d->jumpToFrame(frameNumber);
+}
+
+/*!
+ Returns the number of times the movie will loop before it finishes.
+ If the movie will only play once (no looping), loopCount returns 0.
+ If the movie loops forever, loopCount returns -1.
+
+ Note that, if the image data comes from a sequential device (e.g. a
+ socket), QMovie can only loop the movie if the cacheMode is set to
+ QMovie::CacheAll.
+*/
+int QMovie::loopCount() const
+{
+ Q_D(const QMovie);
+ return d->reader->loopCount();
+}
+
+/*!
+ If \a paused is true, QMovie will enter \l Paused state and emit
+ stateChanged(Paused); otherwise it will enter \l Running state and emit
+ stateChanged(Running).
+
+ \sa state()
+*/
+void QMovie::setPaused(bool paused)
+{
+ Q_D(QMovie);
+ if (paused) {
+ if (d->movieState == NotRunning)
+ return;
+ d->enterState(Paused);
+ d->nextImageTimer.stop();
+ } else {
+ if (d->movieState == Running)
+ return;
+ d->enterState(Running);
+ d->nextImageTimer.start(nextFrameDelay());
+ }
+}
+
+/*!
+ \property QMovie::speed
+ \brief the movie's speed
+
+ The speed is measured in percentage of the original movie speed.
+ The default speed is 100%.
+ Example:
+
+ \snippet doc/src/snippets/code/src_gui_image_qmovie.cpp 1
+*/
+void QMovie::setSpeed(int percentSpeed)
+{
+ Q_D(QMovie);
+ d->speed = percentSpeed;
+}
+
+int QMovie::speed() const
+{
+ Q_D(const QMovie);
+ return d->speed;
+}
+
+/*!
+ Starts the movie. QMovie will enter \l Running state, and start emitting
+ updated() and resized() as the movie progresses.
+
+ If QMovie is in the \l Paused state, this function is equivalent
+ to calling setPaused(false). If QMovie is already in the \l
+ Running state, this function does nothing.
+
+ \sa stop(), setPaused()
+*/
+void QMovie::start()
+{
+ Q_D(QMovie);
+ if (d->movieState == NotRunning) {
+ d->_q_loadNextFrame(true);
+ } else if (d->movieState == Paused) {
+ setPaused(false);
+ }
+}
+
+/*!
+ Stops the movie. QMovie enters \l NotRunning state, and stops emitting
+ updated() and resized(). If start() is called again, the movie will
+ restart from the beginning.
+
+ If QMovie is already in the \l NotRunning state, this function
+ does nothing.
+
+ \sa start(), setPaused()
+*/
+void QMovie::stop()
+{
+ Q_D(QMovie);
+ if (d->movieState == NotRunning)
+ return;
+ d->enterState(NotRunning);
+ d->nextImageTimer.stop();
+ d->nextFrameNumber = 0;
+}
+
+/*!
+ \since 4.1
+
+ Returns the scaled size of frames.
+
+ \sa QImageReader::scaledSize()
+*/
+QSize QMovie::scaledSize()
+{
+ Q_D(QMovie);
+ return d->reader->scaledSize();
+}
+
+/*!
+ \since 4.1
+
+ Sets the scaled frame size to \a size.
+
+ \sa QImageReader::setScaledSize()
+*/
+void QMovie::setScaledSize(const QSize &size)
+{
+ Q_D(QMovie);
+ d->reader->setScaledSize(size);
+}
+
+/*!
+ \since 4.1
+
+ Returns the list of image formats supported by QMovie.
+
+ \sa QImageReader::supportedImageFormats()
+*/
+QList<QByteArray> QMovie::supportedFormats()
+{
+ QList<QByteArray> list = QImageReader::supportedImageFormats();
+ QMutableListIterator<QByteArray> it(list);
+ QBuffer buffer;
+ buffer.open(QIODevice::ReadOnly);
+ while (it.hasNext()) {
+ QImageReader reader(&buffer, it.next());
+ if (!reader.supportsAnimation())
+ it.remove();
+ }
+ return list;
+}
+
+/*!
+ \property QMovie::cacheMode
+ \brief the movie's cache mode
+
+ Caching frames can be useful when the underlying animation format handler
+ that QMovie relies on to decode the animation data does not support
+ jumping to particular frames in the animation, or even "rewinding" the
+ animation to the beginning (for looping). Furthermore, if the image data
+ comes from a sequential device, it is not possible for the underlying
+ animation handler to seek back to frames whose data has already been read
+ (making looping altogether impossible).
+
+ To aid in such situations, a QMovie object can be instructed to cache the
+ frames, at the added memory cost of keeping the frames in memory for the
+ lifetime of the object.
+
+ By default, this property is set to \l CacheNone.
+
+ \sa QMovie::CacheMode
+*/
+
+QMovie::CacheMode QMovie::cacheMode() const
+{
+ Q_D(const QMovie);
+ return d->cacheMode;
+}
+
+void QMovie::setCacheMode(CacheMode cacheMode)
+{
+ Q_D(QMovie);
+ d->cacheMode = cacheMode;
+}
+
+/*!
+ \internal
+*/
+QMovie::CacheMode QMovie::cacheMode()
+{
+ Q_D(QMovie);
+ return d->cacheMode;
+}
+
+QT_END_NAMESPACE
+
+#include "moc_qmovie.cpp"
+
+#endif // QT_NO_MOVIE
diff --git a/src/gui/image/qmovie.h b/src/gui/image/qmovie.h
new file mode 100644
index 0000000000..c2c3abb404
--- /dev/null
+++ b/src/gui/image/qmovie.h
@@ -0,0 +1,177 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QMOVIE_H
+#define QMOVIE_H
+
+#include <QtCore/qobject.h>
+
+#ifndef QT_NO_MOVIE
+
+#include <QtCore/qbytearray.h>
+#include <QtCore/qlist.h>
+#include <QtCore/qobject.h>
+#include <QtGui/qimagereader.h>
+
+#ifdef QT3_SUPPORT
+#include <QtGui/qimage.h>
+#include <QtGui/qpixmap.h>
+#endif
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+class QByteArray;
+class QColor;
+class QIODevice;
+class QImage;
+class QPixmap;
+class QRect;
+class QSize;
+
+class QMoviePrivate;
+class Q_GUI_EXPORT QMovie : public QObject
+{
+ Q_OBJECT
+ Q_DECLARE_PRIVATE(QMovie)
+ Q_ENUMS(MovieState CacheMode)
+ Q_PROPERTY(int speed READ speed WRITE setSpeed)
+ Q_PROPERTY(CacheMode cacheMode READ cacheMode WRITE setCacheMode)
+public:
+ enum MovieState {
+ NotRunning,
+ Paused,
+ Running
+ };
+ enum CacheMode {
+ CacheNone,
+ CacheAll
+ };
+
+ QMovie(QObject *parent = 0);
+ explicit QMovie(QIODevice *device, const QByteArray &format = QByteArray(), QObject *parent = 0);
+ explicit QMovie(const QString &fileName, const QByteArray &format = QByteArray(), QObject *parent = 0);
+ ~QMovie();
+
+ static QList<QByteArray> supportedFormats();
+
+ void setDevice(QIODevice *device);
+ QIODevice *device() const;
+
+ void setFileName(const QString &fileName);
+ QString fileName() const;
+
+ void setFormat(const QByteArray &format);
+ QByteArray format() const;
+
+ void setBackgroundColor(const QColor &color);
+ QColor backgroundColor() const;
+
+ MovieState state() const;
+
+ QRect frameRect() const;
+ QImage currentImage() const;
+ QPixmap currentPixmap() const;
+
+ bool isValid() const;
+
+ bool jumpToFrame(int frameNumber);
+ int loopCount() const;
+ int frameCount() const;
+ int nextFrameDelay() const;
+ int currentFrameNumber() const;
+
+ int speed() const;
+
+ QSize scaledSize();
+ void setScaledSize(const QSize &size);
+
+ CacheMode cacheMode() const;
+ void setCacheMode(CacheMode mode);
+
+ CacheMode cacheMode(); // ### Qt 5: remove me
+
+Q_SIGNALS:
+ void started();
+ void resized(const QSize &size);
+ void updated(const QRect &rect);
+ void stateChanged(QMovie::MovieState state);
+ void error(QImageReader::ImageReaderError error);
+ void finished();
+ void frameChanged(int frameNumber);
+
+public Q_SLOTS:
+ void start();
+ bool jumpToNextFrame();
+ void setPaused(bool paused);
+ void stop();
+ void setSpeed(int percentSpeed);
+
+private:
+ Q_DISABLE_COPY(QMovie)
+ Q_PRIVATE_SLOT(d_func(), void _q_loadNextFrame())
+
+#ifdef QT3_SUPPORT
+public:
+ inline QT3_SUPPORT bool isNull() const { return isValid(); }
+ inline QT3_SUPPORT int frameNumber() const { return currentFrameNumber(); }
+ inline QT3_SUPPORT bool running() const { return state() == Running; }
+ inline QT3_SUPPORT bool paused() const { return state() == Paused; }
+ inline QT3_SUPPORT bool finished() const { return state() == NotRunning; }
+ inline QT3_SUPPORT void restart() { stop(); start(); }
+ inline QT3_SUPPORT QImage frameImage() const { return currentImage(); }
+ inline QT3_SUPPORT QPixmap framePixmap() const { return currentPixmap(); }
+ inline QT3_SUPPORT void step() { jumpToNextFrame(); }
+ inline QT3_SUPPORT void pause() { setPaused(true); }
+ inline QT3_SUPPORT void unpause() { setPaused(false); }
+#endif
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QT_NO_MOVIE
+
+#endif // QMOVIE_H
diff --git a/src/gui/image/qnativeimage.cpp b/src/gui/image/qnativeimage.cpp
new file mode 100644
index 0000000000..6b74323146
--- /dev/null
+++ b/src/gui/image/qnativeimage.cpp
@@ -0,0 +1,279 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <qdebug.h>
+#include "qnativeimage_p.h"
+#include "qcolormap.h"
+
+#include "private/qpaintengine_raster_p.h"
+
+#if defined(Q_WS_X11) && !defined(QT_NO_MITSHM)
+#include <qx11info_x11.h>
+#include <sys/ipc.h>
+#include <sys/shm.h>
+#include <qwidget.h>
+#endif
+
+QT_BEGIN_NAMESPACE
+
+#ifdef Q_WS_WIN
+typedef struct {
+ BITMAPINFOHEADER bmiHeader;
+ DWORD redMask;
+ DWORD greenMask;
+ DWORD blueMask;
+} BITMAPINFO_MASK;
+
+
+QNativeImage::QNativeImage(int width, int height, QImage::Format format, bool isTextBuffer, QWidget *)
+{
+#ifndef Q_OS_WINCE
+ Q_UNUSED(isTextBuffer);
+#endif
+ BITMAPINFO_MASK bmi;
+ memset(&bmi, 0, sizeof(bmi));
+ bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
+ bmi.bmiHeader.biWidth = width;
+ bmi.bmiHeader.biHeight = -height;
+ bmi.bmiHeader.biPlanes = 1;
+ bmi.bmiHeader.biSizeImage = 0;
+
+ if (format == QImage::Format_RGB16) {
+ bmi.bmiHeader.biBitCount = 16;
+#ifdef Q_OS_WINCE
+ if (isTextBuffer) {
+ bmi.bmiHeader.biCompression = BI_RGB;
+ bmi.redMask = 0;
+ bmi.greenMask = 0;
+ bmi.blueMask = 0;
+ } else
+#endif
+ {
+ bmi.bmiHeader.biCompression = BI_BITFIELDS;
+ bmi.redMask = 0xF800;
+ bmi.greenMask = 0x07E0;
+ bmi.blueMask = 0x001F;
+ }
+ } else {
+ bmi.bmiHeader.biBitCount = 32;
+ bmi.bmiHeader.biCompression = BI_RGB;
+ bmi.redMask = 0;
+ bmi.greenMask = 0;
+ bmi.blueMask = 0;
+ }
+
+ hdc = CreateCompatibleDC(qt_win_display_dc());
+ Q_ASSERT(hdc);
+
+ uchar *bits = 0;
+ bitmap = CreateDIBSection(hdc, reinterpret_cast<BITMAPINFO *>(&bmi), DIB_RGB_COLORS, (void**) &bits, 0, 0);
+ Q_ASSERT(bitmap);
+ Q_ASSERT(bits);
+
+ null_bitmap = (HBITMAP)SelectObject(hdc, bitmap);
+ image = QImage(bits, width, height, format);
+
+ Q_ASSERT(image.paintEngine()->type() == QPaintEngine::Raster);
+ static_cast<QRasterPaintEngine *>(image.paintEngine())->setDC(hdc);
+
+#ifndef Q_OS_WINCE
+ GdiFlush();
+#endif
+}
+
+QNativeImage::~QNativeImage()
+{
+ if (bitmap || hdc) {
+ Q_ASSERT(hdc);
+ Q_ASSERT(bitmap);
+ if (null_bitmap)
+ SelectObject(hdc, null_bitmap);
+ DeleteDC(hdc);
+ DeleteObject(bitmap);
+ }
+}
+
+QImage::Format QNativeImage::systemFormat()
+{
+ if (QColormap::instance().depth() == 16)
+ return QImage::Format_RGB16;
+ return QImage::Format_RGB32;
+}
+
+
+#elif defined(Q_WS_X11) && !defined(QT_NO_MITSHM)
+
+QNativeImage::QNativeImage(int width, int height, QImage::Format format,bool /* isTextBuffer */, QWidget *widget)
+{
+ if (!X11->use_mitshm) {
+ xshmimg = 0;
+ xshmpm = 0;
+ image = QImage(width, height, format);
+ return;
+ }
+
+ QX11Info info = widget->x11Info();
+
+ int dd = info.depth();
+ Visual *vis = (Visual*) info.visual();
+
+ xshmimg = XShmCreateImage(X11->display, vis, dd, ZPixmap, 0, &xshminfo, width, height);
+ if (!xshmimg) {
+ qWarning("QNativeImage: Unable to create shared XImage.");
+ return;
+ }
+
+ bool ok;
+ xshminfo.shmid = shmget(IPC_PRIVATE, xshmimg->bytes_per_line * xshmimg->height,
+ IPC_CREAT | 0777);
+ ok = xshminfo.shmid != -1;
+ if (ok) {
+ xshmimg->data = (char*)shmat(xshminfo.shmid, 0, 0);
+ xshminfo.shmaddr = xshmimg->data;
+ ok = (xshminfo.shmaddr != (char*)-1);
+ if (ok)
+ image = QImage((uchar *)xshmimg->data, width, height, systemFormat());
+ }
+ xshminfo.readOnly = false;
+ if (ok)
+ ok = XShmAttach(X11->display, &xshminfo);
+ if (!ok) {
+ qWarning() << "QNativeImage: Unable to attach to shared memory segment.";
+ if (xshmimg->data) {
+ free(xshmimg->data);
+ xshmimg->data = 0;
+ }
+ XDestroyImage(xshmimg);
+ xshmimg = 0;
+ if (xshminfo.shmaddr)
+ shmdt(xshminfo.shmaddr);
+ if (xshminfo.shmid != -1)
+ shmctl(xshminfo.shmid, IPC_RMID, 0);
+ return;
+ }
+ xshmpm = XShmCreatePixmap(X11->display, DefaultRootWindow(X11->display), xshmimg->data,
+ &xshminfo, width, height, dd);
+ if (!xshmpm) {
+ qWarning() << "QNativeImage: Unable to create shared Pixmap.";
+ }
+}
+
+
+QNativeImage::~QNativeImage()
+{
+ if (!xshmimg)
+ return;
+
+ if (xshmpm) {
+ XFreePixmap(X11->display, xshmpm);
+ xshmpm = 0;
+ }
+ XShmDetach(X11->display, &xshminfo);
+ xshmimg->data = 0;
+ XDestroyImage(xshmimg);
+ xshmimg = 0;
+ shmdt(xshminfo.shmaddr);
+ shmctl(xshminfo.shmid, IPC_RMID, 0);
+}
+
+QImage::Format QNativeImage::systemFormat()
+{
+ if (QX11Info::appDepth() == 16)
+ return QImage::Format_RGB16;
+ return QImage::Format_RGB32;
+}
+
+#elif defined(Q_WS_MAC)
+
+QNativeImage::QNativeImage(int width, int height, QImage::Format format, bool /* isTextBuffer */, QWidget *)
+ : image(width, height, format)
+{
+ cgColorSpace = CGColorSpaceCreateDeviceRGB();
+ uint cgflags = kCGImageAlphaNoneSkipFirst;
+
+#ifdef kCGBitmapByteOrder32Host //only needed because CGImage.h added symbols in the minor version
+ if(QSysInfo::MacintoshVersion >= QSysInfo::MV_10_4)
+ cgflags |= kCGBitmapByteOrder32Host;
+#endif
+
+ cg = CGBitmapContextCreate(image.bits(), width, height, 8, image.bytesPerLine(), cgColorSpace, cgflags);
+ CGContextTranslateCTM(cg, 0, height);
+ CGContextScaleCTM(cg, 1, -1);
+
+ Q_ASSERT(image.paintEngine()->type() == QPaintEngine::Raster);
+ static_cast<QRasterPaintEngine *>(image.paintEngine())->setCGContext(cg);
+}
+
+
+QNativeImage::~QNativeImage()
+{
+ CGContextRelease(cg);
+ CGColorSpaceRelease(cgColorSpace);
+}
+
+QImage::Format QNativeImage::systemFormat()
+{
+ return QImage::Format_RGB32;
+}
+
+
+#else // other platforms...
+
+QNativeImage::QNativeImage(int width, int height, QImage::Format format, bool /* isTextBuffer */, QWidget *)
+ : image(width, height, format)
+{
+
+}
+
+
+QNativeImage::~QNativeImage()
+{
+}
+
+QImage::Format QNativeImage::systemFormat()
+{
+ return QImage::Format_RGB32;
+}
+
+#endif // platforms
+
+QT_END_NAMESPACE
+
diff --git a/src/gui/image/qnativeimage_p.h b/src/gui/image/qnativeimage_p.h
new file mode 100644
index 0000000000..860485a827
--- /dev/null
+++ b/src/gui/image/qnativeimage_p.h
@@ -0,0 +1,110 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QNATIVEIMAGE_P_H
+#define QNATIVEIMAGE_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 "qimage.h"
+
+#ifdef Q_WS_WIN
+#include "qt_windows.h"
+
+#elif defined(Q_WS_X11)
+#include <private/qt_x11_p.h>
+
+#elif defined(Q_WS_MAC)
+#include <private/qt_mac_p.h>
+
+#endif
+
+QT_BEGIN_NAMESPACE
+
+class QWidget;
+
+class Q_GUI_EXPORT QNativeImage
+{
+public:
+ QNativeImage(int width, int height, QImage::Format format, bool isTextBuffer = false, QWidget *widget = 0);
+ ~QNativeImage();
+
+ inline int width() const;
+ inline int height() const;
+
+ QImage image;
+
+ static QImage::Format systemFormat();
+
+#ifdef Q_WS_WIN
+ HDC hdc;
+ HBITMAP bitmap;
+ HBITMAP null_bitmap;
+
+#elif defined(Q_WS_X11) && !defined(QT_NO_MITSHM)
+ XImage *xshmimg;
+ Pixmap xshmpm;
+ XShmSegmentInfo xshminfo;
+
+#elif defined(Q_WS_MAC)
+ CGContextRef cg;
+ CGColorSpaceRef cgColorSpace;
+#endif
+
+private:
+ Q_DISABLE_COPY(QNativeImage)
+};
+
+inline int QNativeImage::width() const { return image.width(); }
+inline int QNativeImage::height() const { return image.height(); }
+
+QT_END_NAMESPACE
+
+#endif // QNATIVEIMAGE_P_H
diff --git a/src/gui/image/qpaintengine_pic.cpp b/src/gui/image/qpaintengine_pic.cpp
new file mode 100644
index 0000000000..cba98276ce
--- /dev/null
+++ b/src/gui/image/qpaintengine_pic.cpp
@@ -0,0 +1,519 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "private/qpaintengine_p.h"
+#include "private/qpainter_p.h"
+#include "private/qpicture_p.h"
+#include "private/qfont_p.h"
+
+#ifndef QT_NO_PICTURE
+
+#include "qbuffer.h"
+#include "qbytearray.h"
+#include "qdatastream.h"
+#include "qmath.h"
+#include "qpaintengine_pic_p.h"
+#include "qpicture.h"
+#include "qpolygon.h"
+#include "qrect.h"
+#include <private/qtextengine_p.h>
+
+//#define QT_PICTURE_DEBUG
+#include <qdebug.h>
+
+
+QT_BEGIN_NAMESPACE
+
+class QPicturePaintEnginePrivate : public QPaintEnginePrivate
+{
+ Q_DECLARE_PUBLIC(QPicturePaintEngine)
+public:
+ QDataStream s;
+ QPainter *pt;
+ QPicturePrivate *pic_d;
+};
+
+QPicturePaintEngine::QPicturePaintEngine()
+ : QPaintEngine(*(new QPicturePaintEnginePrivate), AllFeatures)
+{
+ Q_D(QPicturePaintEngine);
+ d->pt = 0;
+}
+
+QPicturePaintEngine::QPicturePaintEngine(QPaintEnginePrivate &dptr)
+ : QPaintEngine(dptr, AllFeatures)
+{
+ Q_D(QPicturePaintEngine);
+ d->pt = 0;
+}
+
+QPicturePaintEngine::~QPicturePaintEngine()
+{
+}
+
+bool QPicturePaintEngine::begin(QPaintDevice *pd)
+{
+ Q_D(QPicturePaintEngine);
+#ifdef QT_PICTURE_DEBUG
+ qDebug() << "QPicturePaintEngine::begin()";
+#endif
+ Q_ASSERT(pd);
+ QPicture *pic = static_cast<QPicture *>(pd);
+
+ d->pdev = pd;
+ d->pic_d = pic->d_func();
+ Q_ASSERT(d->pic_d);
+
+ d->s.setDevice(&d->pic_d->pictb);
+ d->s.setVersion(d->pic_d->formatMajor);
+
+ d->pic_d->pictb.open(QIODevice::WriteOnly | QIODevice::Truncate);
+ d->s.writeRawData(qt_mfhdr_tag, 4);
+ d->s << (quint16) 0 << (quint16) d->pic_d->formatMajor << (quint16) d->pic_d->formatMinor;
+ d->s << (quint8) QPicturePrivate::PdcBegin << (quint8) sizeof(qint32);
+ d->pic_d->brect = QRect();
+ if (d->pic_d->formatMajor >= 4) {
+ QRect r = pic->boundingRect();
+ d->s << (qint32) r.left() << (qint32) r.top() << (qint32) r.width()
+ << (qint32) r.height();
+ }
+ d->pic_d->trecs = 0;
+ d->s << (quint32)d->pic_d->trecs; // total number of records
+ d->pic_d->formatOk = false;
+ setActive(true);
+ return true;
+}
+
+bool QPicturePaintEngine::end()
+{
+ Q_D(QPicturePaintEngine);
+#ifdef QT_PICTURE_DEBUG
+ qDebug() << "QPicturePaintEngine::end()";
+#endif
+ d->pic_d->trecs++;
+ d->s << (quint8) QPicturePrivate::PdcEnd << (quint8) 0;
+ int cs_start = sizeof(quint32); // pos of checksum word
+ int data_start = cs_start + sizeof(quint16);
+ int brect_start = data_start + 2*sizeof(qint16) + 2*sizeof(quint8);
+ int pos = d->pic_d->pictb.pos();
+ d->pic_d->pictb.seek(brect_start);
+ if (d->pic_d->formatMajor >= 4) { // bounding rectangle
+ QRect r = static_cast<QPicture *>(d->pdev)->boundingRect();
+ d->s << (qint32) r.left() << (qint32) r.top() << (qint32) r.width()
+ << (qint32) r.height();
+ }
+ d->s << (quint32) d->pic_d->trecs; // write number of records
+ d->pic_d->pictb.seek(cs_start);
+ QByteArray buf = d->pic_d->pictb.buffer();
+ quint16 cs = (quint16) qChecksum(buf.constData() + data_start, pos - data_start);
+ d->s << cs; // write checksum
+ d->pic_d->pictb.close();
+ setActive(false);
+ return true;
+}
+
+#define SERIALIZE_CMD(c) \
+ d->pic_d->trecs++; \
+ d->s << (quint8) c; \
+ d->s << (quint8) 0; \
+ pos = d->pic_d->pictb.pos()
+
+void QPicturePaintEngine::updatePen(const QPen &pen)
+{
+ Q_D(QPicturePaintEngine);
+#ifdef QT_PICTURE_DEBUG
+ qDebug() << " -> updatePen(): width:" << pen.width() << "style:"
+ << pen.style() << "color:" << pen.color();
+#endif
+ int pos;
+ SERIALIZE_CMD(QPicturePrivate::PdcSetPen);
+ if (d->pic_d->in_memory_only) {
+ int index = d->pic_d->pen_list.size();
+ d->pic_d->pen_list.append(pen);
+ d->s << index;
+ } else {
+ d->s << pen;
+ }
+ writeCmdLength(pos, QRect(), false);
+}
+
+void QPicturePaintEngine::updateCompositionMode(QPainter::CompositionMode cmode)
+{
+ Q_D(QPicturePaintEngine);
+#ifdef QT_PICTURE_DEBUG
+ qDebug() << " -> updateCompositionMode():" << cmode;
+#endif
+ int pos;
+ SERIALIZE_CMD(QPicturePrivate::PdcSetCompositionMode);
+ d->s << (qint32)cmode;
+ writeCmdLength(pos, QRectF(), false);
+}
+
+void QPicturePaintEngine::updateClipEnabled(bool enabled)
+{
+ Q_D(QPicturePaintEngine);
+#ifdef QT_PICTURE_DEBUG
+ qDebug() << " -> updateClipEnabled():" << enabled;
+#endif
+ int pos;
+ SERIALIZE_CMD(QPicturePrivate::PdcSetClipEnabled);
+ d->s << enabled;
+ writeCmdLength(pos, QRectF(), false);
+}
+
+void QPicturePaintEngine::updateOpacity(qreal opacity)
+{
+ Q_D(QPicturePaintEngine);
+#ifdef QT_PICTURE_DEBUG
+ qDebug() << " -> updateOpacity():" << opacity;
+#endif
+ int pos;
+ SERIALIZE_CMD(QPicturePrivate::PdcSetOpacity);
+ d->s << double(opacity);
+ writeCmdLength(pos, QRectF(), false);
+}
+
+void QPicturePaintEngine::updateBrush(const QBrush &brush)
+{
+ Q_D(QPicturePaintEngine);
+#ifdef QT_PICTURE_DEBUG
+ qDebug() << " -> updateBrush(): style:" << brush.style();
+#endif
+ int pos;
+ SERIALIZE_CMD(QPicturePrivate::PdcSetBrush);
+ if (d->pic_d->in_memory_only) {
+ int index = d->pic_d->brush_list.size();
+ d->pic_d->brush_list.append(brush);
+ d->s << index;
+ } else {
+ d->s << brush;
+ }
+ writeCmdLength(pos, QRect(), false);
+}
+
+void QPicturePaintEngine::updateBrushOrigin(const QPointF &p)
+{
+ Q_D(QPicturePaintEngine);
+#ifdef QT_PICTURE_DEBUG
+ qDebug() << " -> updateBrushOrigin(): " << p;
+#endif
+ int pos;
+ SERIALIZE_CMD(QPicturePrivate::PdcSetBrushOrigin);
+ d->s << p;
+ writeCmdLength(pos, QRect(), false);
+}
+
+void QPicturePaintEngine::updateFont(const QFont &font)
+{
+ Q_D(QPicturePaintEngine);
+#ifdef QT_PICTURE_DEBUG
+ qDebug() << " -> updateFont(): pt sz:" << font.pointSize();
+#endif
+ int pos;
+ SERIALIZE_CMD(QPicturePrivate::PdcSetFont);
+ QFont fnt = font;
+ d->s << fnt;
+ writeCmdLength(pos, QRectF(), false);
+}
+
+void QPicturePaintEngine::updateBackground(Qt::BGMode bgMode, const QBrush &bgBrush)
+{
+ Q_D(QPicturePaintEngine);
+#ifdef QT_PICTURE_DEBUG
+ qDebug() << " -> updateBackground(): mode:" << bgMode << "style:" << bgBrush.style();
+#endif
+ int pos;
+ SERIALIZE_CMD(QPicturePrivate::PdcSetBkColor);
+ d->s << bgBrush.color();
+ writeCmdLength(pos, QRect(), false);
+
+ SERIALIZE_CMD(QPicturePrivate::PdcSetBkMode);
+ d->s << (qint8) bgMode;
+ writeCmdLength(pos, QRectF(), false);
+}
+
+void QPicturePaintEngine::updateMatrix(const QTransform &matrix)
+{
+ Q_D(QPicturePaintEngine);
+#ifdef QT_PICTURE_DEBUG
+ qDebug() << " -> updateMatrix():" << matrix;
+#endif
+ int pos;
+ SERIALIZE_CMD(QPicturePrivate::PdcSetWMatrix);
+ d->s << matrix << (qint8) false;
+ writeCmdLength(pos, QRectF(), false);
+}
+
+void QPicturePaintEngine::updateClipRegion(const QRegion &region, Qt::ClipOperation op)
+{
+ Q_D(QPicturePaintEngine);
+#ifdef QT_PICTURE_DEBUG
+ qDebug() << " -> updateClipRegion(): op:" << op
+ << "bounding rect:" << region.boundingRect();
+#endif
+ int pos;
+ SERIALIZE_CMD(QPicturePrivate::PdcSetClipRegion);
+ d->s << region << qint8(op);
+ writeCmdLength(pos, QRectF(), false);
+}
+
+void QPicturePaintEngine::updateClipPath(const QPainterPath &path, Qt::ClipOperation op)
+{
+ Q_D(QPicturePaintEngine);
+#ifdef QT_PICTURE_DEBUG
+ qDebug() << " -> updateClipPath(): op:" << op
+ << "bounding rect:" << path.boundingRect();
+#endif
+ int pos;
+
+ SERIALIZE_CMD(QPicturePrivate::PdcSetClipPath);
+ d->s << path << qint8(op);
+ writeCmdLength(pos, QRectF(), false);
+}
+
+void QPicturePaintEngine::updateRenderHints(QPainter::RenderHints hints)
+{
+ Q_D(QPicturePaintEngine);
+#ifdef QT_PICTURE_DEBUG
+ qDebug() << " -> updateRenderHints(): " << hints;
+#endif
+ int pos;
+ SERIALIZE_CMD(QPicturePrivate::PdcSetRenderHint);
+ d->s << (quint32) hints;
+ writeCmdLength(pos, QRect(), false);
+}
+
+void QPicturePaintEngine::writeCmdLength(int pos, const QRectF &r, bool corr)
+{
+ Q_D(QPicturePaintEngine);
+ int newpos = d->pic_d->pictb.pos(); // new position
+ int length = newpos - pos;
+ QRectF br(r);
+
+ if (length < 255) { // write 8-bit length
+ d->pic_d->pictb.seek(pos - 1); // position to right index
+ d->s << (quint8)length;
+ } else { // write 32-bit length
+ d->s << (quint32)0; // extend the buffer
+ d->pic_d->pictb.seek(pos - 1); // position to right index
+ d->s << (quint8)255; // indicate 32-bit length
+ char *p = d->pic_d->pictb.buffer().data();
+ memmove(p+pos+4, p+pos, length); // make room for 4 byte
+ d->s << (quint32)length;
+ newpos += 4;
+ }
+ d->pic_d->pictb.seek(newpos); // set to new position
+
+ if (br.width() > 0.0 || br.height() > 0.0) {
+ if (corr) { // widen bounding rect
+ int w2 = painter()->pen().width() / 2;
+ br.setCoords(br.left() - w2, br.top() - w2,
+ br.right() + w2, br.bottom() + w2);
+ }
+ br = painter()->transform().mapRect(br);
+ if (painter()->hasClipping()) {
+ QRect cr = painter()->clipRegion().boundingRect();
+ br &= cr;
+ }
+
+ if (br.width() > 0.0 || br.height() > 0.0) {
+ int minx = qFloor(br.left());
+ int miny = qFloor(br.top());
+ int maxx = qCeil(br.right());
+ int maxy = qCeil(br.bottom());
+
+ if (d->pic_d->brect.width() > 0 || d->pic_d->brect.height() > 0) {
+ minx = qMin(minx, d->pic_d->brect.left());
+ miny = qMin(miny, d->pic_d->brect.top());
+ maxx = qMax(maxx, d->pic_d->brect.x() + d->pic_d->brect.width());
+ maxy = qMax(maxy, d->pic_d->brect.y() + d->pic_d->brect.height());
+ d->pic_d->brect = QRect(minx, miny, maxx - minx, maxy - miny);
+ } else {
+ d->pic_d->brect = QRect(minx, miny, maxx - minx, maxy - miny);
+ }
+ }
+ }
+}
+
+void QPicturePaintEngine::drawEllipse(const QRectF &rect)
+{
+ Q_D(QPicturePaintEngine);
+#ifdef QT_PICTURE_DEBUG
+ qDebug() << " -> drawEllipse():" << rect;
+#endif
+ int pos;
+ SERIALIZE_CMD(QPicturePrivate::PdcDrawEllipse);
+ d->s << rect;
+ writeCmdLength(pos, rect, true);
+}
+
+void QPicturePaintEngine::drawPath(const QPainterPath &path)
+{
+ Q_D(QPicturePaintEngine);
+#ifdef QT_PICTURE_DEBUG
+ qDebug() << " -> drawPath():" << path.boundingRect();
+#endif
+ int pos;
+ SERIALIZE_CMD(QPicturePrivate::PdcDrawPath);
+ d->s << path;
+ writeCmdLength(pos, path.boundingRect(), true);
+}
+
+void QPicturePaintEngine::drawPolygon(const QPointF *points, int numPoints, PolygonDrawMode mode)
+{
+ Q_D(QPicturePaintEngine);
+#ifdef QT_PICTURE_DEBUG
+ qDebug() << " -> drawPolygon(): size=" << numPoints;
+#endif
+ int pos;
+
+ QPolygonF polygon;
+ for (int i=0; i<numPoints; ++i)
+ polygon << points[i];
+
+ if (mode == PolylineMode) {
+ SERIALIZE_CMD(QPicturePrivate::PdcDrawPolyline);
+ d->s << polygon;
+ } else {
+ SERIALIZE_CMD(QPicturePrivate::PdcDrawPolygon);
+ d->s << polygon;
+ d->s << (qint8)(mode == OddEvenMode ? 0 : 1);
+ }
+
+ writeCmdLength(pos, polygon.boundingRect(), true);
+}
+
+void QPicturePaintEngine::drawPixmap(const QRectF &r, const QPixmap &pm, const QRectF &sr)
+{
+ Q_D(QPicturePaintEngine);
+#ifdef QT_PICTURE_DEBUG
+ qDebug() << " -> drawPixmap():" << r;
+#endif
+ int pos;
+ SERIALIZE_CMD(QPicturePrivate::PdcDrawPixmap);
+
+ if (d->pic_d->in_memory_only) {
+ int index = d->pic_d->pixmap_list.size();
+ d->pic_d->pixmap_list.append(pm);
+ d->s << r << index << sr;
+ } else {
+ d->s << r << pm << sr;
+ }
+ writeCmdLength(pos, r, false);
+}
+
+void QPicturePaintEngine::drawTiledPixmap(const QRectF &r, const QPixmap &pixmap, const QPointF &s)
+{
+ Q_D(QPicturePaintEngine);
+#ifdef QT_PICTURE_DEBUG
+ qDebug() << " -> drawTiledPixmap():" << r << s;
+#endif
+ int pos;
+ SERIALIZE_CMD(QPicturePrivate::PdcDrawTiledPixmap);
+ if (d->pic_d->in_memory_only) {
+ int index = d->pic_d->pixmap_list.size();
+ d->pic_d->pixmap_list.append(pixmap);
+ d->s << r << index << s;
+ } else {
+ d->s << r << pixmap << s;
+ }
+ writeCmdLength(pos, r, false);
+}
+
+extern int qt_defaultDpi();
+
+void QPicturePaintEngine::drawTextItem(const QPointF &p , const QTextItem &ti)
+{
+ Q_D(QPicturePaintEngine);
+#ifdef QT_PICTURE_DEBUG
+ qDebug() << " -> drawTextItem():" << p << ti.text();
+#endif
+
+ if (d->pic_d->formatMajor >= 9) {
+ const QTextItemInt &si = static_cast<const QTextItemInt &>(ti);
+ int pos;
+ SERIALIZE_CMD(QPicturePrivate::PdcDrawTextItem);
+ QFont fnt = ti.font();
+ fnt.setUnderline(false);
+ fnt.setStrikeOut(false);
+ fnt.setOverline(false);
+
+ qreal justificationWidth = 0;
+ if (si.justified)
+ justificationWidth = si.width.toReal();
+
+ d->s << p << ti.text() << fnt << ti.renderFlags() << double(fnt.d->dpi)/qt_defaultDpi() << justificationWidth;
+ writeCmdLength(pos, /*brect=*/QRectF(), /*corr=*/false);
+ } else if (d->pic_d->formatMajor >= 8) {
+ // old old (buggy) format
+ int pos;
+ SERIALIZE_CMD(QPicturePrivate::PdcDrawTextItem);
+ d->s << QPointF(p.x(), p.y() - ti.ascent()) << ti.text() << ti.font() << ti.renderFlags();
+ writeCmdLength(pos, /*brect=*/QRectF(), /*corr=*/false);
+ } else {
+ // old (buggy) format
+ int pos;
+ SERIALIZE_CMD(QPicturePrivate::PdcDrawText2);
+ d->s << p << ti.text();
+ writeCmdLength(pos, QRectF(p, QSizeF(1,1)), true);
+ }
+}
+
+void QPicturePaintEngine::updateState(const QPaintEngineState &state)
+{
+ QPaintEngine::DirtyFlags flags = state.state();
+ if (flags & DirtyPen) updatePen(state.pen());
+ if (flags & DirtyBrush) updateBrush(state.brush());
+ if (flags & DirtyBrushOrigin) updateBrushOrigin(state.brushOrigin());
+ if (flags & DirtyFont) updateFont(state.font());
+ if (flags & DirtyBackground) updateBackground(state.backgroundMode(), state.backgroundBrush());
+ if (flags & DirtyTransform) updateMatrix(state.transform());
+ if (flags & DirtyClipEnabled) updateClipEnabled(state.isClipEnabled());
+ if (flags & DirtyClipRegion) updateClipRegion(state.clipRegion(), state.clipOperation());
+ if (flags & DirtyClipPath) updateClipPath(state.clipPath(), state.clipOperation());
+ if (flags & DirtyHints) updateRenderHints(state.renderHints());
+ if (flags & DirtyCompositionMode) updateCompositionMode(state.compositionMode());
+ if (flags & DirtyOpacity) updateOpacity(state.opacity());
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_PICTURE
diff --git a/src/gui/image/qpaintengine_pic_p.h b/src/gui/image/qpaintengine_pic_p.h
new file mode 100644
index 0000000000..3ae08451f1
--- /dev/null
+++ b/src/gui/image/qpaintengine_pic_p.h
@@ -0,0 +1,120 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QPAINTENGINE_PIC_P_H
+#define QPAINTENGINE_PIC_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of QAbstractItemModel*. This header file may change from version
+// to version without notice, or even be removed.
+//
+// We mean it.
+//
+//
+
+#include "QtGui/qpaintengine.h"
+
+#ifndef QT_NO_PICTURE
+
+QT_BEGIN_NAMESPACE
+
+class QPicturePaintEnginePrivate;
+class QBuffer;
+
+class QPicturePaintEngine : public QPaintEngine
+{
+ Q_DECLARE_PRIVATE(QPicturePaintEngine)
+public:
+ QPicturePaintEngine();
+ ~QPicturePaintEngine();
+
+ bool begin(QPaintDevice *pdev);
+ bool end();
+
+ void updateState(const QPaintEngineState &state);
+
+ void updatePen(const QPen &pen);
+ void updateBrush(const QBrush &brush);
+ void updateBrushOrigin(const QPointF &origin);
+ void updateFont(const QFont &font);
+ void updateBackground(Qt::BGMode bgmode, const QBrush &bgBrush);
+ void updateMatrix(const QTransform &matrix);
+ void updateClipRegion(const QRegion &region, Qt::ClipOperation op);
+ void updateClipPath(const QPainterPath &path, Qt::ClipOperation op);
+ void updateRenderHints(QPainter::RenderHints hints);
+ void updateCompositionMode(QPainter::CompositionMode cmode);
+ void updateClipEnabled(bool enabled);
+ void updateOpacity(qreal opacity);
+
+ void drawEllipse(const QRectF &rect);
+ void drawPath(const QPainterPath &path);
+ void drawPolygon(const QPointF *points, int numPoints, PolygonDrawMode mode);
+#ifdef Q_NO_USING_KEYWORD
+ inline void drawPolygon(const QPoint *points, int pointCount, PolygonDrawMode mode)
+ { QPaintEngine::drawPolygon(points, pointCount, mode); }
+#else
+ using QPaintEngine::drawPolygon;
+#endif
+
+ 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);
+
+ Type type() const { return Picture; }
+
+protected:
+ QPicturePaintEngine(QPaintEnginePrivate &dptr);
+
+private:
+ Q_DISABLE_COPY(QPicturePaintEngine)
+
+ void writeCmdLength(int pos, const QRectF &r, bool corr);
+};
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_PICTURE
+
+#endif // QPAINTENGINE_PIC_P_H
diff --git a/src/gui/image/qpicture.cpp b/src/gui/image/qpicture.cpp
new file mode 100644
index 0000000000..d5d7cb0f49
--- /dev/null
+++ b/src/gui/image/qpicture.cpp
@@ -0,0 +1,1968 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qpicture.h"
+#include <private/qpicture_p.h>
+
+#ifndef QT_NO_PICTURE
+
+#include <private/qfactoryloader_p.h>
+#include <private/qpaintengine_pic_p.h>
+
+#include "qdatastream.h"
+#include "qfile.h"
+#include "qimage.h"
+#include "qmutex.h"
+#include "qpainter.h"
+#include "qpainterpath.h"
+#include "qpixmap.h"
+#include "qregion.h"
+#include "qdebug.h"
+
+QT_BEGIN_NAMESPACE
+
+void qt_format_text(const QFont &fnt, const QRectF &_r,
+ int tf, const QTextOption *opt, const QString& str, QRectF *brect,
+ int tabstops, int *, int tabarraylen,
+ QPainter *painter);
+
+/*!
+ \class QPicture
+ \brief The QPicture class is a paint device that records and
+ replays QPainter commands.
+
+ \ingroup multimedia
+ \ingroup shared
+ \mainclass
+
+ A picture serializes painter commands to an IO device in a
+ platform-independent format. They are sometimes referred to as meta-files.
+
+ Qt pictures use a proprietary binary format. Unlike native picture
+ (meta-file) formats on many window systems, Qt pictures have no
+ limitations regarding their contents. Everything that can be
+ painted on a widget or pixmap (e.g., fonts, pixmaps, regions,
+ transformed graphics, etc.) can also be stored in a picture.
+
+ QPicture is resolution independent, i.e. a QPicture can be
+ displayed on different devices (for example svg, pdf, ps, printer
+ and screen) looking the same. This is, for instance, needed for
+ WYSIWYG print preview. QPicture runs in the default system dpi,
+ and scales the painter to match differences in resolution
+ depending on the window system.
+
+ Example of how to record a picture:
+ \snippet doc/src/snippets/picture/picture.cpp 0
+
+ Note that the list of painter commands is reset on each call to
+ the QPainter::begin() function.
+
+ Example of how to replay a picture:
+ \snippet doc/src/snippets/picture/picture.cpp 1
+
+ Pictures can also be drawn using play(). Some basic data about a
+ picture is available, for example, size(), isNull() and
+ boundingRect().
+
+ \sa QMovie
+*/
+
+const char *qt_mfhdr_tag = "QPIC"; // header tag
+static const quint16 mfhdr_maj = 11; // major version #
+static const quint16 mfhdr_min = 0; // minor version #
+extern int qt_defaultDpiX();
+extern int qt_defaultDpiY();
+
+/*!
+ Constructs an empty picture.
+
+ The \a formatVersion parameter may be used to \e create a QPicture
+ that can be read by applications that are compiled with earlier
+ versions of Qt.
+
+ Note that the default formatVersion is -1 which signifies the
+ current release, i.e. for Qt 4.0 a formatVersion of 7 is the same
+ as the default formatVersion of -1.
+
+ Reading pictures generated by earlier versions of Qt is not
+ supported in Qt 4.0.
+*/
+
+QPicture::QPicture(int formatVersion)
+ : QPaintDevice(),
+ d_ptr(new QPicturePrivate)
+{
+ Q_D(QPicture);
+ d_ptr->q_ptr = this;
+ d->paintEngine = 0;
+
+ if (formatVersion == 0)
+ qWarning("QPicture: invalid format version 0");
+
+ // still accept the 0 default from before Qt 3.0.
+ if (formatVersion > 0 && formatVersion != (int)mfhdr_maj) {
+ d->formatMajor = formatVersion;
+ d->formatMinor = 0;
+ d->formatOk = false;
+ }
+ else {
+ d->resetFormat();
+ }
+}
+
+/*!
+ Constructs a copy of \a pic.
+
+ This constructor is fast thanks to \l{implicit sharing}.
+*/
+
+QPicture::QPicture(const QPicture &pic)
+ : QPaintDevice(), d_ptr(pic.d_ptr)
+{
+ d_func()->ref.ref();
+}
+
+/*! \internal */
+QPicture::QPicture(QPicturePrivate &dptr)
+ : QPaintDevice(),
+ d_ptr(&dptr)
+{
+ d_ptr->q_ptr = this;
+}
+
+/*!
+ Destroys the picture.
+*/
+QPicture::~QPicture()
+{
+ if (!d_func()->ref.deref()) {
+ delete d_func()->paintEngine;
+ delete d_func();
+ }
+}
+
+/*!
+ \internal
+*/
+int QPicture::devType() const
+{
+ return QInternal::Picture;
+}
+
+/*!
+ \fn bool QPicture::isNull() const
+
+ Returns true if the picture contains no data; otherwise returns
+ false.
+*/
+
+/*!
+ \fn uint QPicture::size() const
+
+ Returns the size of the picture data.
+
+ \sa data()
+*/
+
+/*!
+ \fn const char* QPicture::data() const
+
+ Returns a pointer to the picture data. The pointer is only valid
+ until the next non-const function is called on this picture. The
+ returned pointer is 0 if the picture contains no data.
+
+ \sa size(), isNull()
+*/
+
+
+bool QPicture::isNull() const
+{
+ return d_func()->pictb.buffer().isNull();
+}
+
+uint QPicture::size() const
+{
+ return d_func()->pictb.buffer().size();
+}
+
+const char* QPicture::data() const
+{
+ return d_func()->pictb.buffer();
+}
+
+void QPicture::detach()
+{
+ if (d_func()->ref != 1)
+ detach_helper();
+}
+
+bool QPicture::isDetached() const
+{
+ return d_func()->ref == 1;
+}
+
+/*!
+ Sets the picture data directly from \a data and \a size. This
+ function copies the input data.
+
+ \sa data(), size()
+*/
+
+void QPicture::setData(const char* data, uint size)
+{
+ detach();
+ d_func()->pictb.setData(data, size);
+ d_func()->resetFormat(); // we'll have to check
+}
+
+
+/*!
+ Loads a picture from the file specified by \a fileName and returns
+ true if successful; otherwise returns false.
+
+ Please note that the \a format parameter has been deprecated and
+ will have no effect.
+
+ \sa save()
+*/
+
+bool QPicture::load(const QString &fileName, const char *format)
+{
+ QFile f(fileName);
+ if (!f.open(QIODevice::ReadOnly))
+ return false;
+ return load(&f, format);
+}
+
+/*!
+ \overload
+
+ \a dev is the device to use for loading.
+*/
+
+bool QPicture::load(QIODevice *dev, const char *format)
+{
+ if(format) {
+#ifndef QT_NO_PICTUREIO
+ QPictureIO io(dev, format);
+ bool result = io.read();
+ if (result) {
+ operator=(io.picture());
+
+ } else if (format)
+#else
+ bool result = false;
+#endif
+ {
+ qWarning("QPicture::load: No such picture format: %s", format);
+ }
+ return result;
+ }
+
+ detach();
+ QByteArray a = dev->readAll();
+
+ d_func()->pictb.setData(a); // set byte array in buffer
+ return d_func()->checkFormat();
+}
+
+/*!
+ Saves a picture to the file specified by \a fileName and returns
+ true if successful; otherwise returns false.
+
+ Please note that the \a format parameter has been deprecated and
+ will have no effect.
+
+ \sa load()
+*/
+
+bool QPicture::save(const QString &fileName, const char *format)
+{
+ if (paintingActive()) {
+ qWarning("QPicture::save: still being painted on. "
+ "Call QPainter::end() first");
+ return false;
+ }
+
+
+ if(format) {
+#ifndef QT_NO_PICTUREIO
+ QPictureIO io(fileName, format);
+ bool result = io.write();
+ if (result) {
+ operator=(io.picture());
+ } else if (format)
+#else
+ bool result = false;
+#endif
+ {
+ qWarning("QPicture::save: No such picture format: %s", format);
+ }
+ return result;
+ }
+
+ QFile f(fileName);
+ if (!f.open(QIODevice::WriteOnly))
+ return false;
+ return save(&f, format);
+}
+
+/*!
+ \overload
+
+ \a dev is the device to use for saving.
+*/
+
+bool QPicture::save(QIODevice *dev, const char *format)
+{
+ if (paintingActive()) {
+ qWarning("QPicture::save: still being painted on. "
+ "Call QPainter::end() first");
+ return false;
+ }
+
+ if(format) {
+#ifndef QT_NO_PICTUREIO
+ QPictureIO io(dev, format);
+ bool result = io.write();
+ if (result) {
+ operator=(io.picture());
+ } else if (format)
+#else
+ bool result = false;
+#endif
+ {
+ qWarning("QPicture::save: No such picture format: %s", format);
+ }
+ return result;
+ }
+
+ dev->write(d_func()->pictb.buffer(), d_func()->pictb.buffer().size());
+ return true;
+}
+
+/*!
+ Returns the picture's bounding rectangle or an invalid rectangle
+ if the picture contains no data.
+*/
+
+QRect QPicture::boundingRect() const
+{
+ Q_D(const QPicture);
+ // Use override rect where possible.
+ if (!d->override_rect.isEmpty())
+ return d->override_rect;
+
+ if (!d->formatOk)
+ d_ptr->checkFormat();
+
+ return d->brect;
+}
+
+/*!
+ Sets the picture's bounding rectangle to \a r. The automatically
+ calculated value is overridden.
+*/
+
+void QPicture::setBoundingRect(const QRect &r)
+{
+ d_func()->override_rect = r;
+}
+
+/*!
+ Replays the picture using \a painter, and returns true if
+ successful; otherwise returns false.
+
+ This function does exactly the same as QPainter::drawPicture()
+ with (x, y) = (0, 0).
+*/
+
+bool QPicture::play(QPainter *painter)
+{
+ Q_D(QPicture);
+
+ if (d->pictb.size() == 0) // nothing recorded
+ return true;
+
+ if (!d->formatOk && !d->checkFormat())
+ return false;
+
+ d->pictb.open(QIODevice::ReadOnly); // open buffer device
+ QDataStream s;
+ s.setDevice(&d->pictb); // attach data stream to buffer
+ s.device()->seek(10); // go directly to the data
+ s.setVersion(d->formatMajor == 4 ? 3 : d->formatMajor);
+
+ quint8 c, clen;
+ quint32 nrecords;
+ s >> c >> clen;
+ Q_ASSERT(c == QPicturePrivate::PdcBegin);
+ // bounding rect was introduced in ver 4. Read in checkFormat().
+ if (d->formatMajor >= 4) {
+ qint32 dummy;
+ s >> dummy >> dummy >> dummy >> dummy;
+ }
+ s >> nrecords;
+ if (!exec(painter, s, nrecords)) {
+ qWarning("QPicture::play: Format error");
+ d->pictb.close();
+ return false;
+ }
+ d->pictb.close();
+ return true; // no end-command
+}
+
+
+//
+// QFakeDevice is used to create fonts with a custom DPI
+//
+class QFakeDevice : public QPaintDevice
+{
+public:
+ QFakeDevice() { dpi_x = qt_defaultDpiX(); dpi_y = qt_defaultDpiY(); }
+ void setDpiX(int dpi) { dpi_x = dpi; }
+ void setDpiY(int dpi) { dpi_y = dpi; }
+ QPaintEngine *paintEngine() const { return 0; }
+ int metric(PaintDeviceMetric m) const
+ {
+ switch(m) {
+ case PdmPhysicalDpiX:
+ case PdmDpiX:
+ return dpi_x;
+ case PdmPhysicalDpiY:
+ case PdmDpiY:
+ return dpi_y;
+ default:
+ return QPaintDevice::metric(m);
+ }
+ }
+
+private:
+ int dpi_x;
+ int dpi_y;
+};
+
+/*!
+ \internal
+ Iterates over the internal picture data and draws the picture using
+ \a painter.
+*/
+
+bool QPicture::exec(QPainter *painter, QDataStream &s, int nrecords)
+{
+ Q_D(QPicture);
+#if defined(QT_DEBUG)
+ int strm_pos;
+#endif
+ quint8 c; // command id
+ quint8 tiny_len; // 8-bit length descriptor
+ qint32 len; // 32-bit length descriptor
+ qint16 i_16, i1_16, i2_16; // parameters...
+ qint8 i_8;
+ quint32 ul;
+ double dbl;
+ bool bl;
+ QByteArray str1;
+ QString str;
+ QPointF p, p1, p2;
+ QPoint ip, ip1, ip2;
+ QRect ir;
+ QRectF r;
+ QPolygonF a;
+ QPolygon ia;
+ QColor color;
+ QFont font;
+ QPen pen;
+ QBrush brush;
+ QRegion rgn;
+ QMatrix wmatrix;
+ QTransform matrix;
+
+ QTransform worldMatrix = painter->transform();
+ worldMatrix.scale(qreal(painter->device()->logicalDpiX()) / qreal(qt_defaultDpiX()),
+ qreal(painter->device()->logicalDpiY()) / qreal(qt_defaultDpiY()));
+ painter->setTransform(worldMatrix);
+
+ while (nrecords-- && !s.atEnd()) {
+ s >> c; // read cmd
+ s >> tiny_len; // read param length
+ if (tiny_len == 255) // longer than 254 bytes
+ s >> len;
+ else
+ len = tiny_len;
+#if defined(QT_DEBUG)
+ strm_pos = s.device()->pos();
+#endif
+ switch (c) { // exec cmd
+ case QPicturePrivate::PdcNOP:
+ break;
+ case QPicturePrivate::PdcDrawPoint:
+ if (d->formatMajor <= 5) {
+ s >> ip;
+ painter->drawPoint(ip);
+ } else {
+ s >> p;
+ painter->drawPoint(p);
+ }
+ break;
+ case QPicturePrivate::PdcDrawPoints:
+// ## implement me in the picture paint engine
+// s >> a >> i1_32 >> i2_32;
+// painter->drawPoints(a.mid(i1_32, i2_32));
+ break;
+ case QPicturePrivate::PdcDrawPath: {
+ QPainterPath path;
+ s >> path;
+ painter->drawPath(path);
+ break;
+ }
+ case QPicturePrivate::PdcDrawLine:
+ if (d->formatMajor <= 5) {
+ s >> ip1 >> ip2;
+ painter->drawLine(ip1, ip2);
+ } else {
+ s >> p1 >> p2;
+ painter->drawLine(p1, p2);
+ }
+ break;
+ case QPicturePrivate::PdcDrawRect:
+ if (d->formatMajor <= 5) {
+ s >> ir;
+ painter->drawRect(ir);
+ } else {
+ s >> r;
+ painter->drawRect(r);
+ }
+ break;
+ case QPicturePrivate::PdcDrawRoundRect:
+ if (d->formatMajor <= 5) {
+ s >> ir >> i1_16 >> i2_16;
+ painter->drawRoundedRect(ir, i1_16, i2_16, Qt::RelativeSize);
+ } else {
+ s >> r >> i1_16 >> i2_16;
+ painter->drawRoundedRect(r, i1_16, i2_16, Qt::RelativeSize);
+ }
+ break;
+ case QPicturePrivate::PdcDrawEllipse:
+ if (d->formatMajor <= 5) {
+ s >> ir;
+ painter->drawEllipse(ir);
+ } else {
+ s >> r;
+ painter->drawEllipse(r);
+ }
+ break;
+ case QPicturePrivate::PdcDrawArc:
+ if (d->formatMajor <= 5) {
+ s >> ir;
+ r = ir;
+ } else {
+ s >> r;
+ }
+ s >> i1_16 >> i2_16;
+ painter->drawArc(r, i1_16, i2_16);
+ break;
+ case QPicturePrivate::PdcDrawPie:
+ if (d->formatMajor <= 5) {
+ s >> ir;
+ r = ir;
+ } else {
+ s >> r;
+ }
+ s >> i1_16 >> i2_16;
+ painter->drawPie(r, i1_16, i2_16);
+ break;
+ case QPicturePrivate::PdcDrawChord:
+ if (d->formatMajor <= 5) {
+ s >> ir;
+ r = ir;
+ } else {
+ s >> r;
+ }
+ s >> i1_16 >> i2_16;
+ painter->drawChord(r, i1_16, i2_16);
+ break;
+ case QPicturePrivate::PdcDrawLineSegments:
+ s >> ia;
+ painter->drawLines(ia);
+ ia.clear();
+ break;
+ case QPicturePrivate::PdcDrawPolyline:
+ if (d->formatMajor <= 5) {
+ s >> ia;
+ painter->drawPolyline(ia);
+ ia.clear();
+ } else {
+ s >> a;
+ painter->drawPolyline(a);
+ a.clear();
+ }
+ break;
+ case QPicturePrivate::PdcDrawPolygon:
+ if (d->formatMajor <= 5) {
+ s >> ia >> i_8;
+ painter->drawPolygon(ia, i_8 ? Qt::WindingFill : Qt::OddEvenFill);
+ a.clear();
+ } else {
+ s >> a >> i_8;
+ painter->drawPolygon(a, i_8 ? Qt::WindingFill : Qt::OddEvenFill);
+ a.clear();
+ }
+ break;
+ case QPicturePrivate::PdcDrawCubicBezier: {
+ s >> ia;
+ QPainterPath path;
+ Q_ASSERT(ia.size() == 4);
+ path.moveTo(ia.at(0));
+ path.cubicTo(ia.at(1), ia.at(2), ia.at(3));
+ painter->strokePath(path, painter->pen());
+ a.clear();
+ }
+ break;
+ case QPicturePrivate::PdcDrawText:
+ s >> ip >> str1;
+ painter->drawText(ip, QString::fromLatin1(str1));
+ break;
+ case QPicturePrivate::PdcDrawTextFormatted:
+ s >> ir >> i_16 >> str1;
+ painter->drawText(ir, i_16, QString::fromLatin1(str1));
+ break;
+ case QPicturePrivate::PdcDrawText2:
+ if (d->formatMajor <= 5) {
+ s >> ip >> str;
+ painter->drawText(ip, str);
+ } else {
+ s >> p >> str;
+ painter->drawText(p, str);
+ }
+ break;
+ case QPicturePrivate::PdcDrawText2Formatted:
+ s >> ir;
+ s >> i_16;
+ s >> str;
+ painter->drawText(ir, i_16, str);
+ break;
+ case QPicturePrivate::PdcDrawTextItem: {
+ s >> p >> str >> font >> ul;
+
+ // the text layout direction is not used here because it's already
+ // aligned when QPicturePaintEngine::drawTextItem() serializes the
+ // drawText() call, therefore ul is unsed in this context
+
+ if (d->formatMajor >= 9) {
+ s >> dbl;
+ QFont fnt(font);
+ if (dbl != 1.0) {
+ QFakeDevice fake;
+ fake.setDpiX(qRound(dbl*qt_defaultDpiX()));
+ fake.setDpiY(qRound(dbl*qt_defaultDpiY()));
+ fnt = QFont(font, &fake);
+ }
+
+ qreal justificationWidth;
+ s >> justificationWidth;
+
+ int flags = Qt::TextSingleLine | Qt::TextDontClip | Qt::TextForceLeftToRight;
+
+ QSizeF size(1, 1);
+ if (justificationWidth > 0) {
+ size.setWidth(justificationWidth);
+ flags |= Qt::TextJustificationForced;
+ flags |= Qt::AlignJustify;
+ }
+
+ QFontMetrics fm(fnt);
+ QPointF pt(p.x(), p.y() - fm.ascent());
+ qt_format_text(fnt, QRectF(pt, size), flags, /*opt*/0,
+ str, /*brect=*/0, /*tabstops=*/0, /*...*/0, /*tabarraylen=*/0, painter);
+ } else {
+ qt_format_text(font, QRectF(p, QSizeF(1, 1)), Qt::TextSingleLine | Qt::TextDontClip, /*opt*/0,
+ str, /*brect=*/0, /*tabstops=*/0, /*...*/0, /*tabarraylen=*/0, painter);
+ }
+
+ break;
+ }
+ case QPicturePrivate::PdcDrawPixmap: {
+ QPixmap pixmap;
+ if (d->formatMajor < 4) {
+ s >> ip >> pixmap;
+ painter->drawPixmap(ip, pixmap);
+ } else if (d->formatMajor <= 5) {
+ s >> ir >> pixmap;
+ painter->drawPixmap(ir, pixmap);
+ } else {
+ QRectF sr;
+ if (d->in_memory_only) {
+ int index;
+ s >> r >> index >> sr;
+ Q_ASSERT(index < d->pixmap_list.size());
+ pixmap = d->pixmap_list.at(index);
+ } else {
+ s >> r >> pixmap >> sr;
+ }
+ painter->drawPixmap(r, pixmap, sr);
+ }
+ }
+ break;
+ case QPicturePrivate::PdcDrawTiledPixmap: {
+ QPixmap pixmap;
+ if (d->in_memory_only) {
+ int index;
+ s >> r >> index >> p;
+ Q_ASSERT(index < d->pixmap_list.size());
+ pixmap = d->pixmap_list.at(index);
+ } else {
+ s >> r >> pixmap >> p;
+ }
+ painter->drawTiledPixmap(r, pixmap, p);
+ }
+ break;
+ case QPicturePrivate::PdcDrawImage: {
+ QImage image;
+ if (d->formatMajor < 4) {
+ s >> p >> image;
+ painter->drawPixmap(p, QPixmap::fromImage(image));
+ } else if (d->formatMajor <= 5){
+ s >> ir >> image;
+ painter->drawPixmap(ir, QPixmap::fromImage(image), QRect(0, 0, ir.width(), ir.height()));
+ } else {
+ s >> r >> image;
+ painter->drawPixmap(r, QPixmap::fromImage(image), QRectF(0, 0, r.width(), r.height()));
+ }
+ }
+ break;
+ case QPicturePrivate::PdcBegin:
+ s >> ul; // number of records
+ if (!exec(painter, s, ul))
+ return false;
+ break;
+ case QPicturePrivate::PdcEnd:
+ if (nrecords == 0)
+ return true;
+ break;
+ case QPicturePrivate::PdcSave:
+ painter->save();
+ break;
+ case QPicturePrivate::PdcRestore:
+ painter->restore();
+ break;
+ case QPicturePrivate::PdcSetBkColor:
+ s >> color;
+ painter->setBackground(color);
+ break;
+ case QPicturePrivate::PdcSetBkMode:
+ s >> i_8;
+ painter->setBackgroundMode((Qt::BGMode)i_8);
+ break;
+ case QPicturePrivate::PdcSetROP: // NOP
+ s >> i_8;
+ break;
+ case QPicturePrivate::PdcSetBrushOrigin:
+ if (d->formatMajor <= 5) {
+ s >> ip;
+ painter->setBrushOrigin(ip);
+ } else {
+ s >> p;
+ painter->setBrushOrigin(p);
+ }
+ break;
+ case QPicturePrivate::PdcSetFont:
+ s >> font;
+ painter->setFont(font);
+ break;
+ case QPicturePrivate::PdcSetPen:
+ if (d->in_memory_only) {
+ int index;
+ s >> index;
+ Q_ASSERT(index < d->pen_list.size());
+ pen = d->pen_list.at(index);
+ } else {
+ s >> pen;
+ }
+ painter->setPen(pen);
+ break;
+ case QPicturePrivate::PdcSetBrush:
+ if (d->in_memory_only) {
+ int index;
+ s >> index;
+ Q_ASSERT(index < d->brush_list.size());
+ brush = d->brush_list.at(index);
+ } else {
+ s >> brush;
+ }
+ painter->setBrush(brush);
+ break;
+// #ifdef Q_Q3PAINTER
+// case QPicturePrivate::PdcSetTabStops:
+// s >> i_16;
+// painter->setTabStops(i_16);
+// break;
+// case QPicturePrivate::PdcSetTabArray:
+// s >> i_16;
+// if (i_16 == 0) {
+// painter->setTabArray(0);
+// } else {
+// int *ta = new int[i_16];
+// for (int i=0; i<i_16; i++) {
+// s >> i1_16;
+// ta[i] = i1_16;
+// }
+// painter->setTabArray(ta);
+// delete [] ta;
+// }
+// break;
+// #endif
+ case QPicturePrivate::PdcSetVXform:
+ s >> i_8;
+ painter->setViewTransformEnabled(i_8);
+ break;
+ case QPicturePrivate::PdcSetWindow:
+ if (d->formatMajor <= 5) {
+ s >> ir;
+ painter->setWindow(ir);
+ } else {
+ s >> r;
+ painter->setWindow(r.toRect());
+ }
+ break;
+ case QPicturePrivate::PdcSetViewport:
+ if (d->formatMajor <= 5) {
+ s >> ir;
+ painter->setViewport(ir);
+ } else {
+ s >> r;
+ painter->setViewport(r.toRect());
+ }
+ break;
+ case QPicturePrivate::PdcSetWXform:
+ s >> i_8;
+ painter->setMatrixEnabled(i_8);
+ break;
+ case QPicturePrivate::PdcSetWMatrix:
+ if (d->formatMajor >= 8) {
+ s >> matrix >> i_8;
+ } else {
+ s >> wmatrix >> i_8;
+ matrix = QTransform(wmatrix);
+ }
+ // i_8 is always false due to updateXForm() in qpaintengine_pic.cpp
+ painter->setTransform(matrix * worldMatrix, i_8);
+ break;
+// #ifdef Q_Q3PAINTER
+// case QPicturePrivate::PdcSaveWMatrix:
+// painter->saveWorldMatrix();
+// break;
+// case QPicturePrivate::PdcRestoreWMatrix:
+// painter->restoreWorldMatrix();
+// break;
+// #endif
+ case QPicturePrivate::PdcSetClip:
+ s >> i_8;
+ painter->setClipping(i_8);
+ break;
+ case QPicturePrivate::PdcSetClipRegion:
+ s >> rgn >> i_8;
+ if (d->formatMajor >= 9) {
+ painter->setClipRegion(rgn, Qt::ClipOperation(i_8));
+ } else {
+ painter->setClipRegion(rgn);
+ }
+ break;
+ case QPicturePrivate::PdcSetClipPath:
+ {
+ QPainterPath path;
+ s >> path >> i_8;
+ painter->setClipPath(path, Qt::ClipOperation(i_8));
+ break;
+ }
+ case QPicturePrivate::PdcSetRenderHint:
+ s >> ul;
+ painter->setRenderHint(QPainter::Antialiasing,
+ bool(ul & QPainter::Antialiasing));
+ painter->setRenderHint(QPainter::SmoothPixmapTransform,
+ bool(ul & QPainter::SmoothPixmapTransform));
+ break;
+ case QPicturePrivate::PdcSetCompositionMode:
+ s >> ul;
+ painter->setCompositionMode((QPainter::CompositionMode)ul);
+ break;
+ case QPicturePrivate::PdcSetClipEnabled:
+ s >> bl;
+ painter->setClipping(bl);
+ break;
+ case QPicturePrivate::PdcSetOpacity:
+ s >> dbl;
+ painter->setOpacity(qreal(dbl));
+ break;
+ default:
+ qWarning("QPicture::play: Invalid command %d", c);
+ if (len) // skip unknown command
+ s.device()->seek(s.device()->pos()+len);
+ }
+#if defined(QT_DEBUG)
+ //qDebug("device->at(): %i, strm_pos: %i len: %i", (int)s.device()->pos(), strm_pos, len);
+ Q_ASSERT(qint32(s.device()->pos() - strm_pos) == len);
+#endif
+ }
+ return false;
+}
+
+/*!
+ Internal implementation of the virtual QPaintDevice::metric()
+ function.
+
+ A picture has the following hard-coded values: numcolors=16777216
+ and depth=24.
+
+ \a m is the metric to get.
+*/
+
+int QPicture::metric(PaintDeviceMetric m) const
+{
+ int val;
+ QRect brect = boundingRect();
+ switch (m) {
+ case PdmWidth:
+ val = brect.width();
+ break;
+ case PdmHeight:
+ val = brect.height();
+ break;
+ case PdmWidthMM:
+ val = int(25.4/qt_defaultDpiX()*brect.width());
+ break;
+ case PdmHeightMM:
+ val = int(25.4/qt_defaultDpiY()*brect.height());
+ break;
+ case PdmDpiX:
+ case PdmPhysicalDpiX:
+ val = qt_defaultDpiX();
+ break;
+ case PdmDpiY:
+ case PdmPhysicalDpiY:
+ val = qt_defaultDpiY();
+ break;
+ case PdmNumColors:
+ val = 16777216;
+ break;
+ case PdmDepth:
+ val = 24;
+ break;
+ default:
+ val = 0;
+ qWarning("QPicture::metric: Invalid metric command");
+ }
+ return val;
+}
+
+/*!
+ \fn void QPicture::detach()
+ \internal
+ Detaches from shared picture data and makes sure that this picture
+ is the only one referring to the data.
+
+ If multiple pictures share common data, this picture makes a copy
+ of the data and detaches itself from the sharing mechanism.
+ Nothing is done if there is just a single reference.
+*/
+
+/*! \fn bool QPicture::isDetached() const
+\internal
+*/
+void QPicture::detach_helper()
+{
+ Q_D(QPicture);
+ QPicturePrivate *x = new QPicturePrivate;
+ int pictsize = size();
+ x->pictb.setData(data(), pictsize);
+ if (d->pictb.isOpen()) {
+ x->pictb.open(d->pictb.openMode());
+ x->pictb.seek(d->pictb.pos());
+ }
+ x->trecs = d->trecs;
+ x->formatOk = d->formatOk;
+ x->formatMinor = d->formatMinor;
+ x->brect = d->brect;
+ x->override_rect = d->override_rect;
+ if (!d->ref.deref())
+ delete d;
+ d_ptr = x;
+}
+
+/*!
+ Assigns picture \a p to this picture and returns a reference to
+ this picture.
+*/
+QPicture& QPicture::operator=(const QPicture &p)
+{
+ qAtomicAssign<QPicturePrivate>(d_ptr, p.d_ptr);
+ return *this;
+}
+
+/*!
+ \internal
+
+ Sets formatOk to false and resets the format version numbers to default
+*/
+
+void QPicturePrivate::resetFormat()
+{
+ formatOk = false;
+ formatMajor = mfhdr_maj;
+ formatMinor = mfhdr_min;
+}
+
+
+/*!
+ \internal
+
+ Checks data integrity and format version number. Set formatOk to
+ true on success, to false otherwise. Returns the resulting formatOk
+ value.
+*/
+bool QPicturePrivate::checkFormat()
+{
+ resetFormat();
+
+ // can't check anything in an empty buffer
+ if (pictb.size() == 0 || pictb.isOpen())
+ return false;
+
+ pictb.open(QIODevice::ReadOnly); // open buffer device
+ QDataStream s;
+ s.setDevice(&pictb); // attach data stream to buffer
+
+ char mf_id[4]; // picture header tag
+ s.readRawData(mf_id, 4); // read actual tag
+ if (memcmp(mf_id, qt_mfhdr_tag, 4) != 0) { // wrong header id
+ qWarning("QPicturePaintEngine::checkFormat: Incorrect header");
+ pictb.close();
+ return false;
+ }
+
+ int cs_start = sizeof(quint32); // pos of checksum word
+ int data_start = cs_start + sizeof(quint16);
+ quint16 cs,ccs;
+ QByteArray buf = pictb.buffer(); // pointer to data
+
+ s >> cs; // read checksum
+ ccs = (quint16) qChecksum(buf.constData() + data_start, buf.size() - data_start);
+ if (ccs != cs) {
+ qWarning("QPicturePaintEngine::checkFormat: Invalid checksum %x, %x expected",
+ ccs, cs);
+ pictb.close();
+ return false;
+ }
+
+ quint16 major, minor;
+ s >> major >> minor; // read version number
+ if (major > mfhdr_maj) { // new, incompatible version
+ qWarning("QPicturePaintEngine::checkFormat: Incompatible version %d.%d",
+ major, minor);
+ pictb.close();
+ return false;
+ }
+ s.setVersion(major != 4 ? major : 3);
+
+ quint8 c, clen;
+ s >> c >> clen;
+ if (c == QPicturePrivate::PdcBegin) {
+ if (!(major >= 1 && major <= 3)) {
+ qint32 l, t, w, h;
+ s >> l >> t >> w >> h;
+ brect = QRect(l, t, w, h);
+ }
+ } else {
+ qWarning("QPicturePaintEngine::checkFormat: Format error");
+ pictb.close();
+ return false;
+ }
+ pictb.close();
+
+ formatOk = true; // picture seems to be ok
+ formatMajor = major;
+ formatMinor = minor;
+ return true;
+}
+
+/*! \internal */
+QPaintEngine *QPicture::paintEngine() const
+{
+ if (!d_func()->paintEngine)
+ const_cast<QPicture*>(this)->d_func()->paintEngine = new QPicturePaintEngine;
+ return d_func()->paintEngine;
+}
+
+/*****************************************************************************
+ QPicture stream functions
+ *****************************************************************************/
+
+/*!
+ \relates QPicture
+
+ Writes picture \a r to the stream \a s and returns a reference to
+ the stream.
+*/
+
+QDataStream &operator<<(QDataStream &s, const QPicture &r)
+{
+ quint32 size = r.d_func()->pictb.buffer().size();
+ s << size;
+ // null picture ?
+ if (size == 0)
+ return s;
+ // just write the whole buffer to the stream
+ s.writeRawData (r.d_func()->pictb.buffer(), r.d_func()->pictb.buffer().size());
+ return s;
+}
+
+/*!
+ \relates QPicture
+
+ Reads a picture from the stream \a s into picture \a r and returns
+ a reference to the stream.
+*/
+
+QDataStream &operator>>(QDataStream &s, QPicture &r)
+{
+ QDataStream sr;
+
+ // "init"; this code is similar to the beginning of QPicture::cmd()
+ sr.setDevice(&r.d_func()->pictb);
+ sr.setVersion(r.d_func()->formatMajor);
+ quint32 len;
+ s >> len;
+ QByteArray data;
+ if (len > 0) {
+ data.resize(len);
+ s.readRawData(data.data(), len);
+ }
+
+ r.d_func()->pictb.setData(data);
+ r.d_func()->resetFormat();
+ return s;
+}
+
+
+#ifndef QT_NO_PICTUREIO
+
+QT_BEGIN_INCLUDE_NAMESPACE
+#include "qregexp.h"
+#include "qapplication.h"
+#include "qpictureformatplugin.h"
+QT_END_INCLUDE_NAMESPACE
+
+/*!
+ \obsolete
+
+ Returns a string that specifies the picture format of the file \a
+ fileName, or 0 if the file cannot be read or if the format is not
+ recognized.
+
+ \sa load() save()
+*/
+
+const char* QPicture::pictureFormat(const QString &fileName)
+{
+ return QPictureIO::pictureFormat(fileName);
+}
+
+/*!
+ \obsolete
+
+ Returns a list of picture formats that are supported for picture
+ input.
+
+ \sa outputFormats() inputFormatList() QPictureIO
+*/
+QList<QByteArray> QPicture::inputFormats()
+{
+ return QPictureIO::inputFormats();
+}
+
+static QStringList qToStringList(const QList<QByteArray> arr)
+{
+ QStringList list;
+ for (int i = 0; i < arr.count(); ++i)
+ list.append(QString::fromLatin1(arr.at(i)));
+ return list;
+}
+
+/*!
+ \obsolete
+
+ Returns a list of picture formats that are supported for picture
+ input.
+
+ Note that if you want to iterate over the list, you should iterate
+ over a copy, e.g.
+ \snippet doc/src/snippets/picture/picture.cpp 2
+
+ \sa outputFormatList() inputFormats() QPictureIO
+*/
+QStringList QPicture::inputFormatList()
+{
+ return qToStringList(QPictureIO::inputFormats());
+}
+
+
+/*!
+ \obsolete
+
+ Returns a list of picture formats that are supported for picture
+ output.
+
+ Note that if you want to iterate over the list, you should iterate
+ over a copy, e.g.
+ \snippet doc/src/snippets/picture/picture.cpp 3
+
+ \sa inputFormatList() outputFormats() QPictureIO
+*/
+QStringList QPicture::outputFormatList()
+{
+ return qToStringList(QPictureIO::outputFormats());
+}
+
+/*!
+ \obsolete
+
+ Returns a list of picture formats that are supported for picture
+ output.
+
+ \sa inputFormats() outputFormatList() QPictureIO
+*/
+QList<QByteArray> QPicture::outputFormats()
+{
+ return QPictureIO::outputFormats();
+}
+
+/*****************************************************************************
+ QPictureIO member functions
+ *****************************************************************************/
+
+/*!
+ \obsolete
+
+ \class QPictureIO
+
+ \brief The QPictureIO class contains parameters for loading and
+ saving pictures.
+
+ \ingroup multimedia
+ \ingroup io
+
+ QPictureIO contains a QIODevice object that is used for picture data
+ I/O. The programmer can install new picture file formats in addition
+ to those that Qt provides.
+
+ You don't normally need to use this class; QPicture::load(),
+ QPicture::save().
+
+ \sa QPicture QPixmap QFile
+*/
+
+struct QPictureIOData
+{
+ QPicture pi; // picture
+ int iostat; // IO status
+ QByteArray frmt; // picture format
+ QIODevice *iodev; // IO device
+ QString fname; // file name
+ QString descr; // picture description
+ const char *parameters;
+ int quality;
+ float gamma;
+};
+
+/*!
+ Constructs a QPictureIO object with all parameters set to zero.
+*/
+
+QPictureIO::QPictureIO()
+{
+ init();
+}
+
+/*!
+ Constructs a QPictureIO object with the I/O device \a ioDevice and a
+ \a format tag.
+*/
+
+QPictureIO::QPictureIO(QIODevice *ioDevice, const char *format)
+{
+ init();
+ d->iodev = ioDevice;
+ d->frmt = format;
+}
+
+/*!
+ Constructs a QPictureIO object with the file name \a fileName and a
+ \a format tag.
+*/
+
+QPictureIO::QPictureIO(const QString &fileName, const char* format)
+{
+ init();
+ d->frmt = format;
+ d->fname = fileName;
+}
+
+/*!
+ Contains initialization common to all QPictureIO constructors.
+*/
+
+void QPictureIO::init()
+{
+ d = new QPictureIOData();
+ d->parameters = 0;
+ d->quality = -1; // default quality of the current format
+ d->gamma=0.0f;
+ d->iostat = 0;
+ d->iodev = 0;
+}
+
+/*!
+ Destroys the object and all related data.
+*/
+
+QPictureIO::~QPictureIO()
+{
+ if (d->parameters)
+ delete [] (char*)d->parameters;
+ delete d;
+}
+
+
+/*****************************************************************************
+ QPictureIO picture handler functions
+ *****************************************************************************/
+
+class QPictureHandler
+{
+public:
+ QPictureHandler(const char *f, const char *h, const QByteArray& fl,
+ picture_io_handler r, picture_io_handler w);
+ QByteArray format; // picture format
+ QRegExp header; // picture header pattern
+ enum TMode { Untranslated=0, TranslateIn, TranslateInOut } text_mode;
+ picture_io_handler read_picture; // picture read function
+ picture_io_handler write_picture; // picture write function
+ bool obsolete; // support not "published"
+};
+
+QPictureHandler::QPictureHandler(const char *f, const char *h, const QByteArray& fl,
+ picture_io_handler r, picture_io_handler w)
+ : format(f), header(QString::fromLatin1(h))
+{
+ text_mode = Untranslated;
+ if (fl.contains('t'))
+ text_mode = TranslateIn;
+ else if (fl.contains('T'))
+ text_mode = TranslateInOut;
+ obsolete = fl.contains('O');
+ read_picture = r;
+ write_picture = w;
+}
+
+typedef QList<QPictureHandler *> QPHList;
+Q_GLOBAL_STATIC(QPHList, pictureHandlers)
+
+#ifndef QT_NO_LIBRARY
+Q_GLOBAL_STATIC(QMutex, mutex)
+Q_GLOBAL_STATIC_WITH_ARGS(QFactoryLoader, factoryLoader,
+ (QPictureFormatInterface_iid,
+ QLatin1String("/pictureformats")))
+#endif
+void qt_init_picture_plugins()
+{
+#ifndef QT_NO_LIBRARY
+ QMutexLocker locker(mutex());
+ QFactoryLoader *loader = factoryLoader();
+ QStringList keys = loader->keys();
+ for (int i = 0; i < keys.count(); ++i)
+ if (QPictureFormatInterface *format = qobject_cast<QPictureFormatInterface*>(loader->instance(keys.at(i))))
+ format->installIOHandler(keys.at(i));
+#endif
+}
+
+static void cleanup()
+{
+ // make sure that picture handlers are delete before plugin manager
+ if (QPHList *list = pictureHandlers()) {
+ qDeleteAll(*list);
+ list->clear();
+ }
+}
+
+void qt_init_picture_handlers() // initialize picture handlers
+{
+ static QBasicAtomicInt done = Q_BASIC_ATOMIC_INITIALIZER(0);
+ if (done.testAndSetRelaxed(0, 1)) {
+ qAddPostRoutine(cleanup);
+ }
+}
+
+static QPictureHandler *get_picture_handler(const char *format)
+{ // get pointer to handler
+ qt_init_picture_handlers();
+ qt_init_picture_plugins();
+ if (QPHList *list = pictureHandlers()) {
+ for (int i = 0; i < list->size(); ++i) {
+ if (list->at(i)->format == format)
+ return list->at(i);
+ }
+ }
+ return 0; // no such handler
+}
+
+
+/*!
+ Defines a picture I/O handler for the picture format called \a
+ format, which is recognized using the regular
+ expression defined in \a header, read using \a readPicture and
+ written using \a writePicture.
+
+ \a flags is a string of single-character flags for this format.
+ The only flag defined currently is T (upper case), so the only
+ legal value for \a flags are "T" and the empty string. The "T"
+ flag means that the picture file is a text file, and Qt should treat
+ all newline conventions as equivalent. (XPM files and some PPM
+ files are text files for example.)
+
+ \a format is used to select a handler to write a QPicture; \a header
+ is used to select a handler to read an picture file.
+
+ If \a readPicture is a null pointer, the QPictureIO will not be able
+ to read pictures in \a format. If \a writePicture is a null pointer,
+ the QPictureIO will not be able to write pictures in \a format. If
+ both are null, the QPictureIO object is valid but useless.
+
+ Example:
+ \snippet doc/src/snippets/picture/picture.cpp 6
+ \codeline
+ \snippet doc/src/snippets/picture/picture.cpp 7
+ \codeline
+ \snippet doc/src/snippets/picture/picture.cpp 8
+
+ Before the regular expression test, all the 0 bytes in the file header are
+ converted to 1 bytes. This is done because when Qt was ASCII-based, QRegExp
+ could not handle 0 bytes in strings.
+
+ The regexp is only applied on the first 14 bytes of the file.
+
+ (Note that if one handlerIO supports writing a format and another
+ supports reading it, Qt supports both reading and writing. If two
+ handlers support the same operation, Qt chooses one arbitrarily.)
+*/
+
+void QPictureIO::defineIOHandler(const char *format,
+ const char *header,
+ const char *flags,
+ picture_io_handler readPicture,
+ picture_io_handler writePicture)
+{
+ qt_init_picture_handlers();
+ if (QPHList *list = pictureHandlers()) {
+ QPictureHandler *p;
+ p = new QPictureHandler(format, header, QByteArray(flags), readPicture, writePicture);
+ list->prepend(p);
+ }
+}
+
+
+/*****************************************************************************
+ QPictureIO normal member functions
+ *****************************************************************************/
+
+/*!
+ Returns the picture currently set.
+
+ \sa setPicture()
+*/
+const QPicture &QPictureIO::picture() const { return d->pi; }
+
+/*!
+ Returns the picture's IO status. A non-zero value indicates an
+ error, whereas 0 means that the IO operation was successful.
+
+ \sa setStatus()
+*/
+int QPictureIO::status() const { return d->iostat; }
+
+/*!
+ Returns the picture format string or 0 if no format has been
+ explicitly set.
+*/
+const char *QPictureIO::format() const { return d->frmt; }
+
+/*!
+ Returns the IO device currently set.
+
+ \sa setIODevice()
+*/
+QIODevice *QPictureIO::ioDevice() const { return d->iodev; }
+
+/*!
+ Returns the file name currently set.
+
+ \sa setFileName()
+*/
+QString QPictureIO::fileName() const { return d->fname; }
+
+
+/*!
+ Returns the picture description string.
+
+ \sa setDescription()
+*/
+QString QPictureIO::description() const { return d->descr; }
+
+/*!
+ Sets the picture to \a picture.
+
+ \sa picture()
+*/
+void QPictureIO::setPicture(const QPicture &picture)
+{
+ d->pi = picture;
+}
+
+/*!
+ Sets the picture IO status to \a status. A non-zero value indicates
+ an error, whereas 0 means that the IO operation was successful.
+
+ \sa status()
+*/
+void QPictureIO::setStatus(int status)
+{
+ d->iostat = status;
+}
+
+/*!
+ Sets the picture format to \a format for the picture to be read or
+ written.
+
+ It is necessary to specify a format before writing an picture, but
+ it is not necessary to specify a format before reading an picture.
+
+ If no format has been set, Qt guesses the picture format before
+ reading it. If a format is set the picture will only be read if it
+ has that format.
+
+ \sa read() write() format()
+*/
+void QPictureIO::setFormat(const char *format)
+{
+ d->frmt = format;
+}
+
+/*!
+ Sets the IO device to be used for reading or writing an picture.
+
+ Setting the IO device allows pictures to be read/written to any
+ block-oriented QIODevice.
+
+ If \a ioDevice is not null, this IO device will override file name
+ settings.
+
+ \sa setFileName()
+*/
+void QPictureIO::setIODevice(QIODevice *ioDevice)
+{
+ d->iodev = ioDevice;
+}
+
+/*!
+ Sets the name of the file to read or write an picture from to \a
+ fileName.
+
+ \sa setIODevice()
+*/
+void QPictureIO::setFileName(const QString &fileName)
+{
+ d->fname = fileName;
+}
+
+/*!
+ Returns the quality of the written picture, related to the
+ compression ratio.
+
+ \sa setQuality() QPicture::save()
+*/
+int QPictureIO::quality() const
+{
+ return d->quality;
+}
+
+/*!
+ Sets the quality of the written picture to \a q, related to the
+ compression ratio.
+
+ \a q must be in the range -1..100. Specify 0 to obtain small
+ compressed files, 100 for large uncompressed files. (-1 signifies
+ the default compression.)
+
+ \sa quality() QPicture::save()
+*/
+
+void QPictureIO::setQuality(int q)
+{
+ d->quality = q;
+}
+
+/*!
+ Returns the picture's parameters string.
+
+ \sa setParameters()
+*/
+
+const char *QPictureIO::parameters() const
+{
+ return d->parameters;
+}
+
+/*!
+ Sets the picture's parameter string to \a parameters. This is for
+ picture handlers that require special parameters.
+
+ Although the current picture formats supported by Qt ignore the
+ parameters string, it may be used in future extensions or by
+ contributions (for example, JPEG).
+
+ \sa parameters()
+*/
+
+void QPictureIO::setParameters(const char *parameters)
+{
+ if (d->parameters)
+ delete [] (char*)d->parameters;
+ d->parameters = qstrdup(parameters);
+}
+
+/*!
+ Sets the gamma value at which the picture will be viewed to \a
+ gamma. If the picture format stores a gamma value for which the
+ picture is intended to be used, then this setting will be used to
+ modify the picture. Setting to 0.0 will disable gamma correction
+ (i.e. any specification in the file will be ignored).
+
+ The default value is 0.0.
+
+ \sa gamma()
+*/
+void QPictureIO::setGamma(float gamma)
+{
+ d->gamma=gamma;
+}
+
+/*!
+ Returns the gamma value at which the picture will be viewed.
+
+ \sa setGamma()
+*/
+float QPictureIO::gamma() const
+{
+ return d->gamma;
+}
+
+/*!
+ Sets the picture description string for picture handlers that support
+ picture descriptions to \a description.
+
+ Currently, no picture format supported by Qt uses the description
+ string.
+*/
+
+void QPictureIO::setDescription(const QString &description)
+{
+ d->descr = description;
+}
+
+
+/*!
+ Returns a string that specifies the picture format of the file \a
+ fileName, or null if the file cannot be read or if the format is
+ not recognized.
+*/
+
+QByteArray QPictureIO::pictureFormat(const QString &fileName)
+{
+ QFile file(fileName);
+ QByteArray format;
+ if (!file.open(QIODevice::ReadOnly))
+ return format;
+ format = pictureFormat(&file);
+ file.close();
+ return format;
+}
+
+/*!
+ \overload
+
+ Returns a string that specifies the picture format of the picture read
+ from IO device \a d, or 0 if the device cannot be read or if the
+ format is not recognized.
+
+ Make sure that \a d is at the right position in the device (for
+ example, at the beginning of the file).
+
+ \sa QIODevice::at()
+*/
+
+QByteArray QPictureIO::pictureFormat(QIODevice *d)
+{
+ // if you change this change the documentation for defineIOHandler()
+ const int buflen = 14;
+
+ char buf[buflen];
+ char buf2[buflen];
+ qt_init_picture_handlers();
+ qt_init_picture_plugins();
+ int pos = d->pos(); // save position
+ int rdlen = d->read(buf, buflen); // read a few bytes
+
+ QByteArray format;
+ if (rdlen != buflen)
+ return format;
+
+ memcpy(buf2, buf, buflen);
+
+ for (int n = 0; n < rdlen; n++)
+ if (buf[n] == '\0')
+ buf[n] = '\001';
+ if (rdlen > 0) {
+ buf[rdlen - 1] = '\0';
+ QString bufStr = QString::fromLatin1(buf);
+ if (QPHList *list = pictureHandlers()) {
+ for (int i = 0; i < list->size(); ++i) {
+ if (list->at(i)->header.indexIn(bufStr) != -1) { // try match with headers
+ format = list->at(i)->format;
+ break;
+ }
+ }
+ }
+ }
+ d->seek(pos); // restore position
+ return format;
+}
+
+/*!
+ Returns a sorted list of picture formats that are supported for
+ picture input.
+*/
+QList<QByteArray> QPictureIO::inputFormats()
+{
+ QList<QByteArray> result;
+
+ qt_init_picture_handlers();
+ qt_init_picture_plugins();
+
+ if (QPHList *list = pictureHandlers()) {
+ for (int i = 0; i < list->size(); ++i) {
+ QPictureHandler *p = list->at(i);
+ if (p->read_picture && !p->obsolete && !result.contains(p->format))
+ result.append(p->format);
+ }
+ }
+ qSort(result);
+
+ return result;
+}
+
+/*!
+ Returns a sorted list of picture formats that are supported for
+ picture output.
+*/
+QList<QByteArray> QPictureIO::outputFormats()
+{
+ qt_init_picture_handlers();
+ qt_init_picture_plugins();
+
+ QList<QByteArray> result;
+ if (QPHList *list = pictureHandlers()) {
+ for (int i = 0; i < list->size(); ++i) {
+ QPictureHandler *p = list->at(i);
+ if (p->write_picture && !p->obsolete && !result.contains(p->format))
+ result.append(p->format);
+ }
+ }
+ return result;
+}
+
+
+
+/*!
+ Reads an picture into memory and returns true if the picture was
+ successfully read; otherwise returns false.
+
+ Before reading an picture you must set an IO device or a file name.
+ If both an IO device and a file name have been set, the IO device
+ will be used.
+
+ Setting the picture file format string is optional.
+
+ Note that this function does \e not set the \link format()
+ format\endlink used to read the picture. If you need that
+ information, use the pictureFormat() static functions.
+
+ Example:
+
+ \snippet doc/src/snippets/picture/picture.cpp 4
+
+ \sa setIODevice() setFileName() setFormat() write() QPixmap::load()
+*/
+bool QPictureIO::read()
+{
+ QFile file;
+ const char *picture_format;
+ QPictureHandler *h;
+
+ if (d->iodev) { // read from io device
+ // ok, already open
+ } else if (!d->fname.isEmpty()) { // read from file
+ file.setFileName(d->fname);
+ if (!file.open(QIODevice::ReadOnly))
+ return false; // cannot open file
+ d->iodev = &file;
+ } else { // no file name or io device
+ return false;
+ }
+ if (d->frmt.isEmpty()) {
+ // Try to guess format
+ picture_format = pictureFormat(d->iodev); // get picture format
+ if (!picture_format) {
+ if (file.isOpen()) { // unknown format
+ file.close();
+ d->iodev = 0;
+ }
+ return false;
+ }
+ } else {
+ picture_format = d->frmt;
+ }
+
+ h = get_picture_handler(picture_format);
+ if (file.isOpen()) {
+#if !defined(Q_OS_UNIX)
+ if (h && h->text_mode) { // reopen in translated mode
+ file.close();
+ file.open(QIODevice::ReadOnly | QIODevice::Text);
+ }
+ else
+#endif
+ file.seek(0); // position to start
+ }
+ d->iostat = 1; // assume error
+
+ if (h && h->read_picture)
+ (*h->read_picture)(this);
+
+ if (file.isOpen()) { // picture was read using file
+ file.close();
+ d->iodev = 0;
+ }
+ return d->iostat == 0; // picture successfully read?
+}
+
+
+/*!
+ Writes an picture to an IO device and returns true if the picture was
+ successfully written; otherwise returns false.
+
+ Before writing an picture you must set an IO device or a file name.
+ If both an IO device and a file name have been set, the IO device
+ will be used.
+
+ The picture will be written using the specified picture format.
+
+ Example:
+ \snippet doc/src/snippets/picture/picture.cpp 5
+
+ \sa setIODevice() setFileName() setFormat() read() QPixmap::save()
+*/
+bool QPictureIO::write()
+{
+ if (d->frmt.isEmpty())
+ return false;
+ QPictureHandler *h = get_picture_handler(d->frmt);
+ if (!h || !h->write_picture) {
+ qWarning("QPictureIO::write: No such picture format handler: %s",
+ format());
+ return false;
+ }
+ QFile file;
+ if (!d->iodev && !d->fname.isEmpty()) {
+ file.setFileName(d->fname);
+ bool translate = h->text_mode==QPictureHandler::TranslateInOut;
+ QIODevice::OpenMode fmode = translate ? QIODevice::WriteOnly | QIODevice::Text : QIODevice::OpenMode(QIODevice::WriteOnly);
+ if (!file.open(fmode)) // couldn't create file
+ return false;
+ d->iodev = &file;
+ }
+ d->iostat = 1;
+ (*h->write_picture)(this);
+ if (file.isOpen()) { // picture was written using file
+ file.close();
+ d->iodev = 0;
+ }
+ return d->iostat == 0; // picture successfully written?
+}
+#endif //QT_NO_PICTUREIO
+
+/*!
+ \fn QPicture QPicture::copy() const
+
+ Use simple assignment instead.
+*/
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_PICTURE
+
+/*!
+ \typedef QPicture::DataPtr
+ \internal
+*/
+
+/*!
+ \fn DataPtr &QPicture::data_ptr()
+ \internal
+*/
diff --git a/src/gui/image/qpicture.h b/src/gui/image/qpicture.h
new file mode 100644
index 0000000000..fe86e8d70c
--- /dev/null
+++ b/src/gui/image/qpicture.h
@@ -0,0 +1,196 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QPICTURE_H
+#define QPICTURE_H
+
+#include <QtGui/qpaintdevice.h>
+#include <QtCore/qstringlist.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+#ifndef QT_NO_PICTURE
+
+class QPicturePrivate;
+class Q_GUI_EXPORT QPicture : public QPaintDevice
+{
+ Q_DECLARE_PRIVATE(QPicture)
+public:
+ explicit QPicture(int formatVersion = -1);
+ QPicture(const QPicture &);
+ ~QPicture();
+
+ bool isNull() const;
+
+ int devType() const;
+ uint size() const;
+ const char* data() const;
+ virtual void setData(const char* data, uint size);
+
+ bool play(QPainter *p);
+
+ 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);
+
+ QRect boundingRect() const;
+ void setBoundingRect(const QRect &r);
+
+ QPicture& operator=(const QPicture &p);
+ void detach();
+ bool isDetached() const;
+
+ friend Q_GUI_EXPORT QDataStream &operator<<(QDataStream &in, const QPicture &p);
+ friend Q_GUI_EXPORT QDataStream &operator>>(QDataStream &in, QPicture &p);
+
+ static const char* pictureFormat(const QString &fileName);
+ static QList<QByteArray> inputFormats();
+ static QList<QByteArray> outputFormats();
+ static QStringList inputFormatList();
+ static QStringList outputFormatList();
+
+ QPaintEngine *paintEngine() const;
+
+protected:
+ QPicture(QPicturePrivate &data);
+
+ int metric(PaintDeviceMetric m) const;
+#ifdef QT3_SUPPORT
+ inline QT3_SUPPORT QPicture copy() const { QPicture p(*this); p.detach(); return p; }
+#endif
+
+private:
+ bool exec(QPainter *p, QDataStream &ds, int i);
+ void detach_helper();
+
+ QPicturePrivate *d_ptr;
+ friend class QPicturePaintEngine;
+ friend class Q3Picture;
+ friend class QAlphaPaintEngine;
+ friend class QPreviewPaintEngine;
+
+public:
+ typedef QPicturePrivate* DataPtr;
+ inline DataPtr &data_ptr() { return d_ptr; }
+};
+
+Q_DECLARE_SHARED(QPicture)
+
+
+#ifndef QT_NO_PICTUREIO
+class QIODevice;
+class QPictureIO;
+typedef void (*picture_io_handler)(QPictureIO *); // picture IO handler
+
+struct QPictureIOData;
+
+class Q_GUI_EXPORT QPictureIO
+{
+public:
+ QPictureIO();
+ QPictureIO(QIODevice *ioDevice, const char *format);
+ QPictureIO(const QString &fileName, const char *format);
+ ~QPictureIO();
+
+ const QPicture &picture() const;
+ int status() const;
+ const char *format() const;
+ QIODevice *ioDevice() const;
+ QString fileName() const;
+ int quality() const;
+ QString description() const;
+ const char *parameters() const;
+ float gamma() const;
+
+ void setPicture(const QPicture &);
+ void setStatus(int);
+ void setFormat(const char *);
+ void setIODevice(QIODevice *);
+ void setFileName(const QString &);
+ void setQuality(int);
+ void setDescription(const QString &);
+ void setParameters(const char *);
+ void setGamma(float);
+
+ bool read();
+ bool write();
+
+ static QByteArray pictureFormat(const QString &fileName);
+ static QByteArray pictureFormat(QIODevice *);
+ static QList<QByteArray> inputFormats();
+ static QList<QByteArray> outputFormats();
+
+ static void defineIOHandler(const char *format,
+ const char *header,
+ const char *flags,
+ picture_io_handler read_picture,
+ picture_io_handler write_picture);
+
+private:
+ Q_DISABLE_COPY(QPictureIO)
+
+ void init();
+
+ QPictureIOData *d;
+};
+
+#endif //QT_NO_PICTUREIO
+
+
+/*****************************************************************************
+ QPicture stream functions
+ *****************************************************************************/
+
+Q_GUI_EXPORT QDataStream &operator<<(QDataStream &, const QPicture &);
+Q_GUI_EXPORT QDataStream &operator>>(QDataStream &, QPicture &);
+
+#endif // QT_NO_PICTURE
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QPICTURE_H
diff --git a/src/gui/image/qpicture_p.h b/src/gui/image/qpicture_p.h
new file mode 100644
index 0000000000..1da7f0748e
--- /dev/null
+++ b/src/gui/image/qpicture_p.h
@@ -0,0 +1,170 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QPICTURE_P_H
+#define QPICTURE_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 "QtCore/qatomic.h"
+#include "QtCore/qbuffer.h"
+#include "QtCore/qobjectdefs.h"
+#include "QtGui/qpicture.h"
+#include "QtGui/qpixmap.h"
+#include "QtGui/qpen.h"
+#include "QtGui/qbrush.h"
+#include "QtCore/qrect.h"
+#include "private/qobject_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class QPaintEngine;
+
+extern const char *qt_mfhdr_tag;
+
+class Q_GUI_EXPORT QPicturePrivate
+{
+ Q_DECLARE_PUBLIC(QPicture)
+ friend class QPicturePaintEngine;
+ friend Q_GUI_EXPORT QDataStream &operator<<(QDataStream &s, const QPicture &r);
+ friend Q_GUI_EXPORT QDataStream &operator>>(QDataStream &s, QPicture &r);
+
+public:
+ enum PaintCommand {
+ PdcNOP = 0, // <void>
+ PdcDrawPoint = 1, // point
+ PdcDrawFirst = PdcDrawPoint,
+ PdcMoveTo = 2, // point
+ PdcLineTo = 3, // point
+ PdcDrawLine = 4, // point,point
+ PdcDrawRect = 5, // rect
+ PdcDrawRoundRect = 6, // rect,ival,ival
+ PdcDrawEllipse = 7, // rect
+ PdcDrawArc = 8, // rect,ival,ival
+ PdcDrawPie = 9, // rect,ival,ival
+ PdcDrawChord = 10, // rect,ival,ival
+ PdcDrawLineSegments = 11, // ptarr
+ PdcDrawPolyline = 12, // ptarr
+ PdcDrawPolygon = 13, // ptarr,ival
+ PdcDrawCubicBezier = 14, // ptarr
+ PdcDrawText = 15, // point,str
+ PdcDrawTextFormatted = 16, // rect,ival,str
+ PdcDrawPixmap = 17, // rect,pixmap
+ PdcDrawImage = 18, // rect,image
+ PdcDrawText2 = 19, // point,str
+ PdcDrawText2Formatted = 20, // rect,ival,str
+ PdcDrawTextItem = 21, // pos,text,font,flags
+ PdcDrawLast = PdcDrawTextItem,
+ PdcDrawPoints = 22, // ptarr,ival,ival
+ PdcDrawWinFocusRect = 23, // rect,color
+ PdcDrawTiledPixmap = 24, // rect,pixmap,point
+ PdcDrawPath = 25, // path
+
+ // no painting commands below PdcDrawLast.
+
+ PdcBegin = 30, // <void>
+ PdcEnd = 31, // <void>
+ PdcSave = 32, // <void>
+ PdcRestore = 33, // <void>
+ PdcSetdev = 34, // device - PRIVATE
+ PdcSetBkColor = 40, // color
+ PdcSetBkMode = 41, // ival
+ PdcSetROP = 42, // ival
+ PdcSetBrushOrigin = 43, // point
+ PdcSetFont = 45, // font
+ PdcSetPen = 46, // pen
+ PdcSetBrush = 47, // brush
+ PdcSetTabStops = 48, // ival
+ PdcSetTabArray = 49, // ival,ivec
+ PdcSetUnit = 50, // ival
+ PdcSetVXform = 51, // ival
+ PdcSetWindow = 52, // rect
+ PdcSetViewport = 53, // rect
+ PdcSetWXform = 54, // ival
+ PdcSetWMatrix = 55, // matrix,ival
+ PdcSaveWMatrix = 56,
+ PdcRestoreWMatrix = 57,
+ PdcSetClip = 60, // ival
+ PdcSetClipRegion = 61, // rgn
+ PdcSetClipPath = 62, // path
+ PdcSetRenderHint = 63, // ival
+ PdcSetCompositionMode = 64, // ival
+ PdcSetClipEnabled = 65, // bool
+ PdcSetOpacity = 66, // qreal
+
+ PdcReservedStart = 0, // codes 0-199 are reserved
+ PdcReservedStop = 199 // for Qt
+ };
+
+ inline QPicturePrivate() : in_memory_only(false), q_ptr(0) { ref = 1; }
+ QAtomicInt ref;
+
+ bool checkFormat();
+ void resetFormat();
+
+ QBuffer pictb;
+ int trecs;
+ bool formatOk;
+ int formatMajor;
+ int formatMinor;
+ QRect brect;
+ QRect override_rect;
+ QPaintEngine *paintEngine;
+ bool in_memory_only;
+ QList<QPixmap> pixmap_list;
+ QList<QBrush> brush_list;
+ QList<QPen> pen_list;
+
+ QPicture *q_ptr;
+};
+
+QT_END_NAMESPACE
+
+#endif // QPICTURE_P_H
diff --git a/src/gui/image/qpictureformatplugin.cpp b/src/gui/image/qpictureformatplugin.cpp
new file mode 100644
index 0000000000..33d10a44dd
--- /dev/null
+++ b/src/gui/image/qpictureformatplugin.cpp
@@ -0,0 +1,139 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qpictureformatplugin.h"
+#if !defined(QT_NO_LIBRARY) && !defined(QT_NO_PICTURE)
+#include "qpicture.h"
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \obsolete
+
+ \class QPictureFormatPlugin
+ \brief The QPictureFormatPlugin class provides an abstract base
+ for custom picture format plugins.
+
+ \ingroup plugins
+
+ The picture format plugin is a simple plugin interface that makes
+ it easy to create custom picture formats that can be used
+ transparently by applications.
+
+ Writing an picture format plugin is achieved by subclassing this
+ base class, reimplementing the pure virtual functions keys(),
+ loadPicture(), savePicture(), and installIOHandler(), and
+ exporting the class with the Q_EXPORT_PLUGIN2() macro.
+
+ \sa {How to Create Qt Plugins}
+*/
+
+/*!
+ \fn QStringList QPictureFormatPlugin::keys() const
+
+ Returns the list of picture formats this plugin supports.
+
+ \sa installIOHandler()
+*/
+
+/*!
+ \fn bool QPictureFormatPlugin::installIOHandler(const QString &format)
+
+ Installs a QPictureIO picture I/O handler for the picture format \a
+ format.
+
+ \sa keys()
+*/
+
+
+/*!
+ Constructs an picture format plugin with the given \a parent.
+ This is invoked automatically by the Q_EXPORT_PLUGIN2() macro.
+*/
+QPictureFormatPlugin::QPictureFormatPlugin(QObject *parent)
+ : QObject(parent)
+{
+}
+
+/*!
+ Destroys the picture format plugin.
+
+ You never have to call this explicitly. Qt destroys a plugin
+ automatically when it is no longer used.
+*/
+QPictureFormatPlugin::~QPictureFormatPlugin()
+{
+}
+
+
+/*!
+ Loads the picture stored in the file called \a fileName, with the
+ given \a format, into *\a picture. Returns true on success;
+ otherwise returns false.
+
+ \sa savePicture()
+*/
+bool QPictureFormatPlugin::loadPicture(const QString &format, const QString &fileName, QPicture *picture)
+{
+ Q_UNUSED(format)
+ Q_UNUSED(fileName)
+ Q_UNUSED(picture)
+ return false;
+}
+
+/*!
+ Saves the given \a picture into the file called \a fileName,
+ using the specified \a format. Returns true on success; otherwise
+ returns false.
+
+ \sa loadPicture()
+*/
+bool QPictureFormatPlugin::savePicture(const QString &format, const QString &fileName, const QPicture &picture)
+{
+ Q_UNUSED(format)
+ Q_UNUSED(fileName)
+ Q_UNUSED(picture)
+ return false;
+}
+
+#endif // QT_NO_LIBRARY || QT_NO_PICTURE
+
+QT_END_NAMESPACE
diff --git a/src/gui/image/qpictureformatplugin.h b/src/gui/image/qpictureformatplugin.h
new file mode 100644
index 0000000000..2eca024656
--- /dev/null
+++ b/src/gui/image/qpictureformatplugin.h
@@ -0,0 +1,94 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QPICTUREFORMATPLUGIN_H
+#define QPICTUREFORMATPLUGIN_H
+
+#include <QtCore/qplugin.h>
+#include <QtCore/qfactoryinterface.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+#if !defined(QT_NO_LIBRARY) && !defined(QT_NO_PICTURE)
+
+class QPicture;
+class QImage;
+class QString;
+class QStringList;
+
+struct Q_GUI_EXPORT QPictureFormatInterface : public QFactoryInterface
+{
+ virtual bool loadPicture(const QString &format, const QString &filename, QPicture *) = 0;
+ virtual bool savePicture(const QString &format, const QString &filename, const QPicture &) = 0;
+
+ virtual bool installIOHandler(const QString &) = 0;
+};
+
+#define QPictureFormatInterface_iid "com.trolltech.Qt.QPictureFormatInterface"
+Q_DECLARE_INTERFACE(QPictureFormatInterface, QPictureFormatInterface_iid)
+
+
+class Q_GUI_EXPORT QPictureFormatPlugin : public QObject, public QPictureFormatInterface
+{
+ Q_OBJECT
+ Q_INTERFACES(QPictureFormatInterface:QFactoryInterface)
+public:
+ explicit QPictureFormatPlugin(QObject *parent = 0);
+ ~QPictureFormatPlugin();
+
+ virtual QStringList keys() const = 0;
+ virtual bool loadPicture(const QString &format, const QString &filename, QPicture *pic);
+ virtual bool savePicture(const QString &format, const QString &filename, const QPicture &pic);
+ virtual bool installIOHandler(const QString &format) = 0;
+
+};
+
+#endif // QT_NO_LIBRARY || QT_NO_PICTURE
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QPICTUREFORMATPLUGIN_H
diff --git a/src/gui/image/qpixmap.cpp b/src/gui/image/qpixmap.cpp
new file mode 100644
index 0000000000..8684a1b7fc
--- /dev/null
+++ b/src/gui/image/qpixmap.cpp
@@ -0,0 +1,2003 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <qglobal.h>
+
+#include "qpixmap.h"
+#include "qpixmapdata_p.h"
+
+#include "qbitmap.h"
+#include "qcolormap.h"
+#include "qimage.h"
+#include "qwidget.h"
+#include "qpainter.h"
+#include "qdatastream.h"
+#include "qbuffer.h"
+#include "qapplication.h"
+#include <private/qapplication_p.h>
+#include <private/qgraphicssystem_p.h>
+#include <private/qwidget_p.h>
+#include "qevent.h"
+#include "qfile.h"
+#include "qfileinfo.h"
+#include "qpixmapcache.h"
+#include "qdatetime.h"
+#include "qimagereader.h"
+#include "qimagewriter.h"
+#include "qpaintengine.h"
+#include "qthread.h"
+
+#ifdef Q_WS_MAC
+# include "private/qt_mac_p.h"
+# include "private/qpixmap_mac_p.h"
+#endif
+
+#if defined(Q_WS_X11)
+# include "qx11info_x11.h"
+# include <private/qt_x11_p.h>
+# include <private/qpixmap_x11_p.h>
+#endif
+
+#include "qpixmap_raster_p.h"
+
+QT_BEGIN_NAMESPACE
+
+// ### Qt 5: remove
+typedef void (*_qt_pixmap_cleanup_hook)(int);
+Q_GUI_EXPORT _qt_pixmap_cleanup_hook qt_pixmap_cleanup_hook = 0;
+
+// ### Qt 5: rename
+typedef void (*_qt_pixmap_cleanup_hook_64)(qint64);
+Q_GUI_EXPORT _qt_pixmap_cleanup_hook_64 qt_pixmap_cleanup_hook_64 = 0;
+
+// ### Qt 5: remove
+Q_GUI_EXPORT qint64 qt_pixmap_id(const QPixmap &pixmap)
+{
+ return pixmap.cacheKey();
+}
+
+static bool qt_pixmap_thread_test()
+{
+ if (!qApp) {
+ qFatal("QPixmap: Must construct a QApplication before a QPaintDevice");
+ return false;
+ }
+#ifndef Q_WS_WIN
+ if (qApp->thread() != QThread::currentThread()) {
+ qWarning("QPixmap: It is not safe to use pixmaps outside the GUI thread");
+ return false;
+ }
+#endif
+ return true;
+}
+
+void QPixmap::init(int w, int h, Type type)
+{
+ init(w, h, int(type));
+}
+
+void QPixmap::init(int w, int h, int type)
+{
+ QGraphicsSystem* gs = QApplicationPrivate::graphicsSystem();
+ if (gs)
+ data = gs->createPixmapData(static_cast<QPixmapData::PixelType>(type));
+ else
+ data = QGraphicsSystem::createDefaultPixmapData(static_cast<QPixmapData::PixelType>(type));
+
+ data->resize(w, h);
+ data->ref.ref();
+}
+
+/*!
+ \enum QPixmap::ColorMode
+
+ \compat
+
+ This enum type defines the color modes that exist for converting
+ QImage objects to QPixmap. It is provided here for compatibility
+ with earlier versions of Qt.
+
+ Use Qt::ImageConversionFlags instead.
+
+ \value Auto Select \c Color or \c Mono on a case-by-case basis.
+ \value Color Always create colored pixmaps.
+ \value Mono Always create bitmaps.
+*/
+
+/*!
+ Constructs a null pixmap.
+
+ \sa isNull()
+*/
+
+QPixmap::QPixmap()
+ : QPaintDevice()
+{
+ (void) qt_pixmap_thread_test();
+ init(0, 0, QPixmapData::PixmapType);
+}
+
+/*!
+ \fn QPixmap::QPixmap(int width, int height)
+
+ Constructs a pixmap with the given \a width and \a height. If
+ either \a width or \a height is zero, a null pixmap is
+ constructed.
+
+ \warning This will create a QPixmap with uninitialized data. Call
+ fill() to fill the pixmap with an appropriate color before drawing
+ onto it with QPainter.
+
+ \sa isNull()
+*/
+
+QPixmap::QPixmap(int w, int h)
+ : QPaintDevice()
+{
+ if (!qt_pixmap_thread_test())
+ init(0, 0, QPixmapData::PixmapType);
+ else
+ init(w, h, QPixmapData::PixmapType);
+}
+
+/*!
+ \overload
+
+ Constructs a pixmap of the given \a size.
+
+ \warning This will create a QPixmap with uninitialized data. Call
+ fill() to fill the pixmap with an appropriate color before drawing
+ onto it with QPainter.
+*/
+
+QPixmap::QPixmap(const QSize &size)
+ : QPaintDevice()
+{
+ if (!qt_pixmap_thread_test())
+ init(0, 0, QPixmapData::PixmapType);
+ else
+ init(size.width(), size.height(), QPixmapData::PixmapType);
+}
+
+/*!
+ \internal
+*/
+QPixmap::QPixmap(const QSize &s, Type type)
+{
+ if (!qt_pixmap_thread_test())
+ init(0, 0, type);
+ else
+ init(s.width(), s.height(), type);
+}
+
+/*!
+ \internal
+*/
+QPixmap::QPixmap(const QSize &s, int type)
+{
+ if (!qt_pixmap_thread_test())
+ init(0, 0, static_cast<QPixmapData::PixelType>(type));
+ else
+ init(s.width(), s.height(), static_cast<QPixmapData::PixelType>(type));
+}
+
+/*!
+ \internal
+*/
+QPixmap::QPixmap(QPixmapData *d)
+ : QPaintDevice(), data(d)
+{
+ data->ref.ref();
+}
+
+/*!
+ Constructs a pixmap from the file with the given \a fileName. If the
+ file does not exist or is of an unknown format, the pixmap becomes a
+ null pixmap.
+
+ The loader attempts to read the pixmap using the specified \a
+ format. If the \a format is not specified (which is the default),
+ the loader probes the file for a header to guess the file format.
+
+ The file name can either refer to an actual file on disk or to
+ one of the application's embedded resources. See the
+ \l{resources.html}{Resource System} overview for details on how
+ to embed images and other resource files in the application's
+ executable.
+
+ If the image needs to be modified to fit in a lower-resolution
+ result (e.g. converting from 32-bit to 8-bit), use the \a
+ flags to control the conversion.
+
+ The \a fileName, \a format and \a flags parameters are
+ passed on to load(). This means that the data in \a fileName is
+ not compiled into the binary. If \a fileName contains a relative
+ path (e.g. the filename only) the relevant file must be found
+ relative to the runtime working directory.
+
+ \sa {QPixmap#Reading and Writing Image Files}{Reading and Writing
+ Image Files}
+*/
+
+QPixmap::QPixmap(const QString& fileName, const char *format, Qt::ImageConversionFlags flags)
+ : QPaintDevice()
+{
+ init(0, 0, QPixmapData::PixmapType);
+ if (!qt_pixmap_thread_test())
+ return;
+
+ load(fileName, format, flags);
+}
+
+/*!
+ Constructs a pixmap that is a copy of the given \a pixmap.
+
+ \sa copy()
+*/
+
+QPixmap::QPixmap(const QPixmap &pixmap)
+ : QPaintDevice()
+{
+ if (!qt_pixmap_thread_test()) {
+ init(0, 0, QPixmapData::PixmapType);
+ return;
+ }
+ if (pixmap.paintingActive()) { // make a deep copy
+ data = 0;
+ operator=(pixmap.copy());
+ } else {
+ data = pixmap.data;
+ data->ref.ref();
+ }
+}
+
+/*!
+ Constructs a pixmap from the given \a xpm data, which must be a
+ valid XPM image.
+
+ Errors are silently ignored.
+
+ Note that it's possible to squeeze the XPM variable a little bit
+ by using an unusual declaration:
+
+ \snippet doc/src/snippets/code/src_gui_image_qpixmap.cpp 0
+
+ The extra \c const makes the entire definition read-only, which is
+ slightly more efficient (for example, when the code is in a shared
+ library) and ROMable when the application is to be stored in ROM.
+*/
+#ifndef QT_NO_IMAGEFORMAT_XPM
+QPixmap::QPixmap(const char * const xpm[])
+ : QPaintDevice()
+{
+ init(0, 0, QPixmapData::PixmapType);
+ if (!xpm)
+ return;
+
+ QImage image(xpm);
+ if (!image.isNull()) {
+ if (data->pixelType() == QPixmapData::BitmapType)
+ *this = QBitmap::fromImage(image);
+ else
+ *this = fromImage(image);
+ }
+}
+#endif
+
+
+/*!
+ Destroys the pixmap.
+*/
+
+QPixmap::~QPixmap()
+{
+ deref();
+}
+
+/*!
+ \internal
+*/
+int QPixmap::devType() const
+{
+ return QInternal::Pixmap;
+}
+
+/*!
+ \fn QPixmap QPixmap::copy(int x, int y, int width, int height) const
+ \overload
+
+ Returns a deep copy of the subset of the pixmap that is specified
+ by the rectangle QRect( \a x, \a y, \a width, \a height).
+*/
+
+/*!
+ \fn QPixmap QPixmap::copy(const QRect &rectangle) const
+
+ Returns a deep copy of the subset of the pixmap that is specified
+ by the given \a rectangle. For more information on deep copies,
+ see the \l {Implicit Data Sharing} documentation.
+
+ If the given \a rectangle is empty, the whole image is copied.
+
+ \sa operator=(), QPixmap(), {QPixmap#Pixmap
+ Transformations}{Pixmap Transformations}
+*/
+QPixmap QPixmap::copy(const QRect &rect) const
+{
+ if (isNull())
+ return QPixmap();
+
+ const QRect r = rect.isEmpty() ? QRect(0, 0, width(), height()) : rect;
+
+ QPixmapData *d;
+ QGraphicsSystem* gs = QApplicationPrivate::graphicsSystem();
+ if (gs)
+ d = gs->createPixmapData(data->pixelType());
+ else
+ d = QGraphicsSystem::createDefaultPixmapData(data->pixelType());
+
+ d->copy(data, r);
+ return QPixmap(d);
+}
+
+/*!
+ Assigns the given \a pixmap to this pixmap and returns a reference
+ to this pixmap.
+
+ \sa copy(), QPixmap()
+*/
+
+QPixmap &QPixmap::operator=(const QPixmap &pixmap)
+{
+ if (paintingActive()) {
+ qWarning("QPixmap::operator=: Cannot assign to pixmap during painting");
+ return *this;
+ }
+ if (pixmap.paintingActive()) { // make a deep copy
+ *this = pixmap.copy();
+ } else {
+ pixmap.data->ref.ref(); // avoid 'x = x'
+ deref();
+ data = pixmap.data;
+ }
+ return *this;
+}
+
+/*!
+ Returns the pixmap as a QVariant.
+*/
+QPixmap::operator QVariant() const
+{
+ return QVariant(QVariant::Pixmap, this);
+}
+
+/*!
+ \fn bool QPixmap::operator!() const
+
+ Returns true if this is a null pixmap; otherwise returns false.
+
+ \sa isNull()
+*/
+
+/*!
+ \fn QPixmap::operator QImage() const
+
+ Returns the pixmap as a QImage.
+
+ Use the toImage() function instead.
+*/
+
+/*!
+ Converts the pixmap to a QImage. Returns a null image if the
+ conversion fails.
+
+ If the pixmap has 1-bit depth, the returned image will also be 1
+ bit deep. If the pixmap has 2- to 8-bit depth, the returned image
+ has 8-bit depth. If the pixmap has greater than 8-bit depth, the
+ returned image has 32-bit depth.
+
+ Note that for the moment, alpha masks on monochrome images are
+ ignored.
+
+ \sa fromImage(), {QImage#Image Formats}{Image Formats}
+*/
+QImage QPixmap::toImage() const
+{
+ if (isNull())
+ return QImage();
+
+ return data->toImage();
+}
+
+/*!
+ \fn QMatrix QPixmap::trueMatrix(const QTransform &matrix, int width, int height)
+
+ Returns the actual matrix used for transforming a pixmap with the
+ given \a width, \a height and \a matrix.
+
+ When transforming a pixmap using the transformed() function, the
+ transformation matrix is internally adjusted to compensate for
+ unwanted translation, i.e. transformed() returns the smallest
+ pixmap containing all transformed points of the original
+ pixmap. This function returns the modified matrix, which maps
+ points correctly from the original pixmap into the new pixmap.
+
+ \sa transformed(), {QPixmap#Pixmap Transformations}{Pixmap
+ Transformations}
+*/
+QTransform QPixmap::trueMatrix(const QTransform &m, int w, int h)
+{
+ return QImage::trueMatrix(m, w, h);
+}
+
+/*!
+ \overload
+
+ This convenience function loads the matrix \a m into a
+ QTransform and calls the overloaded function with the
+ QTransform and the width \a w and the height \a h.
+ */
+QMatrix QPixmap::trueMatrix(const QMatrix &m, int w, int h)
+{
+ return trueMatrix(QTransform(m), w, h).toAffine();
+}
+
+
+/*!
+ \fn bool QPixmap::isQBitmap() const
+
+ Returns true if this is a QBitmap; otherwise returns false.
+*/
+
+bool QPixmap::isQBitmap() const
+{
+ return data->type == QPixmapData::BitmapType;
+}
+
+/*!
+ \fn bool QPixmap::isNull() const
+
+ Returns true if this is a null pixmap; otherwise returns false.
+
+ A null pixmap has zero width, zero height and no contents. You
+ cannot draw in a null pixmap.
+*/
+bool QPixmap::isNull() const
+{
+ return data->width() == 0;
+}
+
+/*!
+ \fn int QPixmap::width() const
+
+ Returns the width of the pixmap.
+
+ \sa size(), {QPixmap#Pixmap Information}{Pixmap Information}
+*/
+int QPixmap::width() const
+{
+ return data->width();
+}
+
+/*!
+ \fn int QPixmap::height() const
+
+ Returns the height of the pixmap.
+
+ \sa size(), {QPixmap#Pixmap Information}{Pixmap Information}
+*/
+int QPixmap::height() const
+{
+ return data->height();
+}
+
+/*!
+ \fn QSize QPixmap::size() const
+
+ Returns the size of the pixmap.
+
+ \sa width(), height(), {QPixmap#Pixmap Information}{Pixmap
+ Information}
+*/
+QSize QPixmap::size() const
+{
+ return QSize(data->width(), data->height());
+}
+
+/*!
+ \fn QRect QPixmap::rect() const
+
+ Returns the pixmap's enclosing rectangle.
+
+ \sa {QPixmap#Pixmap Information}{Pixmap Information}
+*/
+QRect QPixmap::rect() const
+{
+ return QRect(0, 0, data->width(), data->height());
+}
+
+/*!
+ \fn int QPixmap::depth() const
+
+ Returns the depth of the pixmap.
+
+ The pixmap depth is also called bits per pixel (bpp) or bit planes
+ of a pixmap. A null pixmap has depth 0.
+
+ \sa defaultDepth(), {QPixmap#Pixmap Information}{Pixmap
+ Information}
+*/
+int QPixmap::depth() const
+{
+ return data->depth();
+}
+
+/*!
+ \fn void QPixmap::resize(const QSize &size)
+ \overload
+ \compat
+
+ Use QPixmap::copy() instead to get the pixmap with the new size.
+
+ \oldcode
+ pixmap.resize(size);
+ \newcode
+ pixmap = pixmap.copy(QRect(QPoint(0, 0), size));
+ \endcode
+*/
+#ifdef QT3_SUPPORT
+void QPixmap::resize_helper(const QSize &s)
+{
+ int w = s.width();
+ int h = s.height();
+ if (w < 1 || h < 1) {
+ *this = QPixmap();
+ return;
+ }
+
+ if (size() == s)
+ return;
+
+ // Create new pixmap
+ QPixmap pm(QSize(w, h), data->type);
+ bool uninit = false;
+#if defined(Q_WS_X11)
+ QX11PixmapData *x11Data = data->classId() == QPixmapData::X11Class ? static_cast<QX11PixmapData*>(data) : 0;
+ if (x11Data) {
+ pm.x11SetScreen(x11Data->xinfo.screen());
+ uninit = x11Data->uninit;
+ }
+#elif defined(Q_WS_MAC)
+ QMacPixmapData *macData = data->classId() == QPixmapData::MacClass ? static_cast<QMacPixmapData*>(data) : 0;
+ if (macData)
+ uninit = macData->uninit;
+#endif
+ if (!uninit && !isNull()) {
+ // Copy old pixmap
+ if (hasAlphaChannel())
+ pm.fill(Qt::transparent);
+ QPainter p(&pm);
+ p.drawPixmap(0, 0, *this, 0, 0, qMin(width(), w), qMin(height(), h));
+ }
+
+#if defined(Q_WS_MAC)
+#ifndef QT_MAC_NO_QUICKDRAW
+ if(macData && macData->qd_alpha)
+ macData->macQDUpdateAlpha();
+#endif
+#elif defined(Q_WS_X11)
+ if (x11Data && x11Data->x11_mask) {
+ QX11PixmapData *pmData = static_cast<QX11PixmapData*>(pm.data);
+ pmData->x11_mask = (Qt::HANDLE)XCreatePixmap(X11->display,
+ RootWindow(x11Data->xinfo.display(),
+ x11Data->xinfo.screen()),
+ w, h, 1);
+ GC gc = XCreateGC(X11->display, pmData->x11_mask, 0, 0);
+ XCopyArea(X11->display, x11Data->x11_mask, pmData->x11_mask, gc, 0, 0, qMin(width(), w), qMin(height(), h), 0, 0);
+ XFreeGC(X11->display, gc);
+ }
+#endif
+ *this = pm;
+}
+#endif
+
+/*!
+ \fn void QPixmap::resize(int width, int height)
+ \compat
+
+ Use QPixmap::copy() instead to get the pixmap with the new size.
+
+ \oldcode
+ pixmap.resize(10, 20);
+ \newcode
+ pixmap = pixmap.copy(0, 0, 10, 20);
+ \endcode
+*/
+
+/*!
+ \fn bool QPixmap::selfMask() const
+ \compat
+
+ Returns whether the pixmap is its own mask or not.
+
+ This function is no longer relevant since the concept of self
+ masking doesn't exists anymore.
+*/
+
+/*!
+ Sets a mask bitmap.
+
+ This function merges the \a mask with the pixmap's alpha channel. A pixel
+ value of 1 on the mask means the pixmap's pixel is unchanged; a value of 0
+ means the pixel is transparent. The mask must have the same size as this
+ pixmap.
+
+ Setting a null mask resets the mask, leaving the previously transparent
+ pixels black. The effect of this function is undefined when the pixmap is
+ being painted on.
+
+ This is potentially an expensive operation.
+
+ \sa mask(), {QPixmap#Pixmap Transformations}{Pixmap Transformations},
+ QBitmap
+*/
+void QPixmap::setMask(const QBitmap &mask)
+{
+ if (paintingActive()) {
+ qWarning("QPixmap::setMask: Cannot set mask while pixmap is being painted on");
+ return;
+ }
+
+ if (!mask.isNull() && mask.size() != size()) {
+ qWarning("QPixmap::setMask() mask size differs from pixmap size");
+ return;
+ }
+
+ if (static_cast<const QPixmap &>(mask).data == data) // trying to selfmask
+ return;
+
+ detach();
+ data->setMask(mask);
+}
+
+#ifndef QT_NO_IMAGE_HEURISTIC_MASK
+/*!
+ Creates and returns a heuristic mask for this pixmap.
+
+ The function works by selecting a color from one of the corners
+ and then chipping away pixels of that color, starting at all the
+ edges. If \a clipTight is true (the default) the mask is just
+ large enough to cover the pixels; otherwise, the mask is larger
+ than the data pixels.
+
+ The mask may not be perfect but it should be reasonable, so you
+ can do things such as the following:
+
+ \snippet doc/src/snippets/code/src_gui_image_qpixmap.cpp 1
+
+ This function is slow because it involves converting to/from a
+ QImage, and non-trivial computations.
+
+ \sa QImage::createHeuristicMask(), createMaskFromColor()
+*/
+QBitmap QPixmap::createHeuristicMask(bool clipTight) const
+{
+ QBitmap m = QBitmap::fromImage(toImage().createHeuristicMask(clipTight));
+ return m;
+}
+#endif
+
+/*!
+ Creates and returns a mask for this pixmap based on the given \a
+ maskColor. If the \a mode is Qt::MaskInColor, all pixels matching the
+ maskColor will be opaque. If \a mode is Qt::MaskOutColor, all pixels
+ matching the maskColor will be transparent.
+
+ This function is slow because it involves converting to/from a
+ QImage.
+
+ \sa createHeuristicMask(), QImage::createMaskFromColor()
+*/
+QBitmap QPixmap::createMaskFromColor(const QColor &maskColor, Qt::MaskMode mode) const
+{
+ QImage image = toImage().convertToFormat(QImage::Format_ARGB32);
+ return QBitmap::fromImage(image.createMaskFromColor(maskColor.rgba(), mode));
+}
+
+/*! \overload
+
+ Creates and returns a mask for this pixmap based on the given \a
+ maskColor. Same as calling createMaskFromColor(maskColor,
+ Qt::MaskInColor)
+
+ \sa createHeuristicMask(), QImage::createMaskFromColor()
+*/
+QBitmap QPixmap::createMaskFromColor(const QColor &maskColor) const
+{
+ return createMaskFromColor(maskColor, Qt::MaskInColor);
+}
+
+/*!
+ Loads a pixmap from the file with the given \a fileName. Returns
+ true if the pixmap was successfully loaded; otherwise returns
+ false.
+
+ The loader attempts to read the pixmap using the specified \a
+ format. If the \a format is not specified (which is the default),
+ the loader probes the file for a header to guess the file format.
+
+ The file name can either refer to an actual file on disk or to one
+ of the application's embedded resources. See the
+ \l{resources.html}{Resource System} overview for details on how to
+ embed pixmaps and other resource files in the application's
+ executable.
+
+ If the data needs to be modified to fit in a lower-resolution
+ result (e.g. converting from 32-bit to 8-bit), use the \a flags to
+ control the conversion.
+
+ Note that QPixmaps are automatically added to the QPixmapCache
+ when loaded from a file; the key used is internal and can not
+ be acquired.
+
+ \sa loadFromData(), {QPixmap#Reading and Writing Image
+ Files}{Reading and Writing Image Files}
+*/
+
+bool QPixmap::load(const QString &fileName, const char *format, Qt::ImageConversionFlags flags)
+{
+ if (fileName.isEmpty())
+ return false;
+
+ QFileInfo info(fileName);
+ QString key = QLatin1String("qt_pixmap_") + info.absoluteFilePath() + QLatin1Char('_') + QString::number(info.lastModified().toTime_t()) + QLatin1Char('_') +
+ QString::number(info.size()) + QLatin1Char('_') + QString::number(data->pixelType());
+
+ if (QPixmapCache::find(key, *this))
+ return true;
+
+ QImage image = QImageReader(fileName, format).read();
+ if (image.isNull())
+ return false;
+ QPixmap pm;
+ if (data->pixelType() == QPixmapData::BitmapType)
+ pm = QBitmap::fromImage(image, flags);
+ else
+ pm = fromImage(image, flags);
+ if (!pm.isNull()) {
+ *this = pm;
+ QPixmapCache::insert(key, *this);
+ return true;
+ }
+ return false;
+}
+
+/*!
+ \fn bool QPixmap::loadFromData(const uchar *data, uint len, const char *format, Qt::ImageConversionFlags flags)
+
+ Loads a pixmap from the \a len first bytes of the given binary \a
+ data. Returns true if the pixmap was loaded successfully;
+ otherwise returns false.
+
+ The loader attempts to read the pixmap using the specified \a
+ format. If the \a format is not specified (which is the default),
+ the loader probes the file for a header to guess the file format.
+
+ If the data needs to be modified to fit in a lower-resolution
+ result (e.g. converting from 32-bit to 8-bit), use the \a flags to
+ control the conversion.
+
+ \sa load(), {QPixmap#Reading and Writing Image Files}{Reading and
+ Writing Image Files}
+*/
+
+bool QPixmap::loadFromData(const uchar *buf, uint len, const char *format, Qt::ImageConversionFlags flags)
+{
+ QByteArray a = QByteArray::fromRawData(reinterpret_cast<const char *>(buf), len);
+ QBuffer b(&a);
+ b.open(QIODevice::ReadOnly);
+
+ QImage image = QImageReader(&b, format).read();
+ QPixmap pm;
+ if (data->pixelType() == QPixmapData::BitmapType)
+ pm = QBitmap::fromImage(image, flags);
+ else
+ pm = fromImage(image, flags);
+ if (!pm.isNull()) {
+ *this = pm;
+ return true;
+ }
+ return false;
+}
+
+/*!
+ \fn bool QPixmap::loadFromData(const QByteArray &data, const char *format, Qt::ImageConversionFlags flags)
+
+ \overload
+
+ Loads a pixmap from the binary \a data using the specified \a
+ format and conversion \a flags.
+*/
+
+
+/*!
+ Saves the pixmap to the file with the given \a fileName using the
+ specified image file \a format and \a quality factor. Returns true
+ if successful; otherwise returns false.
+
+ The \a quality factor must be in the range [0,100] or -1. Specify
+ 0 to obtain small compressed files, 100 for large uncompressed
+ files, and -1 to use the default settings.
+
+ If \a format is 0, an image format will be chosen from \a fileName's
+ suffix.
+
+ \sa {QPixmap#Reading and Writing Image Files}{Reading and Writing
+ Image Files}
+*/
+
+bool QPixmap::save(const QString &fileName, const char *format, int quality) const
+{
+ if (isNull())
+ return false; // nothing to save
+ QImageWriter writer(fileName, format);
+ return doImageIO(&writer, quality);
+}
+
+/*!
+ \overload
+
+ This function writes a QPixmap to the given \a device using the
+ specified image file \a format and \a quality factor. This can be
+ used, for example, to save a pixmap directly into a QByteArray:
+
+ \snippet doc/src/snippets/image/image.cpp 1
+*/
+
+bool QPixmap::save(QIODevice* device, const char* format, int quality) const
+{
+ if (isNull())
+ return false; // nothing to save
+ QImageWriter writer(device, format);
+ return doImageIO(&writer, quality);
+}
+
+/*! \internal
+*/
+bool QPixmap::doImageIO(QImageWriter *writer, int quality) const
+{
+ if (quality > 100 || quality < -1)
+ qWarning("QPixmap::save: quality out of range [-1,100]");
+ if (quality >= 0)
+ writer->setQuality(qMin(quality,100));
+ return writer->write(toImage());
+}
+
+
+// The implementation (and documentation) of
+// QPixmap::fill(const QWidget *, const QPoint &)
+// is in qwidget.cpp
+
+/*!
+ \fn void QPixmap::fill(const QWidget *widget, int x, int y)
+ \overload
+
+ Fills the pixmap with the \a widget's background color or pixmap.
+ The given point, (\a x, \a y), defines an offset in widget
+ coordinates to which the pixmap's top-left pixel will be mapped
+ to.
+*/
+
+/*!
+ Fills the pixmap with the given \a color.
+
+ \sa {QPixmap#Pixmap Transformations}{Pixmap Transformations}
+*/
+
+void QPixmap::fill(const QColor &color)
+{
+ if (isNull())
+ return;
+
+ detach();
+ data->fill(color);
+}
+
+/*! \obsolete
+ Returns a number that identifies the contents of this QPixmap
+ object. Distinct QPixmap objects can only have the same serial
+ number if they refer to the same contents (but they don't have
+ to).
+
+ Use cacheKey() instead.
+
+ \warning The serial number doesn't necessarily change when
+ the pixmap is altered. This means that it may be dangerous to use
+ it as a cache key. For caching pixmaps, we recommend using the
+ QPixmapCache class whenever possible.
+*/
+int QPixmap::serialNumber() const
+{
+ if (isNull())
+ return 0;
+ return data->serialNumber();
+}
+
+/*!
+ Returns a number that identifies this QPixmap. Distinct QPixmap
+ objects can only have the same cache key if they refer to the same
+ contents.
+
+ The cacheKey() will change when the pixmap is altered.
+*/
+qint64 QPixmap::cacheKey() const
+{
+ int classKey = data->classId();
+ if (classKey >= 1024)
+ classKey = -(classKey >> 10);
+ return ((((qint64) classKey) << 56)
+ | (((qint64) data->serialNumber()) << 32)
+ | ((qint64) (data->detach_no)));
+}
+
+static void sendResizeEvents(QWidget *target)
+{
+ QResizeEvent e(target->size(), QSize());
+ QApplication::sendEvent(target, &e);
+
+ const QObjectList children = target->children();
+ for (int i = 0; i < children.size(); ++i) {
+ QWidget *child = static_cast<QWidget*>(children.at(i));
+ if (child->isWidgetType() && !child->isWindow() && child->testAttribute(Qt::WA_PendingResizeEvent))
+ sendResizeEvents(child);
+ }
+}
+
+/*!
+ \fn QPixmap QPixmap::grabWidget(QWidget * widget, const QRect &rectangle)
+
+ Creates a pixmap and paints the given \a widget, restricted by the
+ given \a rectangle, in it. If the \a widget has any children, then
+ they are also painted in the appropriate positions.
+
+ If no rectangle is specified (the default) the entire widget is
+ painted.
+
+ If \a widget is 0, the specified rectangle doesn't overlap the
+ widget's rectangle, or an error occurs, the function will return a
+ null QPixmap. If the rectangle is a superset of the given \a
+ widget, the areas outside the \a widget are covered with the
+ widget's background.
+
+ This function actually asks \a widget to paint itself (and its
+ children to paint themselves) by calling paintEvent() with painter
+ redirection turned on. But QPixmap also provides the grabWindow()
+ function which is a bit faster by grabbing pixels directly off the
+ screen. In addition, if there are overlaying windows,
+ grabWindow(), unlike grabWidget(), will see them.
+
+ \warning Do not grab a widget from its QWidget::paintEvent().
+ However, it is safe to grab a widget from another widget's
+ \l {QWidget::}{paintEvent()}.
+
+ \sa grabWindow()
+*/
+
+QPixmap QPixmap::grabWidget(QWidget * widget, const QRect &rect)
+{
+ if (!widget)
+ return QPixmap();
+
+ if (widget->testAttribute(Qt::WA_PendingResizeEvent) || !widget->testAttribute(Qt::WA_WState_Created))
+ sendResizeEvents(widget);
+
+ QRect r(rect);
+ if (r.width() < 0)
+ r.setWidth(widget->width() - rect.x());
+ if (r.height() < 0)
+ r.setHeight(widget->height() - rect.y());
+
+ if (!r.intersects(widget->rect()))
+ return QPixmap();
+
+ QPixmap res(r.size());
+ widget->render(&res, QPoint(), r,
+ QWidget::DrawWindowBackground | QWidget::DrawChildren | QWidget::IgnoreMask);
+ return res;
+}
+
+/*!
+ \fn QPixmap QPixmap::grabWidget(QWidget *widget, int x, int y, int
+ width, int height)
+
+ \overload
+
+ Creates a pixmap and paints the given \a widget, restricted by
+ QRect(\a x, \a y, \a width, \a height), in it.
+
+ \warning Do not grab a widget from its QWidget::paintEvent().
+ However, it is safe to grab a widget from another widget's
+ \l {QWidget::}{paintEvent()}.
+*/
+
+
+/*!
+ \since 4.5
+
+ \enum QPixmap::ShareMode
+
+ This enum type defines the share modes that are available when
+ creating a QPixmap object from a raw X11 Pixmap handle.
+
+ \value ImplicitlyShared This mode will cause the QPixmap object to
+ create a copy of the internal data before it is modified, thus
+ keeping the original X11 pixmap intact.
+
+ \value ExplicitlyShared In this mode, the pixmap data will \e not be
+ copied before it is modified, which in effect will change the
+ original X11 pixmap.
+
+ \warning This enum is only used for X11 specific functions; using
+ it is non-portable.
+
+ \sa QPixmap::fromX11Pixmap()
+*/
+
+/*!
+ \since 4.5
+
+ \fn QPixmap QPixmap::fromX11Pixmap(Qt::HANDLE pixmap, QPixmap::ShareMode mode)
+
+ Creates a QPixmap from the native X11 Pixmap handle \a pixmap,
+ using \a mode as the share mode. The default share mode is
+ QPixmap::ImplicitlyShared, which means that a copy of the pixmap is
+ made if someone tries to modify it by e.g. drawing onto it.
+
+ QPixmap does \e not take ownership of the \a pixmap handle, and
+ have to be deleted by the user.
+
+ \warning This function is X11 specific; using it is non-portable.
+
+ \sa QPixmap::ShareMode
+*/
+
+
+#if defined(Q_WS_X11) || defined(Q_WS_QWS)
+
+/*!
+ Returns the pixmap's handle to the device context.
+
+ Note that, since QPixmap make use of \l {Implicit Data
+ Sharing}{implicit data sharing}, the detach() function must be
+ called explicitly to ensure that only \e this pixmap's data is
+ modified if the pixmap data is shared.
+
+ \warning This function is X11 specific; using it is non-portable.
+
+ \sa detach()
+*/
+
+Qt::HANDLE QPixmap::handle() const
+{
+#if defined(Q_WS_X11)
+ if (data->classId() == QPixmapData::X11Class)
+ return static_cast<QX11PixmapData*>(data)->handle();
+#endif
+ return 0;
+}
+#endif
+
+
+#ifdef QT3_SUPPORT
+static Qt::ImageConversionFlags colorModeToFlags(QPixmap::ColorMode mode)
+{
+ Qt::ImageConversionFlags flags = Qt::AutoColor;
+ switch (mode) {
+ case QPixmap::Color:
+ flags |= Qt::ColorOnly;
+ break;
+ case QPixmap::Mono:
+ flags |= Qt::MonoOnly;
+ break;
+ default:
+ break;// Nothing.
+ }
+ return flags;
+}
+
+/*!
+ Use the constructor that takes a Qt::ImageConversionFlag instead.
+*/
+
+QPixmap::QPixmap(const QString& fileName, const char *format, ColorMode mode)
+ : QPaintDevice()
+{
+ init(0, 0, QPixmapData::PixmapType);
+ if (!qt_pixmap_thread_test())
+ return;
+
+ load(fileName, format, colorModeToFlags(mode));
+}
+
+/*!
+ Constructs a pixmap from the QImage \a image.
+
+ Use the static fromImage() function instead.
+*/
+QPixmap::QPixmap(const QImage& image)
+ : QPaintDevice()
+{
+ init(0, 0, QPixmapData::PixmapType);
+ if (!qt_pixmap_thread_test())
+ return;
+
+ if (data->pixelType() == QPixmapData::BitmapType)
+ *this = QBitmap::fromImage(image);
+ else
+ *this = fromImage(image);
+}
+
+/*!
+ \overload
+
+ Converts the given \a image to a pixmap that is assigned to this
+ pixmap.
+
+ Use the static fromImage() function instead.
+*/
+
+QPixmap &QPixmap::operator=(const QImage &image)
+{
+ if (data->pixelType() == QPixmapData::BitmapType)
+ *this = QBitmap::fromImage(image);
+ else
+ *this = fromImage(image);
+ return *this;
+}
+
+/*!
+ Use the load() function that takes a Qt::ImageConversionFlag instead.
+*/
+
+bool QPixmap::load(const QString &fileName, const char *format, ColorMode mode)
+{
+ return load(fileName, format, colorModeToFlags(mode));
+}
+
+/*!
+ Use the loadFromData() function that takes a Qt::ImageConversionFlag instead.
+*/
+
+bool QPixmap::loadFromData(const uchar *buf, uint len, const char *format, ColorMode mode)
+{
+ return loadFromData(buf, len, format, colorModeToFlags(mode));
+}
+
+/*!
+ Use the static fromImage() function instead.
+*/
+bool QPixmap::convertFromImage(const QImage &image, ColorMode mode)
+{
+ if (data->pixelType() == QPixmapData::BitmapType)
+ *this = QBitmap::fromImage(image, colorModeToFlags(mode));
+ else
+ *this = fromImage(image, colorModeToFlags(mode));
+ return !isNull();
+}
+
+#endif
+
+/*****************************************************************************
+ QPixmap stream functions
+ *****************************************************************************/
+#if !defined(QT_NO_DATASTREAM)
+/*!
+ \relates QPixmap
+
+ Writes the given \a pixmap to the the given \a stream as a PNG
+ image. Note that writing the stream to a file will not produce a
+ valid image file.
+
+ \sa QPixmap::save(), {Format of the QDataStream Operators}
+*/
+
+QDataStream &operator<<(QDataStream &stream, const QPixmap &pixmap)
+{
+ return stream << pixmap.toImage();
+}
+
+/*!
+ \relates QPixmap
+
+ Reads an image from the given \a stream into the given \a pixmap.
+
+ \sa QPixmap::load(), {Format of the QDataStream Operators}
+*/
+
+QDataStream &operator>>(QDataStream &stream, QPixmap &pixmap)
+{
+ QImage image;
+ stream >> image;
+
+ if (image.isNull()) {
+ pixmap = QPixmap();
+ } else if (image.depth() == 1) {
+ pixmap = QBitmap::fromImage(image);
+ } else {
+ pixmap = QPixmap::fromImage(image);
+ }
+ return stream;
+}
+
+#endif //QT_NO_DATASTREAM
+
+#ifdef QT3_SUPPORT
+Q_GUI_EXPORT void copyBlt(QPixmap *dst, int dx, int dy,
+ const QPixmap *src, int sx, int sy, int sw, int sh)
+{
+ Q_ASSERT_X(dst, "::copyBlt", "Destination pixmap must be non-null");
+ Q_ASSERT_X(src, "::copyBlt", "Source pixmap must be non-null");
+
+ if (src->hasAlphaChannel()) {
+ if (dst->paintEngine()->hasFeature(QPaintEngine::PorterDuff)) {
+ QPainter p(dst);
+ p.setCompositionMode(QPainter::CompositionMode_Source);
+ p.drawPixmap(dx, dy, *src, sx, sy, sw, sh);
+ } else {
+ QImage image = dst->toImage().convertToFormat(QImage::Format_ARGB32_Premultiplied);
+ QPainter p(&image);
+ p.setCompositionMode(QPainter::CompositionMode_Source);
+ p.drawPixmap(dx, dy, *src, sx, sy, sw, sh);
+ p.end();
+ *dst = QPixmap::fromImage(image);
+ }
+ } else {
+ QPainter p(dst);
+ p.drawPixmap(dx, dy, *src, sx, sy, sw, sh);
+ }
+
+}
+#endif
+
+/*!
+ \internal
+*/
+
+bool QPixmap::isDetached() const
+{
+ return data->ref == 1;
+}
+
+void QPixmap::deref()
+{
+ if (data && !data->ref.deref()) { // Destroy image if last ref
+#if !defined(QT_NO_DIRECT3D) && defined(Q_WS_WIN)
+ if (data->classId() == QPixmapData::RasterClass) {
+ QRasterPixmapData *rData = static_cast<QRasterPixmapData*>(data);
+ if (rData->texture)
+ rData->texture->Release();
+ rData->texture = 0;
+ }
+#endif
+ if (data->is_cached && qt_pixmap_cleanup_hook_64)
+ qt_pixmap_cleanup_hook_64(cacheKey());
+ delete data;
+ data = 0;
+ }
+}
+
+/*!
+ \fn QImage QPixmap::convertToImage() const
+
+ Use the toImage() function instead.
+*/
+
+/*!
+ \fn bool QPixmap::convertFromImage(const QImage &image, Qt::ImageConversionFlags flags)
+
+ Use the static fromImage() function instead.
+*/
+
+/*!
+ \fn QPixmap QPixmap::xForm(const QMatrix &matrix) const
+
+ Use transformed() instead.
+*/
+
+/*!
+ \fn QPixmap QPixmap::scaled(int width, int height,
+ Qt::AspectRatioMode aspectRatioMode, Qt::TransformationMode
+ transformMode) const
+
+ \overload
+
+ Returns a copy of the pixmap scaled to a rectangle with the given
+ \a width and \a height according to the given \a aspectRatioMode and
+ \a transformMode.
+
+ If either the \a width or the \a height is zero or negative, this
+ function returns a null pixmap.
+*/
+
+/*!
+ \fn QPixmap QPixmap::scaled(const QSize &size, Qt::AspectRatioMode
+ aspectRatioMode, Qt::TransformationMode transformMode) const
+
+ Scales the pixmap to the given \a size, using the aspect ratio and
+ transformation modes specified by \a aspectRatioMode and \a
+ transformMode.
+
+ \image qimage-scaling.png
+
+ \list
+ \i If \a aspectRatioMode is Qt::IgnoreAspectRatio, the pixmap
+ is scaled to \a size.
+ \i If \a aspectRatioMode is Qt::KeepAspectRatio, the pixmap is
+ scaled to a rectangle as large as possible inside \a size, preserving the aspect ratio.
+ \i If \a aspectRatioMode is Qt::KeepAspectRatioByExpanding,
+ the pixmap is scaled to a rectangle as small as possible
+ outside \a size, preserving the aspect ratio.
+ \endlist
+
+ If the given \a size is empty, this function returns a null
+ pixmap.
+
+ \sa isNull(), {QPixmap#Pixmap Transformations}{Pixmap
+ Transformations}
+
+*/
+QPixmap QPixmap::scaled(const QSize& s, Qt::AspectRatioMode aspectMode, Qt::TransformationMode mode) const
+{
+ if (isNull()) {
+ qWarning("QPixmap::scaled: Pixmap is a null pixmap");
+ return QPixmap();
+ }
+ if (s.isEmpty())
+ return QPixmap();
+
+ QSize newSize = size();
+ newSize.scale(s, aspectMode);
+ if (newSize == size())
+ return *this;
+
+ QPixmap pix;
+ QTransform wm;
+ wm.scale((qreal)newSize.width() / width(), (qreal)newSize.height() / height());
+ pix = transformed(wm, mode);
+ return pix;
+}
+
+/*!
+ \fn QPixmap QPixmap::scaledToWidth(int width, Qt::TransformationMode
+ mode) const
+
+ Returns a scaled copy of the image. The returned image is scaled
+ to the given \a width using the specified transformation \a mode.
+ The height of the pixmap is automatically calculated so that the
+ aspect ratio of the pixmap is preserved.
+
+ If \a width is 0 or negative, a null pixmap is returned.
+
+ \sa isNull(), {QPixmap#Pixmap Transformations}{Pixmap
+ Transformations}
+*/
+QPixmap QPixmap::scaledToWidth(int w, Qt::TransformationMode mode) const
+{
+ if (isNull()) {
+ qWarning("QPixmap::scaleWidth: Pixmap is a null pixmap");
+ return copy();
+ }
+ if (w <= 0)
+ return QPixmap();
+
+ QTransform wm;
+ qreal factor = (qreal) w / width();
+ wm.scale(factor, factor);
+ return transformed(wm, mode);
+}
+
+/*!
+ \fn QPixmap QPixmap::scaledToHeight(int height,
+ Qt::TransformationMode mode) const
+
+ Returns a scaled copy of the image. The returned image is scaled
+ to the given \a height using the specified transformation \a mode.
+ The width of the pixmap is automatically calculated so that the
+ aspect ratio of the pixmap is preserved.
+
+ If \a height is 0 or negative, a null pixmap is returned.
+
+ \sa isNull(), {QPixmap#Pixmap Transformations}{Pixmap
+ Transformations}
+*/
+QPixmap QPixmap::scaledToHeight(int h, Qt::TransformationMode mode) const
+{
+ if (isNull()) {
+ qWarning("QPixmap::scaleHeight: Pixmap is a null pixmap");
+ return copy();
+ }
+ if (h <= 0)
+ return QPixmap();
+
+ QTransform wm;
+ qreal factor = (qreal) h / height();
+ wm.scale(factor, factor);
+ return transformed(wm, mode);
+}
+
+/*!
+ Returns a copy of the pixmap that is transformed using the given
+ transformation \a transform and transformation \a mode. The original
+ pixmap is not changed.
+
+ The transformation \a transform is internally adjusted to compensate
+ for unwanted translation; i.e. the pixmap produced is the smallest
+ pixmap that contains all the transformed points of the original
+ pixmap. Use the trueMatrix() function to retrieve the actual
+ matrix used for transforming the pixmap.
+
+ This function is slow because it involves transformation to a
+ QImage, non-trivial computations and a transformation back to a
+ QPixmap.
+
+ \sa trueMatrix(), {QPixmap#Pixmap Transformations}{Pixmap
+ Transformations}
+*/
+QPixmap QPixmap::transformed(const QTransform &transform,
+ Qt::TransformationMode mode) const
+{
+ if (isNull() || transform.type() <= QTransform::TxTranslate)
+ return *this;
+
+ return data->transformed(transform, mode);
+}
+
+/*!
+ \overload
+
+ This convenience function loads the \a matrix into a
+ QTransform and calls the overloaded function.
+ */
+QPixmap QPixmap::transformed(const QMatrix &matrix, Qt::TransformationMode mode) const
+{
+ return transformed(QTransform(matrix), mode);
+}
+
+
+
+
+
+
+
+
+/*!
+ \class QPixmap
+
+ \brief The QPixmap class is an off-screen image representation
+ that can be used as a paint device.
+
+ \ingroup multimedia
+ \ingroup shared
+ \mainclass
+
+ Qt provides four classes for handling image data: QImage, QPixmap,
+ QBitmap and QPicture. QImage is designed and optimized for I/O,
+ and for direct pixel access and manipulation, while QPixmap is
+ designed and optimized for showing images on screen. QBitmap is
+ only a convenience class that inherits QPixmap, ensuring a depth
+ of 1. The isQBitmap() function returns true if a QPixmap object is
+ really a bitmap, otherwise returns false. Finally, the QPicture class is a
+ paint device that records and replays QPainter commands.
+
+ A QPixmap can easily be displayed on the screen using QLabel or
+ one of QAbstractButton's subclasses (such as QPushButton and
+ QToolButton). QLabel has a pixmap property, whereas
+ QAbstractButton has an icon property. And because QPixmap is a
+ QPaintDevice subclass, QPainter can be used to draw directly onto
+ pixmaps.
+
+ In addition to the ordinary constructors, a QPixmap can be
+ constructed using the static grabWidget() and grabWindow()
+ functions which creates a QPixmap and paints the given widget, or
+ window, in it.
+
+ Note that the pixel data in a pixmap is internal and is managed by
+ the underlying window system. Pixels can only be accessed through
+ QPainter functions or by converting the QPixmap to a QImage.
+ Depending on the system, QPixmap is stored using a RGB32 or a
+ premultiplied alpha format. If the image has an alpha channel, and
+ if the system allows, the preferred format is premultiplied alpha.
+ Note also that QPixmap, unlike QImage, may be hardware dependent.
+ On X11 and Mac, a QPixmap is stored on the server side while a
+ QImage is stored on the client side (on Windows, these two classes
+ have an equivalent internal representation, i.e. both QImage and
+ QPixmap are stored on the client side and don't use any GDI
+ resources).
+
+ There are functions to convert between QImage and
+ QPixmap. Typically, the QImage class is used to load an image
+ file, optionally manipulating the image data, before the QImage
+ object is converted into a QPixmap to be shown on
+ screen. Alternatively, if no manipulation is desired, the image
+ file can be loaded directly into a QPixmap. On Windows, the
+ QPixmap class also supports conversion between \c HBITMAP and
+ QPixmap.
+
+ QPixmap provides a collection of functions that can be used to
+ obtain a variety of information about the pixmap. In addition,
+ there are several functions that enables transformation of the
+ pixmap.
+
+ QPixmap objects can be passed around by value since the QPixmap
+ class uses implicit data sharing. For more information, see the \l
+ {Implicit Data Sharing} documentation. QPixmap objects can also be
+ streamed.
+
+ \tableofcontents
+
+ \section1 Reading and Writing Image Files
+
+ QPixmap provides several ways of reading an image file: The file
+ can be loaded when constructing the QPixmap object, or by using
+ the load() or loadFromData() functions later on. When loading an
+ image, the file name can either refer to an actual file on disk or
+ to one of the application's embedded resources. See \l{The Qt
+ Resource System} overview for details on how to embed images and
+ other resource files in the application's executable.
+
+ Simply call the save() function to save a QPixmap object.
+
+ The complete list of supported file formats are available through
+ the QImageReader::supportedImageFormats() and
+ QImageWriter::supportedImageFormats() functions. New file formats
+ can be added as plugins. By default, Qt supports the following
+ formats:
+
+ \table
+ \header \o Format \o Description \o Qt's support
+ \row \o BMP \o Windows Bitmap \o Read/write
+ \row \o GIF \o Graphic Interchange Format (optional) \o Read
+ \row \o JPG \o Joint Photographic Experts Group \o Read/write
+ \row \o JPEG \o Joint Photographic Experts Group \o Read/write
+ \row \o PNG \o Portable Network Graphics \o Read/write
+ \row \o PBM \o Portable Bitmap \o Read
+ \row \o PGM \o Portable Graymap \o Read
+ \row \o PPM \o Portable Pixmap \o Read/write
+ \row \o XBM \o X11 Bitmap \o Read/write
+ \row \o XPM \o X11 Pixmap \o Read/write
+ \endtable
+
+ \section1 Pixmap Information
+
+ QPixmap provides a collection of functions that can be used to
+ obtain a variety of information about the pixmap:
+
+ \table
+ \header
+ \o \o Available Functions
+ \row
+ \o Geometry
+ \o
+ The size(), width() and height() functions provide information
+ about the pixmap's size. The rect() function returns the image's
+ enclosing rectangle.
+
+ \row
+ \o Alpha component
+ \o
+
+ The hasAlphaChannel() returns true if the pixmap has a format that
+ respects the alpha channel, otherwise returns false, while the
+ hasAlpha() function returns true if the pixmap has an alpha
+ channel \e or a mask (otherwise false).
+
+ The alphaChannel() function returns the alpha channel as a new
+ QPixmap object, while the mask() function returns the mask as a
+ QBitmap object. The alpha channel and mask can be set using the
+ setAlphaChannel() and setMask() functions, respectively.
+
+ \row
+ \o Low-level information
+ \o
+
+ The depth() function returns the depth of the pixmap. The
+ defaultDepth() function returns the default depth, i.e. the depth
+ used by the application on the given screen.
+
+ The cacheKey() function returns a number that uniquely
+ identifies the contents of the QPixmap object.
+
+ The x11Info() function returns information about the configuration
+ of the X display used to display the widget. The
+ x11PictureHandle() function returns the X11 Picture handle of the
+ pixmap for XRender support. Note that the two latter functions are
+ only available on x11.
+
+ \endtable
+
+ \section1 Pixmap Conversion
+
+ A QPixmap object can be converted into a QImage using the
+ toImage() function. Likewise, a QImage can be converted into a
+ QPixmap using the fromImage(). If this is too expensive an
+ operation, you can use QBitmap::fromImage() instead.
+
+ In addition, on Windows, the QPixmap class supports conversion to
+ and from HBitmap: the toWinHBITMAP() function creates a HBITMAP
+ equivalent to the QPixmap, based on the given HBitmapFormat, and
+ returns the HBITMAP handle. The fromWinHBITMAP() function returns
+ a QPixmap that is equivalent to the given bitmap which has the
+ specified format.
+
+ \section1 Pixmap Transformations
+
+ QPixmap supports a number of functions for creating a new pixmap
+ that is a transformed version of the original: The
+ createHeuristicMask() function creates and returns a 1-bpp
+ heuristic mask (i.e. a QBitmap) for this pixmap. It works by
+ selecting a color from one of the corners and then chipping away
+ pixels of that color, starting at all the edges. The
+ createMaskFromColor() function creates and returns a mask (i.e. a
+ QBitmap) for the pixmap based on a given color.
+
+
+ The scaled(), scaledToWidth() and scaledToHeight() functions
+ return scaled copies of the pixmap, while the copy() function
+ creates a QPixmap that is a plain copy of the original one.
+
+ The transformed() function returns a copy of the pixmap that is
+ transformed with the given transformation matrix and
+ transformation mode: Internally, the transformation matrix is
+ adjusted to compensate for unwanted translation,
+ i.e. transformed() returns the smallest pixmap containing all
+ transformed points of the original pixmap. The static trueMatrix()
+ function returns the actual matrix used for transforming the
+ pixmap.
+
+ There are also functions for changing attributes of a pixmap.
+ in-place: The fill() function fills the entire image with the
+ given color, the setMask() function sets a mask bitmap, and the
+ setAlphaChannel() function sets the pixmap's alpha channel.
+
+ \sa QBitmap, QImage, QImageReader, QImageWriter
+*/
+
+
+/*!
+ \typedef QPixmap::DataPtr
+ \internal
+*/
+
+/*!
+ \fn DataPtr &QPixmap::data_ptr()
+ \internal
+*/
+
+/*!
+ Returns true if this pixmap has an alpha channel, \e or has a
+ mask, otherwise returns false.
+
+ \sa hasAlphaChannel(), alphaChannel(), mask()
+*/
+bool QPixmap::hasAlpha() const
+{
+ return (data->hasAlphaChannel() || !data->mask().isNull());
+}
+
+/*!
+ Returns true if the pixmap has a format that respects the alpha
+ channel, otherwise returns false.
+
+ \sa alphaChannel(), hasAlpha()
+*/
+bool QPixmap::hasAlphaChannel() const
+{
+ return data->hasAlphaChannel();
+}
+
+/*!
+ \reimp
+*/
+int QPixmap::metric(PaintDeviceMetric metric) const
+{
+ return data->metric(metric);
+}
+
+/*!
+ \fn void QPixmap::setAlphaChannel(const QPixmap &alphaChannel)
+
+ Sets the alpha channel of this pixmap to the given \a alphaChannel
+ by converting the \a alphaChannel into 32 bit and using the
+ intensity of the RGB pixel values.
+
+ The effect of this function is undefined when the pixmap is being
+ painted on.
+
+ \sa alphaChannel(), {QPixmap#Pixmap Transformations}{Pixmap
+ Transformations}
+ */
+void QPixmap::setAlphaChannel(const QPixmap &alphaChannel)
+{
+ if (alphaChannel.isNull())
+ return;
+
+ if (paintingActive()) {
+ qWarning("QPixmap::setAlphaChannel: "
+ "Cannot set alpha channel while pixmap is being painted on");
+ return;
+ }
+
+ if (width() != alphaChannel.width() && height() != alphaChannel.height()) {
+ qWarning("QPixmap::setAlphaChannel: "
+ "The pixmap and the alpha channel pixmap must have the same size");
+ return;
+ }
+
+ detach();
+ data->setAlphaChannel(alphaChannel);
+}
+
+/*!
+ Returns the alpha channel of the pixmap as a new grayscale QPixmap in which
+ each pixel's red, green, and blue values are given the alpha value of the
+ original pixmap. The color depth of the returned pixmap is the system depth
+ on X11 and 8-bit on Windows and Mac OS X.
+
+ You can use this function while debugging
+ to get a visible image of the alpha channel. If the pixmap doesn't have an
+ alpha channel, i.e., the alpha channel's value for all pixels equals
+ 0xff), a null pixmap is returned. You can check this with the \c isNull()
+ function.
+
+ We show an example:
+
+ \snippet doc/src/snippets/alphachannel.cpp 0
+
+ \image alphachannelimage.png The pixmap and channelImage QPixmaps
+
+ \sa setAlphaChannel(), {QPixmap#Pixmap Information}{Pixmap
+ Information}
+*/
+QPixmap QPixmap::alphaChannel() const
+{
+ return data->alphaChannel();
+}
+
+/*!
+ \reimp
+*/
+QPaintEngine *QPixmap::paintEngine() const
+{
+ return data->paintEngine();
+}
+
+/*!
+ \fn QBitmap QPixmap::mask() const
+
+ Extracts a bitmap mask from the pixmap's alphachannel.
+
+ This is potentially an expensive operation.
+
+ \sa setMask(), {QPixmap#Pixmap Information}{Pixmap Information}
+*/
+QBitmap QPixmap::mask() const
+{
+ return data->mask();
+}
+
+/*!
+ Returns the default pixmap depth used by the application.
+
+ On Windows and Mac, the default depth is always 32. On X11 and
+ embedded, the depth of the screen will be returned by this
+ function.
+
+ \sa depth(), QColormap::depth(), {QPixmap#Pixmap Information}{Pixmap Information}
+
+*/
+int QPixmap::defaultDepth()
+{
+#if defined(Q_WS_QWS)
+ return QScreen::instance()->depth();
+#elif defined(Q_WS_X11)
+ return QX11Info::appDepth();
+#elif defined(Q_OS_WINCE)
+ return QColormap::instance().depth();
+#elif defined(Q_WS_WIN)
+ return 32; // XXX
+#elif defined(Q_WS_MAC)
+ return 32;
+#endif
+}
+
+typedef void (*_qt_pixmap_cleanup_hook_64)(qint64);
+extern _qt_pixmap_cleanup_hook_64 qt_pixmap_cleanup_hook_64;
+
+/*!
+ Detaches the pixmap from shared pixmap data.
+
+ A pixmap is automatically detached by Qt whenever its contents are
+ about to change. This is done in almost all QPixmap member
+ functions that modify the pixmap (fill(), fromImage(),
+ load(), etc.), and in QPainter::begin() on a pixmap.
+
+ There are two exceptions in which detach() must be called
+ explicitly, that is when calling the handle() or the
+ x11PictureHandle() function (only available on X11). Otherwise,
+ any modifications done using system calls, will be performed on
+ the shared data.
+
+ The detach() function returns immediately if there is just a
+ single reference or if the pixmap has not been initialized yet.
+*/
+void QPixmap::detach()
+{
+ QPixmapData::ClassId id = data->classId();
+ if (id == QPixmapData::RasterClass) {
+ QRasterPixmapData *rasterData = static_cast<QRasterPixmapData*>(data);
+ rasterData->image.detach();
+#if defined(Q_WS_WIN) && !defined(QT_NO_DIRECT3D)
+ if (rasterData->texture) {
+ rasterData->texture->Release();
+ rasterData->texture = 0;
+ }
+#endif
+ }
+
+ if (data->is_cached && qt_pixmap_cleanup_hook_64 && data->ref == 1)
+ qt_pixmap_cleanup_hook_64(cacheKey());
+
+#if defined(Q_WS_MAC)
+ QMacPixmapData *macData = id == QPixmapData::MacClass ? static_cast<QMacPixmapData*>(data) : 0;
+ if (macData) {
+ if (macData->cg_mask) {
+ CGImageRelease(macData->cg_mask);
+ macData->cg_mask = 0;
+ }
+ }
+#endif
+
+ if (data->ref != 1) {
+ *this = copy();
+#if defined(Q_WS_MAC) && !defined(QT_MAC_NO_QUICKDRAW)
+ if (id == QPixmapData::MacClass) {
+ macData->qd_alpha = 0;
+ }
+#endif
+ }
+ ++data->detach_no;
+
+#if defined(Q_WS_X11)
+ if (data->classId() == QPixmapData::X11Class) {
+ QX11PixmapData *d = static_cast<QX11PixmapData*>(data);
+ d->uninit = false;
+
+ // reset the cache data
+ if (d->hd2) {
+ XFreePixmap(X11->display, d->hd2);
+ d->hd2 = 0;
+ }
+ }
+#elif defined(Q_WS_MAC)
+ if (macData) {
+ macData->macReleaseCGImageRef();
+ macData->uninit = false;
+ }
+#endif
+}
+
+/*!
+ \fn QPixmap QPixmap::fromImage(const QImage &image, Qt::ImageConversionFlags flags)
+
+ Converts the given \a image to a pixmap using the specified \a
+ flags to control the conversion. The \a flags argument is a
+ bitwise-OR of the \l{Qt::ImageConversionFlags}. Passing 0 for \a
+ flags sets all the default options.
+
+ In case of monochrome and 8-bit images, the image is first
+ converted to a 32-bit pixmap and then filled with the colors in
+ the color table. If this is too expensive an operation, you can
+ use QBitmap::fromImage() instead.
+
+ \sa toImage(), {QPixmap#Pixmap Conversion}{Pixmap Conversion}
+*/
+QPixmap QPixmap::fromImage(const QImage &image, Qt::ImageConversionFlags flags)
+{
+ if (image.isNull())
+ return QPixmap();
+
+ QPixmapData *data;
+ QGraphicsSystem* gs = QApplicationPrivate::graphicsSystem();
+ if (gs)
+ data = gs->createPixmapData(QPixmapData::PixmapType);
+ else
+ data = QGraphicsSystem::createDefaultPixmapData(QPixmapData::PixmapType);
+
+ data->fromImage(image, flags);
+ return QPixmap(data);
+}
+
+/*!
+ \fn QPixmap QPixmap::grabWindow(WId window, int x, int y, int
+ width, int height)
+
+ Creates and returns a pixmap constructed by grabbing the contents
+ of the given \a window restricted by QRect(\a x, \a y, \a width,
+ \a height).
+
+ The arguments (\a{x}, \a{y}) specify the offset in the window,
+ whereas (\a{width}, \a{height}) specify the area to be copied. If
+ \a width is negative, the function copies everything to the right
+ border of the window. If \a height is negative, the function
+ copies everything to the bottom of the window.
+
+ The window system identifier (\c WId) can be retrieved using the
+ QWidget::winId() function. The rationale for using a window
+ identifier and not a QWidget, is to enable grabbing of windows
+ that are not part of the application, window system frames, and so
+ on.
+
+ The grabWindow() function grabs pixels from the screen, not from
+ the window, i.e. if there is another window partially or entirely
+ over the one you grab, you get pixels from the overlying window,
+ too. The mouse cursor is generally not grabbed.
+
+ Note on X11that if the given \a window doesn't have the same depth
+ as the root window, and another window partially or entirely
+ obscures the one you grab, you will \e not get pixels from the
+ overlying window. The contents of the obscured areas in the
+ pixmap will be undefined and uninitialized.
+
+ \warning In general, grabbing an area outside the screen is not
+ safe. This depends on the underlying window system.
+
+ \sa grabWidget(), {Screenshot Example}
+*/
+
+/*!
+ \internal
+*/
+QPixmapData* QPixmap::pixmapData() const
+{
+ return data;
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/image/qpixmap.h b/src/gui/image/qpixmap.h
new file mode 100644
index 0000000000..18632738c8
--- /dev/null
+++ b/src/gui/image/qpixmap.h
@@ -0,0 +1,304 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QPIXMAP_H
+#define QPIXMAP_H
+
+#include <QtGui/qpaintdevice.h>
+#include <QtGui/qcolor.h>
+#include <QtCore/qnamespace.h>
+#include <QtCore/qstring.h> // char*->QString conversion
+#include <QtGui/qimage.h>
+#include <QtGui/qtransform.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+class QImageWriter;
+class QColor;
+class QVariant;
+class QX11Info;
+
+class QPixmapData;
+
+class Q_GUI_EXPORT QPixmap : public QPaintDevice
+{
+public:
+ QPixmap();
+ explicit QPixmap(QPixmapData *data);
+ QPixmap(int w, int h);
+ QPixmap(const QSize &);
+ QPixmap(const QString& fileName, const char *format = 0, Qt::ImageConversionFlags flags = Qt::AutoColor);
+#ifndef QT_NO_IMAGEFORMAT_XPM
+ QPixmap(const char * const xpm[]);
+#endif
+ QPixmap(const QPixmap &);
+ ~QPixmap();
+
+ QPixmap &operator=(const QPixmap &);
+ operator QVariant() const;
+
+ bool isNull() const;
+ int devType() const;
+
+ int width() const;
+ int height() const;
+ QSize size() const;
+ QRect rect() const;
+ int depth() const;
+
+ static int defaultDepth();
+
+ void fill(const QColor &fillColor = Qt::white);
+ void fill(const QWidget *widget, const QPoint &ofs);
+ inline void fill(const QWidget *widget, int xofs, int yofs) { fill(widget, QPoint(xofs, yofs)); }
+
+ QBitmap mask() const;
+ void setMask(const QBitmap &);
+
+ QPixmap alphaChannel() const;
+ void setAlphaChannel(const QPixmap &);
+
+ bool hasAlpha() const;
+ bool hasAlphaChannel() const;
+
+#ifndef QT_NO_IMAGE_HEURISTIC_MASK
+ QBitmap createHeuristicMask(bool clipTight = true) const;
+#endif
+ QBitmap createMaskFromColor(const QColor &maskColor) const; // ### Qt 5: remove
+ QBitmap createMaskFromColor(const QColor &maskColor, Qt::MaskMode mode) const;
+
+ static QPixmap grabWindow(WId, int x=0, int y=0, int w=-1, int h=-1);
+ static QPixmap grabWidget(QWidget *widget, const QRect &rect);
+ static inline QPixmap grabWidget(QWidget *widget, int x=0, int y=0, int w=-1, int h=-1)
+ { return grabWidget(widget, QRect(x, y, w, h)); }
+
+
+ inline QPixmap scaled(int w, int h, Qt::AspectRatioMode aspectMode = Qt::IgnoreAspectRatio,
+ Qt::TransformationMode mode = Qt::FastTransformation) const
+ { return scaled(QSize(w, h), aspectMode, mode); }
+ QPixmap scaled(const QSize &s, Qt::AspectRatioMode aspectMode = Qt::IgnoreAspectRatio,
+ Qt::TransformationMode mode = Qt::FastTransformation) const;
+ QPixmap scaledToWidth(int w, Qt::TransformationMode mode = Qt::FastTransformation) const;
+ QPixmap scaledToHeight(int h, Qt::TransformationMode mode = Qt::FastTransformation) const;
+ QPixmap transformed(const QMatrix &, Qt::TransformationMode mode = Qt::FastTransformation) const;
+ static QMatrix trueMatrix(const QMatrix &m, int w, int h);
+ QPixmap transformed(const QTransform &, Qt::TransformationMode mode = Qt::FastTransformation) const;
+ static QTransform trueMatrix(const QTransform &m, int w, int h);
+
+ QImage toImage() const;
+ static QPixmap fromImage(const QImage &image, Qt::ImageConversionFlags flags = Qt::AutoColor);
+
+ bool load(const QString& fileName, const char *format = 0, Qt::ImageConversionFlags flags = Qt::AutoColor);
+ bool loadFromData(const uchar *buf, uint len, const char* format = 0, Qt::ImageConversionFlags flags = Qt::AutoColor);
+ inline bool loadFromData(const QByteArray &data, const char* format = 0, Qt::ImageConversionFlags flags = Qt::AutoColor);
+ bool save(const QString& fileName, const char* format = 0, int quality = -1) const;
+ bool save(QIODevice* device, const char* format = 0, int quality = -1) const;
+
+#if defined(Q_WS_WIN)
+ enum HBitmapFormat {
+ NoAlpha,
+ PremultipliedAlpha,
+ Alpha
+ };
+
+ HBITMAP toWinHBITMAP(HBitmapFormat format = NoAlpha) const;
+ static QPixmap fromWinHBITMAP(HBITMAP hbitmap, HBitmapFormat format = NoAlpha);
+#endif
+
+#if defined(Q_WS_MAC)
+ CGImageRef toMacCGImageRef() const;
+ static QPixmap fromMacCGImageRef(CGImageRef image);
+#endif
+
+ inline QPixmap copy(int x, int y, int width, int height) const;
+ QPixmap copy(const QRect &rect = QRect()) const;
+
+ int serialNumber() const;
+ qint64 cacheKey() const;
+
+ bool isDetached() const;
+ void detach();
+
+ bool isQBitmap() const;
+
+#if defined(Q_WS_QWS)
+ const uchar *qwsBits() const;
+ int qwsBytesPerLine() const;
+ QRgb *clut() const;
+ int numCols() const;
+#elif defined(Q_WS_MAC)
+ Qt::HANDLE macQDHandle() const;
+ Qt::HANDLE macQDAlphaHandle() const;
+ Qt::HANDLE macCGHandle() const;
+#elif defined(Q_WS_X11)
+ enum ShareMode { ImplicitlyShared, ExplicitlyShared };
+
+ static QPixmap fromX11Pixmap(Qt::HANDLE pixmap, ShareMode mode = ImplicitlyShared);
+ static int x11SetDefaultScreen(int screen);
+ void x11SetScreen(int screen);
+ const QX11Info &x11Info() const;
+ Qt::HANDLE x11PictureHandle() const;
+#endif
+
+#if defined(Q_WS_X11) || defined(Q_WS_QWS)
+ Qt::HANDLE handle() const;
+#endif
+
+ QPaintEngine *paintEngine() const;
+
+ inline bool operator!() const { return isNull(); }
+
+protected:
+ int metric(PaintDeviceMetric) const;
+
+#ifdef QT3_SUPPORT
+public:
+ enum ColorMode { Auto, Color, Mono };
+ QT3_SUPPORT_CONSTRUCTOR QPixmap(const QString& fileName, const char *format, ColorMode mode);
+ QT3_SUPPORT bool load(const QString& fileName, const char *format, ColorMode mode);
+ QT3_SUPPORT bool loadFromData(const uchar *buf, uint len, const char* format, ColorMode mode);
+ QT3_SUPPORT_CONSTRUCTOR QPixmap(const QImage& image);
+ QT3_SUPPORT QPixmap &operator=(const QImage &);
+ inline QT3_SUPPORT QImage convertToImage() const { return toImage(); }
+ QT3_SUPPORT bool convertFromImage(const QImage &, ColorMode mode);
+ QT3_SUPPORT bool convertFromImage(const QImage &img, Qt::ImageConversionFlags flags = Qt::AutoColor)
+ { (*this) = fromImage(img, flags); return !isNull(); }
+ inline QT3_SUPPORT operator QImage() const { return toImage(); }
+ inline QT3_SUPPORT QPixmap xForm(const QMatrix &matrix) const { return transformed(QTransform(matrix)); }
+ inline QT3_SUPPORT bool selfMask() const { return false; }
+private:
+ void resize_helper(const QSize &s);
+public:
+ inline QT3_SUPPORT void resize(const QSize &s) { resize_helper(s); }
+ inline QT3_SUPPORT void resize(int width, int height) { resize_helper(QSize(width, height)); }
+#endif
+
+private:
+ QPixmapData *data;
+
+ bool doImageIO(QImageWriter *io, int quality) const;
+
+ // ### Qt5: remove the following three lines
+ enum Type { PixmapType, BitmapType }; // must match QPixmapData::PixelType
+ QPixmap(const QSize &s, Type);
+ void init(int, int, Type = PixmapType);
+
+ QPixmap(const QSize &s, int type);
+ void init(int, int, int);
+ void deref();
+#if defined(Q_WS_WIN)
+ void initAlphaPixmap(uchar *bytes, int length, struct tagBITMAPINFO *bmi);
+#endif
+ Q_DUMMY_COMPARISON_OPERATOR(QPixmap)
+#ifdef Q_WS_MAC
+ friend CGContextRef qt_mac_cg_context(const QPaintDevice*);
+ friend CGImageRef qt_mac_create_imagemask(const QPixmap&, const QRectF&);
+ friend IconRef qt_mac_create_iconref(const QPixmap&);
+ friend quint32 *qt_mac_pixmap_get_base(const QPixmap*);
+ friend int qt_mac_pixmap_get_bytes_per_line(const QPixmap*);
+#endif
+ friend class QPixmapData;
+ friend class QX11PixmapData;
+ friend class QMacPixmapData;
+ friend class QBitmap;
+ friend class QPaintDevice;
+ friend class QPainter;
+ friend class QGLWidget;
+ friend class QX11PaintEngine;
+ friend class QCoreGraphicsPaintEngine;
+ friend class QWidgetPrivate;
+ friend class QRasterPaintEngine;
+ friend class QRasterBuffer;
+ friend class QDirect3DPaintEngine;
+ friend class QDirect3DPaintEnginePrivate;
+ friend class QDetachedPixmap;
+#if !defined(QT_NO_DATASTREAM)
+ friend Q_GUI_EXPORT QDataStream &operator>>(QDataStream &, QPixmap &);
+#endif
+ friend Q_GUI_EXPORT qint64 qt_pixmap_id(const QPixmap &pixmap);
+
+public:
+ QPixmapData* pixmapData() const;
+
+public:
+ typedef QPixmapData * DataPtr;
+ inline DataPtr &data_ptr() { return data; }
+};
+
+Q_DECLARE_SHARED(QPixmap)
+
+inline QPixmap QPixmap::copy(int ax, int ay, int awidth, int aheight) const
+{
+ return copy(QRect(ax, ay, awidth, aheight));
+}
+
+inline bool QPixmap::loadFromData(const QByteArray &buf, const char *format,
+ Qt::ImageConversionFlags flags)
+{
+ return loadFromData(reinterpret_cast<const uchar *>(buf.constData()), buf.size(), format, flags);
+}
+
+/*****************************************************************************
+ QPixmap stream functions
+*****************************************************************************/
+
+#if !defined(QT_NO_DATASTREAM)
+Q_GUI_EXPORT QDataStream &operator<<(QDataStream &, const QPixmap &);
+Q_GUI_EXPORT QDataStream &operator>>(QDataStream &, QPixmap &);
+#endif
+
+/*****************************************************************************
+ QPixmap (and QImage) helper functions
+*****************************************************************************/
+#ifdef QT3_SUPPORT
+QT3_SUPPORT Q_GUI_EXPORT void copyBlt(QPixmap *dst, int dx, int dy, const QPixmap *src,
+ int sx=0, int sy=0, int sw=-1, int sh=-1);
+#endif // QT3_SUPPORT
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QPIXMAP_H
diff --git a/src/gui/image/qpixmap_mac.cpp b/src/gui/image/qpixmap_mac.cpp
new file mode 100644
index 0000000000..bfc605b591
--- /dev/null
+++ b/src/gui/image/qpixmap_mac.cpp
@@ -0,0 +1,1331 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+//#define QT_RASTER_PAINTENGINE
+
+#include "qpixmap.h"
+#include "qimage.h"
+#include "qapplication.h"
+#include "qbitmap.h"
+#include "qmatrix.h"
+#include "qtransform.h"
+#include "qlibrary.h"
+#include "qvarlengtharray.h"
+#include "qdebug.h"
+#include <private/qdrawhelper_p.h>
+#include <private/qpixmap_mac_p.h>
+#include <private/qpixmap_raster_p.h>
+#ifdef QT_RASTER_PAINTENGINE
+# include <private/qpaintengine_raster_p.h>
+#endif
+#include <private/qpaintengine_mac_p.h>
+#include <private/qt_mac_p.h>
+#include <private/qt_cocoa_helpers_mac_p.h>
+
+#include <limits.h>
+#include <string.h>
+
+QT_BEGIN_NAMESPACE
+
+/*****************************************************************************
+ Externals
+ *****************************************************************************/
+extern const uchar *qt_get_bitflip_array(); //qimage.cpp
+extern CGContextRef qt_mac_cg_context(const QPaintDevice *pdev); //qpaintdevice_mac.cpp
+extern RgnHandle qt_mac_get_rgn(); //qregion_mac.cpp
+extern void qt_mac_dispose_rgn(RgnHandle r); //qregion_mac.cpp
+extern QRegion qt_mac_convert_mac_region(RgnHandle rgn); //qregion_mac.cpp
+
+static int qt_pixmap_serial = 0;
+
+Q_GUI_EXPORT quint32 *qt_mac_pixmap_get_base(const QPixmap *pix)
+{
+ return static_cast<QMacPixmapData*>(pix->data)->pixels;
+}
+
+Q_GUI_EXPORT int qt_mac_pixmap_get_bytes_per_line(const QPixmap *pix)
+{
+ return static_cast<QMacPixmapData*>(pix->data)->bytesPerRow;
+}
+
+void qt_mac_cgimage_data_free(void *info, const void *memoryToFree, size_t)
+{
+ QMacPixmapData *pmdata = static_cast<QMacPixmapData *>(info);
+ if (!pmdata) {
+ free(const_cast<void *>(memoryToFree));
+ } else {
+ if (QMacPixmapData::validDataPointers.contains(pmdata) == false) {
+ free(const_cast<void *>(memoryToFree));
+ return;
+ }
+ if (pmdata->pixels == pmdata->pixelsToFree) {
+ // something we aren't expecting, just free it.
+ Q_ASSERT(memoryToFree != pmdata->pixelsToFree);
+ free(const_cast<void *>(memoryToFree));
+ } else {
+ free(pmdata->pixelsToFree);
+ pmdata->pixelsToFree = static_cast<quint32 *>(const_cast<void *>(memoryToFree));
+ }
+ pmdata->cg_dataBeingReleased = 0;
+ }
+}
+
+CGImageRef qt_mac_image_to_cgimage(const QImage &image)
+{
+ int bitsPerColor = 8;
+ int bitsPerPixel = 32;
+ if (image.depth() == 1) {
+ bitsPerColor = 1;
+ bitsPerPixel = 1;
+ }
+ QCFType<CGDataProviderRef> provider =
+ CGDataProviderCreateWithData(0, image.bits(), image.bytesPerLine() * image.height(),
+ 0);
+
+#if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4)
+ uint cgflags = kCGImageAlphaPremultipliedFirst;
+#ifdef kCGBitmapByteOrder32Host //only needed because CGImage.h added symbols in the minor version
+ if(QSysInfo::MacintoshVersion >= QSysInfo::MV_10_4)
+ cgflags |= kCGBitmapByteOrder32Host;
+#endif
+#else
+ CGImageAlphaInfo cgflags = kCGImageAlphaPremultipliedFirst;
+#endif
+
+ CGImageRef cgImage = CGImageCreate(image.width(), image.height(), bitsPerColor, bitsPerPixel,
+ image.bytesPerLine(),
+ QCoreGraphicsPaintEngine::macGenericColorSpace(),
+ cgflags, provider,
+ 0,
+ 0,
+ kCGRenderingIntentDefault);
+
+ return cgImage;
+}
+
+/*****************************************************************************
+ QPixmap member functions
+ *****************************************************************************/
+
+static inline QRgb qt_conv16ToRgb(ushort c) {
+ static const int qt_rbits = (565/100);
+ static const int qt_gbits = (565/10%10);
+ static const int qt_bbits = (565%10);
+ static const int qt_red_shift = qt_bbits+qt_gbits-(8-qt_rbits);
+ static const int qt_green_shift = qt_bbits-(8-qt_gbits);
+ static const int qt_neg_blue_shift = 8-qt_bbits;
+ static const int qt_blue_mask = (1<<qt_bbits)-1;
+ static const int qt_green_mask = (1<<(qt_gbits+qt_bbits))-((1<<qt_bbits)-1);
+ static const int qt_red_mask = (1<<(qt_rbits+qt_gbits+qt_bbits))-(1<<(qt_gbits+qt_bbits));
+
+ const int r=(c & qt_red_mask);
+ const int g=(c & qt_green_mask);
+ const int b=(c & qt_blue_mask);
+ const int tr = r >> qt_red_shift;
+ const int tg = g >> qt_green_shift;
+ const int tb = b << qt_neg_blue_shift;
+
+ return qRgb(tr,tg,tb);
+}
+
+QSet<QMacPixmapData*> QMacPixmapData::validDataPointers;
+
+QMacPixmapData::QMacPixmapData(PixelType type)
+ : QPixmapData(type, MacClass), w(0), h(0), d(0), has_alpha(0), has_mask(0),
+ uninit(true), pixels(0), pixelsToFree(0), bytesPerRow(0),
+ cg_data(0), cg_dataBeingReleased(0), cg_mask(0),
+#ifndef QT_MAC_NO_QUICKDRAW
+ qd_data(0), qd_alpha(0),
+#endif
+ pengine(0)
+{
+}
+
+#define BEST_BYTE_ALIGNMENT 16
+#define COMPTUE_BEST_BYTES_PER_ROW(bpr) \
+ (((bpr) + (BEST_BYTE_ALIGNMENT - 1)) & ~(BEST_BYTE_ALIGNMENT - 1))
+
+void QMacPixmapData::resize(int width, int height)
+{
+ setSerialNumber(++qt_pixmap_serial);
+
+ w = width;
+ h = height;
+ d = (pixelType() == BitmapType ? 1 : 32);
+ bool make_null = w <= 0 || h <= 0; // create null pixmap
+ if (make_null || d == 0) {
+ w = 0;
+ h = 0;
+ d = 0;
+ if (!make_null)
+ qWarning("Qt: QPixmap: Invalid pixmap parameters");
+ return;
+ }
+
+ if (w < 1 || h < 1)
+ return;
+
+ //create the pixels
+ bytesPerRow = w * sizeof(quint32); // Minimum bytes per row.
+
+ // Quartz2D likes things as a multple of 16 (for now).
+ bytesPerRow = COMPTUE_BEST_BYTES_PER_ROW(bytesPerRow);
+ macCreatePixels();
+}
+
+#undef COMPUTE_BEST_BYTES_PER_ROW
+
+void QMacPixmapData::fromImage(const QImage &img,
+ Qt::ImageConversionFlags flags)
+{
+ setSerialNumber(++qt_pixmap_serial);
+
+ // the conversion code only handles format >=
+ // Format_ARGB32_Premultiplied at the moment..
+ if (img.format() > QImage::Format_ARGB32_Premultiplied) {
+ QImage image;
+ if (img.hasAlphaChannel())
+ image = img.convertToFormat(QImage::Format_ARGB32_Premultiplied);
+ else
+ image = img.convertToFormat(QImage::Format_RGB32);
+ fromImage(image, flags);
+ return;
+ }
+
+ w = img.width();
+ h = img.height();
+ d = (pixelType() == BitmapType ? 1 : img.depth());
+
+ QImage image = img;
+ int dd = QPixmap::defaultDepth();
+ bool force_mono = (dd == 1 ||
+ (flags & Qt::ColorMode_Mask)==Qt::MonoOnly);
+ if (force_mono) { // must be monochrome
+ if (d != 1) {
+ image = image.convertToFormat(QImage::Format_MonoLSB, flags); // dither
+ d = 1;
+ }
+ } else { // can be both
+ bool conv8 = false;
+ if(d > 8 && dd <= 8) { // convert to 8 bit
+ if ((flags & Qt::DitherMode_Mask) == Qt::AutoDither)
+ flags = (flags & ~Qt::DitherMode_Mask)
+ | Qt::PreferDither;
+ conv8 = true;
+ } else if ((flags & Qt::ColorMode_Mask) == Qt::ColorOnly) {
+ conv8 = d == 1; // native depth wanted
+ } else if (d == 1) {
+ if (image.numColors() == 2) {
+ QRgb c0 = image.color(0); // Auto: convert to best
+ QRgb c1 = image.color(1);
+ conv8 = qMin(c0,c1) != qRgb(0,0,0) || qMax(c0,c1) != qRgb(255,255,255);
+ } else {
+ // eg. 1-color monochrome images (they do exist).
+ conv8 = true;
+ }
+ }
+ if (conv8) {
+ image = image.convertToFormat(QImage::Format_Indexed8, flags);
+ d = 8;
+ }
+ }
+
+ if (image.depth()==1) {
+ image.setColor(0, QColor(Qt::color0).rgba());
+ image.setColor(1, QColor(Qt::color1).rgba());
+ }
+
+ if (d == 16 || d == 24) {
+ image = image.convertToFormat(QImage::Format_RGB32, flags);
+ fromImage(image, flags);
+ return;
+ }
+
+ // different size or depth, make a new pixmap
+ resize(w, h);
+
+ quint32 *dptr = pixels, *drow;
+ const uint dbpr = bytesPerRow;
+
+ const QImage::Format sfmt = image.format();
+ const unsigned short sbpr = image.bytesPerLine();
+
+ // use const_cast to prevent a detach
+ const uchar *sptr = const_cast<const QImage &>(image).bits(), *srow;
+
+ for (int y = 0; y < h; ++y) {
+ drow = dptr + (y * (dbpr / 4));
+ srow = sptr + (y * sbpr);
+ switch(sfmt) {
+ case QImage::Format_MonoLSB:
+ case QImage::Format_Mono:{
+ for (int x = 0; x < w; ++x) {
+ char one_bit = *(srow + (x / 8));
+ if (sfmt == QImage::Format_Mono)
+ one_bit = one_bit >> (7 - (x % 8));
+ else
+ one_bit = one_bit >> (x % 8);
+ if ((one_bit & 0x01))
+ *(drow+x) = 0x00000000;
+ else
+ *(drow+x) = 0xFFFFFFFF;
+ }
+ break;
+ }
+ case QImage::Format_Indexed8:
+ for (int x = 0; x < w; ++x) {
+ *(drow+x) = PREMUL(image.color(*(srow + x)));
+ }
+ break;
+ case QImage::Format_RGB32:
+ for (int x = 0; x < w; ++x)
+ *(drow+x) = *(((quint32*)srow) + x) | 0xFF000000;
+ break;
+ case QImage::Format_ARGB32:
+ case QImage::Format_ARGB32_Premultiplied:
+ for (int x = 0; x < w; ++x) {
+ if(sfmt == QImage::Format_RGB32)
+ *(drow+x) = 0xFF000000 | (*(((quint32*)srow) + x) & 0x00FFFFFF);
+ else if(sfmt == QImage::Format_ARGB32_Premultiplied)
+ *(drow+x) = *(((quint32*)srow) + x);
+ else
+ *(drow+x) = PREMUL(*(((quint32*)srow) + x));
+ }
+ break;
+ default:
+ qWarning("Qt: internal: Oops: Forgot a format [%d] %s:%d", sfmt,
+ __FILE__, __LINE__);
+ break;
+ }
+ }
+ if (sfmt != QImage::Format_RGB32) { //setup the alpha
+ bool alphamap = image.depth() == 32;
+ if (sfmt == QImage::Format_Indexed8) {
+ const QVector<QRgb> rgb = image.colorTable();
+ for (int i = 0, count = image.numColors(); i < count; ++i) {
+ const int alpha = qAlpha(rgb[i]);
+ if (alpha != 0xff) {
+ alphamap = true;
+ break;
+ }
+ }
+ }
+ macSetHasAlpha(alphamap);
+ }
+ uninit = false;
+}
+
+int get_index(QImage * qi,QRgb mycol)
+{
+ int loopc;
+ for(loopc=0;loopc<qi->numColors();loopc++) {
+ if(qi->color(loopc)==mycol)
+ return loopc;
+ }
+ qi->setNumColors(qi->numColors()+1);
+ qi->setColor(qi->numColors(),mycol);
+ return qi->numColors();
+}
+
+QImage QMacPixmapData::toImage() const
+{
+ QImage::Format format = QImage::Format_MonoLSB;
+ if (d != 1) //Doesn't support index color modes
+ format = (has_alpha ? QImage::Format_ARGB32_Premultiplied :
+ QImage::Format_RGB32);
+
+ QImage image(w, h, format);
+ quint32 *sptr = pixels, *srow;
+ const uint sbpr = bytesPerRow;
+ if (format == QImage::Format_MonoLSB) {
+ image.fill(0);
+ image.setNumColors(2);
+ image.setColor(0, QColor(Qt::color0).rgba());
+ image.setColor(1, QColor(Qt::color1).rgba());
+ for (int y = 0; y < h; ++y) {
+ uchar *scanLine = image.scanLine(y);
+ srow = sptr + (y * (sbpr/4));
+ for (int x = 0; x < w; ++x) {
+ if (!(*(srow + x) & RGB_MASK))
+ scanLine[x >> 3] |= (1 << (x & 7));
+ }
+ }
+ } else {
+ for (int y = 0; y < h; ++y) {
+ srow = sptr + (y * (sbpr / 4));
+ memcpy(image.scanLine(y), srow, w * 4);
+ }
+
+ }
+
+ return image;
+}
+
+void QMacPixmapData::fill(const QColor &fillColor)
+
+{
+ { //we don't know what backend to use so we cannot paint here
+ quint32 *dptr = pixels;
+ Q_ASSERT_X(dptr, "QPixmap::fill", "No dptr");
+ const quint32 colr = PREMUL(fillColor.rgba());
+ const int nbytes = bytesPerRow * h;
+ if (!colr) {
+ memset(dptr, 0, nbytes);
+ } else {
+ for (uint i = 0; i < nbytes / sizeof(quint32); ++i)
+ *(dptr + i) = colr;
+ }
+ }
+ macSetHasAlpha(fillColor.alpha() != 255);
+}
+
+QPixmap QMacPixmapData::alphaChannel() const
+{
+ if (!has_alpha)
+ return QPixmap();
+
+ QMacPixmapData *alpha = new QMacPixmapData(PixmapType);
+ alpha->resize(w, h);
+ macGetAlphaChannel(alpha, false);
+ return QPixmap(alpha);
+}
+
+void QMacPixmapData::setAlphaChannel(const QPixmap &alpha)
+{
+ has_mask = true;
+ QMacPixmapData *alphaData = static_cast<QMacPixmapData*>(alpha.data);
+ macSetAlphaChannel(alphaData, false);
+}
+
+QBitmap QMacPixmapData::mask() const
+{
+ if (!has_mask && !has_alpha)
+ return QBitmap();
+
+ QMacPixmapData *mask = new QMacPixmapData(BitmapType);
+ mask->resize(w, h);
+ macGetAlphaChannel(mask, true);
+ return QPixmap(mask);
+}
+
+void QMacPixmapData::setMask(const QBitmap &mask)
+{
+ if (mask.isNull()) {
+ QMacPixmapData opaque(PixmapType);
+ opaque.resize(w, h);
+ opaque.fill(QColor(255, 255, 255, 255));
+ macSetAlphaChannel(&opaque, true);
+ has_alpha = has_mask = false;
+ return;
+ }
+
+ has_alpha = false;
+ has_mask = true;
+ QMacPixmapData *maskData = static_cast<QMacPixmapData*>(mask.data);
+ macSetAlphaChannel(maskData, true);
+}
+
+int QMacPixmapData::metric(QPaintDevice::PaintDeviceMetric theMetric) const
+{
+ switch (theMetric) {
+ case QPaintDevice::PdmWidth:
+ return w;
+ case QPaintDevice::PdmHeight:
+ return h;
+ case QPaintDevice::PdmWidthMM:
+ return qRound(metric(QPaintDevice::PdmWidth) * 25.4 / qreal(metric(QPaintDevice::PdmDpiX)));
+ case QPaintDevice::PdmHeightMM:
+ return qRound(metric(QPaintDevice::PdmHeight) * 25.4 / qreal(metric(QPaintDevice::PdmDpiY)));
+ case QPaintDevice::PdmNumColors:
+ return 1 << d;
+ case QPaintDevice::PdmDpiX:
+ case QPaintDevice::PdmPhysicalDpiX: {
+ extern float qt_mac_defaultDpi_x(); //qpaintdevice_mac.cpp
+ return int(qt_mac_defaultDpi_x());
+ }
+ case QPaintDevice::PdmDpiY:
+ case QPaintDevice::PdmPhysicalDpiY: {
+ extern float qt_mac_defaultDpi_y(); //qpaintdevice_mac.cpp
+ return int(qt_mac_defaultDpi_y());
+ }
+ case QPaintDevice::PdmDepth:
+ return d;
+ default:
+ qWarning("QPixmap::metric: Invalid metric command");
+ }
+ return 0;
+}
+
+QMacPixmapData::~QMacPixmapData()
+{
+ validDataPointers.remove(this);
+#ifndef QT_MAC_NO_QUICKDRAW
+ macQDDisposeAlpha();
+ if (qd_data) {
+ DisposeGWorld(qd_data);
+ qd_data = 0;
+ }
+#endif
+ if (cg_mask) {
+ CGImageRelease(cg_mask);
+ cg_mask = 0;
+ }
+
+ delete pengine; // Make sure we aren't drawing on the context anymore.
+ if (cg_data) {
+ CGImageRelease(cg_data);
+ } else if (!cg_dataBeingReleased && pixels != pixelsToFree) {
+ free(pixels);
+ }
+ free(pixelsToFree);
+}
+
+void QMacPixmapData::macSetAlphaChannel(const QMacPixmapData *pix, bool asMask)
+{
+ if (!pixels || !h || !w || pix->w != w || pix->h != h)
+ return;
+
+ quint32 *dptr = pixels, *drow;
+ const uint dbpr = bytesPerRow;
+ const unsigned short sbpr = pix->bytesPerRow;
+ quint32 *sptr = pix->pixels, *srow;
+ for (int y=0; y < h; ++y) {
+ drow = dptr + (y * (dbpr/4));
+ srow = sptr + (y * (sbpr/4));
+ if(d == 1) {
+ for (int x=0; x < w; ++x) {
+ if((*(srow+x) & RGB_MASK))
+ *(drow+x) = 0xFFFFFFFF;
+ }
+ } else if(d == 8) {
+ for (int x=0; x < w; ++x)
+ *(drow+x) = (*(drow+x) & RGB_MASK) | (*(srow+x) << 24);
+ } else if(asMask) {
+ for (int x=0; x < w; ++x) {
+ if(*(srow+x) & RGB_MASK)
+ *(drow+x) = (*(drow+x) & RGB_MASK);
+ else
+ *(drow+x) = (*(drow+x) & RGB_MASK) | 0xFF000000;
+ *(drow+x) = PREMUL(*(drow+x));
+ }
+ } else {
+ for (int x=0; x < w; ++x) {
+ const uchar alpha = qGray(qRed(*(srow+x)), qGreen(*(srow+x)), qBlue(*(srow+x)));
+ const uchar destAlpha = qt_div_255(alpha * qAlpha(*(drow+x)));
+#if 1
+ *(drow+x) = (*(drow+x) & RGB_MASK) | (destAlpha << 24);
+#else
+ *(drow+x) = qRgba(qt_div_255(qRed(*(drow+x) * alpha)),
+ qt_div_255(qGreen(*(drow+x) * alpha)),
+ qt_div_255(qBlue(*(drow+x) * alpha)), destAlpha);
+#endif
+ *(drow+x) = PREMUL(*(drow+x));
+ }
+ }
+ }
+ macSetHasAlpha(true);
+}
+
+void QMacPixmapData::macGetAlphaChannel(QMacPixmapData *pix, bool asMask) const
+{
+ quint32 *dptr = pix->pixels, *drow;
+ const uint dbpr = pix->bytesPerRow;
+ const unsigned short sbpr = bytesPerRow;
+ quint32 *sptr = pixels, *srow;
+ for(int y=0; y < h; ++y) {
+ drow = dptr + (y * (dbpr/4));
+ srow = sptr + (y * (sbpr/4));
+ if(asMask) {
+ for (int x = 0; x < w; ++x) {
+ if (*(srow + x) & qRgba(0, 0, 0, 255))
+ *(drow + x) = 0x00000000;
+ else
+ *(drow + x) = 0xFFFFFFFF;
+ }
+ } else {
+ for (int x = 0; x < w; ++x) {
+ const int alpha = qAlpha(*(srow + x));
+ *(drow + x) = qRgb(alpha, alpha, alpha);
+ }
+ }
+ }
+}
+
+void QMacPixmapData::macSetHasAlpha(bool b)
+{
+ has_alpha = b;
+#ifndef QT_MAC_NO_QUICKDRAW
+ macQDDisposeAlpha(); //let it get created lazily
+#endif
+ macReleaseCGImageRef();
+}
+
+#ifndef QT_MAC_NO_QUICKDRAW
+void QMacPixmapData::macQDDisposeAlpha()
+{
+ if (qd_alpha) {
+ DisposeGWorld(qd_alpha);
+ qd_alpha = 0;
+ }
+}
+
+void QMacPixmapData::macQDUpdateAlpha()
+{
+ macQDDisposeAlpha(); // get rid of alpha pixmap
+ if (!has_alpha && !has_mask)
+ return;
+
+ //setup
+ Rect rect;
+ SetRect(&rect, 0, 0, w, h);
+ const int params = alignPix | stretchPix | newDepth;
+ NewGWorld(&qd_alpha, 32, &rect, 0, 0, params);
+ int *dptr = (int *)GetPixBaseAddr(GetGWorldPixMap(qd_alpha)), *drow;
+ unsigned short dbpr = GetPixRowBytes(GetGWorldPixMap(qd_alpha));
+ const int *sptr = (int*)pixels, *srow;
+ const uint sbpr = bytesPerRow;
+ uchar clr;
+ for (int y = 0; y < h; ++y) {
+ drow = (int*)((char *)dptr + (y * dbpr));
+ srow = (int*)((char *)sptr + (y * sbpr));
+ for (int x=0; x < w; x++) {
+ clr = qAlpha(*(srow + x));
+ *(drow + x) = qRgba(~clr, ~clr, ~clr, 0);
+ }
+ }
+}
+#endif
+
+void QMacPixmapData::macCreateCGImageRef()
+{
+ Q_ASSERT(cg_data == 0);
+ //create the cg data
+ CGColorSpaceRef colorspace = QCoreGraphicsPaintEngine::macDisplayColorSpace();
+ QCFType<CGDataProviderRef> provider = CGDataProviderCreateWithData(this,
+ pixels, bytesPerRow * h,
+ qt_mac_cgimage_data_free);
+ validDataPointers.insert(this);
+#if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4)
+ uint cgflags = kCGImageAlphaPremultipliedFirst;
+#ifdef kCGBitmapByteOrder32Host //only needed because CGImage.h added symbols in the minor version
+ if(QSysInfo::MacintoshVersion >= QSysInfo::MV_10_4)
+ cgflags |= kCGBitmapByteOrder32Host;
+#endif
+#else
+ CGImageAlphaInfo cgflags = kCGImageAlphaPremultipliedFirst;
+#endif
+ cg_data = CGImageCreate(w, h, 8, 32, bytesPerRow, colorspace,
+ cgflags, provider, 0, 0, kCGRenderingIntentDefault);
+}
+
+void QMacPixmapData::macReleaseCGImageRef()
+{
+ if (!cg_data)
+ return; // There's nothing we need to do
+
+ cg_dataBeingReleased = cg_data;
+ CGImageRelease(cg_data);
+ cg_data = 0;
+
+ if (pixels != pixelsToFree) {
+ macCreatePixels();
+ } else {
+ pixelsToFree = 0;
+ }
+}
+
+
+// We create our space in memory to paint on here. If we already have existing pixels
+// copy them over. This is to preserve the fact that CGImageRef's are immutable.
+void QMacPixmapData::macCreatePixels()
+{
+ const int numBytes = bytesPerRow * h;
+ quint32 *base_pixels;
+ if (pixelsToFree && pixelsToFree != pixels) {
+ // Reuse unused block of memory lying around from a previous callback.
+ base_pixels = pixelsToFree;
+ pixelsToFree = 0;
+ } else {
+ // We need a block of memory to do stuff with.
+ base_pixels = static_cast<quint32 *>(malloc(numBytes));
+ }
+
+ if (pixels)
+ memcpy(base_pixels, pixels, numBytes);
+ pixels = base_pixels;
+}
+
+#if 0
+QPixmap QMacPixmapData::transformed(const QTransform &transform,
+ Qt::TransformationMode mode) const
+{
+ int w, h; // size of target pixmap
+ const int ws = width();
+ const int hs = height();
+
+ QTransform mat(transform.m11(), transform.m12(),
+ transform.m21(), transform.m22(), 0., 0.);
+ if (transform.m12() == 0.0F && transform.m21() == 0.0F &&
+ transform.m11() >= 0.0F && transform.m22() >= 0.0F)
+ {
+ h = int(qAbs(mat.m22()) * hs + 0.9999);
+ w = int(qAbs(mat.m11()) * ws + 0.9999);
+ h = qAbs(h);
+ w = qAbs(w);
+ } else { // rotation or shearing
+ QPolygonF a(QRectF(0,0,ws+1,hs+1));
+ a = mat.map(a);
+ QRectF r = a.boundingRect().normalized();
+ w = int(r.width() + 0.9999);
+ h = int(r.height() + 0.9999);
+ }
+ mat = QPixmap::trueMatrix(mat, ws, hs);
+ if (!h || !w)
+ return QPixmap();
+
+ // create destination
+ QMacPixmapData *pm = new QMacPixmapData(pixelType(), w, h);
+ const quint32 *sptr = pixels;
+ quint32 *dptr = pm->pixels;
+ memset(dptr, 0, (pm->bytesPerRow * pm->h));
+
+ // do the transform
+ if (mode == Qt::SmoothTransformation) {
+#warning QMacPixmapData::transformed not properly implemented
+ qWarning("QMacPixmapData::transformed not properly implemented");
+#if 0
+ QPainter p(&pm);
+ p.setRenderHint(QPainter::Antialiasing);
+ p.setRenderHint(QPainter::SmoothPixmapTransform);
+ p.setTransform(mat);
+ p.drawPixmap(0, 0, *this);
+#endif
+ } else {
+ bool invertible;
+ mat = mat.inverted(&invertible);
+ if (!invertible)
+ return QPixmap();
+
+ const int bpp = 32;
+ const int xbpl = (w * bpp) / 8;
+ if (!qt_xForm_helper(mat, 0, QT_XFORM_TYPE_MSBFIRST, bpp,
+ (uchar*)dptr, xbpl, (pm->bytesPerRow) - xbpl,
+ h, (uchar*)sptr, (bytesPerRow), ws, hs)) {
+ qWarning("QMacPixmapData::transform(): failure");
+ return QPixmap();
+ }
+ }
+
+ // update the alpha
+ pm->macSetHasAlpha(true);
+ return QPixmap(pm);
+}
+#endif
+
+QT_BEGIN_INCLUDE_NAMESPACE
+#include <OpenGL/OpenGL.h>
+#include <OpenGL/gl.h>
+QT_END_INCLUDE_NAMESPACE
+
+// Load and resolve the symbols we need from OpenGL manually so QtGui doesn't have to link against the OpenGL framework.
+typedef CGLError (*PtrCGLChoosePixelFormat)(const CGLPixelFormatAttribute *, CGLPixelFormatObj *, long *);
+typedef CGLError (*PtrCGLClearDrawable)(CGLContextObj);
+typedef CGLError (*PtrCGLCreateContext)(CGLPixelFormatObj, CGLContextObj, CGLContextObj *);
+typedef CGLError (*PtrCGLDestroyContext)(CGLContextObj);
+typedef CGLError (*PtrCGLDestroyPixelFormat)(CGLPixelFormatObj);
+typedef CGLError (*PtrCGLSetCurrentContext)(CGLContextObj);
+typedef CGLError (*PtrCGLSetFullScreen)(CGLContextObj);
+typedef void (*PtrglFinish)();
+typedef void (*PtrglPixelStorei)(GLenum, GLint);
+typedef void (*PtrglReadBuffer)(GLenum);
+typedef void (*PtrglReadPixels)(GLint, GLint, GLsizei, GLsizei, GLenum, GLenum, GLvoid *);
+
+static PtrCGLChoosePixelFormat ptrCGLChoosePixelFormat = 0;
+static PtrCGLClearDrawable ptrCGLClearDrawable = 0;
+static PtrCGLCreateContext ptrCGLCreateContext = 0;
+static PtrCGLDestroyContext ptrCGLDestroyContext = 0;
+static PtrCGLDestroyPixelFormat ptrCGLDestroyPixelFormat = 0;
+static PtrCGLSetCurrentContext ptrCGLSetCurrentContext = 0;
+static PtrCGLSetFullScreen ptrCGLSetFullScreen = 0;
+static PtrglFinish ptrglFinish = 0;
+static PtrglPixelStorei ptrglPixelStorei = 0;
+static PtrglReadBuffer ptrglReadBuffer = 0;
+static PtrglReadPixels ptrglReadPixels = 0;
+
+static bool resolveOpenGLSymbols()
+{
+ if (ptrCGLChoosePixelFormat == 0) {
+ QLibrary library(QLatin1String("/System/Library/Frameworks/OpenGL.framework/OpenGL"));
+ ptrCGLChoosePixelFormat = (PtrCGLChoosePixelFormat)(library.resolve("CGLChoosePixelFormat"));
+ ptrCGLClearDrawable = (PtrCGLClearDrawable)(library.resolve("CGLClearDrawable"));
+ ptrCGLCreateContext = (PtrCGLCreateContext)(library.resolve("CGLCreateContext"));
+ ptrCGLDestroyContext = (PtrCGLDestroyContext)(library.resolve("CGLDestroyContext"));
+ ptrCGLDestroyPixelFormat = (PtrCGLDestroyPixelFormat)(library.resolve("CGLDestroyPixelFormat"));
+ ptrCGLSetCurrentContext = (PtrCGLSetCurrentContext)(library.resolve("CGLSetCurrentContext"));
+ ptrCGLSetFullScreen = (PtrCGLSetFullScreen)(library.resolve("CGLSetFullScreen"));
+ ptrglFinish = (PtrglFinish)(library.resolve("glFinish"));
+ ptrglPixelStorei = (PtrglPixelStorei)(library.resolve("glPixelStorei"));
+ ptrglReadBuffer = (PtrglReadBuffer)(library.resolve("glReadBuffer"));
+ ptrglReadPixels = (PtrglReadPixels)(library.resolve("glReadPixels"));
+ }
+ return ptrCGLChoosePixelFormat && ptrCGLClearDrawable && ptrCGLCreateContext
+ && ptrCGLDestroyContext && ptrCGLDestroyPixelFormat && ptrCGLSetCurrentContext
+ && ptrCGLSetFullScreen && ptrglFinish && ptrglPixelStorei
+ && ptrglReadBuffer && ptrglReadPixels;
+}
+
+// Inverts the given pixmap in the y direction.
+static void qt_mac_flipPixmap(void *data, int rowBytes, int height)
+{
+ int bottom = height - 1;
+ void *base = data;
+ void *buffer = malloc(rowBytes);
+
+ int top = 0;
+ while ( top < bottom )
+ {
+ void *topP = (void *)((top * rowBytes) + (intptr_t)base);
+ void *bottomP = (void *)((bottom * rowBytes) + (intptr_t)base);
+
+ bcopy( topP, buffer, rowBytes );
+ bcopy( bottomP, topP, rowBytes );
+ bcopy( buffer, bottomP, rowBytes );
+
+ ++top;
+ --bottom;
+ }
+ free(buffer);
+}
+
+// Grabs displayRect from display and places it into buffer.
+static void qt_mac_grabDisplayRect(CGDirectDisplayID display, const QRect &displayRect, void *buffer)
+{
+ if (display == kCGNullDirectDisplay)
+ return;
+
+ CGLPixelFormatAttribute attribs[] = {
+ kCGLPFAFullScreen,
+ kCGLPFADisplayMask,
+ (CGLPixelFormatAttribute)0, /* Display mask bit goes here */
+ (CGLPixelFormatAttribute)0
+ };
+
+ attribs[2] = (CGLPixelFormatAttribute)CGDisplayIDToOpenGLDisplayMask(display);
+
+ // Build a full-screen GL context
+ CGLPixelFormatObj pixelFormatObj;
+ long numPixelFormats;
+
+ ptrCGLChoosePixelFormat( attribs, &pixelFormatObj, &numPixelFormats );
+
+ if (!pixelFormatObj) // No full screen context support
+ return;
+
+ CGLContextObj glContextObj;
+ ptrCGLCreateContext(pixelFormatObj, 0, &glContextObj);
+ ptrCGLDestroyPixelFormat(pixelFormatObj) ;
+ if (!glContextObj)
+ return;
+
+ ptrCGLSetCurrentContext(glContextObj);
+ ptrCGLSetFullScreen(glContextObj) ;
+
+ ptrglReadBuffer(GL_FRONT);
+
+ ptrglFinish(); // Finish all OpenGL commands
+ ptrglPixelStorei(GL_PACK_ALIGNMENT, 4); // Force 4-byte alignment
+ ptrglPixelStorei(GL_PACK_ROW_LENGTH, 0);
+ ptrglPixelStorei(GL_PACK_SKIP_ROWS, 0);
+ ptrglPixelStorei(GL_PACK_SKIP_PIXELS, 0);
+
+ // Fetch the data in XRGB format, matching the bitmap context.
+ ptrglReadPixels(GLint(displayRect.x()), GLint(displayRect.y()),
+ GLint(displayRect.width()), GLint(displayRect.height()),
+#ifdef __BIG_ENDIAN__
+ GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, buffer
+#else
+ GL_BGRA, GL_UNSIGNED_INT_8_8_8_8, buffer
+#endif
+ );
+
+ ptrCGLSetCurrentContext(0);
+ ptrCGLClearDrawable(glContextObj); // disassociate from full screen
+ ptrCGLDestroyContext(glContextObj); // and destroy the context
+}
+
+static CGImageRef qt_mac_createImageFromBitmapContext(CGContextRef c)
+{
+ void *rasterData = CGBitmapContextGetData(c);
+ const int width = CGBitmapContextGetBytesPerRow(c),
+ height = CGBitmapContextGetHeight(c);
+ size_t imageDataSize = width*height;
+
+ if(!rasterData)
+ return 0;
+
+ // Create the data provider from the image data, using
+ // the image releaser function releaseBitmapContextImageData.
+ CGDataProviderRef dataProvider = CGDataProviderCreateWithData(0, rasterData,
+ imageDataSize,
+ qt_mac_cgimage_data_free);
+
+ if(!dataProvider)
+ return 0;
+
+ uint bitmapInfo = 0;
+#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4
+ if(CGBitmapContextGetBitmapInfo)
+ bitmapInfo = CGBitmapContextGetBitmapInfo(c);
+ else
+#endif
+ bitmapInfo = CGBitmapContextGetAlphaInfo(c);
+ return CGImageCreate(width, height, CGBitmapContextGetBitsPerComponent(c),
+ CGBitmapContextGetBitsPerPixel(c), CGBitmapContextGetBytesPerRow(c),
+ CGBitmapContextGetColorSpace(c), bitmapInfo, dataProvider,
+ 0, true, kCGRenderingIntentDefault);
+}
+
+// Returns a pixmap containing the screen contents at rect.
+static QPixmap qt_mac_grabScreenRect(const QRect &rect)
+{
+ if (!resolveOpenGLSymbols())
+ return QPixmap();
+
+ const int maxDisplays = 128; // 128 displays should be enough for everyone.
+ CGDirectDisplayID displays[maxDisplays];
+ CGDisplayCount displayCount;
+ const CGRect cgRect = CGRectMake(rect.x(), rect.y(), rect.width(), rect.height());
+ const CGDisplayErr err = CGGetDisplaysWithRect(cgRect, maxDisplays, displays, &displayCount);
+
+ if (err && displayCount == 0)
+ return QPixmap();
+
+ long bytewidth = rect.width() * 4; // Assume 4 bytes/pixel for now
+ bytewidth = (bytewidth + 3) & ~3; // Align to 4 bytes
+ QVarLengthArray<char> buffer(rect.height() * bytewidth);
+
+ for (uint i = 0; i < displayCount; ++i) {
+ const CGRect bounds = CGDisplayBounds(displays[i]);
+ // Translate to display-local coordinates
+ QRect displayRect = rect.translated(qRound(-bounds.origin.x), qRound(-bounds.origin.y));
+ // Adjust for inverted y axis.
+ displayRect.moveTop(qRound(bounds.size.height) - displayRect.y() - rect.height());
+ qt_mac_grabDisplayRect(displays[i], displayRect, buffer.data());
+ }
+
+ qt_mac_flipPixmap(buffer.data(), bytewidth, rect.height());
+ QCFType<CGContextRef> bitmap = CGBitmapContextCreate(buffer.data(), rect.width(),
+ rect.height(), 8, bytewidth,
+ QCoreGraphicsPaintEngine::macGenericColorSpace(),
+ kCGImageAlphaNoneSkipFirst);
+
+#if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4)
+ if(QSysInfo::MacintoshVersion >= QSysInfo::MV_10_4) {
+ QCFType<CGImageRef> image = CGBitmapContextCreateImage(bitmap);
+ return QPixmap::fromMacCGImageRef(image);
+ } else
+#endif
+ {
+ QCFType<CGImageRef> image = qt_mac_createImageFromBitmapContext(bitmap);
+ if (!image)
+ return QPixmap();
+ return QPixmap::fromMacCGImageRef(image);
+ }
+}
+
+#ifndef QT_MAC_USE_COCOA // no QuickDraw in 64-bit mode
+static QPixmap qt_mac_grabScreenRect_10_3(int x, int y, int w, int h, QWidget *widget)
+{
+ QPixmap pm = QPixmap(w, h);
+ extern WindowPtr qt_mac_window_for(const QWidget *); // qwidget_mac.cpp
+ const BitMap *windowPort = 0;
+ if((widget->windowType() == Qt::Desktop)) {
+ GDHandle gdh;
+ if(!(gdh=GetMainDevice()))
+ qDebug("Qt: internal: Unexpected condition reached: %s:%d", __FILE__, __LINE__);
+ windowPort = (BitMap*)(*(*gdh)->gdPMap);
+ } else {
+ windowPort = GetPortBitMapForCopyBits(GetWindowPort(qt_mac_window_for(widget)));
+ }
+ const BitMap *pixmapPort = GetPortBitMapForCopyBits(static_cast<GWorldPtr>(pm.macQDHandle()));
+ Rect macSrcRect, macDstRect;
+ SetRect(&macSrcRect, x, y, x + w, y + h);
+ SetRect(&macDstRect, 0, 0, w, h);
+ CopyBits(windowPort, pixmapPort, &macSrcRect, &macDstRect, srcCopy, 0);
+ return pm;
+}
+#endif
+
+QPixmap QPixmap::grabWindow(WId window, int x, int y, int w, int h)
+{
+ QWidget *widget = QWidget::find(window);
+ if (widget == 0)
+ return QPixmap();
+
+ if(w == -1)
+ w = widget->width() - x;
+ if(h == -1)
+ h = widget->height() - y;
+
+ QPoint globalCoord(0, 0);
+ globalCoord = widget->mapToGlobal(globalCoord);
+ QRect rect(globalCoord.x() + x, globalCoord.y() + y, w, h);
+
+#ifdef QT_MAC_USE_COCOA
+ return qt_mac_grabScreenRect(rect);
+#else
+#if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4)
+ if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_4) {
+ return qt_mac_grabScreenRect(rect);
+ } else
+#endif
+ {
+ return qt_mac_grabScreenRect_10_3(x, y, w, h, widget);
+ }
+#endif // ifdef Q_WS_MAC64
+}
+
+/*! \internal
+
+ Returns the QuickDraw CGrafPtr of the pixmap. 0 is returned if it can't
+ be obtained. Do not hold the pointer around for long as it can be
+ relocated.
+
+ \warning This function is only available on Mac OS X.
+*/
+
+Qt::HANDLE QPixmap::macQDHandle() const
+{
+#ifndef QT_MAC_NO_QUICKDRAW
+ QMacPixmapData *d = static_cast<QMacPixmapData*>(data);
+ if (!d->qd_data) { //create the qd data
+ Rect rect;
+ SetRect(&rect, 0, 0, d->w, d->h);
+ unsigned long qdformat = k32ARGBPixelFormat;
+ GWorldFlags qdflags = 0;
+#if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4)
+ if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_4) {
+ //we play such games so we can use the same buffer in CG as QD this
+ //makes our merge much simpler, at some point the hacks will go away
+ //because QD will be removed, but until that day this keeps them coexisting
+ if (QSysInfo::ByteOrder == QSysInfo::LittleEndian)
+ qdformat = k32BGRAPixelFormat;
+#if 0
+ qdflags |= kNativeEndianPixMap;
+#endif
+ }
+#endif
+ if(NewGWorldFromPtr(&d->qd_data, qdformat, &rect, 0, 0, qdflags,
+ (char*)d->pixels, d->bytesPerRow) != noErr)
+ qWarning("Qt: internal: QPixmap::init error (%d %d %d %d)", rect.left, rect.top, rect.right, rect.bottom);
+ }
+ return d->qd_data;
+#else
+ return 0;
+#endif
+}
+
+/*! \internal
+
+ Returns the QuickDraw CGrafPtr of the pixmap's alpha channel. 0 is
+ returned if it can't be obtained. Do not hold the pointer around for
+ long as it can be relocated.
+
+ \warning This function is only available on Mac OS X.
+*/
+
+Qt::HANDLE QPixmap::macQDAlphaHandle() const
+{
+#ifndef QT_MAC_NO_QUICKDRAW
+ QMacPixmapData *d = static_cast<QMacPixmapData*>(data);
+ if (d->has_alpha || d->has_mask) {
+ if (!d->qd_alpha) //lazily created
+ d->macQDUpdateAlpha();
+ return d->qd_alpha;
+ }
+#endif
+ return 0;
+}
+
+/*! \internal
+
+ Returns the CoreGraphics CGContextRef of the pixmap. 0 is returned if
+ it can't be obtained. It is the caller's responsiblity to
+ CGContextRelease the context when finished using it.
+
+ \warning This function is only available on Mac OS X.
+*/
+
+Qt::HANDLE QPixmap::macCGHandle() const
+{
+ if (data->classId() == QPixmapData::MacClass) {
+ QMacPixmapData *d = static_cast<QMacPixmapData *>(data);
+ if (!d->cg_data)
+ d->macCreateCGImageRef();
+ CGImageRef ret = d->cg_data;
+ CGImageRetain(ret);
+ return ret;
+ } else if (data->classId() == QPixmapData::RasterClass) {
+ return qt_mac_image_to_cgimage(static_cast<QRasterPixmapData *>(data)->image);
+ }
+ return 0;
+}
+
+bool QMacPixmapData::hasAlphaChannel() const
+{
+ return has_alpha;
+}
+
+CGImageRef qt_mac_create_imagemask(const QPixmap &pixmap, const QRectF &sr)
+{
+ QMacPixmapData *px = static_cast<QMacPixmapData*>(pixmap.data);
+ if (px->cg_mask) {
+ if (px->cg_mask_rect == sr) {
+ CGImageRetain(px->cg_mask); //reference for the caller
+ return px->cg_mask;
+ }
+ CGImageRelease(px->cg_mask);
+ px->cg_mask = 0;
+ }
+
+ const int sx = qRound(sr.x()), sy = qRound(sr.y()), sw = qRound(sr.width()), sh = qRound(sr.height());
+ const int sbpr = px->bytesPerRow;
+ const uint nbytes = sw * sh;
+ // alpha is always 255 for bitmaps, ignore it in this case.
+ const quint32 mask = px->depth() == 1 ? 0x00ffffff : 0xffffffff;
+ quint8 *dptr = static_cast<quint8 *>(malloc(nbytes));
+ quint32 *sptr = px->pixels, *srow;
+ for(int y = sy, offset=0; y < sh; ++y) {
+ srow = sptr + (y * (sbpr / 4));
+ for(int x = sx; x < sw; ++x)
+ *(dptr+(offset++)) = (*(srow+x) & mask) ? 255 : 0;
+ }
+ QCFType<CGDataProviderRef> provider = CGDataProviderCreateWithData(0, dptr, nbytes, qt_mac_cgimage_data_free);
+ px->cg_mask = CGImageMaskCreate(sw, sh, 8, 8, nbytes / sh, provider, 0, 0);
+ px->cg_mask_rect = sr;
+ CGImageRetain(px->cg_mask); //reference for the caller
+ return px->cg_mask;
+}
+
+#ifndef QT_MAC_USE_COCOA
+IconRef qt_mac_create_iconref(const QPixmap &px)
+{
+ if (px.isNull())
+ return 0;
+
+ QMacSavedPortInfo pi; //save the current state
+ //create icon
+ IconFamilyHandle iconFamily = reinterpret_cast<IconFamilyHandle>(NewHandle(0));
+ //create data
+ {
+ struct {
+ OSType mac_type;
+ int width, height, depth;
+ bool mask;
+ } images[] = {
+ { kThumbnail32BitData, 128, 128, 32, false },
+ { kThumbnail8BitMask, 128, 128, 8, true },
+ { 0, 0, 0, 0, false } //end marker
+ };
+ for(int i = 0; images[i].mac_type; i++) {
+ //get QPixmap data
+ QImage scaled_px = px.toImage().scaled(images[i].width, images[i].height);
+
+ quint32 *sptr = (quint32 *) scaled_px.bits();
+ quint32 *srow;
+ uint sbpr = scaled_px.bytesPerLine();
+
+ //get Handle data
+ const int dbpr = images[i].width * (images[i].depth/8);
+ Handle hdl = NewHandle(dbpr*images[i].height);
+ if(!sptr) { //handle null pixmap
+ memset((*hdl), '\0', dbpr*images[i].height);
+ } else if(images[i].mask) {
+ if(images[i].mac_type == kThumbnail8BitMask) {
+ for(int y = 0, hindex = 0; y < images[i].height; ++y) {
+ srow = sptr + (y * (sbpr/4));
+ for(int x = 0; x < images[i].width; ++x)
+ *((*hdl)+(hindex++)) = qAlpha(*(srow+x));
+ }
+ }
+ } else {
+ char *dest = (*hdl);
+#if defined(__i386__)
+ if(images[i].depth == 32) {
+ for(int y = 0; y < images[i].height; ++y) {
+ uint *source = (uint*)((const uchar*)sptr+(sbpr*y));
+ for(int x = 0; x < images[i].width; ++x, dest += 4)
+ *((uint*)dest) = CFSwapInt32(*(source + x));
+ }
+ } else
+#endif
+ {
+ for(int y = 0; y < images[i].height; ++y)
+ memcpy(dest+(y*dbpr), ((const uchar*)sptr+(sbpr*y)), dbpr);
+ }
+ }
+
+ //set the family data to the Handle
+ OSStatus set = SetIconFamilyData(iconFamily, images[i].mac_type, hdl);
+ if(set != noErr)
+ qWarning("%s: %d -- Unable to create icon data[%d]!! %ld",
+ __FILE__, __LINE__, i, long(set));
+ DisposeHandle(hdl);
+ }
+ }
+
+ //acquire and cleanup
+ IconRef ret;
+ static int counter = 0;
+ const OSType kQtCreator = 'CUTE';
+ RegisterIconRefFromIconFamily(kQtCreator, (OSType)counter, iconFamily, &ret);
+ AcquireIconRef(ret);
+ UnregisterIconRef(kQtCreator, (OSType)counter);
+ DisposeHandle(reinterpret_cast<Handle>(iconFamily));
+ counter++;
+ return ret;
+
+}
+#endif
+
+QPixmap qt_mac_convert_iconref(const IconRef icon, int width, int height)
+{
+ QPixmap ret(width, height);
+ ret.fill(QColor(0, 0, 0, 0));
+
+ CGRect rect = CGRectMake(0, 0, width, height);
+
+ CGContextRef ctx = qt_mac_cg_context(&ret);
+ CGAffineTransform old_xform = CGContextGetCTM(ctx);
+ CGContextConcatCTM(ctx, CGAffineTransformInvert(old_xform));
+ CGContextConcatCTM(ctx, CGAffineTransformIdentity);
+
+ ::RGBColor b;
+ b.blue = b.green = b.red = 255*255;
+ PlotIconRefInContext(ctx, &rect, kAlignNone, kTransformNone, &b, kPlotIconRefNormalFlags, icon);
+ CGContextRelease(ctx);
+ return ret;
+}
+
+/*! \internal */
+QPaintEngine* QMacPixmapData::paintEngine() const
+{
+ if (!pengine) {
+ QMacPixmapData *that = const_cast<QMacPixmapData*>(this);
+#ifdef QT_RASTER_PAINTENGINE
+ if (qgetenv("QT_MAC_USE_COREGRAPHICS").isNull())
+ that->pengine = new QRasterPaintEngine();
+ else
+ that->pengine = new QCoreGraphicsPaintEngine();
+#else
+ that->pengine = new QCoreGraphicsPaintEngine();
+#endif
+ }
+ return pengine;
+}
+
+void QMacPixmapData::copy(const QPixmapData *data, const QRect &rect)
+{
+ if (data->pixelType() == BitmapType) {
+ QBitmap::fromImage(toImage().copy(rect));
+ return;
+ }
+
+ const QMacPixmapData *macData = static_cast<const QMacPixmapData*>(data);
+
+ resize(rect.width(), rect.height());
+
+ has_alpha = macData->has_alpha;
+ has_mask = macData->has_mask;
+ uninit = false;
+
+ const int x = rect.x();
+ const int y = rect.y();
+ char *dest = reinterpret_cast<char*>(pixels);
+ const char *src = reinterpret_cast<const char*>(macData->pixels + x) + y * macData->bytesPerRow;
+ for (int i = 0; i < h; ++i) {
+ memcpy(dest, src, w * 4);
+ dest += bytesPerRow;
+ src += macData->bytesPerRow;
+ }
+
+ has_alpha = macData->has_alpha;
+ has_mask = macData->has_mask;
+}
+
+/*!
+ \since 4.2
+
+ Creates a \c CGImageRef equivalent to the QPixmap. Returns the \c CGImageRef handle.
+
+ It is the caller's responsibility to release the \c CGImageRef data
+ after use.
+
+ \warning This function is only available on Mac OS X.
+
+ \sa fromMacCGImageRef()
+*/
+CGImageRef QPixmap::toMacCGImageRef() const
+{
+ return (CGImageRef)macCGHandle();
+}
+
+/*!
+ \since 4.2
+
+ Returns a QPixmap that is equivalent to the given \a image.
+
+ \warning This function is only available on Mac OS X.
+
+ \sa toMacCGImageRef(), {QPixmap#Pixmap Conversion}{Pixmap Conversion}
+*/
+QPixmap QPixmap::fromMacCGImageRef(CGImageRef image)
+{
+ const size_t w = CGImageGetWidth(image),
+ h = CGImageGetHeight(image);
+ QPixmap ret(w, h);
+ CGRect rect = CGRectMake(0, 0, w, h);
+ CGContextRef ctx = qt_mac_cg_context(&ret);
+ qt_mac_drawCGImage(ctx, &rect, image);
+ CGContextRelease(ctx);
+ return ret;
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/image/qpixmap_mac_p.h b/src/gui/image/qpixmap_mac_p.h
new file mode 100644
index 0000000000..75525c479c
--- /dev/null
+++ b/src/gui/image/qpixmap_mac_p.h
@@ -0,0 +1,136 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QPIXMAP_MAC_P_H
+#define QPIXMAP_MAC_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 <QtGui/private/qpixmapdata_p.h>
+#include <QtGui/private/qpixmapdatafactory_p.h>
+#include <QtGui/private/qt_mac_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QMacPixmapData : public QPixmapData
+{
+public:
+ QMacPixmapData(PixelType type);
+ ~QMacPixmapData();
+
+ void resize(int width, int height);
+ void fromImage(const QImage &image, Qt::ImageConversionFlags flags);
+ void copy(const QPixmapData *data, const QRect &rect);
+
+ int metric(QPaintDevice::PaintDeviceMetric metric) const;
+ void fill(const QColor &color);
+ QBitmap mask() const;
+ void setMask(const QBitmap &mask);
+ bool hasAlphaChannel() const;
+// QPixmap transformed(const QTransform &matrix,
+// Qt::TransformationMode mode) const;
+ void setAlphaChannel(const QPixmap &alphaChannel);
+ QPixmap alphaChannel() const;
+ QImage toImage() const;
+ QPaintEngine* paintEngine() const;
+
+private:
+ int w, h, d;
+
+ uint has_alpha : 1, has_mask : 1, uninit : 1;
+
+ void macSetHasAlpha(bool b);
+ void macGetAlphaChannel(QMacPixmapData *, bool asMask) const;
+ void macSetAlphaChannel(const QMacPixmapData *, bool asMask);
+ void macCreateCGImageRef();
+ void macCreatePixels();
+ void macReleaseCGImageRef();
+ /*
+ pixels stores the pixmap data. pixelsToFree is either 0 or some memory
+ block that was bound to a CGImageRef and released, and for which the
+ release callback has been called. There are two uses to pixelsToFree:
+
+ 1. If pixels == pixelsToFree, then we know that the CGImageRef is done\
+ with the data and we can modify pixels without breaking CGImageRef's
+ mutability invariant.
+
+ 2. If pixels != pixelsToFree and pixelsToFree != 0, then we can reuse
+ pixelsToFree later on instead of malloc'ing memory.
+ */
+ quint32 *pixels;
+ quint32 *pixelsToFree;
+ uint bytesPerRow;
+ QRectF cg_mask_rect;
+ CGImageRef cg_data, cg_dataBeingReleased, cg_mask;
+#ifndef QT_MAC_NO_QUICKDRAW
+ GWorldPtr qd_data, qd_alpha;
+ void macQDDisposeAlpha();
+ void macQDUpdateAlpha();
+#endif
+ static QSet<QMacPixmapData*> validDataPointers;
+
+ QPaintEngine *pengine;
+
+ friend class QPixmap;
+ friend class QRasterBuffer;
+ friend class QRasterPaintEngine;
+ friend class QCoreGraphicsPaintEngine;
+ friend CGImageRef qt_mac_create_imagemask(const QPixmap&, const QRectF&);
+ friend quint32 *qt_mac_pixmap_get_base(const QPixmap*);
+ friend int qt_mac_pixmap_get_bytes_per_line(const QPixmap*);
+ friend void qt_mac_cgimage_data_free(void *, const void*, size_t);
+ friend IconRef qt_mac_create_iconref(const QPixmap&);
+ friend CGContextRef qt_mac_cg_context(const QPaintDevice*);
+ friend QColor qcolorForThemeTextColor(ThemeTextColor themeColor);
+};
+
+QT_END_NAMESPACE
+
+#endif // QPIXMAP_MAC_P_H
diff --git a/src/gui/image/qpixmap_qws.cpp b/src/gui/image/qpixmap_qws.cpp
new file mode 100644
index 0000000000..6cc79818d5
--- /dev/null
+++ b/src/gui/image/qpixmap_qws.cpp
@@ -0,0 +1,164 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <qpixmap.h>
+#include <qapplication.h>
+#include <qwidget.h>
+#include <qdesktopwidget.h>
+#include <qscreen_qws.h>
+#include <qwsdisplay_qws.h>
+#include <private/qdrawhelper_p.h>
+#include <private/qpixmap_raster_p.h>
+
+QPixmap QPixmap::grabWindow(WId window, int x, int y, int w, int h)
+{
+ QWidget *widget = QWidget::find(window);
+ if (!widget)
+ return QPixmap();
+
+ QRect grabRect = widget->frameGeometry();
+ if (!widget->isWindow())
+ grabRect.translate(widget->parentWidget()->mapToGlobal(QPoint()));
+ if (w < 0)
+ w = widget->width() - x;
+ if (h < 0)
+ h = widget->height() - y;
+ grabRect &= QRect(x, y, w, h).translated(widget->mapToGlobal(QPoint()));
+
+ QScreen *screen = qt_screen;
+ QDesktopWidget *desktop = QApplication::desktop();
+ if (!desktop)
+ return QPixmap();
+ if (desktop->numScreens() > 1) {
+ const int screenNo = desktop->screenNumber(widget);
+ if (screenNo != -1)
+ screen = qt_screen->subScreens().at(screenNo);
+ grabRect = grabRect.translated(-screen->region().boundingRect().topLeft());
+ }
+
+ if (screen->pixelFormat() == QImage::Format_Invalid) {
+ qWarning("QPixmap::grabWindow(): Unable to copy pixels from framebuffer");
+ return QPixmap();
+ }
+
+ if (screen->isTransformed()) {
+ const QSize screenSize(screen->width(), screen->height());
+ grabRect = screen->mapToDevice(grabRect, screenSize);
+ }
+
+ QWSDisplay::grab(false);
+ QPixmap pixmap;
+ QImage img(screen->base(),
+ screen->deviceWidth(), screen->deviceHeight(),
+ screen->linestep(), screen->pixelFormat());
+ img = img.copy(grabRect);
+ QWSDisplay::ungrab();
+
+ if (screen->isTransformed()) {
+ QMatrix matrix;
+ switch (screen->transformOrientation()) {
+ case 1: matrix.rotate(90); break;
+ case 2: matrix.rotate(180); break;
+ case 3: matrix.rotate(270); break;
+ default: break;
+ }
+ img = img.transformed(matrix);
+ }
+
+ if (screen->pixelType() == QScreen::BGRPixel)
+ img = img.rgbSwapped();
+
+ return QPixmap::fromImage(img);
+}
+
+/*!
+ \internal
+*/
+QRgb* QPixmap::clut() const
+{
+ if (data->classId() == QPixmapData::RasterClass) {
+ const QRasterPixmapData *d = static_cast<const QRasterPixmapData*>(data);
+ return d->image.colorTable().data();
+ }
+
+ return 0;
+}
+
+/*!
+ \internal
+*/
+int QPixmap::numCols() const
+{
+ if (data->classId() == QPixmapData::RasterClass) {
+ const QRasterPixmapData *d = static_cast<const QRasterPixmapData*>(data);
+ return d->image.numColors();
+ }
+
+ return 0;
+}
+
+/*!
+ \internal
+ \since 4.1
+*/
+const uchar* QPixmap::qwsBits() const
+{
+ if (data->classId() == QPixmapData::RasterClass) {
+ const QRasterPixmapData *d = static_cast<const QRasterPixmapData*>(data);
+ return d->image.bits();
+ }
+
+ return 0;
+}
+
+/*!
+ \internal
+ \since 4.1
+*/
+int QPixmap::qwsBytesPerLine() const
+{
+ if (data->classId() == QPixmapData::RasterClass) {
+ const QRasterPixmapData *d = static_cast<const QRasterPixmapData*>(data);
+ return d->image.bytesPerLine();
+ }
+
+ return 0;
+}
diff --git a/src/gui/image/qpixmap_raster.cpp b/src/gui/image/qpixmap_raster.cpp
new file mode 100644
index 0000000000..7dfab70a79
--- /dev/null
+++ b/src/gui/image/qpixmap_raster.cpp
@@ -0,0 +1,350 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qpixmap.h"
+
+#include "qpixmap_raster_p.h"
+#include "qnativeimage_p.h"
+#include "qimage_p.h"
+
+#include "qbitmap.h"
+#include "qimage.h"
+#include <private/qwidget_p.h>
+#include <private/qdrawhelper_p.h>
+
+#if !defined(QT_NO_DIRECT3D) && defined(Q_WS_WIN)
+#include <private/qpaintengine_d3d_p.h>
+#include <d3d9.h>
+extern QDirect3DPaintEngine *qt_d3dEngine();
+#endif
+
+QT_BEGIN_NAMESPACE
+
+const uchar qt_pixmap_bit_mask[] = { 0x01, 0x02, 0x04, 0x08,
+ 0x10, 0x20, 0x40, 0x80 };
+
+QRasterPixmapData::QRasterPixmapData(PixelType type)
+ : QPixmapData(type, RasterClass)
+#if defined(Q_WS_WIN) && !defined(QT_NO_DIRECT3D)
+ , texture(0)
+#endif
+{
+}
+
+QRasterPixmapData::~QRasterPixmapData()
+{
+}
+
+void QRasterPixmapData::resize(int width, int height)
+{
+ QImage::Format format;
+#ifdef Q_WS_QWS
+ if (pixelType() == BitmapType) {
+ format = QImage::Format_Mono;
+ } else {
+ format = QScreen::instance()->pixelFormat();
+ if (format == QImage::Format_Invalid)
+ format = QImage::Format_ARGB32_Premultiplied;
+ else if (format == QImage::Format_Indexed8) // currently not supported
+ format = QImage::Format_RGB444;
+ }
+#else
+ if (pixelType() == BitmapType)
+ format = QImage::Format_MonoLSB;
+ else
+ format = QNativeImage::systemFormat();
+#endif
+
+ image = QImage(width, height, format);
+
+ if (pixelType() == BitmapType && !image.isNull()) {
+ image.setNumColors(2);
+ image.setColor(0, QColor(Qt::color0).rgba());
+ image.setColor(1, QColor(Qt::color1).rgba());
+ }
+
+ setSerialNumber(image.serialNumber());
+}
+
+void QRasterPixmapData::fromImage(const QImage &sourceImage,
+ Qt::ImageConversionFlags flags)
+{
+ Q_UNUSED(flags);
+
+#ifdef Q_WS_QWS
+ QImage::Format format;
+ if (pixelType() == BitmapType) {
+ format = QImage::Format_Mono;
+ } else {
+ format = QScreen::instance()->pixelFormat();
+ if (format == QImage::Format_Invalid)
+ format = QImage::Format_ARGB32_Premultiplied;
+ else if (format == QImage::Format_Indexed8) // currently not supported
+ format = QImage::Format_RGB444;
+ }
+
+ if (sourceImage.hasAlphaChannel()
+ && ((flags & Qt::NoOpaqueDetection)
+ || const_cast<QImage &>(sourceImage).data_ptr()->checkForAlphaPixels())) {
+ switch (format) {
+ case QImage::Format_RGB16:
+ format = QImage::Format_ARGB8565_Premultiplied;
+ break;
+ case QImage::Format_RGB666:
+ format = QImage::Format_ARGB6666_Premultiplied;
+ break;
+ case QImage::Format_RGB555:
+ format = QImage::Format_ARGB8555_Premultiplied;
+ break;
+ case QImage::Format_RGB444:
+ format = QImage::Format_ARGB4444_Premultiplied;
+ break;
+ default:
+ format = QImage::Format_ARGB32_Premultiplied;
+ break;
+ }
+ } else if (format == QImage::Format_Invalid) {
+ format = QImage::Format_ARGB32_Premultiplied;
+ }
+
+ image = sourceImage.convertToFormat(format);
+#else
+ if (pixelType() == BitmapType) {
+ image = sourceImage.convertToFormat(QImage::Format_MonoLSB);
+ } else {
+ if (sourceImage.depth() == 1) {
+ image = sourceImage.hasAlphaChannel()
+ ? sourceImage.convertToFormat(QImage::Format_ARGB32_Premultiplied)
+ : sourceImage.convertToFormat(QImage::Format_RGB32);
+ } else {
+
+ QImage::Format opaqueFormat = QNativeImage::systemFormat();
+ QImage::Format alphaFormat = QImage::Format_ARGB32_Premultiplied;
+
+ switch (opaqueFormat) {
+ case QImage::Format_RGB16:
+ alphaFormat = QImage::Format_ARGB8565_Premultiplied;
+ break;
+ default: // We don't care about the others...
+ break;
+ }
+
+ if (!sourceImage.hasAlphaChannel()
+ || ((flags & Qt::NoOpaqueDetection) == 0
+ && !const_cast<QImage &>(sourceImage).data_ptr()->checkForAlphaPixels())) {
+ image = sourceImage.convertToFormat(opaqueFormat);
+ } else {
+ image = sourceImage.convertToFormat(alphaFormat);
+ }
+ }
+ }
+#endif
+
+ setSerialNumber(image.serialNumber());
+}
+
+void QRasterPixmapData::fill(const QColor &color)
+{
+ uint pixel;
+
+ if (image.depth() == 1) {
+ int gray = qGray(color.rgba());
+ // Pick the best approximate color in the image's colortable.
+ if (qAbs(qGray(image.color(0)) - gray) < qAbs(qGray(image.color(1)) - gray))
+ pixel = 0;
+ else
+ pixel = 1;
+ } else if (image.depth() >= 15) {
+ int alpha = color.alpha();
+ if (alpha != 255) {
+ if (!image.hasAlphaChannel()) {
+ QImage::Format toFormat;
+ if (image.format() == QImage::Format_RGB16)
+ toFormat = QImage::Format_ARGB8565_Premultiplied;
+ else if (image.format() == QImage::Format_RGB666)
+ toFormat = QImage::Format_ARGB6666_Premultiplied;
+ else if (image.format() == QImage::Format_RGB555)
+ toFormat = QImage::Format_ARGB8555_Premultiplied;
+ else if (image.format() == QImage::Format_RGB444)
+ toFormat = QImage::Format_ARGB4444_Premultiplied;
+ else
+ toFormat = QImage::Format_ARGB32_Premultiplied;
+ image = QImage(image.width(), image.height(), toFormat);
+ }
+
+ switch (image.format()) {
+ case QImage::Format_ARGB8565_Premultiplied:
+ pixel = qargb8565(color.rgba()).rawValue();
+ break;
+ case QImage::Format_ARGB8555_Premultiplied:
+ pixel = qargb8555(color.rgba()).rawValue();
+ break;
+ case QImage::Format_ARGB6666_Premultiplied:
+ pixel = qargb6666(color.rgba()).rawValue();
+ break;
+ case QImage::Format_ARGB4444_Premultiplied:
+ pixel = qargb4444(color.rgba()).rawValue();
+ break;
+ default:
+ pixel = PREMUL(color.rgba());
+ break;
+ }
+ } else {
+ switch (image.format()) {
+ case QImage::Format_RGB16:
+ pixel = qt_colorConvert<quint16, quint32>(color.rgba(), 0);
+ break;
+ case QImage::Format_RGB444:
+ pixel = qrgb444(color.rgba()).rawValue();
+ break;
+ case QImage::Format_RGB555:
+ pixel = qrgb555(color.rgba()).rawValue();
+ break;
+ case QImage::Format_RGB666:
+ pixel = qrgb666(color.rgba()).rawValue();
+ break;
+ case QImage::Format_RGB888:
+ pixel = qrgb888(color.rgba()).rawValue();
+ break;
+ default:
+ pixel = color.rgba();
+ break;
+ }
+ }
+ } else {
+ pixel = 0;
+ // ### what about 8 bits
+ }
+
+ image.fill(pixel);
+}
+
+void QRasterPixmapData::setMask(const QBitmap &mask)
+{
+ if (mask.size().isEmpty()) {
+ if (image.depth() != 1) { // hw: ????
+ image = image.convertToFormat(QImage::Format_RGB32);
+ }
+ } else {
+ const int w = image.width();
+ const int h = image.height();
+
+ switch (image.depth()) {
+ case 1: {
+ const QImage imageMask = mask.toImage().convertToFormat(image.format());
+ for (int y = 0; y < h; ++y) {
+ const uchar *mscan = imageMask.scanLine(y);
+ uchar *tscan = image.scanLine(y);
+ int bytesPerLine = image.bytesPerLine();
+ for (int i = 0; i < bytesPerLine; ++i)
+ tscan[i] &= mscan[i];
+ }
+ break;
+ }
+ default: {
+ const QImage imageMask = mask.toImage().convertToFormat(QImage::Format_MonoLSB);
+ image = image.convertToFormat(QImage::Format_ARGB32_Premultiplied);
+ for (int y = 0; y < h; ++y) {
+ const uchar *mscan = imageMask.scanLine(y);
+ QRgb *tscan = (QRgb *)image.scanLine(y);
+ for (int x = 0; x < w; ++x) {
+ if (!(mscan[x>>3] & qt_pixmap_bit_mask[x&7]))
+ tscan[x] = 0;
+ }
+ }
+ break;
+ }
+ }
+ }
+}
+
+bool QRasterPixmapData::hasAlphaChannel() const
+{
+ return image.hasAlphaChannel();
+}
+
+QImage QRasterPixmapData::toImage() const
+{
+ return image;
+}
+
+void QRasterPixmapData::setAlphaChannel(const QPixmap &alphaChannel)
+{
+ image.setAlphaChannel(alphaChannel.toImage());
+}
+
+QPaintEngine* QRasterPixmapData::paintEngine() const
+{
+ return image.paintEngine();
+}
+
+extern int qt_defaultDpiX();
+extern int qt_defaultDpiY();
+
+int QRasterPixmapData::metric(QPaintDevice::PaintDeviceMetric metric) const
+{
+ // override the image dpi with the screen dpi when rendering to a pixmap
+ const int dpmX = qRound(qt_defaultDpiX() * 100 / qreal(2.54));
+ const int dpmY = qRound(qt_defaultDpiY() * 100 / qreal(2.54));
+ switch (metric) {
+ case QPaintDevice::PdmWidthMM:
+ return qRound(image.width() * 1000 / dpmX);
+ case QPaintDevice::PdmHeightMM:
+ return qRound(image.height() * 1000 / dpmY);
+ case QPaintDevice::PdmDpiX:
+ return qRound(dpmX * qreal(0.0254));
+ case QPaintDevice::PdmDpiY:
+ return qRound(dpmY * qreal(0.0254));
+ case QPaintDevice::PdmPhysicalDpiX:
+ return qRound(dpmX * qreal(0.0254));
+ case QPaintDevice::PdmPhysicalDpiY:
+ return qRound(dpmY * qreal(0.0254));
+ default:
+ return image.metric(metric);
+ }
+}
+
+QImage* QRasterPixmapData::buffer()
+{
+ return &image;
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/image/qpixmap_raster_p.h b/src/gui/image/qpixmap_raster_p.h
new file mode 100644
index 0000000000..095f378bac
--- /dev/null
+++ b/src/gui/image/qpixmap_raster_p.h
@@ -0,0 +1,105 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QPIXMAPDATA_RASTER_P_H
+#define QPIXMAPDATA_RASTER_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 <QtGui/private/qpixmapdata_p.h>
+#include <QtGui/private/qpixmapdatafactory_p.h>
+
+#ifdef Q_WS_WIN
+# include "qt_windows.h"
+# ifndef QT_NO_DIRECT3D
+# include <d3d9.h>
+# endif
+#endif
+
+QT_BEGIN_NAMESPACE
+
+class Q_GUI_EXPORT QRasterPixmapData : public QPixmapData
+{
+public:
+ QRasterPixmapData(PixelType type);
+ ~QRasterPixmapData();
+
+ void resize(int width, int height);
+ void fromFile(const QString &filename, Qt::ImageConversionFlags flags);
+ void fromImage(const QImage &image, Qt::ImageConversionFlags flags);
+
+ void fill(const QColor &color);
+ void setMask(const QBitmap &mask);
+ bool hasAlphaChannel() const;
+ void setAlphaChannel(const QPixmap &alphaChannel);
+ QImage toImage() const;
+ QPaintEngine* paintEngine() const;
+ QImage* buffer();
+
+protected:
+ int metric(QPaintDevice::PaintDeviceMetric metric) const;
+
+private:
+#if defined(Q_WS_WIN) && !defined(QT_NO_DIRECT3D)
+ friend class QDirect3DPaintEnginePrivate;
+ IDirect3DTexture9 *texture;
+#endif
+ friend class QPixmap;
+ friend class QBitmap;
+ friend class QDetachedPixmap;
+ friend class QRasterPaintEngine;
+ QImage image;
+};
+
+QT_END_NAMESPACE
+
+#endif // QPIXMAPDATA_RASTER_P_H
+
+
diff --git a/src/gui/image/qpixmap_win.cpp b/src/gui/image/qpixmap_win.cpp
new file mode 100644
index 0000000000..3ec441bd84
--- /dev/null
+++ b/src/gui/image/qpixmap_win.cpp
@@ -0,0 +1,481 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qpixmap.h"
+#include "qpixmap_raster_p.h"
+
+#include "qbitmap.h"
+#include "qimage.h"
+#include "qwidget.h"
+#include "qpainter.h"
+#include "qdatastream.h"
+#include "qbuffer.h"
+#include "qapplication.h"
+#include "qevent.h"
+#include "qfile.h"
+#include "qfileinfo.h"
+#include "qdatetime.h"
+#include "qpixmapcache.h"
+#include "qimagereader.h"
+#include "qimagewriter.h"
+#include "qdebug.h"
+#include "qt_windows.h"
+
+#if defined(Q_OS_WINCE)
+#include <winbase.h>
+#include "qguifunctions_wince.h"
+extern bool qt_wince_is_high_dpi();
+extern bool qt_wince_is_pocket_pc();
+#endif
+
+#ifndef CAPTUREBLT
+#define CAPTUREBLT ((DWORD)0x40000000)
+#endif
+
+QT_BEGIN_NAMESPACE
+
+QPixmap QPixmap::grabWindow(WId winId, int x, int y, int w, int h )
+{
+ RECT r;
+ GetClientRect(winId, &r);
+
+ if (w < 0) w = r.right - r.left;
+ if (h < 0) h = r.bottom - r.top;
+
+#ifdef Q_OS_WINCE_WM
+ if (qt_wince_is_pocket_pc()) {
+ QWidget *widget = QWidget::find(winId);
+ if (qobject_cast<QDesktopWidget *>(widget)) {
+ RECT rect = {0,0,0,0};
+ AdjustWindowRectEx(&rect, WS_BORDER | WS_CAPTION, FALSE, 0);
+ int magicNumber = qt_wince_is_high_dpi() ? 4 : 2;
+ y += rect.top - magicNumber;
+ }
+ }
+#endif
+
+ // Create and setup bitmap
+ HDC display_dc = GetDC(0);
+ HDC bitmap_dc = CreateCompatibleDC(display_dc);
+ HBITMAP bitmap = CreateCompatibleBitmap(display_dc, w, h);
+ HGDIOBJ null_bitmap = SelectObject(bitmap_dc, bitmap);
+
+ // copy data
+ HDC window_dc = GetDC(winId);
+ BitBlt(bitmap_dc, 0, 0, w, h, window_dc, x, y, SRCCOPY
+#ifndef Q_OS_WINCE
+ | CAPTUREBLT
+#endif
+ );
+
+ // clean up all but bitmap
+ ReleaseDC(winId, window_dc);
+ SelectObject(bitmap_dc, null_bitmap);
+ DeleteDC(bitmap_dc);
+
+ QPixmap pixmap = QPixmap::fromWinHBITMAP(bitmap);
+
+ DeleteObject(bitmap);
+ ReleaseDC(0, display_dc);
+
+ return pixmap;
+}
+
+
+
+/*!
+ \enum QPixmap::HBitmapFormat
+
+ This enum defines how the conversion between \c HBITMAP
+ and QPixmap is performed.
+
+ \warning This enum is only available on Windows.
+
+ \value NoAlpha The alpha channel is ignored and always treated as
+ being set to fully opaque. This is preferred if the \c HBITMAP is
+ used with standard GDI calls, such as \c BitBlt().
+
+ \value PremultipliedAlpha The \c HBITMAP is treated as having an
+ alpha channel and premultiplied colors. This is preferred if the
+ \c HBITMAP is accessed through the \c AlphaBlend() GDI function.
+
+ \value Alpha The \c HBITMAP is treated as having a plain alpha
+ channel. This is the preferred format if the \c HBITMAP is going
+ to be used as an application icon or systray icon.
+
+ \sa fromWinHBITMAP(), toWinHBITMAP()
+*/
+
+/*!
+ Creates a \c HBITMAP equivalent to the QPixmap, based on the given
+ \a format. Returns the \c HBITMAP handle.
+
+ It is the caller's responsibility to free the \c HBITMAP data
+ after use.
+
+ \warning This function is only available on Windows.
+
+ \sa fromWinHBITMAP()
+*/
+HBITMAP QPixmap::toWinHBITMAP(HBitmapFormat format) const
+{
+ HBITMAP bitmap = 0;
+ if (data->classId() == QPixmapData::RasterClass) {
+ QRasterPixmapData* d = static_cast<QRasterPixmapData*>(data);
+ int w = d->image.width();
+ int h = d->image.height();
+
+ HDC display_dc = GetDC(0);
+
+ // Define the header
+ BITMAPINFO bmi;
+ memset(&bmi, 0, sizeof(bmi));
+ bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
+ bmi.bmiHeader.biWidth = w;
+ bmi.bmiHeader.biHeight = -h;
+ bmi.bmiHeader.biPlanes = 1;
+ bmi.bmiHeader.biBitCount = 32;
+ bmi.bmiHeader.biCompression = BI_RGB;
+ bmi.bmiHeader.biSizeImage = w * h * 4;
+
+ // Create the pixmap
+ uchar *pixels = 0;
+ bitmap = CreateDIBSection(display_dc, &bmi, DIB_RGB_COLORS, (void **) &pixels, 0, 0);
+ ReleaseDC(0, display_dc);
+ if (!bitmap) {
+ qErrnoWarning("QPixmap::toWinHBITMAP(), failed to create dibsection");
+ return 0;
+ }
+ if (!pixels) {
+ qErrnoWarning("QPixmap::toWinHBITMAP(), did not allocate pixel data");
+ return 0;
+ }
+
+ // Copy over the data
+ QImage::Format imageFormat = QImage::Format_ARGB32;
+ if (format == NoAlpha)
+ imageFormat = QImage::Format_RGB32;
+ else if (format == PremultipliedAlpha)
+ imageFormat = QImage::Format_ARGB32_Premultiplied;
+ const QImage image = d->image.convertToFormat(imageFormat);
+ int bytes_per_line = w * 4;
+ for (int y=0; y<h; ++y)
+ memcpy(pixels + y * bytes_per_line, image.scanLine(y), bytes_per_line);
+
+ } else {
+ QPixmapData *data = new QRasterPixmapData(depth() == 1 ?
+ QPixmapData::BitmapType : QPixmapData::PixmapType);
+ data->fromImage(toImage(), Qt::AutoColor);
+ return QPixmap(data).toWinHBITMAP(format);
+ }
+ return bitmap;
+}
+
+/*!
+ Returns a QPixmap that is equivalent to the given \a bitmap. The
+ conversion is based on the specified \a format.
+
+ \warning This function is only available on Windows.
+
+ \sa toWinHBITMAP(), {QPixmap#Pixmap Conversion}{Pixmap Conversion}
+
+*/
+QPixmap QPixmap::fromWinHBITMAP(HBITMAP bitmap, HBitmapFormat format)
+{
+ // Verify size
+ BITMAP bitmap_info;
+ memset(&bitmap_info, 0, sizeof(BITMAP));
+
+ int res;
+ QT_WA({
+ res = GetObjectW(bitmap, sizeof(BITMAP), &bitmap_info);
+ } , {
+ res = GetObjectA(bitmap, sizeof(BITMAP), &bitmap_info);
+ });
+
+ if (!res) {
+ qErrnoWarning("QPixmap::fromWinHBITMAP(), failed to get bitmap info");
+ return QPixmap();
+ }
+ int w = bitmap_info.bmWidth;
+ int h = bitmap_info.bmHeight;
+
+ BITMAPINFO bmi;
+ memset(&bmi, 0, sizeof(bmi));
+ bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
+ bmi.bmiHeader.biWidth = w;
+ bmi.bmiHeader.biHeight = -h;
+ bmi.bmiHeader.biPlanes = 1;
+ bmi.bmiHeader.biBitCount = 32;
+ bmi.bmiHeader.biCompression = BI_RGB;
+ bmi.bmiHeader.biSizeImage = w * h * 4;
+
+ QImage result;
+ // Get bitmap bits
+ uchar *data = (uchar *) qMalloc(bmi.bmiHeader.biSizeImage);
+
+ HDC display_dc = GetDC(0);
+ if (GetDIBits(display_dc, bitmap, 0, h, data, &bmi, DIB_RGB_COLORS)) {
+
+ QImage::Format imageFormat = QImage::Format_ARGB32_Premultiplied;
+ uint mask = 0;
+ if (format == NoAlpha) {
+ imageFormat = QImage::Format_RGB32;
+ mask = 0xff000000;
+ }
+
+ // Create image and copy data into image.
+ QImage image(w, h, imageFormat);
+ if (!image.isNull()) { // failed to alloc?
+ int bytes_per_line = w * sizeof(QRgb);
+ for (int y=0; y<h; ++y) {
+ QRgb *dest = (QRgb *) image.scanLine(y);
+ const QRgb *src = (const QRgb *) (data + y * bytes_per_line);
+ for (int x=0; x<w; ++x) {
+ const uint pixel = src[x];
+ if ((pixel & 0xff000000) == 0 && (pixel & 0x00ffffff) != 0)
+ dest[x] = pixel | 0xff000000;
+ else
+ dest[x] = pixel | mask;
+ }
+ }
+ }
+ result = image;
+ } else {
+ qWarning("QPixmap::fromWinHBITMAP(), failed to get bitmap bits");
+ }
+ ReleaseDC(0, display_dc);
+ qFree(data);
+ return fromImage(result);
+}
+
+#ifdef Q_WS_WIN
+#ifndef Q_OS_WINCE
+
+static QImage qt_fromWinHBITMAP(HDC hdc, HBITMAP bitmap, int w, int h)
+{
+ BITMAPINFO bmi;
+ memset(&bmi, 0, sizeof(bmi));
+ bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
+ bmi.bmiHeader.biWidth = w;
+ bmi.bmiHeader.biHeight = -h;
+ bmi.bmiHeader.biPlanes = 1;
+ bmi.bmiHeader.biBitCount = 32;
+ bmi.bmiHeader.biCompression = BI_RGB;
+ bmi.bmiHeader.biSizeImage = w * h * 4;
+
+ QImage image(w, h, QImage::Format_ARGB32_Premultiplied);
+ if (image.isNull())
+ return image;
+
+ // Get bitmap bits
+ uchar *data = (uchar *) qMalloc(bmi.bmiHeader.biSizeImage);
+
+ if (GetDIBits(hdc, bitmap, 0, h, data, &bmi, DIB_RGB_COLORS)) {
+ // Create image and copy data into image.
+ for (int y=0; y<h; ++y) {
+ void *dest = (void *) image.scanLine(y);
+ void *src = data + y * image.bytesPerLine();
+ memcpy(dest, src, image.bytesPerLine());
+ }
+ } else {
+ qWarning("qt_fromWinHBITMAP(), failed to get bitmap bits");
+ }
+
+ return image;
+}
+
+QPixmap convertHIconToPixmap( const HICON icon)
+{
+ bool foundAlpha = false;
+ HDC screenDevice = GetDC(0);
+ HDC hdc = CreateCompatibleDC(screenDevice);
+ ReleaseDC(0, screenDevice);
+
+ ICONINFO iconinfo;
+ bool result = GetIconInfo(icon, &iconinfo); //x and y Hotspot describes the icon center
+ if (!result)
+ qWarning("convertHIconToPixmap(), failed to GetIconInfo()");
+
+ int w = iconinfo.xHotspot * 2;
+ int h = iconinfo.yHotspot * 2;
+
+ BITMAPINFOHEADER bitmapInfo;
+ bitmapInfo.biSize = sizeof(BITMAPINFOHEADER);
+ bitmapInfo.biWidth = w;
+ bitmapInfo.biHeight = h;
+ bitmapInfo.biPlanes = 1;
+ bitmapInfo.biBitCount = 32;
+ bitmapInfo.biCompression = BI_RGB;
+ bitmapInfo.biSizeImage = 0;
+ bitmapInfo.biXPelsPerMeter = 0;
+ bitmapInfo.biYPelsPerMeter = 0;
+ bitmapInfo.biClrUsed = 0;
+ bitmapInfo.biClrImportant = 0;
+ DWORD* bits;
+
+ HBITMAP winBitmap = CreateDIBSection(hdc, (BITMAPINFO*)&bitmapInfo, DIB_RGB_COLORS, (VOID**)&bits, NULL, 0);
+ HGDIOBJ oldhdc = (HBITMAP)SelectObject(hdc, winBitmap);
+ DrawIconEx( hdc, 0, 0, icon, iconinfo.xHotspot * 2, iconinfo.yHotspot * 2, 0, 0, DI_NORMAL);
+ QImage image = qt_fromWinHBITMAP(hdc, winBitmap, w, h);
+
+ for (int y = 0 ; y < h && !foundAlpha ; y++) {
+ QRgb *scanLine= reinterpret_cast<QRgb *>(image.scanLine(y));
+ for (int x = 0; x < w ; x++) {
+ if (qAlpha(scanLine[x]) != 0) {
+ foundAlpha = true;
+ break;
+ }
+ }
+ }
+ if (!foundAlpha) {
+ //If no alpha was found, we use the mask to set alpha values
+ DrawIconEx( hdc, 0, 0, icon, w, h, 0, 0, DI_MASK);
+ QImage mask = qt_fromWinHBITMAP(hdc, winBitmap, w, h);
+
+ for (int y = 0 ; y < h ; y++){
+ QRgb *scanlineImage = reinterpret_cast<QRgb *>(image.scanLine(y));
+ QRgb *scanlineMask = mask.isNull() ? 0 : reinterpret_cast<QRgb *>(mask.scanLine(y));
+ for (int x = 0; x < w ; x++){
+ if (scanlineMask && qRed(scanlineMask[x]) != 0)
+ scanlineImage[x] = 0; //mask out this pixel
+ else
+ scanlineImage[x] |= 0xff000000; // set the alpha channel to 255
+ }
+ }
+ }
+ //dispose resources created by iconinfo call
+ DeleteObject(iconinfo.hbmMask);
+ DeleteObject(iconinfo.hbmColor);
+
+ SelectObject(hdc, oldhdc); //restore state
+ DeleteObject(winBitmap);
+ DeleteDC(hdc);
+ return QPixmap::fromImage(image);
+}
+#else //ifndef Q_OS_WINCE
+QPixmap convertHIconToPixmap( const HICON icon, bool large)
+{
+ HDC screenDevice = GetDC(0);
+ HDC hdc = CreateCompatibleDC(screenDevice);
+ ReleaseDC(0, screenDevice);
+
+ int size = large ? 64 : 32;
+
+ BITMAPINFO bmi;
+ memset(&bmi, 0, sizeof(bmi));
+ bmi.bmiHeader.biSize = sizeof(BITMAPINFO);
+ bmi.bmiHeader.biWidth = size;
+ bmi.bmiHeader.biHeight = -size;
+ bmi.bmiHeader.biPlanes = 1;
+ bmi.bmiHeader.biBitCount = 32;
+ bmi.bmiHeader.biCompression = BI_RGB;
+ bmi.bmiHeader.biSizeImage = size*size*4;
+
+ uchar* bits;
+
+ HBITMAP winBitmap = CreateDIBSection(hdc, &bmi, DIB_RGB_COLORS, (void**) &bits, 0, 0);
+ if (winBitmap )
+ memset(bits, 0xff, size*size*4);
+ if (!winBitmap) {
+ qWarning("convertHIconToPixmap(), failed to CreateDIBSection()");
+ return QPixmap();
+ }
+
+ HGDIOBJ oldhdc = (HBITMAP)SelectObject(hdc, winBitmap);
+ if (!DrawIconEx( hdc, 0, 0, icon, size, size, 0, 0, DI_NORMAL))
+ qWarning("convertHIconToPixmap(), failed to DrawIcon()");
+
+ uint mask = 0xff000000;
+ // Create image and copy data into image.
+ QImage image(size, size, QImage::Format_ARGB32);
+
+ if (!image.isNull()) { // failed to alloc?
+ int bytes_per_line = size * sizeof(QRgb);
+ for (int y=0; y<size; ++y) {
+ QRgb *dest = (QRgb *) image.scanLine(y);
+ const QRgb *src = (const QRgb *) (bits + y * bytes_per_line);
+ for (int x=0; x<size; ++x) {
+ dest[x] = src[x];
+ }
+ }
+ }
+ if (!DrawIconEx( hdc, 0, 0, icon, size, size, 0, 0, DI_MASK))
+ qWarning("convertHIconToPixmap(), failed to DrawIcon()");
+ if (!image.isNull()) { // failed to alloc?
+ int bytes_per_line = size * sizeof(QRgb);
+ for (int y=0; y<size; ++y) {
+ QRgb *dest = (QRgb *) image.scanLine(y);
+ const QRgb *src = (const QRgb *) (bits + y * bytes_per_line);
+ for (int x=0; x<size; ++x) {
+ if (!src[x])
+ dest[x] = dest[x] | mask;
+ }
+ }
+ }
+ SelectObject(hdc, oldhdc); //restore state
+ DeleteObject(winBitmap);
+ DeleteDC(hdc);
+ return QPixmap::fromImage(image);
+}
+#endif //ifndef Q_OS_WINCE
+
+QPixmap loadIconFromShell32( int resourceId, int size )
+{
+#ifdef Q_OS_WINCE
+ HMODULE hmod = LoadLibrary((const wchar_t *) QString::fromLatin1("ceshell.dll").utf16());
+#else
+ HMODULE hmod = LoadLibraryA("shell32.dll");
+#endif
+ if( hmod ) {
+ HICON iconHandle = (HICON)LoadImage(hmod, MAKEINTRESOURCE(resourceId), IMAGE_ICON, size, size, 0);
+ if( iconHandle ) {
+ QPixmap iconpixmap = convertHIconToPixmap( iconHandle );
+ DestroyIcon(iconHandle);
+ return iconpixmap;
+ }
+ }
+ return QPixmap();
+}
+
+#endif
+
+QT_END_NAMESPACE
diff --git a/src/gui/image/qpixmap_x11.cpp b/src/gui/image/qpixmap_x11.cpp
new file mode 100644
index 0000000000..d7582217e3
--- /dev/null
+++ b/src/gui/image/qpixmap_x11.cpp
@@ -0,0 +1,2291 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+// Uncomment the next line to enable the MIT Shared Memory extension
+//
+// WARNING: This has some problems:
+//
+// 1. Consumes a 800x600 pixmap
+// 2. Qt does not handle the ShmCompletion message, so you will
+// get strange effects if you xForm() repeatedly.
+//
+// #define QT_MITSHM
+
+#if defined(Q_OS_WIN32) && defined(QT_MITSHM)
+#undef QT_MITSHM
+#endif
+
+#include "qplatformdefs.h"
+
+#include "qdebug.h"
+#include "qiodevice.h"
+#include "qpixmap_x11_p.h"
+#include "qbitmap.h"
+#include "qcolormap.h"
+#include "qimage.h"
+#include "qmatrix.h"
+#include "qapplication.h"
+#include <private/qpaintengine_x11_p.h>
+#include <private/qt_x11_p.h>
+#include "qx11info_x11.h"
+#include <private/qdrawhelper_p.h>
+
+#include <stdlib.h>
+
+#if defined(Q_CC_MIPS)
+# define for if(0){}else for
+#endif
+
+QT_BEGIN_NAMESPACE
+
+QPixmap qt_toX11Pixmap(const QImage &image)
+{
+ QPixmapData *data =
+ new QX11PixmapData(image.depth() == 1
+ ? QPixmapData::BitmapType
+ : QPixmapData::PixmapType);
+
+ data->fromImage(image, Qt::AutoColor);
+
+ return QPixmap(data);
+}
+
+QPixmap qt_toX11Pixmap(const QPixmap &pixmap)
+{
+ if (pixmap.isNull())
+ return QPixmap();
+
+ if (QPixmap(pixmap).data_ptr()->classId() == QPixmapData::X11Class)
+ return pixmap;
+
+ return qt_toX11Pixmap(pixmap.toImage());
+}
+
+// For thread-safety:
+// image->data does not belong to X11, so we must free it ourselves.
+
+inline static void qSafeXDestroyImage(XImage *x)
+{
+ if (x->data) {
+ free(x->data);
+ x->data = 0;
+ }
+ XDestroyImage(x);
+}
+
+QBitmap QX11PixmapData::mask_to_bitmap(int screen) const
+{
+ if (!x11_mask)
+ return QBitmap();
+ QPixmap::x11SetDefaultScreen(screen);
+ QBitmap bm(w, h);
+ GC gc = XCreateGC(X11->display, bm.handle(), 0, 0);
+ XCopyArea(X11->display, x11_mask, bm.handle(), gc, 0, 0,
+ bm.data->width(), bm.data->height(), 0, 0);
+ XFreeGC(X11->display, gc);
+ return bm;
+}
+
+Qt::HANDLE QX11PixmapData::bitmap_to_mask(const QBitmap &bitmap, int screen)
+{
+ if (bitmap.isNull())
+ return 0;
+ QBitmap bm = bitmap;
+ bm.x11SetScreen(screen);
+
+ Pixmap mask = XCreatePixmap(X11->display, RootWindow(X11->display, screen),
+ bm.data->width(), bm.data->height(), 1);
+ GC gc = XCreateGC(X11->display, mask, 0, 0);
+ XCopyArea(X11->display, bm.handle(), mask, gc, 0, 0,
+ bm.data->width(), bm.data->height(), 0, 0);
+ XFreeGC(X11->display, gc);
+ return mask;
+}
+
+
+/*****************************************************************************
+ MIT Shared Memory Extension support: makes xForm noticeably (~20%) faster.
+ *****************************************************************************/
+
+#if defined(QT_MITSHM)
+
+static bool xshminit = false;
+static XShmSegmentInfo xshminfo;
+static XImage *xshmimg = 0;
+static Pixmap xshmpm = 0;
+
+static void qt_cleanup_mitshm()
+{
+ if (xshmimg == 0)
+ return;
+ Display *dpy = QX11Info::appDisplay();
+ if (xshmpm) {
+ XFreePixmap(dpy, xshmpm);
+ xshmpm = 0;
+ }
+ XShmDetach(dpy, &xshminfo); xshmimg->data = 0;
+ qSafeXDestroyImage(xshmimg); xshmimg = 0;
+ shmdt(xshminfo.shmaddr);
+ shmctl(xshminfo.shmid, IPC_RMID, 0);
+}
+
+static bool qt_create_mitshm_buffer(const QPaintDevice* dev, int w, int h)
+{
+ static int major, minor;
+ static Bool pixmaps_ok;
+ Display *dpy = dev->data->xinfo->display();
+ int dd = dev->x11Depth();
+ Visual *vis = (Visual*)dev->x11Visual();
+
+ if (xshminit) {
+ qt_cleanup_mitshm();
+ } else {
+ if (!XShmQueryVersion(dpy, &major, &minor, &pixmaps_ok))
+ return false; // MIT Shm not supported
+ qAddPostRoutine(qt_cleanup_mitshm);
+ xshminit = true;
+ }
+
+ xshmimg = XShmCreateImage(dpy, vis, dd, ZPixmap, 0, &xshminfo, w, h);
+ if (!xshmimg)
+ return false;
+
+ bool ok;
+ xshminfo.shmid = shmget(IPC_PRIVATE,
+ xshmimg->bytes_per_line * xshmimg->height,
+ IPC_CREAT | 0777);
+ ok = xshminfo.shmid != -1;
+ if (ok) {
+ xshmimg->data = (char*)shmat(xshminfo.shmid, 0, 0);
+ xshminfo.shmaddr = xshmimg->data;
+ ok = (xshminfo.shmaddr != (char*)-1);
+ }
+ xshminfo.readOnly = false;
+ if (ok)
+ ok = XShmAttach(dpy, &xshminfo);
+ if (!ok) {
+ qSafeXDestroyImage(xshmimg);
+ xshmimg = 0;
+ if (xshminfo.shmaddr)
+ shmdt(xshminfo.shmaddr);
+ if (xshminfo.shmid != -1)
+ shmctl(xshminfo.shmid, IPC_RMID, 0);
+ return false;
+ }
+ if (pixmaps_ok)
+ xshmpm = XShmCreatePixmap(dpy, DefaultRootWindow(dpy), xshmimg->data,
+ &xshminfo, w, h, dd);
+
+ return true;
+}
+
+#else
+
+// If extern, need a dummy.
+//
+// static bool qt_create_mitshm_buffer(QPaintDevice*, int, int)
+// {
+// return false;
+// }
+
+#endif // QT_MITSHM
+
+
+/*****************************************************************************
+ Internal functions
+ *****************************************************************************/
+
+extern const uchar *qt_get_bitflip_array(); // defined in qimage.cpp
+
+// Returns position of highest bit set or -1 if none
+static int highest_bit(uint v)
+{
+ int i;
+ uint b = (uint)1 << 31;
+ for (i=31; ((b & v) == 0) && i>=0; i--)
+ b >>= 1;
+ return i;
+}
+
+// Returns position of lowest set bit in 'v' as an integer (0-31), or -1
+static int lowest_bit(uint v)
+{
+ int i;
+ ulong lb;
+ lb = 1;
+ for (i=0; ((v & lb) == 0) && i<32; i++, lb<<=1) {}
+ return i==32 ? -1 : i;
+}
+
+// Counts the number of bits set in 'v'
+static uint n_bits(uint v)
+{
+ int i = 0;
+ while (v) {
+ v = v & (v - 1);
+ i++;
+ }
+ return i;
+}
+
+static uint *red_scale_table = 0;
+static uint *green_scale_table = 0;
+static uint *blue_scale_table = 0;
+
+static void cleanup_scale_tables()
+{
+ delete[] red_scale_table;
+ delete[] green_scale_table;
+ delete[] blue_scale_table;
+}
+
+/*
+ Could do smart bitshifting, but the "obvious" algorithm only works for
+ nBits >= 4. This is more robust.
+*/
+static void build_scale_table(uint **table, uint nBits)
+{
+ if (nBits > 7) {
+ qWarning("build_scale_table: internal error, nBits = %i", nBits);
+ return;
+ }
+ if (!*table) {
+ static bool firstTable = true;
+ if (firstTable) {
+ qAddPostRoutine(cleanup_scale_tables);
+ firstTable = false;
+ }
+ *table = new uint[256];
+ }
+ int maxVal = (1 << nBits) - 1;
+ int valShift = 8 - nBits;
+ int i;
+ for(i = 0 ; i < maxVal + 1 ; i++)
+ (*table)[i << valShift] = i*255/maxVal;
+}
+
+static int defaultScreen = -1;
+
+/*****************************************************************************
+ QPixmap member functions
+ *****************************************************************************/
+
+static int qt_pixmap_serial = 0;
+int Q_GUI_EXPORT qt_x11_preferred_pixmap_depth = 0;
+
+QX11PixmapData::QX11PixmapData(PixelType type)
+ : QPixmapData(type, X11Class), hd(0), w(0), h(0), d(0),
+ uninit(true), read_only(false), x11_mask(0), picture(0), mask_picture(0), hd2(0),
+ share_mode(QPixmap::ImplicitlyShared), pengine(0)
+{
+}
+
+void QX11PixmapData::resize(int width, int height)
+{
+ setSerialNumber(++qt_pixmap_serial);
+
+ w = width;
+ h = height;
+
+ if (defaultScreen >= 0 && defaultScreen != xinfo.screen()) {
+ QX11InfoData* xd = xinfo.getX11Data(true);
+ xd->screen = defaultScreen;
+ xd->depth = QX11Info::appDepth(xd->screen);
+ xd->cells = QX11Info::appCells(xd->screen);
+ xd->colormap = QX11Info::appColormap(xd->screen);
+ xd->defaultColormap = QX11Info::appDefaultColormap(xd->screen);
+ xd->visual = (Visual *)QX11Info::appVisual(xd->screen);
+ xd->defaultVisual = QX11Info::appDefaultVisual(xd->screen);
+ xinfo.setX11Data(xd);
+ }
+
+ int dd = xinfo.depth();
+
+ if (qt_x11_preferred_pixmap_depth)
+ dd = qt_x11_preferred_pixmap_depth;
+
+ bool make_null = w <= 0 || h <= 0; // create null pixmap
+ d = (pixelType() == BitmapType ? 1 : dd);
+ if (make_null || d == 0) {
+ w = 0;
+ h = 0;
+ hd = 0;
+ picture = 0;
+ d = 0;
+ if (!make_null)
+ qWarning("QPixmap: Invalid pixmap parameters");
+ return;
+ }
+ hd = (Qt::HANDLE)XCreatePixmap(X11->display,
+ RootWindow(X11->display, xinfo.screen()),
+ w, h, d);
+#ifndef QT_NO_XRENDER
+ if (X11->use_xrender) {
+ XRenderPictFormat *format = d == 1
+ ? XRenderFindStandardFormat(X11->display, PictStandardA1)
+ : XRenderFindVisualFormat(X11->display, (Visual *)xinfo.visual());
+ picture = XRenderCreatePicture(X11->display, hd, format, 0, 0);
+ }
+#endif // QT_NO_XRENDER
+}
+
+void QX11PixmapData::fromImage(const QImage &img,
+ Qt::ImageConversionFlags flags)
+{
+ setSerialNumber(++qt_pixmap_serial);
+
+ w = img.width();
+ h = img.height();
+ d = img.depth();
+
+ if (defaultScreen >= 0 && defaultScreen != xinfo.screen()) {
+ QX11InfoData* xd = xinfo.getX11Data(true);
+ xd->screen = defaultScreen;
+ xd->depth = QX11Info::appDepth(xd->screen);
+ xd->cells = QX11Info::appCells(xd->screen);
+ xd->colormap = QX11Info::appColormap(xd->screen);
+ xd->defaultColormap = QX11Info::appDefaultColormap(xd->screen);
+ xd->visual = (Visual *)QX11Info::appVisual(xd->screen);
+ xd->defaultVisual = QX11Info::appDefaultVisual(xd->screen);
+ xinfo.setX11Data(xd);
+ }
+
+ if (pixelType() == BitmapType) {
+ bitmapFromImage(img);
+ return;
+ }
+
+ if (uint(w) >= 32768 || uint(h) >= 32768) {
+ w = h = 0;
+ return;
+ }
+
+ int dd = X11->use_xrender && img.hasAlphaChannel() ? 32 : xinfo.depth();
+ if (qt_x11_preferred_pixmap_depth)
+ dd = qt_x11_preferred_pixmap_depth;
+
+ QImage image = img;
+
+ // must be monochrome
+ if (dd == 1 || (flags & Qt::ColorMode_Mask) == Qt::MonoOnly) {
+ if (d != 1) {
+ // dither
+ image = image.convertToFormat(QImage::Format_MonoLSB, flags);
+ d = 1;
+ }
+ } else { // can be both
+ bool conv8 = false;
+ if (d > 8 && dd <= 8) { // convert to 8 bit
+ if ((flags & Qt::DitherMode_Mask) == Qt::AutoDither)
+ flags = (flags & ~Qt::DitherMode_Mask)
+ | Qt::PreferDither;
+ conv8 = true;
+ } else if ((flags & Qt::ColorMode_Mask) == Qt::ColorOnly) {
+ conv8 = (d == 1); // native depth wanted
+ } else if (d == 1) {
+ if (image.numColors() == 2) {
+ QRgb c0 = image.color(0); // Auto: convert to best
+ QRgb c1 = image.color(1);
+ conv8 = qMin(c0,c1) != qRgb(0,0,0) || qMax(c0,c1) != qRgb(255,255,255);
+ } else {
+ // eg. 1-color monochrome images (they do exist).
+ conv8 = true;
+ }
+ }
+ if (conv8) {
+ image = image.convertToFormat(QImage::Format_Indexed8, flags);
+ d = 8;
+ }
+ }
+
+ if (d == 1 || d == 16 || d == 24) {
+ image = image.convertToFormat(QImage::Format_RGB32, flags);
+ fromImage(image, Qt::AutoColor);
+ return;
+ }
+
+ Display *dpy = X11->display;
+ Visual *visual = (Visual *)xinfo.visual();
+ XImage *xi = 0;
+ bool trucol = (visual->c_class >= TrueColor);
+ int nbytes = image.numBytes();
+ uchar *newbits= 0;
+
+#ifndef QT_NO_XRENDER
+ if (X11->use_xrender && image.hasAlphaChannel()) {
+ const QImage &cimage = image;
+
+ d = 32;
+
+ if (QX11Info::appDepth() != d) {
+ if (xinfo.x11data) {
+ xinfo.x11data->depth = d;
+ } else {
+ QX11InfoData *xd = xinfo.getX11Data(true);
+ xd->screen = QX11Info::appScreen();
+ xd->depth = d;
+ xd->cells = QX11Info::appCells();
+ xd->colormap = QX11Info::appColormap();
+ xd->defaultColormap = QX11Info::appDefaultColormap();
+ xd->visual = (Visual *)QX11Info::appVisual();
+ xd->defaultVisual = QX11Info::appDefaultVisual();
+ xinfo.setX11Data(xd);
+ }
+ }
+
+ hd = (Qt::HANDLE)XCreatePixmap(dpy, RootWindow(dpy, xinfo.screen()),
+ w, h, d);
+ picture = XRenderCreatePicture(X11->display, hd,
+ XRenderFindStandardFormat(X11->display, PictStandardARGB32), 0, 0);
+
+ xi = XCreateImage(dpy, visual, d, ZPixmap, 0, 0, w, h, 32, 0);
+ Q_CHECK_PTR(xi);
+ newbits = (uchar *)malloc(xi->bytes_per_line*h);
+ Q_CHECK_PTR(newbits);
+ xi->data = (char *)newbits;
+
+ switch(cimage.format()) {
+ case QImage::Format_Indexed8: {
+ QVector<QRgb> colorTable = cimage.colorTable();
+ uint *xidata = (uint *)xi->data;
+ for (int y = 0; y < h; ++y) {
+ const uchar *p = cimage.scanLine(y);
+ for (int x = 0; x < w; ++x) {
+ const QRgb rgb = colorTable[p[x]];
+ const int a = qAlpha(rgb);
+ if (a == 0xff)
+ *xidata = rgb;
+ else
+ // RENDER expects premultiplied alpha
+ *xidata = qRgba(qt_div_255(qRed(rgb) * a),
+ qt_div_255(qGreen(rgb) * a),
+ qt_div_255(qBlue(rgb) * a),
+ a);
+ ++xidata;
+ }
+ }
+ }
+ break;
+ case QImage::Format_RGB32: {
+ uint *xidata = (uint *)xi->data;
+ for (int y = 0; y < h; ++y) {
+ const QRgb *p = (const QRgb *) cimage.scanLine(y);
+ for (int x = 0; x < w; ++x)
+ *xidata++ = p[x] | 0xff000000;
+ }
+ }
+ break;
+ case QImage::Format_ARGB32: {
+ uint *xidata = (uint *)xi->data;
+ for (int y = 0; y < h; ++y) {
+ const QRgb *p = (const QRgb *) cimage.scanLine(y);
+ for (int x = 0; x < w; ++x) {
+ const QRgb rgb = p[x];
+ const int a = qAlpha(rgb);
+ if (a == 0xff)
+ *xidata = rgb;
+ else
+ // RENDER expects premultiplied alpha
+ *xidata = qRgba(qt_div_255(qRed(rgb) * a),
+ qt_div_255(qGreen(rgb) * a),
+ qt_div_255(qBlue(rgb) * a),
+ a);
+ ++xidata;
+ }
+ }
+
+ }
+ break;
+ case QImage::Format_ARGB32_Premultiplied: {
+ uint *xidata = (uint *)xi->data;
+ for (int y = 0; y < h; ++y) {
+ const QRgb *p = (const QRgb *) cimage.scanLine(y);
+ memcpy(xidata, p, w*sizeof(QRgb));
+ xidata += w;
+ }
+ }
+ break;
+ default:
+ Q_ASSERT(false);
+ }
+
+ if ((xi->byte_order == MSBFirst) != (QSysInfo::ByteOrder == QSysInfo::BigEndian)) {
+ uint *xidata = (uint *)xi->data;
+ uint *xiend = xidata + w*h;
+ while (xidata < xiend) {
+ *xidata = (*xidata >> 24)
+ | ((*xidata >> 8) & 0xff00)
+ | ((*xidata << 8) & 0xff0000)
+ | (*xidata << 24);
+ ++xidata;
+ }
+ }
+
+ GC gc = XCreateGC(dpy, hd, 0, 0);
+ XPutImage(dpy, hd, gc, xi, 0, 0, 0, 0, w, h);
+ XFreeGC(dpy, gc);
+
+ qSafeXDestroyImage(xi);
+
+ return;
+ }
+#endif // QT_NO_XRENDER
+
+ if (trucol) { // truecolor display
+ if (image.format() == QImage::Format_ARGB32_Premultiplied)
+ image = image.convertToFormat(QImage::Format_ARGB32);
+
+ const QImage &cimage = image;
+ QRgb pix[256]; // pixel translation table
+ const bool d8 = (d == 8);
+ const uint red_mask = (uint)visual->red_mask;
+ const uint green_mask = (uint)visual->green_mask;
+ const uint blue_mask = (uint)visual->blue_mask;
+ const int red_shift = highest_bit(red_mask) - 7;
+ const int green_shift = highest_bit(green_mask) - 7;
+ const int blue_shift = highest_bit(blue_mask) - 7;
+ const uint rbits = highest_bit(red_mask) - lowest_bit(red_mask) + 1;
+ const uint gbits = highest_bit(green_mask) - lowest_bit(green_mask) + 1;
+ const uint bbits = highest_bit(blue_mask) - lowest_bit(blue_mask) + 1;
+
+ if (d8) { // setup pixel translation
+ QVector<QRgb> ctable = cimage.colorTable();
+ for (int i=0; i < cimage.numColors(); i++) {
+ int r = qRed (ctable[i]);
+ int g = qGreen(ctable[i]);
+ int b = qBlue (ctable[i]);
+ r = red_shift > 0 ? r << red_shift : r >> -red_shift;
+ g = green_shift > 0 ? g << green_shift : g >> -green_shift;
+ b = blue_shift > 0 ? b << blue_shift : b >> -blue_shift;
+ pix[i] = (b & blue_mask) | (g & green_mask) | (r & red_mask)
+ | ~(blue_mask | green_mask | red_mask);
+ }
+ }
+
+ xi = XCreateImage(dpy, visual, dd, ZPixmap, 0, 0, w, h, 32, 0);
+ Q_CHECK_PTR(xi);
+ newbits = (uchar *)malloc(xi->bytes_per_line*h);
+ Q_CHECK_PTR(newbits);
+ if (!newbits) // no memory
+ return;
+ int bppc = xi->bits_per_pixel;
+
+ bool contig_bits = n_bits(red_mask) == rbits &&
+ n_bits(green_mask) == gbits &&
+ n_bits(blue_mask) == bbits;
+ bool dither_tc =
+ // Want it?
+ (flags & Qt::Dither_Mask) != Qt::ThresholdDither &&
+ (flags & Qt::DitherMode_Mask) != Qt::AvoidDither &&
+ // Need it?
+ bppc < 24 && !d8 &&
+ // Can do it? (Contiguous bits?)
+ contig_bits;
+
+ static bool init=false;
+ static int D[16][16];
+ if (dither_tc && !init) {
+ // I also contributed this code to XV - WWA.
+ /*
+ The dither matrix, D, is obtained with this formula:
+
+ D2 = [0 2]
+ [3 1]
+
+
+ D2*n = [4*Dn 4*Dn+2*Un]
+ [4*Dn+3*Un 4*Dn+1*Un]
+ */
+ int n,i,j;
+ init=1;
+
+ /* Set D2 */
+ D[0][0]=0;
+ D[1][0]=2;
+ D[0][1]=3;
+ D[1][1]=1;
+
+ /* Expand using recursive definition given above */
+ for (n=2; n<16; n*=2) {
+ for (i=0; i<n; i++) {
+ for (j=0; j<n; j++) {
+ D[i][j]*=4;
+ D[i+n][j]=D[i][j]+2;
+ D[i][j+n]=D[i][j]+3;
+ D[i+n][j+n]=D[i][j]+1;
+ }
+ }
+ }
+ init=true;
+ }
+
+ enum { BPP8,
+ BPP16_565, BPP16_555,
+ BPP16_MSB, BPP16_LSB,
+ BPP24_888,
+ BPP24_MSB, BPP24_LSB,
+ BPP32_8888,
+ BPP32_MSB, BPP32_LSB
+ } mode = BPP8;
+
+ bool same_msb_lsb = (xi->byte_order == MSBFirst) == (QSysInfo::ByteOrder == QSysInfo::BigEndian);
+
+ if(bppc == 8) // 8 bit
+ mode = BPP8;
+ else if(bppc == 16) { // 16 bit MSB/LSB
+ if(red_shift == 8 && green_shift == 3 && blue_shift == -3 && !d8 && same_msb_lsb)
+ mode = BPP16_565;
+ else if(red_shift == 7 && green_shift == 2 && blue_shift == -3 && !d8 && same_msb_lsb)
+ mode = BPP16_555;
+ else
+ mode = (xi->byte_order == LSBFirst) ? BPP16_LSB : BPP16_MSB;
+ } else if(bppc == 24) { // 24 bit MSB/LSB
+ if (red_shift == 16 && green_shift == 8 && blue_shift == 0 && !d8 && same_msb_lsb)
+ mode = BPP24_888;
+ else
+ mode = (xi->byte_order == LSBFirst) ? BPP24_LSB : BPP24_MSB;
+ } else if(bppc == 32) { // 32 bit MSB/LSB
+ if(red_shift == 16 && green_shift == 8 && blue_shift == 0 && !d8 && same_msb_lsb)
+ mode = BPP32_8888;
+ else
+ mode = (xi->byte_order == LSBFirst) ? BPP32_LSB : BPP32_MSB;
+ } else
+ qFatal("Logic error 3");
+
+#define GET_PIXEL \
+ uint pixel; \
+ if (d8) pixel = pix[*src++]; \
+ else { \
+ int r = qRed (*p); \
+ int g = qGreen(*p); \
+ int b = qBlue (*p++); \
+ r = red_shift > 0 \
+ ? r << red_shift : r >> -red_shift; \
+ g = green_shift > 0 \
+ ? g << green_shift : g >> -green_shift; \
+ b = blue_shift > 0 \
+ ? b << blue_shift : b >> -blue_shift; \
+ pixel = (r & red_mask)|(g & green_mask) | (b & blue_mask) \
+ | ~(blue_mask | green_mask | red_mask); \
+ }
+
+#define GET_PIXEL_DITHER_TC \
+ int r = qRed (*p); \
+ int g = qGreen(*p); \
+ int b = qBlue (*p++); \
+ const int thres = D[x%16][y%16]; \
+ if (r <= (255-(1<<(8-rbits))) && ((r<<rbits) & 255) \
+ > thres) \
+ r += (1<<(8-rbits)); \
+ if (g <= (255-(1<<(8-gbits))) && ((g<<gbits) & 255) \
+ > thres) \
+ g += (1<<(8-gbits)); \
+ if (b <= (255-(1<<(8-bbits))) && ((b<<bbits) & 255) \
+ > thres) \
+ b += (1<<(8-bbits)); \
+ r = red_shift > 0 \
+ ? r << red_shift : r >> -red_shift; \
+ g = green_shift > 0 \
+ ? g << green_shift : g >> -green_shift; \
+ b = blue_shift > 0 \
+ ? b << blue_shift : b >> -blue_shift; \
+ uint pixel = (r & red_mask)|(g & green_mask) | (b & blue_mask);
+
+// again, optimized case
+// can't be optimized that much :(
+#define GET_PIXEL_DITHER_TC_OPT(red_shift,green_shift,blue_shift,red_mask,green_mask,blue_mask, \
+ rbits,gbits,bbits) \
+ const int thres = D[x%16][y%16]; \
+ int r = qRed (*p); \
+ if (r <= (255-(1<<(8-rbits))) && ((r<<rbits) & 255) \
+ > thres) \
+ r += (1<<(8-rbits)); \
+ int g = qGreen(*p); \
+ if (g <= (255-(1<<(8-gbits))) && ((g<<gbits) & 255) \
+ > thres) \
+ g += (1<<(8-gbits)); \
+ int b = qBlue (*p++); \
+ if (b <= (255-(1<<(8-bbits))) && ((b<<bbits) & 255) \
+ > thres) \
+ b += (1<<(8-bbits)); \
+ uint pixel = ((r red_shift) & red_mask) \
+ | ((g green_shift) & green_mask) \
+ | ((b blue_shift) & blue_mask);
+
+#define CYCLE(body) \
+ for (int y=0; y<h; y++) { \
+ const uchar* src = cimage.scanLine(y); \
+ uchar* dst = newbits + xi->bytes_per_line*y; \
+ const QRgb* p = (const QRgb *)src; \
+ body \
+ }
+
+ if (dither_tc) {
+ switch (mode) {
+ case BPP16_565:
+ CYCLE(
+ quint16* dst16 = (quint16*)dst;
+ for (int x=0; x<w; x++) {
+ GET_PIXEL_DITHER_TC_OPT(<<8,<<3,>>3,0xf800,0x7e0,0x1f,5,6,5)
+ *dst16++ = pixel;
+ }
+ )
+ break;
+ case BPP16_555:
+ CYCLE(
+ quint16* dst16 = (quint16*)dst;
+ for (int x=0; x<w; x++) {
+ GET_PIXEL_DITHER_TC_OPT(<<7,<<2,>>3,0x7c00,0x3e0,0x1f,5,5,5)
+ *dst16++ = pixel;
+ }
+ )
+ break;
+ case BPP16_MSB: // 16 bit MSB
+ CYCLE(
+ for (int x=0; x<w; x++) {
+ GET_PIXEL_DITHER_TC
+ *dst++ = (pixel >> 8);
+ *dst++ = pixel;
+ }
+ )
+ break;
+ case BPP16_LSB: // 16 bit LSB
+ CYCLE(
+ for (int x=0; x<w; x++) {
+ GET_PIXEL_DITHER_TC
+ *dst++ = pixel;
+ *dst++ = pixel >> 8;
+ }
+ )
+ break;
+ default:
+ qFatal("Logic error");
+ }
+ } else {
+ switch (mode) {
+ case BPP8: // 8 bit
+ CYCLE(
+ Q_UNUSED(p);
+ for (int x=0; x<w; x++)
+ *dst++ = pix[*src++];
+ )
+ break;
+ case BPP16_565:
+ CYCLE(
+ quint16* dst16 = (quint16*)dst;
+ for (int x = 0; x < w; x++) {
+ *dst16++ = ((*p >> 8) & 0xf800)
+ | ((*p >> 5) & 0x7e0)
+ | ((*p >> 3) & 0x1f);
+ ++p;
+ }
+ )
+ break;
+ case BPP16_555:
+ CYCLE(
+ quint16* dst16 = (quint16*)dst;
+ for (int x=0; x<w; x++) {
+ *dst16++ = ((*p >> 9) & 0x7c00)
+ | ((*p >> 6) & 0x3e0)
+ | ((*p >> 3) & 0x1f);
+ ++p;
+ }
+ )
+ break;
+ case BPP16_MSB: // 16 bit MSB
+ CYCLE(
+ for (int x=0; x<w; x++) {
+ GET_PIXEL
+ *dst++ = (pixel >> 8);
+ *dst++ = pixel;
+ }
+ )
+ break;
+ case BPP16_LSB: // 16 bit LSB
+ CYCLE(
+ for (int x=0; x<w; x++) {
+ GET_PIXEL
+ *dst++ = pixel;
+ *dst++ = pixel >> 8;
+ }
+ )
+ break;
+ case BPP24_888: // 24 bit MSB
+ CYCLE(
+ for (int x=0; x<w; x++) {
+ *dst++ = qRed (*p);
+ *dst++ = qGreen(*p);
+ *dst++ = qBlue (*p++);
+ }
+ )
+ break;
+ case BPP24_MSB: // 24 bit MSB
+ CYCLE(
+ for (int x=0; x<w; x++) {
+ GET_PIXEL
+ *dst++ = pixel >> 16;
+ *dst++ = pixel >> 8;
+ *dst++ = pixel;
+ }
+ )
+ break;
+ case BPP24_LSB: // 24 bit LSB
+ CYCLE(
+ for (int x=0; x<w; x++) {
+ GET_PIXEL
+ *dst++ = pixel;
+ *dst++ = pixel >> 8;
+ *dst++ = pixel >> 16;
+ }
+ )
+ break;
+ case BPP32_8888:
+ CYCLE(
+ memcpy(dst, p, w * 4);
+ )
+ break;
+ case BPP32_MSB: // 32 bit MSB
+ CYCLE(
+ for (int x=0; x<w; x++) {
+ GET_PIXEL
+ *dst++ = pixel >> 24;
+ *dst++ = pixel >> 16;
+ *dst++ = pixel >> 8;
+ *dst++ = pixel;
+ }
+ )
+ break;
+ case BPP32_LSB: // 32 bit LSB
+ CYCLE(
+ for (int x=0; x<w; x++) {
+ GET_PIXEL
+ *dst++ = pixel;
+ *dst++ = pixel >> 8;
+ *dst++ = pixel >> 16;
+ *dst++ = pixel >> 24;
+ }
+ )
+ break;
+ default:
+ qFatal("Logic error 2");
+ }
+ }
+ xi->data = (char *)newbits;
+ }
+
+ if (d == 8 && !trucol) { // 8 bit pixmap
+ int pop[256]; // pixel popularity
+
+ if (image.numColors() == 0)
+ image.setNumColors(1);
+
+ const QImage &cimage = image;
+ memset(pop, 0, sizeof(int)*256); // reset popularity array
+ for (int i = 0; i < h; i++) { // for each scanline...
+ const uchar* p = cimage.scanLine(i);
+ const uchar *end = p + w;
+ while (p < end) // compute popularity
+ pop[*p++]++;
+ }
+
+ newbits = (uchar *)malloc(nbytes); // copy image into newbits
+ Q_CHECK_PTR(newbits);
+ if (!newbits) // no memory
+ return;
+ uchar* p = newbits;
+ memcpy(p, cimage.bits(), nbytes); // copy image data into newbits
+
+ /*
+ * The code below picks the most important colors. It is based on the
+ * diversity algorithm, implemented in XV 3.10. XV is (C) by John Bradley.
+ */
+
+ struct PIX { // pixel sort element
+ uchar r,g,b,n; // color + pad
+ int use; // popularity
+ int index; // index in colormap
+ int mindist;
+ };
+ int ncols = 0;
+ for (int i=0; i< cimage.numColors(); i++) { // compute number of colors
+ if (pop[i] > 0)
+ ncols++;
+ }
+ for (int i = cimage.numColors(); i < 256; i++) // ignore out-of-range pixels
+ pop[i] = 0;
+
+ // works since we make sure above to have at least
+ // one color in the image
+ if (ncols == 0)
+ ncols = 1;
+
+ PIX pixarr[256]; // pixel array
+ PIX pixarr_sorted[256]; // pixel array (sorted)
+ memset(pixarr, 0, ncols*sizeof(PIX));
+ PIX *px = &pixarr[0];
+ int maxpop = 0;
+ int maxpix = 0;
+ uint j = 0;
+ QVector<QRgb> ctable = cimage.colorTable();
+ for (int i = 0; i < 256; i++) { // init pixel array
+ if (pop[i] > 0) {
+ px->r = qRed (ctable[i]);
+ px->g = qGreen(ctable[i]);
+ px->b = qBlue (ctable[i]);
+ px->n = 0;
+ px->use = pop[i];
+ if (pop[i] > maxpop) { // select most popular entry
+ maxpop = pop[i];
+ maxpix = j;
+ }
+ px->index = i;
+ px->mindist = 1000000;
+ px++;
+ j++;
+ }
+ }
+ pixarr_sorted[0] = pixarr[maxpix];
+ pixarr[maxpix].use = 0;
+
+ for (int i = 1; i < ncols; i++) { // sort pixels
+ int minpix = -1, mindist = -1;
+ px = &pixarr_sorted[i-1];
+ int r = px->r;
+ int g = px->g;
+ int b = px->b;
+ int dist;
+ if ((i & 1) || i<10) { // sort on max distance
+ for (int j=0; j<ncols; j++) {
+ px = &pixarr[j];
+ if (px->use) {
+ dist = (px->r - r)*(px->r - r) +
+ (px->g - g)*(px->g - g) +
+ (px->b - b)*(px->b - b);
+ if (px->mindist > dist)
+ px->mindist = dist;
+ if (px->mindist > mindist) {
+ mindist = px->mindist;
+ minpix = j;
+ }
+ }
+ }
+ } else { // sort on max popularity
+ for (int j=0; j<ncols; j++) {
+ px = &pixarr[j];
+ if (px->use) {
+ dist = (px->r - r)*(px->r - r) +
+ (px->g - g)*(px->g - g) +
+ (px->b - b)*(px->b - b);
+ if (px->mindist > dist)
+ px->mindist = dist;
+ if (px->use > mindist) {
+ mindist = px->use;
+ minpix = j;
+ }
+ }
+ }
+ }
+ pixarr_sorted[i] = pixarr[minpix];
+ pixarr[minpix].use = 0;
+ }
+
+ QColormap cmap = QColormap::instance(xinfo.screen());
+ uint pix[256]; // pixel translation table
+ px = &pixarr_sorted[0];
+ for (int i = 0; i < ncols; i++) { // allocate colors
+ QColor c(px->r, px->g, px->b);
+ pix[px->index] = cmap.pixel(c);
+ px++;
+ }
+
+ p = newbits;
+ for (int i = 0; i < nbytes; i++) { // translate pixels
+ *p = pix[*p];
+ p++;
+ }
+ }
+
+ if (!xi) { // X image not created
+ xi = XCreateImage(dpy, visual, dd, ZPixmap, 0, 0, w, h, 32, 0);
+ if (xi->bits_per_pixel == 16) { // convert 8 bpp ==> 16 bpp
+ ushort *p2;
+ int p2inc = xi->bytes_per_line/sizeof(ushort);
+ ushort *newerbits = (ushort *)malloc(xi->bytes_per_line * h);
+ Q_CHECK_PTR(newerbits);
+ if (!newerbits) // no memory
+ return;
+ uchar* p = newbits;
+ for (int y = 0; y < h; y++) { // OOPS: Do right byte order!!
+ p2 = newerbits + p2inc*y;
+ for (int x = 0; x < w; x++)
+ *p2++ = *p++;
+ }
+ free(newbits);
+ newbits = (uchar *)newerbits;
+ } else if (xi->bits_per_pixel != 8) {
+ qWarning("QPixmap::fromImage: Display not supported "
+ "(bpp=%d)", xi->bits_per_pixel);
+ }
+ xi->data = (char *)newbits;
+ }
+
+ hd = (Qt::HANDLE)XCreatePixmap(X11->display,
+ RootWindow(X11->display, xinfo.screen()),
+ w, h, dd);
+
+ GC gc = XCreateGC(dpy, hd, 0, 0);
+ XPutImage(dpy, hd, gc, xi, 0, 0, 0, 0, w, h);
+ XFreeGC(dpy, gc);
+
+ qSafeXDestroyImage(xi);
+ d = dd;
+
+#ifndef QT_NO_XRENDER
+ if (X11->use_xrender) {
+ XRenderPictFormat *format = d == 1
+ ? XRenderFindStandardFormat(X11->display, PictStandardA1)
+ : XRenderFindVisualFormat(X11->display, (Visual *)xinfo.visual());
+ picture = XRenderCreatePicture(X11->display, hd, format, 0, 0);
+ }
+#endif
+
+ if (image.hasAlphaChannel()) {
+ QBitmap m = QBitmap::fromImage(image.createAlphaMask(flags));
+ setMask(m);
+ }
+}
+
+void QX11PixmapData::bitmapFromImage(const QImage &image)
+{
+ QImage img = image.convertToFormat(QImage::Format_MonoLSB);
+ const QRgb c0 = QColor(Qt::black).rgb();
+ const QRgb c1 = QColor(Qt::white).rgb();
+ if (img.color(0) == c0 && img.color(1) == c1) {
+ img.invertPixels();
+ img.setColor(0, c1);
+ img.setColor(1, c0);
+ }
+
+ char *bits;
+ uchar *tmp_bits;
+ w = img.width();
+ h = img.height();
+ d = 1;
+ int bpl = (w + 7) / 8;
+ int ibpl = img.bytesPerLine();
+ if (bpl != ibpl) {
+ tmp_bits = new uchar[bpl*h];
+ bits = (char *)tmp_bits;
+ uchar *p, *b;
+ int y;
+ b = tmp_bits;
+ p = img.scanLine(0);
+ for (y = 0; y < h; y++) {
+ memcpy(b, p, bpl);
+ b += bpl;
+ p += ibpl;
+ }
+ } else {
+ bits = (char *)img.bits();
+ tmp_bits = 0;
+ }
+ hd = (Qt::HANDLE)XCreateBitmapFromData(xinfo.display(),
+ RootWindow(xinfo.display(), xinfo.screen()),
+ bits, w, h);
+
+#ifndef QT_NO_XRENDER
+ if (X11->use_xrender)
+ picture = XRenderCreatePicture(X11->display, hd,
+ XRenderFindStandardFormat(X11->display, PictStandardA1), 0, 0);
+#endif // QT_NO_XRENDER
+
+ if (tmp_bits) // Avoid purify complaint
+ delete [] tmp_bits;
+}
+
+void QX11PixmapData::fill(const QColor &fillColor)
+{
+ if (fillColor.alpha() != 255) {
+#ifndef QT_NO_XRENDER
+ if (X11->use_xrender) {
+ if (!picture || d != 32)
+ convertToARGB32(/*preserveContents = */false);
+
+ ::Picture src = X11->getSolidFill(xinfo.screen(), fillColor);
+ XRenderComposite(X11->display, PictOpSrc, src, 0, picture,
+ 0, 0, width(), height(),
+ 0, 0, width(), height());
+ } else
+#endif
+ {
+ QImage im(width(), height(), QImage::Format_ARGB32_Premultiplied);
+ im.fill(PREMUL(fillColor.rgba()));
+ release();
+ fromImage(im, Qt::AutoColor | Qt::OrderedAlphaDither);
+ }
+ return;
+ }
+
+ GC gc = XCreateGC(X11->display, hd, 0, 0);
+ if (depth() == 1) {
+ XSetForeground(X11->display, gc, qGray(fillColor.rgb()) > 127 ? 0 : 1);
+ } else if (X11->use_xrender && d >= 24) {
+ XSetForeground(X11->display, gc, fillColor.rgba());
+ } else {
+ XSetForeground(X11->display, gc,
+ QColormap::instance(xinfo.screen()).pixel(fillColor));
+ }
+ XFillRectangle(X11->display, hd, gc, 0, 0, width(), height());
+ XFreeGC(X11->display, gc);
+}
+
+QX11PixmapData::~QX11PixmapData()
+{
+ release();
+}
+
+void QX11PixmapData::release()
+{
+ delete pengine;
+ pengine = 0;
+
+ if (!X11)
+ return;
+
+ if (x11_mask) {
+#ifndef QT_NO_XRENDER
+ if (mask_picture)
+ XRenderFreePicture(X11->display, mask_picture);
+ mask_picture = 0;
+#endif
+ XFreePixmap(X11->display, x11_mask);
+ x11_mask = 0;
+ }
+
+ if (hd) {
+#ifndef QT_NO_XRENDER
+ if (picture) {
+ XRenderFreePicture(X11->display, picture);
+ picture = 0;
+ }
+#endif // QT_NO_XRENDER
+
+ if (hd2) {
+ XFreePixmap(xinfo.display(), hd2);
+ hd2 = 0;
+ }
+ if (!read_only)
+ XFreePixmap(xinfo.display(), hd);
+ hd = 0;
+ }
+}
+
+QPixmap QX11PixmapData::alphaChannel() const
+{
+ if (!hasAlphaChannel())
+ return QPixmap();
+ QImage im(toImage());
+ return QPixmap::fromImage(im.alphaChannel(), Qt::OrderedDither);
+}
+
+void QX11PixmapData::setAlphaChannel(const QPixmap &alpha)
+{
+ QImage image(toImage());
+ image.setAlphaChannel(alpha.toImage());
+ release();
+ fromImage(image, Qt::OrderedDither | Qt::OrderedAlphaDither);
+}
+
+
+QBitmap QX11PixmapData::mask() const
+{
+ QBitmap mask;
+#ifndef QT_NO_XRENDER
+ if (picture && d == 32) {
+ // #### slow - there must be a better way..
+ mask = QBitmap::fromImage(toImage().createAlphaMask());
+ } else
+#endif
+ if (d == 1) {
+ QX11PixmapData *that = const_cast<QX11PixmapData*>(this);
+ mask = QPixmap(that);
+ } else {
+ mask = mask_to_bitmap(xinfo.screen());
+ }
+ return mask;
+}
+
+
+/*!
+ Sets a mask bitmap.
+
+ The \a newmask bitmap defines the clip mask for this pixmap. Every
+ pixel in \a newmask corresponds to a pixel in this pixmap. Pixel
+ value 1 means opaque and pixel value 0 means transparent. The mask
+ must have the same size as this pixmap.
+
+ \warning Setting the mask on a pixmap will cause any alpha channel
+ data to be cleared. For example:
+ \snippet doc/src/snippets/image/image.cpp 2
+ Now, alpha and alphacopy are visually different.
+
+ Setting a null mask resets the mask.
+
+ The effect of this function is undefined when the pixmap is being
+ painted on.
+
+ \sa mask(), {QPixmap#Pixmap Transformations}{Pixmap
+ Transformations}, QBitmap
+*/
+void QX11PixmapData::setMask(const QBitmap &newmask)
+{
+ if (newmask.isNull()) { // clear mask
+#ifndef QT_NO_XRENDER
+ if (picture && d == 32) {
+ QX11PixmapData newData(pixelType());
+ newData.resize(w, h);
+ newData.fill(Qt::black);
+ XRenderComposite(X11->display, PictOpOver,
+ picture, 0, newData.picture,
+ 0, 0, 0, 0, 0, 0, w, h);
+ release();
+ *this = newData;
+ newData.hd = 0;
+ newData.x11_mask = 0;
+ newData.picture = 0;
+ newData.mask_picture = 0;
+ newData.hd2 = 0;
+ } else
+#endif
+ if (x11_mask) {
+#ifndef QT_NO_XRENDER
+ if (picture) {
+ XRenderPictureAttributes attrs;
+ attrs.alpha_map = 0;
+ XRenderChangePicture(X11->display, picture, CPAlphaMap,
+ &attrs);
+ }
+ if (mask_picture)
+ XRenderFreePicture(X11->display, mask_picture);
+ mask_picture = 0;
+#endif
+ XFreePixmap(X11->display, x11_mask);
+ x11_mask = 0;
+ }
+ return;
+ }
+
+#ifndef QT_NO_XRENDER
+ if (picture && d == 32) {
+ XRenderComposite(X11->display, PictOpSrc,
+ picture, newmask.x11PictureHandle(),
+ picture, 0, 0, 0, 0, 0, 0, w, h);
+ } else
+#endif
+ if (depth() == 1) {
+ XGCValues vals;
+ vals.function = GXand;
+ GC gc = XCreateGC(X11->display, hd, GCFunction, &vals);
+ XCopyArea(X11->display, newmask.handle(), hd, gc, 0, 0,
+ width(), height(), 0, 0);
+ XFreeGC(X11->display, gc);
+ } else {
+ // ##### should or the masks together
+ if (x11_mask) {
+ XFreePixmap(X11->display, x11_mask);
+#ifndef QT_NO_XRENDER
+ if (mask_picture)
+ XRenderFreePicture(X11->display, mask_picture);
+#endif
+ }
+ x11_mask = QX11PixmapData::bitmap_to_mask(newmask, xinfo.screen());
+#ifndef QT_NO_XRENDER
+ if (picture) {
+ mask_picture = XRenderCreatePicture(X11->display, x11_mask,
+ XRenderFindStandardFormat(X11->display, PictStandardA1), 0, 0);
+ XRenderPictureAttributes attrs;
+ attrs.alpha_map = mask_picture;
+ XRenderChangePicture(X11->display, picture, CPAlphaMap, &attrs);
+ }
+#endif
+ }
+}
+
+int QX11PixmapData::metric(QPaintDevice::PaintDeviceMetric metric) const
+{
+ switch (metric) {
+ case QPaintDevice::PdmWidth:
+ return w;
+ case QPaintDevice::PdmHeight:
+ return h;
+ case QPaintDevice::PdmNumColors:
+ return 1 << d;
+ case QPaintDevice::PdmDepth:
+ return d;
+ case QPaintDevice::PdmWidthMM: {
+ const int screen = xinfo.screen();
+ const int mm = DisplayWidthMM(X11->display, screen) * w
+ / DisplayWidth(X11->display, screen);
+ return mm;
+ }
+ case QPaintDevice::PdmHeightMM: {
+ const int screen = xinfo.screen();
+ const int mm = (DisplayHeightMM(X11->display, screen) * h)
+ / DisplayHeight(X11->display, screen);
+ return mm;
+ }
+ case QPaintDevice::PdmDpiX:
+ case QPaintDevice::PdmPhysicalDpiX:
+ return QX11Info::appDpiX(xinfo.screen());
+ case QPaintDevice::PdmDpiY:
+ case QPaintDevice::PdmPhysicalDpiY:
+ return QX11Info::appDpiY(xinfo.screen());
+ default:
+ qWarning("QX11PixmapData::metric(): Invalid metric");
+ return 0;
+ }
+}
+
+/*!
+ Converts the pixmap to a QImage. Returns a null image if the
+ conversion fails.
+
+ If the pixmap has 1-bit depth, the returned image will also be 1
+ bit deep. If the pixmap has 2- to 8-bit depth, the returned image
+ has 8-bit depth. If the pixmap has greater than 8-bit depth, the
+ returned image has 32-bit depth.
+
+ Note that for the moment, alpha masks on monochrome images are
+ ignored.
+
+ \sa fromImage(), {QImage#Image Formats}{Image Formats}
+*/
+
+QImage QX11PixmapData::toImage() const
+{
+ int d = depth();
+ Visual *visual = (Visual *)xinfo.visual();
+ bool trucol = (visual->c_class >= TrueColor) && d > 1;
+
+ QImage::Format format = QImage::Format_Mono;
+ if (d > 1 && d <= 8) {
+ d = 8;
+ format = QImage::Format_Indexed8;
+ }
+ // we could run into the situation where d == 8 AND trucol is true, which can
+ // cause problems when converting to and from images. in this case, always treat
+ // the depth as 32...
+ if (d > 8 || trucol) {
+ d = 32;
+ format = QImage::Format_RGB32;
+ }
+
+ XImage *xi = XGetImage(X11->display, hd, 0, 0, w, h, AllPlanes,
+ (d == 1) ? XYPixmap : ZPixmap);
+
+ Q_CHECK_PTR(xi);
+ if (!xi)
+ return QImage();
+
+ if (picture && depth() == 32) {
+ QImage image(w, h, QImage::Format_ARGB32_Premultiplied);
+ memcpy(image.bits(), xi->data, xi->bytes_per_line * xi->height);
+
+ // we may have to swap the byte order
+ if ((QSysInfo::ByteOrder == QSysInfo::LittleEndian && xi->byte_order == MSBFirst)
+ || (QSysInfo::ByteOrder == QSysInfo::BigEndian))
+ {
+ for (int i=0; i < image.height(); i++) {
+ uint *p = (uint*)image.scanLine(i);
+ uint *end = p + image.width();
+ if ((xi->byte_order == LSBFirst && QSysInfo::ByteOrder == QSysInfo::BigEndian)
+ || (xi->byte_order == MSBFirst && QSysInfo::ByteOrder == QSysInfo::LittleEndian)) {
+ while (p < end) {
+ *p = ((*p << 24) & 0xff000000) | ((*p << 8) & 0x00ff0000)
+ | ((*p >> 8) & 0x0000ff00) | ((*p >> 24) & 0x000000ff);
+ p++;
+ }
+ } else if (xi->byte_order == MSBFirst && QSysInfo::ByteOrder == QSysInfo::BigEndian) {
+ while (p < end) {
+ *p = ((*p << 16) & 0x00ff0000) | ((*p >> 16) & 0x000000ff)
+ | ((*p ) & 0xff00ff00);
+ p++;
+ }
+ }
+ }
+ }
+
+ // throw away image data
+ qSafeXDestroyImage(xi);
+
+ return image;
+ }
+
+ if (d == 1 && xi->bitmap_bit_order == LSBFirst)
+ format = QImage::Format_MonoLSB;
+ if (x11_mask && format == QImage::Format_RGB32)
+ format = QImage::Format_ARGB32;
+
+ QImage image(w, h, format);
+ if (image.isNull()) // could not create image
+ return image;
+
+ QImage alpha;
+ if (x11_mask) {
+ alpha = mask().toImage();
+ }
+ bool ale = alpha.format() == QImage::Format_MonoLSB;
+
+ if (trucol) { // truecolor
+ const uint red_mask = (uint)visual->red_mask;
+ const uint green_mask = (uint)visual->green_mask;
+ const uint blue_mask = (uint)visual->blue_mask;
+ const int red_shift = highest_bit(red_mask) - 7;
+ const int green_shift = highest_bit(green_mask) - 7;
+ const int blue_shift = highest_bit(blue_mask) - 7;
+
+ const uint red_bits = n_bits(red_mask);
+ const uint green_bits = n_bits(green_mask);
+ const uint blue_bits = n_bits(blue_mask);
+
+ static uint red_table_bits = 0;
+ static uint green_table_bits = 0;
+ static uint blue_table_bits = 0;
+
+ if (red_bits < 8 && red_table_bits != red_bits) {
+ build_scale_table(&red_scale_table, red_bits);
+ red_table_bits = red_bits;
+ }
+ if (blue_bits < 8 && blue_table_bits != blue_bits) {
+ build_scale_table(&blue_scale_table, blue_bits);
+ blue_table_bits = blue_bits;
+ }
+ if (green_bits < 8 && green_table_bits != green_bits) {
+ build_scale_table(&green_scale_table, green_bits);
+ green_table_bits = green_bits;
+ }
+
+ int r, g, b;
+
+ QRgb *dst;
+ uchar *src;
+ uint pixel;
+ int bppc = xi->bits_per_pixel;
+
+ if (bppc > 8 && xi->byte_order == LSBFirst)
+ bppc++;
+
+ for (int y = 0; y < h; ++y) {
+ uchar* asrc = x11_mask ? alpha.scanLine(y) : 0;
+ dst = (QRgb *)image.scanLine(y);
+ src = (uchar *)xi->data + xi->bytes_per_line*y;
+ for (int x = 0; x < w; x++) {
+ switch (bppc) {
+ case 8:
+ pixel = *src++;
+ break;
+ case 16: // 16 bit MSB
+ pixel = src[1] | (uint)src[0] << 8;
+ src += 2;
+ break;
+ case 17: // 16 bit LSB
+ pixel = src[0] | (uint)src[1] << 8;
+ src += 2;
+ break;
+ case 24: // 24 bit MSB
+ pixel = src[2] | (uint)src[1] << 8 | (uint)src[0] << 16;
+ src += 3;
+ break;
+ case 25: // 24 bit LSB
+ pixel = src[0] | (uint)src[1] << 8 | (uint)src[2] << 16;
+ src += 3;
+ break;
+ case 32: // 32 bit MSB
+ pixel = src[3] | (uint)src[2] << 8 | (uint)src[1] << 16 | (uint)src[0] << 24;
+ src += 4;
+ break;
+ case 33: // 32 bit LSB
+ pixel = src[0] | (uint)src[1] << 8 | (uint)src[2] << 16 | (uint)src[3] << 24;
+ src += 4;
+ break;
+ default: // should not really happen
+ x = w; // leave loop
+ y = h;
+ pixel = 0; // eliminate compiler warning
+ qWarning("QPixmap::convertToImage: Invalid depth %d", bppc);
+ }
+ if (red_shift > 0)
+ r = (pixel & red_mask) >> red_shift;
+ else
+ r = (pixel & red_mask) << -red_shift;
+ if (green_shift > 0)
+ g = (pixel & green_mask) >> green_shift;
+ else
+ g = (pixel & green_mask) << -green_shift;
+ if (blue_shift > 0)
+ b = (pixel & blue_mask) >> blue_shift;
+ else
+ b = (pixel & blue_mask) << -blue_shift;
+
+ if (red_bits < 8)
+ r = red_scale_table[r];
+ if (green_bits < 8)
+ g = green_scale_table[g];
+ if (blue_bits < 8)
+ b = blue_scale_table[b];
+
+ if (x11_mask) {
+ if (ale) {
+ *dst++ = (asrc[x >> 3] & (1 << (x & 7))) ? qRgba(r, g, b, 0xff) : 0;
+ } else {
+ *dst++ = (asrc[x >> 3] & (0x80 >> (x & 7))) ? qRgba(r, g, b, 0xff) : 0;
+ }
+ } else {
+ *dst++ = qRgb(r, g, b);
+ }
+ }
+ }
+ } else if (xi->bits_per_pixel == d) { // compatible depth
+ char *xidata = xi->data; // copy each scanline
+ int bpl = qMin(image.bytesPerLine(),xi->bytes_per_line);
+ for (int y=0; y<h; y++) {
+ memcpy(image.scanLine(y), xidata, bpl);
+ xidata += xi->bytes_per_line;
+ }
+ } else {
+ /* Typically 2 or 4 bits display depth */
+ qWarning("QPixmap::convertToImage: Display not supported (bpp=%d)",
+ xi->bits_per_pixel);
+ return QImage();
+ }
+
+ if (d == 1) { // bitmap
+ image.setNumColors(2);
+ image.setColor(0, qRgb(255,255,255));
+ image.setColor(1, qRgb(0,0,0));
+ } else if (!trucol) { // pixmap with colormap
+ register uchar *p;
+ uchar *end;
+ uchar use[256]; // pixel-in-use table
+ uchar pix[256]; // pixel translation table
+ int ncols, bpl;
+ memset(use, 0, 256);
+ memset(pix, 0, 256);
+ bpl = image.bytesPerLine();
+
+ if (x11_mask) { // which pixels are used?
+ for (int i = 0; i < h; i++) {
+ uchar* asrc = alpha.scanLine(i);
+ p = image.scanLine(i);
+ if (ale) {
+ for (int x = 0; x < w; x++) {
+ if (asrc[x >> 3] & (1 << (x & 7)))
+ use[*p] = 1;
+ ++p;
+ }
+ } else {
+ for (int x = 0; x < w; x++) {
+ if (asrc[x >> 3] & (0x80 >> (x & 7)))
+ use[*p] = 1;
+ ++p;
+ }
+ }
+ }
+ } else {
+ for (int i = 0; i < h; i++) {
+ p = image.scanLine(i);
+ end = p + bpl;
+ while (p < end)
+ use[*p++] = 1;
+ }
+ }
+ ncols = 0;
+ for (int i = 0; i < 256; i++) { // build translation table
+ if (use[i])
+ pix[i] = ncols++;
+ }
+ for (int i = 0; i < h; i++) { // translate pixels
+ p = image.scanLine(i);
+ end = p + bpl;
+ while (p < end) {
+ *p = pix[*p];
+ p++;
+ }
+ }
+ if (x11_mask) {
+ int trans;
+ if (ncols < 256) {
+ trans = ncols++;
+ image.setNumColors(ncols); // create color table
+ image.setColor(trans, 0x00000000);
+ } else {
+ image.setNumColors(ncols); // create color table
+ // oh dear... no spare "transparent" pixel.
+ // use first pixel in image (as good as any).
+ trans = image.scanLine(0)[0];
+ }
+ for (int i = 0; i < h; i++) {
+ uchar* asrc = alpha.scanLine(i);
+ p = image.scanLine(i);
+ if (ale) {
+ for (int x = 0; x < w; x++) {
+ if (!(asrc[x >> 3] & (1 << (x & 7))))
+ *p = trans;
+ ++p;
+ }
+ } else {
+ for (int x = 0; x < w; x++) {
+ if (!(asrc[x >> 3] & (1 << (7 -(x & 7)))))
+ *p = trans;
+ ++p;
+ }
+ }
+ }
+ } else {
+ image.setNumColors(ncols); // create color table
+ }
+ QVector<QColor> colors = QColormap::instance(xinfo.screen()).colormap();
+ int j = 0;
+ for (int i=0; i<colors.size(); i++) { // translate pixels
+ if (use[i])
+ image.setColor(j++, 0xff000000 | colors.at(i).rgb());
+ }
+ }
+
+ qSafeXDestroyImage(xi);
+
+ return image;
+}
+
+/*!
+ Returns a copy of the pixmap that is transformed using the given
+ transformation \a matrix and transformation \a mode. The original
+ pixmap is not changed.
+
+ The transformation \a matrix is internally adjusted to compensate
+ for unwanted translation; i.e. the pixmap produced is the smallest
+ pixmap that contains all the transformed points of the original
+ pixmap. Use the trueMatrix() function to retrieve the actual
+ matrix used for transforming the pixmap.
+
+ This function is slow because it involves transformation to a
+ QImage, non-trivial computations and a transformation back to a
+ QPixmap.
+
+ \sa trueMatrix(), {QPixmap#Pixmap Transformations}{Pixmap
+ Transformations}
+*/
+QPixmap QX11PixmapData::transformed(const QTransform &transform,
+ Qt::TransformationMode mode ) const
+{
+ if (mode == Qt::SmoothTransformation || transform.type() >= QTransform::TxProject) {
+ QImage image = toImage();
+ return QPixmap::fromImage(image.transformed(transform, mode));
+ }
+
+ uint w = 0;
+ uint h = 0; // size of target pixmap
+ uint ws, hs; // size of source pixmap
+ uchar *dptr; // data in target pixmap
+ uint dbpl, dbytes; // bytes per line/bytes total
+ uchar *sptr; // data in original pixmap
+ int sbpl; // bytes per line in original
+ int bpp; // bits per pixel
+ bool depth1 = depth() == 1;
+ Display *dpy = X11->display;
+
+ ws = width();
+ hs = height();
+
+ QTransform mat(transform.m11(), transform.m12(), transform.m13(),
+ transform.m21(), transform.m22(), transform.m23(),
+ 0., 0., 1);
+ bool complex_xform = false;
+ qreal scaledWidth;
+ qreal scaledHeight;
+
+ if (mat.type() <= QTransform::TxScale) {
+ scaledHeight = qAbs(mat.m22()) * hs + 0.9999;
+ scaledWidth = qAbs(mat.m11()) * ws + 0.9999;
+ h = qAbs(int(scaledHeight));
+ w = qAbs(int(scaledWidth));
+ } else { // rotation or shearing
+ QPolygonF a(QRectF(0, 0, ws, hs));
+ a = mat.map(a);
+ QRect r = a.boundingRect().toAlignedRect();
+ w = r.width();
+ h = r.height();
+ scaledWidth = w;
+ scaledHeight = h;
+ complex_xform = true;
+ }
+ mat = QPixmap::trueMatrix(mat, ws, hs); // true matrix
+
+ bool invertible;
+ mat = mat.inverted(&invertible); // invert matrix
+
+ if (h == 0 || w == 0 || !invertible
+ || qAbs(scaledWidth) >= 32768 || qAbs(scaledHeight) >= 32768 )
+ // error, return null pixmap
+ return QPixmap();
+
+#if defined(QT_MITSHM)
+ static bool try_once = true;
+ if (try_once) {
+ try_once = false;
+ if (!xshminit)
+ qt_create_mitshm_buffer(this, 800, 600);
+ }
+
+ bool use_mitshm = xshmimg && !depth1 &&
+ xshmimg->width >= w && xshmimg->height >= h;
+#endif
+ XImage *xi = XGetImage(X11->display, handle(), 0, 0, ws, hs, AllPlanes,
+ depth1 ? XYPixmap : ZPixmap);
+
+ if (!xi)
+ return QPixmap();
+
+ sbpl = xi->bytes_per_line;
+ sptr = (uchar *)xi->data;
+ bpp = xi->bits_per_pixel;
+
+ if (depth1)
+ dbpl = (w+7)/8;
+ else
+ dbpl = ((w*bpp+31)/32)*4;
+ dbytes = dbpl*h;
+
+#if defined(QT_MITSHM)
+ if (use_mitshm) {
+ dptr = (uchar *)xshmimg->data;
+ uchar fillbyte = bpp == 8 ? white.pixel() : 0xff;
+ for (int y=0; y<h; y++)
+ memset(dptr + y*xshmimg->bytes_per_line, fillbyte, dbpl);
+ } else {
+#endif
+ dptr = (uchar *)malloc(dbytes); // create buffer for bits
+ Q_CHECK_PTR(dptr);
+ if (depth1) // fill with zeros
+ memset(dptr, 0, dbytes);
+ else if (bpp == 8) // fill with background color
+ memset(dptr, WhitePixel(X11->display, xinfo.screen()), dbytes);
+ else
+ memset(dptr, 0, dbytes);
+#if defined(QT_MITSHM)
+ }
+#endif
+
+ // #define QT_DEBUG_XIMAGE
+#if defined(QT_DEBUG_XIMAGE)
+ qDebug("----IMAGE--INFO--------------");
+ qDebug("width............. %d", xi->width);
+ qDebug("height............ %d", xi->height);
+ qDebug("xoffset........... %d", xi->xoffset);
+ qDebug("format............ %d", xi->format);
+ qDebug("byte order........ %d", xi->byte_order);
+ qDebug("bitmap unit....... %d", xi->bitmap_unit);
+ qDebug("bitmap bit order.. %d", xi->bitmap_bit_order);
+ qDebug("depth............. %d", xi->depth);
+ qDebug("bytes per line.... %d", xi->bytes_per_line);
+ qDebug("bits per pixel.... %d", xi->bits_per_pixel);
+#endif
+
+ int type;
+ if (xi->bitmap_bit_order == MSBFirst)
+ type = QT_XFORM_TYPE_MSBFIRST;
+ else
+ type = QT_XFORM_TYPE_LSBFIRST;
+ int xbpl, p_inc;
+ if (depth1) {
+ xbpl = (w+7)/8;
+ p_inc = dbpl - xbpl;
+ } else {
+ xbpl = (w*bpp)/8;
+ p_inc = dbpl - xbpl;
+#if defined(QT_MITSHM)
+ if (use_mitshm)
+ p_inc = xshmimg->bytes_per_line - xbpl;
+#endif
+ }
+
+ if (!qt_xForm_helper(mat, xi->xoffset, type, bpp, dptr, xbpl, p_inc, h, sptr, sbpl, ws, hs)){
+ qWarning("QPixmap::transform: display not supported (bpp=%d)",bpp);
+ QPixmap pm;
+ return pm;
+ }
+
+ qSafeXDestroyImage(xi);
+
+ if (depth1) { // mono bitmap
+ QBitmap bm = QBitmap::fromData(QSize(w, h), dptr,
+ BitmapBitOrder(X11->display) == MSBFirst
+ ? QImage::Format_Mono
+ : QImage::Format_MonoLSB);
+ free(dptr);
+ return bm;
+ } else { // color pixmap
+ QPixmap pm;
+ QX11PixmapData *x11Data = static_cast<QX11PixmapData*>(pm.data);
+ x11Data->uninit = false;
+ x11Data->xinfo = xinfo;
+ x11Data->d = d;
+ x11Data->w = w;
+ x11Data->h = h;
+ x11Data->hd = (Qt::HANDLE)XCreatePixmap(X11->display,
+ RootWindow(X11->display, xinfo.screen()),
+ w, h, d);
+#ifndef QT_NO_XRENDER
+ if (X11->use_xrender) {
+ XRenderPictFormat *format = x11Data->d == 32
+ ? XRenderFindStandardFormat(X11->display, PictStandardARGB32)
+ : XRenderFindVisualFormat(X11->display, (Visual *) x11Data->xinfo.visual());
+ x11Data->picture = XRenderCreatePicture(X11->display, x11Data->hd, format, 0, 0);
+ }
+#endif // QT_NO_XRENDER
+
+ GC gc = XCreateGC(X11->display, x11Data->hd, 0, 0);
+#if defined(QT_MITSHM)
+ if (use_mitshm) {
+ XCopyArea(dpy, xshmpm, x11Data->hd, gc, 0, 0, w, h, 0, 0);
+ } else
+#endif
+ {
+ xi = XCreateImage(dpy, (Visual*)x11Data->xinfo.visual(),
+ x11Data->d,
+ ZPixmap, 0, (char *)dptr, w, h, 32, 0);
+ XPutImage(dpy, pm.handle(), gc, xi, 0, 0, 0, 0, w, h);
+ qSafeXDestroyImage(xi);
+ }
+ XFreeGC(X11->display, gc);
+
+ if (x11_mask) { // xform mask, too
+ pm.setMask(mask_to_bitmap(xinfo.screen()).transformed(transform));
+ } else if (d != 32 && complex_xform) { // need a mask!
+ QBitmap mask(ws, hs);
+ mask.fill(Qt::color1);
+ pm.setMask(mask.transformed(transform));
+ }
+ return pm;
+ }
+}
+
+/*!
+ \internal
+*/
+int QPixmap::x11SetDefaultScreen(int screen)
+{
+ int old = defaultScreen;
+ defaultScreen = screen;
+ return old;
+}
+
+/*!
+ \internal
+*/
+void QPixmap::x11SetScreen(int screen)
+{
+ if (paintingActive()) {
+ qWarning("QPixmap::x11SetScreen(): Cannot change screens during painting");
+ return;
+ }
+
+ if (data->classId() != QPixmapData::X11Class)
+ return;
+
+ if (screen < 0)
+ screen = QX11Info::appScreen();
+
+ QX11PixmapData *x11Data = static_cast<QX11PixmapData*>(data);
+ if (screen == x11Data->xinfo.screen())
+ return; // nothing to do
+
+ if (isNull()) {
+ QX11InfoData* xd = x11Data->xinfo.getX11Data(true);
+ xd->screen = screen;
+ xd->depth = QX11Info::appDepth(screen);
+ xd->cells = QX11Info::appCells(screen);
+ xd->colormap = QX11Info::appColormap(screen);
+ xd->defaultColormap = QX11Info::appDefaultColormap(screen);
+ xd->visual = (Visual *)QX11Info::appVisual(screen);
+ xd->defaultVisual = QX11Info::appDefaultVisual(screen);
+ x11Data->xinfo.setX11Data(xd);
+ return;
+ }
+#if 0
+ qDebug("QPixmap::x11SetScreen for %p from %d to %d. Size is %d/%d", x11Data, x11Data->xinfo.screen(), screen, width(), height());
+#endif
+
+ x11SetDefaultScreen(screen);
+ *this = qt_toX11Pixmap(toImage());
+}
+
+QPixmap QPixmap::grabWindow(WId window, int x, int y, int w, int h)
+{
+ if (w == 0 || h == 0)
+ return QPixmap();
+
+ Display *dpy = X11->display;
+ XWindowAttributes window_attr;
+ if (!XGetWindowAttributes(dpy, window, &window_attr))
+ return QPixmap();
+
+ if (w < 0)
+ w = window_attr.width - x;
+ if (h < 0)
+ h = window_attr.height - y;
+
+ // determine the screen
+ int scr;
+ for (scr = 0; scr < ScreenCount(dpy); ++scr) {
+ if (window_attr.root == RootWindow(dpy, scr)) // found it
+ break;
+ }
+ if (scr >= ScreenCount(dpy)) // sanity check
+ return QPixmap();
+
+
+ // get the depth of the root window
+ XWindowAttributes root_attr;
+ if (!XGetWindowAttributes(dpy, window_attr.root, &root_attr))
+ return QPixmap();
+
+ if (window_attr.depth == root_attr.depth) {
+ // if the depth of the specified window and the root window are the
+ // same, grab pixels from the root window (so that we get the any
+ // overlapping windows and window manager frames)
+
+ // map x and y to the root window
+ WId unused;
+ if (!XTranslateCoordinates(dpy, window, window_attr.root, x, y,
+ &x, &y, &unused))
+ return QPixmap();
+
+ window = window_attr.root;
+ window_attr = root_attr;
+ }
+
+ QX11PixmapData *data = new QX11PixmapData(QPixmapData::PixmapType);
+
+ void qt_x11_getX11InfoForWindow(QX11Info * xinfo, const XWindowAttributes &a);
+ qt_x11_getX11InfoForWindow(&data->xinfo,window_attr);
+
+ data->resize(w, h);
+
+ QPixmap pm(data);
+
+ data->uninit = false;
+ pm.x11SetScreen(scr);
+
+ GC gc = XCreateGC(dpy, pm.handle(), 0, 0);
+ XSetSubwindowMode(dpy, gc, IncludeInferiors);
+ XCopyArea(dpy, window, pm.handle(), gc, x, y, w, h, 0, 0);
+ XFreeGC(dpy, gc);
+
+ return pm;
+}
+
+bool QX11PixmapData::hasAlphaChannel() const
+{
+ return d == 32;
+}
+
+/*!
+ Returns information about the configuration of the X display used to display
+ the widget.
+
+ \warning This function is only available on X11.
+
+ \sa {QPixmap#Pixmap Information}{Pixmap Information}
+*/
+const QX11Info &QPixmap::x11Info() const
+{
+ if (data->classId() == QPixmapData::X11Class)
+ return static_cast<QX11PixmapData*>(data)->xinfo;
+ else {
+ static QX11Info nullX11Info;
+ return nullX11Info;
+ }
+}
+
+#if !defined(QT_NO_XRENDER)
+static XRenderPictFormat *qt_renderformat_for_depth(const QX11Info &xinfo, int depth)
+{
+ if (depth == 1)
+ return XRenderFindStandardFormat(X11->display, PictStandardA1);
+ else if (depth == 32)
+ return XRenderFindStandardFormat(X11->display, PictStandardARGB32);
+ else
+ return XRenderFindVisualFormat(X11->display, (Visual *)xinfo.visual());
+}
+#endif
+
+QPaintEngine* QX11PixmapData::paintEngine() const
+{
+ QX11PixmapData *that = const_cast<QX11PixmapData*>(this);
+
+ if (read_only && share_mode == QPixmap::ImplicitlyShared) {
+ // if someone wants to draw onto us, copy the shared contents
+ // and turn it into a fully fledged QPixmap
+ ::Pixmap hd_copy = XCreatePixmap(X11->display, RootWindow(X11->display, xinfo.screen()),
+ w, h, d);
+#if !defined(QT_NO_XRENDER)
+ XRenderPictFormat *format = qt_renderformat_for_depth(xinfo, d);
+ ::Picture picture_copy = XRenderCreatePicture(X11->display, hd_copy, format, 0, 0);
+
+ if (picture && d == 32) {
+ XRenderComposite(X11->display, PictOpSrc, picture, 0, picture_copy,
+ 0, 0, 0, 0, 0, 0, w, h);
+ XRenderFreePicture(X11->display, picture);
+ that->picture = picture_copy;
+ } else
+#endif
+ {
+ GC gc = XCreateGC(X11->display, hd_copy, 0, 0);
+ XCopyArea(X11->display, hd, hd_copy, gc, 0, 0, w, h, 0, 0);
+ XFreeGC(X11->display, gc);
+ }
+ that->hd = hd_copy;
+ that->read_only = false;
+ }
+
+ if (!that->pengine)
+ that->pengine = new QX11PaintEngine;
+ return that->pengine;
+}
+
+/*!
+ Returns the X11 Picture handle of the pixmap for XRender
+ support.
+
+ This function will return 0 if XRender support is not compiled
+ into Qt, if the XRender extension is not supported on the X11
+ display, or if the handle could not be created. Use of this
+ function is not portable.
+
+ \warning This function is only available on X11.
+
+ \sa {QPixmap#Pixmap Information}{Pixmap Information}
+*/
+
+Qt::HANDLE QPixmap::x11PictureHandle() const
+{
+#ifndef QT_NO_XRENDER
+ if (data->classId() == QPixmapData::X11Class)
+ return static_cast<QX11PixmapData*>(data)->picture;
+ else
+ return 0;
+#else
+ return 0;
+#endif // QT_NO_XRENDER
+}
+
+Qt::HANDLE QX11PixmapData::x11ConvertToDefaultDepth()
+{
+#ifndef QT_NO_XRENDER
+ if (d == QX11Info::appDepth() || !X11->use_xrender)
+ return hd;
+ if (!hd2) {
+ hd2 = XCreatePixmap(xinfo.display(), hd, w, h, QX11Info::appDepth());
+ XRenderPictFormat *format = XRenderFindVisualFormat(xinfo.display(),
+ (Visual*) xinfo.visual());
+ Picture pic = XRenderCreatePicture(xinfo.display(), hd2, format, 0, 0);
+ XRenderComposite(xinfo.display(), PictOpSrc, picture,
+ XNone, pic, 0, 0, 0, 0, 0, 0, w, h);
+ XRenderFreePicture(xinfo.display(), pic);
+ }
+ return hd2;
+#else
+ return hd;
+#endif
+}
+
+void QX11PixmapData::copy(const QPixmapData *data, const QRect &rect)
+{
+ if (data->pixelType() == BitmapType) {
+ fromImage(data->toImage().copy(rect), Qt::AutoColor);
+ return;
+ }
+
+ const QX11PixmapData *x11Data = static_cast<const QX11PixmapData*>(data);
+
+ setSerialNumber(++qt_pixmap_serial);
+
+ uninit = false;
+ xinfo = x11Data->xinfo;
+ d = x11Data->d;
+ w = rect.width();
+ h = rect.height();
+ hd = (Qt::HANDLE)XCreatePixmap(X11->display,
+ RootWindow(X11->display, x11Data->xinfo.screen()),
+ w, h, d);
+#ifndef QT_NO_XRENDER
+ if (X11->use_xrender) {
+ XRenderPictFormat *format = d == 32
+ ? XRenderFindStandardFormat(X11->display, PictStandardARGB32)
+ : XRenderFindVisualFormat(X11->display, (Visual *)xinfo.visual());
+ picture = XRenderCreatePicture(X11->display, hd, format, 0, 0);
+ }
+#endif // QT_NO_XRENDER
+ if (x11Data->x11_mask) {
+ x11_mask = XCreatePixmap(X11->display, hd, w, h, 1);
+#ifndef QT_NO_XRENDER
+ if (X11->use_xrender) {
+ mask_picture = XRenderCreatePicture(X11->display, x11_mask,
+ XRenderFindStandardFormat(X11->display, PictStandardA1), 0, 0);
+ XRenderPictureAttributes attrs;
+ attrs.alpha_map = x11Data->mask_picture;
+ XRenderChangePicture(X11->display, x11Data->picture, CPAlphaMap, &attrs);
+ }
+#endif
+ }
+
+#if !defined(QT_NO_XRENDER)
+ if (x11Data->picture && x11Data->d == 32) {
+ XRenderComposite(X11->display, PictOpSrc,
+ x11Data->picture, 0, picture,
+ rect.x(), rect.y(), 0, 0, 0, 0, w, h);
+ } else
+#endif
+ {
+ GC gc = XCreateGC(X11->display, hd, 0, 0);
+ XCopyArea(X11->display, x11Data->hd, hd, gc,
+ rect.x(), rect.y(), w, h, 0, 0);
+ if (x11Data->x11_mask) {
+ GC monogc = XCreateGC(X11->display, x11_mask, 0, 0);
+ XCopyArea(X11->display, x11Data->x11_mask, x11_mask, monogc,
+ rect.x(), rect.y(), w, h, 0, 0);
+ XFreeGC(X11->display, monogc);
+ }
+ XFreeGC(X11->display, gc);
+ }
+}
+
+#if !defined(QT_NO_XRENDER)
+void QX11PixmapData::convertToARGB32(bool preserveContents)
+{
+ if (!X11->use_xrender)
+ return;
+
+ // Q_ASSERT(count == 1);
+ if (read_only && share_mode == QPixmap::ExplicitlyShared)
+ return;
+
+ Pixmap pm = XCreatePixmap(X11->display, RootWindow(X11->display, xinfo.screen()),
+ w, h, 32);
+ Picture p = XRenderCreatePicture(X11->display, pm,
+ XRenderFindStandardFormat(X11->display, PictStandardARGB32), 0, 0);
+ if (picture) {
+ if (preserveContents)
+ XRenderComposite(X11->display, PictOpSrc, picture, 0, p, 0, 0, 0, 0, 0, 0, w, h);
+ if (!read_only)
+ XRenderFreePicture(X11->display, picture);
+ }
+ if (hd && !read_only)
+ XFreePixmap(X11->display, hd);
+ if (x11_mask) {
+ XFreePixmap(X11->display, x11_mask);
+ if (mask_picture)
+ XRenderFreePicture(X11->display, mask_picture);
+ x11_mask = 0;
+ mask_picture = 0;
+ }
+ hd = pm;
+ picture = p;
+ d = 32;
+}
+#endif
+
+QPixmap QPixmap::fromX11Pixmap(Qt::HANDLE pixmap, QPixmap::ShareMode mode)
+{
+ Window root;
+ int x;
+ int y;
+ uint width;
+ uint height;
+ uint border_width;
+ uint depth;
+ XWindowAttributes win_attribs;
+ int num_screens = ScreenCount(X11->display);
+ int screen = 0;
+
+ XGetGeometry(X11->display, pixmap, &root, &x, &y, &width, &height, &border_width, &depth);
+ XGetWindowAttributes(X11->display, root, &win_attribs);
+
+ for (; screen < num_screens; ++screen) {
+ if (win_attribs.screen == ScreenOfDisplay(X11->display, screen))
+ break;
+ }
+
+ QX11PixmapData *data = new QX11PixmapData(depth == 1 ? QPixmapData::BitmapType : QPixmapData::PixmapType);
+ data->setSerialNumber(++qt_pixmap_serial);
+ data->read_only = true;
+ data->share_mode = mode;
+ data->uninit = false;
+ data->w = width;
+ data->h = height;
+ data->d = depth;
+ data->hd = pixmap;
+
+ if (defaultScreen >= 0 && defaultScreen != screen) {
+ QX11InfoData* xd = data->xinfo.getX11Data(true);
+ xd->screen = defaultScreen;
+ xd->depth = QX11Info::appDepth(xd->screen);
+ xd->cells = QX11Info::appCells(xd->screen);
+ xd->colormap = QX11Info::appColormap(xd->screen);
+ xd->defaultColormap = QX11Info::appDefaultColormap(xd->screen);
+ xd->visual = (Visual *)QX11Info::appVisual(xd->screen);
+ xd->defaultVisual = QX11Info::appDefaultVisual(xd->screen);
+ data->xinfo.setX11Data(xd);
+ }
+
+#ifndef QT_NO_XRENDER
+ if (X11->use_xrender) {
+ XRenderPictFormat *format = qt_renderformat_for_depth(data->xinfo, depth);
+ data->picture = XRenderCreatePicture(X11->display, data->hd, format, 0, 0);
+ }
+#endif // QT_NO_XRENDER
+
+ return QPixmap(data);
+}
+
+
+QT_END_NAMESPACE
diff --git a/src/gui/image/qpixmap_x11_p.h b/src/gui/image/qpixmap_x11_p.h
new file mode 100644
index 0000000000..980b10e138
--- /dev/null
+++ b/src/gui/image/qpixmap_x11_p.h
@@ -0,0 +1,131 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QPIXMAPDATA_X11_P_H
+#define QPIXMAPDATA_X11_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 <QtGui/private/qpixmapdata_p.h>
+#include <QtGui/private/qpixmapdatafactory_p.h>
+
+#include "QtGui/qx11info_x11.h"
+
+QT_BEGIN_NAMESPACE
+
+class QX11PaintEngine;
+
+class Q_GUI_EXPORT QX11PixmapData : public QPixmapData
+{
+public:
+ QX11PixmapData(PixelType type);
+// QX11PixmapData(PixelType type, int width, int height);
+// QX11PixmapData(PixelType type, const QImage &image,
+// Qt::ImageConversionFlags flags);
+ ~QX11PixmapData();
+
+ void resize(int width, int height);
+ void fromImage(const QImage &image, Qt::ImageConversionFlags flags);
+ void copy(const QPixmapData *data, const QRect &rect);
+
+ void fill(const QColor &color);
+ QBitmap mask() const;
+ void setMask(const QBitmap &mask);
+ bool hasAlphaChannel() const;
+ void setAlphaChannel(const QPixmap &alphaChannel);
+ QPixmap alphaChannel() const;
+ QPixmap transformed(const QTransform &transform,
+ Qt::TransformationMode mode) const;
+ QImage toImage() const;
+ QPaintEngine* paintEngine() const;
+
+ Qt::HANDLE handle() const { return hd; }
+ Qt::HANDLE x11ConvertToDefaultDepth();
+
+protected:
+ int metric(QPaintDevice::PaintDeviceMetric metric) const;
+
+private:
+ friend class QPixmap;
+ friend class QBitmap;
+ friend class QX11PaintEngine;
+ friend class QX11WindowSurface;
+ friend class QRasterWindowSurface;
+
+ void release();
+
+ QBitmap mask_to_bitmap(int screen) const;
+ static Qt::HANDLE bitmap_to_mask(const QBitmap &, int screen);
+ void bitmapFromImage(const QImage &image);
+
+ Qt::HANDLE hd;
+
+ int w, h;
+ short d;
+ uint uninit : 1;
+ uint read_only : 1;
+
+ QX11Info xinfo;
+ Qt::HANDLE x11_mask;
+ Qt::HANDLE picture;
+ Qt::HANDLE mask_picture;
+ Qt::HANDLE hd2; // sorted in the default display depth
+#ifndef QT_NO_XRENDER
+ void convertToARGB32(bool preserveContents = true);
+#endif
+ QPixmap::ShareMode share_mode;
+
+ QX11PaintEngine *pengine;
+};
+
+QT_END_NAMESPACE
+
+#endif // QPIXMAPDATA_X11_P_H
+
diff --git a/src/gui/image/qpixmapcache.cpp b/src/gui/image/qpixmapcache.cpp
new file mode 100644
index 0000000000..4253f8d02e
--- /dev/null
+++ b/src/gui/image/qpixmapcache.cpp
@@ -0,0 +1,320 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qpixmapcache.h"
+#include "qcache.h"
+#include "qobject.h"
+#include "qdebug.h"
+
+#include "qpaintengine.h"
+#include <private/qimage_p.h>
+#include <private/qpixmap_raster_p.h>
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QPixmapCache
+
+ \brief The QPixmapCache class provides an application-wide cache for pixmaps.
+
+ \ingroup environment
+ \ingroup multimedia
+
+ This class is a tool for optimized drawing with QPixmap. You can
+ use it to store temporary pixmaps that are expensive to generate
+ without using more storage space than cacheLimit(). Use insert()
+ to insert pixmaps, find() to find them, and clear() to empty the
+ cache.
+
+ QPixmapCache contains no member data, only static functions to
+ access the global pixmap cache. It creates an internal QCache
+ object for caching the pixmaps.
+
+ The cache associates a pixmap with a string (key). If two pixmaps
+ are inserted into the cache using equal keys, then the last pixmap
+ will hide the first pixmap. The QHash and QCache classes do
+ exactly the same.
+
+ The cache becomes full when the total size of all pixmaps in the
+ cache exceeds cacheLimit(). The initial cache limit is 1024 KB (1
+ MB); it is changed with setCacheLimit(). A pixmap takes roughly
+ (\e{width} * \e{height} * \e{depth})/8 bytes of memory.
+
+ The \e{Qt Quarterly} article
+ \l{http://doc.trolltech.com/qq/qq12-qpixmapcache.html}{Optimizing
+ with QPixmapCache} explains how to use QPixmapCache to speed up
+ applications by caching the results of painting.
+
+ \sa QCache, QPixmap
+*/
+
+#if defined(Q_WS_QWS) || defined(Q_OS_WINCE)
+static int cache_limit = 2048; // 2048 KB cache limit for embedded
+#else
+static int cache_limit = 10240; // 10 MB cache limit for desktop
+#endif
+
+// XXX: hw: is this a general concept we need to abstract?
+class QDetachedPixmap : public QPixmap
+{
+public:
+ QDetachedPixmap(const QPixmap &pix) : QPixmap(pix)
+ {
+ if (data && data->classId() == QPixmapData::RasterClass) {
+ QRasterPixmapData *d = static_cast<QRasterPixmapData*>(data);
+ if (!d->image.isNull() && d->image.d->paintEngine
+ && !d->image.d->paintEngine->isActive())
+ {
+ delete d->image.d->paintEngine;
+ d->image.d->paintEngine = 0;
+ }
+ }
+ }
+};
+
+class QPMCache : public QObject, public QCache<qint64, QDetachedPixmap>
+{
+ Q_OBJECT
+public:
+ QPMCache()
+ : QObject(0),
+ QCache<qint64, QDetachedPixmap>(cache_limit * 1024),
+ theid(0), ps(0), t(false) { }
+ ~QPMCache() { }
+
+ void timerEvent(QTimerEvent *);
+ bool insert(const QString& key, const QPixmap &pixmap, int cost);
+ bool remove(const QString &key);
+
+ QPixmap *object(const QString &key) const;
+
+private:
+ QHash<QString, qint64> cacheKeys;
+ int theid;
+ int ps;
+ bool t;
+};
+QT_BEGIN_INCLUDE_NAMESPACE
+#include "qpixmapcache.moc"
+QT_END_INCLUDE_NAMESPACE
+
+/*
+ This is supposed to cut the cache size down by about 80-90% in a
+ minute once the application becomes idle, to let any inserted pixmap
+ remain in the cache for some time before it becomes a candidate for
+ cleaning-up, and to not cut down the size of the cache while the
+ cache is in active use.
+
+ When the last pixmap has been deleted from the cache, kill the
+ timer so Qt won't keep the CPU from going into sleep mode.
+*/
+
+void QPMCache::timerEvent(QTimerEvent *)
+{
+ int mc = maxCost();
+ bool nt = totalCost() == ps;
+ setMaxCost(nt ? totalCost() * 3 / 4 : totalCost() -1);
+ setMaxCost(mc);
+ ps = totalCost();
+
+ QHash<QString, qint64>::iterator it = cacheKeys.begin();
+ while (it != cacheKeys.end()) {
+ if (!contains(it.value())) {
+ it = cacheKeys.erase(it);
+ } else {
+ ++it;
+ }
+ }
+
+ if (!size()) {
+ killTimer(theid);
+ theid = 0;
+ } else if (nt != t) {
+ killTimer(theid);
+ theid = startTimer(nt ? 10000 : 30000);
+ t = nt;
+ }
+}
+
+QPixmap *QPMCache::object(const QString &key) const
+{
+ return QCache<qint64, QDetachedPixmap>::object(cacheKeys.value(key, -1));
+}
+
+
+bool QPMCache::insert(const QString& key, const QPixmap &pixmap, int cost)
+{
+ qint64 cacheKey = pixmap.cacheKey();
+ if (QCache<qint64, QDetachedPixmap>::object(cacheKey)) {
+ cacheKeys.insert(key, cacheKey);
+ return true;
+ }
+ bool success = QCache<qint64, QDetachedPixmap>::insert(cacheKey, new QDetachedPixmap(pixmap), cost);
+ if (success) {
+ cacheKeys.insert(key, cacheKey);
+ if (!theid) {
+ theid = startTimer(30000);
+ t = false;
+ }
+ }
+ return success;
+}
+
+bool QPMCache::remove(const QString &key)
+{
+ qint64 cacheKey = cacheKeys.value(key, -1);
+ cacheKeys.remove(key);
+ return QCache<qint64, QDetachedPixmap>::remove(cacheKey);
+}
+
+Q_GLOBAL_STATIC(QPMCache, pm_cache)
+
+/*!
+ \obsolete
+ \overload
+
+ Returns the pixmap associated with the \a key in the cache, or
+ null if there is no such pixmap.
+
+ \warning If valid, you should copy the pixmap immediately (this is
+ fast). Subsequent insertions into the cache could cause the
+ pointer to become invalid. For this reason, we recommend you use
+ find(const QString&, QPixmap&) instead.
+
+ Example:
+ \snippet doc/src/snippets/code/src_gui_image_qpixmapcache.cpp 0
+*/
+
+QPixmap *QPixmapCache::find(const QString &key)
+{
+ return pm_cache()->object(key);
+}
+
+
+/*!
+ Looks for a cached pixmap associated with the \a key in the cache.
+ If the pixmap is found, the function sets \a pm to that pixmap and
+ returns true; otherwise it leaves \a pm alone and returns false.
+
+ Example:
+ \snippet doc/src/snippets/code/src_gui_image_qpixmapcache.cpp 1
+*/
+
+bool QPixmapCache::find(const QString &key, QPixmap& pm)
+{
+ QPixmap *ptr = pm_cache()->object(key);
+ if (ptr)
+ pm = *ptr;
+ return ptr != 0;
+}
+
+
+/*!
+ Inserts a copy of the pixmap \a pm associated with the \a key into
+ the cache.
+
+ All pixmaps inserted by the Qt library have a key starting with
+ "$qt", so your own pixmap keys should never begin "$qt".
+
+ When a pixmap is inserted and the cache is about to exceed its
+ limit, it removes pixmaps until there is enough room for the
+ pixmap to be inserted.
+
+ The oldest pixmaps (least recently accessed in the cache) are
+ deleted when more space is needed.
+
+ The function returns true if the object was inserted into the
+ cache; otherwise it returns false.
+
+ \sa setCacheLimit()
+*/
+
+bool QPixmapCache::insert(const QString &key, const QPixmap &pm)
+{
+ return pm_cache()->insert(key, pm, pm.width() * pm.height() * pm.depth() / 8);
+}
+
+/*!
+ Returns the cache limit (in kilobytes).
+
+ The default cache limit is 2048 KB for Embedded, 10240 KB for Desktops.
+
+ \sa setCacheLimit()
+*/
+
+int QPixmapCache::cacheLimit()
+{
+ return cache_limit;
+}
+
+/*!
+ Sets the cache limit to \a n kilobytes.
+
+ The default setting is 1024 kilobytes.
+
+ \sa cacheLimit()
+*/
+
+void QPixmapCache::setCacheLimit(int n)
+{
+ cache_limit = n;
+ pm_cache()->setMaxCost(1024 * cache_limit);
+}
+
+/*!
+ Removes the pixmap associated with \a key from the cache.
+*/
+void QPixmapCache::remove(const QString &key)
+{
+ pm_cache()->remove(key);
+}
+
+
+/*!
+ Removes all pixmaps from the cache.
+*/
+
+void QPixmapCache::clear()
+{
+ pm_cache()->clear();
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/image/qpixmapcache.h b/src/gui/image/qpixmapcache.h
new file mode 100644
index 0000000000..2750a88113
--- /dev/null
+++ b/src/gui/image/qpixmapcache.h
@@ -0,0 +1,69 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QPIXMAPCACHE_H
+#define QPIXMAPCACHE_H
+
+#include <QtGui/qpixmap.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+class Q_GUI_EXPORT QPixmapCache
+{
+public:
+ static int cacheLimit();
+ static void setCacheLimit(int);
+ static QPixmap *find(const QString &key);
+ static bool find(const QString &key, QPixmap&);
+ static bool insert(const QString &key, const QPixmap&);
+ static void remove(const QString &key);
+ static void clear();
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QPIXMAPCACHE_H
diff --git a/src/gui/image/qpixmapdata.cpp b/src/gui/image/qpixmapdata.cpp
new file mode 100644
index 0000000000..3d88f4b151
--- /dev/null
+++ b/src/gui/image/qpixmapdata.cpp
@@ -0,0 +1,179 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qpixmapdata_p.h"
+#include <QtGui/qbitmap.h>
+#include <QtGui/qimagereader.h>
+
+QT_BEGIN_NAMESPACE
+
+const uchar qt_pixmap_bit_mask[] = { 0x01, 0x02, 0x04, 0x08,
+ 0x10, 0x20, 0x40, 0x80 };
+
+QPixmapData::QPixmapData(PixelType pixelType, int objectId)
+ : ref(0), detach_no(0), type(pixelType), id(objectId), ser_no(0), is_cached(false)
+{
+
+}
+
+QPixmapData::~QPixmapData()
+{
+}
+
+void QPixmapData::fromFile(const QString &fileName, const char *format,
+ Qt::ImageConversionFlags flags)
+{
+ const QImage image = QImageReader(fileName, format).read();
+ if (image.isNull())
+ return;
+
+ fromImage(image, flags);
+}
+
+void QPixmapData::copy(const QPixmapData *data, const QRect &rect)
+{
+ fromImage(data->toImage().copy(rect), Qt::AutoColor);
+}
+
+void QPixmapData::setMask(const QBitmap &mask)
+{
+ if (mask.size().isEmpty()) {
+ if (depth() != 1)
+ fromImage(toImage().convertToFormat(QImage::Format_RGB32),
+ Qt::AutoColor);
+ } else {
+ QImage image = toImage();
+ const int w = image.width();
+ const int h = image.height();
+
+ switch (image.depth()) {
+ case 1: {
+ const QImage imageMask = mask.toImage().convertToFormat(image.format());
+ for (int y = 0; y < h; ++y) {
+ const uchar *mscan = imageMask.scanLine(y);
+ uchar *tscan = image.scanLine(y);
+ int bytesPerLine = image.bytesPerLine();
+ for (int i = 0; i < bytesPerLine; ++i)
+ tscan[i] &= mscan[i];
+ }
+ break;
+ }
+ default: {
+ const QImage imageMask = mask.toImage().convertToFormat(QImage::Format_MonoLSB);
+ image = image.convertToFormat(QImage::Format_ARGB32_Premultiplied);
+ for (int y = 0; y < h; ++y) {
+ const uchar *mscan = imageMask.scanLine(y);
+ QRgb *tscan = (QRgb *)image.scanLine(y);
+ for (int x = 0; x < w; ++x) {
+ if (!(mscan[x>>3] & qt_pixmap_bit_mask[x&7]))
+ tscan[x] = 0;
+ }
+ }
+ break;
+ }
+ }
+ fromImage(image, Qt::AutoColor);
+ }
+}
+
+QBitmap QPixmapData::mask() const
+{
+ if (!hasAlphaChannel())
+ return QBitmap();
+
+ const QImage img = toImage();
+ const QImage image = (img.depth() < 32 ? img.convertToFormat(QImage::Format_ARGB32_Premultiplied) : img);
+ const int w = image.width();
+ const int h = image.height();
+
+ QImage mask(w, h, QImage::Format_MonoLSB);
+ if (mask.isNull()) // allocation failed
+ return QBitmap();
+
+ mask.setNumColors(2);
+ mask.setColor(0, QColor(Qt::color0).rgba());
+ mask.setColor(1, QColor(Qt::color1).rgba());
+
+ const int bpl = mask.bytesPerLine();
+
+ for (int y = 0; y < h; ++y) {
+ const QRgb *src = reinterpret_cast<const QRgb*>(image.scanLine(y));
+ uchar *dest = mask.scanLine(y);
+ memset(dest, 0, bpl);
+ for (int x = 0; x < w; ++x) {
+ if (qAlpha(*src) > 0)
+ dest[x >> 3] |= qt_pixmap_bit_mask[x & 7];
+ ++src;
+ }
+ }
+
+ return QBitmap::fromImage(mask);
+}
+
+QPixmap QPixmapData::transformed(const QTransform &matrix,
+ Qt::TransformationMode mode) const
+{
+ return QPixmap::fromImage(toImage().transformed(matrix, mode));
+}
+
+void QPixmapData::setAlphaChannel(const QPixmap &alphaChannel)
+{
+ QImage image = toImage();
+ image.setAlphaChannel(alphaChannel.toImage());
+ fromImage(image, Qt::AutoColor);
+}
+
+QPixmap QPixmapData::alphaChannel() const
+{
+ return QPixmap::fromImage(toImage().alphaChannel());
+}
+
+void QPixmapData::setSerialNumber(int serNo)
+{
+ ser_no = serNo;
+}
+
+QImage* QPixmapData::buffer()
+{
+ return 0;
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/image/qpixmapdata_p.h b/src/gui/image/qpixmapdata_p.h
new file mode 100644
index 0000000000..2e4da40333
--- /dev/null
+++ b/src/gui/image/qpixmapdata_p.h
@@ -0,0 +1,140 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QPIXMAPDATA_P_H
+#define QPIXMAPDATA_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 <QtGui/qpixmap.h>
+#include <QtCore/qatomic.h>
+
+QT_BEGIN_NAMESPACE
+
+class Q_GUI_EXPORT QPixmapData
+{
+public:
+ enum PixelType {
+ // WARNING: Do not change the first two
+ // Must match QPixmap::Type
+ PixmapType, BitmapType
+ };
+ enum ClassId { RasterClass, X11Class, MacClass, DirectFBClass,
+ OpenGLClass, OpenVGClass, CustomClass = 1024 };
+
+ QPixmapData(PixelType pixelpType, int classId);
+ virtual ~QPixmapData();
+
+ virtual void resize(int width, int height) = 0;
+ virtual void fromImage(const QImage &image,
+ Qt::ImageConversionFlags flags) = 0;
+ virtual void fromFile(const QString &filename, const char *format,
+ Qt::ImageConversionFlags flags);
+ virtual void copy(const QPixmapData *data, const QRect &rect);
+
+ virtual int metric(QPaintDevice::PaintDeviceMetric metric) const = 0;
+ virtual void fill(const QColor &color) = 0;
+ virtual QBitmap mask() const;
+ virtual void setMask(const QBitmap &mask);
+ virtual bool hasAlphaChannel() const = 0;
+ virtual QPixmap transformed(const QTransform &matrix,
+ Qt::TransformationMode mode) const;
+ virtual void setAlphaChannel(const QPixmap &alphaChannel);
+ virtual QPixmap alphaChannel() const;
+ virtual QImage toImage() const = 0;
+ virtual QPaintEngine* paintEngine() const = 0;
+
+ inline int serialNumber() const { return ser_no; }
+
+ inline PixelType pixelType() const { return type; }
+ inline ClassId classId() const { return static_cast<ClassId>(id); }
+
+ virtual QImage* buffer();
+
+ int width() const { return metric(QPaintDevice::PdmWidth); }
+ int height() const { return metric(QPaintDevice::PdmHeight); }
+ int numColors() const { return metric(QPaintDevice::PdmNumColors); }
+ int depth() const { return metric(QPaintDevice::PdmDepth); }
+
+protected:
+ void setSerialNumber(int serNo);
+
+private:
+ friend class QPixmap;
+ friend class QGLContextPrivate;
+
+ QAtomicInt ref;
+ int detach_no;
+
+ PixelType type;
+ int id;
+ int ser_no;
+ uint is_cached;
+};
+
+#ifdef Q_WS_WIN
+#ifndef Q_OS_WINCE
+QPixmap convertHIconToPixmap( const HICON icon);
+#else
+QPixmap convertHIconToPixmap( const HICON icon, bool large = false);
+#endif
+QPixmap loadIconFromShell32( int resourceId, int size );
+#endif
+
+# define QT_XFORM_TYPE_MSBFIRST 0
+# define QT_XFORM_TYPE_LSBFIRST 1
+# if defined(Q_WS_WIN)
+# define QT_XFORM_TYPE_WINDOWSPIXMAP 2
+# endif
+extern bool qt_xForm_helper(const QTransform&, int, int, int, uchar*, int, int, int, const uchar*, int, int, int);
+
+QT_END_NAMESPACE
+
+#endif // QPIXMAPDATA_P_H
diff --git a/src/gui/image/qpixmapdatafactory.cpp b/src/gui/image/qpixmapdatafactory.cpp
new file mode 100644
index 0000000000..699489d5fc
--- /dev/null
+++ b/src/gui/image/qpixmapdatafactory.cpp
@@ -0,0 +1,105 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qpixmapdatafactory_p.h"
+
+#ifdef Q_WS_QWS
+# include <QtGui/qscreen_qws.h>
+#endif
+#ifdef Q_WS_X11
+# include <private/qpixmap_x11_p.h>
+#endif
+#ifdef Q_WS_WIN
+# include <private/qpixmap_raster_p.h>
+#endif
+#ifdef Q_WS_MAC
+# include <private/qpixmap_mac_p.h>
+#endif
+
+#include "private/qapplication_p.h"
+#include "private/qgraphicssystem_p.h"
+
+QT_BEGIN_NAMESPACE
+
+#if !defined(Q_WS_QWS)
+
+class QSimplePixmapDataFactory : public QPixmapDataFactory
+{
+public:
+ ~QSimplePixmapDataFactory() {}
+ QPixmapData* create(QPixmapData::PixelType type);
+};
+
+QPixmapData* QSimplePixmapDataFactory::create(QPixmapData::PixelType type)
+{
+ if (QApplicationPrivate::graphicsSystem())
+ return QApplicationPrivate::graphicsSystem()->createPixmapData(type);
+
+#if defined(Q_WS_X11)
+ return new QX11PixmapData(type);
+#elif defined(Q_WS_WIN)
+ return new QRasterPixmapData(type);
+#elif defined(Q_WS_MAC)
+ return new QMacPixmapData(type);
+#else
+#error QSimplePixmapDataFactory::create() not implemented
+#endif
+}
+
+Q_GLOBAL_STATIC(QSimplePixmapDataFactory, factory);
+
+#endif // !defined(Q_WS_QWS)
+
+QPixmapDataFactory::~QPixmapDataFactory()
+{
+}
+
+QPixmapDataFactory* QPixmapDataFactory::instance(int screen)
+{
+ Q_UNUSED(screen);
+#ifdef Q_WS_QWS
+ return QScreen::instance()->pixmapDataFactory();
+#else
+ return factory();
+#endif
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/image/qpixmapdatafactory_p.h b/src/gui/image/qpixmapdatafactory_p.h
new file mode 100644
index 0000000000..65e3f11801
--- /dev/null
+++ b/src/gui/image/qpixmapdatafactory_p.h
@@ -0,0 +1,81 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QPIXMAPDATAFACTORY_P_H
+#define QPIXMAPDATAFACTORY_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 <QtCore/qstring.h>
+#include <QtGui/qimage.h>
+#include <QtGui/private/qpixmapdata_p.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+class QPixmapData;
+
+class Q_GUI_EXPORT QPixmapDataFactory
+{
+public:
+ static QPixmapDataFactory* instance(int screen = 0);
+ virtual ~QPixmapDataFactory();
+
+ virtual QPixmapData* create(QPixmapData::PixelType type) = 0;
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QPIXMAPDATAFACTORY_P_H
diff --git a/src/gui/image/qpixmapfilter.cpp b/src/gui/image/qpixmapfilter.cpp
new file mode 100644
index 0000000000..8631c8c2a2
--- /dev/null
+++ b/src/gui/image/qpixmapfilter.cpp
@@ -0,0 +1,849 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <qglobal.h>
+
+#include <QDebug>
+
+#include "qpainter.h"
+#include "qpixmap.h"
+#include "qpixmapfilter_p.h"
+#include "qvarlengtharray.h"
+
+#include "private/qapplication_p.h"
+#include "private/qgraphicssystem_p.h"
+#include "private/qpaintengineex_p.h"
+#include "private/qpaintengine_raster_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class QPixmapFilterPrivate : public QObjectPrivate
+{
+ Q_DECLARE_PUBLIC(QPixmapFilter)
+public:
+ QPixmapFilter::FilterType type;
+};
+
+/*!
+ \class QPixmapFilter
+ \since 4.5
+ \ingroup multimedia
+
+ \brief The QPixmapFilter class provides the basic functionality for
+ pixmap filter classes. Pixmap filter can be for example colorize or blur.
+
+ QPixmapFilter is the base class for every pixmap filter. QPixmapFilter is
+ an abstract class and cannot itself be instantiated. It provides a standard
+ interface for filter processing.
+
+ \internal
+*/
+
+/*!
+ \enum QPixmapFilter::FilterType
+
+ \internal
+
+ This enum describes the types of filter that can be applied to pixmaps.
+
+ \value ConvolutionFilter A filter that is used to calculate the convolution
+ of the image with a kernel. See
+ QPixmapConvolutionFilter for more information.
+ \value ColorizeFilter A filter that is used to change the overall color
+ of an image. See QPixmapColorizeFilter for more
+ information.
+ \value DropShadowFilter A filter that is used to add a drop shadow to an
+ image. See QPixmapDropShadowFilter for more
+ information.
+
+ \value UserFilter The first filter type that can be used for
+ application-specific purposes.
+*/
+
+
+/*!
+ Constructs a default QPixmapFilter with the given \a type.
+
+ This constructor should be used when subclassing QPixmapFilter to
+ create custom user filters.
+
+ \internal
+*/
+QPixmapFilter::QPixmapFilter(FilterType type, QObject *parent)
+ : QObject(*new QPixmapFilterPrivate, parent)
+{
+ d_func()->type = type;
+}
+
+
+
+/*!
+ \internal
+*/
+QPixmapFilter::QPixmapFilter(QPixmapFilterPrivate&d, QPixmapFilter::FilterType type, QObject *parent)
+ : QObject(d, parent)
+{
+ d_func()->type = type;
+}
+
+
+/*!
+ Destroys the pixmap filter.
+
+ \internal
+*/
+QPixmapFilter::~QPixmapFilter()
+{
+}
+
+/*!
+ Returns the type of the filter. All standard pixmap filter classes
+ are associated with a unique value.
+
+ \internal
+*/
+QPixmapFilter::FilterType QPixmapFilter::type() const
+{
+ Q_D(const QPixmapFilter);
+ return d->type;
+}
+
+/*!
+ Returns the bounding rectangle that is affected by the pixmap
+ filter if the filter is applied to the specified \a rect.
+
+ \internal
+*/
+QRectF QPixmapFilter::boundingRectFor(const QRectF &rect) const
+{
+ return rect;
+}
+
+/*!
+ \fn void QPixmapFilter::draw(QPainter *painter, const QPointF &p, const QPixmap &src, const QRectF& srcRect) const
+
+ Uses \a painter to draw filtered result of \a src at the point
+ specified by \a p. If \a srcRect is specified the it will
+ be used as a source rectangle to only draw a part of the source.
+
+ draw() will affect the area which boundingRectFor() returns.
+
+ \internal
+*/
+
+/*!
+ \class QPixmapConvolutionFilter
+ \since 4.5
+ \ingroup multimedia
+
+ \brief The QPixmapConvolutionFilter class provides convolution
+ filtering for pixmaps.
+
+ QPixmapConvolutionFilter implements a convolution pixmap filter,
+ which is applied when \l{QPixmapFilter::}{draw()} is called. A
+ convolution filter lets you distort an image by setting the values
+ of a matrix of qreal values called its
+ \l{setConvolutionKernel()}{kernel}. The matrix's values are
+ usually between -1.0 and 1.0.
+
+ \omit
+ In convolution filtering, the pixel value is calculated from the
+ neighboring pixels based on the weighting convolution kernel.
+ This needs explaining to be useful.
+ \endomit
+
+ Example:
+ \snippet doc/src/snippets/code/src_gui_image_qpixmapfilter.cpp 1
+
+ \sa {Pixmap Filters Example}, QPixmapColorizeFilter, QPixmapDropShadowFilter
+
+
+ \internal
+*/
+
+class QPixmapConvolutionFilterPrivate : public QPixmapFilterPrivate
+{
+public:
+ QPixmapConvolutionFilterPrivate(): convolutionKernel(0), kernelWidth(0), kernelHeight(0), convoluteAlpha(false) {}
+ ~QPixmapConvolutionFilterPrivate() {
+ delete[] convolutionKernel;
+ }
+
+ qreal *convolutionKernel;
+ int kernelWidth;
+ int kernelHeight;
+ bool convoluteAlpha;
+};
+
+
+/*!
+ Constructs a pixmap convolution filter.
+
+ By default there is no convolution kernel.
+
+ \internal
+*/
+QPixmapConvolutionFilter::QPixmapConvolutionFilter(QObject *parent)
+ : QPixmapFilter(*new QPixmapConvolutionFilterPrivate, ConvolutionFilter, parent)
+{
+ Q_D(QPixmapConvolutionFilter);
+ d->convoluteAlpha = true;
+}
+
+/*!
+ Destructor of pixmap convolution filter.
+
+ \internal
+*/
+QPixmapConvolutionFilter::~QPixmapConvolutionFilter()
+{
+}
+
+/*!
+ Sets convolution kernel with the given number of \a rows and \a columns.
+ Values from \a kernel are copied to internal data structure.
+
+ To preserve the intensity of the pixmap, the sum of all the
+ values in the convolution kernel should add up to 1.0. A sum
+ greater than 1.0 produces a lighter result and a sum less than 1.0
+ produces a darker and transparent result.
+
+ \internal
+*/
+void QPixmapConvolutionFilter::setConvolutionKernel(const qreal *kernel, int rows, int columns)
+{
+ Q_D(QPixmapConvolutionFilter);
+ delete [] d->convolutionKernel;
+ d->convolutionKernel = new qreal[rows * columns];
+ memcpy(d->convolutionKernel, kernel, sizeof(qreal) * rows * columns);
+ d->kernelWidth = columns;
+ d->kernelHeight = rows;
+}
+
+/*!
+ Gets the convolution kernel data.
+
+ \internal
+*/
+const qreal *QPixmapConvolutionFilter::convolutionKernel() const
+{
+ Q_D(const QPixmapConvolutionFilter);
+ return d->convolutionKernel;
+}
+
+/*!
+ Gets the number of rows in the convolution kernel.
+
+ \internal
+*/
+int QPixmapConvolutionFilter::rows() const
+{
+ Q_D(const QPixmapConvolutionFilter);
+ return d->kernelHeight;
+}
+
+/*!
+ Gets the number of columns in the convolution kernel.
+
+ \internal
+*/
+int QPixmapConvolutionFilter::columns() const
+{
+ Q_D(const QPixmapConvolutionFilter);
+ return d->kernelWidth;
+}
+
+
+/*!
+ \reimp
+
+ \internal
+*/
+QRectF QPixmapConvolutionFilter::boundingRectFor(const QRectF &rect) const
+{
+ Q_D(const QPixmapConvolutionFilter);
+ return rect.adjusted(-d->kernelWidth / 2, -d->kernelHeight / 2, (d->kernelWidth - 1) / 2, (d->kernelHeight - 1) / 2);
+}
+
+// Convolutes the image
+static void convolute(
+ QImage *destImage,
+ const QPointF &pos,
+ const QImage &srcImage,
+ const QRectF &srcRect,
+ QPainter::CompositionMode mode,
+ qreal *kernel,
+ int kernelWidth,
+ int kernelHeight )
+{
+ const QImage processImage = (srcImage.format() != QImage::Format_ARGB32_Premultiplied ) ? srcImage.convertToFormat(QImage::Format_ARGB32_Premultiplied) : srcImage;
+ // TODO: support also other formats directly without copying
+
+ int *fixedKernel = new int[kernelWidth*kernelHeight];
+ for(int i = 0; i < kernelWidth*kernelHeight; i++)
+ {
+ fixedKernel[i] = (int)(65536 * kernel[i]);
+ }
+ QRectF trect = srcRect.isNull() ? processImage.rect() : srcRect;
+ trect.moveTo(pos);
+ QRectF bounded = trect.adjusted(-kernelWidth / 2, -kernelHeight / 2, (kernelWidth - 1) / 2, (kernelHeight - 1) / 2);
+ QRect rect = bounded.toAlignedRect();
+ QRect targetRect = rect.intersected(destImage->rect());
+
+ QRectF srect = srcRect.isNull() ? processImage.rect() : srcRect;
+ QRectF sbounded = srect.adjusted(-kernelWidth / 2, -kernelHeight / 2, (kernelWidth - 1) / 2, (kernelHeight - 1) / 2);
+ QPoint srcStartPoint = sbounded.toAlignedRect().topLeft()+(targetRect.topLeft()-rect.topLeft());
+
+ const uint *sourceStart = (uint*)processImage.scanLine(0);
+ uint *outputStart = (uint*)destImage->scanLine(0);
+
+ int yk = srcStartPoint.y();
+ for (int y = targetRect.top(); y <= targetRect.bottom(); y++) {
+ uint* output = outputStart + (destImage->bytesPerLine()/sizeof(uint))*y+targetRect.left();
+ int xk = srcStartPoint.x();
+ for(int x = targetRect.left(); x <= targetRect.right(); x++) {
+ int r = 0;
+ int g = 0;
+ int b = 0;
+ int a = 0;
+
+ // some out of bounds pre-checking to avoid inner-loop ifs
+ int kernely = -kernelHeight/2;
+ int starty = 0;
+ int endy = kernelHeight;
+ if(yk+kernely+endy >= srcImage.height())
+ endy = kernelHeight-((yk+kernely+endy)-srcImage.height())-1;
+ if(yk+kernely < 0)
+ starty = -(yk+kernely);
+
+ int kernelx = -kernelWidth/2;
+ int startx = 0;
+ int endx = kernelWidth;
+ if(xk+kernelx+endx >= srcImage.width())
+ endx = kernelWidth-((xk+kernelx+endx)-srcImage.width())-1;
+ if(xk+kernelx < 0)
+ startx = -(xk+kernelx);
+
+ for (int ys = starty; ys < endy; ys ++) {
+ const uint *pix = sourceStart + (processImage.bytesPerLine()/sizeof(uint))*(yk+kernely+ys) + ((xk+kernelx+startx));
+ const uint *endPix = pix+endx-startx;
+ int kernelPos = ys*kernelWidth+startx;
+ while (pix < endPix) {
+ int factor = fixedKernel[kernelPos++];
+ a += (((*pix) & 0xff000000)>>24) * factor;
+ r += (((*pix) & 0x00ff0000)>>16) * factor;
+ g += (((*pix) & 0x0000ff00)>>8 ) * factor;
+ b += (((*pix) & 0x000000ff) ) * factor;
+ pix++;
+ }
+ }
+
+ r = qBound((int)0, r >> 16, (int)255);
+ g = qBound((int)0, g >> 16, (int)255);
+ b = qBound((int)0, b >> 16, (int)255);
+ a = qBound((int)0, a >> 16, (int)255);
+ // composition mode checking could be moved outside of loop
+ if(mode == QPainter::CompositionMode_Source) {
+ uint color = (a<<24)+(r<<16)+(g<<8)+b;
+ *output++ = color;
+ } else {
+ uint current = *output;
+ uchar ca = (current&0xff000000)>>24;
+ uchar cr = (current&0x00ff0000)>>16;
+ uchar cg = (current&0x0000ff00)>>8;
+ uchar cb = (current&0x000000ff);
+ uint color =
+ (((ca*(255-a) >> 8)+a) << 24)+
+ (((cr*(255-a) >> 8)+r) << 16)+
+ (((cg*(255-a) >> 8)+g) << 8)+
+ (((cb*(255-a) >> 8)+b));
+ *output++ = color;;
+ }
+ xk++;
+ }
+ yk++;
+ }
+}
+
+/*!
+ \reimp
+
+ \internal
+*/
+void QPixmapConvolutionFilter::draw(QPainter *painter, const QPointF &p, const QPixmap &src, const QRectF& srcRect) const
+{
+ Q_D(const QPixmapConvolutionFilter);
+ if (!painter->isActive())
+ return;
+
+ if(d->kernelWidth<=0 || d->kernelHeight <= 0)
+ return;
+
+ QPixmapFilter *filter = painter->paintEngine() && painter->paintEngine()->isExtended() ?
+ static_cast<QPaintEngineEx *>(painter->paintEngine())->createPixmapFilter(type()) : 0;
+ QPixmapConvolutionFilter *convolutionFilter = static_cast<QPixmapConvolutionFilter*>(filter);
+ if (convolutionFilter) {
+ convolutionFilter->setConvolutionKernel(d->convolutionKernel, d->kernelWidth, d->kernelHeight);
+ convolutionFilter->d_func()->convoluteAlpha = d->convoluteAlpha;
+ convolutionFilter->draw(painter, p, src, srcRect);
+ delete convolutionFilter;
+ return;
+ }
+
+ // falling back to raster implementation
+
+ QImage *target = 0;
+ if (painter->paintEngine()->paintDevice()->devType() == QInternal::Image) {
+ target = static_cast<QImage *>(painter->paintEngine()->paintDevice());
+
+ QTransform mat = painter->combinedTransform();
+
+ if (mat.type() > QTransform::TxTranslate) {
+ // Disabled because of transformation...
+ target = 0;
+ } else {
+ QRasterPaintEngine *pe = static_cast<QRasterPaintEngine *>(painter->paintEngine());
+ if (pe->clipType() == QRasterPaintEngine::ComplexClip)
+ // disabled because of complex clipping...
+ target = 0;
+ else {
+ QRectF clip = pe->clipBoundingRect();
+ QRectF rect = boundingRectFor(srcRect.isEmpty() ? src.rect() : srcRect);
+ QTransform x = painter->deviceTransform();
+ if (!clip.contains(rect.translated(x.dx() + p.x(), x.dy() + p.y()))) {
+ target = 0;
+ }
+
+ }
+ }
+ }
+
+ if (target) {
+ QTransform x = painter->deviceTransform();
+ QPointF offset(x.dx(), x.dy());
+
+ convolute(target, p+offset, src.toImage(), srcRect, QPainter::CompositionMode_SourceOver, d->convolutionKernel, d->kernelWidth, d->kernelHeight);
+ } else {
+ QRect srect = srcRect.isNull() ? src.rect() : srcRect.toRect();
+ QRect rect = boundingRectFor(srect).toRect();
+ QImage result = QImage(rect.size(), QImage::Format_ARGB32_Premultiplied);
+ QPoint offset = srect.topLeft() - rect.topLeft();
+ convolute(&result,
+ offset,
+ src.toImage(),
+ srect,
+ QPainter::CompositionMode_Source,
+ d->convolutionKernel,
+ d->kernelWidth,
+ d->kernelHeight);
+ painter->drawImage(p - offset, result);
+ }
+}
+
+// grayscales the image to dest (could be same). If rect isn't defined
+// destination image size is used to determine the dimension of grayscaling
+// process.
+static void grayscale(const QImage &image, QImage &dest, const QRect& rect = QRect())
+{
+ QRect destRect = rect;
+ QRect srcRect = rect;
+ if (rect.isNull()) {
+ srcRect = dest.rect();
+ destRect = dest.rect();
+ }
+ if (image != dest) {
+ destRect.moveTo(QPoint(0, 0));
+ }
+
+ unsigned int *data = (unsigned int *)image.bits();
+ unsigned int *outData = (unsigned int *)dest.bits();
+
+ if (dest.size() == image.size() && image.rect() == srcRect) {
+ // a bit faster loop for grayscaling everything
+ int pixels = dest.width() * dest.height();
+ for (int i = 0; i < pixels; ++i) {
+ int val = qGray(data[i]);
+ outData[i] = qRgba(val, val, val, qAlpha(data[i]));
+ }
+ } else {
+ int yd = destRect.top();
+ for (int y = srcRect.top(); y <= srcRect.bottom() && y < image.height(); y++) {
+ data = (unsigned int*)image.scanLine(y);
+ outData = (unsigned int*)dest.scanLine(yd++);
+ int xd = destRect.left();
+ for (int x = srcRect.left(); x <= srcRect.right() && x < image.width(); x++) {
+ int val = qGray(data[x]);
+ outData[xd++] = qRgba(val, val, val, qAlpha(data[x]));
+ }
+ }
+ }
+}
+
+/*!
+ \class QPixmapColorizeFilter
+ \since 4.5
+ \ingroup multimedia
+
+ \brief The QPixmapColorizeFilter class provides colorizing
+ filtering for pixmaps.
+
+ A colorize filter gives the pixmap a tint of its color(). The
+ filter first grayscales the pixmap and then converts those to
+ colorized values using QPainter::CompositionMode_Screen with the
+ chosen color. The alpha-channel is not changed.
+
+ Example:
+ \snippet doc/src/snippets/code/src_gui_image_qpixmapfilter.cpp 0
+
+ \sa QPainter::CompositionMode
+
+ \internal
+*/
+class QPixmapColorizeFilterPrivate : public QPixmapFilterPrivate
+{
+ Q_DECLARE_PUBLIC(QPixmapColorizeFilter)
+public:
+ QColor color;
+};
+
+/*!
+ Constructs an pixmap colorize filter.
+
+ Default color value for colorizing is QColor(0, 0, 192).
+
+ \internal
+*/
+QPixmapColorizeFilter::QPixmapColorizeFilter(QObject *parent)
+ : QPixmapFilter(*new QPixmapColorizeFilterPrivate, ColorizeFilter, parent)
+{
+ d_func()->color = QColor(0, 0, 192);
+}
+
+/*!
+ Gets the color of the colorize filter.
+
+ \internal
+*/
+QColor QPixmapColorizeFilter::color() const
+{
+ Q_D(const QPixmapColorizeFilter);
+ return d->color;
+}
+
+/*!
+ Sets the color of the colorize filter to the \a color specified.
+
+ \internal
+*/
+void QPixmapColorizeFilter::setColor(const QColor &color)
+{
+ Q_D(QPixmapColorizeFilter);
+ d->color = color;
+}
+
+/*!
+ \reimp
+
+ \internal
+*/
+void QPixmapColorizeFilter::draw(QPainter *painter, const QPointF &dest, const QPixmap &src, const QRectF &srcRect) const
+{
+ Q_D(const QPixmapColorizeFilter);
+ QPixmapFilter *filter = painter->paintEngine() && painter->paintEngine()->isExtended() ?
+ static_cast<QPaintEngineEx *>(painter->paintEngine())->createPixmapFilter(type()) : 0;
+ QPixmapColorizeFilter *colorizeFilter = static_cast<QPixmapColorizeFilter*>(filter);
+ if (colorizeFilter) {
+ colorizeFilter->setColor(d->color);
+ colorizeFilter->draw(painter, dest, src, srcRect);
+ delete colorizeFilter;
+ return;
+ }
+
+ // falling back to raster implementation
+
+ QImage srcImage;
+ QImage destImage;
+
+ if (srcRect.isNull()) {
+ srcImage = src.toImage();
+ srcImage = srcImage.convertToFormat(srcImage.hasAlphaChannel() ? QImage::Format_ARGB32_Premultiplied : QImage::Format_RGB32);
+ destImage = QImage(srcImage.size(), srcImage.format());
+ } else {
+ QRect rect = srcRect.toAlignedRect().intersected(src.rect());
+
+ srcImage = src.copy(rect).toImage();
+ srcImage = srcImage.convertToFormat(srcImage.hasAlphaChannel() ? QImage::Format_ARGB32_Premultiplied : QImage::Format_RGB32);
+ destImage = QImage(rect.size(), srcImage.format());
+ }
+
+ // do colorizing
+ QPainter destPainter(&destImage);
+ grayscale(srcImage, destImage, srcImage.rect());
+ destPainter.setCompositionMode(QPainter::CompositionMode_Screen);
+ destPainter.fillRect(srcImage.rect(), d->color);
+ destPainter.end();
+
+ if (srcImage.hasAlphaChannel())
+ destImage.setAlphaChannel(srcImage.alphaChannel());
+
+ painter->drawImage(dest, destImage);
+}
+
+class QPixmapDropShadowFilterPrivate : public QPixmapFilterPrivate
+{
+public:
+ QPixmapDropShadowFilterPrivate()
+ : offset(8, 8),
+ radius(1),
+ color(63, 63, 63, 255) {
+ }
+
+ QPointF offset;
+ qreal radius;
+ QColor color;
+
+ QPixmapConvolutionFilter *convolution;
+};
+
+/*!
+ \class QPixmapDropShadowFilter
+ \since 4.5
+ \ingroup multimedia
+
+ \brief The QPixmapDropShadowFilter class is a convenience class
+ for drawing pixmaps with drop shadows.
+
+ The drop shadow is produced by taking a copy of the source pixmap
+ and applying a color to the copy using a
+ QPainter::CompositionMode_DestinationIn operation. This produces a
+ homogeneously-colored pixmap which is then drawn using a
+ QPixmapConvolutionFilter at an offset. The original pixmap is
+ drawn on top.
+
+ The QPixmapDropShadowFilter class provides some customization
+ options to specify how the drop shadow should appear. The color of
+ the drop shadow can be modified using the setColor() function, the
+ drop shadow offset can be modified using the setOffset() function,
+ and the blur radius of the drop shadow can be changed through the
+ setBlurRadius() function.
+
+ By default, the drop shadow is a dark gray shadow, blurred with a
+ radius of 1 at an offset of 8 pixels towards the lower right.
+
+ Example:
+ \snippet doc/src/snippets/code/src_gui_image_qpixmapfilter.cpp 2
+
+ \sa QPixmapColorizeFilter, QPixmapConvolutionFilter
+
+ \internal
+ */
+
+/*!
+ Constructs drop shadow filter.
+
+ \internal
+*/
+QPixmapDropShadowFilter::QPixmapDropShadowFilter(QObject *parent)
+ : QPixmapFilter(*new QPixmapDropShadowFilterPrivate, DropShadowFilter, parent)
+{
+ Q_D(QPixmapDropShadowFilter);
+ d->convolution = new QPixmapConvolutionFilter;
+
+ setBlurRadius(1);
+}
+
+/*!
+ Destroys drop shadow filter.
+
+ \internal
+*/
+QPixmapDropShadowFilter::~QPixmapDropShadowFilter()
+{
+ Q_D(QPixmapDropShadowFilter);
+ delete d->convolution;
+}
+
+/*!
+ Returns the radius in pixels of the blur on the drop shadow.
+
+ A smaller radius results in a sharper shadow.
+
+ \sa color(), offset()
+
+ \internal
+*/
+qreal QPixmapDropShadowFilter::blurRadius() const
+{
+ Q_D(const QPixmapDropShadowFilter);
+ return d->radius;
+}
+
+/*!
+ Sets the radius in pixels of the blur on the drop shadow to the \a radius specified.
+
+ Using a smaller radius results in a sharper shadow.
+
+ \sa setColor(), setOffset()
+
+ \internal
+*/
+void QPixmapDropShadowFilter::setBlurRadius(qreal radius)
+{
+ Q_D(QPixmapDropShadowFilter);
+
+ d->radius = radius;
+
+ int dim = 2 * qRound(radius) + 1;
+ QVarLengthArray<qreal> arr(dim * dim);
+ qreal f = 1 / qreal(dim * dim);
+ for (int i = 0; i < dim * dim; ++i)
+ arr[i] = f;
+ d->convolution->setConvolutionKernel(arr.data(), dim, dim);
+}
+
+/*!
+ Returns the color of the drop shadow.
+
+ \sa blurRadius(), offset()
+
+ \internal
+*/
+QColor QPixmapDropShadowFilter::color() const
+{
+ Q_D(const QPixmapDropShadowFilter);
+ return d->color;
+}
+
+/*!
+ Sets the color of the drop shadow to the \a color specified.
+
+ \sa setBlurRadius(), setOffset()
+
+ \internal
+*/
+void QPixmapDropShadowFilter::setColor(const QColor &color)
+{
+ Q_D(QPixmapDropShadowFilter);
+ d->color = color;
+}
+
+/*!
+ Returns the shadow offset in pixels.
+
+ \sa blurRadius(), color()
+
+ \internal
+*/
+QPointF QPixmapDropShadowFilter::offset() const
+{
+ Q_D(const QPixmapDropShadowFilter);
+ return d->offset;
+}
+
+/*!
+ Sets the shadow offset in pixels to the \a offset specified.
+
+ \sa setBlurRadius(), setColor()
+
+ \internal
+*/
+void QPixmapDropShadowFilter::setOffset(const QPointF &offset)
+{
+ Q_D(QPixmapDropShadowFilter);
+ d->offset = offset;
+}
+
+/*!
+ \fn void QPixmapDropShadowFilter::setOffset(qreal dx, qreal dy)
+ \overload
+
+ Sets the shadow offset in pixels to be the displacement specified by the
+ horizontal \a dx and vertical \a dy coordinates.
+
+ \sa setBlurRadius(), setColor()
+
+ \internal
+*/
+
+/*!
+ \reimp
+
+ \internal
+ */
+QRectF QPixmapDropShadowFilter::boundingRectFor(const QRectF &rect) const
+{
+ Q_D(const QPixmapDropShadowFilter);
+
+ qreal x1 = qMin(rect.left(), rect.left() + d->offset.x() - d->radius);
+ qreal y1 = qMin(rect.top(), rect.top() + d->offset.y() - d->radius);
+ qreal x2 = qMax(rect.right(), rect.right() + d->offset.x() + d->radius);
+ qreal y2 = qMax(rect.bottom(), rect.bottom() + d->offset.y() + d->radius);
+
+ return QRectF(x1, y1, x2 - x1, y2 - y1);
+}
+
+/*!
+ \reimp
+
+ \internal
+ */
+void QPixmapDropShadowFilter::draw(QPainter *p,
+ const QPointF &pos,
+ const QPixmap &px,
+ const QRectF &src) const
+{
+ Q_D(const QPixmapDropShadowFilter);
+
+ QPixmap tmp = src.isNull() ? px : px.copy(src.toRect());
+ QPainter tmpPainter(&tmp);
+
+ // blacken the image...
+ tmpPainter.setCompositionMode(QPainter::CompositionMode_SourceIn);
+ tmpPainter.fillRect(0, 0, tmp.width(), tmp.height(), d->color);
+ tmpPainter.end();
+
+ // draw the blurred drop shadow...
+ d->convolution->draw(p, pos + d->offset, tmp);
+
+ // Draw the actual pixmap...
+ p->drawPixmap(pos, px, src);
+}
+QT_END_NAMESPACE
diff --git a/src/gui/image/qpixmapfilter_p.h b/src/gui/image/qpixmapfilter_p.h
new file mode 100644
index 0000000000..d494c9873a
--- /dev/null
+++ b/src/gui/image/qpixmapfilter_p.h
@@ -0,0 +1,165 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QPIXMAPFILTER_H
+#define QPIXMAPFILTER_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 <QtCore/qnamespace.h>
+#include <QtGui/qpixmap.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+class QPainter;
+class QPixmapData;
+
+class QPixmapFilterPrivate;
+
+class Q_GUI_EXPORT QPixmapFilter : public QObject
+{
+ Q_OBJECT
+ Q_DECLARE_PRIVATE(QPixmapFilter)
+public:
+ virtual ~QPixmapFilter() = 0;
+
+ enum FilterType {
+ ConvolutionFilter,
+ ColorizeFilter,
+ DropShadowFilter,
+
+ UserFilter = 1024
+ };
+
+ FilterType type() const;
+
+ virtual QRectF boundingRectFor(const QRectF &rect) const;
+
+ virtual void draw(QPainter *painter, const QPointF &p, const QPixmap &src, const QRectF &srcRect = QRectF()) const = 0;
+
+protected:
+ QPixmapFilter(QPixmapFilterPrivate &d, FilterType type, QObject *parent);
+ QPixmapFilter(FilterType type, QObject *parent);
+};
+
+class QPixmapConvolutionFilterPrivate;
+
+class Q_GUI_EXPORT QPixmapConvolutionFilter : public QPixmapFilter
+{
+ Q_OBJECT
+ Q_DECLARE_PRIVATE(QPixmapConvolutionFilter)
+
+public:
+ QPixmapConvolutionFilter(QObject *parent = 0);
+ ~QPixmapConvolutionFilter();
+
+ void setConvolutionKernel(const qreal *matrix, int rows, int columns);
+
+ QRectF boundingRectFor(const QRectF &rect) const;
+ void draw(QPainter *painter, const QPointF &dest, const QPixmap &src, const QRectF &srcRect = QRectF()) const;
+
+private:
+ friend class QGLPixmapConvolutionFilter;
+ friend class QVGPixmapConvolutionFilter;
+ const qreal *convolutionKernel() const;
+ int rows() const;
+ int columns() const;
+};
+
+class QPixmapColorizeFilterPrivate;
+
+class Q_GUI_EXPORT QPixmapColorizeFilter : public QPixmapFilter
+{
+ Q_OBJECT
+ Q_DECLARE_PRIVATE(QPixmapColorizeFilter)
+
+public:
+ QPixmapColorizeFilter(QObject *parent = 0);
+
+ void setColor(const QColor& color);
+ QColor color() const;
+
+ void draw(QPainter *painter, const QPointF &dest, const QPixmap &src, const QRectF &srcRect = QRectF()) const;
+};
+
+class QPixmapDropShadowFilterPrivate;
+
+class Q_GUI_EXPORT QPixmapDropShadowFilter : public QPixmapFilter
+{
+ Q_OBJECT
+ Q_DECLARE_PRIVATE(QPixmapDropShadowFilter)
+
+public:
+ QPixmapDropShadowFilter(QObject *parent = 0);
+ ~QPixmapDropShadowFilter();
+
+ QRectF boundingRectFor(const QRectF &rect) const;
+ void draw(QPainter *p, const QPointF &pos, const QPixmap &px, const QRectF &src = QRectF()) const;
+
+ qreal blurRadius() const;
+ void setBlurRadius(qreal radius);
+
+ QColor color() const;
+ void setColor(const QColor &color);
+
+ QPointF offset() const;
+ void setOffset(const QPointF &offset);
+ inline void setOffset(qreal dx, qreal dy) { setOffset(QPointF(dx, dy)); }
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QPIXMAPFILTER_H
diff --git a/src/gui/image/qpnghandler.cpp b/src/gui/image/qpnghandler.cpp
new file mode 100644
index 0000000000..0a55910e45
--- /dev/null
+++ b/src/gui/image/qpnghandler.cpp
@@ -0,0 +1,973 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "private/qpnghandler_p.h"
+
+#ifndef QT_NO_IMAGEFORMAT_PNG
+#include <qcoreapplication.h>
+#include <qiodevice.h>
+#include <qimage.h>
+#include <qlist.h>
+#include <qtextcodec.h>
+#include <qvariant.h>
+#include <qvector.h>
+
+#include <png.h>
+#include <pngconf.h>
+
+#ifdef Q_OS_WINCE
+#define CALLBACK_CALL_TYPE __cdecl
+#else
+#define CALLBACK_CALL_TYPE
+#endif
+
+QT_BEGIN_NAMESPACE
+
+#if defined(Q_OS_WINCE) && defined(STANDARDSHELL_UI_MODEL)
+# define Q_INTERNAL_WIN_NO_THROW __declspec(nothrow)
+#else
+# define Q_INTERNAL_WIN_NO_THROW
+#endif
+
+/*
+ All PNG files load to the minimal QImage equivalent.
+
+ All QImage formats output to reasonably efficient PNG equivalents.
+ Never to grayscale.
+*/
+
+#if defined(Q_C_CALLBACKS)
+extern "C" {
+#endif
+
+class QPNGImageWriter {
+public:
+ explicit QPNGImageWriter(QIODevice*);
+ ~QPNGImageWriter();
+
+ enum DisposalMethod { Unspecified, NoDisposal, RestoreBackground, RestoreImage };
+ void setDisposalMethod(DisposalMethod);
+ void setLooping(int loops=0); // 0 == infinity
+ void setFrameDelay(int msecs);
+ void setGamma(float);
+
+ bool writeImage(const QImage& img, int x, int y);
+ bool writeImage(const QImage& img, int quality, const QString &description, int x, int y);
+ bool writeImage(const QImage& img)
+ { return writeImage(img, 0, 0); }
+ bool writeImage(const QImage& img, int quality, const QString &description)
+ { return writeImage(img, quality, description, 0, 0); }
+
+ QIODevice* device() { return dev; }
+
+private:
+ QIODevice* dev;
+ int frames_written;
+ DisposalMethod disposal;
+ int looping;
+ int ms_delay;
+ float gamma;
+};
+
+static
+void CALLBACK_CALL_TYPE iod_read_fn(png_structp png_ptr, png_bytep data, png_size_t length)
+{
+ QIODevice *in = (QIODevice *)png_get_io_ptr(png_ptr);
+
+ while (length) {
+ int nr = in->read((char*)data, length);
+ if (nr <= 0) {
+ png_error(png_ptr, "Read Error");
+ return;
+ }
+ length -= nr;
+ }
+}
+
+
+static
+void CALLBACK_CALL_TYPE qpiw_write_fn(png_structp png_ptr, png_bytep data, png_size_t length)
+{
+ QPNGImageWriter* qpiw = (QPNGImageWriter*)png_get_io_ptr(png_ptr);
+ QIODevice* out = qpiw->device();
+
+ uint nr = out->write((char*)data, length);
+ if (nr != length) {
+ png_error(png_ptr, "Write Error");
+ return;
+ }
+}
+
+
+static
+void CALLBACK_CALL_TYPE qpiw_flush_fn(png_structp /* png_ptr */)
+{
+}
+
+#if defined(Q_C_CALLBACKS)
+}
+#endif
+
+static
+void setup_qt(QImage& image, png_structp png_ptr, png_infop info_ptr, float screen_gamma=0.0)
+{
+ if (screen_gamma != 0.0 && png_get_valid(png_ptr, info_ptr, PNG_INFO_gAMA)) {
+ double file_gamma;
+ png_get_gAMA(png_ptr, info_ptr, &file_gamma);
+ png_set_gamma(png_ptr, screen_gamma, file_gamma);
+ }
+
+ png_uint_32 width;
+ png_uint_32 height;
+ int bit_depth;
+ int color_type;
+ png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, 0, 0, 0);
+
+ if (color_type == PNG_COLOR_TYPE_GRAY) {
+ // Black & White or 8-bit grayscale
+ if (bit_depth == 1 && info_ptr->channels == 1) {
+ png_set_invert_mono(png_ptr);
+ png_read_update_info(png_ptr, info_ptr);
+ if (image.size() != QSize(width, height) || image.format() != QImage::Format_Mono) {
+ image = QImage(width, height, QImage::Format_Mono);
+ if (image.isNull())
+ return;
+ }
+ image.setNumColors(2);
+ image.setColor(1, qRgb(0,0,0));
+ image.setColor(0, qRgb(255,255,255));
+ } else if (bit_depth == 16 && png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
+ png_set_expand(png_ptr);
+ png_set_strip_16(png_ptr);
+ png_set_gray_to_rgb(png_ptr);
+ if (image.size() != QSize(width, height) || image.format() != QImage::Format_ARGB32) {
+ image = QImage(width, height, QImage::Format_ARGB32);
+ if (image.isNull())
+ return;
+ }
+ if (QSysInfo::ByteOrder == QSysInfo::BigEndian)
+ png_set_swap_alpha(png_ptr);
+
+ png_read_update_info(png_ptr, info_ptr);
+ } else {
+ if (bit_depth == 16)
+ png_set_strip_16(png_ptr);
+ else if (bit_depth < 8)
+ png_set_packing(png_ptr);
+ int ncols = bit_depth < 8 ? 1 << bit_depth : 256;
+ png_read_update_info(png_ptr, info_ptr);
+ if (image.size() != QSize(width, height) || image.format() != QImage::Format_Indexed8) {
+ image = QImage(width, height, QImage::Format_Indexed8);
+ if (image.isNull())
+ return;
+ }
+ image.setNumColors(ncols);
+ for (int i=0; i<ncols; i++) {
+ int c = i*255/(ncols-1);
+ image.setColor(i, qRgba(c,c,c,0xff));
+ }
+ if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
+ const int g = info_ptr->trans_values.gray;
+ if (g < ncols) {
+ image.setColor(g, 0);
+ }
+ }
+ }
+ } else if (color_type == PNG_COLOR_TYPE_PALETTE
+ && png_get_valid(png_ptr, info_ptr, PNG_INFO_PLTE)
+ && info_ptr->num_palette <= 256)
+ {
+ // 1-bit and 8-bit color
+ if (bit_depth != 1)
+ png_set_packing(png_ptr);
+ png_read_update_info(png_ptr, info_ptr);
+ png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, 0, 0, 0);
+ QImage::Format format = bit_depth == 1 ? QImage::Format_Mono : QImage::Format_Indexed8;
+ if (image.size() != QSize(width, height) || image.format() != format) {
+ image = QImage(width, height, format);
+ if (image.isNull())
+ return;
+ }
+ image.setNumColors(info_ptr->num_palette);
+ int i = 0;
+ if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
+ while (i < info_ptr->num_trans) {
+ image.setColor(i, qRgba(
+ info_ptr->palette[i].red,
+ info_ptr->palette[i].green,
+ info_ptr->palette[i].blue,
+ info_ptr->trans[i]
+ )
+ );
+ i++;
+ }
+ }
+ while (i < info_ptr->num_palette) {
+ image.setColor(i, qRgba(
+ info_ptr->palette[i].red,
+ info_ptr->palette[i].green,
+ info_ptr->palette[i].blue,
+ 0xff
+ )
+ );
+ i++;
+ }
+ } else {
+ // 32-bit
+ if (bit_depth == 16)
+ png_set_strip_16(png_ptr);
+
+ png_set_expand(png_ptr);
+
+ if (color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
+ png_set_gray_to_rgb(png_ptr);
+
+ QImage::Format format = QImage::Format_ARGB32;
+ // Only add filler if no alpha, or we can get 5 channel data.
+ if (!(color_type & PNG_COLOR_MASK_ALPHA)
+ && !png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
+ png_set_filler(png_ptr, 0xff, QSysInfo::ByteOrder == QSysInfo::BigEndian ?
+ PNG_FILLER_BEFORE : PNG_FILLER_AFTER);
+ // We want 4 bytes, but it isn't an alpha channel
+ format = QImage::Format_RGB32;
+ }
+ if (image.size() != QSize(width, height) || image.format() != format) {
+ image = QImage(width, height, format);
+ if (image.isNull())
+ return;
+ }
+
+ if (QSysInfo::ByteOrder == QSysInfo::BigEndian)
+ png_set_swap_alpha(png_ptr);
+
+ png_read_update_info(png_ptr, info_ptr);
+ }
+
+ // Qt==ARGB==Big(ARGB)==Little(BGRA)
+ if (QSysInfo::ByteOrder == QSysInfo::LittleEndian) {
+ png_set_bgr(png_ptr);
+ }
+}
+
+
+#if defined(Q_C_CALLBACKS)
+extern "C" {
+#endif
+static void CALLBACK_CALL_TYPE qt_png_warning(png_structp /*png_ptr*/, png_const_charp message)
+{
+ qWarning("libpng warning: %s", message);
+}
+
+#if defined(Q_C_CALLBACKS)
+}
+#endif
+
+class QPngHandlerPrivate
+{
+public:
+ enum State {
+ Ready,
+ ReadHeader,
+ Error
+ };
+
+ QPngHandlerPrivate(QPngHandler *qq)
+ : gamma(0.0), quality(2), png_ptr(0), info_ptr(0),
+ end_info(0), row_pointers(0), state(Ready), q(qq)
+ { }
+
+ float gamma;
+ int quality;
+ QString description;
+
+ png_struct *png_ptr;
+ png_info *info_ptr;
+ png_info *end_info;
+ png_byte **row_pointers;
+
+ bool readPngHeader();
+ bool readPngImage(QImage *image);
+
+ QImage::Format readImageFormat();
+
+ State state;
+
+ QPngHandler *q;
+};
+
+/*!
+ \internal
+*/
+bool Q_INTERNAL_WIN_NO_THROW QPngHandlerPrivate::readPngHeader()
+{
+ state = Error;
+ png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,0,0,0);
+ if (!png_ptr)
+ return false;
+
+ png_set_error_fn(png_ptr, 0, 0, qt_png_warning);
+
+ info_ptr = png_create_info_struct(png_ptr);
+ if (!info_ptr) {
+ png_destroy_read_struct(&png_ptr, 0, 0);
+ png_ptr = 0;
+ return false;
+ }
+
+ end_info = png_create_info_struct(png_ptr);
+ if (!end_info) {
+ png_destroy_read_struct(&png_ptr, &info_ptr, 0);
+ png_ptr = 0;
+ return false;
+ }
+
+ if (setjmp(png_jmpbuf(png_ptr))) {
+ png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
+ png_ptr = 0;
+ return false;
+ }
+
+ png_set_read_fn(png_ptr, q->device(), iod_read_fn);
+ png_read_info(png_ptr, info_ptr);
+
+#ifndef QT_NO_IMAGE_TEXT
+ png_textp text_ptr;
+ int num_text=0;
+ png_get_text(png_ptr,info_ptr,&text_ptr,&num_text);
+
+ while (num_text--) {
+ QString key, value;
+#if defined(PNG_iTXt_SUPPORTED)
+ if (text_ptr->lang) {
+ QTextCodec *codec = QTextCodec::codecForName(text_ptr->lang);
+ if (codec) {
+ key = codec->toUnicode(text_ptr->lang_key);
+ value = codec->toUnicode(QByteArray(text_ptr->text, text_ptr->itxt_length));
+ } else {
+ key = QString::fromLatin1(text_ptr->key);
+ value = QString::fromLatin1(QByteArray(text_ptr->text, int(text_ptr->text_length)));
+ }
+ } else
+#endif
+ {
+ key = QString::fromLatin1(text_ptr->key);
+ value = QString::fromLatin1(QByteArray(text_ptr->text, int(text_ptr->text_length)));
+ }
+ if (!description.isEmpty())
+ description += QLatin1String("\n\n");
+ description += key + QLatin1String(": ") + value.simplified();
+ text_ptr++;
+ }
+#endif
+
+ state = ReadHeader;
+ return true;
+}
+
+/*!
+ \internal
+*/
+bool Q_INTERNAL_WIN_NO_THROW QPngHandlerPrivate::readPngImage(QImage *outImage)
+{
+ if (state == Error)
+ return false;
+
+ if (state == Ready && !readPngHeader()) {
+ state = Error;
+ return false;
+ }
+
+ row_pointers = 0;
+ if (setjmp(png_jmpbuf(png_ptr))) {
+ png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
+ delete [] row_pointers;
+ png_ptr = 0;
+ state = Error;
+ return false;
+ }
+
+ setup_qt(*outImage, png_ptr, info_ptr, gamma);
+
+ if (outImage->isNull()) {
+ png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
+ delete [] row_pointers;
+ png_ptr = 0;
+ state = Error;
+ return false;
+ }
+
+ png_uint_32 width;
+ png_uint_32 height;
+ int bit_depth;
+ int color_type;
+ png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type,
+ 0, 0, 0);
+
+ uchar *data = outImage->bits();
+ int bpl = outImage->bytesPerLine();
+ row_pointers = new png_bytep[height];
+
+ for (uint y = 0; y < height; y++)
+ row_pointers[y] = data + y * bpl;
+
+ png_read_image(png_ptr, row_pointers);
+
+#if 0 // libpng takes care of this.
+ png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)
+ if (outImage->depth()==32 && png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
+ QRgb trans = 0xFF000000 | qRgb(
+ (info_ptr->trans_values.red << 8 >> bit_depth)&0xff,
+ (info_ptr->trans_values.green << 8 >> bit_depth)&0xff,
+ (info_ptr->trans_values.blue << 8 >> bit_depth)&0xff);
+ for (uint y=0; y<height; y++) {
+ for (uint x=0; x<info_ptr->width; x++) {
+ if (((uint**)jt)[y][x] == trans) {
+ ((uint**)jt)[y][x] &= 0x00FFFFFF;
+ } else {
+ }
+ }
+ }
+ }
+#endif
+
+ outImage->setDotsPerMeterX(png_get_x_pixels_per_meter(png_ptr,info_ptr));
+ outImage->setDotsPerMeterY(png_get_y_pixels_per_meter(png_ptr,info_ptr));
+
+#ifndef QT_NO_IMAGE_TEXT
+ png_textp text_ptr;
+ int num_text=0;
+ png_get_text(png_ptr,info_ptr,&text_ptr,&num_text);
+ while (num_text--) {
+ outImage->setText(text_ptr->key,0,QString::fromAscii(text_ptr->text));
+ text_ptr++;
+ }
+
+ foreach (const QString &pair, description.split(QLatin1String("\n\n"))) {
+ int index = pair.indexOf(QLatin1Char(':'));
+ if (index >= 0 && pair.indexOf(QLatin1Char(' ')) < index) {
+ outImage->setText(QLatin1String("Description"), pair.simplified());
+ } else {
+ QString key = pair.left(index);
+ outImage->setText(key, pair.mid(index + 2).simplified());
+ }
+ }
+#endif
+
+ png_read_end(png_ptr, end_info);
+ png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
+ delete [] row_pointers;
+ png_ptr = 0;
+ state = Ready;
+
+ // sanity check palette entries
+ if (color_type == PNG_COLOR_TYPE_PALETTE
+ && outImage->format() == QImage::Format_Indexed8) {
+ int color_table_size = outImage->numColors();
+ for (int y=0; y<(int)height; ++y) {
+ uchar *p = outImage->scanLine(y);
+ uchar *end = p + width;
+ while (p < end) {
+ if (*p >= color_table_size)
+ *p = 0;
+ ++p;
+ }
+ }
+ }
+
+ return true;
+}
+
+QImage::Format QPngHandlerPrivate::readImageFormat()
+{
+ QImage::Format format = QImage::Format_Invalid;
+ png_uint_32 width, height;
+ int bit_depth, color_type;
+ if (info_ptr->color_type == PNG_COLOR_TYPE_GRAY) {
+ // Black & White or 8-bit grayscale
+ if (info_ptr->bit_depth == 1 && info_ptr->channels == 1) {
+ format = QImage::Format_Mono;
+ } else if (info_ptr->bit_depth == 16 && png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
+ format = QImage::Format_ARGB32;
+ } else {
+ format = QImage::Format_Indexed8;
+ }
+ } else if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE
+ && png_get_valid(png_ptr, info_ptr, PNG_INFO_PLTE)
+ && info_ptr->num_palette <= 256)
+ {
+ // 1-bit and 8-bit color
+ if (info_ptr->bit_depth != 1)
+ png_set_packing(png_ptr);
+ png_read_update_info(png_ptr, info_ptr);
+ png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, 0, 0, 0);
+ format = bit_depth == 1 ? QImage::Format_Mono : QImage::Format_Indexed8;
+ } else {
+ // 32-bit
+ if (info_ptr->bit_depth == 16)
+ png_set_strip_16(png_ptr);
+
+ format = QImage::Format_ARGB32;
+ // Only add filler if no alpha, or we can get 5 channel data.
+ if (!(info_ptr->color_type & PNG_COLOR_MASK_ALPHA)
+ && !png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
+ // We want 4 bytes, but it isn't an alpha channel
+ format = QImage::Format_RGB32;
+ }
+ }
+
+ return format;
+}
+
+QPNGImageWriter::QPNGImageWriter(QIODevice* iod) :
+ dev(iod),
+ frames_written(0),
+ disposal(Unspecified),
+ looping(-1),
+ ms_delay(-1),
+ gamma(0.0)
+{
+}
+
+QPNGImageWriter::~QPNGImageWriter()
+{
+}
+
+void QPNGImageWriter::setDisposalMethod(DisposalMethod dm)
+{
+ disposal = dm;
+}
+
+void QPNGImageWriter::setLooping(int loops)
+{
+ looping = loops;
+}
+
+void QPNGImageWriter::setFrameDelay(int msecs)
+{
+ ms_delay = msecs;
+}
+
+void QPNGImageWriter::setGamma(float g)
+{
+ gamma = g;
+}
+
+
+#ifndef QT_NO_IMAGE_TEXT
+static void set_text(const QImage &image, png_structp png_ptr, png_infop info_ptr,
+ const QString &description)
+{
+ QMap<QString, QString> text;
+ foreach (const QString &key, image.textKeys()) {
+ if (!key.isEmpty())
+ text.insert(key, image.text(key));
+ }
+ foreach (const QString &pair, description.split(QLatin1String("\n\n"))) {
+ int index = pair.indexOf(QLatin1Char(':'));
+ if (index >= 0 && pair.indexOf(QLatin1Char(' ')) < index) {
+ QString s = pair.simplified();
+ if (!s.isEmpty())
+ text.insert(QLatin1String("Description"), s);
+ } else {
+ QString key = pair.left(index);
+ if (!key.simplified().isEmpty())
+ text.insert(key, pair.mid(index + 2).simplified());
+ }
+ }
+
+ if (text.isEmpty())
+ return;
+
+ png_textp text_ptr = new png_text[text.size()];
+
+ QMap<QString, QString>::ConstIterator it = text.constBegin();
+ int i = 0;
+ while (it != text.constEnd()) {
+ QString t = it.value();
+ if (t.length() < 40)
+ text_ptr[i].compression = PNG_TEXT_COMPRESSION_NONE;
+ else
+ text_ptr[i].compression = PNG_TEXT_COMPRESSION_zTXt;
+ text_ptr[i].key = qstrdup(it.key().left(79).toLatin1().constData());
+
+#ifndef PNG_iTXt_SUPPORTED
+ QByteArray value = it.value().toLatin1();
+ text_ptr[i].text = qstrdup(value.constData());
+ text_ptr[i].text_length = value.size();
+#else
+ QByteArray value = it.value().toUtf8();
+ text_ptr[i].text = qstrdup(value.constData());
+ text_ptr[i].text_length = 0;
+ text_ptr[i].itxt_length = value.size();
+ text_ptr[i].lang = "UTF-8";
+ text_ptr[i].lang_key = qstrdup(it.key().toUtf8().constData());
+#endif
+ ++i;
+ ++it;
+ }
+
+ png_set_text(png_ptr, info_ptr, text_ptr, i);
+ for (i = 0; i < text.size(); ++i) {
+ delete [] text_ptr[i].key;
+ delete [] text_ptr[i].text;
+#ifdef PNG_iTXt_SUPPORTED
+ delete [] text_ptr[i].lang_key;
+#endif
+ }
+ delete [] text_ptr;
+}
+#endif
+
+bool QPNGImageWriter::writeImage(const QImage& image, int off_x, int off_y)
+{
+ return writeImage(image, -1, QString(), off_x, off_y);
+}
+
+bool Q_INTERNAL_WIN_NO_THROW QPNGImageWriter::writeImage(const QImage& image_in, int quality_in, const QString &description,
+ int off_x_in, int off_y_in)
+{
+#ifdef QT_NO_IMAGE_TEXT
+ Q_UNUSED(description);
+#endif
+
+ QImage image;
+ switch (image_in.format()) {
+ case QImage::Format_ARGB32_Premultiplied:
+ case QImage::Format_ARGB4444_Premultiplied:
+ case QImage::Format_ARGB8555_Premultiplied:
+ case QImage::Format_ARGB8565_Premultiplied:
+ case QImage::Format_ARGB6666_Premultiplied:
+ image = image_in.convertToFormat(QImage::Format_ARGB32);
+ break;
+ case QImage::Format_RGB16:
+ case QImage::Format_RGB444:
+ case QImage::Format_RGB555:
+ case QImage::Format_RGB666:
+ case QImage::Format_RGB888:
+ image = image_in.convertToFormat(QImage::Format_RGB32);
+ break;
+ default:
+ image = image_in;
+ break;
+ }
+
+ QPoint offset = image.offset();
+ int off_x = off_x_in + offset.x();
+ int off_y = off_y_in + offset.y();
+
+ png_structp png_ptr;
+ png_infop info_ptr;
+ png_bytep* row_pointers;
+
+ png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING,0,0,0);
+ if (!png_ptr) {
+ return false;
+ }
+
+ png_set_error_fn(png_ptr, 0, 0, qt_png_warning);
+
+ info_ptr = png_create_info_struct(png_ptr);
+ if (!info_ptr) {
+ png_destroy_write_struct(&png_ptr, 0);
+ return false;
+ }
+
+ if (setjmp(png_jmpbuf(png_ptr))) {
+ png_destroy_write_struct(&png_ptr, &info_ptr);
+ return false;
+ }
+
+ int quality = quality_in;
+ if (quality >= 0) {
+ if (quality > 9) {
+ qWarning("PNG: Quality %d out of range", quality);
+ quality = 9;
+ }
+ png_set_compression_level(png_ptr, quality);
+ }
+
+ if (gamma != 0.0) {
+ png_set_gAMA(png_ptr, info_ptr, 1.0/gamma);
+ }
+
+ png_set_write_fn(png_ptr, (void*)this, qpiw_write_fn, qpiw_flush_fn);
+
+ info_ptr->channels =
+ (image.depth() == 32)
+ ? (image.format() == QImage::Format_RGB32 ? 3 : 4)
+ : 1;
+
+ png_set_IHDR(png_ptr, info_ptr, image.width(), image.height(),
+ image.depth() == 1 ? 1 : 8 /* per channel */,
+ image.depth() == 32
+ ? image.format() == QImage::Format_RGB32
+ ? PNG_COLOR_TYPE_RGB
+ : PNG_COLOR_TYPE_RGB_ALPHA
+ : PNG_COLOR_TYPE_PALETTE, 0, 0, 0);
+
+
+ //png_set_sBIT(png_ptr, info_ptr, 8);
+ info_ptr->sig_bit.red = 8;
+ info_ptr->sig_bit.green = 8;
+ info_ptr->sig_bit.blue = 8;
+
+ if (image.format() == QImage::Format_MonoLSB)
+ png_set_packswap(png_ptr);
+
+ png_colorp palette = 0;
+ png_bytep copy_trans = 0;
+ if (image.numColors()) {
+ // Paletted
+ int num_palette = image.numColors();
+ palette = new png_color[num_palette];
+ png_set_PLTE(png_ptr, info_ptr, palette, num_palette);
+ int* trans = new int[num_palette];
+ int num_trans = 0;
+ for (int i=0; i<num_palette; i++) {
+ QRgb rgb=image.color(i);
+ info_ptr->palette[i].red = qRed(rgb);
+ info_ptr->palette[i].green = qGreen(rgb);
+ info_ptr->palette[i].blue = qBlue(rgb);
+ trans[i] = rgb >> 24;
+ if (trans[i] < 255) {
+ num_trans = i+1;
+ }
+ }
+ if (num_trans) {
+ copy_trans = new png_byte[num_trans];
+ for (int i=0; i<num_trans; i++)
+ copy_trans[i] = trans[i];
+ png_set_tRNS(png_ptr, info_ptr, copy_trans, num_trans, 0);
+ }
+ delete [] trans;
+ }
+
+ if (image.format() != QImage::Format_RGB32) {
+ info_ptr->sig_bit.alpha = 8;
+ }
+
+ // Swap ARGB to RGBA (normal PNG format) before saving on
+ // BigEndian machines
+ if (QSysInfo::ByteOrder == QSysInfo::BigEndian) {
+ png_set_swap_alpha(png_ptr);
+ }
+
+ // Qt==ARGB==Big(ARGB)==Little(BGRA)
+ if (QSysInfo::ByteOrder == QSysInfo::LittleEndian) {
+ png_set_bgr(png_ptr);
+ }
+
+ if (off_x || off_y) {
+ png_set_oFFs(png_ptr, info_ptr, off_x, off_y, PNG_OFFSET_PIXEL);
+ }
+
+ if (frames_written > 0)
+ png_set_sig_bytes(png_ptr, 8);
+
+ if (image.dotsPerMeterX() > 0 || image.dotsPerMeterY() > 0) {
+ png_set_pHYs(png_ptr, info_ptr,
+ image.dotsPerMeterX(), image.dotsPerMeterY(),
+ PNG_RESOLUTION_METER);
+ }
+
+#ifndef QT_NO_IMAGE_TEXT
+ set_text(image, png_ptr, info_ptr, description);
+#endif
+ png_write_info(png_ptr, info_ptr);
+
+ if (image.depth() != 1)
+ png_set_packing(png_ptr);
+
+ if (image.format() == QImage::Format_RGB32)
+ png_set_filler(png_ptr, 0,
+ QSysInfo::ByteOrder == QSysInfo::BigEndian ?
+ PNG_FILLER_BEFORE : PNG_FILLER_AFTER);
+
+ if (looping >= 0 && frames_written == 0) {
+ uchar data[13] = "NETSCAPE2.0";
+ // 0123456789aBC
+ data[0xB] = looping%0x100;
+ data[0xC] = looping/0x100;
+ png_write_chunk(png_ptr, (png_byte*)"gIFx", data, 13);
+ }
+ if (ms_delay >= 0 || disposal!=Unspecified) {
+ uchar data[4];
+ data[0] = disposal;
+ data[1] = 0;
+ data[2] = (ms_delay/10)/0x100; // hundredths
+ data[3] = (ms_delay/10)%0x100;
+ png_write_chunk(png_ptr, (png_byte*)"gIFg", data, 4);
+ }
+
+ png_uint_32 width;
+ png_uint_32 height;
+ int bit_depth;
+ int color_type;
+ png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type,
+ 0, 0, 0);
+
+ const uchar *data = image.bits();
+ int bpl = image.bytesPerLine();
+ row_pointers = new png_bytep[height];
+ uint y;
+ for (y=0; y<height; y++) {
+ row_pointers[y] = (png_bytep)(data + y * bpl);
+ }
+ png_write_image(png_ptr, row_pointers);
+ delete [] row_pointers;
+
+ png_write_end(png_ptr, info_ptr);
+ frames_written++;
+
+ if (palette)
+ delete [] palette;
+ if (copy_trans)
+ delete [] copy_trans;
+
+ png_destroy_write_struct(&png_ptr, &info_ptr);
+
+ return true;
+}
+
+static bool write_png_image(const QImage &image, QIODevice *device,
+ int quality, float gamma, const QString &description)
+{
+ QPNGImageWriter writer(device);
+ if (quality >= 0) {
+ quality = qMin(quality, 100);
+ quality = (100-quality) * 9 / 91; // map [0,100] -> [9,0]
+ }
+ writer.setGamma(gamma);
+ return writer.writeImage(image, quality, description);
+}
+
+QPngHandler::QPngHandler()
+ : d(new QPngHandlerPrivate(this))
+{
+}
+
+QPngHandler::~QPngHandler()
+{
+ if (d->png_ptr)
+ png_destroy_read_struct(&d->png_ptr, &d->info_ptr, &d->end_info);
+ delete d;
+}
+
+bool QPngHandler::canRead() const
+{
+ if (d->state == QPngHandlerPrivate::Ready) {
+ if (!canRead(device()))
+ return false;
+ setFormat("png");
+ return true;
+ }
+ return d->state != QPngHandlerPrivate::Error;
+}
+
+bool QPngHandler::canRead(QIODevice *device)
+{
+ if (!device) {
+ qWarning("QPngHandler::canRead() called with no device");
+ return false;
+ }
+
+ return device->peek(8) == "\x89\x50\x4E\x47\x0D\x0A\x1A\x0A";
+}
+
+bool QPngHandler::read(QImage *image)
+{
+ if (!canRead())
+ return false;
+ return d->readPngImage(image);
+}
+
+bool QPngHandler::write(const QImage &image)
+{
+ return write_png_image(image, device(), d->quality, d->gamma, d->description);
+}
+
+bool QPngHandler::supportsOption(ImageOption option) const
+{
+ return option == Gamma
+ || option == Description
+ || option == ImageFormat
+ || option == Quality
+ || option == Size;
+}
+
+QVariant QPngHandler::option(ImageOption option) const
+{
+ if (d->state == QPngHandlerPrivate::Error)
+ return QVariant();
+ if (d->state == QPngHandlerPrivate::Ready && !d->readPngHeader())
+ return QVariant();
+
+ if (option == Gamma)
+ return d->gamma;
+ else if (option == Quality)
+ return d->quality;
+ else if (option == Description)
+ return d->description;
+ else if (option == Size)
+ return QSize(d->info_ptr->width, d->info_ptr->height);
+ else if (option == ImageFormat)
+ return d->readImageFormat();
+ return 0;
+}
+
+void QPngHandler::setOption(ImageOption option, const QVariant &value)
+{
+ if (option == Gamma)
+ d->gamma = value.toDouble();
+ else if (option == Quality)
+ d->quality = value.toInt();
+ else if (option == Description)
+ d->description = value.toString();
+}
+
+QByteArray QPngHandler::name() const
+{
+ return "png";
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_IMAGEFORMAT_PNG
diff --git a/src/gui/image/qpnghandler_p.h b/src/gui/image/qpnghandler_p.h
new file mode 100644
index 0000000000..543fa0f516
--- /dev/null
+++ b/src/gui/image/qpnghandler_p.h
@@ -0,0 +1,88 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QPNGHANDLER_P_H
+#define QPNGHANDLER_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of the QLibrary class. This header file may change from
+// version to version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "QtGui/qimageiohandler.h"
+
+#ifndef QT_NO_IMAGEFORMAT_PNG
+
+QT_BEGIN_NAMESPACE
+
+class QPngHandlerPrivate;
+class QPngHandler : public QImageIOHandler
+{
+public:
+ QPngHandler();
+ ~QPngHandler();
+
+ bool canRead() const;
+ bool read(QImage *image);
+ bool write(const QImage &image);
+
+ QByteArray name() const;
+
+ QVariant option(ImageOption option) const;
+ void setOption(ImageOption option, const QVariant &value);
+ bool supportsOption(ImageOption option) const;
+
+ static bool canRead(QIODevice *device);
+
+private:
+ QPngHandlerPrivate *d;
+};
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_IMAGEFORMAT_PNG
+#endif // QPNGHANDLER_P_H
diff --git a/src/gui/image/qppmhandler.cpp b/src/gui/image/qppmhandler.cpp
new file mode 100644
index 0000000000..902f764498
--- /dev/null
+++ b/src/gui/image/qppmhandler.cpp
@@ -0,0 +1,531 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "private/qppmhandler_p.h"
+
+#ifndef QT_NO_IMAGEFORMAT_PPM
+
+#include <qimage.h>
+#include <qvariant.h>
+#include <qvector.h>
+#include <ctype.h>
+
+QT_BEGIN_NAMESPACE
+
+/*****************************************************************************
+ PBM/PGM/PPM (ASCII and RAW) image read/write functions
+ *****************************************************************************/
+
+static int read_pbm_int(QIODevice *d)
+{
+ char c;
+ int val = -1;
+ bool digit;
+ const int buflen = 100;
+ char buf[buflen];
+ for (;;) {
+ if (!d->getChar(&c)) // end of file
+ break;
+ digit = isdigit((uchar) c);
+ if (val != -1) {
+ if (digit) {
+ val = 10*val + c - '0';
+ continue;
+ } else {
+ if (c == '#') // comment
+ d->readLine(buf, buflen);
+ break;
+ }
+ }
+ if (digit) // first digit
+ val = c - '0';
+ else if (isspace((uchar) c))
+ continue;
+ else if (c == '#')
+ d->readLine(buf, buflen);
+ else
+ break;
+ }
+ return val;
+}
+
+static bool read_pbm_header(QIODevice *device, char& type, int& w, int& h, int& mcc)
+{
+ char buf[3];
+ if (device->read(buf, 3) != 3) // read P[1-6]<white-space>
+ return false;
+
+ if (!(buf[0] == 'P' && isdigit((uchar) buf[1]) && isspace((uchar) buf[2])))
+ return false;
+
+ type = buf[1];
+ if (type < '1' || type > '6')
+ return false;
+
+ w = read_pbm_int(device); // get image width
+ h = read_pbm_int(device); // get image height
+
+ if (type == '1' || type == '4')
+ mcc = 1; // ignore max color component
+ else
+ mcc = read_pbm_int(device); // get max color component
+
+ if (w <= 0 || w > 32767 || h <= 0 || h > 32767 || mcc <= 0)
+ return false; // weird P.M image
+
+ return true;
+}
+
+static bool read_pbm_body(QIODevice *device, char type, int w, int h, int mcc, QImage *outImage)
+{
+ int nbits, y;
+ int pbm_bpl;
+ bool raw;
+
+ QImage::Format format;
+ switch (type) {
+ case '1': // ascii PBM
+ case '4': // raw PBM
+ nbits = 1;
+ format = QImage::Format_Mono;
+ break;
+ case '2': // ascii PGM
+ case '5': // raw PGM
+ nbits = 8;
+ format = QImage::Format_Indexed8;
+ break;
+ case '3': // ascii PPM
+ case '6': // raw PPM
+ nbits = 32;
+ format = QImage::Format_RGB32;
+ break;
+ default:
+ return false;
+ }
+ raw = type >= '4';
+
+ int maxc = mcc;
+ if (maxc > 255)
+ maxc = 255;
+ if (outImage->size() != QSize(w, h) || outImage->format() != format) {
+ *outImage = QImage(w, h, format);
+ if (outImage->isNull())
+ return false;
+ }
+
+ pbm_bpl = (nbits*w+7)/8; // bytes per scanline in PBM
+
+ if (raw) { // read raw data
+ if (nbits == 32) { // type 6
+ pbm_bpl = mcc < 256 ? 3*w : 6*w;
+ uchar *buf24 = new uchar[pbm_bpl], *b;
+ QRgb *p;
+ QRgb *end;
+ for (y=0; y<h; y++) {
+ if (device->read((char *)buf24, pbm_bpl) != pbm_bpl) {
+ delete[] buf24;
+ return false;
+ }
+ p = (QRgb *)outImage->scanLine(y);
+ end = p + w;
+ b = buf24;
+ while (p < end) {
+ if (mcc < 256) {
+ *p++ = qRgb(b[0],b[1],b[2]);
+ b += 3;
+ } else {
+ *p++ = qRgb(((int(b[0]) * 256 + int(b[1]) + 1) * 256) / (mcc + 1) - 1,
+ ((int(b[2]) * 256 + int(b[3]) + 1) * 256) / (mcc + 1) - 1,
+ ((int(b[4]) * 256 + int(b[5]) + 1) * 256) / (mcc + 1) - 1);
+ b += 6;
+ }
+ }
+ }
+ delete[] buf24;
+ } else { // type 4,5
+ for (y=0; y<h; y++) {
+ if (device->read((char *)outImage->scanLine(y), pbm_bpl)
+ != pbm_bpl)
+ return false;
+ }
+ }
+ } else { // read ascii data
+ register uchar *p;
+ int n;
+ for (y=0; y<h; y++) {
+ p = outImage->scanLine(y);
+ n = pbm_bpl;
+ if (nbits == 1) {
+ int b;
+ int bitsLeft = w;
+ while (n--) {
+ b = 0;
+ for (int i=0; i<8; i++) {
+ if (i < bitsLeft)
+ b = (b << 1) | (read_pbm_int(device) & 1);
+ else
+ b = (b << 1) | (0 & 1); // pad it our self if we need to
+ }
+ bitsLeft -= 8;
+ *p++ = b;
+ }
+ } else if (nbits == 8) {
+ if (mcc == maxc) {
+ while (n--) {
+ *p++ = read_pbm_int(device);
+ }
+ } else {
+ while (n--) {
+ *p++ = read_pbm_int(device) * maxc / mcc;
+ }
+ }
+ } else { // 32 bits
+ n /= 4;
+ int r, g, b;
+ if (mcc == maxc) {
+ while (n--) {
+ r = read_pbm_int(device);
+ g = read_pbm_int(device);
+ b = read_pbm_int(device);
+ *((QRgb*)p) = qRgb(r, g, b);
+ p += 4;
+ }
+ } else {
+ while (n--) {
+ r = read_pbm_int(device) * maxc / mcc;
+ g = read_pbm_int(device) * maxc / mcc;
+ b = read_pbm_int(device) * maxc / mcc;
+ *((QRgb*)p) = qRgb(r, g, b);
+ p += 4;
+ }
+ }
+ }
+ }
+ }
+
+ if (nbits == 1) { // bitmap
+ outImage->setNumColors(2);
+ outImage->setColor(0, qRgb(255,255,255)); // white
+ outImage->setColor(1, qRgb(0,0,0)); // black
+ } else if (nbits == 8) { // graymap
+ outImage->setNumColors(maxc+1);
+ for (int i=0; i<=maxc; i++)
+ outImage->setColor(i, qRgb(i*255/maxc,i*255/maxc,i*255/maxc));
+ }
+
+ return true;
+}
+
+static bool write_pbm_image(QIODevice *out, const QImage &sourceImage, const QByteArray &sourceFormat)
+{
+ QByteArray str;
+ QImage image = sourceImage;
+ QByteArray format = sourceFormat;
+
+ format = format.left(3); // ignore RAW part
+ bool gray = format == "pgm";
+
+ if (format == "pbm") {
+ image = image.convertToFormat(QImage::Format_MonoLSB);
+ } else if (image.depth() == 1) {
+ image = image.convertToFormat(QImage::Format_Indexed8);
+ } else {
+ switch (image.format()) {
+ case QImage::Format_RGB16:
+ case QImage::Format_RGB666:
+ case QImage::Format_RGB555:
+ case QImage::Format_RGB888:
+ case QImage::Format_RGB444:
+ image = image.convertToFormat(QImage::Format_RGB32);
+ break;
+ case QImage::Format_ARGB8565_Premultiplied:
+ case QImage::Format_ARGB6666_Premultiplied:
+ case QImage::Format_ARGB8555_Premultiplied:
+ case QImage::Format_ARGB4444_Premultiplied:
+ image = image.convertToFormat(QImage::Format_ARGB32);
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (image.depth() == 1 && image.numColors() == 2) {
+ if (qGray(image.color(0)) < qGray(image.color(1))) {
+ // 0=dark/black, 1=light/white - invert
+ image.detach();
+ for (int y=0; y<image.height(); y++) {
+ uchar *p = image.scanLine(y);
+ uchar *end = p + image.bytesPerLine();
+ while (p < end)
+ *p++ ^= 0xff;
+ }
+ }
+ }
+
+ uint w = image.width();
+ uint h = image.height();
+
+ str = "P\n";
+ str += QByteArray::number(w);
+ str += ' ';
+ str += QByteArray::number(h);
+ str += '\n';
+
+ switch (image.depth()) {
+ case 1: {
+ str.insert(1, '4');
+ if (out->write(str, str.length()) != str.length())
+ return false;
+ w = (w+7)/8;
+ for (uint y=0; y<h; y++) {
+ uchar* line = image.scanLine(y);
+ if (w != (uint)out->write((char*)line, w))
+ return false;
+ }
+ }
+ break;
+
+ case 8: {
+ str.insert(1, gray ? '5' : '6');
+ str.append("255\n");
+ if (out->write(str, str.length()) != str.length())
+ return false;
+ QVector<QRgb> color = image.colorTable();
+ uint bpl = w*(gray ? 1 : 3);
+ uchar *buf = new uchar[bpl];
+ for (uint y=0; y<h; y++) {
+ uchar *b = image.scanLine(y);
+ uchar *p = buf;
+ uchar *end = buf+bpl;
+ if (gray) {
+ while (p < end) {
+ uchar g = (uchar)qGray(color[*b++]);
+ *p++ = g;
+ }
+ } else {
+ while (p < end) {
+ QRgb rgb = color[*b++];
+ *p++ = qRed(rgb);
+ *p++ = qGreen(rgb);
+ *p++ = qBlue(rgb);
+ }
+ }
+ if (bpl != (uint)out->write((char*)buf, bpl))
+ return false;
+ }
+ delete [] buf;
+ }
+ break;
+
+ case 32: {
+ str.insert(1, gray ? '5' : '6');
+ str.append("255\n");
+ if (out->write(str, str.length()) != str.length())
+ return false;
+ uint bpl = w*(gray ? 1 : 3);
+ uchar *buf = new uchar[bpl];
+ for (uint y=0; y<h; y++) {
+ QRgb *b = (QRgb*)image.scanLine(y);
+ uchar *p = buf;
+ uchar *end = buf+bpl;
+ if (gray) {
+ while (p < end) {
+ uchar g = (uchar)qGray(*b++);
+ *p++ = g;
+ }
+ } else {
+ while (p < end) {
+ QRgb rgb = *b++;
+ *p++ = qRed(rgb);
+ *p++ = qGreen(rgb);
+ *p++ = qBlue(rgb);
+ }
+ }
+ if (bpl != (uint)out->write((char*)buf, bpl))
+ return false;
+ }
+ delete [] buf;
+ }
+ break;
+
+ default:
+ return false;
+ }
+
+ return true;
+}
+
+QPpmHandler::QPpmHandler()
+ : state(Ready)
+{
+}
+
+bool QPpmHandler::readHeader()
+{
+ state = Error;
+ if (!read_pbm_header(device(), type, width, height, mcc))
+ return false;
+ state = ReadHeader;
+ return true;
+}
+
+bool QPpmHandler::canRead() const
+{
+ if (state == Ready) {
+ if (!canRead(device(), &subType))
+ return false;
+ setFormat(subType);
+ return true;
+ }
+ return state != Error;
+}
+
+bool QPpmHandler::canRead(QIODevice *device, QByteArray *subType)
+{
+ if (!device) {
+ qWarning("QPpmHandler::canRead() called with no device");
+ return false;
+ }
+
+ char head[2];
+ if (device->peek(head, sizeof(head)) != sizeof(head))
+ return false;
+
+ if (head[0] != 'P')
+ return false;
+
+ if (head[1] == '1' || head[1] == '4') {
+ if (subType)
+ *subType = "pbm";
+ } else if (head[1] == '2' || head[1] == '5') {
+ if (subType)
+ *subType = "pgm";
+ } else if (head[1] == '3' || head[1] == '6') {
+ if (subType)
+ *subType = "ppm";
+ } else {
+ return false;
+ }
+ return true;
+}
+
+bool QPpmHandler::read(QImage *image)
+{
+ if (state == Error)
+ return false;
+
+ if (state == Ready && !readHeader()) {
+ state = Error;
+ return false;
+ }
+
+ if (!read_pbm_body(device(), type, width, height, mcc, image)) {
+ state = Error;
+ return false;
+ }
+
+ state = Ready;
+ return true;
+}
+
+bool QPpmHandler::write(const QImage &image)
+{
+ return write_pbm_image(device(), image, subType);
+}
+
+bool QPpmHandler::supportsOption(ImageOption option) const
+{
+ return option == SubType
+ || option == Size
+ || option == ImageFormat;
+}
+
+QVariant QPpmHandler::option(ImageOption option) const
+{
+ if (option == SubType) {
+ return subType;
+ } else if (option == Size) {
+ if (state == Error)
+ return QVariant();
+ if (state == Ready && !const_cast<QPpmHandler*>(this)->readHeader())
+ return QVariant();
+ return QSize(width, height);
+ } else if (option == ImageFormat) {
+ if (state == Error)
+ return QVariant();
+ if (state == Ready && !const_cast<QPpmHandler*>(this)->readHeader())
+ return QVariant();
+ QImage::Format format = QImage::Format_Invalid;
+ switch (type) {
+ case '1': // ascii PBM
+ case '4': // raw PBM
+ format = QImage::Format_Mono;
+ break;
+ case '2': // ascii PGM
+ case '5': // raw PGM
+ format = QImage::Format_Indexed8;
+ break;
+ case '3': // ascii PPM
+ case '6': // raw PPM
+ format = QImage::Format_RGB32;
+ break;
+ default:
+ break;
+ }
+ return format;
+ }
+ return QVariant();
+}
+
+void QPpmHandler::setOption(ImageOption option, const QVariant &value)
+{
+ if (option == SubType)
+ subType = value.toByteArray().toLower();
+}
+
+QByteArray QPpmHandler::name() const
+{
+ return subType.isEmpty() ? QByteArray("ppm") : subType;
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_IMAGEFORMAT_PPM
diff --git a/src/gui/image/qppmhandler_p.h b/src/gui/image/qppmhandler_p.h
new file mode 100644
index 0000000000..c40c51d948
--- /dev/null
+++ b/src/gui/image/qppmhandler_p.h
@@ -0,0 +1,98 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QPPMHANDLER_P_H
+#define QPPMHANDLER_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of the QLibrary class. This header file may change from
+// version to version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "QtGui/qimageiohandler.h"
+
+#ifndef QT_NO_IMAGEFORMAT_PPM
+
+QT_BEGIN_NAMESPACE
+
+class QByteArray;
+class QPpmHandler : public QImageIOHandler
+{
+public:
+ QPpmHandler();
+ bool canRead() const;
+ bool read(QImage *image);
+ bool write(const QImage &image);
+
+ QByteArray name() const;
+
+ static bool canRead(QIODevice *device, QByteArray *subType = 0);
+
+ QVariant option(ImageOption option) const;
+ void setOption(ImageOption option, const QVariant &value);
+ bool supportsOption(ImageOption option) const;
+
+private:
+ bool readHeader();
+ enum State {
+ Ready,
+ ReadHeader,
+ Error
+ };
+ State state;
+ char type;
+ int width;
+ int height;
+ int mcc;
+ mutable QByteArray subType;
+};
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_IMAGEFORMAT_PPM
+
+#endif // QPPMHANDLER_P_H
diff --git a/src/gui/image/qxbmhandler.cpp b/src/gui/image/qxbmhandler.cpp
new file mode 100644
index 0000000000..351bc25971
--- /dev/null
+++ b/src/gui/image/qxbmhandler.cpp
@@ -0,0 +1,350 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <qplatformdefs.h>
+#include "private/qxbmhandler_p.h"
+
+#ifndef QT_NO_IMAGEFORMAT_XBM
+
+#include <qimage.h>
+#include <qiodevice.h>
+#include <qvariant.h>
+
+#include <stdio.h>
+#include <ctype.h>
+
+QT_BEGIN_NAMESPACE
+
+/*****************************************************************************
+ X bitmap image read/write functions
+ *****************************************************************************/
+
+static inline int hex2byte(register char *p)
+{
+ return ((isdigit((uchar) *p) ? *p - '0' : toupper((uchar) *p) - 'A' + 10) << 4) |
+ (isdigit((uchar) *(p+1)) ? *(p+1) - '0' : toupper((uchar) *(p+1)) - 'A' + 10);
+}
+
+static bool read_xbm_header(QIODevice *device, int& w, int& h)
+{
+ const int buflen = 300;
+ char buf[buflen + 1];
+ QRegExp r1(QLatin1String("^#define[ \t]+[a-zA-Z0-9._]+[ \t]+"));
+ QRegExp r2(QLatin1String("[0-9]+"));
+
+ qint64 readBytes = 0;
+
+ // "#define .._width <num>"
+ readBytes = device->readLine(buf, buflen);
+ if (readBytes <= 0)
+ return false;
+ buf[readBytes - 1] = '\0';
+
+ // skip initial comment, if any
+ while (buf[0] != '#' && (readBytes = device->readLine( buf, buflen )) > 0) {}
+
+ if (readBytes <= 0)
+ return false;
+ buf[readBytes - 1] = '\0';
+ QString sbuf;
+ sbuf = QString::fromLatin1(buf);
+
+ if (r1.indexIn(sbuf) == 0 &&
+ r2.indexIn(sbuf, r1.matchedLength()) == r1.matchedLength())
+ w = QByteArray(&buf[r1.matchedLength()]).trimmed().toInt();
+
+ // "#define .._height <num>"
+ readBytes = device->readLine(buf, buflen);
+ if (readBytes <= 0)
+ return false;
+ buf[readBytes - 1] = '\0';
+
+ sbuf = QString::fromLatin1(buf);
+
+ if (r1.indexIn(sbuf) == 0 &&
+ r2.indexIn(sbuf, r1.matchedLength()) == r1.matchedLength())
+ h = QByteArray(&buf[r1.matchedLength()]).trimmed().toInt();
+
+ // format error
+ if (w <= 0 || w > 32767 || h <= 0 || h > 32767)
+ return false;
+
+ return true;
+}
+
+static bool read_xbm_body(QIODevice *device, int w, int h, QImage *outImage)
+{
+ const int buflen = 300;
+ char buf[buflen + 1];
+
+ qint64 readBytes = 0;
+
+ // scan for database
+ for (;;) {
+ if ((readBytes = device->readLine(buf, buflen)) <= 0) {
+ // end of file
+ return false;
+ }
+
+ buf[readBytes] = '\0';
+ if (QByteArray::fromRawData(buf, readBytes).contains("0x"))
+ break;
+ }
+
+ if (outImage->size() != QSize(w, h) || outImage->format() != QImage::Format_MonoLSB) {
+ *outImage = QImage(w, h, QImage::Format_MonoLSB);
+ if (outImage->isNull())
+ return false;
+ }
+
+ outImage->setNumColors(2);
+ outImage->setColor(0, qRgb(255,255,255)); // white
+ outImage->setColor(1, qRgb(0,0,0)); // black
+
+ int x = 0, y = 0;
+ uchar *b = outImage->scanLine(0);
+ char *p = buf + QByteArray::fromRawData(buf, readBytes).indexOf("0x");
+ w = (w+7)/8; // byte width
+
+ while (y < h) { // for all encoded bytes...
+ if (p) { // p = "0x.."
+ *b++ = hex2byte(p+2);
+ p += 2;
+ if (++x == w && ++y < h) {
+ b = outImage->scanLine(y);
+ x = 0;
+ }
+ p = strstr(p, "0x");
+ } else { // read another line
+ if ((readBytes = device->readLine(buf,buflen)) <= 0) // EOF ==> truncated image
+ break;
+ p = buf + QByteArray::fromRawData(buf, readBytes).indexOf("0x");
+ }
+ }
+
+ return true;
+}
+
+static bool read_xbm_image(QIODevice *device, QImage *outImage)
+{
+ int w = 0, h = 0;
+ if (!read_xbm_header(device, w, h))
+ return false;
+ return read_xbm_body(device, w, h, outImage);
+}
+
+static bool write_xbm_image(const QImage &sourceImage, QIODevice *device, const QString &fileName)
+{
+ QImage image = sourceImage;
+ int w = image.width();
+ int h = image.height();
+ int i;
+ QString s = fileName; // get file base name
+ int msize = s.length() + 100;
+ char *buf = new char[msize];
+
+ qsnprintf(buf, msize, "#define %s_width %d\n", s.toAscii().data(), w);
+ device->write(buf, qstrlen(buf));
+ qsnprintf(buf, msize, "#define %s_height %d\n", s.toAscii().data(), h);
+ device->write(buf, qstrlen(buf));
+ qsnprintf(buf, msize, "static char %s_bits[] = {\n ", s.toAscii().data());
+ device->write(buf, qstrlen(buf));
+
+ if (image.format() != QImage::Format_MonoLSB)
+ image = image.convertToFormat(QImage::Format_MonoLSB);
+
+ bool invert = qGray(image.color(0)) < qGray(image.color(1));
+ char hexrep[16];
+ for (i=0; i<10; i++)
+ hexrep[i] = '0' + i;
+ for (i=10; i<16; i++)
+ hexrep[i] = 'a' -10 + i;
+ if (invert) {
+ char t;
+ for (i=0; i<8; i++) {
+ t = hexrep[15-i];
+ hexrep[15-i] = hexrep[i];
+ hexrep[i] = t;
+ }
+ }
+ int bcnt = 0;
+ register char *p = buf;
+ int bpl = (w+7)/8;
+ for (int y = 0; y < h; ++y) {
+ uchar *b = image.scanLine(y);
+ for (i = 0; i < bpl; ++i) {
+ *p++ = '0'; *p++ = 'x';
+ *p++ = hexrep[*b >> 4];
+ *p++ = hexrep[*b++ & 0xf];
+
+ if (i < bpl - 1 || y < h - 1) {
+ *p++ = ',';
+ if (++bcnt > 14) {
+ *p++ = '\n';
+ *p++ = ' ';
+ *p = '\0';
+ if ((int)qstrlen(buf) != device->write(buf, qstrlen(buf))) {
+ delete [] buf;
+ return false;
+ }
+ p = buf;
+ bcnt = 0;
+ }
+ }
+ }
+ }
+#if defined(_MSC_VER) && _MSC_VER >= 1400
+ strcpy_s(p, sizeof(" };\n"), " };\n");
+#else
+ strcpy(p, " };\n");
+#endif
+ if ((int)qstrlen(buf) != device->write(buf, qstrlen(buf))) {
+ delete [] buf;
+ return false;
+ }
+
+ delete [] buf;
+ return true;
+}
+
+QXbmHandler::QXbmHandler()
+ : state(Ready)
+{
+}
+
+bool QXbmHandler::readHeader()
+{
+ state = Error;
+ if (!read_xbm_header(device(), width, height))
+ return false;
+ state = ReadHeader;
+ return true;
+}
+
+bool QXbmHandler::canRead() const
+{
+ if (state == Ready) {
+ if (!canRead(device()))
+ return false;
+ setFormat("xbm");
+ return true;
+ }
+ return state != Error;
+}
+
+bool QXbmHandler::canRead(QIODevice *device)
+{
+ QImage image;
+
+ // it's impossible to tell whether we can load an XBM or not when
+ // it's from a sequential device, as the only way to do it is to
+ // attempt to parse the whole image.
+ if (device->isSequential())
+ return false;
+
+ qint64 oldPos = device->pos();
+ bool success = read_xbm_image(device, &image);
+ device->seek(oldPos);
+
+ return success;
+}
+
+bool QXbmHandler::read(QImage *image)
+{
+ if (state == Error)
+ return false;
+
+ if (state == Ready && !readHeader()) {
+ state = Error;
+ return false;
+ }
+
+ if (!read_xbm_body(device(), width, height, image)) {
+ state = Error;
+ return false;
+ }
+
+ state = Ready;
+ return true;
+}
+
+bool QXbmHandler::write(const QImage &image)
+{
+ return write_xbm_image(image, device(), fileName);
+}
+
+bool QXbmHandler::supportsOption(ImageOption option) const
+{
+ return option == Name
+ || option == Size
+ || option == ImageFormat;
+}
+
+QVariant QXbmHandler::option(ImageOption option) const
+{
+ if (option == Name) {
+ return fileName;
+ } else if (option == Size) {
+ if (state == Error)
+ return QVariant();
+ if (state == Ready && !const_cast<QXbmHandler*>(this)->readHeader())
+ return QVariant();
+ return QSize(width, height);
+ } else if (option == ImageFormat) {
+ return QImage::Format_MonoLSB;
+ }
+ return QVariant();
+}
+
+void QXbmHandler::setOption(ImageOption option, const QVariant &value)
+{
+ if (option == Name)
+ fileName = value.toString();
+}
+
+QByteArray QXbmHandler::name() const
+{
+ return "xbm";
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_IMAGEFORMAT_XBM
diff --git a/src/gui/image/qxbmhandler_p.h b/src/gui/image/qxbmhandler_p.h
new file mode 100644
index 0000000000..ffbbfe4a40
--- /dev/null
+++ b/src/gui/image/qxbmhandler_p.h
@@ -0,0 +1,95 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QXBMHANDLER_P_H
+#define QXBMHANDLER_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of the QLibrary class. This header file may change from
+// version to version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "QtGui/qimageiohandler.h"
+
+#ifndef QT_NO_IMAGEFORMAT_XBM
+
+QT_BEGIN_NAMESPACE
+
+class QXbmHandler : public QImageIOHandler
+{
+public:
+ QXbmHandler();
+ bool canRead() const;
+ bool read(QImage *image);
+ bool write(const QImage &image);
+
+ QByteArray name() const;
+
+ static bool canRead(QIODevice *device);
+
+ QVariant option(ImageOption option) const;
+ void setOption(ImageOption option, const QVariant &value);
+ bool supportsOption(ImageOption option) const;
+
+private:
+ bool readHeader();
+ enum State {
+ Ready,
+ ReadHeader,
+ Error
+ };
+ State state;
+ int width;
+ int height;
+ QString fileName;
+};
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_IMAGEFORMAT_XBM
+
+#endif // QXBMHANDLER_P_H
diff --git a/src/gui/image/qxpmhandler.cpp b/src/gui/image/qxpmhandler.cpp
new file mode 100644
index 0000000000..90bd2abd99
--- /dev/null
+++ b/src/gui/image/qxpmhandler.cpp
@@ -0,0 +1,1309 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "private/qxpmhandler_p.h"
+
+#ifndef QT_NO_IMAGEFORMAT_XPM
+
+#include <private/qcolor_p.h>
+#include <qimage.h>
+#include <qmap.h>
+#include <qtextstream.h>
+#include <qvariant.h>
+
+#if defined(Q_CC_BOR)
+// needed for qsort() because of a std namespace problem on Borland
+#include "qplatformdefs.h"
+#endif
+
+#include <stdlib.h>
+
+#if defined(Q_OS_WINCE)
+#include "qguifunctions_wince.h"
+#endif
+
+QT_BEGIN_NAMESPACE
+
+static quint64 xpmHash(const QString &str)
+{
+ unsigned int hashValue = 0;
+ for (int i = 0; i < str.size(); ++i) {
+ hashValue <<= 8;
+ hashValue += (unsigned int)str.at(i).unicode();
+ }
+ return hashValue;
+}
+static quint64 xpmHash(char *str)
+{
+ unsigned int hashValue = 0;
+ while (*str != '\0') {
+ hashValue <<= 8;
+ hashValue += (unsigned int)*str;
+ ++str;
+ }
+ return hashValue;
+}
+
+#ifdef QRGB
+#undef QRGB
+#endif
+#define QRGB(r,g,b) (r*65536 + g*256 + b)
+
+static const int xpmRgbTblSize = 657;
+
+static const struct XPMRGBData {
+ uint value;
+ const char *name;
+} xpmRgbTbl[] = {
+ { QRGB(240,248,255), "aliceblue" },
+ { QRGB(250,235,215), "antiquewhite" },
+ { QRGB(255,239,219), "antiquewhite1" },
+ { QRGB(238,223,204), "antiquewhite2" },
+ { QRGB(205,192,176), "antiquewhite3" },
+ { QRGB(139,131,120), "antiquewhite4" },
+ { QRGB(127,255,212), "aquamarine" },
+ { QRGB(127,255,212), "aquamarine1" },
+ { QRGB(118,238,198), "aquamarine2" },
+ { QRGB(102,205,170), "aquamarine3" },
+ { QRGB( 69,139,116), "aquamarine4" },
+ { QRGB(240,255,255), "azure" },
+ { QRGB(240,255,255), "azure1" },
+ { QRGB(224,238,238), "azure2" },
+ { QRGB(193,205,205), "azure3" },
+ { QRGB(131,139,139), "azure4" },
+ { QRGB(245,245,220), "beige" },
+ { QRGB(255,228,196), "bisque" },
+ { QRGB(255,228,196), "bisque1" },
+ { QRGB(238,213,183), "bisque2" },
+ { QRGB(205,183,158), "bisque3" },
+ { QRGB(139,125,107), "bisque4" },
+ { QRGB( 0, 0, 0), "black" },
+ { QRGB(255,235,205), "blanchedalmond" },
+ { QRGB( 0, 0,255), "blue" },
+ { QRGB( 0, 0,255), "blue1" },
+ { QRGB( 0, 0,238), "blue2" },
+ { QRGB( 0, 0,205), "blue3" },
+ { QRGB( 0, 0,139), "blue4" },
+ { QRGB(138, 43,226), "blueviolet" },
+ { QRGB(165, 42, 42), "brown" },
+ { QRGB(255, 64, 64), "brown1" },
+ { QRGB(238, 59, 59), "brown2" },
+ { QRGB(205, 51, 51), "brown3" },
+ { QRGB(139, 35, 35), "brown4" },
+ { QRGB(222,184,135), "burlywood" },
+ { QRGB(255,211,155), "burlywood1" },
+ { QRGB(238,197,145), "burlywood2" },
+ { QRGB(205,170,125), "burlywood3" },
+ { QRGB(139,115, 85), "burlywood4" },
+ { QRGB( 95,158,160), "cadetblue" },
+ { QRGB(152,245,255), "cadetblue1" },
+ { QRGB(142,229,238), "cadetblue2" },
+ { QRGB(122,197,205), "cadetblue3" },
+ { QRGB( 83,134,139), "cadetblue4" },
+ { QRGB(127,255, 0), "chartreuse" },
+ { QRGB(127,255, 0), "chartreuse1" },
+ { QRGB(118,238, 0), "chartreuse2" },
+ { QRGB(102,205, 0), "chartreuse3" },
+ { QRGB( 69,139, 0), "chartreuse4" },
+ { QRGB(210,105, 30), "chocolate" },
+ { QRGB(255,127, 36), "chocolate1" },
+ { QRGB(238,118, 33), "chocolate2" },
+ { QRGB(205,102, 29), "chocolate3" },
+ { QRGB(139, 69, 19), "chocolate4" },
+ { QRGB(255,127, 80), "coral" },
+ { QRGB(255,114, 86), "coral1" },
+ { QRGB(238,106, 80), "coral2" },
+ { QRGB(205, 91, 69), "coral3" },
+ { QRGB(139, 62, 47), "coral4" },
+ { QRGB(100,149,237), "cornflowerblue" },
+ { QRGB(255,248,220), "cornsilk" },
+ { QRGB(255,248,220), "cornsilk1" },
+ { QRGB(238,232,205), "cornsilk2" },
+ { QRGB(205,200,177), "cornsilk3" },
+ { QRGB(139,136,120), "cornsilk4" },
+ { QRGB( 0,255,255), "cyan" },
+ { QRGB( 0,255,255), "cyan1" },
+ { QRGB( 0,238,238), "cyan2" },
+ { QRGB( 0,205,205), "cyan3" },
+ { QRGB( 0,139,139), "cyan4" },
+ { QRGB( 0, 0,139), "darkblue" },
+ { QRGB( 0,139,139), "darkcyan" },
+ { QRGB(184,134, 11), "darkgoldenrod" },
+ { QRGB(255,185, 15), "darkgoldenrod1" },
+ { QRGB(238,173, 14), "darkgoldenrod2" },
+ { QRGB(205,149, 12), "darkgoldenrod3" },
+ { QRGB(139,101, 8), "darkgoldenrod4" },
+ { QRGB(169,169,169), "darkgray" },
+ { QRGB( 0,100, 0), "darkgreen" },
+ { QRGB(169,169,169), "darkgrey" },
+ { QRGB(189,183,107), "darkkhaki" },
+ { QRGB(139, 0,139), "darkmagenta" },
+ { QRGB( 85,107, 47), "darkolivegreen" },
+ { QRGB(202,255,112), "darkolivegreen1" },
+ { QRGB(188,238,104), "darkolivegreen2" },
+ { QRGB(162,205, 90), "darkolivegreen3" },
+ { QRGB(110,139, 61), "darkolivegreen4" },
+ { QRGB(255,140, 0), "darkorange" },
+ { QRGB(255,127, 0), "darkorange1" },
+ { QRGB(238,118, 0), "darkorange2" },
+ { QRGB(205,102, 0), "darkorange3" },
+ { QRGB(139, 69, 0), "darkorange4" },
+ { QRGB(153, 50,204), "darkorchid" },
+ { QRGB(191, 62,255), "darkorchid1" },
+ { QRGB(178, 58,238), "darkorchid2" },
+ { QRGB(154, 50,205), "darkorchid3" },
+ { QRGB(104, 34,139), "darkorchid4" },
+ { QRGB(139, 0, 0), "darkred" },
+ { QRGB(233,150,122), "darksalmon" },
+ { QRGB(143,188,143), "darkseagreen" },
+ { QRGB(193,255,193), "darkseagreen1" },
+ { QRGB(180,238,180), "darkseagreen2" },
+ { QRGB(155,205,155), "darkseagreen3" },
+ { QRGB(105,139,105), "darkseagreen4" },
+ { QRGB( 72, 61,139), "darkslateblue" },
+ { QRGB( 47, 79, 79), "darkslategray" },
+ { QRGB(151,255,255), "darkslategray1" },
+ { QRGB(141,238,238), "darkslategray2" },
+ { QRGB(121,205,205), "darkslategray3" },
+ { QRGB( 82,139,139), "darkslategray4" },
+ { QRGB( 47, 79, 79), "darkslategrey" },
+ { QRGB( 0,206,209), "darkturquoise" },
+ { QRGB(148, 0,211), "darkviolet" },
+ { QRGB(255, 20,147), "deeppink" },
+ { QRGB(255, 20,147), "deeppink1" },
+ { QRGB(238, 18,137), "deeppink2" },
+ { QRGB(205, 16,118), "deeppink3" },
+ { QRGB(139, 10, 80), "deeppink4" },
+ { QRGB( 0,191,255), "deepskyblue" },
+ { QRGB( 0,191,255), "deepskyblue1" },
+ { QRGB( 0,178,238), "deepskyblue2" },
+ { QRGB( 0,154,205), "deepskyblue3" },
+ { QRGB( 0,104,139), "deepskyblue4" },
+ { QRGB(105,105,105), "dimgray" },
+ { QRGB(105,105,105), "dimgrey" },
+ { QRGB( 30,144,255), "dodgerblue" },
+ { QRGB( 30,144,255), "dodgerblue1" },
+ { QRGB( 28,134,238), "dodgerblue2" },
+ { QRGB( 24,116,205), "dodgerblue3" },
+ { QRGB( 16, 78,139), "dodgerblue4" },
+ { QRGB(178, 34, 34), "firebrick" },
+ { QRGB(255, 48, 48), "firebrick1" },
+ { QRGB(238, 44, 44), "firebrick2" },
+ { QRGB(205, 38, 38), "firebrick3" },
+ { QRGB(139, 26, 26), "firebrick4" },
+ { QRGB(255,250,240), "floralwhite" },
+ { QRGB( 34,139, 34), "forestgreen" },
+ { QRGB(220,220,220), "gainsboro" },
+ { QRGB(248,248,255), "ghostwhite" },
+ { QRGB(255,215, 0), "gold" },
+ { QRGB(255,215, 0), "gold1" },
+ { QRGB(238,201, 0), "gold2" },
+ { QRGB(205,173, 0), "gold3" },
+ { QRGB(139,117, 0), "gold4" },
+ { QRGB(218,165, 32), "goldenrod" },
+ { QRGB(255,193, 37), "goldenrod1" },
+ { QRGB(238,180, 34), "goldenrod2" },
+ { QRGB(205,155, 29), "goldenrod3" },
+ { QRGB(139,105, 20), "goldenrod4" },
+ { QRGB(190,190,190), "gray" },
+ { QRGB( 0, 0, 0), "gray0" },
+ { QRGB( 3, 3, 3), "gray1" },
+ { QRGB( 26, 26, 26), "gray10" },
+ { QRGB(255,255,255), "gray100" },
+ { QRGB( 28, 28, 28), "gray11" },
+ { QRGB( 31, 31, 31), "gray12" },
+ { QRGB( 33, 33, 33), "gray13" },
+ { QRGB( 36, 36, 36), "gray14" },
+ { QRGB( 38, 38, 38), "gray15" },
+ { QRGB( 41, 41, 41), "gray16" },
+ { QRGB( 43, 43, 43), "gray17" },
+ { QRGB( 46, 46, 46), "gray18" },
+ { QRGB( 48, 48, 48), "gray19" },
+ { QRGB( 5, 5, 5), "gray2" },
+ { QRGB( 51, 51, 51), "gray20" },
+ { QRGB( 54, 54, 54), "gray21" },
+ { QRGB( 56, 56, 56), "gray22" },
+ { QRGB( 59, 59, 59), "gray23" },
+ { QRGB( 61, 61, 61), "gray24" },
+ { QRGB( 64, 64, 64), "gray25" },
+ { QRGB( 66, 66, 66), "gray26" },
+ { QRGB( 69, 69, 69), "gray27" },
+ { QRGB( 71, 71, 71), "gray28" },
+ { QRGB( 74, 74, 74), "gray29" },
+ { QRGB( 8, 8, 8), "gray3" },
+ { QRGB( 77, 77, 77), "gray30" },
+ { QRGB( 79, 79, 79), "gray31" },
+ { QRGB( 82, 82, 82), "gray32" },
+ { QRGB( 84, 84, 84), "gray33" },
+ { QRGB( 87, 87, 87), "gray34" },
+ { QRGB( 89, 89, 89), "gray35" },
+ { QRGB( 92, 92, 92), "gray36" },
+ { QRGB( 94, 94, 94), "gray37" },
+ { QRGB( 97, 97, 97), "gray38" },
+ { QRGB( 99, 99, 99), "gray39" },
+ { QRGB( 10, 10, 10), "gray4" },
+ { QRGB(102,102,102), "gray40" },
+ { QRGB(105,105,105), "gray41" },
+ { QRGB(107,107,107), "gray42" },
+ { QRGB(110,110,110), "gray43" },
+ { QRGB(112,112,112), "gray44" },
+ { QRGB(115,115,115), "gray45" },
+ { QRGB(117,117,117), "gray46" },
+ { QRGB(120,120,120), "gray47" },
+ { QRGB(122,122,122), "gray48" },
+ { QRGB(125,125,125), "gray49" },
+ { QRGB( 13, 13, 13), "gray5" },
+ { QRGB(127,127,127), "gray50" },
+ { QRGB(130,130,130), "gray51" },
+ { QRGB(133,133,133), "gray52" },
+ { QRGB(135,135,135), "gray53" },
+ { QRGB(138,138,138), "gray54" },
+ { QRGB(140,140,140), "gray55" },
+ { QRGB(143,143,143), "gray56" },
+ { QRGB(145,145,145), "gray57" },
+ { QRGB(148,148,148), "gray58" },
+ { QRGB(150,150,150), "gray59" },
+ { QRGB( 15, 15, 15), "gray6" },
+ { QRGB(153,153,153), "gray60" },
+ { QRGB(156,156,156), "gray61" },
+ { QRGB(158,158,158), "gray62" },
+ { QRGB(161,161,161), "gray63" },
+ { QRGB(163,163,163), "gray64" },
+ { QRGB(166,166,166), "gray65" },
+ { QRGB(168,168,168), "gray66" },
+ { QRGB(171,171,171), "gray67" },
+ { QRGB(173,173,173), "gray68" },
+ { QRGB(176,176,176), "gray69" },
+ { QRGB( 18, 18, 18), "gray7" },
+ { QRGB(179,179,179), "gray70" },
+ { QRGB(181,181,181), "gray71" },
+ { QRGB(184,184,184), "gray72" },
+ { QRGB(186,186,186), "gray73" },
+ { QRGB(189,189,189), "gray74" },
+ { QRGB(191,191,191), "gray75" },
+ { QRGB(194,194,194), "gray76" },
+ { QRGB(196,196,196), "gray77" },
+ { QRGB(199,199,199), "gray78" },
+ { QRGB(201,201,201), "gray79" },
+ { QRGB( 20, 20, 20), "gray8" },
+ { QRGB(204,204,204), "gray80" },
+ { QRGB(207,207,207), "gray81" },
+ { QRGB(209,209,209), "gray82" },
+ { QRGB(212,212,212), "gray83" },
+ { QRGB(214,214,214), "gray84" },
+ { QRGB(217,217,217), "gray85" },
+ { QRGB(219,219,219), "gray86" },
+ { QRGB(222,222,222), "gray87" },
+ { QRGB(224,224,224), "gray88" },
+ { QRGB(227,227,227), "gray89" },
+ { QRGB( 23, 23, 23), "gray9" },
+ { QRGB(229,229,229), "gray90" },
+ { QRGB(232,232,232), "gray91" },
+ { QRGB(235,235,235), "gray92" },
+ { QRGB(237,237,237), "gray93" },
+ { QRGB(240,240,240), "gray94" },
+ { QRGB(242,242,242), "gray95" },
+ { QRGB(245,245,245), "gray96" },
+ { QRGB(247,247,247), "gray97" },
+ { QRGB(250,250,250), "gray98" },
+ { QRGB(252,252,252), "gray99" },
+ { QRGB( 0,255, 0), "green" },
+ { QRGB( 0,255, 0), "green1" },
+ { QRGB( 0,238, 0), "green2" },
+ { QRGB( 0,205, 0), "green3" },
+ { QRGB( 0,139, 0), "green4" },
+ { QRGB(173,255, 47), "greenyellow" },
+ { QRGB(190,190,190), "grey" },
+ { QRGB( 0, 0, 0), "grey0" },
+ { QRGB( 3, 3, 3), "grey1" },
+ { QRGB( 26, 26, 26), "grey10" },
+ { QRGB(255,255,255), "grey100" },
+ { QRGB( 28, 28, 28), "grey11" },
+ { QRGB( 31, 31, 31), "grey12" },
+ { QRGB( 33, 33, 33), "grey13" },
+ { QRGB( 36, 36, 36), "grey14" },
+ { QRGB( 38, 38, 38), "grey15" },
+ { QRGB( 41, 41, 41), "grey16" },
+ { QRGB( 43, 43, 43), "grey17" },
+ { QRGB( 46, 46, 46), "grey18" },
+ { QRGB( 48, 48, 48), "grey19" },
+ { QRGB( 5, 5, 5), "grey2" },
+ { QRGB( 51, 51, 51), "grey20" },
+ { QRGB( 54, 54, 54), "grey21" },
+ { QRGB( 56, 56, 56), "grey22" },
+ { QRGB( 59, 59, 59), "grey23" },
+ { QRGB( 61, 61, 61), "grey24" },
+ { QRGB( 64, 64, 64), "grey25" },
+ { QRGB( 66, 66, 66), "grey26" },
+ { QRGB( 69, 69, 69), "grey27" },
+ { QRGB( 71, 71, 71), "grey28" },
+ { QRGB( 74, 74, 74), "grey29" },
+ { QRGB( 8, 8, 8), "grey3" },
+ { QRGB( 77, 77, 77), "grey30" },
+ { QRGB( 79, 79, 79), "grey31" },
+ { QRGB( 82, 82, 82), "grey32" },
+ { QRGB( 84, 84, 84), "grey33" },
+ { QRGB( 87, 87, 87), "grey34" },
+ { QRGB( 89, 89, 89), "grey35" },
+ { QRGB( 92, 92, 92), "grey36" },
+ { QRGB( 94, 94, 94), "grey37" },
+ { QRGB( 97, 97, 97), "grey38" },
+ { QRGB( 99, 99, 99), "grey39" },
+ { QRGB( 10, 10, 10), "grey4" },
+ { QRGB(102,102,102), "grey40" },
+ { QRGB(105,105,105), "grey41" },
+ { QRGB(107,107,107), "grey42" },
+ { QRGB(110,110,110), "grey43" },
+ { QRGB(112,112,112), "grey44" },
+ { QRGB(115,115,115), "grey45" },
+ { QRGB(117,117,117), "grey46" },
+ { QRGB(120,120,120), "grey47" },
+ { QRGB(122,122,122), "grey48" },
+ { QRGB(125,125,125), "grey49" },
+ { QRGB( 13, 13, 13), "grey5" },
+ { QRGB(127,127,127), "grey50" },
+ { QRGB(130,130,130), "grey51" },
+ { QRGB(133,133,133), "grey52" },
+ { QRGB(135,135,135), "grey53" },
+ { QRGB(138,138,138), "grey54" },
+ { QRGB(140,140,140), "grey55" },
+ { QRGB(143,143,143), "grey56" },
+ { QRGB(145,145,145), "grey57" },
+ { QRGB(148,148,148), "grey58" },
+ { QRGB(150,150,150), "grey59" },
+ { QRGB( 15, 15, 15), "grey6" },
+ { QRGB(153,153,153), "grey60" },
+ { QRGB(156,156,156), "grey61" },
+ { QRGB(158,158,158), "grey62" },
+ { QRGB(161,161,161), "grey63" },
+ { QRGB(163,163,163), "grey64" },
+ { QRGB(166,166,166), "grey65" },
+ { QRGB(168,168,168), "grey66" },
+ { QRGB(171,171,171), "grey67" },
+ { QRGB(173,173,173), "grey68" },
+ { QRGB(176,176,176), "grey69" },
+ { QRGB( 18, 18, 18), "grey7" },
+ { QRGB(179,179,179), "grey70" },
+ { QRGB(181,181,181), "grey71" },
+ { QRGB(184,184,184), "grey72" },
+ { QRGB(186,186,186), "grey73" },
+ { QRGB(189,189,189), "grey74" },
+ { QRGB(191,191,191), "grey75" },
+ { QRGB(194,194,194), "grey76" },
+ { QRGB(196,196,196), "grey77" },
+ { QRGB(199,199,199), "grey78" },
+ { QRGB(201,201,201), "grey79" },
+ { QRGB( 20, 20, 20), "grey8" },
+ { QRGB(204,204,204), "grey80" },
+ { QRGB(207,207,207), "grey81" },
+ { QRGB(209,209,209), "grey82" },
+ { QRGB(212,212,212), "grey83" },
+ { QRGB(214,214,214), "grey84" },
+ { QRGB(217,217,217), "grey85" },
+ { QRGB(219,219,219), "grey86" },
+ { QRGB(222,222,222), "grey87" },
+ { QRGB(224,224,224), "grey88" },
+ { QRGB(227,227,227), "grey89" },
+ { QRGB( 23, 23, 23), "grey9" },
+ { QRGB(229,229,229), "grey90" },
+ { QRGB(232,232,232), "grey91" },
+ { QRGB(235,235,235), "grey92" },
+ { QRGB(237,237,237), "grey93" },
+ { QRGB(240,240,240), "grey94" },
+ { QRGB(242,242,242), "grey95" },
+ { QRGB(245,245,245), "grey96" },
+ { QRGB(247,247,247), "grey97" },
+ { QRGB(250,250,250), "grey98" },
+ { QRGB(252,252,252), "grey99" },
+ { QRGB(240,255,240), "honeydew" },
+ { QRGB(240,255,240), "honeydew1" },
+ { QRGB(224,238,224), "honeydew2" },
+ { QRGB(193,205,193), "honeydew3" },
+ { QRGB(131,139,131), "honeydew4" },
+ { QRGB(255,105,180), "hotpink" },
+ { QRGB(255,110,180), "hotpink1" },
+ { QRGB(238,106,167), "hotpink2" },
+ { QRGB(205, 96,144), "hotpink3" },
+ { QRGB(139, 58, 98), "hotpink4" },
+ { QRGB(205, 92, 92), "indianred" },
+ { QRGB(255,106,106), "indianred1" },
+ { QRGB(238, 99, 99), "indianred2" },
+ { QRGB(205, 85, 85), "indianred3" },
+ { QRGB(139, 58, 58), "indianred4" },
+ { QRGB(255,255,240), "ivory" },
+ { QRGB(255,255,240), "ivory1" },
+ { QRGB(238,238,224), "ivory2" },
+ { QRGB(205,205,193), "ivory3" },
+ { QRGB(139,139,131), "ivory4" },
+ { QRGB(240,230,140), "khaki" },
+ { QRGB(255,246,143), "khaki1" },
+ { QRGB(238,230,133), "khaki2" },
+ { QRGB(205,198,115), "khaki3" },
+ { QRGB(139,134, 78), "khaki4" },
+ { QRGB(230,230,250), "lavender" },
+ { QRGB(255,240,245), "lavenderblush" },
+ { QRGB(255,240,245), "lavenderblush1" },
+ { QRGB(238,224,229), "lavenderblush2" },
+ { QRGB(205,193,197), "lavenderblush3" },
+ { QRGB(139,131,134), "lavenderblush4" },
+ { QRGB(124,252, 0), "lawngreen" },
+ { QRGB(255,250,205), "lemonchiffon" },
+ { QRGB(255,250,205), "lemonchiffon1" },
+ { QRGB(238,233,191), "lemonchiffon2" },
+ { QRGB(205,201,165), "lemonchiffon3" },
+ { QRGB(139,137,112), "lemonchiffon4" },
+ { QRGB(173,216,230), "lightblue" },
+ { QRGB(191,239,255), "lightblue1" },
+ { QRGB(178,223,238), "lightblue2" },
+ { QRGB(154,192,205), "lightblue3" },
+ { QRGB(104,131,139), "lightblue4" },
+ { QRGB(240,128,128), "lightcoral" },
+ { QRGB(224,255,255), "lightcyan" },
+ { QRGB(224,255,255), "lightcyan1" },
+ { QRGB(209,238,238), "lightcyan2" },
+ { QRGB(180,205,205), "lightcyan3" },
+ { QRGB(122,139,139), "lightcyan4" },
+ { QRGB(238,221,130), "lightgoldenrod" },
+ { QRGB(255,236,139), "lightgoldenrod1" },
+ { QRGB(238,220,130), "lightgoldenrod2" },
+ { QRGB(205,190,112), "lightgoldenrod3" },
+ { QRGB(139,129, 76), "lightgoldenrod4" },
+ { QRGB(250,250,210), "lightgoldenrodyellow" },
+ { QRGB(211,211,211), "lightgray" },
+ { QRGB(144,238,144), "lightgreen" },
+ { QRGB(211,211,211), "lightgrey" },
+ { QRGB(255,182,193), "lightpink" },
+ { QRGB(255,174,185), "lightpink1" },
+ { QRGB(238,162,173), "lightpink2" },
+ { QRGB(205,140,149), "lightpink3" },
+ { QRGB(139, 95,101), "lightpink4" },
+ { QRGB(255,160,122), "lightsalmon" },
+ { QRGB(255,160,122), "lightsalmon1" },
+ { QRGB(238,149,114), "lightsalmon2" },
+ { QRGB(205,129, 98), "lightsalmon3" },
+ { QRGB(139, 87, 66), "lightsalmon4" },
+ { QRGB( 32,178,170), "lightseagreen" },
+ { QRGB(135,206,250), "lightskyblue" },
+ { QRGB(176,226,255), "lightskyblue1" },
+ { QRGB(164,211,238), "lightskyblue2" },
+ { QRGB(141,182,205), "lightskyblue3" },
+ { QRGB( 96,123,139), "lightskyblue4" },
+ { QRGB(132,112,255), "lightslateblue" },
+ { QRGB(119,136,153), "lightslategray" },
+ { QRGB(119,136,153), "lightslategrey" },
+ { QRGB(176,196,222), "lightsteelblue" },
+ { QRGB(202,225,255), "lightsteelblue1" },
+ { QRGB(188,210,238), "lightsteelblue2" },
+ { QRGB(162,181,205), "lightsteelblue3" },
+ { QRGB(110,123,139), "lightsteelblue4" },
+ { QRGB(255,255,224), "lightyellow" },
+ { QRGB(255,255,224), "lightyellow1" },
+ { QRGB(238,238,209), "lightyellow2" },
+ { QRGB(205,205,180), "lightyellow3" },
+ { QRGB(139,139,122), "lightyellow4" },
+ { QRGB( 50,205, 50), "limegreen" },
+ { QRGB(250,240,230), "linen" },
+ { QRGB(255, 0,255), "magenta" },
+ { QRGB(255, 0,255), "magenta1" },
+ { QRGB(238, 0,238), "magenta2" },
+ { QRGB(205, 0,205), "magenta3" },
+ { QRGB(139, 0,139), "magenta4" },
+ { QRGB(176, 48, 96), "maroon" },
+ { QRGB(255, 52,179), "maroon1" },
+ { QRGB(238, 48,167), "maroon2" },
+ { QRGB(205, 41,144), "maroon3" },
+ { QRGB(139, 28, 98), "maroon4" },
+ { QRGB(102,205,170), "mediumaquamarine" },
+ { QRGB( 0, 0,205), "mediumblue" },
+ { QRGB(186, 85,211), "mediumorchid" },
+ { QRGB(224,102,255), "mediumorchid1" },
+ { QRGB(209, 95,238), "mediumorchid2" },
+ { QRGB(180, 82,205), "mediumorchid3" },
+ { QRGB(122, 55,139), "mediumorchid4" },
+ { QRGB(147,112,219), "mediumpurple" },
+ { QRGB(171,130,255), "mediumpurple1" },
+ { QRGB(159,121,238), "mediumpurple2" },
+ { QRGB(137,104,205), "mediumpurple3" },
+ { QRGB( 93, 71,139), "mediumpurple4" },
+ { QRGB( 60,179,113), "mediumseagreen" },
+ { QRGB(123,104,238), "mediumslateblue" },
+ { QRGB( 0,250,154), "mediumspringgreen" },
+ { QRGB( 72,209,204), "mediumturquoise" },
+ { QRGB(199, 21,133), "mediumvioletred" },
+ { QRGB( 25, 25,112), "midnightblue" },
+ { QRGB(245,255,250), "mintcream" },
+ { QRGB(255,228,225), "mistyrose" },
+ { QRGB(255,228,225), "mistyrose1" },
+ { QRGB(238,213,210), "mistyrose2" },
+ { QRGB(205,183,181), "mistyrose3" },
+ { QRGB(139,125,123), "mistyrose4" },
+ { QRGB(255,228,181), "moccasin" },
+ { QRGB(255,222,173), "navajowhite" },
+ { QRGB(255,222,173), "navajowhite1" },
+ { QRGB(238,207,161), "navajowhite2" },
+ { QRGB(205,179,139), "navajowhite3" },
+ { QRGB(139,121, 94), "navajowhite4" },
+ { QRGB( 0, 0,128), "navy" },
+ { QRGB( 0, 0,128), "navyblue" },
+ { QRGB(253,245,230), "oldlace" },
+ { QRGB(107,142, 35), "olivedrab" },
+ { QRGB(192,255, 62), "olivedrab1" },
+ { QRGB(179,238, 58), "olivedrab2" },
+ { QRGB(154,205, 50), "olivedrab3" },
+ { QRGB(105,139, 34), "olivedrab4" },
+ { QRGB(255,165, 0), "orange" },
+ { QRGB(255,165, 0), "orange1" },
+ { QRGB(238,154, 0), "orange2" },
+ { QRGB(205,133, 0), "orange3" },
+ { QRGB(139, 90, 0), "orange4" },
+ { QRGB(255, 69, 0), "orangered" },
+ { QRGB(255, 69, 0), "orangered1" },
+ { QRGB(238, 64, 0), "orangered2" },
+ { QRGB(205, 55, 0), "orangered3" },
+ { QRGB(139, 37, 0), "orangered4" },
+ { QRGB(218,112,214), "orchid" },
+ { QRGB(255,131,250), "orchid1" },
+ { QRGB(238,122,233), "orchid2" },
+ { QRGB(205,105,201), "orchid3" },
+ { QRGB(139, 71,137), "orchid4" },
+ { QRGB(238,232,170), "palegoldenrod" },
+ { QRGB(152,251,152), "palegreen" },
+ { QRGB(154,255,154), "palegreen1" },
+ { QRGB(144,238,144), "palegreen2" },
+ { QRGB(124,205,124), "palegreen3" },
+ { QRGB( 84,139, 84), "palegreen4" },
+ { QRGB(175,238,238), "paleturquoise" },
+ { QRGB(187,255,255), "paleturquoise1" },
+ { QRGB(174,238,238), "paleturquoise2" },
+ { QRGB(150,205,205), "paleturquoise3" },
+ { QRGB(102,139,139), "paleturquoise4" },
+ { QRGB(219,112,147), "palevioletred" },
+ { QRGB(255,130,171), "palevioletred1" },
+ { QRGB(238,121,159), "palevioletred2" },
+ { QRGB(205,104,137), "palevioletred3" },
+ { QRGB(139, 71, 93), "palevioletred4" },
+ { QRGB(255,239,213), "papayawhip" },
+ { QRGB(255,218,185), "peachpuff" },
+ { QRGB(255,218,185), "peachpuff1" },
+ { QRGB(238,203,173), "peachpuff2" },
+ { QRGB(205,175,149), "peachpuff3" },
+ { QRGB(139,119,101), "peachpuff4" },
+ { QRGB(205,133, 63), "peru" },
+ { QRGB(255,192,203), "pink" },
+ { QRGB(255,181,197), "pink1" },
+ { QRGB(238,169,184), "pink2" },
+ { QRGB(205,145,158), "pink3" },
+ { QRGB(139, 99,108), "pink4" },
+ { QRGB(221,160,221), "plum" },
+ { QRGB(255,187,255), "plum1" },
+ { QRGB(238,174,238), "plum2" },
+ { QRGB(205,150,205), "plum3" },
+ { QRGB(139,102,139), "plum4" },
+ { QRGB(176,224,230), "powderblue" },
+ { QRGB(160, 32,240), "purple" },
+ { QRGB(155, 48,255), "purple1" },
+ { QRGB(145, 44,238), "purple2" },
+ { QRGB(125, 38,205), "purple3" },
+ { QRGB( 85, 26,139), "purple4" },
+ { QRGB(255, 0, 0), "red" },
+ { QRGB(255, 0, 0), "red1" },
+ { QRGB(238, 0, 0), "red2" },
+ { QRGB(205, 0, 0), "red3" },
+ { QRGB(139, 0, 0), "red4" },
+ { QRGB(188,143,143), "rosybrown" },
+ { QRGB(255,193,193), "rosybrown1" },
+ { QRGB(238,180,180), "rosybrown2" },
+ { QRGB(205,155,155), "rosybrown3" },
+ { QRGB(139,105,105), "rosybrown4" },
+ { QRGB( 65,105,225), "royalblue" },
+ { QRGB( 72,118,255), "royalblue1" },
+ { QRGB( 67,110,238), "royalblue2" },
+ { QRGB( 58, 95,205), "royalblue3" },
+ { QRGB( 39, 64,139), "royalblue4" },
+ { QRGB(139, 69, 19), "saddlebrown" },
+ { QRGB(250,128,114), "salmon" },
+ { QRGB(255,140,105), "salmon1" },
+ { QRGB(238,130, 98), "salmon2" },
+ { QRGB(205,112, 84), "salmon3" },
+ { QRGB(139, 76, 57), "salmon4" },
+ { QRGB(244,164, 96), "sandybrown" },
+ { QRGB( 46,139, 87), "seagreen" },
+ { QRGB( 84,255,159), "seagreen1" },
+ { QRGB( 78,238,148), "seagreen2" },
+ { QRGB( 67,205,128), "seagreen3" },
+ { QRGB( 46,139, 87), "seagreen4" },
+ { QRGB(255,245,238), "seashell" },
+ { QRGB(255,245,238), "seashell1" },
+ { QRGB(238,229,222), "seashell2" },
+ { QRGB(205,197,191), "seashell3" },
+ { QRGB(139,134,130), "seashell4" },
+ { QRGB(160, 82, 45), "sienna" },
+ { QRGB(255,130, 71), "sienna1" },
+ { QRGB(238,121, 66), "sienna2" },
+ { QRGB(205,104, 57), "sienna3" },
+ { QRGB(139, 71, 38), "sienna4" },
+ { QRGB(135,206,235), "skyblue" },
+ { QRGB(135,206,255), "skyblue1" },
+ { QRGB(126,192,238), "skyblue2" },
+ { QRGB(108,166,205), "skyblue3" },
+ { QRGB( 74,112,139), "skyblue4" },
+ { QRGB(106, 90,205), "slateblue" },
+ { QRGB(131,111,255), "slateblue1" },
+ { QRGB(122,103,238), "slateblue2" },
+ { QRGB(105, 89,205), "slateblue3" },
+ { QRGB( 71, 60,139), "slateblue4" },
+ { QRGB(112,128,144), "slategray" },
+ { QRGB(198,226,255), "slategray1" },
+ { QRGB(185,211,238), "slategray2" },
+ { QRGB(159,182,205), "slategray3" },
+ { QRGB(108,123,139), "slategray4" },
+ { QRGB(112,128,144), "slategrey" },
+ { QRGB(255,250,250), "snow" },
+ { QRGB(255,250,250), "snow1" },
+ { QRGB(238,233,233), "snow2" },
+ { QRGB(205,201,201), "snow3" },
+ { QRGB(139,137,137), "snow4" },
+ { QRGB( 0,255,127), "springgreen" },
+ { QRGB( 0,255,127), "springgreen1" },
+ { QRGB( 0,238,118), "springgreen2" },
+ { QRGB( 0,205,102), "springgreen3" },
+ { QRGB( 0,139, 69), "springgreen4" },
+ { QRGB( 70,130,180), "steelblue" },
+ { QRGB( 99,184,255), "steelblue1" },
+ { QRGB( 92,172,238), "steelblue2" },
+ { QRGB( 79,148,205), "steelblue3" },
+ { QRGB( 54,100,139), "steelblue4" },
+ { QRGB(210,180,140), "tan" },
+ { QRGB(255,165, 79), "tan1" },
+ { QRGB(238,154, 73), "tan2" },
+ { QRGB(205,133, 63), "tan3" },
+ { QRGB(139, 90, 43), "tan4" },
+ { QRGB(216,191,216), "thistle" },
+ { QRGB(255,225,255), "thistle1" },
+ { QRGB(238,210,238), "thistle2" },
+ { QRGB(205,181,205), "thistle3" },
+ { QRGB(139,123,139), "thistle4" },
+ { QRGB(255, 99, 71), "tomato" },
+ { QRGB(255, 99, 71), "tomato1" },
+ { QRGB(238, 92, 66), "tomato2" },
+ { QRGB(205, 79, 57), "tomato3" },
+ { QRGB(139, 54, 38), "tomato4" },
+ { QRGB( 64,224,208), "turquoise" },
+ { QRGB( 0,245,255), "turquoise1" },
+ { QRGB( 0,229,238), "turquoise2" },
+ { QRGB( 0,197,205), "turquoise3" },
+ { QRGB( 0,134,139), "turquoise4" },
+ { QRGB(238,130,238), "violet" },
+ { QRGB(208, 32,144), "violetred" },
+ { QRGB(255, 62,150), "violetred1" },
+ { QRGB(238, 58,140), "violetred2" },
+ { QRGB(205, 50,120), "violetred3" },
+ { QRGB(139, 34, 82), "violetred4" },
+ { QRGB(245,222,179), "wheat" },
+ { QRGB(255,231,186), "wheat1" },
+ { QRGB(238,216,174), "wheat2" },
+ { QRGB(205,186,150), "wheat3" },
+ { QRGB(139,126,102), "wheat4" },
+ { QRGB(255,255,255), "white" },
+ { QRGB(245,245,245), "whitesmoke" },
+ { QRGB(255,255, 0), "yellow" },
+ { QRGB(255,255, 0), "yellow1" },
+ { QRGB(238,238, 0), "yellow2" },
+ { QRGB(205,205, 0), "yellow3" },
+ { QRGB(139,139, 0), "yellow4" },
+ { QRGB(154,205, 50), "yellowgreen" } };
+
+#if defined(Q_C_CALLBACKS)
+extern "C" {
+#endif
+static int rgb_cmp(const void *d1, const void *d2)
+{
+ return qstricmp(((XPMRGBData *)d1)->name, ((XPMRGBData *)d2)->name);
+}
+#if defined(Q_C_CALLBACKS)
+}
+#endif
+
+static bool qt_get_named_xpm_rgb(const char *name_no_space, QRgb *rgb)
+{
+ XPMRGBData x;
+ x.name = name_no_space;
+ // Funtion bsearch() is supposed to be
+ // void *bsearch(const void *key, const void *base, ...
+ // So why (char*)? Are there broken bsearch() declarations out there?
+ XPMRGBData *r = (XPMRGBData *)bsearch((char *)&x, (char *)xpmRgbTbl, xpmRgbTblSize,
+ sizeof(XPMRGBData), rgb_cmp);
+ if (r) {
+ *rgb = r->value;
+ return true;
+ } else {
+ return false;
+ }
+}
+
+/*****************************************************************************
+ Misc. utility functions
+ *****************************************************************************/
+static QString fbname(const QString &fileName) // get file basename (sort of)
+{
+ QString s = fileName;
+ if (!s.isEmpty()) {
+ int i;
+ if ((i = s.lastIndexOf(QLatin1Char('/'))) >= 0)
+ s = s.mid(i);
+ if ((i = s.lastIndexOf(QLatin1Char('\\'))) >= 0)
+ s = s.mid(i);
+ QRegExp r(QLatin1String("[a-zA-Z][a-zA-Z0-9_]*"));
+ int p = r.indexIn(s);
+ if (p == -1)
+ s.clear();
+ else
+ s = s.mid(p, r.matchedLength());
+ }
+ if (s.isEmpty())
+ s = QString::fromLatin1("dummy");
+ return s;
+}
+
+// Skip until ", read until the next ", return the rest in *buf
+// Returns false on error, true on success
+
+static bool read_xpm_string(QByteArray &buf, QIODevice *d, const char * const *source, int &index,
+ QByteArray &state)
+{
+ if (source) {
+ buf = source[index++];
+ return true;
+ }
+
+ buf = "";
+ bool gotQuote = false;
+ int offset = 0;
+ forever {
+ if (offset == state.size() || state.isEmpty()) {
+ char buf[2048];
+ qint64 bytesRead = d->read(buf, sizeof(buf));
+ if (bytesRead <= 0)
+ return false;
+ state = QByteArray(buf, int(bytesRead));
+ offset = 0;
+ }
+
+ if (!gotQuote) {
+ if (state.at(offset++) == '"')
+ gotQuote = true;
+ } else {
+ char c = state.at(offset++);
+ if (c == '"')
+ break;
+ buf += c;
+ }
+ }
+ state.remove(0, offset);
+ return true;
+}
+
+// Tests if the given prefix can be the start of an XPM color specification
+
+static bool is_xpm_color_spec_prefix(const QByteArray& prefix)
+{
+ return prefix == "c" ||
+ prefix == "g" ||
+ prefix == "g4" ||
+ prefix == "m" ||
+ prefix == "s";
+}
+
+// Reads XPM header.
+
+static bool read_xpm_header(
+ QIODevice *device, const char * const * source, int& index, QByteArray &state,
+ int *cpp, int *ncols, int *w, int *h)
+{
+ QByteArray buf(200, 0);
+
+ if (!read_xpm_string(buf, device, source, index, state))
+ return false;
+
+#if defined(_MSC_VER) && _MSC_VER >= 1400 && !defined(Q_OS_WINCE)
+ if (sscanf_s(buf, "%d %d %d %d", w, h, ncols, cpp) < 4)
+#else
+ if (sscanf(buf, "%d %d %d %d", w, h, ncols, cpp) < 4)
+#endif
+ return false; // < 4 numbers parsed
+
+ return true;
+}
+
+// Reads XPM body (color information & pixels).
+
+static bool read_xpm_body(
+ QIODevice *device, const char * const * source, int& index, QByteArray& state,
+ int cpp, int ncols, int w, int h, QImage& image)
+{
+ QByteArray buf(200, 0);
+ int i;
+
+ if (cpp > 15)
+ return false;
+
+ // For > 256 colors, we delay creation of the image until
+ // after we have read the color specifications, so that we can
+ // create it in correct format (Format_RGB32 vs Format_ARGB32,
+ // depending on absence or presence of "c none", respectively)
+ if (ncols <= 256) {
+ if (image.size() != QSize(w, h) || image.format() != QImage::Format_Indexed8) {
+ image = QImage(w, h, QImage::Format_Indexed8);
+ if (image.isNull())
+ return false;
+ }
+ image.setNumColors(ncols);
+ }
+
+ QMap<quint64, int> colorMap;
+ int currentColor;
+ bool hasTransparency = false;
+
+ for(currentColor=0; currentColor < ncols; ++currentColor) {
+ if (!read_xpm_string(buf, device, source, index, state)) {
+ qWarning("QImage: XPM color specification missing");
+ return false;
+ }
+ QByteArray index;
+ index = buf.left(cpp);
+ buf = buf.mid(cpp).simplified().trimmed().toLower();
+ QList<QByteArray> tokens = buf.split(' ');
+ i = tokens.indexOf("c");
+ if (i < 0)
+ i = tokens.indexOf("g");
+ if (i < 0)
+ i = tokens.indexOf("g4");
+ if (i < 0)
+ i = tokens.indexOf("m");
+ if (i < 0) {
+ qWarning("QImage: XPM color specification is missing: %s", buf.constData());
+ return false; // no c/g/g4/m specification at all
+ }
+ QByteArray color;
+ while ((++i < tokens.size()) && !is_xpm_color_spec_prefix(tokens.at(i))) {
+ color.append(tokens.at(i));
+ }
+ if (color.isEmpty()) {
+ qWarning("QImage: XPM color value is missing from specification: %s", buf.constData());
+ return false; // no color value
+ }
+ buf = color;
+ if (buf == "none") {
+ hasTransparency = true;
+ int transparentColor = currentColor;
+ if (ncols <= 256) {
+ image.setColor(transparentColor, 0);
+ colorMap.insert(xpmHash(QLatin1String(index.constData())), transparentColor);
+ } else {
+ colorMap.insert(xpmHash(QLatin1String(index.constData())), 0);
+ }
+ } else {
+ QRgb c_rgb;
+ if (((buf.length()-1) % 3) && (buf[0] == '#')) {
+ buf.truncate(((buf.length()-1) / 4 * 3) + 1); // remove alpha channel left by imagemagick
+ }
+ if (buf[0] == '#') {
+ qt_get_hex_rgb(buf, &c_rgb);
+ } else {
+ qt_get_named_xpm_rgb(buf, &c_rgb);
+ }
+ if (ncols <= 256) {
+ image.setColor(currentColor, 0xff000000 | c_rgb);
+ colorMap.insert(xpmHash(QLatin1String(index.constData())), currentColor);
+ } else {
+ colorMap.insert(xpmHash(QLatin1String(index.constData())), 0xff000000 | c_rgb);
+ }
+ }
+ }
+
+ if (ncols > 256) {
+ // Now we can create 32-bit image of appropriate format
+ QImage::Format format = hasTransparency ?
+ QImage::Format_ARGB32 : QImage::Format_RGB32;
+ if (image.size() != QSize(w, h) || image.format() != format) {
+ image = QImage(w, h, format);
+ if (image.isNull())
+ return false;
+ }
+ }
+
+ // Read pixels
+ for(int y=0; y<h; y++) {
+ if (!read_xpm_string(buf, device, source, index, state)) {
+ qWarning("QImage: XPM pixels missing on image line %d", y);
+ return false;
+ }
+ if (image.depth() == 8) {
+ uchar *p = image.scanLine(y);
+ uchar *d = (uchar *)buf.data();
+ uchar *end = d + buf.length();
+ int x;
+ if (cpp == 1) {
+ char b[2];
+ b[1] = '\0';
+ for (x=0; x<w && d<end; x++) {
+ b[0] = *d++;
+ *p++ = (uchar)colorMap[xpmHash(b)];
+ }
+ } else {
+ char b[16];
+ b[cpp] = '\0';
+ for (x=0; x<w && d<end; x++) {
+ memcpy(b, (char *)d, cpp);
+ *p++ = (uchar)colorMap[xpmHash(b)];
+ d += cpp;
+ }
+ }
+ // avoid uninitialized memory for malformed xpms
+ if (x < w) {
+ qWarning("QImage: XPM pixels missing on image line %d (possibly a C++ trigraph).", y);
+ memset(p, 0, w - x);
+ }
+ } else {
+ QRgb *p = (QRgb*)image.scanLine(y);
+ uchar *d = (uchar *)buf.data();
+ uchar *end = d + buf.length();
+ int x;
+ char b[16];
+ b[cpp] = '\0';
+ for (x=0; x<w && d<end; x++) {
+ memcpy(b, (char *)d, cpp);
+ *p++ = (QRgb)colorMap[xpmHash(b)];
+ d += cpp;
+ }
+ // avoid uninitialized memory for malformed xpms
+ if (x < w) {
+ qWarning("QImage: XPM pixels missing on image line %d (possibly a C++ trigraph).", y);
+ memset(p, 0, (w - x)*4);
+ }
+ }
+ }
+
+ if (device) {
+ // Rewind unused characters, and skip to the end of the XPM struct.
+ for (int i = state.size() - 1; i >= 0; --i)
+ device->ungetChar(state[i]);
+ char c;
+ while (device->getChar(&c) && c != ';') {}
+ while (device->getChar(&c) && c != '\n') {}
+ }
+ return true;
+}
+
+//
+// INTERNAL
+//
+// Reads an .xpm from either the QImageIO or from the QString *.
+// One of the two HAS to be 0, the other one is used.
+//
+
+bool qt_read_xpm_image_or_array(QIODevice *device, const char * const * source, QImage &image)
+{
+ if (!source)
+ return true;
+
+ QByteArray buf(200, 0);
+ QByteArray state;
+
+ int cpp, ncols, w, h, index = 0;
+
+ if (device) {
+ // "/* XPM */"
+ int readBytes;
+ if ((readBytes = device->readLine(buf.data(), buf.size())) < 0)
+ return false;
+
+ if (buf.indexOf("/* XPM") != 0) {
+ while (readBytes > 0) {
+ device->ungetChar(buf.at(readBytes - 1));
+ --readBytes;
+ }
+ return false;
+ }// bad magic
+ }
+
+ if (!read_xpm_header(device, source, index, state, &cpp, &ncols, &w, &h))
+ return false;
+
+ return read_xpm_body(device, source, index, state, cpp, ncols, w, h, image);
+}
+
+static const char* xpm_color_name(int cpp, int index)
+{
+ static char returnable[5];
+ static const char code[] = ".#abcdefghijklmnopqrstuvwxyzABCD"
+ "EFGHIJKLMNOPQRSTUVWXYZ0123456789";
+ // cpp is limited to 4 and index is limited to 64^cpp
+ if (cpp > 1) {
+ if (cpp > 2) {
+ if (cpp > 3) {
+ returnable[3] = code[index % 64];
+ index /= 64;
+ } else
+ returnable[3] = '\0';
+ returnable[2] = code[index % 64];
+ index /= 64;
+ } else
+ returnable[2] = '\0';
+ // the following 4 lines are a joke!
+ if (index == 0)
+ index = 64*44+21;
+ else if (index == 64*44+21)
+ index = 0;
+ returnable[1] = code[index % 64];
+ index /= 64;
+ } else
+ returnable[1] = '\0';
+ returnable[0] = code[index];
+
+ return returnable;
+}
+
+
+// write XPM image data
+static bool write_xpm_image(const QImage &sourceImage, QIODevice *device, const QString &fileName)
+{
+ if (!device->isWritable())
+ return false;
+
+ QImage image;
+ if (sourceImage.depth() != 32)
+ image = sourceImage.convertToFormat(QImage::Format_RGB32);
+ else
+ image = sourceImage;
+
+ QMap<QRgb, int> colorMap;
+
+ int w = image.width(), h = image.height(), ncolors = 0;
+ int x, y;
+
+ // build color table
+ for(y=0; y<h; y++) {
+ QRgb * yp = (QRgb *)image.scanLine(y);
+ for(x=0; x<w; x++) {
+ QRgb color = *(yp + x);
+ if (!colorMap.contains(color))
+ colorMap.insert(color, ncolors++);
+ }
+ }
+
+ // number of 64-bit characters per pixel needed to encode all colors
+ int cpp = 1;
+ for (int k = 64; ncolors > k; k *= 64) {
+ ++cpp;
+ // limit to 4 characters per pixel
+ // 64^4 colors is enough for a 4096x4096 image
+ if (cpp > 4)
+ break;
+ }
+
+ QString line;
+
+ // write header
+ QTextStream s(device);
+ s << "/* XPM */" << endl
+ << "static char *" << fbname(fileName) << "[]={" << endl
+ << "\"" << w << " " << h << " " << ncolors << " " << cpp << "\"";
+
+ // write palette
+ QMap<QRgb, int>::Iterator c = colorMap.begin();
+ while (c != colorMap.end()) {
+ QRgb color = c.key();
+ if (image.format() != QImage::Format_RGB32 && !qAlpha(color))
+ line.sprintf("\"%s c None\"",
+ xpm_color_name(cpp, *c));
+ else
+ line.sprintf("\"%s c #%02x%02x%02x\"",
+ xpm_color_name(cpp, *c),
+ qRed(color),
+ qGreen(color),
+ qBlue(color));
+ ++c;
+ s << "," << endl << line;
+ }
+
+ // write pixels, limit to 4 characters per pixel
+ line.truncate(cpp*w);
+ for(y=0; y<h; y++) {
+ QRgb * yp = (QRgb *) image.scanLine(y);
+ int cc = 0;
+ for(x=0; x<w; x++) {
+ int color = (int)(*(yp + x));
+ QByteArray chars(xpm_color_name(cpp, colorMap[color]));
+ line[cc++] = QLatin1Char(chars[0]);
+ if (cpp > 1) {
+ line[cc++] = QLatin1Char(chars[1]);
+ if (cpp > 2) {
+ line[cc++] = QLatin1Char(chars[2]);
+ if (cpp > 3) {
+ line[cc++] = QLatin1Char(chars[3]);
+ }
+ }
+ }
+ }
+ s << "," << endl << "\"" << line << "\"";
+ }
+ s << "};" << endl;
+ return (s.status() == QTextStream::Ok);
+}
+
+QXpmHandler::QXpmHandler()
+ : state(Ready), index(0)
+{
+}
+
+bool QXpmHandler::readHeader()
+{
+ state = Error;
+ if (!read_xpm_header(device(), 0, index, buffer, &cpp, &ncols, &width, &height))
+ return false;
+ state = ReadHeader;
+ return true;
+}
+
+bool QXpmHandler::readImage(QImage *image)
+{
+ if (state == Error)
+ return false;
+
+ if (state == Ready && !readHeader()) {
+ state = Error;
+ return false;
+ }
+
+ if (!read_xpm_body(device(), 0, index, buffer, cpp, ncols, width, height, *image)) {
+ state = Error;
+ return false;
+ }
+
+ state = Ready;
+ return true;
+}
+
+bool QXpmHandler::canRead() const
+{
+ if (state == Ready && canRead(device())) {
+ setFormat("xpm");
+ return true;
+ }
+ return state != Error;
+}
+
+bool QXpmHandler::canRead(QIODevice *device)
+{
+ if (!device) {
+ qWarning("QXpmHandler::canRead() called with no device");
+ return false;
+ }
+
+ char head[6];
+ if (device->peek(head, sizeof(head)) != sizeof(head))
+ return false;
+
+ return qstrncmp(head, "/* XPM", 6) == 0;
+}
+
+bool QXpmHandler::read(QImage *image)
+{
+ if (!canRead())
+ return false;
+ return readImage(image);
+}
+
+bool QXpmHandler::write(const QImage &image)
+{
+ return write_xpm_image(image, device(), fileName);
+}
+
+bool QXpmHandler::supportsOption(ImageOption option) const
+{
+ return option == Name
+ || option == Size
+ || option == ImageFormat;
+}
+
+QVariant QXpmHandler::option(ImageOption option) const
+{
+ if (option == Name) {
+ return fileName;
+ } else if (option == Size) {
+ if (state == Error)
+ return QVariant();
+ if (state == Ready && !const_cast<QXpmHandler*>(this)->readHeader())
+ return QVariant();
+ return QSize(width, height);
+ } else if (option == ImageFormat) {
+ if (state == Error)
+ return QVariant();
+ if (state == Ready && !const_cast<QXpmHandler*>(this)->readHeader())
+ return QVariant();
+ // If we have more than 256 colors in the table, we need to
+ // figure out, if it contains transparency. That means reading
+ // the whole color table, which is too much work work pre-checking
+ // the image format
+ if (ncols <= 256)
+ return QImage::Format_Indexed8;
+ else
+ return QImage::Format_Invalid;
+ }
+
+ return QVariant();
+}
+
+void QXpmHandler::setOption(ImageOption option, const QVariant &value)
+{
+ if (option == Name)
+ fileName = value.toString();
+}
+
+QByteArray QXpmHandler::name() const
+{
+ return "xpm";
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_IMAGEFORMAT_XPM
diff --git a/src/gui/image/qxpmhandler_p.h b/src/gui/image/qxpmhandler_p.h
new file mode 100644
index 0000000000..5c2f809998
--- /dev/null
+++ b/src/gui/image/qxpmhandler_p.h
@@ -0,0 +1,100 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QXPMHANDLER_P_H
+#define QXPMHANDLER_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of the QLibrary class. This header file may change from
+// version to version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "QtGui/qimageiohandler.h"
+
+#ifndef QT_NO_IMAGEFORMAT_XPM
+
+QT_BEGIN_NAMESPACE
+
+class QXpmHandler : public QImageIOHandler
+{
+public:
+ QXpmHandler();
+ bool canRead() const;
+ bool read(QImage *image);
+ bool write(const QImage &image);
+
+ static bool canRead(QIODevice *device);
+
+ QByteArray name() const;
+
+ QVariant option(ImageOption option) const;
+ void setOption(ImageOption option, const QVariant &value);
+ bool supportsOption(ImageOption option) const;
+
+private:
+ bool readHeader();
+ bool readImage(QImage *image);
+ enum State {
+ Ready,
+ ReadHeader,
+ Error
+ };
+ State state;
+ int width;
+ int height;
+ int ncols;
+ int cpp;
+ QByteArray buffer;
+ int index;
+ QString fileName;
+};
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_IMAGEFORMAT_XPM
+
+#endif // QXPMHANDLER_P_H
diff --git a/src/gui/inputmethod/inputmethod.pri b/src/gui/inputmethod/inputmethod.pri
new file mode 100644
index 0000000000..d321cd4e0b
--- /dev/null
+++ b/src/gui/inputmethod/inputmethod.pri
@@ -0,0 +1,26 @@
+# Qt inputmethod module
+
+HEADERS +=inputmethod/qinputcontextfactory.h \
+ inputmethod/qinputcontextplugin.h \
+ inputmethod/qinputcontext_p.h \
+ inputmethod/qinputcontext.h
+SOURCES +=inputmethod/qinputcontextfactory.cpp \
+ inputmethod/qinputcontextplugin.cpp \
+ inputmethod/qinputcontext.cpp
+x11 {
+ HEADERS += inputmethod/qximinputcontext_p.h
+ SOURCES += inputmethod/qximinputcontext_x11.cpp
+}
+win32 {
+ HEADERS += inputmethod/qwininputcontext_p.h
+ SOURCES += inputmethod/qwininputcontext_win.cpp
+}
+embedded {
+ HEADERS += inputmethod/qwsinputcontext_p.h
+ SOURCES += inputmethod/qwsinputcontext_qws.cpp
+}
+mac:!embedded {
+ HEADERS += inputmethod/qmacinputcontext_p.h
+ SOURCES += inputmethod/qmacinputcontext_mac.cpp
+}
+
diff --git a/src/gui/inputmethod/qinputcontext.cpp b/src/gui/inputmethod/qinputcontext.cpp
new file mode 100644
index 0000000000..f78b86ad66
--- /dev/null
+++ b/src/gui/inputmethod/qinputcontext.cpp
@@ -0,0 +1,464 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/****************************************************************************
+**
+** Implementation of QInputContext class
+**
+** Copyright (C) 2003-2004 immodule for Qt Project. All rights reserved.
+**
+** This file is written to contribute to Nokia Corporation and/or its subsidiary(-ies) under their own
+** license. You may use this file under your Qt license. Following
+** description is copied from their original file headers. Contact
+** immodule-qt@freedesktop.org if any conditions of this licensing are
+** not clear to you.
+**
+****************************************************************************/
+
+//#define QT_NO_IM_PREEDIT_RELOCATION
+
+#include "qinputcontext.h"
+#include "qinputcontext_p.h"
+
+#ifndef QT_NO_IM
+
+#include "qplatformdefs.h"
+
+#include "qapplication.h"
+#include "qmenu.h"
+#include "qtextformat.h"
+#include "qpalette.h"
+
+#include <stdlib.h>
+#include <limits.h>
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QInputContext
+ \brief The QInputContext class abstracts the input method dependent data and composing state.
+
+ \ingroup i18n
+
+ An input method is responsible to input complex text that cannot
+ be inputted via simple keymap. It converts a sequence of input
+ events (typically key events) into a text string through the input
+ method specific converting process. The class of the processes are
+ widely ranging from simple finite state machine to complex text
+ translator that pools a whole paragraph of a text with text
+ editing capability to perform grammar and semantic analysis.
+
+ To abstract such different input method specific intermediate
+ information, Qt offers the QInputContext as base class. The
+ concept is well known as 'input context' in the input method
+ domain. an input context is created for a text widget in response
+ to a demand. It is ensured that an input context is prepared for
+ an input method before input to a text widget.
+
+ Multiple input contexts that is belonging to a single input method
+ may concurrently coexist. Suppose multi-window text editor. Each
+ text widget of window A and B holds different QInputContext
+ instance which contains different state information such as
+ partially composed text.
+
+ \section1 Groups of Functions
+
+ \table
+ \header \o Context \o Functions
+
+ \row \o Receiving information \o
+ x11FilterEvent(),
+ filterEvent(),
+ mouseHandler()
+
+ \row \o Sending back composed text \o
+ sendEvent()
+
+ \row \o State change notification \o
+ setFocusWidget(),
+ reset()
+
+ \row \o Context information \o
+ identifierName(),
+ language(),
+ font(),
+ isComposing()
+
+ \endtable
+
+ \legalese
+ Copyright (C) 2003-2004 immodule for Qt Project. All rights reserved.
+
+ This file is written to contribute to Nokia Corporation and/or its subsidiary(-ies) under their own
+ license. You may use this file under your Qt license. Following
+ description is copied from their original file headers. Contact
+ immodule-qt@freedesktop.org if any conditions of this licensing are
+ not clear to you.
+ \endlegalese
+
+ \sa QInputContextPlugin, QInputContextFactory, QApplication::setInputContext()
+*/
+
+/*!
+ Constructs an input context with the given \a parent.
+*/
+QInputContext::QInputContext(QObject* parent)
+ : QObject(*new QInputContextPrivate, parent)
+{
+}
+
+
+/*!
+ Destroys the input context.
+*/
+QInputContext::~QInputContext()
+{
+}
+
+/*!
+ \internal
+ Returns the widget that has an input focus for this input
+ context. Ordinary input methods should not call this function
+ directly to keep platform independence and flexible configuration
+ possibility.
+
+ The return value may differ from holderWidget() if the input
+ context is shared between several text widgets.
+
+ \sa setFocusWidget(), holderWidget()
+*/
+QWidget *QInputContext::focusWidget() const
+{
+ Q_D(const QInputContext);
+ return d->focusWidget;
+}
+
+
+/*!
+ \internal
+ Sets the widget that has an input focus for this input
+ context. Ordinary input methods must not call this function
+ directly.
+
+ \sa focusWidget()
+*/
+void QInputContext::setFocusWidget(QWidget *widget)
+{
+ Q_ASSERT(!widget || widget->testAttribute(Qt::WA_InputMethodEnabled));
+ Q_D(QInputContext);
+ d->focusWidget = widget;
+}
+
+/*!
+ \fn bool QInputContext::isComposing() const
+
+ This function indicates whether InputMethodStart event had been
+ sent to the current focus widget. It is ensured that an input
+ context can send InputMethodCompose or InputMethodEnd event safely
+ if this function returned true.
+
+ The state is automatically being tracked through sendEvent().
+
+ \sa sendEvent()
+*/
+
+/*!
+ This function can be reimplemented in a subclass to filter input
+ events.
+
+ Return true if the \a event has been consumed. Otherwise, the
+ unfiltered \a event will be forwarded to widgets as ordinary
+ way. Although the input events have accept() and ignore()
+ methods, leave it untouched.
+
+ \a event is currently restricted to QKeyEvent. But some input
+ method related events such as QWheelEvent or QTabletEvent may be
+ added in future.
+
+ The filtering opportunity is always given to the input context as
+ soon as possible. It has to be taken place before any other key
+ event consumers such as eventfilters and accelerators because some
+ input methods require quite various key combination and
+ sequences. It often conflicts with accelerators and so on, so we
+ must give the input context the filtering opportunity first to
+ ensure all input methods work properly regardless of application
+ design.
+
+ Ordinary input methods require discrete key events to work
+ properly, so Qt's key compression is always disabled for any input
+ contexts.
+
+ \sa QKeyEvent, x11FilterEvent()
+*/
+bool QInputContext::filterEvent(const QEvent * /*event*/)
+{
+ return false;
+}
+
+/*!
+ Sends an input method event specified by \a event to the current focus
+ widget. Implementations of QInputContext should call this method to
+ send the generated input method events and not
+ QApplication::sendEvent(), as the events might have to get dispatched
+ to a different application on some platforms.
+
+ Some complex input methods route the handling to several child
+ contexts (e.g. to enable language switching). To account for this,
+ QInputContext will check if the parent object is a QInputContext. If
+ yes, it will call the parents sendEvent() implementation instead of
+ sending the event directly.
+
+ \sa QInputMethodEvent
+*/
+void QInputContext::sendEvent(const QInputMethodEvent &event)
+{
+ // route events over input context parents to make chaining possible.
+ QInputContext *p = qobject_cast<QInputContext *>(parent());
+ if (p) {
+ p->sendEvent(event);
+ return;
+ }
+
+ QWidget *focus = focusWidget();
+ if (!focus)
+ return;
+
+ QInputMethodEvent e(event);
+ qApp->sendEvent(focus, &e);
+}
+
+
+/*!
+ This function can be reimplemented in a subclass to handle mouse
+ press, release, double-click, and move events within the preedit
+ text. You can use the function to implement mouse-oriented user
+ interface such as text selection or popup menu for candidate
+ selection.
+
+ The \a x parameter is the offset within the string that was sent
+ with the InputMethodCompose event. The alteration boundary of \a
+ x is ensured as character boundary of preedit string accurately.
+
+ The \a event parameter is the event that was sent to the editor
+ widget. The event type is QEvent::MouseButtonPress,
+ QEvent::MouseButtonRelease, QEvent::MouseButtonDblClick or
+ QEvent::MouseButtonMove. The event's button and state indicate
+ the kind of operation that was performed.
+*/
+void QInputContext::mouseHandler(int /*x*/, QMouseEvent *event)
+{
+ // Default behavior for simple ephemeral input contexts. Some
+ // complex input contexts should not be reset here.
+ if (event->type() == QEvent::MouseButtonPress || event->type() == QEvent::MouseButtonDblClick)
+ reset();
+}
+
+
+/*!
+ Returns the font of the current input widget
+*/
+QFont QInputContext::font() const
+{
+ Q_D(const QInputContext);
+ if (!d->focusWidget)
+ return QApplication::font();
+
+ return qvariant_cast<QFont>(d->focusWidget->inputMethodQuery(Qt::ImFont));
+}
+
+/*!
+ This virtual function is called when a state in the focus widget
+ has changed. QInputContext can then use
+ QWidget::inputMethodQuery() to query the new state of the widget.
+*/
+void QInputContext::update()
+{
+}
+
+/*!
+ This virtual function is called when the specified \a widget is
+ destroyed. The \a widget is a widget on which this input context
+ is installed.
+*/
+void QInputContext::widgetDestroyed(QWidget *widget)
+{
+ Q_D(QInputContext);
+ if (widget == d->focusWidget)
+ setFocusWidget(0);
+}
+
+/*!
+ \fn void QInputContext::reset()
+
+ This function can be reimplemented in a subclass to reset the
+ state of the input method.
+
+ This function is called by several widgets to reset input
+ state. For example, a text widget call this function before
+ inserting a text to make widget ready to accept a text.
+
+ Default implementation is sufficient for simple input method. You
+ can override this function to reset external input method engines
+ in complex input method. In the case, call QInputContext::reset()
+ to ensure proper termination of inputting.
+
+ You must not send any QInputMethodEvent except empty InputMethodEnd event using
+ QInputContext::reset() at reimplemented reset(). It will break
+ input state consistency.
+*/
+
+
+/*!
+ \fn QString QInputContext::identifierName()
+
+ This function must be implemented in any subclasses to return the
+ identifier name of the input method.
+
+ Return value is the name to identify and specify input methods for
+ the input method switching mechanism and so on. The name has to be
+ consistent with QInputContextPlugin::keys(). The name has to
+ consist of ASCII characters only.
+
+ There are two different names with different responsibility in the
+ input method domain. This function returns one of them. Another
+ name is called 'display name' that stands for the name for
+ endusers appeared in a menu and so on.
+
+ \sa QInputContextPlugin::keys(), QInputContextPlugin::displayName()
+*/
+
+
+/*!
+ \fn QString QInputContext::language()
+
+ This function must be implemented in any subclasses to return a
+ language code (e.g. "zh_CN", "zh_TW", "zh_HK", "ja", "ko", ...)
+ of the input context. If the input context can handle multiple
+ languages, return the currently used one. The name has to be
+ consistent with QInputContextPlugin::language().
+
+ This information will be used by language tagging feature in
+ QInputMethodEvent. It is required to distinguish unified han characters
+ correctly. It enables proper font and character code
+ handling. Suppose CJK-awared multilingual web browser
+ (that automatically modifies fonts in CJK-mixed text) and XML editor
+ (that automatically inserts lang attr).
+*/
+
+
+/*!
+ This is a preliminary interface for Qt 4.
+*/
+QList<QAction *> QInputContext::actions()
+{
+ return QList<QAction *>();
+}
+
+/*!
+ \enum QInputContext::StandardFormat
+
+ \value PreeditFormat The preedit text.
+ \value SelectionFormat The selection text.
+
+ \sa standardFormat()
+*/
+
+/*!
+ Returns a QTextFormat object that specifies the format for
+ component \a s.
+*/
+QTextFormat QInputContext::standardFormat(StandardFormat s) const
+{
+ QWidget *focus = focusWidget();
+ const QPalette &pal = focus ? focus->palette() : qApp->palette();
+
+ QTextCharFormat fmt;
+ QColor bg;
+ switch (s) {
+ case QInputContext::PreeditFormat: {
+ fmt.setUnderlineStyle(QTextCharFormat::DashUnderline);
+#ifndef Q_WS_WIN
+ int h1, s1, v1, h2, s2, v2;
+ pal.color(QPalette::Base).getHsv(&h1, &s1, &v1);
+ pal.color(QPalette::Background).getHsv(&h2, &s2, &v2);
+ bg.setHsv(h1, s1, (v1 + v2) / 2);
+ fmt.setBackground(QBrush(bg));
+#endif
+ break;
+ }
+ case QInputContext::SelectionFormat: {
+ bg = pal.text().color();
+ fmt.setBackground(QBrush(bg));
+ fmt.setForeground(pal.background());
+ break;
+ }
+ }
+ return fmt;
+}
+
+#ifdef Q_WS_X11
+/*!
+ This function may be overridden only if input method is depending
+ on X11 and you need raw XEvent. Otherwise, this function must not.
+
+ This function is designed to filter raw key events for XIM, but
+ other input methods may use this to implement some special
+ features such as distinguishing Shift_L and Shift_R.
+
+ Return true if the \a event has been consumed. Otherwise, the
+ unfiltered \a event will be translated into QEvent and forwarded
+ to filterEvent(). Filtering at both x11FilterEvent() and
+ filterEvent() in single input method is allowed.
+
+ \a keywidget is a client widget into which a text is inputted. \a
+ event is inputted XEvent.
+
+ \sa filterEvent()
+*/
+bool QInputContext::x11FilterEvent(QWidget * /*keywidget*/, XEvent * /*event*/)
+{
+ return false;
+}
+#endif // Q_WS_X11
+
+QT_END_NAMESPACE
+
+#endif //Q_NO_IM
diff --git a/src/gui/inputmethod/qinputcontext.h b/src/gui/inputmethod/qinputcontext.h
new file mode 100644
index 0000000000..bcaa7fe0aa
--- /dev/null
+++ b/src/gui/inputmethod/qinputcontext.h
@@ -0,0 +1,134 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/****************************************************************************
+**
+** Definition of QInputContext class
+**
+** Copyright (C) 2003-2004 immodule for Qt Project. All rights reserved.
+**
+** This file is written to contribute to Nokia Corporation and/or its subsidiary(-ies) under their own
+** license. You may use this file under your Qt license. Following
+** description is copied from their original file headers. Contact
+** immodule-qt@freedesktop.org if any conditions of this licensing are
+** not clear to you.
+**
+****************************************************************************/
+
+#ifndef QINPUTCONTEXT_H
+#define QINPUTCONTEXT_H
+
+#include <QtCore/qobject.h>
+#include <QtCore/qglobal.h>
+#include <QtGui/qevent.h>
+#include <QtCore/qstring.h>
+#include <QtCore/qlist.h>
+#include <QtGui/qaction.h>
+
+#ifndef QT_NO_IM
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+class QWidget;
+class QFont;
+class QPopupMenu;
+class QInputContextPrivate;
+
+
+class Q_GUI_EXPORT QInputContext : public QObject
+{
+ Q_OBJECT
+ Q_DECLARE_PRIVATE(QInputContext)
+public:
+ explicit QInputContext(QObject* parent = 0);
+ virtual ~QInputContext();
+
+ virtual QString identifierName() = 0;
+ virtual QString language() = 0;
+
+ virtual void reset() = 0;
+ virtual void update();
+
+ virtual void mouseHandler( int x, QMouseEvent *event);
+ virtual QFont font() const;
+ virtual bool isComposing() const = 0;
+
+ QWidget *focusWidget() const;
+ virtual void setFocusWidget( QWidget *w );
+
+ virtual void widgetDestroyed(QWidget *w);
+
+ virtual QList<QAction *> actions();
+
+#if defined(Q_WS_X11)
+ virtual bool x11FilterEvent( QWidget *keywidget, XEvent *event );
+#endif // Q_WS_X11
+ virtual bool filterEvent( const QEvent *event );
+
+ void sendEvent(const QInputMethodEvent &event);
+
+ enum StandardFormat {
+ PreeditFormat,
+ SelectionFormat
+ };
+ QTextFormat standardFormat(StandardFormat s) const;
+private:
+ friend class QWidget;
+ friend class QWidgetPrivate;
+ friend class QInputContextFactory;
+ friend class QApplication;
+private: // Disabled copy constructor and operator=
+ QInputContext( const QInputContext & );
+ QInputContext &operator=( const QInputContext & );
+
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif //Q_NO_IM
+
+#endif // QINPUTCONTEXT_H
diff --git a/src/gui/inputmethod/qinputcontext_p.h b/src/gui/inputmethod/qinputcontext_p.h
new file mode 100644
index 0000000000..8c1b8de66b
--- /dev/null
+++ b/src/gui/inputmethod/qinputcontext_p.h
@@ -0,0 +1,98 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//
+// 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.
+//
+
+/****************************************************************************
+**
+** Implementation of QInputContext class
+**
+** Copyright (C) 2003-2004 immodule for Qt Project. All rights reserved.
+**
+** This file is written to contribute to Nokia Corporation and/or its subsidiary(-ies) under their own
+** license. You may use this file under your Qt license. Following
+** description is copied from their original file headers. Contact
+** immodule-qt@freedesktop.org if any conditions of this licensing are
+** not clear to you.
+**
+****************************************************************************/
+
+#ifndef QINPUTCONTEXT_P_H
+#define QINPUTCONTEXT_P_H
+
+#include "private/qobject_p.h"
+#include "qwidget.h"
+#include "qinputcontext.h"
+
+#ifndef QT_NO_IM
+
+QT_BEGIN_NAMESPACE
+
+class QInputContextPrivate : public QObjectPrivate
+{
+ Q_DECLARE_PUBLIC(QInputContext)
+public:
+ QInputContextPrivate()
+ : focusWidget(0)
+ {}
+
+ QWidget *focusWidget;
+
+#if defined(Q_WS_WIN) || defined(Q_WS_QWS)
+ static void updateImeStatus(QWidget *w, bool hasFocus);
+#endif
+};
+
+QT_END_NAMESPACE
+
+#endif
+
+#endif
+
diff --git a/src/gui/inputmethod/qinputcontextfactory.cpp b/src/gui/inputmethod/qinputcontextfactory.cpp
new file mode 100644
index 0000000000..06625f9938
--- /dev/null
+++ b/src/gui/inputmethod/qinputcontextfactory.cpp
@@ -0,0 +1,287 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/****************************************************************************
+**
+** Implementation of QInputContextFactory class
+**
+** Copyright (C) 2003-2004 immodule for Qt Project. All rights reserved.
+**
+** This file is written to contribute to Nokia Corporation and/or its subsidiary(-ies) under their own
+** license. You may use this file under your Qt license. Following
+** description is copied from their original file headers. Contact
+** immodule-qt@freedesktop.org if any conditions of this licensing are
+** not clear to you.
+**
+****************************************************************************/
+
+#include "qinputcontextfactory.h"
+
+#ifndef QT_NO_IM
+
+#include "qcoreapplication.h"
+#include "qinputcontext.h"
+#include "qinputcontextplugin.h"
+
+#ifdef Q_WS_X11
+#include "private/qt_x11_p.h"
+#include "qximinputcontext_p.h"
+#endif
+#ifdef Q_WS_WIN
+#include "qwininputcontext_p.h"
+#endif
+#ifdef Q_WS_MAC
+#include "qmacinputcontext_p.h"
+#endif
+
+#include "private/qfactoryloader_p.h"
+#include "qmutex.h"
+
+QT_BEGIN_NAMESPACE
+
+#if !defined(QT_NO_LIBRARY) && !defined(QT_NO_SETTINGS)
+Q_GLOBAL_STATIC_WITH_ARGS(QFactoryLoader, loader,
+ (QInputContextFactoryInterface_iid, QLatin1String("/inputmethods")))
+#endif
+
+/*!
+ \class QInputContextFactory
+ \brief The QInputContextFactory class creates QInputContext objects.
+
+ \ingroup appearance
+
+ The input context factory creates a QInputContext object for a
+ given key with QInputContextFactory::create().
+
+ The input contexts are either built-in or dynamically loaded from
+ an input context plugin (see QInputContextPlugin).
+
+ keys() returns a list of valid keys. The
+ keys are the names used, for example, to identify and specify
+ input methods for the input method switching mechanism. The names
+ have to be consistent with QInputContext::identifierName(), and
+ may only contain ASCII characters.
+
+ A key can be used to retrieve the associated input context's
+ supported languages using languages(). You
+ can retrieve the input context's description using
+ description() and finally you can get a user
+ friendly internationalized name of the QInputContext object
+ specified by the key using displayName().
+
+ \legalese
+ Copyright (C) 2003-2004 immodule for Qt Project. All rights reserved.
+
+ This file is written to contribute to Nokia Corporation and/or its subsidiary(-ies) under their own
+ license. You may use this file under your Qt license. Following
+ description is copied from their original file headers. Contact
+ immodule-qt@freedesktop.org if any conditions of this licensing are
+ not clear to you.
+ \endlegalese
+
+ \sa QInputContext, QInputContextPlugin
+*/
+
+/*!
+ Creates and returns a QInputContext object for the input context
+ specified by \a key with the given \a parent. Keys are case
+ sensitive.
+
+ \sa keys()
+*/
+QInputContext *QInputContextFactory::create( const QString& key, QObject *parent )
+{
+ QInputContext *result = 0;
+#if defined(Q_WS_X11) && !defined(QT_NO_XIM)
+ if (key == QLatin1String("xim")) {
+ result = new QXIMInputContext;
+ }
+#endif
+#if defined(Q_WS_WIN)
+ if (key == QLatin1String("win")) {
+ result = new QWinInputContext;
+ }
+#endif
+#if defined(Q_WS_MAC)
+ if (key == QLatin1String("mac")) {
+ result = new QMacInputContext;
+ }
+#endif
+#if defined(QT_NO_LIBRARY) || defined(QT_NO_SETTINGS)
+ Q_UNUSED(key);
+#else
+ if (QInputContextFactoryInterface *factory =
+ qobject_cast<QInputContextFactoryInterface*>(loader()->instance(key))) {
+ result = factory->create(key);
+ }
+#endif
+ if (result)
+ result->setParent(parent);
+ return result;
+}
+
+
+/*!
+ Returns the list of keys this factory can create input contexts
+ for.
+
+ The keys are the names used, for example, to identify and specify
+ input methods for the input method switching mechanism. The names
+ have to be consistent with QInputContext::identifierName(), and
+ may only contain ASCII characters.
+
+ \sa create(), displayName(), QInputContext::identifierName()
+*/
+QStringList QInputContextFactory::keys()
+{
+ QStringList result;
+#if defined(Q_WS_X11) && !defined(QT_NO_XIM)
+ result << QLatin1String("xim");
+#endif
+#if defined(Q_WS_WIN) && !defined(QT_NO_XIM)
+ result << QLatin1String("win");
+#endif
+#if defined(Q_WS_MAC)
+ result << QLatin1String("mac");
+#endif
+#if !defined(QT_NO_LIBRARY) && !defined(QT_NO_SETTINGS)
+ result += loader()->keys();
+#endif // QT_NO_LIBRARY
+ return result;
+}
+
+/*!
+ Returns the languages supported by the QInputContext object
+ specified by \a key.
+
+ The languages are expressed as language code (e.g. "zh_CN",
+ "zh_TW", "zh_HK", "ja", "ko", ...). An input context that supports
+ multiple languages can return all supported languages as a
+ QStringList. The name has to be consistent with
+ QInputContext::language().
+
+ This information may be used to optimize a user interface.
+
+ \sa keys(), QInputContext::language(), QLocale
+*/
+QStringList QInputContextFactory::languages( const QString &key )
+{
+ QStringList result;
+#if defined(Q_WS_X11) && !defined(QT_NO_XIM)
+ if (key == QLatin1String("xim"))
+ return QStringList(QString());
+#endif
+#if defined(Q_WS_WIN)
+ if (key == QLatin1String("win"))
+ return QStringList(QString());
+#endif
+#if defined(Q_WS_MAC)
+ if (key == QLatin1String("mac"))
+ return QStringList(QString());
+#endif
+#if defined(QT_NO_LIBRARY) || defined(QT_NO_SETTINGS)
+ Q_UNUSED(key);
+#else
+ if (QInputContextFactoryInterface *factory =
+ qobject_cast<QInputContextFactoryInterface*>(loader()->instance(key)))
+ result = factory->languages(key);
+#endif // QT_NO_LIBRARY
+ return result;
+}
+
+/*!
+ Returns a user friendly internationalized name of the
+ QInputContext object specified by \a key. You can, for example,
+ use this name in a menu.
+
+ \sa keys(), QInputContext::identifierName()
+*/
+QString QInputContextFactory::displayName( const QString &key )
+{
+ QString result;
+#if defined(Q_WS_X11) && !defined(QT_NO_XIM)
+ if (key == QLatin1String("xim"))
+ return QInputContext::tr( "XIM" );
+#endif
+#if defined(QT_NO_LIBRARY) || defined(QT_NO_SETTINGS)
+ Q_UNUSED(key);
+#else
+ if (QInputContextFactoryInterface *factory =
+ qobject_cast<QInputContextFactoryInterface*>(loader()->instance(key)))
+ return factory->displayName(key);
+#endif // QT_NO_LIBRARY
+ return QString();
+}
+
+/*!
+ Returns an internationalized brief description of the QInputContext
+ object specified by \a key. You can, for example, use this
+ description in a user interface.
+
+ \sa keys(), displayName()
+*/
+QString QInputContextFactory::description( const QString &key )
+{
+#if defined(Q_WS_X11) && !defined(QT_NO_XIM)
+ if (key == QLatin1String("xim"))
+ return QInputContext::tr( "XIM input method" );
+#endif
+#if defined(Q_WS_WIN) && !defined(QT_NO_XIM)
+ if (key == QLatin1String("win"))
+ return QInputContext::tr( "Windows input method" );
+#endif
+#if defined(Q_WS_MAC)
+ if (key == QLatin1String("mac"))
+ return QInputContext::tr( "Mac OS X input method" );
+#endif
+#if defined(QT_NO_LIBRARY) || defined(QT_NO_SETTINGS)
+ Q_UNUSED(key);
+#else
+ if (QInputContextFactoryInterface *factory =
+ qobject_cast<QInputContextFactoryInterface*>(loader()->instance(key)))
+ return factory->description(key);
+#endif // QT_NO_LIBRARY
+ return QString();
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_IM
diff --git a/src/gui/inputmethod/qinputcontextfactory.h b/src/gui/inputmethod/qinputcontextfactory.h
new file mode 100644
index 0000000000..bc17fd375d
--- /dev/null
+++ b/src/gui/inputmethod/qinputcontextfactory.h
@@ -0,0 +1,88 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/****************************************************************************
+**
+** Definition of QInputContextFactory class
+**
+** Copyright (C) 2003-2004 immodule for Qt Project. All rights reserved.
+**
+** This file is written to contribute to Nokia Corporation and/or its subsidiary(-ies) under their own
+** license. You may use this file under your Qt license. Following
+** description is copied from their original file headers. Contact
+** immodule-qt@freedesktop.org if any conditions of this licensing are
+** not clear to you.
+**
+****************************************************************************/
+
+#ifndef QINPUTCONTEXTFACTORY_H
+#define QINPUTCONTEXTFACTORY_H
+
+#include <QtCore/qstringlist.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+#ifndef QT_NO_IM
+
+class QInputContext;
+class QWidget;
+
+class Q_GUI_EXPORT QInputContextFactory
+{
+public:
+ static QStringList keys();
+ static QInputContext *create( const QString &key, QObject *parent ); // should be a toplevel widget
+ static QStringList languages( const QString &key );
+ static QString displayName( const QString &key );
+ static QString description( const QString &key );
+};
+
+#endif // QT_NO_IM
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QINPUTCONTEXTFACTORY_H
diff --git a/src/gui/inputmethod/qinputcontextplugin.cpp b/src/gui/inputmethod/qinputcontextplugin.cpp
new file mode 100644
index 0000000000..69b480028b
--- /dev/null
+++ b/src/gui/inputmethod/qinputcontextplugin.cpp
@@ -0,0 +1,178 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/****************************************************************************
+**
+** Implementation of QInputContext class
+**
+** Copyright (C) 2003-2004 immodule for Qt Project. All rights reserved.
+**
+** This file is written to contribute to Nokia Corporation and/or its subsidiary(-ies) under their own
+** license. You may use this file under your Qt license. Following
+** description is copied from their original file headers. Contact
+** immodule-qt@freedesktop.org if any conditions of this licensing are
+** not clear to you.
+**
+****************************************************************************/
+
+#include "qinputcontextplugin.h"
+
+#ifndef QT_NO_IM
+#ifndef QT_NO_LIBRARY
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QInputContextPlugin
+ \brief The QInputContextPlugin class provides an abstract base for custom QInputContext plugins.
+
+ \reentrant
+ \ingroup plugins
+
+ The input context plugin is a simple plugin interface that makes it
+ easy to create custom input contexts that can be loaded dynamically
+ into applications.
+
+ To create an input context plugin you subclass this base class,
+ reimplement the pure virtual functions keys(), create(),
+ languages(), displayName(), and description(), and export the
+ class with the Q_EXPORT_PLUGIN2() macro.
+
+ \legalese
+ Copyright (C) 2003-2004 immodule for Qt Project. All rights reserved.
+
+ This file is written to contribute to Nokia Corporation and/or its subsidiary(-ies) under their own
+ license. You may use this file under your Qt license. Following
+ description is copied from their original file headers. Contact
+ immodule-qt@freedesktop.org if any conditions of this licensing are
+ not clear to you.
+ \endlegalese
+
+ \sa QInputContext, {How to Create Qt Plugins}
+*/
+
+/*!
+ \fn QStringList QInputContextPlugin::keys() const
+
+ Returns the list of QInputContext keys this plugin provides.
+
+ These keys are usually the class names of the custom input context
+ that are implemented in the plugin. The names are used, for
+ example, to identify and specify input methods for the input
+ method switching mechanism. They have to be consistent with
+ QInputContext::identifierName(), and may only contain ASCII
+ characters.
+
+ \sa create(), displayName(), QInputContext::identifierName()
+*/
+
+/*!
+ \fn QInputContext* QInputContextPlugin::create( const QString& key )
+
+ Creates and returns a QInputContext object for the input context
+ key \a key. The input context key is usually the class name of
+ the required input method.
+
+ \sa keys()
+*/
+
+/*!
+ \fn QStringList QInputContextPlugin::languages(const QString &key)
+
+ Returns the languages supported by the QInputContext object
+ specified by \a key.
+
+ The languages are expressed as language code (e.g. "zh_CN",
+ "zh_TW", "zh_HK", "ja", "ko", ...). An input context that supports
+ multiple languages can return all supported languages as
+ QStringList. The name has to be consistent with
+ QInputContext::language().
+
+ This information may be used to optimize user interface.
+
+ \sa keys(), QInputContext::language(), QLocale
+*/
+
+/*!
+ \fn QString QInputContextPlugin::displayName(const QString &key)
+
+ Returns a user friendly internationalized name of the
+ QInputContext object specified by \a key. You can, for example,
+ use this name in a menu.
+
+ \sa keys(), QInputContext::identifierName()
+*/
+
+/*!
+ \fn QString QInputContextPlugin::description(const QString &key)
+
+ Returns an internationalized brief description of the QInputContext
+ object specified by \a key. You can, for example, use this
+ description in a user interface.
+
+ \sa keys(), displayName()
+*/
+
+
+/*!
+ Constructs a input context plugin with the given \a parent. This
+ is invoked automatically by the Q_EXPORT_PLUGIN2() macro.
+*/
+QInputContextPlugin::QInputContextPlugin(QObject *parent)
+ :QObject(parent)
+{
+}
+
+/*!
+ Destroys the input context plugin.
+
+ You never have to call this explicitly. Qt destroys a plugin
+ automatically when it's no longer used.
+*/
+QInputContextPlugin::~QInputContextPlugin()
+{
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_LIBRARY
+
+#endif // QT_NO_IM
diff --git a/src/gui/inputmethod/qinputcontextplugin.h b/src/gui/inputmethod/qinputcontextplugin.h
new file mode 100644
index 0000000000..6c572848cf
--- /dev/null
+++ b/src/gui/inputmethod/qinputcontextplugin.h
@@ -0,0 +1,106 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/****************************************************************************
+**
+** Definition of QInputContextPlugin class
+**
+** Copyright (C) 2003-2004 immodule for Qt Project. All rights reserved.
+**
+** This file is written to contribute to Nokia Corporation and/or its subsidiary(-ies) under their own
+** license. You may use this file under your Qt license. Following
+** description is copied from their original file headers. Contact
+** immodule-qt@freedesktop.org if any conditions of this licensing are
+** not clear to you.
+**
+****************************************************************************/
+
+#ifndef QINPUTCONTEXTPLUGIN_H
+#define QINPUTCONTEXTPLUGIN_H
+
+#include <QtCore/qplugin.h>
+#include <QtCore/qfactoryinterface.h>
+#include <QtCore/qstringlist.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+#if !defined(QT_NO_IM) && !defined(QT_NO_LIBRARY)
+
+class QInputContext;
+class QInputContextPluginPrivate;
+
+struct Q_GUI_EXPORT QInputContextFactoryInterface : public QFactoryInterface
+{
+ virtual QInputContext *create( const QString &key ) = 0;
+ virtual QStringList languages( const QString &key ) = 0;
+ virtual QString displayName( const QString &key ) = 0;
+ virtual QString description( const QString &key ) = 0;
+};
+
+#define QInputContextFactoryInterface_iid "com.trolltech.Qt.QInputContextFactoryInterface"
+Q_DECLARE_INTERFACE(QInputContextFactoryInterface, QInputContextFactoryInterface_iid)
+
+class Q_GUI_EXPORT QInputContextPlugin : public QObject, public QInputContextFactoryInterface
+{
+ Q_OBJECT
+ Q_INTERFACES(QInputContextFactoryInterface:QFactoryInterface)
+public:
+ explicit QInputContextPlugin(QObject *parent = 0);
+ ~QInputContextPlugin();
+
+ virtual QStringList keys() const = 0;
+ virtual QInputContext *create( const QString &key ) = 0;
+ virtual QStringList languages( const QString &key ) = 0;
+ virtual QString displayName( const QString &key ) = 0;
+ virtual QString description( const QString &key ) = 0;
+};
+
+#endif // QT_NO_IM
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QINPUTCONTEXTPLUGIN_H
diff --git a/src/gui/inputmethod/qmacinputcontext_mac.cpp b/src/gui/inputmethod/qmacinputcontext_mac.cpp
new file mode 100644
index 0000000000..f0e7ea9807
--- /dev/null
+++ b/src/gui/inputmethod/qmacinputcontext_mac.cpp
@@ -0,0 +1,349 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <qvarlengtharray.h>
+#include <qwidget.h>
+#include <private/qmacinputcontext_p.h>
+#include "qtextformat.h"
+#include <qdebug.h>
+#include <private/qapplication_p.h>
+
+QT_BEGIN_NAMESPACE
+
+extern bool qt_sendSpontaneousEvent(QObject*, QEvent*);
+
+#if (MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_5)
+# define typeRefCon typeSInt32
+# define typeByteCount typeSInt32
+#endif
+
+static QTextFormat qt_mac_compose_format()
+{
+ QTextCharFormat ret;
+ ret.setFontUnderline(true);
+ return ret;
+}
+
+QMacInputContext::QMacInputContext(QObject *parent)
+ : QInputContext(parent), composing(false), recursionGuard(false), textDocument(0)
+{
+// createTextDocument();
+}
+
+QMacInputContext::~QMacInputContext()
+{
+#ifdef Q_WS_MAC32
+ if(textDocument)
+ DeleteTSMDocument(textDocument);
+#endif
+}
+
+void
+QMacInputContext::createTextDocument()
+{
+#ifdef Q_WS_MAC32
+ if(!textDocument) {
+ InterfaceTypeList itl = { kUnicodeDocument };
+ NewTSMDocument(1, itl, &textDocument, SRefCon(this));
+ }
+#endif
+}
+
+
+QString QMacInputContext::language()
+{
+ return QString();
+}
+
+
+void QMacInputContext::mouseHandler(int pos, QMouseEvent *e)
+{
+#ifdef Q_WS_MAC32
+ if(e->type() != QEvent::MouseButtonPress)
+ return;
+
+ if (!composing)
+ return;
+ if (pos < 0 || pos > currentText.length())
+ reset();
+ // ##### handle mouse position
+#endif
+}
+
+#if !defined QT_MAC_USE_COCOA
+
+void QMacInputContext::reset()
+{
+ if (recursionGuard)
+ return;
+ if (!currentText.isEmpty()){
+ QInputMethodEvent e;
+ e.setCommitString(currentText);
+ qt_sendSpontaneousEvent(focusWidget(), &e);
+ currentText = QString();
+ }
+ recursionGuard = true;
+ createTextDocument();
+ composing = false;
+ ActivateTSMDocument(textDocument);
+ FixTSMDocument(textDocument);
+ recursionGuard = false;
+}
+
+bool QMacInputContext::isComposing() const
+{
+ return composing;
+}
+#endif
+
+void QMacInputContext::setFocusWidget(QWidget *w)
+{
+ createTextDocument();
+#ifdef Q_WS_MAC32
+ if(w)
+ ActivateTSMDocument(textDocument);
+ else
+ DeactivateTSMDocument(textDocument);
+#endif
+ QInputContext::setFocusWidget(w);
+}
+
+
+static EventTypeSpec input_events[] = {
+ { kEventClassTextInput, kEventTextInputUnicodeForKeyEvent },
+ { kEventClassTextInput, kEventTextInputOffsetToPos },
+ { kEventClassTextInput, kEventTextInputUpdateActiveInputArea }
+};
+static EventHandlerUPP input_proc_handlerUPP = 0;
+static EventHandlerRef input_proc_handler = 0;
+
+void
+QMacInputContext::initialize()
+{
+#ifdef Q_WS_MAC32
+ if(!input_proc_handler) {
+ input_proc_handlerUPP = NewEventHandlerUPP(QMacInputContext::globalEventProcessor);
+ InstallEventHandler(GetApplicationEventTarget(), input_proc_handlerUPP,
+ GetEventTypeCount(input_events), input_events,
+ 0, &input_proc_handler);
+ }
+#endif
+}
+
+void
+QMacInputContext::cleanup()
+{
+#ifdef Q_WS_MAC32
+ if(input_proc_handler) {
+ RemoveEventHandler(input_proc_handler);
+ input_proc_handler = 0;
+ }
+ if(input_proc_handlerUPP) {
+ DisposeEventHandlerUPP(input_proc_handlerUPP);
+ input_proc_handlerUPP = 0;
+ }
+#endif
+}
+
+OSStatus
+QMacInputContext::globalEventProcessor(EventHandlerCallRef, EventRef event, void *)
+{
+#ifdef Q_WS_MAC32
+ QScopedLoopLevelCounter loopLevelCounter(QApplicationPrivate::instance()->threadData);
+
+ SRefCon refcon = 0;
+ GetEventParameter(event, kEventParamTextInputSendRefCon, typeRefCon, 0,
+ sizeof(refcon), 0, &refcon);
+ QMacInputContext *context = reinterpret_cast<QMacInputContext*>(refcon);
+
+ bool handled_event=true;
+ UInt32 ekind = GetEventKind(event), eclass = GetEventClass(event);
+ switch(eclass) {
+ case kEventClassTextInput: {
+ handled_event = false;
+ QWidget *widget = QApplicationPrivate::focus_widget;
+ if(!widget || (context && widget->inputContext() != context)) {
+ handled_event = false;
+ } else if(ekind == kEventTextInputOffsetToPos) {
+ if(!widget->testAttribute(Qt::WA_InputMethodEnabled)) {
+ handled_event = false;
+ break;
+ }
+
+ QRect mr(widget->inputMethodQuery(Qt::ImMicroFocus).toRect());
+ QPoint mp(widget->mapToGlobal(QPoint(mr.topLeft())));
+ Point pt;
+ pt.h = mp.x();
+ pt.v = mp.y() + mr.height();
+ SetEventParameter(event, kEventParamTextInputReplyPoint, typeQDPoint,
+ sizeof(pt), &pt);
+ handled_event = true;
+ } else if(ekind == kEventTextInputUpdateActiveInputArea) {
+ if(!widget->testAttribute(Qt::WA_InputMethodEnabled)) {
+ handled_event = false;
+ break;
+ }
+
+ if (context->recursionGuard)
+ break;
+
+ ByteCount unilen = 0;
+ GetEventParameter(event, kEventParamTextInputSendText, typeUnicodeText,
+ 0, 0, &unilen, 0);
+ UniChar *unicode = (UniChar*)NewPtr(unilen);
+ GetEventParameter(event, kEventParamTextInputSendText, typeUnicodeText,
+ 0, unilen, 0, unicode);
+ QString text((QChar*)unicode, unilen / sizeof(UniChar));
+ DisposePtr((char*)unicode);
+
+ ByteCount fixed_length = 0;
+ GetEventParameter(event, kEventParamTextInputSendFixLen, typeByteCount, 0,
+ sizeof(fixed_length), 0, &fixed_length);
+ if(fixed_length == ULONG_MAX || fixed_length == unilen) {
+ QInputMethodEvent e;
+ e.setCommitString(text);
+ context->currentText = QString();
+ qt_sendSpontaneousEvent(context->focusWidget(), &e);
+ handled_event = true;
+ context->reset();
+ } else {
+ ByteCount rngSize = 0;
+ OSStatus err = GetEventParameter(event, kEventParamTextInputSendHiliteRng, typeTextRangeArray, 0,
+ 0, &rngSize, 0);
+ QVarLengthArray<TextRangeArray> highlight(rngSize);
+ if (noErr == err) {
+ err = GetEventParameter(event, kEventParamTextInputSendHiliteRng, typeTextRangeArray, 0,
+ rngSize, &rngSize, highlight.data());
+ }
+ context->composing = true;
+ if(fixed_length > 0) {
+ const int qFixedLength = fixed_length / sizeof(UniChar);
+ QList<QInputMethodEvent::Attribute> attrs;
+ attrs << QInputMethodEvent::Attribute(QInputMethodEvent::TextFormat,
+ qFixedLength, text.length()-qFixedLength,
+ qt_mac_compose_format());
+ QInputMethodEvent e(text, attrs);
+ context->currentText = text;
+ e.setCommitString(text.left(qFixedLength), 0, qFixedLength);
+ qt_sendSpontaneousEvent(widget, &e);
+ handled_event = true;
+ } else {
+ /* Apple's enums that they have removed from Tiger :(
+ enum {
+ kCaretPosition = 1,
+ kRawText = 2,
+ kSelectedRawText = 3,
+ kConvertedText = 4,
+ kSelectedConvertedText = 5,
+ kBlockFillText = 6,
+ kOutlineText = 7,
+ kSelectedText = 8
+ };
+ */
+#ifndef kConvertedText
+#define kConvertedText 4
+#endif
+#ifndef kCaretPosition
+#define kCaretPosition 1
+#endif
+ QList<QInputMethodEvent::Attribute> attrs;
+ if (!highlight.isEmpty()) {
+ TextRangeArray *data = highlight.data();
+ for (int i = 0; i < data->fNumOfRanges; ++i) {
+ int start = data->fRange[i].fStart / sizeof(UniChar);
+ int len = (data->fRange[i].fEnd - data->fRange[i].fStart) / sizeof(UniChar);
+ if (data->fRange[i].fHiliteStyle == kCaretPosition) {
+ attrs << QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, start, 0, QVariant());
+ continue;
+ }
+ QTextCharFormat format;
+ format.setFontUnderline(true);
+ if (data->fRange[i].fHiliteStyle == kConvertedText)
+ format.setUnderlineColor(Qt::gray);
+ else
+ format.setUnderlineColor(Qt::black);
+ attrs << QInputMethodEvent::Attribute(QInputMethodEvent::TextFormat, start, len, format);
+ }
+ } else {
+ attrs << QInputMethodEvent::Attribute(QInputMethodEvent::TextFormat,
+ 0, text.length(), qt_mac_compose_format());
+ }
+ context->currentText = text;
+ QInputMethodEvent e(text, attrs);
+ qt_sendSpontaneousEvent(widget, &e);
+ handled_event = true;
+ }
+ }
+#if 0
+ if(!context->composing)
+ handled_event = false;
+#endif
+
+ extern bool qt_mac_eat_unicode_key; //qapplication_mac.cpp
+ qt_mac_eat_unicode_key = handled_event;
+ } else if(ekind == kEventTextInputUnicodeForKeyEvent) {
+ EventRef key_ev = 0;
+ GetEventParameter(event, kEventParamTextInputSendKeyboardEvent, typeEventRef, 0,
+ sizeof(key_ev), 0, &key_ev);
+ QString text;
+ ByteCount unilen = 0;
+ if(GetEventParameter(key_ev, kEventParamKeyUnicodes, typeUnicodeText, 0, 0, &unilen, 0) == noErr) {
+ UniChar *unicode = (UniChar*)NewPtr(unilen);
+ GetEventParameter(key_ev, kEventParamKeyUnicodes, typeUnicodeText, 0, unilen, 0, unicode);
+ text = QString((QChar*)unicode, unilen / sizeof(UniChar));
+ DisposePtr((char*)unicode);
+ }
+ unsigned char chr = 0;
+ GetEventParameter(key_ev, kEventParamKeyMacCharCodes, typeChar, 0, sizeof(chr), 0, &chr);
+ if(!chr || chr >= 128 || (text.length() > 0 && (text.length() > 1 || text.at(0) != QLatin1Char(chr))))
+ handled_event = !widget->testAttribute(Qt::WA_InputMethodEnabled);
+ }
+ break; }
+ default:
+ break;
+ }
+ if(!handled_event) //let the event go through
+ return eventNotHandledErr;
+#endif
+ return noErr; //we eat the event
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/inputmethod/qmacinputcontext_p.h b/src/gui/inputmethod/qmacinputcontext_p.h
new file mode 100644
index 0000000000..f708040b66
--- /dev/null
+++ b/src/gui/inputmethod/qmacinputcontext_p.h
@@ -0,0 +1,92 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QMACINPUTCONTEXT_P_H
+#define QMACINPUTCONTEXT_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 "QtGui/qinputcontext.h"
+#include "private/qt_mac_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class Q_GUI_EXPORT QMacInputContext : public QInputContext
+{
+ Q_OBJECT
+ //Q_DECLARE_PRIVATE(QMacInputContext)
+ void createTextDocument();
+public:
+ explicit QMacInputContext(QObject* parent = 0);
+ virtual ~QMacInputContext();
+
+ virtual void setFocusWidget(QWidget *w);
+ virtual QString identifierName() { return QLatin1String("mac"); }
+ virtual QString language();
+
+ virtual void reset();
+
+ virtual bool isComposing() const;
+
+ static OSStatus globalEventProcessor(EventHandlerCallRef, EventRef, void *);
+ static void initialize();
+ static void cleanup();
+protected:
+ void mouseHandler(int pos, QMouseEvent *);
+private:
+ bool composing;
+ bool recursionGuard;
+ TSMDocumentID textDocument;
+ QString currentText;
+};
+
+QT_END_NAMESPACE
+
+#endif // QMACINPUTCONTEXT_P_H
diff --git a/src/gui/inputmethod/qwininputcontext_p.h b/src/gui/inputmethod/qwininputcontext_p.h
new file mode 100644
index 0000000000..38d7e328cd
--- /dev/null
+++ b/src/gui/inputmethod/qwininputcontext_p.h
@@ -0,0 +1,96 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QWININPUTCONTEXT_P_H
+#define QWININPUTCONTEXT_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of qinputcontext.cpp. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "QtGui/qinputcontext.h"
+#include "QtCore/qt_windows.h"
+
+QT_BEGIN_NAMESPACE
+
+class QWinInputContext : public QInputContext
+{
+ Q_OBJECT
+public:
+ explicit QWinInputContext(QObject* parent = 0);
+ virtual ~QWinInputContext();
+
+ virtual QString identifierName() { return QLatin1String("win"); }
+ virtual QString language();
+
+ virtual void reset();
+ virtual void update();
+
+ virtual void mouseHandler(int x, QMouseEvent *event);
+ virtual bool isComposing() const;
+
+ virtual void setFocusWidget(QWidget *w);
+
+ bool startComposition();
+ bool endComposition();
+ bool composition(LPARAM lparam);
+
+ static void TranslateMessage(const MSG *msg);
+ static LRESULT DefWindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
+
+ static void enablePopupChild(QWidget *w, bool e);
+ static void enable(QWidget *w, bool e);
+
+private:
+ void init();
+ bool recursionGuard;
+};
+
+QT_END_NAMESPACE
+
+#endif // QWININPUTCONTEXT_P_H
diff --git a/src/gui/inputmethod/qwininputcontext_win.cpp b/src/gui/inputmethod/qwininputcontext_win.cpp
new file mode 100644
index 0000000000..720f0b8866
--- /dev/null
+++ b/src/gui/inputmethod/qwininputcontext_win.cpp
@@ -0,0 +1,861 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qwininputcontext_p.h"
+#include "qinputcontext_p.h"
+
+#include "qfont.h"
+#include "qwidget.h"
+#include "qapplication.h"
+#include "qlibrary.h"
+#include "qevent.h"
+#include "qtextformat.h"
+
+//#define Q_IME_DEBUG
+
+/* Active Input method support on Win95/98/NT */
+#include <objbase.h>
+#include <initguid.h>
+
+#ifdef Q_IME_DEBUG
+#include "qdebug.h"
+#endif
+
+QT_BEGIN_NAMESPACE
+
+extern bool qt_sendSpontaneousEvent(QObject*, QEvent*);
+#if defined(Q_OS_WINCE)
+extern void qt_wince_show_SIP(bool show); // defined in qguifunctions_wince.cpp
+#endif
+
+DEFINE_GUID(IID_IActiveIMMApp,
+0x08c0e040, 0x62d1, 0x11d1, 0x93, 0x26, 0x0, 0x60, 0xb0, 0x67, 0xb8, 0x6e);
+
+
+
+DEFINE_GUID(CLSID_CActiveIMM,
+0x4955DD33, 0xB159, 0x11d0, 0x8F, 0xCF, 0x0, 0xAA, 0x00, 0x6B, 0xCC, 0x59);
+
+
+
+DEFINE_GUID(IID_IActiveIMMMessagePumpOwner,
+0xb5cf2cfa, 0x8aeb, 0x11d1, 0x93, 0x64, 0x0, 0x60, 0xb0, 0x67, 0xb8, 0x6e);
+
+
+
+interface IEnumRegisterWordW;
+interface IEnumInputContext;
+
+
+bool qt_sendSpontaneousEvent(QObject*, QEvent*);
+
+
+#define IFMETHOD HRESULT STDMETHODCALLTYPE
+
+interface IActiveIMMApp : public IUnknown
+{
+public:
+ virtual IFMETHOD AssociateContext(HWND hWnd, HIMC hIME, HIMC __RPC_FAR *phPrev) = 0;
+ virtual IFMETHOD dummy_ConfigureIMEA() = 0;
+ virtual IFMETHOD ConfigureIMEW(HKL hKL, HWND hWnd, DWORD dwMode, REGISTERWORDW __RPC_FAR *pData) = 0;
+ virtual IFMETHOD CreateContext(HIMC __RPC_FAR *phIMC) = 0;
+ virtual IFMETHOD DestroyContext(HIMC hIME) = 0;
+ virtual IFMETHOD dummy_EnumRegisterWordA() = 0;
+ virtual IFMETHOD EnumRegisterWordW(HKL hKL, LPWSTR szReading, DWORD dwStyle, LPWSTR szRegister, LPVOID pData,
+ IEnumRegisterWordW __RPC_FAR *__RPC_FAR *pEnum) = 0;
+ virtual IFMETHOD dummy_EscapeA() = 0;
+ virtual IFMETHOD EscapeW(HKL hKL, HIMC hIMC, UINT uEscape, LPVOID pData, LRESULT __RPC_FAR *plResult) = 0;
+ virtual IFMETHOD dummy_GetCandidateListA() = 0;
+ virtual IFMETHOD GetCandidateListW(HIMC hIMC, DWORD dwIndex, UINT uBufLen, CANDIDATELIST __RPC_FAR *pCandList,
+ UINT __RPC_FAR *puCopied) = 0;
+ virtual IFMETHOD dummy_GetCandidateListCountA() = 0;
+ virtual IFMETHOD GetCandidateListCountW(HIMC hIMC, DWORD __RPC_FAR *pdwListSize, DWORD __RPC_FAR *pdwBufLen) = 0;
+ virtual IFMETHOD GetCandidateWindow(HIMC hIMC, DWORD dwIndex, CANDIDATEFORM __RPC_FAR *pCandidate) = 0;
+ virtual IFMETHOD dummy_GetCompositionFontA() = 0;
+ virtual IFMETHOD GetCompositionFontW(HIMC hIMC, LOGFONTW __RPC_FAR *plf) = 0;
+ virtual IFMETHOD dummy_GetCompositionStringA() = 0;
+ virtual IFMETHOD GetCompositionStringW(HIMC hIMC, DWORD dwIndex, DWORD dwBufLen, LONG __RPC_FAR *plCopied, LPVOID pBuf) = 0;
+ virtual IFMETHOD GetCompositionWindow(HIMC hIMC, COMPOSITIONFORM __RPC_FAR *pCompForm) = 0;
+ virtual IFMETHOD GetContext(HWND hWnd, HIMC __RPC_FAR *phIMC) = 0;
+ virtual IFMETHOD dummy_GetConversionListA() = 0;
+ virtual IFMETHOD GetConversionListW(HKL hKL, HIMC hIMC, LPWSTR pSrc, UINT uBufLen, UINT uFlag,
+ CANDIDATELIST __RPC_FAR *pDst, UINT __RPC_FAR *puCopied) = 0;
+ virtual IFMETHOD GetConversionStatus(HIMC hIMC, DWORD __RPC_FAR *pfdwConversion, DWORD __RPC_FAR *pfdwSentence) = 0;
+ virtual IFMETHOD GetDefaultIMEWnd(HWND hWnd, HWND __RPC_FAR *phDefWnd) = 0;
+ virtual IFMETHOD dummy_GetDescriptionA() = 0;
+ virtual IFMETHOD GetDescriptionW(HKL hKL, UINT uBufLen, LPWSTR szDescription, UINT __RPC_FAR *puCopied) = 0;
+ virtual IFMETHOD dummy_GetGuideLineA() = 0;
+ virtual IFMETHOD GetGuideLineW(HIMC hIMC, DWORD dwIndex, DWORD dwBufLen, LPWSTR pBuf, DWORD __RPC_FAR *pdwResult) = 0;
+ virtual IFMETHOD dummy_GetIMEFileNameA() = 0;
+ virtual IFMETHOD GetIMEFileNameW(HKL hKL, UINT uBufLen, LPWSTR szFileName, UINT __RPC_FAR *puCopied) = 0;
+ virtual IFMETHOD GetOpenStatus(HIMC hIMC) = 0;
+ virtual IFMETHOD GetProperty(HKL hKL, DWORD fdwIndex, DWORD __RPC_FAR *pdwProperty) = 0;
+ virtual IFMETHOD dummy_GetRegisterWordStyleA() = 0;
+ virtual IFMETHOD GetRegisterWordStyleW(HKL hKL, UINT nItem, STYLEBUFW __RPC_FAR *pStyleBuf, UINT __RPC_FAR *puCopied) = 0;
+ virtual IFMETHOD GetStatusWindowPos(HIMC hIMC, POINT __RPC_FAR *pptPos) = 0;
+ virtual IFMETHOD GetVirtualKey(HWND hWnd, UINT __RPC_FAR *puVirtualKey) = 0;
+ virtual IFMETHOD dummy_InstallIMEA() = 0;
+ virtual IFMETHOD InstallIMEW(LPWSTR szIMEFileName, LPWSTR szLayoutText, HKL __RPC_FAR *phKL) = 0;
+ virtual IFMETHOD IsIME(HKL hKL) = 0;
+ virtual IFMETHOD dummy_IsUIMessageA() = 0;
+ virtual IFMETHOD IsUIMessageW(HWND hWndIME, UINT msg, WPARAM wParam, LPARAM lParam) = 0;
+ virtual IFMETHOD NotifyIME(HIMC hIMC, DWORD dwAction, DWORD dwIndex, DWORD dwValue) = 0;
+ virtual IFMETHOD dummy_RegisterWordA() = 0;
+ virtual IFMETHOD RegisterWordW(HKL hKL, LPWSTR szReading, DWORD dwStyle, LPWSTR szRegister) = 0;
+ virtual IFMETHOD ReleaseContext(HWND hWnd, HIMC hIMC) = 0;
+ virtual IFMETHOD SetCandidateWindow(HIMC hIMC, CANDIDATEFORM __RPC_FAR *pCandidate) = 0;
+ virtual IFMETHOD SetCompositionFontA(HIMC hIMC, LOGFONTA __RPC_FAR *plf) = 0;
+ virtual IFMETHOD SetCompositionFontW(HIMC hIMC, LOGFONTW __RPC_FAR *plf) = 0;
+ virtual IFMETHOD dummy_SetCompositionStringA() = 0;
+ virtual IFMETHOD SetCompositionStringW(HIMC hIMC, DWORD dwIndex, LPVOID pComp, DWORD dwCompLen,
+ LPVOID pRead, DWORD dwReadLen) = 0;
+ virtual IFMETHOD SetCompositionWindow(HIMC hIMC, COMPOSITIONFORM __RPC_FAR *pCompForm) = 0;
+ virtual IFMETHOD SetConversionStatus(HIMC hIMC, DWORD fdwConversion, DWORD fdwSentence) = 0;
+ virtual IFMETHOD SetOpenStatus(HIMC hIMC, BOOL fOpen) = 0;
+ virtual IFMETHOD SetStatusWindowPos(HIMC hIMC, POINT __RPC_FAR *pptPos) = 0;
+ virtual IFMETHOD SimulateHotKey(HWND hWnd, DWORD dwHotKeyID) = 0;
+ virtual IFMETHOD dummy_UnregisterWordA() = 0;
+ virtual IFMETHOD UnregisterWordW(HKL hKL, LPWSTR szReading, DWORD dwStyle, LPWSTR szUnregister) = 0;
+ virtual IFMETHOD Activate(BOOL fRestoreLayout) = 0;
+ virtual IFMETHOD Deactivate(void) = 0;
+ virtual IFMETHOD OnDefWindowProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam, LRESULT __RPC_FAR *plResult) = 0;
+ virtual IFMETHOD FilterClientWindows(ATOM __RPC_FAR *aaClassList, UINT uSize) = 0;
+ virtual IFMETHOD dummy_GetCodePageA() = 0;
+ virtual IFMETHOD GetLangId(HKL hKL, LANGID __RPC_FAR *plid) = 0;
+ virtual IFMETHOD AssociateContextEx(HWND hWnd, HIMC hIMC, DWORD dwFlags) = 0;
+ virtual IFMETHOD DisableIME(DWORD idThread) = 0;
+ virtual IFMETHOD dummy_GetImeMenuItemsA() = 0;
+ virtual IFMETHOD GetImeMenuItemsW(HIMC hIMC, DWORD dwFlags, DWORD dwType, /*IMEMENUITEMINFOW*/ void __RPC_FAR *pImeParentMenu,
+ /*IMEMENUITEMINFOW*/ void __RPC_FAR *pImeMenu, DWORD dwSize, DWORD __RPC_FAR *pdwResult) = 0;
+ virtual IFMETHOD EnumInputContext(DWORD idThread, IEnumInputContext __RPC_FAR *__RPC_FAR *ppEnum) = 0;
+};
+
+interface IActiveIMMMessagePumpOwner : public IUnknown
+{
+public:
+ virtual IFMETHOD Start(void) = 0;
+ virtual IFMETHOD End(void) = 0;
+ virtual IFMETHOD OnTranslateMessage(const MSG __RPC_FAR *pMsg) = 0;
+ virtual IFMETHOD Pause(DWORD __RPC_FAR *pdwCookie) = 0;
+ virtual IFMETHOD Resume(DWORD dwCookie) = 0;
+};
+
+
+static IActiveIMMApp *aimm = 0;
+static IActiveIMMMessagePumpOwner *aimmpump = 0;
+static QString *imeComposition = 0;
+static int imePosition = -1;
+bool qt_use_rtl_extensions = false;
+static bool haveCaret = false;
+
+#ifndef LGRPID_INSTALLED
+#define LGRPID_INSTALLED 0x00000001 // installed language group ids
+#define LGRPID_SUPPORTED 0x00000002 // supported language group ids
+#endif
+
+#ifndef LGRPID_ARABIC
+#define LGRPID_WESTERN_EUROPE 0x0001 // Western Europe & U.S.
+#define LGRPID_CENTRAL_EUROPE 0x0002 // Central Europe
+#define LGRPID_BALTIC 0x0003 // Baltic
+#define LGRPID_GREEK 0x0004 // Greek
+#define LGRPID_CYRILLIC 0x0005 // Cyrillic
+#define LGRPID_TURKISH 0x0006 // Turkish
+#define LGRPID_JAPANESE 0x0007 // Japanese
+#define LGRPID_KOREAN 0x0008 // Korean
+#define LGRPID_TRADITIONAL_CHINESE 0x0009 // Traditional Chinese
+#define LGRPID_SIMPLIFIED_CHINESE 0x000a // Simplified Chinese
+#define LGRPID_THAI 0x000b // Thai
+#define LGRPID_HEBREW 0x000c // Hebrew
+#define LGRPID_ARABIC 0x000d // Arabic
+#define LGRPID_VIETNAMESE 0x000e // Vietnamese
+#define LGRPID_INDIC 0x000f // Indic
+#define LGRPID_GEORGIAN 0x0010 // Georgian
+#define LGRPID_ARMENIAN 0x0011 // Armenian
+#endif
+
+static DWORD WM_MSIME_MOUSE = 0;
+
+QWinInputContext::QWinInputContext(QObject *parent)
+ : QInputContext(parent), recursionGuard(false)
+{
+ if (QSysInfo::WindowsVersion < QSysInfo::WV_2000) {
+ // try to get the Active IMM COM object on Win95/98/NT, where english versions don't
+ // support the regular Windows input methods.
+ if (CoCreateInstance(CLSID_CActiveIMM, NULL, CLSCTX_INPROC_SERVER,
+ IID_IActiveIMMApp, (LPVOID *)&aimm) != S_OK) {
+ aimm = 0;
+ }
+ if (aimm && (aimm->QueryInterface(IID_IActiveIMMMessagePumpOwner, (LPVOID *)&aimmpump) != S_OK ||
+ aimm->Activate(true) != S_OK)) {
+ aimm->Release();
+ aimm = 0;
+ if (aimmpump)
+ aimmpump->Release();
+ aimmpump = 0;
+ }
+ if (aimmpump)
+ aimmpump->Start();
+ }
+
+#ifndef Q_OS_WINCE
+ QSysInfo::WinVersion ver = QSysInfo::windowsVersion();
+ if (ver & QSysInfo::WV_NT_based && ver >= QSysInfo::WV_VISTA) {
+ // Since the IsValidLanguageGroup/IsValidLocale functions always return true on
+ // Vista, check the Keyboard Layouts for enabling RTL.
+ UINT nLayouts = GetKeyboardLayoutList(0, 0);
+ if (nLayouts) {
+ HKL *lpList = new HKL[nLayouts];
+ GetKeyboardLayoutList(nLayouts, lpList);
+ for (int i = 0; i<(int)nLayouts; i++) {
+ WORD plangid = PRIMARYLANGID((quintptr)lpList[i]);
+ if (plangid == LANG_ARABIC
+ || plangid == LANG_HEBREW
+ || plangid == LANG_FARSI
+#ifdef LANG_SYRIAC
+ || plangid == LANG_SYRIAC
+#endif
+ ) {
+ qt_use_rtl_extensions = true;
+ break;
+ }
+ }
+ delete []lpList;
+ }
+ } else {
+ // figure out whether a RTL language is installed
+ typedef BOOL(WINAPI *PtrIsValidLanguageGroup)(DWORD,DWORD);
+ PtrIsValidLanguageGroup isValidLanguageGroup = (PtrIsValidLanguageGroup)QLibrary::resolve(QLatin1String("kernel32"), "IsValidLanguageGroup");
+ if (isValidLanguageGroup) {
+ qt_use_rtl_extensions = isValidLanguageGroup(LGRPID_ARABIC, LGRPID_INSTALLED)
+ || isValidLanguageGroup(LGRPID_HEBREW, LGRPID_INSTALLED);
+ }
+ qt_use_rtl_extensions |= IsValidLocale(MAKELCID(MAKELANGID(LANG_ARABIC, SUBLANG_DEFAULT), SORT_DEFAULT), LCID_INSTALLED)
+ || IsValidLocale(MAKELCID(MAKELANGID(LANG_HEBREW, SUBLANG_DEFAULT), SORT_DEFAULT), LCID_INSTALLED)
+#ifdef LANG_SYRIAC
+ || IsValidLocale(MAKELCID(MAKELANGID(LANG_SYRIAC, SUBLANG_DEFAULT), SORT_DEFAULT), LCID_INSTALLED)
+#endif
+ || IsValidLocale(MAKELCID(MAKELANGID(LANG_FARSI, SUBLANG_DEFAULT), SORT_DEFAULT), LCID_INSTALLED);
+ }
+#else
+ qt_use_rtl_extensions = false;
+#endif
+
+ WM_MSIME_MOUSE = QT_WA_INLINE(RegisterWindowMessage(L"MSIMEMouseOperation"), RegisterWindowMessageA("MSIMEMouseOperation"));
+}
+
+QWinInputContext::~QWinInputContext()
+{
+ // release active input method if we have one
+ if (aimm) {
+ aimmpump->End();
+ aimmpump->Release();
+ aimm->Deactivate();
+ aimm->Release();
+ aimm = 0;
+ aimmpump = 0;
+ }
+ delete imeComposition;
+ imeComposition = 0;
+}
+
+static HWND getDefaultIMEWnd(HWND wnd)
+{
+ HWND ime_wnd;
+ if(aimm)
+ aimm->GetDefaultIMEWnd(wnd, &ime_wnd);
+ else
+ ime_wnd = ImmGetDefaultIMEWnd(wnd);
+ return ime_wnd;
+}
+
+static HIMC getContext(HWND wnd)
+{
+ HIMC imc;
+ if (aimm)
+ aimm->GetContext(wnd, &imc);
+ else
+ imc = ImmGetContext(wnd);
+
+ return imc;
+}
+
+static void releaseContext(HWND wnd, HIMC imc)
+{
+ if (aimm)
+ aimm->ReleaseContext(wnd, imc);
+ else
+ ImmReleaseContext(wnd, imc);
+}
+
+static void notifyIME(HIMC imc, DWORD dwAction, DWORD dwIndex, DWORD dwValue)
+{
+ if (!imc)
+ return;
+ if (aimm)
+ aimm->NotifyIME(imc, dwAction, dwIndex, dwValue);
+ else
+ ImmNotifyIME(imc, dwAction, dwIndex, dwValue);
+}
+
+static LONG getCompositionString(HIMC himc, DWORD dwIndex, LPVOID lpbuf, DWORD dBufLen, bool *unicode = 0)
+{
+ LONG len = 0;
+ if (unicode)
+ *unicode = true;
+ if (aimm)
+ aimm->GetCompositionStringW(himc, dwIndex, dBufLen, &len, lpbuf);
+ else
+ {
+ if(QSysInfo::WindowsVersion != QSysInfo::WV_95) {
+ len = ImmGetCompositionStringW(himc, dwIndex, lpbuf, dBufLen);
+ }
+#if !defined(Q_OS_WINCE)
+ else {
+ len = ImmGetCompositionStringA(himc, dwIndex, lpbuf, dBufLen);
+ if (unicode)
+ *unicode = false;
+ }
+#endif
+ }
+ return len;
+}
+
+static int getCursorPosition(HIMC himc)
+{
+ return getCompositionString(himc, GCS_CURSORPOS, 0, 0);
+}
+
+static QString getString(HIMC himc, DWORD dwindex, int *selStart = 0, int *selLength = 0)
+{
+ static char *buffer = 0;
+ static int buflen = 0;
+
+ int len = getCompositionString(himc, dwindex, 0, 0) + 1;
+ if (!buffer || len > buflen) {
+ delete [] buffer;
+ buflen = qMin(len, 256);
+ buffer = new char[buflen];
+ }
+
+ bool unicode = true;
+ len = getCompositionString(himc, dwindex, buffer, buflen, &unicode);
+
+ if (selStart) {
+ static char *attrbuffer = 0;
+ static int attrbuflen = 0;
+ int attrlen = getCompositionString(himc, dwindex, 0, 0) + 1;
+ if (!attrbuffer || attrlen> attrbuflen) {
+ delete [] attrbuffer;
+ attrbuflen = qMin(attrlen, 256);
+ attrbuffer = new char[attrbuflen];
+ }
+ attrlen = getCompositionString(himc, GCS_COMPATTR, attrbuffer, attrbuflen);
+ *selStart = attrlen+1;
+ *selLength = -1;
+ for (int i = 0; i < attrlen; i++) {
+ if (attrbuffer[i] & ATTR_TARGET_CONVERTED) {
+ *selStart = qMin(*selStart, i);
+ *selLength = qMax(*selLength, i);
+ }
+ }
+ *selLength = qMax(0, *selLength - *selStart + 1);
+ }
+
+ if (len <= 0)
+ return QString();
+ if (unicode) {
+ return QString((QChar *)buffer, len/sizeof(QChar));
+ }
+ else {
+ buffer[len] = 0;
+ WCHAR *wc = new WCHAR[len+1];
+ int l = MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED,
+ buffer, len, wc, len+1);
+ QString res = QString((QChar *)wc, l);
+ delete [] wc;
+ return res;
+ }
+}
+
+void QWinInputContext::TranslateMessage(const MSG *msg)
+{
+ if (!aimmpump || aimmpump->OnTranslateMessage(msg) != S_OK)
+ ::TranslateMessage(msg);
+}
+
+LRESULT QWinInputContext::DefWindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ LRESULT retval;
+ if (!aimm || aimm->OnDefWindowProc(hwnd, msg, wParam, lParam, &retval) != S_OK)
+ {
+ QT_WA({
+ retval = ::DefWindowProc(hwnd, msg, wParam, lParam);
+ } , {
+ retval = ::DefWindowProcA(hwnd,msg, wParam, lParam);
+ });
+ }
+ return retval;
+}
+
+
+void QWinInputContext::update()
+{
+ QWidget *w = focusWidget();
+ if(!w)
+ return;
+
+ Q_ASSERT(w->testAttribute(Qt::WA_WState_Created));
+ HIMC imc = getContext(w->effectiveWinId());
+
+ if (!imc)
+ return;
+
+ QFont f = qvariant_cast<QFont>(w->inputMethodQuery(Qt::ImFont));
+ HFONT hf;
+ hf = f.handle();
+
+ QT_WA({
+ LOGFONT lf;
+ if (GetObject(hf, sizeof(lf), &lf))
+ if (aimm)
+ aimm->SetCompositionFontW(imc, &lf);
+ else
+ ImmSetCompositionFont(imc, &lf);
+ } , {
+ LOGFONTA lf;
+ if (GetObjectA(hf, sizeof(lf), &lf))
+ if (aimm)
+ aimm->SetCompositionFontA(imc, &lf);
+ else
+ ImmSetCompositionFontA(imc, &lf);
+ });
+
+ QRect r = w->inputMethodQuery(Qt::ImMicroFocus).toRect();
+
+ // The ime window positions are based on the WinId with active focus.
+ QWidget *imeWnd = QWidget::find(::GetFocus());
+ if (imeWnd && !aimm) {
+ QPoint pt (r.topLeft());
+ pt = w->mapToGlobal(pt);
+ pt = imeWnd->mapFromGlobal(pt);
+ r.moveTo(pt);
+ }
+
+ COMPOSITIONFORM cf;
+ // ### need X-like inputStyle config settings
+ cf.dwStyle = CFS_FORCE_POSITION;
+ cf.ptCurrentPos.x = r.x();
+ cf.ptCurrentPos.y = r.y();
+
+ CANDIDATEFORM candf;
+ candf.dwIndex = 0;
+ candf.dwStyle = CFS_EXCLUDE;
+ candf.ptCurrentPos.x = r.x();
+ candf.ptCurrentPos.y = r.y() + r.height();
+ candf.rcArea.left = r.x();
+ candf.rcArea.top = r.y();
+ candf.rcArea.right = r.x() + r.width();
+ candf.rcArea.bottom = r.y() + r.height();
+
+ if(haveCaret)
+ SetCaretPos(r.x(), r.y());
+
+ if (aimm) {
+ aimm->SetCompositionWindow(imc, &cf);
+ aimm->SetCandidateWindow(imc, &candf);
+ } else {
+ ImmSetCompositionWindow(imc, &cf);
+ ImmSetCandidateWindow(imc, &candf);
+ }
+
+ releaseContext(w->effectiveWinId(), imc);
+}
+
+
+bool QWinInputContext::endComposition()
+{
+ QWidget *fw = focusWidget();
+#ifdef Q_IME_DEBUG
+ qDebug("endComposition! fw = %s", fw ? fw->className() : "(null)");
+#endif
+ bool result = true;
+ if(imePosition == -1 || recursionGuard)
+ return result;
+
+ // Googles Pinyin Input Method likes to call endComposition again
+ // when we call notifyIME with CPS_CANCEL, so protect ourselves
+ // against that.
+ recursionGuard = true;
+
+ if (fw) {
+ Q_ASSERT(fw->testAttribute(Qt::WA_WState_Created));
+ HIMC imc = getContext(fw->effectiveWinId());
+ notifyIME(imc, NI_COMPOSITIONSTR, CPS_CANCEL, 0);
+ releaseContext(fw->effectiveWinId(), imc);
+ if(haveCaret) {
+ DestroyCaret();
+ haveCaret = false;
+ }
+ }
+
+ if (!fw)
+ fw = qApp->focusWidget();
+
+ if (fw) {
+ QInputMethodEvent e;
+ result = qt_sendSpontaneousEvent(fw, &e);
+ }
+
+ if (imeComposition)
+ imeComposition->clear();
+ imePosition = -1;
+
+ recursionGuard = false;
+
+ return result;
+}
+
+void QWinInputContext::reset()
+{
+ QWidget *fw = focusWidget();
+
+#ifdef Q_IME_DEBUG
+ qDebug("sending accept to focus widget %s", fw ? fw->className() : "(null)");
+#endif
+
+ if (fw && imePosition != -1) {
+ QInputMethodEvent e;
+ if (imeComposition)
+ e.setCommitString(*imeComposition);
+ imePosition = -1;
+ qt_sendSpontaneousEvent(fw, &e);
+ }
+
+ if (imeComposition)
+ imeComposition->clear();
+ imePosition = -1;
+
+ if (fw) {
+ Q_ASSERT(fw->testAttribute(Qt::WA_WState_Created));
+ HIMC imc = getContext(fw->effectiveWinId());
+ notifyIME(imc, NI_COMPOSITIONSTR, CPS_CANCEL, 0);
+ releaseContext(fw->effectiveWinId(), imc);
+ }
+
+}
+
+
+bool QWinInputContext::startComposition()
+{
+#ifdef Q_IME_DEBUG
+ qDebug("startComposition");
+#endif
+
+ if (!imeComposition)
+ imeComposition = new QString();
+
+ QWidget *fw = focusWidget();
+ if (fw) {
+ Q_ASSERT(fw->testAttribute(Qt::WA_WState_Created));
+ imePosition = 0;
+ haveCaret = CreateCaret(fw->effectiveWinId(), 0, 1, 1);
+ HideCaret(fw->effectiveWinId());
+ update();
+ }
+ return fw != 0;
+}
+
+enum StandardFormat {
+ PreeditFormat,
+ SelectionFormat
+};
+
+bool QWinInputContext::composition(LPARAM lParam)
+{
+#ifdef Q_IME_DEBUG
+ QString str;
+ if (lParam & GCS_RESULTSTR)
+ str += "RESULTSTR ";
+ if (lParam & GCS_COMPSTR)
+ str += "COMPSTR ";
+ if (lParam & GCS_COMPATTR)
+ str += "COMPATTR ";
+ if (lParam & GCS_CURSORPOS)
+ str += "CURSORPOS ";
+ if (lParam & GCS_COMPCLAUSE)
+ str += "COMPCLAUSE ";
+ if (lParam & CS_INSERTCHAR)
+ str += "INSERTCHAR ";
+ if (lParam & CS_NOMOVECARET)
+ str += "NOMOVECARET ";
+ qDebug("composition, lParam=(%x) %s imePosition=%d", lParam, str.latin1(), imePosition);
+#endif
+
+ bool result = true;
+
+ if(!lParam)
+ // bogus event
+ return true;
+
+ QWidget *fw = qApp->focusWidget();
+ if (fw) {
+ Q_ASSERT(fw->testAttribute(Qt::WA_WState_Created));
+ HIMC imc = getContext(fw->effectiveWinId());
+ QInputMethodEvent e;
+ if (lParam & (GCS_COMPSTR | GCS_COMPATTR | GCS_CURSORPOS)) {
+ if (imePosition == -1)
+ // need to send a start event
+ startComposition();
+
+ // some intermediate composition result
+ int selStart, selLength;
+ *imeComposition = getString(imc, GCS_COMPSTR, &selStart, &selLength);
+ imePosition = getCursorPosition(imc);
+ if (lParam & CS_INSERTCHAR && lParam & CS_NOMOVECARET) {
+ // make korean work correctly. Hope this is correct for all IMEs
+ selStart = 0;
+ selLength = imeComposition->length();
+ }
+ if(selLength == 0)
+ selStart = 0;
+
+ QList<QInputMethodEvent::Attribute> attrs;
+ if (selStart > 0)
+ attrs << QInputMethodEvent::Attribute(QInputMethodEvent::TextFormat, 0, selStart,
+ standardFormat(PreeditFormat));
+ if (selLength)
+ attrs << QInputMethodEvent::Attribute(QInputMethodEvent::TextFormat, selStart, selLength,
+ standardFormat(SelectionFormat));
+ if (selStart + selLength < imeComposition->length())
+ attrs << QInputMethodEvent::Attribute(QInputMethodEvent::TextFormat, selStart + selLength,
+ imeComposition->length() - selStart - selLength,
+ standardFormat(PreeditFormat));
+ if(imePosition >= 0)
+ attrs << QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, imePosition, selLength ? 0 : 1, QVariant());
+
+ e = QInputMethodEvent(*imeComposition, attrs);
+ }
+ if (lParam & GCS_RESULTSTR) {
+ if(imePosition == -1)
+ startComposition();
+ // a fixed result, return the converted string
+ *imeComposition = getString(imc, GCS_RESULTSTR);
+ imePosition = -1;
+ e.setCommitString(*imeComposition);
+ imeComposition->clear();
+ }
+ result = qt_sendSpontaneousEvent(fw, &e);
+ update();
+ releaseContext(fw->effectiveWinId(), imc);
+ }
+#ifdef Q_IME_DEBUG
+ qDebug("imecomposition: cursor pos at %d, str=%x", imePosition, str[0].unicode());
+#endif
+ return result;
+}
+
+static HIMC defaultContext = 0;
+
+// checks whether widget is a popup
+inline bool isPopup(QWidget *w)
+{
+ if (w && (w->windowFlags() & Qt::Popup) == Qt::Popup)
+ return true;
+ else
+ return false;
+}
+// checks whether widget is in a popup
+inline bool isInPopup(QWidget *w)
+{
+ if (w && (isPopup(w) || isPopup(w->window())))
+ return true;
+ else
+ return false;
+}
+
+// find the parent widget, which is a non popup toplevel
+// this is valid only if the widget is/in a popup
+inline QWidget *findParentforPopup(QWidget *w)
+{
+ QWidget *e = QWidget::find(w->effectiveWinId());
+ // check if this or its parent is a popup
+ while (isInPopup(e)) {
+ e = e->window()->parentWidget();
+ if (!e)
+ break;
+ e = QWidget::find(e->effectiveWinId());
+ }
+ if (e)
+ return e->window();
+ else
+ return 0;
+}
+
+// enables or disables the ime
+inline void enableIme(QWidget *w, bool value)
+{
+ if (value) {
+ // enable ime
+ if (defaultContext)
+ ImmAssociateContext(w->effectiveWinId(), defaultContext);
+#ifdef Q_OS_WINCE
+ if (qApp->autoSipEnabled())
+ qt_wince_show_SIP(true);
+#endif
+ } else {
+ // disable ime
+ HIMC oldimc = ImmAssociateContext(w->effectiveWinId(), 0);
+ if (!defaultContext)
+ defaultContext = oldimc;
+#ifdef Q_OS_WINCE
+ if (qApp->autoSipEnabled())
+ qt_wince_show_SIP(false);
+#endif
+ }
+}
+
+
+void QInputContextPrivate::updateImeStatus(QWidget *w, bool hasFocus)
+{
+ if (!w)
+ return;
+ bool e = w->testAttribute(Qt::WA_InputMethodEnabled) && w->isEnabled();
+ bool hasIme = e && hasFocus;
+#ifdef Q_IME_DEBUG
+ qDebug("%s HasFocus = %d hasIme = %d e = %d ", w->className(), hasFocus, hasIme, e);
+#endif
+ if (hasFocus || e) {
+ if (isInPopup(w))
+ QWinInputContext::enablePopupChild(w, hasIme);
+ else
+ QWinInputContext::enable(w, hasIme);
+ }
+}
+
+void QWinInputContext::enablePopupChild(QWidget *w, bool e)
+{
+ if (aimm) {
+ enable(w, e);
+ return;
+ }
+
+ if (!w || !isInPopup(w))
+ return;
+#ifdef Q_IME_DEBUG
+ qDebug("enablePopupChild: w=%s, enable = %s", w ? w->className() : "(null)" , e ? "true" : "false");
+#endif
+ QWidget *parent = findParentforPopup(w);
+ if (parent) {
+ // update ime status of the normal toplevel parent of the popup
+ enableIme(parent, e);
+ }
+ QWidget *toplevel = w->window();
+ if (toplevel) {
+ // update ime status of the toplevel popup
+ enableIme(toplevel, e);
+ }
+}
+
+void QWinInputContext::enable(QWidget *w, bool e)
+{
+ if(w) {
+#ifdef Q_IME_DEBUG
+ qDebug("enable: w=%s, enable = %s", w ? w->className() : "(null)" , e ? "true" : "false");
+#endif
+ if (!w->testAttribute(Qt::WA_WState_Created))
+ return;
+ if(aimm) {
+ HIMC oldimc;
+ if (!e) {
+ aimm->AssociateContext(w->effectiveWinId(), 0, &oldimc);
+ if (!defaultContext)
+ defaultContext = oldimc;
+ } else if (defaultContext) {
+ aimm->AssociateContext(w->effectiveWinId(), defaultContext, &oldimc);
+ }
+ } else {
+ // update ime status on the widget
+ QWidget *p = QWidget::find(w->effectiveWinId());
+ if (p)
+ enableIme(p, e);
+ }
+ }
+}
+
+void QWinInputContext::setFocusWidget(QWidget *w)
+{
+ QInputContext::setFocusWidget(w);
+ update();
+}
+
+bool QWinInputContext::isComposing() const
+{
+ return imeComposition && !imeComposition->isEmpty();
+}
+
+void QWinInputContext::mouseHandler(int pos, QMouseEvent *e)
+{
+ if(e->type() != QEvent::MouseButtonPress)
+ return;
+
+ if (pos < 0 || pos > imeComposition->length())
+ reset();
+
+ // Probably should pass the correct button, but it seems to work fine like this.
+ DWORD button = MK_LBUTTON;
+
+ QWidget *fw = focusWidget();
+ if (fw) {
+ Q_ASSERT(fw->testAttribute(Qt::WA_WState_Created));
+ HIMC himc = getContext(fw->effectiveWinId());
+ HWND ime_wnd = getDefaultIMEWnd(fw->effectiveWinId());
+ SendMessage(ime_wnd, WM_MSIME_MOUSE, MAKELONG(MAKEWORD(button, pos == 0 ? 2 : 1), pos), (LPARAM)himc);
+ releaseContext(fw->effectiveWinId(), himc);
+ }
+ //qDebug("mouseHandler: got value %d pos=%d", ret,pos);
+}
+
+QString QWinInputContext::language()
+{
+ return QString();
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/inputmethod/qwsinputcontext_p.h b/src/gui/inputmethod/qwsinputcontext_p.h
new file mode 100644
index 0000000000..20811dae64
--- /dev/null
+++ b/src/gui/inputmethod/qwsinputcontext_p.h
@@ -0,0 +1,96 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QWSINPUTCONTEXT_P_H
+#define QWSINPUTCONTEXT_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of the QLibrary class. This header file may change from
+// version to version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "QtGui/qinputcontext.h"
+
+#ifndef QT_NO_QWS_INPUTMETHODS
+
+QT_BEGIN_NAMESPACE
+
+class QWSIMEvent;
+class QWSIMQueryEvent;
+class QWSIMInitEvent;
+
+class QWSInputContext : public QInputContext
+{
+ Q_OBJECT
+public:
+ explicit QWSInputContext(QObject* parent = 0);
+ ~QWSInputContext() {}
+
+
+ QString identifierName() { return QString(); }
+ QString language() { return QString(); }
+
+ void reset();
+ void update();
+ void mouseHandler( int x, QMouseEvent *event);
+
+ void setFocusWidget( QWidget *w );
+ void widgetDestroyed(QWidget *w);
+
+ bool isComposing() const;
+
+ static QWidget *activeWidget();
+ static bool translateIMEvent(QWidget *w, const QWSIMEvent *e);
+ static bool translateIMQueryEvent(QWidget *w, const QWSIMQueryEvent *e);
+ static bool translateIMInitEvent(const QWSIMInitEvent *e);
+};
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_QWS_INPUTMETHODS
+
+#endif // QWSINPUTCONTEXT_P_H
diff --git a/src/gui/inputmethod/qwsinputcontext_qws.cpp b/src/gui/inputmethod/qwsinputcontext_qws.cpp
new file mode 100644
index 0000000000..46ac13df53
--- /dev/null
+++ b/src/gui/inputmethod/qwsinputcontext_qws.cpp
@@ -0,0 +1,239 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qwsinputcontext_p.h"
+#include "qinputcontext_p.h"
+#include "qwsdisplay_qws.h"
+#include "qwsevent_qws.h"
+#include "private/qwscommand_qws_p.h"
+#include "qwindowsystem_qws.h"
+#include "qevent.h"
+#include "qtextformat.h"
+
+#include <qbuffer.h>
+
+#include <qdebug.h>
+
+#ifndef QT_NO_QWS_INPUTMETHODS
+
+QT_BEGIN_NAMESPACE
+
+static QWidget* activeWidget = 0;
+
+//#define EXTRA_DEBUG
+
+QWSInputContext::QWSInputContext(QObject *parent)
+ :QInputContext(parent)
+{
+}
+
+void QWSInputContext::reset()
+{
+ QPaintDevice::qwsDisplay()->resetIM();
+}
+
+
+void QWSInputContext::setFocusWidget( QWidget *w )
+{
+ QWidget *oldFocus = focusWidget();
+ if (oldFocus == w)
+ return;
+
+ if (oldFocus) {
+ QWidget *tlw = oldFocus->window();
+ int winid = tlw->internalWinId();
+
+ int widgetid = oldFocus->internalWinId();
+ QPaintDevice::qwsDisplay()->sendIMUpdate(QWSInputMethod::FocusOut, winid, widgetid);
+ }
+
+ QInputContext::setFocusWidget(w);
+
+ if (!w)
+ return;
+
+ QWidget *tlw = w->window();
+ int winid = tlw->winId();
+
+ int widgetid = w->winId();
+ QPaintDevice::qwsDisplay()->sendIMUpdate(QWSInputMethod::FocusIn, winid, widgetid);
+
+ //setfocus ???
+
+ update();
+}
+
+
+void QWSInputContext::widgetDestroyed(QWidget *w)
+{
+ if (w == ::activeWidget)
+ ::activeWidget = 0;
+ QInputContext::widgetDestroyed(w);
+}
+
+void QWSInputContext::update()
+{
+ QWidget *w = focusWidget();
+ if (!w)
+ return;
+
+ QWidget *tlw = w->window();
+ int winid = tlw->winId();
+
+ int widgetid = w->winId();
+ QPaintDevice::qwsDisplay()->sendIMUpdate(QWSInputMethod::Update, winid, widgetid);
+
+}
+
+void QWSInputContext::mouseHandler( int x, QMouseEvent *event)
+{
+ if (event->type() == QEvent::MouseButtonPress || event->type() == QEvent::MouseButtonRelease)
+ QPaintDevice::qwsDisplay()->sendIMMouseEvent( x, event->type() == QEvent::MouseButtonPress );
+}
+
+QWidget *QWSInputContext::activeWidget()
+{
+ return ::activeWidget;
+}
+
+
+bool QWSInputContext::isComposing() const
+{
+ return ::activeWidget != 0;
+}
+
+bool QWSInputContext::translateIMQueryEvent(QWidget *w, const QWSIMQueryEvent *e)
+{
+ Qt::InputMethodQuery type = static_cast<Qt::InputMethodQuery>(e->simpleData.property);
+ QVariant result = w->inputMethodQuery(type);
+ QWidget *tlw = w->window();
+ int winId = tlw->winId();
+
+ if ( type == Qt::ImMicroFocus ) {
+ // translate to relative to tlw
+ QRect mf = result.toRect();
+ mf.moveTopLeft(w->mapTo(tlw,mf.topLeft()));
+ result = mf;
+ }
+
+ QPaintDevice::qwsDisplay()->sendIMResponse(winId, e->simpleData.property, result);
+
+ return false;
+}
+
+bool QWSInputContext::translateIMInitEvent(const QWSIMInitEvent *e)
+{
+ Q_UNUSED(e);
+ qDebug("### QWSInputContext::translateIMInitEvent not implemented ###");
+ return false;
+}
+
+bool QWSInputContext::translateIMEvent(QWidget *w, const QWSIMEvent *e)
+{
+ QDataStream stream(e->streamingData);
+ QString preedit;
+ QString commit;
+
+ stream >> preedit;
+ stream >> commit;
+
+ if (preedit.isEmpty() && ::activeWidget)
+ w = ::activeWidget;
+
+ QInputContext *qic = w->inputContext();
+ if (!qic)
+ return false;
+
+ QList<QInputMethodEvent::Attribute> attrs;
+
+
+ while (!stream.atEnd()) {
+ int type = -1;
+ int start = -1;
+ int length = -1;
+ QVariant data;
+ stream >> type >> start >> length >> data;
+ if (stream.status() != QDataStream::Ok) {
+ qWarning("corrupted QWSIMEvent");
+ //qic->reset(); //???
+ return false;
+ }
+ if (type == QInputMethodEvent::TextFormat)
+ data = qic->standardFormat(static_cast<QInputContext::StandardFormat>(data.toInt()));
+ attrs << QInputMethodEvent::Attribute(static_cast<QInputMethodEvent::AttributeType>(type), start, length, data);
+ }
+#ifdef EXTRA_DEBUG
+ qDebug() << "preedit" << preedit << "len" << preedit.length() <<"commit" << commit << "len" << commit.length()
+ << "n attr" << attrs.count();
+#endif
+
+ if (preedit.isEmpty())
+ ::activeWidget = 0;
+ else
+ ::activeWidget = w;
+
+
+ QInputMethodEvent ime(preedit, attrs);
+ if (!commit.isEmpty() || e->simpleData.replaceLength > 0)
+ ime.setCommitString(commit, e->simpleData.replaceFrom, e->simpleData.replaceLength);
+
+
+ extern bool qt_sendSpontaneousEvent(QObject *, QEvent *); //qapplication_qws.cpp
+ qt_sendSpontaneousEvent(w, &ime);
+
+ return true;
+}
+
+Q_GUI_EXPORT void (*qt_qws_inputMethodStatusChanged)(QWidget*) = 0;
+
+void QInputContextPrivate::updateImeStatus(QWidget *w, bool hasFocus)
+{
+ Q_UNUSED(hasFocus);
+
+ if (!w || !qt_qws_inputMethodStatusChanged)
+ return;
+ qt_qws_inputMethodStatusChanged(w);
+}
+
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_QWS_INPUTMETHODS
diff --git a/src/gui/inputmethod/qximinputcontext_p.h b/src/gui/inputmethod/qximinputcontext_p.h
new file mode 100644
index 0000000000..ca2103be96
--- /dev/null
+++ b/src/gui/inputmethod/qximinputcontext_p.h
@@ -0,0 +1,141 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/****************************************************************************
+**
+** Definition of QXIMInputContext class
+**
+** Copyright (C) 2003-2004 immodule for Qt Project. All rights reserved.
+**
+** This file is written to contribute to Nokia Corporation and/or its subsidiary(-ies) under their own
+** license. You may use this file under your Qt license. Following
+** description is copied from their original file headers. Contact
+** immodule-qt@freedesktop.org if any conditions of this licensing are
+** not clear to you.
+**
+****************************************************************************/
+
+#ifndef QXIMINPUTCONTEXT_P_H
+#define QXIMINPUTCONTEXT_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.
+//
+
+#if !defined(Q_NO_IM)
+
+#include "QtCore/qglobal.h"
+#include "QtGui/qinputcontext.h"
+#include "QtGui/qfont.h"
+#include "QtCore/qhash.h"
+#ifdef Q_WS_X11
+#include "QtCore/qlist.h"
+#include "QtCore/qbitarray.h"
+#include "QtGui/qwindowdefs.h"
+#include "private/qt_x11_p.h"
+#endif
+
+QT_BEGIN_NAMESPACE
+
+class QKeyEvent;
+class QWidget;
+class QFont;
+class QString;
+
+class QXIMInputContext : public QInputContext
+{
+ Q_OBJECT
+public:
+ struct ICData {
+ XIC ic;
+ XFontSet fontset;
+ QWidget *widget;
+ QString text;
+ QBitArray selectedChars;
+ bool composing;
+ void clear();
+ };
+
+ QXIMInputContext();
+ ~QXIMInputContext();
+
+ QString identifierName();
+ QString language();
+
+ void reset();
+
+ void mouseHandler( int x, QMouseEvent *event);
+ bool isComposing() const;
+
+ void setFocusWidget( QWidget *w );
+ void widgetDestroyed(QWidget *w);
+
+ void create_xim();
+ void close_xim();
+
+ void update();
+
+ ICData *icData() const;
+protected:
+ bool x11FilterEvent( QWidget *keywidget, XEvent *event );
+
+private:
+ static XIMStyle xim_style;
+
+ QString _language;
+ XIM xim;
+ QHash<WId, ICData *> ximData;
+
+ ICData *createICData(QWidget *w);
+};
+
+QT_END_NAMESPACE
+
+#endif // Q_NO_IM
+
+#endif // QXIMINPUTCONTEXT_P_H
diff --git a/src/gui/inputmethod/qximinputcontext_x11.cpp b/src/gui/inputmethod/qximinputcontext_x11.cpp
new file mode 100644
index 0000000000..48a96b1b21
--- /dev/null
+++ b/src/gui/inputmethod/qximinputcontext_x11.cpp
@@ -0,0 +1,832 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/****************************************************************************
+**
+** Implementation of QXIMInputContext class
+**
+** Copyright (C) 2003-2004 immodule for Qt Project. All rights reserved.
+**
+** This file is written to contribute to Nokia Corporation and/or its subsidiary(-ies) under their own
+** license. You may use this file under your Qt license. Following
+** description is copied from their original file headers. Contact
+** immodule-qt@freedesktop.org if any conditions of this licensing are
+** not clear to you.
+**
+****************************************************************************/
+
+#include "qdebug.h"
+#include "qximinputcontext_p.h"
+
+#if !defined(QT_NO_IM)
+
+QT_BEGIN_NAMESPACE
+
+#if !defined(QT_NO_XIM)
+
+QT_BEGIN_INCLUDE_NAMESPACE
+#include "qplatformdefs.h"
+
+#include "qapplication.h"
+#include "qwidget.h"
+#include "qstring.h"
+#include "qlist.h"
+#include "qtextcodec.h"
+#include "qevent.h"
+#include "qtextformat.h"
+
+#include "qx11info_x11.h"
+
+#include <stdlib.h>
+#include <limits.h>
+QT_END_INCLUDE_NAMESPACE
+
+// #define QT_XIM_DEBUG
+#ifdef QT_XIM_DEBUG
+#define XIM_DEBUG qDebug
+#else
+#define XIM_DEBUG if (0) qDebug
+#endif
+
+// from qapplication_x11.cpp
+// #### move to X11 struct
+extern XIMStyle qt_xim_preferred_style;
+extern char *qt_ximServer;
+extern int qt_ximComposingKeycode;
+extern QTextCodec * qt_input_mapper;
+
+XIMStyle QXIMInputContext::xim_style = 0;
+// moved from qapplication_x11.cpp
+static const XIMStyle xim_default_style = XIMPreeditCallbacks | XIMStatusNothing;
+
+
+extern "C" {
+#ifdef USE_X11R6_XIM
+ static void xim_create_callback(XIM /*im*/,
+ XPointer client_data,
+ XPointer /*call_data*/)
+ {
+ QXIMInputContext *qic = reinterpret_cast<QXIMInputContext *>(client_data);
+ // qDebug("xim_create_callback");
+ qic->create_xim();
+ }
+
+ static void xim_destroy_callback(XIM /*im*/,
+ XPointer client_data,
+ XPointer /*call_data*/)
+ {
+ QXIMInputContext *qic = reinterpret_cast<QXIMInputContext *>(client_data);
+ // qDebug("xim_destroy_callback");
+ qic->close_xim();
+ XRegisterIMInstantiateCallback(X11->display, 0, 0, 0,
+ (XIMProc) xim_create_callback, reinterpret_cast<char *>(qic));
+ }
+#endif // USE_X11R6_XIM
+
+ static int xic_start_callback(XIC, XPointer client_data, XPointer) {
+ QXIMInputContext *qic = (QXIMInputContext *) client_data;
+ if (!qic) {
+ XIM_DEBUG("xic_start_callback: no qic");
+ return 0;
+ }
+ QXIMInputContext::ICData *data = qic->icData();
+ if (!data) {
+ XIM_DEBUG("xic_start_callback: no ic data");
+ return 0;
+ }
+ XIM_DEBUG("xic_start_callback");
+
+ data->clear();
+ data->composing = true;
+
+ return 0;
+ }
+
+ static int xic_draw_callback(XIC, XPointer client_data, XPointer call_data) {
+ QXIMInputContext *qic = (QXIMInputContext *) client_data;
+ if (!qic) {
+ XIM_DEBUG("xic_draw_callback: no qic");
+ return 0;
+ }
+ QXIMInputContext::ICData *data = qic->icData();
+ if (!data) {
+ XIM_DEBUG("xic_draw_callback: no ic data");
+ return 0;
+ }
+ XIM_DEBUG("xic_draw_callback");
+
+
+ if(!data->composing) {
+ data->clear();
+ data->composing = true;
+ }
+
+ XIMPreeditDrawCallbackStruct *drawstruct = (XIMPreeditDrawCallbackStruct *) call_data;
+ XIMText *text = (XIMText *) drawstruct->text;
+ int cursor = drawstruct->caret, sellen = 0, selstart = 0;
+
+ if (!drawstruct->caret && !drawstruct->chg_first && !drawstruct->chg_length && !text) {
+ if(data->text.isEmpty()) {
+ XIM_DEBUG("compose emptied");
+ // if the composition string has been emptied, we need
+ // to send an InputMethodEnd event
+ QInputMethodEvent e;
+ qic->sendEvent(e);
+ data->clear();
+
+ // if the commit string has coming after here, InputMethodStart
+ // will be sent dynamically
+ }
+ return 0;
+ }
+
+ if (text) {
+ char *str = 0;
+ if (text->encoding_is_wchar) {
+ int l = wcstombs(NULL, text->string.wide_char, text->length);
+ if (l != -1) {
+ str = new char[l + 1];
+ wcstombs(str, text->string.wide_char, l);
+ str[l] = 0;
+ }
+ } else
+ str = text->string.multi_byte;
+
+ if (!str)
+ return 0;
+
+ QString s = QString::fromLocal8Bit(str);
+
+ if (text->encoding_is_wchar)
+ delete [] str;
+
+ if (drawstruct->chg_length < 0)
+ data->text.replace(drawstruct->chg_first, INT_MAX, s);
+ else
+ data->text.replace(drawstruct->chg_first, drawstruct->chg_length, s);
+
+ if (data->selectedChars.size() < data->text.length()) {
+ // expand the selectedChars array if the compose string is longer
+ int from = data->selectedChars.size();
+ data->selectedChars.resize(data->text.length());
+ for (int x = from; x < data->selectedChars.size(); ++x)
+ data->selectedChars.clearBit(x);
+ }
+
+ // determine if the changed chars are selected based on text->feedback
+ for (int x = 0; x < text->length; ++x)
+ data->selectedChars.setBit(x + drawstruct->chg_first,
+ (text->feedback ? (text->feedback[x] & XIMReverse) : 0));
+
+ // figure out where the selection starts, and how long it is
+ bool started = false;
+ for (int x = 0; x < qMin(data->selectedChars.size(), data->text.length()); ++x) {
+ if (started) {
+ if (data->selectedChars.testBit(x)) ++sellen;
+ else break;
+ } else {
+ if (data->selectedChars.testBit(x)) {
+ selstart = x;
+ started = true;
+ sellen = 1;
+ }
+ }
+ }
+ } else {
+ if (drawstruct->chg_length == 0)
+ drawstruct->chg_length = -1;
+
+ data->text.remove(drawstruct->chg_first, drawstruct->chg_length);
+ bool qt_compose_emptied = data->text.isEmpty();
+ if (qt_compose_emptied) {
+ XIM_DEBUG("compose emptied 2 text=%s", data->text.toUtf8().constData());
+ // if the composition string has been emptied, we need
+ // to send an InputMethodEnd event
+ QInputMethodEvent e;
+ qic->sendEvent(e);
+ data->clear();
+ // if the commit string has coming after here, InputMethodStart
+ // will be sent dynamically
+ return 0;
+ }
+ }
+
+ XIM_DEBUG("sending compose: '%s', cursor=%d, sellen=%d",
+ data->text.toUtf8().constData(), cursor, sellen);
+ QList<QInputMethodEvent::Attribute> attrs;
+ if (selstart > 0)
+ attrs << QInputMethodEvent::Attribute(QInputMethodEvent::TextFormat, 0, selstart,
+ qic->standardFormat(QInputContext::PreeditFormat));
+ if (sellen)
+ attrs << QInputMethodEvent::Attribute(QInputMethodEvent::TextFormat, selstart, sellen,
+ qic->standardFormat(QInputContext::SelectionFormat));
+ if (selstart + sellen < data->text.length())
+ attrs << QInputMethodEvent::Attribute(QInputMethodEvent::TextFormat,
+ selstart + sellen, data->text.length() - selstart - sellen,
+ qic->standardFormat(QInputContext::PreeditFormat));
+ attrs << QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, cursor, sellen ? 0 : 1, QVariant());
+ QInputMethodEvent e(data->text, attrs);
+ qic->sendEvent(e);
+
+ return 0;
+ }
+
+ static int xic_done_callback(XIC, XPointer client_data, XPointer) {
+ QXIMInputContext *qic = (QXIMInputContext *) client_data;
+ if (!qic)
+ return 0;
+
+ XIM_DEBUG("xic_done_callback");
+ // Don't send InputMethodEnd here. QXIMInputContext::x11FilterEvent()
+ // handles InputMethodEnd with commit string.
+ return 0;
+ }
+}
+
+void QXIMInputContext::ICData::clear()
+{
+ text = QString();
+ selectedChars.clear();
+ composing = false;
+}
+
+QXIMInputContext::ICData *QXIMInputContext::icData() const
+{
+ if (QWidget *w = focusWidget())
+ return ximData.value(w->effectiveWinId());
+ return 0;
+}
+/* The cache here is needed, as X11 leaks a few kb for every
+ XFreeFontSet call, so we avoid creating and deletion of fontsets as
+ much as possible
+*/
+static XFontSet fontsetCache[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
+static int fontsetRefCount = 0;
+
+static const char * const fontsetnames[] = {
+ "-*-fixed-medium-r-*-*-16-*,-*-*-medium-r-*-*-16-*",
+ "-*-fixed-medium-i-*-*-16-*,-*-*-medium-i-*-*-16-*",
+ "-*-fixed-bold-r-*-*-16-*,-*-*-bold-r-*-*-16-*",
+ "-*-fixed-bold-i-*-*-16-*,-*-*-bold-i-*-*-16-*",
+ "-*-fixed-medium-r-*-*-24-*,-*-*-medium-r-*-*-24-*",
+ "-*-fixed-medium-i-*-*-24-*,-*-*-medium-i-*-*-24-*",
+ "-*-fixed-bold-r-*-*-24-*,-*-*-bold-r-*-*-24-*",
+ "-*-fixed-bold-i-*-*-24-*,-*-*-bold-i-*-*-24-*"
+};
+
+static XFontSet getFontSet(const QFont &f)
+{
+ int i = 0;
+ if (f.italic())
+ i |= 1;
+ if (f.bold())
+ i |= 2;
+
+ if (f.pointSize() > 20)
+ i += 4;
+
+ if (!fontsetCache[i]) {
+ Display* dpy = X11->display;
+ int missCount;
+ char** missList;
+ fontsetCache[i] = XCreateFontSet(dpy, fontsetnames[i], &missList, &missCount, 0);
+ if(missCount > 0)
+ XFreeStringList(missList);
+ if (!fontsetCache[i]) {
+ fontsetCache[i] = XCreateFontSet(dpy, "-*-fixed-*-*-*-*-16-*", &missList, &missCount, 0);
+ if(missCount > 0)
+ XFreeStringList(missList);
+ if (!fontsetCache[i])
+ fontsetCache[i] = (XFontSet)-1;
+ }
+ }
+ return (fontsetCache[i] == (XFontSet)-1) ? 0 : fontsetCache[i];
+}
+
+
+
+QXIMInputContext::QXIMInputContext()
+{
+ if (!qt_xim_preferred_style) // no configured input style, use the default
+ qt_xim_preferred_style = xim_default_style;
+
+ xim = 0;
+ QByteArray ximServerName(qt_ximServer);
+ if (qt_ximServer)
+ ximServerName.prepend("@im=");
+ else
+ ximServerName = "";
+
+ if (!XSupportsLocale())
+#ifndef QT_NO_DEBUG
+ qWarning("Qt: Locale not supported on X server")
+#endif
+ ;
+#ifdef USE_X11R6_XIM
+ else if (XSetLocaleModifiers (ximServerName.constData()) == 0)
+ qWarning("Qt: Cannot set locale modifiers: %s", ximServerName.constData());
+ else
+ XRegisterIMInstantiateCallback(X11->display, 0, 0, 0,
+ (XIMProc) xim_create_callback, reinterpret_cast<char *>(this));
+#else // !USE_X11R6_XIM
+ else if (XSetLocaleModifiers ("") == 0)
+ qWarning("Qt: Cannot set locale modifiers");
+ else
+ QXIMInputContext::create_xim();
+#endif // USE_X11R6_XIM
+}
+
+
+/*!\internal
+ Creates the application input method.
+*/
+void QXIMInputContext::create_xim()
+{
+ ++fontsetRefCount;
+#ifndef QT_NO_XIM
+ xim = XOpenIM(X11->display, 0, 0, 0);
+ if (xim) {
+
+#ifdef USE_X11R6_XIM
+ XIMCallback destroy;
+ destroy.callback = (XIMProc) xim_destroy_callback;
+ destroy.client_data = XPointer(this);
+ if (XSetIMValues(xim, XNDestroyCallback, &destroy, (char *) 0) != 0)
+ qWarning("Xlib doesn't support destroy callback");
+#endif // USE_X11R6_XIM
+
+ XIMStyles *styles = 0;
+ XGetIMValues(xim, XNQueryInputStyle, &styles, (char *) 0, (char *) 0);
+ if (styles) {
+ int i;
+ for (i = 0; !xim_style && i < styles->count_styles; i++) {
+ if (styles->supported_styles[i] == qt_xim_preferred_style) {
+ xim_style = qt_xim_preferred_style;
+ break;
+ }
+ }
+ // if the preferred input style couldn't be found, look for
+ // Nothing
+ for (i = 0; !xim_style && i < styles->count_styles; i++) {
+ if (styles->supported_styles[i] == (XIMPreeditNothing | XIMStatusNothing)) {
+ xim_style = XIMPreeditNothing | XIMStatusNothing;
+ break;
+ }
+ }
+ // ... and failing that, None.
+ for (i = 0; !xim_style && i < styles->count_styles; i++) {
+ if (styles->supported_styles[i] == (XIMPreeditNone |
+ XIMStatusNone)) {
+ xim_style = XIMPreeditNone | XIMStatusNone;
+ break;
+ }
+ }
+
+ // qDebug("QApplication: using im style %lx", xim_style);
+ XFree((char *)styles);
+ }
+
+ if (xim_style) {
+
+#ifdef USE_X11R6_XIM
+ XUnregisterIMInstantiateCallback(X11->display, 0, 0, 0,
+ (XIMProc) xim_create_callback, reinterpret_cast<char *>(this));
+#endif // USE_X11R6_XIM
+
+ if (QWidget *focusWidget = QApplication::focusWidget()) {
+ // reinitialize input context after the input method
+ // server (like SCIM) has been launched without
+ // requiring the user to manually switch focus.
+ if (focusWidget->testAttribute(Qt::WA_InputMethodEnabled))
+ setFocusWidget(focusWidget);
+ }
+ // following code fragment is not required for immodule
+ // version of XIM
+#if 0
+ QWidgetList list = qApp->topLevelWidgets();
+ for (int i = 0; i < list.size(); ++i) {
+ QWidget *w = list.at(i);
+ w->d->createTLSysExtra();
+ }
+#endif
+ } else {
+ // Give up
+ qWarning("No supported input style found."
+ " See InputMethod documentation.");
+ close_xim();
+ }
+ }
+#endif // QT_NO_XIM
+}
+
+/*!\internal
+ Closes the application input method.
+*/
+void QXIMInputContext::close_xim()
+{
+ for(QHash<WId, ICData *>::const_iterator i = ximData.constBegin(),
+ e = ximData.constEnd(); i != e; ++i) {
+ ICData *data = i.value();
+ if (data->ic)
+ XDestroyIC(data->ic);
+ delete data;
+ }
+ ximData.clear();
+
+ if ( --fontsetRefCount == 0 ) {
+ Display *dpy = X11->display;
+ for ( int i = 0; i < 8; i++ ) {
+ if ( fontsetCache[i] && fontsetCache[i] != (XFontSet)-1 ) {
+ XFreeFontSet(dpy, fontsetCache[i]);
+ fontsetCache[i] = 0;
+ }
+ }
+ }
+
+ setFocusWidget(0);
+ xim = 0;
+}
+
+
+
+QXIMInputContext::~QXIMInputContext()
+{
+ XIM old_xim = xim; // close_xim clears xim pointer.
+ close_xim();
+ if (old_xim)
+ XCloseIM(old_xim);
+}
+
+
+QString QXIMInputContext::identifierName()
+{
+ // the name should be "xim" rather than "XIM" to be consistent
+ // with corresponding immodule of GTK+
+ return QLatin1String("xim");
+}
+
+
+QString QXIMInputContext::language()
+{
+ QString language;
+ if (xim) {
+ QByteArray locale(XLocaleOfIM(xim));
+
+ if (locale.startsWith("zh")) {
+ // Chinese language should be formed as "zh_CN", "zh_TW", "zh_HK"
+ language = QLatin1String(locale.left(5));
+ } else {
+ // other languages should be two-letter ISO 639 language code
+ language = QLatin1String(locale.left(2));
+ }
+ }
+ return language;
+}
+
+void QXIMInputContext::reset()
+{
+ QWidget *w = focusWidget();
+ if (!w)
+ return;
+
+ ICData *data = ximData.value(w->effectiveWinId());
+ if (!data)
+ return;
+
+ if (data->ic) {
+ char *mb = XmbResetIC(data->ic);
+ QInputMethodEvent e;
+ if (mb) {
+ e.setCommitString(QString::fromLocal8Bit(mb));
+ XFree(mb);
+ }
+ sendEvent(e);
+ update();
+ }
+ data->clear();
+}
+
+void QXIMInputContext::widgetDestroyed(QWidget *w)
+{
+ QInputContext::widgetDestroyed(w);
+ ICData *data = ximData.take(w->effectiveWinId());
+ if (!data)
+ return;
+
+ data->clear();
+ if (data->ic)
+ XDestroyIC(data->ic);
+ delete data;
+}
+
+void QXIMInputContext::mouseHandler(int pos, QMouseEvent *e)
+{
+ if(e->type() != QEvent::MouseButtonPress)
+ return;
+
+ XIM_DEBUG("QXIMInputContext::mouseHandler pos=%d", pos);
+ if (QWidget *w = focusWidget()) {
+ ICData *data = ximData.value(w->effectiveWinId());
+ if (!data)
+ return;
+ if (pos < 0 || pos > data->text.length())
+ reset();
+ // ##### handle mouse position
+ }
+}
+
+bool QXIMInputContext::isComposing() const
+{
+ QWidget *w = focusWidget();
+ if (!w)
+ return false;
+
+ ICData *data = ximData.value(w->effectiveWinId());
+ if (!data)
+ return false;
+ return data->composing;
+}
+
+void QXIMInputContext::setFocusWidget(QWidget *w)
+{
+ if (!xim)
+ return;
+ QWidget *oldFocus = focusWidget();
+ if (oldFocus == w)
+ return;
+
+ if (language() != QLatin1String("ja"))
+ reset();
+
+ if (oldFocus) {
+ ICData *data = ximData.value(oldFocus->effectiveWinId());
+ if (data && data->ic)
+ XUnsetICFocus(data->ic);
+ }
+
+ QInputContext::setFocusWidget(w);
+
+ if (!w)
+ return;
+
+ ICData *data = ximData.value(w->effectiveWinId());
+ if (!data)
+ data = createICData(w);
+
+ if (data->ic)
+ XSetICFocus(data->ic);
+
+ update();
+}
+
+
+bool QXIMInputContext::x11FilterEvent(QWidget *keywidget, XEvent *event)
+{
+ int xkey_keycode = event->xkey.keycode;
+ if (!keywidget->testAttribute(Qt::WA_WState_Created))
+ return false;
+ if (XFilterEvent(event, keywidget->effectiveWinId())) {
+ qt_ximComposingKeycode = xkey_keycode; // ### not documented in xlib
+
+ update();
+
+ return true;
+ }
+ if (event->type != XKeyPress || event->xkey.keycode != 0)
+ return false;
+
+ QWidget *w = focusWidget();
+ if (keywidget != w)
+ return false;
+ ICData *data = ximData.value(w->effectiveWinId());
+ if (!data)
+ return false;
+
+ // input method has sent us a commit string
+ QByteArray string;
+ string.resize(513);
+ KeySym key; // unused
+ Status status; // unused
+ QString text;
+ int count = XmbLookupString(data->ic, &event->xkey, string.data(), string.size(),
+ &key, &status);
+
+ if (status == XBufferOverflow) {
+ string.resize(count + 1);
+ count = XmbLookupString(data->ic, &event->xkey, string.data(), string.size(),
+ &key, &status);
+ }
+ if (count > 0) {
+ // XmbLookupString() gave us some text, convert it to unicode
+ text = qt_input_mapper->toUnicode(string.constData() , count);
+ if (text.isEmpty()) {
+ // codec couldn't convert to unicode? this can happen when running in the
+ // C locale (or with no LANG set). try converting from latin-1
+ text = QString::fromLatin1(string.constData(), count);
+ }
+ }
+
+#if 0
+ if (!(xim_style & XIMPreeditCallbacks) || !isComposing()) {
+ // ############### send a regular key event here!
+ ;
+ }
+#endif
+
+ QInputMethodEvent e;
+ e.setCommitString(text);
+ sendEvent(e);
+ data->clear();
+
+ update();
+
+ return true;
+}
+
+
+QXIMInputContext::ICData *QXIMInputContext::createICData(QWidget *w)
+{
+ ICData *data = new ICData;
+ data->widget = w;
+
+ XVaNestedList preedit_attr = 0;
+ XIMCallback startcallback, drawcallback, donecallback;
+
+ QFont font = w->font();
+ data->fontset = getFontSet(font);
+
+ if (xim_style & XIMPreeditArea) {
+ XRectangle rect;
+ rect.x = 0;
+ rect.y = 0;
+ rect.width = w->width();
+ rect.height = w->height();
+
+ preedit_attr = XVaCreateNestedList(0,
+ XNArea, &rect,
+ XNFontSet, data->fontset,
+ (char *) 0);
+ } else if (xim_style & XIMPreeditPosition) {
+ XPoint spot;
+ spot.x = 1;
+ spot.y = 1;
+
+ preedit_attr = XVaCreateNestedList(0,
+ XNSpotLocation, &spot,
+ XNFontSet, data->fontset,
+ (char *) 0);
+ } else if (xim_style & XIMPreeditCallbacks) {
+ startcallback.client_data = (XPointer) this;
+ startcallback.callback = (XIMProc) xic_start_callback;
+ drawcallback.client_data = (XPointer) this;
+ drawcallback.callback = (XIMProc)xic_draw_callback;
+ donecallback.client_data = (XPointer) this;
+ donecallback.callback = (XIMProc) xic_done_callback;
+
+ preedit_attr = XVaCreateNestedList(0,
+ XNPreeditStartCallback, &startcallback,
+ XNPreeditDrawCallback, &drawcallback,
+ XNPreeditDoneCallback, &donecallback,
+ (char *) 0);
+ }
+
+ if (preedit_attr) {
+ data->ic = XCreateIC(xim,
+ XNInputStyle, xim_style,
+ XNClientWindow, w->effectiveWinId(),
+ XNPreeditAttributes, preedit_attr,
+ (char *) 0);
+ XFree(preedit_attr);
+ } else {
+ data->ic = XCreateIC(xim,
+ XNInputStyle, xim_style,
+ XNClientWindow, w->effectiveWinId(),
+ (char *) 0);
+ }
+
+ if (data->ic) {
+ // when resetting the input context, preserve the input state
+ (void) XSetICValues(data->ic, XNResetState, XIMPreserveState, (char *) 0);
+ } else {
+ qWarning("Failed to create XIC");
+ }
+
+ ximData[w->effectiveWinId()] = data;
+ return data;
+}
+
+void QXIMInputContext::update()
+{
+ QWidget *w = focusWidget();
+ if (!w)
+ return;
+
+ ICData *data = ximData.value(w->effectiveWinId());
+ if (!data || !data->ic)
+ return;
+
+ QRect r = w->inputMethodQuery(Qt::ImMicroFocus).toRect();
+ QPoint p;
+ if (w->nativeParentWidget())
+ p = w->mapTo(w->nativeParentWidget(), QPoint((r.left() + r.right() + 1)/2, r.bottom()));
+ else
+ p = QPoint((r.left() + r.right() + 1)/2, r.bottom());
+ XPoint spot;
+ spot.x = p.x();
+ spot.y = p.y();
+
+ r = w->rect();
+ XRectangle area;
+ area.x = r.x();
+ area.y = r.y();
+ area.width = r.width();
+ area.height = r.height();
+
+ XFontSet fontset = getFontSet(qvariant_cast<QFont>(w->inputMethodQuery(Qt::ImFont)));
+ if (data->fontset == fontset)
+ fontset = 0;
+ else
+ data->fontset = fontset;
+
+ XVaNestedList preedit_attr;
+ if (fontset)
+ preedit_attr = XVaCreateNestedList(0,
+ XNSpotLocation, &spot,
+ XNArea, &area,
+ XNFontSet, fontset,
+ (char *) 0);
+ else
+ preedit_attr = XVaCreateNestedList(0,
+ XNSpotLocation, &spot,
+ XNArea, &area,
+ (char *) 0);
+
+ XSetICValues(data->ic, XNPreeditAttributes, preedit_attr, (char *) 0);
+ XFree(preedit_attr);
+}
+
+
+#else
+/*
+ When QT_NO_XIM is defined, we provide a dummy implementation for
+ this class. The reason for this is that the header file is moc'ed
+ regardless of QT_NO_XIM. The best would be to remove the file
+ completely from the pri file is QT_NO_XIM was defined, or for moc
+ to understand this preprocessor directive. Since the header does
+ not declare this class when QT_NO_XIM is defined, this is dead
+ code.
+*/
+bool QXIMInputContext::isComposing() const { return false; }
+QString QXIMInputContext::identifierName() { return QString(); }
+void QXIMInputContext::mouseHandler(int, QMouseEvent *) {}
+void QXIMInputContext::setFocusWidget(QWidget *) {}
+void QXIMInputContext::reset() {}
+void QXIMInputContext::update() {}
+QXIMInputContext::~QXIMInputContext() {}
+void QXIMInputContext::widgetDestroyed(QWidget *) {}
+QString QXIMInputContext::language() { return QString(); }
+bool QXIMInputContext::x11FilterEvent(QWidget *, XEvent *) { return true; }
+
+#endif //QT_NO_XIM
+
+QT_END_NAMESPACE
+
+#endif //QT_NO_IM
diff --git a/src/gui/itemviews/itemviews.pri b/src/gui/itemviews/itemviews.pri
new file mode 100644
index 0000000000..bbc1e983ba
--- /dev/null
+++ b/src/gui/itemviews/itemviews.pri
@@ -0,0 +1,70 @@
+# Qt gui library, itemviews
+
+HEADERS += \
+ itemviews/qabstractitemview.h \
+ itemviews/qabstractitemview_p.h \
+ itemviews/qheaderview.h \
+ itemviews/qlistview.h \
+ itemviews/qlistview_p.h \
+ itemviews/qbsptree_p.h \
+ itemviews/qtableview.h \
+ itemviews/qtableview_p.h \
+ itemviews/qtreeview.h \
+ itemviews/qtreeview_p.h \
+ itemviews/qabstractitemdelegate.h \
+ itemviews/qitemdelegate.h \
+ itemviews/qitemselectionmodel.h \
+ itemviews/qitemselectionmodel_p.h \
+ itemviews/qdirmodel.h \
+ itemviews/qlistwidget.h \
+ itemviews/qlistwidget_p.h \
+ itemviews/qtablewidget.h \
+ itemviews/qtablewidget_p.h \
+ itemviews/qtreewidget.h \
+ itemviews/qtreewidget_p.h \
+ itemviews/qwidgetitemdata_p.h \
+ itemviews/qproxymodel.h \
+ itemviews/qproxymodel_p.h \
+ itemviews/qabstractproxymodel.h \
+ itemviews/qabstractproxymodel_p.h \
+ itemviews/qsortfilterproxymodel.h \
+ itemviews/qitemeditorfactory.h \
+ itemviews/qitemeditorfactory_p.h \
+ itemviews/qstandarditemmodel.h \
+ itemviews/qstandarditemmodel_p.h \
+ itemviews/qstringlistmodel.h \
+ itemviews/qtreewidgetitemiterator.h \
+ itemviews/qdatawidgetmapper.h \
+ itemviews/qfileiconprovider.h \
+ itemviews/qcolumnviewgrip_p.h \
+ itemviews/qcolumnview.h \
+ itemviews/qcolumnview_p.h \
+ itemviews/qstyleditemdelegate.h
+
+SOURCES += \
+ itemviews/qabstractitemview.cpp \
+ itemviews/qheaderview.cpp \
+ itemviews/qlistview.cpp \
+ itemviews/qbsptree.cpp \
+ itemviews/qtableview.cpp \
+ itemviews/qtreeview.cpp \
+ itemviews/qabstractitemdelegate.cpp \
+ itemviews/qitemdelegate.cpp \
+ itemviews/qitemselectionmodel.cpp \
+ itemviews/qdirmodel.cpp \
+ itemviews/qlistwidget.cpp \
+ itemviews/qtablewidget.cpp \
+ itemviews/qtreewidget.cpp \
+ itemviews/qproxymodel.cpp \
+ itemviews/qabstractproxymodel.cpp \
+ itemviews/qsortfilterproxymodel.cpp \
+ itemviews/qitemeditorfactory.cpp \
+ itemviews/qstandarditemmodel.cpp \
+ itemviews/qstringlistmodel.cpp \
+ itemviews/qtreewidgetitemiterator.cpp \
+ itemviews/qdatawidgetmapper.cpp \
+ itemviews/qfileiconprovider.cpp \
+ itemviews/qcolumnview.cpp \
+ itemviews/qcolumnviewgrip.cpp \
+ itemviews/qstyleditemdelegate.cpp
+
diff --git a/src/gui/itemviews/qabstractitemdelegate.cpp b/src/gui/itemviews/qabstractitemdelegate.cpp
new file mode 100644
index 0000000000..e94c067047
--- /dev/null
+++ b/src/gui/itemviews/qabstractitemdelegate.cpp
@@ -0,0 +1,387 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qabstractitemdelegate.h"
+
+#ifndef QT_NO_ITEMVIEWS
+#include <qabstractitemmodel.h>
+#include <qabstractitemview.h>
+#include <qfontmetrics.h>
+#include <qwhatsthis.h>
+#include <qtooltip.h>
+#include <qevent.h>
+#include <qstring.h>
+#include <qdebug.h>
+#include <private/qtextengine_p.h>
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QAbstractItemDelegate
+
+ \brief The QAbstractItemDelegate class is used to display and edit
+ data items from a model.
+
+ \ingroup model-view
+ \mainclass
+
+ A QAbstractItemDelegate provides the interface and common functionality
+ for delegates in the model/view architecture. Delegates display
+ individual items in views, and handle the editing of model data.
+
+ The QAbstractItemDelegate class is one of the \l{Model/View Classes}
+ and is part of Qt's \l{Model/View Programming}{model/view framework}.
+
+ To render an item in a custom way, you must implement paint() and
+ sizeHint(). The QItemDelegate class provides default implementations for
+ these functions; if you do not need custom rendering, subclass that
+ class instead.
+
+ We give an example of drawing a progress bar in items; in our case
+ for a package management program.
+
+ \image widgetdelegate.png
+
+ We create the \c WidgetDelegate class, which inherits from
+ QStyledItemDelegate. We do the drawing in the paint() function:
+
+ \snippet doc/src/snippets/widgetdelegate.cpp 0
+
+ Notice that we use a QStyleOptionProgressBar and initialize its
+ members. We can then use the current QStyle to draw it.
+
+ To provide custom editing, there are two approaches that can be
+ used. The first approach is to create an editor widget and display
+ it directly on top of the item. To do this you must reimplement
+ createEditor() to provide an editor widget, setEditorData() to populate
+ the editor with the data from the model, and setModelData() so that the
+ delegate can update the model with data from the editor.
+
+ The second approach is to handle user events directly by reimplementing
+ editorEvent().
+
+ \sa {model-view-programming}{Model/View Programming}, QItemDelegate,
+ {Pixelator Example}, QStyledItemDelegate, QStyle
+*/
+
+/*!
+ \enum QAbstractItemDelegate::EndEditHint
+
+ This enum describes the different hints that the delegate can give to the
+ model and view components to make editing data in a model a comfortable
+ experience for the user.
+
+ \value NoHint There is no recommended action to be performed.
+
+ These hints let the delegate influence the behavior of the view:
+
+ \value EditNextItem The view should use the delegate to open an
+ editor on the next item in the view.
+ \value EditPreviousItem The view should use the delegate to open an
+ editor on the previous item in the view.
+
+ Note that custom views may interpret the concepts of next and previous
+ differently.
+
+ The following hints are most useful when models are used that cache
+ data, such as those that manipulate data locally in order to increase
+ performance or conserve network bandwidth.
+
+ \value SubmitModelCache If the model caches data, it should write out
+ cached data to the underlying data store.
+ \value RevertModelCache If the model caches data, it should discard
+ cached data and replace it with data from the
+ underlying data store.
+
+ Although models and views should respond to these hints in appropriate
+ ways, custom components may ignore any or all of them if they are not
+ relevant.
+*/
+
+/*!
+ \fn void QAbstractItemDelegate::commitData(QWidget *editor)
+
+ This signal must be emitted when the \a editor widget has completed
+ editing the data, and wants to write it back into the model.
+*/
+
+/*!
+ \fn void QAbstractItemDelegate::closeEditor(QWidget *editor, QAbstractItemDelegate::EndEditHint hint)
+
+ This signal is emitted when the user has finished editing an item using
+ the specified \a editor.
+
+ The \a hint provides a way for the delegate to influence how the model and
+ view behave after editing is completed. It indicates to these components
+ what action should be performed next to provide a comfortable editing
+ experience for the user. For example, if \c EditNextItem is specified,
+ the view should use a delegate to open an editor on the next item in the
+ model.
+
+ \sa EndEditHint
+*/
+
+/*!
+ \fn void QAbstractItemDelegate::sizeHintChanged(const QModelIndex &index)
+ \since 4.4
+
+ This signal must be emitted when the sizeHint() of \a index changed.
+
+ Views automatically connect to this signal and relayout items as necessary.
+*/
+
+
+/*!
+ Creates a new abstract item delegate with the given \a parent.
+*/
+QAbstractItemDelegate::QAbstractItemDelegate(QObject *parent)
+ : QObject(parent)
+{
+
+}
+
+/*!
+ \internal
+
+ Creates a new abstract item delegate with the given \a parent.
+*/
+QAbstractItemDelegate::QAbstractItemDelegate(QObjectPrivate &dd, QObject *parent)
+ : QObject(dd, parent)
+{
+
+}
+
+/*!
+ Destroys the abstract item delegate.
+*/
+QAbstractItemDelegate::~QAbstractItemDelegate()
+{
+
+}
+
+/*!
+ \fn void QAbstractItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const = 0;
+
+ This pure abstract function must be reimplemented if you want to
+ provide custom rendering. Use the \a painter and style \a option to
+ render the item specified by the item \a index.
+
+ If you reimplement this you must also reimplement sizeHint().
+*/
+
+/*!
+ \fn QSize QAbstractItemDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const = 0
+
+ This pure abstract function must be reimplemented if you want to
+ provide custom rendering. The options are specified by \a option
+ and the model item by \a index.
+
+ If you reimplement this you must also reimplement paint().
+*/
+
+/*!
+ Returns the editor to be used for editing the data item with the
+ given \a index. Note that the index contains information about the
+ model being used. The editor's parent widget is specified by \a parent,
+ and the item options by \a option.
+
+ The base implementation returns 0. If you want custom editing you
+ will need to reimplement this function.
+
+ The returned editor widget should have Qt::StrongFocus;
+ otherwise, \l{QMouseEvent}s received by the widget will propagate
+ to the view. The view's background will shine through unless the
+ editor paints its own background (e.g., with
+ \l{QWidget::}{setAutoFillBackground()}).
+
+ \sa setModelData() setEditorData()
+*/
+QWidget *QAbstractItemDelegate::createEditor(QWidget *,
+ const QStyleOptionViewItem &,
+ const QModelIndex &) const
+{
+ return 0;
+}
+
+/*!
+ Sets the contents of the given \a editor to the data for the item
+ at the given \a index. Note that the index contains information
+ about the model being used.
+
+ The base implementation does nothing. If you want custom editing
+ you will need to reimplement this function.
+
+ \sa setModelData()
+*/
+void QAbstractItemDelegate::setEditorData(QWidget *,
+ const QModelIndex &) const
+{
+ // do nothing
+}
+
+/*!
+ Sets the data for the item at the given \a index in the \a model
+ to the contents of the given \a editor.
+
+ The base implementation does nothing. If you want custom editing
+ you will need to reimplement this function.
+
+ \sa setEditorData()
+*/
+void QAbstractItemDelegate::setModelData(QWidget *,
+ QAbstractItemModel *,
+ const QModelIndex &) const
+{
+ // do nothing
+}
+
+/*!
+ Updates the geometry of the \a editor for the item with the given
+ \a index, according to the rectangle specified in the \a option.
+ If the item has an internal layout, the editor will be laid out
+ accordingly. Note that the index contains information about the
+ model being used.
+
+ The base implementation does nothing. If you want custom editing
+ you must reimplement this function.
+*/
+void QAbstractItemDelegate::updateEditorGeometry(QWidget *,
+ const QStyleOptionViewItem &,
+ const QModelIndex &) const
+{
+ // do nothing
+}
+
+/*!
+ Whenever an event occurs, this function is called with the \a event
+ \a model \a option and the \a index that corresponds to the item being edited.
+
+ The base implementation returns false (indicating that it has not
+ handled the event).
+*/
+bool QAbstractItemDelegate::editorEvent(QEvent *,
+ QAbstractItemModel *,
+ const QStyleOptionViewItem &,
+ const QModelIndex &)
+{
+ // do nothing
+ return false;
+}
+
+/*!
+ \obsolete
+
+ Use QFontMetrics::elidedText() instead.
+
+ \oldcode
+ QFontMetrics fm = ...
+ QString str = QAbstractItemDelegate::elidedText(fm, width, mode, text);
+ \newcode
+ QFontMetrics fm = ...
+ QString str = fm.elidedText(text, mode, width);
+ \endcode
+*/
+
+QString QAbstractItemDelegate::elidedText(const QFontMetrics &fontMetrics, int width,
+ Qt::TextElideMode mode, const QString &text)
+{
+ return fontMetrics.elidedText(text, mode, width);
+}
+
+/*!
+ \since 4.3
+ Whenever a help event occurs, this function is called with the \a event
+ \a view \a option and the \a index that corresponds to the item where the
+ event occurs.
+
+ Returns true if the delegate can handle the event; otherwise returns false.
+ A return value of true indicates that the data obtained using the index had
+ the required role.
+
+ For QEvent::ToolTip and QEvent::WhatsThis events that were handled successfully,
+ the relevant popup may be shown depending on the user's system configuration.
+
+ \sa QHelpEvent
+*/
+// ### Qt 5: Make this a virtual non-slot function
+bool QAbstractItemDelegate::helpEvent(QHelpEvent *event,
+ QAbstractItemView *view,
+ const QStyleOptionViewItem &option,
+ const QModelIndex &index)
+{
+ Q_UNUSED(option);
+
+ if (!event || !view)
+ return false;
+ switch (event->type()) {
+#ifndef QT_NO_TOOLTIP
+ case QEvent::ToolTip: {
+ QHelpEvent *he = static_cast<QHelpEvent*>(event);
+ QVariant tooltip = index.data(Qt::ToolTipRole);
+ if (qVariantCanConvert<QString>(tooltip)) {
+ QToolTip::showText(he->globalPos(), tooltip.toString(), view);
+ return true;
+ }
+ break;}
+#endif
+#ifndef QT_NO_WHATSTHIS
+ case QEvent::QueryWhatsThis: {
+ if (index.data(Qt::WhatsThisRole).isValid())
+ return true;
+ break; }
+ case QEvent::WhatsThis: {
+ QHelpEvent *he = static_cast<QHelpEvent*>(event);
+ QVariant whatsthis = index.data(Qt::WhatsThisRole);
+ if (qVariantCanConvert<QString>(whatsthis)) {
+ QWhatsThis::showText(he->globalPos(), whatsthis.toString(), view);
+ return true;
+ }
+ break ; }
+#endif
+ default:
+ break;
+ }
+ return false;
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_ITEMVIEWS
diff --git a/src/gui/itemviews/qabstractitemdelegate.h b/src/gui/itemviews/qabstractitemdelegate.h
new file mode 100644
index 0000000000..fe9aca766c
--- /dev/null
+++ b/src/gui/itemviews/qabstractitemdelegate.h
@@ -0,0 +1,134 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QABSTRACTITEMDELEGATE_H
+#define QABSTRACTITEMDELEGATE_H
+
+#include <QtCore/qobject.h>
+#include <QtGui/qstyleoption.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+#ifndef QT_NO_ITEMVIEWS
+
+class QPainter;
+class QModelIndex;
+class QAbstractItemModel;
+class QAbstractItemView;
+class QHelpEvent;
+
+class Q_GUI_EXPORT QAbstractItemDelegate : public QObject
+{
+ Q_OBJECT
+
+public:
+
+ enum EndEditHint {
+ NoHint,
+ EditNextItem,
+ EditPreviousItem,
+ SubmitModelCache,
+ RevertModelCache
+ };
+
+ explicit QAbstractItemDelegate(QObject *parent = 0);
+ virtual ~QAbstractItemDelegate();
+
+ // painting
+ virtual void paint(QPainter *painter,
+ const QStyleOptionViewItem &option,
+ const QModelIndex &index) const = 0;
+
+ virtual QSize sizeHint(const QStyleOptionViewItem &option,
+ const QModelIndex &index) const = 0;
+
+ // editing
+ virtual QWidget *createEditor(QWidget *parent,
+ const QStyleOptionViewItem &option,
+ const QModelIndex &index) const;
+
+ virtual void setEditorData(QWidget *editor, const QModelIndex &index) const;
+
+ virtual void setModelData(QWidget *editor,
+ QAbstractItemModel *model,
+ const QModelIndex &index) const;
+
+ virtual void updateEditorGeometry(QWidget *editor,
+ const QStyleOptionViewItem &option,
+ const QModelIndex &index) const;
+
+ // for non-widget editors
+ virtual bool editorEvent(QEvent *event,
+ QAbstractItemModel *model,
+ const QStyleOptionViewItem &option,
+ const QModelIndex &index);
+
+ static QString elidedText(const QFontMetrics &fontMetrics, int width,
+ Qt::TextElideMode mode, const QString &text);
+
+public Q_SLOTS:
+ bool helpEvent(QHelpEvent *event,
+ QAbstractItemView *view,
+ const QStyleOptionViewItem &option,
+ const QModelIndex &index);
+
+Q_SIGNALS:
+ void commitData(QWidget *editor);
+ void closeEditor(QWidget *editor, QAbstractItemDelegate::EndEditHint hint = NoHint);
+ void sizeHintChanged(const QModelIndex &);
+
+protected:
+ QAbstractItemDelegate(QObjectPrivate &, QObject *parent = 0);
+private:
+ Q_DISABLE_COPY(QAbstractItemDelegate)
+};
+
+#endif // QT_NO_ITEMVIEWS
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QABSTRACTITEMDELEGATE_H
diff --git a/src/gui/itemviews/qabstractitemview.cpp b/src/gui/itemviews/qabstractitemview.cpp
new file mode 100644
index 0000000000..7bd6207c8b
--- /dev/null
+++ b/src/gui/itemviews/qabstractitemview.cpp
@@ -0,0 +1,3918 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qabstractitemview.h"
+
+#ifndef QT_NO_ITEMVIEWS
+#include <qpointer.h>
+#include <qapplication.h>
+#include <qclipboard.h>
+#include <qpainter.h>
+#include <qstyle.h>
+#include <qdrag.h>
+#include <qevent.h>
+#include <qscrollbar.h>
+#include <qwhatsthis.h>
+#include <qtooltip.h>
+#include <qdatetime.h>
+#include <qlineedit.h>
+#include <qspinbox.h>
+#include <qstyleditemdelegate.h>
+#include <private/qabstractitemview_p.h>
+#include <private/qabstractitemmodel_p.h>
+#ifndef QT_NO_ACCESSIBILITY
+#include <qaccessible.h>
+#endif
+
+QT_BEGIN_NAMESPACE
+
+QAbstractItemViewPrivate::QAbstractItemViewPrivate()
+ : model(QAbstractItemModelPrivate::staticEmptyModel()),
+ itemDelegate(0),
+ selectionModel(0),
+ selectionMode(QAbstractItemView::ExtendedSelection),
+ selectionBehavior(QAbstractItemView::SelectItems),
+ currentlyCommittingEditor(0),
+ pressedModifiers(Qt::NoModifier),
+ pressedPosition(QPoint(-1, -1)),
+ viewportEnteredNeeded(false),
+ state(QAbstractItemView::NoState),
+ editTriggers(QAbstractItemView::DoubleClicked|QAbstractItemView::EditKeyPressed),
+ lastTrigger(QAbstractItemView::NoEditTriggers),
+ tabKeyNavigation(false),
+#ifndef QT_NO_DRAGANDDROP
+ showDropIndicator(true),
+ dragEnabled(false),
+ dragDropMode(QAbstractItemView::NoDragDrop),
+ overwrite(false),
+ dropIndicatorPosition(QAbstractItemView::OnItem),
+#endif
+ autoScroll(true),
+ autoScrollMargin(16),
+ autoScrollCount(0),
+ alternatingColors(false),
+ textElideMode(Qt::ElideRight),
+ verticalScrollMode(QAbstractItemView::ScrollPerItem),
+ horizontalScrollMode(QAbstractItemView::ScrollPerItem),
+ currentIndexSet(false),
+ wrapItemText(false),
+ delayedPendingLayout(false)
+{
+}
+
+QAbstractItemViewPrivate::~QAbstractItemViewPrivate()
+{
+}
+
+void QAbstractItemViewPrivate::init()
+{
+ Q_Q(QAbstractItemView);
+ q->setItemDelegate(new QStyledItemDelegate(q));
+
+ q->verticalScrollBar()->setRange(0, 0);
+ q->horizontalScrollBar()->setRange(0, 0);
+
+ QObject::connect(q->verticalScrollBar(), SIGNAL(actionTriggered(int)),
+ q, SLOT(verticalScrollbarAction(int)));
+ QObject::connect(q->horizontalScrollBar(), SIGNAL(actionTriggered(int)),
+ q, SLOT(horizontalScrollbarAction(int)));
+ QObject::connect(q->verticalScrollBar(), SIGNAL(valueChanged(int)),
+ q, SLOT(verticalScrollbarValueChanged(int)));
+ QObject::connect(q->horizontalScrollBar(), SIGNAL(valueChanged(int)),
+ q, SLOT(horizontalScrollbarValueChanged(int)));
+
+ viewport->setBackgroundRole(QPalette::Base);
+
+ doDelayedItemsLayout();
+
+ q->setAttribute(Qt::WA_InputMethodEnabled);
+}
+
+/*!
+ \class QAbstractItemView
+
+ \brief The QAbstractItemView class provides the basic functionality for
+ item view classes.
+
+ \ingroup model-view
+ \mainclass
+
+ QAbstractItemView class is the base class for every standard view
+ that uses a QAbstractItemModel. QAbstractItemView is an abstract
+ class and cannot itself be instantiated. It provides a standard
+ interface for interoperating with models through the signals and
+ slots mechanism, enabling subclasses to be kept up-to-date with
+ changes to their models. This class provides standard support for
+ keyboard and mouse navigation, viewport scrolling, item editing,
+ and selections.
+
+ The QAbstractItemView class is one of the \l{Model/View Classes}
+ and is part of Qt's \l{Model/View Programming}{model/view framework}.
+
+ The view classes that inherit QAbstractItemView only need
+ to implement their own view-specific functionality, such as
+ drawing items, returning the geometry of items, finding items,
+ etc.
+
+ QAbstractItemView provides common slots such as edit() and
+ setCurrentIndex(). Many protected slots are also provided, including
+ dataChanged(), rowsInserted(), rowsAboutToBeRemoved(), selectionChanged(),
+ and currentChanged().
+
+ The root item is returned by rootIndex(), and the current item by
+ currentIndex(). To make sure that an item is visible use
+ scrollTo().
+
+ Some of QAbstractItemView's functions are concerned with
+ scrolling, for example setHorizontalScrollMode() and
+ setVerticalScrollMode(). To set the range of the scroll bars, you
+ can, for example, reimplement the view's resizeEvent() function:
+
+ \snippet doc/src/snippets/code/src_gui_itemviews_qabstractitemview.cpp 0
+
+ Note that the range is not updated until the widget is shown.
+
+ Several other functions are concerned with selection control; for
+ example setSelectionMode(), and setSelectionBehavior(). This class
+ provides a default selection model to work with
+ (selectionModel()), but this can be replaced by using
+ setSelectionModel() with an instance of QItemSelectionModel.
+
+ For complete control over the display and editing of items you can
+ specify a delegate with setItemDelegate().
+
+ QAbstractItemView provides a lot of protected functions. Some are
+ concerned with editing, for example, edit(), and commitData(),
+ whilst others are keyboard and mouse event handlers.
+
+ \note If you inherit QAbstractItemView and intend to update the contents
+ of the viewport, you should use viewport->update() instead of
+ \l{QWidget::update()}{update()} as all painting operations take place on the
+ viewport.
+
+ \sa {View Classes}, {Model/View Programming}, QAbstractItemModel, {Chart Example}
+*/
+
+/*!
+ \enum QAbstractItemView::SelectionMode
+
+ This enum indicates how the view responds to user selections:
+
+ \value SingleSelection When the user selects an item, any already-selected
+ item becomes unselected, and the user cannot unselect the selected item by
+ clicking on it.
+
+ \value ContiguousSelection 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 Shift key while clicking on an item, all items between the
+ current item and the clicked item are selected or unselected, depending on
+ the state of the clicked item.
+
+ \value ExtendedSelection 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. If the user presses the
+ Shift key while clicking on an item, all items between the current item
+ and the clicked item are selected or unselected, depending on the state of
+ the clicked item. Multiple items can be selected by dragging the mouse over
+ them.
+
+ \value MultiSelection 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. Multiple items can be toggled by dragging the mouse over them.
+
+ \value NoSelection Items cannot be selected.
+
+ The most commonly used modes are SingleSelection and ExtendedSelection.
+*/
+
+/*!
+ \enum QAbstractItemView::SelectionBehavior
+
+ \value SelectItems Selecting single items.
+ \value SelectRows Selecting only rows.
+ \value SelectColumns Selecting only columns.
+*/
+
+/*!
+ \enum QAbstractItemView::ScrollHint
+
+ \value EnsureVisible Scroll to ensure that the item is visible.
+ \value PositionAtTop Scroll to position the item at the top of the
+ viewport.
+ \value PositionAtBottom Scroll to position the item at the bottom of the
+ viewport.
+ \value PositionAtCenter Scroll to position the item at the center of the
+ viewport.
+*/
+
+
+/*!
+ \enum QAbstractItemView::EditTrigger
+
+ This enum describes actions which will initiate item editing.
+
+ \value NoEditTriggers No editing possible.
+ \value CurrentChanged Editing start whenever current item changes.
+ \value DoubleClicked Editing starts when an item is double clicked.
+ \value SelectedClicked Editing starts when clicking on an already selected
+ item.
+ \value EditKeyPressed Editing starts when the platform edit key has been
+ pressed over an item.
+ \value AnyKeyPressed Editing starts when any key is pressed over an item.
+ \value AllEditTriggers Editing starts for all above actions.
+*/
+
+/*!
+ \enum QAbstractItemView::CursorAction
+
+ This enum describes the different ways to navigate between items,
+ \sa moveCursor()
+
+ \value MoveUp Move to the item above the current item.
+ \value MoveDown Move to the item below the current item.
+ \value MoveLeft Move to the item left of the current item.
+ \value MoveRight Move to the item right of the current item.
+ \value MoveHome Move to the top-left corner item.
+ \value MoveEnd Move to the bottom-right corner item.
+ \value MovePageUp Move one page up above the current item.
+ \value MovePageDown Move one page down below the current item.
+ \value MoveNext Move to the item after the current item.
+ \value MovePrevious Move to the item before the current item.
+*/
+
+/*!
+ \enum QAbstractItemView::State
+
+ Describes the different states the view can be in. This is usually
+ only interesting when reimplementing your own view.
+
+ \value NoState The is the default state.
+ \value DraggingState The user is dragging items.
+ \value DragSelectingState The user is selecting items.
+ \value EditingState The user is editing an item in a widget editor.
+ \value ExpandingState The user is opening a branch of items.
+ \value CollapsingState The user is closing a branch of items.
+ \value AnimatingState The item view is performing an animation.
+*/
+
+/*!
+ \since 4.2
+ \enum QAbstractItemView::ScrollMode
+
+ \value ScrollPerItem The view will scroll the contents one item at a time.
+ \value ScrollPerPixel The view will scroll the contents one pixel at a time.
+*/
+
+/*!
+ \fn QRect QAbstractItemView::visualRect(const QModelIndex &index) const = 0
+ Returns the rectangle on the viewport occupied by the item at \a index.
+
+ If your item is displayed in several areas then visualRect should return
+ the primary area that contains index and not the complete area that index
+ might encompasses, touch or cause drawing.
+
+ In the base class this is a pure virtual function.
+
+ \sa indexAt(), visualRegionForSelection()
+*/
+
+/*!
+ \fn void QAbstractItemView::scrollTo(const QModelIndex &index, ScrollHint hint) = 0
+
+ Scrolls the view if necessary to ensure that the item at \a index
+ is visible. The view will try to position the item according to the given \a hint.
+
+ In the base class this is a pure virtual function.
+*/
+
+/*!
+ \fn QModelIndex QAbstractItemView::indexAt(const QPoint &point) const = 0
+
+ Returns the model index of the item at the viewport coordinates \a point.
+
+ In the base class this is a pure virtual function.
+
+ \sa visualRect()
+*/
+
+/*!
+ \fn void QAbstractItemView::activated(const QModelIndex &index)
+
+ This signal is emitted when the item specified by \a index is
+ activated by the user. How to activate items depends on the
+ platform; e.g., by single- or double-clicking the item, or by
+ pressing the Return or Enter key when the item is current.
+
+ \sa clicked(), doubleClicked(), entered(), pressed()
+*/
+
+/*!
+ \fn void QAbstractItemView::entered(const QModelIndex &index)
+
+ This signal is emitted when the mouse cursor enters the item
+ specified by \a index.
+ Mouse tracking needs to be enabled for this feature to work.
+
+ \sa viewportEntered(), activated(), clicked(), doubleClicked(), pressed()
+*/
+
+/*!
+ \fn void QAbstractItemView::viewportEntered()
+
+ This signal is emitted when the mouse cursor enters the viewport.
+ Mouse tracking needs to be enabled for this feature to work.
+
+ \sa entered()
+*/
+
+/*!
+ \fn void QAbstractItemView::pressed(const QModelIndex &index)
+
+ This signal is emitted when a mouse button is pressed. The item
+ the mouse was pressed on is specified by \a index. The signal is
+ only emitted when the index is valid.
+
+ Use the QApplication::mouseButtons() function to get the state
+ of the mouse buttons.
+
+ \sa activated(), clicked(), doubleClicked(), entered()
+*/
+
+/*!
+ \fn void QAbstractItemView::clicked(const QModelIndex &index)
+
+ This signal is emitted when a mouse button is clicked. The item
+ the mouse was clicked on is specified by \a index. The signal is
+ only emitted when the index is valid.
+
+ \sa activated(), doubleClicked(), entered(), pressed()
+*/
+
+/*!
+ \fn void QAbstractItemView::doubleClicked(const QModelIndex &index)
+
+ This signal is emitted when a mouse button is double-clicked. The
+ item the mouse was double-clicked on is specified by \a index.
+ The signal is only emitted when the index is valid.
+
+ \sa clicked(), activated()
+*/
+
+/*!
+ \fn QModelIndex QAbstractItemView::moveCursor(CursorAction cursorAction, Qt::KeyboardModifiers modifiers) = 0
+
+ Returns a QModelIndex object pointing to the next object in the view,
+ based on the given \a cursorAction and keyboard modifiers specified
+ by \a modifiers.
+
+ In the base class this is a pure virtual function.
+*/
+
+/*!
+ \fn int QAbstractItemView::horizontalOffset() const = 0
+
+ Returns the horizontal offset of the view.
+
+ In the base class this is a pure virtual function.
+
+ \sa verticalOffset()
+*/
+
+/*!
+ \fn int QAbstractItemView::verticalOffset() const = 0
+
+ Returns the vertical offset of the view.
+
+ In the base class this is a pure virtual function.
+
+ \sa horizontalOffset()
+*/
+
+/*!
+ \fn bool QAbstractItemView::isIndexHidden(const QModelIndex &index) const
+
+ Returns true if the item referred to by the given \a index is hidden in the view,
+ otherwise returns false.
+
+ Hiding is a view specific feature. For example in TableView a column can be marked
+ as hidden or a row in the TreeView.
+
+ In the base class this is a pure virtual function.
+*/
+
+/*!
+ \fn void QAbstractItemView::setSelection(const QRect &rect, QItemSelectionModel::SelectionFlags flags)
+
+ Applies the selection \a flags to the items in or touched by the
+ rectangle, \a rect.
+
+ When implementing your own itemview setSelection should call
+ selectionModel()->select(selection, flags) where selection
+ is either an empty QModelIndex or a QItemSelection that contains
+ all items that are contained in \a rect.
+
+ \sa selectionCommand(), selectedIndexes()
+*/
+
+/*!
+ \fn QRegion QAbstractItemView::visualRegionForSelection(const QItemSelection &selection) const = 0
+
+ Returns the region from the viewport of the items in the given
+ \a selection.
+
+ In the base class this is a pure virtual function.
+
+ \sa visualRect(), selectedIndexes()
+*/
+
+/*!
+ \fn void QAbstractItemView::update()
+ \internal
+*/
+
+/*!
+ Constructs an abstract item view with the given \a parent.
+*/
+QAbstractItemView::QAbstractItemView(QWidget *parent)
+ : QAbstractScrollArea(*(new QAbstractItemViewPrivate), parent)
+{
+ d_func()->init();
+}
+
+/*!
+ \internal
+*/
+QAbstractItemView::QAbstractItemView(QAbstractItemViewPrivate &dd, QWidget *parent)
+ : QAbstractScrollArea(dd, parent)
+{
+ d_func()->init();
+}
+
+/*!
+ Destroys the view.
+*/
+QAbstractItemView::~QAbstractItemView()
+{
+}
+
+/*!
+ Sets the \a model for the view to present.
+
+ This function will create and set a new selection model, replacing any
+ model that was previously set with setSelectionModel(). However, the old
+ selection model will not be deleted as it may be shared between several
+ views. We recommend that you delete the old selection model if it is no
+ longer required. This is done with the following code:
+
+ \snippet doc/src/snippets/code/src_gui_itemviews_qabstractitemview.cpp 2
+
+ If both the old model and the old selection model do not have parents, or
+ if their parents are long-lived objects, it may be preferable to call their
+ deleteLater() functions to explicitly delete them.
+
+ The view \e{does not} take ownership of the model unless it is the model's
+ parent object because the view may be shared between many different views.
+
+ \sa selectionModel(), setSelectionModel()
+*/
+void QAbstractItemView::setModel(QAbstractItemModel *model)
+{
+ Q_D(QAbstractItemView);
+ if (model == d->model)
+ return;
+ if (d->model && d->model != QAbstractItemModelPrivate::staticEmptyModel()) {
+ disconnect(d->model, SIGNAL(destroyed()),
+ this, SLOT(_q_modelDestroyed()));
+ disconnect(d->model, SIGNAL(dataChanged(QModelIndex,QModelIndex)),
+ this, SLOT(dataChanged(QModelIndex,QModelIndex)));
+ disconnect(d->model, SIGNAL(rowsInserted(QModelIndex,int,int)),
+ this, SLOT(rowsInserted(QModelIndex,int,int)));
+ disconnect(d->model, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)),
+ this, SLOT(rowsAboutToBeRemoved(QModelIndex,int,int)));
+ disconnect(d->model, SIGNAL(rowsRemoved(QModelIndex,int,int)),
+ this, SLOT(_q_rowsRemoved(QModelIndex,int,int)));
+ disconnect(d->model, SIGNAL(columnsAboutToBeRemoved(QModelIndex,int,int)),
+ this, SLOT(_q_columnsAboutToBeRemoved(QModelIndex,int,int)));
+ disconnect(d->model, SIGNAL(columnsRemoved(QModelIndex,int,int)),
+ this, SLOT(_q_columnsRemoved(QModelIndex,int,int)));
+ disconnect(d->model, SIGNAL(columnsInserted(QModelIndex,int,int)),
+ this, SLOT(_q_columnsInserted(QModelIndex,int,int)));
+
+ disconnect(d->model, SIGNAL(modelReset()), this, SLOT(reset()));
+ disconnect(d->model, SIGNAL(layoutChanged()), this, SLOT(_q_layoutChanged()));
+ }
+ d->model = (model ? model : QAbstractItemModelPrivate::staticEmptyModel());
+
+ // These asserts do basic sanity checking of the model
+ Q_ASSERT_X(d->model->index(0,0) == d->model->index(0,0),
+ "QAbstractItemView::setModel",
+ "A model should return the exact same index "
+ "(including its internal id/pointer) when asked for it twice in a row.");
+ Q_ASSERT_X(d->model->index(0,0).parent() == QModelIndex(),
+ "QAbstractItemView::setModel",
+ "The parent of a top level index should be invalid");
+
+ if (d->model && d->model != QAbstractItemModelPrivate::staticEmptyModel()) {
+ connect(d->model, SIGNAL(destroyed()),
+ this, SLOT(_q_modelDestroyed()));
+ connect(d->model, SIGNAL(dataChanged(QModelIndex,QModelIndex)),
+ this, SLOT(dataChanged(QModelIndex,QModelIndex)));
+ connect(d->model, SIGNAL(rowsInserted(QModelIndex,int,int)),
+ this, SLOT(rowsInserted(QModelIndex,int,int)));
+ connect(d->model, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)),
+ this, SLOT(rowsAboutToBeRemoved(QModelIndex,int,int)));
+ connect(d->model, SIGNAL(rowsRemoved(QModelIndex,int,int)),
+ this, SLOT(_q_rowsRemoved(QModelIndex,int,int)));
+ connect(d->model, SIGNAL(columnsAboutToBeRemoved(QModelIndex,int,int)),
+ this, SLOT(_q_columnsAboutToBeRemoved(QModelIndex,int,int)));
+ connect(d->model, SIGNAL(columnsRemoved(QModelIndex,int,int)),
+ this, SLOT(_q_columnsRemoved(QModelIndex,int,int)));
+ connect(d->model, SIGNAL(columnsInserted(QModelIndex,int,int)),
+ this, SLOT(_q_columnsInserted(QModelIndex,int,int)));
+
+ connect(d->model, SIGNAL(modelReset()), this, SLOT(reset()));
+ connect(d->model, SIGNAL(layoutChanged()), this, SLOT(_q_layoutChanged()));
+ }
+ setSelectionModel(new QItemSelectionModel(d->model, this));
+ reset(); // kill editors, set new root and do layout
+}
+
+/*!
+ Returns the model that this view is presenting.
+*/
+QAbstractItemModel *QAbstractItemView::model() const
+{
+ Q_D(const QAbstractItemView);
+ return (d->model == QAbstractItemModelPrivate::staticEmptyModel() ? 0 : d->model);
+}
+
+/*!
+ Sets the current selection model to the given \a selectionModel.
+
+ Note that, if you call setModel() after this function, the given \a selectionModel
+ will be replaced by one created by the view.
+
+ \note It is up to the application to delete the old selection model if it is no
+ longer needed; i.e., if it is not being used by other views. This will happen
+ automatically when its parent object is deleted. However, if it does not have a
+ parent, or if the parent is a long-lived object, it may be preferable to call its
+ deleteLater() function to explicitly delete it.
+
+ \sa selectionModel(), setModel(), clearSelection()
+*/
+void QAbstractItemView::setSelectionModel(QItemSelectionModel *selectionModel)
+{
+ // ### if the given model is null, we should use the original selection model
+ Q_ASSERT(selectionModel);
+ Q_D(QAbstractItemView);
+
+ if (selectionModel->model() != d->model) {
+ qWarning("QAbstractItemView::setSelectionModel() failed: "
+ "Trying to set a selection model, which works on "
+ "a different model than the view.");
+ return;
+ }
+
+ if (d->selectionModel) {
+ disconnect(d->selectionModel, SIGNAL(selectionChanged(QItemSelection,QItemSelection)),
+ this, SLOT(selectionChanged(QItemSelection,QItemSelection)));
+ disconnect(d->selectionModel, SIGNAL(currentChanged(QModelIndex,QModelIndex)),
+ this, SLOT(currentChanged(QModelIndex,QModelIndex)));
+ }
+
+ d->selectionModel = selectionModel;
+
+ if (d->selectionModel) {
+ connect(d->selectionModel, SIGNAL(selectionChanged(QItemSelection,QItemSelection)),
+ this, SLOT(selectionChanged(QItemSelection,QItemSelection)));
+ connect(d->selectionModel, SIGNAL(currentChanged(QModelIndex,QModelIndex)),
+ this, SLOT(currentChanged(QModelIndex,QModelIndex)));
+ }
+}
+
+/*!
+ Returns the current selection model.
+
+ \sa setSelectionModel(), selectedIndexes()
+*/
+QItemSelectionModel* QAbstractItemView::selectionModel() const
+{
+ Q_D(const QAbstractItemView);
+ return d->selectionModel;
+}
+
+/*!
+ Sets the item delegate for this view and its model to \a delegate.
+ This is useful if you want complete control over the editing and
+ display of items.
+
+ Any existing delegate will be removed, but not deleted. QAbstractItemView
+ does not take ownership of \a delegate.
+
+ \warning You should not share the same instance of a delegate between views.
+ Doing so can cause incorrect or unintuitive editing behavior since each
+ view connected to a given delegate may receive the \l{QAbstractItemDelegate::}{closeEditor()}
+ signal, and attempt to access, modify or close an editor that has already been closed.
+
+ \sa itemDelegate()
+*/
+void QAbstractItemView::setItemDelegate(QAbstractItemDelegate *delegate)
+{
+ Q_D(QAbstractItemView);
+ if (delegate == d->itemDelegate)
+ return;
+
+ if (d->itemDelegate) {
+ if (d->delegateRefCount(d->itemDelegate) == 1) {
+ disconnect(d->itemDelegate, SIGNAL(closeEditor(QWidget*,QAbstractItemDelegate::EndEditHint)),
+ this, SLOT(closeEditor(QWidget*,QAbstractItemDelegate::EndEditHint)));
+ disconnect(d->itemDelegate, SIGNAL(commitData(QWidget*)), this, SLOT(commitData(QWidget*)));
+ disconnect(d->itemDelegate, SIGNAL(sizeHintChanged(QModelIndex)), this, SLOT(doItemsLayout()));
+ }
+ }
+
+
+ if (delegate) {
+ if (d->delegateRefCount(delegate) == 0) {
+ connect(delegate, SIGNAL(closeEditor(QWidget*,QAbstractItemDelegate::EndEditHint)),
+ this, SLOT(closeEditor(QWidget*,QAbstractItemDelegate::EndEditHint)));
+ connect(delegate, SIGNAL(commitData(QWidget*)), this, SLOT(commitData(QWidget*)));
+ qRegisterMetaType<QModelIndex>("QModelIndex");
+ connect(delegate, SIGNAL(sizeHintChanged(QModelIndex)), this, SLOT(doItemsLayout()), Qt::QueuedConnection);
+ }
+ }
+ d->itemDelegate = delegate;
+}
+
+/*!
+ Returns the item delegate used by this view and model. This is
+ either one set with setItemDelegate(), or the default one.
+
+ \sa setItemDelegate()
+*/
+QAbstractItemDelegate *QAbstractItemView::itemDelegate() const
+{
+ return d_func()->itemDelegate;
+}
+
+/*!
+ \reimp
+*/
+QVariant QAbstractItemView::inputMethodQuery(Qt::InputMethodQuery query) const
+{
+ const QModelIndex current = currentIndex();
+ if (!current.isValid() || query != Qt::ImMicroFocus)
+ return QAbstractScrollArea::inputMethodQuery(query);
+ return visualRect(current);
+}
+
+/*!
+ \since 4.2
+
+ Sets the given item \a delegate used by this view and model for the given
+ \a row. All items on \a row will be drawn and managed by \a delegate
+ instead of using the default delegate (i.e., itemDelegate()).
+
+ Any existing row delegate for \a row will be removed, but not
+ deleted. QAbstractItemView does not take ownership of \a delegate.
+
+ \note If a delegate has been assigned to both a row and a column, the row
+ delegate (i.e., this delegate) will take presedence and manage the
+ intersecting cell index.
+
+ \warning You should not share the same instance of a delegate between views.
+ Doing so can cause incorrect or unintuitive editing behavior since each
+ view connected to a given delegate may receive the \l{QAbstractItemDelegate::}{closeEditor()}
+ signal, and attempt to access, modify or close an editor that has already been closed.
+
+ \sa itemDelegateForRow(), setItemDelegateForColumn(), itemDelegate()
+*/
+void QAbstractItemView::setItemDelegateForRow(int row, QAbstractItemDelegate *delegate)
+{
+ Q_D(QAbstractItemView);
+ if (QAbstractItemDelegate *rowDelegate = d->rowDelegates.value(row, 0)) {
+ if (d->delegateRefCount(rowDelegate) == 1) {
+ disconnect(rowDelegate, SIGNAL(closeEditor(QWidget*,QAbstractItemDelegate::EndEditHint)),
+ this, SLOT(closeEditor(QWidget*,QAbstractItemDelegate::EndEditHint)));
+ disconnect(rowDelegate, SIGNAL(commitData(QWidget*)), this, SLOT(commitData(QWidget*)));
+ }
+ d->rowDelegates.remove(row);
+ }
+ if (delegate) {
+ if (d->delegateRefCount(delegate) == 0) {
+ connect(delegate, SIGNAL(closeEditor(QWidget*,QAbstractItemDelegate::EndEditHint)),
+ this, SLOT(closeEditor(QWidget*,QAbstractItemDelegate::EndEditHint)));
+ connect(delegate, SIGNAL(commitData(QWidget*)), this, SLOT(commitData(QWidget*)));
+ }
+ d->rowDelegates.insert(row, delegate);
+ }
+}
+
+/*!
+ \since 4.2
+
+ Returns the item delegate used by this view and model for the given \a row,
+ or 0 if no delegate has been assigned. You can call itemDelegate() to get a
+ pointer to the current delegate for a given index.
+
+ \sa setItemDelegateForRow(), itemDelegateForColumn(), setItemDelegate()
+*/
+QAbstractItemDelegate *QAbstractItemView::itemDelegateForRow(int row) const
+{
+ Q_D(const QAbstractItemView);
+ return d->rowDelegates.value(row, 0);
+}
+
+/*!
+ \since 4.2
+
+ Sets the given item \a delegate used by this view and model for the given
+ \a column. All items on \a column will be drawn and managed by \a delegate
+ instead of using the default delegate (i.e., itemDelegate()).
+
+ Any existing column delegate for \a column will be removed, but not
+ deleted. QAbstractItemView does not take ownership of \a delegate.
+
+ \note If a delegate has been assigned to both a row and a column, the row
+ delegate will take presedence and manage the intersecting cell index.
+
+ \warning You should not share the same instance of a delegate between views.
+ Doing so can cause incorrect or unintuitive editing behavior since each
+ view connected to a given delegate may receive the \l{QAbstractItemDelegate::}{closeEditor()}
+ signal, and attempt to access, modify or close an editor that has already been closed.
+
+ \sa itemDelegateForColumn(), setItemDelegateForRow(), itemDelegate()
+*/
+void QAbstractItemView::setItemDelegateForColumn(int column, QAbstractItemDelegate *delegate)
+{
+ Q_D(QAbstractItemView);
+ if (QAbstractItemDelegate *columnDelegate = d->columnDelegates.value(column, 0)) {
+ if (d->delegateRefCount(columnDelegate) == 1) {
+ disconnect(columnDelegate, SIGNAL(closeEditor(QWidget*,QAbstractItemDelegate::EndEditHint)),
+ this, SLOT(closeEditor(QWidget*,QAbstractItemDelegate::EndEditHint)));
+ disconnect(columnDelegate, SIGNAL(commitData(QWidget*)), this, SLOT(commitData(QWidget*)));
+ }
+ d->columnDelegates.remove(column);
+ }
+ if (delegate) {
+ if (d->delegateRefCount(delegate) == 0) {
+ connect(delegate, SIGNAL(closeEditor(QWidget*,QAbstractItemDelegate::EndEditHint)),
+ this, SLOT(closeEditor(QWidget*,QAbstractItemDelegate::EndEditHint)));
+ connect(delegate, SIGNAL(commitData(QWidget*)), this, SLOT(commitData(QWidget*)));
+ }
+ d->columnDelegates.insert(column, delegate);
+ }
+}
+
+/*!
+ \since 4.2
+
+ Returns the item delegate used by this view and model for the given \a
+ column. You can call itemDelegate() to get a pointer to the current delegate
+ for a given index.
+
+ \sa setItemDelegateForColumn(), itemDelegateForRow(), itemDelegate()
+*/
+QAbstractItemDelegate *QAbstractItemView::itemDelegateForColumn(int column) const
+{
+ Q_D(const QAbstractItemView);
+ return d->columnDelegates.value(column, 0);
+}
+
+/*!
+ Returns the item delegate used by this view and model for
+ the given \a index.
+*/
+QAbstractItemDelegate *QAbstractItemView::itemDelegate(const QModelIndex &index) const
+{
+ Q_D(const QAbstractItemView);
+ return d->delegateForIndex(index);
+}
+
+/*!
+ \property QAbstractItemView::selectionMode
+ \brief which selection mode the view operates in
+
+ This property controls whether the user can select one or many items
+ and, in many-item selections, whether the selection must be a
+ continuous range of items.
+
+ \sa SelectionMode SelectionBehavior
+*/
+void QAbstractItemView::setSelectionMode(SelectionMode mode)
+{
+ Q_D(QAbstractItemView);
+ d->selectionMode = mode;
+}
+
+QAbstractItemView::SelectionMode QAbstractItemView::selectionMode() const
+{
+ Q_D(const QAbstractItemView);
+ return d->selectionMode;
+}
+
+/*!
+ \property QAbstractItemView::selectionBehavior
+ \brief which selection behavior the view uses
+
+ This property holds whether selections are done
+ in terms of single items, rows or columns.
+
+ \sa SelectionMode SelectionBehavior
+*/
+
+void QAbstractItemView::setSelectionBehavior(QAbstractItemView::SelectionBehavior behavior)
+{
+ Q_D(QAbstractItemView);
+ d->selectionBehavior = behavior;
+}
+
+QAbstractItemView::SelectionBehavior QAbstractItemView::selectionBehavior() const
+{
+ Q_D(const QAbstractItemView);
+ return d->selectionBehavior;
+}
+
+/*!
+ Sets the current item to be the item at \a index.
+ Depending on the current selection mode, the item may also be selected.
+ Note that this function also updates the starting position for any
+ new selections the user performs.
+
+ To set an item as the current item without selecting it, call
+
+ \c{selectionModel()->setCurrentIndex(index, QItemSelectionModel::NoUpdate);}
+
+ \sa currentIndex(), currentChanged(), selectionMode
+*/
+void QAbstractItemView::setCurrentIndex(const QModelIndex &index)
+{
+ Q_D(QAbstractItemView);
+ if (d->selectionModel && (!index.isValid() || d->isIndexEnabled(index))) {
+ QItemSelectionModel::SelectionFlags command = selectionCommand(index, 0);
+ d->selectionModel->setCurrentIndex(index, command);
+ d->currentIndexSet = true;
+ if ((command & QItemSelectionModel::Current) == 0)
+ d->pressedPosition = visualRect(currentIndex()).center() + d->offset();
+ }
+}
+
+/*!
+ Returns the model index of the current item.
+
+ \sa setCurrentIndex()
+*/
+QModelIndex QAbstractItemView::currentIndex() const
+{
+ Q_D(const QAbstractItemView);
+ return d->selectionModel ? d->selectionModel->currentIndex() : QModelIndex();
+}
+
+
+/*!
+ Reset the internal state of the view.
+
+ \warning This function will reset open editors, scroll bar positions,
+ selections, etc. Existing changes will not be committed. If you would like
+ to save your changes when resetting the view, you can reimplement this
+ function, commit your changes, and then call the superclass'
+ implementation.
+*/
+void QAbstractItemView::reset()
+{
+ Q_D(QAbstractItemView);
+ QList<QEditorInfo>::const_iterator it = d->editors.constBegin();
+ for (; it != d->editors.constEnd(); ++it)
+ d->releaseEditor(it->editor);
+ d->editors.clear();
+ d->persistent.clear();
+ d->currentIndexSet = false;
+ setState(NoState);
+ setRootIndex(QModelIndex());
+}
+
+/*!
+ Sets the root item to the item at the given \a index.
+
+ \sa rootIndex()
+*/
+void QAbstractItemView::setRootIndex(const QModelIndex &index)
+{
+ Q_D(QAbstractItemView);
+ if (index.isValid() && index.model() != d->model) {
+ qWarning("QAbstractItemView::setRootIndex failed : index must be from the currently set model");
+ return;
+ }
+ d->root = index;
+ d->doDelayedItemsLayout();
+}
+
+/*!
+ Returns the model index of the model's root item. The root item is
+ the parent item to the view's toplevel items. The root can be invalid.
+
+ \sa setRootIndex()
+*/
+QModelIndex QAbstractItemView::rootIndex() const
+{
+ return QModelIndex(d_func()->root);
+}
+
+/*!
+ Selects all item in the view.
+ This function wil use the selection selection behavior
+ set on the view when selecting.
+
+ \sa setSelection(), selectedIndexes(), clearSelection()
+*/
+void QAbstractItemView::selectAll()
+{
+ Q_D(QAbstractItemView);
+ SelectionMode mode = d->selectionMode;
+ if (mode == MultiSelection || mode == ExtendedSelection)
+ d->selectAll(QItemSelectionModel::ClearAndSelect
+ |d->selectionBehaviorFlags());
+ else if (mode != SingleSelection)
+ d->selectAll(selectionCommand(d->model->index(0, 0, d->root)));
+}
+
+/*!
+ Starts editing the item corresponding to the given \a index if it is
+ editable.
+
+ Note that this function does not change the current index. Since the current
+ index defines the next and previous items to edit, users may find that
+ keyboard navigation does not work as expected. To provide consistent navigation
+ behavior, call setCurrentIndex() before this function with the same model
+ index.
+
+ \sa QModelIndex::flags()
+*/
+void QAbstractItemView::edit(const QModelIndex &index)
+{
+ Q_D(QAbstractItemView);
+ if (!d->isIndexValid(index))
+ qWarning("edit: index was invalid");
+ if (!edit(index, AllEditTriggers, 0))
+ qWarning("edit: editing failed");
+}
+
+/*!
+ Deselects all selected items. The current index will not be changed.
+
+ \sa setSelection(), selectAll()
+*/
+void QAbstractItemView::clearSelection()
+{
+ Q_D(QAbstractItemView);
+ if (d->selectionModel)
+ d->selectionModel->clearSelection();
+}
+
+/*!
+ \internal
+
+ This function is intended to lay out the items in the view.
+ The default implementation just calls updateGeometries() and updates the viewport.
+*/
+void QAbstractItemView::doItemsLayout()
+{
+ Q_D(QAbstractItemView);
+ d->interruptDelayedItemsLayout();
+ updateGeometries();
+ d->viewport->update();
+}
+
+/*!
+ \property QAbstractItemView::editTriggers
+ \brief which actions will initiate item editing
+
+ This property is a selection of flags defined by
+ \l{EditTrigger}, combined using the OR
+ operator. The view will only initiate the editing of an item if the
+ action performed is set in this property.
+*/
+void QAbstractItemView::setEditTriggers(EditTriggers actions)
+{
+ Q_D(QAbstractItemView);
+ d->editTriggers = actions;
+}
+
+QAbstractItemView::EditTriggers QAbstractItemView::editTriggers() const
+{
+ Q_D(const QAbstractItemView);
+ return d->editTriggers;
+}
+
+/*!
+ \since 4.2
+ \property QAbstractItemView::verticalScrollMode
+ \brief how the view scrolls its contents in the vertical direction
+
+ This property controlls how the view scroll its contents vertically.
+ Scrolling can be done either per pixel or per item.
+*/
+
+void QAbstractItemView::setVerticalScrollMode(ScrollMode mode)
+{
+ Q_D(QAbstractItemView);
+ if (mode == d->verticalScrollMode)
+ return;
+ QModelIndex topLeft = indexAt(QPoint(0, 0));
+ d->verticalScrollMode = mode;
+ updateGeometries(); // update the scroll bars
+ scrollTo(topLeft, QAbstractItemView::PositionAtTop);
+}
+
+QAbstractItemView::ScrollMode QAbstractItemView::verticalScrollMode() const
+{
+ Q_D(const QAbstractItemView);
+ return d->verticalScrollMode;
+}
+
+/*!
+ \since 4.2
+ \property QAbstractItemView::horizontalScrollMode
+ \brief how the view scrolls its contents in the horizontal direction
+
+ This property controlls how the view scroll its contents horizontally.
+ Scrolling can be done either per pixel or per item.
+*/
+
+void QAbstractItemView::setHorizontalScrollMode(ScrollMode mode)
+{
+ Q_D(QAbstractItemView);
+ d->horizontalScrollMode = mode;
+ updateGeometries(); // update the scroll bars
+}
+
+QAbstractItemView::ScrollMode QAbstractItemView::horizontalScrollMode() const
+{
+ Q_D(const QAbstractItemView);
+ return d->horizontalScrollMode;
+}
+
+#ifndef QT_NO_DRAGANDDROP
+/*!
+ \since 4.2
+ \property QAbstractItemView::dragDropOverwriteMode
+ \brief the view's drag and drop behavior
+
+ If its value is \c true, the selected data will overwrite the
+ existing item data when dropped, while moving the data will clear
+ the item. If its value is \c false, the selected data will be
+ inserted as a new item when the data is dropped. When the data is
+ moved, the item is removed as well.
+
+ The default value is \c false, as in the QListView and QTreeView
+ subclasses. In the QTableView subclass, on the other hand, the
+ property has been set to \c true.
+
+ Note: This is not intended to prevent overwriting of items.
+ The model's implementation of flags() should do that by not
+ returning Qt::ItemIsDropEnabled.
+
+ \sa dragDropMode
+*/
+void QAbstractItemView::setDragDropOverwriteMode(bool overwrite)
+{
+ Q_D(QAbstractItemView);
+ d->overwrite = overwrite;
+}
+
+bool QAbstractItemView::dragDropOverwriteMode() const
+{
+ Q_D(const QAbstractItemView);
+ return d->overwrite;
+}
+#endif
+
+/*!
+ \property QAbstractItemView::autoScroll
+ \brief whether autoscrolling in drag move events is enabled
+
+ If this property is set to true (the default), the
+ QAbstractItemView automatically scrolls the contents of the view
+ if the user drags within 16 pixels of the viewport edge. If the current
+ item changes, then the view will scroll automatically to ensure that the
+ current item is fully visible.
+
+ This property only works if the viewport accepts drops. Autoscroll is
+ switched off by setting this property to false.
+*/
+
+void QAbstractItemView::setAutoScroll(bool enable)
+{
+ Q_D(QAbstractItemView);
+ d->autoScroll = enable;
+}
+
+bool QAbstractItemView::hasAutoScroll() const
+{
+ Q_D(const QAbstractItemView);
+ return d->autoScroll;
+}
+
+/*!
+ \since 4.4
+ \property QAbstractItemView::autoScrollMargin
+ \brief the size of the area when auto scrolling is triggered
+
+ This property controlls the size of the area at the edge of the viewport that
+ triggers autoscrolling. The default value is 16 pixels.
+*/
+void QAbstractItemView::setAutoScrollMargin(int margin)
+{
+ Q_D(QAbstractItemView);
+ d->autoScrollMargin = margin;
+}
+
+int QAbstractItemView::autoScrollMargin() const
+{
+ Q_D(const QAbstractItemView);
+ return d->autoScrollMargin;
+}
+
+/*!
+ \property QAbstractItemView::tabKeyNavigation
+ \brief whether item navigation with tab and backtab is enabled.
+*/
+
+void QAbstractItemView::setTabKeyNavigation(bool enable)
+{
+ Q_D(QAbstractItemView);
+ d->tabKeyNavigation = enable;
+}
+
+bool QAbstractItemView::tabKeyNavigation() const
+{
+ Q_D(const QAbstractItemView);
+ return d->tabKeyNavigation;
+}
+
+#ifndef QT_NO_DRAGANDDROP
+/*!
+ \property QAbstractItemView::showDropIndicator
+ \brief whether the drop indicator is shown when dragging items and dropping.
+
+ \sa dragEnabled DragDropMode dragDropOverwriteMode acceptDrops
+*/
+
+void QAbstractItemView::setDropIndicatorShown(bool enable)
+{
+ Q_D(QAbstractItemView);
+ d->showDropIndicator = enable;
+}
+
+bool QAbstractItemView::showDropIndicator() const
+{
+ Q_D(const QAbstractItemView);
+ return d->showDropIndicator;
+}
+
+/*!
+ \property QAbstractItemView::dragEnabled
+ \brief whether the view supports dragging of its own items
+
+ \sa showDropIndicator DragDropMode dragDropOverwriteMode acceptDrops
+*/
+
+void QAbstractItemView::setDragEnabled(bool enable)
+{
+ Q_D(QAbstractItemView);
+ d->dragEnabled = enable;
+}
+
+bool QAbstractItemView::dragEnabled() const
+{
+ Q_D(const QAbstractItemView);
+ return d->dragEnabled;
+}
+
+/*!
+ \since 4.2
+ \enum QAbstractItemView::DragDropMode
+
+ Describes the various drag and drop events the view can act upon.
+ By default the view does not support dragging or dropping (\c
+ NoDragDrop).
+
+ \value NoDragDrop Does not support dragging or dropping.
+ \value DragOnly The view supports dragging of its own items
+ \value DropOnly The view accepts drops
+ \value DragDrop The view supports both dragging and dropping
+ \value InternalMove The view accepts move (\bold{not copy}) operations only
+ from itself.
+
+ Note that the model used needs to provide support for drag and drop operations.
+
+ \sa setDragDropMode() {Using Drag and Drop with Item Views}
+*/
+
+/*!
+ \property QAbstractItemView::dragDropMode
+ \brief the drag and drop event the view will act upon
+
+ \since 4.2
+ \sa showDropIndicator dragDropOverwriteMode
+*/
+void QAbstractItemView::setDragDropMode(DragDropMode behavior)
+{
+ Q_D(QAbstractItemView);
+ d->dragDropMode = behavior;
+ setDragEnabled(behavior == DragOnly || behavior == DragDrop || behavior == InternalMove);
+ setAcceptDrops(behavior == DropOnly || behavior == DragDrop || behavior == InternalMove);
+}
+
+QAbstractItemView::DragDropMode QAbstractItemView::dragDropMode() const
+{
+ Q_D(const QAbstractItemView);
+ DragDropMode setBehavior = d->dragDropMode;
+ if (!dragEnabled() && !acceptDrops())
+ return NoDragDrop;
+
+ if (dragEnabled() && !acceptDrops())
+ return DragOnly;
+
+ if (!dragEnabled() && acceptDrops())
+ return DropOnly;
+
+ if (dragEnabled() && acceptDrops()) {
+ if (setBehavior == InternalMove)
+ return setBehavior;
+ else
+ return DragDrop;
+ }
+
+ return NoDragDrop;
+}
+
+#endif // QT_NO_DRAGANDDROP
+
+/*!
+ \property QAbstractItemView::alternatingRowColors
+ \brief whether to draw the background using alternating colors
+
+ If this property is true, the item background will be drawn using
+ QPalette::Base and QPalette::AlternateBase; otherwise the background
+ will be drawn using the QPalette::Base color.
+
+ By default, this property is false.
+*/
+void QAbstractItemView::setAlternatingRowColors(bool enable)
+{
+ Q_D(QAbstractItemView);
+ d->alternatingColors = enable;
+ if (isVisible())
+ d->viewport->update();
+}
+
+bool QAbstractItemView::alternatingRowColors() const
+{
+ Q_D(const QAbstractItemView);
+ return d->alternatingColors;
+}
+
+/*!
+ \property QAbstractItemView::iconSize
+ \brief the size of items' icons
+
+ Setting this property when the view is visible will cause the
+ items to be laid out again.
+*/
+void QAbstractItemView::setIconSize(const QSize &size)
+{
+ Q_D(QAbstractItemView);
+ if (size == d->iconSize)
+ return;
+ d->iconSize = size;
+ d->doDelayedItemsLayout();
+}
+
+QSize QAbstractItemView::iconSize() const
+{
+ Q_D(const QAbstractItemView);
+ return d->iconSize;
+}
+
+/*!
+ \property QAbstractItemView::textElideMode
+
+ \brief the the position of the "..." in elided text.
+
+ The default value for all item views is Qt::ElideRight.
+*/
+void QAbstractItemView::setTextElideMode(Qt::TextElideMode mode)
+{
+ Q_D(QAbstractItemView);
+ d->textElideMode = mode;
+}
+
+Qt::TextElideMode QAbstractItemView::textElideMode() const
+{
+ return d_func()->textElideMode;
+}
+
+/*!
+ \reimp
+*/
+bool QAbstractItemView::focusNextPrevChild(bool next)
+{
+ Q_D(QAbstractItemView);
+ if (d->tabKeyNavigation && isEnabled() && d->viewport->isEnabled()) {
+ QKeyEvent event(QEvent::KeyPress, next ? Qt::Key_Tab : Qt::Key_Backtab, Qt::NoModifier);
+ keyPressEvent(&event);
+ if (event.isAccepted())
+ return true;
+ }
+ return QAbstractScrollArea::focusNextPrevChild(next);
+}
+
+/*!
+ \reimp
+*/
+bool QAbstractItemView::event(QEvent *event)
+{
+ Q_D(QAbstractItemView);
+ switch (event->type()) {
+ case QEvent::Paint:
+ //we call this here because the scrollbars' visibility might be altered
+ //so this can't be done in the paintEvent method
+ d->executePostedLayout(); //make sure we set the layout properly
+ break;
+ case QEvent::Show:
+ {
+ d->executePostedLayout(); //make sure we set the layout properly
+ const QModelIndex current = currentIndex();
+ if (current.isValid() && (d->state == QAbstractItemView::EditingState || d->autoScroll))
+ scrollTo(current);
+ }
+ break;
+ case QEvent::LocaleChange:
+ viewport()->update();
+ break;
+ case QEvent::LayoutDirectionChange:
+ case QEvent::ApplicationLayoutDirectionChange:
+ updateGeometries();
+ break;
+ case QEvent::StyleChange:
+ doItemsLayout();
+ break;
+ case QEvent::FocusOut:
+ d->checkPersistentEditorFocus();
+ break;
+ default:
+ break;
+ }
+ return QAbstractScrollArea::event(event);
+}
+
+/*!
+ \fn bool QAbstractItemView::viewportEvent(QEvent *event)
+
+ This function is used to handle tool tips, and What's
+ This? mode, if the given \a event is a QEvent::ToolTip,or a
+ QEvent::WhatsThis. It passes all other
+ events on to its base class viewportEvent() handler.
+*/
+bool QAbstractItemView::viewportEvent(QEvent *event)
+{
+ Q_D(QAbstractItemView);
+ switch (event->type()) {
+ case QEvent::HoverEnter: {
+ QHoverEvent *he = static_cast<QHoverEvent*>(event);
+ d->hover = indexAt(he->pos());
+ d->viewport->update(visualRect(d->hover));
+ break; }
+ case QEvent::HoverLeave: {
+ d->viewport->update(visualRect(d->hover)); // update old
+ d->hover = QModelIndex();
+ break; }
+ case QEvent::HoverMove: {
+ QHoverEvent *he = static_cast<QHoverEvent*>(event);
+ QModelIndex old = d->hover;
+ d->hover = indexAt(he->pos());
+ if (d->hover != old)
+ d->viewport->update(visualRect(old)|visualRect(d->hover));
+ break; }
+ case QEvent::Enter:
+ d->viewportEnteredNeeded = true;
+ break;
+ case QEvent::Leave:
+ d->enteredIndex = QModelIndex();
+ break;
+ case QEvent::ToolTip:
+ case QEvent::QueryWhatsThis:
+ case QEvent::WhatsThis: {
+ QHelpEvent *he = static_cast<QHelpEvent*>(event);
+ const QModelIndex index = indexAt(he->pos());
+ QStyleOptionViewItemV4 option = d->viewOptionsV4();
+ option.rect = visualRect(index);
+ option.state |= (index == currentIndex() ? QStyle::State_HasFocus : QStyle::State_None);
+ bool retval = false;
+ // ### Qt 5: make this a normal function call to a virtual function
+ QMetaObject::invokeMethod(d->delegateForIndex(index), "helpEvent",
+ Q_RETURN_ARG(bool, retval),
+ Q_ARG(QHelpEvent *, he),
+ Q_ARG(QAbstractItemView *, this),
+ Q_ARG(QStyleOptionViewItem, option),
+ Q_ARG(QModelIndex, index));
+ return retval;
+ }
+ case QEvent::FontChange:
+ d->doDelayedItemsLayout(); // the size of the items will change
+ break;
+ case QEvent::WindowActivate:
+ case QEvent::WindowDeactivate:
+ d->viewport->update();
+ break;
+ default:
+ break;
+ }
+ return QAbstractScrollArea::viewportEvent(event);
+}
+
+/*!
+ This function is called with the given \a event when a mouse button is pressed
+ while the cursor is inside the widget. If a valid item is pressed on it is made
+ into the current item. This function emits the pressed() signal.
+*/
+void QAbstractItemView::mousePressEvent(QMouseEvent *event)
+{
+ Q_D(QAbstractItemView);
+ d->delayedAutoScroll.stop(); //any interaction with the view cancel the auto scrolling
+ QPoint pos = event->pos();
+ QPersistentModelIndex index = indexAt(pos);
+
+ if (!d->selectionModel
+ || (d->state == EditingState && d->hasEditor(index)))
+ return;
+
+ d->pressedAlreadySelected = d->selectionModel->isSelected(index);
+ d->pressedIndex = index;
+ d->pressedModifiers = event->modifiers();
+ QItemSelectionModel::SelectionFlags command = selectionCommand(index, event);
+ QPoint offset = d->offset();
+ if ((command & QItemSelectionModel::Current) == 0)
+ d->pressedPosition = pos + offset;
+
+ if (d->pressedPosition == QPoint(-1, -1))
+ d->pressedPosition = visualRect(currentIndex()).center() + offset;
+
+ if (edit(index, NoEditTriggers, event))
+ return;
+
+ if (index.isValid() && d->isIndexEnabled(index)) {
+ // we disable scrollTo for mouse press so the item doesn't change position
+ // when the user is interacting with it (ie. clicking on it)
+ bool autoScroll = d->autoScroll;
+ d->autoScroll = false;
+ d->selectionModel->setCurrentIndex(index, QItemSelectionModel::NoUpdate);
+ d->autoScroll = autoScroll;
+ QRect rect(d->pressedPosition - offset, pos);
+ setSelection(rect, command);
+
+ // signal handlers may change the model
+ emit pressed(index);
+ if (d->autoScroll) {
+ //we delay the autoscrolling to filter out double click event
+ //100 is to be sure that there won't be a double-click misinterpreted as a 2 single clicks
+ d->delayedAutoScroll.start(QApplication::doubleClickInterval()+100, this);
+ }
+
+ } else {
+ // Forces a finalize() even if mouse is pressed, but not on a item
+ d->selectionModel->select(QModelIndex(), QItemSelectionModel::Select);
+ }
+}
+
+/*!
+ This function is called with the given \a event when a mouse move event is
+ sent to the widget. If a selection is in progress and new items are moved
+ over the selection is extended; if a drag is in progress it is continued.
+*/
+void QAbstractItemView::mouseMoveEvent(QMouseEvent *event)
+{
+ Q_D(QAbstractItemView);
+ QPoint topLeft;
+ QPoint bottomRight = event->pos();
+
+ if (state() == ExpandingState || state() == CollapsingState)
+ return;
+
+#ifndef QT_NO_DRAGANDDROP
+ if (state() == DraggingState) {
+ topLeft = d->pressedPosition - d->offset();
+ if ((topLeft - bottomRight).manhattanLength() > QApplication::startDragDistance()) {
+ d->pressedIndex = QModelIndex();
+ startDrag(d->model->supportedDragActions());
+ setState(NoState); // the startDrag will return when the dnd operation is done
+ stopAutoScroll();
+ }
+ return;
+ }
+#endif // QT_NO_DRAGANDDROP
+
+ QModelIndex index = indexAt(bottomRight);
+ QModelIndex buddy = d->model->buddy(d->pressedIndex);
+ if ((state() == EditingState && d->hasEditor(buddy))
+ || edit(index, NoEditTriggers, event))
+ return;
+
+ if (d->selectionMode != SingleSelection)
+ topLeft = d->pressedPosition - d->offset();
+ else
+ topLeft = bottomRight;
+
+ if (d->viewportEnteredNeeded || d->enteredIndex != index) {
+ d->viewportEnteredNeeded = false;
+
+ // signal handlers may change the model
+ QPersistentModelIndex persistent = index;
+ if (persistent.isValid()) {
+ emit entered(persistent);
+#ifndef QT_NO_STATUSTIP
+ QString statustip = d->model->data(persistent, Qt::StatusTipRole).toString();
+ if (parent() && !statustip.isEmpty()) {
+ QStatusTipEvent tip(statustip);
+ QApplication::sendEvent(parent(), &tip);
+ }
+#endif
+ } else {
+#ifndef QT_NO_STATUSTIP
+ if (parent()) {
+ QString emptyString;
+ QStatusTipEvent tip(emptyString);
+ QApplication::sendEvent(parent(), &tip);
+ }
+#endif
+ emit viewportEntered();
+ }
+ d->enteredIndex = persistent;
+ index = persistent;
+ }
+
+#ifndef QT_NO_DRAGANDDROP
+ if (index.isValid()
+ && d->dragEnabled
+ && (state() != DragSelectingState)
+ && (event->buttons() != Qt::NoButton)
+ && !d->selectedDraggableIndexes().isEmpty()) {
+ setState(DraggingState);
+ return;
+ }
+#endif
+
+ if ((event->buttons() & Qt::LeftButton) && d->selectionAllowed(index) && d->selectionModel) {
+ setState(DragSelectingState);
+ QItemSelectionModel::SelectionFlags command = selectionCommand(index, event);
+
+ // Do the normalize ourselves, since QRect::normalized() is flawed
+ QRect selectionRect = QRect(topLeft, bottomRight);
+ QPersistentModelIndex persistent = index;
+ setSelection(selectionRect, command);
+
+ // set at the end because it might scroll the view
+ if (persistent.isValid()
+ && (persistent != d->selectionModel->currentIndex())
+ && d->isIndexEnabled(persistent))
+ d->selectionModel->setCurrentIndex(persistent, QItemSelectionModel::NoUpdate);
+ }
+}
+
+/*!
+ This function is called with the given \a event when a mouse button is released,
+ after a mouse press event on the widget. If a user presses the mouse inside your
+ widget and then drags the mouse to another location before releasing the mouse button,
+ your widget receives the release event. The function will emit the clicked() signal if an
+ item was being pressed.
+*/
+void QAbstractItemView::mouseReleaseEvent(QMouseEvent *event)
+{
+ Q_D(QAbstractItemView);
+
+ QPoint pos = event->pos();
+ QPersistentModelIndex index = indexAt(pos);
+
+ if (state() == EditingState) {
+ if (d->isIndexValid(index)
+ && d->isIndexEnabled(index)
+ && d->sendDelegateEvent(index, event))
+ d->viewport->update(visualRect(index));
+ return;
+ }
+
+ bool click = (index == d->pressedIndex && index.isValid());
+ bool selectedClicked = click && (event->button() & Qt::LeftButton) && d->pressedAlreadySelected;
+ EditTrigger trigger = (selectedClicked ? SelectedClicked : NoEditTriggers);
+ bool edited = edit(index, trigger, event);
+
+ if (d->selectionModel)
+ d->selectionModel->select(index, selectionCommand(index, event));
+
+ setState(NoState);
+
+ if (click) {
+ emit clicked(index);
+ if (edited)
+ return;
+ if (style()->styleHint(QStyle::SH_ItemView_ActivateItemOnSingleClick, 0, this))
+ emit activated(index);
+ }
+}
+
+/*!
+ This function is called with the given \a event when a mouse button is
+ double clicked inside the widget. If the double-click is on a valid item it
+ emits the doubleClicked() signal and calls edit() on the item.
+*/
+void QAbstractItemView::mouseDoubleClickEvent(QMouseEvent *event)
+{
+ Q_D(QAbstractItemView);
+
+ QModelIndex index = indexAt(event->pos());
+ if (!index.isValid()
+ || !d->isIndexEnabled(index)
+ || (d->pressedIndex != index)) {
+ QMouseEvent me(QEvent::MouseButtonPress,
+ event->pos(), event->button(),
+ event->buttons(), event->modifiers());
+ mousePressEvent(&me);
+ return;
+ }
+ // signal handlers may change the model
+ QPersistentModelIndex persistent = index;
+ emit doubleClicked(persistent);
+ if ((event->button() & Qt::LeftButton) && !edit(persistent, DoubleClicked, event)
+ && !style()->styleHint(QStyle::SH_ItemView_ActivateItemOnSingleClick, 0, this))
+ emit activated(persistent);
+}
+
+#ifndef QT_NO_DRAGANDDROP
+
+/*!
+ This function is called with the given \a event when a drag and drop operation enters
+ the widget. If the drag is over a valid dropping place (e.g. over an item that
+ accepts drops), the event is accepted; otherwise it is ignored.
+
+ \sa dropEvent() startDrag()
+*/
+void QAbstractItemView::dragEnterEvent(QDragEnterEvent *event)
+{
+ if (dragDropMode() == InternalMove
+ && (event->source() != this|| !(event->possibleActions() & Qt::MoveAction)))
+ return;
+
+ if (d_func()->canDecode(event)) {
+ event->accept();
+ setState(DraggingState);
+ } else {
+ event->ignore();
+ }
+}
+
+/*!
+ This function is called continuously with the given \a event during a drag and
+ drop operation over the widget. It can cause the view to scroll if, for example,
+ the user drags a selection to view's right or bottom edge. In this case, the
+ event will be accepted; otherwise it will be ignored.
+
+ \sa dropEvent() startDrag()
+*/
+void QAbstractItemView::dragMoveEvent(QDragMoveEvent *event)
+{
+ Q_D(QAbstractItemView);
+ if (dragDropMode() == InternalMove
+ && (event->source() != this || !(event->possibleActions() & Qt::MoveAction)))
+ return;
+
+ // ignore by default
+ event->ignore();
+
+ QModelIndex index = indexAt(event->pos());
+ d->hover = index;
+ if (!d->droppingOnItself(event, index)
+ && d->canDecode(event)) {
+
+ if (index.isValid() && d->showDropIndicator) {
+ QRect rect = visualRect(index);
+ d->dropIndicatorPosition = d->position(event->pos(), rect, index);
+ switch (d->dropIndicatorPosition) {
+ case AboveItem:
+ if (d->isIndexDropEnabled(index.parent())) {
+ d->dropIndicatorRect = QRect(rect.left(), rect.top(), rect.width(), 0);
+ event->accept();
+ } else {
+ d->dropIndicatorRect = QRect();
+ }
+ break;
+ case BelowItem:
+ if (d->isIndexDropEnabled(index.parent())) {
+ d->dropIndicatorRect = QRect(rect.left(), rect.bottom(), rect.width(), 0);
+ event->accept();
+ } else {
+ d->dropIndicatorRect = QRect();
+ }
+ break;
+ case OnItem:
+ if (d->isIndexDropEnabled(index)) {
+ d->dropIndicatorRect = rect;
+ event->accept();
+ } else {
+ d->dropIndicatorRect = QRect();
+ }
+ break;
+ case OnViewport:
+ d->dropIndicatorRect = QRect();
+ if (d->isIndexDropEnabled(rootIndex())) {
+ event->accept(); // allow dropping in empty areas
+ }
+ break;
+ }
+ } else {
+ d->dropIndicatorRect = QRect();
+ d->dropIndicatorPosition = OnViewport;
+ if (d->isIndexDropEnabled(rootIndex())) {
+ event->accept(); // allow dropping in empty areas
+ }
+ }
+ d->viewport->update();
+ } // can decode
+
+ if (d->shouldAutoScroll(event->pos()))
+ startAutoScroll();
+}
+
+/*!
+ \internal
+ Return true if this is a move from ourself and \a index is a child of the selection that
+ is being moved.
+ */
+bool QAbstractItemViewPrivate::droppingOnItself(QDropEvent *event, const QModelIndex &index)
+{
+ Q_Q(QAbstractItemView);
+ Qt::DropAction dropAction = event->dropAction();
+ if (q->dragDropMode() == QAbstractItemView::InternalMove)
+ dropAction = Qt::MoveAction;
+ if (event->source() == q
+ && event->possibleActions() & Qt::MoveAction
+ && dropAction == Qt::MoveAction) {
+ QModelIndexList selectedIndexes = q->selectedIndexes();
+ QModelIndex child = index;
+ while (child.isValid() && child != root) {
+ if (selectedIndexes.contains(child))
+ return true;
+ child = child.parent();
+ }
+ }
+ return false;
+}
+
+/*!
+ \fn void QAbstractItemView::dragLeaveEvent(QDragLeaveEvent *event)
+
+ This function is called when the item being dragged leaves the view.
+ The \a event describes the state of the drag and drop operation.
+*/
+void QAbstractItemView::dragLeaveEvent(QDragLeaveEvent *)
+{
+ Q_D(QAbstractItemView);
+ stopAutoScroll();
+ setState(NoState);
+ d->hover = QModelIndex();
+ d->viewport->update();
+}
+
+/*!
+ This function is called with the given \a event when a drop event occurs over
+ the widget. If the model accepts the even position the drop event is accepted;
+ otherwise it is ignored.
+
+ \sa startDrag()
+*/
+void QAbstractItemView::dropEvent(QDropEvent *event)
+{
+ Q_D(QAbstractItemView);
+ if (dragDropMode() == InternalMove) {
+ if (event->source() != this || !(event->possibleActions() & Qt::MoveAction))
+ return;
+ }
+
+ QModelIndex index;
+ int col = -1;
+ int row = -1;
+ if (d->dropOn(event, &row, &col, &index)) {
+ if (d->model->dropMimeData(event->mimeData(),
+ dragDropMode() == InternalMove ? Qt::MoveAction : event->dropAction(), row, col, index)) {
+ if (dragDropMode() == InternalMove)
+ event->setDropAction(Qt::MoveAction);
+ event->accept();
+ }
+ }
+ stopAutoScroll();
+ setState(NoState);
+ d->viewport->update();
+}
+
+/*!
+ If the event hasn't already been accepted, determines the index to drop on.
+
+ if (row == -1 && col == -1)
+ // append to this drop index
+ else
+ // place at row, col in drop index
+
+ If it returns true a drop can be done, and dropRow, dropCol and dropIndex reflects the position of the drop.
+ \internal
+ */
+bool QAbstractItemViewPrivate::dropOn(QDropEvent *event, int *dropRow, int *dropCol, QModelIndex *dropIndex)
+{
+ Q_Q(QAbstractItemView);
+ if (event->isAccepted())
+ return false;
+
+ QModelIndex index;
+ // rootIndex() (i.e. the viewport) might be a valid index
+ if (viewport->rect().contains(event->pos())) {
+ index = q->indexAt(event->pos());
+ if (!index.isValid() || !q->visualRect(index).contains(event->pos()))
+ index = root;
+ }
+
+ // If we are allowed to do the drop
+ if (model->supportedDropActions() & event->dropAction()) {
+ int row = -1;
+ int col = -1;
+ if (index != root) {
+ dropIndicatorPosition = position(event->pos(), q->visualRect(index), index);
+ switch (dropIndicatorPosition) {
+ case QAbstractItemView::AboveItem:
+ row = index.row();
+ col = index.column();
+ index = index.parent();
+ break;
+ case QAbstractItemView::BelowItem:
+ row = index.row() + 1;
+ col = index.column();
+ index = index.parent();
+ break;
+ case QAbstractItemView::OnItem:
+ case QAbstractItemView::OnViewport:
+ break;
+ }
+ } else {
+ dropIndicatorPosition = QAbstractItemView::OnViewport;
+ }
+ *dropIndex = index;
+ *dropRow = row;
+ *dropCol = col;
+ if (!droppingOnItself(event, index))
+ return true;
+ }
+ return false;
+}
+
+QAbstractItemView::DropIndicatorPosition
+QAbstractItemViewPrivate::position(const QPoint &pos, const QRect &rect, const QModelIndex &index) const
+{
+ QAbstractItemView::DropIndicatorPosition r = QAbstractItemView::OnViewport;
+ if (!overwrite) {
+ const int margin = 2;
+ if (pos.y() - rect.top() < margin) {
+ r = QAbstractItemView::AboveItem;
+ } else if (rect.bottom() - pos.y() < margin) {
+ r = QAbstractItemView::BelowItem;
+ } else if (rect.contains(pos, true)) {
+ r = QAbstractItemView::OnItem;
+ }
+ } else {
+ QRect touchingRect = rect;
+ touchingRect.adjust(-1, -1, 1, 1);
+ if (touchingRect.contains(pos, false)) {
+ r = QAbstractItemView::OnItem;
+ }
+ }
+
+ if (r == QAbstractItemView::OnItem && (!(model->flags(index) & Qt::ItemIsDropEnabled)))
+ r = pos.y() < rect.center().y() ? QAbstractItemView::AboveItem : QAbstractItemView::BelowItem;
+
+ return r;
+}
+
+#endif // QT_NO_DRAGANDDROP
+
+/*!
+ This function is called with the given \a event when the widget obtains the focus.
+ By default, the event is ignored.
+
+ \sa setFocus(), focusOutEvent()
+*/
+void QAbstractItemView::focusInEvent(QFocusEvent *event)
+{
+ Q_D(QAbstractItemView);
+ QAbstractScrollArea::focusInEvent(event);
+ if (selectionModel()
+ && !d->currentIndexSet
+ && !currentIndex().isValid()) {
+ bool autoScroll = d->autoScroll;
+ d->autoScroll = false;
+ QModelIndex index = moveCursor(MoveNext, Qt::NoModifier); // first visible index
+ if (index.isValid() && d->isIndexEnabled(index))
+ selectionModel()->setCurrentIndex(index, QItemSelectionModel::NoUpdate);
+ d->autoScroll = autoScroll;
+ }
+ d->viewport->update();
+}
+
+/*!
+ This function is called with the given \a event when the widget
+ looses the focus. By default, the event is ignored.
+
+ \sa clearFocus(), focusInEvent()
+*/
+void QAbstractItemView::focusOutEvent(QFocusEvent *event)
+{
+ Q_D(QAbstractItemView);
+ QAbstractScrollArea::focusOutEvent(event);
+ d->viewport->update();
+}
+
+/*!
+ This function is called with the given \a event when a key event is sent to
+ the widget. The default implementation handles basic cursor movement, e.g. Up,
+ Down, Left, Right, Home, PageUp, and PageDown; the activated() signal is
+ emitted if the current index is valid and the activation key is pressed
+ (e.g. Enter or Return, depending on the platform).
+ This function is where editing is initiated by key press, e.g. if F2 is
+ pressed.
+
+ \sa edit(), moveCursor(), keyboardSearch(), tabKeyNavigation
+*/
+void QAbstractItemView::keyPressEvent(QKeyEvent *event)
+{
+ Q_D(QAbstractItemView);
+ d->delayedAutoScroll.stop(); //any interaction with the view cancel the auto scrolling
+
+#ifdef QT_KEYPAD_NAVIGATION
+ switch (event->key()) {
+ case Qt::Key_Select:
+ if (QApplication::keypadNavigationEnabled()) {
+ if (!hasEditFocus()) {
+ setEditFocus(true);
+ return;
+ }
+ }
+ break;
+ case Qt::Key_Back:
+ if (QApplication::keypadNavigationEnabled() && hasEditFocus())
+ setEditFocus(false);
+ else
+ event->ignore();
+ return;
+ default:
+ if (QApplication::keypadNavigationEnabled() && !hasEditFocus()) {
+ event->ignore();
+ return;
+ }
+ }
+#endif
+
+#if !defined(QT_NO_CLIPBOARD) && !defined(QT_NO_SHORTCUT)
+ if (event == QKeySequence::Copy) {
+ QVariant variant;
+ if (d->model)
+ variant = d->model->data(currentIndex(), Qt::DisplayRole);
+ if (variant.type() == QVariant::String)
+ QApplication::clipboard()->setText(variant.toString());
+ event->accept();
+ }
+#endif
+
+ QPersistentModelIndex newCurrent;
+ switch (event->key()) {
+ case Qt::Key_Down:
+ newCurrent = moveCursor(MoveDown, event->modifiers());
+ break;
+ case Qt::Key_Up:
+ newCurrent = moveCursor(MoveUp, event->modifiers());
+ break;
+ case Qt::Key_Left:
+ newCurrent = moveCursor(MoveLeft, event->modifiers());
+ break;
+ case Qt::Key_Right:
+ newCurrent = moveCursor(MoveRight, event->modifiers());
+ break;
+ case Qt::Key_Home:
+ newCurrent = moveCursor(MoveHome, event->modifiers());
+ break;
+ case Qt::Key_End:
+ newCurrent = moveCursor(MoveEnd, event->modifiers());
+ break;
+ case Qt::Key_PageUp:
+ newCurrent = moveCursor(MovePageUp, event->modifiers());
+ break;
+ case Qt::Key_PageDown:
+ newCurrent = moveCursor(MovePageDown, event->modifiers());
+ break;
+ case Qt::Key_Tab:
+ if (d->tabKeyNavigation)
+ newCurrent = moveCursor(MoveNext, event->modifiers());
+ break;
+ case Qt::Key_Backtab:
+ if (d->tabKeyNavigation)
+ newCurrent = moveCursor(MovePrevious, event->modifiers());
+ break;
+ }
+
+ QPersistentModelIndex oldCurrent = currentIndex();
+ if (newCurrent != oldCurrent && newCurrent.isValid() && d->isIndexEnabled(newCurrent)) {
+ if (!hasFocus() && QApplication::focusWidget() == indexWidget(oldCurrent))
+ setFocus();
+ QItemSelectionModel::SelectionFlags command = selectionCommand(newCurrent, event);
+ if (command != QItemSelectionModel::NoUpdate
+ || style()->styleHint(QStyle::SH_ItemView_MovementWithoutUpdatingSelection, 0, this)) {
+ // note that we don't check if the new current index is enabled because moveCursor() makes sure it is
+ if (command & QItemSelectionModel::Current) {
+ d->selectionModel->setCurrentIndex(newCurrent, QItemSelectionModel::NoUpdate);
+ if (d->pressedPosition == QPoint(-1, -1))
+ d->pressedPosition = visualRect(oldCurrent).center();
+ QRect rect(d->pressedPosition - d->offset(), visualRect(newCurrent).center());
+ setSelection(rect, command);
+ } else {
+ d->selectionModel->setCurrentIndex(newCurrent, command);
+ d->pressedPosition = visualRect(newCurrent).center() + d->offset();
+ }
+ return;
+ }
+ }
+
+ switch (event->key()) {
+ // ignored keys
+ case Qt::Key_Down:
+ case Qt::Key_Up:
+#ifdef QT_KEYPAD_NAVIGATION
+ if (QApplication::keypadNavigationEnabled()) {
+ event->accept(); // don't change focus
+ break;
+ }
+#endif
+ case Qt::Key_Left:
+ case Qt::Key_Right:
+ case Qt::Key_Home:
+ case Qt::Key_End:
+ case Qt::Key_PageUp:
+ case Qt::Key_PageDown:
+ case Qt::Key_Escape:
+ case Qt::Key_Shift:
+ case Qt::Key_Control:
+ event->ignore();
+ break;
+ case Qt::Key_Space:
+ case Qt::Key_Select:
+ if (!edit(currentIndex(), AnyKeyPressed, event) && d->selectionModel)
+ d->selectionModel->select(currentIndex(), selectionCommand(currentIndex(), event));
+#ifdef QT_KEYPAD_NAVIGATION
+ if ( event->key()==Qt::Key_Select ) {
+ // Also do Key_Enter action.
+ if (currentIndex().isValid()) {
+ if (state() != EditingState)
+ emit activated(currentIndex());
+ } else {
+ event->ignore();
+ }
+ }
+#endif
+ break;
+#ifdef Q_WS_MAC
+ case Qt::Key_Enter:
+ case Qt::Key_Return:
+ // Propagate the enter if you couldn't edit the item and there are no
+ // current editors (if there are editors, the event was most likely propagated from it).
+ if (!edit(currentIndex(), EditKeyPressed, event) && d->editors.isEmpty())
+ event->ignore();
+ break;
+#else
+ case Qt::Key_F2:
+ if (!edit(currentIndex(), EditKeyPressed, event))
+ event->ignore();
+ break;
+ case Qt::Key_Enter:
+ case Qt::Key_Return:
+ // ### we can't open the editor on enter, becuse
+ // some widgets will forward the enter event back
+ // to the viewport, starting an endless loop
+ if (state() != EditingState || hasFocus()) {
+ if (currentIndex().isValid())
+ emit activated(currentIndex());
+ event->ignore();
+ }
+ break;
+#endif
+ case Qt::Key_A:
+ if (event->modifiers() & Qt::ControlModifier) {
+ selectAll();
+ break;
+ }
+ default: {
+#ifdef Q_WS_MAC
+ if (event->key() == Qt::Key_O && event->modifiers() & Qt::ControlModifier && currentIndex().isValid()) {
+ emit activated(currentIndex());
+ break;
+ }
+#endif
+ bool modified = (event->modifiers() & (Qt::ControlModifier | Qt::AltModifier | Qt::MetaModifier));
+ if (!event->text().isEmpty() && !modified) {
+ if (!edit(currentIndex(), AnyKeyPressed, event))
+ keyboardSearch(event->text());
+ }
+ event->ignore();
+ break; }
+ }
+}
+
+/*!
+ This function is called with the given \a event when a resize event is sent to
+ the widget.
+
+ \sa QWidget::resizeEvent()
+*/
+void QAbstractItemView::resizeEvent(QResizeEvent *event)
+{
+ QAbstractScrollArea::resizeEvent(event);
+ updateGeometries();
+}
+
+/*!
+ This function is called with the given \a event when a timer event is sent
+ to the widget.
+
+ \sa QObject::timerEvent()
+*/
+void QAbstractItemView::timerEvent(QTimerEvent *event)
+{
+ Q_D(QAbstractItemView);
+ if (event->timerId() == d->autoScrollTimer.timerId())
+ doAutoScroll();
+ else if (event->timerId() == d->updateTimer.timerId())
+ d->updateDirtyRegion();
+ else if (event->timerId() == d->delayedEditing.timerId()) {
+ d->delayedEditing.stop();
+ edit(currentIndex());
+ } else if (event->timerId() == d->delayedLayout.timerId()) {
+ d->delayedLayout.stop();
+ if (isVisible()) {
+ d->interruptDelayedItemsLayout();
+ doItemsLayout();
+ const QModelIndex current = currentIndex();
+ if (current.isValid() && d->state == QAbstractItemView::EditingState)
+ scrollTo(current);
+ }
+ } else if (event->timerId() == d->delayedAutoScroll.timerId()) {
+ d->delayedAutoScroll.stop();
+ //end of the timer: if the current item is still the same as the one when the mouse press occurred
+ //we only get here if there was no double click
+ if (d->pressedIndex.isValid() && d->pressedIndex == currentIndex())
+ scrollTo(d->pressedIndex);
+ }
+}
+
+/*!
+ \reimp
+*/
+void QAbstractItemView::inputMethodEvent(QInputMethodEvent *event)
+{
+ if (event->commitString().isEmpty() && event->preeditString().isEmpty()) {
+ event->ignore();
+ return;
+ }
+ if (!edit(currentIndex(), AnyKeyPressed, event)) {
+ if (!event->commitString().isEmpty())
+ keyboardSearch(event->commitString());
+ event->ignore();
+ }
+}
+
+#ifndef QT_NO_DRAGANDDROP
+/*!
+ \enum QAbstractItemView::DropIndicatorPosition
+
+ This enum indicates the position of the drop indicator in
+ relation to the index at the current mouse position:
+
+ \value OnItem The item will be dropped on the index.
+
+ \value AboveItem The item will be dropped above the index.
+
+ \value BelowItem The item will be dropped below the index.
+
+ \value OnViewport The item will be dropped onto a region of the viewport with
+no items. The way each view handles items dropped onto the viewport depends on
+the behavior of the underlying model in use.
+*/
+
+
+/*!
+ \since 4.1
+
+ Returns the position of the drop indicator in relation to the closest item.
+*/
+QAbstractItemView::DropIndicatorPosition QAbstractItemView::dropIndicatorPosition() const
+{
+ Q_D(const QAbstractItemView);
+ return d->dropIndicatorPosition;
+}
+#endif
+
+/*!
+ This convenience function returns a list of all selected and
+ non-hidden item indexes in the view. The list contains no
+ duplicates, and is not sorted.
+
+ \sa QItemSelectionModel::selectedIndexes()
+*/
+QModelIndexList QAbstractItemView::selectedIndexes() const
+{
+ Q_D(const QAbstractItemView);
+ QModelIndexList indexes;
+ if (d->selectionModel) {
+ indexes = d->selectionModel->selectedIndexes();
+ QList<QModelIndex>::iterator it = indexes.begin();
+ while (it != indexes.end())
+ if (isIndexHidden(*it))
+ it = indexes.erase(it);
+ else
+ ++it;
+ }
+ return indexes;
+}
+
+/*!
+ Starts editing the item at \a index, creating an editor if
+ necessary, and returns true if the view's \l{State} is now
+ EditingState; otherwise returns false.
+
+ The action that caused the editing process is described by
+ \a trigger, and the associated event is specified by \a event.
+
+ Editing can be forced by specifying the \a trigger to be
+ QAbstractItemView::AllEditTriggers.
+
+ \sa closeEditor()
+*/
+bool QAbstractItemView::edit(const QModelIndex &index, EditTrigger trigger, QEvent *event)
+{
+ Q_D(QAbstractItemView);
+
+ if (!d->isIndexValid(index))
+ return false;
+
+ if (QWidget *w = (d->persistent.isEmpty() ? static_cast<QWidget*>(0) : d->editorForIndex(index).editor.data())) {
+ if (w->focusPolicy() == Qt::NoFocus)
+ return false;
+ w->setFocus();
+ return true;
+ }
+
+ if (trigger == DoubleClicked) {
+ d->delayedEditing.stop();
+ d->delayedAutoScroll.stop();
+ } else if (trigger == CurrentChanged) {
+ d->delayedEditing.stop();
+ }
+
+ if (d->sendDelegateEvent(index, event)) {
+ d->viewport->update(visualRect(index));
+ return true;
+ }
+
+ // save the previous trigger before updating
+ EditTriggers lastTrigger = d->lastTrigger;
+ d->lastTrigger = trigger;
+
+ if (!d->shouldEdit(trigger, d->model->buddy(index)))
+ return false;
+
+ if (d->delayedEditing.isActive())
+ return false;
+
+ // we will receive a mouseButtonReleaseEvent after a
+ // mouseDoubleClickEvent, so we need to check the previous trigger
+ if (lastTrigger == DoubleClicked && trigger == SelectedClicked)
+ return false;
+
+ // we may get a double click event later
+ if (trigger == SelectedClicked)
+ d->delayedEditing.start(QApplication::doubleClickInterval(), this);
+ else
+ d->openEditor(index, d->shouldForwardEvent(trigger, event) ? event : 0);
+
+ return true;
+}
+
+/*!
+ \internal
+ Updates the data shown in the open editor widgets in the view.
+*/
+void QAbstractItemView::updateEditorData()
+{
+ Q_D(QAbstractItemView);
+ d->updateEditorData(QModelIndex(), QModelIndex());
+}
+
+/*!
+ \internal
+ Updates the geometry of the open editor widgets in the view.
+*/
+void QAbstractItemView::updateEditorGeometries()
+{
+ Q_D(QAbstractItemView);
+ if(d->editors.isEmpty())
+ return;
+ QStyleOptionViewItemV4 option = d->viewOptionsV4();
+ QList<QEditorInfo>::iterator it = d->editors.begin();
+ QWidgetList editorsToRelease;
+ while (it != d->editors.end()) {
+ QModelIndex index = it->index;
+ QWidget *editor = it->editor;
+ if (index.isValid() && editor) {
+ option.rect = visualRect(index);
+ if (option.rect.isValid()) {
+ editor->show();
+ QAbstractItemDelegate *delegate = d->delegateForIndex(index);
+ if (delegate)
+ delegate->updateEditorGeometry(editor, option, index);
+ } else {
+ editor->hide();
+ }
+ ++it;
+ } else {
+ it = d->editors.erase(it);
+ editorsToRelease << editor;
+ }
+ }
+
+ //we release the editor outside of the loop because it might change the focus and try
+ //to change the d->editors list.
+ foreach(QWidget *editor, editorsToRelease) {
+ d->releaseEditor(editor);
+ }
+}
+
+/*!
+ \since 4.4
+
+ Updates the geometry of the child widgets of the view.
+*/
+void QAbstractItemView::updateGeometries()
+{
+ updateEditorGeometries();
+ QMetaObject::invokeMethod(this, "_q_fetchMore", Qt::QueuedConnection);
+}
+
+/*!
+ \internal
+*/
+void QAbstractItemView::verticalScrollbarValueChanged(int value)
+{
+ Q_D(QAbstractItemView);
+ if (verticalScrollBar()->maximum() == value && d->model->canFetchMore(d->root))
+ d->model->fetchMore(d->root);
+}
+
+/*!
+ \internal
+*/
+void QAbstractItemView::horizontalScrollbarValueChanged(int value)
+{
+ Q_D(QAbstractItemView);
+ if (horizontalScrollBar()->maximum() == value && d->model->canFetchMore(d->root))
+ d->model->fetchMore(d->root);
+}
+
+/*!
+ \internal
+*/
+void QAbstractItemView::verticalScrollbarAction(int)
+{
+ //do nothing
+}
+
+/*!
+ \internal
+*/
+void QAbstractItemView::horizontalScrollbarAction(int)
+{
+ //do nothing
+}
+
+/*!
+ Closes the given \a editor, and releases it. The \a hint is
+ used to specify how the view should respond to the end of the editing
+ operation. For example, the hint may indicate that the next item in
+ the view should be opened for editing.
+
+ \sa edit(), commitData()
+*/
+
+void QAbstractItemView::closeEditor(QWidget *editor, QAbstractItemDelegate::EndEditHint hint)
+{
+ Q_D(QAbstractItemView);
+
+ // Close the editor
+ if (editor) {
+ bool isPersistent = d->persistent.contains(editor);
+ bool hadFocus = editor->hasFocus();
+ QModelIndex index = d->indexForEditor(editor);
+ if (!index.isValid())
+ return; // the editor was not registered
+
+ if (!isPersistent) {
+ setState(NoState);
+ QModelIndex index = d->indexForEditor(editor);
+ editor->removeEventFilter(d->delegateForIndex(index));
+ d->removeEditor(editor);
+ }
+ if (hadFocus)
+ setFocus(); // this will send a focusLost event to the editor
+ else
+ d->checkPersistentEditorFocus();
+
+ QPointer<QWidget> ed = editor;
+ QApplication::sendPostedEvents(editor, 0);
+ editor = ed;
+
+ if (!isPersistent && editor)
+ d->releaseEditor(editor);
+ }
+
+ // The EndEditHint part
+ QItemSelectionModel::SelectionFlags flags = QItemSelectionModel::ClearAndSelect
+ | d->selectionBehaviorFlags();
+ switch (hint) {
+ case QAbstractItemDelegate::EditNextItem: {
+ QModelIndex index = moveCursor(MoveNext, Qt::NoModifier);
+ if (index.isValid()) {
+ QPersistentModelIndex persistent(index);
+ d->selectionModel->setCurrentIndex(persistent, flags);
+ // currentChanged signal would have already started editing
+ if (index.flags() & Qt::ItemIsEditable
+ && (!(editTriggers() & QAbstractItemView::CurrentChanged)))
+ edit(persistent);
+ } break; }
+ case QAbstractItemDelegate::EditPreviousItem: {
+ QModelIndex index = moveCursor(MovePrevious, Qt::NoModifier);
+ if (index.isValid()) {
+ QPersistentModelIndex persistent(index);
+ d->selectionModel->setCurrentIndex(persistent, flags);
+ // currentChanged signal would have already started editing
+ if (index.flags() & Qt::ItemIsEditable
+ && (!(editTriggers() & QAbstractItemView::CurrentChanged)))
+ edit(persistent);
+ } break; }
+ case QAbstractItemDelegate::SubmitModelCache:
+ d->model->submit();
+ break;
+ case QAbstractItemDelegate::RevertModelCache:
+ d->model->revert();
+ break;
+ default:
+ break;
+ }
+}
+
+/*!
+ Commit the data in the \a editor to the model.
+
+ \sa closeEditor()
+*/
+void QAbstractItemView::commitData(QWidget *editor)
+{
+ Q_D(QAbstractItemView);
+ if (!editor || !d->itemDelegate || d->currentlyCommittingEditor)
+ return;
+ QModelIndex index = d->indexForEditor(editor);
+ if (!index.isValid())
+ return;
+ d->currentlyCommittingEditor = editor;
+ QAbstractItemDelegate *delegate = d->delegateForIndex(index);
+ editor->removeEventFilter(delegate);
+ delegate->setModelData(editor, d->model, index);
+ editor->installEventFilter(delegate);
+ d->currentlyCommittingEditor = 0;
+}
+
+/*!
+ This function is called when the given \a editor has been destroyed.
+
+ \sa closeEditor()
+*/
+void QAbstractItemView::editorDestroyed(QObject *editor)
+{
+ Q_D(QAbstractItemView);
+ QWidget *w = qobject_cast<QWidget*>(editor);
+ d->removeEditor(w);
+ d->persistent.remove(w);
+ if (state() == EditingState)
+ setState(NoState);
+}
+
+/*!
+ \obsolete
+ Sets the horizontal scroll bar's steps per item to \a steps.
+
+ This is the number of steps used by the horizontal scroll bar to
+ represent the width of an item.
+
+ Note that if the view has a horizontal header, the item steps
+ will be ignored and the header section size will be used instead.
+
+ \sa horizontalStepsPerItem() setVerticalStepsPerItem()
+*/
+void QAbstractItemView::setHorizontalStepsPerItem(int steps)
+{
+ Q_UNUSED(steps);
+ // do nothing
+}
+
+/*!
+ \obsolete
+ Returns the horizontal scroll bar's steps per item.
+
+ \sa setHorizontalStepsPerItem() verticalStepsPerItem()
+*/
+int QAbstractItemView::horizontalStepsPerItem() const
+{
+ return 1;
+}
+
+/*!
+ \obsolete
+ Sets the vertical scroll bar's steps per item to \a steps.
+
+ This is the number of steps used by the vertical scroll bar to
+ represent the height of an item.
+
+ Note that if the view has a vertical header, the item steps
+ will be ignored and the header section size will be used instead.
+
+ \sa verticalStepsPerItem() setHorizontalStepsPerItem()
+*/
+void QAbstractItemView::setVerticalStepsPerItem(int steps)
+{
+ Q_UNUSED(steps);
+ // do nothing
+}
+
+/*!
+ \obsolete
+ Returns the vertical scroll bar's steps per item.
+
+ \sa setVerticalStepsPerItem() horizontalStepsPerItem()
+*/
+int QAbstractItemView::verticalStepsPerItem() const
+{
+ return 1;
+}
+
+/*!
+ Moves to and selects the item best matching the string \a search.
+ If no item is found nothing happens.
+
+ In the default implementation, the search is reset if \a search is empty, or
+ the time interval since the last search has exceeded
+ QApplication::keyboardInputInterval().
+*/
+void QAbstractItemView::keyboardSearch(const QString &search)
+{
+ Q_D(QAbstractItemView);
+ if (!d->model->rowCount(d->root) || !d->model->columnCount(d->root))
+ return;
+
+ QModelIndex start = currentIndex().isValid() ? currentIndex()
+ : d->model->index(0, 0, d->root);
+ QTime now(QTime::currentTime());
+ bool skipRow = false;
+ if (search.isEmpty()
+ || (d->keyboardInputTime.msecsTo(now) > QApplication::keyboardInputInterval())) {
+ d->keyboardInput = search;
+ skipRow = true;
+ } else {
+ d->keyboardInput += search;
+ }
+ d->keyboardInputTime = now;
+
+ // special case for searches with same key like 'aaaaa'
+ bool sameKey = false;
+ if (d->keyboardInput.length() > 1) {
+ int c = d->keyboardInput.count(d->keyboardInput.at(d->keyboardInput.length() - 1));
+ sameKey = (c == d->keyboardInput.length());
+ if (sameKey)
+ skipRow = true;
+ }
+
+ // skip if we are searching for the same key or a new search started
+ if (skipRow) {
+ QModelIndex parent = start.parent();
+ int newRow = (start.row() < d->model->rowCount(parent) - 1) ? start.row() + 1 : 0;
+ start = d->model->index(newRow, start.column(), parent);
+ }
+
+ // search from start with wraparound
+ const QString searchString = sameKey ? QString(d->keyboardInput.at(0)) : d->keyboardInput;
+ QModelIndex current = start;
+ QModelIndexList match;
+ QModelIndex firstMatch;
+ QModelIndexList previous;
+ do {
+ match = d->model->match(current, Qt::DisplayRole, searchString);
+ if (match == previous)
+ break;
+ firstMatch = match.value(0);
+ previous = match;
+ if (firstMatch.isValid()) {
+ if (d->isIndexEnabled(firstMatch)) {
+ setCurrentIndex(firstMatch);
+ break;
+ }
+ int row = firstMatch.row() + 1;
+ if (row >= d->model->rowCount(firstMatch.parent()))
+ row = 0;
+ current = firstMatch.sibling(row, firstMatch.column());
+ }
+ } while (current != start && firstMatch.isValid());
+}
+
+/*!
+ Returns the size hint for the item with the specified \a index or
+ an invalid size for invalid indexes.
+
+ \sa sizeHintForRow(), sizeHintForColumn()
+*/
+QSize QAbstractItemView::sizeHintForIndex(const QModelIndex &index) const
+{
+ Q_D(const QAbstractItemView);
+ if (!d->isIndexValid(index) || !d->itemDelegate)
+ return QSize();
+ return d->delegateForIndex(index)->sizeHint(d->viewOptionsV4(), index);
+}
+
+/*!
+ Returns the height size hint for the specified \a row or -1 if
+ there is no model.
+
+ The returned height is calculated using the size hints of the
+ given \a row's items, i.e. the returned value is the maximum
+ height among the items. Note that to control the height of a row,
+ you must reimplement the QAbstractItemDelegate::sizeHint()
+ function.
+
+ This function is used in views with a vertical header to find the
+ size hint for a header section based on the contents of the given
+ \a row.
+
+ \sa sizeHintForColumn()
+*/
+int QAbstractItemView::sizeHintForRow(int row) const
+{
+ Q_D(const QAbstractItemView);
+
+ if (row < 0 || row >= d->model->rowCount() || !model())
+ return -1;
+
+ QStyleOptionViewItemV4 option = d->viewOptionsV4();
+ int height = 0;
+ int colCount = d->model->columnCount(d->root);
+ QModelIndex index;
+ for (int c = 0; c < colCount; ++c) {
+ index = d->model->index(row, c, d->root);
+ if (QWidget *editor = d->editorForIndex(index).editor)
+ height = qMax(height, editor->size().height());
+ int hint = d->delegateForIndex(index)->sizeHint(option, index).height();
+ height = qMax(height, hint);
+ }
+ return height;
+}
+
+/*!
+ Returns the width size hint for the specified \a column or -1 if there is no model.
+
+ This function is used in views with a horizontal header to find the size hint for
+ a header section based on the contents of the given \a column.
+
+ \sa sizeHintForRow()
+*/
+int QAbstractItemView::sizeHintForColumn(int column) const
+{
+ Q_D(const QAbstractItemView);
+
+ if (column < 0 || column >= d->model->columnCount() || !model())
+ return -1;
+
+ QStyleOptionViewItemV4 option = d->viewOptionsV4();
+ int width = 0;
+ int rows = d->model->rowCount(d->root);
+ QModelIndex index;
+ for (int r = 0; r < rows; ++r) {
+ index = d->model->index(r, column, d->root);
+ if (QWidget *editor = d->editorForIndex(index).editor)
+ width = qMax(width, editor->sizeHint().width());
+ int hint = d->delegateForIndex(index)->sizeHint(option, index).width();
+ width = qMax(width, hint);
+ }
+ return width;
+}
+
+/*!
+ Opens a persistent editor on the item at the given \a index.
+ If no editor exists, the delegate will create a new editor.
+
+ \sa closePersistentEditor()
+*/
+void QAbstractItemView::openPersistentEditor(const QModelIndex &index)
+{
+ Q_D(QAbstractItemView);
+ QStyleOptionViewItemV4 options = d->viewOptionsV4();
+ options.rect = visualRect(index);
+ options.state |= (index == currentIndex() ? QStyle::State_HasFocus : QStyle::State_None);
+
+ QWidget *editor = d->editor(index, options);
+ if (editor) {
+ editor->show();
+ d->persistent.insert(editor);
+ }
+}
+
+/*!
+ Closes the persistent editor for the item at the given \a index.
+
+ \sa openPersistentEditor()
+*/
+void QAbstractItemView::closePersistentEditor(const QModelIndex &index)
+{
+ Q_D(QAbstractItemView);
+ QWidget *editor = d->editorForIndex(index).editor;
+ if (editor) {
+ if (index == selectionModel()->currentIndex())
+ closeEditor(editor, QAbstractItemDelegate::RevertModelCache);
+ d->persistent.remove(editor);
+ d->removeEditor(editor);
+ d->releaseEditor(editor);
+ }
+}
+
+/*!
+ \since 4.1
+
+ Sets the given \a widget on the item at the given \a index, passing the
+ ownership of the widget to the viewport.
+
+ If \a index is invalid (e.g., if you pass the root index), this function
+ will do nothing.
+
+ The given \a widget's \l{QWidget}{autoFillBackground} property must be set
+ to true, otherwise the widget's background will be transparent, showing
+ both the model data and the item at the given \a index.
+
+ If index widget A is replaced with index widget B, index widget A will be
+ deleted. For example, in the code snippet below, the QLineEdit object will
+ be deleted.
+
+ \snippet doc/src/snippets/code/src_gui_itemviews_qabstractitemview.cpp 1
+
+ This function should only be used to display static content within the
+ visible area corresponding to an item of data. If you want to display
+ custom dynamic content or implement a custom editor widget, subclass
+ QItemDelegate instead.
+
+ \sa {Delegate Classes}
+*/
+void QAbstractItemView::setIndexWidget(const QModelIndex &index, QWidget *widget)
+{
+ Q_D(QAbstractItemView);
+ if (!d->isIndexValid(index))
+ return;
+ if (QWidget *oldWidget = indexWidget(index)) {
+ d->removeEditor(oldWidget);
+ oldWidget->deleteLater();
+ }
+ if (widget) {
+ widget->setParent(viewport());
+ d->persistent.insert(widget);
+ d->addEditor(index, widget, true);
+ widget->show();
+ if (!d->delayedPendingLayout)
+ widget->setGeometry(visualRect(index));
+ dataChanged(index, index); // update the geometry
+ }
+}
+
+/*!
+ \since 4.1
+
+ Returns the widget for the item at the given \a index.
+*/
+QWidget* QAbstractItemView::indexWidget(const QModelIndex &index) const
+{
+ Q_D(const QAbstractItemView);
+ if (!d->isIndexValid(index))
+ return 0;
+ return d->editorForIndex(index).editor;
+}
+
+/*!
+ \since 4.1
+
+ Scrolls the view to the top.
+
+ \sa scrollTo(), scrollToBottom()
+*/
+void QAbstractItemView::scrollToTop()
+{
+ verticalScrollBar()->setValue(verticalScrollBar()->minimum());
+}
+
+/*!
+ \since 4.1
+
+ Scrolls the view to the bottom.
+
+ \sa scrollTo(), scrollToTop()
+*/
+void QAbstractItemView::scrollToBottom()
+{
+ Q_D(QAbstractItemView);
+ if (d->delayedPendingLayout) {
+ d->executePostedLayout();
+ updateGeometries();
+ }
+ verticalScrollBar()->setValue(verticalScrollBar()->maximum());
+}
+
+/*!
+ \since 4.3
+
+ Updates the area occupied by the given \a index.
+
+*/
+void QAbstractItemView::update(const QModelIndex &index)
+{
+ Q_D(QAbstractItemView);
+ if (index.isValid())
+ d->viewport->update(visualRect(index));
+}
+
+/*!
+ This slot is called when items are changed in the model. The
+ changed items are those from \a topLeft to \a bottomRight
+ inclusive. If just one item is changed \a topLeft == \a
+ bottomRight.
+*/
+void QAbstractItemView::dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight)
+{
+ // Single item changed
+ Q_D(QAbstractItemView);
+ if (topLeft == bottomRight && topLeft.isValid()) {
+ const QEditorInfo editorInfo = d->editorForIndex(topLeft);
+ //we don't update the edit data if it is static
+ if (!editorInfo.isStatic && editorInfo.editor) {
+ QAbstractItemDelegate *delegate = d->delegateForIndex(topLeft);
+ if (delegate) {
+ delegate->setEditorData(editorInfo.editor, topLeft);
+ }
+ }
+ if (isVisible() && !d->delayedPendingLayout) {
+ // otherwise the items will be update later anyway
+ d->viewport->update(visualRect(topLeft));
+ }
+ return;
+ }
+ d->updateEditorData(topLeft, bottomRight);
+ if (!isVisible() || d->delayedPendingLayout)
+ return; // no need to update
+ d->viewport->update();
+}
+
+/*!
+ This slot is called when rows are inserted. The new rows are those
+ under the given \a parent from \a start to \a end inclusive. The
+ base class implementation calls fetchMore() on the model to check
+ for more data.
+
+ \sa rowsAboutToBeRemoved()
+*/
+void QAbstractItemView::rowsInserted(const QModelIndex &, int, int)
+{
+ if (!isVisible())
+ QMetaObject::invokeMethod(this, "_q_fetchMore", Qt::QueuedConnection);
+ else
+ updateEditorGeometries();
+}
+
+/*!
+ This slot is called when rows are about to be removed. The deleted rows are
+ those under the given \a parent from \a start to \a end inclusive.
+
+ \sa rowsInserted()
+*/
+void QAbstractItemView::rowsAboutToBeRemoved(const QModelIndex &parent, int start, int end)
+{
+ Q_D(QAbstractItemView);
+
+ setState(CollapsingState);
+
+ // Ensure one selected item in single selection mode.
+ QModelIndex current = currentIndex();
+ if (d->selectionMode == SingleSelection
+ && current.isValid()
+ && current.row() >= start
+ && current.row() <= end
+ && current.parent() == parent) {
+ int totalToRemove = end - start + 1;
+ if (d->model->rowCount(parent) <= totalToRemove) { // no more children
+ QModelIndex index = parent;
+ while (index != d->root && !d->isIndexEnabled(index))
+ index = index.parent();
+ if (index != d->root)
+ setCurrentIndex(index);
+ } else {
+ int row = end + 1;
+ QModelIndex next;
+ do { // find the next visible and enabled item
+ next = d->model->index(row++, current.column(), current.parent());
+ } while (next.isValid() && (isIndexHidden(next) || !d->isIndexEnabled(next)));
+ if (row > d->model->rowCount(parent)) {
+ row = start - 1;
+ do { // find the previous visible and enabled item
+ next = d->model->index(row--, current.column(), current.parent());
+ } while (next.isValid() && (isIndexHidden(next) || !d->isIndexEnabled(next)));
+ }
+ setCurrentIndex(next);
+ }
+ }
+
+ // Remove all affected editors; this is more efficient than waiting for updateGeometries() to clean out editors for invalid indexes
+ for (int i = d->editors.size() - 1; i >= 0; --i) {
+ const QModelIndex index = d->editors.at(i).index;
+ QWidget *editor = d->editors.at(i).editor;
+ if (index.row() >= start && index.row() <= end && d->model->parent(index) == parent) {
+ d->editors.removeAt(i);
+ d->releaseEditor(editor);
+ }
+ }
+}
+
+/*!
+ \internal
+
+ This slot is called when rows have been removed. The deleted
+ rows are those under the given \a parent from \a start to \a end
+ inclusive.
+*/
+void QAbstractItemViewPrivate::_q_rowsRemoved(const QModelIndex &, int, int)
+{
+ Q_Q(QAbstractItemView);
+ if (q->isVisible())
+ q->updateEditorGeometries();
+ q->setState(QAbstractItemView::NoState);
+}
+
+/*!
+ \internal
+
+ This slot is called when columns are about to be removed. The deleted
+ columns are those under the given \a parent from \a start to \a end
+ inclusive.
+*/
+void QAbstractItemViewPrivate::_q_columnsAboutToBeRemoved(const QModelIndex &parent, int start, int end)
+{
+ Q_Q(QAbstractItemView);
+
+ q->setState(QAbstractItemView::CollapsingState);
+
+ // Ensure one selected item in single selection mode.
+ QModelIndex current = q->currentIndex();
+ if (current.isValid()
+ && selectionMode == QAbstractItemView::SingleSelection
+ && current.column() >= start
+ && current.column() <= end) {
+ int totalToRemove = end - start + 1;
+ if (model->columnCount(parent) < totalToRemove) { // no more columns
+ QModelIndex index = parent;
+ while (index.isValid() && !isIndexEnabled(index))
+ index = index.parent();
+ if (index.isValid())
+ q->setCurrentIndex(index);
+ } else {
+ int column = end;
+ QModelIndex next;
+ do { // find the next visible and enabled item
+ next = model->index(current.row(), column++, current.parent());
+ } while (next.isValid() && (q->isIndexHidden(next) || !isIndexEnabled(next)));
+ q->setCurrentIndex(next);
+ }
+ }
+
+ // Remove all affected editors; this is more efficient than waiting for updateGeometries() to clean out editors for invalid indexes
+ QList<QEditorInfo>::iterator it = editors.begin();
+ while (it != editors.end()) {
+ QModelIndex index = it->index;
+ if (index.column() <= start && index.column() >= end && model->parent(index) == parent) {
+ QWidget *editor = it->editor;
+ it = editors.erase(it);
+ releaseEditor(editor);
+ } else {
+ ++it;
+ }
+ }
+}
+
+/*!
+ \internal
+
+ This slot is called when columns have been removed. The deleted
+ rows are those under the given \a parent from \a start to \a end
+ inclusive.
+*/
+void QAbstractItemViewPrivate::_q_columnsRemoved(const QModelIndex &, int, int)
+{
+ Q_Q(QAbstractItemView);
+ if (q->isVisible())
+ q->updateEditorGeometries();
+ q->setState(QAbstractItemView::NoState);
+}
+
+/*!
+ \internal
+
+ This slot is called when rows have been inserted.
+*/
+void QAbstractItemViewPrivate::_q_columnsInserted(const QModelIndex &, int, int)
+{
+ Q_Q(QAbstractItemView);
+ if (q->isVisible())
+ q->updateEditorGeometries();
+}
+
+
+
+/*!
+ \internal
+*/
+void QAbstractItemViewPrivate::_q_modelDestroyed()
+{
+ Q_Q(QAbstractItemView);
+ model = QAbstractItemModelPrivate::staticEmptyModel();
+ QMetaObject::invokeMethod(q, "reset", Qt::QueuedConnection);
+}
+
+/*!
+ \internal
+
+ This slot is called when the layout is changed.
+*/
+void QAbstractItemViewPrivate::_q_layoutChanged()
+{
+ doDelayedItemsLayout();
+}
+
+/*!
+ This slot is called when the selection is changed. The previous
+ selection (which may be empty), is specified by \a deselected, and the
+ new selection by \a selected.
+
+ \sa setSelection()
+*/
+void QAbstractItemView::selectionChanged(const QItemSelection &selected,
+ const QItemSelection &deselected)
+{
+ Q_D(QAbstractItemView);
+ if (isVisible() && updatesEnabled()) {
+ d->setDirtyRegion(visualRegionForSelection(deselected));
+ d->setDirtyRegion(visualRegionForSelection(selected));
+ d->updateDirtyRegion();
+ }
+}
+
+/*!
+ This slot is called when a new item becomes the current item.
+ The previous current item is specified by the \a previous index, and the new
+ item by the \a current index.
+
+ If you want to know about changes to items see the
+ dataChanged() signal.
+*/
+void QAbstractItemView::currentChanged(const QModelIndex &current, const QModelIndex &previous)
+{
+ Q_D(QAbstractItemView);
+ Q_ASSERT(d->model);
+
+ if (previous.isValid()) {
+ QModelIndex buddy = d->model->buddy(previous);
+ QWidget *editor = d->editorForIndex(buddy).editor;
+ if (editor && !d->persistent.contains(editor)) {
+ commitData(editor);
+ if (current.row() != previous.row())
+ closeEditor(editor, QAbstractItemDelegate::SubmitModelCache);
+ else
+ closeEditor(editor, QAbstractItemDelegate::NoHint);
+ }
+ if (isVisible()) {
+ d->setDirtyRegion(visualRect(previous));
+ d->updateDirtyRegion();
+ }
+ }
+ if (isVisible() && current.isValid() && !d->autoScrollTimer.isActive()) {
+ if (d->autoScroll)
+ scrollTo(current);
+ d->setDirtyRegion(visualRect(current));
+ d->updateDirtyRegion();
+ edit(current, CurrentChanged, 0);
+ if (current.row() == (d->model->rowCount(d->root) - 1))
+ d->_q_fetchMore();
+ }
+}
+
+#ifndef QT_NO_DRAGANDDROP
+/*!
+ Starts a drag by calling drag->exec() using the given \a supportedActions.
+*/
+void QAbstractItemView::startDrag(Qt::DropActions supportedActions)
+{
+ Q_D(QAbstractItemView);
+ QModelIndexList indexes = d->selectedDraggableIndexes();
+ if (indexes.count() > 0) {
+ QMimeData *data = d->model->mimeData(indexes);
+ if (!data)
+ return;
+ QRect rect;
+ QPixmap pixmap = d->renderToPixmap(indexes, &rect);
+ rect.adjust(horizontalOffset(), verticalOffset(), 0, 0);
+ QDrag *drag = new QDrag(this);
+ drag->setPixmap(pixmap);
+ drag->setMimeData(data);
+ drag->setHotSpot(d->pressedPosition - rect.topLeft());
+ Qt::DropAction defaultDropAction = Qt::IgnoreAction;
+ if (supportedActions & Qt::CopyAction && dragDropMode() != QAbstractItemView::InternalMove)
+ defaultDropAction = Qt::CopyAction;
+ if (drag->exec(supportedActions, defaultDropAction) == Qt::MoveAction)
+ d->clearOrRemove();
+ }
+}
+#endif // QT_NO_DRAGANDDROP
+
+/*!
+ Returns a QStyleOptionViewItem structure populated with the view's
+ palette, font, state, alignments etc.
+*/
+QStyleOptionViewItem QAbstractItemView::viewOptions() const
+{
+ Q_D(const QAbstractItemView);
+ QStyleOptionViewItem option;
+ option.init(this);
+ option.state &= ~QStyle::State_MouseOver;
+ option.font = font();
+
+#ifdef Q_WS_WIN
+ // Note this is currently required on Windows
+ // do give non-focused item views inactive appearance
+ if (!hasFocus())
+ option.state &= ~QStyle::State_Active;
+#endif
+
+ option.state &= ~QStyle::State_HasFocus;
+ if (d->iconSize.isValid()) {
+ option.decorationSize = d->iconSize;
+ } else {
+ int pm = style()->pixelMetric(QStyle::PM_SmallIconSize, 0, this);
+ option.decorationSize = QSize(pm, pm);
+ }
+ option.decorationPosition = QStyleOptionViewItem::Left;
+ option.decorationAlignment = Qt::AlignCenter;
+ option.displayAlignment = Qt::AlignLeft|Qt::AlignVCenter;
+ option.textElideMode = d->textElideMode;
+ option.rect = QRect();
+ option.showDecorationSelected = style()->styleHint(QStyle::SH_ItemView_ShowDecorationSelected, 0, this);
+ return option;
+}
+
+QStyleOptionViewItemV4 QAbstractItemViewPrivate::viewOptionsV4() const
+{
+ Q_Q(const QAbstractItemView);
+ QStyleOptionViewItemV4 option = q->viewOptions();
+ if (wrapItemText)
+ option.features = QStyleOptionViewItemV2::WrapText;
+ option.locale = q->locale();
+ option.locale.setNumberOptions(QLocale::OmitGroupSeparator);
+ option.widget = q;
+ return option;
+}
+
+/*!
+ Returns the item view's state.
+
+ \sa setState()
+*/
+QAbstractItemView::State QAbstractItemView::state() const
+{
+ Q_D(const QAbstractItemView);
+ return d->state;
+}
+
+/*!
+ Sets the item view's state to the given \a state.
+
+ \sa state()
+*/
+void QAbstractItemView::setState(State state)
+{
+ Q_D(QAbstractItemView);
+ d->state = state;
+}
+
+/*!
+ Schedules a layout of the items in the view to be executed when the
+ event processing starts.
+
+ Even if scheduleDelayedItemsLayout() is called multiple times before
+ events are processed, the view will only do the layout once.
+
+ \sa executeDelayedItemsLayout()
+*/
+void QAbstractItemView::scheduleDelayedItemsLayout()
+{
+ Q_D(QAbstractItemView);
+ d->doDelayedItemsLayout();
+}
+
+/*!
+ Executes the scheduled layouts without waiting for the event processing
+ to begin.
+
+ \sa scheduleDelayedItemsLayout()
+*/
+void QAbstractItemView::executeDelayedItemsLayout()
+{
+ Q_D(QAbstractItemView);
+ d->executePostedLayout();
+}
+
+/*!
+ \since 4.1
+
+ Marks the given \a region as dirty and schedules it to be updated.
+ You only need to call this function if you are implementing
+ your own view subclass.
+
+ \sa scrollDirtyRegion(), dirtyRegionOffset()
+*/
+
+void QAbstractItemView::setDirtyRegion(const QRegion &region)
+{
+ Q_D(QAbstractItemView);
+ d->setDirtyRegion(region);
+}
+
+/*!
+ Prepares the view for scrolling by (\a{dx},\a{dy}) pixels by moving the dirty regions in the
+ opposite direction. You only need to call this function if you are implementing a scrolling
+ viewport in your view subclass.
+
+ If you implement scrollContentsBy() in a subclass of QAbstractItemView, call this function
+ before you call QWidget::scroll() on the viewport. Alternatively, just call update().
+
+ \sa scrollContentsBy(), dirtyRegionOffset(), setDirtyRegion()
+*/
+void QAbstractItemView::scrollDirtyRegion(int dx, int dy)
+{
+ Q_D(QAbstractItemView);
+ d->scrollDirtyRegion(dx, dy);
+}
+
+/*!
+ Returns the offset of the dirty regions in the view.
+
+ If you use scrollDirtyRegion() and implement a paintEvent() in a subclass of
+ QAbstractItemView, you should translate the area given by the paint event with
+ the offset returned from this function.
+
+ \sa scrollDirtyRegion(), setDirtyRegion()
+*/
+QPoint QAbstractItemView::dirtyRegionOffset() const
+{
+ Q_D(const QAbstractItemView);
+ return d->scrollDelayOffset;
+}
+
+/*!
+ \internal
+*/
+void QAbstractItemView::startAutoScroll()
+{
+ Q_D(QAbstractItemView);
+ // ### it would be nice to make this into a style hint one day
+ int scrollInterval = (verticalScrollMode() == QAbstractItemView::ScrollPerItem) ? 150 : 50;
+ d->autoScrollTimer.start(scrollInterval, this);
+ d->autoScrollCount = 0;
+}
+
+/*!
+ \internal
+*/
+void QAbstractItemView::stopAutoScroll()
+{
+ Q_D(QAbstractItemView);
+ d->autoScrollTimer.stop();
+ d->autoScrollCount = 0;
+}
+
+/*!
+ \internal
+*/
+void QAbstractItemView::doAutoScroll()
+{
+ // find how much we should scroll with
+ Q_D(QAbstractItemView);
+ int verticalStep = verticalScrollBar()->pageStep();
+ int horizontalStep = horizontalScrollBar()->pageStep();
+ if (d->autoScrollCount < qMax(verticalStep, horizontalStep))
+ ++d->autoScrollCount;
+
+ int margin = d->autoScrollMargin;
+ int verticalValue = verticalScrollBar()->value();
+ int horizontalValue = horizontalScrollBar()->value();
+
+ QPoint pos = d->viewport->mapFromGlobal(QCursor::pos());
+ QRect area = static_cast<QAbstractItemView*>(d->viewport)->d_func()->clipRect(); // access QWidget private by bending C++ rules
+
+ // do the scrolling if we are in the scroll margins
+ if (pos.y() - area.top() < margin)
+ verticalScrollBar()->setValue(verticalValue - d->autoScrollCount);
+ else if (area.bottom() - pos.y() < margin)
+ verticalScrollBar()->setValue(verticalValue + d->autoScrollCount);
+ if (pos.x() - area.left() < margin)
+ horizontalScrollBar()->setValue(horizontalValue - d->autoScrollCount);
+ else if (area.right() - pos.x() < margin)
+ horizontalScrollBar()->setValue(horizontalValue + d->autoScrollCount);
+ // if nothing changed, stop scrolling
+ bool verticalUnchanged = (verticalValue == verticalScrollBar()->value());
+ bool horizontalUnchanged = (horizontalValue == horizontalScrollBar()->value());
+ if (verticalUnchanged && horizontalUnchanged) {
+ stopAutoScroll();
+ } else {
+#ifndef QT_NO_DRAGANDDROP
+ d->dropIndicatorRect = QRect();
+ d->dropIndicatorPosition = QAbstractItemView::OnViewport;
+#endif
+ d->viewport->update();
+ }
+}
+
+/*!
+ Returns the SelectionFlags to be used when updating a selection with
+ to include the \a index specified. The \a event is a user input event,
+ such as a mouse or keyboard event.
+
+ Reimplement this function to define your own selection behavior.
+
+ \sa setSelection()
+*/
+QItemSelectionModel::SelectionFlags QAbstractItemView::selectionCommand(const QModelIndex &index,
+ const QEvent *event) const
+{
+ Q_D(const QAbstractItemView);
+ switch (d->selectionMode) {
+ case NoSelection: // Never update selection model
+ return QItemSelectionModel::NoUpdate;
+ case SingleSelection: // ClearAndSelect on valid index otherwise NoUpdate
+ if (event && event->type() == QEvent::MouseButtonRelease)
+ return QItemSelectionModel::NoUpdate;
+ return QItemSelectionModel::ClearAndSelect|d->selectionBehaviorFlags();
+ case MultiSelection:
+ return d->multiSelectionCommand(index, event);
+ case ExtendedSelection:
+ return d->extendedSelectionCommand(index, event);
+ case ContiguousSelection:
+ return d->contiguousSelectionCommand(index, event);
+ }
+ return QItemSelectionModel::NoUpdate;
+}
+
+QItemSelectionModel::SelectionFlags QAbstractItemViewPrivate::multiSelectionCommand(
+ const QModelIndex &index, const QEvent *event) const
+{
+ Q_UNUSED(index);
+
+ if (event) {
+ switch (event->type()) {
+ case QEvent::KeyPress:
+ if (static_cast<const QKeyEvent*>(event)->key() == Qt::Key_Space
+ || static_cast<const QKeyEvent*>(event)->key() == Qt::Key_Select)
+ return QItemSelectionModel::Toggle|selectionBehaviorFlags();
+ break;
+ case QEvent::MouseButtonPress:
+ if (static_cast<const QMouseEvent*>(event)->button() == Qt::LeftButton)
+ return QItemSelectionModel::Toggle|selectionBehaviorFlags(); // toggle
+ break;
+ case QEvent::MouseButtonRelease:
+ if (static_cast<const QMouseEvent*>(event)->button() == Qt::LeftButton)
+ return QItemSelectionModel::NoUpdate|selectionBehaviorFlags(); // finalize
+ break;
+ case QEvent::MouseMove:
+ if (static_cast<const QMouseEvent*>(event)->buttons() & Qt::LeftButton)
+ return QItemSelectionModel::ToggleCurrent|selectionBehaviorFlags(); // toggle drag select
+ default:
+ break;
+ }
+ return QItemSelectionModel::NoUpdate;
+ }
+
+ return QItemSelectionModel::Toggle|selectionBehaviorFlags();
+}
+
+QItemSelectionModel::SelectionFlags QAbstractItemViewPrivate::extendedSelectionCommand(
+ const QModelIndex &index, const QEvent *event) const
+{
+ Qt::KeyboardModifiers modifiers = QApplication::keyboardModifiers();
+ if (event) {
+ switch (event->type()) {
+ case QEvent::MouseMove: {
+ // Toggle on MouseMove
+ modifiers = static_cast<const QMouseEvent*>(event)->modifiers();
+ if (modifiers & Qt::ControlModifier)
+ return QItemSelectionModel::ToggleCurrent|selectionBehaviorFlags();
+ break;
+ }
+ case QEvent::MouseButtonPress: {
+ modifiers = static_cast<const QMouseEvent*>(event)->modifiers();
+ const Qt::MouseButton button = static_cast<const QMouseEvent*>(event)->button();
+ const bool rightButtonPressed = button & Qt::RightButton;
+ const bool shiftKeyPressed = modifiers & Qt::ShiftModifier;
+ const bool controlKeyPressed = modifiers & Qt::ControlModifier;
+ const bool indexIsSelected = selectionModel->isSelected(index);
+ if ((shiftKeyPressed || controlKeyPressed) && rightButtonPressed)
+ return QItemSelectionModel::NoUpdate;
+ if (!shiftKeyPressed && !controlKeyPressed && indexIsSelected)
+ return QItemSelectionModel::NoUpdate;
+ if (!index.isValid() && !rightButtonPressed && !shiftKeyPressed && !controlKeyPressed)
+ return QItemSelectionModel::Clear;
+ if (!index.isValid())
+ return QItemSelectionModel::NoUpdate;
+ break;
+ }
+ case QEvent::MouseButtonRelease: {
+ // ClearAndSelect on MouseButtonRelease if MouseButtonPress on selected item or empty area
+ modifiers = static_cast<const QMouseEvent*>(event)->modifiers();
+ const Qt::MouseButton button = static_cast<const QMouseEvent*>(event)->button();
+ const bool rightButtonPressed = button & Qt::RightButton;
+ const bool shiftKeyPressed = modifiers & Qt::ShiftModifier;
+ const bool controlKeyPressed = modifiers & Qt::ControlModifier;
+ if (((index == pressedIndex && selectionModel->isSelected(index))
+ || !index.isValid()) && state != QAbstractItemView::DragSelectingState
+ && !shiftKeyPressed && !controlKeyPressed && !rightButtonPressed)
+ return QItemSelectionModel::ClearAndSelect|selectionBehaviorFlags();
+ return QItemSelectionModel::NoUpdate;
+ }
+ case QEvent::KeyPress: {
+ // NoUpdate on Key movement and Ctrl
+ modifiers = static_cast<const QKeyEvent*>(event)->modifiers();
+ switch (static_cast<const QKeyEvent*>(event)->key()) {
+ case Qt::Key_Backtab:
+ modifiers = modifiers & ~Qt::ShiftModifier; // special case for backtab
+ case Qt::Key_Down:
+ case Qt::Key_Up:
+ case Qt::Key_Left:
+ case Qt::Key_Right:
+ case Qt::Key_Home:
+ case Qt::Key_End:
+ case Qt::Key_PageUp:
+ case Qt::Key_PageDown:
+ case Qt::Key_Tab:
+#ifdef QT_KEYPAD_NAVIGATION
+ return QItemSelectionModel::NoUpdate;
+#else
+ if (modifiers & Qt::ControlModifier)
+ return QItemSelectionModel::NoUpdate;
+#endif
+ break;
+ case Qt::Key_Select:
+ return QItemSelectionModel::Toggle|selectionBehaviorFlags();
+ case Qt::Key_Space:// Toggle on Ctrl-Qt::Key_Space, Select on Space
+ if (modifiers & Qt::ControlModifier)
+ return QItemSelectionModel::Toggle|selectionBehaviorFlags();
+ return QItemSelectionModel::Select|selectionBehaviorFlags();
+ default:
+ break;
+ }
+ }
+ default:
+ break;
+ }
+ }
+
+ if (modifiers & Qt::ShiftModifier)
+ return QItemSelectionModel::SelectCurrent|selectionBehaviorFlags();
+ if (modifiers & Qt::ControlModifier)
+ return QItemSelectionModel::Toggle|selectionBehaviorFlags();
+ if (state == QAbstractItemView::DragSelectingState) {
+ //when drag-selecting we need to clear any previous selection and select the current one
+ return QItemSelectionModel::Clear|QItemSelectionModel::SelectCurrent|selectionBehaviorFlags();
+ }
+
+ return QItemSelectionModel::ClearAndSelect|selectionBehaviorFlags();
+}
+
+QItemSelectionModel::SelectionFlags
+QAbstractItemViewPrivate::contiguousSelectionCommand(const QModelIndex &index,
+ const QEvent *event) const
+{
+ QItemSelectionModel::SelectionFlags flags = extendedSelectionCommand(index, event);
+ const int Mask = QItemSelectionModel::Clear | QItemSelectionModel::Select
+ | QItemSelectionModel::Deselect | QItemSelectionModel::Toggle
+ | QItemSelectionModel::Current;
+
+ switch (flags & Mask) {
+ case QItemSelectionModel::Clear:
+ case QItemSelectionModel::ClearAndSelect:
+ case QItemSelectionModel::SelectCurrent:
+ return flags;
+ case QItemSelectionModel::NoUpdate:
+ if (event &&
+ (event->type() == QEvent::MouseButtonPress
+ || event->type() == QEvent::MouseButtonRelease))
+ return flags;
+ return QItemSelectionModel::ClearAndSelect|selectionBehaviorFlags();
+ default:
+ return QItemSelectionModel::SelectCurrent|selectionBehaviorFlags();
+ }
+}
+
+void QAbstractItemViewPrivate::_q_fetchMore()
+{
+ if (!model->canFetchMore(root))
+ return;
+ int last = model->rowCount(root) - 1;
+ if (last < 0) {
+ model->fetchMore(root);
+ return;
+ }
+
+ QModelIndex index = model->index(last, 0, root);
+ QRect rect = q_func()->visualRect(index);
+ if (viewport->rect().intersects(rect))
+ model->fetchMore(root);
+}
+
+bool QAbstractItemViewPrivate::shouldEdit(QAbstractItemView::EditTrigger trigger,
+ const QModelIndex &index) const
+{
+ if (!index.isValid())
+ return false;
+ Qt::ItemFlags flags = model->flags(index);
+ if (((flags & Qt::ItemIsEditable) == 0) || ((flags & Qt::ItemIsEnabled) == 0))
+ return false;
+ if (state == QAbstractItemView::EditingState)
+ return false;
+ if (hasEditor(index))
+ return false;
+ if (trigger == QAbstractItemView::AllEditTriggers) // force editing
+ return true;
+ if ((trigger & editTriggers) == QAbstractItemView::SelectedClicked
+ && !selectionModel->isSelected(index))
+ return false;
+ return (trigger & editTriggers);
+}
+
+bool QAbstractItemViewPrivate::shouldForwardEvent(QAbstractItemView::EditTrigger trigger,
+ const QEvent *event) const
+{
+ if (!event || (trigger & editTriggers) != QAbstractItemView::AnyKeyPressed)
+ return false;
+
+ switch (event->type()) {
+ case QEvent::KeyPress:
+ case QEvent::MouseButtonDblClick:
+ case QEvent::MouseButtonPress:
+ case QEvent::MouseButtonRelease:
+ case QEvent::MouseMove:
+ return true;
+ default:
+ break;
+ };
+
+ return false;
+}
+
+bool QAbstractItemViewPrivate::shouldAutoScroll(const QPoint &pos) const
+{
+ if (!autoScroll)
+ return false;
+ QRect area = static_cast<QAbstractItemView*>(viewport)->d_func()->clipRect(); // access QWidget private by bending C++ rules
+ return (pos.y() - area.top() < autoScrollMargin)
+ || (area.bottom() - pos.y() < autoScrollMargin)
+ || (pos.x() - area.left() < autoScrollMargin)
+ || (area.right() - pos.x() < autoScrollMargin);
+}
+
+void QAbstractItemViewPrivate::doDelayedItemsLayout(int delay)
+{
+ if (!delayedPendingLayout) {
+ delayedPendingLayout = true;
+ delayedLayout.start(delay, q_func());
+ }
+}
+
+void QAbstractItemViewPrivate::interruptDelayedItemsLayout() const
+{
+ delayedLayout.stop();
+ delayedPendingLayout = false;
+}
+
+
+
+QWidget *QAbstractItemViewPrivate::editor(const QModelIndex &index,
+ const QStyleOptionViewItem &options)
+{
+ Q_Q(QAbstractItemView);
+ QWidget *w = editorForIndex(index).editor;
+ if (!w) {
+ QAbstractItemDelegate *delegate = delegateForIndex(index);
+ if (!delegate)
+ return 0;
+ w = delegate->createEditor(viewport, options, index);
+ if (w) {
+ w->installEventFilter(delegate);
+ QObject::connect(w, SIGNAL(destroyed(QObject*)), q, SLOT(editorDestroyed(QObject*)));
+ delegate->updateEditorGeometry(w, options, index);
+ delegate->setEditorData(w, index);
+ addEditor(index, w, false);
+ if (w->parent() == viewport)
+ QWidget::setTabOrder(q, w);
+
+ // Special cases for some editors containing QLineEdit
+ QWidget *focusWidget = w;
+ while (QWidget *fp = focusWidget->focusProxy())
+ focusWidget = fp;
+#ifndef QT_NO_LINEEDIT
+ if (QLineEdit *le = qobject_cast<QLineEdit*>(focusWidget))
+ le->selectAll();
+#endif
+#ifndef QT_NO_SPINBOX
+ if (QSpinBox *sb = qobject_cast<QSpinBox*>(focusWidget))
+ sb->selectAll();
+ else if (QDoubleSpinBox *dsb = qobject_cast<QDoubleSpinBox*>(focusWidget))
+ dsb->selectAll();
+#endif
+ }
+ }
+ return w;
+}
+
+void QAbstractItemViewPrivate::updateEditorData(const QModelIndex &tl, const QModelIndex &br)
+{
+ // we are counting on having relatively few editors
+ const bool checkIndexes = tl.isValid() && br.isValid();
+ const QModelIndex parent = tl.parent();
+ QList<QEditorInfo>::const_iterator it = editors.constBegin();
+ for (; it != editors.constEnd(); ++it) {
+ QWidget *editor = it->editor;
+ const QModelIndex index = it->index;
+ if (it->isStatic || editor == 0 || !index.isValid() ||
+ (checkIndexes
+ && (index.row() < tl.row() || index.row() > br.row()
+ || index.column() < tl.column() || index.column() > br.column()
+ || index.parent() != parent)))
+ continue;
+
+ QAbstractItemDelegate *delegate = delegateForIndex(index);
+ if (delegate) {
+ delegate->setEditorData(editor, index);
+ }
+ }
+}
+
+/*!
+ \internal
+
+ In DND if something has been moved then this is called.
+ Typically this means you should "remove" the selected item or row,
+ but the behavior is view dependant (table just clears the selected indexes for example).
+
+ Either remove the selected rows or clear them
+ */
+void QAbstractItemViewPrivate::clearOrRemove()
+{
+#ifndef QT_NO_DRAGANDDROP
+ const QItemSelection selection = selectionModel->selection();
+ QList<QItemSelectionRange>::const_iterator it = selection.constBegin();
+
+ if (!overwrite) {
+ for (; it != selection.constEnd(); ++it) {
+ QModelIndex parent = (*it).parent();
+ if ((*it).left() != 0)
+ continue;
+ if ((*it).right() != (model->columnCount(parent) - 1))
+ continue;
+ int count = (*it).bottom() - (*it).top() + 1;
+ model->removeRows((*it).top(), count, parent);
+ }
+ } else {
+ // we can't remove the rows so reset the items (i.e. the view is like a table)
+ QModelIndexList list = selection.indexes();
+ for (int i=0; i < list.size(); ++i) {
+ QModelIndex index = list.at(i);
+ QMap<int, QVariant> roles = model->itemData(index);
+ for (QMap<int, QVariant>::Iterator it = roles.begin(); it != roles.end(); ++it)
+ it.value() = QVariant();
+ model->setItemData(index, roles);
+ }
+ }
+#endif
+}
+
+/*!
+ \internal
+
+ When persistent aeditor gets/loses focus, we need to check
+ and setcorrectly the current index.
+ */
+void QAbstractItemViewPrivate::checkPersistentEditorFocus()
+{
+ Q_Q(QAbstractItemView);
+ if (QWidget *widget = qApp->focusWidget()) {
+ if (persistent.contains(widget)) {
+ //a persistent editor has gained the focus
+ QModelIndex index = indexForEditor(widget);
+ if (selectionModel->currentIndex() != index)
+ q->setCurrentIndex(index);
+ }
+ }
+}
+
+
+QEditorInfo QAbstractItemViewPrivate::editorForIndex(const QModelIndex &index) const
+{
+ QList<QEditorInfo>::const_iterator it = editors.constBegin();
+ for (; it != editors.constEnd(); ++it) {
+ if (it->index == index)
+ return *it;
+ }
+
+ return QEditorInfo();
+}
+
+QModelIndex QAbstractItemViewPrivate::indexForEditor(QWidget *editor) const
+{
+ QList<QEditorInfo>::const_iterator it = editors.constBegin();
+ for (; it != editors.constEnd(); ++it) {
+ if (it->editor == editor)
+ return it->index;
+ }
+ return QModelIndex();
+}
+
+void QAbstractItemViewPrivate::removeEditor(QWidget *editor)
+{
+ QList<QEditorInfo>::iterator it = editors.begin();
+ for (; it != editors.end(); ) {
+ if (it->editor == editor)
+ it = editors.erase(it);
+ else
+ ++it;
+ }
+}
+
+void QAbstractItemViewPrivate::addEditor(const QModelIndex &index, QWidget *editor, bool isStatic)
+{
+ editors.append(QEditorInfo(index, editor, isStatic));
+}
+
+bool QAbstractItemViewPrivate::sendDelegateEvent(const QModelIndex &index, QEvent *event) const
+{
+ Q_Q(const QAbstractItemView);
+ QModelIndex buddy = model->buddy(index);
+ QStyleOptionViewItemV4 options = viewOptionsV4();
+ options.rect = q->visualRect(buddy);
+ options.state |= (buddy == q->currentIndex() ? QStyle::State_HasFocus : QStyle::State_None);
+ QAbstractItemDelegate *delegate = delegateForIndex(index);
+ return (event && delegate && delegate->editorEvent(event, model, options, buddy));
+}
+
+bool QAbstractItemViewPrivate::openEditor(const QModelIndex &index, QEvent *event)
+{
+ Q_Q(QAbstractItemView);
+
+ QModelIndex buddy = model->buddy(index);
+ QStyleOptionViewItemV4 options = viewOptionsV4();
+ options.rect = q->visualRect(buddy);
+ options.state |= (buddy == q->currentIndex() ? QStyle::State_HasFocus : QStyle::State_None);
+
+ QWidget *w = editor(buddy, options);
+ if (!w)
+ return false;
+
+ if (event)
+ QApplication::sendEvent(w->focusProxy() ? w->focusProxy() : w, event);
+
+ q->setState(QAbstractItemView::EditingState);
+ w->show();
+ w->setFocus();
+
+ return true;
+}
+
+QPixmap QAbstractItemViewPrivate::renderToPixmap(const QModelIndexList &indexes, QRect *r) const
+{
+ Q_Q(const QAbstractItemView);
+ QRect rect = q->visualRect(indexes.at(0));
+ QList<QRect> rects;
+ for (int i = 0; i < indexes.count(); ++i) {
+ rects.append(q->visualRect(indexes.at(i)));
+ rect |= rects.at(i);
+ }
+ rect = rect.intersected(viewport->rect());
+ if (rect.width() <= 0 || rect.height() <= 0)
+ return QPixmap();
+ QImage image(rect.size(), QImage::Format_ARGB32_Premultiplied);
+ image.fill(0);
+ QPainter painter(&image);
+ QStyleOptionViewItemV4 option = viewOptionsV4();
+ option.state |= QStyle::State_Selected;
+ for (int j = 0; j < indexes.count(); ++j) {
+ option.rect = QRect(rects.at(j).topLeft() - rect.topLeft(), rects.at(j).size());
+ delegateForIndex(indexes.at(j))->paint(&painter, option, indexes.at(j));
+ }
+ painter.end();
+ if (r) *r = rect;
+ return QPixmap::fromImage(image);
+}
+
+void QAbstractItemViewPrivate::selectAll(QItemSelectionModel::SelectionFlags command)
+{
+ if (!selectionModel)
+ return;
+
+ QItemSelection selection;
+ QModelIndex tl = model->index(0, 0, root);
+ QModelIndex br = model->index(model->rowCount(root) - 1,
+ model->columnCount(root) - 1,
+ root);
+ selection.append(QItemSelectionRange(tl, br));
+ selectionModel->select(selection, command);
+}
+
+QModelIndexList QAbstractItemViewPrivate::selectedDraggableIndexes() const
+{
+ Q_Q(const QAbstractItemView);
+ QModelIndexList indexes = q->selectedIndexes();
+ for(int i = indexes.count() - 1 ; i >= 0; --i) {
+ if (!isIndexDragEnabled(indexes.at(i)))
+ indexes.removeAt(i);
+ }
+ return indexes;
+}
+
+
+QT_END_NAMESPACE
+
+#include "moc_qabstractitemview.cpp"
+
+#endif // QT_NO_ITEMVIEWS
diff --git a/src/gui/itemviews/qabstractitemview.h b/src/gui/itemviews/qabstractitemview.h
new file mode 100644
index 0000000000..da8650d01e
--- /dev/null
+++ b/src/gui/itemviews/qabstractitemview.h
@@ -0,0 +1,370 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QABSTRACTITEMVIEW_H
+#define QABSTRACTITEMVIEW_H
+
+#include <QtGui/qabstractscrollarea.h>
+#include <QtCore/qabstractitemmodel.h>
+#include <QtGui/qitemselectionmodel.h>
+#include <QtGui/qabstractitemdelegate.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+#ifndef QT_NO_ITEMVIEWS
+
+class QMenu;
+class QDrag;
+class QEvent;
+class QAbstractItemViewPrivate;
+
+class Q_GUI_EXPORT QAbstractItemView : public QAbstractScrollArea
+{
+ Q_OBJECT
+ Q_ENUMS(SelectionMode SelectionBehavior ScrollHint ScrollMode DragDropMode)
+ Q_FLAGS(EditTriggers)
+ Q_PROPERTY(bool autoScroll READ hasAutoScroll WRITE setAutoScroll)
+ Q_PROPERTY(int autoScrollMargin READ autoScrollMargin WRITE setAutoScrollMargin)
+ Q_PROPERTY(EditTriggers editTriggers READ editTriggers WRITE setEditTriggers)
+ Q_PROPERTY(bool tabKeyNavigation READ tabKeyNavigation WRITE setTabKeyNavigation)
+#ifndef QT_NO_DRAGANDDROP
+ Q_PROPERTY(bool showDropIndicator READ showDropIndicator WRITE setDropIndicatorShown)
+ Q_PROPERTY(bool dragEnabled READ dragEnabled WRITE setDragEnabled)
+ Q_PROPERTY(bool dragDropOverwriteMode READ dragDropOverwriteMode WRITE setDragDropOverwriteMode)
+ Q_PROPERTY(DragDropMode dragDropMode READ dragDropMode WRITE setDragDropMode)
+#endif
+ Q_PROPERTY(bool alternatingRowColors READ alternatingRowColors WRITE setAlternatingRowColors)
+ Q_PROPERTY(SelectionMode selectionMode READ selectionMode WRITE setSelectionMode)
+ Q_PROPERTY(SelectionBehavior selectionBehavior READ selectionBehavior WRITE setSelectionBehavior)
+ Q_PROPERTY(QSize iconSize READ iconSize WRITE setIconSize)
+ Q_PROPERTY(Qt::TextElideMode textElideMode READ textElideMode WRITE setTextElideMode)
+ Q_PROPERTY(ScrollMode verticalScrollMode READ verticalScrollMode WRITE setVerticalScrollMode)
+ Q_PROPERTY(ScrollMode horizontalScrollMode READ horizontalScrollMode WRITE setHorizontalScrollMode)
+
+public:
+ enum SelectionMode {
+ NoSelection,
+ SingleSelection,
+ MultiSelection,
+ ExtendedSelection,
+ ContiguousSelection
+ };
+
+ enum SelectionBehavior {
+ SelectItems,
+ SelectRows,
+ SelectColumns
+ };
+
+ enum ScrollHint {
+ EnsureVisible,
+ PositionAtTop,
+ PositionAtBottom,
+ PositionAtCenter
+ };
+
+ enum EditTrigger {
+ NoEditTriggers = 0,
+ CurrentChanged = 1,
+ DoubleClicked = 2,
+ SelectedClicked = 4,
+ EditKeyPressed = 8,
+ AnyKeyPressed = 16,
+ AllEditTriggers = 31
+ };
+
+ Q_DECLARE_FLAGS(EditTriggers, EditTrigger)
+
+ enum ScrollMode {
+ ScrollPerItem,
+ ScrollPerPixel
+ };
+
+ explicit QAbstractItemView(QWidget *parent = 0);
+ ~QAbstractItemView();
+
+ virtual void setModel(QAbstractItemModel *model);
+ QAbstractItemModel *model() const;
+
+ virtual void setSelectionModel(QItemSelectionModel *selectionModel);
+ QItemSelectionModel *selectionModel() const;
+
+ void setItemDelegate(QAbstractItemDelegate *delegate);
+ QAbstractItemDelegate *itemDelegate() const;
+
+ void setSelectionMode(QAbstractItemView::SelectionMode mode);
+ QAbstractItemView::SelectionMode selectionMode() const;
+
+ void setSelectionBehavior(QAbstractItemView::SelectionBehavior behavior);
+ QAbstractItemView::SelectionBehavior selectionBehavior() const;
+
+ QModelIndex currentIndex() const;
+ QModelIndex rootIndex() const;
+
+ void setEditTriggers(EditTriggers triggers);
+ EditTriggers editTriggers() const;
+
+ void setVerticalScrollMode(ScrollMode mode);
+ ScrollMode verticalScrollMode() const;
+
+ void setHorizontalScrollMode(ScrollMode mode);
+ ScrollMode horizontalScrollMode() const;
+
+ void setAutoScroll(bool enable);
+ bool hasAutoScroll() const;
+
+ void setAutoScrollMargin(int margin);
+ int autoScrollMargin() const;
+
+ void setTabKeyNavigation(bool enable);
+ bool tabKeyNavigation() const;
+
+#ifndef QT_NO_DRAGANDDROP
+ void setDropIndicatorShown(bool enable);
+ bool showDropIndicator() const;
+
+ void setDragEnabled(bool enable);
+ bool dragEnabled() const;
+
+ void setDragDropOverwriteMode(bool overwrite);
+ bool dragDropOverwriteMode() const;
+
+ enum DragDropMode {
+ NoDragDrop,
+ DragOnly,
+ DropOnly,
+ DragDrop,
+ InternalMove
+ };
+
+ void setDragDropMode(DragDropMode behavior);
+ DragDropMode dragDropMode() const;
+#endif
+ void setAlternatingRowColors(bool enable);
+ bool alternatingRowColors() const;
+
+ void setIconSize(const QSize &size);
+ QSize iconSize() const;
+
+ void setTextElideMode(Qt::TextElideMode mode);
+ Qt::TextElideMode textElideMode() const;
+
+ virtual void keyboardSearch(const QString &search);
+
+ virtual QRect visualRect(const QModelIndex &index) const = 0;
+ virtual void scrollTo(const QModelIndex &index, ScrollHint hint = EnsureVisible) = 0;
+ virtual QModelIndex indexAt(const QPoint &point) const = 0;
+
+ QSize sizeHintForIndex(const QModelIndex &index) const;
+ virtual int sizeHintForRow(int row) const;
+ virtual int sizeHintForColumn(int column) const;
+
+ void openPersistentEditor(const QModelIndex &index);
+ void closePersistentEditor(const QModelIndex &index);
+
+ void setIndexWidget(const QModelIndex &index, QWidget *widget);
+ QWidget *indexWidget(const QModelIndex &index) const;
+
+ void setItemDelegateForRow(int row, QAbstractItemDelegate *delegate);
+ QAbstractItemDelegate *itemDelegateForRow(int row) const;
+
+ void setItemDelegateForColumn(int column, QAbstractItemDelegate *delegate);
+ QAbstractItemDelegate *itemDelegateForColumn(int column) const;
+
+ QAbstractItemDelegate *itemDelegate(const QModelIndex &index) const;
+
+ virtual QVariant inputMethodQuery(Qt::InputMethodQuery query) const;
+
+#ifdef Q_NO_USING_KEYWORD
+ inline void update() { QAbstractScrollArea::update(); }
+#else
+ using QAbstractScrollArea::update;
+#endif
+
+public Q_SLOTS:
+ virtual void reset();
+ virtual void setRootIndex(const QModelIndex &index);
+ virtual void doItemsLayout();
+ virtual void selectAll();
+ void edit(const QModelIndex &index);
+ void clearSelection();
+ void setCurrentIndex(const QModelIndex &index);
+ void scrollToTop();
+ void scrollToBottom();
+ void update(const QModelIndex &index);
+
+protected Q_SLOTS:
+ virtual void dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight);
+ virtual void rowsInserted(const QModelIndex &parent, int start, int end);
+ virtual void rowsAboutToBeRemoved(const QModelIndex &parent, int start, int end);
+ virtual void selectionChanged(const QItemSelection &selected, const QItemSelection &deselected);
+ virtual void currentChanged(const QModelIndex &current, const QModelIndex &previous);
+ virtual void updateEditorData();
+ virtual void updateEditorGeometries();
+ virtual void updateGeometries();
+ virtual void verticalScrollbarAction(int action);
+ virtual void horizontalScrollbarAction(int action);
+ virtual void verticalScrollbarValueChanged(int value);
+ virtual void horizontalScrollbarValueChanged(int value);
+ virtual void closeEditor(QWidget *editor, QAbstractItemDelegate::EndEditHint hint);
+ virtual void commitData(QWidget *editor);
+ virtual void editorDestroyed(QObject *editor);
+
+Q_SIGNALS:
+ void pressed(const QModelIndex &index);
+ void clicked(const QModelIndex &index);
+ void doubleClicked(const QModelIndex &index);
+
+ void activated(const QModelIndex &index);
+ void entered(const QModelIndex &index);
+ void viewportEntered();
+
+protected:
+ QAbstractItemView(QAbstractItemViewPrivate &, QWidget *parent = 0);
+
+ void setHorizontalStepsPerItem(int steps);
+ int horizontalStepsPerItem() const;
+ void setVerticalStepsPerItem(int steps);
+ int verticalStepsPerItem() const;
+
+ enum CursorAction { MoveUp, MoveDown, MoveLeft, MoveRight,
+ MoveHome, MoveEnd, MovePageUp, MovePageDown,
+ MoveNext, MovePrevious };
+ virtual QModelIndex moveCursor(CursorAction cursorAction,
+ Qt::KeyboardModifiers modifiers) = 0;
+
+ virtual int horizontalOffset() const = 0;
+ virtual int verticalOffset() const = 0;
+
+ virtual bool isIndexHidden(const QModelIndex &index) const = 0;
+
+ virtual void setSelection(const QRect &rect, QItemSelectionModel::SelectionFlags command) = 0;
+ virtual QRegion visualRegionForSelection(const QItemSelection &selection) const = 0;
+ virtual QModelIndexList selectedIndexes() const;
+
+ virtual bool edit(const QModelIndex &index, EditTrigger trigger, QEvent *event);
+
+ virtual QItemSelectionModel::SelectionFlags selectionCommand(const QModelIndex &index,
+ const QEvent *event = 0) const;
+
+#ifndef QT_NO_DRAGANDDROP
+ virtual void startDrag(Qt::DropActions supportedActions);
+#endif
+
+ virtual QStyleOptionViewItem viewOptions() const;
+
+ enum State {
+ NoState,
+ DraggingState,
+ DragSelectingState,
+ EditingState,
+ ExpandingState,
+ CollapsingState,
+ AnimatingState
+ };
+
+ State state() const;
+ void setState(State state);
+
+ void scheduleDelayedItemsLayout();
+ void executeDelayedItemsLayout();
+
+ void setDirtyRegion(const QRegion &region);
+ void scrollDirtyRegion(int dx, int dy);
+ QPoint dirtyRegionOffset() const;
+
+ void startAutoScroll();
+ void stopAutoScroll();
+ void doAutoScroll();
+
+ bool focusNextPrevChild(bool next);
+ bool event(QEvent *event);
+ bool viewportEvent(QEvent *event);
+ void mousePressEvent(QMouseEvent *event);
+ void mouseMoveEvent(QMouseEvent *event);
+ void mouseReleaseEvent(QMouseEvent *event);
+ void mouseDoubleClickEvent(QMouseEvent *event);
+#ifndef QT_NO_DRAGANDDROP
+ void dragEnterEvent(QDragEnterEvent *event);
+ void dragMoveEvent(QDragMoveEvent *event);
+ void dragLeaveEvent(QDragLeaveEvent *event);
+ void dropEvent(QDropEvent *event);
+#endif
+ void focusInEvent(QFocusEvent *event);
+ void focusOutEvent(QFocusEvent *event);
+ void keyPressEvent(QKeyEvent *event);
+ void resizeEvent(QResizeEvent *event);
+ void timerEvent(QTimerEvent *event);
+ void inputMethodEvent(QInputMethodEvent *event);
+
+#ifndef QT_NO_DRAGANDDROP
+ enum DropIndicatorPosition { OnItem, AboveItem, BelowItem, OnViewport };
+ DropIndicatorPosition dropIndicatorPosition() const;
+#endif
+
+private:
+ Q_DECLARE_PRIVATE(QAbstractItemView)
+ Q_DISABLE_COPY(QAbstractItemView)
+ Q_PRIVATE_SLOT(d_func(), void _q_columnsAboutToBeRemoved(const QModelIndex&, int, int))
+ Q_PRIVATE_SLOT(d_func(), void _q_columnsRemoved(const QModelIndex&, int, int))
+ Q_PRIVATE_SLOT(d_func(), void _q_columnsInserted(const QModelIndex&, int, int))
+ Q_PRIVATE_SLOT(d_func(), void _q_rowsRemoved(const QModelIndex&, int, int))
+ Q_PRIVATE_SLOT(d_func(), void _q_modelDestroyed())
+ Q_PRIVATE_SLOT(d_func(), void _q_layoutChanged())
+ Q_PRIVATE_SLOT(d_func(), void _q_fetchMore())
+
+ friend class QTreeViewPrivate; // needed to compile with MSVC
+ friend class QAccessibleItemRow;
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(QAbstractItemView::EditTriggers)
+
+#endif // QT_NO_ITEMVIEWS
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QABSTRACTITEMVIEW_H
diff --git a/src/gui/itemviews/qabstractitemview_p.h b/src/gui/itemviews/qabstractitemview_p.h
new file mode 100644
index 0000000000..37fe4a2e58
--- /dev/null
+++ b/src/gui/itemviews/qabstractitemview_p.h
@@ -0,0 +1,410 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QABSTRACTITEMVIEW_P_H
+#define QABSTRACTITEMVIEW_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/qabstractscrollarea_p.h"
+#include "private/qabstractitemmodel_p.h"
+#include "QtGui/qapplication.h"
+#include "QtCore/qdatetime.h"
+#include "QtGui/qevent.h"
+#include "QtGui/qmime.h"
+#include "QtGui/qpainter.h"
+#include "QtCore/qpair.h"
+#include "QtCore/qtimer.h"
+#include "QtCore/qtimeline.h"
+#include "QtGui/qregion.h"
+#include "QtCore/qdebug.h"
+#include "QtGui/qpainter.h"
+
+#ifndef QT_NO_ITEMVIEWS
+
+QT_BEGIN_NAMESPACE
+
+struct QEditorInfo
+{
+ QEditorInfo() : isStatic(false)
+ {
+ }
+
+ QEditorInfo(const QPersistentModelIndex &i, QWidget *e, bool b) : index(i), editor(e), isStatic(b)
+ {
+ }
+
+ QPersistentModelIndex index;
+ QPointer<QWidget> editor;
+ bool isStatic; //true when called from setIndexWidget
+
+};
+
+class QEmptyModel : public QAbstractItemModel
+{
+public:
+ explicit QEmptyModel(QObject *parent = 0) : QAbstractItemModel(parent) {}
+ QModelIndex index(int, int, const QModelIndex &) const { return QModelIndex(); }
+ QModelIndex parent(const QModelIndex &) const { return QModelIndex(); }
+ int rowCount(const QModelIndex &) const { return 0; }
+ int columnCount(const QModelIndex &) const { return 0; }
+ bool hasChildren(const QModelIndex &) const { return false; }
+ QVariant data(const QModelIndex &, int) const { return QVariant(); }
+};
+
+class Q_GUI_EXPORT QAbstractItemViewPrivate : public QAbstractScrollAreaPrivate
+{
+ Q_DECLARE_PUBLIC(QAbstractItemView)
+
+public:
+ QAbstractItemViewPrivate();
+ virtual ~QAbstractItemViewPrivate();
+
+ void init();
+
+ void _q_rowsRemoved(const QModelIndex &parent, int start, int end);
+ void _q_columnsAboutToBeRemoved(const QModelIndex &parent, int start, int end);
+ void _q_columnsRemoved(const QModelIndex &parent, int start, int end);
+ void _q_columnsInserted(const QModelIndex &parent, int start, int end);
+ void _q_modelDestroyed();
+ void _q_layoutChanged();
+ void _q_fetchMore();
+
+ bool shouldEdit(QAbstractItemView::EditTrigger trigger, const QModelIndex &index) const;
+ bool shouldForwardEvent(QAbstractItemView::EditTrigger trigger, const QEvent *event) const;
+ bool shouldAutoScroll(const QPoint &pos) const;
+ void doDelayedItemsLayout(int delay = 0);
+ void interruptDelayedItemsLayout() const;
+
+ bool dropOn(QDropEvent *event, int *row, int *col, QModelIndex *index);
+ bool droppingOnItself(QDropEvent *event, const QModelIndex &index);
+
+ QWidget *editor(const QModelIndex &index, const QStyleOptionViewItem &options);
+ bool sendDelegateEvent(const QModelIndex &index, QEvent *event) const;
+ bool openEditor(const QModelIndex &index, QEvent *event);
+ void updateEditorData(const QModelIndex &topLeft, const QModelIndex &bottomRight);
+
+ QItemSelectionModel::SelectionFlags multiSelectionCommand(const QModelIndex &index,
+ const QEvent *event) const;
+ QItemSelectionModel::SelectionFlags extendedSelectionCommand(const QModelIndex &index,
+ const QEvent *event) const;
+ QItemSelectionModel::SelectionFlags contiguousSelectionCommand(const QModelIndex &index,
+ const QEvent *event) const;
+ virtual void selectAll(QItemSelectionModel::SelectionFlags command);
+
+ inline QItemSelectionModel::SelectionFlags selectionBehaviorFlags() const
+ {
+ switch (selectionBehavior) {
+ case QAbstractItemView::SelectRows: return QItemSelectionModel::Rows;
+ case QAbstractItemView::SelectColumns: return QItemSelectionModel::Columns;
+ case QAbstractItemView::SelectItems: default: return QItemSelectionModel::NoUpdate;
+ }
+ }
+
+#ifndef QT_NO_DRAGANDDROP
+ QAbstractItemView::DropIndicatorPosition position(const QPoint &pos, const QRect &rect, const QModelIndex &idx) const;
+ inline bool canDecode(QDropEvent *e) const {
+ QStringList modelTypes = model->mimeTypes();
+ const QMimeData *mime = e->mimeData();
+ for (int i = 0; i < modelTypes.count(); ++i)
+ if (mime->hasFormat(modelTypes.at(i))
+ && (e->dropAction() & model->supportedDropActions()))
+ return true;
+ return false;
+ }
+
+ inline void paintDropIndicator(QPainter *painter)
+ {
+ if (showDropIndicator && state == QAbstractItemView::DraggingState
+#ifndef QT_NO_CURSOR
+ && viewport->cursor().shape() != Qt::ForbiddenCursor
+#endif
+ ) {
+ QStyleOption opt;
+ opt.init(q_func());
+ opt.rect = dropIndicatorRect;
+ q_func()->style()->drawPrimitive(QStyle::PE_IndicatorItemViewItemDrop, &opt, painter, q_func());
+ }
+ }
+#endif
+
+ inline void releaseEditor(QWidget *editor) const {
+ if (editor) {
+ QObject::disconnect(editor, SIGNAL(destroyed(QObject*)),
+ q_func(), SLOT(editorDestroyed(QObject*)));
+ editor->removeEventFilter(itemDelegate);
+ editor->hide();
+ editor->deleteLater();
+ }
+ }
+
+ inline void executePostedLayout() const {
+ if (delayedPendingLayout && state != QAbstractItemView::CollapsingState) {
+ interruptDelayedItemsLayout();
+ const_cast<QAbstractItemView*>(q_func())->doItemsLayout();
+ }
+ }
+
+ inline void setDirtyRegion(const QRegion &visualRegion) {
+ updateRegion += visualRegion;
+ if (!updateTimer.isActive())
+ updateTimer.start(0, q_func());
+ }
+
+ inline void scrollDirtyRegion(int dx, int dy) {
+ scrollDelayOffset = QPoint(-dx, -dy);
+ updateDirtyRegion();
+ scrollDelayOffset = QPoint(0, 0);
+ }
+
+ inline void scrollContentsBy(int dx, int dy) {
+ scrollDirtyRegion(dx, dy);
+ viewport->scroll(dx, dy);
+ }
+
+ void updateDirtyRegion() {
+ updateTimer.stop();
+ viewport->update(updateRegion);
+ updateRegion = QRegion();
+ }
+
+ void clearOrRemove();
+ void checkPersistentEditorFocus();
+
+ QPixmap renderToPixmap(const QModelIndexList &indexes, QRect *r = 0) const;
+
+ inline QPoint offset() const {
+ const Q_Q(QAbstractItemView);
+ return QPoint(q->isRightToLeft() ? -q->horizontalOffset()
+ : q->horizontalOffset(), q->verticalOffset());
+ }
+
+ QEditorInfo editorForIndex(const QModelIndex &index) const;
+ inline bool hasEditor(const QModelIndex &index) const {
+ return editorForIndex(index).editor != 0;
+ }
+
+ QModelIndex indexForEditor(QWidget *editor) const;
+ void addEditor(const QModelIndex &index, QWidget *editor, bool isStatic);
+ void removeEditor(QWidget *editor);
+
+ inline bool isAnimating() const {
+ return state == QAbstractItemView::AnimatingState;
+ }
+
+ inline QAbstractItemDelegate *delegateForIndex(const QModelIndex &index) const {
+ QAbstractItemDelegate *del;
+ if ((del = rowDelegates.value(index.row(), 0))) return del;
+ if ((del = columnDelegates.value(index.column(), 0))) return del;
+ return itemDelegate;
+ }
+
+ inline bool isIndexValid(const QModelIndex &index) const {
+ return (index.row() >= 0) && (index.column() >= 0) && (index.model() == model);
+ }
+ inline bool isIndexSelectable(const QModelIndex &index) const {
+ return (model->flags(index) & Qt::ItemIsSelectable);
+ }
+ inline bool isIndexEnabled(const QModelIndex &index) const {
+ return (model->flags(index) & Qt::ItemIsEnabled);
+ }
+ inline bool isIndexDropEnabled(const QModelIndex &index) const {
+ return (model->flags(index) & Qt::ItemIsDropEnabled);
+ }
+ inline bool isIndexDragEnabled(const QModelIndex &index) const {
+ return (model->flags(index) & Qt::ItemIsDragEnabled);
+ }
+
+ virtual bool selectionAllowed(const QModelIndex &index) const {
+ // in some views we want to go ahead with selections, even if the index is invalid
+ return isIndexValid(index) && isIndexSelectable(index);
+ }
+
+ // reimplemented from QAbstractScrollAreaPrivate
+ virtual QPoint contentsOffset() const {
+ Q_Q(const QAbstractItemView);
+ return QPoint(q->horizontalOffset(), q->verticalOffset());
+ }
+
+ /**
+ * For now, assume that we have few editors, if we need a more efficient implementation
+ * we should add a QMap<QAbstractItemDelegate*, int> member.
+ */
+ int delegateRefCount(const QAbstractItemDelegate *delegate) const
+ {
+ int ref = 0;
+ if (itemDelegate == delegate)
+ ++ref;
+
+ for (int maps = 0; maps < 2; ++maps) {
+ const QMap<int, QPointer<QAbstractItemDelegate> > *delegates = maps ? &columnDelegates : &rowDelegates;
+ for (QMap<int, QPointer<QAbstractItemDelegate> >::const_iterator it = delegates->begin();
+ it != delegates->end(); ++it) {
+ if (it.value() == delegate) {
+ ++ref;
+ // optimization, we are only interested in the ref count values 0, 1 or >=2
+ if (ref >= 2) {
+ return ref;
+ }
+ }
+ }
+ }
+ return ref;
+ }
+
+ /**
+ * return true if the index is registered as a QPersistentModelIndex
+ */
+ inline bool isPersistent(const QModelIndex &index) const
+ {
+ return static_cast<QAbstractItemModelPrivate *>(model->d_ptr)->persistent.indexes.contains(index);
+ }
+
+ QModelIndexList selectedDraggableIndexes() const;
+
+ QStyleOptionViewItemV4 viewOptionsV4() const;
+
+ QAbstractItemModel *model;
+ QPointer<QAbstractItemDelegate> itemDelegate;
+ QMap<int, QPointer<QAbstractItemDelegate> > rowDelegates;
+ QMap<int, QPointer<QAbstractItemDelegate> > columnDelegates;
+ QPointer<QItemSelectionModel> selectionModel;
+
+ QAbstractItemView::SelectionMode selectionMode;
+ QAbstractItemView::SelectionBehavior selectionBehavior;
+
+ QList<QEditorInfo> editors;
+ QSet<QWidget*> persistent;
+ QWidget *currentlyCommittingEditor;
+
+ QPersistentModelIndex enteredIndex;
+ QPersistentModelIndex pressedIndex;
+ Qt::KeyboardModifiers pressedModifiers;
+ QPoint pressedPosition;
+ bool pressedAlreadySelected;
+
+ //forces the next mouseMoveEvent to send the viewportEntered signal
+ //if the mouse is over the viewport and not over an item
+ bool viewportEnteredNeeded;
+
+ QAbstractItemView::State state;
+ QAbstractItemView::EditTriggers editTriggers;
+ QAbstractItemView::EditTrigger lastTrigger;
+
+ QPersistentModelIndex root;
+ QPersistentModelIndex hover;
+
+ bool tabKeyNavigation;
+
+#ifndef QT_NO_DRAGANDDROP
+ bool showDropIndicator;
+ QRect dropIndicatorRect;
+ bool dragEnabled;
+ QAbstractItemView::DragDropMode dragDropMode;
+ bool overwrite;
+ QAbstractItemView::DropIndicatorPosition dropIndicatorPosition;
+#endif
+
+ QString keyboardInput;
+ QTime keyboardInputTime;
+
+ bool autoScroll;
+ QBasicTimer autoScrollTimer;
+ int autoScrollMargin;
+ int autoScrollCount;
+
+ bool alternatingColors;
+
+ QSize iconSize;
+ Qt::TextElideMode textElideMode;
+
+ QRegion updateRegion; // used for the internal update system
+ QPoint scrollDelayOffset;
+
+ QBasicTimer updateTimer;
+ QBasicTimer delayedEditing;
+ QBasicTimer delayedAutoScroll; //used when an item is clicked
+ QTimeLine timeline;
+
+ QAbstractItemView::ScrollMode verticalScrollMode;
+ QAbstractItemView::ScrollMode horizontalScrollMode;
+
+ bool currentIndexSet;
+
+ bool wrapItemText;
+ mutable bool delayedPendingLayout;
+
+private:
+ mutable QBasicTimer delayedLayout;
+};
+
+QT_BEGIN_INCLUDE_NAMESPACE
+#include <qvector.h>
+QT_END_INCLUDE_NAMESPACE
+
+template <typename T>
+inline int qBinarySearch(const QVector<T> &vec, const T &item, int start, int end)
+{
+ int i = (start + end + 1) >> 1;
+ while (end - start > 0) {
+ if (vec.at(i) > item)
+ end = i - 1;
+ else
+ start = i;
+ i = (start + end + 1) >> 1;
+ }
+ return i;
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_ITEMVIEWS
+
+#endif // QABSTRACTITEMVIEW_P_H
diff --git a/src/gui/itemviews/qabstractproxymodel.cpp b/src/gui/itemviews/qabstractproxymodel.cpp
new file mode 100644
index 0000000000..d6e3a93dfe
--- /dev/null
+++ b/src/gui/itemviews/qabstractproxymodel.cpp
@@ -0,0 +1,282 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qabstractproxymodel.h"
+
+#ifndef QT_NO_PROXYMODEL
+
+#include "qitemselectionmodel.h"
+#include <private/qabstractproxymodel_p.h>
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \since 4.1
+ \class QAbstractProxyModel
+ \brief The QAbstractProxyModel class provides a base class for proxy item
+ models that can do sorting, filtering or other data processing tasks.
+ \ingroup model-view
+
+ This class defines the standard interface that proxy models must use to be
+ able to interoperate correctly with other model/view components. It is not
+ supposed to be instantiated directly.
+
+ All standard proxy models are derived from the QAbstractProxyModel class.
+ If you need to create a new proxy model class, it is usually better to
+ subclass an existing class that provides the closest behavior to the one
+ you want to provide.
+
+ Proxy models that filter or sort items of data from a source model should
+ be created by using or subclassing QSortFilterProxyModel.
+
+ To subclass QAbstractProxyModel, you need to implement mapFromSource() and
+ mapToSource(). The mapSelectionFromSource() and mapSelectionToSource()
+ functions only need to be reimplemented if you need a behavior different
+ from the default behavior.
+
+ \note If the source model is deleted or no source model is specified, the
+ proxy model operates on a empty placeholder model.
+
+ \sa QSortFilterProxyModel, QAbstractItemModel, {Model/View Programming}
+*/
+
+//detects the deletion of the source model
+void QAbstractProxyModelPrivate::_q_sourceModelDestroyed()
+{
+ model = QAbstractItemModelPrivate::staticEmptyModel();
+}
+
+/*!
+ Constructs a proxy model with the given \a parent.
+*/
+
+QAbstractProxyModel::QAbstractProxyModel(QObject *parent)
+ :QAbstractItemModel(*new QAbstractProxyModelPrivate, parent)
+{
+ setSourceModel(QAbstractItemModelPrivate::staticEmptyModel());
+}
+
+/*!
+ \internal
+*/
+
+QAbstractProxyModel::QAbstractProxyModel(QAbstractProxyModelPrivate &dd, QObject *parent)
+ : QAbstractItemModel(dd, parent)
+{
+ setSourceModel(QAbstractItemModelPrivate::staticEmptyModel());
+}
+
+/*!
+ Destroys the proxy model.
+*/
+QAbstractProxyModel::~QAbstractProxyModel()
+{
+
+}
+
+/*!
+ Sets the given \a sourceModel to be processed by the proxy model.
+*/
+void QAbstractProxyModel::setSourceModel(QAbstractItemModel *sourceModel)
+{
+ Q_D(QAbstractProxyModel);
+ if (d->model)
+ disconnect(d->model, SIGNAL(destroyed()), this, SLOT(_q_sourceModelDestroyed()));
+
+ if (sourceModel) {
+ d->model = sourceModel;
+ connect(d->model, SIGNAL(destroyed()), this, SLOT(_q_sourceModelDestroyed()));
+ } else {
+ d->model = QAbstractItemModelPrivate::staticEmptyModel();
+ }
+}
+
+/*!
+ Returns the model that contains the data that is available through the proxy model.
+*/
+QAbstractItemModel *QAbstractProxyModel::sourceModel() const
+{
+ Q_D(const QAbstractProxyModel);
+ if (d->model == QAbstractItemModelPrivate::staticEmptyModel())
+ return 0;
+ return d->model;
+}
+
+/*!
+ \reimp
+ */
+bool QAbstractProxyModel::submit()
+{
+ Q_D(QAbstractProxyModel);
+ return d->model->submit();
+}
+
+/*!
+ \reimp
+ */
+void QAbstractProxyModel::revert()
+{
+ Q_D(QAbstractProxyModel);
+ d->model->revert();
+}
+
+
+/*!
+ \fn QModelIndex QAbstractProxyModel::mapToSource(const QModelIndex &proxyIndex) const
+
+ Reimplement this function to return the model index in the source model that
+ corresponds to the \a proxyIndex in the proxy model.
+
+ \sa mapFromSource()
+*/
+
+/*!
+ \fn QModelIndex QAbstractProxyModel::mapFromSource(const QModelIndex &sourceIndex) const
+
+ Reimplement this function to return the model index in the proxy model that
+ corresponds to the \a sourceIndex from the source model.
+
+ \sa mapToSource()
+*/
+
+/*!
+ Returns a source selection mapped from the specified \a proxySelection.
+
+ Reimplement this method to map proxy selections to source selections.
+ */
+QItemSelection QAbstractProxyModel::mapSelectionToSource(const QItemSelection &proxySelection) const
+{
+ QModelIndexList proxyIndexes = proxySelection.indexes();
+ QItemSelection sourceSelection;
+ for (int i = 0; i < proxyIndexes.size(); ++i)
+ sourceSelection << QItemSelectionRange(mapToSource(proxyIndexes.at(i)));
+ return sourceSelection;
+}
+
+/*!
+ Returns a proxy selection mapped from the specified \a sourceSelection.
+
+ Reimplement this method to map source selections to proxy selections.
+*/
+QItemSelection QAbstractProxyModel::mapSelectionFromSource(const QItemSelection &sourceSelection) const
+{
+ QModelIndexList sourceIndexes = sourceSelection.indexes();
+ QItemSelection proxySelection;
+ for (int i = 0; i < sourceIndexes.size(); ++i)
+ proxySelection << QItemSelectionRange(mapFromSource(sourceIndexes.at(i)));
+ return proxySelection;
+}
+
+/*!
+ \reimp
+ */
+QVariant QAbstractProxyModel::data(const QModelIndex &proxyIndex, int role) const
+{
+ Q_D(const QAbstractProxyModel);
+ return d->model->data(mapToSource(proxyIndex), role);
+}
+
+/*!
+ \reimp
+ */
+QVariant QAbstractProxyModel::headerData(int section, Qt::Orientation orientation, int role) const
+{
+ Q_D(const QAbstractProxyModel);
+ int sourceSection;
+ if (orientation == Qt::Horizontal) {
+ const QModelIndex proxyIndex = index(0, section);
+ sourceSection = mapToSource(proxyIndex).column();
+ } else {
+ const QModelIndex proxyIndex = index(section, 0);
+ sourceSection = mapToSource(proxyIndex).row();
+ }
+ return d->model->headerData(sourceSection, orientation, role);
+}
+
+/*!
+ \reimp
+ */
+QMap<int, QVariant> QAbstractProxyModel::itemData(const QModelIndex &proxyIndex) const
+{
+ Q_D(const QAbstractProxyModel);
+ return d->model->itemData(mapToSource(proxyIndex));
+}
+
+/*!
+ \reimp
+ */
+Qt::ItemFlags QAbstractProxyModel::flags(const QModelIndex &index) const
+{
+ Q_D(const QAbstractProxyModel);
+ return d->model->flags(mapToSource(index));
+}
+
+/*!
+ \reimp
+ */
+bool QAbstractProxyModel::setData(const QModelIndex &index, const QVariant &value, int role)
+{
+ Q_D(QAbstractProxyModel);
+ return d->model->setData(mapToSource(index), value, role);
+}
+
+/*!
+ \reimp
+ */
+bool QAbstractProxyModel::setHeaderData(int section, Qt::Orientation orientation, const QVariant &value, int role)
+{
+ Q_D(QAbstractProxyModel);
+ int sourceSection;
+ if (orientation == Qt::Horizontal) {
+ const QModelIndex proxyIndex = index(0, section);
+ sourceSection = mapToSource(proxyIndex).column();
+ } else {
+ const QModelIndex proxyIndex = index(section, 0);
+ sourceSection = mapToSource(proxyIndex).row();
+ }
+ return d->model->setHeaderData(sourceSection, orientation, value, role);
+}
+
+QT_END_NAMESPACE
+
+#include "moc_qabstractproxymodel.cpp"
+
+#endif // QT_NO_PROXYMODEL
diff --git a/src/gui/itemviews/qabstractproxymodel.h b/src/gui/itemviews/qabstractproxymodel.h
new file mode 100644
index 0000000000..51c8829ef5
--- /dev/null
+++ b/src/gui/itemviews/qabstractproxymodel.h
@@ -0,0 +1,101 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QABSTRACTPROXYMODEL_H
+#define QABSTRACTPROXYMODEL_H
+
+#include <QtCore/qabstractitemmodel.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+#ifndef QT_NO_PROXYMODEL
+
+class QAbstractProxyModelPrivate;
+class QItemSelection;
+
+class Q_GUI_EXPORT QAbstractProxyModel : public QAbstractItemModel
+{
+ Q_OBJECT
+
+public:
+ QAbstractProxyModel(QObject *parent = 0);
+ ~QAbstractProxyModel();
+
+ virtual void setSourceModel(QAbstractItemModel *sourceModel);
+ QAbstractItemModel *sourceModel() const;
+
+ virtual QModelIndex mapToSource(const QModelIndex &proxyIndex) const = 0;
+ virtual QModelIndex mapFromSource(const QModelIndex &sourceIndex) const = 0;
+
+ virtual QItemSelection mapSelectionToSource(const QItemSelection &selection) const;
+ virtual QItemSelection mapSelectionFromSource(const QItemSelection &selection) const;
+
+ bool submit();
+ void revert();
+
+ QVariant data(const QModelIndex &proxyIndex, int role = Qt::DisplayRole) const;
+ QVariant headerData(int section, Qt::Orientation orientation, int role) const;
+ QMap<int, QVariant> itemData(const QModelIndex &index) const;
+ Qt::ItemFlags flags(const QModelIndex &index) const;
+
+ bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole);
+ bool setHeaderData(int section, Qt::Orientation orientation, const QVariant &value, int role = Qt::EditRole);
+
+protected:
+ QAbstractProxyModel(QAbstractProxyModelPrivate &, QObject *parent);
+
+private:
+ Q_DECLARE_PRIVATE(QAbstractProxyModel)
+ Q_DISABLE_COPY(QAbstractProxyModel)
+ Q_PRIVATE_SLOT(d_func(), void _q_sourceModelDestroyed())
+};
+
+#endif // QT_NO_PROXYMODEL
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QABSTRACTPROXYMODEL_H
diff --git a/src/gui/itemviews/qabstractproxymodel_p.h b/src/gui/itemviews/qabstractproxymodel_p.h
new file mode 100644
index 0000000000..a31995392e
--- /dev/null
+++ b/src/gui/itemviews/qabstractproxymodel_p.h
@@ -0,0 +1,76 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QABSTRACTPROXYMODEL_P_H
+#define QABSTRACTPROXYMODEL_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of QAbstractItemModel*. This header file may change from version
+// to version without notice, or even be removed.
+//
+// We mean it.
+//
+//
+
+#include "private/qabstractitemmodel_p.h"
+
+#ifndef QT_NO_PROXYMODEL
+
+QT_BEGIN_NAMESPACE
+
+class QAbstractProxyModelPrivate : public QAbstractItemModelPrivate
+{
+ Q_DECLARE_PUBLIC(QAbstractProxyModel)
+public:
+ QAbstractProxyModelPrivate() : QAbstractItemModelPrivate(), model(0) {}
+ QAbstractItemModel *model;
+ virtual void _q_sourceModelDestroyed();
+};
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_PROXYMODEL
+
+#endif // QABSTRACTPROXYMODEL_P_H
diff --git a/src/gui/itemviews/qbsptree.cpp b/src/gui/itemviews/qbsptree.cpp
new file mode 100644
index 0000000000..a09df53e56
--- /dev/null
+++ b/src/gui/itemviews/qbsptree.cpp
@@ -0,0 +1,145 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qbsptree_p.h"
+
+QT_BEGIN_NAMESPACE
+
+QBspTree::QBspTree() : depth(6), visited(0) {}
+
+void QBspTree::create(int n, int d)
+{
+ // simple heuristics to find the best tree depth
+ if (d == -1) {
+ int c;
+ for (c = 0; n; ++c)
+ n = n / 10;
+ depth = c << 1;
+ } else {
+ depth = d;
+ }
+ depth = qMax(depth, uint(1));
+
+ nodes.resize((1 << depth) - 1); // resize to number of nodes
+ leaves.resize(1 << depth); // resize to number of leaves
+}
+
+void QBspTree::destroy()
+{
+ leaves.clear();
+ nodes.clear();
+}
+
+void QBspTree::climbTree(const QRect &rect, callback *function, QBspTreeData data)
+{
+ if (nodes.isEmpty())
+ return;
+ ++visited;
+ climbTree(rect, function, data, 0);
+}
+
+void QBspTree::climbTree(const QRect &area, callback *function, QBspTreeData data, int index)
+{
+ if (index >= nodes.count()) { // the index points to a leaf
+ Q_ASSERT(!nodes.isEmpty());
+ function(leaf(index - nodes.count()), area, visited, data);
+ return;
+ }
+
+ Node::Type t = (Node::Type) nodes.at(index).type;
+
+ int pos = nodes.at(index).pos;
+ int idx = firstChildIndex(index);
+ if (t == Node::VerticalPlane) {
+ if (area.left() < pos)
+ climbTree(area, function, data, idx); // back
+ if (area.right() >= pos)
+ climbTree(area, function, data, idx + 1); // front
+ } else {
+ if (area.top() < pos)
+ climbTree(area, function, data, idx); // back
+ if (area.bottom() >= pos)
+ climbTree(area, function, data, idx + 1); // front
+ }
+}
+
+void QBspTree::init(const QRect &area, int depth, NodeType type, int index)
+{
+ Node::Type t = Node::None; // t should never have this value
+ if (type == Node::Both) // if both planes are specified, use 2d bsp
+ t = (depth & 1) ? Node::HorizontalPlane : Node::VerticalPlane;
+ else
+ t = type;
+ QPoint center = area.center();
+ nodes[index].pos = (t == Node::VerticalPlane ? center.x() : center.y());
+ nodes[index].type = t;
+
+ QRect front = area;
+ QRect back = area;
+
+ if (t == Node::VerticalPlane) {
+ front.setLeft(center.x());
+ back.setRight(center.x() - 1); // front includes the center
+ } else { // t == Node::HorizontalPlane
+ front.setTop(center.y());
+ back.setBottom(center.y() - 1);
+ }
+
+ int idx = firstChildIndex(index);
+ if (--depth) {
+ init(back, depth, type, idx);
+ init(front, depth, type, idx + 1);
+ }
+}
+
+void QBspTree::insert(QVector<int> &leaf, const QRect &, uint, QBspTreeData data)
+{
+ leaf.append(data.i);
+}
+
+void QBspTree::remove(QVector<int> &leaf, const QRect &, uint, QBspTreeData data)
+{
+ int i = leaf.indexOf(data.i);
+ if (i != -1)
+ leaf.remove(i);
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/itemviews/qbsptree_p.h b/src/gui/itemviews/qbsptree_p.h
new file mode 100644
index 0000000000..ff91bfbd38
--- /dev/null
+++ b/src/gui/itemviews/qbsptree_p.h
@@ -0,0 +1,119 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QBSPTREE_P_H
+#define QBSPTREE_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 <qvector.h>
+#include <qrect.h>
+
+QT_BEGIN_NAMESPACE
+
+class QBspTree
+{
+public:
+
+ struct Node
+ {
+ enum Type { None = 0, VerticalPlane = 1, HorizontalPlane = 2, Both = 3 };
+ inline Node() : pos(0), type(None) {}
+ int pos;
+ Type type;
+ };
+ typedef Node::Type NodeType;
+
+ struct Data
+ {
+ Data(void *p) : ptr(p) {}
+ Data(int n) : i(n) {}
+ union {
+ void *ptr;
+ int i;
+ };
+ };
+ typedef QBspTree::Data QBspTreeData;
+ typedef void callback(QVector<int> &leaf, const QRect &area, uint visited, QBspTreeData data);
+
+ QBspTree();
+
+ void create(int n, int d = -1);
+ void destroy();
+
+ inline void init(const QRect &area, NodeType type) { init(area, depth, type, 0); }
+
+ void climbTree(const QRect &rect, callback *function, QBspTreeData data);
+
+ inline int leafCount() const { return leaves.count(); }
+ inline QVector<int> &leaf(int i) { return leaves[i]; }
+ inline void insertLeaf(const QRect &r, int i) { climbTree(r, &insert, i, 0); }
+ inline void removeLeaf(const QRect &r, int i) { climbTree(r, &remove, i, 0); }
+
+protected:
+ void init(const QRect &area, int depth, NodeType type, int index);
+ void climbTree(const QRect &rect, callback *function, QBspTreeData data, int index);
+
+ inline int parentIndex(int i) const { return (i & 1) ? ((i - 1) / 2) : ((i - 2) / 2); }
+ inline int firstChildIndex(int i) const { return ((i * 2) + 1); }
+
+ static void insert(QVector<int> &leaf, const QRect &area, uint visited, QBspTreeData data);
+ static void remove(QVector<int> &leaf, const QRect &area, uint visited, QBspTreeData data);
+
+private:
+ uint depth;
+ mutable uint visited;
+ QVector<Node> nodes;
+ mutable QVector< QVector<int> > leaves; // the leaves are just indices into the items
+};
+
+QT_END_NAMESPACE
+
+#endif // QBSPTREE_P_H
diff --git a/src/gui/itemviews/qcolumnview.cpp b/src/gui/itemviews/qcolumnview.cpp
new file mode 100644
index 0000000000..4fb08bb36a
--- /dev/null
+++ b/src/gui/itemviews/qcolumnview.cpp
@@ -0,0 +1,1128 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <qglobal.h>
+
+#ifndef QT_NO_COLUMNVIEW
+
+#include "qcolumnview.h"
+#include "qcolumnview_p.h"
+#include "qcolumnviewgrip_p.h"
+
+#include <qlistview.h>
+#include <qabstractitemdelegate.h>
+#include <qscrollbar.h>
+#include <qpainter.h>
+#include <qdebug.h>
+#include <qpainterpath.h>
+
+QT_BEGIN_NAMESPACE
+
+#define ANIMATION_DURATION_MSEC 150
+
+/*!
+ \since 4.3
+ \class QColumnView
+ \brief The QColumnView class provides a model/view implementation of a column view.
+ \ingroup model-view
+ \ingroup advanced
+ \mainclass
+
+ QColumnView displays a model in a number of QListViews, one for each
+ hierarchy in the tree. This is sometimes referred to as a cascading list.
+
+ The QColumnView class is one of the \l{Model/View Classes}
+ and is part of Qt's \l{Model/View Programming}{model/view framework}.
+
+ QColumnView implements the interfaces defined by the
+ QAbstractItemView class to allow it to display data provided by
+ models derived from the QAbstractItemModel class.
+
+ \image qcolumnview.png
+
+ \sa \link model-view-programming.html Model/View Programming\endlink
+*/
+
+/*!
+ Constructs a column view with a \a parent to represent a model's
+ data. Use setModel() to set the model.
+
+ \sa QAbstractItemModel
+*/
+QColumnView::QColumnView(QWidget * parent)
+: QAbstractItemView(*new QColumnViewPrivate, parent)
+{
+ Q_D(QColumnView);
+ d->initialize();
+}
+
+/*!
+ \internal
+*/
+QColumnView::QColumnView(QColumnViewPrivate & dd, QWidget * parent)
+: QAbstractItemView(dd, parent)
+{
+ Q_D(QColumnView);
+ d->initialize();
+}
+
+void QColumnViewPrivate::initialize()
+{
+ Q_Q(QColumnView);
+ q->setTextElideMode(Qt::ElideMiddle);
+ q->connect(&currentAnimation, SIGNAL(frameChanged(int)),
+ q->horizontalScrollBar(), SLOT(setValue(int)));
+ q->connect(&currentAnimation, SIGNAL(finished()), q, SLOT(_q_changeCurrentColumn()));
+ delete itemDelegate;
+ q->setItemDelegate(new QColumnViewDelegate(q));
+}
+
+/*!
+ Destroys the column view.
+*/
+QColumnView::~QColumnView()
+{
+}
+
+/*!
+ \property QColumnView::resizeGripsVisible
+ \brief the way to specify if the list views gets resize grips or not
+
+ By default, \c visible is set to true
+
+ \sa setRootIndex()
+*/
+void QColumnView::setResizeGripsVisible(bool visible)
+{
+ Q_D(QColumnView);
+ if (d->showResizeGrips == visible)
+ return;
+ d->showResizeGrips = visible;
+ for (int i = 0; i < d->columns.count(); ++i) {
+ QAbstractItemView *view = d->columns[i];
+ if (visible) {
+ QColumnViewGrip *grip = new QColumnViewGrip(view);
+ view->setCornerWidget(grip);
+ connect(grip, SIGNAL(gripMoved(int)), this, SLOT(_q_gripMoved(int)));
+ } else {
+ QWidget *widget = view->cornerWidget();
+ view->setCornerWidget(0);
+ widget->deleteLater();
+ }
+ }
+}
+
+bool QColumnView::resizeGripsVisible() const
+{
+ Q_D(const QColumnView);
+ return d->showResizeGrips;
+}
+
+/*!
+ \reimp
+*/
+void QColumnView::setModel(QAbstractItemModel *model)
+{
+ Q_D(QColumnView);
+ if (model == d->model)
+ return;
+ d->closeColumns();
+ QAbstractItemView::setModel(model);
+}
+
+/*!
+ \reimp
+*/
+void QColumnView::setRootIndex(const QModelIndex &index)
+{
+ Q_D(QColumnView);
+ if (!model())
+ return;
+
+ d->closeColumns();
+ Q_ASSERT(d->columns.count() == 0);
+
+ QAbstractItemView *view = d->createColumn(index, true);
+ if (view->selectionModel())
+ view->selectionModel()->deleteLater();
+ if (view->model())
+ view->setSelectionModel(selectionModel());
+
+ QAbstractItemView::setRootIndex(index);
+ d->updateScrollbars();
+}
+
+/*!
+ \reimp
+*/
+bool QColumnView::isIndexHidden(const QModelIndex &index) const
+{
+ Q_UNUSED(index);
+ return false;
+}
+
+/*!
+ \reimp
+*/
+QModelIndex QColumnView::indexAt(const QPoint &point) const
+{
+ Q_D(const QColumnView);
+ for (int i = 0; i < d->columns.size(); ++i) {
+ QPoint topLeft = d->columns.at(i)->frameGeometry().topLeft();
+ QPoint adjustedPoint(point.x() - topLeft.x(), point.y() - topLeft.y());
+ QModelIndex index = d->columns.at(i)->indexAt(adjustedPoint);
+ if (index.isValid())
+ return index;
+ }
+ return QModelIndex();
+}
+
+/*!
+ \reimp
+*/
+QRect QColumnView::visualRect(const QModelIndex &index) const
+{
+ if (!index.isValid())
+ return QRect();
+
+ Q_D(const QColumnView);
+ for (int i = 0; i < d->columns.size(); ++i) {
+ QRect rect = d->columns.at(i)->visualRect(index);
+ if (!rect.isNull()) {
+ rect.translate(d->columns.at(i)->frameGeometry().topLeft());
+ return rect;
+ }
+ }
+ return QRect();
+}
+
+/*!
+ \reimp
+ */
+void QColumnView::scrollContentsBy(int dx, int dy)
+{
+ Q_D(QColumnView);
+ if (d->columns.isEmpty() || dx == 0)
+ return;
+
+ dx = isRightToLeft() ? -dx : dx;
+ for (int i = 0; i < d->columns.count(); ++i)
+ d->columns.at(i)->move(d->columns.at(i)->x() + dx, 0);
+ d->offset += dx;
+ QAbstractItemView::scrollContentsBy(dx, dy);
+}
+
+/*!
+ \reimp
+*/
+void QColumnView::scrollTo(const QModelIndex &index, ScrollHint hint)
+{
+ Q_D(QColumnView);
+ Q_UNUSED(hint);
+ if (!index.isValid() || d->columns.isEmpty())
+ return;
+
+ if (d->currentAnimation.state() == QTimeLine::Running)
+ return;
+
+ d->currentAnimation.stop();
+
+ // Fill up what is needed to get to index
+ d->closeColumns(index, true);
+
+ QModelIndex indexParent = index.parent();
+ // Find the left edge of the column that contains index
+ int currentColumn = 0;
+ int leftEdge = 0;
+ while (currentColumn < d->columns.size()) {
+ if (indexParent == d->columns.at(currentColumn)->rootIndex())
+ break;
+ leftEdge += d->columns.at(currentColumn)->width();
+ ++currentColumn;
+ }
+
+ // Don't let us scroll above the root index
+ if (currentColumn == d->columns.size())
+ return;
+
+ int indexColumn = currentColumn;
+ // Find the width of what we want to show (i.e. the right edge)
+ int visibleWidth = d->columns.at(currentColumn)->width();
+ // We want to always try to show two columns
+ if (currentColumn + 1 < d->columns.size()) {
+ ++currentColumn;
+ visibleWidth += d->columns.at(currentColumn)->width();
+ }
+
+ int rightEdge = leftEdge + visibleWidth;
+ if (isRightToLeft()) {
+ leftEdge = viewport()->width() - leftEdge;
+ rightEdge = leftEdge - visibleWidth;
+ qSwap(rightEdge, leftEdge);
+ }
+
+ // If it is already visible don't animate
+ if (leftEdge > -horizontalOffset()
+ && rightEdge <= ( -horizontalOffset() + viewport()->size().width())) {
+ d->columns.at(indexColumn)->scrollTo(index);
+ d->_q_changeCurrentColumn();
+ return;
+ }
+
+ int newScrollbarValue = 0;
+ if (isRightToLeft()) {
+ if (leftEdge < 0) {
+ // scroll to the right
+ newScrollbarValue = viewport()->size().width() - leftEdge;
+ } else {
+ // scroll to the left
+ newScrollbarValue = rightEdge + horizontalOffset();
+ }
+ } else {
+ if (leftEdge > -horizontalOffset()) {
+ // scroll to the right
+ newScrollbarValue = rightEdge - viewport()->size().width();
+ } else {
+ // scroll to the left
+ newScrollbarValue = leftEdge;
+ }
+ }
+
+ //horizontalScrollBar()->setValue(newScrollbarValue);
+ //d->_q_changeCurrentColumn();
+ //return;
+ // or do the following currentAnimation
+
+ int oldValue = horizontalScrollBar()->value();
+
+ if (oldValue < newScrollbarValue) {
+ d->currentAnimation.setFrameRange(oldValue, newScrollbarValue);
+ d->currentAnimation.setDirection(QTimeLine::Forward);
+ d->currentAnimation.setCurrentTime(0);
+ } else {
+ d->currentAnimation.setFrameRange(newScrollbarValue, oldValue);
+ d->currentAnimation.setDirection(QTimeLine::Backward);
+ }
+ d->currentAnimation.start();
+}
+
+/*!
+ \reimp
+ Move left should go to the parent index
+ Move right should go to the child index or down if there is no child
+*/
+QModelIndex QColumnView::moveCursor(CursorAction cursorAction, Qt::KeyboardModifiers modifiers)
+{
+ // the child views which have focus get to deal with this first and if
+ // they don't accept it then it comes up this view and we only grip left/right
+ Q_UNUSED(modifiers);
+ if (!model())
+ return QModelIndex();
+
+ QModelIndex current = currentIndex();
+ if (isRightToLeft()) {
+ if (cursorAction == MoveLeft)
+ cursorAction = MoveRight;
+ else if (cursorAction == MoveRight)
+ cursorAction = MoveLeft;
+ }
+ switch (cursorAction) {
+ case MoveLeft:
+ if (current.parent().isValid() && current.parent() != rootIndex())
+ return (current.parent());
+ else
+ return current;
+ break;
+
+ case MoveRight:
+ if (model()->hasChildren(current))
+ return model()->index(0, 0, current);
+ else
+ return current.sibling(current.row() + 1, current.column());
+ break;
+
+ default:
+ break;
+ }
+
+ return QModelIndex();
+}
+
+/*!
+ \reimp
+*/
+void QColumnView::resizeEvent(QResizeEvent *event)
+{
+ Q_D(QColumnView);
+ d->doLayout();
+ d->updateScrollbars();
+ if (!isRightToLeft()) {
+ int diff = event->oldSize().width() - event->size().width();
+ if (diff < 0 && horizontalScrollBar()->isVisible()
+ && horizontalScrollBar()->value() == horizontalScrollBar()->maximum()) {
+ horizontalScrollBar()->setMaximum(horizontalScrollBar()->maximum() + diff);
+ }
+ }
+ QAbstractItemView::resizeEvent(event);
+}
+
+/*!
+ \internal
+*/
+void QColumnViewPrivate::updateScrollbars()
+{
+ Q_Q(QColumnView);
+ if (currentAnimation.state() == QTimeLine::Running)
+ return;
+
+ // find the total horizontal length of the laid out columns
+ int horizontalLength = 0;
+ if (!columns.isEmpty()) {
+ horizontalLength = (columns.last()->x() + columns.last()->width()) - columns.first()->x();
+ if (horizontalLength <= 0) // reverse mode
+ horizontalLength = (columns.first()->x() + columns.first()->width()) - columns.last()->x();
+ }
+
+ QSize viewportSize = q->viewport()->size();
+ if (horizontalLength < viewportSize.width() && q->horizontalScrollBar()->value() == 0) {
+ q->horizontalScrollBar()->setRange(0, 0);
+ } else {
+ int visibleLength = qMin(horizontalLength + q->horizontalOffset(), viewportSize.width());
+ int hiddenLength = horizontalLength - visibleLength;
+ if (hiddenLength != q->horizontalScrollBar()->maximum())
+ q->horizontalScrollBar()->setRange(0, hiddenLength);
+ }
+ if (!columns.isEmpty()) {
+ int pageStepSize = columns.at(0)->width();
+ if (pageStepSize != q->horizontalScrollBar()->pageStep())
+ q->horizontalScrollBar()->setPageStep(pageStepSize);
+ }
+ bool visible = (q->horizontalScrollBar()->maximum() > 0);
+ if (visible != q->horizontalScrollBar()->isVisible())
+ q->horizontalScrollBar()->setVisible(visible);
+}
+
+/*!
+ \reimp
+*/
+int QColumnView::horizontalOffset() const
+{
+ Q_D(const QColumnView);
+ return d->offset;
+}
+
+/*!
+ \reimp
+*/
+int QColumnView::verticalOffset() const
+{
+ return 0;
+}
+
+/*!
+ \reimp
+*/
+QRegion QColumnView::visualRegionForSelection(const QItemSelection &selection) const
+{
+ int ranges = selection.count();
+
+ if (ranges == 0)
+ return QRect();
+
+ // Note that we use the top and bottom functions of the selection range
+ // since the data is stored in rows.
+ int firstRow = selection.at(0).top();
+ int lastRow = selection.at(0).top();
+ for (int i = 0; i < ranges; ++i) {
+ firstRow = qMin(firstRow, selection.at(i).top());
+ lastRow = qMax(lastRow, selection.at(i).bottom());
+ }
+
+ QModelIndex firstIdx = model()->index(qMin(firstRow, lastRow), 0, rootIndex());
+ QModelIndex lastIdx = model()->index(qMax(firstRow, lastRow), 0, rootIndex());
+
+ if (firstIdx == lastIdx)
+ return visualRect(firstIdx);
+
+ QRegion firstRegion = visualRect(firstIdx);
+ QRegion lastRegion = visualRect(lastIdx);
+ return firstRegion.unite(lastRegion);
+}
+
+/*!
+ \reimp
+*/
+void QColumnView::setSelection(const QRect &rect, QItemSelectionModel::SelectionFlags command)
+{
+ Q_UNUSED(rect);
+ Q_UNUSED(command);
+}
+
+/*!
+ \reimp
+*/
+void QColumnView::setSelectionModel(QItemSelectionModel *newSelectionModel)
+{
+ Q_D(const QColumnView);
+ for (int i = 0; i < d->columns.size(); ++i) {
+ if (d->columns.at(i)->selectionModel() == selectionModel()) {
+ d->columns.at(i)->setSelectionModel(newSelectionModel);
+ break;
+ }
+ }
+ QAbstractItemView::setSelectionModel(newSelectionModel);
+}
+
+/*!
+ \reimp
+*/
+QSize QColumnView::sizeHint() const
+{
+ Q_D(const QColumnView);
+ QSize sizeHint;
+ for (int i = 0; i < d->columns.size(); ++i) {
+ sizeHint += d->columns.at(i)->sizeHint();
+ }
+ return sizeHint.expandedTo(QAbstractItemView::sizeHint());
+}
+
+/*!
+ \internal
+ Move all widgets from the corner grip and to the right
+ */
+void QColumnViewPrivate::_q_gripMoved(int offset)
+{
+ Q_Q(QColumnView);
+
+ QObject *grip = q->sender();
+ Q_ASSERT(grip);
+
+ if (q->isRightToLeft())
+ offset = -1 * offset;
+
+ bool found = false;
+ for (int i = 0; i < columns.size(); ++i) {
+ if (!found && columns.at(i)->cornerWidget() == grip) {
+ found = true;
+ columnSizes[i] = columns.at(i)->width();
+ if (q->isRightToLeft())
+ columns.at(i)->move(columns.at(i)->x() + offset, 0);
+ continue;
+ }
+ if (!found)
+ continue;
+
+ int currentX = columns.at(i)->x();
+ columns.at(i)->move(currentX + offset, 0);
+ }
+
+ updateScrollbars();
+}
+
+/*!
+ \internal
+
+ Find where the current columns intersect parent's columns
+
+ Delete any extra columns and insert any needed columns.
+ */
+void QColumnViewPrivate::closeColumns(const QModelIndex &parent, bool build)
+{
+ if (columns.isEmpty())
+ return;
+
+ bool clearAll = !parent.isValid();
+ bool passThroughRoot = false;
+
+ QList<QModelIndex> dirsToAppend;
+
+ // Find the last column that matches the parent's tree
+ int currentColumn = -1;
+ QModelIndex parentIndex = parent;
+ while (currentColumn == -1 && parentIndex.isValid()) {
+ if (columns.isEmpty())
+ break;
+ parentIndex = parentIndex.parent();
+ if (root == parentIndex)
+ passThroughRoot = true;
+ if (!parentIndex.isValid())
+ break;
+ for (int i = columns.size() - 1; i >= 0; --i) {
+ if (columns.at(i)->rootIndex() == parentIndex) {
+ currentColumn = i;
+ break;
+ }
+ }
+ if (currentColumn == -1)
+ dirsToAppend.append(parentIndex);
+ }
+
+ // Someone wants to go to an index that can be reached without changing
+ // the root index, don't allow them
+ if (!clearAll && !passThroughRoot && currentColumn == -1)
+ return;
+
+ if (currentColumn == -1 && parent.isValid())
+ currentColumn = 0;
+
+ // Optimization so we don't go deleting and then creating the same thing
+ bool alreadyExists = false;
+ if (build && columns.size() > currentColumn + 1) {
+ bool viewingParent = (columns.at(currentColumn + 1)->rootIndex() == parent);
+ bool viewingChild = (!model->hasChildren(parent)
+ && !columns.at(currentColumn + 1)->rootIndex().isValid());
+ if (viewingParent || viewingChild) {
+ currentColumn++;
+ alreadyExists = true;
+ }
+ }
+
+ // Delete columns that don't match our path
+ for (int i = columns.size() - 1; i > currentColumn; --i) {
+ QAbstractItemView* notShownAnymore = columns.at(i);
+ columns.removeAt(i);
+ notShownAnymore->setVisible(false);
+ if (notShownAnymore != previewColumn)
+ notShownAnymore->deleteLater();
+ }
+
+ if (columns.isEmpty()) {
+ offset = 0;
+ updateScrollbars();
+ }
+
+ // Now fill in missing columns
+ while (!dirsToAppend.isEmpty()) {
+ QAbstractItemView *newView = createColumn(dirsToAppend.takeLast(), true);
+ if (!dirsToAppend.isEmpty())
+ newView->setCurrentIndex(dirsToAppend.last());
+ }
+
+ if (build && !alreadyExists)
+ createColumn(parent, false);
+}
+
+void QColumnViewPrivate::_q_clicked(const QModelIndex &index)
+{
+ Q_Q(QColumnView);
+ QModelIndex parent = index.parent();
+ QAbstractItemView *columnClicked = 0;
+ for (int column = 0; column < columns.count(); ++column) {
+ if (columns.at(column)->rootIndex() == parent) {
+ columnClicked = columns[column];
+ break;
+ }
+ }
+ if (q->selectionModel() && columnClicked) {
+ QItemSelectionModel::SelectionFlags flags = QItemSelectionModel::Current;
+ if (columnClicked->selectionModel()->isSelected(index))
+ flags |= QItemSelectionModel::Select;
+ q->selectionModel()->setCurrentIndex(index, flags);
+ }
+}
+
+/*!
+ \internal
+ Create a new column for \a index. A grip is attached if requested and it is shown
+ if requested.
+
+ Return the new view
+
+ \sa createColumn() setPreviewWidget()
+ \sa doLayout()
+*/
+QAbstractItemView *QColumnViewPrivate::createColumn(const QModelIndex &index, bool show)
+{
+ Q_Q(QColumnView);
+ QAbstractItemView *view = 0;
+ if (model->hasChildren(index)) {
+ view = q->createColumn(index);
+ q->connect(view, SIGNAL(clicked(const QModelIndex &)),
+ q, SLOT(_q_clicked(const QModelIndex &)));
+ } else {
+ if (!previewColumn)
+ setPreviewWidget(new QWidget(q));
+ view = previewColumn;
+ view->setMinimumWidth(qMax(view->minimumWidth(), previewWidget->minimumWidth()));
+ }
+
+ q->connect(view, SIGNAL(activated(const QModelIndex &)),
+ q, SIGNAL(activated(const QModelIndex &)));
+ q->connect(view, SIGNAL(clicked(const QModelIndex &)),
+ q, SIGNAL(clicked(const QModelIndex &)));
+ q->connect(view, SIGNAL(doubleClicked(const QModelIndex &)),
+ q, SIGNAL(doubleClicked(const QModelIndex &)));
+ q->connect(view, SIGNAL(entered(const QModelIndex &)),
+ q, SIGNAL(entered(const QModelIndex &)));
+ q->connect(view, SIGNAL(pressed(const QModelIndex &)),
+ q, SIGNAL(pressed(const QModelIndex &)));
+
+ view->setFocusPolicy(Qt::NoFocus);
+ view->setParent(q->viewport());
+ Q_ASSERT(view);
+
+ // Setup corner grip
+ if (showResizeGrips) {
+ QColumnViewGrip *grip = new QColumnViewGrip(view);
+ view->setCornerWidget(grip);
+ q->connect(grip, SIGNAL(gripMoved(int)), q, SLOT(_q_gripMoved(int)));
+ }
+
+ if (columnSizes.count() > columns.count()) {
+ view->setGeometry(0, 0, columnSizes.at(columns.count()), q->viewport()->height());
+ } else {
+ int initialWidth = view->sizeHint().width();
+ if (q->isRightToLeft())
+ view->setGeometry(q->viewport()->width() - initialWidth, 0, initialWidth, q->viewport()->height());
+ else
+ view->setGeometry(0, 0, initialWidth, q->viewport()->height());
+ columnSizes.resize(qMax(columnSizes.count(), columns.count() + 1));
+ columnSizes[columns.count()] = initialWidth;
+ }
+ if (!columns.isEmpty() && columns.last()->isHidden())
+ columns.last()->setVisible(true);
+
+ columns.append(view);
+ doLayout();
+ updateScrollbars();
+ if (show && view->isHidden())
+ view->setVisible(true);
+ return view;
+}
+
+/*!
+ \fn void QColumnView::updatePreviewWidget(const QModelIndex &index)
+
+ This signal is emitted when the preview widget should be updated to
+ provide rich information about \a index
+
+ \sa previewWidget()
+ */
+
+/*!
+ To use a custom widget for the final column when you select
+ an item overload this function and return a widget.
+ \a index is the root index that will be assigned to the view.
+
+ Return the new view. QColumnView will automatically take ownership of the widget.
+
+ \sa setPreviewWidget()
+ */
+QAbstractItemView *QColumnView::createColumn(const QModelIndex &index)
+{
+ QListView *view = new QListView(viewport());
+
+ initializeColumn(view);
+
+ view->setRootIndex(index);
+ if (model()->canFetchMore(index))
+ model()->fetchMore(index);
+
+ return view;
+}
+
+/*!
+ Copies the behavior and options of the column view and applies them to
+ the \a column such as the iconSize(), textElideMode() and
+ alternatingRowColors(). This can be useful when reimplementing
+ createColumn().
+
+ \since 4.4
+ \sa createColumn()
+ */
+void QColumnView::initializeColumn(QAbstractItemView *column) const
+{
+ Q_D(const QColumnView);
+
+ column->setFrameShape(QFrame::NoFrame);
+ column->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
+ column->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
+ column->setMinimumWidth(100);
+ column->setAttribute(Qt::WA_MacShowFocusRect, false);
+
+#ifndef QT_NO_DRAGANDDROP
+ column->setDragDropMode(dragDropMode());
+ column->setDragDropOverwriteMode(dragDropOverwriteMode());
+ column->setDropIndicatorShown(showDropIndicator());
+#endif
+ column->setAlternatingRowColors(alternatingRowColors());
+ column->setAutoScroll(hasAutoScroll());
+ column->setEditTriggers(editTriggers());
+ column->setHorizontalScrollMode(horizontalScrollMode());
+ column->setIconSize(iconSize());
+ column->setSelectionBehavior(selectionBehavior());
+ column->setSelectionMode(selectionMode());
+ column->setTabKeyNavigation(tabKeyNavigation());
+ column->setTextElideMode(textElideMode());
+ column->setVerticalScrollMode(verticalScrollMode());
+
+ column->setModel(model());
+
+ // Copy the custom delegate per row
+ QMapIterator<int, QPointer<QAbstractItemDelegate> > i(d->rowDelegates);
+ while (i.hasNext()) {
+ i.next();
+ column->setItemDelegateForRow(i.key(), i.value());
+ }
+
+ // set the delegate to be the columnview delegate
+ QAbstractItemDelegate *delegate = column->itemDelegate();
+ column->setItemDelegate(d->itemDelegate);
+ delete delegate;
+}
+
+/*!
+ Returns the preview widget, or 0 if there is none.
+
+ \sa setPreviewWidget(), updatePreviewWidget()
+*/
+QWidget *QColumnView::previewWidget() const
+{
+ Q_D(const QColumnView);
+ return d->previewWidget;
+}
+
+/*!
+ Sets the preview \a widget.
+
+ The \a widget becomes a child of the column view, and will be
+ destroyed when the column area is deleted or when a new widget is
+ set.
+
+ \sa previewWidget(), updatePreviewWidget()
+*/
+void QColumnView::setPreviewWidget(QWidget *widget)
+{
+ Q_D(QColumnView);
+ d->setPreviewWidget(widget);
+}
+
+/*!
+ \internal
+*/
+void QColumnViewPrivate::setPreviewWidget(QWidget *widget)
+{
+ Q_Q(QColumnView);
+ if (previewColumn) {
+ if (!columns.isEmpty() && columns.last() == previewColumn)
+ columns.removeLast();
+ previewColumn->deleteLater();
+ }
+ QColumnViewPreviewColumn *column = new QColumnViewPreviewColumn(q);
+ column->setPreviewWidget(widget);
+ previewColumn = column;
+ previewColumn->hide();
+ previewColumn->setFrameShape(QFrame::NoFrame);
+ previewColumn->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
+ previewColumn->setSelectionMode(QAbstractItemView::NoSelection);
+ previewColumn->setMinimumWidth(qMax(previewColumn->verticalScrollBar()->width(),
+ previewColumn->minimumWidth()));
+ previewWidget = widget;
+ previewWidget->setParent(previewColumn->viewport());
+}
+
+/*!
+ Sets the column widths to the values given in the \a list. Extra values in the list are
+ kept and used when the columns are created.
+
+ If list contains too few values, only width of the rest of the columns will not be modified.
+
+ \sa columnWidths(), createColumn()
+*/
+void QColumnView::setColumnWidths(const QList<int> &list)
+{
+ Q_D(QColumnView);
+ int i = 0;
+ for (; (i < list.count() && i < d->columns.count()); ++i) {
+ d->columns.at(i)->resize(list.at(i), d->columns.at(i)->height());
+ d->columnSizes[i] = list.at(i);
+ }
+ for (; i < list.count(); ++i)
+ d->columnSizes.append(list.at(i));
+}
+
+/*!
+ Returns a list of the width of all the columns in this view.
+
+ \sa setColumnWidths()
+*/
+QList<int> QColumnView::columnWidths() const
+{
+ Q_D(const QColumnView);
+ QList<int> list;
+ for (int i = 0; i < d->columns.count(); ++i)
+ list.append(d->columnSizes.at(i));
+ return list;
+}
+
+/*!
+ \reimp
+*/
+void QColumnView::currentChanged(const QModelIndex &current, const QModelIndex &previous)
+{
+ Q_D(QColumnView);
+ if (!current.isValid()) {
+ QAbstractItemView::currentChanged(current, previous);
+ return;
+ }
+
+ QModelIndex currentParent = current.parent();
+ // optimize for just moving up/down in a list where the child view doesn't change
+ if (currentParent == previous.parent()
+ && model()->hasChildren(current) && model()->hasChildren(previous)) {
+ for (int i = 0; i < d->columns.size(); ++i) {
+ if (currentParent == d->columns.at(i)->rootIndex()) {
+ if (d->columns.size() > i + 1) {
+ QAbstractItemView::currentChanged(current, previous);
+ return;
+ }
+ break;
+ }
+ }
+ }
+
+ // Scrolling to the right we need to have an empty spot
+ bool found = false;
+ if (currentParent == previous) {
+ for (int i = 0; i < d->columns.size(); ++i) {
+ if (currentParent == d->columns.at(i)->rootIndex()) {
+ found = true;
+ if (d->columns.size() < i + 2) {
+ d->createColumn(current, false);
+ }
+ break;
+ }
+ }
+ }
+ if (!found)
+ d->closeColumns(current, true);
+
+ if (!model()->hasChildren(current))
+ emit updatePreviewWidget(current);
+
+ QAbstractItemView::currentChanged(current, previous);
+}
+
+/*
+ We have change the current column and need to update focus and selection models
+ on the new current column.
+*/
+void QColumnViewPrivate::_q_changeCurrentColumn()
+{
+ Q_Q(QColumnView);
+ if (columns.isEmpty())
+ return;
+
+ QModelIndex current = q->currentIndex();
+ if (!current.isValid())
+ return;
+
+ // We might have scrolled far to the left so we need to close all of the children
+ closeColumns(current, true);
+
+ // Set up the "current" column with focus
+ int currentColumn = qMax(0, columns.size() - 2);
+ QAbstractItemView *parentColumn = columns.at(currentColumn);
+ if (q->hasFocus())
+ parentColumn->setFocus(Qt::OtherFocusReason);
+ q->setFocusProxy(parentColumn);
+
+ // find the column that is our current selection model and give it a new one.
+ for (int i = 0; i < columns.size(); ++i) {
+ if (columns.at(i)->selectionModel() == q->selectionModel()) {
+ QItemSelectionModel *replacementSelectionModel =
+ new QItemSelectionModel(parentColumn->model());
+ replacementSelectionModel->setCurrentIndex(
+ q->selectionModel()->currentIndex(), QItemSelectionModel::Current);
+ replacementSelectionModel->select(
+ q->selectionModel()->selection(), QItemSelectionModel::Select);
+ QAbstractItemView *view = columns.at(i);
+ view->setSelectionModel(replacementSelectionModel);
+ view->setFocusPolicy(Qt::NoFocus);
+ if (columns.size() > i + 1)
+ view->setCurrentIndex(columns.at(i+1)->rootIndex());
+ break;
+ }
+ }
+ parentColumn->selectionModel()->deleteLater();
+ parentColumn->setFocusPolicy(Qt::StrongFocus);
+ parentColumn->setSelectionModel(q->selectionModel());
+ // We want the parent selection to stay highlighted (but dimmed depending upon the color theme)
+ if (currentColumn > 0) {
+ parentColumn = columns.at(currentColumn - 1);
+ if (parentColumn->currentIndex() != current.parent())
+ parentColumn->setCurrentIndex(current.parent());
+ }
+
+ if (columns.last()->isHidden()) {
+ columns.last()->setVisible(true);
+ }
+ if (columns.last()->selectionModel())
+ columns.last()->selectionModel()->clear();
+ updateScrollbars();
+}
+
+/*!
+ \reimp
+*/
+void QColumnView::selectAll()
+{
+ if (!model() || !selectionModel())
+ return;
+
+ QModelIndexList indexList = selectionModel()->selectedIndexes();
+ QModelIndex parent = rootIndex();
+ QItemSelection selection;
+ if (indexList.count() >= 1)
+ parent = indexList.at(0).parent();
+ if (indexList.count() == 1) {
+ parent = indexList.at(0);
+ if (!model()->hasChildren(parent))
+ parent = parent.parent();
+ else
+ selection.append(QItemSelectionRange(parent, parent));
+ }
+
+ QModelIndex tl = model()->index(0, 0, parent);
+ QModelIndex br = model()->index(model()->rowCount(parent) - 1,
+ model()->columnCount(parent) - 1,
+ parent);
+ selection.append(QItemSelectionRange(tl, br));
+ selectionModel()->select(selection, QItemSelectionModel::ClearAndSelect);
+}
+
+/*
+ * private object implementation
+ */
+QColumnViewPrivate::QColumnViewPrivate()
+: QAbstractItemViewPrivate()
+,showResizeGrips(true)
+,offset(0)
+,currentAnimation(ANIMATION_DURATION_MSEC)
+,previewWidget(0)
+,previewColumn(0)
+{
+}
+
+QColumnViewPrivate::~QColumnViewPrivate()
+{
+}
+
+/*!
+ \internal
+ Place all of the columns where they belong inside of the viewport, resize as necessary.
+*/
+void QColumnViewPrivate::doLayout()
+{
+ Q_Q(QColumnView);
+ if (!model || columns.isEmpty())
+ return;
+
+ int viewportHeight = q->viewport()->height();
+ int x = columns.at(0)->x();
+
+ if (q->isRightToLeft()) {
+ x = q->viewport()->width() + q->horizontalOffset();
+ for (int i = 0; i < columns.size(); ++i) {
+ QAbstractItemView *view = columns.at(i);
+ x -= view->width();
+ if (x != view->x() || viewportHeight != view->height())
+ view->setGeometry(x, 0, view->width(), viewportHeight);
+ }
+ } else {
+ for (int i = 0; i < columns.size(); ++i) {
+ QAbstractItemView *view = columns.at(i);
+ int currentColumnWidth = view->width();
+ if (x != view->x() || viewportHeight != view->height())
+ view->setGeometry(x, 0, currentColumnWidth, viewportHeight);
+ x += currentColumnWidth;
+ }
+ }
+}
+
+/*!
+ \internal
+
+ Draws a delegate with a > if an object has children.
+
+ \sa {Model/View Programming}, QItemDelegate
+*/
+void QColumnViewDelegate::paint(QPainter *painter,
+ const QStyleOptionViewItem &option,
+ const QModelIndex &index) const
+{
+ drawBackground(painter, option, index );
+
+ bool reverse = (option.direction == Qt::RightToLeft);
+ int width = ((option.rect.height() * 2) / 3);
+ // Modify the options to give us room to add an arrow
+ QStyleOptionViewItemV4 opt = option;
+ if (reverse)
+ opt.rect.adjust(width,0,0,0);
+ else
+ opt.rect.adjust(0,0,-width,0);
+
+ if (!(index.model()->flags(index) & Qt::ItemIsEnabled)) {
+ opt.showDecorationSelected = true;
+ opt.state |= QStyle::State_Selected;
+ }
+
+ QItemDelegate::paint(painter, opt, index);
+
+ if (reverse)
+ opt.rect = QRect(option.rect.x(), option.rect.y(), width, option.rect.height());
+ else
+ opt.rect = QRect(option.rect.x() + option.rect.width() - width, option.rect.y(),
+ width, option.rect.height());
+
+ // Draw >
+ if (index.model()->hasChildren(index)) {
+ const QWidget *view = opt.widget;
+ QStyle *style = view ? view->style() : qApp->style();
+ style->drawPrimitive(QStyle::PE_IndicatorColumnViewArrow, &opt, painter, view);
+ }
+}
+
+QT_END_NAMESPACE
+
+#include "moc_qcolumnview.cpp"
+
+#endif // QT_NO_COLUMNVIEW
diff --git a/src/gui/itemviews/qcolumnview.h b/src/gui/itemviews/qcolumnview.h
new file mode 100644
index 0000000000..ca9134cf77
--- /dev/null
+++ b/src/gui/itemviews/qcolumnview.h
@@ -0,0 +1,125 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QCOLUMNVIEW_H
+#define QCOLUMNVIEW_H
+
+#include <QtGui/qabstractitemview.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+#ifndef QT_NO_COLUMNVIEW
+
+class QColumnViewPrivate;
+
+class Q_GUI_EXPORT QColumnView : public QAbstractItemView {
+
+Q_OBJECT
+ Q_PROPERTY(bool resizeGripsVisible READ resizeGripsVisible WRITE setResizeGripsVisible)
+
+Q_SIGNALS:
+ void updatePreviewWidget(const QModelIndex &index);
+
+public:
+ explicit QColumnView(QWidget *parent = 0);
+ ~QColumnView();
+
+ // QAbstractItemView overloads
+ QModelIndex indexAt(const QPoint &point) const;
+ void scrollTo(const QModelIndex &index, ScrollHint hint = EnsureVisible);
+ QSize sizeHint() const;
+ QRect visualRect(const QModelIndex &index) const;
+ void setModel(QAbstractItemModel *model);
+ void setSelectionModel(QItemSelectionModel * selectionModel);
+ void setRootIndex(const QModelIndex &index);
+ void selectAll();
+
+ // QColumnView functions
+ void setResizeGripsVisible(bool visible);
+ bool resizeGripsVisible() const;
+
+ QWidget *previewWidget() const;
+ void setPreviewWidget(QWidget *widget);
+
+ void setColumnWidths(const QList<int> &list);
+ QList<int> columnWidths() const;
+
+protected:
+ QColumnView(QColumnViewPrivate &dd, QWidget *parent = 0);
+
+ // QAbstractItemView overloads
+ bool isIndexHidden(const QModelIndex &index) const;
+ QModelIndex moveCursor(CursorAction cursorAction, Qt::KeyboardModifiers modifiers);
+ void resizeEvent(QResizeEvent *event);
+ void setSelection(const QRect & rect, QItemSelectionModel::SelectionFlags command);
+ QRegion visualRegionForSelection(const QItemSelection &selection) const;
+ int horizontalOffset() const;
+ int verticalOffset() const;
+ void scrollContentsBy(int dx, int dy);
+
+ // QColumnView functions
+ virtual QAbstractItemView* createColumn(const QModelIndex &rootIndex);
+ void initializeColumn(QAbstractItemView *column) const;
+
+protected Q_SLOTS:
+ // QAbstractItemView overloads
+ void currentChanged(const QModelIndex &current, const QModelIndex &previous);
+
+private:
+ Q_DECLARE_PRIVATE(QColumnView)
+ Q_DISABLE_COPY(QColumnView)
+ Q_PRIVATE_SLOT(d_func(), void _q_gripMoved(int))
+ Q_PRIVATE_SLOT(d_func(), void _q_changeCurrentColumn())
+ Q_PRIVATE_SLOT(d_func(), void _q_clicked(const QModelIndex &))
+};
+
+#endif // QT_NO_COLUMNVIEW
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QCOLUMNVIEW_H
+
diff --git a/src/gui/itemviews/qcolumnview_p.h b/src/gui/itemviews/qcolumnview_p.h
new file mode 100644
index 0000000000..8f75becda9
--- /dev/null
+++ b/src/gui/itemviews/qcolumnview_p.h
@@ -0,0 +1,184 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QCOLUMNVIEW_P_H
+#define QCOLUMNVIEW_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 "qcolumnview.h"
+
+#ifndef QT_NO_QCOlUMNVIEW
+
+#include <private/qabstractitemview_p.h>
+
+#include <QtCore/qabstractitemmodel.h>
+#include <QtCore/qtimeline.h>
+#include <QtGui/qabstractitemdelegate.h>
+#include <QtGui/qabstractitemview.h>
+#include <QtGui/qitemdelegate.h>
+#include <qlistview.h>
+#include <qevent.h>
+#include <qscrollbar.h>
+
+QT_BEGIN_NAMESPACE
+
+class QColumnViewPreviewColumn : public QAbstractItemView {
+
+public:
+ QColumnViewPreviewColumn(QWidget *parent) : QAbstractItemView(parent), previewWidget(0) {
+ }
+
+ void setPreviewWidget(QWidget *widget) {
+ previewWidget = widget;
+ setMinimumWidth(previewWidget->minimumWidth());
+ }
+
+ void resizeEvent(QResizeEvent * event){
+ if (!previewWidget)
+ return;
+ previewWidget->resize(
+ qMax(previewWidget->minimumWidth(), event->size().width()),
+ previewWidget->height());
+ QSize p = viewport()->size();
+ QSize v = previewWidget->size();
+ horizontalScrollBar()->setRange(0, v.width() - p.width());
+ horizontalScrollBar()->setPageStep(p.width());
+ verticalScrollBar()->setRange(0, v.height() - p.height());
+ verticalScrollBar()->setPageStep(p.height());
+
+ QAbstractScrollArea::resizeEvent(event);
+ }
+
+ QRect visualRect(const QModelIndex &) const
+ {
+ return QRect();
+ }
+ void scrollTo(const QModelIndex &, ScrollHint)
+ {
+ }
+ QModelIndex indexAt(const QPoint &) const
+ {
+ return QModelIndex();
+ }
+ QModelIndex moveCursor(CursorAction, Qt::KeyboardModifiers)
+ {
+ return QModelIndex();
+ }
+ int horizontalOffset () const {
+ return 0;
+ }
+ int verticalOffset () const {
+ return 0;
+ }
+ QRegion visualRegionForSelection(const QItemSelection &) const
+ {
+ return QRegion();
+ }
+ bool isIndexHidden(const QModelIndex &) const
+ {
+ return false;
+ }
+ void setSelection(const QRect &, QItemSelectionModel::SelectionFlags)
+ {
+ }
+private:
+ QWidget *previewWidget;
+};
+
+class Q_AUTOTEST_EXPORT QColumnViewPrivate : public QAbstractItemViewPrivate
+{
+ Q_DECLARE_PUBLIC(QColumnView)
+
+public:
+ QColumnViewPrivate();
+ ~QColumnViewPrivate();
+ void initialize();
+
+ QAbstractItemView *createColumn(const QModelIndex &index, bool show);
+
+ void updateScrollbars();
+ void closeColumns(const QModelIndex &parent = QModelIndex(), bool build = false);
+ void doLayout();
+ void setPreviewWidget(QWidget *widget);
+
+ void _q_gripMoved(int offset);
+ void _q_changeCurrentColumn();
+ void _q_clicked(const QModelIndex &index);
+
+ QList<QAbstractItemView*> columns;
+ QVector<int> columnSizes; // used during init and corner moving
+ bool showResizeGrips;
+ int offset;
+ QTimeLine currentAnimation;
+ QWidget *previewWidget;
+ QAbstractItemView *previewColumn;
+};
+
+/*!
+ * This is a delegate that will paint the triangle
+ */
+class QColumnViewDelegate : public QItemDelegate
+{
+
+public:
+ explicit QColumnViewDelegate(QObject *parent = 0) : QItemDelegate(parent) {};
+ ~QColumnViewDelegate() {};
+
+ void paint(QPainter *painter,
+ const QStyleOptionViewItem &option,
+ const QModelIndex &index) const;
+};
+#endif // QT_NO_QCOLUMNVIEW
+
+
+QT_END_NAMESPACE
+#endif //QCOLUMNVIEW_P_H
+
diff --git a/src/gui/itemviews/qcolumnviewgrip.cpp b/src/gui/itemviews/qcolumnviewgrip.cpp
new file mode 100644
index 0000000000..5da70f694b
--- /dev/null
+++ b/src/gui/itemviews/qcolumnviewgrip.cpp
@@ -0,0 +1,194 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QT_NO_QCOLUMNVIEW
+
+#include "qcolumnviewgrip_p.h"
+#include <qstyleoption.h>
+#include <qpainter.h>
+#include <qbrush.h>
+#include <qevent.h>
+#include <qdebug.h>
+
+QT_BEGIN_NAMESPACE
+
+/*
+ \internal
+ class QColumnViewGrip
+
+ QColumnViewGrip is created to go inside QAbstractScrollArea's corner.
+ When the mouse it moved it will resize the scroll area and emit's a signal.
+ */
+
+/*
+ \internal
+ \fn void QColumnViewGrip::gripMoved()
+ Signal that is emitted when the grip moves the parent widget.
+ */
+
+/*!
+ Creates a new QColumnViewGrip with the given \a parent to view a model.
+ Use setModel() to set the model.
+*/
+QColumnViewGrip::QColumnViewGrip(QWidget *parent)
+: QWidget(*new QColumnViewGripPrivate, parent, 0)
+{
+#ifndef QT_NO_CURSOR
+ setCursor(Qt::SplitHCursor);
+#endif
+}
+
+/*!
+ \internal
+*/
+QColumnViewGrip::QColumnViewGrip(QColumnViewGripPrivate & dd, QWidget *parent, Qt::WFlags f)
+: QWidget(dd, parent, f)
+{
+}
+
+/*!
+ Destroys the view.
+*/
+QColumnViewGrip::~QColumnViewGrip()
+{
+}
+
+/*!
+ Attempt to resize the parent object by \a offset
+ returns the amount of offset that it was actually able to resized
+*/
+int QColumnViewGrip::moveGrip(int offset)
+{
+ QWidget *parentWidget = (QWidget*)parent();
+
+ // first resize the parent
+ int oldWidth = parentWidget->width();
+ int newWidth = oldWidth;
+ if (isRightToLeft())
+ newWidth -= offset;
+ else
+ newWidth += offset;
+ newWidth = qMax(parentWidget->minimumWidth(), newWidth);
+ parentWidget->resize(newWidth, parentWidget->height());
+
+ // Then have the view move the widget
+ int realOffset = parentWidget->width() - oldWidth;
+ int oldX = parentWidget->x();
+ if (realOffset != 0)
+ emit gripMoved(realOffset);
+ if (isRightToLeft())
+ realOffset = -1 * (oldX - parentWidget->x());
+ return realOffset;
+}
+
+/*!
+ \reimp
+*/
+void QColumnViewGrip::paintEvent(QPaintEvent *event)
+{
+ QPainter painter(this);
+ QStyleOption opt;
+ opt.initFrom(this);
+ style()->drawControl(QStyle::CE_ColumnViewGrip, &opt, &painter, this);
+ event->accept();
+}
+
+/*!
+ \reimp
+ Resize the parent window to the sizeHint
+*/
+void QColumnViewGrip::mouseDoubleClickEvent(QMouseEvent *event)
+{
+ Q_UNUSED(event);
+ QWidget *parentWidget = (QWidget*)parent();
+ int offset = parentWidget->sizeHint().width() - parentWidget->width();
+ if (isRightToLeft())
+ offset *= -1;
+ moveGrip(offset);
+ event->accept();
+}
+
+/*!
+ \reimp
+ Begin watching for mouse movements
+*/
+void QColumnViewGrip::mousePressEvent(QMouseEvent *event)
+{
+ Q_D(QColumnViewGrip);
+ d->originalXLocation = event->globalX();
+ event->accept();
+}
+
+/*!
+ \reimp
+ Calculate the movement of the grip and moveGrip() and emit gripMoved
+*/
+void QColumnViewGrip::mouseMoveEvent(QMouseEvent *event)
+{
+ Q_D(QColumnViewGrip);
+ int offset = event->globalX() - d->originalXLocation;
+ d->originalXLocation = moveGrip(offset) + d->originalXLocation;
+ event->accept();
+}
+
+/*!
+ \reimp
+ Stop watching for mouse movements
+*/
+void QColumnViewGrip::mouseReleaseEvent(QMouseEvent *event)
+{
+ Q_D(QColumnViewGrip);
+ d->originalXLocation = -1;
+ event->accept();
+}
+
+/*
+ * private object implementation
+ */
+QColumnViewGripPrivate::QColumnViewGripPrivate()
+: QWidgetPrivate(),
+originalXLocation(-1)
+{
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_QCOLUMNVIEW
diff --git a/src/gui/itemviews/qcolumnviewgrip_p.h b/src/gui/itemviews/qcolumnviewgrip_p.h
new file mode 100644
index 0000000000..12515b072f
--- /dev/null
+++ b/src/gui/itemviews/qcolumnviewgrip_p.h
@@ -0,0 +1,104 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QCOLUMNVIEWGRIP_P_H
+#define QCOLUMNVIEWGRIP_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 <private/qwidget_p.h>
+
+#ifndef QT_NO_QCOLUMNVIEW
+
+QT_BEGIN_NAMESPACE
+
+class QColumnViewGripPrivate;
+
+class Q_AUTOTEST_EXPORT QColumnViewGrip : public QWidget {
+
+Q_OBJECT
+
+Q_SIGNALS:
+ void gripMoved(int offset);
+
+public:
+ explicit QColumnViewGrip(QWidget *parent = 0);
+ ~QColumnViewGrip();
+ int moveGrip(int offset);
+
+protected:
+ QColumnViewGrip(QColumnViewGripPrivate &, QWidget *parent = 0, Qt::WFlags f = 0);
+ void paintEvent(QPaintEvent *event);
+ void mouseDoubleClickEvent(QMouseEvent *event);
+ void mouseMoveEvent(QMouseEvent *event);
+ void mouseReleaseEvent(QMouseEvent *event);
+ void mousePressEvent(QMouseEvent *event);
+
+private:
+ Q_DECLARE_PRIVATE(QColumnViewGrip)
+ Q_DISABLE_COPY(QColumnViewGrip)
+};
+
+class QColumnViewGripPrivate : public QWidgetPrivate
+{
+ Q_DECLARE_PUBLIC(QColumnViewGrip)
+
+public:
+ QColumnViewGripPrivate();
+ ~QColumnViewGripPrivate() {}
+
+ int originalXLocation;
+};
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_QCOLUMNVIEW
+
+#endif //QCOLUMNVIEWGRIP_P_H
diff --git a/src/gui/itemviews/qdatawidgetmapper.cpp b/src/gui/itemviews/qdatawidgetmapper.cpp
new file mode 100644
index 0000000000..909fba17cc
--- /dev/null
+++ b/src/gui/itemviews/qdatawidgetmapper.cpp
@@ -0,0 +1,849 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qdatawidgetmapper.h"
+
+#ifndef QT_NO_DATAWIDGETMAPPER
+
+#include "qabstractitemmodel.h"
+#include "qitemdelegate.h"
+#include "qmetaobject.h"
+#include "qwidget.h"
+#include "private/qobject_p.h"
+#include "private/qabstractitemmodel_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class QDataWidgetMapperPrivate: public QObjectPrivate
+{
+public:
+ Q_DECLARE_PUBLIC(QDataWidgetMapper)
+
+ QDataWidgetMapperPrivate()
+ : model(QAbstractItemModelPrivate::staticEmptyModel()), delegate(0),
+ orientation(Qt::Horizontal), submitPolicy(QDataWidgetMapper::AutoSubmit)
+ {
+ }
+
+ QAbstractItemModel *model;
+ QAbstractItemDelegate *delegate;
+ Qt::Orientation orientation;
+ QDataWidgetMapper::SubmitPolicy submitPolicy;
+ QPersistentModelIndex rootIndex;
+ QPersistentModelIndex currentTopLeft;
+
+ inline int itemCount()
+ {
+ return orientation == Qt::Horizontal
+ ? model->rowCount(rootIndex)
+ : model->columnCount(rootIndex);
+ }
+
+ inline int currentIdx() const
+ {
+ return orientation == Qt::Horizontal ? currentTopLeft.row() : currentTopLeft.column();
+ }
+
+ inline QModelIndex indexAt(int itemPos)
+ {
+ return orientation == Qt::Horizontal
+ ? model->index(currentIdx(), itemPos, rootIndex)
+ : model->index(itemPos, currentIdx(), rootIndex);
+ }
+
+ inline void flipEventFilters(QAbstractItemDelegate *oldDelegate,
+ QAbstractItemDelegate *newDelegate)
+ {
+ for (int i = 0; i < widgetMap.count(); ++i) {
+ QWidget *w = widgetMap.at(i).widget;
+ if (!w)
+ continue;
+ w->removeEventFilter(oldDelegate);
+ w->installEventFilter(newDelegate);
+ }
+ }
+
+ void populate();
+
+ // private slots
+ void _q_dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight);
+ void _q_commitData(QWidget *);
+ void _q_closeEditor(QWidget *, QAbstractItemDelegate::EndEditHint);
+ void _q_modelDestroyed();
+
+ struct WidgetMapper
+ {
+ inline WidgetMapper(QWidget *w = 0, int c = 0, const QModelIndex &i = QModelIndex())
+ : widget(w), section(c), currentIndex(i) {}
+ inline WidgetMapper(QWidget *w, int c, const QModelIndex &i, const QByteArray &p)
+ : widget(w), section(c), currentIndex(i), property(p) {}
+
+ QPointer<QWidget> widget;
+ int section;
+ QPersistentModelIndex currentIndex;
+ QByteArray property;
+ };
+
+ void populate(WidgetMapper &m);
+ int findWidget(QWidget *w) const;
+
+ bool commit(const WidgetMapper &m);
+
+ QList<WidgetMapper> widgetMap;
+};
+
+int QDataWidgetMapperPrivate::findWidget(QWidget *w) const
+{
+ for (int i = 0; i < widgetMap.count(); ++i) {
+ if (widgetMap.at(i).widget == w)
+ return i;
+ }
+ return -1;
+}
+
+bool QDataWidgetMapperPrivate::commit(const WidgetMapper &m)
+{
+ if (m.widget.isNull())
+ return true; // just ignore
+
+ if (!m.currentIndex.isValid())
+ return false;
+
+ // Create copy to avoid passing the widget mappers data
+ QModelIndex idx = m.currentIndex;
+ if (m.property.isEmpty())
+ delegate->setModelData(m.widget, model, idx);
+ else
+ model->setData(idx, m.widget->property(m.property), Qt::EditRole);
+
+ return true;
+}
+
+void QDataWidgetMapperPrivate::populate(WidgetMapper &m)
+{
+ if (m.widget.isNull())
+ return;
+
+ m.currentIndex = indexAt(m.section);
+ if (m.property.isEmpty())
+ delegate->setEditorData(m.widget, m.currentIndex);
+ else
+ m.widget->setProperty(m.property, m.currentIndex.data(Qt::EditRole));
+}
+
+void QDataWidgetMapperPrivate::populate()
+{
+ for (int i = 0; i < widgetMap.count(); ++i)
+ populate(widgetMap[i]);
+}
+
+static bool qContainsIndex(const QModelIndex &idx, const QModelIndex &topLeft,
+ const QModelIndex &bottomRight)
+{
+ return idx.row() >= topLeft.row() && idx.row() <= bottomRight.row()
+ && idx.column() >= topLeft.column() && idx.column() <= bottomRight.column();
+}
+
+void QDataWidgetMapperPrivate::_q_dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight)
+{
+ if (topLeft.parent() != rootIndex)
+ return; // not in our hierarchy
+
+ for (int i = 0; i < widgetMap.count(); ++i) {
+ WidgetMapper &m = widgetMap[i];
+ if (qContainsIndex(m.currentIndex, topLeft, bottomRight))
+ populate(m);
+ }
+}
+
+void QDataWidgetMapperPrivate::_q_commitData(QWidget *w)
+{
+ if (submitPolicy == QDataWidgetMapper::ManualSubmit)
+ return;
+
+ int idx = findWidget(w);
+ if (idx == -1)
+ return; // not our widget
+
+ commit(widgetMap.at(idx));
+}
+
+class QFocusHelper: public QWidget
+{
+public:
+ bool focusNextPrevChild(bool next)
+ {
+ return QWidget::focusNextPrevChild(next);
+ }
+
+ static inline void focusNextPrevChild(QWidget *w, bool next)
+ {
+ static_cast<QFocusHelper *>(w)->focusNextPrevChild(next);
+ }
+};
+
+void QDataWidgetMapperPrivate::_q_closeEditor(QWidget *w, QAbstractItemDelegate::EndEditHint hint)
+{
+ int idx = findWidget(w);
+ if (idx == -1)
+ return; // not our widget
+
+ switch (hint) {
+ case QAbstractItemDelegate::RevertModelCache: {
+ populate(widgetMap[idx]);
+ break; }
+ case QAbstractItemDelegate::EditNextItem:
+ QFocusHelper::focusNextPrevChild(w, true);
+ break;
+ case QAbstractItemDelegate::EditPreviousItem:
+ QFocusHelper::focusNextPrevChild(w, false);
+ break;
+ case QAbstractItemDelegate::SubmitModelCache:
+ case QAbstractItemDelegate::NoHint:
+ // nothing
+ break;
+ }
+}
+
+void QDataWidgetMapperPrivate::_q_modelDestroyed()
+{
+ Q_Q(QDataWidgetMapper);
+
+ model = 0;
+ q->setModel(QAbstractItemModelPrivate::staticEmptyModel());
+}
+
+/*!
+ \class QDataWidgetMapper
+ \brief The QDataWidgetMapper class provides mapping between a section
+ of a data model to widgets.
+ \since 4.2
+ \ingroup model-view
+ \ingroup advanced
+
+ QDataWidgetMapper can be used to create data-aware widgets by mapping
+ them to sections of an item model. A section is a column of a model
+ if the orientation is horizontal (the default), otherwise a row.
+
+ Every time the current index changes, each widget is updated with data
+ from the model via the property specified when its mapping was made.
+ If the user edits the contents of a widget, the changes are read using
+ the same property and written back to the model.
+ By default, each widget's \l{Q_PROPERTY()}{user property} is used to
+ transfer data between the model and the widget. Since Qt 4.3, an
+ additional addMapping() function enables a named property to be used
+ instead of the default user property.
+
+ It is possible to set an item delegate to support custom widgets. By default,
+ a QItemDelegate is used to synchronize the model with the widgets.
+
+ Let us assume that we have an item model named \c{model} with the following contents:
+
+ \table
+ \row \o 1 \o Nokia Corporation and/or its subsidiary(-ies) \o Oslo
+ \row \o 2 \o Trolltech Pty \o Brisbane
+ \row \o 3 \o Trolltech Inc \o Palo Alto
+ \row \o 4 \o Trolltech China \o Beijing
+ \row \o 5 \o Trolltech GmbH \o Berlin
+ \endtable
+
+ The following code will map the columns of the model to widgets called \c mySpinBox,
+ \c myLineEdit and \c{myCountryChooser}:
+
+ \snippet doc/src/snippets/code/src_gui_itemviews_qdatawidgetmapper.cpp 0
+
+ After the call to toFirst(), \c mySpinBox displays the value \c{1}, \c myLineEdit
+ displays \c {Nokia Corporation and/or its subsidiary(-ies)} and \c myCountryChooser displays \c{Oslo}. The
+ navigational functions toFirst(), toNext(), toPrevious(), toLast() and setCurrentIndex()
+ can be used to navigate in the model and update the widgets with contents from
+ the model.
+
+ The setRootIndex() function enables a particular item in a model to be
+ specified as the root index - children of this item will be mapped to
+ the relevant widgets in the user interface.
+
+ QDataWidgetMapper supports two submit policies, \c AutoSubmit and \c{ManualSubmit}.
+ \c AutoSubmit will update the model as soon as the current widget loses focus,
+ \c ManualSubmit will not update the model unless submit() is called. \c ManualSubmit
+ is useful when displaying a dialog that lets the user cancel all modifications.
+ Also, other views that display the model won't update until the user finishes
+ all their modifications and submits.
+
+ Note that QDataWidgetMapper keeps track of external modifications. If the contents
+ of the model are updated in another module of the application, the widgets are
+ updated as well.
+
+ \sa QAbstractItemModel, QAbstractItemDelegate
+ */
+
+/*! \enum QDataWidgetMapper::SubmitPolicy
+
+ This enum describes the possible submit policies a QDataWidgetMapper
+ supports.
+
+ \value AutoSubmit Whenever a widget loses focus, the widget's current
+ value is set to the item model.
+ \value ManualSubmit The model is not updated until submit() is called.
+ */
+
+/*!
+ \fn void QDataWidgetMapper::currentIndexChanged(int index)
+
+ This signal is emitted after the current index has changed and
+ all widgets were populated with new data. \a index is the new
+ current index.
+
+ \sa currentIndex(), setCurrentIndex()
+ */
+
+/*!
+ Constructs a new QDataWidgetMapper with parent object \a parent.
+ By default, the orientation is horizontal and the submit policy
+ is \c{AutoSubmit}.
+
+ \sa setOrientation(), setSubmitPolicy()
+ */
+QDataWidgetMapper::QDataWidgetMapper(QObject *parent)
+ : QObject(*new QDataWidgetMapperPrivate, parent)
+{
+ setItemDelegate(new QItemDelegate(this));
+}
+
+/*!
+ Destroys the object.
+ */
+QDataWidgetMapper::~QDataWidgetMapper()
+{
+}
+
+/*!
+ Sets the current model to \a model. If another model was set,
+ all mappings to that old model are cleared.
+
+ \sa model()
+ */
+void QDataWidgetMapper::setModel(QAbstractItemModel *model)
+{
+ Q_D(QDataWidgetMapper);
+
+ if (d->model == model)
+ return;
+
+ if (d->model) {
+ disconnect(d->model, SIGNAL(dataChanged(QModelIndex,QModelIndex)), this,
+ SLOT(_q_dataChanged(QModelIndex,QModelIndex)));
+ disconnect(d->model, SIGNAL(destroyed()), this,
+ SLOT(_q_modelDestroyed()));
+ }
+ clearMapping();
+ d->rootIndex = QModelIndex();
+ d->currentTopLeft = QModelIndex();
+
+ d->model = model;
+
+ connect(model, SIGNAL(dataChanged(QModelIndex,QModelIndex)),
+ SLOT(_q_dataChanged(QModelIndex,QModelIndex)));
+ connect(model, SIGNAL(destroyed()), SLOT(_q_modelDestroyed()));
+}
+
+/*!
+ Returns the current model.
+
+ \sa setModel()
+ */
+QAbstractItemModel *QDataWidgetMapper::model() const
+{
+ Q_D(const QDataWidgetMapper);
+ return d->model == QAbstractItemModelPrivate::staticEmptyModel()
+ ? static_cast<QAbstractItemModel *>(0)
+ : d->model;
+}
+
+/*!
+ Sets the item delegate to \a delegate. The delegate will be used to write
+ data from the model into the widget and from the widget to the model,
+ using QAbstractItemDelegate::setEditorData() and QAbstractItemDelegate::setModelData().
+
+ The delegate also decides when to apply data and when to change the editor,
+ using QAbstractItemDelegate::commitData() and QAbstractItemDelegate::closeEditor().
+
+ \warning You should not share the same instance of a delegate between widget mappers
+ or views. Doing so can cause incorrect or unintuitive editing behavior since each
+ view connected to a given delegate may receive the \l{QAbstractItemDelegate::}{closeEditor()}
+ signal, and attempt to access, modify or close an editor that has already been closed.
+ */
+void QDataWidgetMapper::setItemDelegate(QAbstractItemDelegate *delegate)
+{
+ Q_D(QDataWidgetMapper);
+ QAbstractItemDelegate *oldDelegate = d->delegate;
+ if (oldDelegate) {
+ disconnect(oldDelegate, SIGNAL(commitData(QWidget*)), this, SLOT(_q_commitData(QWidget*)));
+ disconnect(oldDelegate, SIGNAL(closeEditor(QWidget*,QAbstractItemDelegate::EndEditHint)),
+ this, SLOT(_q_closeEditor(QWidget*,QAbstractItemDelegate::EndEditHint)));
+ }
+
+ d->delegate = delegate;
+
+ if (delegate) {
+ connect(delegate, SIGNAL(commitData(QWidget*)), SLOT(_q_commitData(QWidget*)));
+ connect(delegate, SIGNAL(closeEditor(QWidget*,QAbstractItemDelegate::EndEditHint)),
+ SLOT(_q_closeEditor(QWidget*,QAbstractItemDelegate::EndEditHint)));
+ }
+
+ d->flipEventFilters(oldDelegate, delegate);
+}
+
+/*!
+ Returns the current item delegate.
+ */
+QAbstractItemDelegate *QDataWidgetMapper::itemDelegate() const
+{
+ Q_D(const QDataWidgetMapper);
+ return d->delegate;
+}
+
+/*!
+ Sets the root item to \a index. This can be used to display
+ a branch of a tree. Pass an invalid model index to display
+ the top-most branch.
+
+ \sa rootIndex()
+ */
+void QDataWidgetMapper::setRootIndex(const QModelIndex &index)
+{
+ Q_D(QDataWidgetMapper);
+ d->rootIndex = index;
+}
+
+/*!
+ Returns the current root index.
+
+ \sa setRootIndex()
+*/
+QModelIndex QDataWidgetMapper::rootIndex() const
+{
+ Q_D(const QDataWidgetMapper);
+ return QModelIndex(d->rootIndex);
+}
+
+/*!
+ Adds a mapping between a \a widget and a \a section from the model.
+ The \a section is a column in the model if the orientation is
+ horizontal (the default), otherwise a row.
+
+ For the following example, we assume a model \c myModel that
+ has two columns: the first one contains the names of people in a
+ group, and the second column contains their ages. The first column
+ is mapped to the QLineEdit \c nameLineEdit, and the second is
+ mapped to the QSpinBox \c{ageSpinBox}:
+
+ \snippet doc/src/snippets/code/src_gui_itemviews_qdatawidgetmapper.cpp 1
+
+ \bold{Notes:}
+ \list
+ \o If the \a widget is already mapped to a section, the
+ old mapping will be replaced by the new one.
+ \o Only one-to-one mappings between sections and widgets are allowed.
+ It is not possible to map a single section to multiple widgets, or to
+ map a single widget to multiple sections.
+ \endlist
+
+ \sa removeMapping(), mappedSection(), clearMapping()
+ */
+void QDataWidgetMapper::addMapping(QWidget *widget, int section)
+{
+ Q_D(QDataWidgetMapper);
+
+ removeMapping(widget);
+ d->widgetMap.append(QDataWidgetMapperPrivate::WidgetMapper(widget, section, d->indexAt(section)));
+ widget->installEventFilter(d->delegate);
+}
+
+/*!
+ \since 4.3
+
+ Essentially the same as addMapping(), but adds the possibility to specify
+ the property to use specifying \a propertyName.
+
+ \sa addMapping()
+*/
+
+void QDataWidgetMapper::addMapping(QWidget *widget, int section, const QByteArray &propertyName)
+{
+ Q_D(QDataWidgetMapper);
+
+ removeMapping(widget);
+ d->widgetMap.append(QDataWidgetMapperPrivate::WidgetMapper(widget, section, d->indexAt(section), propertyName));
+ widget->installEventFilter(d->delegate);
+}
+
+/*!
+ Removes the mapping for the given \a widget.
+
+ \sa addMapping(), clearMapping()
+ */
+void QDataWidgetMapper::removeMapping(QWidget *widget)
+{
+ Q_D(QDataWidgetMapper);
+
+ int idx = d->findWidget(widget);
+ if (idx == -1)
+ return;
+
+ d->widgetMap.removeAt(idx);
+ widget->removeEventFilter(d->delegate);
+}
+
+/*!
+ Returns the section the \a widget is mapped to or -1
+ if the widget is not mapped.
+
+ \sa addMapping(), removeMapping()
+ */
+int QDataWidgetMapper::mappedSection(QWidget *widget) const
+{
+ Q_D(const QDataWidgetMapper);
+
+ int idx = d->findWidget(widget);
+ if (idx == -1)
+ return -1;
+
+ return d->widgetMap.at(idx).section;
+}
+
+/*!
+ \since 4.3
+ Returns the name of the property that is used when mapping
+ data to the given \a widget.
+
+ \sa mappedSection(), addMapping(), removeMapping()
+*/
+
+QByteArray QDataWidgetMapper::mappedPropertyName(QWidget *widget) const
+{
+ Q_D(const QDataWidgetMapper);
+
+ int idx = d->findWidget(widget);
+ if (idx == -1)
+ return QByteArray();
+ const QDataWidgetMapperPrivate::WidgetMapper &m = d->widgetMap.at(idx);
+ if (m.property.isEmpty())
+ return m.widget->metaObject()->userProperty().name();
+ else
+ return m.property;
+}
+
+/*!
+ Returns the widget that is mapped at \a section, or
+ 0 if no widget is mapped at that section.
+
+ \sa addMapping(), removeMapping()
+ */
+QWidget *QDataWidgetMapper::mappedWidgetAt(int section) const
+{
+ Q_D(const QDataWidgetMapper);
+
+ for (int i = 0; i < d->widgetMap.count(); ++i) {
+ if (d->widgetMap.at(i).section == section)
+ return d->widgetMap.at(i).widget;
+ }
+
+ return 0;
+}
+
+/*!
+ Repopulates all widgets with the current data of the model.
+ All unsubmitted changes will be lost.
+
+ \sa submit(), setSubmitPolicy()
+ */
+void QDataWidgetMapper::revert()
+{
+ Q_D(QDataWidgetMapper);
+
+ d->populate();
+}
+
+/*!
+ Submits all changes from the mapped widgets to the model.
+
+ For every mapped section, the item delegate reads the current
+ value from the widget and sets it in the model. Finally, the
+ model's \l {QAbstractItemModel::}{submit()} method is invoked.
+
+ Returns true if all the values were submitted, otherwise false.
+
+ Note: For database models, QSqlQueryModel::lastError() can be
+ used to retrieve the last error.
+
+ \sa revert(), setSubmitPolicy()
+ */
+bool QDataWidgetMapper::submit()
+{
+ Q_D(QDataWidgetMapper);
+
+ for (int i = 0; i < d->widgetMap.count(); ++i) {
+ const QDataWidgetMapperPrivate::WidgetMapper &m = d->widgetMap.at(i);
+ if (!d->commit(m))
+ return false;
+ }
+
+ return d->model->submit();
+}
+
+/*!
+ Populates the widgets with data from the first row of the model
+ if the orientation is horizontal (the default), otherwise
+ with data from the first column.
+
+ This is equivalent to calling \c setCurrentIndex(0).
+
+ \sa toLast(), setCurrentIndex()
+ */
+void QDataWidgetMapper::toFirst()
+{
+ setCurrentIndex(0);
+}
+
+/*!
+ Populates the widgets with data from the last row of the model
+ if the orientation is horizontal (the default), otherwise
+ with data from the last column.
+
+ Calls setCurrentIndex() internally.
+
+ \sa toFirst(), setCurrentIndex()
+ */
+void QDataWidgetMapper::toLast()
+{
+ Q_D(QDataWidgetMapper);
+ setCurrentIndex(d->itemCount() - 1);
+}
+
+
+/*!
+ Populates the widgets with data from the next row of the model
+ if the orientation is horizontal (the default), otherwise
+ with data from the next column.
+
+ Calls setCurrentIndex() internally. Does nothing if there is
+ no next row in the model.
+
+ \sa toPrevious(), setCurrentIndex()
+ */
+void QDataWidgetMapper::toNext()
+{
+ Q_D(QDataWidgetMapper);
+ setCurrentIndex(d->currentIdx() + 1);
+}
+
+/*!
+ Populates the widgets with data from the previous row of the model
+ if the orientation is horizontal (the default), otherwise
+ with data from the previous column.
+
+ Calls setCurrentIndex() internally. Does nothing if there is
+ no previous row in the model.
+
+ \sa toNext(), setCurrentIndex()
+ */
+void QDataWidgetMapper::toPrevious()
+{
+ Q_D(QDataWidgetMapper);
+ setCurrentIndex(d->currentIdx() - 1);
+}
+
+/*!
+ \property QDataWidgetMapper::currentIndex
+ \brief the current row or column
+
+ The widgets are populated with with data from the row at \a index
+ if the orientation is horizontal (the default), otherwise with
+ data from the column at \a index.
+
+ \sa setCurrentModelIndex(), toFirst(), toNext(), toPrevious(), toLast()
+*/
+void QDataWidgetMapper::setCurrentIndex(int index)
+{
+ Q_D(QDataWidgetMapper);
+
+ if (index < 0 || index >= d->itemCount())
+ return;
+ d->currentTopLeft = d->orientation == Qt::Horizontal
+ ? d->model->index(index, 0, d->rootIndex)
+ : d->model->index(0, index, d->rootIndex);
+ d->populate();
+
+ emit currentIndexChanged(index);
+}
+
+int QDataWidgetMapper::currentIndex() const
+{
+ Q_D(const QDataWidgetMapper);
+ return d->currentIdx();
+}
+
+/*!
+ Sets the current index to the row of the \a index if the
+ orientation is horizontal (the default), otherwise to the
+ column of the \a index.
+
+ Calls setCurrentIndex() internally. This convenience slot can be
+ connected to the signal \l
+ {QItemSelectionModel::}{currentRowChanged()} or \l
+ {QItemSelectionModel::}{currentColumnChanged()} of another view's
+ \l {QItemSelectionModel}{selection model}.
+
+ The following example illustrates how to update all widgets
+ with new data whenever the selection of a QTableView named
+ \c myTableView changes:
+
+ \snippet doc/src/snippets/code/src_gui_itemviews_qdatawidgetmapper.cpp 2
+
+ \sa currentIndex()
+*/
+void QDataWidgetMapper::setCurrentModelIndex(const QModelIndex &index)
+{
+ Q_D(QDataWidgetMapper);
+
+ if (!index.isValid()
+ || index.model() != d->model
+ || index.parent() != d->rootIndex)
+ return;
+
+ setCurrentIndex(d->orientation == Qt::Horizontal ? index.row() : index.column());
+}
+
+/*!
+ Clears all mappings.
+
+ \sa addMapping(), removeMapping()
+ */
+void QDataWidgetMapper::clearMapping()
+{
+ Q_D(QDataWidgetMapper);
+
+ while (!d->widgetMap.isEmpty()) {
+ QWidget *w = d->widgetMap.takeLast().widget;
+ if (w)
+ w->removeEventFilter(d->delegate);
+ }
+}
+
+/*!
+ \property QDataWidgetMapper::orientation
+ \brief the orientation of the model
+
+ If the orientation is Qt::Horizontal (the default), a widget is
+ mapped to a column of a data model. The widget will be populated
+ with the model's data from its mapped column and the row that
+ currentIndex() points at.
+
+ Use Qt::Horizontal for tabular data that looks like this:
+
+ \table
+ \row \o 1 \o Nokia Corporation and/or its subsidiary(-ies) \o Oslo
+ \row \o 2 \o Trolltech Pty \o Brisbane
+ \row \o 3 \o Trolltech Inc \o Silicon Valley
+ \row \o 4 \o Trolltech China \o Beijing
+ \row \o 5 \o Trolltech GmbH \o Berlin
+ \endtable
+
+ If the orientation is set to Qt::Vertical, a widget is mapped to
+ a row. Calling setCurrentIndex() will change the current column.
+ The widget will be populates with the model's data from its
+ mapped row and the column that currentIndex() points at.
+
+ Use Qt::Vertical for tabular data that looks like this:
+
+ \table
+ \row \o 1 \o 2 \o 3 \o 4 \o 5
+ \row \o Nokia Corporation and/or its subsidiary(-ies) \o Trolltech Pty \o Trolltech Inc \o Trolltech China \o Trolltech GmbH
+ \row \o Oslo \o Brisbane \o Silicon Valley \o Beijing \i Berlin
+ \endtable
+
+ Changing the orientation clears all existing mappings.
+*/
+void QDataWidgetMapper::setOrientation(Qt::Orientation orientation)
+{
+ Q_D(QDataWidgetMapper);
+
+ if (d->orientation == orientation)
+ return;
+
+ clearMapping();
+ d->orientation = orientation;
+}
+
+Qt::Orientation QDataWidgetMapper::orientation() const
+{
+ Q_D(const QDataWidgetMapper);
+ return d->orientation;
+}
+
+/*!
+ \property QDataWidgetMapper::submitPolicy
+ \brief the current submit policy
+
+ Changing the current submit policy will revert all widgets
+ to the current data from the model.
+*/
+void QDataWidgetMapper::setSubmitPolicy(SubmitPolicy policy)
+{
+ Q_D(QDataWidgetMapper);
+ if (policy == d->submitPolicy)
+ return;
+
+ revert();
+ d->submitPolicy = policy;
+}
+
+QDataWidgetMapper::SubmitPolicy QDataWidgetMapper::submitPolicy() const
+{
+ Q_D(const QDataWidgetMapper);
+ return d->submitPolicy;
+}
+
+QT_END_NAMESPACE
+
+#include "moc_qdatawidgetmapper.cpp"
+
+#endif // QT_NO_DATAWIDGETMAPPER
diff --git a/src/gui/itemviews/qdatawidgetmapper.h b/src/gui/itemviews/qdatawidgetmapper.h
new file mode 100644
index 0000000000..d04e8816fe
--- /dev/null
+++ b/src/gui/itemviews/qdatawidgetmapper.h
@@ -0,0 +1,128 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QDATAWIDGETMAPPER_H
+#define QDATAWIDGETMAPPER_H
+
+#include "QtCore/qobject.h"
+
+#ifndef QT_NO_DATAWIDGETMAPPER
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+class QAbstractItemDelegate;
+class QAbstractItemModel;
+class QModelIndex;
+class QDataWidgetMapperPrivate;
+
+class Q_GUI_EXPORT QDataWidgetMapper: public QObject
+{
+ Q_OBJECT
+
+ Q_ENUMS(SubmitPolicy)
+ Q_PROPERTY(int currentIndex READ currentIndex WRITE setCurrentIndex NOTIFY currentIndexChanged)
+ Q_PROPERTY(Qt::Orientation orientation READ orientation WRITE setOrientation)
+ Q_PROPERTY(SubmitPolicy submitPolicy READ submitPolicy WRITE setSubmitPolicy)
+
+public:
+ QDataWidgetMapper(QObject *parent = 0);
+ ~QDataWidgetMapper();
+
+ void setModel(QAbstractItemModel *model);
+ QAbstractItemModel *model() const;
+
+ void setItemDelegate(QAbstractItemDelegate *delegate);
+ QAbstractItemDelegate *itemDelegate() const;
+
+ void setRootIndex(const QModelIndex &index);
+ QModelIndex rootIndex() const;
+
+ void setOrientation(Qt::Orientation aOrientation);
+ Qt::Orientation orientation() const;
+
+ enum SubmitPolicy { AutoSubmit, ManualSubmit };
+ void setSubmitPolicy(SubmitPolicy policy);
+ SubmitPolicy submitPolicy() const;
+
+ void addMapping(QWidget *widget, int section);
+ void addMapping(QWidget *widget, int section, const QByteArray &propertyName);
+ void removeMapping(QWidget *widget);
+ int mappedSection(QWidget *widget) const;
+ QByteArray mappedPropertyName(QWidget *widget) const;
+ QWidget *mappedWidgetAt(int section) const;
+ void clearMapping();
+
+ int currentIndex() const;
+
+public Q_SLOTS:
+ void revert();
+ bool submit();
+
+ void toFirst();
+ void toLast();
+ void toNext();
+ void toPrevious();
+ virtual void setCurrentIndex(int index);
+ void setCurrentModelIndex(const QModelIndex &index);
+
+Q_SIGNALS:
+ void currentIndexChanged(int index);
+
+private:
+ Q_DECLARE_PRIVATE(QDataWidgetMapper)
+ Q_DISABLE_COPY(QDataWidgetMapper)
+ Q_PRIVATE_SLOT(d_func(), void _q_dataChanged(const QModelIndex &, const QModelIndex &))
+ Q_PRIVATE_SLOT(d_func(), void _q_commitData(QWidget *))
+ Q_PRIVATE_SLOT(d_func(), void _q_closeEditor(QWidget *, QAbstractItemDelegate::EndEditHint))
+ Q_PRIVATE_SLOT(d_func(), void _q_modelDestroyed())
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QT_NO_DATAWIDGETMAPPER
+#endif
+
diff --git a/src/gui/itemviews/qdirmodel.cpp b/src/gui/itemviews/qdirmodel.cpp
new file mode 100644
index 0000000000..7da7c7a88f
--- /dev/null
+++ b/src/gui/itemviews/qdirmodel.cpp
@@ -0,0 +1,1410 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qdirmodel.h"
+
+#ifndef QT_NO_DIRMODEL
+#include <qstack.h>
+#include <qfile.h>
+#include <qurl.h>
+#include <qmime.h>
+#include <qpair.h>
+#include <qvector.h>
+#include <qobject.h>
+#include <qdatetime.h>
+#include <qlocale.h>
+#include <qstyle.h>
+#include <qapplication.h>
+#include <private/qabstractitemmodel_p.h>
+#include <qdebug.h>
+
+/*!
+ \enum QDirModel::Roles
+ \value FileIconRole
+ \value FilePathRole
+ \value FileNameRole
+*/
+
+QT_BEGIN_NAMESPACE
+
+class QDirModelPrivate : public QAbstractItemModelPrivate
+{
+ Q_DECLARE_PUBLIC(QDirModel)
+
+public:
+ struct QDirNode
+ {
+ QDirNode() : parent(0), populated(false), stat(false) {}
+ ~QDirNode() { children.clear(); }
+ QDirNode *parent;
+ QFileInfo info;
+ QIcon icon; // cache the icon
+ mutable QVector<QDirNode> children;
+ mutable bool populated; // have we read the children
+ mutable bool stat;
+ };
+
+ QDirModelPrivate()
+ : resolveSymlinks(true),
+ readOnly(true),
+ lazyChildCount(false),
+ allowAppendChild(true),
+ iconProvider(&defaultProvider),
+ shouldStat(true) // ### This is set to false by QFileDialog
+ { }
+
+ void init();
+ QDirNode *node(int row, QDirNode *parent) const;
+ QVector<QDirNode> children(QDirNode *parent, bool stat) const;
+
+ void _q_refresh();
+
+ void savePersistentIndexes();
+ void restorePersistentIndexes();
+
+ QFileInfoList entryInfoList(const QString &path) const;
+ QStringList entryList(const QString &path) const;
+
+ QString name(const QModelIndex &index) const;
+ QString size(const QModelIndex &index) const;
+ QString type(const QModelIndex &index) const;
+ QString time(const QModelIndex &index) const;
+
+ void appendChild(QDirModelPrivate::QDirNode *parent, const QString &path) const;
+ static QFileInfo resolvedInfo(QFileInfo info);
+
+ inline QDirNode *node(const QModelIndex &index) const;
+ inline void populate(QDirNode *parent) const;
+ inline void clear(QDirNode *parent) const;
+
+ void invalidate();
+
+ mutable QDirNode root;
+ bool resolveSymlinks;
+ bool readOnly;
+ bool lazyChildCount;
+ bool allowAppendChild;
+
+ QDir::Filters filters;
+ QDir::SortFlags sort;
+ QStringList nameFilters;
+
+ QFileIconProvider *iconProvider;
+ QFileIconProvider defaultProvider;
+
+ struct SavedPersistent {
+ QString path;
+ int column;
+ QPersistentModelIndexData *data;
+ QPersistentModelIndex index;
+ };
+ QList<SavedPersistent> savedPersistent;
+ QPersistentModelIndex toBeRefreshed;
+
+ bool shouldStat; // use the "carefull not to stat directories" mode
+};
+
+void qt_setDirModelShouldNotStat(QDirModelPrivate *modelPrivate)
+{
+ modelPrivate->shouldStat = false;
+}
+
+QDirModelPrivate::QDirNode *QDirModelPrivate::node(const QModelIndex &index) const
+{
+ QDirModelPrivate::QDirNode *n =
+ static_cast<QDirModelPrivate::QDirNode*>(index.internalPointer());
+ Q_ASSERT(n);
+ return n;
+}
+
+void QDirModelPrivate::populate(QDirNode *parent) const
+{
+ Q_ASSERT(parent);
+ parent->children = children(parent, parent->stat);
+ parent->populated = true;
+}
+
+void QDirModelPrivate::clear(QDirNode *parent) const
+{
+ Q_ASSERT(parent);
+ parent->children.clear();
+ parent->populated = false;
+}
+
+void QDirModelPrivate::invalidate()
+{
+ QStack<const QDirNode*> nodes;
+ nodes.push(&root);
+ while (!nodes.empty()) {
+ const QDirNode *current = nodes.pop();
+ current->stat = false;
+ const QVector<QDirNode> children = current->children;
+ for (int i = 0; i < children.count(); ++i)
+ nodes.push(&children.at(i));
+ }
+}
+
+/*!
+ \class QDirModel
+
+ \brief The QDirModel class provides a data model for the local filesystem.
+
+ \ingroup model-view
+
+ This class provides access to the local filesystem, providing functions
+ for renaming and removing files and directories, and for creating new
+ directories. In the simplest case, it can be used with a suitable display
+ widget as part of a browser or filer.
+
+ QDirModel keeps a cache with file information. The cache needs to be
+ updated with refresh().
+
+ A directory model that displays the contents of a default directory
+ is usually constructed with a parent object:
+
+ \snippet doc/src/snippets/shareddirmodel/main.cpp 2
+
+ A tree view can be used to display the contents of the model
+
+ \snippet doc/src/snippets/shareddirmodel/main.cpp 4
+
+ and the contents of a particular directory can be displayed by
+ setting the tree view's root index:
+
+ \snippet doc/src/snippets/shareddirmodel/main.cpp 7
+
+ The view's root index can be used to control how much of a
+ hierarchical model is displayed. QDirModel provides a convenience
+ function that returns a suitable model index for a path to a
+ directory within the model.
+
+ QDirModel can be accessed using the standard interface provided by
+ QAbstractItemModel, but it also provides some convenience functions
+ that are specific to a directory model. The fileInfo() and isDir()
+ functions provide information about the underlying files and directories
+ related to items in the model.
+
+ Directories can be created and removed using mkdir(), rmdir(), and the
+ model will be automatically updated to take the changes into account.
+
+ \note QDirModel requires an instance of a GUI application.
+
+ \sa nameFilters(), setFilter(), filter(), QListView, QTreeView,
+ {Dir View Example}, {Model Classes}
+*/
+
+/*!
+ Constructs a new directory model with the given \a parent.
+ Only those files matching the \a nameFilters and the
+ \a filters are included in the model. The sort order is given by the
+ \a sort flags.
+*/
+
+QDirModel::QDirModel(const QStringList &nameFilters,
+ QDir::Filters filters,
+ QDir::SortFlags sort,
+ QObject *parent)
+ : QAbstractItemModel(*new QDirModelPrivate, parent)
+{
+ Q_D(QDirModel);
+ // we always start with QDir::drives()
+ d->nameFilters = nameFilters.isEmpty() ? QStringList(QLatin1String("*")) : nameFilters;
+ d->filters = filters;
+ d->sort = sort;
+ d->root.parent = 0;
+ d->root.info = QFileInfo();
+ d->clear(&d->root);
+}
+
+/*!
+ Constructs a directory model with the given \a parent.
+*/
+
+QDirModel::QDirModel(QObject *parent)
+ : QAbstractItemModel(*new QDirModelPrivate, parent)
+{
+ Q_D(QDirModel);
+ d->init();
+}
+
+/*!
+ \internal
+*/
+QDirModel::QDirModel(QDirModelPrivate &dd, QObject *parent)
+ : QAbstractItemModel(dd, parent)
+{
+ Q_D(QDirModel);
+ d->init();
+}
+
+/*!
+ Destroys this directory model.
+*/
+
+QDirModel::~QDirModel()
+{
+
+}
+
+/*!
+ Returns the model item index for the item in the \a parent with the
+ given \a row and \a column.
+
+*/
+
+QModelIndex QDirModel::index(int row, int column, const QModelIndex &parent) const
+{
+ Q_D(const QDirModel);
+ // note that rowCount does lazy population
+ if (column < 0 || column >= columnCount(parent) || row < 0 || parent.column() > 0)
+ return QModelIndex();
+ // make sure the list of children is up to date
+ QDirModelPrivate::QDirNode *p = (d->indexValid(parent) ? d->node(parent) : &d->root);
+ Q_ASSERT(p);
+ if (!p->populated)
+ d->populate(p); // populate without stat'ing
+ if (row >= p->children.count())
+ return QModelIndex();
+ // now get the internal pointer for the index
+ QDirModelPrivate::QDirNode *n = d->node(row, d->indexValid(parent) ? p : 0);
+ Q_ASSERT(n);
+
+ return createIndex(row, column, n);
+}
+
+/*!
+ Return the parent of the given \a child model item.
+*/
+
+QModelIndex QDirModel::parent(const QModelIndex &child) const
+{
+ Q_D(const QDirModel);
+
+ if (!d->indexValid(child))
+ return QModelIndex();
+ QDirModelPrivate::QDirNode *node = d->node(child);
+ QDirModelPrivate::QDirNode *par = (node ? node->parent : 0);
+ if (par == 0) // parent is the root node
+ return QModelIndex();
+
+ // get the parent's row
+ const QVector<QDirModelPrivate::QDirNode> children =
+ par->parent ? par->parent->children : d->root.children;
+ Q_ASSERT(children.count() > 0);
+ int row = (par - &(children.at(0)));
+ Q_ASSERT(row >= 0);
+
+ return createIndex(row, 0, par);
+}
+
+/*!
+ Returns the number of rows in the \a parent model item.
+
+*/
+
+int QDirModel::rowCount(const QModelIndex &parent) const
+{
+ Q_D(const QDirModel);
+ if (parent.column() > 0)
+ return 0;
+
+ if (!parent.isValid()) {
+ if (!d->root.populated) // lazy population
+ d->populate(&d->root);
+ return d->root.children.count();
+ }
+ if (parent.model() != this)
+ return 0;
+ QDirModelPrivate::QDirNode *p = d->node(parent);
+ if (p->info.isDir() && !p->populated) // lazy population
+ d->populate(p);
+ return p->children.count();
+}
+
+/*!
+ Returns the number of columns in the \a parent model item.
+
+*/
+
+int QDirModel::columnCount(const QModelIndex &parent) const
+{
+ if (parent.column() > 0)
+ return 0;
+ return 4;
+}
+
+/*!
+ Returns the data for the model item \a index with the given \a role.
+*/
+QVariant QDirModel::data(const QModelIndex &index, int role) const
+{
+ Q_D(const QDirModel);
+ if (!d->indexValid(index))
+ return QVariant();
+
+ if (role == Qt::DisplayRole || role == Qt::EditRole) {
+ switch (index.column()) {
+ case 0: return d->name(index);
+ case 1: return d->size(index);
+ case 2: return d->type(index);
+ case 3: return d->time(index);
+ default:
+ qWarning("data: invalid display value column %d", index.column());
+ return QVariant();
+ }
+ }
+
+ if (index.column() == 0) {
+ if (role == FileIconRole)
+ return fileIcon(index);
+ if (role == FilePathRole)
+ return filePath(index);
+ if (role == FileNameRole)
+ return fileName(index);
+ }
+
+ if (index.column() == 1 && Qt::TextAlignmentRole == role) {
+ return Qt::AlignRight;
+ }
+ return QVariant();
+}
+
+/*!
+ Sets the data for the model item \a index with the given \a role to
+ the data referenced by the \a value. Returns true if successful;
+ otherwise returns false.
+
+ \sa Qt::ItemDataRole
+*/
+
+bool QDirModel::setData(const QModelIndex &index, const QVariant &value, int role)
+{
+ Q_D(QDirModel);
+ if (!d->indexValid(index) || index.column() != 0
+ || (flags(index) & Qt::ItemIsEditable) == 0 || role != Qt::EditRole)
+ return false;
+
+ QDirModelPrivate::QDirNode *node = d->node(index);
+ QDir dir = node->info.dir();
+ QString name = value.toString();
+ if (dir.rename(node->info.fileName(), name)) {
+ node->info = QFileInfo(dir, name);
+ QModelIndex sibling = index.sibling(index.row(), 3);
+ emit dataChanged(index, sibling);
+
+ d->toBeRefreshed = index.parent();
+ QMetaObject::invokeMethod(this, "_q_refresh", Qt::QueuedConnection);
+
+ return true;
+ }
+
+ return false;
+}
+
+/*!
+ Returns the data stored under the given \a role for the specified \a section
+ of the header with the given \a orientation.
+*/
+
+QVariant QDirModel::headerData(int section, Qt::Orientation orientation, int role) const
+{
+ if (orientation == Qt::Horizontal) {
+ if (role != Qt::DisplayRole)
+ return QVariant();
+ switch (section) {
+ case 0: return tr("Name");
+ case 1: return tr("Size");
+ case 2: return
+#ifdef Q_OS_MAC
+ tr("Kind", "Match OS X Finder");
+#else
+ tr("Type", "All other platforms");
+#endif
+ // Windows - Type
+ // OS X - Kind
+ // Konqueror - File Type
+ // Nautilus - Type
+ case 3: return tr("Date Modified");
+ default: return QVariant();
+ }
+ }
+ return QAbstractItemModel::headerData(section, orientation, role);
+}
+
+/*!
+ Returns true if the \a parent model item has children; otherwise
+ returns false.
+*/
+
+bool QDirModel::hasChildren(const QModelIndex &parent) const
+{
+ Q_D(const QDirModel);
+ if (parent.column() > 0)
+ return false;
+
+ if (!parent.isValid()) // the invalid index is the "My Computer" item
+ return true; // the drives
+ QDirModelPrivate::QDirNode *p = d->node(parent);
+ Q_ASSERT(p);
+
+ if (d->lazyChildCount) // optimization that only checks for children if the node has been populated
+ return p->info.isDir();
+ return p->info.isDir() && rowCount(parent) > 0;
+}
+
+/*!
+ Returns the item flags for the given \a index in the model.
+
+ \sa Qt::ItemFlags
+*/
+Qt::ItemFlags QDirModel::flags(const QModelIndex &index) const
+{
+ Q_D(const QDirModel);
+ Qt::ItemFlags flags = QAbstractItemModel::flags(index);
+ if (!d->indexValid(index))
+ return flags;
+ flags |= Qt::ItemIsDragEnabled;
+ if (d->readOnly)
+ return flags;
+ QDirModelPrivate::QDirNode *node = d->node(index);
+ if ((index.column() == 0) && node->info.isWritable()) {
+ flags |= Qt::ItemIsEditable;
+ if (fileInfo(index).isDir()) // is directory and is editable
+ flags |= Qt::ItemIsDropEnabled;
+ }
+ return flags;
+}
+
+/*!
+ Sort the model items in the \a column using the \a order given.
+ The order is a value defined in \l Qt::SortOrder.
+*/
+
+void QDirModel::sort(int column, Qt::SortOrder order)
+{
+ QDir::SortFlags sort = QDir::DirsFirst | QDir::IgnoreCase;
+ if (order == Qt::DescendingOrder)
+ sort |= QDir::Reversed;
+
+ switch (column) {
+ case 0:
+ sort |= QDir::Name;
+ break;
+ case 1:
+ sort |= QDir::Size;
+ break;
+ case 2:
+ sort |= QDir::Type;
+ break;
+ case 3:
+ sort |= QDir::Time;
+ break;
+ default:
+ break;
+ }
+
+ setSorting(sort);
+}
+
+/*!
+ Returns a list of MIME types that can be used to describe a list of items
+ in the model.
+*/
+
+QStringList QDirModel::mimeTypes() const
+{
+ return QStringList(QLatin1String("text/uri-list"));
+}
+
+/*!
+ Returns an object that contains a serialized description of the specified
+ \a indexes. The format used to describe the items corresponding to the
+ indexes is obtained from the mimeTypes() function.
+
+ If the list of indexes is empty, 0 is returned rather than a serialized
+ empty list.
+*/
+
+QMimeData *QDirModel::mimeData(const QModelIndexList &indexes) const
+{
+ QList<QUrl> urls;
+ QList<QModelIndex>::const_iterator it = indexes.begin();
+ for (; it != indexes.end(); ++it)
+ if ((*it).column() == 0)
+ urls << QUrl::fromLocalFile(filePath(*it));
+ QMimeData *data = new QMimeData();
+ data->setUrls(urls);
+ return data;
+}
+
+/*!
+ Handles the \a data supplied by a drag and drop operation that ended with
+ the given \a action over the row in the model specified by the \a row and
+ \a column and by the \a parent index.
+
+ \sa supportedDropActions()
+*/
+
+bool QDirModel::dropMimeData(const QMimeData *data, Qt::DropAction action,
+ int /* row */, int /* column */, const QModelIndex &parent)
+{
+ Q_D(QDirModel);
+ if (!d->indexValid(parent) || isReadOnly())
+ return false;
+
+ bool success = true;
+ QString to = filePath(parent) + QDir::separator();
+ QModelIndex _parent = parent;
+
+ QList<QUrl> urls = data->urls();
+ QList<QUrl>::const_iterator it = urls.constBegin();
+
+ switch (action) {
+ case Qt::CopyAction:
+ for (; it != urls.constEnd(); ++it) {
+ QString path = (*it).toLocalFile();
+ success = QFile::copy(path, to + QFileInfo(path).fileName()) && success;
+ }
+ break;
+ case Qt::LinkAction:
+ for (; it != urls.constEnd(); ++it) {
+ QString path = (*it).toLocalFile();
+ success = QFile::link(path, to + QFileInfo(path).fileName()) && success;
+ }
+ break;
+ case Qt::MoveAction:
+ for (; it != urls.constEnd(); ++it) {
+ QString path = (*it).toLocalFile();
+ if (QFile::copy(path, to + QFileInfo(path).fileName())
+ && QFile::remove(path)) {
+ QModelIndex idx=index(QFileInfo(path).path());
+ if (idx.isValid()) {
+ refresh(idx);
+ //the previous call to refresh may invalidate the _parent. so recreate a new QModelIndex
+ _parent = index(to);
+ }
+ } else {
+ success = false;
+ }
+ }
+ break;
+ default:
+ return false;
+ }
+
+ if (success)
+ refresh(_parent);
+
+ return success;
+}
+
+/*!
+ Returns the drop actions supported by this model.
+
+ \sa Qt::DropActions
+*/
+
+Qt::DropActions QDirModel::supportedDropActions() const
+{
+ return Qt::CopyAction | Qt::MoveAction; // FIXME: LinkAction is not supported yet
+}
+
+/*!
+ Sets the \a provider of file icons for the directory model.
+
+*/
+
+void QDirModel::setIconProvider(QFileIconProvider *provider)
+{
+ Q_D(QDirModel);
+ d->iconProvider = provider;
+}
+
+/*!
+ Returns the file icon provider for this directory model.
+*/
+
+QFileIconProvider *QDirModel::iconProvider() const
+{
+ Q_D(const QDirModel);
+ return d->iconProvider;
+}
+
+/*!
+ Sets the name \a filters for the directory model.
+*/
+
+void QDirModel::setNameFilters(const QStringList &filters)
+{
+ Q_D(QDirModel);
+ d->nameFilters = filters;
+ emit layoutAboutToBeChanged();
+ if (d->shouldStat)
+ refresh(QModelIndex());
+ else
+ d->invalidate();
+ emit layoutChanged();
+}
+
+/*!
+ Returns a list of filters applied to the names in the model.
+*/
+
+QStringList QDirModel::nameFilters() const
+{
+ Q_D(const QDirModel);
+ return d->nameFilters;
+}
+
+/*!
+ Sets the directory model's filter to that specified by \a filters.
+
+ Note that the filter you set should always include the QDir::AllDirs enum value,
+ otherwise QDirModel won't be able to read the directory structure.
+
+ \sa QDir::Filters
+*/
+
+void QDirModel::setFilter(QDir::Filters filters)
+{
+ Q_D(QDirModel);
+ d->filters = filters;
+ emit layoutAboutToBeChanged();
+ if (d->shouldStat)
+ refresh(QModelIndex());
+ else
+ d->invalidate();
+ emit layoutChanged();
+}
+
+/*!
+ Returns the filter specification for the directory model.
+
+ \sa QDir::Filters
+*/
+
+QDir::Filters QDirModel::filter() const
+{
+ Q_D(const QDirModel);
+ return d->filters;
+}
+
+/*!
+ Sets the directory model's sorting order to that specified by \a sort.
+
+ \sa QDir::SortFlags
+*/
+
+void QDirModel::setSorting(QDir::SortFlags sort)
+{
+ Q_D(QDirModel);
+ d->sort = sort;
+ emit layoutAboutToBeChanged();
+ if (d->shouldStat)
+ refresh(QModelIndex());
+ else
+ d->invalidate();
+ emit layoutChanged();
+}
+
+/*!
+ Returns the sorting method used for the directory model.
+
+ \sa QDir::SortFlags */
+
+QDir::SortFlags QDirModel::sorting() const
+{
+ Q_D(const QDirModel);
+ return d->sort;
+}
+
+/*!
+ \property QDirModel::resolveSymlinks
+ \brief Whether the directory model should resolve symbolic links
+
+ This is only relevant on operating systems that support symbolic
+ links.
+*/
+void QDirModel::setResolveSymlinks(bool enable)
+{
+ Q_D(QDirModel);
+ d->resolveSymlinks = enable;
+}
+
+bool QDirModel::resolveSymlinks() const
+{
+ Q_D(const QDirModel);
+ return d->resolveSymlinks;
+}
+
+/*!
+ \property QDirModel::readOnly
+ \brief Whether the directory model allows writing to the file system
+
+ If this property is set to false, the directory model will allow renaming, copying
+ and deleting of files and directories.
+
+ This property is true by default
+*/
+
+void QDirModel::setReadOnly(bool enable)
+{
+ Q_D(QDirModel);
+ d->readOnly = enable;
+}
+
+bool QDirModel::isReadOnly() const
+{
+ Q_D(const QDirModel);
+ return d->readOnly;
+}
+
+/*!
+ \property QDirModel::lazyChildCount
+ \brief Whether the directory model optimizes the hasChildren function
+ to only check if the item is a directory.
+
+ If this property is set to false, the directory model will make sure that a directory
+ actually containes any files before reporting that it has children.
+ Otherwise the directory model will report that an item has children if the item
+ is a directory.
+
+ This property is false by default
+*/
+
+void QDirModel::setLazyChildCount(bool enable)
+{
+ Q_D(QDirModel);
+ d->lazyChildCount = enable;
+}
+
+bool QDirModel::lazyChildCount() const
+{
+ Q_D(const QDirModel);
+ return d->lazyChildCount;
+}
+
+/*!
+ QDirModel caches file information. This function updates the
+ cache. The \a parent parameter is the directory from which the
+ model is updated; the default value will update the model from
+ root directory of the file system (the entire model).
+*/
+
+void QDirModel::refresh(const QModelIndex &parent)
+{
+ Q_D(QDirModel);
+
+ QDirModelPrivate::QDirNode *n = d->indexValid(parent) ? d->node(parent) : &(d->root);
+
+ int rows = n->children.count();
+ if (rows == 0) {
+ emit layoutAboutToBeChanged();
+ n->stat = true; // make sure that next time we read all the info
+ n->populated = false;
+ emit layoutChanged();
+ return;
+ }
+
+ emit layoutAboutToBeChanged();
+ d->savePersistentIndexes();
+ d->rowsAboutToBeRemoved(parent, 0, rows - 1);
+ n->stat = true; // make sure that next time we read all the info
+ d->clear(n);
+ d->rowsRemoved(parent, 0, rows - 1);
+ d->restorePersistentIndexes();
+ emit layoutChanged();
+}
+
+/*!
+ \overload
+
+ Returns the model item index for the given \a path.
+*/
+
+QModelIndex QDirModel::index(const QString &path, int column) const
+{
+ Q_D(const QDirModel);
+
+ if (path.isEmpty() || path == QCoreApplication::translate("QFileDialog", "My Computer"))
+ return QModelIndex();
+
+ QString absolutePath = QDir(path).absolutePath();
+#if defined(Q_OS_WIN) && !defined(Q_OS_WINCE)
+ absolutePath = absolutePath.toLower();
+ // On Windows, "filename......." and "filename" are equivalent
+ if (absolutePath.endsWith(QLatin1Char('.'))) {
+ int i;
+ for (i = absolutePath.count() - 1; i >= 0; --i) {
+ if (absolutePath.at(i) != QLatin1Char('.'))
+ break;
+ }
+ absolutePath = absolutePath.left(i+1);
+ }
+#endif
+
+ QStringList pathElements = absolutePath.split(QLatin1Char('/'), QString::SkipEmptyParts);
+ if ((pathElements.isEmpty() || !QFileInfo(path).exists())
+#if !defined(Q_OS_WIN) || defined(Q_OS_WINCE)
+ && path != QLatin1String("/")
+#endif
+ )
+ return QModelIndex();
+
+ QModelIndex idx; // start with "My Computer"
+ if (!d->root.populated) // make sure the root is populated
+ d->populate(&d->root);
+
+#if defined(Q_OS_WIN) && !defined(Q_OS_WINCE)
+ if (absolutePath.startsWith(QLatin1String("//"))) { // UNC path
+ QString host = pathElements.first();
+ int r = 0;
+ for (; r < d->root.children.count(); ++r)
+ if (d->root.children.at(r).info.fileName() == host)
+ break;
+ bool childAppended = false;
+ if (r >= d->root.children.count() && d->allowAppendChild) {
+ d->appendChild(&d->root, QLatin1String("//") + host);
+ childAppended = true;
+ }
+ idx = index(r, 0, QModelIndex());
+ pathElements.pop_front();
+ if (childAppended)
+ emit const_cast<QDirModel*>(this)->layoutChanged();
+ } else if (pathElements.at(0).endsWith(QLatin1Char(':'))) {
+ pathElements[0] += QLatin1Char('/');
+ }
+#else
+ // add the "/" item, since it is a valid path element on unix
+ pathElements.prepend(QLatin1String("/"));
+#endif
+
+ for (int i = 0; i < pathElements.count(); ++i) {
+ Q_ASSERT(!pathElements.at(i).isEmpty());
+ QString element = pathElements.at(i);
+ QDirModelPrivate::QDirNode *parent = (idx.isValid() ? d->node(idx) : &d->root);
+
+ Q_ASSERT(parent);
+ if (!parent->populated)
+ d->populate(parent);
+
+ // search for the element in the child nodes first
+ int row = -1;
+ for (int j = parent->children.count() - 1; j >= 0; --j) {
+ const QFileInfo& fi = parent->children.at(j).info;
+ QString childFileName;
+#if defined(Q_OS_WIN) && !defined(Q_OS_WINCE)
+ childFileName = idx.isValid() ? fi.fileName() : fi.absoluteFilePath();
+ childFileName = childFileName.toLower();
+#else
+ childFileName = idx.isValid() ? fi.fileName() : fi.absoluteFilePath();
+#endif
+ if (childFileName == element) {
+ if (i == pathElements.count() - 1)
+ parent->children[j].stat = true;
+ row = j;
+ break;
+ }
+ }
+
+ // we couldn't find the path element, we create a new node since we _know_ that the path is valid
+ if (row == -1) {
+#if defined(Q_OS_WINCE)
+ QString newPath;
+ if (parent->info.isRoot())
+ newPath = parent->info.absoluteFilePath() + element;
+ else
+ newPath= parent->info.absoluteFilePath() + QLatin1Char('/') + element;
+#else
+ QString newPath = parent->info.absoluteFilePath() + QLatin1Char('/') + element;
+#endif
+ if (!d->allowAppendChild || !QFileInfo(newPath).isDir())
+ return QModelIndex();
+ d->appendChild(parent, newPath);
+ row = parent->children.count() - 1;
+ if (i == pathElements.count() - 1) // always stat children of the last element
+ parent->children[row].stat = true;
+ emit const_cast<QDirModel*>(this)->layoutChanged();
+ }
+
+ Q_ASSERT(row >= 0);
+ idx = createIndex(row, 0, static_cast<void*>(&parent->children[row]));
+ Q_ASSERT(idx.isValid());
+ }
+
+ if (column != 0)
+ return idx.sibling(idx.row(), column);
+ return idx;
+}
+
+/*!
+ Returns true if the model item \a index represents a directory;
+ otherwise returns false.
+*/
+
+bool QDirModel::isDir(const QModelIndex &index) const
+{
+ Q_D(const QDirModel);
+ Q_ASSERT(d->indexValid(index));
+ QDirModelPrivate::QDirNode *node = d->node(index);
+ return node->info.isDir();
+}
+
+/*!
+ Create a directory with the \a name in the \a parent model item.
+*/
+
+QModelIndex QDirModel::mkdir(const QModelIndex &parent, const QString &name)
+{
+ Q_D(QDirModel);
+ if (!d->indexValid(parent) || isReadOnly())
+ return QModelIndex();
+
+ QDirModelPrivate::QDirNode *p = d->node(parent);
+ QString path = p->info.absoluteFilePath();
+ // For the indexOf() method to work, the new directory has to be a direct child of
+ // the parent directory.
+
+ QDir newDir(name);
+ QDir dir(path);
+ if (newDir.isRelative())
+ newDir = QDir(path + QLatin1Char('/') + name);
+ QString childName = newDir.dirName(); // Get the singular name of the directory
+ newDir.cdUp();
+
+ if (newDir.absolutePath() != dir.absolutePath() || !dir.mkdir(name))
+ return QModelIndex(); // nothing happened
+
+ refresh(parent);
+
+ QStringList entryList = d->entryList(path);
+ int r = entryList.indexOf(childName);
+ QModelIndex i = index(r, 0, parent); // return an invalid index
+
+ return i;
+}
+
+/*!
+ Removes the directory corresponding to the model item \a index in the
+ directory model and \bold{deletes the corresponding directory from the
+ file system}, returning true if successful. If the directory cannot be
+ removed, false is returned.
+
+ \warning This function deletes directories from the file system; it does
+ \bold{not} move them to a location where they can be recovered.
+
+ \sa remove()
+*/
+
+bool QDirModel::rmdir(const QModelIndex &index)
+{
+ Q_D(QDirModel);
+ if (!d->indexValid(index) || isReadOnly())
+ return false;
+
+ QDirModelPrivate::QDirNode *n = d_func()->node(index);
+ if (!n->info.isDir()) {
+ qWarning("rmdir: the node is not a directory");
+ return false;
+ }
+
+ QModelIndex par = parent(index);
+ QDirModelPrivate::QDirNode *p = d_func()->node(par);
+ QDir dir = p->info.dir(); // parent dir
+ QString path = n->info.absoluteFilePath();
+ if (!dir.rmdir(path))
+ return false;
+
+ refresh(par);
+
+ return true;
+}
+
+/*!
+ Removes the model item \a index from the directory model and \bold{deletes the
+ corresponding file from the file system}, returning true if successful. If the
+ item cannot be removed, false is returned.
+
+ \warning This function deletes files from the file system; it does \bold{not}
+ move them to a location where they can be recovered.
+
+ \sa rmdir()
+*/
+
+bool QDirModel::remove(const QModelIndex &index)
+{
+ Q_D(QDirModel);
+ if (!d->indexValid(index) || isReadOnly())
+ return false;
+
+ QDirModelPrivate::QDirNode *n = d_func()->node(index);
+ if (n->info.isDir())
+ return false;
+
+ QModelIndex par = parent(index);
+ QDirModelPrivate::QDirNode *p = d_func()->node(par);
+ QDir dir = p->info.dir(); // parent dir
+ QString path = n->info.absoluteFilePath();
+ if (!dir.remove(path))
+ return false;
+
+ refresh(par);
+
+ return true;
+}
+
+/*!
+ Returns the path of the item stored in the model under the
+ \a index given.
+
+*/
+
+QString QDirModel::filePath(const QModelIndex &index) const
+{
+ Q_D(const QDirModel);
+ if (d->indexValid(index)) {
+ QFileInfo fi = fileInfo(index);
+ if (d->resolveSymlinks && fi.isSymLink())
+ fi = d->resolvedInfo(fi);
+ return QDir::cleanPath(fi.absoluteFilePath());
+ }
+ return QString(); // root path
+}
+
+/*!
+ Returns the name of the item stored in the model under the
+ \a index given.
+
+*/
+
+QString QDirModel::fileName(const QModelIndex &index) const
+{
+ Q_D(const QDirModel);
+ if (!d->indexValid(index))
+ return QString();
+ QFileInfo info = fileInfo(index);
+ if (info.isRoot())
+ return info.absoluteFilePath();
+ if (d->resolveSymlinks && info.isSymLink())
+ info = d->resolvedInfo(info);
+ return info.fileName();
+}
+
+/*!
+ Returns the icons for the item stored in the model under the given
+ \a index.
+*/
+
+QIcon QDirModel::fileIcon(const QModelIndex &index) const
+{
+ Q_D(const QDirModel);
+ if (!d->indexValid(index))
+ return d->iconProvider->icon(QFileIconProvider::Computer);
+ QDirModelPrivate::QDirNode *node = d->node(index);
+ if (node->icon.isNull())
+ node->icon = d->iconProvider->icon(node->info);
+ return node->icon;
+}
+
+/*!
+ Returns the file information for the specified model \a index.
+
+ \bold{Note:} If the model index represents a symbolic link in the
+ underlying filing system, the file information returned will contain
+ information about the symbolic link itself, regardless of whether
+ resolveSymlinks is enabled or not.
+
+ \sa QFileInfo::symLinkTarget()
+*/
+
+QFileInfo QDirModel::fileInfo(const QModelIndex &index) const
+{
+ Q_D(const QDirModel);
+ Q_ASSERT(d->indexValid(index));
+
+ QDirModelPrivate::QDirNode *node = d->node(index);
+ return node->info;
+}
+
+/*!
+ \fn QObject *QDirModel::parent() const
+ \internal
+*/
+
+/*
+ The root node is never seen outside the model.
+*/
+
+void QDirModelPrivate::init()
+{
+ filters = QDir::AllEntries | QDir::NoDotAndDotDot;
+ sort = QDir::Name;
+ nameFilters << QLatin1String("*");
+ root.parent = 0;
+ root.info = QFileInfo();
+ clear(&root);
+}
+
+QDirModelPrivate::QDirNode *QDirModelPrivate::node(int row, QDirNode *parent) const
+{
+ if (row < 0)
+ return 0;
+
+ bool isDir = !parent || parent->info.isDir();
+ QDirNode *p = (parent ? parent : &root);
+ if (isDir && !p->populated)
+ populate(p); // will also resolve symlinks
+
+ if (row >= p->children.count()) {
+ qWarning("node: the row does not exist");
+ return 0;
+ }
+
+ return const_cast<QDirNode*>(&p->children.at(row));
+}
+
+QVector<QDirModelPrivate::QDirNode> QDirModelPrivate::children(QDirNode *parent, bool stat) const
+{
+ Q_ASSERT(parent);
+ QFileInfoList infoList;
+ if (parent == &root) {
+ parent = 0;
+ infoList = QDir::drives();
+ } else if (parent->info.isDir()) {
+ //resolve directory links only if requested.
+ if (parent->info.isSymLink() && resolveSymlinks) {
+ QString link = parent->info.symLinkTarget();
+ if (link.size() > 1 && link.at(link.size() - 1) == QDir::separator())
+ link.chop(1);
+ if (stat)
+ infoList = entryInfoList(link);
+ else
+ infoList = QDir(link).entryInfoList(nameFilters, QDir::AllEntries | QDir::System);
+ } else {
+ if (stat)
+ infoList = entryInfoList(parent->info.absoluteFilePath());
+ else
+ infoList = QDir(parent->info.absoluteFilePath()).entryInfoList(nameFilters, QDir::AllEntries | QDir::System);
+ }
+ }
+
+ QVector<QDirNode> nodes(infoList.count());
+ for (int i = 0; i < infoList.count(); ++i) {
+ QDirNode &node = nodes[i];
+ node.parent = parent;
+ node.info = infoList.at(i);
+ node.populated = false;
+ node.stat = shouldStat;
+ }
+
+ return nodes;
+}
+
+void QDirModelPrivate::_q_refresh()
+{
+ Q_Q(QDirModel);
+ q->refresh(toBeRefreshed);
+ toBeRefreshed = QModelIndex();
+}
+
+void QDirModelPrivate::savePersistentIndexes()
+{
+ Q_Q(QDirModel);
+ savedPersistent.clear();
+ foreach (QPersistentModelIndexData *data, persistent.indexes) {
+ SavedPersistent saved;
+ QModelIndex index = data->index;
+ saved.path = q->filePath(index);
+ saved.column = index.column();
+ saved.data = data;
+ saved.index = index;
+ savedPersistent.append(saved);
+ }
+}
+
+void QDirModelPrivate::restorePersistentIndexes()
+{
+ Q_Q(QDirModel);
+ bool allow = allowAppendChild;
+ allowAppendChild = false;
+ for (int i = 0; i < savedPersistent.count(); ++i) {
+ QPersistentModelIndexData *data = savedPersistent.at(i).data;
+ QString path = savedPersistent.at(i).path;
+ int column = savedPersistent.at(i).column;
+ QModelIndex idx = q->index(path, column);
+ if (idx != data->index || data->model == 0) {
+ //data->model may be equal to 0 if the model is getting destroyed
+ persistent.indexes.remove(data->index);
+ data->index = idx;
+ data->model = q;
+ if (idx.isValid())
+ persistent.indexes.insert(idx, data);
+ }
+ }
+ savedPersistent.clear();
+ allowAppendChild = allow;
+}
+
+QFileInfoList QDirModelPrivate::entryInfoList(const QString &path) const
+{
+ const QDir dir(path);
+ return dir.entryInfoList(nameFilters, filters, sort);
+}
+
+QStringList QDirModelPrivate::entryList(const QString &path) const
+{
+ const QDir dir(path);
+ return dir.entryList(nameFilters, filters, sort);
+}
+
+QString QDirModelPrivate::name(const QModelIndex &index) const
+{
+ const QDirNode *n = node(index);
+ const QFileInfo info = n->info;
+ if (info.isRoot()) {
+ QString name = info.absoluteFilePath();
+#if defined(Q_OS_WIN) && !defined(Q_OS_WINCE)
+ if (name.startsWith(QLatin1Char('/'))) // UNC host
+ return info.fileName();
+ if (name.endsWith(QLatin1Char('/')))
+ name.chop(1);
+#endif
+ return name;
+ }
+ return info.fileName();
+}
+
+QString QDirModelPrivate::size(const QModelIndex &index) const
+{
+ const QDirNode *n = node(index);
+ if (n->info.isDir()) {
+#ifdef Q_OS_MAC
+ return QLatin1String("--");
+#else
+ return QLatin1String("");
+#endif
+ // Windows - ""
+ // OS X - "--"
+ // Konqueror - "4 KB"
+ // Nautilus - "9 items" (the number of children)
+ }
+
+ // According to the Si standard KB is 1000 bytes, KiB is 1024
+ // but on windows sizes are calulated by dividing by 1024 so we do what they do.
+ const quint64 kb = 1024;
+ const quint64 mb = 1024 * kb;
+ const quint64 gb = 1024 * mb;
+ const quint64 tb = 1024 * gb;
+ quint64 bytes = n->info.size();
+ if (bytes >= tb)
+ return QLocale().toString(bytes / tb) + QString::fromLatin1(" TB");
+ if (bytes >= gb)
+ return QLocale().toString(bytes / gb) + QString::fromLatin1(" GB");
+ if (bytes >= mb)
+ return QLocale().toString(bytes / mb) + QString::fromLatin1(" MB");
+ if (bytes >= kb)
+ return QLocale().toString(bytes / kb) + QString::fromLatin1(" KB");
+ return QLocale().toString(bytes) + QString::fromLatin1(" bytes");
+}
+
+QString QDirModelPrivate::type(const QModelIndex &index) const
+{
+ return iconProvider->type(node(index)->info);
+}
+
+QString QDirModelPrivate::time(const QModelIndex &index) const
+{
+#ifndef QT_NO_DATESTRING
+ return node(index)->info.lastModified().toString(Qt::LocalDate);
+#else
+ Q_UNUSED(index);
+ return QString();
+#endif
+}
+
+void QDirModelPrivate::appendChild(QDirModelPrivate::QDirNode *parent, const QString &path) const
+{
+ QDirModelPrivate::QDirNode node;
+ node.populated = false;
+ node.stat = shouldStat;
+ node.parent = (parent == &root ? 0 : parent);
+ node.info = QFileInfo(path);
+ node.info.setCaching(true);
+
+ // The following append(node) may reallocate the vector, thus
+ // we need to update the pointers to the childnodes parent.
+ QDirModelPrivate *that = const_cast<QDirModelPrivate *>(this);
+ that->savePersistentIndexes();
+ parent->children.append(node);
+ for (int i = 0; i < parent->children.count(); ++i) {
+ QDirNode *childNode = &parent->children[i];
+ for (int j = 0; j < childNode->children.count(); ++j)
+ childNode->children[j].parent = childNode;
+ }
+ that->restorePersistentIndexes();
+}
+
+QFileInfo QDirModelPrivate::resolvedInfo(QFileInfo info)
+{
+#ifdef Q_OS_WIN
+ // On windows, we cannot create a shortcut to a shortcut.
+ return QFileInfo(info.symLinkTarget());
+#else
+ QStringList paths;
+ do {
+ QFileInfo link(info.symLinkTarget());
+ if (link.isRelative())
+ info.setFile(info.absolutePath(), link.filePath());
+ else
+ info = link;
+ if (paths.contains(info.absoluteFilePath()))
+ return QFileInfo();
+ paths.append(info.absoluteFilePath());
+ } while (info.isSymLink());
+ return info;
+#endif
+}
+
+QT_END_NAMESPACE
+
+#include "moc_qdirmodel.cpp"
+
+#endif // QT_NO_DIRMODEL
diff --git a/src/gui/itemviews/qdirmodel.h b/src/gui/itemviews/qdirmodel.h
new file mode 100644
index 0000000000..ec1056f00b
--- /dev/null
+++ b/src/gui/itemviews/qdirmodel.h
@@ -0,0 +1,160 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QDIRMODEL_H
+#define QDIRMODEL_H
+
+#include <QtCore/qabstractitemmodel.h>
+#include <QtCore/qdir.h>
+#include <QtGui/qfileiconprovider.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+#ifndef QT_NO_DIRMODEL
+
+class QDirModelPrivate;
+
+class Q_GUI_EXPORT QDirModel : public QAbstractItemModel
+{
+ Q_OBJECT
+ Q_PROPERTY(bool resolveSymlinks READ resolveSymlinks WRITE setResolveSymlinks)
+ Q_PROPERTY(bool readOnly READ isReadOnly WRITE setReadOnly)
+ Q_PROPERTY(bool lazyChildCount READ lazyChildCount WRITE setLazyChildCount)
+
+public:
+ enum Roles {
+ FileIconRole = Qt::DecorationRole,
+ FilePathRole = Qt::UserRole + 1,
+ FileNameRole
+ };
+
+ QDirModel(const QStringList &nameFilters, QDir::Filters filters,
+ QDir::SortFlags sort, QObject *parent = 0);
+ explicit QDirModel(QObject *parent = 0);
+ ~QDirModel();
+
+ QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const;
+ QModelIndex parent(const QModelIndex &child) const;
+
+ int rowCount(const QModelIndex &parent = QModelIndex()) const;
+ int columnCount(const QModelIndex &parent = QModelIndex()) const;
+
+ QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
+ bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole);
+
+ QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;
+
+ bool hasChildren(const QModelIndex &index = QModelIndex()) const;
+ Qt::ItemFlags flags(const QModelIndex &index) const;
+
+ void sort(int column, Qt::SortOrder order = Qt::AscendingOrder);
+
+ QStringList mimeTypes() const;
+ QMimeData *mimeData(const QModelIndexList &indexes) const;
+ bool dropMimeData(const QMimeData *data, Qt::DropAction action,
+ int row, int column, const QModelIndex &parent);
+ Qt::DropActions supportedDropActions() const;
+
+ // QDirModel specific API
+
+ void setIconProvider(QFileIconProvider *provider);
+ QFileIconProvider *iconProvider() const;
+
+ void setNameFilters(const QStringList &filters);
+ QStringList nameFilters() const;
+
+ void setFilter(QDir::Filters filters);
+ QDir::Filters filter() const;
+
+ void setSorting(QDir::SortFlags sort);
+ QDir::SortFlags sorting() const;
+
+ void setResolveSymlinks(bool enable);
+ bool resolveSymlinks() const;
+
+ void setReadOnly(bool enable);
+ bool isReadOnly() const;
+
+ void setLazyChildCount(bool enable);
+ bool lazyChildCount() const;
+
+ QModelIndex index(const QString &path, int column = 0) const;
+
+ bool isDir(const QModelIndex &index) const;
+ QModelIndex mkdir(const QModelIndex &parent, const QString &name);
+ bool rmdir(const QModelIndex &index);
+ bool remove(const QModelIndex &index);
+
+ QString filePath(const QModelIndex &index) const;
+ QString fileName(const QModelIndex &index) const;
+ QIcon fileIcon(const QModelIndex &index) const;
+ QFileInfo fileInfo(const QModelIndex &index) const;
+
+#ifdef Q_NO_USING_KEYWORD
+ inline QObject *parent() const { return QObject::parent(); }
+#else
+ using QObject::parent;
+#endif
+
+public Q_SLOTS:
+ void refresh(const QModelIndex &parent = QModelIndex());
+
+protected:
+ QDirModel(QDirModelPrivate &, QObject *parent = 0);
+ friend class QFileDialogPrivate;
+
+private:
+ Q_DECLARE_PRIVATE(QDirModel)
+ Q_DISABLE_COPY(QDirModel)
+ Q_PRIVATE_SLOT(d_func(), void _q_refresh())
+};
+
+#endif // QT_NO_DIRMODEL
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QDIRMODEL_H
diff --git a/src/gui/itemviews/qfileiconprovider.cpp b/src/gui/itemviews/qfileiconprovider.cpp
new file mode 100644
index 0000000000..ac625515e1
--- /dev/null
+++ b/src/gui/itemviews/qfileiconprovider.cpp
@@ -0,0 +1,449 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qfileiconprovider.h"
+
+#ifndef QT_NO_FILEICONPROVIDER
+#include <qstyle.h>
+#include <qapplication.h>
+#include <qdir.h>
+#if defined(Q_WS_WIN)
+#define _WIN32_IE 0x0500
+#include <objbase.h>
+#include <private/qpixmapdata_p.h>
+#include <qpixmapcache.h>
+#elif defined(Q_WS_MAC)
+#include <private/qt_mac_p.h>
+#endif
+#include <private/qfunctions_p.h>
+#ifdef Q_OS_WINCE
+#include <Commctrl.h>
+#endif
+
+#ifndef SHGFI_ADDOVERLAYS
+#define SHGFI_ADDOVERLAYS 0x000000020
+#endif
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QFileIconProvider
+
+ \brief The QFileIconProvider class provides file icons for the QDirModel class.
+*/
+
+/*!
+ \enum QFileIconProvider::IconType
+ \value Computer
+ \value Desktop
+ \value Trashcan
+ \value Network
+ \value Drive
+ \value Folder
+ \value File
+*/
+
+class QFileIconProviderPrivate
+{
+ Q_DECLARE_PUBLIC(QFileIconProvider)
+
+public:
+ QFileIconProviderPrivate();
+ QIcon getIcon(QStyle::StandardPixmap name) const;
+#ifdef Q_WS_WIN
+ QIcon getWinIcon(const QFileInfo &fi) const;
+#elif defined(Q_WS_MAC)
+ QIcon getMacIcon(const QFileInfo &fi) const;
+#endif
+ QFileIconProvider *q_ptr;
+ QString homePath;
+
+private:
+ QIcon file;
+ QIcon fileLink;
+ QIcon directory;
+ QIcon directoryLink;
+ QIcon harddisk;
+ QIcon floppy;
+ QIcon cdrom;
+ QIcon ram;
+ QIcon network;
+ QIcon computer;
+ QIcon desktop;
+ QIcon trashcan;
+ QIcon generic;
+ QIcon home;
+};
+
+QFileIconProviderPrivate::QFileIconProviderPrivate()
+{
+ QStyle *style = QApplication::style();
+ file = style->standardIcon(QStyle::SP_FileIcon);
+ directory = style->standardIcon(QStyle::SP_DirIcon);
+ fileLink = style->standardIcon(QStyle::SP_FileLinkIcon);
+ directoryLink = style->standardIcon(QStyle::SP_DirLinkIcon);
+ harddisk = style->standardIcon(QStyle::SP_DriveHDIcon);
+ floppy = style->standardIcon(QStyle::SP_DriveFDIcon);
+ cdrom = style->standardIcon(QStyle::SP_DriveCDIcon);
+ network = style->standardIcon(QStyle::SP_DriveNetIcon);
+ computer = style->standardIcon(QStyle::SP_ComputerIcon);
+ desktop = style->standardIcon(QStyle::SP_DesktopIcon);
+ trashcan = style->standardIcon(QStyle::SP_TrashIcon);
+ home = style->standardIcon(QStyle::SP_DirHomeIcon);
+ homePath = QDir::home().absolutePath();
+}
+
+QIcon QFileIconProviderPrivate::getIcon(QStyle::StandardPixmap name) const
+{
+ switch (name) {
+ case QStyle::SP_FileIcon:
+ return file;
+ case QStyle::SP_FileLinkIcon:
+ return fileLink;
+ case QStyle::SP_DirIcon:
+ return directory;
+ case QStyle::SP_DirLinkIcon:
+ return directoryLink;
+ case QStyle::SP_DriveHDIcon:
+ return harddisk;
+ case QStyle::SP_DriveFDIcon:
+ return floppy;
+ case QStyle::SP_DriveCDIcon:
+ return cdrom;
+ case QStyle::SP_DriveNetIcon:
+ return network;
+ case QStyle::SP_ComputerIcon:
+ return computer;
+ case QStyle::SP_DesktopIcon:
+ return desktop;
+ case QStyle::SP_TrashIcon:
+ return trashcan;
+ case QStyle::SP_DirHomeIcon:
+ return home;
+ default:
+ return QIcon();
+ }
+ return QIcon();
+}
+
+/*!
+ Constructs a file icon provider.
+*/
+
+QFileIconProvider::QFileIconProvider()
+ : d_ptr(new QFileIconProviderPrivate)
+{
+}
+
+/*!
+ Destroys the file icon provider.
+
+*/
+
+QFileIconProvider::~QFileIconProvider()
+{
+ delete d_ptr;
+}
+
+/*!
+ Returns an icon set for the given \a type.
+*/
+
+QIcon QFileIconProvider::icon(IconType type) const
+{
+ Q_D(const QFileIconProvider);
+ switch (type) {
+ case Computer:
+ return d->getIcon(QStyle::SP_ComputerIcon);
+ case Desktop:
+ return d->getIcon(QStyle::SP_DesktopIcon);
+ case Trashcan:
+ return d->getIcon(QStyle::SP_TrashIcon);
+ case Network:
+ return d->getIcon(QStyle::SP_DriveNetIcon);
+ case Drive:
+ return d->getIcon(QStyle::SP_DriveHDIcon);
+ case Folder:
+ return d->getIcon(QStyle::SP_DirIcon);
+ case File:
+ return d->getIcon(QStyle::SP_FileIcon);
+ default:
+ break;
+ };
+ return QIcon();
+}
+
+#ifdef Q_WS_WIN
+QIcon QFileIconProviderPrivate::getWinIcon(const QFileInfo &fileInfo) const
+{
+ QIcon retIcon;
+ QString fileExtension = fileInfo.suffix().toUpper();
+ fileExtension.prepend( QLatin1String(".") );
+
+ QString key;
+ if (fileInfo.isFile() && !fileInfo.isExecutable() && !fileInfo.isSymLink())
+ key = QLatin1String("qt_") + fileExtension;
+
+ QPixmap pixmap;
+ if (!key.isEmpty()) {
+ QPixmapCache::find(key, pixmap);
+ }
+
+ if (!pixmap.isNull()) {
+ retIcon.addPixmap(pixmap);
+ if (QPixmapCache::find(key + QLatin1Char('l'), pixmap))
+ retIcon.addPixmap(pixmap);
+ return retIcon;
+ }
+
+ /* We don't use the variable, but by storing it statically, we
+ * ensure CoInitialize is only called once. */
+ static HRESULT comInit = CoInitialize(NULL);
+ Q_UNUSED(comInit);
+
+ SHFILEINFO info;
+ unsigned long val = 0;
+
+ //Get the small icon
+#ifndef Q_OS_WINCE
+ val = SHGetFileInfo((const WCHAR *)QDir::toNativeSeparators(fileInfo.filePath()).utf16(), 0, &info,
+ sizeof(SHFILEINFO), SHGFI_ICON|SHGFI_SMALLICON|SHGFI_SYSICONINDEX|SHGFI_ADDOVERLAYS);
+#else
+ val = SHGetFileInfo((const WCHAR *)QDir::toNativeSeparators(fileInfo.filePath()).utf16(), 0, &info,
+ sizeof(SHFILEINFO), SHGFI_SMALLICON|SHGFI_SYSICONINDEX);
+#endif
+ if (val) {
+ if (fileInfo.isDir() && !fileInfo.isRoot()) {
+ //using the unique icon index provided by windows save us from duplicate keys
+ key = QString::fromLatin1("qt_dir_%1").arg(info.iIcon);
+ QPixmapCache::find(key, pixmap);
+ if (!pixmap.isNull()) {
+ retIcon.addPixmap(pixmap);
+ if (QPixmapCache::find(key + QLatin1Char('l'), pixmap))
+ retIcon.addPixmap(pixmap);
+ DestroyIcon(info.hIcon);
+ return retIcon;
+ }
+ }
+ if (pixmap.isNull()) {
+#ifndef Q_OS_WINCE
+ pixmap = convertHIconToPixmap(info.hIcon);
+#else
+ pixmap = convertHIconToPixmap(ImageList_GetIcon((HIMAGELIST) val, info.iIcon, ILD_NORMAL));
+#endif
+ if (!pixmap.isNull()) {
+ retIcon.addPixmap(pixmap);
+ if (!key.isEmpty())
+ QPixmapCache::insert(key, pixmap);
+ }
+ else {
+ qWarning("QFileIconProviderPrivate::getWinIcon() no small icon found");
+ }
+ }
+ DestroyIcon(info.hIcon);
+ }
+
+ //Get the big icon
+#ifndef Q_OS_WINCE
+ val = SHGetFileInfo((const WCHAR *)QDir::toNativeSeparators(fileInfo.filePath()).utf16(), 0, &info,
+ sizeof(SHFILEINFO), SHGFI_ICON|SHGFI_LARGEICON|SHGFI_SYSICONINDEX|SHGFI_ADDOVERLAYS);
+#else
+ val = SHGetFileInfo((const WCHAR *)QDir::toNativeSeparators(fileInfo.filePath()).utf16(), 0, &info,
+ sizeof(SHFILEINFO), SHGFI_LARGEICON|SHGFI_SYSICONINDEX);
+#endif
+ if (val) {
+ if (fileInfo.isDir() && !fileInfo.isRoot()) {
+ //using the unique icon index provided by windows save us from duplicate keys
+ key = QString::fromLatin1("qt_dir_%1").arg(info.iIcon);
+ }
+#ifndef Q_OS_WINCE
+ pixmap = convertHIconToPixmap(info.hIcon);
+#else
+ pixmap = convertHIconToPixmap(ImageList_GetIcon((HIMAGELIST) val, info.iIcon, ILD_NORMAL), true);
+#endif
+ if (!pixmap.isNull()) {
+ retIcon.addPixmap(pixmap);
+ if (!key.isEmpty())
+ QPixmapCache::insert(key + QLatin1Char('l'), pixmap);
+ }
+ else {
+ qWarning("QFileIconProviderPrivate::getWinIcon() no large icon found");
+ }
+ DestroyIcon(info.hIcon);
+ }
+ return retIcon;
+}
+
+#elif defined(Q_WS_MAC)
+QIcon QFileIconProviderPrivate::getMacIcon(const QFileInfo &fi) const
+{
+ QIcon retIcon;
+ FSRef macRef;
+ OSStatus status = FSPathMakeRef(reinterpret_cast<const UInt8*>(fi.canonicalFilePath().toUtf8().constData()),
+ &macRef, 0);
+ if (status != noErr)
+ return retIcon;
+ FSCatalogInfo info;
+ HFSUniStr255 macName;
+ status = FSGetCatalogInfo(&macRef, kIconServicesCatalogInfoMask, &info, &macName, 0, 0);
+ if (status != noErr)
+ return retIcon;
+ IconRef iconRef;
+ SInt16 iconLabel;
+ status = GetIconRefFromFileInfo(&macRef, macName.length, macName.unicode, kIconServicesCatalogInfoMask, &info, kIconServicesNormalUsageFlag, &iconRef, &iconLabel);
+ if (status != noErr)
+ return retIcon;
+ extern void qt_mac_constructQIconFromIconRef(const IconRef, const IconRef, QIcon*, QStyle::StandardPixmap = QStyle::SP_CustomBase); // qmacstyle_mac.cpp
+ qt_mac_constructQIconFromIconRef(iconRef, 0, &retIcon);
+ ReleaseIconRef(iconRef);
+ return retIcon;
+}
+#endif
+
+
+/*!
+ Returns an icon for the file described by \a info.
+*/
+
+QIcon QFileIconProvider::icon(const QFileInfo &info) const
+{
+ Q_D(const QFileIconProvider);
+#ifdef Q_WS_MAC
+ QIcon retIcon = d->getMacIcon(info);
+ if (!retIcon.isNull())
+ return retIcon;
+#elif defined Q_WS_WIN
+ QIcon icon = d->getWinIcon(info);
+ if (!icon.isNull())
+ return icon;
+#endif
+ if (info.isRoot())
+#if defined (Q_WS_WIN) && !defined(Q_OS_WINCE)
+ {
+ uint type = DRIVE_UNKNOWN;
+ QT_WA({ type = GetDriveTypeW((wchar_t *)info.absoluteFilePath().utf16()); },
+ { type = GetDriveTypeA(info.absoluteFilePath().toLocal8Bit()); });
+
+ switch (type) {
+ case DRIVE_REMOVABLE:
+ return d->getIcon(QStyle::SP_DriveFDIcon);
+ case DRIVE_FIXED:
+ return d->getIcon(QStyle::SP_DriveHDIcon);
+ case DRIVE_REMOTE:
+ return d->getIcon(QStyle::SP_DriveNetIcon);
+ case DRIVE_CDROM:
+ return d->getIcon(QStyle::SP_DriveCDIcon);
+ case DRIVE_RAMDISK:
+ case DRIVE_UNKNOWN:
+ case DRIVE_NO_ROOT_DIR:
+ default:
+ return d->getIcon(QStyle::SP_DriveHDIcon);
+ }
+ }
+#else
+ return d->getIcon(QStyle::SP_DriveHDIcon);
+#endif
+ if (info.isFile()) {
+ if (info.isSymLink())
+ return d->getIcon(QStyle::SP_FileLinkIcon);
+ else
+ return d->getIcon(QStyle::SP_FileIcon);
+ }
+ if (info.isDir()) {
+ if (info.isSymLink()) {
+ return d->getIcon(QStyle::SP_DirLinkIcon);
+ } else {
+ if (info.absoluteFilePath() == d->homePath) {
+ return d->getIcon(QStyle::SP_DirHomeIcon);
+ } else {
+ return d->getIcon(QStyle::SP_DirIcon);
+ }
+ }
+ }
+ return QIcon();
+}
+
+/*!
+ Returns the type of the file described by \a info.
+*/
+
+QString QFileIconProvider::type(const QFileInfo &info) const
+{
+ if (info.isRoot())
+ return QApplication::translate("QFileDialog", "Drive");
+ if (info.isFile()) {
+ if (!info.suffix().isEmpty())
+ return info.suffix() + QLatin1Char(' ') + QApplication::translate("QFileDialog", "File");
+ return QApplication::translate("QFileDialog", "File");
+ }
+
+ if (info.isDir())
+ return QApplication::translate("QFileDialog",
+#ifdef Q_WS_WIN
+ "File Folder", "Match Windows Explorer"
+#else
+ "Folder", "All other platforms"
+#endif
+ );
+ // Windows - "File Folder"
+ // OS X - "Folder"
+ // Konqueror - "Folder"
+ // Nautilus - "folder"
+
+ if (info.isSymLink())
+ return QApplication::translate("QFileDialog",
+#ifdef Q_OS_MAC
+ "Alias", "Mac OS X Finder"
+#else
+ "Shortcut", "All other platforms"
+#endif
+ );
+ // OS X - "Alias"
+ // Windows - "Shortcut"
+ // Konqueror - "Folder" or "TXT File" i.e. what it is pointing to
+ // Nautilus - "link to folder" or "link to object file", same as Konqueror
+
+ return QApplication::translate("QFileDialog", "Unknown");
+}
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/gui/itemviews/qfileiconprovider.h b/src/gui/itemviews/qfileiconprovider.h
new file mode 100644
index 0000000000..7928552625
--- /dev/null
+++ b/src/gui/itemviews/qfileiconprovider.h
@@ -0,0 +1,81 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QFILEICONPROVIDER_H
+#define QFILEICONPROVIDER_H
+
+#include <QtGui/qicon.h>
+#include <QtCore/qfileinfo.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+#ifndef QT_NO_FILEICONPROVIDER
+
+class QFileIconProviderPrivate;
+
+class Q_GUI_EXPORT QFileIconProvider
+{
+public:
+ QFileIconProvider();
+ virtual ~QFileIconProvider();
+ enum IconType { Computer, Desktop, Trashcan, Network, Drive, Folder, File };
+ virtual QIcon icon(IconType type) const;
+ virtual QIcon icon(const QFileInfo &info) const;
+ virtual QString type(const QFileInfo &info) const;
+
+private:
+ Q_DECLARE_PRIVATE(QFileIconProvider)
+ QFileIconProviderPrivate *d_ptr;
+ Q_DISABLE_COPY(QFileIconProvider)
+};
+
+#endif // QT_NO_FILEICONPROVIDER
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QFILEICONPROVIDER_H
+
diff --git a/src/gui/itemviews/qheaderview.cpp b/src/gui/itemviews/qheaderview.cpp
new file mode 100644
index 0000000000..5bd82d41e1
--- /dev/null
+++ b/src/gui/itemviews/qheaderview.cpp
@@ -0,0 +1,3558 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qheaderview.h"
+
+#ifndef QT_NO_ITEMVIEWS
+#include <qbitarray.h>
+#include <qbrush.h>
+#include <qdebug.h>
+#include <qevent.h>
+#include <qpainter.h>
+#include <qscrollbar.h>
+#include <qtooltip.h>
+#include <qwhatsthis.h>
+#include <qstyle.h>
+#include <qstyleoption.h>
+#include <qvector.h>
+#include <qapplication.h>
+#include <qvarlengtharray.h>
+#include <qabstractitemdelegate.h>
+#include <qvariant.h>
+#include <private/qheaderview_p.h>
+#include <private/qabstractitemmodel_p.h>
+
+#ifndef QT_NO_DATASTREAM
+#include <qdatastream.h>
+
+QT_BEGIN_NAMESPACE
+
+QDataStream &operator<<(QDataStream &out, const QHeaderViewPrivate::SectionSpan &span)
+{
+ span.write(out);
+ return out;
+}
+
+QDataStream &operator>>(QDataStream &in, QHeaderViewPrivate::SectionSpan &span)
+{
+ span.read(in);
+ return in;
+}
+#endif
+
+
+/*!
+ \class QHeaderView
+
+ \brief The QHeaderView class provides a header row or header column for
+ item views.
+
+ \ingroup model-view
+ \mainclass
+
+ A QHeaderView displays the headers used in item views such as the
+ QTableView and QTreeView classes. It takes the place of Qt3's \c QHeader
+ class previously used for the same purpose, but uses the Qt's model/view
+ architecture for consistency with the item view classes.
+
+ The QHeaderView class is one of the \l{Model/View Classes} and is part of
+ Qt's \l{Model/View Programming}{model/view framework}.
+
+ The header gets the data for each section from the model using the
+ QAbstractItemModel::headerData() function. You can set the data by using
+ QAbstractItemModel::setHeaderData().
+
+ Each header has an orientation() and a number of sections, given by the
+ count() function. A section refers to a part of the header - either a row
+ or a column, depending on the orientation.
+
+ Sections can be moved and resized using moveSection() and resizeSection();
+ they can also be hidden and shown with hideSection() and showSection().
+
+ Each section of a header is described by a section ID, specified by its
+ section(), and can be located at a particular visualIndex() in the header.
+ A section can have a sort indicator set with setSortIndicator(); this
+ indicates whether the items in the associated item view will be sorted in
+ the order given by the section.
+
+ For a horizontal header the section is equivalent to a column in the model,
+ and for a vertical header the section is equivalent to a row in the model.
+
+ \section1 Moving Header Sections
+
+ A header can be fixed in place, or made movable with setMovable(). It can
+ be made clickable with setClickable(), and has resizing behavior in
+ accordance with setResizeMode().
+
+ \note Double-clicking on a header to resize a section only applies for
+ visible rows.
+
+ A header will emit sectionMoved() if the user moves a section,
+ sectionResized() if the user resizes a section, and sectionClicked() as
+ well as sectionHandleDoubleClicked() in response to mouse clicks. A header
+ will also emit sectionCountChanged() and sectionAutoResize().
+
+ You can identify a section using the logicalIndex() and logicalIndexAt()
+ functions, or by its index position, using the visualIndex() and
+ visualIndexAt() functions. The visual index will change if a section is
+ moved, but the logical index will not change.
+
+ \section1 Appearance
+
+ QTableWidget and QTableView create default headers. If you want
+ the headers to be visible, you can use \l{QFrame::}{setVisible()}.
+
+ Not all \l{Qt::}{ItemDataRole}s will have an effect on a
+ QHeaderView. If you need to draw other roles, you can subclass
+ QHeaderView and reimplement \l{QHeaderView::}{paintEvent()}.
+ QHeaderView respects the following item data roles:
+ \l{Qt::}{TextAlignmentRole}, \l{Qt::}{DisplayRole},
+ \l{Qt::}{FontRole}, \l{Qt::}{DecorationRole},
+ \l{Qt::}{ForegroundRole}, and \l{Qt::}{BackgroundRole}.
+
+ \note Each header renders the data for each section itself, and does not
+ rely on a delegate. As a result, calling a header's setItemDelegate()
+ function will have no effect.
+
+ \sa {Model/View Programming}, QListView, QTableView, QTreeView
+*/
+
+/*!
+ \enum QHeaderView::ResizeMode
+
+ The resize mode specifies the behavior of the header sections. It can be
+ set on the entire header view or on individual sections using
+ setResizeMode().
+
+ \value Interactive The user can resize the section. The section can also be
+ resized programmatically using resizeSection(). The section size
+ defaults to \l defaultSectionSize. (See also
+ \l cascadingSectionResizes.)
+
+ \value Fixed The user cannot resize the section. The section can only be
+ resized programmatically using resizeSection(). The section size
+ defaults to \l defaultSectionSize.
+
+ \value Stretch QHeaderView will automatically resize the section to fill
+ the available space. The size cannot be changed by the user or
+ programmatically.
+
+ \value ResizeToContents QHeaderView will automatically resize the section
+ to its optimal size based on the contents of the entire column or
+ row. The size cannot be changed by the user or programmatically.
+ (This value was introduced in 4.2)
+
+ The following values are obsolete:
+ \value Custom Use Fixed instead.
+
+ \sa setResizeMode() stretchLastSection minimumSectionSize
+*/
+
+/*!
+ \fn void QHeaderView::sectionMoved(int logicalIndex, int oldVisualIndex,
+ int newVisualIndex)
+
+ This signal is emitted when a section is moved. The section's logical index
+ is specified by \a logicalIndex, the old index by \a oldVisualIndex, and
+ the new index position by \a newVisualIndex.
+
+ \sa moveSection()
+*/
+
+/*!
+ \fn void QHeaderView::sectionResized(int logicalIndex, int oldSize,
+ int newSize)
+
+ This signal is emitted when a section is resized. The section's logical
+ number is specified by \a logicalIndex, the old size by \a oldSize, and the
+ new size by \a newSize.
+
+ \sa resizeSection()
+*/
+
+/*!
+ \fn void QHeaderView::sectionPressed(int logicalIndex)
+
+ This signal is emitted when a section is pressed. The section's logical
+ index is specified by \a logicalIndex.
+
+ \sa setClickable()
+*/
+
+/*!
+ \fn void QHeaderView::sectionClicked(int logicalIndex)
+
+ This signal is emitted when a section is clicked. The section's logical
+ index is specified by \a logicalIndex.
+
+ Note that the sectionPressed signal will also be emitted.
+
+ \sa setClickable(), sectionPressed()
+*/
+
+/*!
+ \fn void QHeaderView::sectionEntered(int logicalIndex)
+ \since 4.3
+
+ This signal is emitted when the cursor moves over the section and the left
+ mouse button is pressed. The section's logical index is specified by
+ \a logicalIndex.
+
+ \sa setClickable(), sectionPressed()
+*/
+
+/*!
+ \fn void QHeaderView::sectionDoubleClicked(int logicalIndex)
+
+ This signal is emitted when a section is double-clicked. The section's
+ logical index is specified by \a logicalIndex.
+
+ \sa setClickable()
+*/
+
+/*!
+ \fn void QHeaderView::sectionCountChanged(int oldCount, int newCount)
+
+ This signal is emitted when the number of sections changes, i.e., when
+ sections are added or deleted. The original count is specified by
+ \a oldCount, and the new count by \a newCount.
+
+ \sa count(), length(), headerDataChanged()
+*/
+
+/*!
+ \fn void QHeaderView::sectionHandleDoubleClicked(int logicalIndex)
+
+ This signal is emitted when a section is double-clicked. The section's
+ logical index is specified by \a logicalIndex.
+
+ \sa setClickable()
+*/
+
+/*!
+ \fn void QHeaderView::sortIndicatorChanged(int logicalIndex,
+ Qt::SortOrder order)
+ \since 4.3
+
+ This signal is emitted when the section containing the sort indicator or
+ the order indicated is changed. The section's logical index is specified
+ by \a logicalIndex and the sort order is specified by \a order.
+
+ \sa setSortIndicator()
+*/
+
+/*!
+ \fn void QHeaderView::sectionAutoResize(int logicalIndex,
+ QHeaderView::ResizeMode mode)
+
+ This signal is emitted when a section is automatically resized. The
+ section's logical index is specified by \a logicalIndex, and the resize
+ mode by \a mode.
+
+ \sa setResizeMode(), stretchLastSection()
+*/
+// ### Qt 5: change to sectionAutoResized()
+
+/*!
+ \fn void QHeaderView::geometriesChanged()
+ \since 4.2
+
+ This signal is emitted when the header's geometries have changed.
+*/
+
+/*!
+ \property QHeaderView::highlightSections
+ \brief whether the sections containing selected items are highlighted
+
+ By default, this property is false.
+*/
+
+/*!
+ Creates a new generic header with the given \a orientation and \a parent.
+*/
+QHeaderView::QHeaderView(Qt::Orientation orientation, QWidget *parent)
+ : QAbstractItemView(*new QHeaderViewPrivate, parent)
+{
+ Q_D(QHeaderView);
+ d->setDefaultValues(orientation);
+ initialize();
+}
+
+/*!
+ \internal
+*/
+QHeaderView::QHeaderView(QHeaderViewPrivate &dd,
+ Qt::Orientation orientation, QWidget *parent)
+ : QAbstractItemView(dd, parent)
+{
+ Q_D(QHeaderView);
+ d->setDefaultValues(orientation);
+ initialize();
+}
+
+/*!
+ Destroys the header.
+*/
+
+QHeaderView::~QHeaderView()
+{
+}
+
+/*!
+ \internal
+*/
+void QHeaderView::initialize()
+{
+ Q_D(QHeaderView);
+ setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
+ setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
+ setFrameStyle(NoFrame);
+ setFocusPolicy(Qt::NoFocus);
+ d->viewport->setMouseTracking(true);
+ d->viewport->setBackgroundRole(QPalette::Button);
+ d->textElideMode = Qt::ElideNone;
+ delete d->itemDelegate;
+}
+
+/*!
+ \reimp
+*/
+void QHeaderView::setModel(QAbstractItemModel *model)
+{
+ if (model == this->model())
+ return;
+ Q_D(QHeaderView);
+ if (d->model && d->model != QAbstractItemModelPrivate::staticEmptyModel()) {
+ if (d->orientation == Qt::Horizontal) {
+ QObject::disconnect(d->model, SIGNAL(columnsInserted(QModelIndex,int,int)),
+ this, SLOT(sectionsInserted(QModelIndex,int,int)));
+ QObject::disconnect(d->model, SIGNAL(columnsAboutToBeRemoved(QModelIndex,int,int)),
+ this, SLOT(sectionsAboutToBeRemoved(QModelIndex,int,int)));
+ QObject::disconnect(d->model, SIGNAL(columnsRemoved(QModelIndex,int,int)),
+ this, SLOT(_q_sectionsRemoved(QModelIndex,int,int)));
+ } else {
+ QObject::disconnect(d->model, SIGNAL(rowsInserted(QModelIndex,int,int)),
+ this, SLOT(sectionsInserted(QModelIndex,int,int)));
+ QObject::disconnect(d->model, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)),
+ this, SLOT(sectionsAboutToBeRemoved(QModelIndex,int,int)));
+ QObject::disconnect(d->model, SIGNAL(rowsRemoved(QModelIndex,int,int)),
+ this, SLOT(_q_sectionsRemoved(QModelIndex,int,int)));
+ }
+ QObject::disconnect(d->model, SIGNAL(headerDataChanged(Qt::Orientation,int,int)),
+ this, SLOT(headerDataChanged(Qt::Orientation,int,int)));
+ QObject::disconnect(d->model, SIGNAL(layoutAboutToBeChanged()),
+ this, SLOT(_q_layoutAboutToBeChanged()));
+ }
+
+ if (model && model != QAbstractItemModelPrivate::staticEmptyModel()) {
+ if (d->orientation == Qt::Horizontal) {
+ QObject::connect(model, SIGNAL(columnsInserted(QModelIndex,int,int)),
+ this, SLOT(sectionsInserted(QModelIndex,int,int)));
+ QObject::connect(model, SIGNAL(columnsAboutToBeRemoved(QModelIndex,int,int)),
+ this, SLOT(sectionsAboutToBeRemoved(QModelIndex,int,int)));
+ QObject::connect(model, SIGNAL(columnsRemoved(QModelIndex,int,int)),
+ this, SLOT(_q_sectionsRemoved(QModelIndex,int,int)));
+ } else {
+ QObject::connect(model, SIGNAL(rowsInserted(QModelIndex,int,int)),
+ this, SLOT(sectionsInserted(QModelIndex,int,int)));
+ QObject::connect(model, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)),
+ this, SLOT(sectionsAboutToBeRemoved(QModelIndex,int,int)));
+ QObject::connect(model, SIGNAL(rowsRemoved(QModelIndex,int,int)),
+ this, SLOT(_q_sectionsRemoved(QModelIndex,int,int)));
+ }
+ QObject::connect(model, SIGNAL(headerDataChanged(Qt::Orientation,int,int)),
+ this, SLOT(headerDataChanged(Qt::Orientation,int,int)));
+ QObject::connect(model, SIGNAL(layoutAboutToBeChanged()),
+ this, SLOT(_q_layoutAboutToBeChanged()));
+ }
+
+ d->state = QHeaderViewPrivate::NoClear;
+ QAbstractItemView::setModel(model);
+ d->state = QHeaderViewPrivate::NoState;
+
+ // Users want to set sizes and modes before the widget is shown.
+ // Thus, we have to initialize when the model is set,
+ // and not lazily like we do in the other views.
+ initializeSections();
+}
+
+/*!
+ Returns the orientation of the header.
+
+ \sa Qt::Orientation
+*/
+
+Qt::Orientation QHeaderView::orientation() const
+{
+ Q_D(const QHeaderView);
+ return d->orientation;
+}
+
+/*!
+ Returns the offset of the header: this is the header's left-most (or
+ top-most for vertical headers) visible pixel.
+
+ \sa setOffset()
+*/
+
+int QHeaderView::offset() const
+{
+ Q_D(const QHeaderView);
+ return d->offset;
+}
+
+/*!
+ \fn void QHeaderView::setOffset(int offset)
+
+ Sets the header's offset to \a offset.
+
+ \sa offset(), length()
+*/
+
+void QHeaderView::setOffset(int newOffset)
+{
+ Q_D(QHeaderView);
+ if (d->offset == (int)newOffset)
+ return;
+ int ndelta = d->offset - newOffset;
+ d->offset = newOffset;
+ if (d->orientation == Qt::Horizontal)
+ d->viewport->scroll(isRightToLeft() ? -ndelta : ndelta, 0);
+ else
+ d->viewport->scroll(0, ndelta);
+ if (d->state == QHeaderViewPrivate::ResizeSection) {
+ QPoint cursorPos = QCursor::pos();
+ if (d->orientation == Qt::Horizontal)
+ QCursor::setPos(cursorPos.x() + ndelta, cursorPos.y());
+ else
+ QCursor::setPos(cursorPos.x(), cursorPos.y() + ndelta);
+ d->firstPos += ndelta;
+ d->lastPos += ndelta;
+ }
+}
+
+/*!
+ \since 4.2
+ Sets the offset to the start of the section at the given \a visualIndex.
+
+ \sa setOffset(), sectionPosition()
+*/
+void QHeaderView::setOffsetToSectionPosition(int visualIndex)
+{
+ Q_D(QHeaderView);
+ if (visualIndex > -1 && visualIndex < d->sectionCount) {
+ int position = d->headerSectionPosition(d->adjustedVisualIndex(visualIndex));
+ setOffset(position);
+ }
+}
+
+/*!
+ \since 4.2
+ Sets the offset to make the last section visible.
+
+ \sa setOffset(), sectionPosition(), setOffsetToSectionPosition()
+*/
+void QHeaderView::setOffsetToLastSection()
+{
+ Q_D(const QHeaderView);
+ int size = (d->orientation == Qt::Horizontal ? viewport()->width() : viewport()->height());
+ int position = length() - size;
+ setOffset(position);
+}
+
+/*!
+ Returns the length along the orientation of the header.
+
+ \sa sizeHint(), setResizeMode(), offset()
+*/
+
+int QHeaderView::length() const
+{
+ Q_D(const QHeaderView);
+ //Q_ASSERT(d->headerLength() == d->length);
+ return d->length;
+}
+
+/*!
+ Returns a suitable size hint for this header.
+
+ \sa sectionSizeHint()
+*/
+
+QSize QHeaderView::sizeHint() const
+{
+ Q_D(const QHeaderView);
+ if (count() < 1)
+ return QSize(0, 0);
+ if (d->cachedSizeHint.isValid())
+ return d->cachedSizeHint;
+ int width = 0;
+ int height = 0;
+ // get size hint for the first n sections
+ int c = qMin(count(), 100);
+ for (int i = 0; i < c; ++i) {
+ if (isSectionHidden(i))
+ continue;
+ QSize hint = sectionSizeFromContents(i);
+ width = qMax(hint.width(), width);
+ height = qMax(hint.height(), height);
+ }
+ // get size hint for the last n sections
+ c = qMax(count() - 100, c);
+ for (int j = count() - 1; j >= c; --j) {
+ if (isSectionHidden(j))
+ continue;
+ QSize hint = sectionSizeFromContents(j);
+ width = qMax(hint.width(), width);
+ height = qMax(hint.height(), height);
+ }
+ d->cachedSizeHint = QSize(width, height);
+ return d->cachedSizeHint;
+}
+
+/*!
+ Returns a suitable size hint for the section specified by \a logicalIndex.
+
+ \sa sizeHint(), defaultSectionSize(), minimumSectionSize(),
+ Qt::SizeHintRole
+*/
+
+int QHeaderView::sectionSizeHint(int logicalIndex) const
+{
+ Q_D(const QHeaderView);
+ if (isSectionHidden(logicalIndex))
+ return 0;
+ if (logicalIndex < 0 || logicalIndex >= count())
+ return -1;
+ QSize size;
+ QVariant value = d->model->headerData(logicalIndex, d->orientation, Qt::SizeHintRole);
+ if (value.isValid())
+ size = qvariant_cast<QSize>(value);
+ else
+ size = sectionSizeFromContents(logicalIndex);
+ int hint = d->orientation == Qt::Horizontal ? size.width() : size.height();
+ return qMax(minimumSectionSize(), hint);
+}
+
+/*!
+ Returns the visual index of the section that covers the given \a position
+ in the viewport.
+
+ \sa logicalIndexAt()
+*/
+
+int QHeaderView::visualIndexAt(int position) const
+{
+ Q_D(const QHeaderView);
+ int vposition = position;
+ d->executePostedLayout();
+ d->executePostedResize();
+ const int count = d->sectionCount;
+ if (count < 1)
+ return -1;
+
+ if (d->reverse())
+ vposition = d->viewport->width() - vposition;
+ vposition += d->offset;
+
+ if (vposition > d->length)
+ return -1;
+ int visual = d->headerVisualIndexAt(vposition);
+ if (visual < 0)
+ return -1;
+
+ while (d->isVisualIndexHidden(visual)){
+ ++visual;
+ if (visual >= count)
+ return -1;
+ }
+ return visual;
+}
+
+/*!
+ Returns the section that covers the given \a position in the viewport.
+
+ \sa visualIndexAt(), isSectionHidden()
+*/
+
+int QHeaderView::logicalIndexAt(int position) const
+{
+ const int visual = visualIndexAt(position);
+ if (visual > -1)
+ return logicalIndex(visual);
+ return -1;
+}
+
+/*!
+ Returns the width (or height for vertical headers) of the given
+ \a logicalIndex.
+
+ \sa length(), setResizeMode(), defaultSectionSize()
+*/
+
+int QHeaderView::sectionSize(int logicalIndex) const
+{
+ Q_D(const QHeaderView);
+ if (isSectionHidden(logicalIndex))
+ return 0;
+ if (logicalIndex < 0 || logicalIndex >= count())
+ return 0;
+ int visual = visualIndex(logicalIndex);
+ if (visual == -1)
+ return 0;
+ d->executePostedResize();
+ return d->headerSectionSize(visual);
+}
+
+/*!
+ Returns the section position of the given \a logicalIndex, or -1 if the
+ section is hidden.
+
+ \sa sectionViewportPosition()
+*/
+
+int QHeaderView::sectionPosition(int logicalIndex) const
+{
+ Q_D(const QHeaderView);
+ int visual = visualIndex(logicalIndex);
+ // in some cases users may change the selections
+ // before we have a chance to do the layout
+ if (visual == -1)
+ return -1;
+ d->executePostedResize();
+ return d->headerSectionPosition(visual);
+}
+
+/*!
+ Returns the section viewport position of the given \a logicalIndex.
+
+ If the section is hidden, the return value is undefined.
+
+ \sa sectionPosition(), isSectionHidden()
+*/
+
+int QHeaderView::sectionViewportPosition(int logicalIndex) const
+{
+ Q_D(const QHeaderView);
+ if (logicalIndex >= count())
+ return -1;
+ int position = sectionPosition(logicalIndex);
+ if (position < 0)
+ return position; // the section was hidden
+ int offsetPosition = position - d->offset;
+ if (d->reverse())
+ return d->viewport->width() - (offsetPosition + sectionSize(logicalIndex));
+ return offsetPosition;
+}
+
+/*!
+ \fn int QHeaderView::logicalIndexAt(int x, int y) const
+
+ Returns the logical index of the section at the given coordinate. If the
+ header is horizontal \a x will be used, otherwise \a y will be used to
+ find the logical index.
+*/
+
+/*!
+ \fn int QHeaderView::logicalIndexAt(const QPoint &pos) const
+
+ Returns the logical index of the section at the position given in \a pos.
+ If the header is horizontal the x-coordinate will be used, otherwise the
+ y-coordinate will be used to find the logical index.
+
+ \sa sectionPosition()
+*/
+
+/*!
+ Moves the section at visual index \a from to occupy visual index \a to.
+
+ \sa sectionsMoved()
+*/
+
+void QHeaderView::moveSection(int from, int to)
+{
+ Q_D(QHeaderView);
+
+ d->executePostedLayout();
+ if (from < 0 || from >= d->sectionCount || to < 0 || to >= d->sectionCount)
+ return;
+
+ if (from == to) {
+ int logical = logicalIndex(from);
+ Q_ASSERT(logical != -1);
+ updateSection(logical);
+ return;
+ }
+
+ if (stretchLastSection() && to == d->lastVisibleVisualIndex())
+ d->lastSectionSize = sectionSize(from);
+
+ //int oldHeaderLength = length(); // ### for debugging; remove later
+ d->initializeIndexMapping();
+
+ QBitArray sectionHidden = d->sectionHidden;
+ int *visualIndices = d->visualIndices.data();
+ int *logicalIndices = d->logicalIndices.data();
+ int logical = logicalIndices[from];
+ int visual = from;
+
+ int affected_count = qAbs(to - from) + 1;
+ QVarLengthArray<int> sizes(affected_count);
+ QVarLengthArray<ResizeMode> modes(affected_count);
+
+ // move sections and indices
+ if (to > from) {
+ sizes[to - from] = d->headerSectionSize(from);
+ modes[to - from] = d->headerSectionResizeMode(from);
+ while (visual < to) {
+ sizes[visual - from] = d->headerSectionSize(visual + 1);
+ modes[visual - from] = d->headerSectionResizeMode(visual + 1);
+ if (!sectionHidden.isEmpty())
+ sectionHidden.setBit(visual, sectionHidden.testBit(visual + 1));
+ visualIndices[logicalIndices[visual + 1]] = visual;
+ logicalIndices[visual] = logicalIndices[visual + 1];
+ ++visual;
+ }
+ } else {
+ sizes[0] = d->headerSectionSize(from);
+ modes[0] = d->headerSectionResizeMode(from);
+ while (visual > to) {
+ sizes[visual - to] = d->headerSectionSize(visual - 1);
+ modes[visual - to] = d->headerSectionResizeMode(visual - 1);
+ if (!sectionHidden.isEmpty())
+ sectionHidden.setBit(visual, sectionHidden.testBit(visual - 1));
+ visualIndices[logicalIndices[visual - 1]] = visual;
+ logicalIndices[visual] = logicalIndices[visual - 1];
+ --visual;
+ }
+ }
+ if (!sectionHidden.isEmpty()) {
+ sectionHidden.setBit(to, d->sectionHidden.testBit(from));
+ d->sectionHidden = sectionHidden;
+ }
+ visualIndices[logical] = to;
+ logicalIndices[to] = logical;
+
+ //Q_ASSERT(oldHeaderLength == length());
+ // move sizes
+ // ### check for spans of section sizes here
+ if (to > from) {
+ for (visual = from; visual <= to; ++visual) {
+ int size = sizes[visual - from];
+ ResizeMode mode = modes[visual - from];
+ d->createSectionSpan(visual, visual, size, mode);
+ }
+ } else {
+ for (visual = to; visual <= from; ++visual) {
+ int size = sizes[visual - to];
+ ResizeMode mode = modes[visual - to];
+ d->createSectionSpan(visual, visual, size, mode);
+ }
+ }
+ //Q_ASSERT(d->headerLength() == length());
+ //Q_ASSERT(oldHeaderLength == length());
+ //Q_ASSERT(d->logicalIndices.count() == d->sectionCount);
+
+ if (d->hasAutoResizeSections())
+ d->doDelayedResizeSections();
+ d->viewport->update();
+
+ emit sectionMoved(logical, from, to);
+}
+
+/*!
+ \since 4.2
+ Swaps the section at visual index \a first with the section at visual
+ index \a second.
+
+ \sa moveSection()
+*/
+void QHeaderView::swapSections(int first, int second)
+{
+ Q_D(QHeaderView);
+
+ if (first == second)
+ return;
+ d->executePostedLayout();
+ if (first < 0 || first >= d->sectionCount || second < 0 || second >= d->sectionCount)
+ return;
+
+ int firstSize = d->headerSectionSize(first);
+ ResizeMode firstMode = d->headerSectionResizeMode(first);
+ int firstLogical = d->logicalIndex(first);
+
+ int secondSize = d->headerSectionSize(second);
+ ResizeMode secondMode = d->headerSectionResizeMode(second);
+ int secondLogical = d->logicalIndex(second);
+
+ d->createSectionSpan(second, second, firstSize, firstMode);
+ d->createSectionSpan(first, first, secondSize, secondMode);
+
+ d->initializeIndexMapping();
+
+ d->visualIndices[firstLogical] = second;
+ d->logicalIndices[second] = firstLogical;
+
+ d->visualIndices[secondLogical] = first;
+ d->logicalIndices[first] = secondLogical;
+
+ if (!d->sectionHidden.isEmpty()) {
+ bool firstHidden = d->sectionHidden.testBit(first);
+ bool secondHidden = d->sectionHidden.testBit(second);
+ d->sectionHidden.setBit(first, secondHidden);
+ d->sectionHidden.setBit(second, firstHidden);
+ }
+
+ d->viewport->update();
+ emit sectionMoved(firstLogical, first, second);
+ emit sectionMoved(secondLogical, second, first);
+}
+
+/*!
+ \fn void QHeaderView::resizeSection(int logicalIndex, int size)
+
+ Resizes the section specified by \a logicalIndex to \a size measured in
+ pixels.
+
+ \sa sectionResized(), resizeMode(), sectionSize()
+*/
+
+void QHeaderView::resizeSection(int logical, int size)
+{
+ Q_D(QHeaderView);
+ if (logical < 0 || logical >= count())
+ return;
+
+ if (isSectionHidden(logical)) {
+ d->hiddenSectionSize.insert(logical, size);
+ return;
+ }
+
+ int visual = visualIndex(logical);
+ if (visual == -1)
+ return;
+
+ int oldSize = d->headerSectionSize(visual);
+ if (oldSize == size)
+ return;
+
+ d->executePostedLayout();
+ d->invalidateCachedSizeHint();
+
+ if (stretchLastSection() && visual == d->lastVisibleVisualIndex())
+ d->lastSectionSize = size;
+
+ if (size != oldSize)
+ d->createSectionSpan(visual, visual, size, d->headerSectionResizeMode(visual));
+
+ int w = d->viewport->width();
+ int h = d->viewport->height();
+ int pos = sectionViewportPosition(logical);
+ QRect r;
+ if (d->orientation == Qt::Horizontal)
+ if (isRightToLeft())
+ r.setRect(0, 0, pos + size, h);
+ else
+ r.setRect(pos, 0, w - pos, h);
+ else
+ r.setRect(0, pos, w, h - pos);
+
+ if (d->hasAutoResizeSections()) {
+ d->doDelayedResizeSections();
+ r = d->viewport->rect();
+ }
+ d->viewport->update(r.normalized());
+ emit sectionResized(logical, oldSize, size);
+}
+
+/*!
+ Resizes the sections according to the given \a mode, ignoring the current
+ resize mode.
+
+ \sa resizeMode(), sectionResized()
+*/
+
+void QHeaderView::resizeSections(QHeaderView::ResizeMode mode)
+{
+ Q_D(QHeaderView);
+ d->resizeSections(mode, true);
+}
+
+/*!
+ \fn void QHeaderView::hideSection(int logicalIndex)
+ Hides the section specified by \a logicalIndex.
+
+ \sa showSection(), isSectionHidden(), hiddenSectionCount(),
+ setSectionHidden()
+*/
+
+/*!
+ \fn void QHeaderView::showSection(int logicalIndex)
+ Shows the section specified by \a logicalIndex.
+
+ \sa hideSection(), isSectionHidden(), hiddenSectionCount(),
+ setSectionHidden()
+*/
+
+/*!
+ Returns true if the section specified by \a logicalIndex is explicitly
+ hidden from the user; otherwise returns false.
+
+ \sa hideSection(), showSection(), setSectionHidden(), hiddenSectionCount()
+*/
+
+bool QHeaderView::isSectionHidden(int logicalIndex) const
+{
+ Q_D(const QHeaderView);
+ d->executePostedLayout();
+ if (logicalIndex >= d->sectionHidden.count() || logicalIndex < 0 || logicalIndex >= d->sectionCount)
+ return false;
+ int visual = visualIndex(logicalIndex);
+ Q_ASSERT(visual != -1);
+ return d->sectionHidden.testBit(visual);
+}
+
+/*!
+ \since 4.1
+
+ Returns the number of sections in the header that has been hidden.
+
+ \sa setSectionHidden(), isSectionHidden()
+*/
+int QHeaderView::hiddenSectionCount() const
+{
+ Q_D(const QHeaderView);
+ return d->hiddenSectionSize.count();
+}
+
+/*!
+ If \a hide is true the section specified by \a logicalIndex is hidden;
+ otherwise the section is shown.
+
+ \sa isSectionHidden(), hiddenSectionCount()
+*/
+
+void QHeaderView::setSectionHidden(int logicalIndex, bool hide)
+{
+ Q_D(QHeaderView);
+ if (logicalIndex < 0 || logicalIndex >= count())
+ return;
+
+ d->executePostedLayout();
+ int visual = visualIndex(logicalIndex);
+ Q_ASSERT(visual != -1);
+ if (hide == d->isVisualIndexHidden(visual))
+ return;
+ if (hide) {
+ int size = d->headerSectionSize(visual);
+ if (!d->hasAutoResizeSections())
+ resizeSection(logicalIndex, 0);
+ d->hiddenSectionSize.insert(logicalIndex, size);
+ if (d->sectionHidden.count() < count())
+ d->sectionHidden.resize(count());
+ d->sectionHidden.setBit(visual, true);
+ if (d->hasAutoResizeSections())
+ d->doDelayedResizeSections();
+ } else {
+ int size = d->hiddenSectionSize.value(logicalIndex, d->defaultSectionSize);
+ d->hiddenSectionSize.remove(logicalIndex);
+ if (d->hiddenSectionSize.isEmpty()) {
+ d->sectionHidden.clear();
+ } else {
+ Q_ASSERT(visual <= d->sectionHidden.count());
+ d->sectionHidden.setBit(visual, false);
+ }
+ resizeSection(logicalIndex, size);
+ }
+}
+
+/*!
+ Returns the number of sections in the header.
+
+ \sa sectionCountChanged(), length()
+*/
+
+int QHeaderView::count() const
+{
+ Q_D(const QHeaderView);
+ //Q_ASSERT(d->sectionCount == d->headerSectionCount());
+ // ### this may affect the lazy layout
+ d->executePostedLayout();
+ return d->sectionCount;
+}
+
+/*!
+ Returns the visual index position of the section specified by the given
+ \a logicalIndex, or -1 otherwise.
+
+ Hidden sections still have valid visual indexes.
+
+ \sa logicalIndex()
+*/
+
+int QHeaderView::visualIndex(int logicalIndex) const
+{
+ Q_D(const QHeaderView);
+ if (logicalIndex < 0)
+ return -1;
+ d->executePostedLayout();
+ if (d->visualIndices.isEmpty()) { // nothing has been moved, so we have no mapping
+ if (logicalIndex < d->sectionCount)
+ return logicalIndex;
+ } else if (logicalIndex < d->visualIndices.count()) {
+ int visual = d->visualIndices.at(logicalIndex);
+ Q_ASSERT(visual < d->sectionCount);
+ return visual;
+ }
+ return -1;
+}
+
+/*!
+ Returns the logicalIndex for the section at the given \a visualIndex
+ position, or -1 otherwise.
+
+ \sa visualIndex(), sectionPosition()
+*/
+
+int QHeaderView::logicalIndex(int visualIndex) const
+{
+ Q_D(const QHeaderView);
+ if (visualIndex < 0 || visualIndex >= d->sectionCount)
+ return -1;
+ return d->logicalIndex(visualIndex);
+}
+
+/*!
+ If \a movable is true, the header may be moved by the user; otherwise it
+ is fixed in place.
+
+ \sa isMovable(), sectionMoved()
+*/
+
+// ### Qt 5: change to setSectionsMovable()
+void QHeaderView::setMovable(bool movable)
+{
+ Q_D(QHeaderView);
+ d->movableSections = movable;
+}
+
+/*!
+ Returns true if the header can be moved by the user; otherwise returns
+ false.
+
+ \sa setMovable()
+*/
+
+// ### Qt 5: change to sectionsMovable()
+bool QHeaderView::isMovable() const
+{
+ Q_D(const QHeaderView);
+ return d->movableSections;
+}
+
+/*!
+ If \a clickable is true, the header will respond to single clicks.
+
+ \sa isClickable(), sectionClicked(), sectionPressed(),
+ setSortIndicatorShown()
+*/
+
+// ### Qt 5: change to setSectionsClickable()
+void QHeaderView::setClickable(bool clickable)
+{
+ Q_D(QHeaderView);
+ d->clickableSections = clickable;
+}
+
+/*!
+ Returns true if the header is clickable; otherwise returns false. A
+ clickable header could be set up to allow the user to change the
+ representation of the data in the view related to the header.
+
+ \sa setClickable()
+*/
+
+// ### Qt 5: change to sectionsClickable()
+bool QHeaderView::isClickable() const
+{
+ Q_D(const QHeaderView);
+ return d->clickableSections;
+}
+
+void QHeaderView::setHighlightSections(bool highlight)
+{
+ Q_D(QHeaderView);
+ d->highlightSelected = highlight;
+}
+
+bool QHeaderView::highlightSections() const
+{
+ Q_D(const QHeaderView);
+ return d->highlightSelected;
+}
+
+/*!
+ Sets the constraints on how the header can be resized to those described
+ by the given \a mode.
+
+ \sa resizeMode(), length(), sectionResized(), sectionAutoResize()
+*/
+
+void QHeaderView::setResizeMode(ResizeMode mode)
+{
+ Q_D(QHeaderView);
+ initializeSections();
+ d->stretchSections = (mode == Stretch ? count() : 0);
+ d->contentsSections = (mode == ResizeToContents ? count() : 0);
+ d->setGlobalHeaderResizeMode(mode);
+ if (d->hasAutoResizeSections())
+ d->doDelayedResizeSections(); // section sizes may change as a result of the new mode
+}
+
+/*!
+ \overload
+
+ Sets the constraints on how the section specified by \a logicalIndex in
+ the header can be resized to those described by the given \a mode.
+
+ \note This setting will be ignored for the last section if the stretchLastSection
+ property is set to true. This is the default for the horizontal headers provided
+ by QTreeView.
+
+ \sa setStretchLastSection()
+*/
+
+// ### Qt 5: change to setSectionResizeMode()
+void QHeaderView::setResizeMode(int logicalIndex, ResizeMode mode)
+{
+ Q_D(QHeaderView);
+ int visual = visualIndex(logicalIndex);
+ Q_ASSERT(visual != -1);
+
+ ResizeMode old = d->headerSectionResizeMode(visual);
+ d->setHeaderSectionResizeMode(visual, mode);
+
+ if (mode == Stretch && old != Stretch)
+ ++d->stretchSections;
+ else if (mode == ResizeToContents && old != ResizeToContents)
+ ++d->contentsSections;
+ else if (mode != Stretch && old == Stretch)
+ --d->stretchSections;
+ else if (mode != ResizeToContents && old == ResizeToContents)
+ --d->contentsSections;
+
+ if (d->hasAutoResizeSections() && d->state == QHeaderViewPrivate::NoState)
+ d->doDelayedResizeSections(); // section sizes may change as a result of the new mode
+}
+
+/*!
+ Returns the resize mode that applies to the section specified by the given
+ \a logicalIndex.
+
+ \sa setResizeMode()
+*/
+
+QHeaderView::ResizeMode QHeaderView::resizeMode(int logicalIndex) const
+{
+ Q_D(const QHeaderView);
+ int visual = visualIndex(logicalIndex);
+ Q_ASSERT(visual != -1);
+ return d->visualIndexResizeMode(visual);
+}
+
+/*!
+ \since 4.1
+
+ Returns the number of sections that are set to resize mode stretch. In
+ views, this can be used to see if the headerview needs to resize the
+ sections when the view's geometry changes.
+
+ \sa stretchLastSection, resizeMode()
+*/
+
+int QHeaderView::stretchSectionCount() const
+{
+ Q_D(const QHeaderView);
+ return d->stretchSections;
+}
+
+/*!
+ \property QHeaderView::showSortIndicator
+ \brief whether the sort indicator is shown
+
+ By default, this property is false.
+
+ \sa setClickable()
+*/
+
+void QHeaderView::setSortIndicatorShown(bool show)
+{
+ Q_D(QHeaderView);
+ if (d->sortIndicatorShown == show)
+ return;
+
+ d->sortIndicatorShown = show;
+
+ if (sortIndicatorSection() < 0 || sortIndicatorSection() > count())
+ return;
+
+ if (d->visualIndexResizeMode(sortIndicatorSection()) == ResizeToContents)
+ resizeSections();
+
+ d->viewport->update();
+}
+
+bool QHeaderView::isSortIndicatorShown() const
+{
+ Q_D(const QHeaderView);
+ return d->sortIndicatorShown;
+}
+
+/*!
+ Sets the sort indicator for the section specified by the given
+ \a logicalIndex in the direction specified by \a order, and removes the
+ sort indicator from any other section that was showing it.
+
+ \a logicalIndex may be -1, in which case no sort indicator will be shown
+ and the model will return to its natural, unsorted order. Note that not
+ all models support this and may even crash in this case.
+
+ \sa sortIndicatorSection() sortIndicatorOrder()
+*/
+
+void QHeaderView::setSortIndicator(int logicalIndex, Qt::SortOrder order)
+{
+ Q_D(QHeaderView);
+
+ // This is so that people can set the position of the sort indicator before the fill the model
+ int old = d->sortIndicatorSection;
+ d->sortIndicatorSection = logicalIndex;
+ d->sortIndicatorOrder = order;
+
+ if (logicalIndex >= d->sectionCount)
+ return; // nothing to do
+
+ if (old != logicalIndex
+ && ((logicalIndex >= 0 && resizeMode(logicalIndex) == ResizeToContents)
+ || old >= d->sectionCount || (old >= 0 && resizeMode(old) == ResizeToContents))) {
+ resizeSections();
+ d->viewport->update();
+ } else {
+ if (old >= 0 && old != logicalIndex)
+ updateSection(old);
+ if (logicalIndex >= 0)
+ updateSection(logicalIndex);
+ }
+
+ emit sortIndicatorChanged(logicalIndex, order);
+}
+
+/*!
+ Returns the logical index of the section that has a sort indicator.
+ By default this is section 0.
+
+ \sa setSortIndicator() sortIndicatorOrder() setSortIndicatorShown()
+*/
+
+int QHeaderView::sortIndicatorSection() const
+{
+ Q_D(const QHeaderView);
+ return d->sortIndicatorSection;
+}
+
+/*!
+ Returns the order for the sort indicator. If no section has a sort
+ indicator the return value of this function is undefined.
+
+ \sa setSortIndicator() sortIndicatorSection()
+*/
+
+Qt::SortOrder QHeaderView::sortIndicatorOrder() const
+{
+ Q_D(const QHeaderView);
+ return d->sortIndicatorOrder;
+}
+
+/*!
+ \property QHeaderView::stretchLastSection
+ \brief whether the last visible section in the header takes up all the
+ available space
+
+ The default value is false.
+
+ \note The horizontal headers provided by QTreeView are configured with this
+ property set to true, ensuring that the view does not waste any of the
+ space assigned to it for its header. If this value is set to true, this
+ property will override the resize mode set on the last section in the
+ header.
+
+ \sa setResizeMode()
+*/
+bool QHeaderView::stretchLastSection() const
+{
+ Q_D(const QHeaderView);
+ return d->stretchLastSection;
+}
+
+void QHeaderView::setStretchLastSection(bool stretch)
+{
+ Q_D(QHeaderView);
+ d->stretchLastSection = stretch;
+ if (d->state != QHeaderViewPrivate::NoState)
+ return;
+ if (stretch)
+ resizeSections();
+ else if (count())
+ resizeSection(count() - 1, d->defaultSectionSize);
+}
+
+/*!
+ \since 4.2
+ \property QHeaderView::cascadingSectionResizes
+ \brief whether interactive resizing will be cascaded to the following
+ sections once the section being resized by the user has reached its
+ minimum size
+
+ This property only affects sections that have \l Interactive as their
+ resize mode.
+
+ The default value is false.
+
+ \sa setResizeMode()
+*/
+bool QHeaderView::cascadingSectionResizes() const
+{
+ Q_D(const QHeaderView);
+ return d->cascadingResizing;
+}
+
+void QHeaderView::setCascadingSectionResizes(bool enable)
+{
+ Q_D(QHeaderView);
+ d->cascadingResizing = enable;
+}
+
+/*!
+ \property QHeaderView::defaultSectionSize
+ \brief the default size of the header sections before resizing.
+
+ This property only affects sections that have \l Interactive or \l Fixed
+ as their resize mode.
+
+ \sa setResizeMode() minimumSectionSize
+*/
+int QHeaderView::defaultSectionSize() const
+{
+ Q_D(const QHeaderView);
+ return d->defaultSectionSize;
+}
+
+void QHeaderView::setDefaultSectionSize(int size)
+{
+ Q_D(QHeaderView);
+ d->defaultSectionSize = size;
+ d->forceInitializing = true;
+}
+
+/*!
+ \since 4.2
+ \property QHeaderView::minimumSectionSize
+ \brief the minimum size of the header sections.
+
+ The minimum section size is the smallest section size allowed. If the
+ minimum section size is set to -1, QHeaderView will use the maximum of
+ the \l{QApplication::globalStrut()}{global strut} or the
+ \l{fontMetrics()}{font metrics} size.
+
+ This property is honored by all \l{ResizeMode}{resize modes}.
+
+ \sa setResizeMode() defaultSectionSize
+*/
+int QHeaderView::minimumSectionSize() const
+{
+ Q_D(const QHeaderView);
+ if (d->minimumSectionSize == -1) {
+ QSize strut = QApplication::globalStrut();
+ int margin = style()->pixelMetric(QStyle::PM_HeaderMargin, 0, this);
+ if (d->orientation == Qt::Horizontal)
+ return qMax(strut.width(), (fontMetrics().maxWidth() + margin));
+ return qMax(strut.height(), (fontMetrics().lineSpacing() + margin));
+ }
+ return d->minimumSectionSize;
+}
+
+void QHeaderView::setMinimumSectionSize(int size)
+{
+ Q_D(QHeaderView);
+ d->minimumSectionSize = size;
+}
+
+/*!
+ \since 4.1
+ \property QHeaderView::defaultAlignment
+ \brief the default alignment of the text in each header section
+*/
+
+Qt::Alignment QHeaderView::defaultAlignment() const
+{
+ Q_D(const QHeaderView);
+ return d->defaultAlignment;
+}
+
+void QHeaderView::setDefaultAlignment(Qt::Alignment alignment)
+{
+ Q_D(QHeaderView);
+ if (d->defaultAlignment == alignment)
+ return;
+
+ d->defaultAlignment = alignment;
+ d->viewport->update();
+}
+
+/*!
+ \internal
+*/
+void QHeaderView::doItemsLayout()
+{
+ initializeSections();
+ QAbstractItemView::doItemsLayout();
+}
+
+/*!
+ Returns true if sections in the header has been moved; otherwise returns
+ false;
+
+ \sa moveSection()
+*/
+bool QHeaderView::sectionsMoved() const
+{
+ Q_D(const QHeaderView);
+ return !d->visualIndices.isEmpty();
+}
+
+/*!
+ \since 4.1
+
+ Returns true if sections in the header has been hidden; otherwise returns
+ false;
+
+ \sa setSectionHidden()
+*/
+bool QHeaderView::sectionsHidden() const
+{
+ Q_D(const QHeaderView);
+ return !d->hiddenSectionSize.isEmpty();
+}
+
+#ifndef QT_NO_DATASTREAM
+/*!
+ \since 4.3
+
+ Saves the current state of this header view.
+
+ To restore the saved state, pass the return value to restoreState().
+
+ \sa restoreState()
+*/
+QByteArray QHeaderView::saveState() const
+{
+ Q_D(const QHeaderView);
+ QByteArray data;
+ QDataStream stream(&data, QIODevice::WriteOnly);
+ stream << QHeaderViewPrivate::VersionMarker;
+ stream << 0; // current version is 0
+ d->write(stream);
+ return data;
+}
+
+/*!
+ \since 4.3
+ Restores the \a state of this header view.
+ This function returns \c true if the state was restored; otherwise returns
+ false.
+
+ \sa saveState()
+*/
+bool QHeaderView::restoreState(const QByteArray &state)
+{
+ Q_D(QHeaderView);
+ if (state.isEmpty())
+ return false;
+ QByteArray data = state;
+ QDataStream stream(&data, QIODevice::ReadOnly);
+ int marker;
+ int ver;
+ stream >> marker;
+ stream >> ver;
+ if (stream.status() != QDataStream::Ok
+ || marker != QHeaderViewPrivate::VersionMarker
+ || ver != 0) // current version is 0
+ return false;
+
+ if (d->read(stream)) {
+ emit sortIndicatorChanged(d->sortIndicatorSection, d->sortIndicatorOrder );
+ d->viewport->update();
+ return true;
+ }
+ return false;
+}
+#endif
+
+/*!
+ \reimp
+*/
+void QHeaderView::reset()
+{
+ QAbstractItemView::reset();
+ // it would be correct to call clear, but some apps rely
+ // on the header keeping the sections, even after calling reset
+ //d->clear();
+ initializeSections();
+}
+
+/*!
+ Updates the changed header sections with the given \a orientation, from
+ \a logicalFirst to \a logicalLast inclusive.
+*/
+void QHeaderView::headerDataChanged(Qt::Orientation orientation, int logicalFirst, int logicalLast)
+{
+ Q_D(QHeaderView);
+ if (d->orientation != orientation)
+ return;
+
+ if (logicalFirst < 0 || logicalLast < 0 || logicalFirst >= count() || logicalLast >= count())
+ return;
+
+ d->invalidateCachedSizeHint();
+
+ int firstVisualIndex = INT_MAX, lastVisualIndex = -1;
+
+ for (int section = logicalFirst; section <= logicalLast; ++section) {
+ const int visual = visualIndex(section);
+ firstVisualIndex = qMin(firstVisualIndex, visual);
+ lastVisualIndex = qMax(lastVisualIndex, visual);
+ }
+
+ d->executePostedResize();
+ const int first = d->headerSectionPosition(firstVisualIndex),
+ last = d->headerSectionPosition(lastVisualIndex)
+ + d->headerSectionSize(lastVisualIndex);
+
+ if (orientation == Qt::Horizontal) {
+ d->viewport->update(first, 0, last - first, d->viewport->height());
+ } else {
+ d->viewport->update(0, first, d->viewport->width(), last - first);
+ }
+}
+
+/*!
+ \internal
+ \since 4.2
+
+ Updates the section specified by the given \a logicalIndex.
+*/
+
+void QHeaderView::updateSection(int logicalIndex)
+{
+ Q_D(QHeaderView);
+ if (d->orientation == Qt::Horizontal)
+ d->viewport->update(QRect(sectionViewportPosition(logicalIndex),
+ 0, sectionSize(logicalIndex), d->viewport->height()));
+ else
+ d->viewport->update(QRect(0, sectionViewportPosition(logicalIndex),
+ d->viewport->width(), sectionSize(logicalIndex)));
+}
+
+/*!
+ Resizes the sections according to their size hints. Normally, you do not
+ have to call this function.
+*/
+
+void QHeaderView::resizeSections()
+{
+ Q_D(QHeaderView);
+ if (d->hasAutoResizeSections())
+ d->resizeSections(Interactive, false); // no global resize mode
+}
+
+/*!
+ This slot is called when sections are inserted into the \a parent.
+ \a logicalFirst and \a logicalLast indices signify where the new sections
+ were inserted.
+
+ If only one section is inserted, \a logicalFirst and \a logicalLast will
+ be the same.
+*/
+
+void QHeaderView::sectionsInserted(const QModelIndex &parent,
+ int logicalFirst, int logicalLast)
+{
+ Q_D(QHeaderView);
+ if (parent != d->root)
+ return; // we only handle changes in the top level
+ int oldCount = d->sectionCount;
+
+ d->invalidateCachedSizeHint();
+
+ // add the new sections
+ int insertAt = 0;
+ for (int spanStart = 0; insertAt < d->sectionSpans.count() && spanStart < logicalFirst; ++insertAt)
+ spanStart += d->sectionSpans.at(insertAt).count;
+
+ int insertCount = logicalLast - logicalFirst + 1;
+ d->sectionCount += insertCount;
+
+ if (d->sectionSpans.isEmpty() || insertAt >= d->sectionSpans.count()) {
+ int insertLength = d->defaultSectionSize * insertCount;
+ d->length += insertLength;
+ QHeaderViewPrivate::SectionSpan span(insertLength, insertCount, d->globalResizeMode);
+ d->sectionSpans.append(span);
+ } else if ((d->sectionSpans.at(insertAt).sectionSize() == d->defaultSectionSize)
+ && d->sectionSpans.at(insertAt).resizeMode == d->globalResizeMode) {
+ // add the new sections to an existing span
+ int insertLength = d->sectionSpans.at(insertAt).sectionSize() * insertCount;
+ d->length += insertLength;
+ d->sectionSpans[insertAt].size += insertLength;
+ d->sectionSpans[insertAt].count += insertCount;
+ } else {
+ // separate them out into their own span
+ int insertLength = d->defaultSectionSize * insertCount;
+ d->length += insertLength;
+ QHeaderViewPrivate::SectionSpan span(insertLength, insertCount, d->globalResizeMode);
+ d->sectionSpans.insert(insertAt, span);
+ }
+
+ // update sorting column
+ if (d->sortIndicatorSection >= logicalFirst)
+ d->sortIndicatorSection += insertCount;
+
+ // update resize mode section counts
+ if (d->globalResizeMode == Stretch)
+ d->stretchSections = d->sectionCount;
+ else if (d->globalResizeMode == ResizeToContents)
+ d->contentsSections = d->sectionCount;
+
+ // clear selection cache
+ d->sectionSelected.clear();
+
+ // update mapping
+ if (!d->visualIndices.isEmpty() && !d->logicalIndices.isEmpty()) {
+ Q_ASSERT(d->visualIndices.count() == d->logicalIndices.count());
+ int mappingCount = d->visualIndices.count();
+ for (int i = 0; i < mappingCount; ++i) {
+ if (d->visualIndices.at(i) >= logicalFirst)
+ d->visualIndices[i] += insertCount;
+ if (d->logicalIndices.at(i) >= logicalFirst)
+ d->logicalIndices[i] += insertCount;
+ }
+ for (int j = logicalFirst; j <= logicalLast; ++j) {
+ d->visualIndices.insert(j, j);
+ d->logicalIndices.insert(j, j);
+ }
+ }
+
+ // insert sections into sectionsHidden
+ if (!d->sectionHidden.isEmpty()) {
+ QBitArray sectionHidden(d->sectionHidden);
+ sectionHidden.resize(sectionHidden.count() + insertCount);
+ //sectionHidden.fill(false, logicalFirst, logicalLast + 1);
+ for (int i = logicalFirst; i <= logicalLast; ++i)
+ // visual == logical in this range (see previous block)
+ sectionHidden.setBit(i, false);
+ for (int j = logicalLast + 1; j < sectionHidden.count(); ++j)
+ sectionHidden.setBit(d->visualIndex(j),
+ d->sectionHidden.testBit(d->visualIndex(j - insertCount)));
+ d->sectionHidden = sectionHidden;
+ }
+
+ // insert sections into hiddenSectionSize
+ QHash<int, int> newHiddenSectionSize; // from logical index to section size
+ for (int i = 0; i < logicalFirst; ++i)
+ if (isSectionHidden(i))
+ newHiddenSectionSize[i] = d->hiddenSectionSize[i];
+ for (int j = logicalLast + 1; j < d->sectionCount; ++j)
+ if (isSectionHidden(j))
+ newHiddenSectionSize[j] = d->hiddenSectionSize[j - insertCount];
+ d->hiddenSectionSize = newHiddenSectionSize;
+
+ d->doDelayedResizeSections();
+ emit sectionCountChanged(oldCount, count());
+
+ // if the new sections were not updated by resizing, we need to update now
+ if (!d->hasAutoResizeSections())
+ d->viewport->update();
+}
+
+/*!
+ This slot is called when sections are removed from the \a parent.
+ \a logicalFirst and \a logicalLast signify where the sections were removed.
+
+ If only one section is removed, \a logicalFirst and \a logicalLast will
+ be the same.
+*/
+
+void QHeaderView::sectionsAboutToBeRemoved(const QModelIndex &parent,
+ int logicalFirst, int logicalLast)
+{
+ Q_UNUSED(parent);
+ Q_UNUSED(logicalFirst);
+ Q_UNUSED(logicalLast);
+}
+
+void QHeaderViewPrivate::updateHiddenSections(int logicalFirst, int logicalLast)
+{
+ Q_Q(QHeaderView);
+ const int changeCount = logicalLast - logicalFirst + 1;
+
+ // remove sections from hiddenSectionSize
+ QHash<int, int> newHiddenSectionSize; // from logical index to section size
+ for (int i = 0; i < logicalFirst; ++i)
+ if (q->isSectionHidden(i))
+ newHiddenSectionSize[i] = hiddenSectionSize[i];
+ for (int j = logicalLast + 1; j < sectionCount; ++j)
+ if (q->isSectionHidden(j))
+ newHiddenSectionSize[j - changeCount] = hiddenSectionSize[j];
+ hiddenSectionSize = newHiddenSectionSize;
+
+ // remove sections from sectionsHidden
+ if (!sectionHidden.isEmpty()) {
+ const int newsize = qMin(sectionCount - changeCount, sectionHidden.size());
+ QBitArray newSectionHidden(newsize);
+ for (int j = 0, k = 0; j < sectionHidden.size(); ++j) {
+ const int logical = logicalIndex(j);
+ if (logical < logicalFirst || logical > logicalLast) {
+ newSectionHidden[k++] = sectionHidden[j];
+ }
+ }
+ sectionHidden = newSectionHidden;
+ }
+}
+
+void QHeaderViewPrivate::_q_sectionsRemoved(const QModelIndex &parent,
+ int logicalFirst, int logicalLast)
+{
+ Q_Q(QHeaderView);
+ if (parent != root)
+ return; // we only handle changes in the top level
+ if (qMin(logicalFirst, logicalLast) < 0
+ || qMax(logicalLast, logicalFirst) >= sectionCount)
+ return;
+ int oldCount = q->count();
+ int changeCount = logicalLast - logicalFirst + 1;
+
+ updateHiddenSections(logicalFirst, logicalLast);
+
+ if (visualIndices.isEmpty() && logicalIndices.isEmpty()) {
+ //Q_ASSERT(headerSectionCount() == sectionCount);
+ removeSectionsFromSpans(logicalFirst, logicalLast);
+ } else {
+ for (int l = logicalLast; l >= logicalFirst; --l) {
+ int visual = visualIndices.at(l);
+ for (int v = 0; v < sectionCount; ++v) {
+ if (v >= logicalIndices.count())
+ continue; // the section doesn't exist
+ if (v > visual) {
+ int logical = logicalIndices.at(v);
+ --(visualIndices[logical]);
+ }
+ if (logicalIndex(v) > l) // no need to move the positions before l
+ --(logicalIndices[v]);
+ }
+ logicalIndices.remove(visual);
+ visualIndices.remove(l);
+ //Q_ASSERT(headerSectionCount() == sectionCount);
+ removeSectionsFromSpans(visual, visual);
+ }
+ // ### handle sectionSelection, sectionHidden
+ }
+ sectionCount -= changeCount;
+
+ // update sorting column
+ if (sortIndicatorSection >= logicalFirst) {
+ if (sortIndicatorSection <= logicalLast)
+ sortIndicatorSection = -1;
+ else
+ sortIndicatorSection -= changeCount;
+ }
+
+ // if we only have the last section (the "end" position) left, the header is empty
+ if (sectionCount <= 0)
+ clear();
+ invalidateCachedSizeHint();
+ emit q->sectionCountChanged(oldCount, q->count());
+ viewport->update();
+}
+
+void QHeaderViewPrivate::_q_layoutAboutToBeChanged()
+{
+ //if there is no row/column we can't have mapping for columns
+ //because no QModelIndex in the model would be valid
+ // ### this is far from being bullet-proof and we would need a real system to
+ // ### map columns or rows persistently
+ if ((orientation == Qt::Horizontal && model->rowCount(root) == 0)
+ || model->columnCount(root) == 0)
+ return;
+
+ for (int i = 0; i < sectionHidden.count(); ++i)
+ if (sectionHidden.testBit(i)) // ### note that we are using column or row 0
+ persistentHiddenSections.append(orientation == Qt::Horizontal
+ ? model->index(0, logicalIndex(i), root)
+ : model->index(logicalIndex(i), 0, root));
+}
+
+void QHeaderViewPrivate::_q_layoutChanged()
+{
+ Q_Q(QHeaderView);
+ viewport->update();
+ if (persistentHiddenSections.isEmpty() || modelIsEmpty()) {
+ if (modelSectionCount() != sectionCount)
+ q->initializeSections();
+ persistentHiddenSections.clear();
+ return;
+ }
+ bool sectionCountChanged = false;
+ for (int i = 0; i < sectionHidden.count(); ++i) {
+ if (sectionHidden.testBit(i))
+ q->setSectionHidden(logicalIndex(i), false);
+ }
+
+ for (int i = 0; i < persistentHiddenSections.count(); ++i) {
+ QModelIndex index = persistentHiddenSections.at(i);
+ if (index.isValid()) {
+ const int logical = (orientation == Qt::Horizontal
+ ? index.column()
+ : index.row());
+ q->setSectionHidden(logical, true);
+ } else if (!sectionCountChanged && (modelSectionCount() != sectionCount)) {
+ sectionCountChanged = true;
+ break;
+ }
+ }
+ persistentHiddenSections.clear();
+
+ // the number of sections changed; we need to reread the state of the model
+ if (sectionCountChanged)
+ q->initializeSections();
+}
+
+/*!
+ \internal
+*/
+
+void QHeaderView::initializeSections()
+{
+ Q_D(QHeaderView);
+ const int oldCount = d->sectionCount;
+ const int newCount = d->modelSectionCount();
+ if (newCount <= 0) {
+ d->clear();
+ emit sectionCountChanged(oldCount, 0);
+ } else if (newCount != oldCount) {
+ const int min = qBound(0, oldCount, newCount - 1);
+ initializeSections(min, newCount - 1);
+ if (stretchLastSection()) // we've already gotten the size hint
+ d->lastSectionSize = sectionSize(logicalIndex(d->sectionCount - 1));
+
+ //make sure we update the hidden sections
+ if (newCount < oldCount)
+ d->updateHiddenSections(0, newCount-1);
+ } else if (d->forceInitializing) {
+ initializeSections(0, newCount - 1);
+ d->forceInitializing = false;
+ }
+}
+
+/*!
+ \internal
+*/
+
+void QHeaderView::initializeSections(int start, int end)
+{
+ Q_D(QHeaderView);
+
+ Q_ASSERT(start >= 0);
+ Q_ASSERT(end >= 0);
+
+ d->executePostedLayout();
+ d->invalidateCachedSizeHint();
+
+ if (end + 1 < d->sectionCount) {
+ int newCount = end + 1;
+ d->removeSectionsFromSpans(newCount, d->sectionCount);
+ if (!d->hiddenSectionSize.isEmpty()) {
+ if (d->sectionCount - newCount > d->hiddenSectionSize.count()) {
+ for (int i = end + 1; i < d->sectionCount; ++i)
+ d->hiddenSectionSize.remove(i);
+ } else {
+ QHash<int, int>::iterator it = d->hiddenSectionSize.begin();
+ while (it != d->hiddenSectionSize.end()) {
+ if (it.key() > end)
+ it = d->hiddenSectionSize.erase(it);
+ else
+ ++it;
+ }
+ }
+ }
+ }
+
+ int oldCount = d->sectionCount;
+ d->sectionCount = end + 1;
+
+ if (!d->logicalIndices.isEmpty()) {
+ d->logicalIndices.resize(d->sectionCount);
+ d->visualIndices.resize(d->sectionCount);
+ for (int i = start; i < d->sectionCount; ++i){
+ d->logicalIndices[i] = i;
+ d->visualIndices[i] = i;
+ }
+ }
+
+ if (d->globalResizeMode == Stretch)
+ d->stretchSections = d->sectionCount;
+ else if (d->globalResizeMode == ResizeToContents)
+ d->contentsSections = d->sectionCount;
+ if (!d->sectionHidden.isEmpty())
+ d->sectionHidden.resize(d->sectionCount);
+
+ if (d->sectionCount > oldCount || d->forceInitializing)
+ d->createSectionSpan(start, end, (end - start + 1) * d->defaultSectionSize, d->globalResizeMode);
+ //Q_ASSERT(d->headerLength() == d->length);
+
+ if (d->sectionCount != oldCount)
+ emit sectionCountChanged(oldCount, d->sectionCount);
+ d->viewport->update();
+}
+
+/*!
+ \reimp
+*/
+
+void QHeaderView::currentChanged(const QModelIndex &current, const QModelIndex &old)
+{
+ Q_D(QHeaderView);
+
+ if (d->orientation == Qt::Horizontal && current.column() != old.column()) {
+ if (old.isValid() && old.parent() == d->root)
+ d->setDirtyRegion(QRect(sectionViewportPosition(old.column()), 0,
+ sectionSize(old.column()), d->viewport->height()));
+ if (current.isValid() && current.parent() == d->root)
+ d->setDirtyRegion(QRect(sectionViewportPosition(current.column()), 0,
+ sectionSize(current.column()), d->viewport->height()));
+ } else if (d->orientation == Qt::Vertical && current.row() != old.row()) {
+ if (old.isValid() && old.parent() == d->root)
+ d->setDirtyRegion(QRect(0, sectionViewportPosition(old.row()),
+ d->viewport->width(), sectionSize(old.row())));
+ if (current.isValid() && current.parent() == d->root)
+ d->setDirtyRegion(QRect(0, sectionViewportPosition(current.row()),
+ d->viewport->width(), sectionSize(current.row())));
+ }
+ d->updateDirtyRegion();
+}
+
+
+/*!
+ \reimp
+*/
+
+bool QHeaderView::event(QEvent *e)
+{
+ Q_D(QHeaderView);
+ switch (e->type()) {
+ case QEvent::HoverEnter: {
+ QHoverEvent *he = static_cast<QHoverEvent*>(e);
+ d->hover = logicalIndexAt(he->pos());
+ if (d->hover != -1)
+ updateSection(d->hover);
+ break; }
+ case QEvent::Leave:
+ case QEvent::HoverLeave: {
+ if (d->hover != -1)
+ updateSection(d->hover);
+ d->hover = -1;
+ break; }
+ case QEvent::HoverMove: {
+ QHoverEvent *he = static_cast<QHoverEvent*>(e);
+ int oldHover = d->hover;
+ d->hover = logicalIndexAt(he->pos());
+ if (d->hover != oldHover) {
+ if (oldHover != -1)
+ updateSection(oldHover);
+ if (d->hover != -1)
+ updateSection(d->hover);
+ }
+ break; }
+ case QEvent::Timer: { // ### reimplement timerEvent() instead ?
+ QTimerEvent *te = static_cast<QTimerEvent*>(e);
+ if (te->timerId() == d->delayedResize.timerId()) {
+ d->delayedResize.stop();
+ resizeSections();
+ }
+ break; }
+ default:
+ break;
+ }
+ return QAbstractItemView::event(e);
+}
+
+/*!
+ \reimp
+*/
+
+void QHeaderView::paintEvent(QPaintEvent *e)
+{
+ Q_D(QHeaderView);
+
+ if (count() == 0)
+ return;
+
+ QPainter painter(d->viewport);
+ const QPoint offset = d->scrollDelayOffset;
+ QRect translatedEventRect = e->rect();
+ translatedEventRect.translate(offset);
+
+ int start = -1;
+ int end = -1;
+ if (d->orientation == Qt::Horizontal) {
+ start = visualIndexAt(translatedEventRect.left());
+ end = visualIndexAt(translatedEventRect.right());
+ } else {
+ start = visualIndexAt(translatedEventRect.top());
+ end = visualIndexAt(translatedEventRect.bottom());
+ }
+
+ if (d->reverse()) {
+ start = (start == -1 ? count() - 1 : start);
+ end = (end == -1 ? 0 : end);
+ } else {
+ start = (start == -1 ? 0 : start);
+ end = (end == -1 ? count() - 1 : end);
+ }
+
+ int tmp = start;
+ start = qMin(start, end);
+ end = qMax(tmp, end);
+
+ d->prepareSectionSelected(); // clear and resize the bit array
+
+ QRect currentSectionRect;
+ int logical;
+ const int width = d->viewport->width();
+ const int height = d->viewport->height();
+ for (int i = start; i <= end; ++i) {
+ if (d->isVisualIndexHidden(i))
+ continue;
+ painter.save();
+ logical = logicalIndex(i);
+ if (d->orientation == Qt::Horizontal) {
+ currentSectionRect.setRect(sectionViewportPosition(logical), 0, sectionSize(logical), height);
+ } else {
+ currentSectionRect.setRect(0, sectionViewportPosition(logical), width, sectionSize(logical));
+ }
+ currentSectionRect.translate(offset);
+
+ QVariant variant = d->model->headerData(logical, d->orientation,
+ Qt::FontRole);
+ if (variant.isValid() && qVariantCanConvert<QFont>(variant)) {
+ QFont sectionFont = qvariant_cast<QFont>(variant);
+ painter.setFont(sectionFont);
+ }
+ paintSection(&painter, currentSectionRect, logical);
+ painter.restore();
+ }
+
+ QStyleOption opt;
+ opt.init(this);
+ // Paint the area beyond where there are indexes
+ if (d->reverse()) {
+ opt.state |= QStyle::State_Horizontal;
+ if (currentSectionRect.left() > translatedEventRect.left()) {
+ opt.rect = QRect(translatedEventRect.left(), 0,
+ currentSectionRect.left() - translatedEventRect.left(), height);
+ style()->drawControl(QStyle::CE_HeaderEmptyArea, &opt, &painter, this);
+ }
+ } else if (currentSectionRect.right() < translatedEventRect.right()) {
+ // paint to the right
+ opt.state |= QStyle::State_Horizontal;
+ opt.rect = QRect(currentSectionRect.right() + 1, 0,
+ translatedEventRect.right() - currentSectionRect.right(), height);
+ style()->drawControl(QStyle::CE_HeaderEmptyArea, &opt, &painter, this);
+ } else if (currentSectionRect.bottom() < translatedEventRect.bottom()) {
+ // paint the bottom section
+ opt.state &= ~QStyle::State_Horizontal;
+ opt.rect = QRect(0, currentSectionRect.bottom() + 1,
+ width, height - currentSectionRect.bottom() - 1);
+ style()->drawControl(QStyle::CE_HeaderEmptyArea, &opt, &painter, this);
+ }
+
+#if 0
+ // ### visualize section spans
+ for (int a = 0, i = 0; i < d->sectionSpans.count(); ++i) {
+ QColor color((i & 4 ? 255 : 0), (i & 2 ? 255 : 0), (i & 1 ? 255 : 0));
+ if (d->orientation == Qt::Horizontal)
+ painter.fillRect(a - d->offset, 0, d->sectionSpans.at(i).size, 4, color);
+ else
+ painter.fillRect(0, a - d->offset, 4, d->sectionSpans.at(i).size, color);
+ a += d->sectionSpans.at(i).size;
+ }
+
+#endif
+}
+
+/*!
+ \reimp
+*/
+
+void QHeaderView::mousePressEvent(QMouseEvent *e)
+{
+ Q_D(QHeaderView);
+ if (d->state != QHeaderViewPrivate::NoState || e->button() != Qt::LeftButton)
+ return;
+ int pos = d->orientation == Qt::Horizontal ? e->x() : e->y();
+ int handle = d->sectionHandleAt(pos);
+ d->originalSize = -1; // clear the stored original size
+ if (handle == -1) {
+ d->pressed = logicalIndexAt(pos);
+ if (d->clickableSections)
+ emit sectionPressed(d->pressed);
+ if (d->movableSections) {
+ d->section = d->target = d->pressed;
+ if (d->section == -1)
+ return;
+ d->state = QHeaderViewPrivate::MoveSection;
+ d->setupSectionIndicator(d->section, pos);
+ } else if (d->clickableSections && d->pressed != -1) {
+ updateSection(d->pressed);
+ d->state = QHeaderViewPrivate::SelectSections;
+ }
+ } else if (resizeMode(handle) == Interactive) {
+ d->originalSize = sectionSize(handle);
+ d->state = QHeaderViewPrivate::ResizeSection;
+ d->section = handle;
+ }
+
+ d->firstPos = pos;
+ d->lastPos = pos;
+
+ d->clearCascadingSections();
+}
+
+/*!
+ \reimp
+*/
+
+void QHeaderView::mouseMoveEvent(QMouseEvent *e)
+{
+ Q_D(QHeaderView);
+ int pos = d->orientation == Qt::Horizontal ? e->x() : e->y();
+ if (pos < 0)
+ return;
+ if (e->buttons() == Qt::NoButton) {
+ d->state = QHeaderViewPrivate::NoState;
+ d->pressed = -1;
+ }
+ switch (d->state) {
+ case QHeaderViewPrivate::ResizeSection: {
+ Q_ASSERT(d->originalSize != -1);
+ if (d->cascadingResizing) {
+ int delta = d->reverse() ? d->lastPos - pos : pos - d->lastPos;
+ int visual = visualIndex(d->section);
+ d->cascadingResize(visual, d->headerSectionSize(visual) + delta);
+ } else {
+ int delta = d->reverse() ? d->firstPos - pos : pos - d->firstPos;
+ resizeSection(d->section, qMax(d->originalSize + delta, minimumSectionSize()));
+ }
+ d->lastPos = pos;
+ return;
+ }
+ case QHeaderViewPrivate::MoveSection: {
+ if (qAbs(pos - d->firstPos) >= QApplication::startDragDistance()) {
+ int indicatorCenter = (d->orientation == Qt::Horizontal
+ ? d->sectionIndicator->width()
+ : d->sectionIndicator->height()) / 2;
+ int centerOffset = indicatorCenter - d->sectionIndicatorOffset;
+ // This will drop the moved section to the position under the center of the indicator.
+ // If centerOffset is 0, the section will be moved to the position of the mouse cursor.
+ int visual = visualIndexAt(pos + centerOffset);
+ if (visual == -1)
+ return;
+ d->target = d->logicalIndex(visual);
+ d->updateSectionIndicator(d->section, pos);
+ } else {
+ int visual = visualIndexAt(d->firstPos);
+ if (visual == -1)
+ return;
+ d->target = d->logicalIndex(visual);
+ d->updateSectionIndicator(d->section, d->firstPos);
+ }
+ return;
+ }
+ case QHeaderViewPrivate::SelectSections: {
+ int logical = logicalIndexAt(pos);
+ if (logical == d->pressed)
+ return; // nothing to do
+ else if (d->pressed != -1)
+ updateSection(d->pressed);
+ d->pressed = logical;
+ if (d->clickableSections && logical != -1) {
+ emit sectionEntered(d->pressed);
+ updateSection(d->pressed);
+ }
+ return;
+ }
+ case QHeaderViewPrivate::NoState: {
+#ifndef QT_NO_CURSOR
+ int handle = d->sectionHandleAt(pos);
+ bool hasCursor = testAttribute(Qt::WA_SetCursor);
+ if (handle != -1 && (resizeMode(handle) == Interactive)) {
+ if (!hasCursor)
+ setCursor(d->orientation == Qt::Horizontal ? Qt::SplitHCursor : Qt::SplitVCursor);
+ } else if (hasCursor) {
+ unsetCursor();
+ }
+#endif
+ return;
+ }
+ default:
+ break;
+ }
+}
+
+/*!
+ \reimp
+*/
+
+void QHeaderView::mouseReleaseEvent(QMouseEvent *e)
+{
+ Q_D(QHeaderView);
+ int pos = d->orientation == Qt::Horizontal ? e->x() : e->y();
+ switch (d->state) {
+ case QHeaderViewPrivate::MoveSection:
+ if (!d->sectionIndicator->isHidden()) { // moving
+ int from = visualIndex(d->section);
+ Q_ASSERT(from != -1);
+ int to = visualIndex(d->target);
+ Q_ASSERT(to != -1);
+ moveSection(from, to);
+ d->section = d->target = -1;
+ d->updateSectionIndicator(d->section, pos);
+ break;
+ } // not moving
+ case QHeaderViewPrivate::SelectSections:
+ if (!d->clickableSections) {
+ int section = logicalIndexAt(pos);
+ updateSection(section);
+ }
+ // fall through
+ case QHeaderViewPrivate::NoState:
+ if (d->clickableSections) {
+ int section = logicalIndexAt(pos);
+ if (section != -1 && section == d->pressed) {
+ d->flipSortIndicator(section);
+ emit sectionClicked(logicalIndexAt(pos));
+ }
+ if (d->pressed != -1)
+ updateSection(d->pressed);
+ }
+ break;
+ case QHeaderViewPrivate::ResizeSection:
+ d->originalSize = -1;
+ d->clearCascadingSections();
+ break;
+ default:
+ break;
+ }
+ d->state = QHeaderViewPrivate::NoState;
+ d->pressed = -1;
+}
+
+/*!
+ \reimp
+*/
+
+void QHeaderView::mouseDoubleClickEvent(QMouseEvent *e)
+{
+ Q_D(QHeaderView);
+ int pos = d->orientation == Qt::Horizontal ? e->x() : e->y();
+ int handle = d->sectionHandleAt(pos);
+ if (handle > -1 && resizeMode(handle) == Interactive) {
+ emit sectionHandleDoubleClicked(handle);
+#ifndef QT_NO_CURSOR
+ Qt::CursorShape splitCursor = (d->orientation == Qt::Horizontal)
+ ? Qt::SplitHCursor : Qt::SplitVCursor;
+ if (cursor().shape() == splitCursor) {
+ // signal handlers may have changed the section size
+ handle = d->sectionHandleAt(pos);
+ if (!(handle > -1 && resizeMode(handle) == Interactive))
+ setCursor(Qt::ArrowCursor);
+ }
+#endif
+ } else {
+ emit sectionDoubleClicked(logicalIndexAt(e->pos()));
+ }
+}
+
+/*!
+ \reimp
+*/
+
+bool QHeaderView::viewportEvent(QEvent *e)
+{
+ Q_D(QHeaderView);
+ switch (e->type()) {
+#ifndef QT_NO_TOOLTIP
+ case QEvent::ToolTip: {
+ QHelpEvent *he = static_cast<QHelpEvent*>(e);
+ int logical = logicalIndexAt(he->pos());
+ if (logical != -1) {
+ QVariant variant = d->model->headerData(logical, d->orientation, Qt::ToolTipRole);
+ if (variant.isValid()) {
+ QToolTip::showText(he->globalPos(), variant.toString(), this);
+ return true;
+ }
+ }
+ break; }
+#endif
+#ifndef QT_NO_WHATSTHIS
+ case QEvent::QueryWhatsThis: {
+ QHelpEvent *he = static_cast<QHelpEvent*>(e);
+ int logical = logicalIndexAt(he->pos());
+ if (logical != -1
+ && d->model->headerData(logical, d->orientation, Qt::WhatsThisRole).isValid())
+ return true;
+ break; }
+ case QEvent::WhatsThis: {
+ QHelpEvent *he = static_cast<QHelpEvent*>(e);
+ int logical = logicalIndexAt(he->pos());
+ if (logical != -1) {
+ QVariant whatsthis = d->model->headerData(logical, d->orientation,
+ Qt::WhatsThisRole);
+ if (whatsthis.isValid()) {
+ QWhatsThis::showText(he->globalPos(), whatsthis.toString(), this);
+ return true;
+ }
+ }
+ break; }
+#endif // QT_NO_WHATSTHIS
+#ifndef QT_NO_STATUSTIP
+ case QEvent::StatusTip: {
+ QHelpEvent *he = static_cast<QHelpEvent*>(e);
+ int logical = logicalIndexAt(he->pos());
+ if (logical != -1) {
+ QString statustip = d->model->headerData(logical, d->orientation,
+ Qt::StatusTipRole).toString();
+ if (!statustip.isEmpty())
+ setStatusTip(statustip);
+ }
+ return true; }
+#endif // QT_NO_STATUSTIP
+ case QEvent::Hide:
+ case QEvent::Show:
+ case QEvent::FontChange:
+ case QEvent::StyleChange:
+ d->invalidateCachedSizeHint();
+ resizeSections();
+ emit geometriesChanged();
+ break;
+ case QEvent::ContextMenu: {
+ d->state = QHeaderViewPrivate::NoState;
+ d->pressed = d->section = d->target = -1;
+ d->updateSectionIndicator(d->section, -1);
+ }
+ default:
+ break;
+ }
+ return QAbstractItemView::viewportEvent(e);
+}
+
+/*!
+ Paints the section specified by the given \a logicalIndex, using the given
+ \a painter and \a rect.
+
+ Normally, you do not have to call this function.
+*/
+
+void QHeaderView::paintSection(QPainter *painter, const QRect &rect, int logicalIndex) const
+{
+ Q_D(const QHeaderView);
+ if (!rect.isValid())
+ return;
+ // get the state of the section
+ QStyleOptionHeader opt;
+ initStyleOption(&opt);
+ QStyle::State state = QStyle::State_None;
+ if (isEnabled())
+ state |= QStyle::State_Enabled;
+ if (window()->isActiveWindow())
+ state |= QStyle::State_Active;
+ if (d->clickableSections) {
+ if (logicalIndex == d->hover)
+ state |= QStyle::State_MouseOver;
+ if (logicalIndex == d->pressed)
+ state |= QStyle::State_Sunken;
+ else if (d->highlightSelected) {
+ if (d->sectionIntersectsSelection(logicalIndex))
+ state |= QStyle::State_On;
+ if (d->isSectionSelected(logicalIndex))
+ state |= QStyle::State_Sunken;
+ }
+
+ }
+ if (isSortIndicatorShown() && sortIndicatorSection() == logicalIndex)
+ opt.sortIndicator = (sortIndicatorOrder() == Qt::AscendingOrder)
+ ? QStyleOptionHeader::SortDown : QStyleOptionHeader::SortUp;
+
+ // setup the style options structure
+ QVariant textAlignment = d->model->headerData(logicalIndex, d->orientation,
+ Qt::TextAlignmentRole);
+ opt.rect = rect;
+ opt.section = logicalIndex;
+ opt.state |= state;
+ opt.textAlignment = Qt::Alignment(textAlignment.isValid()
+ ? Qt::Alignment(textAlignment.toInt())
+ : d->defaultAlignment);
+
+ opt.iconAlignment = Qt::AlignVCenter;
+ opt.text = d->model->headerData(logicalIndex, d->orientation,
+ Qt::DisplayRole).toString();
+ if (d->textElideMode != Qt::ElideNone)
+ opt.text = opt.fontMetrics.elidedText(opt.text, d->textElideMode , rect.width() - 4);
+
+ QVariant variant = d->model->headerData(logicalIndex, d->orientation,
+ Qt::DecorationRole);
+ opt.icon = qvariant_cast<QIcon>(variant);
+ if (opt.icon.isNull())
+ opt.icon = qvariant_cast<QPixmap>(variant);
+ QVariant foregroundBrush = d->model->headerData(logicalIndex, d->orientation,
+ Qt::ForegroundRole);
+ if (qVariantCanConvert<QBrush>(foregroundBrush))
+ opt.palette.setBrush(QPalette::ButtonText, qvariant_cast<QBrush>(foregroundBrush));
+
+ QPointF oldBO = painter->brushOrigin();
+ QVariant backgroundBrush = d->model->headerData(logicalIndex, d->orientation,
+ Qt::BackgroundRole);
+ if (qVariantCanConvert<QBrush>(backgroundBrush)) {
+ opt.palette.setBrush(QPalette::Button, qvariant_cast<QBrush>(backgroundBrush));
+ opt.palette.setBrush(QPalette::Window, qvariant_cast<QBrush>(backgroundBrush));
+ painter->setBrushOrigin(opt.rect.topLeft());
+ }
+
+ // the section position
+ int visual = visualIndex(logicalIndex);
+ Q_ASSERT(visual != -1);
+ if (count() == 1)
+ opt.position = QStyleOptionHeader::OnlyOneSection;
+ else if (visual == 0)
+ opt.position = QStyleOptionHeader::Beginning;
+ else if (visual == count() - 1)
+ opt.position = QStyleOptionHeader::End;
+ else
+ opt.position = QStyleOptionHeader::Middle;
+ opt.orientation = d->orientation;
+ // the selected position
+ bool previousSelected = d->isSectionSelected(this->logicalIndex(visual - 1));
+ bool nextSelected = d->isSectionSelected(this->logicalIndex(visual + 1));
+ if (previousSelected && nextSelected)
+ opt.selectedPosition = QStyleOptionHeader::NextAndPreviousAreSelected;
+ else if (previousSelected)
+ opt.selectedPosition = QStyleOptionHeader::PreviousIsSelected;
+ else if (nextSelected)
+ opt.selectedPosition = QStyleOptionHeader::NextIsSelected;
+ else
+ opt.selectedPosition = QStyleOptionHeader::NotAdjacent;
+ // draw the section
+ style()->drawControl(QStyle::CE_Header, &opt, painter, this);
+
+ painter->setBrushOrigin(oldBO);
+}
+
+/*!
+ Returns the size of the contents of the section specified by the given
+ \a logicalIndex.
+
+ \sa defaultSectionSize()
+*/
+
+QSize QHeaderView::sectionSizeFromContents(int logicalIndex) const
+{
+ Q_D(const QHeaderView);
+ Q_ASSERT(logicalIndex >= 0);
+
+ // use SizeHintRole
+ QVariant variant = d->model->headerData(logicalIndex, d->orientation, Qt::SizeHintRole);
+ if (variant.isValid())
+ return qvariant_cast<QSize>(variant);
+
+ // otherwise use the contents
+ QStyleOptionHeader opt;
+ initStyleOption(&opt);
+ opt.section = logicalIndex;
+ QVariant var = d->model->headerData(logicalIndex, d->orientation,
+ Qt::FontRole);
+ QFont fnt;
+ if (var.isValid() && qVariantCanConvert<QFont>(var))
+ fnt = qvariant_cast<QFont>(var);
+ else
+ fnt = font();
+ fnt.setBold(true);
+ opt.fontMetrics = QFontMetrics(fnt);
+ opt.text = d->model->headerData(logicalIndex, d->orientation,
+ Qt::DisplayRole).toString();
+ variant = d->model->headerData(logicalIndex, d->orientation, Qt::DecorationRole);
+ opt.icon = qvariant_cast<QIcon>(variant);
+ if (opt.icon.isNull())
+ opt.icon = qvariant_cast<QPixmap>(variant);
+ QSize size = style()->sizeFromContents(QStyle::CT_HeaderSection, &opt, QSize(), this);
+ if (isSortIndicatorShown() && sortIndicatorSection() == logicalIndex) {
+ int margin = style()->pixelMetric(QStyle::PM_HeaderMargin, &opt, this);
+ if (d->orientation == Qt::Horizontal)
+ size.rwidth() += size.height() + margin;
+ else
+ size.rheight() += size.width() + margin;
+ }
+ return size;
+}
+
+/*!
+ Returns the horizontal offset of the header. This is 0 for vertical
+ headers.
+
+ \sa offset()
+*/
+
+int QHeaderView::horizontalOffset() const
+{
+ Q_D(const QHeaderView);
+ if (d->orientation == Qt::Horizontal)
+ return d->offset;
+ return 0;
+}
+
+/*!
+ Returns the vertical offset of the header. This is 0 for horizontal
+ headers.
+
+ \sa offset()
+*/
+
+int QHeaderView::verticalOffset() const
+{
+ Q_D(const QHeaderView);
+ if (d->orientation == Qt::Vertical)
+ return d->offset;
+ return 0;
+}
+
+/*!
+ \reimp
+ \internal
+*/
+
+void QHeaderView::updateGeometries()
+{
+ Q_D(QHeaderView);
+ d->layoutChildren();
+ if (d->hasAutoResizeSections())
+ resizeSections();
+}
+
+/*!
+ \reimp
+ \internal
+*/
+
+void QHeaderView::scrollContentsBy(int dx, int dy)
+{
+ Q_D(QHeaderView);
+ d->scrollDirtyRegion(dx, dy);
+}
+
+/*!
+ \reimp
+ \internal
+*/
+void QHeaderView::dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight)
+{
+ Q_D(QHeaderView);
+ d->invalidateCachedSizeHint();
+ if (d->hasAutoResizeSections()) {
+ bool resizeRequired = d->globalResizeMode == ResizeToContents;
+ int first = orientation() == Qt::Horizontal ? topLeft.column() : topLeft.row();
+ int last = orientation() == Qt::Horizontal ? bottomRight.column() : bottomRight.row();
+ for (int i = first; i <= last && !resizeRequired; ++i)
+ resizeRequired = (resizeRequired && resizeMode(i));
+ if (resizeRequired)
+ d->doDelayedResizeSections();
+ }
+}
+
+/*!
+ \reimp
+ \internal
+
+ Empty implementation because the header doesn't show QModelIndex items.
+*/
+void QHeaderView::rowsInserted(const QModelIndex &, int, int)
+{
+ // do nothing
+}
+
+/*!
+ \reimp
+ \internal
+
+ Empty implementation because the header doesn't show QModelIndex items.
+*/
+
+QRect QHeaderView::visualRect(const QModelIndex &) const
+{
+ return QRect();
+}
+
+/*!
+ \reimp
+ \internal
+
+ Empty implementation because the header doesn't show QModelIndex items.
+*/
+
+void QHeaderView::scrollTo(const QModelIndex &, ScrollHint)
+{
+ // do nothing - the header only displays sections
+}
+
+/*!
+ \reimp
+ \internal
+
+ Empty implementation because the header doesn't show QModelIndex items.
+*/
+
+QModelIndex QHeaderView::indexAt(const QPoint &) const
+{
+ return QModelIndex();
+}
+
+/*!
+ \reimp
+ \internal
+
+ Empty implementation because the header doesn't show QModelIndex items.
+*/
+
+bool QHeaderView::isIndexHidden(const QModelIndex &) const
+{
+ return true; // the header view has no items, just sections
+}
+
+/*!
+ \reimp
+ \internal
+
+ Empty implementation because the header doesn't show QModelIndex items.
+*/
+
+QModelIndex QHeaderView::moveCursor(CursorAction, Qt::KeyboardModifiers)
+{
+ return QModelIndex();
+}
+
+/*!
+ \reimp
+
+ Selects the items in the given \a rect according to the specified
+ \a flags.
+
+ The base class implementation does nothing.
+*/
+
+void QHeaderView::setSelection(const QRect&, QItemSelectionModel::SelectionFlags)
+{
+ // do nothing
+}
+
+/*!
+ \internal
+*/
+
+QRegion QHeaderView::visualRegionForSelection(const QItemSelection &selection) const
+{
+ Q_D(const QHeaderView);
+ const int max = d->modelSectionCount();
+ if (d->orientation == Qt::Horizontal) {
+ int left = max;
+ int right = 0;
+ int rangeLeft, rangeRight;
+
+ for (int i = 0; i < selection.count(); ++i) {
+ QItemSelectionRange r = selection.at(i);
+ if (r.parent().isValid() || !r.isValid())
+ continue; // we only know about toplevel items and we don't want invalid ranges
+ // FIXME an item inside the range may be the leftmost or rightmost
+ rangeLeft = visualIndex(r.left());
+ if (rangeLeft == -1) // in some cases users may change the selections
+ continue; // before we have a chance to do the layout
+ rangeRight = visualIndex(r.right());
+ if (rangeRight == -1) // in some cases users may change the selections
+ continue; // before we have a chance to do the layout
+ if (rangeLeft < left)
+ left = rangeLeft;
+ if (rangeRight > right)
+ right = rangeRight;
+ }
+
+ int logicalLeft = logicalIndex(left);
+ int logicalRight = logicalIndex(right);
+
+ if (logicalLeft < 0 || logicalLeft >= count() ||
+ logicalRight < 0 || logicalRight >= count())
+ return QRegion();
+
+ int leftPos = sectionViewportPosition(logicalLeft);
+ int rightPos = sectionViewportPosition(logicalRight);
+ rightPos += sectionSize(logicalRight);
+ return QRect(leftPos, 0, rightPos - leftPos, height());
+ }
+ // orientation() == Qt::Vertical
+ int top = max;
+ int bottom = 0;
+ int rangeTop, rangeBottom;
+
+ for (int i = 0; i < selection.count(); ++i) {
+ QItemSelectionRange r = selection.at(i);
+ if (r.parent().isValid() || !r.isValid())
+ continue; // we only know about toplevel items
+ // FIXME an item inside the range may be the leftmost or rightmost
+ rangeTop = visualIndex(r.top());
+ if (rangeTop == -1) // in some cases users may change the selections
+ continue; // before we have a chance to do the layout
+ rangeBottom = visualIndex(r.bottom());
+ if (rangeBottom == -1) // in some cases users may change the selections
+ continue; // before we have a chance to do the layout
+ if (rangeTop < top)
+ top = rangeTop;
+ if (rangeBottom > bottom)
+ bottom = rangeBottom;
+ }
+
+ int logicalTop = logicalIndex(top);
+ int logicalBottom = logicalIndex(bottom);
+
+ if (logicalTop == -1 || logicalBottom == -1)
+ return QRect();
+
+ int topPos = sectionViewportPosition(logicalTop);
+ int bottomPos = sectionViewportPosition(logicalBottom) + sectionSize(logicalBottom);
+
+ return QRect(0, topPos, width(), bottomPos - topPos);
+}
+
+
+// private implementation
+
+int QHeaderViewPrivate::sectionHandleAt(int position)
+{
+ Q_Q(QHeaderView);
+ int visual = q->visualIndexAt(position);
+ if (visual == -1)
+ return -1;
+ int log = logicalIndex(visual);
+ int pos = q->sectionViewportPosition(log);
+ int grip = q->style()->pixelMetric(QStyle::PM_HeaderGripMargin, 0, q);
+
+ bool atLeft = position < pos + grip;
+ bool atRight = (position > pos + q->sectionSize(log) - grip);
+ if (reverse())
+ qSwap(atLeft, atRight);
+
+ if (atLeft) {
+ //grip at the beginning of the section
+ while(visual > -1) {
+ int logical = q->logicalIndex(--visual);
+ if (!q->isSectionHidden(logical))
+ return logical;
+ }
+ } else if (atRight) {
+ //grip at the end of the section
+ return log;
+ }
+ return -1;
+}
+
+void QHeaderViewPrivate::setupSectionIndicator(int section, int position)
+{
+ Q_Q(QHeaderView);
+ if (!sectionIndicator) {
+ sectionIndicator = new QLabel(viewport);
+ }
+
+ int x, y, w, h;
+ int p = q->sectionViewportPosition(section);
+ if (orientation == Qt::Horizontal) {
+ x = p;
+ y = 0;
+ w = q->sectionSize(section);
+ h = viewport->height();
+ } else {
+ x = 0;
+ y = p;
+ w = viewport->width();
+ h = q->sectionSize(section);
+ }
+ sectionIndicator->resize(w, h);
+
+ QPixmap pm(w, h);
+ pm.fill(QColor(0, 0, 0, 45));
+ QRect rect(0, 0, w, h);
+
+ QPainter painter(&pm);
+ painter.setOpacity(0.75);
+ q->paintSection(&painter, rect, section);
+ painter.end();
+
+ sectionIndicator->setPixmap(pm);
+ sectionIndicatorOffset = position - qMax(p, 0);
+}
+
+void QHeaderViewPrivate::updateSectionIndicator(int section, int position)
+{
+ if (!sectionIndicator)
+ return;
+
+ if (section == -1 || target == -1) {
+ sectionIndicator->hide();
+ return;
+ }
+
+ if (orientation == Qt::Horizontal)
+ sectionIndicator->move(position - sectionIndicatorOffset, 0);
+ else
+ sectionIndicator->move(0, position - sectionIndicatorOffset);
+
+ sectionIndicator->show();
+}
+
+/*!
+ Initialize \a option with the values from this QHeaderView. This method is
+ useful for subclasses when they need a QStyleOptionHeader, but do not want
+ to fill in all the information themselves.
+
+ \sa QStyleOption::initFrom()
+*/
+void QHeaderView::initStyleOption(QStyleOptionHeader *option) const
+{
+ Q_D(const QHeaderView);
+ option->initFrom(this);
+ option->state = QStyle::State_None | QStyle::State_Raised;
+ option->orientation = d->orientation;
+ if (d->orientation == Qt::Horizontal)
+ option->state |= QStyle::State_Horizontal;
+ if (isEnabled())
+ option->state |= QStyle::State_Enabled;
+ option->section = 0;
+}
+
+bool QHeaderViewPrivate::isSectionSelected(int section) const
+{
+ int i = section * 2;
+ if (i < 0 || i >= sectionSelected.count())
+ return false;
+ if (sectionSelected.testBit(i)) // if the value was cached
+ return sectionSelected.testBit(i + 1);
+ bool s = false;
+ if (orientation == Qt::Horizontal)
+ s = isColumnSelected(section);
+ else
+ s = isRowSelected(section);
+ sectionSelected.setBit(i + 1, s); // selection state
+ sectionSelected.setBit(i, true); // cache state
+ return s;
+}
+
+/*!
+ \internal
+ Returns the last visible (ie. not hidden) visual index
+*/
+int QHeaderViewPrivate::lastVisibleVisualIndex() const
+{
+ Q_Q(const QHeaderView);
+ for (int visual = q->count()-1; visual >= 0; --visual) {
+ if (!q->isSectionHidden(q->logicalIndex(visual)))
+ return visual;
+ }
+
+ //default value if no section is actually visible
+ return -1;
+}
+
+/*!
+ \internal
+ Go through and resize all of the sections applying stretchLastSection,
+ manualy stretches, sizes, and useGlobalMode.
+
+ The different resize modes are:
+ Interactive - the user decides the size
+ Stretch - take up whatever space is left
+ Fixed - the size is set programmatically outside the header
+ ResizeToContentes - the size is set based on the contents of the row or column in the parent view
+
+ The resize mode will not affect the last section if stretchLastSection is true.
+*/
+void QHeaderViewPrivate::resizeSections(QHeaderView::ResizeMode globalMode, bool useGlobalMode)
+{
+ Q_Q(QHeaderView);
+
+ executePostedLayout();
+ if (sectionCount == 0)
+ return;
+ invalidateCachedSizeHint();
+
+ // find stretchLastSection if we have it
+ int stretchSection = -1;
+ if (stretchLastSection && !useGlobalMode) {
+ for (int i = sectionCount - 1; i >= 0; --i) {
+ if (!isVisualIndexHidden(i)) {
+ stretchSection = i;
+ break;
+ }
+ }
+ }
+
+ // count up the number of strected sections and how much space left for them
+ int lengthToStrech = (orientation == Qt::Horizontal ? viewport->width() : viewport->height());
+ int numberOfStretchedSections = 0;
+ QList<int> section_sizes;
+ for (int i = 0; i < sectionCount; ++i) {
+ if (isVisualIndexHidden(i))
+ continue;
+
+ QHeaderView::ResizeMode resizeMode;
+ if (useGlobalMode && (i != stretchSection))
+ resizeMode = globalMode;
+ else
+ resizeMode = (i == stretchSection ? QHeaderView::Stretch : visualIndexResizeMode(i));
+
+ if (resizeMode == QHeaderView::Stretch) {
+ ++numberOfStretchedSections;
+ section_sizes.append(headerSectionSize(i));
+ continue;
+ }
+
+ // because it isn't stretch, determine its width and remove that from lengthToStrech
+ int sectionSize = 0;
+ if (resizeMode == QHeaderView::Interactive || resizeMode == QHeaderView::Fixed) {
+ sectionSize = headerSectionSize(i);
+ } else { // resizeMode == QHeaderView::ResizeToContents
+ int logicalIndex = q->logicalIndex(i);
+ sectionSize = qMax(viewSectionSizeHint(logicalIndex),
+ q->sectionSizeHint(logicalIndex));
+ }
+ section_sizes.append(sectionSize);
+ lengthToStrech -= sectionSize;
+ }
+
+ // calculate the new length for all of the stretched sections
+ int stretchSectionLength = -1;
+ int pixelReminder = 0;
+ if (numberOfStretchedSections > 0 && lengthToStrech > 0) { // we have room to stretch in
+ int hintLengthForEveryStretchedSection = lengthToStrech / numberOfStretchedSections;
+ stretchSectionLength = qMax(hintLengthForEveryStretchedSection, q->minimumSectionSize());
+ pixelReminder = lengthToStrech % numberOfStretchedSections;
+ }
+
+ int spanStartSection = 0;
+ int previousSectionLength = 0;
+ const int lastVisibleSection = lastVisibleVisualIndex();
+
+ QHeaderView::ResizeMode previousSectionResizeMode = QHeaderView::Interactive;
+
+ // resize each section along the total length
+ for (int i = 0; i < sectionCount; ++i) {
+ int oldSectionLength = headerSectionSize(i);
+ int newSectionLength = -1;
+ QHeaderView::ResizeMode newSectionResizeMode = headerSectionResizeMode(i);
+
+ if (isVisualIndexHidden(i)) {
+ newSectionLength = 0;
+ } else {
+ QHeaderView::ResizeMode resizeMode;
+ if (useGlobalMode)
+ resizeMode = globalMode;
+ else
+ resizeMode = (i == stretchSection
+ ? QHeaderView::Stretch
+ : visualIndexResizeMode(i));
+ if (resizeMode == QHeaderView::Stretch && stretchSectionLength != -1) {
+ if (i == lastVisibleSection)
+ newSectionLength = qMax(stretchSectionLength, lastSectionSize);
+ else
+ newSectionLength = stretchSectionLength;
+ if (pixelReminder > 0) {
+ newSectionLength += 1;
+ --pixelReminder;
+ }
+ section_sizes.removeFirst();
+ } else {
+ newSectionLength = section_sizes.front();
+ section_sizes.removeFirst();
+ }
+ }
+
+ //Q_ASSERT(newSectionLength > 0);
+ if ((previousSectionResizeMode != newSectionResizeMode
+ || previousSectionLength != newSectionLength) && i > 0) {
+ int spanLength = (i - spanStartSection) * previousSectionLength;
+ createSectionSpan(spanStartSection, i - 1, spanLength, previousSectionResizeMode);
+ //Q_ASSERT(headerLength() == length);
+ spanStartSection = i;
+ }
+
+ if (newSectionLength != oldSectionLength)
+ emit q->sectionResized(logicalIndex(i), oldSectionLength, newSectionLength);
+
+ previousSectionLength = newSectionLength;
+ previousSectionResizeMode = newSectionResizeMode;
+ }
+
+ createSectionSpan(spanStartSection, sectionCount - 1,
+ (sectionCount - spanStartSection) * previousSectionLength,
+ previousSectionResizeMode);
+ //Q_ASSERT(headerLength() == length);
+
+ viewport->update();
+}
+
+void QHeaderViewPrivate::createSectionSpan(int start, int end, int size, QHeaderView::ResizeMode mode)
+{
+ // ### the code for merging spans does not merge at all opertuneties
+ // ### what if the number of sections is reduced ?
+
+ SectionSpan span(size, (end - start) + 1, mode);
+ int start_section = 0;
+#ifndef QT_NO_DEBUG
+ int initial_section_count = headerSectionCount(); // ### debug code
+#endif
+
+ QList<int> spansToRemove;
+ for (int i = 0; i < sectionSpans.count(); ++i) {
+ int end_section = start_section + sectionSpans.at(i).count - 1;
+ int section_count = sectionSpans.at(i).count;
+ if (start <= start_section && end > end_section) {
+ // the existing span is entirely coveded by the new span
+ spansToRemove.append(i);
+ } else if (start < start_section && end >= end_section) {
+ // the existing span is entirely coveded by the new span
+ spansToRemove.append(i);
+ } else if (start == start_section && end == end_section) {
+ // the new span is covered by an existin span
+ length -= sectionSpans.at(i).size;
+ length += size;
+ sectionSpans[i].size = size;
+ sectionSpans[i].resizeMode = mode;
+ // ### check if we can merge the section with any of its neighbours
+ removeSpans(spansToRemove);
+ Q_ASSERT(initial_section_count == headerSectionCount());
+ return;
+ } else if (start > start_section && end < end_section) {
+ if (sectionSpans.at(i).sectionSize() == span.sectionSize()
+ && sectionSpans.at(i).resizeMode == span.resizeMode) {
+ Q_ASSERT(initial_section_count == headerSectionCount());
+ return;
+ }
+ // the new span is in the middle of the old span, so we have to split it
+ length -= sectionSpans.at(i).size;
+ int section_size = sectionSpans.at(i).sectionSize();
+#ifndef QT_NO_DEBUG
+ int span_count = sectionSpans.at(i).count;
+#endif
+ QHeaderView::ResizeMode span_mode = sectionSpans.at(i).resizeMode;
+ // first span
+ int first_span_count = start - start_section;
+ int first_span_size = section_size * first_span_count;
+ sectionSpans[i].count = first_span_count;
+ sectionSpans[i].size = first_span_size;
+ sectionSpans[i].resizeMode = span_mode;
+ length += first_span_size;
+ // middle span (the new span)
+#ifndef QT_NO_DEBUG
+ int mid_span_count = span.count;
+#endif
+ int mid_span_size = span.size;
+ sectionSpans.insert(i + 1, span);
+ length += mid_span_size;
+ // last span
+ int last_span_count = end_section - end;
+ int last_span_size = section_size * last_span_count;
+ sectionSpans.insert(i + 2, SectionSpan(last_span_size, last_span_count, span_mode));
+ length += last_span_size;
+ Q_ASSERT(span_count == first_span_count + mid_span_count + last_span_count);
+ removeSpans(spansToRemove);
+ Q_ASSERT(initial_section_count == headerSectionCount());
+ return;
+ } else if (start > start_section && start <= end_section && end >= end_section) {
+ // the new span covers the last part of the existing span
+ length -= sectionSpans.at(i).size;
+ int removed_count = (end_section - start + 1);
+ int span_count = sectionSpans.at(i).count - removed_count;
+ int section_size = sectionSpans.at(i).sectionSize();
+ int span_size = section_size * span_count;
+ sectionSpans[i].count = span_count;
+ sectionSpans[i].size = span_size;
+ length += span_size;
+ if (end == end_section) {
+ sectionSpans.insert(i + 1, span); // insert after
+ length += span.size;
+ removeSpans(spansToRemove);
+ Q_ASSERT(initial_section_count == headerSectionCount());
+ return;
+ }
+ } else if (end < end_section && end >= start_section && start <= start_section) {
+ // the new span covers the first part of the existing span
+ length -= sectionSpans.at(i).size;
+ int removed_count = (end - start_section + 1);
+ int section_size = sectionSpans.at(i).sectionSize();
+ int span_count = sectionSpans.at(i).count - removed_count;
+ int span_size = section_size * span_count;
+ sectionSpans[i].count = span_count;
+ sectionSpans[i].size = span_size;
+ length += span_size;
+ sectionSpans.insert(i, span); // insert before
+ length += span.size;
+ removeSpans(spansToRemove);
+ Q_ASSERT(initial_section_count == headerSectionCount());
+ return;
+ }
+ start_section += section_count;
+ }
+
+ // ### adding and removing _ sections_ in addition to spans
+ // ### add some more checks here
+
+ if (spansToRemove.isEmpty()) {
+ if (!sectionSpans.isEmpty()
+ && sectionSpans.last().sectionSize() == span.sectionSize()
+ && sectionSpans.last().resizeMode == span.resizeMode) {
+ length += span.size;
+ int last = sectionSpans.count() - 1;
+ sectionSpans[last].count += span.count;
+ sectionSpans[last].size += span.size;
+ sectionSpans[last].resizeMode = span.resizeMode;
+ } else {
+ length += span.size;
+ sectionSpans.append(span);
+ }
+ } else {
+ removeSpans(spansToRemove);
+ length += span.size;
+ sectionSpans.insert(spansToRemove.first(), span);
+ //Q_ASSERT(initial_section_count == headerSectionCount());
+ }
+}
+
+void QHeaderViewPrivate::removeSectionsFromSpans(int start, int end)
+{
+ // remove sections
+ int start_section = 0;
+ QList<int> spansToRemove;
+ for (int i = 0; i < sectionSpans.count(); ++i) {
+ int end_section = start_section + sectionSpans.at(i).count - 1;
+ int section_size = sectionSpans.at(i).sectionSize();
+ int section_count = sectionSpans.at(i).count;
+ if (start <= start_section && end >= end_section) {
+ // the change covers the entire span
+ spansToRemove.append(i);
+ if (end == end_section)
+ break;
+ } else if (start > start_section && end < end_section) {
+ // all the removed sections are inside the span
+ int change = (end - start + 1);
+ sectionSpans[i].count -= change;
+ sectionSpans[i].size = section_size * sectionSpans.at(i).count;
+ length -= (change * section_size);
+ break;
+ } else if (start >= start_section && start <= end_section) {
+ // the some of the removed sections are inside the span,at the end
+ int change = qMin(end_section - start + 1, end - start + 1);
+ sectionSpans[i].count -= change;
+ sectionSpans[i].size = section_size * sectionSpans.at(i).count;
+ start += change;
+ length -= (change * section_size);
+ // the change affects several spans
+ } else if (end >= start_section && end <= end_section) {
+ // the some of the removed sections are inside the span, at the beginning
+ int change = qMin((end - start_section + 1), end - start + 1);
+ sectionSpans[i].count -= change;
+ sectionSpans[i].size = section_size * sectionSpans.at(i).count;
+ length -= (change * section_size);
+ break;
+ }
+ start_section += section_count;
+ }
+
+ for (int i = spansToRemove.count() - 1; i >= 0; --i) {
+ int s = spansToRemove.at(i);
+ length -= sectionSpans.at(s).size;
+ sectionSpans.remove(s);
+ // ### merge remaining spans
+ }
+}
+
+void QHeaderViewPrivate::clear()
+{
+ if (state != NoClear) {
+ length = 0;
+ sectionCount = 0;
+ visualIndices.clear();
+ logicalIndices.clear();
+ sectionSelected.clear();
+ sectionHidden.clear();
+ hiddenSectionSize.clear();
+ sectionSpans.clear();
+ }
+}
+
+void QHeaderViewPrivate::flipSortIndicator(int section)
+{
+ Q_Q(QHeaderView);
+ bool ascending = (sortIndicatorSection != section
+ || sortIndicatorOrder == Qt::DescendingOrder);
+ q->setSortIndicator(section, ascending ? Qt::AscendingOrder : Qt::DescendingOrder);
+}
+
+void QHeaderViewPrivate::cascadingResize(int visual, int newSize)
+{
+ Q_Q(QHeaderView);
+ const int minimumSize = q->minimumSectionSize();
+ const int oldSize = headerSectionSize(visual);
+ int delta = newSize - oldSize;
+
+ if (delta > 0) { // larger
+ bool sectionResized = false;
+
+ // restore old section sizes
+ for (int i = firstCascadingSection; i < visual; ++i) {
+ if (cascadingSectionSize.contains(i)) {
+ int currentSectionSize = headerSectionSize(i);
+ int originalSectionSize = cascadingSectionSize.value(i);
+ if (currentSectionSize < originalSectionSize) {
+ int newSectionSize = currentSectionSize + delta;
+ resizeSectionSpan(i, currentSectionSize, newSectionSize);
+ if (newSectionSize >= originalSectionSize && false)
+ cascadingSectionSize.remove(i); // the section is now restored
+ sectionResized = true;
+ break;
+ }
+ }
+
+ }
+
+ // resize the section
+ if (!sectionResized) {
+ newSize = qMax(newSize, minimumSize);
+ if (oldSize != newSize)
+ resizeSectionSpan(visual, oldSize, newSize);
+ }
+
+ // cascade the section size change
+ for (int i = visual + 1; i < sectionCount; ++i) {
+ if (!sectionIsCascadable(i))
+ continue;
+ int currentSectionSize = headerSectionSize(i);
+ if (currentSectionSize <= minimumSize)
+ continue;
+ int newSectionSize = qMax(currentSectionSize - delta, minimumSize);
+ //qDebug() << "### cascading to" << i << newSectionSize - currentSectionSize << delta;
+ resizeSectionSpan(i, currentSectionSize, newSectionSize);
+ saveCascadingSectionSize(i, currentSectionSize);
+ delta = delta - (currentSectionSize - newSectionSize);
+ //qDebug() << "new delta" << delta;
+ //if (newSectionSize != minimumSize)
+ if (delta <= 0)
+ break;
+ }
+ } else { // smaller
+ bool sectionResized = false;
+
+ // restore old section sizes
+ for (int i = lastCascadingSection; i > visual; --i) {
+ if (!cascadingSectionSize.contains(i))
+ continue;
+ int currentSectionSize = headerSectionSize(i);
+ int originalSectionSize = cascadingSectionSize.value(i);
+ if (currentSectionSize >= originalSectionSize)
+ continue;
+ int newSectionSize = currentSectionSize - delta;
+ resizeSectionSpan(i, currentSectionSize, newSectionSize);
+ if (newSectionSize >= originalSectionSize && false) {
+ //qDebug() << "section" << i << "restored to" << originalSectionSize;
+ cascadingSectionSize.remove(i); // the section is now restored
+ }
+ sectionResized = true;
+ break;
+ }
+
+ // resize the section
+ resizeSectionSpan(visual, oldSize, qMax(newSize, minimumSize));
+
+ // cascade the section size change
+ if (delta < 0 && newSize < minimumSize) {
+ for (int i = visual - 1; i >= 0; --i) {
+ if (!sectionIsCascadable(i))
+ continue;
+ int sectionSize = headerSectionSize(i);
+ if (sectionSize <= minimumSize)
+ continue;
+ resizeSectionSpan(i, sectionSize, qMax(sectionSize + delta, minimumSize));
+ saveCascadingSectionSize(i, sectionSize);
+ break;
+ }
+ }
+
+ // let the next section get the space from the resized section
+ if (!sectionResized) {
+ for (int i = visual + 1; i < sectionCount; ++i) {
+ if (!sectionIsCascadable(i))
+ continue;
+ int currentSectionSize = headerSectionSize(i);
+ int newSectionSize = qMax(currentSectionSize - delta, minimumSize);
+ resizeSectionSpan(i, currentSectionSize, newSectionSize);
+ break;
+ }
+ }
+ }
+
+ if (hasAutoResizeSections())
+ doDelayedResizeSections();
+
+ viewport->update();
+}
+
+void QHeaderViewPrivate::resizeSectionSpan(int visualIndex, int oldSize, int newSize)
+{
+ Q_Q(QHeaderView);
+ QHeaderView::ResizeMode mode = headerSectionResizeMode(visualIndex);
+ createSectionSpan(visualIndex, visualIndex, newSize, mode);
+ emit q->sectionResized(logicalIndex(visualIndex), oldSize, newSize);
+}
+
+int QHeaderViewPrivate::headerSectionSize(int visual) const
+{
+ // ### stupid iteration
+ int section_start = 0;
+ const int sectionSpansCount = sectionSpans.count();
+ for (int i = 0; i < sectionSpansCount; ++i) {
+ const QHeaderViewPrivate::SectionSpan &currentSection = sectionSpans.at(i);
+ int section_end = section_start + currentSection.count - 1;
+ if (visual >= section_start && visual <= section_end)
+ return currentSection.sectionSize();
+ section_start = section_end + 1;
+ }
+ return -1;
+}
+
+int QHeaderViewPrivate::headerSectionPosition(int visual) const
+{
+ // ### stupid iteration
+ int section_start = 0;
+ int span_position = 0;
+ const int sectionSpansCount = sectionSpans.count();
+ for (int i = 0; i < sectionSpansCount; ++i) {
+ const QHeaderViewPrivate::SectionSpan &currentSection = sectionSpans.at(i);
+ int section_end = section_start + currentSection.count - 1;
+ if (visual >= section_start && visual <= section_end)
+ return span_position + (visual - section_start) * currentSection.sectionSize();
+ section_start = section_end + 1;
+ span_position += currentSection.size;
+ }
+ return -1;
+}
+
+int QHeaderViewPrivate::headerVisualIndexAt(int position) const
+{
+ // ### stupid iteration
+ int span_start_section = 0;
+ int span_position = 0;
+ const int sectionSpansCount = sectionSpans.count();
+ for (int i = 0; i < sectionSpansCount; ++i) {
+ const QHeaderViewPrivate::SectionSpan &currentSection = sectionSpans.at(i);
+ int next_span_start_section = span_start_section + currentSection.count;
+ int next_span_position = span_position + currentSection.size;
+ if (position == span_position)
+ return span_start_section; // spans with no size
+ if (position > span_position && position < next_span_position) {
+ int position_in_span = position - span_position;
+ return span_start_section + (position_in_span / currentSection.sectionSize());
+ }
+ span_start_section = next_span_start_section;
+ span_position = next_span_position;
+ }
+ return -1;
+}
+
+void QHeaderViewPrivate::setHeaderSectionResizeMode(int visual, QHeaderView::ResizeMode mode)
+{
+ int size = headerSectionSize(visual);
+ createSectionSpan(visual, visual, size, mode);
+}
+
+QHeaderView::ResizeMode QHeaderViewPrivate::headerSectionResizeMode(int visual) const
+{
+ int span = sectionSpanIndex(visual);
+ if (span == -1)
+ return globalResizeMode;
+ return sectionSpans.at(span).resizeMode;
+}
+
+void QHeaderViewPrivate::setGlobalHeaderResizeMode(QHeaderView::ResizeMode mode)
+{
+ globalResizeMode = mode;
+ for (int i = 0; i < sectionSpans.count(); ++i)
+ sectionSpans[i].resizeMode = mode;
+}
+
+int QHeaderViewPrivate::viewSectionSizeHint(int logical) const
+{
+ Q_Q(const QHeaderView);
+ if (QAbstractItemView *parent = qobject_cast<QAbstractItemView*>(q->parent())) {
+ return (orientation == Qt::Horizontal
+ ? parent->sizeHintForColumn(logical)
+ : parent->sizeHintForRow(logical));
+ }
+ return 0;
+}
+
+int QHeaderViewPrivate::adjustedVisualIndex(int visualIndex) const
+{
+ if (hiddenSectionSize.count() > 0) {
+ int adjustedVisualIndex = visualIndex;
+ int currentVisualIndex = 0;
+ for (int i = 0; i < sectionSpans.count(); ++i) {
+ if (sectionSpans.at(i).size == 0)
+ adjustedVisualIndex += sectionSpans.at(i).count;
+ else
+ currentVisualIndex += sectionSpans.at(i).count;
+ if (currentVisualIndex >= visualIndex)
+ break;
+ }
+ visualIndex = adjustedVisualIndex;
+ }
+ return visualIndex;
+}
+
+#ifndef QT_NO_DATASTREAM
+void QHeaderViewPrivate::write(QDataStream &out) const
+{
+ out << int(orientation);
+ out << int(sortIndicatorOrder);
+ out << sortIndicatorSection;
+ out << sortIndicatorShown;
+
+ out << visualIndices;
+ out << logicalIndices;
+
+ out << sectionHidden;
+ out << hiddenSectionSize;
+
+ out << length;
+ out << sectionCount;
+ out << movableSections;
+ out << clickableSections;
+ out << highlightSelected;
+ out << stretchLastSection;
+ out << cascadingResizing;
+ out << stretchSections;
+ out << contentsSections;
+ out << defaultSectionSize;
+ out << minimumSectionSize;
+
+ out << int(defaultAlignment);
+ out << int(globalResizeMode);
+
+ out << sectionSpans;
+}
+
+bool QHeaderViewPrivate::read(QDataStream &in)
+{
+ int orient, order, align, global;
+ in >> orient;
+ orientation = (Qt::Orientation)orient;
+
+ in >> order;
+ sortIndicatorOrder = (Qt::SortOrder)order;
+
+ in >> sortIndicatorSection;
+ in >> sortIndicatorShown;
+
+ in >> visualIndices;
+ in >> logicalIndices;
+
+ in >> sectionHidden;
+ in >> hiddenSectionSize;
+
+ in >> length;
+ in >> sectionCount;
+ in >> movableSections;
+ in >> clickableSections;
+ in >> highlightSelected;
+ in >> stretchLastSection;
+ in >> cascadingResizing;
+ in >> stretchSections;
+ in >> contentsSections;
+ in >> defaultSectionSize;
+ in >> minimumSectionSize;
+
+ in >> align;
+ defaultAlignment = (Qt::Alignment)align;
+
+ in >> global;
+ globalResizeMode = (QHeaderView::ResizeMode)global;
+
+ in >> sectionSpans;
+
+ return true;
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_DATASTREAEM
+
+#endif // QT_NO_ITEMVIEWS
+
+#include "moc_qheaderview.cpp"
diff --git a/src/gui/itemviews/qheaderview.h b/src/gui/itemviews/qheaderview.h
new file mode 100644
index 0000000000..47f5269ae4
--- /dev/null
+++ b/src/gui/itemviews/qheaderview.h
@@ -0,0 +1,251 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QHEADERVIEW_H
+#define QHEADERVIEW_H
+
+#include <QtGui/qabstractitemview.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+#ifndef QT_NO_ITEMVIEWS
+
+class QHeaderViewPrivate;
+class QStyleOptionHeader;
+
+class Q_GUI_EXPORT QHeaderView : public QAbstractItemView
+{
+ Q_OBJECT
+ Q_PROPERTY(bool showSortIndicator READ isSortIndicatorShown WRITE setSortIndicatorShown)
+ Q_PROPERTY(bool highlightSections READ highlightSections WRITE setHighlightSections)
+ Q_PROPERTY(bool stretchLastSection READ stretchLastSection WRITE setStretchLastSection)
+ Q_PROPERTY(bool cascadingSectionResizes READ cascadingSectionResizes WRITE setCascadingSectionResizes)
+ Q_PROPERTY(int defaultSectionSize READ defaultSectionSize WRITE setDefaultSectionSize)
+ Q_PROPERTY(int minimumSectionSize READ minimumSectionSize WRITE setMinimumSectionSize)
+ Q_PROPERTY(Qt::Alignment defaultAlignment READ defaultAlignment WRITE setDefaultAlignment)
+ Q_ENUMS(ResizeMode)
+
+public:
+
+ enum ResizeMode
+ {
+ Interactive,
+ Stretch,
+ Fixed,
+ ResizeToContents,
+ Custom = Fixed
+ };
+
+ explicit QHeaderView(Qt::Orientation orientation, QWidget *parent = 0);
+ virtual ~QHeaderView();
+
+ void setModel(QAbstractItemModel *model);
+
+ Qt::Orientation orientation() const;
+ int offset() const;
+ int length() const;
+ QSize sizeHint() const;
+ int sectionSizeHint(int logicalIndex) const;
+
+ int visualIndexAt(int position) const;
+ int logicalIndexAt(int position) const;
+
+ inline int logicalIndexAt(int x, int y) const;
+ inline int logicalIndexAt(const QPoint &pos) const;
+
+ int sectionSize(int logicalIndex) const;
+ int sectionPosition(int logicalIndex) const;
+ int sectionViewportPosition(int logicalIndex) const;
+
+ void moveSection(int from, int to);
+ void swapSections(int first, int second);
+ void resizeSection(int logicalIndex, int size);
+ void resizeSections(QHeaderView::ResizeMode mode);
+
+ bool isSectionHidden(int logicalIndex) const;
+ void setSectionHidden(int logicalIndex, bool hide);
+ int hiddenSectionCount() const;
+
+ inline void hideSection(int logicalIndex);
+ inline void showSection(int logicalIndex);
+
+ int count() const;
+ int visualIndex(int logicalIndex) const;
+ int logicalIndex(int visualIndex) const;
+
+ void setMovable(bool movable);
+ bool isMovable() const;
+
+ void setClickable(bool clickable);
+ bool isClickable() const;
+
+ void setHighlightSections(bool highlight);
+ bool highlightSections() const;
+
+ void setResizeMode(ResizeMode mode);
+ void setResizeMode(int logicalIndex, ResizeMode mode);
+ ResizeMode resizeMode(int logicalIndex) const;
+ int stretchSectionCount() const;
+
+ void setSortIndicatorShown(bool show);
+ bool isSortIndicatorShown() const;
+
+ void setSortIndicator(int logicalIndex, Qt::SortOrder order);
+ int sortIndicatorSection() const;
+ Qt::SortOrder sortIndicatorOrder() const;
+
+ bool stretchLastSection() const;
+ void setStretchLastSection(bool stretch);
+
+ bool cascadingSectionResizes() const;
+ void setCascadingSectionResizes(bool enable);
+
+ int defaultSectionSize() const;
+ void setDefaultSectionSize(int size);
+
+ int minimumSectionSize() const;
+ void setMinimumSectionSize(int size);
+
+ Qt::Alignment defaultAlignment() const;
+ void setDefaultAlignment(Qt::Alignment alignment);
+
+ void doItemsLayout();
+ bool sectionsMoved() const;
+ bool sectionsHidden() const;
+
+#ifndef QT_NO_DATASTREAM
+ QByteArray saveState() const;
+ bool restoreState(const QByteArray &state);
+#endif
+
+ void reset();
+
+public Q_SLOTS:
+ void setOffset(int offset);
+ void setOffsetToSectionPosition(int visualIndex);
+ void setOffsetToLastSection();
+ void headerDataChanged(Qt::Orientation orientation, int logicalFirst, int logicalLast);
+
+Q_SIGNALS:
+ void sectionMoved(int logicalIndex, int oldVisualIndex, int newVisualIndex);
+ void sectionResized(int logicalIndex, int oldSize, int newSize);
+ void sectionPressed(int logicalIndex);
+ void sectionClicked(int logicalIndex);
+ void sectionEntered(int logicalIndex);
+ void sectionDoubleClicked(int logicalIndex);
+ void sectionCountChanged(int oldCount, int newCount);
+ void sectionHandleDoubleClicked(int logicalIndex);
+ void sectionAutoResize(int logicalIndex, QHeaderView::ResizeMode mode);
+ void geometriesChanged();
+ void sortIndicatorChanged(int logicalIndex, Qt::SortOrder order);
+
+protected Q_SLOTS:
+ void updateSection(int logicalIndex);
+ void resizeSections();
+ void sectionsInserted(const QModelIndex &parent, int logicalFirst, int logicalLast);
+ void sectionsAboutToBeRemoved(const QModelIndex &parent, int logicalFirst, int logicalLast);
+
+protected:
+ QHeaderView(QHeaderViewPrivate &dd, Qt::Orientation orientation, QWidget *parent = 0);
+ void initialize();
+
+ void initializeSections();
+ void initializeSections(int start, int end);
+ void currentChanged(const QModelIndex &current, const QModelIndex &old);
+
+ bool event(QEvent *e);
+ void paintEvent(QPaintEvent *e);
+ void mousePressEvent(QMouseEvent *e);
+ void mouseMoveEvent(QMouseEvent *e);
+ void mouseReleaseEvent(QMouseEvent *e);
+ void mouseDoubleClickEvent(QMouseEvent *e);
+ bool viewportEvent(QEvent *e);
+
+ virtual void paintSection(QPainter *painter, const QRect &rect, int logicalIndex) const;
+ virtual QSize sectionSizeFromContents(int logicalIndex) const;
+
+ int horizontalOffset() const;
+ int verticalOffset() const;
+ void updateGeometries();
+ void scrollContentsBy(int dx, int dy);
+
+ void dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight);
+ void rowsInserted(const QModelIndex &parent, int start, int end);
+
+ QRect visualRect(const QModelIndex &index) const;
+ void scrollTo(const QModelIndex &index, ScrollHint hint);
+
+ QModelIndex indexAt(const QPoint &p) const;
+ bool isIndexHidden(const QModelIndex &index) const;
+
+ QModelIndex moveCursor(CursorAction, Qt::KeyboardModifiers);
+ void setSelection(const QRect&, QItemSelectionModel::SelectionFlags);
+ QRegion visualRegionForSelection(const QItemSelection &selection) const;
+ void initStyleOption(QStyleOptionHeader *option) const;
+
+private:
+ Q_PRIVATE_SLOT(d_func(), void _q_sectionsRemoved(const QModelIndex &parent, int logicalFirst, int logicalLast))
+ Q_PRIVATE_SLOT(d_func(), void _q_layoutAboutToBeChanged())
+ Q_PRIVATE_SLOT(d_func(), void _q_layoutChanged())
+ Q_DECLARE_PRIVATE(QHeaderView)
+ Q_DISABLE_COPY(QHeaderView)
+};
+
+inline int QHeaderView::logicalIndexAt(int ax, int ay) const
+{ return orientation() == Qt::Horizontal ? logicalIndexAt(ax) : logicalIndexAt(ay); }
+inline int QHeaderView::logicalIndexAt(const QPoint &apos) const
+{ return logicalIndexAt(apos.x(), apos.y()); }
+inline void QHeaderView::hideSection(int alogicalIndex)
+{ setSectionHidden(alogicalIndex, true); }
+inline void QHeaderView::showSection(int alogicalIndex)
+{ setSectionHidden(alogicalIndex, false); }
+
+#endif // QT_NO_ITEMVIEWS
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QHEADERVIEW_H
diff --git a/src/gui/itemviews/qheaderview_p.h b/src/gui/itemviews/qheaderview_p.h
new file mode 100644
index 0000000000..fbba69a647
--- /dev/null
+++ b/src/gui/itemviews/qheaderview_p.h
@@ -0,0 +1,370 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QHEADERVIEW_P_H
+#define QHEADERVIEW_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/qabstractitemview_p.h"
+
+#ifndef QT_NO_ITEMVIEWS
+
+#include "QtCore/qbitarray.h"
+#include "QtGui/qapplication.h"
+#include "QtGui/qlabel.h"
+
+QT_BEGIN_NAMESPACE
+
+class QHeaderViewPrivate: public QAbstractItemViewPrivate
+{
+ Q_DECLARE_PUBLIC(QHeaderView)
+
+public:
+ enum StateVersion { VersionMarker = 0xff };
+
+ QHeaderViewPrivate()
+ : state(NoState),
+ offset(0),
+ sortIndicatorOrder(Qt::DescendingOrder),
+ sortIndicatorSection(0),
+ sortIndicatorShown(false),
+ lastPos(-1),
+ firstPos(-1),
+ originalSize(-1),
+ section(-1),
+ target(-1),
+ pressed(-1),
+ hover(-1),
+ length(0),
+ sectionCount(0),
+ movableSections(false),
+ clickableSections(false),
+ highlightSelected(false),
+ stretchLastSection(false),
+ cascadingResizing(false),
+ forceInitializing(false),
+ stretchSections(0),
+ contentsSections(0),
+ minimumSectionSize(-1),
+ lastSectionSize(0),
+ sectionIndicatorOffset(0),
+ sectionIndicator(0),
+ globalResizeMode(QHeaderView::Interactive)
+ {}
+
+
+ int lastVisibleVisualIndex() const;
+ int sectionHandleAt(int position);
+ void setupSectionIndicator(int section, int position);
+ void updateSectionIndicator(int section, int position);
+ void updateHiddenSections(int logicalFirst, int logicalLast);
+ void resizeSections(QHeaderView::ResizeMode globalMode, bool useGlobalMode = false);
+ void _q_sectionsRemoved(const QModelIndex &,int,int);
+ void _q_layoutAboutToBeChanged();
+ void _q_layoutChanged();
+
+ bool isSectionSelected(int section) const;
+
+ inline bool rowIntersectsSelection(int row) const {
+ return (selectionModel ? selectionModel->rowIntersectsSelection(row, root) : false);
+ }
+
+ inline bool columnIntersectsSelection(int column) const {
+ return (selectionModel ? selectionModel->columnIntersectsSelection(column, root) : false);
+ }
+
+ inline bool sectionIntersectsSelection(int logical) const {
+ return (orientation == Qt::Horizontal ? columnIntersectsSelection(logical) : rowIntersectsSelection(logical));
+ }
+
+ inline bool isRowSelected(int row) const {
+ return (selectionModel ? selectionModel->isRowSelected(row, root) : false);
+ }
+
+ inline bool isColumnSelected(int column) const {
+ return (selectionModel ? selectionModel->isColumnSelected(column, root) : false);
+ }
+
+ inline void prepareSectionSelected() {
+ if (!selectionModel || !selectionModel->hasSelection())
+ sectionSelected.clear();
+ else if (sectionSelected.count() != sectionCount * 2)
+ sectionSelected.fill(false, sectionCount * 2);
+ else sectionSelected.fill(false);
+ }
+
+ inline bool reverse() const {
+ return orientation == Qt::Horizontal && q_func()->isRightToLeft();
+ }
+
+ inline int logicalIndex(int visualIndex) const {
+ return logicalIndices.isEmpty() ? visualIndex : logicalIndices.at(visualIndex);
+ }
+
+ inline int visualIndex(int logicalIndex) const {
+ return visualIndices.isEmpty() ? logicalIndex : visualIndices.at(logicalIndex);
+ }
+
+ inline void setDefaultValues(Qt::Orientation o) {
+ orientation = o;
+ defaultSectionSize = (o == Qt::Horizontal ? 100
+ : qMax(q_func()->minimumSectionSize(), 30));
+ defaultAlignment = (o == Qt::Horizontal
+ ? Qt::Alignment(Qt::AlignCenter)
+ : Qt::AlignLeft|Qt::AlignVCenter);
+ }
+
+ inline bool isVisualIndexHidden(int visual) const {
+ return !sectionHidden.isEmpty() && sectionHidden.at(visual);
+ }
+
+ inline void setVisualIndexHidden(int visual, bool hidden) {
+ if (!sectionHidden.isEmpty()) sectionHidden.setBit(visual, hidden);
+ }
+
+ inline QHeaderView::ResizeMode visualIndexResizeMode(int visual) const {
+ return headerSectionResizeMode(visual);
+ }
+
+ inline bool hasAutoResizeSections() const {
+ return stretchSections || stretchLastSection || contentsSections;
+ }
+
+ QStyleOptionHeader getStyleOption() const;
+
+ inline void invalidateCachedSizeHint() const {
+ cachedSizeHint = QSize();
+ }
+
+ inline void initializeIndexMapping() const {
+ if (visualIndices.count() != sectionCount
+ || logicalIndices.count() != sectionCount) {
+ visualIndices.resize(sectionCount);
+ logicalIndices.resize(sectionCount);
+ for (int s = 0; s < sectionCount; ++s) {
+ visualIndices[s] = s;
+ logicalIndices[s] = s;
+ }
+ }
+ }
+
+ inline void clearCascadingSections() {
+ firstCascadingSection = sectionCount;
+ lastCascadingSection = 0;
+ cascadingSectionSize.clear();
+ }
+
+ inline void saveCascadingSectionSize(int visual, int size) {
+ if (!cascadingSectionSize.contains(visual)) {
+ cascadingSectionSize.insert(visual, size);
+ firstCascadingSection = qMin(firstCascadingSection, visual);
+ lastCascadingSection = qMax(lastCascadingSection, visual);
+ }
+ }
+
+ inline bool sectionIsCascadable(int visual) const {
+ return visualIndexResizeMode(visual) == QHeaderView::Interactive;
+ }
+
+ inline int modelSectionCount() const {
+ return (orientation == Qt::Horizontal
+ ? model->columnCount(root)
+ : model->rowCount(root));
+ }
+
+ inline bool modelIsEmpty() const {
+ return (model->rowCount(root) == 0 || model->columnCount(root) == 0);
+ }
+
+ inline void doDelayedResizeSections() {
+ if (!delayedResize.isActive())
+ delayedResize.start(0, q_func());
+ }
+
+ inline void executePostedResize() const {
+ if (delayedResize.isActive() && state == NoState) {
+ delayedResize.stop();
+ const_cast<QHeaderView*>(q_func())->resizeSections();
+ }
+ }
+
+ void clear();
+ void flipSortIndicator(int section);
+ void cascadingResize(int visual, int newSize);
+
+ enum State { NoState, ResizeSection, MoveSection, SelectSections, NoClear } state;
+
+ int offset;
+ Qt::Orientation orientation;
+ Qt::SortOrder sortIndicatorOrder;
+ int sortIndicatorSection;
+ bool sortIndicatorShown;
+
+ mutable QVector<int> visualIndices; // visualIndex = visualIndices.at(logicalIndex)
+ mutable QVector<int> logicalIndices; // logicalIndex = row or column in the model
+ mutable QBitArray sectionSelected; // from logical index to bit
+ mutable QBitArray sectionHidden; // from visual index to bit
+ mutable QHash<int, int> hiddenSectionSize; // from logical index to section size
+ mutable QHash<int, int> cascadingSectionSize; // from visual index to section size
+ mutable QSize cachedSizeHint;
+ mutable QBasicTimer delayedResize;
+
+ int firstCascadingSection;
+ int lastCascadingSection;
+
+ int lastPos;
+ int firstPos;
+ int originalSize;
+ int section; // used for resizing and moving sections
+ int target;
+ int pressed;
+ int hover;
+
+ int length;
+ int sectionCount;
+ bool movableSections;
+ bool clickableSections;
+ bool highlightSelected;
+ bool stretchLastSection;
+ bool cascadingResizing;
+ bool forceInitializing;
+ int stretchSections;
+ int contentsSections;
+ int defaultSectionSize;
+ int minimumSectionSize;
+ int lastSectionSize; // $$$
+ int sectionIndicatorOffset;
+ Qt::Alignment defaultAlignment;
+ QLabel *sectionIndicator;
+ QHeaderView::ResizeMode globalResizeMode;
+ QList<QPersistentModelIndex> persistentHiddenSections;
+
+ // header section spans
+
+ struct SectionSpan {
+ int size;
+ int count;
+ QHeaderView::ResizeMode resizeMode;
+ inline SectionSpan() : size(0), count(0), resizeMode(QHeaderView::Interactive) {}
+ inline SectionSpan(int length, int sections, QHeaderView::ResizeMode mode)
+ : size(length), count(sections), resizeMode(mode) {}
+ inline int sectionSize() const { return (count > 0 ? size / count : 0); }
+#ifndef QT_NO_DATASTREAM
+ inline void write(QDataStream &out) const
+ { out << size; out << count; out << (int)resizeMode; }
+ inline void read(QDataStream &in)
+ { in >> size; in >> count; int m; in >> m; resizeMode = (QHeaderView::ResizeMode)m; }
+#endif
+ };
+
+ QVector<SectionSpan> sectionSpans;
+
+ void createSectionSpan(int start, int end, int size, QHeaderView::ResizeMode mode);
+ void removeSectionsFromSpans(int start, int end);
+ void resizeSectionSpan(int visualIndex, int oldSize, int newSize);
+
+ inline int headerSectionCount() const { // for debugging
+ int count = 0;
+ for (int i = 0; i < sectionSpans.count(); ++i)
+ count += sectionSpans.at(i).count;
+ return count;
+ }
+
+ inline int headerLength() const { // for debugging
+ int len = 0;
+ for (int i = 0; i < sectionSpans.count(); ++i)
+ len += sectionSpans.at(i).size;
+ return len;
+ }
+
+ inline void removeSpans(const QList<int> &spans) {
+ for (int i = spans.count() - 1; i >= 0; --i) {
+ length -= sectionSpans.at(spans.at(i)).size;
+ sectionSpans.remove(spans.at(i));
+ }
+ }
+
+ inline int sectionSpanIndex(int visual) const {
+ int section_start = 0;
+ for (int i = 0; i < sectionSpans.count(); ++i) {
+ int section_end = section_start + sectionSpans.at(i).count - 1;
+ if (visual >= section_start && visual <= section_end)
+ return i;
+ section_start = section_end + 1;
+ }
+ return -1;
+ }
+
+ int headerSectionSize(int visual) const;
+ int headerSectionPosition(int visual) const;
+ int headerVisualIndexAt(int position) const;
+
+ // resize mode
+ void setHeaderSectionResizeMode(int visual, QHeaderView::ResizeMode mode);
+ QHeaderView::ResizeMode headerSectionResizeMode(int visual) const;
+ void setGlobalHeaderResizeMode(QHeaderView::ResizeMode mode);
+
+ // other
+ int viewSectionSizeHint(int logical) const;
+ int adjustedVisualIndex(int visualIndex) const;
+
+#ifndef QT_NO_DATASTREAM
+ void write(QDataStream &out) const;
+ bool read(QDataStream &in);
+#endif
+
+};
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_ITEMVIEWS
+
+#endif // QHEADERVIEW_P_H
diff --git a/src/gui/itemviews/qitemdelegate.cpp b/src/gui/itemviews/qitemdelegate.cpp
new file mode 100644
index 0000000000..bf9b5c585c
--- /dev/null
+++ b/src/gui/itemviews/qitemdelegate.cpp
@@ -0,0 +1,1337 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qitemdelegate.h"
+
+#ifndef QT_NO_ITEMVIEWS
+#include <qabstractitemmodel.h>
+#include <qapplication.h>
+#include <qbrush.h>
+#include <qlineedit.h>
+#include <qtextedit.h>
+#include <qpainter.h>
+#include <qpalette.h>
+#include <qpoint.h>
+#include <qrect.h>
+#include <qsize.h>
+#include <qstyle.h>
+#include <qdatetime.h>
+#include <qstyleoption.h>
+#include <qevent.h>
+#include <qpixmap.h>
+#include <qbitmap.h>
+#include <qpixmapcache.h>
+#include <qitemeditorfactory.h>
+#include <qmetaobject.h>
+#include <qtextlayout.h>
+#include <private/qobject_p.h>
+#include <private/qdnd_p.h>
+#include <private/qtextengine_p.h>
+#include <qdebug.h>
+#include <qlocale.h>
+#include <qdialog.h>
+
+#include <limits.h>
+
+#ifndef DBL_DIG
+# define DBL_DIG 10
+#endif
+
+QT_BEGIN_NAMESPACE
+
+class QItemDelegatePrivate : public QObjectPrivate
+{
+ Q_DECLARE_PUBLIC(QItemDelegate)
+
+public:
+ QItemDelegatePrivate() : f(0), clipPainting(true) {}
+
+ inline const QItemEditorFactory *editorFactory() const
+ { return f ? f : QItemEditorFactory::defaultFactory(); }
+
+ inline QIcon::Mode iconMode(QStyle::State state) const
+ {
+ if (!(state & QStyle::State_Enabled)) return QIcon::Disabled;
+ if (state & QStyle::State_Selected) return QIcon::Selected;
+ return QIcon::Normal;
+ }
+
+ inline QIcon::State iconState(QStyle::State state) const
+ { return state & QStyle::State_Open ? QIcon::On : QIcon::Off; }
+
+ inline static QString replaceNewLine(QString text)
+ {
+ const QChar nl = QLatin1Char('\n');
+ for (int i = 0; i < text.count(); ++i)
+ if (text.at(i) == nl)
+ text[i] = QChar::LineSeparator;
+ return text;
+ }
+
+ static QString valueToText(const QVariant &value, const QStyleOptionViewItemV4 &option);
+
+ void _q_commitDataAndCloseEditor(QWidget *editor);
+
+ QItemEditorFactory *f;
+ bool clipPainting;
+
+ QRect textLayoutBounds(const QStyleOptionViewItemV2 &options) const;
+ QSizeF doTextLayout(int lineWidth) const;
+ mutable QTextLayout textLayout;
+ mutable QTextOption textOption;
+
+ const QWidget *widget(const QStyleOptionViewItem &option) const
+ {
+ if (const QStyleOptionViewItemV3 *v3 = qstyleoption_cast<const QStyleOptionViewItemV3 *>(&option))
+ return v3->widget;
+
+ return 0;
+ }
+
+ // ### temporary hack until we have QStandardItemDelegate
+ mutable struct Icon {
+ QIcon icon;
+ QIcon::Mode mode;
+ QIcon::State state;
+ } tmp;
+};
+
+void QItemDelegatePrivate::_q_commitDataAndCloseEditor(QWidget *editor)
+{
+ Q_Q(QItemDelegate);
+ emit q->commitData(editor);
+ emit q->closeEditor(editor, QAbstractItemDelegate::SubmitModelCache);
+}
+
+QRect QItemDelegatePrivate::textLayoutBounds(const QStyleOptionViewItemV2 &option) const
+{
+ QRect rect = option.rect;
+ const bool wrapText = option.features & QStyleOptionViewItemV2::WrapText;
+ switch (option.decorationPosition) {
+ case QStyleOptionViewItem::Left:
+ case QStyleOptionViewItem::Right:
+ rect.setWidth(wrapText && rect.isValid() ? rect.width() : (QFIXED_MAX));
+ break;
+ case QStyleOptionViewItem::Top:
+ case QStyleOptionViewItem::Bottom:
+ rect.setWidth(wrapText ? option.decorationSize.width() : (QFIXED_MAX));
+ break;
+ }
+
+ return rect;
+}
+
+QSizeF QItemDelegatePrivate::doTextLayout(int lineWidth) const
+{
+ qreal height = 0;
+ qreal widthUsed = 0;
+ textLayout.beginLayout();
+ while (true) {
+ QTextLine line = textLayout.createLine();
+ if (!line.isValid())
+ break;
+ line.setLineWidth(lineWidth);
+ line.setPosition(QPointF(0, height));
+ height += line.height();
+ widthUsed = qMax(widthUsed, line.naturalTextWidth());
+ }
+ textLayout.endLayout();
+ return QSizeF(widthUsed, height);
+}
+
+/*!
+ \class QItemDelegate
+
+ \brief The QItemDelegate class provides display and editing facilities for
+ data items from a model.
+
+ \ingroup model-view
+ \mainclass
+
+ QItemDelegate can be used to provide custom display features and editor
+ widgets for item views based on QAbstractItemView subclasses. Using a
+ delegate for this purpose allows the display and editing mechanisms to be
+ customized and developed independently from the model and view.
+
+ The QItemDelegate class is one of the \l{Model/View Classes} and
+ is part of Qt's \l{Model/View Programming}{model/view framework}.
+ Note that QStyledItemDelegate has taken over the job of drawing
+ Qt's item views. We recommend the use of QStyledItemDelegate when
+ creating new delegates.
+
+ When displaying items from a custom model in a standard view, it is
+ often sufficient to simply ensure that the model returns appropriate
+ data for each of the \l{Qt::ItemDataRole}{roles} that determine the
+ appearance of items in views. The default delegate used by Qt's
+ standard views uses this role information to display items in most
+ of the common forms expected by users. However, it is sometimes
+ necessary to have even more control over the appearance of items than
+ the default delegate can provide.
+
+ This class provides default implementations of the functions for
+ painting item data in a view and editing data from item models.
+ Default implementations of the paint() and sizeHint() virtual
+ functions, defined in QAbstractItemDelegate, are provided to
+ ensure that the delegate implements the correct basic behavior
+ expected by views. You can reimplement these functions in
+ subclasses to customize the appearance of items.
+
+ When editing data in an item view, QItemDelegate provides an
+ editor widget, which is a widget that is placed on top of the view
+ while editing takes place. Editors are created with a
+ QItemEditorFactory; a default static instance provided by
+ QItemEditorFactory is installed on all item delagates. You can set
+ a custom factory using setItemEditorFactory() or set a new default
+ factory with QItemEditorFactory::setDefaultFactory(). It is the
+ data stored in the item model with the Qt::EditRole that is edited.
+
+ Only the standard editing functions for widget-based delegates are
+ reimplemented here:
+
+ \list
+ \o createEditor() returns the widget used to change data from the model
+ and can be reimplemented to customize editing behavior.
+ \o setEditorData() provides the widget with data to manipulate.
+ \o updateEditorGeometry() ensures that the editor is displayed correctly
+ with respect to the item view.
+ \o setModelData() returns updated data to the model.
+ \endlist
+
+ The closeEditor() signal indicates that the user has completed editing the data,
+ and that the editor widget can be destroyed.
+
+ \section1 Standard Roles and Data Types
+
+ The default delegate used by the standard views supplied with Qt
+ associates each standard role (defined by Qt::ItemDataRole) with certain
+ data types. Models that return data in these types can influence the
+ appearance of the delegate as described in the following table.
+
+ \table
+ \header \o Role \o Accepted Types
+ \omit
+ \row \o \l Qt::AccessibleDescriptionRole \o QString
+ \row \o \l Qt::AccessibleTextRole \o QString
+ \endomit
+ \row \o \l Qt::BackgroundRole \o QBrush
+ \row \o \l Qt::BackgroundColorRole \o QColor (obsolete; use Qt::BackgroundRole instead)
+ \row \o \l Qt::CheckStateRole \o Qt::CheckState
+ \row \o \l Qt::DecorationRole \o QIcon and QColor
+ \row \o \l Qt::DisplayRole \o QString and types with a string representation
+ \row \o \l Qt::EditRole \o See QItemEditorFactory for details
+ \row \o \l Qt::FontRole \o QFont
+ \row \o \l Qt::SizeHintRole \o QSize
+ \omit
+ \row \o \l Qt::StatusTipRole \o
+ \endomit
+ \row \o \l Qt::TextAlignmentRole \o Qt::Alignment
+ \row \o \l Qt::ForegroundRole \o QBrush
+ \row \o \l Qt::TextColorRole \o QColor (obsolete; use Qt::ForegroundRole instead)
+ \omit
+ \row \o \l Qt::ToolTipRole
+ \row \o \l Qt::WhatsThisRole
+ \endomit
+ \endtable
+
+ If the default delegate does not allow the level of customization that
+ you need, either for display purposes or for editing data, it is possible to
+ subclass QItemDelegate to implement the desired behavior.
+
+ \section1 Subclassing
+
+ When subclassing QItemDelegate to create a delegate that displays items
+ using a custom renderer, it is important to ensure that the delegate can
+ render items suitably for all the required states; e.g. selected,
+ disabled, checked. The documentation for the paint() function contains
+ some hints to show how this can be achieved.
+
+ You can provide custom editors by using a QItemEditorFactory. The
+ \l{Color Editor Factory Example} shows how a custom editor can be
+ made available to delegates with the default item editor
+ factory. This way, there is no need to subclass QItemDelegate. An
+ alternative is to reimplement createEditor(), setEditorData(),
+ setModelData(), and updateEditorGeometry(). This process is
+ described in the \l{Spin Box Delegate Example}.
+
+ \section1 QStyledItemDelegate vs. QItemDelegate
+
+ Since Qt 4.4, there are two delegate classes: QItemDelegate and
+ QStyledItemDelegate. However, the default delegate is QStyledItemDelegate.
+ These two classes are independent alternatives to painting and providing
+ editors for items in views. The difference between them is that
+ QStyledItemDelegate uses the current style to paint its items. We therefore
+ recommend using QStyledItemDelegate as the base class when implementing
+ custom delegates or when working with Qt style sheets. The code required
+ for either class should be equal unless the custom delegate needs to use
+ the style for drawing.
+
+ \sa {Delegate Classes}, QStyledItemDelegate, QAbstractItemDelegate,
+ {Spin Box Delegate Example}, {Settings Editor Example},
+ {Icons Example}
+*/
+
+/*!
+ Constructs an item delegate with the given \a parent.
+*/
+
+QItemDelegate::QItemDelegate(QObject *parent)
+ : QAbstractItemDelegate(*new QItemDelegatePrivate(), parent)
+{
+
+}
+
+/*!
+ Destroys the item delegate.
+*/
+
+QItemDelegate::~QItemDelegate()
+{
+}
+
+/*!
+ \property QItemDelegate::clipping
+ \brief if the delegate should clip the paint events
+ \since 4.2
+
+ This property will set the paint clip to the size of the item.
+ The default value is on. It is useful for cases such
+ as when images are larger than the size of the item.
+*/
+
+bool QItemDelegate::hasClipping() const
+{
+ Q_D(const QItemDelegate);
+ return d->clipPainting;
+}
+
+void QItemDelegate::setClipping(bool clip)
+{
+ Q_D(QItemDelegate);
+ d->clipPainting = clip;
+}
+
+QString QItemDelegatePrivate::valueToText(const QVariant &value, const QStyleOptionViewItemV4 &option)
+{
+ QString text;
+ switch (value.type()) {
+ case QVariant::Double:
+ text = option.locale.toString(value.toDouble(), 'g', DBL_DIG);
+ break;
+ case QVariant::Int:
+ case QVariant::LongLong:
+ text = option.locale.toString(value.toLongLong());
+ break;
+ case QVariant::UInt:
+ case QVariant::ULongLong:
+ text = option.locale.toString(value.toULongLong());
+ break;
+ case QVariant::Date:
+ text = option.locale.toString(value.toDate(), QLocale::ShortFormat);
+ break;
+ case QVariant::Time:
+ text = option.locale.toString(value.toTime(), QLocale::ShortFormat);
+ break;
+ case QVariant::DateTime:
+ text = option.locale.toString(value.toDateTime().date(), QLocale::ShortFormat);
+ text += QLatin1Char(' ');
+ text += option.locale.toString(value.toDateTime().time(), QLocale::ShortFormat);
+ break;
+ default:
+ text = replaceNewLine(value.toString());
+ break;
+ }
+ return text;
+}
+
+/*!
+ Renders the delegate using the given \a painter and style \a option for
+ the item specified by \a index.
+
+ When reimplementing this function in a subclass, you should update the area
+ held by the option's \l{QStyleOption::rect}{rect} variable, using the
+ option's \l{QStyleOption::state}{state} variable to determine the state of
+ the item to be displayed, and adjust the way it is painted accordingly.
+
+ For example, a selected item may need to be displayed differently to
+ unselected items, as shown in the following code:
+
+ \snippet examples/itemviews/pixelator/pixeldelegate.cpp 2
+ \dots
+
+ After painting, you should ensure that the painter is returned to its
+ the state it was supplied in when this function was called. For example,
+ it may be useful to call QPainter::save() before painting and
+ QPainter::restore() afterwards.
+
+ \sa QStyle::State
+*/
+void QItemDelegate::paint(QPainter *painter,
+ const QStyleOptionViewItem &option,
+ const QModelIndex &index) const
+{
+ Q_D(const QItemDelegate);
+ Q_ASSERT(index.isValid());
+
+ QStyleOptionViewItemV4 opt = setOptions(index, option);
+
+ const QStyleOptionViewItemV2 *v2 = qstyleoption_cast<const QStyleOptionViewItemV2 *>(&option);
+ opt.features = v2 ? v2->features
+ : QStyleOptionViewItemV2::ViewItemFeatures(QStyleOptionViewItemV2::None);
+ const QStyleOptionViewItemV3 *v3 = qstyleoption_cast<const QStyleOptionViewItemV3 *>(&option);
+ opt.locale = v3 ? v3->locale : QLocale();
+ opt.widget = v3 ? v3->widget : 0;
+
+ // prepare
+ painter->save();
+ if (d->clipPainting)
+ painter->setClipRect(opt.rect);
+
+ // get the data and the rectangles
+
+ QVariant value;
+
+ QPixmap pixmap;
+ QRect decorationRect;
+ value = index.data(Qt::DecorationRole);
+ if (value.isValid()) {
+ // ### we need the pixmap to call the virtual function
+ pixmap = decoration(opt, value);
+ if (value.type() == QVariant::Icon) {
+ d->tmp.icon = qvariant_cast<QIcon>(value);
+ d->tmp.mode = d->iconMode(option.state);
+ d->tmp.state = d->iconState(option.state);
+ const QSize size = d->tmp.icon.actualSize(option.decorationSize,
+ d->tmp.mode, d->tmp.state);
+ decorationRect = QRect(QPoint(0, 0), size);
+ } else {
+ d->tmp.icon = QIcon();
+ decorationRect = QRect(QPoint(0, 0), pixmap.size());
+ }
+ } else {
+ d->tmp.icon = QIcon();
+ decorationRect = QRect();
+ }
+
+ QString text;
+ QRect displayRect;
+ value = index.data(Qt::DisplayRole);
+ if (value.isValid() && !value.isNull()) {
+ text = QItemDelegatePrivate::valueToText(value, opt);
+ displayRect = textRectangle(painter, d->textLayoutBounds(opt), opt.font, text);
+ }
+
+ QRect checkRect;
+ Qt::CheckState checkState = Qt::Unchecked;
+ value = index.data(Qt::CheckStateRole);
+ if (value.isValid()) {
+ checkState = static_cast<Qt::CheckState>(value.toInt());
+ checkRect = check(opt, opt.rect, value);
+ }
+
+ // do the layout
+
+ doLayout(opt, &checkRect, &decorationRect, &displayRect, false);
+
+ // draw the item
+
+ drawBackground(painter, opt, index);
+ drawCheck(painter, opt, checkRect, checkState);
+ drawDecoration(painter, opt, decorationRect, pixmap);
+ drawDisplay(painter, opt, displayRect, text);
+ drawFocus(painter, opt, displayRect);
+
+ // done
+ painter->restore();
+}
+
+/*!
+ Returns the size needed by the delegate to display the item
+ specified by \a index, taking into account the style information
+ provided by \a option.
+
+ When reimplementing this function, note that in case of text
+ items, QItemDelegate adds a margin (i.e. 2 *
+ QStyle::PM_FocusFrameHMargin) to the length of the text.
+*/
+
+QSize QItemDelegate::sizeHint(const QStyleOptionViewItem &option,
+ const QModelIndex &index) const
+{
+ QVariant value = index.data(Qt::SizeHintRole);
+ if (value.isValid())
+ return qvariant_cast<QSize>(value);
+ QRect decorationRect = rect(option, index, Qt::DecorationRole);
+ QRect displayRect = rect(option, index, Qt::DisplayRole);
+ QRect checkRect = rect(option, index, Qt::CheckStateRole);
+
+ doLayout(option, &checkRect, &decorationRect, &displayRect, true);
+
+ return (decorationRect|displayRect|checkRect).size();
+}
+
+/*!
+ Returns the widget used to edit the item specified by \a index
+ for editing. The \a parent widget and style \a option are used to
+ control how the editor widget appears.
+
+ \sa QAbstractItemDelegate::createEditor()
+*/
+
+QWidget *QItemDelegate::createEditor(QWidget *parent,
+ const QStyleOptionViewItem &,
+ const QModelIndex &index) const
+{
+ Q_D(const QItemDelegate);
+ if (!index.isValid())
+ return 0;
+ QVariant::Type t = static_cast<QVariant::Type>(index.data(Qt::EditRole).userType());
+ const QItemEditorFactory *factory = d->f;
+ if (factory == 0)
+ factory = QItemEditorFactory::defaultFactory();
+ return factory->createEditor(t, parent);
+}
+
+/*!
+ Sets the data to be displayed and edited by the \a editor from the
+ data model item specified by the model \a index.
+
+ The default implementation stores the data in the \a editor
+ widget's \l {Qt's Property System} {user property}.
+
+ \sa QMetaProperty::isUser()
+*/
+
+void QItemDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const
+{
+#ifdef QT_NO_PROPERTIES
+ Q_UNUSED(editor);
+ Q_UNUSED(index);
+#else
+ Q_D(const QItemDelegate);
+ QVariant v = index.data(Qt::EditRole);
+ QByteArray n = editor->metaObject()->userProperty().name();
+
+ // ### Qt 5: remove
+ // A work-around for missing "USER true" in qdatetimeedit.h for
+ // QTimeEdit's time property and QDateEdit's date property.
+ // It only triggers if the default user property "dateTime" is
+ // reported for QTimeEdit and QDateEdit.
+ if (n == "dateTime") {
+ if (editor->inherits("QTimeEdit"))
+ n = "time";
+ else if (editor->inherits("QDateEdit"))
+ n = "date";
+ }
+
+ // ### Qt 5: give QComboBox a USER property
+ if (n.isEmpty() && editor->inherits("QComboBox"))
+ n = d->editorFactory()->valuePropertyName(static_cast<QVariant::Type>(v.userType()));
+ if (!n.isEmpty()) {
+ if (!v.isValid())
+ v = QVariant(editor->property(n).userType(), (const void *)0);
+ editor->setProperty(n, v);
+ }
+#endif
+}
+
+/*!
+ Gets data drom the \a editor widget and stores it in the specified
+ \a model at the item \a index.
+
+ The default implementation gets the value to be stored in the data
+ model from the \a editor widget's \l {Qt's Property System} {user
+ property}.
+
+ \sa QMetaProperty::isUser()
+*/
+
+void QItemDelegate::setModelData(QWidget *editor,
+ QAbstractItemModel *model,
+ const QModelIndex &index) const
+{
+#ifdef QT_NO_PROPERTIES
+ Q_UNUSED(model);
+ Q_UNUSED(editor);
+ Q_UNUSED(index);
+#else
+ Q_D(const QItemDelegate);
+ Q_ASSERT(model);
+ Q_ASSERT(editor);
+ QByteArray n = editor->metaObject()->userProperty().name();
+ if (n.isEmpty())
+ n = d->editorFactory()->valuePropertyName(
+ static_cast<QVariant::Type>(model->data(index, Qt::EditRole).userType()));
+ if (!n.isEmpty())
+ model->setData(index, editor->property(n), Qt::EditRole);
+#endif
+}
+
+/*!
+ Updates the \a editor for the item specified by \a index
+ according to the style \a option given.
+*/
+
+void QItemDelegate::updateEditorGeometry(QWidget *editor,
+ const QStyleOptionViewItem &option,
+ const QModelIndex &index) const
+{
+ if (!editor)
+ return;
+ Q_ASSERT(index.isValid());
+ QPixmap pixmap = decoration(option, index.data(Qt::DecorationRole));
+ QString text = QItemDelegatePrivate::replaceNewLine(index.data(Qt::DisplayRole).toString());
+ QRect pixmapRect = QRect(QPoint(0, 0), option.decorationSize).intersected(pixmap.rect());
+ QRect textRect = textRectangle(0, option.rect, option.font, text);
+ QRect checkRect = check(option, textRect, index.data(Qt::CheckStateRole));
+ QStyleOptionViewItem opt = option;
+ opt.showDecorationSelected = true; // let the editor take up all available space
+ doLayout(opt, &checkRect, &pixmapRect, &textRect, false);
+ editor->setGeometry(textRect);
+}
+
+/*!
+ Returns the editor factory used by the item delegate.
+ If no editor factory is set, the function will return null.
+
+ \sa setItemEditorFactory()
+*/
+QItemEditorFactory *QItemDelegate::itemEditorFactory() const
+{
+ Q_D(const QItemDelegate);
+ return d->f;
+}
+
+/*!
+ Sets the editor factory to be used by the item delegate to be the \a factory
+ specified. If no editor factory is set, the item delegate will use the
+ default editor factory.
+
+ \sa itemEditorFactory()
+*/
+void QItemDelegate::setItemEditorFactory(QItemEditorFactory *factory)
+{
+ Q_D(QItemDelegate);
+ d->f = factory;
+}
+
+/*!
+ Renders the item view \a text within the rectangle specified by \a rect
+ using the given \a painter and style \a option.
+*/
+
+void QItemDelegate::drawDisplay(QPainter *painter, const QStyleOptionViewItem &option,
+ const QRect &rect, const QString &text) const
+{
+ Q_D(const QItemDelegate);
+
+ QPen pen = painter->pen();
+ QPalette::ColorGroup cg = option.state & QStyle::State_Enabled
+ ? QPalette::Normal : QPalette::Disabled;
+ if (cg == QPalette::Normal && !(option.state & QStyle::State_Active))
+ cg = QPalette::Inactive;
+ if (option.state & QStyle::State_Selected) {
+ painter->fillRect(rect, option.palette.brush(cg, QPalette::Highlight));
+ painter->setPen(option.palette.color(cg, QPalette::HighlightedText));
+ } else {
+ painter->setPen(option.palette.color(cg, QPalette::Text));
+ }
+
+ if (text.isEmpty())
+ return;
+
+ if (option.state & QStyle::State_Editing) {
+ painter->save();
+ painter->setPen(option.palette.color(cg, QPalette::Text));
+ painter->drawRect(rect.adjusted(0, 0, -1, -1));
+ painter->restore();
+ }
+
+ const QStyleOptionViewItemV4 opt = option;
+
+ const QWidget *widget = d->widget(option);
+ QStyle *style = widget ? widget->style() : QApplication::style();
+ const int textMargin = style->pixelMetric(QStyle::PM_FocusFrameHMargin, 0, widget) + 1;
+ QRect textRect = rect.adjusted(textMargin, 0, -textMargin, 0); // remove width padding
+ const bool wrapText = opt.features & QStyleOptionViewItemV2::WrapText;
+ d->textOption.setWrapMode(wrapText ? QTextOption::WordWrap : QTextOption::ManualWrap);
+ d->textOption.setTextDirection(option.direction);
+ d->textOption.setAlignment(QStyle::visualAlignment(option.direction, option.displayAlignment));
+ d->textLayout.setTextOption(d->textOption);
+ d->textLayout.setFont(option.font);
+ d->textLayout.setText(QItemDelegatePrivate::replaceNewLine(text));
+
+ QSizeF textLayoutSize = d->doTextLayout(textRect.width());
+
+ if (textRect.width() < textLayoutSize.width()
+ || textRect.height() < textLayoutSize.height()) {
+ QString elided;
+ int start = 0;
+ int end = text.indexOf(QChar::LineSeparator, start);
+ if (end == -1) {
+ elided += option.fontMetrics.elidedText(text, option.textElideMode, textRect.width());
+ } else {
+ while (end != -1) {
+ elided += option.fontMetrics.elidedText(text.mid(start, end - start),
+ option.textElideMode, textRect.width());
+ elided += QChar::LineSeparator;
+ start = end + 1;
+ end = text.indexOf(QChar::LineSeparator, start);
+ }
+ //let's add the last line (after the last QChar::LineSeparator)
+ elided += option.fontMetrics.elidedText(text.mid(start),
+ option.textElideMode, textRect.width());
+ if (end != -1)
+ elided += QChar::LineSeparator;
+ }
+ d->textLayout.setText(elided);
+ textLayoutSize = d->doTextLayout(textRect.width());
+ }
+
+ const QSize layoutSize(textRect.width(), int(textLayoutSize.height()));
+ const QRect layoutRect = QStyle::alignedRect(option.direction, option.displayAlignment,
+ layoutSize, textRect);
+ // if we still overflow even after eliding the text, enable clipping
+ if (!hasClipping() && (textRect.width() < textLayoutSize.width()
+ || textRect.height() < textLayoutSize.height())) {
+ painter->save();
+ painter->setClipRect(layoutRect);
+ d->textLayout.draw(painter, layoutRect.topLeft(), QVector<QTextLayout::FormatRange>(), layoutRect);
+ painter->restore();
+ } else {
+ d->textLayout.draw(painter, layoutRect.topLeft(), QVector<QTextLayout::FormatRange>(), layoutRect);
+ }
+}
+
+/*!
+ Renders the decoration \a pixmap within the rectangle specified by
+ \a rect using the given \a painter and style \a option.
+*/
+void QItemDelegate::drawDecoration(QPainter *painter, const QStyleOptionViewItem &option,
+ const QRect &rect, const QPixmap &pixmap) const
+{
+ Q_D(const QItemDelegate);
+ // if we have an icon, we ignore the pixmap
+ if (!d->tmp.icon.isNull()) {
+ d->tmp.icon.paint(painter, rect, option.decorationAlignment,
+ d->tmp.mode, d->tmp.state);
+ return;
+ }
+
+ if (pixmap.isNull() || !rect.isValid())
+ return;
+ QPoint p = QStyle::alignedRect(option.direction, option.decorationAlignment,
+ pixmap.size(), rect).topLeft();
+ if (option.state & QStyle::State_Selected) {
+ QPixmap *pm = selected(pixmap, option.palette, option.state & QStyle::State_Enabled);
+ painter->drawPixmap(p, *pm);
+ } else {
+ painter->drawPixmap(p, pixmap);
+ }
+}
+
+/*!
+ Renders the region within the rectangle specified by \a rect, indicating
+ that it has the focus, using the given \a painter and style \a option.
+*/
+
+void QItemDelegate::drawFocus(QPainter *painter,
+ const QStyleOptionViewItem &option,
+ const QRect &rect) const
+{
+ Q_D(const QItemDelegate);
+ if ((option.state & QStyle::State_HasFocus) == 0 || !rect.isValid())
+ return;
+ QStyleOptionFocusRect o;
+ o.QStyleOption::operator=(option);
+ o.rect = rect;
+ o.state |= QStyle::State_KeyboardFocusChange;
+ o.state |= QStyle::State_Item;
+ QPalette::ColorGroup cg = (option.state & QStyle::State_Enabled)
+ ? QPalette::Normal : QPalette::Disabled;
+ o.backgroundColor = option.palette.color(cg, (option.state & QStyle::State_Selected)
+ ? QPalette::Highlight : QPalette::Window);
+ const QWidget *widget = d->widget(option);
+ QStyle *style = widget ? widget->style() : QApplication::style();
+ style->drawPrimitive(QStyle::PE_FrameFocusRect, &o, painter, widget);
+}
+
+/*!
+ Renders a check indicator within the rectangle specified by \a
+ rect, using the given \a painter and style \a option, using the
+ given \a state.
+*/
+
+void QItemDelegate::drawCheck(QPainter *painter,
+ const QStyleOptionViewItem &option,
+ const QRect &rect, Qt::CheckState state) const
+{
+ Q_D(const QItemDelegate);
+ if (!rect.isValid())
+ return;
+
+ QStyleOptionViewItem opt(option);
+ opt.rect = rect;
+ opt.state = opt.state & ~QStyle::State_HasFocus;
+
+ switch (state) {
+ case Qt::Unchecked:
+ opt.state |= QStyle::State_Off;
+ break;
+ case Qt::PartiallyChecked:
+ opt.state |= QStyle::State_NoChange;
+ break;
+ case Qt::Checked:
+ opt.state |= QStyle::State_On;
+ break;
+ }
+
+ const QWidget *widget = d->widget(option);
+ QStyle *style = widget ? widget->style() : QApplication::style();
+ style->drawPrimitive(QStyle::PE_IndicatorViewItemCheck, &opt, painter, widget);
+}
+
+/*!
+ \since 4.2
+
+ Renders the item background for the given \a index,
+ using the given \a painter and style \a option.
+*/
+
+void QItemDelegate::drawBackground(QPainter *painter,
+ const QStyleOptionViewItem &option,
+ const QModelIndex &index) const
+{
+ if (option.showDecorationSelected && (option.state & QStyle::State_Selected)) {
+ QPalette::ColorGroup cg = option.state & QStyle::State_Enabled
+ ? QPalette::Normal : QPalette::Disabled;
+ if (cg == QPalette::Normal && !(option.state & QStyle::State_Active))
+ cg = QPalette::Inactive;
+
+ painter->fillRect(option.rect, option.palette.brush(cg, QPalette::Highlight));
+ } else {
+ QVariant value = index.data(Qt::BackgroundRole);
+ if (qVariantCanConvert<QBrush>(value)) {
+ QPointF oldBO = painter->brushOrigin();
+ painter->setBrushOrigin(option.rect.topLeft());
+ painter->fillRect(option.rect, qvariant_cast<QBrush>(value));
+ painter->setBrushOrigin(oldBO);
+ }
+ }
+}
+
+
+/*!
+ \internal
+*/
+
+void QItemDelegate::doLayout(const QStyleOptionViewItem &option,
+ QRect *checkRect, QRect *pixmapRect, QRect *textRect,
+ bool hint) const
+{
+ Q_ASSERT(checkRect && pixmapRect && textRect);
+ Q_D(const QItemDelegate);
+ const QWidget *widget = d->widget(option);
+ QStyle *style = widget ? widget->style() : QApplication::style();
+ const bool hasCheck = checkRect->isValid();
+ const bool hasPixmap = pixmapRect->isValid();
+ const bool hasText = textRect->isValid();
+ const int textMargin = hasText ? style->pixelMetric(QStyle::PM_FocusFrameHMargin, 0, widget) + 1 : 0;
+ const int pixmapMargin = hasPixmap ? style->pixelMetric(QStyle::PM_FocusFrameHMargin, 0, widget) + 1 : 0;
+ const int checkMargin = hasCheck ? style->pixelMetric(QStyle::PM_FocusFrameHMargin, 0, widget) + 1 : 0;
+ int x = option.rect.left();
+ int y = option.rect.top();
+ int w, h;
+
+ textRect->adjust(-textMargin, 0, textMargin, 0); // add width padding
+ if (textRect->height() == 0 && !hasPixmap)
+ textRect->setHeight(option.fontMetrics.height());
+
+ QSize pm(0, 0);
+ if (hasPixmap) {
+ pm = pixmapRect->size();
+ pm.rwidth() += 2 * pixmapMargin;
+ }
+ if (hint) {
+ h = qMax(checkRect->height(), qMax(textRect->height(), pm.height()));
+ if (option.decorationPosition == QStyleOptionViewItem::Left
+ || option.decorationPosition == QStyleOptionViewItem::Right) {
+ w = textRect->width() + pm.width();
+ } else {
+ w = qMax(textRect->width(), pm.width());
+ }
+ } else {
+ w = option.rect.width();
+ h = option.rect.height();
+ }
+
+ int cw = 0;
+ QRect check;
+ if (hasCheck) {
+ cw = checkRect->width() + 2 * checkMargin;
+ if (hint) w += cw;
+ if (option.direction == Qt::RightToLeft) {
+ check.setRect(x + w - cw, y, cw, h);
+ } else {
+ check.setRect(x + checkMargin, y, cw, h);
+ }
+ }
+
+ // at this point w should be the *total* width
+
+ QRect display;
+ QRect decoration;
+ switch (option.decorationPosition) {
+ case QStyleOptionViewItem::Top: {
+ if (hasPixmap)
+ pm.setHeight(pm.height() + pixmapMargin); // add space
+ h = hint ? textRect->height() : h - pm.height();
+
+ if (option.direction == Qt::RightToLeft) {
+ decoration.setRect(x, y, w - cw, pm.height());
+ display.setRect(x, y + pm.height(), w - cw, h);
+ } else {
+ decoration.setRect(x + cw, y, w - cw, pm.height());
+ display.setRect(x + cw, y + pm.height(), w - cw, h);
+ }
+ break; }
+ case QStyleOptionViewItem::Bottom: {
+ if (hasText)
+ textRect->setHeight(textRect->height() + textMargin); // add space
+ h = hint ? textRect->height() + pm.height() : h;
+
+ if (option.direction == Qt::RightToLeft) {
+ display.setRect(x, y, w - cw, textRect->height());
+ decoration.setRect(x, y + textRect->height(), w - cw, h - textRect->height());
+ } else {
+ display.setRect(x + cw, y, w - cw, textRect->height());
+ decoration.setRect(x + cw, y + textRect->height(), w - cw, h - textRect->height());
+ }
+ break; }
+ case QStyleOptionViewItem::Left: {
+ if (option.direction == Qt::LeftToRight) {
+ decoration.setRect(x + cw, y, pm.width(), h);
+ display.setRect(decoration.right() + 1, y, w - pm.width() - cw, h);
+ } else {
+ display.setRect(x, y, w - pm.width() - cw, h);
+ decoration.setRect(display.right() + 1, y, pm.width(), h);
+ }
+ break; }
+ case QStyleOptionViewItem::Right: {
+ if (option.direction == Qt::LeftToRight) {
+ display.setRect(x + cw, y, w - pm.width() - cw, h);
+ decoration.setRect(display.right() + 1, y, pm.width(), h);
+ } else {
+ decoration.setRect(x, y, pm.width(), h);
+ display.setRect(decoration.right() + 1, y, w - pm.width() - cw, h);
+ }
+ break; }
+ default:
+ qWarning("doLayout: decoration position is invalid");
+ decoration = *pixmapRect;
+ break;
+ }
+
+ if (!hint) { // we only need to do the internal layout if we are going to paint
+ *checkRect = QStyle::alignedRect(option.direction, Qt::AlignCenter,
+ checkRect->size(), check);
+ *pixmapRect = QStyle::alignedRect(option.direction, option.decorationAlignment,
+ pixmapRect->size(), decoration);
+ // the text takes up all available space, unless the decoration is not shown as selected
+ if (option.showDecorationSelected)
+ *textRect = display;
+ else
+ *textRect = QStyle::alignedRect(option.direction, option.displayAlignment,
+ textRect->size().boundedTo(display.size()), display);
+ } else {
+ *checkRect = check;
+ *pixmapRect = decoration;
+ *textRect = display;
+ }
+}
+
+/*!
+ \internal
+
+ Returns the pixmap used to decorate the root of the item view.
+ The style \a option controls the appearance of the root; the \a variant
+ refers to the data associated with an item.
+*/
+
+QPixmap QItemDelegate::decoration(const QStyleOptionViewItem &option, const QVariant &variant) const
+{
+ Q_D(const QItemDelegate);
+ switch (variant.type()) {
+ case QVariant::Icon: {
+ QIcon::Mode mode = d->iconMode(option.state);
+ QIcon::State state = d->iconState(option.state);
+ return qvariant_cast<QIcon>(variant).pixmap(option.decorationSize, mode, state); }
+ case QVariant::Color: {
+ static QPixmap pixmap(option.decorationSize);
+ pixmap.fill(qvariant_cast<QColor>(variant));
+ return pixmap; }
+ default:
+ break;
+ }
+
+ return qvariant_cast<QPixmap>(variant);
+}
+
+// hacky but faster version of "QString::sprintf("%d-%d", i, enabled)"
+static QString qPixmapSerial(quint64 i, bool enabled)
+{
+ ushort arr[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '-', '0' + enabled };
+ ushort *ptr = &arr[16];
+
+ while (i > 0) {
+ // hey - it's our internal representation, so use the ascii character after '9'
+ // instead of 'a' for hex
+ *(--ptr) = '0' + i % 16;
+ i >>= 4;
+ }
+
+ return QString::fromUtf16(ptr, int(&arr[sizeof(arr) / sizeof(ushort)] - ptr));
+}
+
+/*!
+ \internal
+ Returns the selected version of the given \a pixmap using the given \a palette.
+ The \a enabled argument decides whether the normal or disabled highlight color of
+ the palette is used.
+*/
+QPixmap *QItemDelegate::selected(const QPixmap &pixmap, const QPalette &palette, bool enabled) const
+{
+ QString key = qPixmapSerial(qt_pixmap_id(pixmap), enabled);
+ QPixmap *pm = QPixmapCache::find(key);
+ if (!pm) {
+ QImage img = pixmap.toImage().convertToFormat(QImage::Format_ARGB32_Premultiplied);
+
+ QColor color = palette.color(enabled ? QPalette::Normal : QPalette::Disabled,
+ QPalette::Highlight);
+ color.setAlphaF((qreal)0.3);
+
+ QPainter painter(&img);
+ painter.setCompositionMode(QPainter::CompositionMode_SourceAtop);
+ painter.fillRect(0, 0, img.width(), img.height(), color);
+ painter.end();
+
+ QPixmap selected = QPixmap(QPixmap::fromImage(img));
+ int n = (img.numBytes() >> 10) + 1;
+ if (QPixmapCache::cacheLimit() < n)
+ QPixmapCache::setCacheLimit(n);
+
+ QPixmapCache::insert(key, selected);
+ pm = QPixmapCache::find(key);
+ }
+ return pm;
+}
+
+/*!
+ \internal
+*/
+
+QRect QItemDelegate::rect(const QStyleOptionViewItem &option,
+ const QModelIndex &index, int role) const
+{
+ Q_D(const QItemDelegate);
+ QVariant value = index.data(role);
+ if (role == Qt::CheckStateRole)
+ return check(option, option.rect, value);
+ if (value.isValid() && !value.isNull()) {
+ switch (value.type()) {
+ case QVariant::Invalid:
+ break;
+ case QVariant::Pixmap:
+ return QRect(QPoint(0, 0), qvariant_cast<QPixmap>(value).size());
+ case QVariant::Image:
+ return QRect(QPoint(0, 0), qvariant_cast<QImage>(value).size());
+ case QVariant::Icon: {
+ QIcon::Mode mode = d->iconMode(option.state);
+ QIcon::State state = d->iconState(option.state);
+ QIcon icon = qvariant_cast<QIcon>(value);
+ QSize size = icon.actualSize(option.decorationSize, mode, state);
+ return QRect(QPoint(0, 0), size); }
+ case QVariant::Color:
+ return QRect(QPoint(0, 0), option.decorationSize);
+ case QVariant::String:
+ default: {
+ QString text = QItemDelegatePrivate::valueToText(value, option);
+ value = index.data(Qt::FontRole);
+ QFont fnt = qvariant_cast<QFont>(value).resolve(option.font);
+ return textRectangle(0, d->textLayoutBounds(option), fnt, text); }
+ }
+ }
+ return QRect();
+}
+
+/*!
+ \internal
+
+ Note that on Mac, if /usr/include/AssertMacros.h is included prior
+ to QItemDelegate, and the application is building in debug mode, the
+ check(assertion) will conflict with QItemDelegate::check.
+
+ To avoid this problem, add
+
+ #ifdef check
+ #undef check
+ #endif
+
+ after including AssertMacros.h
+*/
+QRect QItemDelegate::check(const QStyleOptionViewItem &option,
+ const QRect &bounding, const QVariant &value) const
+{
+ if (value.isValid()) {
+ Q_D(const QItemDelegate);
+ QStyleOptionButton opt;
+ opt.QStyleOption::operator=(option);
+ opt.rect = bounding;
+ const QWidget *widget = d->widget(option); // cast
+ QStyle *style = widget ? widget->style() : QApplication::style();
+ return style->subElementRect(QStyle::SE_ViewItemCheckIndicator, &opt, widget);
+ }
+ return QRect();
+}
+
+/*!
+ \internal
+*/
+QRect QItemDelegate::textRectangle(QPainter * /*painter*/, const QRect &rect,
+ const QFont &font, const QString &text) const
+{
+ Q_D(const QItemDelegate);
+ d->textOption.setWrapMode(QTextOption::WordWrap);
+ d->textLayout.setTextOption(d->textOption);
+ d->textLayout.setFont(font);
+ d->textLayout.setText(QItemDelegatePrivate::replaceNewLine(text));
+ const QSize size = d->doTextLayout(rect.width()).toSize();
+ // ###: textRectangle should take style option as argument
+ const int textMargin = QApplication::style()->pixelMetric(QStyle::PM_FocusFrameHMargin) + 1;
+ return QRect(0, 0, size.width() + 2 * textMargin, size.height());
+}
+
+/*!
+ \fn bool QItemDelegate::eventFilter(QObject *editor, QEvent *event)
+
+ Returns true if the given \a editor is a valid QWidget and the
+ given \a event is handled; otherwise returns false. The following
+ key press events are handled by default:
+
+ \list
+ \o \gui Tab
+ \o \gui Backtab
+ \o \gui Enter
+ \o \gui Return
+ \o \gui Esc
+ \endlist
+
+ In the case of \gui Tab, \gui Backtab, \gui Enter and \gui Return
+ key press events, the \a editor's data is comitted to the model
+ and the editor is closed. If the \a event is a \gui Tab key press
+ the view will open an editor on the next item in the
+ view. Likewise, if the \a event is a \gui Backtab key press the
+ view will open an editor on the \e previous item in the view.
+
+ If the event is a \gui Esc key press event, the \a editor is
+ closed \e without committing its data.
+
+ \sa commitData(), closeEditor()
+*/
+
+bool QItemDelegate::eventFilter(QObject *object, QEvent *event)
+{
+ QWidget *editor = qobject_cast<QWidget*>(object);
+ if (!editor)
+ return false;
+ if (event->type() == QEvent::KeyPress) {
+ switch (static_cast<QKeyEvent *>(event)->key()) {
+ case Qt::Key_Tab:
+ emit commitData(editor);
+ emit closeEditor(editor, QAbstractItemDelegate::EditNextItem);
+ return true;
+ case Qt::Key_Backtab:
+ emit commitData(editor);
+ emit closeEditor(editor, QAbstractItemDelegate::EditPreviousItem);
+ return true;
+ case Qt::Key_Enter:
+ case Qt::Key_Return:
+#ifndef QT_NO_TEXTEDIT
+ if (qobject_cast<QTextEdit*>(editor))
+ return false; // don't filter enter key events for QTextEdit
+ // We want the editor to be able to process the key press
+ // before committing the data (e.g. so it can do
+ // validation/fixup of the input).
+#endif // QT_NO_TEXTEDIT
+#ifndef QT_NO_LINEEDIT
+ if (QLineEdit *e = qobject_cast<QLineEdit*>(editor))
+ if (!e->hasAcceptableInput())
+ return false;
+#endif // QT_NO_LINEEDIT
+ QMetaObject::invokeMethod(this, "_q_commitDataAndCloseEditor",
+ Qt::QueuedConnection, Q_ARG(QWidget*, editor));
+ return false;
+ case Qt::Key_Escape:
+ // don't commit data
+ emit closeEditor(editor, QAbstractItemDelegate::RevertModelCache);
+ break;
+ default:
+ return false;
+ }
+ if (editor->parentWidget())
+ editor->parentWidget()->setFocus();
+ return true;
+ } else if (event->type() == QEvent::FocusOut || event->type() == QEvent::Hide) {
+ //the Hide event will take care of he editors that are in fact complete dialogs
+ if (!editor->isActiveWindow() || (QApplication::focusWidget() != editor)) {
+ QWidget *w = QApplication::focusWidget();
+ while (w) { // don't worry about focus changes internally in the editor
+ if (w == editor)
+ return false;
+ w = w->parentWidget();
+ }
+#ifndef QT_NO_DRAGANDDROP
+ // The window may lose focus during an drag operation.
+ // i.e when dragging involves the taskbar on Windows.
+ if (QDragManager::self() && QDragManager::self()->object != 0)
+ return false;
+#endif
+ // Opening a modal dialog will start a new eventloop
+ // that will process the deleteLater event.
+ if (QApplication::activeModalWidget()
+ && !QApplication::activeModalWidget()->isAncestorOf(editor)
+ && qobject_cast<QDialog*>(QApplication::activeModalWidget()))
+ return false;
+ emit commitData(editor);
+ emit closeEditor(editor, NoHint);
+ }
+ } else if (event->type() == QEvent::ShortcutOverride) {
+ if (static_cast<QKeyEvent*>(event)->key() == Qt::Key_Escape) {
+ event->accept();
+ return true;
+ }
+ }
+ return false;
+}
+
+/*!
+ \reimp
+*/
+
+bool QItemDelegate::editorEvent(QEvent *event,
+ QAbstractItemModel *model,
+ const QStyleOptionViewItem &option,
+ const QModelIndex &index)
+{
+ Q_ASSERT(event);
+ Q_ASSERT(model);
+
+ // make sure that the item is checkable
+ Qt::ItemFlags flags = model->flags(index);
+ if (!(flags & Qt::ItemIsUserCheckable) || !(option.state & QStyle::State_Enabled)
+ || !(flags & Qt::ItemIsEnabled))
+ return false;
+
+ // make sure that we have a check state
+ QVariant value = index.data(Qt::CheckStateRole);
+ if (!value.isValid())
+ return false;
+
+ // make sure that we have the right event type
+ if ((event->type() == QEvent::MouseButtonRelease)
+ || (event->type() == QEvent::MouseButtonDblClick)) {
+ QRect checkRect = check(option, option.rect, Qt::Checked);
+ QRect emptyRect;
+ doLayout(option, &checkRect, &emptyRect, &emptyRect, false);
+ QMouseEvent *me = static_cast<QMouseEvent*>(event);
+ if (me->button() != Qt::LeftButton || !checkRect.contains(me->pos()))
+ return false;
+
+ // eat the double click events inside the check rect
+ if (event->type() == QEvent::MouseButtonDblClick)
+ return true;
+
+ } else if (event->type() == QEvent::KeyPress) {
+ if (static_cast<QKeyEvent*>(event)->key() != Qt::Key_Space
+ && static_cast<QKeyEvent*>(event)->key() != Qt::Key_Select)
+ return false;
+ } else {
+ return false;
+ }
+
+ Qt::CheckState state = (static_cast<Qt::CheckState>(value.toInt()) == Qt::Checked
+ ? Qt::Unchecked : Qt::Checked);
+ return model->setData(index, state, Qt::CheckStateRole);
+}
+
+/*!
+ \internal
+*/
+
+QStyleOptionViewItem QItemDelegate::setOptions(const QModelIndex &index,
+ const QStyleOptionViewItem &option) const
+{
+ QStyleOptionViewItem opt = option;
+
+ // set font
+ QVariant value = index.data(Qt::FontRole);
+ if (value.isValid()){
+ opt.font = qvariant_cast<QFont>(value).resolve(opt.font);
+ opt.fontMetrics = QFontMetrics(opt.font);
+ }
+
+ // set text alignment
+ value = index.data(Qt::TextAlignmentRole);
+ if (value.isValid())
+ opt.displayAlignment = (Qt::Alignment)value.toInt();
+
+ // set foreground brush
+ value = index.data(Qt::ForegroundRole);
+ if (qVariantCanConvert<QBrush>(value))
+ opt.palette.setBrush(QPalette::Text, qvariant_cast<QBrush>(value));
+
+ return opt;
+}
+
+QT_END_NAMESPACE
+
+#include "moc_qitemdelegate.cpp"
+
+#endif // QT_NO_ITEMVIEWS
diff --git a/src/gui/itemviews/qitemdelegate.h b/src/gui/itemviews/qitemdelegate.h
new file mode 100644
index 0000000000..8b286ac08f
--- /dev/null
+++ b/src/gui/itemviews/qitemdelegate.h
@@ -0,0 +1,141 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QITEMDELEGATE_H
+#define QITEMDELEGATE_H
+
+#include <QtGui/qabstractitemdelegate.h>
+#include <QtCore/qstring.h>
+#include <QtGui/qpixmap.h>
+#include <QtCore/qvariant.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+#ifndef QT_NO_ITEMVIEWS
+
+class QItemDelegatePrivate;
+class QItemEditorFactory;
+
+class Q_GUI_EXPORT QItemDelegate : public QAbstractItemDelegate
+{
+ Q_OBJECT
+ Q_PROPERTY(bool clipping READ hasClipping WRITE setClipping)
+
+public:
+ explicit QItemDelegate(QObject *parent = 0);
+ ~QItemDelegate();
+
+ bool hasClipping() const;
+ void setClipping(bool clip);
+
+ // painting
+ void paint(QPainter *painter,
+ const QStyleOptionViewItem &option,
+ const QModelIndex &index) const;
+ QSize sizeHint(const QStyleOptionViewItem &option,
+ const QModelIndex &index) const;
+
+ // editing
+ QWidget *createEditor(QWidget *parent,
+ const QStyleOptionViewItem &option,
+ const QModelIndex &index) const;
+
+ void setEditorData(QWidget *editor, const QModelIndex &index) const;
+ void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const;
+
+ void updateEditorGeometry(QWidget *editor,
+ const QStyleOptionViewItem &option,
+ const QModelIndex &index) const;
+
+ // editor factory
+ QItemEditorFactory *itemEditorFactory() const;
+ void setItemEditorFactory(QItemEditorFactory *factory);
+
+protected:
+ virtual void drawDisplay(QPainter *painter, const QStyleOptionViewItem &option,
+ const QRect &rect, const QString &text) const;
+ virtual void drawDecoration(QPainter *painter, const QStyleOptionViewItem &option,
+ const QRect &rect, const QPixmap &pixmap) const;
+ virtual void drawFocus(QPainter *painter, const QStyleOptionViewItem &option,
+ const QRect &rect) const;
+ virtual void drawCheck(QPainter *painter, const QStyleOptionViewItem &option,
+ const QRect &rect, Qt::CheckState state) const;
+ void drawBackground(QPainter *painter, const QStyleOptionViewItem &option,
+ const QModelIndex &index) const;
+
+ void doLayout(const QStyleOptionViewItem &option,
+ QRect *checkRect, QRect *iconRect, QRect *textRect, bool hint) const;
+
+ QRect rect(const QStyleOptionViewItem &option, const QModelIndex &index, int role) const;
+
+ bool eventFilter(QObject *object, QEvent *event);
+ bool editorEvent(QEvent *event, QAbstractItemModel *model,
+ const QStyleOptionViewItem &option, const QModelIndex &index);
+
+ QStyleOptionViewItem setOptions(const QModelIndex &index,
+ const QStyleOptionViewItem &option) const;
+
+ QPixmap decoration(const QStyleOptionViewItem &option, const QVariant &variant) const;
+ QPixmap *selected(const QPixmap &pixmap, const QPalette &palette, bool enabled) const;
+
+ QRect check(const QStyleOptionViewItem &option, const QRect &bounding,
+ const QVariant &variant) const;
+ QRect textRectangle(QPainter *painter, const QRect &rect,
+ const QFont &font, const QString &text) const;
+
+private:
+ Q_DECLARE_PRIVATE(QItemDelegate)
+ Q_DISABLE_COPY(QItemDelegate)
+
+ Q_PRIVATE_SLOT(d_func(), void _q_commitDataAndCloseEditor(QWidget*))
+};
+
+#endif // QT_NO_ITEMVIEWS
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QITEMDELEGATE_H
diff --git a/src/gui/itemviews/qitemeditorfactory.cpp b/src/gui/itemviews/qitemeditorfactory.cpp
new file mode 100644
index 0000000000..aefdea167a
--- /dev/null
+++ b/src/gui/itemviews/qitemeditorfactory.cpp
@@ -0,0 +1,566 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <qplatformdefs.h>
+#include "qitemeditorfactory.h"
+#include "qitemeditorfactory_p.h"
+
+#ifndef QT_NO_ITEMVIEWS
+
+#include <qcombobox.h>
+#include <qdatetimeedit.h>
+#include <qlabel.h>
+#include <qlineedit.h>
+#include <qspinbox.h>
+#include <limits.h>
+#include <float.h>
+#include <qapplication.h>
+#include <qdebug.h>
+
+QT_BEGIN_NAMESPACE
+
+
+#ifndef QT_NO_COMBOBOX
+
+class QBooleanComboBox : public QComboBox
+{
+ Q_OBJECT
+ Q_PROPERTY(bool value READ value WRITE setValue USER true)
+
+public:
+ QBooleanComboBox(QWidget *parent);
+ void setValue(bool);
+ bool value() const;
+};
+
+#endif // QT_NO_COMBOBOX
+
+/*!
+ \class QItemEditorFactory
+ \brief The QItemEditorFactory class provides widgets for editing item data
+ in views and delegates.
+ \since 4.2
+ \ingroup model-view
+
+ When editing data in an item view, editors are created and
+ displayed by a delegate. QItemDelegate, which is the delegate by
+ default installed on Qt's item views, uses a QItemEditorFactory to
+ create editors for it. A default unique instance provided by
+ QItemEditorFactory is used by all item delegates. If you set a
+ new default factory with setDefaultFactory(), the new factory will
+ be used by existing and new delegates.
+
+ A factory keeps a collection of QItemEditorCreatorBase
+ instances, which are specialized editors that produce editors
+ for one particular QVariant data type (All Qt models store
+ their data in \l{QVariant}s).
+
+ \section1 Standard Editing Widgets
+
+ The standard factory implementation provides editors for a variety of data
+ types. These are created whenever a delegate needs to provide an editor for
+ data supplied by a model. The following table shows the relationship between
+ types and the standard editors provided.
+
+ \table
+ \header \o Type \o Editor Widget
+ \row \o bool \o QComboBox
+ \row \o double \o QDoubleSpinBox
+ \row \o int \o{1,2} QSpinBox
+ \row \o unsigned int
+ \row \o QDate \o QDateEdit
+ \row \o QDateTime \o QDateTimeEdit
+ \row \o QPixmap \o QLabel
+ \row \o QString \o QLineEdit
+ \row \o QTime \o QTimeEdit
+ \endtable
+
+ Additional editors can be registered with the registerEditor() function.
+
+ \sa QItemDelegate, {Model/View Programming}, {Color Editor Factory Example}
+*/
+
+/*!
+ \fn QItemEditorFactory::QItemEditorFactory()
+
+ Constructs a new item editor factory.
+*/
+
+/*!
+ Creates an editor widget with the given \a parent for the specified \a type of data,
+ and returns it as a QWidget.
+
+ \sa registerEditor()
+*/
+QWidget *QItemEditorFactory::createEditor(QVariant::Type type, QWidget *parent) const
+{
+ QItemEditorCreatorBase *creator = creatorMap.value(type, 0);
+ if (!creator) {
+ const QItemEditorFactory *dfactory = defaultFactory();
+ return dfactory == this ? 0 : dfactory->createEditor(type, parent);
+ }
+ return creator->createWidget(parent);
+}
+
+/*!
+ Returns the property name used to access data for the given \a type of data.
+*/
+QByteArray QItemEditorFactory::valuePropertyName(QVariant::Type type) const
+{
+ QItemEditorCreatorBase *creator = creatorMap.value(type, 0);
+ if (!creator) {
+ const QItemEditorFactory *dfactory = defaultFactory();
+ return dfactory == this ? QByteArray() : dfactory->valuePropertyName(type);
+ }
+ return creator->valuePropertyName();
+}
+
+/*!
+ Destroys the item editor factory.
+*/
+QItemEditorFactory::~QItemEditorFactory()
+{
+}
+
+/*!
+ Registers an item editor creator specified by \a creator for the given \a type of data.
+
+ \bold{Note:} The factory takes ownership of the item editor creator and will destroy
+ it if a new creator for the same type is registered later.
+
+ \sa createEditor()
+*/
+void QItemEditorFactory::registerEditor(QVariant::Type type, QItemEditorCreatorBase *creator)
+{
+ delete creatorMap.value(type, 0);
+ creatorMap[type] = creator;
+}
+
+class QDefaultItemEditorFactory : public QItemEditorFactory
+{
+public:
+ inline QDefaultItemEditorFactory() {}
+ QWidget *createEditor(QVariant::Type type, QWidget *parent) const;
+ QByteArray valuePropertyName(QVariant::Type) const;
+};
+
+QWidget *QDefaultItemEditorFactory::createEditor(QVariant::Type type, QWidget *parent) const
+{
+ switch (type) {
+#ifndef QT_NO_COMBOBOX
+ case QVariant::Bool: {
+ QBooleanComboBox *cb = new QBooleanComboBox(parent);
+ cb->setFrame(false);
+ return cb; }
+#endif
+#ifndef QT_NO_SPINBOX
+ case QVariant::UInt: {
+ QSpinBox *sb = new QSpinBox(parent);
+ sb->setFrame(false);
+ sb->setMaximum(INT_MAX);
+ return sb; }
+ case QVariant::Int: {
+ QSpinBox *sb = new QSpinBox(parent);
+ sb->setFrame(false);
+ sb->setMinimum(INT_MIN);
+ sb->setMaximum(INT_MAX);
+ return sb; }
+#endif
+#ifndef QT_NO_DATETIMEEDIT
+ case QVariant::Date: {
+ QDateTimeEdit *ed = new QDateEdit(parent);
+ ed->setFrame(false);
+ return ed; }
+ case QVariant::Time: {
+ QDateTimeEdit *ed = new QTimeEdit(parent);
+ ed->setFrame(false);
+ return ed; }
+ case QVariant::DateTime: {
+ QDateTimeEdit *ed = new QDateTimeEdit(parent);
+ ed->setFrame(false);
+ return ed; }
+#endif
+ case QVariant::Pixmap:
+ return new QLabel(parent);
+#ifndef QT_NO_SPINBOX
+ case QVariant::Double: {
+ QDoubleSpinBox *sb = new QDoubleSpinBox(parent);
+ sb->setFrame(false);
+ sb->setMinimum(-DBL_MAX);
+ sb->setMaximum(DBL_MAX);
+ return sb; }
+#endif
+#ifndef QT_NO_LINEEDIT
+ case QVariant::String:
+ default: {
+ // the default editor is a lineedit
+ QExpandingLineEdit *le = new QExpandingLineEdit(parent);
+ le->setFrame(le->style()->styleHint(QStyle::SH_ItemView_DrawDelegateFrame, 0, le));
+ if (!le->style()->styleHint(QStyle::SH_ItemView_ShowDecorationSelected, 0, le))
+ le->setWidgetOwnsGeometry(true);
+ return le; }
+#else
+ default:
+ break;
+#endif
+ }
+ return 0;
+}
+
+QByteArray QDefaultItemEditorFactory::valuePropertyName(QVariant::Type type) const
+{
+ switch (type) {
+#ifndef QT_NO_COMBOBOX
+ case QVariant::Bool:
+ return "currentIndex";
+#endif
+#ifndef QT_NO_SPINBOX
+ case QVariant::UInt:
+ case QVariant::Int:
+ case QVariant::Double:
+ return "value";
+#endif
+#ifndef QT_NO_DATETIMEEDIT
+ case QVariant::Date:
+ return "date";
+ case QVariant::Time:
+ return "time";
+ case QVariant::DateTime:
+ return "dateTime";
+#endif
+ case QVariant::String:
+ default:
+ // the default editor is a lineedit
+ return "text";
+ }
+}
+
+static QItemEditorFactory *q_default_factory = 0;
+struct QDefaultFactoryCleaner
+{
+ inline QDefaultFactoryCleaner() {}
+ ~QDefaultFactoryCleaner() { delete q_default_factory; q_default_factory = 0; }
+};
+
+/*!
+ Returns the default item editor factory.
+
+ \sa setDefaultFactory()
+*/
+const QItemEditorFactory *QItemEditorFactory::defaultFactory()
+{
+ static const QDefaultItemEditorFactory factory;
+ if (q_default_factory)
+ return q_default_factory;
+ return &factory;
+}
+
+/*!
+ Sets the default item editor factory to the given \a factory.
+ Both new and existing delegates will use the new factory.
+
+ \sa defaultFactory()
+*/
+void QItemEditorFactory::setDefaultFactory(QItemEditorFactory *factory)
+{
+ static const QDefaultFactoryCleaner cleaner;
+ delete q_default_factory;
+ q_default_factory = factory;
+}
+
+/*!
+ \class QItemEditorCreatorBase
+ \brief The QItemEditorCreatorBase class provides an abstract base class that
+ must be subclassed when implementing new item editor creators.
+ \since 4.2
+ \ingroup model-view
+
+ QItemEditorCreatorBase objects are specialized widget factories that
+ provide editor widgets for one particular QVariant data type. They
+ are used by QItemEditorFactory to create editors for
+ \l{QItemDelegate}s. Creator bases must be registered with
+ QItemEditorFactory::registerEditor().
+
+ An editor should provide a user property for the data it edits.
+ QItemDelagates can then access the property using Qt's
+ \l{Meta-Object System}{meta-object system} to set and retrieve the
+ editing data. A property is set as the user property with the USER
+ keyword:
+
+ \snippet doc/src/snippets/code/src_gui_itemviews_qitemeditorfactory.cpp 0
+
+ If the editor does not provide a user property, it must return the
+ name of the property from valuePropertyName(); delegates will then
+ use the name to access the property. If a user property exists,
+ item delegates will not call valuePropertyName().
+
+ QStandardItemEditorCreator is a convenience template class that can be used
+ to register widgets without the need to subclass QItemEditorCreatorBase.
+
+ \sa QStandardItemEditorCreator, QItemEditorFactory,
+ {Model/View Programming}, {Color Editor Factory Example}
+*/
+
+/*!
+ \fn QItemEditorCreatorBase::~QItemEditorCreatorBase()
+
+ Destroys the editor creator object.
+*/
+
+/*!
+ \fn QWidget *QItemEditorCreatorBase::createWidget(QWidget *parent) const
+
+ Returns an editor widget with the given \a parent.
+
+ When implementing this function in subclasses of this class, you must
+ construct and return new editor widgets with the parent widget specified.
+*/
+
+/*!
+ \fn QByteArray QItemEditorCreatorBase::valuePropertyName() const
+
+ Returns the name of the property used to get and set values in the creator's
+ editor widgets.
+
+ When implementing this function in subclasses, you must ensure that the
+ editor widget's property specified by this function can accept the type
+ the creator is registered for. For example, a creator which constructs
+ QCheckBox widgets to edit boolean values would return the
+ \l{QCheckBox::checkable}{checkable} property name from this function,
+ and must be registered in the item editor factory for the QVariant::Bool
+ type.
+
+ Note: Since Qt 4.2 the item delegates query the user property of widgets,
+ and only call this function if the widget has no user property. You can
+ override this behavior by reimplementing QAbstractItemDelegate::setModelData()
+ and QAbstractItemDelegate::setEditorData().
+
+ \sa QMetaObject::userProperty(), QItemEditorFactory::registerEditor()
+*/
+
+/*!
+ \class QItemEditorCreator
+ \brief The QItemEditorCreator class makes it possible to create
+ item editor creator bases without subclassing
+ QItemEditorCreatorBase.
+
+ \since 4.2
+ \ingroup model-view
+
+ QItemEditorCreator is a convenience template class. It uses
+ the template class to create editors for QItemEditorFactory.
+ This way, it is not necessary to subclass
+ QItemEditorCreatorBase.
+
+ \snippet doc/src/snippets/code/src_gui_itemviews_qitemeditorfactory.cpp 1
+
+ The constructor takes the name of the property that contains the
+ editing data. QItemDelegate can then access the property by name
+ when it sets and retrieves editing data. Only use this class if
+ your editor does not define a user property (using the USER
+ keyword in the Q_PROPERTY macro). If the widget has a user
+ property, you should use QStandardItemEditorCreator instead.
+
+ \sa QItemEditorCreatorBase, QStandardItemEditorCreator,
+ QItemEditorFactory, {Color Editor Factory Example}
+*/
+
+/*!
+ \fn QItemEditorCreator::QItemEditorCreator(const QByteArray &valuePropertyName)
+
+ Constructs an editor creator object using \a valuePropertyName
+ as the name of the property to be used for editing. The
+ property name is used by QItemDelegate when setting and
+ getting editor data.
+
+ Note that the \a valuePropertyName is only used if the editor
+ widget does not have a user property defined.
+*/
+
+/*!
+ \fn QWidget *QItemEditorCreator::createWidget(QWidget *parent) const
+ \reimp
+*/
+
+/*!
+ \fn QByteArray QItemEditorCreator::valuePropertyName() const
+ \reimp
+*/
+
+/*!
+ \class QStandardItemEditorCreator
+
+ \brief The QStandardItemEditorCreator class provides the
+ possibility to register widgets without having to subclass
+ QItemEditorCreatorBase.
+
+ \since 4.2
+ \ingroup model-view
+
+ This convenience template class makes it possible to register widgets without
+ having to subclass QItemEditorCreatorBase.
+
+ Example:
+
+ \snippet doc/src/snippets/code/src_gui_itemviews_qitemeditorfactory.cpp 2
+
+ Setting the \c editorFactory created above in an item delegate via
+ QItemDelegate::setItemEditorFactory() makes sure that all values of type
+ QVariant::DateTime will be edited in \c{MyFancyDateTimeEdit}.
+
+ The editor must provide a user property that will contain the
+ editing data. The property is used by \l{QItemDelegate}s to set
+ and retrieve the data (using Qt's \l{Meta-Object
+ System}{meta-object system}). You set the user property with
+ the USER keyword:
+
+ \snippet doc/src/snippets/code/src_gui_itemviews_qitemeditorfactory.cpp 3
+
+ \sa QItemEditorCreatorBase, QItemEditorCreator,
+ QItemEditorFactory, QItemDelegate, {Color Editor Factory Example}
+*/
+
+/*!
+ \fn QStandardItemEditorCreator::QStandardItemEditorCreator()
+
+ Constructs an editor creator object.
+*/
+
+/*!
+ \fn QWidget *QStandardItemEditorCreator::createWidget(QWidget *parent) const
+ \reimp
+*/
+
+/*!
+ \fn QByteArray QStandardItemEditorCreator::valuePropertyName() const
+ \reimp
+*/
+
+#ifndef QT_NO_LINEEDIT
+
+QExpandingLineEdit::QExpandingLineEdit(QWidget *parent)
+ : QLineEdit(parent), originalWidth(-1), widgetOwnsGeometry(false)
+{
+ connect(this, SIGNAL(textChanged(QString)), this, SLOT(resizeToContents()));
+ updateMinimumWidth();
+}
+
+void QExpandingLineEdit::changeEvent(QEvent *e)
+{
+ switch (e->type())
+ {
+ case QEvent::FontChange:
+ case QEvent::StyleChange:
+ case QEvent::ContentsRectChange:
+ updateMinimumWidth();
+ break;
+ default:
+ break;
+ }
+
+ QLineEdit::changeEvent(e);
+}
+
+void QExpandingLineEdit::updateMinimumWidth()
+{
+ int left, right;
+ getTextMargins(&left, 0, &right, 0);
+ int width = left + right + 4 /*horizontalMargin in qlineedit.cpp*/;
+ getContentsMargins(&left, 0, &right, 0);
+ width += left + right;
+
+ QStyleOptionFrameV2 opt;
+ initStyleOption(&opt);
+
+ int minWidth = style()->sizeFromContents(QStyle::CT_LineEdit, &opt, QSize(width, 0).
+ expandedTo(QApplication::globalStrut()), this).width();
+ setMinimumWidth(minWidth);
+}
+
+void QExpandingLineEdit::resizeToContents()
+{
+ int oldWidth = width();
+ if (originalWidth == -1)
+ originalWidth = oldWidth;
+ if (QWidget *parent = parentWidget()) {
+ QPoint position = pos();
+ int hintWidth = minimumWidth() + fontMetrics().width(displayText());
+ int parentWidth = parent->width();
+ int maxWidth = isRightToLeft() ? position.x() + oldWidth : parentWidth - position.x();
+ int newWidth = qBound(originalWidth, hintWidth, maxWidth);
+ if (widgetOwnsGeometry)
+ setMaximumWidth(newWidth);
+ if (isRightToLeft())
+ move(position.x() - newWidth + oldWidth, position.y());
+ resize(newWidth, height());
+ }
+}
+
+#endif // QT_NO_LINEEDIT
+
+#ifndef QT_NO_COMBOBOX
+
+QBooleanComboBox::QBooleanComboBox(QWidget *parent)
+ : QComboBox(parent)
+{
+ addItem(QComboBox::tr("False"));
+ addItem(QComboBox::tr("True"));
+}
+
+void QBooleanComboBox::setValue(bool value)
+{
+ setCurrentIndex(value ? 1 : 0);
+}
+
+bool QBooleanComboBox::value() const
+{
+ return (currentIndex() == 1);
+}
+
+#endif // QT_NO_COMBOBOX
+
+QT_END_NAMESPACE
+
+#if !defined(QT_NO_LINEEDIT) || !defined(QT_NO_COMBOBOX)
+#include "qitemeditorfactory.moc"
+#endif
+
+#endif // QT_NO_ITEMVIEWS
diff --git a/src/gui/itemviews/qitemeditorfactory.h b/src/gui/itemviews/qitemeditorfactory.h
new file mode 100644
index 0000000000..46c209a10d
--- /dev/null
+++ b/src/gui/itemviews/qitemeditorfactory.h
@@ -0,0 +1,124 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QITEMEDITORFACTORY_H
+#define QITEMEDITORFACTORY_H
+
+#include <QtCore/qmetaobject.h>
+#include <QtCore/qbytearray.h>
+#include <QtCore/qhash.h>
+#include <QtCore/qvariant.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+#ifndef QT_NO_ITEMVIEWS
+
+class QWidget;
+
+class Q_GUI_EXPORT QItemEditorCreatorBase
+{
+public:
+ virtual ~QItemEditorCreatorBase() {}
+
+ virtual QWidget *createWidget(QWidget *parent) const = 0;
+ virtual QByteArray valuePropertyName() const = 0;
+};
+
+template <class T>
+class QItemEditorCreator : public QItemEditorCreatorBase
+{
+public:
+ inline QItemEditorCreator(const QByteArray &valuePropertyName);
+ inline QWidget *createWidget(QWidget *parent) const { return new T(parent); }
+ inline QByteArray valuePropertyName() const { return propertyName; }
+
+private:
+ QByteArray propertyName;
+};
+
+template <class T>
+class QStandardItemEditorCreator: public QItemEditorCreatorBase
+{
+public:
+ inline QStandardItemEditorCreator()
+ : propertyName(T::staticMetaObject.userProperty().name())
+ {}
+ inline QWidget *createWidget(QWidget *parent) const { return new T(parent); }
+ inline QByteArray valuePropertyName() const { return propertyName; }
+
+private:
+ QByteArray propertyName;
+};
+
+
+template <class T>
+Q_INLINE_TEMPLATE QItemEditorCreator<T>::QItemEditorCreator(const QByteArray &avaluePropertyName)
+ : propertyName(avaluePropertyName) {}
+
+class Q_GUI_EXPORT QItemEditorFactory
+{
+public:
+ inline QItemEditorFactory() {}
+ virtual ~QItemEditorFactory();
+
+ virtual QWidget *createEditor(QVariant::Type type, QWidget *parent) const;
+ virtual QByteArray valuePropertyName(QVariant::Type type) const;
+
+ void registerEditor(QVariant::Type type, QItemEditorCreatorBase *creator);
+
+ static const QItemEditorFactory *defaultFactory();
+ static void setDefaultFactory(QItemEditorFactory *factory);
+
+private:
+ QHash<QVariant::Type, QItemEditorCreatorBase *> creatorMap;
+};
+
+#endif // QT_NO_ITEMVIEWS
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QITEMEDITORFACTORY_H
diff --git a/src/gui/itemviews/qitemeditorfactory_p.h b/src/gui/itemviews/qitemeditorfactory_p.h
new file mode 100644
index 0000000000..87f68c5cd1
--- /dev/null
+++ b/src/gui/itemviews/qitemeditorfactory_p.h
@@ -0,0 +1,99 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QITEMEDITORFACTORY_P_H
+#define QITEMEDITORFACTORY_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 <qlineedit.h>
+
+#ifndef QT_NO_ITEMVIEWS
+
+#ifndef QT_NO_LINEEDIT
+
+QT_BEGIN_NAMESPACE
+
+
+class QExpandingLineEdit : public QLineEdit
+{
+ Q_OBJECT
+
+public:
+ QExpandingLineEdit(QWidget *parent);
+
+ void setWidgetOwnsGeometry(bool value)
+ {
+ widgetOwnsGeometry = value;
+ }
+
+protected:
+ void changeEvent(QEvent *e);
+
+public Q_SLOTS:
+ void resizeToContents();
+
+private:
+ void updateMinimumWidth();
+
+ int originalWidth;
+ bool widgetOwnsGeometry;
+};
+
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_LINEEDIT
+
+#endif //QT_NO_ITEMVIEWS
+
+#endif //QITEMEDITORFACTORY_P_H
diff --git a/src/gui/itemviews/qitemselectionmodel.cpp b/src/gui/itemviews/qitemselectionmodel.cpp
new file mode 100644
index 0000000000..1a3ae2d58e
--- /dev/null
+++ b/src/gui/itemviews/qitemselectionmodel.cpp
@@ -0,0 +1,1570 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qitemselectionmodel.h"
+#include <private/qitemselectionmodel_p.h>
+#include <qdebug.h>
+
+#ifndef QT_NO_ITEMVIEWS
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QItemSelectionRange
+
+ \brief The QItemSelectionRange class manages information about a
+ range of selected items in a model.
+
+ \ingroup model-view
+
+ A QItemSelectionRange contains information about a range of
+ selected items in a model. A range of items is a contiguous array
+ of model items, extending to cover a number of adjacent rows and
+ columns with a common parent item; this can be visualized as a
+ two-dimensional block of cells in a table. A selection range has a
+ top(), left() a bottom(), right() and a parent().
+
+ The QItemSelectionRange class is one of the \l{Model/View Classes}
+ and is part of Qt's \l{Model/View Programming}{model/view framework}.
+
+ The model items contained in the selection range can be obtained
+ using the indexes() function. Use QItemSelectionModel::selectedIndexes()
+ to get a list of all selected items for a view.
+
+ You can determine whether a given model item lies within a
+ particular range by using the contains() function. Ranges can also
+ be compared using the overloaded operators for equality and
+ inequality, and the intersects() function allows you to determine
+ whether two ranges overlap.
+
+ \sa {Model/View Programming}, QAbstractItemModel, QItemSelection,
+ QItemSelectionModel
+*/
+
+/*!
+ \fn QItemSelectionRange::QItemSelectionRange()
+
+ Constructs an empty selection range.
+*/
+
+/*!
+ \fn QItemSelectionRange::QItemSelectionRange(const QItemSelectionRange &other)
+
+ Copy constructor. Constructs a new selection range with the same contents
+ as the \a other range given.
+
+*/
+
+/*!
+ \fn QItemSelectionRange::QItemSelectionRange(const QModelIndex &topLeft, const QModelIndex &bottomRight)
+
+ Constructs a new selection range containing only the index specified
+ by the \a topLeft and the index \a bottomRight.
+
+*/
+
+/*!
+ \fn QItemSelectionRange::QItemSelectionRange(const QModelIndex &index)
+
+ Constructs a new selection range containing only the model item specified
+ by the model index \a index.
+*/
+
+/*!
+ \fn int QItemSelectionRange::top() const
+
+ Returns the row index corresponding to the uppermost selected row in the
+ selection range.
+
+*/
+
+/*!
+ \fn int QItemSelectionRange::left() const
+
+ Returns the column index corresponding to the leftmost selected column in the
+ selection range.
+*/
+
+/*!
+ \fn int QItemSelectionRange::bottom() const
+
+ Returns the row index corresponding to the lowermost selected row in the
+ selection range.
+
+*/
+
+/*!
+ \fn int QItemSelectionRange::right() const
+
+ Returns the column index corresponding to the rightmost selected column in
+ the selection range.
+
+*/
+
+/*!
+ \fn int QItemSelectionRange::width() const
+
+ Returns the number of selected columns in the selection range.
+
+*/
+
+/*!
+ \fn int QItemSelectionRange::height() const
+
+ Returns the number of selected rows in the selection range.
+
+*/
+
+/*!
+ \fn const QAbstractItemModel *QItemSelectionRange::model() const
+
+ Returns the model that the items in the selection range belong to.
+*/
+
+/*!
+ \fn QModelIndex QItemSelectionRange::topLeft() const
+
+ Returns the index for the item located at the top-left corner of
+ the selection range.
+
+ \sa top(), left(), bottomRight()
+*/
+
+/*!
+ \fn QModelIndex QItemSelectionRange::bottomRight() const
+
+ Returns the index for the item located at the bottom-right corner
+ of the selection range.
+
+ \sa bottom(), right(), topLeft()
+*/
+
+/*!
+ \fn QModelIndex QItemSelectionRange::parent() const
+
+ Returns the parent model item index of the items in the selection range.
+
+*/
+
+/*!
+ \fn bool QItemSelectionRange::contains(const QModelIndex &index) const
+
+ Returns true if the model item specified by the \a index lies within the
+ range of selected items; otherwise returns false.
+*/
+
+/*!
+ \fn bool QItemSelectionRange::contains(int row, int column,
+ const QModelIndex &parentIndex) const
+ \overload
+
+ Returns true if the model item specified by (\a row, \a column)
+ and with \a parentIndex as the parent item lies within the range
+ of selected items; otherwise returns false.
+*/
+
+/*!
+ \fn bool QItemSelectionRange::intersects(const QItemSelectionRange &other) const
+
+ Returns true if this selection range intersects (overlaps with) the \a other
+ range given; otherwise returns false.
+
+*/
+bool QItemSelectionRange::intersects(const QItemSelectionRange &other) const
+{
+ return (isValid() && other.isValid()
+ && parent() == other.parent()
+ && ((top() <= other.top() && bottom() >= other.top())
+ || (top() >= other.top() && top() <= other.bottom()))
+ && ((left() <= other.left() && right() >= other.left())
+ || (left() >= other.left() && left() <= other.right())));
+}
+
+/*!
+ \fn QItemSelectionRange QItemSelectionRange::intersect(const QItemSelectionRange &other) const
+ \obsolete
+
+ Use intersected(\a other) instead.
+*/
+
+/*!
+ \fn QItemSelectionRange QItemSelectionRange::intersected(const QItemSelectionRange &other) const
+ \since 4.2
+
+ Returns a new selection range containing only the items that are found in
+ both the selection range and the \a other selection range.
+*/
+
+QItemSelectionRange QItemSelectionRange::intersect(const QItemSelectionRange &other) const
+{
+ if (model() == other.model() && parent() == other.parent()) {
+ QModelIndex topLeft = model()->index(qMax(top(), other.top()),
+ qMax(left(), other.left()),
+ other.parent());
+ QModelIndex bottomRight = model()->index(qMin(bottom(), other.bottom()),
+ qMin(right(), other.right()),
+ other.parent());
+ return QItemSelectionRange(topLeft, bottomRight);
+ }
+ return QItemSelectionRange();
+}
+
+/*!
+ \fn bool QItemSelectionRange::operator==(const QItemSelectionRange &other) const
+
+ Returns true if the selection range is exactly the same as the \a other
+ range given; otherwise returns false.
+
+*/
+
+/*!
+ \fn bool QItemSelectionRange::operator!=(const QItemSelectionRange &other) const
+
+ Returns true if the selection range differs from the \a other range given;
+ otherwise returns false.
+
+*/
+
+/*!
+ \fn bool QItemSelectionRange::isValid() const
+
+ Returns true if the selection range is valid; otherwise returns false.
+
+*/
+
+/*!
+ Returns the list of model index items stored in the selection.
+*/
+
+QModelIndexList QItemSelectionRange::indexes() const
+{
+ QModelIndex index;
+ QModelIndexList result;
+ if (isValid() && model()) {
+ for (int column = left(); column <= right(); ++column) {
+ for (int row = top(); row <= bottom(); ++row) {
+ index = model()->index(row, column, parent());
+ Qt::ItemFlags flags = model()->flags(index);
+ if ((flags & Qt::ItemIsSelectable) && (flags & Qt::ItemIsEnabled))
+ result.append(index);
+ }
+ }
+ }
+ return result;
+}
+
+/*!
+ \class QItemSelection
+
+ \brief The QItemSelection class manages information about selected items in a model.
+
+ \ingroup model-view
+
+ A QItemSelection describes the items in a model that have been
+ selected by the user. A QItemSelection is basically a list of
+ selection ranges, see QItemSelectionRange. It provides functions for
+ creating and manipulating selections, and selecting a range of items
+ from a model.
+
+ The QItemSelection class is one of the \l{Model/View Classes}
+ and is part of Qt's \l{Model/View Programming}{model/view framework}.
+
+ An item selection can be constructed and initialized to contain a
+ range of items from an existing model. The following example constructs
+ a selection that contains a range of items from the given \c model,
+ beginning at the \c topLeft, and ending at the \c bottomRight.
+
+ \snippet doc/src/snippets/code/src_gui_itemviews_qitemselectionmodel.cpp 0
+
+ An empty item selection can be constructed, and later populated as
+ required. So, if the model is going to be unavailable when we construct
+ the item selection, we can rewrite the above code in the following way:
+
+ \snippet doc/src/snippets/code/src_gui_itemviews_qitemselectionmodel.cpp 1
+
+ QItemSelection saves memory, and avoids unnecessary work, by working with
+ selection ranges rather than recording the model item index for each
+ item in the selection. Generally, an instance of this class will contain
+ a list of non-overlapping selection ranges.
+
+ Use merge() to merge one item selection into another without making
+ overlapping ranges. Use split() to split one selection range into
+ smaller ranges based on a another selection range.
+
+ \sa {Model/View Programming}, QItemSelectionModel
+
+*/
+
+/*!
+ \fn QItemSelection::QItemSelection()
+
+ Constructs an empty selection.
+*/
+
+/*!
+ Constructs an item selection that extends from the top-left model item,
+ specified by the \a topLeft index, to the bottom-right item, specified
+ by \a bottomRight.
+*/
+QItemSelection::QItemSelection(const QModelIndex &topLeft, const QModelIndex &bottomRight)
+{
+ select(topLeft, bottomRight);
+}
+
+/*!
+ Adds the items in the range that extends from the top-left model
+ item, specified by the \a topLeft index, to the bottom-right item,
+ specified by \a bottomRight to the list.
+
+ \note \a topLeft and \a bottomRight must have the same parent.
+*/
+void QItemSelection::select(const QModelIndex &topLeft, const QModelIndex &bottomRight)
+{
+ if (!topLeft.isValid() || !bottomRight.isValid())
+ return;
+
+ if ((topLeft.model() != bottomRight.model())
+ || topLeft.parent() != bottomRight.parent()) {
+ qWarning("Can't select indexes from different model or with different parents");
+ return;
+ }
+ if (topLeft.row() > bottomRight.row() || topLeft.column() > bottomRight.column()) {
+ int top = qMin(topLeft.row(), bottomRight.row());
+ int bottom = qMax(topLeft.row(), bottomRight.row());
+ int left = qMin(topLeft.column(), bottomRight.column());
+ int right = qMax(topLeft.column(), bottomRight.column());
+ QModelIndex tl = topLeft.sibling(top, left);
+ QModelIndex br = bottomRight.sibling(bottom, right);
+ append(QItemSelectionRange(tl, br));
+ return;
+ }
+ append(QItemSelectionRange(topLeft, bottomRight));
+}
+
+/*!
+ Returns true if the selection contains the given \a index; otherwise
+ returns false.
+*/
+
+bool QItemSelection::contains(const QModelIndex &index) const
+{
+ if (index.flags() & Qt::ItemIsSelectable) {
+ QList<QItemSelectionRange>::const_iterator it = begin();
+ for (; it != end(); ++it)
+ if ((*it).contains(index))
+ return true;
+ }
+ return false;
+}
+
+/*!
+ Returns a list of model indexes that correspond to the selected items.
+*/
+
+QModelIndexList QItemSelection::indexes() const
+{
+ QModelIndexList result;
+ QList<QItemSelectionRange>::const_iterator it = begin();
+ for (; it != end(); ++it)
+ result += (*it).indexes();
+ return result;
+}
+
+/*!
+ Merges the \a other selection with this QItemSelection using the
+ \a command given. This method guarantees that no ranges are overlapping.
+
+ Note that only QItemSelectionModel::Select,
+ QItemSelectionModel::Deselect, and QItemSelectionModel::Toggle are
+ supported.
+
+ \sa split()
+*/
+void QItemSelection::merge(const QItemSelection &other, QItemSelectionModel::SelectionFlags command)
+{
+ if (other.isEmpty() ||
+ !(command & QItemSelectionModel::Select ||
+ command & QItemSelectionModel::Deselect ||
+ command & QItemSelectionModel::Toggle))
+ return;
+
+ QItemSelection newSelection = other;
+ // Collect intersections
+ QItemSelection intersections;
+ QItemSelection::iterator it = newSelection.begin();
+ while (it != newSelection.end()) {
+ if (!(*it).isValid()) {
+ it = newSelection.erase(it);
+ continue;
+ }
+ for (int t = 0; t < count(); ++t) {
+ if ((*it).intersects(at(t)))
+ intersections.append(at(t).intersected(*it));
+ }
+ ++it;
+ }
+
+ // Split the old (and new) ranges using the intersections
+ for (int i = 0; i < intersections.count(); ++i) { // for each intersection
+ for (int t = 0; t < count();) { // splitt each old range
+ if (at(t).intersects(intersections.at(i))) {
+ split(at(t), intersections.at(i), this);
+ removeAt(t);
+ } else {
+ ++t;
+ }
+ }
+ // only split newSelection if Toggle is specified
+ for (int n = 0; (command & QItemSelectionModel::Toggle) && n < newSelection.count();) {
+ if (newSelection.at(n).intersects(intersections.at(i))) {
+ split(newSelection.at(n), intersections.at(i), &newSelection);
+ newSelection.removeAt(n);
+ } else {
+ ++n;
+ }
+ }
+ }
+ // do not add newSelection for Deselect
+ if (!(command & QItemSelectionModel::Deselect))
+ operator+=(newSelection);
+}
+
+/*!
+ Splits the selection \a range using the selection \a other range.
+ Removes all items in \a other from \a range and puts the result in \a result.
+ This can be compared with the semantics of the \e subtract operation of a set.
+ \sa merge()
+*/
+
+void QItemSelection::split(const QItemSelectionRange &range,
+ const QItemSelectionRange &other, QItemSelection *result)
+{
+ if (range.parent() != other.parent())
+ return;
+
+ QModelIndex parent = other.parent();
+ int top = range.top();
+ int left = range.left();
+ int bottom = range.bottom();
+ int right = range.right();
+ int other_top = other.top();
+ int other_left = other.left();
+ int other_bottom = other.bottom();
+ int other_right = other.right();
+ const QAbstractItemModel *model = range.model();
+ Q_ASSERT(model);
+ if (other_top > top) {
+ QModelIndex tl = model->index(top, left, parent);
+ QModelIndex br = model->index(other_top - 1, right, parent);
+ result->append(QItemSelectionRange(tl, br));
+ top = other_top;
+ }
+ if (other_bottom < bottom) {
+ QModelIndex tl = model->index(other_bottom + 1, left, parent);
+ QModelIndex br = model->index(bottom, right, parent);
+ result->append(QItemSelectionRange(tl, br));
+ bottom = other_bottom;
+ }
+ if (other_left > left) {
+ QModelIndex tl = model->index(top, left, parent);
+ QModelIndex br = model->index(bottom, other_left - 1, parent);
+ result->append(QItemSelectionRange(tl, br));
+ left = other_left;
+ }
+ if (other_right < right) {
+ QModelIndex tl = model->index(top, other_right + 1, parent);
+ QModelIndex br = model->index(bottom, right, parent);
+ result->append(QItemSelectionRange(tl, br));
+ right = other_right;
+ }
+}
+
+/*!
+ \internal
+
+ returns a QItemSelection where all ranges have been expanded to:
+ Rows: left: 0 and right: columnCount()-1
+ Columns: top: 0 and bottom: rowCount()-1
+*/
+
+QItemSelection QItemSelectionModelPrivate::expandSelection(const QItemSelection &selection,
+ QItemSelectionModel::SelectionFlags command) const
+{
+ if (selection.isEmpty() && !((command & QItemSelectionModel::Rows) ||
+ (command & QItemSelectionModel::Columns)))
+ return selection;
+
+ QItemSelection expanded;
+ if (command & QItemSelectionModel::Rows) {
+ for (int i = 0; i < selection.count(); ++i) {
+ QModelIndex parent = selection.at(i).parent();
+ int colCount = model->columnCount(parent);
+ QModelIndex tl = model->index(selection.at(i).top(), 0, parent);
+ QModelIndex br = model->index(selection.at(i).bottom(), colCount - 1, parent);
+ //we need to merge because the same row could have already been inserted
+ expanded.merge(QItemSelection(tl, br), QItemSelectionModel::Select);
+ }
+ }
+ if (command & QItemSelectionModel::Columns) {
+ for (int i = 0; i < selection.count(); ++i) {
+ QModelIndex parent = selection.at(i).parent();
+ int rowCount = model->rowCount(parent);
+ QModelIndex tl = model->index(0, selection.at(i).left(), parent);
+ QModelIndex br = model->index(rowCount - 1, selection.at(i).right(), parent);
+ //we need to merge because the same column could have already been inserted
+ expanded.merge(QItemSelection(tl, br), QItemSelectionModel::Select);
+ }
+ }
+ return expanded;
+}
+
+/*!
+ \internal
+*/
+void QItemSelectionModelPrivate::_q_rowsAboutToBeRemoved(const QModelIndex &parent,
+ int start, int end)
+{
+ Q_Q(QItemSelectionModel);
+
+ // update current index
+ if (currentIndex.isValid() && parent == currentIndex.parent()
+ && currentIndex.row() >= start && currentIndex.row() <= end) {
+ QModelIndex old = currentIndex;
+ if (start > 0) // there are rows left above the change
+ currentIndex = model->index(start - 1, old.column(), parent);
+ else if (model && end < model->rowCount(parent) - 1) // there are rows left below the change
+ currentIndex = model->index(end + 1, old.column(), parent);
+ else // there are no rows left in the table
+ currentIndex = QModelIndex();
+ emit q->currentChanged(currentIndex, old);
+ emit q->currentRowChanged(currentIndex, old);
+ if (currentIndex.column() != old.column())
+ emit q->currentColumnChanged(currentIndex, old);
+ }
+
+ // update selectionsx
+ QModelIndex tl = model->index(start, 0, parent);
+ QModelIndex br = model->index(end, model->columnCount(parent) - 1, parent);
+ q->select(QItemSelection(tl, br), QItemSelectionModel::Deselect);
+ finalize();
+}
+
+/*!
+ \internal
+*/
+void QItemSelectionModelPrivate::_q_columnsAboutToBeRemoved(const QModelIndex &parent,
+ int start, int end)
+{
+ Q_Q(QItemSelectionModel);
+
+ // update current index
+ if (currentIndex.isValid() && parent == currentIndex.parent()
+ && currentIndex.column() >= start && currentIndex.column() <= end) {
+ QModelIndex old = currentIndex;
+ if (start > 0) // there are columns to the left of the change
+ currentIndex = model->index(old.row(), start - 1, parent);
+ else if (model && end < model->columnCount() - 1) // there are columns to the right of the change
+ currentIndex = model->index(old.row(), end + 1, parent);
+ else // there are no columns left in the table
+ currentIndex = QModelIndex();
+ emit q->currentChanged(currentIndex, old);
+ if (currentIndex.row() != old.row())
+ emit q->currentRowChanged(currentIndex, old);
+ emit q->currentColumnChanged(currentIndex, old);
+ }
+
+ // update selections
+ QModelIndex tl = model->index(0, start, parent);
+ QModelIndex br = model->index(model->rowCount(parent) - 1, end, parent);
+ q->select(QItemSelection(tl, br), QItemSelectionModel::Deselect);
+ finalize();
+}
+
+/*!
+ \internal
+
+ Split selection ranges if columns are about to be inserted in the middle.
+*/
+void QItemSelectionModelPrivate::_q_columnsAboutToBeInserted(const QModelIndex &parent,
+ int start, int end)
+{
+ Q_UNUSED(end);
+ finalize();
+ QList<QItemSelectionRange> split;
+ QList<QItemSelectionRange>::iterator it = ranges.begin();
+ for (; it != ranges.end(); ) {
+ if ((*it).isValid() && (*it).parent() == parent
+ && (*it).left() < start && (*it).right() >= start) {
+ QModelIndex bottomMiddle = model->index((*it).bottom(), start - 1, (*it).parent());
+ QItemSelectionRange left((*it).topLeft(), bottomMiddle);
+ QModelIndex topMiddle = model->index((*it).top(), start, (*it).parent());
+ QItemSelectionRange right(topMiddle, (*it).bottomRight());
+ it = ranges.erase(it);
+ split.append(left);
+ split.append(right);
+ } else {
+ ++it;
+ }
+ }
+ ranges += split;
+}
+
+/*!
+ \internal
+
+ Split selection ranges if rows are about to be inserted in the middle.
+*/
+void QItemSelectionModelPrivate::_q_rowsAboutToBeInserted(const QModelIndex &parent,
+ int start, int end)
+{
+ Q_UNUSED(end);
+ finalize();
+ QList<QItemSelectionRange> split;
+ QList<QItemSelectionRange>::iterator it = ranges.begin();
+ for (; it != ranges.end(); ) {
+ if ((*it).isValid() && (*it).parent() == parent
+ && (*it).top() < start && (*it).bottom() >= start) {
+ QModelIndex middleRight = model->index(start - 1, (*it).right(), (*it).parent());
+ QItemSelectionRange top((*it).topLeft(), middleRight);
+ QModelIndex middleLeft = model->index(start, (*it).left(), (*it).parent());
+ QItemSelectionRange bottom(middleLeft, (*it).bottomRight());
+ it = ranges.erase(it);
+ split.append(top);
+ split.append(bottom);
+ } else {
+ ++it;
+ }
+ }
+ ranges += split;
+}
+
+/*!
+ \internal
+
+ Split selection into individual (persistent) indexes. This is done in
+ preparation for the layoutChanged() signal, where the indexes can be
+ merged again.
+*/
+void QItemSelectionModelPrivate::_q_layoutAboutToBeChanged()
+{
+ savedPersistentIndexes.clear();
+ savedPersistentCurrentIndexes.clear();
+
+ // special case for when all indexes are selected
+ if (ranges.isEmpty() && currentSelection.count() == 1) {
+ QItemSelectionRange range = currentSelection.first();
+ QModelIndex parent = range.parent();
+ if (range.top() == 0
+ && range.left() == 0
+ && range.bottom() == model->rowCount(parent) - 1
+ && range.right() == model->columnCount(parent) - 1) {
+ tableSelected = true;
+ tableParent = parent;
+ tableColCount = model->columnCount(parent);
+ tableRowCount = model->rowCount(parent);
+ return;
+ }
+ }
+ tableSelected = false;
+
+ QModelIndexList indexes = ranges.indexes();
+ QModelIndexList::const_iterator it;
+ for (it = indexes.constBegin(); it != indexes.constEnd(); ++it)
+ savedPersistentIndexes.append(QPersistentModelIndex(*it));
+ indexes = currentSelection.indexes();
+ for (it = indexes.constBegin(); it != indexes.constEnd(); ++it)
+ savedPersistentCurrentIndexes.append(QPersistentModelIndex(*it));
+}
+
+/*!
+ \internal
+
+ Merges \a indexes into an item selection made up of ranges.
+ Assumes that the indexes are sorted.
+*/
+static QItemSelection mergeIndexes(const QList<QPersistentModelIndex> &indexes)
+{
+ QItemSelection colSpans;
+ // merge columns
+ int i = 0;
+ while (i < indexes.count()) {
+ QModelIndex tl = indexes.at(i);
+ QModelIndex br = tl;
+ while (++i < indexes.count()) {
+ QModelIndex next = indexes.at(i);
+ if ((next.parent() == br.parent())
+ && (next.row() == br.row())
+ && (next.column() == br.column() + 1))
+ br = next;
+ else
+ break;
+ }
+ colSpans.append(QItemSelectionRange(tl, br));
+ }
+ // merge rows
+ QItemSelection rowSpans;
+ i = 0;
+ while (i < colSpans.count()) {
+ QModelIndex tl = colSpans.at(i).topLeft();
+ QModelIndex br = colSpans.at(i).bottomRight();
+ QModelIndex prevTl = tl;
+ while (++i < colSpans.count()) {
+ QModelIndex nextTl = colSpans.at(i).topLeft();
+ QModelIndex nextBr = colSpans.at(i).bottomRight();
+ if ((nextTl.column() == prevTl.column()) && (nextBr.column() == br.column())
+ && (nextTl.row() == prevTl.row() + 1) && (nextBr.row() == br.row() + 1)) {
+ br = nextBr;
+ prevTl = nextTl;
+ } else {
+ break;
+ }
+ }
+ rowSpans.append(QItemSelectionRange(tl, br));
+ }
+ return rowSpans;
+}
+
+/*!
+ \internal
+
+ Merge the selected indexes into selection ranges again.
+*/
+void QItemSelectionModelPrivate::_q_layoutChanged()
+{
+ // special case for when all indexes are selected
+ if (tableSelected && tableColCount == model->columnCount(tableParent)
+ && tableRowCount == model->rowCount(tableParent)) {
+ ranges.clear();
+ currentSelection.clear();
+ int bottom = tableRowCount - 1;
+ int right = tableColCount - 1;
+ QModelIndex tl = model->index(0, 0, tableParent);
+ QModelIndex br = model->index(bottom, right, tableParent);
+ currentSelection << QItemSelectionRange(tl, br);
+ tableParent = QModelIndex();
+ tableSelected = false;
+ return;
+ }
+
+ if (savedPersistentCurrentIndexes.isEmpty() && savedPersistentIndexes.isEmpty()) {
+ // either the selection was actually empty, or we
+ // didn't get the layoutAboutToBeChanged() signal
+ return;
+ }
+ // clear the "old" selection
+ ranges.clear();
+ currentSelection.clear();
+
+ // sort the "new" selection, as preparation for merging
+ qStableSort(savedPersistentIndexes.begin(), savedPersistentIndexes.end());
+ qStableSort(savedPersistentCurrentIndexes.begin(), savedPersistentCurrentIndexes.end());
+
+ // update the selection by merging the individual indexes
+ ranges = mergeIndexes(savedPersistentIndexes);
+ currentSelection = mergeIndexes(savedPersistentCurrentIndexes);
+
+ // release the persistent indexes
+ savedPersistentIndexes.clear();
+ savedPersistentCurrentIndexes.clear();
+}
+
+/*!
+ \class QItemSelectionModel
+
+ \brief The QItemSelectionModel class keeps track of a view's selected items.
+
+ \ingroup model-view
+
+ A QItemSelectionModel keeps track of the selected items in a view, or
+ in several views onto the same model. It also keeps track of the
+ currently selected item in a view.
+
+ The QItemSelectionModel class is one of the \l{Model/View Classes}
+ and is part of Qt's \l{Model/View Programming}{model/view framework}.
+
+ The selected items are stored using ranges. Whenever you want to
+ modify the selected items use select() and provide either a
+ QItemSelection, or a QModelIndex and a QItemSelectionModel::SelectionFlag.
+
+ The QItemSelectionModel takes a two layer approach to selection
+ management, dealing with both selected items that have been committed
+ and items that are part of the current selection. The current
+ selected items are part of the current interactive selection (for
+ example with rubber-band selection or keyboard-shift selections).
+
+ To update the currently selected items, use the bitwise OR of
+ QItemSelectionModel::Current and any of the other SelectionFlags.
+ If you omit the QItemSelectionModel::Current command, a new current
+ selection will be created, and the previous one added to the whole
+ selection. All functions operate on both layers; for example,
+ selectedItems() will return items from both layers.
+
+ \sa {Model/View Programming}, QAbstractItemModel, {Chart Example}
+*/
+
+/*!
+ Constructs a selection model that operates on the specified item \a model.
+*/
+QItemSelectionModel::QItemSelectionModel(QAbstractItemModel *model)
+ : QObject(*new QItemSelectionModelPrivate, model)
+{
+ d_func()->model = model;
+ if (model) {
+ connect(model, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)),
+ this, SLOT(_q_rowsAboutToBeRemoved(QModelIndex,int,int)));
+ connect(model, SIGNAL(columnsAboutToBeRemoved(QModelIndex,int,int)),
+ this, SLOT(_q_columnsAboutToBeRemoved(QModelIndex,int,int)));
+ connect(model, SIGNAL(rowsAboutToBeInserted(QModelIndex,int,int)),
+ this, SLOT(_q_rowsAboutToBeInserted(QModelIndex,int,int)));
+ connect(model, SIGNAL(columnsAboutToBeInserted(QModelIndex,int,int)),
+ this, SLOT(_q_columnsAboutToBeInserted(QModelIndex,int,int)));
+ connect(model, SIGNAL(layoutAboutToBeChanged()),
+ this, SLOT(_q_layoutAboutToBeChanged()));
+ connect(model, SIGNAL(layoutChanged()),
+ this, SLOT(_q_layoutChanged()));
+ }
+}
+
+/*!
+ Constructs a selection model that operates on the specified item \a model with \a parent.
+*/
+QItemSelectionModel::QItemSelectionModel(QAbstractItemModel *model, QObject *parent)
+ : QObject(*new QItemSelectionModelPrivate, parent)
+{
+ d_func()->model = model;
+ if (model) {
+ connect(model, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)),
+ this, SLOT(_q_rowsAboutToBeRemoved(QModelIndex,int,int)));
+ connect(model, SIGNAL(columnsAboutToBeRemoved(QModelIndex,int,int)),
+ this, SLOT(_q_columnsAboutToBeRemoved(QModelIndex,int,int)));
+ connect(model, SIGNAL(rowsAboutToBeInserted(QModelIndex,int,int)),
+ this, SLOT(_q_rowsAboutToBeInserted(QModelIndex,int,int)));
+ connect(model, SIGNAL(columnsAboutToBeInserted(QModelIndex,int,int)),
+ this, SLOT(_q_columnsAboutToBeInserted(QModelIndex,int,int)));
+ connect(model, SIGNAL(layoutAboutToBeChanged()),
+ this, SLOT(_q_layoutAboutToBeChanged()));
+ connect(model, SIGNAL(layoutChanged()),
+ this, SLOT(_q_layoutChanged()));
+ }
+}
+
+/*!
+ \internal
+*/
+QItemSelectionModel::QItemSelectionModel(QItemSelectionModelPrivate &dd, QAbstractItemModel *model)
+ : QObject(dd, model)
+{
+ d_func()->model = model;
+ if (model) {
+ connect(model, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)),
+ this, SLOT(_q_rowsAboutToBeRemoved(QModelIndex,int,int)));
+ connect(model, SIGNAL(columnsAboutToBeRemoved(QModelIndex,int,int)),
+ this, SLOT(_q_columnsAboutToBeRemoved(QModelIndex,int,int)));
+ connect(model, SIGNAL(rowsAboutToBeInserted(QModelIndex,int,int)),
+ this, SLOT(_q_rowsAboutToBeInserted(QModelIndex,int,int)));
+ connect(model, SIGNAL(columnsAboutToBeInserted(QModelIndex,int,int)),
+ this, SLOT(_q_columnsAboutToBeInserted(QModelIndex,int,int)));
+ connect(model, SIGNAL(layoutAboutToBeChanged()),
+ this, SLOT(_q_layoutAboutToBeChanged()));
+ connect(model, SIGNAL(layoutChanged()),
+ this, SLOT(_q_layoutChanged()));
+ }
+}
+
+/*!
+ Destroys the selection model.
+*/
+QItemSelectionModel::~QItemSelectionModel()
+{
+ Q_D(QItemSelectionModel);
+ if (d->model) {
+ disconnect(d->model, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)),
+ this, SLOT(_q_rowsAboutToBeRemoved(QModelIndex,int,int)));
+ disconnect(d->model, SIGNAL(columnsAboutToBeRemoved(QModelIndex,int,int)),
+ this, SLOT(_q_columnsAboutToBeRemoved(QModelIndex,int,int)));
+ disconnect(d->model, SIGNAL(rowsAboutToBeInserted(QModelIndex,int,int)),
+ this, SLOT(_q_rowsAboutToBeInserted(QModelIndex,int,int)));
+ disconnect(d->model, SIGNAL(columnsAboutToBeInserted(QModelIndex,int,int)),
+ this, SLOT(_q_columnsAboutToBeInserted(QModelIndex,int,int)));
+ disconnect(d->model, SIGNAL(layoutAboutToBeChanged()),
+ this, SLOT(_q_layoutAboutToBeChanged()));
+ disconnect(d->model, SIGNAL(layoutChanged()),
+ this, SLOT(_q_layoutChanged()));
+ }
+}
+
+/*!
+ Selects the model item \a index using the specified \a command, and emits
+ selectionChanged().
+
+ \sa QItemSelectionModel::SelectionFlags
+*/
+void QItemSelectionModel::select(const QModelIndex &index, QItemSelectionModel::SelectionFlags command)
+{
+ QItemSelection selection(index, index);
+ select(selection, command);
+}
+
+/*!
+ \fn void QItemSelectionModel::currentChanged(const QModelIndex &current, const QModelIndex &previous)
+
+ This signal is emitted whenever the current item changes. The \a previous
+ model item index is replaced by the \a current index as the selection's
+ current item.
+
+ Note that this signal will not be emitted when the item model is reset.
+
+ \sa currentIndex() setCurrentIndex() selectionChanged()
+*/
+
+/*!
+ \fn void QItemSelectionModel::currentColumnChanged(const QModelIndex &current, const QModelIndex &previous)
+
+ This signal is emitted if the \a current item changes and its column is
+ different to the column of the \a previous current item.
+
+ Note that this signal will not be emitted when the item model is reset.
+
+ \sa currentChanged() currentRowChanged() currentIndex() setCurrentIndex()
+*/
+
+/*!
+ \fn void QItemSelectionModel::currentRowChanged(const QModelIndex &current, const QModelIndex &previous)
+
+ This signal is emitted if the \a current item changes and its row is
+ different to the row of the \a previous current item.
+
+ Note that this signal will not be emitted when the item model is reset.
+
+ \sa currentChanged() currentColumnChanged() currentIndex() setCurrentIndex()
+*/
+
+/*!
+ \fn void QItemSelectionModel::selectionChanged(const QItemSelection &selected, const QItemSelection &deselected)
+
+ This signal is emitted whenever the selection changes. The change in the
+ selection is represented as an item selection of \a deselected items and
+ an item selection of \a selected items.
+
+ Note the that the current index changes independently from the selection.
+ Also note that this signal will not be emitted when the item model is reset.
+
+ \sa select() currentChanged()
+*/
+
+/*!
+ \enum QItemSelectionModel::SelectionFlag
+
+ This enum describes the way the selection model will be updated.
+
+ \value NoUpdate No selection will be made.
+ \value Clear The complete selection will be cleared.
+ \value Select All specified indexes will be selected.
+ \value Deselect All specified indexes will be deselected.
+ \value Toggle All specified indexes will be selected or
+ deselected depending on their current state.
+ \value Current The current selection will be updated.
+ \value Rows All indexes will be expanded to span rows.
+ \value Columns All indexes will be expanded to span columns.
+ \value SelectCurrent A combination of Select and Current, provided for
+ convenience.
+ \value ToggleCurrent A combination of Toggle and Current, provided for
+ convenience.
+ \value ClearAndSelect A combination of Clear and Select, provided for
+ convenience.
+*/
+
+/*!
+ Selects the item \a selection using the specified \a command, and emits
+ selectionChanged().
+
+ \sa QItemSelectionModel::SelectionFlag
+*/
+void QItemSelectionModel::select(const QItemSelection &selection, QItemSelectionModel::SelectionFlags command)
+{
+ Q_D(QItemSelectionModel);
+ if (command == NoUpdate)
+ return;
+
+ // store old selection
+ QItemSelection sel = selection;
+ QItemSelection old = d->ranges;
+ old.merge(d->currentSelection, d->currentCommand);
+
+ // expand selection according to SelectionBehavior
+ if (command & Rows || command & Columns)
+ sel = d->expandSelection(sel, command);
+
+ // clear ranges and currentSelection
+ if (command & Clear) {
+ d->ranges.clear();
+ d->currentSelection.clear();
+ }
+
+ // merge and clear currentSelection if Current was not set (ie. start new currentSelection)
+ if (!(command & Current))
+ d->finalize();
+
+ // update currentSelection
+ if (command & Toggle || command & Select || command & Deselect) {
+ d->currentCommand = command;
+ d->currentSelection = sel;
+ }
+
+ // generate new selection, compare with old and emit selectionChanged()
+ QItemSelection newSelection = d->ranges;
+ newSelection.merge(d->currentSelection, d->currentCommand);
+ emitSelectionChanged(newSelection, old);
+}
+
+/*!
+ Clears the selection model. Emits selectionChanged() and currentChanged().
+*/
+void QItemSelectionModel::clear()
+{
+ Q_D(QItemSelectionModel);
+ clearSelection();
+ QModelIndex previous = d->currentIndex;
+ d->currentIndex = QModelIndex();
+ if (previous.isValid()) {
+ emit currentChanged(d->currentIndex, previous);
+ emit currentRowChanged(d->currentIndex, previous);
+ emit currentColumnChanged(d->currentIndex, previous);
+ }
+}
+
+/*!
+ Clears the selection model. Does not emit any signals.
+*/
+void QItemSelectionModel::reset()
+{
+ bool block = blockSignals(true);
+ clear();
+ blockSignals(block);
+}
+
+/*!
+ \since 4.2
+ Clears the selection in the selection model. Emits selectionChanged().
+*/
+void QItemSelectionModel::clearSelection()
+{
+ Q_D(QItemSelectionModel);
+ if (d->ranges.count() == 0 && d->currentSelection.count() == 0)
+ return;
+ QItemSelection selection = d->ranges;
+ selection.merge(d->currentSelection, d->currentCommand);
+ d->ranges.clear();
+ d->currentSelection.clear();
+ emit selectionChanged(QItemSelection(), selection);
+}
+
+
+/*!
+ Sets the model item \a index to be the current item, and emits
+ currentChanged(). 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.
+
+ Depending on the specified \a command, the \a index can also become part
+ of the current selection.
+ \sa select()
+*/
+void QItemSelectionModel::setCurrentIndex(const QModelIndex &index, QItemSelectionModel::SelectionFlags command)
+{
+ Q_D(QItemSelectionModel);
+ if (index == d->currentIndex) {
+ if (command != NoUpdate)
+ select(index, command); // select item
+ return;
+ }
+ QPersistentModelIndex previous = d->currentIndex;
+ d->currentIndex = index; // set current before emitting selection changed below
+ if (command != NoUpdate)
+ select(d->currentIndex, command); // select item
+ emit currentChanged(d->currentIndex, previous);
+ if (d->currentIndex.row() != previous.row() ||
+ d->currentIndex.parent() != previous.parent())
+ emit currentRowChanged(d->currentIndex, previous);
+ if (d->currentIndex.column() != previous.column() ||
+ d->currentIndex.parent() != previous.parent())
+ emit currentColumnChanged(d->currentIndex, previous);
+}
+
+/*!
+ Returns the model item index for the current item, or an invalid index
+ if there is no current item.
+*/
+QModelIndex QItemSelectionModel::currentIndex() const
+{
+ return static_cast<QModelIndex>(d_func()->currentIndex);
+}
+
+/*!
+ Returns true if the given model item \a index is selected.
+*/
+bool QItemSelectionModel::isSelected(const QModelIndex &index) const
+{
+ Q_D(const QItemSelectionModel);
+ if (d->model != index.model() || !index.isValid())
+ return false;
+
+ bool selected = false;
+ // search model ranges
+ QList<QItemSelectionRange>::const_iterator it = d->ranges.begin();
+ for (; it != d->ranges.end(); ++it) {
+ if ((*it).isValid() && (*it).contains(index)) {
+ selected = true;
+ break;
+ }
+ }
+
+ // check currentSelection
+ if (d->currentSelection.count()) {
+ if ((d->currentCommand & Deselect) && selected)
+ selected = !d->currentSelection.contains(index);
+ else if (d->currentCommand & Toggle)
+ selected ^= d->currentSelection.contains(index);
+ else if ((d->currentCommand & Select) && !selected)
+ selected = d->currentSelection.contains(index);
+ }
+
+ if (selected) {
+ Qt::ItemFlags flags = d->model->flags(index);
+ return (flags & Qt::ItemIsSelectable);
+ }
+
+ return false;
+}
+
+/*!
+ Returns true if all items are selected in the \a row with the given
+ \a parent.
+
+ Note that this function is usually faster than calling isSelected()
+ on all items in the same row and that unselectable items are
+ ignored.
+*/
+bool QItemSelectionModel::isRowSelected(int row, const QModelIndex &parent) const
+{
+ Q_D(const QItemSelectionModel);
+ if (parent.isValid() && d->model != parent.model())
+ return false;
+
+ // return false if row exist in currentSelection (Deselect)
+ if (d->currentCommand & Deselect && d->currentSelection.count()) {
+ for (int i=0; i<d->currentSelection.count(); ++i) {
+ if (d->currentSelection.at(i).parent() == parent &&
+ row >= d->currentSelection.at(i).top() &&
+ row <= d->currentSelection.at(i).bottom())
+ return false;
+ }
+ }
+ // return false if ranges in both currentSelection and ranges
+ // intersect and have the same row contained
+ if (d->currentCommand & Toggle && d->currentSelection.count()) {
+ for (int i=0; i<d->currentSelection.count(); ++i)
+ if (d->currentSelection.at(i).top() <= row &&
+ d->currentSelection.at(i).bottom() >= row)
+ for (int j=0; j<d->ranges.count(); ++j)
+ if (d->ranges.at(j).top() <= row && d->ranges.at(j).bottom() >= row
+ && d->currentSelection.at(i).intersected(d->ranges.at(j)).isValid())
+ return false;
+ }
+ // add ranges and currentSelection and check through them all
+ QList<QItemSelectionRange>::const_iterator it;
+ QList<QItemSelectionRange> joined = d->ranges;
+ if (d->currentSelection.count())
+ joined += d->currentSelection;
+ int colCount = d->model->columnCount(parent);
+ for (int column = 0; column < colCount; ++column) {
+ for (it = joined.constBegin(); it != joined.constEnd(); ++it) {
+ if ((*it).contains(row, column, parent)) {
+ bool selectable = false;
+ for (int i = column; !selectable && i <= (*it).right(); ++i) {
+ Qt::ItemFlags flags = d->model->index(row, i, parent).flags();
+ selectable = flags & Qt::ItemIsSelectable;
+ }
+ if (selectable){
+ column = qMax(column, (*it).right());
+ break;
+ }
+ }
+ }
+ if (it == joined.constEnd())
+ return false;
+ }
+ return colCount > 0; // no columns means no selected items
+}
+
+/*!
+ Returns true if all items are selected in the \a column with the given
+ \a parent.
+
+ Note that this function is usually faster than calling isSelected()
+ on all items in the same column and that unselectable items are
+ ignored.
+*/
+bool QItemSelectionModel::isColumnSelected(int column, const QModelIndex &parent) const
+{
+ Q_D(const QItemSelectionModel);
+ if (parent.isValid() && d->model != parent.model())
+ return false;
+
+ // return false if column exist in currentSelection (Deselect)
+ if (d->currentCommand & Deselect && d->currentSelection.count()) {
+ for (int i = 0; i < d->currentSelection.count(); ++i) {
+ if (d->currentSelection.at(i).parent() == parent &&
+ column >= d->currentSelection.at(i).left() &&
+ column <= d->currentSelection.at(i).right())
+ return false;
+ }
+ }
+ // return false if ranges in both currentSelection and the selection model
+ // intersect and have the same column contained
+ if (d->currentCommand & Toggle && d->currentSelection.count()) {
+ for (int i = 0; i < d->currentSelection.count(); ++i) {
+ if (d->currentSelection.at(i).left() <= column &&
+ d->currentSelection.at(i).right() >= column) {
+ for (int j = 0; j < d->ranges.count(); ++j) {
+ if (d->ranges.at(j).left() <= column && d->ranges.at(j).right() >= column
+ && d->currentSelection.at(i).intersected(d->ranges.at(j)).isValid()) {
+ return false;
+ }
+ }
+ }
+ }
+ }
+ // add ranges and currentSelection and check through them all
+ QList<QItemSelectionRange>::const_iterator it;
+ QList<QItemSelectionRange> joined = d->ranges;
+ if (d->currentSelection.count())
+ joined += d->currentSelection;
+ int rowCount = d->model->rowCount(parent);
+ for (int row = 0; row < rowCount; ++row) {
+ for (it = joined.constBegin(); it != joined.constEnd(); ++it) {
+ if ((*it).contains(row, column, parent)) {
+ Qt::ItemFlags flags = d->model->index(row, column, parent).flags();
+ if ((flags & Qt::ItemIsSelectable) && (flags & Qt::ItemIsEnabled)) {
+ row = qMax(row, (*it).bottom());
+ break;
+ }
+ }
+ }
+ if (it == joined.constEnd())
+ return false;
+ }
+ return rowCount > 0; // no rows means no selected items
+}
+
+/*!
+ Returns true if there are any items selected in the \a row with the given
+ \a parent.
+*/
+bool QItemSelectionModel::rowIntersectsSelection(int row, const QModelIndex &parent) const
+{
+ Q_D(const QItemSelectionModel);
+ if (parent.isValid() && d->model != parent.model())
+ return false;
+
+ QItemSelection sel = d->ranges;
+ sel.merge(d->currentSelection, d->currentCommand);
+ for (int i = 0; i < sel.count(); ++i) {
+ int top = sel.at(i).top();
+ int bottom = sel.at(i).bottom();
+ int left = sel.at(i).left();
+ int right = sel.at(i).right();
+ if (top <= row && bottom >= row) {
+ Qt::ItemFlags leftFlags = d->model->index(row, left, parent).flags();
+ Qt::ItemFlags rightFlags = d->model->index(row, right, parent).flags();
+ if ((leftFlags & Qt::ItemIsSelectable) && (leftFlags & Qt::ItemIsEnabled)
+ && (rightFlags & Qt::ItemIsSelectable) && (rightFlags & Qt::ItemIsEnabled))
+ return true;
+ }
+ }
+
+ return false;
+}
+
+/*!
+ Returns true if there are any items selected in the \a column with the given
+ \a parent.
+*/
+bool QItemSelectionModel::columnIntersectsSelection(int column, const QModelIndex &parent) const
+{
+ Q_D(const QItemSelectionModel);
+ if (parent.isValid() && d->model != parent.model())
+ return false;
+
+ QItemSelection sel = d->ranges;
+ sel.merge(d->currentSelection, d->currentCommand);
+ for (int i = 0; i < sel.count(); ++i) {
+ int left = sel.at(i).left();
+ int right = sel.at(i).right();
+ int top = sel.at(i).top();
+ int bottom = sel.at(i).bottom();
+ if (left <= column && right >= column) {
+ Qt::ItemFlags topFlags = d->model->index(top, column, parent).flags();
+ Qt::ItemFlags bottomFlags = d->model->index(bottom, column, parent).flags();
+ if ((topFlags & Qt::ItemIsSelectable) && (topFlags & Qt::ItemIsEnabled)
+ && (bottomFlags & Qt::ItemIsSelectable) && (bottomFlags & Qt::ItemIsEnabled))
+ return true;
+ }
+ }
+
+ return false;
+}
+
+/*!
+ \since 4.2
+
+ Returns true if the selection model contains any selection ranges;
+ otherwise returns false.
+*/
+bool QItemSelectionModel::hasSelection() const
+{
+ Q_D(const QItemSelectionModel);
+ if (d->currentCommand & (Toggle | Deselect)) {
+ QItemSelection sel = d->ranges;
+ sel.merge(d->currentSelection, d->currentCommand);
+ return !sel.isEmpty();
+ }
+ else {
+ return !(d->ranges.isEmpty() && d->currentSelection.isEmpty());
+ }
+}
+
+/*!
+ Returns a list of all selected model item indexes. The list contains no
+ duplicates, and is not sorted.
+*/
+QModelIndexList QItemSelectionModel::selectedIndexes() const
+{
+ Q_D(const QItemSelectionModel);
+ QItemSelection selected = d->ranges;
+ selected.merge(d->currentSelection, d->currentCommand);
+ return selected.indexes();
+}
+
+/*!
+ \since 4.2
+ Returns the indexes in the given \a column for the rows where all columns are selected.
+
+ \sa selectedIndexes(), selectedColumns()
+*/
+
+QModelIndexList QItemSelectionModel::selectedRows(int column) const
+{
+ QModelIndexList indexes;
+ //the QSet contains pairs of parent modelIndex
+ //and row number
+ QSet< QPair<QModelIndex, int> > rowsSeen;
+
+ const QItemSelection ranges = selection();
+ for (int i = 0; i < ranges.count(); ++i) {
+ const QItemSelectionRange &range = ranges.at(i);
+ QModelIndex parent = range.parent();
+ for (int row = range.top(); row <= range.bottom(); row++) {
+ QPair<QModelIndex, int> rowDef = qMakePair(parent, row);
+ if (!rowsSeen.contains(rowDef)) {
+ rowsSeen << rowDef;
+ if (isRowSelected(row, parent)) {
+ indexes.append(model()->index(row, column, parent));
+ }
+ }
+ }
+ }
+
+ return indexes;
+}
+
+/*!
+ \since 4.2
+ Returns the indexes in the given \a row for columns where all rows are selected.
+
+ \sa selectedIndexes(), selectedRows()
+*/
+
+QModelIndexList QItemSelectionModel::selectedColumns(int row) const
+{
+ QModelIndexList indexes;
+ //the QSet contains pairs of parent modelIndex
+ //and column number
+ QSet< QPair<QModelIndex, int> > columnsSeen;
+
+ const QItemSelection ranges = selection();
+ for (int i = 0; i < ranges.count(); ++i) {
+ const QItemSelectionRange &range = ranges.at(i);
+ QModelIndex parent = range.parent();
+ for (int column = range.left(); column <= range.right(); column++) {
+ QPair<QModelIndex, int> columnDef = qMakePair(parent, column);
+ if (!columnsSeen.contains(columnDef)) {
+ columnsSeen << columnDef;
+ if (isColumnSelected(column, parent)) {
+ indexes.append(model()->index(row, column, parent));
+ }
+ }
+ }
+ }
+
+ return indexes;
+}
+
+/*!
+ Returns the selection ranges stored in the selection model.
+*/
+const QItemSelection QItemSelectionModel::selection() const
+{
+ Q_D(const QItemSelectionModel);
+ QItemSelection selected = d->ranges;
+ selected.merge(d->currentSelection, d->currentCommand);
+ int i = 0;
+ // make sure we have no invalid ranges
+ // ### should probably be handled more generic somewhere else
+ while (i<selected.count()) {
+ if (selected.at(i).isValid())
+ ++i;
+ else
+ (selected.removeAt(i));
+ }
+ return selected;
+}
+
+/*!
+ Returns the item model operated on by the selection model.
+*/
+const QAbstractItemModel *QItemSelectionModel::model() const
+{
+ return d_func()->model;
+}
+
+/*!
+ Compares the two selections \a newSelection and \a oldSelection
+ and emits selectionChanged() with the deselected and selected items.
+*/
+void QItemSelectionModel::emitSelectionChanged(const QItemSelection &newSelection,
+ const QItemSelection &oldSelection)
+{
+ // if both selections are empty or equal we return
+ if ((oldSelection.isEmpty() && newSelection.isEmpty()) ||
+ oldSelection == newSelection)
+ return;
+
+ // if either selection is empty we do not need to compare
+ if (oldSelection.isEmpty() || newSelection.isEmpty()) {
+ emit selectionChanged(newSelection, oldSelection);
+ return;
+ }
+
+ QItemSelection deselected = oldSelection;
+ QItemSelection selected = newSelection;
+
+ // remove equal ranges
+ bool advance;
+ for (int o = 0; o < deselected.count(); ++o) {
+ advance = true;
+ for (int s = 0; s < selected.count() && o < deselected.count();) {
+ if (deselected.at(o) == selected.at(s)) {
+ deselected.removeAt(o);
+ selected.removeAt(s);
+ advance = false;
+ } else {
+ ++s;
+ }
+ }
+ if (advance)
+ ++o;
+ }
+
+ // find intersections
+ QItemSelection intersections;
+ for (int o = 0; o < deselected.count(); ++o) {
+ for (int s = 0; s < selected.count(); ++s) {
+ if (deselected.at(o).intersects(selected.at(s)))
+ intersections.append(deselected.at(o).intersected(selected.at(s)));
+ }
+ }
+
+ // compare remaining ranges with intersections and split them to find deselected and selected
+ for (int i = 0; i < intersections.count(); ++i) {
+ // split deselected
+ for (int o = 0; o < deselected.count();) {
+ if (deselected.at(o).intersects(intersections.at(i))) {
+ QItemSelection::split(deselected.at(o), intersections.at(i), &deselected);
+ deselected.removeAt(o);
+ } else {
+ ++o;
+ }
+ }
+ // split selected
+ for (int s = 0; s < selected.count();) {
+ if (selected.at(s).intersects(intersections.at(i))) {
+ QItemSelection::split(selected.at(s), intersections.at(i), &selected);
+ selected.removeAt(s);
+ } else {
+ ++s;
+ }
+ }
+ }
+
+ emit selectionChanged(selected, deselected);
+}
+
+#ifndef QT_NO_DEBUG_STREAM
+QDebug operator<<(QDebug dbg, const QItemSelectionRange &range)
+{
+#ifndef Q_BROKEN_DEBUG_STREAM
+ dbg.nospace() << "QItemSelectionRange(" << range.topLeft()
+ << "," << range.bottomRight() << ")";
+ return dbg.space();
+#else
+ qWarning("This compiler doesn't support streaming QItemSelectionRange to QDebug");
+ return dbg;
+ Q_UNUSED(range);
+#endif
+}
+#endif
+
+QT_END_NAMESPACE
+
+#include "moc_qitemselectionmodel.cpp"
+
+#endif // QT_NO_ITEMVIEWS
diff --git a/src/gui/itemviews/qitemselectionmodel.h b/src/gui/itemviews/qitemselectionmodel.h
new file mode 100644
index 0000000000..24ecf7d721
--- /dev/null
+++ b/src/gui/itemviews/qitemselectionmodel.h
@@ -0,0 +1,229 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QITEMSELECTIONMODEL_H
+#define QITEMSELECTIONMODEL_H
+
+#include <QtCore/qset.h>
+#include <QtCore/qvector.h>
+#include <QtCore/qlist.h>
+#include <QtCore/qabstractitemmodel.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+#ifndef QT_NO_ITEMVIEWS
+
+class Q_GUI_EXPORT QItemSelectionRange
+{
+
+public:
+ inline QItemSelectionRange() {}
+ inline QItemSelectionRange(const QItemSelectionRange &other)
+ : tl(other.tl), br(other.br) {}
+ inline QItemSelectionRange(const QModelIndex &topLeft, const QModelIndex &bottomRight);
+ explicit inline QItemSelectionRange(const QModelIndex &index)
+ { tl = index; br = tl; }
+
+ inline int top() const { return tl.row(); }
+ inline int left() const { return tl.column(); }
+ inline int bottom() const { return br.row(); }
+ inline int right() const { return br.column(); }
+ inline int width() const { return br.column() - tl.column() + 1; }
+ inline int height() const { return br.row() - tl.row() + 1; }
+
+ inline QModelIndex topLeft() const { return QModelIndex(tl); }
+ inline QModelIndex bottomRight() const { return QModelIndex(br); }
+ inline QModelIndex parent() const { return tl.parent(); }
+ inline const QAbstractItemModel *model() const { return tl.model(); }
+
+ inline bool contains(const QModelIndex &index) const
+ {
+ return (parent() == index.parent()
+ && tl.row() <= index.row() && tl.column() <= index.column()
+ && br.row() >= index.row() && br.column() >= index.column());
+ }
+
+ inline bool contains(int row, int column, const QModelIndex &parentIndex) const
+ {
+ return (parent() == parentIndex
+ && tl.row() <= row && tl.column() <= column
+ && br.row() >= row && br.column() >= column);
+ }
+
+ bool intersects(const QItemSelectionRange &other) const;
+ QItemSelectionRange intersect(const QItemSelectionRange &other) const; // ### Qt 5: make QT4_SUPPORT
+ inline QItemSelectionRange intersected(const QItemSelectionRange &other) const
+ { return intersect(other); }
+
+ inline bool operator==(const QItemSelectionRange &other) const
+ { return (tl == other.tl && br == other.br); }
+ inline bool operator!=(const QItemSelectionRange &other) const
+ { return !operator==(other); }
+
+ inline bool isValid() const
+ {
+ return (tl.isValid() && br.isValid() && tl.parent() == br.parent()
+ && top() <= bottom() && left() <= right());
+ }
+
+ QModelIndexList indexes() const;
+
+private:
+ QPersistentModelIndex tl, br;
+};
+Q_DECLARE_TYPEINFO(QItemSelectionRange, Q_MOVABLE_TYPE);
+
+inline QItemSelectionRange::QItemSelectionRange(const QModelIndex &atopLeft,
+ const QModelIndex &abottomRight)
+{ tl = atopLeft; br = abottomRight; }
+
+class QItemSelection;
+class QItemSelectionModelPrivate;
+
+class Q_GUI_EXPORT QItemSelectionModel : public QObject
+{
+ Q_OBJECT
+ Q_DECLARE_PRIVATE(QItemSelectionModel)
+ Q_FLAGS(SelectionFlags)
+
+public:
+
+ enum SelectionFlag {
+ NoUpdate = 0x0000,
+ Clear = 0x0001,
+ Select = 0x0002,
+ Deselect = 0x0004,
+ Toggle = 0x0008,
+ Current = 0x0010,
+ Rows = 0x0020,
+ Columns = 0x0040,
+ SelectCurrent = Select | Current,
+ ToggleCurrent = Toggle | Current,
+ ClearAndSelect = Clear | Select
+ };
+
+ Q_DECLARE_FLAGS(SelectionFlags, SelectionFlag)
+
+ explicit QItemSelectionModel(QAbstractItemModel *model);
+ explicit QItemSelectionModel(QAbstractItemModel *model, QObject *parent);
+ virtual ~QItemSelectionModel();
+
+ QModelIndex currentIndex() const;
+
+ bool isSelected(const QModelIndex &index) const;
+ bool isRowSelected(int row, const QModelIndex &parent) const;
+ bool isColumnSelected(int column, const QModelIndex &parent) const;
+
+ bool rowIntersectsSelection(int row, const QModelIndex &parent) const;
+ bool columnIntersectsSelection(int column, const QModelIndex &parent) const;
+
+ bool hasSelection() const;
+
+ QModelIndexList selectedIndexes() const;
+ QModelIndexList selectedRows(int column = 0) const;
+ QModelIndexList selectedColumns(int row = 0) const;
+ const QItemSelection selection() const;
+
+ const QAbstractItemModel *model() const;
+
+public Q_SLOTS:
+ void setCurrentIndex(const QModelIndex &index, QItemSelectionModel::SelectionFlags command);
+ virtual void select(const QModelIndex &index, QItemSelectionModel::SelectionFlags command);
+ virtual void select(const QItemSelection &selection, QItemSelectionModel::SelectionFlags command);
+ virtual void clear();
+ virtual void reset();
+
+ void clearSelection();
+
+Q_SIGNALS:
+ void selectionChanged(const QItemSelection &selected, const QItemSelection &deselected);
+ void currentChanged(const QModelIndex &current, const QModelIndex &previous);
+ void currentRowChanged(const QModelIndex &current, const QModelIndex &previous);
+ void currentColumnChanged(const QModelIndex &current, const QModelIndex &previous);
+
+protected:
+ QItemSelectionModel(QItemSelectionModelPrivate &dd, QAbstractItemModel *model);
+ void emitSelectionChanged(const QItemSelection &newSelection, const QItemSelection &oldSelection);
+
+private:
+ Q_DISABLE_COPY(QItemSelectionModel)
+ Q_PRIVATE_SLOT(d_func(), void _q_columnsAboutToBeRemoved(const QModelIndex&, int, int))
+ Q_PRIVATE_SLOT(d_func(), void _q_rowsAboutToBeRemoved(const QModelIndex&, int, int))
+ Q_PRIVATE_SLOT(d_func(), void _q_columnsAboutToBeInserted(const QModelIndex&, int, int))
+ Q_PRIVATE_SLOT(d_func(), void _q_rowsAboutToBeInserted(const QModelIndex&, int, int))
+ Q_PRIVATE_SLOT(d_func(), void _q_layoutAboutToBeChanged())
+ Q_PRIVATE_SLOT(d_func(), void _q_layoutChanged())
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(QItemSelectionModel::SelectionFlags)
+
+// dummy implentation of qHash() necessary for instantiating QList<QItemSelectionRange>::toSet() with MSVC
+inline uint qHash(const QItemSelectionRange &) { return 0; }
+
+class Q_GUI_EXPORT QItemSelection : public QList<QItemSelectionRange>
+{
+public:
+ QItemSelection() {}
+ QItemSelection(const QModelIndex &topLeft, const QModelIndex &bottomRight);
+ void select(const QModelIndex &topLeft, const QModelIndex &bottomRight);
+ bool contains(const QModelIndex &index) const;
+ QModelIndexList indexes() const;
+ void merge(const QItemSelection &other, QItemSelectionModel::SelectionFlags command);
+ static void split(const QItemSelectionRange &range,
+ const QItemSelectionRange &other,
+ QItemSelection *result);
+};
+
+#ifndef QT_NO_DEBUG_STREAM
+Q_GUI_EXPORT QDebug operator<<(QDebug, const QItemSelectionRange &);
+#endif
+
+#endif // QT_NO_ITEMVIEWS
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QITEMSELECTIONMODEL_H
diff --git a/src/gui/itemviews/qitemselectionmodel_p.h b/src/gui/itemviews/qitemselectionmodel_p.h
new file mode 100644
index 0000000000..4c11b9f427
--- /dev/null
+++ b/src/gui/itemviews/qitemselectionmodel_p.h
@@ -0,0 +1,111 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QITEMSELECTIONMODEL_P_H
+#define QITEMSELECTIONMODEL_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/qobject_p.h"
+
+QT_BEGIN_NAMESPACE
+
+#ifndef QT_NO_ITEMVIEWS
+class QItemSelectionModelPrivate: public QObjectPrivate
+{
+ Q_DECLARE_PUBLIC(QItemSelectionModel)
+public:
+ QItemSelectionModelPrivate()
+ : model(0),
+ currentCommand(QItemSelectionModel::NoUpdate),
+ tableSelected(false) {}
+
+ QItemSelection expandSelection(const QItemSelection &selection,
+ QItemSelectionModel::SelectionFlags command) const;
+
+ void _q_rowsAboutToBeRemoved(const QModelIndex &parent, int start, int end);
+ void _q_columnsAboutToBeRemoved(const QModelIndex &parent, int start, int end);
+ void _q_rowsAboutToBeInserted(const QModelIndex &parent, int start, int end);
+ void _q_columnsAboutToBeInserted(const QModelIndex &parent, int start, int end);
+ void _q_layoutAboutToBeChanged();
+ void _q_layoutChanged();
+
+ inline void remove(QList<QItemSelectionRange> &r)
+ {
+ QList<QItemSelectionRange>::const_iterator it = r.constBegin();
+ for (; it != r.constEnd(); ++it)
+ ranges.removeAll(*it);
+ }
+
+ inline void finalize()
+ {
+ ranges.merge(currentSelection, currentCommand);
+ if (!currentSelection.isEmpty()) // ### perhaps this should be in QList
+ currentSelection.clear();
+ }
+
+ QPointer<QAbstractItemModel> model;
+ QItemSelection ranges;
+ QItemSelection currentSelection;
+ QPersistentModelIndex currentIndex;
+ QItemSelectionModel::SelectionFlags currentCommand;
+ QList<QPersistentModelIndex> savedPersistentIndexes;
+ QList<QPersistentModelIndex> savedPersistentCurrentIndexes;
+ // optimization when all indexes are selected
+ bool tableSelected;
+ QPersistentModelIndex tableParent;
+ int tableColCount, tableRowCount;
+};
+
+#endif // QT_NO_ITEMVIEWS
+
+QT_END_NAMESPACE
+
+#endif // QITEMSELECTIONMODEL_P_H
diff --git a/src/gui/itemviews/qlistview.cpp b/src/gui/itemviews/qlistview.cpp
new file mode 100644
index 0000000000..07f0a38a9b
--- /dev/null
+++ b/src/gui/itemviews/qlistview.cpp
@@ -0,0 +1,3001 @@
+/***************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qlistview.h"
+
+#ifndef QT_NO_LISTVIEW
+#include <qabstractitemdelegate.h>
+#include <qapplication.h>
+#include <qpainter.h>
+#include <qbitmap.h>
+#include <qvector.h>
+#include <qstyle.h>
+#include <qevent.h>
+#include <qscrollbar.h>
+#include <qrubberband.h>
+#include <private/qlistview_p.h>
+#include <qdebug.h>
+#ifndef QT_NO_ACCESSIBILITY
+#include <qaccessible.h>
+#endif
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QListView
+
+ \brief The QListView class provides a list or icon view onto a model.
+
+ \ingroup model-view
+ \ingroup advanced
+ \mainclass
+
+ A QListView presents items stored in a model, either as a simple
+ non-hierarchical list, or as a collection of icons. This class is used
+ to provide lists and icon views that were previously provided by the
+ \c QListBox and \c QIconView classes, but using the more flexible
+ approach provided by Qt's model/view architecture.
+
+ The QListView class is one of the \l{Model/View Classes}
+ and is part of Qt's \l{Model/View Programming}{model/view framework}.
+
+ This view does not display horizontal or vertical headers; to display
+ a list of items with a horizontal header, use QTreeView instead.
+
+ QListView implements the interfaces defined by the
+ QAbstractItemView class to allow it to display data provided by
+ models derived from the QAbstractItemModel class.
+
+ Items in a list view can be displayed using one of two view modes:
+ In \l ListMode, the items are displayed in the form of a simple list;
+ in \l IconMode, the list view takes the form of an \e{icon view} in
+ which the items are displayed with icons like files in a file manager.
+ By default, the list view is in \l ListMode. To change the view mode,
+ use the setViewMode() function, and to determine the current view mode,
+ use viewMode().
+
+ Items in these views are laid out in the direction specified by the
+ flow() of the list view. The items may be fixed in place, or allowed
+ to move, depending on the view's movement() state.
+
+ If the items in the model cannot be completely laid out in the
+ direction of flow, they can be wrapped at the boundary of the view
+ widget; this depends on isWrapping(). This property is useful when the
+ items are being represented by an icon view.
+
+ The resizeMode() and layoutMode() govern how and when the items are
+ laid out. Items are spaced according to their spacing(), and can exist
+ within a notional grid of size specified by gridSize(). The items can
+ be rendered as large or small icons depending on their iconSize().
+
+ \table 100%
+ \row \o \inlineimage windowsxp-listview.png Screenshot of a Windows XP style list view
+ \o \inlineimage macintosh-listview.png Screenshot of a Macintosh style table view
+ \o \inlineimage plastique-listview.png Screenshot of a Plastique style table view
+ \row \o A \l{Windows XP Style Widget Gallery}{Windows XP style} list view.
+ \o A \l{Macintosh Style Widget Gallery}{Macintosh style} list view.
+ \o A \l{Plastique Style Widget Gallery}{Plastique style} list view.
+ \endtable
+
+ \section1 Improving Performance
+
+ It is possible to give the view hints about the data it is handling in order
+ to improve its performance when displaying large numbers of items. One approach
+ that can be taken for views that are intended to display items with equal sizes
+ is to set the \l uniformItemSizes property to true.
+
+ \sa {View Classes}, QTreeView, QTableView, QListWidget
+*/
+
+/*!
+ \enum QListView::ViewMode
+
+ \value ListMode The items are laid out using TopToBottom flow, with Small size and Static movement
+ \value IconMode The items are laid out using LeftToRight flow, with Large size and Free movement
+*/
+
+/*!
+ \enum QListView::Movement
+
+ \value Static The items cannot be moved by the user.
+ \value Free The items can be moved freely by the user.
+ \value Snap The items snap to the specified grid when moved; see
+ setGridSize().
+*/
+
+/*!
+ \enum QListView::Flow
+
+ \value LeftToRight The items are laid out in the view from the left
+ to the right.
+ \value TopToBottom The items are laid out in the view from the top
+ to the bottom.
+*/
+
+/*!
+ \enum QListView::ResizeMode
+
+ \value Fixed The items will only be laid out the first time the view is shown.
+ \value Adjust The items will be laid out every time the view is resized.
+*/
+
+/*!
+ \enum QListView::LayoutMode
+
+ \value SinglePass The items are laid out all at once.
+ \value Batched The items are laid out in batches of \l batchSize items.
+ \sa batchSize
+*/
+
+/*!
+ \since 4.2
+ \fn void QListView::indexesMoved(const QModelIndexList &indexes)
+
+ This signal is emitted when the specified \a indexes are moved in the view.
+*/
+
+/*!
+ Creates a new QListView with the given \a parent to view a model.
+ Use setModel() to set the model.
+*/
+QListView::QListView(QWidget *parent)
+ : QAbstractItemView(*new QListViewPrivate, parent)
+{
+ setViewMode(ListMode);
+ setSelectionMode(SingleSelection);
+ setAttribute(Qt::WA_MacShowFocusRect);
+ Q_D(QListView); // We rely on a qobject_cast for PM_DefaultFrameWidth to change
+ d->updateStyledFrameWidths(); // hence we have to force an update now that the object has been constructed
+}
+
+/*!
+ \internal
+*/
+QListView::QListView(QListViewPrivate &dd, QWidget *parent)
+ : QAbstractItemView(dd, parent)
+{
+ setViewMode(ListMode);
+ setSelectionMode(SingleSelection);
+ setAttribute(Qt::WA_MacShowFocusRect);
+ Q_D(QListView); // We rely on a qobject_cast for PM_DefaultFrameWidth to change
+ d->updateStyledFrameWidths(); // hence we have to force an update now that the object has been constructed
+}
+
+/*!
+ Destroys the view.
+*/
+QListView::~QListView()
+{
+}
+
+/*!
+ \property QListView::movement
+ \brief whether the items can be moved freely, are snapped to a
+ grid, or cannot be moved at all.
+
+ This property determines how the user can move the items in the
+ view. \l Static means that the items can't be moved the user. \l
+ Free means that the user can drag and drop the items to any
+ position in the view. \l Snap means that the user can drag and
+ drop the items, but only to the positions in a notional grid
+ signified by the gridSize property.
+
+ Setting this property when the view is visible will cause the
+ items to be laid out again.
+
+ By default, this property is set to \l Static.
+
+ \sa gridSize, resizeMode, viewMode
+*/
+void QListView::setMovement(Movement movement)
+{
+ Q_D(QListView);
+ d->modeProperties |= uint(QListViewPrivate::Movement);
+ d->movement = movement;
+
+#ifndef QT_NO_DRAGANDDROP
+ bool movable = (movement != Static);
+ setDragEnabled(movable);
+ d->viewport->setAcceptDrops(movable);
+#endif
+ d->doDelayedItemsLayout();
+}
+
+QListView::Movement QListView::movement() const
+{
+ Q_D(const QListView);
+ return d->movement;
+}
+
+/*!
+ \property QListView::flow
+ \brief which direction the items layout should flow.
+
+ If this property is \l LeftToRight, the items will be laid out left
+ to right. If the \l isWrapping property is true, the layout will wrap
+ when it reaches the right side of the visible area. If this
+ property is \l TopToBottom, the items will be laid out from the top
+ of the visible area, wrapping when it reaches the bottom.
+
+ Setting this property when the view is visible will cause the
+ items to be laid out again.
+
+ By default, this property is set to \l TopToBottom.
+
+ \sa viewMode
+*/
+void QListView::setFlow(Flow flow)
+{
+ Q_D(QListView);
+ d->modeProperties |= uint(QListViewPrivate::Flow);
+ d->flow = flow;
+ d->doDelayedItemsLayout();
+}
+
+QListView::Flow QListView::flow() const
+{
+ Q_D(const QListView);
+ return d->flow;
+}
+
+/*!
+ \property QListView::isWrapping
+ \brief whether the items layout should wrap.
+
+ This property holds whether the layout should wrap when there is
+ no more space in the visible area. The point at which the layout wraps
+ depends on the \l flow property.
+
+ Setting this property when the view is visible will cause the
+ items to be laid out again.
+
+ By default, this property is false.
+
+ \sa viewMode
+*/
+void QListView::setWrapping(bool enable)
+{
+ Q_D(QListView);
+ d->modeProperties |= uint(QListViewPrivate::Wrap);
+ d->setWrapping(enable);
+ d->doDelayedItemsLayout();
+}
+
+bool QListView::isWrapping() const
+{
+ Q_D(const QListView);
+ return d->isWrapping();
+}
+
+/*!
+ \property QListView::resizeMode
+ \brief whether the items are laid out again when the view is resized.
+
+ If this property is \l Adjust, the items will be laid out again
+ when the view is resized. If the value is \l Fixed, the items will
+ not be laid out when the view is resized.
+
+ By default, this property is set to \l Fixed.
+
+ \sa movement, gridSize, viewMode
+*/
+void QListView::setResizeMode(ResizeMode mode)
+{
+ Q_D(QListView);
+ d->modeProperties |= uint(QListViewPrivate::ResizeMode);
+ d->resizeMode = mode;
+}
+
+QListView::ResizeMode QListView::resizeMode() const
+{
+ Q_D(const QListView);
+ return d->resizeMode;
+}
+
+/*!
+ \property QListView::layoutMode
+ \brief determines whether the layout of items should happen immediately or be delayed.
+
+ This property holds the layout mode for the items. When the mode
+ is \l SinglePass (the default), the items are laid out all in one go.
+ When the mode is \l Batched, the items are laid out in batches of \l batchSize
+ items, while processing events. This makes it possible to
+ instantly view and interact with the visible items while the rest
+ are being laid out.
+
+ \sa viewMode
+*/
+void QListView::setLayoutMode(LayoutMode mode)
+{
+ Q_D(QListView);
+ d->layoutMode = mode;
+}
+
+QListView::LayoutMode QListView::layoutMode() const
+{
+ Q_D(const QListView);
+ return d->layoutMode;
+}
+
+/*!
+ \property QListView::spacing
+ \brief the space between items in the layout
+
+ This property is the size of the empty space that is padded around
+ an item in the layout.
+
+ Setting this property when the view is visible will cause the
+ items to be laid out again.
+
+ By default, this property contains a value of 0.
+
+ \sa viewMode
+*/
+// ### Qt5: Use same semantic as layouts (spacing is the size of space
+// *between* items)
+void QListView::setSpacing(int space)
+{
+ Q_D(QListView);
+ d->modeProperties |= uint(QListViewPrivate::Spacing);
+ d->setSpacing(space);
+ d->doDelayedItemsLayout();
+}
+
+int QListView::spacing() const
+{
+ Q_D(const QListView);
+ return d->spacing();
+}
+
+/*!
+ \property QListView::batchSize
+ \brief the number of items laid out in each batch if \l layoutMode is
+ set to \l Batched
+
+ The default value is 100.
+
+ \since 4.2
+*/
+
+void QListView::setBatchSize(int batchSize)
+{
+ Q_D(QListView);
+ if (batchSize <= 0) {
+ qWarning("Invalid batchSize (%d)", batchSize);
+ return;
+ }
+ d->batchSize = batchSize;
+}
+
+int QListView::batchSize() const
+{
+ Q_D(const QListView);
+ return d->batchSize;
+}
+
+/*!
+ \property QListView::gridSize
+ \brief the size of the layout grid
+
+ This property is the size of the grid in which the items are laid
+ out. The default is an empty size which means that there is no
+ grid and the layout is not done in a grid. Setting this property
+ to a non-empty size switches on the grid layout. (When a grid
+ layout is in force the \l spacing property is ignored.)
+
+ Setting this property when the view is visible will cause the
+ items to be laid out again.
+
+ \sa viewMode
+*/
+void QListView::setGridSize(const QSize &size)
+{
+ Q_D(QListView);
+ d->modeProperties |= uint(QListViewPrivate::GridSize);
+ d->setGridSize(size);
+ d->doDelayedItemsLayout();
+}
+
+QSize QListView::gridSize() const
+{
+ Q_D(const QListView);
+ return d->gridSize();
+}
+
+/*!
+ \property QListView::viewMode
+ \brief the view mode of the QListView.
+
+ This property will change the other unset properties to conform
+ with the set view mode. QListView-specific properties that have already been set
+ will not be changed, unless clearPropertyFlags() has been called.
+
+ Setting the view mode will enable or disable drag and drop based on the
+ selected movement. For ListMode, the default movement is \l Static
+ (drag and drop disabled); for IconMode, the default movement is
+ \l Free (drag and drop enabled).
+
+ \sa isWrapping, spacing, gridSize, flow, movement, resizeMode
+*/
+void QListView::setViewMode(ViewMode mode)
+{
+ Q_D(QListView);
+ d->viewMode = mode;
+
+ if (mode == ListMode) {
+ delete d->dynamicListView;
+ d->dynamicListView = 0;
+ if (!d->staticListView)
+ d->staticListView = new QStaticListViewBase(this, d);
+ if (!(d->modeProperties & QListViewPrivate::Wrap))
+ d->setWrapping(false);
+ if (!(d->modeProperties & QListViewPrivate::Spacing))
+ d->setSpacing(0);
+ if (!(d->modeProperties & QListViewPrivate::GridSize))
+ d->setGridSize(QSize());
+ if (!(d->modeProperties & QListViewPrivate::Flow))
+ d->flow = TopToBottom;
+ if (!(d->modeProperties & QListViewPrivate::Movement))
+ d->movement = Static;
+ if (!(d->modeProperties & QListViewPrivate::ResizeMode))
+ d->resizeMode = Fixed;
+ if (!(d->modeProperties & QListViewPrivate::SelectionRectVisible))
+ d->showElasticBand = false;
+ } else {
+ delete d->staticListView;
+ d->staticListView = 0;
+ if (!d->dynamicListView)
+ d->dynamicListView = new QDynamicListViewBase(this, d);
+ if (!(d->modeProperties & QListViewPrivate::Wrap))
+ d->setWrapping(true);
+ if (!(d->modeProperties & QListViewPrivate::Spacing))
+ d->setSpacing(0);
+ if (!(d->modeProperties & QListViewPrivate::GridSize))
+ d->setGridSize(QSize());
+ if (!(d->modeProperties & QListViewPrivate::Flow))
+ d->flow = LeftToRight;
+ if (!(d->modeProperties & QListViewPrivate::Movement))
+ d->movement = Free;
+ if (!(d->modeProperties & QListViewPrivate::ResizeMode))
+ d->resizeMode = Fixed;
+ if (!(d->modeProperties & QListViewPrivate::SelectionRectVisible))
+ d->showElasticBand = true;
+ }
+
+#ifndef QT_NO_DRAGANDDROP
+ bool movable = (d->movement != Static);
+ setDragEnabled(movable);
+ setAcceptDrops(movable);
+#endif
+ d->clear();
+ d->doDelayedItemsLayout();
+}
+
+QListView::ViewMode QListView::viewMode() const
+{
+ Q_D(const QListView);
+ return d->viewMode;
+}
+
+/*!
+ Clears the QListView-specific property flags. See \l{viewMode}.
+
+ Properties inherited from QAbstractItemView are not covered by the
+ property flags. Specifically, \l{QAbstractItemView::dragEnabled}
+ {dragEnabled} and \l{QAbstractItemView::acceptDrops}
+ {acceptsDrops} are computed by QListView when calling
+ setMovement() or setViewMode().
+*/
+void QListView::clearPropertyFlags()
+{
+ Q_D(QListView);
+ d->modeProperties = 0;
+}
+
+/*!
+ Returns true if the \a row is hidden; otherwise returns false.
+*/
+bool QListView::isRowHidden(int row) const
+{
+ Q_D(const QListView);
+ return d->isHidden(row);
+}
+
+/*!
+ If \a hide is true, the given \a row will be hidden; otherwise
+ the \a row will be shown.
+*/
+void QListView::setRowHidden(int row, bool hide)
+{
+ Q_D(QListView);
+ const bool hidden = d->isHidden(row);
+ if (d->viewMode == ListMode) {
+ if (hide && !hidden)
+ d->hiddenRows.append(d->model->index(row, 0));
+ else if (!hide && hidden)
+ d->hiddenRows.remove(d->hiddenRows.indexOf(d->model->index(row, 0)));
+ d->doDelayedItemsLayout();
+ } else {
+ if (hide && !hidden) {
+ d->dynamicListView->removeItem(row);
+ d->hiddenRows.append(d->model->index(row, 0));
+ } else if (!hide && hidden) {
+ d->hiddenRows.remove(d->hiddenRows.indexOf(d->model->index(row, 0)));
+ d->dynamicListView->insertItem(row);
+ }
+ if (d->resizeMode == Adjust)
+ d->doDelayedItemsLayout();
+ d->viewport->update();
+ }
+}
+
+/*!
+ \reimp
+*/
+QRect QListView::visualRect(const QModelIndex &index) const
+{
+ Q_D(const QListView);
+ return d->mapToViewport(rectForIndex(index), d->viewMode == QListView::ListMode);
+}
+
+/*!
+ \reimp
+*/
+void QListView::scrollTo(const QModelIndex &index, ScrollHint hint)
+{
+ Q_D(QListView);
+
+ if (index.parent() != d->root || index.column() != d->column)
+ return;
+
+ const QRect rect = visualRect(index);
+ if (hint == EnsureVisible && d->viewport->rect().contains(rect)) {
+ d->setDirtyRegion(rect);
+ return;
+ }
+
+ if (d->flow == QListView::TopToBottom || d->isWrapping()) // vertical
+ verticalScrollBar()->setValue(d->verticalScrollToValue(index, rect, hint));
+
+ if (d->flow == QListView::LeftToRight || d->isWrapping()) // horizontal
+ horizontalScrollBar()->setValue(d->horizontalScrollToValue(index, rect, hint));
+}
+
+int QListViewPrivate::horizontalScrollToValue(const QModelIndex &index, const QRect &rect,
+ QListView::ScrollHint hint) const
+{
+ Q_Q(const QListView);
+ const QRect area = viewport->rect();
+ const bool leftOf = q->isRightToLeft()
+ ? (rect.left() < area.left()) && (rect.right() < area.right())
+ : rect.left() < area.left();
+ const bool rightOf = q->isRightToLeft()
+ ? rect.right() > area.right()
+ : (rect.right() > area.right()) && (rect.left() > area.left());
+ int horizontalValue = q->horizontalScrollBar()->value();
+
+ // ScrollPerItem
+ if (q->horizontalScrollMode() == QAbstractItemView::ScrollPerItem && viewMode == QListView::ListMode) {
+ const QListViewItem item = indexToListViewItem(index);
+ const QRect rect = q->visualRect(index);
+ horizontalValue = staticListView->horizontalPerItemValue(itemIndex(item),
+ horizontalValue, area.width(),
+ leftOf, rightOf, isWrapping(), hint, rect.width());
+ } else { // ScrollPerPixel
+ if (q->isRightToLeft()) {
+ if (hint == QListView::PositionAtCenter) {
+ horizontalValue += ((area.width() - rect.width()) / 2) - rect.left();
+ } else {
+ if (leftOf)
+ horizontalValue -= rect.left();
+ else if (rightOf)
+ horizontalValue += qMin(rect.left(), area.width() - rect.right());
+ }
+ } else {
+ if (hint == QListView::PositionAtCenter) {
+ horizontalValue += rect.left() - ((area.width()- rect.width()) / 2);
+ } else {
+ if (leftOf)
+ horizontalValue += rect.left();
+ else if (rightOf)
+ horizontalValue += qMin(rect.left(), rect.right() - area.width());
+ }
+ }
+ }
+ return horizontalValue;
+}
+
+int QListViewPrivate::verticalScrollToValue(const QModelIndex &index, const QRect &rect,
+ QListView::ScrollHint hint) const
+{
+ Q_Q(const QListView);
+
+ const QRect area = viewport->rect();
+ const bool above = (hint == QListView::EnsureVisible && rect.top() < area.top());
+ const bool below = (hint == QListView::EnsureVisible && rect.bottom() > area.bottom());
+
+ int verticalValue = q->verticalScrollBar()->value();
+
+ // ScrollPerItem
+ if (q->verticalScrollMode() == QAbstractItemView::ScrollPerItem && viewMode == QListView::ListMode) {
+ const QListViewItem item = indexToListViewItem(index);
+ const QRect rect = q->visualRect(index);
+ verticalValue = staticListView->verticalPerItemValue(itemIndex(item),
+ verticalValue, area.height(),
+ above, below, isWrapping(), hint, rect.height());
+
+ } else { // ScrollPerPixel
+ QRect adjusted = rect.adjusted(-spacing(), -spacing(), spacing(), spacing());
+ if (hint == QListView::PositionAtTop || above)
+ verticalValue += adjusted.top();
+ else if (hint == QListView::PositionAtBottom || below)
+ verticalValue += qMin(adjusted.top(), adjusted.bottom() - area.height() + 1);
+ else if (hint == QListView::PositionAtCenter)
+ verticalValue += adjusted.top() - ((area.height() - adjusted.height()) / 2);
+ }
+
+ return verticalValue;
+}
+
+void QListViewPrivate::selectAll(QItemSelectionModel::SelectionFlags command)
+{
+ if (!selectionModel)
+ return;
+
+ QItemSelection selection;
+ QModelIndex topLeft;
+ int row = 0;
+ const int colCount = model->columnCount(root);
+ for(; row < model->rowCount(root); ++row) {
+ if (isHidden(row)) {
+ //it might be the end of a selection range
+ if (topLeft.isValid()) {
+ QModelIndex bottomRight = model->index(row - 1, colCount - 1, root);
+ selection.append(QItemSelectionRange(topLeft, bottomRight));
+ topLeft = QModelIndex();
+ }
+ continue;
+ }
+
+ if (!topLeft.isValid()) //start of a new selection range
+ topLeft = model->index(row, 0, root);
+ }
+
+ if (topLeft.isValid()) {
+ //last selected range
+ QModelIndex bottomRight = model->index(row - 1, colCount - 1, root);
+ selection.append(QItemSelectionRange(topLeft, bottomRight));
+ }
+
+ if (!selection.isEmpty())
+ selectionModel->select(selection, command);
+}
+
+
+/*!
+ \internal
+*/
+void QListView::reset()
+{
+ Q_D(QListView);
+ d->clear();
+ d->hiddenRows.clear();
+ QAbstractItemView::reset();
+}
+
+/*!
+ \internal
+*/
+void QListView::setRootIndex(const QModelIndex &index)
+{
+ Q_D(QListView);
+ d->column = qBound(0, d->column, d->model->columnCount(index) - 1);
+ QAbstractItemView::setRootIndex(index);
+ // sometimes we get an update before reset() is called
+ d->clear();
+ d->hiddenRows.clear();
+}
+
+/*!
+ \internal
+
+ Scroll the view contents by \a dx and \a dy.
+*/
+void QListView::scrollContentsBy(int dx, int dy)
+{
+ Q_D(QListView);
+
+ d->delayedAutoScroll.stop(); // auto scroll was canceled by the user scrolling
+
+ if (d->viewMode == ListMode)
+ d->staticListView->scrollContentsBy(dx, dy);
+ else if (state() == DragSelectingState)
+ d->scrollElasticBandBy(isRightToLeft() ? -dx : dx, dy);
+
+ d->scrollContentsBy(isRightToLeft() ? -dx : dx, dy);
+
+ // update the dragged items
+ if (d->viewMode == IconMode) // ### move to dynamic class
+ if (!d->dynamicListView->draggedItems.isEmpty())
+ d->setDirtyRegion(d->dynamicListView->draggedItemsRect().translated(dx, dy));
+}
+
+/*!
+ \internal
+
+ Resize the internal contents to \a width and \a height and set the
+ scroll bar ranges accordingly.
+*/
+void QListView::resizeContents(int width, int height)
+{
+ Q_D(QListView);
+ d->setContentsSize(width, height);
+}
+
+/*!
+ \internal
+*/
+QSize QListView::contentsSize() const
+{
+ Q_D(const QListView);
+ return d->contentsSize();
+}
+
+/*!
+ \reimp
+*/
+void QListView::dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight)
+{
+ Q_D(QListView);
+ if (d->viewMode == IconMode)
+ d->dynamicListView->dataChanged(topLeft, bottomRight);
+ QAbstractItemView::dataChanged(topLeft, bottomRight);
+}
+
+/*!
+ \reimp
+*/
+void QListView::rowsInserted(const QModelIndex &parent, int start, int end)
+{
+ Q_D(QListView);
+ // ### be smarter about inserted items
+ d->clear();
+ d->doDelayedItemsLayout();
+ QAbstractItemView::rowsInserted(parent, start, end);
+}
+
+/*!
+ \reimp
+*/
+void QListView::rowsAboutToBeRemoved(const QModelIndex &parent, int start, int end)
+{
+ Q_D(QListView);
+ // if the parent is above d->root in the tree, nothing will happen
+ QAbstractItemView::rowsAboutToBeRemoved(parent, start, end);
+ if (parent == d->root) {
+ for (int i = d->hiddenRows.count() - 1; i >= 0; --i) {
+ int hiddenRow = d->hiddenRows.at(i).row();
+ if (hiddenRow >= start && hiddenRow <= end) {
+ d->hiddenRows.remove(i);
+ }
+ }
+ }
+ d->clear();
+ d->doDelayedItemsLayout();
+}
+
+/*!
+ \reimp
+*/
+void QListView::mouseMoveEvent(QMouseEvent *e)
+{
+ Q_D(QListView);
+ QAbstractItemView::mouseMoveEvent(e);
+ if (state() == DragSelectingState
+ && d->showElasticBand
+ && d->selectionMode != SingleSelection
+ && d->selectionMode != NoSelection) {
+ QRect rect(d->pressedPosition, e->pos() + QPoint(horizontalOffset(), verticalOffset()));
+ rect = rect.normalized();
+ d->setDirtyRegion(d->mapToViewport(rect.united(d->elasticBand), d->viewMode == QListView::ListMode));
+ d->elasticBand = rect;
+ }
+}
+
+/*!
+ \reimp
+*/
+void QListView::mouseReleaseEvent(QMouseEvent *e)
+{
+ Q_D(QListView);
+ QAbstractItemView::mouseReleaseEvent(e);
+ // #### move this implementation into a dynamic class
+ if (d->showElasticBand && d->elasticBand.isValid()) {
+ d->setDirtyRegion(d->mapToViewport(d->elasticBand, d->viewMode == QListView::ListMode));
+ d->elasticBand = QRect();
+ }
+}
+
+/*!
+ \reimp
+*/
+void QListView::timerEvent(QTimerEvent *e)
+{
+ Q_D(QListView);
+ if (e->timerId() == d->batchLayoutTimer.timerId()) {
+ if (d->doItemsLayout(d->batchSize)) { // layout is done
+ d->batchLayoutTimer.stop();
+ updateGeometries();
+ d->viewport->update();
+ }
+ }
+ QAbstractItemView::timerEvent(e);
+}
+
+/*!
+ \reimp
+*/
+void QListView::resizeEvent(QResizeEvent *e)
+{
+ Q_D(QListView);
+ if (d->delayedPendingLayout)
+ return;
+
+ QSize delta = e->size() - e->oldSize();
+
+ if (delta.isNull())
+ return;
+
+ bool listWrap = (d->viewMode == ListMode) && d->wrapItemText;
+ bool flowDimensionChanged = (d->flow == LeftToRight && delta.width() != 0)
+ || (d->flow == TopToBottom && delta.height() != 0);
+
+ // We post a delayed relayout in the following cases :
+ // - we're wrapping
+ // - the state is NoState, we're adjusting and the size has changed in the flowing direction
+ if (listWrap
+ || (state() == NoState && d->resizeMode == Adjust && flowDimensionChanged)) {
+ d->doDelayedItemsLayout(100); // wait 1/10 sec before starting the layout
+ } else {
+ QAbstractItemView::resizeEvent(e);
+ }
+}
+
+#ifndef QT_NO_DRAGANDDROP
+
+/*!
+ \reimp
+*/
+void QListView::dragMoveEvent(QDragMoveEvent *e)
+{
+ // ### move implementation to dynamic
+ Q_D(QListView);
+ if (e->source() == this && d->viewMode == IconMode) {
+ // the ignore by default
+ e->ignore();
+ if (d->canDecode(e)) {
+ // get old dragged items rect
+ QRect itemsRect = d->dynamicListView->itemsRect(d->dynamicListView->draggedItems);
+ d->setDirtyRegion(itemsRect.translated(d->dynamicListView->draggedItemsDelta()));
+ // update position
+ d->dynamicListView->draggedItemsPos = e->pos();
+ // get new items rect
+ d->setDirtyRegion(itemsRect.translated(d->dynamicListView->draggedItemsDelta()));
+ // set the item under the cursor to current
+ QModelIndex index;
+ if (d->movement == Snap) {
+ QRect rect(d->dynamicListView->snapToGrid(e->pos() + d->offset()), d->gridSize());
+ d->intersectingSet(rect);
+ index = d->intersectVector.count() > 0
+ ? d->intersectVector.last() : QModelIndex();
+ } else {
+ index = indexAt(e->pos());
+ }
+ // check if we allow drops here
+ if (e->source() == this && d->dynamicListView->draggedItems.contains(index))
+ e->accept(); // allow changing item position
+ else if (d->model->flags(index) & Qt::ItemIsDropEnabled)
+ e->accept(); // allow dropping on dropenabled items
+ else if (!index.isValid())
+ e->accept(); // allow dropping in empty areas
+ }
+ // do autoscrolling
+ if (d->shouldAutoScroll(e->pos()))
+ startAutoScroll();
+ } else { // not internal
+ QAbstractItemView::dragMoveEvent(e);
+ }
+}
+
+/*!
+ \reimp
+*/
+void QListView::dragLeaveEvent(QDragLeaveEvent *e)
+{
+ // ### move implementation to dynamic
+ Q_D(QListView);
+ if (d->viewMode == IconMode) {
+ d->viewport->update(d->dynamicListView->draggedItemsRect()); // erase the area
+ d->dynamicListView->draggedItemsPos = QPoint(-1, -1); // don't draw the dragged items
+ }
+ QAbstractItemView::dragLeaveEvent(e);
+}
+
+/*!
+ \reimp
+*/
+void QListView::dropEvent(QDropEvent *event)
+{
+ Q_D(QListView);
+ if (event->source() == this && d->viewMode == IconMode)
+ internalDrop(event); // ### move to dynamic
+ else
+ QAbstractItemView::dropEvent(event);
+}
+
+/*!
+ \reimp
+*/
+void QListView::startDrag(Qt::DropActions supportedActions)
+{
+ Q_D(QListView);
+ if (d->viewMode == IconMode) // ### move to dynamic
+ internalDrag(supportedActions);
+ else
+ QAbstractItemView::startDrag(supportedActions);
+}
+
+/*!
+ \internal
+
+ Called whenever items from the view is dropped on the viewport.
+ The \a event provides additional information.
+*/
+void QListView::internalDrop(QDropEvent *event)
+{
+ Q_D(QListView);
+ if (d->viewMode == QListView::ListMode)
+ return;
+
+ // ### move to dynamic class
+ QPoint offset(horizontalOffset(), verticalOffset());
+ QPoint end = event->pos() + offset;
+ QPoint start = d->pressedPosition;
+ QPoint delta = (d->movement == Snap ?
+ d->dynamicListView->snapToGrid(end)
+ - d->dynamicListView->snapToGrid(start) : end - start);
+ QSize contents = d->contentsSize();
+ QList<QModelIndex> indexes = d->selectionModel->selectedIndexes();
+ for (int i = 0; i < indexes.count(); ++i) {
+ QModelIndex index = indexes.at(i);
+ QRect rect = rectForIndex(index);
+ d->setDirtyRegion(d->mapToViewport(rect, d->viewMode == QListView::ListMode));
+ QPoint dest = rect.topLeft() + delta;
+ if (isRightToLeft())
+ dest.setX(d->flipX(dest.x()) - rect.width());
+ d->dynamicListView->moveItem(index.row(), dest);
+ d->setDirtyRegion(visualRect(index));
+ }
+ stopAutoScroll();
+ d->dynamicListView->draggedItems.clear();
+ emit indexesMoved(indexes);
+ event->accept(); // we have handled the event
+ // if the size has not grown, we need to check if it has shrinked
+ if (d->dynamicListView
+ && (d->contentsSize().width() <= contents.width()
+ || d->contentsSize().height() <= contents.height())) {
+ d->dynamicListView->updateContentsSize();
+ }
+ if (d->contentsSize() != contents)
+ updateGeometries();
+}
+
+/*!
+ \internal
+
+ Called whenever the user starts dragging items and the items are movable,
+ enabling internal dragging and dropping of items.
+*/
+void QListView::internalDrag(Qt::DropActions supportedActions)
+{
+ Q_D(QListView);
+ if (d->viewMode == QListView::ListMode)
+ return;
+
+ // #### move to dynamic class
+
+ // This function does the same thing as in QAbstractItemView::startDrag(),
+ // plus adding viewitems to the draggedItems list.
+ // We need these items to draw the drag items
+ QModelIndexList indexes = d->selectionModel->selectedIndexes();
+ if (indexes.count() > 0 ) {
+ if (d->viewport->acceptDrops()) {
+ QModelIndexList::ConstIterator it = indexes.constBegin();
+ for (; it != indexes.constEnd(); ++it)
+ if (d->model->flags(*it) & Qt::ItemIsDragEnabled
+ && (*it).column() == d->column)
+ d->dynamicListView->draggedItems.push_back(*it);
+ }
+ QDrag *drag = new QDrag(this);
+ drag->setMimeData(d->model->mimeData(indexes));
+ Qt::DropAction action = drag->exec(supportedActions, Qt::CopyAction);
+ d->dynamicListView->draggedItems.clear();
+ if (action == Qt::MoveAction)
+ d->clearOrRemove();
+ }
+}
+
+#endif // QT_NO_DRAGANDDROP
+
+/*!
+ \reimp
+*/
+QStyleOptionViewItem QListView::viewOptions() const
+{
+ Q_D(const QListView);
+ QStyleOptionViewItem option = QAbstractItemView::viewOptions();
+ if (!d->iconSize.isValid()) { // otherwise it was already set in abstractitemview
+ int pm = (d->viewMode == ListMode
+ ? style()->pixelMetric(QStyle::PM_ListViewIconSize, 0, this)
+ : style()->pixelMetric(QStyle::PM_IconViewIconSize, 0, this));
+ option.decorationSize = QSize(pm, pm);
+ }
+ if (d->viewMode == IconMode) {
+ option.showDecorationSelected = false;
+ option.decorationPosition = QStyleOptionViewItem::Top;
+ option.displayAlignment = Qt::AlignCenter;
+ } else {
+ option.decorationPosition = QStyleOptionViewItem::Left;
+ }
+ return option;
+}
+
+/*!
+ \reimp
+*/
+void QListView::paintEvent(QPaintEvent *e)
+{
+ Q_D(QListView);
+ if (!d->itemDelegate)
+ return;
+ QStyleOptionViewItemV4 option = d->viewOptionsV4();
+ QPainter painter(d->viewport);
+ QRect area = e->rect();
+
+ QVector<QModelIndex> toBeRendered;
+// QVector<QRect> rects = e->region().rects();
+// for (int i = 0; i < rects.size(); ++i) {
+// d->intersectingSet(rects.at(i).translated(horizontalOffset(), verticalOffset()));
+// toBeRendered += d->intersectVector;
+// }
+ d->intersectingSet(e->rect().translated(horizontalOffset(), verticalOffset()), false);
+ toBeRendered = d->intersectVector;
+
+ const QModelIndex current = currentIndex();
+ const QModelIndex hover = d->hover;
+ const QAbstractItemModel *itemModel = d->model;
+ const QItemSelectionModel *selections = d->selectionModel;
+ const bool focus = (hasFocus() || d->viewport->hasFocus()) && current.isValid();
+ const bool alternate = d->alternatingColors;
+ const QStyle::State state = option.state;
+ const QAbstractItemView::State viewState = this->state();
+ const bool enabled = (state & QStyle::State_Enabled) != 0;
+
+ bool alternateBase = false;
+ int previousRow = -2; // trigger the alternateBase adjustment on first pass
+
+ QVector<QModelIndex>::const_iterator end = toBeRendered.constEnd();
+ for (QVector<QModelIndex>::const_iterator it = toBeRendered.constBegin(); it != end; ++it) {
+ Q_ASSERT((*it).isValid());
+ option.rect = visualRect(*it);
+ option.state = state;
+ if (selections && selections->isSelected(*it))
+ option.state |= QStyle::State_Selected;
+ if (enabled) {
+ QPalette::ColorGroup cg;
+ if ((itemModel->flags(*it) & Qt::ItemIsEnabled) == 0) {
+ option.state &= ~QStyle::State_Enabled;
+ cg = QPalette::Disabled;
+ } else {
+ cg = QPalette::Normal;
+ }
+ option.palette.setCurrentColorGroup(cg);
+ }
+ if (focus && current == *it) {
+ option.state |= QStyle::State_HasFocus;
+ if (viewState == EditingState)
+ option.state |= QStyle::State_Editing;
+ }
+ if (*it == hover)
+ option.state |= QStyle::State_MouseOver;
+ else
+ option.state &= ~QStyle::State_MouseOver;
+
+ if (alternate) {
+ int row = (*it).row();
+ if (row != previousRow + 1) {
+ // adjust alternateBase according to rows in the "gap"
+ if (!d->hiddenRows.isEmpty()) {
+ for (int r = qMax(previousRow + 1, 0); r < row; ++r) {
+ if (!d->isHidden(r))
+ alternateBase = !alternateBase;
+ }
+ } else {
+ alternateBase = (row & 1) != 0;
+ }
+ }
+ if (alternateBase) {
+ option.features |= QStyleOptionViewItemV2::Alternate;
+ } else {
+ option.features &= ~QStyleOptionViewItemV2::Alternate;
+ }
+
+ // draw background of the item (only alternate row). rest of the background
+ // is provided by the delegate
+ QStyle::State oldState = option.state;
+ option.state &= ~QStyle::State_Selected;
+ style()->drawPrimitive(QStyle::PE_PanelItemViewRow, &option, &painter, this);
+ option.state = oldState;
+
+ alternateBase = !alternateBase;
+ previousRow = row;
+ }
+
+ if (const QWidget *widget = d->editorForIndex(*it).editor) {
+ QRegion itemGeometry(option.rect);
+ QRegion widgetGeometry(widget->geometry());
+ painter.save();
+ painter.setClipRegion(itemGeometry.subtracted(widgetGeometry));
+ d->delegateForIndex(*it)->paint(&painter, option, *it);
+ painter.restore();
+ } else {
+ d->delegateForIndex(*it)->paint(&painter, option, *it);
+ }
+ }
+
+#ifndef QT_NO_DRAGANDDROP
+ // #### move this implementation into a dynamic class
+ if (d->viewMode == IconMode)
+ if (!d->dynamicListView->draggedItems.isEmpty()
+ && d->viewport->rect().contains(d->dynamicListView->draggedItemsPos)) {
+ QPoint delta = d->dynamicListView->draggedItemsDelta();
+ painter.translate(delta.x(), delta.y());
+ d->dynamicListView->drawItems(&painter, d->dynamicListView->draggedItems);
+ }
+ // FIXME: Until the we can provide a proper drop indicator
+ // in IconMode, it makes no sense to show it
+ if (d->viewMode == ListMode)
+ d->paintDropIndicator(&painter);
+#endif
+
+#ifndef QT_NO_RUBBERBAND
+ // #### move this implementation into a dynamic class
+ if (d->showElasticBand && d->elasticBand.isValid()) {
+ QStyleOptionRubberBand opt;
+ opt.initFrom(this);
+ opt.shape = QRubberBand::Rectangle;
+ opt.opaque = false;
+ opt.rect = d->mapToViewport(d->elasticBand, false).intersected(
+ d->viewport->rect().adjusted(-16, -16, 16, 16));
+ painter.save();
+ style()->drawControl(QStyle::CE_RubberBand, &opt, &painter);
+ painter.restore();
+ }
+#endif
+}
+
+/*!
+ \reimp
+*/
+QModelIndex QListView::indexAt(const QPoint &p) const
+{
+ Q_D(const QListView);
+ QRect rect(p.x() + horizontalOffset(), p.y() + verticalOffset(), 1, 1);
+ d->intersectingSet(rect);
+ QModelIndex index = d->intersectVector.count() > 0
+ ? d->intersectVector.last() : QModelIndex();
+ if (index.isValid() && visualRect(index).contains(p))
+ return index;
+ return QModelIndex();
+}
+
+/*!
+ \reimp
+*/
+int QListView::horizontalOffset() const
+{
+ Q_D(const QListView);
+ // ### split into static and dynamic
+ if (horizontalScrollMode() == QAbstractItemView::ScrollPerItem && d->viewMode == ListMode) {
+ if (d->isWrapping()) {
+ if (d->flow == TopToBottom && !d->staticListView->segmentPositions.isEmpty()) {
+ const int max = d->staticListView->segmentPositions.count() - 1;
+ int currentValue = qBound(0, horizontalScrollBar()->value(), max);
+ int position = d->staticListView->segmentPositions.at(currentValue);
+ int maximumValue = qBound(0, horizontalScrollBar()->maximum(), max);
+ int maximum = d->staticListView->segmentPositions.at(maximumValue);
+ return (isRightToLeft() ? maximum - position : position);
+ }
+ //return 0;
+ } else {
+ if (d->flow == LeftToRight && !d->staticListView->flowPositions.isEmpty()) {
+ int position = d->staticListView->flowPositions.at(horizontalScrollBar()->value());
+ int maximum = d->staticListView->flowPositions.at(horizontalScrollBar()->maximum());
+ return (isRightToLeft() ? maximum - position : position);
+ }
+ //return 0;
+ }
+ }
+ return (isRightToLeft()
+ ? horizontalScrollBar()->maximum() - horizontalScrollBar()->value()
+ : horizontalScrollBar()->value());
+}
+
+/*!
+ \reimp
+*/
+int QListView::verticalOffset() const
+{
+ // ## split into static and dynamic
+ Q_D(const QListView);
+ if (verticalScrollMode() == QAbstractItemView::ScrollPerItem && d->viewMode == ListMode) {
+ if (d->isWrapping()) {
+ if (d->flow == LeftToRight && !d->staticListView->segmentPositions.isEmpty()) {
+ int value = verticalScrollBar()->value();
+ if (value >= d->staticListView->segmentPositions.count()) {
+ //qWarning("QListView: Vertical scroll bar is out of bounds");
+ return 0;
+ }
+ return d->staticListView->segmentPositions.at(value);
+ }
+ } else {
+ if (d->flow == TopToBottom && !d->staticListView->flowPositions.isEmpty()) {
+ int value = verticalScrollBar()->value();
+ if (value > d->staticListView->flowPositions.count()) {
+ //qWarning("QListView: Vertical scroll bar is out of bounds");
+ return 0;
+ }
+ return d->staticListView->flowPositions.at(value) - d->spacing();
+ }
+ }
+ }
+ return verticalScrollBar()->value();
+}
+
+/*!
+ \reimp
+*/
+QModelIndex QListView::moveCursor(CursorAction cursorAction, Qt::KeyboardModifiers modifiers)
+{
+ Q_D(QListView);
+ Q_UNUSED(modifiers);
+
+ QModelIndex current = currentIndex();
+ if (!current.isValid()) {
+ int rowCount = d->model->rowCount(d->root);
+ if (!rowCount)
+ return QModelIndex();
+ int row = 0;
+ while (row < rowCount && d->isHiddenOrDisabled(row))
+ ++row;
+ if (row >= rowCount)
+ return QModelIndex();
+ return d->model->index(row, 0, d->root);
+ }
+
+ const QRect initialRect = rectForIndex(current);
+ QRect rect = initialRect;
+ if (rect.isEmpty()) {
+ return d->model->index(0, 0, d->root);
+ }
+ if (d->gridSize().isValid()) rect.setSize(d->gridSize());
+
+ QSize contents = d->contentsSize();
+ d->intersectVector.clear();
+
+ switch (cursorAction) {
+ case MoveLeft:
+ while (d->intersectVector.isEmpty()) {
+ rect.translate(-rect.width(), 0);
+ if (rect.right() <= 0)
+ return current;
+ if (rect.left() < 0)
+ rect.setLeft(0);
+ d->intersectingSet(rect);
+ d->removeCurrentAndDisabled(&d->intersectVector, current);
+ }
+ return d->closestIndex(initialRect, d->intersectVector);
+ case MoveRight:
+ while (d->intersectVector.isEmpty()) {
+ rect.translate(rect.width(), 0);
+ if (rect.left() >= contents.width())
+ return current;
+ if (rect.right() > contents.width())
+ rect.setRight(contents.width());
+ d->intersectingSet(rect);
+ d->removeCurrentAndDisabled(&d->intersectVector, current);
+ }
+ return d->closestIndex(initialRect, d->intersectVector);
+ case MovePageUp:
+ rect.moveTop(rect.top() - d->viewport->height());
+ if (rect.top() < rect.height())
+ rect.moveTop(rect.height());
+ case MovePrevious:
+ case MoveUp:
+ while (d->intersectVector.isEmpty()) {
+ rect.translate(0, -rect.height());
+ if (rect.bottom() <= 0) {
+#ifdef QT_KEYPAD_NAVIGATION
+ if (QApplication::keypadNavigationEnabled()) {
+ int row = d->batchStartRow() - 1;
+ while (row >= 0 && d->isHiddenOrDisabled(row))
+ --row;
+ if (row >= 0)
+ return d->model->index(row, d->column, d->root);
+ }
+#endif
+ return current;
+ }
+ if (rect.top() < 0)
+ rect.setTop(0);
+ d->intersectingSet(rect);
+ d->removeCurrentAndDisabled(&d->intersectVector, current);
+ }
+ return d->closestIndex(initialRect, d->intersectVector);
+ case MovePageDown:
+ rect.moveTop(rect.top() + d->viewport->height());
+ if (rect.bottom() > contents.height() - rect.height())
+ rect.moveBottom(contents.height() - rect.height());
+ case MoveNext:
+ case MoveDown:
+ while (d->intersectVector.isEmpty()) {
+ rect.translate(0, rect.height());
+ if (rect.top() >= contents.height()) {
+#ifdef QT_KEYPAD_NAVIGATION
+ if (QApplication::keypadNavigationEnabled()) {
+ int rowCount = d->model->rowCount(d->root);
+ int row = 0;
+ while (row < rowCount && d->isHiddenOrDisabled(row))
+ ++row;
+ if (row < rowCount)
+ return d->model->index(row, d->column, d->root);
+ }
+#endif
+ return current;
+ }
+ if (rect.bottom() > contents.height())
+ rect.setBottom(contents.height());
+ d->intersectingSet(rect);
+ d->removeCurrentAndDisabled(&d->intersectVector, current);
+ }
+ return d->closestIndex(initialRect, d->intersectVector);
+ case MoveHome:
+ return d->model->index(0, d->column, d->root);
+ case MoveEnd:
+ return d->model->index(d->batchStartRow() - 1, d->column, d->root);}
+
+ return current;
+}
+
+/*!
+ Returns the rectangle of the item at position \a index in the
+ model. The rectangle is in contents coordinates.
+
+ \sa visualRect()
+*/
+QRect QListView::rectForIndex(const QModelIndex &index) const
+{
+ Q_D(const QListView);
+ if (!d->isIndexValid(index)
+ || index.parent() != d->root
+ || index.column() != d->column
+ || isIndexHidden(index))
+ return QRect();
+ d->executePostedLayout();
+ QListViewItem item = d->indexToListViewItem(index);
+ return d->viewItemRect(item);
+}
+
+/*!
+ \since 4.1
+
+ Sets the contents position of the item at \a index in the model to the given
+ \a position.
+ If the list view's movement mode is Static, this function will have no
+ effect.
+*/
+void QListView::setPositionForIndex(const QPoint &position, const QModelIndex &index)
+{
+ Q_D(QListView);
+ if (d->movement == Static
+ || !d->isIndexValid(index)
+ || index.parent() != d->root
+ || index.column() != d->column)
+ return;
+
+ d->executePostedLayout();
+ if (index.row() >= d->dynamicListView->items.count())
+ return;
+ const QSize oldContents = d->contentsSize();
+ d->setDirtyRegion(visualRect(index)); // update old position
+ d->dynamicListView->moveItem(index.row(), position);
+ d->setDirtyRegion(visualRect(index)); // update new position
+
+ if (d->contentsSize() != oldContents)
+ updateGeometries(); // update the scroll bars
+}
+
+/*!
+ \reimp
+*/
+void QListView::setSelection(const QRect &rect, QItemSelectionModel::SelectionFlags command)
+{
+ Q_D(QListView);
+ if (!d->selectionModel)
+ return;
+
+ // if we are wrapping, we can only selecte inside the contents rectangle
+ int w = qMax(d->contentsSize().width(), d->viewport->width());
+ int h = qMax(d->contentsSize().height(), d->viewport->height());
+ if (d->wrap && !QRect(0, 0, w, h).intersects(rect))
+ return;
+
+ QItemSelection selection;
+
+ if (rect.width() == 1 && rect.height() == 1) {
+ d->intersectingSet(rect.translated(horizontalOffset(), verticalOffset()));
+ QModelIndex tl;
+ if (!d->intersectVector.isEmpty())
+ tl = d->intersectVector.last(); // special case for mouse press; only select the top item
+ if (tl.isValid() && d->isIndexEnabled(tl))
+ selection.select(tl, tl);
+ } else {
+ if (state() == DragSelectingState) { // visual selection mode (rubberband selection)
+ selection = d->selection(rect.translated(horizontalOffset(), verticalOffset()));
+ } else { // logical selection mode (key and mouse click selection)
+ QModelIndex tl, br;
+ // get the first item
+ const QRect topLeft(rect.left() + horizontalOffset(), rect.top() + verticalOffset(), 1, 1);
+ d->intersectingSet(topLeft);
+ if (!d->intersectVector.isEmpty())
+ tl = d->intersectVector.last();
+ // get the last item
+ const QRect bottomRight(rect.right() + horizontalOffset(), rect.bottom() + verticalOffset(), 1, 1);
+ d->intersectingSet(bottomRight);
+ if (!d->intersectVector.isEmpty())
+ br = d->intersectVector.last();
+
+ // get the ranges
+ if (tl.isValid() && br.isValid()
+ && d->isIndexEnabled(tl)
+ && d->isIndexEnabled(br)) {
+ QRect first = rectForIndex(tl);
+ QRect last = rectForIndex(br);
+ QRect middle;
+ if (d->flow == LeftToRight) {
+ QRect &top = first;
+ QRect &bottom = last;
+ // if bottom is above top, swap them
+ if (top.center().y() > bottom.center().y()) {
+ QRect tmp = top;
+ top = bottom;
+ bottom = tmp;
+ }
+ // if the rect are on differnet lines, expand
+ if (top.top() != bottom.top()) {
+ // top rectangle
+ if (isRightToLeft())
+ top.setLeft(0);
+ else
+ top.setRight(contentsSize().width());
+ // bottom rectangle
+ if (isRightToLeft())
+ bottom.setRight(contentsSize().width());
+ else
+ bottom.setLeft(0);
+ } else if (top.left() > bottom.right()) {
+ if (isRightToLeft())
+ bottom.setLeft(top.right());
+ else
+ bottom.setRight(top.left());
+ } else {
+ if (isRightToLeft())
+ top.setLeft(bottom.right());
+ else
+ top.setRight(bottom.left());
+ }
+ // middle rectangle
+ if (top.bottom() < bottom.top()) {
+ middle.setTop(top.bottom() + 1);
+ middle.setLeft(qMin(top.left(), bottom.left()));
+ middle.setBottom(bottom.top() - 1);
+ middle.setRight(qMax(top.right(), bottom.right()));
+ }
+ } else { // TopToBottom
+ QRect &left = first;
+ QRect &right = last;
+ if (left.center().x() > right.center().x())
+ qSwap(left, right);
+
+ int ch = contentsSize().height();
+ if (left.left() != right.left()) {
+ // left rectangle
+ if (isRightToLeft())
+ left.setTop(0);
+ else
+ left.setBottom(ch);
+
+ // top rectangle
+ if (isRightToLeft())
+ right.setBottom(ch);
+ else
+ right.setTop(0);
+ // only set middle if the
+ middle.setTop(0);
+ middle.setBottom(ch);
+ middle.setLeft(left.right() + 1);
+ middle.setRight(right.left() - 1);
+ } else if (left.bottom() < right.top()) {
+ left.setBottom(right.top() - 1);
+ } else {
+ right.setBottom(left.top() - 1);
+ }
+ }
+
+ // do the selections
+ QItemSelection topSelection = d->selection(first);
+ QItemSelection middleSelection = d->selection(middle);
+ QItemSelection bottomSelection = d->selection(last);
+ // merge
+ selection.merge(topSelection, QItemSelectionModel::Select);
+ selection.merge(middleSelection, QItemSelectionModel::Select);
+ selection.merge(bottomSelection, QItemSelectionModel::Select);
+ }
+ }
+ }
+
+ d->selectionModel->select(selection, command);
+}
+
+/*!
+ \reimp
+*/
+QRegion QListView::visualRegionForSelection(const QItemSelection &selection) const
+{
+ Q_D(const QListView);
+ // ### NOTE: this is a potential bottleneck in non-static mode
+ int c = d->column;
+ QRegion selectionRegion;
+ for (int i = 0; i < selection.count(); ++i) {
+ if (!selection.at(i).isValid())
+ continue;
+ QModelIndex parent = selection.at(i).topLeft().parent();
+ int t = selection.at(i).topLeft().row();
+ int b = selection.at(i).bottomRight().row();
+ if (d->viewMode == IconMode || d->isWrapping()) { // in non-static mode, we have to go through all selected items
+ for (int r = t; r <= b; ++r)
+ selectionRegion += QRegion(visualRect(d->model->index(r, c, parent)));
+ } else { // in static mode, we can optimize a bit
+ while (t <= b && d->isHidden(t)) ++t;
+ while (b >= t && d->isHidden(b)) --b;
+ const QModelIndex top = d->model->index(t, c, d->root);
+ const QModelIndex bottom = d->model->index(b, c, d->root);
+ QRect rect(visualRect(top).topLeft(),
+ visualRect(bottom).bottomRight());
+ selectionRegion += QRegion(rect);
+ }
+ }
+
+ return selectionRegion;
+}
+
+/*!
+ \reimp
+*/
+QModelIndexList QListView::selectedIndexes() const
+{
+ Q_D(const QListView);
+ QModelIndexList viewSelected;
+ QModelIndexList modelSelected;
+ if (d->selectionModel)
+ modelSelected = d->selectionModel->selectedIndexes();
+ for (int i = 0; i < modelSelected.count(); ++i) {
+ QModelIndex index = modelSelected.at(i);
+ if (!isIndexHidden(index) && index.parent() == d->root && index.column() == d->column)
+ viewSelected.append(index);
+ }
+ return viewSelected;
+}
+
+/*!
+ \internal
+
+ Layout the items according to the flow and wrapping properties.
+*/
+void QListView::doItemsLayout()
+{
+ Q_D(QListView);
+ // showing the scroll bars will trigger a resize event,
+ // so we set the state to expanding to avoid
+ // triggering another layout
+ QAbstractItemView::State oldState = state();
+ setState(ExpandingState);
+ if (d->model->columnCount(d->root) > 0) { // no columns means no contents
+ d->resetBatchStartRow();
+ if (layoutMode() == SinglePass)
+ d->doItemsLayout(d->model->rowCount(d->root)); // layout everything
+ else if (!d->batchLayoutTimer.isActive()) {
+ if (!d->doItemsLayout(d->batchSize)) // layout is done
+ d->batchLayoutTimer.start(0, this); // do a new batch as fast as possible
+ }
+ }
+ QAbstractItemView::doItemsLayout();
+ setState(oldState); // restoring the oldState
+}
+
+/*!
+ \reimp
+*/
+void QListView::updateGeometries()
+{
+ Q_D(QListView);
+ if (d->model->rowCount(d->root) <= 0 || d->model->columnCount(d->root) <= 0) {
+ horizontalScrollBar()->setRange(0, 0);
+ verticalScrollBar()->setRange(0, 0);
+ } else {
+ QModelIndex index = d->model->index(0, d->column, d->root);
+ QStyleOptionViewItemV4 option = d->viewOptionsV4();
+ QSize step = d->itemSize(option, index);
+
+ QSize csize = d->contentsSize();
+ QSize vsize = d->viewport->size();
+ QSize max = maximumViewportSize();
+ if (max.width() >= d->contentsSize().width() && max.height() >= d->contentsSize().height())
+ vsize = max;
+
+ // ### reorder the logic
+
+ // ### split into static and dynamic
+
+ const bool vertical = verticalScrollMode() == QAbstractItemView::ScrollPerItem;
+ const bool horizontal = horizontalScrollMode() == QAbstractItemView::ScrollPerItem;
+
+ if (d->flow == TopToBottom) {
+ if (horizontal && d->isWrapping() && d->viewMode == ListMode) {
+ const QVector<int> segmentPositions = d->staticListView->segmentPositions;
+ const int steps = segmentPositions.count() - 1;
+ if (steps > 0) {
+ int pageSteps = d->staticListView->perItemScrollingPageSteps(vsize.width(),
+ csize.width(),
+ isWrapping());
+ horizontalScrollBar()->setSingleStep(1);
+ horizontalScrollBar()->setPageStep(pageSteps);
+ horizontalScrollBar()->setRange(0, steps - pageSteps);
+ } else {
+ horizontalScrollBar()->setRange(0, 0);
+ }
+ } else {
+ horizontalScrollBar()->setSingleStep(step.width() + d->spacing());
+ horizontalScrollBar()->setPageStep(vsize.width());
+ horizontalScrollBar()->setRange(0, d->contentsSize().width() - vsize.width());
+ }
+ if (vertical && !d->isWrapping() && d->viewMode == ListMode) {
+ const QVector<int> flowPositions = d->staticListView->flowPositions;
+ const int steps = flowPositions.count() - 1;
+ if (steps > 0) {
+ int pageSteps = d->staticListView->perItemScrollingPageSteps(vsize.height(),
+ csize.height(),
+ isWrapping());
+ verticalScrollBar()->setSingleStep(1);
+ verticalScrollBar()->setPageStep(pageSteps);
+ verticalScrollBar()->setRange(0, steps - pageSteps);
+ } else {
+ verticalScrollBar()->setRange(0, 0);
+ }
+ // } else if (vertical && d->isWrapping() && d->movement == Static) {
+ // ### wrapped scrolling in flow direction
+ } else {
+ verticalScrollBar()->setSingleStep(step.height() + d->spacing());
+ verticalScrollBar()->setPageStep(vsize.height());
+ verticalScrollBar()->setRange(0, d->contentsSize().height() - vsize.height());
+ }
+ } else { // LeftToRight
+ if (horizontal && !d->isWrapping() && d->viewMode == ListMode) {
+ const QVector<int> flowPositions = d->staticListView->flowPositions;
+ int steps = flowPositions.count() - 1;
+ if (steps > 0) {
+ int pageSteps = d->staticListView->perItemScrollingPageSteps(vsize.width(),
+ csize.width(),
+ isWrapping());
+ horizontalScrollBar()->setSingleStep(1);
+ horizontalScrollBar()->setPageStep(pageSteps);
+ horizontalScrollBar()->setRange(0, steps - pageSteps);
+ } else {
+ horizontalScrollBar()->setRange(0, 0);
+ }
+ // } else if (horizontal && d->isWrapping() && d->movement == Static) {
+ // ### wrapped scrolling in flow direction
+ } else {
+ horizontalScrollBar()->setSingleStep(step.width() + d->spacing());
+ horizontalScrollBar()->setPageStep(vsize.width());
+ horizontalScrollBar()->setRange(0, d->contentsSize().width() - vsize.width());
+ }
+ if (vertical && d->isWrapping() && d->viewMode == ListMode) {
+ const QVector<int> segmentPositions = d->staticListView->segmentPositions;
+ int steps = segmentPositions.count() - 1;
+ if (steps > 0) {
+ int pageSteps = d->staticListView->perItemScrollingPageSteps(vsize.height(),
+ csize.height(),
+ isWrapping());
+ verticalScrollBar()->setSingleStep(1);
+ verticalScrollBar()->setPageStep(pageSteps);
+ verticalScrollBar()->setRange(0, steps - pageSteps);
+ } else {
+ verticalScrollBar()->setRange(0, 0);
+ }
+ } else {
+ verticalScrollBar()->setSingleStep(step.height() + d->spacing());
+ verticalScrollBar()->setPageStep(vsize.height());
+ verticalScrollBar()->setRange(0, d->contentsSize().height() - vsize.height());
+ }
+ }
+ }
+
+ QAbstractItemView::updateGeometries();
+
+ // if the scroll bars are turned off, we resize the contents to the viewport
+ if (d->movement == Static && !d->isWrapping()) {
+ d->layoutChildren(); // we need the viewport size to be updated
+ if (d->flow == TopToBottom) {
+ if (horizontalScrollBarPolicy() == Qt::ScrollBarAlwaysOff) {
+ d->setContentsSize(viewport()->width(), contentsSize().height());
+ horizontalScrollBar()->setRange(0, 0); // we see all the contents anyway
+ }
+ } else { // LeftToRight
+ if (verticalScrollBarPolicy() == Qt::ScrollBarAlwaysOff) {
+ d->setContentsSize(contentsSize().width(), viewport()->height());
+ verticalScrollBar()->setRange(0, 0); // we see all the contents anyway
+ }
+ }
+ }
+}
+
+/*!
+ \reimp
+*/
+bool QListView::isIndexHidden(const QModelIndex &index) const
+{
+ Q_D(const QListView);
+ return (d->isHidden(index.row())
+ && (index.parent() == d->root)
+ && index.column() == d->column);
+}
+
+/*!
+ \property QListView::modelColumn
+ \brief the column in the model that is visible
+
+ By default, this property contains 0, indicating that the first
+ column in the model will be shown.
+*/
+void QListView::setModelColumn(int column)
+{
+ Q_D(QListView);
+ if (column < 0 || column >= d->model->columnCount(d->root))
+ return;
+ d->column = column;
+ d->doDelayedItemsLayout();
+}
+
+int QListView::modelColumn() const
+{
+ Q_D(const QListView);
+ return d->column;
+}
+
+/*!
+ \property QListView::uniformItemSizes
+ \brief whether all items in the listview have the same size
+ \since 4.1
+
+ This property should only be set to true if it is guaranteed that all items
+ in the view have the same size. This enables the view to do some
+ optimizations for performance purposes.
+
+ By default, this property is false.
+*/
+void QListView::setUniformItemSizes(bool enable)
+{
+ Q_D(QListView);
+ d->uniformItemSizes = enable;
+}
+
+bool QListView::uniformItemSizes() const
+{
+ Q_D(const QListView);
+ return d->uniformItemSizes;
+}
+
+/*!
+ \property QListView::wordWrap
+ \brief the item text word-wrapping policy
+ \since 4.2
+
+ If this property is true then the item text is wrapped where
+ necessary at word-breaks; otherwise it is not wrapped at all.
+ This property is false by default.
+
+ Please note that even if wrapping is enabled, the cell will not be
+ expanded to make room for the text. It will print ellipsis for
+ text that cannot be shown, according to the view's
+ \l{QAbstractItemView::}{textElideMode}.
+*/
+void QListView::setWordWrap(bool on)
+{
+ Q_D(QListView);
+ if (d->wrapItemText == on)
+ return;
+ d->wrapItemText = on;
+ d->doDelayedItemsLayout();
+}
+
+bool QListView::wordWrap() const
+{
+ Q_D(const QListView);
+ return d->wrapItemText;
+}
+
+/*!
+ \property QListView::selectionRectVisible
+ \brief if the selection rectangle should be visible
+ \since 4.3
+
+ If this property is true then the selection rectangle is visible;
+ otherwise it will be hidden.
+
+ \note The selection rectangle will only be visible if the selection mode
+ is in a mode where more than one item can be selected; i.e., it will not
+ draw a selection rectangle if the selection mode is
+ QAbstractItemView::SingleSelection.
+
+ By default, this property is false.
+*/
+void QListView::setSelectionRectVisible(bool show)
+{
+ Q_D(QListView);
+ d->modeProperties |= uint(QListViewPrivate::SelectionRectVisible);
+ d->setSelectionRectVisible(show);
+}
+
+bool QListView::isSelectionRectVisible() const
+{
+ Q_D(const QListView);
+ return d->isSelectionRectVisible();
+}
+
+/*!
+ \reimp
+*/
+bool QListView::event(QEvent *e)
+{
+ return QAbstractItemView::event(e);
+}
+
+/*
+ * private object implementation
+ */
+
+QListViewPrivate::QListViewPrivate()
+ : QAbstractItemViewPrivate(),
+ dynamicListView(0),
+ staticListView(0),
+ wrap(false),
+ space(0),
+ flow(QListView::TopToBottom),
+ movement(QListView::Static),
+ resizeMode(QListView::Fixed),
+ layoutMode(QListView::SinglePass),
+ viewMode(QListView::ListMode),
+ modeProperties(0),
+ column(0),
+ uniformItemSizes(false),
+ batchSize(100)
+{
+}
+
+QListViewPrivate::~QListViewPrivate()
+{
+ delete staticListView;
+ delete dynamicListView;
+}
+
+void QListViewPrivate::clear()
+{
+ // ### split into dynamic and static
+ // initialization of data structs
+ cachedItemSize = QSize();
+ if (viewMode == QListView::ListMode)
+ staticListView->clear();
+ else
+ dynamicListView->clear();
+}
+
+void QListViewPrivate::prepareItemsLayout()
+{
+ Q_Q(QListView);
+ clear();
+
+ //take the size as if there were scrollbar in order to prevent scrollbar to blink
+ layoutBounds = QRect(QPoint(0,0), q->maximumViewportSize());
+
+ int frameAroundContents = 0;
+ if (q->style()->styleHint(QStyle::SH_ScrollView_FrameOnlyAroundContents))
+ frameAroundContents = q->style()->pixelMetric(QStyle::PM_DefaultFrameWidth) * 2;
+ int verticalMargin = vbarpolicy==Qt::ScrollBarAlwaysOff ? 0 :
+ q->style()->pixelMetric(QStyle::PM_ScrollBarExtent, 0, q->verticalScrollBar()) + frameAroundContents;
+ int horizontalMargin = hbarpolicy==Qt::ScrollBarAlwaysOff ? 0 :
+ q->style()->pixelMetric(QStyle::PM_ScrollBarExtent, 0, q->horizontalScrollBar()) + frameAroundContents;
+
+ layoutBounds.adjust(0, 0, -verticalMargin, -horizontalMargin);
+
+ int rowCount = model->rowCount(root);
+ int colCount = model->columnCount(root);
+ if (colCount <= 0)
+ rowCount = 0; // no contents
+ if (viewMode == QListView::ListMode) {
+ staticListView->flowPositions.resize(rowCount);
+ } else {
+ dynamicListView->tree.create(qMax(rowCount - hiddenRows.count(), 0));
+ }
+}
+
+/*!
+ \internal
+*/
+bool QListViewPrivate::doItemsLayout(int delta)
+{
+ // ### split into static and dynamic
+ int max = model->rowCount(root) - 1;
+ int first = batchStartRow();
+ int last = qMin(first + delta - 1, max);
+
+ if (max < 0 || last < first)
+ return true; // nothing to do
+
+ if (first == 0) {
+ layoutChildren(); // make sure the viewport has the right size
+ prepareItemsLayout();
+ }
+
+ QListViewLayoutInfo info;
+ info.bounds = layoutBounds;
+ info.grid = gridSize();
+ info.spacing = (info.grid.isValid() ? 0 : spacing());
+ info.first = first;
+ info.last = last;
+ info.wrap = isWrapping();
+ info.flow = flow;
+ info.max = max;
+
+ if (viewMode == QListView::ListMode)
+ return staticListView->doBatchedItemLayout(info, max);
+ return dynamicListView->doBatchedItemLayout(info, max);
+}
+
+QListViewItem QListViewPrivate::indexToListViewItem(const QModelIndex &index) const
+{
+ if (!index.isValid() || isHidden(index.row()))
+ return QListViewItem();
+
+ if (viewMode == QListView::ListMode)
+ return staticListView->indexToListViewItem(index);
+ return dynamicListView->indexToListViewItem(index);
+}
+
+
+int QListViewPrivate::itemIndex(const QListViewItem &item) const
+{
+ if (viewMode == QListView::ListMode)
+ return staticListView->itemIndex(item);
+ return dynamicListView->itemIndex(item);
+}
+
+QRect QListViewPrivate::mapToViewport(const QRect &rect, bool greedy) const
+{
+ Q_Q(const QListView);
+ if (!rect.isValid())
+ return rect;
+
+ QRect result = rect;
+ if (greedy)
+ result = staticListView->mapToViewport(rect);
+
+ int dx = -q->horizontalOffset();
+ int dy = -q->verticalOffset();
+ result.adjust(dx, dy, dx, dy);
+ return result;
+}
+
+QModelIndex QListViewPrivate::closestIndex(const QRect &target,
+ const QVector<QModelIndex> &candidates) const
+{
+ int distance = 0;
+ int shortest = INT_MAX;
+ QModelIndex closest;
+ QVector<QModelIndex>::const_iterator it = candidates.begin();
+
+ for (; it != candidates.end(); ++it) {
+ if (!(*it).isValid())
+ continue;
+
+ const QRect indexRect = indexToListViewItem(*it).rect();
+
+ //if the center x (or y) position of an item is included in the rect of the other item,
+ //we define the distance between them as the difference in x (or y) of their respective center.
+ // Otherwise, we use the nahattan length between the 2 items
+ if ((target.center().x() >= indexRect.x() && target.center().x() < indexRect.right())
+ || (indexRect.center().x() >= target.x() && indexRect.center().x() < target.right())) {
+ //one item's center is at the vertical of the other
+ distance = qAbs(indexRect.center().y() - target.center().y());
+ } else if ((target.center().y() >= indexRect.y() && target.center().y() < indexRect.bottom())
+ || (indexRect.center().y() >= target.y() && indexRect.center().y() < target.bottom())) {
+ //one item's center is at the vertical of the other
+ distance = qAbs(indexRect.center().x() - target.center().x());
+ } else {
+ distance = (indexRect.center() - target.center()).manhattanLength();
+ }
+ if (distance < shortest) {
+ shortest = distance;
+ closest = *it;
+ }
+ }
+ return closest;
+}
+
+QSize QListViewPrivate::itemSize(const QStyleOptionViewItem &option, const QModelIndex &index) const
+{
+ if (!uniformItemSizes) {
+ const QAbstractItemDelegate *delegate = delegateForIndex(index);
+ return delegate ? delegate->sizeHint(option, index) : QSize();
+ }
+ if (!cachedItemSize.isValid()) { // the last item is probaly the largest, so we use its size
+ int row = model->rowCount(root) - 1;
+ QModelIndex sample = model->index(row, column, root);
+ const QAbstractItemDelegate *delegate = delegateForIndex(sample);
+ cachedItemSize = delegate ? delegate->sizeHint(option, sample) : QSize();
+ }
+ return cachedItemSize;
+}
+
+QItemSelection QListViewPrivate::selection(const QRect &rect) const
+{
+ QItemSelection selection;
+ QModelIndex tl, br;
+ intersectingSet(rect);
+ QVector<QModelIndex>::iterator it = intersectVector.begin();
+ for (; it != intersectVector.end(); ++it) {
+ if (!tl.isValid() && !br.isValid()) {
+ tl = br = *it;
+ } else if ((*it).row() == (tl.row() - 1)) {
+ tl = *it; // expand current range
+ } else if ((*it).row() == (br.row() + 1)) {
+ br = (*it); // expand current range
+ } else {
+ selection.select(tl, br); // select current range
+ tl = br = *it; // start new range
+ }
+ }
+
+ if (tl.isValid() && br.isValid())
+ selection.select(tl, br);
+ else if (tl.isValid())
+ selection.select(tl, tl);
+ else if (br.isValid())
+ selection.select(br, br);
+
+ return selection;
+}
+
+/*
+ * Static ListView Implementation
+*/
+
+int QStaticListViewBase::verticalPerItemValue(int itemIndex, int verticalValue, int areaHeight,
+ bool above, bool below, bool wrap,
+ QListView::ScrollHint hint, int itemHeight) const
+{
+ int value = qBound(0, verticalValue, flowPositions.count() - 1);
+ if (above)
+ return perItemScrollToValue(itemIndex, value, areaHeight, QListView::PositionAtTop,
+ Qt::Vertical,wrap, itemHeight);
+ else if (below)
+ return perItemScrollToValue(itemIndex, value, areaHeight, QListView::PositionAtBottom,
+ Qt::Vertical, wrap, itemHeight);
+ else if (hint != QListView::EnsureVisible)
+ return perItemScrollToValue(itemIndex, value, areaHeight, hint, Qt::Vertical, wrap, itemHeight);
+ return value;
+}
+
+int QStaticListViewBase::horizontalPerItemValue(int itemIndex, int horizontalValue, int areaWidth,
+ bool leftOf, bool rightOf, bool wrap,
+ QListView::ScrollHint hint, int itemWidth) const
+{
+ int value = qBound(0, horizontalValue, flowPositions.count() - 1);
+ if (leftOf)
+ return perItemScrollToValue(itemIndex, value, areaWidth, QListView::PositionAtTop,
+ Qt::Horizontal, wrap, itemWidth);
+ else if (rightOf)
+ return perItemScrollToValue(itemIndex, value, areaWidth, QListView::PositionAtBottom,
+ Qt::Horizontal, wrap, itemWidth);
+ else if (hint != QListView::EnsureVisible)
+ return perItemScrollToValue(itemIndex, value, areaWidth, hint, Qt::Horizontal, wrap, itemWidth);
+ return value;
+}
+
+void QStaticListViewBase::scrollContentsBy(int &dx, int &dy)
+{
+ // ### reorder this logic
+ const int verticalValue = verticalScrollBarValue();
+ const int horizontalValue = horizontalScrollBarValue();
+ const bool vertical = (verticalScrollMode() == QAbstractItemView::ScrollPerItem);
+ const bool horizontal = (horizontalScrollMode() == QAbstractItemView::ScrollPerItem);
+
+ if (isWrapping()) {
+ if (segmentPositions.isEmpty())
+ return;
+ const int max = segmentPositions.count() - 1;
+ if (horizontal && flow() == QListView::TopToBottom && dx != 0) {
+ int currentValue = qBound(0, horizontalValue, max);
+ int previousValue = qBound(0, currentValue + dx, max);
+ int currentCoordinate = segmentPositions.at(currentValue);
+ int previousCoordinate = segmentPositions.at(previousValue);
+ dx = previousCoordinate - currentCoordinate;
+ } else if (vertical && flow() == QListView::LeftToRight && dy != 0) {
+ int currentValue = qBound(0, verticalValue, max);
+ int previousValue = qBound(0, currentValue + dy, max);
+ int currentCoordinate = segmentPositions.at(currentValue);
+ int previousCoordinate = segmentPositions.at(previousValue);
+ dy = previousCoordinate - currentCoordinate;
+ }
+ } else {
+ if (flowPositions.isEmpty())
+ return;
+ const int max = flowPositions.count() - 1;
+ if (vertical && flow() == QListView::TopToBottom && dy != 0) {
+ int currentValue = qBound(0, verticalValue, max);
+ int previousValue = qBound(0, currentValue + dy, max);
+ int currentCoordinate = flowPositions.at(currentValue);
+ int previousCoordinate = flowPositions.at(previousValue);
+ dy = previousCoordinate - currentCoordinate;
+ } else if (horizontal && flow() == QListView::LeftToRight && dx != 0) {
+ int currentValue = qBound(0, horizontalValue, max);
+ int previousValue = qBound(0, currentValue + dx, max);
+ int currentCoordinate = flowPositions.at(currentValue);
+ int previousCoordinate = flowPositions.at(previousValue);
+ dx = previousCoordinate - currentCoordinate;
+ }
+ }
+}
+
+bool QStaticListViewBase::doBatchedItemLayout(const QListViewLayoutInfo &info, int max)
+{
+ doStaticLayout(info);
+ if (batchStartRow > max) { // stop items layout
+ flowPositions.resize(flowPositions.count());
+ segmentPositions.resize(segmentPositions.count());
+ segmentStartRows.resize(segmentStartRows.count());
+ return true; // done
+ }
+ return false; // not done
+}
+
+QListViewItem QStaticListViewBase::indexToListViewItem(const QModelIndex &index) const
+{
+ if (flowPositions.isEmpty()
+ || segmentPositions.isEmpty()
+ || index.row() > flowPositions.count())
+ return QListViewItem();
+
+ const int segment = qBinarySearch<int>(segmentStartRows, index.row(),
+ 0, segmentStartRows.count() - 1);
+
+
+ QStyleOptionViewItemV4 options = viewOptions();
+ options.rect.setSize(contentsSize);
+ QSize size = (uniformItemSizes() && cachedItemSize().isValid())
+ ? cachedItemSize() : itemSize(options, index);
+
+ QPoint pos;
+ if (flow() == QListView::LeftToRight) {
+ pos.setX(flowPositions.at(index.row()));
+ pos.setY(segmentPositions.at(segment));
+ } else { // TopToBottom
+ pos.setY(flowPositions.at(index.row()));
+ pos.setX(segmentPositions.at(segment));
+ if (isWrapping()) { // make the items as wide as the segment
+ int right = (segment + 1 >= segmentPositions.count()
+ ? contentsSize.width()
+ : segmentPositions.at(segment + 1));
+ size.setWidth(right - pos.x());
+ } else { // make the items as wide as the viewport
+ size.setWidth(qMax(size.width(), viewport()->width()));
+ }
+ }
+
+ return QListViewItem(QRect(pos, size), index.row());
+}
+
+QPoint QStaticListViewBase::initStaticLayout(const QListViewLayoutInfo &info)
+{
+ int x, y;
+ if (info.first == 0) {
+ flowPositions.clear();
+ segmentPositions.clear();
+ segmentStartRows.clear();
+ segmentExtents.clear();
+ x = info.bounds.left() + info.spacing;
+ y = info.bounds.top() + info.spacing;
+ segmentPositions.append(info.flow == QListView::LeftToRight ? y : x);
+ segmentStartRows.append(0);
+ } else if (info.wrap) {
+ if (info.flow == QListView::LeftToRight) {
+ x = batchSavedPosition;
+ y = segmentPositions.last();
+ } else { // flow == QListView::TopToBottom
+ x = segmentPositions.last();
+ y = batchSavedPosition;
+ }
+ } else { // not first and not wrap
+ if (info.flow == QListView::LeftToRight) {
+ x = batchSavedPosition;
+ y = info.bounds.top() + info.spacing;
+ } else { // flow == QListView::TopToBottom
+ x = info.bounds.left() + info.spacing;
+ y = batchSavedPosition;
+ }
+ }
+ return QPoint(x, y);
+}
+
+/*!
+ \internal
+*/
+void QStaticListViewBase::doStaticLayout(const QListViewLayoutInfo &info)
+{
+ const bool useItemSize = !info.grid.isValid();
+ const QPoint topLeft = initStaticLayout(info);
+ QStyleOptionViewItemV4 option = viewOptions();
+ option.rect = info.bounds;
+
+ // The static layout data structures are as follows:
+ // One vector contains the coordinate in the direction of layout flow.
+ // Another vector contains the coordinates of the segments.
+ // A third vector contains the index (model row) of the first item
+ // of each segment.
+
+ int segStartPosition;
+ int segEndPosition;
+ int deltaFlowPosition;
+ int deltaSegPosition;
+ int deltaSegHint;
+ int flowPosition;
+ int segPosition;
+
+ if (info.flow == QListView::LeftToRight) {
+ segStartPosition = info.bounds.left();
+ segEndPosition = info.bounds.width();
+ flowPosition = topLeft.x();
+ segPosition = topLeft.y();
+ deltaFlowPosition = info.grid.width(); // dx
+ deltaSegPosition = useItemSize ? batchSavedDeltaSeg : info.grid.height(); // dy
+ deltaSegHint = info.grid.height();
+ } else { // flow == QListView::TopToBottom
+ segStartPosition = info.bounds.top();
+ segEndPosition = info.bounds.height();
+ flowPosition = topLeft.y();
+ segPosition = topLeft.x();
+ deltaFlowPosition = info.grid.height(); // dy
+ deltaSegPosition = useItemSize ? batchSavedDeltaSeg : info.grid.width(); // dx
+ deltaSegHint = info.grid.width();
+ }
+
+ for (int row = info.first; row <= info.last; ++row) {
+ if (isHidden(row)) { // ###
+ flowPositions.append(flowPosition);
+ } else {
+ // if we are not using a grid, we need to find the deltas
+ if (useItemSize) {
+ QSize hint = itemSize(option, modelIndex(row));
+ if (info.flow == QListView::LeftToRight) {
+ deltaFlowPosition = hint.width() + info.spacing;
+ deltaSegHint = hint.height() + info.spacing;
+ } else { // TopToBottom
+ deltaFlowPosition = hint.height() + info.spacing;
+ deltaSegHint = hint.width() + info.spacing;
+ }
+ }
+ // create new segment
+ if (info.wrap && (flowPosition + deltaFlowPosition >= segEndPosition)) {
+ segmentExtents.append(flowPosition);
+ flowPosition = info.spacing + segStartPosition;
+ segPosition += deltaSegPosition;
+ segmentPositions.append(segPosition);
+ segmentStartRows.append(row);
+ deltaSegPosition = 0;
+ }
+ // save the flow position of this item
+ flowPositions.append(flowPosition);
+ // prepare for the next item
+ deltaSegPosition = qMax(deltaSegHint, deltaSegPosition);
+ flowPosition += info.spacing + deltaFlowPosition;
+ }
+ }
+ // used when laying out next batch
+ batchSavedPosition = flowPosition;
+ batchSavedDeltaSeg = deltaSegPosition;
+ batchStartRow = info.last + 1;
+ if (info.last == info.max)
+ flowPosition -= info.spacing; // remove extra spacing
+ // set the contents size
+ QRect rect = info.bounds;
+ if (info.flow == QListView::LeftToRight) {
+ rect.setRight(segmentPositions.count() == 1 ? flowPosition : info.bounds.right());
+ rect.setBottom(segPosition + deltaSegPosition);
+ } else { // TopToBottom
+ rect.setRight(segPosition + deltaSegPosition);
+ rect.setBottom(segmentPositions.count() == 1 ? flowPosition : info.bounds.bottom());
+ }
+ contentsSize = QSize(rect.right(), rect.bottom());
+ // if it is the last batch, save the end of the segments
+ if (info.last == info.max) {
+ segmentExtents.append(flowPosition);
+ flowPositions.append(flowPosition);
+ segmentPositions.append(info.wrap ? segPosition + deltaSegPosition : INT_MAX);
+ }
+ // if the new items are visble, update the viewport
+ QRect changedRect(topLeft, rect.bottomRight());
+ if (clipRect().intersects(changedRect))
+ viewport()->update();
+}
+
+/*!
+ \internal
+ Finds the set of items intersecting with \a area.
+ In this function, itemsize is counted from topleft to the start of the next item.
+*/
+void QStaticListViewBase::intersectingStaticSet(const QRect &area) const
+{
+ clearIntersections();
+ int segStartPosition;
+ int segEndPosition;
+ int flowStartPosition;
+ int flowEndPosition;
+ if (flow() == QListView::LeftToRight) {
+ segStartPosition = area.top();
+ segEndPosition = area.bottom();
+ flowStartPosition = area.left();
+ flowEndPosition = area.right();
+ } else {
+ segStartPosition = area.left();
+ segEndPosition = area.right();
+ flowStartPosition = area.top();
+ flowEndPosition = area.bottom();
+ }
+ if (segmentPositions.count() < 2 || flowPositions.isEmpty())
+ return;
+ // the last segment position is actually the edge of the last segment
+ const int segLast = segmentPositions.count() - 2;
+ int seg = qBinarySearch<int>(segmentPositions, segStartPosition, 0, segLast + 1);
+ for (; seg <= segLast && segmentPositions.at(seg) <= segEndPosition; ++seg) {
+ int first = segmentStartRows.at(seg);
+ int last = (seg < segLast ? segmentStartRows.at(seg + 1) : batchStartRow) - 1;
+ if (segmentExtents.at(seg) < flowStartPosition)
+ continue;
+ int row = qBinarySearch<int>(flowPositions, flowStartPosition, first, last);
+ for (; row <= last && flowPositions.at(row) <= flowEndPosition; ++row) {
+ if (isHidden(row))
+ continue;
+ QModelIndex index = modelIndex(row);
+ if (index.isValid())
+ appendToIntersections(index);
+#if 0 // for debugging
+ else
+ qWarning("intersectingStaticSet: row %d was invalid", row);
+#endif
+ }
+ }
+}
+
+int QStaticListViewBase::itemIndex(const QListViewItem &item) const
+{
+ return item.indexHint;
+}
+
+QRect QStaticListViewBase::mapToViewport(const QRect &rect) const
+{
+ if (isWrapping())
+ return rect;
+ // If the listview is in "listbox-mode", the items are as wide as the view.
+ QRect result = rect;
+ QSize vsize = viewport()->size();
+ QSize csize = contentsSize;
+ if (flow() == QListView::TopToBottom) {
+ result.setLeft(spacing());
+ result.setWidth(qMax(csize.width(), vsize.width()) - 2 * spacing());
+ } else { // LeftToRight
+ result.setTop(spacing());
+ result.setHeight(qMax(csize.height(), vsize.height()) - 2 * spacing());
+ }
+ return result;
+}
+
+int QStaticListViewBase::perItemScrollingPageSteps(int length, int bounds, bool wrap) const
+{
+ const QVector<int> positions = (wrap ? segmentPositions : flowPositions);
+ if (positions.isEmpty() || bounds <= length)
+ return positions.count();
+ if (uniformItemSizes()) {
+ for (int i = 1; i < positions.count(); ++i)
+ if (positions.at(i) > 0)
+ return length / positions.at(i);
+ return 0; // all items had height 0
+ }
+ int pageSteps = 0;
+ int steps = positions.count() - 1;
+ int max = qMax(length, bounds);
+ int min = qMin(length, bounds);
+ int pos = min - (max - positions.last());
+
+ while (pos >= 0 && steps > 0) {
+ pos -= (positions.at(steps) - positions.at(steps - 1));
+ if (pos >= 0) //this item should be visible
+ ++pageSteps;
+ --steps;
+ }
+
+ // at this point we know that positions has at least one entry
+ return qMax(pageSteps, 1);
+}
+
+int QStaticListViewBase::perItemScrollToValue(int index, int scrollValue, int viewportSize,
+ QAbstractItemView::ScrollHint hint,
+ Qt::Orientation orientation, bool wrap, int itemExtent) const
+{
+ if (index < 0)
+ return scrollValue;
+ if (!wrap) {
+ int topIndex = index;
+ const int bottomIndex = topIndex;
+ const int bottomCoordinate = flowPositions.at(index);
+
+ while (topIndex > 0 &&
+ (bottomCoordinate - flowPositions.at(topIndex-1) + itemExtent) <= (viewportSize)) {
+ topIndex--;
+ }
+
+ const int itemCount = bottomIndex - topIndex + 1;
+ switch (hint) {
+ case QAbstractItemView::PositionAtTop:
+ return index;
+ case QAbstractItemView::PositionAtBottom:
+ return index - itemCount + 1;
+ case QAbstractItemView::PositionAtCenter:
+ return index - (itemCount / 2);
+ default:
+ break;
+ }
+ } else { // wrapping
+ Qt::Orientation flowOrientation = (flow() == QListView::LeftToRight
+ ? Qt::Horizontal : Qt::Vertical);
+ if (flowOrientation == orientation) { // scrolling in the "flow" direction
+ // ### wrapped scrolling in the flow direction
+ return flowPositions.at(index); // ### always pixel based for now
+ } else if (!segmentStartRows.isEmpty()) { // we are scrolling in the "segment" direction
+ int segment = qBinarySearch<int>(segmentStartRows, index, 0, segmentStartRows.count() - 1);
+ int leftSegment = segment;
+ const int rightSegment = leftSegment;
+ const int bottomCoordinate = segmentPositions.at(segment);
+
+ while (leftSegment > scrollValue &&
+ (bottomCoordinate - segmentPositions.at(leftSegment-1) + itemExtent) <= (viewportSize)) {
+ leftSegment--;
+ }
+
+ const int segmentCount = rightSegment - leftSegment + 1;
+ switch (hint) {
+ case QAbstractItemView::PositionAtTop:
+ return segment;
+ case QAbstractItemView::PositionAtBottom:
+ return segment - segmentCount + 1;
+ case QAbstractItemView::PositionAtCenter:
+ return segment - (segmentCount / 2);
+ default:
+ break;
+ }
+ }
+ }
+ return scrollValue;
+}
+
+void QStaticListViewBase::clear()
+{
+ flowPositions.clear();
+ segmentPositions.clear();
+ segmentStartRows.clear();
+ segmentExtents.clear();
+ batchSavedPosition = 0;
+ batchStartRow = 0;
+ batchSavedDeltaSeg = 0;
+}
+
+/*
+ * Dynamic ListView Implementation
+*/
+
+void QDynamicListViewBase::dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight)
+{
+ if (column() >= topLeft.column() && column() <= bottomRight.column()) {
+ QStyleOptionViewItemV4 option = viewOptions();
+ int bottom = qMin(items.count(), bottomRight.row() + 1);
+ for (int row = topLeft.row(); row < bottom; ++row)
+ items[row].resize(itemSize(option, modelIndex(row)));
+ }
+}
+
+bool QDynamicListViewBase::doBatchedItemLayout(const QListViewLayoutInfo &info, int max)
+{
+ if (info.last >= items.count()) {
+ createItems(info.last + 1);
+ doDynamicLayout(info);
+ }
+ return (batchStartRow > max); // done
+}
+
+QListViewItem QDynamicListViewBase::indexToListViewItem(const QModelIndex &index) const
+{
+ if (index.isValid() && index.row() < items.count())
+ return items.at(index.row());
+ return QListViewItem();
+}
+
+void QDynamicListViewBase::initBspTree(const QSize &contents)
+{
+ // remove all items from the tree
+ int leafCount = tree.leafCount();
+ for (int l = 0; l < leafCount; ++l)
+ tree.leaf(l).clear();
+ // we have to get the bounding rect of the items before we can initialize the tree
+ QBspTree::Node::Type type = QBspTree::Node::Both; // 2D
+ // simple heuristics to get better bsp
+ if (contents.height() / contents.width() >= 3)
+ type = QBspTree::Node::HorizontalPlane;
+ else if (contents.width() / contents.height() >= 3)
+ type = QBspTree::Node::VerticalPlane;
+ // build tree for the bounding rect (not just the contents rect)
+ tree.init(QRect(0, 0, contents.width(), contents.height()), type);
+}
+
+QPoint QDynamicListViewBase::initDynamicLayout(const QListViewLayoutInfo &info)
+{
+ int x, y;
+ if (info.first == 0) {
+ x = info.bounds.x() + info.spacing;
+ y = info.bounds.y() + info.spacing;
+ items.reserve(rowCount() - hiddenCount());
+ } else {
+ const QListViewItem item = items.at(info.first - 1);
+ x = item.x;
+ y = item.y;
+ if (info.flow == QListView::LeftToRight)
+ x += (info.grid.isValid() ? info.grid.width() : item.w) + info.spacing;
+ else
+ y += (info.grid.isValid() ? info.grid.height() : item.h) + info.spacing;
+ }
+ return QPoint(x, y);
+}
+
+/*!
+ \internal
+*/
+void QDynamicListViewBase::doDynamicLayout(const QListViewLayoutInfo &info)
+{
+ const bool useItemSize = !info.grid.isValid();
+ const QPoint topLeft = initDynamicLayout(info);
+
+ int segStartPosition;
+ int segEndPosition;
+ int deltaFlowPosition;
+ int deltaSegPosition;
+ int deltaSegHint;
+ int flowPosition;
+ int segPosition;
+
+ if (info.flow == QListView::LeftToRight) {
+ segStartPosition = info.bounds.left() + info.spacing;
+ segEndPosition = info.bounds.right();
+ deltaFlowPosition = info.grid.width(); // dx
+ deltaSegPosition = (useItemSize ? batchSavedDeltaSeg : info.grid.height()); // dy
+ deltaSegHint = info.grid.height();
+ flowPosition = topLeft.x();
+ segPosition = topLeft.y();
+ } else { // flow == QListView::TopToBottom
+ segStartPosition = info.bounds.top() + info.spacing;
+ segEndPosition = info.bounds.bottom();
+ deltaFlowPosition = info.grid.height(); // dy
+ deltaSegPosition = (useItemSize ? batchSavedDeltaSeg : info.grid.width()); // dx
+ deltaSegHint = info.grid.width();
+ flowPosition = topLeft.y();
+ segPosition = topLeft.x();
+ }
+
+ if (moved.count() != items.count())
+ moved.resize(items.count());
+
+ QRect rect(QPoint(0, 0), topLeft);
+ QListViewItem *item = 0;
+ for (int row = info.first; row <= info.last; ++row) {
+ item = &items[row];
+ if (isHidden(row)) {
+ item->invalidate();
+ } else {
+ // if we are not using a grid, we need to find the deltas
+ if (useItemSize) {
+ if (info.flow == QListView::LeftToRight)
+ deltaFlowPosition = item->w + info.spacing;
+ else
+ deltaFlowPosition = item->h + info.spacing;
+ } else {
+ item->w = qMin<int>(info.grid.width(), item->w);
+ item->h = qMin<int>(info.grid.height(), item->h);
+ }
+
+ // create new segment
+ if (info.wrap
+ && flowPosition + deltaFlowPosition > segEndPosition
+ && flowPosition > segStartPosition) {
+ flowPosition = segStartPosition;
+ segPosition += deltaSegPosition;
+ if (useItemSize)
+ deltaSegPosition = 0;
+ }
+ // We must delay calculation of the seg adjustment, as this item
+ // may have caused a wrap to occur
+ if (useItemSize) {
+ if (info.flow == QListView::LeftToRight)
+ deltaSegHint = item->h + info.spacing;
+ else
+ deltaSegHint = item->w + info.spacing;
+ deltaSegPosition = qMax(deltaSegPosition, deltaSegHint);
+ }
+
+ // set the position of the item
+ // ### idealy we should have some sort of alignment hint for the item
+ // ### (normally that would be a point between the icon and the text)
+ if (!moved.testBit(row)) {
+ if (info.flow == QListView::LeftToRight) {
+ if (useItemSize) {
+ item->x = flowPosition;
+ item->y = segPosition;
+ } else { // use grid
+ item->x = flowPosition + ((deltaFlowPosition - item->w) / 2);
+ item->y = segPosition;
+ }
+ } else { // TopToBottom
+ if (useItemSize) {
+ item->y = flowPosition;
+ item->x = segPosition;
+ } else { // use grid
+ item->y = flowPosition + ((deltaFlowPosition - item->h) / 2);
+ item->x = segPosition;
+ }
+ }
+ }
+
+ // let the contents contain the new item
+ if (useItemSize)
+ rect |= item->rect();
+ else if (info.flow == QListView::LeftToRight)
+ rect |= QRect(flowPosition, segPosition, deltaFlowPosition, deltaSegPosition);
+ else // flow == TopToBottom
+ rect |= QRect(segPosition, flowPosition, deltaSegPosition, deltaFlowPosition);
+
+ // prepare for next item
+ flowPosition += deltaFlowPosition; // current position + item width + gap
+ }
+ }
+ batchSavedDeltaSeg = deltaSegPosition;
+ batchStartRow = info.last + 1;
+ bool done = (info.last >= rowCount() - 1);
+ // resize the content area
+ if (done || !info.bounds.contains(item->rect()))
+ contentsSize = QSize(rect.width(), rect.height());
+ // resize tree
+ int insertFrom = info.first;
+ if (done || info.first == 0) {
+ initBspTree(rect.size());
+ insertFrom = 0;
+ }
+ // insert items in tree
+ for (int row = insertFrom; row <= info.last; ++row)
+ tree.insertLeaf(items.at(row).rect(), row);
+ // if the new items are visble, update the viewport
+ QRect changedRect(topLeft, rect.bottomRight());
+ if (clipRect().intersects(changedRect))
+ viewport()->update();
+}
+
+void QDynamicListViewBase::intersectingDynamicSet(const QRect &area) const
+{
+ clearIntersections();
+ QListViewPrivate *that = const_cast<QListViewPrivate*>(dd);
+ QBspTree::Data data(static_cast<void*>(that));
+ that->dynamicListView->tree.climbTree(area, &QDynamicListViewBase::addLeaf, data);
+}
+
+void QDynamicListViewBase::createItems(int to)
+{
+ int count = items.count();
+ QSize size;
+ QStyleOptionViewItemV4 option = viewOptions();
+ for (int row = count; row < to; ++row) {
+ size = itemSize(option, modelIndex(row));
+ QListViewItem item(QRect(0, 0, size.width(), size.height()), row); // default pos
+ items.append(item);
+ }
+}
+
+void QDynamicListViewBase::drawItems(QPainter *painter, const QVector<QModelIndex> &indexes) const
+{
+ QStyleOptionViewItemV4 option = viewOptions();
+ option.state &= ~QStyle::State_MouseOver;
+ QVector<QModelIndex>::const_iterator it = indexes.begin();
+ QListViewItem item = indexToListViewItem(*it);
+ for (; it != indexes.end(); ++it) {
+ item = indexToListViewItem(*it);
+ option.rect = viewItemRect(item);
+ delegate(*it)->paint(painter, option, *it);
+ }
+}
+
+QRect QDynamicListViewBase::itemsRect(const QVector<QModelIndex> &indexes) const
+{
+ QVector<QModelIndex>::const_iterator it = indexes.begin();
+ QListViewItem item = indexToListViewItem(*it);
+ QRect rect(item.x, item.y, item.w, item.h);
+ for (; it != indexes.end(); ++it) {
+ item = indexToListViewItem(*it);
+ rect |= viewItemRect(item);
+ }
+ return rect;
+}
+
+int QDynamicListViewBase::itemIndex(const QListViewItem &item) const
+{
+ if (!item.isValid())
+ return -1;
+ int i = item.indexHint;
+ if (i < items.count()) {
+ if (items.at(i) == item)
+ return i;
+ } else {
+ i = items.count() - 1;
+ }
+
+ int j = i;
+ int c = items.count();
+ bool a = true;
+ bool b = true;
+
+ while (a || b) {
+ if (a) {
+ if (items.at(i) == item) {
+ items.at(i).indexHint = i;
+ return i;
+ }
+ a = ++i < c;
+ }
+ if (b) {
+ if (items.at(j) == item) {
+ items.at(j).indexHint = j;
+ return j;
+ }
+ b = --j > -1;
+ }
+ }
+ return -1;
+}
+
+void QDynamicListViewBase::addLeaf(QVector<int> &leaf, const QRect &area,
+ uint visited, QBspTree::Data data)
+{
+ QListViewItem *vi;
+ QListViewPrivate *_this = static_cast<QListViewPrivate *>(data.ptr);
+ for (int i = 0; i < leaf.count(); ++i) {
+ int idx = leaf.at(i);
+ if (idx < 0 || idx >= _this->dynamicListView->items.count())
+ continue;
+ vi = &_this->dynamicListView->items[idx];
+ Q_ASSERT(vi);
+ if (vi->isValid() && vi->rect().intersects(area) && vi->visited != visited) {
+ QModelIndex index = _this->listViewItemToIndex(*vi);
+ Q_ASSERT(index.isValid());
+ _this->intersectVector.append(index);
+ vi->visited = visited;
+ }
+ }
+}
+
+void QDynamicListViewBase::insertItem(int index)
+{
+ if (index >= 0 && index < items.count())
+ tree.insertLeaf(items.at(index).rect(), index);
+}
+
+void QDynamicListViewBase::removeItem(int index)
+{
+ if (index >= 0 && index < items.count())
+ tree.removeLeaf(items.at(index).rect(), index);
+}
+
+void QDynamicListViewBase::moveItem(int index, const QPoint &dest)
+{
+ // does not impact on the bintree itself or the contents rect
+ QListViewItem *item = &items[index];
+ QRect rect = item->rect();
+
+ // move the item without removing it from the tree
+ tree.removeLeaf(rect, index);
+ item->move(dest);
+ tree.insertLeaf(QRect(dest, rect.size()), index);
+
+ // resize the contents area
+ contentsSize = (QRect(QPoint(0, 0), contentsSize)|QRect(dest, rect.size())).size();
+
+ // mark the item as moved
+ if (moved.count() != items.count())
+ moved.resize(items.count());
+ moved.setBit(index, true);
+}
+
+QPoint QDynamicListViewBase::snapToGrid(const QPoint &pos) const
+{
+ int x = pos.x() - (pos.x() % gridSize().width());
+ int y = pos.y() - (pos.y() % gridSize().height());
+ return QPoint(x, y);
+}
+
+QPoint QDynamicListViewBase::draggedItemsDelta() const
+{
+ if (movement() == QListView::Snap) {
+ QPoint snapdelta = QPoint((offset().x() % gridSize().width()),
+ (offset().y() % gridSize().height()));
+ return snapToGrid(draggedItemsPos + snapdelta) - snapToGrid(pressedPosition()) - snapdelta;
+ }
+ return draggedItemsPos - pressedPosition();
+}
+
+QRect QDynamicListViewBase::draggedItemsRect() const
+{
+ QRect rect = itemsRect(draggedItems);
+ rect.translate(draggedItemsDelta());
+ return rect;
+}
+
+void QListViewPrivate::scrollElasticBandBy(int dx, int dy)
+{
+ if (dx > 0) // right
+ elasticBand.moveRight(elasticBand.right() + dx);
+ else if (dx < 0) // left
+ elasticBand.moveLeft(elasticBand.left() - dx);
+ if (dy > 0) // down
+ elasticBand.moveBottom(elasticBand.bottom() + dy);
+ else if (dy < 0) // up
+ elasticBand.moveTop(elasticBand.top() - dy);
+}
+
+void QDynamicListViewBase::clear()
+{
+ tree.destroy();
+ items.clear();
+ moved.clear();
+ batchStartRow = 0;
+ batchSavedDeltaSeg = 0;
+}
+
+void QDynamicListViewBase::updateContentsSize()
+{
+ QRect bounding;
+ for (int i = 0; i < items.count(); ++i)
+ bounding |= items.at(i).rect();
+ contentsSize = bounding.size();
+}
+
+/*!
+ \reimp
+*/
+void QListView::currentChanged(const QModelIndex &current, const QModelIndex &previous)
+{
+#ifndef QT_NO_ACCESSIBILITY
+ if (QAccessible::isActive()) {
+ if (current.isValid()) {
+ int entry = visualIndex(current) + 1;
+ QAccessible::updateAccessibility(viewport(), entry, QAccessible::Focus);
+ }
+ }
+#endif
+ QAbstractItemView::currentChanged(current, previous);
+}
+
+/*!
+ \reimp
+*/
+void QListView::selectionChanged(const QItemSelection &selected,
+ const QItemSelection &deselected)
+{
+#ifndef QT_NO_ACCESSIBILITY
+ if (QAccessible::isActive()) {
+ // ### does not work properly for selection ranges.
+ QModelIndex sel = selected.indexes().value(0);
+ if (sel.isValid()) {
+ int entry = visualIndex(sel) + 1;
+ QAccessible::updateAccessibility(viewport(), entry, QAccessible::Selection);
+ }
+ QModelIndex desel = deselected.indexes().value(0);
+ if (desel.isValid()) {
+ int entry = visualIndex(desel) + 1;
+ QAccessible::updateAccessibility(viewport(), entry, QAccessible::SelectionRemove);
+ }
+ }
+#endif
+ QAbstractItemView::selectionChanged(selected, deselected);
+}
+
+int QListView::visualIndex(const QModelIndex &index) const
+{
+ Q_D(const QListView);
+ d->executePostedLayout();
+ QListViewItem itm = d->indexToListViewItem(index);
+ return d->itemIndex(itm);
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_LISTVIEW
diff --git a/src/gui/itemviews/qlistview.h b/src/gui/itemviews/qlistview.h
new file mode 100644
index 0000000000..dd719d9891
--- /dev/null
+++ b/src/gui/itemviews/qlistview.h
@@ -0,0 +1,203 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QLISTVIEW_H
+#define QLISTVIEW_H
+
+#include <QtGui/qabstractitemview.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+#ifndef QT_NO_LISTVIEW
+
+class QListViewPrivate;
+
+class Q_GUI_EXPORT QListView : public QAbstractItemView
+{
+ Q_OBJECT
+ Q_ENUMS(Movement Flow ResizeMode LayoutMode ViewMode)
+ Q_PROPERTY(Movement movement READ movement WRITE setMovement)
+ Q_PROPERTY(Flow flow READ flow WRITE setFlow)
+ Q_PROPERTY(bool isWrapping READ isWrapping WRITE setWrapping)
+ Q_PROPERTY(ResizeMode resizeMode READ resizeMode WRITE setResizeMode)
+ Q_PROPERTY(LayoutMode layoutMode READ layoutMode WRITE setLayoutMode)
+ Q_PROPERTY(int spacing READ spacing WRITE setSpacing)
+ Q_PROPERTY(QSize gridSize READ gridSize WRITE setGridSize)
+ Q_PROPERTY(ViewMode viewMode READ viewMode WRITE setViewMode)
+ Q_PROPERTY(int modelColumn READ modelColumn WRITE setModelColumn)
+ Q_PROPERTY(bool uniformItemSizes READ uniformItemSizes WRITE setUniformItemSizes)
+ Q_PROPERTY(int batchSize READ batchSize WRITE setBatchSize)
+ Q_PROPERTY(bool wordWrap READ wordWrap WRITE setWordWrap)
+ Q_PROPERTY(bool selectionRectVisible READ isSelectionRectVisible WRITE setSelectionRectVisible)
+
+public:
+ enum Movement { Static, Free, Snap };
+ enum Flow { LeftToRight, TopToBottom };
+ enum ResizeMode { Fixed, Adjust };
+ enum LayoutMode { SinglePass, Batched };
+ enum ViewMode { ListMode, IconMode };
+
+ explicit QListView(QWidget *parent = 0);
+ ~QListView();
+
+ void setMovement(Movement movement);
+ Movement movement() const;
+
+ void setFlow(Flow flow);
+ Flow flow() const;
+
+ void setWrapping(bool enable);
+ bool isWrapping() const;
+
+ void setResizeMode(ResizeMode mode);
+ ResizeMode resizeMode() const;
+
+ void setLayoutMode(LayoutMode mode);
+ LayoutMode layoutMode() const;
+
+ void setSpacing(int space);
+ int spacing() const;
+
+ void setBatchSize(int batchSize);
+ int batchSize() const;
+
+ void setGridSize(const QSize &size);
+ QSize gridSize() const;
+
+ void setViewMode(ViewMode mode);
+ ViewMode viewMode() const;
+
+ void clearPropertyFlags();
+
+ bool isRowHidden(int row) const;
+ void setRowHidden(int row, bool hide);
+
+ void setModelColumn(int column);
+ int modelColumn() const;
+
+ void setUniformItemSizes(bool enable);
+ bool uniformItemSizes() const;
+
+ void setWordWrap(bool on);
+ bool wordWrap() const;
+
+ void setSelectionRectVisible(bool show);
+ bool isSelectionRectVisible() const;
+
+ QRect visualRect(const QModelIndex &index) const;
+ void scrollTo(const QModelIndex &index, ScrollHint hint = EnsureVisible);
+ QModelIndex indexAt(const QPoint &p) const;
+
+ void doItemsLayout();
+ void reset();
+ void setRootIndex(const QModelIndex &index);
+
+Q_SIGNALS:
+ void indexesMoved(const QModelIndexList &indexes);
+
+protected:
+ QListView(QListViewPrivate &, QWidget *parent = 0);
+
+ bool event(QEvent *e);
+
+ void scrollContentsBy(int dx, int dy);
+
+ void resizeContents(int width, int height);
+ QSize contentsSize() const;
+
+ void dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight);
+ void rowsInserted(const QModelIndex &parent, int start, int end);
+ void rowsAboutToBeRemoved(const QModelIndex &parent, int start, int end);
+
+ void mouseMoveEvent(QMouseEvent *e);
+ void mouseReleaseEvent(QMouseEvent *e);
+
+ void timerEvent(QTimerEvent *e);
+ void resizeEvent(QResizeEvent *e);
+#ifndef QT_NO_DRAGANDDROP
+ void dragMoveEvent(QDragMoveEvent *e);
+ void dragLeaveEvent(QDragLeaveEvent *e);
+ void dropEvent(QDropEvent *e);
+ void startDrag(Qt::DropActions supportedActions);
+
+ void internalDrop(QDropEvent *e);
+ void internalDrag(Qt::DropActions supportedActions);
+#endif // QT_NO_DRAGANDDROP
+
+ QStyleOptionViewItem viewOptions() const;
+ void paintEvent(QPaintEvent *e);
+
+ int horizontalOffset() const;
+ int verticalOffset() const;
+ QModelIndex moveCursor(CursorAction cursorAction, Qt::KeyboardModifiers modifiers);
+ QRect rectForIndex(const QModelIndex &index) const;
+ void setPositionForIndex(const QPoint &position, const QModelIndex &index);
+
+ void setSelection(const QRect &rect, QItemSelectionModel::SelectionFlags command);
+ QRegion visualRegionForSelection(const QItemSelection &selection) const;
+ QModelIndexList selectedIndexes() const;
+
+ void updateGeometries();
+
+ bool isIndexHidden(const QModelIndex &index) const;
+
+ void selectionChanged(const QItemSelection &selected, const QItemSelection &deselected);
+ void currentChanged(const QModelIndex &current, const QModelIndex &previous);
+
+private:
+ friend class QAccessibleItemView;
+ int visualIndex(const QModelIndex &index) const;
+
+ Q_DECLARE_PRIVATE(QListView)
+ Q_DISABLE_COPY(QListView)
+};
+
+#endif // QT_NO_LISTVIEW
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QLISTVIEW_H
diff --git a/src/gui/itemviews/qlistview_p.h b/src/gui/itemviews/qlistview_p.h
new file mode 100644
index 0000000000..4568d8c46b
--- /dev/null
+++ b/src/gui/itemviews/qlistview_p.h
@@ -0,0 +1,450 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QLISTVIEW_P_H
+#define QLISTVIEW_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 "private/qabstractitemview_p.h"
+#include "qrubberband.h"
+#include "qbitarray.h"
+#include "qbsptree_p.h"
+#include <limits.h>
+#include <qscrollbar.h>
+
+#ifndef QT_NO_LISTVIEW
+
+QT_BEGIN_NAMESPACE
+
+class QListViewItem
+{
+ friend class QListViewPrivate;
+ friend class QStaticListViewBase;
+ friend class QDynamicListViewBase;
+public:
+ inline QListViewItem()
+ : x(-1), y(-1), w(0), h(0), indexHint(-1), visited(0xffff) {}
+ inline QListViewItem(const QListViewItem &other)
+ : x(other.x), y(other.y), w(other.w), h(other.h),
+ indexHint(other.indexHint), visited(other.visited) {}
+ inline QListViewItem(QRect r, int i)
+ : x(r.x()), y(r.y()), w(qMin(r.width(), SHRT_MAX)), h(qMin(r.height(), SHRT_MAX)),
+ indexHint(i), visited(0xffff) {}
+ inline bool operator==(const QListViewItem &other) const {
+ return (x == other.x && y == other.y && w == other.w && h == other.h &&
+ indexHint == other.indexHint); }
+ inline bool operator!=(const QListViewItem &other) const
+ { return !(*this == other); }
+ inline bool isValid() const
+ { return (x > -1) && (y > -1) && (w > 0) && (h > 0) && (indexHint > -1); }
+ inline void invalidate()
+ { x = -1; y = -1; w = 0; h = 0; }
+ inline void resize(const QSize &size)
+ { w = qMin(size.width(), SHRT_MAX); h = qMin(size.height(), SHRT_MAX); }
+ inline void move(const QPoint &position)
+ { x = position.x(); y = position.y(); }
+ inline int width() const { return w; }
+ inline int height() const { return h; }
+private:
+ inline QRect rect() const
+ { return QRect(x, y, w, h); }
+ int x, y;
+ short w, h;
+ mutable int indexHint;
+ uint visited;
+};
+
+struct QListViewLayoutInfo
+{
+ QRect bounds;
+ QSize grid;
+ int spacing;
+ int first;
+ int last;
+ bool wrap;
+ QListView::Flow flow;
+ int max;
+};
+
+class QListView;
+class QListViewPrivate;
+
+class QCommonListViewBase
+{
+public:
+ inline QCommonListViewBase(QListView *q, QListViewPrivate *d) : dd(d), qq(q) {}
+
+ inline int spacing() const;
+ inline bool isWrapping() const;
+ inline QSize gridSize() const;
+ inline QListView::Flow flow() const;
+ inline QListView::Movement movement() const;
+
+ inline QPoint offset() const;
+ inline QPoint pressedPosition() const;
+ inline bool uniformItemSizes() const;
+ inline int column() const;
+
+ inline int verticalScrollBarValue() const;
+ inline int horizontalScrollBarValue() const;
+ inline QListView::ScrollMode verticalScrollMode() const;
+ inline QListView::ScrollMode horizontalScrollMode() const;
+
+ inline QModelIndex modelIndex(int row) const;
+ inline int rowCount() const;
+
+ inline QStyleOptionViewItemV4 viewOptions() const;
+ inline QWidget *viewport() const;
+ inline QRect clipRect() const;
+
+ inline QSize cachedItemSize() const;
+ inline QRect viewItemRect(const QListViewItem &item) const;
+ inline QSize itemSize(const QStyleOptionViewItemV2 &opt, const QModelIndex &idx) const;
+ inline QAbstractItemDelegate *delegate(const QModelIndex &idx) const;
+
+ inline bool isHidden(int row) const;
+ inline int hiddenCount() const;
+
+ inline void clearIntersections() const;
+ inline void appendToIntersections(const QModelIndex &idx) const;
+
+ inline bool isRightToLeft() const;
+
+ QListViewPrivate *dd;
+ QListView *qq;
+};
+
+// ### rename to QListModeViewBase
+class QStaticListViewBase : public QCommonListViewBase
+{
+ friend class QListViewPrivate;
+public:
+ QStaticListViewBase(QListView *q, QListViewPrivate *d) : QCommonListViewBase(q, d),
+ batchStartRow(0), batchSavedDeltaSeg(0), batchSavedPosition(0) {}
+
+ QVector<int> flowPositions;
+ QVector<int> segmentPositions;
+ QVector<int> segmentStartRows;
+ QVector<int> segmentExtents;
+
+ QSize contentsSize;
+
+ // used when laying out in batches
+ int batchStartRow;
+ int batchSavedDeltaSeg;
+ int batchSavedPosition;
+
+ bool doBatchedItemLayout(const QListViewLayoutInfo &info, int max);
+
+ QPoint initStaticLayout(const QListViewLayoutInfo &info);
+ void doStaticLayout(const QListViewLayoutInfo &info);
+ void intersectingStaticSet(const QRect &area) const;
+
+ int itemIndex(const QListViewItem &item) const;
+
+ int perItemScrollingPageSteps(int length, int bounds, bool wrap) const;
+
+ int perItemScrollToValue(int index, int value, int height,
+ QAbstractItemView::ScrollHint hint,
+ Qt::Orientation orientation, bool wrap, int extent) const;
+
+ QRect mapToViewport(const QRect &rect) const;
+
+ QListViewItem indexToListViewItem(const QModelIndex &index) const;
+
+ void scrollContentsBy(int &dx, int &dy);
+
+ int verticalPerItemValue(int itemIndex, int verticalValue, int areaHeight,
+ bool above, bool below, bool wrap, QListView::ScrollHint hint, int itemHeight) const;
+ int horizontalPerItemValue(int itemIndex, int horizontalValue, int areaWidth,
+ bool leftOf, bool rightOf, bool wrap, QListView::ScrollHint hint, int itemWidth) const;
+
+ void clear();
+};
+
+// ### rename to QIconModeViewBase
+class QDynamicListViewBase : public QCommonListViewBase
+{
+ friend class QListViewPrivate;
+public:
+ QDynamicListViewBase(QListView *q, QListViewPrivate *d) : QCommonListViewBase(q, d),
+ batchStartRow(0), batchSavedDeltaSeg(0) {}
+
+ QBspTree tree;
+ QVector<QListViewItem> items;
+ QBitArray moved;
+
+ QSize contentsSize;
+
+ QVector<QModelIndex> draggedItems; // indices to the tree.itemVector
+ mutable QPoint draggedItemsPos;
+
+ // used when laying out in batches
+ int batchStartRow;
+ int batchSavedDeltaSeg;
+
+ void dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight);
+ bool doBatchedItemLayout(const QListViewLayoutInfo &info, int max);
+
+ void initBspTree(const QSize &contents);
+ QPoint initDynamicLayout(const QListViewLayoutInfo &info);
+ void doDynamicLayout(const QListViewLayoutInfo &info);
+ void intersectingDynamicSet(const QRect &area) const;
+
+ static void addLeaf(QVector<int> &leaf, const QRect &area,
+ uint visited, QBspTree::Data data);
+
+ void insertItem(int index);
+ void removeItem(int index);
+ void moveItem(int index, const QPoint &dest);
+
+ int itemIndex(const QListViewItem &item) const;
+
+ void createItems(int to);
+ void drawItems(QPainter *painter, const QVector<QModelIndex> &indexes) const;
+ QRect itemsRect(const QVector<QModelIndex> &indexes) const;
+
+ QPoint draggedItemsDelta() const;
+ QRect draggedItemsRect() const;
+
+ QPoint snapToGrid(const QPoint &pos) const;
+
+ void scrollElasticBandBy(int dx, int dy);
+
+ QListViewItem indexToListViewItem(const QModelIndex &index) const;
+
+ void clear();
+ void updateContentsSize();
+};
+
+class QListViewPrivate: public QAbstractItemViewPrivate
+{
+ Q_DECLARE_PUBLIC(QListView)
+public:
+ QListViewPrivate();
+ ~QListViewPrivate();
+
+ void clear();
+ void prepareItemsLayout();
+
+ bool doItemsLayout(int num);
+
+ inline void intersectingSet(const QRect &area, bool doLayout = true) const {
+ if (doLayout) executePostedLayout();
+ QRect a = (q_func()->isRightToLeft() ? flipX(area.normalized()) : area.normalized());
+ if (viewMode == QListView::ListMode) staticListView->intersectingStaticSet(a);
+ else dynamicListView->intersectingDynamicSet(a);
+ }
+
+ // ### FIXME:
+ inline void resetBatchStartRow()
+ { if (viewMode == QListView::ListMode) staticListView->batchStartRow = 0;
+ else dynamicListView->batchStartRow = 0; }
+ inline int batchStartRow() const
+ { return (viewMode == QListView::ListMode
+ ? staticListView->batchStartRow : dynamicListView->batchStartRow); }
+ inline QSize contentsSize() const
+ { return (viewMode == QListView::ListMode
+ ? staticListView->contentsSize : dynamicListView->contentsSize); }
+ inline void setContentsSize(int w, int h)
+ { if (viewMode == QListView::ListMode) staticListView->contentsSize = QSize(w, h);
+ else dynamicListView->contentsSize = QSize(w, h); }
+
+ inline int flipX(int x) const
+ { return qMax(viewport->width(), contentsSize().width()) - x; }
+ inline QPoint flipX(const QPoint &p) const
+ { return QPoint(flipX(p.x()), p.y()); }
+ inline QRect flipX(const QRect &r) const
+ { return QRect(flipX(r.x()) - r.width(), r.y(), r.width(), r.height()); }
+ inline QRect viewItemRect(const QListViewItem &item) const
+ { if (q_func()->isRightToLeft()) return flipX(item.rect()); return item.rect(); }
+
+ int itemIndex(const QListViewItem &item) const;
+ QListViewItem indexToListViewItem(const QModelIndex &index) const;
+ inline QModelIndex listViewItemToIndex(const QListViewItem &item) const
+ { return model->index(itemIndex(item), column, root); }
+
+ QRect mapToViewport(const QRect &rect, bool greedy = false) const;
+
+ QModelIndex closestIndex(const QRect &target, const QVector<QModelIndex> &candidates) const;
+ QSize itemSize(const QStyleOptionViewItem &option, const QModelIndex &index) const;
+
+ bool selectionAllowed(const QModelIndex &index) const
+ { if (viewMode == QListView::ListMode && !showElasticBand) return index.isValid(); return true; }
+
+ int horizontalScrollToValue(const QModelIndex &index, const QRect &rect, QListView::ScrollHint hint) const;
+ int verticalScrollToValue(const QModelIndex &index, const QRect &rect, QListView::ScrollHint hint) const;
+
+ QItemSelection selection(const QRect &rect) const;
+ void selectAll(QItemSelectionModel::SelectionFlags command);
+
+ inline void setGridSize(const QSize &size) { grid = size; }
+ inline QSize gridSize() const { return grid; }
+ inline void setWrapping(bool b) { wrap = b; }
+ inline bool isWrapping() const { return wrap; }
+ inline void setSpacing(int s) { space = s; }
+ inline int spacing() const { return space; }
+ inline void setSelectionRectVisible(bool visible) { showElasticBand = visible; }
+ inline bool isSelectionRectVisible() const { return showElasticBand; }
+
+ inline QModelIndex modelIndex(int row) const { return model->index(row, column, root); }
+ inline bool isHidden(int row) const { return hiddenRows.contains(model->index(row, 0, root)); }
+ inline bool isHiddenOrDisabled(int row) const { return isHidden(row) || !isIndexEnabled(modelIndex(row)); }
+
+ inline void removeCurrentAndDisabled(QVector<QModelIndex> *indexes, const QModelIndex &current) const {
+ QVector<QModelIndex>::iterator it = indexes->begin();
+ while (it != indexes->end()) {
+ if (!isIndexEnabled(*it) || (*it) == current)
+ indexes->erase(it);
+ else
+ ++it;
+ }
+ }
+
+ void scrollElasticBandBy(int dx, int dy);
+
+ // ### FIXME: we only need one at a time
+ QDynamicListViewBase *dynamicListView;
+ QStaticListViewBase *staticListView;
+
+ // ### FIXME: see if we can move the members into the dynamic/static classes
+
+ bool wrap;
+ int space;
+ QSize grid;
+
+ QListView::Flow flow;
+ QListView::Movement movement;
+ QListView::ResizeMode resizeMode;
+ QListView::LayoutMode layoutMode;
+ QListView::ViewMode viewMode;
+
+ // the properties controlling the
+ // icon- or list-view modes
+ enum ModeProperties {
+ Wrap = 1,
+ Spacing = 2,
+ GridSize = 4,
+ Flow = 8,
+ Movement = 16,
+ ResizeMode = 32,
+ SelectionRectVisible = 64
+ };
+
+ uint modeProperties : 8;
+
+ QRect layoutBounds;
+
+ // used for intersecting set
+ mutable QVector<QModelIndex> intersectVector;
+
+ // timers
+ QBasicTimer batchLayoutTimer;
+
+ // used for hidden items
+ QVector<QPersistentModelIndex> hiddenRows;
+
+ int column;
+ bool uniformItemSizes;
+ mutable QSize cachedItemSize;
+ int batchSize;
+
+ QRect elasticBand;
+ bool showElasticBand;
+};
+
+// inline implementations
+
+inline int QCommonListViewBase::spacing() const { return dd->spacing(); }
+inline bool QCommonListViewBase::isWrapping() const { return dd->isWrapping(); }
+inline QSize QCommonListViewBase::gridSize() const { return dd->gridSize(); }
+inline QListView::Flow QCommonListViewBase::flow() const { return dd->flow; }
+inline QListView::Movement QCommonListViewBase::movement() const { return dd->movement; }
+
+inline QPoint QCommonListViewBase::offset() const { return dd->offset(); }
+inline QPoint QCommonListViewBase::pressedPosition() const { return dd->pressedPosition; }
+inline bool QCommonListViewBase::uniformItemSizes() const { return dd->uniformItemSizes; }
+inline int QCommonListViewBase::column() const { return dd->column; }
+
+inline int QCommonListViewBase::verticalScrollBarValue() const { return qq->verticalScrollBar()->value(); }
+inline int QCommonListViewBase::horizontalScrollBarValue() const { return qq->horizontalScrollBar()->value(); }
+inline QListView::ScrollMode QCommonListViewBase::verticalScrollMode() const { return qq->verticalScrollMode(); }
+inline QListView::ScrollMode QCommonListViewBase::horizontalScrollMode() const { return qq->horizontalScrollMode(); }
+
+inline QModelIndex QCommonListViewBase::modelIndex(int row) const
+ { return dd->model->index(row, dd->column, dd->root); }
+inline int QCommonListViewBase::rowCount() const { return dd->model->rowCount(dd->root); }
+
+inline QStyleOptionViewItemV4 QCommonListViewBase::viewOptions() const { return dd->viewOptionsV4(); }
+inline QWidget *QCommonListViewBase::viewport() const { return dd->viewport; }
+inline QRect QCommonListViewBase::clipRect() const { return dd->clipRect(); }
+
+inline QSize QCommonListViewBase::cachedItemSize() const { return dd->cachedItemSize; }
+inline QRect QCommonListViewBase::viewItemRect(const QListViewItem &item) const { return dd->viewItemRect(item); }
+inline QSize QCommonListViewBase::itemSize(const QStyleOptionViewItemV2 &opt, const QModelIndex &idx) const
+ { return dd->itemSize(opt, idx); }
+
+inline QAbstractItemDelegate *QCommonListViewBase::delegate(const QModelIndex &idx) const
+ { return dd->delegateForIndex(idx); }
+
+inline bool QCommonListViewBase::isHidden(int row) const { return dd->isHidden(row); }
+inline int QCommonListViewBase::hiddenCount() const { return dd->hiddenRows.count(); }
+
+inline void QCommonListViewBase::clearIntersections() const { dd->intersectVector.clear(); }
+inline void QCommonListViewBase::appendToIntersections(const QModelIndex &idx) const { dd->intersectVector.append(idx); }
+
+inline bool QCommonListViewBase::isRightToLeft() const { return qq->isRightToLeft(); }
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_LISTVIEW
+
+#endif // QLISTVIEW_P_H
diff --git a/src/gui/itemviews/qlistwidget.cpp b/src/gui/itemviews/qlistwidget.cpp
new file mode 100644
index 0000000000..7a366d1bb2
--- /dev/null
+++ b/src/gui/itemviews/qlistwidget.cpp
@@ -0,0 +1,1865 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qlistwidget.h"
+
+#ifndef QT_NO_LISTWIDGET
+#include <qitemdelegate.h>
+#include <private/qlistview_p.h>
+#include <private/qwidgetitemdata_p.h>
+#include <private/qlistwidget_p.h>
+
+QT_BEGIN_NAMESPACE
+
+// workaround for VC++ 6.0 linker bug (?)
+typedef bool(*LessThan)(const QPair<QListWidgetItem*,int>&,const QPair<QListWidgetItem*,int>&);
+
+class QListWidgetMimeData : public QMimeData
+{
+ Q_OBJECT
+public:
+ QList<QListWidgetItem*> items;
+};
+
+QT_BEGIN_INCLUDE_NAMESPACE
+#include "qlistwidget.moc"
+QT_END_INCLUDE_NAMESPACE
+
+QListModel::QListModel(QListWidget *parent)
+ : QAbstractListModel(parent)
+{
+}
+
+QListModel::~QListModel()
+{
+ clear();
+}
+
+void QListModel::clear()
+{
+ for (int i = 0; i < items.count(); ++i) {
+ if (items.at(i)) {
+ items.at(i)->d->theid = -1;
+ items.at(i)->view = 0;
+ delete items.at(i);
+ }
+ }
+ items.clear();
+ reset();
+}
+
+QListWidgetItem *QListModel::at(int row) const
+{
+ return items.value(row);
+}
+
+void QListModel::remove(QListWidgetItem *item)
+{
+ if (!item)
+ return;
+ int row = items.indexOf(item); // ### use index(item) - it's faster
+ Q_ASSERT(row != -1);
+ beginRemoveRows(QModelIndex(), row, row);
+ items.at(row)->d->theid = -1;
+ items.at(row)->view = 0;
+ items.removeAt(row);
+ endRemoveRows();
+}
+
+void QListModel::insert(int row, QListWidgetItem *item)
+{
+ if (!item)
+ return;
+
+ item->view = qobject_cast<QListWidget*>(QObject::parent());
+ if (item->view && item->view->isSortingEnabled()) {
+ // sorted insertion
+ QList<QListWidgetItem*>::iterator it;
+ it = sortedInsertionIterator(items.begin(), items.end(),
+ item->view->sortOrder(), item);
+ row = qMax(it - items.begin(), 0);
+ } else {
+ if (row < 0)
+ row = 0;
+ else if (row > items.count())
+ row = items.count();
+ }
+ beginInsertRows(QModelIndex(), row, row);
+ items.insert(row, item);
+ item->d->theid = row;
+ endInsertRows();
+}
+
+void QListModel::insert(int row, const QStringList &labels)
+{
+ const int count = labels.count();
+ if (count <= 0)
+ return;
+ QListWidget *view = qobject_cast<QListWidget*>(QObject::parent());
+ if (view && view->isSortingEnabled()) {
+ // sorted insertion
+ for (int i = 0; i < count; ++i) {
+ QListWidgetItem *item = new QListWidgetItem(labels.at(i));
+ insert(row, item);
+ }
+ } else {
+ if (row < 0)
+ row = 0;
+ else if (row > items.count())
+ row = items.count();
+ beginInsertRows(QModelIndex(), row, row + count - 1);
+ for (int i = 0; i < count; ++i) {
+ QListWidgetItem *item = new QListWidgetItem(labels.at(i));
+ item->d->theid = row;
+ item->view = qobject_cast<QListWidget*>(QObject::parent());
+ items.insert(row++, item);
+ }
+ endInsertRows();
+ }
+}
+
+QListWidgetItem *QListModel::take(int row)
+{
+ if (row < 0 || row >= items.count())
+ return 0;
+
+ beginRemoveRows(QModelIndex(), row, row);
+ items.at(row)->d->theid = -1;
+ items.at(row)->view = 0;
+ QListWidgetItem *item = items.takeAt(row);
+ endRemoveRows();
+ return item;
+}
+
+int QListModel::rowCount(const QModelIndex &parent) const
+{
+ return parent.isValid() ? 0 : items.count();
+}
+
+QModelIndex QListModel::index(QListWidgetItem *item) const
+{
+ if (!item || !item->view || static_cast<const QListModel *>(item->view->model()) != this
+ || items.isEmpty())
+ return QModelIndex();
+ int row;
+ const int theid = item->d->theid;
+ if (theid >= 0 && theid < items.count() && items.at(theid) == item) {
+ row = theid;
+ } else { // we need to search for the item
+ row = items.lastIndexOf(item); // lastIndexOf is an optimization in favor of indexOf
+ if (row == -1) // not found
+ return QModelIndex();
+ item->d->theid = row;
+ }
+ return createIndex(row, 0, item);
+}
+
+QModelIndex QListModel::index(int row, int column, const QModelIndex &parent) const
+{
+ if (hasIndex(row, column, parent))
+ return createIndex(row, column, items.at(row));
+ return QModelIndex();
+}
+
+QVariant QListModel::data(const QModelIndex &index, int role) const
+{
+ if (!index.isValid() || index.row() >= items.count())
+ return QVariant();
+ return items.at(index.row())->data(role);
+}
+
+bool QListModel::setData(const QModelIndex &index, const QVariant &value, int role)
+{
+ if (!index.isValid() || index.row() >= items.count())
+ return false;
+ items.at(index.row())->setData(role, value);
+ return true;
+}
+
+QMap<int, QVariant> QListModel::itemData(const QModelIndex &index) const
+{
+ QMap<int, QVariant> roles;
+ if (!index.isValid() || index.row() >= items.count())
+ return roles;
+ QListWidgetItem *itm = items.at(index.row());
+ for (int i = 0; i < itm->d->values.count(); ++i) {
+ roles.insert(itm->d->values.at(i).role,
+ itm->d->values.at(i).value);
+ }
+ return roles;
+}
+
+bool QListModel::insertRows(int row, int count, const QModelIndex &parent)
+{
+ if (count < 1 || row < 0 || row > rowCount() || parent.isValid())
+ return false;
+
+ beginInsertRows(QModelIndex(), row, row + count - 1);
+ QListWidget *view = qobject_cast<QListWidget*>(QObject::parent());
+ QListWidgetItem *itm = 0;
+
+ for (int r = row; r < row + count; ++r) {
+ itm = new QListWidgetItem;
+ itm->view = view;
+ itm->d->theid = r;
+ items.insert(r, itm);
+ }
+
+ endInsertRows();
+ return true;
+}
+
+bool QListModel::removeRows(int row, int count, const QModelIndex &parent)
+{
+ if (count < 1 || row < 0 || (row + count) > rowCount() || parent.isValid())
+ return false;
+
+ beginRemoveRows(QModelIndex(), row, row + count - 1);
+ QListWidgetItem *itm = 0;
+ for (int r = row; r < row + count; ++r) {
+ itm = items.takeAt(row);
+ itm->view = 0;
+ itm->d->theid = -1;
+ delete itm;
+ }
+ endRemoveRows();
+ return true;
+}
+
+Qt::ItemFlags QListModel::flags(const QModelIndex &index) const
+{
+ if (!index.isValid() || index.row() >= items.count() || index.model() != this)
+ return Qt::ItemIsDropEnabled; // we allow drops outside the items
+ return items.at(index.row())->flags();
+}
+
+void QListModel::sort(int column, Qt::SortOrder order)
+{
+ if (column != 0)
+ return;
+
+ emit layoutAboutToBeChanged();
+
+ QVector < QPair<QListWidgetItem*,int> > sorting(items.count());
+ for (int i = 0; i < items.count(); ++i) {
+ QListWidgetItem *item = items.at(i);
+ sorting[i].first = item;
+ sorting[i].second = i;
+ }
+
+ LessThan compare = (order == Qt::AscendingOrder ? &itemLessThan : &itemGreaterThan);
+ qSort(sorting.begin(), sorting.end(), compare);
+ QModelIndexList fromIndexes;
+ QModelIndexList toIndexes;
+ for (int r = 0; r < sorting.count(); ++r) {
+ QListWidgetItem *item = sorting.at(r).first;
+ toIndexes.append(createIndex(r, 0, item));
+ fromIndexes.append(createIndex(sorting.at(r).second, 0, sorting.at(r).first));
+ items[r] = sorting.at(r).first;
+ }
+ changePersistentIndexList(fromIndexes, toIndexes);
+
+ emit layoutChanged();
+}
+
+/**
+ * This function assumes that all items in the model except the items that are between
+ * (inclusive) start and end are sorted.
+ * With these assumptions, this function can ensure that the model is sorted in a
+ * much more efficient way than doing a naive 'sort everything'.
+ * (provided that the range is relatively small compared to the total number of items)
+ */
+void QListModel::ensureSorted(int column, Qt::SortOrder order, int start, int end)
+{
+ if (column != 0)
+ return;
+
+ int count = end - start + 1;
+ QVector < QPair<QListWidgetItem*,int> > sorting(count);
+ for (int i = 0; i < count; ++i) {
+ sorting[i].first = items.at(start + i);
+ sorting[i].second = start + i;
+ }
+
+ LessThan compare = (order == Qt::AscendingOrder ? &itemLessThan : &itemGreaterThan);
+ qSort(sorting.begin(), sorting.end(), compare);
+
+ QModelIndexList oldPersistentIndexes = persistentIndexList();
+ QModelIndexList newPersistentIndexes = oldPersistentIndexes;
+ QList<QListWidgetItem*> tmp = items;
+ QList<QListWidgetItem*>::iterator lit = tmp.begin();
+ bool changed = false;
+ for (int i = 0; i < count; ++i) {
+ int oldRow = sorting.at(i).second;
+ QListWidgetItem *item = tmp.takeAt(oldRow);
+ lit = sortedInsertionIterator(lit, tmp.end(), order, item);
+ int newRow = qMax(lit - tmp.begin(), 0);
+ lit = tmp.insert(lit, item);
+ if (newRow != oldRow) {
+ changed = true;
+ for (int j = i + 1; j < count; ++j) {
+ int otherRow = sorting.at(j).second;
+ if (oldRow < otherRow && newRow >= otherRow)
+ --sorting[j].second;
+ else if (oldRow > otherRow && newRow <= otherRow)
+ ++sorting[j].second;
+ }
+ for (int k = 0; k < newPersistentIndexes.count(); ++k) {
+ QModelIndex pi = newPersistentIndexes.at(k);
+ int oldPersistentRow = pi.row();
+ int newPersistentRow = oldPersistentRow;
+ if (oldPersistentRow == oldRow)
+ newPersistentRow = newRow;
+ else if (oldRow < oldPersistentRow && newRow >= oldPersistentRow)
+ newPersistentRow = oldPersistentRow - 1;
+ else if (oldRow > oldPersistentRow && newRow <= oldPersistentRow)
+ newPersistentRow = oldPersistentRow + 1;
+ if (newPersistentRow != oldPersistentRow)
+ newPersistentIndexes[k] = createIndex(newPersistentRow,
+ pi.column(), pi.internalPointer());
+ }
+ }
+ }
+
+ if (changed) {
+ emit layoutAboutToBeChanged();
+ items = tmp;
+ changePersistentIndexList(oldPersistentIndexes, newPersistentIndexes);
+ emit layoutChanged();
+ }
+}
+
+bool QListModel::itemLessThan(const QPair<QListWidgetItem*,int> &left,
+ const QPair<QListWidgetItem*,int> &right)
+{
+ return (*left.first) < (*right.first);
+}
+
+bool QListModel::itemGreaterThan(const QPair<QListWidgetItem*,int> &left,
+ const QPair<QListWidgetItem*,int> &right)
+{
+ return (*right.first) < (*left.first);
+}
+
+QList<QListWidgetItem*>::iterator QListModel::sortedInsertionIterator(
+ const QList<QListWidgetItem*>::iterator &begin,
+ const QList<QListWidgetItem*>::iterator &end,
+ Qt::SortOrder order, QListWidgetItem *item)
+{
+ if (order == Qt::AscendingOrder)
+ return qLowerBound(begin, end, item, QListModelLessThan());
+ return qLowerBound(begin, end, item, QListModelGreaterThan());
+}
+
+void QListModel::itemChanged(QListWidgetItem *item)
+{
+ QModelIndex idx = index(item);
+ emit dataChanged(idx, idx);
+}
+
+QStringList QListModel::mimeTypes() const
+{
+ const QListWidget *view = qobject_cast<const QListWidget*>(QObject::parent());
+ return view->mimeTypes();
+}
+
+QMimeData *QListModel::internalMimeData() const
+{
+ return QAbstractItemModel::mimeData(cachedIndexes);
+}
+
+QMimeData *QListModel::mimeData(const QModelIndexList &indexes) const
+{
+ QList<QListWidgetItem*> itemlist;
+ for (int i = 0; i < indexes.count(); ++i)
+ itemlist << at(indexes.at(i).row());
+ const QListWidget *view = qobject_cast<const QListWidget*>(QObject::parent());
+
+ cachedIndexes = indexes;
+ QMimeData *mimeData = view->mimeData(itemlist);
+ cachedIndexes.clear();
+ return mimeData;
+}
+
+#ifndef QT_NO_DRAGANDDROP
+bool QListModel::dropMimeData(const QMimeData *data, Qt::DropAction action,
+ int row, int column, const QModelIndex &index)
+{
+ Q_UNUSED(column);
+ QListWidget *view = qobject_cast<QListWidget*>(QObject::parent());
+ if (index.isValid())
+ row = index.row();
+ else if (row == -1)
+ row = items.count();
+
+ return view->dropMimeData(row, data, action);
+}
+
+Qt::DropActions QListModel::supportedDropActions() const
+{
+ const QListWidget *view = qobject_cast<const QListWidget*>(QObject::parent());
+ return view->supportedDropActions();
+}
+#endif // QT_NO_DRAGANDDROP
+
+/*!
+ \class QListWidgetItem
+ \brief The QListWidgetItem class provides an item for use with the
+ QListWidget item view class.
+
+ \ingroup model-view
+
+ QListWidgetItem is used to represent items in a list provided by the
+ QListWidget class. Each item can hold several pieces of information,
+ and will display these appropriately.
+
+ The item view convenience classes use a classic item-based interface
+ rather than a pure model/view approach. For a more flexible list view
+ widget, consider using the QListView class with a standard model.
+
+ List items can be automatically inserted into a list when they are
+ constructed by specifying the list widget:
+
+ \snippet doc/src/snippets/qlistwidget-using/mainwindow.cpp 2
+
+ They can also be created without a parent widget, and later inserted into
+ a list (see \l{QListWidget::insertItem()}).
+
+ List items are typically used to display text() and an icon(). These are
+ set with the setText() and setIcon() functions. The appearance of the text
+ can be customized with setFont(), setForeground(), and setBackground().
+ Text in list items can be aligned using the setTextAlignment() function.
+ Tooltips, status tips and "What's This?" help can be added to list items
+ with setToolTip(), setStatusTip(), and setWhatsThis().
+
+ By default, items are enabled, selectable, checkable, and can be the source
+ of a drag and drop operation.
+ Each item's flags can be changed by calling setFlags() with the appropriate
+ value (see \l{Qt::ItemFlags}). Checkable items can be checked, unchecked and
+ partially checked with the setCheckState() function. The corresponding
+ checkState() function indicates what check state the item currently has.
+
+ The isHidden() function can be used to determine whether the
+ item is hidden. Items can be hidden with setHidden().
+
+ \section1 Subclassing
+
+ When subclassing QListWidgetItem to provide custom items, it is possible to
+ define new types for them so that they can be distinguished from standard
+ items. The constructors for subclasses that require this feature need to
+ call the base class constructor with a new type value equal to or greater
+ than \l UserType.
+
+ \sa QListWidget, {Model/View Programming}, QTreeWidgetItem, QTableWidgetItem
+*/
+
+/*!
+ \enum QListWidgetItem::ItemType
+
+ This enum describes the types that are used to describe list widget items.
+
+ \value Type The default type for list widget items.
+ \value UserType The minimum value for custom types. Values below UserType are
+ reserved by Qt.
+
+ You can define new user types in QListWidgetItem subclasses to ensure that
+ custom items are treated specially.
+
+ \sa type()
+*/
+
+/*!
+ \fn int QListWidgetItem::type() const
+
+ Returns the type passed to the QListWidgetItem constructor.
+*/
+
+/*!
+ \fn QListWidget *QListWidgetItem::listWidget() const
+
+ Returns the list widget that contains the item.
+*/
+
+/*!
+ \fn void QListWidgetItem::setSelected(bool select)
+ \since 4.2
+
+ Sets the selected state of the item to \a select.
+
+ \sa isSelected()
+*/
+
+/*!
+ \fn bool QListWidgetItem::isSelected() const
+ \since 4.2
+
+ Returns true if the item is selected, otherwise returns false.
+
+ \sa setSelected()
+*/
+
+/*!
+ \fn void QListWidgetItem::setHidden(bool hide)
+ \since 4.2
+
+ Hides the item if \a hide is true, otherwise shows the item.
+
+ \sa isHidden()
+*/
+
+/*!
+ \fn bool QListWidgetItem::isHidden() const
+ \since 4.2
+
+ Returns true if the item is hidden, otherwise returns false.
+
+ \sa setHidden()
+*/
+
+/*!
+ \fn QListWidgetItem::QListWidgetItem(QListWidget *parent, int type)
+
+ Constructs an empty list widget item of the specified \a type with the
+ given \a parent.
+ If the parent is not specified, the item will need to be inserted into a
+ list widget with QListWidget::insertItem().
+
+ \sa type()
+*/
+QListWidgetItem::QListWidgetItem(QListWidget *view, int type)
+ : rtti(type), view(view), d(new QListWidgetItemPrivate(this)),
+ itemFlags(Qt::ItemIsSelectable
+ |Qt::ItemIsUserCheckable
+ |Qt::ItemIsEnabled
+ |Qt::ItemIsDragEnabled)
+{
+ if (QListModel *model = (view ? qobject_cast<QListModel*>(view->model()) : 0))
+ model->insert(model->rowCount(), this);
+}
+
+/*!
+ \fn QListWidgetItem::QListWidgetItem(const QString &text, QListWidget *parent, int type)
+
+ Constructs an empty list widget item of the specified \a type with the
+ given \a text and \a parent.
+ If the parent is not specified, the item will need to be inserted into a
+ list widget with QListWidget::insertItem().
+
+ \sa type()
+*/
+QListWidgetItem::QListWidgetItem(const QString &text, QListWidget *view, int type)
+ : rtti(type), view(0), d(new QListWidgetItemPrivate(this)),
+ itemFlags(Qt::ItemIsSelectable
+ |Qt::ItemIsUserCheckable
+ |Qt::ItemIsEnabled
+ |Qt::ItemIsDragEnabled)
+{
+ setData(Qt::DisplayRole, text);
+ this->view = view;
+ if (QListModel *model = (view ? qobject_cast<QListModel*>(view->model()) : 0))
+ model->insert(model->rowCount(), this);
+}
+
+/*!
+ \fn QListWidgetItem::QListWidgetItem(const QIcon &icon, const QString &text, QListWidget *parent, int type)
+
+ Constructs an empty list widget item of the specified \a type with the
+ given \a icon, \a text and \a parent.
+ If the parent is not specified, the item will need to be inserted into a
+ list widget with QListWidget::insertItem().
+
+ \sa type()
+*/
+QListWidgetItem::QListWidgetItem(const QIcon &icon,const QString &text,
+ QListWidget *view, int type)
+ : rtti(type), view(0), d(new QListWidgetItemPrivate(this)),
+ itemFlags(Qt::ItemIsSelectable
+ |Qt::ItemIsUserCheckable
+ |Qt::ItemIsEnabled
+ |Qt::ItemIsDragEnabled)
+{
+ setData(Qt::DisplayRole, text);
+ setData(Qt::DecorationRole, icon);
+ this->view = view;
+ if (QListModel *model = (view ? qobject_cast<QListModel*>(view->model()) : 0))
+ model->insert(model->rowCount(), this);
+}
+
+/*!
+ Destroys the list item.
+*/
+QListWidgetItem::~QListWidgetItem()
+{
+ if (QListModel *model = (view ? qobject_cast<QListModel*>(view->model()) : 0))
+ model->remove(this);
+ delete d;
+}
+
+/*!
+ Creates an exact copy of the item.
+*/
+QListWidgetItem *QListWidgetItem::clone() const
+{
+ return new QListWidgetItem(*this);
+}
+
+/*!
+ This function sets the data for a given \a role to the given \a value (see
+ \l{Qt::ItemDataRole}). Reimplement this function if you need
+ extra roles or special behavior for certain roles.
+
+ \sa Qt::ItemDataRole, data()
+*/
+void QListWidgetItem::setData(int role, const QVariant &value)
+{
+ bool found = false;
+ role = (role == Qt::EditRole ? Qt::DisplayRole : role);
+ for (int i = 0; i < d->values.count(); ++i) {
+ if (d->values.at(i).role == role) {
+ if (d->values.at(i).value == value)
+ return;
+ d->values[i].value = value;
+ found = true;
+ break;
+ }
+ }
+ if (!found)
+ d->values.append(QWidgetItemData(role, value));
+ if (QListModel *model = (view ? qobject_cast<QListModel*>(view->model()) : 0))
+ model->itemChanged(this);
+}
+
+/*!
+ This function returns the item's data for a given \a role (see
+ Qt::ItemDataRole). Reimplement this function if you need
+ extra roles or special behavior for certain roles.
+*/
+QVariant QListWidgetItem::data(int role) const
+{
+ role = (role == Qt::EditRole ? Qt::DisplayRole : role);
+ for (int i = 0; i < d->values.count(); ++i)
+ if (d->values.at(i).role == role)
+ return d->values.at(i).value;
+ return QVariant();
+}
+
+/*!
+ Returns true if this item's text is less then \a other item's text;
+ otherwise returns false.
+*/
+bool QListWidgetItem::operator<(const QListWidgetItem &other) const
+{
+ return text() < other.text();
+}
+
+#ifndef QT_NO_DATASTREAM
+
+/*!
+ Reads the item from stream \a in.
+
+ \sa write()
+*/
+void QListWidgetItem::read(QDataStream &in)
+{
+ in >> d->values;
+}
+
+/*!
+ Writes the item to stream \a out.
+
+ \sa read()
+*/
+void QListWidgetItem::write(QDataStream &out) const
+{
+ out << d->values;
+}
+
+/*!
+ \since 4.1
+
+ Constructs a copy of \a other. Note that type() and listWidget()
+ are not copied.
+
+ This function is useful when reimplementing clone().
+
+ \sa data(), flags()
+*/
+QListWidgetItem::QListWidgetItem(const QListWidgetItem &other)
+ : rtti(Type), view(0),
+ d(new QListWidgetItemPrivate(this)),
+ itemFlags(other.itemFlags)
+{
+ d->values = other.d->values;
+}
+
+/*!
+ Assigns \a other's data and flags to this item. Note that type()
+ and listWidget() are not copied.
+
+ This function is useful when reimplementing clone().
+
+ \sa data(), flags()
+*/
+QListWidgetItem &QListWidgetItem::operator=(const QListWidgetItem &other)
+{
+ d->values = other.d->values;
+ itemFlags = other.itemFlags;
+ return *this;
+}
+
+/*!
+ \relates QListWidgetItem
+
+ Writes the list widget item \a item to stream \a out.
+
+ This operator uses QListWidgetItem::write().
+
+ \sa {Format of the QDataStream Operators}
+*/
+QDataStream &operator<<(QDataStream &out, const QListWidgetItem &item)
+{
+ item.write(out);
+ return out;
+}
+
+/*!
+ \relates QListWidgetItem
+
+ Reads a list widget item from stream \a in into \a item.
+
+ This operator uses QListWidgetItem::read().
+
+ \sa {Format of the QDataStream Operators}
+*/
+QDataStream &operator>>(QDataStream &in, QListWidgetItem &item)
+{
+ item.read(in);
+ return in;
+}
+
+#endif // QT_NO_DATASTREAM
+
+/*!
+ \fn Qt::ItemFlags QListWidgetItem::flags() const
+
+ Returns the item flags for this item (see \l{Qt::ItemFlags}).
+*/
+
+/*!
+ \fn QString QListWidgetItem::text() const
+
+ Returns the list item's text.
+
+ \sa setText()
+*/
+
+/*!
+ \fn QIcon QListWidgetItem::icon() const
+
+ Returns the list item's icon.
+
+ \sa setIcon(), {QAbstractItemView::iconSize}{iconSize}
+*/
+
+/*!
+ \fn QString QListWidgetItem::statusTip() const
+
+ Returns the list item's status tip.
+
+ \sa setStatusTip()
+*/
+
+/*!
+ \fn QString QListWidgetItem::toolTip() const
+
+ Returns the list item's tooltip.
+
+ \sa setToolTip() statusTip() whatsThis()
+*/
+
+/*!
+ \fn QString QListWidgetItem::whatsThis() const
+
+ Returns the list item's "What's This?" help text.
+
+ \sa setWhatsThis() statusTip() toolTip()
+*/
+
+/*!
+ \fn QFont QListWidgetItem::font() const
+
+ Returns the font used to display this list item's text.
+*/
+
+/*!
+ \fn int QListWidgetItem::textAlignment() const
+
+ Returns the text alignment for the list item (see \l{Qt::AlignmentFlag}).
+*/
+
+/*!
+ \fn QColor QListWidgetItem::backgroundColor() const
+ \obsolete
+
+ This function is deprecated. Use background() instead.
+*/
+
+/*!
+ \fn QBrush QListWidgetItem::background() const
+ \since 4.2
+
+ Returns the brush used to display the list item's background.
+
+ \sa setBackground() foreground()
+*/
+
+/*!
+ \fn QColor QListWidgetItem::textColor() const
+ \obsolete
+
+ Returns the color used to display the list item's text.
+
+ This function is deprecated. Use foreground() instead.
+*/
+
+/*!
+ \fn QBrush QListWidgetItem::foreground() const
+ \since 4.2
+
+ Returns the brush used to display the list item's foreground (e.g. text).
+
+ \sa setForeground() background()
+*/
+
+/*!
+ \fn Qt::CheckState QListWidgetItem::checkState() const
+
+ Returns the checked state of the list item (see \l{Qt::CheckState}).
+
+ \sa flags()
+*/
+
+/*!
+ \fn QSize QListWidgetItem::sizeHint() const
+ \since 4.1
+
+ Returns the size hint set for the list item.
+*/
+
+/*!
+ \fn void QListWidgetItem::setSizeHint(const QSize &size)
+ \since 4.1
+
+ Sets the size hint for the list item to be \a size.
+ If no size hint is set, the item delegate will compute the
+ size hint based on the item data.
+*/
+
+/*!
+ \fn void QListWidgetItem::setFlags(Qt::ItemFlags flags)
+
+ Sets the item flags for the list item to \a flags (see
+ \l{Qt::ItemFlags}).
+*/
+void QListWidgetItem::setFlags(Qt::ItemFlags aflags) {
+ itemFlags = aflags;
+ if (QListModel *model = (view ? qobject_cast<QListModel*>(view->model()) : 0))
+ model->itemChanged(this);
+}
+
+
+/*!
+ \fn void QListWidgetItem::setText(const QString &text)
+
+ Sets the text for the list widget item's to the given \a text.
+
+ \sa text()
+*/
+
+/*!
+ \fn void QListWidgetItem::setIcon(const QIcon &icon)
+
+ Sets the icon for the list item to the given \a icon.
+
+ \sa icon(), text(), {QAbstractItemView::iconSize}{iconSize}
+*/
+
+/*!
+ \fn void QListWidgetItem::setStatusTip(const QString &statusTip)
+
+ Sets the status tip for the list item to the text specified by
+ \a statusTip. QListWidget mouse tracking needs to be enabled for this
+ feature to work.
+
+ \sa statusTip() setToolTip() setWhatsThis()
+*/
+
+/*!
+ \fn void QListWidgetItem::setToolTip(const QString &toolTip)
+
+ Sets the tooltip for the list item to the text specified by \a toolTip.
+
+ \sa toolTip() setStatusTip() setWhatsThis()
+*/
+
+/*!
+ \fn void QListWidgetItem::setWhatsThis(const QString &whatsThis)
+
+ Sets the "What's This?" help for the list item to the text specified
+ by \a whatsThis.
+
+ \sa whatsThis() setStatusTip() setToolTip()
+*/
+
+/*!
+ \fn void QListWidgetItem::setFont(const QFont &font)
+
+ Sets the font used when painting the item to the given \a font.
+*/
+
+/*!
+ \fn void QListWidgetItem::setTextAlignment(int alignment)
+
+ Sets the list item's text alignment to \a alignment (see
+ \l{Qt::AlignmentFlag}).
+*/
+
+/*!
+ \fn void QListWidgetItem::setBackgroundColor(const QColor &color)
+ \obsolete
+
+ This function is deprecated. Use setBackground() instead.
+*/
+
+/*!
+ \fn void QListWidgetItem::setBackground(const QBrush &brush)
+ \since 4.2
+
+ Sets the background brush of the list item to the given \a brush.
+
+ \sa background() setForeground()
+*/
+
+/*!
+ \fn void QListWidgetItem::setTextColor(const QColor &color)
+ \obsolete
+
+ This function is deprecated. Use setForeground() instead.
+*/
+
+/*!
+ \fn void QListWidgetItem::setForeground(const QBrush &brush)
+ \since 4.2
+
+ Sets the foreground brush of the list item to the given \a brush.
+
+ \sa foreground() setBackground()
+*/
+
+/*!
+ \fn void QListWidgetItem::setCheckState(Qt::CheckState state)
+
+ Sets the check state of the list item to \a state.
+
+ \sa checkState()
+*/
+
+void QListWidgetPrivate::setup()
+{
+ Q_Q(QListWidget);
+ q->QListView::setModel(new QListModel(q));
+ // view signals
+ QObject::connect(q, SIGNAL(pressed(QModelIndex)), q, SLOT(_q_emitItemPressed(QModelIndex)));
+ QObject::connect(q, SIGNAL(clicked(QModelIndex)), q, SLOT(_q_emitItemClicked(QModelIndex)));
+ QObject::connect(q, SIGNAL(doubleClicked(QModelIndex)),
+ q, SLOT(_q_emitItemDoubleClicked(QModelIndex)));
+ QObject::connect(q, SIGNAL(activated(QModelIndex)),
+ q, SLOT(_q_emitItemActivated(QModelIndex)));
+ QObject::connect(q, SIGNAL(entered(QModelIndex)), q, SLOT(_q_emitItemEntered(QModelIndex)));
+ QObject::connect(model(), SIGNAL(dataChanged(QModelIndex,QModelIndex)),
+ q, SLOT(_q_emitItemChanged(QModelIndex)));
+ QObject::connect(q->selectionModel(), SIGNAL(currentChanged(QModelIndex,QModelIndex)),
+ q, SLOT(_q_emitCurrentItemChanged(QModelIndex,QModelIndex)));
+ QObject::connect(q->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)),
+ q, SIGNAL(itemSelectionChanged()));
+ QObject::connect(model(), SIGNAL(dataChanged(QModelIndex,QModelIndex)),
+ q, SLOT(_q_dataChanged(QModelIndex,QModelIndex)));
+ QObject::connect(model(), SIGNAL(columnsRemoved(QModelIndex,int,int)), q, SLOT(_q_sort()));
+}
+
+void QListWidgetPrivate::_q_emitItemPressed(const QModelIndex &index)
+{
+ Q_Q(QListWidget);
+ emit q->itemPressed(model()->at(index.row()));
+}
+
+void QListWidgetPrivate::_q_emitItemClicked(const QModelIndex &index)
+{
+ Q_Q(QListWidget);
+ emit q->itemClicked(model()->at(index.row()));
+}
+
+void QListWidgetPrivate::_q_emitItemDoubleClicked(const QModelIndex &index)
+{
+ Q_Q(QListWidget);
+ emit q->itemDoubleClicked(model()->at(index.row()));
+}
+
+void QListWidgetPrivate::_q_emitItemActivated(const QModelIndex &index)
+{
+ Q_Q(QListWidget);
+ emit q->itemActivated(model()->at(index.row()));
+}
+
+void QListWidgetPrivate::_q_emitItemEntered(const QModelIndex &index)
+{
+ Q_Q(QListWidget);
+ emit q->itemEntered(model()->at(index.row()));
+}
+
+void QListWidgetPrivate::_q_emitItemChanged(const QModelIndex &index)
+{
+ Q_Q(QListWidget);
+ emit q->itemChanged(model()->at(index.row()));
+}
+
+void QListWidgetPrivate::_q_emitCurrentItemChanged(const QModelIndex &current,
+ const QModelIndex &previous)
+{
+ Q_Q(QListWidget);
+ QPersistentModelIndex persistentCurrent = current;
+ QListWidgetItem *currentItem = model()->at(persistentCurrent.row());
+ emit q->currentItemChanged(currentItem, model()->at(previous.row()));
+
+ //persistentCurrent is invalid if something changed the model in response
+ //to the currentItemChanged signal emission and the item was removed
+ if (!persistentCurrent.isValid()) {
+ currentItem = 0;
+ }
+
+ emit q->currentTextChanged(currentItem ? currentItem->text() : QString());
+ emit q->currentRowChanged(persistentCurrent.row());
+}
+
+void QListWidgetPrivate::_q_sort()
+{
+ if (sortingEnabled)
+ model()->sort(0, sortOrder);
+}
+
+void QListWidgetPrivate::_q_dataChanged(const QModelIndex &topLeft,
+ const QModelIndex &bottomRight)
+{
+ if (sortingEnabled && topLeft.isValid() && bottomRight.isValid())
+ model()->ensureSorted(topLeft.column(), sortOrder,
+ topLeft.row(), bottomRight.row());
+}
+
+/*!
+ \class QListWidget
+ \brief The QListWidget class provides an item-based list widget.
+
+ \ingroup model-view
+ \mainclass
+
+ QListWidget is a convenience class that provides a list view similar to
+ the one supplied by QListView, but with a classic item-based interface
+ for adding and removing items. QListWidget uses an internal model to
+ manage each QListWidgetItem in the list.
+
+ For a more flexible list view widget, use the QListView class with a
+ standard model.
+
+ List widgets are constructed in the same way as other widgets:
+
+ \snippet doc/src/snippets/qlistwidget-using/mainwindow.cpp 0
+
+ The selectionMode() of a list widget determines how many of the items in
+ the list can be selected at the same time, and whether complex selections
+ of items can be created. This can be set with the setSelectionMode()
+ function.
+
+ There are two ways to add items to the list: they can be constructed with
+ the list widget as their parent widget, or they can be constructed with
+ no parent widget and added to the list later. If a list widget already
+ exists when the items are constructed, the first method is easier to use:
+
+ \snippet doc/src/snippets/qlistwidget-using/mainwindow.cpp 1
+
+ If you need to insert a new item into the list at a particular position,
+ it is more required to construct the item without a parent widget and
+ use the insertItem() function to place it within the list. The list
+ widget will take ownership of the item.
+
+ \snippet doc/src/snippets/qlistwidget-using/mainwindow.cpp 6
+ \snippet doc/src/snippets/qlistwidget-using/mainwindow.cpp 7
+
+ For multiple items, insertItems() can be used instead. The number of
+ items in the list is found with the count() function.
+ To remove items from the list, use takeItem().
+
+ The current item in the list can be found with currentItem(), and changed
+ with setCurrentItem(). The user can also change the current item by
+ navigating with the keyboard or clicking on a different item. When the
+ current item changes, the currentItemChanged() signal is emitted with the
+ new current item and the item that was previously current.
+
+ \table 100%
+ \row \o \inlineimage windowsxp-listview.png Screenshot of a Windows XP style list widget
+ \o \inlineimage macintosh-listview.png Screenshot of a Macintosh style table widget
+ \o \inlineimage plastique-listview.png Screenshot of a Plastique style table widget
+ \row \o A \l{Windows XP Style Widget Gallery}{Windows XP style} list widget.
+ \o A \l{Macintosh Style Widget Gallery}{Macintosh style} list widget.
+ \o A \l{Plastique Style Widget Gallery}{Plastique style} list widget.
+ \endtable
+
+ \sa QListWidgetItem, QListView, QTreeView, {Model/View Programming},
+ {Config Dialog Example}
+*/
+
+/*!
+ \fn void QListWidget::addItem(QListWidgetItem *item)
+
+ Inserts the \a item at the the end of the list widget.
+
+ \warning A QListWidgetItem can only be added to a
+ QListWidget once. Adding the same QListWidgetItem multiple
+ times to a QListWidget will result in undefined behavior.
+
+ \sa insertItem()
+*/
+
+/*!
+ \fn void QListWidget::addItem(const QString &label)
+
+ Inserts an item with the text \a label at the end of the list
+ widget.
+*/
+
+/*!
+ \fn void QListWidget::addItems(const QStringList &labels)
+
+ Inserts items with the text \a labels at the end of the list widget.
+
+ \sa insertItems()
+*/
+
+/*!
+ \fn void QListWidget::itemPressed(QListWidgetItem *item)
+
+ This signal is emitted with the specified \a item when a mouse button is pressed
+ on an item in the widget.
+
+ \sa itemClicked(), itemDoubleClicked()
+*/
+
+/*!
+ \fn void QListWidget::itemClicked(QListWidgetItem *item)
+
+ This signal is emitted with the specified \a item when a mouse button is clicked
+ on an item in the widget.
+
+ \sa itemPressed(), itemDoubleClicked()
+*/
+
+/*!
+ \fn void QListWidget::itemDoubleClicked(QListWidgetItem *item)
+
+ This signal is emitted with the specified \a item when a mouse button is double
+ clicked on an item in the widget.
+
+ \sa itemClicked(), itemPressed()
+*/
+
+/*!
+ \fn void QListWidget::itemActivated(QListWidgetItem *item)
+
+ This signal is emitted when the \a item is activated. The \a item
+ is activated when the user clicks or double clicks on it,
+ depending on the system configuration. It is also activated when
+ the user presses the activation key (on Windows and X11 this is
+ the \gui Return key, on Mac OS X it is \key{Ctrl+0}).
+*/
+
+/*!
+ \fn void QListWidget::itemEntered(QListWidgetItem *item)
+
+ This signal is emitted when the mouse cursor enters an item. The
+ \a item is the item entered. This signal is only emitted when
+ mouseTracking is turned on, or when a mouse button is pressed
+ while moving into an item.
+*/
+
+/*!
+ \fn void QListWidget::itemChanged(QListWidgetItem *item)
+
+ This signal is emitted whenever the data of \a item has changed.
+*/
+
+/*!
+ \fn void QListWidget::currentItemChanged(QListWidgetItem *current, QListWidgetItem *previous)
+
+ This signal is emitted whenever the current item changes. The \a
+ previous item is the item that previously had the focus, \a
+ current is the new current item.
+*/
+
+/*!
+ \fn void QListWidget::currentTextChanged(const QString &currentText)
+
+ This signal is emitted whenever the current item changes. The \a currentText
+ is the text data in the current item. If there is no current item, the \a currentText
+ is invalid.
+*/
+
+/*!
+ \fn void QListWidget::currentRowChanged(int currentRow)
+
+ This signal is emitted whenever the current item changes. The \a currentRow
+ is the row of the current item. If there is no current item, the \a currentRow is -1.
+*/
+
+/*!
+ \fn void QListWidget::itemSelectionChanged()
+
+ This signal is emitted whenever the selection changes.
+
+ \sa selectedItems() isItemSelected() currentItemChanged()
+*/
+
+/*!
+ \since 4.3
+
+ \fn void QListWidget::removeItemWidget(QListWidgetItem *item)
+
+ Removes the widget set on the given \a item.
+*/
+
+/*!
+ Constructs an empty QListWidget with the given \a parent.
+*/
+
+QListWidget::QListWidget(QWidget *parent)
+ : QListView(*new QListWidgetPrivate(), parent)
+{
+ Q_D(QListWidget);
+ d->setup();
+}
+
+/*!
+ Destroys the list widget and all its items.
+*/
+
+QListWidget::~QListWidget()
+{
+}
+
+/*!
+ Returns the item that occupies the given \a row in the list if one has been
+ set; otherwise returns 0.
+
+ \sa row()
+*/
+
+QListWidgetItem *QListWidget::item(int row) const
+{
+ Q_D(const QListWidget);
+ if (row < 0 || row >= d->model()->rowCount())
+ return 0;
+ return d->model()->at(row);
+}
+
+/*!
+ Returns the row containing the given \a item.
+
+ \sa item()
+*/
+
+int QListWidget::row(const QListWidgetItem *item) const
+{
+ Q_D(const QListWidget);
+ return d->model()->index(const_cast<QListWidgetItem*>(item)).row();
+}
+
+
+/*!
+ Inserts the \a item at the position in the list given by \a row.
+
+ \sa addItem()
+*/
+
+void QListWidget::insertItem(int row, QListWidgetItem *item)
+{
+ Q_D(QListWidget);
+ if (item && !item->view)
+ d->model()->insert(row, item);
+}
+
+/*!
+ Inserts an item with the text \a label in the list widget at the
+ position given by \a row.
+
+ \sa addItem()
+*/
+
+void QListWidget::insertItem(int row, const QString &label)
+{
+ Q_D(QListWidget);
+ d->model()->insert(row, new QListWidgetItem(label));
+}
+
+/*!
+ Inserts items from the list of \a labels into the list, starting at the
+ given \a row.
+
+ \sa insertItem(), addItem()
+*/
+
+void QListWidget::insertItems(int row, const QStringList &labels)
+{
+ Q_D(QListWidget);
+ d->model()->insert(row, labels);
+}
+
+/*!
+ Removes and returns the item from the given \a row in the list widget; otherwise
+ returns 0.
+
+ Items removed from a list widget will not be managed by Qt, and will need to be
+ deleted manually.
+
+ \sa insertItem(), addItem()
+*/
+
+QListWidgetItem *QListWidget::takeItem(int row)
+{
+ Q_D(QListWidget);
+ if (row < 0 || row >= d->model()->rowCount())
+ return 0;
+ return d->model()->take(row);
+}
+
+/*!
+ \property QListWidget::count
+ \brief the number of items in the list including any hidden items.
+*/
+
+int QListWidget::count() const
+{
+ Q_D(const QListWidget);
+ return d->model()->rowCount();
+}
+
+/*!
+ Returns the current item.
+*/
+QListWidgetItem *QListWidget::currentItem() const
+{
+ Q_D(const QListWidget);
+ return d->model()->at(currentIndex().row());
+}
+
+
+/*!
+ Sets the current item to \a item.
+
+ Depending on the current selection mode, the item may also be selected.
+*/
+void QListWidget::setCurrentItem(QListWidgetItem *item)
+{
+ setCurrentRow(row(item));
+}
+
+/*!
+ \since 4.4
+ Set the current item to \a item, using the given \a command.
+*/
+void QListWidget::setCurrentItem(QListWidgetItem *item, QItemSelectionModel::SelectionFlags command)
+{
+ setCurrentRow(row(item), command);
+}
+
+/*!
+ \property QListWidget::currentRow
+ \brief the row of the current item.
+
+ Depending on the current selection mode, the row may also be selected.
+*/
+
+int QListWidget::currentRow() const
+{
+ return currentIndex().row();
+}
+
+void QListWidget::setCurrentRow(int row)
+{
+ Q_D(QListWidget);
+ QModelIndex index = d->model()->index(row);
+ if (d->selectionMode == SingleSelection)
+ selectionModel()->setCurrentIndex(index, QItemSelectionModel::ClearAndSelect);
+ else if (d->selectionMode == NoSelection)
+ selectionModel()->setCurrentIndex(index, QItemSelectionModel::NoUpdate);
+ else
+ selectionModel()->setCurrentIndex(index, QItemSelectionModel::SelectCurrent);
+}
+
+/*!
+ \since 4.4
+
+ Sets the current row to be the given \a row, using the given \a command,
+*/
+void QListWidget::setCurrentRow(int row, QItemSelectionModel::SelectionFlags command)
+{
+ Q_D(QListWidget);
+ d->selectionModel->setCurrentIndex(d->model()->index(row), command);
+}
+
+/*!
+ Returns a pointer to the item at the coordinates \a p.
+*/
+QListWidgetItem *QListWidget::itemAt(const QPoint &p) const
+{
+ Q_D(const QListWidget);
+ return d->model()->at(indexAt(p).row());
+
+}
+
+/*!
+ \fn QListWidgetItem *QListWidget::itemAt(int x, int y) const
+ \overload
+
+ Returns a pointer to the item at the coordinates (\a x, \a y).
+*/
+
+
+/*!
+ Returns the rectangle on the viewport occupied by the item at \a item.
+*/
+QRect QListWidget::visualItemRect(const QListWidgetItem *item) const
+{
+ Q_D(const QListWidget);
+ QModelIndex index = d->model()->index(const_cast<QListWidgetItem*>(item));
+ return visualRect(index);
+}
+
+/*!
+ Sorts all the items in the list widget according to the specified \a order.
+*/
+void QListWidget::sortItems(Qt::SortOrder order)
+{
+ Q_D(QListWidget);
+ d->sortOrder = order;
+ d->model()->sort(0, order);
+}
+
+/*!
+ \since 4.2
+ \property QListWidget::sortingEnabled
+ \brief whether sorting is enabled
+
+ If this property is true, sorting is enabled for the list; if the
+ property is false, sorting is not enabled. The default value is false.
+*/
+void QListWidget::setSortingEnabled(bool enable)
+{
+ Q_D(QListWidget);
+ d->sortingEnabled = enable;
+}
+
+bool QListWidget::isSortingEnabled() const
+{
+ Q_D(const QListWidget);
+ return d->sortingEnabled;
+}
+
+/*!
+ \internal
+*/
+Qt::SortOrder QListWidget::sortOrder() const
+{
+ Q_D(const QListWidget);
+ return d->sortOrder;
+}
+
+/*!
+ Starts editing the \a item if it is editable.
+*/
+
+void QListWidget::editItem(QListWidgetItem *item)
+{
+ Q_D(QListWidget);
+ edit(d->model()->index(item));
+}
+
+/*!
+ Opens an editor for the given \a item. The editor remains open after editing.
+
+ \sa closePersistentEditor()
+*/
+void QListWidget::openPersistentEditor(QListWidgetItem *item)
+{
+ Q_D(QListWidget);
+ QModelIndex index = d->model()->index(item);
+ QAbstractItemView::openPersistentEditor(index);
+}
+
+/*!
+ Closes the persistent editor for the given \a item.
+
+ \sa openPersistentEditor()
+*/
+void QListWidget::closePersistentEditor(QListWidgetItem *item)
+{
+ Q_D(QListWidget);
+ QModelIndex index = d->model()->index(item);
+ QAbstractItemView::closePersistentEditor(index);
+}
+
+/*!
+ \since 4.1
+
+ Returns the widget displayed in the given \a item.
+*/
+QWidget *QListWidget::itemWidget(QListWidgetItem *item) const
+{
+ Q_D(const QListWidget);
+ QModelIndex index = d->model()->index(item);
+ return QAbstractItemView::indexWidget(index);
+}
+
+/*!
+ \since 4.1
+
+ Sets the \a widget to be displayed in the give \a item.
+
+ This function should only be used to display static content in the place of a list
+ widget item. If you want to display custom dynamic content or implement a custom
+ editor widget, use QListView and subclass QItemDelegate instead.
+
+ \sa {Delegate Classes}
+*/
+void QListWidget::setItemWidget(QListWidgetItem *item, QWidget *widget)
+{
+ Q_D(QListWidget);
+ QModelIndex index = d->model()->index(item);
+ QAbstractItemView::setIndexWidget(index, widget);
+}
+
+/*!
+ Returns true if \a item is selected; otherwise returns false.
+
+ \obsolete
+
+ This function is deprecated. Use \l{QListWidgetItem::isSelected()} instead.
+*/
+bool QListWidget::isItemSelected(const QListWidgetItem *item) const
+{
+ Q_D(const QListWidget);
+ QModelIndex index = d->model()->index(const_cast<QListWidgetItem*>(item));
+ return selectionModel()->isSelected(index);
+}
+
+/*!
+ Selects or deselects the given \a item depending on whether \a select is
+ true of false.
+
+ \obsolete
+
+ This function is deprecated. Use \l{QListWidgetItem::setSelected()} instead.
+*/
+void QListWidget::setItemSelected(const QListWidgetItem *item, bool select)
+{
+ Q_D(QListWidget);
+ QModelIndex index = d->model()->index(const_cast<QListWidgetItem*>(item));
+
+ if (d->selectionMode == SingleSelection) {
+ selectionModel()->select(index, select
+ ? QItemSelectionModel::ClearAndSelect
+ : QItemSelectionModel::Deselect);
+ } else if (d->selectionMode != NoSelection) {
+ selectionModel()->select(index, select
+ ? QItemSelectionModel::Select
+ : QItemSelectionModel::Deselect);
+ }
+
+}
+
+/*!
+ Returns a list of all selected items in the list widget.
+*/
+
+QList<QListWidgetItem*> QListWidget::selectedItems() const
+{
+ Q_D(const QListWidget);
+ QModelIndexList indexes = selectionModel()->selectedIndexes();
+ QList<QListWidgetItem*> items;
+ for (int i = 0; i < indexes.count(); ++i)
+ items.append(d->model()->at(indexes.at(i).row()));
+ return items;
+}
+
+/*!
+ Finds items with the text that matches the string \a text using the given \a flags.
+*/
+
+QList<QListWidgetItem*> QListWidget::findItems(const QString &text, Qt::MatchFlags flags) const
+{
+ Q_D(const QListWidget);
+ QModelIndexList indexes = d->model()->match(model()->index(0, 0, QModelIndex()),
+ Qt::DisplayRole, text, -1, flags);
+ QList<QListWidgetItem*> items;
+ for (int i = 0; i < indexes.size(); ++i)
+ items.append(d->model()->at(indexes.at(i).row()));
+ return items;
+}
+
+/*!
+ Returns true if the \a item is explicitly hidden; otherwise returns false.
+
+ \obsolete
+
+ This function is deprecated. Use \l{QListWidgetItem::isHidden()} instead.
+*/
+bool QListWidget::isItemHidden(const QListWidgetItem *item) const
+{
+ return isRowHidden(row(item));
+}
+
+/*!
+ If \a hide is true, the \a item will be hidden; otherwise it will be shown.
+
+ \obsolete
+
+ This function is deprecated. Use \l{QListWidgetItem::setHidden()} instead.
+*/
+void QListWidget::setItemHidden(const QListWidgetItem *item, bool hide)
+{
+ setRowHidden(row(item), hide);
+}
+
+/*!
+ Scrolls the view if necessary to ensure that the \a item is
+ visible. The \a hint parameter specifies more precisely where the
+ \a item should be located after the operation.
+*/
+
+void QListWidget::scrollToItem(const QListWidgetItem *item, QAbstractItemView::ScrollHint hint)
+{
+ Q_D(QListWidget);
+ QModelIndex index = d->model()->index(const_cast<QListWidgetItem*>(item));
+ QListView::scrollTo(index, hint);
+}
+
+/*!
+ Removes all items and selections in the view.
+
+ \note All items will be permanently deleted.
+*/
+void QListWidget::clear()
+{
+ Q_D(QListWidget);
+ selectionModel()->clear();
+ d->model()->clear();
+}
+
+/*!
+ Returns a list of MIME types that can be used to describe a list of
+ listwidget items.
+
+ \sa mimeData()
+*/
+QStringList QListWidget::mimeTypes() const
+{
+ return d_func()->model()->QAbstractListModel::mimeTypes();
+}
+
+/*!
+ Returns an object that contains a serialized description of the specified
+ \a items. The format used to describe the items is obtained from the
+ mimeTypes() function.
+
+ If the list of items is empty, 0 is returned rather than a serialized
+ empty list.
+*/
+QMimeData *QListWidget::mimeData(const QList<QListWidgetItem*>) const
+{
+ return d_func()->model()->internalMimeData();
+}
+
+#ifndef QT_NO_DRAGANDDROP
+/*!
+ Handles the \a data supplied by an external drag and drop operation
+ that ended with the given \a action in the given \a index.
+ Returns true if the data and action can be handled by the model;
+ otherwise returns false.
+
+ \sa supportedDropActions()
+*/
+bool QListWidget::dropMimeData(int index, const QMimeData *data, Qt::DropAction action)
+{
+ QModelIndex idx;
+ int row = index;
+ int column = 0;
+ if (dropIndicatorPosition() == QAbstractItemView::OnItem) {
+ // QAbstractListModel::dropMimeData will overwrite on the index if row == -1 and column == -1
+ idx = model()->index(row, column);
+ row = -1;
+ column = -1;
+ }
+ return d_func()->model()->QAbstractListModel::dropMimeData(data, action , row, column, idx);
+}
+
+/*! \reimp */
+void QListWidget::dropEvent(QDropEvent *event) {
+ Q_D(QListWidget);
+ if (event->source() == this && d->movement != Static) {
+ QListView::dropEvent(event);
+ return;
+ }
+
+ if (event->source() == this && (event->dropAction() == Qt::MoveAction ||
+ dragDropMode() == QAbstractItemView::InternalMove)) {
+ QModelIndex topIndex;
+ int col = -1;
+ int row = -1;
+ if (d->dropOn(event, &row, &col, &topIndex)) {
+ QList<QModelIndex> selIndexes = selectedIndexes();
+ QList<QPersistentModelIndex> persIndexes;
+ for (int i = 0; i < selIndexes.count(); i++)
+ persIndexes.append(selIndexes.at(i));
+
+ if (persIndexes.contains(topIndex))
+ return;
+
+ QPersistentModelIndex dropRow = model()->index(row, col, topIndex);
+
+ QList<QListWidgetItem *> taken;
+ for (int i = 0; i < persIndexes.count(); ++i)
+ taken.append(takeItem(persIndexes.at(i).row()));
+
+ // insert them back in at their new positions
+ for (int i = 0; i < persIndexes.count(); ++i) {
+ // Either at a specific point or appended
+ if (row == -1) {
+ insertItem(count(), taken.takeFirst());
+ } else {
+ int r = dropRow.row() >= 0 ? dropRow.row() : row;
+ insertItem(qMin(r, count()), taken.takeFirst());
+ }
+ }
+
+ event->accept();
+ // Don't want QAbstractItemView to delete it because it was "moved" we already did it
+ event->setDropAction(Qt::CopyAction);
+ }
+ }
+
+ QListView::dropEvent(event);
+}
+
+/*!
+ Returns the drop actions supported by this view.
+
+ \sa Qt::DropActions
+*/
+Qt::DropActions QListWidget::supportedDropActions() const
+{
+ Q_D(const QListWidget);
+ return d->model()->QAbstractListModel::supportedDropActions() | Qt::MoveAction;
+}
+#endif // QT_NO_DRAGANDDROP
+
+/*!
+ Returns a list of pointers to the items contained in the \a data object.
+ If the object was not created by a QListWidget in the same process, the list
+ is empty.
+*/
+QList<QListWidgetItem*> QListWidget::items(const QMimeData *data) const
+{
+ const QListWidgetMimeData *lwd = qobject_cast<const QListWidgetMimeData*>(data);
+ if (lwd)
+ return lwd->items;
+ return QList<QListWidgetItem*>();
+}
+
+/*!
+ Returns the QModelIndex assocated with the given \a item.
+*/
+
+QModelIndex QListWidget::indexFromItem(QListWidgetItem *item) const
+{
+ Q_D(const QListWidget);
+ return d->model()->index(item);
+}
+
+/*!
+ Returns a pointer to the QListWidgetItem assocated with the given \a index.
+*/
+
+QListWidgetItem *QListWidget::itemFromIndex(const QModelIndex &index) const
+{
+ Q_D(const QListWidget);
+ if (d->isIndexValid(index))
+ return d->model()->at(index.row());
+ return 0;
+}
+
+/*!
+ \internal
+*/
+void QListWidget::setModel(QAbstractItemModel * /*model*/)
+{
+ Q_ASSERT(!"QListWidget::setModel() - Changing the model of the QListWidget is not allowed.");
+}
+
+/*!
+ \reimp
+*/
+bool QListWidget::event(QEvent *e)
+{
+ return QListView::event(e);
+}
+
+QT_END_NAMESPACE
+
+#include "moc_qlistwidget.cpp"
+
+#endif // QT_NO_LISTWIDGET
diff --git a/src/gui/itemviews/qlistwidget.h b/src/gui/itemviews/qlistwidget.h
new file mode 100644
index 0000000000..c13405d237
--- /dev/null
+++ b/src/gui/itemviews/qlistwidget.h
@@ -0,0 +1,335 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QLISTWIDGET_H
+#define QLISTWIDGET_H
+
+#include <QtGui/qlistview.h>
+#include <QtCore/qvariant.h>
+#include <QtCore/qvector.h>
+#include <QtGui/qitemselectionmodel.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+#ifndef QT_NO_LISTWIDGET
+
+class QListWidget;
+class QListModel;
+class QWidgetItemData;
+class QListWidgetItemPrivate;
+
+class Q_GUI_EXPORT QListWidgetItem
+{
+ friend class QListModel;
+ friend class QListWidget;
+public:
+ enum ItemType { Type = 0, UserType = 1000 };
+ explicit QListWidgetItem(QListWidget *view = 0, int type = Type);
+ explicit QListWidgetItem(const QString &text, QListWidget *view = 0, int type = Type);
+ explicit QListWidgetItem(const QIcon &icon, const QString &text,
+ QListWidget *view = 0, int type = Type);
+ QListWidgetItem(const QListWidgetItem &other);
+ virtual ~QListWidgetItem();
+
+ virtual QListWidgetItem *clone() const;
+
+ inline QListWidget *listWidget() const { return view; }
+
+ inline void setSelected(bool select);
+ inline bool isSelected() const;
+
+ inline void setHidden(bool hide);
+ inline bool isHidden() const;
+
+ inline Qt::ItemFlags flags() const { return itemFlags; }
+ void setFlags(Qt::ItemFlags flags);
+
+ inline QString text() const
+ { return data(Qt::DisplayRole).toString(); }
+ inline void setText(const QString &text);
+
+ inline QIcon icon() const
+ { return qvariant_cast<QIcon>(data(Qt::DecorationRole)); }
+ inline void setIcon(const QIcon &icon);
+
+ inline QString statusTip() const
+ { return data(Qt::StatusTipRole).toString(); }
+ inline void setStatusTip(const QString &statusTip);
+
+#ifndef QT_NO_TOOLTIP
+ inline QString toolTip() const
+ { return data(Qt::ToolTipRole).toString(); }
+ inline void setToolTip(const QString &toolTip);
+#endif
+
+#ifndef QT_NO_WHATSTHIS
+ inline QString whatsThis() const
+ { return data(Qt::WhatsThisRole).toString(); }
+ inline void setWhatsThis(const QString &whatsThis);
+#endif
+
+ inline QFont font() const
+ { return qvariant_cast<QFont>(data(Qt::FontRole)); }
+ inline void setFont(const QFont &font);
+
+ inline int textAlignment() const
+ { return data(Qt::TextAlignmentRole).toInt(); }
+ inline void setTextAlignment(int alignment)
+ { setData(Qt::TextAlignmentRole, alignment); }
+
+ inline QColor backgroundColor() const
+ { return qvariant_cast<QColor>(data(Qt::BackgroundColorRole)); }
+ virtual void setBackgroundColor(const QColor &color)
+ { setData(Qt::BackgroundColorRole, color); }
+
+ inline QBrush background() const
+ { return qvariant_cast<QBrush>(data(Qt::BackgroundRole)); }
+ inline void setBackground(const QBrush &brush)
+ { setData(Qt::BackgroundRole, brush); }
+
+ inline QColor textColor() const
+ { return qvariant_cast<QColor>(data(Qt::TextColorRole)); }
+ inline void setTextColor(const QColor &color)
+ { setData(Qt::TextColorRole, color); }
+
+ inline QBrush foreground() const
+ { return qvariant_cast<QBrush>(data(Qt::ForegroundRole)); }
+ inline void setForeground(const QBrush &brush)
+ { setData(Qt::ForegroundRole, brush); }
+
+ inline Qt::CheckState checkState() const
+ { return static_cast<Qt::CheckState>(data(Qt::CheckStateRole).toInt()); }
+ inline void setCheckState(Qt::CheckState state)
+ { setData(Qt::CheckStateRole, static_cast<int>(state)); }
+
+ inline QSize sizeHint() const
+ { return qvariant_cast<QSize>(data(Qt::SizeHintRole)); }
+ inline void setSizeHint(const QSize &size)
+ { setData(Qt::SizeHintRole, size); }
+
+ virtual QVariant data(int role) const;
+ virtual void setData(int role, const QVariant &value);
+
+ virtual bool operator<(const QListWidgetItem &other) const;
+
+#ifndef QT_NO_DATASTREAM
+ virtual void read(QDataStream &in);
+ virtual void write(QDataStream &out) const;
+#endif
+ QListWidgetItem &operator=(const QListWidgetItem &other);
+
+ inline int type() const { return rtti; }
+
+private:
+ int rtti;
+ QVector<void *> dummy;
+ QListWidget *view;
+ QListWidgetItemPrivate *d;
+ Qt::ItemFlags itemFlags;
+};
+
+inline void QListWidgetItem::setText(const QString &atext)
+{ setData(Qt::DisplayRole, atext); }
+
+inline void QListWidgetItem::setIcon(const QIcon &aicon)
+{ setData(Qt::DecorationRole, aicon); }
+
+inline void QListWidgetItem::setStatusTip(const QString &astatusTip)
+{ setData(Qt::StatusTipRole, astatusTip); }
+
+#ifndef QT_NO_TOOLTIP
+inline void QListWidgetItem::setToolTip(const QString &atoolTip)
+{ setData(Qt::ToolTipRole, atoolTip); }
+#endif
+
+#ifndef QT_NO_WHATSTHIS
+inline void QListWidgetItem::setWhatsThis(const QString &awhatsThis)
+{ setData(Qt::WhatsThisRole, awhatsThis); }
+#endif
+
+inline void QListWidgetItem::setFont(const QFont &afont)
+{ setData(Qt::FontRole, afont); }
+
+#ifndef QT_NO_DATASTREAM
+Q_GUI_EXPORT QDataStream &operator<<(QDataStream &out, const QListWidgetItem &item);
+Q_GUI_EXPORT QDataStream &operator>>(QDataStream &in, QListWidgetItem &item);
+#endif
+
+class QListWidgetPrivate;
+
+class Q_GUI_EXPORT QListWidget : public QListView
+{
+ Q_OBJECT
+ Q_PROPERTY(int count READ count)
+ Q_PROPERTY(int currentRow READ currentRow WRITE setCurrentRow NOTIFY currentRowChanged USER true)
+ Q_PROPERTY(bool sortingEnabled READ isSortingEnabled WRITE setSortingEnabled)
+
+ friend class QListWidgetItem;
+ friend class QListModel;
+public:
+ explicit QListWidget(QWidget *parent = 0);
+ ~QListWidget();
+
+ QListWidgetItem *item(int row) const;
+ int row(const QListWidgetItem *item) const;
+ void insertItem(int row, QListWidgetItem *item);
+ void insertItem(int row, const QString &label);
+ void insertItems(int row, const QStringList &labels);
+ inline void addItem(const QString &label) { insertItem(count(), label); }
+ inline void addItem(QListWidgetItem *item);
+ inline void addItems(const QStringList &labels) { insertItems(count(), labels); }
+ QListWidgetItem *takeItem(int row);
+ int count() const;
+
+ QListWidgetItem *currentItem() const;
+ void setCurrentItem(QListWidgetItem *item);
+ void setCurrentItem(QListWidgetItem *item, QItemSelectionModel::SelectionFlags command);
+
+ int currentRow() const;
+ void setCurrentRow(int row);
+ void setCurrentRow(int row, QItemSelectionModel::SelectionFlags command);
+
+ QListWidgetItem *itemAt(const QPoint &p) const;
+ inline QListWidgetItem *itemAt(int x, int y) const;
+ QRect visualItemRect(const QListWidgetItem *item) const;
+
+ void sortItems(Qt::SortOrder order = Qt::AscendingOrder);
+ void setSortingEnabled(bool enable);
+ bool isSortingEnabled() const;
+
+ void editItem(QListWidgetItem *item);
+ void openPersistentEditor(QListWidgetItem *item);
+ void closePersistentEditor(QListWidgetItem *item);
+
+ QWidget *itemWidget(QListWidgetItem *item) const;
+ void setItemWidget(QListWidgetItem *item, QWidget *widget);
+ inline void removeItemWidget(QListWidgetItem *item);
+
+ bool isItemSelected(const QListWidgetItem *item) const;
+ void setItemSelected(const QListWidgetItem *item, bool select);
+ QList<QListWidgetItem*> selectedItems() const;
+ QList<QListWidgetItem*> findItems(const QString &text, Qt::MatchFlags flags) const;
+
+ bool isItemHidden(const QListWidgetItem *item) const;
+ void setItemHidden(const QListWidgetItem *item, bool hide);
+ void dropEvent(QDropEvent *event);
+
+public Q_SLOTS:
+ void scrollToItem(const QListWidgetItem *item, QAbstractItemView::ScrollHint hint = EnsureVisible);
+ void clear();
+
+Q_SIGNALS:
+ void itemPressed(QListWidgetItem *item);
+ void itemClicked(QListWidgetItem *item);
+ void itemDoubleClicked(QListWidgetItem *item);
+ void itemActivated(QListWidgetItem *item);
+ void itemEntered(QListWidgetItem *item);
+ void itemChanged(QListWidgetItem *item);
+
+ void currentItemChanged(QListWidgetItem *current, QListWidgetItem *previous);
+ void currentTextChanged(const QString &currentText);
+ void currentRowChanged(int currentRow);
+
+ void itemSelectionChanged();
+
+protected:
+ bool event(QEvent *e);
+ virtual QStringList mimeTypes() const;
+ virtual QMimeData *mimeData(const QList<QListWidgetItem*> items) const;
+#ifndef QT_NO_DRAGANDDROP
+ virtual bool dropMimeData(int index, const QMimeData *data, Qt::DropAction action);
+ virtual Qt::DropActions supportedDropActions() const;
+#endif
+ QList<QListWidgetItem*> items(const QMimeData *data) const;
+
+ QModelIndex indexFromItem(QListWidgetItem *item) const;
+ QListWidgetItem *itemFromIndex(const QModelIndex &index) const;
+
+private:
+ void setModel(QAbstractItemModel *model);
+ Qt::SortOrder sortOrder() const;
+
+ Q_DECLARE_PRIVATE(QListWidget)
+ Q_DISABLE_COPY(QListWidget)
+
+ Q_PRIVATE_SLOT(d_func(), void _q_emitItemPressed(const QModelIndex &index))
+ Q_PRIVATE_SLOT(d_func(), void _q_emitItemClicked(const QModelIndex &index))
+ Q_PRIVATE_SLOT(d_func(), void _q_emitItemDoubleClicked(const QModelIndex &index))
+ Q_PRIVATE_SLOT(d_func(), void _q_emitItemActivated(const QModelIndex &index))
+ Q_PRIVATE_SLOT(d_func(), void _q_emitItemEntered(const QModelIndex &index))
+ Q_PRIVATE_SLOT(d_func(), void _q_emitItemChanged(const QModelIndex &index))
+ Q_PRIVATE_SLOT(d_func(), void _q_emitCurrentItemChanged(const QModelIndex &previous, const QModelIndex &current))
+ Q_PRIVATE_SLOT(d_func(), void _q_sort())
+ Q_PRIVATE_SLOT(d_func(), void _q_dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight))
+};
+
+inline void QListWidget::removeItemWidget(QListWidgetItem *aItem)
+{ setItemWidget(aItem, 0); }
+
+inline void QListWidget::addItem(QListWidgetItem *aitem)
+{ insertItem(count(), aitem); }
+
+inline QListWidgetItem *QListWidget::itemAt(int ax, int ay) const
+{ return itemAt(QPoint(ax, ay)); }
+
+inline void QListWidgetItem::setSelected(bool aselect)
+{ if (view) view->setItemSelected(this, aselect); }
+
+inline bool QListWidgetItem::isSelected() const
+{ return (view ? view->isItemSelected(this) : false); }
+
+inline void QListWidgetItem::setHidden(bool ahide)
+{ if (view) view->setItemHidden(this, ahide); }
+
+inline bool QListWidgetItem::isHidden() const
+{ return (view ? view->isItemHidden(this) : false); }
+
+#endif // QT_NO_LISTWIDGET
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QLISTWIDGET_H
diff --git a/src/gui/itemviews/qlistwidget_p.h b/src/gui/itemviews/qlistwidget_p.h
new file mode 100644
index 0000000000..855604700f
--- /dev/null
+++ b/src/gui/itemviews/qlistwidget_p.h
@@ -0,0 +1,175 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QLISTWIDGET_P_H
+#define QLISTWIDGET_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. This header file may change
+// from version to version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtCore/qabstractitemmodel.h>
+#include <QtGui/qabstractitemview.h>
+#include <QtGui/qlistwidget.h>
+#include <qitemdelegate.h>
+#include <private/qlistview_p.h>
+#include <private/qwidgetitemdata_p.h>
+
+#ifndef QT_NO_LISTWIDGET
+
+QT_BEGIN_NAMESPACE
+
+class QListModelLessThan
+{
+public:
+ inline bool operator()(QListWidgetItem *i1, QListWidgetItem *i2) const
+ { return *i1 < *i2; }
+};
+
+class QListModelGreaterThan
+{
+public:
+ inline bool operator()(QListWidgetItem *i1, QListWidgetItem *i2) const
+ { return *i2 < *i1; }
+};
+
+class QListModel : public QAbstractListModel
+{
+ Q_OBJECT
+public:
+ QListModel(QListWidget *parent);
+ ~QListModel();
+
+ void clear();
+ QListWidgetItem *at(int row) const;
+ void insert(int row, QListWidgetItem *item);
+ void insert(int row, const QStringList &items);
+ void remove(QListWidgetItem *item);
+ QListWidgetItem *take(int row);
+
+ int rowCount(const QModelIndex &parent = QModelIndex()) const;
+
+ QModelIndex index(QListWidgetItem *item) const;
+ QModelIndex index(int row, int column = 0, const QModelIndex &parent = QModelIndex()) const;
+
+ QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
+ bool setData(const QModelIndex &index, const QVariant &value, int role);
+
+ QMap<int, QVariant> itemData(const QModelIndex &index) const;
+
+ bool insertRows(int row, int count = 1, const QModelIndex &parent = QModelIndex());
+ bool removeRows(int row, int count = 1, const QModelIndex &parent = QModelIndex());
+
+ Qt::ItemFlags flags(const QModelIndex &index) const;
+
+ void sort(int column, Qt::SortOrder order);
+ void ensureSorted(int column, Qt::SortOrder order, int start, int end);
+ static bool itemLessThan(const QPair<QListWidgetItem*,int> &left,
+ const QPair<QListWidgetItem*,int> &right);
+ static bool itemGreaterThan(const QPair<QListWidgetItem*,int> &left,
+ const QPair<QListWidgetItem*,int> &right);
+ static QList<QListWidgetItem*>::iterator sortedInsertionIterator(
+ const QList<QListWidgetItem*>::iterator &begin,
+ const QList<QListWidgetItem*>::iterator &end,
+ Qt::SortOrder order, QListWidgetItem *item);
+
+ void itemChanged(QListWidgetItem *item);
+
+ // dnd
+ QStringList mimeTypes() const;
+ QMimeData *mimeData(const QModelIndexList &indexes) const;
+#ifndef QT_NO_DRAGANDDROP
+ bool dropMimeData(const QMimeData *data, Qt::DropAction action,
+ int row, int column, const QModelIndex &parent);
+ Qt::DropActions supportedDropActions() const;
+#endif
+
+ QMimeData *internalMimeData() const;
+private:
+ QList<QListWidgetItem*> items;
+
+ // A cache must be mutable if get-functions should have const modifiers
+ mutable QModelIndexList cachedIndexes;
+};
+
+
+
+class QListWidgetPrivate : public QListViewPrivate
+{
+ Q_DECLARE_PUBLIC(QListWidget)
+public:
+ QListWidgetPrivate() : QListViewPrivate(), sortOrder(Qt::AscendingOrder), sortingEnabled(false) {}
+ inline QListModel *model() const { return qobject_cast<QListModel*>(q_func()->model()); }
+ void setup();
+ void _q_emitItemPressed(const QModelIndex &index);
+ void _q_emitItemClicked(const QModelIndex &index);
+ void _q_emitItemDoubleClicked(const QModelIndex &index);
+ void _q_emitItemActivated(const QModelIndex &index);
+ void _q_emitItemEntered(const QModelIndex &index);
+ void _q_emitItemChanged(const QModelIndex &index);
+ void _q_emitCurrentItemChanged(const QModelIndex &current, const QModelIndex &previous);
+ void _q_sort();
+ void _q_dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight);
+ Qt::SortOrder sortOrder;
+ bool sortingEnabled;
+};
+
+class QListWidgetItemPrivate
+{
+public:
+ QListWidgetItemPrivate(QListWidgetItem *item) : q(item), theid(-1) {}
+ QListWidgetItem *q;
+ int id;
+ QVector<QWidgetItemData> values;
+ int theid;
+};
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_LISTWIDGET
+
+#endif // QLISTWIDGET_P_H
diff --git a/src/gui/itemviews/qproxymodel.cpp b/src/gui/itemviews/qproxymodel.cpp
new file mode 100644
index 0000000000..1be5effb7b
--- /dev/null
+++ b/src/gui/itemviews/qproxymodel.cpp
@@ -0,0 +1,547 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qproxymodel.h"
+
+#ifndef QT_NO_PROXYMODEL
+#include <private/qproxymodel_p.h>
+#include <qsize.h>
+#include <qstringlist.h>
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QProxyModel
+ \obsolete
+ \brief The QProxyModel class provides support for processing data
+ passed between another model and a view.
+
+ \ingroup model-view
+
+ If you want to do filtering and sorting, see QSortFilterProxyModel.
+
+ Proxy models provide a standard model interface that can be used to
+ manipulate the data retrieved through an underlying model. They can be used to
+ perform operations such as sorting and filtering on the data obtained without
+ changing the contents of the model.
+
+ Just as with subclasses of QAbstractItemView, QProxyModel provides the setModel()
+ function that is used to specify the model to be acted on by the proxy.
+ Views can be connected to either the underlying model or the proxy model with
+ \l QAbstractItemView::setModel().
+
+ Since views rely on the information provided in model indexes to identify
+ items of data from models, and to position these items in some visual
+ representation, proxy models must create their own model indexes instead of
+ supplying model indexes from their underlying models.
+
+ \sa \link model-view-programming.html Model/View Programming\endlink QAbstractItemModel
+
+*/
+
+/*!
+ Constructs a proxy model with the given \a parent.
+*/
+
+QProxyModel::QProxyModel(QObject *parent)
+ : QAbstractItemModel(*new QProxyModelPrivate, parent)
+{
+ Q_D(QProxyModel);
+ setModel(&d->empty);
+}
+
+/*!
+ \internal
+*/
+QProxyModel::QProxyModel(QProxyModelPrivate &dd, QObject *parent)
+ : QAbstractItemModel(dd, parent)
+{
+ Q_D(QProxyModel);
+ setModel(&d->empty);
+}
+
+/*!
+ Destroys the proxy model.
+*/
+QProxyModel::~QProxyModel()
+{
+}
+
+/*!
+ Sets the given \a model to be processed by the proxy model.
+*/
+void QProxyModel::setModel(QAbstractItemModel *model)
+{
+ Q_D(QProxyModel);
+ if (d->model && d->model != &d->empty)
+ disconnectFromModel(d->model);
+ if (model) {
+ d->model = model;
+ connectToModel(model);
+ } else {
+ d->model = &d->empty;
+ }
+}
+
+/*!
+ Returns the model that contains the data that is available through the
+ proxy model.
+*/
+QAbstractItemModel *QProxyModel::model() const
+{
+ Q_D(const QProxyModel);
+ return d->model;
+}
+
+/*!
+ Returns the model index with the given \a row, \a column, and \a parent.
+
+ \sa QAbstractItemModel::index()
+*/
+QModelIndex QProxyModel::index(int row, int column, const QModelIndex &parent) const
+{
+ Q_D(const QProxyModel);
+ return setProxyModel(d->model->index(row, column, setSourceModel(parent)));
+}
+
+/*!
+ Returns the model index that corresponds to the parent of the given \a child
+ index.
+*/
+QModelIndex QProxyModel::parent(const QModelIndex &child) const
+{
+ Q_D(const QProxyModel);
+ return setProxyModel(d->model->parent(setSourceModel(child)));
+}
+
+/*!
+ Returns the number of rows for the given \a parent.
+
+ \sa QAbstractItemModel::rowCount()
+*/
+int QProxyModel::rowCount(const QModelIndex &parent) const
+{
+ Q_D(const QProxyModel);
+ return d->model->rowCount(setSourceModel(parent));
+}
+
+/*!
+ Returns the number of columns for the given \a parent.
+
+ \sa QAbstractItemModel::columnCount()
+*/
+int QProxyModel::columnCount(const QModelIndex &parent) const
+{
+ Q_D(const QProxyModel);
+ return d->model->columnCount(setSourceModel(parent));
+}
+
+/*!
+ Returns true if the item corresponding to the \a parent index has child
+ items; otherwise returns false.
+
+ \sa QAbstractItemModel::hasChildren()
+*/
+bool QProxyModel::hasChildren(const QModelIndex &parent) const
+{
+ Q_D(const QProxyModel);
+ return d->model->hasChildren(setSourceModel(parent));
+}
+
+/*!
+ Returns the data stored in the item with the given \a index under the
+ specified \a role.
+*/
+QVariant QProxyModel::data(const QModelIndex &index, int role) const
+{
+ Q_D(const QProxyModel);
+ return d->model->data(setSourceModel(index), role);
+}
+
+/*!
+ Sets the \a role data for the item at \a index to \a value.
+ Returns true if successful; otherwise returns false.
+
+ The base class implementation returns false. This function and
+ data() must be reimplemented for editable models.
+
+ \sa data() itemData() QAbstractItemModel::setData()
+*/
+bool QProxyModel::setData(const QModelIndex &index, const QVariant &value, int role)
+{
+ Q_D(const QProxyModel);
+ return d->model->setData(setSourceModel(index), value, role);
+}
+
+/*!
+ Returns the data stored in the \a section of the header with specified
+ \a orientation under the given \a role.
+*/
+QVariant QProxyModel::headerData(int section, Qt::Orientation orientation, int role) const
+{
+ Q_D(const QProxyModel);
+ return d->model->headerData(section, orientation, role);
+}
+
+/*!
+ Sets the \a role data in the \a section of the header with the specified
+ \a orientation to the \a value given.
+
+ \sa QAbstractItemModel::setHeaderData()
+*/
+bool QProxyModel::setHeaderData(int section, Qt::Orientation orientation,
+ const QVariant &value, int role)
+{
+ Q_D(const QProxyModel);
+ return d->model->setHeaderData(section, orientation, value, role);
+}
+
+/*!
+ Returns a list of MIME types that are supported by the model.
+*/
+QStringList QProxyModel::mimeTypes() const
+{
+ Q_D(const QProxyModel);
+ return d->model->mimeTypes();
+}
+
+/*!
+ Returns MIME data for the specified \a indexes in the model.
+*/
+QMimeData *QProxyModel::mimeData(const QModelIndexList &indexes) const
+{
+ Q_D(const QProxyModel);
+ QModelIndexList lst;
+ for (int i = 0; i < indexes.count(); ++i)
+ lst.append(setSourceModel(indexes.at(i)));
+ return d->model->mimeData(lst);
+}
+
+/*!
+ Returns true if the model accepts the \a data dropped onto an attached
+ view for the specified \a action; otherwise returns false.
+
+ The \a parent, \a row, and \a column details can be used to control
+ which MIME types are acceptable to different parts of a model when
+ received via the drag and drop system.
+*/
+bool QProxyModel::dropMimeData(const QMimeData *data, Qt::DropAction action,
+ int row, int column, const QModelIndex &parent)
+{
+ Q_D(const QProxyModel);
+ return d->model->dropMimeData(data, action, row, column, setSourceModel(parent));
+}
+
+/*!
+ Returns the drop actions that are supported by the model; this is
+ a combination of the individual actions defined in \l Qt::DropActions.
+
+ The selection of drop actions provided by the model will influence the
+ behavior of the component that started the drag and drop operation.
+
+ \sa \link dnd.html Drag and Drop\endlink
+*/
+Qt::DropActions QProxyModel::supportedDropActions() const
+{
+ Q_D(const QProxyModel);
+ return d->model->supportedDropActions();
+}
+
+/*!
+ Inserts \a count rows into the model, creating new items as children of
+ the given \a parent. The new rows are inserted before the \a row
+ specified. If the \a parent item has no children, a single column is
+ created to contain the required number of rows.
+
+ Returns true if the rows were successfully inserted; otherwise
+ returns false.
+
+ \sa QAbstractItemModel::insertRows()*/
+bool QProxyModel::insertRows(int row, int count, const QModelIndex &parent)
+{
+ Q_D(const QProxyModel);
+ return d->model->insertRows(row, count, setSourceModel(parent));
+}
+
+/*!
+ Inserts \a count columns into the model, creating new items as children of
+ the given \a parent. The new columns are inserted before the \a column
+ specified. If the \a parent item has no children, a single row is created
+ to contain the required number of columns.
+
+ Returns true if the columns were successfully inserted; otherwise
+ returns false.
+
+ \sa QAbstractItemModel::insertColumns()
+*/
+bool QProxyModel::insertColumns(int column, int count, const QModelIndex &parent)
+{
+ Q_D(const QProxyModel);
+ return d->model->insertColumns(column, count, setSourceModel(parent));
+}
+
+/*!
+ Fetches more child items of the given \a parent. This function is used by views
+ to tell the model that they can display more data than the model has provided.
+
+ \sa QAbstractItemModel::fetchMore()
+*/
+void QProxyModel::fetchMore(const QModelIndex &parent)
+{
+ Q_D(const QProxyModel);
+ d->model->fetchMore(parent);
+}
+
+/*!
+ Returns the item flags for the given \a index.
+*/
+Qt::ItemFlags QProxyModel::flags(const QModelIndex &index) const
+{
+ Q_D(const QProxyModel);
+ return d->model->flags(setSourceModel(index));
+}
+
+/*!
+ Sorts the child items in the specified \a column according to the sort
+ order defined by \a order.
+
+ \sa QAbstractItemModel::sort()
+*/
+void QProxyModel::sort(int column, Qt::SortOrder order)
+{
+ Q_D(QProxyModel);
+ d->model->sort(column, order);
+}
+
+/*!
+ Returns a list of model indexes that each contain the given \a value for
+ the \a role specified. The search begins at the \a start index and is
+ performed according to the specified \a flags. The search continues until
+ the number of matching data items equals \a hits, the last row is reached,
+ or the search reaches \a start again, depending on whether \c MatchWrap is
+ specified in \a flags.
+
+ \sa QAbstractItemModel::match()
+*/
+QModelIndexList QProxyModel::match(const QModelIndex &start,
+ int role, const QVariant &value,
+ int hits, Qt::MatchFlags flags) const
+{
+ Q_D(const QProxyModel);
+ return d->model->match(start, role, value, hits, flags);
+}
+
+/*!
+ Returns the size of the item that corresponds to the specified \a index.
+*/
+QSize QProxyModel::span(const QModelIndex &index) const
+{
+ Q_D(const QProxyModel);
+ return d->model->span(setSourceModel(index));
+}
+
+/*!
+ */
+bool QProxyModel::submit()
+{
+ Q_D(QProxyModel);
+ return d->model->submit();
+}
+
+/*!
+ */
+void QProxyModel::revert()
+{
+ Q_D(QProxyModel);
+ d->model->revert();
+}
+
+/*!
+ \internal
+ Change the model pointer in the given \a source_index to point to the proxy model.
+ */
+QModelIndex QProxyModel::setProxyModel(const QModelIndex &source_index) const
+{
+ QModelIndex proxy_index = source_index;
+ if (proxy_index.isValid())
+ proxy_index.m = this;
+ return proxy_index;
+}
+
+/*!
+ \internal
+ Change the model pointer in the given \a proxy_index to point to the source model.
+ */
+QModelIndex QProxyModel::setSourceModel(const QModelIndex &proxy_index) const
+{
+ Q_D(const QProxyModel);
+ QModelIndex source_index = proxy_index;
+ source_index.m = d->model;
+ return source_index;
+}
+
+/*!
+ \internal
+ Connect to all the signals emitted by given \a model.
+*/
+void QProxyModel::connectToModel(const QAbstractItemModel *model) const
+{
+ connect(model, SIGNAL(dataChanged(QModelIndex,QModelIndex)),
+ this, SLOT(_q_sourceDataChanged(QModelIndex,QModelIndex)));
+ connect(model, SIGNAL(headerDataChanged(Qt::Orientation,int,int)),
+ this, SIGNAL(headerDataChanged(Qt::Orientation,int,int))); // signal to signal
+ connect(model, SIGNAL(rowsAboutToBeInserted(QModelIndex,int,int)),
+ this, SLOT(_q_sourceRowsAboutToBeInserted(QModelIndex,int,int)));
+ connect(model, SIGNAL(rowsInserted(QModelIndex,int,int)),
+ this, SLOT(_q_sourceRowsInserted(QModelIndex,int,int)));
+ connect(model, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)),
+ this, SLOT(_q_sourceRowsAboutToBeRemoved(QModelIndex,int,int)));
+ connect(model, SIGNAL(rowsRemoved(QModelIndex,int,int)),
+ this, SLOT(_q_sourceRowsRemoved(QModelIndex,int,int)));
+ connect(model, SIGNAL(columnsAboutToBeInserted(QModelIndex,int,int)),
+ this, SLOT(_q_sourceColumnsAboutToBeInserted(QModelIndex,int,int)));
+ connect(model, SIGNAL(columnsInserted(QModelIndex,int,int)),
+ this, SLOT(_q_sourceColumnsInserted(QModelIndex,int,int)));
+ connect(model, SIGNAL(columnsAboutToBeRemoved(QModelIndex,int,int)),
+ this, SLOT(_q_sourceColumnsAboutToBeRemoved(QModelIndex,int,int)));
+ connect(model, SIGNAL(columnsRemoved(QModelIndex,int,int)),
+ this, SLOT(_q_sourceColumnsRemoved(QModelIndex,int,int)));
+ connect(model, SIGNAL(modelReset()), this, SIGNAL(modelReset())); // signal to signal
+ connect(model, SIGNAL(layoutAboutToBeChanged()), this, SIGNAL(layoutAboutToBeChanged())); // signal to signal
+ connect(model, SIGNAL(layoutChanged()), this, SIGNAL(layoutChanged())); // signal to signal
+}
+
+/*!
+ \internal
+ Disconnect from all the signals emitted by the given \a model.
+ */
+void QProxyModel::disconnectFromModel(const QAbstractItemModel *model) const
+{
+ disconnect(model, SIGNAL(dataChanged(QModelIndex,QModelIndex)),
+ this, SLOT(_q_sourceDataChanged(QModelIndex,QModelIndex)));
+ disconnect(model, SIGNAL(headerDataChanged(Qt::Orientation,int,int)),
+ this, SIGNAL(headerDataChanged(Qt::Orientation,int,int))); // signal to signal
+ disconnect(model, SIGNAL(rowsAboutToBeInserted(QModelIndex,int,int)),
+ this, SLOT(_q_sourceRowsAboutToBeInserted(QModelIndex,int,int)));
+ disconnect(model, SIGNAL(rowsInserted(QModelIndex,int,int)),
+ this, SLOT(rowsInserted(QModelIndex,int,int)));
+ disconnect(model, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)),
+ this, SLOT(_q_sourceRowsAboutToBeRemoved(QModelIndex,int,int)));
+ disconnect(model, SIGNAL(rowsRemoved(QModelIndex,int,int)),
+ this, SLOT(_q_sourceRowsRemoved(QModelIndex,int,int)));
+ disconnect(model, SIGNAL(columnsAboutToBeInserted(QModelIndex,int,int)),
+ this, SLOT(_q_sourceColumnsAboutToBeInserted(QModelIndex,int,int)));
+ disconnect(model, SIGNAL(columnsInserted(QModelIndex,int,int)),
+ this, SLOT(_q_sourceColumnsInserted(QModelIndex,int,int)));
+ disconnect(model, SIGNAL(columnsAboutToBeRemoved(QModelIndex,int,int)),
+ this, SLOT(_q_sourceColumnsAboutToBeRemoved(QModelIndex,int,int)));
+ disconnect(model, SIGNAL(columnsRemoved(QModelIndex,int,int)),
+ this, SLOT(_q_sourceColumnsRemoved(QModelIndex,int,int)));
+ disconnect(model, SIGNAL(modelReset()), this, SIGNAL(modelReset())); // signal to signal
+ disconnect(model, SIGNAL(layoutAboutToBeChanged()), this, SIGNAL(layoutAboutToBeChanged())); // signal to signal
+ disconnect(model, SIGNAL(layoutChanged()), this, SIGNAL(layoutChanged())); // signal to signal
+}
+
+/*!
+ \fn QObject *QProxyModel::parent() const
+ \internal
+*/
+
+void QProxyModelPrivate::_q_sourceDataChanged(const QModelIndex &tl,const QModelIndex &br)
+{
+ Q_Q(QProxyModel);
+ emit q->dataChanged(q->setProxyModel(tl), q->setProxyModel(br));
+}
+
+void QProxyModelPrivate::_q_sourceRowsAboutToBeInserted(const QModelIndex &parent, int first ,int last)
+{
+ Q_Q(QProxyModel);
+ q->beginInsertRows(q->setProxyModel(parent), first, last);
+}
+
+void QProxyModelPrivate::_q_sourceRowsInserted(const QModelIndex &, int, int)
+{
+ Q_Q(QProxyModel);
+ q->endInsertRows();
+}
+
+void QProxyModelPrivate::_q_sourceRowsAboutToBeRemoved(const QModelIndex &parent, int first, int last)
+{
+ Q_Q(QProxyModel);
+ q->beginRemoveRows(q->setProxyModel(parent), first, last);
+}
+
+void QProxyModelPrivate::_q_sourceRowsRemoved(const QModelIndex &, int, int)
+{
+ Q_Q(QProxyModel);
+ q->endRemoveRows();
+}
+
+void QProxyModelPrivate::_q_sourceColumnsAboutToBeInserted(const QModelIndex &parent, int first, int last)
+{
+ Q_Q(QProxyModel);
+ q->beginInsertColumns(q->setProxyModel(parent), first, last);
+}
+
+void QProxyModelPrivate::_q_sourceColumnsInserted(const QModelIndex &, int, int)
+{
+ Q_Q(QProxyModel);
+ q->endInsertColumns();
+}
+
+void QProxyModelPrivate::_q_sourceColumnsAboutToBeRemoved(const QModelIndex &parent, int first, int last)
+{
+ Q_Q(QProxyModel);
+ q->beginRemoveColumns(q->setProxyModel(parent), first, last);
+}
+
+
+void QProxyModelPrivate::_q_sourceColumnsRemoved(const QModelIndex &, int, int)
+{
+ Q_Q(QProxyModel);
+ q->endRemoveColumns();
+}
+
+QT_END_NAMESPACE
+
+#include "moc_qproxymodel.cpp"
+
+#endif // QT_NO_PROXYMODEL
diff --git a/src/gui/itemviews/qproxymodel.h b/src/gui/itemviews/qproxymodel.h
new file mode 100644
index 0000000000..0644962c56
--- /dev/null
+++ b/src/gui/itemviews/qproxymodel.h
@@ -0,0 +1,142 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QPROXYMODEL_H
+#define QPROXYMODEL_H
+
+#include <QtCore/qabstractitemmodel.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+#ifndef QT_NO_PROXYMODEL
+
+class QProxyModelPrivate;
+
+class Q_GUI_EXPORT QProxyModel : public QAbstractItemModel
+{
+ Q_OBJECT
+
+public:
+ explicit QProxyModel(QObject *parent = 0);
+ ~QProxyModel();
+
+ virtual void setModel(QAbstractItemModel *model);
+ QAbstractItemModel *model() const;
+
+ // implementing model interface
+
+ QModelIndex index(int row, int column, const QModelIndex &parent) const;
+ QModelIndex parent(const QModelIndex &child) const;
+
+ int rowCount(const QModelIndex &parent) const;
+ int columnCount(const QModelIndex &parent) const;
+ bool hasChildren(const QModelIndex &parent) const;
+
+ QVariant data(const QModelIndex &index, int role) const;
+ bool setData(const QModelIndex &index, const QVariant &value, int role);
+
+ QVariant headerData(int section, Qt::Orientation orientation, int role) const;
+ bool setHeaderData(int section, Qt::Orientation orientation, const QVariant &value,
+ int role);
+
+ QStringList mimeTypes() const;
+ QMimeData *mimeData(const QModelIndexList &indexes) const;
+ bool dropMimeData(const QMimeData *data, Qt::DropAction action,
+ int row, int column, const QModelIndex &parent);
+ Qt::DropActions supportedDropActions() const;
+
+ bool insertRows(int row, int count, const QModelIndex &parent);
+ bool insertColumns(int column, int count, const QModelIndex &parent);
+
+ void fetchMore(const QModelIndex &parent);
+ Qt::ItemFlags flags(const QModelIndex &index) const;
+
+ void sort(int column, Qt::SortOrder order);
+
+ QModelIndexList match(const QModelIndex &start, int role, const QVariant &value,
+ int hits, Qt::MatchFlags flags) const;
+
+ QSize span(const QModelIndex &index) const;
+
+ bool submit();
+ void revert();
+
+#ifdef Q_NO_USING_KEYWORD
+ inline QObject *parent() const { return QObject::parent(); }
+#else
+ using QObject::parent;
+#endif
+
+protected:
+ QProxyModel(QProxyModelPrivate &, QObject *parent = 0);
+
+ QModelIndex setProxyModel(const QModelIndex &source_index) const;
+ QModelIndex setSourceModel(const QModelIndex &proxy_index) const;
+
+ void connectToModel(const QAbstractItemModel *model) const;
+ void disconnectFromModel(const QAbstractItemModel *model) const;
+
+private:
+ Q_DECLARE_PRIVATE(QProxyModel)
+ Q_DISABLE_COPY(QProxyModel)
+
+ Q_PRIVATE_SLOT(d_func(), void _q_sourceDataChanged(const QModelIndex&,const QModelIndex&))
+ Q_PRIVATE_SLOT(d_func(), void _q_sourceRowsAboutToBeInserted(const QModelIndex&,int,int))
+ Q_PRIVATE_SLOT(d_func(), void _q_sourceRowsInserted(const QModelIndex&,int,int))
+ Q_PRIVATE_SLOT(d_func(), void _q_sourceRowsAboutToBeRemoved(const QModelIndex&,int,int))
+ Q_PRIVATE_SLOT(d_func(), void _q_sourceRowsRemoved(const QModelIndex&,int,int))
+ Q_PRIVATE_SLOT(d_func(), void _q_sourceColumnsAboutToBeInserted(const QModelIndex&,int,int))
+ Q_PRIVATE_SLOT(d_func(), void _q_sourceColumnsInserted(const QModelIndex&,int,int))
+ Q_PRIVATE_SLOT(d_func(), void _q_sourceColumnsAboutToBeRemoved(const QModelIndex&,int,int))
+ Q_PRIVATE_SLOT(d_func(), void _q_sourceColumnsRemoved(const QModelIndex&,int,int))
+};
+
+#endif // QT_NO_PROXYMODEL
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QPROXYMODEL_H
diff --git a/src/gui/itemviews/qproxymodel_p.h b/src/gui/itemviews/qproxymodel_p.h
new file mode 100644
index 0000000000..bfca27465c
--- /dev/null
+++ b/src/gui/itemviews/qproxymodel_p.h
@@ -0,0 +1,100 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QPROXYMODEL_P_H
+#define QPROXYMODEL_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of QAbstractItemModel*. This header file may change from version
+// to version without notice, or even be removed.
+//
+// We mean it.
+//
+//
+
+#include "QtCore/qabstractitemmodel.h"
+#include "private/qabstractitemmodel_p.h"
+
+#ifndef QT_NO_PROXYMODEL
+
+QT_BEGIN_NAMESPACE
+
+class QEmptyModel : public QAbstractItemModel
+{
+public:
+ explicit QEmptyModel(QObject *parent = 0) : QAbstractItemModel(parent) {}
+ QModelIndex index(int, int, const QModelIndex &) const { return QModelIndex(); }
+ QModelIndex parent(const QModelIndex &) const { return QModelIndex(); }
+ int rowCount(const QModelIndex &) const { return 0; }
+ int columnCount(const QModelIndex &) const { return 0; }
+ bool hasChildren(const QModelIndex &) const { return false; }
+ QVariant data(const QModelIndex &, int) const { return QVariant(); }
+};
+
+class QProxyModelPrivate : private QAbstractItemModelPrivate
+{
+ Q_DECLARE_PUBLIC(QProxyModel)
+
+public:
+ void _q_sourceDataChanged(const QModelIndex &tl,const QModelIndex &br);
+ void _q_sourceRowsAboutToBeInserted(const QModelIndex &parent, int first ,int last);
+ void _q_sourceRowsInserted(const QModelIndex &parent, int first ,int last);
+ void _q_sourceRowsAboutToBeRemoved(const QModelIndex &parent, int first, int last);
+ void _q_sourceRowsRemoved(const QModelIndex &parent, int first, int last);
+ void _q_sourceColumnsAboutToBeInserted(const QModelIndex &parent, int first, int last);
+ void _q_sourceColumnsInserted(const QModelIndex &parent, int first, int last);
+ void _q_sourceColumnsAboutToBeRemoved(const QModelIndex &parent, int first, int last);
+ void _q_sourceColumnsRemoved(const QModelIndex &parent, int first, int last);
+
+ QProxyModelPrivate() : QAbstractItemModelPrivate(), model(0) {}
+ QAbstractItemModel *model;
+ QEmptyModel empty;
+};
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_PROXYMODEL
+
+#endif // QPROXYMODEL_P_H
diff --git a/src/gui/itemviews/qsortfilterproxymodel.cpp b/src/gui/itemviews/qsortfilterproxymodel.cpp
new file mode 100644
index 0000000000..b3993c7e56
--- /dev/null
+++ b/src/gui/itemviews/qsortfilterproxymodel.cpp
@@ -0,0 +1,2392 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qsortfilterproxymodel.h"
+
+#ifndef QT_NO_SORTFILTERPROXYMODEL
+
+#include "qitemselectionmodel.h"
+#include <qsize.h>
+#include <qdebug.h>
+#include <qdatetime.h>
+#include <qpair.h>
+#include <qstringlist.h>
+#include <private/qabstractitemmodel_p.h>
+#include <private/qabstractproxymodel_p.h>
+
+QT_BEGIN_NAMESPACE
+
+typedef QList<QPair<QModelIndex, QPersistentModelIndex> > QModelIndexPairList;
+
+class QSortFilterProxyModelLessThan
+{
+public:
+ inline QSortFilterProxyModelLessThan(int column, const QModelIndex &parent,
+ const QAbstractItemModel *source,
+ const QSortFilterProxyModel *proxy)
+ : sort_column(column), source_parent(parent), source_model(source), proxy_model(proxy) {}
+
+ inline bool operator()(int r1, int r2) const
+ {
+ QModelIndex i1 = source_model->index(r1, sort_column, source_parent);
+ QModelIndex i2 = source_model->index(r2, sort_column, source_parent);
+ return proxy_model->lessThan(i1, i2);
+ }
+
+private:
+ int sort_column;
+ QModelIndex source_parent;
+ const QAbstractItemModel *source_model;
+ const QSortFilterProxyModel *proxy_model;
+};
+
+class QSortFilterProxyModelGreaterThan
+{
+public:
+ inline QSortFilterProxyModelGreaterThan(int column, const QModelIndex &parent,
+ const QAbstractItemModel *source,
+ const QSortFilterProxyModel *proxy)
+ : sort_column(column), source_parent(parent),
+ source_model(source), proxy_model(proxy) {}
+
+ inline bool operator()(int r1, int r2) const
+ {
+ QModelIndex i1 = source_model->index(r1, sort_column, source_parent);
+ QModelIndex i2 = source_model->index(r2, sort_column, source_parent);
+ return proxy_model->lessThan(i2, i1);
+ }
+
+private:
+ int sort_column;
+ QModelIndex source_parent;
+ const QAbstractItemModel *source_model;
+ const QSortFilterProxyModel *proxy_model;
+};
+
+
+class QSortFilterProxyModelPrivate : public QAbstractProxyModelPrivate
+{
+ Q_DECLARE_PUBLIC(QSortFilterProxyModel)
+
+public:
+ struct Mapping {
+ QVector<int> source_rows;
+ QVector<int> source_columns;
+ QVector<int> proxy_rows;
+ QVector<int> proxy_columns;
+ QVector<QModelIndex> mapped_children;
+ QMap<QModelIndex, Mapping *>::const_iterator map_iter;
+ };
+
+ mutable QMap<QModelIndex, Mapping*> source_index_mapping;
+
+ int source_sort_column;
+ int proxy_sort_column;
+ Qt::SortOrder sort_order;
+ Qt::CaseSensitivity sort_casesensitivity;
+ int sort_role;
+ bool sort_localeaware;
+
+ int filter_column;
+ QRegExp filter_regexp;
+ int filter_role;
+
+ bool dynamic_sortfilter;
+
+ QModelIndexPairList saved_persistent_indexes;
+
+ QMap<QModelIndex, Mapping *>::const_iterator create_mapping(
+ const QModelIndex &source_parent) const;
+ QModelIndex proxy_to_source(const QModelIndex &proxyIndex) const;
+ QModelIndex source_to_proxy(const QModelIndex &sourceIndex) const;
+
+ void remove_from_mapping(const QModelIndex &source_parent);
+
+ inline QMap<QModelIndex, Mapping *>::const_iterator index_to_iterator(
+ const QModelIndex &proxy_index) const
+ {
+ Q_ASSERT(proxy_index.isValid());
+ const void *p = proxy_index.internalPointer();
+ Q_ASSERT(p);
+ QMap<QModelIndex, Mapping *>::const_iterator it =
+ static_cast<const Mapping*>(p)->map_iter;
+ Q_ASSERT(it != source_index_mapping.constEnd());
+ Q_ASSERT(it.value());
+ return it;
+ }
+
+ inline QModelIndex create_index(int row, int column,
+ QMap<QModelIndex, Mapping*>::const_iterator it) const
+ {
+ return q_func()->createIndex(row, column, *it);
+ }
+
+ void _q_sourceDataChanged(const QModelIndex &source_top_left,
+ const QModelIndex &source_bottom_right);
+ void _q_sourceHeaderDataChanged(Qt::Orientation orientation, int start, int end);
+
+ void _q_sourceReset();
+
+ void _q_sourceLayoutAboutToBeChanged();
+ void _q_sourceLayoutChanged();
+
+ void _q_sourceRowsAboutToBeInserted(const QModelIndex &source_parent,
+ int start, int end);
+ void _q_sourceRowsInserted(const QModelIndex &source_parent,
+ int start, int end);
+ void _q_sourceRowsAboutToBeRemoved(const QModelIndex &source_parent,
+ int start, int end);
+ void _q_sourceRowsRemoved(const QModelIndex &source_parent,
+ int start, int end);
+ void _q_sourceColumnsAboutToBeInserted(const QModelIndex &source_parent,
+ int start, int end);
+ void _q_sourceColumnsInserted(const QModelIndex &source_parent,
+ int start, int end);
+ void _q_sourceColumnsAboutToBeRemoved(const QModelIndex &source_parent,
+ int start, int end);
+ void _q_sourceColumnsRemoved(const QModelIndex &source_parent,
+ int start, int end);
+
+ void clear_mapping();
+
+ void sort();
+ bool update_source_sort_column();
+ void sort_source_rows(QVector<int> &source_rows,
+ const QModelIndex &source_parent) const;
+ QVector<QPair<int, QVector<int > > > proxy_intervals_for_source_items_to_add(
+ const QVector<int> &proxy_to_source, const QVector<int> &source_items,
+ const QModelIndex &source_parent, Qt::Orientation orient) const;
+ QVector<QPair<int, int > > proxy_intervals_for_source_items(
+ const QVector<int> &source_to_proxy, const QVector<int> &source_items) const;
+ void insert_source_items(
+ QVector<int> &source_to_proxy, QVector<int> &proxy_to_source,
+ const QVector<int> &source_items, const QModelIndex &source_parent,
+ Qt::Orientation orient, bool emit_signal = true);
+ void remove_source_items(
+ QVector<int> &source_to_proxy, QVector<int> &proxy_to_source,
+ const QVector<int> &source_items, const QModelIndex &source_parent,
+ Qt::Orientation orient, bool emit_signal = true);
+ void remove_proxy_interval(
+ QVector<int> &source_to_proxy, QVector<int> &proxy_to_source,
+ int proxy_start, int proxy_end, const QModelIndex &proxy_parent,
+ Qt::Orientation orient, bool emit_signal = true);
+ void build_source_to_proxy_mapping(
+ const QVector<int> &proxy_to_source, QVector<int> &source_to_proxy) const;
+ void source_items_inserted(const QModelIndex &source_parent,
+ int start, int end, Qt::Orientation orient);
+ void source_items_about_to_be_removed(const QModelIndex &source_parent,
+ int start, int end, Qt::Orientation orient);
+ void source_items_removed(const QModelIndex &source_parent,
+ int start, int end, Qt::Orientation orient);
+ void proxy_item_range(
+ const QVector<int> &source_to_proxy, const QVector<int> &source_items,
+ int &proxy_low, int &proxy_high) const;
+
+ QModelIndexPairList store_persistent_indexes();
+ void update_persistent_indexes(const QModelIndexPairList &source_indexes);
+
+ void filter_changed();
+ void handle_filter_changed(
+ QVector<int> &source_to_proxy, QVector<int> &proxy_to_source,
+ const QModelIndex &source_parent, Qt::Orientation orient);
+
+ void updateChildrenMapping(const QModelIndex &source_parent, Mapping *parent_mapping,
+ Qt::Orientation orient, int start, int end, int delta_item_count, bool remove);
+
+ virtual void _q_sourceModelDestroyed();
+};
+
+typedef QMap<QModelIndex, QSortFilterProxyModelPrivate::Mapping *> IndexMap;
+
+void QSortFilterProxyModelPrivate::_q_sourceModelDestroyed()
+{
+ QAbstractProxyModelPrivate::_q_sourceModelDestroyed();
+ clear_mapping();
+}
+
+void QSortFilterProxyModelPrivate::remove_from_mapping(const QModelIndex &source_parent)
+{
+ if (Mapping *m = source_index_mapping.take(source_parent)) {
+ for (int i = 0; i < m->mapped_children.size(); ++i)
+ remove_from_mapping(m->mapped_children.at(i));
+ delete m;
+ }
+}
+
+void QSortFilterProxyModelPrivate::clear_mapping()
+{
+ // store the persistent indexes
+ QModelIndexPairList source_indexes = store_persistent_indexes();
+
+ qDeleteAll(source_index_mapping);
+ source_index_mapping.clear();
+
+ // update the persistent indexes
+ update_persistent_indexes(source_indexes);
+}
+
+IndexMap::const_iterator QSortFilterProxyModelPrivate::create_mapping(
+ const QModelIndex &source_parent) const
+{
+ Q_Q(const QSortFilterProxyModel);
+
+ IndexMap::const_iterator it = source_index_mapping.constFind(source_parent);
+ if (it != source_index_mapping.constEnd()) // was mapped already
+ return it;
+
+ Mapping *m = new Mapping;
+
+ int source_rows = model->rowCount(source_parent);
+ for (int i = 0; i < source_rows; ++i) {
+ if (q->filterAcceptsRow(i, source_parent))
+ m->source_rows.append(i);
+ }
+ int source_cols = model->columnCount(source_parent);
+ for (int i = 0; i < source_cols; ++i) {
+ if (q->filterAcceptsColumn(i, source_parent))
+ m->source_columns.append(i);
+ }
+
+ sort_source_rows(m->source_rows, source_parent);
+ m->proxy_rows.resize(source_rows);
+ build_source_to_proxy_mapping(m->source_rows, m->proxy_rows);
+ m->proxy_columns.resize(source_cols);
+ build_source_to_proxy_mapping(m->source_columns, m->proxy_columns);
+
+ it = IndexMap::const_iterator(source_index_mapping.insert(source_parent, m));
+ m->map_iter = it;
+
+ if (source_parent.isValid()) {
+ QModelIndex source_grand_parent = source_parent.parent();
+ IndexMap::const_iterator it2 = create_mapping(source_grand_parent);
+ Q_ASSERT(it2 != source_index_mapping.constEnd());
+ it2.value()->mapped_children.append(source_parent);
+ }
+
+ Q_ASSERT(it != source_index_mapping.constEnd());
+ Q_ASSERT(it.value());
+
+ return it;
+}
+
+QModelIndex QSortFilterProxyModelPrivate::proxy_to_source(const QModelIndex &proxy_index) const
+{
+ if (!proxy_index.isValid())
+ return QModelIndex(); // for now; we may want to be able to set a root index later
+ IndexMap::const_iterator it = index_to_iterator(proxy_index);
+ Mapping *m = it.value();
+ if ((proxy_index.row() >= m->source_rows.size()) || (proxy_index.column() >= m->source_columns.size()))
+ return QModelIndex();
+ int source_row = m->source_rows.at(proxy_index.row());
+ int source_col = m->source_columns.at(proxy_index.column());
+ return model->index(source_row, source_col, it.key());
+}
+
+QModelIndex QSortFilterProxyModelPrivate::source_to_proxy(const QModelIndex &source_index) const
+{
+ if (!source_index.isValid())
+ return QModelIndex(); // for now; we may want to be able to set a root index later
+ QModelIndex source_parent = source_index.parent();
+ IndexMap::const_iterator it = create_mapping(source_parent);
+ Mapping *m = it.value();
+ if ((source_index.row() >= m->proxy_rows.size()) || (source_index.column() >= m->proxy_columns.size()))
+ return QModelIndex();
+ int proxy_row = m->proxy_rows.at(source_index.row());
+ int proxy_column = m->proxy_columns.at(source_index.column());
+ if (proxy_row == -1 || proxy_column == -1)
+ return QModelIndex();
+ return create_index(proxy_row, proxy_column, it);
+}
+
+/*!
+ \internal
+
+ Sorts the existing mappings.
+*/
+void QSortFilterProxyModelPrivate::sort()
+{
+ Q_Q(QSortFilterProxyModel);
+ emit q->layoutAboutToBeChanged();
+ QModelIndexPairList source_indexes = store_persistent_indexes();
+ IndexMap::const_iterator it = source_index_mapping.constBegin();
+ for (; it != source_index_mapping.constEnd(); ++it) {
+ QModelIndex source_parent = it.key();
+ Mapping *m = it.value();
+ sort_source_rows(m->source_rows, source_parent);
+ build_source_to_proxy_mapping(m->source_rows, m->proxy_rows);
+ }
+ update_persistent_indexes(source_indexes);
+ emit q->layoutChanged();
+}
+
+/*!
+ \internal
+
+ update the source_sort_column according to the proxy_sort_column
+ return true if the column was changed
+*/
+bool QSortFilterProxyModelPrivate::update_source_sort_column()
+{
+ Q_Q(QSortFilterProxyModel);
+ QModelIndex proxy_index = q->index(0, proxy_sort_column, QModelIndex());
+ int old_source_sort_colum = source_sort_column;
+ source_sort_column = q->mapToSource(proxy_index).column();
+ return old_source_sort_colum != source_sort_column;
+}
+
+
+/*!
+ \internal
+
+ Sorts the given \a source_rows according to current sort column and order.
+*/
+void QSortFilterProxyModelPrivate::sort_source_rows(
+ QVector<int> &source_rows, const QModelIndex &source_parent) const
+{
+ Q_Q(const QSortFilterProxyModel);
+ if (source_sort_column >= 0) {
+ if (sort_order == Qt::AscendingOrder) {
+ QSortFilterProxyModelLessThan lt(source_sort_column, source_parent, model, q);
+ qStableSort(source_rows.begin(), source_rows.end(), lt);
+ } else {
+ QSortFilterProxyModelGreaterThan gt(source_sort_column, source_parent, model, q);
+ qStableSort(source_rows.begin(), source_rows.end(), gt);
+ }
+ } else { // restore the source model order
+ qStableSort(source_rows.begin(), source_rows.end());
+ }
+}
+
+/*!
+ \internal
+
+ Given source-to-proxy mapping \a source_to_proxy and the set of
+ source items \a source_items (which are part of that mapping),
+ determines the corresponding proxy item intervals that should
+ be removed from the proxy model.
+
+ The result is a vector of pairs, where each pair represents a
+ (start, end) tuple, sorted in ascending order.
+*/
+QVector<QPair<int, int > > QSortFilterProxyModelPrivate::proxy_intervals_for_source_items(
+ const QVector<int> &source_to_proxy, const QVector<int> &source_items) const
+{
+ QVector<QPair<int, int> > proxy_intervals;
+ if (source_items.isEmpty())
+ return proxy_intervals;
+
+ int source_items_index = 0;
+ while (source_items_index < source_items.size()) {
+ int first_proxy_item = source_to_proxy.at(source_items.at(source_items_index));
+ Q_ASSERT(first_proxy_item != -1);
+ int last_proxy_item = first_proxy_item;
+ ++source_items_index;
+ // Find end of interval
+ while ((source_items_index < source_items.size())
+ && (source_to_proxy.at(source_items.at(source_items_index)) == last_proxy_item + 1)) {
+ ++last_proxy_item;
+ ++source_items_index;
+ }
+ // Add interval to result
+ proxy_intervals.append(QPair<int, int>(first_proxy_item, last_proxy_item));
+ }
+ qStableSort(proxy_intervals.begin(), proxy_intervals.end());
+ return proxy_intervals;
+}
+
+/*!
+ \internal
+
+ Given source-to-proxy mapping \a src_to_proxy and proxy-to-source mapping
+ \a proxy_to_source, removes \a source_items from this proxy model.
+ The corresponding proxy items are removed in intervals, so that the proper
+ rows/columnsRemoved(start, end) signals will be generated.
+*/
+void QSortFilterProxyModelPrivate::remove_source_items(
+ QVector<int> &source_to_proxy, QVector<int> &proxy_to_source,
+ const QVector<int> &source_items, const QModelIndex &source_parent,
+ Qt::Orientation orient, bool emit_signal)
+{
+ Q_Q(QSortFilterProxyModel);
+ QModelIndex proxy_parent = q->mapFromSource(source_parent);
+ if (!proxy_parent.isValid() && source_parent.isValid())
+ return; // nothing to do (already removed)
+
+ QVector<QPair<int, int> > proxy_intervals;
+ proxy_intervals = proxy_intervals_for_source_items(source_to_proxy, source_items);
+
+ for (int i = proxy_intervals.size()-1; i >= 0; --i) {
+ QPair<int, int> interval = proxy_intervals.at(i);
+ int proxy_start = interval.first;
+ int proxy_end = interval.second;
+ remove_proxy_interval(source_to_proxy, proxy_to_source, proxy_start, proxy_end,
+ proxy_parent, orient, emit_signal);
+ }
+}
+
+/*!
+ \internal
+
+ Given source-to-proxy mapping \a source_to_proxy and proxy-to-source mapping
+ \a proxy_to_source, removes items from \a proxy_start to \a proxy_end
+ (inclusive) from this proxy model.
+*/
+void QSortFilterProxyModelPrivate::remove_proxy_interval(
+ QVector<int> &source_to_proxy, QVector<int> &proxy_to_source, int proxy_start, int proxy_end,
+ const QModelIndex &proxy_parent, Qt::Orientation orient, bool emit_signal)
+{
+ Q_Q(QSortFilterProxyModel);
+ if (emit_signal) {
+ if (orient == Qt::Vertical)
+ q->beginRemoveRows(proxy_parent, proxy_start, proxy_end);
+ else
+ q->beginRemoveColumns(proxy_parent, proxy_start, proxy_end);
+ }
+
+ // Remove items from proxy-to-source mapping
+ proxy_to_source.remove(proxy_start, proxy_end - proxy_start + 1);
+
+ build_source_to_proxy_mapping(proxy_to_source, source_to_proxy);
+
+ if (emit_signal) {
+ if (orient == Qt::Vertical)
+ q->endRemoveRows();
+ else
+ q->endRemoveColumns();
+ }
+}
+
+/*!
+ \internal
+
+ Given proxy-to-source mapping \a proxy_to_source and a set of
+ unmapped source items \a source_items, determines the proxy item
+ intervals at which the subsets of source items should be inserted
+ (but does not actually add them to the mapping).
+
+ The result is a vector of pairs, each pair representing a tuple (start,
+ items), where items is a vector containing the (sorted) source items that
+ should be inserted at that proxy model location.
+*/
+QVector<QPair<int, QVector<int > > > QSortFilterProxyModelPrivate::proxy_intervals_for_source_items_to_add(
+ const QVector<int> &proxy_to_source, const QVector<int> &source_items,
+ const QModelIndex &source_parent, Qt::Orientation orient) const
+{
+ Q_Q(const QSortFilterProxyModel);
+ QVector<QPair<int, QVector<int> > > proxy_intervals;
+ if (source_items.isEmpty())
+ return proxy_intervals;
+
+ int proxy_low = 0;
+ int proxy_item = 0;
+ int source_items_index = 0;
+ QVector<int> source_items_in_interval;
+ bool compare = (orient == Qt::Vertical && source_sort_column >= 0);
+ while (source_items_index < source_items.size()) {
+ source_items_in_interval.clear();
+ int first_new_source_item = source_items.at(source_items_index);
+ source_items_in_interval.append(first_new_source_item);
+ ++source_items_index;
+
+ // Find proxy item at which insertion should be started
+ int proxy_high = proxy_to_source.size() - 1;
+ QModelIndex i1 = compare ? model->index(first_new_source_item, source_sort_column, source_parent) : QModelIndex();
+ while (proxy_low <= proxy_high) {
+ proxy_item = (proxy_low + proxy_high) / 2;
+ if (compare) {
+ QModelIndex i2 = model->index(proxy_to_source.at(proxy_item), source_sort_column, source_parent);
+ if ((sort_order == Qt::AscendingOrder) ? q->lessThan(i1, i2) : q->lessThan(i2, i1))
+ proxy_high = proxy_item - 1;
+ else
+ proxy_low = proxy_item + 1;
+ } else {
+ if (first_new_source_item < proxy_to_source.at(proxy_item))
+ proxy_high = proxy_item - 1;
+ else
+ proxy_low = proxy_item + 1;
+ }
+ }
+ proxy_item = proxy_low;
+
+ // Find the sequence of new source items that should be inserted here
+ if (proxy_item >= proxy_to_source.size()) {
+ for ( ; source_items_index < source_items.size(); ++source_items_index)
+ source_items_in_interval.append(source_items.at(source_items_index));
+ } else {
+ i1 = compare ? model->index(proxy_to_source.at(proxy_item), source_sort_column, source_parent) : QModelIndex();
+ for ( ; source_items_index < source_items.size(); ++source_items_index) {
+ int new_source_item = source_items.at(source_items_index);
+ if (compare) {
+ QModelIndex i2 = model->index(new_source_item, source_sort_column, source_parent);
+ if ((sort_order == Qt::AscendingOrder) ? q->lessThan(i1, i2) : q->lessThan(i2, i1))
+ break;
+ } else {
+ if (proxy_to_source.at(proxy_item) < new_source_item)
+ break;
+ }
+ source_items_in_interval.append(new_source_item);
+ }
+ }
+
+ // Add interval to result
+ proxy_intervals.append(QPair<int, QVector<int> >(proxy_item, source_items_in_interval));
+ }
+ return proxy_intervals;
+}
+
+/*!
+ \internal
+
+ Given source-to-proxy mapping \a source_to_proxy and proxy-to-source mapping
+ \a proxy_to_source, inserts the given \a source_items into this proxy model.
+ The source items are inserted in intervals (based on some sorted order), so
+ that the proper rows/columnsInserted(start, end) signals will be generated.
+*/
+void QSortFilterProxyModelPrivate::insert_source_items(
+ QVector<int> &source_to_proxy, QVector<int> &proxy_to_source,
+ const QVector<int> &source_items, const QModelIndex &source_parent,
+ Qt::Orientation orient, bool emit_signal)
+{
+ Q_Q(QSortFilterProxyModel);
+ QModelIndex proxy_parent = q->mapFromSource(source_parent);
+ if (!proxy_parent.isValid() && source_parent.isValid())
+ return; // nothing to do (source_parent is not mapped)
+
+ QVector<QPair<int, QVector<int> > > proxy_intervals;
+ proxy_intervals = proxy_intervals_for_source_items_to_add(
+ proxy_to_source, source_items, source_parent, orient);
+
+ for (int i = proxy_intervals.size()-1; i >= 0; --i) {
+ QPair<int, QVector<int> > interval = proxy_intervals.at(i);
+ int proxy_start = interval.first;
+ QVector<int> source_items = interval.second;
+ int proxy_end = proxy_start + source_items.size() - 1;
+
+ if (emit_signal) {
+ if (orient == Qt::Vertical)
+ q->beginInsertRows(proxy_parent, proxy_start, proxy_end);
+ else
+ q->beginInsertColumns(proxy_parent, proxy_start, proxy_end);
+ }
+
+ for (int i = 0; i < source_items.size(); ++i)
+ proxy_to_source.insert(proxy_start + i, source_items.at(i));
+
+ build_source_to_proxy_mapping(proxy_to_source, source_to_proxy);
+
+ if (emit_signal) {
+ if (orient == Qt::Vertical)
+ q->endInsertRows();
+ else
+ q->endInsertColumns();
+ }
+ }
+}
+
+/*!
+ \internal
+
+ Handles source model items insertion (columnsInserted(), rowsInserted()).
+ Determines
+ 1) which of the inserted items to also insert into proxy model (filtering),
+ 2) where to insert the items into the proxy model (sorting),
+ then inserts those items.
+ The items are inserted into the proxy model in intervals (based on
+ sorted order), so that the proper rows/columnsInserted(start, end)
+ signals will be generated.
+*/
+void QSortFilterProxyModelPrivate::source_items_inserted(
+ const QModelIndex &source_parent, int start, int end, Qt::Orientation orient)
+{
+ Q_Q(QSortFilterProxyModel);
+ if ((start < 0) || (end < 0))
+ return;
+ IndexMap::const_iterator it = source_index_mapping.constFind(source_parent);
+ if (it == source_index_mapping.constEnd()) {
+ if (source_parent.isValid()) {
+ QModelIndex source_grand_parent = source_parent.parent();
+ it = source_index_mapping.constFind(source_grand_parent);
+ if (it == source_index_mapping.constEnd()) {
+ // Don't care, since we don't have mapping for the grand parent
+ return;
+ }
+ Mapping *gm = it.value();
+ if (gm->proxy_rows.at(source_parent.row()) == -1 ||
+ gm->proxy_columns.at(source_parent.column()) == -1) {
+ // Don't care, since parent is filtered
+ return;
+ }
+ }
+ it = create_mapping(source_parent);
+ Mapping *m = it.value();
+ QModelIndex proxy_parent = q->mapFromSource(source_parent);
+ if (m->source_rows.count() > 0) {
+ q->beginInsertRows(proxy_parent, 0, m->source_rows.count() - 1);
+ q->endInsertRows();
+ }
+ if (m->source_columns.count() > 0) {
+ q->beginInsertColumns(proxy_parent, 0, m->source_columns.count() - 1);
+ q->endInsertColumns();
+ }
+ return;
+ }
+
+ Mapping *m = it.value();
+ QVector<int> &source_to_proxy = (orient == Qt::Vertical) ? m->proxy_rows : m->proxy_columns;
+ QVector<int> &proxy_to_source = (orient == Qt::Vertical) ? m->source_rows : m->source_columns;
+
+ int delta_item_count = end - start + 1;
+ int old_item_count = source_to_proxy.size();
+
+ updateChildrenMapping(source_parent, m, orient, start, end, delta_item_count, false);
+
+ // Expand source-to-proxy mapping to account for new items
+ if (start < 0 || start > source_to_proxy.size()) {
+ qWarning("QSortFilterProxyModel: invalid inserted rows reported by source model");
+ remove_from_mapping(source_parent);
+ return;
+ }
+ source_to_proxy.insert(start, delta_item_count, -1);
+
+ if (start < old_item_count) {
+ // Adjust existing "stale" indexes in proxy-to-source mapping
+ int proxy_count = proxy_to_source.size();
+ for (int proxy_item = 0; proxy_item < proxy_count; ++proxy_item) {
+ int source_item = proxy_to_source.at(proxy_item);
+ if (source_item >= start)
+ proxy_to_source.replace(proxy_item, source_item + delta_item_count);
+ }
+ build_source_to_proxy_mapping(proxy_to_source, source_to_proxy);
+ }
+
+ // Figure out which items to add to mapping based on filter
+ QVector<int> source_items;
+ for (int i = start; i <= end; ++i) {
+ if ((orient == Qt::Vertical)
+ ? q->filterAcceptsRow(i, source_parent)
+ : q->filterAcceptsColumn(i, source_parent)) {
+ source_items.append(i);
+ }
+ }
+
+ // Sort and insert the items
+ if (orient == Qt::Vertical) // Only sort rows
+ sort_source_rows(source_items, source_parent);
+ insert_source_items(source_to_proxy, proxy_to_source, source_items, source_parent, orient);
+}
+
+/*!
+ \internal
+
+ Handles source model items removal
+ (columnsAboutToBeRemoved(), rowsAboutToBeRemoved()).
+*/
+void QSortFilterProxyModelPrivate::source_items_about_to_be_removed(
+ const QModelIndex &source_parent, int start, int end, Qt::Orientation orient)
+{
+ if ((start < 0) || (end < 0))
+ return;
+ IndexMap::const_iterator it = source_index_mapping.constFind(source_parent);
+ if (it == source_index_mapping.constEnd()) {
+ // Don't care, since we don't have mapping for this index
+ return;
+ }
+
+ Mapping *m = it.value();
+ QVector<int> &source_to_proxy = (orient == Qt::Vertical) ? m->proxy_rows : m->proxy_columns;
+ QVector<int> &proxy_to_source = (orient == Qt::Vertical) ? m->source_rows : m->source_columns;
+
+ // figure out which items to remove
+ QVector<int> source_items_to_remove;
+ int proxy_count = proxy_to_source.size();
+ for (int proxy_item = 0; proxy_item < proxy_count; ++proxy_item) {
+ int source_item = proxy_to_source.at(proxy_item);
+ if ((source_item >= start) && (source_item <= end))
+ source_items_to_remove.append(source_item);
+ }
+
+ remove_source_items(source_to_proxy, proxy_to_source, source_items_to_remove,
+ source_parent, orient);
+}
+
+/*!
+ \internal
+
+ Handles source model items removal (columnsRemoved(), rowsRemoved()).
+*/
+void QSortFilterProxyModelPrivate::source_items_removed(
+ const QModelIndex &source_parent, int start, int end, Qt::Orientation orient)
+{
+ if ((start < 0) || (end < 0))
+ return;
+ IndexMap::const_iterator it = source_index_mapping.constFind(source_parent);
+ if (it == source_index_mapping.constEnd()) {
+ // Don't care, since we don't have mapping for this index
+ return;
+ }
+
+ Mapping *m = it.value();
+ QVector<int> &source_to_proxy = (orient == Qt::Vertical) ? m->proxy_rows : m->proxy_columns;
+ QVector<int> &proxy_to_source = (orient == Qt::Vertical) ? m->source_rows : m->source_columns;
+
+ if (end >= source_to_proxy.size())
+ end = source_to_proxy.size() - 1;
+
+ // Shrink the source-to-proxy mapping to reflect the new item count
+ int delta_item_count = end - start + 1;
+ source_to_proxy.remove(start, delta_item_count);
+
+ int proxy_count = proxy_to_source.size();
+ if (proxy_count > source_to_proxy.size()) {
+ // mapping is in an inconsistent state -- redo the whole mapping
+ qWarning("QSortFilterProxyModel: inconsistent changes reported by source model");
+ remove_from_mapping(source_parent);
+ Q_Q(QSortFilterProxyModel);
+ q->reset();
+ return;
+ }
+
+ // Adjust "stale" indexes in proxy-to-source mapping
+ for (int proxy_item = 0; proxy_item < proxy_count; ++proxy_item) {
+ int source_item = proxy_to_source.at(proxy_item);
+ if (source_item >= start) {
+ Q_ASSERT(source_item - delta_item_count >= 0);
+ proxy_to_source.replace(proxy_item, source_item - delta_item_count);
+ }
+ }
+ build_source_to_proxy_mapping(proxy_to_source, source_to_proxy);
+
+ updateChildrenMapping(source_parent, m, orient, start, end, delta_item_count, true);
+
+}
+
+
+/*!
+ \internal
+ updates the mapping of the children when inserting or removing items
+*/
+void QSortFilterProxyModelPrivate::updateChildrenMapping(const QModelIndex &source_parent, Mapping *parent_mapping,
+ Qt::Orientation orient, int start, int end, int delta_item_count, bool remove)
+{
+ // see if any mapped children should be (re)moved
+ QVector<QPair<QModelIndex, Mapping*> > moved_source_index_mappings;
+ QVector<QModelIndex>::iterator it2 = parent_mapping->mapped_children.begin();
+ for ( ; it2 != parent_mapping->mapped_children.end();) {
+ const QModelIndex source_child_index = *it2;
+ const int pos = (orient == Qt::Vertical)
+ ? source_child_index.row()
+ : source_child_index.column();
+ if (pos < start) {
+ // not affected
+ ++it2;
+ } else if (remove && pos <= end) {
+ // in the removed interval
+ it2 = parent_mapping->mapped_children.erase(it2);
+ remove_from_mapping(source_child_index);
+ } else {
+ // below the removed items -- recompute the index
+ QModelIndex new_index;
+ const int newpos = remove ? pos - delta_item_count : pos + delta_item_count;
+ if (orient == Qt::Vertical) {
+ new_index = model->index(newpos,
+ source_child_index.column(),
+ source_parent);
+ } else {
+ new_index = model->index(source_child_index.row(),
+ newpos,
+ source_parent);
+ }
+ *it2 = new_index;
+ ++it2;
+
+ // update mapping
+ Mapping *cm = source_index_mapping.take(source_child_index);
+ Q_ASSERT(cm);
+ // we do not reinsert right away, because the new index might be identical with another, old index
+ moved_source_index_mappings.append(QPair<QModelIndex, Mapping*>(new_index, cm));
+ }
+ }
+
+ // reinsert moved, mapped indexes
+ QVector<QPair<QModelIndex, Mapping*> >::iterator it = moved_source_index_mappings.begin();
+ for (; it != moved_source_index_mappings.end(); ++it) {
+#ifdef QT_STRICT_ITERATORS
+ source_index_mapping.insert((*it).first, (*it).second);
+ (*it).second->map_iter = source_index_mapping.constFind((*it).first);
+#else
+ (*it).second->map_iter = source_index_mapping.insert((*it).first, (*it).second);
+#endif
+ }
+}
+
+/*!
+ \internal
+*/
+void QSortFilterProxyModelPrivate::proxy_item_range(
+ const QVector<int> &source_to_proxy, const QVector<int> &source_items,
+ int &proxy_low, int &proxy_high) const
+{
+ proxy_low = INT_MAX;
+ proxy_high = INT_MIN;
+ foreach (int source_item, source_items) {
+ int proxy_item = source_to_proxy.at(source_item);
+ Q_ASSERT(proxy_item != -1);
+ if (proxy_item < proxy_low)
+ proxy_low = proxy_item;
+ if (proxy_item > proxy_high)
+ proxy_high = proxy_item;
+ }
+}
+
+/*!
+ \internal
+*/
+void QSortFilterProxyModelPrivate::build_source_to_proxy_mapping(
+ const QVector<int> &proxy_to_source, QVector<int> &source_to_proxy) const
+{
+ source_to_proxy.fill(-1);
+ int proxy_count = proxy_to_source.size();
+ for (int i = 0; i < proxy_count; ++i)
+ source_to_proxy[proxy_to_source.at(i)] = i;
+}
+
+/*!
+ \internal
+
+ Maps the persistent proxy indexes to source indexes and
+ returns the list of source indexes.
+*/
+QModelIndexPairList QSortFilterProxyModelPrivate::store_persistent_indexes()
+{
+ Q_Q(QSortFilterProxyModel);
+ QModelIndexPairList source_indexes;
+ foreach (QPersistentModelIndexData *data, persistent.indexes) {
+ QModelIndex proxy_index = data->index;
+ QModelIndex source_index = q->mapToSource(proxy_index);
+ source_indexes.append(qMakePair(proxy_index, QPersistentModelIndex(source_index)));
+ }
+ return source_indexes;
+}
+
+/*!
+ \internal
+
+ Maps \a source_indexes to proxy indexes and stores those
+ as persistent indexes.
+*/
+void QSortFilterProxyModelPrivate::update_persistent_indexes(
+ const QModelIndexPairList &source_indexes)
+{
+ Q_Q(QSortFilterProxyModel);
+ QModelIndexList from, to;
+ for (int i = 0; i < source_indexes.count(); ++i) {
+ QModelIndex source_index = source_indexes.at(i).second;
+ QModelIndex old_proxy_index = source_indexes.at(i).first;
+ create_mapping(source_index.parent());
+ QModelIndex proxy_index = q->mapFromSource(source_index);
+ from << old_proxy_index;
+ to << proxy_index;
+ }
+ q->changePersistentIndexList(from, to);
+}
+
+/*!
+ \internal
+
+ Updates the proxy model (adds/removes rows) based on the
+ new filter.
+*/
+void QSortFilterProxyModelPrivate::filter_changed()
+{
+ QMap<QModelIndex, Mapping *>::const_iterator it;
+ for (it = source_index_mapping.constBegin(); it != source_index_mapping.constEnd(); ++it) {
+ QModelIndex source_parent = it.key();
+ Mapping *m = it.value();
+ handle_filter_changed(m->proxy_rows, m->source_rows, source_parent, Qt::Vertical);
+ handle_filter_changed(m->proxy_columns, m->source_columns, source_parent, Qt::Horizontal);
+ }
+}
+
+/*!
+ \internal
+*/
+void QSortFilterProxyModelPrivate::handle_filter_changed(
+ QVector<int> &source_to_proxy, QVector<int> &proxy_to_source,
+ const QModelIndex &source_parent, Qt::Orientation orient)
+{
+ Q_Q(QSortFilterProxyModel);
+ // Figure out which mapped items to remove
+ QVector<int> source_items_remove;
+ foreach (int source_item, proxy_to_source) {
+ if ((orient == Qt::Vertical)
+ ? !q->filterAcceptsRow(source_item, source_parent)
+ : !q->filterAcceptsColumn(source_item, source_parent)) {
+ // This source item does not satisfy the filter, so it must be removed
+ source_items_remove.append(source_item);
+ }
+ }
+ // Figure out which non-mapped items to insert
+ QVector<int> source_items_insert;
+ int source_count = source_to_proxy.size();
+ for (int source_item = 0; source_item < source_count; ++source_item) {
+ if (source_to_proxy.at(source_item) == -1) {
+ if ((orient == Qt::Vertical)
+ ? q->filterAcceptsRow(source_item, source_parent)
+ : q->filterAcceptsColumn(source_item, source_parent)) {
+ // This source item satisfies the filter, so it must be added
+ source_items_insert.append(source_item);
+ }
+ }
+ }
+ if (!source_items_remove.isEmpty() || !source_items_insert.isEmpty()) {
+ // Do item removal and insertion
+ remove_source_items(source_to_proxy, proxy_to_source,
+ source_items_remove, source_parent, orient);
+ if (orient == Qt::Vertical)
+ sort_source_rows(source_items_insert, source_parent);
+ insert_source_items(source_to_proxy, proxy_to_source,
+ source_items_insert, source_parent, orient);
+ }
+}
+
+void QSortFilterProxyModelPrivate::_q_sourceDataChanged(const QModelIndex &source_top_left,
+ const QModelIndex &source_bottom_right)
+{
+ Q_Q(QSortFilterProxyModel);
+ if (!source_top_left.isValid() || !source_bottom_right.isValid())
+ return;
+ QModelIndex source_parent = source_top_left.parent();
+ IndexMap::const_iterator it = create_mapping(source_parent);
+ if (it == source_index_mapping.constEnd()) {
+ // Don't care, since we don't have mapping for this index
+ return;
+ }
+ Mapping *m = it.value();
+
+ // Figure out how the source changes affect us
+ QVector<int> source_rows_remove;
+ QVector<int> source_rows_insert;
+ QVector<int> source_rows_change;
+ QVector<int> source_rows_resort;
+ int end = qMin(source_bottom_right.row(), m->proxy_rows.count() - 1);
+ for (int source_row = source_top_left.row(); source_row <= end; ++source_row) {
+ if (dynamic_sortfilter) {
+ if (m->proxy_rows.at(source_row) != -1) {
+ if (!q->filterAcceptsRow(source_row, source_parent)) {
+ // This source row no longer satisfies the filter, so it must be removed
+ source_rows_remove.append(source_row);
+ } else if (source_sort_column >= source_top_left.column() && source_sort_column <= source_bottom_right.column()) {
+ // This source row has changed in a way that may affect sorted order
+ source_rows_resort.append(source_row);
+ } else {
+ // This row has simply changed, without affecting filtering nor sorting
+ source_rows_change.append(source_row);
+ }
+ } else {
+ if (q->filterAcceptsRow(source_row, source_parent)) {
+ // This source row now satisfies the filter, so it must be added
+ source_rows_insert.append(source_row);
+ }
+ }
+ } else {
+ if (m->proxy_rows.at(source_row) != -1)
+ source_rows_change.append(source_row);
+ }
+ }
+
+ if (!source_rows_remove.isEmpty())
+ remove_source_items(m->proxy_rows, m->source_rows,
+ source_rows_remove, source_parent, Qt::Vertical);
+
+ if (!source_rows_resort.isEmpty()) {
+ // Re-sort the rows
+ emit q->layoutAboutToBeChanged();
+ QModelIndexPairList source_indexes = store_persistent_indexes();
+ remove_source_items(m->proxy_rows, m->source_rows, source_rows_resort,
+ source_parent, Qt::Vertical, false);
+ sort_source_rows(source_rows_resort, source_parent);
+ insert_source_items(m->proxy_rows, m->source_rows, source_rows_resort,
+ source_parent, Qt::Vertical, false);
+ update_persistent_indexes(source_indexes);
+ emit q->layoutChanged();
+ // Make sure we also emit dataChanged for the rows
+ source_rows_change += source_rows_resort;
+ }
+
+ if (!source_rows_change.isEmpty()) {
+ // Find the proxy row range
+ int proxy_start_row;
+ int proxy_end_row;
+ proxy_item_range(m->proxy_rows, source_rows_change,
+ proxy_start_row, proxy_end_row);
+ // ### Find the proxy column range also
+ if (proxy_end_row >= 0) {
+ // the row was accepted, but some columns might still be filtered out
+ int source_left_column = source_top_left.column();
+ while (source_left_column < source_bottom_right.column()
+ && m->proxy_columns.at(source_left_column) == -1)
+ ++source_left_column;
+ const QModelIndex proxy_top_left = create_index(
+ proxy_start_row, m->proxy_columns.at(source_left_column), it);
+ int source_right_column = source_bottom_right.column();
+ while (source_right_column > source_top_left.column()
+ && m->proxy_columns.at(source_right_column) == -1)
+ --source_right_column;
+ const QModelIndex proxy_bottom_right = create_index(
+ proxy_end_row, m->proxy_columns.at(source_right_column), it);
+ emit q->dataChanged(proxy_top_left, proxy_bottom_right);
+ }
+ }
+
+ if (!source_rows_insert.isEmpty()) {
+ sort_source_rows(source_rows_insert, source_parent);
+ insert_source_items(m->proxy_rows, m->source_rows,
+ source_rows_insert, source_parent, Qt::Vertical);
+ }
+}
+
+void QSortFilterProxyModelPrivate::_q_sourceHeaderDataChanged(Qt::Orientation orientation,
+ int start, int end)
+{
+ Q_Q(QSortFilterProxyModel);
+ Mapping *m = create_mapping(QModelIndex()).value();
+ int proxy_start = (orientation == Qt::Vertical
+ ? m->proxy_rows.at(start)
+ : m->proxy_columns.at(start));
+ int proxy_end = (orientation == Qt::Vertical
+ ? m->proxy_rows.at(end)
+ : m->proxy_columns.at(end));
+ emit q->headerDataChanged(orientation, proxy_start, proxy_end);
+}
+
+void QSortFilterProxyModelPrivate::_q_sourceReset()
+{
+ Q_Q(QSortFilterProxyModel);
+ // All internal structures are deleted in clear()
+ q->reset();
+ update_source_sort_column();
+}
+
+void QSortFilterProxyModelPrivate::_q_sourceLayoutAboutToBeChanged()
+{
+ Q_Q(QSortFilterProxyModel);
+ saved_persistent_indexes.clear();
+ if (persistent.indexes.isEmpty())
+ return;
+ emit q->layoutAboutToBeChanged();
+ saved_persistent_indexes = store_persistent_indexes();
+}
+
+void QSortFilterProxyModelPrivate::_q_sourceLayoutChanged()
+{
+ Q_Q(QSortFilterProxyModel);
+ if (saved_persistent_indexes.isEmpty()) {
+ q->invalidate();
+ return;
+ }
+
+ qDeleteAll(source_index_mapping);
+ source_index_mapping.clear();
+
+ update_persistent_indexes(saved_persistent_indexes);
+ saved_persistent_indexes.clear();
+
+ update_source_sort_column();
+
+ emit q->layoutChanged();
+}
+
+void QSortFilterProxyModelPrivate::_q_sourceRowsAboutToBeInserted(
+ const QModelIndex &source_parent, int start, int end)
+{
+ Q_UNUSED(start);
+ Q_UNUSED(end);
+ //Force the creation of a mapping now, even if its empty.
+ //We need it because the proxy can be acessed at the moment it emits rowsAboutToBeInserted in insert_source_items
+ create_mapping(source_parent);
+}
+
+void QSortFilterProxyModelPrivate::_q_sourceRowsInserted(
+ const QModelIndex &source_parent, int start, int end)
+{
+ source_items_inserted(source_parent, start, end, Qt::Vertical);
+ if (update_source_sort_column()) //previous call to update_source_sort_column may fail if the model has no column.
+ sort(); // now it should succeed so we need to make sure to sort again
+}
+
+void QSortFilterProxyModelPrivate::_q_sourceRowsAboutToBeRemoved(
+ const QModelIndex &source_parent, int start, int end)
+{
+ source_items_about_to_be_removed(source_parent, start, end,
+ Qt::Vertical);
+}
+
+void QSortFilterProxyModelPrivate::_q_sourceRowsRemoved(
+ const QModelIndex &source_parent, int start, int end)
+{
+ source_items_removed(source_parent, start, end, Qt::Vertical);
+}
+
+void QSortFilterProxyModelPrivate::_q_sourceColumnsAboutToBeInserted(
+ const QModelIndex &source_parent, int start, int end)
+{
+ Q_UNUSED(start);
+ Q_UNUSED(end);
+ //Force the creation of a mapping now, even if its empty.
+ //We need it because the proxy can be acessed at the moment it emits columnsAboutToBeInserted in insert_source_items
+ create_mapping(source_parent);
+}
+
+void QSortFilterProxyModelPrivate::_q_sourceColumnsInserted(
+ const QModelIndex &source_parent, int start, int end)
+{
+ Q_Q(const QSortFilterProxyModel);
+ source_items_inserted(source_parent, start, end, Qt::Horizontal);
+
+ if (source_parent.isValid())
+ return; //we sort according to the root column only
+ if (source_sort_column == -1) {
+ //we update the source_sort_column depending on the prox_sort_column
+ if (update_source_sort_column())
+ sort();
+ } else {
+ if (start <= source_sort_column)
+ source_sort_column += end - start + 1;
+
+ proxy_sort_column = q->mapFromSource(model->index(0,source_sort_column, source_parent)).column();
+ }
+}
+
+void QSortFilterProxyModelPrivate::_q_sourceColumnsAboutToBeRemoved(
+ const QModelIndex &source_parent, int start, int end)
+{
+ source_items_about_to_be_removed(source_parent, start, end,
+ Qt::Horizontal);
+}
+
+void QSortFilterProxyModelPrivate::_q_sourceColumnsRemoved(
+ const QModelIndex &source_parent, int start, int end)
+{
+ Q_Q(const QSortFilterProxyModel);
+ source_items_removed(source_parent, start, end, Qt::Horizontal);
+
+ if (source_parent.isValid())
+ return; //we sort according to the root column only
+ if (start <= source_sort_column) {
+ if (end < source_sort_column)
+ source_sort_column -= end - start + 1;
+ else
+ source_sort_column = -1;
+ }
+
+ proxy_sort_column = q->mapFromSource(model->index(0,source_sort_column, source_parent)).column();
+}
+
+/*!
+ \since 4.1
+ \class QSortFilterProxyModel
+ \brief The QSortFilterProxyModel class provides support for sorting and filtering data passed
+ between another model and a view.
+
+ \ingroup model-view
+
+ QSortFilterProxyModel can be used for sorting items, filtering
+ out items, or both. The model transforms the structure of a
+ source model by mapping the model indexes it supplies to new
+ indexes, corresponding to different locations, for views to use.
+ This approach allows a given source model to be restructured as
+ far as views are concerned without requiring any transformations
+ on the underlying data, and without duplicating the data in
+ memory.
+
+ Let's assume that we want to sort and filter the items provided
+ by a custom model. The code to set up the model and the view, \e
+ without sorting and filtering, would look like this:
+
+ \snippet doc/src/snippets/qsortfilterproxymodel-details/main.cpp 1
+
+ To add sorting and filtering support to \c MyItemModel, we need
+ to create a QSortFilterProxyModel, call setSourceModel() with the
+ \c MyItemModel as argument, and install the QSortFilterProxyModel
+ on the view:
+
+ \snippet doc/src/snippets/qsortfilterproxymodel-details/main.cpp 0
+ \snippet doc/src/snippets/qsortfilterproxymodel-details/main.cpp 2
+
+ At this point, neither sorting nor filtering is enabled; the
+ original data is displayed in the view. Any changes made through
+ the QSortFilterProxyModel are applied to the original model.
+
+ The QSortFilterProxyModel acts as a wrapper for the original
+ model. If you need to convert source \l{QModelIndex}es to
+ sorted/filtered model indexes or vice versa, use mapToSource(),
+ mapFromSource(), mapSelectionToSource(), and
+ mapSelectionFromSource().
+
+ \note By default, the model does not dynamically re-sort and re-filter
+ data whenever the original model changes. This behavior can be
+ changed by setting the \l{QSortFilterProxyModel::dynamicSortFilter}
+ {dynamicSortFilter} property.
+
+ The \l{itemviews/basicsortfiltermodel}{Basic Sort/Filter Model}
+ and \l{itemviews/customsortfiltermodel}{Custom Sort/Filter Model}
+ examples illustrate how to use QSortFilterProxyModel to perform
+ basic sorting and filtering and how to subclass it to implement
+ custom behavior.
+
+ \section1 Sorting
+
+ QTableView and QTreeView have a
+ \l{QTreeView::sortingEnabled}{sortingEnabled} property that
+ controls whether the user can sort the view by clicking the
+ view's horizontal header. For example:
+
+ \snippet doc/src/snippets/qsortfilterproxymodel-details/main.cpp 3
+
+ When this feature is on (the default is off), clicking on a
+ header section sorts the items according to that column. By
+ clicking repeatedly, the user can alternate between ascending and
+ descending order.
+
+ \image qsortfilterproxymodel-sorting.png A sorted QTreeView
+
+ Behind the scene, the view calls the sort() virtual function on
+ the model to reorder the data in the model. To make your data
+ sortable, you can either implement sort() in your model, or you
+ use a QSortFilterProxyModel to wrap your model --
+ QSortFilterProxyModel provides a generic sort() reimplementation
+ that operates on the sortRole() (Qt::DisplayRole by default) of
+ the items and that understands several data types, including \c
+ int, QString, and QDateTime. For hierarchical models, sorting is
+ applied recursively to all child items. String comparisons are
+ case sensitive by default; this can be changed by setting the
+ \l{QSortFilterProxyModel::}{sortCaseSensitivity} property.
+
+ Custom sorting behavior is achieved by subclassing
+ QSortFilterProxyModel and reimplementing lessThan(), which is
+ used to compare items. For example:
+
+ \snippet examples/itemviews/customsortfiltermodel/mysortfilterproxymodel.cpp 5
+
+ (This code snippet comes from the
+ \l{itemviews/customsortfiltermodel}{Custom Sort/Filter Model}
+ example.)
+
+ An alternative approach to sorting is to disable sorting on the
+ view and to impose a certain order to the user. This is done by
+ explicitly calling sort() with the desired column and order as
+ arguments on the QSortFilterProxyModel (or on the original model
+ if it implements sort()). For example:
+
+ \snippet doc/src/snippets/qsortfilterproxymodel-details/main.cpp 4
+
+ QSortFilterProxyModel can be sorted by column -1, in which case it
+ returns to the sort order of the underlying source model.
+
+ \section1 Filtering
+
+ In addition to sorting, QSortFilterProxyModel can be used to hide
+ items that don't match a certain filter. The filter is specified
+ using a QRegExp object and is applied to the filterRole()
+ (Qt::DisplayRole by default) of each item, for a given column.
+ The QRegExp object can be used to match a regular expression, a
+ wildcard pattern, or a fixed string. For example:
+
+ \snippet doc/src/snippets/qsortfilterproxymodel-details/main.cpp 5
+
+ For hierarchical models, the filter is applied recursively to all
+ children. If a parent item doesn't match the filter, none of its
+ children will be shown.
+
+ A common use case is to let the user specify the filter regexp,
+ wildcard pattern, or fixed string in a QLineEdit and to connect
+ the \l{QLineEdit::textChanged()}{textChanged()} signal to
+ setFilterRegExp(), setFilterWildcard(), or setFilterFixedString()
+ to reapply the filter.
+
+ Custom filtering behavior can be achieved by reimplementing the
+ filterAcceptsRow() and filterAcceptsColumn() functions. For
+ example, the following implementation ignores the
+ \l{QSortFilterProxyModel::filterKeyColumn}{filterKeyColumn}
+ property and performs filtering on columns 0, 1, and 2:
+
+ \snippet examples/itemviews/customsortfiltermodel/mysortfilterproxymodel.cpp 3
+
+ (This code snippet comes from the
+ \l{itemviews/customsortfiltermodel}{Custom Sort/Filter Model}
+ example.)
+
+ If you are working with large amounts of filtering and have to invoke
+ invalidateFilter() repeatedly, using reset() may be more efficient,
+ depending on the implementation of your model. However, note that reset()
+ returns the proxy model to its original state, losing selection
+ information, and will cause the proxy model to be repopulated.
+
+ \section1 Subclassing
+
+ \bold{Note:} Some general guidelines for subclassing models are
+ available in the \l{Model Subclassing Reference}.
+
+ Since QAbstractProxyModel and its subclasses are derived from
+ QAbstractItemModel, much of the same advice about subclassing normal
+ models also applies to proxy models. In addition, it is worth noting
+ that many of the default implementations of functions in this class
+ are written so that they call the equivalent functions in the relevant
+ source model. This simple proxying mechanism may need to be overridden
+ for source models with more complex behavior; for example, if the
+ source model provides a custom hasChildren() implementation, you
+ should also provide one in the proxy model.
+
+ \sa QAbstractProxyModel, QAbstractItemModel, {Model/View Programming},
+ {Basic Sort/Filter Model Example}, {Custom Sort/Filter Model Example}
+*/
+
+/*!
+ Constructs a sorting filter model with the given \a parent.
+*/
+
+QSortFilterProxyModel::QSortFilterProxyModel(QObject *parent)
+ : QAbstractProxyModel(*new QSortFilterProxyModelPrivate, parent)
+{
+ Q_D(QSortFilterProxyModel);
+ d->proxy_sort_column = d->source_sort_column = -1;
+ d->sort_order = Qt::AscendingOrder;
+ d->sort_casesensitivity = Qt::CaseSensitive;
+ d->sort_role = Qt::DisplayRole;
+ d->sort_localeaware = false;
+ d->filter_column = 0;
+ d->filter_role = Qt::DisplayRole;
+ d->dynamic_sortfilter = false;
+ connect(this, SIGNAL(modelReset()), this, SLOT(invalidate()));
+}
+
+/*!
+ Destroys this sorting filter model.
+*/
+QSortFilterProxyModel::~QSortFilterProxyModel()
+{
+ Q_D(QSortFilterProxyModel);
+ qDeleteAll(d->source_index_mapping);
+ d->source_index_mapping.clear();
+}
+
+/*!
+ \reimp
+*/
+void QSortFilterProxyModel::setSourceModel(QAbstractItemModel *sourceModel)
+{
+ Q_D(QSortFilterProxyModel);
+
+ disconnect(d->model, SIGNAL(dataChanged(QModelIndex,QModelIndex)),
+ this, SLOT(_q_sourceDataChanged(QModelIndex,QModelIndex)));
+
+ disconnect(d->model, SIGNAL(headerDataChanged(Qt::Orientation,int,int)),
+ this, SLOT(_q_sourceHeaderDataChanged(Qt::Orientation,int,int)));
+
+ disconnect(d->model, SIGNAL(rowsAboutToBeInserted(QModelIndex,int,int)),
+ this, SLOT(_q_sourceRowsAboutToBeInserted(QModelIndex,int,int)));
+
+ disconnect(d->model, SIGNAL(rowsInserted(QModelIndex,int,int)),
+ this, SLOT(_q_sourceRowsInserted(QModelIndex,int,int)));
+
+ disconnect(d->model, SIGNAL(columnsAboutToBeInserted(QModelIndex,int,int)),
+ this, SLOT(_q_sourceColumnsAboutToBeInserted(QModelIndex,int,int)));
+
+ disconnect(d->model, SIGNAL(columnsInserted(QModelIndex,int,int)),
+ this, SLOT(_q_sourceColumnsInserted(QModelIndex,int,int)));
+
+ disconnect(d->model, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)),
+ this, SLOT(_q_sourceRowsAboutToBeRemoved(QModelIndex,int,int)));
+
+ disconnect(d->model, SIGNAL(rowsRemoved(QModelIndex,int,int)),
+ this, SLOT(_q_sourceRowsRemoved(QModelIndex,int,int)));
+
+ disconnect(d->model, SIGNAL(columnsAboutToBeRemoved(QModelIndex,int,int)),
+ this, SLOT(_q_sourceColumnsAboutToBeRemoved(QModelIndex,int,int)));
+
+ disconnect(d->model, SIGNAL(columnsRemoved(QModelIndex,int,int)),
+ this, SLOT(_q_sourceColumnsRemoved(QModelIndex,int,int)));
+
+ disconnect(d->model, SIGNAL(layoutAboutToBeChanged()),
+ this, SLOT(_q_sourceLayoutAboutToBeChanged()));
+
+ disconnect(d->model, SIGNAL(layoutChanged()),
+ this, SLOT(_q_sourceLayoutChanged()));
+
+ disconnect(d->model, SIGNAL(modelReset()), this, SLOT(_q_sourceReset()));
+
+ QAbstractProxyModel::setSourceModel(sourceModel);
+
+ connect(d->model, SIGNAL(dataChanged(QModelIndex,QModelIndex)),
+ this, SLOT(_q_sourceDataChanged(QModelIndex,QModelIndex)));
+
+ connect(d->model, SIGNAL(headerDataChanged(Qt::Orientation,int,int)),
+ this, SLOT(_q_sourceHeaderDataChanged(Qt::Orientation,int,int)));
+
+ connect(d->model, SIGNAL(rowsAboutToBeInserted(QModelIndex,int,int)),
+ this, SLOT(_q_sourceRowsAboutToBeInserted(QModelIndex,int,int)));
+
+ connect(d->model, SIGNAL(rowsInserted(QModelIndex,int,int)),
+ this, SLOT(_q_sourceRowsInserted(QModelIndex,int,int)));
+
+ connect(d->model, SIGNAL(columnsAboutToBeInserted(QModelIndex,int,int)),
+ this, SLOT(_q_sourceColumnsAboutToBeInserted(QModelIndex,int,int)));
+
+ connect(d->model, SIGNAL(columnsInserted(QModelIndex,int,int)),
+ this, SLOT(_q_sourceColumnsInserted(QModelIndex,int,int)));
+
+ connect(d->model, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)),
+ this, SLOT(_q_sourceRowsAboutToBeRemoved(QModelIndex,int,int)));
+
+ connect(d->model, SIGNAL(rowsRemoved(QModelIndex,int,int)),
+ this, SLOT(_q_sourceRowsRemoved(QModelIndex,int,int)));
+
+ connect(d->model, SIGNAL(columnsAboutToBeRemoved(QModelIndex,int,int)),
+ this, SLOT(_q_sourceColumnsAboutToBeRemoved(QModelIndex,int,int)));
+
+ connect(d->model, SIGNAL(columnsRemoved(QModelIndex,int,int)),
+ this, SLOT(_q_sourceColumnsRemoved(QModelIndex,int,int)));
+
+ connect(d->model, SIGNAL(layoutAboutToBeChanged()),
+ this, SLOT(_q_sourceLayoutAboutToBeChanged()));
+
+ connect(d->model, SIGNAL(layoutChanged()),
+ this, SLOT(_q_sourceLayoutChanged()));
+
+ connect(d->model, SIGNAL(modelReset()), this, SLOT(_q_sourceReset()));
+
+ d->clear_mapping();
+ reset();
+}
+
+/*!
+ \reimp
+*/
+QModelIndex QSortFilterProxyModel::index(int row, int column, const QModelIndex &parent) const
+{
+ Q_D(const QSortFilterProxyModel);
+ if (row < 0 || column < 0)
+ return QModelIndex();
+
+ QModelIndex source_parent = mapToSource(parent); // parent is already mapped at this point
+ IndexMap::const_iterator it = d->create_mapping(source_parent); // but make sure that the children are mapped
+ if (it.value()->source_rows.count() <= row || it.value()->source_columns.count() <= column)
+ return QModelIndex();
+
+ return d->create_index(row, column, it);
+}
+
+/*!
+ \reimp
+*/
+QModelIndex QSortFilterProxyModel::parent(const QModelIndex &child) const
+{
+ Q_D(const QSortFilterProxyModel);
+ if (!d->indexValid(child))
+ return QModelIndex();
+ IndexMap::const_iterator it = d->index_to_iterator(child);
+ Q_ASSERT(it != d->source_index_mapping.constEnd());
+ QModelIndex source_parent = it.key();
+ QModelIndex proxy_parent = mapFromSource(source_parent);
+ return proxy_parent;
+}
+
+/*!
+ \reimp
+*/
+int QSortFilterProxyModel::rowCount(const QModelIndex &parent) const
+{
+ Q_D(const QSortFilterProxyModel);
+ QModelIndex source_parent = mapToSource(parent);
+ if (parent.isValid() && !source_parent.isValid())
+ return 0;
+ IndexMap::const_iterator it = d->create_mapping(source_parent);
+ return it.value()->source_rows.count();
+}
+
+/*!
+ \reimp
+*/
+int QSortFilterProxyModel::columnCount(const QModelIndex &parent) const
+{
+ Q_D(const QSortFilterProxyModel);
+ QModelIndex source_parent = mapToSource(parent);
+ if (parent.isValid() && !source_parent.isValid())
+ return 0;
+ IndexMap::const_iterator it = d->create_mapping(source_parent);
+ return it.value()->source_columns.count();
+}
+
+/*!
+ \reimp
+*/
+bool QSortFilterProxyModel::hasChildren(const QModelIndex &parent) const
+{
+ Q_D(const QSortFilterProxyModel);
+ QModelIndex source_parent = mapToSource(parent);
+ if (parent.isValid() && !source_parent.isValid())
+ return false;
+ if (!d->model->hasChildren(source_parent))
+ return false;
+ QSortFilterProxyModelPrivate::Mapping *m = d->create_mapping(source_parent).value();
+ return m->source_rows.count() != 0 && m->source_columns.count() != 0;
+}
+
+/*!
+ \reimp
+*/
+QVariant QSortFilterProxyModel::data(const QModelIndex &index, int role) const
+{
+ Q_D(const QSortFilterProxyModel);
+ QModelIndex source_index = mapToSource(index);
+ if (index.isValid() && !source_index.isValid())
+ return QVariant();
+ return d->model->data(source_index, role);
+}
+
+/*!
+ \reimp
+*/
+bool QSortFilterProxyModel::setData(const QModelIndex &index, const QVariant &value, int role)
+{
+ Q_D(QSortFilterProxyModel);
+ QModelIndex source_index = mapToSource(index);
+ if (index.isValid() && !source_index.isValid())
+ return false;
+ return d->model->setData(source_index, value, role);
+}
+
+/*!
+ \reimp
+*/
+QVariant QSortFilterProxyModel::headerData(int section, Qt::Orientation orientation, int role) const
+{
+ Q_D(const QSortFilterProxyModel);
+ IndexMap::const_iterator it = d->create_mapping(QModelIndex());
+ if (it.value()->source_rows.count() * it.value()->source_columns.count() > 0)
+ return QAbstractProxyModel::headerData(section, orientation, role);
+ int source_section;
+ if (orientation == Qt::Vertical) {
+ if (section < 0 || section >= it.value()->source_rows.count())
+ return QVariant();
+ source_section = it.value()->source_rows.at(section);
+ } else {
+ if (section < 0 || section >= it.value()->source_columns.count())
+ return QVariant();
+ source_section = it.value()->source_columns.at(section);
+ }
+ return d->model->headerData(source_section, orientation, role);
+}
+
+/*!
+ \reimp
+*/
+bool QSortFilterProxyModel::setHeaderData(int section, Qt::Orientation orientation,
+ const QVariant &value, int role)
+{
+ Q_D(QSortFilterProxyModel);
+ IndexMap::const_iterator it = d->create_mapping(QModelIndex());
+ if (it.value()->source_rows.count() * it.value()->source_columns.count() > 0)
+ return QAbstractProxyModel::setHeaderData(section, orientation, value, role);
+ int source_section;
+ if (orientation == Qt::Vertical) {
+ if (section < 0 || section >= it.value()->source_rows.count())
+ return false;
+ source_section = it.value()->source_rows.at(section);
+ } else {
+ if (section < 0 || section >= it.value()->source_columns.count())
+ return false;
+ source_section = it.value()->source_columns.at(section);
+ }
+ return d->model->setHeaderData(source_section, orientation, value, role);
+}
+
+/*!
+ \reimp
+*/
+QMimeData *QSortFilterProxyModel::mimeData(const QModelIndexList &indexes) const
+{
+ Q_D(const QSortFilterProxyModel);
+ QModelIndexList source_indexes;
+ for (int i = 0; i < indexes.count(); ++i)
+ source_indexes << mapToSource(indexes.at(i));
+ return d->model->mimeData(source_indexes);
+}
+
+/*!
+ \reimp
+*/
+QStringList QSortFilterProxyModel::mimeTypes() const
+{
+ Q_D(const QSortFilterProxyModel);
+ return d->model->mimeTypes();
+}
+
+/*!
+ \reimp
+*/
+Qt::DropActions QSortFilterProxyModel::supportedDropActions() const
+{
+ Q_D(const QSortFilterProxyModel);
+ return d->model->supportedDropActions();
+}
+
+/*!
+ \reimp
+*/
+bool QSortFilterProxyModel::dropMimeData(const QMimeData *data, Qt::DropAction action,
+ int row, int column, const QModelIndex &parent)
+{
+ Q_D(QSortFilterProxyModel);
+ if ((row == -1) && (column == -1))
+ return d->model->dropMimeData(data, action, -1, -1, mapToSource(parent));
+ int source_destination_row = -1;
+ int source_destination_column = -1;
+ QModelIndex source_parent;
+ if (row == rowCount(parent)) {
+ source_parent = mapToSource(parent);
+ source_destination_row = d->model->rowCount(source_parent);
+ } else {
+ QModelIndex proxy_index = index(row, column, parent);
+ QModelIndex source_index = mapToSource(proxy_index);
+ source_destination_row = source_index.row();
+ source_destination_column = source_index.column();
+ source_parent = source_index.parent();
+ }
+ return d->model->dropMimeData(data, action, source_destination_row,
+ source_destination_column, source_parent);
+}
+
+/*!
+ \reimp
+*/
+bool QSortFilterProxyModel::insertRows(int row, int count, const QModelIndex &parent)
+{
+ Q_D(QSortFilterProxyModel);
+ if (row < 0 || count <= 0)
+ return false;
+ QModelIndex source_parent = mapToSource(parent);
+ if (parent.isValid() && !source_parent.isValid())
+ return false;
+ QSortFilterProxyModelPrivate::Mapping *m = d->create_mapping(source_parent).value();
+ if (row > m->source_rows.count())
+ return false;
+ int source_row = (row >= m->source_rows.count()
+ ? m->source_rows.count()
+ : m->source_rows.at(row));
+ return d->model->insertRows(source_row, count, source_parent);
+}
+
+/*!
+ \reimp
+*/
+bool QSortFilterProxyModel::insertColumns(int column, int count, const QModelIndex &parent)
+{
+ Q_D(QSortFilterProxyModel);
+ if (column < 0|| count <= 0)
+ return false;
+ QModelIndex source_parent = mapToSource(parent);
+ if (parent.isValid() && !source_parent.isValid())
+ return false;
+ QSortFilterProxyModelPrivate::Mapping *m = d->create_mapping(source_parent).value();
+ if (column > m->source_columns.count())
+ return false;
+ int source_column = (column >= m->source_columns.count()
+ ? m->source_columns.count()
+ : m->source_columns.at(column));
+ return d->model->insertColumns(source_column, count, source_parent);
+}
+
+/*!
+ \reimp
+*/
+bool QSortFilterProxyModel::removeRows(int row, int count, const QModelIndex &parent)
+{
+ Q_D(QSortFilterProxyModel);
+ if (row < 0 || count <= 0)
+ return false;
+ QModelIndex source_parent = mapToSource(parent);
+ if (parent.isValid() && !source_parent.isValid())
+ return false;
+ QSortFilterProxyModelPrivate::Mapping *m = d->create_mapping(source_parent).value();
+ if (row + count > m->source_rows.count())
+ return false;
+ if ((count == 1)
+ || ((d->source_sort_column < 0) && (m->proxy_rows.count() == m->source_rows.count()))) {
+ int source_row = m->source_rows.at(row);
+ return d->model->removeRows(source_row, count, source_parent);
+ }
+ // remove corresponding source intervals
+ // ### if this proves to be slow, we can switch to single-row removal
+ QVector<int> rows;
+ for (int i = row; i < row + count; ++i)
+ rows.append(m->source_rows.at(i));
+ qSort(rows.begin(), rows.end());
+
+ int pos = rows.count() - 1;
+ bool ok = true;
+ while (pos >= 0) {
+ const int source_end = rows.at(pos--);
+ int source_start = source_end;
+ while ((pos >= 0) && (rows.at(pos) == (source_start - 1))) {
+ --source_start;
+ --pos;
+ }
+ ok = ok && d->model->removeRows(source_start, source_end - source_start + 1,
+ source_parent);
+ }
+ return ok;
+}
+
+/*!
+ \reimp
+*/
+bool QSortFilterProxyModel::removeColumns(int column, int count, const QModelIndex &parent)
+{
+ Q_D(QSortFilterProxyModel);
+ if (column < 0 || count <= 0)
+ return false;
+ QModelIndex source_parent = mapToSource(parent);
+ if (parent.isValid() && !source_parent.isValid())
+ return false;
+ QSortFilterProxyModelPrivate::Mapping *m = d->create_mapping(source_parent).value();
+ if (column + count > m->source_columns.count())
+ return false;
+ if ((count == 1) || (m->proxy_columns.count() == m->source_columns.count())) {
+ int source_column = m->source_columns.at(column);
+ return d->model->removeColumns(source_column, count, source_parent);
+ }
+ // remove corresponding source intervals
+ QVector<int> columns;
+ for (int i = column; i < column + count; ++i)
+ columns.append(m->source_columns.at(i));
+
+ int pos = columns.count() - 1;
+ bool ok = true;
+ while (pos >= 0) {
+ const int source_end = columns.at(pos--);
+ int source_start = source_end;
+ while ((pos >= 0) && (columns.at(pos) == (source_start - 1))) {
+ --source_start;
+ --pos;
+ }
+ ok = ok && d->model->removeColumns(source_start, source_end - source_start + 1,
+ source_parent);
+ }
+ return ok;
+}
+
+/*!
+ \reimp
+*/
+void QSortFilterProxyModel::fetchMore(const QModelIndex &parent)
+{
+ Q_D(QSortFilterProxyModel);
+ QModelIndex source_parent;
+ if (d->indexValid(parent))
+ source_parent = mapToSource(parent);
+ d->model->fetchMore(source_parent);
+}
+
+/*!
+ \reimp
+*/
+bool QSortFilterProxyModel::canFetchMore(const QModelIndex &parent) const
+{
+ Q_D(const QSortFilterProxyModel);
+ QModelIndex source_parent;
+ if (d->indexValid(parent))
+ source_parent = mapToSource(parent);
+ return d->model->canFetchMore(source_parent);
+}
+
+/*!
+ \reimp
+*/
+Qt::ItemFlags QSortFilterProxyModel::flags(const QModelIndex &index) const
+{
+ Q_D(const QSortFilterProxyModel);
+ QModelIndex source_index;
+ if (d->indexValid(index))
+ source_index = mapToSource(index);
+ return d->model->flags(source_index);
+}
+
+/*!
+ \reimp
+*/
+QModelIndex QSortFilterProxyModel::buddy(const QModelIndex &index) const
+{
+ Q_D(const QSortFilterProxyModel);
+ if (!d->indexValid(index))
+ return QModelIndex();
+ QModelIndex source_index = mapToSource(index);
+ QModelIndex source_buddy = d->model->buddy(source_index);
+ if (source_index == source_buddy)
+ return index;
+ return mapFromSource(source_buddy);
+}
+
+/*!
+ \reimp
+*/
+QModelIndexList QSortFilterProxyModel::match(const QModelIndex &start, int role,
+ const QVariant &value, int hits,
+ Qt::MatchFlags flags) const
+{
+ return QAbstractProxyModel::match(start, role, value, hits, flags);
+}
+
+/*!
+ \reimp
+*/
+QSize QSortFilterProxyModel::span(const QModelIndex &index) const
+{
+ Q_D(const QSortFilterProxyModel);
+ QModelIndex source_index = mapToSource(index);
+ if (index.isValid() && !source_index.isValid())
+ return QSize();
+ return d->model->span(source_index);
+}
+
+/*!
+ \reimp
+*/
+void QSortFilterProxyModel::sort(int column, Qt::SortOrder order)
+{
+ Q_D(QSortFilterProxyModel);
+ if (d->proxy_sort_column == column && d->sort_order == order)
+ return;
+ d->sort_order = order;
+ d->proxy_sort_column = column;
+ d->update_source_sort_column();
+ d->sort();
+}
+
+/*!
+ \since 4.5
+ \brief the column currently used for sorting
+
+ This returns the most recently used sort column.
+*/
+int QSortFilterProxyModel::sortColumn() const
+{
+ Q_D(const QSortFilterProxyModel);
+ return d->proxy_sort_column;
+}
+
+/*!
+ \since 4.5
+ \brief the order currently used for sorting
+
+ This returns the most recently used sort order.
+*/
+Qt::SortOrder QSortFilterProxyModel::sortOrder() const
+{
+ Q_D(const QSortFilterProxyModel);
+ return d->sort_order;
+}
+
+/*!
+ \property QSortFilterProxyModel::filterRegExp
+ \brief the QRegExp used to filter the contents of the source model
+
+ Setting this property overwrites the current
+ \l{QSortFilterProxyModel::filterCaseSensitivity}
+ {filterCaseSensitivity}. By default, the QRegExp is an empty
+ string matching all contents.
+
+ \sa filterCaseSensitivity, setFilterWildcard(), setFilterFixedString()
+*/
+QRegExp QSortFilterProxyModel::filterRegExp() const
+{
+ Q_D(const QSortFilterProxyModel);
+ return d->filter_regexp;
+}
+
+void QSortFilterProxyModel::setFilterRegExp(const QRegExp &regExp)
+{
+ Q_D(QSortFilterProxyModel);
+ d->filter_regexp = regExp;
+ d->filter_changed();
+}
+
+/*!
+ \property QSortFilterProxyModel::filterKeyColumn
+ \brief the column where the key used to filter the contents of the
+ source model is read from.
+
+ The default value is 0. If the value is -1, the keys will be read
+ from all columns.
+*/
+int QSortFilterProxyModel::filterKeyColumn() const
+{
+ Q_D(const QSortFilterProxyModel);
+ return d->filter_column;
+}
+
+void QSortFilterProxyModel::setFilterKeyColumn(int column)
+{
+ Q_D(QSortFilterProxyModel);
+ d->filter_column = column;
+ d->filter_changed();
+}
+
+/*!
+ \property QSortFilterProxyModel::filterCaseSensitivity
+
+ \brief the case sensitivity of the QRegExp pattern used to filter the
+ contents of the source model
+
+ By default, the filter is case sensitive.
+
+ \sa filterRegExp, sortCaseSensitivity
+*/
+Qt::CaseSensitivity QSortFilterProxyModel::filterCaseSensitivity() const
+{
+ Q_D(const QSortFilterProxyModel);
+ return d->filter_regexp.caseSensitivity();
+}
+
+void QSortFilterProxyModel::setFilterCaseSensitivity(Qt::CaseSensitivity cs)
+{
+ Q_D(QSortFilterProxyModel);
+ if (cs == d->filter_regexp.caseSensitivity())
+ return;
+ d->filter_regexp.setCaseSensitivity(cs);
+ d->filter_changed();
+}
+
+/*!
+ \since 4.2
+ \property QSortFilterProxyModel::sortCaseSensitivity
+ \brief the case sensitivity setting used for comparing strings when sorting
+
+ By default, sorting is case sensitive.
+
+ \sa filterCaseSensitivity, lessThan()
+*/
+Qt::CaseSensitivity QSortFilterProxyModel::sortCaseSensitivity() const
+{
+ Q_D(const QSortFilterProxyModel);
+ return d->sort_casesensitivity;
+}
+
+void QSortFilterProxyModel::setSortCaseSensitivity(Qt::CaseSensitivity cs)
+{
+ Q_D(QSortFilterProxyModel);
+ if (d->sort_casesensitivity == cs)
+ return;
+
+ d->sort_casesensitivity = cs;
+ d->sort();
+}
+
+/*!
+ \since 4.3
+ \property QSortFilterProxyModel::isSortLocaleAware
+ \brief the local aware setting used for comparing strings when sorting
+
+ By default, sorting is not local aware.
+
+ \sa sortCaseSensitivity, lessThan()
+*/
+bool QSortFilterProxyModel::isSortLocaleAware() const
+{
+ Q_D(const QSortFilterProxyModel);
+ return d->sort_localeaware;
+}
+
+void QSortFilterProxyModel::setSortLocaleAware(bool on)
+{
+ Q_D(QSortFilterProxyModel);
+ if (d->sort_localeaware == on)
+ return;
+
+ d->sort_localeaware = on;
+ d->sort();
+}
+
+/*!
+ \overload
+
+ Sets the regular expression used to filter the contents
+ of the source model to \a pattern.
+
+ \sa setFilterCaseSensitivity(), setFilterWildcard(), setFilterFixedString(), filterRegExp()
+*/
+void QSortFilterProxyModel::setFilterRegExp(const QString &pattern)
+{
+ Q_D(QSortFilterProxyModel);
+ d->filter_regexp.setPatternSyntax(QRegExp::RegExp);
+ d->filter_regexp.setPattern(pattern);
+ d->filter_changed();
+}
+
+/*!
+ Sets the wildcard expression used to filter the contents
+ of the source model to the given \a pattern.
+
+ \sa setFilterCaseSensitivity(), setFilterRegExp(), setFilterFixedString(), filterRegExp()
+*/
+void QSortFilterProxyModel::setFilterWildcard(const QString &pattern)
+{
+ Q_D(QSortFilterProxyModel);
+ d->filter_regexp.setPatternSyntax(QRegExp::Wildcard);
+ d->filter_regexp.setPattern(pattern);
+ d->filter_changed();
+}
+
+/*!
+ Sets the fixed string used to filter the contents
+ of the source model to the given \a pattern.
+
+ \sa setFilterCaseSensitivity(), setFilterRegExp(), setFilterWildcard(), filterRegExp()
+*/
+void QSortFilterProxyModel::setFilterFixedString(const QString &pattern)
+{
+ Q_D(QSortFilterProxyModel);
+ d->filter_regexp.setPatternSyntax(QRegExp::FixedString);
+ d->filter_regexp.setPattern(pattern);
+ d->filter_changed();
+}
+
+/*!
+ \since 4.2
+ \property QSortFilterProxyModel::dynamicSortFilter
+ \brief whether the proxy model is dynamically sorted and filtered
+ whenever the contents of the source model change
+
+ The default value is false.
+*/
+bool QSortFilterProxyModel::dynamicSortFilter() const
+{
+ Q_D(const QSortFilterProxyModel);
+ return d->dynamic_sortfilter;
+}
+
+void QSortFilterProxyModel::setDynamicSortFilter(bool enable)
+{
+ Q_D(QSortFilterProxyModel);
+ d->dynamic_sortfilter = enable;
+}
+
+/*!
+ \since 4.2
+ \property QSortFilterProxyModel::sortRole
+ \brief the item role that is used to query the source model's data when sorting items
+
+ The default value is Qt::DisplayRole.
+
+ \sa lessThan()
+*/
+int QSortFilterProxyModel::sortRole() const
+{
+ Q_D(const QSortFilterProxyModel);
+ return d->sort_role;
+}
+
+void QSortFilterProxyModel::setSortRole(int role)
+{
+ Q_D(QSortFilterProxyModel);
+ if (d->sort_role == role)
+ return;
+ d->sort_role = role;
+ d->sort();
+}
+
+/*!
+ \since 4.2
+ \property QSortFilterProxyModel::filterRole
+ \brief the item role that is used to query the source model's data when filtering items
+
+ The default value is Qt::DisplayRole.
+
+ \sa filterAcceptsRow()
+*/
+int QSortFilterProxyModel::filterRole() const
+{
+ Q_D(const QSortFilterProxyModel);
+ return d->filter_role;
+}
+
+void QSortFilterProxyModel::setFilterRole(int role)
+{
+ Q_D(QSortFilterProxyModel);
+ if (d->filter_role == role)
+ return;
+ d->filter_role = role;
+ d->filter_changed();
+}
+
+/*!
+ \obsolete
+
+ This function is obsolete. Use invalidate() instead.
+*/
+void QSortFilterProxyModel::clear()
+{
+ Q_D(QSortFilterProxyModel);
+ emit layoutAboutToBeChanged();
+ d->clear_mapping();
+ emit layoutChanged();
+}
+
+/*!
+ \since 4.3
+
+ Invalidates the current sorting and filtering.
+
+ \sa invalidateFilter()
+*/
+void QSortFilterProxyModel::invalidate()
+{
+ Q_D(QSortFilterProxyModel);
+ emit layoutAboutToBeChanged();
+ d->clear_mapping();
+ emit layoutChanged();
+}
+
+/*!
+ \obsolete
+
+ This function is obsolete. Use invalidateFilter() instead.
+*/
+void QSortFilterProxyModel::filterChanged()
+{
+ Q_D(QSortFilterProxyModel);
+ d->filter_changed();
+}
+
+/*!
+ \since 4.3
+
+ Invalidates the current filtering.
+
+ This function should be called if you are implementing custom filtering
+ (e.g. filterAcceptsRow()), and your filter parameters have changed.
+
+ \sa invalidate()
+*/
+void QSortFilterProxyModel::invalidateFilter()
+{
+ Q_D(QSortFilterProxyModel);
+ d->filter_changed();
+}
+
+/*!
+ Returns true if the value of the item referred to by the given
+ index \a left is less than the value of the item referred to by
+ the given index \a right, otherwise returns false.
+
+ This function is used as the < operator when sorting, and handles
+ the following QVariant types:
+
+ \list
+ \o QVariant::Int
+ \o QVariant::UInt
+ \o QVariant::LongLong
+ \o QVariant::ULongLong
+ \o QVariant::Double
+ \o QVariant::Char
+ \o QVariant::Date
+ \o QVariant::Time
+ \o QVariant::DateTime
+ \o QVariant::String
+ \endlist
+
+ Any other type will be converted to a QString using
+ QVariant::toString().
+
+ Comparison of \l{QString}s is case sensitive by default; this can
+ be changed using the \l {QSortFilterProxyModel::sortCaseSensitivity}
+ {sortCaseSensitivity} property.
+
+ By default, the Qt::DisplayRole associated with the
+ \l{QModelIndex}es is used for comparisons. This can be changed by
+ setting the \l {QSortFilterProxyModel::sortRole} {sortRole} property.
+
+ \note The indices passed in correspond to the source model.
+
+ \sa sortRole, sortCaseSensitivity, dynamicSortFilter
+*/
+bool QSortFilterProxyModel::lessThan(const QModelIndex &left, const QModelIndex &right) const
+{
+ Q_D(const QSortFilterProxyModel);
+ QVariant l = (left.model() ? left.model()->data(left, d->sort_role) : QVariant());
+ QVariant r = (right.model() ? right.model()->data(right, d->sort_role) : QVariant());
+ switch (l.type()) {
+ case QVariant::Invalid:
+ return (r.type() == QVariant::Invalid);
+ case QVariant::Int:
+ return l.toInt() < r.toInt();
+ case QVariant::UInt:
+ return l.toUInt() < r.toUInt();
+ case QVariant::LongLong:
+ return l.toLongLong() < r.toLongLong();
+ case QVariant::ULongLong:
+ return l.toULongLong() < r.toULongLong();
+ case QVariant::Double:
+ return l.toDouble() < r.toDouble();
+ case QVariant::Char:
+ return l.toChar() < r.toChar();
+ case QVariant::Date:
+ return l.toDate() < r.toDate();
+ case QVariant::Time:
+ return l.toTime() < r.toTime();
+ case QVariant::DateTime:
+ return l.toDateTime() < r.toDateTime();
+ case QVariant::String:
+ default:
+ if (d->sort_localeaware)
+ return l.toString().localeAwareCompare(r.toString()) < 0;
+ else
+ return l.toString().compare(r.toString(), d->sort_casesensitivity) < 0;
+ }
+ return false;
+}
+
+/*!
+ Returns true if the item in the row indicated by the given \a source_row
+ and \a source_parent should be included in the model; otherwise returns
+ false.
+
+ The default implementation returns true if the value held by the relevant item
+ matches the filter string, wildcard string or regular expression.
+
+ \note By default, the Qt::DisplayRole is used to determine if the row
+ should be accepted or not. This can be changed by setting the
+ \l{QSortFilterProxyModel::filterRole}{filterRole} property.
+
+ \sa filterAcceptsColumn(), setFilterFixedString(), setFilterRegExp(), setFilterWildcard()
+*/
+bool QSortFilterProxyModel::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const
+{
+ Q_D(const QSortFilterProxyModel);
+ if (d->filter_regexp.isEmpty())
+ return true;
+ if (d->filter_column == -1) {
+ int column_count = d->model->columnCount(source_parent);
+ for (int column = 0; column < column_count; ++column) {
+ QModelIndex source_index = d->model->index(source_row, column, source_parent);
+ QString key = d->model->data(source_index, d->filter_role).toString();
+ if (key.contains(d->filter_regexp))
+ return true;
+ }
+ return false;
+ }
+ QModelIndex source_index = d->model->index(source_row, d->filter_column, source_parent);
+ if (!source_index.isValid()) // the column may not exist
+ return true;
+ QString key = d->model->data(source_index, d->filter_role).toString();
+ return key.contains(d->filter_regexp);
+}
+
+/*!
+ Returns true if the item in the column indicated by the given \a source_column
+ and \a source_parent should be included in the model; otherwise returns false.
+
+ The default implementation returns true if the value held by the relevant item
+ matches the filter string, wildcard string or regular expression.
+
+ \note By default, the Qt::DisplayRole is used to determine if the row
+ should be accepted or not. This can be changed by setting the \l
+ filterRole property.
+
+ \sa filterAcceptsRow(), setFilterFixedString(), setFilterRegExp(), setFilterWildcard()
+*/
+bool QSortFilterProxyModel::filterAcceptsColumn(int source_column, const QModelIndex &source_parent) const
+{
+ Q_UNUSED(source_column);
+ Q_UNUSED(source_parent);
+ return true;
+}
+
+/*!
+ Returns the source model index corresponding to the given \a
+ proxyIndex from the sorting filter model.
+
+ \sa mapFromSource()
+*/
+QModelIndex QSortFilterProxyModel::mapToSource(const QModelIndex &proxyIndex) const
+{
+ Q_D(const QSortFilterProxyModel);
+ return d->proxy_to_source(proxyIndex);
+}
+
+/*!
+ Returns the model index in the QSortFilterProxyModel given the \a
+ sourceIndex from the source model.
+
+ \sa mapToSource()
+*/
+QModelIndex QSortFilterProxyModel::mapFromSource(const QModelIndex &sourceIndex) const
+{
+ Q_D(const QSortFilterProxyModel);
+ return d->source_to_proxy(sourceIndex);
+}
+
+/*!
+ \reimp
+*/
+QItemSelection QSortFilterProxyModel::mapSelectionToSource(const QItemSelection &proxySelection) const
+{
+ return QAbstractProxyModel::mapSelectionToSource(proxySelection);
+}
+
+/*!
+ \reimp
+*/
+QItemSelection QSortFilterProxyModel::mapSelectionFromSource(const QItemSelection &sourceSelection) const
+{
+ return QAbstractProxyModel::mapSelectionFromSource(sourceSelection);
+}
+
+/*!
+ \fn QObject *QSortFilterProxyModel::parent() const
+ \internal
+*/
+
+QT_END_NAMESPACE
+
+#include "moc_qsortfilterproxymodel.cpp"
+
+#endif // QT_NO_SORTFILTERPROXYMODEL
diff --git a/src/gui/itemviews/qsortfilterproxymodel.h b/src/gui/itemviews/qsortfilterproxymodel.h
new file mode 100644
index 0000000000..f537adbfc8
--- /dev/null
+++ b/src/gui/itemviews/qsortfilterproxymodel.h
@@ -0,0 +1,199 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSORTFILTERPROXYMODEL_H
+#define QSORTFILTERPROXYMODEL_H
+
+#include <QtGui/qabstractproxymodel.h>
+
+#ifndef QT_NO_SORTFILTERPROXYMODEL
+
+#include <QtCore/qregexp.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+class QSortFilterProxyModelPrivate;
+class QSortFilterProxyModelLessThan;
+class QSortFilterProxyModelGreaterThan;
+
+class Q_GUI_EXPORT QSortFilterProxyModel : public QAbstractProxyModel
+{
+ friend class QSortFilterProxyModelLessThan;
+ friend class QSortFilterProxyModelGreaterThan;
+
+ Q_OBJECT
+ Q_PROPERTY(QRegExp filterRegExp READ filterRegExp WRITE setFilterRegExp)
+ Q_PROPERTY(int filterKeyColumn READ filterKeyColumn WRITE setFilterKeyColumn)
+ Q_PROPERTY(bool dynamicSortFilter READ dynamicSortFilter WRITE setDynamicSortFilter)
+ Q_PROPERTY(Qt::CaseSensitivity filterCaseSensitivity READ filterCaseSensitivity WRITE setFilterCaseSensitivity)
+ Q_PROPERTY(Qt::CaseSensitivity sortCaseSensitivity READ sortCaseSensitivity WRITE setSortCaseSensitivity)
+ Q_PROPERTY(bool isSortLocaleAware READ isSortLocaleAware WRITE setSortLocaleAware)
+ Q_PROPERTY(int sortRole READ sortRole WRITE setSortRole)
+ Q_PROPERTY(int filterRole READ filterRole WRITE setFilterRole)
+
+public:
+ QSortFilterProxyModel(QObject *parent = 0);
+ ~QSortFilterProxyModel();
+
+ void setSourceModel(QAbstractItemModel *sourceModel);
+
+ QModelIndex mapToSource(const QModelIndex &proxyIndex) const;
+ QModelIndex mapFromSource(const QModelIndex &sourceIndex) const;
+
+ QItemSelection mapSelectionToSource(const QItemSelection &proxySelection) const;
+ QItemSelection mapSelectionFromSource(const QItemSelection &sourceSelection) const;
+
+ QRegExp filterRegExp() const;
+ void setFilterRegExp(const QRegExp &regExp);
+
+ int filterKeyColumn() const;
+ void setFilterKeyColumn(int column);
+
+ Qt::CaseSensitivity filterCaseSensitivity() const;
+ void setFilterCaseSensitivity(Qt::CaseSensitivity cs);
+
+ Qt::CaseSensitivity sortCaseSensitivity() const;
+ void setSortCaseSensitivity(Qt::CaseSensitivity cs);
+
+ bool isSortLocaleAware() const;
+ void setSortLocaleAware(bool on);
+
+ int sortColumn() const;
+ Qt::SortOrder sortOrder() const;
+
+ bool dynamicSortFilter() const;
+ void setDynamicSortFilter(bool enable);
+
+ int sortRole() const;
+ void setSortRole(int role);
+
+ int filterRole() const;
+ void setFilterRole(int role);
+
+public Q_SLOTS:
+ void setFilterRegExp(const QString &pattern);
+ void setFilterWildcard(const QString &pattern);
+ void setFilterFixedString(const QString &pattern);
+ void clear();
+ void invalidate();
+
+protected:
+ virtual bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const;
+ virtual bool filterAcceptsColumn(int source_column, const QModelIndex &source_parent) const;
+ virtual bool lessThan(const QModelIndex &left, const QModelIndex &right) const;
+
+ void filterChanged();
+ void invalidateFilter();
+
+public:
+#ifdef Q_NO_USING_KEYWORD
+ inline QObject *parent() const { return QObject::parent(); }
+#else
+ using QObject::parent;
+#endif
+
+ QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const;
+ QModelIndex parent(const QModelIndex &child) const;
+
+ int rowCount(const QModelIndex &parent = QModelIndex()) const;
+ int columnCount(const QModelIndex &parent = QModelIndex()) const;
+ bool hasChildren(const QModelIndex &parent = QModelIndex()) const;
+
+ QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
+ bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole);
+
+ QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;
+ bool setHeaderData(int section, Qt::Orientation orientation,
+ const QVariant &value, int role = Qt::EditRole);
+
+ QMimeData *mimeData(const QModelIndexList &indexes) const;
+ bool dropMimeData(const QMimeData *data, Qt::DropAction action,
+ int row, int column, const QModelIndex &parent);
+
+ bool insertRows(int row, int count, const QModelIndex &parent = QModelIndex());
+ bool insertColumns(int column, int count, const QModelIndex &parent = QModelIndex());
+ bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex());
+ bool removeColumns(int column, int count, const QModelIndex &parent = QModelIndex());
+
+ void fetchMore(const QModelIndex &parent);
+ bool canFetchMore(const QModelIndex &parent) const;
+ Qt::ItemFlags flags(const QModelIndex &index) const;
+
+ QModelIndex buddy(const QModelIndex &index) const;
+ QModelIndexList match(const QModelIndex &start, int role,
+ const QVariant &value, int hits = 1,
+ Qt::MatchFlags flags =
+ Qt::MatchFlags(Qt::MatchStartsWith|Qt::MatchWrap)) const;
+ QSize span(const QModelIndex &index) const;
+ void sort(int column, Qt::SortOrder order = Qt::AscendingOrder);
+
+ QStringList mimeTypes() const;
+ Qt::DropActions supportedDropActions() const;
+private:
+ Q_DECLARE_PRIVATE(QSortFilterProxyModel)
+ Q_DISABLE_COPY(QSortFilterProxyModel)
+
+ Q_PRIVATE_SLOT(d_func(), void _q_sourceDataChanged(const QModelIndex &source_top_left, const QModelIndex &source_bottom_right))
+ Q_PRIVATE_SLOT(d_func(), void _q_sourceHeaderDataChanged(Qt::Orientation orientation, int start, int end))
+ Q_PRIVATE_SLOT(d_func(), void _q_sourceReset())
+ Q_PRIVATE_SLOT(d_func(), void _q_sourceLayoutAboutToBeChanged())
+ Q_PRIVATE_SLOT(d_func(), void _q_sourceLayoutChanged())
+ Q_PRIVATE_SLOT(d_func(), void _q_sourceRowsAboutToBeInserted(const QModelIndex &source_parent, int start, int end))
+ Q_PRIVATE_SLOT(d_func(), void _q_sourceRowsInserted(const QModelIndex &source_parent, int start, int end))
+ Q_PRIVATE_SLOT(d_func(), void _q_sourceRowsAboutToBeRemoved(const QModelIndex &source_parent, int start, int end))
+ Q_PRIVATE_SLOT(d_func(), void _q_sourceRowsRemoved(const QModelIndex &source_parent, int start, int end))
+ Q_PRIVATE_SLOT(d_func(), void _q_sourceColumnsAboutToBeInserted(const QModelIndex &source_parent, int start, int end))
+ Q_PRIVATE_SLOT(d_func(), void _q_sourceColumnsInserted(const QModelIndex &source_parent, int start, int end))
+ Q_PRIVATE_SLOT(d_func(), void _q_sourceColumnsAboutToBeRemoved(const QModelIndex &source_parent, int start, int end))
+ Q_PRIVATE_SLOT(d_func(), void _q_sourceColumnsRemoved(const QModelIndex &source_parent, int start, int end))
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QT_NO_SORTFILTERPROXYMODEL
+
+#endif // QSORTFILTERPROXYMODEL_H
diff --git a/src/gui/itemviews/qstandarditemmodel.cpp b/src/gui/itemviews/qstandarditemmodel.cpp
new file mode 100644
index 0000000000..10aac9ab14
--- /dev/null
+++ b/src/gui/itemviews/qstandarditemmodel.cpp
@@ -0,0 +1,3108 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qstandarditemmodel.h"
+
+#ifndef QT_NO_STANDARDITEMMODEL
+
+#include <QtCore/qdatetime.h>
+#include <QtCore/qlist.h>
+#include <QtCore/qmap.h>
+#include <QtCore/qpair.h>
+#include <QtCore/qvariant.h>
+#include <QtCore/qvector.h>
+#include <QtCore/qstringlist.h>
+#include <QtCore/qbitarray.h>
+#include <QtCore/qmimedata.h>
+
+#include <private/qstandarditemmodel_p.h>
+#include <qdebug.h>
+
+QT_BEGIN_NAMESPACE
+
+class QStandardItemModelLessThan
+{
+public:
+ inline QStandardItemModelLessThan()
+ { }
+
+ inline bool operator()(const QPair<QStandardItem*, int> &l,
+ const QPair<QStandardItem*, int> &r) const
+ {
+ return *(l.first) < *(r.first);
+ }
+};
+
+class QStandardItemModelGreaterThan
+{
+public:
+ inline QStandardItemModelGreaterThan()
+ { }
+
+ inline bool operator()(const QPair<QStandardItem*, int> &l,
+ const QPair<QStandardItem*, int> &r) const
+ {
+ return *(r.first) < *(l.first);
+ }
+};
+
+/*!
+ \internal
+*/
+QStandardItemPrivate::~QStandardItemPrivate()
+{
+ QVector<QStandardItem*>::const_iterator it;
+ for (it = children.constBegin(); it != children.constEnd(); ++it) {
+ QStandardItem *child = *it;
+ if (child)
+ child->d_func()->setModel(0);
+ delete child;
+ }
+ children.clear();
+ if (parent && model)
+ parent->d_func()->childDeleted(q_func());
+}
+
+/*!
+ \internal
+*/
+QPair<int, int> QStandardItemPrivate::position() const
+{
+ if (QStandardItem *par = parent) {
+ int idx = par->d_func()->childIndex(q_func());
+ if (idx == -1)
+ return QPair<int, int>(-1, -1);
+ return QPair<int, int>(idx / par->columnCount(), idx % par->columnCount());
+ }
+ // ### support header items?
+ return QPair<int, int>(-1, -1);
+}
+
+/*!
+ \internal
+*/
+void QStandardItemPrivate::setChild(int row, int column, QStandardItem *item,
+ bool emitChanged)
+{
+ Q_Q(QStandardItem);
+ if (item == q) {
+ qWarning("QStandardItem::setChild: Can't make an item a child of itself %p",
+ item);
+ return;
+ }
+ if ((row < 0) || (column < 0))
+ return;
+ if (rows <= row)
+ q->setRowCount(row + 1);
+ if (columns <= column)
+ q->setColumnCount(column + 1);
+ int index = childIndex(row, column);
+ Q_ASSERT(index != -1);
+ QStandardItem *oldItem = children.at(index);
+ if (item == oldItem)
+ return;
+ if (item) {
+ if (item->d_func()->parent == 0) {
+ item->d_func()->setParentAndModel(q, model);
+ } else {
+ qWarning("QStandardItem::setChild: Ignoring duplicate insertion of item %p",
+ item);
+ return;
+ }
+ }
+ if (oldItem)
+ oldItem->d_func()->setModel(0);
+ delete oldItem;
+ children.replace(index, item);
+ if (emitChanged && model)
+ model->d_func()->itemChanged(item);
+}
+
+
+/*!
+ \internal
+*/
+void QStandardItemPrivate::changeFlags(bool enable, Qt::ItemFlags f)
+{
+ Q_Q(QStandardItem);
+ Qt::ItemFlags flags = q->flags();
+ if (enable)
+ flags |= f;
+ else
+ flags &= ~f;
+ q->setFlags(flags);
+}
+
+/*!
+ \internal
+*/
+void QStandardItemPrivate::childDeleted(QStandardItem *child)
+{
+ int index = childIndex(child);
+ Q_ASSERT(index != -1);
+ children.replace(index, 0);
+}
+
+/*!
+ \internal
+*/
+void QStandardItemPrivate::setItemData(const QMap<int, QVariant> &roles)
+{
+ Q_Q(QStandardItem);
+
+ //let's build the vector of new values
+ QVector<QWidgetItemData> newValues;
+ QMap<int, QVariant>::const_iterator it;
+ for (it = roles.begin(); it != roles.end(); ++it) {
+ QVariant value = it.value();
+ if (value.isValid()) {
+ int role = it.key();
+ role = (role == Qt::EditRole) ? Qt::DisplayRole : role;
+ QWidgetItemData wid(role,it.value());
+ newValues.append(wid);
+ }
+ }
+
+ if (values!=newValues) {
+ values=newValues;
+ if (model)
+ model->d_func()->itemChanged(q);
+ }
+}
+
+/*!
+ \internal
+*/
+const QMap<int, QVariant> QStandardItemPrivate::itemData() const
+{
+ QMap<int, QVariant> result;
+ QVector<QWidgetItemData>::const_iterator it;
+ for (it = values.begin(); it != values.end(); ++it)
+ result.insert((*it).role, (*it).value);
+ return result;
+}
+
+/*!
+ \internal
+*/
+void QStandardItemPrivate::sortChildren(int column, Qt::SortOrder order)
+{
+ Q_Q(QStandardItem);
+ if (column >= columnCount())
+ return;
+
+ QVector<QPair<QStandardItem*, int> > sortable;
+ QVector<int> unsortable;
+
+ sortable.reserve(rowCount());
+ unsortable.reserve(rowCount());
+
+ for (int row = 0; row < rowCount(); ++row) {
+ QStandardItem *itm = q->child(row, column);
+ if (itm)
+ sortable.append(QPair<QStandardItem*,int>(itm, row));
+ else
+ unsortable.append(row);
+ }
+
+ if (order == Qt::AscendingOrder) {
+ QStandardItemModelLessThan lt;
+ qStableSort(sortable.begin(), sortable.end(), lt);
+ } else {
+ QStandardItemModelGreaterThan gt;
+ qStableSort(sortable.begin(), sortable.end(), gt);
+ }
+
+ QModelIndexList changedPersistentIndexesFrom, changedPersistentIndexesTo;
+ QVector<QStandardItem*> sorted_children(children.count());
+ for (int i = 0; i < rowCount(); ++i) {
+ int r = (i < sortable.count()
+ ? sortable.at(i).second
+ : unsortable.at(i - sortable.count()));
+ for (int c = 0; c < columnCount(); ++c) {
+ QStandardItem *itm = q->child(r, c);
+ sorted_children[childIndex(i, c)] = itm;
+ if (model) {
+ QModelIndex from = model->createIndex(r, c, q);
+ if (model->d_func()->persistent.indexes.contains(from)) {
+ QModelIndex to = model->createIndex(i, c, q);
+ changedPersistentIndexesFrom.append(from);
+ changedPersistentIndexesTo.append(to);
+ }
+ }
+ }
+ }
+
+ children = sorted_children;
+
+ if (model) {
+ model->changePersistentIndexList(changedPersistentIndexesFrom, changedPersistentIndexesTo);
+ }
+
+ QVector<QStandardItem*>::iterator it;
+ for (it = children.begin(); it != children.end(); ++it) {
+ if (*it)
+ (*it)->d_func()->sortChildren(column, order);
+ }
+}
+
+/*!
+ \internal
+ set the model of this item and all its children
+ */
+void QStandardItemPrivate::setModel(QStandardItemModel *mod)
+{
+ if (children.isEmpty()) {
+ if (model)
+ model->d_func()->invalidatePersistentIndex(model->indexFromItem(q_ptr));
+ model = mod;
+ } else {
+ QStack<QStandardItem*> stack;
+ stack.push(q_ptr);
+ while (!stack.isEmpty()) {
+ QStandardItem *itm = stack.pop();
+ if (itm->d_func()->model) {
+ itm->d_func()->model->d_func()->invalidatePersistentIndex(itm->d_func()->model->indexFromItem(itm));
+ }
+ itm->d_func()->model = mod;
+ const QVector<QStandardItem*> &childList = itm->d_func()->children;
+ for (int i = 0; i < childList.count(); ++i) {
+ QStandardItem *chi = childList.at(i);
+ if (chi)
+ stack.push(chi);
+ }
+ }
+ }
+}
+
+/*!
+ \internal
+*/
+QStandardItemModelPrivate::QStandardItemModelPrivate()
+ : root(new QStandardItem),
+ itemPrototype(0),
+ sortRole(Qt::DisplayRole)
+{
+ root->setFlags(Qt::ItemIsDropEnabled);
+}
+
+/*!
+ \internal
+*/
+QStandardItemModelPrivate::~QStandardItemModelPrivate()
+{
+ delete root;
+ delete itemPrototype;
+ qDeleteAll(columnHeaderItems);
+ qDeleteAll(rowHeaderItems);
+}
+
+/*!
+ \internal
+*/
+void QStandardItemModelPrivate::init()
+{
+ Q_Q(QStandardItemModel);
+ QObject::connect(q, SIGNAL(dataChanged(QModelIndex,QModelIndex)),
+ q, SLOT(_q_emitItemChanged(QModelIndex,QModelIndex)));
+}
+
+/*!
+ \internal
+*/
+void QStandardItemModelPrivate::_q_emitItemChanged(const QModelIndex &topLeft,
+ const QModelIndex &bottomRight)
+{
+ Q_Q(QStandardItemModel);
+ QModelIndex parent = topLeft.parent();
+ for (int row = topLeft.row(); row <= bottomRight.row(); ++row) {
+ for (int column = topLeft.column(); column <= bottomRight.column(); ++column) {
+ QModelIndex index = q->index(row, column, parent);
+ if (QStandardItem *item = itemFromIndex(index))
+ emit q->itemChanged(item);
+ }
+ }
+}
+
+/*!
+ \internal
+*/
+bool QStandardItemPrivate::insertRows(int row, const QList<QStandardItem*> &items)
+{
+ Q_Q(QStandardItem);
+ if ((row < 0) || (row > rowCount()))
+ return false;
+ int count = items.count();
+ if (model)
+ model->d_func()->rowsAboutToBeInserted(q, row, row + count - 1);
+ if (rowCount() == 0) {
+ if (columnCount() == 0)
+ q->setColumnCount(1);
+ children.resize(columnCount() * count);
+ rows = count;
+ } else {
+ rows += count;
+ int index = childIndex(row, 0);
+ if (index != -1)
+ children.insert(index, columnCount() * count, 0);
+ }
+ for (int i = 0; i < items.count(); ++i) {
+ QStandardItem *item = items.at(i);
+ item->d_func()->model = model;
+ item->d_func()->parent = q;
+ int index = childIndex(i + row, 0);
+ children.replace(index, item);
+ }
+ if (model)
+ model->d_func()->rowsInserted(q, row, count);
+ return true;
+}
+
+bool QStandardItemPrivate::insertRows(int row, int count, const QList<QStandardItem*> &items)
+{
+ Q_Q(QStandardItem);
+ if ((count < 1) || (row < 0) || (row > rowCount()))
+ return false;
+ if (model)
+ model->d_func()->rowsAboutToBeInserted(q, row, row + count - 1);
+ if (rowCount() == 0) {
+ children.resize(columnCount() * count);
+ rows = count;
+ } else {
+ rows += count;
+ int index = childIndex(row, 0);
+ if (index != -1)
+ children.insert(index, columnCount() * count, 0);
+ }
+ if (!items.isEmpty()) {
+ int index = childIndex(row, 0);
+ int limit = qMin(items.count(), columnCount() * count);
+ for (int i = 0; i < limit; ++i) {
+ QStandardItem *item = items.at(i);
+ if (item) {
+ if (item->d_func()->parent == 0) {
+ item->d_func()->setParentAndModel(q, model);
+ } else {
+ qWarning("QStandardItem::insertRows: Ignoring duplicate insertion of item %p",
+ item);
+ item = 0;
+ }
+ }
+ children.replace(index, item);
+ ++index;
+ }
+ }
+ if (model)
+ model->d_func()->rowsInserted(q, row, count);
+ return true;
+}
+
+/*!
+ \internal
+*/
+bool QStandardItemPrivate::insertColumns(int column, int count, const QList<QStandardItem*> &items)
+{
+ Q_Q(QStandardItem);
+ if ((count < 1) || (column < 0) || (column > columnCount()))
+ return false;
+ if (model)
+ model->d_func()->columnsAboutToBeInserted(q, column, column + count - 1);
+ if (columnCount() == 0) {
+ children.resize(rowCount() * count);
+ columns = count;
+ } else {
+ columns += count;
+ int index = childIndex(0, column);
+ for (int row = 0; row < rowCount(); ++row) {
+ children.insert(index, count, 0);
+ index += columnCount();
+ }
+ }
+ if (!items.isEmpty()) {
+ int limit = qMin(items.count(), rowCount() * count);
+ for (int i = 0; i < limit; ++i) {
+ QStandardItem *item = items.at(i);
+ if (item) {
+ if (item->d_func()->parent == 0) {
+ item->d_func()->setParentAndModel(q, model);
+ } else {
+ qWarning("QStandardItem::insertColumns: Ignoring duplicate insertion of item %p",
+ item);
+ item = 0;
+ }
+ }
+ int r = i / count;
+ int c = column + (i % count);
+ int index = childIndex(r, c);
+ children.replace(index, item);
+ }
+ }
+ if (model)
+ model->d_func()->columnsInserted(q, column, count);
+ return true;
+}
+
+/*!
+ \internal
+*/
+void QStandardItemModelPrivate::itemChanged(QStandardItem *item)
+{
+ Q_Q(QStandardItemModel);
+ if (item->d_func()->parent == 0) {
+ // Header item
+ int idx = columnHeaderItems.indexOf(item);
+ if (idx != -1) {
+ emit q->headerDataChanged(Qt::Horizontal, idx, idx);
+ } else {
+ idx = rowHeaderItems.indexOf(item);
+ if (idx != -1)
+ emit q->headerDataChanged(Qt::Vertical, idx, idx);
+ }
+ } else {
+ // Normal item
+ QModelIndex index = q->indexFromItem(item);
+ emit q->dataChanged(index, index);
+ }
+}
+
+/*!
+ \internal
+*/
+void QStandardItemModelPrivate::rowsAboutToBeInserted(QStandardItem *parent,
+ int start, int end)
+{
+ Q_Q(QStandardItemModel);
+ QModelIndex index = q->indexFromItem(parent);
+ q->beginInsertRows(index, start, end);
+}
+
+/*!
+ \internal
+*/
+void QStandardItemModelPrivate::columnsAboutToBeInserted(QStandardItem *parent,
+ int start, int end)
+{
+ Q_Q(QStandardItemModel);
+ QModelIndex index = q->indexFromItem(parent);
+ q->beginInsertColumns(index, start, end);
+}
+
+/*!
+ \internal
+*/
+void QStandardItemModelPrivate::rowsAboutToBeRemoved(QStandardItem *parent,
+ int start, int end)
+{
+ Q_Q(QStandardItemModel);
+ QModelIndex index = q->indexFromItem(parent);
+ q->beginRemoveRows(index, start, end);
+}
+
+/*!
+ \internal
+*/
+void QStandardItemModelPrivate::columnsAboutToBeRemoved(QStandardItem *parent,
+ int start, int end)
+{
+ Q_Q(QStandardItemModel);
+ QModelIndex index = q->indexFromItem(parent);
+ q->beginRemoveColumns(index, start, end);
+}
+
+/*!
+ \internal
+*/
+void QStandardItemModelPrivate::rowsInserted(QStandardItem *parent,
+ int row, int count)
+{
+ Q_Q(QStandardItemModel);
+ if (parent == root)
+ rowHeaderItems.insert(row, count, 0);
+ q->endInsertRows();
+}
+
+/*!
+ \internal
+*/
+void QStandardItemModelPrivate::columnsInserted(QStandardItem *parent,
+ int column, int count)
+{
+ Q_Q(QStandardItemModel);
+ if (parent == root)
+ columnHeaderItems.insert(column, count, 0);
+ q->endInsertColumns();
+}
+
+/*!
+ \internal
+*/
+void QStandardItemModelPrivate::rowsRemoved(QStandardItem *parent,
+ int row, int count)
+{
+ Q_Q(QStandardItemModel);
+ if (parent == root) {
+ for (int i = row; i < row + count; ++i) {
+ QStandardItem *oldItem = rowHeaderItems.at(i);
+ if (oldItem)
+ oldItem->d_func()->setModel(0);
+ delete oldItem;
+ }
+ rowHeaderItems.remove(row, count);
+ }
+ q->endRemoveRows();
+}
+
+/*!
+ \internal
+*/
+void QStandardItemModelPrivate::columnsRemoved(QStandardItem *parent,
+ int column, int count)
+{
+ Q_Q(QStandardItemModel);
+ if (parent == root) {
+ for (int i = column; i < column + count; ++i) {
+ QStandardItem *oldItem = columnHeaderItems.at(i);
+ if (oldItem)
+ oldItem->d_func()->setModel(0);
+ delete oldItem;
+ }
+ columnHeaderItems.remove(column, count);
+ }
+ q->endRemoveColumns();
+}
+
+/*!
+ \class QStandardItem
+ \brief The QStandardItem class provides an item for use with the
+ QStandardItemModel class.
+ \since 4.2
+ \ingroup model-view
+
+ Items usually contain text, icons, or checkboxes.
+
+ Each item can have its own background brush which is set with the
+ setBackground() function. The current background brush can be found with
+ background(). The text label for each item can be rendered with its own
+ font and brush. These are specified with the setFont() and setForeground()
+ functions, and read with font() and foreground().
+
+ By default, items are enabled, editable, selectable, checkable, and can be
+ used both as the source of a drag and drop operation and as a drop target.
+ Each item's flags can be changed by calling setFlags(). Checkable items
+ can be checked and unchecked with the setCheckState() function. The
+ corresponding checkState() function indicates whether the item is
+ currently checked.
+
+ You can store application-specific data in an item by calling setData().
+
+ Each item can have a two-dimensional table of child items. This makes it
+ possible to build hierarchies of items. The typical hierarchy is the tree,
+ in which case the child table is a table with a single column (a list).
+
+ The dimensions of the child table can be set with setRowCount() and
+ setColumnCount(). Items can be positioned in the child table with
+ setChild(). Get a pointer to a child item with child(). New rows and
+ columns of children can also be inserted with insertRow() and
+ insertColumn(), or appended with appendRow() and appendColumn(). When
+ using the append and insert functions, the dimensions of the child table
+ will grow as needed.
+
+ An existing row of children can be removed with removeRow() or takeRow();
+ correspondingly, a column can be removed with removeColumn() or
+ takeColumn().
+
+ An item's children can be sorted by calling sortChildren().
+
+ \section1 Subclassing
+
+ When subclassing QStandardItem to provide custom items, it is possible to
+ define new types for them so that they can be distinguished from the base
+ class. The type() function should be reimplemented to return a new type
+ value equal to or greater than \l UserType.
+
+ Reimplement data() and setData() if you want to perform custom handling of
+ data queries and/or control how an item's data is represented.
+
+ Reimplement clone() if you want QStandardItemModel to be able to create
+ instances of your custom item class on demand (see
+ QStandardItemModel::setItemPrototype()).
+
+ Reimplement read() and write() if you want to control how items are
+ represented in their serialized form.
+
+ Reimplement \l{operator<()} if you want to control the semantics of item
+ comparison. \l{operator<()} determines the sorted order when sorting items
+ with sortChildren() or with QStandardItemModel::sort().
+
+ \sa QStandardItemModel, {Item View Convenience Classes}, {Model/View Programming}
+*/
+
+/*!
+ \enum QStandardItem::ItemType
+
+ This enum describes the types that are used to describe standard items.
+
+ \value Type The default type for standard items.
+ \value UserType The minimum value for custom types. Values below UserType are
+ reserved by Qt.
+
+ You can define new user types in QStandardItem subclasses to ensure that
+ custom items are treated specially; for example, when items are sorted.
+
+ \sa type()
+*/
+
+/*!
+ Constructs an item.
+*/
+QStandardItem::QStandardItem()
+ : d_ptr(new QStandardItemPrivate)
+{
+ Q_D(QStandardItem);
+ d->q_ptr = this;
+}
+
+/*!
+ Constructs an item with the given \a text.
+*/
+QStandardItem::QStandardItem(const QString &text)
+ : d_ptr(new QStandardItemPrivate)
+{
+ Q_D(QStandardItem);
+ d->q_ptr = this;
+ setText(text);
+}
+
+/*!
+ Constructs an item with the given \a icon and \a text.
+*/
+QStandardItem::QStandardItem(const QIcon &icon, const QString &text)
+ : d_ptr(new QStandardItemPrivate)
+{
+ Q_D(QStandardItem);
+ d->q_ptr = this;
+ setIcon(icon);
+ setText(text);
+}
+
+/*!
+ Constructs an item with \a rows rows and \a columns columns of child items.
+*/
+QStandardItem::QStandardItem(int rows, int columns)
+ : d_ptr(new QStandardItemPrivate)
+{
+ Q_D(QStandardItem);
+ d->q_ptr = this;
+ setRowCount(rows);
+ setColumnCount(columns);
+}
+
+/*!
+ \internal
+*/
+QStandardItem::QStandardItem(QStandardItemPrivate &dd)
+ : d_ptr(&dd)
+{
+ Q_D(QStandardItem);
+ d->q_ptr = this;
+}
+
+/*!
+ Constructs a copy of \a other. Note that model() is
+ not copied.
+
+ This function is useful when reimplementing clone().
+*/
+QStandardItem::QStandardItem(const QStandardItem &other)
+ : d_ptr(new QStandardItemPrivate)
+{
+ Q_D(QStandardItem);
+ d->q_ptr = this;
+ operator=(other);
+}
+
+/*!
+ Assigns \a other's data and flags to this item. Note that
+ type() and model() are not copied.
+
+ This function is useful when reimplementing clone().
+*/
+QStandardItem &QStandardItem::operator=(const QStandardItem &other)
+{
+ Q_D(QStandardItem);
+ d->values = other.d_func()->values;
+ return *this;
+}
+
+/*!
+ Destructs the item.
+ This causes the item's children to be destructed as well.
+*/
+QStandardItem::~QStandardItem()
+{
+ Q_D(QStandardItem);
+ delete d;
+}
+
+/*!
+ Returns the item's parent item, or 0 if the item has no parent.
+
+ \sa child()
+*/
+QStandardItem *QStandardItem::parent() const
+{
+ Q_D(const QStandardItem);
+ if (!d->model || (d->model->d_func()->root != d->parent))
+ return d->parent;
+ return 0;
+}
+
+/*!
+ Sets the item's data for the given \a role to the specified \a value.
+
+ If you subclass QStandardItem and reimplement this function, your
+ reimplementation should call emitDataChanged() if you do not call
+ the base implementation of setData(). This will ensure that e.g.
+ views using the model are notified of the changes.
+
+ \note The default implementation treats Qt::EditRole and Qt::DisplayRole
+ as referring to the same data.
+
+ \sa Qt::ItemDataRole, data(), setFlags()
+*/
+void QStandardItem::setData(const QVariant &value, int role)
+{
+ Q_D(QStandardItem);
+ role = (role == Qt::EditRole) ? Qt::DisplayRole : role;
+ QVector<QWidgetItemData>::iterator it;
+ for (it = d->values.begin(); it != d->values.end(); ++it) {
+ if ((*it).role == role) {
+ if (value.isValid()) {
+ if ((*it).value.type() == value.type() && (*it).value == value)
+ return;
+ (*it).value = value;
+ } else {
+ d->values.erase(it);
+ }
+ if (d->model)
+ d->model->d_func()->itemChanged(this);
+ return;
+ }
+ }
+ d->values.append(QWidgetItemData(role, value));
+ if (d->model)
+ d->model->d_func()->itemChanged(this);
+}
+
+/*!
+ Returns the item's data for the given \a role, or an invalid
+ QVariant if there is no data for the role.
+
+ \note The default implementation treats Qt::EditRole and Qt::DisplayRole
+ as referring to the same data.
+*/
+QVariant QStandardItem::data(int role) const
+{
+ Q_D(const QStandardItem);
+ role = (role == Qt::EditRole) ? Qt::DisplayRole : role;
+ QVector<QWidgetItemData>::const_iterator it;
+ for (it = d->values.begin(); it != d->values.end(); ++it) {
+ if ((*it).role == role)
+ return (*it).value;
+ }
+ return QVariant();
+}
+
+/*!
+ \since 4.4
+
+ Causes the model associated with this item to emit a
+ \l{QAbstractItemModel::dataChanged()}{dataChanged}() signal for this
+ item.
+
+ You normally only need to call this function if you have subclassed
+ QStandardItem and reimplemented data() and/or setData().
+
+ \sa setData()
+*/
+void QStandardItem::emitDataChanged()
+{
+ Q_D(QStandardItem);
+ if (d->model)
+ d->model->d_func()->itemChanged(this);
+}
+
+/*!
+ Sets the item flags for the item to \a flags.
+
+ The item flags determine how the user can interact with the item.
+ This is often used to disable an item.
+
+ \sa flags(), setData()
+*/
+void QStandardItem::setFlags(Qt::ItemFlags flags)
+{
+ setData((int)flags, Qt::UserRole - 1);
+}
+
+/*!
+ Returns the item flags for the item.
+
+ The item flags determine how the user can interact with the item.
+
+ By default, items are enabled, editable, selectable, checkable, and can be
+ used both as the source of a drag and drop operation and as a drop target.
+
+ \sa setFlags()
+*/
+Qt::ItemFlags QStandardItem::flags() const
+{
+ QVariant v = data(Qt::UserRole - 1);
+ if (!v.isValid())
+ return (Qt::ItemIsSelectable|Qt::ItemIsEnabled|Qt::ItemIsEditable
+ |Qt::ItemIsDragEnabled|Qt::ItemIsDropEnabled);
+ return ((Qt::ItemFlags)(v.toInt()));
+}
+
+/*!
+ \fn QString QStandardItem::text() const
+
+ Returns the item's text. This is the text that's presented to the user
+ in a view.
+
+ \sa setText()
+*/
+
+/*!
+ \fn void QStandardItem::setText(const QString &text)
+
+ Sets the item's text to the \a text specified.
+
+ \sa text(), setFont(), setForeground()
+*/
+
+/*!
+ \fn QIcon QStandardItem::icon() const
+
+ Returns the item's icon.
+
+ \sa setIcon(), {QAbstractItemView::iconSize}{iconSize}
+*/
+
+/*!
+ \fn void QStandardItem::setIcon(const QIcon &icon)
+
+ Sets the item's icon to the \a icon specified.
+*/
+
+/*!
+ \fn QString QStandardItem::statusTip() const
+
+ Returns the item's status tip.
+
+ \sa setStatusTip(), toolTip(), whatsThis()
+*/
+
+/*!
+ \fn void QStandardItem::setStatusTip(const QString &statusTip)
+
+ Sets the item's status tip to the string specified by \a statusTip.
+
+ \sa statusTip(), setToolTip(), setWhatsThis()
+*/
+
+/*!
+ \fn QString QStandardItem::toolTip() const
+
+ Returns the item's tooltip.
+
+ \sa setToolTip(), statusTip(), whatsThis()
+*/
+
+/*!
+ \fn void QStandardItem::setToolTip(const QString &toolTip)
+
+ Sets the item's tooltip to the string specified by \a toolTip.
+
+ \sa toolTip(), setStatusTip(), setWhatsThis()
+*/
+
+/*!
+ \fn QString QStandardItem::whatsThis() const
+
+ Returns the item's "What's This?" help.
+
+ \sa setWhatsThis(), toolTip(), statusTip()
+*/
+
+/*!
+ \fn void QStandardItem::setWhatsThis(const QString &whatsThis)
+
+ Sets the item's "What's This?" help to the string specified by \a whatsThis.
+
+ \sa whatsThis(), setStatusTip(), setToolTip()
+*/
+
+/*!
+ \fn QFont QStandardItem::font() const
+
+ Returns the font used to render the item's text.
+
+ \sa setFont()
+*/
+
+/*!
+ \fn void QStandardItem::setFont(const QFont &font)
+
+ Sets the font used to display the item's text to the given \a font.
+
+ \sa font() setText() setForeground()
+*/
+
+/*!
+ \fn QBrush QStandardItem::background() const
+
+ Returns the brush used to render the item's background.
+
+ \sa foreground() setBackground()
+*/
+
+/*!
+ \fn void QStandardItem::setBackground(const QBrush &brush)
+
+ Sets the item's background brush to the specified \a brush.
+
+ \sa background() setForeground()
+*/
+
+/*!
+ \fn QBrush QStandardItem::foreground() const
+
+ Returns the brush used to render the item's foreground (e.g. text).
+
+ \sa setForeground() background()
+*/
+
+/*!
+ \fn void QStandardItem::setForeground(const QBrush &brush)
+
+ Sets the brush used to display the item's foreground (e.g. text) to the
+ given \a brush.
+
+ \sa foreground() setBackground() setFont()
+*/
+
+/*!
+ \fn int QStandardItem::textAlignment() const
+
+ Returns the text alignment for the item's text.
+*/
+
+/*!
+ \fn void QStandardItem::setTextAlignment(Qt::Alignment alignment)
+
+ Sets the text alignment for the item's text to the \a alignment
+ specified.
+
+ \sa textAlignment()
+*/
+
+/*!
+ \fn QSize QStandardItem::sizeHint() const
+
+ Returns the size hint set for the item, or an invalid QSize if no
+ size hint has been set.
+
+ If no size hint has been set, the item delegate will compute the
+ size hint based on the item data.
+
+ \sa setSizeHint()
+*/
+
+/*!
+ \fn void QStandardItem::setSizeHint(const QSize &size)
+
+ Sets the size hint for the item to be \a size.
+ If no size hint is set, the item delegate will compute the
+ size hint based on the item data.
+
+ \sa sizeHint()
+*/
+
+/*!
+ \fn Qt::CheckState QStandardItem::checkState() const
+
+ Returns the checked state of the item.
+
+ \sa setCheckState(), isCheckable()
+*/
+
+/*!
+ \fn void QStandardItem::setCheckState(Qt::CheckState state)
+
+ Sets the check state of the item to be \a state.
+
+ \sa checkState(), setCheckable()
+*/
+
+/*!
+ \fn QString QStandardItem::accessibleText() const
+
+ Returns the item's accessible text.
+
+ The accessible text is used by assistive technologies (i.e. for users who
+ cannot use conventional means of interaction).
+
+ \sa setAccessibleText(), accessibleDescription()
+*/
+
+/*!
+ \fn void QStandardItem::setAccessibleText(const QString &accessibleText)
+
+ Sets the item's accessible text to the string specified by \a accessibleText.
+
+ The accessible text is used by assistive technologies (i.e. for users who
+ cannot use conventional means of interaction).
+
+ \sa accessibleText(), setAccessibleDescription()
+*/
+
+/*!
+ \fn QString QStandardItem::accessibleDescription() const
+
+ Returns the item's accessible description.
+
+ The accessible description is used by assistive technologies (i.e. for
+ users who cannot use conventional means of interaction).
+
+ \sa setAccessibleDescription(), accessibleText()
+*/
+
+/*!
+ \fn void QStandardItem::setAccessibleDescription(const QString &accessibleDescription)
+
+ Sets the item's accessible description to the string specified by \a
+ accessibleDescription.
+
+ The accessible description is used by assistive technologies (i.e. for
+ users who cannot use conventional means of interaction).
+
+ \sa accessibleDescription(), setAccessibleText()
+*/
+
+/*!
+ Sets whether the item is enabled. If \a enabled is true, the item is enabled,
+ meaning that the user can interact with the item; if \a enabled is false, the
+ user cannot interact with the item.
+
+ This flag takes presedence over the other item flags; e.g. if an item is not
+ enabled, it cannot be selected by the user, even if the Qt::ItemIsSelectable
+ flag has been set.
+
+ \sa isEnabled(), Qt::ItemIsEnabled, setFlags()
+*/
+void QStandardItem::setEnabled(bool enabled)
+{
+ Q_D(QStandardItem);
+ d->changeFlags(enabled, Qt::ItemIsEnabled);
+}
+
+/*!
+ \fn bool QStandardItem::isEnabled() const
+
+ Returns whether the item is enabled.
+
+ When an item is enabled, the user can interact with it. The possible
+ types of interaction are specified by the other item flags, such as
+ isEditable() and isSelectable().
+
+ The default value is true.
+
+ \sa setEnabled(), flags()
+*/
+
+/*!
+ Sets whether the item is editable. If \a editable is true, the item can be
+ edited by the user; otherwise, the user cannot edit the item.
+
+ How the user can edit items in a view is determined by the view's edit
+ triggers; see QAbstractItemView::editTriggers.
+
+ \sa isEditable(), setFlags()
+*/
+void QStandardItem::setEditable(bool editable)
+{
+ Q_D(QStandardItem);
+ d->changeFlags(editable, Qt::ItemIsEditable);
+}
+
+/*!
+ \fn bool QStandardItem::isEditable() const
+
+ Returns whether the item can be edited by the user.
+
+ When an item is editable (and enabled), the user can edit the item by
+ invoking one of the view's edit triggers; see
+ QAbstractItemView::editTriggers.
+
+ The default value is true.
+
+ \sa setEditable(), flags()
+*/
+
+/*!
+ Sets whether the item is selectable. If \a selectable is true, the item
+ can be selected by the user; otherwise, the user cannot select the item.
+
+ You can control the selection behavior and mode by manipulating their
+ view properties; see QAbstractItemView::selectionMode and
+ QAbstractItemView::selectionBehavior.
+
+ \sa isSelectable(), setFlags()
+*/
+void QStandardItem::setSelectable(bool selectable)
+{
+ Q_D(QStandardItem);
+ d->changeFlags(selectable, Qt::ItemIsSelectable);
+}
+
+/*!
+ \fn bool QStandardItem::isSelectable() const
+
+ Returns whether the item is selectable by the user.
+
+ The default value is true.
+
+ \sa setSelectable(), flags()
+*/
+
+/*!
+ Sets whether the item is user-checkable. If \a checkable is true, the
+ item can be checked by the user; otherwise, the user cannot check
+ the item.
+
+ The item delegate will render a checkable item with a check box next to the
+ item's text.
+
+ \sa isCheckable(), setCheckState(), setTristate()
+*/
+void QStandardItem::setCheckable(bool checkable)
+{
+ Q_D(QStandardItem);
+ if (checkable && !isCheckable()) {
+ // make sure there's data for the checkstate role
+ if (!data(Qt::CheckStateRole).isValid())
+ setData(Qt::Unchecked, Qt::CheckStateRole);
+ }
+ d->changeFlags(checkable, Qt::ItemIsUserCheckable);
+}
+
+/*!
+ \fn bool QStandardItem::isCheckable() const
+
+ Returns whether the item is user-checkable.
+
+ The default value is false.
+
+ \sa setCheckable(), checkState(), isTristate()
+*/
+
+/*!
+ Sets whether the item is tristate. If \a tristate is true, the
+ item is checkable with three separate states; otherwise, the item
+ is checkable with two states. (Note that this also requires that
+ the item is checkable; see isCheckable().)
+
+ \sa isTristate(), setCheckable(), setCheckState()
+*/
+void QStandardItem::setTristate(bool tristate)
+{
+ Q_D(QStandardItem);
+ d->changeFlags(tristate, Qt::ItemIsTristate);
+}
+
+/*!
+ \fn bool QStandardItem::isTristate() const
+
+ Returns whether the item is tristate; that is, if it's checkable with three
+ separate states.
+
+ The default value is false.
+
+ \sa setTristate(), isCheckable(), checkState()
+*/
+
+#ifndef QT_NO_DRAGANDDROP
+
+/*!
+ Sets whether the item is drag enabled. If \a dragEnabled is true, the item
+ can be dragged by the user; otherwise, the user cannot drag the item.
+
+ Note that you also need to ensure that item dragging is enabled in the view;
+ see QAbstractItemView::dragEnabled.
+
+ \sa isDragEnabled(), setDropEnabled(), setFlags()
+*/
+void QStandardItem::setDragEnabled(bool dragEnabled)
+{
+ Q_D(QStandardItem);
+ d->changeFlags(dragEnabled, Qt::ItemIsDragEnabled);
+}
+
+/*!
+ \fn bool QStandardItem::isDragEnabled() const
+
+ Returns whether the item is drag enabled. An item that is drag enabled can
+ be dragged by the user.
+
+ The default value is true.
+
+ Note that item dragging must be enabled in the view for dragging to work;
+ see QAbstractItemView::dragEnabled.
+
+ \sa setDragEnabled(), isDropEnabled(), flags()
+*/
+
+/*!
+ Sets whether the item is drop enabled. If \a dropEnabled is true, the item
+ can be used as a drop target; otherwise, it cannot.
+
+ Note that you also need to ensure that drops are enabled in the view; see
+ QWidget::acceptDrops(); and that the model supports the desired drop actions;
+ see QAbstractItemModel::supportedDropActions().
+
+ \sa isDropEnabled(), setDragEnabled(), setFlags()
+*/
+void QStandardItem::setDropEnabled(bool dropEnabled)
+{
+ Q_D(QStandardItem);
+ d->changeFlags(dropEnabled, Qt::ItemIsDropEnabled);
+}
+
+/*!
+ \fn bool QStandardItem::isDropEnabled() const
+
+ Returns whether the item is drop enabled. When an item is drop enabled, it
+ can be used as a drop target.
+
+ The default value is true.
+
+ \sa setDropEnabled(), isDragEnabled(), flags()
+*/
+
+#endif // QT_NO_DRAGANDDROP
+
+/*!
+ Returns the row where the item is located in its parent's child table, or
+ -1 if the item has no parent.
+
+ \sa column(), parent()
+*/
+int QStandardItem::row() const
+{
+ Q_D(const QStandardItem);
+ QPair<int, int> pos = d->position();
+ return pos.first;
+}
+
+/*!
+ Returns the column where the item is located in its parent's child table,
+ or -1 if the item has no parent.
+
+ \sa row(), parent()
+*/
+int QStandardItem::column() const
+{
+ Q_D(const QStandardItem);
+ QPair<int, int> pos = d->position();
+ return pos.second;
+}
+
+/*!
+ Returns the QModelIndex associated with this item.
+
+ When you need to invoke item functionality in a QModelIndex-based API (e.g.
+ QAbstractItemView), you can call this function to obtain an index that
+ corresponds to the item's location in the model.
+
+ If the item is not associated with a model, an invalid QModelIndex is
+ returned.
+
+ \sa model(), QStandardItemModel::itemFromIndex()
+*/
+QModelIndex QStandardItem::index() const
+{
+ Q_D(const QStandardItem);
+ return d->model ? d->model->indexFromItem(this) : QModelIndex();
+}
+
+/*!
+ Returns the QStandardItemModel that this item belongs to.
+
+ If the item is not a child of another item that belongs to the model, this
+ function returns 0.
+
+ \sa index()
+*/
+QStandardItemModel *QStandardItem::model() const
+{
+ Q_D(const QStandardItem);
+ return d->model;
+}
+
+/*!
+ Sets the number of child item rows to \a rows. If this is less than
+ rowCount(), the data in the unwanted rows is discarded.
+
+ \sa rowCount(), setColumnCount()
+*/
+void QStandardItem::setRowCount(int rows)
+{
+ int rc = rowCount();
+ if (rc == rows)
+ return;
+ if (rc < rows)
+ insertRows(qMax(rc, 0), rows - rc);
+ else
+ removeRows(qMax(rows, 0), rc - rows);
+}
+
+/*!
+ Returns the number of child item rows that the item has.
+
+ \sa setRowCount(), columnCount()
+*/
+int QStandardItem::rowCount() const
+{
+ Q_D(const QStandardItem);
+ return d->rowCount();
+}
+
+/*!
+ Sets the number of child item columns to \a columns. If this is less than
+ columnCount(), the data in the unwanted columns is discarded.
+
+ \sa columnCount(), setRowCount()
+*/
+void QStandardItem::setColumnCount(int columns)
+{
+ int cc = columnCount();
+ if (cc == columns)
+ return;
+ if (cc < columns)
+ insertColumns(qMax(cc, 0), columns - cc);
+ else
+ removeColumns(qMax(columns, 0), cc - columns);
+}
+
+/*!
+ Returns the number of child item columns that the item has.
+
+ \sa setColumnCount(), rowCount()
+*/
+int QStandardItem::columnCount() const
+{
+ Q_D(const QStandardItem);
+ return d->columnCount();
+}
+
+/*!
+ Inserts a row at \a row containing \a items. If necessary, the column
+ count is increased to the size of \a items.
+
+ \sa insertRows(), insertColumn()
+*/
+void QStandardItem::insertRow(int row, const QList<QStandardItem*> &items)
+{
+ Q_D(QStandardItem);
+ if (row < 0)
+ return;
+ if (columnCount() < items.count())
+ setColumnCount(items.count());
+ d->insertRows(row, 1, items);
+}
+
+/*!
+ Inserts \a items at \a row. The column count wont be changed.
+
+ \sa insertRow(), insertColumn()
+*/
+void QStandardItem::insertRows(int row, const QList<QStandardItem*> &items)
+{
+ Q_D(QStandardItem);
+ if (row < 0)
+ return;
+ d->insertRows(row, items);
+}
+
+/*!
+ Inserts a column at \a column containing \a items. If necessary,
+ the row count is increased to the size of \a items.
+
+ \sa insertColumns(), insertRow()
+*/
+void QStandardItem::insertColumn(int column, const QList<QStandardItem*> &items)
+{
+ Q_D(QStandardItem);
+ if (column < 0)
+ return;
+ if (rowCount() < items.count())
+ setRowCount(items.count());
+ d->insertColumns(column, 1, items);
+}
+
+/*!
+ Inserts \a count rows of child items at row \a row.
+
+ \sa insertRow(), insertColumns()
+*/
+void QStandardItem::insertRows(int row, int count)
+{
+ Q_D(QStandardItem);
+ if (rowCount() < row) {
+ count += row - rowCount();
+ row = rowCount();
+ }
+ d->insertRows(row, count, QList<QStandardItem*>());
+}
+
+/*!
+ Inserts \a count columns of child items at column \a column.
+
+ \sa insertColumn(), insertRows()
+*/
+void QStandardItem::insertColumns(int column, int count)
+{
+ Q_D(QStandardItem);
+ if (columnCount() < column) {
+ count += column - columnCount();
+ column = columnCount();
+ }
+ d->insertColumns(column, count, QList<QStandardItem*>());
+}
+
+/*!
+ \fn void QStandardItem::appendRow(const QList<QStandardItem*> &items)
+
+ Appends a row containing \a items. If necessary, the column count is
+ increased to the size of \a items.
+
+ \sa insertRow()
+*/
+
+/*!
+ \fn void QStandardItem::appendRows(const QList<QStandardItem*> &items)
+
+ Appends rows containing \a items. The column count will not change.
+
+ \sa insertRow()
+*/
+
+/*!
+ \fn void QStandardItem::appendColumn(const QList<QStandardItem*> &items)
+
+ Appends a column containing \a items. If necessary, the row count is
+ increased to the size of \a items.
+
+ \sa insertColumn()
+*/
+
+/*!
+ \fn bool QStandardItemModel::insertRow(int row, const QModelIndex &parent)
+
+ Inserts a single row before the given \a row in the child items of the
+ \a parent specified. Returns true if the row is inserted; otherwise
+ returns false.
+
+ \sa insertRows(), insertColumn(), removeRow()
+*/
+
+/*!
+ \fn bool QStandardItemModel::insertColumn(int column, const QModelIndex &parent)
+
+ Inserts a single column before the given \a column in the child items of
+ the \a parent specified. Returns true if the column is inserted; otherwise
+ returns false.
+
+ \sa insertColumns(), insertRow(), removeColumn()
+*/
+
+/*!
+ \fn QStandardItem::insertRow(int row, QStandardItem *item)
+ \overload
+
+ Inserts a row at \a row containing \a item.
+
+ When building a list or a tree that has only one column, this function
+ provides a convenient way to insert a single new item.
+*/
+
+/*!
+ \fn QStandardItem::appendRow(QStandardItem *item)
+ \overload
+
+ Appends a row containing \a item.
+
+ When building a list or a tree that has only one column, this function
+ provides a convenient way to append a single new item.
+*/
+
+/*!
+ Removes the given \a row. The items that were in the row are deleted.
+
+ \sa takeRow(), removeRows(), removeColumn()
+*/
+void QStandardItem::removeRow(int row)
+{
+ removeRows(row, 1);
+}
+
+/*!
+ Removes the given \a column. The items that were in the
+ column are deleted.
+
+ \sa takeColumn(), removeColumns(), removeRow()
+*/
+void QStandardItem::removeColumn(int column)
+{
+ removeColumns(column, 1);
+}
+
+/*!
+ Removes \a count rows at row \a row. The items that were in those rows are
+ deleted.
+
+ \sa removeRow(), removeColumn()
+*/
+void QStandardItem::removeRows(int row, int count)
+{
+ Q_D(QStandardItem);
+ if ((count < 1) || (row < 0) || ((row + count) > rowCount()))
+ return;
+ if (d->model)
+ d->model->d_func()->rowsAboutToBeRemoved(this, row, row + count - 1);
+ int i = d->childIndex(row, 0);
+ int n = count * d->columnCount();
+ for (int j = i; j < n+i; ++j) {
+ QStandardItem *oldItem = d->children.at(j);
+ if (oldItem)
+ oldItem->d_func()->setModel(0);
+ delete oldItem;
+ }
+ d->children.remove(qMax(i, 0), n);
+ d->rows -= count;
+ if (d->model)
+ d->model->d_func()->rowsRemoved(this, row, count);
+}
+
+/*!
+ Removes \a count columns at column \a column. The items that were in those
+ columns are deleted.
+
+ \sa removeColumn(), removeRows()
+*/
+void QStandardItem::removeColumns(int column, int count)
+{
+ Q_D(QStandardItem);
+ if ((count < 1) || (column < 0) || ((column + count) > columnCount()))
+ return;
+ if (d->model)
+ d->model->d_func()->columnsAboutToBeRemoved(this, column, column + count - 1);
+ for (int row = d->rowCount() - 1; row >= 0; --row) {
+ int i = d->childIndex(row, column);
+ for (int j=i; j<i+count; ++j) {
+ QStandardItem *oldItem = d->children.at(j);
+ if (oldItem)
+ oldItem->d_func()->setModel(0);
+ delete oldItem;
+ }
+ d->children.remove(i, count);
+ }
+ d->columns -= count;
+ if (d->model)
+ d->model->d_func()->columnsRemoved(this, column, count);
+}
+
+/*!
+ Returns true if this item has any children; otherwise returns false.
+
+ \sa rowCount(), columnCount(), child()
+*/
+bool QStandardItem::hasChildren() const
+{
+ return (rowCount() > 0) && (columnCount() > 0);
+}
+
+/*!
+ Sets the child item at (\a row, \a column) to \a item. This item (the parent
+ item) takes ownership of \a item. If necessary, the row count and column
+ count are increased to fit the item.
+
+ \sa child()
+*/
+void QStandardItem::setChild(int row, int column, QStandardItem *item)
+{
+ Q_D(QStandardItem);
+ d->setChild(row, column, item, true);
+}
+
+/*!
+ \fn QStandardItem::setChild(int row, QStandardItem *item)
+ \overload
+
+ Sets the child at \a row to \a item.
+*/
+
+/*!
+ Returns the child item at (\a row, \a column) if one has been set; otherwise
+ returns 0.
+
+ \sa setChild(), takeChild(), parent()
+*/
+QStandardItem *QStandardItem::child(int row, int column) const
+{
+ Q_D(const QStandardItem);
+ int index = d->childIndex(row, column);
+ if (index == -1)
+ return 0;
+ return d->children.at(index);
+}
+
+/*!
+ Removes the child item at (\a row, \a column) without deleting it, and returns
+ a pointer to the item. If there was no child at the given location, then
+ this function returns 0.
+
+ Note that this function, unlike takeRow() and takeColumn(), does not affect
+ the dimensions of the child table.
+
+ \sa child(), takeRow(), takeColumn()
+*/
+QStandardItem *QStandardItem::takeChild(int row, int column)
+{
+ Q_D(QStandardItem);
+ QStandardItem *item = 0;
+ int index = d->childIndex(row, column);
+ if (index != -1) {
+ item = d->children.at(index);
+ if (item)
+ item->d_func()->setParentAndModel(0, 0);
+ d->children.replace(index, 0);
+ }
+ return item;
+}
+
+/*!
+ Removes \a row without deleting the row items, and returns a list of
+ pointers to the removed items. For items in the row that have not been
+ set, the corresponding pointers in the list will be 0.
+
+ \sa removeRow(), insertRow(), takeColumn()
+*/
+QList<QStandardItem*> QStandardItem::takeRow(int row)
+{
+ Q_D(QStandardItem);
+ if ((row < 0) || (row >= rowCount()))
+ return QList<QStandardItem*>();
+ QList<QStandardItem*> items;
+ int index = d->childIndex(row, 0);
+ for (int column = 0; column < d->columnCount(); ++column) {
+ QStandardItem *ch = d->children.at(index);
+ if (ch) {
+ ch->d_func()->setParentAndModel(0, 0);
+ d->children.replace(index, 0);
+ }
+ items.append(ch);
+ ++index;
+ }
+ removeRow(row);
+ return items;
+}
+
+/*!
+ Removes \a column without deleting the column items, and returns a list of
+ pointers to the removed items. For items in the column that have not been
+ set, the corresponding pointers in the list will be 0.
+
+ \sa removeColumn(), insertColumn(), takeRow()
+*/
+QList<QStandardItem*> QStandardItem::takeColumn(int column)
+{
+ Q_D(QStandardItem);
+ if ((column < 0) || (column >= columnCount()))
+ return QList<QStandardItem*>();
+ QList<QStandardItem*> items;
+ int index = d->childIndex(0, column);
+ for (int row = 0; row < d->rowCount(); ++row) {
+ QStandardItem *ch = d->children.at(index);
+ if (ch) {
+ ch->d_func()->setParentAndModel(0, 0);
+ d->children.replace(index, 0);
+ }
+ items.append(ch);
+ index += d->columnCount();
+ }
+ removeColumn(column);
+ return items;
+}
+
+/*!
+ Returns true if this item is less than \a other; otherwise returns false.
+
+ The default implementation uses the data for the item's sort role (see
+ QStandardItemModel::sortRole) to perform the comparison if the item
+ belongs to a model; otherwise, the data for the item's Qt::DisplayRole
+ (text()) is used to perform the comparison.
+
+ sortChildren() and QStandardItemModel::sort() use this function when
+ sorting items. If you want custom sorting, you can subclass QStandardItem
+ and reimplement this function.
+*/
+bool QStandardItem::operator<(const QStandardItem &other) const
+{
+ const int role = model() ? model()->sortRole() : Qt::DisplayRole;
+ const QVariant l = data(role), r = other.data(role);
+ // this code is copied from QSortFilterProxyModel::lessThan()
+ switch (l.type()) {
+ case QVariant::Invalid:
+ return (r.type() == QVariant::Invalid);
+ case QVariant::Int:
+ return l.toInt() < r.toInt();
+ case QVariant::UInt:
+ return l.toUInt() < r.toUInt();
+ case QVariant::LongLong:
+ return l.toLongLong() < r.toLongLong();
+ case QVariant::ULongLong:
+ return l.toULongLong() < r.toULongLong();
+ case QVariant::Double:
+ return l.toDouble() < r.toDouble();
+ case QVariant::Char:
+ return l.toChar() < r.toChar();
+ case QVariant::Date:
+ return l.toDate() < r.toDate();
+ case QVariant::Time:
+ return l.toTime() < r.toTime();
+ case QVariant::DateTime:
+ return l.toDateTime() < r.toDateTime();
+ case QVariant::String:
+ default:
+ return l.toString().compare(r.toString()) < 0;
+ }
+}
+
+/*!
+ Sorts the children of the item using the given \a order, by the values in
+ the given \a column.
+
+ \note This function is recursive, therefore it sorts the children of the
+ item, its grandchildren, etc.
+
+ \sa {operator<()}
+*/
+void QStandardItem::sortChildren(int column, Qt::SortOrder order)
+{
+ Q_D(QStandardItem);
+ if ((column < 0) || (rowCount() == 0))
+ return;
+ if (d->model)
+ emit d->model->layoutAboutToBeChanged();
+ d->sortChildren(column, order);
+ if (d->model)
+ emit d->model->layoutChanged();
+}
+
+/*!
+ Returns a copy of this item. The item's children are not copied.
+
+ When subclassing QStandardItem, you can reimplement this function
+ to provide QStandardItemModel with a factory that it can use to
+ create new items on demand.
+
+ \sa QStandardItemModel::setItemPrototype(), operator=()
+*/
+QStandardItem *QStandardItem::clone() const
+{
+ return new QStandardItem(*this);
+}
+
+/*!
+ Returns the type of this item. The type is used to distinguish custom
+ items from the base class. When subclassing QStandardItem, you should
+ reimplement this function and return a new value greater than or equal
+ to \l UserType.
+
+ \sa QStandardItem::Type
+*/
+int QStandardItem::type() const
+{
+ return Type;
+}
+
+#ifndef QT_NO_DATASTREAM
+
+/*!
+ Reads the item from stream \a in. Only the data and flags of the item are
+ read, not the child items.
+
+ \sa write()
+*/
+void QStandardItem::read(QDataStream &in)
+{
+ Q_D(QStandardItem);
+ in >> d->values;
+ qint32 flags;
+ in >> flags;
+ setFlags((Qt::ItemFlags)flags);
+}
+
+/*!
+ Writes the item to stream \a out. Only the data and flags of the item
+ are written, not the child items.
+
+ \sa read()
+*/
+void QStandardItem::write(QDataStream &out) const
+{
+ Q_D(const QStandardItem);
+ out << d->values;
+ out << flags();
+}
+
+/*!
+ \relates QStandardItem
+ \since 4.2
+
+ Reads a QStandardItem from stream \a in into \a item.
+
+ This operator uses QStandardItem::read().
+
+ \sa {Format of the QDataStream Operators}
+*/
+QDataStream &operator>>(QDataStream &in, QStandardItem &item)
+{
+ item.read(in);
+ return in;
+}
+
+/*!
+ \relates QStandardItem
+ \since 4.2
+
+ Writes the QStandardItem \a item to stream \a out.
+
+ This operator uses QStandardItem::write().
+
+ \sa {Format of the QDataStream Operators}
+*/
+QDataStream &operator<<(QDataStream &out, const QStandardItem &item)
+{
+ item.write(out);
+ return out;
+}
+
+#endif // !QT_NO_DATASTREAM
+
+/*!
+ \class QStandardItemModel
+ \brief The QStandardItemModel class provides a generic model for storing custom data.
+ \ingroup model-view
+
+ QStandardItemModel can be used as a repository for standard Qt
+ data types. It is one of the \l {Model/View Classes} and is part
+ of Qt's \l {Model/View Programming}{model/view} framework.
+
+ QStandardItemModel provides a classic item-based approach to working with
+ the model. The items in a QStandardItemModel are provided by
+ QStandardItem.
+
+ QStandardItemModel implements the QAbstractItemModel interface, which
+ means that the model can be used to provide data in any view that supports
+ that interface (such as QListView, QTableView and QTreeView, and your own
+ custom views). For performance and flexibility, you may want to subclass
+ QAbstractItemModel to provide support for different kinds of data
+ repositories. For example, the QDirModel provides a model interface to the
+ underlying file system.
+
+ When you want a list or tree, you typically create an empty
+ QStandardItemModel and use appendRow() to add items to the model, and
+ item() to access an item. If your model represents a table, you typically
+ pass the dimensions of the table to the QStandardItemModel constructor and
+ use setItem() to position items into the table. You can also use setRowCount()
+ and setColumnCount() to alter the dimensions of the model. To insert items,
+ use insertRow() or insertColumn(), and to remove items, use removeRow() or
+ removeColumn().
+
+ You can set the header labels of your model with setHorizontalHeaderLabels()
+ and setVerticalHeaderLabels().
+
+ You can search for items in the model with findItems(), and sort the model by
+ calling sort().
+
+ Call clear() to remove all items from the model.
+
+ An example usage of QStandardItemModel to create a table:
+
+ \snippet doc/src/snippets/code/src_gui_itemviews_qstandarditemmodel.cpp 0
+
+ An example usage of QStandardItemModel to create a tree:
+
+ \snippet doc/src/snippets/code/src_gui_itemviews_qstandarditemmodel.cpp 1
+
+ After setting the model on a view, you typically want to react to user
+ actions, such as an item being clicked. Since a QAbstractItemView provides
+ QModelIndex-based signals and functions, you need a way to obtain the
+ QStandardItem that corresponds to a given QModelIndex, and vice
+ versa. itemFromIndex() and indexFromItem() provide this mapping. Typical
+ usage of itemFromIndex() includes obtaining the item at the current index
+ in a view, and obtaining the item that corresponds to an index carried by
+ a QAbstractItemView signal, such as QAbstractItemView::clicked(). First
+ you connect the view's signal to a slot in your class:
+
+ \snippet doc/src/snippets/code/src_gui_itemviews_qstandarditemmodel.cpp 2
+
+ When you receive the signal, you call itemFromIndex() on the given model
+ index to get a pointer to the item:
+
+ \snippet doc/src/snippets/code/src_gui_itemviews_qstandarditemmodel.cpp 3
+
+ Conversely, you must obtain the QModelIndex of an item when you want to
+ invoke a model/view function that takes an index as argument. You can
+ obtain the index either by using the model's indexFromItem() function, or,
+ equivalently, by calling QStandardItem::index():
+
+ \snippet doc/src/snippets/code/src_gui_itemviews_qstandarditemmodel.cpp 4
+
+ You are, of course, not required to use the item-based approach; you could
+ instead rely entirely on the QAbstractItemModel interface when working with
+ the model, or use a combination of the two as appropriate.
+
+ \sa QStandardItem, {Model/View Programming}, QAbstractItemModel,
+ {itemviews/simpletreemodel}{Simple Tree Model example},
+ {Item View Convenience Classes}
+*/
+
+/*!
+ \fn void QStandardItemModel::itemChanged(QStandardItem *item)
+ \since 4.2
+
+ This signal is emitted whenever the data of \a item has changed.
+*/
+
+/*!
+ Constructs a new item model with the given \a parent.
+*/
+QStandardItemModel::QStandardItemModel(QObject *parent)
+ : QAbstractItemModel(*new QStandardItemModelPrivate, parent)
+{
+ Q_D(QStandardItemModel);
+ d->init();
+ d->root->d_func()->setModel(this);
+}
+
+/*!
+ Constructs a new item model that initially has \a rows rows and \a columns
+ columns, and that has the given \a parent.
+*/
+QStandardItemModel::QStandardItemModel(int rows, int columns, QObject *parent)
+ : QAbstractItemModel(*new QStandardItemModelPrivate, parent)
+{
+ Q_D(QStandardItemModel);
+ d->init();
+ d->root->insertColumns(0, columns);
+ d->columnHeaderItems.insert(0, columns, 0);
+ d->root->insertRows(0, rows);
+ d->rowHeaderItems.insert(0, rows, 0);
+ d->root->d_func()->setModel(this);
+}
+
+/*!
+ \internal
+*/
+QStandardItemModel::QStandardItemModel(QStandardItemModelPrivate &dd, QObject *parent)
+ : QAbstractItemModel(dd, parent)
+{
+ Q_D(QStandardItemModel);
+ d->init();
+}
+
+/*!
+ Destructs the model. The model destroys all its items.
+*/
+QStandardItemModel::~QStandardItemModel()
+{
+}
+
+/*!
+ Removes all items (including header items) from the model and sets the
+ number of rows and columns to zero.
+
+ \sa removeColumns(), removeRows()
+*/
+void QStandardItemModel::clear()
+{
+ Q_D(QStandardItemModel);
+ delete d->root;
+ d->root = new QStandardItem;
+ d->root->d_func()->setModel(this);
+ qDeleteAll(d->columnHeaderItems);
+ d->columnHeaderItems.clear();
+ qDeleteAll(d->rowHeaderItems);
+ d->rowHeaderItems.clear();
+ reset();
+}
+
+/*!
+ \since 4.2
+
+ Returns a pointer to the QStandardItem associated with the given \a index.
+
+ Calling this function is typically the initial step when processing
+ QModelIndex-based signals from a view, such as
+ QAbstractItemView::activated(). In your slot, you call itemFromIndex(),
+ with the QModelIndex carried by the signal as argument, to obtain a
+ pointer to the corresponding QStandardItem.
+
+ Note that this function will lazily create an item for the index (using
+ itemPrototype()), and set it in the parent item's child table, if no item
+ already exists at that index.
+
+ If \a index is an invalid index, this function returns 0.
+
+ \sa indexFromItem()
+*/
+QStandardItem *QStandardItemModel::itemFromIndex(const QModelIndex &index) const
+{
+ Q_D(const QStandardItemModel);
+ if ((index.row() < 0) || (index.column() < 0) || (index.model() != this))
+ return 0;
+ QStandardItem *parent = static_cast<QStandardItem*>(index.internalPointer());
+ if (parent == 0)
+ return 0;
+ QStandardItem *item = parent->child(index.row(), index.column());
+ // lazy part
+ if (item == 0) {
+ item = d->createItem();
+ parent->d_func()->setChild(index.row(), index.column(), item);
+ }
+ return item;
+}
+
+/*!
+ \since 4.2
+
+ Returns the QModelIndex associated with the given \a item.
+
+ Use this function when you want to perform an operation that requires the
+ QModelIndex of the item, such as
+ QAbstractItemView::scrollTo(). QStandardItem::index() is provided as
+ convenience; it is equivalent to calling this function.
+
+ \sa itemFromIndex(), QStandardItem::index()
+*/
+QModelIndex QStandardItemModel::indexFromItem(const QStandardItem *item) const
+{
+ if (item && item->d_func()->parent) {
+ QPair<int, int> pos = item->d_func()->position();
+ return createIndex(pos.first, pos.second, item->d_func()->parent);
+ }
+ return QModelIndex();
+}
+
+/*!
+ \since 4.2
+
+ Sets the number of rows in this model to \a rows. If
+ this is less than rowCount(), the data in the unwanted rows
+ is discarded.
+
+ \sa setColumnCount()
+*/
+void QStandardItemModel::setRowCount(int rows)
+{
+ Q_D(QStandardItemModel);
+ d->root->setRowCount(rows);
+}
+
+/*!
+ \since 4.2
+
+ Sets the number of columns in this model to \a columns. If
+ this is less than columnCount(), the data in the unwanted columns
+ is discarded.
+
+ \sa setRowCount()
+*/
+void QStandardItemModel::setColumnCount(int columns)
+{
+ Q_D(QStandardItemModel);
+ d->root->setColumnCount(columns);
+}
+
+/*!
+ \since 4.2
+
+ Sets the item for the given \a row and \a column to \a item. The model
+ takes ownership of the item. If necessary, the row count and column count
+ are increased to fit the item. The previous item at the given location (if
+ there was one) is deleted.
+
+ \sa item()
+*/
+void QStandardItemModel::setItem(int row, int column, QStandardItem *item)
+{
+ Q_D(QStandardItemModel);
+ d->root->d_func()->setChild(row, column, item, true);
+}
+
+/*!
+ \fn QStandardItemModel::setItem(int row, QStandardItem *item)
+ \overload
+*/
+
+/*!
+ \since 4.2
+
+ Returns the item for the given \a row and \a column if one has been set;
+ otherwise returns 0.
+
+ \sa setItem(), takeItem(), itemFromIndex()
+*/
+QStandardItem *QStandardItemModel::item(int row, int column) const
+{
+ Q_D(const QStandardItemModel);
+ return d->root->child(row, column);
+}
+
+/*!
+ \since 4.2
+
+ Returns the model's invisible root item.
+
+ The invisible root item provides access to the model's top-level items
+ through the QStandardItem API, making it possible to write functions that
+ can treat top-level items and their children in a uniform way; for
+ example, recursive functions involving a tree model.
+
+ \note Calling \l{QAbstractItemModel::index()}{index()} on the QStandardItem object
+ retrieved from this function is not valid.
+*/
+QStandardItem *QStandardItemModel::invisibleRootItem() const
+{
+ Q_D(const QStandardItemModel);
+ return d->root;
+}
+
+/*!
+ \since 4.2
+
+ Sets the horizontal header item for \a column to \a item. The model takes
+ ownership of the item. If necessary, the column count is increased to fit
+ the item. The previous header item (if there was one) is deleted.
+
+ \sa horizontalHeaderItem(), setHorizontalHeaderLabels(),
+ setVerticalHeaderItem()
+*/
+void QStandardItemModel::setHorizontalHeaderItem(int column, QStandardItem *item)
+{
+ Q_D(QStandardItemModel);
+ if (column < 0)
+ return;
+ if (columnCount() <= column)
+ setColumnCount(column + 1);
+
+ QStandardItem *oldItem = d->columnHeaderItems.at(column);
+ if (item == oldItem)
+ return;
+
+ if (item) {
+ if (item->model() == 0) {
+ item->d_func()->setModel(this);
+ } else {
+ qWarning("QStandardItem::setHorizontalHeaderItem: Ignoring duplicate insertion of item %p",
+ item);
+ return;
+ }
+ }
+
+ if (oldItem)
+ oldItem->d_func()->setModel(0);
+ delete oldItem;
+
+ d->columnHeaderItems.replace(column, item);
+ emit headerDataChanged(Qt::Horizontal, column, column);
+}
+
+/*!
+ \since 4.2
+
+ Returns the horizontal header item for \a column if one has been set;
+ otherwise returns 0.
+
+ \sa setHorizontalHeaderItem(), verticalHeaderItem()
+*/
+QStandardItem *QStandardItemModel::horizontalHeaderItem(int column) const
+{
+ Q_D(const QStandardItemModel);
+ if ((column < 0) || (column >= columnCount()))
+ return 0;
+ return d->columnHeaderItems.at(column);
+}
+
+/*!
+ \since 4.2
+
+ Sets the vertical header item for \a row to \a item. The model takes
+ ownership of the item. If necessary, the row count is increased to fit the
+ item. The previous header item (if there was one) is deleted.
+
+ \sa verticalHeaderItem(), setVerticalHeaderLabels(),
+ setHorizontalHeaderItem()
+*/
+void QStandardItemModel::setVerticalHeaderItem(int row, QStandardItem *item)
+{
+ Q_D(QStandardItemModel);
+ if (row < 0)
+ return;
+ if (rowCount() <= row)
+ setRowCount(row + 1);
+
+ QStandardItem *oldItem = d->rowHeaderItems.at(row);
+ if (item == oldItem)
+ return;
+
+ if (item) {
+ if (item->model() == 0) {
+ item->d_func()->setModel(this);
+ } else {
+ qWarning("QStandardItem::setVerticalHeaderItem: Ignoring duplicate insertion of item %p",
+ item);
+ return;
+ }
+ }
+
+ if (oldItem)
+ oldItem->d_func()->setModel(0);
+ delete oldItem;
+
+ d->rowHeaderItems.replace(row, item);
+ emit headerDataChanged(Qt::Vertical, row, row);
+}
+
+/*!
+ \since 4.2
+
+ Returns the vertical header item for row \a row if one has been set;
+ otherwise returns 0.
+
+ \sa setVerticalHeaderItem(), horizontalHeaderItem()
+*/
+QStandardItem *QStandardItemModel::verticalHeaderItem(int row) const
+{
+ Q_D(const QStandardItemModel);
+ if ((row < 0) || (row >= rowCount()))
+ return 0;
+ return d->rowHeaderItems.at(row);
+}
+
+/*!
+ \since 4.2
+
+ Sets the horizontal header labels using \a labels. If necessary, the
+ column count is increased to the size of \a labels.
+
+ \sa setHorizontalHeaderItem()
+*/
+void QStandardItemModel::setHorizontalHeaderLabels(const QStringList &labels)
+{
+ Q_D(QStandardItemModel);
+ if (columnCount() < labels.count())
+ setColumnCount(labels.count());
+ for (int i = 0; i < labels.count(); ++i) {
+ QStandardItem *item = horizontalHeaderItem(i);
+ if (!item) {
+ item = d->createItem();
+ setHorizontalHeaderItem(i, item);
+ }
+ item->setText(labels.at(i));
+ }
+}
+
+/*!
+ \since 4.2
+
+ Sets the vertical header labels using \a labels. If necessary, the row
+ count is increased to the size of \a labels.
+
+ \sa setVerticalHeaderItem()
+*/
+void QStandardItemModel::setVerticalHeaderLabels(const QStringList &labels)
+{
+ Q_D(QStandardItemModel);
+ if (rowCount() < labels.count())
+ setRowCount(labels.count());
+ for (int i = 0; i < labels.count(); ++i) {
+ QStandardItem *item = verticalHeaderItem(i);
+ if (!item) {
+ item = d->createItem();
+ setVerticalHeaderItem(i, item);
+ }
+ item->setText(labels.at(i));
+ }
+}
+
+/*!
+ \since 4.2
+
+ Sets the item prototype for the model to the specified \a item. The model
+ takes ownership of the prototype.
+
+ The item prototype acts as a QStandardItem factory, by relying on the
+ QStandardItem::clone() function. To provide your own prototype, subclass
+ QStandardItem, reimplement QStandardItem::clone() and set the prototype to
+ be an instance of your custom class. Whenever QStandardItemModel needs to
+ create an item on demand (for instance, when a view or item delegate calls
+ setData())), the new items will be instances of your custom class.
+
+ \sa itemPrototype(), QStandardItem::clone()
+*/
+void QStandardItemModel::setItemPrototype(const QStandardItem *item)
+{
+ Q_D(QStandardItemModel);
+ if (d->itemPrototype != item) {
+ delete d->itemPrototype;
+ d->itemPrototype = item;
+ }
+}
+
+/*!
+ \since 4.2
+
+ Returns the item prototype used by the model. The model uses the item
+ prototype as an item factory when it needs to construct new items on
+ demand (for instance, when a view or item delegate calls setData()).
+
+ \sa setItemPrototype()
+*/
+const QStandardItem *QStandardItemModel::itemPrototype() const
+{
+ Q_D(const QStandardItemModel);
+ return d->itemPrototype;
+}
+
+/*!
+ \since 4.2
+
+ Returns a list of items that match the given \a text, using the given \a
+ flags, in the given \a column.
+*/
+QList<QStandardItem*> QStandardItemModel::findItems(const QString &text,
+ Qt::MatchFlags flags, int column) const
+{
+ QModelIndexList indexes = match(index(0, column, QModelIndex()),
+ Qt::DisplayRole, text, -1, flags);
+ QList<QStandardItem*> items;
+ for (int i = 0; i < indexes.size(); ++i)
+ items.append(itemFromIndex(indexes.at(i)));
+ return items;
+}
+
+/*!
+ \since 4.2
+
+ Appends a row containing \a items. If necessary, the column count is
+ increased to the size of \a items.
+
+ \sa insertRow(), appendColumn()
+*/
+void QStandardItemModel::appendRow(const QList<QStandardItem*> &items)
+{
+ invisibleRootItem()->appendRow(items);
+}
+
+/*!
+ \since 4.2
+
+ Appends a column containing \a items. If necessary, the row count is
+ increased to the size of \a items.
+
+ \sa insertColumn(), appendRow()
+*/
+void QStandardItemModel::appendColumn(const QList<QStandardItem*> &items)
+{
+ invisibleRootItem()->appendColumn(items);
+}
+
+/*!
+ \since 4.2
+ \fn QStandardItemModel::appendRow(QStandardItem *item)
+ \overload
+
+ When building a list or a tree that has only one column, this function
+ provides a convenient way to append a single new \a item.
+*/
+
+/*!
+ \since 4.2
+
+ Inserts a row at \a row containing \a items. If necessary, the column
+ count is increased to the size of \a items.
+
+ \sa takeRow(), appendRow(), insertColumn()
+*/
+void QStandardItemModel::insertRow(int row, const QList<QStandardItem*> &items)
+{
+ invisibleRootItem()->insertRow(row, items);
+}
+
+/*!
+ \since 4.2
+
+ \fn void QStandardItemModel::insertRow(int row, QStandardItem *item)
+ \overload
+
+ Inserts a row at \a row containing \a item.
+
+ When building a list or a tree that has only one column, this function
+ provides a convenient way to append a single new item.
+*/
+
+/*!
+ \since 4.2
+
+ Inserts a column at \a column containing \a items. If necessary, the row
+ count is increased to the size of \a items.
+
+ \sa takeColumn(), appendColumn(), insertRow()
+*/
+void QStandardItemModel::insertColumn(int column, const QList<QStandardItem*> &items)
+{
+ invisibleRootItem()->insertColumn(column, items);
+}
+
+/*!
+ \since 4.2
+
+ Removes the item at (\a row, \a column) without deleting it. The model
+ releases ownership of the item.
+
+ \sa item(), takeRow(), takeColumn()
+*/
+QStandardItem *QStandardItemModel::takeItem(int row, int column)
+{
+ Q_D(QStandardItemModel);
+ return d->root->takeChild(row, column);
+}
+
+/*!
+ \since 4.2
+
+ Removes the given \a row without deleting the row items, and returns a
+ list of pointers to the removed items. The model releases ownership of the
+ items. For items in the row that have not been set, the corresponding
+ pointers in the list will be 0.
+
+ \sa takeColumn()
+*/
+QList<QStandardItem*> QStandardItemModel::takeRow(int row)
+{
+ Q_D(QStandardItemModel);
+ return d->root->takeRow(row);
+}
+
+/*!
+ \since 4.2
+
+ Removes the given \a column without deleting the column items, and returns
+ a list of pointers to the removed items. The model releases ownership of
+ the items. For items in the column that have not been set, the
+ corresponding pointers in the list will be 0.
+
+ \sa takeRow()
+*/
+QList<QStandardItem*> QStandardItemModel::takeColumn(int column)
+{
+ Q_D(QStandardItemModel);
+ return d->root->takeColumn(column);
+}
+
+/*!
+ \since 4.2
+
+ Removes the horizontal header item at \a column from the header without
+ deleting it, and returns a pointer to the item. The model releases
+ ownership of the item.
+
+ \sa horizontalHeaderItem(), takeVerticalHeaderItem()
+*/
+QStandardItem *QStandardItemModel::takeHorizontalHeaderItem(int column)
+{
+ Q_D(QStandardItemModel);
+ if ((column < 0) || (column >= columnCount()))
+ return 0;
+ QStandardItem *headerItem = d->columnHeaderItems.at(column);
+ if (headerItem) {
+ headerItem->d_func()->setParentAndModel(0, 0);
+ d->columnHeaderItems.replace(column, 0);
+ }
+ return headerItem;
+}
+
+/*!
+ \since 4.2
+
+ Removes the vertical header item at \a row from the header without
+ deleting it, and returns a pointer to the item. The model releases
+ ownership of the item.
+
+ \sa verticalHeaderItem(), takeHorizontalHeaderItem()
+*/
+QStandardItem *QStandardItemModel::takeVerticalHeaderItem(int row)
+{
+ Q_D(QStandardItemModel);
+ if ((row < 0) || (row >= rowCount()))
+ return 0;
+ QStandardItem *headerItem = d->rowHeaderItems.at(row);
+ if (headerItem) {
+ headerItem->d_func()->setParentAndModel(0, 0);
+ d->rowHeaderItems.replace(row, 0);
+ }
+ return headerItem;
+}
+
+/*!
+ \since 4.2
+ \property QStandardItemModel::sortRole
+ \brief the item role that is used to query the model's data when sorting items
+
+ The default value is Qt::DisplayRole.
+
+ \sa sort(), QStandardItem::sortChildren()
+*/
+int QStandardItemModel::sortRole() const
+{
+ Q_D(const QStandardItemModel);
+ return d->sortRole;
+}
+
+void QStandardItemModel::setSortRole(int role)
+{
+ Q_D(QStandardItemModel);
+ d->sortRole = role;
+}
+
+/*!
+ \reimp
+*/
+int QStandardItemModel::columnCount(const QModelIndex &parent) const
+{
+ Q_D(const QStandardItemModel);
+ QStandardItem *item = d->itemFromIndex(parent);
+ return item ? item->columnCount() : 0;
+}
+
+/*!
+ \reimp
+*/
+QVariant QStandardItemModel::data(const QModelIndex &index, int role) const
+{
+ Q_D(const QStandardItemModel);
+ QStandardItem *item = d->itemFromIndex(index);
+ return item ? item->data(role) : QVariant();
+}
+
+/*!
+ \reimp
+*/
+Qt::ItemFlags QStandardItemModel::flags(const QModelIndex &index) const
+{
+ Q_D(const QStandardItemModel);
+ if (!d->indexValid(index))
+ return d->root->flags();
+ QStandardItem *item = d->itemFromIndex(index);
+ if (item)
+ return item->flags();
+ return Qt::ItemIsSelectable
+ |Qt::ItemIsEnabled
+ |Qt::ItemIsEditable
+ |Qt::ItemIsDragEnabled
+ |Qt::ItemIsDropEnabled;
+}
+
+/*!
+ \reimp
+*/
+bool QStandardItemModel::hasChildren(const QModelIndex &parent) const
+{
+ Q_D(const QStandardItemModel);
+ QStandardItem *item = d->itemFromIndex(parent);
+ return item ? item->hasChildren() : false;
+}
+
+/*!
+ \reimp
+*/
+QVariant QStandardItemModel::headerData(int section, Qt::Orientation orientation, int role) const
+{
+ Q_D(const QStandardItemModel);
+ if ((section < 0)
+ || ((orientation == Qt::Horizontal) && (section >= columnCount()))
+ || ((orientation == Qt::Vertical) && (section >= rowCount()))) {
+ return QVariant();
+ }
+ QStandardItem *headerItem = 0;
+ if (orientation == Qt::Horizontal)
+ headerItem = d->columnHeaderItems.at(section);
+ else if (orientation == Qt::Vertical)
+ headerItem = d->rowHeaderItems.at(section);
+ return headerItem ? headerItem->data(role)
+ : QAbstractItemModel::headerData(section, orientation, role);
+}
+
+/*!
+ \reimp
+
+ QStandardItemModel supports both copy and move.
+*/
+Qt::DropActions QStandardItemModel::supportedDropActions () const
+{
+ return Qt::CopyAction | Qt::MoveAction;
+}
+
+/*!
+ \reimp
+*/
+QModelIndex QStandardItemModel::index(int row, int column, const QModelIndex &parent) const
+{
+ Q_D(const QStandardItemModel);
+ QStandardItem *parentItem = d->itemFromIndex(parent);
+ if ((parentItem == 0)
+ || (row < 0)
+ || (column < 0)
+ || (row >= parentItem->rowCount())
+ || (column >= parentItem->columnCount())) {
+ return QModelIndex();
+ }
+ return createIndex(row, column, parentItem);
+}
+
+/*!
+ \reimp
+*/
+bool QStandardItemModel::insertColumns(int column, int count, const QModelIndex &parent)
+{
+ Q_D(QStandardItemModel);
+ QStandardItem *item = parent.isValid() ? itemFromIndex(parent) : d->root;
+ if (item == 0)
+ return false;
+ return item->d_func()->insertColumns(column, count, QList<QStandardItem*>());
+}
+
+/*!
+ \reimp
+*/
+bool QStandardItemModel::insertRows(int row, int count, const QModelIndex &parent)
+{
+ Q_D(QStandardItemModel);
+ QStandardItem *item = parent.isValid() ? itemFromIndex(parent) : d->root;
+ if (item == 0)
+ return false;
+ return item->d_func()->insertRows(row, count, QList<QStandardItem*>());
+}
+
+/*!
+ \reimp
+*/
+QMap<int, QVariant> QStandardItemModel::itemData(const QModelIndex &index) const
+{
+ Q_D(const QStandardItemModel);
+ QStandardItem *item = d->itemFromIndex(index);
+ return item ? item->d_func()->itemData() : QMap<int, QVariant>();
+}
+
+/*!
+ \reimp
+*/
+QModelIndex QStandardItemModel::parent(const QModelIndex &child) const
+{
+ Q_D(const QStandardItemModel);
+ if (!d->indexValid(child))
+ return QModelIndex();
+ QStandardItem *parentItem = static_cast<QStandardItem*>(child.internalPointer());
+ return indexFromItem(parentItem);
+}
+
+/*!
+ \reimp
+*/
+bool QStandardItemModel::removeColumns(int column, int count, const QModelIndex &parent)
+{
+ Q_D(QStandardItemModel);
+ QStandardItem *item = d->itemFromIndex(parent);
+ if ((item == 0) || (count < 1) || (column < 0) || ((column + count) > item->columnCount()))
+ return false;
+ item->removeColumns(column, count);
+ return true;
+}
+
+/*!
+ \reimp
+*/
+bool QStandardItemModel::removeRows(int row, int count, const QModelIndex &parent)
+{
+ Q_D(QStandardItemModel);
+ QStandardItem *item = d->itemFromIndex(parent);
+ if ((item == 0) || (count < 1) || (row < 0) || ((row + count) > item->rowCount()))
+ return false;
+ item->removeRows(row, count);
+ return true;
+}
+
+/*!
+ \reimp
+*/
+int QStandardItemModel::rowCount(const QModelIndex &parent) const
+{
+ Q_D(const QStandardItemModel);
+ QStandardItem *item = d->itemFromIndex(parent);
+ return item ? item->rowCount() : 0;
+}
+
+/*!
+ \reimp
+*/
+bool QStandardItemModel::setData(const QModelIndex &index, const QVariant &value, int role)
+{
+ if (!index.isValid())
+ return false;
+ QStandardItem *item = itemFromIndex(index);
+ if (item == 0)
+ return false;
+ item->setData(value, role);
+ return true;
+}
+
+/*!
+ \reimp
+*/
+bool QStandardItemModel::setHeaderData(int section, Qt::Orientation orientation, const QVariant &value, int role)
+{
+ Q_D(QStandardItemModel);
+ if ((section < 0)
+ || ((orientation == Qt::Horizontal) && (section >= columnCount()))
+ || ((orientation == Qt::Vertical) && (section >= rowCount()))) {
+ return false;
+ }
+ QStandardItem *headerItem = 0;
+ if (orientation == Qt::Horizontal) {
+ headerItem = d->columnHeaderItems.at(section);
+ if (headerItem == 0) {
+ headerItem = d->createItem();
+ headerItem->d_func()->setModel(this);
+ d->columnHeaderItems.replace(section, headerItem);
+ }
+ } else if (orientation == Qt::Vertical) {
+ headerItem = d->rowHeaderItems.at(section);
+ if (headerItem == 0) {
+ headerItem = d->createItem();
+ headerItem->d_func()->setModel(this);
+ d->rowHeaderItems.replace(section, headerItem);
+ }
+ }
+ if (headerItem) {
+ headerItem->setData(value, role);
+ return true;
+ }
+ return false;
+}
+
+/*!
+ \reimp
+*/
+bool QStandardItemModel::setItemData(const QModelIndex &index, const QMap<int, QVariant> &roles)
+{
+ QStandardItem *item = itemFromIndex(index);
+ if (item == 0)
+ return false;
+ item->d_func()->setItemData(roles);
+ return true;
+}
+
+/*!
+ \reimp
+*/
+void QStandardItemModel::sort(int column, Qt::SortOrder order)
+{
+ Q_D(QStandardItemModel);
+ d->root->sortChildren(column, order);
+}
+
+/*!
+ \fn QObject *QStandardItemModel::parent() const
+ \internal
+*/
+
+
+/*!
+ \reimp
+*/
+QStringList QStandardItemModel::mimeTypes() const
+{
+ return QAbstractItemModel::mimeTypes() << QLatin1String("application/x-qstandarditemmodeldatalist");
+}
+
+/*!
+ \reimp
+*/
+QMimeData *QStandardItemModel::mimeData(const QModelIndexList &indexes) const
+{
+ QMimeData *data = QAbstractItemModel::mimeData(indexes);
+ if(!data)
+ return 0;
+
+ QString format = QLatin1String("application/x-qstandarditemmodeldatalist");
+ if (!mimeTypes().contains(format))
+ return data;
+ QByteArray encoded;
+ QDataStream stream(&encoded, QIODevice::WriteOnly);
+
+ QSet<QStandardItem*> itemsSet;
+ QStack<QStandardItem*> stack;
+ itemsSet.reserve(indexes.count());
+ stack.reserve(indexes.count());
+ foreach (const QModelIndex &index, indexes) {
+ QStandardItem *item = itemFromIndex(index);
+ itemsSet << item;
+ stack.push(item);
+ }
+
+ //remove duplicates childrens
+ {
+ QSet<QStandardItem *> seen;
+ while (!stack.isEmpty()) {
+ QStandardItem *itm = stack.pop();
+ if (seen.contains(itm))
+ continue;
+ seen.insert(itm);
+
+ const QVector<QStandardItem*> &childList = itm->d_func()->children;
+ for (int i = 0; i < childList.count(); ++i) {
+ QStandardItem *chi = childList.at(i);
+ if (chi) {
+ QSet<QStandardItem *>::iterator it = itemsSet.find(chi);
+ if (it != itemsSet.end()) {
+ itemsSet.erase(it);
+ }
+ stack.push(chi);
+ }
+ }
+ }
+ }
+
+ stack.reserve(itemsSet.count());
+ foreach (QStandardItem *item, itemsSet) {
+ stack.push(item);
+ }
+
+ //stream everything recursively
+ while (!stack.isEmpty()) {
+ QStandardItem *item = stack.pop();
+ if(itemsSet.contains(item)) { //if the item is selection 'top-level', strem its position
+ stream << item->row() << item->column();
+ }
+ if(item) {
+ stream << *item << item->columnCount() << item->d_ptr->children.count();
+ stack += item->d_ptr->children;
+ } else {
+ QStandardItem dummy;
+ stream << dummy << 0 << 0;
+ }
+ }
+
+ data->setData(format, encoded);
+ return data;
+}
+
+
+/* \internal
+ Used by QStandardItemModel::dropMimeData
+ stream out an item and his children
+ */
+static void decodeDataRecursive(QDataStream &stream, QStandardItem *item)
+{
+ int colCount, childCount;
+ stream >> *item;
+ stream >> colCount >> childCount;
+ item->setColumnCount(colCount);
+
+ int childPos = childCount;
+
+ while(childPos > 0) {
+ childPos--;
+ QStandardItem *child = new QStandardItem;
+ decodeDataRecursive(stream, child);
+ item->setChild( childPos / colCount, childPos % colCount, child);
+ }
+}
+
+
+/*!
+ \reimp
+*/
+bool QStandardItemModel::dropMimeData(const QMimeData *data, Qt::DropAction action,
+ int row, int column, const QModelIndex &parent)
+{
+ // check if the action is supported
+ if (!data || !(action == Qt::CopyAction || action == Qt::MoveAction))
+ return false;
+ // check if the format is supported
+ QString format = QLatin1String("application/x-qstandarditemmodeldatalist");
+ if (!data->hasFormat(format))
+ return QAbstractItemModel::dropMimeData(data, action, row, column, parent);
+
+ if (row > rowCount(parent))
+ row = rowCount(parent);
+ if (row == -1)
+ row = rowCount(parent);
+ if (column == -1)
+ column = 0;
+
+ // decode and insert
+ QByteArray encoded = data->data(format);
+ QDataStream stream(&encoded, QIODevice::ReadOnly);
+
+
+ //code based on QAbstractItemModel::decodeData
+ // adapted to work with QStandardItem
+ int top = INT_MAX;
+ int left = INT_MAX;
+ int bottom = 0;
+ int right = 0;
+ QVector<int> rows, columns;
+ QVector<QStandardItem *> items;
+
+ while (!stream.atEnd()) {
+ int r, c;
+ QStandardItem *item = new QStandardItem;
+ stream >> r >> c;
+ decodeDataRecursive(stream, item);
+
+ rows.append(r);
+ columns.append(c);
+ items.append(item);
+ top = qMin(r, top);
+ left = qMin(c, left);
+ bottom = qMax(r, bottom);
+ right = qMax(c, right);
+ }
+
+ // insert the dragged items into the table, use a bit array to avoid overwriting items,
+ // since items from different tables can have the same row and column
+ int dragRowCount = 0;
+ int dragColumnCount = right - left + 1;
+
+ // Compute the number of continuous rows upon insertion and modify the rows to match
+ QVector<int> rowsToInsert(bottom + 1);
+ for (int i = 0; i < rows.count(); ++i)
+ rowsToInsert[rows.at(i)] = 1;
+ for (int i = 0; i < rowsToInsert.count(); ++i) {
+ if (rowsToInsert[i] == 1){
+ rowsToInsert[i] = dragRowCount;
+ ++dragRowCount;
+ }
+ }
+ for (int i = 0; i < rows.count(); ++i)
+ rows[i] = top + rowsToInsert[rows[i]];
+
+ QBitArray isWrittenTo(dragRowCount * dragColumnCount);
+
+ // make space in the table for the dropped data
+ int colCount = columnCount(parent);
+ if (colCount < dragColumnCount + column) {
+ insertColumns(colCount, dragColumnCount + column - colCount, parent);
+ colCount = columnCount(parent);
+ }
+ insertRows(row, dragRowCount, parent);
+
+ row = qMax(0, row);
+ column = qMax(0, column);
+
+ QStandardItem *parentItem = itemFromIndex (parent);
+ if (!parentItem)
+ parentItem = invisibleRootItem();
+
+ QVector<QPersistentModelIndex> newIndexes(items.size());
+ // set the data in the table
+ for (int j = 0; j < items.size(); ++j) {
+ int relativeRow = rows.at(j) - top;
+ int relativeColumn = columns.at(j) - left;
+ int destinationRow = relativeRow + row;
+ int destinationColumn = relativeColumn + column;
+ int flat = (relativeRow * dragColumnCount) + relativeColumn;
+ // if the item was already written to, or we just can't fit it in the table, create a new row
+ if (destinationColumn >= colCount || isWrittenTo.testBit(flat)) {
+ destinationColumn = qBound(column, destinationColumn, colCount - 1);
+ destinationRow = row + dragRowCount;
+ insertRows(row + dragRowCount, 1, parent);
+ flat = (dragRowCount * dragColumnCount) + relativeColumn;
+ isWrittenTo.resize(++dragRowCount * dragColumnCount);
+ }
+ if (!isWrittenTo.testBit(flat)) {
+ newIndexes[j] = index(destinationRow, destinationColumn, parentItem->index());
+ isWrittenTo.setBit(flat);
+ }
+ }
+
+ for(int k = 0; k < newIndexes.size(); k++) {
+ if (newIndexes.at(k).isValid()) {
+ parentItem->setChild(newIndexes.at(k).row(), newIndexes.at(k).column(), items.at(k));
+ } else {
+ delete items.at(k);
+ }
+ }
+
+ return true;
+}
+
+QT_END_NAMESPACE
+
+#include "moc_qstandarditemmodel.cpp"
+
+#endif // QT_NO_STANDARDITEMMODEL
diff --git a/src/gui/itemviews/qstandarditemmodel.h b/src/gui/itemviews/qstandarditemmodel.h
new file mode 100644
index 0000000000..f24f0ab349
--- /dev/null
+++ b/src/gui/itemviews/qstandarditemmodel.h
@@ -0,0 +1,456 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSTANDARDITEMMODEL_H
+#define QSTANDARDITEMMODEL_H
+
+#include <QtCore/qabstractitemmodel.h>
+#include <QtGui/qbrush.h>
+#include <QtGui/qfont.h>
+#include <QtGui/qicon.h>
+#ifndef QT_NO_DATASTREAM
+#include <QtCore/qdatastream.h>
+#endif
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+#ifndef QT_NO_STANDARDITEMMODEL
+
+template <class T> class QList;
+
+class QStandardItemModel;
+
+class QStandardItemPrivate;
+class Q_GUI_EXPORT QStandardItem
+{
+public:
+ QStandardItem();
+ QStandardItem(const QString &text);
+ QStandardItem(const QIcon &icon, const QString &text);
+ explicit QStandardItem(int rows, int columns = 1);
+ virtual ~QStandardItem();
+
+ virtual QVariant data(int role = Qt::UserRole + 1) const;
+ virtual void setData(const QVariant &value, int role = Qt::UserRole + 1);
+
+ inline QString text() const {
+ return qvariant_cast<QString>(data(Qt::DisplayRole));
+ }
+ inline void setText(const QString &text);
+
+ inline QIcon icon() const {
+ return qvariant_cast<QIcon>(data(Qt::DecorationRole));
+ }
+ inline void setIcon(const QIcon &icon);
+
+#ifndef QT_NO_TOOLTIP
+ inline QString toolTip() const {
+ return qvariant_cast<QString>(data(Qt::ToolTipRole));
+ }
+ inline void setToolTip(const QString &toolTip);
+#endif
+
+#ifndef QT_NO_STATUSTIP
+ inline QString statusTip() const {
+ return qvariant_cast<QString>(data(Qt::StatusTipRole));
+ }
+ inline void setStatusTip(const QString &statusTip);
+#endif
+
+#ifndef QT_NO_WHATSTHIS
+ inline QString whatsThis() const {
+ return qvariant_cast<QString>(data(Qt::WhatsThisRole));
+ }
+ inline void setWhatsThis(const QString &whatsThis);
+#endif
+
+ inline QSize sizeHint() const {
+ return qvariant_cast<QSize>(data(Qt::SizeHintRole));
+ }
+ inline void setSizeHint(const QSize &sizeHint);
+
+ inline QFont font() const {
+ return qvariant_cast<QFont>(data(Qt::FontRole));
+ }
+ inline void setFont(const QFont &font);
+
+ inline Qt::Alignment textAlignment() const {
+ return Qt::Alignment(qvariant_cast<int>(data(Qt::TextAlignmentRole)));
+ }
+ inline void setTextAlignment(Qt::Alignment textAlignment);
+
+ inline QBrush background() const {
+ return qvariant_cast<QBrush>(data(Qt::BackgroundRole));
+ }
+ inline void setBackground(const QBrush &brush);
+
+ inline QBrush foreground() const {
+ return qvariant_cast<QBrush>(data(Qt::ForegroundRole));
+ }
+ inline void setForeground(const QBrush &brush);
+
+ inline Qt::CheckState checkState() const {
+ return Qt::CheckState(qvariant_cast<int>(data(Qt::CheckStateRole)));
+ }
+ inline void setCheckState(Qt::CheckState checkState);
+
+ inline QString accessibleText() const {
+ return qvariant_cast<QString>(data(Qt::AccessibleTextRole));
+ }
+ inline void setAccessibleText(const QString &accessibleText);
+
+ inline QString accessibleDescription() const {
+ return qvariant_cast<QString>(data(Qt::AccessibleDescriptionRole));
+ }
+ inline void setAccessibleDescription(const QString &accessibleDescription);
+
+ Qt::ItemFlags flags() const;
+ void setFlags(Qt::ItemFlags flags);
+
+ inline bool isEnabled() const {
+ return (flags() & Qt::ItemIsEnabled) != 0;
+ }
+ void setEnabled(bool enabled);
+
+ inline bool isEditable() const {
+ return (flags() & Qt::ItemIsEditable) != 0;
+ }
+ void setEditable(bool editable);
+
+ inline bool isSelectable() const {
+ return (flags() & Qt::ItemIsSelectable) != 0;
+ }
+ void setSelectable(bool selectable);
+
+ inline bool isCheckable() const {
+ return (flags() & Qt::ItemIsUserCheckable) != 0;
+ }
+ void setCheckable(bool checkable);
+
+ inline bool isTristate() const {
+ return (flags() & Qt::ItemIsTristate) != 0;
+ }
+ void setTristate(bool tristate);
+
+#ifndef QT_NO_DRAGANDDROP
+ inline bool isDragEnabled() const {
+ return (flags() & Qt::ItemIsDragEnabled) != 0;
+ }
+ void setDragEnabled(bool dragEnabled);
+
+ inline bool isDropEnabled() const {
+ return (flags() & Qt::ItemIsDropEnabled) != 0;
+ }
+ void setDropEnabled(bool dropEnabled);
+#endif // QT_NO_DRAGANDDROP
+
+ QStandardItem *parent() const;
+ int row() const;
+ int column() const;
+ QModelIndex index() const;
+ QStandardItemModel *model() const;
+
+ int rowCount() const;
+ void setRowCount(int rows);
+ int columnCount() const;
+ void setColumnCount(int columns);
+
+ bool hasChildren() const;
+ QStandardItem *child(int row, int column = 0) const;
+ void setChild(int row, int column, QStandardItem *item);
+ inline void setChild(int row, QStandardItem *item);
+
+ void insertRow(int row, const QList<QStandardItem*> &items);
+ void insertColumn(int column, const QList<QStandardItem*> &items);
+ void insertRows(int row, const QList<QStandardItem*> &items);
+ void insertRows(int row, int count);
+ void insertColumns(int column, int count);
+
+ void removeRow(int row);
+ void removeColumn(int column);
+ void removeRows(int row, int count);
+ void removeColumns(int column, int count);
+
+ inline void appendRow(const QList<QStandardItem*> &items);
+ inline void appendRows(const QList<QStandardItem*> &items);
+ inline void appendColumn(const QList<QStandardItem*> &items);
+ inline void insertRow(int row, QStandardItem *item);
+ inline void appendRow(QStandardItem *item);
+
+ QStandardItem *takeChild(int row, int column = 0);
+ QList<QStandardItem*> takeRow(int row);
+ QList<QStandardItem*> takeColumn(int column);
+
+ void sortChildren(int column, Qt::SortOrder order = Qt::AscendingOrder);
+
+ virtual QStandardItem *clone() const;
+
+ enum ItemType { Type = 0, UserType = 1000 };
+ virtual int type() const;
+
+#ifndef QT_NO_DATASTREAM
+ virtual void read(QDataStream &in);
+ virtual void write(QDataStream &out) const;
+#endif
+ virtual bool operator<(const QStandardItem &other) const;
+
+protected:
+ QStandardItem(const QStandardItem &other);
+ QStandardItem(QStandardItemPrivate &dd);
+ QStandardItem &operator=(const QStandardItem &other);
+ QStandardItemPrivate *d_ptr;
+
+ void emitDataChanged();
+
+private:
+ Q_DECLARE_PRIVATE(QStandardItem)
+ friend class QStandardItemModelPrivate;
+ friend class QStandardItemModel;
+};
+
+inline void QStandardItem::setText(const QString &atext)
+{ setData(atext, Qt::DisplayRole); }
+
+inline void QStandardItem::setIcon(const QIcon &aicon)
+{ setData(aicon, Qt::DecorationRole); }
+
+#ifndef QT_NO_TOOLTIP
+inline void QStandardItem::setToolTip(const QString &atoolTip)
+{ setData(atoolTip, Qt::ToolTipRole); }
+#endif
+
+#ifndef QT_NO_STATUSTIP
+inline void QStandardItem::setStatusTip(const QString &astatusTip)
+{ setData(astatusTip, Qt::StatusTipRole); }
+#endif
+
+#ifndef QT_NO_WHATSTHIS
+inline void QStandardItem::setWhatsThis(const QString &awhatsThis)
+{ setData(awhatsThis, Qt::WhatsThisRole); }
+#endif
+
+inline void QStandardItem::setSizeHint(const QSize &asizeHint)
+{ setData(asizeHint, Qt::SizeHintRole); }
+
+inline void QStandardItem::setFont(const QFont &afont)
+{ setData(afont, Qt::FontRole); }
+
+inline void QStandardItem::setTextAlignment(Qt::Alignment atextAlignment)
+{ setData(int(atextAlignment), Qt::TextAlignmentRole); }
+
+inline void QStandardItem::setBackground(const QBrush &abrush)
+{ setData(abrush, Qt::BackgroundRole); }
+
+inline void QStandardItem::setForeground(const QBrush &abrush)
+{ setData(abrush, Qt::ForegroundRole); }
+
+inline void QStandardItem::setCheckState(Qt::CheckState acheckState)
+{ setData(acheckState, Qt::CheckStateRole); }
+
+inline void QStandardItem::setAccessibleText(const QString &aaccessibleText)
+{ setData(aaccessibleText, Qt::AccessibleTextRole); }
+
+inline void QStandardItem::setAccessibleDescription(const QString &aaccessibleDescription)
+{ setData(aaccessibleDescription, Qt::AccessibleDescriptionRole); }
+
+inline void QStandardItem::setChild(int arow, QStandardItem *aitem)
+{ setChild(arow, 0, aitem); }
+
+inline void QStandardItem::appendRow(const QList<QStandardItem*> &aitems)
+{ insertRow(rowCount(), aitems); }
+
+inline void QStandardItem::appendRows(const QList<QStandardItem*> &aitems)
+{ insertRows(rowCount(), aitems); }
+
+inline void QStandardItem::appendColumn(const QList<QStandardItem*> &aitems)
+{ insertColumn(columnCount(), aitems); }
+
+inline void QStandardItem::insertRow(int arow, QStandardItem *aitem)
+{ insertRow(arow, QList<QStandardItem*>() << aitem); }
+
+inline void QStandardItem::appendRow(QStandardItem *aitem)
+{ insertRow(rowCount(), aitem); }
+
+class QStandardItemModelPrivate;
+
+class Q_GUI_EXPORT QStandardItemModel : public QAbstractItemModel
+{
+ Q_OBJECT
+ Q_PROPERTY(int sortRole READ sortRole WRITE setSortRole)
+
+public:
+ explicit QStandardItemModel(QObject *parent = 0);
+ QStandardItemModel(int rows, int columns, QObject *parent = 0);
+ ~QStandardItemModel();
+
+ QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const;
+ QModelIndex parent(const QModelIndex &child) const;
+
+ int rowCount(const QModelIndex &parent = QModelIndex()) const;
+ int columnCount(const QModelIndex &parent = QModelIndex()) const;
+ bool hasChildren(const QModelIndex &parent = QModelIndex()) const;
+
+ QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
+ bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole);
+
+ QVariant headerData(int section, Qt::Orientation orientation,
+ int role = Qt::DisplayRole) const;
+ bool setHeaderData(int section, Qt::Orientation orientation, const QVariant &value,
+ int role = Qt::EditRole);
+
+ bool insertRows(int row, int count, const QModelIndex &parent = QModelIndex());
+ bool insertColumns(int column, int count, const QModelIndex &parent = QModelIndex());
+ bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex());
+ bool removeColumns(int column, int count, const QModelIndex &parent = QModelIndex());
+
+ Qt::ItemFlags flags(const QModelIndex &index) const;
+ Qt::DropActions supportedDropActions() const;
+
+ QMap<int, QVariant> itemData(const QModelIndex &index) const;
+ bool setItemData(const QModelIndex &index, const QMap<int, QVariant> &roles);
+
+ void clear();
+
+#ifdef Q_NO_USING_KEYWORD
+ inline QObject *parent() const { return QObject::parent(); }
+#else
+ using QObject::parent;
+#endif
+
+ void sort(int column, Qt::SortOrder order = Qt::AscendingOrder);
+
+ QStandardItem *itemFromIndex(const QModelIndex &index) const;
+ QModelIndex indexFromItem(const QStandardItem *item) const;
+
+ QStandardItem *item(int row, int column = 0) const;
+ void setItem(int row, int column, QStandardItem *item);
+ inline void setItem(int row, QStandardItem *item);
+ QStandardItem *invisibleRootItem() const;
+
+ QStandardItem *horizontalHeaderItem(int column) const;
+ void setHorizontalHeaderItem(int column, QStandardItem *item);
+ QStandardItem *verticalHeaderItem(int row) const;
+ void setVerticalHeaderItem(int row, QStandardItem *item);
+
+ void setHorizontalHeaderLabels(const QStringList &labels);
+ void setVerticalHeaderLabels(const QStringList &labels);
+
+ void setRowCount(int rows);
+ void setColumnCount(int columns);
+
+ void appendRow(const QList<QStandardItem*> &items);
+ void appendColumn(const QList<QStandardItem*> &items);
+ inline void appendRow(QStandardItem *item);
+
+ void insertRow(int row, const QList<QStandardItem*> &items);
+ void insertColumn(int column, const QList<QStandardItem*> &items);
+ inline void insertRow(int row, QStandardItem *item);
+
+ inline bool insertRow(int row, const QModelIndex &parent = QModelIndex());
+ inline bool insertColumn(int column, const QModelIndex &parent = QModelIndex());
+
+ QStandardItem *takeItem(int row, int column = 0);
+ QList<QStandardItem*> takeRow(int row);
+ QList<QStandardItem*> takeColumn(int column);
+
+ QStandardItem *takeHorizontalHeaderItem(int column);
+ QStandardItem *takeVerticalHeaderItem(int row);
+
+ const QStandardItem *itemPrototype() const;
+ void setItemPrototype(const QStandardItem *item);
+
+ QList<QStandardItem*> findItems(const QString &text,
+ Qt::MatchFlags flags = Qt::MatchExactly,
+ int column = 0) const;
+
+ int sortRole() const;
+ void setSortRole(int role);
+
+ QStringList mimeTypes() const;
+ QMimeData *mimeData(const QModelIndexList &indexes) const;
+ bool dropMimeData (const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent);
+
+Q_SIGNALS:
+ void itemChanged(QStandardItem *item);
+
+protected:
+ QStandardItemModel(QStandardItemModelPrivate &dd, QObject *parent = 0);
+
+private:
+ friend class QStandardItemPrivate;
+ friend class QStandardItem;
+ Q_DISABLE_COPY(QStandardItemModel)
+ Q_DECLARE_PRIVATE(QStandardItemModel)
+
+ Q_PRIVATE_SLOT(d_func(), void _q_emitItemChanged(const QModelIndex &topLeft,
+ const QModelIndex &bottomRight))
+};
+
+inline void QStandardItemModel::setItem(int arow, QStandardItem *aitem)
+{ setItem(arow, 0, aitem); }
+
+inline void QStandardItemModel::appendRow(QStandardItem *aitem)
+{ appendRow(QList<QStandardItem*>() << aitem); }
+
+inline void QStandardItemModel::insertRow(int arow, QStandardItem *aitem)
+{ insertRow(arow, QList<QStandardItem*>() << aitem); }
+
+inline bool QStandardItemModel::insertRow(int arow, const QModelIndex &aparent)
+{ return QAbstractItemModel::insertRow(arow, aparent); }
+inline bool QStandardItemModel::insertColumn(int acolumn, const QModelIndex &aparent)
+{ return QAbstractItemModel::insertColumn(acolumn, aparent); }
+
+#ifndef QT_NO_DATASTREAM
+Q_GUI_EXPORT QDataStream &operator>>(QDataStream &in, QStandardItem &item);
+Q_GUI_EXPORT QDataStream &operator<<(QDataStream &out, const QStandardItem &item);
+#endif
+
+#endif // QT_NO_STANDARDITEMMODEL
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif //QSTANDARDITEMMODEL_H
diff --git a/src/gui/itemviews/qstandarditemmodel_p.h b/src/gui/itemviews/qstandarditemmodel_p.h
new file mode 100644
index 0000000000..721194f596
--- /dev/null
+++ b/src/gui/itemviews/qstandarditemmodel_p.h
@@ -0,0 +1,189 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSTANDARDITEMMODEL_P_H
+#define QSTANDARDITEMMODEL_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 "private/qabstractitemmodel_p.h"
+
+#ifndef QT_NO_STANDARDITEMMODEL
+
+#include <private/qwidgetitemdata_p.h>
+#include <QtCore/qlist.h>
+#include <QtCore/qpair.h>
+#include <QtCore/qstack.h>
+#include <QtCore/qvariant.h>
+#include <QtCore/qvector.h>
+
+QT_BEGIN_NAMESPACE
+
+class QStandardItemPrivate
+{
+ Q_DECLARE_PUBLIC(QStandardItem)
+public:
+ inline QStandardItemPrivate()
+ : model(0),
+ parent(0),
+ rows(0),
+ columns(0),
+ lastIndexOf(2)
+ { }
+ virtual ~QStandardItemPrivate();
+
+ inline int childIndex(int row, int column) const {
+ if ((row < 0) || (column < 0)
+ || (row >= rowCount()) || (column >= columnCount())) {
+ return -1;
+ }
+ return (row * columnCount()) + column;
+ }
+ inline int childIndex(const QStandardItem *child) {
+ int start = qMax(0, lastIndexOf -2);
+ lastIndexOf = children.indexOf(const_cast<QStandardItem*>(child), start);
+ if (lastIndexOf == -1 && start != 0)
+ lastIndexOf = children.lastIndexOf(const_cast<QStandardItem*>(child), start);
+ return lastIndexOf;
+ }
+ QPair<int, int> position() const;
+ void setChild(int row, int column, QStandardItem *item,
+ bool emitChanged = false);
+ inline int rowCount() const {
+ return rows;
+ }
+ inline int columnCount() const {
+ return columns;
+ }
+ void childDeleted(QStandardItem *child);
+
+ void setModel(QStandardItemModel *mod);
+
+ inline void setParentAndModel(
+ QStandardItem *par,
+ QStandardItemModel *mod) {
+ setModel(mod);
+ parent = par;
+ }
+
+ void changeFlags(bool enable, Qt::ItemFlags f);
+ void setItemData(const QMap<int, QVariant> &roles);
+ const QMap<int, QVariant> itemData() const;
+
+ bool insertRows(int row, int count, const QList<QStandardItem*> &items);
+ bool insertRows(int row, const QList<QStandardItem*> &items);
+ bool insertColumns(int column, int count, const QList<QStandardItem*> &items);
+
+ void sortChildren(int column, Qt::SortOrder order);
+
+ QStandardItemModel *model;
+ QStandardItem *parent;
+ QVector<QWidgetItemData> values;
+ QVector<QStandardItem*> children;
+ int rows;
+ int columns;
+
+ QStandardItem *q_ptr;
+
+ int lastIndexOf;
+};
+
+class QStandardItemModelPrivate : public QAbstractItemModelPrivate
+{
+ Q_DECLARE_PUBLIC(QStandardItemModel)
+
+public:
+ QStandardItemModelPrivate();
+ virtual ~QStandardItemModelPrivate();
+
+ void init();
+
+ inline QStandardItem *createItem() const {
+ return itemPrototype ? itemPrototype->clone() : new QStandardItem;
+ }
+
+ inline QStandardItem *itemFromIndex(const QModelIndex &index) const {
+ Q_Q(const QStandardItemModel);
+ if (!index.isValid())
+ return root;
+ if (index.model() != q)
+ return 0;
+ QStandardItem *parent = static_cast<QStandardItem*>(index.internalPointer());
+ if (parent == 0)
+ return 0;
+ return parent->child(index.row(), index.column());
+ }
+
+ void sort(QStandardItem *parent, int column, Qt::SortOrder order);
+ void itemChanged(QStandardItem *item);
+ void rowsAboutToBeInserted(QStandardItem *parent, int start, int end);
+ void columnsAboutToBeInserted(QStandardItem *parent, int start, int end);
+ void rowsAboutToBeRemoved(QStandardItem *parent, int start, int end);
+ void columnsAboutToBeRemoved(QStandardItem *parent, int start, int end);
+ void rowsInserted(QStandardItem *parent, int row, int count);
+ void columnsInserted(QStandardItem *parent, int column, int count);
+ void rowsRemoved(QStandardItem *parent, int row, int count);
+ void columnsRemoved(QStandardItem *parent, int column, int count);
+
+ void _q_emitItemChanged(const QModelIndex &topLeft,
+ const QModelIndex &bottomRight);
+
+ QVector<QStandardItem*> columnHeaderItems;
+ QVector<QStandardItem*> rowHeaderItems;
+ QStandardItem *root;
+ const QStandardItem *itemPrototype;
+ int sortRole;
+};
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_STANDARDITEMMODEL
+
+#endif // QSTANDARDITEMMODEL_P_H
diff --git a/src/gui/itemviews/qstringlistmodel.cpp b/src/gui/itemviews/qstringlistmodel.cpp
new file mode 100644
index 0000000000..6d20907965
--- /dev/null
+++ b/src/gui/itemviews/qstringlistmodel.cpp
@@ -0,0 +1,307 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*
+ A simple model that uses a QStringList as its data source.
+*/
+
+#include "qstringlistmodel.h"
+
+#ifndef QT_NO_STRINGLISTMODEL
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QStringListModel
+ \brief The QStringListModel class provides a model that supplies strings to views.
+
+ \ingroup model-view
+ \mainclass
+
+ QStringListModel is an editable model that can be used for simple
+ cases where you need to display a number of strings in a view
+ widget, such as a QListView or a QComboBox.
+
+ The model provides all the standard functions of an editable
+ model, representing the data in the string list as a model with
+ one column and a number of rows equal to the number of items in
+ the list.
+
+ Model indexes corresponding to items are obtained with the
+ \l{QAbstractListModel::index()}{index()} function, and item flags
+ are obtained with flags(). Item data is read with the data()
+ function and written with setData(). The number of rows (and
+ number of items in the string list) can be found with the
+ rowCount() function.
+
+ The model can be constructed with an existing string list, or
+ strings can be set later with the setStringList() convenience
+ function. Strings can also be inserted in the usual way with the
+ insertRows() function, and removed with removeRows(). The contents
+ of the string list can be retrieved with the stringList()
+ convenience function.
+
+ An example usage of QStringListModel:
+
+ \snippet doc/src/snippets/qstringlistmodel/main.cpp 0
+
+ \sa QAbstractListModel, QAbstractItemModel, {Model Classes}
+*/
+
+/*!
+ Constructs a string list model with the given \a parent.
+*/
+
+QStringListModel::QStringListModel(QObject *parent)
+ : QAbstractListModel(parent)
+{
+}
+
+/*!
+ Constructs a string list model containing the specified \a strings
+ with the given \a parent.
+*/
+
+QStringListModel::QStringListModel(const QStringList &strings, QObject *parent)
+ : QAbstractListModel(parent), lst(strings)
+{
+}
+
+/*!
+ Returns the number of rows in the model. This value corresponds to the
+ number of items in the model's internal string list.
+
+ The optional \a parent argument is in most models used to specify
+ the parent of the rows to be counted. Because this is a list if a
+ valid parent is specified, the result will always be 0.
+
+ \sa insertRows(), removeRows(), QAbstractItemModel::rowCount()
+*/
+
+int QStringListModel::rowCount(const QModelIndex &parent) const
+{
+ if (parent.isValid())
+ return 0;
+
+ return lst.count();
+}
+
+/*!
+ Returns data for the specified \a role, from the item with the
+ given \a index.
+
+ If the view requests an invalid index, an invalid variant is returned.
+
+ \sa setData()
+*/
+
+QVariant QStringListModel::data(const QModelIndex &index, int role) const
+{
+ if (index.row() < 0 || index.row() >= lst.size())
+ return QVariant();
+
+ if (role == Qt::DisplayRole || role == Qt::EditRole)
+ return lst.at(index.row());
+
+ return QVariant();
+}
+
+/*!
+ Returns the flags for the item with the given \a index.
+
+ Valid items are enabled, selectable, editable, drag enabled and drop enabled.
+
+ \sa QAbstractItemModel::flags()
+*/
+
+Qt::ItemFlags QStringListModel::flags(const QModelIndex &index) const
+{
+ if (!index.isValid())
+ return QAbstractItemModel::flags(index) | Qt::ItemIsDropEnabled;
+
+ return QAbstractItemModel::flags(index) | Qt::ItemIsEditable | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled;
+}
+
+/*!
+ Sets the data for the specified \a role in the item with the given
+ \a index in the model, to the provided \a value.
+
+ The dataChanged() signal is emitted if the item is changed.
+
+ \sa Qt::ItemDataRole, data()
+*/
+
+bool QStringListModel::setData(const QModelIndex &index, const QVariant &value, int role)
+{
+ if (index.row() >= 0 && index.row() < lst.size()
+ && (role == Qt::EditRole || role == Qt::DisplayRole)) {
+ lst.replace(index.row(), value.toString());
+ emit dataChanged(index, index);
+ return true;
+ }
+ return false;
+}
+
+/*!
+ Inserts \a count rows into the model, beginning at the given \a row.
+
+ The \a parent index of the rows is optional and is only used for
+ consistency with QAbstractItemModel. By default, a null index is
+ specified, indicating that the rows are inserted in the top level of
+ the model.
+
+ \sa QAbstractItemModel::insertRows()
+*/
+
+bool QStringListModel::insertRows(int row, int count, const QModelIndex &parent)
+{
+ if (count < 1 || row < 0 || row > rowCount(parent))
+ return false;
+
+ beginInsertRows(QModelIndex(), row, row + count - 1);
+
+ for (int r = 0; r < count; ++r)
+ lst.insert(row, QString());
+
+ endInsertRows();
+
+ return true;
+}
+
+/*!
+ Removes \a count rows from the model, beginning at the given \a row.
+
+ The \a parent index of the rows is optional and is only used for
+ consistency with QAbstractItemModel. By default, a null index is
+ specified, indicating that the rows are removed in the top level of
+ the model.
+
+ \sa QAbstractItemModel::removeRows()
+*/
+
+bool QStringListModel::removeRows(int row, int count, const QModelIndex &parent)
+{
+ if (count <= 0 || row < 0 || (row + count) > rowCount(parent))
+ return false;
+
+ beginRemoveRows(QModelIndex(), row, row + count - 1);
+
+ for (int r = 0; r < count; ++r)
+ lst.removeAt(row);
+
+ endRemoveRows();
+
+ return true;
+}
+
+static bool ascendingLessThan(const QPair<QString, int> &s1, const QPair<QString, int> &s2)
+{
+ return s1.first < s2.first;
+}
+
+static bool decendingLessThan(const QPair<QString, int> &s1, const QPair<QString, int> &s2)
+{
+ return s1.first > s2.first;
+}
+
+/*!
+ \reimp
+*/
+void QStringListModel::sort(int, Qt::SortOrder order)
+{
+ emit layoutAboutToBeChanged();
+
+ QList<QPair<QString, int> > list;
+ for (int i = 0; i < lst.count(); ++i)
+ list.append(QPair<QString, int>(lst.at(i), i));
+
+ if (order == Qt::AscendingOrder)
+ qSort(list.begin(), list.end(), ascendingLessThan);
+ else
+ qSort(list.begin(), list.end(), decendingLessThan);
+
+ lst.clear();
+ QVector<int> forwarding(list.count());
+ for (int i = 0; i < list.count(); ++i) {
+ lst.append(list.at(i).first);
+ forwarding[list.at(i).second] = i;
+ }
+
+ QModelIndexList oldList = persistentIndexList();
+ QModelIndexList newList;
+ for (int i = 0; i < oldList.count(); ++i)
+ newList.append(index(forwarding.at(oldList.at(i).row()), 0));
+ changePersistentIndexList(oldList, newList);
+
+ emit layoutChanged();
+}
+
+/*!
+ Returns the string list used by the model to store data.
+*/
+QStringList QStringListModel::stringList() const
+{
+ return lst;
+}
+
+/*!
+ Sets the model's internal string list to \a strings. The model will
+ notify any attached views that its underlying data has changed.
+
+ \sa dataChanged()
+*/
+void QStringListModel::setStringList(const QStringList &strings)
+{
+ lst = strings;
+ reset();
+}
+
+/*!
+ \reimp
+*/
+Qt::DropActions QStringListModel::supportedDropActions() const
+{
+ return QAbstractItemModel::supportedDropActions() | Qt::MoveAction;
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_STRINGLISTMODEL
diff --git a/src/gui/itemviews/qstringlistmodel.h b/src/gui/itemviews/qstringlistmodel.h
new file mode 100644
index 0000000000..d2d0f70f48
--- /dev/null
+++ b/src/gui/itemviews/qstringlistmodel.h
@@ -0,0 +1,91 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSTRINGLISTMODEL_H
+#define QSTRINGLISTMODEL_H
+
+#include <QtCore/qstringlist.h>
+#include <QtGui/qabstractitemview.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+#ifndef QT_NO_STRINGLISTMODEL
+
+class Q_GUI_EXPORT QStringListModel : public QAbstractListModel
+{
+ Q_OBJECT
+public:
+ explicit QStringListModel(QObject *parent = 0);
+ QStringListModel(const QStringList &strings, QObject *parent = 0);
+
+ int rowCount(const QModelIndex &parent = QModelIndex()) const;
+
+ QVariant data(const QModelIndex &index, int role) const;
+ bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole);
+
+ Qt::ItemFlags flags(const QModelIndex &index) const;
+
+ bool insertRows(int row, int count, const QModelIndex &parent = QModelIndex());
+ bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex());
+
+ void sort(int column, Qt::SortOrder order = Qt::AscendingOrder);
+
+ QStringList stringList() const;
+ void setStringList(const QStringList &strings);
+
+ Qt::DropActions supportedDropActions() const;
+
+private:
+ Q_DISABLE_COPY(QStringListModel)
+ QStringList lst;
+};
+
+#endif // QT_NO_STRINGLISTMODEL
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QSTRINGLISTMODEL_H
diff --git a/src/gui/itemviews/qstyleditemdelegate.cpp b/src/gui/itemviews/qstyleditemdelegate.cpp
new file mode 100644
index 0000000000..be0971b016
--- /dev/null
+++ b/src/gui/itemviews/qstyleditemdelegate.cpp
@@ -0,0 +1,763 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qstyleditemdelegate.h"
+
+#ifndef QT_NO_ITEMVIEWS
+#include <qabstractitemmodel.h>
+#include <qapplication.h>
+#include <qbrush.h>
+#include <qlineedit.h>
+#include <qtextedit.h>
+#include <qpainter.h>
+#include <qpalette.h>
+#include <qpoint.h>
+#include <qrect.h>
+#include <qsize.h>
+#include <qstyle.h>
+#include <qdatetime.h>
+#include <qstyleoption.h>
+#include <qevent.h>
+#include <qpixmap.h>
+#include <qbitmap.h>
+#include <qpixmapcache.h>
+#include <qitemeditorfactory.h>
+#include <private/qitemeditorfactory_p.h>
+#include <qmetaobject.h>
+#include <qtextlayout.h>
+#include <private/qobject_p.h>
+#include <private/qdnd_p.h>
+#include <private/qtextengine_p.h>
+#include <private/qlayoutengine_p.h>
+#include <qdebug.h>
+#include <qlocale.h>
+#include <qdialog.h>
+#include <qtableview.h>
+
+#include <limits.h>
+
+QT_BEGIN_NAMESPACE
+
+class QStyledItemDelegatePrivate : public QObjectPrivate
+{
+ Q_DECLARE_PUBLIC(QStyledItemDelegate)
+
+public:
+ QStyledItemDelegatePrivate() : factory(0) { }
+
+ static const QWidget *widget(const QStyleOptionViewItem &option)
+ {
+ if (const QStyleOptionViewItemV3 *v3 = qstyleoption_cast<const QStyleOptionViewItemV3 *>(&option))
+ return v3->widget;
+ return 0;
+ }
+
+ const QItemEditorFactory *editorFactory() const
+ {
+ return factory ? factory : QItemEditorFactory::defaultFactory();
+ }
+
+ void _q_commitDataAndCloseEditor(QWidget *editor)
+ {
+ Q_Q(QStyledItemDelegate);
+ emit q->commitData(editor);
+ emit q->closeEditor(editor, QAbstractItemDelegate::SubmitModelCache);
+ }
+ QItemEditorFactory *factory;
+};
+
+/*!
+ \class QStyledItemDelegate
+
+ \brief The QStyledItemDelegate class provides display and editing facilities for
+ data items from a model.
+
+ \ingroup model-view
+ \mainclass
+ \since 4.4
+
+ When displaying data from models in Qt item views, e.g., a
+ QTableView, the individual items are drawn by a delegate. Also,
+ when an item is edited, it provides an editor widget, which is
+ placed on top of the item view while editing takes place.
+ QStyledItemDelegate is the default delegate for all Qt item
+ views, and is installed upon them when they are created.
+
+ The QStyledItemDelegate class is one of the \l{Model/View Classes}
+ and is part of Qt's \l{Model/View Programming}{model/view
+ framework}. The delegate allows the display and editing of items
+ to be developed independently from the model and view.
+
+ The data of items in models are assigned an
+ \l{Qt::}{ItemDataRole}; each item can store a QVariant for each
+ role. QStyledItemDelegate implements display and editing for the
+ most common datatypes expected by users, including booleans,
+ integers, and strings.
+
+ The data will be drawn differently depending on which role they
+ have in the model. The following table describes the roles and the
+ data types the delegate can handle for each of them. It is often
+ sufficient to ensure that the model returns appropriate data for
+ each of the roles to determine the appearance of items in views.
+
+ \table
+ \header \o Role \o Accepted Types
+ \omit
+ \row \o \l Qt::AccessibleDescriptionRole \o QString
+ \row \o \l Qt::AccessibleTextRole \o QString
+ \endomit
+ \row \o \l Qt::BackgroundRole \o QBrush
+ \row \o \l Qt::BackgroundColorRole \o QColor (obsolete; use Qt::BackgroundRole instead)
+ \row \o \l Qt::CheckStateRole \o Qt::CheckState
+ \row \o \l Qt::DecorationRole \o QIcon and QColor
+ \row \o \l Qt::DisplayRole \o QString and types with a string representation
+ \row \o \l Qt::EditRole \o See QItemEditorFactory for details
+ \row \o \l Qt::FontRole \o QFont
+ \row \o \l Qt::SizeHintRole \o QSize
+ \omit
+ \row \o \l Qt::StatusTipRole \o
+ \endomit
+ \row \o \l Qt::TextAlignmentRole \o Qt::Alignment
+ \row \o \l Qt::ForegroundRole \o QBrush
+ \row \o \l Qt::TextColorRole \o QColor (obsolete; use Qt::ForegroundRole instead)
+ \omit
+ \row \o \l Qt::ToolTipRole
+ \row \o \l Qt::WhatsThisRole
+ \endomit
+ \endtable
+
+ Editors are created with a QItemEditorFactory; a default static
+ instance provided by QItemEditorFactory is installed on all item
+ delegates. You can set a custom factory using
+ setItemEditorFactory() or set a new default factory with
+ QItemEditorFactory::setDefaultFactory(). It is the data stored in
+ the item model with the \l{Qt::}{EditRole} that is edited. See the
+ QItemEditorFactory class for a more high-level introduction to
+ item editor factories. The \l{Color Editor Factory Example}{Color
+ Editor Factory} example shows how to create custom editors with a
+ factory.
+
+ \section1 Subclassing QStyledItemDelegate
+
+ If the delegate does not support painting of the data types you
+ need or you want to customize the drawing of items, you need to
+ subclass QStyledItemDelegate, and reimplement paint() and possibly
+ sizeHint(). The paint() function is called individually for each
+ item, and with sizeHint(), you can specify the hint for each
+ of them.
+
+ When reimplementing paint(), one would typically handle the
+ datatypes one would like to draw and use the superclass
+ implementation for other types.
+
+ The painting of check box indicators are performed by the current
+ style. The style also specifies the size and the bounding
+ rectangles in which to draw the data for the different data roles.
+ The bounding rectangle of the item itself is also calculated by
+ the style. When drawing already supported datatypes, it is
+ therefore a good idea to ask the style for these bounding
+ rectangles. The QStyle class description describes this in
+ more detail.
+
+ If you wish to change any of the bounding rectangles calculated by
+ the style or the painting of check box indicators, you can
+ subclass QStyle. Note, however, that the size of the items can
+ also be affected by reimplementing sizeHint().
+
+ It is possible for a custom delegate to provide editors
+ without the use of an editor item factory. In this case, the
+ following virtual functions must be reimplemented:
+
+ \list
+ \o createEditor() returns the widget used to change data from the model
+ and can be reimplemented to customize editing behavior.
+ \o setEditorData() provides the widget with data to manipulate.
+ \o updateEditorGeometry() ensures that the editor is displayed correctly
+ with respect to the item view.
+ \o setModelData() returns updated data to the model.
+ \endlist
+
+ The \l{Star Delegate Example}{Star Delegate} example creates
+ editors by reimplementing these methods.
+
+ \section1 QStyledItemDelegate vs. QItemDelegate
+
+ Since Qt 4.4, there are two delegate classes: QItemDelegate and
+ QStyledItemDelegate. However, the default delegate is QStyledItemDelegate.
+ These two classes are independent alternatives to painting and providing
+ editors for items in views. The difference between them is that
+ QStyledItemDelegate uses the current style to paint its items. We therefore
+ recommend using QStyledItemDelegate as the base class when implementing
+ custom delegates or when working with Qt style sheets. The code required
+ for either class should be equal unless the custom delegate needs to use
+ the style for drawing.
+
+ If you wish to customize the painting of item views, you should
+ implement a custom style. Please see the QStyle class
+ documentation for details.
+
+ \sa {Delegate Classes}, QItemDelegate, QAbstractItemDelegate, QStyle,
+ {Spin Box Delegate Example}, {Star Delegate Example}, {Color
+ Editor Factory Example}
+*/
+
+
+/*!
+ Constructs an item delegate with the given \a parent.
+*/
+QStyledItemDelegate::QStyledItemDelegate(QObject *parent)
+ : QAbstractItemDelegate(*new QStyledItemDelegatePrivate(), parent)
+{
+}
+
+/*!
+ Destroys the item delegate.
+*/
+QStyledItemDelegate::~QStyledItemDelegate()
+{
+}
+
+/*!
+ This function returns the string that the delegate will use to display the
+ Qt::DisplayRole of the model in \a locale. \a value is the value of the Qt::DisplayRole
+ provided by the model.
+
+ The default implementation uses the QLocale::toString to convert \a value into
+ a QString.
+*/
+QString QStyledItemDelegate::displayText(const QVariant &value, const QLocale& locale) const
+{
+ QString text;
+ switch (value.type()) {
+ case QVariant::Double:
+ text = locale.toString(value.toDouble());
+ break;
+ case QVariant::Int:
+ case QVariant::LongLong:
+ text = locale.toString(value.toLongLong());
+ break;
+ case QVariant::UInt:
+ case QVariant::ULongLong:
+ text = locale.toString(value.toULongLong());
+ break;
+ case QVariant::Date:
+ text = locale.toString(value.toDate(), QLocale::ShortFormat);
+ break;
+ case QVariant::Time:
+ text = locale.toString(value.toTime(), QLocale::ShortFormat);
+ break;
+ case QVariant::DateTime:
+ text = locale.toString(value.toDateTime().date(), QLocale::ShortFormat);
+ text += QLatin1Char(' ');
+ text += locale.toString(value.toDateTime().time(), QLocale::ShortFormat);
+ break;
+ default:
+ // convert new lines into line separators
+ text = value.toString();
+ for (int i = 0; i < text.count(); ++i) {
+ if (text.at(i) == QLatin1Char('\n'))
+ text[i] = QChar::LineSeparator;
+ }
+ break;
+ }
+ return text;
+}
+
+/*!
+ Initialize \a option with the values using the index \a index. This method
+ is useful for subclasses when they need a QStyleOptionViewItem, but don't want
+ to fill in all the information themselves. This function will check the version
+ of the QStyleOptionViewItem and fill in the additional values for a
+ QStyleOptionViewItemV2, QStyleOptionViewItemV3 and QStyleOptionViewItemV4.
+
+ \sa QStyleOption::initFrom()
+*/
+void QStyledItemDelegate::initStyleOption(QStyleOptionViewItem *option,
+ const QModelIndex &index) const
+{
+ QVariant value = index.data(Qt::FontRole);
+ if (value.isValid() && !value.isNull()) {
+ option->font = qvariant_cast<QFont>(value).resolve(option->font);
+ option->fontMetrics = QFontMetrics(option->font);
+ }
+
+ value = index.data(Qt::TextAlignmentRole);
+ if (value.isValid() && !value.isNull())
+ option->displayAlignment = (Qt::Alignment)value.toInt();
+
+ value = index.data(Qt::ForegroundRole);
+ if (qVariantCanConvert<QBrush>(value))
+ option->palette.setBrush(QPalette::Text, qvariant_cast<QBrush>(value));
+
+ if (QStyleOptionViewItemV4 *v4 = qstyleoption_cast<QStyleOptionViewItemV4 *>(option)) {
+ v4->index = index;
+ QVariant value = index.data(Qt::CheckStateRole);
+ if (value.isValid() && !value.isNull()) {
+ v4->features |= QStyleOptionViewItemV2::HasCheckIndicator;
+ v4->checkState = static_cast<Qt::CheckState>(value.toInt());
+ }
+
+ value = index.data(Qt::DecorationRole);
+ if (value.isValid() && !value.isNull()) {
+ v4->features |= QStyleOptionViewItemV2::HasDecoration;
+ switch (value.type()) {
+ case QVariant::Icon: {
+ v4->icon = qvariant_cast<QIcon>(value);
+ QIcon::Mode mode;
+ if (!(option->state & QStyle::State_Enabled))
+ mode = QIcon::Disabled;
+ else if (option->state & QStyle::State_Selected)
+ mode = QIcon::Selected;
+ else
+ mode = QIcon::Normal;
+ QIcon::State state = option->state & QStyle::State_Open ? QIcon::On : QIcon::Off;
+ v4->decorationSize = v4->icon.actualSize(option->decorationSize, mode, state);
+ break;
+ }
+ case QVariant::Color: {
+ QPixmap pixmap(option->decorationSize);
+ pixmap.fill(qvariant_cast<QColor>(value));
+ v4->icon = QIcon(pixmap);
+ break;
+ }
+ case QVariant::Image: {
+ QImage image = qvariant_cast<QImage>(value);
+ v4->icon = QIcon(QPixmap::fromImage(image));
+ v4->decorationSize = image.size();
+ break;
+ }
+ case QVariant::Pixmap: {
+ QPixmap pixmap = qvariant_cast<QPixmap>(value);
+ v4->icon = QIcon(pixmap);
+ v4->decorationSize = pixmap.size();
+ break;
+ }
+ default:
+ break;
+ }
+ }
+
+ value = index.data(Qt::DisplayRole);
+ if (value.isValid() && !value.isNull()) {
+ v4->features |= QStyleOptionViewItemV2::HasDisplay;
+ v4->text = displayText(value, v4->locale);
+ }
+
+ v4->backgroundBrush = qvariant_cast<QBrush>(index.data(Qt::BackgroundRole));
+ }
+}
+
+/*!
+ Renders the delegate using the given \a painter and style \a option for
+ the item specified by \a index.
+
+ This function paints the item using the view's QStyle.
+
+ When reimplementing paint in a subclass. Use the initStyleOption()
+ to set up the \a option in the same way as the
+ QStyledItemDelegate; the option will always be an instance of
+ QStyleOptionViewItemV4. Please see its class description for
+ information on its contents.
+
+ Whenever possible, use the \a option while painting.
+ Especially its \l{QStyleOption::}{rect} variable to decide
+ where to draw and its \l{QStyleOption::}{state} to determine
+ if it is enabled or selected.
+
+ After painting, you should ensure that the painter is returned to
+ its the state it was supplied in when this function was called.
+ For example, it may be useful to call QPainter::save() before
+ painting and QPainter::restore() afterwards.
+
+ \sa QItemDelegate::paint(), QStyle::drawControl(), QStyle::CE_ItemViewItem
+*/
+void QStyledItemDelegate::paint(QPainter *painter,
+ const QStyleOptionViewItem &option, const QModelIndex &index) const
+{
+ Q_ASSERT(index.isValid());
+
+ QStyleOptionViewItemV4 opt = option;
+ initStyleOption(&opt, index);
+
+ const QWidget *widget = QStyledItemDelegatePrivate::widget(option);
+ QStyle *style = widget ? widget->style() : QApplication::style();
+ style->drawControl(QStyle::CE_ItemViewItem, &opt, painter, widget);
+}
+
+/*!
+ Returns the size needed by the delegate to display the item
+ specified by \a index, taking into account the style information
+ provided by \a option.
+
+ This function uses the view's QStyle to determine the size of the
+ item.
+
+ \sa QStyle::sizeFromContents(), QStyle::CT_ItemViewItem
+*/
+QSize QStyledItemDelegate::sizeHint(const QStyleOptionViewItem &option,
+ const QModelIndex &index) const
+{
+ QVariant value = index.data(Qt::SizeHintRole);
+ if (value.isValid())
+ return qvariant_cast<QSize>(value);
+
+ QStyleOptionViewItemV4 opt = option;
+ initStyleOption(&opt, index);
+ const QWidget *widget = QStyledItemDelegatePrivate::widget(option);
+ QStyle *style = widget ? widget->style() : QApplication::style();
+ return style->sizeFromContents(QStyle::CT_ItemViewItem, &opt, QSize(), widget);
+}
+
+/*!
+ Returns the widget used to edit the item specified by \a index
+ for editing. The \a parent widget and style \a option are used to
+ control how the editor widget appears.
+
+ \sa QAbstractItemDelegate::createEditor()
+*/
+QWidget *QStyledItemDelegate::createEditor(QWidget *parent,
+ const QStyleOptionViewItem &,
+ const QModelIndex &index) const
+{
+ Q_D(const QStyledItemDelegate);
+ if (!index.isValid())
+ return 0;
+ QVariant::Type t = static_cast<QVariant::Type>(index.data(Qt::EditRole).userType());
+ return d->editorFactory()->createEditor(t, parent);
+}
+
+/*!
+ Sets the data to be displayed and edited by the \a editor from the
+ data model item specified by the model \a index.
+
+ The default implementation stores the data in the \a editor
+ widget's \l {Qt's Property System} {user property}.
+
+ \sa QMetaProperty::isUser()
+*/
+void QStyledItemDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const
+{
+#ifdef QT_NO_PROPERTIES
+ Q_UNUSED(editor);
+ Q_UNUSED(index);
+#else
+ Q_D(const QStyledItemDelegate);
+ QVariant v = index.data(Qt::EditRole);
+ QByteArray n = editor->metaObject()->userProperty().name();
+
+ // ### Qt 5: remove
+ // A work-around for missing "USER true" in qdatetimeedit.h for
+ // QTimeEdit's time property and QDateEdit's date property.
+ // It only triggers if the default user property "dateTime" is
+ // reported for QTimeEdit and QDateEdit.
+ if (n == "dateTime") {
+ if (editor->inherits("QTimeEdit"))
+ n = "time";
+ else if (editor->inherits("QDateEdit"))
+ n = "date";
+ }
+
+ // ### Qt 5: give QComboBox a USER property
+ if (n.isEmpty() && editor->inherits("QComboBox"))
+ n = d->editorFactory()->valuePropertyName(static_cast<QVariant::Type>(v.userType()));
+ if (!n.isEmpty()) {
+ if (!v.isValid())
+ v = QVariant(editor->property(n).userType(), (const void *)0);
+ editor->setProperty(n, v);
+ }
+#endif
+}
+
+/*!
+ Gets data drom the \a editor widget and stores it in the specified
+ \a model at the item \a index.
+
+ The default implementation gets the value to be stored in the data
+ model from the \a editor widget's \l {Qt's Property System} {user
+ property}.
+
+ \sa QMetaProperty::isUser()
+*/
+void QStyledItemDelegate::setModelData(QWidget *editor,
+ QAbstractItemModel *model,
+ const QModelIndex &index) const
+{
+#ifdef QT_NO_PROPERTIES
+ Q_UNUSED(model);
+ Q_UNUSED(editor);
+ Q_UNUSED(index);
+#else
+ Q_D(const QStyledItemDelegate);
+ Q_ASSERT(model);
+ Q_ASSERT(editor);
+ QByteArray n = editor->metaObject()->userProperty().name();
+ if (n.isEmpty())
+ n = d->editorFactory()->valuePropertyName(
+ static_cast<QVariant::Type>(model->data(index, Qt::EditRole).userType()));
+ if (!n.isEmpty())
+ model->setData(index, editor->property(n), Qt::EditRole);
+#endif
+}
+
+/*!
+ Updates the \a editor for the item specified by \a index
+ according to the style \a option given.
+*/
+void QStyledItemDelegate::updateEditorGeometry(QWidget *editor,
+ const QStyleOptionViewItem &option,
+ const QModelIndex &index) const
+{
+ if (!editor)
+ return;
+ Q_ASSERT(index.isValid());
+ const QWidget *widget = QStyledItemDelegatePrivate::widget(option);
+
+ QStyleOptionViewItemV4 opt = option;
+ initStyleOption(&opt, index);
+ // let the editor take up all available space
+ //if the editor is not a QLineEdit
+ //or it is in a QTableView
+#if !defined(QT_NO_TABLEVIEW) && !defined(QT_NO_LINEEDIT)
+ if (qobject_cast<QExpandingLineEdit*>(editor) && !qobject_cast<const QTableView*>(widget))
+ opt.showDecorationSelected = editor->style()->styleHint(QStyle::SH_ItemView_ShowDecorationSelected, 0, editor);
+ else
+#endif
+ opt.showDecorationSelected = true;
+
+ QStyle *style = widget ? widget->style() : QApplication::style();
+ QRect geom = style->subElementRect(QStyle::SE_ItemViewItemText, &opt, widget);
+ if ( editor->layoutDirection() == Qt::RightToLeft) {
+ const int delta = qSmartMinSize(editor).width() - geom.width();
+ if (delta > 0) {
+ //we need to widen the geometry
+ geom.adjust(-delta, 0, 0, 0);
+ }
+ }
+
+ editor->setGeometry(geom);
+}
+
+/*!
+ Returns the editor factory used by the item delegate.
+ If no editor factory is set, the function will return null.
+
+ \sa setItemEditorFactory()
+*/
+QItemEditorFactory *QStyledItemDelegate::itemEditorFactory() const
+{
+ Q_D(const QStyledItemDelegate);
+ return d->factory;
+}
+
+/*!
+ Sets the editor factory to be used by the item delegate to be the \a factory
+ specified. If no editor factory is set, the item delegate will use the
+ default editor factory.
+
+ \sa itemEditorFactory()
+*/
+void QStyledItemDelegate::setItemEditorFactory(QItemEditorFactory *factory)
+{
+ Q_D(QStyledItemDelegate);
+ d->factory = factory;
+}
+
+
+/*!
+ \fn bool QStyledItemDelegate::eventFilter(QObject *editor, QEvent *event)
+
+ Returns true if the given \a editor is a valid QWidget and the
+ given \a event is handled; otherwise returns false. The following
+ key press events are handled by default:
+
+ \list
+ \o \gui Tab
+ \o \gui Backtab
+ \o \gui Enter
+ \o \gui Return
+ \o \gui Esc
+ \endlist
+
+ In the case of \gui Tab, \gui Backtab, \gui Enter and \gui Return
+ key press events, the \a editor's data is comitted to the model
+ and the editor is closed. If the \a event is a \gui Tab key press
+ the view will open an editor on the next item in the
+ view. Likewise, if the \a event is a \gui Backtab key press the
+ view will open an editor on the \e previous item in the view.
+
+ If the event is a \gui Esc key press event, the \a editor is
+ closed \e without committing its data.
+
+ \sa commitData(), closeEditor()
+*/
+bool QStyledItemDelegate::eventFilter(QObject *object, QEvent *event)
+{
+ QWidget *editor = qobject_cast<QWidget*>(object);
+ if (!editor)
+ return false;
+ if (event->type() == QEvent::KeyPress) {
+ switch (static_cast<QKeyEvent *>(event)->key()) {
+ case Qt::Key_Tab:
+ emit commitData(editor);
+ emit closeEditor(editor, QAbstractItemDelegate::EditNextItem);
+ return true;
+ case Qt::Key_Backtab:
+ emit commitData(editor);
+ emit closeEditor(editor, QAbstractItemDelegate::EditPreviousItem);
+ return true;
+ case Qt::Key_Enter:
+ case Qt::Key_Return:
+#ifndef QT_NO_TEXTEDIT
+ if (qobject_cast<QTextEdit*>(editor))
+ return false; // don't filter enter key events for QTextEdit
+ // We want the editor to be able to process the key press
+ // before committing the data (e.g. so it can do
+ // validation/fixup of the input).
+#endif // QT_NO_TEXTEDIT
+#ifndef QT_NO_LINEEDIT
+ if (QLineEdit *e = qobject_cast<QLineEdit*>(editor))
+ if (!e->hasAcceptableInput())
+ return false;
+#endif // QT_NO_LINEEDIT
+ QMetaObject::invokeMethod(this, "_q_commitDataAndCloseEditor",
+ Qt::QueuedConnection, Q_ARG(QWidget*, editor));
+ return false;
+ case Qt::Key_Escape:
+ // don't commit data
+ emit closeEditor(editor, QAbstractItemDelegate::RevertModelCache);
+ break;
+ default:
+ return false;
+ }
+ if (editor->parentWidget())
+ editor->parentWidget()->setFocus();
+ return true;
+ } else if (event->type() == QEvent::FocusOut || event->type() == QEvent::Hide) {
+ //the Hide event will take care of he editors that are in fact complete dialogs
+ if (!editor->isActiveWindow() || (QApplication::focusWidget() != editor)) {
+ QWidget *w = QApplication::focusWidget();
+ while (w) { // don't worry about focus changes internally in the editor
+ if (w == editor)
+ return false;
+ w = w->parentWidget();
+ }
+#ifndef QT_NO_DRAGANDDROP
+ // The window may lose focus during an drag operation.
+ // i.e when dragging involves the taskbar on Windows.
+ if (QDragManager::self() && QDragManager::self()->object != 0)
+ return false;
+#endif
+ // Opening a modal dialog will start a new eventloop
+ // that will process the deleteLater event.
+ QWidget *activeModalWidget = QApplication::activeModalWidget();
+ if (activeModalWidget
+ && !activeModalWidget->isAncestorOf(editor)
+ && qobject_cast<QDialog*>(activeModalWidget))
+ return false;
+ emit commitData(editor);
+ emit closeEditor(editor, NoHint);
+ }
+ } else if (event->type() == QEvent::ShortcutOverride) {
+ if (static_cast<QKeyEvent*>(event)->key() == Qt::Key_Escape) {
+ event->accept();
+ return true;
+ }
+ }
+ return false;
+}
+
+/*!
+ \reimp
+*/
+bool QStyledItemDelegate::editorEvent(QEvent *event,
+ QAbstractItemModel *model,
+ const QStyleOptionViewItem &option,
+ const QModelIndex &index)
+{
+ Q_ASSERT(event);
+ Q_ASSERT(model);
+
+ // make sure that the item is checkable
+ Qt::ItemFlags flags = model->flags(index);
+ if (!(flags & Qt::ItemIsUserCheckable) || !(option.state & QStyle::State_Enabled)
+ || !(flags & Qt::ItemIsEnabled))
+ return false;
+
+ // make sure that we have a check state
+ QVariant value = index.data(Qt::CheckStateRole);
+ if (!value.isValid())
+ return false;
+
+ const QWidget *widget = QStyledItemDelegatePrivate::widget(option);
+ QStyle *style = widget ? widget->style() : QApplication::style();
+
+ // make sure that we have the right event type
+ if ((event->type() == QEvent::MouseButtonRelease)
+ || (event->type() == QEvent::MouseButtonDblClick)) {
+ QStyleOptionViewItemV4 viewOpt(option);
+ initStyleOption(&viewOpt, index);
+ QRect checkRect = style->subElementRect(QStyle::SE_ItemViewItemCheckIndicator, &viewOpt, widget);
+ QMouseEvent *me = static_cast<QMouseEvent*>(event);
+ if (me->button() != Qt::LeftButton || !checkRect.contains(me->pos()))
+ return false;
+
+ // eat the double click events inside the check rect
+ if (event->type() == QEvent::MouseButtonDblClick)
+ return true;
+
+ } else if (event->type() == QEvent::KeyPress) {
+ if (static_cast<QKeyEvent*>(event)->key() != Qt::Key_Space
+ && static_cast<QKeyEvent*>(event)->key() != Qt::Key_Select)
+ return false;
+ } else {
+ return false;
+ }
+
+ Qt::CheckState state = (static_cast<Qt::CheckState>(value.toInt()) == Qt::Checked
+ ? Qt::Unchecked : Qt::Checked);
+ return model->setData(index, state, Qt::CheckStateRole);
+}
+
+QT_END_NAMESPACE
+
+#include "moc_qstyleditemdelegate.cpp"
+
+#endif // QT_NO_ITEMVIEWS
diff --git a/src/gui/itemviews/qstyleditemdelegate.h b/src/gui/itemviews/qstyleditemdelegate.h
new file mode 100644
index 0000000000..dee3ca94a4
--- /dev/null
+++ b/src/gui/itemviews/qstyleditemdelegate.h
@@ -0,0 +1,116 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSTYLEDITEMDELEGATE_H
+#define QSTYLEDITEMDELEGATE_H
+
+#include <QtGui/qabstractitemdelegate.h>
+#include <QtCore/qstring.h>
+#include <QtGui/qpixmap.h>
+#include <QtCore/qvariant.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+#ifndef QT_NO_ITEMVIEWS
+
+class QStyledItemDelegatePrivate;
+class QItemEditorFactory;
+
+class Q_GUI_EXPORT QStyledItemDelegate : public QAbstractItemDelegate
+{
+ Q_OBJECT
+
+public:
+ explicit QStyledItemDelegate(QObject *parent = 0);
+ ~QStyledItemDelegate();
+
+ // painting
+ void paint(QPainter *painter,
+ const QStyleOptionViewItem &option, const QModelIndex &index) const;
+ QSize sizeHint(const QStyleOptionViewItem &option,
+ const QModelIndex &index) const;
+
+ // editing
+ QWidget *createEditor(QWidget *parent,
+ const QStyleOptionViewItem &option,
+ const QModelIndex &index) const;
+
+ void setEditorData(QWidget *editor, const QModelIndex &index) const;
+ void setModelData(QWidget *editor,
+ QAbstractItemModel *model,
+ const QModelIndex &index) const;
+
+ void updateEditorGeometry(QWidget *editor,
+ const QStyleOptionViewItem &option,
+ const QModelIndex &index) const;
+
+ // editor factory
+ QItemEditorFactory *itemEditorFactory() const;
+ void setItemEditorFactory(QItemEditorFactory *factory);
+
+ virtual QString displayText(const QVariant &value, const QLocale &locale) const;
+
+protected:
+ virtual void initStyleOption(QStyleOptionViewItem *option,
+ const QModelIndex &index) const;
+
+ bool eventFilter(QObject *object, QEvent *event);
+ bool editorEvent(QEvent *event, QAbstractItemModel *model,
+ const QStyleOptionViewItem &option, const QModelIndex &index);
+
+private:
+ Q_DECLARE_PRIVATE(QStyledItemDelegate)
+ Q_DISABLE_COPY(QStyledItemDelegate)
+
+ Q_PRIVATE_SLOT(d_func(), void _q_commitDataAndCloseEditor(QWidget*))
+};
+
+#endif // QT_NO_ITEMVIEWS
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QSTYLEDITEMDELEGATE_H
diff --git a/src/gui/itemviews/qtableview.cpp b/src/gui/itemviews/qtableview.cpp
new file mode 100644
index 0000000000..2902768b73
--- /dev/null
+++ b/src/gui/itemviews/qtableview.cpp
@@ -0,0 +1,2515 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qtableview.h"
+
+#ifndef QT_NO_TABLEVIEW
+#include <qheaderview.h>
+#include <qitemdelegate.h>
+#include <qapplication.h>
+#include <qpainter.h>
+#include <qstyle.h>
+#include <qsize.h>
+#include <qevent.h>
+#include <qbitarray.h>
+#include <qscrollbar.h>
+#include <qabstractbutton.h>
+#include <private/qtableview_p.h>
+#ifndef QT_NO_ACCESSIBILITY
+#include <qaccessible.h>
+#endif
+
+QT_BEGIN_NAMESPACE
+
+class QTableCornerButton : public QAbstractButton
+{
+ Q_OBJECT
+public:
+ QTableCornerButton(QWidget *parent) : QAbstractButton(parent) {}
+ void paintEvent(QPaintEvent*) {
+ QStyleOptionHeader opt;
+ opt.init(this);
+ QStyle::State state = QStyle::State_None;
+ if (isEnabled())
+ state |= QStyle::State_Enabled;
+ if (isActiveWindow())
+ state |= QStyle::State_Active;
+ if (isDown())
+ state |= QStyle::State_Sunken;
+ opt.state = state;
+ opt.rect = rect();
+ opt.position = QStyleOptionHeader::OnlyOneSection;
+ QPainter painter(this);
+ style()->drawControl(QStyle::CE_Header, &opt, &painter, this);
+ }
+};
+
+void QTableViewPrivate::init()
+{
+ Q_Q(QTableView);
+
+ q->setEditTriggers(editTriggers|QAbstractItemView::AnyKeyPressed);
+
+ QHeaderView *vertical = new QHeaderView(Qt::Vertical, q);
+ vertical->setClickable(true);
+ vertical->setHighlightSections(true);
+ q->setVerticalHeader(vertical);
+
+ QHeaderView *horizontal = new QHeaderView(Qt::Horizontal, q);
+ horizontal->setClickable(true);
+ horizontal->setHighlightSections(true);
+ q->setHorizontalHeader(horizontal);
+
+ tabKeyNavigation = true;
+
+ cornerWidget = new QTableCornerButton(q);
+ cornerWidget->setFocusPolicy(Qt::NoFocus);
+ QObject::connect(cornerWidget, SIGNAL(clicked()), q, SLOT(selectAll()));
+}
+
+/*!
+ \internal
+ Trims away indices that are hidden in the treeview due to hidden horizontal or vertical sections.
+*/
+void QTableViewPrivate::trimHiddenSelections(QItemSelectionRange *range) const
+{
+ Q_ASSERT(range && range->isValid());
+
+ int top = range->top();
+ int left = range->left();
+ int bottom = range->bottom();
+ int right = range->right();
+
+ while (bottom >= top && verticalHeader->isSectionHidden(bottom))
+ --bottom;
+ while (right >= left && horizontalHeader->isSectionHidden(right))
+ --right;
+
+ if (top > bottom || left > right) { // everything is hidden
+ *range = QItemSelectionRange();
+ return;
+ }
+
+ while (verticalHeader->isSectionHidden(top) && top <= bottom)
+ ++top;
+ while (horizontalHeader->isSectionHidden(left) && left <= right)
+ ++left;
+
+ if (top > bottom || left > right) { // everything is hidden
+ *range = QItemSelectionRange();
+ return;
+ }
+
+ QModelIndex bottomRight = model->index(bottom, right, range->parent());
+ QModelIndex topLeft = model->index(top, left, range->parent());
+ *range = QItemSelectionRange(topLeft, bottomRight);
+}
+
+/*!
+ \internal
+ Sets the span for the cell at (\a row, \a column).
+*/
+void QTableViewPrivate::setSpan(int row, int column, int rowSpan, int columnSpan)
+{
+ if (row < 0 || column < 0 || rowSpan < 0 || columnSpan < 0)
+ return;
+ Span sp(row, column, rowSpan, columnSpan);
+ QList<Span>::iterator it;
+ for (it = spans.begin(); it != spans.end(); ++it) {
+ if (((*it).top() == sp.top()) && ((*it).left() == sp.left())) {
+ if ((sp.height() == 1) && (sp.width() == 1))
+ spans.erase(it); // "Implicit" span (1, 1), no need to store it
+ else
+ *it = sp; // Replace
+ return;
+ }
+ }
+ spans.append(sp);
+}
+
+/*!
+ \internal
+ Gets the span information for the cell at (\a row, \a column).
+*/
+QTableViewPrivate::Span QTableViewPrivate::span(int row, int column) const
+{
+ QList<Span>::const_iterator it;
+ for (it = spans.constBegin(); it != spans.constEnd(); ++it) {
+ Span span = *it;
+ if (isInSpan(row, column, span))
+ return span;
+ }
+ return Span(row, column, 1, 1);
+}
+
+/*!
+ \internal
+ Returns the logical index of the last section that's part of the span.
+*/
+int QTableViewPrivate::sectionSpanEndLogical(const QHeaderView *header, int logical, int span) const
+{
+ int visual = header->visualIndex(logical);
+ for (int i = 1; i < span; ) {
+ if (++visual >= header->count())
+ break;
+ logical = header->logicalIndex(visual);
+ ++i;
+ }
+ return logical;
+}
+
+/*!
+ \internal
+ Returns the size of the span starting at logical index \a logical
+ and spanning \a span sections.
+*/
+int QTableViewPrivate::sectionSpanSize(const QHeaderView *header, int logical, int span) const
+{
+ int endLogical = sectionSpanEndLogical(header, logical, span);
+ return header->sectionPosition(endLogical)
+ - header->sectionPosition(logical)
+ + header->sectionSize(endLogical);
+}
+
+/*!
+ \internal
+ Returns true if the section at logical index \a logical is part of the span
+ starting at logical index \a spanLogical and spanning \a span sections;
+ otherwise, returns false.
+*/
+bool QTableViewPrivate::spanContainsSection(const QHeaderView *header, int logical, int spanLogical, int span) const
+{
+ if (logical == spanLogical)
+ return true; // it's the start of the span
+ int visual = header->visualIndex(spanLogical);
+ for (int i = 1; i < span; ) {
+ if (++visual >= header->count())
+ break;
+ spanLogical = header->logicalIndex(visual);
+ if (logical == spanLogical)
+ return true;
+ ++i;
+ }
+ return false;
+}
+
+/*!
+ \internal
+ Returns true if one or more spans intersect column \a column.
+*/
+bool QTableViewPrivate::spansIntersectColumn(int column) const
+{
+ QList<Span>::const_iterator it;
+ for (it = spans.constBegin(); it != spans.constEnd(); ++it) {
+ Span span = *it;
+ if (spanContainsColumn(column, span.left(), span.width()))
+ return true;
+ }
+ return false;
+}
+
+/*!
+ \internal
+ Returns true if one or more spans intersect row \a row.
+*/
+bool QTableViewPrivate::spansIntersectRow(int row) const
+{
+ QList<Span>::const_iterator it;
+ for (it = spans.constBegin(); it != spans.constEnd(); ++it) {
+ Span span = *it;
+ if (spanContainsRow(row, span.top(), span.height()))
+ return true;
+ }
+ return false;
+}
+
+/*!
+ \internal
+ Returns true if one or more spans intersect one or more columns.
+*/
+bool QTableViewPrivate::spansIntersectColumns(const QList<int> &columns) const
+{
+ QList<int>::const_iterator it;
+ for (it = columns.constBegin(); it != columns.constEnd(); ++it) {
+ if (spansIntersectColumn(*it))
+ return true;
+ }
+ return false;
+}
+
+/*!
+ \internal
+ Returns true if one or more spans intersect one or more rows.
+*/
+bool QTableViewPrivate::spansIntersectRows(const QList<int> &rows) const
+{
+ QList<int>::const_iterator it;
+ for (it = rows.constBegin(); it != rows.constEnd(); ++it) {
+ if (spansIntersectRow(*it))
+ return true;
+ }
+ return false;
+}
+
+/*!
+ \internal
+ Returns the visual rect for the given \a span.
+*/
+QRect QTableViewPrivate::visualSpanRect(const Span &span) const
+{
+ Q_Q(const QTableView);
+ // vertical
+ int row = span.top();
+ int rowp = verticalHeader->sectionViewportPosition(row);
+ int rowh = rowSpanHeight(row, span.height());
+ // horizontal
+ int column = span.left();
+ int colw = columnSpanWidth(column, span.width());
+ if (q->isRightToLeft())
+ column = span.right();
+ int colp = horizontalHeader->sectionViewportPosition(column);
+
+ const int i = showGrid ? 1 : 0;
+ if (q->isRightToLeft())
+ return QRect(colp + i, rowp, colw - i, rowh - i);
+ return QRect(colp, rowp, colw - i, rowh - i);
+}
+
+/*!
+ \internal
+ Draws the spanning cells within rect \a area, and clips them off as
+ preparation for the main drawing loop.
+ \a drawn is a QBitArray of visualRowCountxvisualCoulumnCount which say if particular cell has been drawn
+*/
+void QTableViewPrivate::drawAndClipSpans(const QRect &area, QPainter *painter,
+ const QStyleOptionViewItemV4 &option, QBitArray *drawn,
+ int firstVisualRow, int lastVisualRow, int firstVisualColumn, int lastVisualColumn)
+{
+ bool alternateBase = false;
+ QRegion region = viewport->rect();
+
+ QList<Span>::const_iterator it;
+ for (it = spans.constBegin(); it != spans.constEnd(); ++it) {
+ Span span = *it;
+
+ int row = span.top();
+ int col = span.left();
+ if (isHidden(row, col))
+ continue;
+ QModelIndex index = model->index(row, col, root);
+ if (!index.isValid())
+ continue;
+ QRect rect = visualSpanRect(span);
+ rect.translate(scrollDelayOffset);
+ if (!rect.intersects(area))
+ continue;
+ QStyleOptionViewItemV4 opt = option;
+ opt.rect = rect;
+ alternateBase = alternatingColors && (span.top() & 1);
+ if (alternateBase)
+ opt.features |= QStyleOptionViewItemV2::Alternate;
+ else
+ opt.features &= ~QStyleOptionViewItemV2::Alternate;
+ drawCell(painter, opt, index);
+ region -= rect;
+ for (int r = span.top(); r <= span.bottom(); ++r) {
+ const int vr = visualRow(r);
+ if (vr < firstVisualRow || vr > lastVisualRow)
+ continue;
+ for (int c = span.left(); c <= span.right(); ++c) {
+ const int vc = visualColumn(c);
+ if (vc < firstVisualColumn || vc > lastVisualColumn)
+ continue;
+ drawn->setBit((vr - firstVisualRow) * (lastVisualColumn - firstVisualColumn + 1)
+ + vc - firstVisualColumn);
+ }
+ }
+
+ }
+ painter->setClipRegion(region);
+}
+
+/*!
+ \internal
+ Draws a table cell.
+*/
+void QTableViewPrivate::drawCell(QPainter *painter, const QStyleOptionViewItemV4 &option, const QModelIndex &index)
+{
+ Q_Q(QTableView);
+ QStyleOptionViewItemV4 opt = option;
+
+ if (selectionModel && selectionModel->isSelected(index))
+ opt.state |= QStyle::State_Selected;
+ if (index == hover)
+ opt.state |= QStyle::State_MouseOver;
+ if (option.state & QStyle::State_Enabled) {
+ QPalette::ColorGroup cg;
+ if ((model->flags(index) & Qt::ItemIsEnabled) == 0) {
+ opt.state &= ~QStyle::State_Enabled;
+ cg = QPalette::Disabled;
+ } else {
+ cg = QPalette::Normal;
+ }
+ opt.palette.setCurrentColorGroup(cg);
+ }
+
+ if (index == q->currentIndex()) {
+ const bool focus = (q->hasFocus() || viewport->hasFocus()) && q->currentIndex().isValid();
+ if (focus)
+ opt.state |= QStyle::State_HasFocus;
+ }
+
+ if (opt.features & QStyleOptionViewItemV2::Alternate)
+ painter->fillRect(opt.rect, opt.palette.brush(QPalette::AlternateBase));
+
+ if (const QWidget *widget = editorForIndex(index).editor) {
+ painter->save();
+ painter->setClipRect(widget->geometry());
+ q->itemDelegate(index)->paint(painter, opt, index);
+ painter->restore();
+ } else {
+ q->itemDelegate(index)->paint(painter, opt, index);
+ }
+}
+
+/*!
+ \class QTableView
+
+ \brief The QTableView class provides a default model/view
+ implementation of a table view.
+
+ \ingroup model-view
+ \ingroup advanced
+ \mainclass
+
+ A QTableView implements a table view that displays items from a
+ model. This class is used to provide standard tables that were
+ previously provided by the QTable class, but using the more
+ flexible approach provided by Qt's model/view architecture.
+
+ The QTableView class is one of the \l{Model/View Classes}
+ and is part of Qt's \l{Model/View Programming}{model/view framework}.
+
+ QTableView implements the interfaces defined by the
+ QAbstractItemView class to allow it to display data provided by
+ models derived from the QAbstractItemModel class.
+
+ \section1 Navigation
+
+ You can navigate the cells in the table by clicking on a cell with the
+ mouse, or by using the arrow keys. Because QTableView enables
+ \l{QAbstractItemView::tabKeyNavigation}{tabKeyNavigation} by default, you
+ can also hit Tab and Backtab to move from cell to cell.
+
+ \section1 Visual Appearance
+
+ The table has a vertical header that can be obtained using the
+ verticalHeader() function, and a horizontal header that is available
+ through the horizontalHeader() function. The height of each row in the
+ table can be found by using rowHeight(); similarly, the width of
+ columns can be found using columnWidth(). Since both of these are plain
+ widgets, you can hide either of them using their hide() functions.
+
+ Rows and columns can be hidden and shown with hideRow(), hideColumn(),
+ showRow(), and showColumn(). They can be selected with selectRow()
+ and selectColumn(). The table will show a grid depending on the
+ \l showGrid property.
+
+ The items shown in a table view, like those in the other item views, are
+ rendered and edited using standard \l{QItemDelegate}{delegates}. However,
+ for some tasks it is sometimes useful to be able to insert widgets in a
+ table instead. Widgets are set for particular indexes with the
+ \l{QAbstractItemView::}{setIndexWidget()} function, and
+ later retrieved with \l{QAbstractItemView::}{indexWidget()}.
+
+ \table
+ \row \o \inlineimage qtableview-resized.png
+ \o By default, the cells in a table do not expand to fill the available space.
+
+ You can make the cells fill the available space by stretching the last
+ header section. Access the relevant header using horizontalHeader()
+ or verticalHeader() and set the header's \l{QHeaderView::}{stretchLastSection}
+ property.
+
+ To distribute the available space according to the space requirement of
+ each column or row, call the view's resizeColumnsToContents() or
+ resizeRowsToContents() functions.
+ \endtable
+
+ \section1 Coordinate Systems
+
+ For some specialized forms of tables it is useful to be able to
+ convert between row and column indexes and widget coordinates.
+ The rowAt() function provides the y-coordinate within the view of the
+ specified row; the row index can be used to obtain a corresponding
+ y-coordinate with rowViewportPosition(). The columnAt() and
+ columnViewportPosition() functions provide the equivalent conversion
+ operations between x-coordinates and column indexes.
+
+ \section1 Styles
+
+ QTableView is styled appropriately for each platform. The following images show
+ how it looks on three different platforms. Go to the \l{Qt Widget Gallery} to see
+ its appearance in other styles.
+
+ \table 100%
+ \row \o \inlineimage windowsxp-tableview.png Screenshot of a Windows XP style table view
+ \o \inlineimage macintosh-tableview.png Screenshot of a Macintosh style table view
+ \o \inlineimage plastique-tableview.png Screenshot of a Plastique style table view
+ \row \o A \l{Windows XP Style Widget Gallery}{Windows XP style} table view.
+ \o A \l{Macintosh Style Widget Gallery}{Macintosh style} table view.
+ \o A \l{Plastique Style Widget Gallery}{Plastique style} table view.
+ \endtable
+
+ \sa QTableWidget, {View Classes}, QAbstractItemModel, QAbstractItemView,
+ {Chart Example}, {Pixelator Example}, {Table Model Example}
+*/
+
+/*!
+ Constructs a table view with a \a parent to represent the data.
+
+ \sa QAbstractItemModel
+*/
+
+QTableView::QTableView(QWidget *parent)
+ : QAbstractItemView(*new QTableViewPrivate, parent)
+{
+ Q_D(QTableView);
+ d->init();
+}
+
+/*!
+ \internal
+*/
+QTableView::QTableView(QTableViewPrivate &dd, QWidget *parent)
+ : QAbstractItemView(dd, parent)
+{
+ Q_D(QTableView);
+ d->init();
+}
+
+/*!
+ Destroys the table view.
+*/
+QTableView::~QTableView()
+{
+}
+
+/*!
+ \reimp
+*/
+void QTableView::setModel(QAbstractItemModel *model)
+{
+ Q_D(QTableView);
+ d->verticalHeader->setModel(model);
+ d->horizontalHeader->setModel(model);
+ QAbstractItemView::setModel(model);
+}
+
+/*!
+ \reimp
+*/
+void QTableView::setRootIndex(const QModelIndex &index)
+{
+ Q_D(QTableView);
+ if (index == d->root) {
+ viewport()->update();
+ return;
+ }
+ d->verticalHeader->setRootIndex(index);
+ d->horizontalHeader->setRootIndex(index);
+ QAbstractItemView::setRootIndex(index);
+}
+
+/*!
+ \reimp
+*/
+void QTableView::setSelectionModel(QItemSelectionModel *selectionModel)
+{
+ Q_D(QTableView);
+ Q_ASSERT(selectionModel);
+ d->verticalHeader->setSelectionModel(selectionModel);
+ d->horizontalHeader->setSelectionModel(selectionModel);
+ QAbstractItemView::setSelectionModel(selectionModel);
+}
+
+/*!
+ Returns the table view's horizontal header.
+
+ \sa setHorizontalHeader(), verticalHeader(), QAbstractItemModel::headerData()
+*/
+QHeaderView *QTableView::horizontalHeader() const
+{
+ Q_D(const QTableView);
+ return d->horizontalHeader;
+}
+
+/*!
+ Returns the table view's vertical header.
+
+ \sa setVerticalHeader(), horizontalHeader(), QAbstractItemModel::headerData()
+*/
+QHeaderView *QTableView::verticalHeader() const
+{
+ Q_D(const QTableView);
+ return d->verticalHeader;
+}
+
+/*!
+ Sets the widget to use for the horizontal header to \a header.
+
+ \sa horizontalHeader() setVerticalHeader()
+*/
+void QTableView::setHorizontalHeader(QHeaderView *header)
+{
+ Q_D(QTableView);
+
+ if (!header || header == d->horizontalHeader)
+ return;
+ if (d->horizontalHeader && d->horizontalHeader->parent() == this)
+ delete d->horizontalHeader;
+ d->horizontalHeader = header;
+ d->horizontalHeader->setParent(this);
+ if (!d->horizontalHeader->model()) {
+ d->horizontalHeader->setModel(d->model);
+ if (d->selectionModel)
+ d->horizontalHeader->setSelectionModel(d->selectionModel);
+ }
+
+ connect(d->horizontalHeader,SIGNAL(sectionResized(int,int,int)),
+ this, SLOT(columnResized(int,int,int)));
+ connect(d->horizontalHeader, SIGNAL(sectionMoved(int,int,int)),
+ this, SLOT(columnMoved(int,int,int)));
+ connect(d->horizontalHeader, SIGNAL(sectionCountChanged(int,int)),
+ this, SLOT(columnCountChanged(int,int)));
+ connect(d->horizontalHeader, SIGNAL(sectionPressed(int)), this, SLOT(selectColumn(int)));
+ connect(d->horizontalHeader, SIGNAL(sectionEntered(int)), this, SLOT(_q_selectColumn(int)));
+ connect(d->horizontalHeader, SIGNAL(sectionHandleDoubleClicked(int)),
+ this, SLOT(resizeColumnToContents(int)));
+ connect(d->horizontalHeader, SIGNAL(geometriesChanged()), this, SLOT(updateGeometries()));
+}
+
+/*!
+ Sets the widget to use for the vertical header to \a header.
+
+ \sa verticalHeader() setHorizontalHeader()
+*/
+void QTableView::setVerticalHeader(QHeaderView *header)
+{
+ Q_D(QTableView);
+
+ if (!header || header == d->verticalHeader)
+ return;
+ if (d->verticalHeader && d->verticalHeader->parent() == this)
+ delete d->verticalHeader;
+ d->verticalHeader = header;
+ d->verticalHeader->setParent(this);
+ if (!d->verticalHeader->model()) {
+ d->verticalHeader->setModel(d->model);
+ if (d->selectionModel)
+ d->verticalHeader->setSelectionModel(d->selectionModel);
+ }
+
+ connect(d->verticalHeader, SIGNAL(sectionResized(int,int,int)),
+ this, SLOT(rowResized(int,int,int)));
+ connect(d->verticalHeader, SIGNAL(sectionMoved(int,int,int)),
+ this, SLOT(rowMoved(int,int,int)));
+ connect(d->verticalHeader, SIGNAL(sectionCountChanged(int,int)),
+ this, SLOT(rowCountChanged(int,int)));
+ connect(d->verticalHeader, SIGNAL(sectionPressed(int)), this, SLOT(selectRow(int)));
+ connect(d->verticalHeader, SIGNAL(sectionEntered(int)), this, SLOT(_q_selectRow(int)));
+ connect(d->verticalHeader, SIGNAL(sectionHandleDoubleClicked(int)),
+ this, SLOT(resizeRowToContents(int)));
+ connect(d->verticalHeader, SIGNAL(geometriesChanged()), this, SLOT(updateGeometries()));
+}
+
+/*!
+ \internal
+
+ Scroll the contents of the table view by (\a dx, \a dy).
+*/
+void QTableView::scrollContentsBy(int dx, int dy)
+{
+ Q_D(QTableView);
+
+ d->delayedAutoScroll.stop(); // auto scroll was canceled by the user scrolling
+
+ dx = isRightToLeft() ? -dx : dx;
+ if (dx) {
+ if (horizontalScrollMode() == QAbstractItemView::ScrollPerItem) {
+ int oldOffset = d->horizontalHeader->offset();
+ if (horizontalScrollBar()->value() == horizontalScrollBar()->maximum())
+ d->horizontalHeader->setOffsetToLastSection();
+ else
+ d->horizontalHeader->setOffsetToSectionPosition(horizontalScrollBar()->value());
+ int newOffset = d->horizontalHeader->offset();
+ dx = isRightToLeft() ? newOffset - oldOffset : oldOffset - newOffset;
+ } else {
+ d->horizontalHeader->setOffset(horizontalScrollBar()->value());
+ }
+ }
+ if (dy) {
+ if (verticalScrollMode() == QAbstractItemView::ScrollPerItem) {
+ int oldOffset = d->verticalHeader->offset();
+ if (verticalScrollBar()->value() == verticalScrollBar()->maximum())
+ d->verticalHeader->setOffsetToLastSection();
+ else
+ d->verticalHeader->setOffsetToSectionPosition(verticalScrollBar()->value());
+ int newOffset = d->verticalHeader->offset();
+ dy = oldOffset - newOffset;
+ } else {
+ d->verticalHeader->setOffset(verticalScrollBar()->value());
+ }
+ }
+ d->scrollContentsBy(dx, dy);
+
+ if (d->showGrid) {
+ //we need to update the first line of the previous top item in the view
+ //because it has the grid drawn if the header is invisible.
+ //It is strictly related to what's done at then end of the paintEvent
+ if (dy > 0 && d->horizontalHeader->isHidden() && d->verticalScrollMode == ScrollPerItem) {
+ d->viewport->update(0, dy, d->viewport->width(), dy);
+ }
+ if (dx > 0 && d->verticalHeader->isHidden() && d->horizontalScrollMode == ScrollPerItem) {
+ d->viewport->update(dx, 0, dx, d->viewport->height());
+ }
+ }
+}
+
+/*!
+ \reimp
+*/
+QStyleOptionViewItem QTableView::viewOptions() const
+{
+ QStyleOptionViewItem option = QAbstractItemView::viewOptions();
+ option.showDecorationSelected = true;
+ return option;
+}
+
+/*!
+ Paints the table on receipt of the given paint event \a event.
+*/
+void QTableView::paintEvent(QPaintEvent *event)
+{
+ Q_D(QTableView);
+ // setup temp variables for the painting
+ QStyleOptionViewItemV4 option = d->viewOptionsV4();
+ const QPoint offset = d->scrollDelayOffset;
+ const bool showGrid = d->showGrid;
+ const int gridSize = showGrid ? 1 : 0;
+ const int gridHint = style()->styleHint(QStyle::SH_Table_GridLineColor, &option, this);
+ const QColor gridColor = static_cast<QRgb>(gridHint);
+ const QPen gridPen = QPen(gridColor, 0, d->gridStyle);
+ const QHeaderView *verticalHeader = d->verticalHeader;
+ const QHeaderView *horizontalHeader = d->horizontalHeader;
+ const QStyle::State state = option.state;
+ const bool alternate = d->alternatingColors;
+ const bool rightToLeft = isRightToLeft();
+
+ QPainter painter(d->viewport);
+
+ // if there's nothing to do, clear the area and return
+ if (horizontalHeader->count() == 0 || verticalHeader->count() == 0 || !d->itemDelegate)
+ return;
+
+ uint x = horizontalHeader->length() - horizontalHeader->offset() - (rightToLeft ? 0 : 1);
+ uint y = verticalHeader->length() - verticalHeader->offset() - 1;
+
+ QVector<QRect> rects = event->region().rects();
+
+ //firstVisualRow is the visual index of the first visible row. lastVisualRow is the visual index of the last visible Row.
+ //same goes for ...VisualColumn
+ int firstVisualRow = qMax(verticalHeader->visualIndexAt(0),0);
+ int lastVisualRow = verticalHeader->visualIndexAt(verticalHeader->viewport()->height());
+ if (lastVisualRow == -1)
+ lastVisualRow = d->model->rowCount(d->root) - 1;
+
+ int firstVisualColumn = horizontalHeader->visualIndexAt(0);
+ int lastVisualColumn = horizontalHeader->visualIndexAt(horizontalHeader->viewport()->width());
+ if (rightToLeft)
+ qSwap(firstVisualColumn, lastVisualColumn);
+ if (firstVisualColumn == -1)
+ firstVisualColumn = 0;
+ if (lastVisualColumn == -1)
+ lastVisualColumn = horizontalHeader->count() - 1;
+
+ QBitArray drawn((lastVisualRow - firstVisualRow + 1) * (lastVisualColumn - firstVisualColumn + 1));
+
+ for (int i = 0; i < rects.size(); ++i) {
+ QRect dirtyArea = rects.at(i);
+ dirtyArea.translate(offset);
+ dirtyArea.setBottom(qMin(dirtyArea.bottom(), int(y)));
+ if (rightToLeft) {
+ dirtyArea.setLeft(qMax(dirtyArea.left(), d->viewport->width() - int(x)));
+ } else {
+ dirtyArea.setRight(qMin(dirtyArea.right(), int(x)));
+ }
+
+ if (d->hasSpans())
+ d->drawAndClipSpans(dirtyArea, &painter, option, &drawn,
+ firstVisualRow, lastVisualRow, firstVisualColumn, lastVisualColumn);
+
+ // get the horizontal start and end visual sections
+ int left = horizontalHeader->visualIndexAt(dirtyArea.left());
+ int right = horizontalHeader->visualIndexAt(dirtyArea.right());
+ if (rightToLeft)
+ qSwap(left, right);
+ if (left == -1) left = 0;
+ if (right == -1) right = horizontalHeader->count() - 1;
+
+ // get the vertical start and end visual sections and if alternate color
+ int bottom = verticalHeader->visualIndexAt(dirtyArea.bottom());
+ if (bottom == -1) bottom = verticalHeader->count() - 1;
+ int top = 0;
+ bool alternateBase = false;
+ if (alternate && verticalHeader->sectionsHidden()) {
+ uint verticalOffset = verticalHeader->offset();
+ int row = verticalHeader->logicalIndex(top);
+ for (int y = 0;
+ ((uint)(y += verticalHeader->sectionSize(top)) <= verticalOffset) && (top < bottom);
+ ++top) {
+ row = verticalHeader->logicalIndex(top);
+ if (alternate && !verticalHeader->isSectionHidden(row))
+ alternateBase = !alternateBase;
+ }
+ } else {
+ top = verticalHeader->visualIndexAt(dirtyArea.top());
+ alternateBase = (top & 1) && alternate;
+ }
+ if (top == -1 || top > bottom)
+ continue;
+
+ // Paint each row item
+ for (int visualRowIndex = top; visualRowIndex <= bottom; ++visualRowIndex) {
+ int row = verticalHeader->logicalIndex(visualRowIndex);
+ if (verticalHeader->isSectionHidden(row))
+ continue;
+ int rowY = rowViewportPosition(row);
+ rowY += offset.y();
+ int rowh = rowHeight(row) - gridSize;
+
+ // Paint each column item
+ for (int visualColumnIndex = left; visualColumnIndex <= right; ++visualColumnIndex) {
+ int currentBit = (visualRowIndex - firstVisualRow) * (lastVisualColumn - firstVisualColumn + 1)
+ + visualColumnIndex - firstVisualColumn;
+
+ if (currentBit < 0 || currentBit >= drawn.size() || drawn.testBit(currentBit))
+ continue;
+ drawn.setBit(currentBit);
+
+ int col = horizontalHeader->logicalIndex(visualColumnIndex);
+ if (horizontalHeader->isSectionHidden(col))
+ continue;
+ int colp = columnViewportPosition(col);
+ colp += offset.x();
+ int colw = columnWidth(col) - gridSize;
+
+ const QModelIndex index = d->model->index(row, col, d->root);
+ if (index.isValid()) {
+ option.rect = QRect(colp + (showGrid && rightToLeft ? 1 : 0), rowY, colw, rowh);
+ if (alternate) {
+ if (alternateBase)
+ option.features |= QStyleOptionViewItemV2::Alternate;
+ else
+ option.features &= ~QStyleOptionViewItemV2::Alternate;
+ }
+ d->drawCell(&painter, option, index);
+ }
+ }
+ alternateBase = !alternateBase && alternate;
+ }
+
+ if (showGrid) {
+ // Find the bottom right (the last rows/coloumns might be hidden)
+ while (verticalHeader->isSectionHidden(verticalHeader->logicalIndex(bottom))) --bottom;
+ QPen old = painter.pen();
+ painter.setPen(gridPen);
+ // Paint each row
+ for (int visualIndex = top; visualIndex <= bottom; ++visualIndex) {
+ int row = verticalHeader->logicalIndex(visualIndex);
+ if (verticalHeader->isSectionHidden(row))
+ continue;
+ int rowY = rowViewportPosition(row);
+ rowY += offset.y();
+ int rowh = rowHeight(row) - gridSize;
+ painter.drawLine(dirtyArea.left(), rowY + rowh, dirtyArea.right(), rowY + rowh);
+ }
+
+ // Paint each column
+ for (int h = left; h <= right; ++h) {
+ int col = horizontalHeader->logicalIndex(h);
+ if (horizontalHeader->isSectionHidden(col))
+ continue;
+ int colp = columnViewportPosition(col);
+ colp += offset.x();
+ if (!rightToLeft)
+ colp += columnWidth(col) - gridSize;
+ painter.drawLine(colp, dirtyArea.top(), colp, dirtyArea.bottom());
+ }
+
+ //draw the top & left grid lines if the headers are not visible.
+ //We do update this line when subsequent scroll happen (see scrollContentsBy)
+ if (horizontalHeader->isHidden() && verticalScrollMode() == ScrollPerItem)
+ painter.drawLine(dirtyArea.left(), 0, dirtyArea.right(), 0);
+ if (verticalHeader->isHidden() && horizontalScrollMode() == ScrollPerItem)
+ painter.drawLine(0, dirtyArea.top(), 0, dirtyArea.bottom());
+ painter.setPen(old);
+ }
+ }
+
+#ifndef QT_NO_DRAGANDDROP
+ // Paint the dropIndicator
+ d->paintDropIndicator(&painter);
+#endif
+}
+
+/*!
+ Returns the index position of the model item corresponding to the
+ table item at position \a pos in contents coordinates.
+*/
+QModelIndex QTableView::indexAt(const QPoint &pos) const
+{
+ Q_D(const QTableView);
+ d->executePostedLayout();
+ int r = rowAt(pos.y());
+ int c = columnAt(pos.x());
+ if (r >= 0 && c >= 0) {
+ if (d->hasSpans()) {
+ QTableViewPrivate::Span span = d->span(r, c);
+ r = span.top();
+ c = span.left();
+ }
+ return d->model->index(r, c, d->root);
+ }
+ return QModelIndex();
+}
+
+/*!
+ Returns the horizontal offset of the items in the table view.
+
+ Note that the table view uses the horizontal header section
+ positions to determine the positions of columns in the view.
+
+ \sa verticalOffset()
+*/
+int QTableView::horizontalOffset() const
+{
+ Q_D(const QTableView);
+ return d->horizontalHeader->offset();
+}
+
+/*!
+ Returns the vertical offset of the items in the table view.
+
+ Note that the table view uses the vertical header section
+ positions to determine the positions of rows in the view.
+
+ \sa horizontalOffset()
+*/
+int QTableView::verticalOffset() const
+{
+ Q_D(const QTableView);
+ return d->verticalHeader->offset();
+}
+
+/*!
+ \fn QModelIndex QTableView::moveCursor(CursorAction cursorAction, Qt::KeyboardModifiers modifiers)
+
+ Moves the cursor in accordance with the given \a cursorAction, using the
+ information provided by the \a modifiers.
+
+ \sa QAbstractItemView::CursorAction
+*/
+QModelIndex QTableView::moveCursor(CursorAction cursorAction, Qt::KeyboardModifiers modifiers)
+{
+ Q_D(QTableView);
+ Q_UNUSED(modifiers);
+
+ int bottom = d->model->rowCount(d->root) - 1;
+ // make sure that bottom is the bottommost *visible* row
+ while (bottom >= 0 && isRowHidden(d->logicalRow(bottom)))
+ --bottom;
+
+ int right = d->model->columnCount(d->root) - 1;
+
+ while (right >= 0 && isColumnHidden(d->logicalColumn(right)))
+ --right;
+
+ if (bottom == -1 || right == -1)
+ return QModelIndex(); // model is empty
+
+ QModelIndex current = currentIndex();
+
+ if (!current.isValid()) {
+ int row = 0;
+ int column = 0;
+ while (column < right && isColumnHidden(d->logicalColumn(column)))
+ ++column;
+ while (isRowHidden(d->logicalRow(row)) && row < bottom)
+ ++row;
+ return d->model->index(d->logicalRow(row), d->logicalColumn(column), d->root);
+ }
+
+ int visualRow = d->visualRow(current.row());
+ Q_ASSERT(visualRow != -1);
+ int visualColumn = d->visualColumn(current.column());
+ Q_ASSERT(visualColumn != -1);
+
+ if (isRightToLeft()) {
+ if (cursorAction == MoveLeft)
+ cursorAction = MoveRight;
+ else if (cursorAction == MoveRight)
+ cursorAction = MoveLeft;
+ }
+
+ switch (cursorAction) {
+ case MoveUp:
+#ifdef QT_KEYPAD_NAVIGATION
+ if (QApplication::keypadNavigationEnabled() && visualRow == 0)
+ visualRow = d->visualRow(model()->rowCount() - 1) + 1;
+#endif
+ --visualRow;
+ while (visualRow > 0 && d->isVisualRowHiddenOrDisabled(visualRow, visualColumn))
+ --visualRow;
+ if (d->hasSpans()) {
+ int row = d->logicalRow(visualRow);
+ QTableViewPrivate::Span span = d->span(row, current.column());
+ visualRow = d->visualRow(span.top());
+ visualColumn = d->visualColumn(span.left());
+ }
+ break;
+ case MoveDown:
+ if (d->hasSpans()) {
+ QTableViewPrivate::Span span = d->span(current.row(), current.column());
+ visualRow = d->visualRow(d->rowSpanEndLogical(span.top(), span.height()));
+ }
+#ifdef QT_KEYPAD_NAVIGATION
+ if (QApplication::keypadNavigationEnabled() && visualRow >= bottom)
+ visualRow = -1;
+#endif
+ ++visualRow;
+ while (visualRow < bottom && d->isVisualRowHiddenOrDisabled(visualRow, visualColumn))
+ ++visualRow;
+ if (d->hasSpans()) {
+ int row = d->logicalRow(visualRow);
+ QTableViewPrivate::Span span = d->span(row, current.column());
+ visualColumn = d->visualColumn(span.left());
+ }
+ break;
+ case MovePrevious: {
+ int left = 0;
+ while (d->isVisualColumnHiddenOrDisabled(visualRow, left) && left < right)
+ ++left;
+ if (visualColumn == left) {
+ visualColumn = right;
+ int top = 0;
+ while (top < bottom && d->isVisualRowHiddenOrDisabled(top, visualColumn))
+ ++top;
+ if (visualRow == top)
+ visualRow = bottom;
+ else
+ --visualRow;
+ while (visualRow > 0 && d->isVisualRowHiddenOrDisabled(visualRow, visualColumn))
+ --visualRow;
+ break;
+ } // else MoveLeft
+ }
+ case MoveLeft:
+ --visualColumn;
+ while (visualColumn > 0 && d->isVisualColumnHiddenOrDisabled(visualRow, visualColumn))
+ --visualColumn;
+ if (d->hasSpans()) {
+ int column = d->logicalColumn(visualColumn);
+ QTableViewPrivate::Span span = d->span(current.row(), column);
+ visualRow = d->visualRow(span.top());
+ visualColumn = d->visualColumn(span.left());
+ }
+ break;
+ case MoveNext:
+ if (visualColumn == right) {
+ visualColumn = 0;
+ while (visualColumn < right && d->isVisualColumnHiddenOrDisabled(visualRow, visualColumn))
+ ++visualColumn;
+ if (visualRow == bottom)
+ visualRow = 0;
+ else
+ ++visualRow;
+ while (visualRow < bottom && d->isVisualRowHiddenOrDisabled(visualRow, visualColumn))
+ ++visualRow;
+ break;
+ } // else MoveRight
+ case MoveRight:
+ if (d->hasSpans()) {
+ QTableViewPrivate::Span span = d->span(current.row(), current.column());
+ visualColumn = d->visualColumn(d->columnSpanEndLogical(span.left(), span.width()));
+ }
+ ++visualColumn;
+ while (visualColumn < right && d->isVisualColumnHiddenOrDisabled(visualRow, visualColumn))
+ ++visualColumn;
+ if (d->hasSpans()) {
+ int column = d->logicalColumn(visualColumn);
+ QTableViewPrivate::Span span = d->span(current.row(), column);
+ visualRow = d->visualRow(span.top());
+ }
+ break;
+ case MoveHome:
+ visualColumn = 0;
+ while (visualColumn < right && d->isVisualColumnHiddenOrDisabled(visualRow, visualColumn))
+ ++visualColumn;
+ if (modifiers & Qt::ControlModifier) {
+ visualRow = 0;
+ while (visualRow < bottom && d->isVisualRowHiddenOrDisabled(visualRow, visualColumn))
+ ++visualRow;
+ }
+ break;
+ case MoveEnd:
+ visualColumn = right;
+ if (modifiers & Qt::ControlModifier)
+ visualRow = bottom;
+ break;
+ case MovePageUp: {
+ int top = 0;
+ while (top < bottom && d->isVisualRowHiddenOrDisabled(top, visualColumn))
+ ++top;
+ int newRow = qMax(rowAt(visualRect(current).top() - d->viewport->height()), top);
+ return d->model->index(qBound(0, newRow, bottom), current.column(), d->root);
+ }
+ case MovePageDown: {
+ int newRow = qMin(rowAt(visualRect(current).bottom() + d->viewport->height()), bottom);
+ if (newRow < 0)
+ newRow = bottom;
+ return d->model->index(qBound(0, newRow, bottom), current.column(), d->root);
+ }}
+
+ int logicalRow = d->logicalRow(visualRow);
+ int logicalColumn = d->logicalColumn(visualColumn);
+ if (!d->model->hasIndex(logicalRow, logicalColumn, d->root))
+ return QModelIndex();
+
+ QModelIndex result = d->model->index(logicalRow, logicalColumn, d->root);
+ if (!isIndexHidden(result) && d->isIndexEnabled(result))
+ return d->model->index(logicalRow, logicalColumn, d->root);
+
+ return QModelIndex();
+}
+
+/*!
+ \fn void QTableView::setSelection(const QRect &rect,
+ QItemSelectionModel::SelectionFlags flags)
+
+ Selects the items within the given \a rect and in accordance with
+ the specified selection \a flags.
+*/
+void QTableView::setSelection(const QRect &rect, QItemSelectionModel::SelectionFlags command)
+{
+ Q_D(QTableView);
+ QModelIndex tl = indexAt(QPoint(isRightToLeft() ? qMax(rect.left(), rect.right())
+ : qMin(rect.left(), rect.right()), qMin(rect.top(), rect.bottom())));
+ QModelIndex br = indexAt(QPoint(isRightToLeft() ? qMin(rect.left(), rect.right()) :
+ qMax(rect.left(), rect.right()), qMax(rect.top(), rect.bottom())));
+ if (!d->selectionModel || !tl.isValid() || !br.isValid() || !d->isIndexEnabled(tl) || !d->isIndexEnabled(br))
+ return;
+
+ bool verticalMoved = verticalHeader()->sectionsMoved();
+ bool horizontalMoved = horizontalHeader()->sectionsMoved();
+
+ QItemSelection selection;
+
+ if (d->hasSpans()) {
+ bool expanded;
+ int top = qMin(d->visualRow(tl.row()), d->visualRow(br.row()));
+ int left = qMin(d->visualColumn(tl.column()), d->visualColumn(br.column()));
+ int bottom = qMax(d->visualRow(tl.row()), d->visualRow(br.row()));
+ int right = qMax(d->visualColumn(tl.column()), d->visualColumn(br.column()));
+ do {
+ expanded = false;
+ QList<QTableViewPrivate::Span>::const_iterator it;
+ for (it = d->spans.constBegin(); it != d->spans.constEnd(); ++it) {
+ QTableViewPrivate::Span span = *it;
+ int t = d->visualRow(span.top());
+ int l = d->visualColumn(span.left());
+ int b = d->visualRow(d->rowSpanEndLogical(span.top(), span.height()));
+ int r = d->visualColumn(d->columnSpanEndLogical(span.left(), span.width()));
+ if ((t > bottom) || (l > right) || (top > b) || (left > r))
+ continue; // no intersect
+ if (t < top) {
+ top = t;
+ expanded = true;
+ }
+ if (l < left) {
+ left = l;
+ expanded = true;
+ }
+ if (b > bottom) {
+ bottom = b;
+ expanded = true;
+ }
+ if (r > right) {
+ right = r;
+ expanded = true;
+ }
+ if (expanded)
+ break;
+ }
+ } while (expanded);
+ for (int horizontal = left; horizontal <= right; ++horizontal) {
+ int column = d->logicalColumn(horizontal);
+ for (int vertical = top; vertical <= bottom; ++vertical) {
+ int row = d->logicalRow(vertical);
+ QModelIndex index = d->model->index(row, column, d->root);
+ selection.append(QItemSelectionRange(index));
+ }
+ }
+ } else if (verticalMoved && horizontalMoved) {
+ int top = d->visualRow(tl.row());
+ int left = d->visualColumn(tl.column());
+ int bottom = d->visualRow(br.row());
+ int right = d->visualColumn(br.column());
+ for (int horizontal = left; horizontal <= right; ++horizontal) {
+ int column = d->logicalColumn(horizontal);
+ for (int vertical = top; vertical <= bottom; ++vertical) {
+ int row = d->logicalRow(vertical);
+ QModelIndex index = d->model->index(row, column, d->root);
+ selection.append(QItemSelectionRange(index));
+ }
+ }
+ } else if (horizontalMoved) {
+ int left = d->visualColumn(tl.column());
+ int right = d->visualColumn(br.column());
+ for (int visual = left; visual <= right; ++visual) {
+ int column = d->logicalColumn(visual);
+ QModelIndex topLeft = d->model->index(tl.row(), column, d->root);
+ QModelIndex bottomRight = d->model->index(br.row(), column, d->root);
+ selection.append(QItemSelectionRange(topLeft, bottomRight));
+ }
+ } else if (verticalMoved) {
+ int top = d->visualRow(tl.row());
+ int bottom = d->visualRow(br.row());
+ for (int visual = top; visual <= bottom; ++visual) {
+ int row = d->logicalRow(visual);
+ QModelIndex topLeft = d->model->index(row, tl.column(), d->root);
+ QModelIndex bottomRight = d->model->index(row, br.column(), d->root);
+ selection.append(QItemSelectionRange(topLeft, bottomRight));
+ }
+ } else { // nothing moved
+ selection.append(QItemSelectionRange(tl, br));
+ }
+
+ d->selectionModel->select(selection, command);
+}
+
+/*!
+ \internal
+
+ Returns the rectangle from the viewport of the items in the given
+ \a selection.
+*/
+QRegion QTableView::visualRegionForSelection(const QItemSelection &selection) const
+{
+ Q_D(const QTableView);
+
+ if (selection.isEmpty())
+ return QRegion();
+
+ QRegion selectionRegion;
+ bool verticalMoved = verticalHeader()->sectionsMoved();
+ bool horizontalMoved = horizontalHeader()->sectionsMoved();
+
+ if ((verticalMoved && horizontalMoved) || d->hasSpans()) {
+ for (int i = 0; i < selection.count(); ++i) {
+ QItemSelectionRange range = selection.at(i);
+ if (range.parent() != d->root || !range.isValid())
+ continue;
+ for (int r = range.top(); r <= range.bottom(); ++r)
+ for (int c = range.left(); c <= range.right(); ++c)
+ selectionRegion += QRegion(visualRect(d->model->index(r, c, d->root)));
+ }
+ } else if (horizontalMoved) {
+ for (int i = 0; i < selection.count(); ++i) {
+ QItemSelectionRange range = selection.at(i);
+ if (range.parent() != d->root || !range.isValid())
+ continue;
+ int top = rowViewportPosition(range.top());
+ int bottom = rowViewportPosition(range.bottom()) + rowHeight(range.bottom());
+ if (top > bottom)
+ qSwap<int>(top, bottom);
+ int height = bottom - top;
+ for (int c = range.left(); c <= range.right(); ++c)
+ selectionRegion += QRegion(QRect(columnViewportPosition(c), top,
+ columnWidth(c), height));
+ }
+ } else if (verticalMoved) {
+ for (int i = 0; i < selection.count(); ++i) {
+ QItemSelectionRange range = selection.at(i);
+ if (range.parent() != d->root || !range.isValid())
+ continue;
+ int left = columnViewportPosition(range.left());
+ int right = columnViewportPosition(range.right()) + columnWidth(range.right());
+ if (left > right)
+ qSwap<int>(left, right);
+ int width = right - left;
+ for (int r = range.top(); r <= range.bottom(); ++r)
+ selectionRegion += QRegion(QRect(left, rowViewportPosition(r),
+ width, rowHeight(r)));
+ }
+ } else { // nothing moved
+ for (int i = 0; i < selection.count(); ++i) {
+ QItemSelectionRange range = selection.at(i);
+ if (range.parent() != d->root || !range.isValid())
+ continue;
+ d->trimHiddenSelections(&range);
+ QRect tl = visualRect(range.topLeft());
+ QRect br = visualRect(range.bottomRight());
+ selectionRegion += QRegion(tl|br);
+ }
+ }
+
+ return selectionRegion;
+}
+
+
+/*!
+ \reimp
+*/
+QModelIndexList QTableView::selectedIndexes() const
+{
+ Q_D(const QTableView);
+ QModelIndexList viewSelected;
+ QModelIndexList modelSelected;
+ if (d->selectionModel)
+ modelSelected = d->selectionModel->selectedIndexes();
+ for (int i = 0; i < modelSelected.count(); ++i) {
+ QModelIndex index = modelSelected.at(i);
+ if (!isIndexHidden(index) && index.parent() == d->root)
+ viewSelected.append(index);
+ }
+ return viewSelected;
+}
+
+
+/*!
+ This slot is called whenever rows are added or deleted. The
+ previous number of rows is specified by \a oldCount, and the new
+ number of rows is specified by \a newCount.
+*/
+void QTableView::rowCountChanged(int /*oldCount*/, int /*newCount*/ )
+{
+ Q_D(QTableView);
+ updateGeometries();
+ if (verticalScrollMode() == QAbstractItemView::ScrollPerItem)
+ d->verticalHeader->setOffsetToSectionPosition(verticalScrollBar()->value());
+ else
+ d->verticalHeader->setOffset(verticalScrollBar()->value());
+ d->viewport->update();
+}
+
+/*!
+ This slot is called whenever columns are added or deleted. The
+ previous number of columns is specified by \a oldCount, and the new
+ number of columns is specified by \a newCount.
+*/
+void QTableView::columnCountChanged(int, int)
+{
+ Q_D(QTableView);
+ updateGeometries();
+ if (horizontalScrollMode() == QAbstractItemView::ScrollPerItem)
+ d->horizontalHeader->setOffsetToSectionPosition(horizontalScrollBar()->value());
+ else
+ d->horizontalHeader->setOffset(horizontalScrollBar()->value());
+ d->viewport->update();
+}
+
+/*!
+ \reimp
+*/
+void QTableView::updateGeometries()
+{
+ Q_D(QTableView);
+ if (d->geometryRecursionBlock)
+ return;
+ d->geometryRecursionBlock = true;
+
+ int width = 0;
+ if (!d->verticalHeader->isHidden()) {
+ width = qMax(d->verticalHeader->minimumWidth(), d->verticalHeader->sizeHint().width());
+ width = qMin(width, d->verticalHeader->maximumWidth());
+ }
+ int height = 0;
+ if (!d->horizontalHeader->isHidden()) {
+ height = qMax(d->horizontalHeader->minimumHeight(), d->horizontalHeader->sizeHint().height());
+ height = qMin(height, d->horizontalHeader->maximumHeight());
+ }
+ bool reverse = isRightToLeft();
+ if (reverse)
+ setViewportMargins(0, height, width, 0);
+ else
+ setViewportMargins(width, height, 0, 0);
+
+ // update headers
+
+ QRect vg = d->viewport->geometry();
+
+ int verticalLeft = reverse ? vg.right() + 1 : (vg.left() - width);
+ d->verticalHeader->setGeometry(verticalLeft, vg.top(), width, vg.height());
+ if (d->verticalHeader->isHidden())
+ QMetaObject::invokeMethod(d->verticalHeader, "updateGeometries");
+
+ int horizontalTop = vg.top() - height;
+ d->horizontalHeader->setGeometry(vg.left(), horizontalTop, vg.width(), height);
+ if (d->horizontalHeader->isHidden())
+ QMetaObject::invokeMethod(d->horizontalHeader, "updateGeometries");
+
+ // update cornerWidget
+ if (d->horizontalHeader->isHidden() || d->verticalHeader->isHidden()) {
+ d->cornerWidget->setHidden(true);
+ } else {
+ d->cornerWidget->setHidden(false);
+ d->cornerWidget->setGeometry(verticalLeft, horizontalTop, width, height);
+ }
+
+ // update scroll bars
+
+ // ### move this block into the if
+ QSize vsize = d->viewport->size();
+ QSize max = maximumViewportSize();
+ uint horizontalLength = d->horizontalHeader->length();
+ uint verticalLength = d->verticalHeader->length();
+ if ((uint)max.width() >= horizontalLength && (uint)max.height() >= verticalLength)
+ vsize = max;
+
+ // horizontal scroll bar
+ const int columnCount = d->horizontalHeader->count();
+ const int viewportWidth = vsize.width();
+ int columnsInViewport = 0;
+ for (int width = 0, column = columnCount - 1; column >= 0; --column) {
+ int logical = d->horizontalHeader->logicalIndex(column);
+ if (!d->horizontalHeader->isSectionHidden(logical)) {
+ width += d->horizontalHeader->sectionSize(logical);
+ if (width > viewportWidth)
+ break;
+ ++columnsInViewport;
+ }
+ }
+ if (horizontalScrollMode() == QAbstractItemView::ScrollPerItem) {
+ const int visibleColumns = columnCount - d->horizontalHeader->hiddenSectionCount();
+ horizontalScrollBar()->setRange(0, visibleColumns - columnsInViewport);
+ horizontalScrollBar()->setPageStep(columnsInViewport);
+ if (columnsInViewport >= visibleColumns)
+ d->horizontalHeader->setOffset(0);
+ horizontalScrollBar()->setSingleStep(1);
+ } else { // ScrollPerPixel
+ horizontalScrollBar()->setPageStep(vsize.width());
+ horizontalScrollBar()->setRange(0, horizontalLength - vsize.width());
+ horizontalScrollBar()->setSingleStep(qMax(vsize.width() / (columnsInViewport + 1), 2));
+ }
+
+ // vertical scroll bar
+ const int rowCount = d->verticalHeader->count();
+ const int viewportHeight = vsize.height();
+ int rowsInViewport = 0;
+ for (int height = 0, row = rowCount - 1; row >= 0; --row) {
+ int logical = d->verticalHeader->logicalIndex(row);
+ if (!d->verticalHeader->isSectionHidden(logical)) {
+ height += d->verticalHeader->sectionSize(logical);
+ if (height > viewportHeight)
+ break;
+ ++rowsInViewport;
+ }
+ }
+ if (verticalScrollMode() == QAbstractItemView::ScrollPerItem) {
+ const int visibleRows = rowCount - d->verticalHeader->hiddenSectionCount();
+ verticalScrollBar()->setRange(0, visibleRows - rowsInViewport);
+ verticalScrollBar()->setPageStep(rowsInViewport);
+ if (rowsInViewport >= visibleRows)
+ d->verticalHeader->setOffset(0);
+ verticalScrollBar()->setSingleStep(1);
+ } else { // ScrollPerPixel
+ verticalScrollBar()->setPageStep(vsize.height());
+ verticalScrollBar()->setRange(0, verticalLength - vsize.height());
+ verticalScrollBar()->setSingleStep(qMax(vsize.height() / (rowsInViewport + 1), 2));
+ }
+
+ d->geometryRecursionBlock = false;
+ QAbstractItemView::updateGeometries();
+}
+
+/*!
+ Returns the size hint for the given \a row's height or -1 if there
+ is no model.
+
+ If you need to set the height of a given row to a fixed value, call
+ QHeaderView::resizeSection() on the table's vertical header.
+
+ If you reimplement this function in a subclass, note that the value you
+ return is only used when resizeRowToContents() is called. In that case,
+ if a larger row height is required by either the vertical header or
+ the item delegate, that width will be used instead.
+
+ \sa QWidget::sizeHint, verticalHeader()
+*/
+int QTableView::sizeHintForRow(int row) const
+{
+ Q_D(const QTableView);
+
+ if (!model())
+ return -1;
+
+ int left = qMax(0, columnAt(0));
+ int right = columnAt(d->viewport->width());
+ if (right == -1) // the table don't have enough columns to fill the viewport
+ right = d->model->columnCount(d->root) - 1;
+
+ QStyleOptionViewItemV4 option = d->viewOptionsV4();
+
+ int hint = 0;
+ QModelIndex index;
+ for (int column = left; column <= right; ++column) {
+ int logicalColumn = d->horizontalHeader->logicalIndex(column);
+ if (d->horizontalHeader->isSectionHidden(logicalColumn))
+ continue;
+ index = d->model->index(row, logicalColumn, d->root);
+ if (d->wrapItemText) {// for wrapping boundaries
+ option.rect.setY(rowViewportPosition(index.row()));
+ option.rect.setHeight(rowHeight(index.row()));
+ option.rect.setX(columnViewportPosition(index.column()));
+ option.rect.setWidth(columnWidth(index.column()));
+ }
+
+ QWidget *editor = d->editorForIndex(index).editor;
+ if (editor && d->persistent.contains(editor)) {
+ hint = qMax(hint, editor->sizeHint().height());
+ int min = editor->minimumSize().height();
+ int max = editor->maximumSize().height();
+ hint = qBound(min, hint, max);
+ }
+
+ hint = qMax(hint, itemDelegate(index)->sizeHint(option, index).height());
+ }
+
+ return d->showGrid ? hint + 1 : hint;
+}
+
+/*!
+ Returns the size hint for the given \a column's width or -1 if
+ there is no model.
+
+ If you need to set the width of a given column to a fixed value, call
+ QHeaderView::resizeSection() on the table's horizontal header.
+
+ If you reimplement this function in a subclass, note that the value you
+ return will be used when resizeColumnToContents() or
+ QHeaderView::resizeSections() is called. If a larger column width is
+ required by either the horizontal header or the item delegate, the larger
+ width will be used instead.
+
+ \sa QWidget::sizeHint, horizontalHeader()
+*/
+int QTableView::sizeHintForColumn(int column) const
+{
+ Q_D(const QTableView);
+
+ if (!model())
+ return -1;
+
+ int top = qMax(0, rowAt(0));
+ int bottom = rowAt(d->viewport->height());
+ if (!isVisible() || bottom == -1) // the table don't have enough rows to fill the viewport
+ bottom = d->model->rowCount(d->root) - 1;
+
+ QStyleOptionViewItemV4 option = d->viewOptionsV4();
+
+ int hint = 0;
+ QModelIndex index;
+ for (int row = top; row <= bottom; ++row) {
+ int logicalRow = d->verticalHeader->logicalIndex(row);
+ if (d->verticalHeader->isSectionHidden(logicalRow))
+ continue;
+ index = d->model->index(logicalRow, column, d->root);
+
+ QWidget *editor = d->editorForIndex(index).editor;
+ if (editor && d->persistent.contains(editor)) {
+ hint = qMax(hint, editor->sizeHint().width());
+ int min = editor->minimumSize().width();
+ int max = editor->maximumSize().width();
+ hint = qBound(min, hint, max);
+ }
+
+ hint = qMax(hint, itemDelegate(index)->sizeHint(option, index).width());
+ }
+
+ return d->showGrid ? hint + 1 : hint;
+}
+
+/*!
+ Returns the y-coordinate in contents coordinates of the given \a
+ row.
+*/
+int QTableView::rowViewportPosition(int row) const
+{
+ Q_D(const QTableView);
+ return d->verticalHeader->sectionViewportPosition(row);
+}
+
+/*!
+ Returns the row in which the given y-coordinate, \a y, in contents
+ coordinates is located.
+
+ \note This function returns -1 if the given coordinate is not valid
+ (has no row).
+
+ \sa columnAt()
+*/
+int QTableView::rowAt(int y) const
+{
+ Q_D(const QTableView);
+ return d->verticalHeader->logicalIndexAt(y);
+}
+
+/*!
+ \since 4.1
+
+ Sets the height of the given \a row to be \a height.
+*/
+void QTableView::setRowHeight(int row, int height)
+{
+ Q_D(const QTableView);
+ d->verticalHeader->resizeSection(row, height);
+}
+
+/*!
+ Returns the height of the given \a row.
+
+ \sa resizeRowToContents(), columnWidth()
+*/
+int QTableView::rowHeight(int row) const
+{
+ Q_D(const QTableView);
+ return d->verticalHeader->sectionSize(row);
+}
+
+/*!
+ Returns the x-coordinate in contents coordinates of the given \a
+ column.
+*/
+int QTableView::columnViewportPosition(int column) const
+{
+ Q_D(const QTableView);
+ return d->horizontalHeader->sectionViewportPosition(column);
+}
+
+/*!
+ Returns the column in which the given x-coordinate, \a x, in contents
+ coordinates is located.
+
+ \note This function returns -1 if the given coordinate is not valid
+ (has no column).
+
+ \sa rowAt()
+*/
+int QTableView::columnAt(int x) const
+{
+ Q_D(const QTableView);
+ return d->horizontalHeader->logicalIndexAt(x);
+}
+
+/*!
+ \since 4.1
+
+ Sets the width of the given \a column to be \a width.
+*/
+void QTableView::setColumnWidth(int column, int width)
+{
+ Q_D(const QTableView);
+ d->horizontalHeader->resizeSection(column, width);
+}
+
+/*!
+ Returns the width of the given \a column.
+
+ \sa resizeColumnToContents(), rowHeight()
+*/
+int QTableView::columnWidth(int column) const
+{
+ Q_D(const QTableView);
+ return d->horizontalHeader->sectionSize(column);
+}
+
+/*!
+ Returns true if the given \a row is hidden; otherwise returns false.
+
+ \sa isColumnHidden()
+*/
+bool QTableView::isRowHidden(int row) const
+{
+ Q_D(const QTableView);
+ return d->verticalHeader->isSectionHidden(row);
+}
+
+/*!
+ If \a hide is true \a row will be hidden, otherwise it will be shown.
+
+ \sa setColumnHidden()
+*/
+void QTableView::setRowHidden(int row, bool hide)
+{
+ Q_D(QTableView);
+ if (row < 0 || row >= d->verticalHeader->count())
+ return;
+ d->verticalHeader->setSectionHidden(row, hide);
+}
+
+/*!
+ Returns true if the given \a column is hidden; otherwise returns false.
+
+ \sa isRowHidden()
+*/
+bool QTableView::isColumnHidden(int column) const
+{
+ Q_D(const QTableView);
+ return d->horizontalHeader->isSectionHidden(column);
+}
+
+/*!
+ If \a hide is true the given \a column will be hidden; otherwise it
+ will be shown.
+
+ \sa setRowHidden()
+*/
+void QTableView::setColumnHidden(int column, bool hide)
+{
+ Q_D(QTableView);
+ if (column < 0 || column >= d->horizontalHeader->count())
+ return;
+ d->horizontalHeader->setSectionHidden(column, hide);
+}
+
+/*!
+ \since 4.2
+ \property QTableView::sortingEnabled
+ \brief whether sorting is enabled
+
+ If this property is true, sorting is enabled for the table; if the
+ property is false, sorting is not enabled. The default value is false.
+
+ \sa sortByColumn()
+*/
+
+void QTableView::setSortingEnabled(bool enable)
+{
+ Q_D(QTableView);
+ d->sortingEnabled = enable;
+ horizontalHeader()->setSortIndicatorShown(enable);
+ if (enable) {
+ disconnect(d->horizontalHeader, SIGNAL(sectionEntered(int)),
+ this, SLOT(_q_selectColumn(int)));
+ disconnect(horizontalHeader(), SIGNAL(sectionPressed(int)),
+ this, SLOT(selectColumn(int)));
+ connect(horizontalHeader(), SIGNAL(sortIndicatorChanged(int,Qt::SortOrder)),
+ this, SLOT(sortByColumn(int)));
+ sortByColumn(horizontalHeader()->sortIndicatorSection(),
+ horizontalHeader()->sortIndicatorOrder());
+ } else {
+ connect(d->horizontalHeader, SIGNAL(sectionEntered(int)),
+ this, SLOT(_q_selectColumn(int)));
+ connect(horizontalHeader(), SIGNAL(sectionPressed(int)),
+ this, SLOT(selectColumn(int)));
+ disconnect(horizontalHeader(), SIGNAL(sortIndicatorChanged(int,Qt::SortOrder)),
+ this, SLOT(sortByColumn(int)));
+ }
+}
+
+bool QTableView::isSortingEnabled() const
+{
+ Q_D(const QTableView);
+ return d->sortingEnabled;
+}
+
+/*!
+ \property QTableView::showGrid
+ \brief whether the grid is shown
+
+ If this property is true a grid is drawn for the table; if the
+ property is false, no grid is drawn. The default value is true.
+*/
+bool QTableView::showGrid() const
+{
+ Q_D(const QTableView);
+ return d->showGrid;
+}
+
+void QTableView::setShowGrid(bool show)
+{
+ Q_D(QTableView);
+ if (d->showGrid != show) {
+ d->showGrid = show;
+ d->viewport->update();
+ }
+}
+
+/*!
+ \property QTableView::gridStyle
+ \brief the pen style used to draw the grid.
+
+ This property holds the style used when drawing the grid (see \l{showGrid}).
+*/
+Qt::PenStyle QTableView::gridStyle() const
+{
+ Q_D(const QTableView);
+ return d->gridStyle;
+}
+
+void QTableView::setGridStyle(Qt::PenStyle style)
+{
+ Q_D(QTableView);
+ if (d->gridStyle != style) {
+ d->gridStyle = style;
+ d->viewport->update();
+ }
+}
+
+/*!
+ \property QTableView::wordWrap
+ \brief the item text word-wrapping policy
+ \since 4.3
+
+ If this property is true then the item text is wrapped where
+ necessary at word-breaks; otherwise it is not wrapped at all.
+ This property is true by default.
+
+ Note that even of wrapping is enabled, the cell will not be
+ expanded to fit all text. Ellipsis will be inserted according to
+ the current \l{QAbstractItemView::}{textElideMode}.
+
+*/
+void QTableView::setWordWrap(bool on)
+{
+ Q_D(QTableView);
+ if (d->wrapItemText == on)
+ return;
+ d->wrapItemText = on;
+ QMetaObject::invokeMethod(d->verticalHeader, "resizeSections");
+ QMetaObject::invokeMethod(d->horizontalHeader, "resizeSections");
+}
+
+bool QTableView::wordWrap() const
+{
+ Q_D(const QTableView);
+ return d->wrapItemText;
+}
+
+/*!
+ \property QTableView::cornerButtonEnabled
+ \brief whether the button in the top-left corner is enabled
+ \since 4.3
+
+ If this property is true then button in the top-left corner
+ of the table view is enabled. Clicking on this button will
+ select all the cells in the table view.
+
+ This property is true by default.
+*/
+void QTableView::setCornerButtonEnabled(bool enable)
+{
+ Q_D(QTableView);
+ d->cornerWidget->setEnabled(enable);
+}
+
+bool QTableView::isCornerButtonEnabled() const
+{
+ Q_D(const QTableView);
+ return d->cornerWidget->isEnabled();
+}
+
+/*!
+ \internal
+
+ Returns the rectangle on the viewport occupied by the given \a
+ index.
+ If the index is hidden in the view it will return a null QRect.
+*/
+QRect QTableView::visualRect(const QModelIndex &index) const
+{
+ Q_D(const QTableView);
+ if (!d->isIndexValid(index) || index.parent() != d->root || isIndexHidden(index) )
+ return QRect();
+
+ d->executePostedLayout();
+
+ if (d->hasSpans()) {
+ QTableViewPrivate::Span span = d->span(index.row(), index.column());
+ return d->visualSpanRect(span);
+ }
+
+ int rowp = rowViewportPosition(index.row());
+ int rowh = rowHeight(index.row());
+ int colp = columnViewportPosition(index.column());
+ int colw = columnWidth(index.column());
+
+ const int i = showGrid() ? 1 : 0;
+ return QRect(colp, rowp, colw - i, rowh - i);
+}
+
+/*!
+ \internal
+
+ Makes sure that the given \a item is visible in the table view,
+ scrolling if necessary.
+*/
+void QTableView::scrollTo(const QModelIndex &index, ScrollHint hint)
+{
+ Q_D(QTableView);
+
+ // check if we really need to do anything
+ if (!d->isIndexValid(index)
+ || (d->model->parent(index) != d->root)
+ || isIndexHidden(index))
+ return;
+
+ QTableViewPrivate::Span span;
+ if (d->hasSpans())
+ span = d->span(index.row(), index.column());
+
+ // Adjust horizontal position
+
+ int viewportWidth = d->viewport->width();
+ int horizontalOffset = d->horizontalHeader->offset();
+ int horizontalPosition = d->horizontalHeader->sectionPosition(index.column());
+ int horizontalIndex = d->horizontalHeader->visualIndex(index.column());
+ int cellWidth = d->hasSpans()
+ ? d->columnSpanWidth(index.column(), span.width())
+ : d->horizontalHeader->sectionSize(index.column());
+
+ if (horizontalScrollMode() == QAbstractItemView::ScrollPerItem) {
+
+ bool positionAtLeft = (horizontalPosition - horizontalOffset < 0);
+ bool positionAtRight = (horizontalPosition - horizontalOffset + cellWidth > viewportWidth);
+
+ if (hint == PositionAtCenter || positionAtRight) {
+ int w = (hint == PositionAtCenter ? viewportWidth / 2 : viewportWidth);
+ int x = cellWidth;
+ while (horizontalIndex > 0) {
+ x += columnWidth(d->horizontalHeader->logicalIndex(horizontalIndex-1));
+ if (x > w)
+ break;
+ --horizontalIndex;
+ }
+ }
+
+ if (positionAtRight || hint == PositionAtCenter || positionAtLeft) {
+ int hiddenSections = 0;
+ if (d->horizontalHeader->sectionsHidden()) {
+ for (int s = horizontalIndex; s >= 0; --s) {
+ int column = d->horizontalHeader->logicalIndex(s);
+ if (d->horizontalHeader->isSectionHidden(column))
+ ++hiddenSections;
+ }
+ }
+ horizontalScrollBar()->setValue(horizontalIndex - hiddenSections);
+ }
+
+ } else { // ScrollPerPixel
+ if (hint == PositionAtCenter) {
+ horizontalScrollBar()->setValue(horizontalPosition - ((viewportWidth - cellWidth) / 2));
+ } else {
+ if (horizontalPosition - horizontalOffset < 0 || cellWidth > viewportWidth)
+ horizontalScrollBar()->setValue(horizontalPosition);
+ else if (horizontalPosition - horizontalOffset + cellWidth > viewportWidth)
+ horizontalScrollBar()->setValue(horizontalPosition - viewportWidth + cellWidth);
+ }
+ }
+
+ // Adjust vertical position
+
+ int viewportHeight = d->viewport->height();
+ int verticalOffset = d->verticalHeader->offset();
+ int verticalPosition = d->verticalHeader->sectionPosition(index.row());
+ int verticalIndex = d->verticalHeader->visualIndex(index.row());
+ int cellHeight = d->hasSpans()
+ ? d->rowSpanHeight(index.row(), span.height())
+ : d->verticalHeader->sectionSize(index.row());
+
+ if (verticalPosition - verticalOffset < 0 || cellHeight > viewportHeight) {
+ if (hint == EnsureVisible)
+ hint = PositionAtTop;
+ } else if (verticalPosition - verticalOffset + cellHeight > viewportHeight) {
+ if (hint == EnsureVisible)
+ hint = PositionAtBottom;
+ }
+
+ if (verticalScrollMode() == QAbstractItemView::ScrollPerItem) {
+
+ if (hint == PositionAtBottom || hint == PositionAtCenter) {
+ int h = (hint == PositionAtCenter ? viewportHeight / 2 : viewportHeight);
+ int y = cellHeight;
+ while (verticalIndex > 0) {
+ int row = d->verticalHeader->logicalIndex(verticalIndex - 1);
+ y += d->verticalHeader->sectionSize(row);
+ if (y > h)
+ break;
+ --verticalIndex;
+ }
+ }
+
+ if (hint == PositionAtBottom || hint == PositionAtCenter || hint == PositionAtTop) {
+ int hiddenSections = 0;
+ if (d->verticalHeader->sectionsHidden()) {
+ for (int s = verticalIndex; s >= 0; --s) {
+ int row = d->verticalHeader->logicalIndex(s);
+ if (d->verticalHeader->isSectionHidden(row))
+ ++hiddenSections;
+ }
+ }
+ verticalScrollBar()->setValue(verticalIndex - hiddenSections);
+ }
+
+ } else { // ScrollPerPixel
+ if (hint == PositionAtTop) {
+ verticalScrollBar()->setValue(verticalPosition);
+ } else if (hint == PositionAtBottom) {
+ verticalScrollBar()->setValue(verticalPosition - viewportHeight + cellHeight);
+ } else if (hint == PositionAtCenter) {
+ verticalScrollBar()->setValue(verticalPosition - ((viewportHeight - cellHeight) / 2));
+ }
+ }
+
+ d->setDirtyRegion(visualRect(index));
+}
+
+/*!
+ This slot is called to change the height of the given \a row. The
+ old height is specified by \a oldHeight, and the new height by \a
+ newHeight.
+
+ \sa columnResized()
+*/
+void QTableView::rowResized(int row, int, int)
+{
+ Q_D(QTableView);
+ d->rowsToUpdate.append(row);
+ if (d->rowResizeTimerID == 0)
+ d->rowResizeTimerID = startTimer(0);
+}
+
+/*!
+ This slot is called to change the width of the given \a column.
+ The old width is specified by \a oldWidth, and the new width by \a
+ newWidth.
+
+ \sa rowResized()
+*/
+void QTableView::columnResized(int column, int, int)
+{
+ Q_D(QTableView);
+ d->columnsToUpdate.append(column);
+ if (d->columnResizeTimerID == 0)
+ d->columnResizeTimerID = startTimer(0);
+}
+
+/*!
+ \reimp
+ */
+void QTableView::timerEvent(QTimerEvent *event)
+{
+ Q_D(QTableView);
+
+ if (event->timerId() == d->columnResizeTimerID) {
+ updateGeometries();
+ killTimer(d->columnResizeTimerID);
+ d->columnResizeTimerID = 0;
+
+ QRect rect;
+ int viewportHeight = d->viewport->height();
+ int viewportWidth = d->viewport->width();
+ if (d->hasSpans() && d->spansIntersectColumns(d->columnsToUpdate)) {
+ rect = QRect(0, 0, viewportWidth, viewportHeight);
+ } else {
+ for (int i = d->columnsToUpdate.size()-1; i >= 0; --i) {
+ int column = d->columnsToUpdate.at(i);
+ int x = columnViewportPosition(column);
+ if (isRightToLeft())
+ rect |= QRect(0, 0, x + columnWidth(column), viewportHeight);
+ else
+ rect |= QRect(x, 0, viewportWidth - x, viewportHeight);
+ }
+ }
+
+ d->viewport->update(rect.normalized());
+ d->columnsToUpdate.clear();
+ }
+
+ if (event->timerId() == d->rowResizeTimerID) {
+ updateGeometries();
+ killTimer(d->rowResizeTimerID);
+ d->rowResizeTimerID = 0;
+
+ int viewportHeight = d->viewport->height();
+ int viewportWidth = d->viewport->width();
+ int top;
+ if (d->hasSpans() && d->spansIntersectRows(d->rowsToUpdate)) {
+ top = 0;
+ } else {
+ top = viewportHeight;
+ for (int i = d->rowsToUpdate.size()-1; i >= 0; --i) {
+ int y = rowViewportPosition(d->rowsToUpdate.at(i));
+ top = qMin(top, y);
+ }
+ }
+
+ d->viewport->update(QRect(0, top, viewportWidth, viewportHeight - top));
+ d->rowsToUpdate.clear();
+ }
+
+ QAbstractItemView::timerEvent(event);
+}
+
+/*!
+ This slot is called to change the index of the given \a row in the
+ table view. The old index is specified by \a oldIndex, and the new
+ index by \a newIndex.
+
+ \sa columnMoved()
+*/
+void QTableView::rowMoved(int, int oldIndex, int newIndex)
+{
+ Q_D(QTableView);
+
+ updateGeometries();
+ int logicalOldIndex = d->verticalHeader->logicalIndex(oldIndex);
+ int logicalNewIndex = d->verticalHeader->logicalIndex(newIndex);
+ if (d->hasSpans() && (d->spansIntersectRow(logicalOldIndex) || d->spansIntersectRow(logicalNewIndex))) {
+ d->viewport->update();
+ } else {
+ int oldTop = rowViewportPosition(logicalOldIndex);
+ int newTop = rowViewportPosition(logicalNewIndex);
+ int oldBottom = oldTop + rowHeight(logicalOldIndex);
+ int newBottom = newTop + rowHeight(logicalNewIndex);
+ int top = qMin(oldTop, newTop);
+ int bottom = qMax(oldBottom, newBottom);
+ int height = bottom - top;
+ d->viewport->update(0, top, d->viewport->width(), height);
+ }
+}
+
+/*!
+ This slot is called to change the index of the given \a column in
+ the table view. The old index is specified by \a oldIndex, and
+ the new index by \a newIndex.
+
+ \sa rowMoved()
+*/
+void QTableView::columnMoved(int, int oldIndex, int newIndex)
+{
+ Q_D(QTableView);
+
+ updateGeometries();
+ int logicalOldIndex = d->horizontalHeader->logicalIndex(oldIndex);
+ int logicalNewIndex = d->horizontalHeader->logicalIndex(newIndex);
+ if (d->hasSpans() && (d->spansIntersectColumn(logicalOldIndex) || d->spansIntersectColumn(logicalNewIndex))) {
+ d->viewport->update();
+ } else {
+ int oldLeft = columnViewportPosition(logicalOldIndex);
+ int newLeft = columnViewportPosition(logicalNewIndex);
+ int oldRight = oldLeft + columnWidth(logicalOldIndex);
+ int newRight = newLeft + columnWidth(logicalNewIndex);
+ int left = qMin(oldLeft, newLeft);
+ int right = qMax(oldRight, newRight);
+ int width = right - left;
+ d->viewport->update(left, 0, width, d->viewport->height());
+ }
+}
+
+/*!
+ Selects the given \a row in the table view if the current
+ SelectionMode and SelectionBehavior allows rows to be selected.
+
+ \sa selectColumn()
+*/
+void QTableView::selectRow(int row)
+{
+ Q_D(QTableView);
+ d->selectRow(row, true);
+}
+
+/*!
+ Selects the given \a column in the table view if the current
+ SelectionMode and SelectionBehavior allows columns to be selected.
+
+ \sa selectRow()
+*/
+void QTableView::selectColumn(int column)
+{
+ Q_D(QTableView);
+ d->selectColumn(column, true);
+}
+
+/*!
+ Hide the given \a row.
+
+ \sa showRow() hideColumn()
+*/
+void QTableView::hideRow(int row)
+{
+ Q_D(QTableView);
+ d->verticalHeader->hideSection(row);
+}
+
+/*!
+ Hide the given \a column.
+
+ \sa showColumn() hideRow()
+*/
+void QTableView::hideColumn(int column)
+{
+ Q_D(QTableView);
+ d->horizontalHeader->hideSection(column);
+}
+
+/*!
+ Show the given \a row.
+
+ \sa hideRow() showColumn()
+*/
+void QTableView::showRow(int row)
+{
+ Q_D(QTableView);
+ d->verticalHeader->showSection(row);
+}
+
+/*!
+ Show the given \a column.
+
+ \sa hideColumn() showRow()
+*/
+void QTableView::showColumn(int column)
+{
+ Q_D(QTableView);
+ d->horizontalHeader->showSection(column);
+}
+
+/*!
+ Resizes the given \a row based on the size hints of the delegate
+ used to render each item in the row.
+*/
+void QTableView::resizeRowToContents(int row)
+{
+ Q_D(QTableView);
+ int content = sizeHintForRow(row);
+ int header = d->verticalHeader->sectionSizeHint(row);
+ d->verticalHeader->resizeSection(row, qMax(content, header));
+}
+
+/*!
+ Resizes all rows based on the size hints of the delegate
+ used to render each item in the rows.
+*/
+void QTableView::resizeRowsToContents()
+{
+ Q_D(QTableView);
+ d->verticalHeader->resizeSections(QHeaderView::ResizeToContents);
+}
+
+/*!
+ Resizes the given \a column based on the size hints of the delegate
+ used to render each item in the column.
+
+ \note Only visible columns will be resized. Reimplement sizeHintForColumn()
+ to resize hidden columns as well.
+*/
+void QTableView::resizeColumnToContents(int column)
+{
+ Q_D(QTableView);
+ int content = sizeHintForColumn(column);
+ int header = d->horizontalHeader->sectionSizeHint(column);
+ d->horizontalHeader->resizeSection(column, qMax(content, header));
+}
+
+/*!
+ Resizes all columns based on the size hints of the delegate
+ used to render each item in the columns.
+*/
+void QTableView::resizeColumnsToContents()
+{
+ Q_D(QTableView);
+ d->horizontalHeader->resizeSections(QHeaderView::ResizeToContents);
+}
+
+/*!
+ \obsolete
+ \overload
+
+ Sorts the model by the values in the given \a column.
+*/
+void QTableView::sortByColumn(int column)
+{
+ Q_D(QTableView);
+ if (column == -1)
+ return;
+ d->model->sort(column, d->horizontalHeader->sortIndicatorOrder());
+}
+
+/*!
+ \since 4.2
+
+ Sorts the model by the values in the given \a column in the given \a order.
+
+ \sa sortingEnabled
+ */
+void QTableView::sortByColumn(int column, Qt::SortOrder order)
+{
+ Q_D(QTableView);
+ d->horizontalHeader->setSortIndicator(column, order);
+ sortByColumn(column);
+}
+
+/*!
+ \internal
+*/
+void QTableView::verticalScrollbarAction(int action)
+{
+ QAbstractItemView::verticalScrollbarAction(action);
+}
+
+/*!
+ \internal
+*/
+void QTableView::horizontalScrollbarAction(int action)
+{
+ QAbstractItemView::horizontalScrollbarAction(action);
+}
+
+/*!
+ \reimp
+*/
+bool QTableView::isIndexHidden(const QModelIndex &index) const
+{
+ Q_D(const QTableView);
+ Q_ASSERT(d->isIndexValid(index));
+ if (isRowHidden(index.row()) || isColumnHidden(index.column()))
+ return true;
+ if (d->hasSpans()) {
+ QTableViewPrivate::Span span = d->span(index.row(), index.column());
+ return !((span.top() == index.row()) && (span.left() == index.column()));
+ }
+ return false;
+}
+
+/*!
+ \fn void QTableView::setSpan(int row, int column, int rowSpanCount, int columnSpanCount)
+ \since 4.2
+
+ Sets the span of the table element at (\a row, \a column) to the number of
+ rows and columns specified by (\a rowSpanCount, \a columnSpanCount).
+
+ \sa rowSpan(), columnSpan()
+*/
+void QTableView::setSpan(int row, int column, int rowSpan, int columnSpan)
+{
+ Q_D(QTableView);
+ if (row < 0 || column < 0 || rowSpan < 0 || columnSpan < 0)
+ return;
+ d->setSpan(row, column, rowSpan, columnSpan);
+ d->viewport->update();
+}
+
+/*!
+ \since 4.2
+
+ Returns the row span of the table element at (\a row, \a column).
+ The default is 1.
+
+ \sa setSpan(), columnSpan()
+*/
+int QTableView::rowSpan(int row, int column) const
+{
+ Q_D(const QTableView);
+ return d->rowSpan(row, column);
+}
+
+/*!
+ \since 4.2
+
+ Returns the column span of the table element at (\a row, \a
+ column). The default is 1.
+
+ \sa setSpan(), rowSpan()
+*/
+int QTableView::columnSpan(int row, int column) const
+{
+ Q_D(const QTableView);
+ return d->columnSpan(row, column);
+}
+
+/*!
+ \since 4.4
+
+ Removes all row and column spans in the table view.
+
+ \sa setSpan()
+*/
+
+void QTableView::clearSpans()
+{
+ Q_D(QTableView);
+ d->spans.clear();
+ d->viewport->update();
+}
+
+void QTableViewPrivate::_q_selectRow(int row)
+{
+ selectRow(row, false);
+}
+
+void QTableViewPrivate::_q_selectColumn(int column)
+{
+ selectColumn(column, false);
+}
+
+void QTableViewPrivate::selectRow(int row, bool anchor)
+{
+ Q_Q(QTableView);
+
+ if (q->selectionBehavior() == QTableView::SelectColumns
+ || (q->selectionMode() == QTableView::SingleSelection
+ && q->selectionBehavior() == QTableView::SelectItems))
+ return;
+
+ if (row >= 0 && row < model->rowCount(root)) {
+ int column = horizontalHeader->logicalIndexAt(q->isRightToLeft() ? viewport->width() : 0);
+ QModelIndex index = model->index(row, column, root);
+ QItemSelectionModel::SelectionFlags command = q->selectionCommand(index);
+ selectionModel->setCurrentIndex(index, QItemSelectionModel::NoUpdate);
+ if ((!(command & QItemSelectionModel::Current) && anchor)
+ || (q->selectionMode() == QTableView::SingleSelection))
+ rowSectionAnchor = row;
+ QModelIndex tl = model->index(qMin(rowSectionAnchor, row), 0, root);
+ QModelIndex br = model->index(qMax(rowSectionAnchor, row), model->columnCount(root) - 1, root);
+ if (verticalHeader->sectionsMoved() && tl.row() != br.row())
+ q->setSelection(q->visualRect(tl)|q->visualRect(br), command);
+ else
+ selectionModel->select(QItemSelection(tl, br), command);
+ }
+}
+
+void QTableViewPrivate::selectColumn(int column, bool anchor)
+{
+ Q_Q(QTableView);
+
+ if (q->selectionBehavior() == QTableView::SelectRows
+ || (q->selectionMode() == QTableView::SingleSelection
+ && q->selectionBehavior() == QTableView::SelectItems))
+ return;
+
+ if (column >= 0 && column < model->columnCount(root)) {
+ int row = verticalHeader->logicalIndexAt(0);
+ QModelIndex index = model->index(row, column, root);
+ QItemSelectionModel::SelectionFlags command = q->selectionCommand(index);
+ selectionModel->setCurrentIndex(index, QItemSelectionModel::NoUpdate);
+ if ((!(command & QItemSelectionModel::Current) && anchor)
+ || (q->selectionMode() == QTableView::SingleSelection))
+ columnSectionAnchor = column;
+ QModelIndex tl = model->index(0, qMin(columnSectionAnchor, column), root);
+ QModelIndex br = model->index(model->rowCount(root) - 1,
+ qMax(columnSectionAnchor, column), root);
+ if (horizontalHeader->sectionsMoved() && tl.column() != br.column())
+ q->setSelection(q->visualRect(tl)|q->visualRect(br), command);
+ else
+ selectionModel->select(QItemSelection(tl, br), command);
+ }
+}
+
+/*!
+ \reimp
+ */
+void QTableView::currentChanged(const QModelIndex &current, const QModelIndex &previous)
+{
+#ifndef QT_NO_ACCESSIBILITY
+ if (QAccessible::isActive()) {
+ if (current.isValid()) {
+ int entry = visualIndex(current) + 1;
+ if (horizontalHeader())
+ ++entry;
+ QAccessible::updateAccessibility(viewport(), entry, QAccessible::Focus);
+ }
+ }
+#endif
+ QAbstractItemView::currentChanged(current, previous);
+}
+
+/*!
+ \reimp
+ */
+void QTableView::selectionChanged(const QItemSelection &selected,
+ const QItemSelection &deselected)
+{
+#ifndef QT_NO_ACCESSIBILITY
+ if (QAccessible::isActive()) {
+ // ### does not work properly for selection ranges.
+ QModelIndex sel = selected.indexes().value(0);
+ if (sel.isValid()) {
+ int entry = visualIndex(sel);
+ if (horizontalHeader())
+ ++entry;
+ QAccessible::updateAccessibility(viewport(), entry, QAccessible::Selection);
+ }
+ QModelIndex desel = deselected.indexes().value(0);
+ if (desel.isValid()) {
+ int entry = visualIndex(sel);
+ if (horizontalHeader())
+ ++entry;
+ QAccessible::updateAccessibility(viewport(), entry, QAccessible::SelectionRemove);
+ }
+ }
+#endif
+ QAbstractItemView::selectionChanged(selected, deselected);
+}
+
+int QTableView::visualIndex(const QModelIndex &index) const
+{
+ return index.row();
+}
+
+QT_END_NAMESPACE
+
+#include "qtableview.moc"
+
+#include "moc_qtableview.cpp"
+
+#endif // QT_NO_TABLEVIEW
diff --git a/src/gui/itemviews/qtableview.h b/src/gui/itemviews/qtableview.h
new file mode 100644
index 0000000000..6654d7f24a
--- /dev/null
+++ b/src/gui/itemviews/qtableview.h
@@ -0,0 +1,193 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QTABLEVIEW_H
+#define QTABLEVIEW_H
+
+#include <QtGui/qabstractitemview.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+#ifndef QT_NO_TABLEVIEW
+
+class QHeaderView;
+class QTableViewPrivate;
+
+class Q_GUI_EXPORT QTableView : public QAbstractItemView
+{
+ Q_OBJECT
+ Q_PROPERTY(bool showGrid READ showGrid WRITE setShowGrid)
+ Q_PROPERTY(Qt::PenStyle gridStyle READ gridStyle WRITE setGridStyle)
+ Q_PROPERTY(bool sortingEnabled READ isSortingEnabled WRITE setSortingEnabled)
+ Q_PROPERTY(bool wordWrap READ wordWrap WRITE setWordWrap)
+ Q_PROPERTY(bool cornerButtonEnabled READ isCornerButtonEnabled WRITE setCornerButtonEnabled)
+
+public:
+ explicit QTableView(QWidget *parent = 0);
+ ~QTableView();
+
+ void setModel(QAbstractItemModel *model);
+ void setRootIndex(const QModelIndex &index);
+ void setSelectionModel(QItemSelectionModel *selectionModel);
+
+ QHeaderView *horizontalHeader() const;
+ QHeaderView *verticalHeader() const;
+ void setHorizontalHeader(QHeaderView *header);
+ void setVerticalHeader(QHeaderView *header);
+
+ int rowViewportPosition(int row) const;
+ int rowAt(int y) const;
+
+ void setRowHeight(int row, int height);
+ int rowHeight(int row) const;
+
+ int columnViewportPosition(int column) const;
+ int columnAt(int x) const;
+
+ void setColumnWidth(int column, int width);
+ int columnWidth(int column) const;
+
+ bool isRowHidden(int row) const;
+ void setRowHidden(int row, bool hide);
+
+ bool isColumnHidden(int column) const;
+ void setColumnHidden(int column, bool hide);
+
+ void setSortingEnabled(bool enable);
+ bool isSortingEnabled() const;
+
+ bool showGrid() const;
+
+ Qt::PenStyle gridStyle() const;
+ void setGridStyle(Qt::PenStyle style);
+
+ void setWordWrap(bool on);
+ bool wordWrap() const;
+
+ void setCornerButtonEnabled(bool enable);
+ bool isCornerButtonEnabled() const;
+
+ QRect visualRect(const QModelIndex &index) const;
+ void scrollTo(const QModelIndex &index, ScrollHint hint = EnsureVisible);
+ QModelIndex indexAt(const QPoint &p) const;
+
+ void setSpan(int row, int column, int rowSpan, int columnSpan);
+ int rowSpan(int row, int column) const;
+ int columnSpan(int row, int column) const;
+ void clearSpans();
+
+ void sortByColumn(int column, Qt::SortOrder order);
+
+public Q_SLOTS:
+ void selectRow(int row);
+ void selectColumn(int column);
+ void hideRow(int row);
+ void hideColumn(int column);
+ void showRow(int row);
+ void showColumn(int column);
+ void resizeRowToContents(int row);
+ void resizeRowsToContents();
+ void resizeColumnToContents(int column);
+ void resizeColumnsToContents();
+ void sortByColumn(int column);
+ void setShowGrid(bool show);
+
+protected Q_SLOTS:
+ void rowMoved(int row, int oldIndex, int newIndex);
+ void columnMoved(int column, int oldIndex, int newIndex);
+ void rowResized(int row, int oldHeight, int newHeight);
+ void columnResized(int column, int oldWidth, int newWidth);
+ void rowCountChanged(int oldCount, int newCount);
+ void columnCountChanged(int oldCount, int newCount);
+
+protected:
+ QTableView(QTableViewPrivate &, QWidget *parent);
+ void scrollContentsBy(int dx, int dy);
+
+ QStyleOptionViewItem viewOptions() const;
+ void paintEvent(QPaintEvent *e);
+
+ void timerEvent(QTimerEvent *event);
+
+ int horizontalOffset() const;
+ int verticalOffset() const;
+ QModelIndex moveCursor(CursorAction cursorAction, Qt::KeyboardModifiers modifiers);
+
+ void setSelection(const QRect &rect, QItemSelectionModel::SelectionFlags command);
+ QRegion visualRegionForSelection(const QItemSelection &selection) const;
+ QModelIndexList selectedIndexes() const;
+
+ void updateGeometries();
+
+ int sizeHintForRow(int row) const;
+ int sizeHintForColumn(int column) const;
+
+ void verticalScrollbarAction(int action);
+ void horizontalScrollbarAction(int action);
+
+ bool isIndexHidden(const QModelIndex &index) const;
+
+ void selectionChanged(const QItemSelection &selected,
+ const QItemSelection &deselected);
+ void currentChanged(const QModelIndex &current,
+ const QModelIndex &previous);
+
+private:
+ friend class QAccessibleItemView;
+ int visualIndex(const QModelIndex &index) const;
+
+ Q_DECLARE_PRIVATE(QTableView)
+ Q_DISABLE_COPY(QTableView)
+ Q_PRIVATE_SLOT(d_func(), void _q_selectRow(int))
+ Q_PRIVATE_SLOT(d_func(), void _q_selectColumn(int))
+};
+
+#endif // QT_NO_TABLEVIEW
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QTABLEVIEW_H
diff --git a/src/gui/itemviews/qtableview_p.h b/src/gui/itemviews/qtableview_p.h
new file mode 100644
index 0000000000..b08eabd8e3
--- /dev/null
+++ b/src/gui/itemviews/qtableview_p.h
@@ -0,0 +1,210 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QTABLEVIEW_P_H
+#define QTABLEVIEW_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/qabstractitemview_p.h"
+
+#ifndef QT_NO_TABLEVIEW
+
+QT_BEGIN_NAMESPACE
+
+class QTableViewPrivate : public QAbstractItemViewPrivate
+{
+ Q_DECLARE_PUBLIC(QTableView)
+public:
+ QTableViewPrivate()
+ : showGrid(true), gridStyle(Qt::SolidLine),
+ rowSectionAnchor(-1), columnSectionAnchor(-1),
+ columnResizeTimerID(0), rowResizeTimerID(0),
+ horizontalHeader(0), verticalHeader(0),
+ sortingEnabled(false), geometryRecursionBlock(false)
+ {
+ wrapItemText = true;
+#ifndef QT_NO_DRAGANDDROP
+ overwrite = true;
+#endif
+ }
+ void init();
+ void trimHiddenSelections(QItemSelectionRange *range) const;
+
+ inline bool isHidden(int row, int col) const {
+ return verticalHeader->isSectionHidden(row)
+ || horizontalHeader->isSectionHidden(col);
+ }
+ inline int visualRow(int logicalRow) const {
+ return verticalHeader->visualIndex(logicalRow);
+ }
+ inline int visualColumn(int logicalCol) const {
+ return horizontalHeader->visualIndex(logicalCol);
+ }
+ inline int logicalRow(int visualRow) const {
+ return verticalHeader->logicalIndex(visualRow);
+ }
+ inline int logicalColumn(int visualCol) const {
+ return horizontalHeader->logicalIndex(visualCol);
+ }
+
+ int sectionSpanEndLogical(const QHeaderView *header, int logical, int span) const;
+ int sectionSpanSize(const QHeaderView *header, int logical, int span) const;
+ bool spanContainsSection(const QHeaderView *header, int logical, int spanLogical, int span) const;
+ bool spansIntersectColumn(int column) const;
+ bool spansIntersectRow(int row) const;
+ bool spansIntersectColumns(const QList<int> &columns) const;
+ bool spansIntersectRows(const QList<int> &rows) const;
+ void drawAndClipSpans(const QRect &area, QPainter *painter,
+ const QStyleOptionViewItemV4 &option, QBitArray *drawn,
+ int firstVisualRow, int lastVisualRow, int firstVisualColumn, int lastVisualColumn);
+ void drawCell(QPainter *painter, const QStyleOptionViewItemV4 &option, const QModelIndex &index);
+
+ bool showGrid;
+ Qt::PenStyle gridStyle;
+ int rowSectionAnchor;
+ int columnSectionAnchor;
+ int columnResizeTimerID;
+ int rowResizeTimerID;
+ QList<int> columnsToUpdate;
+ QList<int> rowsToUpdate;
+ QHeaderView *horizontalHeader;
+ QHeaderView *verticalHeader;
+ QWidget *cornerWidget;
+ bool sortingEnabled;
+ bool geometryRecursionBlock;
+
+ struct Span
+ {
+ int m_top;
+ int m_left;
+ int m_bottom;
+ int m_right;
+ Span()
+ : m_top(-1), m_left(-1), m_bottom(-1), m_right(-1) { }
+ Span(int row, int column, int rowCount, int columnCount)
+ : m_top(row), m_left(column), m_bottom(row+rowCount-1), m_right(column+columnCount-1) { }
+ inline int top() const { return m_top; }
+ inline int left() const { return m_left; }
+ inline int bottom() const { return m_bottom; }
+ inline int right() const { return m_right; }
+ inline int height() const { return m_bottom - m_top + 1; }
+ inline int width() const { return m_right - m_left + 1; }
+ };
+ QList<Span> spans;
+
+ void setSpan(int row, int column, int rowSpan, int columnSpan);
+ Span span(int row, int column) const;
+ inline int rowSpan(int row, int column) const {
+ return span(row, column).height();
+ }
+ inline int columnSpan(int row, int column) const {
+ return span(row, column).width();
+ }
+ inline bool hasSpans() const {
+ return !spans.isEmpty();
+ }
+ inline bool spanContainsRow(int row, int spanRow, int span) const {
+ return spanContainsSection(verticalHeader, row, spanRow, span);
+ }
+ inline bool spanContainsColumn(int column, int spanColumn, int span) const {
+ return spanContainsSection(horizontalHeader, column, spanColumn, span);
+ }
+ inline bool isInSpan(int row, int column, const Span &span) const {
+ return spanContainsRow(row, span.top(), span.height())
+ && spanContainsColumn(column, span.left(), span.width());
+ }
+ inline int rowSpanHeight(int row, int span) const {
+ return sectionSpanSize(verticalHeader, row, span);
+ }
+ inline int columnSpanWidth(int column, int span) const {
+ return sectionSpanSize(horizontalHeader, column, span);
+ }
+ inline int rowSpanEndLogical(int row, int span) const {
+ return sectionSpanEndLogical(verticalHeader, row, span);
+ }
+ inline int columnSpanEndLogical(int column, int span) const {
+ return sectionSpanEndLogical(horizontalHeader, column, span);
+ }
+
+ inline bool isRowHidden(int row) const {
+ return verticalHeader->isSectionHidden(row);
+ }
+ inline bool isColumnHidden(int column) const {
+ return horizontalHeader->isSectionHidden(column);
+ }
+ inline bool isCellEnabled(int row, int column) const {
+ return isIndexEnabled(model->index(row, column, root));
+ }
+ inline bool isVisualRowHiddenOrDisabled(int row, int column) const {
+ int r = logicalRow(row);
+ int c = logicalColumn(column);
+ return isRowHidden(r) || !isCellEnabled(r, c);
+ }
+ inline bool isVisualColumnHiddenOrDisabled(int row, int column) const {
+ int r = logicalRow(row);
+ int c = logicalColumn(column);
+ return isColumnHidden(c) || !isCellEnabled(r, c);
+ }
+
+ QRect visualSpanRect(const Span &span) const;
+
+ void _q_selectRow(int row);
+ void _q_selectColumn(int column);
+
+ void selectRow(int row, bool anchor);
+ void selectColumn(int column, bool anchor);
+};
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_TABLEVIEW
+
+#endif // QTABLEVIEW_P_H
diff --git a/src/gui/itemviews/qtablewidget.cpp b/src/gui/itemviews/qtablewidget.cpp
new file mode 100644
index 0000000000..e88301e36e
--- /dev/null
+++ b/src/gui/itemviews/qtablewidget.cpp
@@ -0,0 +1,2703 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qtablewidget.h"
+
+#ifndef QT_NO_TABLEWIDGET
+#include <qitemdelegate.h>
+#include <qpainter.h>
+#include <private/qtablewidget_p.h>
+
+QT_BEGIN_NAMESPACE
+
+QTableModel::QTableModel(int rows, int columns, QTableWidget *parent)
+ : QAbstractTableModel(parent),
+ prototype(0),
+ tableItems(rows * columns, 0),
+ verticalHeaderItems(rows, 0),
+ horizontalHeaderItems(columns, 0)
+{}
+
+QTableModel::~QTableModel()
+{
+ clear();
+ delete prototype;
+}
+
+bool QTableModel::insertRows(int row, int count, const QModelIndex &)
+{
+ if (count < 1 || row < 0 || row > verticalHeaderItems.count())
+ return false;
+
+ beginInsertRows(QModelIndex(), row, row + count - 1);
+ int rc = verticalHeaderItems.count();
+ int cc = horizontalHeaderItems.count();
+ verticalHeaderItems.insert(row, count, 0);
+ if (rc == 0)
+ tableItems.resize(cc * count);
+ else
+ tableItems.insert(tableIndex(row, 0), cc * count, 0);
+ endInsertRows();
+ return true;
+}
+
+bool QTableModel::insertColumns(int column, int count, const QModelIndex &)
+{
+ if (count < 1 || column < 0 || column > horizontalHeaderItems.count())
+ return false;
+
+ beginInsertColumns(QModelIndex(), column, column + count - 1);
+ int rc = verticalHeaderItems.count();
+ int cc = horizontalHeaderItems.count();
+ horizontalHeaderItems.insert(column, count, 0);
+ if (cc == 0)
+ tableItems.resize(rc * count);
+ else
+ for (int row = 0; row < rc; ++row)
+ tableItems.insert(tableIndex(row, column), count, 0);
+ endInsertColumns();
+ return true;
+}
+
+bool QTableModel::removeRows(int row, int count, const QModelIndex &)
+{
+ if (count < 1 || row < 0 || row + count > verticalHeaderItems.count())
+ return false;
+
+ beginRemoveRows(QModelIndex(), row, row + count - 1);
+ int i = tableIndex(row, 0);
+ int n = count * columnCount();
+ QTableWidgetItem *oldItem = 0;
+ for (int j = i; j < n + i; ++j) {
+ oldItem = tableItems.at(j);
+ if (oldItem)
+ oldItem->view = 0;
+ delete oldItem;
+ }
+ tableItems.remove(qMax(i, 0), n);
+ for (int v = row; v < row + count; ++v) {
+ oldItem = verticalHeaderItems.at(v);
+ if (oldItem)
+ oldItem->view = 0;
+ delete oldItem;
+ }
+ verticalHeaderItems.remove(row, count);
+ endRemoveRows();
+ return true;
+}
+
+bool QTableModel::removeColumns(int column, int count, const QModelIndex &)
+{
+ if (count < 1 || column < 0 || column + count > horizontalHeaderItems.count())
+ return false;
+
+ beginRemoveColumns(QModelIndex(), column, column + count - 1);
+ QTableWidgetItem *oldItem = 0;
+ for (int row = rowCount() - 1; row >= 0; --row) {
+ int i = tableIndex(row, column);
+ for (int j = i; j < i + count; ++j) {
+ oldItem = tableItems.at(j);
+ if (oldItem)
+ oldItem->view = 0;
+ delete oldItem;
+ }
+ tableItems.remove(i, count);
+ }
+ for (int h=column; h<column+count; ++h) {
+ oldItem = horizontalHeaderItems.at(h);
+ if (oldItem)
+ oldItem->view = 0;
+ delete oldItem;
+ }
+ horizontalHeaderItems.remove(column, count);
+ endRemoveColumns();
+ return true;
+}
+
+void QTableModel::setItem(int row, int column, QTableWidgetItem *item)
+{
+ int i = tableIndex(row, column);
+ if (i < 0 || i >= tableItems.count())
+ return;
+ QTableWidgetItem *oldItem = tableItems.at(i);
+ if (item == oldItem)
+ return;
+
+ // remove old
+ if (oldItem)
+ oldItem->view = 0;
+ delete tableItems.at(i);
+
+ QTableWidget *view = qobject_cast<QTableWidget*>(QObject::parent());
+
+ // set new
+ if (item)
+ item->d->id = i;
+ tableItems[i] = item;
+
+ if (view && view->isSortingEnabled()
+ && view->horizontalHeader()->sortIndicatorSection() == column) {
+ // sorted insertion
+ Qt::SortOrder order = view->horizontalHeader()->sortIndicatorOrder();
+ QVector<QTableWidgetItem*> colItems = columnItems(column);
+ if (row < colItems.count())
+ colItems.remove(row);
+ int sortedRow;
+ if (item == 0) {
+ // move to after all non-0 (sortable) items
+ sortedRow = colItems.count();
+ } else {
+ QVector<QTableWidgetItem*>::iterator it;
+ it = sortedInsertionIterator(colItems.begin(), colItems.end(), order, item);
+ sortedRow = qMax((int)(it - colItems.begin()), 0);
+ }
+ if (sortedRow != row) {
+ emit layoutAboutToBeChanged();
+ // move the items @ row to sortedRow
+ int cc = columnCount();
+ QVector<QTableWidgetItem*> rowItems(cc);
+ for (int j = 0; j < cc; ++j)
+ rowItems[j] = tableItems.at(tableIndex(row, j));
+ tableItems.remove(tableIndex(row, 0), cc);
+ tableItems.insert(tableIndex(sortedRow, 0), cc, 0);
+ for (int j = 0; j < cc; ++j)
+ tableItems[tableIndex(sortedRow, j)] = rowItems.at(j);
+ QTableWidgetItem *header = verticalHeaderItems.at(row);
+ verticalHeaderItems.remove(row);
+ verticalHeaderItems.insert(sortedRow, header);
+ // update persistent indexes
+ QModelIndexList oldPersistentIndexes = persistentIndexList();
+ QModelIndexList newPersistentIndexes = oldPersistentIndexes;
+ updateRowIndexes(newPersistentIndexes, row, sortedRow);
+ changePersistentIndexList(oldPersistentIndexes,
+ newPersistentIndexes);
+
+ emit layoutChanged();
+ return;
+ }
+ }
+ QModelIndex idx = QAbstractTableModel::index(row, column);
+ emit dataChanged(idx, idx);
+}
+
+QTableWidgetItem *QTableModel::takeItem(int row, int column)
+{
+ long i = tableIndex(row, column);
+ QTableWidgetItem *itm = tableItems.value(i);
+ if (itm) {
+ itm->view = 0;
+ itm->d->id = -1;
+ tableItems[i] = 0;
+ }
+ return itm;
+}
+
+QTableWidgetItem *QTableModel::item(int row, int column) const
+{
+ return tableItems.value(tableIndex(row, column));
+}
+
+QTableWidgetItem *QTableModel::item(const QModelIndex &index) const
+{
+ if (!isValid(index))
+ return 0;
+ return tableItems.at(tableIndex(index.row(), index.column()));
+}
+
+void QTableModel::removeItem(QTableWidgetItem *item)
+{
+ int i = tableItems.indexOf(item);
+ if (i != -1) {
+ tableItems[i] = 0;
+ QModelIndex idx = index(item);
+ emit dataChanged(idx, idx);
+ return;
+ }
+
+ i = verticalHeaderItems.indexOf(item);
+
+ if (i != -1) {
+ verticalHeaderItems[i] = 0;
+ emit headerDataChanged(Qt::Vertical, i, i);
+ return;
+ }
+ i = horizontalHeaderItems.indexOf(item);
+ if (i != -1) {
+ horizontalHeaderItems[i] = 0;
+ emit headerDataChanged(Qt::Horizontal, i, i);
+ return;
+ }
+}
+
+void QTableModel::setHorizontalHeaderItem(int section, QTableWidgetItem *item)
+{
+ if (section < 0 || section >= horizontalHeaderItems.count())
+ return;
+ QTableWidgetItem *oldItem = horizontalHeaderItems.at(section);
+ if (item == oldItem)
+ return;
+
+ if (oldItem)
+ oldItem->view = 0;
+ delete oldItem;
+
+ QTableWidget *view = qobject_cast<QTableWidget*>(QObject::parent());
+
+ if (item) {
+ item->view = view;
+ item->itemFlags = Qt::ItemFlags(int(item->itemFlags)|ItemIsHeaderItem);
+ }
+ horizontalHeaderItems[section] = item;
+ emit headerDataChanged(Qt::Horizontal, section, section);
+}
+
+void QTableModel::setVerticalHeaderItem(int section, QTableWidgetItem *item)
+{
+ if (section < 0 || section >= verticalHeaderItems.count())
+ return;
+ QTableWidgetItem *oldItem = verticalHeaderItems.at(section);
+ if (item == oldItem)
+ return;
+
+ if (oldItem)
+ oldItem->view = 0;
+ delete oldItem;
+
+ QTableWidget *view = qobject_cast<QTableWidget*>(QObject::parent());
+
+ if (item) {
+ item->view = view;
+ item->itemFlags = Qt::ItemFlags(int(item->itemFlags)|ItemIsHeaderItem);
+ }
+ verticalHeaderItems[section] = item;
+ emit headerDataChanged(Qt::Vertical, section, section);
+}
+
+QTableWidgetItem *QTableModel::takeHorizontalHeaderItem(int section)
+{
+ if (section < 0 || section >= horizontalHeaderItems.count())
+ return 0;
+ QTableWidgetItem *itm = horizontalHeaderItems.at(section);
+ if (itm) {
+ itm->view = 0;
+ itm->itemFlags &= ~ItemIsHeaderItem;
+ horizontalHeaderItems[section] = 0;
+ }
+ return itm;
+}
+
+QTableWidgetItem *QTableModel::takeVerticalHeaderItem(int section)
+{
+ if (section < 0 || section >= verticalHeaderItems.count())
+ return 0;
+ QTableWidgetItem *itm = verticalHeaderItems.at(section);
+ if (itm) {
+ itm->view = 0;
+ itm->itemFlags &= ~ItemIsHeaderItem;
+ verticalHeaderItems[section] = 0;
+ }
+ return itm;
+}
+
+QTableWidgetItem *QTableModel::horizontalHeaderItem(int section)
+{
+ return horizontalHeaderItems.value(section);
+}
+
+QTableWidgetItem *QTableModel::verticalHeaderItem(int section)
+{
+ return verticalHeaderItems.value(section);
+}
+
+QModelIndex QTableModel::index(const QTableWidgetItem *item) const
+{
+ if (!item)
+ return QModelIndex();
+ int i = -1;
+ const int id = item->d->id;
+ if (id >= 0 && id < tableItems.count() && tableItems.at(id) == item) {
+ i = id;
+ } else { // we need to search for the item
+ i = tableItems.indexOf(const_cast<QTableWidgetItem*>(item));
+ if (i == -1) // not found
+ return QModelIndex();
+ }
+ int row = i / columnCount();
+ int col = i % columnCount();
+ return QAbstractTableModel::index(row, col);
+}
+
+void QTableModel::setRowCount(int rows)
+{
+ int rc = verticalHeaderItems.count();
+ if (rows < 0 || rc == rows)
+ return;
+ if (rc < rows)
+ insertRows(qMax(rc, 0), rows - rc);
+ else
+ removeRows(qMax(rows, 0), rc - rows);
+}
+
+void QTableModel::setColumnCount(int columns)
+{
+ int cc = horizontalHeaderItems.count();
+ if (columns < 0 || cc == columns)
+ return;
+ if (cc < columns)
+ insertColumns(qMax(cc, 0), columns - cc);
+ else
+ removeColumns(qMax(columns, 0), cc - columns);
+}
+
+int QTableModel::rowCount(const QModelIndex &parent) const
+{
+ return parent.isValid() ? 0 : verticalHeaderItems.count();
+}
+
+int QTableModel::columnCount(const QModelIndex &parent) const
+{
+ return parent.isValid() ? 0 : horizontalHeaderItems.count();
+}
+
+QVariant QTableModel::data(const QModelIndex &index, int role) const
+{
+ QTableWidgetItem *itm = item(index);
+ if (itm)
+ return itm->data(role);
+ return QVariant();
+}
+
+bool QTableModel::setData(const QModelIndex &index, const QVariant &value, int role)
+{
+ if (!index.isValid())
+ return false;
+
+ QTableWidgetItem *itm = item(index);
+ if (itm) {
+ itm->setData(role, value);
+ return true;
+ }
+
+ // don't create dummy table items for empty values
+ if (!value.isValid())
+ return false;
+
+ QTableWidget *view = qobject_cast<QTableWidget*>(QObject::parent());
+ if (!view)
+ return false;
+
+ itm = createItem();
+ itm->setData(role, value);
+ view->setItem(index.row(), index.column(), itm);
+ return true;
+}
+
+QMap<int, QVariant> QTableModel::itemData(const QModelIndex &index) const
+{
+ QMap<int, QVariant> roles;
+ QTableWidgetItem *itm = item(index);
+ if (itm) {
+ for (int i = 0; i < itm->values.count(); ++i) {
+ roles.insert(itm->values.at(i).role,
+ itm->values.at(i).value);
+ }
+ }
+ return roles;
+}
+
+// reimplemented to ensure that only one dataChanged() signal is emitted
+bool QTableModel::setItemData(const QModelIndex &index, const QMap<int, QVariant> &roles)
+{
+ if (!index.isValid())
+ return false;
+
+ QTableWidget *view = qobject_cast<QTableWidget*>(QObject::parent());
+ QTableWidgetItem *itm = item(index);
+ if (itm) {
+ itm->view = 0; // prohibits item from calling itemChanged()
+ bool changed = false;
+ for (QMap<int, QVariant>::ConstIterator it = roles.constBegin(); it != roles.constEnd(); ++it) {
+ if (itm->data(it.key()) != it.value()) {
+ itm->setData(it.key(), it.value());
+ changed = true;
+ }
+ }
+ itm->view = view;
+ if (changed)
+ itemChanged(itm);
+ return true;
+ }
+
+ if (!view)
+ return false;
+
+ itm = createItem();
+ for (QMap<int, QVariant>::ConstIterator it = roles.constBegin(); it != roles.constEnd(); ++it)
+ itm->setData(it.key(), it.value());
+ view->setItem(index.row(), index.column(), itm);
+ return true;
+}
+
+Qt::ItemFlags QTableModel::flags(const QModelIndex &index) const
+{
+ if (!index.isValid())
+ return Qt::ItemIsDropEnabled;
+ if (QTableWidgetItem *itm = item(index))
+ return itm->flags();
+ return (Qt::ItemIsEditable
+ |Qt::ItemIsSelectable
+ |Qt::ItemIsUserCheckable
+ |Qt::ItemIsEnabled
+ |Qt::ItemIsDragEnabled
+ |Qt::ItemIsDropEnabled);
+}
+
+void QTableModel::sort(int column, Qt::SortOrder order)
+{
+ QVector<QPair<QTableWidgetItem*, int> > sortable;
+ QVector<int> unsortable;
+
+ sortable.reserve(rowCount());
+ unsortable.reserve(rowCount());
+
+ for (int row = 0; row < rowCount(); ++row) {
+ if (QTableWidgetItem *itm = item(row, column))
+ sortable.append(QPair<QTableWidgetItem*,int>(itm, row));
+ else
+ unsortable.append(row);
+ }
+
+ LessThan compare = (order == Qt::AscendingOrder ? &itemLessThan : &itemGreaterThan);
+ qStableSort(sortable.begin(), sortable.end(), compare);
+
+ QVector<QTableWidgetItem*> sorted_table(tableItems.count());
+ QModelIndexList from;
+ QModelIndexList to;
+ for (int i = 0; i < rowCount(); ++i) {
+ int r = (i < sortable.count()
+ ? sortable.at(i).second
+ : unsortable.at(i - sortable.count()));
+ for (int c = 0; c < columnCount(); ++c) {
+ sorted_table[tableIndex(i, c)] = item(r, c);
+ from.append(createIndex(r, c, 0));
+ to.append(createIndex(i, c, 0));
+ }
+ }
+
+ emit layoutAboutToBeChanged();
+
+ tableItems = sorted_table;
+ changePersistentIndexList(from, to); // ### slow
+
+ emit layoutChanged();
+}
+
+bool QTableModel::canConvertToDouble(const QVariant &value)
+{
+ switch (value.type()) {
+ case QVariant::Bool:
+ case QVariant::Int:
+ case QVariant::UInt:
+ case QVariant::LongLong:
+ case QVariant::ULongLong:
+ case QVariant::Double:
+ case QVariant::Char:
+ return true;
+ default:
+ return false;
+ }
+ return false;
+}
+
+
+/*
+ \internal
+
+ Ensures that rows in the interval [start, end] are
+ sorted according to the contents of column \a column
+ and the given sort \a order.
+*/
+void QTableModel::ensureSorted(int column, Qt::SortOrder order,
+ int start, int end)
+{
+ int count = end - start + 1;
+ QVector < QPair<QTableWidgetItem*,int> > sorting;
+ sorting.reserve(count);
+ for (int row = start; row <= end; ++row) {
+ QTableWidgetItem *itm = item(row, column);
+ if (itm == 0) {
+ // no more sortable items (all 0-items are
+ // at the end of the table when it is sorted)
+ break;
+ }
+ sorting.append(QPair<QTableWidgetItem*,int>(itm, row));
+ }
+
+ LessThan compare = (order == Qt::AscendingOrder ? &itemLessThan : &itemGreaterThan);
+ qStableSort(sorting.begin(), sorting.end(), compare);
+
+ QModelIndexList oldPersistentIndexes = persistentIndexList();
+ QModelIndexList newPersistentIndexes = oldPersistentIndexes;
+ QVector<QTableWidgetItem*> newTable = tableItems;
+ QVector<QTableWidgetItem*> newVertical = verticalHeaderItems;
+ QVector<QTableWidgetItem*> colItems = columnItems(column);
+ QVector<QTableWidgetItem*>::iterator vit = colItems.begin();
+ bool changed = false;
+ for (int i = 0; i < sorting.count(); ++i) {
+ int oldRow = sorting.at(i).second;
+ QTableWidgetItem *item = colItems.at(oldRow);
+ colItems.remove(oldRow);
+ vit = sortedInsertionIterator(vit, colItems.end(), order, item);
+ int newRow = qMax((int)(vit - colItems.begin()), 0);
+ vit = colItems.insert(vit, item);
+ if (newRow != oldRow) {
+ changed = true;
+ // move the items @ oldRow to newRow
+ int cc = columnCount();
+ QVector<QTableWidgetItem*> rowItems(cc);
+ for (int j = 0; j < cc; ++j)
+ rowItems[j] = newTable.at(tableIndex(oldRow, j));
+ newTable.remove(tableIndex(oldRow, 0), cc);
+ newTable.insert(tableIndex(newRow, 0), cc, 0);
+ for (int j = 0; j < cc; ++j)
+ newTable[tableIndex(newRow, j)] = rowItems.at(j);
+ QTableWidgetItem *header = newVertical.at(oldRow);
+ newVertical.remove(oldRow);
+ newVertical.insert(newRow, header);
+ // update persistent indexes
+ updateRowIndexes(newPersistentIndexes, oldRow, newRow);
+ // the index of the remaining rows may have changed
+ for (int j = i + 1; j < sorting.count(); ++j) {
+ int otherRow = sorting.at(j).second;
+ if (oldRow < otherRow && newRow >= otherRow)
+ --sorting[j].second;
+ else if (oldRow > otherRow && newRow <= otherRow)
+ ++sorting[j].second;
+ }
+ }
+ }
+
+ if (changed) {
+ emit layoutAboutToBeChanged();
+ tableItems = newTable;
+ verticalHeaderItems = newVertical;
+ changePersistentIndexList(oldPersistentIndexes,
+ newPersistentIndexes);
+ emit layoutChanged();
+ }
+}
+
+/*
+ \internal
+
+ Returns the non-0 items in column \a column.
+*/
+QVector<QTableWidgetItem*> QTableModel::columnItems(int column) const
+{
+ QVector<QTableWidgetItem*> items;
+ int rc = rowCount();
+ items.reserve(rc);
+ for (int row = 0; row < rc; ++row) {
+ QTableWidgetItem *itm = item(row, column);
+ if (itm == 0) {
+ // no more sortable items (all 0-items are
+ // at the end of the table when it is sorted)
+ break;
+ }
+ items.append(itm);
+ }
+ return items;
+}
+
+/*
+ \internal
+
+ Adjusts the row of each index in \a indexes if necessary, given
+ that a row of items has been moved from row \a movedFrom to row
+ \a movedTo.
+*/
+void QTableModel::updateRowIndexes(QModelIndexList &indexes,
+ int movedFromRow, int movedToRow)
+{
+ QModelIndexList::iterator it;
+ for (it = indexes.begin(); it != indexes.end(); ++it) {
+ int oldRow = (*it).row();
+ int newRow = oldRow;
+ if (oldRow == movedFromRow)
+ newRow = movedToRow;
+ else if (movedFromRow < oldRow && movedToRow >= oldRow)
+ newRow = oldRow - 1;
+ else if (movedFromRow > oldRow && movedToRow <= oldRow)
+ newRow = oldRow + 1;
+ if (newRow != oldRow)
+ *it = index(newRow, (*it).column(), (*it).parent());
+ }
+}
+
+/*
+ \internal
+
+ Returns an iterator to the item where \a item should be
+ inserted in the interval (\a begin, \a end) according to
+ the given sort \a order.
+*/
+QVector<QTableWidgetItem*>::iterator QTableModel::sortedInsertionIterator(
+ const QVector<QTableWidgetItem*>::iterator &begin,
+ const QVector<QTableWidgetItem*>::iterator &end,
+ Qt::SortOrder order, QTableWidgetItem *item)
+{
+ if (order == Qt::AscendingOrder)
+ return qLowerBound(begin, end, item, QTableModelLessThan());
+ return qLowerBound(begin, end, item, QTableModelGreaterThan());
+}
+
+bool QTableModel::itemLessThan(const QPair<QTableWidgetItem*,int> &left,
+ const QPair<QTableWidgetItem*,int> &right)
+{
+ return *(left.first) < *(right.first);
+}
+
+bool QTableModel::itemGreaterThan(const QPair<QTableWidgetItem*,int> &left,
+ const QPair<QTableWidgetItem*,int> &right)
+{
+ return (*(right.first) < *(left .first));
+}
+
+QVariant QTableModel::headerData(int section, Qt::Orientation orientation, int role) const
+{
+ if (section < 0)
+ return QVariant();
+
+ QTableWidgetItem *itm = 0;
+ if (orientation == Qt::Horizontal && section < horizontalHeaderItems.count())
+ itm = horizontalHeaderItems.at(section);
+ else if (orientation == Qt::Vertical && section < verticalHeaderItems.count())
+ itm = verticalHeaderItems.at(section);
+ else
+ return QVariant(); // section is out of bounds
+
+ if (itm)
+ return itm->data(role);
+ if (role == Qt::DisplayRole)
+ return section + 1;
+ return QVariant();
+}
+
+bool QTableModel::setHeaderData(int section, Qt::Orientation orientation,
+ const QVariant &value, int role)
+{
+ if (section < 0 ||
+ (orientation == Qt::Horizontal && horizontalHeaderItems.size() <= section) ||
+ (orientation == Qt::Vertical && verticalHeaderItems.size() <= section))
+ return false;
+
+ QTableWidgetItem *itm = 0;
+ if (orientation == Qt::Horizontal)
+ itm = horizontalHeaderItems.at(section);
+ else
+ itm = verticalHeaderItems.at(section);
+ if (itm) {
+ itm->setData(role, value);
+ return true;
+ }
+ return false;
+}
+
+bool QTableModel::isValid(const QModelIndex &index) const
+{
+ return (index.isValid()
+ && index.row() < verticalHeaderItems.count()
+ && index.column() < horizontalHeaderItems.count());
+}
+
+void QTableModel::clear()
+{
+ for (int j = 0; j < verticalHeaderItems.count(); ++j) {
+ if (verticalHeaderItems.at(j)) {
+ verticalHeaderItems.at(j)->view = 0;
+ delete verticalHeaderItems.at(j);
+ verticalHeaderItems[j] = 0;
+ }
+ }
+ for (int k = 0; k < horizontalHeaderItems.count(); ++k) {
+ if (horizontalHeaderItems.at(k)) {
+ horizontalHeaderItems.at(k)->view = 0;
+ delete horizontalHeaderItems.at(k);
+ horizontalHeaderItems[k] = 0;
+ }
+ }
+ clearContents();
+}
+
+void QTableModel::clearContents()
+{
+ for (int i = 0; i < tableItems.count(); ++i) {
+ if (tableItems.at(i)) {
+ tableItems.at(i)->view = 0;
+ delete tableItems.at(i);
+ tableItems[i] = 0;
+ }
+ }
+ reset();
+}
+
+void QTableModel::itemChanged(QTableWidgetItem *item)
+{
+ if (!item)
+ return;
+ if (item->flags() & ItemIsHeaderItem) {
+ int row = verticalHeaderItems.indexOf(item);
+ if (row >= 0) {
+ emit headerDataChanged(Qt::Vertical, row, row);
+ } else {
+ int column = horizontalHeaderItems.indexOf(item);
+ if (column >= 0)
+ emit headerDataChanged(Qt::Horizontal, column, column);
+ }
+ } else {
+ QModelIndex idx = index(item);
+ if (idx.isValid())
+ emit dataChanged(idx, idx);
+ }
+}
+
+QTableWidgetItem* QTableModel::createItem() const
+{
+ return prototype ? prototype->clone() : new QTableWidgetItem;
+}
+
+const QTableWidgetItem *QTableModel::itemPrototype() const
+{
+ return prototype;
+}
+
+void QTableModel::setItemPrototype(const QTableWidgetItem *item)
+{
+ if (prototype != item) {
+ delete prototype;
+ prototype = item;
+ }
+}
+
+QStringList QTableModel::mimeTypes() const
+{
+ const QTableWidget *view = qobject_cast<const QTableWidget*>(QObject::parent());
+ return (view ? view->mimeTypes() : QStringList());
+}
+
+QMimeData *QTableModel::internalMimeData() const
+{
+ return QAbstractTableModel::mimeData(cachedIndexes);
+}
+
+QMimeData *QTableModel::mimeData(const QModelIndexList &indexes) const
+{
+ QList<QTableWidgetItem*> items;
+ for (int i = 0; i < indexes.count(); ++i)
+ items << item(indexes.at(i));
+ const QTableWidget *view = qobject_cast<const QTableWidget*>(QObject::parent());
+
+ // cachedIndexes is a little hack to avoid copying from QModelIndexList to
+ // QList<QTreeWidgetItem*> and back again in the view
+ cachedIndexes = indexes;
+ QMimeData *mimeData = (view ? view->mimeData(items) : 0);
+ cachedIndexes.clear();
+ return mimeData;
+}
+
+bool QTableModel::dropMimeData(const QMimeData *data, Qt::DropAction action,
+ int row , int column, const QModelIndex &index)
+{
+ if (index.isValid()) {
+ row = index.row();
+ column = index.column();
+ }else if (row == -1 || column == -1) { // The user dropped outside the table.
+ row = rowCount();
+ column = 0;
+ }
+
+ QTableWidget *view = qobject_cast<QTableWidget*>(QObject::parent());
+ return (view ? view->dropMimeData(row, column, data, action) : false);
+}
+
+Qt::DropActions QTableModel::supportedDropActions() const
+{
+ const QTableWidget *view = qobject_cast<const QTableWidget*>(QObject::parent());
+ return (view ? view->supportedDropActions() : Qt::DropActions(Qt::IgnoreAction));
+}
+
+/*!
+ \class QTableWidgetSelectionRange
+
+ \brief The QTableWidgetSelectionRange class provides a way to interact with
+ selection in a model without using model indexes and a selection model.
+
+ \ingroup model-view
+
+ The QTableWidgetSelectionRange class stores the top left and bottom
+ right rows and columns of a selection range in a table. The
+ selections in the table may consist of several selection ranges.
+
+ \note If the item within the selection range is marked as not selectable,
+ e.g., \c{itemFlags() & Qt::ItemIsSelectable == 0} then it will not appear
+ in the selection range.
+
+ \sa QTableWidget
+*/
+
+/*!
+ Constructs an table selection range, i.e. a range
+ whose rowCount() and columnCount() are 0.
+*/
+QTableWidgetSelectionRange::QTableWidgetSelectionRange()
+ : top(-1), left(-1), bottom(-2), right(-2)
+{
+}
+
+/*!
+ Constructs the table selection range from the given \a top, \a
+ left, \a bottom and \a right table rows and columns.
+
+ \sa topRow(), leftColumn(), bottomRow(), rightColumn()
+*/
+QTableWidgetSelectionRange::QTableWidgetSelectionRange(int top, int left, int bottom, int right)
+ : top(top), left(left), bottom(bottom), right(right)
+{
+}
+
+/*!
+ Constructs a the table selection range by copying the given \a
+ other table selection range.
+*/
+QTableWidgetSelectionRange::QTableWidgetSelectionRange(const QTableWidgetSelectionRange &other)
+ : top(other.top), left(other.left), bottom(other.bottom), right(other.right)
+{
+}
+
+/*!
+ Destroys the table selection range.
+*/
+QTableWidgetSelectionRange::~QTableWidgetSelectionRange()
+{
+}
+
+/*!
+ \fn int QTableWidgetSelectionRange::topRow() const
+
+ Returns the top row of the range.
+
+ \sa bottomRow(), leftColumn(), rowCount()
+*/
+
+/*!
+ \fn int QTableWidgetSelectionRange::bottomRow() const
+
+ Returns the bottom row of the range.
+
+ \sa topRow(), rightColumn(), rowCount()
+*/
+
+/*!
+ \fn int QTableWidgetSelectionRange::leftColumn() const
+
+ Returns the left column of the range.
+
+ \sa rightColumn(), topRow(), columnCount()
+*/
+
+/*!
+ \fn int QTableWidgetSelectionRange::rightColumn() const
+
+ Returns the right column of the range.
+
+ \sa leftColumn(), bottomRow(), columnCount()
+*/
+
+/*!
+ \since 4.1
+ \fn int QTableWidgetSelectionRange::rowCount() const
+
+ Returns the number of rows in the range.
+
+ This is equivalent to bottomRow() - topRow() + 1.
+
+ \sa columnCount(), topRow(), bottomRow()
+*/
+
+/*!
+ \since 4.1
+ \fn int QTableWidgetSelectionRange::columnCount() const
+
+ Returns the number of columns in the range.
+
+ This is equivalent to rightColumn() - leftColumn() + 1.
+
+ \sa rowCount(), leftColumn(), rightColumn()
+*/
+
+/*!
+ \class QTableWidgetItem
+ \brief The QTableWidgetItem class provides an item for use with the
+ QTableWidget class.
+
+ \ingroup model-view
+
+ Table items are used to hold pieces of information for table widgets.
+ Items usually contain text, icons, or checkboxes
+
+ The QTableWidgetItem class is a convenience class that replaces the
+ \c QTableItem class in Qt 3. It provides an item for use with
+ the QTableWidget class.
+
+ Top-level items are constructed without a parent then inserted at the
+ position specified by a pair of row and column numbers:
+
+ \snippet doc/src/snippets/qtablewidget-using/mainwindow.cpp 3
+
+ Each item can have its own background brush which is set with
+ the setBackground() function. The current background brush can be
+ found with background().
+ The text label for each item can be rendered with its own font and brush.
+ These are specified with the setFont() and setForeground() functions,
+ and read with font() and foreground().
+
+ By default, items are enabled, editable, selectable, checkable, and can be
+ used both as the source of a drag and drop operation and as a drop target.
+ Each item's flags can be changed by calling setFlags() with the appropriate
+ value (see \l{Qt::ItemFlags}). Checkable items can be checked and unchecked
+ with the setCheckState() function. The corresponding checkState() function
+ indicates whether the item is currently checked.
+
+ \section1 Subclassing
+
+ When subclassing QTableWidgetItem to provide custom items, it is possible to
+ define new types for them so that they can be distinguished from standard
+ items. The constructors for subclasses that require this feature need to
+ call the base class constructor with a new type value equal to or greater
+ than \l UserType.
+
+ \sa QTableWidget, {Model/View Programming}, QListWidgetItem, QTreeWidgetItem
+*/
+
+/*!
+ \fn int QTableWidgetItem::row() const
+ \since 4.2
+
+ Returns the row of the item in the table.
+ If the item is not in a table, this function will return -1.
+
+ \sa column()
+*/
+
+/*!
+ \fn int QTableWidgetItem::column() const
+ \since 4.2
+
+ Returns the column of the item in the table.
+ If the item is not in a table, this function will return -1.
+
+ \sa row()
+*/
+
+/*!
+ \fn void QTableWidgetItem::setSelected(bool select)
+ \since 4.2
+
+ Sets the selected state of the item to \a select.
+
+ \sa isSelected()
+*/
+
+/*!
+ \fn bool QTableWidgetItem::isSelected() const
+ \since 4.2
+
+ Returns true if the item is selected, otherwise returns false.
+
+ \sa setSelected()
+*/
+
+/*!
+ \fn QSize QTableWidgetItem::sizeHint() const
+ \since 4.1
+
+ Returns the size hint set for the table item.
+*/
+
+/*!
+ \fn void QTableWidgetItem::setSizeHint(const QSize &size)
+ \since 4.1
+
+ Sets the size hint for the table item to be \a size.
+ If no size hint is set, the item delegate will compute the
+ size hint based on the item data.
+*/
+
+/*!
+ \fn Qt::CheckState QTableWidgetItem::checkState() const
+
+ Returns the checked state of the table item.
+
+ \sa flags()
+*/
+
+/*!
+ \fn void QTableWidgetItem::setCheckState(Qt::CheckState state)
+
+ Sets the check state of the table item to be \a state.
+*/
+
+/*!
+ \fn QTableWidget *QTableWidgetItem::tableWidget() const
+
+ Returns the table widget that contains the item.
+*/
+
+/*!
+ \fn Qt::ItemFlags QTableWidgetItem::flags() const
+
+ Returns the flags used to describe the item. These determine whether
+ the item can be checked, edited, and selected.
+
+ \sa setFlags()
+*/
+
+/*!
+ \fn void QTableWidgetItem::setFlags(Qt::ItemFlags flags)
+
+ Sets the flags for the item to the given \a flags. These determine whether
+ the item can be selected or modified.
+
+ \sa flags()
+*/
+void QTableWidgetItem::setFlags(Qt::ItemFlags aflags)
+{
+ itemFlags = aflags;
+ if (QTableModel *model = (view ? qobject_cast<QTableModel*>(view->model()) : 0))
+ model->itemChanged(this);
+}
+
+
+/*!
+ \fn QString QTableWidgetItem::text() const
+
+ Returns the item's text.
+
+ \sa setText()
+*/
+
+/*!
+ \fn void QTableWidgetItem::setText(const QString &text)
+
+ Sets the item's text to the \a text specified.
+
+ \sa text() setFont() setForeground()
+*/
+
+/*!
+ \fn QIcon QTableWidgetItem::icon() const
+
+ Returns the item's icon.
+
+ \sa setIcon(), {QAbstractItemView::iconSize}{iconSize}
+*/
+
+/*!
+ \fn void QTableWidgetItem::setIcon(const QIcon &icon)
+
+ Sets the item's icon to the \a icon specified.
+
+ \sa icon(), setText(), {QAbstractItemView::iconSize}{iconSize}
+*/
+
+/*!
+ \fn QString QTableWidgetItem::statusTip() const
+
+ Returns the item's status tip.
+
+ \sa setStatusTip()
+*/
+
+/*!
+ \fn void QTableWidgetItem::setStatusTip(const QString &statusTip)
+
+ Sets the status tip for the table item to the text specified by
+ \a statusTip. QTableWidget mouse tracking needs to be enabled for this
+ feature to work.
+
+ \sa statusTip() setToolTip() setWhatsThis()
+*/
+
+/*!
+ \fn QString QTableWidgetItem::toolTip() const
+
+ Returns the item's tooltip.
+
+ \sa setToolTip()
+*/
+
+/*!
+ \fn void QTableWidgetItem::setToolTip(const QString &toolTip)
+
+ Sets the item's tooltip to the string specified by \a toolTip.
+
+ \sa toolTip() setStatusTip() setWhatsThis()
+*/
+
+/*!
+ \fn QString QTableWidgetItem::whatsThis() const
+
+ Returns the item's "What's This?" help.
+
+ \sa setWhatsThis()
+*/
+
+/*!
+ \fn void QTableWidgetItem::setWhatsThis(const QString &whatsThis)
+
+ Sets the item's "What's This?" help to the string specified by \a whatsThis.
+
+ \sa whatsThis() setStatusTip() setToolTip()
+*/
+
+/*!
+ \fn QFont QTableWidgetItem::font() const
+
+ Returns the font used to render the item's text.
+
+ \sa setFont()
+*/
+
+/*!
+ \fn void QTableWidgetItem::setFont(const QFont &font)
+
+ Sets the font used to display the item's text to the given \a font.
+
+ \sa font() setText() setForeground()
+*/
+
+/*!
+ \fn QColor QTableWidgetItem::backgroundColor() const
+ \obsolete
+
+ This function is deprecated. Use background() instead.
+*/
+
+/*!
+ \fn void QTableWidgetItem::setBackgroundColor(const QColor &color)
+ \obsolete
+
+ This function is deprecated. Use setBackground() instead.
+*/
+
+/*!
+ \fn QBrush QTableWidgetItem::background() const
+ \since 4.2
+
+ Returns the brush used to render the item's background.
+
+ \sa foreground()
+*/
+
+/*!
+ \fn void QTableWidgetItem::setBackground(const QBrush &brush)
+ \since 4.2
+
+ Sets the item's background brush to the specified \a brush.
+
+ \sa setForeground()
+*/
+
+/*!
+ \fn QColor QTableWidgetItem::textColor() const
+ \obsolete
+
+ This function is deprecated. Use foreground() instead.
+*/
+
+/*!
+ \fn void QTableWidgetItem::setTextColor(const QColor &color)
+ \obsolete
+
+ This function is deprecated. Use setForeground() instead.
+*/
+
+/*!
+ \fn QBrush QTableWidgetItem::foreground() const
+ \since 4.2
+
+ Returns the brush used to render the item's foreground (e.g. text).
+
+ \sa background()
+*/
+
+/*!
+ \fn void QTableWidgetItem::setForeground(const QBrush &brush)
+ \since 4.2
+
+ Sets the item's foreground brush to the specified \a brush.
+
+ \sa setBackground()
+*/
+
+/*!
+ \fn int QTableWidgetItem::textAlignment() const
+
+ Returns the text alignment for the item's text.
+
+ \sa Qt::Alignment
+*/
+
+/*!
+ \fn void QTableWidgetItem::setTextAlignment(int alignment)
+
+ Sets the text alignment for the item's text to the \a alignment
+ specified.
+
+ \sa Qt::Alignment
+*/
+
+/*!
+ Constructs a table item of the specified \a type that does not belong
+ to any table.
+
+ \sa type()
+*/
+QTableWidgetItem::QTableWidgetItem(int type)
+ : rtti(type), view(0), d(new QTableWidgetItemPrivate(this)),
+ itemFlags(Qt::ItemIsEditable
+ |Qt::ItemIsSelectable
+ |Qt::ItemIsUserCheckable
+ |Qt::ItemIsEnabled
+ |Qt::ItemIsDragEnabled
+ |Qt::ItemIsDropEnabled)
+{
+}
+
+/*!
+ Constructs a table item with the given \a text.
+
+ \sa type()
+*/
+QTableWidgetItem::QTableWidgetItem(const QString &text, int type)
+ : rtti(type), view(0), d(new QTableWidgetItemPrivate(this)),
+ itemFlags(Qt::ItemIsEditable
+ |Qt::ItemIsSelectable
+ |Qt::ItemIsUserCheckable
+ |Qt::ItemIsEnabled
+ |Qt::ItemIsDragEnabled
+ |Qt::ItemIsDropEnabled)
+{
+ setData(Qt::DisplayRole, text);
+}
+
+/*!
+ Constructs a table item with the given \a icon and \a text.
+
+ \sa type()
+*/
+QTableWidgetItem::QTableWidgetItem(const QIcon &icon, const QString &text, int type)
+ : rtti(type), view(0), d(new QTableWidgetItemPrivate(this)),
+ itemFlags(Qt::ItemIsEditable
+ |Qt::ItemIsSelectable
+ |Qt::ItemIsUserCheckable
+ |Qt::ItemIsEnabled
+ |Qt::ItemIsDragEnabled
+ |Qt::ItemIsDropEnabled)
+{
+ setData(Qt::DecorationRole, icon);
+ setData(Qt::DisplayRole, text);
+}
+
+/*!
+ Destroys the table item.
+*/
+QTableWidgetItem::~QTableWidgetItem()
+{
+ if (QTableModel *model = (view ? qobject_cast<QTableModel*>(view->model()) : 0))
+ model->removeItem(this);
+ view = 0;
+ delete d;
+}
+
+/*!
+ Creates a copy of the item.
+*/
+QTableWidgetItem *QTableWidgetItem::clone() const
+{
+ return new QTableWidgetItem(*this);
+}
+
+/*!
+ Sets the item's data for the given \a role to the specified \a value.
+
+ \sa Qt::ItemDataRole, data()
+*/
+void QTableWidgetItem::setData(int role, const QVariant &value)
+{
+ bool found = false;
+ role = (role == Qt::EditRole ? Qt::DisplayRole : role);
+ for (int i = 0; i < values.count(); ++i) {
+ if (values.at(i).role == role) {
+ if (values[i].value == value)
+ return;
+
+ values[i].value = value;
+ found = true;
+ break;
+ }
+ }
+ if (!found)
+ values.append(QWidgetItemData(role, value));
+ if (QTableModel *model = (view ? qobject_cast<QTableModel*>(view->model()) : 0))
+ model->itemChanged(this);
+}
+
+/*!
+ Returns the item's data for the given \a role.
+*/
+QVariant QTableWidgetItem::data(int role) const
+{
+ role = (role == Qt::EditRole ? Qt::DisplayRole : role);
+ for (int i = 0; i < values.count(); ++i)
+ if (values.at(i).role == role)
+ return values.at(i).value;
+ return QVariant();
+}
+
+/*!
+ Returns true if the item is less than the \a other item; otherwise returns
+ false.
+*/
+bool QTableWidgetItem::operator<(const QTableWidgetItem &other) const
+{
+ const QVariant v1 = data(Qt::DisplayRole), v2 = other.data(Qt::DisplayRole);
+ if (QTableModel::canConvertToDouble(v1) && QTableModel::canConvertToDouble(v2))
+ return v1.toDouble() < v2.toDouble();
+ return v1.toString() < v2.toString();
+}
+
+#ifndef QT_NO_DATASTREAM
+
+/*!
+ Reads the item from stream \a in.
+
+ \sa write()
+*/
+void QTableWidgetItem::read(QDataStream &in)
+{
+ in >> values;
+}
+
+/*!
+ Writes the item to stream \a out.
+
+ \sa read()
+*/
+void QTableWidgetItem::write(QDataStream &out) const
+{
+ out << values;
+}
+
+/*!
+ \relates QTableWidgetItem
+
+ Reads a table widget item from stream \a in into \a item.
+
+ This operator uses QTableWidgetItem::read().
+
+ \sa {Format of the QDataStream Operators}
+*/
+QDataStream &operator>>(QDataStream &in, QTableWidgetItem &item)
+{
+ item.read(in);
+ return in;
+}
+
+/*!
+ \relates QTableWidgetItem
+
+ Writes the table widget item \a item to stream \a out.
+
+ This operator uses QTableWidgetItem::write().
+
+ \sa {Format of the QDataStream Operators}
+*/
+QDataStream &operator<<(QDataStream &out, const QTableWidgetItem &item)
+{
+ item.write(out);
+ return out;
+}
+
+#endif // QT_NO_DATASTREAM
+
+/*!
+ \since 4.1
+
+ Constructs a copy of \a other. Note that type() and tableWidget()
+ are not copied.
+
+ This function is useful when reimplementing clone().
+
+ \sa data(), flags()
+*/
+QTableWidgetItem::QTableWidgetItem(const QTableWidgetItem &other)
+ : rtti(Type), values(other.values), view(0),
+ d(new QTableWidgetItemPrivate(this)),
+ itemFlags(other.itemFlags)
+{
+}
+
+/*!
+ Assigns \a other's data and flags to this item. Note that type()
+ and tableWidget() are not copied.
+
+ This function is useful when reimplementing clone().
+
+ \sa data(), flags()
+*/
+QTableWidgetItem &QTableWidgetItem::operator=(const QTableWidgetItem &other)
+{
+ values = other.values;
+ itemFlags = other.itemFlags;
+ return *this;
+}
+
+/*!
+ \class QTableWidget
+ \brief The QTableWidget class provides an item-based table view with a default model.
+
+ \ingroup model-view
+ \mainclass
+
+ Table widgets provide standard table display facilities for applications.
+ The items in a QTableWidget are provided by QTableWidgetItem.
+
+ If you want a table that uses your own data model you should
+ use QTableView rather than this class.
+
+ Table widgets can be constructed with the required numbers of rows and
+ columns:
+
+ \snippet doc/src/snippets/qtablewidget-using/mainwindow.cpp 0
+
+ Alternatively, tables can be constructed without a given size and resized
+ later:
+
+ \snippet doc/src/snippets/qtablewidget-resizing/mainwindow.cpp 0
+ \snippet doc/src/snippets/qtablewidget-resizing/mainwindow.cpp 1
+
+ Items are created ouside the table (with no parent widget) and inserted
+ into the table with setItem():
+
+ \snippet doc/src/snippets/qtablewidget-resizing/mainwindow.cpp 2
+
+ If you want to enable sorting in your table widget, do so after you
+ have populated it with items, otherwise sorting may interfere with
+ the insertion order (see setItem() for details).
+
+ Tables can be given both horizontal and vertical headers. The simplest way
+ to create the headers is to supply a list of strings to the
+ setHorizontalHeaderLabels() and setVerticalHeaderLabels() functions. These
+ will provide simple textual headers for the table's columns and rows.
+ More sophisticated headers can be created from existing table items
+ that are usually constructed outside the table. For example, we can
+ construct a table item with an icon and aligned text, and use it as the
+ header for a particular column:
+
+ \snippet doc/src/snippets/qtablewidget-using/mainwindow.cpp 2
+
+ The number of rows in the table can be found with rowCount(), and the
+ number of columns with columnCount(). The table can be cleared with the
+ clear() function.
+
+ \table 100%
+ \row \o \inlineimage windowsxp-tableview.png Screenshot of a Windows XP style table widget
+ \o \inlineimage macintosh-tableview.png Screenshot of a Macintosh style table widget
+ \o \inlineimage plastique-tableview.png Screenshot of a Plastique style table widget
+ \row \o A \l{Windows XP Style Widget Gallery}{Windows XP style} table widget.
+ \o A \l{Macintosh Style Widget Gallery}{Macintosh style} table widget.
+ \o A \l{Plastique Style Widget Gallery}{Plastique style} table widget.
+ \endtable
+
+ \sa QTableWidgetItem, QTableView, {Model/View Programming}
+*/
+
+/*!
+ \property QTableWidget::rowCount
+ \brief the number of rows in the table
+
+ By default, for a table constructed without row and column counts,
+ this property contains a value of 0.
+*/
+
+/*!
+ \property QTableWidget::columnCount
+ \brief the number of columns in the table
+
+ By default, for a table constructed without row and column counts,
+ this property contains a value of 0.
+*/
+
+void QTableWidgetPrivate::setup()
+{
+ Q_Q(QTableWidget);
+ // view signals
+ QObject::connect(q, SIGNAL(pressed(QModelIndex)), q, SLOT(_q_emitItemPressed(QModelIndex)));
+ QObject::connect(q, SIGNAL(clicked(QModelIndex)), q, SLOT(_q_emitItemClicked(QModelIndex)));
+ QObject::connect(q, SIGNAL(doubleClicked(QModelIndex)),
+ q, SLOT(_q_emitItemDoubleClicked(QModelIndex)));
+ QObject::connect(q, SIGNAL(activated(QModelIndex)), q, SLOT(_q_emitItemActivated(QModelIndex)));
+ QObject::connect(q, SIGNAL(entered(QModelIndex)), q, SLOT(_q_emitItemEntered(QModelIndex)));
+ // model signals
+ QObject::connect(model(), SIGNAL(dataChanged(QModelIndex,QModelIndex)),
+ q, SLOT(_q_emitItemChanged(QModelIndex)));
+ // selection signals
+ QObject::connect(q->selectionModel(), SIGNAL(currentChanged(QModelIndex,QModelIndex)),
+ q, SLOT(_q_emitCurrentItemChanged(QModelIndex,QModelIndex)));
+ QObject::connect(q->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)),
+ q, SIGNAL(itemSelectionChanged()));
+ // sorting
+ QObject::connect(model(), SIGNAL(dataChanged(QModelIndex,QModelIndex)),
+ q, SLOT(_q_dataChanged(QModelIndex,QModelIndex)));
+ QObject::connect(model(), SIGNAL(columnsRemoved(QModelIndex,int,int)), q, SLOT(_q_sort()));
+}
+
+void QTableWidgetPrivate::_q_emitItemPressed(const QModelIndex &index)
+{
+ Q_Q(QTableWidget);
+ if (QTableWidgetItem *item = model()->item(index))
+ emit q->itemPressed(item);
+ emit q->cellPressed(index.row(), index.column());
+}
+
+void QTableWidgetPrivate::_q_emitItemClicked(const QModelIndex &index)
+{
+ Q_Q(QTableWidget);
+ if (QTableWidgetItem *item = model()->item(index))
+ emit q->itemClicked(item);
+ emit q->cellClicked(index.row(), index.column());
+}
+
+void QTableWidgetPrivate::_q_emitItemDoubleClicked(const QModelIndex &index)
+{
+ Q_Q(QTableWidget);
+ if (QTableWidgetItem *item = model()->item(index))
+ emit q->itemDoubleClicked(item);
+ emit q->cellDoubleClicked(index.row(), index.column());
+}
+
+void QTableWidgetPrivate::_q_emitItemActivated(const QModelIndex &index)
+{
+ Q_Q(QTableWidget);
+ if (QTableWidgetItem *item = model()->item(index))
+ emit q->itemActivated(item);
+ emit q->cellActivated(index.row(), index.column());
+}
+
+void QTableWidgetPrivate::_q_emitItemEntered(const QModelIndex &index)
+{
+ Q_Q(QTableWidget);
+ if (QTableWidgetItem *item = model()->item(index))
+ emit q->itemEntered(item);
+ emit q->cellEntered(index.row(), index.column());
+}
+
+void QTableWidgetPrivate::_q_emitItemChanged(const QModelIndex &index)
+{
+ Q_Q(QTableWidget);
+ if (QTableWidgetItem *item = model()->item(index))
+ emit q->itemChanged(item);
+ emit q->cellChanged(index.row(), index.column());
+}
+
+void QTableWidgetPrivate::_q_emitCurrentItemChanged(const QModelIndex &current,
+ const QModelIndex &previous)
+{
+ Q_Q(QTableWidget);
+ QTableWidgetItem *currentItem = model()->item(current);
+ QTableWidgetItem *previousItem = model()->item(previous);
+ if (currentItem || previousItem)
+ emit q->currentItemChanged(currentItem, previousItem);
+ emit q->currentCellChanged(current.row(), current.column(), previous.row(), previous.column());
+}
+
+void QTableWidgetPrivate::_q_sort()
+{
+ Q_Q(QTableWidget);
+ if (sortingEnabled) {
+ int column = q->horizontalHeader()->sortIndicatorSection();
+ Qt::SortOrder order = q->horizontalHeader()->sortIndicatorOrder();
+ model()->sort(column, order);
+ }
+}
+
+void QTableWidgetPrivate::_q_dataChanged(const QModelIndex &topLeft,
+ const QModelIndex &bottomRight)
+{
+ Q_Q(QTableWidget);
+ if (sortingEnabled && topLeft.isValid() && bottomRight.isValid()) {
+ int column = q->horizontalHeader()->sortIndicatorSection();
+ if (column >= topLeft.column() && column <= bottomRight.column()) {
+ Qt::SortOrder order = q->horizontalHeader()->sortIndicatorOrder();
+ model()->ensureSorted(column, order, topLeft.row(), bottomRight.row());
+ }
+ }
+}
+
+/*!
+ \fn void QTableWidget::itemPressed(QTableWidgetItem *item)
+
+ This signal is emitted whenever an item in the table is pressed.
+ The \a item specified is the item that was pressed.
+*/
+
+/*!
+ \fn void QTableWidget::itemClicked(QTableWidgetItem *item)
+
+ This signal is emitted whenever an item in the table is clicked.
+ The \a item specified is the item that was clicked.
+*/
+
+/*!
+ \fn void QTableWidget::itemDoubleClicked(QTableWidgetItem *item)
+
+ This signal is emitted whenever an item in the table is double
+ clicked. The \a item specified is the item that was double clicked.
+*/
+
+/*!
+ \fn void QTableWidget::itemActivated(QTableWidgetItem *item)
+
+ This signal is emitted when the specified \a item has been activated
+*/
+
+/*!
+ \fn void QTableWidget::itemEntered(QTableWidgetItem *item)
+
+ This signal is emitted when the mouse cursor enters an item. The
+ \a item is the item entered.
+
+ This signal is only emitted when mouseTracking is turned on, or when a
+ mouse button is pressed while moving into an item.
+*/
+
+/*!
+ \fn void QTableWidget::itemChanged(QTableWidgetItem *item)
+
+ This signal is emitted whenever the data of \a item has changed.
+*/
+
+/*!
+ \fn void QTableWidget::currentItemChanged(QTableWidgetItem *current, QTableWidgetItem *previous)
+
+ This signal is emitted whenever the current item changes. The \a
+ previous item is the item that previously had the focus, \a
+ current is the new current item.
+*/
+
+/*!
+ \fn void QTableWidget::itemSelectionChanged()
+
+ This signal is emitted whenever the selection changes.
+
+ \sa selectedItems() isItemSelected()
+*/
+
+
+/*!
+ \since 4.1
+ \fn void QTableWidget::cellPressed(int row, int column)
+
+ This signal is emitted whenever a cell in the table is pressed.
+ The \a row and \a column specified is the cell that was pressed.
+*/
+
+/*!
+ \since 4.1
+ \fn void QTableWidget::cellClicked(int row, int column)
+
+ This signal is emitted whenever a cell in the table is clicked.
+ The \a row and \a column specified is the cell that was clicked.
+*/
+
+/*!
+ \since 4.1
+ \fn void QTableWidget::cellDoubleClicked(int row, int column)
+
+ This signal is emitted whenever a cell in the table is double
+ clicked. The \a row and \a column specified is the cell that was
+ double clicked.
+*/
+
+/*!
+ \since 4.1
+ \fn void QTableWidget::cellActivated(int row, int column)
+
+ This signal is emitted when the cell specified by \a row and \a column
+ has been activated
+*/
+
+/*!
+ \since 4.1
+ \fn void QTableWidget::cellEntered(int row, int column)
+
+ This signal is emitted when the mouse cursor enters a cell. The
+ cell is specified by \a row and \a column.
+
+ This signal is only emitted when mouseTracking is turned on, or when a
+ mouse button is pressed while moving into an item.
+*/
+
+/*!
+ \since 4.1
+ \fn void QTableWidget::cellChanged(int row, int column)
+
+ This signal is emitted whenever the data of the item in the cell
+ specified by \a row and \a column has changed.
+*/
+
+/*!
+ \since 4.1
+ \fn void QTableWidget::currentCellChanged(int currentRow, int currentColumn, int previousRow, int previousColumn)
+
+ This signal is emitted whenever the current cell changes. The cell
+ specified by \a previousRow and \a previousColumn is the cell that
+ previously had the focus, the cell specified by \a currentRow and \a
+ currentColumn is the new current cell.
+*/
+
+/*!
+ \since 4.3
+ \fn void QTableWidget::removeCellWidget(int row, int column)
+
+ Removes the widget set on the cell indicated by \a row and \a column.
+*/
+
+/*!
+ \fn QTableWidgetItem *QTableWidget::itemAt(int ax, int ay) const
+
+ Returns the item at the position equivalent to QPoint(\a{ax}, \a{ay}) in
+ the table widget's coordinate system, or returns 0 if the specified point
+ is not covered by an item in the table widget.
+
+ \sa item()
+*/
+
+/*!
+ \enum QTableWidgetItem::ItemType
+
+ This enum describes the types that are used to describe table widget items.
+
+ \value Type The default type for table widget items.
+ \value UserType The minimum value for custom types. Values below UserType are
+ reserved by Qt.
+
+ You can define new user types in QTableWidgetItem subclasses to ensure that
+ custom items are treated specially.
+
+ \sa type()
+*/
+
+/*!
+ \fn int QTableWidgetItem::type() const
+
+ Returns the type passed to the QTableWidgetItem constructor.
+*/
+
+/*!
+ Creates a new table view with the given \a parent.
+*/
+QTableWidget::QTableWidget(QWidget *parent)
+ : QTableView(*new QTableWidgetPrivate, parent)
+{
+ Q_D(QTableWidget);
+ QTableView::setModel(new QTableModel(0, 0, this));
+ d->setup();
+}
+
+/*!
+ Creates a new table view with the given \a rows and \a columns, and with the given \a parent.
+*/
+QTableWidget::QTableWidget(int rows, int columns, QWidget *parent)
+ : QTableView(*new QTableWidgetPrivate, parent)
+{
+ Q_D(QTableWidget);
+ QTableView::setModel(new QTableModel(rows, columns, this));
+ d->setup();
+}
+
+/*!
+ Destroys this QTableWidget.
+*/
+QTableWidget::~QTableWidget()
+{
+}
+
+/*!
+ Sets the number of rows in this table's model to \a rows. If
+ this is less than rowCount(), the data in the unwanted rows
+ is discarded.
+
+ \sa setColumnCount()
+*/
+void QTableWidget::setRowCount(int rows)
+{
+ Q_D(QTableWidget);
+ d->model()->setRowCount(rows);
+}
+
+/*!
+ Returns the number of rows.
+*/
+
+int QTableWidget::rowCount() const
+{
+ Q_D(const QTableWidget);
+ return d->model()->rowCount();
+}
+
+/*!
+ Sets the number of columns in this table's model to \a columns. If
+ this is less than columnCount(), the data in the unwanted columns
+ is discarded.
+
+ \sa setRowCount()
+*/
+void QTableWidget::setColumnCount(int columns)
+{
+ Q_D(QTableWidget);
+ d->model()->setColumnCount(columns);
+}
+
+/*!
+ Returns the number of columns.
+*/
+
+int QTableWidget::columnCount() const
+{
+ Q_D(const QTableWidget);
+ return d->model()->columnCount();
+}
+
+/*!
+ Returns the row for the \a item.
+*/
+int QTableWidget::row(const QTableWidgetItem *item) const
+{
+ Q_D(const QTableWidget);
+ return d->model()->index(item).row();
+}
+
+/*!
+ Returns the column for the \a item.
+*/
+int QTableWidget::column(const QTableWidgetItem *item) const
+{
+ Q_D(const QTableWidget);
+ return d->model()->index(item).column();
+}
+
+
+/*!
+ Returns the item for the given \a row and \a column if one has been set; otherwise
+ returns 0.
+
+ \sa setItem()
+*/
+QTableWidgetItem *QTableWidget::item(int row, int column) const
+{
+ Q_D(const QTableWidget);
+ return d->model()->item(row, column);
+}
+
+/*!
+ Sets the item for the given \a row and \a column to \a item.
+
+ The table takes ownership of the item.
+
+ Note that if sorting is enabled (see
+ \l{QTableView::sortingEnabled} {sortingEnabled}) and \a column is
+ the current sort column, the \a row will be moved to the sorted
+ position determined by \a item.
+
+ If you want to set several items of a particular row (say, by
+ calling setItem() in a loop), you may want to turn off sorting
+ before doing so, and turn it back on afterwards; this will allow
+ you to use the same \a row argument for all items in the same row
+ (i.e. setItem() will not move the row).
+
+ \sa item() takeItem()
+*/
+void QTableWidget::setItem(int row, int column, QTableWidgetItem *item)
+{
+ Q_D(QTableWidget);
+ if (item) {
+ if (item->view != 0) {
+ qWarning("QTableWidget: cannot insert an item that is already owned by another QTableWidget");
+ } else {
+ item->view = this;
+ d->model()->setItem(row, column, item);
+ }
+ } else {
+ delete takeItem(row, column);
+ }
+}
+
+/*!
+ Removes the item at \a row and \a column from the table without deleting it.
+*/
+QTableWidgetItem *QTableWidget::takeItem(int row, int column)
+{
+ Q_D(QTableWidget);
+ QTableWidgetItem *item = d->model()->takeItem(row, column);
+ if (item)
+ item->view = 0;
+ return item;
+}
+
+/*!
+ Returns the vertical header item for row \a row.
+*/
+QTableWidgetItem *QTableWidget::verticalHeaderItem(int row) const
+{
+ Q_D(const QTableWidget);
+ return d->model()->verticalHeaderItem(row);
+}
+
+/*!
+ Sets the vertical header item for row \a row to \a item.
+*/
+void QTableWidget::setVerticalHeaderItem(int row, QTableWidgetItem *item)
+{
+ Q_D(QTableWidget);
+ if (item) {
+ item->view = this;
+ d->model()->setVerticalHeaderItem(row, item);
+ } else {
+ delete takeVerticalHeaderItem(row);
+ }
+}
+
+/*!
+ \since 4.1
+ Removes the vertical header item at \a row from the header without deleting it.
+*/
+QTableWidgetItem *QTableWidget::takeVerticalHeaderItem(int row)
+{
+ Q_D(QTableWidget);
+ QTableWidgetItem *itm = d->model()->takeVerticalHeaderItem(row);
+ if (itm)
+ itm->view = 0;
+ return itm;
+}
+
+/*!
+ Returns the horizontal header item for column, \a column, if one has been
+ set; otherwise returns 0.
+*/
+QTableWidgetItem *QTableWidget::horizontalHeaderItem(int column) const
+{
+ Q_D(const QTableWidget);
+ return d->model()->horizontalHeaderItem(column);
+}
+
+/*!
+ Sets the horizontal header item for column \a column to \a item.
+*/
+void QTableWidget::setHorizontalHeaderItem(int column, QTableWidgetItem *item)
+{
+ Q_D(QTableWidget);
+ if (item) {
+ item->view = this;
+ d->model()->setHorizontalHeaderItem(column, item);
+ } else {
+ delete takeHorizontalHeaderItem(column);
+ }
+}
+
+/*!
+ \since 4.1
+ Removes the horizontal header item at \a column from the header without deleting it.
+*/
+QTableWidgetItem *QTableWidget::takeHorizontalHeaderItem(int column)
+{
+ Q_D(QTableWidget);
+ QTableWidgetItem *itm = d->model()->takeHorizontalHeaderItem(column);
+ if (itm)
+ itm->view = 0;
+ return itm;
+}
+
+/*!
+ Sets the vertical header labels using \a labels.
+*/
+void QTableWidget::setVerticalHeaderLabels(const QStringList &labels)
+{
+ Q_D(QTableWidget);
+ QTableModel *model = d->model();
+ QTableWidgetItem *item = 0;
+ for (int i = 0; i < model->rowCount() && i < labels.count(); ++i) {
+ item = model->verticalHeaderItem(i);
+ if (!item) {
+ item = model->createItem();
+ setVerticalHeaderItem(i, item);
+ }
+ item->setText(labels.at(i));
+ }
+}
+
+/*!
+ Sets the horizontal header labels using \a labels.
+*/
+void QTableWidget::setHorizontalHeaderLabels(const QStringList &labels)
+{
+ Q_D(QTableWidget);
+ QTableModel *model = d->model();
+ QTableWidgetItem *item = 0;
+ for (int i = 0; i < model->columnCount() && i < labels.count(); ++i) {
+ item = model->horizontalHeaderItem(i);
+ if (!item) {
+ item = model->createItem();
+ setHorizontalHeaderItem(i, item);
+ }
+ item->setText(labels.at(i));
+ }
+}
+
+/*!
+ Returns the row of the current item.
+
+ \sa currentColumn(), setCurrentCell()
+*/
+int QTableWidget::currentRow() const
+{
+ return currentIndex().row();
+}
+
+/*!
+ Returns the column of the current item.
+
+ \sa currentRow(), setCurrentCell()
+*/
+int QTableWidget::currentColumn() const
+{
+ return currentIndex().column();
+}
+
+/*!
+ Returns the current item.
+
+ \sa setCurrentItem()
+*/
+QTableWidgetItem *QTableWidget::currentItem() const
+{
+ Q_D(const QTableWidget);
+ return d->model()->item(currentIndex());
+}
+
+/*!
+ Sets the current item to \a item.
+
+ Depending on the current \l{QAbstractItemView::SelectionMode}{selection mode},
+ the item may also be selected.
+
+ \sa currentItem(), setCurrentCell()
+*/
+void QTableWidget::setCurrentItem(QTableWidgetItem *item)
+{
+ Q_D(QTableWidget);
+ setCurrentIndex(d->model()->index(item));
+}
+
+/*!
+ \since 4.4
+
+ Sets the current item to be \a item, using the given \a command.
+
+ \sa currentItem(), setCurrentCell()
+*/
+void QTableWidget::setCurrentItem(QTableWidgetItem *item, QItemSelectionModel::SelectionFlags command)
+{
+ Q_D(QTableWidget);
+ d->selectionModel->setCurrentIndex(d->model()->index(item), command);
+}
+
+/*!
+ \since 4.1
+
+ Sets the current cell to be the cell at position (\a row, \a
+ column).
+
+ Depending on the current \l{QAbstractItemView::SelectionMode}{selection mode},
+ the cell may also be selected.
+
+ \sa setCurrentItem(), currentRow(), currentColumn()
+*/
+void QTableWidget::setCurrentCell(int row, int column)
+{
+ setCurrentIndex(model()->index(row, column, QModelIndex()));
+}
+
+/*!
+ \since 4.4
+
+ Sets the current cell to be the cell at position (\a row, \a
+ column), using the given \a command.
+
+ \sa setCurrentItem(), currentRow(), currentColumn()
+*/
+void QTableWidget::setCurrentCell(int row, int column, QItemSelectionModel::SelectionFlags command)
+{
+ Q_D(QTableWidget);
+ d->selectionModel->setCurrentIndex(model()->index(row, column, QModelIndex()), command);
+}
+
+/*!
+ Sorts all the rows in the table widget based on \a column and \a order.
+*/
+void QTableWidget::sortItems(int column, Qt::SortOrder order)
+{
+ Q_D(QTableWidget);
+ d->model()->sort(column, order);
+ horizontalHeader()->setSortIndicator(column, order);
+}
+
+/*!
+ \internal
+*/
+void QTableWidget::setSortingEnabled(bool enable)
+{
+ QTableView::setSortingEnabled(enable);
+}
+
+/*!
+ \internal
+*/
+bool QTableWidget::isSortingEnabled() const
+{
+ return QTableView::isSortingEnabled();
+}
+
+/*!
+ Starts editing the \a item if it is editable.
+*/
+
+void QTableWidget::editItem(QTableWidgetItem *item)
+{
+ Q_D(QTableWidget);
+ if (!item)
+ return;
+ edit(d->model()->index(item));
+}
+
+/*!
+ Opens an editor for the give \a item. The editor remains open after editing.
+
+ \sa closePersistentEditor()
+*/
+void QTableWidget::openPersistentEditor(QTableWidgetItem *item)
+{
+ Q_D(QTableWidget);
+ if (!item)
+ return;
+ QModelIndex index = d->model()->index(item);
+ QAbstractItemView::openPersistentEditor(index);
+}
+
+/*!
+ Closes the persistent editor for \a item.
+
+ \sa openPersistentEditor()
+*/
+void QTableWidget::closePersistentEditor(QTableWidgetItem *item)
+{
+ Q_D(QTableWidget);
+ if (!item)
+ return;
+ QModelIndex index = d->model()->index(item);
+ QAbstractItemView::closePersistentEditor(index);
+}
+
+/*!
+ \since 4.1
+
+ Returns the widget displayed in the cell in the given \a row and \a column.
+
+ \note The table takes ownership of the widget.
+
+ \sa setCellWidget()
+*/
+QWidget *QTableWidget::cellWidget(int row, int column) const
+{
+ QModelIndex index = model()->index(row, column, QModelIndex());
+ return QAbstractItemView::indexWidget(index);
+}
+
+/*!
+ \since 4.1
+
+ Sets the given \a widget to be displayed in the cell in the given \a row
+ and \a column, passing the ownership of the widget to the table.
+
+ If cell widget A is replaced with cell widget B, cell widget A will be
+ deleted. For example, in the code snippet below, the QLineEdit object will
+ be deleted.
+
+ \snippet doc/src/snippets/code/src_gui_itemviews_qtablewidget.cpp 0
+
+ \sa cellWidget()
+*/
+void QTableWidget::setCellWidget(int row, int column, QWidget *widget)
+{
+ QModelIndex index = model()->index(row, column, QModelIndex());
+ QAbstractItemView::setIndexWidget(index, widget);
+}
+
+/*!
+ Returns true if the \a item is selected, otherwise returns false.
+
+ \obsolete
+
+ This function is deprecated. Use \l{QTableWidgetItem::isSelected()} instead.
+*/
+
+bool QTableWidget::isItemSelected(const QTableWidgetItem *item) const
+{
+ Q_D(const QTableWidget);
+ QModelIndex index = d->model()->index(item);
+ return selectionModel()->isSelected(index);
+}
+
+/*!
+ Selects or deselects \a item depending on \a select.
+
+ \obsolete
+
+ This function is deprecated. Use \l{QTableWidgetItem::setSelected()} instead.
+*/
+void QTableWidget::setItemSelected(const QTableWidgetItem *item, bool select)
+{
+ Q_D(QTableWidget);
+ QModelIndex index = d->model()->index(item);
+ selectionModel()->select(index, select ? QItemSelectionModel::Select : QItemSelectionModel::Deselect);
+}
+
+/*!
+ Selects or deselects the \a range depending on \a select.
+*/
+void QTableWidget::setRangeSelected(const QTableWidgetSelectionRange &range, bool select)
+{
+ if (!model()->hasIndex(range.topRow(), range.leftColumn(), rootIndex()) ||
+ !model()->hasIndex(range.bottomRow(), range.rightColumn(), rootIndex()))
+ return;
+
+ QModelIndex topLeft = model()->index(range.topRow(), range.leftColumn(), rootIndex());
+ QModelIndex bottomRight = model()->index(range.bottomRow(), range.rightColumn(), rootIndex());
+
+ selectionModel()->select(QItemSelection(topLeft, bottomRight),
+ select ? QItemSelectionModel::Select : QItemSelectionModel::Deselect);
+}
+
+/*!
+ Returns a list of all selected ranges.
+
+ \sa QTableWidgetSelectionRange
+*/
+
+QList<QTableWidgetSelectionRange> QTableWidget::selectedRanges() const
+{
+ const QList<QItemSelectionRange> ranges = selectionModel()->selection();
+ QList<QTableWidgetSelectionRange> result;
+ for (int i = 0; i < ranges.count(); ++i)
+ result.append(QTableWidgetSelectionRange(ranges.at(i).top(),
+ ranges.at(i).left(),
+ ranges.at(i).bottom(),
+ ranges.at(i).right()));
+ return result;
+}
+
+/*!
+ Returns a list of all selected items.
+
+ This function returns a list of pointers to the contents of the
+ selected cells. Use the selectedIndexes() function to retrieve the
+ complete selection \e including empty cells.
+
+ \sa selectedIndexes()
+*/
+
+QList<QTableWidgetItem*> QTableWidget::selectedItems()
+{
+ Q_D(QTableWidget);
+ QModelIndexList indexes = selectionModel()->selectedIndexes();
+ QList<QTableWidgetItem*> items;
+ for (int i = 0; i < indexes.count(); ++i) {
+ QModelIndex index = indexes.at(i);
+ if (isIndexHidden(index))
+ continue;
+ QTableWidgetItem *item = d->model()->item(index);
+ if (item)
+ items.append(item);
+ }
+ return items;
+}
+
+/*!
+ Finds items that matches the \a text using the given \a flags.
+*/
+
+QList<QTableWidgetItem*> QTableWidget::findItems(const QString &text, Qt::MatchFlags flags) const
+{
+ Q_D(const QTableWidget);
+ QModelIndexList indexes;
+ for (int column = 0; column < columnCount(); ++column)
+ indexes += d->model()->match(model()->index(0, column, QModelIndex()),
+ Qt::DisplayRole, text, -1, flags);
+ QList<QTableWidgetItem*> items;
+ for (int i = 0; i < indexes.size(); ++i)
+ items.append(d->model()->item(indexes.at(i)));
+ return items;
+}
+
+/*!
+ Returns the visual row of the given \a logicalRow.
+*/
+
+int QTableWidget::visualRow(int logicalRow) const
+{
+ return verticalHeader()->visualIndex(logicalRow);
+}
+
+/*!
+ Returns the visual column of the given \a logicalColumn.
+*/
+
+int QTableWidget::visualColumn(int logicalColumn) const
+{
+ return horizontalHeader()->visualIndex(logicalColumn);
+}
+
+/*!
+ \fn QTableWidgetItem *QTableWidget::itemAt(const QPoint &point) const
+
+ Returns a pointer to the item at the given \a point, or returns 0 if
+ \a point is not covered by an item in the table widget.
+
+ \sa item()
+*/
+
+QTableWidgetItem *QTableWidget::itemAt(const QPoint &p) const
+{
+ Q_D(const QTableWidget);
+ return d->model()->item(indexAt(p));
+}
+
+/*!
+ Returns the rectangle on the viewport occupied by the item at \a item.
+*/
+QRect QTableWidget::visualItemRect(const QTableWidgetItem *item) const
+{
+ Q_D(const QTableWidget);
+ if (!item)
+ return QRect();
+ QModelIndex index = d->model()->index(const_cast<QTableWidgetItem*>(item));
+ Q_ASSERT(index.isValid());
+ return visualRect(index);
+}
+
+/*!
+ Scrolls the view if necessary to ensure that the \a item is visible.
+ The \a hint parameter specifies more precisely where the
+ \a item should be located after the operation.
+*/
+
+void QTableWidget::scrollToItem(const QTableWidgetItem *item, QAbstractItemView::ScrollHint hint)
+{
+ Q_D(QTableWidget);
+ if (!item)
+ return;
+ QModelIndex index = d->model()->index(const_cast<QTableWidgetItem*>(item));
+ Q_ASSERT(index.isValid());
+ QTableView::scrollTo(index, hint);
+}
+
+/*!
+ Returns the item prototype used by the table.
+
+ \sa setItemPrototype()
+*/
+const QTableWidgetItem *QTableWidget::itemPrototype() const
+{
+ Q_D(const QTableWidget);
+ return d->model()->itemPrototype();
+}
+
+/*!
+ Sets the item prototype for the table to the specified \a item.
+
+ The table widget will use the item prototype clone function when it needs
+ to create a new table item. For example when the user is editing
+ editing in an empty cell. This is useful when you have a QTableWidgetItem
+ subclass and want to make sure that QTableWidget creates instances of
+ your subclass.
+
+ The table takes ownership of the prototype.
+
+ \sa itemPrototype()
+*/
+void QTableWidget::setItemPrototype(const QTableWidgetItem *item)
+{
+ Q_D(QTableWidget);
+ d->model()->setItemPrototype(item);
+}
+
+/*!
+ Inserts an empty row into the table at \a row.
+*/
+void QTableWidget::insertRow(int row)
+{
+ Q_D(QTableWidget);
+ d->model()->insertRows(row);
+}
+
+/*!
+ Inserts an empty column into the table at \a column.
+*/
+void QTableWidget::insertColumn(int column)
+{
+ Q_D(QTableWidget);
+ d->model()->insertColumns(column);
+}
+
+/*!
+ Removes the row \a row and all its items from the table.
+*/
+void QTableWidget::removeRow(int row)
+{
+ Q_D(QTableWidget);
+ d->model()->removeRows(row);
+}
+
+/*!
+ Removes the column \a column and all its items from the table.
+*/
+void QTableWidget::removeColumn(int column)
+{
+ Q_D(QTableWidget);
+ d->model()->removeColumns(column);
+}
+
+/*!
+ Removes all items in the view.
+ This will also remove all selections.
+ The table dimensions stay the same.
+*/
+
+void QTableWidget::clear()
+{
+ Q_D(QTableWidget);
+ selectionModel()->clear();
+ d->model()->clear();
+}
+
+/*!
+ \since 4.2
+
+ Removes all items not in the headers from the view.
+ This will also remove all selections.
+ The table dimensions stay the same.
+*/
+void QTableWidget::clearContents()
+{
+ Q_D(QTableWidget);
+ selectionModel()->clear();
+ d->model()->clearContents();
+}
+
+/*!
+ Returns a list of MIME types that can be used to describe a list of
+ tablewidget items.
+
+ \sa mimeData()
+*/
+QStringList QTableWidget::mimeTypes() const
+{
+ return d_func()->model()->QAbstractTableModel::mimeTypes();
+}
+
+/*!
+ Returns an object that contains a serialized description of the specified
+ \a items. The format used to describe the items is obtained from the
+ mimeTypes() function.
+
+ If the list of items is empty, 0 is returned rather than a serialized
+ empty list.
+*/
+QMimeData *QTableWidget::mimeData(const QList<QTableWidgetItem*>) const
+{
+ return d_func()->model()->internalMimeData();
+}
+
+/*!
+ Handles the \a data supplied by a drag and drop operation that ended with
+ the given \a action in the given \a row and \a column.
+ Returns true if the data and action can be handled by the model;
+ otherwise returns false.
+
+ \sa supportedDropActions()
+*/
+bool QTableWidget::dropMimeData(int row, int column, const QMimeData *data, Qt::DropAction action)
+{
+ QModelIndex idx;
+#ifndef QT_NO_DRAGANDDROP
+ if (dropIndicatorPosition() == QAbstractItemView::OnItem) {
+ // QAbstractTableModel::dropMimeData will overwrite on the index if row == -1 and column == -1
+ idx = model()->index(row, column);
+ row = -1;
+ column = -1;
+ }
+#endif
+ return d_func()->model()->QAbstractTableModel::dropMimeData(data, action , row, column, idx);
+}
+
+/*!
+ Returns the drop actions supported by this view.
+
+ \sa Qt::DropActions
+*/
+Qt::DropActions QTableWidget::supportedDropActions() const
+{
+ return d_func()->model()->QAbstractTableModel::supportedDropActions() | Qt::MoveAction;
+}
+
+/*!
+ Returns a list of pointers to the items contained in the \a data object.
+ If the object was not created by a QTreeWidget in the same process, the list
+ is empty.
+
+*/
+QList<QTableWidgetItem*> QTableWidget::items(const QMimeData *data) const
+{
+ const QTableWidgetMimeData *twd = qobject_cast<const QTableWidgetMimeData*>(data);
+ if (twd)
+ return twd->items;
+ return QList<QTableWidgetItem*>();
+}
+
+/*!
+ Returns the QModelIndex assocated with the given \a item.
+*/
+
+QModelIndex QTableWidget::indexFromItem(QTableWidgetItem *item) const
+{
+ Q_D(const QTableWidget);
+ return d->model()->index(item);
+}
+
+/*!
+ Returns a pointer to the QTableWidgetItem assocated with the given \a index.
+*/
+
+QTableWidgetItem *QTableWidget::itemFromIndex(const QModelIndex &index) const
+{
+ Q_D(const QTableWidget);
+ return d->model()->item(index);
+}
+
+/*!
+ \internal
+*/
+void QTableWidget::setModel(QAbstractItemModel * /*model*/)
+{
+ Q_ASSERT(!"QTableWidget::setModel() - Changing the model of the QTableWidget is not allowed.");
+}
+
+/*! \reimp */
+bool QTableWidget::event(QEvent *e)
+{
+ return QTableView::event(e);
+}
+
+#ifndef QT_NO_DRAGANDDROP
+/*! \reimp */
+void QTableWidget::dropEvent(QDropEvent *event) {
+ Q_D(QTableWidget);
+ if (event->source() == this && (event->dropAction() == Qt::MoveAction ||
+ dragDropMode() == QAbstractItemView::InternalMove)) {
+ QModelIndex topIndex;
+ int col = -1;
+ int row = -1;
+ if (d->dropOn(event, &row, &col, &topIndex)) {
+ QModelIndexList indexes = selectedIndexes();
+ int top = INT_MAX;
+ int left = INT_MAX;
+ for (int i = 0; i < indexes.count(); ++i) {
+ top = qMin(indexes.at(i).row(), top);
+ left = qMin(indexes.at(i).column(), left);
+ }
+
+ QList<QTableWidgetItem *> taken;
+ for (int i = 0; i < indexes.count(); ++i)
+ taken.append(takeItem(indexes.at(i).row(), indexes.at(i).column()));
+
+ for (int i = 0; i < indexes.count(); ++i) {
+ QModelIndex index = indexes.at(i);
+ int r = index.row() - top + topIndex.row();
+ int c = index.column() - left + topIndex.column();
+ setItem(r, c, taken.takeFirst());
+ }
+
+ event->accept();
+ // Don't want QAbstractItemView to delete it because it was "moved" we already did it
+ event->setDropAction(Qt::CopyAction);
+ }
+ }
+
+ QTableView::dropEvent(event);
+}
+#endif
+
+QT_END_NAMESPACE
+
+#include "moc_qtablewidget.cpp"
+
+#endif // QT_NO_TABLEWIDGET
diff --git a/src/gui/itemviews/qtablewidget.h b/src/gui/itemviews/qtablewidget.h
new file mode 100644
index 0000000000..c97f6a6259
--- /dev/null
+++ b/src/gui/itemviews/qtablewidget.h
@@ -0,0 +1,377 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QTABLEWIDGET_H
+#define QTABLEWIDGET_H
+
+#include <QtGui/qtableview.h>
+#include <QtCore/qvariant.h>
+#include <QtCore/qvector.h>
+//#include <QtGui/qitemselectionmodel.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+#ifndef QT_NO_TABLEWIDGET
+
+class Q_GUI_EXPORT QTableWidgetSelectionRange
+{
+public:
+ QTableWidgetSelectionRange();
+ QTableWidgetSelectionRange(int top, int left, int bottom, int right);
+ QTableWidgetSelectionRange(const QTableWidgetSelectionRange &other);
+ ~QTableWidgetSelectionRange();
+
+ inline int topRow() const { return top; }
+ inline int bottomRow() const { return bottom; }
+ inline int leftColumn() const { return left; }
+ inline int rightColumn() const { return right; }
+ inline int rowCount() const { return bottom - top + 1; }
+ inline int columnCount() const { return right - left + 1; }
+
+private:
+ int top, left, bottom, right;
+};
+
+class QTableWidget;
+class QTableModel;
+class QWidgetItemData;
+class QTableWidgetItemPrivate;
+
+class Q_GUI_EXPORT QTableWidgetItem
+{
+ friend class QTableWidget;
+ friend class QTableModel;
+public:
+ enum ItemType { Type = 0, UserType = 1000 };
+ QTableWidgetItem(int type = Type);
+ explicit QTableWidgetItem(const QString &text, int type = Type);
+ explicit QTableWidgetItem(const QIcon &icon, const QString &text, int type = Type);
+ QTableWidgetItem(const QTableWidgetItem &other);
+ virtual ~QTableWidgetItem();
+
+ virtual QTableWidgetItem *clone() const;
+
+ inline QTableWidget *tableWidget() const { return view; }
+
+ inline int row() const;
+ inline int column() const;
+
+ inline void setSelected(bool select);
+ inline bool isSelected() const;
+
+ inline Qt::ItemFlags flags() const { return itemFlags; }
+ void setFlags(Qt::ItemFlags flags);
+
+ inline QString text() const
+ { return data(Qt::DisplayRole).toString(); }
+ inline void setText(const QString &text);
+
+ inline QIcon icon() const
+ { return qvariant_cast<QIcon>(data(Qt::DecorationRole)); }
+ inline void setIcon(const QIcon &icon);
+
+ inline QString statusTip() const
+ { return data(Qt::StatusTipRole).toString(); }
+ inline void setStatusTip(const QString &statusTip);
+
+#ifndef QT_NO_TOOLTIP
+ inline QString toolTip() const
+ { return data(Qt::ToolTipRole).toString(); }
+ inline void setToolTip(const QString &toolTip);
+#endif
+
+#ifndef QT_NO_WHATSTHIS
+ inline QString whatsThis() const
+ { return data(Qt::WhatsThisRole).toString(); }
+ inline void setWhatsThis(const QString &whatsThis);
+#endif
+
+ inline QFont font() const
+ { return qvariant_cast<QFont>(data(Qt::FontRole)); }
+ inline void setFont(const QFont &font);
+
+ inline int textAlignment() const
+ { return data(Qt::TextAlignmentRole).toInt(); }
+ inline void setTextAlignment(int alignment)
+ { setData(Qt::TextAlignmentRole, alignment); }
+
+ inline QColor backgroundColor() const
+ { return qvariant_cast<QColor>(data(Qt::BackgroundColorRole)); }
+ inline void setBackgroundColor(const QColor &color)
+ { setData(Qt::BackgroundColorRole, color); }
+
+ inline QBrush background() const
+ { return qvariant_cast<QBrush>(data(Qt::BackgroundRole)); }
+ inline void setBackground(const QBrush &brush)
+ { setData(Qt::BackgroundRole, brush); }
+
+ inline QColor textColor() const
+ { return qvariant_cast<QColor>(data(Qt::TextColorRole)); }
+ inline void setTextColor(const QColor &color)
+ { setData(Qt::TextColorRole, color); }
+
+ inline QBrush foreground() const
+ { return qvariant_cast<QBrush>(data(Qt::ForegroundRole)); }
+ inline void setForeground(const QBrush &brush)
+ { setData(Qt::ForegroundRole, brush); }
+
+ inline Qt::CheckState checkState() const
+ { return static_cast<Qt::CheckState>(data(Qt::CheckStateRole).toInt()); }
+ inline void setCheckState(Qt::CheckState state)
+ { setData(Qt::CheckStateRole, state); }
+
+ inline QSize sizeHint() const
+ { return qvariant_cast<QSize>(data(Qt::SizeHintRole)); }
+ inline void setSizeHint(const QSize &size)
+ { setData(Qt::SizeHintRole, size); }
+
+ virtual QVariant data(int role) const;
+ virtual void setData(int role, const QVariant &value);
+
+ virtual bool operator<(const QTableWidgetItem &other) const;
+
+#ifndef QT_NO_DATASTREAM
+ virtual void read(QDataStream &in);
+ virtual void write(QDataStream &out) const;
+#endif
+ QTableWidgetItem &operator=(const QTableWidgetItem &other);
+
+ inline int type() const { return rtti; }
+
+private:
+ int rtti;
+ QVector<QWidgetItemData> values;
+ QTableWidget *view;
+ QTableWidgetItemPrivate *d;
+ Qt::ItemFlags itemFlags;
+};
+
+inline void QTableWidgetItem::setText(const QString &atext)
+{ setData(Qt::DisplayRole, atext); }
+
+inline void QTableWidgetItem::setIcon(const QIcon &aicon)
+{ setData(Qt::DecorationRole, aicon); }
+
+inline void QTableWidgetItem::setStatusTip(const QString &astatusTip)
+{ setData(Qt::StatusTipRole, astatusTip); }
+
+#ifndef QT_NO_TOOLTIP
+inline void QTableWidgetItem::setToolTip(const QString &atoolTip)
+{ setData(Qt::ToolTipRole, atoolTip); }
+#endif
+
+#ifndef QT_NO_WHATSTHIS
+inline void QTableWidgetItem::setWhatsThis(const QString &awhatsThis)
+{ setData(Qt::WhatsThisRole, awhatsThis); }
+#endif
+
+inline void QTableWidgetItem::setFont(const QFont &afont)
+{ setData(Qt::FontRole, afont); }
+
+#ifndef QT_NO_DATASTREAM
+Q_GUI_EXPORT QDataStream &operator>>(QDataStream &in, QTableWidgetItem &item);
+Q_GUI_EXPORT QDataStream &operator<<(QDataStream &out, const QTableWidgetItem &item);
+#endif
+
+class QTableWidgetPrivate;
+
+class Q_GUI_EXPORT QTableWidget : public QTableView
+{
+ Q_OBJECT
+ Q_PROPERTY(int rowCount READ rowCount WRITE setRowCount)
+ Q_PROPERTY(int columnCount READ columnCount WRITE setColumnCount)
+
+ friend class QTableModel;
+public:
+ explicit QTableWidget(QWidget *parent = 0);
+ QTableWidget(int rows, int columns, QWidget *parent = 0);
+ ~QTableWidget();
+
+ void setRowCount(int rows);
+ int rowCount() const;
+
+ void setColumnCount(int columns);
+ int columnCount() const;
+
+ int row(const QTableWidgetItem *item) const;
+ int column(const QTableWidgetItem *item) const;
+
+ QTableWidgetItem *item(int row, int column) const;
+ void setItem(int row, int column, QTableWidgetItem *item);
+ QTableWidgetItem *takeItem(int row, int column);
+
+ QTableWidgetItem *verticalHeaderItem(int row) const;
+ void setVerticalHeaderItem(int row, QTableWidgetItem *item);
+ QTableWidgetItem *takeVerticalHeaderItem(int row);
+
+ QTableWidgetItem *horizontalHeaderItem(int column) const;
+ void setHorizontalHeaderItem(int column, QTableWidgetItem *item);
+ QTableWidgetItem *takeHorizontalHeaderItem(int column);
+ void setVerticalHeaderLabels(const QStringList &labels);
+ void setHorizontalHeaderLabels(const QStringList &labels);
+
+ int currentRow() const;
+ int currentColumn() const;
+ QTableWidgetItem *currentItem() const;
+ void setCurrentItem(QTableWidgetItem *item);
+ void setCurrentItem(QTableWidgetItem *item, QItemSelectionModel::SelectionFlags command);
+ void setCurrentCell(int row, int column);
+ void setCurrentCell(int row, int column, QItemSelectionModel::SelectionFlags command);
+
+ void sortItems(int column, Qt::SortOrder order = Qt::AscendingOrder);
+ void setSortingEnabled(bool enable);
+ bool isSortingEnabled() const;
+
+ void editItem(QTableWidgetItem *item);
+ void openPersistentEditor(QTableWidgetItem *item);
+ void closePersistentEditor(QTableWidgetItem *item);
+
+ QWidget *cellWidget(int row, int column) const;
+ void setCellWidget(int row, int column, QWidget *widget);
+ inline void removeCellWidget(int row, int column);
+
+ bool isItemSelected(const QTableWidgetItem *item) const;
+ void setItemSelected(const QTableWidgetItem *item, bool select);
+ void setRangeSelected(const QTableWidgetSelectionRange &range, bool select);
+
+ QList<QTableWidgetSelectionRange> selectedRanges() const;
+ QList<QTableWidgetItem*> selectedItems();
+ QList<QTableWidgetItem*> findItems(const QString &text, Qt::MatchFlags flags) const;
+
+ int visualRow(int logicalRow) const;
+ int visualColumn(int logicalColumn) const;
+
+ QTableWidgetItem *itemAt(const QPoint &p) const;
+ inline QTableWidgetItem *itemAt(int x, int y) const;
+ QRect visualItemRect(const QTableWidgetItem *item) const;
+
+ const QTableWidgetItem *itemPrototype() const;
+ void setItemPrototype(const QTableWidgetItem *item);
+
+public Q_SLOTS:
+ void scrollToItem(const QTableWidgetItem *item, QAbstractItemView::ScrollHint hint = EnsureVisible);
+ void insertRow(int row);
+ void insertColumn(int column);
+ void removeRow(int row);
+ void removeColumn(int column);
+ void clear();
+ void clearContents();
+
+Q_SIGNALS:
+ void itemPressed(QTableWidgetItem *item);
+ void itemClicked(QTableWidgetItem *item);
+ void itemDoubleClicked(QTableWidgetItem *item);
+
+ void itemActivated(QTableWidgetItem *item);
+ void itemEntered(QTableWidgetItem *item);
+ void itemChanged(QTableWidgetItem *item);
+
+ void currentItemChanged(QTableWidgetItem *current, QTableWidgetItem *previous);
+ void itemSelectionChanged();
+
+ void cellPressed(int row, int column);
+ void cellClicked(int row, int column);
+ void cellDoubleClicked(int row, int column);
+
+ void cellActivated(int row, int column);
+ void cellEntered(int row, int column);
+ void cellChanged(int row, int column);
+
+ void currentCellChanged(int currentRow, int currentColumn, int previousRow, int previousColumn);
+
+protected:
+ bool event(QEvent *e);
+ virtual QStringList mimeTypes() const;
+ virtual QMimeData *mimeData(const QList<QTableWidgetItem*> items) const;
+ virtual bool dropMimeData(int row, int column, const QMimeData *data, Qt::DropAction action);
+ virtual Qt::DropActions supportedDropActions() const;
+ QList<QTableWidgetItem*> items(const QMimeData *data) const;
+
+ QModelIndex indexFromItem(QTableWidgetItem *item) const;
+ QTableWidgetItem *itemFromIndex(const QModelIndex &index) const;
+ void dropEvent(QDropEvent *event);
+
+private:
+ void setModel(QAbstractItemModel *model);
+
+ Q_DECLARE_PRIVATE(QTableWidget)
+ Q_DISABLE_COPY(QTableWidget)
+
+ Q_PRIVATE_SLOT(d_func(), void _q_emitItemPressed(const QModelIndex &index))
+ Q_PRIVATE_SLOT(d_func(), void _q_emitItemClicked(const QModelIndex &index))
+ Q_PRIVATE_SLOT(d_func(), void _q_emitItemDoubleClicked(const QModelIndex &index))
+ Q_PRIVATE_SLOT(d_func(), void _q_emitItemActivated(const QModelIndex &index))
+ Q_PRIVATE_SLOT(d_func(), void _q_emitItemEntered(const QModelIndex &index))
+ Q_PRIVATE_SLOT(d_func(), void _q_emitItemChanged(const QModelIndex &index))
+ Q_PRIVATE_SLOT(d_func(), void _q_emitCurrentItemChanged(const QModelIndex &previous, const QModelIndex &current))
+ Q_PRIVATE_SLOT(d_func(), void _q_sort())
+ Q_PRIVATE_SLOT(d_func(), void _q_dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight))
+};
+
+inline void QTableWidget::removeCellWidget(int arow, int acolumn)
+{ setCellWidget(arow, acolumn, 0); }
+
+inline QTableWidgetItem *QTableWidget::itemAt(int ax, int ay) const
+{ return itemAt(QPoint(ax, ay)); }
+
+inline int QTableWidgetItem::row() const
+{ return (view ? view->row(this) : -1); }
+
+inline int QTableWidgetItem::column() const
+{ return (view ? view->column(this) : -1); }
+
+inline void QTableWidgetItem::setSelected(bool aselect)
+{ if (view) view->setItemSelected(this, aselect); }
+
+inline bool QTableWidgetItem::isSelected() const
+{ return (view ? view->isItemSelected(this) : false); }
+
+#endif // QT_NO_TABLEWIDGET
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QTABLEWIDGET_H
diff --git a/src/gui/itemviews/qtablewidget_p.h b/src/gui/itemviews/qtablewidget_p.h
new file mode 100644
index 0000000000..2e1dab64c0
--- /dev/null
+++ b/src/gui/itemviews/qtablewidget_p.h
@@ -0,0 +1,223 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QTABLEWIDGET_P_H
+#define QTABLEWIDGET_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. This header file may change
+// from version to version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <qheaderview.h>
+#include <qtablewidget.h>
+#include <qabstractitemmodel.h>
+#include <private/qabstractitemmodel_p.h>
+#include <private/qtableview_p.h>
+#include <private/qwidgetitemdata_p.h>
+
+#ifndef QT_NO_TABLEWIDGET
+
+QT_BEGIN_NAMESPACE
+
+// workaround for VC++ 6.0 linker bug
+typedef bool(*LessThan)(const QPair<QTableWidgetItem*,int>&,const QPair<QTableWidgetItem*,int>&);
+
+class QTableWidgetMimeData : public QMimeData
+{
+ Q_OBJECT
+public:
+ QList<QTableWidgetItem*> items;
+};
+
+class QTableModelLessThan
+{
+public:
+ inline bool operator()(QTableWidgetItem *i1, QTableWidgetItem *i2) const
+ { return (*i1 < *i2); }
+};
+
+class QTableModelGreaterThan
+{
+public:
+ inline bool operator()(QTableWidgetItem *i1, QTableWidgetItem *i2) const
+ { return (*i2 < *i1); }
+};
+
+class QTableModel : public QAbstractTableModel
+{
+ Q_OBJECT
+public:
+ enum ItemFlagsExtension {
+ ItemIsHeaderItem = 128
+ }; // we need this to separate header items from other items
+
+ QTableModel(int rows, int columns, QTableWidget *parent);
+ ~QTableModel();
+
+ bool insertRows(int row, int count = 1, const QModelIndex &parent = QModelIndex());
+ bool insertColumns(int column, int count = 1, const QModelIndex &parent = QModelIndex());
+
+ bool removeRows(int row, int count = 1, const QModelIndex &parent = QModelIndex());
+ bool removeColumns(int column, int count = 1, const QModelIndex &parent = QModelIndex());
+
+ void setItem(int row, int column, QTableWidgetItem *item);
+ QTableWidgetItem *takeItem(int row, int column);
+ QTableWidgetItem *item(int row, int column) const;
+ QTableWidgetItem *item(const QModelIndex &index) const;
+ void removeItem(QTableWidgetItem *item);
+
+ void setHorizontalHeaderItem(int section, QTableWidgetItem *item);
+ void setVerticalHeaderItem(int section, QTableWidgetItem *item);
+ QTableWidgetItem *takeHorizontalHeaderItem(int section);
+ QTableWidgetItem *takeVerticalHeaderItem(int section);
+ QTableWidgetItem *horizontalHeaderItem(int section);
+ QTableWidgetItem *verticalHeaderItem(int section);
+
+ QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const
+ { return QAbstractTableModel::index(row, column, parent); }
+
+ QModelIndex index(const QTableWidgetItem *item) const;
+
+ void setRowCount(int rows);
+ void setColumnCount(int columns);
+
+ int rowCount(const QModelIndex &parent = QModelIndex()) const;
+ int columnCount(const QModelIndex &parent = QModelIndex()) const;
+
+ QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
+ bool setData(const QModelIndex &index, const QVariant &value, int role);
+ bool setItemData(const QModelIndex &index, const QMap<int, QVariant> &roles);
+
+ QMap<int, QVariant> itemData(const QModelIndex &index) const;
+
+ QVariant headerData(int section, Qt::Orientation orientation, int role) const;
+ bool setHeaderData(int section, Qt::Orientation orientation, const QVariant &value, int role);
+
+ Qt::ItemFlags flags(const QModelIndex &index) const;
+
+ void sort(int column, Qt::SortOrder order);
+ static bool itemLessThan(const QPair<QTableWidgetItem*,int> &left,
+ const QPair<QTableWidgetItem*,int> &right);
+ static bool itemGreaterThan(const QPair<QTableWidgetItem*,int> &left,
+ const QPair<QTableWidgetItem*,int> &right);
+ static bool canConvertToDouble(const QVariant &value);
+
+ void ensureSorted(int column, Qt::SortOrder order, int start, int end);
+ QVector<QTableWidgetItem*> columnItems(int column) const;
+ void updateRowIndexes(QModelIndexList &indexes, int movedFromRow, int movedToRow);
+ static QVector<QTableWidgetItem*>::iterator sortedInsertionIterator(
+ const QVector<QTableWidgetItem*>::iterator &begin,
+ const QVector<QTableWidgetItem*>::iterator &end,
+ Qt::SortOrder order, QTableWidgetItem *item);
+
+ bool isValid(const QModelIndex &index) const;
+ inline long tableIndex(int row, int column) const
+ { return (row * horizontalHeaderItems.count()) + column; }
+
+ void clear();
+ void clearContents();
+ void itemChanged(QTableWidgetItem *item);
+
+ QTableWidgetItem *createItem() const;
+ const QTableWidgetItem *itemPrototype() const;
+ void setItemPrototype(const QTableWidgetItem *item);
+
+ // dnd
+ QStringList mimeTypes() const;
+ QMimeData *mimeData(const QModelIndexList &indexes) const;
+ bool dropMimeData(const QMimeData *data, Qt::DropAction action,
+ int row, int column, const QModelIndex &parent);
+ Qt::DropActions supportedDropActions() const;
+
+ QMimeData *internalMimeData() const;
+
+private:
+ const QTableWidgetItem *prototype;
+ QVector<QTableWidgetItem*> tableItems;
+ QVector<QTableWidgetItem*> verticalHeaderItems;
+ QVector<QTableWidgetItem*> horizontalHeaderItems;
+
+ // A cache must be mutable if get-functions should have const modifiers
+ mutable QModelIndexList cachedIndexes;
+};
+
+class QTableWidgetPrivate : public QTableViewPrivate
+{
+ Q_DECLARE_PUBLIC(QTableWidget)
+public:
+ QTableWidgetPrivate() : QTableViewPrivate() {}
+ inline QTableModel *model() const { return qobject_cast<QTableModel*>(q_func()->model()); }
+ void setup();
+
+ // view signals
+ void _q_emitItemPressed(const QModelIndex &index);
+ void _q_emitItemClicked(const QModelIndex &index);
+ void _q_emitItemDoubleClicked(const QModelIndex &index);
+ void _q_emitItemActivated(const QModelIndex &index);
+ void _q_emitItemEntered(const QModelIndex &index);
+ // model signals
+ void _q_emitItemChanged(const QModelIndex &index);
+ // selection signals
+ void _q_emitCurrentItemChanged(const QModelIndex &previous, const QModelIndex &current);
+ // sorting
+ void _q_sort();
+ void _q_dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight);
+};
+
+class QTableWidgetItemPrivate
+{
+public:
+ QTableWidgetItemPrivate(QTableWidgetItem *item) : q(item), id(-1) {}
+ QTableWidgetItem *q;
+ int id;
+};
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_TABLEWIDGET
+
+#endif // QTABLEWIDGET_P_H
diff --git a/src/gui/itemviews/qtreeview.cpp b/src/gui/itemviews/qtreeview.cpp
new file mode 100644
index 0000000000..2aa0c57146
--- /dev/null
+++ b/src/gui/itemviews/qtreeview.cpp
@@ -0,0 +1,3851 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#include "qtreeview.h"
+
+#ifndef QT_NO_TREEVIEW
+#include <qheaderview.h>
+#include <qitemdelegate.h>
+#include <qapplication.h>
+#include <qscrollbar.h>
+#include <qpainter.h>
+#include <qstack.h>
+#include <qstyle.h>
+#include <qstyleoption.h>
+#include <qevent.h>
+#include <qpen.h>
+#include <qdebug.h>
+#ifndef QT_NO_ACCESSIBILITY
+#include <qaccessible.h>
+#endif
+
+#include <private/qtreeview_p.h>
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QTreeView
+ \brief The QTreeView class provides a default model/view implementation of a tree view.
+
+ \ingroup model-view
+ \ingroup advanced
+ \mainclass
+
+ A QTreeView implements a tree representation of items from a
+ model. This class is used to provide standard hierarchical lists that
+ were previously provided by the \c QListView class, but using the more
+ flexible approach provided by Qt's model/view architecture.
+
+ The QTreeView class is one of the \l{Model/View Classes} and is part of
+ Qt's \l{Model/View Programming}{model/view framework}.
+
+ QTreeView implements the interfaces defined by the
+ QAbstractItemView class to allow it to display data provided by
+ models derived from the QAbstractItemModel class.
+
+ It is simple to construct a tree view displaying data from a
+ model. In the following example, the contents of a directory are
+ supplied by a QDirModel and displayed as a tree:
+
+ \snippet doc/src/snippets/shareddirmodel/main.cpp 3
+ \snippet doc/src/snippets/shareddirmodel/main.cpp 6
+
+ The model/view architecture ensures that the contents of the tree view
+ are updated as the model changes.
+
+ Items that have children can be in an expanded (children are
+ visible) or collapsed (children are hidden) state. When this state
+ changes a collapsed() or expanded() signal is emitted with the
+ model index of the relevant item.
+
+ The amount of indentation used to indicate levels of hierarchy is
+ controlled by the \l indentation property.
+
+ Headers in tree views are constructed using the QHeaderView class and can
+ be hidden using \c{header()->hide()}. Note that each header is configured
+ with its \l{QHeaderView::}{stretchLastSection} property set to true,
+ ensuring that the view does not waste any of the space assigned to it for
+ its header. If this value is set to true, this property will override the
+ resize mode set on the last section in the header.
+
+
+ \section1 Key Bindings
+
+ QTreeView supports a set of key bindings that enable the user to
+ navigate in the view and interact with the contents of items:
+
+ \table
+ \header \o Key \o Action
+ \row \o Up \o Moves the cursor to the item in the same column on
+ the previous row. If the parent of the current item has no more rows to
+ navigate to, the cursor moves to the relevant item in the last row
+ of the sibling that precedes the parent.
+ \row \o Down \o Moves the cursor to the item in the same column on
+ the next row. If the parent of the current item has no more rows to
+ navigate to, the cursor moves to the relevant item in the first row
+ of the sibling that follows the parent.
+ \row \o Left \o Hides the children of the current item (if present)
+ by collapsing a branch.
+ \row \o Minus \o Same as LeftArrow.
+ \row \o Right \o Reveals the children of the current item (if present)
+ by expanding a branch.
+ \row \o Plus \o Same as RightArrow.
+ \row \o Asterisk \o Expands all children of the current item (if present).
+ \row \o PageUp \o Moves the cursor up one page.
+ \row \o PageDown \o Moves the cursor down one page.
+ \row \o Home \o Moves the cursor to an item in the same column of the first
+ row of the first top-level item in the model.
+ \row \o End \o Moves the cursor to an item in the same column of the last
+ row of the last top-level item in the model.
+ \row \o F2 \o In editable models, this opens the current item for editing.
+ The Escape key can be used to cancel the editing process and revert
+ any changes to the data displayed.
+ \endtable
+
+ \omit
+ Describe the expanding/collapsing concept if not covered elsewhere.
+ \endomit
+
+ \table 100%
+ \row \o \inlineimage windowsxp-treeview.png Screenshot of a Windows XP style tree view
+ \o \inlineimage macintosh-treeview.png Screenshot of a Macintosh style tree view
+ \o \inlineimage plastique-treeview.png Screenshot of a Plastique style tree view
+ \row \o A \l{Windows XP Style Widget Gallery}{Windows XP style} tree view.
+ \o A \l{Macintosh Style Widget Gallery}{Macintosh style} tree view.
+ \o A \l{Plastique Style Widget Gallery}{Plastique style} tree view.
+ \endtable
+
+ \section1 Improving Performance
+
+ It is possible to give the view hints about the data it is handling in order
+ to improve its performance when displaying large numbers of items. One approach
+ that can be taken for views that are intended to display items with equal heights
+ is to set the \l uniformRowHeights property to true.
+
+ \sa QListView, QTreeWidget, {View Classes}, QAbstractItemModel, QAbstractItemView,
+ {Dir View Example}
+*/
+
+
+/*!
+ \fn void QTreeView::expanded(const QModelIndex &index)
+
+ This signal is emitted when the item specified by \a index is expanded.
+*/
+
+
+/*!
+ \fn void QTreeView::collapsed(const QModelIndex &index)
+
+ This signal is emitted when the item specified by \a index is collapsed.
+*/
+
+/*!
+ Constructs a table view with a \a parent to represent a model's
+ data. Use setModel() to set the model.
+
+ \sa QAbstractItemModel
+*/
+QTreeView::QTreeView(QWidget *parent)
+ : QAbstractItemView(*new QTreeViewPrivate, parent)
+{
+ Q_D(QTreeView);
+ d->initialize();
+}
+
+/*!
+ \internal
+*/
+QTreeView::QTreeView(QTreeViewPrivate &dd, QWidget *parent)
+ : QAbstractItemView(dd, parent)
+{
+ Q_D(QTreeView);
+ d->initialize();
+}
+
+/*!
+ Destroys the tree view.
+*/
+QTreeView::~QTreeView()
+{
+}
+
+/*!
+ \reimp
+*/
+void QTreeView::setModel(QAbstractItemModel *model)
+{
+ Q_D(QTreeView);
+ if (model == d->model)
+ return;
+ if (d->selectionModel) { // support row editing
+ disconnect(d->selectionModel, SIGNAL(currentRowChanged(QModelIndex,QModelIndex)),
+ d->model, SLOT(submit()));
+ disconnect(d->model, SIGNAL(rowsRemoved(QModelIndex,int,int)),
+ this, SLOT(rowsRemoved(QModelIndex,int,int)));
+ disconnect(d->model, SIGNAL(modelAboutToBeReset()), this, SLOT(_q_modelAboutToBeReset()));
+ }
+ d->viewItems.clear();
+ d->expandedIndexes.clear();
+ d->hiddenIndexes.clear();
+ d->header->setModel(model);
+ QAbstractItemView::setModel(model);
+
+ // QAbstractItemView connects to a private slot
+ disconnect(d->model, SIGNAL(rowsRemoved(QModelIndex,int,int)),
+ this, SLOT(_q_rowsRemoved(QModelIndex,int,int)));
+ // do header layout after the tree
+ disconnect(d->model, SIGNAL(layoutChanged()),
+ d->header, SLOT(_q_layoutChanged()));
+ // QTreeView has a public slot for this
+ connect(d->model, SIGNAL(rowsRemoved(QModelIndex,int,int)),
+ this, SLOT(rowsRemoved(QModelIndex,int,int)));
+
+ connect(d->model, SIGNAL(columnsAboutToBeRemoved(QModelIndex,int,int)),
+ this, SLOT(_q_columnsAboutToBeRemoved(QModelIndex,int,int)));
+ connect(d->model, SIGNAL(columnsRemoved(QModelIndex,int,int)),
+ this, SLOT(_q_columnsRemoved(QModelIndex,int,int)));
+
+ connect(d->model, SIGNAL(modelAboutToBeReset()), SLOT(_q_modelAboutToBeReset()));
+
+ if (d->sortingEnabled)
+ sortByColumn(header()->sortIndicatorSection(), header()->sortIndicatorOrder());
+}
+
+/*!
+ \reimp
+*/
+void QTreeView::setRootIndex(const QModelIndex &index)
+{
+ Q_D(QTreeView);
+ d->header->setRootIndex(index);
+ QAbstractItemView::setRootIndex(index);
+}
+
+/*!
+ \reimp
+*/
+void QTreeView::setSelectionModel(QItemSelectionModel *selectionModel)
+{
+ Q_D(QTreeView);
+ Q_ASSERT(selectionModel);
+ if (d->selectionModel) {
+ if (d->allColumnsShowFocus) {
+ QObject::disconnect(d->selectionModel, SIGNAL(currentChanged(QModelIndex,QModelIndex)),
+ this, SLOT(_q_currentChanged(QModelIndex,QModelIndex)));
+ }
+ // support row editing
+ disconnect(d->selectionModel, SIGNAL(currentRowChanged(QModelIndex,QModelIndex)),
+ d->model, SLOT(submit()));
+ }
+
+ d->header->setSelectionModel(selectionModel);
+ QAbstractItemView::setSelectionModel(selectionModel);
+
+ if (d->selectionModel) {
+ if (d->allColumnsShowFocus) {
+ QObject::connect(d->selectionModel, SIGNAL(currentChanged(QModelIndex,QModelIndex)),
+ this, SLOT(_q_currentChanged(QModelIndex,QModelIndex)));
+ }
+ // support row editing
+ connect(d->selectionModel, SIGNAL(currentRowChanged(QModelIndex,QModelIndex)),
+ d->model, SLOT(submit()));
+ }
+}
+
+/*!
+ Returns the header for the tree view.
+
+ \sa QAbstractItemModel::headerData()
+*/
+QHeaderView *QTreeView::header() const
+{
+ Q_D(const QTreeView);
+ return d->header;
+}
+
+/*!
+ Sets the header for the tree view, to the given \a header.
+
+ The view takes ownership over the given \a header and deletes it
+ when a new header is set.
+
+ \sa QAbstractItemModel::headerData()
+*/
+void QTreeView::setHeader(QHeaderView *header)
+{
+ Q_D(QTreeView);
+ if (header == d->header || !header)
+ return;
+ if (d->header && d->header->parent() == this)
+ delete d->header;
+ d->header = header;
+ d->header->setParent(this);
+
+ if (!d->header->model()) {
+ d->header->setModel(d->model);
+ if (d->selectionModel)
+ d->header->setSelectionModel(d->selectionModel);
+ }
+
+ connect(d->header, SIGNAL(sectionResized(int,int,int)),
+ this, SLOT(columnResized(int,int,int)));
+ connect(d->header, SIGNAL(sectionMoved(int,int,int)),
+ this, SLOT(columnMoved()));
+ connect(d->header, SIGNAL(sectionCountChanged(int,int)),
+ this, SLOT(columnCountChanged(int,int)));
+ connect(d->header, SIGNAL(sectionHandleDoubleClicked(int)),
+ this, SLOT(resizeColumnToContents(int)));
+ connect(d->header, SIGNAL(geometriesChanged()),
+ this, SLOT(updateGeometries()));
+
+ setSortingEnabled(d->sortingEnabled);
+}
+
+/*!
+ \property QTreeView::autoExpandDelay
+ \brief The delay time before items in a tree are opened during a drag and drop operation.
+ \since 4.3
+
+ This property holds the amount of time in milliseconds that the user must wait over
+ a node before that node will automatically open or close. If the time is
+ set to less then 0 then it will not be activated.
+
+ By default, this property has a value of -1, meaning that auto-expansion is disabled.
+*/
+int QTreeView::autoExpandDelay() const
+{
+ Q_D(const QTreeView);
+ return d->autoExpandDelay;
+}
+
+void QTreeView::setAutoExpandDelay(int delay)
+{
+ Q_D(QTreeView);
+ d->autoExpandDelay = delay;
+}
+
+/*!
+ \property QTreeView::indentation
+ \brief indentation of the items in the tree view.
+
+ This property holds the indentation measured in pixels of the items for each
+ level in the tree view. For top-level items, the indentation specifies the
+ horizontal distance from the viewport edge to the items in the first column;
+ for child items, it specifies their indentation from their parent items.
+
+ By default, this property has a value of 20.
+*/
+int QTreeView::indentation() const
+{
+ Q_D(const QTreeView);
+ return d->indent;
+}
+
+void QTreeView::setIndentation(int i)
+{
+ Q_D(QTreeView);
+ if (i != d->indent) {
+ d->indent = i;
+ d->viewport->update();
+ }
+}
+
+/*!
+ \property QTreeView::rootIsDecorated
+ \brief whether to show controls for expanding and collapsing top-level items
+
+ Items with children are typically shown with controls to expand and collapse
+ them, allowing their children to be shown or hidden. If this property is
+ false, these controls are not shown for top-level items. This can be used to
+ make a single level tree structure appear like a simple list of items.
+
+ By default, this property is true.
+*/
+bool QTreeView::rootIsDecorated() const
+{
+ Q_D(const QTreeView);
+ return d->rootDecoration;
+}
+
+void QTreeView::setRootIsDecorated(bool show)
+{
+ Q_D(QTreeView);
+ if (show != d->rootDecoration) {
+ d->rootDecoration = show;
+ d->viewport->update();
+ }
+}
+
+/*!
+ \property QTreeView::uniformRowHeights
+ \brief whether all items in the treeview have the same height
+
+ This property should only be set to true if it is guaranteed that all items
+ in the view has the same height. This enables the view to do some
+ optimizations.
+
+ The height is obtained from the first item in the view. It is updated
+ when the data changes on that item.
+
+ By default, this property is false.
+*/
+bool QTreeView::uniformRowHeights() const
+{
+ Q_D(const QTreeView);
+ return d->uniformRowHeights;
+}
+
+void QTreeView::setUniformRowHeights(bool uniform)
+{
+ Q_D(QTreeView);
+ d->uniformRowHeights = uniform;
+}
+
+/*!
+ \property QTreeView::itemsExpandable
+ \brief whether the items are expandable by the user.
+
+ This property holds whether the user can expand and collapse items
+ interactively.
+
+ By default, this property is true.
+
+*/
+bool QTreeView::itemsExpandable() const
+{
+ Q_D(const QTreeView);
+ return d->itemsExpandable;
+}
+
+void QTreeView::setItemsExpandable(bool enable)
+{
+ Q_D(QTreeView);
+ d->itemsExpandable = enable;
+}
+
+/*!
+ \property QTreeView::expandsOnDoubleClick
+ \since 4.4
+ \brief whether the items can be expanded by double-clicking.
+
+ This property holds whether the user can expand and collapse items
+ by double-clicking. The default value is true.
+
+ \sa itemsExpandable
+*/
+bool QTreeView::expandsOnDoubleClick() const
+{
+ Q_D(const QTreeView);
+ return d->expandsOnDoubleClick;
+}
+
+void QTreeView::setExpandsOnDoubleClick(bool enable)
+{
+ Q_D(QTreeView);
+ d->expandsOnDoubleClick = enable;
+}
+
+/*!
+ Returns the horizontal position of the \a column in the viewport.
+*/
+int QTreeView::columnViewportPosition(int column) const
+{
+ Q_D(const QTreeView);
+ return d->header->sectionViewportPosition(column);
+}
+
+/*!
+ Returns the width of the \a column.
+
+ \sa resizeColumnToContents(), setColumnWidth()
+*/
+int QTreeView::columnWidth(int column) const
+{
+ Q_D(const QTreeView);
+ return d->header->sectionSize(column);
+}
+
+/*!
+ \since 4.2
+
+ Sets the width of the given \a column to the \a width specified.
+
+ \sa columnWidth(), resizeColumnToContents()
+*/
+void QTreeView::setColumnWidth(int column, int width)
+{
+ Q_D(QTreeView);
+ d->header->resizeSection(column, width);
+}
+
+/*!
+ Returns the column in the tree view whose header covers the \a x
+ coordinate given.
+*/
+int QTreeView::columnAt(int x) const
+{
+ Q_D(const QTreeView);
+ return d->header->logicalIndexAt(x);
+}
+
+/*!
+ Returns true if the \a column is hidden; otherwise returns false.
+
+ \sa hideColumn(), isRowHidden()
+*/
+bool QTreeView::isColumnHidden(int column) const
+{
+ Q_D(const QTreeView);
+ return d->header->isSectionHidden(column);
+}
+
+/*!
+ If \a hide is true the \a column is hidden, otherwise the \a column is shown.
+
+ \sa hideColumn(), setRowHidden()
+*/
+void QTreeView::setColumnHidden(int column, bool hide)
+{
+ Q_D(QTreeView);
+ if (column < 0 || column >= d->header->count())
+ return;
+ d->header->setSectionHidden(column, hide);
+}
+
+/*!
+ \property QTreeView::headerHidden
+ \brief whether the header is shown or not.
+ \since 4.4
+
+ If this property is true, the header is not shown otherwise it is.
+ The default value is false.
+
+ \sa header()
+*/
+bool QTreeView::isHeaderHidden() const
+{
+ Q_D(const QTreeView);
+ return d->header->isHidden();
+}
+
+void QTreeView::setHeaderHidden(bool hide)
+{
+ Q_D(QTreeView);
+ d->header->setHidden(hide);
+}
+
+/*!
+ Returns true if the item in the given \a row of the \a parent is hidden;
+ otherwise returns false.
+
+ \sa setRowHidden(), isColumnHidden()
+*/
+bool QTreeView::isRowHidden(int row, const QModelIndex &parent) const
+{
+ Q_D(const QTreeView);
+ if (!d->model)
+ return false;
+ return d->isRowHidden(d->model->index(row, 0, parent));
+}
+
+/*!
+ If \a hide is true the \a row with the given \a parent is hidden, otherwise the \a row is shown.
+
+ \sa isRowHidden(), setColumnHidden()
+*/
+void QTreeView::setRowHidden(int row, const QModelIndex &parent, bool hide)
+{
+ Q_D(QTreeView);
+ if (!d->model)
+ return;
+ QModelIndex index = d->model->index(row, 0, parent);
+ if (!index.isValid())
+ return;
+
+ if (hide) {
+ d->hiddenIndexes.insert(index);
+ } else if(d->isPersistent(index)) { //if the index is not persistent, it cannot be in the set
+ d->hiddenIndexes.remove(index);
+ }
+
+ d->doDelayedItemsLayout();
+}
+
+/*!
+ \since 4.3
+
+ Returns true if the item in first column in the given \a row
+ of the \a parent is spanning all the columns; otherwise returns false.
+
+ \sa setFirstColumnSpanned()
+*/
+bool QTreeView::isFirstColumnSpanned(int row, const QModelIndex &parent) const
+{
+ Q_D(const QTreeView);
+ if (d->spanningIndexes.isEmpty() || !d->model)
+ return false;
+ QModelIndex index = d->model->index(row, 0, parent);
+ for (int i = 0; i < d->spanningIndexes.count(); ++i)
+ if (d->spanningIndexes.at(i) == index)
+ return true;
+ return false;
+}
+
+/*!
+ \since 4.3
+
+ If \a span is true the item in the first column in the \a row
+ with the given \a parent is set to span all columns, otherwise all items
+ on the \a row are shown.
+
+ \sa isFirstColumnSpanned()
+*/
+void QTreeView::setFirstColumnSpanned(int row, const QModelIndex &parent, bool span)
+{
+ Q_D(QTreeView);
+ if (!d->model)
+ return;
+ QModelIndex index = d->model->index(row, 0, parent);
+ if (!index.isValid())
+ return;
+
+ if (span) {
+ QPersistentModelIndex persistent(index);
+ if (!d->spanningIndexes.contains(persistent))
+ d->spanningIndexes.append(persistent);
+ } else {
+ QPersistentModelIndex persistent(index);
+ int i = d->spanningIndexes.indexOf(persistent);
+ if (i >= 0)
+ d->spanningIndexes.remove(i);
+ }
+
+ d->executePostedLayout();
+ int i = d->viewIndex(index);
+ if (i >= 0)
+ d->viewItems[i].spanning = span;
+
+ d->viewport->update();
+}
+
+/*!
+ \reimp
+*/
+void QTreeView::dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight)
+{
+ Q_D(QTreeView);
+
+ // if we are going to do a complete relayout anyway, there is no need to update
+ if (d->delayedPendingLayout)
+ return;
+
+ // refresh the height cache here; we don't really lose anything by getting the size hint,
+ // since QAbstractItemView::dataChanged() will get the visualRect for the items anyway
+
+ QModelIndex top = topLeft.sibling(topLeft.row(), 0);
+ int topViewIndex = d->viewIndex(top);
+ if (topViewIndex == 0)
+ d->defaultItemHeight = indexRowSizeHint(top);
+ bool sizeChanged = false;
+ if (topViewIndex != -1) {
+ if (topLeft == bottomRight) {
+ int oldHeight = d->itemHeight(topViewIndex);
+ d->invalidateHeightCache(topViewIndex);
+ sizeChanged = (oldHeight != d->itemHeight(topViewIndex));
+ } else {
+ QModelIndex bottom = bottomRight.sibling(bottomRight.row(), 0);
+ int bottomViewIndex = d->viewIndex(bottom);
+ for (int i = topViewIndex; i <= bottomViewIndex; ++i) {
+ int oldHeight = d->itemHeight(i);
+ d->invalidateHeightCache(i);
+ sizeChanged |= (oldHeight != d->itemHeight(i));
+ }
+ }
+ }
+
+ if (sizeChanged) {
+ d->updateScrollBars();
+ d->viewport->update();
+ }
+ QAbstractItemView::dataChanged(topLeft, bottomRight);
+}
+
+/*!
+ Hides the \a column given.
+
+ \note This function should only be called after the model has been
+ initialized, as the view needs to know the number of columns in order to
+ hide \a column.
+
+ \sa showColumn(), setColumnHidden()
+*/
+void QTreeView::hideColumn(int column)
+{
+ Q_D(QTreeView);
+ d->header->hideSection(column);
+}
+
+/*!
+ Shows the given \a column in the tree view.
+
+ \sa hideColumn(), setColumnHidden()
+*/
+void QTreeView::showColumn(int column)
+{
+ Q_D(QTreeView);
+ d->header->showSection(column);
+}
+
+/*!
+ \fn void QTreeView::expand(const QModelIndex &index)
+
+ Expands the model item specified by the \a index.
+
+ \sa expanded()
+*/
+void QTreeView::expand(const QModelIndex &index)
+{
+ Q_D(QTreeView);
+ if (!d->isIndexValid(index))
+ return;
+ if (d->delayedPendingLayout) {
+ //A complete relayout is going to be performed, just store the expanded index, no need to layout.
+ if (d->storeExpanded(index))
+ emit expanded(index);
+ return;
+ }
+
+ int i = d->viewIndex(index);
+ if (i != -1) { // is visible
+ d->expand(i, true);
+ if (!d->isAnimating()) {
+ updateGeometries();
+ d->viewport->update();
+ }
+ } else if (d->storeExpanded(index)) {
+ emit expanded(index);
+ }
+}
+
+/*!
+ \fn void QTreeView::collapse(const QModelIndex &index)
+
+ Collapses the model item specified by the \a index.
+
+ \sa collapsed()
+*/
+void QTreeView::collapse(const QModelIndex &index)
+{
+ Q_D(QTreeView);
+ if (!d->isIndexValid(index))
+ return;
+ //if the current item is now invisible, the autoscroll will expand the tree to see it, so disable the autoscroll
+ d->delayedAutoScroll.stop();
+
+ if (d->delayedPendingLayout) {
+ //A complete relayout is going to be performed, just un-store the expanded index, no need to layout.
+ if (d->isPersistent(index) && d->expandedIndexes.remove(index))
+ emit collapsed(index);
+ return;
+ }
+ int i = d->viewIndex(index);
+ if (i != -1) { // is visible
+ d->collapse(i, true);
+ if (!d->isAnimating()) {
+ updateGeometries();
+ viewport()->update();
+ }
+ } else {
+ if (d->isPersistent(index) && d->expandedIndexes.remove(index))
+ emit collapsed(index);
+ }
+}
+
+/*!
+ \fn bool QTreeView::isExpanded(const QModelIndex &index) const
+
+ Returns true if the model item \a index is expanded; otherwise returns
+ false.
+
+ \sa expand(), expanded(), setExpanded()
+*/
+bool QTreeView::isExpanded(const QModelIndex &index) const
+{
+ Q_D(const QTreeView);
+ return d->isIndexExpanded(index);
+}
+
+/*!
+ Sets the item referred to by \a index to either collapse or expanded,
+ depending on the value of \a expanded.
+
+ \sa expanded(), expand(), isExpanded()
+*/
+void QTreeView::setExpanded(const QModelIndex &index, bool expanded)
+{
+ if (expanded)
+ this->expand(index);
+ else
+ this->collapse(index);
+}
+
+/*!
+ \since 4.2
+ \property QTreeView::sortingEnabled
+ \brief whether sorting is enabled
+
+ If this property is true, sorting is enabled for the tree; if the property
+ is false, sorting is not enabled. The default value is false.
+
+ \note In order to avoid performance issues, it is recommended that
+ sorting is enabled \e after inserting the items into the tree.
+ Alternatively, you could also insert the items into a list before inserting
+ the items into the tree.
+
+ \sa sortByColumn()
+*/
+
+void QTreeView::setSortingEnabled(bool enable)
+{
+ Q_D(QTreeView);
+ d->sortingEnabled = enable;
+ header()->setSortIndicatorShown(enable);
+ header()->setClickable(enable);
+ if (enable) {
+ connect(header(), SIGNAL(sortIndicatorChanged(int,Qt::SortOrder)),
+ this, SLOT(_q_sortIndicatorChanged(int, Qt::SortOrder)));
+ sortByColumn(header()->sortIndicatorSection(), header()->sortIndicatorOrder());
+ } else {
+ disconnect(header(), SIGNAL(sortIndicatorChanged(int,Qt::SortOrder)),
+ this, SLOT(_q_sortIndicatorChanged(int, Qt::SortOrder)));
+ }
+}
+
+bool QTreeView::isSortingEnabled() const
+{
+ Q_D(const QTreeView);
+ return d->sortingEnabled;
+}
+
+/*!
+ \since 4.2
+ \property QTreeView::animated
+ \brief whether animations are enabled
+
+ If this property is true the treeview will animate expandsion
+ and collasping of branches. If this property is false, the treeview
+ will expand or collapse branches immediately without showing
+ the animation.
+
+ By default, this property is false.
+*/
+
+void QTreeView::setAnimated(bool animate)
+{
+ Q_D(QTreeView);
+ d->animationsEnabled = animate;
+}
+
+bool QTreeView::isAnimated() const
+{
+ Q_D(const QTreeView);
+ return d->animationsEnabled;
+}
+
+/*!
+ \since 4.2
+ \property QTreeView::allColumnsShowFocus
+ \brief whether items should show keyboard focus using all columns
+
+ If this property is true all columns will show focus, otherwise only
+ one column will show focus.
+
+ The default is false.
+*/
+
+void QTreeView::setAllColumnsShowFocus(bool enable)
+{
+ Q_D(QTreeView);
+ if (d->allColumnsShowFocus == enable)
+ return;
+ if (d->selectionModel) {
+ if (enable) {
+ QObject::connect(d->selectionModel, SIGNAL(currentChanged(QModelIndex,QModelIndex)),
+ this, SLOT(_q_currentChanged(QModelIndex,QModelIndex)));
+ } else {
+ QObject::disconnect(d->selectionModel, SIGNAL(currentChanged(QModelIndex,QModelIndex)),
+ this, SLOT(_q_currentChanged(QModelIndex,QModelIndex)));
+ }
+ }
+ d->allColumnsShowFocus = enable;
+ d->viewport->update();
+}
+
+bool QTreeView::allColumnsShowFocus() const
+{
+ Q_D(const QTreeView);
+ return d->allColumnsShowFocus;
+}
+
+/*!
+ \property QTreeView::wordWrap
+ \brief the item text word-wrapping policy
+ \since 4.3
+
+ If this property is true then the item text is wrapped where
+ necessary at word-breaks; otherwise it is not wrapped at all.
+ This property is false by default.
+
+ Note that even if wrapping is enabled, the cell will not be
+ expanded to fit all text. Ellipsis will be inserted according to
+ the current \l{QAbstractItemView::}{textElideMode}.
+*/
+void QTreeView::setWordWrap(bool on)
+{
+ Q_D(QTreeView);
+ if (d->wrapItemText == on)
+ return;
+ d->wrapItemText = on;
+ d->doDelayedItemsLayout();
+}
+
+bool QTreeView::wordWrap() const
+{
+ Q_D(const QTreeView);
+ return d->wrapItemText;
+}
+
+
+/*!
+ \reimp
+ */
+void QTreeView::keyboardSearch(const QString &search)
+{
+ Q_D(QTreeView);
+ if (!d->model->rowCount(d->root) || !d->model->columnCount(d->root))
+ return;
+
+ QModelIndex start;
+ if (currentIndex().isValid())
+ start = currentIndex();
+ else
+ start = d->model->index(0, 0, d->root);
+
+ QTime now(QTime::currentTime());
+ bool skipRow = false;
+ if (search.isEmpty()
+ || (d->keyboardInputTime.msecsTo(now) > QApplication::keyboardInputInterval())) {
+ d->keyboardInput = search;
+ skipRow = true;
+ } else {
+ d->keyboardInput += search;
+ }
+ d->keyboardInputTime = now;
+
+ // special case for searches with same key like 'aaaaa'
+ bool sameKey = false;
+ if (d->keyboardInput.length() > 1) {
+ int c = d->keyboardInput.count(d->keyboardInput.at(d->keyboardInput.length() - 1));
+ sameKey = (c == d->keyboardInput.length());
+ if (sameKey)
+ skipRow = true;
+ }
+
+ // skip if we are searching for the same key or a new search started
+ if (skipRow) {
+ if (indexBelow(start).isValid())
+ start = indexBelow(start);
+ else
+ start = d->model->index(0, start.column(), d->root);
+ }
+
+ d->executePostedLayout();
+ int startIndex = d->viewIndex(start);
+ if (startIndex <= -1)
+ return;
+
+ int previousLevel = -1;
+ int bestAbove = -1;
+ int bestBelow = -1;
+ QString searchString = sameKey ? QString(d->keyboardInput.at(0)) : d->keyboardInput;
+ for (int i = 0; i < d->viewItems.count(); ++i) {
+ if ((int)d->viewItems.at(i).level > previousLevel) {
+ QModelIndex searchFrom = d->viewItems.at(i).index;
+ if (searchFrom.parent() == start.parent())
+ searchFrom = start;
+ QModelIndexList match = d->model->match(searchFrom, Qt::DisplayRole, searchString);
+ if (match.count()) {
+ int hitIndex = d->viewIndex(match.at(0));
+ if (hitIndex >= 0 && hitIndex < startIndex)
+ bestAbove = bestAbove == -1 ? hitIndex : qMin(hitIndex, bestAbove);
+ else if (hitIndex >= startIndex)
+ bestBelow = bestBelow == -1 ? hitIndex : qMin(hitIndex, bestBelow);
+ }
+ }
+ previousLevel = d->viewItems.at(i).level;
+ }
+
+ QModelIndex index;
+ if (bestBelow > -1)
+ index = d->viewItems.at(bestBelow).index;
+ else if (bestAbove > -1)
+ index = d->viewItems.at(bestAbove).index;
+
+ if (index.isValid()) {
+ QItemSelectionModel::SelectionFlags flags = (d->selectionMode == SingleSelection
+ ? QItemSelectionModel::SelectionFlags(
+ QItemSelectionModel::ClearAndSelect
+ |d->selectionBehaviorFlags())
+ : QItemSelectionModel::SelectionFlags(
+ QItemSelectionModel::NoUpdate));
+ selectionModel()->setCurrentIndex(index, flags);
+ }
+}
+
+/*!
+ Returns the rectangle on the viewport occupied by the item at \a index.
+ If the index is not visible or explicitly hidden, the returned rectangle is invalid.
+*/
+QRect QTreeView::visualRect(const QModelIndex &index) const
+{
+ Q_D(const QTreeView);
+
+ if (!d->isIndexValid(index) || isIndexHidden(index))
+ return QRect();
+
+ d->executePostedLayout();
+
+ int vi = d->viewIndex(index);
+ if (vi < 0)
+ return QRect();
+
+ bool spanning = d->viewItems.at(vi).spanning;
+
+ // if we have a spanning item, make the selection stretch from left to right
+ int x = (spanning ? 0 : columnViewportPosition(index.column()));
+ int w = (spanning ? d->header->length() : columnWidth(index.column()));
+ // handle indentation
+ if (index.column() == 0) {
+ int i = d->indentationForItem(vi);
+ w -= i;
+ if (!isRightToLeft())
+ x += i;
+ }
+
+ int y = d->coordinateForItem(vi);
+ int h = d->itemHeight(vi);
+
+ return QRect(x, y, w, h);
+}
+
+/*!
+ Scroll the contents of the tree view until the given model item
+ \a index is visible. The \a hint parameter specifies more
+ precisely where the item should be located after the
+ operation.
+ If any of the parents of the model item are collapsed, they will
+ be expanded to ensure that the model item is visible.
+*/
+void QTreeView::scrollTo(const QModelIndex &index, ScrollHint hint)
+{
+ Q_D(QTreeView);
+
+ if (!d->isIndexValid(index))
+ return;
+
+ d->executePostedLayout();
+ d->updateScrollBars();
+
+ // Expand all parents if the parent(s) of the node are not expanded.
+ QModelIndex parent = index.parent();
+ while (parent.isValid() && state() == NoState && d->itemsExpandable) {
+ if (!isExpanded(parent))
+ expand(parent);
+ parent = d->model->parent(parent);
+ }
+
+ int item = d->viewIndex(index);
+ if (item < 0)
+ return;
+
+ QRect area = d->viewport->rect();
+
+ // vertical
+ if (verticalScrollMode() == QAbstractItemView::ScrollPerItem) {
+ int top = verticalScrollBar()->value();
+ int bottom = top + verticalScrollBar()->pageStep();
+ if (hint == EnsureVisible && item >= top && item < bottom) {
+ // nothing to do
+ } else if (hint == PositionAtTop || (hint == EnsureVisible && item < top)) {
+ verticalScrollBar()->setValue(item);
+ } else { // PositionAtBottom or PositionAtCenter
+ int itemLocation = item;
+ int y = (hint == PositionAtCenter
+ ? area.height() / 2
+ : area.height());
+ while (y > 0 && item > 0)
+ y -= d->itemHeight(item--);
+ // end up half over the top of the area
+ if (y < 0 && item < itemLocation)
+ ++item;
+ // end up half over the bottom of the area
+ if (item >= 0 && item < itemLocation)
+ ++item;
+ verticalScrollBar()->setValue(item);
+ }
+ } else { // ScrollPerPixel
+ QRect rect(columnViewportPosition(index.column()),
+ d->coordinateForItem(item), // ### slow for items outside the view
+ columnWidth(index.column()),
+ d->itemHeight(item));
+
+ if (rect.isEmpty()) {
+ // nothing to do
+ } else if (hint == EnsureVisible && area.contains(rect)) {
+ d->setDirtyRegion(rect);
+ // nothing to do
+ } else {
+ bool above = (hint == EnsureVisible
+ && (rect.top() < area.top()
+ || area.height() < rect.height()));
+ bool below = (hint == EnsureVisible
+ && rect.bottom() > area.bottom()
+ && rect.height() < area.height());
+
+ int verticalValue = verticalScrollBar()->value();
+ if (hint == PositionAtTop || above)
+ verticalValue += rect.top();
+ else if (hint == PositionAtBottom || below)
+ verticalValue += rect.bottom() - area.height();
+ else if (hint == PositionAtCenter)
+ verticalValue += rect.top() - ((area.height() - rect.height()) / 2);
+ verticalScrollBar()->setValue(verticalValue);
+ }
+ }
+ // horizontal
+ int viewportWidth = d->viewport->width();
+ int horizontalOffset = d->header->offset();
+ int horizontalPosition = d->header->sectionPosition(index.column());
+ int cellWidth = d->header->sectionSize(index.column());
+
+ if (hint == PositionAtCenter) {
+ horizontalScrollBar()->setValue(horizontalPosition - ((viewportWidth - cellWidth) / 2));
+ } else {
+ if (horizontalPosition - horizontalOffset < 0 || cellWidth > viewportWidth)
+ horizontalScrollBar()->setValue(horizontalPosition);
+ else if (horizontalPosition - horizontalOffset + cellWidth > viewportWidth)
+ horizontalScrollBar()->setValue(horizontalPosition - viewportWidth + cellWidth);
+ }
+}
+
+/*!
+ \reimp
+*/
+void QTreeView::timerEvent(QTimerEvent *event)
+{
+ Q_D(QTreeView);
+ if (event->timerId() == d->columnResizeTimerID) {
+ updateGeometries();
+ killTimer(d->columnResizeTimerID);
+ d->columnResizeTimerID = 0;
+ QRect rect;
+ int viewportHeight = d->viewport->height();
+ int viewportWidth = d->viewport->width();
+ for (int i = d->columnsToUpdate.size() - 1; i >= 0; --i) {
+ int column = d->columnsToUpdate.at(i);
+ int x = columnViewportPosition(column);
+ if (isRightToLeft())
+ rect |= QRect(0, 0, x + columnWidth(column), viewportHeight);
+ else
+ rect |= QRect(x, 0, viewportWidth - x, viewportHeight);
+ }
+ d->viewport->update(rect.normalized());
+ d->columnsToUpdate.clear();
+ } else if (event->timerId() == d->openTimer.timerId()) {
+ QPoint pos = d->viewport->mapFromGlobal(QCursor::pos());
+ if (state() == QAbstractItemView::DraggingState
+ && d->viewport->rect().contains(pos)) {
+ QModelIndex index = indexAt(pos);
+ setExpanded(index, !isExpanded(index));
+ }
+ d->openTimer.stop();
+ }
+
+ QAbstractItemView::timerEvent(event);
+}
+
+/*!
+ \reimp
+*/
+#ifndef QT_NO_DRAGANDDROP
+void QTreeView::dragMoveEvent(QDragMoveEvent *event)
+{
+ Q_D(QTreeView);
+ if (d->autoExpandDelay >= 0)
+ d->openTimer.start(d->autoExpandDelay, this);
+ QAbstractItemView::dragMoveEvent(event);
+}
+#endif
+
+/*!
+ \reimp
+*/
+bool QTreeView::viewportEvent(QEvent *event)
+{
+ Q_D(QTreeView);
+ switch (event->type()) {
+ case QEvent::HoverEnter:
+ case QEvent::HoverLeave:
+ case QEvent::HoverMove: {
+ QHoverEvent *he = static_cast<QHoverEvent*>(event);
+ int oldBranch = d->hoverBranch;
+ d->hoverBranch = d->itemDecorationAt(he->pos());
+ if (oldBranch != d->hoverBranch) {
+ QModelIndex oldIndex = d->modelIndex(oldBranch),
+ newIndex = d->modelIndex(d->hoverBranch);
+ if (oldIndex != newIndex) {
+ QRect oldRect = visualRect(oldIndex);
+ QRect newRect = visualRect(newIndex);
+ viewport()->update(oldRect.left() - d->indent, oldRect.top(), d->indent, oldRect.height());
+ viewport()->update(newRect.left() - d->indent, newRect.top(), d->indent, newRect.height());
+ }
+ }
+ if (selectionBehavior() == QAbstractItemView::SelectRows) {
+ QModelIndex newHoverIndex = indexAt(he->pos());
+ if (d->hover != newHoverIndex) {
+ QRect oldHoverRect = visualRect(d->hover);
+ QRect newHoverRect = visualRect(newHoverIndex);
+ viewport()->update(QRect(0, newHoverRect.y(), viewport()->width(), newHoverRect.height()));
+ viewport()->update(QRect(0, oldHoverRect.y(), viewport()->width(), oldHoverRect.height()));
+ }
+ }
+ break; }
+ default:
+ break;
+ }
+ return QAbstractItemView::viewportEvent(event);
+}
+
+/*!
+ \reimp
+*/
+void QTreeView::paintEvent(QPaintEvent *event)
+{
+ Q_D(QTreeView);
+ d->executePostedLayout();
+ QPainter painter(viewport());
+ if (d->isAnimating()) {
+ drawTree(&painter, event->region() - d->animationRect());
+ d->drawAnimatedOperation(&painter);
+ } else {
+ drawTree(&painter, event->region());
+#ifndef QT_NO_DRAGANDDROP
+ d->paintDropIndicator(&painter);
+#endif
+ }
+}
+
+void QTreeViewPrivate::paintAlternatingRowColors(QPainter *painter, QStyleOptionViewItemV4 *option, int y, int bottom) const
+{
+ Q_Q(const QTreeView);
+ if (!alternatingColors || !q->style()->styleHint(QStyle::SH_ItemView_PaintAlternatingRowColorsForEmptyArea, option, q))
+ return;
+ int rowHeight = defaultItemHeight;
+ if (rowHeight <= 0) {
+ rowHeight = itemDelegate->sizeHint(*option, QModelIndex()).height();
+ if (rowHeight <= 0)
+ return;
+ }
+ while (y <= bottom) {
+ option->rect.setRect(0, y, viewport->width(), rowHeight);
+ if (current & 1) {
+ option->features |= QStyleOptionViewItemV2::Alternate;
+ } else {
+ option->features &= ~QStyleOptionViewItemV2::Alternate;
+ }
+ ++current;
+ q->style()->drawPrimitive(QStyle::PE_PanelItemViewRow, option, painter, q);
+ y += rowHeight;
+ }
+}
+
+bool QTreeViewPrivate::expandOrCollapseItemAtPos(const QPoint &pos)
+{
+ Q_Q(QTreeView);
+ // we want to handle mousePress in EditingState (persistent editors)
+ if ((q->state() != QAbstractItemView::NoState
+ && q->state() != QAbstractItemView::EditingState)
+ || !viewport->rect().contains(pos))
+ return true;
+
+ int i = itemDecorationAt(pos);
+ if ((i != -1) && q->itemsExpandable() && hasVisibleChildren(viewItems.at(i).index)) {
+ if (viewItems.at(i).expanded)
+ collapse(i, true);
+ else
+ expand(i, true);
+ if (!isAnimating()) {
+ q->updateGeometries();
+ q->viewport()->update();
+ }
+ return true;
+ }
+ return false;
+}
+
+void QTreeViewPrivate::_q_modelDestroyed()
+{
+ //we need to clear that list because it contais QModelIndex to
+ //the model currently being destroyed
+ viewItems.clear();
+ QAbstractItemViewPrivate::_q_modelDestroyed();
+}
+
+/*!
+ \since 4.2
+ Draws the part of the tree intersecting the given \a region using the specified
+ \a painter.
+
+ \sa paintEvent()
+*/
+void QTreeView::drawTree(QPainter *painter, const QRegion &region) const
+{
+ Q_D(const QTreeView);
+ const QVector<QTreeViewItem> viewItems = d->viewItems;
+
+ QStyleOptionViewItemV4 option = d->viewOptionsV4();
+ const QStyle::State state = option.state;
+ d->current = 0;
+
+ if (viewItems.count() == 0 || d->header->count() == 0 || !d->itemDelegate) {
+ d->paintAlternatingRowColors(painter, &option, 0, region.boundingRect().bottom()+1);
+ return;
+ }
+
+ int firstVisibleItemOffset = 0;
+ const int firstVisibleItem = d->firstVisibleItem(&firstVisibleItemOffset);
+ if (firstVisibleItem < 0) {
+ d->paintAlternatingRowColors(painter, &option, 0, region.boundingRect().bottom()+1);
+ return;
+ }
+
+ const int viewportWidth = d->viewport->width();
+
+ QVector<QRect> rects = region.rects();
+ QVector<int> drawn;
+ bool multipleRects = (rects.size() > 1);
+ for (int a = 0; a < rects.size(); ++a) {
+ const QRect area = (multipleRects
+ ? QRect(0, rects.at(a).y(), viewportWidth, rects.at(a).height())
+ : rects.at(a));
+ d->leftAndRight = d->startAndEndColumns(area);
+
+ int i = firstVisibleItem; // the first item at the top of the viewport
+ int y = firstVisibleItemOffset; // we may only see part of the first item
+
+ // start at the top of the viewport and iterate down to the update area
+ for (; i < viewItems.count(); ++i) {
+ const int itemHeight = d->itemHeight(i);
+ if (y + itemHeight > area.top())
+ break;
+ y += itemHeight;
+ }
+
+ // paint the visible rows
+ for (; i < viewItems.count() && y <= area.bottom(); ++i) {
+ const int itemHeight = d->itemHeight(i);
+ option.rect.setRect(0, y, viewportWidth, itemHeight);
+ option.state = state | (viewItems.at(i).expanded
+ ? QStyle::State_Open : QStyle::State_None);
+ d->current = i;
+ d->spanning = viewItems.at(i).spanning;
+ if (!multipleRects || !drawn.contains(i)) {
+ drawRow(painter, option, viewItems.at(i).index);
+ if (multipleRects) // even if the rect only intersects the item,
+ drawn.append(i); // the entire item will be painted
+ }
+ y += itemHeight;
+ }
+
+ if (y <= area.bottom()) {
+ d->current = i;
+ d->paintAlternatingRowColors(painter, &option, y, area.bottom());
+ }
+ }
+}
+
+/// ### move to QObject :)
+static inline bool ancestorOf(QObject *widget, QObject *other)
+{
+ for (QObject *parent = other; parent != 0; parent = parent->parent()) {
+ if (parent == widget)
+ return true;
+ }
+ return false;
+}
+
+/*!
+ Draws the row in the tree view that contains the model item \a index,
+ using the \a painter given. The \a option control how the item is
+ displayed.
+
+ \sa setAlternatingRowColors()
+*/
+void QTreeView::drawRow(QPainter *painter, const QStyleOptionViewItem &option,
+ const QModelIndex &index) const
+{
+ Q_D(const QTreeView);
+ QStyleOptionViewItemV4 opt = option;
+ const QPoint offset = d->scrollDelayOffset;
+ const int y = option.rect.y() + offset.y();
+ const QModelIndex parent = index.parent();
+ const QHeaderView *header = d->header;
+ const QModelIndex current = currentIndex();
+ const QModelIndex hover = d->hover;
+ const bool reverse = isRightToLeft();
+ const QStyle::State state = opt.state;
+ const bool spanning = d->spanning;
+ const int left = (spanning ? header->visualIndex(0) : d->leftAndRight.first);
+ const int right = (spanning ? header->visualIndex(0) : d->leftAndRight.second);
+ const bool alternate = d->alternatingColors;
+ const bool enabled = (state & QStyle::State_Enabled) != 0;
+ const bool allColumnsShowFocus = d->allColumnsShowFocus;
+
+
+ // when the row contains an index widget which has focus,
+ // we want to paint the entire row as active
+ bool indexWidgetHasFocus = false;
+ if ((current.row() == index.row()) && !d->editors.isEmpty()) {
+ const int r = index.row();
+ QWidget *fw = QApplication::focusWidget();
+ for (int c = 0; c < header->count(); ++c) {
+ QModelIndex idx = d->model->index(r, c, parent);
+ if (QWidget *editor = indexWidget(idx)) {
+ if (ancestorOf(editor, fw)) {
+ indexWidgetHasFocus = true;
+ break;
+ }
+ }
+ }
+ }
+
+ const bool widgetHasFocus = hasFocus();
+ bool currentRowHasFocus = false;
+ if (allColumnsShowFocus && widgetHasFocus && current.isValid()) {
+ // check if the focus index is before or after the visible columns
+ const int r = index.row();
+ for (int c = 0; c < left && !currentRowHasFocus; ++c) {
+ QModelIndex idx = d->model->index(r, c, parent);
+ currentRowHasFocus = (idx == current);
+ }
+ QModelIndex parent = d->model->parent(index);
+ for (int c = right; c < header->count() && !currentRowHasFocus; ++c) {
+ currentRowHasFocus = (d->model->index(r, c, parent) == current);
+ }
+ }
+
+ // ### special case: treeviews with multiple columns draw
+ // the selections differently than with only one column
+ opt.showDecorationSelected = (d->selectionBehavior & SelectRows)
+ || option.showDecorationSelected;
+
+ int width, height = option.rect.height();
+ int position;
+ QModelIndex modelIndex;
+ int columnCount = header->count();
+ const bool hoverRow = selectionBehavior() == QAbstractItemView::SelectRows
+ && index.parent() == hover.parent()
+ && index.row() == hover.row();
+
+ /* 'left' and 'right' are the left-most and right-most visible visual indices.
+ Compute the first visible logical indices before and after the left and right.
+ We will use these values to determine the QStyleOptionViewItemV4::viewItemPosition. */
+ int logicalIndexBeforeLeft = -1, logicalIndexAfterRight = -1;
+ for (int visualIndex = left - 1; visualIndex >= 0; --visualIndex) {
+ int logicalIndex = header->logicalIndex(visualIndex);
+ if (!header->isSectionHidden(logicalIndex)) {
+ logicalIndexBeforeLeft = logicalIndex;
+ break;
+ }
+ }
+ QVector<int> logicalIndices; // vector of currently visibly logical indices
+ for (int visualIndex = left; visualIndex < columnCount; ++visualIndex) {
+ int logicalIndex = header->logicalIndex(visualIndex);
+ if (!header->isSectionHidden(logicalIndex)) {
+ if (visualIndex > right) {
+ logicalIndexAfterRight = logicalIndex;
+ break;
+ }
+ logicalIndices.append(logicalIndex);
+ }
+ }
+
+ for (int currentLogicalSection = 0; currentLogicalSection < logicalIndices.count(); ++currentLogicalSection) {
+ int headerSection = logicalIndices.at(currentLogicalSection);
+ position = columnViewportPosition(headerSection) + offset.x();
+ width = header->sectionSize(headerSection);
+
+ if (spanning) {
+ int lastSection = header->logicalIndex(header->count() - 1);
+ if (!reverse) {
+ width = columnViewportPosition(lastSection) + header->sectionSize(lastSection) - position;
+ } else {
+ width += position - columnViewportPosition(lastSection);
+ position = columnViewportPosition(lastSection);
+ }
+ }
+
+ modelIndex = d->model->index(index.row(), headerSection, parent);
+ if (!modelIndex.isValid())
+ continue;
+ opt.state = state;
+
+ // determine the viewItemPosition depending on the position of column 0
+ int nextLogicalSection = currentLogicalSection + 1 >= logicalIndices.count()
+ ? logicalIndexAfterRight
+ : logicalIndices.at(currentLogicalSection + 1);
+ int prevLogicalSection = currentLogicalSection - 1 < 0
+ ? logicalIndexBeforeLeft
+ : logicalIndices.at(currentLogicalSection - 1);
+ if (columnCount == 1 || (nextLogicalSection == 0 && prevLogicalSection == -1)
+ || (headerSection == 0 && nextLogicalSection == -1))
+ opt.viewItemPosition = QStyleOptionViewItemV4::OnlyOne;
+ else if (headerSection == 0 || (nextLogicalSection != 0 && prevLogicalSection == -1))
+ opt.viewItemPosition = QStyleOptionViewItemV4::Beginning;
+ else if (nextLogicalSection == 0 || nextLogicalSection == -1)
+ opt.viewItemPosition = QStyleOptionViewItemV4::End;
+ else
+ opt.viewItemPosition = QStyleOptionViewItemV4::Middle;
+
+ // fake activeness when row editor has focus
+ if (indexWidgetHasFocus)
+ opt.state |= QStyle::State_Active;
+
+ if (d->selectionModel->isSelected(modelIndex))
+ opt.state |= QStyle::State_Selected;
+ if (widgetHasFocus && (current == modelIndex)) {
+ if (allColumnsShowFocus)
+ currentRowHasFocus = true;
+ else
+ opt.state |= QStyle::State_HasFocus;
+ }
+ if ((hoverRow || modelIndex == hover)
+ && (option.showDecorationSelected || (d->hoverBranch == -1)))
+ opt.state |= QStyle::State_MouseOver;
+ else
+ opt.state &= ~QStyle::State_MouseOver;
+
+ if (enabled) {
+ QPalette::ColorGroup cg;
+ if ((d->model->flags(modelIndex) & Qt::ItemIsEnabled) == 0) {
+ opt.state &= ~QStyle::State_Enabled;
+ cg = QPalette::Disabled;
+ } else if (opt.state & QStyle::State_Active) {
+ cg = QPalette::Active;
+ } else {
+ cg = QPalette::Inactive;
+ }
+ opt.palette.setCurrentColorGroup(cg);
+ }
+
+ if (alternate) {
+ if (d->current & 1) {
+ opt.features |= QStyleOptionViewItemV2::Alternate;
+ } else {
+ opt.features &= ~QStyleOptionViewItemV2::Alternate;
+ }
+ }
+
+ /* Prior to Qt 4.3, the background of the branch (in selected state and
+ alternate row color was provided by the view. For backward compatibility,
+ this is now delegated to the style using PE_PanelViewItemRow which
+ does the appropriate fill */
+ if (headerSection == 0) {
+ const int i = d->indentationForItem(d->current);
+ QRect branches(reverse ? position + width - i : position, y, i, height);
+ const bool setClipRect = branches.width() > width;
+ if (setClipRect) {
+ painter->save();
+ painter->setClipRect(QRect(position, y, width, height));
+ }
+ // draw background for the branch (selection + alternate row)
+ opt.rect = branches;
+ style()->drawPrimitive(QStyle::PE_PanelItemViewRow, &opt, painter, this);
+
+ // draw background of the item (only alternate row). rest of the background
+ // is provided by the delegate
+ QStyle::State oldState = opt.state;
+ opt.state &= ~QStyle::State_Selected;
+ opt.rect.setRect(reverse ? position : i + position, y, width - i, height);
+ style()->drawPrimitive(QStyle::PE_PanelItemViewRow, &opt, painter, this);
+ opt.state = oldState;
+
+ drawBranches(painter, branches, index);
+ if (setClipRect)
+ painter->restore();
+ } else {
+ QStyle::State oldState = opt.state;
+ opt.state &= ~QStyle::State_Selected;
+ opt.rect.setRect(position, y, width, height);
+ style()->drawPrimitive(QStyle::PE_PanelItemViewRow, &opt, painter, this);
+ opt.state = oldState;
+ }
+
+ if (const QWidget *widget = d->editorForIndex(modelIndex).editor) {
+ painter->save();
+ painter->setClipRect(widget->geometry());
+ d->delegateForIndex(modelIndex)->paint(painter, opt, modelIndex);
+ painter->restore();
+ } else {
+ d->delegateForIndex(modelIndex)->paint(painter, opt, modelIndex);
+ }
+ }
+
+ if (currentRowHasFocus) {
+ QStyleOptionFocusRect o;
+ o.QStyleOption::operator=(option);
+ o.state |= QStyle::State_KeyboardFocusChange;
+ QPalette::ColorGroup cg = (option.state & QStyle::State_Enabled)
+ ? QPalette::Normal : QPalette::Disabled;
+ o.backgroundColor = option.palette.color(cg, d->selectionModel->isSelected(index)
+ ? QPalette::Highlight : QPalette::Background);
+ int x = 0;
+ if (!option.showDecorationSelected)
+ x = header->sectionPosition(0) + d->indentationForItem(d->current);
+ QRect focusRect(x - header->offset(), y, header->length() - x, height);
+ o.rect = style()->visualRect(layoutDirection(), d->viewport->rect(), focusRect);
+ style()->drawPrimitive(QStyle::PE_FrameFocusRect, &o, painter);
+ // if we show focus on all columns and the first section is moved,
+ // we have to split the focus rect into two rects
+ if (allColumnsShowFocus && !option.showDecorationSelected
+ && header->sectionsMoved() && (header->visualIndex(0) != 0)) {
+ QRect sectionRect(0, y, header->sectionPosition(0), height);
+ o.rect = style()->visualRect(layoutDirection(), d->viewport->rect(), sectionRect);
+ style()->drawPrimitive(QStyle::PE_FrameFocusRect, &o, painter);
+ }
+ }
+}
+
+/*!
+ Draws the branches in the tree view on the same row as the model item
+ \a index, using the \a painter given. The branches are drawn in the
+ rectangle specified by \a rect.
+*/
+void QTreeView::drawBranches(QPainter *painter, const QRect &rect,
+ const QModelIndex &index) const
+{
+ Q_D(const QTreeView);
+ const bool reverse = isRightToLeft();
+ const int indent = d->indent;
+ const int outer = d->rootDecoration ? 0 : 1;
+ const int item = d->current;
+ const QTreeViewItem &viewItem = d->viewItems.at(item);
+ int level = viewItem.level;
+ QRect primitive(reverse ? rect.left() : rect.right() + 1, rect.top(), indent, rect.height());
+
+ QModelIndex parent = index.parent();
+ QModelIndex current = parent;
+ QModelIndex ancestor = current.parent();
+
+ QStyleOptionViewItemV2 opt = viewOptions();
+ QStyle::State extraFlags = QStyle::State_None;
+ if (isEnabled())
+ extraFlags |= QStyle::State_Enabled;
+ if (window()->isActiveWindow())
+ extraFlags |= QStyle::State_Active;
+ QPoint oldBO = painter->brushOrigin();
+ if (verticalScrollMode() == QAbstractItemView::ScrollPerPixel)
+ painter->setBrushOrigin(QPoint(0, verticalOffset()));
+
+ if (d->alternatingColors) {
+ if (d->current & 1) {
+ opt.features |= QStyleOptionViewItemV2::Alternate;
+ } else {
+ opt.features &= ~QStyleOptionViewItemV2::Alternate;
+ }
+ }
+
+ // When hovering over a row, pass State_Hover for painting the branch
+ // indicators if it has the decoration (aka branch) selected.
+ bool hoverRow = selectionBehavior() == QAbstractItemView::SelectRows
+ && opt.showDecorationSelected
+ && index.parent() == d->hover.parent()
+ && index.row() == d->hover.row();
+
+ if (d->selectionModel->isSelected(index))
+ extraFlags |= QStyle::State_Selected;
+
+ if (level >= outer) {
+ // start with the innermost branch
+ primitive.moveLeft(reverse ? primitive.left() : primitive.left() - indent);
+ opt.rect = primitive;
+
+ const bool expanded = viewItem.expanded;
+ const bool children = (((expanded && viewItem.total > 0)) // already laid out and has children
+ || d->hasVisibleChildren(index)); // not laid out yet, so we don't know
+ bool moreSiblings = false;
+ if (d->hiddenIndexes.isEmpty())
+ moreSiblings = (d->model->rowCount(parent) - 1 > index.row());
+ else
+ moreSiblings = ((d->viewItems.size() > item +1)
+ && (d->viewItems.at(item + 1).index.parent() == parent));
+
+ opt.state = QStyle::State_Item | extraFlags
+ | (moreSiblings ? QStyle::State_Sibling : QStyle::State_None)
+ | (children ? QStyle::State_Children : QStyle::State_None)
+ | (expanded ? QStyle::State_Open : QStyle::State_None);
+ if (hoverRow || item == d->hoverBranch)
+ opt.state |= QStyle::State_MouseOver;
+ else
+ opt.state &= ~QStyle::State_MouseOver;
+ style()->drawPrimitive(QStyle::PE_IndicatorBranch, &opt, painter, this);
+ }
+ // then go out level by level
+ for (--level; level >= outer; --level) { // we have already drawn the innermost branch
+ primitive.moveLeft(reverse ? primitive.left() + indent : primitive.left() - indent);
+ opt.rect = primitive;
+ opt.state = extraFlags;
+ bool moreSiblings = false;
+ if (d->hiddenIndexes.isEmpty()) {
+ moreSiblings = (d->model->rowCount(ancestor) - 1 > current.row());
+ } else {
+ int successor = item + viewItem.total + 1;
+ while (successor < d->viewItems.size()
+ && d->viewItems.at(successor).level >= uint(level)) {
+ const QTreeViewItem &successorItem = d->viewItems.at(successor);
+ if (successorItem.level == uint(level)) {
+ moreSiblings = true;
+ break;
+ }
+ successor += successorItem.total + 1;
+ }
+ }
+ if (moreSiblings)
+ opt.state |= QStyle::State_Sibling;
+ if (hoverRow || item == d->hoverBranch)
+ opt.state |= QStyle::State_MouseOver;
+ else
+ opt.state &= ~QStyle::State_MouseOver;
+ style()->drawPrimitive(QStyle::PE_IndicatorBranch, &opt, painter, this);
+ current = ancestor;
+ ancestor = current.parent();
+ }
+ painter->setBrushOrigin(oldBO);
+}
+
+/*!
+ \reimp
+*/
+void QTreeView::mousePressEvent(QMouseEvent *event)
+{
+ Q_D(QTreeView);
+ bool handled = false;
+ if (style()->styleHint(QStyle::SH_Q3ListViewExpand_SelectMouseType, 0, this) == QEvent::MouseButtonPress)
+ handled = d->expandOrCollapseItemAtPos(event->pos());
+ if (!handled && d->itemDecorationAt(event->pos()) == -1)
+ QAbstractItemView::mousePressEvent(event);
+}
+
+/*!
+ \reimp
+*/
+void QTreeView::mouseReleaseEvent(QMouseEvent *event)
+{
+ Q_D(QTreeView);
+ if (d->itemDecorationAt(event->pos()) == -1) {
+ QAbstractItemView::mouseReleaseEvent(event);
+ } else {
+ if (state() == QAbstractItemView::DragSelectingState)
+ setState(QAbstractItemView::NoState);
+ if (style()->styleHint(QStyle::SH_Q3ListViewExpand_SelectMouseType, 0, this) == QEvent::MouseButtonRelease)
+ d->expandOrCollapseItemAtPos(event->pos());
+ }
+}
+
+/*!
+ \reimp
+*/
+void QTreeView::mouseDoubleClickEvent(QMouseEvent *event)
+{
+ Q_D(QTreeView);
+ if (state() != NoState || !d->viewport->rect().contains(event->pos()))
+ return;
+
+ int i = d->itemDecorationAt(event->pos());
+ if (i == -1) {
+ i = d->itemAtCoordinate(event->y());
+ if (i == -1)
+ return; // user clicked outside the items
+
+ const QModelIndex &index = d->viewItems.at(i).index;
+
+ int column = d->header->logicalIndexAt(event->x());
+ QPersistentModelIndex persistent = index.sibling(index.row(), column);
+
+ if (d->pressedIndex != persistent) {
+ mousePressEvent(event);
+ return;
+ }
+
+ // signal handlers may change the model
+ emit doubleClicked(persistent);
+
+ if (!persistent.isValid())
+ return;
+
+ if (edit(persistent, DoubleClicked, event) || state() != NoState)
+ return; // the double click triggered editing
+
+ if (!style()->styleHint(QStyle::SH_ItemView_ActivateItemOnSingleClick, 0, this))
+ emit activated(persistent);
+
+ d->executePostedLayout(); // we need to make sure viewItems is updated
+ if (d->itemsExpandable
+ && d->expandsOnDoubleClick
+ && d->hasVisibleChildren(persistent)) {
+ if (!((i < d->viewItems.count()) && (d->viewItems.at(i).index == persistent))) {
+ // find the new index of the item
+ for (i = 0; i < d->viewItems.count(); ++i) {
+ if (d->viewItems.at(i).index == persistent)
+ break;
+ }
+ if (i == d->viewItems.count())
+ return;
+ }
+ if (d->viewItems.at(i).expanded)
+ d->collapse(i, true);
+ else
+ d->expand(i, true);
+ updateGeometries();
+ viewport()->update();
+ }
+ }
+}
+
+/*!
+ \reimp
+*/
+void QTreeView::mouseMoveEvent(QMouseEvent *event)
+{
+ Q_D(QTreeView);
+ if (d->itemDecorationAt(event->pos()) == -1) // ### what about expanding/collapsing state ?
+ QAbstractItemView::mouseMoveEvent(event);
+}
+
+/*!
+ \reimp
+*/
+void QTreeView::keyPressEvent(QKeyEvent *event)
+{
+ Q_D(QTreeView);
+ QModelIndex current = currentIndex();
+ //this is the management of the expansion
+ if (d->isIndexValid(current) && d->model && d->itemsExpandable) {
+ switch (event->key()) {
+ case Qt::Key_Asterisk: {
+ QStack<QModelIndex> parents;
+ parents.push(current);
+ while (!parents.isEmpty()) {
+ QModelIndex parent = parents.pop();
+ for (int row = 0; row < d->model->rowCount(parent); ++row) {
+ QModelIndex child = d->model->index(row, 0, parent);
+ if (!d->isIndexValid(child))
+ break;
+ parents.push(child);
+ expand(child);
+ }
+ }
+ expand(current);
+ break; }
+ case Qt::Key_Plus:
+ expand(current);
+ break;
+ case Qt::Key_Minus:
+ collapse(current);
+ break;
+ }
+ }
+
+ QAbstractItemView::keyPressEvent(event);
+}
+
+/*!
+ \reimp
+*/
+QModelIndex QTreeView::indexAt(const QPoint &point) const
+{
+ Q_D(const QTreeView);
+ d->executePostedLayout();
+
+ int visualIndex = d->itemAtCoordinate(point.y());
+ QModelIndex idx = d->modelIndex(visualIndex);
+ if (!idx.isValid())
+ return QModelIndex();
+
+ if (d->viewItems.at(visualIndex).spanning)
+ return idx;
+
+ int column = d->columnAt(point.x());
+ if (column == idx.column())
+ return idx;
+ if (column < 0)
+ return QModelIndex();
+ return idx.sibling(idx.row(), column);
+}
+
+/*!
+ Returns the model index of the item above \a index.
+*/
+QModelIndex QTreeView::indexAbove(const QModelIndex &index) const
+{
+ Q_D(const QTreeView);
+ if (!d->isIndexValid(index))
+ return QModelIndex();
+ d->executePostedLayout();
+ int i = d->viewIndex(index);
+ if (--i < 0)
+ return QModelIndex();
+ return d->viewItems.at(i).index;
+}
+
+/*!
+ Returns the model index of the item below \a index.
+*/
+QModelIndex QTreeView::indexBelow(const QModelIndex &index) const
+{
+ Q_D(const QTreeView);
+ if (!d->isIndexValid(index))
+ return QModelIndex();
+ d->executePostedLayout();
+ int i = d->viewIndex(index);
+ if (++i >= d->viewItems.count())
+ return QModelIndex();
+ return d->viewItems.at(i).index;
+}
+
+/*!
+ \internal
+
+ Lays out the items in the tree view.
+*/
+void QTreeView::doItemsLayout()
+{
+ Q_D(QTreeView);
+ d->viewItems.clear(); // prepare for new layout
+ QModelIndex parent = d->root;
+ if (d->model->hasChildren(parent)) {
+ d->layout(-1);
+ }
+ QAbstractItemView::doItemsLayout();
+ d->header->doItemsLayout();
+}
+
+/*!
+ \reimp
+*/
+void QTreeView::reset()
+{
+ Q_D(QTreeView);
+ d->expandedIndexes.clear();
+ d->hiddenIndexes.clear();
+ d->spanningIndexes.clear();
+ d->viewItems.clear();
+ QAbstractItemView::reset();
+}
+
+/*!
+ Returns the horizontal offset of the items in the treeview.
+
+ Note that the tree view uses the horizontal header section
+ positions to determine the positions of columns in the view.
+
+ \sa verticalOffset()
+*/
+int QTreeView::horizontalOffset() const
+{
+ Q_D(const QTreeView);
+ return d->header->offset();
+}
+
+/*!
+ Returns the vertical offset of the items in the tree view.
+
+ \sa horizontalOffset()
+*/
+int QTreeView::verticalOffset() const
+{
+ Q_D(const QTreeView);
+ if (d->verticalScrollMode == QAbstractItemView::ScrollPerItem) {
+ if (d->uniformRowHeights)
+ return verticalScrollBar()->value() * d->defaultItemHeight;
+ // If we are scrolling per item and have non-uniform row heights,
+ // finding the vertical offset in pixels is going to be relatively slow.
+ // ### find a faster way to do this
+ int offset = 0;
+ for (int i = 0; i < d->viewItems.count(); ++i) {
+ if (i == verticalScrollBar()->value())
+ return offset;
+ offset += d->itemHeight(i);
+ }
+ return 0;
+ }
+ // scroll per pixel
+ return verticalScrollBar()->value();
+}
+
+/*!
+ Move the cursor in the way described by \a cursorAction, using the
+ information provided by the button \a modifiers.
+*/
+QModelIndex QTreeView::moveCursor(CursorAction cursorAction, Qt::KeyboardModifiers modifiers)
+{
+ Q_D(QTreeView);
+ Q_UNUSED(modifiers);
+
+ d->executePostedLayout();
+
+ QModelIndex current = currentIndex();
+ if (!current.isValid()) {
+ int i = d->below(-1);
+ int c = 0;
+ while (c < d->header->count() && d->header->isSectionHidden(c))
+ ++c;
+ if (i < d->viewItems.count() && c < d->header->count()) {
+ return d->modelIndex(i, c);
+ }
+ return QModelIndex();
+ }
+ int vi = -1;
+#if defined(Q_WS_MAC) && !defined(QT_NO_STYLE_MAC)
+ // Selection behavior is slightly different on the Mac.
+ if (d->selectionMode == QAbstractItemView::ExtendedSelection
+ && d->selectionModel
+ && d->selectionModel->hasSelection()) {
+
+ const bool moveUpDown = (cursorAction == MoveUp || cursorAction == MoveDown);
+ const bool moveNextPrev = (cursorAction == MoveNext || cursorAction == MovePrevious);
+ const bool contiguousSelection = moveUpDown && (modifiers & Qt::ShiftModifier);
+
+ // Use the outermost index in the selection as the current index
+ if (!contiguousSelection && (moveUpDown || moveNextPrev)) {
+
+ // Find outermost index.
+ const bool useTopIndex = (cursorAction == MoveUp || cursorAction == MovePrevious);
+ int index = useTopIndex ? INT_MAX : INT_MIN;
+ const QItemSelection selection = d->selectionModel->selection();
+ foreach (const QItemSelectionRange &range, selection) {
+ int candidate = d->viewIndex(useTopIndex ? range.topLeft() : range.bottomRight());
+ if (candidate >= 0)
+ index = useTopIndex ? qMin(index, candidate) : qMax(index, candidate);
+ }
+
+ if (index >= 0 && index < INT_MAX)
+ vi = index;
+ }
+ }
+#endif
+ if (vi < 0)
+ vi = qMax(0, d->viewIndex(current));
+
+ switch (cursorAction) {
+ case MoveNext:
+ case MoveDown:
+#ifdef QT_KEYPAD_NAVIGATION
+ if (vi == d->viewItems.count()-1 && QApplication::keypadNavigationEnabled())
+ return d->model->index(0, current.column(), d->root);
+#endif
+ return d->modelIndex(d->below(vi), current.column());
+ case MovePrevious:
+ case MoveUp:
+#ifdef QT_KEYPAD_NAVIGATION
+ if (vi == 0 && QApplication::keypadNavigationEnabled())
+ return d->modelIndex(d->viewItems.count() - 1, current.column());
+#endif
+ return d->modelIndex(d->above(vi), current.column());
+ case MoveLeft: {
+ QScrollBar *sb = horizontalScrollBar();
+ if (vi < d->viewItems.count() && d->viewItems.at(vi).expanded && d->itemsExpandable && sb->value() == sb->minimum())
+ d->collapse(vi, true);
+ else {
+ bool descend = style()->styleHint(QStyle::SH_ItemView_ArrowKeysNavigateIntoChildren, 0, this);
+ if (descend) {
+ QModelIndex par = current.parent();
+ if (par.isValid() && par != rootIndex())
+ return par;
+ else
+ descend = false;
+ }
+ if (!descend) {
+ if (d->selectionBehavior == SelectItems || d->selectionBehavior == SelectColumns) {
+ int visualColumn = d->header->visualIndex(current.column()) - 1;
+ while (visualColumn >= 0 && isColumnHidden(d->header->logicalIndex(visualColumn)))
+ visualColumn--;
+ int newColumn = d->header->logicalIndex(visualColumn);
+ QModelIndex next = current.sibling(current.row(), newColumn);
+ if (next.isValid())
+ return next;
+ }
+
+ sb->setValue(sb->value() - sb->singleStep());
+ }
+
+ }
+ updateGeometries();
+ viewport()->update();
+ break;
+ }
+ case MoveRight:
+ if (vi < d->viewItems.count() && !d->viewItems.at(vi).expanded && d->itemsExpandable
+ && d->hasVisibleChildren(d->viewItems.at(vi).index)) {
+ d->expand(vi, true);
+ } else {
+ bool descend = style()->styleHint(QStyle::SH_ItemView_ArrowKeysNavigateIntoChildren, 0, this);
+ if (descend) {
+ QModelIndex idx = d->modelIndex(d->below(vi));
+ if (idx.parent() == current)
+ return idx;
+ else
+ descend = false;
+ }
+ if (!descend) {
+ if (d->selectionBehavior == SelectItems || d->selectionBehavior == SelectColumns) {
+ int visualColumn = d->header->visualIndex(current.column()) + 1;
+ while (visualColumn < d->model->columnCount(current.parent()) && isColumnHidden(d->header->logicalIndex(visualColumn)))
+ visualColumn++;
+
+ QModelIndex next = current.sibling(current.row(), visualColumn);
+ if (next.isValid())
+ return next;
+ }
+
+ //last restort: we change the scrollbar value
+ QScrollBar *sb = horizontalScrollBar();
+ sb->setValue(sb->value() + sb->singleStep());
+ }
+ }
+ updateGeometries();
+ viewport()->update();
+ break;
+ case MovePageUp:
+ return d->modelIndex(d->pageUp(vi), current.column());
+ case MovePageDown:
+ return d->modelIndex(d->pageDown(vi), current.column());
+ case MoveHome:
+ return d->model->index(0, current.column(), d->root);
+ case MoveEnd:
+ return d->modelIndex(d->viewItems.count() - 1, current.column());
+ }
+ return current;
+}
+
+/*!
+ Applies the selection \a command to the items in or touched by the
+ rectangle, \a rect.
+
+ \sa selectionCommand()
+*/
+void QTreeView::setSelection(const QRect &rect, QItemSelectionModel::SelectionFlags command)
+{
+ Q_D(QTreeView);
+ if (!selectionModel() || rect.isNull())
+ return;
+
+ d->executePostedLayout();
+ QPoint tl(isRightToLeft() ? qMax(rect.left(), rect.right())
+ : qMin(rect.left(), rect.right()), qMin(rect.top(), rect.bottom()));
+ QPoint br(isRightToLeft() ? qMin(rect.left(), rect.right()) :
+ qMax(rect.left(), rect.right()), qMax(rect.top(), rect.bottom()));
+ QModelIndex topLeft = indexAt(tl);
+ QModelIndex bottomRight = indexAt(br);
+ if (!topLeft.isValid() && !bottomRight.isValid()) {
+ if (command & QItemSelectionModel::Clear)
+ selectionModel()->clear();
+ return;
+ }
+ if (!topLeft.isValid() && !d->viewItems.isEmpty())
+ topLeft = d->viewItems.first().index;
+ if (!bottomRight.isValid() && !d->viewItems.isEmpty()) {
+ const int column = d->header->logicalIndex(d->header->count() - 1);
+ const QModelIndex index = d->viewItems.last().index;
+ bottomRight = index.sibling(index.row(), column);
+ }
+
+ if (!d->isIndexEnabled(topLeft) || !d->isIndexEnabled(bottomRight))
+ return;
+
+ d->select(topLeft, bottomRight, command);
+}
+
+/*!
+ Returns the rectangle from the viewport of the items in the given
+ \a selection.
+*/
+QRegion QTreeView::visualRegionForSelection(const QItemSelection &selection) const
+{
+ Q_D(const QTreeView);
+ if (selection.isEmpty())
+ return QRegion();
+
+ QRegion selectionRegion;
+ for (int i = 0; i < selection.count(); ++i) {
+ QItemSelectionRange range = selection.at(i);
+ if (!range.isValid())
+ continue;
+ QModelIndex parent = range.parent();
+ QModelIndex leftIndex = range.topLeft();
+ int columnCount = d->model->columnCount(parent);
+ while (leftIndex.isValid() && isIndexHidden(leftIndex)) {
+ if (leftIndex.column() + 1 < columnCount)
+ leftIndex = d->model->index(leftIndex.row(), leftIndex.column() + 1, parent);
+ else
+ leftIndex = QModelIndex();
+ }
+ if (!leftIndex.isValid())
+ continue;
+ const QRect leftRect = visualRect(leftIndex);
+ int top = leftRect.top();
+ QModelIndex rightIndex = range.bottomRight();
+ while (rightIndex.isValid() && isIndexHidden(rightIndex)) {
+ if (rightIndex.column() - 1 >= 0)
+ rightIndex = d->model->index(rightIndex.row(), rightIndex.column() - 1, parent);
+ else
+ rightIndex = QModelIndex();
+ }
+ if (!rightIndex.isValid())
+ continue;
+ const QRect rightRect = visualRect(rightIndex);
+ int bottom = rightRect.bottom();
+ if (top > bottom)
+ qSwap<int>(top, bottom);
+ int height = bottom - top + 1;
+ if (d->header->sectionsMoved()) {
+ for (int c = range.left(); c <= range.right(); ++c)
+ selectionRegion += QRegion(QRect(columnViewportPosition(c), top,
+ columnWidth(c), height));
+ } else {
+ QRect combined = leftRect|rightRect;
+ combined.setX(columnViewportPosition(isRightToLeft() ? range.right() : range.left()));
+ selectionRegion += combined;
+ }
+ }
+ return selectionRegion;
+}
+
+/*!
+ \reimp
+*/
+QModelIndexList QTreeView::selectedIndexes() const
+{
+ QModelIndexList viewSelected;
+ QModelIndexList modelSelected;
+ if (selectionModel())
+ modelSelected = selectionModel()->selectedIndexes();
+ for (int i = 0; i < modelSelected.count(); ++i) {
+ // check that neither the parents nor the index is hidden before we add
+ QModelIndex index = modelSelected.at(i);
+ while (index.isValid() && !isIndexHidden(index))
+ index = index.parent();
+ if (index.isValid())
+ continue;
+ viewSelected.append(modelSelected.at(i));
+ }
+ return viewSelected;
+}
+
+/*!
+ Scrolls the contents of the tree view by (\a dx, \a dy).
+*/
+void QTreeView::scrollContentsBy(int dx, int dy)
+{
+ Q_D(QTreeView);
+
+ d->delayedAutoScroll.stop(); // auto scroll was canceled by the user scrolling
+
+ dx = isRightToLeft() ? -dx : dx;
+ if (dx) {
+ if (horizontalScrollMode() == QAbstractItemView::ScrollPerItem) {
+ int oldOffset = d->header->offset();
+ if (horizontalScrollBar()->value() == horizontalScrollBar()->maximum())
+ d->header->setOffsetToLastSection();
+ else
+ d->header->setOffsetToSectionPosition(horizontalScrollBar()->value());
+ int newOffset = d->header->offset();
+ dx = isRightToLeft() ? newOffset - oldOffset : oldOffset - newOffset;
+ } else {
+ d->header->setOffset(horizontalScrollBar()->value());
+ }
+ }
+
+ const int itemHeight = d->defaultItemHeight <= 0 ? sizeHintForRow(0) : d->defaultItemHeight;
+ if (d->viewItems.isEmpty() || itemHeight == 0)
+ return;
+
+ // guestimate the number of items in the viewport
+ int viewCount = d->viewport->height() / itemHeight;
+ int maxDeltaY = qMin(d->viewItems.count(), viewCount);
+ // no need to do a lot of work if we are going to redraw the whole thing anyway
+ if (qAbs(dy) > qAbs(maxDeltaY) && d->editors.isEmpty()) {
+ verticalScrollBar()->update();
+ d->viewport->update();
+ return;
+ }
+
+ if (dy && verticalScrollMode() == QAbstractItemView::ScrollPerItem) {
+ int currentScrollbarValue = verticalScrollBar()->value();
+ int previousScrollbarValue = currentScrollbarValue + dy; // -(-dy)
+ int currentViewIndex = currentScrollbarValue; // the first visible item
+ int previousViewIndex = previousScrollbarValue;
+ const QVector<QTreeViewItem> viewItems = d->viewItems;
+ dy = 0;
+ if (previousViewIndex < currentViewIndex) { // scrolling down
+ for (int i = previousViewIndex; i < currentViewIndex; ++i) {
+ if (i < d->viewItems.count())
+ dy -= d->itemHeight(i);
+ }
+ } else if (previousViewIndex > currentViewIndex) { // scrolling up
+ for (int i = previousViewIndex - 1; i >= currentViewIndex; --i) {
+ if (i < d->viewItems.count())
+ dy += d->itemHeight(i);
+ }
+ }
+ }
+
+ d->scrollContentsBy(dx, dy);
+}
+
+/*!
+ This slot is called whenever a column has been moved.
+*/
+void QTreeView::columnMoved()
+{
+ Q_D(QTreeView);
+ updateEditorGeometries();
+ d->viewport->update();
+}
+
+/*!
+ \internal
+*/
+void QTreeView::reexpand()
+{
+ // do nothing
+}
+
+/*!
+ \internal
+*/
+static bool treeViewItemLessThan(const QTreeViewItem &left,
+ const QTreeViewItem &right)
+{
+ if (left.level != right.level) {
+ Q_ASSERT(left.level > right.level);
+ QModelIndex leftParent = left.index.parent();
+ QModelIndex rightParent = right.index.parent();
+ // computer parent, don't get
+ while (leftParent.isValid() && leftParent.parent() != rightParent)
+ leftParent = leftParent.parent();
+ return (leftParent.row() < right.index.row());
+ }
+ return (left.index.row() < right.index.row());
+}
+
+/*!
+ Informs the view that the rows from the \a start row to the \a end row
+ inclusive have been inserted into the \a parent model item.
+*/
+void QTreeView::rowsInserted(const QModelIndex &parent, int start, int end)
+{
+ Q_D(QTreeView);
+ // if we are going to do a complete relayout anyway, there is no need to update
+ if (d->delayedPendingLayout) {
+ QAbstractItemView::rowsInserted(parent, start, end);
+ return;
+ }
+
+ //don't add a hierarchy on a column != 0
+ if (parent.column() != 0 && parent.isValid()) {
+ QAbstractItemView::rowsInserted(parent, start, end);
+ return;
+ }
+
+ if (parent != d->root && !d->isIndexExpanded(parent) && d->model->rowCount(parent) > (end - start) + 1) {
+ QAbstractItemView::rowsInserted(parent, start, end);
+ return;
+ }
+
+ const int parentItem = d->viewIndex(parent);
+ if (((parentItem != -1) && d->viewItems.at(parentItem).expanded && updatesEnabled())
+ || (parent == d->root)) {
+ const uint childLevel = (parentItem == -1)
+ ? uint(0) : d->viewItems.at(parentItem).level + 1;
+ const int firstChildItem = parentItem + 1;
+ const int lastChildItem = firstChildItem + ((parentItem == -1)
+ ? d->viewItems.count()
+ : d->viewItems.at(parentItem).total) - 1;
+
+ int firstColumn = 0;
+ while (isColumnHidden(firstColumn) && firstColumn < header()->count() - 1)
+ ++firstColumn;
+
+ const int delta = end - start + 1;
+ QVector<QTreeViewItem> insertedItems(delta);
+ for (int i = 0; i < delta; ++i) {
+ insertedItems[i].index = d->model->index(i + start, firstColumn, parent);
+ insertedItems[i].level = childLevel;
+ }
+ if (d->viewItems.isEmpty())
+ d->defaultItemHeight = indexRowSizeHint(insertedItems[0].index);
+
+ int insertPos;
+ if (lastChildItem < firstChildItem) { // no children
+ insertPos = firstChildItem;
+ } else {
+ // do a binary search to figure out where to insert
+ QVector<QTreeViewItem>::iterator it;
+ it = qLowerBound(d->viewItems.begin() + firstChildItem,
+ d->viewItems.begin() + lastChildItem + 1,
+ insertedItems.at(0), treeViewItemLessThan);
+ insertPos = it - d->viewItems.begin();
+
+ // update stale model indexes of siblings
+ for (int item = insertPos; item <= lastChildItem; ) {
+ Q_ASSERT(d->viewItems.at(item).level == childLevel);
+ const QModelIndex modelIndex = d->viewItems.at(item).index;
+ //Q_ASSERT(modelIndex.parent() == parent);
+ d->viewItems[item].index = d->model->index(
+ modelIndex.row() + delta, modelIndex.column(), parent);
+
+ if (!d->viewItems[item].index.isValid()) {
+ // Something really bad is happening, a bad model is
+ // often the cause. We can't optimize in this case :(
+ qWarning() << "QTreeView::rowsInserted internal representation of the model has been corrupted, resetting.";
+ doItemsLayout();
+ return;
+ }
+
+ item += d->viewItems.at(item).total + 1;
+ }
+ }
+
+ d->viewItems.insert(insertPos, delta, insertedItems.at(0));
+ if (delta > 1) {
+ qCopy(insertedItems.begin() + 1, insertedItems.end(),
+ d->viewItems.begin() + insertPos + 1);
+ }
+
+ d->updateChildCount(parentItem, delta);
+ updateGeometries();
+ viewport()->update();
+ } else if ((parentItem != -1) && d->viewItems.at(parentItem).expanded) {
+ d->doDelayedItemsLayout();
+ } else if (parentItem != -1 && (d->model->rowCount(parent) == end - start + 1)) {
+ // the parent just went from 0 children to having some update to re-paint the decoration
+ viewport()->update();
+ }
+ QAbstractItemView::rowsInserted(parent, start, end);
+}
+
+/*!
+ Informs the view that the rows from the \a start row to the \a end row
+ inclusive are about to removed from the given \a parent model item.
+*/
+void QTreeView::rowsAboutToBeRemoved(const QModelIndex &parent, int start, int end)
+{
+ Q_D(QTreeView);
+ d->rowsRemoved(parent, start, end, false);
+ QAbstractItemView::rowsAboutToBeRemoved(parent, start, end);
+}
+
+/*!
+ \since 4.1
+
+ Informs the view that the rows from the \a start row to the \a end row
+ inclusive have been removed from the given \a parent model item.
+*/
+void QTreeView::rowsRemoved(const QModelIndex &parent, int start, int end)
+{
+ Q_D(QTreeView);
+ d->rowsRemoved(parent, start, end, true);
+}
+
+/*!
+ Informs the tree view that the number of columns in the tree view has
+ changed from \a oldCount to \a newCount.
+*/
+void QTreeView::columnCountChanged(int oldCount, int newCount)
+{
+ Q_D(QTreeView);
+ if (oldCount == 0 && newCount > 0) {
+ //if the first column has just been added we need to relayout.
+ d->doDelayedItemsLayout();
+ }
+
+ if (isVisible())
+ updateGeometries();
+ viewport()->update();
+}
+
+/*!
+ Resizes the \a column given to the size of its contents.
+
+ \sa columnWidth(), setColumnWidth()
+*/
+void QTreeView::resizeColumnToContents(int column)
+{
+ Q_D(QTreeView);
+ d->executePostedLayout();
+ if (column < 0 || column >= d->header->count())
+ return;
+ int contents = sizeHintForColumn(column);
+ int header = d->header->isHidden() ? 0 : d->header->sectionSizeHint(column);
+ d->header->resizeSection(column, qMax(contents, header));
+}
+
+/*!
+ \obsolete
+ \overload
+
+ Sorts the model by the values in the given \a column.
+*/
+void QTreeView::sortByColumn(int column)
+{
+ Q_D(QTreeView);
+ sortByColumn(column, d->header->sortIndicatorOrder());
+}
+
+/*!
+ \since 4.2
+
+ Sets the model up for sorting by the values in the given \a column and \a order.
+
+ \a column may be -1, in which case no sort indicator will be shown
+ and the model will return to its natural, unsorted order. Note that not
+ all models support this and may even crash in this case.
+
+ \sa sortingEnabled
+*/
+void QTreeView::sortByColumn(int column, Qt::SortOrder order)
+{
+ Q_D(QTreeView);
+
+ //If sorting is enabled will emit a signal connected to _q_sortIndicatorChanged, which then actually sorts
+ d->header->setSortIndicator(column, order);
+ //If sorting is not enabled, force to sort now.
+ if (!d->sortingEnabled)
+ d->model->sort(column, order);
+}
+
+/*!
+ \reimp
+*/
+void QTreeView::selectAll()
+{
+ Q_D(QTreeView);
+ if (!selectionModel())
+ return;
+ SelectionMode mode = d->selectionMode;
+ d->executePostedLayout(); //make sure we lay out the items
+ if (mode != SingleSelection && !d->viewItems.isEmpty())
+ d->select(d->viewItems.first().index, d->viewItems.last().index,
+ QItemSelectionModel::ClearAndSelect
+ |QItemSelectionModel::Rows);
+}
+
+/*!
+ \since 4.2
+ Expands all expandable items.
+
+ Warning: if the model contains a large number of items,
+ this function will take some time to execute.
+
+ \sa collapseAll() expand() collapse() setExpanded()
+*/
+void QTreeView::expandAll()
+{
+ Q_D(QTreeView);
+ d->viewItems.clear();
+ d->expandedIndexes.clear();
+ d->interruptDelayedItemsLayout();
+ d->layout(-1);
+ for (int i = 0; i < d->viewItems.count(); ++i) {
+ if (d->viewItems[i].expanded)
+ continue;
+ d->viewItems[i].expanded = true;
+ d->layout(i);
+ QModelIndex idx = d->viewItems.at(i).index;
+ d->expandedIndexes.insert(idx.sibling(idx.row(), 0));
+ }
+ updateGeometries();
+ d->viewport->update();
+}
+
+/*!
+ \since 4.2
+
+ Collapses all expanded items.
+
+ \sa expandAll() expand() collapse() setExpanded()
+*/
+void QTreeView::collapseAll()
+{
+ Q_D(QTreeView);
+ d->expandedIndexes.clear();
+ doItemsLayout();
+}
+
+/*!
+ \since 4.3
+ Expands all expandable items to the given \a depth.
+
+ \sa expandAll() collapseAll() expand() collapse() setExpanded()
+*/
+void QTreeView::expandToDepth(int depth)
+{
+ Q_D(QTreeView);
+ d->viewItems.clear();
+ d->expandedIndexes.clear();
+ d->interruptDelayedItemsLayout();
+ d->layout(-1);
+ for (int i = 0; i < d->viewItems.count(); ++i) {
+ if (d->viewItems.at(i).level <= (uint)depth) {
+ d->viewItems[i].expanded = true;
+ d->layout(i);
+ d->storeExpanded(d->viewItems.at(i).index);
+ }
+ }
+ updateGeometries();
+ d->viewport->update();
+}
+
+/*!
+ This function is called whenever \a{column}'s size is changed in
+ the header. \a oldSize and \a newSize give the previous size and
+ the new size in pixels.
+
+ \sa setColumnWidth()
+*/
+void QTreeView::columnResized(int column, int /* oldSize */, int /* newSize */)
+{
+ Q_D(QTreeView);
+ d->columnsToUpdate.append(column);
+ if (d->columnResizeTimerID == 0)
+ d->columnResizeTimerID = startTimer(0);
+}
+
+/*!
+ \reimp
+*/
+void QTreeView::updateGeometries()
+{
+ Q_D(QTreeView);
+ if (d->header) {
+ if (d->geometryRecursionBlock)
+ return;
+ d->geometryRecursionBlock = true;
+ QSize hint = d->header->isHidden() ? QSize(0, 0) : d->header->sizeHint();
+ setViewportMargins(0, hint.height(), 0, 0);
+ QRect vg = d->viewport->geometry();
+ QRect geometryRect(vg.left(), vg.top() - hint.height(), vg.width(), hint.height());
+ d->header->setGeometry(geometryRect);
+ //d->header->setOffset(horizontalScrollBar()->value()); // ### bug ???
+ QMetaObject::invokeMethod(d->header, "updateGeometries");
+ d->updateScrollBars();
+ d->geometryRecursionBlock = false;
+ }
+ QAbstractItemView::updateGeometries();
+}
+
+/*!
+ Returns the size hint for the \a column's width or -1 if there is no
+ model.
+
+ If you need to set the width of a given column to a fixed value, call
+ QHeaderView::resizeSection() on the view's header.
+
+ If you reimplement this function in a subclass, note that the value you
+ return is only used when resizeColumnToContents() is called. In that case,
+ if a larger column width is required by either the view's header or
+ the item delegate, that width will be used instead.
+
+ \sa QWidget::sizeHint, header()
+*/
+int QTreeView::sizeHintForColumn(int column) const
+{
+ Q_D(const QTreeView);
+ d->executePostedLayout();
+ if (d->viewItems.isEmpty())
+ return -1;
+ int w = 0;
+ QStyleOptionViewItemV4 option = d->viewOptionsV4();
+ const QVector<QTreeViewItem> viewItems = d->viewItems;
+
+ int start = 0;
+ int end = viewItems.count();
+ if(end > 1000) { //if we have too many item this function would be too slow.
+ //we get a good approximation by only iterate over 1000 items.
+ start = qMax(0, d->firstVisibleItem() - 100);
+ end = qMin(end, start + 900);
+ }
+
+ for (int i = start; i < end; ++i) {
+ if (viewItems.at(i).spanning)
+ continue; // we have no good size hint
+ QModelIndex index = viewItems.at(i).index;
+ index = index.sibling(index.row(), column);
+ QWidget *editor = d->editorForIndex(index).editor;
+ if (editor && d->persistent.contains(editor)) {
+ w = qMax(w, editor->sizeHint().width());
+ int min = editor->minimumSize().width();
+ int max = editor->maximumSize().width();
+ w = qBound(min, w, max);
+ }
+ int hint = d->delegateForIndex(index)->sizeHint(option, index).width();
+ w = qMax(w, hint + (column == 0 ? d->indentationForItem(i) : 0));
+ }
+ return w;
+}
+
+/*!
+ Returns the size hint for the row indicated by \a index.
+
+ \sa sizeHintForColumn(), uniformRowHeights()
+*/
+int QTreeView::indexRowSizeHint(const QModelIndex &index) const
+{
+ Q_D(const QTreeView);
+ if (!d->isIndexValid(index) || !d->itemDelegate)
+ return 0;
+
+ int start = -1;
+ int end = -1;
+ int count = d->header->count();
+ bool emptyHeader = (count == 0);
+ QModelIndex parent = index.parent();
+
+ if (count && isVisible()) {
+ // If the sections have moved, we end up checking too many or too few
+ start = d->header->visualIndexAt(0);
+ } else {
+ // If the header has not been laid out yet, we use the model directly
+ count = d->model->columnCount(parent);
+ }
+
+ if (isRightToLeft()) {
+ start = (start == -1 ? count - 1 : start);
+ end = (end == -1 ? 0 : end);
+ } else {
+ start = (start == -1 ? 0 : start);
+ end = (end == -1 ? count - 1 : end);
+ }
+
+ int tmp = start;
+ start = qMin(start, end);
+ end = qMax(tmp, end);
+
+ int height = -1;
+ QStyleOptionViewItemV4 option = d->viewOptionsV4();
+ // ### If we want word wrapping in the items,
+ // ### we need to go through all the columns
+ // ### and set the width of the column
+
+ // Hack to speed up the function
+ option.rect.setWidth(-1);
+
+ for (int column = start; column <= end; ++column) {
+ int logicalColumn = emptyHeader ? column : d->header->logicalIndex(column);
+ if (d->header->isSectionHidden(logicalColumn))
+ continue;
+ QModelIndex idx = d->model->index(index.row(), logicalColumn, parent);
+ if (idx.isValid()) {
+ QWidget *editor = d->editorForIndex(idx).editor;
+ if (editor && d->persistent.contains(editor)) {
+ height = qMax(height, editor->sizeHint().height());
+ int min = editor->minimumSize().height();
+ int max = editor->maximumSize().height();
+ height = qBound(min, height, max);
+ }
+ int hint = d->delegateForIndex(idx)->sizeHint(option, idx).height();
+ height = qMax(height, hint);
+ }
+ }
+
+ return height;
+}
+
+/*!
+ \since 4.3
+ Returns the height of the row indicated by the given \a index.
+ \sa indexRowSizeHint()
+*/
+int QTreeView::rowHeight(const QModelIndex &index) const
+{
+ Q_D(const QTreeView);
+ d->executePostedLayout();
+ int i = d->viewIndex(index);
+ if (i == -1)
+ return 0;
+ return d->itemHeight(i);
+}
+
+/*!
+ \reimp
+*/
+void QTreeView::horizontalScrollbarAction(int action)
+{
+ QAbstractItemView::horizontalScrollbarAction(action);
+}
+
+/*!
+ \reimp
+*/
+bool QTreeView::isIndexHidden(const QModelIndex &index) const
+{
+ return (isColumnHidden(index.column()) || isRowHidden(index.row(), index.parent()));
+}
+
+/*
+ private implementation
+*/
+void QTreeViewPrivate::initialize()
+{
+ Q_Q(QTreeView);
+ updateStyledFrameWidths();
+ q->setSelectionBehavior(QAbstractItemView::SelectRows);
+ q->setSelectionMode(QAbstractItemView::SingleSelection);
+ q->setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel);
+ q->setAttribute(Qt::WA_MacShowFocusRect);
+
+ QHeaderView *header = new QHeaderView(Qt::Horizontal, q);
+ header->setMovable(true);
+ header->setStretchLastSection(true);
+ header->setDefaultAlignment(Qt::AlignLeft|Qt::AlignVCenter);
+ q->setHeader(header);
+
+ // animation
+ QObject::connect(&timeline, SIGNAL(frameChanged(int)), q, SLOT(_q_animate()));
+ QObject::connect(&timeline, SIGNAL(finished()), q, SLOT(_q_endAnimatedOperation()), Qt::QueuedConnection);
+}
+
+void QTreeViewPrivate::expand(int item, bool emitSignal)
+{
+ Q_Q(QTreeView);
+
+ if (item == -1 || viewItems.at(item).expanded)
+ return;
+
+ if (emitSignal && animationsEnabled)
+ prepareAnimatedOperation(item, AnimatedOperation::Expand);
+
+ QAbstractItemView::State oldState = q->state();
+ q->setState(QAbstractItemView::ExpandingState);
+ const QModelIndex index = viewItems.at(item).index;
+ storeExpanded(index);
+ viewItems[item].expanded = true;
+ layout(item);
+ q->setState(oldState);
+
+ if (emitSignal) {
+ emit q->expanded(index);
+ if (animationsEnabled)
+ beginAnimatedOperation();
+ }
+ if (model->canFetchMore(index))
+ model->fetchMore(index);
+}
+
+void QTreeViewPrivate::collapse(int item, bool emitSignal)
+{
+ Q_Q(QTreeView);
+
+ if (item == -1 || expandedIndexes.isEmpty())
+ return;
+
+ //if the current item is now invisible, the autoscroll will expand the tree to see it, so disable the autoscroll
+ delayedAutoScroll.stop();
+
+ int total = viewItems.at(item).total;
+ const QModelIndex &modelIndex = viewItems.at(item).index;
+ if (!isPersistent(modelIndex))
+ return; // if the index is not persistent, no chances it is expanded
+ QSet<QPersistentModelIndex>::iterator it = expandedIndexes.find(modelIndex);
+ if (it == expandedIndexes.end() || viewItems.at(item).expanded == false)
+ return; // nothing to do
+
+ if (emitSignal && animationsEnabled)
+ prepareAnimatedOperation(item, AnimatedOperation::Collapse);
+
+ QAbstractItemView::State oldState = q->state();
+ q->setState(QAbstractItemView::CollapsingState);
+ expandedIndexes.erase(it);
+ viewItems[item].expanded = false;
+ int index = item;
+ QModelIndex parent = modelIndex;
+ while (parent.isValid() && parent != root) {
+ Q_ASSERT(index > -1);
+ viewItems[index].total -= total;
+ parent = parent.parent();
+ index = viewIndex(parent);
+ }
+ viewItems.remove(item + 1, total); // collapse
+ q->setState(oldState);
+
+ if (emitSignal) {
+ emit q->collapsed(modelIndex);
+ if (animationsEnabled)
+ beginAnimatedOperation();
+ }
+}
+
+void QTreeViewPrivate::prepareAnimatedOperation(int item, AnimatedOperation::Type type)
+{
+ animatedOperation.item = item;
+ animatedOperation.type = type;
+
+ int top = coordinateForItem(item) + itemHeight(item);
+ QRect rect = viewport->rect();
+ rect.setTop(top);
+ if (type == AnimatedOperation::Collapse) {
+ const int limit = rect.height() * 2;
+ int h = 0;
+ int c = item + viewItems.at(item).total + 1;
+ for (int i = item + 1; i < c && h < limit; ++i)
+ h += itemHeight(i);
+ rect.setHeight(h);
+ animatedOperation.duration = h;
+ }
+ animatedOperation.top = top;
+ animatedOperation.before = renderTreeToPixmapForAnimation(rect);
+}
+
+void QTreeViewPrivate::beginAnimatedOperation()
+{
+ Q_Q(QTreeView);
+
+ QRect rect = viewport->rect();
+ rect.setTop(animatedOperation.top);
+ if (animatedOperation.type == AnimatedOperation::Expand) {
+ const int limit = rect.height() * 2;
+ int h = 0;
+ int c = animatedOperation.item + viewItems.at(animatedOperation.item).total + 1;
+ for (int i = animatedOperation.item + 1; i < c && h < limit; ++i)
+ h += itemHeight(i);
+ rect.setHeight(h);
+ animatedOperation.duration = h;
+ }
+
+ animatedOperation.after = renderTreeToPixmapForAnimation(rect);
+
+ q->setState(QAbstractItemView::AnimatingState);
+
+ timeline.stop();
+ timeline.setDuration(250);
+ timeline.setFrameRange(animatedOperation.top, animatedOperation.top + animatedOperation.duration);
+ timeline.start();
+}
+
+void QTreeViewPrivate::_q_endAnimatedOperation()
+{
+ Q_Q(QTreeView);
+ animatedOperation.before = QPixmap();
+ animatedOperation.after = QPixmap();
+ q->setState(QAbstractItemView::NoState);
+ q->updateGeometries();
+ viewport->update();
+}
+
+void QTreeViewPrivate::_q_animate()
+{
+ QRect rect = viewport->rect();
+ rect.moveTop(animatedOperation.top);
+ viewport->repaint(rect);
+}
+
+void QTreeViewPrivate::drawAnimatedOperation(QPainter *painter) const
+{
+ int start = timeline.startFrame();
+ int end = timeline.endFrame();
+ bool collapsing = animatedOperation.type == AnimatedOperation::Collapse;
+ int current = collapsing ? end - timeline.currentFrame() + start : timeline.currentFrame();
+ const QPixmap top = collapsing ? animatedOperation.before : animatedOperation.after;
+ painter->drawPixmap(0, start, top, 0, end - current - 1, top.width(), top.height());
+ const QPixmap bottom = collapsing ? animatedOperation.after : animatedOperation.before;
+ painter->drawPixmap(0, current, bottom);
+}
+
+QPixmap QTreeViewPrivate::renderTreeToPixmapForAnimation(const QRect &rect) const
+{
+ Q_Q(const QTreeView);
+ QPixmap pixmap(rect.size());
+ pixmap.fill(Qt::transparent); //the base might not be opaque, and we don't want uninitialized pixels.
+ QPainter painter(&pixmap);
+ painter.fillRect(QRect(QPoint(0,0), rect.size()), q->palette().base());
+ painter.translate(0, -rect.top());
+ q->drawTree(&painter, QRegion(rect));
+ painter.end();
+
+ //and now let's render the editors the editors
+ QStyleOptionViewItemV4 option = viewOptionsV4();
+ for (QList<QEditorInfo>::const_iterator it = editors.constBegin(); it != editors.constEnd(); ++it) {
+ QWidget *editor = it->editor;
+ QModelIndex index = it->index;
+ option.rect = q->visualRect(index);
+ if (option.rect.isValid()) {
+
+ if (QAbstractItemDelegate *delegate = delegateForIndex(index))
+ delegate->updateEditorGeometry(editor, option, index);
+
+ const QPoint pos = editor->pos();
+ if (rect.contains(pos)) {
+ editor->render(&pixmap, pos - rect.topLeft());
+ //the animation uses pixmap to display the treeview's content
+ //the editor is rendered on this pixmap and thus can (should) be hidden
+ editor->hide();
+ }
+ }
+ }
+
+
+ return pixmap;
+}
+
+void QTreeViewPrivate::_q_currentChanged(const QModelIndex &current, const QModelIndex &previous)
+{
+ Q_Q(QTreeView);
+ if (previous.isValid()) {
+ QRect previousRect = q->visualRect(previous);
+ if (allColumnsShowFocus) {
+ previousRect.setX(0);
+ previousRect.setWidth(viewport->width());
+ }
+ viewport->update(previousRect);
+ }
+ if (current.isValid()) {
+ QRect currentRect = q->visualRect(current);
+ if (allColumnsShowFocus) {
+ currentRect.setX(0);
+ currentRect.setWidth(viewport->width());
+ }
+ viewport->update(currentRect);
+ }
+}
+
+void QTreeViewPrivate::_q_modelAboutToBeReset()
+{
+ viewItems.clear();
+}
+
+void QTreeViewPrivate::_q_columnsAboutToBeRemoved(const QModelIndex &parent, int start, int end)
+{
+ Q_UNUSED(parent);
+ if (start <= 0 && 0 <= end)
+ viewItems.clear();
+}
+
+void QTreeViewPrivate::_q_columnsRemoved(const QModelIndex &parent, int start, int end)
+{
+ Q_UNUSED(parent);
+ if (start <= 0 && 0 <= end)
+ doDelayedItemsLayout();
+}
+
+void QTreeViewPrivate::layout(int i)
+{
+ Q_Q(QTreeView);
+ QModelIndex current;
+ QModelIndex parent = (i < 0) ? (QModelIndex)root : modelIndex(i);
+ // modelIndex() will return an index that don't have a parent if column 0 is hidden,
+ // so we must make sure that parent points to the actual parent that has children.
+ if (parent != root)
+ parent = model->index(parent.row(), 0, parent.parent());
+
+ if (i>=0 && !parent.isValid()) {
+ //modelIndex() should never return something invalid for the real items.
+ //This can happen if columncount has been set to 0.
+ //To avoid infinite loop we stop here.
+ return;
+ }
+
+ int count = 0;
+ if (model->hasChildren(parent)) {
+ if (model->canFetchMore(parent))
+ model->fetchMore(parent);
+ count = model->rowCount(parent);
+ }
+
+ bool expanding = true;
+ if (i == -1) {
+ if (uniformRowHeights) {
+ QModelIndex index = model->index(0, 0, parent);
+ defaultItemHeight = q->indexRowSizeHint(index);
+ }
+ viewItems.resize(count);
+ } else if (viewItems[i].total != (uint)count) {
+ viewItems.insert(i + 1, count, QTreeViewItem()); // expand
+ } else {
+ expanding = false;
+ }
+
+ int first = i + 1;
+ int level = (i >= 0 ? viewItems.at(i).level + 1 : 0);
+ int hidden = 0;
+ int last = 0;
+ int children = 0;
+
+ int firstColumn = 0;
+ while (header->isSectionHidden(firstColumn) && firstColumn < header->count())
+ ++firstColumn;
+
+ for (int j = first; j < first + count; ++j) {
+ current = model->index(j - first, firstColumn, parent);
+ if (isRowHidden(current.sibling(current.row(), 0))) {
+ ++hidden;
+ last = j - hidden + children;
+ } else {
+ last = j - hidden + children;
+ viewItems[last].index = current;
+ viewItems[last].level = level;
+ viewItems[last].height = 0;
+ viewItems[last].spanning = q->isFirstColumnSpanned(current.row(), parent);
+ viewItems[last].expanded = false;
+ viewItems[last].total = 0;
+ if (isIndexExpanded(current)) {
+ viewItems[last].expanded = true;
+ layout(last);
+ children += viewItems[last].total;
+ last = j - hidden + children;
+ }
+ }
+ }
+
+ // remove hidden items
+ if (hidden > 0)
+ viewItems.remove(last + 1, hidden); // collapse
+
+ if (!expanding)
+ return; // nothing changed
+
+ while (parent != root) {
+ Q_ASSERT(i > -1);
+ viewItems[i].total += count - hidden;
+ parent = parent.parent();
+ i = viewIndex(parent);
+ }
+}
+
+int QTreeViewPrivate::pageUp(int i) const
+{
+ int index = itemAtCoordinate(coordinateForItem(i) - viewport->height());
+ return index == -1 ? 0 : index;
+}
+
+int QTreeViewPrivate::pageDown(int i) const
+{
+ int index = itemAtCoordinate(coordinateForItem(i) + viewport->height());
+ return index == -1 ? viewItems.count() - 1 : index;
+}
+
+int QTreeViewPrivate::indentationForItem(int item) const
+{
+ if (item < 0 || item >= viewItems.count())
+ return 0;
+ int level = viewItems.at(item).level;
+ if (rootDecoration)
+ ++level;
+ return level * indent;
+}
+
+int QTreeViewPrivate::itemHeight(int item) const
+{
+ if (uniformRowHeights)
+ return defaultItemHeight;
+ if (viewItems.isEmpty())
+ return 0;
+ const QModelIndex &index = viewItems.at(item).index;
+ int height = viewItems.at(item).height;
+ if (height <= 0 && index.isValid()) {
+ height = q_func()->indexRowSizeHint(index);
+ viewItems[item].height = height;
+ }
+ if (!index.isValid() || height < 0)
+ return 0;
+ return height;
+}
+
+
+/*!
+ \internal
+ Returns the viewport y coordinate for \a item.
+*/
+int QTreeViewPrivate::coordinateForItem(int item) const
+{
+ Q_Q(const QTreeView);
+ if (verticalScrollMode == QAbstractItemView::ScrollPerPixel) {
+ if (uniformRowHeights)
+ return (item * defaultItemHeight) - q->verticalScrollBar()->value();
+ // ### optimize (spans or caching)
+ int y = 0;
+ for (int i = 0; i < viewItems.count(); ++i) {
+ if (i == item)
+ return y - q->verticalScrollBar()->value();
+ y += itemHeight(i);
+ }
+ } else { // ScrollPerItem
+ int topViewItemIndex = q->verticalScrollBar()->value();
+ if (uniformRowHeights)
+ return defaultItemHeight * (item - topViewItemIndex);
+ if (item >= topViewItemIndex) {
+ // search in the visible area first and continue down
+ // ### slow if the item is not visible
+ int viewItemCoordinate = 0;
+ int viewItemIndex = topViewItemIndex;
+ while (viewItemIndex < viewItems.count()) {
+ if (viewItemIndex == item)
+ return viewItemCoordinate;
+ viewItemCoordinate += itemHeight(viewItemIndex);
+ ++viewItemIndex;
+ }
+ // below the last item in the view
+ Q_ASSERT(false);
+ return viewItemCoordinate;
+ } else {
+ // search the area above the viewport (used for editor widgets)
+ int viewItemCoordinate = 0;
+ for (int viewItemIndex = topViewItemIndex; viewItemIndex > 0; --viewItemIndex) {
+ if (viewItemIndex == item)
+ return viewItemCoordinate;
+ viewItemCoordinate -= itemHeight(viewItemIndex - 1);
+ }
+ return viewItemCoordinate;
+ }
+ }
+ return 0;
+}
+
+/*!
+ \internal
+ Returns the index of the view item at the
+ given viewport \a coordinate.
+
+ \sa modelIndex()
+*/
+int QTreeViewPrivate::itemAtCoordinate(int coordinate) const
+{
+ Q_Q(const QTreeView);
+ const int itemCount = viewItems.count();
+ if (itemCount == 0)
+ return -1;
+ if (uniformRowHeights && defaultItemHeight <= 0)
+ return -1;
+ if (verticalScrollMode == QAbstractItemView::ScrollPerPixel) {
+ if (uniformRowHeights) {
+ const int viewItemIndex = (coordinate + q->verticalScrollBar()->value()) / defaultItemHeight;
+ return ((viewItemIndex >= itemCount || viewItemIndex < 0) ? -1 : viewItemIndex);
+ }
+ // ### optimize
+ int viewItemCoordinate = 0;
+ const int contentsCoordinate = coordinate + q->verticalScrollBar()->value();
+ for (int viewItemIndex = 0; viewItemIndex < viewItems.count(); ++viewItemIndex) {
+ viewItemCoordinate += itemHeight(viewItemIndex);
+ if (viewItemCoordinate >= contentsCoordinate)
+ return (viewItemIndex >= itemCount ? -1 : viewItemIndex);
+ }
+ } else { // ScrollPerItem
+ int topViewItemIndex = q->verticalScrollBar()->value();
+ if (uniformRowHeights) {
+ if (coordinate < 0)
+ coordinate -= defaultItemHeight - 1;
+ const int viewItemIndex = topViewItemIndex + (coordinate / defaultItemHeight);
+ return ((viewItemIndex >= itemCount || viewItemIndex < 0) ? -1 : viewItemIndex);
+ }
+ if (coordinate >= 0) {
+ // the coordinate is in or below the viewport
+ int viewItemCoordinate = 0;
+ for (int viewItemIndex = topViewItemIndex; viewItemIndex < viewItems.count(); ++viewItemIndex) {
+ viewItemCoordinate += itemHeight(viewItemIndex);
+ if (viewItemCoordinate > coordinate)
+ return (viewItemIndex >= itemCount ? -1 : viewItemIndex);
+ }
+ } else {
+ // the coordinate is above the viewport
+ int viewItemCoordinate = 0;
+ for (int viewItemIndex = topViewItemIndex; viewItemIndex >= 0; --viewItemIndex) {
+ if (viewItemCoordinate <= coordinate)
+ return (viewItemIndex >= itemCount ? -1 : viewItemIndex);
+ viewItemCoordinate -= itemHeight(viewItemIndex);
+ }
+ }
+ }
+ return -1;
+}
+
+int QTreeViewPrivate::viewIndex(const QModelIndex &_index) const
+{
+ Q_Q(const QTreeView);
+ if (!_index.isValid() || viewItems.isEmpty())
+ return -1;
+
+ const int totalCount = viewItems.count();
+ int firstColumn = 0;
+ while (q->isColumnHidden(firstColumn) && firstColumn < header->count())
+ ++firstColumn;
+ const QModelIndex index = _index.sibling(_index.row(), firstColumn);
+
+
+ // A quick check near the last item to see if we are just incrementing
+ const int start = lastViewedItem > 2 ? lastViewedItem - 2 : 0;
+ const int end = lastViewedItem < totalCount - 2 ? lastViewedItem + 2 : totalCount;
+ int row = index.row();
+ for (int i = start; i < end; ++i) {
+ const QModelIndex &idx = viewItems.at(i).index;
+ if (idx.row() == row) {
+ if (idx.internalId() == index.internalId()) {
+ lastViewedItem = i;
+ return i;
+ }
+ }
+ }
+
+ // NOTE: this function is slow if the item is outside the visible area
+ // search in visible items first and below
+ int t = firstVisibleItem();
+ t = t > 100 ? t - 100 : 0; // start 100 items above the visible area
+
+ for (int i = t; i < totalCount; ++i) {
+ const QModelIndex &idx = viewItems.at(i).index;
+ if (idx.row() == row) {
+ if (idx.internalId() == index.internalId()) {
+ lastViewedItem = i;
+ return i;
+ }
+ }
+ }
+ // search from top to first visible
+ for (int j = 0; j < t; ++j) {
+ const QModelIndex &idx = viewItems.at(j).index;
+ if (idx.row() == row) {
+ if (idx.internalId() == index.internalId()) {
+ lastViewedItem = j;
+ return j;
+ }
+ }
+ }
+ // nothing found
+ return -1;
+}
+
+QModelIndex QTreeViewPrivate::modelIndex(int i, int column) const
+{
+ if (i < 0 || i >= viewItems.count())
+ return QModelIndex();
+
+ QModelIndex ret = viewItems.at(i).index;
+ if (column)
+ ret = ret.sibling(ret.row(), column);
+ return ret;
+}
+
+int QTreeViewPrivate::firstVisibleItem(int *offset) const
+{
+ Q_Q(const QTreeView);
+ const int value = q->verticalScrollBar()->value();
+ if (verticalScrollMode == QAbstractItemView::ScrollPerItem) {
+ if (offset)
+ *offset = 0;
+ return (value < 0 || value >= viewItems.count()) ? -1 : value;
+ }
+ // ScrollMode == ScrollPerPixel
+ if (uniformRowHeights) {
+ if (!defaultItemHeight)
+ return -1;
+
+ if (offset)
+ *offset = -(value % defaultItemHeight);
+ return value / defaultItemHeight;
+ }
+ int y = 0; // ### optimize (use spans ?)
+ for (int i = 0; i < viewItems.count(); ++i) {
+ y += itemHeight(i); // the height value is cached
+ if (y > value) {
+ if (offset)
+ *offset = y - value - itemHeight(i);
+ return i;
+ }
+ }
+ return -1;
+}
+
+int QTreeViewPrivate::columnAt(int x) const
+{
+ return header->logicalIndexAt(x);
+}
+
+void QTreeViewPrivate::relayout(const QModelIndex &parent)
+{
+ Q_Q(QTreeView);
+ // do a local relayout of the items
+ if (parent.isValid()) {
+ int parentViewIndex = viewIndex(parent);
+ if (parentViewIndex > -1 && viewItems.at(parentViewIndex).expanded) {
+ collapse(parentViewIndex, false); // remove the current layout
+ expand(parentViewIndex, false); // do the relayout
+ q->updateGeometries();
+ viewport->update();
+ }
+ } else {
+ viewItems.clear();
+ q->doItemsLayout();
+ }
+}
+
+
+void QTreeViewPrivate::updateScrollBars()
+{
+ Q_Q(QTreeView);
+ QSize viewportSize = viewport->size();
+ if (!viewportSize.isValid())
+ viewportSize = QSize(0, 0);
+
+ int itemsInViewport = 0;
+ if (uniformRowHeights) {
+ if (defaultItemHeight <= 0)
+ itemsInViewport = viewItems.count();
+ else
+ itemsInViewport = viewportSize.height() / defaultItemHeight;
+ } else {
+ const int itemsCount = viewItems.count();
+ const int viewportHeight = viewportSize.height();
+ for (int height = 0, item = itemsCount - 1; item >= 0; --item) {
+ height += itemHeight(item);
+ if (height > viewportHeight)
+ break;
+ ++itemsInViewport;
+ }
+ }
+ if (verticalScrollMode == QAbstractItemView::ScrollPerItem) {
+ if (!viewItems.isEmpty())
+ itemsInViewport = qMax(1, itemsInViewport);
+ q->verticalScrollBar()->setRange(0, viewItems.count() - itemsInViewport);
+ q->verticalScrollBar()->setPageStep(itemsInViewport);
+ q->verticalScrollBar()->setSingleStep(1);
+ } else { // scroll per pixel
+ int contentsHeight = 0;
+ if (uniformRowHeights) {
+ contentsHeight = defaultItemHeight * viewItems.count();
+ } else { // ### optimize (spans or caching)
+ for (int i = 0; i < viewItems.count(); ++i)
+ contentsHeight += itemHeight(i);
+ }
+ q->verticalScrollBar()->setRange(0, contentsHeight - viewportSize.height());
+ q->verticalScrollBar()->setPageStep(viewportSize.height());
+ q->verticalScrollBar()->setSingleStep(qMax(viewportSize.height() / (itemsInViewport + 1), 2));
+ }
+
+ const int columnCount = header->count();
+ const int viewportWidth = viewportSize.width();
+ int columnsInViewport = 0;
+ for (int width = 0, column = columnCount - 1; column >= 0; --column) {
+ int logical = header->logicalIndex(column);
+ width += header->sectionSize(logical);
+ if (width > viewportWidth)
+ break;
+ ++columnsInViewport;
+ }
+ if (columnCount > 0)
+ columnsInViewport = qMax(1, columnsInViewport);
+ if (horizontalScrollMode == QAbstractItemView::ScrollPerItem) {
+ q->horizontalScrollBar()->setRange(0, columnCount - columnsInViewport);
+ q->horizontalScrollBar()->setPageStep(columnsInViewport);
+ q->horizontalScrollBar()->setSingleStep(1);
+ } else { // scroll per pixel
+ const int horizontalLength = header->length();
+ const QSize maxSize = q->maximumViewportSize();
+ if (maxSize.width() >= horizontalLength && q->verticalScrollBar()->maximum() <= 0)
+ viewportSize = maxSize;
+ q->horizontalScrollBar()->setPageStep(viewportSize.width());
+ q->horizontalScrollBar()->setRange(0, qMax(horizontalLength - viewportSize.width(), 0));
+ q->horizontalScrollBar()->setSingleStep(qMax(viewportSize.width() / (columnsInViewport + 1), 2));
+ }
+}
+
+int QTreeViewPrivate::itemDecorationAt(const QPoint &pos) const
+{
+ int x = pos.x();
+ int column = header->logicalIndexAt(x);
+ if (column != 0)
+ return -1; // no logical index at x
+
+ int viewItemIndex = itemAtCoordinate(pos.y());
+ QRect returning = itemDecorationRect(modelIndex(viewItemIndex));
+ if (!returning.contains(pos))
+ return -1;
+
+ return viewItemIndex;
+}
+
+QRect QTreeViewPrivate::itemDecorationRect(const QModelIndex &index) const
+{
+ Q_Q(const QTreeView);
+ if (!rootDecoration && index.parent() == root)
+ return QRect(); // no decoration at root
+
+ int viewItemIndex = viewIndex(index);
+ if (viewItemIndex < 0 || !hasVisibleChildren(viewItems.at(viewItemIndex).index))
+ return QRect();
+
+ int itemIndentation = indentationForItem(viewItemIndex);
+ int position = header->sectionViewportPosition(0);
+ int size = header->sectionSize(0);
+
+ QRect rect;
+ if (q->isRightToLeft())
+ rect = QRect(position + size - itemIndentation, coordinateForItem(viewItemIndex),
+ indent, itemHeight(viewItemIndex));
+ else
+ rect = QRect(position + itemIndentation - indent, coordinateForItem(viewItemIndex),
+ indent, itemHeight(viewItemIndex));
+ QStyleOption opt;
+ opt.initFrom(q);
+ opt.rect = rect;
+ return q->style()->subElementRect(QStyle::SE_TreeViewDisclosureItem, &opt, q);
+}
+
+QList<QPair<int, int> > QTreeViewPrivate::columnRanges(const QModelIndex &topIndex,
+ const QModelIndex &bottomIndex) const
+{
+ const int topVisual = header->visualIndex(topIndex.column()),
+ bottomVisual = header->visualIndex(bottomIndex.column());
+
+ const int start = qMin(topVisual, bottomVisual);
+ const int end = qMax(topVisual, bottomVisual);
+
+ QList<int> logicalIndexes;
+
+ //we iterate over the visual indexes to get the logical indexes
+ for (int c = start; c <= end; c++) {
+ const int logical = header->logicalIndex(c);
+ if (!header->isSectionHidden(logical)) {
+ logicalIndexes << logical;
+ }
+ }
+ //let's sort the list
+ qSort(logicalIndexes.begin(), logicalIndexes.end());
+
+ QList<QPair<int, int> > ret;
+ QPair<int, int> current;
+ current.first = -2; // -1 is not enough because -1+1 = 0
+ current.second = -2;
+ foreach (int logicalColumn, logicalIndexes) {
+ if (current.second + 1 != logicalColumn) {
+ if (current.first != -2) {
+ //let's save the current one
+ ret += current;
+ }
+ //let's start a new one
+ current.first = current.second = logicalColumn;
+ } else {
+ current.second++;
+ }
+ }
+
+ //let's get the last range
+ if (current.first != -2) {
+ ret += current;
+ }
+
+ return ret;
+}
+
+void QTreeViewPrivate::select(const QModelIndex &topIndex, const QModelIndex &bottomIndex,
+ QItemSelectionModel::SelectionFlags command)
+{
+ Q_Q(QTreeView);
+ QItemSelection selection;
+ const int top = viewIndex(topIndex),
+ bottom = viewIndex(bottomIndex);
+
+ const QList< QPair<int, int> > colRanges = columnRanges(topIndex, bottomIndex);
+ QList< QPair<int, int> >::const_iterator it;
+ for (it = colRanges.begin(); it != colRanges.end(); ++it) {
+ const int left = (*it).first,
+ right = (*it).second;
+
+ QModelIndex previous;
+ QItemSelectionRange currentRange;
+ QStack<QItemSelectionRange> rangeStack;
+ for (int i = top; i <= bottom; ++i) {
+ QModelIndex index = modelIndex(i);
+ QModelIndex parent = index.parent();
+ QModelIndex previousParent = previous.parent();
+ if (previous.isValid() && parent == previousParent) {
+ // same parent
+ if (qAbs(previous.row() - index.row()) > 1) {
+ //a hole (hidden index inside a range) has been detected
+ if (currentRange.isValid()) {
+ selection.append(currentRange);
+ }
+ //let's start a new range
+ currentRange = QItemSelectionRange(index.sibling(index.row(), left), index.sibling(index.row(), right));
+ } else {
+ QModelIndex tl = model->index(currentRange.top(), currentRange.left(),
+ currentRange.parent());
+ currentRange = QItemSelectionRange(tl, index.sibling(index.row(), right));
+ }
+ } else if (previous.isValid() && parent == model->index(previous.row(), 0, previousParent)) {
+ // item is child of previous
+ rangeStack.push(currentRange);
+ currentRange = QItemSelectionRange(index.sibling(index.row(), left), index.sibling(index.row(), right));
+ } else {
+ if (currentRange.isValid())
+ selection.append(currentRange);
+ if (rangeStack.isEmpty()) {
+ currentRange = QItemSelectionRange(index.sibling(index.row(), left), index.sibling(index.row(), right));
+ } else {
+ currentRange = rangeStack.pop();
+ index = currentRange.bottomRight(); //let's resume the range
+ --i; //we process again the current item
+ }
+ }
+ previous = index;
+ }
+ if (currentRange.isValid())
+ selection.append(currentRange);
+ for (int i = 0; i < rangeStack.count(); ++i)
+ selection.append(rangeStack.at(i));
+ }
+ q->selectionModel()->select(selection, command);
+}
+
+QPair<int,int> QTreeViewPrivate::startAndEndColumns(const QRect &rect) const
+{
+ Q_Q(const QTreeView);
+ int start = header->visualIndexAt(rect.left());
+ int end = header->visualIndexAt(rect.right());
+ if (q->isRightToLeft()) {
+ start = (start == -1 ? header->count() - 1 : start);
+ end = (end == -1 ? 0 : end);
+ } else {
+ start = (start == -1 ? 0 : start);
+ end = (end == -1 ? header->count() - 1 : end);
+ }
+ return qMakePair<int,int>(qMin(start, end), qMax(start, end));
+}
+
+bool QTreeViewPrivate::hasVisibleChildren(const QModelIndex& parent) const
+{
+ Q_Q(const QTreeView);
+ if (model->hasChildren(parent)) {
+ if (hiddenIndexes.isEmpty())
+ return true;
+ if (q->isIndexHidden(parent))
+ return false;
+ int rowCount = model->rowCount(parent);
+ for (int i = 0; i < rowCount; ++i) {
+ if (!q->isRowHidden(i, parent))
+ return true;
+ }
+ if (rowCount == 0)
+ return true;
+ }
+ return false;
+}
+
+void QTreeViewPrivate::rowsRemoved(const QModelIndex &parent,
+ int start, int end, bool after)
+{
+ Q_Q(QTreeView);
+ // if we are going to do a complete relayout anyway, there is no need to update
+ if (delayedPendingLayout) {
+ _q_rowsRemoved(parent, start, end);
+ return;
+ }
+
+ const int parentItem = viewIndex(parent);
+ if ((parentItem != -1) || (parent == root)) {
+
+ const uint childLevel = (parentItem == -1)
+ ? uint(0) : viewItems.at(parentItem).level + 1;
+ Q_UNUSED(childLevel); // unused in release mode, used in assert below
+
+ const int firstChildItem = parentItem + 1;
+ int lastChildItem = firstChildItem + ((parentItem == -1)
+ ? viewItems.count()
+ : viewItems.at(parentItem).total) - 1;
+
+ const int delta = end - start + 1;
+
+ int removedCount = 0;
+ for (int item = firstChildItem; item <= lastChildItem; ) {
+ Q_ASSERT(viewItems.at(item).level == childLevel);
+ const QModelIndex modelIndex = viewItems.at(item).index;
+ //Q_ASSERT(modelIndex.parent() == parent);
+ const int count = viewItems.at(item).total + 1;
+ if (modelIndex.row() < start) {
+ // not affected by the removal
+ item += count;
+ } else if (modelIndex.row() <= end) {
+ // removed
+ viewItems.remove(item, count);
+ removedCount += count;
+ lastChildItem -= count;
+ } else {
+ if (after) {
+ // moved; update the model index
+ viewItems[item].index = model->index(
+ modelIndex.row() - delta, modelIndex.column(), parent);
+ }
+ item += count;
+ }
+ }
+
+ updateChildCount(parentItem, -removedCount);
+ if (after) {
+ q->updateGeometries();
+ viewport->update();
+ } else {
+ //we have removed items: we should at least update the scroll bar values.
+ // They are used to determine the item geometry.
+ updateScrollBars();
+ }
+ } else {
+ // If an ancestor of root is removed then relayout
+ QModelIndex idx = root;
+ while (idx.isValid()) {
+ idx = idx.parent();
+ if (idx == parent) {
+ doDelayedItemsLayout();
+ break;
+ }
+ }
+ }
+ _q_rowsRemoved(parent, start, end);
+
+ QSet<QPersistentModelIndex>::iterator it = expandedIndexes.begin();
+ while (it != expandedIndexes.constEnd()) {
+ if (!it->isValid())
+ it = expandedIndexes.erase(it);
+ else
+ ++it;
+ }
+ it = hiddenIndexes.begin();
+ while (it != hiddenIndexes.constEnd()) {
+ if (!it->isValid())
+ it = hiddenIndexes.erase(it);
+ else
+ ++it;
+ }
+}
+
+void QTreeViewPrivate::updateChildCount(const int parentItem, const int delta)
+{
+ if ((parentItem != -1) && delta) {
+ int level = viewItems.at(parentItem).level;
+ int item = parentItem;
+ do {
+ Q_ASSERT(item >= 0);
+ for ( ; int(viewItems.at(item).level) != level; --item) ;
+ viewItems[item].total += delta;
+ --level;
+ } while (level >= 0);
+ }
+}
+
+
+void QTreeViewPrivate::_q_sortIndicatorChanged(int column, Qt::SortOrder order)
+{
+ model->sort(column, order);
+}
+
+/*!
+ \reimp
+ */
+void QTreeView::currentChanged(const QModelIndex &current, const QModelIndex &previous)
+{
+#ifndef QT_NO_ACCESSIBILITY
+ if (QAccessible::isActive()) {
+ int entry = visualIndex(current) + 1;
+ if (header())
+ ++entry;
+ QAccessible::updateAccessibility(viewport(), entry, QAccessible::Focus);
+ }
+#endif
+ QAbstractItemView::currentChanged(current, previous);
+}
+
+/*!
+ \reimp
+ */
+void QTreeView::selectionChanged(const QItemSelection &selected,
+ const QItemSelection &deselected)
+{
+#ifndef QT_NO_ACCESSIBILITY
+ if (QAccessible::isActive()) {
+ // ### does not work properly for selection ranges.
+ QModelIndex sel = selected.indexes().value(0);
+ if (sel.isValid()) {
+ int entry = visualIndex(sel) + 1;
+ if (header())
+ ++entry;
+ QAccessible::updateAccessibility(viewport(), entry, QAccessible::Selection);
+ }
+ QModelIndex desel = deselected.indexes().value(0);
+ if (desel.isValid()) {
+ int entry = visualIndex(desel) + 1;
+ if (header())
+ ++entry;
+ QAccessible::updateAccessibility(viewport(), entry, QAccessible::SelectionRemove);
+ }
+ }
+#endif
+ QAbstractItemView::selectionChanged(selected, deselected);
+}
+
+int QTreeView::visualIndex(const QModelIndex &index) const
+{
+ Q_D(const QTreeView);
+ d->executePostedLayout();
+ return d->viewIndex(index);
+}
+
+QT_END_NAMESPACE
+
+#include "moc_qtreeview.cpp"
+
+#endif // QT_NO_TREEVIEW
diff --git a/src/gui/itemviews/qtreeview.h b/src/gui/itemviews/qtreeview.h
new file mode 100644
index 0000000000..1e1a36201b
--- /dev/null
+++ b/src/gui/itemviews/qtreeview.h
@@ -0,0 +1,241 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QTREEVIEW_H
+#define QTREEVIEW_H
+
+#include <QtGui/qabstractitemview.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+#ifndef QT_NO_TREEVIEW
+
+class QTreeViewPrivate;
+class QHeaderView;
+
+class Q_GUI_EXPORT QTreeView : public QAbstractItemView
+{
+ Q_OBJECT
+ Q_PROPERTY(int autoExpandDelay READ autoExpandDelay WRITE setAutoExpandDelay)
+ Q_PROPERTY(int indentation READ indentation WRITE setIndentation)
+ Q_PROPERTY(bool rootIsDecorated READ rootIsDecorated WRITE setRootIsDecorated)
+ Q_PROPERTY(bool uniformRowHeights READ uniformRowHeights WRITE setUniformRowHeights)
+ Q_PROPERTY(bool itemsExpandable READ itemsExpandable WRITE setItemsExpandable)
+ Q_PROPERTY(bool sortingEnabled READ isSortingEnabled WRITE setSortingEnabled)
+ Q_PROPERTY(bool animated READ isAnimated WRITE setAnimated)
+ Q_PROPERTY(bool allColumnsShowFocus READ allColumnsShowFocus WRITE setAllColumnsShowFocus)
+ Q_PROPERTY(bool wordWrap READ wordWrap WRITE setWordWrap)
+ Q_PROPERTY(bool headerHidden READ isHeaderHidden WRITE setHeaderHidden)
+ Q_PROPERTY(bool expandsOnDoubleClick READ expandsOnDoubleClick WRITE setExpandsOnDoubleClick)
+
+public:
+ explicit QTreeView(QWidget *parent = 0);
+ ~QTreeView();
+
+ void setModel(QAbstractItemModel *model);
+ void setRootIndex(const QModelIndex &index);
+ void setSelectionModel(QItemSelectionModel *selectionModel);
+
+ QHeaderView *header() const;
+ void setHeader(QHeaderView *header);
+
+ int autoExpandDelay() const;
+ void setAutoExpandDelay(int delay);
+
+ int indentation() const;
+ void setIndentation(int i);
+
+ bool rootIsDecorated() const;
+ void setRootIsDecorated(bool show);
+
+ bool uniformRowHeights() const;
+ void setUniformRowHeights(bool uniform);
+
+ bool itemsExpandable() const;
+ void setItemsExpandable(bool enable);
+
+ bool expandsOnDoubleClick() const;
+ void setExpandsOnDoubleClick(bool enable);
+
+ int columnViewportPosition(int column) const;
+ int columnWidth(int column) const;
+ void setColumnWidth(int column, int width);
+ int columnAt(int x) const;
+
+ bool isColumnHidden(int column) const;
+ void setColumnHidden(int column, bool hide);
+
+ bool isHeaderHidden() const;
+ void setHeaderHidden(bool hide);
+
+ bool isRowHidden(int row, const QModelIndex &parent) const;
+ void setRowHidden(int row, const QModelIndex &parent, bool hide);
+
+ bool isFirstColumnSpanned(int row, const QModelIndex &parent) const;
+ void setFirstColumnSpanned(int row, const QModelIndex &parent, bool span);
+
+ bool isExpanded(const QModelIndex &index) const;
+ void setExpanded(const QModelIndex &index, bool expand);
+
+ void setSortingEnabled(bool enable);
+ bool isSortingEnabled() const;
+
+ void setAnimated(bool enable);
+ bool isAnimated() const;
+
+ void setAllColumnsShowFocus(bool enable);
+ bool allColumnsShowFocus() const;
+
+ void setWordWrap(bool on);
+ bool wordWrap() const;
+
+ void keyboardSearch(const QString &search);
+
+ QRect visualRect(const QModelIndex &index) const;
+ void scrollTo(const QModelIndex &index, ScrollHint hint = EnsureVisible);
+ QModelIndex indexAt(const QPoint &p) const;
+ QModelIndex indexAbove(const QModelIndex &index) const;
+ QModelIndex indexBelow(const QModelIndex &index) const;
+
+ void doItemsLayout();
+ void reset();
+
+ void sortByColumn(int column, Qt::SortOrder order);
+
+Q_SIGNALS:
+ void expanded(const QModelIndex &index);
+ void collapsed(const QModelIndex &index);
+
+public Q_SLOTS:
+ void dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight);
+ void hideColumn(int column);
+ void showColumn(int column);
+ void expand(const QModelIndex &index);
+ void collapse(const QModelIndex &index);
+ void resizeColumnToContents(int column);
+ void sortByColumn(int column);
+ void selectAll();
+ void expandAll();
+ void collapseAll();
+ void expandToDepth(int depth);
+
+protected Q_SLOTS:
+ void columnResized(int column, int oldSize, int newSize);
+ void columnCountChanged(int oldCount, int newCount);
+ void columnMoved();
+ void reexpand();
+ void rowsRemoved(const QModelIndex &parent, int first, int last);
+
+protected:
+ QTreeView(QTreeViewPrivate &dd, QWidget *parent = 0);
+ void scrollContentsBy(int dx, int dy);
+ void rowsInserted(const QModelIndex &parent, int start, int end);
+ void rowsAboutToBeRemoved(const QModelIndex &parent, int start, int end);
+
+ QModelIndex moveCursor(CursorAction cursorAction, Qt::KeyboardModifiers modifiers);
+ int horizontalOffset() const;
+ int verticalOffset() const;
+
+ void setSelection(const QRect &rect, QItemSelectionModel::SelectionFlags command);
+ QRegion visualRegionForSelection(const QItemSelection &selection) const;
+ QModelIndexList selectedIndexes() const;
+
+ void timerEvent(QTimerEvent *event);
+ void paintEvent(QPaintEvent *event);
+
+ void drawTree(QPainter *painter, const QRegion &region) const;
+ virtual void drawRow(QPainter *painter,
+ const QStyleOptionViewItem &options,
+ const QModelIndex &index) const;
+ virtual void drawBranches(QPainter *painter,
+ const QRect &rect,
+ const QModelIndex &index) const;
+
+ void mousePressEvent(QMouseEvent *event);
+ void mouseReleaseEvent(QMouseEvent *event);
+ void mouseDoubleClickEvent(QMouseEvent *event);
+ void mouseMoveEvent(QMouseEvent *event);
+ void keyPressEvent(QKeyEvent *event);
+#ifndef QT_NO_DRAGANDDROP
+ void dragMoveEvent(QDragMoveEvent *event);
+#endif
+ bool viewportEvent(QEvent *event);
+
+ void updateGeometries();
+
+ int sizeHintForColumn(int column) const;
+ int indexRowSizeHint(const QModelIndex &index) const;
+ int rowHeight(const QModelIndex &index) const;
+
+ void horizontalScrollbarAction(int action);
+
+ bool isIndexHidden(const QModelIndex &index) const;
+ void selectionChanged(const QItemSelection &selected,
+ const QItemSelection &deselected);
+ void currentChanged(const QModelIndex &current, const QModelIndex &previous);
+
+private:
+ friend class QAccessibleItemView;
+ int visualIndex(const QModelIndex &index) const;
+
+ Q_DECLARE_PRIVATE(QTreeView)
+ Q_DISABLE_COPY(QTreeView)
+ Q_PRIVATE_SLOT(d_func(), void _q_endAnimatedOperation())
+ Q_PRIVATE_SLOT(d_func(), void _q_animate())
+ Q_PRIVATE_SLOT(d_func(), void _q_currentChanged(const QModelIndex&, const QModelIndex &))
+ Q_PRIVATE_SLOT(d_func(), void _q_columnsAboutToBeRemoved(const QModelIndex &, int, int))
+ Q_PRIVATE_SLOT(d_func(), void _q_columnsRemoved(const QModelIndex &, int, int))
+ Q_PRIVATE_SLOT(d_func(), void _q_modelAboutToBeReset())
+ Q_PRIVATE_SLOT(d_func(), void _q_sortIndicatorChanged(int column, Qt::SortOrder order))
+ Q_PRIVATE_SLOT(d_func(), void _q_modelDestroyed())
+};
+
+#endif // QT_NO_TREEVIEW
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QTREEVIEW_H
diff --git a/src/gui/itemviews/qtreeview_p.h b/src/gui/itemviews/qtreeview_p.h
new file mode 100644
index 0000000000..22155c758a
--- /dev/null
+++ b/src/gui/itemviews/qtreeview_p.h
@@ -0,0 +1,240 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QTREEVIEW_P_H
+#define QTREEVIEW_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/qabstractitemview_p.h"
+
+#ifndef QT_NO_TREEVIEW
+
+QT_BEGIN_NAMESPACE
+
+struct QTreeViewItem
+{
+ QTreeViewItem() : expanded(false), spanning(false), total(0), level(0), height(0) {}
+ QModelIndex index; // we remove items whenever the indexes are invalidated
+ uint expanded : 1;
+ uint spanning : 1;
+ uint total : 30; // total number of children visible
+ uint level : 16; // indentation
+ int height : 16; // row height
+};
+
+class QTreeViewPrivate : public QAbstractItemViewPrivate
+{
+ Q_DECLARE_PUBLIC(QTreeView)
+public:
+
+ QTreeViewPrivate()
+ : QAbstractItemViewPrivate(),
+ header(0), indent(20), lastViewedItem(0), defaultItemHeight(-1),
+ uniformRowHeights(false), rootDecoration(true),
+ itemsExpandable(true), sortingEnabled(false),
+ expandsOnDoubleClick(true),
+ allColumnsShowFocus(false),
+ animationsEnabled(false), columnResizeTimerID(0),
+ autoExpandDelay(-1), hoverBranch(-1), geometryRecursionBlock(false) {}
+
+ ~QTreeViewPrivate() {}
+ void initialize();
+
+ struct AnimatedOperation
+ {
+ enum Type { Expand, Collapse };
+ int item;
+ int top;
+ int duration;
+ Type type;
+ QPixmap before;
+ QPixmap after;
+ };
+
+ void expand(int item, bool emitSignal);
+ void collapse(int item, bool emitSignal);
+
+ void prepareAnimatedOperation(int item, AnimatedOperation::Type type);
+ void beginAnimatedOperation();
+ void _q_endAnimatedOperation();
+ void drawAnimatedOperation(QPainter *painter) const;
+ QPixmap renderTreeToPixmapForAnimation(const QRect &rect) const;
+
+ inline QRect animationRect() const
+ { return QRect(0, animatedOperation.top, viewport->width(),
+ viewport->height() - animatedOperation.top); }
+
+ void _q_currentChanged(const QModelIndex&, const QModelIndex&);
+ void _q_columnsAboutToBeRemoved(const QModelIndex &, int, int);
+ void _q_columnsRemoved(const QModelIndex &, int, int);
+ void _q_modelAboutToBeReset();
+ void _q_animate();
+ void _q_sortIndicatorChanged(int column, Qt::SortOrder order);
+ void _q_modelDestroyed();
+
+ void layout(int item);
+
+ int pageUp(int item) const;
+ int pageDown(int item) const;
+
+ int itemHeight(int item) const;
+ int indentationForItem(int item) const;
+ int coordinateForItem(int item) const;
+ int itemAtCoordinate(int coordinate) const;
+
+ int viewIndex(const QModelIndex &index) const;
+ QModelIndex modelIndex(int i, int column = 0) const;
+
+ int firstVisibleItem(int *offset = 0) const;
+ int columnAt(int x) const;
+ bool hasVisibleChildren( const QModelIndex& parent) const;
+
+ void relayout(const QModelIndex &parent);
+ bool expandOrCollapseItemAtPos(const QPoint &pos);
+
+ void updateScrollBars();
+
+ int itemDecorationAt(const QPoint &pos) const;
+ QRect itemDecorationRect(const QModelIndex &index) const;
+
+
+ QList<QPair<int, int> > columnRanges(const QModelIndex &topIndex, const QModelIndex &bottomIndex) const;
+ void select(const QModelIndex &start, const QModelIndex &stop, QItemSelectionModel::SelectionFlags command);
+
+ QPair<int,int> startAndEndColumns(const QRect &rect) const;
+
+ void updateChildCount(const int parentItem, const int delta);
+ void rowsRemoved(const QModelIndex &parent,
+ int start, int end, bool before);
+
+ void paintAlternatingRowColors(QPainter *painter, QStyleOptionViewItemV4 *option, int y, int bottom) const;
+
+ QHeaderView *header;
+ int indent;
+
+ mutable QVector<QTreeViewItem> viewItems;
+ mutable int lastViewedItem;
+ int defaultItemHeight; // this is just a number; contentsHeight() / numItems
+ bool uniformRowHeights; // used when all rows have the same height
+ bool rootDecoration;
+ bool itemsExpandable;
+ bool sortingEnabled;
+ bool expandsOnDoubleClick;
+ bool allColumnsShowFocus;
+
+ // used for drawing
+ mutable QPair<int,int> leftAndRight;
+ mutable int current;
+ mutable bool spanning;
+
+ // used when expanding and collapsing items
+ QSet<QPersistentModelIndex> expandedIndexes;
+ QStack<bool> expandParent;
+ AnimatedOperation animatedOperation;
+ bool animationsEnabled;
+
+ inline bool storeExpanded(const QPersistentModelIndex &idx) {
+ if (expandedIndexes.contains(idx))
+ return false;
+ expandedIndexes.insert(idx);
+ return true;
+ }
+
+ inline bool isIndexExpanded(const QModelIndex &idx) const {
+ //We first check if the idx is a QPersistentModelIndex, because creating QPersistentModelIndex is slow
+ return isPersistent(idx) && expandedIndexes.contains(idx);
+ }
+
+ // used when hiding and showing items
+ QSet<QPersistentModelIndex> hiddenIndexes;
+
+ inline bool isRowHidden(const QModelIndex &idx) const {
+ //We first check if the idx is a QPersistentModelIndex, because creating QPersistentModelIndex is slow
+ return isPersistent(idx) && hiddenIndexes.contains(idx);
+ }
+
+ inline bool isItemHiddenOrDisabled(int i) const {
+ if (i < 0 || i >= viewItems.count())
+ return false;
+ const QModelIndex index = viewItems.at(i).index;
+ return isRowHidden(index) || !isIndexEnabled(index);
+ }
+
+ inline int above(int item) const
+ { int i = item; while (isItemHiddenOrDisabled(--item)){} return item < 0 ? i : item; }
+ inline int below(int item) const
+ { int i = item; while (isItemHiddenOrDisabled(++item)){} return item >= viewItems.count() ? i : item; }
+ inline void invalidateHeightCache(int item) const
+ { viewItems[item].height = 0; }
+
+ // used for spanning rows
+ QVector<QPersistentModelIndex> spanningIndexes;
+
+ // used for updating resized columns
+ int columnResizeTimerID;
+ QList<int> columnsToUpdate;
+
+ // used for the automatic opening of nodes during DND
+ int autoExpandDelay;
+ QBasicTimer openTimer;
+
+ // used for drawing hilighted expand/collapse indicators
+ int hoverBranch;
+
+ // used for blocking recursion when calling setViewportMargins from updateGeometries
+ bool geometryRecursionBlock;
+};
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_TREEVIEW
+
+#endif // QTREEVIEW_P_H
diff --git a/src/gui/itemviews/qtreewidget.cpp b/src/gui/itemviews/qtreewidget.cpp
new file mode 100644
index 0000000000..610322516a
--- /dev/null
+++ b/src/gui/itemviews/qtreewidget.cpp
@@ -0,0 +1,3437 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qtreewidget.h"
+
+#ifndef QT_NO_TREEWIDGET
+#include <qheaderview.h>
+#include <qpainter.h>
+#include <qitemdelegate.h>
+#include <qstack.h>
+#include <qdebug.h>
+#include <private/qtreewidget_p.h>
+#include <private/qwidgetitemdata_p.h>
+#include <private/qtreewidgetitemiterator_p.h>
+
+QT_BEGIN_NAMESPACE
+
+// workaround for VC++ 6.0 linker bug (?)
+typedef bool(*LessThan)(const QPair<QTreeWidgetItem*,int>&,const QPair<QTreeWidgetItem*,int>&);
+
+class QTreeModelLessThan
+{
+public:
+ inline bool operator()(QTreeWidgetItem *i1, QTreeWidgetItem *i2) const
+ { return *i1 < *i2; }
+};
+
+class QTreeModelGreaterThan
+{
+public:
+ inline bool operator()(QTreeWidgetItem *i1, QTreeWidgetItem *i2) const
+ { return *i2 < *i1; }
+};
+
+/*
+ \class QTreeModel
+ \brief The QTreeModel class manages the items stored in a tree view.
+
+ \ingroup model-view
+ \mainclass
+*/
+
+/*!
+ \enum QTreeWidgetItem::ChildIndicatorPolicy
+ \since 4.3
+
+ \value ShowIndicator The controls for expanding and collapsing will be shown for this item even if there are no children.
+ \value DontShowIndicator The controls for expanding and collapsing will never be shown even if there are children. If the node is forced open the user will not be able to expand or collapse the item.
+ \value DontShowIndicatorWhenChildless The controls for expanding and collapsing will be shown if the item contains children.
+*/
+
+/*!
+ \fn void QTreeWidgetItem::setDisabled(bool disabled)
+ \since 4.3
+
+ Disables the item if \a disabled is true; otherwise enables the item.
+
+ \sa setFlags()
+*/
+
+/*!
+ \fn bool QTreeWidgetItem::isDisabled() const
+ \since 4.3
+
+ Returns true if the item is disabled; otherwise returns false.
+
+ \sa setFlags()
+*/
+
+/*!
+ \internal
+
+ Constructs a tree model with a \a parent object and the given
+ number of \a columns.
+*/
+
+QTreeModel::QTreeModel(int columns, QTreeWidget *parent)
+ : QAbstractItemModel(parent), rootItem(new QTreeWidgetItem),
+ headerItem(new QTreeWidgetItem), skipPendingSort(false)
+{
+ rootItem->view = parent;
+ rootItem->itemFlags = Qt::ItemIsDropEnabled;
+ headerItem->view = parent;
+ setColumnCount(columns);
+}
+
+/*!
+ \internal
+
+*/
+
+QTreeModel::QTreeModel(QTreeModelPrivate &dd, QTreeWidget *parent)
+ : QAbstractItemModel(dd, parent), rootItem(new QTreeWidgetItem),
+ headerItem(new QTreeWidgetItem), skipPendingSort(false)
+{
+ rootItem->view = parent;
+ rootItem->itemFlags = Qt::ItemIsDropEnabled;
+ headerItem->view = parent;
+}
+
+/*!
+ \internal
+
+ Destroys this tree model.
+*/
+
+QTreeModel::~QTreeModel()
+{
+ clear();
+ delete headerItem;
+ rootItem->view = 0;
+ delete rootItem;
+}
+
+/*!
+ \internal
+
+ Removes all items in the model.
+*/
+
+void QTreeModel::clear()
+{
+ SkipSorting skipSorting(this);
+ for (int i = 0; i < rootItem->childCount(); ++i) {
+ QTreeWidgetItem *item = rootItem->children.at(i);
+ item->par = 0;
+ item->view = 0;
+ delete item;
+ }
+ rootItem->children.clear();
+ sortPendingTimer.stop();
+ reset();
+}
+
+/*!
+ \internal
+
+ Sets the number of \a columns in the tree model.
+*/
+
+void QTreeModel::setColumnCount(int columns)
+{
+ SkipSorting skipSorting(this);
+ if (columns < 0)
+ return;
+ if (!headerItem) {
+ headerItem = new QTreeWidgetItem();
+ headerItem->view = view();
+ }
+ int count = columnCount();
+ if (count == columns)
+ return;
+
+ if (columns < count) {
+ beginRemoveColumns(QModelIndex(), columns, count - 1);
+ headerItem->values.resize(columns);
+ endRemoveColumns();
+ } else {
+ beginInsertColumns(QModelIndex(), count, columns - 1);
+ headerItem->values.resize(columns);
+ for (int i = count; i < columns; ++i) {// insert data without emitting the dataChanged signal
+ headerItem->values[i].append(QWidgetItemData(Qt::DisplayRole, QString::number(i + 1)));
+ headerItem->d->display.append(QString::number(i + 1));
+ }
+ endInsertColumns();
+ }
+}
+
+/*!
+ \internal
+
+ Returns the tree view item corresponding to the \a index given.
+
+ \sa QModelIndex
+*/
+
+QTreeWidgetItem *QTreeModel::item(const QModelIndex &index) const
+{
+ if (!index.isValid())
+ return 0;
+ return static_cast<QTreeWidgetItem*>(index.internalPointer());
+}
+
+/*!
+ \internal
+
+ Returns the model index that refers to the
+ tree view \a item and \a column.
+*/
+
+QModelIndex QTreeModel::index(const QTreeWidgetItem *item, int column) const
+{
+ executePendingSort();
+
+ if (!item || (item == rootItem))
+ return QModelIndex();
+ const QTreeWidgetItem *par = item->parent();
+ QTreeWidgetItem *itm = const_cast<QTreeWidgetItem*>(item);
+ if (!par)
+ par = rootItem;
+ int row;
+ int guess = item->d->rowGuess;
+ if (guess >= 0
+ && par->children.count() > guess
+ && par->children.at(guess) == itm) {
+ row = guess;
+ } else {
+ row = par->children.lastIndexOf(itm);
+ itm->d->rowGuess = row;
+ }
+ return createIndex(row, column, itm);
+}
+
+/*!
+ \internal
+ \reimp
+
+ Returns the model index with the given \a row,
+ \a column and \a parent.
+*/
+
+QModelIndex QTreeModel::index(int row, int column, const QModelIndex &parent) const
+{
+ executePendingSort();
+
+ int c = columnCount(parent);
+ if (row < 0 || column < 0 || column >= c)
+ return QModelIndex();
+
+ QTreeWidgetItem *parentItem = parent.isValid() ? item(parent) : rootItem;
+ if (parentItem && row < parentItem->childCount()) {
+ QTreeWidgetItem *itm = parentItem->child(row);
+ if (itm)
+ return createIndex(row, column, itm);
+ return QModelIndex();
+ }
+
+ return QModelIndex();
+}
+
+/*!
+ \internal
+ \reimp
+
+ Returns the parent model index of the index given as
+ the \a child.
+*/
+
+QModelIndex QTreeModel::parent(const QModelIndex &child) const
+{
+ SkipSorting skipSorting(this); //The reason we don't sort here is that this might be called from a valid QPersistentModelIndex
+ //We don't want it to become suddenly invalid
+
+ if (!child.isValid())
+ return QModelIndex();
+ QTreeWidgetItem *itm = static_cast<QTreeWidgetItem *>(child.internalPointer());
+ if (!itm || itm == rootItem)
+ return QModelIndex();
+ QTreeWidgetItem *parent = itm->parent();
+ return index(parent, 0);
+}
+
+/*!
+ \internal
+ \reimp
+
+ Returns the number of rows in the \a parent model index.
+*/
+
+int QTreeModel::rowCount(const QModelIndex &parent) const
+{
+ if (!parent.isValid())
+ return rootItem->childCount();
+
+ QTreeWidgetItem *parentItem = item(parent);
+ if (parentItem)
+ return parentItem->childCount();
+ return 0;
+}
+
+/*!
+ \internal
+ \reimp
+
+ Returns the number of columns in the item referred to by
+ the given \a index.
+*/
+
+int QTreeModel::columnCount(const QModelIndex &index) const
+{
+ Q_UNUSED(index);
+ if (!headerItem)
+ return 0;
+ return headerItem->columnCount();
+}
+
+bool QTreeModel::hasChildren(const QModelIndex &parent) const
+{
+ if (!parent.isValid())
+ return (rootItem->childCount() > 0);
+
+ QTreeWidgetItem *itm = item(parent);
+ if (!itm)
+ return false;
+ switch (itm->d->policy) {
+ case QTreeWidgetItem::ShowIndicator:
+ return true;
+ case QTreeWidgetItem::DontShowIndicator:
+ return false;
+ case QTreeWidgetItem::DontShowIndicatorWhenChildless:
+ return (itm->childCount() > 0);
+ }
+ return false;
+}
+
+/*!
+ \internal
+ \reimp
+
+ Returns the data corresponding to the given model \a index
+ and \a role.
+*/
+
+QVariant QTreeModel::data(const QModelIndex &index, int role) const
+{
+ if (!index.isValid())
+ return QVariant();
+ QTreeWidgetItem *itm = item(index);
+ if (itm)
+ return itm->data(index.column(), role);
+ return QVariant();
+}
+
+/*!
+ \internal
+ \reimp
+
+ Sets the data for the item specified by the \a index and \a role
+ to that referred to by the \a value.
+
+ Returns true if successful; otherwise returns false.
+*/
+
+bool QTreeModel::setData(const QModelIndex &index, const QVariant &value, int role)
+{
+ if (!index.isValid())
+ return false;
+ QTreeWidgetItem *itm = item(index);
+ if (itm) {
+ itm->setData(index.column(), role, value);
+ return true;
+ }
+ return false;
+}
+
+QMap<int, QVariant> QTreeModel::itemData(const QModelIndex &index) const
+{
+ QMap<int, QVariant> roles;
+ QTreeWidgetItem *itm = item(index);
+ if (itm) {
+ int column = index.column();
+ if (column < itm->values.count()) {
+ for (int i = 0; i < itm->values.at(column).count(); ++i) {
+ roles.insert(itm->values.at(column).at(i).role,
+ itm->values.at(column).at(i).value);
+ }
+ }
+
+ // the two special cases
+ QVariant displayValue = itm->data(column, Qt::DisplayRole);
+ if (displayValue.isValid())
+ roles.insert(Qt::DisplayRole, displayValue);
+
+ QVariant checkValue = itm->data(column, Qt::CheckStateRole);
+ if (checkValue.isValid())
+ roles.insert(Qt::CheckStateRole, checkValue);
+ }
+ return roles;
+}
+
+/*!
+ \internal
+ \reimp
+*/
+bool QTreeModel::insertRows(int row, int count, const QModelIndex &parent)
+{
+ SkipSorting skipSorting(this);
+ if (count < 1 || row < 0 || row > rowCount(parent) || parent.column() > 0)
+ return false;
+
+ beginInsertRows(parent, row, row + count - 1);
+ QTreeWidgetItem *par = item(parent);
+ while (count > 0) {
+ QTreeWidgetItem *item = new QTreeWidgetItem();
+ item->view = view();
+ item->par = par;
+ if (par)
+ par->children.insert(row++, item);
+ else
+ rootItem->children.insert(row++, item);
+ --count;
+ }
+ endInsertRows();
+ return true;
+}
+
+/*!
+ \internal
+ \reimp
+*/
+bool QTreeModel::insertColumns(int column, int count, const QModelIndex &parent)
+{
+ SkipSorting skipSorting(this);
+ if (count < 1 || column < 0 || column > columnCount(parent) || parent.column() > 0 || !headerItem)
+ return false;
+
+ beginInsertColumns(parent, column, column + count - 1);
+
+ int oldCount = columnCount(parent);
+ column = qBound(0, column, oldCount);
+ headerItem->values.resize(oldCount + count);
+ for (int i = oldCount; i < oldCount + count; ++i) {
+ headerItem->values[i].append(QWidgetItemData(Qt::DisplayRole, QString::number(i + 1)));
+ headerItem->d->display.append(QString::number(i + 1));
+ }
+
+ QStack<QTreeWidgetItem*> itemstack;
+ itemstack.push(0);
+ while (!itemstack.isEmpty()) {
+ QTreeWidgetItem *par = itemstack.pop();
+ QList<QTreeWidgetItem*> children = par ? par->children : rootItem->children;
+ for (int row = 0; row < children.count(); ++row) {
+ QTreeWidgetItem *child = children.at(row);
+ if (child->children.count())
+ itemstack.push(child);
+ child->values.insert(column, count, QVector<QWidgetItemData>());
+ }
+ }
+
+ endInsertColumns();
+ return true;
+}
+
+/*!
+ \internal
+ \reimp
+*/
+bool QTreeModel::removeRows(int row, int count, const QModelIndex &parent) {
+ if (count < 1 || row < 0 || (row + count) > rowCount(parent))
+ return false;
+
+ beginRemoveRows(parent, row, row + count - 1);
+
+ bool blockSignal = signalsBlocked();
+ blockSignals(true);
+
+ QTreeWidgetItem *itm = item(parent);
+ for (int i = row + count - 1; i >= row; --i) {
+ QTreeWidgetItem *child = itm ? itm->takeChild(i) : rootItem->children.takeAt(i);
+ Q_ASSERT(child);
+ child->view = 0;
+ delete child;
+ child = 0;
+ }
+ blockSignals(blockSignal);
+
+ endRemoveRows();
+ return true;
+}
+
+/*!
+ \internal
+ \reimp
+
+ Returns the header data corresponding to the given header \a section,
+ \a orientation and data \a role.
+*/
+
+QVariant QTreeModel::headerData(int section, Qt::Orientation orientation, int role) const
+{
+ if (orientation != Qt::Horizontal)
+ return QVariant();
+
+ if (headerItem)
+ return headerItem->data(section, role);
+ if (role == Qt::DisplayRole)
+ return QString::number(section + 1);
+ return QVariant();
+}
+
+/*!
+ \internal
+ \reimp
+
+ Sets the header data for the item specified by the header \a section,
+ \a orientation and data \a role to the given \a value.
+
+ Returns true if successful; otherwise returns false.
+*/
+
+bool QTreeModel::setHeaderData(int section, Qt::Orientation orientation,
+ const QVariant &value, int role)
+{
+ if (section < 0 || orientation != Qt::Horizontal || !headerItem || section >= columnCount())
+ return false;
+
+ headerItem->setData(section, role, value);
+ return true;
+}
+
+/*!
+ \reimp
+
+ Returns the flags for the item referred to the given \a index.
+
+*/
+
+Qt::ItemFlags QTreeModel::flags(const QModelIndex &index) const
+{
+ if (!index.isValid())
+ return rootItem->flags();
+ QTreeWidgetItem *itm = item(index);
+ Q_ASSERT(itm);
+ return itm->flags();
+}
+
+/*!
+ \internal
+
+ Sorts the entire tree in the model in the given \a order,
+ by the values in the given \a column.
+*/
+
+void QTreeModel::sort(int column, Qt::SortOrder order)
+{
+ SkipSorting skipSorting(this);
+ sortPendingTimer.stop();
+
+ if (column < 0 || column >= columnCount())
+ return;
+
+ //layoutAboutToBeChanged and layoutChanged will be called by sortChildren
+ rootItem->sortChildren(column, order, true);
+}
+
+/*!
+ \internal
+*/
+void QTreeModel::ensureSorted(int column, Qt::SortOrder order,
+ int start, int end, const QModelIndex &parent)
+{
+ if (isChanging())
+ return;
+
+ sortPendingTimer.stop();
+
+ if (column < 0 || column >= columnCount())
+ return;
+
+ SkipSorting skipSorting(this);
+
+ QTreeWidgetItem *itm = item(parent);
+ if (!itm)
+ itm = rootItem;
+ QList<QTreeWidgetItem*> lst = itm->children;
+
+ int count = end - start + 1;
+ QVector < QPair<QTreeWidgetItem*,int> > sorting(count);
+ for (int i = 0; i < count; ++i) {
+ sorting[i].first = lst.at(start + i);
+ sorting[i].second = start + i;
+ }
+
+ LessThan compare = (order == Qt::AscendingOrder ? &itemLessThan : &itemGreaterThan);
+ qStableSort(sorting.begin(), sorting.end(), compare);
+
+ QModelIndexList oldPersistentIndexes;
+ QModelIndexList newPersistentIndexes;
+ QList<QTreeWidgetItem*>::iterator lit = lst.begin();
+ bool changed = false;
+
+ for (int i = 0; i < count; ++i) {
+ int oldRow = sorting.at(i).second;
+ QTreeWidgetItem *item = lst.takeAt(oldRow);
+ lit = sortedInsertionIterator(lit, lst.end(), order, item);
+ int newRow = qMax(lit - lst.begin(), 0);
+ lit = lst.insert(lit, item);
+ if (newRow != oldRow) {
+ // we are going to change the persistent indexes, so we need to prepare
+ if (!changed) { // this will only happen once
+ changed = true;
+ emit layoutAboutToBeChanged(); // the selection model needs to know
+ oldPersistentIndexes = persistentIndexList();
+ newPersistentIndexes = oldPersistentIndexes;
+ }
+ for (int j = i + 1; j < count; ++j) {
+ int otherRow = sorting.at(j).second;
+ if (oldRow < otherRow && newRow >= otherRow)
+ --sorting[j].second;
+ else if (oldRow > otherRow && newRow <= otherRow)
+ ++sorting[j].second;
+ }
+ for (int k = 0; k < newPersistentIndexes.count(); ++k) {
+ QModelIndex pi = newPersistentIndexes.at(k);
+ if (pi.parent() != parent)
+ continue;
+ int oldPersistentRow = pi.row();
+ int newPersistentRow = oldPersistentRow;
+ if (oldPersistentRow == oldRow)
+ newPersistentRow = newRow;
+ else if (oldRow < oldPersistentRow && newRow >= oldPersistentRow)
+ newPersistentRow = oldPersistentRow - 1;
+ else if (oldRow > oldPersistentRow && newRow <= oldPersistentRow)
+ newPersistentRow = oldPersistentRow + 1;
+ if (newPersistentRow != oldPersistentRow)
+ newPersistentIndexes[k] = createIndex(newPersistentRow,
+ pi.column(), pi.internalPointer());
+ }
+ }
+ }
+
+ if (changed) {
+ itm->children = lst;
+ changePersistentIndexList(oldPersistentIndexes, newPersistentIndexes);
+ emit layoutChanged();
+ }
+}
+
+/*!
+ \internal
+
+ Returns true if the value of the \a left item is
+ less than the value of the \a right item.
+
+ Used by the sorting functions.
+*/
+
+bool QTreeModel::itemLessThan(const QPair<QTreeWidgetItem*,int> &left,
+ const QPair<QTreeWidgetItem*,int> &right)
+{
+ return *(left.first) < *(right.first);
+}
+
+/*!
+ \internal
+
+ Returns true if the value of the \a left item is
+ greater than the value of the \a right item.
+
+ Used by the sorting functions.
+*/
+
+bool QTreeModel::itemGreaterThan(const QPair<QTreeWidgetItem*,int> &left,
+ const QPair<QTreeWidgetItem*,int> &right)
+{
+ return *(right.first) < *(left.first);
+}
+
+/*!
+ \internal
+*/
+QList<QTreeWidgetItem*>::iterator QTreeModel::sortedInsertionIterator(
+ const QList<QTreeWidgetItem*>::iterator &begin,
+ const QList<QTreeWidgetItem*>::iterator &end,
+ Qt::SortOrder order, QTreeWidgetItem *item)
+{
+ if (order == Qt::AscendingOrder)
+ return qLowerBound(begin, end, item, QTreeModelLessThan());
+ return qLowerBound(begin, end, item, QTreeModelGreaterThan());
+}
+
+QStringList QTreeModel::mimeTypes() const
+{
+ return view()->mimeTypes();
+}
+
+QMimeData *QTreeModel::internalMimeData() const
+{
+ return QAbstractItemModel::mimeData(cachedIndexes);
+}
+
+QMimeData *QTreeModel::mimeData(const QModelIndexList &indexes) const
+{
+ QList<QTreeWidgetItem*> items;
+ for (int i = 0; i < indexes.count(); ++i) {
+ if (indexes.at(i).column() == 0) // only one item per row
+ items << item(indexes.at(i));
+ }
+
+ // cachedIndexes is a little hack to avoid copying from QModelIndexList to
+ // QList<QTreeWidgetItem*> and back again in the view
+ cachedIndexes = indexes;
+ QMimeData *mimeData = view()->mimeData(items);
+ cachedIndexes.clear();
+ return mimeData;
+}
+
+bool QTreeModel::dropMimeData(const QMimeData *data, Qt::DropAction action,
+ int row, int column, const QModelIndex &parent)
+{
+ if (row == -1 && column == -1)
+ row = rowCount(parent); // append
+ return view()->dropMimeData(item(parent), row, data, action);
+}
+
+Qt::DropActions QTreeModel::supportedDropActions() const
+{
+ return view()->supportedDropActions();
+}
+
+void QTreeModel::itemChanged(QTreeWidgetItem *item)
+{
+ SkipSorting skipSorting(this); //this is kind of wrong, but not doing this would kill performence
+ QModelIndex left = index(item, 0);
+ QModelIndex right = index(item, item->columnCount() - 1);
+ emit dataChanged(left, right);
+}
+
+bool QTreeModel::isChanging() const
+{
+ Q_D(const QTreeModel);
+ return !d->changes.isEmpty();
+}
+
+/*!
+ \internal
+ Emits the dataChanged() signal for the given \a item.
+ if column is -1 then all columns have changed
+*/
+
+void QTreeModel::emitDataChanged(QTreeWidgetItem *item, int column)
+{
+ if (signalsBlocked())
+ return;
+
+ if (headerItem == item && column < item->columnCount()) {
+ if (column == -1)
+ emit headerDataChanged(Qt::Horizontal, 0, columnCount() - 1);
+ else
+ emit headerDataChanged(Qt::Horizontal, column, column);
+ return;
+ }
+
+ SkipSorting skipSorting(this); //This is a little bit wrong, but not doing it would kill performence
+
+ QModelIndex bottomRight, topLeft;
+ if (column == -1) {
+ topLeft = index(item, 0);
+ bottomRight = createIndex(topLeft.row(), columnCount() - 1, item);
+ } else {
+ topLeft = index(item, column);
+ bottomRight = topLeft;
+ }
+ emit dataChanged(topLeft, bottomRight);
+}
+
+void QTreeModel::beginInsertItems(QTreeWidgetItem *parent, int row, int count)
+{
+ QModelIndex par = index(parent, 0);
+ beginInsertRows(par, row, row + count - 1);
+}
+
+void QTreeModel::endInsertItems()
+{
+ endInsertRows();
+}
+
+void QTreeModel::beginRemoveItems(QTreeWidgetItem *parent, int row, int count)
+{
+ Q_ASSERT(row >= 0);
+ Q_ASSERT(count > 0);
+ beginRemoveRows(index(parent, 0), row, row + count - 1);
+ if (!parent)
+ parent = rootItem;
+ // now update the iterators
+ for (int i = 0; i < iterators.count(); ++i) {
+ for (int j = 0; j < count; j++) {
+ QTreeWidgetItem *c = parent->child(row + j);
+ iterators[i]->d_func()->ensureValidIterator(c);
+ }
+ }
+}
+
+void QTreeModel::endRemoveItems()
+{
+ endRemoveRows();
+}
+
+void QTreeModel::sortItems(QList<QTreeWidgetItem*> *items, int column, Qt::SortOrder order)
+{
+ // see QTreeViewItem::operator<
+ Q_UNUSED(column);
+ if (isChanging())
+ return;
+
+ // store the original order of indexes
+ QVector< QPair<QTreeWidgetItem*,int> > sorting(items->count());
+ for (int i = 0; i < sorting.count(); ++i) {
+ sorting[i].first = items->at(i);
+ sorting[i].second = i;
+ }
+
+ // do the sorting
+ LessThan compare = (order == Qt::AscendingOrder ? &itemLessThan : &itemGreaterThan);
+ qStableSort(sorting.begin(), sorting.end(), compare);
+
+ QModelIndexList fromList;
+ QModelIndexList toList;
+ int colCount = columnCount();
+ for (int r = 0; r < sorting.count(); ++r) {
+ int oldRow = sorting.at(r).second;
+ if (oldRow == r)
+ continue;
+ QTreeWidgetItem *item = sorting.at(r).first;
+ items->replace(r, item);
+ for (int c = 0; c < colCount; ++c) {
+ QModelIndex from = createIndex(oldRow, c, item);
+ if (static_cast<QAbstractItemModelPrivate *>(d_ptr)->persistent.indexes.contains(from)) {
+ QModelIndex to = createIndex(r, c, item);
+ fromList << from;
+ toList << to;
+ }
+ }
+ }
+ changePersistentIndexList(fromList, toList);
+}
+
+void QTreeModel::timerEvent(QTimerEvent *ev)
+{
+ if (ev->timerId() == sortPendingTimer.timerId()) {
+ executePendingSort();
+ } else {
+ QAbstractItemModel::timerEvent(ev);
+ }
+}
+
+/*!
+ \class QTreeWidgetItem
+
+ \brief The QTreeWidgetItem class provides an item for use with the
+ QTreeWidget convenience class.
+
+ \ingroup model-view
+
+ Tree widget items are used to hold rows of information for tree widgets.
+ Rows usually contain several columns of data, each of which can contain
+ a text label and an icon.
+
+ The QTreeWidgetItem class is a convenience class that replaces the
+ QListViewItem class in Qt 3. It provides an item for use with
+ the QTreeWidget class.
+
+ Items are usually constructed with a parent that is either a QTreeWidget
+ (for top-level items) or a QTreeWidgetItem (for items on lower levels of
+ the tree). For example, the following code constructs a top-level item
+ to represent cities of the world, and adds a entry for Oslo as a child
+ item:
+
+ \snippet doc/src/snippets/qtreewidget-using/mainwindow.cpp 3
+
+ Items can be added in a particular order by specifying the item they
+ follow when they are constructed:
+
+ \snippet doc/src/snippets/qtreewidget-using/mainwindow.cpp 5
+
+ Each column in an item can have its own background brush which is set with
+ the setBackground() function. The current background brush can be
+ found with background().
+ The text label for each column can be rendered with its own font and brush.
+ These are specified with the setFont() and setForeground() functions,
+ and read with font() and foreground().
+
+ The main difference between top-level items and those in lower levels of
+ the tree is that a top-level item has no parent(). This information
+ can be used to tell the difference between items, and is useful to know
+ when inserting and removing items from the tree.
+ Children of an item can be removed with takeChild() and inserted at a
+ given index in the list of children with the insertChild() function.
+
+ By default, items are enabled, selectable, checkable, and can be the source
+ of a drag and drop operation.
+ Each item's flags can be changed by calling setFlags() with the appropriate
+ value (see \l{Qt::ItemFlags}). Checkable items can be checked and unchecked
+ with the setCheckState() function. The corresponding checkState() function
+ indicates whether the item is currently checked.
+
+ \section1 Subclassing
+
+ When subclassing QTreeWidgetItem to provide custom items, it is possible to
+ define new types for them so that they can be distinguished from standard
+ items. The constructors for subclasses that require this feature need to
+ call the base class constructor with a new type value equal to or greater
+ than \l UserType.
+
+ \sa QTreeWidget, QTreeWidgetItemIterator, {Model/View Programming},
+ QListWidgetItem, QTableWidgetItem
+*/
+
+/*!
+ \enum QTreeWidgetItem::ItemType
+
+ This enum describes the types that are used to describe tree widget items.
+
+ \value Type The default type for tree widget items.
+ \value UserType The minimum value for custom types. Values below UserType are
+ reserved by Qt.
+
+ You can define new user types in QTreeWidgetItem subclasses to ensure that
+ custom items are treated specially; for example, when items are sorted.
+
+ \sa type()
+*/
+
+/*!
+ \fn int QTreeWidgetItem::type() const
+
+ Returns the type passed to the QTreeWidgetItem constructor.
+*/
+
+/*!
+ \fn void QTreeWidgetItem::sortChildren(int column, Qt::SortOrder order)
+ \since 4.2
+
+ Sorts the children of the item using the given \a order,
+ by the values in the given \a column.
+
+ \note This function does nothing if the item is not associated with a
+ QTreeWidget.
+*/
+
+/*!
+ \fn QTreeWidget *QTreeWidgetItem::treeWidget() const
+
+ Returns the tree widget that contains the item.
+*/
+
+/*!
+ \fn void QTreeWidgetItem::setSelected(bool select)
+ \since 4.2
+
+ Sets the selected state of the item to \a select.
+
+ \sa isSelected()
+
+*/
+
+/*!
+ \fn bool QTreeWidgetItem::isSelected() const
+ \since 4.2
+
+ Returns true if the item is selected, otherwise returns false.
+
+ \sa setSelected()
+*/
+
+/*!
+ \fn void QTreeWidgetItem::setHidden(bool hide)
+ \since 4.2
+
+ Hides the item if \a hide is true, otherwise shows the item.
+
+ \sa isHidden()
+*/
+
+/*!
+ \fn bool QTreeWidgetItem::isHidden() const
+ \since 4.2
+
+ Returns true if the item is hidden, otherwise returns false.
+
+ \sa setHidden()
+*/
+
+/*!
+ \fn void QTreeWidgetItem::setExpanded(bool expand)
+ \since 4.2
+
+ Expands the item if \a expand is true, otherwise collapses the item.
+ \warning The QTreeWidgetItem must be added to the QTreeWidget before calling this function.
+
+ \sa isExpanded()
+*/
+
+/*!
+ \fn bool QTreeWidgetItem::isExpanded() const
+ \since 4.2
+
+ Returns true if the item is expanded, otherwise returns false.
+
+ \sa setExpanded()
+*/
+
+/*!
+ \fn void QTreeWidgetItem::setFirstColumnSpanned(bool span)
+ \since 4.3
+
+ Sets the first section to span all columns if \a span is true;
+ otherwise all item sections are shown.
+
+ \sa isFirstColumnSpanned()
+*/
+
+/*!
+ \fn bool QTreeWidgetItem::isFirstColumnSpanned() const
+ \since 4.3
+
+ Returns true if the item is spanning all the columns in a row; otherwise returns false.
+
+ \sa setFirstColumnSpanned()
+*/
+
+/*!
+ \fn QString QTreeWidgetItem::text(int column) const
+
+ Returns the text in the specified \a column.
+
+ \sa setText()
+*/
+
+/*!
+ \fn void QTreeWidgetItem::setText(int column, const QString &text)
+
+ Sets the text to be displayed in the given \a column to the given \a text.
+
+ \sa text() setFont() setForeground()
+*/
+
+/*!
+ \fn QIcon QTreeWidgetItem::icon(int column) const
+
+ Returns the icon that is displayed in the specified \a column.
+
+ \sa setIcon(), {QAbstractItemView::iconSize}{iconSize}
+*/
+
+/*!
+ \fn void QTreeWidgetItem::setIcon(int column, const QIcon &icon)
+
+ Sets the icon to be displayed in the given \a column to \a icon.
+
+ \sa icon(), setText(), {QAbstractItemView::iconSize}{iconSize}
+*/
+
+/*!
+ \fn QString QTreeWidgetItem::statusTip(int column) const
+
+ Returns the status tip for the contents of the given \a column.
+
+ \sa setStatusTip()
+*/
+
+/*!
+ \fn void QTreeWidgetItem::setStatusTip(int column, const QString &statusTip)
+
+ Sets the status tip for the given \a column to the given \a statusTip.
+ QTreeWidget mouse tracking needs to be enabled for this feature to work.
+
+ \sa statusTip() setToolTip() setWhatsThis()
+*/
+
+/*!
+ \fn QString QTreeWidgetItem::toolTip(int column) const
+
+ Returns the tool tip for the given \a column.
+
+ \sa setToolTip()
+*/
+
+/*!
+ \fn void QTreeWidgetItem::setToolTip(int column, const QString &toolTip)
+
+ Sets the tooltip for the given \a column to \a toolTip.
+
+ \sa toolTip() setStatusTip() setWhatsThis()
+*/
+
+/*!
+ \fn QString QTreeWidgetItem::whatsThis(int column) const
+
+ Returns the "What's This?" help for the contents of the given \a column.
+
+ \sa setWhatsThis()
+*/
+
+/*!
+ \fn void QTreeWidgetItem::setWhatsThis(int column, const QString &whatsThis)
+
+ Sets the "What's This?" help for the given \a column to \a whatsThis.
+
+ \sa whatsThis() setStatusTip() setToolTip()
+*/
+
+/*!
+ \fn QFont QTreeWidgetItem::font(int column) const
+
+ Returns the font used to render the text in the specified \a column.
+
+ \sa setFont()
+*/
+
+/*!
+ \fn void QTreeWidgetItem::setFont(int column, const QFont &font)
+
+ Sets the font used to display the text in the given \a column to the given
+ \a font.
+
+ \sa font() setText() setForeground()
+*/
+
+/*!
+ \fn QColor QTreeWidgetItem::backgroundColor(int column) const
+ \obsolete
+
+ This function is deprecated. Use background() instead.
+*/
+
+/*!
+ \fn void QTreeWidgetItem::setBackgroundColor(int column, const QColor &color)
+ \obsolete
+
+ This function is deprecated. Use setBackground() instead.
+*/
+
+/*!
+ \fn QBrush QTreeWidgetItem::background(int column) const
+ \since 4.2
+
+ Returns the brush used to render the background of the specified \a column.
+
+ \sa foreground()
+*/
+
+/*!
+ \fn void QTreeWidgetItem::setBackground(int column, const QBrush &brush)
+ \since 4.2
+
+ Sets the background brush of the label in the given \a column to the
+ specified \a brush.
+
+ \sa setForeground()
+*/
+
+/*!
+ \fn QColor QTreeWidgetItem::textColor(int column) const
+ \obsolete
+
+ This function is deprecated. Use foreground() instead.
+*/
+
+/*!
+ \fn void QTreeWidgetItem::setTextColor(int column, const QColor &color)
+ \obsolete
+
+ This function is deprecated. Use setForeground() instead.
+*/
+
+/*!
+ \fn QBrush QTreeWidgetItem::foreground(int column) const
+ \since 4.2
+
+ Returns the brush used to render the foreground (e.g. text) of the
+ specified \a column.
+
+ \sa background()
+*/
+
+/*!
+ \fn void QTreeWidgetItem::setForeground(int column, const QBrush &brush)
+ \since 4.2
+
+ Sets the foreground brush of the label in the given \a column to the
+ specified \a brush.
+
+ \sa setBackground()
+*/
+
+/*!
+ \fn Qt::CheckState QTreeWidgetItem::checkState(int column) const
+
+ Returns the check state of the label in the given \a column.
+
+ \sa Qt::CheckState
+*/
+
+/*!
+ \fn void QTreeWidgetItem::setCheckState(int column, Qt::CheckState state)
+
+ Sets the item in the given \a column check state to be \a state.
+
+ \sa checkState()
+*/
+
+/*!
+ \fn QSize QTreeWidgetItem::sizeHint(int column) const
+ \since 4.1
+
+ Returns the size hint set for the tree item in the given
+ \a column (see \l{QSize}).
+*/
+
+/*!
+ \fn void QTreeWidgetItem::setSizeHint(int column, const QSize &size)
+ \since 4.1
+
+ Sets the size hint for the tree item in the given \a column to be \a size.
+ If no size hint is set, the item delegate will compute the size hint based
+ on the item data.
+*/
+
+/*!
+ \fn QTreeWidgetItem *QTreeWidgetItem::parent() const
+
+ Returns the item's parent.
+
+ \sa child()
+*/
+
+/*!
+ \fn QTreeWidgetItem *QTreeWidgetItem::child(int index) const
+
+ Returns the item at the given \a index in the list of the item's children.
+
+ \sa parent()
+*/
+
+/*!
+ \fn int QTreeWidgetItem::childCount() const
+
+ Returns the number of child items.
+*/
+
+/*!
+ \fn int QTreeWidgetItem::columnCount() const
+
+ Returns the number of columns in the item.
+*/
+
+/*!
+ \fn int QTreeWidgetItem::textAlignment(int column) const
+
+ Returns the text alignment for the label in the given \a column
+ (see \l{Qt::AlignmentFlag}).
+*/
+
+/*!
+ \fn void QTreeWidgetItem::setTextAlignment(int column, int alignment)
+
+ Sets the text alignment for the label in the given \a column to
+ the \a alignment specified (see \l{Qt::AlignmentFlag}).
+*/
+
+/*!
+ \fn int QTreeWidgetItem::indexOfChild(QTreeWidgetItem *child) const
+
+ Returns the index of the given \a child in the item's list of children.
+*/
+
+/*!
+ Constructs a tree widget item of the specified \a type. The item
+ must be inserted into a tree widget.
+
+ \sa type()
+*/
+QTreeWidgetItem::QTreeWidgetItem(int type)
+ : rtti(type), view(0), d(new QTreeWidgetItemPrivate(this)), par(0),
+ itemFlags(Qt::ItemIsSelectable
+ |Qt::ItemIsUserCheckable
+ |Qt::ItemIsEnabled
+ |Qt::ItemIsDragEnabled
+ |Qt::ItemIsDropEnabled)
+{
+}
+
+
+/*!
+ Constructs a tree widget item of the specified \a type. The item
+ must be inserted into a tree widget.
+ The given list of \a strings will be set as the item text for each
+ column in the item.
+
+ \sa type()
+*/
+QTreeWidgetItem::QTreeWidgetItem(const QStringList &strings, int type)
+ : rtti(type), view(0), d(new QTreeWidgetItemPrivate(this)), par(0),
+ itemFlags(Qt::ItemIsSelectable
+ |Qt::ItemIsUserCheckable
+ |Qt::ItemIsEnabled
+ |Qt::ItemIsDragEnabled
+ |Qt::ItemIsDropEnabled)
+{
+ for (int i = 0; i < strings.count(); ++i)
+ setText(i, strings.at(i));
+}
+
+/*!
+ \fn QTreeWidgetItem::QTreeWidgetItem(QTreeWidget *parent, int type)
+
+ Constructs a tree widget item of the specified \a type and appends it
+ to the items in the given \a parent.
+
+ \sa type()
+*/
+
+QTreeWidgetItem::QTreeWidgetItem(QTreeWidget *view, int type)
+ : rtti(type), view(0), d(new QTreeWidgetItemPrivate(this)), par(0),
+ itemFlags(Qt::ItemIsSelectable
+ |Qt::ItemIsUserCheckable
+ |Qt::ItemIsEnabled
+ |Qt::ItemIsDragEnabled
+ |Qt::ItemIsDropEnabled)
+{
+ if (view && view->model()) {
+ QTreeModel *model = qobject_cast<QTreeModel*>(view->model());
+ model->rootItem->addChild(this);
+ values.reserve(model->headerItem->columnCount());
+ }
+}
+
+/*!
+ \fn QTreeWidgetItem::QTreeWidgetItem(QTreeWidget *parent, const QStringList &strings, int type)
+
+ Constructs a tree widget item of the specified \a type and appends it
+ to the items in the given \a parent. The given list of \a strings will be set as
+ the item text for each column in the item.
+
+ \sa type()
+*/
+
+QTreeWidgetItem::QTreeWidgetItem(QTreeWidget *view, const QStringList &strings, int type)
+ : rtti(type), view(0), d(new QTreeWidgetItemPrivate(this)), par(0),
+ itemFlags(Qt::ItemIsSelectable
+ |Qt::ItemIsUserCheckable
+ |Qt::ItemIsEnabled
+ |Qt::ItemIsDragEnabled
+ |Qt::ItemIsDropEnabled)
+{
+ for (int i = 0; i < strings.count(); ++i)
+ setText(i, strings.at(i));
+ if (view && view->model()) {
+ QTreeModel *model = qobject_cast<QTreeModel*>(view->model());
+ model->rootItem->addChild(this);
+ values.reserve(model->headerItem->columnCount());
+ }
+}
+
+/*!
+ \fn QTreeWidgetItem::QTreeWidgetItem(QTreeWidget *parent, QTreeWidgetItem *preceding, int type)
+
+ Constructs a tree widget item of the specified \a type and inserts it into
+ the given \a parent after the \a preceding item.
+
+ \sa type()
+*/
+QTreeWidgetItem::QTreeWidgetItem(QTreeWidget *view, QTreeWidgetItem *after, int type)
+ : rtti(type), view(0), d(new QTreeWidgetItemPrivate(this)), par(0),
+ itemFlags(Qt::ItemIsSelectable
+ |Qt::ItemIsUserCheckable
+ |Qt::ItemIsEnabled
+ |Qt::ItemIsDragEnabled
+ |Qt::ItemIsDropEnabled)
+{
+ if (view) {
+ QTreeModel *model = qobject_cast<QTreeModel*>(view->model());
+ if (model) {
+ int i = model->rootItem->children.indexOf(after) + 1;
+ model->rootItem->insertChild(i, this);
+ values.reserve(model->headerItem->columnCount());
+ }
+ }
+}
+
+/*!
+ Constructs a tree widget item and append it to the given \a parent.
+
+ \sa type()
+*/
+QTreeWidgetItem::QTreeWidgetItem(QTreeWidgetItem *parent, int type)
+ : rtti(type), view(0), d(new QTreeWidgetItemPrivate(this)), par(0),
+ itemFlags(Qt::ItemIsSelectable
+ |Qt::ItemIsUserCheckable
+ |Qt::ItemIsEnabled
+ |Qt::ItemIsDragEnabled
+ |Qt::ItemIsDropEnabled)
+{
+ if (parent)
+ parent->addChild(this);
+}
+
+/*!
+ Constructs a tree widget item and append it to the given \a parent.
+ The given list of \a strings will be set as the item text for each column in the item.
+
+ \sa type()
+*/
+QTreeWidgetItem::QTreeWidgetItem(QTreeWidgetItem *parent, const QStringList &strings, int type)
+ : rtti(type), view(0), d(new QTreeWidgetItemPrivate(this)), par(0),
+ itemFlags(Qt::ItemIsSelectable
+ |Qt::ItemIsUserCheckable
+ |Qt::ItemIsEnabled
+ |Qt::ItemIsDragEnabled
+ |Qt::ItemIsDropEnabled)
+{
+ for (int i = 0; i < strings.count(); ++i)
+ setText(i, strings.at(i));
+ if (parent)
+ parent->addChild(this);
+}
+
+/*!
+ \fn QTreeWidgetItem::QTreeWidgetItem(QTreeWidgetItem *parent, QTreeWidgetItem *preceding, int type)
+
+ Constructs a tree widget item of the specified \a type that is inserted
+ into the \a parent after the \a preceding child item.
+
+ \sa type()
+*/
+QTreeWidgetItem::QTreeWidgetItem(QTreeWidgetItem *parent, QTreeWidgetItem *after, int type)
+ : rtti(type), view(0), d(new QTreeWidgetItemPrivate(this)), par(0),
+ itemFlags(Qt::ItemIsSelectable
+ |Qt::ItemIsUserCheckable
+ |Qt::ItemIsEnabled
+ |Qt::ItemIsDragEnabled
+ |Qt::ItemIsDropEnabled)
+{
+ if (parent) {
+ int i = parent->children.indexOf(after) + 1;
+ parent->insertChild(i, this);
+ }
+}
+
+/*!
+ Destroys this tree widget item.
+*/
+
+QTreeWidgetItem::~QTreeWidgetItem()
+{
+ QTreeModel *model = (view ? qobject_cast<QTreeModel*>(view->model()) : 0);
+ bool wasSkipSort = false;
+ if (model) {
+ wasSkipSort = model->skipPendingSort;
+ model->skipPendingSort = true;
+ }
+ if (par) {
+ int i = par->children.indexOf(this);
+ if (i >= 0) {
+ if (model) model->beginRemoveItems(par, i, 1);
+ // users _could_ do changes when connected to rowsAboutToBeRemoved,
+ // so we check again to make sure 'i' is valid
+ if (!par->children.isEmpty() && par->children.at(i) == this)
+ par->children.takeAt(i);
+ if (model) model->endRemoveItems();
+ }
+ } else if (model) {
+ if (this == model->headerItem) {
+ model->headerItem = 0;
+ } else {
+ int i = model->rootItem->children.indexOf(this);
+ if (i >= 0) {
+ model->beginRemoveItems(0, i, 1);
+ // users _could_ do changes when connected to rowsAboutToBeRemoved,
+ // so we check again to make sure 'i' is valid
+ if (!model->rootItem->children.isEmpty() && model->rootItem->children.at(i) == this)
+ model->rootItem->children.takeAt(i);
+ model->endRemoveItems();
+ }
+ }
+ }
+ // at this point the persistent indexes for the children should also be invalidated
+ // since we invalidated the parent
+ for (int i = 0; i < children.count(); ++i) {
+ QTreeWidgetItem *child = children.at(i);
+ // make sure the child does not try to remove itself from our children list
+ child->par = 0;
+ // make sure the child does not try to remove itself from the top level list
+ child->view = 0;
+ delete child;
+ }
+
+ children.clear();
+ delete d;
+ if (model) {
+ model->skipPendingSort = wasSkipSort;
+ }
+}
+
+/*!
+ Creates a deep copy of the item and of its children.
+*/
+QTreeWidgetItem *QTreeWidgetItem::clone() const
+{
+ QTreeWidgetItem *copy = 0;
+
+ QStack<const QTreeWidgetItem*> stack;
+ QStack<QTreeWidgetItem*> parentStack;
+ stack.push(this);
+ parentStack.push(0);
+
+ QTreeWidgetItem *root = 0;
+ const QTreeWidgetItem *item = 0;
+ QTreeWidgetItem *parent = 0;
+ while (!stack.isEmpty()) {
+ // get current item, and copied parent
+ item = stack.pop();
+ parent = parentStack.pop();
+
+ // copy item
+ copy = new QTreeWidgetItem(*item);
+ if (!root)
+ root = copy;
+
+ // set parent and add to parents children list
+ if (parent) {
+ copy->par = parent;
+ parent->children.insert(0, copy);
+ }
+
+ for (int i = 0; i < item->childCount(); ++i) {
+ stack.push(item->child(i));
+ parentStack.push(copy);
+ }
+ }
+ return root;
+}
+
+/*!
+ Sets the item indicator \a policy. This policy decides when the
+ tree branch expand/collapse indicator is shown.
+ The default value is ShowForChildren.
+
+ \sa childIndicatorPolicy()
+*/
+void QTreeWidgetItem::setChildIndicatorPolicy(QTreeWidgetItem::ChildIndicatorPolicy policy)
+{
+ if (d->policy == policy)
+ return;
+ d->policy = policy;
+
+ if (!view)
+ return;
+
+ view->viewport()->update( view->d_func()->itemDecorationRect(view->d_func()->index(this)));
+}
+
+/*!
+ Returns the item indicator policy. This policy decides when the
+ tree branch expand/collapse indicator is shown.
+
+ \sa setChildIndicatorPolicy()
+*/
+QTreeWidgetItem::ChildIndicatorPolicy QTreeWidgetItem::childIndicatorPolicy() const
+{
+ return d->policy;
+}
+
+/*!
+ \fn void QTreeWidgetItem::setFlags(Qt::ItemFlags flags)
+
+ Sets the flags for the item to the given \a flags. These determine whether
+ the item can be selected or modified. This is often used to disable an item.
+
+ \sa flags()
+*/
+void QTreeWidgetItem::setFlags(Qt::ItemFlags flags)
+{
+ const bool enable = (flags & Qt::ItemIsEnabled);
+ const bool changedState = bool(itemFlags & Qt::ItemIsEnabled) != enable;
+ const bool changedExplicit = d->disabled != !enable;
+
+ d->disabled = !enable;
+
+ if (enable && par && !(par->itemFlags & Qt::ItemIsEnabled)) // inherit from parent
+ itemFlags = flags & ~Qt::ItemIsEnabled;
+ else // this item is explicitly disabled or has no parent
+ itemFlags = flags;
+
+ if (changedState && changedExplicit) { // if the propagate the change to the children
+ QStack<QTreeWidgetItem*> parents;
+ parents.push(this);
+ while (!parents.isEmpty()) {
+ QTreeWidgetItem *parent = parents.pop();
+ for (int i = 0; i < parent->children.count(); ++i) {
+ QTreeWidgetItem *child = parent->children.at(i);
+ if (!child->d->disabled) { // if not explicitly disabled
+ parents.push(child);
+ if (enable)
+ child->itemFlags = child->itemFlags | Qt::ItemIsEnabled;
+ else
+ child->itemFlags = child->itemFlags & ~Qt::ItemIsEnabled;
+ child->itemChanged(); // ### we may want to optimize this
+ }
+ }
+ }
+ }
+ itemChanged();
+}
+
+void QTreeWidgetItemPrivate::propagateDisabled(QTreeWidgetItem *item)
+{
+ Q_ASSERT(item);
+ const bool enable = item->par ? (item->par->itemFlags.testFlag(Qt::ItemIsEnabled)) : true;
+
+ QStack<QTreeWidgetItem*> parents;
+ parents.push(item);
+ while (!parents.isEmpty()) {
+ QTreeWidgetItem *parent = parents.pop();
+ if (!parent->d->disabled) { // if not explicitly disabled
+ Qt::ItemFlags oldFlags = parent->itemFlags;
+ if (enable)
+ parent->itemFlags = parent->itemFlags | Qt::ItemIsEnabled;
+ else
+ parent->itemFlags = parent->itemFlags & ~Qt::ItemIsEnabled;
+ if (parent->itemFlags != oldFlags)
+ parent->itemChanged();
+ }
+
+ for (int i = 0; i < parent->children.count(); ++i) {
+ QTreeWidgetItem *child = parent->children.at(i);
+ parents.push(child);
+ }
+ }
+}
+/*!
+ \fn Qt::ItemFlags QTreeWidgetItem::flags() const
+
+ Returns the flags used to describe the item. These determine whether
+ the item can be checked, edited, and selected.
+
+ The default value for flags is
+ Qt::ItemIsSelectable | Qt::ItemIsUserCheckable | Qt::ItemIsEnabled | Qt::ItemIsDragEnabled.
+ If the item was constructed with a parent, flags will in addition contain Qt::ItemIsDropEnabled.
+
+ \sa setFlags()
+*/
+Qt::ItemFlags QTreeWidgetItem::flags() const
+{
+ return itemFlags;
+}
+
+/*!
+ Sets the value for the item's \a column and \a role to the given
+ \a value.
+
+ The \a role describes the type of data specified by \a value, and is defined by
+ the Qt::ItemDataRole enum.
+*/
+void QTreeWidgetItem::setData(int column, int role, const QVariant &value)
+{
+ if (column < 0)
+ return;
+
+ QTreeModel *model = (view ? qobject_cast<QTreeModel*>(view->model()) : 0);
+ switch (role) {
+ case Qt::EditRole:
+ case Qt::DisplayRole: {
+ if (values.count() <= column) {
+ if (model && this == model->headerItem)
+ model->setColumnCount(column + 1);
+ else
+ values.resize(column + 1);
+ }
+ if (d->display.count() <= column) {
+ for (int i = d->display.count() - 1; i < column - 1; ++i)
+ d->display.append(QVariant());
+ d->display.append(value);
+ } else if (d->display[column] != value) {
+ d->display[column] = value;
+ } else {
+ return; // value is unchanged
+ }
+ } break;
+ case Qt::CheckStateRole:
+ if (itemFlags & Qt::ItemIsTristate) {
+ for (int i = 0; i < children.count(); ++i) {
+ QTreeWidgetItem *child = children.at(i);
+ if (child->data(column, role).isValid()) {// has a CheckState
+ Qt::ItemFlags f = itemFlags; // a little hack to avoid multiple dataChanged signals
+ itemFlags &= ~Qt::ItemIsTristate;
+ child->setData(column, role, value);
+ itemFlags = f;
+ }
+ }
+ }
+ // Don't break, but fall through
+ default:
+ if (column < values.count()) {
+ bool found = false;
+ QVector<QWidgetItemData> column_values = values.at(column);
+ for (int i = 0; i < column_values.count(); ++i) {
+ if (column_values.at(i).role == role) {
+ if (column_values.at(i).value == value)
+ return; // value is unchanged
+ values[column][i].value = value;
+ found = true;
+ break;
+ }
+ }
+ if (!found)
+ values[column].append(QWidgetItemData(role, value));
+ } else {
+ if (model && this == model->headerItem)
+ model->setColumnCount(column + 1);
+ else
+ values.resize(column + 1);
+ values[column].append(QWidgetItemData(role, value));
+ }
+ }
+
+ if (model) {
+ model->emitDataChanged(this, column);
+ if (role == Qt::CheckStateRole) {
+ QTreeWidgetItem *p;
+ for (p = par; p && (p->itemFlags & Qt::ItemIsTristate); p = p->par)
+ model->emitDataChanged(p, column);
+ }
+ }
+}
+
+/*!
+ Returns the value for the item's \a column and \a role.
+*/
+QVariant QTreeWidgetItem::data(int column, int role) const
+{
+ switch (role) {
+ case Qt::EditRole:
+ case Qt::DisplayRole:
+ if (column >= 0 && column < d->display.count())
+ return d->display.at(column);
+ break;
+ case Qt::CheckStateRole:
+ // special case for check state in tristate
+ if (children.count() && (itemFlags & Qt::ItemIsTristate))
+ return childrenCheckState(column);
+ default:
+ if (column >= 0 && column < values.size()) {
+ const QVector<QWidgetItemData> &column_values = values.at(column);
+ for (int i = 0; i < column_values.count(); ++i)
+ if (column_values.at(i).role == role)
+ return column_values.at(i).value;
+ }
+ }
+ return QVariant();
+}
+
+/*!
+ Returns true if the text in the item is less than the text in the
+ \a other item, otherwise returns false.
+*/
+
+bool QTreeWidgetItem::operator<(const QTreeWidgetItem &other) const
+{
+ int column = view ? view->sortColumn() : 0;
+ return text(column) < other.text(column);
+}
+
+#ifndef QT_NO_DATASTREAM
+
+/*!
+ Reads the item from stream \a in. This only reads data into a single item.
+
+ \sa write()
+*/
+void QTreeWidgetItem::read(QDataStream &in)
+{
+ // convert from streams written before we introduced display (4.2.0)
+ if (in.version() < QDataStream::Qt_4_2) {
+ d->display.clear();
+ in >> values;
+ // move the display value over to the display string list
+ for (int column = 0; column < values.count(); ++column) {
+ d->display << QVariant();
+ for (int i = 0; i < values.at(column).count(); ++i) {
+ if (values.at(column).at(i).role == Qt::DisplayRole) {
+ d->display[column] = values.at(column).at(i).value;
+ values[column].remove(i--);
+ }
+ }
+ }
+ } else {
+ in >> values >> d->display;
+ }
+}
+
+/*!
+ Writes the item to stream \a out. This only writes data from one single item.
+
+ \sa read()
+*/
+void QTreeWidgetItem::write(QDataStream &out) const
+{
+ out << values << d->display;
+}
+
+/*!
+ \since 4.1
+
+ Constructs a copy of \a other. Note that type() and treeWidget()
+ are not copied.
+
+ This function is useful when reimplementing clone().
+
+ \sa data(), flags()
+*/
+QTreeWidgetItem::QTreeWidgetItem(const QTreeWidgetItem &other)
+ : rtti(Type), values(other.values), view(0),
+ d(new QTreeWidgetItemPrivate(this)), par(0),
+ itemFlags(other.itemFlags)
+{
+ d->display = other.d->display;
+}
+
+/*!
+ Assigns \a other's data and flags to this item. Note that type()
+ and treeWidget() are not copied.
+
+ This function is useful when reimplementing clone().
+
+ \sa data(), flags()
+*/
+QTreeWidgetItem &QTreeWidgetItem::operator=(const QTreeWidgetItem &other)
+{
+ values = other.values;
+ d->display = other.d->display;
+ d->policy = other.d->policy;
+ itemFlags = other.itemFlags;
+ return *this;
+}
+
+#endif // QT_NO_DATASTREAM
+
+/*!
+ Appends the \a child item to the list of children.
+
+ \sa insertChild() takeChild()
+*/
+void QTreeWidgetItem::addChild(QTreeWidgetItem *child)
+{
+ if (child) {
+ insertChild(children.count(), child);
+ child->d->rowGuess = children.count() - 1;
+ }
+}
+
+/*!
+ Inserts the \a child item at \a index in the list of children.
+
+ If the child has already been inserted somewhere else it wont be inserted again.
+*/
+void QTreeWidgetItem::insertChild(int index, QTreeWidgetItem *child)
+{
+ if (index < 0 || index > children.count() || child == 0 || child->view != 0 || child->par != 0)
+ return;
+
+ if (QTreeModel *model = (view ? qobject_cast<QTreeModel*>(view->model()) : 0)) {
+ const bool wasSkipSort = model->skipPendingSort;
+ model->skipPendingSort = true;
+ if (model->rootItem == this)
+ child->par = 0;
+ else
+ child->par = this;
+ if (view->isSortingEnabled()) {
+ // do a delayed sort instead
+ if (!model->sortPendingTimer.isActive())
+ model->sortPendingTimer.start(0, model);
+ }
+ model->beginInsertItems(this, index, 1);
+ int cols = model->columnCount();
+ QStack<QTreeWidgetItem*> stack;
+ stack.push(child);
+ while (!stack.isEmpty()) {
+ QTreeWidgetItem *i = stack.pop();
+ i->view = view;
+ i->values.reserve(cols);
+ for (int c = 0; c < i->children.count(); ++c)
+ stack.push(i->children.at(c));
+ }
+ children.insert(index, child);
+ model->endInsertItems();
+ model->skipPendingSort = wasSkipSort;
+ } else {
+ child->par = this;
+ children.insert(index, child);
+ }
+ if (child->par)
+ d->propagateDisabled(child);
+}
+
+/*!
+ Removes the given item indicated by \a child.
+ The removed item will not be deleted.
+*/
+void QTreeWidgetItem::removeChild(QTreeWidgetItem *child)
+{
+ (void)takeChild(children.indexOf(child));
+}
+
+/*!
+ Removes the item at \a index and returns it, otherwise return 0.
+*/
+QTreeWidgetItem *QTreeWidgetItem::takeChild(int index)
+{
+ // we move this outside the check of the index to allow executing
+ // pending sorts from inline functions, using this function (hack)
+ QTreeModel *model = (view ? qobject_cast<QTreeModel*>(view->model()) : 0);
+ if (model) {
+ // This will trigger a layoutChanged signal, thus we might want to optimize
+ // this function by not emitting the rowsRemoved signal etc to the view.
+ // On the other hand we also need to make sure that the selectionmodel
+ // is updated in case we take an item that is selected.
+ model->skipPendingSort = false;
+ model->executePendingSort();
+ }
+ if (index >= 0 && index < children.count()) {
+ if (model) model->beginRemoveItems(this, index, 1);
+ QTreeWidgetItem *item = children.takeAt(index);
+ item->par = 0;
+ QStack<QTreeWidgetItem*> stack;
+ stack.push(item);
+ while (!stack.isEmpty()) {
+ QTreeWidgetItem *i = stack.pop();
+ i->view = 0;
+ for (int c = 0; c < i->children.count(); ++c)
+ stack.push(i->children.at(c));
+ }
+ d->propagateDisabled(item);
+ if (model) model->endRemoveRows();
+ return item;
+ }
+ return 0;
+}
+
+/*!
+ \since 4.1
+
+ Appends the given list of \a children to the item.
+
+ \sa insertChildren() takeChildren()
+*/
+void QTreeWidgetItem::addChildren(const QList<QTreeWidgetItem*> &children)
+{
+ insertChildren(this->children.count(), children);
+}
+
+/*!
+ \since 4.1
+
+ Inserts the given list of \a children into the list of the item children at \a index .
+
+ Children that have already been inserted somewhere else wont be inserted.
+*/
+void QTreeWidgetItem::insertChildren(int index, const QList<QTreeWidgetItem*> &children)
+{
+ if (view && view->isSortingEnabled()) {
+ for (int n = 0; n < children.count(); ++n)
+ insertChild(index, children.at(n));
+ return;
+ }
+ QTreeModel *model = (view ? qobject_cast<QTreeModel*>(view->model()) : 0);
+ QStack<QTreeWidgetItem*> stack;
+ QList<QTreeWidgetItem*> itemsToInsert;
+ for (int n = 0; n < children.count(); ++n) {
+ QTreeWidgetItem *child = children.at(n);
+ if (child->view || child->par)
+ continue;
+ itemsToInsert.append(child);
+ if (view && model) {
+ if (child->childCount() == 0)
+ child->view = view;
+ else
+ stack.push(child);
+ }
+ if (model && (model->rootItem == this))
+ child->par = 0;
+ else
+ child->par = this;
+ }
+ if (!itemsToInsert.isEmpty()) {
+ while (!stack.isEmpty()) {
+ QTreeWidgetItem *i = stack.pop();
+ i->view = view;
+ for (int c = 0; c < i->children.count(); ++c)
+ stack.push(i->children.at(c));
+ }
+ if (model) model->beginInsertItems(this, index, itemsToInsert.count());
+ for (int n = 0; n < itemsToInsert.count(); ++n) {
+ QTreeWidgetItem *child = itemsToInsert.at(n);
+ this->children.insert(index + n, child);
+ if (child->par)
+ d->propagateDisabled(child);
+ }
+ if (model) model->endInsertItems();
+ }
+}
+
+/*!
+ \since 4.1
+
+ Removes the list of children and returns it, otherwise returns an empty list.
+*/
+QList<QTreeWidgetItem*> QTreeWidgetItem::takeChildren()
+{
+ QList<QTreeWidgetItem*> removed;
+ if (children.count() > 0) {
+ QTreeModel *model = (view ? qobject_cast<QTreeModel*>(view->model()) : 0);
+ if (model) {
+ // This will trigger a layoutChanged signal, thus we might want to optimize
+ // this function by not emitting the rowsRemoved signal etc to the view.
+ // On the other hand we also need to make sure that the selectionmodel
+ // is updated in case we take an item that is selected.
+ model->executePendingSort();
+ }
+ if (model) model->beginRemoveItems(this, 0, children.count());
+ for (int n = 0; n < children.count(); ++n) {
+ QTreeWidgetItem *item = children.at(n);
+ item->par = 0;
+ QStack<QTreeWidgetItem*> stack;
+ stack.push(item);
+ while (!stack.isEmpty()) {
+ QTreeWidgetItem *i = stack.pop();
+ i->view = 0;
+ for (int c = 0; c < i->children.count(); ++c)
+ stack.push(i->children.at(c));
+ }
+ d->propagateDisabled(item);
+ }
+ removed = children;
+ children.clear(); // detach
+ if (model) model->endRemoveItems();
+ }
+ return removed;
+}
+
+
+void QTreeWidgetItemPrivate::sortChildren(int column, Qt::SortOrder order, bool climb)
+{
+ QTreeModel *model = (q->view ? qobject_cast<QTreeModel*>(q->view->model()) : 0);
+ model->sortItems(&q->children, column, order);
+ if (climb) {
+ QList<QTreeWidgetItem*>::iterator it = q->children.begin();
+ for (; it != q->children.end(); ++it) {
+ //here we call the private object's method to avoid emitting
+ //the layoutAboutToBeChanged and layoutChanged signals
+ (*it)->d->sortChildren(column, order, climb);
+ }
+ }
+}
+
+/*!
+ \internal
+
+ Sorts the children by the value in the given \a column, in the \a order
+ specified. If \a climb is true, the items below each of the children will
+ also be sorted.
+*/
+void QTreeWidgetItem::sortChildren(int column, Qt::SortOrder order, bool climb)
+{
+ QTreeModel *model = (view ? qobject_cast<QTreeModel*>(view->model()) : 0);
+ if (!model)
+ return;
+ if (model->isChanging())
+ return;
+ int oldSortColumn = view->d_func()->explicitSortColumn;
+ view->d_func()->explicitSortColumn = column;
+ emit model->layoutAboutToBeChanged();
+ d->sortChildren(column, order, climb);
+ emit model->layoutChanged();
+ view->d_func()->explicitSortColumn = oldSortColumn;
+}
+
+/*!
+ \internal
+
+ Calculates the checked state of the item based on the checked state
+ of its children. E.g. if all children checked => this item is also
+ checked; if some children checked => this item is partially checked;
+ if no children checked => this item is unchecked.
+*/
+QVariant QTreeWidgetItem::childrenCheckState(int column) const
+{
+ if (column < 0)
+ return QVariant();
+ bool checkedChildren = false;
+ bool uncheckedChildren = false;
+ for (int i = 0; i < children.count(); ++i) {
+ QVariant value = children.at(i)->data(column, Qt::CheckStateRole);
+ if (!value.isValid())
+ return QVariant();
+
+ switch (static_cast<Qt::CheckState>(value.toInt()))
+ {
+ case Qt::Unchecked:
+ uncheckedChildren = true;
+ break;
+ case Qt::Checked:
+ checkedChildren = true;
+ break;
+ case Qt::PartiallyChecked:
+ default:
+ return Qt::PartiallyChecked;
+ }
+ }
+
+ if (uncheckedChildren && checkedChildren)
+ return Qt::PartiallyChecked;
+ if (uncheckedChildren)
+ return Qt::Unchecked;
+ else if (checkedChildren)
+ return Qt::Checked;
+ else
+ return QVariant(); // value was not defined
+}
+
+/*!
+ \since 4.5
+
+ Causes the model associated with this item to emit a
+ \l{QAbstractItemModel::dataChanged()}{dataChanged}() signal for this
+ item.
+
+ You normally only need to call this function if you have subclassed
+ QTreeWidgetItem and reimplemented data() and/or setData().
+
+ \sa setData()
+*/
+void QTreeWidgetItem::emitDataChanged()
+{
+ itemChanged();
+}
+
+/*!
+ \internal
+*/
+void QTreeWidgetItem::itemChanged()
+{
+ if (QTreeModel *model = (view ? qobject_cast<QTreeModel*>(view->model()) : 0))
+ model->itemChanged(this);
+}
+
+/*!
+ \internal
+*/
+void QTreeWidgetItem::executePendingSort() const
+{
+ if (QTreeModel *model = (view ? qobject_cast<QTreeModel*>(view->model()) : 0))
+ model->executePendingSort();
+}
+
+
+#ifndef QT_NO_DATASTREAM
+/*!
+ \relates QTreeWidgetItem
+
+ Writes the tree widget item \a item to stream \a out.
+
+ This operator uses QTreeWidgetItem::write().
+
+ \sa {Format of the QDataStream Operators}
+*/
+QDataStream &operator<<(QDataStream &out, const QTreeWidgetItem &item)
+{
+ item.write(out);
+ return out;
+}
+
+/*!
+ \relates QTreeWidgetItem
+
+ Reads a tree widget item from stream \a in into \a item.
+
+ This operator uses QTreeWidgetItem::read().
+
+ \sa {Format of the QDataStream Operators}
+*/
+QDataStream &operator>>(QDataStream &in, QTreeWidgetItem &item)
+{
+ item.read(in);
+ return in;
+}
+#endif // QT_NO_DATASTREAM
+
+
+void QTreeWidgetPrivate::_q_emitItemPressed(const QModelIndex &index)
+{
+ Q_Q(QTreeWidget);
+ emit q->itemPressed(item(index), index.column());
+}
+
+void QTreeWidgetPrivate::_q_emitItemClicked(const QModelIndex &index)
+{
+ Q_Q(QTreeWidget);
+ emit q->itemClicked(item(index), index.column());
+}
+
+void QTreeWidgetPrivate::_q_emitItemDoubleClicked(const QModelIndex &index)
+{
+ Q_Q(QTreeWidget);
+ emit q->itemDoubleClicked(item(index), index.column());
+}
+
+void QTreeWidgetPrivate::_q_emitItemActivated(const QModelIndex &index)
+{
+ Q_Q(QTreeWidget);
+ emit q->itemActivated(item(index), index.column());
+}
+
+void QTreeWidgetPrivate::_q_emitItemEntered(const QModelIndex &index)
+{
+ Q_Q(QTreeWidget);
+ emit q->itemEntered(item(index), index.column());
+}
+
+void QTreeWidgetPrivate::_q_emitItemChanged(const QModelIndex &index)
+{
+ Q_Q(QTreeWidget);
+ QTreeWidgetItem *indexItem = item(index);
+ if (indexItem)
+ emit q->itemChanged(indexItem, index.column());
+}
+
+void QTreeWidgetPrivate::_q_emitItemExpanded(const QModelIndex &index)
+{
+ Q_Q(QTreeWidget);
+ emit q->itemExpanded(item(index));
+}
+
+void QTreeWidgetPrivate::_q_emitItemCollapsed(const QModelIndex &index)
+{
+ Q_Q(QTreeWidget);
+ emit q->itemCollapsed(item(index));
+}
+
+void QTreeWidgetPrivate::_q_emitCurrentItemChanged(const QModelIndex &current,
+ const QModelIndex &previous)
+{
+ Q_Q(QTreeWidget);
+ QTreeWidgetItem *currentItem = item(current);
+ QTreeWidgetItem *previousItem = item(previous);
+ emit q->currentItemChanged(currentItem, previousItem);
+}
+
+void QTreeWidgetPrivate::_q_sort()
+{
+ Q_Q(QTreeWidget);
+ if (sortingEnabled) {
+ int column = q->header()->sortIndicatorSection();
+ Qt::SortOrder order = q->header()->sortIndicatorOrder();
+ model()->sort(column, order);
+ }
+}
+
+void QTreeWidgetPrivate::_q_selectionChanged(const QItemSelection &selected, const QItemSelection &deselected)
+{
+ Q_Q(QTreeWidget);
+ QModelIndexList indices = selected.indexes();
+ int i;
+ QTreeModel *m = model();
+ for (i = 0; i < indices.count(); ++i) {
+ QTreeWidgetItem *item = m->item(indices.at(i));
+ item->d->selected = true;
+ }
+
+ indices = deselected.indexes();
+ for (i = 0; i < indices.count(); ++i) {
+ QTreeWidgetItem *item = m->item(indices.at(i));
+ item->d->selected = false;
+ }
+
+ emit q->itemSelectionChanged();
+}
+
+void QTreeWidgetPrivate::_q_dataChanged(const QModelIndex &topLeft,
+ const QModelIndex &bottomRight)
+{
+ Q_Q(QTreeWidget);
+ if (sortingEnabled && topLeft.isValid() && bottomRight.isValid()
+ && !model()->sortPendingTimer.isActive()) {
+ int column = q->header()->sortIndicatorSection();
+ if (column >= topLeft.column() && column <= bottomRight.column()) {
+ Qt::SortOrder order = q->header()->sortIndicatorOrder();
+ model()->ensureSorted(column, order, topLeft.row(),
+ bottomRight.row(), topLeft.parent());
+ }
+ }
+}
+
+/*!
+ \class QTreeWidget
+
+ \brief The QTreeWidget class provides a tree view that uses a predefined
+ tree model.
+
+ \ingroup model-view
+ \mainclass
+
+ The QTreeWidget class is a convenience class that provides a standard
+ tree widget with a classic item-based interface similar to that used by
+ the QListView class in Qt 3.
+ This class is based on Qt's Model/View architecture and uses a default
+ model to hold items, each of which is a QTreeWidgetItem.
+
+ Developers who do not need the flexibility of the Model/View framework
+ can use this class to create simple hierarchical lists very easily. A more
+ flexible approach involves combining a QTreeView with a standard item model.
+ This allows the storage of data to be separated from its representation.
+
+ In its simplest form, a tree widget can be constructed in the following way:
+
+ \snippet doc/src/snippets/code/src_gui_itemviews_qtreewidget.cpp 0
+
+ Before items can be added to the tree widget, the number of columns must
+ be set with setColumnCount(). This allows each item to have one or more
+ labels or other decorations. The number of columns in use can be found
+ with the columnCount() function.
+
+ The tree can have a header that contains a section for each column in
+ the widget. It is easiest to set up the labels for each section by
+ supplying a list of strings with setHeaderLabels(), but a custom header
+ can be constructed with a QTreeWidgetItem and inserted into the tree
+ with the setHeaderItem() function.
+
+ The items in the tree can be sorted by column according to a predefined
+ sort order. If sorting is enabled, the user can sort the items by clicking
+ on a column header. Sorting can be enabled or disabled by calling
+ \l{QTreeView::setSortingEnabled()}{setSortingEnabled()}. The
+ \l{QTreeView::isSortingEnabled()}{isSortingEnabled()} function indicates
+ whether sorting is enabled.
+
+ \table 100%
+ \row \o \inlineimage windowsxp-treeview.png Screenshot of a Windows XP style tree widget
+ \o \inlineimage macintosh-treeview.png Screenshot of a Macintosh style tree widget
+ \o \inlineimage plastique-treeview.png Screenshot of a Plastique style tree widget
+ \row \o A \l{Windows XP Style Widget Gallery}{Windows XP style} tree widget.
+ \o A \l{Macintosh Style Widget Gallery}{Macintosh style} tree widget.
+ \o A \l{Plastique Style Widget Gallery}{Plastique style} tree widget.
+ \endtable
+
+ \sa QTreeWidgetItem, QTreeWidgetItemIterator, QTreeView,
+ {Model/View Programming}, {Settings Editor Example}
+*/
+
+/*!
+ \property QTreeWidget::columnCount
+ \brief the number of columns displayed in the tree widget
+
+ By default, this property has a value of 1.
+*/
+
+/*!
+ \fn void QTreeWidget::itemActivated(QTreeWidgetItem *item, int column)
+
+ This signal is emitted when the user activates an item by single-
+ or double-clicking (depending on the platform, i.e. on the
+ QStyle::SH_ItemView_ActivateItemOnSingleClick style hint) or
+ pressing a special key (e.g., \key Enter).
+
+ The specified \a item is the item that was clicked, or 0 if no
+ item was clicked. The \a column is the item's column that was
+ clicked, or -1 if no item was clicked.
+*/
+
+/*!
+ \fn void QTreeWidget::itemPressed(QTreeWidgetItem *item, int column)
+
+ This signal is emitted when the user presses a mouse button inside
+ the widget.
+
+ The specified \a item is the item that was clicked, or 0 if no
+ item was clicked. The \a column is the item's column that was
+ clicked, or -1 if no item was clicked.
+*/
+
+/*!
+ \fn void QTreeWidget::itemClicked(QTreeWidgetItem *item, int column)
+
+ This signal is emitted when the user clicks inside the widget.
+
+ The specified \a item is the item that was clicked. The \a column is the
+ item's column that was clicked. If no item was clicked, no signal will be
+ emitted.
+*/
+
+/*!
+ \fn void QTreeWidget::itemDoubleClicked(QTreeWidgetItem *item, int column)
+
+ This signal is emitted when the user double clicks inside the
+ widget.
+
+ The specified \a item is the item that was clicked, or 0 if no
+ item was clicked. The \a column is the item's column that was
+ clicked. If no item was double clicked, no signal will be emitted.
+*/
+
+/*!
+ \fn void QTreeWidget::itemExpanded(QTreeWidgetItem *item)
+
+ This signal is emitted when the specified \a item is expanded so that
+ all of its children are displayed.
+
+ \note This signal will not be emitted if an item changes its state when
+ expandAll() is invoked.
+
+ \sa isItemExpanded(), itemCollapsed(), expandItem()
+*/
+
+/*!
+ \fn void QTreeWidget::itemCollapsed(QTreeWidgetItem *item)
+
+ This signal is emitted when the specified \a item is collapsed so that
+ none of its children are displayed.
+
+ \note This signal will not be emitted if an item changes its state when
+ collapseAll() is invoked.
+
+ \sa isItemExpanded(), itemExpanded(), collapseItem()
+*/
+
+/*!
+ \fn void QTreeWidget::currentItemChanged(QTreeWidgetItem *current, QTreeWidgetItem *previous)
+
+ This signal is emitted when the current item changes. The current
+ item is specified by \a current, and this replaces the \a previous
+ current item.
+
+ \sa setCurrentItem()
+*/
+
+/*!
+ \fn void QTreeWidget::itemSelectionChanged()
+
+ This signal is emitted when the selection changes in the tree widget.
+ The current selection can be found with selectedItems().
+*/
+
+/*!
+ \fn void QTreeWidget::itemEntered(QTreeWidgetItem *item, int column)
+
+ This signal is emitted when the mouse cursor enters an \a item over the
+ specified \a column.
+ QTreeWidget mouse tracking needs to be enabled for this feature to work.
+*/
+
+/*!
+ \fn void QTreeWidget::itemChanged(QTreeWidgetItem *item, int column)
+
+ This signal is emitted when the contents of the \a column in the specified
+ \a item changes.
+*/
+
+/*!
+ \since 4.3
+
+ \fn void QTreeWidget::removeItemWidget(QTreeWidgetItem *item, int column)
+
+ Removes the widget set in the given \a item in the given \a column.
+*/
+
+/*!
+ Constructs a tree widget with the given \a parent.
+*/
+QTreeWidget::QTreeWidget(QWidget *parent)
+ : QTreeView(*new QTreeWidgetPrivate(), parent)
+{
+ QTreeView::setModel(new QTreeModel(1, this));
+ connect(this, SIGNAL(pressed(QModelIndex)),
+ SLOT(_q_emitItemPressed(QModelIndex)));
+ connect(this, SIGNAL(clicked(QModelIndex)),
+ SLOT(_q_emitItemClicked(QModelIndex)));
+ connect(this, SIGNAL(doubleClicked(QModelIndex)),
+ SLOT(_q_emitItemDoubleClicked(QModelIndex)));
+ connect(this, SIGNAL(activated(QModelIndex)),
+ SLOT(_q_emitItemActivated(QModelIndex)));
+ connect(this, SIGNAL(entered(QModelIndex)),
+ SLOT(_q_emitItemEntered(QModelIndex)));
+ connect(this, SIGNAL(expanded(QModelIndex)),
+ SLOT(_q_emitItemExpanded(QModelIndex)));
+ connect(this, SIGNAL(collapsed(QModelIndex)),
+ SLOT(_q_emitItemCollapsed(QModelIndex)));
+ connect(selectionModel(), SIGNAL(currentChanged(QModelIndex,QModelIndex)),
+ this, SLOT(_q_emitCurrentItemChanged(QModelIndex,QModelIndex)));
+ connect(model(), SIGNAL(dataChanged(QModelIndex,QModelIndex)),
+ this, SLOT(_q_emitItemChanged(QModelIndex)));
+ connect(model(), SIGNAL(dataChanged(QModelIndex,QModelIndex)),
+ this, SLOT(_q_dataChanged(QModelIndex,QModelIndex)));
+ connect(model(), SIGNAL(columnsRemoved(QModelIndex,int,int)),
+ this, SLOT(_q_sort()));
+ connect(selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)),
+ this, SLOT(_q_selectionChanged(QItemSelection,QItemSelection)));
+ header()->setClickable(false);
+}
+
+/*!
+ Destroys the tree widget and all its items.
+*/
+
+QTreeWidget::~QTreeWidget()
+{
+}
+
+/*
+ Retuns the number of header columns in the view.
+
+ \sa sortColumn(), currentColumn(), topLevelItemCount()
+*/
+
+int QTreeWidget::columnCount() const
+{
+ Q_D(const QTreeWidget);
+ return d->model()->columnCount();
+}
+
+/*
+ Sets the number of header \a columns in the tree widget.
+*/
+
+void QTreeWidget::setColumnCount(int columns)
+{
+ Q_D(QTreeWidget);
+ if (columns < 0)
+ return;
+ d->model()->setColumnCount(columns);
+}
+
+/*!
+ \since 4.2
+
+ Returns the tree widget's invisible root item.
+
+ The invisible root item provides access to the tree widget's top-level items
+ through the QTreeWidgetItem API, making it possible to write functions that
+ can treat top-level items and their children in a uniform way; for example,
+ recursive functions.
+*/
+
+QTreeWidgetItem *QTreeWidget::invisibleRootItem() const
+{
+ Q_D(const QTreeWidget);
+ return d->model()->rootItem;
+}
+
+/*!
+ Returns the top level item at the given \a index, or 0 if the item does
+ not exist.
+
+ \sa topLevelItemCount(), insertTopLevelItem()
+*/
+
+QTreeWidgetItem *QTreeWidget::topLevelItem(int index) const
+{
+ Q_D(const QTreeWidget);
+ return d->model()->rootItem->child(index);
+}
+
+/*!
+ \property QTreeWidget::topLevelItemCount
+ \brief the number of top-level items
+
+ By default, this property has a value of 0.
+
+ \sa columnCount(), currentItem()
+*/
+
+int QTreeWidget::topLevelItemCount() const
+{
+ Q_D(const QTreeWidget);
+ return d->model()->rootItem->childCount();
+}
+
+/*!
+ Inserts the \a item at \a index in the top level in the view.
+
+ If the item has already been inserted somewhere else it wont be inserted.
+
+ \sa addTopLevelItem(), columnCount()
+*/
+
+void QTreeWidget::insertTopLevelItem(int index, QTreeWidgetItem *item)
+{
+ Q_D(QTreeWidget);
+ d->model()->rootItem->insertChild(index, item);
+}
+
+/*!
+ \since 4.1
+
+ Appends the \a item as a top-level item in the widget.
+
+ \sa insertTopLevelItem()
+*/
+void QTreeWidget::addTopLevelItem(QTreeWidgetItem *item)
+{
+ insertTopLevelItem(topLevelItemCount(), item);
+}
+
+/*!
+ Removes the top-level item at the given \a index in the tree and
+ returns it, otherwise returns 0;
+
+ \sa insertTopLevelItem(), topLevelItem(), topLevelItemCount()
+*/
+
+QTreeWidgetItem *QTreeWidget::takeTopLevelItem(int index)
+{
+ Q_D(QTreeWidget);
+ return d->model()->rootItem->takeChild(index);
+}
+
+/*!
+ \internal
+*/
+int QTreeWidget::indexOfTopLevelItem(QTreeWidgetItem *item)
+{
+ Q_D(QTreeWidget);
+ d->model()->executePendingSort();
+ return d->model()->rootItem->children.indexOf(item);
+}
+
+/*!
+ Returns the index of the given top-level \a item, or -1 if the item
+ cannot be found.
+
+ \sa sortItems(), topLevelItemCount()
+ */
+int QTreeWidget::indexOfTopLevelItem(QTreeWidgetItem *item) const
+{
+ Q_D(const QTreeWidget);
+ d->model()->executePendingSort();
+ return d->model()->rootItem->children.indexOf(item);
+}
+
+/*!
+ \since 4.1
+
+ Inserts the list of \a items at \a index in the top level in the view.
+
+ Items that have already been inserted somewhere else wont be inserted.
+
+ \sa addTopLevelItems()
+*/
+void QTreeWidget::insertTopLevelItems(int index, const QList<QTreeWidgetItem*> &items)
+{
+ Q_D(QTreeWidget);
+ d->model()->rootItem->insertChildren(index, items);
+}
+
+/*!
+ Appends the list of \a items as a top-level items in the widget.
+
+ \sa insertTopLevelItems()
+*/
+void QTreeWidget::addTopLevelItems(const QList<QTreeWidgetItem*> &items)
+{
+ insertTopLevelItems(topLevelItemCount(), items);
+}
+
+/*!
+ Returns the item used for the tree widget's header.
+
+ \sa setHeaderItem()
+*/
+
+QTreeWidgetItem *QTreeWidget::headerItem() const
+{
+ Q_D(const QTreeWidget);
+ return d->model()->headerItem;
+}
+
+/*!
+ Sets the header \a item for the tree widget. The label for each column in
+ the header is supplied by the corresponding label in the item.
+
+ The tree widget takes ownership of the item.
+
+ \sa headerItem(), setHeaderLabels()
+*/
+
+void QTreeWidget::setHeaderItem(QTreeWidgetItem *item)
+{
+ Q_D(QTreeWidget);
+ if (!item)
+ return;
+ item->view = this;
+
+ int oldCount = columnCount();
+ if (oldCount < item->columnCount())
+ d->model()->beginInsertColumns(QModelIndex(), oldCount, item->columnCount());
+ else
+ d->model()->beginRemoveColumns(QModelIndex(), item->columnCount(), oldCount);
+ delete d->model()->headerItem;
+ d->model()->headerItem = item;
+ if (oldCount < item->columnCount())
+ d->model()->endInsertColumns();
+ else
+ d->model()->endRemoveColumns();
+ d->model()->headerDataChanged(Qt::Horizontal, 0, oldCount);
+}
+
+
+/*!
+ Adds a column in the header for each item in the \a labels list, and sets
+ the label for each column.
+
+ Note that setHeaderLabels() won't remove existing columns.
+
+ \sa setHeaderItem(), setHeaderLabel()
+*/
+void QTreeWidget::setHeaderLabels(const QStringList &labels)
+{
+ Q_D(QTreeWidget);
+ if (columnCount() < labels.count())
+ setColumnCount(labels.count());
+ QTreeModel *model = d->model();
+ QTreeWidgetItem *item = model->headerItem;
+ for (int i = 0; i < labels.count(); ++i)
+ item->setText(i, labels.at(i));
+}
+
+/*!
+ \fn void QTreeWidget::setHeaderLabel(const QString &label)
+ \since 4.2
+
+ Same as setHeaderLabels(QStringList(\a label)).
+*/
+
+/*!
+ Returns the current item in the tree widget.
+
+ \sa setCurrentItem(), currentItemChanged()
+*/
+QTreeWidgetItem *QTreeWidget::currentItem() const
+{
+ Q_D(const QTreeWidget);
+ return d->item(currentIndex());
+}
+
+/*!
+ \since 4.1
+ Returns the current column in the tree widget.
+
+ \sa setCurrentItem(), columnCount()
+*/
+int QTreeWidget::currentColumn() const
+{
+ return currentIndex().column();
+}
+
+/*!
+ Sets the current \a item in the tree widget.
+
+ Depending on the current selection mode, the item may also be selected.
+
+ \sa currentItem(), currentItemChanged()
+*/
+void QTreeWidget::setCurrentItem(QTreeWidgetItem *item)
+{
+ setCurrentItem(item, 0);
+}
+
+/*!
+ \since 4.1
+ Sets the current \a item in the tree widget and the current column to \a column.
+
+ \sa currentItem()
+*/
+void QTreeWidget::setCurrentItem(QTreeWidgetItem *item, int column)
+{
+ Q_D(QTreeWidget);
+ setCurrentIndex(d->index(item, column));
+}
+
+/*!
+ \since 4.4
+ Sets the current \a item in the tree widget and the current column to \a column,
+ using the given \a command.
+
+ \sa currentItem()
+*/
+void QTreeWidget::setCurrentItem(QTreeWidgetItem *item, int column,
+ QItemSelectionModel::SelectionFlags command)
+{
+ Q_D(QTreeWidget);
+ d->selectionModel->setCurrentIndex(d->index(item, column), command);
+}
+
+
+/*!
+ Returns a pointer to the item at the coordinates \a p.
+
+ \sa visualItemRect()
+*/
+QTreeWidgetItem *QTreeWidget::itemAt(const QPoint &p) const
+{
+ Q_D(const QTreeWidget);
+ return d->item(indexAt(p));
+}
+
+/*!
+ \fn QTreeWidgetItem *QTreeWidget::itemAt(int x, int y) const
+ \overload
+
+ Returns a pointer to the item at the coordinates (\a x, \a y).
+*/
+
+/*!
+ Returns the rectangle on the viewport occupied by the item at \a item.
+
+ \sa itemAt()
+*/
+QRect QTreeWidget::visualItemRect(const QTreeWidgetItem *item) const
+{
+ Q_D(const QTreeWidget);
+ return visualRect(d->index(item));
+}
+
+/*!
+ \since 4.1
+
+ Returns the column used to sort the contents of the widget.
+
+ \sa sortItems()
+*/
+int QTreeWidget::sortColumn() const
+{
+ Q_D(const QTreeWidget);
+ return (d->explicitSortColumn != -1
+ ? d->explicitSortColumn
+ : header()->sortIndicatorSection());
+}
+
+/*!
+ Sorts the items in the widget in the specified \a order by the values in
+ the given \a column.
+
+ \sa sortColumn()
+*/
+
+void QTreeWidget::sortItems(int column, Qt::SortOrder order)
+{
+ Q_D(QTreeWidget);
+ header()->setSortIndicator(column, order);
+ d->model()->sort(column, order);
+}
+
+/*!
+ \internal
+
+ ### Qt 5: remove
+*/
+void QTreeWidget::setSortingEnabled(bool enable)
+{
+ QTreeView::setSortingEnabled(enable);
+}
+
+/*!
+ \internal
+
+ ### Qt 5: remove
+*/
+bool QTreeWidget::isSortingEnabled() const
+{
+ return QTreeView::isSortingEnabled();
+}
+
+/*!
+ Starts editing the \a item in the given \a column if it is editable.
+*/
+
+void QTreeWidget::editItem(QTreeWidgetItem *item, int column)
+{
+ Q_D(QTreeWidget);
+ edit(d->index(item, column));
+}
+
+/*!
+ Opens a persistent editor for the \a item in the given \a column.
+
+ \sa closePersistentEditor()
+*/
+
+void QTreeWidget::openPersistentEditor(QTreeWidgetItem *item, int column)
+{
+ Q_D(QTreeWidget);
+ QAbstractItemView::openPersistentEditor(d->index(item, column));
+}
+
+/*!
+ Closes the persistent editor for the \a item in the given \a column.
+
+ This function has no effect if no persistent editor is open for this
+ combination of item and column.
+
+ \sa openPersistentEditor()
+*/
+
+void QTreeWidget::closePersistentEditor(QTreeWidgetItem *item, int column)
+{
+ Q_D(QTreeWidget);
+ QAbstractItemView::closePersistentEditor(d->index(item, column));
+}
+
+/*!
+ \since 4.1
+
+ Returns the widget displayed in the cell specified by \a item and the given \a column.
+
+ \note The tree takes ownership of the widget.
+
+*/
+QWidget *QTreeWidget::itemWidget(QTreeWidgetItem *item, int column) const
+{
+ Q_D(const QTreeWidget);
+ return QAbstractItemView::indexWidget(d->index(item, column));
+}
+
+/*!
+ \since 4.1
+
+ Sets the given \a widget to be displayed in the cell specified by the given
+ \a item and \a column.
+
+ The given \a widget's \l {QWidget::}{autoFillBackground} property must be
+ set to true, otherwise the widget's background will be transparent, showing
+ both the model data and the tree widget item.
+
+ This function should only be used to display static content in the place of
+ a tree widget item. If you want to display custom dynamic content or
+ implement a custom editor widget, use QTreeView and subclass QItemDelegate
+ instead.
+
+ This function cannot be called before the item hierarchy has been set up,
+ i.e., the QTreeWidgetItem that will hold \a widget must have been added to
+ the view before \a widget is set.
+
+ \note The tree takes ownership of the widget.
+
+ \sa {Delegate Classes}
+*/
+void QTreeWidget::setItemWidget(QTreeWidgetItem *item, int column, QWidget *widget)
+{
+ Q_D(QTreeWidget);
+ QAbstractItemView::setIndexWidget(d->index(item, column), widget);
+}
+
+/*!
+ Returns true if the \a item is selected; otherwise returns false.
+
+ \sa itemSelectionChanged()
+
+ \obsolete
+
+ This function is deprecated. Use \l{QTreeWidgetItem::isSelected()} instead.
+*/
+bool QTreeWidget::isItemSelected(const QTreeWidgetItem *item) const
+{
+ if (!item)
+ return false;
+ return item->d->selected;
+}
+
+/*!
+ If \a select is true, the given \a item is selected; otherwise it is
+ deselected.
+
+ \sa itemSelectionChanged()
+
+ \obsolete
+
+ This function is deprecated. Use \l{QTreeWidgetItem::setSelected()} instead.
+*/
+void QTreeWidget::setItemSelected(const QTreeWidgetItem *item, bool select)
+{
+ Q_D(QTreeWidget);
+
+ if (!item)
+ return;
+
+ selectionModel()->select(d->index(item), (select ? QItemSelectionModel::Select
+ : QItemSelectionModel::Deselect)
+ |QItemSelectionModel::Rows);
+ item->d->selected = select;
+}
+
+/*!
+ Returns a list of all selected non-hidden items.
+
+ \sa itemSelectionChanged()
+*/
+QList<QTreeWidgetItem*> QTreeWidget::selectedItems() const
+{
+ Q_D(const QTreeWidget);
+ QModelIndexList indexes = selectionModel()->selectedIndexes();
+ QList<QTreeWidgetItem*> items;
+ for (int i = 0; i < indexes.count(); ++i) {
+ QTreeWidgetItem *item = d->item(indexes.at(i));
+ if (isItemHidden(item) || items.contains(item)) // ### slow, optimize later
+ continue;
+ items.append(item);
+ }
+ return items;
+}
+
+/*!
+ Returns a list of items that match the given \a text, using the given \a flags, in the given \a column.
+*/
+QList<QTreeWidgetItem*> QTreeWidget::findItems(const QString &text, Qt::MatchFlags flags, int column) const
+{
+ Q_D(const QTreeWidget);
+ QModelIndexList indexes = d->model()->match(model()->index(0, column, QModelIndex()),
+ Qt::DisplayRole, text, -1, flags);
+ QList<QTreeWidgetItem*> items;
+ for (int i = 0; i < indexes.size(); ++i)
+ items.append(d->item(indexes.at(i)));
+ return items;
+}
+
+/*!
+ Returns true if the \a item is explicitly hidden, otherwise returns false.
+
+ \obsolete
+
+ This function is deprecated. Use \l{QTreeWidgetItem::isHidden()} instead.
+*/
+bool QTreeWidget::isItemHidden(const QTreeWidgetItem *item) const
+{
+ Q_D(const QTreeWidget);
+ if (item == d->model()->headerItem)
+ return header()->isHidden();
+ if (d->hiddenIndexes.isEmpty())
+ return false;
+ QTreeModel::SkipSorting skipSorting(d->model());
+ return d->isRowHidden(d->index(item));
+}
+
+/*!
+ Hides the given \a item if \a hide is true; otherwise shows the item.
+
+ \sa itemChanged()
+
+ \obsolete
+
+ This function is deprecated. Use \l{QTreeWidgetItem::setHidden()} instead.
+*/
+void QTreeWidget::setItemHidden(const QTreeWidgetItem *item, bool hide)
+{
+ Q_D(QTreeWidget);
+ if (item == d->model()->headerItem) {
+ header()->setHidden(hide);
+ } else {
+ const QModelIndex index = d->index(item);
+ setRowHidden(index.row(), index.parent(), hide);
+ }
+}
+
+/*!
+ Returns true if the given \a item is open; otherwise returns false.
+
+ \sa itemExpanded()
+
+ \obsolete
+
+ This function is deprecated. Use \l{QTreeWidgetItem::isExpanded()} instead.
+*/
+bool QTreeWidget::isItemExpanded(const QTreeWidgetItem *item) const
+{
+ Q_D(const QTreeWidget);
+ QTreeModel::SkipSorting skipSorting(d->model());
+ return isExpanded(d->index(item));
+}
+
+/*!
+ Sets the item referred to by \a item to either closed or opened,
+ depending on the value of \a expand.
+
+ \sa expandItem(), collapseItem(), itemExpanded()
+
+ \obsolete
+
+ This function is deprecated. Use \l{QTreeWidgetItem::setExpanded()} instead.
+*/
+void QTreeWidget::setItemExpanded(const QTreeWidgetItem *item, bool expand)
+{
+ Q_D(QTreeWidget);
+ QTreeModel::SkipSorting skipSorting(d->model());
+ setExpanded(d->index(item), expand);
+}
+
+/*!
+ \since 4.3
+
+ Returns true if the given \a item is set to show only one section over all columns;
+ otherwise returns false.
+
+ \sa setFirstItemColumnSpanned()
+*/
+bool QTreeWidget::isFirstItemColumnSpanned(const QTreeWidgetItem *item) const
+{
+ Q_D(const QTreeWidget);
+ if (item == d->model()->headerItem)
+ return false; // We can't set the header items to spanning
+ const QModelIndex index = d->index(item);
+ return isFirstColumnSpanned(index.row(), index.parent());
+}
+
+/*!
+ \since 4.3
+
+ Sets the given \a item to only show one section for all columns if \a span is true;
+ otherwise the item will show one section per column.
+
+ \sa isFirstItemColumnSpanned()
+*/
+void QTreeWidget::setFirstItemColumnSpanned(const QTreeWidgetItem *item, bool span)
+{
+ Q_D(QTreeWidget);
+ if (item == d->model()->headerItem)
+ return; // We can't set header items to spanning
+ const QModelIndex index = d->index(item);
+ setFirstColumnSpanned(index.row(), index.parent(), span);
+}
+
+/*!
+ \since 4.3
+
+ Returns the item above the given \a item.
+*/
+QTreeWidgetItem *QTreeWidget::itemAbove(const QTreeWidgetItem *item) const
+{
+ Q_D(const QTreeWidget);
+ if (item == d->model()->headerItem)
+ return 0;
+ const QModelIndex index = d->index(item);
+ const QModelIndex above = indexAbove(index);
+ return d->item(above);
+}
+
+/*!
+ \since 4.3
+
+ Returns the item visually below the given \a item.
+*/
+QTreeWidgetItem *QTreeWidget::itemBelow(const QTreeWidgetItem *item) const
+{
+ Q_D(const QTreeWidget);
+ if (item == d->model()->headerItem)
+ return 0;
+ const QModelIndex index = d->index(item);
+ const QModelIndex below = indexBelow(index);
+ return d->item(below);
+}
+
+/*!
+ \reimp
+ */
+void QTreeWidget::setSelectionModel(QItemSelectionModel *selectionModel)
+{
+ Q_D(QTreeWidget);
+ QTreeView::setSelectionModel(selectionModel);
+ QItemSelection newSelection = selectionModel->selection();
+ if (!newSelection.isEmpty())
+ d->_q_selectionChanged(newSelection, QItemSelection());
+}
+
+/*!
+ Ensures that the \a item is visible, scrolling the view if necessary using
+ the specified \a hint.
+
+ \sa currentItem(), itemAt(), topLevelItem()
+*/
+void QTreeWidget::scrollToItem(const QTreeWidgetItem *item, QAbstractItemView::ScrollHint hint)
+{
+ Q_D(QTreeWidget);
+ QTreeView::scrollTo(d->index(item), hint);
+}
+
+/*!
+ Expands the \a item. This causes the tree containing the item's children
+ to be expanded.
+
+ \sa collapseItem(), currentItem(), itemAt(), topLevelItem(), itemExpanded()
+*/
+void QTreeWidget::expandItem(const QTreeWidgetItem *item)
+{
+ Q_D(QTreeWidget);
+ QTreeModel::SkipSorting skipSorting(d->model());
+ expand(d->index(item));
+}
+
+/*!
+ Closes the \a item. This causes the tree containing the item's children
+ to be collapsed.
+
+ \sa expandItem(), currentItem(), itemAt(), topLevelItem()
+*/
+void QTreeWidget::collapseItem(const QTreeWidgetItem *item)
+{
+ Q_D(QTreeWidget);
+ QTreeModel::SkipSorting skipSorting(d->model());
+ collapse(d->index(item));
+}
+
+/*!
+ Clears the tree widget by removing all of its items and selections.
+
+ \bold{Note:} Since each item is removed from the tree widget before being
+ deleted, the return value of QTreeWidgetItem::treeWidget() will be invalid
+ when called from an item's destructor.
+
+ \sa takeTopLevelItem(), topLevelItemCount(), columnCount()
+*/
+void QTreeWidget::clear()
+{
+ Q_D(QTreeWidget);
+ selectionModel()->clear();
+ d->model()->clear();
+}
+
+/*!
+ Returns a list of MIME types that can be used to describe a list of
+ treewidget items.
+
+ \sa mimeData()
+*/
+QStringList QTreeWidget::mimeTypes() const
+{
+ return model()->QAbstractItemModel::mimeTypes();
+}
+
+/*!
+ Returns an object that contains a serialized description of the specified
+ \a items. The format used to describe the items is obtained from the
+ mimeTypes() function.
+
+ If the list of items is empty, 0 is returned rather than a serialized
+ empty list.
+*/
+QMimeData *QTreeWidget::mimeData(const QList<QTreeWidgetItem*> items) const
+{
+ Q_D(const QTreeWidget);
+ if (d->model()->cachedIndexes.isEmpty()) {
+ QList<QModelIndex> indexes;
+ for (int i = 0; i < items.count(); ++i) {
+ QTreeWidgetItem *item = items.at(i);
+ for (int c = 0; c < item->values.count(); ++c) {
+ indexes << indexFromItem(item, c);
+ }
+ }
+ return model()->QAbstractItemModel::mimeData(indexes);
+ }
+ return d->model()->internalMimeData();
+}
+
+/*!
+ Handles the \a data supplied by a drag and drop operation that ended with
+ the given \a action in the \a index in the given \a parent item.
+
+ The default implementation returns true if the drop was
+ successfully handled by decoding the mime data and inserting it
+ into the model; otherwise it returns false.
+
+ \sa supportedDropActions()
+*/
+bool QTreeWidget::dropMimeData(QTreeWidgetItem *parent, int index,
+ const QMimeData *data, Qt::DropAction action)
+{
+ QModelIndex idx;
+ if (parent) idx = indexFromItem(parent);
+ return model()->QAbstractItemModel::dropMimeData(data, action , index, 0, idx);
+}
+
+/*!
+ Returns the drop actions supported by this view.
+
+ \sa Qt::DropActions
+*/
+Qt::DropActions QTreeWidget::supportedDropActions() const
+{
+ return model()->QAbstractItemModel::supportedDropActions() | Qt::MoveAction;
+}
+
+/*!
+ \obsolete
+ Returns an empty list
+
+ \sa mimeData()
+*/
+QList<QTreeWidgetItem*> QTreeWidget::items(const QMimeData *data) const
+{
+ Q_UNUSED(data);
+ return QList<QTreeWidgetItem*>();
+}
+
+/*!
+ Returns the QModelIndex assocated with the given \a item in the given \a column.
+
+ \sa itemFromIndex(), topLevelItem()
+*/
+QModelIndex QTreeWidget::indexFromItem(QTreeWidgetItem *item, int column) const
+{
+ Q_D(const QTreeWidget);
+ return d->index(item, column);
+}
+
+/*!
+ Returns a pointer to the QTreeWidgetItem assocated with the given \a index.
+
+ \sa indexFromItem()
+*/
+QTreeWidgetItem *QTreeWidget::itemFromIndex(const QModelIndex &index) const
+{
+ Q_D(const QTreeWidget);
+ return d->item(index);
+}
+
+#ifndef QT_NO_DRAGANDDROP
+/*! \reimp */
+void QTreeWidget::dropEvent(QDropEvent *event) {
+ Q_D(QTreeWidget);
+ if (event->source() == this && (event->dropAction() == Qt::MoveAction ||
+ dragDropMode() == QAbstractItemView::InternalMove)) {
+ QModelIndex topIndex;
+ int col = -1;
+ int row = -1;
+ if (d->dropOn(event, &row, &col, &topIndex)) {
+ QList<QModelIndex> idxs = selectedIndexes();
+ QList<QPersistentModelIndex> indexes;
+ for (int i = 0; i < idxs.count(); i++)
+ indexes.append(idxs.at(i));
+
+ if (indexes.contains(topIndex))
+ return;
+
+ // When removing items the drop location could shift
+ QPersistentModelIndex dropRow = model()->index(row, col, topIndex);
+
+ // Remove the items
+ QList<QTreeWidgetItem *> taken;
+ for (int i = indexes.count() - 1; i >= 0; --i) {
+ QTreeWidgetItem *parent = itemFromIndex(indexes.at(i));
+ if (!parent || !parent->parent()) {
+ taken.append(takeTopLevelItem(indexes.at(i).row()));
+ } else {
+ taken.append(parent->parent()->takeChild(indexes.at(i).row()));
+ }
+ }
+
+ // insert them back in at their new positions
+ for (int i = 0; i < indexes.count(); ++i) {
+ // Either at a specific point or appended
+ if (row == -1) {
+ if (topIndex.isValid()) {
+ QTreeWidgetItem *parent = itemFromIndex(topIndex);
+ parent->insertChild(parent->childCount(), taken.takeFirst());
+ } else {
+ insertTopLevelItem(topLevelItemCount(), taken.takeFirst());
+ }
+ } else {
+ int r = dropRow.row() >= 0 ? dropRow.row() : row;
+ if (topIndex.isValid()) {
+ QTreeWidgetItem *parent = itemFromIndex(topIndex);
+ parent->insertChild(qMin(r, parent->childCount()), taken.takeFirst());
+ } else {
+ insertTopLevelItem(qMin(r, topLevelItemCount()), taken.takeFirst());
+ }
+ }
+ }
+
+ event->accept();
+ // Don't want QAbstractItemView to delete it because it was "moved" we already did it
+ event->setDropAction(Qt::CopyAction);
+ }
+ }
+
+ QTreeView::dropEvent(event);
+}
+#endif
+
+/*!
+ \reimp
+*/
+
+void QTreeWidget::setModel(QAbstractItemModel * /*model*/)
+{
+ Q_ASSERT(!"QTreeWidget::setModel() - Changing the model of the QTreeWidget is not allowed.");
+}
+
+/*!
+ \reimp
+*/
+bool QTreeWidget::event(QEvent *e)
+{
+ Q_D(QTreeWidget);
+ if (e->type() == QEvent::Polish)
+ d->model()->executePendingSort();
+ return QTreeView::event(e);
+}
+
+QT_END_NAMESPACE
+
+#include "moc_qtreewidget.cpp"
+
+#endif // QT_NO_TREEWIDGET
diff --git a/src/gui/itemviews/qtreewidget.h b/src/gui/itemviews/qtreewidget.h
new file mode 100644
index 0000000000..4bacce7f6c
--- /dev/null
+++ b/src/gui/itemviews/qtreewidget.h
@@ -0,0 +1,432 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QTREEWIDGET_H
+#define QTREEWIDGET_H
+
+#include <QtGui/qtreeview.h>
+#include <QtGui/qtreewidgetitemiterator.h>
+#include <QtCore/qvariant.h>
+#include <QtCore/qvector.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+#ifndef QT_NO_TREEWIDGET
+
+class QTreeWidget;
+class QTreeModel;
+class QWidgetItemData;
+class QTreeWidgetItemPrivate;
+
+class Q_GUI_EXPORT QTreeWidgetItem
+{
+ friend class QTreeModel;
+ friend class QTreeWidget;
+ friend class QTreeWidgetPrivate;
+ friend class QTreeWidgetItemIterator;
+ friend class QTreeWidgetItemPrivate;
+public:
+ enum ItemType { Type = 0, UserType = 1000 };
+ explicit QTreeWidgetItem(int type = Type);
+ QTreeWidgetItem(const QStringList &strings, int type = Type);
+ explicit QTreeWidgetItem(QTreeWidget *view, int type = Type);
+ QTreeWidgetItem(QTreeWidget *view, const QStringList &strings, int type = Type);
+ QTreeWidgetItem(QTreeWidget *view, QTreeWidgetItem *after, int type = Type);
+ explicit QTreeWidgetItem(QTreeWidgetItem *parent, int type = Type);
+ QTreeWidgetItem(QTreeWidgetItem *parent, const QStringList &strings, int type = Type);
+ QTreeWidgetItem(QTreeWidgetItem *parent, QTreeWidgetItem *after, int type = Type);
+ QTreeWidgetItem(const QTreeWidgetItem &other);
+ virtual ~QTreeWidgetItem();
+
+ virtual QTreeWidgetItem *clone() const;
+
+ inline QTreeWidget *treeWidget() const { return view; }
+
+ inline void setSelected(bool select);
+ inline bool isSelected() const;
+
+ inline void setHidden(bool hide);
+ inline bool isHidden() const;
+
+ inline void setExpanded(bool expand);
+ inline bool isExpanded() const;
+
+ inline void setFirstColumnSpanned(bool span);
+ inline bool isFirstColumnSpanned() const;
+
+ inline void setDisabled(bool disabled);
+ inline bool isDisabled() const;
+
+ enum ChildIndicatorPolicy { ShowIndicator, DontShowIndicator, DontShowIndicatorWhenChildless };
+ void setChildIndicatorPolicy(QTreeWidgetItem::ChildIndicatorPolicy policy);
+ QTreeWidgetItem::ChildIndicatorPolicy childIndicatorPolicy() const;
+
+ Qt::ItemFlags flags() const;
+ void setFlags(Qt::ItemFlags flags);
+
+ inline QString text(int column) const
+ { return data(column, Qt::DisplayRole).toString(); }
+ inline void setText(int column, const QString &text);
+
+ inline QIcon icon(int column) const
+ { return qvariant_cast<QIcon>(data(column, Qt::DecorationRole)); }
+ inline void setIcon(int column, const QIcon &icon);
+
+ inline QString statusTip(int column) const
+ { return data(column, Qt::StatusTipRole).toString(); }
+ inline void setStatusTip(int column, const QString &statusTip);
+
+#ifndef QT_NO_TOOLTIP
+ inline QString toolTip(int column) const
+ { return data(column, Qt::ToolTipRole).toString(); }
+ inline void setToolTip(int column, const QString &toolTip);
+#endif
+
+#ifndef QT_NO_WHATSTHIS
+ inline QString whatsThis(int column) const
+ { return data(column, Qt::WhatsThisRole).toString(); }
+ inline void setWhatsThis(int column, const QString &whatsThis);
+#endif
+
+ inline QFont font(int column) const
+ { return qvariant_cast<QFont>(data(column, Qt::FontRole)); }
+ inline void setFont(int column, const QFont &font);
+
+ inline int textAlignment(int column) const
+ { return data(column, Qt::TextAlignmentRole).toInt(); }
+ inline void setTextAlignment(int column, int alignment)
+ { setData(column, Qt::TextAlignmentRole, alignment); }
+
+ inline QColor backgroundColor(int column) const
+ { return qvariant_cast<QColor>(data(column, Qt::BackgroundColorRole)); }
+ inline void setBackgroundColor(int column, const QColor &color)
+ { setData(column, Qt::BackgroundColorRole, color); }
+
+ inline QBrush background(int column) const
+ { return qvariant_cast<QBrush>(data(column, Qt::BackgroundRole)); }
+ inline void setBackground(int column, const QBrush &brush)
+ { setData(column, Qt::BackgroundRole, brush); }
+
+ inline QColor textColor(int column) const
+ { return qvariant_cast<QColor>(data(column, Qt::TextColorRole)); }
+ inline void setTextColor(int column, const QColor &color)
+ { setData(column, Qt::TextColorRole, color); }
+
+ inline QBrush foreground(int column) const
+ { return qvariant_cast<QBrush>(data(column, Qt::ForegroundRole)); }
+ inline void setForeground(int column, const QBrush &brush)
+ { setData(column, Qt::ForegroundRole, brush); }
+
+ inline Qt::CheckState checkState(int column) const
+ { return static_cast<Qt::CheckState>(data(column, Qt::CheckStateRole).toInt()); }
+ inline void setCheckState(int column, Qt::CheckState state)
+ { setData(column, Qt::CheckStateRole, state); }
+
+ inline QSize sizeHint(int column) const
+ { return qvariant_cast<QSize>(data(column, Qt::SizeHintRole)); }
+ inline void setSizeHint(int column, const QSize &size)
+ { setData(column, Qt::SizeHintRole, size); }
+
+ virtual QVariant data(int column, int role) const;
+ virtual void setData(int column, int role, const QVariant &value);
+
+ virtual bool operator<(const QTreeWidgetItem &other) const;
+
+#ifndef QT_NO_DATASTREAM
+ virtual void read(QDataStream &in);
+ virtual void write(QDataStream &out) const;
+#endif
+ QTreeWidgetItem &operator=(const QTreeWidgetItem &other);
+
+ inline QTreeWidgetItem *parent() const { return par; }
+ inline QTreeWidgetItem *child(int index) const {
+ if (index < 0 || index >= children.size())
+ return 0;
+ executePendingSort();
+ return children.at(index);
+ }
+ inline int childCount() const { return children.count(); }
+ inline int columnCount() const { return values.count(); }
+ inline int indexOfChild(QTreeWidgetItem *child) const;
+
+ void addChild(QTreeWidgetItem *child);
+ void insertChild(int index, QTreeWidgetItem *child);
+ void removeChild(QTreeWidgetItem *child);
+ QTreeWidgetItem *takeChild(int index);
+
+ void addChildren(const QList<QTreeWidgetItem*> &children);
+ void insertChildren(int index, const QList<QTreeWidgetItem*> &children);
+ QList<QTreeWidgetItem*> takeChildren();
+
+ inline int type() const { return rtti; }
+ inline void sortChildren(int column, Qt::SortOrder order)
+ { sortChildren(column, order, false); }
+
+protected:
+ void emitDataChanged();
+
+private:
+ void sortChildren(int column, Qt::SortOrder order, bool climb);
+ QVariant childrenCheckState(int column) const;
+ void itemChanged();
+ void executePendingSort() const;
+
+ int rtti;
+ // One item has a vector of column entries. Each column has a vector of (role, value) pairs.
+ QVector< QVector<QWidgetItemData> > values;
+ QTreeWidget *view;
+ QTreeWidgetItemPrivate *d;
+ QTreeWidgetItem *par;
+ QList<QTreeWidgetItem*> children;
+ Qt::ItemFlags itemFlags;
+};
+
+inline void QTreeWidgetItem::setText(int column, const QString &atext)
+{ setData(column, Qt::DisplayRole, atext); }
+
+inline void QTreeWidgetItem::setIcon(int column, const QIcon &aicon)
+{ setData(column, Qt::DecorationRole, aicon); }
+
+#ifndef QT_NO_STATUSTIP
+inline void QTreeWidgetItem::setStatusTip(int column, const QString &astatusTip)
+{ setData(column, Qt::StatusTipRole, astatusTip); }
+#endif
+
+#ifndef QT_NO_TOOLTIP
+inline void QTreeWidgetItem::setToolTip(int column, const QString &atoolTip)
+{ setData(column, Qt::ToolTipRole, atoolTip); }
+#endif
+
+#ifndef QT_NO_WHATSTHIS
+inline void QTreeWidgetItem::setWhatsThis(int column, const QString &awhatsThis)
+{ setData(column, Qt::WhatsThisRole, awhatsThis); }
+#endif
+
+inline void QTreeWidgetItem::setFont(int column, const QFont &afont)
+{ setData(column, Qt::FontRole, afont); }
+
+inline int QTreeWidgetItem::indexOfChild(QTreeWidgetItem *achild) const
+{ executePendingSort(); return children.indexOf(achild); }
+
+#ifndef QT_NO_DATASTREAM
+Q_GUI_EXPORT QDataStream &operator<<(QDataStream &out, const QTreeWidgetItem &item);
+Q_GUI_EXPORT QDataStream &operator>>(QDataStream &in, QTreeWidgetItem &item);
+#endif
+
+class QTreeWidgetPrivate;
+
+class Q_GUI_EXPORT QTreeWidget : public QTreeView
+{
+ Q_OBJECT
+ Q_PROPERTY(int columnCount READ columnCount WRITE setColumnCount)
+ Q_PROPERTY(int topLevelItemCount READ topLevelItemCount)
+
+ friend class QTreeModel;
+ friend class QTreeWidgetItem;
+public:
+ explicit QTreeWidget(QWidget *parent = 0);
+ ~QTreeWidget();
+
+ int columnCount() const;
+ void setColumnCount(int columns);
+
+ QTreeWidgetItem *invisibleRootItem() const;
+ QTreeWidgetItem *topLevelItem(int index) const;
+ int topLevelItemCount() const;
+ void insertTopLevelItem(int index, QTreeWidgetItem *item);
+ void addTopLevelItem(QTreeWidgetItem *item);
+ QTreeWidgetItem *takeTopLevelItem(int index);
+ int indexOfTopLevelItem(QTreeWidgetItem *item); // ### Qt 5: remove me
+ int indexOfTopLevelItem(QTreeWidgetItem *item) const;
+
+ void insertTopLevelItems(int index, const QList<QTreeWidgetItem*> &items);
+ void addTopLevelItems(const QList<QTreeWidgetItem*> &items);
+
+ QTreeWidgetItem *headerItem() const;
+ void setHeaderItem(QTreeWidgetItem *item);
+ void setHeaderLabels(const QStringList &labels);
+ inline void setHeaderLabel(const QString &label);
+
+ QTreeWidgetItem *currentItem() const;
+ int currentColumn() const;
+ void setCurrentItem(QTreeWidgetItem *item);
+ void setCurrentItem(QTreeWidgetItem *item, int column);
+ void setCurrentItem(QTreeWidgetItem *item, int column, QItemSelectionModel::SelectionFlags command);
+
+ QTreeWidgetItem *itemAt(const QPoint &p) const;
+ inline QTreeWidgetItem *itemAt(int x, int y) const;
+ QRect visualItemRect(const QTreeWidgetItem *item) const;
+
+ int sortColumn() const;
+ void sortItems(int column, Qt::SortOrder order);
+ void setSortingEnabled(bool enable);
+ bool isSortingEnabled() const;
+
+ void editItem(QTreeWidgetItem *item, int column = 0);
+ void openPersistentEditor(QTreeWidgetItem *item, int column = 0);
+ void closePersistentEditor(QTreeWidgetItem *item, int column = 0);
+
+ QWidget *itemWidget(QTreeWidgetItem *item, int column) const;
+ void setItemWidget(QTreeWidgetItem *item, int column, QWidget *widget);
+ inline void removeItemWidget(QTreeWidgetItem *item, int column);
+
+ bool isItemSelected(const QTreeWidgetItem *item) const;
+ void setItemSelected(const QTreeWidgetItem *item, bool select);
+ QList<QTreeWidgetItem*> selectedItems() const;
+ QList<QTreeWidgetItem*> findItems(const QString &text, Qt::MatchFlags flags,
+ int column = 0) const;
+
+ bool isItemHidden(const QTreeWidgetItem *item) const;
+ void setItemHidden(const QTreeWidgetItem *item, bool hide);
+
+ bool isItemExpanded(const QTreeWidgetItem *item) const;
+ void setItemExpanded(const QTreeWidgetItem *item, bool expand);
+
+ bool isFirstItemColumnSpanned(const QTreeWidgetItem *item) const;
+ void setFirstItemColumnSpanned(const QTreeWidgetItem *item, bool span);
+
+ QTreeWidgetItem *itemAbove(const QTreeWidgetItem *item) const;
+ QTreeWidgetItem *itemBelow(const QTreeWidgetItem *item) const;
+
+ void setSelectionModel(QItemSelectionModel *selectionModel);
+
+public Q_SLOTS:
+ void scrollToItem(const QTreeWidgetItem *item,
+ QAbstractItemView::ScrollHint hint = EnsureVisible);
+ void expandItem(const QTreeWidgetItem *item);
+ void collapseItem(const QTreeWidgetItem *item);
+ void clear();
+
+Q_SIGNALS:
+ void itemPressed(QTreeWidgetItem *item, int column);
+ void itemClicked(QTreeWidgetItem *item, int column);
+ void itemDoubleClicked(QTreeWidgetItem *item, int column);
+ void itemActivated(QTreeWidgetItem *item, int column);
+ void itemEntered(QTreeWidgetItem *item, int column);
+ void itemChanged(QTreeWidgetItem *item, int column);
+ void itemExpanded(QTreeWidgetItem *item);
+ void itemCollapsed(QTreeWidgetItem *item);
+ void currentItemChanged(QTreeWidgetItem *current, QTreeWidgetItem *previous);
+ void itemSelectionChanged();
+
+protected:
+ bool event(QEvent *e);
+ virtual QStringList mimeTypes() const;
+ virtual QMimeData *mimeData(const QList<QTreeWidgetItem*> items) const;
+ virtual bool dropMimeData(QTreeWidgetItem *parent, int index,
+ const QMimeData *data, Qt::DropAction action);
+ virtual Qt::DropActions supportedDropActions() const;
+ QList<QTreeWidgetItem*> items(const QMimeData *data) const;
+
+ QModelIndex indexFromItem(QTreeWidgetItem *item, int column = 0) const;
+ QTreeWidgetItem *itemFromIndex(const QModelIndex &index) const;
+ void dropEvent(QDropEvent *event);
+
+private:
+ void setModel(QAbstractItemModel *model);
+
+ Q_DECLARE_PRIVATE(QTreeWidget)
+ Q_DISABLE_COPY(QTreeWidget)
+
+ Q_PRIVATE_SLOT(d_func(), void _q_emitItemPressed(const QModelIndex &index))
+ Q_PRIVATE_SLOT(d_func(), void _q_emitItemClicked(const QModelIndex &index))
+ Q_PRIVATE_SLOT(d_func(), void _q_emitItemDoubleClicked(const QModelIndex &index))
+ Q_PRIVATE_SLOT(d_func(), void _q_emitItemActivated(const QModelIndex &index))
+ Q_PRIVATE_SLOT(d_func(), void _q_emitItemEntered(const QModelIndex &index))
+ Q_PRIVATE_SLOT(d_func(), void _q_emitItemChanged(const QModelIndex &index))
+ Q_PRIVATE_SLOT(d_func(), void _q_emitItemExpanded(const QModelIndex &index))
+ Q_PRIVATE_SLOT(d_func(), void _q_emitItemCollapsed(const QModelIndex &index))
+ Q_PRIVATE_SLOT(d_func(), void _q_emitCurrentItemChanged(const QModelIndex &previous, const QModelIndex &current))
+ Q_PRIVATE_SLOT(d_func(), void _q_sort())
+ Q_PRIVATE_SLOT(d_func(), void _q_dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight))
+ Q_PRIVATE_SLOT(d_func(), void _q_selectionChanged(const QItemSelection &selected, const QItemSelection &deselected))
+};
+
+inline void QTreeWidget::removeItemWidget(QTreeWidgetItem *item, int column)
+{ setItemWidget(item, column, 0); }
+
+inline QTreeWidgetItem *QTreeWidget::itemAt(int ax, int ay) const
+{ return itemAt(QPoint(ax, ay)); }
+
+inline void QTreeWidget::setHeaderLabel(const QString &alabel)
+{ setHeaderLabels(QStringList(alabel)); }
+
+inline void QTreeWidgetItem::setSelected(bool aselect)
+{ if (view) view->setItemSelected(this, aselect); }
+
+inline bool QTreeWidgetItem::isSelected() const
+{ return (view ? view->isItemSelected(this) : false); }
+
+inline void QTreeWidgetItem::setHidden(bool ahide)
+{ if (view) view->setItemHidden(this, ahide); }
+
+inline bool QTreeWidgetItem::isHidden() const
+{ return (view ? view->isItemHidden(this) : false); }
+
+inline void QTreeWidgetItem::setExpanded(bool aexpand)
+{ if (view) view->setItemExpanded(this, aexpand); }
+
+inline bool QTreeWidgetItem::isExpanded() const
+{ return (view ? view->isItemExpanded(this) : false); }
+
+inline void QTreeWidgetItem::setFirstColumnSpanned(bool aspan)
+{ if (view) view->setFirstItemColumnSpanned(this, aspan); }
+
+inline bool QTreeWidgetItem::isFirstColumnSpanned() const
+{ return (view ? view->isFirstItemColumnSpanned(this) : false); }
+
+inline void QTreeWidgetItem::setDisabled(bool disabled)
+{ setFlags(disabled ? (flags() & ~Qt::ItemIsEnabled) : flags() | Qt::ItemIsEnabled); }
+
+inline bool QTreeWidgetItem::isDisabled() const
+{ return !(flags() & Qt::ItemIsEnabled); }
+
+#endif // QT_NO_TREEWIDGET
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QTREEWIDGET_H
diff --git a/src/gui/itemviews/qtreewidget_p.h b/src/gui/itemviews/qtreewidget_p.h
new file mode 100644
index 0000000000..a089cf5e93
--- /dev/null
+++ b/src/gui/itemviews/qtreewidget_p.h
@@ -0,0 +1,249 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QTREEWIDGET_P_H
+#define QTREEWIDGET_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. This header file may change
+// from version to version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtCore/qabstractitemmodel.h>
+#include <private/qabstractitemmodel_p.h>
+#include <QtCore/qpair.h>
+#include <QtCore/qbasictimer.h>
+#include <QtGui/qtreewidget.h>
+#include <private/qtreeview_p.h>
+#include <QtGui/qheaderview.h>
+
+#ifndef QT_NO_TREEWIDGET
+
+QT_BEGIN_NAMESPACE
+
+class QTreeWidgetItem;
+class QTreeWidgetItemIterator;
+class QTreeModelPrivate;
+
+class QTreeModel : public QAbstractItemModel
+{
+ Q_OBJECT
+ friend class QTreeWidget;
+ friend class QTreeWidgetPrivate;
+ friend class QTreeWidgetItem;
+ friend class QTreeWidgetItemPrivate;
+ friend class QTreeWidgetItemIterator;
+ friend class QTreeWidgetItemIteratorPrivate;
+
+public:
+ explicit QTreeModel(int columns = 0, QTreeWidget *parent = 0);
+ ~QTreeModel();
+
+ inline QTreeWidget *view() const
+ { return qobject_cast<QTreeWidget*>(QObject::parent()); }
+
+ void clear();
+ void setColumnCount(int columns);
+
+ QTreeWidgetItem *item(const QModelIndex &index) const;
+ void itemChanged(QTreeWidgetItem *item);
+
+ QModelIndex index(const QTreeWidgetItem *item, int column) const;
+ QModelIndex index(int row, int column, const QModelIndex &parent) const;
+ QModelIndex parent(const QModelIndex &child) const;
+ int rowCount(const QModelIndex &parent) const;
+ int columnCount(const QModelIndex &parent = QModelIndex()) const;
+ bool hasChildren(const QModelIndex &parent) const;
+
+ QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
+ bool setData(const QModelIndex &index, const QVariant &value, int role);
+
+ QMap<int, QVariant> itemData(const QModelIndex &index) const;
+
+ QVariant headerData(int section, Qt::Orientation orientation, int role) const;
+ bool setHeaderData(int section, Qt::Orientation orientation, const QVariant &value,
+ int role);
+
+ Qt::ItemFlags flags(const QModelIndex &index) const;
+
+ void sort(int column, Qt::SortOrder order);
+ void ensureSorted(int column, Qt::SortOrder order,
+ int start, int end, const QModelIndex &parent);
+ static bool itemLessThan(const QPair<QTreeWidgetItem*,int> &left,
+ const QPair<QTreeWidgetItem*,int> &right);
+ static bool itemGreaterThan(const QPair<QTreeWidgetItem*,int> &left,
+ const QPair<QTreeWidgetItem*,int> &right);
+ static QList<QTreeWidgetItem*>::iterator sortedInsertionIterator(
+ const QList<QTreeWidgetItem*>::iterator &begin,
+ const QList<QTreeWidgetItem*>::iterator &end,
+ Qt::SortOrder order, QTreeWidgetItem *item);
+
+ bool insertRows(int row, int count, const QModelIndex &);
+ bool insertColumns(int column, int count, const QModelIndex &);
+
+ bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex());
+
+ // dnd
+ QStringList mimeTypes() const;
+ QMimeData *mimeData(const QModelIndexList &indexes) const;
+ bool dropMimeData(const QMimeData *data, Qt::DropAction action,
+ int row, int column, const QModelIndex &parent);
+ Qt::DropActions supportedDropActions() const;
+
+ QMimeData *internalMimeData() const;
+
+ inline QModelIndex createIndexFromItem(int row, int col, QTreeWidgetItem *item) const
+ { return createIndex(row, col, item); }
+
+protected:
+ QTreeModel(QTreeModelPrivate &, QTreeWidget *parent = 0);
+ void emitDataChanged(QTreeWidgetItem *item, int column);
+ void beginInsertItems(QTreeWidgetItem *parent, int row, int count);
+ void endInsertItems();
+ void beginRemoveItems(QTreeWidgetItem *parent, int row, int count);
+ void endRemoveItems();
+ void sortItems(QList<QTreeWidgetItem*> *items, int column, Qt::SortOrder order);
+ void timerEvent(QTimerEvent *);
+
+private:
+ QTreeWidgetItem *rootItem;
+ QTreeWidgetItem *headerItem;
+
+ mutable QModelIndexList cachedIndexes;
+ QList<QTreeWidgetItemIterator*> iterators;
+
+ mutable QBasicTimer sortPendingTimer;
+ mutable bool skipPendingSort; //while doing internal operation we don't care about sorting
+ bool inline executePendingSort() const;
+
+ bool isChanging() const;
+
+private:
+ Q_DECLARE_PRIVATE(QTreeModel)
+public:
+ struct SkipSorting
+ {
+ const QTreeModel * const model;
+ const bool previous;
+ SkipSorting(const QTreeModel *m) : model(m), previous(model->skipPendingSort)
+ { model->skipPendingSort = true; }
+ ~SkipSorting() { model->skipPendingSort = previous; }
+ };
+ friend struct SkipSorting;
+};
+
+QT_BEGIN_INCLUDE_NAMESPACE
+#include "private/qabstractitemmodel_p.h"
+QT_END_INCLUDE_NAMESPACE
+
+class QTreeModelPrivate : public QAbstractItemModelPrivate
+{
+ Q_DECLARE_PUBLIC(QTreeModel)
+};
+
+class QTreeWidgetItemPrivate
+{
+public:
+ QTreeWidgetItemPrivate(QTreeWidgetItem *item)
+ : q(item), disabled(false), selected(false), rowGuess(-1), policy(QTreeWidgetItem::DontShowIndicatorWhenChildless) {}
+ void propagateDisabled(QTreeWidgetItem *item);
+ void sortChildren(int column, Qt::SortOrder order, bool climb);
+ QTreeWidgetItem *q;
+ QVariantList display;
+ uint disabled : 1;
+ uint selected : 1;
+ int rowGuess;
+ QTreeWidgetItem::ChildIndicatorPolicy policy;
+};
+
+
+inline bool QTreeModel::executePendingSort() const
+{
+ if (!skipPendingSort && sortPendingTimer.isActive() && !isChanging()) {
+ sortPendingTimer.stop();
+ int column = view()->header()->sortIndicatorSection();
+ Qt::SortOrder order = view()->header()->sortIndicatorOrder();
+ QTreeModel *that = const_cast<QTreeModel*>(this);
+ that->sort(column, order);
+ return true;
+ }
+ return false;
+}
+
+class QTreeWidgetPrivate : public QTreeViewPrivate
+{
+ friend class QTreeModel;
+ Q_DECLARE_PUBLIC(QTreeWidget)
+public:
+ QTreeWidgetPrivate() : QTreeViewPrivate(), explicitSortColumn(-1) {}
+ inline QTreeModel *model() const
+ { return qobject_cast<QTreeModel*>(q_func()->model()); }
+ inline QModelIndex index(const QTreeWidgetItem *item, int column = 0) const
+ { return model()->index(item, column); }
+ inline QTreeWidgetItem *item(const QModelIndex &index) const
+ { return model()->item(index); }
+ void _q_emitItemPressed(const QModelIndex &index);
+ void _q_emitItemClicked(const QModelIndex &index);
+ void _q_emitItemDoubleClicked(const QModelIndex &index);
+ void _q_emitItemActivated(const QModelIndex &index);
+ void _q_emitItemEntered(const QModelIndex &index);
+ void _q_emitItemChanged(const QModelIndex &index);
+ void _q_emitItemExpanded(const QModelIndex &index);
+ void _q_emitItemCollapsed(const QModelIndex &index);
+ void _q_emitCurrentItemChanged(const QModelIndex &previous, const QModelIndex &index);
+ void _q_sort();
+ void _q_dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight);
+ void _q_selectionChanged(const QItemSelection &selected, const QItemSelection &deselected);
+
+ // used by QTreeWidgetItem::sortChildren to make sure the column argument is used
+ int explicitSortColumn;
+};
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_TREEWIDGET
+
+#endif // QTREEWIDGET_P_H
diff --git a/src/gui/itemviews/qtreewidgetitemiterator.cpp b/src/gui/itemviews/qtreewidgetitemiterator.cpp
new file mode 100644
index 0000000000..3e30e0346a
--- /dev/null
+++ b/src/gui/itemviews/qtreewidgetitemiterator.cpp
@@ -0,0 +1,459 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <private/qtreewidgetitemiterator_p.h>
+#include "qtreewidget.h"
+#include "qtreewidget_p.h"
+#include "qwidgetitemdata_p.h"
+
+#ifndef QT_NO_TREEWIDGET
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QTreeWidgetItemIterator
+ \ingroup model-view
+ \brief The QTreeWidgetItemIterator class provides a way to iterate over the
+ items in a QTreeWidget instance.
+
+ The iterator will walk the items in a pre-order traversal order, thus visiting the
+ parent node \e before it continues to the child nodes.
+
+ For example, the following code examples each item in a tree, checking the
+ text in the first column against a user-specified search string:
+
+ \snippet doc/src/snippets/qtreewidgetitemiterator-using/mainwindow.cpp 0
+
+ It is also possible to filter out certain types of node by passing certain
+ \l{IteratorFlag}{flags} to the constructor of QTreeWidgetItemIterator.
+
+ \sa QTreeWidget, {Model/View Programming}, QTreeWidgetItem
+*/
+
+/*!
+ Constructs an iterator for the same QTreeWidget as \a it. The
+ current iterator item is set to point on the current item of \a it.
+*/
+
+QTreeWidgetItemIterator::QTreeWidgetItemIterator(const QTreeWidgetItemIterator &it)
+ : d_ptr(new QTreeWidgetItemIteratorPrivate(*(it.d_ptr))),
+ current(it.current), flags(it.flags)
+{
+ Q_D(QTreeWidgetItemIterator);
+ Q_ASSERT(d->m_model);
+ d->m_model->iterators.append(this);
+}
+
+/*!
+ Constructs an iterator for the given \a widget that uses the specified \a flags
+ to determine which items are found during iteration.
+ The iterator is set to point to the first top-level item contained in the widget,
+ or the next matching item if the top-level item doesn't match the flags.
+
+ \sa QTreeWidgetItemIterator::IteratorFlag
+*/
+
+QTreeWidgetItemIterator::QTreeWidgetItemIterator(QTreeWidget *widget, IteratorFlags flags)
+: current(0), flags(flags)
+{
+ Q_ASSERT(widget);
+ QTreeModel *model = qobject_cast<QTreeModel*>(widget->model());
+ Q_ASSERT(model);
+ d_ptr = new QTreeWidgetItemIteratorPrivate(this, model);
+ model->iterators.append(this);
+ if (!model->rootItem->children.isEmpty()) current = model->rootItem->children.first();
+ if (current && !matchesFlags(current))
+ ++(*this);
+}
+
+/*!
+ Constructs an iterator for the given \a item that uses the specified \a flags
+ to determine which items are found during iteration.
+ The iterator is set to point to \a item, or the next matching item if \a item
+ doesn't match the flags.
+
+ \sa QTreeWidgetItemIterator::IteratorFlag
+*/
+
+QTreeWidgetItemIterator::QTreeWidgetItemIterator(QTreeWidgetItem *item, IteratorFlags flags)
+ : d_ptr(new QTreeWidgetItemIteratorPrivate(
+ this, qobject_cast<QTreeModel*>(item->view->model()))),
+ current(item), flags(flags)
+{
+ Q_D(QTreeWidgetItemIterator);
+ Q_ASSERT(item);
+ QTreeModel *model = qobject_cast<QTreeModel*>(item->view->model());
+ Q_ASSERT(model);
+ model->iterators.append(this);
+
+ // Initialize m_currentIndex and m_parentIndex as it would be if we had traversed from
+ // the beginning.
+ QTreeWidgetItem *parent = item;
+ parent = parent->parent();
+ QList<QTreeWidgetItem *> children = parent ? parent->children : d->m_model->rootItem->children;
+ d->m_currentIndex = children.indexOf(item);
+
+ while (parent) {
+ QTreeWidgetItem *itm = parent;
+ parent = parent->parent();
+ QList<QTreeWidgetItem *> children = parent ? parent->children : d->m_model->rootItem->children;
+ int index = children.indexOf(itm);
+ d->m_parentIndex.prepend(index);
+ }
+
+ if (current && !matchesFlags(current))
+ ++(*this);
+}
+
+/*!
+ Destroys the iterator.
+*/
+
+QTreeWidgetItemIterator::~QTreeWidgetItemIterator()
+{
+ d_func()->m_model->iterators.removeAll(this);
+ delete d_ptr;
+}
+
+/*!
+ Assignment. Makes a copy of \a it and returns a reference to its
+ iterator.
+*/
+
+QTreeWidgetItemIterator &QTreeWidgetItemIterator::operator=(const QTreeWidgetItemIterator &it)
+{
+ Q_D(QTreeWidgetItemIterator);
+ if (d_func()->m_model != it.d_func()->m_model) {
+ d_func()->m_model->iterators.removeAll(this);
+ it.d_func()->m_model->iterators.append(this);
+ }
+ current = it.current;
+ flags = it.flags;
+ d->operator=(*it.d_func());
+ return *this;
+}
+
+/*!
+ The prefix ++ operator (++it) advances the iterator to the next matching item
+ and returns a reference to the resulting iterator.
+ Sets the current pointer to 0 if the current item is the last matching item.
+*/
+
+QTreeWidgetItemIterator &QTreeWidgetItemIterator::operator++()
+{
+ if (current)
+ do {
+ current = d_func()->next(current);
+ } while (current && !matchesFlags(current));
+ return *this;
+}
+
+/*!
+ The prefix -- operator (--it) advances the iterator to the previous matching item
+ and returns a reference to the resulting iterator.
+ Sets the current pointer to 0 if the current item is the first matching item.
+*/
+
+QTreeWidgetItemIterator &QTreeWidgetItemIterator::operator--()
+{
+ if (current)
+ do {
+ current = d_func()->previous(current);
+ } while (current && !matchesFlags(current));
+ return *this;
+}
+
+/*!
+ \internal
+*/
+bool QTreeWidgetItemIterator::matchesFlags(const QTreeWidgetItem *item) const
+{
+ if (!item)
+ return false;
+
+ if (flags == All)
+ return true;
+
+ {
+ Qt::ItemFlags itemFlags = item->flags();
+ if ((flags & Selectable) && !(itemFlags & Qt::ItemIsSelectable))
+ return false;
+ if ((flags & NotSelectable) && (itemFlags & Qt::ItemIsSelectable))
+ return false;
+ if ((flags & DragEnabled) && !(itemFlags & Qt::ItemIsDragEnabled))
+ return false;
+ if ((flags & DragDisabled) && (itemFlags & Qt::ItemIsDragEnabled))
+ return false;
+ if ((flags & DropEnabled) && !(itemFlags & Qt::ItemIsDropEnabled))
+ return false;
+ if ((flags & DropDisabled) && (itemFlags & Qt::ItemIsDropEnabled))
+ return false;
+ if ((flags & Enabled) && !(itemFlags & Qt::ItemIsEnabled))
+ return false;
+ if ((flags & Disabled) && (itemFlags & Qt::ItemIsEnabled))
+ return false;
+ if ((flags & Editable) && !(itemFlags & Qt::ItemIsEditable))
+ return false;
+ if ((flags & NotEditable) && (itemFlags & Qt::ItemIsEditable))
+ return false;
+ }
+
+ if (flags & (Checked|NotChecked)) {
+ // ### We only test the check state for column 0
+ Qt::CheckState check = item->checkState(0);
+ // PartiallyChecked matches as Checked.
+ if ((flags & Checked) && (check == Qt::Unchecked))
+ return false;
+ if ((flags & NotChecked) && (check != Qt::Unchecked))
+ return false;
+ }
+
+ if ((flags & HasChildren) && !item->childCount())
+ return false;
+ if ((flags & NoChildren) && item->childCount())
+ return false;
+
+ if ((flags & Hidden) && !item->isHidden())
+ return false;
+ if ((flags & NotHidden) && item->isHidden())
+ return false;
+
+ if ((flags & Selected) && !item->isSelected())
+ return false;
+ if ((flags & Unselected) && item->isSelected())
+ return false;
+
+ return true;
+}
+
+/*
+ * Implementation of QTreeWidgetItemIteratorPrivate
+ */
+QTreeWidgetItem* QTreeWidgetItemIteratorPrivate::nextSibling(const QTreeWidgetItem* item) const
+{
+ Q_ASSERT(item);
+ QTreeWidgetItem *next = 0;
+ if (QTreeWidgetItem *par = item->parent()) {
+ int i = par->indexOfChild(const_cast<QTreeWidgetItem*>(item));
+ next = par->child(i + 1);
+ } else {
+ QTreeWidget *tw = item->treeWidget();
+ int i = tw->indexOfTopLevelItem(const_cast<QTreeWidgetItem*>(item));
+ next = tw->topLevelItem(i + 1);
+ }
+ return next;
+}
+
+QTreeWidgetItem *QTreeWidgetItemIteratorPrivate::next(const QTreeWidgetItem *current)
+{
+ if (!current) return 0;
+
+ QTreeWidgetItem *next = 0;
+ if (current->childCount()) {
+ // walk the child
+ m_parentIndex.push(m_currentIndex);
+ m_currentIndex = 0;
+ next = current->child(0);
+ } else {
+ // walk the sibling
+ QTreeWidgetItem *parent = current->parent();
+ next = parent ? parent->child(m_currentIndex + 1)
+ : m_model->rootItem->child(m_currentIndex + 1);
+ while (!next && parent) {
+ // if we had no sibling walk up the parent and try the sibling of that
+ parent = parent->parent();
+ m_currentIndex = m_parentIndex.pop();
+ next = parent ? parent->child(m_currentIndex + 1)
+ : m_model->rootItem->child(m_currentIndex + 1);
+ }
+ if (next) ++(m_currentIndex);
+ }
+ return next;
+}
+
+QTreeWidgetItem *QTreeWidgetItemIteratorPrivate::previous(const QTreeWidgetItem *current)
+{
+ if (!current) return 0;
+
+ QTreeWidgetItem *prev = 0;
+ // walk the previous sibling
+ QTreeWidgetItem *parent = current->parent();
+ prev = parent ? parent->child(m_currentIndex - 1)
+ : m_model->rootItem->child(m_currentIndex - 1);
+ if (prev) {
+ // Yes, we had a previous sibling but we need go down to the last leafnode.
+ --m_currentIndex;
+ while (prev && prev->childCount()) {
+ m_parentIndex.push(m_currentIndex);
+ m_currentIndex = prev->childCount() - 1;
+ prev = prev->child(m_currentIndex);
+ }
+ } else if (parent) {
+ m_currentIndex = m_parentIndex.pop();
+ prev = parent;
+ }
+ return prev;
+}
+
+void QTreeWidgetItemIteratorPrivate::ensureValidIterator(const QTreeWidgetItem *itemToBeRemoved)
+{
+ Q_Q(QTreeWidgetItemIterator);
+ Q_ASSERT(itemToBeRemoved);
+
+ if (!q->current) return;
+ QTreeWidgetItem *nextItem = q->current;
+
+ // Do not walk to the ancestor to find the other item if they have the same parent.
+ if (nextItem->parent() != itemToBeRemoved->parent()) {
+ while (nextItem->parent() && nextItem != itemToBeRemoved) {
+ nextItem = nextItem->parent();
+ }
+ }
+ // If the item to be removed is an ancestor of the current iterator item,
+ // we need to adjust the iterator.
+ if (nextItem == itemToBeRemoved) {
+ QTreeWidgetItem *parent = nextItem;
+ nextItem = 0;
+ while (parent && !nextItem) {
+ nextItem = nextSibling(parent);
+ parent = parent->parent();
+ }
+ if (nextItem) {
+ // Ooooh... Set the iterator to the next valid item
+ *q = QTreeWidgetItemIterator(nextItem, q->flags);
+ if (!(q->matchesFlags(nextItem))) ++(*q);
+ } else {
+ // set it to null.
+ q->current = 0;
+ m_parentIndex.clear();
+ return;
+ }
+ }
+ if (nextItem->parent() == itemToBeRemoved->parent()) {
+ // They have the same parent, i.e. we have to adjust the m_currentIndex member of the iterator
+ // if the deleted item is to the left of the nextItem.
+
+ QTreeWidgetItem *par = itemToBeRemoved->parent(); // We know they both have the same parent.
+ QTreeWidget *tw = itemToBeRemoved->treeWidget(); // ..and widget
+ int indexOfItemToBeRemoved = par ? par->indexOfChild(const_cast<QTreeWidgetItem *>(itemToBeRemoved))
+ : tw->indexOfTopLevelItem(const_cast<QTreeWidgetItem *>(itemToBeRemoved));
+ int indexOfNextItem = par ? par->indexOfChild(nextItem) : tw->indexOfTopLevelItem(nextItem);
+
+ if (indexOfItemToBeRemoved <= indexOfNextItem) {
+ // A sibling to the left of us was deleted, adjust the m_currentIndex member of the iterator.
+ // Note that the m_currentIndex will be wrong until the item is actually removed!
+ m_currentIndex--;
+ }
+ }
+}
+
+/*!
+ \fn const QTreeWidgetItemIterator QTreeWidgetItemIterator::operator++(int)
+
+ The postfix ++ operator (it++) advances the iterator to the next matching item
+ and returns an iterator to the previously current item.
+*/
+
+/*!
+ \fn QTreeWidgetItemIterator &QTreeWidgetItemIterator::operator+=(int n)
+
+ Makes the iterator go forward by \a n matching items. (If n is negative, the
+ iterator goes backward.)
+
+ If the current item is beyond the last item, the current item pointer is
+ set to 0. Returns the resulting iterator.
+*/
+
+/*!
+ \fn const QTreeWidgetItemIterator QTreeWidgetItemIterator::operator--(int)
+
+ The postfix -- operator (it--) makes the preceding matching item current and returns an iterator to the previously current item.
+*/
+
+/*!
+ \fn QTreeWidgetItemIterator &QTreeWidgetItemIterator::operator-=(int n)
+
+ Makes the iterator go backward by \a n matching items. (If n is negative, the
+ iterator goes forward.)
+
+ If the current item is ahead of the last item, the current item pointer is
+ set to 0. Returns the resulting iterator.
+*/
+
+/*!
+ \fn QTreeWidgetItem *QTreeWidgetItemIterator::operator*() const
+
+ Dereference operator. Returns a pointer to the current item.
+*/
+
+
+/*!
+ \enum QTreeWidgetItemIterator::IteratorFlag
+
+ These flags can be passed to a QTreeWidgetItemIterator 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 All
+ \value Hidden
+ \value NotHidden
+ \value Selected
+ \value Unselected
+ \value Selectable
+ \value NotSelectable
+ \value DragEnabled
+ \value DragDisabled
+ \value DropEnabled
+ \value DropDisabled
+ \value HasChildren
+ \value NoChildren
+ \value Checked
+ \value NotChecked
+ \value Enabled
+ \value Disabled
+ \value Editable
+ \value NotEditable
+ \value UserFlag
+*/
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_TREEWIDGET
diff --git a/src/gui/itemviews/qtreewidgetitemiterator.h b/src/gui/itemviews/qtreewidgetitemiterator.h
new file mode 100644
index 0000000000..8a76c691aa
--- /dev/null
+++ b/src/gui/itemviews/qtreewidgetitemiterator.h
@@ -0,0 +1,158 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QTREEWIDGETITEMITERATOR_H
+#define QTREEWIDGETITEMITERATOR_H
+
+#include <QtCore/qglobal.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+#ifndef QT_NO_TREEWIDGET
+
+class QTreeWidget;
+class QTreeWidgetItem;
+class QTreeModel;
+
+class QTreeWidgetItemIteratorPrivate;
+class Q_GUI_EXPORT QTreeWidgetItemIterator
+{
+ friend class QTreeModel;
+
+public:
+ enum IteratorFlag {
+ All = 0x00000000,
+ Hidden = 0x00000001,
+ NotHidden = 0x00000002,
+ Selected = 0x00000004,
+ Unselected = 0x00000008,
+ Selectable = 0x00000010,
+ NotSelectable = 0x00000020,
+ DragEnabled = 0x00000040,
+ DragDisabled = 0x00000080,
+ DropEnabled = 0x00000100,
+ DropDisabled = 0x00000200,
+ HasChildren = 0x00000400,
+ NoChildren = 0x00000800,
+ Checked = 0x00001000,
+ NotChecked = 0x00002000,
+ Enabled = 0x00004000,
+ Disabled = 0x00008000,
+ Editable = 0x00010000,
+ NotEditable = 0x00020000,
+ UserFlag = 0x01000000 // The first flag that can be used by the user.
+ };
+ Q_DECLARE_FLAGS(IteratorFlags, IteratorFlag)
+
+ QTreeWidgetItemIterator(const QTreeWidgetItemIterator &it);
+ explicit QTreeWidgetItemIterator(QTreeWidget *widget, IteratorFlags flags = All);
+ explicit QTreeWidgetItemIterator(QTreeWidgetItem *item, IteratorFlags flags = All);
+ ~QTreeWidgetItemIterator();
+
+ QTreeWidgetItemIterator &operator=(const QTreeWidgetItemIterator &it);
+
+ QTreeWidgetItemIterator &operator++();
+ inline const QTreeWidgetItemIterator operator++(int);
+ inline QTreeWidgetItemIterator &operator+=(int n);
+
+ QTreeWidgetItemIterator &operator--();
+ inline const QTreeWidgetItemIterator operator--(int);
+ inline QTreeWidgetItemIterator &operator-=(int n);
+
+ inline QTreeWidgetItem *operator*() const;
+
+private:
+ bool matchesFlags(const QTreeWidgetItem *item) const;
+ QTreeWidgetItemIteratorPrivate *d_ptr;
+ QTreeWidgetItem *current;
+ IteratorFlags flags;
+ Q_DECLARE_PRIVATE(QTreeWidgetItemIterator)
+};
+
+inline const QTreeWidgetItemIterator QTreeWidgetItemIterator::operator++(int)
+{
+ QTreeWidgetItemIterator it = *this;
+ ++(*this);
+ return it;
+}
+
+inline const QTreeWidgetItemIterator QTreeWidgetItemIterator::operator--(int)
+{
+ QTreeWidgetItemIterator it = *this;
+ --(*this);
+ return it;
+}
+
+inline QTreeWidgetItemIterator &QTreeWidgetItemIterator::operator+=(int n)
+{
+ if (n < 0)
+ return (*this) -= (-n);
+ while (current && n--)
+ ++(*this);
+ return *this;
+}
+
+inline QTreeWidgetItemIterator &QTreeWidgetItemIterator::operator-=(int n)
+{
+ if (n < 0)
+ return (*this) += (-n);
+ while (current && n--)
+ --(*this);
+ return *this;
+}
+
+inline QTreeWidgetItem *QTreeWidgetItemIterator::operator*() const
+{
+ return current;
+}
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(QTreeWidgetItemIterator::IteratorFlags)
+
+
+QT_END_NAMESPACE
+#endif // QT_NO_TREEWIDGET
+QT_END_HEADER
+
+#endif // QTREEWIDGETITEMITERATOR_H
diff --git a/src/gui/itemviews/qtreewidgetitemiterator_p.h b/src/gui/itemviews/qtreewidgetitemiterator_p.h
new file mode 100644
index 0000000000..cf800ec346
--- /dev/null
+++ b/src/gui/itemviews/qtreewidgetitemiterator_p.h
@@ -0,0 +1,109 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QTREEWIDGETITEMITERATOR_P_H
+#define QTREEWIDGETITEMITERATOR_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 <QtCore/qstack.h>
+
+#ifndef QT_NO_TREEWIDGET
+#include "qtreewidgetitemiterator.h"
+
+QT_BEGIN_NAMESPACE
+
+class QTreeModel;
+class QTreeWidgetItem;
+
+class QTreeWidgetItemIteratorPrivate {
+ Q_DECLARE_PUBLIC(QTreeWidgetItemIterator)
+public:
+ QTreeWidgetItemIteratorPrivate(QTreeWidgetItemIterator *q, QTreeModel *model)
+ : m_currentIndex(0), m_model(model), q_ptr(q)
+ {
+
+ }
+
+ QTreeWidgetItemIteratorPrivate(const QTreeWidgetItemIteratorPrivate& other)
+ : m_currentIndex(other.m_currentIndex), m_model(other.m_model), m_parentIndex(other.m_parentIndex)
+ {
+
+ }
+
+ QTreeWidgetItemIteratorPrivate &operator=(const QTreeWidgetItemIteratorPrivate& other)
+ {
+ m_currentIndex = other.m_currentIndex;
+ m_parentIndex = other.m_parentIndex;
+ m_model = other.m_model;
+ return (*this);
+ }
+
+ ~QTreeWidgetItemIteratorPrivate()
+ {
+ }
+
+ QTreeWidgetItem* nextSibling(const QTreeWidgetItem* item) const;
+ void ensureValidIterator(const QTreeWidgetItem *itemToBeRemoved);
+
+ QTreeWidgetItem *next(const QTreeWidgetItem *current);
+ QTreeWidgetItem *previous(const QTreeWidgetItem *current);
+private:
+ int m_currentIndex;
+ QTreeModel *m_model; // This iterator class should not have ownership of the model.
+ QStack<int> m_parentIndex;
+ QTreeWidgetItemIterator *q_ptr;
+};
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_TREEWIDGET
+
+#endif //QTREEWIDGETITEMITERATOR_P_H
diff --git a/src/gui/itemviews/qwidgetitemdata_p.h b/src/gui/itemviews/qwidgetitemdata_p.h
new file mode 100644
index 0000000000..32297fb5de
--- /dev/null
+++ b/src/gui/itemviews/qwidgetitemdata_p.h
@@ -0,0 +1,88 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QWIDGETITEMDATA_P_H
+#define QWIDGETITEMDATA_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.
+//
+
+QT_BEGIN_NAMESPACE
+
+class QWidgetItemData
+{
+public:
+ inline QWidgetItemData() : role(-1) {}
+ inline QWidgetItemData(int r, QVariant v) : role(r), value(v) {}
+ int role;
+ QVariant value;
+ inline bool operator==(const QWidgetItemData &other) { return role == other.role && value == other.value; }
+};
+
+#ifndef QT_NO_DATASTREAM
+
+inline QDataStream &operator>>(QDataStream &in, QWidgetItemData &data)
+{
+ in >> data.role;
+ in >> data.value;
+ return in;
+}
+
+inline QDataStream &operator<<(QDataStream &out, const QWidgetItemData &data)
+{
+ out << data.role;
+ out << data.value;
+ return out;
+}
+
+#endif // QT_NO_DATASTREAM
+
+QT_END_NAMESPACE
+
+#endif // QWIDGETITEMDATA_P_H
diff --git a/src/gui/kernel/kernel.pri b/src/gui/kernel/kernel.pri
new file mode 100644
index 0000000000..cb2b0148ec
--- /dev/null
+++ b/src/gui/kernel/kernel.pri
@@ -0,0 +1,209 @@
+# Qt kernel module
+
+# Only used on platforms with CONFIG += precompile_header
+PRECOMPILED_HEADER = kernel/qt_gui_pch.h
+
+
+KERNEL_P= kernel
+HEADERS += \
+ kernel/qaction.h \
+ kernel/qactiongroup.h \
+ kernel/qapplication.h \
+ kernel/qapplication_p.h \
+ kernel/qboxlayout.h \
+ kernel/qclipboard.h \
+ kernel/qcursor.h \
+ kernel/qdesktopwidget.h \
+ kernel/qdrag.h \
+ kernel/qdnd_p.h \
+ kernel/qevent.h \
+ kernel/qevent_p.h \
+ kernel/qformlayout.h \
+ kernel/qgridlayout.h \
+ kernel/qkeysequence.h \
+ kernel/qlayout.h \
+ kernel/qlayout_p.h \
+ kernel/qlayoutengine_p.h \
+ kernel/qlayoutitem.h \
+ kernel/qmime.h \
+ kernel/qsessionmanager.h \
+ kernel/qshortcut.h \
+ kernel/qshortcutmap_p.h \
+ kernel/qsizepolicy.h \
+ kernel/qpalette.h \
+ kernel/qsound.h \
+ kernel/qsound_p.h \
+ kernel/qstackedlayout.h \
+ kernel/qtooltip.h \
+ kernel/qwhatsthis.h \
+ kernel/qwidget.h \
+ kernel/qwidget_p.h \
+ kernel/qwidgetaction.h \
+ kernel/qwidgetaction_p.h \
+ kernel/qwindowdefs.h \
+ kernel/qkeymapper_p.h
+
+SOURCES += \
+ kernel/qaction.cpp \
+ kernel/qactiongroup.cpp \
+ kernel/qapplication.cpp \
+ kernel/qboxlayout.cpp \
+ kernel/qclipboard.cpp \
+ kernel/qcursor.cpp \
+ kernel/qdrag.cpp \
+ kernel/qdnd.cpp \
+ kernel/qevent.cpp \
+ kernel/qformlayout.cpp \
+ kernel/qgridlayout.cpp \
+ kernel/qkeysequence.cpp \
+ kernel/qlayout.cpp \
+ kernel/qlayoutengine.cpp \
+ kernel/qlayoutitem.cpp \
+ kernel/qmime.cpp \
+ kernel/qpalette.cpp \
+ kernel/qshortcut.cpp \
+ kernel/qshortcutmap.cpp \
+ kernel/qsound.cpp \
+ kernel/qstackedlayout.cpp \
+ kernel/qtooltip.cpp \
+ kernel/qguivariant.cpp \
+ kernel/qwhatsthis.cpp \
+ kernel/qwidget.cpp \
+ kernel/qwidgetaction.cpp \
+ kernel/qkeymapper.cpp
+
+win32 {
+ DEFINES += QT_NO_DIRECTDRAW
+
+ SOURCES += \
+ kernel/qapplication_win.cpp \
+ kernel/qclipboard_win.cpp \
+ kernel/qcursor_win.cpp \
+ kernel/qdesktopwidget_win.cpp \
+ kernel/qdnd_win.cpp \
+ kernel/qmime_win.cpp \
+ kernel/qsound_win.cpp \
+ kernel/qwidget_win.cpp \
+ kernel/qole_win.cpp \
+ kernel/qkeymapper_win.cpp
+
+ !contains(DEFINES, QT_NO_DIRECTDRAW):LIBS += ddraw.lib
+}
+
+unix:x11 {
+ INCLUDEPATH += ../3rdparty/xorg
+ HEADERS += \
+ kernel/qx11embed_x11.h \
+ kernel/qx11info_x11.h
+
+ SOURCES += \
+ kernel/qapplication_x11.cpp \
+ kernel/qclipboard_x11.cpp \
+ kernel/qcursor_x11.cpp \
+ kernel/qdnd_x11.cpp \
+ kernel/qdesktopwidget_x11.cpp \
+ kernel/qmotifdnd_x11.cpp \
+ kernel/qsound_x11.cpp \
+ kernel/qwidget_x11.cpp \
+ kernel/qwidgetcreate_x11.cpp \
+ kernel/qx11embed_x11.cpp \
+ kernel/qx11info_x11.cpp \
+ kernel/qkeymapper_x11.cpp
+
+ contains(QT_CONFIG, glib) {
+ SOURCES += \
+ kernel/qguieventdispatcher_glib.cpp
+ HEADERS += \
+ kernel/qguieventdispatcher_glib_p.h
+ QMAKE_CXXFLAGS += $$QT_CFLAGS_GLIB
+ LIBS +=$$QT_LIBS_GLIB
+ }
+ SOURCES += \
+ kernel/qeventdispatcher_x11.cpp
+ HEADERS += \
+ kernel/qeventdispatcher_x11_p.h
+}
+
+embedded {
+ HEADERS += \
+ kernel/qeventdispatcher_qws_p.h
+
+ SOURCES += \
+ kernel/qapplication_qws.cpp \
+ kernel/qclipboard_qws.cpp \
+ kernel/qcursor_qws.cpp \
+ kernel/qdesktopwidget_qws.cpp \
+ kernel/qdnd_qws.cpp \
+ kernel/qeventdispatcher_qws.cpp \
+ kernel/qsound_qws.cpp \
+ kernel/qwidget_qws.cpp \
+ kernel/qkeymapper_qws.cpp \
+ kernel/qsessionmanager_qws.cpp
+
+ contains(QT_CONFIG, glib) {
+ SOURCES += \
+ kernel/qeventdispatcher_glib_qws.cpp
+ HEADERS += \
+ kernel/qeventdispatcher_glib_qws_p.h
+ QMAKE_CXXFLAGS += $$QT_CFLAGS_GLIB
+ }
+
+
+}
+
+!embedded:!x11:mac {
+ SOURCES += \
+ kernel/qclipboard_mac.cpp \
+ kernel/qmime_mac.cpp \
+ kernel/qt_mac.cpp \
+ kernel/qkeymapper_mac.cpp
+
+ OBJECTIVE_HEADERS += \
+ qcocoawindow_mac_p.h \
+ qcocoawindowdelegate_mac_p.h \
+ qcocoaview_mac_p.h \
+ qcocoaapplication_mac_p.h \
+ qcocoaapplicationdelegate_mac_p.h \
+
+ OBJECTIVE_SOURCES += \
+ kernel/qcursor_mac.mm \
+ kernel/qdnd_mac.mm \
+ kernel/qsound_mac.mm \
+ kernel/qapplication_mac.mm \
+ kernel/qwidget_mac.mm \
+ kernel/qcocoapanel_mac.mm \
+ kernel/qcocoaview_mac.mm \
+ kernel/qcocoawindow_mac.mm \
+ kernel/qcocoawindowdelegate_mac.mm \
+ kernel/qcocoamenuloader_mac.mm \
+ kernel/qcocoaapplication_mac.mm \
+ kernel/qcocoaapplicationdelegate_mac.mm \
+ kernel/qt_cocoa_helpers_mac.mm \
+ kernel/qdesktopwidget_mac.mm \
+ kernel/qeventdispatcher_mac.mm \
+ kernel/qcocoawindowcustomthemeframe_mac.mm
+
+ HEADERS += \
+ kernel/qt_cocoa_helpers_mac_p.h \
+ kernel/qcocoaapplication_mac_p.h \
+ kernel/qcocoaapplicationdelegate_mac_p.h \
+ kernel/qeventdispatcher_mac_p.h
+
+ MENU_NIB.files = mac/qt_menu.nib
+ MENU_NIB.path = Resources
+ MENU_NIB.version = Versions
+ QMAKE_BUNDLE_DATA += MENU_NIB
+ RESOURCES += mac/maccursors.qrc
+
+ LIBS += -framework AppKit
+}
+
+wince*: {
+ HEADERS += \
+ ../corelib/kernel/qfunctions_wince.h \
+ kernel/qguifunctions_wince.h
+
+ SOURCES += \
+ ../corelib/kernel/qfunctions_wince.cpp \
+ kernel/qguifunctions_wince.cpp
+}
diff --git a/src/gui/kernel/mac.pri b/src/gui/kernel/mac.pri
new file mode 100644
index 0000000000..415fe0aec5
--- /dev/null
+++ b/src/gui/kernel/mac.pri
@@ -0,0 +1,4 @@
+!x11:!embedded:mac {
+ LIBS += -framework Carbon -lz
+ *-mwerks:INCLUDEPATH += compat
+}
diff --git a/src/gui/kernel/qaction.cpp b/src/gui/kernel/qaction.cpp
new file mode 100644
index 0000000000..abb17d7a63
--- /dev/null
+++ b/src/gui/kernel/qaction.cpp
@@ -0,0 +1,1396 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qaction.h"
+#include "qactiongroup.h"
+
+#ifndef QT_NO_ACTION
+#include "qaction_p.h"
+#include "qapplication.h"
+#include "qevent.h"
+#include "qlist.h"
+#include "qdebug.h"
+#include <private/qshortcutmap_p.h>
+#include <private/qapplication_p.h>
+#include <private/qmenu_p.h>
+
+#define QAPP_CHECK(functionName) \
+ if (!qApp) { \
+ qWarning("QAction: Initialize QApplication before calling '" functionName "'."); \
+ return; \
+ }
+
+QT_BEGIN_NAMESPACE
+
+/*
+ internal: guesses a descriptive text from a text suited for a menu entry
+ */
+static QString qt_strippedText(QString s)
+{
+ s.remove( QString::fromLatin1("...") );
+ int i = 0;
+ while (i < s.size()) {
+ ++i;
+ if (s.at(i-1) != QLatin1Char('&'))
+ continue;
+ if (i < s.size() && s.at(i) == QLatin1Char('&'))
+ ++i;
+ s.remove(i-1,1);
+ }
+ return s.trimmed();
+};
+
+
+QActionPrivate::QActionPrivate() : group(0), enabled(1), forceDisabled(0),
+ visible(1), forceInvisible(0), checkable(0), checked(0), separator(0), fontSet(false),
+ menuRole(QAction::TextHeuristicRole), iconVisibleInMenu(-1)
+{
+#ifdef QT3_SUPPORT
+ static int qt_static_action_id = -1;
+ param = id = --qt_static_action_id;
+ act_signal = 0;
+#endif
+#ifndef QT_NO_SHORTCUT
+ shortcutId = 0;
+ shortcutContext = Qt::WindowShortcut;
+ autorepeat = true;
+#endif
+}
+
+QActionPrivate::~QActionPrivate()
+{
+}
+
+void QActionPrivate::sendDataChanged()
+{
+ Q_Q(QAction);
+ QActionEvent e(QEvent::ActionChanged, q);
+ for (int i = 0; i < widgets.size(); ++i) {
+ QWidget *w = widgets.at(i);
+ QApplication::sendEvent(w, &e);
+ }
+#ifndef QT_NO_GRAPHICSVIEW
+ for (int i = 0; i < graphicsWidgets.size(); ++i) {
+ QGraphicsWidget *w = graphicsWidgets.at(i);
+ QApplication::sendEvent(w, &e);
+ }
+#endif
+ QApplication::sendEvent(q, &e);
+
+ emit q->changed();
+}
+
+#ifndef QT_NO_SHORTCUT
+void QActionPrivate::redoGrab(QShortcutMap &map)
+{
+ Q_Q(QAction);
+ if (shortcutId)
+ map.removeShortcut(shortcutId, q);
+ if (shortcut.isEmpty())
+ return;
+ shortcutId = map.addShortcut(q, shortcut, shortcutContext);
+ if (!enabled)
+ map.setShortcutEnabled(false, shortcutId, q);
+ if (!autorepeat)
+ map.setShortcutAutoRepeat(false, shortcutId, q);
+}
+
+void QActionPrivate::redoGrabAlternate(QShortcutMap &map)
+{
+ Q_Q(QAction);
+ foreach (int id, alternateShortcutIds)
+ if (id)
+ map.removeShortcut(id, q);
+ alternateShortcutIds.clear();
+ if (alternateShortcuts.isEmpty())
+ return;
+ foreach (const QKeySequence& alternate, alternateShortcuts) {
+ if (!alternate.isEmpty())
+ alternateShortcutIds.append(map.addShortcut(q, alternate, shortcutContext));
+ else
+ alternateShortcutIds.append(0);
+ }
+ if (!enabled) {
+ foreach (int id, alternateShortcutIds)
+ map.setShortcutEnabled(false, id, q);
+ }
+ if (!autorepeat) {
+ foreach (int id, alternateShortcutIds)
+ map.setShortcutAutoRepeat(false, id, q);
+ }
+}
+
+void QActionPrivate::setShortcutEnabled(bool enable, QShortcutMap &map)
+{
+ Q_Q(QAction);
+ if (shortcutId)
+ map.setShortcutEnabled(enable, shortcutId, q);
+ foreach (int id, alternateShortcutIds)
+ if (id)
+ map.setShortcutEnabled(enable, id, q);
+}
+#endif // QT_NO_SHORTCUT
+
+
+/*!
+ \class QAction
+ \brief The QAction class provides an abstract user interface
+ action that can be inserted into widgets.
+
+ \ingroup application
+ \mainclass
+
+ \omit
+ * parent and widget are different
+ * parent does not define context
+ \endomit
+
+ In applications many common commands can be invoked via menus,
+ toolbar buttons, and keyboard shortcuts. Since the user expects
+ each command to be performed in the same way, regardless of the
+ user interface used, it is useful to represent each command as
+ an \e action.
+
+ Actions can be added to menus and toolbars, and will
+ automatically keep them in sync. For example, in a word processor,
+ if the user presses a Bold toolbar button, the Bold menu item
+ will automatically be checked.
+
+ Actions can be created as independent objects, but they may
+ also be created during the construction of menus; the QMenu class
+ contains convenience functions for creating actions suitable for
+ use as menu items.
+
+ A QAction may contain an icon, menu text, a shortcut, status text,
+ "What's This?" text, and a tooltip. Most of these can be set in
+ the constructor. They can also be set independently with
+ setIcon(), setText(), setIconText(), setShortcut(),
+ setStatusTip(), setWhatsThis(), and setToolTip(). For menu items,
+ it is possible to set an individual font with setFont().
+
+ Actions are added to widgets using QWidget::addAction() or
+ QGraphicsWidget::addAction(). Note that an action must be added to a
+ widget before it can be used; this is also true when the shortcut should
+ be global (i.e., Qt::ApplicationShortcut as Qt::ShortcutContext).
+
+ Once a QAction has been created it should be added to the relevant
+ menu and toolbar, then connected to the slot which will perform
+ the action. For example:
+
+ \snippet examples/mainwindows/application/mainwindow.cpp 19
+ \codeline
+ \snippet examples/mainwindows/application/mainwindow.cpp 28
+ \snippet examples/mainwindows/application/mainwindow.cpp 31
+
+ We recommend that actions are created as children of the window
+ they are used in. In most cases actions will be children of
+ the application's main window.
+
+ \sa QMenu, QToolBar, {Application Example}
+*/
+
+/*!
+ \fn void QAction::trigger()
+
+ This is a convenience slot that calls activate(Trigger).
+*/
+
+/*!
+ \fn void QAction::hover()
+
+ This is a convenience slot that calls activate(Hover).
+*/
+
+/*!
+ \enum QAction::MenuRole
+
+ This enum describes how an action should be moved into the application menu on Mac OS X.
+
+ \value NoRole This action should not be put into the application menu
+ \value TextHeuristicRole This action should be put in the application menu based on the action's text
+ as described in the QMenuBar documentation.
+ \value ApplicationSpecificRole This action should be put in the application menu with an application specific role
+ \value AboutQtRole This action matches handles the "About Qt" menu item.
+ \value AboutRole This action should be placed where the "About" menu item is in the application menu.
+ \value PreferencesRole This action should be placed where the "Preferences..." menu item is in the application menu.
+ \value QuitRole This action should be placed where the Quit menu item is in the application menu.
+*/
+
+/*!
+ Constructs an action with \a parent. If \a parent is an action
+ group the action will be automatically inserted into the group.
+*/
+QAction::QAction(QObject* parent)
+ : QObject(*(new QActionPrivate), parent)
+{
+ Q_D(QAction);
+ d->group = qobject_cast<QActionGroup *>(parent);
+ if (d->group)
+ d->group->addAction(this);
+}
+
+
+/*!
+ Constructs an action with some \a text and \a parent. If \a
+ parent is an action group the action will be automatically
+ inserted into the group.
+
+ The action uses a stripped version of \a text (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 will be used for
+ tooltips unless you specify a different text using
+ setToolTip().
+
+*/
+QAction::QAction(const QString &text, QObject* parent)
+ : QObject(*(new QActionPrivate), parent)
+{
+ Q_D(QAction);
+ d->text = text;
+ d->group = qobject_cast<QActionGroup *>(parent);
+ if (d->group)
+ d->group->addAction(this);
+}
+
+/*!
+ Constructs an action with an \a icon and some \a text and \a
+ parent. If \a parent is an action group the action will be
+ automatically inserted into the group.
+
+ The action uses a stripped version of \a text (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 will be used for
+ tooltips unless you specify a different text using
+ setToolTip().
+*/
+QAction::QAction(const QIcon &icon, const QString &text, QObject* parent)
+ : QObject(*(new QActionPrivate), parent)
+{
+ Q_D(QAction);
+ d->icon = icon;
+ d->text = text;
+ d->group = qobject_cast<QActionGroup *>(parent);
+ if (d->group)
+ d->group->addAction(this);
+}
+
+/*!
+ \internal
+*/
+QAction::QAction(QActionPrivate &dd, QObject *parent)
+ : QObject(dd, parent)
+{
+ Q_D(QAction);
+ d->group = qobject_cast<QActionGroup *>(parent);
+ if (d->group)
+ d->group->addAction(this);
+}
+
+/*!
+ Returns the parent widget.
+*/
+QWidget *QAction::parentWidget() const
+{
+ QObject *ret = parent();
+ while (ret && !ret->isWidgetType())
+ ret = ret->parent();
+ return (QWidget*)ret;
+}
+
+/*!
+ \since 4.2
+ Returns a list of widgets this action has been added to.
+
+ \sa QWidget::addAction(), associatedGraphicsWidgets()
+*/
+QList<QWidget *> QAction::associatedWidgets() const
+{
+ Q_D(const QAction);
+ return d->widgets;
+}
+
+#ifndef QT_NO_GRAPHICSVIEW
+/*!
+ \since 4.5
+ Returns a list of widgets this action has been added to.
+
+ \sa QWidget::addAction(), associatedWidgets()
+*/
+QList<QGraphicsWidget *> QAction::associatedGraphicsWidgets() const
+{
+ Q_D(const QAction);
+ return d->graphicsWidgets;
+}
+#endif
+
+#ifndef QT_NO_SHORTCUT
+/*!
+ \property QAction::shortcut
+ \brief the action's primary shortcut key
+
+ Valid keycodes for this property can be found in \l Qt::Key and
+ \l Qt::Modifier. There is no default shortcut key.
+*/
+void QAction::setShortcut(const QKeySequence &shortcut)
+{
+ QAPP_CHECK("setShortcut");
+
+ Q_D(QAction);
+ if (d->shortcut == shortcut)
+ return;
+
+ d->shortcut = shortcut;
+ d->redoGrab(qApp->d_func()->shortcutMap);
+ d->sendDataChanged();
+}
+
+/*!
+ \since 4.2
+
+ Sets \a shortcuts as the list of shortcuts that trigger the
+ action. The first element of the list is the primary shortcut.
+
+ \sa shortcut
+*/
+void QAction::setShortcuts(const QList<QKeySequence> &shortcuts)
+{
+ Q_D(QAction);
+
+ QList <QKeySequence> listCopy = shortcuts;
+
+ QKeySequence primary;
+ if (!listCopy.isEmpty())
+ primary = listCopy.takeFirst();
+
+ if (d->shortcut == primary && d->alternateShortcuts == listCopy)
+ return;
+
+ QAPP_CHECK("setShortcuts");
+
+ d->shortcut = primary;
+ d->alternateShortcuts = listCopy;
+ d->redoGrab(qApp->d_func()->shortcutMap);
+ d->redoGrabAlternate(qApp->d_func()->shortcutMap);
+ d->sendDataChanged();
+}
+
+/*!
+ \since 4.2
+
+ Sets a platform dependent list of shortcuts based on the \a key.
+ The result of calling this function will depend on the currently running platform.
+ Note that more than one shortcut can assigned by this action.
+ If only the primary shortcut is required, use setShortcut instead.
+
+ \sa QKeySequence::keyBindings()
+*/
+void QAction::setShortcuts(QKeySequence::StandardKey key)
+{
+ QList <QKeySequence> list = QKeySequence::keyBindings(key);
+ setShortcuts(list);
+}
+
+/*!
+ Returns the primary shortcut.
+
+ \sa setShortcuts()
+*/
+QKeySequence QAction::shortcut() const
+{
+ Q_D(const QAction);
+ return d->shortcut;
+}
+
+/*!
+ \since 4.2
+
+ Returns the list of shortcuts, with the primary shortcut as
+ the first element of the list.
+
+ \sa setShortcuts()
+*/
+QList<QKeySequence> QAction::shortcuts() const
+{
+ Q_D(const QAction);
+ QList <QKeySequence> shortcuts;
+ if (!d->shortcut.isEmpty())
+ shortcuts << d->shortcut;
+ if (!d->alternateShortcuts.isEmpty())
+ shortcuts << d->alternateShortcuts;
+ return shortcuts;
+}
+
+/*!
+ \property QAction::shortcutContext
+ \brief the context for the action's shortcut
+
+ Valid values for this property can be found in \l Qt::ShortcutContext.
+ The default value is Qt::WindowShortcut.
+*/
+void QAction::setShortcutContext(Qt::ShortcutContext context)
+{
+ Q_D(QAction);
+ if (d->shortcutContext == context)
+ return;
+ QAPP_CHECK("setShortcutContext");
+ d->shortcutContext = context;
+ d->redoGrab(qApp->d_func()->shortcutMap);
+ d->redoGrabAlternate(qApp->d_func()->shortcutMap);
+ d->sendDataChanged();
+}
+
+Qt::ShortcutContext QAction::shortcutContext() const
+{
+ Q_D(const QAction);
+ return d->shortcutContext;
+}
+
+/*!
+ \property QAction::autoRepeat
+ \brief whether the action can auto repeat
+ \since 4.2
+
+ If true, the action will auto repeat when the keyboard shortcut
+ combination is held down, provided that keyboard auto repeat is
+ enabled on the system.
+ The default value is true.
+*/
+void QAction::setAutoRepeat(bool on)
+{
+ Q_D(QAction);
+ if (d->autorepeat == on)
+ return;
+ QAPP_CHECK("setAutoRepeat");
+ d->autorepeat = on;
+ d->redoGrab(qApp->d_func()->shortcutMap);
+ d->redoGrabAlternate(qApp->d_func()->shortcutMap);
+ d->sendDataChanged();
+}
+
+bool QAction::autoRepeat() const
+{
+ Q_D(const QAction);
+ return d->autorepeat;
+}
+#endif // QT_NO_SHORTCUT
+
+/*!
+ \property QAction::font
+ \brief the action's font
+
+ The font property is used to render the text set on the
+ QAction. The font will can be considered a hint as it will not be
+ consulted in all cases based upon application and style.
+
+ By default, this property contains the application's default font.
+
+ \sa QAction::setText() QStyle
+*/
+void QAction::setFont(const QFont &font)
+{
+ Q_D(QAction);
+ if (d->font == font)
+ return;
+
+ d->fontSet = true;
+ d->font = font;
+ d->sendDataChanged();
+}
+
+QFont QAction::font() const
+{
+ Q_D(const QAction);
+ return d->font;
+}
+
+#ifdef QT3_SUPPORT
+/*!
+ Use one of the QAction constructors that doesn't take a \a name
+ argument and call setObjectName() instead.
+*/
+QAction::QAction(QObject* parent, const char* name)
+ : QObject(*(new QActionPrivate), parent)
+{
+ Q_D(QAction);
+ setObjectName(QString::fromAscii(name));
+ d->group = qobject_cast<QActionGroup *>(parent);
+ if (d->group)
+ d->group->addAction(this);
+}
+
+
+/*!
+ Use one of the QAction constructors that doesn't take a \a name
+ argument and call setObjectName() instead.
+*/
+QAction::QAction(const QString &text, const QKeySequence &shortcut, QObject* parent, const char* name)
+ : QObject(*(new QActionPrivate), parent)
+{
+ Q_D(QAction);
+ setObjectName(QString::fromAscii(name));
+ d->text = text;
+ setShortcut(shortcut);
+ d->group = qobject_cast<QActionGroup *>(parent);
+ if (d->group)
+ d->group->addAction(this);
+}
+
+/*!
+ Use one of the QAction constructors that doesn't take a \a name
+ argument and call setObjectName() instead.
+*/
+QAction::QAction(const QIcon &icon, const QString &text, const QKeySequence &shortcut,
+ QObject* parent, const char* name)
+ : QObject(*(new QActionPrivate), parent)
+{
+ Q_D(QAction);
+ setObjectName(QString::fromAscii(name));
+ d->text = text;
+ setShortcut(shortcut);
+ d->icon = icon;
+ d->group = qobject_cast<QActionGroup *>(parent);
+ if (d->group)
+ d->group->addAction(this);
+}
+#endif
+
+/*!
+ Destroys the object and frees allocated resources.
+*/
+QAction::~QAction()
+{
+ Q_D(QAction);
+ for (int i = d->widgets.size()-1; i >= 0; --i) {
+ QWidget *w = d->widgets.at(i);
+ w->removeAction(this);
+ }
+#ifndef QT_NO_GRAPHICSVIEW
+ for (int i = d->graphicsWidgets.size()-1; i >= 0; --i) {
+ QGraphicsWidget *w = d->graphicsWidgets.at(i);
+ w->removeAction(this);
+ }
+#endif
+ if (d->group)
+ d->group->removeAction(this);
+#ifndef QT_NO_SHORTCUT
+ if (d->shortcutId && qApp) {
+ qApp->d_func()->shortcutMap.removeShortcut(d->shortcutId, this);
+ foreach (int id, d->alternateShortcutIds)
+ qApp->d_func()->shortcutMap.removeShortcut(id, this);
+ }
+#endif
+}
+
+/*!
+ Sets this action group to \a group. The action will be automatically
+ added to the group's list of actions.
+
+ Actions within the group will be mutually exclusive.
+
+ \sa QActionGroup, QAction::actionGroup()
+*/
+void QAction::setActionGroup(QActionGroup *group)
+{
+ Q_D(QAction);
+ if(group == d->group)
+ return;
+
+ if(d->group)
+ d->group->removeAction(this);
+ d->group = group;
+ if(group)
+ group->addAction(this);
+}
+
+/*!
+ Returns the action group for this action. If no action group manages
+ this action then 0 will be returned.
+
+ \sa QActionGroup, QAction::setActionGroup()
+*/
+QActionGroup *QAction::actionGroup() const
+{
+ Q_D(const QAction);
+ return d->group;
+}
+
+
+/*!
+ \property QAction::icon
+ \brief the action's icon
+
+ In toolbars, the icon is used as the tool button icon; in menus,
+ it is displayed 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.
+*/
+void QAction::setIcon(const QIcon &icon)
+{
+ Q_D(QAction);
+ d->icon = icon;
+ d->sendDataChanged();
+}
+
+QIcon QAction::icon() const
+{
+ Q_D(const QAction);
+ return d->icon;
+}
+
+#ifndef QT_NO_MENU
+/*!
+ Returns the menu contained by this action. Actions that contain
+ menus can be used to create menu items with submenus, or inserted
+ into toolbars to create buttons with popup menus.
+
+ \sa QMenu::addAction()
+*/
+QMenu *QAction::menu() const
+{
+ Q_D(const QAction);
+ return d->menu;
+}
+
+/*!
+ Sets the menu contained by this action to the specified \a menu.
+*/
+void QAction::setMenu(QMenu *menu)
+{
+ Q_D(QAction);
+ if (d->menu)
+ d->menu->d_func()->setOverrideMenuAction(0); //we reset the default action of any previous menu
+ d->menu = menu;
+ if (menu)
+ menu->d_func()->setOverrideMenuAction(this);
+ d->sendDataChanged();
+}
+#endif // QT_NO_MENU
+
+/*!
+ If \a b is true then this action will be considered a separator.
+
+ How a separator is represented depends on the widget it is inserted
+ into. Under most circumstances the text, submenu, and icon will be
+ ignored for separator actions.
+
+ \sa QAction::isSeparator()
+*/
+void QAction::setSeparator(bool b)
+{
+ Q_D(QAction);
+ if (d->separator == b)
+ return;
+
+ d->separator = b;
+ d->sendDataChanged();
+}
+
+/*!
+ Returns true if this action is a separator action; otherwise it
+ returns false.
+
+ \sa QAction::setSeparator()
+*/
+bool QAction::isSeparator() const
+{
+ Q_D(const QAction);
+ return d->separator;
+}
+
+/*!
+ \property QAction::text
+ \brief the action's descriptive text
+
+ If the action is added to a menu, the menu option will consist of
+ the icon (if there is one), the text, and the shortcut (if there
+ is one). If the text is not explicitly set in the constructor, or
+ by using setText(), the action's description icon text will be
+ used as text. There is no default text.
+
+ \sa iconText
+*/
+void QAction::setText(const QString &text)
+{
+ Q_D(QAction);
+ if (d->text == text)
+ return;
+
+ d->text = text;
+ d->sendDataChanged();
+}
+
+QString QAction::text() const
+{
+ Q_D(const QAction);
+ QString s = d->text;
+ if(s.isEmpty()) {
+ s = d->iconText;
+ s.replace(QLatin1Char('&'), QLatin1String("&&"));
+ }
+ return s;
+}
+
+
+
+
+
+/*!
+ \property QAction::iconText
+ \brief the action's descriptive icon text
+
+ If QToolBar::toolButtonStyle is set to a value that permits text to
+ be displayed, the text defined held in this property appears as a
+ label in the relevant tool button.
+
+ It also serves as the default text in menus and tooltips if the action
+ has not been defined with setText() or setToolTip(), and will
+ also be used in toolbar buttons if no icon has been defined using setIcon().
+
+ If the icon text is not explicitly set, the action's normal text will be
+ used for the icon text.
+
+ By default, this property contains an empty string.
+
+ \sa setToolTip(), setStatusTip()
+*/
+void QAction::setIconText(const QString &text)
+{
+ Q_D(QAction);
+ if (d->iconText == text)
+ return;
+
+ d->iconText = text;
+ d->sendDataChanged();
+}
+
+QString QAction::iconText() const
+{
+ Q_D(const QAction);
+ if (d->iconText.isEmpty())
+ return qt_strippedText(d->text);
+ return d->iconText;
+}
+
+/*!
+ \property QAction::toolTip
+ \brief the action's tooltip
+
+ This text is used for the tooltip. If no tooltip is specified,
+ the action's text is used.
+
+ By default, this property contains the action's text.
+
+ \sa setStatusTip() setShortcut()
+*/
+void QAction::setToolTip(const QString &tooltip)
+{
+ Q_D(QAction);
+ if (d->tooltip == tooltip)
+ return;
+
+ d->tooltip = tooltip;
+ d->sendDataChanged();
+}
+
+QString QAction::toolTip() const
+{
+ Q_D(const QAction);
+ if (d->tooltip.isEmpty()) {
+ if (!d->text.isEmpty())
+ return qt_strippedText(d->text);
+ return qt_strippedText(d->iconText);
+ }
+ return d->tooltip;
+}
+
+/*!
+ \property QAction::statusTip
+ \brief the action's status tip
+
+ The status tip is displayed on all status bars provided by the
+ action's top-level parent widget.
+
+ By default, this property contains an empty string.
+
+ \sa setToolTip() showStatusText()
+*/
+void QAction::setStatusTip(const QString &statustip)
+{
+ Q_D(QAction);
+ if (d->statustip == statustip)
+ return;
+
+ d->statustip = statustip;
+ d->sendDataChanged();
+}
+
+QString QAction::statusTip() const
+{
+ Q_D(const QAction);
+ return d->statustip;
+}
+
+/*!
+ \property QAction::whatsThis
+ \brief the action's "What's This?" help text
+
+ The "What's This?" text is used to provide a brief description of
+ the action. The text may contain rich text. There is no default
+ "What's This?" text.
+
+ \sa QWhatsThis Q3StyleSheet
+*/
+void QAction::setWhatsThis(const QString &whatsthis)
+{
+ Q_D(QAction);
+ if (d->whatsthis == whatsthis)
+ return;
+
+ d->whatsthis = whatsthis;
+ d->sendDataChanged();
+}
+
+QString QAction::whatsThis() const
+{
+ Q_D(const QAction);
+ return d->whatsthis;
+}
+
+
+/*!
+ \property QAction::checkable
+ \brief whether the action is a checkable action
+
+ A checkable action is one which has an on/off state. For example,
+ in a word processor, a Bold toolbar button may be 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.
+ By default, this property 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
+ QActionGroup with the QActionGroup::exclusive property set to
+ true.
+
+ \sa QAction::setChecked()
+*/
+void QAction::setCheckable(bool b)
+{
+ Q_D(QAction);
+ if (d->checkable == b)
+ return;
+
+ d->checkable = b;
+ d->checked = false;
+ d->sendDataChanged();
+}
+
+bool QAction::isCheckable() const
+{
+ Q_D(const QAction);
+ return d->checkable;
+}
+
+/*!
+ \fn void QAction::toggle()
+
+ This is a convenience function for the \l checked property.
+ Connect to it to change the checked state to its opposite state.
+*/
+void QAction::toggle()
+{
+ Q_D(QAction);
+ setChecked(!d->checked);
+}
+
+/*!
+ \property QAction::checked
+ \brief whether the action is checked.
+
+ Only checkable actions can be checked. By default, this is false
+ (the action is unchecked).
+
+ \sa checkable
+*/
+void QAction::setChecked(bool b)
+{
+ Q_D(QAction);
+ if (!d->checkable || d->checked == b)
+ return;
+
+ QPointer<QAction> guard(this);
+ d->checked = b;
+ d->sendDataChanged();
+ if (guard)
+ emit toggled(b);
+}
+
+bool QAction::isChecked() const
+{
+ Q_D(const QAction);
+ return d->checked;
+}
+
+/*!
+ \fn void QAction::setDisabled(bool b)
+
+ This is a convenience function for the \l enabled property, that
+ is useful for signals--slots connections. If \a b is true the
+ action is disabled; otherwise it is enabled.
+*/
+
+/*!
+ \property QAction::enabled
+ \brief whether the action is enabled
+
+ Disabled actions cannot be chosen by the user. They do not
+ disappear from menus or toolbars, but they are displayed in a way
+ which indicates that they are unavailable. For example, they might
+ be displayed using only shades of gray.
+
+ \gui{What's This?} help on disabled actions is still available, provided
+ that the QAction::whatsThis property is set.
+
+ An action will be disabled when all widgets to which it is added
+ (with QWidget::addAction()) are disabled or not visible. When an
+ action is disabled, it is not possible to trigger it through its
+ shortcut.
+
+ By default, this property is true (actions are enabled).
+
+ \sa text
+*/
+void QAction::setEnabled(bool b)
+{
+ Q_D(QAction);
+ if (b == d->enabled && b != d->forceDisabled)
+ return;
+ d->forceDisabled = !b;
+ if (b && (!d->visible || (d->group && !d->group->isEnabled())))
+ return;
+ QAPP_CHECK("setEnabled");
+ d->enabled = b;
+#ifndef QT_NO_SHORTCUT
+ d->setShortcutEnabled(b, qApp->d_func()->shortcutMap);
+#endif
+ d->sendDataChanged();
+}
+
+bool QAction::isEnabled() const
+{
+ Q_D(const QAction);
+ return d->enabled;
+}
+
+/*!
+ \property QAction::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.
+
+ By default, this property is true (actions are visible).
+*/
+void QAction::setVisible(bool b)
+{
+ Q_D(QAction);
+ if (b == d->visible && b != d->forceInvisible)
+ return;
+ QAPP_CHECK("setVisible");
+ d->forceInvisible = !b;
+ d->visible = b;
+ d->enabled = b && !d->forceDisabled && (!d->group || d->group->isEnabled()) ;
+#ifndef QT_NO_SHORTCUT
+ d->setShortcutEnabled(d->enabled, qApp->d_func()->shortcutMap);
+#endif
+ d->sendDataChanged();
+}
+
+
+bool QAction::isVisible() const
+{
+ Q_D(const QAction);
+ return d->visible;
+}
+
+/*!
+ \reimp
+*/
+bool
+QAction::event(QEvent *e)
+{
+#ifndef QT_NO_SHORTCUT
+ if (e->type() == QEvent::Shortcut) {
+ QShortcutEvent *se = static_cast<QShortcutEvent *>(e);
+ Q_ASSERT_X(se->key() == d_func()->shortcut || d_func()->alternateShortcuts.contains(se->key()),
+ "QAction::event",
+ "Received shortcut event from incorrect shortcut");
+ if (se->isAmbiguous())
+ qWarning("QAction::eventFilter: Ambiguous shortcut overload: %s", QString(se->key()).toLatin1().constData());
+ else
+ activate(Trigger);
+ return true;
+ }
+#endif
+ return QObject::event(e);
+}
+
+/*!
+ Returns the user data as set in QAction::setData.
+
+ \sa setData()
+*/
+QVariant
+QAction::data() const
+{
+ Q_D(const QAction);
+ return d->userData;
+}
+
+/*!
+ \fn void QAction::setData(const QVariant &userData)
+
+ Sets the action's internal data to the given \a userData.
+
+ \sa data()
+*/
+void
+QAction::setData(const QVariant &data)
+{
+ Q_D(QAction);
+ d->userData = data;
+ d->sendDataChanged();
+}
+
+
+/*!
+ Updates the relevant status bar for the \a widget specified by sending a
+ QStatusTipEvent to its parent widget. Returns true if an event was sent;
+ otherwise returns false.
+
+ If a null widget is specified, the event is sent to the action's parent.
+
+ \sa statusTip
+*/
+bool
+QAction::showStatusText(QWidget *widget)
+{
+#ifdef QT_NO_STATUSTIP
+ Q_UNUSED(widget);
+#else
+ if(QObject *object = widget ? widget : parent()) {
+ QStatusTipEvent tip(statusTip());
+ QApplication::sendEvent(object, &tip);
+ return true;
+ }
+#endif
+ return false;
+}
+
+/*!
+ Sends the relevant signals for ActionEvent \a event.
+
+ Action based widgets use this API to cause the QAction
+ to emit signals as well as emitting their own.
+*/
+void QAction::activate(ActionEvent event)
+{
+ Q_D(QAction);
+ if(event == Trigger) {
+ QObject *guard = this;
+ QMetaObject::addGuard(&guard);
+ if(d->checkable) {
+ // the checked action of an exclusive group cannot be unchecked
+ if (d->checked && (d->group && d->group->isExclusive()
+ && d->group->checkedAction() == this)) {
+ QMetaObject::removeGuard(&guard);
+ return;
+ }
+ setChecked(!d->checked);
+ }
+ if (guard)
+ emit triggered(d->checked);
+#ifdef QT3_SUPPORT
+ if (guard)
+ emit activated(d->param);
+#endif
+ QMetaObject::removeGuard(&guard);
+ } else if(event == Hover) {
+ emit hovered();
+ }
+}
+
+/*!
+ \fn void QAction::triggered(bool checked)
+
+ This signal is emitted when an action is activated by the user;
+ for example, when the user clicks a menu option, toolbar button,
+ or presses an action's shortcut key combination, or when trigger()
+ was called. Notably, it is \e not emitted when setChecked() or
+ toggle() is called.
+
+ If the action is checkable, \a checked is true if the action is
+ checked, or false if the action is unchecked.
+
+ \sa QAction::activate(), QAction::toggled(), checked
+*/
+
+/*!
+ \fn void QAction::toggled(bool checked)
+
+ This signal is emitted whenever a checkable action changes its
+ isChecked() status. This can be the result of a user interaction,
+ or because setChecked() was called.
+
+ \a checked is true if the action is checked, or false if the
+ action is unchecked.
+
+ \sa QAction::activate(), QAction::triggered(), checked
+*/
+
+/*!
+ \fn void QAction::hovered()
+
+ This signal is emitted when an action is highlighted by the user;
+ for example, when the user pauses with the cursor over a menu option,
+ toolbar button, or presses an action's shortcut key combination.
+
+ \sa QAction::activate()
+*/
+
+/*!
+ \fn void QAction::changed()
+
+ This signal is emitted when an action has changed. If you
+ are only interested in actions in a given widget, you can
+ watch for QWidget::actionEvent() sent with an
+ QEvent::ActionChanged.
+
+ \sa QWidget::actionEvent()
+*/
+
+/*!
+ \enum QAction::ActionEvent
+
+ This enum type is used when calling QAction::activate()
+
+ \value Trigger this will cause the QAction::triggered() signal to be emitted.
+
+ \value Hover this will cause the QAction::hovered() signal to be emitted.
+*/
+
+/*!
+ \fn void QAction::setMenuText(const QString &text)
+
+ Use setText() instead.
+*/
+
+/*!
+ \fn QString QAction::menuText() const
+
+ Use text() instead.
+*/
+
+/*!
+ \fn bool QAction::isOn() const
+
+ Use isChecked() instead.
+*/
+
+/*!
+ \fn void QAction::setOn(bool b)
+
+ Use setChecked() instead.
+*/
+
+/*!
+ \fn bool QAction::isToggleAction() const
+
+ Use isCheckable() instead.
+*/
+
+/*!
+ \fn void QAction::setToggleAction(bool b)
+
+ Use setCheckable() instead.
+*/
+
+/*!
+ \fn void QAction::setIconSet(const QIcon &i)
+
+ Use setIcon() instead.
+*/
+
+/*!
+ \fn bool QAction::addTo(QWidget *w)
+
+ Use QWidget::addAction() instead.
+
+ \oldcode
+ action->addTo(widget);
+ \newcode
+ widget->addAction(action);
+ \endcode
+*/
+
+/*!
+ \fn bool QAction::removeFrom(QWidget *w)
+
+ Use QWidget::removeAction() instead.
+
+ \oldcode
+ action->removeFrom(widget);
+ \newcode
+ widget->removeAction(action);
+ \endcode
+*/
+
+/*!
+ \fn void QAction::setAccel(const QKeySequence &shortcut)
+
+ Use setShortcut() instead.
+*/
+
+/*!
+ \fn QIcon QAction::iconSet() const
+
+ Use icon() instead.
+*/
+
+/*!
+ \fn QKeySequence QAction::accel() const
+
+ Use shortcut() instead.
+*/
+
+/*!
+ \fn void QAction::activated(int i);
+
+ Use triggered() instead.
+*/
+
+
+/*!
+ \property QAction::menuRole
+ \brief the action's menu role
+ \since 4.2
+
+ This indicates what role the action serves in the application menu on Mac
+ OS X. By default all action have the TextHeuristicRole, which means that
+ the action is added based on its text (see QMenuBar for more information).
+
+ The menu role can only be changed before the actions are put into the menu
+ bar in Mac OS X (usually just before the first application window is
+ shown).
+*/
+void QAction::setMenuRole(MenuRole menuRole)
+{
+ Q_D(QAction);
+ if (d->menuRole == menuRole)
+ return;
+
+ d->menuRole = menuRole;
+ d->sendDataChanged();
+}
+
+QAction::MenuRole QAction::menuRole() const
+{
+ Q_D(const QAction);
+ return d->menuRole;
+}
+
+/*!
+ \property QAction::iconVisibleInMenu
+ \brief Whether or not an action should show an icon in a menu
+ \since 4.4
+
+ In some applications, it may make sense to have actions with icons in the
+ toolbar, but not in menus. If true, the icon (if valid) is shown in the menu, when it
+ is false, it is not shown.
+
+ The default is to follow whether the Qt::AA_DontShowIconsInMenus attribute
+ is set for the application. Explicitly settings this property overrides
+ the presence (or abscence) of the attribute.
+
+ For example:
+ \snippet doc/src/snippets/code/src_gui_kernel_qaction.cpp 0
+
+ \sa QAction::icon QApplication::setAttribute()
+*/
+void QAction::setIconVisibleInMenu(bool visible)
+{
+ Q_D(QAction);
+ if (visible != (bool)d->iconVisibleInMenu) {
+ int oldValue = d->iconVisibleInMenu;
+ d->iconVisibleInMenu = visible;
+ // Only send data changed if we really need to.
+ if (oldValue != -1
+ || (oldValue == -1
+ && visible == !QApplication::instance()->testAttribute(Qt::AA_DontShowIconsInMenus))) {
+ d->sendDataChanged();
+ }
+ }
+}
+
+bool QAction::isIconVisibleInMenu() const
+{
+ Q_D(const QAction);
+ if (d->iconVisibleInMenu == -1) {
+ return !QApplication::instance()->testAttribute(Qt::AA_DontShowIconsInMenus);
+ }
+ return d->iconVisibleInMenu;
+}
+
+QT_END_NAMESPACE
+
+#include "moc_qaction.cpp"
+
+#endif // QT_NO_ACTION
diff --git a/src/gui/kernel/qaction.h b/src/gui/kernel/qaction.h
new file mode 100644
index 0000000000..d7bf8c356a
--- /dev/null
+++ b/src/gui/kernel/qaction.h
@@ -0,0 +1,246 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QACTION_H
+#define QACTION_H
+
+#include <QtGui/qkeysequence.h>
+#include <QtCore/qstring.h>
+#include <QtGui/qwidget.h>
+#include <QtCore/qvariant.h>
+#include <QtGui/qicon.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+#ifndef QT_NO_ACTION
+
+class QMenu;
+class QActionGroup;
+class QActionPrivate;
+class QGraphicsWidget;
+
+class Q_GUI_EXPORT QAction : public QObject
+{
+ Q_OBJECT
+ Q_DECLARE_PRIVATE(QAction)
+
+ Q_ENUMS(MenuRole)
+ Q_PROPERTY(bool checkable READ isCheckable WRITE setCheckable)
+ Q_PROPERTY(bool checked READ isChecked WRITE setChecked DESIGNABLE isCheckable NOTIFY toggled)
+ Q_PROPERTY(bool enabled READ isEnabled WRITE setEnabled)
+ Q_PROPERTY(QIcon icon READ icon WRITE setIcon)
+ Q_PROPERTY(QString text READ text WRITE setText)
+ Q_PROPERTY(QString iconText READ iconText WRITE setIconText)
+ Q_PROPERTY(QString toolTip READ toolTip WRITE setToolTip)
+ Q_PROPERTY(QString statusTip READ statusTip WRITE setStatusTip)
+ Q_PROPERTY(QString whatsThis READ whatsThis WRITE setWhatsThis)
+ Q_PROPERTY(QFont font READ font WRITE setFont)
+#ifndef QT_NO_SHORTCUT
+ Q_PROPERTY(QKeySequence shortcut READ shortcut WRITE setShortcut)
+ Q_PROPERTY(Qt::ShortcutContext shortcutContext READ shortcutContext WRITE setShortcutContext)
+ Q_PROPERTY(bool autoRepeat READ autoRepeat WRITE setAutoRepeat)
+#endif
+ Q_PROPERTY(bool visible READ isVisible WRITE setVisible)
+ Q_PROPERTY(MenuRole menuRole READ menuRole WRITE setMenuRole)
+ Q_PROPERTY(bool iconVisibleInMenu READ isIconVisibleInMenu WRITE setIconVisibleInMenu)
+
+public:
+ enum MenuRole { NoRole, TextHeuristicRole, ApplicationSpecificRole, AboutQtRole,
+ AboutRole, PreferencesRole, QuitRole };
+ explicit QAction(QObject* parent);
+ QAction(const QString &text, QObject* parent);
+ QAction(const QIcon &icon, const QString &text, QObject* parent);
+
+#ifdef QT3_SUPPORT
+ QT3_SUPPORT_CONSTRUCTOR QAction(QObject* parent, const char* name);
+ QT3_SUPPORT_CONSTRUCTOR QAction(const QString &text, const QKeySequence &shortcut,
+ QObject* parent, const char* name);
+ QT3_SUPPORT_CONSTRUCTOR QAction(const QIcon &icon, const QString &text,
+ const QKeySequence &shortcut,
+ QObject* parent, const char* name);
+#endif
+ ~QAction();
+
+ void setActionGroup(QActionGroup *group);
+ QActionGroup *actionGroup() const;
+ void setIcon(const QIcon &icon);
+ QIcon icon() const;
+
+ void setText(const QString &text);
+ QString text() const;
+
+ void setIconText(const QString &text);
+ QString iconText() const;
+
+ void setToolTip(const QString &tip);
+ QString toolTip() const;
+
+ void setStatusTip(const QString &statusTip);
+ QString statusTip() const;
+
+ void setWhatsThis(const QString &what);
+ QString whatsThis() const;
+
+#ifndef QT_NO_MENU
+ QMenu *menu() const;
+ void setMenu(QMenu *menu);
+#endif
+
+ void setSeparator(bool b);
+ bool isSeparator() const;
+
+#ifndef QT_NO_SHORTCUT
+ void setShortcut(const QKeySequence &shortcut);
+ QKeySequence shortcut() const;
+
+ void setShortcuts(const QList<QKeySequence> &shortcuts);
+ void setShortcuts(QKeySequence::StandardKey);
+ QList<QKeySequence> shortcuts() const;
+
+ void setShortcutContext(Qt::ShortcutContext context);
+ Qt::ShortcutContext shortcutContext() const;
+
+ void setAutoRepeat(bool);
+ bool autoRepeat() const;
+#endif
+
+ void setFont(const QFont &font);
+ QFont font() const;
+
+ void setCheckable(bool);
+ bool isCheckable() const;
+
+ QVariant data() const;
+ void setData(const QVariant &var);
+
+ bool isChecked() const;
+
+ bool isEnabled() const;
+
+ bool isVisible() const;
+
+ enum ActionEvent { Trigger, Hover };
+ void activate(ActionEvent event);
+ bool showStatusText(QWidget *widget=0);
+
+ void setMenuRole(MenuRole menuRole);
+ MenuRole menuRole() const;
+
+ void setIconVisibleInMenu(bool visible);
+ bool isIconVisibleInMenu() const;
+
+#ifdef QT3_SUPPORT
+ inline QT3_SUPPORT void setMenuText(const QString &text) { setText(text); }
+ inline QT3_SUPPORT QString menuText() const { return text(); }
+ inline QT3_SUPPORT bool isOn() const { return isChecked(); }
+ inline QT3_SUPPORT bool isToggleAction() const { return isCheckable(); }
+ inline QT3_SUPPORT void setToggleAction(bool b) { setCheckable(b); }
+ inline QT3_SUPPORT void setIconSet(const QIcon &i) { setIcon(i); }
+ inline QT3_SUPPORT QIcon iconSet() const { return icon(); }
+ inline QT3_SUPPORT bool addTo(QWidget *w) { w->addAction(this); return true; }
+ inline QT3_SUPPORT bool removeFrom(QWidget *w) { w->removeAction(this); return true; }
+ inline QT3_SUPPORT void setAccel(const QKeySequence &shortcut) { setShortcut(shortcut); }
+ inline QT3_SUPPORT QKeySequence accel() const { return shortcut(); }
+#endif
+
+ QWidget *parentWidget() const;
+
+ QList<QWidget *> associatedWidgets() const;
+#ifndef QT_NO_GRAPHICSVIEW
+ QList<QGraphicsWidget *> associatedGraphicsWidgets() const; // ### suboptimal
+#endif
+
+protected:
+ bool event(QEvent *);
+ QAction(QActionPrivate &dd, QObject *parent);
+
+public Q_SLOTS:
+#ifdef QT3_SUPPORT
+ inline QT_MOC_COMPAT void setOn(bool b) { setChecked(b); }
+#endif
+ void trigger() { activate(Trigger); }
+ void hover() { activate(Hover); }
+ void setChecked(bool);
+ void toggle();
+ void setEnabled(bool);
+ inline void setDisabled(bool b) { setEnabled(!b); }
+ void setVisible(bool);
+
+Q_SIGNALS:
+ void changed();
+ void triggered(bool checked = false);
+ void hovered();
+ void toggled(bool);
+#ifdef QT3_SUPPORT
+ QT_MOC_COMPAT void activated(int = 0);
+#endif
+
+private:
+ Q_DISABLE_COPY(QAction)
+
+#ifdef QT3_SUPPORT
+ friend class QMenuItem;
+#endif
+ friend class QGraphicsWidget;
+ friend class QWidget;
+ friend class QActionGroup;
+ friend class QMenu;
+ friend class QMenuPrivate;
+ friend class QMenuBar;
+ friend class QShortcutMap;
+ friend class QToolButton;
+};
+
+QT_BEGIN_INCLUDE_NAMESPACE
+#include <QtGui/qactiongroup.h>
+QT_END_INCLUDE_NAMESPACE
+
+#endif // QT_NO_ACTION
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QACTION_H
diff --git a/src/gui/kernel/qaction_p.h b/src/gui/kernel/qaction_p.h
new file mode 100644
index 0000000000..0617ef56c7
--- /dev/null
+++ b/src/gui/kernel/qaction_p.h
@@ -0,0 +1,129 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QACTION_P_H
+#define QACTION_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 "QtGui/qaction.h"
+#include "QtGui/qmenu.h"
+#include "private/qgraphicswidget_p.h"
+#include "private/qobject_p.h"
+
+QT_BEGIN_NAMESPACE
+
+#ifndef QT_NO_ACTION
+
+#ifdef QT3_SUPPORT
+class QMenuItemEmitter;
+#endif
+
+class QShortcutMap;
+
+class Q_AUTOTEST_EXPORT QActionPrivate : public QObjectPrivate
+{
+ Q_DECLARE_PUBLIC(QAction)
+public:
+ QActionPrivate();
+ ~QActionPrivate();
+
+ QPointer<QActionGroup> group;
+ QString text;
+ QString iconText;
+ QIcon icon;
+ QString tooltip;
+ QString statustip;
+ QString whatsthis;
+#ifndef QT_NO_SHORTCUT
+ QKeySequence shortcut;
+ QList<QKeySequence> alternateShortcuts;
+#endif
+ QVariant userData;
+#ifndef QT_NO_SHORTCUT
+ int shortcutId;
+ QList<int> alternateShortcutIds;
+ Qt::ShortcutContext shortcutContext;
+ uint autorepeat : 1;
+#endif
+ QFont font;
+ QPointer<QMenu> menu;
+ uint enabled : 1, forceDisabled : 1;
+ uint visible : 1, forceInvisible : 1;
+ uint checkable : 1;
+ uint checked : 1;
+ uint separator : 1;
+ uint fontSet : 1;
+ QAction::MenuRole menuRole;
+ int iconVisibleInMenu : 3; // Only has values -1, 0, and 1
+ QList<QWidget *> widgets;
+#ifndef QT_NO_GRAPHICSVIEW
+ QList<QGraphicsWidget *> graphicsWidgets;
+#endif
+#ifndef QT_NO_SHORTCUT
+ void redoGrab(QShortcutMap &map);
+ void redoGrabAlternate(QShortcutMap &map);
+ void setShortcutEnabled(bool enable, QShortcutMap &map);
+
+ static QShortcutMap *globalMap;
+#endif // QT_NO_SHORTCUT
+
+#ifdef QT3_SUPPORT //for menubar/menu compat
+ QMenuItemEmitter *act_signal;
+ int id, param;
+#endif
+ void sendDataChanged();
+};
+
+#endif // QT_NO_ACTION
+
+QT_END_NAMESPACE
+
+#endif // QACTION_P_H
diff --git a/src/gui/kernel/qactiongroup.cpp b/src/gui/kernel/qactiongroup.cpp
new file mode 100644
index 0000000000..2fd595e34c
--- /dev/null
+++ b/src/gui/kernel/qactiongroup.cpp
@@ -0,0 +1,416 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qactiongroup.h"
+
+#ifndef QT_NO_ACTION
+
+#include "qaction_p.h"
+#include "qapplication.h"
+#include "qevent.h"
+#include "qlist.h"
+
+QT_BEGIN_NAMESPACE
+
+class QActionGroupPrivate : public QObjectPrivate
+{
+ Q_DECLARE_PUBLIC(QActionGroup)
+public:
+ QActionGroupPrivate() : exclusive(1), enabled(1), visible(1) { }
+ QList<QAction *> actions;
+ QPointer<QAction> current;
+ uint exclusive : 1;
+ uint enabled : 1;
+ uint visible : 1;
+
+private:
+ void _q_actionTriggered(); //private slot
+ void _q_actionChanged(); //private slot
+ void _q_actionHovered(); //private slot
+};
+
+void QActionGroupPrivate::_q_actionChanged()
+{
+ Q_Q(QActionGroup);
+ QAction *action = qobject_cast<QAction*>(q->sender());
+ Q_ASSERT_X(action != 0, "QWidgetGroup::_q_actionChanged", "internal error");
+ if(exclusive && action->isChecked() && action != current) {
+ if(current)
+ current->setChecked(false);
+ current = action;
+ }
+}
+
+void QActionGroupPrivate::_q_actionTriggered()
+{
+ Q_Q(QActionGroup);
+ QAction *action = qobject_cast<QAction*>(q->sender());
+ Q_ASSERT_X(action != 0, "QWidgetGroup::_q_actionTriggered", "internal error");
+ emit q->triggered(action);
+ emit q->selected(action);
+}
+
+void QActionGroupPrivate::_q_actionHovered()
+{
+ Q_Q(QActionGroup);
+ QAction *action = qobject_cast<QAction*>(q->sender());
+ Q_ASSERT_X(action != 0, "QWidgetGroup::_q_actionHovered", "internal error");
+ emit q->hovered(action);
+}
+
+/*!
+ \class QActionGroup
+ \brief The QActionGroup class groups actions together.
+
+ \ingroup application
+
+ In some situations it is useful to group actions together. For
+ example, if you have a \gui{Left Align} action, a \gui{Right
+ Align} action, a \gui{Justify} action, and a \gui{Center} action,
+ only one of these actions should be active at any one time. One
+ simple way of achieving this is to group the actions together in
+ an action group.
+
+ Here's a example (from the \l{mainwindows/menus}{Menus} example):
+
+ \snippet examples/mainwindows/menus/mainwindow.cpp 6
+
+ Here we create a new action group. Since the action group is
+ exclusive by default, only one of the actions in the group is
+ checked at any one time.
+
+ \img qactiongroup-align.png Alignment options in a QMenu
+
+ A QActionGroup emits an triggered() signal when one of its
+ actions is chosen. Each action in an action group emits its
+ triggered() signal as usual.
+
+ As stated above, an action group is \l exclusive by default; it
+ ensures that only one checkable action is active at any one time.
+ If you want to group checkable actions without making them
+ exclusive, you can turn of exclusiveness by calling
+ setExclusive(false).
+
+ Actions can be added to an action group using addAction(), but it
+ is usually more convenient to specify a group when creating
+ actions; this ensures that actions are automatically created with
+ a parent. Actions can be visually separated from each other by
+ adding a separator action to the group; create an action and use
+ QAction's \l {QAction::}{setSeparator()} function to make it
+ considered a separator. Action groups are added to widgets with
+ the QWidget::addActions() function.
+
+ \sa QAction
+*/
+
+/*!
+ Constructs an action group for the \a parent object.
+
+ The action group is exclusive by default. Call setExclusive(false)
+ to make the action group non-exclusive.
+*/
+QActionGroup::QActionGroup(QObject* parent) : QObject(*new QActionGroupPrivate, parent)
+{
+}
+
+/*!
+ Destroys the action group.
+*/
+QActionGroup::~QActionGroup()
+{
+}
+
+/*!
+ \fn QAction *QActionGroup::addAction(QAction *action)
+
+ Adds the \a action to this group, and returns it.
+
+ Normally an action is added to a group by creating it with the
+ group as its parent, so this function is not usually used.
+
+ \sa QAction::setActionGroup()
+*/
+QAction *QActionGroup::addAction(QAction* a)
+{
+ Q_D(QActionGroup);
+ if(!d->actions.contains(a)) {
+ d->actions.append(a);
+ QObject::connect(a, SIGNAL(triggered()), this, SLOT(_q_actionTriggered()));
+ QObject::connect(a, SIGNAL(changed()), this, SLOT(_q_actionChanged()));
+ QObject::connect(a, SIGNAL(hovered()), this, SLOT(_q_actionHovered()));
+ }
+ if(!a->d_func()->forceDisabled) {
+ a->setEnabled(d->enabled);
+ a->d_func()->forceDisabled = false;
+ }
+ if(!a->d_func()->forceInvisible) {
+ a->setVisible(d->visible);
+ a->d_func()->forceInvisible = false;
+ }
+ if(a->isChecked())
+ d->current = a;
+ QActionGroup *oldGroup = a->d_func()->group;
+ if(oldGroup != this) {
+ if (oldGroup)
+ oldGroup->removeAction(a);
+ a->d_func()->group = this;
+ }
+ return a;
+}
+
+/*!
+ Creates and returns an action with \a text. The newly created
+ action is a child of this action 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 QAction::setActionGroup()
+*/
+QAction *QActionGroup::addAction(const QString &text)
+{
+ return new QAction(text, this);
+}
+
+/*!
+ Creates and returns an action with \a text and an \a icon. The
+ newly created action is a child of this action group.
+
+ Normally an action is added to a group by creating it with the
+ group as its parent, so this function is not usually used.
+
+ \sa QAction::setActionGroup()
+*/
+QAction *QActionGroup::addAction(const QIcon &icon, const QString &text)
+{
+ return new QAction(icon, text, this);
+}
+
+/*!
+ Removes the \a action from this group. The action will have no
+ parent as a result.
+
+ \sa QAction::setActionGroup()
+*/
+void QActionGroup::removeAction(QAction *action)
+{
+ Q_D(QActionGroup);
+ if (d->actions.removeAll(action)) {
+ if (action == d->current)
+ d->current = 0;
+ QObject::disconnect(action, SIGNAL(triggered()), this, SLOT(_q_actionTriggered()));
+ QObject::disconnect(action, SIGNAL(changed()), this, SLOT(_q_actionChanged()));
+ QObject::disconnect(action, SIGNAL(hovered()), this, SLOT(_q_actionHovered()));
+ action->d_func()->group = 0;
+ }
+}
+
+/*!
+ Returns the list of this groups's actions. This may be empty.
+*/
+QList<QAction*> QActionGroup::actions() const
+{
+ Q_D(const QActionGroup);
+ return d->actions;
+}
+
+/*!
+ \property QActionGroup::exclusive
+ \brief whether the action group does exclusive checking
+
+ If exclusive is true, only one checkable action in the action group
+ can ever be active at any time. If the user chooses another
+ checkable action in the group, the one they chose becomes active and
+ the one that was active becomes inactive.
+
+ \sa QAction::checkable
+*/
+void QActionGroup::setExclusive(bool b)
+{
+ Q_D(QActionGroup);
+ d->exclusive = b;
+}
+
+bool QActionGroup::isExclusive() const
+{
+ Q_D(const QActionGroup);
+ return d->exclusive;
+}
+
+/*!
+ \fn void QActionGroup::setDisabled(bool b)
+
+ This is a convenience function for the \l enabled property, that
+ is useful for signals--slots connections. If \a b is true the
+ action group is disabled; otherwise it is enabled.
+*/
+
+/*!
+ \property QActionGroup::enabled
+ \brief whether the action group is enabled
+
+ Each action in the group will be enabled or disabled unless it
+ has been explicitly disabled.
+
+ \sa QAction::setEnabled()
+*/
+void QActionGroup::setEnabled(bool b)
+{
+ Q_D(QActionGroup);
+ d->enabled = b;
+ for(QList<QAction*>::const_iterator it = d->actions.constBegin(); it != d->actions.constEnd(); ++it) {
+ if(!(*it)->d_func()->forceDisabled) {
+ (*it)->setEnabled(b);
+ (*it)->d_func()->forceDisabled = false;
+ }
+ }
+}
+
+bool QActionGroup::isEnabled() const
+{
+ Q_D(const QActionGroup);
+ return d->enabled;
+}
+
+/*!
+ Returns the currently checked action in the group, or 0 if none
+ are checked.
+*/
+QAction *QActionGroup::checkedAction() const
+{
+ Q_D(const QActionGroup);
+ return d->current;
+}
+
+/*!
+ \property QActionGroup::visible
+ \brief whether the action group is visible
+
+ Each action in the action group will match the visible state of
+ this group unless it has been explicitly hidden.
+
+ \sa QAction::setEnabled()
+*/
+void QActionGroup::setVisible(bool b)
+{
+ Q_D(QActionGroup);
+ d->visible = b;
+ for(QList<QAction*>::Iterator it = d->actions.begin(); it != d->actions.end(); ++it) {
+ if(!(*it)->d_func()->forceInvisible) {
+ (*it)->setVisible(b);
+ (*it)->d_func()->forceInvisible = false;
+ }
+ }
+}
+
+bool QActionGroup::isVisible() const
+{
+ Q_D(const QActionGroup);
+ return d->visible;
+}
+
+/*!
+ \fn void QActionGroup::triggered(QAction *action)
+
+ This signal is emitted when the given \a action in the action
+ group is activated by the user; for example, when the user clicks
+ a menu option, toolbar button, or presses an action's shortcut key
+ combination.
+
+ Connect to this signal for command actions.
+
+ \sa QAction::activate()
+*/
+
+/*!
+ \fn void QActionGroup::hovered(QAction *action)
+
+ This signal is emitted when the given \a action in the action
+ group is highlighted by the user; for example, when the user
+ pauses with the cursor over a menu option, toolbar button, or
+ presses an action's shortcut key combination.
+
+ \sa QAction::activate()
+*/
+
+/*!
+ \fn void QActionGroup::add(QAction* a)
+
+ Use addAction() instead.
+*/
+
+/*!
+ \fn void QActionGroup::addSeparator()
+
+ Normally you add a separator to the menus or widgets to which
+ actions are added, so this function is very rarely needed.
+
+ \oldcode
+ actionGroup->addSeparator();
+ \newcode
+ QAction *separator = new QAction(this);
+ separator->setSeparator(true);
+ actionGroup->addAction(separator);
+ \endcode
+*/
+
+/*!
+ \fn bool QActionGroup::addTo(QWidget *widget)
+
+ \oldcode
+ actionGroup->addTo(widget);
+ \newcode
+ widget->addActions(actionGroup->actions());
+ \endcode
+*/
+
+/*!
+ \fn void QActionGroup::selected(QAction *action);
+
+ Use triggered() instead.
+
+*/
+
+QT_END_NAMESPACE
+
+#include "moc_qactiongroup.cpp"
+
+#endif // QT_NO_ACTION
diff --git a/src/gui/kernel/qactiongroup.h b/src/gui/kernel/qactiongroup.h
new file mode 100644
index 0000000000..3d1231f092
--- /dev/null
+++ b/src/gui/kernel/qactiongroup.h
@@ -0,0 +1,112 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QACTIONGROUP_H
+#define QACTIONGROUP_H
+
+#include <QtGui/qaction.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+#ifndef QT_NO_ACTION
+
+class QActionGroupPrivate;
+
+class Q_GUI_EXPORT QActionGroup : public QObject
+{
+ Q_OBJECT
+ Q_DECLARE_PRIVATE(QActionGroup)
+
+ Q_PROPERTY(bool exclusive READ isExclusive WRITE setExclusive)
+ Q_PROPERTY(bool enabled READ isEnabled WRITE setEnabled)
+ Q_PROPERTY(bool visible READ isVisible WRITE setVisible)
+
+public:
+ explicit QActionGroup(QObject* parent);
+ ~QActionGroup();
+
+ QAction *addAction(QAction* a);
+ QAction *addAction(const QString &text);
+ QAction *addAction(const QIcon &icon, const QString &text);
+ void removeAction(QAction *a);
+ QList<QAction*> actions() const;
+
+ QAction *checkedAction() const;
+ bool isExclusive() const;
+ bool isEnabled() const;
+ bool isVisible() const;
+
+#ifdef QT3_SUPPORT
+ inline QT3_SUPPORT void add(QAction* a) { addAction(a); }
+ inline QT3_SUPPORT void addSeparator()
+ { QAction *act = new QAction(this); act->setSeparator(true); addAction(act); }
+ inline QT3_SUPPORT bool addTo(QWidget *w) { w->addActions(actions()); return true; }
+#endif
+
+public Q_SLOTS:
+ void setEnabled(bool);
+ inline void setDisabled(bool b) { setEnabled(!b); }
+ void setVisible(bool);
+ void setExclusive(bool);
+
+Q_SIGNALS:
+ void triggered(QAction *);
+ QT_MOC_COMPAT void selected(QAction *);
+ void hovered(QAction *);
+
+private:
+ Q_DISABLE_COPY(QActionGroup)
+ Q_PRIVATE_SLOT(d_func(), void _q_actionTriggered())
+ Q_PRIVATE_SLOT(d_func(), void _q_actionChanged())
+ Q_PRIVATE_SLOT(d_func(), void _q_actionHovered())
+};
+
+#endif // QT_NO_ACTION
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QACTIONGROUP_H
diff --git a/src/gui/kernel/qapplication.cpp b/src/gui/kernel/qapplication.cpp
new file mode 100644
index 0000000000..1bbb0190c2
--- /dev/null
+++ b/src/gui/kernel/qapplication.cpp
@@ -0,0 +1,5051 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qabstracteventdispatcher.h"
+#include "qaccessible.h"
+#include "qapplication.h"
+#include "qclipboard.h"
+#include "qcursor.h"
+#include "qdesktopwidget.h"
+#include "qdir.h"
+#include "qevent.h"
+#include "qfile.h"
+#include "qfileinfo.h"
+#include "qgraphicsscene.h"
+#include "qhash.h"
+#include "qset.h"
+#include "qlayout.h"
+#include "qsessionmanager.h"
+#include "qstyle.h"
+#include "qstylefactory.h"
+#include "qtextcodec.h"
+#include "qtranslator.h"
+#include "qvariant.h"
+#include "qwidget.h"
+#include "qdnd_p.h"
+#include "qcolormap.h"
+#include "qdebug.h"
+#include "private/qgraphicssystemfactory_p.h"
+#include "private/qstylesheetstyle_p.h"
+#include "private/qstyle_p.h"
+#include "qmessagebox.h"
+#include <QtGui/qgraphicsproxywidget.h>
+
+#include "qinputcontext.h"
+#include "qkeymapper_p.h"
+
+#ifdef Q_WS_X11
+#include <private/qt_x11_p.h>
+#include "qinputcontextfactory.h"
+#endif
+
+#include <qthread.h>
+#include <private/qthread_p.h>
+
+#include <private/qfont_p.h>
+
+#include <stdlib.h>
+
+#include "qapplication_p.h"
+#include "qwidget_p.h"
+
+#include "qapplication.h"
+
+#ifdef Q_OS_WINCE
+#include "qdatetime.h"
+#include "qguifunctions_wince.h"
+extern bool qt_wince_is_smartphone(); //qguifunctions_wince.cpp
+extern bool qt_wince_is_mobile(); //qguifunctions_wince.cpp
+extern bool qt_wince_is_pocket_pc(); //qguifunctions_wince.cpp
+#endif
+
+//#define ALIEN_DEBUG
+
+static void initResources()
+{
+#ifdef Q_OS_WINCE
+ Q_INIT_RESOURCE(qstyle_wince);
+#else
+ Q_INIT_RESOURCE(qstyle);
+#endif
+
+#if !defined(QT_NO_DIRECT3D) && defined(Q_WS_WIN)
+ Q_INIT_RESOURCE(qpaintengine_d3d);
+#endif
+ Q_INIT_RESOURCE(qmessagebox);
+#if !defined(QT_NO_PRINTDIALOG)
+ Q_INIT_RESOURCE(qprintdialog);
+#endif
+
+}
+
+QT_BEGIN_NAMESPACE
+
+extern void qt_call_post_routines();
+
+int QApplicationPrivate::app_compile_version = 0x040000; //we don't know exactly, but it's at least 4.0.0
+
+QApplication::Type qt_appType=QApplication::Tty;
+QApplicationPrivate *QApplicationPrivate::self = 0;
+
+QInputContext *QApplicationPrivate::inputContext;
+
+bool QApplicationPrivate::quitOnLastWindowClosed = true;
+
+#ifdef Q_OS_WINCE
+int QApplicationPrivate::autoMaximizeThreshold = -1;
+bool QApplicationPrivate::autoSipEnabled = false;
+#endif
+
+QApplicationPrivate::QApplicationPrivate(int &argc, char **argv, QApplication::Type type)
+ : QCoreApplicationPrivate(argc, argv)
+{
+ application_type = type;
+ qt_appType = type;
+
+#ifndef QT_NO_SESSIONMANAGER
+ is_session_restored = false;
+#endif
+
+ quitOnLastWindowClosed = true;
+
+#ifdef QT3_SUPPORT
+ qt_compat_used = 0;
+ qt_compat_resolved = 0;
+ qt_tryAccelEvent = 0;
+ qt_tryComposeUnicode = 0;
+ qt_dispatchAccelEvent = 0;
+#endif
+#if defined(Q_WS_QWS) && !defined(QT_NO_DIRECTPAINTER)
+ directPainters = 0;
+#endif
+
+ if (!self)
+ self = this;
+}
+
+QApplicationPrivate::~QApplicationPrivate()
+{
+ if (self == this)
+ self = 0;
+}
+
+/*!
+ \class QApplication
+ \brief The QApplication class manages the GUI application's control
+ flow and main settings.
+
+ \ingroup application
+ \mainclass
+
+ It contains the main event loop, where all events from the window
+ system and other sources are processed and dispatched. It also
+ handles the application's initialization and finalization, and
+ provides session management. It also handles most system-wide and
+ application-wide settings.
+
+ For any GUI application that uses Qt, there is precisely one
+ QApplication object, no matter whether the application has 0, 1, 2
+ or more windows at any time. For non-GUI Qt applications, use
+ QCoreApplication instead, which doesn't depend on the \l QtGui
+ library.
+
+ The QApplication object is accessible through the instance()
+ function which return a pointer equivalent to the global qApp
+ pointer.
+
+ QApplication's main areas of responsibility are:
+ \list
+
+ \o It initializes the application with the user's desktop settings
+ such as palette(), font() and doubleClickInterval(). It keeps track
+ of these properties in case the user changes the desktop globally, for
+ example through some kind of control panel.
+
+ \o It performs event handling, meaning that it receives events
+ from the underlying window system and dispatches them to the relevant
+ widgets. By using sendEvent() and postEvent() you can send your own
+ events to widgets.
+
+ \o It parses common command line arguments and sets its internal
+ state accordingly. See the \link QApplication::QApplication()
+ constructor documentation\endlink below for more details about this.
+
+ \o It defines the application's look and feel, which is
+ encapsulated in a QStyle object. This can be changed at runtime
+ with setStyle().
+
+ \o It specifies how the application is to allocate colors.
+ See setColorSpec() for details.
+
+ \o It provides localization of strings that are visible to the user
+ via translate().
+
+ \o It provides some magical objects like the desktop() and the
+ clipboard().
+
+ \o It knows about the application's windows. You can ask which
+ widget is at a certain position using widgetAt(), get a list of
+ topLevelWidgets() and closeAllWindows(), etc.
+
+ \o It manages the application's mouse cursor handling,
+ see setOverrideCursor()
+
+ \o On the X window system, it provides functions to flush and sync
+ the communication stream, see flushX() and syncX().
+
+ \o It provides support for sophisticated \link
+ session.html session management \endlink. This makes it possible
+ for applications to terminate gracefully when the user logs out, to
+ cancel a shutdown process if termination isn't possible and even to
+ preserve the entire application's state for a future session. See
+ isSessionRestored(), sessionId() and commitData() and saveState()
+ for details.
+
+ \endlist
+
+ Since the QApplication object does so much initialization, it
+ \e{must} be created before any other objects related to the user
+ interface are created.
+
+ Since it also deals with common command line arguments, it is
+ usually a good idea to create it \e before any interpretation or
+ modification of \c argv is done in the application itself.
+
+ \table
+ \header \o{2,1} Groups of functions
+ \row
+ \o System settings
+ \o
+ desktopSettingsAware(),
+ setDesktopSettingsAware(),
+ cursorFlashTime(),
+ setCursorFlashTime(),
+ doubleClickInterval(),
+ setDoubleClickInterval(),
+ setKeyboardInputInterval(),
+ wheelScrollLines(),
+ setWheelScrollLines(),
+ palette(),
+ setPalette(),
+ font(),
+ setFont(),
+ fontMetrics().
+
+ \row
+ \o Event handling
+ \o
+ exec(),
+ processEvents(),
+ exit(),
+ quit().
+ sendEvent(),
+ postEvent(),
+ sendPostedEvents(),
+ removePostedEvents(),
+ hasPendingEvents(),
+ notify(),
+ macEventFilter(),
+ qwsEventFilter(),
+ x11EventFilter(),
+ x11ProcessEvent(),
+ winEventFilter().
+
+ \row
+ \o GUI Styles
+ \o
+ style(),
+ setStyle().
+
+ \row
+ \o Color usage
+ \o
+ colorSpec(),
+ setColorSpec(),
+ qwsSetCustomColors().
+
+ \row
+ \o Text handling
+ \o
+ installTranslator(),
+ removeTranslator()
+ translate().
+
+ \row
+ \o Widgets
+ \o
+ allWidgets(),
+ topLevelWidgets(),
+ desktop(),
+ activePopupWidget(),
+ activeModalWidget(),
+ clipboard(),
+ focusWidget(),
+ winFocus(),
+ activeWindow(),
+ widgetAt().
+
+ \row
+ \o Advanced cursor handling
+ \o
+ overrideCursor(),
+ setOverrideCursor(),
+ restoreOverrideCursor().
+
+ \row
+ \o X Window System synchronization
+ \o
+ flushX(),
+ syncX().
+
+ \row
+ \o Session management
+ \o
+ isSessionRestored(),
+ sessionId(),
+ commitData(),
+ saveState().
+
+ \row
+ \o Miscellaneous
+ \o
+ closeAllWindows(),
+ startingUp(),
+ closingDown(),
+ type().
+ \endtable
+
+ \sa QCoreApplication, QAbstractEventDispatcher, QEventLoop, QSettings
+*/
+
+/*!
+ \enum QApplication::Type
+
+ \value Tty a console application
+ \value GuiClient a GUI client application
+ \value GuiServer a GUI server application (for Qt for Embedded Linux)
+*/
+
+/*!
+ \enum QApplication::ColorSpec
+
+ \value NormalColor the default color allocation policy
+ \value CustomColor the same as NormalColor for X11; allocates colors
+ to a palette on demand under Windows
+ \value ManyColor the right choice for applications that use thousands of
+ colors
+
+ See setColorSpec() for full details.
+*/
+
+/*!
+ \fn QWidget *QApplication::topLevelAt(const QPoint &point)
+
+ Returns the top-level widget at the given \a point; returns 0 if
+ there is no such widget.
+*/
+/*!
+ \fn QWidget *QApplication::topLevelAt(int x, int y)
+
+ \overload
+
+ Returns the top-level widget at the point (\a{x}, \a{y}); returns
+ 0 if there is no such widget.
+*/
+
+
+/*
+ The qt_init() and qt_cleanup() functions are implemented in the
+ qapplication_xyz.cpp file.
+*/
+
+void qt_init(QApplicationPrivate *priv, int type
+#ifdef Q_WS_X11
+ , Display *display = 0, Qt::HANDLE visual = 0, Qt::HANDLE colormap = 0
+#endif
+ );
+void qt_cleanup();
+
+Qt::MouseButtons QApplicationPrivate::mouse_buttons = Qt::NoButton;
+Qt::KeyboardModifiers QApplicationPrivate::modifier_buttons = Qt::NoModifier;
+
+QStyle *QApplicationPrivate::app_style = 0; // default application style
+
+#ifndef QT_NO_STYLE_STYLESHEET
+QString QApplicationPrivate::styleSheet; // default application stylesheet
+#endif
+QPointer<QWidget> QApplicationPrivate::leaveAfterRelease = 0;
+
+int QApplicationPrivate::app_cspec = QApplication::NormalColor;
+QPalette *QApplicationPrivate::app_pal = 0; // default application palette
+QPalette *QApplicationPrivate::sys_pal = 0; // default system palette
+QPalette *QApplicationPrivate::set_pal = 0; // default palette set by programmer
+
+QGraphicsSystem *QApplicationPrivate::graphics_system = 0; // default graphics system
+QString QApplicationPrivate::graphics_system_name; // graphics system id - for delayed initialization
+
+Q_GLOBAL_STATIC(QMutex, applicationFontMutex);
+QFont *QApplicationPrivate::app_font = 0; // default application font
+QFont *QApplicationPrivate::sys_font = 0; // default system font
+QFont *QApplicationPrivate::set_font = 0; // default font set by programmer
+
+QIcon *QApplicationPrivate::app_icon = 0;
+QWidget *QApplicationPrivate::main_widget = 0; // main application widget
+QWidget *QApplicationPrivate::focus_widget = 0; // has keyboard input focus
+QWidget *QApplicationPrivate::hidden_focus_widget = 0; // will get keyboard input focus after show()
+QWidget *QApplicationPrivate::active_window = 0; // toplevel with keyboard focus
+bool QApplicationPrivate::obey_desktop_settings = true; // use winsys resources
+int QApplicationPrivate::cursor_flash_time = 1000; // text caret flash time
+int QApplicationPrivate::mouse_double_click_time = 400; // mouse dbl click limit
+int QApplicationPrivate::keyboard_input_time = 400; // keyboard input interval
+#ifndef QT_NO_WHEELEVENT
+int QApplicationPrivate::wheel_scroll_lines; // number of lines to scroll
+#endif
+bool qt_is_gui_used;
+bool Q_GUI_EXPORT qt_tab_all_widgets = true;
+bool qt_in_tab_key_event = false;
+int qt_antialiasing_threshold = -1;
+static int drag_time = 500;
+static int drag_distance = 4;
+static Qt::LayoutDirection layout_direction = Qt::LeftToRight;
+QSize QApplicationPrivate::app_strut = QSize(0,0); // no default application strut
+bool QApplicationPrivate::animate_ui = true;
+bool QApplicationPrivate::animate_menu = false;
+bool QApplicationPrivate::fade_menu = false;
+bool QApplicationPrivate::animate_combo = false;
+bool QApplicationPrivate::animate_tooltip = false;
+bool QApplicationPrivate::fade_tooltip = false;
+bool QApplicationPrivate::animate_toolbox = false;
+bool QApplicationPrivate::widgetCount = false;
+QString* QApplicationPrivate::styleOverride = 0;
+#if defined(Q_WS_WIN) && !defined(Q_OS_WINCE)
+bool QApplicationPrivate::inSizeMove = false;
+#endif
+#ifdef QT_KEYPAD_NAVIGATION
+bool QApplicationPrivate::keypadNavigation = false;
+QWidget *QApplicationPrivate::oldEditFocus = 0;
+#endif
+
+bool qt_tabletChokeMouse = false;
+static bool force_reverse = false;
+
+static inline bool isAlien(QWidget *widget)
+{
+ if (!widget)
+ return false;
+#ifdef Q_WS_MAC // Fake alien behavior on the Mac :)
+ return !widget->isWindow() && widget->window()->testAttribute(Qt::WA_DontShowOnScreen);
+#else
+ return !widget->internalWinId();
+#endif
+}
+
+// ######## move to QApplicationPrivate
+// Default application palettes and fonts (per widget type)
+
+typedef QHash<QByteArray, QPalette> PaletteHash;
+Q_GLOBAL_STATIC(PaletteHash, app_palettes)
+PaletteHash *qt_app_palettes_hash()
+{
+ return app_palettes();
+}
+
+typedef QHash<QByteArray, QFont> FontHash;
+Q_GLOBAL_STATIC(FontHash, app_fonts)
+FontHash *qt_app_fonts_hash()
+{
+ return app_fonts();
+}
+
+QWidgetList *QApplicationPrivate::popupWidgets = 0; // has keyboard input focus
+
+QDesktopWidget *qt_desktopWidget = 0; // root window widgets
+#ifndef QT_NO_CLIPBOARD
+QClipboard *qt_clipboard = 0; // global clipboard object
+#endif
+QWidgetList * qt_modal_stack=0; // stack of modal widgets
+
+/*!
+ \internal
+*/
+void QApplicationPrivate::process_cmdline()
+{
+ Q_Q(QApplication);
+ Q_UNUSED(q);// only static members being used.
+ // process platform-indep command line
+ if (!qt_is_gui_used || !argc)
+ return;
+
+ int i, j;
+
+ j = 1;
+ for (i=1; i<argc; i++) { // if you add anything here, modify QCoreApplication::arguments()
+ if (argv[i] && *argv[i] != '-') {
+ argv[j++] = argv[i];
+ continue;
+ }
+ QByteArray arg = argv[i];
+ arg = arg;
+ QString s;
+ if (arg == "-qdevel" || arg == "-qdebug") {
+ // obsolete argument
+ } else if (arg.indexOf("-style=", 0) != -1) {
+ s = QString::fromLocal8Bit(arg.right(arg.length() - 7).toLower());
+ } else if (arg == "-style" && i < argc-1) {
+ s = QString::fromLocal8Bit(argv[++i]).toLower();
+#ifndef QT_NO_SESSIONMANAGER
+ } else if (arg == "-session" && i < argc-1) {
+ ++i;
+ if (argv[i] && *argv[i]) {
+ session_id = QString::fromLatin1(argv[i]);
+ int p = session_id.indexOf(QLatin1Char('_'));
+ if (p >= 0) {
+ session_key = session_id.mid(p +1);
+ session_id = session_id.left(p);
+ }
+ is_session_restored = true;
+ }
+#endif
+#ifndef QT_NO_STYLE_STYLESHEET
+ } else if (arg == "-stylesheet" && i < argc -1) {
+ styleSheet = QLatin1String("file:///");
+ styleSheet.append(QString::fromLocal8Bit(argv[++i]));
+ } else if (arg.indexOf("-stylesheet=") != -1) {
+ styleSheet = QLatin1String("file:///");
+ styleSheet.append(QString::fromLocal8Bit(arg.right(arg.length() - 12)));
+#endif
+ } else if (qstrcmp(arg, "-reverse") == 0) {
+ force_reverse = true;
+ q->setLayoutDirection(Qt::RightToLeft);
+ } else if (qstrcmp(arg, "-widgetcount") == 0) {
+ widgetCount = true;
+ } else if (arg == "-graphicssystem" && i < argc-1) {
+ graphics_system_name = QString::fromLocal8Bit(argv[++i]);
+ } else {
+ argv[j++] = argv[i];
+ }
+ if (!s.isEmpty()) {
+ if (app_style) {
+ delete app_style;
+ app_style = 0;
+ }
+ if (!styleOverride)
+ styleOverride = new QString;
+ *styleOverride = s;
+ }
+ }
+
+ if(j < argc) {
+ argv[j] = 0;
+ argc = j;
+ }
+}
+
+/*!
+ Initializes the window system and constructs an application object
+ with \a argc command line arguments in \a argv.
+
+ \warning The data referred to by \a argc and \a argv must stay valid
+ for the entire lifetime of the QApplication object. In addition,
+ \a argc must be greater than zero and \a argv must contain at least
+ one valid character string.
+
+ The global \c qApp pointer refers to this application object. Only
+ one application object should be created.
+
+ This application object must be constructed before any \link
+ QPaintDevice paint devices\endlink (including widgets, pixmaps, bitmaps
+ etc.).
+
+ Note that \a argc and \a argv might be changed. Qt removes command
+ line arguments that it recognizes.
+
+ Qt debugging options (not available if Qt was compiled without the
+ QT_DEBUG flag defined):
+ \list
+ \o -nograb, tells Qt that it must never grab the mouse or the keyboard.
+ \o -dograb (only under X11), running under a debugger can cause
+ an implicit -nograb, use -dograb to override.
+ \o -sync (only under X11), switches to synchronous mode for
+ debugging.
+ \endlist
+
+ See \link debug.html Debugging Techniques \endlink for a more
+ detailed explanation.
+
+ All Qt programs automatically support the following command line options:
+ \list
+ \o -style= \e style, sets the application GUI style. Possible values
+ are \c motif, \c windows, and \c platinum. If you compiled Qt
+ with additional styles or have additional styles as plugins these
+ will be available to the \c -style command line option.
+ \o -style \e style, is the same as listed above.
+ \o -stylesheet= \e stylesheet, sets the application \l styleSheet. The value
+ must be a path to a file that contains the Style Sheet. Note that relative URLs
+ in the Style Sheet file are relative to the Style Sheet file's path.
+ \o -stylesheet \e stylesheet, is the same as listed above.
+ \o -session= \e session, restores the application from an earlier
+ \link session.html session \endlink.
+ \o -session \e session, is the same as listed above.
+ \o -widgetcount, prints debug message at the end about number of widgets left
+ undestroyed and maximum number of widgets existed at the same time
+ \o -reverse, sets the application's layout direction to Qt::RightToLeft
+ \o -graphicssystem, sets the backend to be used for on-screen
+ widgets and QPixmaps. Available options are \c{raster} and \c{opengl}.
+
+ \endlist
+
+ The Windows version of Qt also support one additional command line
+ option, if Direct3D support has been compiled into Qt:
+ \list
+ \o -direct3d will make the Direct3D paint engine the default widget
+ paint engine in Qt. \bold {This functionality is experimental.}
+ \endlist
+
+ The X11 version of Qt also supports some traditional X11
+ command line options:
+ \list
+ \o -display \e display, sets the X display (default is $DISPLAY).
+ \o -geometry \e geometry, sets the client geometry of the
+ first window that is shown.
+ \o -fn or \c -font \e font, defines the application font. The
+ font should be specified using an X logical font description.
+ \o -bg or \c -background \e color, sets the default background color
+ and an application palette (light and dark shades are calculated).
+ \o -fg or \c -foreground \e color, sets the default foreground color.
+ \o -btn or \c -button \e color, sets the default button color.
+ \o -name \e name, sets the application name.
+ \o -title \e title, sets the application title.
+ \o -visual \c TrueColor, forces the application to use a TrueColor visual
+ on an 8-bit display.
+ \o -ncols \e count, limits the number of colors allocated in the
+ color cube on an 8-bit display, if the application is using the
+ QApplication::ManyColor color specification. If \e count is
+ 216 then a 6x6x6 color cube is used (i.e. 6 levels of red, 6 of green,
+ and 6 of blue); for other values, a cube
+ approximately proportional to a 2x3x1 cube is used.
+ \o -cmap, causes the application to install a private color map
+ on an 8-bit display.
+ \o -im, sets the input method server (equivalent to setting the XMODIFIERS
+ environment variable)
+ \o -inputstyle, defines how the input is inserted into the given widget. E.g.,
+ \c onTheSpot makes the input appear directly in the widget, while
+ \c overTheSpot makes the input appear in a box floating over the
+ widget and is not inserted until the editing is done.
+ \endlist
+
+ \sa arguments()
+*/
+
+QApplication::QApplication(int &argc, char **argv)
+ : QCoreApplication(*new QApplicationPrivate(argc, argv, GuiClient))
+{ Q_D(QApplication); d->construct(); }
+
+QApplication::QApplication(int &argc, char **argv, int _internal)
+ : QCoreApplication(*new QApplicationPrivate(argc, argv, GuiClient))
+{ Q_D(QApplication); d->construct(); QApplicationPrivate::app_compile_version = _internal;}
+
+
+/*!
+ Constructs an application object with \a argc command line arguments
+ in \a argv. If \a GUIenabled is true, a GUI application is
+ constructed, otherwise a non-GUI (console) application is created.
+
+ \warning The data referred to by \a argc and \a argv must stay valid
+ for the entire lifetime of the QApplication object. In addition,
+ \a argc must be greater than zero and \a argv must contain at least
+ one valid character string.
+
+ Set \a GUIenabled to false for programs without a graphical user
+ interface that should be able to run without a window system.
+
+ On X11, the window system is initialized if \a GUIenabled is true.
+ If \a GUIenabled is false, the application does not connect to the
+ X server. On Windows and Macintosh, currently the window system is
+ always initialized, regardless of the value of GUIenabled. This may
+ change in future versions of Qt.
+
+ The following example shows how to create an application that
+ uses a graphical interface when available.
+
+ \snippet doc/src/snippets/code/src_gui_kernel_qapplication.cpp 0
+*/
+
+QApplication::QApplication(int &argc, char **argv, bool GUIenabled )
+ : QCoreApplication(*new QApplicationPrivate(argc, argv, GUIenabled ? GuiClient : Tty))
+{ Q_D(QApplication); d->construct(); }
+
+QApplication::QApplication(int &argc, char **argv, bool GUIenabled , int _internal)
+ : QCoreApplication(*new QApplicationPrivate(argc, argv, GUIenabled ? GuiClient : Tty))
+{ Q_D(QApplication); d->construct(); QApplicationPrivate::app_compile_version = _internal;}
+
+
+
+/*!
+ Constructs an application object with \a argc command line arguments
+ in \a argv.
+
+ \warning The data referred to by \a argc and \a argv must stay valid
+ for the entire lifetime of the QApplication object. In addition,
+ \a argc must be greater than zero and \a argv must contain at least
+ one valid character string.
+
+ With Qt for Embedded Linux, passing QApplication::GuiServer for \a type
+ makes this application the server (equivalent to running with the
+ \c -qws option).
+*/
+QApplication::QApplication(int &argc, char **argv, Type type)
+ : QCoreApplication(*new QApplicationPrivate(argc, argv, type))
+{ Q_D(QApplication); d->construct(); }
+
+QApplication::QApplication(int &argc, char **argv, Type type , int _internal)
+ : QCoreApplication(*new QApplicationPrivate(argc, argv, type))
+{ Q_D(QApplication); d->construct(); QApplicationPrivate::app_compile_version = _internal;}
+
+
+/*!
+ \internal
+*/
+void QApplicationPrivate::construct(
+#ifdef Q_WS_X11
+ Display *dpy, Qt::HANDLE visual, Qt::HANDLE cmap
+#endif
+ )
+{
+ initResources();
+
+ qt_is_gui_used = (qt_appType != QApplication::Tty);
+ process_cmdline();
+ // Must be called before initialize()
+ qt_init(this, qt_appType
+#ifdef Q_WS_X11
+ , dpy, visual, cmap
+#endif
+ );
+ initialize();
+ eventDispatcher->startingUp();
+
+#ifdef QT_EVAL
+ extern void qt_gui_eval_init(uint);
+ qt_gui_eval_init(application_type);
+#endif
+}
+
+#if defined(Q_WS_X11)
+// ### a string literal is a cont char*
+// ### using it as a char* is wrong and could lead to segfaults
+// ### if aargv is modified someday
+// ########## make it work with argc == argv == 0
+static int aargc = 1;
+static char *aargv[] = { (char*)"unknown", 0 };
+
+/*!
+ \fn QApplication::QApplication(Display* display, Qt::HANDLE visual, Qt::HANDLE colormap)
+
+ Create an application, given an already open display \a display. If \a
+ visual and \a colormap are non-zero, the application will use those as
+ the default Visual and Colormap contexts.
+
+ \warning Qt only supports TrueColor visuals at depths higher than 8
+ bits-per-pixel.
+
+ This is available only on X11.
+*/
+QApplication::QApplication(Display* dpy, Qt::HANDLE visual, Qt::HANDLE colormap)
+ : QCoreApplication(*new QApplicationPrivate(aargc, aargv, GuiClient))
+{
+ if (! dpy)
+ qWarning("QApplication: Invalid Display* argument");
+ Q_D(QApplication);
+ d->construct(dpy, visual, colormap);
+}
+
+QApplication::QApplication(Display* dpy, Qt::HANDLE visual, Qt::HANDLE colormap, int _internal)
+ : QCoreApplication(*new QApplicationPrivate(aargc, aargv, GuiClient))
+{
+ if (! dpy)
+ qWarning("QApplication: Invalid Display* argument");
+ Q_D(QApplication);
+ d->construct(dpy, visual, colormap);
+ QApplicationPrivate::app_compile_version = _internal;
+}
+
+/*!
+ \fn QApplication::QApplication(Display *display, int &argc, char **argv,
+ Qt::HANDLE visual, Qt::HANDLE colormap)
+
+ Create an application, given an already open \a display and using \a
+ argc command line arguments in \a argv. If \a visual and \a colormap
+ are non-zero, the application will use those as the default Visual
+ and Colormap contexts.
+
+ \warning Qt only supports TrueColor visuals at depths higher than 8
+ bits-per-pixel.
+
+ This is available only on X11.
+
+*/
+QApplication::QApplication(Display *dpy, int &argc, char **argv,
+ Qt::HANDLE visual, Qt::HANDLE colormap)
+ : QCoreApplication(*new QApplicationPrivate(argc, argv, GuiClient))
+{
+ if (! dpy)
+ qWarning("QApplication: Invalid Display* argument");
+ Q_D(QApplication);
+ d->construct(dpy, visual, colormap);
+}
+
+QApplication::QApplication(Display *dpy, int &argc, char **argv,
+ Qt::HANDLE visual, Qt::HANDLE colormap, int _internal)
+ : QCoreApplication(*new QApplicationPrivate(argc, argv, GuiClient))
+{
+ if (! dpy)
+ qWarning("QApplication: Invalid Display* argument");
+ Q_D(QApplication);
+ d->construct(dpy, visual, colormap);
+ QApplicationPrivate::app_compile_version = _internal;
+}
+
+#endif // Q_WS_X11
+
+
+/*!
+ Initializes the QApplication object, called from the constructors.
+*/
+extern void qInitDrawhelperAsm();
+
+void QApplicationPrivate::initialize()
+{
+ QWidgetPrivate::mapper = new QWidgetMapper;
+ QWidgetPrivate::uncreatedWidgets = new QWidgetSet;
+ if (qt_appType != QApplication::Tty)
+ (void) QApplication::style(); // trigger creation of application style
+ // trigger registering of QVariant's GUI types
+ extern int qRegisterGuiVariant();
+ qRegisterGuiVariant();
+
+ is_app_running = true; // no longer starting up
+
+ Q_Q(QApplication);
+#ifndef QT_NO_SESSIONMANAGER
+ // connect to the session manager
+ session_manager = new QSessionManager(q, session_id, session_key);
+#endif
+
+ if (qgetenv("QT_USE_NATIVE_WINDOWS").toInt() > 0)
+ q->setAttribute(Qt::AA_NativeWindows);
+
+#if defined(Q_WS_WIN)
+ // Alien is not currently working on Windows 98
+ if (QSysInfo::WindowsVersion & QSysInfo::WV_DOS_based)
+ q->setAttribute(Qt::AA_NativeWindows);
+#endif
+
+#ifdef Q_OS_WINCE
+#ifdef QT_AUTO_MAXIMIZE_THRESHOLD
+ autoMaximizeThreshold = QT_AUTO_MAXIMIZE_THRESHOLD;
+#else
+ if (qt_wince_is_mobile())
+ autoMaximizeThreshold = 50;
+ else
+ autoMaximizeThreshold = -1;
+#endif //QT_AUTO_MAXIMIZE_THRESHOLD
+#endif //Q_OS_WINCE
+
+ // Set up which span functions should be used in raster engine...
+ qInitDrawhelperAsm();
+
+#if !defined(Q_WS_X11) && !defined(Q_WS_QWS)
+ // initialize the graphics system - on X11 this is initialized inside
+ // qt_init() in qapplication_x11.cpp because of several reasons.
+ // On QWS, the graphics system is set by the QScreen plugin.
+ graphics_system = QGraphicsSystemFactory::create(graphics_system_name);
+#endif
+#ifndef QT_NO_WHEELEVENT
+#ifdef QT_MAC_USE_COCOA
+ QApplicationPrivate::wheel_scroll_lines = 1;
+#else
+ QApplicationPrivate::wheel_scroll_lines = 3;
+#endif
+#endif
+}
+
+/*!
+ Returns the type of application (\l Tty, GuiClient, or
+ GuiServer). The type is set when constructing the QApplication
+ object.
+*/
+QApplication::Type QApplication::type()
+{
+ return qt_appType;
+}
+
+/*****************************************************************************
+ Functions returning the active popup and modal widgets.
+ *****************************************************************************/
+
+/*!
+ Returns the active popup widget.
+
+ A popup widget is a special top-level widget that sets the \c
+ Qt::WType_Popup widget flag, e.g. the QMenu widget. When the
+ application opens a popup widget, all events are sent to the popup.
+ Normal widgets and modal widgets cannot be accessed before the popup
+ widget is closed.
+
+ Only other popup widgets may be opened when a popup widget is shown.
+ The popup widgets are organized in a stack. This function returns
+ the active popup widget at the top of the stack.
+
+ \sa activeModalWidget(), topLevelWidgets()
+*/
+
+QWidget *QApplication::activePopupWidget()
+{
+ return QApplicationPrivate::popupWidgets && !QApplicationPrivate::popupWidgets->isEmpty() ?
+ QApplicationPrivate::popupWidgets->last() : 0;
+}
+
+
+/*!
+ Returns the active modal widget.
+
+ A modal widget is a special top-level widget which is a subclass of
+ QDialog that specifies the modal parameter of the constructor as
+ true. A modal widget must be closed before the user can continue
+ with other parts of the program.
+
+ Modal widgets are organized in a stack. This function returns
+ the active modal widget at the top of the stack.
+
+ \sa activePopupWidget(), topLevelWidgets()
+*/
+
+QWidget *QApplication::activeModalWidget()
+{
+ return qt_modal_stack && !qt_modal_stack->isEmpty() ? qt_modal_stack->first() : 0;
+}
+
+/*!
+ Cleans up any window system resources that were allocated by this
+ application. Sets the global variable \c qApp to 0.
+*/
+
+QApplication::~QApplication()
+{
+ Q_D(QApplication);
+
+#ifndef QT_NO_CLIPBOARD
+ // flush clipboard contents
+ if (qt_clipboard) {
+ QEvent event(QEvent::Clipboard);
+ QApplication::sendEvent(qt_clipboard, &event);
+ }
+#endif
+
+ //### this should probable be done even later
+ qt_call_post_routines();
+
+ // kill timers before closing down the dispatcher
+ d->toolTipWakeUp.stop();
+ d->toolTipFallAsleep.stop();
+
+ d->eventDispatcher->closingDown();
+ d->eventDispatcher = 0;
+
+ delete qt_desktopWidget;
+ qt_desktopWidget = 0;
+ QApplicationPrivate::is_app_closing = true;
+ QApplicationPrivate::is_app_running = false;
+
+#ifndef QT_NO_CLIPBOARD
+ delete qt_clipboard;
+ qt_clipboard = 0;
+#endif
+
+ // delete widget mapper
+ if (QWidgetPrivate::mapper) {
+ QWidgetMapper * myMapper = QWidgetPrivate::mapper;
+ QWidgetPrivate::mapper = 0;
+ for (QWidgetMapper::Iterator it = myMapper->begin(); it != myMapper->end(); ++it) {
+ register QWidget *w = *it;
+ if (!w->parent()) // window
+ w->destroy(true, true);
+ }
+ delete myMapper;
+ }
+
+ // delete uncreated widgets
+ if (QWidgetPrivate::uncreatedWidgets) {
+ QWidgetSet *mySet = QWidgetPrivate::uncreatedWidgets;
+ QWidgetPrivate::uncreatedWidgets = 0;
+ for (QWidgetSet::Iterator it = mySet->begin(); it != mySet->end(); ++it) {
+ register QWidget *w = *it;
+ if (!w->parent()) // window
+ w->destroy(true, true);
+ }
+ delete mySet;
+ }
+
+ delete QApplicationPrivate::app_pal;
+ QApplicationPrivate::app_pal = 0;
+ delete QApplicationPrivate::sys_pal;
+ QApplicationPrivate::sys_pal = 0;
+ delete QApplicationPrivate::set_pal;
+ QApplicationPrivate::set_pal = 0;
+ app_palettes()->clear();
+
+ {
+ QMutexLocker locker(applicationFontMutex());
+ delete QApplicationPrivate::app_font;
+ QApplicationPrivate::app_font = 0;
+ }
+ delete QApplicationPrivate::sys_font;
+ QApplicationPrivate::sys_font = 0;
+ delete QApplicationPrivate::set_font;
+ QApplicationPrivate::set_font = 0;
+ app_fonts()->clear();
+
+ delete QApplicationPrivate::app_style;
+ QApplicationPrivate::app_style = 0;
+ delete QApplicationPrivate::app_icon;
+ QApplicationPrivate::app_icon = 0;
+#ifndef QT_NO_CURSOR
+ d->cursor_list.clear();
+#endif
+
+#ifndef QT_NO_DRAGANDDROP
+ if (qt_is_gui_used)
+ delete QDragManager::self();
+#endif
+
+ qt_cleanup();
+
+ if (QApplicationPrivate::widgetCount)
+ qDebug("Widgets left: %i Max widgets: %i \n", QWidgetPrivate::instanceCounter, QWidgetPrivate::maxInstances);
+#ifndef QT_NO_SESSIONMANAGER
+ delete d->session_manager;
+ d->session_manager = 0;
+#endif //QT_NO_SESSIONMANAGER
+
+ QApplicationPrivate::obey_desktop_settings = true;
+ QApplicationPrivate::cursor_flash_time = 1000;
+ QApplicationPrivate::mouse_double_click_time = 400;
+ QApplicationPrivate::keyboard_input_time = 400;
+
+ drag_time = 500;
+ drag_distance = 4;
+ layout_direction = Qt::LeftToRight;
+ QApplicationPrivate::app_strut = QSize(0, 0);
+ QApplicationPrivate::animate_ui = true;
+ QApplicationPrivate::animate_menu = false;
+ QApplicationPrivate::fade_menu = false;
+ QApplicationPrivate::animate_combo = false;
+ QApplicationPrivate::animate_tooltip = false;
+ QApplicationPrivate::fade_tooltip = false;
+ QApplicationPrivate::widgetCount = false;
+
+ // trigger unregistering of QVariant's GUI types
+ extern int qUnregisterGuiVariant();
+ qUnregisterGuiVariant();
+}
+
+
+/*!
+ \fn QWidget *QApplication::widgetAt(const QPoint &point)
+
+ Returns the widget at global screen position \a point, or 0 if there
+ is no Qt widget there.
+
+ This function can be slow.
+
+ \sa QCursor::pos(), QWidget::grabMouse(), QWidget::grabKeyboard()
+*/
+QWidget *QApplication::widgetAt(const QPoint &p)
+{
+ QWidget *window = QApplication::topLevelAt(p);
+ if (!window)
+ return 0;
+
+ QWidget *child = 0;
+
+ if (!window->testAttribute(Qt::WA_TransparentForMouseEvents))
+ child = window->childAt(window->mapFromGlobal(p));
+
+ if (child)
+ return child;
+
+ if (window->testAttribute(Qt::WA_TransparentForMouseEvents)) {
+ //shoot a hole in the widget and try once again,
+ //suboptimal on Qt for Embedded Linux where we do
+ //know the stacking order of the toplevels.
+ int x = p.x();
+ int y = p.y();
+ QRegion oldmask = window->mask();
+ QPoint wpoint = window->mapFromGlobal(QPoint(x, y));
+ QRegion newmask = (oldmask.isEmpty() ? QRegion(window->rect()) : oldmask)
+ - QRegion(wpoint.x(), wpoint.y(), 1, 1);
+ window->setMask(newmask);
+ QWidget *recurse = 0;
+ if (QApplication::topLevelAt(p) != window) // verify recursion will terminate
+ recurse = widgetAt(x, y);
+ if (oldmask.isEmpty())
+ window->clearMask();
+ else
+ window->setMask(oldmask);
+ return recurse;
+ }
+ return window;
+}
+
+/*!
+ \fn QWidget *QApplication::widgetAt(int x, int y)
+
+ \overload
+
+ Returns the widget at global screen position (\a x, \a y), or 0
+ if there is no Qt widget there.
+*/
+
+/*!
+ \fn void QApplication::setArgs(int argc, char **argv)
+ \internal
+*/
+
+
+
+/*!
+ \internal
+*/
+bool QApplication::compressEvent(QEvent *event, QObject *receiver, QPostEventList *postedEvents)
+{
+ if ((event->type() == QEvent::UpdateRequest
+#ifdef QT3_SUPPORT
+ || event->type() == QEvent::LayoutHint
+#endif
+ || event->type() == QEvent::LayoutRequest
+ || event->type() == QEvent::Resize
+ || event->type() == QEvent::Move
+ || event->type() == QEvent::LanguageChange
+ || event->type() == QEvent::InputMethod)) {
+ for (int i = 0; i < postedEvents->size(); ++i) {
+ const QPostEvent &cur = postedEvents->at(i);
+ if (cur.receiver != receiver || cur.event == 0 || cur.event->type() != event->type())
+ continue;
+ if (cur.event->type() == QEvent::LayoutRequest
+#ifdef QT3_SUPPORT
+ || cur.event->type() == QEvent::LayoutHint
+#endif
+ || cur.event->type() == QEvent::UpdateRequest) {
+ ;
+ } else if (cur.event->type() == QEvent::Resize) {
+ ((QResizeEvent *)(cur.event))->s = ((QResizeEvent *)event)->s;
+ } else if (cur.event->type() == QEvent::Move) {
+ ((QMoveEvent *)(cur.event))->p = ((QMoveEvent *)event)->p;
+ } else if (cur.event->type() == QEvent::LanguageChange) {
+ ;
+ } else if ( cur.event->type() == QEvent::InputMethod ) {
+ *(QInputMethodEvent *)(cur.event) = *(QInputMethodEvent *)event;
+ } else {
+ continue;
+ }
+ delete event;
+ return true;
+ }
+ return false;
+ }
+ return QCoreApplication::compressEvent(event, receiver, postedEvents);
+}
+
+/*!
+ \property QApplication::styleSheet
+ \brief the application style sheet
+ \since 4.2
+
+ By default, this property returns an empty string unless the user specifies
+ the \c{-stylesheet} option on the command line when running the application.
+
+ \sa QWidget::setStyle(), {Qt Style Sheets}
+*/
+
+/*!
+ \property QApplication::autoMaximizeThreshold
+ \since 4.4
+ \brief defines a threshold for auto maximizing widgets
+
+ The auto maximize threshold is only available
+ as part of Qt for Windows CE.
+
+ This property defines a threshold for the size of a window as a percentage
+ of the screen size. If the minimum size hint of a window exceeds the threshold,
+ calling show() will then cause the window to be maximized automatically.
+
+ Setting the threshold to be 100 or greater means that it will cause it to always
+ be maximized. Setting it to be 50 means that the widget is maximized if the vertical
+ minimum size hint is at least 50% of the vertical screen size.
+
+ If -1 is specified then this will disable the feature.
+
+ On Windows CE the default is -1 (i.e. it is disabled).
+ On Windows Mobile the default is 40.
+*/
+
+/*!
+ \property QApplication::autoSipEnabled
+ \since 4.5
+ \brief toggles automatic SIP (software input panel) visibility
+
+ The auto SIP property is only available
+ as part of Qt for Windows CE.
+
+ Set this property to true to automatically display the SIP when entering
+ widgets that accept keyboard input. This only affects widgets that have the
+ attribute WA_InputMethodEnabled set.
+*/
+
+#ifdef Q_OS_WINCE
+void QApplication::setAutoMaximizeThreshold(const int threshold)
+{
+ QApplicationPrivate::autoMaximizeThreshold = threshold;
+}
+
+int QApplication::autoMaximizeThreshold() const
+{
+ return QApplicationPrivate::autoMaximizeThreshold;
+}
+
+void QApplication::setAutoSipEnabled(const bool enabled)
+{
+ QApplicationPrivate::autoSipEnabled = enabled;
+}
+
+bool QApplication::autoSipEnabled() const
+{
+ return QApplicationPrivate::autoSipEnabled;
+}
+#endif
+
+#ifndef QT_NO_STYLE_STYLESHEET
+
+QString QApplication::styleSheet() const
+{
+ return QApplicationPrivate::styleSheet;
+}
+
+void QApplication::setStyleSheet(const QString& styleSheet)
+{
+ QApplicationPrivate::styleSheet = styleSheet;
+ QStyleSheetStyle *proxy = qobject_cast<QStyleSheetStyle*>(QApplicationPrivate::app_style);
+ if (styleSheet.isEmpty()) { // application style sheet removed
+ if (!proxy)
+ return; // there was no stylesheet before
+ setStyle(proxy->base);
+ } else if (proxy) { // style sheet update, just repolish
+ proxy->repolish(qApp);
+ } else { // stylesheet set the first time
+ QStyleSheetStyle *newProxy = new QStyleSheetStyle(QApplicationPrivate::app_style);
+ QApplicationPrivate::app_style->setParent(newProxy);
+ setStyle(newProxy);
+ }
+}
+
+#endif // QT_NO_STYLE_STYLESHEET
+
+/*!
+ Returns the application's style object.
+
+ \sa setStyle(), QStyle
+*/
+QStyle *QApplication::style()
+{
+ if (QApplicationPrivate::app_style)
+ return QApplicationPrivate::app_style;
+ if (!qt_is_gui_used) {
+ Q_ASSERT(!"No style available in non-gui applications!");
+ return 0;
+ }
+
+#if defined(Q_WS_X11)
+ if(!QApplicationPrivate::styleOverride)
+ QApplicationPrivate::x11_initialize_style(); // run-time search for default style
+#endif
+ if (!QApplicationPrivate::app_style) {
+ // Compile-time search for default style
+ //
+ QString style;
+ if (QApplicationPrivate::styleOverride) {
+ style = *QApplicationPrivate::styleOverride;
+ delete QApplicationPrivate::styleOverride;
+ QApplicationPrivate::styleOverride = 0;
+ } else {
+#if defined(Q_WS_WIN) && defined(Q_OS_WINCE)
+ if (qt_wince_is_smartphone() || qt_wince_is_pocket_pc())
+ style = QLatin1String("WindowsMobile");
+ else
+ style = QLatin1String("WindowsCE");
+
+#elif defined(Q_WS_WIN)
+ if ((QSysInfo::WindowsVersion >= QSysInfo::WV_VISTA
+ && QSysInfo::WindowsVersion < QSysInfo::WV_NT_based))
+ style = QLatin1String("WindowsVista");
+ else if ((QSysInfo::WindowsVersion >= QSysInfo::WV_XP
+ && QSysInfo::WindowsVersion < QSysInfo::WV_NT_based))
+ style = QLatin1String("WindowsXP");
+ else
+ style = QLatin1String("Windows"); // default styles for Windows
+#elif defined(Q_WS_X11) && defined(Q_OS_SOLARIS)
+ style = QLatin1String("CDE"); // default style for X11 on Solaris
+#elif defined(Q_WS_X11) && defined(Q_OS_IRIX)
+ style = QLatin1String("SGI"); // default style for X11 on IRIX
+#elif defined(Q_WS_X11) || defined(Q_WS_QWS)
+ style = QLatin1String("Plastique"); // default style for X11 and small devices
+#elif defined(Q_WS_MAC)
+ style = QLatin1String("Macintosh"); // default style for all Mac's
+#endif
+ }
+
+ QStyle *&app_style = QApplicationPrivate::app_style;
+ app_style = QStyleFactory::create(style);
+ if (!app_style) {
+ QStringList styles = QStyleFactory::keys();
+ for (int i = 0; i < styles.size(); ++i) {
+ if ((app_style = QStyleFactory::create(styles.at(i))))
+ break;
+ }
+ }
+ if (!app_style) {
+ Q_ASSERT(!"No styles available!");
+ return 0;
+ }
+ }
+ // take ownership of the style
+ QApplicationPrivate::app_style->setParent(qApp);
+
+ if (!QApplicationPrivate::sys_pal)
+ QApplicationPrivate::setSystemPalette(QApplicationPrivate::app_style->standardPalette());
+ if (QApplicationPrivate::set_pal) // repolish set palette with the new style
+ QApplication::setPalette(*QApplicationPrivate::set_pal);
+
+#ifndef QT_NO_STYLE_STYLESHEET
+ if (!QApplicationPrivate::styleSheet.isEmpty()) {
+ qApp->setStyleSheet(QApplicationPrivate::styleSheet);
+ } else
+#endif
+ QApplicationPrivate::app_style->polish(qApp);
+
+ return QApplicationPrivate::app_style;
+}
+
+/*!
+ Sets the application's GUI style to \a style. Ownership of the style
+ object is transferred to QApplication, so QApplication will delete
+ the style object on application exit or when a new style is set and
+ the old style is still the parent of the application object.
+
+ Example usage:
+ \snippet doc/src/snippets/code/src_gui_kernel_qapplication.cpp 1
+
+ When switching application styles, the color palette is set back to
+ the initial colors or the system defaults. This is necessary since
+ certain styles have to adapt the color palette to be fully
+ style-guide compliant.
+
+ Note that setting the style before a palette has been set
+ (i.e. before creating QApplication) will cause the application to
+ use QStyle::standardPalette() for the palette.
+
+ \warning Qt style sheets are currently not supported for custom QStyle
+ subclasses. We plan to address this in some future release.
+
+ \sa style(), QStyle, setPalette(), desktopSettingsAware()
+*/
+void QApplication::setStyle(QStyle *style)
+{
+ if (!style || style == QApplicationPrivate::app_style)
+ return;
+
+ QWidgetList all = allWidgets();
+
+ // clean up the old style
+ if (QApplicationPrivate::app_style) {
+ if (QApplicationPrivate::is_app_running && !QApplicationPrivate::is_app_closing) {
+ for (QWidgetList::ConstIterator it = all.constBegin(); it != all.constEnd(); ++it) {
+ register QWidget *w = *it;
+ if (!(w->windowType() == Qt::Desktop) && // except desktop
+ w->testAttribute(Qt::WA_WState_Polished)) { // has been polished
+ QApplicationPrivate::app_style->unpolish(w);
+ }
+ }
+ }
+ QApplicationPrivate::app_style->unpolish(qApp);
+ }
+
+ QStyle *old = QApplicationPrivate::app_style; // save
+
+#ifndef QT_NO_STYLE_STYLESHEET
+ if (!QApplicationPrivate::styleSheet.isEmpty() && !qobject_cast<QStyleSheetStyle *>(style)) {
+ // we have a stylesheet already and a new style is being set
+ QStyleSheetStyle *newProxy = new QStyleSheetStyle(style);
+ style->setParent(newProxy);
+ QApplicationPrivate::app_style = newProxy;
+ } else
+#endif // QT_NO_STYLE_STYLESHEET
+ QApplicationPrivate::app_style = style;
+
+ QApplicationPrivate::app_style->setParent(qApp); // take ownership
+
+ // take care of possible palette requirements of certain gui
+ // styles. Do it before polishing the application since the style
+ // might call QApplication::setPalette() itself
+ if (QApplicationPrivate::set_pal) {
+ QApplication::setPalette(*QApplicationPrivate::set_pal);
+ } else if (QApplicationPrivate::sys_pal) {
+ QApplicationPrivate::initializeWidgetPaletteHash();
+ QApplicationPrivate::setPalette_helper(*QApplicationPrivate::sys_pal, /*className=*/0, /*clearWidgetPaletteHash=*/false);
+ } else if (!QApplicationPrivate::sys_pal) {
+ // Initialize the sys_pal if it hasn't happened yet...
+ QApplicationPrivate::setSystemPalette(QApplicationPrivate::app_style->standardPalette());
+ }
+
+ // initialize the application with the new style
+ QApplicationPrivate::app_style->polish(qApp);
+
+ // re-polish existing widgets if necessary
+ if (QApplicationPrivate::is_app_running && !QApplicationPrivate::is_app_closing) {
+ for (QWidgetList::ConstIterator it1 = all.constBegin(); it1 != all.constEnd(); ++it1) {
+ register QWidget *w = *it1;
+ if (w->windowType() != Qt::Desktop && w->testAttribute(Qt::WA_WState_Polished)) {
+ if (w->style() == QApplicationPrivate::app_style)
+ QApplicationPrivate::app_style->polish(w); // repolish
+#ifndef QT_NO_STYLE_STYLESHEET
+ else
+ w->setStyleSheet(w->styleSheet()); // touch
+#endif
+ }
+ }
+
+ for (QWidgetList::ConstIterator it2 = all.constBegin(); it2 != all.constEnd(); ++it2) {
+ register QWidget *w = *it2;
+ if (w->windowType() != Qt::Desktop && !w->testAttribute(Qt::WA_SetStyle)) {
+ QEvent e(QEvent::StyleChange);
+ QApplication::sendEvent(w, &e);
+#ifdef QT3_SUPPORT
+ if (old)
+ w->styleChange(*old);
+#endif
+ w->update();
+ }
+ }
+ }
+
+#ifndef QT_NO_STYLE_STYLESHEET
+ if (QStyleSheetStyle *oldProxy = qobject_cast<QStyleSheetStyle *>(old)) {
+ oldProxy->deref();
+ } else
+#endif
+ if (old && old->parent() == qApp) {
+ delete old;
+ }
+
+ if (QApplicationPrivate::focus_widget) {
+ QFocusEvent in(QEvent::FocusIn, Qt::OtherFocusReason);
+ QApplication::sendEvent(QApplicationPrivate::focus_widget->style(), &in);
+ QApplicationPrivate::focus_widget->update();
+ }
+}
+
+/*!
+ \overload
+
+ Requests a QStyle object for \a style from the QStyleFactory.
+
+ The string must be one of the QStyleFactory::keys(), typically one
+ of "windows", "motif", "cde", "plastique", "windowsxp", or
+ "macintosh". Style names are case insensitive.
+
+ Returns 0 if an unknown \a style is passed, otherwise the QStyle object
+ returned is set as the application's GUI style.
+
+ \warning To ensure that the application's style is set correctly, it is
+ best to call this function before the QApplication constructor, if
+ possible.
+*/
+QStyle* QApplication::setStyle(const QString& style)
+{
+ QStyle *s = QStyleFactory::create(style);
+ if (!s)
+ return 0;
+
+ setStyle(s);
+ return s;
+}
+
+/*!
+ \since 4.5
+
+ Sets the default graphics backend to \a system, which will be used
+ for on-screen widgets and QPixmaps. The available systems are
+ \c{"native"}, \c{"raster"} and \c{"opengl"}.
+
+ Note that this function call overrides both the application
+ commandline \c{-graphicssystem} switch and the configure
+ \c{-graphicssystem} switch.
+
+ \warning This function must be called before the QApplication
+ constructor is called.
+
+ The \c{"opengl"} option is currently considered experimental.
+*/
+
+void QApplication::setGraphicsSystem(const QString &system)
+{
+ QApplicationPrivate::graphics_system_name = system;
+}
+
+/*!
+ Returns the color specification.
+ \sa QApplication::setColorSpec()
+ */
+
+int QApplication::colorSpec()
+{
+ return QApplicationPrivate::app_cspec;
+}
+
+/*!
+ Sets the color specification for the application to \a spec.
+
+ The color specification controls how the application allocates colors
+ when run on a display with a limited amount of colors, e.g. 8 bit / 256
+ color displays.
+
+ The color specification must be set before you create the QApplication
+ object.
+
+ The options are:
+ \list
+ \o QApplication::NormalColor.
+ This is the default color allocation strategy. Use this option if
+ your application uses buttons, menus, texts and pixmaps with few
+ colors. With this option, the application uses system global
+ colors. This works fine for most applications under X11, but on
+ Windows machines it may cause dithering of non-standard colors.
+ \o QApplication::CustomColor.
+ Use this option if your application needs a small number of custom
+ colors. On X11, this option is the same as NormalColor. On Windows, Qt
+ creates a Windows palette, and allocates colors to it on demand.
+ \o QApplication::ManyColor.
+ Use this option if your application is very color hungry
+ (e.g. it requires thousands of colors).
+ Under X11 the effect is:
+ \list
+ \o For 256-color displays which have at best a 256 color true
+ color visual, the default visual is used, and colors are
+ allocated from a color cube. The color cube is the 6x6x6 (216
+ color) "Web palette" (the red, green, and blue components
+ always have one of the following values: 0x00, 0x33, 0x66,
+ 0x99, 0xCC, or 0xFF), but the number of colors can be changed
+ by the \e -ncols option. The user can force the application to
+ use the true color visual with the \link
+ QApplication::QApplication() -visual \endlink option.
+ \o For 256-color displays which have a true color visual with more
+ than 256 colors, use that visual. Silicon Graphics X servers
+ have this feature, for example. They provide an 8 bit visual
+ by default but can deliver true color when asked.
+ \endlist
+ On Windows, Qt creates a Windows palette, and fills it with a color cube.
+ \endlist
+
+ Be aware that the CustomColor and ManyColor choices may lead to colormap
+ flashing: The foreground application gets (most) of the available
+ colors, while the background windows will look less attractive.
+
+ Example:
+
+ \snippet doc/src/snippets/code/src_gui_kernel_qapplication.cpp 2
+
+ \sa colorSpec()
+*/
+
+void QApplication::setColorSpec(int spec)
+{
+ if (qApp)
+ qWarning("QApplication::setColorSpec: This function must be "
+ "called before the QApplication object is created");
+ QApplicationPrivate::app_cspec = spec;
+}
+
+/*!
+ \property QApplication::globalStrut
+ \brief the minimum size that any GUI element that the user can interact
+ with should have
+
+ For example, no button should be resized to be smaller than the
+ global strut size. The strut size should be considered when
+ reimplementing GUI controls that may be used on touch-screens or
+ similar I/O devices.
+
+ Example:
+
+ \snippet doc/src/snippets/code/src_gui_kernel_qapplication.cpp 3
+
+ By default, this property contains a QSize object with zero width and height.
+*/
+QSize QApplication::globalStrut()
+{
+ return QApplicationPrivate::app_strut;
+}
+
+void QApplication::setGlobalStrut(const QSize& strut)
+{
+ QApplicationPrivate::app_strut = strut;
+}
+
+/*!
+ Returns the application palette.
+
+ \sa setPalette(), QWidget::palette()
+*/
+QPalette QApplication::palette()
+{
+ if (!QApplicationPrivate::app_pal)
+ QApplicationPrivate::app_pal = new QPalette(Qt::black);
+ return *QApplicationPrivate::app_pal;
+}
+
+/*!
+ \fn QPalette QApplication::palette(const QWidget* widget)
+ \overload
+
+ If a \a widget is passed, the default palette for the
+ widget's class is returned. This may or may not be the application
+ palette. In most cases there isn't a special palette for certain
+ types of widgets, but one notable exception is the popup menu
+ under Windows, if the user has defined a special background color
+ for menus in the display settings.
+
+ \sa setPalette(), QWidget::palette()
+*/
+QPalette QApplication::palette(const QWidget* w)
+{
+ PaletteHash *hash = app_palettes();
+ if (w && hash && hash->size()) {
+ QHash<QByteArray, QPalette>::ConstIterator it = hash->constFind(w->metaObject()->className());
+ if (it != hash->constEnd())
+ return *it;
+ for (it = hash->constBegin(); it != hash->constEnd(); ++it) {
+ if (w->inherits(it.key()))
+ return it.value();
+ }
+ }
+ return palette();
+}
+
+/*!
+ \overload
+
+ Returns the palette for widgets of the given \a className.
+
+ \sa setPalette(), QWidget::palette()
+*/
+QPalette QApplication::palette(const char *className)
+{
+ if (!QApplicationPrivate::app_pal)
+ palette();
+ PaletteHash *hash = app_palettes();
+ if (className && hash && hash->size()) {
+ QHash<QByteArray, QPalette>::ConstIterator it = hash->constFind(className);
+ if (it != hash->constEnd())
+ return *it;
+ }
+ return *QApplicationPrivate::app_pal;
+}
+
+void QApplicationPrivate::setPalette_helper(const QPalette &palette, const char* className, bool clearWidgetPaletteHash)
+{
+ QPalette pal = palette;
+
+ if (QApplicationPrivate::app_style)
+ QApplicationPrivate::app_style->polish(pal); // NB: non-const reference
+
+ bool all = false;
+ PaletteHash *hash = app_palettes();
+ if (!className) {
+ if (QApplicationPrivate::app_pal && pal.isCopyOf(*QApplicationPrivate::app_pal))
+ return;
+ if (!QApplicationPrivate::app_pal)
+ QApplicationPrivate::app_pal = new QPalette(pal);
+ else
+ *QApplicationPrivate::app_pal = pal;
+ if (hash && hash->size()) {
+ all = true;
+ if (clearWidgetPaletteHash)
+ hash->clear();
+ }
+ } else if (hash) {
+ hash->insert(className, pal);
+ }
+
+ if (QApplicationPrivate::is_app_running && !QApplicationPrivate::is_app_closing) {
+ // Send ApplicationPaletteChange to qApp itself, and to the widgets.
+ QEvent e(QEvent::ApplicationPaletteChange);
+ QApplication::sendEvent(QApplication::instance(), &e);
+
+ QWidgetList wids = QApplication::allWidgets();
+ for (QWidgetList::ConstIterator it = wids.constBegin(); it != wids.constEnd(); ++it) {
+ register QWidget *w = *it;
+ if (all || (!className && w->isWindow()) || w->inherits(className)) // matching class
+ QApplication::sendEvent(w, &e);
+ }
+
+ // Send to all scenes as well.
+#ifndef QT_NO_GRAPHICSVIEW
+ QList<QGraphicsScene *> &scenes = qApp->d_func()->scene_list;
+ for (QList<QGraphicsScene *>::ConstIterator it = scenes.constBegin();
+ it != scenes.constEnd(); ++it) {
+ QApplication::sendEvent(*it, &e);
+ }
+#endif //QT_NO_GRAPHICSVIEW
+ }
+ if (!className && (!QApplicationPrivate::sys_pal || !palette.isCopyOf(*QApplicationPrivate::sys_pal))) {
+ if (!QApplicationPrivate::set_pal)
+ QApplicationPrivate::set_pal = new QPalette(palette);
+ else
+ *QApplicationPrivate::set_pal = palette;
+ }
+}
+
+/*!
+ Changes the default application palette to \a palette.
+
+ If \a className is passed, the change applies only to widgets that
+ inherit \a className (as reported by QObject::inherits()). If
+ \a className is left 0, the change affects all widgets, thus overriding
+ any previously set class specific palettes.
+
+ The palette may be changed according to the current GUI style in
+ QStyle::polish().
+
+ \warning Do not use this function in conjunction with \l{Qt Style Sheets}.
+ When using style sheets, the palette of a widget can be customized using the "color",
+ "background-color", "selection-color", "selection-background-color" and
+ "alternate-background-color".
+
+ Note that some styles do not use the palette for all drawing,
+ for instance, if they make use of native theme engines. This is
+ the case for the Windows XP, Windows Vista, and Mac OS X styles.
+
+ \sa QWidget::setPalette(), palette(), QStyle::polish()
+*/
+
+void QApplication::setPalette(const QPalette &palette, const char* className)
+{
+ QApplicationPrivate::setPalette_helper(palette, className, /*clearWidgetPaletteHash=*/ true);
+}
+
+
+
+void QApplicationPrivate::setSystemPalette(const QPalette &pal)
+{
+ QPalette adjusted;
+
+#if 0
+ // adjust the system palette to avoid dithering
+ QColormap cmap = QColormap::instance();
+ if (cmap.depths() > 4 && cmap.depths() < 24) {
+ for (int g = 0; g < QPalette::NColorGroups; g++)
+ for (int i = 0; i < QPalette::NColorRoles; i++) {
+ QColor color = pal.color((QPalette::ColorGroup)g, (QPalette::ColorRole)i);
+ color = cmap.colorAt(cmap.pixel(color));
+ adjusted.setColor((QPalette::ColorGroup)g, (QPalette::ColorRole) i, color);
+ }
+ }
+#else
+ adjusted = pal;
+#endif
+
+ if (!sys_pal)
+ sys_pal = new QPalette(adjusted);
+ else
+ *sys_pal = adjusted;
+
+
+ if (!QApplicationPrivate::set_pal)
+ QApplication::setPalette(*sys_pal);
+}
+
+/*!
+ Returns the default application font.
+
+ \sa fontMetrics(), QWidget::font()
+*/
+QFont QApplication::font()
+{
+ QMutexLocker locker(applicationFontMutex());
+ if (!QApplicationPrivate::app_font)
+ QApplicationPrivate::app_font = new QFont(QLatin1String("Helvetica"));
+ return *QApplicationPrivate::app_font;
+}
+
+/*!
+ \overload
+
+ Returns the default font for the \a widget.
+
+ \sa fontMetrics(), QWidget::setFont()
+*/
+
+QFont QApplication::font(const QWidget *widget)
+{
+ FontHash *hash = app_fonts();
+
+#ifdef Q_WS_MAC
+ // short circuit for small and mini controls
+ if (widget->testAttribute(Qt::WA_MacSmallSize)) {
+ return hash->value("QSmallFont");
+ } else if (widget->testAttribute(Qt::WA_MacMiniSize)) {
+ return hash->value("QMiniFont");
+ }
+#endif
+ if (widget && hash && hash->size()) {
+ QHash<QByteArray, QFont>::ConstIterator it =
+ hash->constFind(widget->metaObject()->className());
+ if (it != hash->constEnd())
+ return it.value();
+ for (it = hash->constBegin(); it != hash->constEnd(); ++it) {
+ if (widget->inherits(it.key()))
+ return it.value();
+ }
+ }
+ return font();
+}
+
+/*!
+ \overload
+
+ Returns the font for widgets of the given \a className.
+
+ \sa setFont(), QWidget::font()
+*/
+QFont QApplication::font(const char *className)
+{
+ FontHash *hash = app_fonts();
+ if (className && hash && hash->size()) {
+ QHash<QByteArray, QFont>::ConstIterator it = hash->constFind(className);
+ if (it != hash->constEnd())
+ return *it;
+ }
+ return font();
+}
+
+
+/*!
+ Changes the default application font to \a font. If \a className
+ is passed, the change applies only to classes that inherit \a
+ className (as reported by QObject::inherits()).
+
+ On application start-up, the default font depends on the window
+ system. It can vary depending on both the window system version and
+ the locale. This function lets you override the default font; but
+ overriding may be a bad idea because, for example, some locales need
+ extra large fonts to support their special characters.
+
+ \warning Do not use this function in conjunction with \l{Qt Style Sheets}.
+ The font of an application can be customized using the "font" style sheet
+ property. To set a bold font for all QPushButtons, set the application
+ styleSheet() as "QPushButton { font: bold }"
+
+ \sa font(), fontMetrics(), QWidget::setFont()
+*/
+
+void QApplication::setFont(const QFont &font, const char *className)
+{
+ bool all = false;
+ FontHash *hash = app_fonts();
+ if (!className) {
+ QMutexLocker locker(applicationFontMutex());
+ if (!QApplicationPrivate::app_font)
+ QApplicationPrivate::app_font = new QFont(font);
+ else
+ *QApplicationPrivate::app_font = font;
+ if (hash && hash->size()) {
+ all = true;
+ hash->clear();
+ }
+ } else if (hash) {
+ hash->insert(className, font);
+ }
+ if (QApplicationPrivate::is_app_running && !QApplicationPrivate::is_app_closing) {
+ // Send ApplicationFontChange to qApp itself, and to the widgets.
+ QEvent e(QEvent::ApplicationFontChange);
+ QApplication::sendEvent(QApplication::instance(), &e);
+
+ QWidgetList wids = QApplication::allWidgets();
+ for (QWidgetList::ConstIterator it = wids.constBegin(); it != wids.constEnd(); ++it) {
+ register QWidget *w = *it;
+ if (all || (!className && w->isWindow()) || w->inherits(className)) // matching class
+ sendEvent(w, &e);
+ }
+
+#ifndef QT_NO_GRAPHICSVIEW
+ // Send to all scenes as well.
+ QList<QGraphicsScene *> &scenes = qApp->d_func()->scene_list;
+ for (QList<QGraphicsScene *>::ConstIterator it = scenes.constBegin();
+ it != scenes.constEnd(); ++it) {
+ QApplication::sendEvent(*it, &e);
+ }
+#endif //QT_NO_GRAPHICSVIEW
+ }
+ if (!className && (!QApplicationPrivate::sys_font || !font.isCopyOf(*QApplicationPrivate::sys_font))) {
+ if (!QApplicationPrivate::set_font)
+ QApplicationPrivate::set_font = new QFont(font);
+ else
+ *QApplicationPrivate::set_font = font;
+ }
+}
+
+/*! \internal
+*/
+void QApplicationPrivate::setSystemFont(const QFont &font)
+{
+ if (!sys_font)
+ sys_font = new QFont(font);
+ else
+ *sys_font = font;
+
+ if (!QApplicationPrivate::set_font)
+ QApplication::setFont(*sys_font);
+}
+
+/*!
+ \property QApplication::windowIcon
+ \brief the default window icon
+
+ \sa QWidget::setWindowIcon(), {Setting the Application Icon}
+*/
+QIcon QApplication::windowIcon()
+{
+ return QApplicationPrivate::app_icon ? *QApplicationPrivate::app_icon : QIcon();
+}
+
+void QApplication::setWindowIcon(const QIcon &icon)
+{
+ if (!QApplicationPrivate::app_icon)
+ QApplicationPrivate::app_icon = new QIcon();
+ *QApplicationPrivate::app_icon = icon;
+ if (QApplicationPrivate::is_app_running && !QApplicationPrivate::is_app_closing) {
+#ifdef Q_WS_MAC
+ void qt_mac_set_app_icon(const QPixmap &); //qapplication_mac.cpp
+ QSize size = QApplicationPrivate::app_icon->actualSize(QSize(128, 128));
+ qt_mac_set_app_icon(QApplicationPrivate::app_icon->pixmap(size));
+#endif
+ QEvent e(QEvent::ApplicationWindowIconChange);
+ QWidgetList all = QApplication::allWidgets();
+ for (QWidgetList::ConstIterator it = all.constBegin(); it != all.constEnd(); ++it) {
+ register QWidget *w = *it;
+ if (w->isWindow())
+ sendEvent(w, &e);
+ }
+ }
+}
+
+/*!
+ Returns a list of the top-level widgets (windows) in the
+ application.
+
+ Note that some of the top-level widgets may be hidden, for
+ example a tooltip if no tooltip is currently shown.
+
+ Example:
+
+ \snippet doc/src/snippets/code/src_gui_kernel_qapplication.cpp 4
+
+ \sa allWidgets(), QWidget::isWindow(), QWidget::isHidden()
+*/
+QWidgetList QApplication::topLevelWidgets()
+{
+ QWidgetList list;
+ QWidgetList all = allWidgets();
+
+ for (QWidgetList::ConstIterator it = all.constBegin(); it != all.constEnd(); ++it) {
+ QWidget *w = *it;
+ if (w->isWindow() && w->windowType() != Qt::Desktop)
+ list.append(w);
+ }
+ return list;
+}
+
+/*!
+ Returns a list of all the widgets in the application.
+
+ The list is empty (QList::isEmpty()) if there are no widgets.
+
+ Note that some of the widgets may be hidden.
+
+ Example:
+ \snippet doc/src/snippets/code/src_gui_kernel_qapplication.cpp 5
+
+ \sa topLevelWidgets(), QWidget::isVisible()
+*/
+
+QWidgetList QApplication::allWidgets()
+{
+ QWidgetList list;
+ if (QWidgetPrivate::mapper)
+ list += QWidgetPrivate::mapper->values();
+ if (QWidgetPrivate::uncreatedWidgets)
+ list += QWidgetPrivate::uncreatedWidgets->toList();
+ return list;
+}
+
+/*!
+ Returns the application widget that has the keyboard input focus, or
+ 0 if no widget in this application has the focus.
+
+ \sa QWidget::setFocus(), QWidget::hasFocus(), activeWindow(), focusChanged()
+*/
+
+QWidget *QApplication::focusWidget()
+{
+ return QApplicationPrivate::focus_widget;
+}
+
+void QApplicationPrivate::setFocusWidget(QWidget *focus, Qt::FocusReason reason)
+{
+ if (focus && focus->window()
+#ifndef QT_NO_GRAPHICSVIEW
+ && focus->window()->graphicsProxyWidget()
+#endif
+ )
+ return;
+
+ hidden_focus_widget = 0;
+
+ if (focus != focus_widget) {
+ if (focus && focus->isHidden()) {
+ hidden_focus_widget = focus;
+ return;
+ }
+
+ if (focus && (reason == Qt::BacktabFocusReason || reason == Qt::TabFocusReason)
+ && qt_in_tab_key_event)
+ focus->window()->setAttribute(Qt::WA_KeyboardFocusChange);
+ else if (focus && reason == Qt::ShortcutFocusReason) {
+ focus->window()->setAttribute(Qt::WA_KeyboardFocusChange);
+ }
+ QWidget *prev = focus_widget;
+ focus_widget = focus;
+
+ if (prev && reason != Qt::PopupFocusReason && reason != Qt::MenuBarFocusReason &&
+ prev->testAttribute(Qt::WA_InputMethodEnabled)) {
+ QInputContext *qic = prev->inputContext();
+ if(qic) {
+ qic->reset();
+ qic->setFocusWidget(0);
+ }
+ }
+
+ if(focus_widget)
+ focus_widget->d_func()->setFocus_sys();
+
+ if (reason != Qt::NoFocusReason) {
+
+ //send events
+ if (prev) {
+#ifdef QT_KEYPAD_NAVIGATION
+ if (QApplication::keypadNavigationEnabled()) {
+ if (prev->hasEditFocus() && reason != Qt::PopupFocusReason)
+ prev->setEditFocus(false);
+ }
+#endif
+ QFocusEvent out(QEvent::FocusOut, reason);
+ QPointer<QWidget> that = prev;
+ QApplication::sendEvent(prev, &out);
+ if (that)
+ QApplication::sendEvent(that->style(), &out);
+ }
+ if(focus && QApplicationPrivate::focus_widget == focus) {
+ if (focus->testAttribute(Qt::WA_InputMethodEnabled)) {
+ QInputContext *qic = focus->inputContext();
+ if (qic && focus_widget->testAttribute(Qt::WA_WState_Created))
+ qic->setFocusWidget( focus_widget );
+ }
+ QFocusEvent in(QEvent::FocusIn, reason);
+ QPointer<QWidget> that = focus;
+ QApplication::sendEvent(focus, &in);
+ if (that)
+ QApplication::sendEvent(that->style(), &in);
+ }
+ }
+ emit qApp->focusChanged(prev, focus_widget);
+ }
+}
+
+
+/*!
+ Returns the application top-level window that has the keyboard input
+ focus, or 0 if no application window has the focus. Note that
+ there might be an activeWindow() even if there is no focusWidget(),
+ for example if no widget in that window accepts key events.
+
+ \sa QWidget::setFocus(), QWidget::hasFocus(), focusWidget()
+*/
+
+QWidget *QApplication::activeWindow()
+{
+ return QApplicationPrivate::active_window;
+}
+
+/*!
+ Returns display (screen) font metrics for the application font.
+
+ \sa font(), setFont(), QWidget::fontMetrics(), QPainter::fontMetrics()
+*/
+
+QFontMetrics QApplication::fontMetrics()
+{
+ return desktop()->fontMetrics();
+}
+
+
+/*!
+ Closes all top-level windows.
+
+ This function is particularly useful for applications with many
+ top-level windows. It could, for example, be connected to a
+ \gui{Exit} entry in the \gui{File} menu:
+
+ \snippet examples/mainwindows/mdi/mainwindow.cpp 0
+
+ The windows are closed in random order, until one window does not
+ accept the close event. The application quits when the last window
+ was successfully closed; this can be turned off by setting \l
+ quitOnLastWindowClosed to false.
+
+ \sa quitOnLastWindowClosed, lastWindowClosed() QWidget::close(), QWidget::closeEvent(), lastWindowClosed(),
+ quit(), topLevelWidgets(), QWidget::isWindow()
+*/
+void QApplication::closeAllWindows()
+{
+ bool did_close = true;
+ QWidget *w;
+ while((w = activeModalWidget()) && did_close) {
+ if(!w->isVisible())
+ break;
+ did_close = w->close();
+ }
+ QWidgetList list = QApplication::topLevelWidgets();
+ for (int i = 0; did_close && i < list.size(); ++i) {
+ w = list.at(i);
+ if (w->isVisible() && w->windowType() != Qt::Desktop) {
+ did_close = w->close();
+ list = QApplication::topLevelWidgets();
+ i = -1;
+ }
+ }
+}
+
+/*!
+ Displays a simple message box about Qt. The message includes the
+ version number of Qt being used by the application.
+
+ This is useful for inclusion in the \gui Help menu of an application,
+ as shown in the \l{mainwindows/menus}{Menus} example.
+
+ This function is a convenience slot for QMessageBox::aboutQt().
+*/
+void QApplication::aboutQt()
+{
+#ifndef QT_NO_MESSAGEBOX
+ QMessageBox::aboutQt(
+#ifdef Q_WS_MAC
+ 0
+#else
+ activeWindow()
+#endif // Q_WS_MAC
+ );
+#endif // QT_NO_MESSAGEBOX
+}
+
+
+/*!
+ \fn void QApplication::lastWindowClosed()
+
+ This signal is emitted from QApplication::exec() when the last
+ visible primary window (i.e. window with no parent) with the
+ Qt::WA_QuitOnClose attribute set is closed.
+
+ By default,
+
+ \list
+
+ \i this attribute is set for all widgets except transient windows
+ such as splash screens, tool windows, and popup menus
+
+ \i QApplication implicitly quits when this signal is emitted.
+
+ \endlist
+
+ This feature be turned off by setting \l quitOnLastWindowClosed to
+ false.
+
+ \sa QWidget::close()
+*/
+
+/*!
+ \since 4.1
+ \fn void QApplication::focusChanged(QWidget *old, QWidget *now)
+
+ This signal is emitted when the widget that has keyboard focus
+ changed from \a old to \a now, i.e. because the user pressed the
+ tab-key, clicked into a widget or changed the active window. Note
+ that both \a old and \a now can be the null-pointer.
+
+ The signal is emitted after both widget have been notified about
+ the change through QFocusEvent.
+
+ \sa QWidget::setFocus() QWidget::clearFocus() Qt::FocusReason
+*/
+
+/*!
+ \since 4.5
+ \fn void QApplication::fontDatabaseChanged()
+
+ This signal is emitted when application fonts are loaded or removed.
+
+ \sa QFontDatabase::addApplicationFont()
+ \sa QFontDatabase::addApplicationFontFromData()
+ \sa QFontDatabase::removeAllApplicationFonts()
+ \sa QFontDatabase::removeApplicationFont()
+*/
+
+#ifndef QT_NO_TRANSLATION
+static bool qt_detectRTLLanguage()
+{
+ return force_reverse ^
+ (QApplication::tr("QT_LAYOUT_DIRECTION",
+ "Translate this string to the string 'LTR' in left-to-right"
+ " languages or to 'RTL' in right-to-left languages (such as Hebrew"
+ " and Arabic) to get proper widget layout.") == QLatin1String("RTL"));
+}
+#endif
+
+/*!\reimp
+
+*/
+bool QApplication::event(QEvent *e)
+{
+ Q_D(QApplication);
+ if(e->type() == QEvent::Close) {
+ QCloseEvent *ce = static_cast<QCloseEvent*>(e);
+ ce->accept();
+ closeAllWindows();
+
+ QWidgetList list = topLevelWidgets();
+ for (int i = 0; i < list.size(); ++i) {
+ QWidget *w = list.at(i);
+ if (w->isVisible() && !(w->windowType() == Qt::Desktop) && !(w->windowType() == Qt::Popup) &&
+ (!(w->windowType() == Qt::Dialog) || !w->parentWidget())) {
+ ce->ignore();
+ break;
+ }
+ }
+ if(ce->isAccepted())
+ return true;
+ } else if(e->type() == QEvent::LanguageChange) {
+#ifndef QT_NO_TRANSLATION
+ setLayoutDirection(qt_detectRTLLanguage()?Qt::RightToLeft:Qt::LeftToRight);
+#endif
+ QWidgetList list = topLevelWidgets();
+ for (int i = 0; i < list.size(); ++i) {
+ QWidget *w = list.at(i);
+ if (!(w->windowType() == Qt::Desktop))
+ postEvent(w, new QEvent(QEvent::LanguageChange));
+ }
+ } else if (e->type() == QEvent::Timer) {
+ QTimerEvent *te = static_cast<QTimerEvent*>(e);
+ Q_ASSERT(te != 0);
+ if (te->timerId() == d->toolTipWakeUp.timerId()) {
+ d->toolTipWakeUp.stop();
+ if (d->toolTipWidget) {
+ QWidget *w = d->toolTipWidget->window();
+ // show tooltip if WA_AlwaysShowToolTips is set, or if
+ // any ancestor of d->toolTipWidget is the active
+ // window
+ bool showToolTip = w->testAttribute(Qt::WA_AlwaysShowToolTips);
+ while (w && !showToolTip) {
+ showToolTip = w->isActiveWindow();
+ w = w->parentWidget();
+ w = w ? w->window() : 0;
+ }
+ if (showToolTip) {
+ QHelpEvent e(QEvent::ToolTip, d->toolTipPos, d->toolTipGlobalPos);
+ QApplication::sendEvent(d->toolTipWidget, &e);
+ if (e.isAccepted())
+ d->toolTipFallAsleep.start(2000, this);
+ }
+ }
+ } else if (te->timerId() == d->toolTipFallAsleep.timerId()) {
+ d->toolTipFallAsleep.stop();
+ }
+#ifdef QT_MAC_USE_COCOA
+ } else if (e->type() == QEvent::CocoaRequestModal) {
+ d->_q_runAppModalWindow();
+#endif
+ }
+ return QCoreApplication::event(e);
+}
+#if !defined(Q_WS_X11)
+
+// The doc and X implementation of this function is in qapplication_x11.cpp
+
+void QApplication::syncX() {} // do nothing
+
+#endif
+
+/*!
+ \fn Qt::WindowsVersion QApplication::winVersion()
+
+ Use \l QSysInfo::WindowsVersion instead.
+*/
+
+/*!
+ \fn void QApplication::setActiveWindow(QWidget* active)
+
+ Sets the active window to the \a active widget in response to a system
+ event. The function is called from the platform specific event
+ handlers.
+
+ \warning This function does \e not set the keyboard focus to the
+ active widget. Call QWidget::activateWindow() instead.
+
+ It sets the activeWindow() and focusWidget() attributes and sends
+ proper \l{QEvent::WindowActivate}{WindowActivate}/\l{QEvent::WindowDeactivate}{WindowDeactivate}
+ and \l{QEvent::FocusIn}{FocusIn}/\l{QEvent::FocusOut}{FocusOut} events
+ to all appropriate widgets. The window will then be painted in
+ active state (e.g. cursors in line edits will blink), and it will
+ have tool tips enabled.
+
+ \sa activeWindow(), QWidget::activateWindow()
+*/
+void QApplication::setActiveWindow(QWidget* act)
+{
+ QWidget* window = act?act->window():0;
+
+ if (QApplicationPrivate::active_window == window)
+ return;
+
+#ifndef QT_NO_GRAPHICSVIEW
+ if (window && window->graphicsProxyWidget()) {
+ // Activate the proxy's view->viewport() ?
+ return;
+ }
+#endif
+
+ QWidgetList toBeActivated;
+ QWidgetList toBeDeactivated;
+
+ if (QApplicationPrivate::active_window) {
+ if (style()->styleHint(QStyle::SH_Widget_ShareActivation, 0, QApplicationPrivate::active_window)) {
+ QWidgetList list = topLevelWidgets();
+ for (int i = 0; i < list.size(); ++i) {
+ QWidget *w = list.at(i);
+ if (w->isVisible() && w->isActiveWindow())
+ toBeDeactivated.append(w);
+ }
+ } else {
+ toBeDeactivated.append(QApplicationPrivate::active_window);
+ }
+ }
+
+#if !defined(Q_WS_MAC)
+ QWidget *previousActiveWindow = QApplicationPrivate::active_window;
+#endif
+ QApplicationPrivate::active_window = window;
+
+ if (QApplicationPrivate::active_window) {
+ if (style()->styleHint(QStyle::SH_Widget_ShareActivation, 0, QApplicationPrivate::active_window)) {
+ QWidgetList list = topLevelWidgets();
+ for (int i = 0; i < list.size(); ++i) {
+ QWidget *w = list.at(i);
+ if (w->isVisible() && w->isActiveWindow())
+ toBeActivated.append(w);
+ }
+ } else {
+ toBeActivated.append(QApplicationPrivate::active_window);
+ }
+
+ }
+
+ // first the activation/deactivation events
+ QEvent activationChange(QEvent::ActivationChange);
+ QEvent windowActivate(QEvent::WindowActivate);
+ QEvent windowDeactivate(QEvent::WindowDeactivate);
+
+#if !defined(Q_WS_MAC)
+ if (!previousActiveWindow) {
+ QEvent appActivate(QEvent::ApplicationActivate);
+ sendSpontaneousEvent(qApp, &appActivate);
+ }
+#endif
+
+ for (int i = 0; i < toBeActivated.size(); ++i) {
+ QWidget *w = toBeActivated.at(i);
+ sendSpontaneousEvent(w, &windowActivate);
+ sendSpontaneousEvent(w, &activationChange);
+ }
+
+ for(int i = 0; i < toBeDeactivated.size(); ++i) {
+ QWidget *w = toBeDeactivated.at(i);
+ sendSpontaneousEvent(w, &windowDeactivate);
+ sendSpontaneousEvent(w, &activationChange);
+ }
+
+#if !defined(Q_WS_MAC)
+ if (!QApplicationPrivate::active_window) {
+ QEvent appDeactivate(QEvent::ApplicationDeactivate);
+ sendSpontaneousEvent(qApp, &appDeactivate);
+ }
+#endif
+
+ if (QApplicationPrivate::popupWidgets == 0) { // !inPopupMode()
+ // then focus events
+ if (!QApplicationPrivate::active_window && QApplicationPrivate::focus_widget) {
+ QApplicationPrivate::setFocusWidget(0, Qt::ActiveWindowFocusReason);
+ } else if (QApplicationPrivate::active_window) {
+ QWidget *w = QApplicationPrivate::active_window->focusWidget();
+ if (w && w->isVisible() /*&& w->focusPolicy() != QWidget::NoFocus*/)
+ w->setFocus(Qt::ActiveWindowFocusReason);
+ else {
+ w = QApplicationPrivate::focusNextPrevChild_helper(QApplicationPrivate::active_window, true);
+ if (w) {
+ w->setFocus(Qt::ActiveWindowFocusReason);
+ } else {
+ // If the focus widget is not in the activate_window, clear the focus
+ w = QApplicationPrivate::focus_widget;
+ if (w && !QApplicationPrivate::active_window->isAncestorOf(w))
+ QApplicationPrivate::setFocusWidget(0, Qt::ActiveWindowFocusReason);
+ }
+ }
+ }
+ }
+}
+
+/*!internal
+ * Helper function that returns the new focus widget, but does not set the focus reason.
+ * Returns 0 if a new focus widget could not be found.
+*/
+QWidget *QApplicationPrivate::focusNextPrevChild_helper(QWidget *toplevel, bool next)
+{
+ uint focus_flag = qt_tab_all_widgets ? Qt::TabFocus : Qt::StrongFocus;
+
+ QWidget *f = toplevel->focusWidget();
+ if (!f)
+ f = toplevel;
+
+ QWidget *w = f;
+ QWidget *test = f->d_func()->focus_next;
+ while (test && test != f) {
+ if ((test->focusPolicy() & focus_flag) == focus_flag
+ && !(test->d_func()->extra && test->d_func()->extra->focus_proxy)
+ && test->isVisibleTo(toplevel) && test->isEnabled()
+ && !(w->windowType() == Qt::SubWindow && !w->isAncestorOf(test))
+ && (toplevel->windowType() != Qt::SubWindow || toplevel->isAncestorOf(test))) {
+ w = test;
+ if (next)
+ break;
+ }
+ test = test->d_func()->focus_next;
+ }
+ if (w == f) {
+ if (qt_in_tab_key_event) {
+ w->window()->setAttribute(Qt::WA_KeyboardFocusChange);
+ w->update();
+ }
+ return 0;
+ }
+ return w;
+}
+
+/*!
+ \fn void QApplicationPrivate::dispatchEnterLeave(QWidget* enter, QWidget* leave)
+ \internal
+
+ Creates the proper Enter/Leave event when widget \a enter is entered
+ and widget \a leave is left.
+ */
+#if defined(Q_WS_WIN)
+ extern void qt_win_set_cursor(QWidget *, bool);
+#elif defined(Q_WS_X11)
+ extern void qt_x11_enforce_cursor(QWidget *, bool);
+#endif
+
+void QApplicationPrivate::dispatchEnterLeave(QWidget* enter, QWidget* leave) {
+#if 0
+ if (leave) {
+ QEvent e(QEvent::Leave);
+ QApplication::sendEvent(leave, & e);
+ }
+ if (enter) {
+ QEvent e(QEvent::Enter);
+ QApplication::sendEvent(enter, & e);
+ }
+ return;
+#endif
+
+ QWidget* w ;
+ if ((!enter && !leave) || (enter == leave))
+ return;
+#ifdef ALIEN_DEBUG
+ qDebug() << "QApplicationPrivate::dispatchEnterLeave, ENTER:" << enter << "LEAVE:" << leave;
+#endif
+ QWidgetList leaveList;
+ QWidgetList enterList;
+
+ bool sameWindow = leave && enter && leave->window() == enter->window();
+ if (leave && !sameWindow) {
+ w = leave;
+ do {
+ leaveList.append(w);
+ } while (!w->isWindow() && (w = w->parentWidget()));
+ }
+ if (enter && !sameWindow) {
+ w = enter;
+ do {
+ enterList.prepend(w);
+ } while (!w->isWindow() && (w = w->parentWidget()));
+ }
+ if (sameWindow) {
+ int enterDepth = 0;
+ int leaveDepth = 0;
+ w = enter;
+ while (!w->isWindow() && (w = w->parentWidget()))
+ enterDepth++;
+ w = leave;
+ while (!w->isWindow() && (w = w->parentWidget()))
+ leaveDepth++;
+ QWidget* wenter = enter;
+ QWidget* wleave = leave;
+ while (enterDepth > leaveDepth) {
+ wenter = wenter->parentWidget();
+ enterDepth--;
+ }
+ while (leaveDepth > enterDepth) {
+ wleave = wleave->parentWidget();
+ leaveDepth--;
+ }
+ while (!wenter->isWindow() && wenter != wleave) {
+ wenter = wenter->parentWidget();
+ wleave = wleave->parentWidget();
+ }
+
+ w = leave;
+ while (w != wleave) {
+ leaveList.append(w);
+ w = w->parentWidget();
+ }
+ w = enter;
+ while (w != wenter) {
+ enterList.prepend(w);
+ w = w->parentWidget();
+ }
+ }
+
+ QEvent leaveEvent(QEvent::Leave);
+ for (int i = 0; i < leaveList.size(); ++i) {
+ w = leaveList.at(i);
+ if (!qApp->activeModalWidget() || QApplicationPrivate::tryModalHelper(w, 0)) {
+#if defined(Q_WS_WIN) || defined(Q_WS_X11)
+ if (leaveAfterRelease == w)
+ leaveAfterRelease = 0;
+#endif
+ QApplication::sendEvent(w, &leaveEvent);
+ if (w->testAttribute(Qt::WA_Hover) &&
+ (!qApp->activePopupWidget() || qApp->activePopupWidget() == w->window())) {
+ Q_ASSERT(instance());
+ QHoverEvent he(QEvent::HoverLeave, QPoint(-1, -1), w->mapFromGlobal(QApplicationPrivate::instance()->hoverGlobalPos));
+ qApp->d_func()->notify_helper(w, &he);
+ }
+ }
+ }
+ QPoint posEnter = QCursor::pos();
+ QEvent enterEvent(QEvent::Enter);
+ for (int i = 0; i < enterList.size(); ++i) {
+ w = enterList.at(i);
+ if (!qApp->activeModalWidget() || QApplicationPrivate::tryModalHelper(w, 0)) {
+ QApplication::sendEvent(w, &enterEvent);
+ if (w->testAttribute(Qt::WA_Hover) &&
+ (!qApp->activePopupWidget() || qApp->activePopupWidget() == w->window())) {
+ QHoverEvent he(QEvent::HoverEnter, w->mapFromGlobal(posEnter), QPoint(-1, -1));
+ qApp->d_func()->notify_helper(w, &he);
+ }
+ }
+ }
+
+#ifndef QT_NO_CURSOR
+ // Update cursor for alien/graphics widgets.
+
+ const bool enterOnAlien = (enter && (isAlien(enter) || enter->testAttribute(Qt::WA_DontShowOnScreen)));
+#if defined(Q_WS_X11)
+ //Whenever we leave an alien widget on X11, we need to reset its nativeParentWidget()'s cursor.
+ // This is not required on Windows as the cursor is reset on every single mouse move.
+ QWidget *parentOfLeavingCursor = 0;
+ for (int i = 0; i < leaveList.size(); ++i) {
+ w = leaveList.at(i);
+ if (!isAlien(w))
+ break;
+ if (w->testAttribute(Qt::WA_SetCursor)) {
+ parentOfLeavingCursor = w->parentWidget();
+ //continue looping, we need to find the downest alien widget with a cursor.
+ // (downest on the screen)
+ }
+ }
+ //check that we will not call qt_x11_enforce_cursor twice with the same native widget
+ if (parentOfLeavingCursor && (!enterOnAlien
+ || parentOfLeavingCursor->effectiveWinId() != enter->effectiveWinId())) {
+#ifndef QT_NO_GRAPHICSVIEW
+ if (!parentOfLeavingCursor->window()->graphicsProxyWidget())
+#endif
+ {
+ qt_x11_enforce_cursor(parentOfLeavingCursor,true);
+ }
+ }
+#endif
+ if (enterOnAlien) {
+ QWidget *cursorWidget = enter;
+ while (!cursorWidget->isWindow() && !cursorWidget->isEnabled())
+ cursorWidget = cursorWidget->parentWidget();
+
+ if (!cursorWidget)
+ return;
+
+#ifndef QT_NO_GRAPHICSVIEW
+ if (cursorWidget->window()->graphicsProxyWidget()) {
+ QWidgetPrivate::nearestGraphicsProxyWidget(cursorWidget)->setCursor(cursorWidget->cursor());
+ } else
+#endif
+ {
+#if defined(Q_WS_WIN)
+ qt_win_set_cursor(cursorWidget, true);
+#elif defined(Q_WS_X11)
+ qt_x11_enforce_cursor(cursorWidget, true);
+#endif
+ }
+ }
+#endif
+}
+
+/* exported for the benefit of testing tools */
+Q_GUI_EXPORT bool qt_tryModalHelper(QWidget *widget, QWidget **rettop)
+{
+ return QApplicationPrivate::tryModalHelper(widget, rettop);
+}
+
+/*! \internal
+ Returns true if \a widget is blocked by a modal window.
+ */
+bool QApplicationPrivate::isBlockedByModal(QWidget *widget)
+{
+ widget = widget->window();
+ if (!modalState())
+ return false;
+ if (qApp->activePopupWidget() == widget)
+ return false;
+
+ for (int i = 0; i < qt_modal_stack->size(); ++i) {
+ QWidget *modalWidget = qt_modal_stack->at(i);
+
+ {
+ // check if the active modal widget is our widget or a parent of our widget
+ QWidget *w = widget;
+ while (w) {
+ if (w == modalWidget)
+ return false;
+ w = w->parentWidget();
+ }
+#ifdef Q_WS_WIN
+ if ((widget->testAttribute(Qt::WA_WState_Created) || widget->data->winid)
+ && (modalWidget->testAttribute(Qt::WA_WState_Created) || modalWidget->data->winid)
+ && IsChild(modalWidget->data->winid, widget->data->winid))
+ return false;
+#endif
+ }
+
+ Qt::WindowModality windowModality = modalWidget->windowModality();
+ if (windowModality == Qt::NonModal) {
+ // determine the modality type if it hasn't been set on the
+ // modalWidget, this normally happens when waiting for a
+ // native dialog. use WindowModal if we are the child of a
+ // group leader; otherwise use ApplicationModal.
+ QWidget *m = modalWidget;
+ while (m && !m->testAttribute(Qt::WA_GroupLeader)) {
+ m = m->parentWidget();
+ if (m)
+ m = m->window();
+ }
+ windowModality = (m && m->testAttribute(Qt::WA_GroupLeader))
+ ? Qt::WindowModal
+ : Qt::ApplicationModal;
+ }
+
+ switch (windowModality) {
+ case Qt::ApplicationModal:
+ {
+ QWidget *groupLeaderForWidget = widget;
+ while (groupLeaderForWidget && !groupLeaderForWidget->testAttribute(Qt::WA_GroupLeader))
+ groupLeaderForWidget = groupLeaderForWidget->parentWidget();
+
+ if (groupLeaderForWidget) {
+ // if \a widget has WA_GroupLeader, it can only be blocked by ApplicationModal children
+ QWidget *m = modalWidget;
+ while (m && m != groupLeaderForWidget && !m->testAttribute(Qt::WA_GroupLeader))
+ m = m->parentWidget();
+ if (m == groupLeaderForWidget)
+ return true;
+ } else if (modalWidget != widget) {
+ return true;
+ }
+ break;
+ }
+ case Qt::WindowModal:
+ {
+ QWidget *w = widget;
+ do {
+ QWidget *m = modalWidget;
+ do {
+ if (m == w)
+ return true;
+ m = m->parentWidget();
+ if (m)
+ m = m->window();
+ } while (m);
+ w = w->parentWidget();
+ if (w)
+ w = w->window();
+ } while (w);
+ break;
+ }
+ default:
+ Q_ASSERT_X(false, "QApplication", "internal error, a modal widget cannot be modeless");
+ break;
+ }
+ }
+ return false;
+}
+
+/*!\internal
+ */
+void QApplicationPrivate::enterModal(QWidget *widget)
+{
+ QSet<QWidget*> blocked;
+ QList<QWidget*> windows = qApp->topLevelWidgets();
+ for (int i = 0; i < windows.count(); ++i) {
+ QWidget *window = windows.at(i);
+ if (window->windowType() != Qt::Tool && isBlockedByModal(window))
+ blocked.insert(window);
+ }
+
+ enterModal_sys(widget);
+
+ windows = qApp->topLevelWidgets();
+ QEvent e(QEvent::WindowBlocked);
+ for (int i = 0; i < windows.count(); ++i) {
+ QWidget *window = windows.at(i);
+ if (!blocked.contains(window) && window->windowType() != Qt::Tool && isBlockedByModal(window))
+ QApplication::sendEvent(window, &e);
+ }
+}
+
+/*!\internal
+ */
+void QApplicationPrivate::leaveModal(QWidget *widget)
+{
+ QSet<QWidget*> blocked;
+ QList<QWidget*> windows = qApp->topLevelWidgets();
+ for (int i = 0; i < windows.count(); ++i) {
+ QWidget *window = windows.at(i);
+ if (window->windowType() != Qt::Tool && isBlockedByModal(window))
+ blocked.insert(window);
+ }
+
+ leaveModal_sys(widget);
+
+ windows = qApp->topLevelWidgets();
+ QEvent e(QEvent::WindowUnblocked);
+ for (int i = 0; i < windows.count(); ++i) {
+ QWidget *window = windows.at(i);
+ if(blocked.contains(window) && window->windowType() != Qt::Tool && !isBlockedByModal(window))
+ QApplication::sendEvent(window, &e);
+ }
+}
+
+
+
+/*!\internal
+
+ Called from qapplication_\e{platform}.cpp, returns true
+ if the widget should accept the event.
+ */
+bool QApplicationPrivate::tryModalHelper(QWidget *widget, QWidget **rettop)
+{
+ QWidget *top = QApplication::activeModalWidget();
+ if (rettop)
+ *rettop = top;
+
+ // the active popup widget always gets the input event
+ if (qApp->activePopupWidget())
+ return true;
+
+#if defined(Q_WS_MAC) && defined(QT_MAC_USE_COCOA)
+ top = QApplicationPrivate::tryModalHelper_sys(top);
+ if (rettop)
+ *rettop = top;
+#endif
+
+ return !isBlockedByModal(widget->window());
+}
+
+/*
+ \internal
+*/
+QWidget *QApplicationPrivate::pickMouseReceiver(QWidget *candidate, const QPoint &globalPos,
+ QPoint &pos, QEvent::Type type,
+ Qt::MouseButtons buttons, QWidget *buttonDown,
+ QWidget *alienWidget)
+{
+ Q_ASSERT(candidate);
+
+ QWidget *mouseGrabber = QWidget::mouseGrabber();
+ if (((type == QEvent::MouseMove && buttons) || (type == QEvent::MouseButtonRelease))
+ && !buttonDown && !mouseGrabber) {
+ return 0;
+ }
+
+ if (alienWidget && alienWidget->internalWinId())
+ alienWidget = 0;
+
+ QWidget *receiver = candidate;
+
+ if (!mouseGrabber)
+ mouseGrabber = buttonDown ? buttonDown : alienWidget;
+
+ if (mouseGrabber && mouseGrabber != candidate) {
+ receiver = mouseGrabber;
+ pos = receiver->mapFromGlobal(globalPos);
+#ifdef ALIEN_DEBUG
+ qDebug() << " ** receiver adjusted to:" << receiver << "pos:" << pos;
+#endif
+ }
+
+ return receiver;
+
+}
+
+/*
+ \internal
+*/
+bool QApplicationPrivate::sendMouseEvent(QWidget *receiver, QMouseEvent *event,
+ QWidget *alienWidget, QWidget *nativeWidget,
+ QWidget **buttonDown, QPointer<QWidget> &lastMouseReceiver)
+{
+ Q_ASSERT(receiver);
+ Q_ASSERT(event);
+ Q_ASSERT(nativeWidget);
+ Q_ASSERT(buttonDown);
+
+ if (alienWidget && !isAlien(alienWidget))
+ alienWidget = 0;
+
+ QPointer<QWidget> receiverGuard = receiver;
+ QPointer<QWidget> nativeGuard = nativeWidget;
+ QPointer<QWidget> alienGuard = alienWidget;
+ QPointer<QWidget> activePopupWidget = qApp->activePopupWidget();
+
+ const bool graphicsWidget = nativeWidget->testAttribute(Qt::WA_DontShowOnScreen);
+
+ if (*buttonDown) {
+ if (!graphicsWidget) {
+ // Register the widget that shall receive a leave event
+ // after the last button is released.
+ if ((alienWidget || !receiver->internalWinId()) && !leaveAfterRelease && !QWidget::mouseGrabber())
+ leaveAfterRelease = *buttonDown;
+ if (event->type() == QEvent::MouseButtonRelease && !event->buttons())
+ *buttonDown = 0;
+ }
+ } else if (lastMouseReceiver) {
+ // Dispatch enter/leave if we move:
+ // 1) from an alien widget to another alien widget or
+ // from a native widget to an alien widget (first OR case)
+ // 2) from an alien widget to a native widget (second OR case)
+ if ((alienWidget && alienWidget != lastMouseReceiver)
+ || (isAlien(lastMouseReceiver) && !alienWidget)) {
+ if (activePopupWidget) {
+ if (!QWidget::mouseGrabber())
+ dispatchEnterLeave(alienWidget ? alienWidget : nativeWidget, lastMouseReceiver);
+ } else {
+ dispatchEnterLeave(receiver, lastMouseReceiver);
+ }
+
+ }
+ }
+
+#ifdef ALIEN_DEBUG
+ qDebug() << "QApplicationPrivate::sendMouseEvent: receiver:" << receiver
+ << "pos:" << event->pos() << "alien" << alienWidget << "button down"
+ << *buttonDown << "last" << lastMouseReceiver << "leave after release"
+ << leaveAfterRelease;
+#endif
+
+ // We need this quard in case someone opens a modal dialog / popup. If that's the case
+ // leaveAfterRelease is set to null, but we shall not update lastMouseReceiver.
+ const bool wasLeaveAfterRelease = leaveAfterRelease != 0;
+ bool result = QApplication::sendSpontaneousEvent(receiver, event);
+
+ if (!graphicsWidget && leaveAfterRelease && event->type() == QEvent::MouseButtonRelease
+ && !event->buttons() && QWidget::mouseGrabber() != leaveAfterRelease) {
+ // Dispatch enter/leave if:
+ // 1) the mouse grabber is an alien widget
+ // 2) the button is released on an alien widget
+
+ QWidget *enter = 0;
+ if (nativeGuard)
+ enter = alienGuard ? alienWidget : nativeWidget;
+ else // The receiver is typically deleted on mouse release with drag'n'drop.
+ enter = QApplication::widgetAt(event->globalPos());
+
+ dispatchEnterLeave(enter, leaveAfterRelease);
+ leaveAfterRelease = 0;
+ lastMouseReceiver = enter;
+ } else if (!wasLeaveAfterRelease) {
+ if (activePopupWidget) {
+ if (!QWidget::mouseGrabber())
+ lastMouseReceiver = alienGuard ? alienWidget : (nativeGuard ? nativeWidget : 0);
+ } else {
+ lastMouseReceiver = receiverGuard ? receiver : QApplication::widgetAt(event->globalPos());
+ }
+ }
+
+ return result;
+}
+
+#if defined(Q_WS_WIN) || defined(Q_WS_X11)
+/*
+ This function should only be called when the widget changes visibility, i.e.
+ when the \a widget is shown, hidden or deleted. This function does nothing
+ if the widget is a top-level or native, i.e. not an alien widget. In that
+ case enter/leave events are genereated by the underlying windowing system.
+*/
+extern QPointer<QWidget> qt_last_mouse_receiver;
+extern QWidget *qt_button_down;
+void QApplicationPrivate::sendSyntheticEnterLeave(QWidget *widget)
+{
+#ifndef QT_NO_CURSOR
+ if (!widget || widget->internalWinId() || widget->isWindow())
+ return;
+
+ const bool widgetInShow = widget->isVisible() && !widget->data->in_destructor;
+ if (!widgetInShow && widget != qt_last_mouse_receiver)
+ return; // Widget was not under the cursor when it was hidden/deleted.
+
+ if (widgetInShow && widget->parentWidget()->data->in_show)
+ return; // Ingore recursive show.
+
+ QWidget *mouseGrabber = QWidget::mouseGrabber();
+ if (mouseGrabber && mouseGrabber != widget)
+ return; // Someone else has the grab; enter/leave should not occur.
+
+ QWidget *tlw = widget->window();
+ if (tlw->data->in_destructor || tlw->data->is_closing)
+ return; // Closing down the business.
+
+ if (widgetInShow && (!qt_last_mouse_receiver || qt_last_mouse_receiver->window() != tlw))
+ return; // Mouse cursor not inside the widget's top-level.
+
+ const QPoint globalPos(QCursor::pos());
+ QPoint pos = tlw->mapFromGlobal(globalPos);
+
+ // Find the current widget under the mouse. If this function was called from
+ // the widget's destructor, we have to make sure childAt() doesn't take into
+ // account widgets that are about to be destructed.
+ QWidget *widgetUnderCursor = tlw->d_func()->childAt_helper(pos, widget->data->in_destructor);
+ if (!widgetUnderCursor)
+ widgetUnderCursor = tlw;
+ else
+ pos = widgetUnderCursor->mapFrom(tlw, pos);
+
+ if (widgetInShow && widgetUnderCursor != widget && !widget->isAncestorOf(widgetUnderCursor))
+ return; // Mouse cursor not inside the widget or any of its children.
+
+ if (widget->data->in_destructor && qt_button_down == widget)
+ qt_button_down = 0;
+
+ // Send enter/leave events followed by a mouse move on the entered widget.
+ QMouseEvent e(QEvent::MouseMove, pos, globalPos, Qt::NoButton, mouse_buttons, modifier_buttons);
+ sendMouseEvent(widgetUnderCursor, &e, widgetUnderCursor, tlw, &qt_button_down, qt_last_mouse_receiver);
+#endif // QT_NO_CURSOR
+}
+#endif // Q_WS_WIN || Q_WS_X11
+
+/*!
+ Returns the desktop widget (also called the root window).
+
+ Note that the desktop may be composed of multiple screens, so it would be
+ incorrect, for example, to attempt to \e center some widget in the
+ desktop's geometry. QDesktopWidget has various functions for obtaining
+ useful geometries upon the desktop, such as QDesktopWidget::screenGeometry()
+ and QDesktopWidget::availableGeometry().
+
+ On X11, it is also possible to draw on the desktop.
+*/
+QDesktopWidget *QApplication::desktop()
+{
+ if (!qt_desktopWidget || // not created yet
+ !(qt_desktopWidget->windowType() == Qt::Desktop)) { // reparented away
+ qt_desktopWidget = new QDesktopWidget();
+ }
+ return qt_desktopWidget;
+}
+
+#ifndef QT_NO_CLIPBOARD
+/*!
+ Returns a pointer to the application global clipboard.
+
+ \note The QApplication object should already be constructed before
+ accessing the clipboard.
+*/
+QClipboard *QApplication::clipboard()
+{
+ if (qt_clipboard == 0) {
+ if (!qApp) {
+ qWarning("QApplication: Must construct a QApplication before accessing a QClipboard");
+ return 0;
+ }
+ qt_clipboard = new QClipboard(0);
+ }
+ return qt_clipboard;
+}
+#endif // QT_NO_CLIPBOARD
+
+/*!
+ Sets whether Qt should use the system's standard colors, fonts,
+ etc., to \a on. By default, this is true.
+
+ This function must be called before creating the QApplication
+ object, like this:
+
+ \snippet doc/src/snippets/code/src_gui_kernel_qapplication.cpp 6
+
+ \sa desktopSettingsAware()
+*/
+void QApplication::setDesktopSettingsAware(bool on)
+{
+ QApplicationPrivate::obey_desktop_settings = on;
+}
+
+/*!
+ Returns true if Qt is set to use the system's standard colors,
+ fonts, etc.; otherwise returns false. The default is true.
+
+ \sa setDesktopSettingsAware()
+*/
+bool QApplication::desktopSettingsAware()
+{
+ return QApplicationPrivate::obey_desktop_settings;
+}
+
+/*!
+ Returns the current state of the modifier keys on the keyboard. The
+ current state is updated sychronously as the event queue is emptied
+ of events that will spontaneously change the keyboard state
+ (QEvent::KeyPress and QEvent::KeyRelease events).
+
+ It should be noted this may not reflect the actual keys held on the
+ input device at the time of calling but rather the modifiers as
+ last reported in one of the above events. If no keys are being held
+ Qt::NoModifier is returned.
+
+ \sa mouseButtons()
+*/
+
+Qt::KeyboardModifiers QApplication::keyboardModifiers()
+{
+ return QApplicationPrivate::modifier_buttons;
+}
+
+/*!
+ Returns the current state of the buttons on the mouse. The current
+ state is updated syncronously as the event queue is emptied of
+ events that will spontaneously change the mouse state
+ (QEvent::MouseButtonPress and QEvent::MouseButtonRelease events).
+
+ It should be noted this may not reflect the actual buttons held on
+ theinput device at the time of calling but rather the mouse buttons
+ as last reported in one of the above events. If no mouse buttons are
+ being held Qt::NoButton is returned.
+
+ \sa keyboardModifiers()
+*/
+
+Qt::MouseButtons QApplication::mouseButtons()
+{
+ return QApplicationPrivate::mouse_buttons;
+}
+
+/*!
+ \fn bool QApplication::isSessionRestored() const
+
+ Returns true if the application has been restored from an earlier
+ \link session.html session\endlink; otherwise returns false.
+
+ \sa sessionId(), commitData(), saveState()
+*/
+
+
+/*!
+ \fn QString QApplication::sessionId() const
+
+ Returns the current \link session.html session's\endlink identifier.
+
+ If the application has been restored from an earlier session, this
+ identifier is the same as it was in that previous session.
+
+ The session identifier is guaranteed to be unique both for different
+ applications and for different instances of the same application.
+
+ \sa isSessionRestored(), sessionKey(), commitData(), saveState()
+ */
+
+/*!
+ \fn QString QApplication::sessionKey() const
+
+ Returns the session key in the current \link session.html
+ session\endlink.
+
+ If the application has been restored from an earlier session, this
+ key is the same as it was when the previous session ended.
+
+ The session key changes with every call of commitData() or
+ saveState().
+
+ \sa isSessionRestored(), sessionId(), commitData(), saveState()
+ */
+#ifndef QT_NO_SESSIONMANAGER
+bool QApplication::isSessionRestored() const
+{
+ Q_D(const QApplication);
+ return d->is_session_restored;
+}
+
+QString QApplication::sessionId() const
+{
+ Q_D(const QApplication);
+ return d->session_id;
+}
+
+QString QApplication::sessionKey() const
+{
+ Q_D(const QApplication);
+ return d->session_key;
+}
+#endif
+
+
+
+
+
+/*!
+ \since 4.2
+ \fn void QApplication::commitDataRequest(QSessionManager &manager)
+
+ This signal deals with \link session.html session
+ management\endlink. It is emitted when the QSessionManager wants the
+ application to commit all its data.
+
+ Usually this means saving all open files, after getting
+ permission from the user. Furthermore you may want to provide a means
+ by which the user can cancel the shutdown.
+
+ Note that you should not exit the application when called.
+ Instead, the session manager may or may not do this afterwards,
+ depending on the context.
+
+ \warning Within this signal, no user interaction is possible, \e
+ unless you ask the \a manager for explicit permission. See
+ QSessionManager::allowsInteraction() and
+ QSessionManager::allowsErrorInteraction() for details and example
+ usage.
+
+ Note: You should use Qt::DirectConnection when connecting to this signal.
+
+ \sa isSessionRestored(), sessionId(), saveState(), {Session Management}
+*/
+
+/*!
+ This function deals with \link session.html session
+ management\endlink. It is invoked when the QSessionManager wants the
+ application to commit all its data.
+
+ Usually this means saving all open files, after getting
+ permission from the user. Furthermore you may want to provide a means
+ by which the user can cancel the shutdown.
+
+ Note that you should not exit the application within this function.
+ Instead, the session manager may or may not do this afterwards,
+ depending on the context.
+
+ \warning Within this function, no user interaction is possible, \e
+ unless you ask the \a manager for explicit permission. See
+ QSessionManager::allowsInteraction() and
+ QSessionManager::allowsErrorInteraction() for details and example
+ usage.
+
+ The default implementation requests interaction and sends a close
+ event to all visible top-level widgets. If any event was
+ rejected, the shutdown is canceled.
+
+ \sa isSessionRestored(), sessionId(), saveState(), {Session Management}
+*/
+#ifndef QT_NO_SESSIONMANAGER
+void QApplication::commitData(QSessionManager& manager )
+{
+ emit commitDataRequest(manager);
+ if (manager.allowsInteraction()) {
+ QWidgetList done;
+ QWidgetList list = QApplication::topLevelWidgets();
+ bool cancelled = false;
+ for (int i = 0; !cancelled && i < list.size(); ++i) {
+ QWidget* w = list.at(i);
+ if (w->isVisible() && !done.contains(w)) {
+ cancelled = !w->close();
+ if (!cancelled)
+ done.append(w);
+ list = QApplication::topLevelWidgets();
+ i = -1;
+ }
+ }
+ if (cancelled)
+ manager.cancel();
+ }
+}
+
+/*!
+ \since 4.2
+ \fn void QApplication::saveStateRequest(QSessionManager &manager)
+
+ This signal deals with \link session.html session
+ management\endlink. It is invoked when the
+ \link QSessionManager session manager \endlink wants the application
+ to preserve its state for a future session.
+
+ For example, a text editor would create a temporary file that
+ includes the current contents of its edit buffers, the location of
+ the cursor and other aspects of the current editing session.
+
+ Note that you should never exit the application within this
+ signal. Instead, the session manager may or may not do this
+ afterwards, depending on the context. Futhermore, most session
+ managers will very likely request a saved state immediately after
+ the application has been started. This permits the session manager
+ to learn about the application's restart policy.
+
+ \warning Within this function, no user interaction is possible, \e
+ unless you ask the \a manager for explicit permission. See
+ QSessionManager::allowsInteraction() and
+ QSessionManager::allowsErrorInteraction() for details.
+
+ Note:: You should use Qt::DirectConnection when connecting to this signal.
+
+ \sa isSessionRestored(), sessionId(), commitData(), {Session Management}
+*/
+
+/*!
+ This function deals with \link session.html session
+ management\endlink. It is invoked when the
+ \link QSessionManager session manager \endlink wants the application
+ to preserve its state for a future session.
+
+ For example, a text editor would create a temporary file that
+ includes the current contents of its edit buffers, the location of
+ the cursor and other aspects of the current editing session.
+
+ Note that you should never exit the application within this
+ function. Instead, the session manager may or may not do this
+ afterwards, depending on the context. Futhermore, most session
+ managers will very likely request a saved state immediately after
+ the application has been started. This permits the session manager
+ to learn about the application's restart policy.
+
+ \warning Within this function, no user interaction is possible, \e
+ unless you ask the \a manager for explicit permission. See
+ QSessionManager::allowsInteraction() and
+ QSessionManager::allowsErrorInteraction() for details.
+
+ \sa isSessionRestored(), sessionId(), commitData(), {Session Management}
+*/
+
+void QApplication::saveState(QSessionManager &manager)
+{
+ emit saveStateRequest(manager);
+}
+#endif //QT_NO_SESSIONMANAGER
+/*
+ Sets the time after which a drag should start to \a ms ms.
+
+ \sa startDragTime()
+*/
+
+void QApplication::setStartDragTime(int ms)
+{
+ drag_time = ms;
+}
+
+/*!
+ \property QApplication::startDragTime
+ \brief the time in milliseconds that a mouse button must be held down
+ before a drag and drop operation will begin
+
+ If you support drag and drop in your application, and want to start a
+ drag and drop operation after the user has held down a mouse button for
+ a certain amount of time, you should use this property's value as the
+ delay.
+
+ Qt also uses this delay internally, e.g. in QTextEdit and QLineEdit,
+ for starting a drag.
+
+ The default value is 500 ms.
+
+ \sa startDragDistance(), {Drag and Drop}
+*/
+
+int QApplication::startDragTime()
+{
+ return drag_time;
+}
+
+/*
+ Sets the distance after which a drag should start to \a l pixels.
+
+ \sa startDragDistance()
+*/
+
+void QApplication::setStartDragDistance(int l)
+{
+ drag_distance = l;
+}
+
+/*!
+ \property QApplication::startDragDistance
+
+ If you support drag and drop in your application, and want to start a
+ drag and drop operation after the user has moved the cursor a certain
+ distance with a button held down, you should use this property's value
+ as the minimum distance required.
+
+ For example, if the mouse position of the click is stored in \c
+ startPos and the current position (e.g. in the mouse move event) is
+ \c currentPos, you can find out if a drag should be started with code
+ like this:
+
+ \snippet doc/src/snippets/code/src_gui_kernel_qapplication.cpp 7
+
+ Qt uses this value internally, e.g. in QFileDialog.
+
+ The default value is 4 pixels.
+
+ \sa startDragTime() QPoint::manhattanLength() {Drag and Drop}
+*/
+
+int QApplication::startDragDistance()
+{
+ return drag_distance;
+}
+
+/*!
+ \fn void QApplication::setReverseLayout(bool reverse)
+
+ Use setLayoutDirection() instead.
+*/
+
+/*!
+ \fn void QApplication::reverseLayout()
+
+ Use layoutDirection() instead.
+*/
+
+/*!
+ \fn bool QApplication::isRightToLeft()
+
+ Returns true if the application's layout direction is
+ Qt::RightToLeft; otherwise returns false.
+
+ \sa layoutDirection(), isLeftToRight()
+*/
+
+/*!
+ \fn bool QApplication::isLeftToRight()
+
+ Returns true if the application's layout direction is
+ Qt::LeftToRight; otherwise returns false.
+
+ \sa layoutDirection(), isRightToLeft()
+*/
+
+/*! \property QApplication::layoutDirection
+
+ \brief the default layout direction for this application
+
+ On system start-up, the default layout direction depends on the
+ application's language.
+
+ \sa QWidget::layoutDirection, isLeftToRight(), isRightToLeft()
+ */
+
+void QApplication::setLayoutDirection(Qt::LayoutDirection direction)
+{
+ if (layout_direction == direction)
+ return;
+
+ layout_direction = direction;
+
+ QWidgetList list = topLevelWidgets();
+ for (int i = 0; i < list.size(); ++i) {
+ QWidget *w = list.at(i);
+ QEvent ev(QEvent::ApplicationLayoutDirectionChange);
+ sendEvent(w, &ev);
+ }
+}
+
+Qt::LayoutDirection QApplication::layoutDirection()
+{
+ return layout_direction;
+}
+
+
+/*! \obsolete
+
+ Strips out vertical alignment flags and transforms an
+ alignment \a align of Qt::AlignLeft into Qt::AlignLeft or
+ Qt::AlignRight according to the language used.
+*/
+
+#ifdef QT3_SUPPORT
+Qt::Alignment QApplication::horizontalAlignment(Qt::Alignment align)
+{
+ return QStyle::visualAlignment(layoutDirection(), align);
+}
+#endif
+
+
+/*!
+ \fn QCursor *QApplication::overrideCursor()
+
+ Returns the active application override cursor.
+
+ This function returns 0 if no application cursor has been defined
+ (i.e. the internal cursor stack is empty).
+
+ \sa setOverrideCursor(), restoreOverrideCursor()
+*/
+#ifndef QT_NO_CURSOR
+QCursor *QApplication::overrideCursor()
+{
+ return qApp->d_func()->cursor_list.isEmpty() ? 0 : &qApp->d_func()->cursor_list.first();
+}
+
+/*!
+ Changes the currently active application override cursor to \a cursor.
+
+ This function has no effect if setOverrideCursor() wasn't called.
+
+ \sa setOverrideCursor() overrideCursor() restoreOverrideCursor() QWidget::setCursor()
+ */
+void QApplication::changeOverrideCursor(const QCursor &cursor)
+{
+ if (qApp->d_func()->cursor_list.isEmpty())
+ return;
+ qApp->d_func()->cursor_list.removeFirst();
+ setOverrideCursor(cursor);
+}
+#endif
+
+/*!
+ \fn void QApplication::setOverrideCursor(const QCursor &cursor, bool replace)
+
+ Use changeOverrideCursor(\a cursor) (if \a replace is true)
+ or setOverrideCursor(\a cursor) (if \a replace is false).
+*/
+
+/*!
+ Enters the main event loop and waits until exit() is called, then returns
+ the value that was set to exit() (which is 0 if exit() is called via
+ quit()).
+
+ It is necessary to call this function to start event handling. The
+ main event loop receives events from the window system and
+ dispatches these to the application widgets.
+
+ Generally speaking, no user interaction can take place before
+ calling exec(). As a special case, modal widgets like QMessageBox
+ can be used before calling exec(), because modal widgets call
+ exec() to start a local event loop.
+
+ To make your application perform idle processing, i.e. executing a
+ special function whenever there are no pending events, use a
+ QTimer with 0 timeout. More advanced idle processing schemes can
+ be achieved using processEvents().
+
+ We recommend that you connect clean-up code to the
+ \l{QCoreApplication::}{aboutToQuit()} signal, instead of putting it in
+ your application's \c{main()} function because on some platforms the
+ QApplication::exec() call may not return. For example, on Windows
+ when the user logs off, the system terminates the process after Qt
+ closes all top-level windows. Hence, there is no guarantee that the
+ application will have time to exit its event loop and execute code at
+ the end of the \c{main()} function after the QApplication::exec()
+ call.
+
+ \sa quitOnLastWindowClosed, quit(), exit(), processEvents(),
+ QCoreApplication::exec()
+*/
+int QApplication::exec()
+{
+#ifndef QT_NO_ACCESSIBILITY
+ QAccessible::setRootObject(qApp);
+#endif
+ return QCoreApplication::exec();
+}
+
+/*! \reimp
+ */
+bool QApplication::notify(QObject *receiver, QEvent *e)
+{
+ Q_D(QApplication);
+ // no events are delivered after ~QCoreApplication() has started
+ if (QApplicationPrivate::is_app_closing)
+ return true;
+
+ if (receiver == 0) { // serious error
+ qWarning("QApplication::notify: Unexpected null receiver");
+ return true;
+ }
+
+#ifndef QT_NO_DEBUG
+ d->checkReceiverThread(receiver);
+#endif
+
+#ifdef QT3_SUPPORT
+ if (e->type() == QEvent::ChildRemoved && !receiver->d_func()->pendingChildInsertedEvents.isEmpty())
+ receiver->d_func()->removePendingChildInsertedEvents(static_cast<QChildEvent *>(e)->child());
+#endif // QT3_SUPPORT
+
+ // capture the current mouse/keyboard state
+ if(e->spontaneous()) {
+ if (e->type() == QEvent::KeyPress
+ || e->type() == QEvent::KeyRelease) {
+ QKeyEvent *ke = static_cast<QKeyEvent*>(e);
+ QApplicationPrivate::modifier_buttons = ke->modifiers();
+ } else if(e->type() == QEvent::MouseButtonPress
+ || e->type() == QEvent::MouseButtonRelease) {
+ QMouseEvent *me = static_cast<QMouseEvent*>(e);
+ QApplicationPrivate::modifier_buttons = me->modifiers();
+ if(me->type() == QEvent::MouseButtonPress)
+ QApplicationPrivate::mouse_buttons |= me->button();
+ else
+ QApplicationPrivate::mouse_buttons &= ~me->button();
+ }
+#if !defined(QT_NO_WHEELEVENT) || !defined(QT_NO_TABLETEVENT)
+ else if (
+# ifndef QT_NO_WHEELEVENT
+ e->type() == QEvent::Wheel ||
+# endif
+# ifndef QT_NO_TABLETEVENT
+ e->type() == QEvent::TabletMove
+ || e->type() == QEvent::TabletPress
+ || e->type() == QEvent::TabletRelease
+# endif
+ ) {
+ QInputEvent *ie = static_cast<QInputEvent*>(e);
+ QApplicationPrivate::modifier_buttons = ie->modifiers();
+ }
+#endif // !QT_NO_WHEELEVENT || !QT_NO_TABLETEVENT
+ }
+
+ // User input and window activation makes tooltips sleep
+ switch (e->type()) {
+ case QEvent::Wheel:
+ case QEvent::ActivationChange:
+ case QEvent::KeyPress:
+ case QEvent::KeyRelease:
+ case QEvent::FocusOut:
+ case QEvent::FocusIn:
+ case QEvent::MouseButtonPress:
+ case QEvent::MouseButtonRelease:
+ case QEvent::MouseButtonDblClick:
+ d->toolTipFallAsleep.stop();
+ case QEvent::Leave:
+ d->toolTipWakeUp.stop();
+ default:
+ break;
+ }
+
+ bool res = false;
+ if (!receiver->isWidgetType()) {
+ res = d->notify_helper(receiver, e);
+ } else switch (e->type()) {
+#if defined QT3_SUPPORT && !defined(QT_NO_SHORTCUT)
+ case QEvent::Accel:
+ {
+ if (d->use_compat()) {
+ QKeyEvent* key = static_cast<QKeyEvent*>(e);
+ res = d->notify_helper(receiver, e);
+
+ if (!res && !key->isAccepted())
+ res = d->qt_dispatchAccelEvent(static_cast<QWidget *>(receiver), key);
+
+ // next lines are for compatibility with Qt <= 3.0.x: old
+ // QAccel was listening on toplevel widgets
+ if (!res && !key->isAccepted() && !static_cast<QWidget *>(receiver)->isWindow())
+ res = d->notify_helper(static_cast<QWidget *>(receiver)->window(), e);
+ }
+ break;
+ }
+#endif //QT3_SUPPORT && !QT_NO_SHORTCUT
+ case QEvent::ShortcutOverride:
+ case QEvent::KeyPress:
+ case QEvent::KeyRelease:
+ {
+ bool isWidget = receiver->isWidgetType();
+ bool isGraphicsWidget = false;
+#ifndef QT_NO_GRAPHICSVIEW
+ isGraphicsWidget = !isWidget && qobject_cast<QGraphicsWidget *>(receiver);
+#endif
+ if (!isWidget && !isGraphicsWidget) {
+ res = d->notify_helper(receiver, e);
+ break;
+ }
+
+ QKeyEvent* key = static_cast<QKeyEvent*>(e);
+#if defined QT3_SUPPORT && !defined(QT_NO_SHORTCUT)
+ if (d->use_compat() && d->qt_tryComposeUnicode(static_cast<QWidget*>(receiver), key))
+ break;
+#endif
+ if (key->type()==QEvent::KeyPress) {
+#ifndef QT_NO_SHORTCUT
+ // Try looking for a Shortcut before sending key events
+ if ((res = qApp->d_func()->shortcutMap.tryShortcutEvent(receiver, key)))
+ return res;
+#endif
+ qt_in_tab_key_event = (key->key() == Qt::Key_Backtab
+ || key->key() == Qt::Key_Tab
+ || key->key() == Qt::Key_Left
+ || key->key() == Qt::Key_Up
+ || key->key() == Qt::Key_Right
+ || key->key() == Qt::Key_Down);
+ }
+ bool def = key->isAccepted();
+ QPointer<QObject> pr = receiver;
+ while (receiver) {
+ if (def)
+ key->accept();
+ else
+ key->ignore();
+ res = d->notify_helper(receiver, e);
+ QWidget *w = isWidget ? static_cast<QWidget *>(receiver) : 0;
+#ifndef QT_NO_GRAPHICSVIEW
+ QGraphicsWidget *gw = isGraphicsWidget ? static_cast<QGraphicsWidget *>(receiver) : 0;
+#endif
+
+ if ((res && key->isAccepted())
+ /*
+ QLineEdit will emit a signal on Key_Return, but
+ ignore the event, and sometimes the connected
+ slot deletes the QLineEdit (common in itemview
+ delegates), so we have to check if the widget
+ was destroyed even if the event was ignored (to
+ prevent a crash)
+
+ note that we don't have to reset pw while
+ propagating (because the original receiver will
+ be destroyed if one of its ancestors is)
+ */
+ || !pr
+ || (isWidget && (w->isWindow() || !w->parentWidget()))
+#ifndef QT_NO_GRAPHICSVIEW
+ || (isGraphicsWidget && (gw->isWindow() || !gw->parentWidget()))
+#endif
+ ) {
+ break;
+ }
+
+#ifndef QT_NO_GRAPHICSVIEW
+ receiver = w ? (QObject *)w->parentWidget() : (QObject *)gw->parentWidget();
+#else
+ receiver = w->parentWidget();
+#endif
+ }
+ qt_in_tab_key_event = false;
+ }
+ break;
+ case QEvent::MouseButtonPress:
+ case QEvent::MouseButtonRelease:
+ case QEvent::MouseButtonDblClick:
+ case QEvent::MouseMove:
+ {
+ QWidget* w = static_cast<QWidget *>(receiver);
+
+ QMouseEvent* mouse = static_cast<QMouseEvent*>(e);
+ QPoint relpos = mouse->pos();
+
+ if (e->spontaneous()) {
+
+ if (e->type() == QEvent::MouseButtonPress) {
+ QWidget *fw = w;
+ while (fw) {
+ if (fw->isEnabled()
+ && QApplicationPrivate::shouldSetFocus(fw, Qt::ClickFocus)) {
+ fw->setFocus(Qt::MouseFocusReason);
+ break;
+ }
+ if (fw->isWindow())
+ break;
+ fw = fw->parentWidget();
+ }
+ }
+
+ if (e->type() == QEvent::MouseMove && mouse->buttons() == 0) {
+ d->toolTipWidget = w;
+ d->toolTipPos = relpos;
+ d->toolTipGlobalPos = mouse->globalPos();
+ d->toolTipWakeUp.start(d->toolTipFallAsleep.isActive()?20:700, this);
+ }
+ }
+
+ bool eventAccepted = mouse->isAccepted();
+
+ QPointer<QWidget> pw = w;
+ while (w) {
+ QMouseEvent me(mouse->type(), relpos, mouse->globalPos(), mouse->button(), mouse->buttons(),
+ mouse->modifiers());
+ me.spont = mouse->spontaneous();
+ // throw away any mouse-tracking-only mouse events
+ if (!w->hasMouseTracking()
+ && mouse->type() == QEvent::MouseMove && mouse->buttons() == 0) {
+ // but still send them through all application event filters (normally done by notify_helper)
+ for (int i = 0; i < d->eventFilters.size(); ++i) {
+ register QObject *obj = d->eventFilters.at(i);
+ if (!obj)
+ continue;
+ if (obj->d_func()->threadData != w->d_func()->threadData) {
+ qWarning("QApplication: Object event filter cannot be in a different thread.");
+ continue;
+ }
+ if (obj->eventFilter(w, w == receiver ? mouse : &me))
+ break;
+ }
+ res = true;
+ } else {
+ w->setAttribute(Qt::WA_NoMouseReplay, false);
+ res = d->notify_helper(w, w == receiver ? mouse : &me);
+ e->spont = false;
+ }
+ eventAccepted = (w == receiver ? mouse : &me)->isAccepted();
+ if (res && eventAccepted)
+ break;
+ if (w->isWindow() || w->testAttribute(Qt::WA_NoMousePropagation))
+ break;
+ relpos += w->pos();
+ w = w->parentWidget();
+ }
+
+ mouse->setAccepted(eventAccepted);
+
+ if (e->type() == QEvent::MouseMove) {
+ if (!pw)
+ break;
+
+ w = static_cast<QWidget *>(receiver);
+ relpos = mouse->pos();
+ QPoint diff = relpos - w->mapFromGlobal(d->hoverGlobalPos);
+ while (w) {
+ if (w->testAttribute(Qt::WA_Hover) &&
+ (!qApp->activePopupWidget() || qApp->activePopupWidget() == w->window())) {
+ QHoverEvent he(QEvent::HoverMove, relpos, relpos - diff);
+ d->notify_helper(w, &he);
+ }
+ if (w->isWindow() || w->testAttribute(Qt::WA_NoMousePropagation))
+ break;
+ relpos += w->pos();
+ w = w->parentWidget();
+ }
+ }
+
+ d->hoverGlobalPos = mouse->globalPos();
+ }
+ break;
+#ifndef QT_NO_WHEELEVENT
+ case QEvent::Wheel:
+ {
+ QWidget* w = static_cast<QWidget *>(receiver);
+ QWheelEvent* wheel = static_cast<QWheelEvent*>(e);
+ QPoint relpos = wheel->pos();
+ bool eventAccepted = wheel->isAccepted();
+
+ if (e->spontaneous()) {
+ QWidget *fw = w;
+ while (fw) {
+ if (fw->isEnabled()
+ && QApplicationPrivate::shouldSetFocus(fw, Qt::WheelFocus)) {
+ fw->setFocus(Qt::MouseFocusReason);
+ break;
+ }
+ if (fw->isWindow())
+ break;
+ fw = fw->parentWidget();
+ }
+ }
+
+ while (w) {
+ QWheelEvent we(relpos, wheel->globalPos(), wheel->delta(), wheel->buttons(),
+ wheel->modifiers(), wheel->orientation());
+ we.spont = wheel->spontaneous();
+ res = d->notify_helper(w, w == receiver ? wheel : &we);
+ eventAccepted = ((w == receiver) ? wheel : &we)->isAccepted();
+ e->spont = false;
+ if ((res && eventAccepted)
+ || w->isWindow() || w->testAttribute(Qt::WA_NoMousePropagation))
+ break;
+
+ relpos += w->pos();
+ w = w->parentWidget();
+ }
+ wheel->setAccepted(eventAccepted);
+ }
+ break;
+#endif
+#ifndef QT_NO_CONTEXTMENU
+ case QEvent::ContextMenu:
+ {
+ QWidget* w = static_cast<QWidget *>(receiver);
+ QContextMenuEvent *context = static_cast<QContextMenuEvent*>(e);
+ QPoint relpos = context->pos();
+ bool eventAccepted = context->isAccepted();
+ while (w) {
+ QContextMenuEvent ce(context->reason(), relpos, context->globalPos(), context->modifiers());
+ ce.spont = e->spontaneous();
+ res = d->notify_helper(w, w == receiver ? context : &ce);
+ eventAccepted = ((w == receiver) ? context : &ce)->isAccepted();
+ e->spont = false;
+
+ if ((res && eventAccepted)
+ || w->isWindow() || w->testAttribute(Qt::WA_NoMousePropagation))
+ break;
+
+ relpos += w->pos();
+ w = w->parentWidget();
+ }
+ context->setAccepted(eventAccepted);
+ }
+ break;
+#endif // QT_NO_CONTEXTMENU
+#ifndef QT_NO_TABLETEVENT
+ case QEvent::TabletMove:
+ case QEvent::TabletPress:
+ case QEvent::TabletRelease:
+ {
+ QWidget *w = static_cast<QWidget *>(receiver);
+ QTabletEvent *tablet = static_cast<QTabletEvent*>(e);
+ QPoint relpos = tablet->pos();
+ bool eventAccepted = tablet->isAccepted();
+ while (w) {
+ QTabletEvent te(tablet->type(), relpos, tablet->globalPos(),
+ tablet->hiResGlobalPos(), tablet->device(), tablet->pointerType(),
+ tablet->pressure(), tablet->xTilt(), tablet->yTilt(),
+ tablet->tangentialPressure(), tablet->rotation(), tablet->z(),
+ tablet->modifiers(), tablet->uniqueId());
+ te.spont = e->spontaneous();
+ res = d->notify_helper(w, w == receiver ? tablet : &te);
+ eventAccepted = ((w == receiver) ? tablet : &te)->isAccepted();
+ e->spont = false;
+ if ((res && eventAccepted)
+ || w->isWindow()
+ || w->testAttribute(Qt::WA_NoMousePropagation))
+ break;
+
+ relpos += w->pos();
+ w = w->parentWidget();
+ }
+ tablet->setAccepted(eventAccepted);
+ qt_tabletChokeMouse = tablet->isAccepted();
+ }
+ break;
+#endif // QT_NO_TABLETEVENT
+
+#if !defined(QT_NO_TOOLTIP) || !defined(QT_NO_WHATSTHIS)
+ case QEvent::ToolTip:
+ case QEvent::WhatsThis:
+ case QEvent::QueryWhatsThis:
+ {
+ QWidget* w = static_cast<QWidget *>(receiver);
+ QHelpEvent *help = static_cast<QHelpEvent*>(e);
+ QPoint relpos = help->pos();
+ bool eventAccepted = help->isAccepted();
+ while (w) {
+ QHelpEvent he(help->type(), relpos, help->globalPos());
+ he.spont = e->spontaneous();
+ res = d->notify_helper(w, w == receiver ? help : &he);
+ e->spont = false;
+ eventAccepted = (w == receiver ? help : &he)->isAccepted();
+ if ((res && eventAccepted) || w->isWindow())
+ break;
+
+ relpos += w->pos();
+ w = w->parentWidget();
+ }
+ help->setAccepted(eventAccepted);
+ }
+ break;
+#endif
+#if !defined(QT_NO_STATUSTIP) || !defined(QT_NO_WHATSTHIS)
+ case QEvent::StatusTip:
+ case QEvent::WhatsThisClicked:
+ {
+ QWidget *w = static_cast<QWidget *>(receiver);
+ while (w) {
+ res = d->notify_helper(w, e);
+ if ((res && e->isAccepted()) || w->isWindow())
+ break;
+ w = w->parentWidget();
+ }
+ }
+ break;
+#endif
+
+#ifndef QT_NO_DRAGANDDROP
+ case QEvent::DragEnter: {
+ QWidget* w = static_cast<QWidget *>(receiver);
+ QDragEnterEvent *dragEvent = static_cast<QDragEnterEvent *>(e);
+#ifdef Q_WS_MAC
+ // HIView has a slight difference in how it delivers events to children and parents
+ // It will not give a leave to a child's parent when it enters a child.
+ QWidget *currentTarget = QDragManager::self()->currentTarget();
+ if (currentTarget) {
+ // Assume currentTarget did not get a leave
+ QDragLeaveEvent event;
+ QApplication::sendEvent(currentTarget, &event);
+ }
+#endif
+#ifndef QT_NO_GRAPHICSVIEW
+ // QGraphicsProxyWidget handles its own propagation,
+ // and we must not change QDragManagers currentTarget.
+ QWExtra *extra = w->window()->d_func()->extra;
+ if (extra && extra->proxyWidget) {
+ res = d->notify_helper(w, dragEvent);
+ break;
+ }
+#endif
+ while (w) {
+ if (w->isEnabled() && w->acceptDrops()) {
+ res = d->notify_helper(w, dragEvent);
+ if (res && dragEvent->isAccepted()) {
+ QDragManager::self()->setCurrentTarget(w);
+ break;
+ }
+ }
+ if (w->isWindow())
+ break;
+ dragEvent->p = w->mapToParent(dragEvent->p);
+ w = w->parentWidget();
+ }
+ }
+ break;
+ case QEvent::DragMove:
+ case QEvent::Drop:
+ case QEvent::DragLeave: {
+ QWidget* w = static_cast<QWidget *>(receiver);
+#ifndef QT_NO_GRAPHICSVIEW
+ // QGraphicsProxyWidget handles its own propagation,
+ // and we must not change QDragManagers currentTarget.
+ QWExtra *extra = w->window()->d_func()->extra;
+ bool isProxyWidget = extra && extra->proxyWidget;
+ if (!isProxyWidget)
+#endif
+ w = QDragManager::self()->currentTarget();
+
+ if (!w) {
+#ifdef Q_WS_MAC
+ // HIView has a slight difference in how it delivers events to children and parents
+ // It will not give an enter to a child's parent when it leaves the child.
+ if (e->type() == QEvent::DragLeave)
+ break;
+ // Assume that w did not get an enter.
+ QDropEvent *dropEvent = static_cast<QDropEvent *>(e);
+ QDragEnterEvent dragEnterEvent(dropEvent->pos(), dropEvent->possibleActions(),
+ dropEvent->mimeData(), dropEvent->mouseButtons(),
+ dropEvent->keyboardModifiers());
+ QApplication::sendEvent(receiver, &dragEnterEvent);
+ w = QDragManager::self()->currentTarget();
+ if (!w)
+#endif
+ break;
+ }
+ if (e->type() == QEvent::DragMove || e->type() == QEvent::Drop) {
+ QDropEvent *dragEvent = static_cast<QDropEvent *>(e);
+ QWidget *origReciver = static_cast<QWidget *>(receiver);
+ while (origReciver && w != origReciver) {
+ dragEvent->p = origReciver->mapToParent(dragEvent->p);
+ origReciver = origReciver->parentWidget();
+ }
+ }
+ res = d->notify_helper(w, e);
+ if (e->type() != QEvent::DragMove
+#ifndef QT_NO_GRAPHICSVIEW
+ && !isProxyWidget
+#endif
+ )
+ QDragManager::self()->setCurrentTarget(0, e->type() == QEvent::Drop);
+ }
+ break;
+#endif
+
+ default:
+ res = d->notify_helper(receiver, e);
+ break;
+ }
+
+ return res;
+}
+
+bool QApplicationPrivate::notify_helper(QObject *receiver, QEvent * e)
+{
+ // send to all application event filters
+ if (sendThroughApplicationEventFilters(receiver, e))
+ return true;
+
+ if (receiver->isWidgetType()) {
+ QWidget *widget = static_cast<QWidget *>(receiver);
+
+#if !defined(Q_OS_WINCE) || (defined(GWES_ICONCURS) && !defined(QT_NO_CURSOR))
+ // toggle HasMouse widget state on enter and leave
+ if ((e->type() == QEvent::Enter || e->type() == QEvent::DragEnter) &&
+ (!qApp->activePopupWidget() || qApp->activePopupWidget() == widget->window()))
+ widget->setAttribute(Qt::WA_UnderMouse, true);
+ else if (e->type() == QEvent::Leave || e->type() == QEvent::DragLeave)
+ widget->setAttribute(Qt::WA_UnderMouse, false);
+#endif
+
+ if (QLayout *layout=widget->d_func()->layout) {
+ layout->widgetEvent(e);
+ }
+ }
+
+ // send to all receiver event filters
+ if (sendThroughObjectEventFilters(receiver, e))
+ return true;
+
+ // deliver the event
+ bool consumed = receiver->event(e);
+ e->spont = false;
+ return consumed;
+}
+
+
+/*!
+ \class QSessionManager
+ \brief The QSessionManager class provides access to the session manager.
+
+ \ingroup application
+ \ingroup environment
+
+ A session manager in a desktop environment (in which Qt GUI
+ applications live) keeps track of a session, which is a group of
+ running applications, each of which has a particular state. The
+ state of an application contains (most notably) the documents the
+ application has open and the position and size of its windows.
+
+ The session manager is used to save the session, e.g. when the
+ machine is shut down, and to restore a session, e.g. when the
+ machine is started up. We recommend that you use QSettings to save
+ an individual application's settings, e.g. window positions,
+ recently used files, etc. When the application is restarted by the
+ session manager, you can restore the settings.
+
+ QSessionManager provides an interface between the application
+ and the session manager so that the program can work well with the
+ session manager. In Qt, session management requests for action are
+ handled by the two virtual functions QApplication::commitData()
+ and QApplication::saveState(). Both provide a reference to a
+ session manager object as argument, to allow the application to
+ communicate with the session manager. The session manager can only
+ be accessed through these functions.
+
+ No user interaction is possible \e unless the application gets
+ explicit permission from the session manager. You ask for permission
+ by calling allowsInteraction() or, if it's really urgent,
+ allowsErrorInteraction(). Qt does not enforce this, but the session
+ manager may.
+
+ You can try to abort the shutdown process by calling cancel(). The
+ default commitData() function does this if some top-level window
+ rejected its closeEvent().
+
+ For sophisticated session managers provided on Unix/X11, QSessionManager
+ offers further possibilities to fine-tune an application's session
+ management behavior: setRestartCommand(), setDiscardCommand(),
+ setRestartHint(), setProperty(), requestPhase2(). See the respective
+ function descriptions for further details.
+
+ \sa QApplication, {Session Management}
+*/
+
+/*! \enum QSessionManager::RestartHint
+
+ This enum type defines the circumstances under which this
+ application wants to be restarted by the session manager. The
+ current values are
+
+ \value RestartIfRunning if the application is still running when
+ the session is shut down, it wants to be restarted at the start of
+ the next session.
+
+ \value RestartAnyway the application wants to be started at the
+ start of the next session, no matter what. (This is useful for
+ utilities that run just after startup and then quit.)
+
+ \value RestartImmediately the application wants to be started
+ immediately whenever it is not running.
+
+ \value RestartNever the application does not want to be restarted
+ automatically.
+
+ The default hint is \c RestartIfRunning.
+*/
+
+
+/*!
+ \fn QString QSessionManager::sessionId() const
+
+ Returns the identifier of the current session.
+
+ If the application has been restored from an earlier session, this
+ identifier is the same as it was in that earlier session.
+
+ \sa sessionKey(), QApplication::sessionId()
+ */
+
+/*!
+ \fn QString QSessionManager::sessionKey() const
+
+ Returns the session key in the current session.
+
+ If the application has been restored from an earlier session, this
+ key is the same as it was when the previous session ended.
+
+ The session key changes with every call of commitData() or
+ saveState().
+
+ \sa sessionId(), QApplication::sessionKey()
+ */
+
+/*!
+ \fn void* QSessionManager::handle() const
+
+ \internal
+*/
+
+/*!
+ \fn bool QSessionManager::allowsInteraction()
+
+ Asks the session manager for permission to interact with the
+ user. Returns true if interaction is permitted; otherwise
+ returns false.
+
+ The rationale behind this mechanism is to make it possible to
+ synchronize user interaction during a shutdown. Advanced session
+ managers may ask all applications simultaneously to commit their
+ data, resulting in a much faster shutdown.
+
+ When the interaction is completed we strongly recommend releasing the
+ user interaction semaphore with a call to release(). This way, other
+ applications may get the chance to interact with the user while your
+ application is still busy saving data. (The semaphore is implicitly
+ released when the application exits.)
+
+ If the user decides to cancel the shutdown process during the
+ interaction phase, you must tell the session manager that this has
+ happened by calling cancel().
+
+ Here's an example of how an application's QApplication::commitData()
+ might be implemented:
+
+ \snippet doc/src/snippets/code/src_gui_kernel_qapplication.cpp 8
+
+ If an error occurred within the application while saving its data,
+ you may want to try allowsErrorInteraction() instead.
+
+ \sa QApplication::commitData(), release(), cancel()
+*/
+
+
+/*!
+ \fn bool QSessionManager::allowsErrorInteraction()
+
+ Returns true if error interaction is permitted; otherwise returns false.
+
+ This is similar to allowsInteraction(), but also enables the application
+ to tell the user about any errors that occur. Session managers
+ may give error interaction requests higher priority, which means that it
+ is more likely that an error interaction is permitted. However, you are
+ still not guaranteed that the session manager will allow interaction.
+
+ \sa allowsInteraction(), release(), cancel()
+*/
+
+/*!
+ \fn void QSessionManager::release()
+
+ Releases the session manager's interaction semaphore after an
+ interaction phase.
+
+ \sa allowsInteraction(), allowsErrorInteraction()
+*/
+
+/*!
+ \fn void QSessionManager::cancel()
+
+ Tells the session manager to cancel the shutdown process. Applications
+ should not call this function without first asking the user.
+
+ \sa allowsInteraction(), allowsErrorInteraction()
+
+*/
+
+/*!
+ \fn void QSessionManager::setRestartHint(RestartHint hint)
+
+ Sets the application's restart hint to \a hint. On application
+ startup the hint is set to \c RestartIfRunning.
+
+ Note that these flags are only hints, a session manager may or may
+ not respect them.
+
+ We recommend setting the restart hint in QApplication::saveState()
+ because most session managers perform a checkpoint shortly after an
+ application's startup.
+
+ \sa restartHint()
+*/
+
+/*!
+ \fn QSessionManager::RestartHint QSessionManager::restartHint() const
+
+ Returns the application's current restart hint. The default is
+ \c RestartIfRunning.
+
+ \sa setRestartHint()
+*/
+
+/*!
+ \fn void QSessionManager::setRestartCommand(const QStringList& command)
+
+ If the session manager is capable of restoring sessions it will
+ execute \a command in order to restore the application. The command
+ defaults to
+
+ \snippet doc/src/snippets/code/src_gui_kernel_qapplication.cpp 9
+
+ The \c -session option is mandatory; otherwise QApplication cannot
+ tell whether it has been restored or what the current session
+ identifier is. See QApplication::isSessionRestored() and
+ QApplication::sessionId() for details.
+
+ If your application is very simple, it may be possible to store the
+ entire application state in additional command line options. This
+ is usually a very bad idea because command lines are often limited
+ to a few hundred bytes. Instead, use QSettings, or temporary files
+ or a database for this purpose. By marking the data with the unique
+ sessionId(), you will be able to restore the application in a future
+ session.
+
+ \sa restartCommand(), setDiscardCommand(), setRestartHint()
+*/
+
+/*!
+ \fn QStringList QSessionManager::restartCommand() const
+
+ Returns the currently set restart command.
+
+ To iterate over the list, you can use the \l foreach
+ pseudo-keyword:
+
+ \snippet doc/src/snippets/code/src_gui_kernel_qapplication.cpp 10
+
+ \sa setRestartCommand(), restartHint()
+*/
+
+/*!
+ \fn void QSessionManager::setDiscardCommand(const QStringList& list)
+
+ Sets the discard command to the given \a list.
+
+ \sa discardCommand(), setRestartCommand()
+*/
+
+
+/*!
+ \fn QStringList QSessionManager::discardCommand() const
+
+ Returns the currently set discard command.
+
+ To iterate over the list, you can use the \l foreach
+ pseudo-keyword:
+
+ \snippet doc/src/snippets/code/src_gui_kernel_qapplication.cpp 11
+
+ \sa setDiscardCommand(), restartCommand(), setRestartCommand()
+*/
+
+/*!
+ \fn void QSessionManager::setManagerProperty(const QString &name, const QString &value)
+ \overload
+
+ Low-level write access to the application's identification and state
+ records are kept in the session manager.
+
+ The property called \a name has its value set to the string \a value.
+*/
+
+/*!
+ \fn void QSessionManager::setManagerProperty(const QString& name,
+ const QStringList& value)
+
+ Low-level write access to the application's identification and state
+ record are kept in the session manager.
+
+ The property called \a name has its value set to the string list \a value.
+*/
+
+/*!
+ \fn bool QSessionManager::isPhase2() const
+
+ Returns true if the session manager is currently performing a second
+ session management phase; otherwise returns false.
+
+ \sa requestPhase2()
+*/
+
+/*!
+ \fn void QSessionManager::requestPhase2()
+
+ Requests a second session management phase for the application. The
+ application may then return immediately from the
+ QApplication::commitData() or QApplication::saveState() function,
+ and they will be called again once most or all other applications have
+ finished their session management.
+
+ The two phases are useful for applications such as the X11 window manager
+ that need to store information about another application's windows
+ and therefore have to wait until these applications have completed their
+ respective session management tasks.
+
+ Note that if another application has requested a second phase it
+ may get called before, simultaneously with, or after your
+ application's second phase.
+
+ \sa isPhase2()
+*/
+
+/*****************************************************************************
+ Stubbed session management support
+ *****************************************************************************/
+#ifndef QT_NO_SESSIONMANAGER
+#if defined(Q_WS_WIN) || defined(Q_WS_MAC) || defined(Q_WS_QWS)
+
+#if defined(Q_OS_WINCE)
+HRESULT qt_CoCreateGuid(GUID* guid)
+{
+ // We will use the following information to create the GUID
+ // 1. absolute path to application
+ wchar_t tempFilename[512];
+ if (!GetModuleFileNameW(0, tempFilename, 512))
+ return S_FALSE;
+ unsigned int hash = qHash(QString::fromUtf16((const unsigned short *) tempFilename));
+ guid->Data1 = hash;
+ // 2. creation time of file
+ QFileInfo info(QString::fromUtf16((const unsigned short *) tempFilename));
+ guid->Data2 = qHash(info.created().toTime_t());
+ // 3. current system time
+ guid->Data3 = qHash(QDateTime::currentDateTime().toTime_t());
+ return S_OK;
+}
+#if !defined(OLE32_MCOMGUID) || defined(QT_WINCE_FORCE_CREATE_GUID)
+#define CoCreateGuid qt_CoCreateGuid
+#endif
+
+#endif
+
+class QSessionManagerPrivate : public QObjectPrivate
+{
+public:
+ QStringList restartCommand;
+ QStringList discardCommand;
+ QString sessionId;
+ QString sessionKey;
+ QSessionManager::RestartHint restartHint;
+};
+
+QSessionManager* qt_session_manager_self = 0;
+QSessionManager::QSessionManager(QApplication * app, QString &id, QString &key)
+ : QObject(*new QSessionManagerPrivate, app)
+{
+ Q_D(QSessionManager);
+ setObjectName(QLatin1String("qt_sessionmanager"));
+ qt_session_manager_self = this;
+#if defined(Q_WS_WIN)
+ wchar_t guidstr[40];
+ GUID guid;
+ CoCreateGuid(&guid);
+ StringFromGUID2(guid, guidstr, 40);
+ id = QString::fromUtf16((ushort*)guidstr);
+ CoCreateGuid(&guid);
+ StringFromGUID2(guid, guidstr, 40);
+ key = QString::fromUtf16((ushort*)guidstr);
+#endif
+ d->sessionId = id;
+ d->sessionKey = key;
+ d->restartHint = RestartIfRunning;
+}
+
+QSessionManager::~QSessionManager()
+{
+ qt_session_manager_self = 0;
+}
+
+QString QSessionManager::sessionId() const
+{
+ Q_D(const QSessionManager);
+ return d->sessionId;
+}
+
+QString QSessionManager::sessionKey() const
+{
+ Q_D(const QSessionManager);
+ return d->sessionKey;
+}
+
+
+#if defined(Q_WS_X11) || defined(Q_WS_MAC)
+void* QSessionManager::handle() const
+{
+ return 0;
+}
+#endif
+
+#if !defined(Q_WS_WIN)
+bool QSessionManager::allowsInteraction()
+{
+ return true;
+}
+
+bool QSessionManager::allowsErrorInteraction()
+{
+ return true;
+}
+void QSessionManager::release()
+{
+}
+
+void QSessionManager::cancel()
+{
+}
+#endif
+
+
+void QSessionManager::setRestartHint(QSessionManager::RestartHint hint)
+{
+ Q_D(QSessionManager);
+ d->restartHint = hint;
+}
+
+QSessionManager::RestartHint QSessionManager::restartHint() const
+{
+ Q_D(const QSessionManager);
+ return d->restartHint;
+}
+
+void QSessionManager::setRestartCommand(const QStringList& command)
+{
+ Q_D(QSessionManager);
+ d->restartCommand = command;
+}
+
+QStringList QSessionManager::restartCommand() const
+{
+ Q_D(const QSessionManager);
+ return d->restartCommand;
+}
+
+void QSessionManager::setDiscardCommand(const QStringList& command)
+{
+ Q_D(QSessionManager);
+ d->discardCommand = command;
+}
+
+QStringList QSessionManager::discardCommand() const
+{
+ Q_D(const QSessionManager);
+ return d->discardCommand;
+}
+
+void QSessionManager::setManagerProperty(const QString&, const QString&)
+{
+}
+
+void QSessionManager::setManagerProperty(const QString&, const QStringList&)
+{
+}
+
+bool QSessionManager::isPhase2() const
+{
+ return false;
+}
+
+void QSessionManager::requestPhase2()
+{
+}
+
+#endif
+#endif // QT_NO_SESSIONMANAGER
+
+/*!
+ \typedef QApplication::ColorMode
+ \compat
+
+ Use ColorSpec instead.
+*/
+
+/*!
+ \fn Qt::MacintoshVersion QApplication::macVersion()
+
+ Use QSysInfo::MacintoshVersion instead.
+*/
+
+/*!
+ \fn QApplication::ColorMode QApplication::colorMode()
+
+ Use colorSpec() instead, and use ColorSpec as the enum type.
+*/
+
+/*!
+ \fn void QApplication::setColorMode(ColorMode mode)
+
+ Use setColorSpec() instead, and pass a ColorSpec value instead.
+*/
+
+/*!
+ \fn bool QApplication::hasGlobalMouseTracking()
+
+ This feature does not exist anymore. This function
+ always returns true in Qt 4.
+*/
+
+/*!
+ \fn void QApplication::setGlobalMouseTracking(bool dummy)
+
+ This function does nothing in Qt 4. The \a dummy parameter
+ is ignored.
+*/
+
+/*!
+ \fn void QApplication::flushX()
+
+ Use flush() instead.
+*/
+
+/*!
+ \fn void QApplication::setWinStyleHighlightColor(const QColor &c)
+
+ Use the palette instead.
+
+ \oldcode
+ app.setWinStyleHighlightColor(color);
+ \newcode
+ QPalette palette(qApp->palette());
+ palette.setColor(QPalette::Highlight, color);
+ qApp->setPalette(palette);
+ \endcode
+*/
+
+/*!
+ \fn void QApplication::setPalette(const QPalette &pal, bool b, const char* className = 0)
+
+ Use the two-argument overload instead.
+*/
+
+/*!
+ \fn void QApplication::setFont(const QFont &font, bool b, const char* className = 0)
+
+ Use the two-argument overload instead.
+*/
+
+/*!
+ \fn const QColor &QApplication::winStyleHighlightColor()
+
+ Use qApp->palette().color(QPalette::Active, QPalette::Highlight)
+ instead.
+*/
+
+/*!
+ \fn QWidget *QApplication::widgetAt(int x, int y, bool child)
+
+ Use the two-argument widgetAt() overload to get the child widget.
+ To get the top-level widget do this:
+
+ \snippet doc/src/snippets/code/src_gui_kernel_qapplication.cpp 12
+*/
+
+/*!
+ \fn QWidget *QApplication::widgetAt(const QPoint &point, bool child)
+
+ Use the single-argument widgetAt() overload to get the child widget.
+ To get the top-level widget do this:
+
+ \snippet doc/src/snippets/code/src_gui_kernel_qapplication.cpp 13
+*/
+
+#ifdef QT3_SUPPORT
+QWidget *QApplication::mainWidget()
+{
+ return QApplicationPrivate::main_widget;
+}
+#endif
+bool QApplicationPrivate::inPopupMode() const
+{
+ return QApplicationPrivate::popupWidgets != 0;
+}
+
+/*!
+ \property QApplication::quitOnLastWindowClosed
+
+ \brief whether the application implicitly quits when the last window is
+ closed.
+
+ The default is true.
+
+ If this property is true, the applications quits when the last visible
+ primary window (i.e. window with no parent) with the Qt::WA_QuitOnClose
+ attribute set is closed. By default this attribute is set for all widgets
+ except for sub-windows. Refer to \l{Qt::WindowType} for a detailed list of
+ Qt::Window objects.
+
+ \sa quit(), QWidget::close()
+ */
+
+void QApplication::setQuitOnLastWindowClosed(bool quit)
+{
+ QApplicationPrivate::quitOnLastWindowClosed = quit;
+}
+
+bool QApplication::quitOnLastWindowClosed()
+{
+ return QApplicationPrivate::quitOnLastWindowClosed;
+}
+
+void QApplicationPrivate::emitLastWindowClosed()
+{
+ if (qApp && qApp->d_func()->in_exec) {
+ if (QApplicationPrivate::quitOnLastWindowClosed) {
+ // get ready to quit, this event might be removed if the
+ // event loop is re-entered, however
+ QApplication::postEvent(qApp, new QEvent(QEvent::Quit));
+ }
+ emit qApp->lastWindowClosed();
+ }
+}
+
+/*! \variable QApplication::NormalColors
+ \compat
+
+ Use \l NormalColor instead.
+*/
+
+/*! \variable QApplication::CustomColors
+ \compat
+
+ Use \l CustomColor instead.
+*/
+
+#ifdef QT_KEYPAD_NAVIGATION
+/*!
+ Sets whether Qt should use focus navigation suitable for use with a
+ minimal keypad.
+
+ If \a enable is true, Qt::Key_Up and Qt::Key_Down are used to
+ change focus.
+
+ This feature is only available in Qt for Embedded Linux.
+
+ \sa keypadNavigationEnabled()
+*/
+void QApplication::setKeypadNavigationEnabled(bool enable)
+{
+ QApplicationPrivate::keypadNavigation = enable;
+}
+
+/*!
+ Returns true if Qt is set to use keypad navigation; otherwise returns
+ false. The default is false.
+
+ This feature is only available in Qt for Embedded Linux.
+ \sa setKeypadNavigationEnabled()
+*/
+bool QApplication::keypadNavigationEnabled()
+{
+ return QApplicationPrivate::keypadNavigation;
+}
+#endif
+
+/*!
+ \fn void QApplication::alert(QWidget *widget, int msec)
+ \since 4.3
+
+ Causes an alert to be shown for \a widget if the window is not the active
+ window. The alert is shown for \a msec miliseconds. If \a msec is zero (the
+ default), then the alert is shown indefinitely until the window becomes
+ active again.
+
+ Currently this function does nothing on Qt for Embedded Linux.
+
+ On Mac OS X, this works more at the application level and will cause the
+ application icon to bounce in the dock.
+
+ On Windows this causes the window's taskbar entry to flash for a time. If \a
+ msec is zero, the flashing will stop and the taskbar entry will turn a
+ different color (currently orange).
+
+ On X11, this will cause the window to be marked as "demands attention",
+ the window must not be hidden (i.e. not have hide() called on it, but be
+ visible in some sort of way) in order for this to work.
+*/
+
+/*!
+ \property QApplication::cursorFlashTime
+ \brief the text cursor's flash (blink) time in milliseconds
+
+ The flash time is the time required to display, invert and
+ restore the caret display. Usually the text cursor is displayed
+ for half the cursor flash time, then hidden for the same amount
+ of time, but this may vary.
+
+ The default value on X11 is 1000 milliseconds. On Windows, the
+ control panel value is used. Widgets should not cache this value
+ since it may be changed at any time by the user changing the
+ global desktop settings.
+
+ Note that on Microsoft Windows, setting this property sets the
+ cursor flash time for all applications.
+*/
+
+/*!
+ \property QApplication::doubleClickInterval
+ \brief the time limit in milliseconds that distinguishes a double click from two
+ consecutive mouse clicks
+
+ The default value on X11 is 400 milliseconds. On Windows and Mac
+ OS X, the operating system's value is used.
+
+ On Microsoft Windows, calling this function sets the
+ double click interval for all applications.
+*/
+
+/*!
+ \property QApplication::keyboardInputInterval
+ \brief the time limit in milliseconds that distinguishes a key press
+ from two consecutive key presses
+ \since 4.2
+
+ The default value on X11 is 400 milliseconds. On Windows and Mac OS X, the
+ operating system's value is used.
+*/
+
+/*!
+ \property QApplication::wheelScrollLines
+ \brief the number of lines to scroll a widget, when the
+ mouse wheel is rotated.
+
+ If the value exceeds the widget's number of visible lines, the
+ widget should interpret the scroll operation as a single \e{page
+ up} or \e{page down}. If the widget is an \l{QAbstractItemView}
+ {item view class}, then the result of scrolling one \e line
+ depends on the setting of the widget's
+ \l{QAbstractItemView::verticalScrollMode()} {scroll mode}. Scroll
+ one \e line can mean \l{QAbstractItemView::ScrollPerItem} {scroll
+ one item} or \l{QAbstractItemView::ScrollPerPixel} {scroll one
+ pixel}.
+
+ By default, this property has a value of 3.
+*/
+
+/*!
+ \fn void QApplication::setEffectEnabled(Qt::UIEffect effect, bool enable)
+
+ Enables the UI effect \a effect if \a enable is true, otherwise
+ the effect will not be used.
+
+ Note: All effects are disabled on screens running at less than
+ 16-bit color depth.
+
+ \sa isEffectEnabled(), Qt::UIEffect, setDesktopSettingsAware()
+*/
+
+/*!
+ \fn bool QApplication::isEffectEnabled(Qt::UIEffect effect)
+
+ Returns true if \a effect is enabled; otherwise returns false.
+
+ By default, Qt will try to use the desktop settings. Call
+ setDesktopSettingsAware(false) to prevent this.
+
+ Note: All effects are disabled on screens running at less than
+ 16-bit color depth.
+
+ \sa setEffectEnabled(), Qt::UIEffect
+*/
+
+/*!
+ \fn QWidget *QApplication::mainWidget()
+
+ Returns the main application widget, or 0 if there is no main
+ widget.
+*/
+
+/*!
+ \fn void QApplication::setMainWidget(QWidget *mainWidget)
+
+ Sets the application's main widget to \a mainWidget.
+
+ In most respects the main widget is like any other widget, except
+ that if it is closed, the application exits. Note that
+ QApplication does \e not take ownership of the \a mainWidget, so
+ if you create your main widget on the heap you must delete it
+ yourself.
+
+ You need not have a main widget; connecting lastWindowClosed() to
+ quit() is an alternative.
+
+ For X11, this function also resizes and moves the main widget
+ according to the \e -geometry command-line option, so you should
+ set the default geometry (using \l QWidget::setGeometry()) before
+ calling setMainWidget().
+
+ \sa mainWidget(), exec(), quit()
+*/
+
+/*!
+ \fn void QApplication::beep()
+
+ Sounds the bell, using the default volume and sound. The function
+ is \e not available in Qt for Embedded Linux.
+*/
+
+/*!
+ \fn void QApplication::setOverrideCursor(const QCursor &cursor)
+
+ Sets the application override cursor to \a cursor.
+
+ Application override cursors are intended for showing the user
+ that the application is in a special state, for example during an
+ operation that might take some time.
+
+ This cursor will be displayed in all the application's widgets
+ until restoreOverrideCursor() or another setOverrideCursor() is
+ called.
+
+ Application cursors are stored on an internal stack.
+ setOverrideCursor() pushes the cursor onto the stack, and
+ restoreOverrideCursor() pops the active cursor off the
+ stack. changeOverrideCursor() changes the curently active
+ application override cursor. Every setOverrideCursor() must
+ eventually be followed by a corresponding restoreOverrideCursor(),
+ otherwise the stack will never be emptied.
+
+ Example:
+ \snippet doc/src/snippets/code/src_gui_kernel_qapplication_x11.cpp 0
+
+ \sa overrideCursor() restoreOverrideCursor() changeOverrideCursor() QWidget::setCursor()
+*/
+
+/*!
+ \fn void QApplication::restoreOverrideCursor()
+
+ Undoes the last setOverrideCursor().
+
+ If setOverrideCursor() has been called twice, calling
+ restoreOverrideCursor() will activate the first cursor set.
+ Calling this function a second time restores the original widgets'
+ cursors.
+
+ \sa setOverrideCursor(), overrideCursor()
+*/
+
+/*!
+ \macro qApp
+ \relates QApplication
+
+ A global pointer referring to the unique application object. It is
+ equivalent to the pointer returned by the
+ QCoreApplication::instance() function except that, in GUI applications,
+ it is a pointer to a QApplication instance.
+
+ Only one application object can be created.
+
+ \sa QCoreApplication::instance()
+*/
+
+// ************************************************************************
+// Input Method support
+// ************************************************************************
+
+/*!
+ This function replaces the QInputContext instance used by the
+ application with \a inputContext.
+
+ \sa inputContext()
+*/
+void QApplication::setInputContext(QInputContext *inputContext)
+{
+ Q_D(QApplication);
+ Q_UNUSED(d);// only static members being used.
+ if (!inputContext) {
+ qWarning("QApplication::setInputContext: called with 0 input context");
+ return;
+ }
+ if (d->inputContext)
+ delete d->inputContext;
+ d->inputContext = inputContext;
+}
+
+/*!
+ Returns the QInputContext instance used by the application.
+
+ \sa setInputContext()
+*/
+QInputContext *QApplication::inputContext() const
+{
+ Q_D(const QApplication);
+ Q_UNUSED(d);// only static members being used.
+#ifdef Q_WS_X11
+ if (!X11)
+ return 0;
+ if (!d->inputContext) {
+ QApplication *that = const_cast<QApplication *>(this);
+ QInputContext *qic = QInputContextFactory::create(X11->default_im, that);
+ // fallback to default X Input Method.
+ if (!qic)
+ qic = QInputContextFactory::create(QLatin1String("xim"), that);
+ that->d_func()->inputContext = qic;
+ }
+#endif
+ return d->inputContext;
+}
+
+//Returns the current platform used by keyBindings
+uint QApplicationPrivate::currentPlatform(){
+ uint platform = KB_Win;
+#ifdef Q_WS_MAC
+ platform = KB_Mac;
+#elif defined Q_WS_X11
+ platform = KB_X11;
+ if (X11->desktopEnvironment == DE_KDE)
+ platform |= KB_KDE;
+ if (X11->desktopEnvironment == DE_GNOME)
+ platform |= KB_Gnome;
+ if (X11->desktopEnvironment == DE_CDE)
+ platform |= KB_CDE;
+#endif
+ return platform;
+}
+
+bool qt_sendSpontaneousEvent(QObject *receiver, QEvent *event)
+{
+ return QCoreApplication::sendSpontaneousEvent(receiver, event);
+}
+
+
+/*!
+ \since 4.2
+
+ Returns the current keyboard input locale.
+*/
+QLocale QApplication::keyboardInputLocale()
+{
+ if (!QApplicationPrivate::checkInstance("keyboardInputLocale"))
+ return QLocale::c();
+ return qt_keymapper_private()->keyboardInputLocale;
+}
+
+/*!
+ \since 4.2
+
+ Returns the current keyboard input direction.
+*/
+Qt::LayoutDirection QApplication::keyboardInputDirection()
+{
+ if (!QApplicationPrivate::checkInstance("keyboardInputDirection"))
+ return Qt::LeftToRight;
+ return qt_keymapper_private()->keyboardInputDirection;
+}
+
+bool QApplicationPrivate::shouldSetFocus(QWidget *w, Qt::FocusPolicy policy)
+{
+ QWidget *f = w;
+ while (f->d_func()->extra && f->d_func()->extra->focus_proxy)
+ f = f->d_func()->extra->focus_proxy;
+
+ if ((w->focusPolicy() & policy) != policy)
+ return false;
+ if (w != f && (f->focusPolicy() & policy) != policy)
+ return false;
+ return true;
+}
+
+QT_END_NAMESPACE
+
+#include "moc_qapplication.cpp"
diff --git a/src/gui/kernel/qapplication.h b/src/gui/kernel/qapplication.h
new file mode 100644
index 0000000000..1d6941d5fc
--- /dev/null
+++ b/src/gui/kernel/qapplication.h
@@ -0,0 +1,391 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QAPPLICATION_H
+#define QAPPLICATION_H
+
+#include <QtCore/qcoreapplication.h>
+#include <QtGui/qwindowdefs.h>
+#include <QtCore/qpoint.h>
+#include <QtCore/qsize.h>
+#include <QtGui/qcursor.h>
+#ifdef QT_INCLUDE_COMPAT
+# include <QtGui/qdesktopwidget.h>
+#endif
+#ifdef QT3_SUPPORT
+# include <QtGui/qwidget.h>
+# include <QtGui/qpalette.h>
+#endif
+#ifdef Q_WS_QWS
+# include <QtGui/qrgb.h>
+# include <QtGui/qtransportauth_qws.h>
+#endif
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+class QSessionManager;
+class QDesktopWidget;
+class QStyle;
+class QEventLoop;
+class QIcon;
+class QInputContext;
+template <typename T> class QList;
+class QLocale;
+#if defined(Q_WS_QWS)
+class QDecoration;
+#endif
+
+class QApplication;
+class QApplicationPrivate;
+#if defined(qApp)
+#undef qApp
+#endif
+#define qApp (static_cast<QApplication *>(QCoreApplication::instance()))
+
+class Q_GUI_EXPORT QApplication : public QCoreApplication
+{
+ Q_OBJECT
+ Q_PROPERTY(Qt::LayoutDirection layoutDirection READ layoutDirection WRITE setLayoutDirection)
+ Q_PROPERTY(QIcon windowIcon READ windowIcon WRITE setWindowIcon)
+ Q_PROPERTY(int cursorFlashTime READ cursorFlashTime WRITE setCursorFlashTime)
+ Q_PROPERTY(int doubleClickInterval READ doubleClickInterval WRITE setDoubleClickInterval)
+ Q_PROPERTY(int keyboardInputInterval READ keyboardInputInterval WRITE setKeyboardInputInterval)
+#ifndef QT_NO_WHEELEVENT
+ Q_PROPERTY(int wheelScrollLines READ wheelScrollLines WRITE setWheelScrollLines)
+#endif
+ Q_PROPERTY(QSize globalStrut READ globalStrut WRITE setGlobalStrut)
+ Q_PROPERTY(int startDragTime READ startDragTime WRITE setStartDragTime)
+ Q_PROPERTY(int startDragDistance READ startDragDistance WRITE setStartDragDistance)
+ Q_PROPERTY(bool quitOnLastWindowClosed READ quitOnLastWindowClosed WRITE setQuitOnLastWindowClosed)
+#ifndef QT_NO_STYLE_STYLESHEET
+ Q_PROPERTY(QString styleSheet READ styleSheet WRITE setStyleSheet)
+#endif
+#ifdef Q_OS_WINCE
+ Q_PROPERTY(int autoMaximizeThreshold READ autoMaximizeThreshold WRITE setAutoMaximizeThreshold)
+ Q_PROPERTY(bool autoSipEnabled READ autoSipEnabled WRITE setAutoSipEnabled)
+#endif
+
+public:
+ enum Type { Tty, GuiClient, GuiServer };
+#ifndef qdoc
+ QApplication(int &argc, char **argv, int = QT_VERSION);
+ QApplication(int &argc, char **argv, bool GUIenabled, int = QT_VERSION);
+ QApplication(int &argc, char **argv, Type, int = QT_VERSION);
+#if defined(Q_WS_X11)
+ QApplication(Display* dpy, Qt::HANDLE visual = 0, Qt::HANDLE cmap = 0, int = QT_VERSION);
+ QApplication(Display *dpy, int &argc, char **argv, Qt::HANDLE visual = 0, Qt::HANDLE cmap= 0, int = QT_VERSION);
+#endif
+#endif
+ virtual ~QApplication();
+
+ static Type type();
+
+ static QStyle *style();
+ static void setStyle(QStyle*);
+ static QStyle *setStyle(const QString&);
+ enum ColorSpec { NormalColor=0, CustomColor=1, ManyColor=2 };
+ static int colorSpec();
+ static void setColorSpec(int);
+ static void setGraphicsSystem(const QString &);
+
+#ifndef QT_NO_CURSOR
+ static QCursor *overrideCursor();
+ static void setOverrideCursor(const QCursor &);
+ static void changeOverrideCursor(const QCursor &);
+ static void restoreOverrideCursor();
+#endif
+ static QPalette palette();
+ static QPalette palette(const QWidget *);
+ static QPalette palette(const char *className);
+ static void setPalette(const QPalette &, const char* className = 0);
+ static QFont font();
+ static QFont font(const QWidget*);
+ static QFont font(const char *className);
+ static void setFont(const QFont &, const char* className = 0);
+ static QFontMetrics fontMetrics();
+
+ static void setWindowIcon(const QIcon &icon);
+ static QIcon windowIcon();
+
+
+#ifdef QT3_SUPPORT
+ static QT3_SUPPORT QWidget *mainWidget();
+ static QT3_SUPPORT void setMainWidget(QWidget *);
+#endif
+
+ static QWidgetList allWidgets();
+ static QWidgetList topLevelWidgets();
+
+ static QDesktopWidget *desktop();
+
+ static QWidget *activePopupWidget();
+ static QWidget *activeModalWidget();
+#ifndef QT_NO_CLIPBOARD
+ static QClipboard *clipboard();
+#endif
+ static QWidget *focusWidget();
+
+ static QWidget *activeWindow();
+ static void setActiveWindow(QWidget* act);
+
+ static QWidget *widgetAt(const QPoint &p);
+ static inline QWidget *widgetAt(int x, int y) { return widgetAt(QPoint(x, y)); }
+ static QWidget *topLevelAt(const QPoint &p);
+ static inline QWidget *topLevelAt(int x, int y) { return topLevelAt(QPoint(x, y)); }
+
+ static void syncX();
+ static void beep();
+ static void alert(QWidget *widget, int duration = 0);
+
+ static Qt::KeyboardModifiers keyboardModifiers();
+ static Qt::MouseButtons mouseButtons();
+
+ static void setDesktopSettingsAware(bool);
+ static bool desktopSettingsAware();
+
+ static void setCursorFlashTime(int);
+ static int cursorFlashTime();
+
+ static void setDoubleClickInterval(int);
+ static int doubleClickInterval();
+
+ static void setKeyboardInputInterval(int);
+ static int keyboardInputInterval();
+
+#ifndef QT_NO_WHEELEVENT
+ static void setWheelScrollLines(int);
+ static int wheelScrollLines();
+#endif
+ static void setGlobalStrut(const QSize &);
+ static QSize globalStrut();
+
+ static void setStartDragTime(int ms);
+ static int startDragTime();
+ static void setStartDragDistance(int l);
+ static int startDragDistance();
+
+ static void setLayoutDirection(Qt::LayoutDirection direction);
+ static Qt::LayoutDirection layoutDirection();
+
+ static inline bool isRightToLeft() { return layoutDirection() == Qt::RightToLeft; }
+ static inline bool isLeftToRight() { return layoutDirection() == Qt::LeftToRight; }
+
+ static bool isEffectEnabled(Qt::UIEffect);
+ static void setEffectEnabled(Qt::UIEffect, bool enable = true);
+
+#if defined(Q_WS_MAC)
+ virtual bool macEventFilter(EventHandlerCallRef, EventRef);
+#endif
+#if defined(Q_WS_X11)
+ virtual bool x11EventFilter(XEvent *);
+ virtual int x11ClientMessage(QWidget*, XEvent*, bool passive_only);
+ int x11ProcessEvent(XEvent*);
+#endif
+#if defined(Q_WS_QWS)
+ virtual bool qwsEventFilter(QWSEvent *);
+ int qwsProcessEvent(QWSEvent*);
+ void qwsSetCustomColors(QRgb *colortable, int start, int numColors);
+#ifndef QT_NO_QWS_MANAGER
+ static QDecoration &qwsDecoration();
+ static void qwsSetDecoration(QDecoration *);
+ static QDecoration *qwsSetDecoration(const QString &decoration);
+#endif
+#endif
+
+
+#if defined(Q_WS_WIN)
+ void winFocus(QWidget *, bool);
+ static void winMouseButtonUp();
+#endif
+
+#ifndef QT_NO_SESSIONMANAGER
+ // session management
+ bool isSessionRestored() const;
+ QString sessionId() const;
+ QString sessionKey() const;
+ virtual void commitData(QSessionManager& sm);
+ virtual void saveState(QSessionManager& sm);
+#endif
+ void setInputContext(QInputContext *);
+ QInputContext *inputContext() const;
+
+ static QLocale keyboardInputLocale();
+ static Qt::LayoutDirection keyboardInputDirection();
+
+ static int exec();
+ bool notify(QObject *, QEvent *);
+
+
+ static void setQuitOnLastWindowClosed(bool quit);
+ static bool quitOnLastWindowClosed();
+
+#ifdef QT_KEYPAD_NAVIGATION
+ static void setKeypadNavigationEnabled(bool);
+ static bool keypadNavigationEnabled();
+#endif
+
+Q_SIGNALS:
+ void lastWindowClosed();
+ void focusChanged(QWidget *old, QWidget *now);
+ void fontDatabaseChanged();
+#ifndef QT_NO_SESSIONMANAGER
+ void commitDataRequest(QSessionManager &sessionManager);
+ void saveStateRequest(QSessionManager &sessionManager);
+#endif
+
+public:
+ QString styleSheet() const;
+public Q_SLOTS:
+#ifndef QT_NO_STYLE_STYLESHEET
+ void setStyleSheet(const QString& sheet);
+#endif
+#ifdef Q_OS_WINCE
+ void setAutoMaximizeThreshold(const int threshold);
+ int autoMaximizeThreshold() const;
+ void setAutoSipEnabled(const bool enabled);
+ bool autoSipEnabled() const;
+#endif
+ static void closeAllWindows();
+ static void aboutQt();
+
+protected:
+#if defined(Q_WS_QWS)
+ void setArgs(int, char **);
+#endif
+ bool event(QEvent *);
+ bool compressEvent(QEvent *, QObject *receiver, QPostEventList *);
+
+#ifdef QT3_SUPPORT
+public:
+ static inline QT3_SUPPORT void setReverseLayout(bool b) { setLayoutDirection(b?Qt::RightToLeft:Qt::LeftToRight); }
+ static inline bool QT3_SUPPORT reverseLayout() { return layoutDirection() == Qt::RightToLeft; }
+ static QT3_SUPPORT Qt::Alignment horizontalAlignment(Qt::Alignment align);
+ typedef int ColorMode;
+ enum { NormalColors = NormalColor, CustomColors = CustomColor };
+ static inline QT3_SUPPORT ColorMode colorMode() { return static_cast<ColorMode>(colorSpec()); }
+ static inline QT3_SUPPORT void setColorMode(ColorMode mode) { setColorSpec(int(mode)); }
+#if defined(Q_OS_WIN32) || defined(Q_OS_CYGWIN)
+ static QT3_SUPPORT Qt::WindowsVersion winVersion() { return (Qt::WindowsVersion)QSysInfo::WindowsVersion; }
+#endif
+#if defined(Q_OS_MAC)
+ static QT3_SUPPORT Qt::MacintoshVersion macVersion() { return (Qt::MacintoshVersion)QSysInfo::MacintoshVersion; }
+#endif
+# ifndef QT_NO_CURSOR
+ inline static QT3_SUPPORT void setOverrideCursor(const QCursor &cursor, bool replace)
+ { if (replace) changeOverrideCursor(cursor); else setOverrideCursor(cursor); }
+# endif
+ inline static QT3_SUPPORT bool hasGlobalMouseTracking() {return true;}
+ inline static QT3_SUPPORT void setGlobalMouseTracking(bool) {};
+ inline static QT3_SUPPORT void flushX() { flush(); }
+ static inline QT3_SUPPORT void setWinStyleHighlightColor(const QColor &c) {
+ QPalette p(palette());
+ p.setColor(QPalette::Highlight, c);
+ setPalette(p);
+ }
+ static inline QT3_SUPPORT const QColor &winStyleHighlightColor()
+ { return palette().color(QPalette::Active, QPalette::Highlight); }
+ static inline QT3_SUPPORT void setPalette(const QPalette &pal, bool, const char* className = 0)
+ { setPalette(pal, className); };
+ static inline QT3_SUPPORT void setFont(const QFont &font, bool, const char* className = 0)
+ { setFont(font, className); }
+
+ static inline QT3_SUPPORT QWidget *widgetAt(int x, int y, bool child)
+ { QWidget *w = widgetAt(x, y); return child ? w : (w ? w->window() : 0); }
+ static inline QT3_SUPPORT QWidget *widgetAt(const QPoint &p, bool child)
+ { QWidget *w = widgetAt(p); return child ? w : (w ? w->window() : 0); }
+#endif // QT3_SUPPORT
+
+#if defined(Q_INTERNAL_QAPP_SRC) || defined(qdoc)
+ QApplication(int &argc, char **argv);
+ QApplication(int &argc, char **argv, bool GUIenabled);
+ QApplication(int &argc, char **argv, Type);
+#if defined(Q_WS_X11)
+ QApplication(Display* dpy, Qt::HANDLE visual = 0, Qt::HANDLE cmap = 0);
+ QApplication(Display *dpy, int &argc, char **argv, Qt::HANDLE visual = 0, Qt::HANDLE cmap= 0);
+#endif
+#endif
+
+private:
+ Q_DISABLE_COPY(QApplication)
+ Q_DECLARE_PRIVATE(QApplication)
+
+ friend class QGraphicsWidget;
+ friend class QGraphicsScene;
+ friend class QGraphicsScenePrivate;
+ friend class QWidget;
+ friend class QWidgetPrivate;
+ friend class QETWidget;
+ friend class Q3AccelManager;
+ friend class QTranslator;
+ friend class QWidgetAnimator;
+#ifndef QT_NO_SHORTCUT
+ friend class QShortcut;
+ friend class QLineEdit;
+ friend class QTextControl;
+#endif
+ friend class QAction;
+ friend class QFontDatabasePrivate;
+
+#if defined(Q_WS_QWS)
+ friend class QInputContext;
+ friend class QWSDirectPainterSurface;
+ friend class QDirectPainter;
+ friend class QDirectPainterPrivate;
+#endif
+
+#if defined(Q_WS_WIN)
+ friend QApplicationPrivate* getQApplicationPrivateInternal();
+#endif
+
+#if defined(Q_WS_MAC) || defined(Q_WS_X11)
+ Q_PRIVATE_SLOT(d_func(), void _q_alertTimeOut())
+#endif
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QAPPLICATION_H
diff --git a/src/gui/kernel/qapplication_mac.mm b/src/gui/kernel/qapplication_mac.mm
new file mode 100644
index 0000000000..5f8c572d94
--- /dev/null
+++ b/src/gui/kernel/qapplication_mac.mm
@@ -0,0 +1,2976 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/****************************************************************************
+**
+** Copyright (c) 2007-2008, Apple, Inc.
+**
+** All rights reserved.
+**
+** Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are met:
+**
+** * Redistributions of source code must retain the above copyright notice,
+** this list of conditions and the following disclaimer.
+**
+** * Redistributions in binary form must reproduce the above copyright notice,
+** this list of conditions and the following disclaimer in the documentation
+** and/or other materials provided with the distribution.
+**
+** * Neither the name of Apple, Inc. nor the names of its contributors
+** may be used to endorse or promote products derived from this software
+** without specific prior written permission.
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+** CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+** EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+** PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+** PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+** LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+** NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+** SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+**
+****************************************************************************/
+
+#include <Cocoa/Cocoa.h>
+
+#include "qapplication.h"
+#include "qbitarray.h"
+#include "qclipboard.h"
+#include "qcursor.h"
+#include "qdatastream.h"
+#include "qdatetime.h"
+#include "qdesktopwidget.h"
+#include "qdockwidget.h"
+#include "qevent.h"
+#include "qhash.h"
+#include "qlayout.h"
+#include "qmenubar.h"
+#include "qmessagebox.h"
+#include "qmime.h"
+#include "qpixmapcache.h"
+#include "qpointer.h"
+#include "qsessionmanager.h"
+#include "qsettings.h"
+#include "qsocketnotifier.h"
+#include "qstyle.h"
+#include "qstylefactory.h"
+#include "qtextcodec.h"
+#include "qtoolbar.h"
+#include "qvariant.h"
+#include "qwidget.h"
+#include "qcolormap.h"
+#include "qdir.h"
+#include "qdebug.h"
+#include "qtimer.h"
+#include "private/qmacinputcontext_p.h"
+#include "private/qpaintengine_mac_p.h"
+#include "private/qcursor_p.h"
+#include "private/qapplication_p.h"
+#include "private/qcolor_p.h"
+#include "private/qwidget_p.h"
+#include "private/qkeymapper_p.h"
+#include "private/qeventdispatcher_mac_p.h"
+#include "private/qeventdispatcher_unix_p.h"
+#include <private/qcocoamenuloader_mac_p.h>
+#include <private/qcocoaapplication_mac_p.h>
+#include <private/qcocoaapplicationdelegate_mac_p.h>
+#include <private/qt_cocoa_helpers_mac_p.h>
+#include <private/qcocoawindow_mac_p.h>
+#include <private/qpixmap_mac_p.h>
+#include <private/qdesktopwidget_mac_p.h>
+#include <private/qeventdispatcher_mac_p.h>
+#include <qvarlengtharray.h>
+
+#ifndef QT_NO_ACCESSIBILITY
+# include "qaccessible.h"
+#endif
+
+#ifndef QT_NO_THREAD
+# include "qmutex.h"
+#endif
+
+#include <unistd.h>
+#include <string.h>
+#include <sys/time.h>
+#include <sys/select.h>
+
+/*****************************************************************************
+ QApplication debug facilities
+ *****************************************************************************/
+//#define DEBUG_EVENTS //like EventDebug but more specific to Qt
+//#define DEBUG_DROPPED_EVENTS
+//#define DEBUG_MOUSE_MAPS
+//#define DEBUG_MODAL_EVENTS
+//#define DEBUG_PLATFORM_SETTINGS
+
+#define QMAC_SPEAK_TO_ME
+#ifdef QMAC_SPEAK_TO_ME
+#include "qregexp.h"
+#endif
+
+#ifndef kThemeBrushAlternatePrimaryHighlightColor
+#define kThemeBrushAlternatePrimaryHighlightColor -5
+#endif
+
+
+QT_BEGIN_NAMESPACE
+
+//for qt_mac.h
+QPaintDevice *qt_mac_safe_pdev = 0;
+QList<QMacWindowChangeEvent*> *QMacWindowChangeEvent::change_events = 0;
+extern QHash<QByteArray, QFont> *qt_app_fonts_hash(); // qapplication.cpp
+
+/*****************************************************************************
+ Internal variables and functions
+ *****************************************************************************/
+static struct {
+ bool use_qt_time_limit;
+ QPointer<QWidget> last_widget;
+ int last_x, last_y;
+ int last_modifiers, last_button;
+ EventTime last_time;
+} qt_mac_dblclick = { false, 0, -1, -1, 0, 0, -2 };
+
+static bool app_do_modal = false; // modal mode
+extern QWidgetList *qt_modal_stack; // stack of modal widgets
+extern bool qt_tab_all_widgets; // from qapplication.cpp
+bool qt_mac_app_fullscreen = false;
+bool qt_scrollbar_jump_to_pos = false;
+static bool qt_mac_collapse_on_dblclick = true;
+extern int qt_antialiasing_threshold; // from qapplication.cpp
+QPointer<QWidget> qt_button_down; // widget got last button-down
+#ifndef QT_MAC_USE_COCOA
+static bool qt_button_down_in_content; // whether the button_down was in the content area.
+static bool qt_mac_previous_press_in_popup_mode = false;
+static bool qt_mac_no_click_through_mode = false;
+static int tablet_button_state = 0;
+#endif
+QPointer<QWidget> qt_mouseover;
+#if defined(QT_DEBUG)
+static bool appNoGrab = false; // mouse/keyboard grabbing
+#endif
+#ifndef QT_MAC_USE_COCOA
+static EventHandlerRef app_proc_handler = 0;
+static EventHandlerUPP app_proc_handlerUPP = 0;
+static AEEventHandlerUPP app_proc_ae_handlerUPP = NULL;
+#endif
+static EventHandlerRef tablet_proximity_handler = 0;
+static EventHandlerUPP tablet_proximity_UPP = 0;
+bool QApplicationPrivate::native_modal_dialog_active;
+
+/*****************************************************************************
+ External functions
+ *****************************************************************************/
+extern void qt_mac_beep(); //qsound_mac.mm
+extern Qt::KeyboardModifiers qt_mac_get_modifiers(int keys); //qkeymapper_mac.cpp
+extern bool qt_mac_can_clickThrough(const QWidget *); //qwidget_mac.cpp
+extern bool qt_mac_is_macdrawer(const QWidget *); //qwidget_mac.cpp
+extern OSWindowRef qt_mac_window_for(const QWidget*); //qwidget_mac.cpp
+extern QWidget *qt_mac_find_window(OSWindowRef); //qwidget_mac.cpp
+extern void qt_mac_set_cursor(const QCursor *, const QPoint &); //qcursor_mac.cpp
+extern bool qt_mac_is_macsheet(const QWidget *); //qwidget_mac.cpp
+extern QString qt_mac_from_pascal_string(const Str255); //qglobal.cpp
+extern void qt_mac_command_set_enabled(MenuRef, UInt32, bool); //qmenu_mac.cpp
+extern bool qt_sendSpontaneousEvent(QObject *obj, QEvent *event); // qapplication.cpp
+
+// Forward Decls
+void onApplicationWindowChangedActivation( QWidget*widget, bool activated );
+void onApplicationChangedActivation( bool activated );
+
+Q_GUI_EXPORT bool qt_mac_execute_apple_script(const char *script, long script_len, AEDesc *ret) {
+ OSStatus err;
+ AEDesc scriptTextDesc;
+ ComponentInstance theComponent = 0;
+ OSAID scriptID = kOSANullScript, resultID = kOSANullScript;
+
+ // set up locals to a known state
+ AECreateDesc(typeNull, 0, 0, &scriptTextDesc);
+ scriptID = kOSANullScript;
+ resultID = kOSANullScript;
+
+ // open the scripting component
+ theComponent = OpenDefaultComponent(kOSAComponentType, typeAppleScript);
+ if (!theComponent) {
+ err = paramErr;
+ goto bail;
+ }
+
+ // put the script text into an aedesc
+ err = AECreateDesc(typeUTF8Text, script, script_len, &scriptTextDesc);
+ if (err != noErr)
+ goto bail;
+
+ // compile the script
+ err = OSACompile(theComponent, &scriptTextDesc, kOSAModeNull, &scriptID);
+ if (err != noErr)
+ goto bail;
+
+ // run the script
+ err = OSAExecute(theComponent, scriptID, kOSANullScript, kOSAModeNull, &resultID);
+
+ // collect the results - if any
+ if (ret) {
+ AECreateDesc(typeNull, 0, 0, ret);
+ if (err == errOSAScriptError)
+ OSAScriptError(theComponent, kOSAErrorMessage, typeChar, ret);
+ else if (err == noErr && resultID != kOSANullScript)
+ OSADisplay(theComponent, resultID, typeChar, kOSAModeNull, ret);
+ }
+bail:
+ AEDisposeDesc(&scriptTextDesc);
+ if (scriptID != kOSANullScript)
+ OSADispose(theComponent, scriptID);
+ if (resultID != kOSANullScript)
+ OSADispose(theComponent, resultID);
+ if (theComponent)
+ CloseComponent(theComponent);
+ return err == noErr;
+}
+
+Q_GUI_EXPORT bool qt_mac_execute_apple_script(const char *script, AEDesc *ret)
+{
+ return qt_mac_execute_apple_script(script, qstrlen(script), ret);
+}
+
+Q_GUI_EXPORT bool qt_mac_execute_apple_script(const QString &script, AEDesc *ret)
+{
+ const QByteArray l = script.toUtf8(); return qt_mac_execute_apple_script(l.constData(), l.size(), ret);
+}
+
+/* Resolution change magic */
+void qt_mac_display_change_callbk(CGDirectDisplayID, CGDisplayChangeSummaryFlags flags, void *)
+{
+#if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5)
+ const bool resized = flags & kCGDisplayDesktopShapeChangedFlag;
+#else
+ Q_UNUSED(flags);
+ const bool resized = true;
+#endif
+ if (resized && qApp) {
+ if (QDesktopWidget *dw = qApp->desktop()) {
+ QResizeEvent *re = new QResizeEvent(dw->size(), dw->size());
+ QApplication::postEvent(dw, re);
+ QCoreGraphicsPaintEngine::cleanUpMacColorSpaces();
+ }
+ }
+}
+
+#ifdef DEBUG_PLATFORM_SETTINGS
+static void qt_mac_debug_palette(const QPalette &pal, const QPalette &pal2, const QString &where)
+{
+ const char *const groups[] = {"Active", "Disabled", "Inactive" };
+ const char *const roles[] = { "WindowText", "Button", "Light", "Midlight", "Dark", "Mid",
+ "Text", "BrightText", "ButtonText", "Base", "Window", "Shadow",
+ "Highlight", "HighlightedText", "Link", "LinkVisited" };
+ if (!where.isNull())
+ qDebug("qt-internal: %s", where.toLatin1().constData());
+ for(int grp = 0; grp < QPalette::NColorGroups; grp++) {
+ for(int role = 0; role < QPalette::NColorRoles; role++) {
+ QBrush b = pal.brush((QPalette::ColorGroup)grp, (QPalette::ColorRole)role);
+ QPixmap pm = b.texture();
+ qDebug(" %s::%s %d::%d::%d [%p]%s", groups[grp], roles[role], b.color().red(),
+ b.color().green(), b.color().blue(), pm.isNull() ? 0 : &pm,
+ pal2.brush((QPalette::ColorGroup)grp, (QPalette::ColorRole)role) != b ? " (*)" : "");
+ }
+ }
+
+}
+#else
+#define qt_mac_debug_palette(x, y, z)
+#endif
+
+//raise a notification
+#ifndef QT_MAC_USE_COCOA
+static NMRec qt_mac_notification = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+#endif
+void qt_mac_send_notification()
+{
+#ifndef QT_MAC_USE_COCOA
+ //send it
+ qt_mac_notification.nmMark = 1; //non-zero magic number
+ qt_mac_notification.qType = nmType;
+ NMInstall(&qt_mac_notification);
+#else
+ QMacCocoaAutoReleasePool pool;
+ [[NSApplication sharedApplication] requestUserAttention:NSInformationalRequest];
+#endif
+}
+
+void qt_mac_cancel_notification()
+{
+#ifndef QT_MAC_USE_COCOA
+ NMRemove(&qt_mac_notification);
+#else
+ QMacCocoaAutoReleasePool pool;
+ [[NSApplication sharedApplication] cancelUserAttentionRequest:NSInformationalRequest];
+#endif
+}
+
+#ifndef QT_MAC_USE_COCOA
+//find widget (and part) at a given point
+static short qt_mac_window_at(int x, int y, QWidget **w=0)
+{
+ Point p;
+ p.h = x;
+ p.v = y;
+ OSWindowRef wp;
+ WindowPartCode wpc;
+ OSStatus err = FindWindowOfClass(&p, kAllWindowClasses, &wp, &wpc);
+ if(err != noErr) {
+ if(w)
+ (*w) = 0;
+ return wpc;
+ }
+ if(w) {
+ if(wp) {
+ *w = qt_mac_find_window(wp);
+#if 0
+ if(!*w)
+ qWarning("QApplication: qt_mac_window_at: Couldn't find %d",(int)wp);
+#endif
+ } else {
+ *w = 0;
+ }
+ }
+ return wpc;
+}
+
+#endif
+
+void qt_mac_set_app_icon(const QPixmap &pixmap)
+{
+#ifndef QT_MAC_USE_COCOA
+ if(pixmap.isNull()) {
+ RestoreApplicationDockTileImage();
+ } else {
+ CGImageRef img = (CGImageRef)pixmap.macCGHandle();
+ SetApplicationDockTileImage(img);
+ CGImageRelease(img);
+ }
+#else
+ QMacCocoaAutoReleasePool pool;
+ NSImage *image = NULL;
+ if (pixmap.isNull()) {
+ // Get Application icon from bundle
+ image = [[NSImage imageNamed:@"NSApplicationIcon"] retain]; // released below
+ } else {
+ image = static_cast<NSImage *>(qt_mac_create_nsimage(pixmap));
+ }
+
+ [NSApp setApplicationIconImage:image];
+ [image release];
+#endif
+}
+
+Q_GUI_EXPORT void qt_mac_set_press_and_hold_context(bool b)
+{
+ Q_UNUSED(b);
+ qWarning("qt_mac_set_press_and_hold_context: This functionality is no longer available");
+}
+
+bool qt_nograb() // application no-grab option
+{
+#if defined(QT_DEBUG)
+ return appNoGrab;
+#else
+ return false;
+#endif
+}
+
+void qt_mac_update_os_settings()
+{
+ if (!qApp)
+ return;
+ if (!QApplication::startingUp()) {
+ static bool needToPolish = true;
+ if (needToPolish) {
+ QApplication::style()->polish(qApp);
+ needToPolish = false;
+ }
+ }
+ //focus mode
+ /* First worked as of 10.2.3 */
+ QSettings appleSettings(QLatin1String("apple.com"));
+ QVariant appleValue = appleSettings.value(QLatin1String("AppleKeyboardUIMode"), 0);
+ qt_tab_all_widgets = (appleValue.toInt() & 0x2);
+ //paging mode
+ /* First worked as of 10.2.3 */
+ appleValue = appleSettings.value(QLatin1String("AppleScrollerPagingBehavior"), false);
+ qt_scrollbar_jump_to_pos = appleValue.toBool();
+ //collapse
+ /* First worked as of 10.3.3 */
+ appleValue = appleSettings.value(QLatin1String("AppleMiniaturizeOnDoubleClick"), true);
+ qt_mac_collapse_on_dblclick = appleValue.toBool();
+
+ // Anti-aliasing threshold
+ appleValue = appleSettings.value(QLatin1String("AppleAntiAliasingThreshold"));
+ if (appleValue.isValid())
+ qt_antialiasing_threshold = appleValue.toInt();
+
+#ifdef DEBUG_PLATFORM_SETTINGS
+ qDebug("qt_mac_update_os_settings *********************************************************************");
+#endif
+ { // setup the global palette
+ QColor qc;
+ (void) QApplication::style(); // trigger creation of application style and system palettes
+ QPalette pal = *QApplicationPrivate::sys_pal;
+
+ pal.setBrush( QPalette::Active, QPalette::Highlight, qcolorForTheme(kThemeBrushPrimaryHighlightColor) );
+ pal.setBrush( QPalette::Inactive, QPalette::Highlight, qcolorForTheme(kThemeBrushSecondaryHighlightColor) );
+
+ pal.setBrush( QPalette::Disabled, QPalette::Highlight, qcolorForTheme(kThemeBrushSecondaryHighlightColor) );
+ pal.setBrush( QPalette::Active, QPalette::Shadow, qcolorForTheme(kThemeBrushButtonActiveDarkShadow) );
+
+ pal.setBrush( QPalette::Inactive, QPalette::Shadow, qcolorForTheme(kThemeBrushButtonInactiveDarkShadow) );
+ pal.setBrush( QPalette::Disabled, QPalette::Shadow, qcolorForTheme(kThemeBrushButtonInactiveDarkShadow) );
+
+ qc = qcolorForThemeTextColor(kThemeTextColorDialogActive);
+ pal.setColor(QPalette::Active, QPalette::Text, qc);
+ pal.setColor(QPalette::Active, QPalette::WindowText, qc);
+ pal.setColor(QPalette::Active, QPalette::HighlightedText, qc);
+
+ qc = qcolorForThemeTextColor(kThemeTextColorDialogInactive);
+ pal.setColor(QPalette::Inactive, QPalette::Text, qc);
+ pal.setColor(QPalette::Inactive, QPalette::WindowText, qc);
+ pal.setColor(QPalette::Inactive, QPalette::HighlightedText, qc);
+ pal.setColor(QPalette::Disabled, QPalette::Text, qc);
+ pal.setColor(QPalette::Disabled, QPalette::WindowText, qc);
+ pal.setColor(QPalette::Disabled, QPalette::HighlightedText, qc);
+ pal.setBrush(QPalette::ToolTipBase, QColor(255, 255, 199));
+
+ if (!QApplicationPrivate::sys_pal || *QApplicationPrivate::sys_pal != pal) {
+ QApplicationPrivate::setSystemPalette(pal);
+ QApplication::setPalette(pal);
+ }
+#ifdef DEBUG_PLATFORM_SETTINGS
+ qt_mac_debug_palette(pal, QApplication::palette(), "Global Palette");
+#endif
+ }
+
+ QFont fnt = qfontForThemeFont(kThemeApplicationFont);
+#ifdef DEBUG_PLATFORM_SETTINGS
+ qDebug("qt-internal: Font for Application [%s::%d::%d::%d]",
+ fnt.family().toLatin1().constData(), fnt.pointSize(), fnt.bold(), fnt.italic());
+#endif
+ if (!QApplicationPrivate::sys_font || *QApplicationPrivate::sys_font != fnt)
+ QApplicationPrivate::setSystemFont(fnt);
+
+ { //setup the fonts
+ struct FontMap {
+ FontMap(const char *qc, short fk) : qt_class(qc), font_key(fk) { }
+ const char *const qt_class;
+ short font_key;
+ } mac_widget_fonts[] = {
+ FontMap("QPushButton", kThemePushButtonFont),
+ FontMap("QListView", kThemeViewsFont),
+ FontMap("QListBox", kThemeViewsFont),
+ FontMap("QTitleBar", kThemeWindowTitleFont),
+ FontMap("QMenuBar", kThemeMenuTitleFont),
+ FontMap("QMenu", kThemeMenuItemFont),
+ FontMap("QComboMenuItem", kThemeSystemFont),
+ FontMap("QHeaderView", kThemeSmallSystemFont),
+ FontMap("Q3Header", kThemeSmallSystemFont),
+ FontMap("QTipLabel", kThemeSmallSystemFont),
+ FontMap("QLabel", kThemeSystemFont),
+ FontMap("QToolButton", kThemeSmallSystemFont),
+ FontMap("QMenuItem", kThemeMenuItemCmdKeyFont), // It doesn't exist, but its unique.
+ FontMap("QComboLineEdit", kThemeViewsFont), // It doesn't exist, but its unique.
+ FontMap("QSmallFont", kThemeSmallSystemFont), // It doesn't exist, but its unique.
+ FontMap("QMiniFont", kThemeMiniSystemFont), // It doesn't exist, but its unique.
+ FontMap(0, 0) };
+ for(int i = 0; mac_widget_fonts[i].qt_class; i++) {
+ QFont fnt = qfontForThemeFont(mac_widget_fonts[i].font_key);
+ bool set_font = true;
+ QHash<QByteArray, QFont> *hash = qt_app_fonts_hash();
+ if (!hash->isEmpty()) {
+ QHash<QByteArray, QFont>::const_iterator it
+ = hash->constFind(mac_widget_fonts[i].qt_class);
+ if (it != hash->constEnd())
+ set_font = (fnt != *it);
+ }
+ if (set_font) {
+ QApplication::setFont(fnt, mac_widget_fonts[i].qt_class);
+#ifdef DEBUG_PLATFORM_SETTINGS
+ qDebug("qt-internal: Font for %s [%s::%d::%d::%d]", mac_widget_fonts[i].qt_class,
+ fnt.family().toLatin1().constData(), fnt.pointSize(), fnt.bold(), fnt.italic());
+#endif
+ }
+ }
+ }
+ QApplicationPrivate::initializeWidgetPaletteHash();
+#ifdef DEBUG_PLATFORM_SETTINGS
+ qDebug("qt_mac_update_os_settings END !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
+#endif
+}
+
+void QApplicationPrivate::initializeWidgetPaletteHash()
+{
+ { //setup the palette
+ struct PaletteMap {
+ inline PaletteMap(const char *qc, ThemeBrush a, ThemeBrush i) :
+ qt_class(qc), active(a), inactive(i) { }
+ const char *const qt_class;
+ ThemeBrush active, inactive;
+ } mac_widget_colors[] = {
+ PaletteMap("QToolButton", kThemeTextColorBevelButtonActive, kThemeTextColorBevelButtonInactive),
+ PaletteMap("QAbstractButton", kThemeTextColorPushButtonActive, kThemeTextColorPushButtonInactive),
+ PaletteMap("QHeaderView", kThemeTextColorPushButtonActive, kThemeTextColorPushButtonInactive),
+ PaletteMap("Q3Header", kThemeTextColorPushButtonActive, kThemeTextColorPushButtonInactive),
+ PaletteMap("QComboBox", kThemeTextColorPopupButtonActive, kThemeTextColorPopupButtonInactive),
+ PaletteMap("QAbstractItemView", kThemeTextColorListView, kThemeTextColorDialogInactive),
+ PaletteMap("QMessageBoxLabel", kThemeTextColorAlertActive, kThemeTextColorAlertInactive),
+ PaletteMap("QTabBar", kThemeTextColorTabFrontActive, kThemeTextColorTabFrontInactive),
+ PaletteMap("QLabel", kThemeTextColorPlacardActive, kThemeTextColorPlacardInactive),
+ PaletteMap("QGroupBox", kThemeTextColorPlacardActive, kThemeTextColorPlacardInactive),
+ PaletteMap("QMenu", kThemeTextColorPopupLabelActive, kThemeTextColorPopupLabelInactive),
+ PaletteMap("QTextEdit", 0, 0),
+ PaletteMap("QTextControl", 0, 0),
+ PaletteMap("QLineEdit", 0, 0),
+ PaletteMap(0, 0, 0) };
+ QColor qc;
+ for(int i = 0; mac_widget_colors[i].qt_class; i++) {
+ QPalette pal;
+ if (mac_widget_colors[i].active != 0) {
+ qc = qcolorForThemeTextColor(mac_widget_colors[i].active);
+ pal.setColor(QPalette::Active, QPalette::Text, qc);
+ pal.setColor(QPalette::Active, QPalette::WindowText, qc);
+ pal.setColor(QPalette::Active, QPalette::HighlightedText, qc);
+ qc = qcolorForThemeTextColor(mac_widget_colors[i].inactive);
+ pal.setColor(QPalette::Inactive, QPalette::Text, qc);
+ pal.setColor(QPalette::Disabled, QPalette::Text, qc);
+ pal.setColor(QPalette::Inactive, QPalette::WindowText, qc);
+ pal.setColor(QPalette::Disabled, QPalette::WindowText, qc);
+ pal.setColor(QPalette::Inactive, QPalette::HighlightedText, qc);
+ pal.setColor(QPalette::Disabled, QPalette::HighlightedText, qc);
+ }
+ if (!strcmp(mac_widget_colors[i].qt_class, "QMenu")) {
+ qc = qcolorForThemeTextColor(kThemeTextColorMenuItemActive);
+ pal.setBrush(QPalette::ButtonText, qc);
+ qc = qcolorForThemeTextColor(kThemeTextColorMenuItemSelected);
+ pal.setBrush(QPalette::HighlightedText, qc);
+ qc = qcolorForThemeTextColor(kThemeTextColorMenuItemDisabled);
+ pal.setBrush(QPalette::Disabled, QPalette::Text, qc);
+ } else if (!strcmp(mac_widget_colors[i].qt_class, "QAbstractButton")
+ || !strcmp(mac_widget_colors[i].qt_class, "QHeaderView")
+ || !strcmp(mac_widget_colors[i].qt_class, "Q3Header")) { //special
+ pal.setColor(QPalette::Disabled, QPalette::ButtonText,
+ pal.color(QPalette::Disabled, QPalette::Text));
+ pal.setColor(QPalette::Inactive, QPalette::ButtonText,
+ pal.color(QPalette::Inactive, QPalette::Text));
+ pal.setColor(QPalette::Active, QPalette::ButtonText,
+ pal.color(QPalette::Active, QPalette::Text));
+ } else if (!strcmp(mac_widget_colors[i].qt_class, "QAbstractItemView")) {
+ pal.setBrush(QPalette::Active, QPalette::Highlight,
+ qcolorForTheme(kThemeBrushAlternatePrimaryHighlightColor));
+ qc = qcolorForThemeTextColor(kThemeTextColorMenuItemSelected);
+ pal.setBrush(QPalette::Active, QPalette::HighlightedText, qc);
+#if 1
+ pal.setBrush(QPalette::Inactive, QPalette::Text,
+ pal.brush(QPalette::Active, QPalette::Text));
+ pal.setBrush(QPalette::Inactive, QPalette::HighlightedText,
+ pal.brush(QPalette::Active, QPalette::Text));
+#endif
+ } else if (!strcmp(mac_widget_colors[i].qt_class, "QTextEdit")
+ || !strcmp(mac_widget_colors[i].qt_class, "QTextControl")) {
+ pal.setBrush(QPalette::Inactive, QPalette::Text,
+ pal.brush(QPalette::Active, QPalette::Text));
+ pal.setBrush(QPalette::Inactive, QPalette::HighlightedText,
+ pal.brush(QPalette::Active, QPalette::Text));
+ } else if (!strcmp(mac_widget_colors[i].qt_class, "QLineEdit")) {
+ pal.setBrush(QPalette::Disabled, QPalette::Base,
+ pal.brush(QPalette::Active, QPalette::Base));
+ }
+
+ bool set_palette = true;
+ extern QHash<QByteArray, QPalette> *qt_app_palettes_hash(); //qapplication.cpp
+ QHash<QByteArray, QPalette> *phash = qt_app_palettes_hash();
+ if (!phash->isEmpty()) {
+ QHash<QByteArray, QPalette>::const_iterator it
+ = phash->constFind(mac_widget_colors[i].qt_class);
+ if (it != phash->constEnd())
+ set_palette = (pal != *it);
+ }
+ if (set_palette) {
+ QApplication::setPalette(pal, mac_widget_colors[i].qt_class);
+#ifdef DEBUG_PLATFORM_SETTINGS
+ qt_mac_debug_palette(pal, QApplication::palette(), QLatin1String("Palette for ") + QString::fromLatin1(mac_widget_colors[i].qt_class));
+#endif
+ }
+ }
+ }
+}
+
+static void qt_mac_event_release(EventRef &event)
+{
+ ReleaseEvent(event);
+ event = 0;
+}
+#ifndef QT_MAC_USE_COCOA
+static void qt_mac_event_release(QWidget *w, EventRef &event)
+{
+ if (event) {
+ QWidget *widget = 0;
+ if (GetEventParameter(event, kEventParamQWidget, typeQWidget, 0, sizeof(widget), 0, &widget) == noErr
+ && w == widget) {
+ if (IsEventInQueue(GetMainEventQueue(), event))
+ RemoveEventFromQueue(GetMainEventQueue(), event);
+ qt_mac_event_release(event);
+ }
+ }
+}
+
+static bool qt_mac_event_remove(EventRef &event)
+{
+ if (event) {
+ if (IsEventInQueue(GetMainEventQueue(), event))
+ RemoveEventFromQueue(GetMainEventQueue(), event);
+ qt_mac_event_release(event);
+ return true;
+ }
+ return false;
+}
+#endif
+
+/* sheets */
+#ifndef QT_MAC_USE_COCOA
+static EventRef request_showsheet_pending = 0;
+#endif
+void qt_event_request_showsheet(QWidget *w)
+{
+ Q_ASSERT(qt_mac_is_macsheet(w));
+#ifdef QT_MAC_USE_COCOA
+ [NSApp beginSheet:qt_mac_window_for(w) modalForWindow:qt_mac_window_for(w->parentWidget())
+ modalDelegate:nil didEndSelector:nil contextInfo:0];
+#else
+ qt_mac_event_remove(request_showsheet_pending);
+ CreateEvent(0, kEventClassQt, kEventQtRequestShowSheet, GetCurrentEventTime(),
+ kEventAttributeUserEvent, &request_showsheet_pending);
+ SetEventParameter(request_showsheet_pending, kEventParamQWidget, typeQWidget, sizeof(w), &w);
+ PostEventToQueue(GetMainEventQueue(), request_showsheet_pending, kEventPriorityStandard);
+#endif
+}
+
+static void qt_post_window_change_event(QWidget *widget)
+{
+ qt_widget_private(widget)->needWindowChange = true;
+ QEvent *glWindowChangeEvent = new QEvent(QEvent::MacGLWindowChange);
+ QApplication::postEvent(widget, glWindowChangeEvent);
+}
+
+/*
+ Posts updates to all child and grandchild OpenGL widgets for the given widget.
+*/
+static void qt_mac_update_child_gl_widgets(QWidget *widget)
+{
+ if (widget->isWindow())
+ return;
+
+ // Update all OpenGL child widgets for the given widget.
+ QList<QWidgetPrivate::GlWidgetInfo> &glWidgets = qt_widget_private(widget)->glWidgets;
+ QList<QWidgetPrivate::GlWidgetInfo>::iterator end = glWidgets.end();
+ QList<QWidgetPrivate::GlWidgetInfo>::iterator it = glWidgets.begin();
+
+ for (;it != end; ++it) {
+ qt_post_window_change_event(it->widget);
+ }
+}
+
+/*
+ Sends updates to all child and grandchild gl widgets that have updates pending.
+*/
+void qt_mac_send_posted_gl_updates(QWidget *widget)
+{
+ QList<QWidgetPrivate::GlWidgetInfo> &glWidgets = qt_widget_private(widget)->glWidgets;
+ QList<QWidgetPrivate::GlWidgetInfo>::iterator end = glWidgets.end();
+ QList<QWidgetPrivate::GlWidgetInfo>::iterator it = glWidgets.begin();
+
+ for (;it != end; ++it) {
+ QWidget *glWidget = it->widget;
+ if (qt_widget_private(glWidget)->needWindowChange) {
+ QEvent glChangeEvent(QEvent::MacGLWindowChange);
+ QApplication::sendEvent(glWidget, &glChangeEvent);
+ }
+ }
+}
+
+/*
+ Posts updates to all OpenGL widgets within the window that the given widget intersects.
+*/
+static void qt_mac_update_intersected_gl_widgets(QWidget *widget)
+{
+#ifndef QT_MAC_USE_COCOA
+ QList<QWidgetPrivate::GlWidgetInfo> &glWidgets = qt_widget_private(widget->window())->glWidgets;
+ if (glWidgets.isEmpty())
+ return;
+
+ // Exit if the window has not been created yet (mapToGlobal/size will force create it)
+ if (widget->testAttribute(Qt::WA_WState_Created) == false || HIViewGetWindow(qt_mac_nativeview_for(widget)) == 0)
+ return;
+
+ const QRect globalWidgetRect = QRect(widget->mapToGlobal(QPoint(0, 0)), widget->size());
+
+ QList<QWidgetPrivate::GlWidgetInfo>::iterator end = glWidgets.end();
+ QList<QWidgetPrivate::GlWidgetInfo>::iterator it = glWidgets.begin();
+
+ for (;it != end; ++it){
+ QWidget *glWidget = it->widget;
+ const QRect globalGlWidgetRect = QRect(glWidget->mapToGlobal(QPoint(0, 0)), glWidget->size());
+ if (globalWidgetRect.intersects(globalGlWidgetRect)) {
+ qt_post_window_change_event(glWidget);
+ it->lastUpdateWidget = widget;
+ } else if (it->lastUpdateWidget == widget) {
+ // Update the gl wigets that the widget intersected the last time around,
+ // and that we are not intersecting now. This prevents paint errors when the
+ // intersecting widget leaves a gl widget.
+ qt_post_window_change_event(glWidget);
+ it->lastUpdateWidget = 0;
+ }
+ }
+#else
+ Q_UNUSED(widget);
+#endif
+}
+
+/*
+ Posts a kEventQtRequestWindowChange event to the main Carbon event queue.
+*/
+static EventRef request_window_change_pending = 0;
+Q_GUI_EXPORT void qt_event_request_window_change()
+{
+ if(request_window_change_pending)
+ return;
+
+ CreateEvent(0, kEventClassQt, kEventQtRequestWindowChange, GetCurrentEventTime(),
+ kEventAttributeUserEvent, &request_window_change_pending);
+ PostEventToQueue(GetMainEventQueue(), request_window_change_pending, kEventPriorityHigh);
+}
+
+/* window changing. This is a hack around Apple's missing functionality, pending the toolbox
+ team fix. --Sam */
+Q_GUI_EXPORT void qt_event_request_window_change(QWidget *widget)
+{
+ if (!widget)
+ return;
+
+ // Post a kEventQtRequestWindowChange event. This event is semi-public,
+ // don't remove this line!
+ qt_event_request_window_change();
+
+ // Post update request on gl widgets unconditionally.
+ if (qt_widget_private(widget)->isGLWidget == true) {
+ qt_post_window_change_event(widget);
+ return;
+ }
+
+ qt_mac_update_child_gl_widgets(widget);
+ qt_mac_update_intersected_gl_widgets(widget);
+}
+
+/* activation */
+static struct {
+ QPointer<QWidget> widget;
+ EventRef event;
+ EventLoopTimerRef timer;
+ EventLoopTimerUPP timerUPP;
+} request_activate_pending = { 0, 0, 0, 0 };
+bool qt_event_remove_activate()
+{
+ if (request_activate_pending.timer) {
+ RemoveEventLoopTimer(request_activate_pending.timer);
+ request_activate_pending.timer = 0;
+ }
+ if (request_activate_pending.event)
+ qt_mac_event_release(request_activate_pending.event);
+ return true;
+}
+
+void qt_event_activate_timer_callbk(EventLoopTimerRef r, void *)
+{
+ EventLoopTimerRef otc = request_activate_pending.timer;
+ qt_event_remove_activate();
+ if (r == otc && !request_activate_pending.widget.isNull()) {
+ const QWidget *tlw = request_activate_pending.widget->window();
+ Qt::WindowType wt = tlw->windowType();
+ if (tlw->isVisible()
+ && ((wt != Qt::Desktop && wt != Qt::Popup && wt != Qt::Tool) || tlw->isModal())) {
+ CreateEvent(0, kEventClassQt, kEventQtRequestActivate, GetCurrentEventTime(),
+ kEventAttributeUserEvent, &request_activate_pending.event);
+ PostEventToQueue(GetMainEventQueue(), request_activate_pending.event, kEventPriorityHigh);
+ }
+ }
+}
+
+void qt_event_request_activate(QWidget *w)
+{
+ if (w == request_activate_pending.widget)
+ return;
+
+ /* We put these into a timer because due to order of events being sent we need to be sure this
+ comes from inside of the event loop */
+ qt_event_remove_activate();
+ if (!request_activate_pending.timerUPP)
+ request_activate_pending.timerUPP = NewEventLoopTimerUPP(qt_event_activate_timer_callbk);
+ request_activate_pending.widget = w;
+ InstallEventLoopTimer(GetMainEventLoop(), 0, 0, request_activate_pending.timerUPP, 0, &request_activate_pending.timer);
+}
+
+
+/* menubars */
+#ifndef QT_MAC_USE_COCOA
+static EventRef request_menubarupdate_pending = 0;
+#endif
+void qt_event_request_menubarupdate()
+{
+#ifndef QT_MAC_USE_COCOA
+ if (request_menubarupdate_pending) {
+ if (IsEventInQueue(GetMainEventQueue(), request_menubarupdate_pending))
+ return;
+#ifdef DEBUG_DROPPED_EVENTS
+ qDebug("%s:%d Whoa, we dropped an event on the floor!", __FILE__, __LINE__);
+#endif
+ }
+
+ CreateEvent(0, kEventClassQt, kEventQtRequestMenubarUpdate, GetCurrentEventTime(),
+ kEventAttributeUserEvent, &request_menubarupdate_pending);
+ PostEventToQueue(GetMainEventQueue(), request_menubarupdate_pending, kEventPriorityHigh);
+#else
+ // Just call this. The request has the benefit that we don't call this multiple times, but
+ // we can optimize this.
+ QMenuBar::macUpdateMenuBar();
+#endif
+}
+
+#ifndef QT_MAC_USE_COCOA
+//context menu
+static EventRef request_context_pending = 0;
+static void qt_event_request_context(QWidget *w=0, EventRef *where=0)
+{
+ if (!where)
+ where = &request_context_pending;
+ if (*where)
+ return;
+ CreateEvent(0, kEventClassQt, kEventQtRequestContext, GetCurrentEventTime(),
+ kEventAttributeUserEvent, where);
+ if (w)
+ SetEventParameter(*where, kEventParamQWidget, typeQWidget, sizeof(w), &w);
+ PostEventToQueue(GetMainEventQueue(), *where, kEventPriorityStandard);
+}
+#endif
+
+void QApplicationPrivate::createEventDispatcher()
+{
+ Q_Q(QApplication);
+ if (q->type() != QApplication::Tty)
+ eventDispatcher = new QEventDispatcherMac(q);
+ else
+ eventDispatcher = new QEventDispatcherUNIX(q);
+}
+
+/* clipboard */
+void qt_event_send_clipboard_changed()
+{
+#ifndef QT_MAC_USE_COCOA
+ AppleEvent ae;
+ if (AECreateAppleEvent(kEventClassQt, typeAEClipboardChanged, 0, kAutoGenerateReturnID, kAnyTransactionID, &ae) != noErr)
+ qDebug("Can't happen!!");
+ AppleEvent reply;
+ AESend(&ae, &reply, kAENoReply, kAENormalPriority, kAEDefaultTimeout, 0, 0);
+#endif
+}
+
+/* app menu */
+static QMenu *qt_mac_dock_menu = 0;
+Q_GUI_EXPORT void qt_mac_set_dock_menu(QMenu *menu)
+{
+ qt_mac_dock_menu = menu;
+#ifdef QT_MAC_USE_COCOA
+ [NSApp setDockMenu:menu->macMenu()];
+#else
+ SetApplicationDockTileMenu(menu->macMenu());
+#endif
+}
+
+/* events that hold pointers to widgets, must be cleaned up like this */
+void qt_mac_event_release(QWidget *w)
+{
+ if (w) {
+#ifndef QT_MAC_USE_COCOA
+ qt_mac_event_release(w, request_showsheet_pending);
+ qt_mac_event_release(w, request_context_pending);
+#endif
+ if (w == qt_mac_dock_menu) {
+ qt_mac_dock_menu = 0;
+#ifndef QT_MAC_USE_COCOA
+ SetApplicationDockTileMenu(0);
+#else
+ [NSApp setDockMenu:0];
+#endif
+ }
+ }
+}
+
+#ifndef QT_MAC_USE_COCOA
+struct QMacAppleEventTypeSpec {
+ AEEventClass mac_class;
+ AEEventID mac_id;
+} app_apple_events[] = {
+ { kCoreEventClass, kAEQuitApplication },
+ { kCoreEventClass, kAEOpenDocuments }
+};
+/* watched events */
+static EventTypeSpec app_events[] = {
+ { kEventClassQt, kEventQtRequestWindowChange },
+ { kEventClassQt, kEventQtRequestShowSheet },
+ { kEventClassQt, kEventQtRequestContext },
+ { kEventClassQt, kEventQtRequestActivate },
+ { kEventClassQt, kEventQtRequestMenubarUpdate },
+
+ { kEventClassWindow, kEventWindowActivated },
+ { kEventClassWindow, kEventWindowDeactivated },
+
+ { kEventClassMouse, kEventMouseWheelMoved },
+ { kEventClassMouse, kEventMouseDown },
+ { kEventClassMouse, kEventMouseUp },
+ { kEventClassMouse, kEventMouseDragged },
+ { kEventClassMouse, kEventMouseMoved },
+
+ { kEventClassTablet, kEventTabletProximity },
+
+ { kEventClassApplication, kEventAppActivated },
+ { kEventClassApplication, kEventAppDeactivated },
+ { kEventClassApplication, kEventAppAvailableWindowBoundsChanged },
+
+ // { kEventClassTextInput, kEventTextInputUnicodeForKeyEvent },
+ { kEventClassKeyboard, kEventRawKeyModifiersChanged },
+ { kEventClassKeyboard, kEventRawKeyRepeat },
+ { kEventClassKeyboard, kEventRawKeyUp },
+ { kEventClassKeyboard, kEventRawKeyDown },
+
+ { kEventClassCommand, kEventCommandProcess },
+
+ { kEventClassAppleEvent, kEventAppleEvent },
+
+ { kAppearanceEventClass, kAEAppearanceChanged }
+};
+
+void qt_init_app_proc_handler()
+{
+ InstallEventHandler(GetApplicationEventTarget(), app_proc_handlerUPP,
+ GetEventTypeCount(app_events), app_events, (void *)qApp,
+ &app_proc_handler);
+}
+#endif // QT_MAC_USE_COCOA
+
+static void qt_init_tablet_proximity_handler()
+{
+ EventTypeSpec tabletProximityEvent = { kEventClassTablet, kEventTabletProximity };
+ InstallEventHandler(GetEventMonitorTarget(), tablet_proximity_UPP,
+ 1, &tabletProximityEvent, qApp, &tablet_proximity_handler);
+}
+
+static void qt_release_tablet_proximity_handler()
+{
+ RemoveEventHandler(tablet_proximity_handler);
+}
+
+QString QApplicationPrivate::appName() const
+{
+ static QString applName;
+ if (applName.isEmpty()) {
+ applName = QCoreApplicationPrivate::macMenuBarName();
+ ProcessSerialNumber psn;
+ if (applName.isEmpty() && qt_is_gui_used && GetCurrentProcess(&psn) == noErr) {
+ QCFString cfstr;
+ CopyProcessName(&psn, &cfstr);
+ applName = cfstr;
+ }
+ }
+ return applName;
+}
+
+void qt_release_app_proc_handler()
+{
+#ifndef QT_MAC_USE_COCOA
+ if (app_proc_handler) {
+ RemoveEventHandler(app_proc_handler);
+ app_proc_handler = 0;
+ }
+#endif
+}
+
+/* platform specific implementations */
+void qt_init(QApplicationPrivate *priv, int)
+{
+ if (qt_is_gui_used) {
+ CGDisplayRegisterReconfigurationCallback(qt_mac_display_change_callbk, 0);
+ ProcessSerialNumber psn;
+ if (GetCurrentProcess(&psn) == noErr) {
+ // Jambi needs to transform itself since most people aren't "used"
+ // to putting things in bundles, but other people may actually not
+ // want to tranform the process (running as a helper or somethng)
+ // so don't do that for them. This means checking both LSUIElement
+ // and LSBackgroundOnly. If you set them both... well, you
+ // shouldn't do that.
+
+ bool forceTransform = true;
+ CFTypeRef value = CFBundleGetValueForInfoDictionaryKey(CFBundleGetMainBundle(),
+ CFSTR("LSUIElement"));
+ if (value) {
+ CFTypeID valueType = CFGetTypeID(value);
+ // Officially it's supposed to be a string, a boolean makes sense, so we'll check.
+ // A number less so, but OK.
+ if (valueType == CFStringGetTypeID())
+ forceTransform = !(QCFString::toQString(static_cast<CFStringRef>(value)).toInt());
+ else if (valueType == CFBooleanGetTypeID())
+ forceTransform = !CFBooleanGetValue(static_cast<CFBooleanRef>(value));
+ else if (valueType == CFNumberGetTypeID()) {
+ int valueAsInt;
+ CFNumberGetValue(static_cast<CFNumberRef>(value), kCFNumberIntType, &valueAsInt);
+ forceTransform = !valueAsInt;
+ }
+ }
+
+ if (forceTransform) {
+ value = CFBundleGetValueForInfoDictionaryKey(CFBundleGetMainBundle(),
+ CFSTR("LSBackgroundOnly"));
+ if (value) {
+ CFTypeID valueType = CFGetTypeID(value);
+ if (valueType == CFBooleanGetTypeID())
+ forceTransform = !CFBooleanGetValue(static_cast<CFBooleanRef>(value));
+ else if (valueType == CFStringGetTypeID())
+ forceTransform = !(QCFString::toQString(static_cast<CFStringRef>(value)).toInt());
+ else if (valueType == CFNumberGetTypeID()) {
+ int valueAsInt;
+ CFNumberGetValue(static_cast<CFNumberRef>(value), kCFNumberIntType, &valueAsInt);
+ forceTransform = !valueAsInt;
+ }
+ }
+ }
+
+
+ if (forceTransform) {
+ TransformProcessType(&psn, kProcessTransformToForegroundApplication);
+ }
+ }
+ }
+
+ char **argv = priv->argv;
+
+ // Get command line params
+ if (int argc = priv->argc) {
+ int i, j = 1;
+ QString passed_psn;
+ for(i=1; i < argc; i++) {
+ if (argv[i] && *argv[i] != '-') {
+ argv[j++] = argv[i];
+ continue;
+ }
+ QByteArray arg(argv[i]);
+#if defined(QT_DEBUG)
+ if (arg == "-nograb")
+ appNoGrab = !appNoGrab;
+ else
+#endif // QT_DEBUG
+ if (arg.left(5) == "-psn_") {
+ passed_psn = QString::fromLatin1(arg.mid(6));
+ } else {
+ argv[j++] = argv[i];
+ }
+ }
+ if (j < priv->argc) {
+ priv->argv[j] = 0;
+ priv->argc = j;
+ }
+
+ //special hack to change working directory (for an app bundle) when running from finder
+ if (!passed_psn.isNull() && QDir::currentPath() == QLatin1String("/")) {
+ QCFType<CFURLRef> bundleURL(CFBundleCopyBundleURL(CFBundleGetMainBundle()));
+ QString qbundlePath = QCFString(CFURLCopyFileSystemPath(bundleURL,
+ kCFURLPOSIXPathStyle));
+ if (qbundlePath.endsWith(QLatin1String(".app")))
+ QDir::setCurrent(qbundlePath.section(QLatin1Char('/'), 0, -2));
+ }
+ }
+
+ QMacPasteboardMime::initialize();
+
+ qApp->setObjectName(priv->appName());
+ if (qt_is_gui_used) {
+ QColormap::initialize();
+ QFont::initialize();
+ QCursorData::initialize();
+ QCoreGraphicsPaintEngine::initialize();
+#ifndef QT_NO_ACCESSIBILITY
+ QAccessible::initialize();
+#endif
+ QMacInputContext::initialize();
+ QApplicationPrivate::inputContext = new QMacInputContext;
+
+ if (QApplication::desktopSettingsAware())
+ qt_mac_update_os_settings();
+#ifndef QT_MAC_USE_COCOA
+ if (!app_proc_handler) {
+ app_proc_handlerUPP = NewEventHandlerUPP(QApplicationPrivate::globalEventProcessor);
+ qt_init_app_proc_handler();
+ }
+
+ if (!app_proc_ae_handlerUPP) {
+ app_proc_ae_handlerUPP = AEEventHandlerUPP(QApplicationPrivate::globalAppleEventProcessor);
+ for(uint i = 0; i < sizeof(app_apple_events) / sizeof(QMacAppleEventTypeSpec); ++i)
+ AEInstallEventHandler(app_apple_events[i].mac_class, app_apple_events[i].mac_id,
+ app_proc_ae_handlerUPP, SRefCon(qApp), true);
+ }
+#endif
+
+ if (QApplicationPrivate::app_style) {
+ QEvent ev(QEvent::Style);
+ qt_sendSpontaneousEvent(QApplicationPrivate::app_style, &ev);
+ }
+ }
+ if (QApplication::desktopSettingsAware())
+ QApplicationPrivate::qt_mac_apply_settings();
+ // Cocoa application delegate
+#ifdef QT_MAC_USE_COCOA
+ NSApplication *cocoaApp = [NSApplication sharedApplication];
+ QMacCocoaAutoReleasePool pool;
+ NSObject *oldDelegate = [cocoaApp delegate];
+ QT_MANGLE_NAMESPACE(QCocoaApplicationDelegate) *newDelegate = [QT_MANGLE_NAMESPACE(QCocoaApplicationDelegate) sharedDelegate];
+ Q_ASSERT(newDelegate);
+ [newDelegate setQtPrivate:priv];
+ // Only do things that make sense to do once, otherwise we crash.
+ if (oldDelegate != newDelegate && !QApplication::testAttribute(Qt::AA_MacPluginApplication)) {
+ [newDelegate setReflectionDelegate:oldDelegate];
+ [cocoaApp setDelegate:newDelegate];
+
+ QT_MANGLE_NAMESPACE(QCocoaMenuLoader) *qtMenuLoader = [[QT_MANGLE_NAMESPACE(QCocoaMenuLoader) alloc] init];
+ if ([NSBundle loadNibNamed:@"qt_menu" owner:qtMenuLoader] == false) {
+ qFatal("Qt internal error: qt_menu.nib could not be loaded. The .nib file"
+ " should be placed in QtGui.framework/Versions/Current/Resources/ "
+ " or in the resources directory of your application bundle.");
+ }
+
+ [cocoaApp setMenu:[qtMenuLoader menu]];
+ [newDelegate setMenuLoader:qtMenuLoader];
+ [qtMenuLoader release];
+ }
+#endif
+ if (QApplication::testAttribute(Qt::AA_MacPluginApplication)) {
+ extern void qt_mac_set_native_menubar(bool);
+ qt_mac_set_native_menubar(false);
+ }
+ // Register for Carbon tablet proximity events on the event monitor target.
+ // This means that we should receive proximity events even when we aren't the active application.
+ if (!tablet_proximity_handler) {
+ tablet_proximity_UPP = NewEventHandlerUPP(QApplicationPrivate::tabletProximityCallback);
+ qt_init_tablet_proximity_handler();
+ }
+ priv->native_modal_dialog_active = false;
+
+}
+
+/*****************************************************************************
+ qt_cleanup() - cleans up when the application is finished
+ *****************************************************************************/
+
+void qt_cleanup()
+{
+ CGDisplayRemoveReconfigurationCallback(qt_mac_display_change_callbk, 0);
+#ifndef QT_MAC_USE_COCOA
+ qt_release_app_proc_handler();
+ if (app_proc_handlerUPP) {
+ DisposeEventHandlerUPP(app_proc_handlerUPP);
+ app_proc_handlerUPP = 0;
+ }
+ if(app_proc_ae_handlerUPP) {
+ for(uint i = 0; i < sizeof(app_apple_events) / sizeof(QMacAppleEventTypeSpec); ++i)
+ AERemoveEventHandler(app_apple_events[i].mac_class, app_apple_events[i].mac_id,
+ app_proc_ae_handlerUPP, true);
+ DisposeAEEventHandlerUPP(app_proc_ae_handlerUPP);
+ app_proc_ae_handlerUPP = NULL;
+ }
+#endif
+
+ qt_release_tablet_proximity_handler();
+ if (tablet_proximity_UPP)
+ DisposeEventHandlerUPP(tablet_proximity_UPP);
+
+ QPixmapCache::clear();
+ if (qt_is_gui_used) {
+#ifndef QT_NO_ACCESSIBILITY
+ QAccessible::cleanup();
+#endif
+ QMacInputContext::cleanup();
+ QCursorData::cleanup();
+ QFont::cleanup();
+ QColormap::cleanup();
+ if (qt_mac_safe_pdev) {
+ delete qt_mac_safe_pdev;
+ qt_mac_safe_pdev = 0;
+ }
+ extern void qt_mac_unregister_widget(); // qapplication_mac.cpp
+ qt_mac_unregister_widget();
+ }
+}
+
+/*****************************************************************************
+ Platform specific global and internal functions
+ *****************************************************************************/
+void qt_updated_rootinfo()
+{
+}
+
+bool qt_wstate_iconified(WId)
+{
+ return false;
+}
+
+/*****************************************************************************
+ Platform specific QApplication members
+ *****************************************************************************/
+extern QWidget * mac_mouse_grabber;
+extern QWidget * mac_keyboard_grabber;
+
+#ifdef QT3_SUPPORT
+void QApplication::setMainWidget(QWidget *mainWidget)
+{
+ QApplicationPrivate::main_widget = mainWidget;
+ if (QApplicationPrivate::main_widget && windowIcon().isNull()
+ && QApplicationPrivate::main_widget->testAttribute(Qt::WA_SetWindowIcon))
+ setWindowIcon(QApplicationPrivate::main_widget->windowIcon());
+}
+#endif
+#ifndef QT_NO_CURSOR
+
+/*****************************************************************************
+ QApplication cursor stack
+ *****************************************************************************/
+void QApplication::setOverrideCursor(const QCursor &cursor)
+{
+ qApp->d_func()->cursor_list.prepend(cursor);
+
+ if (qApp && qApp->activeWindow())
+ qt_mac_set_cursor(&qApp->d_func()->cursor_list.first(), QCursor::pos());
+}
+
+void QApplication::restoreOverrideCursor()
+{
+ if (qApp->d_func()->cursor_list.isEmpty())
+ return;
+ qApp->d_func()->cursor_list.removeFirst();
+
+ if (qApp && qApp->activeWindow()) {
+ const QCursor def(Qt::ArrowCursor);
+ qt_mac_set_cursor(qApp->d_func()->cursor_list.isEmpty() ? &def : &qApp->d_func()->cursor_list.first(), QCursor::pos());
+ }
+}
+#endif
+
+QWidget *QApplication::topLevelAt(const QPoint &p)
+{
+#ifndef QT_MAC_USE_COCOA
+ QWidget *widget;
+ qt_mac_window_at(p.x(), p.y(), &widget);
+ return widget;
+#else
+ NSInteger windowCount;
+ NSCountWindows(&windowCount);
+ if (windowCount <= 0)
+ return 0; // There's no window to find!
+ QMacCocoaAutoReleasePool pool;
+ NSPoint cocoaPoint = flipPoint(p);
+ QVarLengthArray<NSInteger> windowList(windowCount);
+ NSWindowList(windowCount, windowList.data());
+ for (int i = 0; i < windowCount; ++i) {
+ NSWindow *window = [NSApp windowWithWindowNumber:windowList[i]];
+ if (window && NSPointInRect(cocoaPoint, [window frame])) {
+ QWidget *candidateWindow = [window QT_MANGLE_NAMESPACE(qt_qwidget)];
+ // Check to see if there's a hole in the window where the mask is.
+ // If there is, we should just continue to see if there is a window below.
+ if (candidateWindow && !candidateWindow->mask().isEmpty()) {
+ QPoint localPoint = candidateWindow->mapFromGlobal(p);
+ if (!candidateWindow->mask().contains(localPoint)) {
+ continue;
+ }
+ }
+ return candidateWindow;
+ }
+ }
+ return 0; // Couldn't find a window at this point
+#endif
+}
+
+static QWidget *qt_mac_recursive_widgetAt(QWidget *widget, int x, int y)
+{
+ if (!widget)
+ return 0;
+ const QObjectList kids = widget->children();
+ for(int i = kids.size()-1; i >= 0; --i) {
+ if ( QWidget *kid = qobject_cast<QWidget*>(kids.at(i)) ) {
+ if (kid->isVisible() && !kid->isTopLevel() &&
+ !kid->testAttribute(Qt::WA_TransparentForMouseEvents)) {
+ const int wx=kid->x(), wy=kid->y(),
+ wx2=wx+kid->width(), wy2=wy+kid->height();
+ if (x >= wx && y >= wy && x < wx2 && y < wy2) {
+ const QRegion mask = kid->mask();
+ if (!mask.isEmpty() && !mask.contains(QPoint(x-wx, y-wy)))
+ continue;
+ return qt_mac_recursive_widgetAt(kid, x-wx, y-wy);
+ }
+ }
+ }
+ }
+ return widget;
+}
+
+/*****************************************************************************
+ Main event loop
+ *****************************************************************************/
+
+bool QApplicationPrivate::modalState()
+{
+ return app_do_modal;
+}
+
+#ifdef QT_MAC_USE_COCOA
+#endif
+
+void QApplicationPrivate::enterModal_sys(QWidget *widget)
+{
+#ifdef DEBUG_MODAL_EVENTS
+ Q_ASSERT(widget);
+ qDebug("Entering modal state with %s::%s::%p (%d)", widget->metaObject()->className(), widget->objectName().toLocal8Bit().constData(),
+ widget, qt_modal_stack ? (int)qt_modal_stack->count() : -1);
+#endif
+ if (!qt_modal_stack)
+ qt_modal_stack = new QWidgetList;
+
+ dispatchEnterLeave(0, qt_mouseover);
+ qt_mouseover = 0;
+
+ qt_modal_stack->insert(0, widget);
+ if (!app_do_modal)
+ qt_event_request_menubarupdate();
+ app_do_modal = true;
+ qt_button_down = 0;
+
+#ifdef QT_MAC_USE_COCOA
+ if (!qt_mac_is_macsheet(widget)) {
+ // Add a new, empty (null), NSModalSession to the stack.
+ // The next time we spin the event dispatcher, it will
+ // check the stack, and recurse into a modal session for it:
+ QCocoaModalSessionInfo info = {widget, 0};
+ QEventDispatcherMacPrivate::cocoaModalSessionStack.push(info);
+ }
+#endif
+}
+
+void QApplicationPrivate::leaveModal_sys(QWidget *widget)
+{
+ if (qt_modal_stack && qt_modal_stack->removeAll(widget)) {
+#ifdef DEBUG_MODAL_EVENTS
+ qDebug("Leaving modal state with %s::%s::%p (%d)", widget->metaObject()->className(), widget->objectName().toLocal8Bit().constData(),
+ widget, qt_modal_stack->count());
+#endif
+ if (qt_modal_stack->isEmpty()) {
+ delete qt_modal_stack;
+ qt_modal_stack = 0;
+ QPoint p(QCursor::pos());
+ app_do_modal = false;
+ QWidget* w = 0;
+ if (QWidget *grabber = QWidget::mouseGrabber())
+ w = grabber;
+ else
+ w = QApplication::widgetAt(p.x(), p.y());
+ dispatchEnterLeave(w, qt_mouseover); // send synthetic enter event
+ qt_mouseover = w;
+ }
+#ifdef QT_MAC_USE_COCOA
+ if (!qt_mac_is_macsheet(widget))
+ QEventDispatcherMacPrivate::rebuildModalSessionStack(true);
+#endif
+ }
+#ifdef DEBUG_MODAL_EVENTS
+ else qDebug("Failure to remove %s::%s::%p -- %p", widget->metaObject()->className(), widget->objectName().toLocal8Bit().constData(), widget, qt_modal_stack);
+#endif
+ app_do_modal = (qt_modal_stack != 0);
+ if (!app_do_modal)
+ qt_event_request_menubarupdate();
+}
+
+#if defined(QT_MAC_USE_COCOA)
+void QApplicationPrivate::_q_runAppModalWindow()
+{
+ if (QEventDispatcherMacPrivate::blockCocoaRequestModal) {
+ // Just postpone the event until the event dispatcher tells
+ // us (by releasing the block) that it is OK to recurse into
+ // a new event loop for our non-execing modal window:
+ qApp->postEvent(qApp, new QEvent(QEvent::CocoaRequestModal));
+ } else {
+ // Recurse into a new event loop for the current app modal window:
+ threadData->eventDispatcher->processEvents(QEventLoop::DialogExec);
+ }
+}
+#endif
+
+QWidget *QApplicationPrivate::tryModalHelper_sys(QWidget *top)
+{
+#ifndef QT_MAC_USE_COCOA
+ if(top && qt_mac_is_macsheet(top) && !IsWindowVisible(qt_mac_window_for(top))) {
+ if(OSWindowRef wp = GetFrontWindowOfClass(kSheetWindowClass, true)) {
+ if(QWidget *sheet = qt_mac_find_window(wp))
+ top = sheet;
+ }
+ }
+#endif
+ return top;
+}
+
+static bool qt_try_modal(QWidget *widget, EventRef event)
+{
+ QWidget * top = 0;
+
+ if (QApplicationPrivate::tryModalHelper(widget, &top))
+ return true;
+
+ // INVARIANT: widget is modally shaddowed within its
+ // window, and should therefore not handle the event.
+ // However, if the window is not active, the event
+ // might suggest that we should bring it to front:
+
+ bool block_event = false;
+
+ if (event) {
+ switch (GetEventClass(event)) {
+ case kEventClassMouse:
+ case kEventClassKeyboard:
+ block_event = true;
+ break;
+ }
+ }
+
+ QWidget *activeWidget = QApplication::activeWindow();
+ if ((!activeWidget || QApplicationPrivate::isBlockedByModal(activeWidget)) &&
+ top->isWindow() && block_event && !QApplicationPrivate::native_modal_dialog_active)
+ top->raise();
+
+#ifdef DEBUG_MODAL_EVENTS
+ qDebug("%s:%d -- final decision! (%s)", __FILE__, __LINE__, block_event ? "false" : "true");
+#endif
+ return !block_event;
+}
+
+OSStatus QApplicationPrivate::tabletProximityCallback(EventHandlerCallRef, EventRef carbonEvent,
+ void *)
+{
+ OSType eventClass = GetEventClass(carbonEvent);
+ UInt32 eventKind = GetEventKind(carbonEvent);
+ if (eventClass != kEventClassTablet || eventKind != kEventTabletProximity)
+ return eventNotHandledErr;
+
+ // Get the current point of the device and its unique ID.
+ ::TabletProximityRec proxRec;
+ GetEventParameter(carbonEvent, kEventParamTabletProximityRec, typeTabletProximityRec, 0,
+ sizeof(proxRec), 0, &proxRec);
+ qt_dispatchTabletProximityEvent(proxRec);
+ return noErr;
+}
+
+OSStatus
+QApplicationPrivate::globalEventProcessor(EventHandlerCallRef er, EventRef event, void *data)
+{
+#ifndef QT_MAC_USE_COCOA
+ QApplication *app = (QApplication *)data;
+ QScopedLoopLevelCounter loopLevelCounter(app->d_func()->threadData);
+ long result;
+ if (app->filterEvent(&event, &result))
+ return result;
+ if(app->macEventFilter(er, event)) //someone else ate it
+ return noErr;
+ QPointer<QWidget> widget;
+
+ /*We assume all events are handled and in
+ the code below we set it to false when we know we didn't handle it, this
+ will let rogue events through (shouldn't really happen, but better safe
+ than sorry) */
+ bool handled_event=true;
+ UInt32 ekind = GetEventKind(event), eclass = GetEventClass(event);
+ switch(eclass)
+ {
+ case kEventClassQt:
+ if(ekind == kEventQtRequestShowSheet) {
+ request_showsheet_pending = 0;
+ QWidget *widget = 0;
+ GetEventParameter(event, kEventParamQWidget, typeQWidget, 0,
+ sizeof(widget), 0, &widget);
+ if(widget) {
+ if (widget->macEvent(er, event))
+ return noErr;
+ WindowPtr window = qt_mac_window_for(widget);
+ bool just_show = !qt_mac_is_macsheet(widget);
+ if(!just_show) {
+ OSStatus err = ShowSheetWindow(window, qt_mac_window_for(widget->parentWidget()));
+ if(err != noErr)
+ qWarning("Qt: QWidget: Unable to show as sheet %s::%s [%ld]", widget->metaObject()->className(),
+ widget->objectName().toLocal8Bit().constData(), long(err));
+ just_show = true;
+ }
+ if(just_show) //at least the window will be visible, but the sheet flag doesn't work sadly (probalby too many sheets)
+ ShowHide(window, true);
+ }
+ } else if(ekind == kEventQtRequestWindowChange) {
+ qt_mac_event_release(request_window_change_pending);
+ } else if(ekind == kEventQtRequestMenubarUpdate) {
+ qt_mac_event_release(request_menubarupdate_pending);
+ QMenuBar::macUpdateMenuBar();
+ } else if(ekind == kEventQtRequestActivate) {
+ qt_mac_event_release(request_activate_pending.event);
+ if(request_activate_pending.widget) {
+ QWidget *tlw = request_activate_pending.widget->window();
+ if (tlw->macEvent(er, event))
+ return noErr;
+ request_activate_pending.widget = 0;
+ tlw->activateWindow();
+ SelectWindow(qt_mac_window_for(tlw));
+ }
+ } else if(ekind == kEventQtRequestContext) {
+ bool send = false;
+ if ((send = (event == request_context_pending)))
+ qt_mac_event_release(request_context_pending);
+ if(send) {
+ //figure out which widget to send it to
+ QPoint where = QCursor::pos();
+ QWidget *widget = 0;
+ GetEventParameter(event, kEventParamQWidget, typeQWidget, 0,
+ sizeof(widget), 0, &widget);
+ if(!widget) {
+ if(qt_button_down)
+ widget = qt_button_down;
+ else
+ widget = QApplication::widgetAt(where.x(), where.y());
+ }
+ if(widget && !isBlockedByModal(widget)) {
+ if (widget->macEvent(er, event))
+ return noErr;
+ QPoint plocal(widget->mapFromGlobal(where));
+ const Qt::KeyboardModifiers keyboardModifiers = qt_mac_get_modifiers(GetCurrentEventKeyModifiers());
+ QContextMenuEvent qme(QContextMenuEvent::Mouse, plocal, where, keyboardModifiers);
+ QApplication::sendEvent(widget, &qme);
+ if(qme.isAccepted()) { //once this happens the events before are pitched
+ qt_button_down = 0;
+ qt_mac_dblclick.last_widget = 0;
+ }
+ } else {
+ handled_event = false;
+ }
+ }
+ } else {
+ handled_event = false;
+ }
+ break;
+ case kEventClassTablet:
+ switch (ekind) {
+ case kEventTabletProximity:
+ // Get the current point of the device and its unique ID.
+ ::TabletProximityRec proxRec;
+ GetEventParameter(event, kEventParamTabletProximityRec, typeTabletProximityRec, 0,
+ sizeof(proxRec), 0, &proxRec);
+ qt_dispatchTabletProximityEvent(proxRec);
+ }
+ break;
+ case kEventClassMouse:
+ {
+ static const int kEventParamQAppSeenMouseEvent = 'QASM';
+ // Check if we've seen the event, if we have we shouldn't process
+ // it again as it may lead to spurious "double events"
+ bool seenEvent;
+ if (GetEventParameter(event, kEventParamQAppSeenMouseEvent,
+ typeBoolean, 0, sizeof(bool), 0, &seenEvent) == noErr) {
+ if (seenEvent)
+ return eventNotHandledErr;
+ }
+ seenEvent = true;
+ SetEventParameter(event, kEventParamQAppSeenMouseEvent, typeBoolean,
+ sizeof(bool), &seenEvent);
+
+ Point where;
+ bool inNonClientArea = false;
+ GetEventParameter(event, kEventParamMouseLocation, typeQDPoint, 0,
+ sizeof(where), 0, &where);
+ if(ekind == kEventMouseMoved && qt_mac_app_fullscreen &&
+ QApplication::desktop()->screenNumber(QPoint(where.h, where.v)) ==
+ QApplication::desktop()->primaryScreen()) {
+ if(where.v <= 0)
+ ShowMenuBar();
+ else if(qt_mac_window_at(where.h, where.v, 0) != inMenuBar)
+ HideMenuBar();
+ }
+
+#if defined(DEBUG_MOUSE_MAPS)
+ const char *edesc = 0;
+ switch(ekind) {
+ case kEventMouseDown: edesc = "MouseButtonPress"; break;
+ case kEventMouseUp: edesc = "MouseButtonRelease"; break;
+ case kEventMouseDragged: case kEventMouseMoved: edesc = "MouseMove"; break;
+ case kEventMouseWheelMoved: edesc = "MouseWheelMove"; break;
+ }
+ if(ekind == kEventMouseDown || ekind == kEventMouseUp)
+ qDebug("Handling mouse: %s", edesc);
+#endif
+ QEvent::Type etype = QEvent::None;
+ Qt::KeyboardModifiers modifiers;
+ {
+ UInt32 mac_modifiers = 0;
+ GetEventParameter(event, kEventParamKeyModifiers, typeUInt32, 0,
+ sizeof(mac_modifiers), 0, &mac_modifiers);
+ modifiers = qt_mac_get_modifiers(mac_modifiers);
+ }
+ Qt::MouseButtons buttons;
+ {
+ UInt32 mac_buttons = 0;
+ GetEventParameter(event, kEventParamMouseChord, typeUInt32, 0,
+ sizeof(mac_buttons), 0, &mac_buttons);
+ buttons = qt_mac_get_buttons(mac_buttons);
+ }
+ int wheel_delta=0;
+ if(ekind == kEventMouseWheelMoved) {
+ int mdelt = 0;
+ GetEventParameter(event, kEventParamMouseWheelDelta, typeSInt32, 0,
+ sizeof(mdelt), 0, &mdelt);
+ wheel_delta = mdelt * 120;
+ }
+
+ Qt::MouseButton button = Qt::NoButton;
+ if(ekind == kEventMouseDown || ekind == kEventMouseUp) {
+ EventMouseButton mac_button = 0;
+ GetEventParameter(event, kEventParamMouseButton, typeMouseButton, 0,
+ sizeof(mac_button), 0, &mac_button);
+ button = qt_mac_get_button(mac_button);
+ }
+
+ switch(ekind) {
+ case kEventMouseDown:
+ etype = QEvent::MouseButtonPress;
+ break;
+ case kEventMouseUp:
+ etype = QEvent::MouseButtonRelease;
+ break;
+ case kEventMouseDragged:
+ case kEventMouseMoved:
+ etype = QEvent::MouseMove;
+ break;
+ }
+
+ const bool inPopupMode = app->d_func()->inPopupMode();
+
+ // A click outside a popup closes the popup. Make sure
+ // that no events are generated for the release part of that click.
+ // (The press goes to the popup and closes it.)
+ if (etype == QEvent::MouseButtonPress) {
+ qt_mac_previous_press_in_popup_mode = inPopupMode;
+ } else if (qt_mac_previous_press_in_popup_mode && !inPopupMode && etype == QEvent::MouseButtonRelease) {
+ qt_mac_previous_press_in_popup_mode = false;
+ handled_event = true;
+#if defined(DEBUG_MOUSE_MAPS)
+ qDebug("Bail out early due to qt_mac_previous_press_in_popup_mode");
+#endif
+ break; // break from case kEventClassMouse
+ }
+
+ //figure out which widget to send it to
+ if(inPopupMode) {
+ QWidget *popup = qApp->activePopupWidget();
+ if (qt_button_down && qt_button_down->window() == popup) {
+ widget = qt_button_down;
+ } else {
+ QPoint pos = popup->mapFromGlobal(QPoint(where.h, where.v));
+ widget = popup->childAt(pos);
+ }
+ if(!widget)
+ widget = popup;
+ } else {
+ if(mac_mouse_grabber) {
+ widget = mac_mouse_grabber;
+ } else if (qt_button_down) {
+ widget = qt_button_down;
+ } else {
+ {
+ WindowPtr window = 0;
+ if(GetEventParameter(event, kEventParamWindowRef, typeWindowRef, 0,
+ sizeof(window), 0, &window) != noErr)
+ FindWindowOfClass(&where, kAllWindowClasses, &window, 0);
+ if(window) {
+ HIViewRef hiview;
+ if(HIViewGetViewForMouseEvent(HIViewGetRoot(window), event, &hiview) == noErr) {
+ widget = QWidget::find((WId)hiview);;
+ if (widget) {
+ // Make sure we didn't pass over a widget with a "fake hole" in it.
+ QWidget *otherWidget = QApplication::widgetAt(where.h, where.v);
+ if (otherWidget && otherWidget->testAttribute(Qt::WA_MouseNoMask))
+ widget = otherWidget;
+ }
+ }
+ }
+ }
+ if(!widget) //fallback
+ widget = QApplication::widgetAt(where.h, where.v);
+ if(ekind == kEventMouseUp && widget) {
+ short part = qt_mac_window_at(where.h, where.v);
+ if(part == inDrag) {
+ UInt32 count = 0;
+ GetEventParameter(event, kEventParamClickCount, typeUInt32, NULL,
+ sizeof(count), NULL, &count);
+ if(count == 2 && qt_mac_collapse_on_dblclick) {
+ if (widget->macEvent(er, event))
+ return noErr;
+ widget->setWindowState(widget->windowState() | Qt::WindowMinimized);
+ //we send a hide to be like X11/Windows
+ QEvent e(QEvent::Hide);
+ QApplication::sendSpontaneousEvent(widget, &e);
+ break;
+ }
+ }
+ }
+ }
+ }
+ if (widget && widget->macEvent(er, event))
+ return noErr;
+ WindowPartCode wpc = qt_mac_window_at(where.h, where.v, 0);
+ if (wpc == inProxyIcon && modifiers == Qt::ControlModifier && buttons != Qt::NoButton) {
+ QIconDragEvent e;
+ QApplication::sendSpontaneousEvent(widget, &e);
+ if (e.isAccepted()) {
+ return noErr; // IconDrag ate it.
+ }
+ }
+ if (inPopupMode == false
+ && (qt_button_down == 0 || qt_button_down_in_content == false)
+ && (wpc != inContent && wpc != inStructure)) {
+ inNonClientArea = true;
+ switch (etype) {
+ case QEvent::MouseButtonPress: {
+ UInt32 count = 0;
+ GetEventParameter(event, kEventParamClickCount, typeUInt32, 0,
+ sizeof(count), 0, &count);
+ if(count % 2 || count == 0) {
+ etype = QEvent::NonClientAreaMouseButtonPress;
+ } else {
+ etype = QEvent::NonClientAreaMouseButtonDblClick;
+ }} break;
+ case QEvent::MouseButtonRelease:
+ etype = QEvent::NonClientAreaMouseButtonRelease;
+ break;
+ case QEvent::MouseMove:
+ if (widget == 0 || widget->hasMouseTracking())
+ etype = QEvent::NonClientAreaMouseMove;
+ break;
+ default:
+ break;
+ }
+ }
+
+ if(qt_mac_find_window((FrontWindow()))) { //set the cursor up
+ QCursor cursor(Qt::ArrowCursor);
+ QWidget *cursor_widget = widget;
+ if(cursor_widget && cursor_widget == qt_button_down && ekind == kEventMouseUp)
+ cursor_widget = QApplication::widgetAt(where.h, where.v);
+ if(cursor_widget) { //only over the app, do we set a cursor..
+ if(!qApp->d_func()->cursor_list.isEmpty()) {
+ cursor = qApp->d_func()->cursor_list.first();
+ } else {
+ for(; cursor_widget; cursor_widget = cursor_widget->parentWidget()) {
+ QWExtra *extra = cursor_widget->d_func()->extraData();
+ if(extra && extra->curs && cursor_widget->isEnabled()) {
+ cursor = *extra->curs;
+ break;
+ }
+ }
+ }
+ }
+ qt_mac_set_cursor(&cursor, QPoint(where.h, where.v));
+ }
+
+ //This mouse button state stuff looks like this on purpose
+ //although it looks hacky it is VERY intentional..
+ if(widget && app_do_modal && !qt_try_modal(widget, event)) {
+ if(ekind == kEventMouseDown && qt_mac_is_macsheet(QApplication::activeModalWidget()))
+ QApplication::activeModalWidget()->parentWidget()->activateWindow(); //sheets have a parent
+ handled_event = false;
+#if defined(DEBUG_MOUSE_MAPS)
+ qDebug("Bail out early due to qt_try_modal");
+#endif
+ break;
+ }
+
+ UInt32 tabletEventType = 0;
+ GetEventParameter(event, kEventParamTabletEventType, typeUInt32, 0,
+ sizeof(tabletEventType), 0, &tabletEventType);
+ if (tabletEventType == kEventTabletPoint) {
+ TabletPointRec tabletPointRec;
+ GetEventParameter(event, kEventParamTabletPointRec, typeTabletPointRec, 0,
+ sizeof(tabletPointRec), 0, &tabletPointRec);
+ QEvent::Type t = QEvent::TabletMove; //default
+ int new_tablet_button_state = tabletPointRec.buttons ? 1 : 0;
+ if (new_tablet_button_state != tablet_button_state)
+ if (new_tablet_button_state)
+ t = QEvent::TabletPress;
+ else
+ t = QEvent::TabletRelease;
+ tablet_button_state = new_tablet_button_state;
+
+ QMacTabletHash *tabletHash = qt_mac_tablet_hash();
+ if (!tabletHash->contains(tabletPointRec.deviceID)) {
+ qWarning("QCocoaView handleTabletEvent: This tablet device is unknown"
+ " (received no proximity event for it). Discarding event.");
+ return false;
+ }
+ QTabletDeviceData &deviceData = tabletHash->operator[](tabletPointRec.deviceID);
+ if (t == QEvent::TabletPress) {
+ deviceData.widgetToGetPress = widget;
+ } else if (t == QEvent::TabletRelease && deviceData.widgetToGetPress) {
+ widget = deviceData.widgetToGetPress;
+ deviceData.widgetToGetPress = 0;
+ }
+
+ if (widget) {
+ int tiltX = ((int)tabletPointRec.tiltX)/(32767/64); // 32K -> 60
+ int tiltY = ((int)tabletPointRec.tiltY)/(-32767/64); // 32K -> 60
+ HIPoint hiPoint;
+ GetEventParameter(event, kEventParamMouseLocation, typeHIPoint, 0, sizeof(HIPoint), 0, &hiPoint);
+ QPointF hiRes(hiPoint.x, hiPoint.y);
+ QPoint global(where.h, where.v);
+
+
+
+ QPoint local(widget->mapFromGlobal(global));
+ int z = 0;
+ qreal rotation = 0.0;
+ qreal tp = 0.0;
+ // Again from the Wacom.h header
+
+ if (deviceData.capabilityMask & 0x0200) // Z-axis
+ z = tabletPointRec.absZ;
+
+ if (deviceData.capabilityMask & 0x0800) // Tangential pressure
+ tp = tabletPointRec.tangentialPressure / 32767.0;
+
+ if (deviceData.capabilityMask & 0x2000) // Rotation
+ rotation = qreal(tabletPointRec.rotation) / 64.0;
+
+ QTabletEvent e(t, local, global, hiRes, deviceData.tabletDeviceType,
+ deviceData.tabletPointerType,
+ qreal(tabletPointRec.pressure / qreal(0xffff)), tiltX, tiltY,
+ tp, rotation, z, modifiers, deviceData.tabletUniqueID);
+ QApplication::sendSpontaneousEvent(widget, &e);
+ if (e.isAccepted()) {
+#if defined(DEBUG_MOUSE_MAPS)
+ qDebug("Bail out early due to table acceptance");
+#endif
+ break;
+ }
+ }
+ }
+
+ if(ekind == kEventMouseDown) {
+ qt_mac_no_click_through_mode = false;
+ const short windowPart = qt_mac_window_at(where.h, where.v, 0);
+ // Menubar almost always wins.
+ if (!inPopupMode && windowPart == inMenuBar) {
+ MenuSelect(where); //allow menu tracking
+ return noErr;
+ }
+
+ if (widget && !(GetCurrentKeyModifiers() & cmdKey)) {
+ extern bool qt_isGenuineQWidget(const QWidget *); // qwidget_mac.cpp
+ QWidget *window = widget->window();
+ bool genuineQtWidget = qt_isGenuineQWidget(widget); // the widget, not the window.
+ window->raise();
+
+ bool needActivate = (window->windowType() != Qt::Desktop)
+ && (window->windowType() != Qt::Popup)
+ && !qt_mac_is_macsheet(window);
+ if (needActivate && (!window->isModal() && qobject_cast<QDockWidget *>(window)))
+ needActivate = false;
+
+ if (genuineQtWidget && needActivate)
+ needActivate = !window->isActiveWindow()
+ || !IsWindowActive(qt_mac_window_for(window));
+
+ if (needActivate) {
+ window->activateWindow();
+ if (!qt_mac_can_clickThrough(widget)) {
+ qt_mac_no_click_through_mode = true;
+ handled_event = false;
+#if defined(DEBUG_MOUSE_MAPS)
+ qDebug("Bail out early due to qt_mac_canClickThrough %s::%s", widget->metaObject()->className(),
+ widget->objectName().toLocal8Bit().constData());
+#endif
+ break;
+ }
+ }
+ }
+
+ if(qt_mac_dblclick.last_widget &&
+ qt_mac_dblclick.last_x != -1 && qt_mac_dblclick.last_y != -1 &&
+ QRect(qt_mac_dblclick.last_x-2, qt_mac_dblclick.last_y-2, 4, 4).contains(QPoint(where.h, where.v))) {
+ if(qt_mac_dblclick.use_qt_time_limit) {
+ EventTime now = GetEventTime(event);
+ if(qt_mac_dblclick.last_time != -2 && qt_mac_dblclick.last_widget == widget &&
+ now - qt_mac_dblclick.last_time <= ((double)QApplicationPrivate::mouse_double_click_time)/1000 &&
+ qt_mac_dblclick.last_button == button)
+ etype = QEvent::MouseButtonDblClick;
+ } else {
+ UInt32 count = 0;
+ GetEventParameter(event, kEventParamClickCount, typeUInt32, 0,
+ sizeof(count), 0, &count);
+ if(!(count % 2) && qt_mac_dblclick.last_modifiers == modifiers &&
+ qt_mac_dblclick.last_widget == widget && qt_mac_dblclick.last_button == button)
+ etype = QEvent::MouseButtonDblClick;
+ }
+ if(etype == QEvent::MouseButtonDblClick)
+ qt_mac_dblclick.last_widget = 0;
+ }
+ if(etype != QEvent::MouseButtonDblClick) {
+ qt_mac_dblclick.last_x = where.h;
+ qt_mac_dblclick.last_y = where.v;
+ } else {
+ qt_mac_dblclick.last_x = qt_mac_dblclick.last_y = -1;
+ }
+ } else if(qt_mac_no_click_through_mode) {
+ if(ekind == kEventMouseUp)
+ qt_mac_no_click_through_mode = false;
+ handled_event = false;
+#if defined(DEBUG_MOUSE_MAPS)
+ qDebug("Bail out early due to qt_mac_no_click_through_mode");
+#endif
+ break;
+ }
+
+ QPointer<QWidget> leaveAfterRelease = 0;
+ switch(ekind) {
+ case kEventMouseUp:
+ if (!buttons) {
+ if (!inPopupMode && !QWidget::mouseGrabber())
+ leaveAfterRelease = qt_button_down;
+ qt_button_down = 0;
+ }
+ break;
+ case kEventMouseDown: {
+ if (!qt_button_down)
+ qt_button_down = widget;
+ WindowPartCode wpc = qt_mac_window_at(where.h, where.v, 0);
+ qt_button_down_in_content = (wpc == inContent || wpc == inStructure);
+ break; }
+ }
+
+ // Check if we should send enter/leave events:
+ switch(ekind) {
+ case kEventMouseDragged:
+ case kEventMouseMoved:
+ case kEventMouseUp:
+ case kEventMouseDown: {
+ // If we are in popup mode, widget will point to the current popup no matter
+ // where the mouse cursor is. In that case find out if the mouse cursor is
+ // really over the popup in order to send correct enter / leave envents.
+ QWidget * const enterLeaveWidget = (inPopupMode || ekind == kEventMouseUp) ?
+ QApplication::widgetAt(where.h, where.v) : static_cast<QWidget*>(widget);
+
+ if ((QWidget *) qt_mouseover != enterLeaveWidget || inNonClientArea) {
+#ifdef DEBUG_MOUSE_MAPS
+ qDebug("Entering: %p - %s (%s), Leaving %s (%s)", (QWidget*)enterLeaveWidget,
+ enterLeaveWidget ? enterLeaveWidget->metaObject()->className() : "none",
+ enterLeaveWidget ? enterLeaveWidget->objectName().toLocal8Bit().constData() : "",
+ qt_mouseover ? qt_mouseover->metaObject()->className() : "none",
+ qt_mouseover ? qt_mouseover->objectName().toLocal8Bit().constData() : "");
+#endif
+
+ QWidget * const mouseGrabber = QWidget::mouseGrabber();
+
+ if (inPopupMode) {
+ QWidget *enter = enterLeaveWidget;
+ QWidget *leave = qt_mouseover;
+ if (mouseGrabber) {
+ QWidget * const popupWidget = qApp->activePopupWidget();
+ if (leave == popupWidget)
+ enter = mouseGrabber;
+ if (enter == popupWidget)
+ leave = mouseGrabber;
+ if ((enter == mouseGrabber && leave == popupWidget)
+ || (leave == mouseGrabber && enter == popupWidget)) {
+ QApplicationPrivate::dispatchEnterLeave(enter, leave);
+ qt_mouseover = enter;
+ }
+ } else {
+ QApplicationPrivate::dispatchEnterLeave(enter, leave);
+ qt_mouseover = enter;
+ }
+ } else if ((!qt_button_down || !qt_mouseover) && !mouseGrabber && !leaveAfterRelease) {
+ QApplicationPrivate::dispatchEnterLeave(enterLeaveWidget, qt_mouseover);
+ qt_mouseover = enterLeaveWidget;
+ }
+ }
+ break; }
+ }
+
+ if(widget) {
+ QPoint p(where.h, where.v);
+ QPoint plocal(widget->mapFromGlobal(p));
+ if(etype == QEvent::MouseButtonPress) {
+ qt_mac_dblclick.last_widget = widget;
+ qt_mac_dblclick.last_modifiers = modifiers;
+ qt_mac_dblclick.last_button = button;
+ qt_mac_dblclick.last_time = GetEventTime(event);
+ }
+ if(wheel_delta) {
+ EventMouseWheelAxis axis;
+ GetEventParameter(event, kEventParamMouseWheelAxis, typeMouseWheelAxis, 0,
+ sizeof(axis), 0, &axis);
+ QWheelEvent qwe(plocal, p, wheel_delta, buttons, modifiers,
+ axis == kEventMouseWheelAxisX ? Qt::Horizontal : Qt::Vertical);
+ QApplication::sendSpontaneousEvent(widget, &qwe);
+ if(!qwe.isAccepted() && QApplicationPrivate::focus_widget && QApplicationPrivate::focus_widget != widget) {
+ QWheelEvent qwe2(QApplicationPrivate::focus_widget->mapFromGlobal(p), p,
+ wheel_delta, buttons, modifiers,
+ axis == kEventMouseWheelAxisX ? Qt::Horizontal : Qt::Vertical);
+ QApplication::sendSpontaneousEvent(QApplicationPrivate::focus_widget, &qwe2);
+ if(!qwe2.isAccepted())
+ handled_event = false;
+ }
+ } else {
+#ifdef QMAC_SPEAK_TO_ME
+ const int speak_keys = Qt::AltModifier | Qt::ShiftModifier;
+ if(etype == QMouseEvent::MouseButtonDblClick && ((modifiers & speak_keys) == speak_keys)) {
+ QVariant v = widget->property("displayText");
+ if(!v.isValid()) v = widget->property("text");
+ if(!v.isValid()) v = widget->property("windowTitle");
+ if(v.isValid()) {
+ QString s = v.toString();
+ s.replace(QRegExp(QString::fromLatin1("(\\&|\\<[^\\>]*\\>)")), QLatin1String(""));
+ SpeechChannel ch;
+ NewSpeechChannel(0, &ch);
+ SpeakText(ch, s.toLatin1().constData(), s.length());
+ DisposeSpeechChannel(ch);
+ }
+ }
+#endif
+ Qt::MouseButton buttonToSend = button;
+ static bool lastButtonTranslated = false;
+ if(ekind == kEventMouseDown &&
+ button == Qt::LeftButton && (modifiers & Qt::MetaModifier)) {
+ buttonToSend = Qt::RightButton;
+ lastButtonTranslated = true;
+ } else if(ekind == kEventMouseUp && lastButtonTranslated) {
+ buttonToSend = Qt::RightButton;
+ lastButtonTranslated = false;
+ }
+ QMouseEvent qme(etype, plocal, p, buttonToSend, buttons, modifiers);
+ QApplication::sendSpontaneousEvent(widget, &qme);
+ if(!qme.isAccepted() || inNonClientArea)
+ handled_event = false;
+ }
+
+ if (leaveAfterRelease) {
+ QWidget *enter = QApplication::widgetAt(where.h, where.v);
+ QApplicationPrivate::dispatchEnterLeave(enter, leaveAfterRelease);
+ qt_mouseover = enter;
+ leaveAfterRelease = 0;
+ }
+
+ if(ekind == kEventMouseDown &&
+ ((button == Qt::RightButton) ||
+ (button == Qt::LeftButton && (modifiers & Qt::MetaModifier))))
+ qt_event_request_context();
+
+#ifdef DEBUG_MOUSE_MAPS
+ const char *event_desc = edesc;
+ if(etype == QEvent::MouseButtonDblClick)
+ event_desc = "Double Click";
+ else if(etype == QEvent::NonClientAreaMouseButtonPress)
+ event_desc = "NonClientMousePress";
+ else if(etype == QEvent::NonClientAreaMouseButtonRelease)
+ event_desc = "NonClientMouseRelease";
+ else if(etype == QEvent::NonClientAreaMouseMove)
+ event_desc = "NonClientMouseMove";
+ else if(etype == QEvent::NonClientAreaMouseButtonDblClick)
+ event_desc = "NonClientMouseDblClick";
+ qDebug("%d %d (%d %d) - Would send (%s) event to %p %s %s (%d 0x%08x 0x%08x %d)", p.x(), p.y(),
+ plocal.x(), plocal.y(), event_desc, (QWidget*)widget,
+ widget ? widget->objectName().toLocal8Bit().constData() : "*Unknown*",
+ widget ? widget->metaObject()->className() : "*Unknown*",
+ button, (int)buttons, (int)modifiers, wheel_delta);
+#endif
+ } else {
+ handled_event = false;
+ }
+ break;
+ }
+ case kEventClassTextInput:
+ case kEventClassKeyboard: {
+ EventRef key_event = event;
+ if(eclass == kEventClassTextInput) {
+ Q_ASSERT(ekind == kEventTextInputUnicodeForKeyEvent);
+ OSStatus err = GetEventParameter(event, kEventParamTextInputSendKeyboardEvent, typeEventRef, 0,
+ sizeof(key_event), 0, &key_event);
+ Q_ASSERT(err == noErr);
+ Q_UNUSED(err);
+ }
+ const UInt32 key_ekind = GetEventKind(key_event);
+ Q_ASSERT(GetEventClass(key_event) == kEventClassKeyboard);
+
+ if(key_ekind == kEventRawKeyDown)
+ qt_keymapper_private()->updateKeyMap(er, key_event, data);
+ if(mac_keyboard_grabber)
+ widget = mac_keyboard_grabber;
+ else if (app->activePopupWidget())
+ widget = (app->activePopupWidget()->focusWidget() ?
+ app->activePopupWidget()->focusWidget() : app->activePopupWidget());
+ else if(QApplication::focusWidget())
+ widget = QApplication::focusWidget();
+ else
+ widget = app->activeWindow();
+
+ if (widget) {
+ if (widget->macEvent(er, event))
+ return noErr;
+ } else {
+ // Darn, I need to update tho modifier state, even though
+ // Qt itself isn't getting them, otherwise the keyboard state get inconsistent.
+ if (key_ekind == kEventRawKeyModifiersChanged) {
+ UInt32 modifiers = 0;
+ GetEventParameter(key_event, kEventParamKeyModifiers, typeUInt32, 0,
+ sizeof(modifiers), 0, &modifiers);
+ extern void qt_mac_send_modifiers_changed(quint32 modifiers, QObject *object); // qkeymapper_mac.cpp
+ // Just send it to the qApp for the time being.
+ qt_mac_send_modifiers_changed(modifiers, qApp);
+ }
+ handled_event = false;
+ break;
+ }
+
+ if(app_do_modal && !qt_try_modal(widget, key_event))
+ break;
+ if (eclass == kEventClassTextInput) {
+ handled_event = false;
+ } else {
+ handled_event = qt_keymapper_private()->translateKeyEvent(widget, er, key_event, data,
+ widget == mac_keyboard_grabber);
+ }
+ break; }
+ case kEventClassWindow: {
+ WindowRef wid = 0;
+ GetEventParameter(event, kEventParamDirectObject, typeWindowRef, 0,
+ sizeof(WindowRef), 0, &wid);
+ widget = qt_mac_find_window(wid);
+ if (widget && widget->macEvent(er, event))
+ return noErr;
+ if(ekind == kEventWindowActivated) {
+ if(QApplicationPrivate::app_style) {
+ QEvent ev(QEvent::Style);
+ QApplication::sendSpontaneousEvent(QApplicationPrivate::app_style, &ev);
+ }
+
+ if(widget && app_do_modal && !qt_try_modal(widget, event))
+ break;
+
+ if(widget && widget->window()->isVisible()) {
+ QWidget *tlw = widget->window();
+ if(tlw->isWindow() && !(tlw->windowType() == Qt::Popup)
+ && !qt_mac_is_macdrawer(tlw)
+ && (!tlw->parentWidget() || tlw->isModal()
+ || !(tlw->windowType() == Qt::Tool))) {
+ bool just_send_event = false;
+ {
+ WindowActivationScope scope;
+ if(GetWindowActivationScope((WindowRef)wid, &scope) == noErr &&
+ scope == kWindowActivationScopeIndependent) {
+ if(GetFrontWindowOfClass(kAllWindowClasses, true) != wid)
+ just_send_event = true;
+ }
+ }
+ if(just_send_event) {
+ QEvent e(QEvent::WindowActivate);
+ QApplication::sendSpontaneousEvent(widget, &e);
+ } else {
+ app->setActiveWindow(tlw);
+ }
+ }
+ QMenuBar::macUpdateMenuBar();
+ }
+ } else if(ekind == kEventWindowDeactivated) {
+ if(widget && QApplicationPrivate::active_window == widget)
+ app->setActiveWindow(0);
+ } else {
+ handled_event = false;
+ }
+ break; }
+ case kEventClassApplication:
+ if(ekind == kEventAppActivated) {
+ if(QApplication::desktopSettingsAware())
+ qt_mac_update_os_settings();
+ if(qt_clipboard) { //manufacture an event so the clipboard can see if it has changed
+ QEvent ev(QEvent::Clipboard);
+ QApplication::sendSpontaneousEvent(qt_clipboard, &ev);
+ }
+ if(app) {
+ QEvent ev(QEvent::ApplicationActivate);
+ QApplication::sendSpontaneousEvent(app, &ev);
+ }
+ if(!app->activeWindow()) {
+ WindowPtr wp = ActiveNonFloatingWindow();
+ if(QWidget *tmp_w = qt_mac_find_window(wp))
+ app->setActiveWindow(tmp_w);
+ }
+ QMenuBar::macUpdateMenuBar();
+ } else if(ekind == kEventAppDeactivated) {
+ //qt_mac_no_click_through_mode = false;
+ while(app->d_func()->inPopupMode())
+ app->activePopupWidget()->close();
+ if(app) {
+ QEvent ev(QEvent::ApplicationDeactivate);
+ QApplication::sendSpontaneousEvent(app, &ev);
+ }
+ app->setActiveWindow(0);
+ } else if(ekind == kEventAppAvailableWindowBoundsChanged) {
+ QDesktopWidgetImplementation::instance()->onResize();
+ } else {
+ handled_event = false;
+ }
+ break;
+ case kAppearanceEventClass:
+ if(ekind == kAEAppearanceChanged) {
+ if(QApplication::desktopSettingsAware())
+ qt_mac_update_os_settings();
+ if(QApplicationPrivate::app_style) {
+ QEvent ev(QEvent::Style);
+ QApplication::sendSpontaneousEvent(QApplicationPrivate::app_style, &ev);
+ }
+ } else {
+ handled_event = false;
+ }
+ break;
+ case kEventClassAppleEvent:
+ if(ekind == kEventAppleEvent) {
+ EventRecord erec;
+ if(!ConvertEventRefToEventRecord(event, &erec))
+ qDebug("Qt: internal: WH0A, unexpected condition reached. %s:%d", __FILE__, __LINE__);
+ else if(AEProcessAppleEvent(&erec) != noErr)
+ handled_event = false;
+ } else {
+ handled_event = false;
+ }
+ break;
+ case kEventClassCommand:
+ if(ekind == kEventCommandProcess) {
+ HICommand cmd;
+ GetEventParameter(event, kEventParamDirectObject, typeHICommand,
+ 0, sizeof(cmd), 0, &cmd);
+ handled_event = false;
+ if(!cmd.menu.menuRef && GetApplicationDockTileMenu()) {
+ EventRef copy = CopyEvent(event);
+ HICommand copy_cmd;
+ GetEventParameter(event, kEventParamDirectObject, typeHICommand,
+ 0, sizeof(copy_cmd), 0, &copy_cmd);
+ copy_cmd.menu.menuRef = GetApplicationDockTileMenu();
+ SetEventParameter(copy, kEventParamDirectObject, typeHICommand, sizeof(copy_cmd), &copy_cmd);
+ if(SendEventToMenu(copy, copy_cmd.menu.menuRef) == noErr)
+ handled_event = true;
+ }
+ if(!handled_event) {
+ if(cmd.commandID == kHICommandQuit) {
+ handled_event = true;
+ HiliteMenu(0);
+ bool handle_quit = true;
+ if(QApplicationPrivate::modalState()) {
+ int visible = 0;
+ const QWidgetList tlws = QApplication::topLevelWidgets();
+ for(int i = 0; i < tlws.size(); ++i) {
+ if(tlws.at(i)->isVisible())
+ ++visible;
+ }
+ handle_quit = (visible <= 1);
+ }
+ if(handle_quit) {
+ QCloseEvent ev;
+ QApplication::sendSpontaneousEvent(app, &ev);
+ if(ev.isAccepted())
+ app->quit();
+ } else {
+ QApplication::beep();
+ }
+ } else if(cmd.commandID == kHICommandSelectWindow) {
+ if((GetCurrentKeyModifiers() & cmdKey))
+ handled_event = true;
+ } else if(cmd.commandID == kHICommandAbout) {
+ QMessageBox::aboutQt(0);
+ HiliteMenu(0);
+ handled_event = true;
+ }
+ }
+ }
+ break;
+ }
+
+#ifdef DEBUG_EVENTS
+ qDebug("%shandled event %c%c%c%c %d", handled_event ? "(*) " : "",
+ char(eclass >> 24), char((eclass >> 16) & 255), char((eclass >> 8) & 255),
+ char(eclass & 255), (int)ekind);
+#endif
+ if(!handled_event) //let the event go through
+ return eventNotHandledErr;
+ return noErr; //we eat the event
+#else
+ Q_UNUSED(er);
+ Q_UNUSED(event);
+ Q_UNUSED(data);
+ return eventNotHandledErr;
+#endif
+}
+
+OSStatus QApplicationPrivate::globalAppleEventProcessor(const AppleEvent *ae, AppleEvent *, long handlerRefcon)
+{
+ QApplication *app = (QApplication *)handlerRefcon;
+ bool handled_event=false;
+ OSType aeID=typeWildCard, aeClass=typeWildCard;
+ AEGetAttributePtr(ae, keyEventClassAttr, typeType, 0, &aeClass, sizeof(aeClass), 0);
+ AEGetAttributePtr(ae, keyEventIDAttr, typeType, 0, &aeID, sizeof(aeID), 0);
+ if(aeClass == kCoreEventClass) {
+ switch(aeID) {
+ case kAEQuitApplication: {
+ extern bool qt_mac_quit_menu_item_enabled; // qmenu_mac.cpp
+ if(!QApplicationPrivate::modalState() && qt_mac_quit_menu_item_enabled) {
+ QCloseEvent ev;
+ QApplication::sendSpontaneousEvent(app, &ev);
+ if(ev.isAccepted()) {
+ handled_event = true;
+ app->quit();
+ }
+ } else {
+ QApplication::beep(); // Sorry, you can't quit right now.
+ }
+ break; }
+ case kAEOpenDocuments: {
+ AEDescList docs;
+ if(AEGetParamDesc(ae, keyDirectObject, typeAEList, &docs) == noErr) {
+ long cnt = 0;
+ AECountItems(&docs, &cnt);
+ UInt8 *str_buffer = NULL;
+ for(int i = 0; i < cnt; i++) {
+ FSRef ref;
+ if(AEGetNthPtr(&docs, i+1, typeFSRef, 0, 0, &ref, sizeof(ref), 0) != noErr)
+ continue;
+ if(!str_buffer)
+ str_buffer = (UInt8 *)malloc(1024);
+ FSRefMakePath(&ref, str_buffer, 1024);
+ QFileOpenEvent ev(QString::fromUtf8((const char *)str_buffer));
+ QApplication::sendSpontaneousEvent(app, &ev);
+ }
+ if(str_buffer)
+ free(str_buffer);
+ }
+ break; }
+ default:
+ break;
+ }
+ }
+#ifdef DEBUG_EVENTS
+ qDebug("Qt: internal: %shandled Apple event! %c%c%c%c %c%c%c%c", handled_event ? "(*)" : "",
+ char(aeID >> 24), char((aeID >> 16) & 255), char((aeID >> 8) & 255),char(aeID & 255),
+ char(aeClass >> 24), char((aeClass >> 16) & 255), char((aeClass >> 8) & 255),char(aeClass & 255));
+#else
+ if(!handled_event) //let the event go through
+ return eventNotHandledErr;
+ return noErr; //we eat the event
+#endif
+}
+
+/*!
+ \fn bool QApplication::macEventFilter(EventHandlerCallRef caller, EventRef event)
+
+ \warning This virtual function is only implemented under Mac OS X when against Carbon.
+
+ If you create an application that inherits QApplication and reimplement
+ this function, you get direct access to all Carbon Events that Qt registers
+ for from Mac OS X with this function being called with the \a caller and
+ the \a event.
+
+ Return true if you want to stop the event from being processed.
+ Return false for normal event dispatching. The default
+ implementation returns false.
+
+ Cocoa uses a different event system which means this function is NOT CALLED
+ when building Qt against Cocoa. If you want similar functionality subclass
+ NSApplication and reimplement the sendEvent: message to handle all the
+ NSEvents. You also will need to to instantiate your custom NSApplication
+ before creating a QApplication. See \l
+ {http://developer.apple.com/documentation/Cocoa/Reference/ApplicationKit/Classes/NSApplication_Class/Reference/Reference.html}{Apple's
+ NSApplication Reference} for more information.
+
+*/
+bool QApplication::macEventFilter(EventHandlerCallRef, EventRef)
+{
+ return false;
+}
+
+/*!
+ \internal
+*/
+void QApplicationPrivate::openPopup(QWidget *popup)
+{
+ if (!QApplicationPrivate::popupWidgets) // create list
+ QApplicationPrivate::popupWidgets = new QWidgetList;
+ QApplicationPrivate::popupWidgets->append(popup); // add to end of list
+
+ // popups are not focus-handled by the window system (the first
+ // popup grabbed the keyboard), so we have to do that manually: A
+ // new popup gets the focus
+ if (popup->focusWidget()) {
+ popup->focusWidget()->setFocus(Qt::PopupFocusReason);
+ } else if (QApplicationPrivate::popupWidgets->count() == 1) { // this was the first popup
+ popup->setFocus(Qt::PopupFocusReason);
+ }
+}
+
+/*!
+ \internal
+*/
+void QApplicationPrivate::closePopup(QWidget *popup)
+{
+ Q_Q(QApplication);
+ if (!QApplicationPrivate::popupWidgets)
+ return;
+
+ QApplicationPrivate::popupWidgets->removeAll(popup);
+ if (popup == qt_button_down)
+ qt_button_down = 0;
+ if (QApplicationPrivate::popupWidgets->isEmpty()) { // this was the last popup
+ delete QApplicationPrivate::popupWidgets;
+ QApplicationPrivate::popupWidgets = 0;
+
+ // Special case for Tool windows: since they are activated and deactived together
+ // with a normal window they never become the QApplicationPrivate::active_window.
+ QWidget *appFocusWidget = QApplication::focusWidget();
+ if (appFocusWidget && appFocusWidget->window()->windowType() == Qt::Tool) {
+ appFocusWidget->setFocus(Qt::PopupFocusReason);
+ } else if (QApplicationPrivate::active_window) {
+ if (QWidget *fw = QApplicationPrivate::active_window->focusWidget()) {
+ if (fw != QApplication::focusWidget()) {
+ fw->setFocus(Qt::PopupFocusReason);
+ } else {
+ QFocusEvent e(QEvent::FocusIn, Qt::PopupFocusReason);
+ q->sendEvent(fw, &e);
+ }
+ }
+ }
+ } else {
+ // popups are not focus-handled by the window system (the
+ // first popup grabbed the keyboard), so we have to do that
+ // manually: A popup was closed, so the previous popup gets
+ // the focus.
+ QWidget* aw = QApplicationPrivate::popupWidgets->last();
+ if (QWidget *fw = aw->focusWidget())
+ fw->setFocus(Qt::PopupFocusReason);
+ }
+}
+
+void QApplication::beep()
+{
+ qt_mac_beep();
+}
+
+void QApplication::alert(QWidget *widget, int duration)
+{
+ if (!QApplicationPrivate::checkInstance("alert"))
+ return;
+
+ QWidgetList windowsToMark;
+ if (!widget)
+ windowsToMark += topLevelWidgets();
+ else
+ windowsToMark.append(widget->window());
+
+ bool needNotification = false;
+ for (int i = 0; i < windowsToMark.size(); ++i) {
+ QWidget *window = windowsToMark.at(i);
+ if (!window->isActiveWindow() && window->isVisible()) {
+ needNotification = true; // yeah, we may set it multiple times, but that's OK.
+ if (duration != 0) {
+ QTimer *timer = new QTimer(qApp);
+ timer->setSingleShot(true);
+ connect(timer, SIGNAL(timeout()), qApp, SLOT(_q_alertTimeOut()));
+ if (QTimer *oldTimer = qApp->d_func()->alertTimerHash.value(widget)) {
+ qApp->d_func()->alertTimerHash.remove(widget);
+ delete oldTimer;
+ }
+ qApp->d_func()->alertTimerHash.insert(widget, timer);
+ timer->start(duration);
+ }
+ }
+ }
+ if (needNotification)
+ qt_mac_send_notification();
+}
+
+void QApplicationPrivate::_q_alertTimeOut()
+{
+ if (QTimer *timer = qobject_cast<QTimer *>(q_func()->sender())) {
+ QHash<QWidget *, QTimer *>::iterator it = alertTimerHash.begin();
+ while (it != alertTimerHash.end()) {
+ if (it.value() == timer) {
+ alertTimerHash.erase(it);
+ timer->deleteLater();
+ break;
+ }
+ ++it;
+ }
+ if (alertTimerHash.isEmpty()) {
+ qt_mac_cancel_notification();
+ }
+ }
+}
+
+void QApplication::setCursorFlashTime(int msecs)
+{
+ QApplicationPrivate::cursor_flash_time = msecs;
+}
+
+int QApplication::cursorFlashTime()
+{
+ return QApplicationPrivate::cursor_flash_time;
+}
+
+void QApplication::setDoubleClickInterval(int ms)
+{
+ qt_mac_dblclick.use_qt_time_limit = true;
+ QApplicationPrivate::mouse_double_click_time = ms;
+}
+
+int QApplication::doubleClickInterval()
+{
+ if (!qt_mac_dblclick.use_qt_time_limit) { //get it from the system
+ QSettings appleSettings(QLatin1String("apple.com"));
+ /* First worked as of 10.3.3 */
+ double dci = appleSettings.value(QLatin1String("com/apple/mouse/doubleClickThreshold"), 0.5).toDouble();
+ return int(dci * 1000);
+ }
+ return QApplicationPrivate::mouse_double_click_time;
+}
+
+void QApplication::setKeyboardInputInterval(int ms)
+{
+ QApplicationPrivate::keyboard_input_time = ms;
+}
+
+int QApplication::keyboardInputInterval()
+{
+ // FIXME: get from the system
+ return QApplicationPrivate::keyboard_input_time;
+}
+
+void QApplication::setWheelScrollLines(int n)
+{
+ QApplicationPrivate::wheel_scroll_lines = n;
+}
+
+int QApplication::wheelScrollLines()
+{
+ return QApplicationPrivate::wheel_scroll_lines;
+}
+
+void QApplication::setEffectEnabled(Qt::UIEffect effect, bool enable)
+{
+ switch (effect) {
+ case Qt::UI_FadeMenu:
+ QApplicationPrivate::fade_menu = enable;
+ break;
+ case Qt::UI_AnimateMenu:
+ QApplicationPrivate::animate_menu = enable;
+ break;
+ case Qt::UI_FadeTooltip:
+ QApplicationPrivate::fade_tooltip = enable;
+ break;
+ case Qt::UI_AnimateTooltip:
+ QApplicationPrivate::animate_tooltip = enable;
+ break;
+ case Qt::UI_AnimateCombo:
+ QApplicationPrivate::animate_combo = enable;
+ break;
+ case Qt::UI_AnimateToolBox:
+ QApplicationPrivate::animate_toolbox = enable;
+ break;
+ case Qt::UI_General:
+ QApplicationPrivate::fade_tooltip = true;
+ break;
+ default:
+ QApplicationPrivate::animate_ui = enable;
+ break;
+ }
+
+ if (enable)
+ QApplicationPrivate::animate_ui = true;
+}
+
+bool QApplication::isEffectEnabled(Qt::UIEffect effect)
+{
+ if (QColormap::instance().depth() < 16 || !QApplicationPrivate::animate_ui)
+ return false;
+
+ switch(effect) {
+ case Qt::UI_AnimateMenu:
+ return QApplicationPrivate::animate_menu;
+ case Qt::UI_FadeMenu:
+ return QApplicationPrivate::fade_menu;
+ case Qt::UI_AnimateCombo:
+ return QApplicationPrivate::animate_combo;
+ case Qt::UI_AnimateTooltip:
+ return QApplicationPrivate::animate_tooltip;
+ case Qt::UI_FadeTooltip:
+ return QApplicationPrivate::fade_tooltip;
+ case Qt::UI_AnimateToolBox:
+ return QApplicationPrivate::animate_toolbox;
+ default:
+ break;
+ }
+ return QApplicationPrivate::animate_ui;
+}
+
+/*!
+ \internal
+*/
+bool QApplicationPrivate::qt_mac_apply_settings()
+{
+ QSettings settings(QSettings::UserScope, QLatin1String("Trolltech"));
+ settings.beginGroup(QLatin1String("Qt"));
+
+ /*
+ Qt settings. This is how they are written into the datastream.
+ Palette/ * - QPalette
+ font - QFont
+ libraryPath - QStringList
+ style - QString
+ doubleClickInterval - int
+ cursorFlashTime - int
+ wheelScrollLines - int
+ colorSpec - QString
+ defaultCodec - QString
+ globalStrut/width - int
+ globalStrut/height - int
+ GUIEffects - QStringList
+ Font Substitutions/ * - QStringList
+ Font Substitutions/... - QStringList
+ */
+
+ // read library (ie. plugin) path list
+ QString libpathkey =
+ QString::fromLatin1("%1.%2/libraryPath")
+ .arg(QT_VERSION >> 16)
+ .arg((QT_VERSION & 0xff00) >> 8);
+ QStringList pathlist = settings.value(libpathkey).toString().split(QLatin1Char(':'));
+ if (!pathlist.isEmpty()) {
+ QStringList::ConstIterator it = pathlist.begin();
+ while(it != pathlist.end())
+ QApplication::addLibraryPath(*it++);
+ }
+
+ QString defaultcodec = settings.value(QLatin1String("defaultCodec"), QVariant(QLatin1String("none"))).toString();
+ if (defaultcodec != QLatin1String("none")) {
+ QTextCodec *codec = QTextCodec::codecForName(defaultcodec.toLatin1().constData());
+ if (codec)
+ QTextCodec::setCodecForTr(codec);
+ }
+
+ if (qt_is_gui_used) {
+ QString str;
+ QStringList strlist;
+ int num;
+
+ // read new palette
+ int i;
+ QPalette pal(QApplication::palette());
+ strlist = settings.value(QLatin1String("Palette/active")).toStringList();
+ if (strlist.count() == QPalette::NColorRoles) {
+ for (i = 0; i < QPalette::NColorRoles; i++)
+ pal.setColor(QPalette::Active, (QPalette::ColorRole) i,
+ QColor(strlist[i]));
+ }
+ strlist = settings.value(QLatin1String("Palette/inactive")).toStringList();
+ if (strlist.count() == QPalette::NColorRoles) {
+ for (i = 0; i < QPalette::NColorRoles; i++)
+ pal.setColor(QPalette::Inactive, (QPalette::ColorRole) i,
+ QColor(strlist[i]));
+ }
+ strlist = settings.value(QLatin1String("Palette/disabled")).toStringList();
+ if (strlist.count() == QPalette::NColorRoles) {
+ for (i = 0; i < QPalette::NColorRoles; i++)
+ pal.setColor(QPalette::Disabled, (QPalette::ColorRole) i,
+ QColor(strlist[i]));
+ }
+
+ if (pal != QApplication::palette())
+ QApplication::setPalette(pal);
+
+ // read new font
+ QFont font(QApplication::font());
+ str = settings.value(QLatin1String("font")).toString();
+ if (!str.isEmpty()) {
+ font.fromString(str);
+ if (font != QApplication::font())
+ QApplication::setFont(font);
+ }
+
+ // read new QStyle
+ QString stylename = settings.value(QLatin1String("style")).toString();
+ if (! stylename.isNull() && ! stylename.isEmpty()) {
+ QStyle *style = QStyleFactory::create(stylename);
+ if (style)
+ QApplication::setStyle(style);
+ else
+ stylename = QLatin1String("default");
+ } else {
+ stylename = QLatin1String("default");
+ }
+
+ num = settings.value(QLatin1String("doubleClickInterval"),
+ QApplication::doubleClickInterval()).toInt();
+ QApplication::setDoubleClickInterval(num);
+
+ num = settings.value(QLatin1String("cursorFlashTime"),
+ QApplication::cursorFlashTime()).toInt();
+ QApplication::setCursorFlashTime(num);
+
+ num = settings.value(QLatin1String("wheelScrollLines"),
+ QApplication::wheelScrollLines()).toInt();
+ QApplication::setWheelScrollLines(num);
+
+ QString colorspec = settings.value(QLatin1String("colorSpec"),
+ QVariant(QLatin1String("default"))).toString();
+ if (colorspec == QLatin1String("normal"))
+ QApplication::setColorSpec(QApplication::NormalColor);
+ else if (colorspec == QLatin1String("custom"))
+ QApplication::setColorSpec(QApplication::CustomColor);
+ else if (colorspec == QLatin1String("many"))
+ QApplication::setColorSpec(QApplication::ManyColor);
+ else if (colorspec != QLatin1String("default"))
+ colorspec = QLatin1String("default");
+
+ int w = settings.value(QLatin1String("globalStrut/width")).toInt();
+ int h = settings.value(QLatin1String("globalStrut/height")).toInt();
+ QSize strut(w, h);
+ if (strut.isValid())
+ QApplication::setGlobalStrut(strut);
+
+ QStringList effects = settings.value(QLatin1String("GUIEffects")).toStringList();
+ if (!effects.isEmpty()) {
+ if (effects.contains(QLatin1String("none")))
+ QApplication::setEffectEnabled(Qt::UI_General, false);
+ if (effects.contains(QLatin1String("general")))
+ QApplication::setEffectEnabled(Qt::UI_General, true);
+ if (effects.contains(QLatin1String("animatemenu")))
+ QApplication::setEffectEnabled(Qt::UI_AnimateMenu, true);
+ if (effects.contains(QLatin1String("fademenu")))
+ QApplication::setEffectEnabled(Qt::UI_FadeMenu, true);
+ if (effects.contains(QLatin1String("animatecombo")))
+ QApplication::setEffectEnabled(Qt::UI_AnimateCombo, true);
+ if (effects.contains(QLatin1String("animatetooltip")))
+ QApplication::setEffectEnabled(Qt::UI_AnimateTooltip, true);
+ if (effects.contains(QLatin1String("fadetooltip")))
+ QApplication::setEffectEnabled(Qt::UI_FadeTooltip, true);
+ if (effects.contains(QLatin1String("animatetoolbox")))
+ QApplication::setEffectEnabled(Qt::UI_AnimateToolBox, true);
+ } else {
+ QApplication::setEffectEnabled(Qt::UI_General, true);
+ }
+
+ settings.beginGroup(QLatin1String("Font Substitutions"));
+ QStringList fontsubs = settings.childKeys();
+ if (!fontsubs.isEmpty()) {
+ QStringList::Iterator it = fontsubs.begin();
+ for (; it != fontsubs.end(); ++it) {
+ QString fam = QString::fromLatin1((*it).toLatin1().constData());
+ QStringList subs = settings.value(fam).toStringList();
+ QFont::insertSubstitutions(fam, subs);
+ }
+ }
+ settings.endGroup();
+ }
+
+ settings.endGroup();
+ return true;
+}
+
+// DRSWAT
+
+bool QApplicationPrivate::canQuit()
+{
+#ifndef QT_MAC_USE_COCOA
+ return true;
+#else
+ Q_Q(QApplication);
+#ifdef QT_MAC_USE_COCOA
+ [[NSApp mainMenu] cancelTracking];
+#else
+ HiliteMenu(0);
+#endif
+
+ bool handle_quit = true;
+ if (QApplicationPrivate::modalState() && [[[[QT_MANGLE_NAMESPACE(QCocoaApplicationDelegate) sharedDelegate]
+ menuLoader] quitMenuItem] isEnabled]) {
+ int visible = 0;
+ const QWidgetList tlws = QApplication::topLevelWidgets();
+ for(int i = 0; i < tlws.size(); ++i) {
+ if (tlws.at(i)->isVisible())
+ ++visible;
+ }
+ handle_quit = (visible <= 1);
+ }
+ if (handle_quit) {
+ QCloseEvent ev;
+ QApplication::sendSpontaneousEvent(q, &ev);
+ if (ev.isAccepted()) {
+ return true;
+ }
+ }
+ return false;
+#endif
+}
+
+void onApplicationWindowChangedActivation( QWidget*widget, bool activated )
+{
+#if QT_MAC_USE_COCOA
+ QApplication *app = qApp;
+
+ if ( activated )
+ {
+ if (QApplicationPrivate::app_style)
+ {
+ QEvent ev(QEvent::Style);
+ qt_sendSpontaneousEvent(QApplicationPrivate::app_style, &ev);
+ }
+
+ if (widget && app_do_modal && !qt_try_modal(widget, NULL))
+ return;
+
+ if (widget && widget->window()->isVisible())
+ {
+ QWidget *tlw = widget->window();
+
+ if (tlw->isWindow() && !(tlw->windowType() == Qt::Popup)
+ && !qt_mac_is_macdrawer(tlw)
+ && (!tlw->parentWidget() || tlw->isModal() || !(tlw->windowType() == Qt::Tool))) {
+ bool just_send_event = false;
+#if 0
+ WindowActivationScope scope;
+ if ( GetWindowActivationScope((OSWindowRef)wid, &scope) == noErr &&
+ scope == kWindowActivationScopeIndependent)
+ {
+ if ( GetFrontWindowOfClass(kAllWindowClasses, true) != wid )
+ just_send_event = true;
+ }
+#endif
+ if (just_send_event) {
+ QEvent e(QEvent::WindowActivate);
+ qt_sendSpontaneousEvent(widget, &e);
+ } else {
+ app->setActiveWindow(tlw);
+ }
+ }
+ }
+ } else { // deactivated
+ if (widget && QApplicationPrivate::active_window == widget)
+ app->setActiveWindow(0);
+ }
+ QMenuBar::macUpdateMenuBar();
+#else
+ Q_UNUSED(widget);
+ Q_UNUSED(activated);
+#endif
+}
+
+
+void onApplicationChangedActivation( bool activated )
+{
+#if QT_MAC_USE_COCOA
+ QApplication *app = qApp;
+
+//NSLog(@"App Changed Activation\n");
+
+ if ( activated ) {
+ if (QApplication::desktopSettingsAware())
+ qt_mac_update_os_settings();
+
+ if (qt_clipboard) { //manufacture an event so the clipboard can see if it has changed
+ QEvent ev(QEvent::Clipboard);
+ qt_sendSpontaneousEvent(qt_clipboard, &ev);
+ }
+
+ if (app) {
+ QEvent ev(QEvent::ApplicationActivate);
+ qt_sendSpontaneousEvent(app, &ev);
+ }
+
+ if (!app->activeWindow()) {
+#if QT_MAC_USE_COCOA
+ OSWindowRef wp = [NSApp keyWindow];
+#else
+ OSWindowRef wp = ActiveNonFloatingWindow();
+#endif
+ if (QWidget *tmp_w = qt_mac_find_window(wp))
+ app->setActiveWindow(tmp_w);
+ }
+ QMenuBar::macUpdateMenuBar();
+ } else { // de-activated
+ QApplicationPrivate *priv = [[QT_MANGLE_NAMESPACE(QCocoaApplicationDelegate) sharedDelegate] qAppPrivate];
+ while (priv->inPopupMode())
+ app->activePopupWidget()->close();
+ if (app) {
+ QEvent ev(QEvent::ApplicationDeactivate);
+ qt_sendSpontaneousEvent(app, &ev);
+ }
+ app->setActiveWindow(0);
+ }
+#else
+ Q_UNUSED(activated);
+#endif
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/kernel/qapplication_p.h b/src/gui/kernel/qapplication_p.h
new file mode 100644
index 0000000000..7487f0a919
--- /dev/null
+++ b/src/gui/kernel/qapplication_p.h
@@ -0,0 +1,438 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QAPPLICATION_P_H
+#define QAPPLICATION_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of qapplication_*.cpp, qwidget*.cpp, qcolor_x11.cpp, qfiledialog.cpp
+// and many other. This header file may change from version to version
+// without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "QtGui/qapplication.h"
+#include "QtGui/qfont.h"
+#include "QtGui/qcursor.h"
+#include "QtGui/qregion.h"
+#include "QtCore/qmutex.h"
+#include "QtCore/qtranslator.h"
+#include "QtCore/qbasictimer.h"
+#include "QtCore/qhash.h"
+#include "QtCore/qpointer.h"
+#include "private/qcoreapplication_p.h"
+#include "private/qshortcutmap_p.h"
+#include <private/qthread_p.h>
+#ifdef Q_WS_QWS
+#include "QtGui/qscreen_qws.h"
+#include <private/qgraphicssystem_qws_p.h>
+#endif
+
+QT_BEGIN_NAMESPACE
+
+class QClipboard;
+class QGraphicsScene;
+class QGraphicsSystem;
+class QInputContext;
+class QKeyEvent;
+class QMouseEvent;
+class QObject;
+class QWheelEvent;
+class QWidget;
+
+extern bool qt_is_gui_used;
+#ifndef QT_NO_CLIPBOARD
+extern QClipboard *qt_clipboard;
+#endif
+
+#if defined (Q_OS_WIN32) || defined (Q_OS_CYGWIN) || defined(Q_OS_WINCE)
+extern QSysInfo::WinVersion qt_winver;
+enum { QT_TABLET_NPACKETQSIZE = 128 };
+# ifdef Q_OS_WINCE
+ extern DWORD qt_cever;
+# endif
+#elif defined (Q_OS_MAC)
+extern QSysInfo::MacVersion qt_macver;
+#endif
+#if defined(Q_WS_QWS)
+class QWSManager;
+class QDirectPainter;
+#endif
+
+#ifndef QT_NO_TABLET
+struct QTabletDeviceData
+{
+#ifndef Q_WS_MAC
+ int minPressure;
+ int maxPressure;
+ int minTanPressure;
+ int maxTanPressure;
+ int minX, maxX, minY, maxY, minZ, maxZ;
+ inline QPointF scaleCoord(int coordX, int coordY, int outOriginX, int outExtentX,
+ int outOriginY, int outExtentY) const;
+#endif
+
+#if defined(Q_WS_X11) || (defined(Q_WS_MAC) && !defined(QT_MAC_USE_COCOA))
+ QPointer<QWidget> widgetToGetPress;
+#endif
+
+#ifdef Q_WS_X11
+ int deviceType;
+ enum {
+ TOTAL_XINPUT_EVENTS = 64
+ };
+ void *device;
+ int eventCount;
+ long unsigned int eventList[TOTAL_XINPUT_EVENTS]; // XEventClass is in fact a long unsigned int
+
+ int xinput_motion;
+ int xinput_key_press;
+ int xinput_key_release;
+ int xinput_button_press;
+ int xinput_button_release;
+ int xinput_proximity_in;
+ int xinput_proximity_out;
+#elif defined(Q_WS_WIN)
+ qint64 llId;
+ int currentDevice;
+ int currentPointerType;
+#elif defined(Q_WS_MAC)
+ quint64 tabletUniqueID;
+ int tabletDeviceType;
+ int tabletPointerType;
+ int capabilityMask;
+#endif
+};
+
+static inline int sign(int x)
+{
+ return x >= 0 ? 1 : -1;
+}
+
+#ifndef Q_WS_MAC
+inline QPointF QTabletDeviceData::scaleCoord(int coordX, int coordY,
+ int outOriginX, int outExtentX,
+ int outOriginY, int outExtentY) const
+{
+ QPointF ret;
+ if (sign(outExtentX) == sign(maxX))
+ ret.setX(((coordX - minX) * qAbs(outExtentX) / qAbs(qreal(maxX))) + outOriginX);
+ else
+ ret.setX(((qAbs(maxX) - (coordX - minX)) * qAbs(outExtentX) / qAbs(qreal(maxX)))
+ + outOriginX);
+
+ if (sign(outExtentY) == sign(maxY))
+ ret.setY(((coordY - minY) * qAbs(outExtentY) / qAbs(qreal(maxY))) + outOriginY);
+ else
+ ret.setY(((qAbs(maxY) - (coordY - minY)) * qAbs(outExtentY) / qAbs(qreal(maxY)))
+ + outOriginY);
+ return ret;
+}
+#endif
+
+typedef QList<QTabletDeviceData> QTabletDeviceDataList;
+QTabletDeviceDataList *qt_tablet_devices();
+# if defined(Q_WS_MAC)
+typedef QHash<int, QTabletDeviceData> QMacTabletHash;
+QMacTabletHash *qt_mac_tablet_hash();
+# endif
+#endif
+
+#ifdef QT3_SUPPORT
+extern "C" {
+ typedef bool (*Ptrqt_tryAccelEvent)(QWidget *w, QKeyEvent *e);
+ typedef bool (*Ptrqt_tryComposeUnicode)(QWidget *w, QKeyEvent *e);
+ typedef bool (*Ptrqt_dispatchAccelEvent)(QWidget *w, QKeyEvent *e);
+}
+#endif
+
+class QScopedLoopLevelCounter
+{
+ QThreadData *threadData;
+public:
+ QScopedLoopLevelCounter(QThreadData *threadData)
+ : threadData(threadData)
+ { ++threadData->loopLevel; }
+ ~QScopedLoopLevelCounter()
+ { --threadData->loopLevel; }
+};
+
+class Q_GUI_EXPORT QApplicationPrivate : public QCoreApplicationPrivate
+{
+ Q_DECLARE_PUBLIC(QApplication)
+public:
+ QApplicationPrivate(int &argc, char **argv, QApplication::Type type);
+ ~QApplicationPrivate();
+
+#if defined(Q_WS_X11)
+#ifndef QT_NO_SETTINGS
+ static QString kdeHome();
+ static bool x11_apply_settings();
+#endif
+ static void reset_instance_pointer();
+#elif defined(Q_WS_QWS)
+ static bool qws_apply_settings();
+ static QWidget *findWidget(const QObjectList&, const QPoint &, bool rec);
+#endif
+ static bool quitOnLastWindowClosed;
+ static void emitLastWindowClosed();
+#ifdef Q_OS_WINCE
+ static int autoMaximizeThreshold;
+ static bool autoSipEnabled;
+#endif
+
+ static QGraphicsSystem *graphicsSystem()
+#if !defined(Q_WS_QWS)
+ { return graphics_system; }
+#else
+ { return QScreen::instance()->graphicsSystem(); }
+#endif
+
+ void createEventDispatcher();
+ QString appName() const;
+
+ static void dispatchEnterLeave(QWidget *enter, QWidget *leave);
+
+ //modality
+ static void enterModal(QWidget*);
+ static void leaveModal(QWidget*);
+ static void enterModal_sys(QWidget*);
+ static void leaveModal_sys(QWidget*);
+ static bool isBlockedByModal(QWidget *widget);
+ static bool modalState();
+ static bool tryModalHelper(QWidget *widget, QWidget **rettop = 0);
+#ifdef Q_WS_MAC
+ static QWidget *tryModalHelper_sys(QWidget *top);
+ bool canQuit();
+#endif
+
+ bool notify_helper(QObject *receiver, QEvent * e);
+
+ void construct(
+#ifdef Q_WS_X11
+ Display *dpy = 0, Qt::HANDLE visual = 0, Qt::HANDLE cmap = 0
+#endif
+ );
+ void initialize();
+ void process_cmdline();
+
+#if defined(Q_WS_X11)
+ static void x11_initialize_style();
+#endif
+
+ enum KeyPlatform {
+ KB_Win = 1,
+ KB_Mac = 2,
+ KB_X11 = 4,
+ KB_KDE = 8,
+ KB_Gnome = 16,
+ KB_CDE = 32,
+ KB_All = 0xffff
+ };
+
+ static uint currentPlatform();
+ bool inPopupMode() const;
+ void closePopup(QWidget *popup);
+ void openPopup(QWidget *popup);
+ static void setFocusWidget(QWidget *focus, Qt::FocusReason reason);
+ static QWidget *focusNextPrevChild_helper(QWidget *toplevel, bool next);
+
+#ifndef QT_NO_SESSIONMANAGER
+ QSessionManager *session_manager;
+ QString session_id;
+ QString session_key;
+ bool is_session_restored;
+#endif
+
+#ifndef QT_NO_CURSOR
+ QList<QCursor> cursor_list;
+#endif
+#ifndef QT_NO_GRAPHICSVIEW
+ // Maintain a list of all scenes to ensure font and palette propagation to
+ // all scenes.
+ QList<QGraphicsScene *> scene_list;
+#endif
+
+ QBasicTimer toolTipWakeUp, toolTipFallAsleep;
+ QPoint toolTipPos, toolTipGlobalPos, hoverGlobalPos;
+ QPointer<QWidget> toolTipWidget;
+#ifndef QT_NO_SHORTCUT
+ QShortcutMap shortcutMap;
+#endif
+
+#ifdef QT3_SUPPORT
+ bool qt_compat_used;
+ bool qt_compat_resolved;
+ Ptrqt_tryAccelEvent qt_tryAccelEvent;
+ Ptrqt_tryComposeUnicode qt_tryComposeUnicode;
+ Ptrqt_dispatchAccelEvent qt_dispatchAccelEvent;
+
+ bool use_compat() {
+ return qt_tryAccelEvent
+ && qt_tryComposeUnicode
+ && qt_dispatchAccelEvent;
+ }
+#endif
+ static QInputContext *inputContext;
+
+ static Qt::MouseButtons mouse_buttons;
+ static Qt::KeyboardModifiers modifier_buttons;
+
+ static QSize app_strut;
+ static QWidgetList *popupWidgets;
+ static QStyle *app_style;
+ static int app_cspec;
+ static QPalette *app_pal;
+ static QPalette *sys_pal;
+ static QPalette *set_pal;
+ static QGraphicsSystem *graphics_system;
+ static QString graphics_system_name;
+
+private:
+ static QFont *app_font; // private for a reason! Always use QApplication::font() instead!
+public:
+ static QFont *sys_font;
+ static QFont *set_font;
+ static QWidget *main_widget;
+ static QWidget *focus_widget;
+ static QWidget *hidden_focus_widget;
+ static QWidget *active_window;
+ static QIcon *app_icon;
+ static bool obey_desktop_settings;
+ static int cursor_flash_time;
+ static int mouse_double_click_time;
+ static int keyboard_input_time;
+ static int wheel_scroll_lines;
+
+ static bool animate_ui;
+ static bool animate_menu;
+ static bool animate_tooltip;
+ static bool animate_combo;
+ static bool fade_menu;
+ static bool fade_tooltip;
+ static bool animate_toolbox;
+ static bool widgetCount; // Coupled with -widgetcount switch
+#ifdef Q_WS_MAC
+ static bool native_modal_dialog_active;
+#endif
+#if defined(Q_WS_WIN) && !defined(Q_OS_WINCE)
+ static bool inSizeMove;
+#endif
+
+ static void setSystemPalette(const QPalette &pal);
+ static void setPalette_helper(const QPalette &palette, const char* className, bool clearWidgetPaletteHash);
+ static void initializeWidgetPaletteHash();
+ static void setSystemFont(const QFont &font);
+
+#if defined(Q_WS_X11)
+ static void applyX11SpecificCommandLineArguments(QWidget *main_widget);
+#elif defined(Q_WS_QWS)
+ static void applyQWSSpecificCommandLineArguments(QWidget *main_widget);
+#endif
+
+#ifdef Q_WS_MAC
+ static OSStatus globalEventProcessor(EventHandlerCallRef, EventRef, void *);
+ static OSStatus globalAppleEventProcessor(const AppleEvent *, AppleEvent *, long);
+ static OSStatus tabletProximityCallback(EventHandlerCallRef, EventRef, void *);
+ static bool qt_mac_apply_settings();
+#endif
+
+#ifdef Q_WS_QWS
+ QPointer<QWSManager> last_manager;
+# ifndef QT_NO_DIRECTPAINTER
+ QMap<WId, QDirectPainter *> *directPainters;
+# endif
+ QRect maxWindowRect(const QScreen *screen) const { return maxWindowRects[screen]; }
+ void setMaxWindowRect(const QScreen *screen, int screenNo, const QRect &rect);
+ void setScreenTransformation(QScreen *screen, int screenNo, int transformation);
+#endif
+
+ static QApplicationPrivate *instance() { return self; }
+
+ static QString *styleOverride;
+
+ static int app_compile_version;
+
+#ifdef QT_KEYPAD_NAVIGATION
+ static bool keypadNavigation;
+ static QWidget *oldEditFocus;
+#endif
+
+#if defined(Q_WS_MAC) || defined(Q_WS_X11)
+ void _q_alertTimeOut();
+ QHash<QWidget *, QTimer *> alertTimerHash;
+#endif
+#if defined(QT_MAC_USE_COCOA)
+ void _q_runAppModalWindow();
+#endif
+#if defined(QT_MAC_USE_COCOA)
+ void _q_runModalWindow();
+#endif
+#ifndef QT_NO_STYLE_STYLESHEET
+ static QString styleSheet;
+#endif
+ static QPointer<QWidget> leaveAfterRelease;
+ static QWidget *pickMouseReceiver(QWidget *candidate, const QPoint &globalPos, QPoint &pos,
+ QEvent::Type type, Qt::MouseButtons buttons,
+ QWidget *buttonDown, QWidget *alienWidget);
+ static bool sendMouseEvent(QWidget *receiver, QMouseEvent *event, QWidget *alienWidget,
+ QWidget *native, QWidget **buttonDown, QPointer<QWidget> &lastMouseReceiver);
+#if defined(Q_WS_WIN) || defined(Q_WS_X11)
+ void sendSyntheticEnterLeave(QWidget *widget);
+#endif
+
+private:
+#ifdef Q_WS_QWS
+ QMap<const QScreen*, QRect> maxWindowRects;
+#endif
+
+ static QApplicationPrivate *self;
+ static bool shouldSetFocus(QWidget *w, Qt::FocusPolicy policy);
+};
+
+QT_END_NAMESPACE
+
+#endif // QAPPLICATION_P_H
diff --git a/src/gui/kernel/qapplication_qws.cpp b/src/gui/kernel/qapplication_qws.cpp
new file mode 100644
index 0000000000..2deda8e7df
--- /dev/null
+++ b/src/gui/kernel/qapplication_qws.cpp
@@ -0,0 +1,3817 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qglobal.h"
+#include "qlibrary.h"
+#include "qcursor.h"
+#include "qapplication.h"
+#include "private/qapplication_p.h"
+#include "qwidget.h"
+#include "qbitarray.h"
+#include "qpainter.h"
+#include "qpixmapcache.h"
+#include "qdatetime.h"
+#include "qtextcodec.h"
+#include "qdatastream.h"
+#include "qbuffer.h"
+#include "qsocketnotifier.h"
+#include "qsessionmanager.h"
+#include "qclipboard.h"
+#include "qbitmap.h"
+#include "qwssocket_qws.h"
+#include "qtransportauth_qws.h"
+#include "private/qtransportauth_qws_p.h"
+#include "qwsevent_qws.h"
+#include "private/qwscommand_qws_p.h"
+#include "qwsproperty_qws.h"
+#include "qscreen_qws.h"
+#include "qscreenproxy_qws.h"
+#include "qcopchannel_qws.h"
+#include "private/qlock_p.h"
+#include "private/qwslock_p.h"
+//#include "qmemorymanager_qws.h"
+#include "qwsmanager_qws.h"
+//#include "qwsregionmanager_qws.h"
+#include "qwindowsystem_qws.h"
+#include "private/qwindowsystem_p.h"
+
+#include "qwsdisplay_qws.h"
+#include "private/qwsdisplay_qws_p.h"
+#include "private/qwsinputcontext_p.h"
+#include "qfile.h"
+#include "qhash.h"
+#include "qdesktopwidget.h"
+#include "qcolormap.h"
+#include "private/qcursor_p.h"
+#include "qsettings.h"
+#include "qdebug.h"
+#include "qeventdispatcher_qws_p.h"
+#if !defined(QT_NO_GLIB)
+# include "qeventdispatcher_glib_qws_p.h"
+#endif
+
+
+#include "private/qwidget_p.h"
+#include "private/qbackingstore_p.h"
+#include "private/qwindowsurface_qws_p.h"
+#include "private/qfont_p.h"
+
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <locale.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#ifndef QT_NO_QWS_MULTIPROCESS
+#ifdef QT_NO_QSHM
+#include <sys/ipc.h>
+#include <sys/shm.h>
+#ifndef Q_OS_DARWIN
+# include <sys/sem.h>
+#endif
+#include <sys/socket.h>
+#else
+#include "private/qwssharedmemory_p.h"
+#endif
+#endif
+
+#ifndef QT_NO_DIRECTPAINTER
+class QDirectPainter;
+extern void qt_directpainter_region(QDirectPainter *dp, const QRegion &alloc, int type);
+#ifndef QT_NO_QWSEMBEDWIDGET
+extern void qt_directpainter_embedevent(QDirectPainter *dp,
+ const QWSEmbedEvent *e);
+#endif
+#endif // QT_NO_DIRECTPAINTER
+
+const int qwsSharedRamSize = 1 * 1024; // misc data, written by server, read by clients
+
+extern QApplication::Type qt_appType;
+extern QDesktopWidget *qt_desktopWidget;
+
+//these used to be environment variables, they are initialized from
+//environment variables in
+
+bool qws_savefonts = false;
+bool qws_screen_is_interlaced=false; //### should be detected
+bool qws_shared_memory = false;
+bool qws_sw_cursor = true;
+bool qws_accel = true; // ### never set
+QByteArray qws_display_spec(":0");
+Q_GUI_EXPORT int qws_display_id = 0;
+Q_GUI_EXPORT int qws_client_id = 0;
+QWidget *qt_pressGrab = 0;
+QWidget *qt_mouseGrb = 0;
+int *qt_last_x = 0;
+int *qt_last_y = 0;
+
+static int mouse_x_root = -1;
+static int mouse_y_root = -1;
+static int mouse_state = 0;
+static int mouse_double_click_distance = 5;
+
+int qt_servershmid = -1;
+
+bool qws_overrideCursor = false;
+#ifndef QT_NO_QWS_MANAGER
+#include "qdecorationfactory_qws.h"
+
+QT_BEGIN_NAMESPACE
+
+QT_USE_NAMESPACE
+static QDecoration *qws_decoration = 0;
+#endif
+
+#if defined(QT_DEBUG)
+/*
+extern "C" void dumpmem(const char* m)
+{
+ static int init=0;
+ static int prev=0;
+ FILE* f = fopen("/proc/meminfo","r");
+ // char line[100];
+ int total=0,used=0,free=0,shared=0,buffers=0,cached=0;
+ fscanf(f,"%*[^M]Mem: %d %d %d %d %d %d",&total,&used,&free,&shared,&buffers,&cached);
+ used -= buffers + cached;
+ if (!init) {
+ init=used;
+ } else {
+ printf("%40s: %+8d = %8d\n",m,used-init-prev,used-init);
+ prev = used-init;
+ }
+ fclose(f);
+}
+*/
+#endif
+
+// Get the name of the directory where Qt for Embedded Linux temporary data should
+// live.
+QString qws_dataDir()
+{
+ static QString result;
+ if (!result.isEmpty())
+ return result;
+ QByteArray dataDir = QString(QLatin1String("/tmp/qtembedded-%1")).arg(qws_display_id).toLocal8Bit();
+ if (mkdir(dataDir, 0700)) {
+ if (errno != EEXIST) {
+ qFatal("Cannot create Qt for Embedded Linux data directory: %s", dataDir.constData());
+ }
+ }
+
+ struct stat buf;
+ if (lstat(dataDir, &buf))
+ qFatal("stat failed for Qt for Embedded Linux data directory: %s", dataDir.constData());
+
+ if (!S_ISDIR(buf.st_mode))
+ qFatal("%s is not a directory", dataDir.constData());
+
+#ifndef Q_OS_INTEGRITY
+ if (buf.st_uid != getuid())
+ qFatal("Qt for Embedded Linux data directory is not owned by user %d", getuid());
+
+ if ((buf.st_mode & 0677) != 0600)
+ qFatal("Qt for Embedded Linux data directory has incorrect permissions: %s", dataDir.constData());
+#endif
+ dataDir += "/";
+
+ result = QString::fromLocal8Bit(dataDir);
+ return result;
+}
+
+// Get the filename of the pipe Qt for Embedded Linux uses for server/client comms
+Q_GUI_EXPORT QString qws_qtePipeFilename()
+{
+ return (qws_dataDir() + QString(QLatin1String(QTE_PIPE)).arg(qws_display_id));
+}
+
+static void setMaxWindowRect(const QRect &rect)
+{
+ const QList<QScreen*> subScreens = qt_screen->subScreens();
+ QScreen *screen = qt_screen;
+ int screenNo = 0;
+ for (int i = 0; i < subScreens.size(); ++i) {
+ if (subScreens.at(i)->region().contains(rect)) {
+ screen = subScreens.at(i);
+ screenNo = i;
+ break;
+ }
+ }
+
+ QApplicationPrivate *ap = QApplicationPrivate::instance();
+ ap->setMaxWindowRect(screen, screenNo, rect);
+}
+
+void QApplicationPrivate::setMaxWindowRect(const QScreen *screen, int screenNo,
+ const QRect &rect)
+{
+ if (maxWindowRects.value(screen) == rect)
+ return;
+
+ maxWindowRects[screen] = rect;
+
+ // Re-resize any maximized windows
+ QWidgetList l = QApplication::topLevelWidgets();
+ for (int i = 0; i < l.size(); ++i) {
+ QWidget *w = l.at(i);
+ QScreen *s = w->d_func()->getScreen();
+ if (w->isMaximized() && s == screen)
+ w->d_func()->setMaxWindowState_helper();
+ }
+
+ if ( qt_desktopWidget ) // XXX workaround crash
+ emit QApplication::desktop()->workAreaResized(screenNo);
+}
+
+#ifndef QT_NO_QWS_DYNAMICSCREENTRANSFORMATION
+
+typedef void (*TransformFunc)(QScreen *, int);
+#ifndef QT_NO_QWS_TRANSFORMED
+extern "C" void qws_setScreenTransformation(QScreen *, int);
+#endif
+static TransformFunc getTransformationFunction()
+{
+ static TransformFunc func = 0;
+
+ if (!func) {
+#ifdef QT_NO_QWS_TRANSFORMED
+# ifndef QT_NO_LIBRARY
+ // symbol is not built into the library, search for the plugin
+ const QStringList paths = QApplication::libraryPaths();
+ foreach (const QString &path, paths) {
+ const QString file = path + QLatin1String("/gfxdrivers/libqgfxtransformed");
+ func = (TransformFunc)QLibrary::resolve(file,
+ "qws_setScreenTransformation");
+ if (func)
+ break;
+ }
+# endif
+#else
+ func = qws_setScreenTransformation;
+#endif
+ if (!func)
+ func = (TransformFunc)-1;
+ }
+
+ if (func == (TransformFunc)-1)
+ return 0;
+
+ return func;
+}
+
+static void setScreenTransformation(int screenNo, int transformation)
+{
+ QScreen *screen = QScreen::instance();
+ const QList<QScreen*> subScreens = screen->subScreens();
+
+ if (screenNo == -1)
+ screenNo = 0;
+
+ if (screenNo == -1 && !subScreens.isEmpty())
+ screenNo = 0;
+
+ if (subScreens.isEmpty() && screenNo == 0) {
+ // nothing
+ } else if (screenNo < 0 || screenNo >= subScreens.size()) {
+ qWarning("setScreenTransformation: invalid screen %i", screenNo);
+ return;
+ }
+
+ if (screenNo < subScreens.size())
+ screen = subScreens.at(screenNo);
+
+ QApplicationPrivate *ap = QApplicationPrivate::instance();
+ ap->setScreenTransformation(screen, screenNo, transformation);
+}
+
+void QApplicationPrivate::setScreenTransformation(QScreen *screen,
+ int screenNo,
+ int transformation)
+{
+ QScreen *transformed = screen;
+
+ while (transformed->classId() == QScreen::ProxyClass)
+ transformed = static_cast<QProxyScreen*>(transformed)->screen();
+
+ if (transformed->classId() != QScreen::TransformedClass)
+ return;
+
+ TransformFunc setScreenTransformation = getTransformationFunction();
+ if (!setScreenTransformation)
+ return;
+
+ setScreenTransformation(transformed, transformation);
+
+ // need to re-configure() proxies bottom-up
+ if (screen->classId() == QScreen::ProxyClass) {
+ QList<QProxyScreen*> proxies;
+ QScreen *s = screen;
+
+ do {
+ QProxyScreen *proxy = static_cast<QProxyScreen*>(s);
+ proxies.append(proxy);
+ s = proxy->screen();
+ } while (s->classId() == QScreen::ProxyClass);
+
+ do {
+ QProxyScreen *proxy = proxies.takeLast();
+ proxy->setScreen(proxy->screen()); // triggers configure()
+ } while (!proxies.isEmpty());
+ }
+
+ if (qt_desktopWidget) { // XXX workaround crash for early screen transform events
+ QDesktopWidget *desktop = QApplication::desktop();
+
+ emit desktop->resized(screenNo);
+ if (maxWindowRect(screen).isEmpty()) // not explicitly set
+ emit desktop->workAreaResized(screenNo);
+ }
+
+ QWSServer *server = QWSServer::instance();
+ if (server) {
+ server->updateWindowRegions();
+ QRegion r = screen->region();
+ server->refresh(r);
+ }
+
+ // make sure maximized and fullscreen windows are updated
+ QWidgetList list = QApplication::topLevelWidgets();
+ for (int i = list.size() - 1; i >= 0; --i) {
+ QWidget *w = list.at(i);
+ if (w->isFullScreen())
+ w->d_func()->setFullScreenSize_helper();
+ else if (w->isMaximized())
+ w->d_func()->setMaxWindowState_helper();
+ }
+}
+
+#endif // QT_NO_QWS_DYNAMICSCREENTRANSFORMATION
+
+/*****************************************************************************
+ Internal variables and functions
+ *****************************************************************************/
+
+
+static QString appName; // application name
+static const char *appFont = 0; // application font
+static const char *appBGCol = 0; // application bg color
+static const char *appFGCol = 0; // application fg color
+static const char *appBTNCol = 0; // application btn color
+static const char *mwGeometry = 0; // main widget geometry
+static const char *mwTitle = 0; // main widget title
+//static bool mwIconic = false; // main widget iconified
+
+static bool app_do_modal = false; // modal mode
+Q_GUI_EXPORT QWSDisplay *qt_fbdpy = 0; // QWS `display'
+QLock *QWSDisplay::lock = 0;
+
+static int mouseButtonPressed = 0; // last mouse button pressed
+static int mouseButtonPressTime = 0; // when was a button pressed
+static short mouseXPos, mouseYPos; // mouse position in act window
+
+extern QWidgetList *qt_modal_stack; // stack of modal widgets
+
+static QWidget *popupButtonFocus = 0;
+static QWidget *popupOfPopupButtonFocus = 0;
+static bool popupCloseDownMode = false;
+static bool popupGrabOk;
+static QPointer<QWidget> *mouseInWidget = 0;
+
+static bool sm_blockUserInput = false; // session management
+
+QWidget *qt_button_down = 0; // widget got last button-down
+WId qt_last_cursor = 0xffffffff; // Was -1, but WIds are unsigned
+
+class QWSMouseEvent;
+class QWSKeyEvent;
+
+class QETWidget : public QWidget // event translator widget
+{
+public:
+ bool translateMouseEvent(const QWSMouseEvent *, int oldstate);
+ bool translateKeyEvent(const QWSKeyEvent *, bool grab);
+ bool translateRegionEvent(const QWSRegionEvent *);
+#ifndef QT_NO_QWSEMBEDWIDGET
+ void translateEmbedEvent(const QWSEmbedEvent *event);
+#endif
+ bool translateWheelEvent(const QWSMouseEvent *me);
+ void repaintDecoration(QRegion r, bool post);
+ void updateRegion();
+
+ bool raiseOnClick()
+ {
+ // With limited windowmanagement/taskbar/etc., raising big windows
+ // (eg. spreadsheet) over the top of everything else (eg. calculator)
+ // is just annoying.
+ return !isMaximized() && !isFullScreen();
+ }
+};
+
+void QApplicationPrivate::createEventDispatcher()
+{
+ Q_Q(QApplication);
+#if !defined(QT_NO_GLIB)
+ if (qgetenv("QT_NO_GLIB").isEmpty() && QEventDispatcherGlib::versionSupported())
+ eventDispatcher = (q->type() != QApplication::Tty
+ ? new QWSEventDispatcherGlib(q)
+ : new QEventDispatcherGlib(q));
+ else
+#endif
+ eventDispatcher = (q->type() != QApplication::Tty
+ ? new QEventDispatcherQWS(q)
+ : new QEventDispatcherUNIX(q));
+}
+
+// Single-process stuff. This should maybe move into qwindowsystem_qws.cpp
+
+static bool qws_single_process;
+static QList<QWSEvent*> incoming;
+static QList<QWSCommand*> outgoing;
+
+void qt_client_enqueue(const QWSEvent *event)
+{
+ QWSEvent *copy = QWSEvent::factory(event->type);
+ copy->copyFrom(event);
+ incoming.append(copy);
+}
+
+QList<QWSCommand*> *qt_get_server_queue()
+{
+ return &outgoing;
+}
+
+void qt_server_enqueue(const QWSCommand *command)
+{
+ QWSCommand *copy = QWSCommand::factory(command->type);
+ copy->copyFrom(command);
+ outgoing.append(copy);
+}
+
+QWSDisplay::Data::Data(QObject* parent, bool singleProcess)
+{
+#ifdef QT_NO_QWS_MULTIPROCESS
+ Q_UNUSED(parent);
+ Q_UNUSED(singleProcess);
+#else
+ if (singleProcess)
+ csocket = 0;
+ else {
+ csocket = new QWSSocket(parent);
+ QObject::connect(csocket, SIGNAL(disconnected()),
+ qApp, SLOT(quit()));
+ }
+ clientLock = 0;
+#endif
+ init();
+}
+
+QWSDisplay::Data::~Data()
+{
+// delete rgnMan; rgnMan = 0;
+// delete memorymanager; memorymanager = 0;
+ qt_screen->disconnect();
+ delete qt_screen; qt_screen = 0;
+#ifndef QT_NO_QWS_CURSOR
+ delete qt_screencursor; qt_screencursor = 0;
+#endif
+#ifndef QT_NO_QWS_MULTIPROCESS
+ shm.detach();
+ if (csocket) {
+ QWSCommand shutdownCmd(QWSCommand::Shutdown, 0, 0);
+ shutdownCmd.write(csocket);
+ csocket->flush(); // may be pending QCop message, eg.
+ delete csocket;
+ }
+ delete clientLock;
+ clientLock = 0;
+#endif
+ delete connected_event;
+ delete mouse_event;
+ delete current_event;
+ qDeleteAll(queue);
+#ifndef QT_NO_COP
+ delete qcop_response;
+#endif
+}
+
+#ifndef QT_NO_QWS_MULTIPROCESS
+bool QWSDisplay::Data::lockClient(QWSLock::LockType type, int timeout)
+{
+ return !clientLock || clientLock->lock(type, timeout);
+}
+
+void QWSDisplay::Data::unlockClient(QWSLock::LockType type)
+{
+ if (clientLock) clientLock->unlock(type);
+}
+
+bool QWSDisplay::Data::waitClient(QWSLock::LockType type, int timeout)
+{
+ return !clientLock || clientLock->wait(type, timeout);
+}
+
+QWSLock* QWSDisplay::Data::getClientLock()
+{
+ return clientLock;
+}
+#endif // QT_NO_QWS_MULTIPROCESS
+
+void QWSDisplay::Data::flush()
+{
+#ifndef QT_NO_QWS_MULTIPROCESS
+ if (csocket) {
+ csocket->waitForReadyRead(0);
+ csocket->flush();
+ }
+#endif
+}
+
+#if 0
+void QWSDisplay::Data::debugQueue() {
+ for (int i = 0; i < queue.size(); ++i) {
+ QWSEvent *e = queue.at(i);
+ qDebug( " ev %d type %d sl %d rl %d", i, e->type, e->simpleLen, e->rawLen);
+ }
+}
+#endif
+
+bool QWSDisplay::Data::queueNotEmpty()
+{
+ return mouse_event/*||region_event*/||queue.count() > 0;
+}
+QWSEvent* QWSDisplay::Data::dequeue()
+{
+ QWSEvent *r=0;
+ if (queue.count()) {
+ r = queue.first();
+ queue.removeFirst();
+ if (r->type == QWSEvent::Region)
+ region_events_count--;
+ } else if (mouse_event) {
+ r = mouse_event;
+ mouse_event = 0;
+#ifdef QAPPLICATION_EXTRA_DEBUG
+ mouse_event_count = 0;
+#endif
+ }
+ return r;
+}
+
+QWSEvent* QWSDisplay::Data::peek()
+{
+ return queue.first();
+}
+
+bool QWSDisplay::Data::directServerConnection()
+{
+#ifndef QT_NO_QWS_MULTIPROCESS
+ return csocket == 0;
+#else
+ return true;
+#endif
+}
+
+void QWSDisplay::Data::create(int n)
+{
+ QWSCreateCommand cmd(n);
+ sendCommand(cmd);
+}
+
+void QWSDisplay::Data::flushCommands()
+{
+#ifndef QT_NO_QWS_MULTIPROCESS
+ if (csocket)
+ csocket->flush();
+#endif
+}
+
+void QWSDisplay::Data::sendCommand(QWSCommand & cmd)
+{
+#ifndef QT_NO_QWS_MULTIPROCESS
+ if (csocket)
+ cmd.write(csocket);
+ else
+#endif
+ qt_server_enqueue(&cmd);
+}
+
+void QWSDisplay::Data::sendSynchronousCommand(QWSCommand & cmd)
+{
+#ifndef QT_NO_QWS_MULTIPROCESS
+ if (csocket) {
+ lockClient(QWSLock::Communication);
+ cmd.write(csocket);
+ bool ok = true;
+ while (csocket->bytesToWrite() > 0) {
+ if (!csocket->waitForBytesWritten(-1)) {
+ qCritical("QWSDisplay::Data::sendSynchronousCommand: %s",
+ qPrintable(csocket->errorString()));
+ ok = false;
+ break;
+ }
+ }
+ if (ok)
+ waitClient(QWSLock::Communication);
+ } else
+#endif
+ qt_server_enqueue(&cmd);
+}
+
+int QWSDisplay::Data::takeId()
+{
+ if (unused_identifiers.count() == 10)
+ create(15);
+ if (unused_identifiers.count() == 0)
+ waitForCreation();
+ return unused_identifiers.takeFirst();
+}
+
+void QWSDisplay::Data::setMouseFilter(void (*filter)(QWSMouseEvent*))
+{
+ mouseFilter = filter;
+}
+
+#ifndef QT_NO_QWS_MULTIPROCESS
+
+QWSLock* QWSDisplay::Data::clientLock = 0;
+
+void Q_GUI_EXPORT qt_app_reinit( const QString& newAppName )
+{
+ qt_fbdpy->d->reinit( newAppName );
+}
+
+#endif // QT_NO_QWS_MULTIPROCESS
+
+class QDesktopWidget;
+
+#ifndef QT_NO_QWS_MULTIPROCESS
+void QWSDisplay::Data::reinit( const QString& newAppName )
+{
+ Q_ASSERT(csocket);
+
+ delete connected_event;
+ connected_event = 0;
+ region_events_count = 0;
+// region_ack = 0;
+ delete mouse_event;
+ mouse_event = 0;
+// region_event = 0;
+ region_offset_window = 0;
+#ifndef QT_NO_COP
+ delete qcop_response;
+ qcop_response = 0;
+#endif
+ delete current_event;
+ current_event = 0;
+#ifdef QAPPLICATION_EXTRA_DEBUG
+ mouse_event_count = 0;
+#endif
+ mouseFilter = 0;
+
+ qt_desktopWidget = 0;
+ delete QWSDisplay::Data::clientLock;
+ QWSDisplay::Data::clientLock = 0;
+
+ QString pipe = qws_qtePipeFilename();
+
+ // QWS client
+ // Cleanup all cached ids
+ unused_identifiers.clear();
+ delete csocket;
+
+ appName = newAppName;
+ qApp->setObjectName( appName );
+
+ csocket = new QWSSocket();
+ QObject::connect(csocket, SIGNAL(disconnected()),
+ qApp, SLOT(quit()));
+ csocket->connectToLocalFile(pipe);
+
+ QWSDisplay::Data::clientLock = new QWSLock();
+
+ QWSIdentifyCommand cmd;
+ cmd.setId(appName, QWSDisplay::Data::clientLock->id());
+
+#ifndef QT_NO_SXE
+ QTransportAuth *a = QTransportAuth::getInstance();
+ QTransportAuth::Data *d = a->connectTransport(
+ QTransportAuth::UnixStreamSock |
+ QTransportAuth::Trusted,
+ csocket->socketDescriptor());
+ QAuthDevice *ad = a->authBuf( d, csocket );
+ ad->setClient( csocket );
+
+ cmd.write(ad);
+#else
+ cmd.write(csocket);
+#endif
+
+ // wait for connect confirmation
+ waitForConnection();
+
+ qws_client_id = connected_event->simpleData.clientId;
+
+ if (!QWSDisplay::initLock(pipe, false))
+ qFatal("Cannot get display lock");
+
+ if (shm.attach(connected_event->simpleData.servershmid)) {
+ sharedRam = static_cast<uchar *>(shm.address());
+ QScreen *s = qt_get_screen(qws_display_id, qws_display_spec.constData());
+ if (s)
+ sharedRamSize += s->memoryNeeded(QLatin1String(qws_display_spec.constData()));
+ } else {
+ perror("QWSDisplay::Data::init");
+ qFatal("Client can't attach to main ram memory.");
+ }
+
+ qApp->desktop();
+
+ // We wait for creation mainly so that we can process important
+ // initialization events such as MaxWindowRect that are sent
+ // before object id creation. Waiting here avoids later window
+ // resizing since we have the MWR before windows are displayed.
+ waitForCreation();
+
+ sharedRamSize -= sizeof(int);
+ qt_last_x = reinterpret_cast<int *>(sharedRam + sharedRamSize);
+ sharedRamSize -= sizeof(int);
+ qt_last_y = reinterpret_cast<int *>(sharedRam + sharedRamSize);
+
+#ifndef QT_NO_COP
+ QCopChannel::reregisterAll();
+#endif
+ csocket->flush();
+}
+#endif
+
+void QWSDisplay::Data::init()
+{
+ connected_event = 0;
+ region_events_count = 0;
+// region_ack = 0;
+ mouse_event = 0;
+ mouse_state = -1;
+ mouse_winid = 0;
+// region_event = 0;
+ region_offset_window = 0;
+#ifndef QT_NO_COP
+ qcop_response = 0;
+#endif
+ current_event = 0;
+#ifdef QAPPLICATION_EXTRA_DEBUG
+ mouse_event_count = 0;
+#endif
+ mouseFilter = 0;
+
+ QString pipe = qws_qtePipeFilename();
+
+ sharedRamSize = qwsSharedRamSize;
+
+#ifndef QT_NO_QWS_MULTIPROCESS
+ if (csocket) {
+ // QWS client
+
+ connectToPipe();
+
+ QWSDisplay::Data::clientLock = new QWSLock();
+
+ QWSIdentifyCommand cmd;
+ cmd.setId(appName, QWSDisplay::Data::clientLock->id());
+#ifndef QT_NO_SXE
+ QTransportAuth *a = QTransportAuth::getInstance();
+ QTransportAuth::Data *d = a->connectTransport(
+ QTransportAuth::UnixStreamSock |
+ QTransportAuth::Trusted,
+ csocket->socketDescriptor());
+ QAuthDevice *ad = a->authBuf( d, csocket );
+ ad->setClient( csocket );
+ cmd.write(ad);
+#else
+ cmd.write(csocket);
+#endif
+
+ // create(30); // not necessary, server will send ids anyway
+ waitForConnection();
+
+ qws_client_id = connected_event->simpleData.clientId;
+
+ // now we want to get the exact display spec to use if we haven't
+ // specified anything.
+ if (qws_display_spec.at(0) == ':')
+ qws_display_spec = connected_event->display;
+
+ if (!QWSDisplay::initLock(pipe, false))
+ qFatal("Cannot get display lock");
+
+ if (shm.attach(connected_event->simpleData.servershmid)) {
+ sharedRam = static_cast<uchar *>(shm.address());
+ QScreen *s = qt_get_screen(qws_display_id, qws_display_spec.constData());
+ if (s)
+ sharedRamSize += s->memoryNeeded(QLatin1String(qws_display_spec.constData()));
+ } else {
+ perror("QWSDisplay::Data::init");
+ qFatal("Client can't attach to main ram memory.");
+ }
+
+ // We wait for creation mainly so that we can process important
+ // initialization events such as MaxWindowRect that are sent
+ // before object id creation. Waiting here avoids later window
+ // resizing since we have the MWR before windows are displayed.
+ waitForCreation();
+ } else
+#endif
+ {
+ create(30);
+
+ // QWS server
+ if (!QWSDisplay::initLock(pipe, true))
+ qFatal("Cannot get display lock");
+
+ QScreen *s = qt_get_screen(qws_display_id, qws_display_spec.constData());
+ if (s)
+ sharedRamSize += s->memoryNeeded(QLatin1String(qws_display_spec.constData()));
+
+#ifndef QT_NO_QWS_MULTIPROCESS
+
+ if (!shm.create(sharedRamSize)) {
+ perror("Cannot create main ram shared memory\n");
+ qFatal("Unable to allocate %d bytes of shared memory", sharedRamSize);
+ }
+ qt_servershmid = shm.id();
+ sharedRam = static_cast<uchar *>(shm.address());
+#else
+ sharedRam=static_cast<uchar *>(malloc(sharedRamSize));
+#endif
+ // Need to zero index count at end of block, might as well zero
+ // the rest too
+ memset(sharedRam,0,sharedRamSize);
+
+ QWSIdentifyCommand cmd;
+ cmd.setId(appName, -1);
+ qt_server_enqueue(&cmd);
+ }
+
+ // Allow some memory for the graphics driver too
+ //### Note that sharedRamSize() has side effects; it must be called
+ //### once, and only once, and before initDevice()
+ sharedRamSize -= qt_screen->sharedRamSize(sharedRam+sharedRamSize);
+
+#ifndef QT_NO_QWS_MULTIPROCESS
+ if(!csocket)
+#endif
+ {
+ //QWS server process
+ if (!qt_screen->initDevice())
+ qFatal("Unable to initialize screen driver!");
+ }
+
+ sharedRamSize -= sizeof(int);
+ qt_last_x = reinterpret_cast<int *>(sharedRam + sharedRamSize);
+ sharedRamSize -= sizeof(int);
+ qt_last_y = reinterpret_cast<int *>(sharedRam + sharedRamSize);
+
+ /* Initialise framebuffer memory manager */
+ /* Add 4k for luck and to avoid clobbering hardware cursor */
+// int screensize=qt_screen->screenSize();
+// memorymanager=new QMemoryManager(qt_screen->base()+screensize+4096,
+// qt_screen->totalSize()-(screensize+4096),0);
+
+// #ifndef QT_NO_QWS_MULTIPROCESS
+// rgnMan = new QWSRegionManager(pipe, csocket);
+// #else
+// rgnMan = new QWSRegionManager(pipe, 0); //####### not necessary
+// #endif
+#ifndef QT_NO_QWS_MULTIPROCESS
+ if (csocket)
+ csocket->flush();
+#endif
+}
+
+
+QWSEvent* QWSDisplay::Data::readMore()
+{
+#ifdef QT_NO_QWS_MULTIPROCESS
+ return incoming.isEmpty() ? 0 : incoming.takeFirst();
+#else
+ if (!csocket)
+ return incoming.isEmpty() ? 0 : incoming.takeFirst();
+ // read next event
+ if (!current_event) {
+ int event_type = qws_read_uint(csocket);
+
+ if (event_type >= 0) {
+ current_event = QWSEvent::factory(event_type);
+ }
+ }
+
+ if (current_event) {
+ if (current_event->read(csocket)) {
+ // Finished reading a whole event.
+ QWSEvent* result = current_event;
+ current_event = 0;
+ return result;
+ }
+ }
+
+ // Not finished reading a whole event.
+ return 0;
+#endif
+}
+
+void QWSDisplay::Data::fillQueue()
+{
+ QWSServer::processEventQueue();
+ QWSEvent *e = readMore();
+#ifndef QT_NO_QWS_MULTIPROCESS
+ int bytesAvailable = csocket ? csocket->bytesAvailable() : 0;
+ int bytesRead = 0;
+#endif
+ while (e) {
+#ifndef QT_NO_QWS_MULTIPROCESS
+ bytesRead += QWS_PROTOCOL_ITEM_SIZE((*e));
+#endif
+ if (e->type == QWSEvent::Connected) {
+ connected_event = static_cast<QWSConnectedEvent *>(e);
+ return;
+ } else if (e->type == QWSEvent::Creation) {
+ QWSCreationEvent *ce = static_cast<QWSCreationEvent*>(e);
+ int id = ce->simpleData.objectid;
+ int count = ce->simpleData.count;
+ for (int i = 0; i < count; ++i)
+ unused_identifiers.append(id++);
+ delete e;
+ } else if (e->type == QWSEvent::Mouse) {
+ if (!qt_screen) {
+ delete e;
+ } else {
+ QWSMouseEvent *me = static_cast<QWSMouseEvent*>(e);
+ if (mouseFilter)
+ mouseFilter(me);
+#ifdef QAPPLICATION_EXTRA_DEBUG
+ static const char *defaultAction= "INITIAL";
+ const char * action = defaultAction;
+#endif
+ delete mouse_event;
+ if (mouse_winid != me->window ()
+ || mouse_state != me->simpleData.state) {
+ queue.append(me);
+ mouse_winid = me->window();
+ mouse_state = me->simpleData.state;
+ mouse_event = 0;
+#ifdef QAPPLICATION_EXTRA_DEBUG
+ mouse_event_count = 0;
+ action = "ENQUEUE";
+#endif
+ } else {
+#ifdef QAPPLICATION_EXTRA_DEBUG
+ if (mouse_event)
+ action = "COMPRESS";
+ mouse_event_count++;
+#endif
+ mouse_event = me;
+ }
+#ifdef QAPPLICATION_EXTRA_DEBUG
+ if (me->simpleData.state !=0 || action != defaultAction || mouse_event_count != 0)
+ qDebug("fillQueue %s (%d,%d), state %x win %d count %d", action,
+ me->simpleData.x_root, me->simpleData.y_root, me->simpleData.state,
+ me->window(), mouse_event_count);
+#endif
+ }
+#ifndef QT_NO_QWS_MULTIPROCESS
+ } else if (e->type == QWSEvent::Region && clientLock) {
+ // not really an unlock, decrements the semaphore
+ region_events_count++;
+ clientLock->unlock(QWSLock::RegionEvent);
+ queue.append(e);
+#endif
+#ifndef QT_NO_QWS_PROPERTIES
+ } else if (e->type == QWSEvent::PropertyReply) {
+ QWSPropertyReplyEvent *pe = static_cast<QWSPropertyReplyEvent*>(e);
+ int len = pe->simpleData.len;
+ char *data;
+ if (len <= 0) {
+ data = 0;
+ } else {
+ data = new char[len];
+ memcpy(data, pe->data, len) ;
+ }
+ QPaintDevice::qwsDisplay()->getPropertyLen = len;
+ QPaintDevice::qwsDisplay()->getPropertyData = data;
+ delete e;
+#endif // QT_NO_QWS_PROPERTIES
+ } else if (e->type==QWSEvent::MaxWindowRect && qt_screen) {
+ // Process this ASAP, in case new widgets are created (startup)
+ setMaxWindowRect((static_cast<QWSMaxWindowRectEvent*>(e))->simpleData.rect);
+ delete e;
+#ifndef QT_NO_QWS_DYNAMICSCREENTRANSFORMATION
+ } else if (e->type == QWSEvent::ScreenTransformation) {
+ QWSScreenTransformationEvent *pe = static_cast<QWSScreenTransformationEvent*>(e);
+ setScreenTransformation(pe->simpleData.screen,
+ pe->simpleData.transformation);
+ delete e;
+#endif
+#ifndef QT_NO_COP
+ } else if (e->type == QWSEvent::QCopMessage) {
+ QWSQCopMessageEvent *pe = static_cast<QWSQCopMessageEvent*>(e);
+ if (pe->simpleData.is_response) {
+ qcop_response = pe;
+ } else {
+ queue.append(e);
+ }
+#endif
+ } else {
+ queue.append(e);
+ }
+ //debugQueue();
+#ifndef QT_NO_QWS_MULTIPROCESS
+ if (csocket && bytesRead >= bytesAvailable)
+ break;
+#endif
+ e = readMore();
+ }
+}
+
+#ifndef QT_NO_QWS_MULTIPROCESS
+
+static int qws_connection_timeout = 5;
+
+void QWSDisplay::Data::connectToPipe()
+{
+ Q_ASSERT(csocket);
+
+ int timeout = qgetenv("QWS_CONNECTION_TIMEOUT").toInt();
+ if (timeout)
+ qws_connection_timeout = timeout;
+
+ const QString pipe = qws_qtePipeFilename();
+ int i = 0;
+ while (!csocket->connectToLocalFile(pipe)) {
+ if (++i > qws_connection_timeout) {
+ qWarning("No Qt for Embedded Linux server appears to be running.");
+ qWarning("If you want to run this program as a server,");
+ qWarning("add the \"-qws\" command-line option.");
+ exit(1);
+ }
+ sleep(1);
+ }
+}
+
+void QWSDisplay::Data::waitForConnection()
+{
+ connected_event = 0;
+
+ for (int i = 0; i < qws_connection_timeout; i++) {
+ fillQueue();
+ if (connected_event)
+ return;
+ csocket->flush();
+ csocket->waitForReadyRead(1000);
+ }
+
+ csocket->flush();
+ if (!connected_event)
+ qFatal("Did not receive a connection event from the qws server");
+}
+
+void QWSDisplay::Data::waitForRegionAck(int winId)
+{
+ QWSEvent *ack = 0;
+
+ if (csocket) { // GuiClient
+ int i = 0;
+ while (!ack) {
+ fillQueue();
+
+ while (i < queue.size()) {
+ QWSEvent *e = queue.at(i);
+ if (e->type == QWSEvent::Region && e->window() == winId) {
+ ack = e;
+ queue.removeAt(i);
+ break;
+ }
+ ++i;
+ }
+
+ if (!ack) {
+ csocket->flush();
+ csocket->waitForReadyRead(1000);
+ }
+ }
+ } else { // GuiServer
+ fillQueue();
+ for (int i = 0; i < queue.size(); /* nothing */) {
+ QWSEvent *e = queue.at(i);
+ if (e->type == QWSEvent::Region && e->window() == winId) {
+ ack = e;
+ queue.removeAt(i);
+ break;
+ }
+ ++i;
+ }
+ if (!ack) // already processed
+ return;
+ }
+
+ Q_ASSERT(ack);
+
+ qApp->qwsProcessEvent(ack);
+ delete ack;
+ region_events_count--;
+}
+
+void QWSDisplay::Data::waitForRegionEvents(int winId, bool ungrabDisplay)
+{
+ if (!clientLock)
+ return;
+
+ int removedEventsCount = 0;
+
+ // fill queue with unreceived region events
+ if (!clientLock->hasLock(QWSLock::RegionEvent)) {
+ bool ungrabbed = false;
+ if (ungrabDisplay && QWSDisplay::grabbed()) {
+ QWSDisplay::ungrab();
+ ungrabbed = true;
+ }
+
+ for (;;) {
+ fillQueue();
+ if (clientLock->hasLock(QWSLock::RegionEvent))
+ break;
+ csocket->flush();
+ csocket->waitForReadyRead(1000);
+ }
+
+ if (ungrabbed)
+ QWSDisplay::grab(true);
+ }
+
+ // check the queue for pending region events
+ QWSEvent *regionEvent = 0;
+ for (int i = 0; i < queue.size(); /* nothing */) {
+ QWSEvent *e = queue.at(i);
+ if (e->type == QWSEvent::Region && e->window() == winId) {
+ QWSRegionEvent *re = static_cast<QWSRegionEvent*>(e);
+ if (re->simpleData.type == QWSRegionEvent::Allocation) {
+ delete regionEvent;
+ regionEvent = re;
+ }
+ queue.removeAt(i);
+ removedEventsCount++;
+ } else {
+ ++i;
+ }
+ }
+
+ if (regionEvent) {
+ qApp->qwsProcessEvent(regionEvent);
+ delete regionEvent;
+ }
+ region_events_count -= removedEventsCount;
+}
+
+bool QWSDisplay::Data::hasPendingRegionEvents() const
+{
+ if (clientLock && !clientLock->hasLock(QWSLock::RegionEvent))
+ return true;
+
+ return region_events_count > 0;
+}
+
+#endif // QT_NO_QWS_MULTIPROCESS
+
+void QWSDisplay::Data::waitForCreation()
+{
+ fillQueue();
+#ifndef QT_NO_QWS_MULTIPROCESS
+ while (unused_identifiers.count() == 0) {
+ if (csocket) {
+ csocket->flush();
+ csocket->waitForReadyRead(1000);
+ }
+ fillQueue();
+ }
+#endif
+}
+
+
+#ifndef QT_NO_QWS_MULTIPROCESS
+void QWSDisplay::Data::waitForPropertyReply()
+{
+ if (!csocket)
+ return;
+ fillQueue();
+ while (qt_fbdpy->getPropertyLen == -2) {
+ csocket->flush();
+ csocket->waitForReadyRead(1000);
+ fillQueue();
+ }
+}
+#endif
+
+#ifndef QT_NO_COP
+void QWSDisplay::Data::waitForQCopResponse()
+{
+ for (;;) {
+ fillQueue();
+ if (qcop_response)
+ break;
+#ifndef QT_NO_QWS_MULTIPROCESS
+ if (csocket) {
+ csocket->flush();
+ csocket->waitForReadyRead(1000);
+ }
+#endif
+ }
+ queue.prepend(qcop_response);
+ qcop_response = 0;
+}
+#endif
+
+/*!
+ \class QWSDisplay
+ \brief The QWSDisplay class provides a display for QWS; it is an internal class.
+
+ \internal
+
+ \ingroup qws
+*/
+
+QWSDisplay::QWSDisplay()
+{
+ d = new Data(0, qws_single_process);
+}
+
+QWSDisplay::~QWSDisplay()
+{
+ delete d;
+ delete lock;
+ lock = 0;
+}
+
+bool QWSDisplay::grabbed()
+{
+ return lock->locked();
+}
+
+void QWSDisplay::grab()
+{
+ lock->lock(QLock::Read);
+}
+
+void QWSDisplay::grab(bool write)
+{
+ lock->lock(write ? QLock::Write : QLock::Read);
+
+}
+void QWSDisplay::ungrab()
+{
+ lock->unlock();
+}
+
+#if 0
+QWSRegionManager *QWSDisplay::regionManager() const
+{
+ return d->rgnMan;
+}
+#endif
+
+bool QWSDisplay::eventPending() const
+{
+#ifndef QT_NO_QWS_MULTIPROCESS
+ d->flush();
+#endif
+ d->fillQueue();
+ return d->queueNotEmpty();
+}
+
+
+/*
+ Caller must delete return value!
+ */
+QWSEvent *QWSDisplay::getEvent()
+{
+ d->fillQueue();
+ Q_ASSERT(d->queueNotEmpty());
+ QWSEvent* e = d->dequeue();
+
+ return e;
+}
+
+uchar* QWSDisplay::frameBuffer() const { return qt_screen->base(); }
+int QWSDisplay::width() const { return qt_screen->width(); }
+int QWSDisplay::height() const { return qt_screen->height(); }
+int QWSDisplay::depth() const { return qt_screen->depth(); }
+int QWSDisplay::pixmapDepth() const { return qt_screen->pixmapDepth(); }
+bool QWSDisplay::supportsDepth(int depth) const { return qt_screen->supportsDepth(depth); }
+uchar *QWSDisplay::sharedRam() const { return d->sharedRam; }
+int QWSDisplay::sharedRamSize() const { return d->sharedRamSize; }
+
+#ifndef QT_NO_QWS_PROPERTIES
+
+void QWSDisplay::addProperty(int winId, int property)
+{
+ QWSAddPropertyCommand cmd;
+ cmd.simpleData.windowid = winId;
+ cmd.simpleData.property = property;
+ d->sendCommand(cmd);
+}
+
+void QWSDisplay::setProperty(int winId, int property, int mode, const QByteArray &data)
+{
+ QWSSetPropertyCommand cmd;
+ cmd.simpleData.windowid = winId;
+ cmd.simpleData.property = property;
+ cmd.simpleData.mode = mode;
+ cmd.setData(data.constData(), data.size());
+ d->sendCommand(cmd);
+}
+
+void QWSDisplay::setProperty(int winId, int property, int mode,
+ const char * data)
+{
+ QWSSetPropertyCommand cmd;
+ cmd.simpleData.windowid = winId;
+ cmd.simpleData.property = property;
+ cmd.simpleData.mode = mode;
+ cmd.setData(data, strlen(data));
+ d->sendCommand(cmd);
+}
+
+void QWSDisplay::removeProperty(int winId, int property)
+{
+ QWSRemovePropertyCommand cmd;
+ cmd.simpleData.windowid = winId;
+ cmd.simpleData.property = property;
+ d->sendCommand(cmd);
+}
+
+/*
+ It is the caller's responsibility to delete[] \a data.
+ */
+bool QWSDisplay::getProperty(int winId, int property, char *&data, int &len)
+{
+ if (d->directServerConnection()) {
+ const char *propertyData;
+ bool retval = qwsServer->d_func()->get_property(winId, property, propertyData, len);
+ if (len <= 0) {
+ data = 0;
+ } else {
+ data = new char[len];
+ memcpy(data, propertyData, len) ;
+ }
+ return retval;
+ }
+ QWSGetPropertyCommand cmd;
+ cmd.simpleData.windowid = winId;
+ cmd.simpleData.property = property;
+ d->sendCommand(cmd);
+
+ getPropertyLen = -2;
+ getPropertyData = 0;
+
+#ifndef QT_NO_QWS_MULTIPROCESS
+ d->waitForPropertyReply();
+#endif
+
+ len = getPropertyLen;
+ data = getPropertyData;
+
+ getPropertyLen = -2;
+ getPropertyData = 0;
+
+ return len != -1;
+}
+
+#endif // QT_NO_QWS_PROPERTIES
+
+void QWSDisplay::setAltitude(int winId, int alt, bool fixed)
+{
+ QWSChangeAltitudeCommand cmd;
+#ifdef QT_DEBUG
+ memset(cmd.simpleDataPtr, 0, sizeof(cmd.simpleData)); //shut up Valgrind
+#endif
+ cmd.simpleData.windowid = winId;
+ cmd.simpleData.altitude = QWSChangeAltitudeCommand::Altitude(alt);
+ cmd.simpleData.fixed = fixed;
+ if (d->directServerConnection()) {
+ qwsServer->d_func()->set_altitude(&cmd);
+ } else {
+ d->sendSynchronousCommand(cmd);
+ }
+}
+
+void QWSDisplay::setOpacity(int winId, int opacity)
+{
+ QWSSetOpacityCommand cmd;
+ cmd.simpleData.windowid = winId;
+ cmd.simpleData.opacity = opacity;
+ if (d->directServerConnection()) {
+ qwsServer->d_func()->set_opacity(&cmd);
+ } else {
+ d->sendCommand(cmd);
+ }
+}
+
+
+
+void QWSDisplay::requestFocus(int winId, bool get)
+{
+ QWSRequestFocusCommand cmd;
+ cmd.simpleData.windowid = winId;
+ cmd.simpleData.flag = get;
+ if (d->directServerConnection())
+ qwsServer->d_func()->request_focus(&cmd);
+ else
+ d->sendCommand(cmd);
+}
+
+void QWSDisplay::setIdentity(const QString &appName)
+{
+ QWSIdentifyCommand cmd;
+#ifdef QT_NO_QWS_MULTIPROCESS
+ const int id = -1;
+#else
+ const int id = QWSDisplay::Data::clientLock ? QWSDisplay::Data::clientLock->id() : -1;
+#endif
+ cmd.setId(appName, id);
+ if (d->directServerConnection())
+ qwsServer->d_func()->set_identity(&cmd);
+ else
+ d->sendCommand(cmd);
+}
+
+void QWSDisplay::nameRegion(int winId, const QString& n, const QString &c)
+{
+ QWSRegionNameCommand cmd;
+ cmd.simpleData.windowid = winId;
+ cmd.setName(n, c);
+ if (d->directServerConnection())
+ qwsServer->d_func()->name_region(&cmd);
+ else
+ d->sendCommand(cmd);
+}
+
+void QWSDisplay::requestRegion(int winId, const QString &surfaceKey,
+ const QByteArray &surfaceData,
+ const QRegion &region)
+{
+ if (d->directServerConnection()) {
+ qwsServer->d_func()->request_region(winId, surfaceKey,
+ surfaceData, region);
+ } else {
+ QWSRegionCommand cmd;
+ cmd.setData(winId, surfaceKey, surfaceData, region);
+ d->sendSynchronousCommand(cmd);
+ }
+}
+
+void QWSDisplay::repaintRegion(int winId, int windowFlags, bool opaque, QRegion r)
+{
+ if (d->directServerConnection()) {
+ qwsServer->d_func()->repaint_region(winId, windowFlags, opaque, r);
+ } else {
+ QVector<QRect> ra = r.rects();
+
+ /*
+ for (int i = 0; i < ra.size(); i++) {
+ QRect r(ra[i]);
+ qDebug("rect: %d %d %d %d", r.x(), r.y(), r.right(), r.bottom());
+ }
+ */
+
+ QWSRepaintRegionCommand cmd;
+ /* XXX QWSRegionCommand is padded out in a compiler dependent way.
+ Zeroed out to avoid valgrind reporting uninitialized memory usage.
+ */
+#ifdef QT_DEBUG
+ memset(cmd.simpleDataPtr, 0, sizeof(cmd.simpleData)); //shut up Valgrind
+#endif
+ cmd.simpleData.windowid = winId;
+ cmd.simpleData.windowFlags = windowFlags;
+ cmd.simpleData.opaque = opaque;
+ cmd.simpleData.nrectangles = ra.count();
+ cmd.setData(reinterpret_cast<const char *>(ra.constData()),
+ ra.count() * sizeof(QRect), false);
+
+ d->sendSynchronousCommand(cmd);
+ }
+}
+
+
+void QWSDisplay::moveRegion(int winId, int dx, int dy)
+{
+ QWSRegionMoveCommand cmd;
+ cmd.simpleData.windowid = winId;
+ cmd.simpleData.dx = dx;
+ cmd.simpleData.dy = dy;
+
+ if (d->directServerConnection()) {
+ qwsServer->d_func()->move_region(&cmd);
+ } else {
+ d->sendSynchronousCommand(cmd);
+ }
+// d->offsetPendingExpose(winId, QPoint(cmd.simpleData.dx, cmd.simpleData.dy));
+}
+
+void QWSDisplay::destroyRegion(int winId)
+{
+ QWSRegionDestroyCommand cmd;
+ cmd.simpleData.windowid = winId;
+ if (d->directServerConnection()) {
+ qwsServer->d_func()->destroy_region(&cmd);
+ } else {
+ d->sendCommand(cmd);
+ }
+}
+
+#ifndef QT_NO_QWS_INPUTMETHODS
+
+void QWSDisplay::sendIMUpdate(int type, int winId, int widgetid)
+{
+ QWSIMUpdateCommand cmd;
+ cmd.simpleData.windowid = winId;
+ cmd.simpleData.widgetid = widgetid;
+
+ cmd.simpleData.type = type;
+
+ if (d->directServerConnection()) {
+ qwsServer->d_func()->im_update(&cmd);
+ } else {
+ d->sendCommand(cmd);
+ }
+}
+
+void QWSDisplay::sendIMResponse(int winId, int property, const QVariant &result)
+{
+ QWSIMResponseCommand cmd;
+ cmd.simpleData.windowid = winId;
+ cmd.simpleData.property = property;
+
+ cmd.setResult(result);
+
+ if (d->directServerConnection()) {
+ qwsServer->d_func()->im_response(&cmd);
+ } else {
+ d->sendCommand(cmd);
+ }
+}
+
+void QWSDisplay::resetIM()
+{
+ sendIMUpdate(QWSInputMethod::Reset, -1, -1);
+}
+
+void QWSDisplay::sendIMMouseEvent(int index, bool isPress)
+{
+ QWSIMMouseCommand cmd;
+ cmd.simpleData.index = index;
+ cmd.simpleData.state = isPress ? QWSServer::MousePress : QWSServer::MouseRelease;
+ if (d->directServerConnection()) {
+ qwsServer->d_func()->send_im_mouse(&cmd);
+ } else {
+ d->sendCommand(cmd);
+ }
+}
+
+#endif
+
+int QWSDisplay::takeId()
+{
+ return d->takeId();
+}
+
+bool QWSDisplay::initLock(const QString &filename, bool create)
+{
+ if (!lock) {
+ lock = new QLock(filename, 'd', create);
+
+ if (!lock->isValid()) {
+ delete lock;
+ lock = 0;
+ return false;
+ }
+ }
+
+ return true;
+}
+
+void QWSDisplay::setSelectionOwner(int winId, const QTime &time)
+{
+ QWSSetSelectionOwnerCommand cmd;
+ cmd.simpleData.windowid = winId;
+ cmd.simpleData.hour = time.hour();
+ cmd.simpleData.minute = time.minute();
+ cmd.simpleData.sec = time.second();
+ cmd.simpleData.ms = time.msec();
+ d->sendCommand(cmd);
+}
+
+void QWSDisplay::convertSelection(int winId, int selectionProperty, const QString &mimeTypes)
+{
+#ifdef QT_NO_QWS_PROPERTIES
+ Q_UNUSED(mimeTypes);
+#else
+ // ### we need the atom/property thingy like in X here
+ addProperty(winId, QT_QWS_PROPERTY_CONVERTSELECTION);
+ setProperty(winId, QT_QWS_PROPERTY_CONVERTSELECTION,
+ int(QWSPropertyManager::PropReplace), mimeTypes.toLatin1());
+#endif
+ QWSConvertSelectionCommand cmd;
+ cmd.simpleData.requestor = winId;
+ cmd.simpleData.selection = selectionProperty;
+ cmd.simpleData.mimeTypes = QT_QWS_PROPERTY_CONVERTSELECTION;
+ d->sendCommand(cmd);
+}
+
+void QWSDisplay::defineCursor(int id, const QBitmap &curs, const QBitmap &mask,
+ int hotX, int hotY)
+{
+ const QImage cursImg = curs.toImage().convertToFormat(QImage::Format_MonoLSB);
+ const QImage maskImg = mask.toImage().convertToFormat(QImage::Format_MonoLSB);
+
+ QWSDefineCursorCommand cmd;
+ cmd.simpleData.width = curs.width();
+ cmd.simpleData.height = curs.height();
+ cmd.simpleData.hotX = hotX;
+ cmd.simpleData.hotY = hotY;
+ cmd.simpleData.id = id;
+
+
+ // must copy each scanline since there might be gaps between them
+ const int height = curs.height();
+ const int width = curs.width();
+ const int dst_bpl = (width + 7) / 8;
+
+ int dataLen = dst_bpl * height;
+ uchar *data = new uchar[dataLen*2];
+ uchar *dst = data;
+
+ int src_bpl = cursImg.bytesPerLine();
+ const uchar *cursSrc = cursImg.bits();
+ for (int i = 0; i < height; ++i) {
+ memcpy(dst, cursSrc + i*src_bpl, dst_bpl);
+ dst += dst_bpl;
+ }
+
+ src_bpl = maskImg.bytesPerLine();
+ const uchar *maskSrc = maskImg.bits();
+ for (int i = 0; i < height; ++i) {
+ memcpy(dst, maskSrc + i*src_bpl, dst_bpl);
+ dst += dst_bpl;
+ }
+
+ cmd.setData(reinterpret_cast<char*>(data), dataLen*2);
+ delete [] data;
+ d->sendCommand(cmd);
+}
+
+void QWSDisplay::destroyCursor(int id)
+{
+ QWSDefineCursorCommand cmd;
+ cmd.simpleData.width = 0;
+ cmd.simpleData.height = 0;
+ cmd.simpleData.hotX = 0;
+ cmd.simpleData.hotY = 0;
+ cmd.simpleData.id = id;
+ cmd.setData(0, 0);
+
+ d->sendCommand(cmd);
+}
+
+#ifndef QT_NO_SOUND
+void QWSDisplay::playSoundFile(const QString& f)
+{
+ QWSPlaySoundCommand cmd;
+ cmd.setFileName(f);
+ d->sendCommand(cmd);
+}
+#endif
+
+#ifndef QT_NO_COP
+void QWSDisplay::registerChannel(const QString& channel)
+{
+ QWSQCopRegisterChannelCommand reg;
+ reg.setChannel(channel);
+ qt_fbdpy->d->sendCommand(reg);
+}
+
+void QWSDisplay::sendMessage(const QString &channel, const QString &msg,
+ const QByteArray &data)
+{
+ QWSQCopSendCommand com;
+ com.setMessage(channel, msg, data);
+ qt_fbdpy->d->sendCommand(com);
+}
+
+void QWSDisplay::flushCommands()
+{
+ qt_fbdpy->d->flushCommands();
+}
+
+/*
+ caller deletes result
+*/
+QWSQCopMessageEvent* QWSDisplay::waitForQCopResponse()
+{
+ qt_fbdpy->d->waitForQCopResponse();
+ QWSQCopMessageEvent *e = static_cast<QWSQCopMessageEvent*>(qt_fbdpy->d->dequeue());
+ Q_ASSERT(e->type == QWSEvent::QCopMessage);
+ return e;
+}
+#endif
+
+void QWSDisplay::sendFontCommand(int type, const QByteArray &fontName)
+{
+ QWSFontCommand cmd;
+ cmd.simpleData.type = type;
+ cmd.setFontName(fontName);
+ d->sendCommand(cmd);
+}
+
+void QWSDisplay::setWindowCaption(QWidget *w, const QString &c)
+{
+ if (w->isWindow()) {
+ nameRegion(w->internalWinId(), w->objectName(), c);
+ static_cast<QETWidget *>(w)->repaintDecoration(qApp->desktop()->rect(), true);
+ }
+}
+
+void QWSDisplay::selectCursor(QWidget *w, unsigned int cursId)
+{
+ if (cursId != qt_last_cursor)
+ {
+ QWidget *top = w->window();
+ qt_last_cursor = cursId;
+ QWSSelectCursorCommand cmd;
+ cmd.simpleData.windowid = top->internalWinId();
+ cmd.simpleData.id = cursId;
+ d->sendCommand(cmd);
+ d->flush();
+ }
+}
+
+void QWSDisplay::setCursorPosition(int x, int y)
+{
+ QWSPositionCursorCommand cmd;
+ cmd.simpleData.newX = x;
+ cmd.simpleData.newY = y;
+ d->sendCommand(cmd);
+ d->flush();
+}
+
+void QWSDisplay::grabMouse(QWidget *w, bool grab)
+{
+ QWidget *top = w->window();
+ QWSGrabMouseCommand cmd;
+#ifdef QT_DEBUG
+ memset(cmd.simpleDataPtr, 0, sizeof(cmd.simpleData)); //shut up Valgrind
+#endif
+ cmd.simpleData.windowid = top->winId();
+ cmd.simpleData.grab = grab;
+ d->sendCommand(cmd);
+ d->flush();
+}
+
+void QWSDisplay::grabKeyboard(QWidget *w, bool grab)
+{
+ QWidget *top = w->window();
+ QWSGrabKeyboardCommand cmd;
+#ifdef QT_DEBUG
+ memset(cmd.simpleDataPtr, 0, sizeof(cmd.simpleData)); //shut up Valgrind
+#endif
+ cmd.simpleData.windowid = top->winId();
+ cmd.simpleData.grab = grab;
+ d->sendCommand(cmd);
+ d->flush();
+}
+
+QList<QWSWindowInfo> QWSDisplay::windowList()
+{
+ QList<QWSWindowInfo> ret;
+ if(d->directServerConnection()) {
+ QList<QWSInternalWindowInfo*> * qin=QWSServer::windowList();
+ for (int i = 0; i < qin->count(); ++i) {
+ QWSInternalWindowInfo * qwi = qin->at(i);
+ QWSWindowInfo tmp;
+ tmp.winid = qwi->winid;
+ tmp.clientid = qwi->clientid;
+ tmp.name = QString(qwi->name);
+ ret.append(tmp);
+ }
+ qDeleteAll(*qin);
+ delete qin;
+ }
+ return ret;
+}
+
+int QWSDisplay::windowAt(const QPoint &p)
+{
+ //### currently only implemented for the server process
+ int ret = 0;
+ if(d->directServerConnection()) {
+ QWSWindow *win = qwsServer->windowAt(p);
+ if (win)
+ return win->winId();
+ }
+ return ret;
+}
+
+void QWSDisplay::setRawMouseEventFilter(void (*filter)(QWSMouseEvent *))
+{
+ if (qt_fbdpy)
+ qt_fbdpy->d->setMouseFilter(filter);
+}
+
+/*!
+ \relates QScreen
+
+ Here it is. \a transformation and \a screenNo
+ */
+void QWSDisplay::setTransformation(int transformation, int screenNo)
+{
+ QWSScreenTransformCommand cmd;
+ cmd.setTransformation(screenNo, transformation);
+ QWSDisplay::instance()->d->sendCommand(cmd);
+}
+
+static bool qt_try_modal(QWidget *, QWSEvent *);
+
+/*****************************************************************************
+ qt_init() - initializes Qt/FB
+ *****************************************************************************/
+
+static void qt_set_qws_resources()
+
+{
+ if (QApplication::desktopSettingsAware())
+ QApplicationPrivate::qws_apply_settings();
+
+ if (appFont)
+ QApplication::setFont(QFont(QString::fromLocal8Bit(appFont)));
+
+ if (appBGCol || appBTNCol || appFGCol) {
+ (void) QApplication::style(); // trigger creation of application style and system palettes
+ QColor btn;
+ QColor bg;
+ QColor fg;
+ if (appBGCol)
+ bg = QColor(appBGCol);
+ else
+ bg = QApplicationPrivate::sys_pal->color(QPalette::Window);
+ if (appFGCol)
+ fg = QColor(appFGCol);
+ else
+ fg = QApplicationPrivate::sys_pal->color(QPalette::WindowText);
+ if (appBTNCol)
+ btn = QColor(appBTNCol);
+ else
+ btn = QApplicationPrivate::sys_pal->color(QPalette::Button);
+
+ int h,s,v;
+ fg.getHsv(&h,&s,&v);
+ QColor base = Qt::white;
+ bool bright_mode = false;
+ if (v >= 255 - 50) {
+ base = btn.darker(150);
+ bright_mode = true;
+ }
+
+ QPalette pal(fg, btn, btn.lighter(), btn.darker(), btn.darker(150), fg, Qt::white, base, bg);
+ if (bright_mode) {
+ pal.setColor(QPalette::HighlightedText, base);
+ pal.setColor(QPalette::Highlight, Qt::white);
+ } else {
+ pal.setColor(QPalette::HighlightedText, Qt::white);
+ pal.setColor(QPalette::Highlight, Qt::darkBlue);
+ }
+ QColor disabled((fg.red() + btn.red()) / 2,
+ (fg.green() + btn.green())/ 2,
+ (fg.blue() + btn.blue()) / 2);
+ pal.setColorGroup(QPalette::Disabled, disabled, btn, btn.lighter(125),
+ btn.darker(), btn.darker(150), disabled, Qt::white, Qt::white, bg);
+ if (bright_mode) {
+ pal.setColor(QPalette::Disabled, QPalette::HighlightedText, base);
+ pal.setColor(QPalette::Disabled, QPalette::Highlight, Qt::white);
+ } else {
+ pal.setColor(QPalette::Disabled, QPalette::HighlightedText, Qt::white);
+ pal.setColor(QPalette::Disabled, QPalette::Highlight, Qt::darkBlue);
+ }
+ QApplicationPrivate::setSystemPalette(pal);
+
+ }
+}
+
+void QApplicationPrivate::initializeWidgetPaletteHash()
+{
+}
+
+/*! \internal
+ apply the settings to the application
+*/
+bool QApplicationPrivate::qws_apply_settings()
+{
+#ifndef QT_NO_SETTINGS
+ QSettings settings(QSettings::UserScope, QLatin1String("Trolltech"));
+ settings.beginGroup(QLatin1String("Qt"));
+
+ QStringList strlist;
+ int i;
+ QPalette pal(Qt::black);
+ int groupCount = 0;
+ strlist = settings.value(QLatin1String("Palette/active")).toStringList();
+ if (strlist.count() == QPalette::NColorRoles) {
+ ++groupCount;
+ for (i = 0; i < QPalette::NColorRoles; i++)
+ pal.setColor(QPalette::Active, (QPalette::ColorRole) i,
+ QColor(strlist[i]));
+ }
+ strlist = settings.value(QLatin1String("Palette/inactive")).toStringList();
+ if (strlist.count() == QPalette::NColorRoles) {
+ ++groupCount;
+ for (i = 0; i < QPalette::NColorRoles; i++)
+ pal.setColor(QPalette::Inactive, (QPalette::ColorRole) i,
+ QColor(strlist[i]));
+ }
+ strlist = settings.value(QLatin1String("Palette/disabled")).toStringList();
+ if (strlist.count() == QPalette::NColorRoles) {
+ ++groupCount;
+ for (i = 0; i < QPalette::NColorRoles; i++)
+ pal.setColor(QPalette::Disabled, (QPalette::ColorRole) i,
+ QColor(strlist[i]));
+ }
+
+
+ if (groupCount == QPalette::NColorGroups)
+ QApplicationPrivate::setSystemPalette(pal);
+
+ QString str = settings.value(QLatin1String("font")).toString();
+ if (!str.isEmpty()) {
+ QFont font(QApplication::font());
+ font.fromString(str);
+ QApplicationPrivate::setSystemFont(font);
+ }
+
+ // read library (ie. plugin) path list
+ QString libpathkey =
+ QString::fromLatin1("%1.%2/libraryPath")
+ .arg(QT_VERSION >> 16)
+ .arg((QT_VERSION & 0xff00) >> 8);
+ QStringList pathlist = settings.value(libpathkey).toString().split(QLatin1Char(':'));
+#ifndef QT_NO_LIBRARY
+ if (! pathlist.isEmpty()) {
+ QStringList::ConstIterator it = pathlist.constBegin();
+ while (it != pathlist.constEnd())
+ QApplication::addLibraryPath(*it++);
+ }
+#endif
+
+ // read new QStyle
+ QString stylename = settings.value(QLatin1String("style")).toString();
+ if (QCoreApplication::startingUp()) {
+ if (!stylename.isEmpty() && !QApplicationPrivate::styleOverride)
+ QApplicationPrivate::styleOverride = new QString(stylename);
+ } else {
+ QApplication::setStyle(stylename);
+ }
+
+ int num =
+ settings.value(QLatin1String("doubleClickInterval"),
+ QApplication::doubleClickInterval()).toInt();
+ QApplication::setDoubleClickInterval(num);
+
+ num =
+ settings.value(QLatin1String("cursorFlashTime"),
+ QApplication::cursorFlashTime()).toInt();
+ QApplication::setCursorFlashTime(num);
+
+#ifndef QT_NO_WHEELEVENT
+ num =
+ settings.value(QLatin1String("wheelScrollLines"),
+ QApplication::wheelScrollLines()).toInt();
+ QApplication::setWheelScrollLines(num);
+#endif
+
+ QString colorspec = settings.value(QLatin1String("colorSpec"),
+ QVariant(QLatin1String("default"))).toString();
+ if (colorspec == QLatin1String("normal"))
+ QApplication::setColorSpec(QApplication::NormalColor);
+ else if (colorspec == QLatin1String("custom"))
+ QApplication::setColorSpec(QApplication::CustomColor);
+ else if (colorspec == QLatin1String("many"))
+ QApplication::setColorSpec(QApplication::ManyColor);
+ else if (colorspec != QLatin1String("default"))
+ colorspec = QLatin1String("default");
+
+#ifndef QT_NO_TEXTCODEC
+ QString defaultcodec = settings.value(QLatin1String("defaultCodec"),
+ QVariant(QLatin1String("none"))).toString();
+ if (defaultcodec != QLatin1String("none")) {
+ QTextCodec *codec = QTextCodec::codecForName(defaultcodec.toLatin1());
+ if (codec)
+ QTextCodec::setCodecForTr(codec);
+ }
+#endif
+
+ int w = settings.value(QLatin1String("globalStrut/width")).toInt();
+ int h = settings.value(QLatin1String("globalStrut/height")).toInt();
+ QSize strut(w, h);
+ if (strut.isValid())
+ QApplication::setGlobalStrut(strut);
+
+ QStringList effects = settings.value(QLatin1String("GUIEffects")).toStringList();
+ QApplication::setEffectEnabled(Qt::UI_General,
+ effects.contains(QLatin1String("general")));
+ QApplication::setEffectEnabled(Qt::UI_AnimateMenu,
+ effects.contains(QLatin1String("animatemenu")));
+ QApplication::setEffectEnabled(Qt::UI_FadeMenu,
+ effects.contains(QLatin1String("fademenu")));
+ QApplication::setEffectEnabled(Qt::UI_AnimateCombo,
+ effects.contains(QLatin1String("animatecombo")));
+ QApplication::setEffectEnabled(Qt::UI_AnimateTooltip,
+ effects.contains(QLatin1String("animatetooltip")));
+ QApplication::setEffectEnabled(Qt::UI_FadeTooltip,
+ effects.contains(QLatin1String("fadetooltip")));
+ QApplication::setEffectEnabled(Qt::UI_AnimateToolBox,
+ effects.contains(QLatin1String("animatetoolbox")));
+
+ settings.beginGroup(QLatin1String("Font Substitutions"));
+ QStringList fontsubs = settings.childKeys();
+ if (!fontsubs.isEmpty()) {
+ QStringList::Iterator it = fontsubs.begin();
+ for (; it != fontsubs.end(); ++it) {
+ QString fam = *it;
+ QStringList subs = settings.value(fam).toStringList();
+ QFont::insertSubstitutions(fam, subs);
+ }
+ }
+ settings.endGroup();
+
+ settings.endGroup(); // Qt
+
+ settings.beginGroup(QLatin1String("QWS Font Fallbacks"));
+ if (!settings.childKeys().isEmpty()) {
+ // from qfontdatabase_qws.cpp
+ extern void qt_applyFontDatabaseSettings(const QSettings &);
+ qt_applyFontDatabaseSettings(settings);
+ }
+ settings.endGroup();
+
+ return true;
+#else
+ return false;
+#endif // QT_NO_SETTINGS
+}
+
+
+
+static void init_display()
+{
+ if (qt_fbdpy) return; // workaround server==client case
+
+ // Connect to FB server
+ qt_fbdpy = new QWSDisplay();
+
+ // Get display parameters
+ // Set paintdevice parameters
+ // XXX initial info sent from server
+ // Misc. initialization
+
+ QColormap::initialize();
+ QFont::initialize();
+#ifndef QT_NO_CURSOR
+ QCursorData::initialize();
+#endif
+
+ qApp->setObjectName(appName);
+
+ if (!QApplicationPrivate::sys_font) {
+#ifdef QT_NO_FREETYPE
+ QFont f = QFont(QLatin1String("helvetica"), 10);
+#else
+ QFont f = QFont(QLatin1String("DejaVu Sans"), 12);
+#endif
+ QApplicationPrivate::setSystemFont(f);
+ }
+ qt_set_qws_resources();
+}
+
+void qt_init_display()
+{
+ qt_is_gui_used = true;
+ qws_single_process = true;
+ init_display();
+}
+
+static bool read_bool_env_var(const char *var, bool defaultvalue)
+{
+ // returns true if env variable is set to non-zero
+ // returns false if env var is set to zero
+ // returns defaultvalue if env var not set
+ char *x = ::getenv(var);
+ return (x && *x) ? (strcmp(x,"0") != 0) : defaultvalue;
+}
+
+static int read_int_env_var(const char *var, int defaultvalue)
+{
+ bool ok;
+ int r = qgetenv(var).toInt(&ok);
+ return ok ? r : defaultvalue;
+}
+
+void qt_init(QApplicationPrivate *priv, int type)
+{
+#ifdef QT_NO_QWS_MULTIPROCESS
+ if (type == QApplication::GuiClient)
+ type = QApplication::GuiServer;
+#endif
+ if (type == QApplication::GuiServer)
+ qt_is_gui_used = false; //we'll turn it on in a second
+ qws_sw_cursor = read_bool_env_var("QWS_SW_CURSOR",qws_sw_cursor);
+ qws_screen_is_interlaced = read_bool_env_var("QWS_INTERLACE",false);
+
+ const char *display = ::getenv("QWS_DISPLAY");
+ if (display)
+ qws_display_spec = display; // since we setenv later!
+
+ //qws_savefonts = qgetenv("QWS_SAVEFONTS") != 0;
+ //qws_shared_memory = qgetenv("QWS_NOSHARED") == 0;
+
+ mouse_double_click_distance = read_int_env_var("QWS_DBLCLICK_DISTANCE", 5);
+
+ int flags = 0;
+ char *p;
+ int argc = priv->argc;
+ char **argv = priv->argv;
+ int j;
+
+ // Set application name
+
+ if (argv && *argv) { //apparently, we allow people to pass 0 on the other platforms
+ p = strrchr(argv[0], '/');
+ appName = QString::fromLocal8Bit(p ? p + 1 : argv[0]);
+ }
+
+ // Get command line params
+
+ j = argc ? 1 : 0;
+ QString decoration;
+ for (int i=1; i<argc; i++) {
+ if (argv[i] && *argv[i] != '-') {
+ argv[j++] = argv[i];
+ continue;
+ }
+ QByteArray arg = argv[i];
+ if (arg == "-fn" || arg == "-font") {
+ if (++i < argc)
+ appFont = argv[i];
+ } else if (arg == "-bg" || arg == "-background") {
+ if (++i < argc)
+ appBGCol = argv[i];
+ } else if (arg == "-btn" || arg == "-button") {
+ if (++i < argc)
+ appBTNCol = argv[i];
+ } else if (arg == "-fg" || arg == "-foreground") {
+ if (++i < argc)
+ appFGCol = argv[i];
+ } else if (arg == "-name") {
+ if (++i < argc)
+ appName = QString::fromLocal8Bit(argv[i]);
+ } else if (arg == "-title") {
+ if (++i < argc)
+ mwTitle = argv[i];
+ } else if (arg == "-geometry") {
+ if (++i < argc)
+ mwGeometry = argv[i];
+ } else if (arg == "-shared") {
+ qws_shared_memory = true;
+ } else if (arg == "-noshared") {
+ qws_shared_memory = false;
+ } else if (arg == "-savefonts") {
+ qws_savefonts = true;
+ } else if (arg == "-nosavefonts") {
+ qws_savefonts = false;
+ } else if (arg == "-swcursor") {
+ qws_sw_cursor = true;
+ } else if (arg == "-noswcursor") {
+ qws_sw_cursor = false;
+ } else if (arg == "-keyboard") {
+ flags &= ~QWSServer::DisableKeyboard;
+ } else if (arg == "-nokeyboard") {
+ flags |= QWSServer::DisableKeyboard;
+ } else if (arg == "-mouse") {
+ flags &= ~QWSServer::DisableMouse;
+ } else if (arg == "-nomouse") {
+ flags |= QWSServer::DisableMouse;
+ } else if (arg == "-qws") {
+ type = QApplication::GuiServer;
+ } else if (arg == "-interlaced") {
+ qws_screen_is_interlaced = true;
+ } else if (arg == "-display") {
+ if (++i < argc)
+ qws_display_spec = argv[i];
+ } else if (arg == "-decoration") {
+ if (++i < argc)
+ decoration = QString::fromLocal8Bit(argv[i]);
+ } else {
+ argv[j++] = argv[i];
+ }
+ }
+ if(j < priv->argc) {
+ priv->argv[j] = 0;
+ priv->argc = j;
+ }
+
+ mouseInWidget = new QPointer<QWidget>;
+
+ const QString disp = QString::fromLatin1(qws_display_spec);
+ QRegExp regexp(QLatin1String(":(\\d+)$"));
+ if (regexp.lastIndexIn(disp) != -1) {
+ const QString capture = regexp.cap(1);
+ bool ok = false;
+ int id = capture.toInt(&ok);
+ if (ok)
+ qws_display_id = id;
+ }
+
+ if (type == QApplication::GuiServer) {
+ qt_appType = QApplication::Type(type);
+ qws_single_process = true;
+ QWSServer::startup(flags);
+ setenv("QWS_DISPLAY", qws_display_spec.constData(), 0);
+ }
+
+ if(qt_is_gui_used) {
+ init_display();
+#ifndef QT_NO_QWS_MANAGER
+ if (decoration.isEmpty() && !qws_decoration) {
+ const QStringList keys = QDecorationFactory::keys();
+ if (!keys.isEmpty())
+ decoration = keys.first();
+ }
+ if (!decoration.isEmpty())
+ qws_decoration = QApplication::qwsSetDecoration(decoration);
+#endif // QT_NO_QWS_MANAGER
+#ifndef QT_NO_QWS_INPUTMETHODS
+ qApp->setInputContext(new QWSInputContext);
+#endif
+ }
+
+/*### convert interlace style
+ if (qws_screen_is_interlaced)
+ QApplication::setStyle(new QInterlaceStyle);
+*/
+}
+
+/*****************************************************************************
+ qt_cleanup() - cleans up when the application is finished
+ *****************************************************************************/
+
+void qt_cleanup()
+{
+ QPixmapCache::clear();
+#ifndef QT_NO_CURSOR
+ QCursorData::cleanup();
+#endif
+ QFont::cleanup();
+ QColormap::cleanup();
+
+ if (qws_single_process) {
+ QWSServer::closedown();
+ }
+
+ qDeleteAll(outgoing);
+ outgoing.clear();
+ qDeleteAll(incoming);
+ incoming.clear();
+
+ if (qt_is_gui_used) {
+ delete qt_fbdpy;
+ }
+ qt_fbdpy = 0;
+
+#ifndef QT_NO_QWS_MANAGER
+ delete qws_decoration;
+ qws_decoration = 0;
+#endif
+
+ delete mouseInWidget;
+ mouseInWidget = 0;
+}
+
+
+/*****************************************************************************
+ Platform specific global and internal functions
+ *****************************************************************************/
+
+QString QApplicationPrivate::appName() const // get application name
+{
+ return ::appName;
+}
+
+/*****************************************************************************
+ Platform specific QApplication members
+ *****************************************************************************/
+
+#define NoValue 0x0000
+#define XValue 0x0001
+#define YValue 0x0002
+#define WidthValue 0x0004
+#define HeightValue 0x0008
+#define AllValues 0x000F
+#define XNegative 0x0010
+#define YNegative 0x0020
+
+/* Copyright notice for ReadInteger and parseGeometry
+
+Copyright (c) 1985, 1986, 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.
+
+*/
+/*
+ * XParseGeometry parses strings of the form
+ * "=<width>x<height>{+-}<xoffset>{+-}<yoffset>", where
+ * width, height, xoffset, and yoffset are unsigned integers.
+ * Example: "=80x24+300-49"
+ * The equal sign is optional.
+ * It returns a bitmask that indicates which of the four values
+ * were actually found in the string. For each value found,
+ * the corresponding argument is updated; for each value
+ * not found, the corresponding argument is left unchanged.
+ */
+
+static int
+ReadInteger(char *string, char **NextString)
+{
+ register int Result = 0;
+ int Sign = 1;
+
+ if (*string == '+')
+ string++;
+ else if (*string == '-')
+ {
+ string++;
+ Sign = -1;
+ }
+ for (; (*string >= '0') && (*string <= '9'); string++)
+ {
+ Result = (Result * 10) + (*string - '0');
+ }
+ *NextString = string;
+ if (Sign >= 0)
+ return Result;
+ else
+ return -Result;
+}
+
+static int parseGeometry(const char* string,
+ int* x, int* y, int* width, int* height)
+{
+ int mask = NoValue;
+ register char *strind;
+ unsigned int tempWidth=0, tempHeight=0;
+ int tempX=0, tempY=0;
+ char *nextCharacter;
+
+ if (!string || (*string == '\0')) return mask;
+ if (*string == '=')
+ string++; /* ignore possible '=' at beg of geometry spec */
+
+ strind = const_cast<char *>(string);
+ if (*strind != '+' && *strind != '-' && *strind != 'x') {
+ tempWidth = ReadInteger(strind, &nextCharacter);
+ if (strind == nextCharacter)
+ return 0;
+ strind = nextCharacter;
+ mask |= WidthValue;
+ }
+
+ if (*strind == 'x' || *strind == 'X') {
+ strind++;
+ tempHeight = ReadInteger(strind, &nextCharacter);
+ if (strind == nextCharacter)
+ return 0;
+ strind = nextCharacter;
+ mask |= HeightValue;
+ }
+
+ if ((*strind == '+') || (*strind == '-')) {
+ if (*strind == '-') {
+ strind++;
+ tempX = -ReadInteger(strind, &nextCharacter);
+ if (strind == nextCharacter)
+ return 0;
+ strind = nextCharacter;
+ mask |= XNegative;
+
+ }
+ else
+ { strind++;
+ tempX = ReadInteger(strind, &nextCharacter);
+ if (strind == nextCharacter)
+ return 0;
+ strind = nextCharacter;
+ }
+ mask |= XValue;
+ if ((*strind == '+') || (*strind == '-')) {
+ if (*strind == '-') {
+ strind++;
+ tempY = -ReadInteger(strind, &nextCharacter);
+ if (strind == nextCharacter)
+ return 0;
+ strind = nextCharacter;
+ mask |= YNegative;
+
+ }
+ else
+ {
+ strind++;
+ tempY = ReadInteger(strind, &nextCharacter);
+ if (strind == nextCharacter)
+ return 0;
+ strind = nextCharacter;
+ }
+ mask |= YValue;
+ }
+ }
+
+ /* If strind isn't at the end of the string then it's an invalid
+ geometry specification. */
+
+ if (*strind != '\0') return 0;
+
+ if (mask & XValue)
+ *x = tempX;
+ if (mask & YValue)
+ *y = tempY;
+ if (mask & WidthValue)
+ *width = tempWidth;
+ if (mask & HeightValue)
+ *height = tempHeight;
+ return mask;
+}
+
+#ifdef QT3_SUPPORT
+void QApplication::setMainWidget(QWidget *mainWidget)
+{
+ QApplicationPrivate::main_widget = mainWidget;
+ if (QApplicationPrivate::main_widget) // give WM command line
+ QApplicationPrivate::applyQWSSpecificCommandLineArguments(QApplicationPrivate::main_widget);
+}
+#endif
+
+void QApplicationPrivate::applyQWSSpecificCommandLineArguments(QWidget *main_widget)
+{
+ static bool beenHereDoneThat = false;
+ if (beenHereDoneThat)
+ return;
+ beenHereDoneThat = true;
+ if (qApp->windowIcon().isNull() && main_widget->testAttribute(Qt::WA_SetWindowIcon))
+ qApp->setWindowIcon(main_widget->windowIcon());
+ if (mwTitle) // && main_widget->windowTitle().isEmpty())
+ main_widget->setWindowTitle(QString::fromLocal8Bit(mwTitle));
+ if (mwGeometry) { // parse geometry
+ int x = 0;
+ int y = 0;
+ int w = 0;
+ int h = 0;
+ int m = parseGeometry(mwGeometry, &x, &y, &w, &h);
+ QSize minSize = main_widget->minimumSize();
+ QSize maxSize = main_widget->maximumSize();
+ if ((m & XValue) == 0)
+ x = main_widget->geometry().x();
+ if ((m & YValue) == 0)
+ y = main_widget->geometry().y();
+ if ((m & WidthValue) == 0)
+ w = main_widget->width();
+ if ((m & HeightValue) == 0)
+ h = main_widget->height();
+ w = qMin(w,maxSize.width());
+ h = qMin(h,maxSize.height());
+ w = qMax(w,minSize.width());
+ h = qMax(h,minSize.height());
+ if ((m & XNegative)) {
+ x = qApp->desktop()->width() + x - w;
+ x -= (main_widget->frameGeometry().width() - main_widget->width()) / 2;
+ } else {
+ x += (main_widget->geometry().x() - main_widget->x());
+ }
+ if ((m & YNegative)) {
+ y = qApp->desktop()->height() + y - h;
+ } else {
+ y += (main_widget->geometry().y() - main_widget->y());
+ }
+
+ main_widget->setGeometry(x, y, w, h);
+ }
+}
+
+/*****************************************************************************
+ QApplication cursor stack
+ *****************************************************************************/
+#ifndef QT_NO_CURSOR
+void QApplication::setOverrideCursor(const QCursor &cursor)
+{
+ qApp->d_func()->cursor_list.prepend(cursor);
+
+ QWidget *w = QWidget::mouseGrabber();
+ if (!w && qt_last_x)
+ w = topLevelAt(*qt_last_x, *qt_last_y);
+ if (!w)
+ w = desktop();
+ QPaintDevice::qwsDisplay()->selectCursor(w, qApp->d_func()->cursor_list.first().handle());
+}
+
+void QApplication::restoreOverrideCursor()
+{
+ if (qApp->d_func()->cursor_list.isEmpty())
+ return;
+ qApp->d_func()->cursor_list.removeFirst();
+
+ QWidget *w = QWidget::mouseGrabber();
+ if (!w && qt_last_x)
+ w = topLevelAt(*qt_last_x, *qt_last_y);
+ if (!w)
+ w = desktop();
+
+ int cursor_handle = Qt::ArrowCursor;
+ if (qApp->d_func()->cursor_list.isEmpty()) {
+ qws_overrideCursor = false;
+ QWidget *upw = QApplication::widgetAt(*qt_last_x, *qt_last_y);
+ if (upw)
+ cursor_handle = upw->cursor().handle();
+ } else {
+ cursor_handle = qApp->d_func()->cursor_list.first().handle();
+ }
+ QPaintDevice::qwsDisplay()->selectCursor(w, cursor_handle);
+}
+#endif// QT_NO_CURSOR
+
+
+
+/*****************************************************************************
+ Routines to find a Qt widget from a screen position
+ *****************************************************************************/
+
+/*!
+ \internal
+*/
+QWidget *QApplicationPrivate::findWidget(const QObjectList& list,
+ const QPoint &pos, bool rec)
+{
+ QWidget *w;
+
+ for (int i = list.size()-1; i >= 0; --i) {
+ if (list.at(i)->isWidgetType()) {
+ w = static_cast<QWidget*>(list.at(i));
+ if (w->isVisible() && !w->testAttribute(Qt::WA_TransparentForMouseEvents) && w->geometry().contains(pos)
+ && (!w->d_func()->extra || w->d_func()->extra->mask.isEmpty() || w->d_func()->extra->mask.contains(pos - w->geometry().topLeft()) )) {
+ if (!rec)
+ return w;
+ QWidget *c = w->childAt(w->mapFromParent(pos));
+ return c ? c : w;
+ }
+ }
+ }
+ return 0;
+}
+
+
+QWidget *QApplication::topLevelAt(const QPoint &pos)
+{
+ //### QWSDisplay::windowAt() is currently only implemented in the server process
+ int winId = QPaintDevice::qwsDisplay()->windowAt(pos);
+ if (winId !=0)
+ return QWidget::find(winId);
+
+#if 1
+ // fallback implementation for client processes
+//### This is slightly wrong: we have no guarantee that the list is in
+//### stacking order, so if the topmost window is transparent, we may
+//### return the wrong widget
+
+ QWidgetList list = topLevelWidgets();
+ for (int i = list.size()-1; i >= 0; --i) {
+ QWidget *w = list[i];
+ if (w != QApplication::desktop() &&
+ w->isVisible() && w->d_func()->localAllocatedRegion().contains(w->mapFromParent(pos))
+ )
+ return w;
+ }
+#endif
+ return 0;
+}
+
+void QApplication::beep()
+{
+}
+
+void QApplication::alert(QWidget *, int)
+{
+}
+
+/*!
+ \internal
+*/
+int QApplication::qwsProcessEvent(QWSEvent* event)
+{
+ Q_D(QApplication);
+ QScopedLoopLevelCounter loopLevelCounter(d->threadData);
+ int oldstate = -1;
+ bool isMove = false;
+ if (event->type == QWSEvent::Mouse) {
+ QWSMouseEvent::SimpleData &mouse = event->asMouse()->simpleData;
+ isMove = mouse_x_root != mouse.x_root || mouse_y_root != mouse.y_root;
+ oldstate = mouse_state;
+ mouse_x_root = mouse.x_root;
+ mouse_y_root = mouse.y_root;
+ mouse_state = mouse.state;
+ }
+
+ long unused;
+ if (filterEvent(event, &unused)) // send through app filter
+ return 1;
+
+ if (qwsEventFilter(event)) // send through app filter
+ return 1;
+
+
+#ifndef QT_NO_QWS_PROPERTIES
+ if (event->type == QWSEvent::PropertyNotify) {
+ QWSPropertyNotifyEvent *e = static_cast<QWSPropertyNotifyEvent*>(event);
+ if (e->simpleData.property == 424242) { // Clipboard
+#ifndef QT_NO_CLIPBOARD
+ if (qt_clipboard) {
+ QClipboardEvent e(reinterpret_cast<QEventPrivate*>(event));
+ QApplication::sendEvent(qt_clipboard, &e);
+ }
+#endif
+ }
+ }
+#endif //QT_NO_QWS_PROPERTIES
+#ifndef QT_NO_COP
+ else if (event->type == QWSEvent::QCopMessage) {
+ QWSQCopMessageEvent *e = static_cast<QWSQCopMessageEvent*>(event);
+ QCopChannel::sendLocally(QLatin1String(e->channel), QLatin1String(e->message), e->data);
+ return 0;
+ }
+#endif
+#if !defined(QT_NO_QWS_QPF2)
+ else if (event->type == QWSEvent::Font) {
+ QWSFontEvent *e = static_cast<QWSFontEvent *>(event);
+ if (e->simpleData.type == QWSFontEvent::FontRemoved) {
+ QFontCache::instance()->removeEngineForFont(e->fontName);
+ }
+ }
+#endif
+
+ QPointer<QETWidget> widget = static_cast<QETWidget*>(QWidget::find(WId(event->window())));
+#ifdef Q_BACKINGSTORE_SUBSURFACES
+ if (!widget) { // XXX: hw: hack for accessing subsurfaces
+ extern QWSWindowSurface* qt_findWindowSurface(int);
+ QWSWindowSurface *s = qt_findWindowSurface(event->window());
+ if (s)
+ widget = static_cast<QETWidget*>(s->window());
+ }
+#endif
+
+#ifndef QT_NO_DIRECTPAINTER
+ if (!widget && d->directPainters) {
+ QDirectPainter *dp = d->directPainters->value(WId(event->window()));
+ if (dp == 0) {
+ } else if (event->type == QWSEvent::Region) {
+ QWSRegionEvent *e = static_cast<QWSRegionEvent*>(event);
+ QRegion reg;
+ reg.setRects(e->rectangles, e->simpleData.nrectangles);
+ qt_directpainter_region(dp, reg, e->simpleData.type);
+ return 1;
+#ifndef QT_NO_QWSEMBEDWIDGET
+ } else if (event->type == QWSEvent::Embed) {
+ QWSEmbedEvent *e = static_cast<QWSEmbedEvent*>(event);
+ qt_directpainter_embedevent(dp, e);
+ return 1;
+ #endif // QT_NO_QWSEMBEDWIDGET
+ }
+ }
+#endif // QT_NO_DIRECTPAINTER
+
+#ifndef QT_NO_QWS_MANAGER
+ if (d->last_manager && event->type == QWSEvent::Mouse) {
+ QPoint pos(event->asMouse()->simpleData.x_root, event->asMouse()->simpleData.y_root);
+ if (!d->last_manager->cachedRegion().contains(pos)) {
+ // MouseEvent not yet delivered, so QCursor::pos() is not yet updated, sending 2 x pos
+ QMouseEvent outside(QEvent::MouseMove, pos, pos, Qt::NoButton, 0, 0);
+ QApplication::sendSpontaneousEvent(d->last_manager, &outside);
+ d->last_manager = 0;
+ qt_last_cursor = 0xffffffff; //decoration is like another window; must redo cursor
+ }
+ }
+#endif // QT_NO_QWS_MANAGER
+
+ QETWidget *keywidget=0;
+ bool grabbed=false;
+ if (event->type==QWSEvent::Key || event->type == QWSEvent::IMEvent || event->type == QWSEvent::IMQuery) {
+ keywidget = static_cast<QETWidget*>(QWidget::keyboardGrabber());
+ if (keywidget) {
+ grabbed = true;
+ } else {
+ if (QWidget *popup = QApplication::activePopupWidget()) {
+ if (popup->focusWidget())
+ keywidget = static_cast<QETWidget*>(popup->focusWidget());
+ else
+ keywidget = static_cast<QETWidget*>(popup);
+ } else if (QApplicationPrivate::focus_widget && QApplicationPrivate::focus_widget->isVisible())
+ keywidget = static_cast<QETWidget*>(QApplicationPrivate::focus_widget);
+ else if (widget)
+ keywidget = static_cast<QETWidget*>(widget->window());
+ }
+ } else if (event->type==QWSEvent::MaxWindowRect) {
+ QRect r = static_cast<QWSMaxWindowRectEvent*>(event)->simpleData.rect;
+ setMaxWindowRect(r);
+ return 0;
+#ifndef QT_NO_QWS_DYNAMICSCREENTRANSFORMATION
+ } else if (event->type == QWSEvent::ScreenTransformation) {
+ QWSScreenTransformationEvent *pe = static_cast<QWSScreenTransformationEvent*>(event);
+ setScreenTransformation(pe->simpleData.screen,
+ pe->simpleData.transformation);
+ return 0;
+#endif
+ } else if (widget && event->type==QWSEvent::Mouse) {
+ // The mouse event is to one of my top-level widgets
+ // which one?
+ const int btnMask = Qt::LeftButton | Qt::RightButton | Qt::MidButton;
+ QPoint p(event->asMouse()->simpleData.x_root,
+ event->asMouse()->simpleData.y_root);
+ int mouseButtonState = event->asMouse()->simpleData.state & btnMask;
+ static int btnstate = 0;
+
+ QETWidget *w = static_cast<QETWidget*>(QWidget::mouseGrabber());
+ if (w && !mouseButtonState && qt_pressGrab == w)
+ qt_pressGrab = 0;
+#ifndef QT_NO_QWS_MANAGER
+ if (!w)
+ w = static_cast<QETWidget*>(QWSManager::grabbedMouse());
+#endif
+ if (w) {
+ // Our mouse is grabbed - send it.
+ widget = w;
+ btnstate = mouseButtonState;
+ } else {
+ static QWidget *gw = 0;
+ // Three jobs to do here:
+ // 1. find the child widget this event belongs to.
+ // 2. make sure the cursor is correct.
+ // 3. handle implicit mouse grab due to button press.
+ w = widget; // w is the widget the cursor is in.
+
+ //### ??? alloc_region
+ //#### why should we get events outside alloc_region ????
+ if (1 /*widget->data->alloc_region.contains(dp) */) {
+ // Find the child widget that the cursor is in.
+ w = static_cast<QETWidget*>(widget->childAt(widget->mapFromParent(p)));
+ if (!w)
+ w = widget;
+#ifndef QT_NO_CURSOR
+ // Update Cursor.
+ if (!gw || gw != w || qt_last_cursor == 0xffffffff) {
+ QCursor *curs = 0;
+ if (!qApp->d_func()->cursor_list.isEmpty())
+ curs = &qApp->d_func()->cursor_list.first();
+ else if (w->d_func()->extraData())
+ curs = w->d_func()->extraData()->curs;
+ QWidget *pw = w;
+ // If this widget has no cursor set, try parent.
+ while (!curs) {
+ pw = pw->parentWidget();
+ if (!pw)
+ break;
+ if (pw->d_func()->extraData())
+ curs = pw->d_func()->extraData()->curs;
+ }
+ if (!qws_overrideCursor) {
+ if (curs)
+ QPaintDevice::qwsDisplay()->selectCursor(widget, curs->handle());
+ else
+ QPaintDevice::qwsDisplay()->selectCursor(widget, Qt::ArrowCursor);
+ }
+ }
+#endif
+ gw = w;
+ } else {
+ // This event is not for any of our widgets
+ gw = 0;
+ }
+ if (mouseButtonState && !btnstate) {
+ // The server has grabbed the mouse for us.
+ // Remember which of my widgets has it.
+ qt_pressGrab = w;
+ if (!widget->isActiveWindow() &&
+ (!app_do_modal || QApplication::activeModalWidget() == widget) &&
+ !((widget->windowFlags() & Qt::FramelessWindowHint) || (widget->windowType() == Qt::Tool))) {
+ widget->activateWindow();
+ if (widget->raiseOnClick())
+ widget->raise();
+ }
+ }
+ btnstate = mouseButtonState;
+ widget = w;
+ }
+ }
+
+ if (!widget) { // don't know this window
+ if (!QWidget::mouseGrabber()
+#ifndef QT_NO_QWS_MANAGER
+ && !QWSManager::grabbedMouse()
+#endif
+ ) {
+ qt_last_cursor = 0xffffffff; // cursor can be changed by another application
+ }
+
+ QWidget* popup = QApplication::activePopupWidget();
+ if (popup) {
+
+ /*
+ That is more than suboptimal. The real solution should
+ do some keyevent and buttonevent translation, so that
+ the popup still continues to work as the user expects.
+ Unfortunately this translation is currently only
+ possible with a known widget. I'll change that soon
+ (Matthias).
+ */
+
+ // Danger - make sure we don't lock the server
+ switch (event->type) {
+ case QWSEvent::Mouse:
+ case QWSEvent::Key:
+ do {
+ popup->close();
+ } while ((popup = qApp->activePopupWidget()));
+ return 1;
+ }
+ }
+ if (event->type == QWSEvent::Mouse && *mouseInWidget) {
+ QApplicationPrivate::dispatchEnterLeave(0, *mouseInWidget);
+ (*mouseInWidget) = 0;
+ }
+ return -1;
+ }
+
+ if (app_do_modal) // modal event handling
+ if (!qt_try_modal(widget, event)) {
+ return 1;
+ }
+
+ if (widget->qwsEvent(event)) // send through widget filter
+ return 1;
+ switch (event->type) {
+
+ case QWSEvent::Mouse: { // mouse event
+ QWSMouseEvent *me = event->asMouse();
+ QWSMouseEvent::SimpleData &mouse = me->simpleData;
+
+ // Translate a QWS event into separate move
+ // and press/release events
+ // Beware of reentrancy: we can enter a modal state
+ // inside translateMouseEvent
+
+ if (isMove) {
+ QWSMouseEvent move = *me;
+ move.simpleData.state = oldstate;
+ widget->translateMouseEvent(&move, oldstate);
+ }
+ if ((mouse.state&Qt::MouseButtonMask) != (oldstate&Qt::MouseButtonMask)) {
+ widget->translateMouseEvent(me, oldstate);
+ }
+
+ if (mouse.delta != 0)
+ widget->translateWheelEvent(me);
+
+ if (qt_button_down && (mouse_state & Qt::MouseButtonMask) == 0)
+ qt_button_down = 0;
+
+ break;
+ }
+ case QWSEvent::Key: // keyboard event
+ if (keywidget) // should always exist
+ keywidget->translateKeyEvent(static_cast<QWSKeyEvent*>(event), grabbed);
+ break;
+
+#ifndef QT_NO_QWS_INPUTMETHODS
+ case QWSEvent::IMEvent:
+ if (keywidget) // should always exist
+ QWSInputContext::translateIMEvent(keywidget, static_cast<QWSIMEvent*>(event));
+ break;
+
+ case QWSEvent::IMQuery:
+ if (keywidget) // should always exist
+ QWSInputContext::translateIMQueryEvent(keywidget, static_cast<QWSIMQueryEvent*>(event));
+ break;
+
+ case QWSEvent::IMInit:
+ QWSInputContext::translateIMInitEvent(static_cast<QWSIMInitEvent*>(event));
+ break;
+#endif
+ case QWSEvent::Region:
+ widget->translateRegionEvent(static_cast<QWSRegionEvent*>(event));
+ break;
+ case QWSEvent::Focus:
+ if ((static_cast<QWSFocusEvent*>(event))->simpleData.get_focus) {
+ if (widget == static_cast<QWidget *>(desktop()))
+ return true; // not interesting
+ if (activeWindow() != widget) {
+ setActiveWindow(widget);
+ if (QApplicationPrivate::active_window)
+ static_cast<QETWidget *>(QApplicationPrivate::active_window)->repaintDecoration(desktop()->rect(), false);
+ if (widget && !d->inPopupMode()) {
+ QWidget *w = widget->focusWidget();
+ while (w && w->focusProxy())
+ w = w->focusProxy();
+ if (w && (w->focusPolicy() != Qt::NoFocus))
+ w->setFocus();
+ else
+ widget->QWidget::focusNextPrevChild(true);
+ if (!QApplicationPrivate::focus_widget) {
+ if (widget->focusWidget())
+ widget->focusWidget()->setFocus();
+ else
+ widget->window()->setFocus();
+ }
+ }
+ }
+ } else { // lost focus
+ if (widget == static_cast<QWidget *>(desktop()))
+ return true; // not interesting
+ if (QApplicationPrivate::focus_widget) {
+ QETWidget *old = static_cast<QETWidget *>(QApplicationPrivate::active_window);
+ setActiveWindow(0);
+ qt_last_cursor = 0xffffffff;
+ //QApplicationPrivate::active_window = 0;
+ if (old)
+ old->repaintDecoration(desktop()->rect(), false);
+ /* activateWindow() sends focus events
+ QApplication::setFocusWidget(0);
+ */
+ }
+ }
+ break;
+
+ case QWSEvent::WindowOperation:
+ if (static_cast<QWidget *>(widget) == desktop())
+ return true;
+ switch ((static_cast<QWSWindowOperationEvent *>(event))->simpleData.op) {
+ case QWSWindowOperationEvent::Show:
+ widget->show();
+ break;
+ case QWSWindowOperationEvent::Hide:
+ widget->hide();
+ break;
+ case QWSWindowOperationEvent::ShowMaximized:
+ widget->showMaximized();
+ break;
+ case QWSWindowOperationEvent::ShowMinimized:
+ widget->showMinimized();
+ break;
+ case QWSWindowOperationEvent::ShowNormal:
+ widget->showNormal();
+ break;
+ case QWSWindowOperationEvent::Close:
+ widget->d_func()->close_helper(QWidgetPrivate::CloseWithSpontaneousEvent);
+ break;
+ }
+ break;
+#ifndef QT_NO_QWSEMBEDWIDGET
+ case QWSEvent::Embed:
+ widget->translateEmbedEvent(static_cast<QWSEmbedEvent*>(event));
+ break;
+#endif
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+/*!
+ \fn bool QApplication::qwsEventFilter(QWSEvent *event)
+
+ This virtual function is only implemented under Qt for Embedded Linux.
+
+ If you create an application that inherits QApplication and
+ reimplement this function, you get direct access to all QWS (Q
+ Window System) events that the are received from the QWS master
+ process. The events are passed in the \a event parameter.
+
+ Return true if you want to stop the event from being processed.
+ Return false for normal event dispatching. The default
+ implementation returns false.
+*/
+bool QApplication::qwsEventFilter(QWSEvent *)
+{
+ return false;
+}
+
+/*!
+ Set Qt for Embedded Linux custom color table.
+
+ Qt for Embedded Linux on 8-bpp displays allocates a standard 216 color cube.
+ The remaining 40 colors may be used by setting a custom color
+ table in the QWS master process before any clients connect.
+
+ \a colorTable is an array of up to 40 custom colors. \a start is
+ the starting index (0-39) and \a numColors is the number of colors
+ to be set (1-40).
+
+ This method is non-portable. It is available \e only in
+ Qt for Embedded Linux.
+
+ \note The custom colors will not be used by the default screen
+ driver. To make use of the new colors, implement a custom screen
+ driver, or use QDirectPainter.
+*/
+void QApplication::qwsSetCustomColors(QRgb *colorTable, int start, int numColors)
+{
+ if (start < 0 || start > 39) {
+ qWarning("QApplication::qwsSetCustomColors: start < 0 || start > 39");
+ return;
+ }
+ if (start + numColors > 40) {
+ numColors = 40 - start;
+ qWarning("QApplication::qwsSetCustomColors: Too many colors");
+ }
+ start += 216;
+ for (int i = 0; i < numColors; i++) {
+ qt_screen->set(start + i, qRed(colorTable[i]), qGreen(colorTable[i]),
+ qBlue(colorTable[i]));
+ }
+}
+
+#ifndef QT_NO_QWS_MANAGER
+/*!
+ Return the QWSDecoration used for decorating windows.
+
+ \warning This method is non-portable. It is only available in
+ Qt for Embedded Linux.
+
+ \sa QDecoration
+*/
+QDecoration &QApplication::qwsDecoration()
+{
+ return *qws_decoration;
+}
+
+/*!
+ \fn void QApplication::qwsSetDecoration(QDecoration *decoration)
+
+ Sets the QDecoration derived class to use for decorating the
+ windows used by Qt for Embedded Linux to the \a decoration
+ specified.
+
+ This method is non-portable. It is only available in Qt for Embedded Linux.
+
+ \sa QDecoration
+*/
+void QApplication::qwsSetDecoration(QDecoration *dec)
+{
+ if (dec) {
+ delete qws_decoration;
+ qws_decoration = dec;
+ QWidgetList widgets = topLevelWidgets();
+ for (int i = 0; i < widgets.size(); ++i) {
+ QWidget *w = widgets[i];
+ if (w->isVisible() && w != desktop()) {
+ static_cast<QETWidget *>(w)->updateRegion();
+ static_cast<QETWidget *>(w)->repaintDecoration(desktop()->rect(), false);
+ if (w->isMaximized())
+ w->showMaximized();
+ }
+ }
+ }
+}
+
+/*!
+ \overload
+
+ Requests a QDecoration object for \a decoration from the QDecorationFactory.
+
+ The string must be one of the QDecorationFactory::keys(). Keys are
+ case insensitive.
+
+ A later call to the QApplication constructor will override the
+ requested style when a "-style" option is passed in as a commandline
+ parameter.
+
+ Returns 0 if an unknown \a decoration is passed, otherwise the QStyle object
+ returned is set as the application's GUI style.
+*/
+QDecoration* QApplication::qwsSetDecoration(const QString &decoration)
+{
+ QDecoration *decore = QDecorationFactory::create(decoration);
+ if (!decore)
+ return 0;
+
+ qwsSetDecoration(decore);
+ return decore;
+}
+
+#endif
+
+bool QApplicationPrivate::modalState()
+{
+ return app_do_modal;
+}
+
+void QApplicationPrivate::enterModal_sys(QWidget *widget)
+{
+ if (!qt_modal_stack)
+ qt_modal_stack = new QWidgetList;
+ qt_modal_stack->insert(0, widget);
+ app_do_modal = true;
+}
+
+void QApplicationPrivate::leaveModal_sys(QWidget *widget)
+{
+ if (qt_modal_stack && qt_modal_stack->removeAll(widget)) {
+ if (qt_modal_stack->isEmpty()) {
+ delete qt_modal_stack;
+ qt_modal_stack = 0;
+ }
+ }
+ app_do_modal = qt_modal_stack != 0;
+}
+
+static bool qt_try_modal(QWidget *widget, QWSEvent *event)
+{
+ QWidget * top = 0;
+
+ if (QApplicationPrivate::tryModalHelper(widget, &top))
+ return true;
+
+ bool block_event = false;
+ bool paint_event = false;
+
+ switch (event->type) {
+ case QWSEvent::Focus:
+ if (!static_cast<QWSFocusEvent*>(event)->simpleData.get_focus)
+ break;
+ // drop through
+ case QWSEvent::Mouse: // disallow mouse/key events
+ case QWSEvent::Key:
+ block_event = true;
+ break;
+ }
+
+ if (top->parentWidget() == 0 && (block_event || paint_event))
+ top->raise();
+
+ return !block_event;
+}
+
+static int openPopupCount = 0;
+void QApplicationPrivate::openPopup(QWidget *popup)
+{
+ openPopupCount++;
+ if (!popupWidgets) { // create list
+ popupWidgets = new QWidgetList;
+
+ /* only grab if you are the first/parent popup */
+ QPaintDevice::qwsDisplay()->grabMouse(popup,true);
+ QPaintDevice::qwsDisplay()->grabKeyboard(popup,true);
+ popupGrabOk = true;
+ }
+ popupWidgets->append(popup); // add to end of list
+
+ // popups are not focus-handled by the window system (the first
+ // popup grabbed the keyboard), so we have to do that manually: A
+ // new popup gets the focus
+ if (popup->focusWidget()) {
+ popup->focusWidget()->setFocus(Qt::PopupFocusReason);
+ } else if (popupWidgets->count() == 1) { // this was the first popup
+ if (QWidget *fw = QApplication::focusWidget()) {
+ QFocusEvent e(QEvent::FocusOut, Qt::PopupFocusReason);
+ QApplication::sendEvent(fw, &e);
+ }
+ }
+}
+
+void QApplicationPrivate::closePopup(QWidget *popup)
+{
+ if (!popupWidgets)
+ return;
+
+ popupWidgets->removeAll(popup);
+ if (popup == popupOfPopupButtonFocus) {
+ popupButtonFocus = 0;
+ popupOfPopupButtonFocus = 0;
+ }
+ if (popupWidgets->count() == 0) { // this was the last popup
+ popupCloseDownMode = true; // control mouse events
+ delete popupWidgets;
+ popupWidgets = 0;
+ if (popupGrabOk) { // grabbing not disabled
+ QPaintDevice::qwsDisplay()->grabMouse(popup,false);
+ QPaintDevice::qwsDisplay()->grabKeyboard(popup,false);
+ popupGrabOk = false;
+ // XXX ungrab keyboard
+ }
+ if (active_window) {
+ if (QWidget *fw = active_window->focusWidget()) {
+ if (fw != QApplication::focusWidget()) {
+ fw->setFocus(Qt::PopupFocusReason);
+ } else {
+ QFocusEvent e(QEvent::FocusIn, Qt::PopupFocusReason);
+ QApplication::sendEvent(fw, &e);
+ }
+ }
+ }
+ } else {
+ // popups are not focus-handled by the window system (the
+ // first popup grabbed the keyboard), so we have to do that
+ // manually: A popup was closed, so the previous popup gets
+ // the focus.
+ QWidget* aw = popupWidgets->last();
+ if (QWidget *fw = aw->focusWidget())
+ fw->setFocus(Qt::PopupFocusReason);
+ }
+}
+
+/*****************************************************************************
+ Event translation; translates FB events to Qt events
+ *****************************************************************************/
+
+//
+// Mouse event translation
+//
+// FB doesn't give mouse double click events, so we generate them by
+// comparing window, time and position between two mouse press events.
+//
+
+
+// Needed for QCursor::pos
+
+static const int AnyButton = (Qt::LeftButton | Qt::MidButton | Qt::RightButton);
+
+
+
+//
+// Wheel event translation
+//
+bool QETWidget::translateWheelEvent(const QWSMouseEvent *me)
+{
+#ifdef QT_NO_WHEELEVENT
+ Q_UNUSED(me);
+ return false;
+#else
+ const QWSMouseEvent::SimpleData &mouse = me->simpleData;
+
+ // Figure out wheeling direction:
+ // Horizontal wheel w/o Alt
+ // OR Vertical wheel w/ Alt ==> Horizontal wheeling
+ // ..all other permutations ==> Vertical wheeling
+ int axis = mouse.delta / 120; // WHEEL_DELTA?
+ Qt::Orientation orient = ((axis == 2 || axis == -2) && ((mouse.state & Qt::AltModifier) == 0))
+ ||((axis == 1 || axis == -1) && mouse.state & Qt::AltModifier)
+ ? Qt::Horizontal : Qt::Vertical;
+
+ QPoint mousePoint = QPoint(mouse.x_root, mouse.y_root);
+
+ // send the event to the widget or its ancestors
+ QWidget* popup = qApp->activePopupWidget();
+ if (popup && window() != popup)
+ popup->close();
+ QWheelEvent we(mapFromGlobal(mousePoint), mousePoint, mouse.delta,
+ Qt::MouseButtons(mouse.state & Qt::MouseButtonMask),
+ Qt::KeyboardModifiers(mouse.state & Qt::KeyboardModifierMask), orient);
+ if (QApplication::sendSpontaneousEvent(this, &we))
+ return true;
+
+ // send the event to the widget that has the focus or its ancestors, if different
+ QWidget *w = this;
+ if (w != qApp->focusWidget() && (w = qApp->focusWidget())) {
+ QWidget* popup = qApp->activePopupWidget();
+ if (popup && w != popup)
+ popup->hide();
+ if (QApplication::sendSpontaneousEvent(w, &we))
+ return true;
+ }
+ return false;
+#endif
+}
+
+bool QETWidget::translateMouseEvent(const QWSMouseEvent *event, int prevstate)
+{
+ static bool manualGrab = false;
+ QPoint pos;
+ QPoint globalPos;
+ int button = 0;
+
+ if (sm_blockUserInput) // block user interaction during session management
+ return true;
+ const QWSMouseEvent::SimpleData &mouse = event->simpleData;
+ pos = mapFromGlobal(QPoint(mouse.x_root, mouse.y_root));
+// if (qt_last_x) {
+// *qt_last_x=mouse.x_root;
+// *qt_last_y=mouse.y_root;
+// }
+ globalPos.rx() = mouse.x_root;
+ globalPos.ry() = mouse.y_root;
+
+ QEvent::Type type = QEvent::None;
+
+ Qt::MouseButtons buttonstate = Qt::MouseButtons(mouse.state & Qt::MouseButtonMask);
+ Qt::KeyboardModifiers keystate = Qt::KeyboardModifiers(mouse.state & Qt::KeyboardModifierMask);
+
+ if (mouse.state == prevstate) {
+ // mouse move
+ type = QEvent::MouseMove;
+ } else if ((mouse.state&AnyButton) != (prevstate&AnyButton)) {
+ Qt::MouseButtons current_buttons = Qt::MouseButtons(prevstate&Qt::MouseButtonMask);
+ for (button = Qt::LeftButton; !type && button <= Qt::MidButton; button<<=1) {
+ if ((mouse.state&button) != (current_buttons&button)) {
+ // button press or release
+ current_buttons = Qt::MouseButtons(current_buttons ^ button);
+
+#ifndef QT_NO_QWS_INPUTMETHODS
+ //############ We used to do a QInputContext::reset(oldFocus);
+ // when we changed the focus widget. See change 93389 for where the
+ // focus code went. The IM code was (after testing for ClickToFocus):
+ //if (mouse.state&button && w != QInputContext::microFocusWidget()) //button press
+ // QInputContext::reset(oldFocus);
+
+#endif
+ if (mouse.state&button) { //button press
+ qt_button_down = childAt(pos);
+ if (!qt_button_down)
+ qt_button_down = this;
+ if (/*XXX mouseActWindow == this &&*/
+ mouseButtonPressed == button &&
+ long(mouse.time) -long(mouseButtonPressTime)
+ < QApplication::doubleClickInterval() &&
+ qAbs(mouse.x_root - mouseXPos) < mouse_double_click_distance &&
+ qAbs(mouse.y_root - mouseYPos) < mouse_double_click_distance ) {
+ type = QEvent::MouseButtonDblClick;
+ mouseButtonPressTime -= 2000; // no double-click next time
+ } else {
+ type = QEvent::MouseButtonPress;
+ mouseButtonPressTime = mouse.time;
+ }
+ mouseButtonPressed = button; // save event params for
+ mouseXPos = globalPos.x(); // future double click tests
+ mouseYPos = globalPos.y();
+ } else { // mouse button released
+ if (manualGrab) { // release manual grab
+ manualGrab = false;
+ // XXX XUngrabPointer(x11Display(), CurrentTime);
+ }
+
+ type = QEvent::MouseButtonRelease;
+ }
+ }
+ }
+ button >>= 1;
+ }
+ //XXX mouseActWindow = winId(); // save some event params
+
+ if (type == 0) { // event consumed
+ return false; //EXIT in the normal case
+ }
+
+ if (qApp->d_func()->inPopupMode()) { // in popup mode
+ QWidget *popup = qApp->activePopupWidget();
+ // in X11, this would be the window we are over.
+ // in QWS this is the top level popup. to allow mouse
+ // events to other widgets, need to go through qApp->QApplicationPrivate::popupWidgets.
+ QSize s(qt_screen->width(), qt_screen->height());
+ for (int i = 0; i < QApplicationPrivate::popupWidgets->size(); ++i) {
+ QWidget *w = QApplicationPrivate::popupWidgets->at(i);
+
+ if ((w->windowType() == Qt::Popup) && w->d_func()->localAllocatedRegion().contains(globalPos - w->geometry().topLeft()))
+ {
+ popup = w;
+ break;
+ }
+ }
+ pos = popup->mapFromGlobal(globalPos);
+ bool releaseAfter = false;
+ QWidget *popupChild = popup->childAt(pos);
+ QWidget *popupTarget = popupChild ? popupChild : popup;
+
+ if (popup != popupOfPopupButtonFocus){
+ popupButtonFocus = 0;
+ popupOfPopupButtonFocus = 0;
+ }
+
+ if (!popupTarget->isEnabled()) {
+ return false; //EXIT special case
+ }
+
+ switch (type) {
+ case QEvent::MouseButtonPress:
+ case QEvent::MouseButtonDblClick:
+ popupButtonFocus = popupChild;
+ popupOfPopupButtonFocus = popup;
+ break;
+ case QEvent::MouseButtonRelease:
+ releaseAfter = true;
+ break;
+ default:
+ break; // nothing for mouse move
+ }
+
+ int oldOpenPopupCount = openPopupCount;
+
+ if (popupButtonFocus) {
+ QMouseEvent e(type, popupButtonFocus->mapFromGlobal(globalPos),
+ globalPos, Qt::MouseButton(button), buttonstate, keystate);
+ QApplication::sendSpontaneousEvent(popupButtonFocus, & e);
+ if (releaseAfter) {
+ popupButtonFocus = 0;
+ popupOfPopupButtonFocus = 0;
+ }
+ } else if (popupChild) {
+ QMouseEvent e(type, popupChild->mapFromGlobal(globalPos),
+ globalPos, Qt::MouseButton(button), buttonstate, keystate);
+ QApplication::sendSpontaneousEvent(popupChild, & e);
+ } else {
+ QMouseEvent e(type, pos, globalPos, Qt::MouseButton(button), buttonstate, keystate);
+ QApplication::sendSpontaneousEvent(popupChild ? popupChild : popup, & e);
+ }
+#ifndef QT_NO_CONTEXTMENU
+ if (type == QEvent::MouseButtonPress && button == Qt::RightButton && (openPopupCount == oldOpenPopupCount)) {
+ QWidget *popupEvent = popup;
+ if(popupButtonFocus)
+ popupEvent = popupButtonFocus;
+ else if(popupChild)
+ popupEvent = popupChild;
+ QContextMenuEvent e(QContextMenuEvent::Mouse, pos, globalPos, keystate);
+ QApplication::sendSpontaneousEvent(popupEvent, &e);
+ }
+#endif // QT_NO_CONTEXTMENU
+
+ if (releaseAfter)
+ qt_button_down = 0;
+
+ } else { //qApp not in popup mode
+ QWidget *widget = this;
+ QWidget *w = QWidget::mouseGrabber();
+ if (!w && qt_button_down)
+ w = qt_button_down;
+ if (w && w != this) {
+ widget = w;
+ pos = mapToGlobal(pos);
+ pos = w->mapFromGlobal(pos);
+ }
+
+ if (popupCloseDownMode) {
+ popupCloseDownMode = false;
+ if ((windowType() == Qt::Popup)) // ignore replayed event
+ return true; //EXIT
+ }
+
+ QPointer<QWidget> leaveAfterRelease = 0;
+ if (type == QEvent::MouseButtonRelease &&
+ (mouse.state & (~button) & (Qt::LeftButton |
+ Qt::MidButton |
+ Qt::RightButton)) == 0) {
+ // Button released outside the widget -> leave the widget after the
+ // release event has been delivered.
+ if (widget == qt_button_down && (pos.x() < 0 || pos.y() < 0))
+ leaveAfterRelease = qt_button_down;
+ qt_button_down = 0;
+ }
+
+ int oldOpenPopupCount = openPopupCount;
+
+ QMouseEvent e(type, pos, globalPos, Qt::MouseButton(button), buttonstate, keystate);
+#ifndef QT_NO_QWS_MANAGER
+ if (widget->isWindow() && widget->d_func()->topData()->qwsManager
+ && (widget->d_func()->topData()->qwsManager->region().contains(globalPos)
+ || QWSManager::grabbedMouse() )) {
+ if ((*mouseInWidget)) {
+ QApplicationPrivate::dispatchEnterLeave(0, *mouseInWidget);
+ (*mouseInWidget) = 0;
+ }
+ QApplication::sendSpontaneousEvent(widget->d_func()->topData()->qwsManager, &e);
+ qApp->d_func()->last_manager = widget->d_func()->topData()->qwsManager;
+ } else
+#endif
+ {
+ if (widget != (*mouseInWidget)) {
+ QApplicationPrivate::dispatchEnterLeave(widget, *mouseInWidget);
+ (*mouseInWidget) = widget;
+ }
+ QApplication::sendSpontaneousEvent(widget, &e);
+ if (leaveAfterRelease && !QWidget::mouseGrabber()) {
+ *mouseInWidget = QApplication::widgetAt(globalPos);
+ QApplicationPrivate::dispatchEnterLeave(*mouseInWidget, leaveAfterRelease);
+ leaveAfterRelease = 0;
+ }
+ }
+#ifndef QT_NO_CONTEXTMENU
+ if (type == QEvent::MouseButtonPress && button == Qt::RightButton && (openPopupCount == oldOpenPopupCount)) {
+ QContextMenuEvent e(QContextMenuEvent::Mouse, pos, globalPos, keystate);
+ QApplication::sendSpontaneousEvent(widget, &e);
+ }
+#endif // QT_NO_CONTEXTMENU
+ }
+ return true;
+}
+
+
+bool QETWidget::translateKeyEvent(const QWSKeyEvent *event, bool grab) /* grab is used in the #ifdef */
+{
+ int code = -1;
+ //### Qt assumes keyboard state is state *before*, while QWS uses state after the event
+ static Qt::KeyboardModifiers oldstate;
+ Qt::KeyboardModifiers state = oldstate;
+ oldstate = event->simpleData.modifiers;
+
+ if (sm_blockUserInput) // block user interaction during session management
+ return true;
+
+ if (!isEnabled())
+ return true;
+
+ QEvent::Type type = event->simpleData.is_press ?
+ QEvent::KeyPress : QEvent::KeyRelease;
+ bool autor = event->simpleData.is_auto_repeat;
+ QString text;
+ char ascii = 0;
+ if (event->simpleData.unicode) {
+ QChar ch(event->simpleData.unicode);
+ if (ch.unicode() != 0xffff)
+ text += ch;
+ ascii = ch.toLatin1();
+ }
+ code = event->simpleData.keycode;
+
+#if defined QT3_SUPPORT && !defined(QT_NO_SHORTCUT)
+ if (type == QEvent::KeyPress && !grab
+ && static_cast<QApplicationPrivate*>(qApp->d_ptr)->use_compat()) {
+ // send accel events if the keyboard is not grabbed
+ QKeyEvent a(type, code, state, text, autor, int(text.length()));
+ if (static_cast<QApplicationPrivate*>(qApp->d_ptr)->qt_tryAccelEvent(this, &a))
+ return true;
+ }
+#else
+ Q_UNUSED(grab);
+#endif
+ if (!text.isEmpty() && testAttribute(Qt::WA_KeyCompression)) {
+ // the widget wants key compression so it gets it
+
+ // XXX not implemented
+ }
+
+ QKeyEvent e(type, code, state, text, autor, int(text.length()));
+ return QApplication::sendSpontaneousEvent(this, &e);
+}
+
+bool QETWidget::translateRegionEvent(const QWSRegionEvent *event)
+{
+ QWSWindowSurface *surface = static_cast<QWSWindowSurface*>(windowSurface());
+ Q_ASSERT(surface);
+
+ QRegion region;
+ region.setRects(event->rectangles, event->simpleData.nrectangles);
+
+ switch (event->simpleData.type) {
+ case QWSRegionEvent::Allocation:
+ region.translate(-mapToGlobal(QPoint()));
+ surface->setClipRegion(region);
+ break;
+#ifdef QT_QWS_CLIENTBLIT
+ case QWSRegionEvent::DirectPaint:
+ surface->setDirectRegion(region, event->simpleData.id);
+ break;
+#endif
+ default:
+ break;
+ }
+
+ return true;
+}
+
+#ifndef QT_NO_QWSEMBEDWIDGET
+void QETWidget::translateEmbedEvent(const QWSEmbedEvent *event)
+{
+ if (event->simpleData.type | QWSEmbedEvent::Region) {
+ const QRegion region = event->region;
+ setGeometry(region.boundingRect());
+ setVisible(!region.isEmpty());
+ }
+}
+#endif // QT_NO_QWSEMBEDWIDGET
+
+void QETWidget::repaintDecoration(QRegion r, bool post)
+{
+ Q_UNUSED(post);
+#ifdef QT_NO_QWS_MANAGER
+ Q_UNUSED(r);
+#else
+ //please note that qwsManager is a QObject, not a QWidget.
+ //therefore, normal ways of painting do not work.
+ // However, it does listen to paint events.
+
+ Q_D(QWidget);
+ if (isWindow() && d->topData()->qwsManager && isVisible()) {
+ QWSManager *manager = d->topData()->qwsManager;
+ r &= manager->region();
+ if (!r.isEmpty())
+ manager->repaintRegion(QDecoration::All, QDecoration::Normal);
+ }
+#endif
+}
+
+void QETWidget::updateRegion()
+{
+ Q_D(QWidget);
+
+ QTLWExtra *topextra = d->maybeTopData();
+ if (!topextra)
+ return;
+
+ QRegion myregion = d->localRequestedRegion();
+ myregion.translate(geometry().topLeft());
+
+#ifndef QT_NO_QWS_MANAGER
+ QWSManager *manager = topextra->qwsManager;
+ if (manager)
+ myregion += manager->region();
+#endif
+
+ QRect br(myregion.boundingRect());
+ topextra->frameStrut.setCoords(d->data.crect.x() - br.x(),
+ d->data.crect.y() - br.y(),
+ br.right() - d->data.crect.right(),
+ br.bottom() - d->data.crect.bottom());
+}
+
+void QApplication::setCursorFlashTime(int msecs)
+{
+ QApplicationPrivate::cursor_flash_time = msecs;
+}
+
+
+int QApplication::cursorFlashTime()
+{
+ return QApplicationPrivate::cursor_flash_time;
+}
+
+void QApplication::setDoubleClickInterval(int ms)
+{
+ QApplicationPrivate::mouse_double_click_time = ms;
+}
+
+int QApplication::doubleClickInterval()
+{
+ return QApplicationPrivate::mouse_double_click_time;
+}
+
+void QApplication::setKeyboardInputInterval(int ms)
+{
+ QApplicationPrivate::keyboard_input_time = ms;
+}
+
+int QApplication::keyboardInputInterval()
+{
+ return QApplicationPrivate::keyboard_input_time;
+}
+
+#ifndef QT_NO_WHEELEVENT
+void QApplication::setWheelScrollLines(int lines)
+{
+ QApplicationPrivate::wheel_scroll_lines = lines;
+}
+
+int QApplication::wheelScrollLines()
+{
+ return QApplicationPrivate::wheel_scroll_lines;
+}
+#endif
+
+void QApplication::setEffectEnabled(Qt::UIEffect effect, bool enable)
+{
+ switch (effect) {
+ case Qt::UI_AnimateMenu:
+ QApplicationPrivate::animate_menu = enable;
+ break;
+ case Qt::UI_FadeMenu:
+ if (enable)
+ QApplicationPrivate::animate_menu = true;
+ QApplicationPrivate::fade_menu = enable;
+ break;
+ case Qt::UI_AnimateCombo:
+ QApplicationPrivate::animate_combo = enable;
+ break;
+ case Qt::UI_AnimateTooltip:
+ QApplicationPrivate::animate_tooltip = enable;
+ break;
+ case Qt::UI_FadeTooltip:
+ if (enable)
+ QApplicationPrivate::animate_tooltip = true;
+ QApplicationPrivate::fade_tooltip = enable;
+ break;
+ case Qt::UI_AnimateToolBox:
+ QApplicationPrivate::animate_toolbox = enable;
+ break;
+ default:
+ QApplicationPrivate::animate_ui = enable;
+ break;
+ }
+}
+
+bool QApplication::isEffectEnabled(Qt::UIEffect effect)
+{
+ if (QColormap::instance().depth() < 16 || !QApplicationPrivate::animate_ui)
+ return false;
+
+ switch(effect) {
+ case Qt::UI_AnimateMenu:
+ return QApplicationPrivate::animate_menu;
+ case Qt::UI_FadeMenu:
+ return QApplicationPrivate::fade_menu;
+ case Qt::UI_AnimateCombo:
+ return QApplicationPrivate::animate_combo;
+ case Qt::UI_AnimateTooltip:
+ return QApplicationPrivate::animate_tooltip;
+ case Qt::UI_FadeTooltip:
+ return QApplicationPrivate::fade_tooltip;
+ case Qt::UI_AnimateToolBox:
+ return QApplicationPrivate::animate_toolbox;
+ default:
+ return QApplicationPrivate::animate_ui;
+ }
+}
+
+void QApplication::setArgs(int c, char **v)
+{
+ Q_D(QApplication);
+ d->argc = c;
+ d->argv = v;
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/kernel/qapplication_win.cpp b/src/gui/kernel/qapplication_win.cpp
new file mode 100644
index 0000000000..fae0335a2d
--- /dev/null
+++ b/src/gui/kernel/qapplication_win.cpp
@@ -0,0 +1,3956 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifdef Q_OS_WINCE
+#include "qguifunctions_wince.h"
+#include "qmenubar.h"
+extern bool qt_wince_is_mobile(); //defined in qguifunctions_wince.cpp
+extern bool qt_wince_is_high_dpi(); //defined in qguifunctions_wince.cpp
+extern bool qt_wince_is_smartphone(); //defined in qguifunctions_wince.cpp
+extern bool qt_wince_is_pocket_pc(); //defined in qguifunctions_wince.cpp
+extern void qt_wince_hide_taskbar(HWND hwnd); //defined in qguifunctions_wince.cpp
+#endif
+#ifdef Q_OS_WINCE_WM
+#include <windowsm.h>
+#include <tpcshell.h>
+#endif
+
+#include "qapplication.h"
+#include "qdesktopwidget.h"
+#include "qevent.h"
+#include "private/qeventdispatcher_win_p.h"
+#include "qeventloop.h"
+#include "qclipboard.h"
+#include "qcursor.h"
+#include "qdatetime.h"
+#include "qpointer.h"
+#include "qhash.h"
+#include "qlibrary.h"
+#include "qmetaobject.h"
+#include "qmime.h"
+#include "qpainter.h"
+#include "qpixmapcache.h"
+#include "qsessionmanager.h"
+#include "qstyle.h"
+#include "qwhatsthis.h" // ######## dependency
+#include "qwidget.h"
+#include "qcolormap.h"
+#include "qlayout.h"
+#include "qtooltip.h"
+#include "qt_windows.h"
+#if defined(QT_NON_COMMERCIAL)
+#include "qnc_win.h"
+#endif
+#include "private/qwininputcontext_p.h"
+#include "private/qcursor_p.h"
+#include "private/qmath_p.h"
+#include "private/qapplication_p.h"
+#include "private/qbackingstore_p.h"
+#include "private/qwindowsurface_raster_p.h"
+#include "qdebug.h"
+#include <private/qkeymapper_p.h>
+#include <private/qlocale_p.h>
+
+//#define ALIEN_DEBUG
+
+#ifndef QT_NO_THREAD
+#include "qmutex.h"
+#endif
+
+#ifndef QT_NO_ACCESSIBILITY
+#include "qaccessible.h"
+
+#include <oleacc.h>
+#ifndef WM_GETOBJECT
+#define WM_GETOBJECT 0x003D
+#endif
+#endif // QT_NO_ACCESSIBILITY
+
+#include <winuser.h>
+#if !defined(WINABLEAPI)
+# if defined(Q_OS_WINCE)
+# include <bldver.h>
+# endif
+# include <winable.h>
+#endif
+
+
+#ifndef FLASHW_STOP
+typedef struct {
+ UINT cbSize;
+ HWND hwnd;
+ DWORD dwFlags;
+ UINT uCount;
+ DWORD dwTimeout;
+} FLASHWINFO, *PFLASHWINFO;
+#define FLASHW_STOP 0
+#define FLASHW_CAPTION 0x00000001
+#define FLASHW_TRAY 0x00000002
+#define FLASHW_ALL (FLASHW_CAPTION | FLASHW_TRAY)
+#define FLASHW_TIMER 0x00000004
+#define FLASHW_TIMERNOFG 0x0000000C
+#endif /* FLASHW_STOP */
+typedef BOOL (WINAPI *PtrFlashWindowEx)(PFLASHWINFO pfwi);
+static PtrFlashWindowEx pFlashWindowEx = 0;
+
+#include <windowsx.h>
+#include <limits.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <math.h>
+
+#define PACKETDATA (PK_X | PK_Y | PK_BUTTONS | PK_NORMAL_PRESSURE | PK_TANGENT_PRESSURE \
+ | PK_ORIENTATION | PK_CURSOR | PK_Z)
+#define PACKETMODE 0
+
+#include <wintab.h>
+#ifndef CSR_TYPE
+#define CSR_TYPE 20 // Some old Wacom wintab.h may not provide this constant.
+#endif
+#include <pktdef.h>
+
+#if defined(__CYGWIN32__)
+#define __INSIDE_CYGWIN32__
+#include <mywinsock.h>
+#endif
+
+QT_BEGIN_NAMESPACE
+
+#ifdef Q_OS_WINCE
+#ifndef SHRG_RETURNCMD
+struct SHRGINFO {
+ DWORD cbSize;
+ HWND hwndClient;
+ POINT ptDown;
+ DWORD dwFlags;
+};
+#define GN_CONTEXTMENU 1000
+#define SHRG_RETURNCMD 0x00000001
+#define SHRG_NOANIMATION 0x00000010
+#endif
+
+#ifndef SPI_SETSIPINFO
+#define SPI_SETSIPINFO 224
+#endif
+
+typedef DWORD (API *AygRecognizeGesture)(SHRGINFO*);
+static AygRecognizeGesture ptrRecognizeGesture = 0;
+static bool aygResolved = false;
+static void resolveAygLibs()
+{
+ if (!aygResolved) {
+ aygResolved = true;
+ QLibrary ayglib(QLatin1String("aygshell"));
+ if (!ayglib.load())
+ return;
+ ptrRecognizeGesture = (AygRecognizeGesture) ayglib.resolve("SHRecognizeGesture");
+ }
+}
+
+#endif
+
+#ifndef SPI_SETFONTSMOOTHINGTYPE
+# define SPI_SETFONTSMOOTHINGTYPE 0x200B
+#endif
+#ifndef SPI_GETFONTSMOOTHINGTYPE
+# define SPI_GETFONTSMOOTHINGTYPE 0x200A
+#endif
+#ifndef FE_FONTSMOOTHINGCLEARTYPE
+# define FE_FONTSMOOTHINGCLEARTYPE 0x0002
+#endif
+
+bool qt_cleartype_enabled;
+Q_GUI_EXPORT bool qt_win_owndc_required; // CS_OWNDC is required if we use the GL graphicssystem as default
+
+typedef HCTX (API *PtrWTOpen)(HWND, LPLOGCONTEXT, BOOL);
+typedef BOOL (API *PtrWTClose)(HCTX);
+typedef UINT (API *PtrWTInfo)(UINT, UINT, LPVOID);
+typedef BOOL (API *PtrWTEnable)(HCTX, BOOL);
+typedef BOOL (API *PtrWTOverlap)(HCTX, BOOL);
+typedef int (API *PtrWTPacketsGet)(HCTX, int, LPVOID);
+typedef BOOL (API *PtrWTGet)(HCTX, LPLOGCONTEXT);
+typedef int (API *PtrWTQueueSizeGet)(HCTX);
+typedef BOOL (API *PtrWTQueueSizeSet)(HCTX, int);
+
+static PtrWTInfo ptrWTInfo = 0;
+static PtrWTEnable ptrWTEnable = 0;
+static PtrWTOverlap ptrWTOverlap = 0;
+static PtrWTPacketsGet ptrWTPacketsGet = 0;
+static PtrWTGet ptrWTGet = 0;
+
+static PACKET localPacketBuf[QT_TABLET_NPACKETQSIZE]; // our own tablet packet queue.
+HCTX qt_tablet_context; // the hardware context for the tablet (like a window handle)
+bool qt_tablet_tilt_support;
+static void tabletInit(UINT wActiveCsr, HCTX hTab);
+static void initWinTabFunctions(); // resolve the WINTAB api functions
+
+
+#ifndef QT_NO_ACCESSIBILITY
+extern IAccessible *qt_createWindowsAccessible(QAccessibleInterface *object);
+#endif // QT_NO_ACCESSIBILITY
+
+extern bool qt_tabletChokeMouse;
+extern QWidget* qt_get_tablet_widget();
+extern bool qt_sendSpontaneousEvent(QObject*, QEvent*);
+extern QRegion qt_dirtyRegion(QWidget *);
+
+typedef QHash<UINT, QTabletDeviceData> QTabletCursorInfo;
+Q_GLOBAL_STATIC(QTabletCursorInfo, tCursorInfo)
+QTabletDeviceData currentTabletPointer;
+
+// from qregion_win.cpp
+extern HRGN qt_tryCreateRegion(QRegion::RegionType type, int left, int top, int right, int bottom);
+
+Q_CORE_EXPORT bool winPeekMessage(MSG* msg, HWND hWnd, UINT wMsgFilterMin,
+ UINT wMsgFilterMax, UINT wRemoveMsg);
+Q_CORE_EXPORT bool winPostMessage(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
+
+// support for on-the-fly changes of the XP theme engine
+#ifndef WM_THEMECHANGED
+#define WM_THEMECHANGED 0x031A
+#endif
+#ifndef COLOR_MENUHILIGHT
+#define COLOR_MENUHILIGHT 29
+#define COLOR_MENUBAR 30
+#endif
+
+// support for xbuttons
+#ifndef WM_XBUTTONDOWN
+#define WM_XBUTTONDOWN 0x020B
+#define WM_XBUTTONUP 0x020C
+#define WM_XBUTTONDBLCLK 0x020D
+#define GET_KEYSTATE_WPARAM(wParam) (LOWORD(wParam))
+#define GET_XBUTTON_WPARAM(wParam) (HIWORD(wParam))
+#define XBUTTON1 0x0001
+#define XBUTTON2 0x0002
+#define MK_XBUTTON1 0x0020
+#define MK_XBUTTON2 0x0040
+#endif
+
+#ifdef Q_OS_WINCE
+#define GET_KEYSTATE_WPARAM(wParam) (LOWORD(wParam))
+#endif
+
+// support for multi-media-keys on ME/2000/XP
+#ifndef WM_APPCOMMAND
+#define WM_APPCOMMAND 0x0319
+
+#define FAPPCOMMAND_MOUSE 0x8000
+#define FAPPCOMMAND_KEY 0
+#define FAPPCOMMAND_OEM 0x1000
+#define FAPPCOMMAND_MASK 0xF000
+#define GET_APPCOMMAND_LPARAM(lParam) ((short)(HIWORD(lParam) & ~FAPPCOMMAND_MASK))
+#define GET_DEVICE_LPARAM(lParam) ((WORD)(HIWORD(lParam) & FAPPCOMMAND_MASK))
+#define GET_MOUSEORKEY_LPARAM GET_DEVICE_LPARAM
+#define GET_FLAGS_LPARAM(lParam) (LOWORD(lParam))
+#define GET_KEYSTATE_LPARAM(lParam) GET_FLAGS_LPARAM(lParam)
+
+#define APPCOMMAND_BROWSER_BACKWARD 1
+#define APPCOMMAND_BROWSER_FORWARD 2
+#define APPCOMMAND_BROWSER_REFRESH 3
+#define APPCOMMAND_BROWSER_STOP 4
+#define APPCOMMAND_BROWSER_SEARCH 5
+#define APPCOMMAND_BROWSER_FAVORITES 6
+#define APPCOMMAND_BROWSER_HOME 7
+#define APPCOMMAND_VOLUME_MUTE 8
+#define APPCOMMAND_VOLUME_DOWN 9
+#define APPCOMMAND_VOLUME_UP 10
+#define APPCOMMAND_MEDIA_NEXTTRACK 11
+#define APPCOMMAND_MEDIA_PREVIOUSTRACK 12
+#define APPCOMMAND_MEDIA_STOP 13
+#define APPCOMMAND_MEDIA_PLAY_PAUSE 14
+#define APPCOMMAND_LAUNCH_MAIL 15
+#define APPCOMMAND_LAUNCH_MEDIA_SELECT 16
+#define APPCOMMAND_LAUNCH_APP1 17
+#define APPCOMMAND_LAUNCH_APP2 18
+#define APPCOMMAND_BASS_DOWN 19
+#define APPCOMMAND_BASS_BOOST 20
+#define APPCOMMAND_BASS_UP 21
+#define APPCOMMAND_TREBLE_DOWN 22
+#define APPCOMMAND_TREBLE_UP 23
+
+// New commands from Windows XP (some even Sp1)
+#ifndef APPCOMMAND_MICROPHONE_VOLUME_MUTE
+#define APPCOMMAND_MICROPHONE_VOLUME_MUTE 24
+#define APPCOMMAND_MICROPHONE_VOLUME_DOWN 25
+#define APPCOMMAND_MICROPHONE_VOLUME_UP 26
+#define APPCOMMAND_HELP 27
+#define APPCOMMAND_FIND 28
+#define APPCOMMAND_NEW 29
+#define APPCOMMAND_OPEN 30
+#define APPCOMMAND_CLOSE 31
+#define APPCOMMAND_SAVE 32
+#define APPCOMMAND_PRINT 33
+#define APPCOMMAND_UNDO 34
+#define APPCOMMAND_REDO 35
+#define APPCOMMAND_COPY 36
+#define APPCOMMAND_CUT 37
+#define APPCOMMAND_PASTE 38
+#define APPCOMMAND_REPLY_TO_MAIL 39
+#define APPCOMMAND_FORWARD_MAIL 40
+#define APPCOMMAND_SEND_MAIL 41
+#define APPCOMMAND_SPELL_CHECK 42
+#define APPCOMMAND_DICTATE_OR_COMMAND_CONTROL_TOGGLE 43
+#define APPCOMMAND_MIC_ON_OFF_TOGGLE 44
+#define APPCOMMAND_CORRECTION_LIST 45
+#define APPCOMMAND_MEDIA_PLAY 46
+#define APPCOMMAND_MEDIA_PAUSE 47
+#define APPCOMMAND_MEDIA_RECORD 48
+#define APPCOMMAND_MEDIA_FAST_FORWARD 49
+#define APPCOMMAND_MEDIA_REWIND 50
+#define APPCOMMAND_MEDIA_CHANNEL_UP 51
+#define APPCOMMAND_MEDIA_CHANNEL_DOWN 52
+#endif // APPCOMMAND_MICROPHONE_VOLUME_MUTE
+
+#endif // WM_APPCOMMAND
+
+static UINT WM95_MOUSEWHEEL = 0;
+
+#if (_WIN32_WINNT < 0x0400)
+// This struct is defined in winuser.h if the _WIN32_WINNT >= 0x0400 -- in the
+// other cases we have to define it on our own.
+typedef struct tagTRACKMOUSEEVENT {
+ DWORD cbSize;
+ DWORD dwFlags;
+ HWND hwndTrack;
+ DWORD dwHoverTime;
+} TRACKMOUSEEVENT, *LPTRACKMOUSEEVENT;
+#endif
+#ifndef WM_MOUSELEAVE
+#define WM_MOUSELEAVE 0x02A3
+#endif
+
+QT_BEGIN_INCLUDE_NAMESPACE
+#include "private/qwidget_p.h"
+QT_END_INCLUDE_NAMESPACE
+
+static int translateButtonState(int s, int type, int button);
+
+// ##### get rid of this!
+QRgb qt_colorref2qrgb(COLORREF col)
+{
+ return qRgb(GetRValue(col),GetGValue(col),GetBValue(col));
+}
+
+
+/*****************************************************************************
+ Internal variables and functions
+ *****************************************************************************/
+
+extern Q_CORE_EXPORT char theAppName[];
+extern Q_CORE_EXPORT char appFileName[];
+extern Q_CORE_EXPORT HINSTANCE appInst; // handle to app instance
+extern Q_CORE_EXPORT HINSTANCE appPrevInst; // handle to prev app instance
+extern Q_CORE_EXPORT int appCmdShow; // main window show command
+static HWND curWin = 0; // current window
+static HDC displayDC = 0; // display device context
+
+// Session management
+static bool sm_blockUserInput = false;
+static bool sm_smActive = false;
+extern QSessionManager* qt_session_manager_self;
+static bool sm_cancel;
+
+static bool replayPopupMouseEvent = false; // replay handling when popups close
+
+// ignore the next release event if return from a modal widget
+Q_GUI_EXPORT bool qt_win_ignoreNextMouseReleaseEvent = false;
+
+
+#if defined(QT_DEBUG)
+static bool appNoGrab = false; // mouse/keyboard grabbing
+#endif
+
+static bool app_do_modal = false; // modal mode
+extern QWidgetList *qt_modal_stack;
+extern QDesktopWidget *qt_desktopWidget;
+static QPointer<QWidget> popupButtonFocus;
+static bool qt_try_modal(QWidget *, MSG *, int& ret);
+
+QWidget *qt_button_down = 0; // widget got last button-down
+QPointer<QWidget> qt_last_mouse_receiver = 0;
+
+static HWND autoCaptureWnd = 0;
+static HWND imeParentWnd = 0;
+static void setAutoCapture(HWND); // automatic capture
+static void releaseAutoCapture();
+
+static void unregWinClasses();
+
+extern QCursor *qt_grab_cursor();
+
+#if defined(Q_WS_WIN)
+#define __export
+#endif
+
+QApplicationPrivate* getQApplicationPrivateInternal()
+{
+ return qApp->d_func();
+}
+
+extern "C" LRESULT CALLBACK QtWndProc(HWND, UINT, WPARAM, LPARAM);
+
+class QETWidget : public QWidget // event translator widget
+{
+public:
+ QWExtra *xtra() { return d_func()->extraData(); }
+ QTLWExtra *topData() { return d_func()->topData(); }
+ QTLWExtra *maybeTopData() { return d_func()->maybeTopData(); }
+ void syncBackingStore(const QRegion &rgn) { d_func()->syncBackingStore(rgn); }
+ void syncBackingStore() { d_func()->syncBackingStore(); }
+ QWidgetData *dataPtr() { return data; }
+ QWidgetPrivate *dptr() { return d_func(); }
+ QRect frameStrut() const { return d_func()->frameStrut(); }
+ bool winEvent(MSG *m, long *r) { return QWidget::winEvent(m, r); }
+ void markFrameStrutDirty() { data->fstrut_dirty = 1; }
+ bool translateMouseEvent(const MSG &msg);
+ bool translateWheelEvent(const MSG &msg);
+ bool translatePaintEvent(const MSG &msg);
+ bool translateConfigEvent(const MSG &msg);
+ bool translateCloseEvent(const MSG &msg);
+ bool translateTabletEvent(const MSG &msg, PACKET *localPacketBuf, int numPackets);
+ void repolishStyle(QStyle &style);
+ inline void showChildren(bool spontaneous) { d_func()->showChildren(spontaneous); }
+ inline void hideChildren(bool spontaneous) { d_func()->hideChildren(spontaneous); }
+ inline uint testWindowState(uint teststate){ return dataPtr()->window_state & teststate; }
+ inline void setWindowTitle_helper(const QString &title) { d_func()->setWindowTitle_helper(title); }
+ inline void forceUpdate() {
+ QTLWExtra *tlwExtra = window()->d_func()->maybeTopData();
+ if (tlwExtra && tlwExtra->backingStore)
+ tlwExtra->backingStore->markDirty(rect(), this, true, true);
+ }
+};
+
+// need to get default font?
+extern bool qt_app_has_font;
+
+extern QFont qt_LOGFONTtoQFont(LOGFONT& lf,bool scale);
+
+static void qt_set_windows_color_resources()
+{
+ // Do the color settings
+ QPalette pal;
+ pal.setColor(QPalette::WindowText,
+ QColor(qt_colorref2qrgb(GetSysColor(COLOR_WINDOWTEXT))));
+ pal.setColor(QPalette::Button,
+ QColor(qt_colorref2qrgb(GetSysColor(COLOR_BTNFACE))));
+ pal.setColor(QPalette::Light,
+ QColor(qt_colorref2qrgb(GetSysColor(COLOR_BTNHIGHLIGHT))));
+ pal.setColor(QPalette::Dark,
+ QColor(qt_colorref2qrgb(GetSysColor(COLOR_BTNSHADOW))));
+ pal.setColor(QPalette::Mid, pal.button().color().darker(150));
+ pal.setColor(QPalette::Text,
+ QColor(qt_colorref2qrgb(GetSysColor(COLOR_WINDOWTEXT))));
+ pal.setColor(QPalette::BrightText,
+ QColor(qt_colorref2qrgb(GetSysColor(COLOR_BTNHIGHLIGHT))));
+ pal.setColor(QPalette::Base,
+ QColor(qt_colorref2qrgb(GetSysColor(COLOR_WINDOW))));
+ pal.setColor(QPalette::Window,
+ QColor(qt_colorref2qrgb(GetSysColor(COLOR_BTNFACE))));
+ pal.setColor(QPalette::ButtonText,
+ QColor(qt_colorref2qrgb(GetSysColor(COLOR_BTNTEXT))));
+ pal.setColor(QPalette::Midlight,
+ QColor(qt_colorref2qrgb(GetSysColor(COLOR_3DLIGHT))));
+ pal.setColor(QPalette::Shadow,
+ QColor(qt_colorref2qrgb(GetSysColor(COLOR_3DDKSHADOW))));
+ pal.setColor(QPalette::Highlight,
+ QColor(qt_colorref2qrgb(GetSysColor(COLOR_HIGHLIGHT))));
+ pal.setColor(QPalette::HighlightedText,
+ QColor(qt_colorref2qrgb(GetSysColor(COLOR_HIGHLIGHTTEXT))));
+
+#if defined(Q_OS_WINCE)
+ // ### hardcoded until I find out how to get it from the system settings.
+ pal.setColor(QPalette::LinkVisited, pal.highlight().color().dark(150));
+ pal.setColor(QPalette::Link, pal.highlight().color().light(130));
+ // Background == Base on Windows CE
+ if (qt_wince_is_smartphone() || qt_wince_is_pocket_pc())
+ pal.setColor(QPalette::Background, pal.base().color());
+#else
+ pal.setColor(QPalette::Link, Qt::blue);
+ pal.setColor(QPalette::LinkVisited, Qt::magenta);
+#endif
+
+
+
+ pal.setColor(QPalette::Inactive, QPalette::Button, pal.button().color());
+ pal.setColor(QPalette::Inactive, QPalette::Window, pal.background().color());
+ pal.setColor(QPalette::Inactive, QPalette::Light, pal.light().color());
+ pal.setColor(QPalette::Inactive, QPalette::Dark, pal.dark().color());
+
+ if (QSysInfo::WindowsVersion != QSysInfo::WV_NT && QSysInfo::WindowsVersion != QSysInfo::WV_95) {
+ if (pal.midlight() == pal.button())
+ pal.setColor(QPalette::Midlight, pal.button().color().lighter(110));
+ if (pal.background() != pal.base()) {
+ pal.setColor(QPalette::Inactive, QPalette::Highlight, pal.color(QPalette::Inactive, QPalette::Window));
+ pal.setColor(QPalette::Inactive, QPalette::HighlightedText, pal.color(QPalette::Inactive, QPalette::Text));
+ }
+ }
+ const QColor bg = pal.background().color();
+ const QColor fg = pal.foreground().color(), btn = pal.button().color();
+ QColor disabled((fg.red()+btn.red())/2,(fg.green()+btn.green())/2,
+ (fg.blue()+btn.blue())/2);
+ pal.setColorGroup(QPalette::Disabled, pal.foreground(), pal.button(), pal.light(),
+ pal.dark(), pal.mid(), pal.text(), pal.brightText(), pal.base(), pal.background() );
+ pal.setColor(QPalette::Disabled, QPalette::WindowText, disabled);
+ pal.setColor(QPalette::Disabled, QPalette::Text, disabled);
+ pal.setColor(QPalette::Disabled, QPalette::ButtonText, disabled);
+ pal.setColor(QPalette::Disabled, QPalette::Highlight,
+ QColor(qt_colorref2qrgb(GetSysColor(COLOR_HIGHLIGHT))));
+ pal.setColor(QPalette::Disabled, QPalette::HighlightedText,
+ QColor(qt_colorref2qrgb(GetSysColor(COLOR_HIGHLIGHTTEXT))));
+ pal.setColor(QPalette::Disabled, QPalette::Base, bg);
+
+ QApplicationPrivate::setSystemPalette(pal);
+
+ QApplicationPrivate::initializeWidgetPaletteHash();
+
+ QColor ttip(qt_colorref2qrgb(GetSysColor(COLOR_INFOBK)));
+
+ QColor ttipText(qt_colorref2qrgb(GetSysColor(COLOR_INFOTEXT)));
+ {
+#ifndef QT_NO_TOOLTIP
+ QPalette tiplabel(pal);
+ tiplabel.setColor(QPalette::All, QPalette::Button, ttip);
+ tiplabel.setColor(QPalette::All, QPalette::Window, ttip);
+ tiplabel.setColor(QPalette::All, QPalette::Text, ttipText);
+ tiplabel.setColor(QPalette::All, QPalette::WindowText, ttipText);
+ tiplabel.setColor(QPalette::All, QPalette::ButtonText, ttipText);
+ tiplabel.setColor(QPalette::All, QPalette::Button, ttip);
+ tiplabel.setColor(QPalette::All, QPalette::Window, ttip);
+ tiplabel.setColor(QPalette::All, QPalette::Text, ttipText);
+ tiplabel.setColor(QPalette::All, QPalette::WindowText, ttipText);
+ tiplabel.setColor(QPalette::All, QPalette::ButtonText, ttipText);
+ const QColor fg = tiplabel.foreground().color(), btn = tiplabel.button().color();
+ QColor disabled((fg.red()+btn.red())/2,(fg.green()+btn.green())/2,
+ (fg.blue()+btn.blue())/2);
+ tiplabel.setColor(QPalette::Disabled, QPalette::WindowText, disabled);
+ tiplabel.setColor(QPalette::Disabled, QPalette::Text, disabled);
+ tiplabel.setColor(QPalette::Disabled, QPalette::Base, Qt::white);
+ tiplabel.setColor(QPalette::Disabled, QPalette::BrightText, Qt::white);
+ QToolTip::setPalette(tiplabel);
+#endif //QT_NO_TOOLTIP
+ }
+}
+
+static void qt_set_windows_font_resources()
+{
+#ifndef Q_OS_WINCE
+ QFont menuFont;
+ QFont messageFont;
+ QFont statusFont;
+ QFont titleFont;
+ QFont iconTitleFont;
+ QT_WA({
+ NONCLIENTMETRICS ncm;
+ ncm.cbSize = FIELD_OFFSET(NONCLIENTMETRICS, lfMessageFont) + sizeof(LOGFONTW);
+ SystemParametersInfo(SPI_GETNONCLIENTMETRICS, ncm.cbSize , &ncm, 0);
+ menuFont = qt_LOGFONTtoQFont(ncm.lfMenuFont,true);
+ messageFont = qt_LOGFONTtoQFont(ncm.lfMessageFont,true);
+ statusFont = qt_LOGFONTtoQFont(ncm.lfStatusFont,true);
+ titleFont = qt_LOGFONTtoQFont(ncm.lfCaptionFont,true);
+ LOGFONTW lfIconTitleFont;
+ SystemParametersInfoW(SPI_GETICONTITLELOGFONT, sizeof(lfIconTitleFont), &lfIconTitleFont, 0);
+ iconTitleFont = qt_LOGFONTtoQFont(lfIconTitleFont,true);
+ } , {
+ // A version
+ NONCLIENTMETRICSA ncm;
+ ncm.cbSize = FIELD_OFFSET(NONCLIENTMETRICSA, lfMessageFont) + sizeof(LOGFONTA);
+ SystemParametersInfoA(SPI_GETNONCLIENTMETRICS, ncm.cbSize, &ncm, 0);
+ menuFont = qt_LOGFONTtoQFont((LOGFONT&)ncm.lfMenuFont,true);
+ messageFont = qt_LOGFONTtoQFont((LOGFONT&)ncm.lfMessageFont,true);
+ statusFont = qt_LOGFONTtoQFont((LOGFONT&)ncm.lfStatusFont,true);
+ titleFont = qt_LOGFONTtoQFont((LOGFONT&)ncm.lfCaptionFont,true);
+ LOGFONTA lfIconTitleFont;
+ SystemParametersInfoA(SPI_GETICONTITLELOGFONT, sizeof(lfIconTitleFont), &lfIconTitleFont, 0);
+ iconTitleFont = qt_LOGFONTtoQFont((LOGFONT&)lfIconTitleFont,true);
+ });
+
+ QApplication::setFont(menuFont, "QMenu");
+ QApplication::setFont(menuFont, "QMenuBar");
+ QApplication::setFont(messageFont, "QMessageBox");
+ QApplication::setFont(statusFont, "QTipLabel");
+ QApplication::setFont(statusFont, "QStatusBar");
+ QApplication::setFont(titleFont, "Q3TitleBar");
+ QApplication::setFont(titleFont, "QWorkspaceTitleBar");
+ QApplication::setFont(iconTitleFont, "QAbstractItemView");
+ QApplication::setFont(iconTitleFont, "QDockWidgetTitle");
+
+#else
+ LOGFONT lf;
+ HGDIOBJ stockFont = GetStockObject(SYSTEM_FONT);
+ GetObject(stockFont, sizeof(lf), &lf);
+ QFont systemFont = qt_LOGFONTtoQFont(lf, true);
+ QApplicationPrivate::setSystemFont(systemFont);
+ QFont smallerFont = systemFont;
+ if (qt_wince_is_mobile()) {
+ smallerFont.setPointSize(systemFont.pointSize()-1);
+ QApplication::setFont(smallerFont, "QTabBar");
+ }
+#endif// Q_OS_WINCE
+}
+
+static void qt_win_read_cleartype_settings()
+{
+ QT_WA({
+ UINT result;
+ BOOL ok;
+ ok = SystemParametersInfoW(SPI_GETFONTSMOOTHINGTYPE, 0, &result, 0);
+ if (ok)
+ qt_cleartype_enabled = (result == FE_FONTSMOOTHINGCLEARTYPE);
+ }, {
+ UINT result;
+ BOOL ok;
+ ok = SystemParametersInfoA(SPI_GETFONTSMOOTHINGTYPE, 0, &result, 0);
+ if (ok)
+ qt_cleartype_enabled = (result == FE_FONTSMOOTHINGCLEARTYPE);
+ });
+}
+
+
+static void qt_set_windows_resources()
+{
+ if (QApplication::type() != QApplication::Tty)
+ (void) QApplication::style(); // trigger creation of application style
+ qt_set_windows_font_resources();
+ qt_set_windows_color_resources();
+}
+
+void QApplicationPrivate::initializeWidgetPaletteHash()
+{
+ QPalette pal = *QApplicationPrivate::sys_pal;
+ QColor menuCol(qt_colorref2qrgb(GetSysColor(COLOR_MENU)));
+ QColor menuText(qt_colorref2qrgb(GetSysColor(COLOR_MENUTEXT)));
+ BOOL isFlat = 0;
+ if ((QSysInfo::WindowsVersion >= QSysInfo::WV_XP
+ && QSysInfo::WindowsVersion < QSysInfo::WV_NT_based))
+ SystemParametersInfo(0x1022 /*SPI_GETFLATMENU*/, 0, &isFlat, 0);
+ QPalette menu(pal);
+ // we might need a special color group for the menu.
+ menu.setColor(QPalette::Active, QPalette::Button, menuCol);
+ menu.setColor(QPalette::Active, QPalette::Text, menuText);
+ menu.setColor(QPalette::Active, QPalette::WindowText, menuText);
+ menu.setColor(QPalette::Active, QPalette::ButtonText, menuText);
+ const QColor fg = menu.foreground().color(), btn = menu.button().color();
+ QColor disabled(qt_colorref2qrgb(GetSysColor(COLOR_GRAYTEXT)));
+ menu.setColor(QPalette::Disabled, QPalette::WindowText, disabled);
+ menu.setColor(QPalette::Disabled, QPalette::Text, disabled);
+ menu.setColor(QPalette::Disabled, QPalette::Highlight,
+ QColor(qt_colorref2qrgb(GetSysColor(
+ (QSysInfo::WindowsVersion >= QSysInfo::WV_XP
+ && QSysInfo::WindowsVersion < QSysInfo::WV_NT_based)
+ && isFlat ? COLOR_MENUHILIGHT
+ : COLOR_HIGHLIGHT))));
+ menu.setColor(QPalette::Disabled, QPalette::HighlightedText, disabled);
+ menu.setColor(QPalette::Disabled, QPalette::Button,
+ menu.color(QPalette::Active, QPalette::Button));
+ menu.setColor(QPalette::Inactive, QPalette::Button,
+ menu.color(QPalette::Active, QPalette::Button));
+ menu.setColor(QPalette::Inactive, QPalette::Text,
+ menu.color(QPalette::Active, QPalette::Text));
+ menu.setColor(QPalette::Inactive, QPalette::WindowText,
+ menu.color(QPalette::Active, QPalette::WindowText));
+ menu.setColor(QPalette::Inactive, QPalette::ButtonText,
+ menu.color(QPalette::Active, QPalette::ButtonText));
+ menu.setColor(QPalette::Inactive, QPalette::Highlight,
+ menu.color(QPalette::Active, QPalette::Highlight));
+ menu.setColor(QPalette::Inactive, QPalette::HighlightedText,
+ menu.color(QPalette::Active, QPalette::HighlightedText));
+
+ if (QSysInfo::WindowsVersion != QSysInfo::WV_NT && QSysInfo::WindowsVersion != QSysInfo::WV_95)
+ menu.setColor(QPalette::Inactive, QPalette::ButtonText,
+ pal.color(QPalette::Inactive, QPalette::Dark));
+ QApplication::setPalette(menu, "QMenu");
+
+ if ((QSysInfo::WindowsVersion >= QSysInfo::WV_XP
+ && QSysInfo::WindowsVersion < QSysInfo::WV_NT_based) && isFlat) {
+ QColor menubar(qt_colorref2qrgb(GetSysColor(COLOR_MENUBAR)));
+ menu.setColor(QPalette::Active, QPalette::Button, menubar);
+ menu.setColor(QPalette::Disabled, QPalette::Button, menubar);
+ menu.setColor(QPalette::Inactive, QPalette::Button, menubar);
+ }
+ QApplication::setPalette(menu, "QMenuBar");
+}
+
+/*****************************************************************************
+ qt_init() - initializes Qt for Windows
+ *****************************************************************************/
+
+typedef BOOL (WINAPI *PtrUpdateLayeredWindow)(HWND hwnd, HDC hdcDst, const POINT *pptDst,
+ const SIZE *psize, HDC hdcSrc, const POINT *pptSrc, COLORREF crKey,
+ const Q_BLENDFUNCTION *pblend, DWORD dwflags);
+static PtrUpdateLayeredWindow ptrUpdateLayeredWindow = 0;
+
+static BOOL WINAPI qt_updateLayeredWindowIndirect(HWND hwnd, const Q_UPDATELAYEREDWINDOWINFO *info)
+{
+ return (*ptrUpdateLayeredWindow)(hwnd, info->hdcDst, info->pptDst, info->psize, info->hdcSrc,
+ info->pptSrc, info->crKey, info->pblend, info->dwFlags);
+}
+
+void qt_init(QApplicationPrivate *priv, int)
+{
+
+ int argc = priv->argc;
+ char **argv = priv->argv;
+ int i, j;
+
+ // Get command line params
+
+ j = argc ? 1 : 0;
+ for (i=1; i<argc; i++) {
+ if (argv[i] && *argv[i] != '-') {
+ argv[j++] = argv[i];
+ continue;
+ }
+#if defined(QT_DEBUG)
+ if (qstrcmp(argv[i], "-nograb") == 0)
+ appNoGrab = !appNoGrab;
+ else
+#endif // QT_DEBUG
+ if (qstrcmp(argv[i], "-direct3d") == 0)
+ QApplication::setAttribute(Qt::AA_MSWindowsUseDirect3DByDefault);
+ else
+ argv[j++] = argv[i];
+ }
+ if(j < priv->argc) {
+ priv->argv[j] = 0;
+ priv->argc = j;
+ }
+
+ // Get the application name/instance if qWinMain() was not invoked
+#ifndef Q_OS_WINCE
+ // No message boxes but important ones
+ SetErrorMode(SetErrorMode(0) | SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
+#endif
+
+ if (appInst == 0) {
+ QT_WA({
+ appInst = GetModuleHandle(0);
+ }, {
+ appInst = GetModuleHandleA(0);
+ });
+ }
+
+#ifndef Q_OS_WINCE
+ // Initialize OLE/COM
+ // S_OK means success and S_FALSE means that it has already
+ // been initialized
+ HRESULT r;
+ r = OleInitialize(0);
+ if (r != S_OK && r != S_FALSE) {
+ qWarning("Qt: Could not initialize OLE (error %x)", (unsigned int)r);
+ }
+#endif
+
+ // Misc. initialization
+#if defined(QT_DEBUG) && !defined(Q_OS_WINCE)
+ GdiSetBatchLimit(1);
+#endif
+
+ // initialize key mapper
+ QKeyMapper::changeKeyboard();
+
+ QColormap::initialize();
+ QFont::initialize();
+#ifndef QT_NO_CURSOR
+ QCursorData::initialize();
+#endif
+ qApp->setObjectName(QLatin1String(theAppName));
+
+#if !defined(Q_OS_WINCE)
+ // default font
+ HFONT hfont = (HFONT)GetStockObject(DEFAULT_GUI_FONT);
+ QFont f(QLatin1String("MS Sans Serif"),8);
+ int result = 0;
+ QT_WA({
+ LOGFONT lf;
+ if (result = GetObject(hfont, sizeof(lf), &lf))
+ f = qt_LOGFONTtoQFont((LOGFONT&)lf,true);
+ } , {
+ LOGFONTA lf;
+ if (result = GetObjectA(hfont, sizeof(lf), &lf))
+ f = qt_LOGFONTtoQFont((LOGFONT&)lf,true);
+ });
+ if (result
+ && QSysInfo::WindowsVersion >= QSysInfo::WV_2000
+ && QSysInfo::WindowsVersion <= QSysInfo::WV_NT_based
+ && f.family() == QLatin1String("MS Shell Dlg"))
+ f.setFamily(QLatin1String("MS Shell Dlg 2"));
+ QApplicationPrivate::setSystemFont(f);
+#else //Q_OS_WINCE
+ LOGFONT lf;
+ HGDIOBJ stockFont = GetStockObject(SYSTEM_FONT);
+ int result = 0;
+ result = GetObject(stockFont, sizeof(lf), &lf);
+ QFont font = qt_LOGFONTtoQFont(lf, true);
+ if (result)
+ QApplicationPrivate::setSystemFont(font);
+#endif //Q_OS_WINCE
+
+ // QFont::locale_init(); ### Uncomment when it does something on Windows
+
+ if (QApplication::desktopSettingsAware())
+ qt_set_windows_resources();
+
+ QT_WA({
+ WM95_MOUSEWHEEL = RegisterWindowMessage(L"MSWHEEL_ROLLMSG");
+ } , {
+ WM95_MOUSEWHEEL = RegisterWindowMessageA("MSWHEEL_ROLLMSG");
+ });
+ initWinTabFunctions();
+ QApplicationPrivate::inputContext = new QWinInputContext(0);
+
+ // Read the initial cleartype settings...
+ qt_win_read_cleartype_settings();
+ qt_win_owndc_required = false;
+
+ extern void qt_win_initialize_directdraw();
+ qt_win_initialize_directdraw();
+
+#ifndef Q_OS_WINCE
+ ptrUpdateLayeredWindowIndirect =
+ (PtrUpdateLayeredWindowIndirect) QLibrary::resolve(QLatin1String("user32"),
+ "UpdateLayeredWindowIndirect");
+ ptrUpdateLayeredWindow =
+ (PtrUpdateLayeredWindow) QLibrary::resolve(QLatin1String("user32"),
+ "UpdateLayeredWindow");
+
+ if (ptrUpdateLayeredWindow && !ptrUpdateLayeredWindowIndirect)
+ ptrUpdateLayeredWindowIndirect = qt_updateLayeredWindowIndirect;
+#endif
+}
+
+/*****************************************************************************
+ qt_cleanup() - cleans up when the application is finished
+ *****************************************************************************/
+
+void qt_cleanup()
+{
+ unregWinClasses();
+ QPixmapCache::clear();
+
+#ifndef QT_NO_CURSOR
+ QCursorData::cleanup();
+#endif
+ QFont::cleanup();
+ QColormap::cleanup();
+ if (displayDC) {
+ ReleaseDC(0, displayDC);
+ displayDC = 0;
+ }
+
+ delete QApplicationPrivate::inputContext;
+ QApplicationPrivate::inputContext = 0;
+
+#ifndef Q_OS_WINCE
+ // Deinitialize OLE/COM
+ OleUninitialize();
+#endif
+}
+
+
+/*****************************************************************************
+ Platform specific global and internal functions
+ *****************************************************************************/
+
+Q_GUI_EXPORT int qWinAppCmdShow() // get main window show command
+{
+ return appCmdShow;
+}
+
+
+Q_GUI_EXPORT HDC qt_win_display_dc() // get display DC
+{
+ Q_ASSERT(qApp && qApp->thread() == QThread::currentThread());
+ if (!displayDC)
+ displayDC = GetDC(0);
+ return displayDC;
+}
+
+bool qt_nograb() // application no-grab option
+{
+#if defined(QT_DEBUG)
+ return appNoGrab;
+#else
+ return false;
+#endif
+}
+
+typedef QHash<QString, int> WinClassNameHash;
+Q_GLOBAL_STATIC(WinClassNameHash, winclassNames)
+
+const QString qt_reg_winclass(QWidget *w) // register window class
+{
+ int flags = w->windowFlags();
+ int type = flags & Qt::WindowType_Mask;
+
+ uint style;
+ bool icon;
+ QString cname;
+ if (flags & Qt::MSWindowsOwnDC) {
+ cname = QLatin1String("QWidgetOwnDC");
+ style = CS_DBLCLKS;
+#ifndef Q_OS_WINCE
+ style |= CS_OWNDC;
+#endif
+ icon = true;
+ } else if (type == Qt::Tool || type == Qt::ToolTip){
+ style = CS_DBLCLKS;
+ if (w->inherits("QTipLabel") || w->inherits("QAlphaWidget")) {
+ if ((QSysInfo::WindowsVersion >= QSysInfo::WV_XP
+ && QSysInfo::WindowsVersion < QSysInfo::WV_NT_based)) {
+ style |= 0x00020000; // CS_DROPSHADOW
+ }
+ cname = QLatin1String("QToolTip");
+ } else {
+ cname = QLatin1String("QTool");
+ }
+#ifndef Q_OS_WINCE
+ style |= CS_SAVEBITS;
+#endif
+ icon = false;
+ } else if (type == Qt::Popup) {
+ cname = QLatin1String("QPopup");
+ style = CS_DBLCLKS;
+#ifndef Q_OS_WINCE
+ style |= CS_SAVEBITS;
+#endif
+ if ((QSysInfo::WindowsVersion >= QSysInfo::WV_XP
+ && QSysInfo::WindowsVersion < QSysInfo::WV_NT_based))
+ style |= 0x00020000; // CS_DROPSHADOW
+ icon = false;
+ } else {
+ cname = QLatin1String("QWidget");
+ style = CS_DBLCLKS;
+ icon = true;
+ }
+
+#ifndef Q_OS_WINCE
+ // force CS_OWNDC when the GL graphics system is
+ // used as the default renderer
+ if (qt_win_owndc_required)
+ style |= CS_OWNDC;
+#endif
+
+#ifdef Q_OS_WINCE
+ // We need to register the classes with the
+ // unique ID on WinCE to make sure we can
+ // move the windows to the front when starting
+ // a second instance.
+ wchar_t uniqueAppID[256];
+ GetModuleFileNameW(0, uniqueAppID, 255);
+ cname = QString::number(RegisterWindowMessageW(
+ (const wchar_t *) QString::fromUtf16((const ushort *)uniqueAppID).toLower().replace(QString(QString::fromLatin1("\\")),
+ QString(QString::fromLatin1("_"))).utf16()));
+#endif
+
+ // since multiple Qt versions can be used in one process
+ // each one has to have window class names with a unique name
+ // The first instance gets the unmodified name; if the class
+ // has already been registered by another instance of Qt then
+ // add an instance-specific ID, the address of the window proc.
+ static int classExists = -1;
+
+ if (classExists == -1) {
+ QT_WA({
+ WNDCLASS wcinfo;
+ classExists = GetClassInfo((HINSTANCE)qWinAppInst(), (TCHAR*)cname.utf16(), &wcinfo);
+ classExists = classExists && wcinfo.lpfnWndProc != QtWndProc;
+ }, {
+ WNDCLASSA wcinfo;
+ classExists = GetClassInfoA((HINSTANCE)qWinAppInst(), cname.toLatin1(), &wcinfo);
+ classExists = classExists && wcinfo.lpfnWndProc != QtWndProc;
+ });
+ }
+
+ if (classExists)
+ cname += QString::number((quintptr)QtWndProc);
+
+ if (winclassNames()->contains(cname)) // already registered in our list
+ return cname;
+
+ ATOM atom;
+#ifndef Q_OS_WINCE
+ HBRUSH bgBrush = (HBRUSH)GetSysColorBrush(COLOR_WINDOW);
+ QT_WA({
+ WNDCLASS wc;
+ wc.style = style;
+ wc.lpfnWndProc = (WNDPROC)QtWndProc;
+ wc.cbClsExtra = 0;
+ wc.cbWndExtra = 0;
+ wc.hInstance = (HINSTANCE)qWinAppInst();
+ if (icon) {
+ wc.hIcon = LoadIcon(appInst, L"IDI_ICON1");
+ if (!wc.hIcon)
+ wc.hIcon = LoadIcon(0, IDI_APPLICATION);
+ } else {
+ wc.hIcon = 0;
+ }
+ wc.hCursor = 0;
+ wc.hbrBackground= bgBrush;
+ wc.lpszMenuName = 0;
+ wc.lpszClassName= (TCHAR*)cname.utf16();
+ atom = RegisterClass(&wc);
+ } , {
+ WNDCLASSA wc;
+ wc.style = style;
+ wc.lpfnWndProc = (WNDPROC)QtWndProc;
+ wc.cbClsExtra = 0;
+ wc.cbWndExtra = 0;
+ wc.hInstance = (HINSTANCE)qWinAppInst();
+ if (icon) {
+ wc.hIcon = LoadIconA(appInst, (char*)"IDI_ICON1");
+ if (!wc.hIcon)
+ wc.hIcon = LoadIconA(0, (char*)IDI_APPLICATION);
+ } else {
+ wc.hIcon = 0;
+ }
+ wc.hCursor = 0;
+ wc.hbrBackground= bgBrush;
+ wc.lpszMenuName = 0;
+ QByteArray tempArray = cname.toLatin1();
+ wc.lpszClassName= tempArray;
+ atom = RegisterClassA(&wc);
+ });
+#else
+ WNDCLASS wc;
+ wc.style = style;
+ wc.lpfnWndProc = (WNDPROC)QtWndProc;
+ wc.cbClsExtra = 0;
+ wc.cbWndExtra = 0;
+ wc.hInstance = (HINSTANCE)qWinAppInst();
+ if (icon) {
+ wc.hIcon = LoadIcon(appInst, L"IDI_ICON1");
+// if (!wc.hIcon)
+// wc.hIcon = LoadIcon(0, IDI_APPLICATION);
+ } else {
+ wc.hIcon = 0;
+ }
+ wc.hCursor = 0;
+ wc.hbrBackground= 0;
+ wc.lpszMenuName = 0;
+ wc.lpszClassName= (TCHAR*)cname.utf16();
+ atom = RegisterClass(&wc);
+#endif
+
+#ifndef QT_NO_DEBUG
+ if (!atom)
+ qErrnoWarning("QApplication::regClass: Registering window class failed.");
+#endif
+
+ winclassNames()->insert(cname, 1);
+ return cname;
+}
+
+Q_GUI_EXPORT const QString qt_getRegisteredWndClass()
+{
+ QWidget w;
+ return qt_reg_winclass(&w);
+}
+
+static void unregWinClasses()
+{
+ WinClassNameHash *hash = winclassNames();
+ QHash<QString, int>::ConstIterator it = hash->constBegin();
+ while (it != hash->constEnd()) {
+ QT_WA({
+ UnregisterClass((TCHAR*)it.key().utf16(), (HINSTANCE)qWinAppInst());
+ } , {
+ UnregisterClassA(it.key().toLatin1(), (HINSTANCE)qWinAppInst());
+ });
+ ++it;
+ }
+ hash->clear();
+}
+
+
+/*****************************************************************************
+ Safe configuration (move,resize,setGeometry) mechanism to avoid
+ recursion when processing messages.
+ *****************************************************************************/
+
+struct QWinConfigRequest {
+ WId id; // widget to be configured
+ int req; // 0=move, 1=resize, 2=setGeo
+ int x, y, w, h; // request parameters
+};
+
+static QList<QWinConfigRequest*> *configRequests = 0;
+
+void qWinRequestConfig(WId id, int req, int x, int y, int w, int h)
+{
+ if (!configRequests) // create queue
+ configRequests = new QList<QWinConfigRequest*>;
+ QWinConfigRequest *r = new QWinConfigRequest;
+ r->id = id; // create new request
+ r->req = req;
+ r->x = x;
+ r->y = y;
+ r->w = w;
+ r->h = h;
+ configRequests->append(r); // store request in queue
+}
+
+Q_GUI_EXPORT void qWinProcessConfigRequests() // perform requests in queue
+{
+ if (!configRequests)
+ return;
+ QWinConfigRequest *r;
+ for (;;) {
+ if (configRequests->isEmpty())
+ break;
+ r = configRequests->takeLast();
+ QWidget *w = QWidget::find(r->id);
+ QRect rect(r->x, r->y, r->w, r->h);
+ int req = r->req;
+ delete r;
+
+ if ( w ) { // widget exists
+ if (w->testAttribute(Qt::WA_WState_ConfigPending))
+ return; // biting our tail
+ if (req == 0)
+ w->move(rect.topLeft());
+ else if (req == 1)
+ w->resize(rect.size());
+ else
+ w->setGeometry(rect);
+ }
+ }
+ delete configRequests;
+ configRequests = 0;
+}
+
+
+/*****************************************************************************
+ GUI event dispatcher
+ *****************************************************************************/
+
+class QGuiEventDispatcherWin32 : public QEventDispatcherWin32
+{
+ Q_DECLARE_PRIVATE(QEventDispatcherWin32)
+public:
+ QGuiEventDispatcherWin32(QObject *parent = 0);
+ bool processEvents(QEventLoop::ProcessEventsFlags flags);
+};
+
+QGuiEventDispatcherWin32::QGuiEventDispatcherWin32(QObject *parent)
+ : QEventDispatcherWin32(parent)
+{
+ createInternalHwnd();
+}
+
+bool QGuiEventDispatcherWin32::processEvents(QEventLoop::ProcessEventsFlags flags)
+{
+ if (!QEventDispatcherWin32::processEvents(flags))
+ return false;
+
+ if (configRequests) // any pending configs?
+ qWinProcessConfigRequests();
+
+ return true;
+}
+
+void QApplicationPrivate::createEventDispatcher()
+{
+ Q_Q(QApplication);
+ if (q->type() != QApplication::Tty)
+ eventDispatcher = new QGuiEventDispatcherWin32(q);
+ else
+ eventDispatcher = new QEventDispatcherWin32(q);
+}
+
+/*****************************************************************************
+ Platform specific QApplication members
+ *****************************************************************************/
+
+#ifdef QT3_SUPPORT
+void QApplication::setMainWidget(QWidget *mainWidget)
+{
+ QApplicationPrivate::main_widget = mainWidget;
+ if (QApplicationPrivate::main_widget && windowIcon().isNull()
+ && QApplicationPrivate::main_widget->testAttribute(Qt::WA_SetWindowIcon))
+ setWindowIcon(QApplicationPrivate::main_widget->windowIcon());
+}
+#endif
+
+#ifndef QT_NO_CURSOR
+
+/*****************************************************************************
+ QApplication cursor stack
+ *****************************************************************************/
+
+void QApplication::setOverrideCursor(const QCursor &cursor)
+{
+ qApp->d_func()->cursor_list.prepend(cursor);
+ SetCursor(qApp->d_func()->cursor_list.first().handle());
+}
+
+void QApplication::restoreOverrideCursor()
+{
+ if (qApp->d_func()->cursor_list.isEmpty())
+ return;
+ qApp->d_func()->cursor_list.removeFirst();
+
+ if (!qApp->d_func()->cursor_list.isEmpty()) {
+ SetCursor(qApp->d_func()->cursor_list.first().handle());
+ } else {
+ QWidget *w = QWidget::find(curWin);
+ if (w)
+ SetCursor(w->cursor().handle());
+ else
+ SetCursor(QCursor(Qt::ArrowCursor).handle());
+ }
+}
+
+#endif
+
+/*
+ Internal function called from QWidget::setCursor()
+ force is true if this function is called from dispatchEnterLeave, it means that the
+ mouse is actually directly under this widget.
+*/
+
+#ifndef QT_NO_CURSOR
+void qt_win_set_cursor(QWidget *w, bool force)
+{
+ static QPointer<QWidget> lastUnderMouse = 0;
+ if (force) {
+ lastUnderMouse = w;
+ } else if (w->testAttribute(Qt::WA_WState_Created) && lastUnderMouse
+ && lastUnderMouse->effectiveWinId() == w->effectiveWinId()) {
+ w = lastUnderMouse;
+ }
+
+ if (!curWin && w && w->internalWinId())
+ return;
+ QWidget* cW = w && !w->internalWinId() ? w : QWidget::find(curWin);
+ if (!cW || cW->window() != w->window() ||
+ !cW->isVisible() || !cW->underMouse() || QApplication::overrideCursor())
+ return;
+
+ SetCursor(cW->cursor().handle());
+}
+#endif // QT_NO_CURSOR
+
+Qt::KeyboardModifiers qt_win_getKeyboardModifiers()
+{
+ Qt::KeyboardModifiers modifiers = Qt::NoModifier;
+ if (GetKeyState(VK_SHIFT) < 0)
+ modifiers |= Qt::ShiftModifier;
+ if (GetKeyState(VK_CONTROL) < 0)
+ modifiers |= Qt::ControlModifier;
+ if (GetKeyState(VK_MENU) < 0)
+ modifiers |= Qt::AltModifier;
+ return modifiers;
+}
+
+/*****************************************************************************
+ Routines to find a Qt widget from a screen position
+ *****************************************************************************/
+
+QWidget *QApplication::topLevelAt(const QPoint &pos)
+{
+ POINT p;
+ HWND win;
+ QWidget *w;
+ p.x = pos.x();
+ p.y = pos.y();
+ win = WindowFromPoint(p);
+ if (!win)
+ return 0;
+
+ w = QWidget::find(win);
+ while (!w && win) {
+ win = GetParent(win);
+ w = QWidget::find(win);
+ }
+ return w ? w->window() : 0;
+}
+
+void QApplication::beep()
+{
+ MessageBeep(MB_OK);
+}
+
+static void alert_widget(QWidget *widget, int duration)
+{
+ bool stopFlash = duration < 0;
+
+ if (!pFlashWindowEx) {
+#ifndef Q_OS_WINCE
+ QLibrary themeLib(QLatin1String("user32"));
+ pFlashWindowEx = (PtrFlashWindowEx)themeLib.resolve("FlashWindowEx");
+#endif
+ }
+
+ if (pFlashWindowEx && widget && (!widget->isActiveWindow() || stopFlash)) {
+ DWORD timeOut = GetCaretBlinkTime();
+ if (timeOut <= 0)
+ timeOut = 250;
+
+ UINT flashCount;
+ if (duration == 0)
+ flashCount = 10;
+ else
+ flashCount = duration/timeOut;
+
+ FLASHWINFO info;
+ info.cbSize = sizeof(info);
+ info.hwnd = widget->window()->winId();
+ info.dwFlags = stopFlash ? FLASHW_STOP : FLASHW_TRAY;
+ info.dwTimeout = stopFlash ? 0 : timeOut;
+ info.uCount = stopFlash ? 0 : flashCount;
+
+ pFlashWindowEx(&info);
+ }
+}
+
+void QApplication::alert(QWidget *widget, int duration)
+{
+ if (!QApplicationPrivate::checkInstance("alert"))
+ return;
+
+ if (widget) {
+ alert_widget(widget, duration);
+ } else {
+ const QWidgetList toplevels(topLevelWidgets());
+ for (int i = 0; i < toplevels.count(); ++i) {
+ QWidget *topLevel = toplevels.at(i);
+ alert_widget(topLevel, duration);
+ }
+ }
+}
+
+QString QApplicationPrivate::appName() const
+{
+ return QCoreApplicationPrivate::appName();
+}
+
+
+/*****************************************************************************
+ Main event loop
+ *****************************************************************************/
+
+extern uint qGlobalPostedEventsCount();
+
+/*!
+ \internal
+ \since 4.1
+
+ If \a gotFocus is true, \a widget will become the active window.
+ Otherwise the active window is reset to 0.
+*/
+void QApplication::winFocus(QWidget *widget, bool gotFocus)
+{
+ if (d_func()->inPopupMode()) // some delayed focus event to ignore
+ return;
+ if (gotFocus) {
+ setActiveWindow(widget);
+ if (QApplicationPrivate::active_window
+ && (QApplicationPrivate::active_window->windowType() == Qt::Dialog)) {
+ // raise the entire application, not just the dialog
+ QWidget* mw = QApplicationPrivate::active_window;
+#ifndef Q_OS_WINCE
+ while(mw->parentWidget() && (mw->windowType() == Qt::Dialog))
+ mw = mw->parentWidget()->window();
+ if (mw->testAttribute(Qt::WA_WState_Created) && mw != QApplicationPrivate::active_window)
+ SetWindowPos(mw->internalWinId(), HWND_TOP, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE);
+#else
+ // On Desktop Windows, we set the first parent of the dialog on top
+ // Child windows will be automatically set above again.
+ // On Windows CE we pass no parent in CreateWindowEx as otherwise
+ // dialogs get embedded into the parent window. Thus we need to
+ // manually iterate and reactivate all windows from bottom up.
+ QList<QWidget*> raiseList;
+ raiseList.push_back(mw);
+ while(mw->parentWidget() && (mw->windowType() == Qt::Dialog)) {
+ mw = mw->parentWidget()->window();
+ raiseList.push_back(mw);
+ }
+ while(!raiseList.isEmpty()) {
+ mw = raiseList.takeLast();
+ if (mw->testAttribute(Qt::WA_WState_Created)) {
+ HWND state = HWND_TOP;
+ if (mw->windowFlags() & Qt::WindowStaysOnBottomHint)
+ state = HWND_BOTTOM;
+ else if (mw->windowFlags() & Qt::WindowStaysOnTopHint)
+ state = HWND_TOPMOST;
+ SetWindowPos(mw->internalWinId(), state, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE);
+ }
+ }
+#endif
+ }
+ } else {
+ setActiveWindow(0);
+ }
+}
+
+
+//
+// QtWndProc() receives all messages from the main event loop
+//
+
+static bool inLoop = false;
+static int inputcharset = CP_ACP;
+
+#define RETURN(x) { inLoop=false;return x; }
+
+static bool qt_is_translatable_mouse_event(UINT message)
+{
+ return (message >= WM_MOUSEFIRST && message <= WM_MOUSELAST ||
+ message >= WM_XBUTTONDOWN && message <= WM_XBUTTONDBLCLK)
+ && message != WM_MOUSEWHEEL
+
+#ifndef Q_OS_WINCE
+ || message >= WM_NCMOUSEMOVE && message <= WM_NCMBUTTONDBLCLK
+#endif
+ ;
+}
+
+extern "C"
+LRESULT CALLBACK QtWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
+{
+ bool result = true;
+ QEvent::Type evt_type = QEvent::None;
+ QETWidget *widget = 0;
+
+ // there is no need to process pakcets from tablet unless
+ // it is actually on the tablet, a flag to let us know...
+ int nPackets; // the number of packets we get from the queue
+
+ long res = 0;
+ if (!qApp) // unstable app state
+ RETURN(QWinInputContext::DefWindowProc(hwnd,message,wParam,lParam))
+
+ QScopedLoopLevelCounter loopLevelCounter(QThreadData::get2(qApp->thread()));
+
+#if 0
+ // make sure we update widgets also when the user resizes
+ if (inLoop && qApp->loopLevel())
+ qApp->sendPostedEvents(0, QEvent::Paint);
+#endif
+
+ inLoop = true;
+
+ MSG msg;
+ msg.hwnd = hwnd; // create MSG structure
+ msg.message = message; // time and pt fields ignored
+ msg.wParam = wParam;
+ msg.lParam = lParam;
+ msg.pt.x = GET_X_LPARAM(lParam);
+ msg.pt.y = GET_Y_LPARAM(lParam);
+ // If it's a non-client-area message the coords are screen coords, otherwise they are
+ // client coords.
+#ifndef Q_OS_WINCE
+ if (message < WM_NCMOUSEMOVE || message > WM_NCMBUTTONDBLCLK)
+#endif
+ ClientToScreen(msg.hwnd, &msg.pt);
+
+ /*
+ // sometimes the autograb is not released, so the clickevent is sent
+ // to the wrong window. We ignore this for now, because it doesn't
+ // cause any problems.
+ if (msg.message == WM_LBUTTONDOWN || msg.message == WM_RBUTTONDOWN || msg.message == WM_MBUTTONDOWN) {
+ HWND handle = WindowFromPoint(msg.pt);
+ if (msg.hwnd != handle) {
+ msg.hwnd = handle;
+ hwnd = handle;
+ }
+ }
+ */
+
+#if defined(QT_NON_COMMERCIAL)
+ QT_NC_WNDPROC
+#endif
+
+ // send through app filter
+ if (qApp->filterEvent(&msg, &res))
+ return res;
+
+ // close any opened ime candidate window (enabled only on a popup widget)
+ if (imeParentWnd && QApplication::activePopupWidget()
+ && (message == WM_MBUTTONDOWN || message == WM_XBUTTONDOWN
+ || message == WM_LBUTTONDOWN || message == WM_RBUTTONDOWN
+#ifndef Q_OS_WINCE
+ || message == WM_NCMBUTTONDOWN || message == WM_NCLBUTTONDOWN
+ || message == WM_NCRBUTTONDOWN)) {
+#else
+ )) {
+#endif
+ ::SendMessage(imeParentWnd, WM_IME_ENDCOMPOSITION, 0, 0);
+ }
+
+ switch (message) {
+#ifndef Q_OS_WINCE
+ case WM_QUERYENDSESSION: {
+ if (sm_smActive) // bogus message from windows
+ RETURN(true);
+
+ sm_smActive = true;
+ sm_blockUserInput = true; // prevent user-interaction outside interaction windows
+ sm_cancel = false;
+ if (qt_session_manager_self)
+ qApp->commitData(*qt_session_manager_self);
+ if (lParam & ENDSESSION_LOGOFF) {
+ _flushall();
+ }
+ RETURN(!sm_cancel);
+ }
+ case WM_ENDSESSION: {
+ sm_smActive = false;
+ sm_blockUserInput = false;
+ bool endsession = (bool) wParam;
+
+ // we receive the message for each toplevel window included internal hidden ones,
+ // but the aboutToQuit signal should be emitted only once.
+ QApplicationPrivate *qAppPriv = getQApplicationPrivateInternal();
+ if (endsession && !qAppPriv->aboutToQuitEmitted) {
+ qAppPriv->aboutToQuitEmitted = true;
+ int index = QApplication::staticMetaObject.indexOfSignal("aboutToQuit()");
+ qApp->qt_metacall(QMetaObject::InvokeMetaMethod, index,0);
+ // since the process will be killed immediately quit() has no real effect
+ qApp->quit();
+ }
+
+ RETURN(0);
+ }
+ case WM_DISPLAYCHANGE:
+ if (qApp->type() == QApplication::Tty)
+ break;
+ if (qt_desktopWidget) {
+ qt_desktopWidget->move(GetSystemMetrics(76), GetSystemMetrics(77));
+ QSize sz(GetSystemMetrics(78), GetSystemMetrics(79));
+ if (sz == qt_desktopWidget->size()) {
+ // a screen resized without changing size of the virtual desktop
+ QResizeEvent rs(sz, qt_desktopWidget->size());
+ QApplication::sendEvent(qt_desktopWidget, &rs);
+ } else {
+ qt_desktopWidget->resize(sz);
+ }
+ }
+ break;
+#endif
+
+ case WM_SETTINGCHANGE:
+#ifdef Q_OS_WINCE
+ // CE SIP hide/show
+ if (wParam == SPI_SETSIPINFO) {
+ QResizeEvent re(QSize(0, 0), QSize(0, 0)); // Calculated by QDesktopWidget
+ QApplication::sendEvent(qt_desktopWidget, &re);
+ break;
+ }
+#endif
+ // ignore spurious XP message when user logs in again after locking
+ if (qApp->type() == QApplication::Tty)
+ break;
+ if (QApplication::desktopSettingsAware() && wParam != SPI_SETWORKAREA) {
+ widget = (QETWidget*)QWidget::find(hwnd);
+ if (widget) {
+ if (wParam == SPI_SETNONCLIENTMETRICS)
+ widget->markFrameStrutDirty();
+ }
+ }
+ else if (qt_desktopWidget && wParam == SPI_SETWORKAREA) {
+ qt_desktopWidget->move(GetSystemMetrics(76), GetSystemMetrics(77));
+ QSize sz(GetSystemMetrics(78), GetSystemMetrics(79));
+ if (sz == qt_desktopWidget->size()) {
+ // a screen resized without changing size of the virtual desktop
+ QResizeEvent rs(sz, qt_desktopWidget->size());
+ QApplication::sendEvent(qt_desktopWidget, &rs);
+ } else {
+ qt_desktopWidget->resize(sz);
+ }
+ }
+
+ if (wParam == SPI_SETFONTSMOOTHINGTYPE) {
+ qt_win_read_cleartype_settings();
+ foreach (QWidget *w, qApp->topLevelWidgets()) {
+ if (!w->isVisible())
+ continue;
+ ((QETWidget *) w)->forceUpdate();
+ }
+ }
+
+ break;
+ case WM_SYSCOLORCHANGE:
+ if (qApp->type() == QApplication::Tty)
+ break;
+ if (QApplication::desktopSettingsAware()) {
+ widget = (QETWidget*)QWidget::find(hwnd);
+ if (widget && !widget->parentWidget())
+ qt_set_windows_color_resources();
+ }
+ break;
+
+ case WM_LBUTTONDOWN:
+ case WM_MBUTTONDOWN:
+ case WM_RBUTTONDOWN:
+ case WM_XBUTTONDOWN:
+ if (qt_win_ignoreNextMouseReleaseEvent)
+ qt_win_ignoreNextMouseReleaseEvent = false;
+ break;
+
+ case WM_LBUTTONUP:
+ case WM_MBUTTONUP:
+ case WM_RBUTTONUP:
+ case WM_XBUTTONUP:
+ if (qt_win_ignoreNextMouseReleaseEvent) {
+ qt_win_ignoreNextMouseReleaseEvent = false;
+ if (qt_button_down && qt_button_down->internalWinId() == autoCaptureWnd) {
+ releaseAutoCapture();
+ qt_button_down = 0;
+ }
+
+ RETURN(0);
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ if (!widget)
+ widget = (QETWidget*)QWidget::find(hwnd);
+ if (!widget) // don't know this widget
+ goto do_default;
+
+ if (app_do_modal) { // modal event handling
+ int ret = 0;
+ if (!qt_try_modal(widget, &msg, ret))
+ RETURN(ret);
+ }
+
+ res = 0;
+ if (widget->winEvent(&msg, &res)) // send through widget filter
+ RETURN(res);
+
+ if (qt_is_translatable_mouse_event(message)) {
+ if (qApp->activePopupWidget() != 0) { // in popup mode
+ POINT curPos = msg.pt;
+ QWidget* w = QApplication::widgetAt(curPos.x, curPos.y);
+ if (w)
+ widget = (QETWidget*)w;
+ }
+
+ if (!qt_tabletChokeMouse) {
+ result = widget->translateMouseEvent(msg); // mouse event
+#if defined(Q_OS_WINCE) && !defined(QT_NO_CONTEXTMENU)
+ if (message == WM_LBUTTONDOWN && widget != qApp->activePopupWidget()) {
+ QWidget* alienWidget = widget;
+ if ((alienWidget != qApp->activePopupWidget()) && (alienWidget->contextMenuPolicy() != Qt::PreventContextMenu)) {
+ QPoint pos(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
+ QPoint globalPos(msg.pt.x, msg.pt.y);
+ // In case we are using Alien, then the widget to
+ // send the context menu event is a different one
+ if (!alienWidget->testAttribute(Qt::WA_NativeWindow) && !alienWidget->testAttribute(Qt::WA_PaintOnScreen)) {
+ alienWidget = QApplication::widgetAt(globalPos);
+ pos = alienWidget->mapFromGlobal(globalPos);
+ }
+ SHRGINFO shrg;
+ shrg.cbSize = sizeof(shrg);
+ shrg.hwndClient = hwnd;
+ shrg.ptDown.x = GET_X_LPARAM(lParam);
+ shrg.ptDown.y = GET_Y_LPARAM(lParam);
+ shrg.dwFlags = SHRG_RETURNCMD | SHRG_NOANIMATION;
+ resolveAygLibs();
+ if (ptrRecognizeGesture && (ptrRecognizeGesture(&shrg) == GN_CONTEXTMENU)) {
+ if (qApp->activePopupWidget())
+ qApp->activePopupWidget()->close();
+ QContextMenuEvent e(QContextMenuEvent::Mouse, pos, globalPos);
+ result = qt_sendSpontaneousEvent(alienWidget, &e);
+ }
+ }
+ }
+#endif
+ } else {
+ // Sometimes we only get a WM_MOUSEMOVE message
+ // and sometimes we get both a WM_MOUSEMOVE and
+ // a WM_LBUTTONDOWN/UP, this creates a spurious mouse
+ // press/release event, using the winPeekMessage
+ // will help us fix this. This leaves us with a
+ // question:
+ // This effectively kills using the mouse AND the
+ // tablet simultaneously, well creates wacky input.
+ // Is this going to be a problem? (probably not)
+ bool next_is_button = false;
+ bool is_mouse_move = (message == WM_MOUSEMOVE);
+ if (is_mouse_move) {
+ MSG msg1;
+ if (winPeekMessage(&msg1, msg.hwnd, WM_MOUSEFIRST,
+ WM_MOUSELAST, PM_NOREMOVE))
+ next_is_button = (msg1.message == WM_LBUTTONUP
+ || msg1.message == WM_LBUTTONDOWN);
+ }
+ if (!is_mouse_move || (is_mouse_move && !next_is_button))
+ qt_tabletChokeMouse = false;
+ }
+ } else if (message == WM95_MOUSEWHEEL) {
+ result = widget->translateWheelEvent(msg);
+ } else {
+ switch (message) {
+ case WM_KEYDOWN: // keyboard event
+ case WM_SYSKEYDOWN:
+ qt_keymapper_private()->updateKeyMap(msg);
+ // fall-through intended
+ case WM_KEYUP:
+ case WM_SYSKEYUP:
+ case WM_IME_CHAR:
+ case WM_IME_KEYDOWN:
+ case WM_CHAR: {
+ MSG msg1;
+ bool anyMsg = winPeekMessage(&msg1, msg.hwnd, 0, 0, PM_NOREMOVE);
+ if (anyMsg && msg1.message == WM_DEADCHAR) {
+ result = true; // consume event since there is a dead char next
+ break;
+ }
+ QWidget *g = QWidget::keyboardGrabber();
+ if (g && qt_get_tablet_widget() && hwnd == qt_get_tablet_widget()->winId()) {
+ // if we get an event for the internal tablet widget,
+ // then don't send it to the keyboard grabber, but
+ // send it to the widget itself (we don't use it right
+ // now, just in case).
+ g = 0;
+ }
+ if (g)
+ widget = (QETWidget*)g;
+ else if (QApplication::activePopupWidget())
+ widget = (QETWidget*)QApplication::activePopupWidget()->focusWidget()
+ ? (QETWidget*)QApplication::activePopupWidget()->focusWidget()
+ : (QETWidget*)QApplication::activePopupWidget();
+ else if (qApp->focusWidget())
+ widget = (QETWidget*)QApplication::focusWidget();
+ else if (!widget || widget->internalWinId() == GetFocus()) // We faked the message to go to exactly that widget.
+ widget = (QETWidget*)widget->window();
+ if (widget->isEnabled())
+ result = sm_blockUserInput
+ ? true
+ : qt_keymapper_private()->translateKeyEvent(widget, msg, g != 0);
+ break;
+ }
+ case WM_SYSCHAR:
+ result = true; // consume event
+ break;
+
+ case WM_MOUSEWHEEL:
+ result = widget->translateWheelEvent(msg);
+ break;
+
+ case WM_APPCOMMAND:
+ {
+ uint cmd = GET_APPCOMMAND_LPARAM(lParam);
+ uint uDevice = GET_DEVICE_LPARAM(lParam);
+ uint dwKeys = GET_KEYSTATE_LPARAM(lParam);
+
+ int state = translateButtonState(dwKeys, QEvent::KeyPress, 0);
+
+ switch (uDevice) {
+ case FAPPCOMMAND_KEY:
+ {
+ int key = 0;
+
+ switch(cmd) {
+ case APPCOMMAND_BASS_BOOST:
+ key = Qt::Key_BassBoost;
+ break;
+ case APPCOMMAND_BASS_DOWN:
+ key = Qt::Key_BassDown;
+ break;
+ case APPCOMMAND_BASS_UP:
+ key = Qt::Key_BassUp;
+ break;
+ case APPCOMMAND_BROWSER_BACKWARD:
+ key = Qt::Key_Back;
+ break;
+ case APPCOMMAND_BROWSER_FAVORITES:
+ key = Qt::Key_Favorites;
+ break;
+ case APPCOMMAND_BROWSER_FORWARD:
+ key = Qt::Key_Forward;
+ break;
+ case APPCOMMAND_BROWSER_HOME:
+ key = Qt::Key_HomePage;
+ break;
+ case APPCOMMAND_BROWSER_REFRESH:
+ key = Qt::Key_Refresh;
+ break;
+ case APPCOMMAND_BROWSER_SEARCH:
+ key = Qt::Key_Search;
+ break;
+ case APPCOMMAND_BROWSER_STOP:
+ key = Qt::Key_Stop;
+ break;
+ case APPCOMMAND_LAUNCH_APP1:
+ key = Qt::Key_Launch0;
+ break;
+ case APPCOMMAND_LAUNCH_APP2:
+ key = Qt::Key_Launch1;
+ break;
+ case APPCOMMAND_LAUNCH_MAIL:
+ key = Qt::Key_LaunchMail;
+ break;
+ case APPCOMMAND_LAUNCH_MEDIA_SELECT:
+ key = Qt::Key_LaunchMedia;
+ break;
+ case APPCOMMAND_MEDIA_NEXTTRACK:
+ key = Qt::Key_MediaNext;
+ break;
+ case APPCOMMAND_MEDIA_PLAY_PAUSE:
+ key = Qt::Key_MediaPlay;
+ break;
+ case APPCOMMAND_MEDIA_PREVIOUSTRACK:
+ key = Qt::Key_MediaPrevious;
+ break;
+ case APPCOMMAND_MEDIA_STOP:
+ key = Qt::Key_MediaStop;
+ break;
+ case APPCOMMAND_TREBLE_DOWN:
+ key = Qt::Key_TrebleDown;
+ break;
+ case APPCOMMAND_TREBLE_UP:
+ key = Qt::Key_TrebleUp;
+ break;
+ case APPCOMMAND_VOLUME_DOWN:
+ key = Qt::Key_VolumeDown;
+ break;
+ case APPCOMMAND_VOLUME_MUTE:
+ key = Qt::Key_VolumeMute;
+ break;
+ case APPCOMMAND_VOLUME_UP:
+ key = Qt::Key_VolumeUp;
+ break;
+ // Commands new in Windows XP
+ case APPCOMMAND_HELP:
+ key = Qt::Key_Help;
+ break;
+ case APPCOMMAND_FIND:
+ key = Qt::Key_Search;
+ break;
+ case APPCOMMAND_PRINT:
+ key = Qt::Key_Print;
+ break;
+ case APPCOMMAND_MEDIA_PLAY:
+ key = Qt::Key_MediaPlay;
+ break;
+ default:
+ break;
+ }
+ if (key) {
+ bool res = false;
+ QWidget *g = QWidget::keyboardGrabber();
+ if (g)
+ widget = (QETWidget*)g;
+ else if (qApp->focusWidget())
+ widget = (QETWidget*)qApp->focusWidget();
+ else
+ widget = (QETWidget*)widget->window();
+ if (widget->isEnabled()) {
+ res = QKeyMapper::sendKeyEvent(widget, g != 0, QEvent::KeyPress, key,
+ Qt::KeyboardModifier(state),
+ QString(), false, 0, 0, 0, 0);
+ }
+ if (res)
+ return true;
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ result = false;
+ }
+ break;
+
+#ifndef Q_OS_WINCE
+ case WM_NCHITTEST:
+ if (widget->isWindow()) {
+ QPoint pos = widget->mapFromGlobal(QPoint(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)));
+ // don't show resize-cursors for fixed-size widgets
+ QRect fs = widget->frameStrut();
+ if (!widget->isMinimized()) {
+ if (widget->minimumWidth() == widget->maximumWidth() && (pos.x() < 0 || pos.x() >= widget->width()))
+ break;
+ if (widget->minimumHeight() == widget->maximumHeight() && (pos.y() < -(fs.top() - fs.left()) || pos.y() >= widget->height()))
+ break;
+ }
+ }
+
+ result = false;
+ break;
+#endif
+
+ case WM_SYSCOMMAND: {
+#ifndef Q_OS_WINCE
+ bool window_state_change = false;
+ Qt::WindowStates oldstate = Qt::WindowStates(widget->dataPtr()->window_state);
+ // MSDN:In WM_SYSCOMMAND messages, the four low-order bits of the wParam parameter are
+ // used internally by the system. To obtain the correct result when testing the value of
+ // wParam, an application must combine the value 0xFFF0 with the wParam value by using
+ // the bitwise AND operator.
+ switch(0xfff0 & wParam) {
+ case SC_CONTEXTHELP:
+#ifndef QT_NO_WHATSTHIS
+ QWhatsThis::enterWhatsThisMode();
+#endif
+ QT_WA({
+ DefWindowProc(hwnd, WM_NCPAINT, 1, 0);
+ } , {
+ DefWindowProcA(hwnd, WM_NCPAINT, 1, 0);
+ });
+ break;
+#if defined(QT_NON_COMMERCIAL)
+ QT_NC_SYSCOMMAND
+#endif
+ case SC_MINIMIZE:
+ window_state_change = true;
+ widget->dataPtr()->window_state |= Qt::WindowMinimized;
+ if (widget->isVisible()) {
+ QHideEvent e;
+ qt_sendSpontaneousEvent(widget, &e);
+ widget->hideChildren(true);
+#ifndef Q_OS_WINCE
+ const QString title = widget->windowIconText();
+ if (!title.isEmpty())
+ widget->setWindowTitle_helper(title);
+#endif
+ }
+ result = false;
+ break;
+ case SC_MAXIMIZE:
+ if(widget->isWindow())
+ widget->topData()->normalGeometry = widget->geometry();
+ case SC_RESTORE:
+ window_state_change = true;
+ if ((0xfff0 & wParam) == SC_MAXIMIZE)
+ widget->dataPtr()->window_state |= Qt::WindowMaximized;
+ else if (!widget->isMinimized())
+ widget->dataPtr()->window_state &= ~Qt::WindowMaximized;
+
+ if (widget->isMinimized()) {
+ widget->dataPtr()->window_state &= ~Qt::WindowMinimized;
+ widget->showChildren(true);
+ QShowEvent e;
+ qt_sendSpontaneousEvent(widget, &e);
+#ifndef Q_OS_WINCE
+ const QString title = widget->windowTitle();
+ if (!title.isEmpty())
+ widget->setWindowTitle_helper(title);
+#endif
+ }
+ result = false;
+ break;
+ default:
+ result = false;
+ break;
+ }
+
+ if (window_state_change) {
+ QWindowStateChangeEvent e(oldstate);
+ qt_sendSpontaneousEvent(widget, &e);
+ }
+#endif
+
+ break;
+ }
+
+ case WM_SETTINGCHANGE:
+ if ( qApp->type() == QApplication::Tty )
+ break;
+
+ if (!msg.wParam) {
+ QString area = QT_WA_INLINE(QString::fromUtf16((unsigned short *)msg.lParam),
+ QString::fromLocal8Bit((char*)msg.lParam));
+ if (area == QLatin1String("intl")) {
+ QLocalePrivate::updateSystemPrivate();
+ if (!widget->testAttribute(Qt::WA_SetLocale))
+ widget->dptr()->setLocale_helper(QLocale(), true);
+ }
+ }
+ else if (msg.wParam == SPI_SETICONTITLELOGFONT) {
+ if (qApp->desktopSettingsAware()) {
+ widget = (QETWidget*)QWidget::find(hwnd);
+ if (widget && !widget->parentWidget()) {
+ qt_set_windows_font_resources();
+ }
+ }
+ }
+ break;
+
+ case WM_PAINT: // paint event
+ case WM_ERASEBKGND: // erase window background
+ result = widget->translatePaintEvent(msg);
+ break;
+
+#ifndef Q_OS_WINCE
+ case WM_ENTERSIZEMOVE:
+ autoCaptureWnd = hwnd;
+ QApplicationPrivate::inSizeMove = true;
+ break;
+ case WM_EXITSIZEMOVE:
+ autoCaptureWnd = 0;
+ QApplicationPrivate::inSizeMove = false;
+ break;
+#endif
+ case WM_MOVE: // move window
+ case WM_SIZE: // resize window
+ result = widget->translateConfigEvent(msg);
+ break;
+
+ case WM_ACTIVATEAPP:
+ if (wParam == FALSE) {
+ QApplication::setActiveWindow(0);
+ // Another application was activated while our popups are open,
+ // then close all popups. In case some popup refuses to close,
+ // we give up after 1024 attempts (to avoid an infinite loop).
+ int maxiter = 1024;
+ QWidget *popup;
+ while ((popup=QApplication::activePopupWidget()) && maxiter--)
+ popup->close();
+ }
+ break;
+
+ case WM_ACTIVATE:
+ if ( qApp->type() == QApplication::Tty )
+ break;
+
+ if (ptrWTOverlap && ptrWTEnable) {
+ // cooperate with other tablet applications, but when
+ // we get focus, I want to use the tablet...
+ if (qt_tablet_context && GET_WM_ACTIVATE_STATE(wParam, lParam)) {
+ if (ptrWTEnable(qt_tablet_context, true))
+ ptrWTOverlap(qt_tablet_context, true);
+ }
+ }
+ if (QApplication::activePopupWidget() && LOWORD(wParam) == WA_INACTIVE &&
+ QWidget::find((HWND)lParam) == 0) {
+ // Another application was activated while our popups are open,
+ // then close all popups. In case some popup refuses to close,
+ // we give up after 1024 attempts (to avoid an infinite loop).
+ int maxiter = 1024;
+ QWidget *popup;
+ while ((popup=QApplication::activePopupWidget()) && maxiter--)
+ popup->close();
+ }
+
+ if (LOWORD(wParam) != WA_INACTIVE) {
+ // WM_ACTIVATEAPP handles the "true" false case, as this is only when the application
+ // loses focus. Doing it here would result in the widget getting focus to not know
+ // where it got it from; it would simply get a 0 value as the old focus widget.
+ if (!(widget->windowState() & Qt::WindowMinimized)) {
+ // Ignore the activate message send by WindowsXP to a minimized window
+#ifdef Q_OS_WINCE_WM
+ if (widget->windowState() & Qt::WindowFullScreen)
+ qt_wince_hide_taskbar(widget->winId());
+#endif
+ qApp->winFocus(widget, true);
+ // reset any window alert flashes
+ alert_widget(widget, -1);
+ }
+ }
+
+ // Windows tries to activate a modally blocked window.
+ // This happens when restoring an application after "Show Desktop"
+ if (app_do_modal && LOWORD(wParam) == WA_ACTIVE) {
+ QWidget *top = 0;
+ if (!QApplicationPrivate::tryModalHelper(widget, &top) && top && widget != top) {
+ if (top->isVisible()) {
+ top->activateWindow();
+ } else {
+ // This is the case when native file dialogs are shown
+ QWidget *p = (top->parentWidget() ? top->parentWidget()->window() : 0);
+ if (p && p->isVisible())
+ p->activateWindow();
+ }
+ }
+ }
+ break;
+
+#ifndef Q_OS_WINCE
+ case WM_MOUSEACTIVATE:
+ if (widget->window()->windowType() == Qt::Tool) {
+ QWidget *w = widget;
+ if (!w->window()->focusWidget()) {
+ while (w && (w->focusPolicy() & Qt::ClickFocus) == 0) {
+ if (w->isWindow()) {
+ QWidget *fw = w;
+ while ((fw = fw->nextInFocusChain()) != w && fw->focusPolicy() == Qt::NoFocus)
+ ;
+ if (fw != w)
+ break;
+ QWidget *pw = w->parentWidget();
+ while (pw) {
+ pw = pw->window();
+ if (pw && pw->isVisible() && pw->focusWidget()) {
+ Q_ASSERT(pw->testAttribute(Qt::WA_WState_Created));
+ SetWindowPos(pw->internalWinId(), HWND_TOP, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE);
+ break;
+ }
+ pw = pw->parentWidget();
+ }
+ RETURN(MA_NOACTIVATE);
+ }
+ w = w->parentWidget();
+ }
+ }
+ }
+ RETURN(MA_ACTIVATE);
+ break;
+#endif
+ case WM_SHOWWINDOW:
+ if (lParam == SW_PARENTOPENING) {
+ if (widget->testAttribute(Qt::WA_WState_Hidden))
+ RETURN(0);
+ }
+ if (widget->isWindow() && widget->testAttribute(Qt::WA_WState_Visible)
+ && !widget->testWindowState(Qt::WindowMinimized)) {
+ if (lParam == SW_PARENTOPENING) {
+ QShowEvent e;
+ qt_sendSpontaneousEvent(widget, &e);
+ widget->showChildren(true);
+ } else if (lParam == SW_PARENTCLOSING) {
+ QHideEvent e;
+ qt_sendSpontaneousEvent(widget, &e);
+ widget->hideChildren(true);
+ }
+ }
+ if (!wParam && autoCaptureWnd == widget->internalWinId())
+ releaseAutoCapture();
+ result = false;
+ break;
+
+ case WM_PALETTECHANGED: // our window changed palette
+ if (QColormap::hPal() && (WId)wParam == widget->internalWinId())
+ RETURN(0); // otherwise: FALL THROUGH!
+ // FALL THROUGH
+ case WM_QUERYNEWPALETTE: // realize own palette
+ if (QColormap::hPal()) {
+ Q_ASSERT(widget->testAttribute(Qt::WA_WState_Created));
+ HDC hdc = GetDC(widget->internalWinId());
+ HPALETTE hpalOld = SelectPalette(hdc, QColormap::hPal(), FALSE);
+ uint n = RealizePalette(hdc);
+ if (n)
+ InvalidateRect(widget->internalWinId(), 0, TRUE);
+ SelectPalette(hdc, hpalOld, TRUE);
+ RealizePalette(hdc);
+ ReleaseDC(widget->internalWinId(), hdc);
+ RETURN(n);
+ }
+ break;
+ case WM_CLOSE: // close window
+ widget->translateCloseEvent(msg);
+ RETURN(0); // always handled
+
+ case WM_DESTROY: // destroy window
+ if (hwnd == curWin) {
+ QWidget *enter = QWidget::mouseGrabber();
+ if (enter == widget)
+ enter = 0;
+ QApplicationPrivate::dispatchEnterLeave(enter, widget);
+ curWin = enter ? enter->effectiveWinId() : 0;
+ qt_last_mouse_receiver = enter;
+ }
+ if (widget == popupButtonFocus)
+ popupButtonFocus = 0;
+ result = false;
+ break;
+
+#ifndef Q_OS_WINCE
+ case WM_WINDOWPOSCHANGING:
+ {
+ result = false;
+ if (widget->isWindow()) {
+ WINDOWPOS *winPos = (WINDOWPOS *)lParam;
+ if (widget->layout() && widget->layout()->hasHeightForWidth()
+ && !(winPos->flags & (SWP_NOCOPYBITS | SWP_NOSIZE))) {
+ QRect fs = widget->frameStrut();
+ QRect rect = widget->geometry();
+ QRect newRect = QRect(winPos->x + fs.left(),
+ winPos->y + fs.top(),
+ winPos->cx - fs.left() - fs.right(),
+ winPos->cy - fs.top() - fs.bottom());
+
+ QSize newSize = QLayout::closestAcceptableSize(widget, newRect.size());
+
+ int dh = newSize.height() - newRect.height();
+ int dw = newSize.width() - newRect.width();
+ if (!dw && ! dh)
+ break; // Size OK
+
+ if (rect.y() != newRect.y()) {
+ newRect.setTop(newRect.top() - dh);
+ } else {
+ newRect.setBottom(newRect.bottom() + dh);
+ }
+
+ if (rect.x() != newRect.x()) {
+ newRect.setLeft(newRect.left() - dw);
+ } else {
+ newRect.setRight(newRect.right() + dw);
+ }
+
+ winPos->x = newRect.x() - fs.left();
+ winPos->y = newRect.y() - fs.top();
+ winPos->cx = newRect.width() + fs.left() + fs.right();
+ winPos->cy = newRect.height() + fs.top() + fs.bottom();
+
+ RETURN(0);
+ }
+ if (widget->windowFlags() & Qt::WindowStaysOnBottomHint) {
+ winPos->hwndInsertAfter = HWND_BOTTOM;
+ }
+ }
+ }
+ break;
+
+ case WM_GETMINMAXINFO:
+ if (widget->xtra()) {
+ MINMAXINFO *mmi = (MINMAXINFO *)lParam;
+ QWExtra *x = widget->xtra();
+ QRect fs = widget->frameStrut();
+ if ( x->minw > 0 )
+ mmi->ptMinTrackSize.x = x->minw + fs.right() + fs.left();
+ if ( x->minh > 0 )
+ mmi->ptMinTrackSize.y = x->minh + fs.top() + fs.bottom();
+ qint32 maxw = (x->maxw >= x->minw) ? x->maxw : x->minw;
+ qint32 maxh = (x->maxh >= x->minh) ? x->maxh : x->minh;
+ if ( maxw < QWIDGETSIZE_MAX ) {
+ mmi->ptMaxTrackSize.x = maxw + fs.right() + fs.left();
+ // windows with title bar have an implicit size limit of 112 pixels
+ if (widget->windowFlags() & Qt::WindowTitleHint)
+ mmi->ptMaxTrackSize.x = qMax<long>(mmi->ptMaxTrackSize.x, 112);
+ }
+ if ( maxh < QWIDGETSIZE_MAX )
+ mmi->ptMaxTrackSize.y = maxh + fs.top() + fs.bottom();
+ RETURN(0);
+ }
+ break;
+
+ case WM_CONTEXTMENU:
+ {
+ // it's not VK_APPS or Shift+F10, but a click in the NC area
+ if (lParam != (int)0xffffffff) {
+ result = false;
+ break;
+ }
+
+ QWidget *fw = QWidget::keyboardGrabber();
+ if (!fw) {
+ if (qApp->activePopupWidget())
+ fw = (qApp->activePopupWidget()->focusWidget()
+ ? qApp->activePopupWidget()->focusWidget()
+ : qApp->activePopupWidget());
+ else if (qApp->focusWidget())
+ fw = qApp->focusWidget();
+ else if (widget)
+ fw = widget->window();
+ }
+ if (fw && fw->isEnabled()) {
+ QPoint pos = fw->inputMethodQuery(Qt::ImMicroFocus).toRect().center();
+ QContextMenuEvent e(QContextMenuEvent::Keyboard, pos, fw->mapToGlobal(pos),
+ qt_win_getKeyboardModifiers());
+ result = qt_sendSpontaneousEvent(fw, &e);
+ }
+ }
+ break;
+#endif
+
+ case WM_IME_STARTCOMPOSITION:
+ case WM_IME_ENDCOMPOSITION:
+ case WM_IME_COMPOSITION: {
+ QWidget *fw = qApp->focusWidget();
+ QWinInputContext *im = fw ? qobject_cast<QWinInputContext *>(fw->inputContext()) : 0;
+ if (fw && im) {
+ if(message == WM_IME_STARTCOMPOSITION)
+ result = im->startComposition();
+ else if (message == WM_IME_ENDCOMPOSITION)
+ result = im->endComposition();
+ else if (message == WM_IME_COMPOSITION)
+ result = im->composition(lParam);
+ }
+ break;
+ }
+
+#ifndef Q_OS_WINCE
+ case WM_CHANGECBCHAIN:
+ case WM_DRAWCLIPBOARD:
+#endif
+ case WM_RENDERFORMAT:
+ case WM_RENDERALLFORMATS:
+#ifndef QT_NO_CLIPBOARD
+ case WM_DESTROYCLIPBOARD:
+ if (qt_clipboard) {
+ QClipboardEvent e(reinterpret_cast<QEventPrivate *>(&msg));
+ qt_sendSpontaneousEvent(qt_clipboard, &e);
+ RETURN(0);
+ }
+ result = false;
+ break;
+#endif //QT_NO_CLIPBOARD
+#ifndef QT_NO_ACCESSIBILITY
+ case WM_GETOBJECT:
+ {
+ // Ignoring all requests while starting up
+ if (qApp->startingUp() || qApp->closingDown() || (DWORD)lParam != OBJID_CLIENT) {
+ result = false;
+ break;
+ }
+
+ typedef LRESULT (WINAPI *PtrLresultFromObject)(REFIID, WPARAM, LPUNKNOWN);
+ static PtrLresultFromObject ptrLresultFromObject = 0;
+ static bool oleaccChecked = false;
+
+ if (!oleaccChecked) {
+ oleaccChecked = true;
+#if !defined(Q_OS_WINCE)
+ ptrLresultFromObject = (PtrLresultFromObject)QLibrary::resolve(QLatin1String("oleacc.dll"), "LresultFromObject");
+#endif
+ }
+ if (ptrLresultFromObject) {
+ QAccessibleInterface *acc = QAccessible::queryAccessibleInterface(widget);
+ if (!acc) {
+ result = false;
+ break;
+ }
+
+ // and get an instance of the IAccessibile implementation
+ IAccessible *iface = qt_createWindowsAccessible(acc);
+ res = ptrLresultFromObject(IID_IAccessible, wParam, iface); // ref == 2
+ iface->Release(); // the client will release the object again, and then it will destroy itself
+
+ if (res > 0)
+ RETURN(res);
+ }
+ }
+ result = false;
+ break;
+ case WM_GETTEXT:
+ if (!widget->isWindow()) {
+ int ret = 0;
+ QAccessibleInterface *acc = QAccessible::queryAccessibleInterface(widget);
+ if (acc) {
+ QString text = acc->text(QAccessible::Name, 0);
+ if (text.isEmpty())
+ text = widget->objectName();
+ ret = qMin<int>(wParam - 1, text.size());
+ text.resize(ret);
+ QT_WA({
+ memcpy((void *)lParam, text.utf16(), (text.size() + 1) * 2);
+ }, {
+ memcpy((void *)lParam, text.toLocal8Bit().data(), text.size() + 1);
+ });
+ delete acc;
+ }
+ if (!ret) {
+ result = false;
+ break;
+ }
+ RETURN(ret);
+ }
+ result = false;
+ break;
+#endif
+ case WT_PACKET:
+ if (ptrWTPacketsGet) {
+ if ((nPackets = ptrWTPacketsGet(qt_tablet_context, QT_TABLET_NPACKETQSIZE, &localPacketBuf))) {
+ result = widget->translateTabletEvent(msg, localPacketBuf, nPackets);
+ }
+ }
+ break;
+ case WT_PROXIMITY:
+ if (ptrWTPacketsGet) {
+ bool enteredProximity = LOWORD(lParam) != 0;
+ PACKET proximityBuffer[QT_TABLET_NPACKETQSIZE];
+ int totalPacks = ptrWTPacketsGet(qt_tablet_context, QT_TABLET_NPACKETQSIZE, proximityBuffer);
+ if (totalPacks > 0 && enteredProximity) {
+ uint currentCursor = proximityBuffer[0].pkCursor;
+ if (!tCursorInfo()->contains(currentCursor))
+ tabletInit(currentCursor, qt_tablet_context);
+ currentTabletPointer = tCursorInfo()->value(currentCursor);
+ }
+ qt_tabletChokeMouse = false;
+#ifndef QT_NO_TABLETEVENT
+ QTabletEvent tabletProximity(enteredProximity ? QEvent::TabletEnterProximity
+ : QEvent::TabletLeaveProximity,
+ QPoint(), QPoint(), QPointF(), currentTabletPointer.currentDevice, currentTabletPointer.currentPointerType, 0, 0,
+ 0, 0, 0, 0, 0, currentTabletPointer.llId);
+ QApplication::sendEvent(qApp, &tabletProximity);
+#endif // QT_NO_TABLETEVENT
+ }
+ break;
+#ifdef Q_OS_WINCE_WM
+ case WM_SETFOCUS: {
+ HIMC hC;
+ hC = ImmGetContext(hwnd);
+ ImmSetOpenStatus(hC, TRUE);
+ ImmEscape(NULL, hC, IME_ESC_SET_MODE, (LPVOID)IM_SPELL);
+ result = false;
+ }
+ break;
+#endif
+ case WM_KILLFOCUS:
+ if (!QWidget::find((HWND)wParam)) { // we don't get focus, so unset it now
+ if (!widget->hasFocus()) // work around Windows bug after minimizing/restoring
+ widget = (QETWidget*)qApp->focusWidget();
+ HWND focus = ::GetFocus();
+ //if there is a current widget and the new widget belongs to the same toplevel window
+ //then we clear the focus on the widget
+ //in case the new widget belongs to a different widget hierarchy, clearing the focus
+ //will be handled because the active window will change
+ if (widget && ::IsChild(widget->window()->internalWinId(), focus)) {
+ widget->clearFocus();
+ result = true;
+ } else {
+ result = false;
+ }
+ } else {
+ result = false;
+ }
+ break;
+ case WM_THEMECHANGED:
+ if ((widget->windowType() == Qt::Desktop) || !qApp || qApp->closingDown()
+ || qApp->type() == QApplication::Tty)
+ break;
+
+ if (widget->testAttribute(Qt::WA_WState_Polished))
+ qApp->style()->unpolish(widget);
+
+ if (widget->testAttribute(Qt::WA_WState_Polished))
+ qApp->style()->polish(widget);
+ widget->repolishStyle(*qApp->style());
+ if (widget->isVisible())
+ widget->update();
+ break;
+
+#ifndef Q_OS_WINCE
+ case WM_INPUTLANGCHANGE: {
+ char info[7];
+ if (!GetLocaleInfoA(MAKELCID(lParam, SORT_DEFAULT), LOCALE_IDEFAULTANSICODEPAGE, info, 6)) {
+ inputcharset = CP_ACP;
+ } else {
+ inputcharset = QString::fromLatin1(info).toInt();
+ }
+ QKeyMapper::changeKeyboard();
+ break;
+ }
+#else
+ case WM_COMMAND: {
+ bool OkCommand = (LOWORD(wParam) == 0x1);
+ bool CancelCommand = (LOWORD(wParam) == 0x2);
+ if (OkCommand)
+ QApplication::postEvent(widget, new QEvent(QEvent::OkRequest));
+ if (CancelCommand)
+ QApplication::postEvent(widget, new QEvent(QEvent::Close));
+ else
+#ifndef QT_NO_MENUBAR
+ QMenuBar::wceCommands(LOWORD(wParam), (HWND) lParam);
+#endif
+ result = true;
+ }
+ break;
+ case WM_HELP:
+ QApplication::postEvent(widget, new QEvent(QEvent::HelpRequest));
+ result = true;
+ break;
+#endif
+
+ case WM_MOUSELEAVE:
+ // We receive a mouse leave for curWin, meaning
+ // the mouse was moved outside our widgets
+ if (widget->internalWinId() == curWin) {
+ bool dispatch = !widget->underMouse();
+ // hasMouse is updated when dispatching enter/leave,
+ // so test if it is actually up-to-date
+ if (!dispatch) {
+ QRect geom = widget->geometry();
+ if (widget->parentWidget() && !widget->isWindow()) {
+ QPoint gp = widget->parentWidget()->mapToGlobal(widget->pos());
+ geom.setX(gp.x());
+ geom.setY(gp.y());
+ }
+ QPoint cpos = QCursor::pos();
+ dispatch = !geom.contains(cpos);
+ if ( !dispatch && !QWidget::mouseGrabber()) {
+ QWidget *hittest = QApplication::widgetAt(cpos);
+ dispatch = !hittest || hittest->internalWinId() != curWin;
+ }
+ if (!dispatch) {
+ HRGN hrgn = qt_tryCreateRegion(QRegion::Rectangle, 0,0,0,0);
+ if (GetWindowRgn(curWin, hrgn) != ERROR) {
+ QPoint lcpos = widget->mapFromGlobal(cpos);
+ dispatch = !PtInRegion(hrgn, lcpos.x(), lcpos.y());
+ }
+ DeleteObject(hrgn);
+ }
+ }
+ if (dispatch) {
+ if (qt_last_mouse_receiver && !qt_last_mouse_receiver->internalWinId())
+ QApplicationPrivate::dispatchEnterLeave(0, qt_last_mouse_receiver);
+ else
+ QApplicationPrivate::dispatchEnterLeave(0, QWidget::find((WId)curWin));
+ curWin = 0;
+ qt_last_mouse_receiver = 0;
+ }
+ }
+ break;
+
+ case WM_CANCELMODE:
+ {
+ // this goes through QMenuBar's event filter
+ QEvent e(QEvent::ActivationChange);
+ QApplication::sendEvent(qApp, &e);
+ }
+ break;
+
+ case WM_IME_NOTIFY:
+ // special handling for ime, only for widgets in a popup
+ if (wParam == IMN_OPENCANDIDATE) {
+ imeParentWnd = hwnd;
+ if (QApplication::activePopupWidget()) {
+ // temporarily disable the mouse grab to allow mouse input in
+ // the ime candidate window. The actual handle is untouched
+ if (autoCaptureWnd)
+ ReleaseCapture();
+ }
+ } else if (wParam == IMN_CLOSECANDIDATE) {
+ imeParentWnd = 0;
+ if (QApplication::activePopupWidget()) {
+ // undo the action above, when candidate window is closed
+ if (autoCaptureWnd)
+ SetCapture(autoCaptureWnd);
+ }
+ }
+ result = false;
+ break;
+ default:
+ result = false; // event was not processed
+ break;
+ }
+ }
+
+ if (evt_type != QEvent::None) { // simple event
+ QEvent e(evt_type);
+ result = qt_sendSpontaneousEvent(widget, &e);
+ }
+
+ if (result)
+ RETURN(false);
+
+do_default:
+ RETURN(QWinInputContext::DefWindowProc(hwnd,message,wParam,lParam))
+}
+
+
+/*****************************************************************************
+ Modal widgets; We have implemented our own modal widget mechanism
+ to get total control.
+ A modal widget without a parent becomes application-modal.
+ A modal widget with a parent becomes modal to its parent and grandparents..
+
+ QApplicationPrivate::enterModal()
+ Enters modal state
+ Arguments:
+ QWidget *widget A modal widget
+
+ QApplicationPrivate::leaveModal()
+ Leaves modal state for a widget
+ Arguments:
+ QWidget *widget A modal widget
+ *****************************************************************************/
+
+bool QApplicationPrivate::modalState()
+{
+ return app_do_modal;
+}
+
+void QApplicationPrivate::enterModal_sys(QWidget *widget)
+{
+ if (!qt_modal_stack)
+ qt_modal_stack = new QWidgetList;
+
+ releaseAutoCapture();
+ ClipCursor(0);
+ QWidget *leave = qt_last_mouse_receiver;
+ if (!leave)
+ leave = QWidget::find((WId)curWin);
+ QApplicationPrivate::dispatchEnterLeave(0, leave);
+ qt_modal_stack->insert(0, widget);
+ app_do_modal = true;
+ curWin = 0;
+ qt_last_mouse_receiver = 0;
+ qt_win_ignoreNextMouseReleaseEvent = false;
+}
+
+void QApplicationPrivate::leaveModal_sys(QWidget *widget)
+{
+ if (qt_modal_stack && qt_modal_stack->removeAll(widget)) {
+ if (qt_modal_stack->isEmpty()) {
+ delete qt_modal_stack;
+ qt_modal_stack = 0;
+ QPoint p(QCursor::pos());
+ app_do_modal = false; // necessary, we may get recursively into qt_try_modal below
+ QWidget* w = QApplication::widgetAt(p.x(), p.y());
+ QWidget *leave = qt_last_mouse_receiver;
+ if (!leave)
+ leave = QWidget::find((WId)curWin);
+ if (QWidget *grabber = QWidget::mouseGrabber()) {
+ w = grabber;
+ if (leave == w)
+ leave = 0;
+ }
+ QApplicationPrivate::dispatchEnterLeave(w, leave); // send synthetic enter event
+ curWin = w ? w->effectiveWinId() : 0;
+ qt_last_mouse_receiver = w;
+ }
+ qt_win_ignoreNextMouseReleaseEvent = true;
+ }
+ app_do_modal = qt_modal_stack != 0;
+}
+
+bool qt_try_modal(QWidget *widget, MSG *msg, int& ret)
+{
+#if defined(Q_OS_WINCE)
+ Q_UNUSED(ret);
+#endif
+ QWidget * top = 0;
+
+ if (QApplicationPrivate::tryModalHelper(widget, &top))
+ return true;
+
+ int type = msg->message;
+
+ bool block_event = false;
+#ifndef Q_OS_WINCE
+ if (type != WM_NCHITTEST)
+#endif
+ if ((type >= WM_MOUSEFIRST && type <= WM_MOUSELAST) ||
+ type == WM_MOUSEWHEEL || type == (int)WM95_MOUSEWHEEL ||
+ type == WM_MOUSELEAVE ||
+ (type >= WM_KEYFIRST && type <= WM_KEYLAST)
+#ifndef Q_OS_WINCE
+ || type == WM_NCMOUSEMOVE
+#endif
+ ) {
+ if (type == WM_MOUSEMOVE
+#ifndef Q_OS_WINCE
+ || type == WM_NCMOUSEMOVE
+#endif
+ ) {
+#ifndef QT_NO_CURSOR
+ QCursor *c = qt_grab_cursor();
+ if (!c)
+ c = QApplication::overrideCursor();
+ if (c) // application cursor defined
+ SetCursor(c->handle());
+ else
+ SetCursor(QCursor(Qt::ArrowCursor).handle());
+#endif // QT_NO_CURSOR
+ }
+ block_event = true;
+ } else if (type == WM_CLOSE) {
+ block_event = true;
+ }
+#ifndef Q_OS_WINCE
+ else if (type == WM_MOUSEACTIVATE || type == WM_NCLBUTTONDOWN){
+ if (!top->isActiveWindow()) {
+ top->activateWindow();
+ } else {
+ QApplication::beep();
+ }
+ block_event = true;
+ ret = MA_NOACTIVATEANDEAT;
+ } else if (type == WM_SYSCOMMAND) {
+ if (!(msg->wParam == SC_RESTORE && widget->isMinimized()))
+ block_event = true;
+ }
+#endif
+
+ return !block_event;
+}
+
+
+/*****************************************************************************
+ Popup widget mechanism
+
+ openPopup()
+ Adds a widget to the list of popup widgets
+ Arguments:
+ QWidget *widget The popup widget to be added
+
+ closePopup()
+ Removes a widget from the list of popup widgets
+ Arguments:
+ QWidget *widget The popup widget to be removed
+ *****************************************************************************/
+
+void QApplicationPrivate::openPopup(QWidget *popup)
+{
+ if (!QApplicationPrivate::popupWidgets)
+ QApplicationPrivate::popupWidgets = new QWidgetList;
+ QApplicationPrivate::popupWidgets->append(popup);
+ if (!popup->isEnabled())
+ return;
+
+ // close any opened 'ime candidate window'
+ if (imeParentWnd)
+ ::SendMessage(imeParentWnd, WM_IME_ENDCOMPOSITION, 0, 0);
+
+ if (QApplicationPrivate::popupWidgets->count() == 1 && !qt_nograb()) {
+ Q_ASSERT(popup->testAttribute(Qt::WA_WState_Created));
+ setAutoCapture(popup->internalWinId()); // grab mouse/keyboard
+ }
+ // Popups are not focus-handled by the window system (the first
+ // popup grabbed the keyboard), so we have to do that manually: A
+ // new popup gets the focus
+ if (popup->focusWidget()) {
+ popup->focusWidget()->setFocus(Qt::PopupFocusReason);
+ } else if (QApplicationPrivate::popupWidgets->count() == 1) { // this was the first popup
+ if (QWidget *fw = q_func()->focusWidget()) {
+ QFocusEvent e(QEvent::FocusOut, Qt::PopupFocusReason);
+ q_func()->sendEvent(fw, &e);
+ }
+ }
+}
+
+void QApplicationPrivate::closePopup(QWidget *popup)
+{
+ if (!QApplicationPrivate::popupWidgets)
+ return;
+ QApplicationPrivate::popupWidgets->removeAll(popup);
+ POINT curPos;
+ GetCursorPos(&curPos);
+
+ // close any opened 'ime candidate window'
+ if (imeParentWnd)
+ ::SendMessage(imeParentWnd, WM_IME_ENDCOMPOSITION, 0, 0);
+
+ if (QApplicationPrivate::popupWidgets->isEmpty()) { // this was the last popup
+ delete QApplicationPrivate::popupWidgets;
+ QApplicationPrivate::popupWidgets = 0;
+ replayPopupMouseEvent = (!popup->geometry().contains(QPoint(curPos.x, curPos.y))
+ && !popup->testAttribute(Qt::WA_NoMouseReplay));
+ if (!popup->isEnabled())
+ return;
+ if (!qt_nograb()) // grabbing not disabled
+ releaseAutoCapture();
+ QWidget *fw = QApplicationPrivate::active_window ? QApplicationPrivate::active_window->focusWidget()
+ : q_func()->focusWidget();
+ if (fw) {
+ if (fw != q_func()->focusWidget()) {
+ fw->setFocus(Qt::PopupFocusReason);
+ } else {
+ QFocusEvent e(QEvent::FocusIn, Qt::PopupFocusReason);
+ q_func()->sendEvent(fw, &e);
+ }
+ }
+ } else {
+ // Popups are not focus-handled by the window system (the
+ // first popup grabbed the keyboard), so we have to do that
+ // manually: A popup was closed, so the previous popup gets
+ // the focus.
+ QWidget* aw = QApplicationPrivate::popupWidgets->last();
+ if (QApplicationPrivate::popupWidgets->count() == 1) {
+ Q_ASSERT(aw->testAttribute(Qt::WA_WState_Created));
+ setAutoCapture(aw->internalWinId());
+ }
+ if (QWidget *fw = aw->focusWidget())
+ fw->setFocus(Qt::PopupFocusReason);
+ }
+}
+
+
+
+
+/*****************************************************************************
+ Event translation; translates Windows events to Qt events
+ *****************************************************************************/
+
+//
+// Auto-capturing for mouse press and mouse release
+//
+
+static void setAutoCapture(HWND h)
+{
+ if (autoCaptureWnd)
+ releaseAutoCapture();
+ autoCaptureWnd = h;
+ SetCapture(h);
+}
+
+static void releaseAutoCapture()
+{
+ if (autoCaptureWnd) {
+ ReleaseCapture();
+ autoCaptureWnd = 0;
+ }
+}
+
+
+//
+// Mouse event translation
+//
+// Non-client mouse messages are not translated
+//
+
+static const ushort mouseTbl[] = {
+ WM_MOUSEMOVE, QEvent::MouseMove, 0,
+ WM_LBUTTONDOWN, QEvent::MouseButtonPress, Qt::LeftButton,
+ WM_LBUTTONUP, QEvent::MouseButtonRelease, Qt::LeftButton,
+ WM_LBUTTONDBLCLK, QEvent::MouseButtonDblClick, Qt::LeftButton,
+ WM_RBUTTONDOWN, QEvent::MouseButtonPress, Qt::RightButton,
+ WM_RBUTTONUP, QEvent::MouseButtonRelease, Qt::RightButton,
+ WM_RBUTTONDBLCLK, QEvent::MouseButtonDblClick, Qt::RightButton,
+ WM_MBUTTONDOWN, QEvent::MouseButtonPress, Qt::MidButton,
+ WM_MBUTTONUP, QEvent::MouseButtonRelease, Qt::MidButton,
+ WM_MBUTTONDBLCLK, QEvent::MouseButtonDblClick, Qt::MidButton,
+ // use XButton1 for now, the real X button is decided later
+ WM_XBUTTONDOWN, QEvent::MouseButtonPress, Qt::XButton1,
+ WM_XBUTTONUP, QEvent::MouseButtonRelease, Qt::XButton1,
+ WM_XBUTTONDBLCLK, QEvent::MouseButtonDblClick, Qt::XButton1,
+
+#ifndef Q_OS_WINCE
+ WM_NCMOUSEMOVE, QEvent::NonClientAreaMouseMove, 0,
+ WM_NCLBUTTONDOWN, QEvent::NonClientAreaMouseButtonPress, Qt::LeftButton,
+ WM_NCLBUTTONUP, QEvent::NonClientAreaMouseButtonRelease, Qt::LeftButton,
+ WM_NCLBUTTONDBLCLK, QEvent::NonClientAreaMouseButtonDblClick, Qt::LeftButton,
+ WM_NCRBUTTONDOWN, QEvent::NonClientAreaMouseButtonPress, Qt::RightButton,
+ WM_NCRBUTTONUP, QEvent::NonClientAreaMouseButtonRelease, Qt::RightButton,
+ WM_NCRBUTTONDBLCLK, QEvent::NonClientAreaMouseButtonDblClick, Qt::RightButton,
+ WM_NCMBUTTONDOWN, QEvent::NonClientAreaMouseButtonPress, Qt::MidButton,
+ WM_NCMBUTTONUP, QEvent::NonClientAreaMouseButtonRelease, Qt::MidButton,
+ WM_NCMBUTTONDBLCLK, QEvent::NonClientAreaMouseButtonDblClick, Qt::MidButton,
+#endif
+
+ 0, 0, 0
+};
+
+static int translateButtonState(int s, int type, int button)
+{
+ Q_UNUSED(type);
+ Q_UNUSED(button);
+ int bst = 0;
+ if (s & MK_LBUTTON)
+ bst |= Qt::LeftButton;
+ if (s & MK_MBUTTON)
+ bst |= Qt::MidButton;
+ if (s & MK_RBUTTON)
+ bst |= Qt::RightButton;
+ if (s & MK_SHIFT)
+ bst |= Qt::ShiftModifier;
+ if (s & MK_CONTROL)
+ bst |= Qt::ControlModifier;
+
+ if (s & MK_XBUTTON1)
+ bst |= Qt::XButton1;
+ if (s & MK_XBUTTON2)
+ bst |= Qt::XButton2;
+
+ if (GetKeyState(VK_MENU) < 0)
+ bst |= Qt::AltModifier;
+
+ if ((GetKeyState(VK_LWIN) < 0) ||
+ (GetKeyState(VK_RWIN) < 0))
+ bst |= Qt::MetaModifier;
+
+ return bst;
+}
+
+void qt_win_eatMouseMove()
+{
+ // after closing a windows dialog with a double click (i.e. open a file)
+ // the message queue still contains a dubious WM_MOUSEMOVE message where
+ // the left button is reported to be down (wParam != 0).
+ // remove all those messages (usually 1) and post the last one with a
+ // reset button state
+
+ MSG msg = {0, 0, 0, 0, 0, 0, 0};
+ QT_WA( {
+ while (PeekMessage(&msg, 0, WM_MOUSEMOVE, WM_MOUSEMOVE, PM_REMOVE))
+ ;
+ if (msg.message == WM_MOUSEMOVE)
+ PostMessage(msg.hwnd, msg.message, 0, msg.lParam);
+ }, {
+ MSG msg;
+ msg.message = 0;
+ while (PeekMessageA(&msg, 0, WM_MOUSEMOVE, WM_MOUSEMOVE, PM_REMOVE))
+ ;
+ if (msg.message == WM_MOUSEMOVE)
+ PostMessageA(msg.hwnd, msg.message, 0, msg.lParam);
+ } );
+}
+
+// In DnD, the mouse release event never appears, so the
+// mouse button state machine must be manually reset
+/*! \internal */
+void QApplication::winMouseButtonUp()
+{
+ qt_button_down = 0;
+ releaseAutoCapture();
+}
+
+void QETWidget::repolishStyle(QStyle &)
+{
+ QEvent e(QEvent::StyleChange);
+ QApplication::sendEvent(this, &e);
+}
+
+bool QETWidget::translateMouseEvent(const MSG &msg)
+{
+ if (!isWindow() && testAttribute(Qt::WA_NativeWindow))
+ Q_ASSERT(internalWinId());
+
+ static QPoint pos;
+ static POINT gpos={-1,-1};
+ QEvent::Type type; // event parameters
+ int button;
+ int state;
+ int i;
+
+ if (sm_blockUserInput) //block user interaction during session management
+ return true;
+
+ // Compress mouse move events
+ if (msg.message == WM_MOUSEMOVE) {
+ MSG mouseMsg;
+ while (winPeekMessage(&mouseMsg, msg.hwnd, WM_MOUSEFIRST,
+ WM_MOUSELAST, PM_NOREMOVE)) {
+ if (mouseMsg.message == WM_MOUSEMOVE) {
+#define PEEKMESSAGE_IS_BROKEN 1
+#ifdef PEEKMESSAGE_IS_BROKEN
+ // Since the Windows PeekMessage() function doesn't
+ // correctly return the wParam for WM_MOUSEMOVE events
+ // if there is a key release event in the queue
+ // _before_ the mouse event, we have to also consider
+ // key release events (kls 2003-05-13):
+ MSG keyMsg;
+ bool done = false;
+ while (winPeekMessage(&keyMsg, 0, WM_KEYFIRST, WM_KEYLAST,
+ PM_NOREMOVE)) {
+ if (keyMsg.time < mouseMsg.time) {
+ if ((keyMsg.lParam & 0xC0000000) == 0x40000000) {
+ winPeekMessage(&keyMsg, 0, keyMsg.message,
+ keyMsg.message, PM_REMOVE);
+ } else {
+ done = true;
+ break;
+ }
+ } else {
+ break; // no key event before the WM_MOUSEMOVE event
+ }
+ }
+ if (done)
+ break;
+#else
+ // Actually the following 'if' should work instead of
+ // the above key event checking, but apparently
+ // PeekMessage() is broken :-(
+ if (mouseMsg.wParam != msg.wParam)
+ break; // leave the message in the queue because
+ // the key state has changed
+#endif
+ MSG *msgPtr = (MSG *)(&msg);
+ // Update the passed in MSG structure with the
+ // most recent one.
+ msgPtr->lParam = mouseMsg.lParam;
+ msgPtr->wParam = mouseMsg.wParam;
+ msgPtr->pt = mouseMsg.pt;
+ // Remove the mouse move message
+ winPeekMessage(&mouseMsg, msg.hwnd, WM_MOUSEMOVE,
+ WM_MOUSEMOVE, PM_REMOVE);
+ } else {
+ break; // there was no more WM_MOUSEMOVE event
+ }
+ }
+ }
+
+ for (i=0; (UINT)mouseTbl[i] != msg.message && mouseTbl[i]; i += 3)
+ ;
+ if (!mouseTbl[i])
+ return false;
+ type = (QEvent::Type)mouseTbl[++i]; // event type
+ button = mouseTbl[++i]; // which button
+ if (button == Qt::XButton1) {
+ switch(GET_XBUTTON_WPARAM(msg.wParam)) {
+ case XBUTTON1:
+ button = Qt::XButton1;
+ break;
+ case XBUTTON2:
+ button = Qt::XButton2;
+ break;
+ }
+ }
+ state = translateButtonState(msg.wParam, type, button); // button state
+ const QPoint widgetPos = mapFromGlobal(QPoint(msg.pt.x, msg.pt.y));
+ QWidget *alienWidget = !internalWinId() ? this : childAt(widgetPos);
+ if (alienWidget && alienWidget->internalWinId())
+ alienWidget = 0;
+
+ if (type == QEvent::MouseMove || type == QEvent::NonClientAreaMouseMove) {
+ if (!(state & Qt::MouseButtonMask))
+ qt_button_down = 0;
+#ifndef QT_NO_CURSOR
+ QCursor *c = qt_grab_cursor();
+ if (!c)
+ c = QApplication::overrideCursor();
+ if (c) // application cursor defined
+ SetCursor(c->handle());
+ else if (type != QEvent::NonClientAreaMouseMove && !qt_button_down) {
+ // use widget cursor if widget is enabled
+ QWidget *w = alienWidget ? alienWidget : this;
+ while (!w->isWindow() && !w->isEnabled())
+ w = w->parentWidget();
+ SetCursor(w->cursor().handle());
+ }
+#endif // QT_NO_CURSOR
+
+ HWND id = effectiveWinId();
+ QWidget *mouseGrabber = QWidget::mouseGrabber();
+ QWidget *activePopupWidget = qApp->activePopupWidget();
+ if (mouseGrabber) {
+ if (!activePopupWidget || (activePopupWidget == this && !rect().contains(widgetPos)))
+ id = mouseGrabber->effectiveWinId();
+ } else if (type == QEvent::NonClientAreaMouseMove) {
+ id = 0;
+ }
+
+ if (curWin != id) { // new current window
+ if (id == 0) {
+ QWidget *leave = qt_last_mouse_receiver;
+ if (!leave)
+ leave = QWidget::find(curWin);
+ QApplicationPrivate::dispatchEnterLeave(0, leave);
+ qt_last_mouse_receiver = 0;
+ curWin = 0;
+ } else {
+ QWidget *leave = 0;
+ if (curWin && qt_last_mouse_receiver)
+ leave = qt_last_mouse_receiver;
+ else
+ leave = QWidget::find(curWin);
+ QWidget *enter = alienWidget ? alienWidget : this;
+ if (mouseGrabber && activePopupWidget) {
+ if (leave != mouseGrabber)
+ enter = mouseGrabber;
+ else
+ enter = activePopupWidget == this ? this : mouseGrabber;
+ }
+ QApplicationPrivate::dispatchEnterLeave(enter, leave);
+ qt_last_mouse_receiver = enter;
+ curWin = enter ? enter->effectiveWinId() : 0;
+ }
+#ifndef Q_OS_WINCE
+
+ if (curWin != 0) {
+ static bool trackMouseEventLookup = false;
+ typedef BOOL (WINAPI *PtrTrackMouseEvent)(LPTRACKMOUSEEVENT);
+ static PtrTrackMouseEvent ptrTrackMouseEvent = 0;
+ if (!trackMouseEventLookup) {
+ trackMouseEventLookup = true;
+ ptrTrackMouseEvent = (PtrTrackMouseEvent)QLibrary::resolve(QLatin1String("comctl32"), "_TrackMouseEvent");
+ }
+ if (ptrTrackMouseEvent && !qApp->d_func()->inPopupMode()) {
+ // We always have to set the tracking, since
+ // Windows detects more leaves than we do..
+ TRACKMOUSEEVENT tme;
+ tme.cbSize = sizeof(TRACKMOUSEEVENT);
+ tme.dwFlags = 0x00000002; // TME_LEAVE
+ tme.hwndTrack = curWin; // Track on window receiving msgs
+ tme.dwHoverTime = (DWORD)-1; // HOVER_DEFAULT
+ ptrTrackMouseEvent(&tme);
+ }
+ }
+#endif // Q_OS_WINCE
+ }
+
+ POINT curPos = msg.pt;
+ if (curPos.x == gpos.x && curPos.y == gpos.y)
+ return true; // same global position
+ gpos = curPos;
+
+ Q_ASSERT(testAttribute(Qt::WA_WState_Created));
+ ScreenToClient(internalWinId(), &curPos);
+
+ pos.rx() = curPos.x;
+ pos.ry() = curPos.y;
+ pos = d_func()->mapFromWS(pos);
+ } else {
+ gpos = msg.pt;
+ pos = mapFromGlobal(QPoint(gpos.x, gpos.y));
+
+ // mouse button pressed
+ if (!qt_button_down && (type == QEvent::MouseButtonPress || type == QEvent::MouseButtonDblClick)) {
+ QWidget *tlw = window();
+ if (QWidget *child = tlw->childAt(mapTo(tlw, pos)))
+ qt_button_down = child;
+ else
+ qt_button_down = this;
+ }
+ }
+
+ bool res = false;
+
+ bool nonClientAreaEvent = type >= QEvent::NonClientAreaMouseMove
+ && type <= QEvent::NonClientAreaMouseButtonDblClick;
+
+ if (qApp->d_func()->inPopupMode()) { // in popup mode
+
+ if (nonClientAreaEvent)
+ return false;
+
+ replayPopupMouseEvent = false;
+ QWidget* activePopupWidget = qApp->activePopupWidget();
+ QWidget *target = activePopupWidget;
+ const QPoint globalPos(gpos.x, gpos.y);
+
+ if (target != this) {
+ if ((windowType() == Qt::Popup) && rect().contains(pos) && 0)
+ target = this;
+ else // send to last popup
+ pos = target->mapFromGlobal(globalPos);
+ }
+ QWidget *popupChild = target->childAt(pos);
+ bool releaseAfter = false;
+ switch (type) {
+ case QEvent::MouseButtonPress:
+ case QEvent::MouseButtonDblClick:
+ popupButtonFocus = popupChild;
+ break;
+ case QEvent::MouseButtonRelease:
+ releaseAfter = true;
+ break;
+ default:
+ break; // nothing for mouse move
+ }
+
+ if (target->isEnabled()) {
+ if (popupButtonFocus) {
+ target = popupButtonFocus;
+ } else if (popupChild) {
+ // forward mouse events to the popup child. mouse move events
+ // are only forwarded to popup children that enable mouse tracking.
+ if (type != QEvent::MouseMove || popupChild->hasMouseTracking())
+ target = popupChild;
+ }
+
+ pos = target->mapFromGlobal(globalPos);
+ QMouseEvent e(type, pos, globalPos,
+ Qt::MouseButton(button),
+ Qt::MouseButtons(state & Qt::MouseButtonMask),
+ Qt::KeyboardModifiers(state & Qt::KeyboardModifierMask));
+ res = QApplicationPrivate::sendMouseEvent(target, &e, alienWidget, this, &qt_button_down,
+ qt_last_mouse_receiver);
+ res = res && e.isAccepted();
+ } else {
+ // close disabled popups when a mouse button is pressed or released
+ switch (type) {
+ case QEvent::MouseButtonPress:
+ case QEvent::MouseButtonDblClick:
+ case QEvent::MouseButtonRelease:
+ target->close();
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (releaseAfter) {
+ popupButtonFocus = 0;
+ qt_button_down = 0;
+ }
+
+ if (type == QEvent::MouseButtonPress
+ && qApp->activePopupWidget() != activePopupWidget
+ && replayPopupMouseEvent) {
+ // the popup dissappeared. Replay the event
+ QWidget* w = QApplication::widgetAt(gpos.x, gpos.y);
+ if (w && !QApplicationPrivate::isBlockedByModal(w)) {
+ Q_ASSERT(w->testAttribute(Qt::WA_WState_Created));
+ HWND hwndTarget = w->effectiveWinId();
+ if (QWidget::mouseGrabber() == 0)
+ setAutoCapture(hwndTarget);
+ if (!w->isActiveWindow())
+ w->activateWindow();
+ POINT widgetpt = gpos;
+ ScreenToClient(hwndTarget, &widgetpt);
+ LPARAM lParam = MAKELPARAM(widgetpt.x, widgetpt.y);
+ winPostMessage(hwndTarget, msg.message, msg.wParam, lParam);
+ }
+ } else if (type == QEvent::MouseButtonRelease && button == Qt::RightButton
+ && qApp->activePopupWidget() == activePopupWidget) {
+ // popup still alive and received right-button-release
+#if !defined(QT_NO_CONTEXTMENU)
+ QContextMenuEvent e2(QContextMenuEvent::Mouse, pos, globalPos,
+ qt_win_getKeyboardModifiers());
+ bool res2 = QApplication::sendSpontaneousEvent( target, &e2 );
+ if (!res) // RMB not accepted
+ res = res2 && e2.isAccepted();
+#endif
+ }
+ } else { // not popup mode
+ int bs = state & Qt::MouseButtonMask;
+ if ((type == QEvent::MouseButtonPress ||
+ type == QEvent::MouseButtonDblClick) && bs == button) {
+ Q_ASSERT(testAttribute(Qt::WA_WState_Created));
+ if (QWidget::mouseGrabber() == 0)
+ setAutoCapture(internalWinId());
+ } else if (type == QEvent::MouseButtonRelease && bs == 0) {
+ if (QWidget::mouseGrabber() == 0)
+ releaseAutoCapture();
+ }
+
+ const QPoint globalPos(gpos.x,gpos.y);
+ QWidget *widget = QApplicationPrivate::pickMouseReceiver(this, globalPos, pos, type,
+ Qt::MouseButtons(bs),
+ qt_button_down, alienWidget);
+ if (!widget)
+ return false; // don't send event
+
+ QMouseEvent e(type, pos, globalPos, Qt::MouseButton(button),
+ Qt::MouseButtons(state & Qt::MouseButtonMask),
+ Qt::KeyboardModifiers(state & Qt::KeyboardModifierMask));
+
+ res = QApplicationPrivate::sendMouseEvent(widget, &e, alienWidget, this, &qt_button_down,
+ qt_last_mouse_receiver);
+
+ // non client area events are only informational, you cannot "handle" them
+ res = res && e.isAccepted() && !nonClientAreaEvent;
+#if !defined(QT_NO_CONTEXTMENU)
+ if (type == QEvent::MouseButtonRelease && button == Qt::RightButton) {
+ QContextMenuEvent e2(QContextMenuEvent::Mouse, pos, globalPos,
+ qt_win_getKeyboardModifiers());
+ bool res2 = QApplication::sendSpontaneousEvent(widget, &e2);
+ if (!res)
+ res = res2 && e2.isAccepted();
+ }
+#endif
+
+ if (type != QEvent::MouseMove)
+ pos.rx() = pos.ry() = -9999; // init for move compression
+ }
+ return res;
+}
+
+bool QETWidget::translateWheelEvent(const MSG &msg)
+{
+ int state = 0;
+
+ if (sm_blockUserInput) // block user interaction during session management
+ return true;
+
+ state = translateButtonState(GET_KEYSTATE_WPARAM(msg.wParam), 0, 0);
+
+ int delta;
+ if (msg.message == WM_MOUSEWHEEL)
+ delta = (short) HIWORD (msg.wParam);
+ else
+ delta = (int) msg.wParam;
+
+ Qt::Orientation orient = (state&Qt::AltModifier
+#if 0
+ // disabled for now - Trenton's one-wheel mouse makes trouble...
+ // "delta" for usual wheels is +-120. +-240 seems to indicate
+ // the second wheel see more recent MSDN for WM_MOUSEWHEEL
+
+ ( // <- parantheses added to make update happy, remove if the
+ // #if 0 is removed
+ || delta == 240 || delta == -240)?Qt::Horizontal:Vertical;
+ if (delta == 240 || delta == -240)
+ delta /= 2;
+#endif
+ ) ? Qt::Horizontal : Qt::Vertical;
+
+ QPoint globalPos;
+
+ globalPos.rx() = (short)LOWORD (msg.lParam);
+ globalPos.ry() = (short)HIWORD (msg.lParam);
+
+
+ // if there is a widget under the mouse and it is not shadowed
+ // by modality, we send the event to it first
+ int ret = 0;
+ QWidget* w = QApplication::widgetAt(globalPos);
+ if (!w || !qt_try_modal(w, (MSG*)&msg, ret)) {
+ //synaptics touchpad shows its own widget at this position
+ //so widgetAt() will fail with that HWND, try child of this widget
+ w = this->childAt(this->mapFromGlobal(globalPos));
+ if (!w)
+ w = this;
+ }
+
+ // send the event to the widget or its ancestors
+ {
+ QWidget* popup = qApp->activePopupWidget();
+ if (popup && w->window() != popup)
+ popup->close();
+#ifndef QT_NO_WHEELEVENT
+ QWheelEvent e(w->mapFromGlobal(globalPos), globalPos, delta,
+ Qt::MouseButtons(state & Qt::MouseButtonMask),
+ Qt::KeyboardModifier(state & Qt::KeyboardModifierMask), orient);
+
+ if (QApplication::sendSpontaneousEvent(w, &e))
+#else
+ Q_UNUSED(orient);
+#endif //QT_NO_WHEELEVENT
+ return true;
+ }
+
+ // send the event to the widget that has the focus or its ancestors, if different
+ if (w != qApp->focusWidget() && (w = qApp->focusWidget())) {
+ QWidget* popup = qApp->activePopupWidget();
+ if (popup && w->window() != popup)
+ popup->close();
+#ifndef QT_NO_WHEELEVENT
+ QWheelEvent e(w->mapFromGlobal(globalPos), globalPos, delta,
+ Qt::MouseButtons(state & Qt::MouseButtonMask),
+ Qt::KeyboardModifier(state & Qt::KeyboardModifierMask), orient);
+ if (QApplication::sendSpontaneousEvent(w, &e))
+#endif //QT_NO_WHEELEVENT
+ return true;
+ }
+ return false;
+}
+
+
+//
+// Windows Wintab to QTabletEvent translation
+//
+
+// the following is adapted from the wintab syspress example (public domain)
+/* -------------------------------------------------------------------------- */
+static void tabletInit(UINT wActiveCsr, HCTX hTab)
+{
+ /* browse WinTab's many info items to discover pressure handling. */
+ if (ptrWTInfo && ptrWTGet) {
+ AXIS np;
+ LOGCONTEXT lc;
+ BYTE wPrsBtn;
+ BYTE logBtns[32];
+ UINT size;
+
+ /* discover the LOGICAL button generated by the pressure channel. */
+ /* get the PHYSICAL button from the cursor category and run it */
+ /* through that cursor's button map (usually the identity map). */
+ wPrsBtn = (BYTE)-1;
+ ptrWTInfo(WTI_CURSORS + wActiveCsr, CSR_NPBUTTON, &wPrsBtn);
+ size = ptrWTInfo(WTI_CURSORS + wActiveCsr, CSR_BUTTONMAP, &logBtns);
+ if ((UINT)wPrsBtn < size)
+ wPrsBtn = logBtns[wPrsBtn];
+
+ /* get the current context for its device variable. */
+ ptrWTGet(hTab, &lc);
+
+ /* get the size of the pressure axis. */
+ QTabletDeviceData tdd;
+ ptrWTInfo(WTI_DEVICES + lc.lcDevice, DVC_NPRESSURE, &np);
+ tdd.minPressure = int(np.axMin);
+ tdd.maxPressure = int(np.axMax);
+
+ ptrWTInfo(WTI_DEVICES + lc.lcDevice, DVC_TPRESSURE, &np);
+ tdd.minTanPressure = int(np.axMin);
+ tdd.maxTanPressure = int(np.axMax);
+
+ ptrWTInfo(WTI_DEVICES + lc.lcDevice, DVC_X, &np);
+ tdd.minX = int(np.axMin);
+ tdd.maxX = int(np.axMax);
+
+ ptrWTInfo(WTI_DEVICES + lc.lcDevice, DVC_Y, &np);
+ tdd.minY = int(np.axMin);
+ tdd.maxY = int(np.axMax);
+
+ ptrWTInfo(WTI_DEVICES + lc.lcDevice, DVC_Z, &np);
+ tdd.minZ = int(np.axMin);
+ tdd.maxZ = int(np.axMax);
+
+ int csr_type,
+ csr_physid;
+ ptrWTInfo(WTI_CURSORS + wActiveCsr, CSR_TYPE, &csr_type);
+ ptrWTInfo(WTI_CURSORS + wActiveCsr, CSR_PHYSID, &csr_physid);
+ tdd.llId = csr_type & 0x0F06;
+ tdd.llId = (tdd.llId << 24) | csr_physid;
+#ifndef QT_NO_TABLETEVENT
+ if (((csr_type & 0x0006) == 0x0002) && ((csr_type & 0x0F06) != 0x0902)) {
+ tdd.currentDevice = QTabletEvent::Stylus;
+ } else {
+ switch (csr_type & 0x0F06) {
+ case 0x0802:
+ tdd.currentDevice = QTabletEvent::Stylus;
+ break;
+ case 0x0902:
+ tdd.currentDevice = QTabletEvent::Airbrush;
+ break;
+ case 0x0004:
+ tdd.currentDevice = QTabletEvent::FourDMouse;
+ break;
+ case 0x0006:
+ tdd.currentDevice = QTabletEvent::Puck;
+ break;
+ case 0x0804:
+ tdd.currentDevice = QTabletEvent::RotationStylus;
+ break;
+ default:
+ tdd.currentDevice = QTabletEvent::NoDevice;
+ }
+ }
+
+ switch (wActiveCsr % 3) {
+ case 2:
+ tdd.currentPointerType = QTabletEvent::Eraser;
+ break;
+ case 1:
+ tdd.currentPointerType = QTabletEvent::Pen;
+ break;
+ case 0:
+ tdd.currentPointerType = QTabletEvent::Cursor;
+ break;
+ default:
+ tdd.currentPointerType = QTabletEvent::UnknownPointer;
+ }
+#endif // QT_NO_TABLETEVENT
+ tCursorInfo()->insert(wActiveCsr, tdd);
+ }
+}
+
+bool QETWidget::translateTabletEvent(const MSG &msg, PACKET *localPacketBuf,
+ int numPackets)
+{
+ Q_UNUSED(msg);
+ POINT ptNew;
+ static DWORD btnNew, btnOld, btnChange;
+ qreal prsNew;
+ ORIENTATION ort;
+ static bool button_pressed = false;
+ int i,
+ tiltX,
+ tiltY;
+ bool sendEvent = false;
+ QEvent::Type t;
+ int z = 0;
+ qreal rotation = 0.0;
+ qreal tangentialPressure;
+
+ // the most common event that we get...
+ t = QEvent::TabletMove;
+ for (i = 0; i < numPackets; i++) {
+ // get the unique ID of the device...
+ btnOld = btnNew;
+ btnNew = localPacketBuf[i].pkButtons;
+ btnChange = btnOld ^ btnNew;
+
+ if (btnNew & btnChange) {
+ button_pressed = true;
+ t = QEvent::TabletPress;
+ }
+ ptNew.x = UINT(localPacketBuf[i].pkX);
+ ptNew.y = UINT(localPacketBuf[i].pkY);
+#ifndef QT_NO_TABLETEVENT
+ z = (currentTabletPointer.currentDevice == QTabletEvent::FourDMouse) ? UINT(localPacketBuf[i].pkZ) : 0;
+#else
+ Q_UNUSED(z);
+#endif // QT_NO_TABLETEVENT
+ prsNew = 0.0;
+ QRect desktopArea = QApplication::desktop()->geometry();
+ QPointF hiResGlobal = currentTabletPointer.scaleCoord(ptNew.x, ptNew.y, desktopArea.left(),
+ desktopArea.width(), desktopArea.top(),
+ desktopArea.height());
+
+ if (btnNew) {
+#ifndef QT_NO_TABLETEVENT
+ if (currentTabletPointer.currentPointerType == QTabletEvent::Pen || currentTabletPointer.currentPointerType == QTabletEvent::Eraser)
+ prsNew = localPacketBuf[i].pkNormalPressure
+ / qreal(currentTabletPointer.maxPressure
+ - currentTabletPointer.minPressure);
+ else
+#endif // QT_NO_TABLETEVENT
+ prsNew = 0;
+ } else if (button_pressed) {
+ // One button press, should only give one button release
+ t = QEvent::TabletRelease;
+ button_pressed = false;
+ }
+ QPoint globalPos(qRound(hiResGlobal.x()), qRound(hiResGlobal.y()));
+
+ // make sure the tablet event get's sent to the proper widget...
+ QWidget *w = QApplication::widgetAt(globalPos);
+ if (qt_button_down)
+ w = qt_button_down; // Pass it to the thing that's grabbed it.
+
+ if (!w)
+ w = this;
+ QPoint localPos = w->mapFromGlobal(globalPos);
+#ifndef QT_NO_TABLETEVENT
+ if (currentTabletPointer.currentDevice == QTabletEvent::Airbrush) {
+ tangentialPressure = localPacketBuf[i].pkTangentPressure
+ / qreal(currentTabletPointer.maxTanPressure
+ - currentTabletPointer.minTanPressure);
+ } else {
+ tangentialPressure = 0.0;
+ }
+#else
+ tangentialPressure = 0.0;
+#endif // QT_NO_TABLETEVENT
+
+ if (!qt_tablet_tilt_support) {
+ tiltX = tiltY = 0;
+ rotation = 0.0;
+ } else {
+ ort = localPacketBuf[i].pkOrientation;
+ // convert from azimuth and altitude to x tilt and y tilt
+ // what follows is the optimized version. Here are the equations
+ // I used to get to this point (in case things change :)
+ // X = sin(azimuth) * cos(altitude)
+ // Y = cos(azimuth) * cos(altitude)
+ // Z = sin(altitude)
+ // X Tilt = arctan(X / Z)
+ // Y Tilt = arctan(Y / Z)
+ double radAzim = (ort.orAzimuth / 10) * (Q_PI / 180);
+ //double radAlt = abs(ort.orAltitude / 10) * (Q_PI / 180);
+ double tanAlt = tan((abs(ort.orAltitude / 10)) * (Q_PI / 180));
+
+ double degX = atan(sin(radAzim) / tanAlt);
+ double degY = atan(cos(radAzim) / tanAlt);
+ tiltX = int(degX * (180 / Q_PI));
+ tiltY = int(-degY * (180 / Q_PI));
+ rotation = ort.orTwist;
+ }
+#ifndef QT_NO_TABLETEVENT
+ QTabletEvent e(t, localPos, globalPos, hiResGlobal, currentTabletPointer.currentDevice,
+ currentTabletPointer.currentPointerType, prsNew, tiltX, tiltY,
+ tangentialPressure, rotation, z, QApplication::keyboardModifiers(), currentTabletPointer.llId);
+ sendEvent = QApplication::sendSpontaneousEvent(w, &e);
+#endif // QT_NO_TABLETEVENT
+ }
+ return sendEvent;
+}
+
+extern bool qt_is_gui_used;
+static void initWinTabFunctions()
+{
+#if defined(Q_OS_WINCE)
+ return;
+#else
+ if (!qt_is_gui_used)
+ return;
+
+ QLibrary library(QLatin1String("wintab32"));
+ if (library.load()) {
+ QT_WA({
+ ptrWTInfo = (PtrWTInfo)library.resolve("WTInfoW");
+ ptrWTGet = (PtrWTGet)library.resolve("WTGetW");
+ } , {
+ ptrWTInfo = (PtrWTInfo)library.resolve("WTInfoA");
+ ptrWTGet = (PtrWTGet)library.resolve("WTGetA");
+ });
+
+ ptrWTEnable = (PtrWTEnable)library.resolve("WTEnable");
+ ptrWTOverlap = (PtrWTEnable)library.resolve("WTOverlap");
+ ptrWTPacketsGet = (PtrWTPacketsGet)library.resolve("WTPacketsGet");
+ }
+#endif // Q_OS_WINCE
+}
+
+
+//
+// Paint event translation
+//
+bool QETWidget::translatePaintEvent(const MSG &msg)
+{
+ if (!isWindow() && testAttribute(Qt::WA_NativeWindow))
+ Q_ASSERT(internalWinId());
+
+ Q_ASSERT(testAttribute(Qt::WA_WState_Created));
+ if (!GetUpdateRect(internalWinId(), 0, FALSE)) { // The update bounding rect is invalid
+ d_func()->hd = 0;
+ setAttribute(Qt::WA_PendingUpdate, false);
+ return false;
+ }
+
+ if (msg.message == WM_ERASEBKGND)
+ return true;
+
+ setAttribute(Qt::WA_PendingUpdate, false);
+ const QRegion dirtyInBackingStore(qt_dirtyRegion(this));
+ // Make sure the invalidated region contains the region we're about to repaint.
+ // BeginPaint will set the clip to the invalidated region and it is impossible
+ // to enlarge it afterwards (only shrink it). Using GetDCEx is not suffient
+ // as it may return an invalid context (especially on Windows Vista).
+ if (!dirtyInBackingStore.isEmpty())
+ InvalidateRgn(internalWinId(), dirtyInBackingStore.handle(), false);
+ PAINTSTRUCT ps;
+ d_func()->hd = BeginPaint(internalWinId(), &ps);
+
+ const QRect updateRect(QPoint(ps.rcPaint.left, ps.rcPaint.top),
+ QPoint(ps.rcPaint.right, ps.rcPaint.bottom));
+
+ // Mapping region from system to qt (32 bit) coordinate system.
+ d_func()->syncBackingStore(updateRect.translated(data->wrect.topLeft()));
+
+ d_func()->hd = 0;
+ EndPaint(internalWinId(), &ps);
+
+ return true;
+}
+
+//
+// Window move and resize (configure) events
+//
+
+bool QETWidget::translateConfigEvent(const MSG &msg)
+{
+ if (!testAttribute(Qt::WA_WState_Created)) // in QWidget::create()
+ return true;
+ if (testAttribute(Qt::WA_WState_ConfigPending))
+ return true;
+ if (testAttribute(Qt::WA_DontShowOnScreen))
+ return true;
+ if (!isWindow())
+ return true;
+ setAttribute(Qt::WA_WState_ConfigPending); // set config flag
+ QRect cr = geometry();
+ if (msg.message == WM_SIZE) { // resize event
+ WORD a = LOWORD(msg.lParam);
+ WORD b = HIWORD(msg.lParam);
+ QSize oldSize = size();
+ QSize newSize(a, b);
+#ifdef Q_OS_WINCE_WM
+ if (isFullScreen() && (oldSize.width() == newSize.height()) && (oldSize.height() == newSize.width()))
+ qt_wince_hide_taskbar(internalWinId());
+#endif
+ cr.setSize(newSize);
+ if (msg.wParam != SIZE_MINIMIZED)
+ data->crect = cr;
+ if (isWindow()) { // update title/icon text
+ d_func()->createTLExtra();
+ // Capture SIZE_MINIMIZED without preceding WM_SYSCOMMAND
+ // (like Windows+M)
+ if (msg.wParam == SIZE_MINIMIZED && !isMinimized()) {
+#ifndef Q_OS_WINCE
+ const QString title = windowIconText();
+ if (!title.isEmpty())
+ d_func()->setWindowTitle_helper(title);
+#endif
+ data->window_state |= Qt::WindowMinimized;
+ if (isVisible()) {
+ QHideEvent e;
+ QApplication::sendSpontaneousEvent(this, &e);
+ hideChildren(true);
+ }
+ } else if (msg.wParam != SIZE_MINIMIZED && isMinimized()) {
+#ifndef Q_OS_WINCE
+ const QString title = windowTitle();
+ if (!title.isEmpty())
+ d_func()->setWindowTitle_helper(title);
+#endif
+ data->window_state &= ~Qt::WindowMinimized;
+ showChildren(true);
+ QShowEvent e;
+ QApplication::sendSpontaneousEvent(this, &e);
+ }
+ }
+ if (msg.wParam != SIZE_MINIMIZED && oldSize != newSize) {
+ if (isVisible()) {
+ QTLWExtra *tlwExtra = maybeTopData();
+ static bool slowResize = qgetenv("QT_SLOW_TOPLEVEL_RESIZE").toInt();
+ const bool hasStaticContents = tlwExtra && tlwExtra->backingStore
+ && tlwExtra->backingStore->hasStaticContents();
+ // If we have a backing store with static contents, we have to disable the top-level
+ // resize optimization in order to get invalidated regions for resized widgets.
+ // The optimization discards all invalidateBuffer() calls since we're going to
+ // repaint everything anyways, but that's not the case with static contents.
+ if (!slowResize && tlwExtra && !hasStaticContents)
+ tlwExtra->inTopLevelResize = true;
+ QResizeEvent e(newSize, oldSize);
+ QApplication::sendSpontaneousEvent(this, &e);
+ if (d_func()->paintOnScreen()) {
+ QRegion updateRegion(rect());
+ if (testAttribute(Qt::WA_StaticContents))
+ updateRegion -= QRect(0, 0, oldSize.width(), oldSize.height());
+ d_func()->syncBackingStore(updateRegion);
+ } else {
+ d_func()->syncBackingStore();
+ }
+ if (!slowResize && tlwExtra)
+ tlwExtra->inTopLevelResize = false;
+ } else {
+ QResizeEvent *e = new QResizeEvent(newSize, oldSize);
+ QApplication::postEvent(this, e);
+ }
+ }
+} else if (msg.message == WM_MOVE) { // move event
+ int a = (int) (short) LOWORD(msg.lParam);
+ int b = (int) (short) HIWORD(msg.lParam);
+ QPoint oldPos = geometry().topLeft();
+ QPoint newCPos(a, b);
+ // Ignore silly Windows move event to wild pos after iconify.
+#if !defined(Q_OS_WINCE)
+ if (!IsIconic(internalWinId()) && newCPos != oldPos) {
+#endif
+ cr.moveTopLeft(newCPos);
+ data->crect = cr;
+ if (isVisible()) {
+ QMoveEvent e(newCPos, oldPos); // cpos (client position)
+ QApplication::sendSpontaneousEvent(this, &e);
+ } else {
+ QMoveEvent * e = new QMoveEvent(newCPos, oldPos);
+ QApplication::postEvent(this, e);
+ }
+#if !defined(Q_OS_WINCE)
+ }
+#endif
+ }
+ setAttribute(Qt::WA_WState_ConfigPending, false); // clear config flag
+ return true;
+}
+
+
+//
+// Close window event translation.
+//
+// This class is a friend of QApplication because it needs to emit the
+// lastWindowClosed() signal when the last top level widget is closed.
+//
+
+bool QETWidget::translateCloseEvent(const MSG &)
+{
+ return d_func()->close_helper(QWidgetPrivate::CloseWithSpontaneousEvent);
+}
+
+
+void QApplication::setCursorFlashTime(int msecs)
+{
+ SetCaretBlinkTime(msecs / 2);
+ QApplicationPrivate::cursor_flash_time = msecs;
+}
+
+
+int QApplication::cursorFlashTime()
+{
+ int blink = (int)GetCaretBlinkTime();
+ if (!blink)
+ return QApplicationPrivate::cursor_flash_time;
+ if (blink > 0)
+ return 2*blink;
+ return 0;
+}
+
+
+void QApplication::setDoubleClickInterval(int ms)
+{
+#ifndef Q_OS_WINCE
+ SetDoubleClickTime(ms);
+#endif
+ QApplicationPrivate::mouse_double_click_time = ms;
+}
+
+int QApplication::doubleClickInterval()
+{
+ int ms = GetDoubleClickTime();
+ if (ms != 0)
+ return ms;
+ return QApplicationPrivate::mouse_double_click_time;
+}
+
+
+void QApplication::setKeyboardInputInterval(int ms)
+{
+ QApplicationPrivate::keyboard_input_time = ms;
+}
+
+int QApplication::keyboardInputInterval()
+{
+ // FIXME: get from the system
+ return QApplicationPrivate::keyboard_input_time;
+}
+
+#ifndef QT_NO_WHEELEVENT
+void QApplication::setWheelScrollLines(int n)
+{
+#ifdef SPI_SETWHEELSCROLLLINES
+ if (n < 0)
+ n = 0;
+ QT_WA({
+ SystemParametersInfo(SPI_SETWHEELSCROLLLINES, (uint)n, 0, 0);
+ } , {
+ SystemParametersInfoA(SPI_SETWHEELSCROLLLINES, (uint)n, 0, 0);
+ });
+#else
+ QApplicationPrivate::wheel_scroll_lines = n;
+#endif
+}
+
+int QApplication::wheelScrollLines()
+{
+#ifdef SPI_GETWHEELSCROLLLINES
+ uint i = 3;
+ QT_WA({
+ SystemParametersInfo(SPI_GETWHEELSCROLLLINES, sizeof(uint), &i, 0);
+ } , {
+ SystemParametersInfoA(SPI_GETWHEELSCROLLLINES, sizeof(uint), &i, 0);
+ });
+ if (i > INT_MAX)
+ i = INT_MAX;
+ return i;
+#else
+ return QApplicationPrivate::wheel_scroll_lines;
+#endif
+}
+#endif //QT_NO_WHEELEVENT
+
+static bool effect_override = false;
+
+void QApplication::setEffectEnabled(Qt::UIEffect effect, bool enable)
+{
+ effect_override = true;
+ switch (effect) {
+ case Qt::UI_AnimateMenu:
+ QApplicationPrivate::animate_menu = enable;
+ break;
+ case Qt::UI_FadeMenu:
+ QApplicationPrivate::fade_menu = enable;
+ break;
+ case Qt::UI_AnimateCombo:
+ QApplicationPrivate::animate_combo = enable;
+ break;
+ case Qt::UI_AnimateTooltip:
+ QApplicationPrivate::animate_tooltip = enable;
+ break;
+ case Qt::UI_FadeTooltip:
+ QApplicationPrivate::fade_tooltip = enable;
+ break;
+ case Qt::UI_AnimateToolBox:
+ QApplicationPrivate::animate_toolbox = enable;
+ break;
+ default:
+ QApplicationPrivate::animate_ui = enable;
+ break;
+ }
+}
+
+bool QApplication::isEffectEnabled(Qt::UIEffect effect)
+{
+ if (QColormap::instance().depth() < 16)
+ return false;
+
+ if (!effect_override && desktopSettingsAware()
+ && !(QSysInfo::WindowsVersion == QSysInfo::WV_95 || QSysInfo::WindowsVersion == QSysInfo::WV_NT)) {
+ // we know that they can be used when we are here
+ BOOL enabled = false;
+ UINT api;
+ switch (effect) {
+ case Qt::UI_AnimateMenu:
+ api = SPI_GETMENUANIMATION;
+ break;
+ case Qt::UI_FadeMenu:
+ if (QSysInfo::WindowsVersion & QSysInfo::WV_DOS_based)
+ return false;
+ api = SPI_GETMENUFADE;
+ break;
+ case Qt::UI_AnimateCombo:
+ api = SPI_GETCOMBOBOXANIMATION;
+ break;
+ case Qt::UI_AnimateTooltip:
+ if (QSysInfo::WindowsVersion & QSysInfo::WV_DOS_based)
+ api = SPI_GETMENUANIMATION;
+ else
+ api = SPI_GETTOOLTIPANIMATION;
+ break;
+ case Qt::UI_FadeTooltip:
+ if (QSysInfo::WindowsVersion & QSysInfo::WV_DOS_based)
+ return false;
+ api = SPI_GETTOOLTIPFADE;
+ break;
+ default:
+ api = SPI_GETUIEFFECTS;
+ break;
+ }
+ QT_WA({
+ SystemParametersInfo(api, 0, &enabled, 0);
+ } , {
+ SystemParametersInfoA(api, 0, &enabled, 0);
+ });
+ return enabled;
+ }
+
+ switch(effect) {
+ case Qt::UI_AnimateMenu:
+ return QApplicationPrivate::animate_menu;
+ case Qt::UI_FadeMenu:
+ return QApplicationPrivate::fade_menu;
+ case Qt::UI_AnimateCombo:
+ return QApplicationPrivate::animate_combo;
+ case Qt::UI_AnimateTooltip:
+ return QApplicationPrivate::animate_tooltip;
+ case Qt::UI_FadeTooltip:
+ return QApplicationPrivate::fade_tooltip;
+ case Qt::UI_AnimateToolBox:
+ return QApplicationPrivate::animate_toolbox;
+ default:
+ return QApplicationPrivate::animate_ui;
+ }
+}
+
+#ifndef QT_NO_SESSIONMANAGER
+
+bool QSessionManager::allowsInteraction()
+{
+ sm_blockUserInput = false;
+ return true;
+}
+
+bool QSessionManager::allowsErrorInteraction()
+{
+ sm_blockUserInput = false;
+ return true;
+}
+
+void QSessionManager::release()
+{
+ if (sm_smActive)
+ sm_blockUserInput = true;
+}
+
+void QSessionManager::cancel()
+{
+ sm_cancel = true;
+}
+
+#endif //QT_NO_SESSIONMANAGER
+
+QT_END_NAMESPACE
diff --git a/src/gui/kernel/qapplication_x11.cpp b/src/gui/kernel/qapplication_x11.cpp
new file mode 100644
index 0000000000..1df442f39c
--- /dev/null
+++ b/src/gui/kernel/qapplication_x11.cpp
@@ -0,0 +1,5919 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+// ### 4.0: examine Q_EXPORT's below. The respective symbols had all
+// been in use (e.g. in the KDE wm) before the introduction of a version
+// map. One might want to turn some of them into proper public API and
+// provide a proper alternative for others. See also the exports in
+// qapplication_win.cpp, which suggest a unification.
+
+#include "qplatformdefs.h"
+
+#include "qcolormap.h"
+#include "qdesktopwidget.h"
+#include "qapplication.h"
+#include "qapplication_p.h"
+#include "qcursor.h"
+#include "qwidget.h"
+#include "qbitarray.h"
+#include "qpainter.h"
+#include "qfile.h"
+#include "qpixmapcache.h"
+#include "qdatetime.h"
+#include "qtextcodec.h"
+#include "qdatastream.h"
+#include "qbuffer.h"
+#include "qsocketnotifier.h"
+#include "qsessionmanager.h"
+#include "qclipboard.h"
+#include "qwhatsthis.h"
+#include "qsettings.h"
+#include "qstylefactory.h"
+#include "qfileinfo.h"
+#include "qdir.h"
+#include "qhash.h"
+#include "qevent.h"
+#include "qevent_p.h"
+#include "qvarlengtharray.h"
+#include "qdebug.h"
+#include <private/qunicodetables_p.h>
+#include <private/qcrashhandler_p.h>
+#include <private/qcolor_p.h>
+#include <private/qcursor_p.h>
+#include "qstyle.h"
+#include "qmetaobject.h"
+#include "qtimer.h"
+#include "qlibrary.h"
+#include <private/qgraphicssystemfactory_p.h>
+
+#if !defined (QT_NO_TABLET)
+extern "C" {
+# define class c_class //XIproto.h has a name member named 'class' which the c++ compiler doesn't like
+# include <wacomcfg.h>
+# undef class
+}
+#endif
+
+//#define ALIEN_DEBUG
+
+#if !defined(QT_NO_GLIB)
+# include "qguieventdispatcher_glib_p.h"
+#endif
+#include "qeventdispatcher_x11_p.h"
+#include <private/qpaintengine_x11_p.h>
+
+#include <private/qkeymapper_p.h>
+
+// Input method stuff
+#ifndef QT_NO_IM
+#include "qinputcontext.h"
+#include "qinputcontextfactory.h"
+#endif // QT_NO_IM
+
+#ifndef QT_NO_XFIXES
+#include <X11/extensions/Xfixes.h>
+#endif // QT_NO_XFIXES
+
+#include "qt_x11_p.h"
+#include "qx11info_x11.h"
+
+#define XK_MISCELLANY
+#include <X11/keysymdef.h>
+#include <X11/extensions/XI.h>
+
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <locale.h>
+
+#include "qwidget_p.h"
+
+#include <private/qbackingstore_p.h>
+
+QT_BEGIN_NAMESPACE
+
+//#define X_NOT_BROKEN
+#ifdef X_NOT_BROKEN
+// Some X libraries are built with setlocale #defined to _Xsetlocale,
+// even though library users are then built WITHOUT such a definition.
+// This creates a problem - Qt might setlocale() one value, but then
+// X looks and doesn't see the value Qt set. The solution here is to
+// implement _Xsetlocale just in case X calls it - redirecting it to
+// the real libC version.
+//
+# ifndef setlocale
+extern "C" char *_Xsetlocale(int category, const char *locale);
+char *_Xsetlocale(int category, const char *locale)
+{
+ //qDebug("_Xsetlocale(%d,%s),category,locale");
+ return setlocale(category,locale);
+}
+# endif // setlocale
+#endif // X_NOT_BROKEN
+
+/* Warning: if you modify this string, modify the list of atoms in qt_x11_p.h as well! */
+static const char * x11_atomnames = {
+ // window-manager <-> client protocols
+ "WM_PROTOCOLS\0"
+ "WM_DELETE_WINDOW\0"
+ "WM_TAKE_FOCUS\0"
+ "_NET_WM_PING\0"
+ "_NET_WM_CONTEXT_HELP\0"
+
+ // ICCCM window state
+ "WM_STATE\0"
+ "WM_CHANGE_STATE\0"
+
+ // Session management
+ "WM_CLIENT_LEADER\0"
+ "WM_WINDOW_ROLE\0"
+ "SM_CLIENT_ID\0"
+
+ // Clipboard
+ "CLIPBOARD\0"
+ "INCR\0"
+ "TARGETS\0"
+ "MULTIPLE\0"
+ "TIMESTAMP\0"
+ "CLIP_TEMPORARY\0"
+ "_QT_SELECTION\0"
+ "_QT_CLIPBOARD_SENTINEL\0"
+ "_QT_SELECTION_SENTINEL\0"
+
+ "RESOURCE_MANAGER\0"
+
+ "_XSETROOT_ID\0"
+
+ "_QT_SCROLL_DONE\0"
+ "_QT_INPUT_ENCODING\0"
+
+ "_MOTIF_WM_HINTS\0"
+
+ "DTWM_IS_RUNNING\0"
+ "KDE_FULL_SESSION\0"
+ "KWIN_RUNNING\0"
+ "KWM_RUNNING\0"
+ "GNOME_BACKGROUND_PROPERTIES\0"
+ "ENLIGHTENMENT_DESKTOP\0"
+ "_SGI_DESKS_MANAGER\0"
+
+ // EWMH (aka NETWM)
+ "_NET_SUPPORTED\0"
+ "_NET_VIRTUAL_ROOTS\0"
+ "_NET_WORKAREA\0"
+
+ "_NET_MOVERESIZE_WINDOW\0"
+ "_NET_WM_MOVERESIZE\0"
+
+ "_NET_WM_NAME\0"
+ "_NET_WM_ICON_NAME\0"
+ "_NET_WM_ICON\0"
+
+ "_NET_WM_PID\0"
+
+ "_NET_WM_WINDOW_OPACITY\0"
+
+ "_NET_WM_STATE\0"
+ "_NET_WM_STATE_ABOVE\0"
+ "_NET_WM_STATE_BELOW\0"
+ "_NET_WM_STATE_FULLSCREEN\0"
+ "_NET_WM_STATE_MAXIMIZED_HORZ\0"
+ "_NET_WM_STATE_MAXIMIZED_VERT\0"
+ "_NET_WM_STATE_MODAL\0"
+ "_NET_WM_STATE_STAYS_ON_TOP\0"
+ "_NET_WM_STATE_DEMANDS_ATTENTION\0"
+
+ "_NET_WM_USER_TIME\0"
+ "_NET_WM_USER_TIME_WINDOW\0"
+ "_NET_WM_FULL_PLACEMENT\0"
+
+ "_NET_WM_WINDOW_TYPE\0"
+ "_NET_WM_WINDOW_TYPE_DESKTOP\0"
+ "_NET_WM_WINDOW_TYPE_DOCK\0"
+ "_NET_WM_WINDOW_TYPE_TOOLBAR\0"
+ "_NET_WM_WINDOW_TYPE_MENU\0"
+ "_NET_WM_WINDOW_TYPE_UTILITY\0"
+ "_NET_WM_WINDOW_TYPE_SPLASH\0"
+ "_NET_WM_WINDOW_TYPE_DIALOG\0"
+ "_NET_WM_WINDOW_TYPE_DROPDOWN_MENU\0"
+ "_NET_WM_WINDOW_TYPE_POPUP_MENU\0"
+ "_NET_WM_WINDOW_TYPE_TOOLTIP\0"
+ "_NET_WM_WINDOW_TYPE_NOTIFICATION\0"
+ "_NET_WM_WINDOW_TYPE_COMBO\0"
+ "_NET_WM_WINDOW_TYPE_DND\0"
+ "_NET_WM_WINDOW_TYPE_NORMAL\0"
+ "_KDE_NET_WM_WINDOW_TYPE_OVERRIDE\0"
+
+ "_KDE_NET_WM_FRAME_STRUT\0"
+
+ "_NET_STARTUP_INFO\0"
+ "_NET_STARTUP_INFO_BEGIN\0"
+
+ "_NET_SUPPORTING_WM_CHECK\0"
+
+ "_NET_WM_CM_S0\0"
+
+ "_NET_SYSTEM_TRAY_VISUAL\0"
+
+ // Property formats
+ "COMPOUND_TEXT\0"
+ "TEXT\0"
+ "UTF8_STRING\0"
+
+ // xdnd
+ "XdndEnter\0"
+ "XdndPosition\0"
+ "XdndStatus\0"
+ "XdndLeave\0"
+ "XdndDrop\0"
+ "XdndFinished\0"
+ "XdndTypeList\0"
+ "XdndActionList\0"
+
+ "XdndSelection\0"
+
+ "XdndAware\0"
+ "XdndProxy\0"
+
+ "XdndActionCopy\0"
+ "XdndActionLink\0"
+ "XdndActionMove\0"
+ "XdndActionPrivate\0"
+
+ // Motif DND
+ "_MOTIF_DRAG_AND_DROP_MESSAGE\0"
+ "_MOTIF_DRAG_INITIATOR_INFO\0"
+ "_MOTIF_DRAG_RECEIVER_INFO\0"
+ "_MOTIF_DRAG_WINDOW\0"
+ "_MOTIF_DRAG_TARGETS\0"
+
+ "XmTRANSFER_SUCCESS\0"
+ "XmTRANSFER_FAILURE\0"
+
+ // Xkb
+ "_XKB_RULES_NAMES\0"
+
+ // XEMBED
+ "_XEMBED\0"
+ "_XEMBED_INFO\0"
+};
+
+Q_GUI_EXPORT QX11Data *qt_x11Data = 0;
+
+/*****************************************************************************
+ Internal variables and functions
+ *****************************************************************************/
+static const char *appName = 0; // application name
+static const char *appClass = 0; // application class
+static const char *appFont = 0; // application font
+static const char *appBGCol = 0; // application bg color
+static const char *appFGCol = 0; // application fg color
+static const char *appBTNCol = 0; // application btn color
+static const char *mwGeometry = 0; // main widget geometry
+static const char *mwTitle = 0; // main widget title
+char *qt_ximServer = 0; // XIM Server will connect to
+static bool appSync = false; // X11 synchronization
+#if defined(QT_DEBUG)
+static bool appNoGrab = false; // X11 grabbing enabled
+static bool appDoGrab = false; // X11 grabbing override (gdb)
+#endif
+static bool app_save_rootinfo = false; // save root info
+static bool app_do_modal = false; // modal mode
+static Window curWin = 0; // current window
+
+
+// function to update the workarea of the screen - in qdesktopwidget_x11.cpp
+extern void qt_desktopwidget_update_workarea();
+
+// Function to change the window manager state (from qwidget_x11.cpp)
+extern void qt_change_net_wm_state(const QWidget *w, bool set, Atom one, Atom two = 0);
+
+// modifier masks for alt, meta, super, hyper, and mode_switch - detected when the application starts
+// and/or keyboard layout changes
+uchar qt_alt_mask = 0;
+uchar qt_meta_mask = 0;
+uchar qt_super_mask = 0;
+uchar qt_hyper_mask = 0;
+uchar qt_mode_switch_mask = 0;
+
+// flags for extensions for special Languages, currently only for RTL languages
+bool qt_use_rtl_extensions = false;
+
+static Window mouseActWindow = 0; // window where mouse is
+static Qt::MouseButton mouseButtonPressed = Qt::NoButton; // last mouse button pressed
+static Qt::MouseButtons mouseButtonState = Qt::NoButton; // mouse button state
+static Time mouseButtonPressTime = 0; // when was a button pressed
+static short mouseXPos, mouseYPos; // mouse pres position in act window
+static short mouseGlobalXPos, mouseGlobalYPos; // global mouse press position
+
+extern QWidgetList *qt_modal_stack; // stack of modal widgets
+
+// window where mouse buttons have been pressed
+static Window pressed_window = XNone;
+
+// popup control
+static bool replayPopupMouseEvent = false;
+static bool popupGrabOk;
+
+bool qt_sm_blockUserInput = false; // session management
+
+Q_GUI_EXPORT int qt_xfocusout_grab_counter = 0;
+
+#if !defined (QT_NO_TABLET)
+Q_GLOBAL_STATIC(QTabletDeviceDataList, tablet_devices)
+QTabletDeviceDataList *qt_tablet_devices()
+{
+ return tablet_devices();
+}
+
+extern bool qt_tabletChokeMouse;
+#endif
+
+static bool qt_x11EventFilter(XEvent* ev)
+{
+ long unused;
+ if (qApp->filterEvent(ev, &unused))
+ return true;
+ return qApp->x11EventFilter(ev);
+}
+
+#if !defined(QT_NO_XIM)
+XIMStyle qt_xim_preferred_style = 0;
+#endif
+int qt_ximComposingKeycode=0;
+QTextCodec * qt_input_mapper = 0;
+
+extern bool qt_check_clipboard_sentinel(); //def in qclipboard_x11.cpp
+extern bool qt_check_selection_sentinel(); //def in qclipboard_x11.cpp
+extern bool qt_xfixes_clipboard_changed(Window clipboardOwner, Time timestamp); //def in qclipboard_x11.cpp
+extern bool qt_xfixes_selection_changed(Window selectionOwner, Time timestamp); //def in qclipboard_x11.cpp
+
+static void qt_save_rootinfo();
+Q_GUI_EXPORT bool qt_try_modal(QWidget *, XEvent *);
+
+QWidget *qt_button_down = 0; // last widget to be pressed with the mouse
+QPointer<QWidget> qt_last_mouse_receiver = 0;
+static QWidget *qt_popup_down = 0; // popup that contains the pressed widget
+
+extern bool qt_xdnd_dragging;
+
+// gui or non-gui from qapplication.cpp
+extern bool qt_is_gui_used;
+
+/*!
+ \internal
+ Try to resolve a \a symbol from \a library with the version specified
+ by \a vernum.
+
+ Note that, in the case of the Xfixes library, \a vernum is not the same as
+ \c XFIXES_MAJOR - it is a part of soname and may differ from the Xfixes
+ version.
+*/
+static void* qt_load_library_runtime(const char *library, int vernum,
+ int highestVernum, const char *symbol)
+{
+ QList<int> versions;
+ // we try to load in the following order:
+ // explicit version -> the default one -> (from the highest (highestVernum) to the lowest (vernum) )
+ if (vernum != -1)
+ versions << vernum;
+ versions << -1;
+ if (vernum != -1) {
+ for(int i = highestVernum; i > vernum; --i)
+ versions << i;
+ }
+ Q_FOREACH(int version, versions) {
+ QLatin1String libName(library);
+ QLibrary xfixesLib(libName, version);
+ if (xfixesLib.load()) {
+ void *ptr = xfixesLib.resolve(symbol);
+ if (ptr)
+ return ptr;
+ }
+ }
+ return 0;
+}
+
+#ifndef QT_NO_XINPUT
+# ifdef QT_RUNTIME_XINPUT
+# define XINPUT_LOAD_RUNTIME(vernum, symbol, symbol_type) \
+ (symbol_type)qt_load_library_runtime("libXi", vernum, 6, #symbol);
+# define XINPUT_LOAD(symbol) \
+ XINPUT_LOAD_RUNTIME(1, symbol, Ptr##symbol)
+# else // not runtime XInput
+# define XINPUT_LOAD(symbol) symbol
+# endif // QT_RUNTIME_XINPUT
+#else // not using Xinput at all
+# define XINPUT_LOAD(symbol) 0
+#endif // QT_NO_XINPUT
+
+#ifndef QT_NO_XFIXES
+# ifdef QT_RUNTIME_XFIXES
+# define XFIXES_LOAD_RUNTIME(vernum, symbol, symbol_type) \
+ (symbol_type)qt_load_library_runtime("libXfixes", vernum, 4, #symbol);
+# define XFIXES_LOAD_V1(symbol) \
+ XFIXES_LOAD_RUNTIME(1, symbol, Ptr##symbol)
+# define XFIXES_LOAD_V2(symbol) \
+ XFIXES_LOAD_RUNTIME(2, symbol, Ptr##symbol)
+
+# else // not runtime Xfixes
+
+# if XFIXES_MAJOR >= 2
+# define XFIXES_LOAD_V1(symbol) symbol
+# define XFIXES_LOAD_V2(symbol) symbol
+# elif XFIXES_MAJOR >= 1
+# define XFIXES_LOAD_V1(symbol) symbol
+# define XFIXES_LOAD_V2(symbol) 0
+# else
+# error Unsupported version of Xfixes
+# endif
+# endif // QT_RUNTIME_XFIXES
+#else // not using Xfixes at all
+# define XFIXES_LOAD_V1(symbol) 0
+# define XFIXES_LOAD_V2(symbol) 0
+#endif // QT_NO_XFIXES
+
+#ifndef QT_NO_XFIXES
+
+struct qt_xfixes_selection_event_data
+{
+ // which selection to filter out.
+ Atom selection;
+};
+
+#if defined(Q_C_CALLBACKS)
+extern "C" {
+#endif
+
+static Bool qt_xfixes_scanner(Display*, XEvent *event, XPointer arg)
+{
+ qt_xfixes_selection_event_data *data =
+ reinterpret_cast<qt_xfixes_selection_event_data*>(arg);
+ if (event->type == X11->xfixes_eventbase + XFixesSelectionNotify) {
+ XFixesSelectionNotifyEvent *xfixes_event = reinterpret_cast<XFixesSelectionNotifyEvent*>(event);
+ if (xfixes_event->selection == data->selection)
+ return true;
+ }
+ return false;
+}
+
+#if defined(Q_C_CALLBACKS)
+}
+#endif
+
+#endif // QT_NO_XFIXES
+
+class QETWidget : public QWidget // event translator widget
+{
+public:
+ bool translateMouseEvent(const XEvent *);
+ void translatePaintEvent(const XEvent *);
+ bool translateConfigEvent(const XEvent *);
+ bool translateCloseEvent(const XEvent *);
+ bool translateScrollDoneEvent(const XEvent *);
+ bool translateWheelEvent(int global_x, int global_y, int delta, Qt::MouseButtons buttons,
+ Qt::KeyboardModifiers modifiers, Qt::Orientation orient);
+#if !defined (QT_NO_TABLET)
+ bool translateXinputEvent(const XEvent*, QTabletDeviceData *tablet);
+#endif
+ bool translatePropertyEvent(const XEvent *);
+
+ void doDeferredMap()
+ {
+ Q_ASSERT(testAttribute(Qt::WA_WState_Created));
+ if (!testAttribute(Qt::WA_Resized)) {
+ adjustSize();
+ setAttribute(Qt::WA_Resized, false);
+ }
+
+ /*
+ workaround for WM's that throw away ConfigureRequests from the following:
+
+ window->hide();
+ window->move(x, y); // could also be resize(), move()+resize(), or setGeometry()
+ window->show();
+ */
+ QRect r = geometry();
+
+ XMoveResizeWindow(X11->display,
+ internalWinId(),
+ r.x(),
+ r.y(),
+ r.width(),
+ r.height());
+
+ // static gravity!
+ XSizeHints sh;
+ long unused;
+ XGetWMNormalHints(X11->display, internalWinId(), &sh, &unused);
+ sh.flags |= USPosition | PPosition | USSize | PSize | PWinGravity;
+ sh.x = r.x();
+ sh.y = r.y();
+ sh.width = r.width();
+ sh.height = r.height();
+ sh.win_gravity = StaticGravity;
+ XSetWMNormalHints(X11->display, internalWinId(), &sh);
+
+ setAttribute(Qt::WA_Mapped);
+ if (testAttribute(Qt::WA_DontShowOnScreen))
+ return;
+ d_func()->topData()->waitingForMapNotify = 1;
+ XMapWindow(X11->display, internalWinId());
+ }
+};
+
+
+void QApplicationPrivate::createEventDispatcher()
+{
+ Q_Q(QApplication);
+#if !defined(QT_NO_GLIB)
+ if (qgetenv("QT_NO_GLIB").isEmpty() && QEventDispatcherGlib::versionSupported())
+ eventDispatcher = (q->type() != QApplication::Tty
+ ? new QGuiEventDispatcherGlib(q)
+ : new QEventDispatcherGlib(q));
+ else
+#endif
+ eventDispatcher = (q->type() != QApplication::Tty
+ ? new QEventDispatcherX11(q)
+ : new QEventDispatcherUNIX(q));
+}
+
+/*****************************************************************************
+ Default X error handlers
+ *****************************************************************************/
+
+#if defined(Q_C_CALLBACKS)
+extern "C" {
+#endif
+
+static int (*original_x_errhandler)(Display *dpy, XErrorEvent *);
+static int (*original_xio_errhandler)(Display *dpy);
+
+static int qt_x_errhandler(Display *dpy, XErrorEvent *err)
+{
+ switch (err->error_code) {
+ case BadAtom:
+ if (err->request_code == 20 /* X_GetProperty */
+ && (err->resourceid == XA_RESOURCE_MANAGER
+ || err->resourceid == XA_RGB_DEFAULT_MAP
+ || err->resourceid == ATOM(_NET_SUPPORTED)
+ || err->resourceid == ATOM(_NET_SUPPORTING_WM_CHECK)
+ || err->resourceid == ATOM(KDE_FULL_SESSION)
+ || err->resourceid == ATOM(KWIN_RUNNING)
+ || err->resourceid == ATOM(XdndProxy)
+ || err->resourceid == ATOM(XdndAware))) {
+ // Perhaps we're running under SECURITY reduction? :/
+ return 0;
+ }
+ break;
+
+ case BadWindow:
+ if (err->request_code == 2 /* X_ChangeWindowAttributes */
+ || err->request_code == 38 /* X_QueryPointer */) {
+ for (int i = 0; i < ScreenCount(dpy); ++i) {
+ if (err->resourceid == RootWindow(dpy, i)) {
+ // Perhaps we're running under SECURITY reduction? :/
+ return 0;
+ }
+ }
+ }
+ X11->seen_badwindow = true;
+ if (err->request_code == 25 /* X_SendEvent */) {
+ for (int i = 0; i < ScreenCount(dpy); ++i) {
+ if (err->resourceid == RootWindow(dpy, i)) {
+ // Perhaps we're running under SECURITY reduction? :/
+ return 0;
+ }
+ }
+ if (X11->xdndHandleBadwindow()) {
+ qDebug("xdndHandleBadwindow returned true");
+ return 0;
+ }
+ }
+ if (X11->ignore_badwindow)
+ return 0;
+ break;
+
+ case BadMatch:
+ if (err->request_code == 42 /* X_SetInputFocus */)
+ return 0;
+ break;
+
+ default:
+ if (err->request_code == X11->xinput_major
+ && err->error_code == (X11->xinput_errorbase + XI_BadDevice)
+ && err->minor_code == 3 /* X_OpenDevice */) {
+ return 0;
+ }
+ break;
+ }
+
+ char errstr[256];
+ XGetErrorText( dpy, err->error_code, errstr, 256 );
+ char buffer[256];
+ char request_str[256];
+ qsnprintf(buffer, 256, "%d", err->request_code);
+ XGetErrorDatabaseText(dpy, "XRequest", buffer, "", request_str, 256);
+ if (err->request_code < 128) {
+ // X error for a normal protocol request
+ qWarning( "X Error: %s %d\n"
+ " Major opcode: %d (%s)\n"
+ " Resource id: 0x%lx",
+ errstr, err->error_code,
+ err->request_code,
+ request_str,
+ err->resourceid );
+ } else {
+ // X error for an extension request
+ const char *extensionName = 0;
+ if (err->request_code == X11->xrender_major)
+ extensionName = "RENDER";
+ else if (err->request_code == X11->xrandr_major)
+ extensionName = "RANDR";
+ else if (err->request_code == X11->xinput_major)
+ extensionName = "XInputExtension";
+ else if (err->request_code == X11->mitshm_major)
+ extensionName = "MIT-SHM";
+
+ char minor_str[256];
+ if (extensionName) {
+ qsnprintf(buffer, 256, "%s.%d", extensionName, err->minor_code);
+ XGetErrorDatabaseText(dpy, "XRequest", buffer, "", minor_str, 256);
+ } else {
+ extensionName = "Uknown extension";
+ qsnprintf(minor_str, 256, "Unknown request");
+ }
+ qWarning( "X Error: %s %d\n"
+ " Extension: %d (%s)\n"
+ " Minor opcode: %d (%s)\n"
+ " Resource id: 0x%lx",
+ errstr, err->error_code,
+ err->request_code,
+ extensionName,
+ err->minor_code,
+ minor_str,
+ err->resourceid );
+ }
+
+ // ### we really should distinguish between severe, non-severe and
+ // ### application specific errors
+
+ return 0;
+}
+
+
+static int qt_xio_errhandler(Display *)
+{
+ qWarning("%s: Fatal IO error: client killed", appName);
+ QApplicationPrivate::reset_instance_pointer();
+ exit(1);
+ //### give the application a chance for a proper shutdown instead,
+ //### exit(1) doesn't help.
+ return 0;
+}
+
+#if defined(Q_C_CALLBACKS)
+}
+#endif
+
+
+static void qt_x11_create_intern_atoms()
+{
+ const char *names[QX11Data::NAtoms];
+ const char *ptr = x11_atomnames;
+
+ int i = 0;
+ while (*ptr) {
+ names[i++] = ptr;
+ while (*ptr)
+ ++ptr;
+ ++ptr;
+ }
+
+ Q_ASSERT(i == QX11Data::NPredefinedAtoms);
+
+ QByteArray settings_atom_name("_QT_SETTINGS_TIMESTAMP_");
+ settings_atom_name += XDisplayName(X11->displayName);
+ names[i++] = settings_atom_name;
+
+ Q_ASSERT(i == QX11Data::NAtoms);
+#if defined(XlibSpecificationRelease) && (XlibSpecificationRelease >= 6)
+ XInternAtoms(X11->display, (char **)names, i, False, X11->atoms);
+#else
+ for (i = 0; i < QX11Data::NAtoms; ++i)
+ X11->atoms[i] = XInternAtom(X11->display, (char *)names[i], False);
+#endif
+}
+
+Q_GUI_EXPORT void qt_x11_apply_settings_in_all_apps()
+{
+ QByteArray stamp;
+ QDataStream s(&stamp, QIODevice::WriteOnly);
+ s << QDateTime::currentDateTime();
+
+ XChangeProperty(QX11Info::display(), QX11Info::appRootWindow(0),
+ ATOM(_QT_SETTINGS_TIMESTAMP), ATOM(_QT_SETTINGS_TIMESTAMP), 8,
+ PropModeReplace, (unsigned char *)stamp.data(), stamp.size());
+}
+
+/*! \internal
+ Gets the current KDE 3 or 4 home path
+*/
+QString QApplicationPrivate::kdeHome()
+{
+ static QString kdeHomePath;
+ if (kdeHomePath.isEmpty()) {
+ kdeHomePath = QString::fromLocal8Bit(qgetenv("KDEHOME"));
+ if (kdeHomePath.isEmpty()) {
+ int kdeSessionVersion = QString::fromLocal8Bit(qgetenv("KDE_SESSION_VERSION")).toInt();
+ QDir homeDir(QDir::homePath());
+ QString kdeConfDir(QLatin1String("/.kde"));
+ if (4 == kdeSessionVersion && homeDir.exists(QLatin1String(".kde4")))
+ kdeConfDir = QLatin1String("/.kde4");
+ kdeHomePath = QDir::homePath() + kdeConfDir;
+ }
+ }
+ return kdeHomePath;
+}
+
+/*! \internal
+ apply the settings to the application
+*/
+bool QApplicationPrivate::x11_apply_settings()
+{
+ QSettings settings(QSettings::UserScope, QLatin1String("Trolltech"));
+
+ settings.beginGroup(QLatin1String("Qt"));
+
+ /*
+ Qt settings. This is now they are written into the datastream.
+
+ Palette / * - QPalette
+ font - QFont
+ libraryPath - QStringList
+ style - QString
+ doubleClickInterval - int
+ keyboardInputInterval - int
+ cursorFlashTime - int
+ wheelScrollLines - int
+ colorSpec - QString
+ defaultCodec - QString
+ globalStrut/width - int
+ globalStrut/height - int
+ GUIEffects - QStringList
+ Font Substitutions/ * - QStringList
+ Font Substitutions/... - QStringList
+ */
+
+ QStringList strlist;
+ int i;
+ QPalette pal(Qt::black);
+ int groupCount = 0;
+ strlist = settings.value(QLatin1String("Palette/active")).toStringList();
+ if (!strlist.isEmpty()) {
+ ++groupCount;
+ for (i = 0; i < qMin(strlist.count(), int(QPalette::NColorRoles)); i++)
+ pal.setColor(QPalette::Active, (QPalette::ColorRole) i,
+ QColor(strlist[i]));
+ }
+ strlist = settings.value(QLatin1String("Palette/inactive")).toStringList();
+ if (!strlist.isEmpty()) {
+ ++groupCount;
+ for (i = 0; i < qMin(strlist.count(), int(QPalette::NColorRoles)); i++)
+ pal.setColor(QPalette::Inactive, (QPalette::ColorRole) i,
+ QColor(strlist[i]));
+ }
+ strlist = settings.value(QLatin1String("Palette/disabled")).toStringList();
+ if (!strlist.isEmpty()) {
+ ++groupCount;
+ for (i = 0; i < qMin(strlist.count(), int(QPalette::NColorRoles)); i++)
+ pal.setColor(QPalette::Disabled, (QPalette::ColorRole) i,
+ QColor(strlist[i]));
+ }
+
+ if (groupCount == QPalette::NColorGroups)
+ QApplicationPrivate::setSystemPalette(pal);
+
+ int kdeSessionVersion = QString::fromLocal8Bit(qgetenv("KDE_SESSION_VERSION")).toInt();
+
+ if (!appFont) {
+ QFont font(QApplication::font());
+ QString fontDescription;
+ // Override Qt font if KDE4 settings can be used
+ if (4 == kdeSessionVersion) {
+ QSettings kdeSettings(kdeHome() + QLatin1String("/share/config/kdeglobals"), QSettings::IniFormat);
+ fontDescription = kdeSettings.value(QLatin1String("font")).toString();
+ if (fontDescription.isEmpty()) {
+ // KDE stores fonts without quotes
+ fontDescription = kdeSettings.value(QLatin1String("font")).toStringList().join(QLatin1String(","));
+ }
+ }
+ if (fontDescription.isEmpty())
+ fontDescription = settings.value(QLatin1String("font")).toString();
+ if (!fontDescription .isEmpty()) {
+ font.fromString(fontDescription );
+ QApplicationPrivate::setSystemFont(font);
+ }
+ }
+
+ // read library (ie. plugin) path list
+ QString libpathkey =
+ QString::fromLatin1("%1.%2/libraryPath")
+ .arg(QT_VERSION >> 16)
+ .arg((QT_VERSION & 0xff00) >> 8);
+ QStringList pathlist = settings.value(libpathkey).toString().split(QLatin1Char(':'));
+ if (! pathlist.isEmpty()) {
+ QStringList::ConstIterator it = pathlist.constBegin();
+ while (it != pathlist.constEnd())
+ QApplication::addLibraryPath(*it++);
+ }
+
+ // read new QStyle
+ QString stylename = settings.value(QLatin1String("style")).toString();
+ if (stylename.isEmpty() && !QApplicationPrivate::styleOverride && X11->use_xrender) {
+ QStringList availableStyles = QStyleFactory::keys();
+ // Override Qt style if KDE4 settings can be used
+ if (4 == kdeSessionVersion) {
+ QSettings kdeSettings(kdeHome() + QLatin1String("/share/config/kdeglobals"), QSettings::IniFormat);
+ QString kde4Style = kdeSettings.value(QLatin1String("widgetStyle"),
+ QLatin1String("Oxygen")).toString();
+ foreach (const QString &style, availableStyles) {
+ if (style.toLower() == kde4Style.toLower())
+ stylename = kde4Style;
+ }
+ // Set QGtkStyle for GNOME
+ } else if (X11->desktopEnvironment == DE_GNOME) {
+ QString gtkStyleKey = QString::fromLatin1("GTK+");
+ if (availableStyles.contains(gtkStyleKey))
+ stylename = gtkStyleKey;
+ }
+ }
+
+ if (QCoreApplication::startingUp()) {
+ if (!stylename.isEmpty() && !QApplicationPrivate::styleOverride)
+ QApplicationPrivate::styleOverride = new QString(stylename);
+ } else {
+ QApplication::setStyle(stylename);
+ }
+
+ int num =
+ settings.value(QLatin1String("doubleClickInterval"),
+ QApplication::doubleClickInterval()).toInt();
+ QApplication::setDoubleClickInterval(num);
+
+ num =
+ settings.value(QLatin1String("cursorFlashTime"),
+ QApplication::cursorFlashTime()).toInt();
+ QApplication::setCursorFlashTime(num);
+
+ num =
+ settings.value(QLatin1String("wheelScrollLines"),
+ QApplication::wheelScrollLines()).toInt();
+ QApplication::setWheelScrollLines(num);
+
+ QString colorspec = settings.value(QLatin1String("colorSpec"),
+ QVariant(QLatin1String("default"))).toString();
+ if (colorspec == QLatin1String("normal"))
+ QApplication::setColorSpec(QApplication::NormalColor);
+ else if (colorspec == QLatin1String("custom"))
+ QApplication::setColorSpec(QApplication::CustomColor);
+ else if (colorspec == QLatin1String("many"))
+ QApplication::setColorSpec(QApplication::ManyColor);
+ else if (colorspec != QLatin1String("default"))
+ colorspec = QLatin1String("default");
+
+ QString defaultcodec = settings.value(QLatin1String("defaultCodec"),
+ QVariant(QLatin1String("none"))).toString();
+ if (defaultcodec != QLatin1String("none")) {
+ QTextCodec *codec = QTextCodec::codecForName(defaultcodec.toLatin1());
+ if (codec)
+ QTextCodec::setCodecForTr(codec);
+ }
+
+ int w = settings.value(QLatin1String("globalStrut/width")).toInt();
+ int h = settings.value(QLatin1String("globalStrut/height")).toInt();
+ QSize strut(w, h);
+ if (strut.isValid())
+ QApplication::setGlobalStrut(strut);
+
+ QStringList effects = settings.value(QLatin1String("GUIEffects")).toStringList();
+ QApplication::setEffectEnabled(Qt::UI_General,
+ effects.contains(QLatin1String("general")));
+ QApplication::setEffectEnabled(Qt::UI_AnimateMenu,
+ effects.contains(QLatin1String("animatemenu")));
+ QApplication::setEffectEnabled(Qt::UI_FadeMenu,
+ effects.contains(QLatin1String("fademenu")));
+ QApplication::setEffectEnabled(Qt::UI_AnimateCombo,
+ effects.contains(QLatin1String("animatecombo")));
+ QApplication::setEffectEnabled(Qt::UI_AnimateTooltip,
+ effects.contains(QLatin1String("animatetooltip")));
+ QApplication::setEffectEnabled(Qt::UI_FadeTooltip,
+ effects.contains(QLatin1String("fadetooltip")));
+ QApplication::setEffectEnabled(Qt::UI_AnimateToolBox,
+ effects.contains(QLatin1String("animatetoolbox")));
+
+ if (!X11->has_fontconfig) {
+ settings.beginGroup(QLatin1String("Font Substitutions"));
+ QStringList fontsubs = settings.childKeys();
+ if (!fontsubs.isEmpty()) {
+ QStringList::Iterator it = fontsubs.begin();
+ for (; it != fontsubs.end(); ++it) {
+ QString fam = *it;
+ QStringList subs = settings.value(fam).toStringList();
+ QFont::insertSubstitutions(fam, subs);
+ }
+ }
+ settings.endGroup();
+ }
+
+ qt_use_rtl_extensions =
+ settings.value(QLatin1String("useRtlExtensions"), false).toBool();
+
+#ifndef QT_NO_XIM
+ if (qt_xim_preferred_style == 0) {
+ QString ximInputStyle = settings.value(QLatin1String("XIMInputStyle"),
+ QVariant(QLatin1String("on the spot"))).toString().toLower();
+ if (ximInputStyle == QLatin1String("on the spot"))
+ qt_xim_preferred_style = XIMPreeditCallbacks | XIMStatusNothing;
+ else if (ximInputStyle == QLatin1String("over the spot"))
+ qt_xim_preferred_style = XIMPreeditPosition | XIMStatusNothing;
+ else if (ximInputStyle == QLatin1String("off the spot"))
+ qt_xim_preferred_style = XIMPreeditArea | XIMStatusArea;
+ else if (ximInputStyle == QLatin1String("root"))
+ qt_xim_preferred_style = XIMPreeditNothing | XIMStatusNothing;
+ }
+#endif
+ QStringList inputMethods = QInputContextFactory::keys();
+ if (inputMethods.size() > 2 && inputMethods.contains(QLatin1String("imsw-multi"))) {
+ X11->default_im = QLatin1String("imsw-multi");
+ } else {
+ X11->default_im = settings.value(QLatin1String("DefaultInputMethod"),
+ QLatin1String("xim")).toString();
+ }
+
+ settings.endGroup(); // Qt
+
+ return true;
+}
+
+
+/*! \internal
+ Resets the QApplication::instance() pointer to zero
+*/
+void QApplicationPrivate::reset_instance_pointer()
+{ QApplication::self = 0; }
+
+
+// read the _QT_INPUT_ENCODING property and apply the settings to
+// the application
+static void qt_set_input_encoding()
+{
+ Atom type;
+ int format;
+ ulong nitems, after = 1;
+ unsigned char *data = 0;
+
+ int e = XGetWindowProperty(X11->display, QX11Info::appRootWindow(),
+ ATOM(_QT_INPUT_ENCODING), 0, 1024,
+ False, XA_STRING, &type, &format, &nitems,
+ &after, &data);
+ if (e != Success || !nitems || type == XNone) {
+ // Always use the locale codec, since we have no examples of non-local
+ // XIMs, and since we cannot get a sensible answer about the encoding
+ // from the XIM.
+ qt_input_mapper = QTextCodec::codecForLocale();
+
+ } else {
+ if (!qstricmp((char *)data, "locale"))
+ qt_input_mapper = QTextCodec::codecForLocale();
+ else
+ qt_input_mapper = QTextCodec::codecForName((char *)data);
+ // make sure we have an input codec
+ if(!qt_input_mapper)
+ qt_input_mapper = QTextCodec::codecForName("ISO 8859-1");
+ }
+ if (qt_input_mapper && qt_input_mapper->mibEnum() == 11) // 8859-8
+ qt_input_mapper = QTextCodec::codecForName("ISO 8859-8-I");
+ if(data)
+ XFree((char *)data);
+}
+
+// Reads a KDE color setting
+static QColor kdeColor(const QString &key)
+{
+ QSettings kdeSettings(QApplicationPrivate::kdeHome() +
+ QLatin1String("/share/config/kdeglobals"), QSettings::IniFormat);
+ QVariant variant = kdeSettings.value(key);
+ if (variant.isValid()) {
+ QStringList values = variant.toStringList();
+ if (values.size() == 3) {
+ int r = values[0].toInt();
+ int g = values[1].toInt();
+ int b = values[2].toInt();
+ return QColor(r, g, b);
+ }
+ }
+ return QColor();
+}
+
+// set font, foreground and background from x11 resources. The
+// arguments may override the resource settings.
+static void qt_set_x11_resources(const char* font = 0, const char* fg = 0,
+ const char* bg = 0, const char* button = 0)
+{
+
+ QString resFont, resFG, resBG, resButton, resEF, sysFont, selectBackground, selectForeground;
+
+ QApplication::setEffectEnabled(Qt::UI_General, false);
+ QApplication::setEffectEnabled(Qt::UI_AnimateMenu, false);
+ QApplication::setEffectEnabled(Qt::UI_FadeMenu, false);
+ QApplication::setEffectEnabled(Qt::UI_AnimateCombo, false);
+ QApplication::setEffectEnabled(Qt::UI_AnimateTooltip, false);
+ QApplication::setEffectEnabled(Qt::UI_FadeTooltip, false);
+ QApplication::setEffectEnabled(Qt::UI_AnimateToolBox, false);
+
+ bool paletteAlreadySet = false;
+ if (QApplication::desktopSettingsAware()) {
+ // first, read from settings
+ QApplicationPrivate::x11_apply_settings();
+
+ // the call to QApplication::style() below creates the system
+ // palette, which breaks the logic after the RESOURCE_MANAGER
+ // loop... so I have to save this value to be able to use it later
+ paletteAlreadySet = (QApplicationPrivate::sys_pal != 0);
+
+ // second, parse the RESOURCE_MANAGER property
+ int format;
+ ulong nitems, after = 1;
+ QString res;
+ long offset = 0;
+ Atom type = XNone;
+
+ while (after > 0) {
+ uchar *data = 0;
+ if (XGetWindowProperty(X11->display, QX11Info::appRootWindow(0),
+ ATOM(RESOURCE_MANAGER),
+ offset, 8192, False, AnyPropertyType,
+ &type, &format, &nitems, &after,
+ &data) != Success) {
+ res = QString();
+ break;
+ }
+ if (type == XA_STRING)
+ res += QString::fromLatin1((char*)data);
+ else
+ res += QString::fromLocal8Bit((char*)data);
+ offset += 2048; // offset is in 32bit quantities... 8192/4 == 2048
+ if (data)
+ XFree((char *)data);
+ }
+
+ QString key, value;
+ int l = 0, r;
+ QString apn = QString::fromLocal8Bit(appName);
+ QString apc = QString::fromLocal8Bit(appClass);
+ int apnl = apn.length();
+ int apcl = apc.length();
+ int resl = res.length();
+
+ while (l < resl) {
+ r = res.indexOf(QLatin1Char('\n'), l);
+ if (r < 0)
+ r = resl;
+ while (res.at(l).isSpace())
+ l++;
+ bool mine = false;
+ QChar sc = res.at(l + 1);
+ if (res.at(l) == QLatin1Char('*') &&
+ (sc == QLatin1Char('f') || sc == QLatin1Char('b') || sc == QLatin1Char('g') ||
+ sc == QLatin1Char('F') || sc == QLatin1Char('B') || sc == QLatin1Char('G') ||
+ sc == QLatin1Char('s') || sc == QLatin1Char('S')
+ // capital T only, since we're looking for "Text.selectSomething"
+ || sc == QLatin1Char('T'))) {
+ // OPTIMIZED, since we only want "*[fbgsT].."
+ QString item = res.mid(l, r - l).simplified();
+ int i = item.indexOf(QLatin1Char(':'));
+ key = item.left(i).trimmed().mid(1).toLower();
+ value = item.right(item.length() - i - 1).trimmed();
+ mine = true;
+ } else if ((apnl && res.at(l) == apn.at(0)) || (appClass && apcl && res.at(l) == apc.at(0))) {
+ if (res.mid(l,apnl) == apn && (res.at(l+apnl) == QLatin1Char('.')
+ || res.at(l+apnl) == QLatin1Char('*'))) {
+ QString item = res.mid(l, r - l).simplified();
+ int i = item.indexOf(QLatin1Char(':'));
+ key = item.left(i).trimmed().mid(apnl+1).toLower();
+ value = item.right(item.length() - i - 1).trimmed();
+ mine = true;
+ } else if (res.mid(l,apcl) == apc && (res.at(l+apcl) == QLatin1Char('.')
+ || res.at(l+apcl) == QLatin1Char('*'))) {
+ QString item = res.mid(l, r - l).simplified();
+ int i = item.indexOf(QLatin1Char(':'));
+ key = item.left(i).trimmed().mid(apcl+1).toLower();
+ value = item.right(item.length() - i - 1).trimmed();
+ mine = true;
+ }
+ }
+
+ if (mine) {
+ if (!font && key == QLatin1String("systemfont"))
+ sysFont = value.left(value.lastIndexOf(QLatin1Char(':')));
+ if (!font && key == QLatin1String("font"))
+ resFont = value;
+ else if (!fg && !paletteAlreadySet) {
+ if (key == QLatin1String("foreground"))
+ resFG = value;
+ else if (!bg && key == QLatin1String("background"))
+ resBG = value;
+ else if (!bg && !button && key == QLatin1String("button.background"))
+ resButton = value;
+ else if (key == QLatin1String("text.selectbackground")) {
+ selectBackground = value;
+ } else if (key == QLatin1String("text.selectforeground")) {
+ selectForeground = value;
+ }
+ } else if (key == QLatin1String("guieffects"))
+ resEF = value;
+ // NOTE: if you add more, change the [fbg] stuff above
+ }
+
+ l = r + 1;
+ }
+ }
+ if (!sysFont.isEmpty())
+ resFont = sysFont;
+ if (resFont.isEmpty())
+ resFont = QString::fromLocal8Bit(font);
+ if (resFG.isEmpty())
+ resFG = QString::fromLocal8Bit(fg);
+ if (resBG.isEmpty())
+ resBG = QString::fromLocal8Bit(bg);
+ if (resButton.isEmpty())
+ resButton = QString::fromLocal8Bit(button);
+ if (!resFont.isEmpty()
+ && !X11->has_fontconfig
+ && !QApplicationPrivate::sys_font) {
+ // set application font
+ QFont fnt;
+ fnt.setRawName(resFont);
+
+ // the font we get may actually be an alias for another font,
+ // so we reset the application font to the real font info.
+ if (! fnt.exactMatch()) {
+ QFontInfo fontinfo(fnt);
+ fnt.setFamily(fontinfo.family());
+ fnt.setRawMode(fontinfo.rawMode());
+
+ if (! fnt.rawMode()) {
+ fnt.setItalic(fontinfo.italic());
+ fnt.setWeight(fontinfo.weight());
+ fnt.setUnderline(fontinfo.underline());
+ fnt.setStrikeOut(fontinfo.strikeOut());
+ fnt.setStyleHint(fontinfo.styleHint());
+
+ if (fnt.pointSize() <= 0 && fnt.pixelSize() <= 0) {
+ // size is all wrong... fix it
+ qreal pointSize = fontinfo.pixelSize() * 72. / (float) QX11Info::appDpiY();
+ if (pointSize <= 0)
+ pointSize = 12;
+ fnt.setPointSize(qRound(pointSize));
+ }
+ }
+ }
+
+ QApplicationPrivate::setSystemFont(fnt);
+ }
+ bool kdeColors = (QApplication::desktopSettingsAware() && X11->desktopEnvironment == DE_KDE);
+
+ if (kdeColors || (button || !resBG.isEmpty() || !resFG.isEmpty())) {// set app colors
+ bool allowX11ColorNames = QColor::allowX11ColorNames();
+ QColor::setAllowX11ColorNames(true);
+
+ (void) QApplication::style(); // trigger creation of application style and system palettes
+ QColor btn;
+ QColor bg;
+ QColor fg;
+ QColor bfg;
+ QColor wfg;
+ if (!resBG.isEmpty())
+ bg = QColor(resBG);
+ if (!bg.isValid())
+ bg = QApplicationPrivate::sys_pal->color(QPalette::Active, QPalette::Window);
+
+ if (!resFG.isEmpty())
+ fg = QColor(resFG);
+ if (!fg.isValid())
+ fg = QApplicationPrivate::sys_pal->color(QPalette::Active, QPalette::WindowText);
+
+ if (!resButton.isEmpty())
+ btn = QColor(resButton);
+ else if (!resBG.isEmpty())
+ btn = bg;
+ if (!btn.isValid())
+ btn = QApplicationPrivate::sys_pal->color(QPalette::Active, QPalette::Button);
+
+ int h,s,v;
+ fg.getHsv(&h,&s,&v);
+ QColor base = Qt::white;
+ bool bright_mode = false;
+ if (v >= 255 - 50) {
+ base = btn.darker(150);
+ bright_mode = true;
+ }
+
+ if (kdeColors) {
+ // Setup KDE palette
+ QColor color;
+ color = kdeColor(QLatin1String("buttonBackground"));
+ if (!color.isValid())
+ color = kdeColor(QLatin1String("Colors:Button/BackgroundNormal"));
+ if (color.isValid())
+ btn = color;
+
+ color = kdeColor(QLatin1String("background"));
+ if (!color.isValid())
+ color = kdeColor(QLatin1String("Colors:Window/BackgroundNormal"));
+ if (color.isValid())
+ bg = color;
+
+ color = kdeColor(QLatin1String("foreground"));
+ if (!color.isValid())
+ color = kdeColor(QLatin1String("Colors:View/ForegroundNormal"));
+ if (color.isValid()) {
+ fg = color;
+ }
+
+ color = kdeColor(QLatin1String("windowForeground"));
+ if (!color.isValid())
+ color = kdeColor(QLatin1String("Colors:Window/ForegroundNormal"));
+ if (color.isValid())
+ wfg = color;
+
+ color = kdeColor(QLatin1String("windowBackground"));
+ if (!color.isValid())
+ color = kdeColor(QLatin1String("Colors:View/BackgroundNormal"));
+ if (color.isValid())
+ base = color;
+ }
+
+ QPalette pal(fg, btn, btn.lighter(125), btn.darker(130), btn.darker(120), wfg.isValid() ? wfg : fg, Qt::white, base, bg);
+ QColor disabled((fg.red() + btn.red()) / 2,
+ (fg.green() + btn.green())/ 2,
+ (fg.blue() + btn.blue()) / 2);
+ pal.setColorGroup(QPalette::Disabled, disabled, btn, btn.lighter(125),
+ btn.darker(130), btn.darker(150), disabled, Qt::white, Qt::white, bg);
+
+ QColor highlight, highlightText;
+ if (!selectBackground.isEmpty() && !selectForeground.isEmpty()) {
+ highlight = QColor(selectBackground);
+ highlightText = QColor(selectForeground);
+ }
+ // Use KDE3 or KDE4 color settings if present
+ if (kdeColors) {
+ QColor color = kdeColor(QLatin1String("selectBackground"));
+ if (!color.isValid())
+ color = kdeColor(QLatin1String("Colors:Selection/BackgroundNormal"));
+ if (color.isValid())
+ highlight = color;
+
+ color = kdeColor(QLatin1String("selectForeground"));
+ if (!color.isValid())
+ color = kdeColor(QLatin1String("Colors:Selection/ForegroundNormal"));
+ if (color.isValid())
+ highlightText = color;
+
+ color = kdeColor(QLatin1String("alternateBackground"));
+ if (!color.isValid())
+ color = kdeColor(QLatin1String("Colors:View/BackgroundAlternate"));
+ if (color.isValid())
+ pal.setColor(QPalette::AlternateBase, color);
+ else
+ pal.setBrush(QPalette::AlternateBase, pal.base().color().darker(110));
+
+ color = kdeColor(QLatin1String("buttonForeground"));
+ if (!color.isValid())
+ color = kdeColor(QLatin1String("Colors:Button/ForegroundNormal"));
+ if (color.isValid())
+ pal.setColor(QPalette::ButtonText, color);
+ }
+
+ if (highlight.isValid() && highlightText.isValid()) {
+ pal.setColor(QPalette::Highlight, highlight);
+ pal.setColor(QPalette::HighlightedText, highlightText);
+
+ // calculate disabled colors by removing saturation
+ highlight.setHsv(highlight.hue(), 0, highlight.value(), highlight.alpha());
+ highlightText.setHsv(highlightText.hue(), 0, highlightText.value(), highlightText.alpha());
+ pal.setColor(QPalette::Disabled, QPalette::Highlight, highlight);
+ pal.setColor(QPalette::Disabled, QPalette::HighlightedText, highlightText);
+ } else if (bright_mode) {
+ pal.setColor(QPalette::HighlightedText, base);
+ pal.setColor(QPalette::Highlight, Qt::white);
+ pal.setColor(QPalette::Disabled, QPalette::HighlightedText, base);
+ pal.setColor(QPalette::Disabled, QPalette::Highlight, Qt::white);
+ } else {
+ pal.setColor(QPalette::HighlightedText, Qt::white);
+ pal.setColor(QPalette::Highlight, Qt::darkBlue);
+ pal.setColor(QPalette::Disabled, QPalette::HighlightedText, Qt::white);
+ pal.setColor(QPalette::Disabled, QPalette::Highlight, Qt::darkBlue);
+ }
+
+ QApplicationPrivate::setSystemPalette(pal);
+
+ QColor::setAllowX11ColorNames(allowX11ColorNames);
+ }
+
+ if (!resEF.isEmpty()) {
+ QStringList effects = resEF.split(QLatin1Char(' '));
+ QApplication::setEffectEnabled(Qt::UI_General, effects.contains(QLatin1String("general")));
+ QApplication::setEffectEnabled(Qt::UI_AnimateMenu,
+ effects.contains(QLatin1String("animatemenu")));
+ QApplication::setEffectEnabled(Qt::UI_FadeMenu,
+ effects.contains(QLatin1String("fademenu")));
+ QApplication::setEffectEnabled(Qt::UI_AnimateCombo,
+ effects.contains(QLatin1String("animatecombo")));
+ QApplication::setEffectEnabled(Qt::UI_AnimateTooltip,
+ effects.contains(QLatin1String("animatetooltip")));
+ QApplication::setEffectEnabled(Qt::UI_FadeTooltip,
+ effects.contains(QLatin1String("fadetooltip")));
+ QApplication::setEffectEnabled(Qt::UI_AnimateToolBox,
+ effects.contains(QLatin1String("animatetoolbox")));
+ }
+}
+
+
+// update the supported array
+static void qt_get_net_supported()
+{
+ Atom type;
+ int format;
+ long offset = 0;
+ unsigned long nitems, after;
+ unsigned char *data = 0;
+
+ int e = XGetWindowProperty(X11->display, QX11Info::appRootWindow(),
+ ATOM(_NET_SUPPORTED), 0, 0,
+ False, XA_ATOM, &type, &format, &nitems, &after, &data);
+ if (data)
+ XFree(data);
+
+ if (X11->net_supported_list)
+ delete [] X11->net_supported_list;
+ X11->net_supported_list = 0;
+
+ if (e == Success && type == XA_ATOM && format == 32) {
+ QBuffer ts;
+ ts.open(QIODevice::WriteOnly);
+
+ while (after > 0) {
+ XGetWindowProperty(X11->display, QX11Info::appRootWindow(),
+ ATOM(_NET_SUPPORTED), offset, 1024,
+ False, XA_ATOM, &type, &format, &nitems, &after, &data);
+
+ if (type == XA_ATOM && format == 32) {
+ ts.write(reinterpret_cast<char *>(data), nitems * sizeof(long));
+ offset += nitems;
+ } else
+ after = 0;
+ if (data)
+ XFree(data);
+ }
+
+ // compute nitems
+ QByteArray buffer(ts.buffer());
+ nitems = buffer.size() / sizeof(Atom);
+ X11->net_supported_list = new Atom[nitems + 1];
+ Atom *a = (Atom *) buffer.data();
+ uint i;
+ for (i = 0; i < nitems; i++)
+ X11->net_supported_list[i] = a[i];
+ X11->net_supported_list[nitems] = 0;
+ }
+}
+
+
+bool QX11Data::isSupportedByWM(Atom atom)
+{
+ if (!X11->net_supported_list)
+ return false;
+
+ bool supported = false;
+ int i = 0;
+ while (X11->net_supported_list[i] != 0) {
+ if (X11->net_supported_list[i++] == atom) {
+ supported = true;
+ break;
+ }
+ }
+
+ return supported;
+}
+
+
+// update the virtual roots array
+static void qt_get_net_virtual_roots()
+{
+ if (X11->net_virtual_root_list)
+ delete [] X11->net_virtual_root_list;
+ X11->net_virtual_root_list = 0;
+
+ if (!X11->isSupportedByWM(ATOM(_NET_VIRTUAL_ROOTS)))
+ return;
+
+ Atom type;
+ int format;
+ long offset = 0;
+ unsigned long nitems, after;
+ unsigned char *data;
+
+ int e = XGetWindowProperty(X11->display, QX11Info::appRootWindow(),
+ ATOM(_NET_VIRTUAL_ROOTS), 0, 0,
+ False, XA_ATOM, &type, &format, &nitems, &after, &data);
+ if (data)
+ XFree(data);
+
+ if (e == Success && type == XA_ATOM && format == 32) {
+ QBuffer ts;
+ ts.open(QIODevice::WriteOnly);
+
+ while (after > 0) {
+ XGetWindowProperty(X11->display, QX11Info::appRootWindow(),
+ ATOM(_NET_VIRTUAL_ROOTS), offset, 1024,
+ False, XA_ATOM, &type, &format, &nitems, &after, &data);
+
+ if (type == XA_ATOM && format == 32) {
+ ts.write(reinterpret_cast<char *>(data), nitems * 4);
+ offset += nitems;
+ } else
+ after = 0;
+ if (data)
+ XFree(data);
+ }
+
+ // compute nitems
+ QByteArray buffer(ts.buffer());
+ nitems = buffer.size() / sizeof(Window);
+ X11->net_virtual_root_list = new Window[nitems + 1];
+ Window *a = (Window *) buffer.data();
+ uint i;
+ for (i = 0; i < nitems; i++)
+ X11->net_virtual_root_list[i] = a[i];
+ X11->net_virtual_root_list[nitems] = 0;
+ }
+}
+
+void qt_net_remove_user_time(QWidget *tlw)
+{
+ Q_ASSERT(tlw);
+ QTLWExtra *extra = tlw->d_func()->maybeTopData();
+ if (extra && extra->userTimeWindow) {
+ Q_ASSERT(tlw->internalWinId());
+ XDeleteProperty(X11->display, tlw->internalWinId(), ATOM(_NET_WM_USER_TIME_WINDOW));
+ XDestroyWindow(X11->display, extra->userTimeWindow);
+ extra->userTimeWindow = 0;
+ }
+}
+
+void qt_net_update_user_time(QWidget *tlw, unsigned long timestamp)
+{
+ Q_ASSERT(tlw);
+ Q_ASSERT(tlw->isWindow());
+ Q_ASSERT(tlw->testAttribute(Qt::WA_WState_Created));
+ QTLWExtra *extra = tlw->d_func()->topData();
+ WId wid = tlw->internalWinId();
+ const bool isSupportedByWM = X11->isSupportedByWM(ATOM(_NET_WM_USER_TIME_WINDOW));
+ if (extra->userTimeWindow || isSupportedByWM) {
+ if (!extra->userTimeWindow) {
+ extra->userTimeWindow = XCreateSimpleWindow(X11->display,
+ tlw->internalWinId(),
+ -1, -1, 1, 1, 0, 0, 0);
+ wid = extra->userTimeWindow;
+ XChangeProperty(X11->display, tlw->internalWinId(), ATOM(_NET_WM_USER_TIME_WINDOW),
+ XA_WINDOW, 32, PropModeReplace,
+ (unsigned char *)&wid, 1);
+ XDeleteProperty(X11->display, tlw->internalWinId(), ATOM(_NET_WM_USER_TIME));
+ } else if (!isSupportedByWM) {
+ // WM no longer supports it, then we should remove the
+ // _NET_WM_USER_TIME_WINDOW atom.
+ qt_net_remove_user_time(tlw);
+ } else {
+ wid = extra->userTimeWindow;
+ }
+ }
+ XChangeProperty(X11->display, wid, ATOM(_NET_WM_USER_TIME),
+ XA_CARDINAL, 32, PropModeReplace, (unsigned char *) &timestamp, 1);
+}
+
+static void qt_check_focus_model()
+{
+ Window fw = XNone;
+ int unused;
+ XGetInputFocus(X11->display, &fw, &unused);
+ if (fw == PointerRoot)
+ X11->focus_model = QX11Data::FM_PointerRoot;
+ else
+ X11->focus_model = QX11Data::FM_Other;
+}
+
+#ifndef QT_NO_TABLET
+
+#if !defined (Q_OS_IRIX)
+// from include/Xwacom.h
+# define XWACOM_PARAM_TOOLID 322
+# define XWACOM_PARAM_TOOLSERIAL 323
+
+typedef WACOMCONFIG * (*PtrWacomConfigInit) (Display*, WACOMERRORFUNC);
+typedef WACOMDEVICE * (*PtrWacomConfigOpenDevice) (WACOMCONFIG*, const char*);
+typedef int *(*PtrWacomConfigGetRawParam) (WACOMDEVICE*, int, int*, int, unsigned*);
+typedef int (*PtrWacomConfigCloseDevice) (WACOMDEVICE *);
+typedef void (*PtrWacomConfigTerm) (WACOMCONFIG *);
+
+static PtrWacomConfigInit ptrWacomConfigInit = 0;
+static PtrWacomConfigOpenDevice ptrWacomConfigOpenDevice = 0;
+static PtrWacomConfigGetRawParam ptrWacomConfigGetRawParam = 0;
+static PtrWacomConfigCloseDevice ptrWacomConfigCloseDevice = 0;
+static PtrWacomConfigTerm ptrWacomConfigTerm = 0;
+#endif
+
+#endif
+
+/*****************************************************************************
+ qt_init() - initializes Qt for X11
+ *****************************************************************************/
+
+#if !defined(QT_NO_FONTCONFIG)
+static void getXDefault(const char *group, const char *key, int *val)
+{
+ char *str = XGetDefault(X11->display, group, key);
+ if (str) {
+ char *end = 0;
+ int v = strtol(str, &end, 0);
+ if (str != end)
+ *val = v;
+ }
+}
+
+static void getXDefault(const char *group, const char *key, double *val)
+{
+ char *str = XGetDefault(X11->display, group, key);
+ if (str) {
+ bool ok;
+ double v = QByteArray(str).toDouble(&ok);
+ if (ok)
+ *val = v;
+ }
+}
+
+static void getXDefault(const char *group, const char *key, bool *val)
+{
+ char *str = XGetDefault(X11->display, group, key);
+ if (str) {
+ char c = str[0];
+ if (isupper((int)c))
+ c = tolower(c);
+ if (c == 't' || c == 'y' || c == '1')
+ *val = true;
+ else if (c == 'f' || c == 'n' || c == '0')
+ *val = false;
+ if (c == 'o') {
+ c = str[1];
+ if (isupper((int)c))
+ c = tolower(c);
+ if (c == 'n')
+ *val = true;
+ if (c == 'f')
+ *val = false;
+ }
+ }
+}
+#endif
+
+// ### This should be static but it isn't because of the friend declaration
+// ### in qpaintdevice.h which then should have a static too but can't have
+// ### it because "storage class specifiers invalid in friend function
+// ### declarations" :-) Ideas anyone?
+void qt_init(QApplicationPrivate *priv, int,
+ Display *display, Qt::HANDLE visual, Qt::HANDLE colormap)
+{
+ X11 = new QX11Data;
+ X11->display = display;
+ X11->displayName = 0;
+ X11->foreignDisplay = (display != 0);
+ X11->focus_model = -1;
+
+ // RANDR
+ X11->use_xrandr = false;
+ X11->xrandr_major = 0;
+ X11->xrandr_eventbase = 0;
+ X11->xrandr_errorbase = 0;
+
+ // RENDER
+ X11->use_xrender = false;
+ X11->xrender_major = 0;
+ X11->xrender_version = 0;
+
+ // XFIXES
+ X11->use_xfixes = false;
+ X11->xfixes_major = 0;
+ X11->xfixes_eventbase = 0;
+ X11->xfixes_errorbase = 0;
+
+ // XInputExtension
+ X11->use_xinput = false;
+ X11->xinput_major = 0;
+ X11->xinput_eventbase = 0;
+ X11->xinput_errorbase = 0;
+
+ // MIT-SHM
+ X11->use_mitshm = false;
+ X11->mitshm_major = 0;
+
+ X11->sip_serial = 0;
+ X11->net_supported_list = 0;
+ X11->net_virtual_root_list = 0;
+ X11->wm_client_leader = 0;
+ X11->screens = 0;
+ X11->argbVisuals = 0;
+ X11->argbColormaps = 0;
+ X11->screenCount = 0;
+ X11->time = CurrentTime;
+ X11->userTime = CurrentTime;
+ X11->ignore_badwindow = false;
+ X11->seen_badwindow = false;
+
+ X11->motifdnd_active = false;
+
+ X11->default_im = QLatin1String("imsw-multi");
+ priv->inputContext = 0;
+
+ // colormap control
+ X11->visual_class = -1;
+ X11->visual_id = -1;
+ X11->color_count = 0;
+ X11->custom_cmap = false;
+
+ // outside visual/colormap
+ X11->visual = reinterpret_cast<Visual *>(visual);
+ X11->colormap = colormap;
+
+ // Fontconfig
+ X11->has_fontconfig = false;
+#if !defined(QT_NO_FONTCONFIG)
+ if (qgetenv("QT_X11_NO_FONTCONFIG").isNull())
+ X11->has_fontconfig = FcInit();
+ X11->fc_antialias = true;
+#endif
+
+#ifndef QT_NO_XRENDER
+ memset(X11->solid_fills, 0, sizeof(X11->solid_fills));
+ for (int i = 0; i < X11->solid_fill_count; ++i)
+ X11->solid_fills[i].screen = -1;
+ memset(X11->pattern_fills, 0, sizeof(X11->pattern_fills));
+ for (int i = 0; i < X11->pattern_fill_count; ++i)
+ X11->pattern_fills[i].screen = -1;
+#endif
+
+ X11->startupId = X11->originalStartupId = 0;
+
+ int argc = priv->argc;
+ char **argv = priv->argv;
+
+ if (X11->display) {
+ // Qt part of other application
+
+ // Set application name and class
+ appName = qstrdup("Qt-subapplication");
+ char *app_class = 0;
+ if (argv) {
+ const char* p = strrchr(argv[0], '/');
+ app_class = qstrdup(p ? p + 1 : argv[0]);
+ if (app_class[0])
+ app_class[0] = toupper(app_class[0]);
+ }
+ appClass = app_class;
+ } else {
+ // Qt controls everything (default)
+
+ // Set application name and class
+ char *app_class = 0;
+ if (argv && argv[0]) {
+ const char *p = strrchr(argv[0], '/');
+ appName = p ? p + 1 : argv[0];
+ app_class = qstrdup(appName);
+ if (app_class[0])
+ app_class[0] = toupper(app_class[0]);
+ }
+ appClass = app_class;
+ }
+
+ // Install default error handlers
+ original_x_errhandler = XSetErrorHandler(qt_x_errhandler);
+ original_xio_errhandler = XSetIOErrorHandler(qt_xio_errhandler);
+
+ // Get command line params
+ int j = argc ? 1 : 0;
+ for (int i=1; i<argc; i++) {
+ if (argv[i] && *argv[i] != '-') {
+ argv[j++] = argv[i];
+ continue;
+ }
+ QByteArray arg(argv[i]);
+ if (arg == "-display") {
+ if (++i < argc && !X11->display)
+ X11->displayName = argv[i];
+ } else if (arg == "-fn" || arg == "-font") {
+ if (++i < argc)
+ appFont = argv[i];
+ } else if (arg == "-bg" || arg == "-background") {
+ if (++i < argc)
+ appBGCol = argv[i];
+ } else if (arg == "-btn" || arg == "-button") {
+ if (++i < argc)
+ appBTNCol = argv[i];
+ } else if (arg == "-fg" || arg == "-foreground") {
+ if (++i < argc)
+ appFGCol = argv[i];
+ } else if (arg == "-name") {
+ if (++i < argc)
+ appName = argv[i];
+ } else if (arg == "-title") {
+ if (++i < argc)
+ mwTitle = argv[i];
+ } else if (arg == "-geometry") {
+ if (++i < argc)
+ mwGeometry = argv[i];
+ } else if (arg == "-im") {
+ if (++i < argc)
+ qt_ximServer = argv[i];
+ } else if (arg == "-ncols") { // xv and netscape use this name
+ if (++i < argc)
+ X11->color_count = qMax(0,atoi(argv[i]));
+ } else if (arg == "-visual") { // xv and netscape use this name
+ if (++i < argc && !X11->visual) {
+ QString s = QString::fromLocal8Bit(argv[i]).toLower();
+ if (s == QLatin1String("staticgray"))
+ X11->visual_class = StaticGray;
+ else if (s == QLatin1String("grayscale"))
+ X11->visual_class = XGrayScale;
+ else if (s == QLatin1String("staticcolor"))
+ X11->visual_class = StaticColor;
+ else if (s == QLatin1String("pseudocolor"))
+ X11->visual_class = PseudoColor;
+ else if (s == QLatin1String("truecolor"))
+ X11->visual_class = TrueColor;
+ else if (s == QLatin1String("directcolor"))
+ X11->visual_class = DirectColor;
+ else
+ X11->visual_id = static_cast<int>(strtol(argv[i], 0, 0));
+ }
+#ifndef QT_NO_XIM
+ } else if (arg == "-inputstyle") {
+ if (++i < argc) {
+ QString s = QString::fromLocal8Bit(argv[i]).toLower();
+ if (s == QLatin1String("onthespot"))
+ qt_xim_preferred_style = XIMPreeditCallbacks |
+ XIMStatusNothing;
+ else if (s == QLatin1String("overthespot"))
+ qt_xim_preferred_style = XIMPreeditPosition |
+ XIMStatusNothing;
+ else if (s == QLatin1String("offthespot"))
+ qt_xim_preferred_style = XIMPreeditArea |
+ XIMStatusArea;
+ else if (s == QLatin1String("root"))
+ qt_xim_preferred_style = XIMPreeditNothing |
+ XIMStatusNothing;
+ }
+#endif
+ } else if (arg == "-cmap") { // xv uses this name
+ if (!X11->colormap)
+ X11->custom_cmap = true;
+ }
+ else if (arg == "-sync")
+ appSync = !appSync;
+#if defined(QT_DEBUG)
+ else if (arg == "-nograb")
+ appNoGrab = !appNoGrab;
+ else if (arg == "-dograb")
+ appDoGrab = !appDoGrab;
+#endif
+ else
+ argv[j++] = argv[i];
+ }
+
+ priv->argc = j;
+
+#if defined(QT_DEBUG) && defined(Q_OS_LINUX)
+ if (!appNoGrab && !appDoGrab) {
+ QString s;
+ s.sprintf("/proc/%d/cmdline", getppid());
+ QFile f(s);
+ if (f.open(QIODevice::ReadOnly)) {
+ s.clear();
+ char c;
+ while (f.getChar(&c) && c) {
+ if (c == '/')
+ s.clear();
+ else
+ s += QLatin1Char(c);
+ }
+ if (s == QLatin1String("gdb")) {
+ appNoGrab = true;
+ qDebug("Qt: gdb: -nograb added to command-line options.\n"
+ "\t Use the -dograb option to enforce grabbing.");
+ }
+ f.close();
+ }
+ }
+#endif
+
+ // Connect to X server
+ if (qt_is_gui_used && !X11->display) {
+ if ((X11->display = XOpenDisplay(X11->displayName)) == 0) {
+ qWarning("%s: cannot connect to X server %s", appName,
+ XDisplayName(X11->displayName));
+ QApplicationPrivate::reset_instance_pointer();
+ exit(1);
+ }
+
+ if (appSync) // if "-sync" argument
+ XSynchronize(X11->display, true);
+ }
+
+ // Common code, regardless of whether display is foreign.
+
+ // Get X parameters
+
+ if (qt_is_gui_used) {
+ X11->defaultScreen = DefaultScreen(X11->display);
+ X11->screenCount = ScreenCount(X11->display);
+
+ X11->screens = new QX11InfoData[X11->screenCount];
+ X11->argbVisuals = new Visual *[X11->screenCount];
+ X11->argbColormaps = new Colormap[X11->screenCount];
+
+ for (int s = 0; s < X11->screenCount; s++) {
+ QX11InfoData *screen = X11->screens + s;
+ screen->ref = 1; // ensures it doesn't get deleted
+ screen->screen = s;
+ screen->dpiX = (DisplayWidth(X11->display, s) * 254 + DisplayWidthMM(X11->display, s)*5)
+ / (DisplayWidthMM(X11->display, s)*10);
+ screen->dpiY = (DisplayHeight(X11->display, s) * 254 + DisplayHeightMM(X11->display, s)*5)
+ / (DisplayHeightMM(X11->display, s)*10);
+
+ X11->argbVisuals[s] = 0;
+ X11->argbColormaps[s] = 0;
+ }
+
+
+#ifndef QT_NO_XRENDER
+ int xrender_eventbase, xrender_errorbase;
+ // See if XRender is supported on the connected display
+ if (XQueryExtension(X11->display, "RENDER", &X11->xrender_major,
+ &xrender_eventbase, &xrender_errorbase)
+ && XRenderQueryExtension(X11->display, &xrender_eventbase,
+ &xrender_errorbase)) {
+ // Check the version as well - we need v0.4 or higher
+ int major = 0;
+ int minor = 0;
+ XRenderQueryVersion(X11->display, &major, &minor);
+ if (qgetenv("QT_X11_NO_XRENDER").isNull()) {
+ X11->use_xrender = (major >= 0 && minor >= 5);
+ X11->xrender_version = major*100+minor;
+ // workaround for broken XServer on Ubuntu Breezy (6.8 compiled with 7.0
+ // protocol headers)
+ if (X11->xrender_version == 10
+ && VendorRelease(X11->display) < 60900000
+ && QByteArray(ServerVendor(X11->display)).contains("X.Org"))
+ X11->xrender_version = 9;
+ }
+ }
+#endif // QT_NO_XRENDER
+
+#ifndef QT_NO_MITSHM
+ int mitshm_minor;
+ int mitshm_major;
+ int mitshm_eventbase;
+ int mitshm_errorbase;
+ int mitshm_pixmaps;
+ if (XQueryExtension(X11->display, "MIT-SHM", &X11->mitshm_major,
+ &mitshm_eventbase, &mitshm_errorbase)
+ && XShmQueryVersion(X11->display, &mitshm_major, &mitshm_minor,
+ &mitshm_pixmaps))
+ {
+ QString displayName = QLatin1String(XDisplayName(NULL));
+
+ // apparently MITSHM only works for local displays, so do a quick check here
+ // to determine whether the display is local or not (not 100 % accurate)
+ bool local = displayName.isEmpty() || displayName.lastIndexOf(QLatin1Char(':')) == 0;
+ if (local && (qgetenv("QT_X11_NO_MITSHM").toInt() == 0))
+ X11->use_mitshm = mitshm_pixmaps;
+ }
+#endif // QT_NO_MITSHM
+
+ // initialize the graphics system - order is imporant here - it must be done before
+ // the QColormap::initialize() call
+ QApplicationPrivate::graphics_system = QGraphicsSystemFactory::create(QApplicationPrivate::graphics_system_name);
+ QColormap::initialize();
+
+ // Support protocols
+ X11->xdndSetup();
+
+ // Finally create all atoms
+ qt_x11_create_intern_atoms();
+
+ // initialize NET lists
+ qt_get_net_supported();
+ qt_get_net_virtual_roots();
+
+#ifndef QT_NO_XRANDR
+ // See if XRandR is supported on the connected display
+ if (XQueryExtension(X11->display, "RANDR", &X11->xrandr_major,
+ &X11->xrandr_eventbase, &X11->xrandr_errorbase)) {
+
+# ifdef QT_RUNTIME_XRANDR
+ X11->ptrXRRSelectInput = 0;
+ X11->ptrXRRUpdateConfiguration = 0;
+ X11->ptrXRRRootToScreen = 0;
+ X11->ptrXRRQueryExtension = 0;
+ QLibrary xrandrLib(QLatin1String("Xrandr"), 2);
+ if (!xrandrLib.load()) { // try without the version number
+ xrandrLib.setFileName(QLatin1String("Xrandr"));
+ xrandrLib.load();
+ }
+ if (xrandrLib.isLoaded()) {
+ X11->ptrXRRSelectInput =
+ (PtrXRRSelectInput) xrandrLib.resolve("XRRSelectInput");
+ X11->ptrXRRUpdateConfiguration =
+ (PtrXRRUpdateConfiguration) xrandrLib.resolve("XRRUpdateConfiguration");
+ X11->ptrXRRRootToScreen =
+ (PtrXRRRootToScreen) xrandrLib.resolve("XRRRootToScreen");
+ X11->ptrXRRQueryExtension =
+ (PtrXRRQueryExtension) xrandrLib.resolve("XRRQueryExtension");
+ }
+# else
+ X11->ptrXRRSelectInput = XRRSelectInput;
+ X11->ptrXRRUpdateConfiguration = XRRUpdateConfiguration;
+ X11->ptrXRRRootToScreen = XRRRootToScreen;
+ X11->ptrXRRQueryExtension = XRRQueryExtension;
+# endif
+
+ if (X11->ptrXRRQueryExtension
+ && X11->ptrXRRQueryExtension(X11->display, &X11->xrandr_eventbase, &X11->xrandr_errorbase)) {
+ // XRandR is supported
+ X11->use_xrandr = true;
+ }
+ }
+#endif // QT_NO_XRANDR
+
+#ifndef QT_NO_XRENDER
+ if (X11->use_xrender) {
+ // XRender is supported, let's see if we have a PictFormat for the
+ // default visual
+ XRenderPictFormat *format =
+ XRenderFindVisualFormat(X11->display,
+ (Visual *) QX11Info::appVisual(X11->defaultScreen));
+
+ if (!format) {
+ X11->use_xrender = false;
+ }
+ }
+#endif // QT_NO_XRENDER
+
+#ifndef QT_NO_XFIXES
+ // See if Xfixes is supported on the connected display
+ if (XQueryExtension(X11->display, "XFIXES", &X11->xfixes_major,
+ &X11->xfixes_eventbase, &X11->xfixes_errorbase)) {
+ X11->ptrXFixesQueryExtension = XFIXES_LOAD_V1(XFixesQueryExtension);
+ X11->ptrXFixesQueryVersion = XFIXES_LOAD_V1(XFixesQueryVersion);
+ X11->ptrXFixesSetCursorName = XFIXES_LOAD_V2(XFixesSetCursorName);
+ X11->ptrXFixesSelectSelectionInput = XFIXES_LOAD_V2(XFixesSelectSelectionInput);
+
+ if(X11->ptrXFixesQueryExtension && X11->ptrXFixesQueryVersion
+ && X11->ptrXFixesQueryExtension(X11->display, &X11->xfixes_eventbase,
+ &X11->xfixes_errorbase)) {
+ // Xfixes is supported.
+ // Note: the XFixes protocol version is negotiated using QueryVersion.
+ // We supply the highest version we support, the X server replies with
+ // the highest version it supports, but no higher than the version we
+ // asked for. The version sent back is the protocol version the X server
+ // will use to talk us. If this call is removed, the behavior of the
+ // X server when it receives an XFixes request is undefined.
+ int major = 3;
+ int minor = 0;
+ X11->ptrXFixesQueryVersion(X11->display, &major, &minor);
+ X11->use_xfixes = (major >= 1);
+ X11->xfixes_major = major;
+ }
+ }
+ if (X11->use_xfixes && X11->ptrXFixesSelectSelectionInput) {
+ const unsigned long eventMask =
+ XFixesSetSelectionOwnerNotifyMask | XFixesSelectionWindowDestroyNotifyMask | XFixesSelectionClientCloseNotifyMask;
+ X11->ptrXFixesSelectSelectionInput(X11->display, QX11Info::appRootWindow(0),
+ XA_PRIMARY, eventMask);
+ X11->ptrXFixesSelectSelectionInput(X11->display, QX11Info::appRootWindow(0),
+ ATOM(CLIPBOARD), eventMask);
+ }
+#endif // QT_NO_XFIXES
+
+#ifndef QT_NO_XCURSOR
+#ifdef QT_RUNTIME_XCURSOR
+ X11->ptrXcursorLibraryLoadCursor = 0;
+ QLibrary xcursorLib(QLatin1String("Xcursor"), 1);
+ bool xcursorFound = xcursorLib.load();
+ if (!xcursorFound) { //try without the version number
+ xcursorLib.setFileName(QLatin1String("Xcursor"));
+ xcursorFound = xcursorLib.load();
+ }
+ if (xcursorFound) {
+ X11->ptrXcursorLibraryLoadCursor =
+ (PtrXcursorLibraryLoadCursor) xcursorLib.resolve("XcursorLibraryLoadCursor");
+ }
+#else
+ X11->ptrXcursorLibraryLoadCursor = XcursorLibraryLoadCursor;
+#endif // QT_RUNTIME_XCURSOR
+#endif // QT_NO_XCURSOR
+
+#ifndef QT_NO_XINERAMA
+#ifdef QT_RUNTIME_XINERAMA
+ X11->ptrXineramaQueryExtension = 0;
+ X11->ptrXineramaIsActive = 0;
+ X11->ptrXineramaQueryScreens = 0;
+ QLibrary xineramaLib(QLatin1String("Xinerama"), 1);
+ bool xineramaFound = xineramaLib.load();
+ if (!xineramaFound) { //try without the version number
+ xineramaLib.setFileName(QLatin1String("Xinerama"));
+ xineramaFound = xineramaLib.load();
+ }
+ if (xineramaFound) {
+ X11->ptrXineramaQueryExtension =
+ (PtrXineramaQueryExtension) xineramaLib.resolve("XineramaQueryExtension");
+ X11->ptrXineramaIsActive =
+ (PtrXineramaIsActive) xineramaLib.resolve("XineramaIsActive");
+ X11->ptrXineramaQueryScreens =
+ (PtrXineramaQueryScreens) xineramaLib.resolve("XineramaQueryScreens");
+ }
+#else
+ X11->ptrXineramaQueryScreens = XineramaQueryScreens;
+ X11->ptrXineramaIsActive = XineramaIsActive;
+ X11->ptrXineramaQueryExtension = XineramaQueryExtension;
+#endif // QT_RUNTIME_XINERAMA
+#endif // QT_NO_XINERAMA
+
+#ifndef QT_NO_XINPUT
+ // See if Xinput is supported on the connected display
+ X11->ptrXCloseDevice = 0;
+ X11->ptrXListInputDevices = 0;
+ X11->ptrXOpenDevice = 0;
+ X11->ptrXFreeDeviceList = 0;
+ X11->ptrXSelectExtensionEvent = 0;
+ X11->use_xinput = XQueryExtension(X11->display, "XInputExtension", &X11->xinput_major,
+ &X11->xinput_eventbase, &X11->xinput_errorbase);
+ if (X11->use_xinput) {
+ X11->ptrXCloseDevice = XINPUT_LOAD(XCloseDevice);
+ X11->ptrXListInputDevices = XINPUT_LOAD(XListInputDevices);
+ X11->ptrXOpenDevice = XINPUT_LOAD(XOpenDevice);
+ X11->ptrXFreeDeviceList = XINPUT_LOAD(XFreeDeviceList);
+ X11->ptrXSelectExtensionEvent = XINPUT_LOAD(XSelectExtensionEvent);
+ }
+#endif // QT_NO_XINPUT
+
+#if !defined(QT_NO_FONTCONFIG)
+ int dpi = 0;
+ getXDefault("Xft", FC_DPI, &dpi);
+ if (dpi) {
+ for (int s = 0; s < ScreenCount(X11->display); ++s) {
+ QX11Info::setAppDpiX(s, dpi);
+ QX11Info::setAppDpiY(s, dpi);
+ }
+ }
+ double fc_scale = 1.;
+ getXDefault("Xft", FC_SCALE, &fc_scale);
+ X11->fc_scale = fc_scale;
+ for (int s = 0; s < ScreenCount(X11->display); ++s) {
+ int subpixel = FC_RGBA_UNKNOWN;
+#if RENDER_MAJOR > 0 || RENDER_MINOR >= 6
+ if (X11->use_xrender) {
+ int rsp = XRenderQuerySubpixelOrder(X11->display, s);
+ switch (rsp) {
+ default:
+ case SubPixelUnknown:
+ subpixel = FC_RGBA_UNKNOWN;
+ break;
+ case SubPixelHorizontalRGB:
+ subpixel = FC_RGBA_RGB;
+ break;
+ case SubPixelHorizontalBGR:
+ subpixel = FC_RGBA_BGR;
+ break;
+ case SubPixelVerticalRGB:
+ subpixel = FC_RGBA_VRGB;
+ break;
+ case SubPixelVerticalBGR:
+ subpixel = FC_RGBA_VBGR;
+ break;
+ case SubPixelNone:
+ subpixel = FC_RGBA_NONE;
+ break;
+ }
+ }
+#endif
+
+ char *rgba = XGetDefault(X11->display, "Xft", FC_RGBA);
+ if (rgba) {
+ char *end = 0;
+ int v = strtol(rgba, &end, 0);
+ if (rgba != end) {
+ subpixel = v;
+ } else if (qstrncmp(rgba, "unknown", 7) == 0) {
+ subpixel = FC_RGBA_UNKNOWN;
+ } else if (qstrncmp(rgba, "rgb", 3) == 0) {
+ subpixel = FC_RGBA_RGB;
+ } else if (qstrncmp(rgba, "bgr", 3) == 0) {
+ subpixel = FC_RGBA_BGR;
+ } else if (qstrncmp(rgba, "vrgb", 4) == 0) {
+ subpixel = FC_RGBA_VRGB;
+ } else if (qstrncmp(rgba, "vbgr", 4) == 0) {
+ subpixel = FC_RGBA_VBGR;
+ } else if (qstrncmp(rgba, "none", 4) == 0) {
+ subpixel = FC_RGBA_NONE;
+ }
+ }
+ X11->screens[s].subpixel = subpixel;
+ }
+ getXDefault("Xft", FC_ANTIALIAS, &X11->fc_antialias);
+#ifdef FC_HINT_STYLE
+ getXDefault("Xft", FC_HINT_STYLE, &X11->fc_hint_style);
+#endif
+#if 0
+ // ###### these are implemented by Xft, not sure we need them
+ getXDefault("Xft", FC_AUTOHINT, &X11->fc_autohint);
+ getXDefault("Xft", FC_HINTING, &X11->fc_autohint);
+ getXDefault("Xft", FC_MINSPACE, &X11->fc_autohint);
+#endif
+#endif // QT_NO_XRENDER
+
+ // initialize key mapper
+ QKeyMapper::changeKeyboard();
+
+#ifndef QT_NO_XKB
+ if (qt_keymapper_private()->useXKB) {
+ // If XKB is detected, set the GrabsUseXKBState option so input method
+ // compositions continue to work (ie. deadkeys)
+ unsigned int state = XkbPCF_GrabsUseXKBStateMask;
+ (void) XkbSetPerClientControls(X11->display, state, &state);
+ }
+#endif // QT_NO_XKB
+
+ // Misc. initialization
+#if 0 //disabled for now..
+ QSegfaultHandler::initialize(priv->argv, priv->argc);
+#endif
+ QCursorData::initialize();
+ }
+ QFont::initialize();
+
+ if(qt_is_gui_used) {
+ qApp->setObjectName(QString::fromLocal8Bit(appName));
+
+ int screen;
+ for (screen = 0; screen < X11->screenCount; ++screen) {
+ XSelectInput(X11->display, QX11Info::appRootWindow(screen),
+ KeymapStateMask | EnterWindowMask | LeaveWindowMask | PropertyChangeMask);
+
+#ifndef QT_NO_XRANDR
+ if (X11->use_xrandr)
+ X11->ptrXRRSelectInput(X11->display, QX11Info::appRootWindow(screen), True);
+#endif // QT_NO_XRANDR
+ }
+ }
+
+ if (qt_is_gui_used) {
+ // Attempt to determine the current running X11 Desktop Enviornment
+ // Use dbus if/when we can, but fall back to using windowManagerName() for now
+
+ X11->compositingManagerRunning = XGetSelectionOwner(X11->display,
+ ATOM(_NET_WM_CM_S0));
+ X11->desktopEnvironment = DE_UNKNOWN;
+
+ // See if the current window manager is using the freedesktop.org spec to give its name
+ Window windowManagerWindow = XNone;
+ Atom typeReturned;
+ int formatReturned;
+ unsigned long nitemsReturned;
+ unsigned long unused;
+ unsigned char *data = 0;
+ if (XGetWindowProperty(QX11Info::display(), QX11Info::appRootWindow(),
+ ATOM(_NET_SUPPORTING_WM_CHECK),
+ 0, 1024, False, XA_WINDOW, &typeReturned,
+ &formatReturned, &nitemsReturned, &unused, &data)
+ == Success) {
+ if (typeReturned == XA_WINDOW && formatReturned == 32)
+ windowManagerWindow = *((Window*) data);
+ if (data)
+ XFree(data);
+
+ if (windowManagerWindow != XNone) {
+ QString wmName;
+ Atom utf8atom = ATOM(UTF8_STRING);
+ if (XGetWindowProperty(QX11Info::display(), windowManagerWindow, ATOM(_NET_WM_NAME),
+ 0, 1024, False, utf8atom, &typeReturned,
+ &formatReturned, &nitemsReturned, &unused, &data)
+ == Success) {
+ if (typeReturned == utf8atom && formatReturned == 8)
+ wmName = QString::fromUtf8((const char*)data);
+ if (data)
+ XFree(data);
+ if (wmName == QLatin1String("KWin"))
+ X11->desktopEnvironment = DE_KDE;
+ if (wmName == QLatin1String("Metacity"))
+ X11->desktopEnvironment = DE_GNOME;
+ }
+ }
+ }
+
+ // Running a different/newer/older window manager? Try some other things
+ if (X11->desktopEnvironment == DE_UNKNOWN){
+ Atom type;
+ int format;
+ unsigned long length, after;
+ uchar *data = 0;
+
+ if (XGetWindowProperty(X11->display, QX11Info::appRootWindow(), ATOM(DTWM_IS_RUNNING),
+ 0, 1, False, AnyPropertyType, &type, &format, &length,
+ &after, &data) == Success && length) {
+ // DTWM is running, meaning most likely CDE is running...
+ X11->desktopEnvironment = DE_CDE;
+ } else if (XGetWindowProperty(X11->display, QX11Info::appRootWindow(),
+ ATOM(GNOME_BACKGROUND_PROPERTIES), 0, 1, False, AnyPropertyType,
+ &type, &format, &length, &after, &data) == Success && length) {
+ X11->desktopEnvironment = DE_GNOME;
+ } else if (!qgetenv("GNOME_DESKTOP_SESSION_ID").isEmpty()) {
+ X11->desktopEnvironment = DE_GNOME;
+ } else if ((XGetWindowProperty(X11->display, QX11Info::appRootWindow(), ATOM(KDE_FULL_SESSION),
+ 0, 1, False, AnyPropertyType, &type, &format, &length, &after, &data) == Success
+ && length)
+ || (XGetWindowProperty(X11->display, QX11Info::appRootWindow(), ATOM(KWIN_RUNNING),
+ 0, 1, False, AnyPropertyType, &type, &format, &length,
+ &after, &data) == Success
+ && length)
+ || (XGetWindowProperty(X11->display, QX11Info::appRootWindow(), ATOM(KWM_RUNNING),
+ 0, 1, False, AnyPropertyType, &type, &format, &length,
+ &after, &data) == Success && length)) {
+ X11->desktopEnvironment = DE_KDE;
+ } else if (XGetWindowProperty(X11->display, QX11Info::appRootWindow(), ATOM(_SGI_DESKS_MANAGER),
+ 0, 1, False, XA_WINDOW, &type, &format, &length, &after, &data) == Success
+ && length) {
+ X11->desktopEnvironment = DE_4DWM;
+ }
+ if (data)
+ XFree((char *)data);
+ }
+
+ qt_set_input_encoding();
+
+ qt_set_x11_resources(appFont, appFGCol, appBGCol, appBTNCol);
+
+ // be smart about the size of the default font. most X servers have helvetica
+ // 12 point available at 2 resolutions:
+ // 75dpi (12 pixels) and 100dpi (17 pixels).
+ // At 95 DPI, a 12 point font should be 16 pixels tall - in which case a 17
+ // pixel font is a closer match than a 12 pixel font
+ int ptsz = (X11->use_xrender
+ ? 9
+ : (int) (((QX11Info::appDpiY() >= 95 ? 17. : 12.) *
+ 72. / (float) QX11Info::appDpiY()) + 0.5));
+
+ if (!QApplicationPrivate::sys_font) {
+ // no font from settings or RESOURCE_MANAGER, provide a fallback
+ QFont f(X11->has_fontconfig ? QLatin1String("Sans Serif") : QLatin1String("Helvetica"),
+ ptsz);
+ QApplicationPrivate::setSystemFont(f);
+ }
+
+#if !defined (QT_NO_TABLET)
+ if (X11->use_xinput) {
+ int ndev,
+ i,
+ j;
+ bool gotStylus,
+ gotEraser;
+ XDeviceInfo *devices = 0, *devs;
+ XInputClassInfo *ip;
+ XAnyClassPtr any;
+ XValuatorInfoPtr v;
+ XAxisInfoPtr a;
+ XDevice *dev = 0;
+
+#if !defined(Q_OS_IRIX)
+ // XFree86 divides a stylus and eraser into 2 devices, so we must do for both...
+ const QString XFREENAMESTYLUS = QLatin1String("stylus");
+ const QString XFREENAMEPEN = QLatin1String("pen");
+ const QString XFREENAMEERASER = QLatin1String("eraser");
+#endif
+
+ if (X11->ptrXListInputDevices) {
+ devices = X11->ptrXListInputDevices(X11->display, &ndev);
+ if (!devices)
+ qWarning("QApplication: Failed to get list of tablet devices");
+ }
+ if (!devices)
+ ndev = -1;
+ QTabletEvent::TabletDevice deviceType;
+ for (devs = devices, i = 0; i < ndev && devs; i++, devs++) {
+ dev = 0;
+ deviceType = QTabletEvent::NoDevice;
+ gotStylus = false;
+ gotEraser = false;
+
+ QString devName = QString::fromLocal8Bit(devs->name).toLower();
+#if defined(Q_OS_IRIX)
+ if (devName == QLatin1String(WACOM_NAME)) {
+ deviceType = QTabletEvent::Stylus;
+ gotStylus = true;
+ }
+#else
+ if (devName.startsWith(XFREENAMEPEN)
+ || devName.startsWith(XFREENAMESTYLUS)) {
+ deviceType = QTabletEvent::Stylus;
+ gotStylus = true;
+ } else if (devName.startsWith(XFREENAMEERASER)) {
+ deviceType = QTabletEvent::XFreeEraser;
+ gotEraser = true;
+ }
+#endif
+ if (deviceType == QTabletEvent::NoDevice)
+ continue;
+
+ if (gotStylus || gotEraser) {
+ if (X11->ptrXOpenDevice)
+ dev = X11->ptrXOpenDevice(X11->display, devs->id);
+
+ if (!dev)
+ continue;
+
+ QTabletDeviceData device_data;
+ device_data.deviceType = deviceType;
+ device_data.eventCount = 0;
+ device_data.device = dev;
+ device_data.xinput_motion = -1;
+ device_data.xinput_key_press = -1;
+ device_data.xinput_key_release = -1;
+ device_data.xinput_button_press = -1;
+ device_data.xinput_button_release = -1;
+ device_data.xinput_proximity_in = -1;
+ device_data.xinput_proximity_out = -1;
+ device_data.widgetToGetPress = 0;
+
+ if (dev->num_classes > 0) {
+ for (ip = dev->classes, j = 0; j < dev->num_classes;
+ ip++, j++) {
+ switch (ip->input_class) {
+ case KeyClass:
+ DeviceKeyPress(dev, device_data.xinput_key_press,
+ device_data.eventList[device_data.eventCount]);
+ if (device_data.eventList[device_data.eventCount])
+ ++device_data.eventCount;
+ DeviceKeyRelease(dev, device_data.xinput_key_release,
+ device_data.eventList[device_data.eventCount]);
+ if (device_data.eventList[device_data.eventCount])
+ ++device_data.eventCount;
+ break;
+ case ButtonClass:
+ DeviceButtonPress(dev, device_data.xinput_button_press,
+ device_data.eventList[device_data.eventCount]);
+ if (device_data.eventList[device_data.eventCount])
+ ++device_data.eventCount;
+ DeviceButtonRelease(dev, device_data.xinput_button_release,
+ device_data.eventList[device_data.eventCount]);
+ if (device_data.eventList[device_data.eventCount])
+ ++device_data.eventCount;
+ break;
+ case ValuatorClass:
+ // I'm only going to be interested in motion when the
+ // stylus is already down anyway!
+ DeviceMotionNotify(dev, device_data.xinput_motion,
+ device_data.eventList[device_data.eventCount]);
+ if (device_data.eventList[device_data.eventCount])
+ ++device_data.eventCount;
+ ProximityIn(dev, device_data.xinput_proximity_in, device_data.eventList[device_data.eventCount]);
+ if (device_data.eventList[device_data.eventCount])
+ ++device_data.eventCount;
+ ProximityOut(dev, device_data.xinput_proximity_out, device_data.eventList[device_data.eventCount]);
+ if (device_data.eventList[device_data.eventCount])
+ ++device_data.eventCount;
+ default:
+ break;
+ }
+ }
+ }
+
+ // get the min/max value for pressure!
+ any = (XAnyClassPtr) (devs->inputclassinfo);
+ for (j = 0; j < devs->num_classes; j++) {
+ if (any->c_class == ValuatorClass) {
+ v = (XValuatorInfoPtr) any;
+ a = (XAxisInfoPtr) ((char *) v +
+ sizeof (XValuatorInfo));
+#if defined (Q_OS_IRIX)
+ // I'm not exaclty wild about this, but the
+ // dimensions of the tablet are more relevant here
+ // than the min and max values from the axis
+ // (actually it seems to be 2/3 or what is in the
+ // axis. So we'll try to parse it from this
+ // string. --tws
+ char returnString[SGIDeviceRtrnLen];
+ int tmp;
+ if (XSGIMiscQueryExtension(X11->display, &tmp, &tmp)
+ && XSGIDeviceQuery(X11->display, devs->id,
+ "dimensions", returnString)) {
+ QString str = QLatin1String(returnString);
+ int comma = str.indexOf(',');
+ device_data.minX = 0;
+ device_data.minY = 0;
+ device_data.maxX = str.left(comma).toInt();
+ device_data.maxY = str.mid(comma + 1).toInt();
+ } else {
+ device_data.minX = a[WAC_XCOORD_I].min_value;
+ device_data.maxX = a[WAC_XCOORD_I].max_value;
+ device_data.minY = a[WAC_YCOORD_I].min_value;
+ device_data.maxY = a[WAC_YCOORD_I].max_value;
+ }
+ device_data.minPressure = a[WAC_PRESSURE_I].min_value;
+ device_data.maxPressure = a[WAC_PRESSURE_I].max_value;
+ device_data.minTanPressure = a[WAC_TAN_PRESSURE_I].min_value;
+ device_data.maxTanPressure = a[WAC_TAN_PRESSURE_I].max_value;
+ device_data.minZ = a[WAC_ZCOORD_I].min_value;
+ device_data.maxZ = a[WAC_ZCOORD_I].max_value;
+#else
+ device_data.minX = a[0].min_value;
+ device_data.maxX = a[0].max_value;
+ device_data.minY = a[1].min_value;
+ device_data.maxY = a[1].max_value;
+ device_data.minPressure = a[2].min_value;
+ device_data.maxPressure = a[2].max_value;
+ device_data.minTanPressure = 0;
+ device_data.maxTanPressure = 0;
+ device_data.minZ = 0;
+ device_data.maxZ = 0;
+#endif
+
+ // got the max pressure no need to go further...
+ break;
+ }
+ any = (XAnyClassPtr) ((char *) any + any->length);
+ } // end of for loop
+
+ tablet_devices()->append(device_data);
+ } // if (gotStylus || gotEraser)
+ }
+ if (X11->ptrXFreeDeviceList)
+ X11->ptrXFreeDeviceList(devices);
+ }
+#endif // QT_NO_TABLET
+
+ X11->startupId = getenv("DESKTOP_STARTUP_ID");
+ X11->originalStartupId = X11->startupId;
+ static char desktop_startup_id[] = "DESKTOP_STARTUP_ID=";
+ putenv(desktop_startup_id);
+
+ } else {
+ // read some non-GUI settings when not using the X server...
+
+ if (QApplication::desktopSettingsAware()) {
+ QSettings settings(QSettings::UserScope, QLatin1String("Trolltech"));
+ settings.beginGroup(QLatin1String("Qt"));
+
+ // read library (ie. plugin) path list
+ QString libpathkey = QString::fromLatin1("%1.%2/libraryPath")
+ .arg(QT_VERSION >> 16)
+ .arg((QT_VERSION & 0xff00) >> 8);
+ QStringList pathlist =
+ settings.value(libpathkey).toString().split(QLatin1Char(':'));
+ if (! pathlist.isEmpty()) {
+ QStringList::ConstIterator it = pathlist.constBegin();
+ while (it != pathlist.constEnd())
+ QApplication::addLibraryPath(*it++);
+ }
+
+ QString defaultcodec = settings.value(QLatin1String("defaultCodec"),
+ QVariant(QLatin1String("none"))).toString();
+ if (defaultcodec != QLatin1String("none")) {
+ QTextCodec *codec = QTextCodec::codecForName(defaultcodec.toLatin1());
+ if (codec)
+ QTextCodec::setCodecForTr(codec);
+ }
+
+ settings.endGroup(); // Qt
+ }
+ }
+
+#if !defined (Q_OS_IRIX) && !defined (QT_NO_TABLET)
+ QLibrary wacom(QString::fromLatin1("wacomcfg"), 0); // version 0 is the latest release at time of writing this.
+ if (wacom.load()) {
+ // NOTE: C casts instead of reinterpret_cast for GCC 3.3.x
+ ptrWacomConfigInit = (PtrWacomConfigInit)wacom.resolve("WacomConfigInit");
+ ptrWacomConfigOpenDevice = (PtrWacomConfigOpenDevice)wacom.resolve("WacomConfigOpenDevice");
+ ptrWacomConfigGetRawParam = (PtrWacomConfigGetRawParam)wacom.resolve("WacomConfigGetRawParam");
+ ptrWacomConfigCloseDevice = (PtrWacomConfigCloseDevice)wacom.resolve("WacomConfigCloseDevice");
+ ptrWacomConfigTerm = (PtrWacomConfigTerm)wacom.resolve("WacomConfigTerm");
+
+ if (ptrWacomConfigInit == 0 || ptrWacomConfigOpenDevice == 0 || ptrWacomConfigGetRawParam == 0
+ || ptrWacomConfigCloseDevice == 0 || ptrWacomConfigTerm == 0) { // either we have all, or we have none.
+ ptrWacomConfigInit = 0;
+ ptrWacomConfigOpenDevice = 0;
+ ptrWacomConfigGetRawParam = 0;
+ ptrWacomConfigCloseDevice = 0;
+ ptrWacomConfigTerm = 0;
+ }
+ }
+#endif
+}
+
+
+ // run-time search for default style
+/*!
+ \internal
+*/
+void QApplicationPrivate::x11_initialize_style()
+{
+ if (QApplicationPrivate::app_style)
+ return;
+
+ switch(X11->desktopEnvironment) {
+ case DE_KDE:
+ if (X11->use_xrender)
+ QApplicationPrivate::app_style = QStyleFactory::create(QLatin1String("plastique"));
+ else
+ QApplicationPrivate::app_style = QStyleFactory::create(QLatin1String("windows"));
+ break;
+ case DE_GNOME:
+ if (X11->use_xrender)
+ QApplicationPrivate::app_style = QStyleFactory::create(QLatin1String("cleanlooks"));
+ else
+ QApplicationPrivate::app_style = QStyleFactory::create(QLatin1String("windows"));
+ break;
+ case DE_CDE:
+ QApplicationPrivate::app_style = QStyleFactory::create(QLatin1String("cde"));
+ break;
+ default:
+ // Don't do anything
+ break;
+ }
+}
+
+void QApplicationPrivate::initializeWidgetPaletteHash()
+{
+}
+
+/*****************************************************************************
+ qt_cleanup() - cleans up when the application is finished
+ *****************************************************************************/
+
+void qt_cleanup()
+{
+ if (app_save_rootinfo) // root window must keep state
+ qt_save_rootinfo();
+
+ if (qt_is_gui_used) {
+ QPixmapCache::clear();
+ QCursorData::cleanup();
+ QFont::cleanup();
+ QColormap::cleanup();
+
+#if !defined (QT_NO_TABLET)
+ QTabletDeviceDataList *devices = qt_tablet_devices();
+ if (X11->ptrXCloseDevice)
+ for (int i = 0; i < devices->size(); ++i)
+ X11->ptrXCloseDevice(X11->display, (XDevice*)devices->at(i).device);
+ devices->clear();
+#endif
+ }
+
+ // restore original value back. This is also done in QWidgetPrivate::show_sys.
+ if (X11->originalStartupId)
+ putenv(X11->originalStartupId);
+
+#ifndef QT_NO_XRENDER
+ for (int i = 0; i < X11->solid_fill_count; ++i) {
+ if (X11->solid_fills[i].picture)
+ XRenderFreePicture(X11->display, X11->solid_fills[i].picture);
+ }
+ for (int i = 0; i < X11->pattern_fill_count; ++i) {
+ if (X11->pattern_fills[i].picture)
+ XRenderFreePicture(X11->display, X11->pattern_fills[i].picture);
+ }
+#endif
+
+#if !defined(QT_NO_IM)
+ delete QApplicationPrivate::inputContext;
+ QApplicationPrivate::inputContext = 0;
+#endif
+
+ // Reset the error handlers
+ if (qt_is_gui_used)
+ XSync(X11->display, False); // sync first to process all possible errors
+ XSetErrorHandler(original_x_errhandler);
+ XSetIOErrorHandler(original_xio_errhandler);
+
+ if (X11->argbColormaps) {
+ for (int s = 0; s < X11->screenCount; s++) {
+ if (X11->argbColormaps[s])
+ XFreeColormap(X11->display, X11->argbColormaps[s]);
+ }
+ }
+
+ if (qt_is_gui_used && !X11->foreignDisplay)
+ XCloseDisplay(X11->display); // close X display
+ X11->display = 0;
+
+ delete [] X11->screens;
+ delete [] X11->argbVisuals;
+ delete [] X11->argbColormaps;
+
+ if (X11->foreignDisplay) {
+ delete [] (char *)appName;
+ appName = 0;
+ }
+
+ delete [] (char *)appClass;
+ appClass = 0;
+
+ if (X11->net_supported_list)
+ delete [] X11->net_supported_list;
+ X11->net_supported_list = 0;
+
+ if (X11->net_virtual_root_list)
+ delete [] X11->net_virtual_root_list;
+ X11->net_virtual_root_list = 0;
+
+ delete X11;
+ X11 = 0;
+}
+
+
+/*****************************************************************************
+ Platform specific global and internal functions
+ *****************************************************************************/
+
+void qt_save_rootinfo() // save new root info
+{
+ Atom type;
+ int format;
+ unsigned long length, after;
+ uchar *data = 0;
+
+ if (ATOM(_XSETROOT_ID)) { // kill old pixmap
+ if (XGetWindowProperty(X11->display, QX11Info::appRootWindow(),
+ ATOM(_XSETROOT_ID), 0, 1,
+ True, AnyPropertyType, &type, &format,
+ &length, &after, &data) == Success) {
+ if (type == XA_PIXMAP && format == 32 && length == 1 &&
+ after == 0 && data) {
+ XKillClient(X11->display, *((Pixmap*)data));
+ }
+ Pixmap dummy = XCreatePixmap(X11->display, QX11Info::appRootWindow(),
+ 1, 1, 1);
+ XChangeProperty(X11->display, QX11Info::appRootWindow(),
+ ATOM(_XSETROOT_ID), XA_PIXMAP, 32,
+ PropModeReplace, (uchar *)&dummy, 1);
+ XSetCloseDownMode(X11->display, RetainPermanent);
+ }
+ }
+ if (data)
+ XFree((char *)data);
+}
+
+void qt_updated_rootinfo()
+{
+ app_save_rootinfo = true;
+}
+
+// ### Cleanup, this function is not in use!
+bool qt_wstate_iconified(WId winid)
+{
+ Atom type;
+ int format;
+ unsigned long length, after;
+ uchar *data = 0;
+ int r = XGetWindowProperty(X11->display, winid, ATOM(WM_STATE), 0, 2,
+ False, AnyPropertyType, &type, &format,
+ &length, &after, &data);
+ bool iconic = false;
+ if (r == Success && data && format == 32) {
+ // quint32 *wstate = (quint32*)data;
+ unsigned long *wstate = (unsigned long *) data;
+ iconic = (*wstate == IconicState);
+ XFree((char *)data);
+ }
+ return iconic;
+}
+
+QString QApplicationPrivate::appName() const
+{
+ return QString::fromLocal8Bit(QT_PREPEND_NAMESPACE(appName));
+}
+
+const char *QX11Info::appClass() // get application class
+{
+ return QT_PREPEND_NAMESPACE(appClass);
+}
+
+bool qt_nograb() // application no-grab option
+{
+#if defined(QT_DEBUG)
+ return appNoGrab;
+#else
+ return false;
+#endif
+}
+
+
+/*****************************************************************************
+ Platform specific QApplication members
+ *****************************************************************************/
+
+#ifdef QT3_SUPPORT
+void QApplication::setMainWidget(QWidget *mainWidget)
+{
+#ifndef QT_NO_DEBUG
+ if (mainWidget && mainWidget->parentWidget() && mainWidget->isWindow())
+ qWarning("QApplication::setMainWidget: New main widget (%s/%s) "
+ "has a parent",
+ mainWidget->metaObject()->className(), mainWidget->objectName().toLocal8Bit().constData());
+#endif
+ if (mainWidget)
+ mainWidget->d_func()->createWinId();
+ QApplicationPrivate::main_widget = mainWidget;
+ if (QApplicationPrivate::main_widget) // give WM command line
+ QApplicationPrivate::applyX11SpecificCommandLineArguments(QApplicationPrivate::main_widget);
+}
+#endif
+
+void QApplicationPrivate::applyX11SpecificCommandLineArguments(QWidget *main_widget)
+{
+ static bool beenHereDoneThat = false;
+ if (beenHereDoneThat)
+ return;
+ beenHereDoneThat = true;
+ Q_ASSERT(main_widget->testAttribute(Qt::WA_WState_Created));
+ if (mwTitle) {
+ XStoreName(X11->display, main_widget->effectiveWinId(), (char*)mwTitle);
+ QByteArray net_wm_name = QString::fromLocal8Bit(mwTitle).toUtf8();
+ XChangeProperty(X11->display, main_widget->effectiveWinId(), ATOM(_NET_WM_NAME), ATOM(UTF8_STRING), 8,
+ PropModeReplace, (unsigned char *)net_wm_name.data(), net_wm_name.size());
+ }
+ if (mwGeometry) { // parse geometry
+ int x, y;
+ int w, h;
+ int m = XParseGeometry((char*)mwGeometry, &x, &y, (uint*)&w, (uint*)&h);
+ QSize minSize = main_widget->minimumSize();
+ QSize maxSize = main_widget->maximumSize();
+ if ((m & XValue) == 0)
+ x = main_widget->geometry().x();
+ if ((m & YValue) == 0)
+ y = main_widget->geometry().y();
+ if ((m & WidthValue) == 0)
+ w = main_widget->width();
+ if ((m & HeightValue) == 0)
+ h = main_widget->height();
+ w = qMin(w,maxSize.width());
+ h = qMin(h,maxSize.height());
+ w = qMax(w,minSize.width());
+ h = qMax(h,minSize.height());
+ if ((m & XNegative)) {
+ x = QApplication::desktop()->width() + x - w;
+ }
+ if ((m & YNegative)) {
+ y = QApplication::desktop()->height() + y - h;
+ }
+ main_widget->setGeometry(x, y, w, h);
+ }
+}
+
+#ifndef QT_NO_CURSOR
+
+/*****************************************************************************
+ QApplication cursor stack
+ *****************************************************************************/
+
+extern void qt_x11_enforce_cursor(QWidget * w);
+
+void QApplication::setOverrideCursor(const QCursor &cursor)
+{
+ qApp->d_func()->cursor_list.prepend(cursor);
+
+ QWidgetList all = allWidgets();
+ for (QWidgetList::ConstIterator it = all.constBegin(); it != all.constEnd(); ++it) {
+ register QWidget *w = *it;
+ if ((w->testAttribute(Qt::WA_SetCursor) || w->isWindow()) && (w->windowType() != Qt::Desktop))
+ qt_x11_enforce_cursor(w);
+ }
+ XFlush(X11->display); // make X execute it NOW
+}
+
+void QApplication::restoreOverrideCursor()
+{
+ if (qApp->d_func()->cursor_list.isEmpty())
+ return;
+ qApp->d_func()->cursor_list.removeFirst();
+
+ if (QWidgetPrivate::mapper != 0 && !closingDown()) {
+ QWidgetList all = allWidgets();
+ for (QWidgetList::ConstIterator it = all.constBegin(); it != all.constEnd(); ++it) {
+ register QWidget *w = *it;
+ if ((w->testAttribute(Qt::WA_SetCursor) || w->isWindow()) && (w->windowType() != Qt::Desktop))
+ qt_x11_enforce_cursor(w);
+ }
+ XFlush(X11->display);
+ }
+}
+
+#endif
+
+
+/*****************************************************************************
+ Routines to find a Qt widget from a screen position
+ *****************************************************************************/
+
+Window QX11Data::findClientWindow(Window win, Atom property, bool leaf)
+{
+ Atom type = XNone;
+ int format, i;
+ ulong nitems, after;
+ uchar *data = 0;
+ Window root, parent, target=0, *children=0;
+ uint nchildren;
+ if (XGetWindowProperty(X11->display, win, property, 0, 0, false, AnyPropertyType,
+ &type, &format, &nitems, &after, &data) == Success) {
+ if (data)
+ XFree((char *)data);
+ if (type)
+ return win;
+ }
+ if (!XQueryTree(X11->display,win,&root,&parent,&children,&nchildren)) {
+ if (children)
+ XFree((char *)children);
+ return 0;
+ }
+ for (i=nchildren-1; !target && i >= 0; i--)
+ target = X11->findClientWindow(children[i], property, leaf);
+ if (children)
+ XFree((char *)children);
+ return target;
+}
+
+QWidget *QApplication::topLevelAt(const QPoint &p)
+{
+#ifdef QT_NO_CURSOR
+ Q_UNUSED(p);
+ return 0;
+#else
+ int screen = QCursor::x11Screen();
+ int unused;
+
+ int x = p.x();
+ int y = p.y();
+ Window target;
+ if (!XTranslateCoordinates(X11->display,
+ QX11Info::appRootWindow(screen),
+ QX11Info::appRootWindow(screen),
+ x, y, &unused, &unused, &target)) {
+ return 0;
+ }
+ if (!target || target == QX11Info::appRootWindow(screen))
+ return 0;
+ QWidget *w;
+ w = QWidget::find((WId)target);
+
+ if (!w) {
+ X11->ignoreBadwindow();
+ target = X11->findClientWindow(target, ATOM(WM_STATE), true);
+ if (X11->badwindow())
+ return 0;
+ w = QWidget::find((WId)target);
+ if (!w) {
+ // Perhaps the widget at (x,y) is inside a foreign application?
+ // Search all toplevel widgets to see if one is within target
+ QWidgetList list = QApplication::topLevelWidgets();
+ for (int i = 0; i < list.count(); ++i) {
+ QWidget *widget = list.at(i);
+ Window ctarget = target;
+ if (widget->isVisible() && !(widget->windowType() == Qt::Desktop)) {
+ Q_ASSERT(widget->testAttribute(Qt::WA_WState_Created));
+ Window wid = widget->internalWinId();
+ while (ctarget && !w) {
+ X11->ignoreBadwindow();
+ XTranslateCoordinates(X11->display,
+ QX11Info::appRootWindow(screen),
+ ctarget, x, y, &unused, &unused, &ctarget);
+ if (X11->badwindow())
+ break;
+ if (ctarget == wid) {
+ // Found!
+ w = widget;
+ break;
+ }
+ }
+ }
+ if (w)
+ break;
+ }
+ }
+ }
+ return w ? w->window() : 0;
+#endif
+}
+
+/*!
+ Synchronizes with the X server in the X11 implementation. This
+ normally takes some time. Does nothing on other platforms.
+*/
+
+void QApplication::syncX()
+{
+ if (X11->display)
+ XSync(X11->display, False); // don't discard events
+}
+
+
+void QApplication::beep()
+{
+ if (X11->display)
+ XBell(X11->display, 0);
+ else
+ printf("\7");
+}
+
+void QApplication::alert(QWidget *widget, int msec)
+{
+ if (!QApplicationPrivate::checkInstance("alert"))
+ return;
+
+ QWidgetList windowsToMark;
+ if (!widget) {
+ windowsToMark += topLevelWidgets();
+ } else {
+ windowsToMark.append(widget->window());
+ }
+
+ for (int i = 0; i < windowsToMark.size(); ++i) {
+ QWidget *window = windowsToMark.at(i);
+ if (!window->isActiveWindow()) {
+ qt_change_net_wm_state(window, true, ATOM(_NET_WM_STATE_DEMANDS_ATTENTION));
+ if (msec != 0) {
+ QTimer *timer = new QTimer(qApp);
+ timer->setSingleShot(true);
+ connect(timer, SIGNAL(timeout()), qApp, SLOT(_q_alertTimeOut()));
+ if (QTimer *oldTimer = qApp->d_func()->alertTimerHash.value(window)) {
+ qApp->d_func()->alertTimerHash.remove(window);
+ delete oldTimer;
+ }
+ qApp->d_func()->alertTimerHash.insert(window, timer);
+ timer->start(msec);
+ }
+ }
+ }
+}
+
+void QApplicationPrivate::_q_alertTimeOut()
+{
+ if (QTimer *timer = qobject_cast<QTimer *>(q_func()->sender())) {
+ QHash<QWidget *, QTimer *>::iterator it = alertTimerHash.begin();
+ while (it != alertTimerHash.end()) {
+ if (it.value() == timer) {
+ QWidget *window = it.key();
+ qt_change_net_wm_state(window, false, ATOM(_NET_WM_STATE_DEMANDS_ATTENTION));
+ alertTimerHash.erase(it);
+ timer->deleteLater();
+ break;
+ }
+ ++it;
+ }
+ }
+}
+
+/*****************************************************************************
+ Special lookup functions for windows that have been reparented recently
+ *****************************************************************************/
+
+static QWidgetMapper *wPRmapper = 0; // alternative widget mapper
+
+void qPRCreate(const QWidget *widget, Window oldwin)
+{ // QWidget::reparent mechanism
+ if (!wPRmapper)
+ wPRmapper = new QWidgetMapper;
+
+ QETWidget *w = static_cast<QETWidget *>(const_cast<QWidget *>(widget));
+ wPRmapper->insert((int)oldwin, w); // add old window to mapper
+ w->setAttribute(Qt::WA_WState_Reparented); // set reparented flag
+}
+
+void qPRCleanup(QWidget *widget)
+{
+ QETWidget *etw = static_cast<QETWidget *>(const_cast<QWidget *>(widget));
+ if (!(wPRmapper && widget->testAttribute(Qt::WA_WState_Reparented)))
+ return; // not a reparented widget
+ QWidgetMapper::Iterator it = wPRmapper->begin();
+ while (it != wPRmapper->constEnd()) {
+ QWidget *w = *it;
+ if (w == etw) { // found widget
+ etw->setAttribute(Qt::WA_WState_Reparented, false); // clear flag
+ it = wPRmapper->erase(it);// old window no longer needed
+ } else {
+ ++it;
+ }
+ }
+ if (wPRmapper->size() == 0) { // became empty
+ delete wPRmapper; // then reset alt mapper
+ wPRmapper = 0;
+ }
+}
+
+static QETWidget *qPRFindWidget(Window oldwin)
+{
+ return wPRmapper ? (QETWidget*)wPRmapper->value((int)oldwin, 0) : 0;
+}
+
+/*!
+ \internal
+*/
+int QApplication::x11ClientMessage(QWidget* w, XEvent* event, bool passive_only)
+{
+ if (w && !w->internalWinId())
+ return 0;
+ QETWidget *widget = (QETWidget*)w;
+ if (event->xclient.format == 32 && event->xclient.message_type) {
+ if (event->xclient.message_type == ATOM(WM_PROTOCOLS)) {
+ Atom a = event->xclient.data.l[0];
+ if (a == ATOM(WM_DELETE_WINDOW)) {
+ if (passive_only) return 0;
+ widget->translateCloseEvent(event);
+ }
+ else if (a == ATOM(WM_TAKE_FOCUS)) {
+ if ((ulong) event->xclient.data.l[1] > X11->time)
+ X11->time = event->xclient.data.l[1];
+ QWidget *amw = activeModalWidget();
+ if (amw && !QApplicationPrivate::tryModalHelper(widget, 0)) {
+ QWidget *p = amw->parentWidget();
+ while (p && p != widget)
+ p = p->parentWidget();
+ if (!p || !X11->net_supported_list)
+ amw->raise(); // help broken window managers
+ amw->activateWindow();
+ }
+#ifndef QT_NO_WHATSTHIS
+ } else if (a == ATOM(_NET_WM_CONTEXT_HELP)) {
+ QWhatsThis::enterWhatsThisMode();
+#endif // QT_NO_WHATSTHIS
+ } else if (a == ATOM(_NET_WM_PING)) {
+ // avoid send/reply loops
+ Window root = RootWindow(X11->display, w->x11Info().screen());
+ if (event->xclient.window != root) {
+ event->xclient.window = root;
+ XSendEvent(event->xclient.display, event->xclient.window,
+ False, SubstructureNotifyMask|SubstructureRedirectMask, event);
+ }
+ }
+ } else if (event->xclient.message_type == ATOM(_QT_SCROLL_DONE)) {
+ widget->translateScrollDoneEvent(event);
+ } else if (event->xclient.message_type == ATOM(XdndPosition)) {
+ X11->xdndHandlePosition(widget, event, passive_only);
+ } else if (event->xclient.message_type == ATOM(XdndEnter)) {
+ X11->xdndHandleEnter(widget, event, passive_only);
+ } else if (event->xclient.message_type == ATOM(XdndStatus)) {
+ X11->xdndHandleStatus(widget, event, passive_only);
+ } else if (event->xclient.message_type == ATOM(XdndLeave)) {
+ X11->xdndHandleLeave(widget, event, passive_only);
+ } else if (event->xclient.message_type == ATOM(XdndDrop)) {
+ X11->xdndHandleDrop(widget, event, passive_only);
+ } else if (event->xclient.message_type == ATOM(XdndFinished)) {
+ X11->xdndHandleFinished(widget, event, passive_only);
+ } else {
+ if (passive_only) return 0;
+ // All other are interactions
+ }
+ } else {
+ X11->motifdndHandle(widget, event, passive_only);
+ }
+
+ return 0;
+}
+
+/*!
+ This function does the core processing of individual X
+ \a{event}s, normally by dispatching Qt events to the right
+ destination.
+
+ It returns 1 if the event was consumed by special handling, 0 if
+ the \a event was consumed by normal handling, and -1 if the \a
+ event was for an unrecognized widget.
+
+ \sa x11EventFilter()
+*/
+int QApplication::x11ProcessEvent(XEvent* event)
+{
+ Q_D(QApplication);
+ QScopedLoopLevelCounter loopLevelCounter(d->threadData);
+#ifdef ALIEN_DEBUG
+ //qDebug() << "QApplication::x11ProcessEvent:" << event->type;
+#endif
+ switch (event->type) {
+ case ButtonPress:
+ pressed_window = event->xbutton.window;
+ X11->userTime = event->xbutton.time;
+ // fallthrough intended
+ case ButtonRelease:
+ X11->time = event->xbutton.time;
+ break;
+ case MotionNotify:
+ X11->time = event->xmotion.time;
+ break;
+ case XKeyPress:
+ X11->userTime = event->xkey.time;
+ // fallthrough intended
+ case XKeyRelease:
+ X11->time = event->xkey.time;
+ break;
+ case PropertyNotify:
+ X11->time = event->xproperty.time;
+ break;
+ case EnterNotify:
+ case LeaveNotify:
+ X11->time = event->xcrossing.time;
+ break;
+ case SelectionClear:
+ X11->time = event->xselectionclear.time;
+ break;
+ default:
+ break;
+ }
+#ifndef QT_NO_XFIXES
+ if (X11->use_xfixes && event->type == (X11->xfixes_eventbase + XFixesSelectionNotify)) {
+ XFixesSelectionNotifyEvent *req =
+ reinterpret_cast<XFixesSelectionNotifyEvent *>(event);
+ X11->time = req->selection_timestamp;
+ }
+#endif
+
+ QETWidget *widget = (QETWidget*)QWidget::find((WId)event->xany.window);
+
+ if (wPRmapper) { // just did a widget reparent?
+ if (widget == 0) { // not in std widget mapper
+ switch (event->type) { // only for mouse/key events
+ case ButtonPress:
+ case ButtonRelease:
+ case MotionNotify:
+ case XKeyPress:
+ case XKeyRelease:
+ widget = qPRFindWidget(event->xany.window);
+ break;
+ }
+ }
+ else if (widget->testAttribute(Qt::WA_WState_Reparented))
+ qPRCleanup(widget); // remove from alt mapper
+ }
+
+ QETWidget *keywidget=0;
+ bool grabbed=false;
+ if (event->type==XKeyPress || event->type==XKeyRelease) {
+ keywidget = (QETWidget*)QWidget::keyboardGrabber();
+ if (keywidget) {
+ grabbed = true;
+ } else if (!keywidget) {
+ if (d->inPopupMode()) // no focus widget, see if we have a popup
+ keywidget = (QETWidget*) (activePopupWidget()->focusWidget() ? activePopupWidget()->focusWidget() : activePopupWidget());
+ else if (QApplicationPrivate::focus_widget)
+ keywidget = (QETWidget*)QApplicationPrivate::focus_widget;
+ else if (widget)
+ keywidget = (QETWidget*)widget->window();
+ }
+ }
+
+#ifndef QT_NO_IM
+ // Filtering input events by the input context. It has to be taken
+ // place before any other key event consumers such as eventfilters
+ // and accelerators because some input methods require quite
+ // various key combination and sequences. It often conflicts with
+ // accelerators and so on, so we must give the input context the
+ // filtering opportunity first to ensure all input methods work
+ // properly regardless of application design.
+
+ if(keywidget && keywidget->isEnabled() && keywidget->testAttribute(Qt::WA_InputMethodEnabled)) {
+ // block user interaction during session management
+ if((event->type==XKeyPress || event->type==XKeyRelease) && qt_sm_blockUserInput)
+ return true;
+
+ // for XIM handling
+ QInputContext *qic = keywidget->inputContext();
+ if(qic && qic->x11FilterEvent(keywidget, event))
+ return true;
+
+ // filterEvent() accepts QEvent *event rather than preexpanded
+ // key event attribute values. This is intended to pass other
+ // QInputEvent in future. Other non IM-related events should
+ // not be forwarded to input contexts to prevent weird event
+ // handling.
+ if ((event->type == XKeyPress || event->type == XKeyRelease)) {
+ int code = -1;
+ int count = 0;
+ Qt::KeyboardModifiers modifiers;
+ QEvent::Type type;
+ QString text;
+ KeySym keySym;
+
+ qt_keymapper_private()->translateKeyEventInternal(keywidget, event, keySym, count,
+ text, modifiers, code, type, false);
+
+ // both key press/release is required for some complex
+ // input methods. don't eliminate anything.
+ QKeyEventEx keyevent(type, code, modifiers, text, false, qMax(qMax(count, 1), text.length()),
+ event->xkey.keycode, keySym, event->xkey.state);
+ if(qic && qic->filterEvent(&keyevent))
+ return true;
+ }
+ } else
+#endif // QT_NO_IM
+ {
+ if (XFilterEvent(event, XNone))
+ return true;
+ }
+
+ if (qt_x11EventFilter(event)) // send through app filter
+ return 1;
+
+ if (event->type == MappingNotify) {
+ // keyboard mapping changed
+ XRefreshKeyboardMapping(&event->xmapping);
+
+ QKeyMapper::changeKeyboard();
+ return 0;
+ }
+
+ if (!widget) { // don't know this windows
+ QWidget* popup = QApplication::activePopupWidget();
+ if (popup) {
+
+ /*
+ That is more than suboptimal. The real solution should
+ do some keyevent and buttonevent translation, so that
+ the popup still continues to work as the user expects.
+ Unfortunately this translation is currently only
+ possible with a known widget. I'll change that soon
+ (Matthias).
+ */
+
+ // Danger - make sure we don't lock the server
+ switch (event->type) {
+ case ButtonPress:
+ case ButtonRelease:
+ case XKeyPress:
+ case XKeyRelease:
+ do {
+ popup->close();
+ } while ((popup = qApp->activePopupWidget()));
+ return 1;
+ }
+ }
+ return -1;
+ }
+
+ if (event->type == XKeyPress || event->type == XKeyRelease)
+ widget = keywidget; // send XKeyEvents through keywidget->x11Event()
+
+ if (app_do_modal) // modal event handling
+ if (!qt_try_modal(widget, event)) {
+ if (event->type == ClientMessage && !widget->x11Event(event))
+ x11ClientMessage(widget, event, true);
+ return 1;
+ }
+
+
+ if (widget->x11Event(event)) // send through widget filter
+ return 1;
+#if !defined (QT_NO_TABLET)
+ if (!qt_xdnd_dragging) {
+ QTabletDeviceDataList *tablets = qt_tablet_devices();
+ for (int i = 0; i < tablets->size(); ++i) {
+ QTabletDeviceData &tab = tablets->operator [](i);
+ if (event->type == tab.xinput_motion
+ || event->type == tab.xinput_button_release
+ || event->type == tab.xinput_button_press
+ || event->type == tab.xinput_proximity_in
+ || event->type == tab.xinput_proximity_out) {
+ widget->translateXinputEvent(event, &tab);
+ return 0;
+ }
+ }
+ }
+#endif
+
+#ifndef QT_NO_XRANDR
+ if (X11->use_xrandr && event->type == (X11->xrandr_eventbase + RRScreenChangeNotify)) {
+ // update Xlib internals with the latest screen configuration
+ X11->ptrXRRUpdateConfiguration(event);
+
+ // update the size for desktop widget
+ int scr = X11->ptrXRRRootToScreen(X11->display, event->xany.window);
+ QWidget *w = desktop()->screen(scr);
+ QSize oldSize(w->size());
+ w->data->crect.setWidth(DisplayWidth(X11->display, scr));
+ w->data->crect.setHeight(DisplayHeight(X11->display, scr));
+ if (w->size() != oldSize) {
+ QResizeEvent e(w->size(), oldSize);
+ QApplication::sendEvent(w, &e);
+ emit desktop()->resized(scr);
+ }
+ }
+#endif // QT_NO_XRANDR
+
+#ifndef QT_NO_XFIXES
+ if (X11->use_xfixes && event->type == (X11->xfixes_eventbase + XFixesSelectionNotify)) {
+ XFixesSelectionNotifyEvent *req = reinterpret_cast<XFixesSelectionNotifyEvent *>(event);
+
+ // compress all XFixes events related to this selection
+ // we don't want to handle old SelectionNotify events.
+ qt_xfixes_selection_event_data xfixes_event;
+ xfixes_event.selection = req->selection;
+ for (XEvent ev;;) {
+ if (!XCheckIfEvent(X11->display, &ev, &qt_xfixes_scanner, (XPointer)&xfixes_event))
+ break;
+ }
+
+ if (req->selection == ATOM(CLIPBOARD)) {
+ if (qt_xfixes_clipboard_changed(req->owner, req->selection_timestamp)) {
+ emit clipboard()->changed(QClipboard::Clipboard);
+ emit clipboard()->dataChanged();
+ }
+ } else if (req->selection == XA_PRIMARY) {
+ if (qt_xfixes_selection_changed(req->owner, req->selection_timestamp)) {
+ emit clipboard()->changed(QClipboard::Selection);
+ emit clipboard()->selectionChanged();
+ }
+ }
+ }
+#endif // QT_NO_XFIXES
+
+ switch (event->type) {
+
+ case ButtonRelease: // mouse event
+ if (!d->inPopupMode() && !QWidget::mouseGrabber() && pressed_window != widget->internalWinId()
+ && (widget = (QETWidget*) QWidget::find((WId)pressed_window)) == 0)
+ break;
+ // fall through intended
+ case ButtonPress:
+ if (event->xbutton.root != RootWindow(X11->display, widget->x11Info().screen())
+ && ! qt_xdnd_dragging) {
+ while (activePopupWidget())
+ activePopupWidget()->close();
+ return 1;
+ }
+ if (event->type == ButtonPress)
+ qt_net_update_user_time(widget->window(), X11->userTime);
+ // fall through intended
+ case MotionNotify:
+#if !defined(QT_NO_TABLET)
+ if (!qt_tabletChokeMouse) {
+#endif
+ if (widget->testAttribute(Qt::WA_TransparentForMouseEvents)) {
+ QPoint pos(event->xbutton.x, event->xbutton.y);
+ pos = widget->d_func()->mapFromWS(pos);
+ QWidget *window = widget->window();
+ pos = widget->mapTo(window, pos);
+ if (QWidget *child = window->childAt(pos)) {
+ widget = static_cast<QETWidget *>(child);
+ pos = child->mapFrom(window, pos);
+ event->xbutton.x = pos.x();
+ event->xbutton.y = pos.y();
+ }
+ }
+ widget->translateMouseEvent(event);
+#if !defined(QT_NO_TABLET)
+ } else {
+ qt_tabletChokeMouse = false;
+ }
+#endif
+ break;
+
+ case XKeyPress: // keyboard event
+ qt_net_update_user_time(widget->window(), X11->userTime);
+ // fallthrough intended
+ case XKeyRelease:
+ {
+ if (keywidget && keywidget->isEnabled()) { // should always exist
+ // qDebug("sending key event");
+ qt_keymapper_private()->translateKeyEvent(keywidget, event, grabbed);
+ }
+ break;
+ }
+
+ case GraphicsExpose:
+ case Expose: // paint event
+ widget->translatePaintEvent(event);
+ break;
+
+ case ConfigureNotify: // window move/resize event
+ if (event->xconfigure.event == event->xconfigure.window)
+ widget->translateConfigEvent(event);
+ break;
+
+ case XFocusIn: { // got focus
+ if ((widget->windowType() == Qt::Desktop))
+ break;
+ if (d->inPopupMode()) // some delayed focus event to ignore
+ break;
+ if (!widget->isWindow())
+ break;
+ if (event->xfocus.detail != NotifyAncestor &&
+ event->xfocus.detail != NotifyInferior &&
+ event->xfocus.detail != NotifyNonlinear)
+ break;
+ setActiveWindow(widget);
+ if (X11->focus_model == QX11Data::FM_PointerRoot) {
+ // We got real input focus from somewhere, but we were in PointerRoot
+ // mode, so we don't trust this event. Check the focus model to make
+ // sure we know what focus mode we are using...
+ qt_check_focus_model();
+ }
+ }
+ break;
+
+ case XFocusOut: // lost focus
+ if ((widget->windowType() == Qt::Desktop))
+ break;
+ if (!widget->isWindow())
+ break;
+ if (event->xfocus.mode == NotifyGrab) {
+ qt_xfocusout_grab_counter++;
+ break;
+ }
+ if (event->xfocus.detail != NotifyAncestor &&
+ event->xfocus.detail != NotifyNonlinearVirtual &&
+ event->xfocus.detail != NotifyNonlinear)
+ break;
+ if (!d->inPopupMode() && widget == QApplicationPrivate::active_window) {
+ XEvent ev;
+ bool focus_will_change = false;
+ if (XCheckTypedEvent(X11->display, XFocusIn, &ev)) {
+ // we're about to get an XFocusIn, if we know we will
+ // get a new active window, we don't want to set the
+ // active window to 0 now
+ QWidget *w2 = QWidget::find(ev.xany.window);
+ if (w2
+ && w2->windowType() != Qt::Desktop
+ && !d->inPopupMode() // some delayed focus event to ignore
+ && w2->isWindow()
+ && (ev.xfocus.detail == NotifyAncestor
+ || ev.xfocus.detail == NotifyInferior
+ || ev.xfocus.detail == NotifyNonlinear))
+ focus_will_change = true;
+
+ XPutBackEvent(X11->display, &ev);
+ }
+ if (!focus_will_change)
+ setActiveWindow(0);
+ }
+ break;
+
+ case EnterNotify: { // enter window
+ if (QWidget::mouseGrabber() && (!d->inPopupMode() || widget->window() != activePopupWidget()))
+ break;
+ if ((event->xcrossing.mode != NotifyNormal
+ && event->xcrossing.mode != NotifyUngrab)
+ || event->xcrossing.detail == NotifyVirtual
+ || event->xcrossing.detail == NotifyNonlinearVirtual)
+ break;
+ if (event->xcrossing.focus &&
+ !(widget->windowType() == Qt::Desktop) && !widget->isActiveWindow()) {
+ if (X11->focus_model == QX11Data::FM_Unknown) // check focus model
+ qt_check_focus_model();
+ if (X11->focus_model == QX11Data::FM_PointerRoot) // PointerRoot mode
+ setActiveWindow(widget);
+ }
+
+ if (qt_button_down && !d->inPopupMode())
+ break;
+
+ QWidget *alien = widget->childAt(widget->d_func()->mapFromWS(QPoint(event->xcrossing.x,
+ event->xcrossing.y)));
+ QWidget *enter = alien ? alien : widget;
+ QWidget *leave = 0;
+ if (qt_last_mouse_receiver && !qt_last_mouse_receiver->internalWinId())
+ leave = qt_last_mouse_receiver;
+ else
+ leave = QWidget::find(curWin);
+
+ // ### Alien: enter/leave might be wrong here with overlapping siblings
+ // if the enter widget is native and stacked under a non-native widget.
+ QApplicationPrivate::dispatchEnterLeave(enter, leave);
+ curWin = widget->internalWinId();
+ qt_last_mouse_receiver = enter;
+ if (!d->inPopupMode() || widget->window() == activePopupWidget())
+ widget->translateMouseEvent(event); //we don't get MotionNotify, emulate it
+ }
+ break;
+ case LeaveNotify: { // leave window
+ QWidget *mouseGrabber = QWidget::mouseGrabber();
+ if (mouseGrabber && !d->inPopupMode())
+ break;
+ if (curWin && widget->internalWinId() != curWin)
+ break;
+ if ((event->xcrossing.mode != NotifyNormal
+ && event->xcrossing.mode != NotifyUngrab)
+ || event->xcrossing.detail == NotifyInferior)
+ break;
+ if (!(widget->windowType() == Qt::Desktop))
+ widget->translateMouseEvent(event); //we don't get MotionNotify, emulate it
+
+ QWidget* enter = 0;
+ QPoint enterPoint;
+ XEvent ev;
+ while (XCheckMaskEvent(X11->display, EnterWindowMask | LeaveWindowMask , &ev)
+ && !qt_x11EventFilter(&ev)) {
+ QWidget* event_widget = QWidget::find(ev.xcrossing.window);
+ if(event_widget && event_widget->x11Event(&ev))
+ break;
+ if (ev.type == LeaveNotify
+ || (ev.xcrossing.mode != NotifyNormal
+ && ev.xcrossing.mode != NotifyUngrab)
+ || ev.xcrossing.detail == NotifyVirtual
+ || ev.xcrossing.detail == NotifyNonlinearVirtual)
+ continue;
+ enter = event_widget;
+ if (enter)
+ enterPoint = enter->d_func()->mapFromWS(QPoint(ev.xcrossing.x, ev.xcrossing.y));
+ if (ev.xcrossing.focus &&
+ enter && !(enter->windowType() == Qt::Desktop) && !enter->isActiveWindow()) {
+ if (X11->focus_model == QX11Data::FM_Unknown) // check focus model
+ qt_check_focus_model();
+ if (X11->focus_model == QX11Data::FM_PointerRoot) // PointerRoot mode
+ setActiveWindow(enter);
+ }
+ break;
+ }
+
+ if ((! enter || (enter->windowType() == Qt::Desktop)) &&
+ event->xcrossing.focus && widget == QApplicationPrivate::active_window &&
+ X11->focus_model == QX11Data::FM_PointerRoot // PointerRoot mode
+ ) {
+ setActiveWindow(0);
+ }
+
+ if (qt_button_down && !d->inPopupMode())
+ break;
+
+ if (!curWin)
+ QApplicationPrivate::dispatchEnterLeave(widget, 0);
+
+ if (enter) {
+ QWidget *alienEnter = enter->childAt(enterPoint);
+ if (alienEnter)
+ enter = alienEnter;
+ }
+
+ QWidget *leave = qt_last_mouse_receiver ? qt_last_mouse_receiver : widget;
+ QWidget *activePopupWidget = qApp->activePopupWidget();
+
+ if (mouseGrabber && activePopupWidget && leave == activePopupWidget)
+ enter = mouseGrabber;
+ else if (enter != widget && mouseGrabber) {
+ if (!widget->rect().contains(widget->d_func()->mapFromWS(QPoint(event->xcrossing.x,
+ event->xcrossing.y))))
+ break;
+ }
+
+ QApplicationPrivate::dispatchEnterLeave(enter, leave);
+ qt_last_mouse_receiver = enter;
+
+ if (enter && QApplicationPrivate::tryModalHelper(enter, 0)) {
+ QWidget *nativeEnter = enter->internalWinId() ? enter : enter->nativeParentWidget();
+ curWin = nativeEnter->internalWinId();
+ static_cast<QETWidget *>(nativeEnter)->translateMouseEvent(&ev); //we don't get MotionNotify, emulate it
+ } else {
+ curWin = 0;
+ qt_last_mouse_receiver = 0;
+ }
+ }
+ break;
+
+ case UnmapNotify: // window hidden
+ if (widget->isWindow()) {
+ Q_ASSERT(widget->testAttribute(Qt::WA_WState_Created));
+ widget->d_func()->topData()->waitingForMapNotify = 0;
+
+ if (widget->windowType() != Qt::Popup && !widget->testAttribute(Qt::WA_DontShowOnScreen)) {
+ widget->setAttribute(Qt::WA_Mapped, false);
+ if (widget->isVisible()) {
+ widget->d_func()->topData()->spont_unmapped = 1;
+ QHideEvent e;
+ QApplication::sendSpontaneousEvent(widget, &e);
+ widget->d_func()->hideChildren(true);
+ }
+ }
+
+ if (!widget->d_func()->topData()->validWMState && X11->deferred_map.removeAll(widget))
+ widget->doDeferredMap();
+ }
+ break;
+
+ case MapNotify: // window shown
+ if (widget->isWindow()) {
+ widget->d_func()->topData()->waitingForMapNotify = 0;
+
+ if (widget->windowType() != Qt::Popup) {
+ widget->setAttribute(Qt::WA_Mapped);
+ if (widget->d_func()->topData()->spont_unmapped) {
+ widget->d_func()->topData()->spont_unmapped = 0;
+ widget->d_func()->showChildren(true);
+ QShowEvent e;
+ QApplication::sendSpontaneousEvent(widget, &e);
+
+ // show() must have been called on this widget in
+ // order to reach this point, but we could have
+ // cleared these 2 attributes in case something
+ // previously forced us into WithdrawnState
+ // (e.g. kdocker)
+ widget->setAttribute(Qt::WA_WState_ExplicitShowHide, true);
+ widget->setAttribute(Qt::WA_WState_Visible, true);
+ }
+ }
+ }
+ break;
+
+ case ClientMessage: // client message
+ return x11ClientMessage(widget,event,False);
+
+ case ReparentNotify: { // window manager reparents
+ // compress old reparent events to self
+ XEvent ev;
+ while (XCheckTypedWindowEvent(X11->display,
+ widget->effectiveWinId(),
+ ReparentNotify,
+ &ev)) {
+ if (ev.xreparent.window != ev.xreparent.event) {
+ XPutBackEvent(X11->display, &ev);
+ break;
+ }
+ }
+ if (widget->isWindow()) {
+ QTLWExtra *topData = widget->d_func()->topData();
+
+ // store the parent. Useful for many things, embedding for instance.
+ topData->parentWinId = event->xreparent.parent;
+
+ // the widget frame strut should also be invalidated
+ widget->data->fstrut_dirty = 1;
+
+ // work around broken window managers... if we get a
+ // ReparentNotify before the MapNotify, we assume that
+ // we're being managed by a reparenting window
+ // manager.
+ //
+ // however, the WM_STATE property may not have been set
+ // yet, but we are going to assume that it will
+ // be... otherwise we could try to map again after getting
+ // an UnmapNotify... which could then, in turn, trigger a
+ // race in the window manager which causes the window to
+ // disappear when it really should be hidden.
+ if (topData->waitingForMapNotify && !topData->validWMState) {
+ topData->waitingForMapNotify = 0;
+ topData->validWMState = 1;
+ }
+
+ if (X11->focus_model != QX11Data::FM_Unknown) {
+ // toplevel reparented...
+ QWidget *newparent = QWidget::find(event->xreparent.parent);
+ if (! newparent || (newparent->windowType() == Qt::Desktop)) {
+ // we dont' know about the new parent (or we've been
+ // reparented to root), perhaps a window manager
+ // has been (re)started? reset the focus model to unknown
+ X11->focus_model = QX11Data::FM_Unknown;
+ }
+ }
+ }
+ break;
+ }
+ case SelectionRequest: {
+ XSelectionRequestEvent *req = &event->xselectionrequest;
+ if (! req)
+ break;
+
+ if (ATOM(XdndSelection) && req->selection == ATOM(XdndSelection)) {
+ X11->xdndHandleSelectionRequest(req);
+
+ } else if (qt_clipboard) {
+ QClipboardEvent e(reinterpret_cast<QEventPrivate*>(event));
+ QApplication::sendSpontaneousEvent(qt_clipboard, &e);
+ }
+ break;
+ }
+ case SelectionClear: {
+ XSelectionClearEvent *req = &event->xselectionclear;
+ // don't deliver dnd events to the clipboard, it gets confused
+ if (! req || (ATOM(XdndSelection) && req->selection == ATOM(XdndSelection)))
+ break;
+
+ if (qt_clipboard && !X11->use_xfixes) {
+ QClipboardEvent e(reinterpret_cast<QEventPrivate*>(event));
+ QApplication::sendSpontaneousEvent(qt_clipboard, &e);
+ }
+ break;
+ }
+
+ case SelectionNotify: {
+ XSelectionEvent *req = &event->xselection;
+ // don't deliver dnd events to the clipboard, it gets confused
+ if (! req || (ATOM(XdndSelection) && req->selection == ATOM(XdndSelection)))
+ break;
+
+ if (qt_clipboard) {
+ QClipboardEvent e(reinterpret_cast<QEventPrivate*>(event));
+ QApplication::sendSpontaneousEvent(qt_clipboard, &e);
+ }
+ break;
+ }
+ case PropertyNotify:
+ // some properties changed
+ if (event->xproperty.window == QX11Info::appRootWindow(0)) {
+ // root properties for the first screen
+ if (!X11->use_xfixes && event->xproperty.atom == ATOM(_QT_CLIPBOARD_SENTINEL)) {
+ if (qt_check_clipboard_sentinel()) {
+ emit clipboard()->changed(QClipboard::Clipboard);
+ emit clipboard()->dataChanged();
+ }
+ } else if (!X11->use_xfixes && event->xproperty.atom == ATOM(_QT_SELECTION_SENTINEL)) {
+ if (qt_check_selection_sentinel()) {
+ emit clipboard()->changed(QClipboard::Selection);
+ emit clipboard()->selectionChanged();
+ }
+ } else if (QApplicationPrivate::obey_desktop_settings) {
+ if (event->xproperty.atom == ATOM(RESOURCE_MANAGER))
+ qt_set_x11_resources();
+ else if (event->xproperty.atom == ATOM(_QT_SETTINGS_TIMESTAMP))
+ qt_set_x11_resources();
+ }
+ }
+ if (event->xproperty.window == QX11Info::appRootWindow()) {
+ // root properties for the default screen
+ if (event->xproperty.atom == ATOM(_QT_INPUT_ENCODING)) {
+ qt_set_input_encoding();
+ } else if (event->xproperty.atom == ATOM(_NET_SUPPORTED)) {
+ qt_get_net_supported();
+ } else if (event->xproperty.atom == ATOM(_NET_VIRTUAL_ROOTS)) {
+ qt_get_net_virtual_roots();
+ } else if (event->xproperty.atom == ATOM(_NET_WORKAREA)) {
+ qt_desktopwidget_update_workarea();
+ }
+ } else if (widget) {
+ widget->translatePropertyEvent(event);
+ } else {
+ return -1; // don't know this window
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+/*!
+ \fn bool QApplication::x11EventFilter(XEvent *event)
+
+ \warning This virtual function is only implemented under X11.
+
+ If you create an application that inherits QApplication and
+ reimplement this function, you get direct access to all X events
+ that the are received from the X server. The events are passed in
+ the \a event parameter.
+
+ Return true if you want to stop the event from being processed.
+ Return false for normal event dispatching. The default
+ implementation returns false.
+
+ It is only the directly addressed messages that are filtered.
+ You must install an event filter directly on the event
+ dispatcher, which is returned by
+ QAbstractEventDispatcher::instance(), to handle system wide
+ messages.
+
+ \sa x11ProcessEvent()
+*/
+
+bool QApplication::x11EventFilter(XEvent *)
+{
+ return false;
+}
+
+
+
+/*****************************************************************************
+ Modal widgets; Since Xlib has little support for this we roll our own
+ modal widget mechanism.
+ A modal widget without a parent becomes application-modal.
+ A modal widget with a parent becomes modal to its parent and grandparents..
+
+ QApplicationPrivate::enterModal()
+ Enters modal state
+ Arguments:
+ QWidget *widget A modal widget
+
+ QApplicationPrivate::leaveModal()
+ Leaves modal state for a widget
+ Arguments:
+ QWidget *widget A modal widget
+ *****************************************************************************/
+
+bool QApplicationPrivate::modalState()
+{
+ return app_do_modal;
+}
+
+void QApplicationPrivate::enterModal_sys(QWidget *widget)
+{
+ if (!qt_modal_stack)
+ qt_modal_stack = new QWidgetList;
+
+ QWidget *leave = qt_last_mouse_receiver;
+ if (!leave)
+ leave = QWidget::find((WId)curWin);
+ QApplicationPrivate::dispatchEnterLeave(0, leave);
+ qt_modal_stack->insert(0, widget);
+ app_do_modal = true;
+ curWin = 0;
+ qt_last_mouse_receiver = 0;
+}
+
+void QApplicationPrivate::leaveModal_sys(QWidget *widget)
+{
+ if (qt_modal_stack && qt_modal_stack->removeAll(widget)) {
+ if (qt_modal_stack->isEmpty()) {
+ delete qt_modal_stack;
+ qt_modal_stack = 0;
+ QPoint p(QCursor::pos());
+ QWidget* w = QApplication::widgetAt(p.x(), p.y());
+ QWidget *leave = qt_last_mouse_receiver;
+ if (!leave)
+ leave = QWidget::find((WId)curWin);
+ if (QWidget *grabber = QWidget::mouseGrabber()) {
+ w = grabber;
+ if (leave == w)
+ leave = 0;
+ }
+ QApplicationPrivate::dispatchEnterLeave(w, leave); // send synthetic enter event
+ curWin = w ? w->effectiveWinId() : 0;
+ qt_last_mouse_receiver = w;
+ }
+ }
+ app_do_modal = qt_modal_stack != 0;
+}
+
+bool qt_try_modal(QWidget *widget, XEvent *event)
+{
+ if (qt_xdnd_dragging) {
+ // allow mouse events while DnD is active
+ switch (event->type) {
+ case ButtonPress:
+ case ButtonRelease:
+ case MotionNotify:
+ return true;
+ default:
+ break;
+ }
+ }
+
+ // allow mouse release events to be sent to widgets that have been pressed
+ if (event->type == ButtonRelease) {
+ QWidget *alienWidget = widget->childAt(widget->mapFromGlobal(QPoint(event->xbutton.x_root,
+ event->xbutton.y_root)));
+ if (widget == qt_button_down || (alienWidget && alienWidget == qt_button_down))
+ return true;
+ }
+
+ if (QApplicationPrivate::tryModalHelper(widget))
+ return true;
+
+ // disallow mouse/key events
+ switch (event->type) {
+ case ButtonPress:
+ case ButtonRelease:
+ case MotionNotify:
+ case XKeyPress:
+ case XKeyRelease:
+ case EnterNotify:
+ case LeaveNotify:
+ case ClientMessage:
+ return false;
+ default:
+ break;
+ }
+
+ return true;
+}
+
+
+/*****************************************************************************
+ Popup widget mechanism
+
+ openPopup()
+ Adds a widget to the list of popup widgets
+ Arguments:
+ QWidget *widget The popup widget to be added
+
+ closePopup()
+ Removes a widget from the list of popup widgets
+ Arguments:
+ QWidget *widget The popup widget to be removed
+ *****************************************************************************/
+
+
+static int openPopupCount = 0;
+void QApplicationPrivate::openPopup(QWidget *popup)
+{
+ Q_Q(QApplication);
+ openPopupCount++;
+ if (!QApplicationPrivate::popupWidgets) { // create list
+ QApplicationPrivate::popupWidgets = new QWidgetList;
+ }
+ QApplicationPrivate::popupWidgets->append(popup); // add to end of list
+ Display *dpy = X11->display;
+ if (QApplicationPrivate::popupWidgets->count() == 1 && !qt_nograb()){ // grab mouse/keyboard
+ Q_ASSERT(popup->testAttribute(Qt::WA_WState_Created));
+ int r = XGrabKeyboard(dpy, popup->effectiveWinId(), false,
+ GrabModeAsync, GrabModeAsync, X11->time);
+ if ((popupGrabOk = (r == GrabSuccess))) {
+ r = XGrabPointer(dpy, popup->effectiveWinId(), true,
+ (ButtonPressMask | ButtonReleaseMask | ButtonMotionMask
+ | EnterWindowMask | LeaveWindowMask | PointerMotionMask),
+ GrabModeAsync, GrabModeAsync, XNone, XNone, X11->time);
+ if (!(popupGrabOk = (r == GrabSuccess))) {
+ // transfer grab back to the keyboard grabber if any
+ if (QWidgetPrivate::keyboardGrabber != 0)
+ QWidgetPrivate::keyboardGrabber->grabKeyboard();
+ else
+ XUngrabKeyboard(dpy, X11->time);
+ }
+ }
+ }
+
+ // popups are not focus-handled by the window system (the first
+ // popup grabbed the keyboard), so we have to do that manually: A
+ // new popup gets the focus
+ if (popup->focusWidget()) {
+ popup->focusWidget()->setFocus(Qt::PopupFocusReason);
+ } else if (QApplicationPrivate::popupWidgets->count() == 1) { // this was the first popup
+ if (QWidget *fw = QApplication::focusWidget()) {
+ QFocusEvent e(QEvent::FocusOut, Qt::PopupFocusReason);
+ q->sendEvent(fw, &e);
+ }
+ }
+}
+
+void QApplicationPrivate::closePopup(QWidget *popup)
+{
+ Q_Q(QApplication);
+ if (!QApplicationPrivate::popupWidgets)
+ return;
+ QApplicationPrivate::popupWidgets->removeAll(popup);
+ if (popup == qt_popup_down) {
+ qt_button_down = 0;
+ qt_popup_down = 0;
+ }
+ if (QApplicationPrivate::popupWidgets->count() == 0) { // this was the last popup
+ delete QApplicationPrivate::popupWidgets;
+ QApplicationPrivate::popupWidgets = 0;
+ if (!qt_nograb() && popupGrabOk) { // grabbing not disabled
+ Display *dpy = X11->display;
+ if (popup->geometry().contains(QPoint(mouseGlobalXPos, mouseGlobalYPos))
+ || popup->testAttribute(Qt::WA_NoMouseReplay)) {
+ // mouse release event or inside
+ replayPopupMouseEvent = false;
+ } else { // mouse press event
+ mouseButtonPressTime -= 10000; // avoid double click
+ replayPopupMouseEvent = true;
+ }
+ // transfer grab back to mouse grabber if any, otherwise release the grab
+ if (QWidgetPrivate::mouseGrabber != 0)
+ QWidgetPrivate::mouseGrabber->grabMouse();
+ else
+ XUngrabPointer(dpy, X11->time);
+
+ // transfer grab back to keyboard grabber if any, otherwise release the grab
+ if (QWidgetPrivate::keyboardGrabber != 0)
+ QWidgetPrivate::keyboardGrabber->grabKeyboard();
+ else
+ XUngrabKeyboard(dpy, X11->time);
+
+ XFlush(dpy);
+ }
+ if (QApplicationPrivate::active_window) {
+ if (QWidget *fw = QApplicationPrivate::active_window->focusWidget()) {
+ if (fw != QApplication::focusWidget()) {
+ fw->setFocus(Qt::PopupFocusReason);
+ } else {
+ QFocusEvent e(QEvent::FocusIn, Qt::PopupFocusReason);
+ q->sendEvent(fw, &e);
+ }
+ }
+ }
+ } else {
+ // popups are not focus-handled by the window system (the
+ // first popup grabbed the keyboard), so we have to do that
+ // manually: A popup was closed, so the previous popup gets
+ // the focus.
+ QWidget* aw = QApplicationPrivate::popupWidgets->last();
+ if (QWidget *fw = aw->focusWidget())
+ fw->setFocus(Qt::PopupFocusReason);
+
+ // regrab the keyboard and mouse in case 'popup' lost the grab
+ if (QApplicationPrivate::popupWidgets->count() == 1 && !qt_nograb()){ // grab mouse/keyboard
+ Display *dpy = X11->display;
+ Q_ASSERT(aw->testAttribute(Qt::WA_WState_Created));
+ int r = XGrabKeyboard(dpy, aw->effectiveWinId(), false,
+ GrabModeAsync, GrabModeAsync, X11->time);
+ if ((popupGrabOk = (r == GrabSuccess))) {
+ r = XGrabPointer(dpy, aw->effectiveWinId(), true,
+ (ButtonPressMask | ButtonReleaseMask | ButtonMotionMask
+ | EnterWindowMask | LeaveWindowMask | PointerMotionMask),
+ GrabModeAsync, GrabModeAsync, XNone, XNone, X11->time);
+ if (!(popupGrabOk = (r == GrabSuccess))) {
+ // transfer grab back to keyboard grabber
+ if (QWidgetPrivate::keyboardGrabber != 0)
+ QWidgetPrivate::keyboardGrabber->grabKeyboard();
+ else
+ XUngrabKeyboard(dpy, X11->time);
+ }
+ }
+ }
+ }
+}
+
+/*****************************************************************************
+ Event translation; translates X11 events to Qt events
+ *****************************************************************************/
+
+//
+// Mouse event translation
+//
+// Xlib doesn't give mouse double click events, so we generate them by
+// comparing window, time and position between two mouse press events.
+//
+
+static Qt::MouseButtons translateMouseButtons(int s)
+{
+ Qt::MouseButtons ret = 0;
+ if (s & Button1Mask)
+ ret |= Qt::LeftButton;
+ if (s & Button2Mask)
+ ret |= Qt::MidButton;
+ if (s & Button3Mask)
+ ret |= Qt::RightButton;
+ return ret;
+}
+
+Qt::KeyboardModifiers QX11Data::translateModifiers(int s)
+{
+ Qt::KeyboardModifiers ret = 0;
+ if (s & ShiftMask)
+ ret |= Qt::ShiftModifier;
+ if (s & ControlMask)
+ ret |= Qt::ControlModifier;
+ if (s & qt_alt_mask)
+ ret |= Qt::AltModifier;
+ if (s & qt_meta_mask)
+ ret |= Qt::MetaModifier;
+ if (s & qt_mode_switch_mask)
+ ret |= Qt::GroupSwitchModifier;
+ return ret;
+}
+
+bool QETWidget::translateMouseEvent(const XEvent *event)
+{
+ if (!isWindow() && testAttribute(Qt::WA_NativeWindow))
+ Q_ASSERT(internalWinId());
+
+ Q_D(QWidget);
+ QEvent::Type type; // event parameters
+ QPoint pos;
+ QPoint globalPos;
+ Qt::MouseButton button = Qt::NoButton;
+ Qt::MouseButtons buttons;
+ Qt::KeyboardModifiers modifiers;
+ XEvent nextEvent;
+
+ if (qt_sm_blockUserInput) // block user interaction during session management
+ return true;
+
+ if (event->type == MotionNotify) { // mouse move
+ if (event->xmotion.root != RootWindow(X11->display, x11Info().screen()) &&
+ ! qt_xdnd_dragging)
+ return false;
+
+ XMotionEvent lastMotion = event->xmotion;
+ while(XPending(X11->display)) { // compress mouse moves
+ XNextEvent(X11->display, &nextEvent);
+ if (nextEvent.type == ConfigureNotify
+ || nextEvent.type == PropertyNotify
+ || nextEvent.type == Expose
+ || nextEvent.type == GraphicsExpose
+ || nextEvent.type == NoExpose
+ || nextEvent.type == KeymapNotify
+ || ((nextEvent.type == EnterNotify || nextEvent.type == LeaveNotify)
+ && qt_button_down == this)
+ || (nextEvent.type == ClientMessage
+ && nextEvent.xclient.message_type == ATOM(_QT_SCROLL_DONE))) {
+ qApp->x11ProcessEvent(&nextEvent);
+ continue;
+ } else if (nextEvent.type != MotionNotify ||
+ nextEvent.xmotion.window != event->xmotion.window ||
+ nextEvent.xmotion.state != event->xmotion.state) {
+ XPutBackEvent(X11->display, &nextEvent);
+ break;
+ }
+ if (!qt_x11EventFilter(&nextEvent)
+ && !x11Event(&nextEvent)) // send event through filter
+ lastMotion = nextEvent.xmotion;
+ else
+ break;
+ }
+ type = QEvent::MouseMove;
+ pos.rx() = lastMotion.x;
+ pos.ry() = lastMotion.y;
+ pos = d->mapFromWS(pos);
+ globalPos.rx() = lastMotion.x_root;
+ globalPos.ry() = lastMotion.y_root;
+ buttons = translateMouseButtons(lastMotion.state);
+ modifiers = X11->translateModifiers(lastMotion.state);
+ if (qt_button_down && !buttons)
+ qt_button_down = 0;
+ } else if (event->type == EnterNotify || event->type == LeaveNotify) {
+ XEvent *xevent = (XEvent *)event;
+ //unsigned int xstate = event->xcrossing.state;
+ type = QEvent::MouseMove;
+ pos.rx() = xevent->xcrossing.x;
+ pos.ry() = xevent->xcrossing.y;
+ pos = d->mapFromWS(pos);
+ globalPos.rx() = xevent->xcrossing.x_root;
+ globalPos.ry() = xevent->xcrossing.y_root;
+ buttons = translateMouseButtons(xevent->xcrossing.state);
+ modifiers = X11->translateModifiers(xevent->xcrossing.state);
+ if (qt_button_down && !buttons)
+ qt_button_down = 0;
+ if (qt_button_down)
+ return true;
+ } else { // button press or release
+ pos.rx() = event->xbutton.x;
+ pos.ry() = event->xbutton.y;
+ pos = d->mapFromWS(pos);
+ globalPos.rx() = event->xbutton.x_root;
+ globalPos.ry() = event->xbutton.y_root;
+ buttons = translateMouseButtons(event->xbutton.state);
+ modifiers = X11->translateModifiers(event->xbutton.state);
+ switch (event->xbutton.button) {
+ case Button1: button = Qt::LeftButton; break;
+ case Button2: button = Qt::MidButton; break;
+ case Button3: button = Qt::RightButton; break;
+ case Button4:
+ case Button5:
+ case 6:
+ case 7:
+ // the fancy mouse wheel.
+
+ // We are only interested in ButtonPress.
+ if (event->type == ButtonPress){
+ // compress wheel events (the X Server will simply
+ // send a button press for each single notch,
+ // regardless whether the application can catch up
+ // or not)
+ int delta = 1;
+ XEvent xevent;
+ while (XCheckTypedWindowEvent(X11->display, effectiveWinId(), ButtonPress, &xevent)){
+ if (xevent.xbutton.button != event->xbutton.button){
+ XPutBackEvent(X11->display, &xevent);
+ break;
+ }
+ delta++;
+ }
+
+ // the delta is defined as multiples of
+ // WHEEL_DELTA, which is set to 120. Future wheels
+ // may offer a finer-resolution. A positive delta
+ // indicates forward rotation, a negative one
+ // backward rotation respectively.
+ int btn = event->xbutton.button;
+ delta *= 120 * ((btn == Button4 || btn == 6) ? 1 : -1);
+ bool hor = (((btn == Button4 || btn == Button5) && (modifiers & Qt::AltModifier)) ||
+ (btn == 6 || btn == 7));
+ translateWheelEvent(globalPos.x(), globalPos.y(), delta, buttons,
+ modifiers, (hor) ? Qt::Horizontal: Qt::Vertical);
+ }
+ return true;
+ case 8: button = Qt::XButton1; break;
+ case 9: button = Qt::XButton2; break;
+ }
+ if (event->type == ButtonPress) { // mouse button pressed
+ buttons |= button;
+#if defined(Q_OS_IRIX) && !defined(QT_NO_TABLET)
+ QTabletDeviceDataList *tablets = qt_tablet_devices();
+ for (int i = 0; i < tablets->size(); ++i) {
+ QTabletDeviceData &tab = tablets->operator[](i);
+ XEvent myEv;
+ if (XCheckTypedEvent(X11->display, tab.xinput_button_press, &myEv)) {
+ if (translateXinputEvent(&myEv, &tab)) {
+ //Spontaneous event sent. Check if we need to continue.
+ if (qt_tabletChokeMouse) {
+ qt_tabletChokeMouse = false;
+ return false;
+ }
+ }
+ }
+ }
+#endif
+ if (!qt_button_down) {
+ qt_button_down = childAt(pos); //magic for masked widgets
+ if (!qt_button_down)
+ qt_button_down = this;
+ }
+ if (mouseActWindow == event->xbutton.window &&
+ mouseButtonPressed == button &&
+ (long)event->xbutton.time -(long)mouseButtonPressTime
+ < QApplication::doubleClickInterval() &&
+ qAbs(event->xbutton.x - mouseXPos) < 5 &&
+ qAbs(event->xbutton.y - mouseYPos) < 5) {
+ type = QEvent::MouseButtonDblClick;
+ mouseButtonPressTime -= 2000; // no double-click next time
+ } else {
+ type = QEvent::MouseButtonPress;
+ mouseButtonPressTime = event->xbutton.time;
+ }
+ mouseButtonPressed = button; // save event params for
+ mouseXPos = event->xbutton.x; // future double click tests
+ mouseYPos = event->xbutton.y;
+ mouseGlobalXPos = globalPos.x();
+ mouseGlobalYPos = globalPos.y();
+ } else { // mouse button released
+ buttons &= ~button;
+#if defined(Q_OS_IRIX) && !defined(QT_NO_TABLET)
+ QTabletDeviceDataList *tablets = qt_tablet_devices();
+ for (int i = 0; i < tablets->size(); ++i) {
+ QTabletDeviceData &tab = tablets->operator[](i);
+ XEvent myEv;
+ if (XCheckTypedEvent(X11->display, tab.xinput_button_press, &myEv)) {
+ if (translateXinputEvent(&myEv, &tab)) {
+ //Spontaneous event sent. Check if we need to continue.
+ if (qt_tabletChokeMouse) {
+ qt_tabletChokeMouse = false;
+ return false;
+ }
+ }
+ }
+ }
+#endif
+ type = QEvent::MouseButtonRelease;
+ }
+ }
+ mouseActWindow = effectiveWinId(); // save some event params
+ mouseButtonState = buttons;
+ if (type == 0) // don't send event
+ return false;
+
+ if (qApp->d_func()->inPopupMode()) { // in popup mode
+ QWidget *activePopupWidget = qApp->activePopupWidget();
+ QWidget *popup = qApp->activePopupWidget();
+ if (popup != this) {
+ if (event->type == LeaveNotify)
+ return false;
+ if ((windowType() == Qt::Popup) && rect().contains(pos) && 0)
+ popup = this;
+ else // send to last popup
+ pos = popup->mapFromGlobal(globalPos);
+ }
+ bool releaseAfter = false;
+ QWidget *popupChild = popup->childAt(pos);
+
+ if (popup != qt_popup_down){
+ qt_button_down = 0;
+ qt_popup_down = 0;
+ }
+
+ switch (type) {
+ case QEvent::MouseButtonPress:
+ case QEvent::MouseButtonDblClick:
+ qt_button_down = popupChild;
+ qt_popup_down = popup;
+ break;
+ case QEvent::MouseButtonRelease:
+ releaseAfter = true;
+ break;
+ default:
+ break; // nothing for mouse move
+ }
+
+ int oldOpenPopupCount = openPopupCount;
+
+ if (popup->isEnabled()) {
+ // deliver event
+ replayPopupMouseEvent = false;
+ QWidget *receiver = popup;
+ QPoint widgetPos = pos;
+ if (qt_button_down)
+ receiver = qt_button_down;
+ else if (popupChild)
+ receiver = popupChild;
+ if (receiver != popup)
+ widgetPos = receiver->mapFromGlobal(globalPos);
+ QWidget *alien = childAt(mapFromGlobal(globalPos));
+ QMouseEvent e(type, widgetPos, globalPos, button, buttons, modifiers);
+ QApplicationPrivate::sendMouseEvent(receiver, &e, alien, this, &qt_button_down, qt_last_mouse_receiver);
+ } else {
+ // close disabled popups when a mouse button is pressed or released
+ switch (type) {
+ case QEvent::MouseButtonPress:
+ case QEvent::MouseButtonDblClick:
+ case QEvent::MouseButtonRelease:
+ popup->close();
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (qApp->activePopupWidget() != activePopupWidget
+ && replayPopupMouseEvent) {
+ // the active popup was closed, replay the mouse event
+ if (!(windowType() == Qt::Popup)) {
+#if 1
+ qt_button_down = 0;
+#else
+ if (buttons == button)
+ qt_button_down = this;
+ QMouseEvent e(type, mapFromGlobal(globalPos), globalPos, button,
+ buttons, modifiers);
+ QApplication::sendSpontaneousEvent(this, &e);
+
+ if (type == QEvent::MouseButtonPress
+ && button == Qt::RightButton
+ && (openPopupCount == oldOpenPopupCount)) {
+ QContextMenuEvent e(QContextMenuEvent::Mouse, mapFromGlobal(globalPos),
+ globalPos, modifiers);
+ QApplication::sendSpontaneousEvent(this, &e);
+ }
+#endif
+ }
+ replayPopupMouseEvent = false;
+ } else if (type == QEvent::MouseButtonPress
+ && button == Qt::RightButton
+ && (openPopupCount == oldOpenPopupCount)) {
+ QWidget *popupEvent = popup;
+ if (qt_button_down)
+ popupEvent = qt_button_down;
+ else if(popupChild)
+ popupEvent = popupChild;
+ QContextMenuEvent e(QContextMenuEvent::Mouse, pos, globalPos, modifiers);
+ QApplication::sendSpontaneousEvent(popupEvent, &e);
+ }
+
+ if (releaseAfter) {
+ qt_button_down = 0;
+ qt_popup_down = 0;
+ }
+ } else {
+ QWidget *alienWidget = childAt(pos);
+ QWidget *widget = QApplicationPrivate::pickMouseReceiver(this, globalPos, pos, type, buttons,
+ qt_button_down, alienWidget);
+ if (!widget) {
+ if (type == QEvent::MouseButtonRelease)
+ QApplicationPrivate::mouse_buttons &= ~button;
+ return false; // don't send event
+ }
+
+ int oldOpenPopupCount = openPopupCount;
+ QMouseEvent e(type, pos, globalPos, button, buttons, modifiers);
+ QApplicationPrivate::sendMouseEvent(widget, &e, alienWidget, this, &qt_button_down,
+ qt_last_mouse_receiver);
+
+ if (type == QEvent::MouseButtonPress
+ && button == Qt::RightButton
+ && (openPopupCount == oldOpenPopupCount)) {
+ QContextMenuEvent e(QContextMenuEvent::Mouse, pos, globalPos, modifiers);
+ QApplication::sendSpontaneousEvent(widget, &e);
+ }
+ }
+ return true;
+}
+
+
+//
+// Wheel event translation
+//
+bool QETWidget::translateWheelEvent(int global_x, int global_y, int delta,
+ Qt::MouseButtons buttons, Qt::KeyboardModifiers modifiers,
+ Qt::Orientation orient)
+{
+ const QPoint globalPos = QPoint(global_x, global_y);
+ QPoint pos = mapFromGlobal(globalPos);
+ QWidget *widget = childAt(pos);
+ if (!widget)
+ widget = this;
+ else if (!widget->internalWinId())
+ pos = widget->mapFromGlobal(globalPos);
+
+#ifdef ALIEN_DEBUG
+ qDebug() << "QETWidget::translateWheelEvent: receiver:" << widget << "pos:" << pos;
+#endif
+
+ // send the event to the widget or its ancestors
+ {
+ QWidget* popup = qApp->activePopupWidget();
+ if (popup && window() != popup)
+ popup->close();
+ QWheelEvent e(pos, globalPos, delta, buttons, modifiers, orient);
+ if (QApplication::sendSpontaneousEvent(widget, &e))
+ return true;
+ }
+
+ // send the event to the widget that has the focus or its ancestors, if different
+ if (widget != qApp->focusWidget() && (widget = qApp->focusWidget())) {
+ if (widget && !widget->internalWinId())
+ pos = widget->mapFromGlobal(globalPos);
+ QWidget* popup = qApp->activePopupWidget();
+ if (popup && widget != popup)
+ popup->hide();
+ QWheelEvent e(pos, globalPos, delta, buttons, modifiers, orient);
+ if (QApplication::sendSpontaneousEvent(widget, &e))
+ return true;
+ }
+ return false;
+}
+
+
+//
+// XInput Translation Event
+//
+#if !defined (QT_NO_TABLET)
+
+#if !defined (Q_OS_IRIX)
+void fetchWacomToolId(int &deviceType, qint64 &serialId)
+{
+ if (ptrWacomConfigInit == 0) // we actually have the lib
+ return;
+ WACOMCONFIG *config = ptrWacomConfigInit(X11->display, 0);
+ if (config == 0)
+ return;
+ const char *name = "stylus"; // TODO get this from the X config instead (users may have called it differently)
+ WACOMDEVICE *device = ptrWacomConfigOpenDevice (config, name);
+ if (device == 0)
+ return;
+ unsigned keys[1];
+ int serialInt;
+ ptrWacomConfigGetRawParam (device, XWACOM_PARAM_TOOLSERIAL, &serialInt, 1, keys);
+ serialId = serialInt;
+ int toolId;
+ ptrWacomConfigGetRawParam (device, XWACOM_PARAM_TOOLID, &toolId, 1, keys);
+ switch(toolId) {
+ case 0x007: /* Mouse 4D and 2D */
+ case 0x017: /* Intuos3 2D Mouse */
+ case 0x094:
+ case 0x09c:
+ deviceType = QTabletEvent::FourDMouse;
+ break;
+ case 0x096: /* Lens cursor */
+ case 0x097: /* Intuos3 Lens cursor */
+ deviceType = QTabletEvent::Puck;
+ break;
+ case 0x0fa:
+ case 0x81b: /* Intuos3 Classic Pen Eraser */
+ case 0x82a: /* Eraser */
+ case 0x82b: /* Intuos3 Grip Pen Eraser */
+ case 0x85a:
+ case 0x91a:
+ case 0x91b: /* Intuos3 Airbrush Eraser */
+ case 0xd1a:
+ deviceType = QTabletEvent::XFreeEraser;
+ break;
+ case 0x112:
+ case 0x912:
+ case 0x913: /* Intuos3 Airbrush */
+ case 0xd12:
+ deviceType = QTabletEvent::Airbrush;
+ break;
+ case 0x012:
+ case 0x022:
+ case 0x032:
+ case 0x801: /* Intuos3 Inking pen */
+ case 0x812: /* Inking pen */
+ case 0x813: /* Intuos3 Classic Pen */
+ case 0x822: /* Pen */
+ case 0x823: /* Intuos3 Grip Pen */
+ case 0x832: /* Stroke pen */
+ case 0x842:
+ case 0x852:
+ case 0x885: /* Intuos3 Marker Pen */
+ default: /* Unknown tool */
+ deviceType = QTabletEvent::Stylus;
+ }
+
+ /* Close device and return */
+ ptrWacomConfigCloseDevice (device);
+ ptrWacomConfigTerm(config);
+}
+#endif
+
+bool QETWidget::translateXinputEvent(const XEvent *ev, QTabletDeviceData *tablet)
+{
+#if defined (Q_OS_IRIX)
+ // Wacom has put defines in their wacom.h file so it would be quite wise
+ // to use them, need to think of a decent way of not using
+ // it when it doesn't exist...
+ XDeviceState *s;
+ XInputClass *iClass;
+ XValuatorState *vs;
+ int j;
+#endif
+
+ Q_ASSERT(tablet != 0);
+
+ QWidget *w = this;
+ QPoint global,
+ curr;
+ QPointF hiRes;
+ qreal pressure = 0;
+ int xTilt = 0,
+ yTilt = 0,
+ z = 0;
+ qreal tangentialPressure = 0;
+ qreal rotation = 0;
+ int deviceType = QTabletEvent::NoDevice;
+ int pointerType = QTabletEvent::UnknownPointer;
+ XEvent xinputMotionEvent;
+ XEvent mouseMotionEvent;
+ const XDeviceMotionEvent *motion = 0;
+ XDeviceButtonEvent *button = 0;
+ const XProximityNotifyEvent *proximity = 0;
+ QEvent::Type t;
+ Qt::KeyboardModifiers modifiers = 0;
+ bool reinsertMouseEvent = false;
+ bool neverFoundMouseEvent = true;
+ XEvent xinputMotionEventNext;
+ XEvent mouseMotionEventSave;
+#if !defined (Q_OS_IRIX)
+ XID device_id;
+#endif
+
+ if (ev->type == tablet->xinput_motion) {
+ motion = reinterpret_cast<const XDeviceMotionEvent*>(ev);
+ for (;;) {
+ // get the corresponding mouseMotionEvent for motion
+ if (XCheckTypedWindowEvent(X11->display, internalWinId(), MotionNotify, &mouseMotionEvent)) {
+ mouseMotionEventSave = mouseMotionEvent;
+ reinsertMouseEvent = true;
+ neverFoundMouseEvent = false;
+
+ if (mouseMotionEvent.xmotion.time > motion->time) {
+ XEvent xinputMotionEventLoop = *ev;
+
+ // xinput event is older than the mouse event --> search for the corresponding xinput event for the given mouse event
+ while (mouseMotionEvent.xmotion.time > (reinterpret_cast<const XDeviceMotionEvent*>(&xinputMotionEventLoop))->time) {
+ if (XCheckTypedWindowEvent(X11->display, internalWinId(), tablet->xinput_motion, &xinputMotionEventLoop)) {
+ xinputMotionEvent = xinputMotionEventLoop;
+ }
+ else {
+ break;
+ }
+ }
+ motion = reinterpret_cast<const XDeviceMotionEvent*>(&xinputMotionEvent);
+ }
+
+ // get the next xinputMotionEvent, for the next loop run
+ if (!XCheckTypedWindowEvent(X11->display, internalWinId(), tablet->xinput_motion, &xinputMotionEventNext)) {
+ XPutBackEvent(X11->display, &mouseMotionEvent);
+ reinsertMouseEvent = false;
+ break;
+ }
+
+ if (mouseMotionEvent.xmotion.time != motion->time) {
+ // reinsert in order
+ if (mouseMotionEvent.xmotion.time >= motion->time) {
+ XPutBackEvent(X11->display, &mouseMotionEvent);
+ XPutBackEvent(X11->display, &xinputMotionEventNext);
+ // next entry in queue is xinputMotionEventNext
+ }
+ else {
+ XPutBackEvent(X11->display, &xinputMotionEventNext);
+ XPutBackEvent(X11->display, &mouseMotionEvent);
+ // next entry in queue is mouseMotionEvent
+ }
+ reinsertMouseEvent = false;
+ break;
+ }
+ }
+ else {
+ break;
+ }
+
+ xinputMotionEvent = xinputMotionEventNext;
+ motion = (reinterpret_cast<const XDeviceMotionEvent*>(&xinputMotionEvent));
+ }
+
+ if (reinsertMouseEvent) {
+ XPutBackEvent(X11->display, &mouseMotionEventSave);
+ }
+
+ if (neverFoundMouseEvent) {
+ XEvent xinputMotionEventLoop;
+ bool eventFound = false;
+ // xinput event without mouseMotionEvent --> search the newest xinputMotionEvent
+ while (XCheckTypedWindowEvent(X11->display, internalWinId(), tablet->xinput_motion, &xinputMotionEventLoop)) {
+ xinputMotionEvent = xinputMotionEventLoop;
+ eventFound = true;
+ }
+ if (eventFound) motion = reinterpret_cast<const XDeviceMotionEvent*>(&xinputMotionEvent);
+ }
+
+ t = QEvent::TabletMove;
+ global = QPoint(motion->x_root, motion->y_root);
+ curr = QPoint(motion->x, motion->y);
+#if !defined (Q_OS_IRIX)
+ device_id = motion->deviceid;
+#endif
+ } else if (ev->type == tablet->xinput_button_press || ev->type == tablet->xinput_button_release) {
+ if (ev->type == tablet->xinput_button_press) {
+ t = QEvent::TabletPress;
+ } else {
+ t = QEvent::TabletRelease;
+ }
+ button = (XDeviceButtonEvent*)ev;
+
+ global = QPoint(button->x_root, button->y_root);
+ curr = QPoint(button->x, button->y);
+#if !defined (Q_OS_IRIX)
+ device_id = button->deviceid;
+#endif
+ } else { // Proximity
+ if (ev->type == tablet->xinput_proximity_in)
+ t = QEvent::TabletEnterProximity;
+ else
+ t = QEvent::TabletLeaveProximity;
+ proximity = (const XProximityNotifyEvent*)ev;
+#if !defined (Q_OS_IRIX)
+ device_id = proximity->deviceid;
+#endif
+ }
+
+ qint64 uid = 0;
+#if defined (Q_OS_IRIX)
+ QRect screenArea = qApp->desktop()->screenGeometry(this);
+ s = XQueryDeviceState(X11->display, static_cast<XDevice *>(tablet->device));
+ if (!s)
+ return false;
+ iClass = s->data;
+ for (j = 0; j < s->num_classes; j++) {
+ if (iClass->c_class == ValuatorClass) {
+ vs = reinterpret_cast<XValuatorState *>(iClass);
+ // figure out what device we have, based on bitmasking...
+ if (vs->valuators[WAC_TRANSDUCER_I]
+ & WAC_TRANSDUCER_PROX_MSK) {
+ switch (vs->valuators[WAC_TRANSDUCER_I]
+ & WAC_TRANSDUCER_MSK) {
+ case WAC_PUCK_ID:
+ pointerType = QTabletEvent::Puck;
+ break;
+ case WAC_STYLUS_ID:
+ pointerType = QTabletEvent::Pen;
+ break;
+ case WAC_ERASER_ID:
+ pointerType = QTabletEvent::Eraser;
+ break;
+ }
+ // Get a Unique Id for the device, Wacom gives us this ability
+ uid = vs->valuators[WAC_TRANSDUCER_I] & WAC_TRANSDUCER_ID_MSK;
+ uid = (uid << 24) | vs->valuators[WAC_SERIAL_NUM_I];
+ switch (WAC_TRANSDUCER_I & 0x0F0600) {
+ case 0x080200:
+ deviceType = QTabletEvent::Stylus;
+ break;
+ case 0x090200:
+ deviceType = QTabletEvent::Airbrush;
+ break;
+ case 0x000400:
+ deviceType = QTabletEvent::FourDMouse;
+ break;
+ case 0x000600:
+ deviceType = QTabletEvent::Puck;
+ break;
+ case 0x080400:
+ deviceType = QTabletEvent::RotationStylus;
+ break;
+ }
+ } else {
+ pointerType = QTabletEvent::UnknownPointer;
+ deviceType = QTabletEvent::NoDevice;
+ uid = 0;
+ }
+
+ if (!proximity) {
+ // apparently Wacom needs a cast for the +/- values to make sense
+ xTilt = short(vs->valuators[WAC_XTILT_I]);
+ yTilt = short(vs->valuators[WAC_YTILT_I]);
+ pressure = vs->valuators[WAC_PRESSURE_I];
+ if (deviceType == QTabletEvent::FourDMouse
+ || deviceType == QTabletEvent::RotationStylus) {
+ rotation = vs->valuators[WAC_ROTATION_I] / 64.0;
+ if (deviceType == QTabletEvent::FourDMouse)
+ z = vs->valuators[WAC_ZCOORD_I];
+ } else if (deviceType == QTabletEvent::Airbrush) {
+ tangentialPressure = vs->valuators[WAC_TAN_PRESSURE_I]
+ / qreal(tablet->maxTanPressure - tablet->minTanPressure);
+ }
+
+ hiRes = tablet->scaleCoord(vs->valuators[WAC_XCOORD_I], vs->valuators[WAC_YCOORD_I],
+ screenArea.x(), screenArea.width(),
+ screenArea.y(), screenArea.height());
+ }
+ break;
+ }
+ iClass = reinterpret_cast<XInputClass*>(reinterpret_cast<char*>(iClass) + iClass->length);
+ }
+ XFreeDeviceState(s);
+#else
+ QTabletDeviceDataList *tablet_list = qt_tablet_devices();
+ for (int i = 0; i < tablet_list->size(); ++i) {
+ const QTabletDeviceData &t = tablet_list->at(i);
+ if (device_id == static_cast<XDevice *>(t.device)->device_id) {
+ deviceType = t.deviceType;
+ if (t.deviceType == QTabletEvent::XFreeEraser) {
+ deviceType = QTabletEvent::Stylus;
+ pointerType = QTabletEvent::Eraser;
+ } else if (t.deviceType == QTabletEvent::Stylus) {
+ pointerType = QTabletEvent::Pen;
+ }
+ break;
+ }
+ }
+
+ fetchWacomToolId(deviceType, uid);
+
+ QRect screenArea = qApp->desktop()->rect();
+ if (motion) {
+ xTilt = (short) motion->axis_data[3];
+ yTilt = (short) motion->axis_data[4];
+ rotation = ((short) motion->axis_data[5]) / 64.0;
+ pressure = (short) motion->axis_data[2];
+ modifiers = X11->translateModifiers(motion->state);
+ hiRes = tablet->scaleCoord(motion->axis_data[0], motion->axis_data[1],
+ screenArea.x(), screenArea.width(),
+ screenArea.y(), screenArea.height());
+ } else if (button) {
+ xTilt = (short) button->axis_data[3];
+ yTilt = (short) button->axis_data[4];
+ rotation = ((short) button->axis_data[5]) / 64.0;
+ pressure = (short) button->axis_data[2];
+ modifiers = X11->translateModifiers(button->state);
+ hiRes = tablet->scaleCoord(button->axis_data[0], button->axis_data[1],
+ screenArea.x(), screenArea.width(),
+ screenArea.y(), screenArea.height());
+ } else if (proximity) {
+ pressure = 0;
+ modifiers = 0;
+ }
+ if (deviceType == QTabletEvent::Airbrush) {
+ tangentialPressure = rotation;
+ rotation = 0.;
+ }
+#endif
+
+ QWidget *child = w->childAt(curr);
+ if (child) {
+ w = child;
+ curr = w->mapFromGlobal(global);
+ }
+
+ if (t == QEvent::TabletPress) {
+ tablet->widgetToGetPress = w;
+ } else if (t == QEvent::TabletRelease && tablet->widgetToGetPress) {
+ w = tablet->widgetToGetPress;
+ curr = w->mapFromGlobal(global);
+ tablet->widgetToGetPress = 0;
+ }
+
+ QTabletEvent e(t, curr, global, hiRes,
+ deviceType, pointerType,
+ qreal(pressure / qreal(tablet->maxPressure - tablet->minPressure)),
+ xTilt, yTilt, tangentialPressure, rotation, z, modifiers, uid);
+ if (proximity)
+ QApplication::sendSpontaneousEvent(qApp, &e);
+ else
+ QApplication::sendSpontaneousEvent(w, &e);
+ return true;
+}
+#endif
+
+bool QETWidget::translatePropertyEvent(const XEvent *event)
+{
+ Q_D(QWidget);
+ if (!isWindow()) return true;
+
+ Atom ret;
+ int format, e;
+ unsigned char *data = 0;
+ unsigned long nitems, after;
+
+ if (event->xproperty.atom == ATOM(_KDE_NET_WM_FRAME_STRUT)) {
+ this->data->fstrut_dirty = 1;
+
+ if (event->xproperty.state == PropertyNewValue) {
+ e = XGetWindowProperty(X11->display, event->xproperty.window, ATOM(_KDE_NET_WM_FRAME_STRUT),
+ 0, 4, // struts are 4 longs
+ False, XA_CARDINAL, &ret, &format, &nitems, &after, &data);
+
+ if (e == Success && ret == XA_CARDINAL &&
+ format == 32 && nitems == 4) {
+ long *strut = (long *) data;
+ d->topData()->frameStrut.setCoords(strut[0], strut[2], strut[1], strut[3]);
+ this->data->fstrut_dirty = 0;
+ }
+ }
+ } else if (event->xproperty.atom == ATOM(_NET_WM_STATE)) {
+ bool max = false;
+ bool full = false;
+ Qt::WindowStates oldState = Qt::WindowStates(this->data->window_state);
+
+ if (event->xproperty.state == PropertyNewValue) {
+ // using length of 1024 should be safe for all current and
+ // possible NET states...
+ e = XGetWindowProperty(X11->display, event->xproperty.window, ATOM(_NET_WM_STATE), 0, 1024,
+ False, XA_ATOM, &ret, &format, &nitems, &after, &data);
+
+ if (e == Success && ret == XA_ATOM && format == 32 && nitems > 0) {
+ Atom *states = (Atom *) data;
+
+ unsigned long i;
+ uint maximized = 0;
+ for (i = 0; i < nitems; i++) {
+ if (states[i] == ATOM(_NET_WM_STATE_MAXIMIZED_VERT))
+ maximized |= 1;
+ else if (states[i] == ATOM(_NET_WM_STATE_MAXIMIZED_HORZ))
+ maximized |= 2;
+ else if (states[i] == ATOM(_NET_WM_STATE_FULLSCREEN))
+ full = true;
+ }
+ if (maximized == 3) {
+ // only set maximized if both horizontal and vertical properties are set
+ max = true;
+ }
+ }
+ }
+
+ bool send_event = false;
+
+ if (X11->isSupportedByWM(ATOM(_NET_WM_STATE_MAXIMIZED_VERT))
+ && X11->isSupportedByWM(ATOM(_NET_WM_STATE_MAXIMIZED_HORZ))) {
+ if (max && !isMaximized()) {
+ this->data->window_state = this->data->window_state | Qt::WindowMaximized;
+ send_event = true;
+ } else if (!max && isMaximized()) {
+ this->data->window_state &= ~Qt::WindowMaximized;
+ send_event = true;
+ }
+ }
+
+ if (X11->isSupportedByWM(ATOM(_NET_WM_STATE_FULLSCREEN))) {
+ if (full && !isFullScreen()) {
+ this->data->window_state = this->data->window_state | Qt::WindowFullScreen;
+ send_event = true;
+ } else if (!full && isFullScreen()) {
+ this->data->window_state &= ~Qt::WindowFullScreen;
+ send_event = true;
+ }
+ }
+
+ if (send_event) {
+ QWindowStateChangeEvent e(oldState);
+ QApplication::sendSpontaneousEvent(this, &e);
+ }
+ } else if (event->xproperty.atom == ATOM(WM_STATE)) {
+ // the widget frame strut should also be invalidated
+ this->data->fstrut_dirty = 1;
+
+ if (event->xproperty.state == PropertyDelete) {
+ // the window manager has removed the WM State property,
+ // so it is now in the withdrawn state (ICCCM 4.1.3.1) and
+ // we are free to reuse this window
+ d->topData()->parentWinId = 0;
+ d->topData()->validWMState = 0;
+ // map the window if we were waiting for a transition to
+ // withdrawn
+ if (X11->deferred_map.removeAll(this)) {
+ doDeferredMap();
+ } else if (isVisible()
+ && !testAttribute(Qt::WA_Mapped)
+ && !testAttribute(Qt::WA_OutsideWSRange)) {
+ // so that show() will work again. As stated in the
+ // ICCCM section 4.1.4: "Only the client can effect a
+ // transition into or out of the Withdrawn state.",
+ // but apparently this particular window manager
+ // doesn't seem to care
+ setAttribute(Qt::WA_WState_ExplicitShowHide, false);
+ setAttribute(Qt::WA_WState_Visible, false);
+ }
+ } else {
+ // the window manager has changed the WM State property...
+ // we are wanting to see if we are withdrawn so that we
+ // can reuse this window...
+ e = XGetWindowProperty(X11->display, internalWinId(), ATOM(WM_STATE), 0, 2, False,
+ ATOM(WM_STATE), &ret, &format, &nitems, &after, &data);
+
+ if (e == Success && ret == ATOM(WM_STATE) && format == 32 && nitems > 0) {
+ long *state = (long *) data;
+ switch (state[0]) {
+ case WithdrawnState:
+ // if we are in the withdrawn state, we are free
+ // to reuse this window provided we remove the
+ // WM_STATE property (ICCCM 4.1.3.1)
+ XDeleteProperty(X11->display, internalWinId(), ATOM(WM_STATE));
+
+ // set the parent id to zero, so that show() will
+ // work again
+ d->topData()->parentWinId = 0;
+ d->topData()->validWMState = 0;
+ // map the window if we were waiting for a
+ // transition to withdrawn
+ if (X11->deferred_map.removeAll(this)) {
+ doDeferredMap();
+ } else if (isVisible()
+ && !testAttribute(Qt::WA_Mapped)
+ && !testAttribute(Qt::WA_OutsideWSRange)) {
+ // so that show() will work again. As stated
+ // in the ICCCM section 4.1.4: "Only the
+ // client can effect a transition into or out
+ // of the Withdrawn state.", but apparently
+ // this particular window manager doesn't seem
+ // to care
+ setAttribute(Qt::WA_WState_ExplicitShowHide, false);
+ setAttribute(Qt::WA_WState_Visible, false);
+ }
+ break;
+
+ case IconicState:
+ d->topData()->validWMState = 1;
+ if (!isMinimized()) {
+ // window was minimized
+ this->data->window_state = this->data->window_state | Qt::WindowMinimized;
+ QWindowStateChangeEvent e(Qt::WindowStates(this->data->window_state & ~Qt::WindowMinimized));
+ QApplication::sendSpontaneousEvent(this, &e);
+ }
+ break;
+
+ default:
+ d->topData()->validWMState = 1;
+ if (isMinimized()) {
+ // window was un-minimized
+ this->data->window_state &= ~Qt::WindowMinimized;
+ QWindowStateChangeEvent e(Qt::WindowStates(this->data->window_state | Qt::WindowMinimized));
+ QApplication::sendSpontaneousEvent(this, &e);
+ }
+ break;
+ }
+ }
+ }
+ } else if (event->xproperty.atom == ATOM(_NET_WM_WINDOW_OPACITY)) {
+ // the window opacity was changed
+ if (event->xproperty.state == PropertyNewValue) {
+ e = XGetWindowProperty(event->xclient.display,
+ event->xclient.window,
+ ATOM(_NET_WM_WINDOW_OPACITY),
+ 0, 1, False, XA_CARDINAL,
+ &ret, &format, &nitems, &after, &data);
+
+ if (e == Success && ret == XA_CARDINAL && format == 32 && nitems == 1
+ && after == 0 && data) {
+ ulong value = *(ulong*)(data);
+ d->topData()->opacity = uint(value >> 24);
+ }
+ } else
+ d->topData()->opacity = 255;
+ }
+
+ if (data)
+ XFree(data);
+
+ return true;
+}
+
+//
+// Paint event translation
+//
+// When receiving many expose events, we compress them (union of all expose
+// rectangles) into one event which is sent to the widget.
+
+struct PaintEventInfo {
+ Window window;
+};
+
+#if defined(Q_C_CALLBACKS)
+extern "C" {
+#endif
+
+static Bool isPaintOrScrollDoneEvent(Display *, XEvent *ev, XPointer a)
+{
+ PaintEventInfo *info = (PaintEventInfo *)a;
+ if (ev->type == Expose || ev->type == GraphicsExpose
+ || (ev->type == ClientMessage && ev->xclient.message_type == ATOM(_QT_SCROLL_DONE)))
+ {
+ if (ev->xexpose.window == info->window)
+ return True;
+ }
+ return False;
+}
+
+#if defined(Q_C_CALLBACKS)
+}
+#endif
+
+
+
+static
+bool translateBySips(QWidget* that, QRect& paintRect)
+{
+ int dx=0, dy=0;
+ int sips=0;
+ for (int i = 0; i < X11->sip_list.size(); ++i) {
+ const QX11Data::ScrollInProgress &sip = X11->sip_list.at(i);
+ if (sip.scrolled_widget == that) {
+ if (sips) {
+ dx += sip.dx;
+ dy += sip.dy;
+ }
+ sips++;
+ }
+ }
+ if (sips > 1) {
+ paintRect.translate(dx, dy);
+ return true;
+ }
+ return false;
+}
+
+void QETWidget::translatePaintEvent(const XEvent *event)
+{
+ if (!isWindow() && testAttribute(Qt::WA_NativeWindow))
+ Q_ASSERT(internalWinId());
+
+ Q_D(QWidget);
+ QRect paintRect(event->xexpose.x, event->xexpose.y,
+ event->xexpose.width, event->xexpose.height);
+ XEvent xevent;
+ PaintEventInfo info;
+ info.window = internalWinId();
+ translateBySips(this, paintRect);
+ paintRect = d->mapFromWS(paintRect);
+
+ QRegion paintRegion = paintRect;
+
+ // WARNING: this is O(number_of_events * number_of_matching_events)
+ while (XCheckIfEvent(X11->display,&xevent,isPaintOrScrollDoneEvent,
+ (XPointer)&info) &&
+ !qt_x11EventFilter(&xevent) &&
+ !x11Event(&xevent)) // send event through filter
+ {
+ if (xevent.type == Expose || xevent.type == GraphicsExpose) {
+ QRect exposure(xevent.xexpose.x,
+ xevent.xexpose.y,
+ xevent.xexpose.width,
+ xevent.xexpose.height);
+ translateBySips(this, exposure);
+ exposure = d->mapFromWS(exposure);
+ paintRegion |= exposure;
+ } else {
+ translateScrollDoneEvent(&xevent);
+ }
+ }
+
+ if (!paintRegion.isEmpty() && !testAttribute(Qt::WA_WState_ConfigPending))
+ d->syncBackingStore(paintRegion);
+}
+
+//
+// Scroll-done event translation.
+//
+
+bool QETWidget::translateScrollDoneEvent(const XEvent *event)
+{
+ long id = event->xclient.data.l[0];
+
+ // Remove any scroll-in-progress record for the given id.
+ for (int i = 0; i < X11->sip_list.size(); ++i) {
+ const QX11Data::ScrollInProgress &sip = X11->sip_list.at(i);
+ if (sip.id == id) {
+ X11->sip_list.removeAt(i);
+ return true;
+ }
+ }
+
+ return false;
+}
+
+//
+// ConfigureNotify (window move and resize) event translation
+
+bool QETWidget::translateConfigEvent(const XEvent *event)
+{
+ Q_ASSERT((!isWindow() && !testAttribute(Qt::WA_NativeWindow)) ? internalWinId() : true);
+
+ Q_D(QWidget);
+ bool wasResize = testAttribute(Qt::WA_WState_ConfigPending); // set in QWidget::setGeometry_sys()
+ setAttribute(Qt::WA_WState_ConfigPending, false);
+
+ if (testAttribute(Qt::WA_OutsideWSRange)) {
+ // discard events for windows that have a geometry X can't handle
+ XEvent xevent;
+ while (XCheckTypedWindowEvent(X11->display,internalWinId(), ConfigureNotify,&xevent) &&
+ !qt_x11EventFilter(&xevent) &&
+ !x11Event(&xevent)) // send event through filter
+ ;
+ return true;
+ }
+
+ const QSize oldSize = size();
+
+ if (isWindow()) {
+ QPoint newCPos(geometry().topLeft());
+ QSize newSize(event->xconfigure.width, event->xconfigure.height);
+
+ bool trust = isVisible()
+ && (d->topData()->parentWinId == XNone ||
+ d->topData()->parentWinId == QX11Info::appRootWindow());
+
+ if (event->xconfigure.send_event || trust) {
+ // if a ConfigureNotify comes from a real sendevent request, we can
+ // trust its values.
+ newCPos.rx() = event->xconfigure.x + event->xconfigure.border_width;
+ newCPos.ry() = event->xconfigure.y + event->xconfigure.border_width;
+ }
+
+ if (isVisible())
+ QApplication::syncX();
+
+ if (d->extra->compress_events) {
+ // ConfigureNotify compression for faster opaque resizing
+ XEvent otherEvent;
+ while (XCheckTypedWindowEvent(X11->display, internalWinId(), ConfigureNotify,
+ &otherEvent)) {
+ if (qt_x11EventFilter(&otherEvent))
+ continue;
+
+ if (x11Event(&otherEvent))
+ continue;
+
+ if (otherEvent.xconfigure.event != otherEvent.xconfigure.window)
+ continue;
+
+ newSize.setWidth(otherEvent.xconfigure.width);
+ newSize.setHeight(otherEvent.xconfigure.height);
+
+ if (otherEvent.xconfigure.send_event || trust) {
+ newCPos.rx() = otherEvent.xconfigure.x +
+ otherEvent.xconfigure.border_width;
+ newCPos.ry() = otherEvent.xconfigure.y +
+ otherEvent.xconfigure.border_width;
+ }
+ }
+ }
+
+ QRect cr (geometry());
+ if (newCPos != cr.topLeft()) { // compare with cpos (exluding frame)
+ QPoint oldPos = geometry().topLeft();
+ cr.moveTopLeft(newCPos);
+ data->crect = cr;
+ if (isVisible()) {
+ QMoveEvent e(newCPos, oldPos); // pos (including frame), not cpos
+ QApplication::sendSpontaneousEvent(this, &e);
+ } else {
+ setAttribute(Qt::WA_PendingMoveEvent, true);
+ }
+ }
+ if (newSize != cr.size()) { // size changed
+ cr.setSize(newSize);
+ data->crect = cr;
+
+ uint old_state = data->window_state;
+ if (!X11->isSupportedByWM(ATOM(_NET_WM_STATE_MAXIMIZED_VERT))
+ && !X11->isSupportedByWM(ATOM(_NET_WM_STATE_MAXIMIZED_HORZ)))
+ data->window_state &= ~Qt::WindowMaximized;
+ if (!X11->isSupportedByWM(ATOM(_NET_WM_STATE_FULLSCREEN)))
+ data->window_state &= ~Qt::WindowFullScreen;
+
+ if (old_state != data->window_state) {
+ QWindowStateChangeEvent e((Qt::WindowStates) old_state);
+ QApplication::sendEvent(this, &e);
+ }
+
+ if (!isVisible())
+ setAttribute(Qt::WA_PendingResizeEvent, true);
+ wasResize = true;
+ }
+
+ } else {
+ XEvent xevent;
+ while (XCheckTypedWindowEvent(X11->display,internalWinId(), ConfigureNotify,&xevent) &&
+ !qt_x11EventFilter(&xevent) &&
+ !x11Event(&xevent)) // send event through filter
+ ;
+ }
+
+ if (wasResize) {
+ static bool slowResize = qgetenv("QT_SLOW_TOPLEVEL_RESIZE").toInt();
+ if (d->extra->compress_events && !slowResize && !data->in_show && isVisible()) {
+ QApplication::syncX();
+ XEvent otherEvent;
+ while (XCheckTypedWindowEvent(X11->display, internalWinId(), ConfigureNotify, &otherEvent)
+ && !qt_x11EventFilter(&otherEvent) && !x11Event(&otherEvent)
+ && otherEvent.xconfigure.event == otherEvent.xconfigure.window) {
+ data->crect.setWidth(otherEvent.xconfigure.width);
+ data->crect.setHeight(otherEvent.xconfigure.height);
+ }
+ }
+
+ if (isVisible() && data->crect.size() != oldSize) {
+ Q_ASSERT(d->extra->topextra);
+ QWidgetBackingStore *bs = d->extra->topextra->backingStore;
+ const bool hasStaticContents = bs && bs->hasStaticContents();
+ // If we have a backing store with static contents, we have to disable the top-level
+ // resize optimization in order to get invalidated regions for resized widgets.
+ // The optimization discards all invalidateBuffer() calls since we're going to
+ // repaint everything anyways, but that's not the case with static contents.
+ if (!slowResize && !hasStaticContents)
+ d->extra->topextra->inTopLevelResize = true;
+ QResizeEvent e(data->crect.size(), oldSize);
+ QApplication::sendSpontaneousEvent(this, &e);
+ }
+
+ const bool waitingForMapNotify = d->extra->topextra && d->extra->topextra->waitingForMapNotify;
+ if (!waitingForMapNotify) {
+ if (d->paintOnScreen()) {
+ QRegion updateRegion(rect());
+ if (testAttribute(Qt::WA_StaticContents))
+ updateRegion -= QRect(0, 0, oldSize.width(), oldSize.height());
+ d->syncBackingStore(updateRegion);
+ } else {
+ d->syncBackingStore();
+ }
+ }
+
+ if (d->extra && d->extra->topextra)
+ d->extra->topextra->inTopLevelResize = false;
+ }
+ return true;
+}
+
+//
+// Close window event translation.
+//
+bool QETWidget::translateCloseEvent(const XEvent *)
+{
+ Q_D(QWidget);
+ return d->close_helper(QWidgetPrivate::CloseWithSpontaneousEvent);
+}
+
+
+void QApplication::setCursorFlashTime(int msecs)
+{
+ QApplicationPrivate::cursor_flash_time = msecs;
+}
+
+int QApplication::cursorFlashTime()
+{
+ return QApplicationPrivate::cursor_flash_time;
+}
+
+void QApplication::setDoubleClickInterval(int ms)
+{
+ QApplicationPrivate::mouse_double_click_time = ms;
+}
+
+int QApplication::doubleClickInterval()
+{
+ return QApplicationPrivate::mouse_double_click_time;
+}
+
+void QApplication::setKeyboardInputInterval(int ms)
+{
+ QApplicationPrivate::keyboard_input_time = ms;
+}
+
+int QApplication::keyboardInputInterval()
+{
+ return QApplicationPrivate::keyboard_input_time;
+}
+
+void QApplication::setWheelScrollLines(int n)
+{
+ QApplicationPrivate::wheel_scroll_lines = n;
+}
+
+int QApplication::wheelScrollLines()
+{
+ return QApplicationPrivate::wheel_scroll_lines;
+}
+
+void QApplication::setEffectEnabled(Qt::UIEffect effect, bool enable)
+{
+ switch (effect) {
+ case Qt::UI_AnimateMenu:
+ if (enable) QApplicationPrivate::fade_menu = false;
+ QApplicationPrivate::animate_menu = enable;
+ break;
+ case Qt::UI_FadeMenu:
+ if (enable)
+ QApplicationPrivate::animate_menu = true;
+ QApplicationPrivate::fade_menu = enable;
+ break;
+ case Qt::UI_AnimateCombo:
+ QApplicationPrivate::animate_combo = enable;
+ break;
+ case Qt::UI_AnimateTooltip:
+ if (enable) QApplicationPrivate::fade_tooltip = false;
+ QApplicationPrivate::animate_tooltip = enable;
+ break;
+ case Qt::UI_FadeTooltip:
+ if (enable)
+ QApplicationPrivate::animate_tooltip = true;
+ QApplicationPrivate::fade_tooltip = enable;
+ break;
+ case Qt::UI_AnimateToolBox:
+ QApplicationPrivate::animate_toolbox = enable;
+ break;
+ default:
+ QApplicationPrivate::animate_ui = enable;
+ break;
+ }
+}
+
+bool QApplication::isEffectEnabled(Qt::UIEffect effect)
+{
+ if (QColormap::instance().depth() < 16 || !QApplicationPrivate::animate_ui)
+ return false;
+
+ switch(effect) {
+ case Qt::UI_AnimateMenu:
+ return QApplicationPrivate::animate_menu;
+ case Qt::UI_FadeMenu:
+ return QApplicationPrivate::fade_menu;
+ case Qt::UI_AnimateCombo:
+ return QApplicationPrivate::animate_combo;
+ case Qt::UI_AnimateTooltip:
+ return QApplicationPrivate::animate_tooltip;
+ case Qt::UI_FadeTooltip:
+ return QApplicationPrivate::fade_tooltip;
+ case Qt::UI_AnimateToolBox:
+ return QApplicationPrivate::animate_toolbox;
+ default:
+ return QApplicationPrivate::animate_ui;
+ }
+}
+
+/*****************************************************************************
+ Session management support
+ *****************************************************************************/
+
+#ifndef QT_NO_SESSIONMANAGER
+
+QT_BEGIN_INCLUDE_NAMESPACE
+#include <X11/SM/SMlib.h>
+QT_END_INCLUDE_NAMESPACE
+
+class QSessionManagerPrivate : public QObjectPrivate
+{
+public:
+ QSessionManagerPrivate(QSessionManager* mgr, QString& id, QString& key)
+ : QObjectPrivate(), sm(mgr), sessionId(id), sessionKey(key), eventLoop(0) {}
+ QSessionManager* sm;
+ QStringList restartCommand;
+ QStringList discardCommand;
+ QString& sessionId;
+ QString& sessionKey;
+ QSessionManager::RestartHint restartHint;
+ QEventLoop *eventLoop;
+};
+
+class QSmSocketReceiver : public QObject
+{
+ Q_OBJECT
+public:
+ QSmSocketReceiver(int socket)
+ {
+ QSocketNotifier* sn = new QSocketNotifier(socket, QSocketNotifier::Read, this);
+ connect(sn, SIGNAL(activated(int)), this, SLOT(socketActivated(int)));
+ }
+
+public slots:
+ void socketActivated(int);
+};
+
+
+static SmcConn smcConnection = 0;
+static bool sm_interactionActive;
+static bool sm_smActive;
+static int sm_interactStyle;
+static int sm_saveType;
+static bool sm_cancel;
+// static bool sm_waitingForPhase2; ### never used?!?
+static bool sm_waitingForInteraction;
+static bool sm_isshutdown;
+// static bool sm_shouldbefast; ### never used?!?
+static bool sm_phase2;
+static bool sm_in_phase2;
+
+static QSmSocketReceiver* sm_receiver = 0;
+
+static void resetSmState();
+static void sm_setProperty(const char* name, const char* type,
+ int num_vals, SmPropValue* vals);
+static void sm_saveYourselfCallback(SmcConn smcConn, SmPointer clientData,
+ int saveType, Bool shutdown , int interactStyle, Bool fast);
+static void sm_saveYourselfPhase2Callback(SmcConn smcConn, SmPointer clientData) ;
+static void sm_dieCallback(SmcConn smcConn, SmPointer clientData) ;
+static void sm_shutdownCancelledCallback(SmcConn smcConn, SmPointer clientData);
+static void sm_saveCompleteCallback(SmcConn smcConn, SmPointer clientData);
+static void sm_interactCallback(SmcConn smcConn, SmPointer clientData);
+static void sm_performSaveYourself(QSessionManagerPrivate*);
+
+static void resetSmState()
+{
+// sm_waitingForPhase2 = false; ### never used?!?
+ sm_waitingForInteraction = false;
+ sm_interactionActive = false;
+ sm_interactStyle = SmInteractStyleNone;
+ sm_smActive = false;
+ qt_sm_blockUserInput = false;
+ sm_isshutdown = false;
+// sm_shouldbefast = false; ### never used?!?
+ sm_phase2 = false;
+ sm_in_phase2 = false;
+}
+
+
+// theoretically it's possible to set several properties at once. For
+// simplicity, however, we do just one property at a time
+static void sm_setProperty(const char* name, const char* type,
+ int num_vals, SmPropValue* vals)
+{
+ if (num_vals) {
+ SmProp prop;
+ prop.name = (char*)name;
+ prop.type = (char*)type;
+ prop.num_vals = num_vals;
+ prop.vals = vals;
+
+ SmProp* props[1];
+ props[0] = &prop;
+ SmcSetProperties(smcConnection, 1, props);
+ }
+ else {
+ char* names[1];
+ names[0] = (char*) name;
+ SmcDeleteProperties(smcConnection, 1, names);
+ }
+}
+
+static void sm_setProperty(const QString& name, const QString& value)
+{
+ QByteArray v = value.toUtf8();
+ SmPropValue prop;
+ prop.length = v.length();
+ prop.value = (SmPointer) v.constData();
+ sm_setProperty(name.toLatin1().data(), SmARRAY8, 1, &prop);
+}
+
+static void sm_setProperty(const QString& name, const QStringList& value)
+{
+ SmPropValue *prop = new SmPropValue[value.count()];
+ int count = 0;
+ QList<QByteArray> vl;
+ for (QStringList::ConstIterator it = value.begin(); it != value.end(); ++it) {
+ prop[count].length = (*it).length();
+ vl.append((*it).toUtf8());
+ prop[count].value = (char*)vl.last().data();
+ ++count;
+ }
+ sm_setProperty(name.toLatin1().data(), SmLISTofARRAY8, count, prop);
+ delete [] prop;
+}
+
+
+// workaround for broken libsm, see below
+struct QT_smcConn {
+ unsigned int save_yourself_in_progress : 1;
+ unsigned int shutdown_in_progress : 1;
+};
+
+static void sm_saveYourselfCallback(SmcConn smcConn, SmPointer clientData,
+ int saveType, Bool shutdown , int interactStyle, Bool /*fast*/)
+{
+ if (smcConn != smcConnection)
+ return;
+ sm_cancel = false;
+ sm_smActive = true;
+ sm_isshutdown = shutdown;
+ sm_saveType = saveType;
+ sm_interactStyle = interactStyle;
+// sm_shouldbefast = fast; ### never used?!?
+
+ // ugly workaround for broken libSM. libSM should do that _before_
+ // actually invoking the callback in sm_process.c
+ ((QT_smcConn*)smcConn)->save_yourself_in_progress = true;
+ if (sm_isshutdown)
+ ((QT_smcConn*)smcConn)->shutdown_in_progress = true;
+
+ sm_performSaveYourself((QSessionManagerPrivate*) clientData);
+ if (!sm_isshutdown) // we cannot expect a confirmation message in that case
+ resetSmState();
+}
+
+static void sm_performSaveYourself(QSessionManagerPrivate* smd)
+{
+ if (sm_isshutdown)
+ qt_sm_blockUserInput = true;
+
+ QSessionManager* sm = smd->sm;
+
+ // generate a new session key
+ timeval tv;
+ gettimeofday(&tv, 0);
+ smd->sessionKey = QString::number(qulonglong(tv.tv_sec)) + QLatin1Char('_') + QString::number(qulonglong(tv.tv_usec));
+
+ QStringList arguments = qApp->arguments();
+ QString argument0 = arguments.isEmpty() ? qApp->applicationFilePath() : arguments.at(0);
+
+ // tell the session manager about our program in best POSIX style
+ sm_setProperty(QString::fromLatin1(SmProgram), argument0);
+ // tell the session manager about our user as well.
+ struct passwd *entryPtr = 0;
+#if !defined(QT_NO_THREAD) && defined(_POSIX_THREAD_SAFE_FUNCTIONS) && !defined(Q_OS_OPENBSD)
+ QVarLengthArray<char, 1024> buf(sysconf(_SC_GETPW_R_SIZE_MAX));
+ struct passwd entry;
+ getpwuid_r(geteuid(), &entry, buf.data(), buf.size(), &entryPtr);
+#else
+ entryPtr = getpwuid(geteuid());
+#endif
+ if (entryPtr)
+ sm_setProperty(QString::fromLatin1(SmUserID), QString::fromLatin1(entryPtr->pw_name));
+
+ // generate a restart and discard command that makes sense
+ QStringList restart;
+ restart << argument0 << QLatin1String("-session")
+ << smd->sessionId + QLatin1Char('_') + smd->sessionKey;
+ if (qstricmp(appName, QX11Info::appClass()) != 0)
+ restart << QLatin1String("-name") << qAppName();
+ sm->setRestartCommand(restart);
+ QStringList discard;
+ sm->setDiscardCommand(discard);
+
+ switch (sm_saveType) {
+ case SmSaveBoth:
+ qApp->commitData(*sm);
+ if (sm_isshutdown && sm_cancel)
+ break; // we cancelled the shutdown, no need to save state
+ // fall through
+ case SmSaveLocal:
+ qApp->saveState(*sm);
+ break;
+ case SmSaveGlobal:
+ qApp->commitData(*sm);
+ break;
+ default:
+ break;
+ }
+
+ if (sm_phase2 && !sm_in_phase2) {
+ SmcRequestSaveYourselfPhase2(smcConnection, sm_saveYourselfPhase2Callback, (SmPointer*) smd);
+ qt_sm_blockUserInput = false;
+ }
+ else {
+ // close eventual interaction monitors and cancel the
+ // shutdown, if required. Note that we can only cancel when
+ // performing a shutdown, it does not work for checkpoints
+ if (sm_interactionActive) {
+ SmcInteractDone(smcConnection, sm_isshutdown && sm_cancel);
+ sm_interactionActive = false;
+ }
+ else if (sm_cancel && sm_isshutdown) {
+ if (sm->allowsErrorInteraction()) {
+ SmcInteractDone(smcConnection, True);
+ sm_interactionActive = false;
+ }
+ }
+
+ // set restart and discard command in session manager
+ sm_setProperty(QString::fromLatin1(SmRestartCommand), sm->restartCommand());
+ sm_setProperty(QString::fromLatin1(SmDiscardCommand), sm->discardCommand());
+
+ // set the restart hint
+ SmPropValue prop;
+ prop.length = sizeof(int);
+ int value = sm->restartHint();
+ prop.value = (SmPointer) &value;
+ sm_setProperty(SmRestartStyleHint, SmCARD8, 1, &prop);
+
+ // we are done
+ SmcSaveYourselfDone(smcConnection, !sm_cancel);
+ }
+}
+
+static void sm_dieCallback(SmcConn smcConn, SmPointer /* clientData */)
+{
+ if (smcConn != smcConnection)
+ return;
+ resetSmState();
+ QEvent quitEvent(QEvent::Quit);
+ QApplication::sendEvent(qApp, &quitEvent);
+}
+
+static void sm_shutdownCancelledCallback(SmcConn smcConn, SmPointer clientData)
+{
+ if (smcConn != smcConnection)
+ return;
+ if (sm_waitingForInteraction)
+ ((QSessionManagerPrivate *) clientData)->eventLoop->exit();
+ resetSmState();
+}
+
+static void sm_saveCompleteCallback(SmcConn smcConn, SmPointer /*clientData */)
+{
+ if (smcConn != smcConnection)
+ return;
+ resetSmState();
+}
+
+static void sm_interactCallback(SmcConn smcConn, SmPointer clientData)
+{
+ if (smcConn != smcConnection)
+ return;
+ if (sm_waitingForInteraction)
+ ((QSessionManagerPrivate *) clientData)->eventLoop->exit();
+}
+
+static void sm_saveYourselfPhase2Callback(SmcConn smcConn, SmPointer clientData)
+{
+ if (smcConn != smcConnection)
+ return;
+ sm_in_phase2 = true;
+ sm_performSaveYourself((QSessionManagerPrivate*) clientData);
+}
+
+
+void QSmSocketReceiver::socketActivated(int)
+{
+ IceProcessMessages(SmcGetIceConnection(smcConnection), 0, 0);
+}
+
+
+#undef Bool
+QT_BEGIN_INCLUDE_NAMESPACE
+#include "qapplication_x11.moc"
+QT_END_INCLUDE_NAMESPACE
+
+QSessionManager::QSessionManager(QApplication * app, QString &id, QString& key)
+ : QObject(*new QSessionManagerPrivate(this, id, key), app)
+{
+ Q_D(QSessionManager);
+ d->restartHint = RestartIfRunning;
+
+ resetSmState();
+ char cerror[256];
+ char* myId = 0;
+ QByteArray b_id = id.toLatin1();
+ char* prevId = b_id.data();
+
+ SmcCallbacks cb;
+ cb.save_yourself.callback = sm_saveYourselfCallback;
+ cb.save_yourself.client_data = (SmPointer) d;
+ cb.die.callback = sm_dieCallback;
+ cb.die.client_data = (SmPointer) d;
+ cb.save_complete.callback = sm_saveCompleteCallback;
+ cb.save_complete.client_data = (SmPointer) d;
+ cb.shutdown_cancelled.callback = sm_shutdownCancelledCallback;
+ cb.shutdown_cancelled.client_data = (SmPointer) d;
+
+ // avoid showing a warning message below
+ if (qgetenv("SESSION_MANAGER").isEmpty())
+ return;
+
+ smcConnection = SmcOpenConnection(0, 0, 1, 0,
+ SmcSaveYourselfProcMask |
+ SmcDieProcMask |
+ SmcSaveCompleteProcMask |
+ SmcShutdownCancelledProcMask,
+ &cb,
+ prevId,
+ &myId,
+ 256, cerror);
+
+ id = QString::fromLatin1(myId);
+ ::free(myId); // it was allocated by C
+
+ QString error = QString::fromLocal8Bit(cerror);
+ if (!smcConnection) {
+ qWarning("Qt: Session management error: %s", qPrintable(error));
+ }
+ else {
+ sm_receiver = new QSmSocketReceiver(IceConnectionNumber(SmcGetIceConnection(smcConnection)));
+ }
+}
+
+QSessionManager::~QSessionManager()
+{
+ if (smcConnection)
+ SmcCloseConnection(smcConnection, 0, 0);
+ smcConnection = 0;
+ delete sm_receiver;
+}
+
+QString QSessionManager::sessionId() const
+{
+ Q_D(const QSessionManager);
+ return d->sessionId;
+}
+
+QString QSessionManager::sessionKey() const
+{
+ Q_D(const QSessionManager);
+ return d->sessionKey;
+}
+
+
+void* QSessionManager::handle() const
+{
+ return (void*) smcConnection;
+}
+
+
+bool QSessionManager::allowsInteraction()
+{
+ Q_D(QSessionManager);
+ if (sm_interactionActive)
+ return true;
+
+ if (sm_waitingForInteraction)
+ return false;
+
+ if (sm_interactStyle == SmInteractStyleAny) {
+ sm_waitingForInteraction = SmcInteractRequest(smcConnection, SmDialogNormal,
+ sm_interactCallback, (SmPointer*) d);
+ }
+ if (sm_waitingForInteraction) {
+ QEventLoop eventLoop;
+ d->eventLoop = &eventLoop;
+ (void) eventLoop.exec();
+ d->eventLoop = 0;
+
+ sm_waitingForInteraction = false;
+ if (sm_smActive) { // not cancelled
+ sm_interactionActive = true;
+ qt_sm_blockUserInput = false;
+ return true;
+ }
+ }
+ return false;
+}
+
+bool QSessionManager::allowsErrorInteraction()
+{
+ Q_D(QSessionManager);
+ if (sm_interactionActive)
+ return true;
+
+ if (sm_waitingForInteraction)
+ return false;
+
+ if (sm_interactStyle == SmInteractStyleAny || sm_interactStyle == SmInteractStyleErrors) {
+ sm_waitingForInteraction = SmcInteractRequest(smcConnection, SmDialogError,
+ sm_interactCallback, (SmPointer*) d);
+ }
+ if (sm_waitingForInteraction) {
+ QEventLoop eventLoop;
+ d->eventLoop = &eventLoop;
+ (void) eventLoop.exec();
+ d->eventLoop = 0;
+
+ sm_waitingForInteraction = false;
+ if (sm_smActive) { // not cancelled
+ sm_interactionActive = true;
+ qt_sm_blockUserInput = false;
+ return true;
+ }
+ }
+ return false;
+}
+
+void QSessionManager::release()
+{
+ if (sm_interactionActive) {
+ SmcInteractDone(smcConnection, False);
+ sm_interactionActive = false;
+ if (sm_smActive && sm_isshutdown)
+ qt_sm_blockUserInput = true;
+ }
+}
+
+void QSessionManager::cancel()
+{
+ sm_cancel = true;
+}
+
+void QSessionManager::setRestartHint(QSessionManager::RestartHint hint)
+{
+ Q_D(QSessionManager);
+ d->restartHint = hint;
+}
+
+QSessionManager::RestartHint QSessionManager::restartHint() const
+{
+ Q_D(const QSessionManager);
+ return d->restartHint;
+}
+
+void QSessionManager::setRestartCommand(const QStringList& command)
+{
+ Q_D(QSessionManager);
+ d->restartCommand = command;
+}
+
+QStringList QSessionManager::restartCommand() const
+{
+ Q_D(const QSessionManager);
+ return d->restartCommand;
+}
+
+void QSessionManager::setDiscardCommand(const QStringList& command)
+{
+ Q_D(QSessionManager);
+ d->discardCommand = command;
+}
+
+QStringList QSessionManager::discardCommand() const
+{
+ Q_D(const QSessionManager);
+ return d->discardCommand;
+}
+
+void QSessionManager::setManagerProperty(const QString& name, const QString& value)
+{
+ sm_setProperty(name, value);
+}
+
+void QSessionManager::setManagerProperty(const QString& name, const QStringList& value)
+{
+ sm_setProperty(name, value);
+}
+
+bool QSessionManager::isPhase2() const
+{
+ return sm_in_phase2;
+}
+
+void QSessionManager::requestPhase2()
+{
+ sm_phase2 = true;
+}
+
+#endif // QT_NO_SESSIONMANAGER
+
+QT_END_NAMESPACE
diff --git a/src/gui/kernel/qboxlayout.cpp b/src/gui/kernel/qboxlayout.cpp
new file mode 100644
index 0000000000..49dd4a5690
--- /dev/null
+++ b/src/gui/kernel/qboxlayout.cpp
@@ -0,0 +1,1534 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qboxlayout.h"
+#include "qapplication.h"
+#include "qwidget.h"
+#include "qlist.h"
+#include "qsizepolicy.h"
+#include "qvector.h"
+
+#include "qlayoutengine_p.h"
+#include "qlayout_p.h"
+
+QT_BEGIN_NAMESPACE
+
+/*
+ Returns true if the \a widget can be added to the \a layout;
+ otherwise returns false.
+*/
+static bool checkWidget(QLayout *layout, QWidget *widget)
+{
+ if (!widget) {
+ qWarning("QLayout: Cannot add null widget to %s/%s", layout->metaObject()->className(),
+ layout->objectName().toLocal8Bit().data());
+ return false;
+ }
+ return true;
+}
+
+struct QBoxLayoutItem
+{
+ QBoxLayoutItem(QLayoutItem *it, int stretch_ = 0)
+ : item(it), stretch(stretch_), magic(false) { }
+ ~QBoxLayoutItem() { delete item; }
+
+ int hfw(int w) {
+ if (item->hasHeightForWidth()) {
+ return item->heightForWidth(w);
+ } else {
+ return item->sizeHint().height();
+ }
+ }
+ int mhfw(int w) {
+ if (item->hasHeightForWidth()) {
+ return item->heightForWidth(w);
+ } else {
+ return item->minimumSize().height();
+ }
+ }
+ int hStretch() {
+ if (stretch == 0 && item->widget()) {
+ return item->widget()->sizePolicy().horizontalStretch();
+ } else {
+ return stretch;
+ }
+ }
+ int vStretch() {
+ if (stretch == 0 && item->widget()) {
+ return item->widget()->sizePolicy().verticalStretch();
+ } else {
+ return stretch;
+ }
+ }
+
+ QLayoutItem *item;
+ int stretch;
+ bool magic;
+};
+
+class QBoxLayoutPrivate : public QLayoutPrivate
+{
+ Q_DECLARE_PUBLIC(QBoxLayout)
+public:
+ QBoxLayoutPrivate() : hfwWidth(-1), dirty(true), spacing(-1) { }
+ ~QBoxLayoutPrivate();
+
+ void setDirty() {
+ geomArray.clear();
+ hfwWidth = -1;
+ hfwHeight = -1;
+ dirty = true;
+ }
+
+ QList<QBoxLayoutItem *> list;
+ QVector<QLayoutStruct> geomArray;
+ int hfwWidth;
+ int hfwHeight;
+ int hfwMinHeight;
+ QSize sizeHint;
+ QSize minSize;
+ QSize maxSize;
+ int leftMargin, topMargin, rightMargin, bottomMargin;
+ Qt::Orientations expanding;
+ uint hasHfw : 1;
+ uint dirty : 1;
+ QBoxLayout::Direction dir;
+ int spacing;
+
+ inline void deleteAll() { while (!list.isEmpty()) delete list.takeFirst(); }
+
+ void setupGeom();
+ void calcHfw(int);
+
+ void effectiveMargins(int *left, int *top, int *right, int *bottom) const;
+};
+
+QBoxLayoutPrivate::~QBoxLayoutPrivate()
+{
+}
+
+static inline bool horz(QBoxLayout::Direction dir)
+{
+ return dir == QBoxLayout::RightToLeft || dir == QBoxLayout::LeftToRight;
+}
+
+/**
+ * The purpose of this function is to make sure that widgets are not laid out outside its layout.
+ * E.g. the layoutItemRect margins are only meant to take of the surrounding margins/spacings.
+ * However, if the margin is 0, it can easily cover the area of a widget above it.
+ */
+void QBoxLayoutPrivate::effectiveMargins(int *left, int *top, int *right, int *bottom) const
+{
+ int l = leftMargin;
+ int t = topMargin;
+ int r = rightMargin;
+ int b = bottomMargin;
+#ifdef Q_WS_MAC
+ Q_Q(const QBoxLayout);
+ if (horz(dir)) {
+ QBoxLayoutItem *leftBox = 0;
+ QBoxLayoutItem *rightBox = 0;
+
+ if (left || right) {
+ leftBox = list.value(0);
+ rightBox = list.value(list.count() - 1);
+ if (dir == QBoxLayout::RightToLeft)
+ qSwap(leftBox, rightBox);
+
+ int leftDelta = 0;
+ int rightDelta = 0;
+ if (leftBox) {
+ QLayoutItem *itm = leftBox->item;
+ if (QWidget *w = itm->widget())
+ leftDelta = itm->geometry().left() - w->geometry().left();
+ }
+ if (rightBox) {
+ QLayoutItem *itm = rightBox->item;
+ if (QWidget *w = itm->widget())
+ rightDelta = w->geometry().right() - itm->geometry().right();
+ }
+ QWidget *w = q->parentWidget();
+ Qt::LayoutDirection layoutDirection = w ? w->layoutDirection() : QApplication::layoutDirection();
+ if (layoutDirection == Qt::RightToLeft)
+ qSwap(leftDelta, rightDelta);
+
+ l = qMax(l, leftDelta);
+ r = qMax(r, rightDelta);
+ }
+
+ int count = top || bottom ? list.count() : 0;
+ for (int i = 0; i < count; ++i) {
+ QBoxLayoutItem *box = list.at(i);
+ QLayoutItem *itm = box->item;
+ QWidget *w = itm->widget();
+ if (w) {
+ QRect lir = itm->geometry();
+ QRect wr = w->geometry();
+ if (top)
+ t = qMax(t, lir.top() - wr.top());
+ if (bottom)
+ b = qMax(b, wr.bottom() - lir.bottom());
+ }
+ }
+ } else { // vertical layout
+ QBoxLayoutItem *topBox = 0;
+ QBoxLayoutItem *bottomBox = 0;
+
+ if (top || bottom) {
+ topBox = list.value(0);
+ bottomBox = list.value(list.count() - 1);
+ if (dir == QBoxLayout::BottomToTop) {
+ qSwap(topBox, bottomBox);
+ }
+
+ if (top && topBox) {
+ QLayoutItem *itm = topBox->item;
+ QWidget *w = itm->widget();
+ if (w)
+ t = qMax(t, itm->geometry().top() - w->geometry().top());
+ }
+
+ if (bottom && bottomBox) {
+ QLayoutItem *itm = bottomBox->item;
+ QWidget *w = itm->widget();
+ if (w)
+ b = qMax(b, w->geometry().bottom() - itm->geometry().bottom());
+ }
+ }
+
+ int count = left || right ? list.count() : 0;
+ for (int i = 0; i < count; ++i) {
+ QBoxLayoutItem *box = list.at(i);
+ QLayoutItem *itm = box->item;
+ QWidget *w = itm->widget();
+ if (w) {
+ QRect lir = itm->geometry();
+ QRect wr = w->geometry();
+ if (left)
+ l = qMax(l, lir.left() - wr.left());
+ if (right)
+ r = qMax(r, wr.right() - lir.right());
+ }
+ }
+ }
+#endif
+ if (left)
+ *left = l;
+ if (top)
+ *top = t;
+ if (right)
+ *right = r;
+ if (bottom)
+ *bottom = b;
+}
+
+
+/*
+ Initializes the data structure needed by qGeomCalc and
+ recalculates max/min and size hint.
+*/
+void QBoxLayoutPrivate::setupGeom()
+{
+ if (!dirty)
+ return;
+
+ Q_Q(QBoxLayout);
+ int maxw = horz(dir) ? 0 : QLAYOUTSIZE_MAX;
+ int maxh = horz(dir) ? QLAYOUTSIZE_MAX : 0;
+ int minw = 0;
+ int minh = 0;
+ int hintw = 0;
+ int hinth = 0;
+
+ bool horexp = false;
+ bool verexp = false;
+
+ hasHfw = false;
+
+ int n = list.count();
+ geomArray.clear();
+ QVector<QLayoutStruct> a(n);
+
+ QSizePolicy::ControlTypes controlTypes1;
+ QSizePolicy::ControlTypes controlTypes2;
+ int fixedSpacing = q->spacing();
+ int previousNonEmptyIndex = -1;
+
+ QStyle *style = 0;
+ if (fixedSpacing < 0) {
+ if (QWidget *parentWidget = q->parentWidget())
+ style = parentWidget->style();
+ }
+
+ for (int i = 0; i < n; i++) {
+ QBoxLayoutItem *box = list.at(i);
+ QSize max = box->item->maximumSize();
+ QSize min = box->item->minimumSize();
+ QSize hint = box->item->sizeHint();
+ Qt::Orientations exp = box->item->expandingDirections();
+ bool empty = box->item->isEmpty();
+ int spacing = 0;
+
+ if (!empty) {
+ if (fixedSpacing >= 0) {
+ spacing = (previousNonEmptyIndex >= 0) ? fixedSpacing : 0;
+#ifdef Q_WS_MAC
+ if (!horz(dir) && previousNonEmptyIndex >= 0) {
+ QBoxLayoutItem *sibling = (dir == QBoxLayout::TopToBottom ? box : list.at(previousNonEmptyIndex));
+ if (sibling) {
+ QWidget *wid = sibling->item->widget();
+ if (wid)
+ spacing = qMax(spacing, sibling->item->geometry().top() - wid->geometry().top());
+ }
+ }
+#endif
+ } else {
+ controlTypes1 = controlTypes2;
+ controlTypes2 = box->item->controlTypes();
+ if (previousNonEmptyIndex >= 0) {
+ QSizePolicy::ControlTypes actual1 = controlTypes1;
+ QSizePolicy::ControlTypes actual2 = controlTypes2;
+ if (dir == QBoxLayout::RightToLeft || dir == QBoxLayout::BottomToTop)
+ qSwap(actual1, actual2);
+
+ if (style) {
+ spacing = style->combinedLayoutSpacing(actual1, actual2,
+ horz(dir) ? Qt::Horizontal : Qt::Vertical,
+ 0, q->parentWidget());
+ if (spacing < 0)
+ spacing = 0;
+ }
+ }
+ }
+
+ if (previousNonEmptyIndex >= 0)
+ a[previousNonEmptyIndex].spacing = spacing;
+ previousNonEmptyIndex = i;
+ }
+
+ bool ignore = empty && box->item->widget(); // ignore hidden widgets
+ bool dummy = true;
+ if (horz(dir)) {
+ bool expand = (exp & Qt::Horizontal || box->stretch > 0);
+ horexp = horexp || expand;
+ maxw += spacing + max.width();
+ minw += spacing + min.width();
+ hintw += spacing + hint.width();
+ if (!ignore)
+ qMaxExpCalc(maxh, verexp, dummy,
+ max.height(), exp & Qt::Vertical, box->item->isEmpty());
+ minh = qMax(minh, min.height());
+ hinth = qMax(hinth, hint.height());
+
+ a[i].sizeHint = hint.width();
+ a[i].maximumSize = max.width();
+ a[i].minimumSize = min.width();
+ a[i].expansive = expand;
+ a[i].stretch = box->stretch ? box->stretch : box->hStretch();
+ } else {
+ bool expand = (exp & Qt::Vertical || box->stretch > 0);
+ verexp = verexp || expand;
+ maxh += spacing + max.height();
+ minh += spacing + min.height();
+ hinth += spacing + hint.height();
+ if (!ignore)
+ qMaxExpCalc(maxw, horexp, dummy,
+ max.width(), exp & Qt::Horizontal, box->item->isEmpty());
+ minw = qMax(minw, min.width());
+ hintw = qMax(hintw, hint.width());
+
+ a[i].sizeHint = hint.height();
+ a[i].maximumSize = max.height();
+ a[i].minimumSize = min.height();
+ a[i].expansive = expand;
+ a[i].stretch = box->stretch ? box->stretch : box->vStretch();
+ }
+
+ a[i].empty = empty;
+ a[i].spacing = 0; // might be be initialized with a non-zero value in a later iteration
+ hasHfw = hasHfw || box->item->hasHeightForWidth();
+ }
+
+ geomArray = a;
+
+ expanding = (Qt::Orientations)
+ ((horexp ? Qt::Horizontal : 0)
+ | (verexp ? Qt::Vertical : 0));
+
+ minSize = QSize(minw, minh);
+ maxSize = QSize(maxw, maxh).expandedTo(minSize);
+ sizeHint = QSize(hintw, hinth).expandedTo(minSize).boundedTo(maxSize);
+
+ q->getContentsMargins(&leftMargin, &topMargin, &rightMargin, &bottomMargin);
+ int left, top, right, bottom;
+ effectiveMargins(&left, &top, &right, &bottom);
+ QSize extra(left + right, top + bottom);
+
+ minSize += extra;
+ maxSize += extra;
+ sizeHint += extra;
+
+ dirty = false;
+}
+
+/*
+ Calculates and stores the preferred height given the width \a w.
+*/
+void QBoxLayoutPrivate::calcHfw(int w)
+{
+ QVector<QLayoutStruct> &a = geomArray;
+ int n = a.count();
+ int h = 0;
+ int mh = 0;
+
+ Q_ASSERT(n == list.size());
+
+ if (horz(dir)) {
+ qGeomCalc(a, 0, n, 0, w);
+ for (int i = 0; i < n; i++) {
+ QBoxLayoutItem *box = list.at(i);
+ h = qMax(h, box->hfw(a.at(i).size));
+ mh = qMax(mh, box->mhfw(a.at(i).size));
+ }
+ } else {
+ for (int i = 0; i < n; ++i) {
+ QBoxLayoutItem *box = list.at(i);
+ int spacing = a.at(i).spacing;
+ h += box->hfw(w);
+ mh += box->mhfw(w);
+ h += spacing;
+ mh += spacing;
+ }
+ }
+ hfwWidth = w;
+ hfwHeight = h;
+ hfwMinHeight = mh;
+}
+
+
+/*!
+ \class QBoxLayout
+
+ \brief The QBoxLayout class lines up child widgets horizontally or
+ vertically.
+
+ \ingroup geomanagement
+ \ingroup appearance
+
+ QBoxLayout takes the space it gets (from its parent layout or from
+ the parentWidget()), divides it up into a row of boxes, and makes
+ each managed widget fill one box.
+
+ \image qhboxlayout-with-5-children.png Horizontal box layout with five child widgets
+
+ If the QBoxLayout's orientation is Qt::Horizontal the boxes are
+ placed in a row, with suitable sizes. Each widget (or other box)
+ will get at least its minimum size and at most its maximum size.
+ Any excess space is shared according to the stretch factors (more
+ about that below).
+
+ \image qvboxlayout-with-5-children.png Vertical box layout with five child widgets
+
+ If the QBoxLayout's orientation is Qt::Vertical, the boxes are
+ placed in a column, again with suitable sizes.
+
+ The easiest way to create a QBoxLayout is to use one of the
+ convenience classes, e.g. QHBoxLayout (for Qt::Horizontal boxes)
+ or QVBoxLayout (for Qt::Vertical boxes). You can also use the
+ QBoxLayout constructor directly, specifying its direction as
+ LeftToRight, RightToLeft, TopToBottom, or BottomToTop.
+
+ If the QBoxLayout is not the top-level layout (i.e. it is not
+ managing all of the widget's area and children), you must add it
+ to its parent layout before you can do anything with it. The
+ normal way to add a layout is by calling
+ parentLayout-\>addLayout().
+
+ Once you have done this, you can add boxes to the QBoxLayout using
+ one of four functions:
+
+ \list
+ \o addWidget() to add a widget to the QBoxLayout and set the
+ widget's stretch factor. (The stretch factor is along the row of
+ boxes.)
+
+ \o addSpacing() to create an empty box; this is one of the
+ functions you use to create nice and spacious dialogs. See below
+ for ways to set margins.
+
+ \o addStretch() to create an empty, stretchable box.
+
+ \o addLayout() to add a box containing another QLayout to the row
+ and set that layout's stretch factor.
+ \endlist
+
+ Use insertWidget(), insertSpacing(), insertStretch() or
+ insertLayout() to insert a box at a specified position in the
+ layout.
+
+ QBoxLayout also includes two margin widths:
+
+ \list
+ \o setContentsMargins() sets the width of the outer border on
+ each side of the widget. This is the width of the reserved space
+ along each of the QBoxLayout's four sides.
+ \o setSpacing() sets the width between neighboring boxes. (You
+ can use addSpacing() to get more space at a particular spot.)
+ \endlist
+
+ The margin default is provided by the style. The default margin
+ most Qt styles specify is 9 for child widgets and 11 for windows.
+ The spacing defaults to the same as the margin width for a
+ top-level layout, or to the same as the parent layout.
+
+ To remove a widget from a layout, call removeWidget(). Calling
+ QWidget::hide() on a widget also effectively removes the widget
+ from the layout until QWidget::show() is called.
+
+ You will almost always want to use QVBoxLayout and QHBoxLayout
+ rather than QBoxLayout because of their convenient constructors.
+
+ \sa QGridLayout, QStackedLayout, {Layout Classes}
+*/
+
+/*!
+ \enum QBoxLayout::Direction
+
+ This type is used to determine the direction of a box layout.
+
+ \value LeftToRight Horizontal from left to right.
+ \value RightToLeft Horizontal from right to left.
+ \value TopToBottom Vertical from top to bottom.
+ \value BottomToTop Vertical from bottom to top.
+
+ \omitvalue Down
+ \omitvalue Up
+*/
+
+/*!
+ Constructs a new QBoxLayout with direction \a dir and parent widget \a
+ parent.
+
+ \sa direction()
+*/
+QBoxLayout::QBoxLayout(Direction dir, QWidget *parent)
+ : QLayout(*new QBoxLayoutPrivate, 0, parent)
+{
+ Q_D(QBoxLayout);
+ d->dir = dir;
+}
+
+#ifdef QT3_SUPPORT
+/*!
+ Constructs a new QBoxLayout with direction \a dir and main widget \a
+ parent. \a parent may not be 0.
+
+ 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 \a margin is used for \a spacing.
+
+ \a name is the internal object name.
+
+ \sa direction()
+*/
+QBoxLayout::QBoxLayout(QWidget *parent, Direction dir,
+ int margin, int spacing, const char *name)
+ : QLayout(*new QBoxLayoutPrivate, 0, parent)
+{
+ Q_D(QBoxLayout);
+ d->dir = dir;
+ setMargin(margin);
+ setObjectName(QString::fromAscii(name));
+ setSpacing(spacing<0 ? margin : spacing);
+}
+
+/*!
+ Constructs a new QBoxLayout called \a name, with direction \a dir,
+ and inserts it into \a parentLayout.
+
+ The \a spacing is the default number of pixels between neighboring
+ children. If \a spacing is -1, the layout will inherit its
+ parent's spacing().
+*/
+QBoxLayout::QBoxLayout(QLayout *parentLayout, Direction dir, int spacing,
+ const char *name)
+ : QLayout(*new QBoxLayoutPrivate, parentLayout, 0)
+{
+ Q_D(QBoxLayout);
+ d->dir = dir;
+ setObjectName(QString::fromAscii(name));
+ setSpacing(spacing);
+}
+
+/*!
+ Constructs a new QBoxLayout called \a name, with direction \a dir.
+
+ If \a spacing is -1, the layout will inherit its parent's
+ spacing(); otherwise \a spacing is used.
+
+ You must insert this box into another layout.
+*/
+QBoxLayout::QBoxLayout(Direction dir, int spacing, const char *name)
+ : QLayout(*new QBoxLayoutPrivate,0, 0)
+{
+ Q_D(QBoxLayout);
+ d->dir = dir;
+ setObjectName(QString::fromAscii(name));
+ setSpacing(spacing);
+}
+#endif // QT3_SUPPORT
+
+
+/*!
+ Destroys this box layout.
+
+ The layout's widgets aren't destroyed.
+*/
+QBoxLayout::~QBoxLayout()
+{
+ Q_D(QBoxLayout);
+ d->deleteAll(); // must do it before QObject deletes children, so can't be in ~QBoxLayoutPrivate
+}
+
+/*!
+ Reimplements QLayout::spacing(). If the spacing property is
+ valid, that value is returned. Otherwise, a value for the spacing
+ property is computed and returned. Since layout spacing in a widget
+ is style dependent, if the parent is a widget, it queries the style
+ for the (horizontal or vertical) spacing of the layout. Otherwise,
+ the parent is a layout, and it queries the parent layout for the
+ spacing().
+
+ \sa QLayout::spacing(), setSpacing()
+ */
+int QBoxLayout::spacing() const
+{
+ Q_D(const QBoxLayout);
+ if (d->spacing >=0) {
+ return d->spacing;
+ } else {
+ return qSmartSpacing(this, d->dir == LeftToRight || d->dir == RightToLeft
+ ? QStyle::PM_LayoutHorizontalSpacing
+ : QStyle::PM_LayoutVerticalSpacing);
+ }
+}
+
+/*!
+ Reimplements QLayout::setSpacing(). Sets the spacing
+ property to \a spacing.
+
+ \sa QLayout::setSpacing(), spacing()
+ */
+void QBoxLayout::setSpacing(int spacing)
+{
+ Q_D(QBoxLayout);
+ d->spacing = spacing;
+ invalidate();
+}
+
+/*!
+ \reimp
+*/
+QSize QBoxLayout::sizeHint() const
+{
+ Q_D(const QBoxLayout);
+ if (d->dirty)
+ const_cast<QBoxLayout*>(this)->d_func()->setupGeom();
+ return d->sizeHint;
+}
+
+/*!
+ \reimp
+*/
+QSize QBoxLayout::minimumSize() const
+{
+ Q_D(const QBoxLayout);
+ if (d->dirty)
+ const_cast<QBoxLayout*>(this)->d_func()->setupGeom();
+ return d->minSize;
+}
+
+/*!
+ \reimp
+*/
+QSize QBoxLayout::maximumSize() const
+{
+ Q_D(const QBoxLayout);
+ if (d->dirty)
+ const_cast<QBoxLayout*>(this)->d_func()->setupGeom();
+
+ QSize s = d->maxSize.boundedTo(QSize(QLAYOUTSIZE_MAX, QLAYOUTSIZE_MAX));
+
+ if (alignment() & Qt::AlignHorizontal_Mask)
+ s.setWidth(QLAYOUTSIZE_MAX);
+ if (alignment() & Qt::AlignVertical_Mask)
+ s.setHeight(QLAYOUTSIZE_MAX);
+ return s;
+}
+
+/*!
+ \reimp
+*/
+bool QBoxLayout::hasHeightForWidth() const
+{
+ Q_D(const QBoxLayout);
+ if (d->dirty)
+ const_cast<QBoxLayout*>(this)->d_func()->setupGeom();
+ return d->hasHfw;
+}
+
+/*!
+ \reimp
+*/
+int QBoxLayout::heightForWidth(int w) const
+{
+ Q_D(const QBoxLayout);
+ if (!hasHeightForWidth())
+ return -1;
+
+ int left, top, right, bottom;
+ d->effectiveMargins(&left, &top, &right, &bottom);
+
+ w -= left + right;
+ if (w != d->hfwWidth)
+ const_cast<QBoxLayout*>(this)->d_func()->calcHfw(w);
+
+ return d->hfwHeight + top + bottom;
+}
+
+/*!
+ \reimp
+*/
+int QBoxLayout::minimumHeightForWidth(int w) const
+{
+ Q_D(const QBoxLayout);
+ (void) heightForWidth(w);
+ int top, bottom;
+ d->effectiveMargins(0, &top, 0, &bottom);
+ return d->hasHfw ? (d->hfwMinHeight + top + bottom) : -1;
+}
+
+/*!
+ Resets cached information.
+*/
+void QBoxLayout::invalidate()
+{
+ Q_D(QBoxLayout);
+ d->setDirty();
+ QLayout::invalidate();
+}
+
+/*!
+ \reimp
+*/
+int QBoxLayout::count() const
+{
+ Q_D(const QBoxLayout);
+ return d->list.count();
+}
+
+/*!
+ \reimp
+*/
+QLayoutItem *QBoxLayout::itemAt(int index) const
+{
+ Q_D(const QBoxLayout);
+ return index >= 0 && index < d->list.count() ? d->list.at(index)->item : 0;
+}
+
+/*!
+ \reimp
+*/
+QLayoutItem *QBoxLayout::takeAt(int index)
+{
+ Q_D(QBoxLayout);
+ if (index < 0 || index >= d->list.count())
+ return 0;
+ QBoxLayoutItem *b = d->list.takeAt(index);
+ QLayoutItem *item = b->item;
+ b->item = 0;
+ delete b;
+
+ invalidate();
+ return item;
+}
+
+
+/*!
+ \reimp
+*/
+Qt::Orientations QBoxLayout::expandingDirections() const
+{
+ Q_D(const QBoxLayout);
+ if (d->dirty)
+ const_cast<QBoxLayout*>(this)->d_func()->setupGeom();
+ return d->expanding;
+}
+
+/*!
+ \reimp
+*/
+void QBoxLayout::setGeometry(const QRect &r)
+{
+ Q_D(QBoxLayout);
+ if (d->dirty || r != geometry()) {
+ QRect oldRect = geometry();
+ QLayout::setGeometry(r);
+ if (d->dirty)
+ d->setupGeom();
+ QRect cr = alignment() ? alignmentRect(r) : r;
+
+ int left, top, right, bottom;
+ d->effectiveMargins(&left, &top, &right, &bottom);
+ QRect s(cr.x() + left, cr.y() + top,
+ cr.width() - (left + right),
+ cr.height() - (top + bottom));
+
+ QVector<QLayoutStruct> a = d->geomArray;
+ int pos = horz(d->dir) ? s.x() : s.y();
+ int space = horz(d->dir) ? s.width() : s.height();
+ int n = a.count();
+ if (d->hasHfw && !horz(d->dir)) {
+ for (int i = 0; i < n; i++) {
+ QBoxLayoutItem *box = d->list.at(i);
+ if (box->item->hasHeightForWidth())
+ a[i].sizeHint = a[i].minimumSize =
+ box->item->heightForWidth(s.width());
+ }
+ }
+
+ Direction visualDir = d->dir;
+ QWidget *parent = parentWidget();
+ if (parent && parent->isRightToLeft()) {
+ if (d->dir == LeftToRight)
+ visualDir = RightToLeft;
+ else if (d->dir == RightToLeft)
+ visualDir = LeftToRight;
+ }
+
+ qGeomCalc(a, 0, n, pos, space);
+
+ bool reverse = (horz(visualDir)
+ ? ((r.right() > oldRect.right()) != (visualDir == RightToLeft))
+ : r.bottom() > oldRect.bottom());
+ for (int j = 0; j < n; j++) {
+ int i = reverse ? n-j-1 : j;
+ QBoxLayoutItem *box = d->list.at(i);
+
+ switch (visualDir) {
+ case LeftToRight:
+ box->item->setGeometry(QRect(a.at(i).pos, s.y(), a.at(i).size, s.height()));
+ break;
+ case RightToLeft:
+ box->item->setGeometry(QRect(s.left() + s.right() - a.at(i).pos - a.at(i).size + 1,
+ s.y(), a.at(i).size, s.height()));
+ break;
+ case TopToBottom:
+ box->item->setGeometry(QRect(s.x(), a.at(i).pos, s.width(), a.at(i).size));
+ break;
+ case BottomToTop:
+ box->item->setGeometry(QRect(s.x(),
+ s.top() + s.bottom() - a.at(i).pos - a.at(i).size + 1,
+ s.width(), a.at(i).size));
+ }
+ }
+ }
+}
+
+/*!
+ \reimp
+*/
+void QBoxLayout::addItem(QLayoutItem *item)
+{
+ Q_D(QBoxLayout);
+ QBoxLayoutItem *it = new QBoxLayoutItem(item);
+ d->list.append(it);
+ invalidate();
+}
+
+/*!
+ Inserts \a item into this box layout at position \a index. If \a
+ index is negative, the item is added at the end.
+
+ \sa addItem(), insertWidget(), insertLayout(), insertStretch(),
+ insertSpacing()
+*/
+void QBoxLayout::insertItem(int index, QLayoutItem *item)
+{
+ Q_D(QBoxLayout);
+ if (index < 0) // append
+ index = d->list.count();
+
+ QBoxLayoutItem *it = new QBoxLayoutItem(item);
+ d->list.insert(index, it);
+ invalidate();
+}
+
+/*!
+ Inserts a non-stretchable space (a QSpacerItem) at position \a index, with
+ size \a size. If \a index is negative the space is added at the end.
+
+ The box layout has default margin and spacing. This function adds
+ additional space.
+
+ \sa addSpacing(), insertItem(), QSpacerItem
+*/
+void QBoxLayout::insertSpacing(int index, int size)
+{
+ Q_D(QBoxLayout);
+ if (index < 0) // append
+ index = d->list.count();
+
+ QLayoutItem *b;
+ if (horz(d->dir))
+ b = QLayoutPrivate::createSpacerItem(this, size, 0, QSizePolicy::Fixed, QSizePolicy::Minimum);
+ else
+ b = QLayoutPrivate::createSpacerItem(this, 0, size, QSizePolicy::Minimum, QSizePolicy::Fixed);
+
+ QBoxLayoutItem *it = new QBoxLayoutItem(b);
+ it->magic = true;
+ d->list.insert(index, it);
+ invalidate();
+}
+
+/*!
+ Inserts a stretchable space (a QSpacerItem) at position \a
+ index, with zero minimum size and stretch factor \a stretch. If \a
+ index is negative the space is added at the end.
+
+ \sa addStretch(), insertItem(), QSpacerItem
+*/
+void QBoxLayout::insertStretch(int index, int stretch)
+{
+ Q_D(QBoxLayout);
+ if (index < 0) // append
+ index = d->list.count();
+
+ QLayoutItem *b;
+ if (horz(d->dir))
+ b = QLayoutPrivate::createSpacerItem(this, 0, 0, QSizePolicy::Expanding, QSizePolicy::Minimum);
+ else
+ b = QLayoutPrivate::createSpacerItem(this, 0, 0, QSizePolicy::Minimum, QSizePolicy::Expanding);
+
+ QBoxLayoutItem *it = new QBoxLayoutItem(b, stretch);
+ it->magic = true;
+ d->list.insert(index, it);
+ invalidate();
+}
+
+/*!
+ \since 4.4
+
+ Inserts \a spacerItem at position \a index, with zero minimum
+ size and stretch factor. If \a index is negative the
+ space is added at the end.
+
+ \sa addSpacerItem(), insertStretch(), insertSpacing()
+*/
+void QBoxLayout::insertSpacerItem(int index, QSpacerItem *spacerItem)
+{
+ Q_D(QBoxLayout);
+ if (index < 0) // append
+ index = d->list.count();
+
+ QBoxLayoutItem *it = new QBoxLayoutItem(spacerItem);
+ it->magic = true;
+ d->list.insert(index, it);
+ invalidate();
+}
+
+/*!
+ Inserts \a layout at position \a index, with stretch factor \a
+ stretch. If \a index is negative, the layout is added at the end.
+
+ \a layout becomes a child of the box layout.
+
+ \sa addLayout(), insertItem()
+*/
+void QBoxLayout::insertLayout(int index, QLayout *layout, int stretch)
+{
+ Q_D(QBoxLayout);
+ addChildLayout(layout);
+ if (index < 0) // append
+ index = d->list.count();
+ QBoxLayoutItem *it = new QBoxLayoutItem(layout, stretch);
+ d->list.insert(index, it);
+ invalidate();
+}
+
+/*!
+ Inserts \a widget at position \a index, with stretch factor \a
+ stretch and alignment \a alignment. If \a index is negative, the
+ widget is added at the end.
+
+ The stretch factor applies only in the \l{direction()}{direction}
+ of the QBoxLayout, and is relative to the other boxes and widgets
+ in this QBoxLayout. Widgets and boxes with higher stretch factors
+ grow more.
+
+ If the stretch factor is 0 and nothing else in the QBoxLayout has
+ a stretch factor greater than zero, the space is distributed
+ according to the QWidget:sizePolicy() of each widget that's
+ involved.
+
+ The alignment is specified by \a alignment. The default alignment
+ is 0, which means that the widget fills the entire cell.
+
+ \sa addWidget(), insertItem()
+*/
+void QBoxLayout::insertWidget(int index, QWidget *widget, int stretch,
+ Qt::Alignment alignment)
+{
+ Q_D(QBoxLayout);
+ if (!checkWidget(this, widget))
+ return;
+ addChildWidget(widget);
+ if (index < 0) // append
+ index = d->list.count();
+ QWidgetItem *b = QLayoutPrivate::createWidgetItem(this, widget);
+ b->setAlignment(alignment);
+ QBoxLayoutItem *it = new QBoxLayoutItem(b, stretch);
+ d->list.insert(index, it);
+ invalidate();
+}
+
+/*!
+ Adds a non-stretchable space (a QSpacerItem) with size \a size
+ to the end of this box layout. QBoxLayout provides default margin
+ and spacing. This function adds additional space.
+
+ \sa insertSpacing(), addItem(), QSpacerItem
+*/
+void QBoxLayout::addSpacing(int size)
+{
+ insertSpacing(-1, size);
+}
+
+/*!
+ Adds a stretchable space (a QSpacerItem) with zero minimum
+ size and stretch factor \a stretch to the end of this box layout.
+
+ \sa insertStretch(), addItem(), QSpacerItem
+*/
+void QBoxLayout::addStretch(int stretch)
+{
+ insertStretch(-1, stretch);
+}
+
+/*!
+ \since 4.4
+
+ Adds \a spacerItem to the end of this box layout.
+
+ \sa addSpacing(), addStretch()
+*/
+void QBoxLayout::addSpacerItem(QSpacerItem *spacerItem)
+{
+ insertSpacerItem(-1, spacerItem);
+}
+
+/*!
+ Adds \a widget to the end of this box layout, with a stretch
+ factor of \a stretch and alignment \a alignment.
+
+ The stretch factor applies only in the \l{direction()}{direction}
+ of the QBoxLayout, and is relative to the other boxes and widgets
+ in this QBoxLayout. Widgets and boxes with higher stretch factors
+ grow more.
+
+ If the stretch factor is 0 and nothing else in the QBoxLayout has
+ a stretch factor greater than zero, the space is distributed
+ according to the QWidget:sizePolicy() of each widget that's
+ involved.
+
+ The alignment is specified by \a alignment. The default
+ alignment is 0, which means that the widget fills the entire cell.
+
+ \sa insertWidget(), addItem(), addLayout(), addStretch(),
+ addSpacing(), addStrut()
+*/
+void QBoxLayout::addWidget(QWidget *widget, int stretch, Qt::Alignment alignment)
+{
+ insertWidget(-1, widget, stretch, alignment);
+}
+
+/*!
+ Adds \a layout to the end of the box, with serial stretch factor
+ \a stretch.
+
+ \sa insertLayout(), addItem(), addWidget()
+*/
+void QBoxLayout::addLayout(QLayout *layout, int stretch)
+{
+ insertLayout(-1, layout, stretch);
+}
+
+/*!
+ Limits the perpendicular dimension of the box (e.g. height if the
+ box is \l LeftToRight) to a minimum of \a size. Other constraints
+ may increase the limit.
+
+ \sa addItem()
+*/
+void QBoxLayout::addStrut(int size)
+{
+ Q_D(QBoxLayout);
+ QLayoutItem *b;
+ if (horz(d->dir))
+ b = QLayoutPrivate::createSpacerItem(this, 0, size, QSizePolicy::Fixed, QSizePolicy::Minimum);
+ else
+ b = QLayoutPrivate::createSpacerItem(this, size, 0, QSizePolicy::Minimum, QSizePolicy::Fixed);
+
+ QBoxLayoutItem *it = new QBoxLayoutItem(b);
+ it->magic = true;
+ d->list.append(it);
+ invalidate();
+}
+
+/*!
+ \fn int QBoxLayout::findWidget(QWidget *widget)
+
+ Use indexOf(\a widget) instead.
+*/
+
+/*!
+ Sets the stretch factor for \a widget to \a stretch and returns
+ true if \a widget is found in this layout (not including child
+ layouts); otherwise returns false.
+
+ \sa setAlignment()
+*/
+bool QBoxLayout::setStretchFactor(QWidget *widget, int stretch)
+{
+ Q_D(QBoxLayout);
+ if (!widget)
+ return false;
+ for (int i = 0; i < d->list.size(); ++i) {
+ QBoxLayoutItem *box = d->list.at(i);
+ if (box->item->widget() == widget) {
+ box->stretch = stretch;
+ invalidate();
+ return true;
+ }
+ }
+ return false;
+}
+
+/*!
+ \overload
+
+ Sets the stretch factor for the layout \a layout to \a stretch and
+ returns true if \a layout is found in this layout (not including
+ child layouts); otherwise returns false.
+*/
+bool QBoxLayout::setStretchFactor(QLayout *layout, int stretch)
+{
+ Q_D(QBoxLayout);
+ for (int i = 0; i < d->list.size(); ++i) {
+ QBoxLayoutItem *box = d->list.at(i);
+ if (box->item->layout() == layout) {
+ if (box->stretch != stretch) {
+ box->stretch = stretch;
+ invalidate();
+ }
+ return true;
+ }
+ }
+ return false;
+}
+
+/*!
+ Sets the stretch factor at position \a index. to \a stretch.
+
+ \since 4.5
+*/
+
+void QBoxLayout::setStretch(int index, int stretch)
+{
+ Q_D(QBoxLayout);
+ if (index >= 0 && index < d->list.size()) {
+ QBoxLayoutItem *box = d->list.at(index);
+ if (box->stretch != stretch) {
+ box->stretch = stretch;
+ invalidate();
+ }
+ }
+}
+
+/*!
+ Returns the stretch factor at position \a index.
+
+ \since 4.5
+*/
+
+int QBoxLayout::stretch(int index) const
+{
+ Q_D(const QBoxLayout);
+ if (index >= 0 && index < d->list.size())
+ return d->list.at(index)->stretch;
+ return -1;
+}
+
+/*!
+ Sets the direction of this layout to \a direction.
+*/
+void QBoxLayout::setDirection(Direction direction)
+{
+ Q_D(QBoxLayout);
+ if (d->dir == direction)
+ return;
+ if (horz(d->dir) != horz(direction)) {
+ //swap around the spacers (the "magic" bits)
+ //#### a bit yucky, knows too much.
+ //#### probably best to add access functions to spacerItem
+ //#### or even a QSpacerItem::flip()
+ for (int i = 0; i < d->list.size(); ++i) {
+ QBoxLayoutItem *box = d->list.at(i);
+ if (box->magic) {
+ QSpacerItem *sp = box->item->spacerItem();
+ if (sp) {
+ if (sp->expandingDirections() == Qt::Orientations(0) /*No Direction*/) {
+ //spacing or strut
+ QSize s = sp->sizeHint();
+ sp->changeSize(s.height(), s.width(),
+ horz(direction) ? QSizePolicy::Fixed:QSizePolicy::Minimum,
+ horz(direction) ? QSizePolicy::Minimum:QSizePolicy::Fixed);
+
+ } else {
+ //stretch
+ if (horz(direction))
+ sp->changeSize(0, 0, QSizePolicy::Expanding,
+ QSizePolicy::Minimum);
+ else
+ sp->changeSize(0, 0, QSizePolicy::Minimum,
+ QSizePolicy::Expanding);
+ }
+ }
+ }
+ }
+ }
+ d->dir = direction;
+ invalidate();
+}
+
+/*!
+ \fn QBoxLayout::Direction QBoxLayout::direction() const
+
+ Returns the direction of the box. addWidget() and addSpacing()
+ work in this direction; the stretch stretches in this direction.
+
+ \sa QBoxLayout::Direction addWidget() addSpacing()
+*/
+
+QBoxLayout::Direction QBoxLayout::direction() const
+{
+ Q_D(const QBoxLayout);
+ return d->dir;
+}
+
+/*!
+ \class QHBoxLayout
+ \brief The QHBoxLayout class lines up widgets horizontally.
+
+ \ingroup geomanagement
+ \ingroup appearance
+ \mainclass
+
+ This class is used to construct horizontal box layout objects. See
+ QBoxLayout for details.
+
+ The simplest use of the class is like this:
+
+ \snippet doc/src/snippets/layouts/layouts.cpp 0
+ \snippet doc/src/snippets/layouts/layouts.cpp 1
+ \snippet doc/src/snippets/layouts/layouts.cpp 2
+ \codeline
+ \snippet doc/src/snippets/layouts/layouts.cpp 3
+ \snippet doc/src/snippets/layouts/layouts.cpp 4
+ \snippet doc/src/snippets/layouts/layouts.cpp 5
+
+ First, we create the widgets we want in the layout. Then, we
+ create the QHBoxLayout object and add the widgets into the
+ layout. Finally, we call QWidget::setLayout() to install the
+ QHBoxLayout object onto the widget. At that point, the widgets in
+ the layout are reparented to have \c window as their parent.
+
+ \image qhboxlayout-with-5-children.png Horizontal box layout with five child widgets
+
+ \sa QVBoxLayout, QGridLayout, QStackedLayout, {Layout Classes}, {Basic Layouts Example}
+*/
+
+
+/*!
+ Constructs a new top-level horizontal box with
+ parent \a parent.
+*/
+QHBoxLayout::QHBoxLayout(QWidget *parent)
+ : QBoxLayout(LeftToRight, parent)
+{
+}
+
+/*!
+ Constructs a new horizontal box. You must add
+ it to another layout.
+*/
+QHBoxLayout::QHBoxLayout()
+ : QBoxLayout(LeftToRight)
+{
+}
+
+
+
+#ifdef QT3_SUPPORT
+/*!
+ 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 \a margin is used for \a spacing.
+*/
+QHBoxLayout::QHBoxLayout(QWidget *parent, int margin,
+ int spacing, const char *name)
+ : QBoxLayout(LeftToRight, parent)
+{
+ setMargin(margin);
+ setSpacing(spacing<0 ? margin : spacing);
+ setObjectName(QString::fromAscii(name));
+}
+
+/*!
+ Constructs a new horizontal box called name \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 QHBoxLayout will inherit its
+ parent's spacing().
+*/
+QHBoxLayout::QHBoxLayout(QLayout *parentLayout, int spacing,
+ const char *name)
+ : QBoxLayout(LeftToRight)
+{
+ setSpacing(spacing);
+ setObjectName(QString::fromAscii(name));
+ if (parentLayout) {
+ setParent(parentLayout);
+ parentLayout->addItem(this);
+ }
+}
+
+/*!
+ Constructs a new horizontal box called name \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().
+*/
+QHBoxLayout::QHBoxLayout(int spacing, const char *name)
+ : QBoxLayout(LeftToRight)
+{
+ setSpacing(spacing);
+ setObjectName(QString::fromAscii(name));
+}
+#endif
+
+
+/*!
+ Destroys this box layout.
+
+ The layout's widgets aren't destroyed.
+*/
+QHBoxLayout::~QHBoxLayout()
+{
+}
+
+/*!
+ \class QVBoxLayout
+ \brief The QVBoxLayout class lines up widgets vertically.
+
+ \ingroup geomanagement
+ \ingroup appearance
+ \mainclass
+
+ This class is used to construct vertical box layout objects. See
+ QBoxLayout for details.
+
+ The simplest use of the class is like this:
+
+ \snippet doc/src/snippets/layouts/layouts.cpp 6
+ \snippet doc/src/snippets/layouts/layouts.cpp 7
+ \snippet doc/src/snippets/layouts/layouts.cpp 8
+ \codeline
+ \snippet doc/src/snippets/layouts/layouts.cpp 9
+ \snippet doc/src/snippets/layouts/layouts.cpp 10
+ \snippet doc/src/snippets/layouts/layouts.cpp 11
+
+ First, we create the widgets we want in the layout. Then, we
+ create the QVBoxLayout object and add the widgets into the
+ layout. Finally, we call QWidget::setLayout() to install the
+ QVBoxLayout object onto the widget. At that point, the widgets in
+ the layout are reparented to have \c window as their parent.
+
+ \image qvboxlayout-with-5-children.png Horizontal box layout with five child widgets
+
+ \sa QHBoxLayout, QGridLayout, QStackedLayout, {Layout Classes}, {Basic Layouts Example}
+*/
+
+/*!
+ Constructs a new top-level vertical box with
+ parent \a parent.
+*/
+QVBoxLayout::QVBoxLayout(QWidget *parent)
+ : QBoxLayout(TopToBottom, parent)
+{
+}
+
+/*!
+ Constructs a new vertical box. You must add
+ it to another layout.
+
+*/
+QVBoxLayout::QVBoxLayout()
+ : QBoxLayout(TopToBottom)
+{
+}
+
+#ifdef QT3_SUPPORT
+/*!
+ 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 \a margin is used for \a spacing.
+*/
+QVBoxLayout::QVBoxLayout(QWidget *parent, int margin, int spacing,
+ const char *name)
+ : QBoxLayout(TopToBottom, parent)
+{
+ setMargin(margin);
+ setSpacing(spacing<0 ? margin : spacing);
+ setObjectName(QString::fromAscii(name));
+}
+
+/*!
+ Constructs a new vertical box called name \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 QVBoxLayout will inherit its
+ parent's spacing().
+*/
+QVBoxLayout::QVBoxLayout(QLayout *parentLayout, int spacing,
+ const char *name)
+ : QBoxLayout(TopToBottom)
+{
+ setSpacing(spacing);
+ setObjectName(QString::fromAscii(name));
+ if (parentLayout) {
+ setParent(parentLayout);
+ parentLayout->addItem(this);
+ }
+}
+
+/*!
+ Constructs a new vertical box called name \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 QVBoxLayout will inherit its
+ parent's spacing().
+*/
+QVBoxLayout::QVBoxLayout(int spacing, const char *name)
+ : QBoxLayout(TopToBottom)
+{
+ setSpacing(spacing);
+ setObjectName(QString::fromAscii(name));
+}
+
+
+#endif
+
+/*!
+ Destroys this box layout.
+
+ The layout's widgets aren't destroyed.
+*/
+QVBoxLayout::~QVBoxLayout()
+{
+}
+
+/*!
+ \fn QWidget *QLayout::mainWidget() const
+
+ Use parentWidget() instead.
+*/
+
+/*!
+ \fn void QLayout::remove(QWidget *widget)
+
+ Use removeWidget(\a widget) instead.
+*/
+
+/*!
+ \fn void QLayout::add(QWidget *widget)
+
+ Use addWidget(\a widget) instead.
+*/
+
+/*!
+ \fn QLayoutIterator QLayout::iterator()
+
+ Use a QLayoutIterator() constructor instead.
+*/
+
+/*!
+ \fn int QLayout::defaultBorder() const
+
+ Use spacing() instead.
+*/
+
+QT_END_NAMESPACE
diff --git a/src/gui/kernel/qboxlayout.h b/src/gui/kernel/qboxlayout.h
new file mode 100644
index 0000000000..f4ba0c7409
--- /dev/null
+++ b/src/gui/kernel/qboxlayout.h
@@ -0,0 +1,173 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QBOXLAYOUT_H
+#define QBOXLAYOUT_H
+
+#include <QtGui/qlayout.h>
+#ifdef QT_INCLUDE_COMPAT
+#include <QtGui/qwidget.h>
+#endif
+
+#include <limits.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+class QBoxLayoutPrivate;
+
+class Q_GUI_EXPORT QBoxLayout : public QLayout
+{
+ Q_OBJECT
+ Q_DECLARE_PRIVATE(QBoxLayout)
+public:
+ enum Direction { LeftToRight, RightToLeft, TopToBottom, BottomToTop,
+ Down = TopToBottom, Up = BottomToTop };
+
+ explicit QBoxLayout(Direction, QWidget *parent = 0);
+
+#ifdef QT3_SUPPORT
+ QT3_SUPPORT_CONSTRUCTOR QBoxLayout(QWidget *parent, Direction, int border = 0, int spacing = -1,
+ const char *name = 0);
+ QT3_SUPPORT_CONSTRUCTOR QBoxLayout(QLayout *parentLayout, Direction, int spacing = -1,
+ const char *name = 0);
+ QT3_SUPPORT_CONSTRUCTOR QBoxLayout(Direction, int spacing, const char *name = 0);
+#endif
+ ~QBoxLayout();
+
+ Direction direction() const;
+ void setDirection(Direction);
+
+ void addSpacing(int size);
+ void addStretch(int stretch = 0);
+ void addSpacerItem(QSpacerItem *spacerItem);
+ void addWidget(QWidget *, int stretch = 0, Qt::Alignment alignment = 0);
+ void addLayout(QLayout *layout, int stretch = 0);
+ void addStrut(int);
+ void addItem(QLayoutItem *);
+
+ void insertSpacing(int index, int size);
+ void insertStretch(int index, int stretch = 0);
+ void insertSpacerItem(int index, QSpacerItem *spacerItem);
+ void insertWidget(int index, QWidget *widget, int stretch = 0, Qt::Alignment alignment = 0);
+ void insertLayout(int index, QLayout *layout, int stretch = 0);
+
+ int spacing() const;
+ void setSpacing(int spacing);
+
+ bool setStretchFactor(QWidget *w, int stretch);
+ bool setStretchFactor(QLayout *l, int stretch);
+ void setStretch(int index, int stretch);
+ int stretch(int index) const;
+
+ QSize sizeHint() const;
+ QSize minimumSize() const;
+ QSize maximumSize() const;
+
+ bool hasHeightForWidth() const;
+ int heightForWidth(int) const;
+ int minimumHeightForWidth(int) const;
+
+ Qt::Orientations expandingDirections() const;
+ void invalidate();
+ QLayoutItem *itemAt(int) const;
+ QLayoutItem *takeAt(int);
+ int count() const;
+ void setGeometry(const QRect&);
+#ifdef QT3_SUPPORT
+ inline QT3_SUPPORT int findWidget(QWidget* w) {return indexOf(w);}
+#endif
+protected:
+ // ### Qt 5: make public
+ void insertItem(int index, QLayoutItem *);
+
+private:
+ Q_DISABLE_COPY(QBoxLayout)
+};
+
+class Q_GUI_EXPORT QHBoxLayout : public QBoxLayout
+{
+ Q_OBJECT
+public:
+ QHBoxLayout();
+ explicit QHBoxLayout(QWidget *parent);
+ ~QHBoxLayout();
+
+#ifdef QT3_SUPPORT
+ QT3_SUPPORT_CONSTRUCTOR QHBoxLayout(QWidget *parent, int border,
+ int spacing = -1, const char *name = 0);
+ QT3_SUPPORT_CONSTRUCTOR QHBoxLayout(QLayout *parentLayout,
+ int spacing = -1, const char *name = 0);
+ QT3_SUPPORT_CONSTRUCTOR QHBoxLayout(int spacing, const char *name = 0);
+#endif
+
+private:
+ Q_DISABLE_COPY(QHBoxLayout)
+};
+
+class Q_GUI_EXPORT QVBoxLayout : public QBoxLayout
+{
+ Q_OBJECT
+public:
+ QVBoxLayout();
+ explicit QVBoxLayout(QWidget *parent);
+ ~QVBoxLayout();
+
+#ifdef QT3_SUPPORT
+ QT3_SUPPORT_CONSTRUCTOR QVBoxLayout(QWidget *parent, int border,
+ int spacing = -1, const char *name = 0);
+ QT3_SUPPORT_CONSTRUCTOR QVBoxLayout(QLayout *parentLayout,
+ int spacing = -1, const char *name = 0);
+ QT3_SUPPORT_CONSTRUCTOR QVBoxLayout(int spacing, const char *name = 0);
+#endif
+
+private:
+ Q_DISABLE_COPY(QVBoxLayout)
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QBOXLAYOUT_H
diff --git a/src/gui/kernel/qclipboard.cpp b/src/gui/kernel/qclipboard.cpp
new file mode 100644
index 0000000000..917b5d59f5
--- /dev/null
+++ b/src/gui/kernel/qclipboard.cpp
@@ -0,0 +1,651 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qclipboard.h"
+
+#ifndef QT_NO_CLIPBOARD
+
+#include "qapplication.h"
+#include "qapplication_p.h"
+#include "qpixmap.h"
+#include "qclipboard_p.h"
+#include "qvariant.h"
+#include "qbuffer.h"
+#include "qimage.h"
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QClipboard
+ \brief The QClipboard class provides access to the window system clipboard.
+
+ \ingroup io
+ \ingroup environment
+ \mainclass
+
+ The clipboard offers a simple mechanism to copy and paste data
+ between applications.
+
+ QClipboard supports the same data types that QDrag does, and uses
+ similar mechanisms. For advanced clipboard usage read \l{Drag and
+ Drop}.
+
+ There is a single QClipboard object in an application, accessible
+ as QApplication::clipboard().
+
+ Example:
+ \snippet doc/src/snippets/code/src_gui_kernel_qclipboard.cpp 0
+
+ QClipboard features some convenience functions to access common
+ data types: setText() allows the exchange of Unicode text and
+ setPixmap() and setImage() allows the exchange of QPixmaps and
+ QImages between applications. The setMimeData() function is the
+ ultimate in flexibility: it allows you to add any QMimeData into
+ the clipboard. There are corresponding getters for each of these,
+ e.g. text(), image() and pixmap(). You can clear the clipboard by
+ calling clear().
+
+ A typical example of the use of these functions follows:
+
+ \snippet doc/src/snippets/droparea.cpp 0
+
+ \section1 Notes for X11 Users
+
+ \list
+
+ \i The X11 Window System has the concept of a separate selection
+ and clipboard. When text is selected, it is immediately available
+ as the global mouse selection. The global mouse selection may
+ later be copied to the clipboard. By convention, the middle mouse
+ button is used to paste the global mouse selection.
+
+ \i X11 also has the concept of ownership; if you change the
+ selection within a window, X11 will only notify the owner and the
+ previous owner of the change, i.e. it will not notify all
+ applications that the selection or clipboard data changed.
+
+ \i Lastly, the X11 clipboard is event driven, i.e. the clipboard
+ will not function properly if the event loop is not running.
+ Similarly, it is recommended that the contents of the clipboard
+ are stored or retrieved in direct response to user-input events,
+ e.g. mouse button or key presses and releases. You should not
+ store or retrieve the clipboard contents in response to timer or
+ non-user-input events.
+
+ \endlist
+
+ \section1 Notes for Mac OS X Users
+
+ Mac OS X supports a separate find buffer that holds the current
+ search string in Find operations. This find clipboard can be accessed
+ by specifying the FindBuffer mode.
+
+ \section1 Notes for Windows and Mac OS X Users
+
+ \list
+
+ \i Windows and Mac OS X do not support the global mouse
+ selection; they only supports the global clipboard, i.e. they
+ only add text to the clipboard when an explicit copy or cut is
+ made.
+
+ \i Windows and Mac OS X does not have the concept of ownership;
+ the clipboard is a fully global resource so all applications are
+ notified of changes.
+
+ \endlist
+
+ \sa QApplication
+*/
+
+#ifndef Q_WS_X11
+// for X11 there is a separate implementation of a constructor.
+/*!
+ \internal
+
+ Constructs a clipboard object.
+
+ Do not call this function.
+
+ Call QApplication::clipboard() instead to get a pointer to the
+ application's global clipboard object.
+
+ There is only one clipboard in the window system, and creating
+ more than one object to represent it is almost certainly an error.
+*/
+
+QClipboard::QClipboard(QObject *parent)
+ : QObject(*new QClipboardPrivate, parent)
+{
+ // nothing
+}
+#endif
+
+#ifndef Q_WS_WIN32
+/*!
+ \internal
+
+ Destroys the clipboard.
+
+ You should never delete the clipboard. QApplication will do this
+ when the application terminates.
+*/
+QClipboard::~QClipboard()
+{
+}
+#endif
+
+/*!
+ \fn void QClipboard::changed(QClipboard::Mode mode)
+ \since 4.2
+
+ This signal is emitted when the data for the given clipboard \a
+ mode is changed.
+
+ \sa dataChanged(), selectionChanged(), findBufferChanged()
+*/
+
+/*!
+ \fn void QClipboard::dataChanged()
+
+ This signal is emitted when the clipboard data is changed.
+
+ On Mac OS X and with Qt version 4.3 or higher, clipboard
+ changes made by other applications will only be detected
+ when the application is activated.
+
+ \sa findBufferChanged(), selectionChanged(), changed()
+*/
+
+/*!
+ \fn void QClipboard::selectionChanged()
+
+ This signal is emitted when the selection is changed. This only
+ applies to windowing systems that support selections, e.g. X11.
+ Windows and Mac OS X don't support selections.
+
+ \sa dataChanged(), findBufferChanged(), changed()
+*/
+
+/*!
+ \fn void QClipboard::findBufferChanged()
+ \since 4.2
+
+ This signal is emitted when the find buffer is changed. This only
+ applies to Mac OS X.
+
+ With Qt version 4.3 or higher, clipboard changes made by other
+ applications will only be detected when the application is activated.
+
+ \sa dataChanged(), selectionChanged(), changed()
+*/
+
+
+/*! \enum QClipboard::Mode
+ \keyword clipboard mode
+
+ This enum type is used to control which part of the system clipboard is
+ used by QClipboard::mimeData(), QClipboard::setMimeData() and related functions.
+
+ \value Clipboard indicates that data should be stored and retrieved from
+ the global clipboard.
+
+ \value Selection indicates that data should be stored and retrieved from
+ the global mouse selection. Support for \c Selection is provided only on
+ systems with a global mouse selection (e.g. X11).
+
+ \value FindBuffer indicates that data should be stored and retrieved from
+ the Find buffer. This mode is used for holding search strings on Mac OS X.
+
+ \omitvalue LastMode
+
+ \sa QClipboard::supportsSelection()
+*/
+
+
+/*****************************************************************************
+ QApplication member functions related to QClipboard.
+ *****************************************************************************/
+
+// text handling is done directly in qclipboard_qws, for now
+
+/*!
+ \fn bool QClipboard::event(QEvent *e)
+ \reimp
+*/
+
+/*!
+ \overload
+
+ Returns the clipboard text in subtype \a subtype, or an empty string
+ if the clipboard does not contain any text. If \a subtype is null,
+ any subtype is acceptable, and \a subtype is set to the chosen
+ subtype.
+
+ The \a mode argument is used to control which part of the system
+ clipboard is used. If \a mode is QClipboard::Clipboard, the
+ text is retrieved from the global clipboard. If \a mode is
+ QClipboard::Selection, the text is retrieved from the global
+ mouse selection.
+
+ Common values for \a subtype are "plain" and "html".
+
+ Note that calling this function repeatedly, for instance from a
+ key event handler, may be slow. In such cases, you should use the
+ \c dataChanged() signal instead.
+
+ \sa setText(), mimeData()
+*/
+QString QClipboard::text(QString &subtype, Mode mode) const
+{
+ const QMimeData *data = mimeData(mode);
+ if (!data)
+ return QString();
+ if (subtype.isEmpty()) {
+ QStringList formats = data->formats();
+ if (formats.contains(QLatin1String("text/plain")))
+ subtype = QLatin1String("plain");
+ else {
+ for (int i = 0; i < formats.size(); ++i)
+ if (formats.at(i).startsWith(QLatin1String("text/"))) {
+ subtype = formats.at(i).mid(5);
+ break;
+ }
+ }
+ }
+ if (subtype.isEmpty())
+ return QString();
+ if (subtype == QLatin1String("plain"))
+ return data->text();
+ return QString::fromUtf8(data->data(QLatin1String("text/") + subtype));
+}
+
+/*!
+ Returns the clipboard text as plain text, or an empty string if the
+ clipboard does not contain any text.
+
+ The \a mode argument is used to control which part of the system
+ clipboard is used. If \a mode is QClipboard::Clipboard, the
+ text is retrieved from the global clipboard. If \a mode is
+ QClipboard::Selection, the text is retrieved from the global
+ mouse selection. If \a mode is QClipboard::FindBuffer, the
+ text is retrieved from the search string buffer.
+
+ \sa setText(), mimeData()
+*/
+QString QClipboard::text(Mode mode) const
+{
+ const QMimeData *data = mimeData(mode);
+ return data ? data->text() : QString();
+}
+
+/*!
+ Copies \a text into the clipboard as plain text.
+
+ The \a mode argument is used to control which part of the system
+ clipboard is used. If \a mode is QClipboard::Clipboard, the
+ text is stored in the global clipboard. If \a mode is
+ QClipboard::Selection, the text is stored in the global
+ mouse selection. If \a mode is QClipboard::FindBuffer, the
+ text is stored in the search string buffer.
+
+ \sa text(), setMimeData()
+*/
+void QClipboard::setText(const QString &text, Mode mode)
+{
+ QMimeData *data = new QMimeData;
+ data->setText(text);
+ setMimeData(data, mode);
+}
+
+/*!
+ Returns the clipboard image, or returns a null image if the
+ clipboard does not contain an image or if it contains an image in
+ an unsupported image format.
+
+ The \a mode argument is used to control which part of the system
+ clipboard is used. If \a mode is QClipboard::Clipboard, the
+ image is retrieved from the global clipboard. If \a mode is
+ QClipboard::Selection, the image is retrieved from the global
+ mouse selection.
+
+ \sa setImage() pixmap() mimeData(), QImage::isNull()
+*/
+QImage QClipboard::image(Mode mode) const
+{
+ const QMimeData *data = mimeData(mode);
+ if (!data)
+ return QImage();
+ return qvariant_cast<QImage>(data->imageData());
+}
+
+/*!
+ Copies the \a image into the clipboard.
+
+ The \a mode argument is used to control which part of the system
+ clipboard is used. If \a mode is QClipboard::Clipboard, the
+ image is stored in the global clipboard. If \a mode is
+ QClipboard::Selection, the data is stored in the global
+ mouse selection.
+
+ This is shorthand for:
+
+ \snippet doc/src/snippets/code/src_gui_kernel_qclipboard.cpp 1
+
+ \sa image(), setPixmap() setMimeData()
+*/
+void QClipboard::setImage(const QImage &image, Mode mode)
+{
+ QMimeData *data = new QMimeData;
+ data->setImageData(image);
+ setMimeData(data, mode);
+}
+
+/*!
+ Returns the clipboard pixmap, or null if the clipboard does not
+ contain a pixmap. Note that this can lose information. For
+ example, if the image is 24-bit and the display is 8-bit, the
+ result is converted to 8 bits, and if the image has an alpha
+ channel, the result just has a mask.
+
+ The \a mode argument is used to control which part of the system
+ clipboard is used. If \a mode is QClipboard::Clipboard, the
+ pixmap is retrieved from the global clipboard. If \a mode is
+ QClipboard::Selection, the pixmap is retrieved from the global
+ mouse selection.
+
+ \sa setPixmap() image() mimeData() QPixmap::convertFromImage()
+*/
+QPixmap QClipboard::pixmap(Mode mode) const
+{
+ const QMimeData *data = mimeData(mode);
+ return data ? qvariant_cast<QPixmap>(data->imageData()) : QPixmap();
+}
+
+/*!
+ Copies \a pixmap into the clipboard. Note that this is slower
+ than setImage() because it needs to convert the QPixmap to a
+ QImage first.
+
+ The \a mode argument is used to control which part of the system
+ clipboard is used. If \a mode is QClipboard::Clipboard, the
+ pixmap is stored in the global clipboard. If \a mode is
+ QClipboard::Selection, the pixmap is stored in the global
+ mouse selection.
+
+ \sa pixmap() setImage() setMimeData()
+*/
+void QClipboard::setPixmap(const QPixmap &pixmap, Mode mode)
+{
+ QMimeData *data = new QMimeData;
+ data->setImageData(pixmap);
+ setMimeData(data, mode);
+}
+
+
+/*!
+ \fn QMimeData *QClipboard::mimeData(Mode mode) const
+
+ Returns a reference to a QMimeData representation of the current
+ clipboard data.
+
+ The \a mode argument is used to control which part of the system
+ clipboard is used. If \a mode is QClipboard::Clipboard, the
+ data is retrieved from the global clipboard. If \a mode is
+ QClipboard::Selection, the data is retrieved from the global
+ mouse selection. If \a mode is QClipboard::FindBuffer, the
+ data is retrieved from the search string buffer.
+
+ The text(), image(), and pixmap() functions are simpler
+ wrappers for retrieving text, image, and pixmap data.
+
+ \sa setMimeData()
+*/
+
+/*!
+ \fn void QClipboard::setMimeData(QMimeData *src, Mode mode)
+
+ Sets the clipboard data to \a src. Ownership of the data is
+ transferred to the clipboard. If you want to remove the data
+ either call clear() or call setMimeData() again with new data.
+
+ The \a mode argument is used to control which part of the system
+ clipboard is used. If \a mode is QClipboard::Clipboard, the
+ data is stored in the global clipboard. If \a mode is
+ QClipboard::Selection, the data is stored in the global
+ mouse selection. If \a mode is QClipboard::FindBuffer, the
+ data is stored in the search string buffer.
+
+ The setText(), setImage() and setPixmap() functions are simpler
+ wrappers for setting text, image and pixmap data respectively.
+
+ \sa mimeData()
+*/
+
+/*!
+ \fn void QClipboard::clear(Mode mode)
+ Clear the clipboard contents.
+
+ The \a mode argument is used to control which part of the system
+ clipboard is used. If \a mode is QClipboard::Clipboard, this
+ function clears the the global clipboard contents. If \a mode is
+ QClipboard::Selection, this function clears the global mouse
+ selection contents. If \a mode is QClipboard::FindBuffer, this
+ function clears the search string buffer.
+
+ \sa QClipboard::Mode, supportsSelection()
+*/
+
+#ifdef QT3_SUPPORT
+/*!
+ \fn QMimeSource *QClipboard::data(Mode mode) const
+ \compat
+
+ Use mimeData() instead.
+*/
+QMimeSource *QClipboard::data(Mode mode) const
+{
+ Q_D(const QClipboard);
+
+ if (supportsMode(mode) == false)
+ return 0;
+
+ if (d->compat_data[mode])
+ return d->compat_data[mode];
+
+ d->wrapper[mode]->data = mimeData(mode);
+ return d->wrapper[mode];
+}
+
+
+/*!
+ \fn void QClipboard::setData(QMimeSource *src, Mode mode)
+ \compat
+
+ Use setMimeData() instead.
+*/
+void QClipboard::setData(QMimeSource *source, Mode mode)
+{
+ Q_D(QClipboard);
+
+ if (supportsMode(mode) == false)
+ return;
+
+ d->compat_data[mode] = source;
+ setMimeData(new QMimeSourceWrapper(d, mode), mode);
+}
+#endif // QT3_SUPPORT
+
+/*!
+ Returns true if the clipboard supports mouse selection; otherwise
+ returns false.
+*/
+bool QClipboard::supportsSelection() const
+{
+ return supportsMode(Selection);
+}
+
+/*!
+ Returns true if the clipboard supports a separate search buffer; otherwise
+ returns false.
+*/
+bool QClipboard::supportsFindBuffer() const
+{
+ return supportsMode(FindBuffer);
+}
+
+/*!
+ Returns true if this clipboard object owns the clipboard data;
+ otherwise returns false.
+*/
+bool QClipboard::ownsClipboard() const
+{
+ return ownsMode(Clipboard);
+}
+
+/*!
+ Returns true if this clipboard object owns the mouse selection
+ data; otherwise returns false.
+*/
+bool QClipboard::ownsSelection() const
+{
+ return ownsMode(Selection);
+}
+
+/*!
+ \since 4.2
+
+ Returns true if this clipboard object owns the find buffer data;
+ otherwise returns false.
+*/
+bool QClipboard::ownsFindBuffer() const
+{
+ return ownsMode(FindBuffer);
+}
+
+/*!
+ \internal
+ \fn bool QClipboard::supportsMode(Mode mode) const;
+ Returns true if the clipboard supports the clipboard mode speacified by \a mode;
+ otherwise returns false.
+*/
+
+/*!
+ \internal
+ \fn bool QClipboard::ownsMode(Mode mode) const;
+ Returns true if the clipboard supports the clipboard data speacified by \a mode;
+ otherwise returns false.
+*/
+
+/*!
+ \internal
+ Emits the appropriate changed signal for \a mode.
+*/
+void QClipboard::emitChanged(Mode mode)
+{
+ switch (mode) {
+ case Clipboard:
+ emit dataChanged();
+ break;
+ case Selection:
+ emit selectionChanged();
+ break;
+ case FindBuffer:
+ emit findBufferChanged();
+ break;
+ default:
+ break;
+ }
+ emit changed(mode);
+}
+
+const char* QMimeDataWrapper::format(int n) const
+{
+ if (formats.isEmpty()) {
+ QStringList fmts = data->formats();
+ for (int i = 0; i < fmts.size(); ++i)
+ formats.append(fmts.at(i).toLatin1());
+ }
+ if (n < 0 || n >= formats.size())
+ return 0;
+ return formats.at(n).data();
+}
+
+QByteArray QMimeDataWrapper::encodedData(const char *format) const
+{
+ if (QLatin1String(format) != QLatin1String("application/x-qt-image")){
+ return data->data(QLatin1String(format));
+ } else{
+ QVariant variant = data->imageData();
+ QImage img = qVariantValue<QImage>(variant);
+ QByteArray ba;
+ QBuffer buffer(&ba);
+ buffer.open(QIODevice::WriteOnly);
+ img.save(&buffer, "PNG");
+ return ba;
+ }
+}
+
+QVariant QMimeSourceWrapper::retrieveData(const QString &mimetype, QVariant::Type) const
+{
+ return source->encodedData(mimetype.toLatin1());
+}
+
+bool QMimeSourceWrapper::hasFormat(const QString &mimetype) const
+{
+ return source->provides(mimetype.toLatin1());
+}
+
+QStringList QMimeSourceWrapper::formats() const
+{
+ QStringList fmts;
+ int i = 0;
+ const char *fmt;
+ while ((fmt = source->format(i))) {
+ fmts.append(QLatin1String(fmt));
+ ++i;
+ }
+ return fmts;
+}
+
+#endif // QT_NO_CLIPBOARD
+
+QT_END_NAMESPACE
diff --git a/src/gui/kernel/qclipboard.h b/src/gui/kernel/qclipboard.h
new file mode 100644
index 0000000000..fa3c6fabf0
--- /dev/null
+++ b/src/gui/kernel/qclipboard.h
@@ -0,0 +1,130 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QCLIPBOARD_H
+#define QCLIPBOARD_H
+
+#include <QtCore/qobject.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+#ifndef QT_NO_CLIPBOARD
+
+class QMimeSource;
+class QMimeData;
+class QImage;
+class QPixmap;
+
+class QClipboardPrivate;
+
+class Q_GUI_EXPORT QClipboard : public QObject
+{
+ Q_OBJECT
+ Q_DECLARE_PRIVATE(QClipboard)
+private:
+ QClipboard(QObject *parent);
+ ~QClipboard();
+
+public:
+ enum Mode { Clipboard, Selection, FindBuffer, LastMode = FindBuffer };
+
+ void clear(Mode mode = Clipboard);
+
+ bool supportsSelection() const;
+ bool supportsFindBuffer() const;
+
+ bool ownsSelection() const;
+ bool ownsClipboard() const;
+ bool ownsFindBuffer() const;
+
+ QString text(Mode mode = Clipboard) const;
+ QString text(QString& subtype, Mode mode = Clipboard) const;
+ void setText(const QString &, Mode mode = Clipboard);
+
+#ifdef QT3_SUPPORT
+ QT3_SUPPORT QMimeSource *data(Mode mode = Clipboard) const;
+ QT3_SUPPORT void setData(QMimeSource*, Mode mode = Clipboard);
+#endif
+ const QMimeData *mimeData(Mode mode = Clipboard ) const;
+ void setMimeData(QMimeData *data, Mode mode = Clipboard);
+
+ QImage image(Mode mode = Clipboard) const;
+ QPixmap pixmap(Mode mode = Clipboard) const;
+ void setImage(const QImage &, Mode mode = Clipboard);
+ void setPixmap(const QPixmap &, Mode mode = Clipboard);
+
+Q_SIGNALS:
+ void changed(QClipboard::Mode mode);
+ void selectionChanged();
+ void findBufferChanged();
+ void dataChanged();
+private Q_SLOTS:
+ void ownerDestroyed();
+
+protected:
+ void connectNotify(const char *);
+ bool event(QEvent *);
+
+ friend class QApplication;
+ friend class QApplicationPrivate;
+ friend class QBaseApplication;
+ friend class QDragManager;
+ friend class QMimeSource;
+
+private:
+ Q_DISABLE_COPY(QClipboard)
+
+ bool supportsMode(Mode mode) const;
+ bool ownsMode(Mode mode) const;
+ void emitChanged(Mode mode);
+};
+
+#endif // QT_NO_CLIPBOARD
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QCLIPBOARD_H
diff --git a/src/gui/kernel/qclipboard_mac.cpp b/src/gui/kernel/qclipboard_mac.cpp
new file mode 100644
index 0000000000..aafa5879b7
--- /dev/null
+++ b/src/gui/kernel/qclipboard_mac.cpp
@@ -0,0 +1,612 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qclipboard.h"
+#include "qapplication.h"
+#include "qbitmap.h"
+#include "qdatetime.h"
+#include "qdebug.h"
+#include "qapplication_p.h"
+#include <private/qt_mac_p.h>
+#include "qevent.h"
+#include "qurl.h"
+#include <stdlib.h>
+#include <string.h>
+
+QT_BEGIN_NAMESPACE
+
+QT_USE_NAMESPACE
+
+/*****************************************************************************
+ QClipboard debug facilities
+ *****************************************************************************/
+//#define DEBUG_PASTEBOARD
+
+#ifndef QT_NO_CLIPBOARD
+
+/*****************************************************************************
+ QClipboard member functions for mac.
+ *****************************************************************************/
+
+static QMacPasteboard *qt_mac_pasteboards[2] = {0, 0};
+
+static inline QMacPasteboard *qt_mac_pasteboard(QClipboard::Mode mode)
+{
+ Q_ASSERT(mode == QClipboard::Clipboard || mode == QClipboard::FindBuffer);
+ if (mode == QClipboard::Clipboard)
+ return qt_mac_pasteboards[0];
+ else
+ return qt_mac_pasteboards[1];
+}
+
+static void qt_mac_cleanupPasteboard() {
+ delete qt_mac_pasteboards[0];
+ delete qt_mac_pasteboards[1];
+ qt_mac_pasteboards[0] = 0;
+ qt_mac_pasteboards[1] = 0;
+}
+
+static bool qt_mac_updateScrap(QClipboard::Mode mode)
+{
+ if(!qt_mac_pasteboards[0]) {
+ qt_mac_pasteboards[0] = new QMacPasteboard(kPasteboardClipboard, QMacPasteboardMime::MIME_CLIP);
+ qt_mac_pasteboards[1] = new QMacPasteboard(kPasteboardFind, QMacPasteboardMime::MIME_CLIP);
+ qAddPostRoutine(qt_mac_cleanupPasteboard);
+ return true;
+ }
+ return qt_mac_pasteboard(mode)->sync();
+}
+
+void QClipboard::clear(Mode mode)
+{
+ if (!supportsMode(mode))
+ return;
+ qt_mac_updateScrap(mode);
+ qt_mac_pasteboard(mode)->clear();
+ setMimeData(0, mode);
+}
+
+void QClipboard::ownerDestroyed()
+{
+}
+
+
+void QClipboard::connectNotify(const char *signal)
+{
+ Q_UNUSED(signal);
+}
+
+bool QClipboard::event(QEvent *e)
+{
+ if(e->type() != QEvent::Clipboard)
+ return QObject::event(e);
+
+ if (qt_mac_updateScrap(QClipboard::Clipboard)) {
+ emitChanged(QClipboard::Clipboard);
+ }
+
+ if (qt_mac_updateScrap(QClipboard::FindBuffer)) {
+ emitChanged(QClipboard::FindBuffer);
+ }
+
+ return QObject::event(e);
+}
+
+const QMimeData *QClipboard::mimeData(Mode mode) const
+{
+ if (!supportsMode(mode))
+ return 0;
+ qt_mac_updateScrap(mode);
+ return qt_mac_pasteboard(mode)->mimeData();
+}
+
+void QClipboard::setMimeData(QMimeData *src, Mode mode)
+{
+ if (!supportsMode(mode))
+ return;
+ qt_mac_updateScrap(mode);
+ qt_mac_pasteboard(mode)->setMimeData(src);
+ emitChanged(mode);
+}
+
+bool QClipboard::supportsMode(Mode mode) const
+{
+ return (mode == Clipboard || mode == FindBuffer);
+}
+
+bool QClipboard::ownsMode(Mode mode) const
+{
+ Q_UNUSED(mode);
+ return false;
+}
+
+#endif // QT_NO_CLIPBOARD
+
+/*****************************************************************************
+ QMacPasteboard code
+*****************************************************************************/
+
+QMacPasteboard::QMacPasteboard(PasteboardRef p, uchar mt)
+{
+ mac_mime_source = false;
+ mime_type = mt ? mt : uchar(QMacPasteboardMime::MIME_ALL);
+ paste = p;
+ CFRetain(paste);
+}
+
+QMacPasteboard::QMacPasteboard(uchar mt)
+{
+ mac_mime_source = false;
+ mime_type = mt ? mt : uchar(QMacPasteboardMime::MIME_ALL);
+ paste = 0;
+ OSStatus err = PasteboardCreate(0, &paste);
+ if(err == noErr) {
+ PasteboardSetPromiseKeeper(paste, promiseKeeper, this);
+ } else {
+ qDebug("PasteBoard: Error creating pasteboard: [%d]", (int)err);
+ }
+}
+
+QMacPasteboard::QMacPasteboard(CFStringRef name, uchar mt)
+{
+ mac_mime_source = false;
+ mime_type = mt ? mt : uchar(QMacPasteboardMime::MIME_ALL);
+ paste = 0;
+ OSStatus err = PasteboardCreate(name, &paste);
+ if(err == noErr) {
+ PasteboardSetPromiseKeeper(paste, promiseKeeper, this);
+ } else {
+ qDebug("PasteBoard: Error creating pasteboard: %s [%d]", QCFString::toQString(name).toLatin1().constData(), (int)err);
+ }
+}
+
+QMacPasteboard::~QMacPasteboard()
+{
+ // commit all promises for paste after exit close
+ for (int i = 0; i < promises.count(); ++i) {
+ const Promise &promise = promises.at(i);
+ QCFString flavor = QCFString(promise.convertor->flavorFor(promise.mime));
+ promiseKeeper(paste, (PasteboardItemID)promise.itemId, flavor, this);
+ }
+
+ if(paste)
+ CFRelease(paste);
+}
+
+PasteboardRef
+QMacPasteboard::pasteBoard() const
+{
+ return paste;
+}
+
+OSStatus QMacPasteboard::promiseKeeper(PasteboardRef paste, PasteboardItemID id, CFStringRef flavor, void *_qpaste)
+{
+ QMacPasteboard *qpaste = (QMacPasteboard*)_qpaste;
+ const long promise_id = (long)id;
+
+ // Find the kept promise
+ const QString flavorAsQString = QCFString::toQString(flavor);
+ QMacPasteboard::Promise promise;
+ for (int i = 0; i < qpaste->promises.size(); i++){
+ QMacPasteboard::Promise tmp = qpaste->promises[i];
+ if (tmp.itemId == promise_id && tmp.convertor->canConvert(tmp.mime, flavorAsQString)){
+ promise = tmp;
+ break;
+ }
+ }
+
+ if (!promise.itemId && flavorAsQString == QLatin1String("com.trolltech.qt.MimeTypeName")) {
+ // we have promised this data, but wont be able to convert, so return null data.
+ // This helps in making the application/x-qt-mime-type-name hidden from normal use.
+ QByteArray ba;
+ QCFType<CFDataRef> data = CFDataCreate(0, (UInt8*)ba.constData(), ba.size());
+ PasteboardPutItemFlavor(paste, id, flavor, data, kPasteboardFlavorNoFlags);
+ return noErr;
+ }
+
+ if (!promise.itemId) {
+ // There was no promise that could deliver data for the
+ // given id and flavor. This should not happend.
+ qDebug("Pasteboard: %d: Request for %ld, %s, but no promise found!", __LINE__, promise_id, qPrintable(flavorAsQString));
+ return cantGetFlavorErr;
+ }
+
+#ifdef DEBUG_PASTEBOARD
+ qDebug("PasteBoard: Calling in promise for %s[%ld] [%s] (%s) [%d]", qPrintable(promise.mime), promise_id,
+ qPrintable(flavorAsQString), qPrintable(promise.convertor->convertorName()), promise.offset);
+#endif
+
+ QList<QByteArray> md = promise.convertor->convertFromMime(promise.mime, promise.data, flavorAsQString);
+ if (md.size() <= promise.offset)
+ return cantGetFlavorErr;
+ const QByteArray &ba = md[promise.offset];
+ QCFType<CFDataRef> data = CFDataCreate(0, (UInt8*)ba.constData(), ba.size());
+ PasteboardPutItemFlavor(paste, id, flavor, data, kPasteboardFlavorNoFlags);
+ return noErr;
+}
+
+bool
+QMacPasteboard::hasOSType(int c_flavor) const
+{
+ if (!paste)
+ return false;
+
+ sync();
+
+ ItemCount cnt = 0;
+ if(PasteboardGetItemCount(paste, &cnt) || !cnt)
+ return false;
+
+#ifdef DEBUG_PASTEBOARD
+ qDebug("PasteBoard: hasOSType [%c%c%c%c]", (c_flavor>>24)&0xFF, (c_flavor>>16)&0xFF,
+ (c_flavor>>8)&0xFF, (c_flavor>>0)&0xFF);
+#endif
+ for(uint index = 1; index <= cnt; ++index) {
+
+ PasteboardItemID id;
+ if(PasteboardGetItemIdentifier(paste, index, &id) != noErr)
+ return false;
+
+ QCFType<CFArrayRef> types;
+ if(PasteboardCopyItemFlavors(paste, id, &types ) != noErr)
+ return false;
+
+ const int type_count = CFArrayGetCount(types);
+ for(int i = 0; i < type_count; ++i) {
+ CFStringRef flavor = (CFStringRef)CFArrayGetValueAtIndex(types, i);
+ const int os_flavor = UTGetOSTypeFromString(UTTypeCopyPreferredTagWithClass(flavor, kUTTagClassOSType));
+ if(os_flavor == c_flavor) {
+#ifdef DEBUG_PASTEBOARD
+ qDebug(" - Found!");
+#endif
+ return true;
+ }
+ }
+ }
+#ifdef DEBUG_PASTEBOARD
+ qDebug(" - NotFound!");
+#endif
+ return false;
+}
+
+bool
+QMacPasteboard::hasFlavor(QString c_flavor) const
+{
+ if (!paste)
+ return false;
+
+ sync();
+
+ ItemCount cnt = 0;
+ if(PasteboardGetItemCount(paste, &cnt) || !cnt)
+ return false;
+
+#ifdef DEBUG_PASTEBOARD
+ qDebug("PasteBoard: hasFlavor [%s]", qPrintable(c_flavor));
+#endif
+ for(uint index = 1; index <= cnt; ++index) {
+
+ PasteboardItemID id;
+ if(PasteboardGetItemIdentifier(paste, index, &id) != noErr)
+ return false;
+
+ PasteboardFlavorFlags flags;
+ if(PasteboardGetItemFlavorFlags(paste, id, QCFString(c_flavor), &flags) == noErr) {
+#ifdef DEBUG_PASTEBOARD
+ qDebug(" - Found!");
+#endif
+ return true;
+ }
+ }
+#ifdef DEBUG_PASTEBOARD
+ qDebug(" - NotFound!");
+#endif
+ return false;
+}
+
+class QMacPasteboardMimeSource : public QMimeData {
+ const QMacPasteboard *paste;
+public:
+ QMacPasteboardMimeSource(const QMacPasteboard *p) : QMimeData(), paste(p) { }
+ ~QMacPasteboardMimeSource() { }
+ virtual QStringList formats() const { return paste->formats(); }
+ virtual QVariant retrieveData(const QString &format, QVariant::Type type) const { return paste->retrieveData(format, type); }
+};
+
+QMimeData
+*QMacPasteboard::mimeData() const
+{
+ if(!mime) {
+ mac_mime_source = true;
+ mime = new QMacPasteboardMimeSource(this);
+
+ }
+ return mime;
+}
+
+class QMacMimeData : public QMimeData
+{
+public:
+ QVariant variantData(const QString &mime) { return retrieveData(mime, QVariant::Invalid); }
+private:
+ QMacMimeData();
+};
+
+void
+QMacPasteboard::setMimeData(QMimeData *mime_src)
+{
+ if (!paste)
+ return;
+
+ if (mime == mime_src || (!mime_src && mime && mac_mime_source))
+ return;
+ mac_mime_source = false;
+ delete mime;
+ mime = mime_src;
+
+ QList<QMacPasteboardMime*> availableConverters = QMacPasteboardMime::all(mime_type);
+ if (mime != 0) {
+ clear_helper();
+ QStringList formats = mime_src->formats();
+
+ for(int f = 0; f < formats.size(); ++f) {
+ QString mimeType = formats.at(f);
+ for (QList<QMacPasteboardMime *>::Iterator it = availableConverters.begin(); it != availableConverters.end(); ++it) {
+ QMacPasteboardMime *c = (*it);
+ QString flavor(c->flavorFor(mimeType));
+ if(!flavor.isEmpty()) {
+ QVariant mimeData = static_cast<QMacMimeData*>(mime_src)->variantData(mimeType);
+#if 0
+ //### Grrr, why didn't I put in a virtual int QMacPasteboardMime::count()? --Sam
+ const int numItems = c->convertFromMime(mimeType, mimeData, flavor).size();
+#else
+ int numItems = 1; //this is a hack but it is much faster than allowing conversion above
+ if(c->convertorName() == QLatin1String("FileURL"))
+ numItems = mime_src->urls().count();
+#endif
+ for(int item = 0; item < numItems; ++item) {
+ const int itemID = item+1; //id starts at 1
+ promises.append(QMacPasteboard::Promise(itemID, c, mimeType, mimeData, item));
+ PasteboardPutItemFlavor(paste, (PasteboardItemID)itemID, QCFString(flavor), 0, kPasteboardFlavorNoFlags);
+#ifdef DEBUG_PASTEBOARD
+ qDebug(" - adding %d %s [%s] <%s> [%d]",
+ itemID, qPrintable(mimeType), qPrintable(flavor), qPrintable(c->convertorName()), item);
+#endif
+ }
+ }
+ }
+ }
+ }
+}
+
+QStringList
+QMacPasteboard::formats() const
+{
+ if (!paste)
+ return QStringList();
+
+ sync();
+
+ QStringList ret;
+ ItemCount cnt = 0;
+ if(PasteboardGetItemCount(paste, &cnt) || !cnt)
+ return ret;
+
+#ifdef DEBUG_PASTEBOARD
+ qDebug("PasteBoard: Formats [%d]", (int)cnt);
+#endif
+ for(uint index = 1; index <= cnt; ++index) {
+
+ PasteboardItemID id;
+ if(PasteboardGetItemIdentifier(paste, index, &id) != noErr)
+ continue;
+
+ QCFType<CFArrayRef> types;
+ if(PasteboardCopyItemFlavors(paste, id, &types ) != noErr)
+ continue;
+
+ const int type_count = CFArrayGetCount(types);
+ for(int i = 0; i < type_count; ++i) {
+ const QString flavor = QCFString::toQString((CFStringRef)CFArrayGetValueAtIndex(types, i));
+#ifdef DEBUG_PASTEBOARD
+ qDebug(" -%s", qPrintable(QString(flavor)));
+#endif
+ QString mimeType = QMacPasteboardMime::flavorToMime(mime_type, flavor);
+ if(!mimeType.isEmpty() && !ret.contains(mimeType)) {
+#ifdef DEBUG_PASTEBOARD
+ qDebug(" -<%d> %s [%s]", ret.size(), qPrintable(mimeType), qPrintable(QString(flavor)));
+#endif
+ ret << mimeType;
+ }
+ }
+ }
+ return ret;
+}
+
+bool
+QMacPasteboard::hasFormat(const QString &format) const
+{
+ if (!paste)
+ return false;
+
+ sync();
+
+ ItemCount cnt = 0;
+ if(PasteboardGetItemCount(paste, &cnt) || !cnt)
+ return false;
+
+#ifdef DEBUG_PASTEBOARD
+ qDebug("PasteBoard: hasFormat [%s]", qPrintable(format));
+#endif
+ for(uint index = 1; index <= cnt; ++index) {
+
+ PasteboardItemID id;
+ if(PasteboardGetItemIdentifier(paste, index, &id) != noErr)
+ continue;
+
+ QCFType<CFArrayRef> types;
+ if(PasteboardCopyItemFlavors(paste, id, &types ) != noErr)
+ continue;
+
+ const int type_count = CFArrayGetCount(types);
+ for(int i = 0; i < type_count; ++i) {
+ const QString flavor = QCFString::toQString((CFStringRef)CFArrayGetValueAtIndex(types, i));
+#ifdef DEBUG_PASTEBOARD
+ qDebug(" -%s [0x%x]", qPrintable(QString(flavor)), mime_type);
+#endif
+ QString mimeType = QMacPasteboardMime::flavorToMime(mime_type, flavor);
+#ifdef DEBUG_PASTEBOARD
+ if(!mimeType.isEmpty())
+ qDebug(" - %s", qPrintable(mimeType));
+#endif
+ if(mimeType == format)
+ return true;
+ }
+ }
+ return false;
+}
+
+QVariant
+QMacPasteboard::retrieveData(const QString &format, QVariant::Type) const
+{
+ if (!paste)
+ return QVariant();
+
+ sync();
+
+ ItemCount cnt = 0;
+ if(PasteboardGetItemCount(paste, &cnt) || !cnt)
+ return QByteArray();
+
+#ifdef DEBUG_PASTEBOARD
+ qDebug("Pasteboard: retrieveData [%s]", qPrintable(format));
+#endif
+ const QList<QMacPasteboardMime *> mimes = QMacPasteboardMime::all(mime_type);
+ for(int mime = 0; mime < mimes.size(); ++mime) {
+ QMacPasteboardMime *c = mimes.at(mime);
+ QString c_flavor = c->flavorFor(format);
+ if(!c_flavor.isEmpty()) {
+ // Handle text/plain a little differently. Try handling Unicode first.
+ if((c_flavor == QLatin1String("com.apple.traditional-mac-plain-text") || c_flavor == QLatin1String("public.utf8-plain-text")) &&
+ hasFlavor(QLatin1String("public.utf16-plain-text")))
+ c_flavor = QLatin1String("public.utf16-plain-text");
+
+ QVariant ret;
+ QList<QByteArray> retList;
+ for(uint index = 1; index <= cnt; ++index) {
+ PasteboardItemID id;
+ if(PasteboardGetItemIdentifier(paste, index, &id) != noErr)
+ continue;
+
+ QCFType<CFArrayRef> types;
+ if(PasteboardCopyItemFlavors(paste, id, &types ) != noErr)
+ continue;
+
+ const int type_count = CFArrayGetCount(types);
+ for(int i = 0; i < type_count; ++i) {
+ CFStringRef flavor = static_cast<CFStringRef>(CFArrayGetValueAtIndex(types, i));
+ if(c_flavor == QCFString::toQString(flavor)) {
+ QCFType<CFDataRef> macBuffer;
+ if(PasteboardCopyItemFlavorData(paste, id, flavor, &macBuffer) == noErr) {
+ QByteArray buffer((const char *)CFDataGetBytePtr(macBuffer), CFDataGetLength(macBuffer));
+ if(!buffer.isEmpty()) {
+#ifdef DEBUG_PASTEBOARD
+ qDebug(" - %s [%s] (%s)", qPrintable(format), qPrintable(QCFString::toQString(flavor)), qPrintable(c->convertorName()));
+#endif
+ buffer.detach(); //detach since we release the macBuffer
+ retList.append(buffer);
+ break; //skip to next element
+ }
+ }
+ } else {
+#ifdef DEBUG_PASTEBOARD
+ qDebug(" - NoMatch %s [%s] (%s)", qPrintable(c_flavor), qPrintable(QCFString::toQString(flavor)), qPrintable(c->convertorName()));
+#endif
+ }
+ }
+ }
+
+ if (!retList.isEmpty()) {
+ ret = c->convertToMime(format, retList, c_flavor);
+ return ret;
+ }
+ }
+ }
+ return QVariant();
+}
+
+void QMacPasteboard::clear_helper()
+{
+ if (paste)
+ PasteboardClear(paste);
+ promises.clear();
+}
+
+void
+QMacPasteboard::clear()
+{
+#ifdef DEBUG_PASTEBOARD
+ qDebug("PasteBoard: clear!");
+#endif
+ clear_helper();
+}
+
+bool
+QMacPasteboard::sync() const
+{
+ if (!paste)
+ return false;
+ const bool fromGlobal = PasteboardSynchronize(paste) & kPasteboardModified;
+
+ if (fromGlobal)
+ const_cast<QMacPasteboard *>(this)->setMimeData(0);
+
+#ifdef DEBUG_PASTEBOARD
+ if(fromGlobal)
+ qDebug("Pasteboard: Syncronize!");
+#endif
+ return fromGlobal;
+}
+
+
+
+
+QT_END_NAMESPACE
diff --git a/src/gui/kernel/qclipboard_p.h b/src/gui/kernel/qclipboard_p.h
new file mode 100644
index 0000000000..ef6dc3f405
--- /dev/null
+++ b/src/gui/kernel/qclipboard_p.h
@@ -0,0 +1,131 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QCLIPBOARD_P_H
+#define QCLIPBOARD_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/qobject_p.h"
+#include "QtGui/qmime.h"
+#include "QtGui/qclipboard.h"
+
+QT_BEGIN_NAMESPACE
+
+class QClipboardPrivate;
+
+class QMimeDataWrapper : public QMimeSource
+{
+public:
+ QMimeDataWrapper() {}
+
+ const char* format(int n) const;
+ QByteArray encodedData(const char*) const;
+
+ mutable QList<QByteArray> formats;
+ const QMimeData *data;
+};
+
+class QMimeSourceWrapper : public QMimeData
+{
+public:
+ QMimeSourceWrapper(QClipboardPrivate *priv, QClipboard::Mode m);
+ ~QMimeSourceWrapper();
+
+ bool hasFormat(const QString &mimetype) const;
+ QStringList formats() const;
+
+protected:
+ QVariant retrieveData(const QString &mimetype, QVariant::Type) const;
+private:
+ QClipboardPrivate *d;
+ QClipboard::Mode mode;
+ QMimeSource *source;
+};
+
+
+class QClipboardPrivate : public QObjectPrivate
+{
+public:
+ QClipboardPrivate() : QObjectPrivate() {
+ for (int i = 0; i <= QClipboard::LastMode; ++i) {
+ compat_data[i] = 0;
+ wrapper[i] = new QMimeDataWrapper();
+ }
+ }
+ ~QClipboardPrivate() {
+ for (int i = 0; i <= QClipboard::LastMode; ++i) {
+ delete wrapper[i];
+ delete compat_data[i];
+ }
+ }
+
+ mutable QMimeDataWrapper *wrapper[QClipboard::LastMode + 1];
+ mutable QMimeSource *compat_data[QClipboard::LastMode + 1];
+};
+
+inline QMimeSourceWrapper::QMimeSourceWrapper(QClipboardPrivate *priv, QClipboard::Mode m)
+ : QMimeData()
+{
+ d = priv;
+ mode = m;
+ source = d->compat_data[mode];
+}
+
+inline QMimeSourceWrapper::~QMimeSourceWrapper()
+{
+ if (d->compat_data[mode] == source)
+ d->compat_data[mode] = 0;
+ delete source;
+}
+
+QT_END_NAMESPACE
+
+#endif // QCLIPBOARD_P_H
diff --git a/src/gui/kernel/qclipboard_qws.cpp b/src/gui/kernel/qclipboard_qws.cpp
new file mode 100644
index 0000000000..1a03149e7d
--- /dev/null
+++ b/src/gui/kernel/qclipboard_qws.cpp
@@ -0,0 +1,304 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qclipboard.h"
+
+#ifndef QT_NO_CLIPBOARD
+
+#include "qapplication.h"
+#include "qbitmap.h"
+#include "qdatetime.h"
+#include "qbuffer.h"
+#include "qwidget.h"
+#include "qevent.h"
+
+#include <qwsdisplay_qws.h>
+#include <qwsproperty_qws.h>
+#include <qwsevent_qws.h>
+
+QT_BEGIN_NAMESPACE
+
+QT_USE_NAMESPACE
+
+
+/*****************************************************************************
+ Internal QClipboard functions for Qt for Embedded Linux
+ *****************************************************************************/
+
+static const int TextClipboard=424242;
+static bool init = false;
+
+static inline void qwsInitClipboard()
+{
+ //### this should go into QWSServer; it only needs to happen once.
+ if( !init ) {
+ QPaintDevice::qwsDisplay()->addProperty(0, TextClipboard);
+ init = true;
+ }
+}
+
+static QString qwsClipboardText()
+{
+ char * data;
+ int len;
+ qwsInitClipboard();
+ if( !QPaintDevice::qwsDisplay()->getProperty(0, TextClipboard, data, len) ) {
+// qDebug("Property received: %d bytes", len);
+ }
+
+ QString s((const QChar*)data, len/sizeof(QChar));
+ // qDebug("Property received: '%s'", s.toAscii().constData());
+ delete[] data;
+ return s;
+}
+
+
+static void qwsSetClipboardText(const QString& s)
+{
+ qwsInitClipboard();
+ // qDebug("qwsSetClipboardText( %s )", s.toAscii().data());
+ int len = s.length()*sizeof(QChar);
+ QByteArray ba((const char*)s.unicode(), len);
+ QPaintDevice::qwsDisplay()->
+ setProperty(0, TextClipboard, QWSPropertyManager::PropReplace, ba);
+
+}
+
+class QClipboardData
+{
+public:
+ QClipboardData();
+ ~QClipboardData();
+
+ void setSource(QMimeData* s)
+ {
+ if (s == src)
+ return;
+ delete src;
+ src = s;
+ }
+ QMimeData* source()
+ { return src; }
+#if 0
+ void addTransferredPixmap(QPixmap pm)
+ { /* TODO: queue them */
+ transferred[tindex] = pm;
+ tindex=(tindex+1)%2;
+ }
+ void clearTransfers()
+ {
+ transferred[0] = QPixmap();
+ transferred[1] = QPixmap();
+ }
+#endif
+
+ void clear();
+
+private:
+ QMimeData* src;
+
+#if 0
+ QPixmap transferred[2];
+ int tindex;
+#endif
+};
+
+QClipboardData::QClipboardData()
+{
+ src = 0;
+#if 0
+ tindex=0;
+#endif
+}
+
+QClipboardData::~QClipboardData()
+{
+ delete src;
+}
+
+void QClipboardData::clear()
+{
+ delete src;
+ src = 0;
+}
+
+
+static QClipboardData *internalCbData = 0;
+
+static void cleanupClipboardData()
+{
+ delete internalCbData;
+ internalCbData = 0;
+}
+
+static QClipboardData *clipboardData()
+{
+ if (internalCbData == 0) {
+ internalCbData = new QClipboardData;
+ qAddPostRoutine(cleanupClipboardData);
+ }
+ return internalCbData;
+}
+
+
+/*****************************************************************************
+ QClipboard member functions for FB.
+ *****************************************************************************/
+
+#if 0
+
+QString QClipboard::text() const
+{
+ return qwsClipboardText();
+}
+
+void QClipboard::setText(const QString &text)
+{
+ qwsSetClipboardText(text);
+}
+
+QString QClipboard::text(QString& subtype) const
+{
+ QString r;
+ if (subtype == "plain")
+ r = text();
+ return r;
+}
+
+#endif
+
+void QClipboard::clear(Mode mode)
+{
+ setText(QString(), mode);
+}
+
+
+bool QClipboard::event(QEvent *e)
+{
+ static bool recursionWatch = false;
+ if (e->type() != QEvent::Clipboard || recursionWatch)
+ return QObject::event(e);
+
+ recursionWatch = true;
+ QWSPropertyNotifyEvent *event = (QWSPropertyNotifyEvent *)(((QClipboardEvent *)e)->data());
+ if (event && event->simpleData.state == QWSPropertyNotifyEvent::PropertyNewValue) {
+ QClipboardData *d = clipboardData();
+ QString t = qwsClipboardText();
+ if( (d->source() == 0 && !t.isEmpty()) || (d->source() != 0 && d->source()->text() != t) ) {
+ if( !d->source() )
+ d->setSource(new QMimeData);
+ d->source()->setText( t );
+ emitChanged(QClipboard::Clipboard);
+ }
+ }
+
+ recursionWatch = false;
+ return true;
+}
+
+const QMimeData* QClipboard::mimeData(Mode mode) const
+{
+ if (mode != Clipboard) return 0;
+
+ QClipboardData *d = clipboardData();
+ // Try and get data from QWSProperty if no mime data has been set on us.
+ if( !d->source() ) {
+ QString t = qwsClipboardText();
+ if( !t.isEmpty() ) {
+ QMimeData* nd = new QMimeData;
+ nd->setText( t );
+ d->setSource( nd );
+ }
+ }
+ return d->source();
+}
+
+void QClipboard::setMimeData(QMimeData* src, Mode mode)
+{
+ if (mode != Clipboard) return;
+
+ QClipboardData *d = clipboardData();
+
+ /* Propagate text data to other QWSClients */
+
+ QString newText;
+ if( src != 0 )
+ newText = src->text();
+ QString oldText;
+ if( d->source() != 0 )
+ oldText = d->source()->text();
+
+ d->setSource(src);
+
+ if( oldText != newText ) {
+ if( d->source() == 0 ) {
+ qwsSetClipboardText( QString() );
+ } else {
+ qwsSetClipboardText( d->source()->text() );
+ }
+ }
+
+ emitChanged(QClipboard::Clipboard);
+}
+
+bool QClipboard::supportsMode(Mode mode) const
+{
+ return (mode == Clipboard);
+}
+
+bool QClipboard::ownsMode(Mode mode) const
+{
+ if (mode == Clipboard)
+ qWarning("QClipboard::ownsClipboard: UNIMPLEMENTED!");
+ return false;
+}
+
+void QClipboard::connectNotify( const char * )
+{
+}
+
+void QClipboard::ownerDestroyed()
+{
+}
+
+#endif // QT_NO_CLIPBOARD
+
+QT_END_NAMESPACE
diff --git a/src/gui/kernel/qclipboard_win.cpp b/src/gui/kernel/qclipboard_win.cpp
new file mode 100644
index 0000000000..90d436251c
--- /dev/null
+++ b/src/gui/kernel/qclipboard_win.cpp
@@ -0,0 +1,389 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qclipboard.h"
+
+#ifndef QT_NO_CLIPBOARD
+
+#include "qapplication.h"
+#include "qapplication_p.h"
+#include "qeventloop.h"
+#include "qwidget.h"
+#include "qevent.h"
+#include "qmime.h"
+#include "qt_windows.h"
+#include "qdnd_p.h"
+
+QT_BEGIN_NAMESPACE
+
+#if defined(Q_OS_WINCE)
+QT_BEGIN_INCLUDE_NAMESPACE
+#include "qguifunctions_wince.h"
+QT_END_INCLUDE_NAMESPACE
+
+HRESULT QtCeGetClipboard(IDataObject** obj);
+HRESULT QtCeSetClipboard(IDataObject* obj);
+void QtCeFlushClipboard();
+
+#define OleGetClipboard QtCeGetClipboard
+#define OleSetClipboard QtCeSetClipboard
+#define OleFlushClipboard QtCeFlushClipboard
+
+#endif
+
+
+class QClipboardWatcher : public QInternalMimeData {
+public:
+ QClipboardWatcher()
+ : QInternalMimeData()
+ {
+ }
+
+ bool hasFormat_sys(const QString &mimetype) const;
+ QStringList formats_sys() const;
+ QVariant retrieveData_sys(const QString &mimetype, QVariant::Type preferredType) const;
+};
+
+
+bool QClipboardWatcher::hasFormat_sys(const QString &mime) const
+{
+ IDataObject * pDataObj = 0;
+
+ if (OleGetClipboard(&pDataObj) != S_OK && !pDataObj) // Sanity
+ return false;
+
+ bool has = QWindowsMime::converterToMime(mime, pDataObj) != 0;
+
+ pDataObj->Release();
+
+ return has;
+}
+
+QStringList QClipboardWatcher::formats_sys() const
+{
+ QStringList fmts;
+ IDataObject * pDataObj = 0;
+
+ if (OleGetClipboard(&pDataObj) != S_OK && !pDataObj) // Sanity
+ return QStringList();
+
+ fmts = QWindowsMime::allMimesForFormats(pDataObj);
+
+ pDataObj->Release();
+
+ return fmts;
+}
+
+QVariant QClipboardWatcher::retrieveData_sys(const QString &mimeType, QVariant::Type type) const
+{
+ QVariant result;
+ IDataObject * pDataObj = 0;
+
+ if (OleGetClipboard(&pDataObj) != S_OK && !pDataObj) // Sanity
+ return result;
+
+ QWindowsMime *converter = QWindowsMime::converterToMime(mimeType, pDataObj);
+
+ if (converter)
+ result = converter->convertToMime(mimeType, pDataObj, type);
+
+ pDataObj->Release();
+
+ return result;
+}
+
+class QClipboardData
+{
+public:
+ QClipboardData()
+ : iData(0)
+ , nextClipboardViewer(0)
+ {
+ clipBoardViewer = new QWidget();
+ clipBoardViewer->createWinId();
+ clipBoardViewer->setObjectName(QLatin1String("internal clipboard owner"));
+ }
+
+ ~QClipboardData()
+ {
+ Q_ASSERT(clipBoardViewer->testAttribute(Qt::WA_WState_Created));
+ ChangeClipboardChain(clipBoardViewer->internalWinId(), nextClipboardViewer);
+ delete clipBoardViewer;
+ releaseIData();
+ }
+
+ void releaseIData()
+ {
+ if (iData) {
+ delete iData->mimeData();
+ iData->releaseQt();
+ iData->Release();
+ iData = 0;
+ }
+ }
+
+ QOleDataObject * iData;
+ QWidget *clipBoardViewer;
+ HWND nextClipboardViewer;
+ QClipboardWatcher watcher;
+};
+
+static QClipboardData *ptrClipboardData = 0;
+
+static QClipboardData *clipboardData()
+{
+ if (ptrClipboardData == 0) {
+ ptrClipboardData = new QClipboardData;
+ // this needs to be done here to avoid recursion
+ Q_ASSERT(ptrClipboardData->clipBoardViewer->testAttribute(Qt::WA_WState_Created));
+ ptrClipboardData->nextClipboardViewer = SetClipboardViewer(ptrClipboardData->clipBoardViewer->internalWinId());
+ }
+ return ptrClipboardData;
+}
+
+static void cleanupClipboardData()
+{
+ delete ptrClipboardData;
+ ptrClipboardData = 0;
+}
+
+#if defined(Q_OS_WINCE)
+HRESULT QtCeGetClipboard(IDataObject** obj)
+{
+ HWND owner = ptrClipboardData->clipBoardViewer->internalWinId();
+ if (!OpenClipboard(owner))
+ return !S_OK;
+
+ if (!IsClipboardFormatAvailable(CF_TEXT) && !IsClipboardFormatAvailable(CF_UNICODETEXT))
+ return !S_OK;
+
+ HANDLE clipData = GetClipboardData(CF_TEXT);
+ QString clipText;
+ if (clipData == 0) {
+ clipData = GetClipboardData(CF_UNICODETEXT);
+ if (clipData != 0)
+ clipText = QString::fromUtf16((unsigned short *)clipData);
+ } else {
+ clipText = QString::fromLatin1((const char*)clipData);
+ }
+
+ QMimeData *mimeData = new QMimeData();
+ mimeData->setText(clipText);
+ QOleDataObject* data = new QOleDataObject(mimeData);
+ *obj = data;
+ CloseClipboard();
+ return S_OK;
+}
+
+HRESULT QtCeSetClipboard(IDataObject* obj)
+{
+ HWND owner = ptrClipboardData->clipBoardViewer->internalWinId();
+ if (!OpenClipboard(owner))
+ return !S_OK;
+
+ bool result = false;
+ if (obj == 0) {
+ result = true;
+ EmptyClipboard();
+ CloseClipboard();
+ } else {
+ QOleDataObject* qobj = static_cast<QOleDataObject*>(obj);
+
+ const QMimeData* data = qobj->mimeData();
+ if (data->hasText()) {
+ EmptyClipboard();
+ result = SetClipboardData(CF_UNICODETEXT, wcsdup(reinterpret_cast<const wchar_t *> (data->text().utf16()))) != NULL;
+ CloseClipboard();
+ result = true;
+ }
+ }
+ return result ? S_OK : !S_OK;
+}
+
+void QtCeFlushClipboard() { }
+#endif
+
+
+
+QClipboard::~QClipboard()
+{
+ cleanupClipboardData();
+}
+
+void QClipboard::setMimeData(QMimeData *src, Mode mode)
+{
+ if (mode != Clipboard)
+ return;
+ QClipboardData *d = clipboardData();
+
+ if (!(d->iData && d->iData->mimeData() == src)) {
+ d->releaseIData();
+ d->iData = new QOleDataObject(src);
+ }
+
+ if (OleSetClipboard(d->iData) != S_OK) {
+ d->releaseIData();
+ qErrnoWarning("QClipboard::setMimeData: Failed to set data on clipboard");
+ return;
+ }
+#if defined(Q_OS_WINCE)
+ // As WinCE does not support notifications we send the signal here
+ // We will get no event when the clipboard changes outside...
+ emit dataChanged();
+ emit changed(Clipboard);
+#endif
+}
+
+void QClipboard::clear(Mode mode)
+{
+ if (mode != Clipboard) return;
+
+ QClipboardData *d = clipboardData();
+
+ d->releaseIData();
+
+ if (OleSetClipboard(0) != S_OK) {
+ qErrnoWarning("QClipboard::clear: Failed to clear data on clipboard");
+ return;
+ }
+#if defined(Q_OS_WINCE)
+ // As WinCE does not support notifications we send the signal here
+ // We will get no event when the clipboard changes outside...
+ emit dataChanged();
+ emit changed(Clipboard);
+#endif
+}
+
+bool QClipboard::event(QEvent *e)
+{
+ if (e->type() != QEvent::Clipboard)
+ return QObject::event(e);
+
+ QClipboardData *d = clipboardData();
+
+ MSG *m = (MSG *)((QClipboardEvent*)e)->data();
+ if (!m) {
+ // this is sent to render all formats at app shut down
+ if (ownsClipboard()) {
+ OleFlushClipboard();
+ d->releaseIData();
+ }
+ return true;
+ }
+
+ bool propagate = false;
+
+ if (m->message == WM_CHANGECBCHAIN) {
+ if ((HWND)m->wParam == d->nextClipboardViewer)
+ d->nextClipboardViewer = (HWND)m->lParam;
+ else
+ propagate = true;
+ } else if (m->message == WM_DRAWCLIPBOARD) {
+ emitChanged(QClipboard::Clipboard);
+ if (!ownsClipboard() && d->iData)
+ // clean up the clipboard object if we no longer own the clipboard
+ d->releaseIData();
+ propagate = true;
+ }
+
+ if (propagate && d->nextClipboardViewer) {
+ QT_WA({
+ SendMessage(d->nextClipboardViewer, m->message,
+ m->wParam, m->lParam);
+ } , {
+ SendMessageA(d->nextClipboardViewer, m->message,
+ m->wParam, m->lParam);
+ });
+ }
+
+ return true;
+}
+
+void QClipboard::connectNotify(const char *signal)
+{
+ if (qstrcmp(signal,SIGNAL(dataChanged())) == 0) {
+ // ensure we are up and running but block signals so the dataChange signal
+ // is not emitted while being connected to.
+ bool blocked = blockSignals(true);
+ QClipboardData *d = clipboardData();
+ blockSignals(blocked);
+ Q_UNUSED(d);
+ }
+}
+
+const QMimeData *QClipboard::mimeData(Mode mode) const
+{
+ if (mode != Clipboard)
+ return 0;
+
+ QClipboardData *data = clipboardData();
+ // sort cut for local copy / paste
+ if (ownsClipboard() && data->iData->mimeData())
+ return data->iData->mimeData();
+ return &data->watcher;
+}
+
+bool QClipboard::supportsMode(Mode mode) const
+{
+ return (mode == Clipboard);
+}
+
+bool QClipboard::ownsMode(Mode mode) const
+{
+ if (mode == Clipboard) {
+ QClipboardData *d = clipboardData();
+#if !defined(Q_OS_WINCE)
+ return d->iData && OleIsCurrentClipboard(d->iData) == S_OK;
+#else
+ return d->iData && GetClipboardOwner() == d->clipBoardViewer->internalWinId();
+#endif
+ } else {
+ return false;
+ }
+}
+
+void QClipboard::ownerDestroyed()
+{
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_CLIPBOARD
diff --git a/src/gui/kernel/qclipboard_x11.cpp b/src/gui/kernel/qclipboard_x11.cpp
new file mode 100644
index 0000000000..089cc43937
--- /dev/null
+++ b/src/gui/kernel/qclipboard_x11.cpp
@@ -0,0 +1,1498 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+// #define QCLIPBOARD_DEBUG
+// #define QCLIPBOARD_DEBUG_VERBOSE
+
+#ifdef QCLIPBOARD_DEBUG
+# define DEBUG qDebug
+#else
+# define DEBUG if (false) qDebug
+#endif
+
+#ifdef QCLIPBOARD_DEBUG_VERBOSE
+# define VDEBUG qDebug
+#else
+# define VDEBUG if (false) qDebug
+#endif
+
+#include "qplatformdefs.h"
+
+#include "qclipboard.h"
+#include "qclipboard_p.h"
+
+#ifndef QT_NO_CLIPBOARD
+
+#include "qabstracteventdispatcher.h"
+#include "qapplication.h"
+#include "qdesktopwidget.h"
+#include "qbitmap.h"
+#include "qdatetime.h"
+#include "qiodevice.h"
+#include "qbuffer.h"
+#include "qtextcodec.h"
+#include "qlist.h"
+#include "qmap.h"
+#include "qapplication_p.h"
+#include "qevent.h"
+#include "qt_x11_p.h"
+#include "qx11info_x11.h"
+#include "qimagewriter.h"
+#include "qvariant.h"
+#include "qdnd_p.h"
+
+#ifndef QT_NO_XFIXES
+#include <X11/extensions/Xfixes.h>
+#endif // QT_NO_XFIXES
+
+QT_BEGIN_NAMESPACE
+
+/*****************************************************************************
+ Internal QClipboard functions for X11.
+ *****************************************************************************/
+
+static int clipboard_timeout = 5000; // 5s timeout on clipboard operations
+
+static QWidget * owner = 0;
+static QWidget *requestor = 0;
+static bool timer_event_clear = false;
+static int timer_id = 0;
+
+static int pending_timer_id = 0;
+static bool pending_clipboard_changed = false;
+static bool pending_selection_changed = false;
+
+
+// event capture mechanism for qt_xclb_wait_for_event
+static bool waiting_for_data = false;
+static bool has_captured_event = false;
+static Window capture_event_win = XNone;
+static int capture_event_type = -1;
+static XEvent captured_event;
+
+class QClipboardWatcher; // forward decl
+static QClipboardWatcher *selection_watcher = 0;
+static QClipboardWatcher *clipboard_watcher = 0;
+
+static void cleanup()
+{
+ delete owner;
+ delete requestor;
+ owner = 0;
+ requestor = 0;
+}
+
+static
+void setupOwner()
+{
+ if (owner)
+ return;
+ owner = new QWidget(0);
+ owner->setObjectName(QLatin1String("internal clipboard owner"));
+ owner->createWinId();
+ requestor = new QWidget(0);
+ requestor->createWinId();
+ requestor->setObjectName(QLatin1String("internal clipboard requestor"));
+ qAddPostRoutine(cleanup);
+}
+
+
+class QClipboardWatcher : public QInternalMimeData {
+public:
+ QClipboardWatcher(QClipboard::Mode mode);
+ ~QClipboardWatcher();
+ bool empty() const;
+ virtual bool hasFormat_sys(const QString &mimetype) const;
+ virtual QStringList formats_sys() const;
+
+ QVariant retrieveData_sys(const QString &mimetype, QVariant::Type type) const;
+ QByteArray getDataInFormat(Atom fmtatom) const;
+
+ Atom atom;
+ mutable QStringList formatList;
+ mutable QByteArray format_atoms;
+};
+
+class QClipboardData
+{
+private:
+ QMimeData *&mimeDataRef() const
+ {
+ if(mode == QClipboard::Selection)
+ return selectionData;
+ return clipboardData;
+ }
+
+public:
+ QClipboardData(QClipboard::Mode mode);
+ ~QClipboardData();
+
+ void setSource(QMimeData* s)
+ {
+ if ((mode == QClipboard::Selection && selectionData == s)
+ || clipboardData == s) {
+ return;
+ }
+
+ if (selectionData != clipboardData) {
+ delete mimeDataRef();
+ }
+
+ mimeDataRef() = s;
+ }
+
+ QMimeData *source() const
+ {
+ return mimeDataRef();
+ }
+
+ void clear()
+ {
+ timestamp = CurrentTime;
+ if (selectionData == clipboardData) {
+ mimeDataRef() = 0;
+ } else {
+ QMimeData *&src = mimeDataRef();
+ delete src;
+ src = 0;
+ }
+ }
+
+ static QMimeData *selectionData;
+ static QMimeData *clipboardData;
+ Time timestamp;
+ QClipboard::Mode mode;
+};
+
+QMimeData *QClipboardData::selectionData = 0;
+QMimeData *QClipboardData::clipboardData = 0;
+
+QClipboardData::QClipboardData(QClipboard::Mode clipboardMode)
+{
+ timestamp = CurrentTime;
+ mode = clipboardMode;
+}
+
+QClipboardData::~QClipboardData()
+{ clear(); }
+
+
+static QClipboardData *internalCbData = 0;
+static QClipboardData *internalSelData = 0;
+
+static void cleanupClipboardData()
+{
+ delete internalCbData;
+ internalCbData = 0;
+}
+
+static QClipboardData *clipboardData()
+{
+ if (internalCbData == 0) {
+ internalCbData = new QClipboardData(QClipboard::Clipboard);
+ qAddPostRoutine(cleanupClipboardData);
+ }
+ return internalCbData;
+}
+
+static void cleanupSelectionData()
+{
+ delete internalSelData;
+ internalSelData = 0;
+}
+
+static QClipboardData *selectionData()
+{
+ if (internalSelData == 0) {
+ internalSelData = new QClipboardData(QClipboard::Selection);
+ qAddPostRoutine(cleanupSelectionData);
+ }
+ return internalSelData;
+}
+
+class QClipboardINCRTransaction
+{
+public:
+ QClipboardINCRTransaction(Window w, Atom p, Atom t, int f, QByteArray d, unsigned int i);
+ ~QClipboardINCRTransaction(void);
+
+ int x11Event(XEvent *event);
+
+ Window window;
+ Atom property, target;
+ int format;
+ QByteArray data;
+ unsigned int increment;
+ unsigned int offset;
+};
+
+typedef QMap<Window,QClipboardINCRTransaction*> TransactionMap;
+static TransactionMap *transactions = 0;
+static QApplication::EventFilter prev_event_filter = 0;
+static int incr_timer_id = 0;
+
+static bool qt_x11_incr_event_filter(void *message, long *result)
+{
+ XEvent *event = reinterpret_cast<XEvent *>(message);
+ TransactionMap::Iterator it = transactions->find(event->xany.window);
+ if (it != transactions->end()) {
+ if ((*it)->x11Event(event) != 0)
+ return true;
+ }
+ if (prev_event_filter)
+ return prev_event_filter(event, result);
+ return false;
+}
+
+/*
+ called when no INCR activity has happened for 'clipboard_timeout'
+ milliseconds... we assume that all unfinished transactions have
+ timed out and remove everything from the transaction map
+*/
+static void qt_xclb_incr_timeout(void)
+{
+ qWarning("QClipboard: Timed out while sending data");
+
+ while (transactions)
+ delete *transactions->begin();
+}
+
+QClipboardINCRTransaction::QClipboardINCRTransaction(Window w, Atom p, Atom t, int f,
+ QByteArray d, unsigned int i)
+ : window(w), property(p), target(t), format(f), data(d), increment(i), offset(0u)
+{
+ DEBUG("QClipboard: sending %d bytes (INCR transaction %p)", d.size(), this);
+
+ XSelectInput(X11->display, window, PropertyChangeMask);
+
+ if (! transactions) {
+ VDEBUG("QClipboard: created INCR transaction map");
+ transactions = new TransactionMap;
+ prev_event_filter = qApp->setEventFilter(qt_x11_incr_event_filter);
+ incr_timer_id = QApplication::clipboard()->startTimer(clipboard_timeout);
+ }
+ transactions->insert(window, this);
+}
+
+QClipboardINCRTransaction::~QClipboardINCRTransaction(void)
+{
+ VDEBUG("QClipboard: destroyed INCR transacton %p", this);
+
+ XSelectInput(X11->display, window, NoEventMask);
+
+ transactions->remove(window);
+ if (transactions->isEmpty()) {
+ VDEBUG("QClipboard: no more INCR transactions");
+ delete transactions;
+ transactions = 0;
+
+ (void)qApp->setEventFilter(prev_event_filter);
+
+ if (incr_timer_id != 0) {
+ QApplication::clipboard()->killTimer(incr_timer_id);
+ incr_timer_id = 0;
+ }
+ }
+}
+
+int QClipboardINCRTransaction::x11Event(XEvent *event)
+{
+ if (event->type != PropertyNotify
+ || (event->xproperty.state != PropertyDelete
+ || event->xproperty.atom != property))
+ return 0;
+
+ // restart the INCR timer
+ if (incr_timer_id) QApplication::clipboard()->killTimer(incr_timer_id);
+ incr_timer_id = QApplication::clipboard()->startTimer(clipboard_timeout);
+
+ unsigned int bytes_left = data.size() - offset;
+ if (bytes_left > 0) {
+ unsigned int xfer = qMin(increment, bytes_left);
+ VDEBUG("QClipboard: sending %d bytes, %d remaining (INCR transaction %p)",
+ xfer, bytes_left - xfer, this);
+
+ XChangeProperty(X11->display, window, property, target, format,
+ PropModeReplace, (uchar *) data.data() + offset, xfer);
+ offset += xfer;
+ } else {
+ // INCR transaction finished...
+ XChangeProperty(X11->display, window, property, target, format,
+ PropModeReplace, (uchar *) data.data(), 0);
+ delete this;
+ }
+
+ return 1;
+}
+
+
+/*****************************************************************************
+ QClipboard member functions for X11.
+ *****************************************************************************/
+
+struct qt_init_timestamp_data
+{
+ Time timestamp;
+};
+
+#if defined(Q_C_CALLBACKS)
+extern "C" {
+#endif
+
+static Bool qt_init_timestamp_scanner(Display*, XEvent *event, XPointer arg)
+{
+ qt_init_timestamp_data *data =
+ reinterpret_cast<qt_init_timestamp_data*>(arg);
+ switch(event->type)
+ {
+ case ButtonPress:
+ case ButtonRelease:
+ data->timestamp = event->xbutton.time;
+ break;
+ case MotionNotify:
+ data->timestamp = event->xmotion.time;
+ break;
+ case XKeyPress:
+ case XKeyRelease:
+ data->timestamp = event->xkey.time;
+ break;
+ case PropertyNotify:
+ data->timestamp = event->xproperty.time;
+ break;
+ case EnterNotify:
+ case LeaveNotify:
+ data->timestamp = event->xcrossing.time;
+ break;
+ case SelectionClear:
+ data->timestamp = event->xselectionclear.time;
+ break;
+ default:
+ break;
+ }
+#ifndef QT_NO_XFIXES
+ if (X11->use_xfixes && event->type == (X11->xfixes_eventbase + XFixesSelectionNotify)) {
+ XFixesSelectionNotifyEvent *req =
+ reinterpret_cast<XFixesSelectionNotifyEvent *>(event);
+ data->timestamp = req->selection_timestamp;
+ }
+#endif
+ return false;
+}
+
+#if defined(Q_C_CALLBACKS)
+}
+#endif
+
+QClipboard::QClipboard(QObject *parent)
+ : QObject(*new QClipboardPrivate, parent)
+{
+ // create desktop widget since we need it to get PropertyNotify or
+ // XFixesSelectionNotify events when someone changes the
+ // clipboard.
+ (void)QApplication::desktop();
+ if (X11->time == CurrentTime) {
+ // send a dummy event to myself to get the timestamp from X11.
+ qt_init_timestamp_data data;
+ data.timestamp = CurrentTime;
+ XEvent ev;
+ XCheckIfEvent(X11->display, &ev, &qt_init_timestamp_scanner, (XPointer)&data);
+ if (data.timestamp == CurrentTime) {
+ setupOwner();
+ int dummy = 0;
+ Window ownerId = owner->internalWinId();
+ XChangeProperty(X11->display, ownerId,
+ ATOM(CLIP_TEMPORARY), XA_INTEGER, 32,
+ PropModeReplace, (uchar*)&dummy, 1);
+ XWindowEvent(X11->display, ownerId, PropertyChangeMask, &ev);
+ data.timestamp = ev.xproperty.time;
+ XDeleteProperty(X11->display, ownerId, ATOM(CLIP_TEMPORARY));
+ }
+ X11->time = data.timestamp;
+ }
+}
+
+void QClipboard::clear(Mode mode)
+{
+ setMimeData(0, mode);
+}
+
+
+bool QClipboard::supportsMode(Mode mode) const
+{
+ return (mode == Clipboard || mode == Selection);
+}
+
+bool QClipboard::ownsMode(Mode mode) const
+{
+ if (mode == Clipboard)
+ return clipboardData()->timestamp != CurrentTime;
+ else if(mode == Selection)
+ return selectionData()->timestamp != CurrentTime;
+ else
+ return false;
+}
+
+
+// event filter function... captures interesting events while
+// qt_xclb_wait_for_event is running the event loop
+static bool qt_x11_clipboard_event_filter(void *message, long *)
+{
+ XEvent *event = reinterpret_cast<XEvent *>(message);
+ if (event->xany.type == capture_event_type &&
+ event->xany.window == capture_event_win) {
+ VDEBUG("QClipboard: event_filter(): caught event type %d", event->type);
+ has_captured_event = true;
+ captured_event = *event;
+ return true;
+ }
+ return false;
+}
+
+static Bool checkForClipboardEvents(Display *, XEvent *e, XPointer)
+{
+ return ((e->type == SelectionRequest && (e->xselectionrequest.selection == XA_PRIMARY
+ || e->xselectionrequest.selection == ATOM(CLIPBOARD)))
+ || (e->type == SelectionClear && (e->xselectionclear.selection == XA_PRIMARY
+ || e->xselectionclear.selection == ATOM(CLIPBOARD))));
+}
+
+bool QX11Data::clipboardWaitForEvent(Window win, int type, XEvent *event, int timeout)
+{
+ QTime started = QTime::currentTime();
+ QTime now = started;
+
+ if (QAbstractEventDispatcher::instance()->inherits("QtMotif")
+ || QApplication::clipboard()->property("useEventLoopWhenWaiting").toBool()) {
+ if (waiting_for_data) {
+ Q_ASSERT(!"QClipboard: internal error, qt_xclb_wait_for_event recursed");
+ return false;
+ }
+ waiting_for_data = true;
+
+
+ has_captured_event = false;
+ capture_event_win = win;
+ capture_event_type = type;
+
+ QApplication::EventFilter old_event_filter =
+ qApp->setEventFilter(qt_x11_clipboard_event_filter);
+
+ do {
+ if (XCheckTypedWindowEvent(display, win, type, event)) {
+ waiting_for_data = false;
+ qApp->setEventFilter(old_event_filter);
+ return true;
+ }
+
+ XSync(X11->display, false);
+ usleep(50000);
+
+ now = QTime::currentTime();
+ if (started > now) // crossed midnight
+ started = now;
+
+ QEventLoop::ProcessEventsFlags flags(QEventLoop::ExcludeUserInputEvents
+ | QEventLoop::ExcludeSocketNotifiers
+ | QEventLoop::WaitForMoreEvents
+ | QEventLoop::X11ExcludeTimers);
+ QAbstractEventDispatcher *eventDispatcher = QAbstractEventDispatcher::instance();
+ eventDispatcher->processEvents(flags);
+
+ if (has_captured_event) {
+ waiting_for_data = false;
+ *event = captured_event;
+ qApp->setEventFilter(old_event_filter);
+ return true;
+ }
+ } while (started.msecsTo(now) < timeout);
+
+ waiting_for_data = false;
+ qApp->setEventFilter(old_event_filter);
+ } else {
+ do {
+ if (XCheckTypedWindowEvent(X11->display,win,type,event))
+ return true;
+
+ // process other clipboard events, since someone is probably requesting data from us
+ XEvent e;
+ if (XCheckIfEvent(X11->display, &e, checkForClipboardEvents, 0))
+ qApp->x11ProcessEvent(&e);
+
+ now = QTime::currentTime();
+ if ( started > now ) // crossed midnight
+ started = now;
+
+ XFlush(X11->display);
+
+ // sleep 50 ms, so we don't use up CPU cycles all the time.
+ struct timeval usleep_tv;
+ usleep_tv.tv_sec = 0;
+ usleep_tv.tv_usec = 50000;
+ select(0, 0, 0, 0, &usleep_tv);
+ } while (started.msecsTo(now) < timeout);
+ }
+ return false;
+}
+
+
+static inline int maxSelectionIncr(Display *dpy)
+{ return XMaxRequestSize(dpy) > 65536 ? 65536*4 : XMaxRequestSize(dpy)*4 - 100; }
+
+bool QX11Data::clipboardReadProperty(Window win, Atom property, bool deleteProperty,
+ QByteArray *buffer, int *size, Atom *type, int *format, bool nullterm)
+{
+ int maxsize = maxSelectionIncr(display);
+ ulong bytes_left; // bytes_after
+ ulong length; // nitems
+ uchar *data;
+ Atom dummy_type;
+ int dummy_format;
+ int r;
+
+ if (!type) // allow null args
+ type = &dummy_type;
+ if (!format)
+ format = &dummy_format;
+
+ // Don't read anything, just get the size of the property data
+ r = XGetWindowProperty(display, win, property, 0, 0, False,
+ AnyPropertyType, type, format,
+ &length, &bytes_left, &data);
+ if (r != Success || (type && *type == XNone)) {
+ buffer->resize(0);
+ return false;
+ }
+ XFree((char*)data);
+
+ int offset = 0, buffer_offset = 0, format_inc = 1, proplen = bytes_left;
+
+ VDEBUG("QClipboard: read_property(): initial property length: %d", proplen);
+
+ switch (*format) {
+ case 8:
+ default:
+ format_inc = sizeof(char) / 1;
+ break;
+
+ case 16:
+ format_inc = sizeof(short) / 2;
+ proplen *= sizeof(short) / 2;
+ break;
+
+ case 32:
+ format_inc = sizeof(long) / 4;
+ proplen *= sizeof(long) / 4;
+ break;
+ }
+
+ int newSize = proplen + (nullterm ? 1 : 0);
+ buffer->resize(newSize);
+
+ bool ok = (buffer->size() == newSize);
+ VDEBUG("QClipboard: read_property(): buffer resized to %d", buffer->size());
+
+ if (ok) {
+ // could allocate buffer
+
+ while (bytes_left) {
+ // more to read...
+
+ r = XGetWindowProperty(display, win, property, offset, maxsize/4,
+ False, AnyPropertyType, type, format,
+ &length, &bytes_left, &data);
+ if (r != Success || (type && *type == XNone))
+ break;
+
+ offset += length / (32 / *format);
+ length *= format_inc * (*format) / 8;
+
+ // Here we check if we get a buffer overflow and tries to
+ // recover -- this shouldn't normally happen, but it doesn't
+ // hurt to be defensive
+ if ((int)(buffer_offset + length) > buffer->size()) {
+ length = buffer->size() - buffer_offset;
+
+ // escape loop
+ bytes_left = 0;
+ }
+
+ memcpy(buffer->data() + buffer_offset, data, length);
+ buffer_offset += length;
+
+ XFree((char*)data);
+ }
+
+ if (*format == 8 && *type == ATOM(COMPOUND_TEXT)) {
+ // convert COMPOUND_TEXT to a multibyte string
+ XTextProperty textprop;
+ textprop.encoding = *type;
+ textprop.format = *format;
+ textprop.nitems = length;
+ textprop.value = (unsigned char *) buffer->data();
+
+ char **list_ret = 0;
+ int count;
+ if (XmbTextPropertyToTextList(display, &textprop, &list_ret,
+ &count) == Success && count && list_ret) {
+ offset = strlen(list_ret[0]);
+ buffer->resize(offset + (nullterm ? 1 : 0));
+ memcpy(buffer->data(), list_ret[0], offset);
+ }
+ if (list_ret) XFreeStringList(list_ret);
+ }
+
+ // zero-terminate (for text)
+ if (nullterm)
+ buffer->data()[buffer_offset] = '\0';
+ }
+
+ // correct size, not 0-term.
+ if (size)
+ *size = buffer_offset;
+
+ VDEBUG("QClipboard: read_property(): buffer size %d, buffer offset %d, offset %d",
+ buffer->size(), buffer_offset, offset);
+
+ if (deleteProperty)
+ XDeleteProperty(display, win, property);
+
+ XFlush(display);
+
+ return ok;
+}
+
+QByteArray QX11Data::clipboardReadIncrementalProperty(Window win, Atom property, int nbytes, bool nullterm)
+{
+ XEvent event;
+
+ QByteArray buf;
+ QByteArray tmp_buf;
+ bool alloc_error = false;
+ int length;
+ int offset = 0;
+
+ if (nbytes > 0) {
+ // Reserve buffer + zero-terminator (for text data)
+ // We want to complete the INCR transfer even if we cannot
+ // allocate more memory
+ buf.resize(nbytes+1);
+ alloc_error = buf.size() != nbytes+1;
+ }
+
+ for (;;) {
+ XFlush(display);
+ if (!clipboardWaitForEvent(win,PropertyNotify,&event,clipboard_timeout))
+ break;
+ if (event.xproperty.atom != property ||
+ event.xproperty.state != PropertyNewValue)
+ continue;
+ if (X11->clipboardReadProperty(win, property, true, &tmp_buf, &length, 0, 0, false)) {
+ if (length == 0) { // no more data, we're done
+ if (nullterm) {
+ buf.resize(offset+1);
+ buf[offset] = '\0';
+ } else {
+ buf.resize(offset);
+ }
+ return buf;
+ } else if (!alloc_error) {
+ if (offset+length > (int)buf.size()) {
+ buf.resize(offset+length+65535);
+ if (buf.size() != offset+length+65535) {
+ alloc_error = true;
+ length = buf.size() - offset;
+ }
+ }
+ memcpy(buf.data()+offset, tmp_buf.constData(), length);
+ tmp_buf.resize(0);
+ offset += length;
+ }
+ } else {
+ break;
+ }
+ }
+
+ // timed out ... create a new requestor window, otherwise the requestor
+ // could consider next request to be still part of this timed out request
+ delete requestor;
+ requestor = new QWidget(0);
+ requestor->setObjectName(QLatin1String("internal clipboard requestor"));
+
+ return QByteArray();
+}
+
+static Atom send_targets_selection(QClipboardData *d, Window window, Atom property)
+{
+ QVector<Atom> types;
+ QStringList formats = QInternalMimeData::formatsHelper(d->source());
+ for (int i = 0; i < formats.size(); ++i) {
+ QList<Atom> atoms = X11->xdndMimeAtomsForFormat(formats.at(i));
+ for (int j = 0; j < atoms.size(); ++j) {
+ if (!types.contains(atoms.at(j)))
+ types.append(atoms.at(j));
+ }
+ }
+ types.append(ATOM(TARGETS));
+ types.append(ATOM(MULTIPLE));
+ types.append(ATOM(TIMESTAMP));
+
+ XChangeProperty(X11->display, window, property, XA_ATOM, 32,
+ PropModeReplace, (uchar *) types.data(), types.size());
+ return property;
+}
+
+static Atom send_selection(QClipboardData *d, Atom target, Window window, Atom property)
+{
+ Atom atomFormat = target;
+ int dataFormat = 0;
+ QByteArray data;
+
+ QByteArray fmt = X11->xdndAtomToString(target);
+ if (fmt.isEmpty() || !QInternalMimeData::hasFormatHelper(QString::fromAscii(fmt), d->source())) { // Not a MIME type we have
+ DEBUG("QClipboard: send_selection(): converting to type '%s' is not supported", fmt.data());
+ return XNone;
+ }
+ DEBUG("QClipboard: send_selection(): converting to type '%s'", fmt.data());
+
+ if (X11->xdndMimeDataForAtom(target, d->source(), &data, &atomFormat, &dataFormat)) {
+
+ VDEBUG("QClipboard: send_selection():\n"
+ " property type %lx\n"
+ " property name '%s'\n"
+ " format %d\n"
+ " %d bytes\n",
+ target, X11->xdndMimeAtomToString(atomFormat).toLatin1().data(), dataFormat, data.size());
+
+ // don't allow INCR transfers when using MULTIPLE or to
+ // Motif clients (since Motif doesn't support INCR)
+ static Atom motif_clip_temporary = ATOM(CLIP_TEMPORARY);
+ bool allow_incr = property != motif_clip_temporary;
+
+ // X_ChangeProperty protocol request is 24 bytes
+ const int increment = (XMaxRequestSize(X11->display) * 4) - 24;
+ if (data.size() > increment && allow_incr) {
+ long bytes = data.size();
+ XChangeProperty(X11->display, window, property,
+ ATOM(INCR), 32, PropModeReplace, (uchar *) &bytes, 1);
+
+ (void)new QClipboardINCRTransaction(window, property, atomFormat, dataFormat, data, increment);
+ return ATOM(INCR);
+ }
+
+ // make sure we can perform the XChangeProperty in a single request
+ if (data.size() > increment)
+ return XNone; // ### perhaps use several XChangeProperty calls w/ PropModeAppend?
+ int dataSize = data.size() / (dataFormat / 8);
+ // use a single request to transfer data
+ XChangeProperty(X11->display, window, property, atomFormat,
+ dataFormat, PropModeReplace, (uchar *) data.data(),
+ dataSize);
+ }
+ return property;
+}
+
+/*! \internal
+ Internal cleanup for Windows.
+*/
+void QClipboard::ownerDestroyed()
+{ }
+
+
+/*! \internal
+ Internal optimization for Windows.
+*/
+void QClipboard::connectNotify(const char *)
+{ }
+
+
+bool QClipboard::event(QEvent *e)
+{
+ if (e->type() == QEvent::Timer) {
+ QTimerEvent *te = (QTimerEvent *) e;
+
+ if (waiting_for_data) // should never happen
+ return false;
+
+ if (te->timerId() == timer_id) {
+ killTimer(timer_id);
+ timer_id = 0;
+
+ timer_event_clear = true;
+ if (selection_watcher) // clear selection
+ selectionData()->clear();
+ if (clipboard_watcher) // clear clipboard
+ clipboardData()->clear();
+ timer_event_clear = false;
+
+ return true;
+ } else if (te->timerId() == pending_timer_id) {
+ // I hate klipper
+ killTimer(pending_timer_id);
+ pending_timer_id = 0;
+
+ if (pending_clipboard_changed) {
+ pending_clipboard_changed = false;
+ clipboardData()->clear();
+ emitChanged(QClipboard::Clipboard);
+ }
+ if (pending_selection_changed) {
+ pending_selection_changed = false;
+ selectionData()->clear();
+ emitChanged(QClipboard::Selection);
+ }
+
+ return true;
+ } else if (te->timerId() == incr_timer_id) {
+ killTimer(incr_timer_id);
+ incr_timer_id = 0;
+
+ qt_xclb_incr_timeout();
+
+ return true;
+ } else {
+ return QObject::event(e);
+ }
+ } else if (e->type() != QEvent::Clipboard) {
+ return QObject::event(e);
+ }
+
+ XEvent *xevent = (XEvent *)(((QClipboardEvent *)e)->data());
+ Display *dpy = X11->display;
+
+ if (!xevent)
+ return true;
+
+ switch (xevent->type) {
+
+ case SelectionClear:
+ // new selection owner
+ if (xevent->xselectionclear.selection == XA_PRIMARY) {
+ QClipboardData *d = selectionData();
+
+ // ignore the event if it was generated before we gained selection ownership
+ if (d->timestamp != CurrentTime && xevent->xselectionclear.time <= d->timestamp)
+ break;
+
+ DEBUG("QClipboard: new selection owner 0x%lx at time %lx (ours %lx)",
+ XGetSelectionOwner(dpy, XA_PRIMARY),
+ xevent->xselectionclear.time, d->timestamp);
+
+ if (! waiting_for_data) {
+ d->clear();
+ emitChanged(QClipboard::Selection);
+ } else {
+ pending_selection_changed = true;
+ if (! pending_timer_id)
+ pending_timer_id = QApplication::clipboard()->startTimer(0);
+ }
+ } else if (xevent->xselectionclear.selection == ATOM(CLIPBOARD)) {
+ QClipboardData *d = clipboardData();
+
+ // ignore the event if it was generated before we gained selection ownership
+ if (d->timestamp != CurrentTime && xevent->xselectionclear.time <= d->timestamp)
+ break;
+
+ DEBUG("QClipboard: new clipboard owner 0x%lx at time %lx (%lx)",
+ XGetSelectionOwner(dpy, ATOM(CLIPBOARD)),
+ xevent->xselectionclear.time, d->timestamp);
+
+ if (! waiting_for_data) {
+ d->clear();
+ emitChanged(QClipboard::Clipboard);
+ } else {
+ pending_clipboard_changed = true;
+ if (! pending_timer_id)
+ pending_timer_id = QApplication::clipboard()->startTimer(0);
+ }
+ } else {
+ qWarning("QClipboard: Unknown SelectionClear event received");
+ return false;
+ }
+ break;
+
+ case SelectionNotify:
+ /*
+ Something has delivered data to us, but this was not caught
+ by QClipboardWatcher::getDataInFormat()
+
+ Just skip the event to prevent Bad Things (tm) from
+ happening later on...
+ */
+ break;
+
+ case SelectionRequest:
+ {
+ // someone wants our data
+ XSelectionRequestEvent *req = &xevent->xselectionrequest;
+
+ if (requestor && req->requestor == requestor->internalWinId())
+ break;
+
+ XEvent event;
+ event.xselection.type = SelectionNotify;
+ event.xselection.display = req->display;
+ event.xselection.requestor = req->requestor;
+ event.xselection.selection = req->selection;
+ event.xselection.target = req->target;
+ event.xselection.property = XNone;
+ event.xselection.time = req->time;
+
+ DEBUG("QClipboard: SelectionRequest from %lx\n"
+ " selection 0x%lx (%s) target 0x%lx (%s)",
+ req->requestor,
+ req->selection,
+ X11->xdndAtomToString(req->selection).data(),
+ req->target,
+ X11->xdndAtomToString(req->target).data());
+
+ QClipboardData *d;
+ if (req->selection == XA_PRIMARY) {
+ d = selectionData();
+ } else if (req->selection == ATOM(CLIPBOARD)) {
+ d = clipboardData();
+ } else {
+ qWarning("QClipboard: Unknown selection '%lx'", req->selection);
+ XSendEvent(dpy, req->requestor, False, NoEventMask, &event);
+ break;
+ }
+
+ if (! d->source()) {
+ qWarning("QClipboard: Cannot transfer data, no data available");
+ XSendEvent(dpy, req->requestor, False, NoEventMask, &event);
+ break;
+ }
+
+ DEBUG("QClipboard: SelectionRequest at time %lx (ours %lx)",
+ req->time, d->timestamp);
+
+ if (d->timestamp == CurrentTime // we don't own the selection anymore
+ || (req->time != CurrentTime && req->time < d->timestamp)) {
+ DEBUG("QClipboard: SelectionRequest too old");
+ XSendEvent(dpy, req->requestor, False, NoEventMask, &event);
+ break;
+ }
+
+ Atom xa_targets = ATOM(TARGETS);
+ Atom xa_multiple = ATOM(MULTIPLE);
+ Atom xa_timestamp = ATOM(TIMESTAMP);
+
+ struct AtomPair { Atom target; Atom property; } *multi = 0;
+ Atom multi_type = XNone;
+ int multi_format = 0;
+ int nmulti = 0;
+ int imulti = -1;
+ bool multi_writeback = false;
+
+ if (req->target == xa_multiple) {
+ QByteArray multi_data;
+ if (req->property == XNone
+ || !X11->clipboardReadProperty(req->requestor, req->property, false, &multi_data,
+ 0, &multi_type, &multi_format, 0)
+ || multi_format != 32) {
+ // MULTIPLE property not formatted correctly
+ XSendEvent(dpy, req->requestor, False, NoEventMask, &event);
+ break;
+ }
+ nmulti = multi_data.size()/sizeof(*multi);
+ multi = new AtomPair[nmulti];
+ memcpy(multi,multi_data.data(),multi_data.size());
+ imulti = 0;
+ }
+
+ for (; imulti < nmulti; ++imulti) {
+ Atom target;
+ Atom property;
+
+ if (multi) {
+ target = multi[imulti].target;
+ property = multi[imulti].property;
+ } else {
+ target = req->target;
+ property = req->property;
+ if (property == XNone) // obsolete client
+ property = target;
+ }
+
+ Atom ret = XNone;
+ if (target == XNone || property == XNone) {
+ ;
+ } else if (target == xa_timestamp) {
+ if (d->timestamp != CurrentTime) {
+ XChangeProperty(dpy, req->requestor, property, XA_INTEGER, 32,
+ PropModeReplace, (uchar *) &d->timestamp, 1);
+ ret = property;
+ } else {
+ qWarning("QClipboard: Invalid data timestamp");
+ }
+ } else if (target == xa_targets) {
+ ret = send_targets_selection(d, req->requestor, property);
+ } else {
+ ret = send_selection(d, target, req->requestor, property);
+ }
+
+ if (nmulti > 0) {
+ if (ret == XNone) {
+ multi[imulti].property = XNone;
+ multi_writeback = true;
+ }
+ } else {
+ event.xselection.property = ret;
+ break;
+ }
+ }
+
+ if (nmulti > 0) {
+ if (multi_writeback) {
+ // according to ICCCM 2.6.2 says to put None back
+ // into the original property on the requestor window
+ XChangeProperty(dpy, req->requestor, req->property, multi_type, 32,
+ PropModeReplace, (uchar *) multi, nmulti * 2);
+ }
+
+ delete [] multi;
+ event.xselection.property = req->property;
+ }
+
+ // send selection notify to requestor
+ XSendEvent(dpy, req->requestor, False, NoEventMask, &event);
+
+ DEBUG("QClipboard: SelectionNotify to 0x%lx\n"
+ " property 0x%lx (%s)",
+ req->requestor, event.xselection.property,
+ X11->xdndAtomToString(event.xselection.property).data());
+ }
+ break;
+ }
+
+ return true;
+}
+
+
+
+
+
+
+QClipboardWatcher::QClipboardWatcher(QClipboard::Mode mode)
+ : QInternalMimeData()
+{
+ switch (mode) {
+ case QClipboard::Selection:
+ atom = XA_PRIMARY;
+ break;
+
+ case QClipboard::Clipboard:
+ atom = ATOM(CLIPBOARD);
+ break;
+
+ default:
+ qWarning("QClipboardWatcher: Internal error: Unsupported clipboard mode");
+ break;
+ }
+
+ setupOwner();
+}
+
+QClipboardWatcher::~QClipboardWatcher()
+{
+ if(selection_watcher == this)
+ selection_watcher = 0;
+ if(clipboard_watcher == this)
+ clipboard_watcher = 0;
+}
+
+bool QClipboardWatcher::empty() const
+{
+ Display *dpy = X11->display;
+ Window win = XGetSelectionOwner(dpy, atom);
+
+ if(win == requestor->internalWinId()) {
+ qWarning("QClipboardWatcher::empty: Internal error: Application owns the selection");
+ return true;
+ }
+
+ return win == XNone;
+}
+
+QStringList QClipboardWatcher::formats_sys() const
+{
+ if (empty())
+ return QStringList();
+
+ if (!formatList.count()) {
+ // get the list of targets from the current clipboard owner - we do this
+ // once so that multiple calls to this function don't require multiple
+ // server round trips...
+
+ format_atoms = getDataInFormat(ATOM(TARGETS));
+
+ if (format_atoms.size() > 0) {
+ Atom *targets = (Atom *) format_atoms.data();
+ int size = format_atoms.size() / sizeof(Atom);
+
+ for (int i = 0; i < size; ++i) {
+ if (targets[i] == 0)
+ continue;
+
+ QStringList formatsForAtom = X11->xdndMimeFormatsForAtom(targets[i]);
+ for (int j = 0; j < formatsForAtom.size(); ++j) {
+ if (!formatList.contains(formatsForAtom.at(j)))
+ formatList.append(formatsForAtom.at(j));
+ }
+ VDEBUG(" format: %s", X11->xdndAtomToString(targets[i]).data());
+ VDEBUG(" data:\n%s\n", getDataInFormat(targets[i]).data());
+ }
+ DEBUG("QClipboardWatcher::format: %d formats available", formatList.count());
+ }
+ }
+
+ return formatList;
+}
+
+bool QClipboardWatcher::hasFormat_sys(const QString &format) const
+{
+ QStringList list = formats();
+ return list.contains(format);
+}
+
+QVariant QClipboardWatcher::retrieveData_sys(const QString &fmt, QVariant::Type requestedType) const
+{
+ if (fmt.isEmpty() || empty())
+ return QByteArray();
+
+ (void)formats(); // trigger update of format list
+ DEBUG("QClipboardWatcher::data: fetching format '%s'", fmt.toLatin1().data());
+
+ QList<Atom> atoms;
+ Atom *targets = (Atom *) format_atoms.data();
+ int size = format_atoms.size() / sizeof(Atom);
+ for (int i = 0; i < size; ++i)
+ atoms.append(targets[i]);
+
+ QByteArray encoding;
+ Atom fmtatom = X11->xdndMimeAtomForFormat(fmt, requestedType, atoms, &encoding);
+
+ if (fmtatom == 0)
+ return QVariant();
+
+ return X11->xdndMimeConvertToFormat(fmtatom, getDataInFormat(fmtatom), fmt, requestedType, encoding);
+}
+
+QByteArray QClipboardWatcher::getDataInFormat(Atom fmtatom) const
+{
+ QByteArray buf;
+
+ Display *dpy = X11->display;
+ requestor->createWinId();
+ Window win = requestor->internalWinId();
+ Q_ASSERT(requestor->testAttribute(Qt::WA_WState_Created));
+
+ DEBUG("QClipboardWatcher::getDataInFormat: selection '%s' format '%s'",
+ X11->xdndAtomToString(atom).data(), X11->xdndAtomToString(fmtatom).data());
+
+ XSelectInput(dpy, win, NoEventMask); // don't listen for any events
+
+ XDeleteProperty(dpy, win, ATOM(_QT_SELECTION));
+ XConvertSelection(dpy, atom, fmtatom, ATOM(_QT_SELECTION), win, X11->time);
+ XSync(dpy, false);
+
+ VDEBUG("QClipboardWatcher::getDataInFormat: waiting for SelectionNotify event");
+
+ XEvent xevent;
+ if (!X11->clipboardWaitForEvent(win,SelectionNotify,&xevent,clipboard_timeout) ||
+ xevent.xselection.property == XNone) {
+ DEBUG("QClipboardWatcher::getDataInFormat: format not available");
+ return buf;
+ }
+
+ VDEBUG("QClipboardWatcher::getDataInFormat: fetching data...");
+
+ Atom type;
+ XSelectInput(dpy, win, PropertyChangeMask);
+
+ if (X11->clipboardReadProperty(win, ATOM(_QT_SELECTION), true, &buf, 0, &type, 0, false)) {
+ if (type == ATOM(INCR)) {
+ int nbytes = buf.size() >= 4 ? *((int*)buf.data()) : 0;
+ buf = X11->clipboardReadIncrementalProperty(win, ATOM(_QT_SELECTION), nbytes, false);
+ }
+ }
+
+ XSelectInput(dpy, win, NoEventMask);
+
+ DEBUG("QClipboardWatcher::getDataInFormat: %d bytes received", buf.size());
+
+ return buf;
+}
+
+
+const QMimeData* QClipboard::mimeData(Mode mode) const
+{
+ QClipboardData *d = 0;
+ switch (mode) {
+ case Selection:
+ d = selectionData();
+ break;
+ case Clipboard:
+ d = clipboardData();
+ break;
+ default:
+ qWarning("QClipboard::mimeData: unsupported mode '%d'", mode);
+ return 0;
+ }
+
+ if (! d->source() && ! timer_event_clear) {
+ if (mode == Selection) {
+ if (! selection_watcher)
+ selection_watcher = new QClipboardWatcher(mode);
+ d->setSource(selection_watcher);
+ } else {
+ if (! clipboard_watcher)
+ clipboard_watcher = new QClipboardWatcher(mode);
+ d->setSource(clipboard_watcher);
+ }
+
+ if (! timer_id) {
+ // start a zero timer - we will clear cached data when the timer
+ // times out, which will be the next time we hit the event loop...
+ // that way, the data is cached long enough for calls within a single
+ // loop/function, but the data doesn't linger around in case the selection
+ // changes
+ QClipboard *that = ((QClipboard *) this);
+ timer_id = that->startTimer(0);
+ }
+ }
+
+ return d->source();
+}
+
+
+void QClipboard::setMimeData(QMimeData* src, Mode mode)
+{
+ Atom atom, sentinel_atom;
+ QClipboardData *d;
+ switch (mode) {
+ case Selection:
+ atom = XA_PRIMARY;
+ sentinel_atom = ATOM(_QT_SELECTION_SENTINEL);
+ d = selectionData();
+ break;
+
+ case Clipboard:
+ atom = ATOM(CLIPBOARD);
+ sentinel_atom = ATOM(_QT_CLIPBOARD_SENTINEL);
+ d = clipboardData();
+ break;
+
+ default:
+ qWarning("QClipboard::setMimeData: unsupported mode '%d'", mode);
+ return;
+ }
+
+ Display *dpy = X11->display;
+ Window newOwner;
+
+ if (! src) { // no data, clear clipboard contents
+ newOwner = XNone;
+ d->clear();
+ } else {
+ setupOwner();
+
+ newOwner = owner->internalWinId();
+
+ d->setSource(src);
+ d->timestamp = X11->time;
+ }
+
+ Window prevOwner = XGetSelectionOwner(dpy, atom);
+ // use X11->time, since d->timestamp == CurrentTime when clearing
+ XSetSelectionOwner(dpy, atom, newOwner, X11->time);
+
+ if (mode == Selection)
+ emitChanged(QClipboard::Selection);
+ else
+ emitChanged(QClipboard::Clipboard);
+
+ if (XGetSelectionOwner(dpy, atom) != newOwner) {
+ qWarning("QClipboard::setData: Cannot set X11 selection owner for %s",
+ X11->xdndAtomToString(atom).data());
+ d->clear();
+ return;
+ }
+
+ // Signal to other Qt processes that the selection has changed
+ Window owners[2];
+ owners[0] = newOwner;
+ owners[1] = prevOwner;
+ XChangeProperty(dpy, QApplication::desktop()->screen(0)->internalWinId(),
+ sentinel_atom, XA_WINDOW, 32, PropModeReplace,
+ (unsigned char*)&owners, 2);
+}
+
+
+/*
+ Called by the main event loop in qapplication_x11.cpp when either
+ the _QT_SELECTION_SENTINEL property has been changed (i.e. when some
+ Qt process has performed QClipboard::setData()) or when Xfixes told
+ us that an other application changed the selection. If it returns
+ true, the QClipBoard dataChanged() signal should be emitted.
+*/
+
+bool qt_check_selection_sentinel()
+{
+ bool doIt = true;
+ if (owner && !X11->use_xfixes) {
+ /*
+ Since the X selection mechanism cannot give any signal when
+ the selection has changed, we emulate it (for Qt processes) here.
+ The notification should be ignored in case of either
+ a) This is the process that did setData (because setData()
+ then has already emitted dataChanged())
+ b) This is the process that owned the selection when dataChanged()
+ was called (because we have then received a SelectionClear event,
+ and have already emitted dataChanged() as a result of that)
+ */
+
+ unsigned char *retval;
+ Atom actualType;
+ int actualFormat;
+ ulong nitems;
+ ulong bytesLeft;
+
+ if (XGetWindowProperty(X11->display,
+ QApplication::desktop()->screen(0)->internalWinId(),
+ ATOM(_QT_SELECTION_SENTINEL), 0, 2, False, XA_WINDOW,
+ &actualType, &actualFormat, &nitems,
+ &bytesLeft, &retval) == Success) {
+ Window *owners = (Window *)retval;
+ if (actualType == XA_WINDOW && actualFormat == 32 && nitems == 2) {
+ Window win = owner->internalWinId();
+ if (owners[0] == win || owners[1] == win)
+ doIt = false;
+ }
+
+ XFree(owners);
+ }
+ }
+
+ if (doIt) {
+ if (waiting_for_data) {
+ pending_selection_changed = true;
+ if (! pending_timer_id)
+ pending_timer_id = QApplication::clipboard()->startTimer(0);
+ doIt = false;
+ } else {
+ selectionData()->clear();
+ }
+ }
+
+ return doIt;
+}
+
+
+bool qt_check_clipboard_sentinel()
+{
+ bool doIt = true;
+ if (owner && !X11->use_xfixes) {
+ unsigned char *retval;
+ Atom actualType;
+ int actualFormat;
+ unsigned long nitems, bytesLeft;
+
+ if (XGetWindowProperty(X11->display,
+ QApplication::desktop()->screen(0)->internalWinId(),
+ ATOM(_QT_CLIPBOARD_SENTINEL), 0, 2, False, XA_WINDOW,
+ &actualType, &actualFormat, &nitems, &bytesLeft,
+ &retval) == Success) {
+ Window *owners = (Window *)retval;
+ if (actualType == XA_WINDOW && actualFormat == 32 && nitems == 2) {
+ Window win = owner->internalWinId();
+ if (owners[0] == win || owners[1] == win)
+ doIt = false;
+ }
+
+ XFree(owners);
+ }
+ }
+
+ if (doIt) {
+ if (waiting_for_data) {
+ pending_clipboard_changed = true;
+ if (! pending_timer_id)
+ pending_timer_id = QApplication::clipboard()->startTimer(0);
+ doIt = false;
+ } else {
+ clipboardData()->clear();
+ }
+ }
+
+ return doIt;
+}
+
+bool qt_xfixes_selection_changed(Window selectionOwner, Time timestamp)
+{
+ QClipboardData *d = selectionData();
+#ifdef QCLIPBOARD_DEBUG
+ DEBUG("qt_xfixes_selection_changed: owner = %u; selectionOwner = %u; internal timestamp = %u; external timestamp = %u",
+ (unsigned int)(owner ? (int)owner->internalWinId() : 0), (unsigned int)selectionOwner,
+ (unsigned int)(d ? d->timestamp : 0), (unsigned int)timestamp);
+#endif
+ if (!owner || (selectionOwner && selectionOwner != owner->internalWinId()) ||
+ (!selectionOwner && d->timestamp != CurrentTime && d->timestamp < timestamp))
+ return qt_check_selection_sentinel();
+ return false;
+}
+
+bool qt_xfixes_clipboard_changed(Window clipboardOwner, Time timestamp)
+{
+ QClipboardData *d = clipboardData();
+#ifdef QCLIPBOARD_DEBUG
+ DEBUG("qt_xfixes_clipboard_changed: owner = %u; clipboardOwner = %u; internal timestamp = %u; external timestamp = %u",
+ (unsigned int)(owner ? (int)owner->internalWinId() : 0), (unsigned int)clipboardOwner,
+ (unsigned int)(d ? d->timestamp : 0), (unsigned int)timestamp);
+#endif
+ if (!owner || (clipboardOwner && clipboardOwner != owner->internalWinId()) ||
+ (!clipboardOwner && d->timestamp != CurrentTime && d->timestamp < timestamp))
+ return qt_check_clipboard_sentinel();
+ return false;
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_CLIPBOARD
diff --git a/src/gui/kernel/qcocoaapplication_mac.mm b/src/gui/kernel/qcocoaapplication_mac.mm
new file mode 100644
index 0000000000..f95f004f33
--- /dev/null
+++ b/src/gui/kernel/qcocoaapplication_mac.mm
@@ -0,0 +1,114 @@
+/****************************************************************************
+ **
+ ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+ **
+ ** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+ **
+ ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
+ ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ **
+ ****************************************************************************/
+
+/****************************************************************************
+**
+** Copyright (c) 2007-2008, Apple, Inc.
+**
+** All rights reserved.
+**
+** Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are met:
+**
+** * Redistributions of source code must retain the above copyright notice,
+** this list of conditions and the following disclaimer.
+**
+** * Redistributions in binary form must reproduce the above copyright notice,
+** this list of conditions and the following disclaimer in the documentation
+** and/or other materials provided with the distribution.
+**
+** * Neither the name of Apple, Inc. nor the names of its contributors
+** may be used to endorse or promote products derived from this software
+** without specific prior written permission.
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+** CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+** EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+** PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+** PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+** LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+** NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+** SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+**
+****************************************************************************/
+
+#include <qglobal.h>
+#ifdef QT_MAC_USE_COCOA
+#include <private/qcocoaapplication_mac_p.h>
+#include <private/qcocoaapplicationdelegate_mac_p.h>
+#include <private/qt_cocoa_helpers_mac_p.h>
+
+@implementation NSApplication (QT_MANGLE_NAMESPACE(QApplicationIntegration))
+
+- (void)QT_MANGLE_NAMESPACE(qt_setDockMenu):(NSMenu *)newMenu
+{
+ [[QT_MANGLE_NAMESPACE(QCocoaApplicationDelegate) sharedDelegate] setDockMenu:newMenu];
+}
+
+- (QApplicationPrivate *)QT_MANGLE_NAMESPACE(qt_qappPrivate)
+{
+ return [[QT_MANGLE_NAMESPACE(QCocoaApplicationDelegate) sharedDelegate] qAppPrivate];
+}
+
+- (QT_MANGLE_NAMESPACE(QCocoaMenuLoader) *)QT_MANGLE_NAMESPACE(qt_qcocoamenuLoader)
+{
+ return [[QT_MANGLE_NAMESPACE(QCocoaApplicationDelegate) sharedDelegate] menuLoader];
+}
+
+- (int)QT_MANGLE_NAMESPACE(qt_validModesForFontPanel):(NSFontPanel *)fontPanel
+{
+ Q_UNUSED(fontPanel);
+ // only display those things that QFont can handle
+ return NSFontPanelFaceModeMask
+ | NSFontPanelSizeModeMask
+ | NSFontPanelCollectionModeMask
+ | NSFontPanelUnderlineEffectModeMask
+ | NSFontPanelStrikethroughEffectModeMask;
+}
+
+@end
+#endif
diff --git a/src/gui/kernel/qcocoaapplication_mac_p.h b/src/gui/kernel/qcocoaapplication_mac_p.h
new file mode 100644
index 0000000000..15c64d53fe
--- /dev/null
+++ b/src/gui/kernel/qcocoaapplication_mac_p.h
@@ -0,0 +1,103 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/****************************************************************************
+**
+** Copyright (c) 2007-2008, Apple, Inc.
+**
+** All rights reserved.
+**
+** Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are met:
+**
+** * Redistributions of source code must retain the above copyright notice,
+** this list of conditions and the following disclaimer.
+**
+** * Redistributions in binary form must reproduce the above copyright notice,
+** this list of conditions and the following disclaimer in the documentation
+** and/or other materials provided with the distribution.
+**
+** * Neither the name of Apple, Inc. nor the names of its contributors
+** may be used to endorse or promote products derived from this software
+** without specific prior written permission.
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+** CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+** EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+** PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+** PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+** LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+** NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+** SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+**
+****************************************************************************/
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of qapplication_*.cpp, qwidget*.cpp, qcolor_x11.cpp, qfiledialog.cpp
+// and many other. This header file may change from version to version
+// without notice, or even be removed.
+//
+// We mean it.
+//
+
+/*
+ Cocoa Application Categories
+*/
+#include "qmacdefines_mac.h"
+#ifdef QT_MAC_USE_COCOA
+#import <AppKit/AppKit.h>
+QT_FORWARD_DECLARE_CLASS(QApplicationPrivate)
+@class QT_MANGLE_NAMESPACE(QCocoaMenuLoader);
+
+@interface NSApplication (QT_MANGLE_NAMESPACE(QApplicationIntegration))
+- (void)QT_MANGLE_NAMESPACE(qt_setDockMenu):(NSMenu *)newMenu;
+- (QApplicationPrivate *)QT_MANGLE_NAMESPACE(qt_qappPrivate);
+- (QT_MANGLE_NAMESPACE(QCocoaMenuLoader) *)QT_MANGLE_NAMESPACE(qt_qcocoamenuLoader);
+- (int)QT_MANGLE_NAMESPACE(qt_validModesForFontPanel):(NSFontPanel *)fontPanel;
+@end
+#endif
diff --git a/src/gui/kernel/qcocoaapplicationdelegate_mac.mm b/src/gui/kernel/qcocoaapplicationdelegate_mac.mm
new file mode 100644
index 0000000000..650ebbd663
--- /dev/null
+++ b/src/gui/kernel/qcocoaapplicationdelegate_mac.mm
@@ -0,0 +1,282 @@
+/****************************************************************************
+ **
+ ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+ **
+ ** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+ **
+ ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
+ ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ **
+ ****************************************************************************/
+
+/****************************************************************************
+ **
+ ** Copyright (c) 2007-2008, Apple, Inc.
+ **
+ ** All rights reserved.
+ **
+ ** Redistribution and use in source and binary forms, with or without
+ ** modification, are permitted provided that the following conditions are met:
+ **
+ ** * Redistributions of source code must retain the above copyright notice,
+ ** this list of conditions and the following disclaimer.
+ **
+ ** * Redistributions in binary form must reproduce the above copyright notice,
+ ** this list of conditions and the following disclaimer in the documentation
+ ** and/or other materials provided with the distribution.
+ **
+ ** * Neither the name of Apple, Inc. nor the names of its contributors
+ ** may be used to endorse or promote products derived from this software
+ ** without specific prior written permission.
+ **
+ ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ ** CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ ** EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ ** PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ ** PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ ** LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ ** NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ ** SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ **
+ ****************************************************************************/
+
+#include "qmacdefines_mac.h"
+#ifdef QT_MAC_USE_COCOA
+
+#import <private/qcocoaapplicationdelegate_mac_p.h>
+#import <private/qcocoamenuloader_mac_p.h>
+#include <private/qapplication_p.h>
+#include <private/qt_mac_p.h>
+#include <private/qt_cocoa_helpers_mac_p.h>
+#include <private/qdesktopwidget_mac_p.h>
+#include <qevent.h>
+#include <qapplication.h>
+
+QT_BEGIN_NAMESPACE
+extern void onApplicationChangedActivation(bool); // qapplication_mac.mm
+QT_END_NAMESPACE
+
+QT_FORWARD_DECLARE_CLASS(QDesktopWidgetImplementation)
+QT_USE_NAMESPACE
+
+static QT_MANGLE_NAMESPACE(QCocoaApplicationDelegate) *sharedCocoaApplicationDelegate = nil;
+
+static void cleanupCocoaApplicationDelegate()
+{
+ [sharedCocoaApplicationDelegate release];
+}
+
+@implementation QT_MANGLE_NAMESPACE(QCocoaApplicationDelegate)
+
+- (id)init
+{
+ self = [super init];
+ return self;
+}
+
+- (void)dealloc
+{
+ sharedCocoaApplicationDelegate = nil;
+ [dockMenu release];
+ [qtMenuLoader release];
+ if (reflectionDelegate) {
+ [NSApp setDelegate:reflectionDelegate];
+ [reflectionDelegate release];
+ }
+ [super dealloc];
+}
+
++ (id)allocWithZone:(NSZone *)zone
+{
+ @synchronized(self) {
+ if (sharedCocoaApplicationDelegate == nil) {
+ sharedCocoaApplicationDelegate = [super allocWithZone:zone];
+ return sharedCocoaApplicationDelegate;
+ qAddPostRoutine(cleanupCocoaApplicationDelegate);
+ }
+ }
+ return nil;
+}
+
++ (QT_MANGLE_NAMESPACE(QCocoaApplicationDelegate)*)sharedDelegate
+{
+ @synchronized(self) {
+ if (sharedCocoaApplicationDelegate == nil)
+ [[self alloc] init];
+ }
+ return [[sharedCocoaApplicationDelegate retain] autorelease];
+}
+
+- (void)setDockMenu:(NSMenu*)newMenu
+{
+ [newMenu retain];
+ [dockMenu release];
+ dockMenu = newMenu;
+}
+
+- (NSMenu *)applicationDockMenu
+{
+ return [[dockMenu retain] autorelease];
+}
+
+- (QApplicationPrivate *)qAppPrivate
+{
+ return qtPrivate;
+}
+
+- (void)setQtPrivate:(QApplicationPrivate *)value
+{
+ qtPrivate = value;
+}
+
+- (void)setMenuLoader:(QT_MANGLE_NAMESPACE(QCocoaMenuLoader) *)menuLoader
+{
+ [menuLoader retain];
+ [qtMenuLoader release];
+ qtMenuLoader = menuLoader;
+}
+
+- (QT_MANGLE_NAMESPACE(QCocoaMenuLoader) *)menuLoader;
+{
+ return [[qtMenuLoader retain] autorelease];
+}
+
+- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender
+{
+ Q_UNUSED(sender);
+ // The reflection delegate gets precedence
+ NSApplicationTerminateReply reply = NSTerminateCancel;
+ if (reflectionDelegate
+ && [reflectionDelegate respondsToSelector:@selector(applicationShouldTerminate:)]) {
+ return [reflectionDelegate applicationShouldTerminate:sender];
+ }
+
+ if (qtPrivate->canQuit()) {
+ reply = NSTerminateNow;
+ if (!startedQuit) {
+ startedQuit = true;
+ qAppInstance()->quit();
+ startedQuit = false;
+ }
+ }
+ return reply;
+}
+
+- (void)application:(NSApplication *)sender openFiles:(NSArray *)filenames
+{
+ unsigned int ix;
+ for( ix = 0; ix < [filenames count]; ix++) {
+ NSString *fileName = [filenames objectAtIndex:ix];
+ qApp->postEvent(qApp, new QFileOpenEvent(QCFString::toQString((CFStringRef)fileName)));
+ }
+
+ if (reflectionDelegate &&
+ [reflectionDelegate respondsToSelector:@selector(application:openFiles:)])
+ [reflectionDelegate application:sender openFiles:filenames];
+}
+
+- (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)sender
+{
+ // If we have a reflection delegate, that will get to call the shots.
+ if (reflectionDelegate
+ && [reflectionDelegate respondsToSelector:
+ @selector(applicationShouldTerminateAfterLastWindowClosed:)])
+ return [reflectionDelegate applicationShouldTerminateAfterLastWindowClosed:sender];
+ return NO; // Someday qApp->quitOnLastWindowClosed(); when QApp and NSApp work closer together.
+}
+
+
+- (void)applicationDidBecomeActive:(NSNotification *)notification
+{
+ if (reflectionDelegate
+ && [reflectionDelegate respondsToSelector:@selector(applicationDidBecomeActive:)])
+ [reflectionDelegate applicationDidBecomeActive:notification];
+ onApplicationChangedActivation(true);
+}
+
+- (void)applicationDidResignActive:(NSNotification *)notification;
+{
+ if (reflectionDelegate
+ && [reflectionDelegate respondsToSelector:@selector(applicationDidResignActive:)])
+ [reflectionDelegate applicationDidResignActive:notification];
+ onApplicationChangedActivation(false);
+}
+
+class QDesktopWidgetImplementation;
+- (void)applicationDidChangeScreenParameters:(NSNotification *)notification
+{
+ Q_UNUSED(notification);
+ QDesktopWidgetImplementation::instance()->onResize();
+}
+
+- (void)setReflectionDelegate:(NSObject *)oldDelegate
+{
+ [oldDelegate retain];
+ [reflectionDelegate release];
+ reflectionDelegate = oldDelegate;
+}
+
+- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
+{
+ NSMethodSignature *result = [super methodSignatureForSelector:aSelector];
+ if (!result && reflectionDelegate) {
+ result = [reflectionDelegate methodSignatureForSelector:aSelector];
+ }
+ return result;
+}
+
+- (BOOL)respondsToSelector:(SEL)aSelector
+{
+ BOOL result = [super respondsToSelector:aSelector];
+ if (!result && reflectionDelegate)
+ result = [reflectionDelegate respondsToSelector:aSelector];
+ return result;
+}
+
+- (void)forwardInvocation:(NSInvocation *)invocation
+{
+ SEL invocationSelector = [invocation selector];
+ if (reflectionDelegate && [reflectionDelegate respondsToSelector:invocationSelector])
+ [invocation invokeWithTarget:reflectionDelegate];
+ else
+ [self doesNotRecognizeSelector:invocationSelector];
+}
+
+@end
+#endif
diff --git a/src/gui/kernel/qcocoaapplicationdelegate_mac_p.h b/src/gui/kernel/qcocoaapplicationdelegate_mac_p.h
new file mode 100644
index 0000000000..c5336f1797
--- /dev/null
+++ b/src/gui/kernel/qcocoaapplicationdelegate_mac_p.h
@@ -0,0 +1,119 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+
+/****************************************************************************
+ **
+ ** Copyright (c) 2007-2008, Apple, Inc.
+ **
+ ** All rights reserved.
+ **
+ ** Redistribution and use in source and binary forms, with or without
+ ** modification, are permitted provided that the following conditions are met:
+ **
+ ** * Redistributions of source code must retain the above copyright notice,
+ ** this list of conditions and the following disclaimer.
+ **
+ ** * Redistributions in binary form must reproduce the above copyright notice,
+ ** this list of conditions and the following disclaimer in the documentation
+ ** and/or other materials provided with the distribution.
+ **
+ ** * Neither the name of Apple, Inc. nor the names of its contributors
+ ** may be used to endorse or promote products derived from this software
+ ** without specific prior written permission.
+ **
+ ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ ** CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ ** EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ ** PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ ** PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ ** LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ ** NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ ** SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ **
+ ****************************************************************************/
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of qapplication_*.cpp, qwidget*.cpp, qcolor_x11.cpp, qfiledialog.cpp
+// and many other. This header file may change from version to version
+// without notice, or even be removed.
+//
+// We mean it.
+//
+
+
+#include "qmacdefines_mac.h"
+#ifdef QT_MAC_USE_COCOA
+#import <Cocoa/Cocoa.h>
+
+QT_FORWARD_DECLARE_CLASS(QApplicationPrivate);
+
+@class QT_MANGLE_NAMESPACE(QCocoaMenuLoader);
+
+#if MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_5
+
+@protocol NSApplicationDelegate <NSObject> @end
+
+#endif
+
+@interface QT_MANGLE_NAMESPACE(QCocoaApplicationDelegate) : NSObject <NSApplicationDelegate> {
+ bool startedQuit;
+ QApplicationPrivate *qtPrivate;
+ NSMenu *dockMenu;
+ QT_MANGLE_NAMESPACE(QCocoaMenuLoader) *qtMenuLoader;
+ id <NSApplicationDelegate> reflectionDelegate;
+}
++ (QT_MANGLE_NAMESPACE(QCocoaApplicationDelegate)*)sharedDelegate;
+- (void)setDockMenu:(NSMenu *)newMenu;
+- (void)setQtPrivate:(QApplicationPrivate *)value;
+- (QApplicationPrivate *)qAppPrivate;
+- (void)setMenuLoader:(QT_MANGLE_NAMESPACE(QCocoaMenuLoader)*)menuLoader;
+- (QT_MANGLE_NAMESPACE(QCocoaMenuLoader) *)menuLoader;
+- (void)setReflectionDelegate:(NSObject *)oldDelegate;
+@end
+#endif
diff --git a/src/gui/kernel/qcocoamenuloader_mac.mm b/src/gui/kernel/qcocoamenuloader_mac.mm
new file mode 100644
index 0000000000..7ecb765b3c
--- /dev/null
+++ b/src/gui/kernel/qcocoamenuloader_mac.mm
@@ -0,0 +1,215 @@
+/****************************************************************************
+ **
+ ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+ **
+ ** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+ **
+ ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
+ ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ **
+ ****************************************************************************/
+
+#include "qmacdefines_mac.h"
+#ifdef QT_MAC_USE_COCOA
+#include <qaction.h>
+#include <qcoreapplication.h>
+#include <private/qcocoamenuloader_mac_p.h>
+#include <private/qt_mac_p.h>
+#include <qmenubar.h>
+
+QT_FORWARD_DECLARE_CLASS(QCFString)
+QT_FORWARD_DECLARE_CLASS(QString)
+
+QT_USE_NAMESPACE
+
+@implementation QT_MANGLE_NAMESPACE(QCocoaMenuLoader)
+
+- (void)awakeFromNib
+{
+ // Get the names in the nib to match the app name set by Qt.
+ NSString *appName = reinterpret_cast<const NSString*>(QCFString::toCFStringRef(qAppName()));
+ [quitItem setTitle:[[quitItem title] stringByReplacingOccurrencesOfString:@"NewApplication"
+ withString:appName]];
+ [hideItem setTitle:[[hideItem title] stringByReplacingOccurrencesOfString:@"NewApplication"
+ withString:appName]];
+ [aboutItem setTitle:[[aboutItem title] stringByReplacingOccurrencesOfString:@"NewApplication"
+ withString:appName]];
+ [appName release];
+ // Disable the items that don't do anything. If someone associates a QAction with them
+ // They should get synced back in.
+ [preferencesItem setEnabled:NO];
+ [preferencesItem setHidden:YES];
+ [aboutItem setEnabled:NO];
+ [aboutItem setHidden:YES];
+}
+
+- (void)ensureAppMenuInMenu:(NSMenu *)menu
+{
+ NSMenu *mainMenu = [NSApp mainMenu];
+ if ([NSApp mainMenu] == menu)
+ return; // nothing to do!
+#ifndef QT_NAMESPACE
+ Q_ASSERT(mainMenu);
+#endif
+ // Grab the app menu out of the current menu.
+ int numItems = [mainMenu numberOfItems];
+ NSMenuItem *oldAppMenuItem = 0;
+ for (int i = 0; i < numItems; ++i) {
+ NSMenuItem *item = [mainMenu itemAtIndex:i];
+ if ([item submenu] == appMenu) {
+ oldAppMenuItem = item;
+ [oldAppMenuItem retain];
+ [mainMenu removeItemAtIndex:i];
+ break;
+ }
+ }
+
+ if (oldAppMenuItem) {
+ [oldAppMenuItem setSubmenu:nil];
+ [oldAppMenuItem release];
+ NSMenuItem *appMenuItem = [[NSMenuItem alloc] initWithTitle:@"Apple"
+ action:nil keyEquivalent:@""];
+ [appMenuItem setSubmenu:appMenu];
+ [menu insertItem:appMenuItem atIndex:0];
+ }
+}
+
+- (void)dealloc
+{
+ [lastAppSpecificItem release];
+ [theMenu release];
+ [appMenu release];
+ [super dealloc];
+}
+
+- (NSMenu *)menu
+{
+ return [[theMenu retain] autorelease];
+}
+
+- (NSMenu *)applicationMenu
+{
+ return [[appMenu retain] autorelease];
+}
+
+- (NSMenuItem *)quitMenuItem
+{
+ return [[quitItem retain] autorelease];
+}
+
+- (NSMenuItem *)preferencesMenuItem
+{
+ return [[preferencesItem retain] autorelease];
+}
+
+- (NSMenuItem *)aboutMenuItem
+{
+ return [[aboutItem retain] autorelease];
+}
+
+- (NSMenuItem *)aboutQtMenuItem
+{
+ return [[aboutQtItem retain] autorelease];
+}
+
+- (NSMenuItem *)hideMenuItem;
+{
+ return [[hideItem retain] autorelease];
+}
+
+- (NSMenuItem *)appSpecificMenuItem;
+{
+ // Create an App-Specific menu item, insert it into the menu and return
+ // it as an autorelease item.
+ NSMenuItem *item = [[NSMenuItem alloc] init];
+
+ NSInteger location;
+ if (lastAppSpecificItem == nil) {
+ location = [appMenu indexOfItem:aboutQtItem];
+ } else {
+ location = [appMenu indexOfItem:lastAppSpecificItem];
+ [lastAppSpecificItem release];
+ }
+ lastAppSpecificItem = item; // Keep track of this for later (i.e., don't release it)
+ [appMenu insertItem:item atIndex:location + 1];
+
+ return [[item retain] autorelease];
+}
+
+- (BOOL) acceptsFirstResponder
+{
+ return YES;
+}
+
+- (void)terminate:(id)sender
+{
+ [NSApp terminate:sender];
+}
+
+- (void)orderFrontStandardAboutPanel:(id)sender
+{
+ [NSApp orderFrontStandardAboutPanel:sender];
+}
+
+- (void)hideOtherApplications:(id)sender
+{
+ [NSApp hideOtherApplications:sender];
+}
+
+- (void)unhideAllApplications:(id)sender
+{
+ [NSApp unhideAllApplications:sender];
+}
+
+- (void)hide:(id)sender
+{
+ [NSApp hide:sender];
+}
+
+- (IBAction)qtDispatcherToQAction:(id)sender
+{
+ NSMenuItem *item = static_cast<NSMenuItem *>(sender);
+ if (QAction *action = reinterpret_cast<QAction *>([item tag])) {
+ action->trigger();
+ } else if (item == quitItem) {
+ // We got here because someone was once the quitItem, but it has been
+ // abandoned (e.g., the menubar was deleted). In the meantime, just do
+ // normal QApplication::quit().
+ qApp->quit();
+ }
+}
+@end
+#endif // QT_MAC_USE_COCOA
diff --git a/src/gui/kernel/qcocoamenuloader_mac_p.h b/src/gui/kernel/qcocoamenuloader_mac_p.h
new file mode 100644
index 0000000000..cc7338cc3d
--- /dev/null
+++ b/src/gui/kernel/qcocoamenuloader_mac_p.h
@@ -0,0 +1,90 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QCOCOAMENULOADER_P_H
+#define QCOCOAMENULOADER_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of qapplication_*.cpp, qwidget*.cpp and qfiledialog.cpp. This header
+// file may change from version to version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qmacdefines_mac.h"
+#ifdef QT_MAC_USE_COCOA
+#import <Cocoa/Cocoa.h>
+
+@interface QT_MANGLE_NAMESPACE(QCocoaMenuLoader) : NSResponder
+{
+ IBOutlet NSMenu *theMenu;
+ IBOutlet NSMenu *appMenu;
+ IBOutlet NSMenuItem *quitItem;
+ IBOutlet NSMenuItem *preferencesItem;
+ IBOutlet NSMenuItem *aboutItem;
+ IBOutlet NSMenuItem *aboutQtItem;
+ IBOutlet NSMenuItem *hideItem;
+ NSMenuItem *lastAppSpecificItem;
+
+}
+- (void)ensureAppMenuInMenu:(NSMenu *)menu;
+- (NSMenu *)applicationMenu;
+- (NSMenu *)menu;
+- (NSMenuItem *)quitMenuItem;
+- (NSMenuItem *)preferencesMenuItem;
+- (NSMenuItem *)aboutMenuItem;
+- (NSMenuItem *)aboutQtMenuItem;
+- (NSMenuItem *)hideMenuItem;
+- (NSMenuItem *)appSpecificMenuItem;
+- (IBAction)terminate:(id)sender;
+- (IBAction)orderFrontStandardAboutPanel:(id)sender;
+- (IBAction)hideOtherApplications:(id)sender;
+- (IBAction)unhideAllApplications:(id)sender;
+- (IBAction)hide:(id)sender;
+- (IBAction)qtDispatcherToQAction:(id)sender;
+@end
+
+#endif // QT_MAC_USE_COCOA
+#endif // QCOCOAMENULOADER_P_H
diff --git a/src/gui/kernel/qcocoapanel_mac.mm b/src/gui/kernel/qcocoapanel_mac.mm
new file mode 100644
index 0000000000..6aeee34400
--- /dev/null
+++ b/src/gui/kernel/qcocoapanel_mac.mm
@@ -0,0 +1,79 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#import <private/qcocoapanel_mac_p.h>
+#ifdef QT_MAC_USE_COCOA
+#import <private/qt_cocoa_helpers_mac_p.h>
+#import <private/qcocoawindow_mac_p.h>
+#import <private/qcocoawindowcustomthemeframe_mac_p.h>
+
+#include <QtGui/QWidget>
+
+QT_USE_NAMESPACE
+
+@implementation QT_MANGLE_NAMESPACE(QCocoaPanel)
+
+- (BOOL)canBecomeKeyWindow
+{
+ QWidget *widget = [self QT_MANGLE_NAMESPACE(qt_qwidget)];
+
+ bool isToolTip = (widget->windowType() == Qt::ToolTip);
+ bool isPopup = (widget->windowType() == Qt::Popup);
+ return !(isPopup || isToolTip);
+}
+
+- (void)sendEvent:(NSEvent *)event
+{
+ [self retain];
+ [super sendEvent:event];
+ qt_mac_dispatchNCMouseMessage(self, event, [self QT_MANGLE_NAMESPACE(qt_qwidget)], leftButtonIsRightButton);
+ [self release];
+}
+
++ (Class)frameViewClassForStyleMask:(NSUInteger)styleMask
+{
+ if (styleMask & QtMacCustomizeWindow)
+ return [QCocoaWindowCustomThemeFrame class];
+ return [super frameViewClassForStyleMask:styleMask];
+}
+
+@end
+#endif
diff --git a/src/gui/kernel/qcocoapanel_mac_p.h b/src/gui/kernel/qcocoapanel_mac_p.h
new file mode 100644
index 0000000000..0d1f0083aa
--- /dev/null
+++ b/src/gui/kernel/qcocoapanel_mac_p.h
@@ -0,0 +1,65 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//
+// 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 "qmacdefines_mac.h"
+#ifdef QT_MAC_USE_COCOA
+#import <Cocoa/Cocoa.h>
+
+
+@interface QT_MANGLE_NAMESPACE(QCocoaPanel) : NSPanel {
+ bool leftButtonIsRightButton;
+}
+
++ (Class)frameViewClassForStyleMask:(NSUInteger)styleMask;
+
+@end
+#endif
diff --git a/src/gui/kernel/qcocoaview_mac.mm b/src/gui/kernel/qcocoaview_mac.mm
new file mode 100644
index 0000000000..19367d1fc8
--- /dev/null
+++ b/src/gui/kernel/qcocoaview_mac.mm
@@ -0,0 +1,1254 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#import <private/qcocoaview_mac_p.h>
+#ifdef QT_MAC_USE_COCOA
+
+#include <private/qwidget_p.h>
+#include <private/qt_mac_p.h>
+#include <private/qapplication_p.h>
+#include <private/qabstractscrollarea_p.h>
+#include <private/qt_cocoa_helpers_mac_p.h>
+#include <private/qdnd_p.h>
+#include <private/qmacinputcontext_p.h>
+
+#include <qscrollarea.h>
+#include <qhash.h>
+#include <qtextformat.h>
+#include <qpaintengine.h>
+#include <QUrl>
+#include <QAccessible>
+#include <QFileInfo>
+#include <QFile>
+
+#include <qdebug.h>
+
+
+QT_BEGIN_NAMESPACE
+
+Q_GLOBAL_STATIC(DnDParams, qMacDnDParams);
+
+extern void qt_mac_update_cursor_at_global_pos(const QPoint &globalPos); // qcursor_mac.mm
+extern bool qt_sendSpontaneousEvent(QObject *, QEvent *); // qapplication.cpp
+extern OSViewRef qt_mac_nativeview_for(const QWidget *w); // qwidget_mac.mm
+extern const QStringList& qEnabledDraggedTypes(); // qmime_mac.cpp
+extern QPointer<QWidget> qt_mouseover; //qapplication_mac.mm
+
+Qt::MouseButton cocoaButton2QtButton(NSInteger buttonNum)
+{
+ if (buttonNum == 0)
+ return Qt::LeftButton;
+ if (buttonNum == 1)
+ return Qt::RightButton;
+ if (buttonNum == 2)
+ return Qt::MidButton;
+ if (buttonNum == 3)
+ return Qt::XButton1;
+ if (buttonNum == 4)
+ return Qt::XButton2;
+ return Qt::NoButton;
+}
+
+struct dndenum_mapper
+{
+ NSDragOperation mac_code;
+ Qt::DropAction qt_code;
+ bool Qt2Mac;
+};
+
+static dndenum_mapper dnd_enums[] = {
+ { NSDragOperationLink, Qt::LinkAction, true },
+ { NSDragOperationMove, Qt::MoveAction, true },
+ { NSDragOperationCopy, Qt::CopyAction, true },
+ { NSDragOperationGeneric, Qt::CopyAction, false },
+ { NSDragOperationEvery, Qt::ActionMask, false },
+ { NSDragOperationNone, Qt::IgnoreAction, false }
+};
+
+static NSDragOperation qt_mac_mapDropAction(Qt::DropAction action)
+{
+ for (int i=0; dnd_enums[i].qt_code; i++) {
+ if (dnd_enums[i].Qt2Mac && (action & dnd_enums[i].qt_code)) {
+ return dnd_enums[i].mac_code;
+ }
+ }
+ return NSDragOperationNone;
+}
+
+static NSDragOperation qt_mac_mapDropActions(Qt::DropActions actions)
+{
+ NSDragOperation nsActions = NSDragOperationNone;
+ for (int i=0; dnd_enums[i].qt_code; i++) {
+ if (dnd_enums[i].Qt2Mac && (actions & dnd_enums[i].qt_code))
+ nsActions |= dnd_enums[i].mac_code;
+ }
+ return nsActions;
+}
+
+static Qt::DropAction qt_mac_mapNSDragOperation(NSDragOperation nsActions)
+{
+ Qt::DropAction action = Qt::IgnoreAction;
+ for (int i=0; dnd_enums[i].mac_code; i++) {
+ if (nsActions & dnd_enums[i].mac_code)
+ return dnd_enums[i].qt_code;
+ }
+ return action;
+}
+
+static Qt::DropActions qt_mac_mapNSDragOperations(NSDragOperation nsActions)
+{
+ Qt::DropActions actions = Qt::IgnoreAction;
+ for (int i=0; dnd_enums[i].mac_code; i++) {
+ if (nsActions & dnd_enums[i].mac_code)
+ actions |= dnd_enums[i].qt_code;
+ }
+ return actions;
+}
+
+static QColor colorFrom(NSColor *color)
+{
+ QColor qtColor;
+ NSString *colorSpace = [color colorSpaceName];
+ if (colorSpace == NSDeviceCMYKColorSpace) {
+ CGFloat cyan, magenta, yellow, black, alpha;
+ [color getCyan:&cyan magenta:&magenta yellow:&yellow black:&black alpha:&alpha];
+ qtColor.setCmykF(cyan, magenta, yellow, black, alpha);
+ } else {
+ NSColor *tmpColor;
+ tmpColor = [color colorUsingColorSpaceName:NSDeviceRGBColorSpace];
+ CGFloat red, green, blue, alpha;
+ [tmpColor getRed:&red green:&green blue:&blue alpha:&alpha];
+ qtColor.setRgbF(red, green, blue, alpha);
+ }
+ return qtColor;
+}
+
+QT_END_NAMESPACE
+
+QT_FORWARD_DECLARE_CLASS(QMacCocoaAutoReleasePool)
+QT_FORWARD_DECLARE_CLASS(QCFString)
+QT_FORWARD_DECLARE_CLASS(QDragManager)
+QT_FORWARD_DECLARE_CLASS(QMimeData)
+QT_FORWARD_DECLARE_CLASS(QPoint)
+QT_FORWARD_DECLARE_CLASS(QApplication)
+QT_FORWARD_DECLARE_CLASS(QApplicationPrivate)
+QT_FORWARD_DECLARE_CLASS(QDragEnterEvent)
+QT_FORWARD_DECLARE_CLASS(QDragMoveEvent)
+QT_FORWARD_DECLARE_CLASS(QStringList)
+QT_FORWARD_DECLARE_CLASS(QString)
+QT_FORWARD_DECLARE_CLASS(QRect)
+QT_FORWARD_DECLARE_CLASS(QRegion)
+QT_FORWARD_DECLARE_CLASS(QAbstractScrollArea)
+QT_FORWARD_DECLARE_CLASS(QAbstractScrollAreaPrivate)
+QT_FORWARD_DECLARE_CLASS(QPaintEvent)
+QT_FORWARD_DECLARE_CLASS(QPainter)
+QT_FORWARD_DECLARE_CLASS(QHoverEvent)
+QT_USE_NAMESPACE
+extern "C" {
+ extern NSString *NSTextInputReplacementRangeAttributeName;
+}
+
+
+@implementation QT_MANGLE_NAMESPACE(QCocoaView)
+
+- (id)initWithQWidget:(QWidget *)widget widgetPrivate:(QWidgetPrivate *)widgetprivate
+{
+ self = [super init];
+ if (self) {
+ [self finishInitWithQWidget:widget widgetPrivate:widgetprivate];
+ }
+ composing = false;
+ sendKeyEvents = true;
+ [self setHidden:YES];
+ return self;
+}
+
+- (void) finishInitWithQWidget:(QWidget *)widget widgetPrivate:(QWidgetPrivate *)widgetprivate
+{
+ qwidget = widget;
+ qwidgetprivate = widgetprivate;
+ [[NSNotificationCenter defaultCenter] addObserver:self
+ selector:@selector(frameDidChange:)
+ name:@"NSViewFrameDidChangeNotification"
+ object:self];
+}
+
+-(void)registerDragTypes:(bool)accept
+{
+ QMacCocoaAutoReleasePool pool;
+ if (accept) {
+ const NSString* mimeTypeGeneric = @"com.trolltech.qt.MimeTypeName";
+ NSMutableArray *supportedTypes = [NSMutableArray arrayWithObjects:NSColorPboardType,
+ NSFilenamesPboardType, NSStringPboardType,
+ NSFilenamesPboardType, NSPostScriptPboardType, NSTIFFPboardType,
+ NSRTFPboardType, NSTabularTextPboardType, NSFontPboardType,
+ NSRulerPboardType, NSFileContentsPboardType, NSColorPboardType,
+ NSRTFDPboardType, NSHTMLPboardType, NSPICTPboardType,
+ NSURLPboardType, NSPDFPboardType, NSVCardPboardType,
+ NSFilesPromisePboardType, NSInkTextPboardType,
+ NSMultipleTextSelectionPboardType, mimeTypeGeneric, nil];
+ // Add custom types supported by the application.
+ const QStringList& customTypes = qEnabledDraggedTypes();
+ for (int i = 0; i < customTypes.size(); i++) {
+ [supportedTypes addObject:reinterpret_cast<const NSString *>(QCFString::toCFStringRef(customTypes[i]))];
+ }
+ [self registerForDraggedTypes:supportedTypes];
+ } else {
+ [self unregisterDraggedTypes];
+ }
+}
+
+- (void)removeDropData
+{
+ if (dropData) {
+ delete dropData;
+ dropData = 0;
+ }
+}
+
+- (void)addDropData:(id <NSDraggingInfo>)sender
+{
+ [self removeDropData];
+ CFStringRef dropPasteboard = (CFStringRef) [[sender draggingPasteboard] name];
+ dropData = new QCocoaDropData(dropPasteboard);
+}
+
+- (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender
+{
+ [self addDropData:sender];
+ QMimeData *mimeData = dropData;
+ if (QDragManager::self()->source())
+ mimeData = QDragManager::self()->dragPrivate()->data;
+ NSPoint windowPoint = [sender draggingLocation];
+ NSPoint globalPoint = [[sender draggingDestinationWindow] convertBaseToScreen:windowPoint];
+ NSPoint localPoint = [self convertPoint:windowPoint fromView:nil];
+ QPoint posDrag(localPoint.x, localPoint.y);
+ NSDragOperation nsActions = [sender draggingSourceOperationMask];
+ Qt::DropActions qtAllowed = qt_mac_mapNSDragOperations(nsActions);
+ // send the drag enter event to the widget.
+ QDragEnterEvent qDEEvent(posDrag, qtAllowed, mimeData,
+ QApplication::mouseButtons(), QApplication::keyboardModifiers());
+ QApplication::sendEvent(qwidget, &qDEEvent);
+ if (!qDEEvent.isAccepted()) {
+ // widget is not interested in this drag, so ignore this drop data.
+ [self removeDropData];
+ return NSDragOperationNone;
+ } else {
+ // send a drag move event immediately after a drag enter event (as per documentation).
+ QDragMoveEvent qDMEvent(posDrag, qtAllowed, mimeData,
+ QApplication::mouseButtons(), QApplication::keyboardModifiers());
+ qDMEvent.setDropAction(qDEEvent.dropAction());
+ qDMEvent.accept(); // accept by default, since enter event was accepted.
+ QApplication::sendEvent(qwidget, &qDMEvent);
+ if (!qDMEvent.isAccepted() || qDMEvent.dropAction() == Qt::IgnoreAction) {
+ // since we accepted the drag enter event, the widget expects
+ // future drage move events.
+ // ### check if we need to treat this like the drag enter event.
+ nsActions = QT_PREPEND_NAMESPACE(qt_mac_mapDropAction)(qDEEvent.dropAction());
+ } else {
+ nsActions = QT_PREPEND_NAMESPACE(qt_mac_mapDropAction)(qDMEvent.dropAction());
+ }
+ QT_PREPEND_NAMESPACE(qt_mac_copy_answer_rect)(qDMEvent);
+ return nsActions;
+ }
+ }
+
+- (NSDragOperation)draggingUpdated:(id < NSDraggingInfo >)sender
+{
+ // drag enter event was rejected, so ignore the move event.
+ if (dropData == 0)
+ return NSDragOperationNone;
+ // return last value, if we are still in the answerRect.
+ NSPoint windowPoint = [sender draggingLocation];
+ NSPoint globalPoint = [[sender draggingDestinationWindow] convertBaseToScreen:windowPoint];
+ NSPoint localPoint = [self convertPoint:windowPoint fromView:nil];
+ QPoint posDrag(localPoint.x, localPoint.y);
+ if (qt_mac_mouse_inside_answer_rect(posDrag))
+ return QT_PREPEND_NAMESPACE(qt_mac_mapDropActions)(QT_PREPEND_NAMESPACE(qt_mac_dnd_answer_rec.lastAction));
+ // send drag move event to the widget
+ NSDragOperation nsActions = [sender draggingSourceOperationMask];
+ Qt::DropActions qtAllowed = QT_PREPEND_NAMESPACE(qt_mac_mapNSDragOperations)(nsActions);
+ QMimeData *mimeData = dropData;
+ if (QDragManager::self()->source())
+ mimeData = QDragManager::self()->dragPrivate()->data;
+ QDragMoveEvent qDMEvent(posDrag, qtAllowed, mimeData,
+ QApplication::mouseButtons(), QApplication::keyboardModifiers());
+ qDMEvent.setDropAction(QT_PREPEND_NAMESPACE(qt_mac_dnd_answer_rec).lastAction);
+ qDMEvent.accept();
+ QApplication::sendEvent(qwidget, &qDMEvent);
+ qt_mac_copy_answer_rect(qDMEvent);
+
+ NSDragOperation operation = qt_mac_mapDropAction(qDMEvent.dropAction());
+ if (!qDMEvent.isAccepted() || qDMEvent.dropAction() == Qt::IgnoreAction) {
+ // ignore this event (we will still receive further notifications)
+ operation = NSDragOperationNone;
+ }
+ return operation;
+}
+
+- (void)draggingExited:(id < NSDraggingInfo >)sender
+{
+ Q_UNUSED(sender)
+ // drag enter event was rejected, so ignore the move event.
+ if (dropData) {
+ QDragLeaveEvent de;
+ QApplication::sendEvent(qwidget, &de);
+ [self removeDropData];
+ }
+
+}
+
+- (BOOL)performDragOperation:(id <NSDraggingInfo>)sender
+{
+ [self addDropData:sender];
+
+ NSPoint windowPoint = [sender draggingLocation];
+ NSPoint globalPoint = [[sender draggingDestinationWindow] convertBaseToScreen:windowPoint];
+ NSPoint localPoint = [self convertPoint:windowPoint fromView:nil];
+ QPoint posDrop(localPoint.x, localPoint.y);
+
+ NSDragOperation nsActions = [sender draggingSourceOperationMask];
+ Qt::DropActions qtAllowed = qt_mac_mapNSDragOperations(nsActions);
+ QMimeData *mimeData = dropData;
+ if (QDragManager::self()->source())
+ mimeData = QDragManager::self()->dragPrivate()->data;
+ // send the drop event to the widget.
+ QDropEvent de(posDrop, qtAllowed, mimeData,
+ QApplication::mouseButtons(), QApplication::keyboardModifiers());
+ if (QDragManager::self()->object)
+ QDragManager::self()->dragPrivate()->target = qwidget;
+ QApplication::sendEvent(qwidget, &de);
+ if (!de.isAccepted())
+ return NO;
+ else
+ return YES;
+}
+
+- (void)dealloc
+{
+ [[NSNotificationCenter defaultCenter] removeObserver:self];
+ [super dealloc];
+}
+
+- (BOOL)isOpaque;
+{
+ return qwidgetprivate->isOpaque;
+}
+
+- (BOOL)isFlipped;
+{
+ return YES;
+}
+
+- (BOOL) preservesContentDuringLiveResize;
+{
+ return qwidget->testAttribute(Qt::WA_StaticContents);
+}
+
+- (void) setFrameSize:(NSSize)newSize
+{
+ [super setFrameSize:newSize];
+
+ // A change in size has required the view to be invalidated.
+ if ([self inLiveResize])
+ {
+ NSRect rects[4];
+ NSInteger count;
+ [self getRectsExposedDuringLiveResize:rects count:&count];
+ while (count-- > 0)
+ {
+ [self setNeedsDisplayInRect:rects[count]];
+ }
+ }
+ else
+ {
+ [self setNeedsDisplay:YES];
+ }
+}
+
+- (void)drawRect:(NSRect)aRect
+{
+ CGContextRef cg = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];
+ qwidgetprivate->hd = cg;
+ CGContextSaveGState(cg);
+
+ if (qwidget->isVisible() && qwidget->updatesEnabled()) { //process the actual paint event.
+ if (qwidget->testAttribute(Qt::WA_WState_InPaintEvent))
+ qWarning("QWidget::repaint: Recursive repaint detected");
+
+ const QRect qrect = QRect(aRect.origin.x, aRect.origin.y, aRect.size.width, aRect.size.height);
+ QRegion qrgn(qrect);
+
+ if (!qwidget->isWindow() && !qobject_cast<QAbstractScrollArea *>(qwidget->parent())) {
+ const QRegion &parentMask = qwidget->window()->mask();
+ if (!parentMask.isEmpty()) {
+ const QPoint mappedPoint = qwidget->mapTo(qwidget->window(), qrect.topLeft());
+ qrgn.translate(mappedPoint);
+ qrgn &= parentMask;
+ qrgn.translate(-mappedPoint.x(), -mappedPoint.y());
+ }
+ }
+
+ QPoint redirectionOffset(0, 0);
+ //setup the context
+ qwidget->setAttribute(Qt::WA_WState_InPaintEvent);
+ QPaintEngine *engine = qwidget->paintEngine();
+ if (engine)
+ engine->setSystemClip(qrgn);
+ if (qwidgetprivate->extra && qwidgetprivate->extra->hasMask) {
+ CGRect widgetRect = CGRectMake(0, 0, qwidget->width(), qwidget->height());
+ CGContextTranslateCTM (cg, 0, widgetRect.size.height);
+ CGContextScaleCTM(cg, 1, -1);
+ if (qwidget->isWindow())
+ CGContextClearRect(cg, widgetRect);
+ CGContextClipToMask(cg, widgetRect, qwidgetprivate->extra->imageMask);
+ CGContextScaleCTM(cg, 1, -1);
+ CGContextTranslateCTM (cg, 0, -widgetRect.size.height);
+ }
+
+ if (qwidget->isWindow() && !qwidgetprivate->isOpaque
+ && !qwidget->testAttribute(Qt::WA_MacBrushedMetal)) {
+ CGContextClearRect(cg, NSRectToCGRect(aRect));
+ }
+
+ if (engine && !qwidget->testAttribute(Qt::WA_NoSystemBackground)
+ && (qwidget->isWindow() || qwidget->autoFillBackground())
+ || qwidget->testAttribute(Qt::WA_TintedBackground)
+ || qwidget->testAttribute(Qt::WA_StyledBackground)) {
+#ifdef DEBUG_WIDGET_PAINT
+ if(doDebug)
+ qDebug(" Handling erase for [%s::%s]", qwidget->metaObject()->className(),
+ qwidget->objectName().local8Bit().data());
+#endif
+ QPainter p(qwidget);
+ QAbstractScrollArea *scrollArea = qobject_cast<QAbstractScrollArea *>(qwidget->parent());
+ QPoint scrollAreaOffset;
+ if (scrollArea && scrollArea->viewport() == qwidget) {
+ QAbstractScrollAreaPrivate *priv
+ = static_cast<QAbstractScrollAreaPrivate *>(qt_widget_private(scrollArea));
+ scrollAreaOffset = priv->contentsOffset();
+ p.translate(-scrollAreaOffset);
+ }
+ qwidgetprivate->paintBackground(&p, qrgn, scrollAreaOffset,
+ qwidget->isWindow() ? QWidgetPrivate::DrawAsRoot : 0);
+ p.end();
+ }
+ QPaintEvent e(qrgn);
+#ifdef QT3_SUPPORT
+ e.setErased(true);
+#endif
+ qt_sendSpontaneousEvent(qwidget, &e);
+ if (!redirectionOffset.isNull())
+ QPainter::restoreRedirected(qwidget);
+#ifdef QT_RASTER_PAINTENGINE
+ if(engine && engine->type() == QPaintEngine::Raster)
+ static_cast<QRasterPaintEngine*>(engine)->flush(qwidget,
+ qrgn.boundingRect().topLeft());
+#endif
+ if (engine)
+ engine->setSystemClip(QRegion());
+ qwidget->setAttribute(Qt::WA_WState_InPaintEvent, false);
+ if(!qwidget->testAttribute(Qt::WA_PaintOutsidePaintEvent) && qwidget->paintingActive())
+ qWarning("QWidget: It is dangerous to leave painters active on a"
+ " widget outside of the PaintEvent");
+ }
+ qwidgetprivate->hd = 0;
+ CGContextRestoreGState(cg);
+}
+
+- (BOOL)acceptsFirstMouse:(NSEvent *)theEvent
+{
+ Q_UNUSED(theEvent);
+ return !qwidget->testAttribute(Qt::WA_MacNoClickThrough);
+}
+
+- (void)updateTrackingAreas
+{
+ QMacCocoaAutoReleasePool pool;
+ if (NSArray *trackingArray = [self trackingAreas]) {
+ NSUInteger size = [trackingArray count];
+ for (NSUInteger i = 0; i < size; ++i) {
+ NSTrackingArea *t = [trackingArray objectAtIndex:i];
+ [self removeTrackingArea:t];
+ }
+ }
+ NSUInteger trackingOptions = NSTrackingMouseEnteredAndExited | NSTrackingActiveInActiveApp
+ | NSTrackingInVisibleRect;
+ if (qwidget->hasMouseTracking() || !qwidgetprivate->toolTip.isEmpty()
+ || qwidget->testAttribute(Qt::WA_Hover))
+ trackingOptions |= NSTrackingMouseMoved;
+ NSTrackingArea *ta = [[NSTrackingArea alloc] initWithRect:NSMakeRect(0, 0,
+ qwidget->width(),
+ qwidget->height())
+ options:trackingOptions
+ owner:self
+ userInfo:nil];
+ [self addTrackingArea:ta];
+ [ta release];
+}
+
+- (void)mouseEntered:(NSEvent *)event
+{
+ QEvent enterEvent(QEvent::Enter);
+ NSPoint windowPoint = [event locationInWindow];
+ NSPoint globalPoint = [[event window] convertBaseToScreen:windowPoint];
+ NSPoint viewPoint = [self convertPoint:windowPoint fromView:nil];
+ if (!qAppInstance()->activeModalWidget() || QApplicationPrivate::tryModalHelper(qwidget, 0)) {
+ QApplication::sendEvent(qwidget, &enterEvent);
+ qt_mouseover = qwidget;
+
+ // Update cursor and dispatch hover events.
+ qt_mac_update_cursor_at_global_pos(flipPoint(globalPoint).toPoint());
+ if (qwidget->testAttribute(Qt::WA_Hover) &&
+ (!qAppInstance()->activePopupWidget() || qAppInstance()->activePopupWidget() == qwidget->window())) {
+ QHoverEvent he(QEvent::HoverEnter, QPoint(viewPoint.x, viewPoint.y), QPoint(-1, -1));
+ QApplicationPrivate::instance()->notify_helper(qwidget, &he);
+ }
+ }
+}
+
+- (void)mouseExited:(NSEvent *)event
+{
+ QEvent leaveEvent(QEvent::Leave);
+ NSPoint globalPoint = [[event window] convertBaseToScreen:[event locationInWindow]];
+ if (!qAppInstance()->activeModalWidget() || QApplicationPrivate::tryModalHelper(qwidget, 0)) {
+ QApplication::sendEvent(qwidget, &leaveEvent);
+
+ // ### Think about if it is necessary to update the cursor, should only be for a few cases.
+ qt_mac_update_cursor_at_global_pos(flipPoint(globalPoint).toPoint());
+ if (qwidget->testAttribute(Qt::WA_Hover)
+ && (!qAppInstance()->activePopupWidget() || qAppInstance()->activePopupWidget() == qwidget->window())) {
+ QHoverEvent he(QEvent::HoverLeave, QPoint(-1, -1),
+ qwidget->mapFromGlobal(QApplicationPrivate::instance()->hoverGlobalPos));
+ QApplicationPrivate::instance()->notify_helper(qwidget, &he);
+ }
+ }
+}
+
+- (void)flagsChanged:(NSEvent *)theEvent
+{
+ QWidget *widgetToGetKey = qwidget;
+
+ QWidget *popup = qAppInstance()->activePopupWidget();
+ if (popup && popup != qwidget->window())
+ widgetToGetKey = popup->focusWidget() ? popup->focusWidget() : popup;
+ qt_dispatchModifiersChanged(theEvent, widgetToGetKey);
+ [super flagsChanged:theEvent];
+}
+
+- (void)mouseMoved:(NSEvent *)theEvent
+{
+ qt_mac_handleMouseEvent(self, theEvent, QEvent::MouseMove, Qt::NoButton);
+}
+
+- (NSView *)viewUnderTransparentForMouseView:(NSView *)mouseView widget:(QWidget *)widgetToGetMouse
+ withWindowPoint:(NSPoint)windowPoint
+{
+ NSMutableArray *viewsToLookAt = [NSMutableArray arrayWithCapacity:5];
+ [viewsToLookAt addObject:mouseView];
+ QWidget *parentWidget = widgetToGetMouse->parentWidget();
+ while (parentWidget) {
+ [viewsToLookAt addObject:qt_mac_nativeview_for(parentWidget)];
+ parentWidget = parentWidget->parentWidget();
+ }
+
+ // Now walk through the subviews of each view and determine which subview should
+ // get the event. We look through all the subviews at a given level with
+ // the assumption that the last item to be found the candidate has a higher z-order.
+ // Unfortunately, fast enumeration doesn't go backwards in 10.5, so assume go fast
+ // forward is quicker than the slow normal way backwards.
+ NSView *candidateView = nil;
+ for (NSView *lookView in viewsToLookAt) {
+ NSPoint tmpPoint = [lookView convertPoint:windowPoint fromView:nil];
+ for (NSView *view in [lookView subviews]) {
+ if (view == mouseView)
+ continue;
+ NSRect frameRect = [view frame];
+ if (NSMouseInRect(tmpPoint, [view frame], [view isFlipped]))
+ candidateView = view;
+ }
+ if (candidateView)
+ break;
+ }
+
+
+ if (candidateView != nil) {
+ // Now that we've got a candidate, we have to dig into it's tree and see where it is.
+ NSView *lowerView = nil;
+ NSView *viewForDescent = candidateView;
+ while (viewForDescent) {
+ NSPoint tmpPoint = [viewForDescent convertPoint:windowPoint fromView:nil];
+ // Apply same rule as above wrt z-order.
+ for (NSView *view in [viewForDescent subviews]) {
+ if (NSMouseInRect(tmpPoint, [view frame], [view isFlipped]))
+ lowerView = view;
+ }
+ if (!lowerView) // Low as we can be at this point.
+ candidateView = viewForDescent;
+
+ // Try to go deeper, will also exit out of the loop, if we found the point.
+ viewForDescent = lowerView;
+ lowerView = nil;
+ }
+ }
+ // I am transparent, so I can't be a candidate.
+ if (candidateView == mouseView)
+ candidateView = nil;
+ return candidateView;
+}
+
+- (void)mouseDown:(NSEvent *)theEvent
+{
+ qt_mac_handleMouseEvent(self, theEvent, QEvent::MouseButtonPress, Qt::LeftButton);
+ // Don't call super here. This prevents us from getting the mouseUp event,
+ // which we need to send even if the mouseDown event was not accepted.
+ // (this is standard Qt behavior.)
+}
+
+
+- (void)mouseUp:(NSEvent *)theEvent
+{
+ bool mouseOK = qt_mac_handleMouseEvent(self, theEvent, QEvent::MouseButtonRelease, Qt::LeftButton);
+
+ if (!mouseOK)
+ [super mouseUp:theEvent];
+}
+
+- (void)rightMouseDown:(NSEvent *)theEvent
+{
+ bool mouseOK = qt_mac_handleMouseEvent(self, theEvent, QEvent::MouseButtonPress, Qt::RightButton);
+
+ if (!mouseOK)
+ [super rightMouseDown:theEvent];
+}
+
+- (void)rightMouseUp:(NSEvent *)theEvent
+{
+ bool mouseOK = qt_mac_handleMouseEvent(self, theEvent, QEvent::MouseButtonRelease, Qt::RightButton);
+
+ if (!mouseOK)
+ [super rightMouseUp:theEvent];
+}
+
+- (void)otherMouseDown:(NSEvent *)theEvent
+{
+ Qt::MouseButton mouseButton = cocoaButton2QtButton([theEvent buttonNumber]);
+ bool mouseOK = qt_mac_handleMouseEvent(self, theEvent, QEvent::MouseButtonPress, mouseButton);
+
+ if (!mouseOK)
+ [super otherMouseDown:theEvent];
+}
+
+- (void)otherMouseUp:(NSEvent *)theEvent
+{
+ Qt::MouseButton mouseButton = cocoaButton2QtButton([theEvent buttonNumber]);
+ bool mouseOK = qt_mac_handleMouseEvent(self, theEvent, QEvent::MouseButtonRelease, mouseButton);
+
+ if (!mouseOK)
+ [super otherMouseUp:theEvent];
+
+}
+
+- (void)mouseDragged:(NSEvent *)theEvent
+{
+ qMacDnDParams()->view = self;
+ qMacDnDParams()->theEvent = theEvent;
+ bool mouseOK = qt_mac_handleMouseEvent(self, theEvent, QEvent::MouseMove, Qt::LeftButton);
+
+ if (!mouseOK)
+ [super mouseDragged:theEvent];
+}
+
+- (void)rightMouseDragged:(NSEvent *)theEvent
+{
+ qMacDnDParams()->view = self;
+ qMacDnDParams()->theEvent = theEvent;
+ bool mouseOK = qt_mac_handleMouseEvent(self, theEvent, QEvent::MouseMove, Qt::RightButton);
+
+ if (!mouseOK)
+ [super rightMouseDragged:theEvent];
+}
+
+- (void)otherMouseDragged:(NSEvent *)theEvent
+{
+ qMacDnDParams()->view = self;
+ qMacDnDParams()->theEvent = theEvent;
+ Qt::MouseButton mouseButton = cocoaButton2QtButton([theEvent buttonNumber]);
+ bool mouseOK = qt_mac_handleMouseEvent(self, theEvent, QEvent::MouseMove, mouseButton);
+
+ if (!mouseOK)
+ [super otherMouseDragged:theEvent];
+}
+
+- (void)scrollWheel:(NSEvent *)theEvent
+{
+ // Give the Input Manager a chance to process the wheel event.
+ NSInputManager *currentIManager = [NSInputManager currentInputManager];
+ if (currentIManager && [currentIManager wantsToHandleMouseEvents]) {
+ [currentIManager handleMouseEvent:theEvent];
+ }
+
+ NSPoint windowPoint = [theEvent locationInWindow];
+ NSPoint globalPoint = [[theEvent window] convertBaseToScreen:windowPoint];
+ NSPoint localPoint = [self convertPoint:windowPoint fromView:nil];
+ QPoint qlocal = QPoint(localPoint.x, localPoint.y);
+ QPoint qglobal = QPoint(globalPoint.x, globalPoint.y);
+ Qt::MouseButton buttons = cocoaButton2QtButton([theEvent buttonNumber]);
+ bool wheelOK = false;
+ Qt::KeyboardModifiers keyMods = qt_cocoaModifiers2QtModifiers([theEvent modifierFlags]);
+
+ // Mouse wheel deltas seem to tick in at increments of 0.1. Qt widgets
+ // expect the delta to be a multiple of 120.
+ const int ScrollFactor = 10 * 120;
+ // The qMax(...) factor reduces the
+ // acceleration for large wheel deltas.
+ int deltaX = [theEvent deltaX] * ScrollFactor * qMax(0.6, 1.1 - qAbs([theEvent deltaX]));
+ int deltaY = [theEvent deltaY] * ScrollFactor * qMax(0.6, 1.1 - qAbs([theEvent deltaY]));
+ int deltaZ = [theEvent deltaZ] * ScrollFactor * qMax(0.6, 1.1 - qAbs([theEvent deltaZ]));
+
+ if (deltaX != 0) {
+ QWheelEvent qwe(qlocal, qglobal, deltaX, buttons, keyMods, Qt::Horizontal);
+ qt_sendSpontaneousEvent(qwidget, &qwe);
+ wheelOK = qwe.isAccepted();
+ if (!wheelOK && QApplicationPrivate::focus_widget
+ && QApplicationPrivate::focus_widget != qwidget) {
+ QWheelEvent qwe2(QApplicationPrivate::focus_widget->mapFromGlobal(qglobal), qglobal,
+ deltaX, buttons, keyMods, Qt::Horizontal);
+ qt_sendSpontaneousEvent(QApplicationPrivate::focus_widget, &qwe2);
+ wheelOK = qwe2.isAccepted();
+ }
+ }
+
+ if (deltaY) {
+ QWheelEvent qwe(qlocal, qglobal, deltaY, buttons, keyMods, Qt::Vertical);
+ qt_sendSpontaneousEvent(qwidget, &qwe);
+ wheelOK = qwe.isAccepted();
+ if (wheelOK && QApplicationPrivate::focus_widget
+ && QApplicationPrivate::focus_widget != qwidget) {
+ QWheelEvent qwe2(QApplicationPrivate::focus_widget->mapFromGlobal(qglobal), qglobal,
+ deltaZ, buttons, keyMods, Qt::Vertical);
+ qt_sendSpontaneousEvent(QApplicationPrivate::focus_widget, &qwe2);
+ wheelOK = qwe2.isAccepted();
+ }
+ }
+
+ if (deltaZ) {
+ // Qt doesn't explicitly support wheels with a Z component. In a misguided attempt to
+ // try to be ahead of the pack, I'm adding this extra value.
+ QWheelEvent qwe(qlocal, qglobal, deltaZ, buttons, keyMods, (Qt::Orientation)3);
+ qt_sendSpontaneousEvent(qwidget, &qwe);
+ wheelOK = qwe.isAccepted();
+ if (!wheelOK && QApplicationPrivate::focus_widget
+ && QApplicationPrivate::focus_widget != qwidget) {
+ QWheelEvent qwe2(QApplicationPrivate::focus_widget->mapFromGlobal(qglobal), qglobal,
+ deltaZ, buttons, keyMods, (Qt::Orientation)3);
+ qt_sendSpontaneousEvent(QApplicationPrivate::focus_widget, &qwe2);
+ wheelOK = qwe2.isAccepted();
+ }
+ }
+ if (!wheelOK) {
+ return [super scrollWheel:theEvent];
+ }
+}
+
+- (void)tabletProximity:(NSEvent *)tabletEvent
+{
+ qt_dispatchTabletProximityEvent(tabletEvent);
+}
+
+- (void)tabletPoint:(NSEvent *)tabletEvent
+{
+ if (!qt_mac_handleTabletEvent(self, tabletEvent))
+ [super tabletPoint:tabletEvent];
+}
+
+- (void)frameDidChange:(NSNotification *)note
+{
+ Q_UNUSED(note);
+ if (qwidget->isWindow())
+ return;
+ NSRect newFrame = [self frame];
+ QRect newGeo(newFrame.origin.x, newFrame.origin.y, newFrame.size.width, newFrame.size.height);
+ bool moved = qwidget->testAttribute(Qt::WA_Moved);
+ bool resized = qwidget->testAttribute(Qt::WA_Resized);
+ qwidget->setGeometry(newGeo);
+ qwidget->setAttribute(Qt::WA_Moved, moved);
+ qwidget->setAttribute(Qt::WA_Resized, resized);
+ qwidgetprivate->syncCocoaMask();
+}
+
+- (BOOL)isEnabled
+{
+ if (!qwidget)
+ return [super isEnabled];
+ return [super isEnabled] && qwidget->isEnabled();
+}
+
+- (void)setEnabled:(BOOL)flag
+{
+ QMacCocoaAutoReleasePool pool;
+ [super setEnabled:flag];
+ if (qwidget->isEnabled() != flag)
+ qwidget->setEnabled(flag);
+}
+
++ (Class)cellClass
+{
+ return [NSActionCell class];
+}
+
+- (BOOL)acceptsFirstResponder
+{
+ if (qwidget->isWindow())
+ return YES; // Always do it, so that windows can accept key press events.
+ return qwidget->focusPolicy() != Qt::NoFocus;
+}
+
+- (NSDragOperation)draggingSourceOperationMaskForLocal:(BOOL)isLocal
+{
+ Q_UNUSED(isLocal);
+ return supportedActions;
+}
+
+- (void)setSupportedActions:(NSDragOperation)actions
+{
+ supportedActions = actions;
+}
+
+- (void)draggedImage:(NSImage *)anImage endedAt:(NSPoint)aPoint operation:(NSDragOperation)operation
+{
+ Q_UNUSED(anImage);
+ Q_UNUSED(aPoint);
+ qMacDnDParams()->performedAction = operation;
+}
+
+- (QWidget *)qt_qwidget
+{
+ return qwidget;
+}
+
+- (BOOL)qt_leftButtonIsRightButton
+{
+ return leftButtonIsRightButton;
+}
+
+- (void)qt_setLeftButtonIsRightButton:(BOOL)isSwapped
+{
+ leftButtonIsRightButton = isSwapped;
+}
+
++ (DnDParams*)currentMouseEvent
+{
+ return qMacDnDParams();
+}
+
+- (void)keyDown:(NSEvent *)theEvent
+{
+ sendKeyEvents = true;
+
+ QWidget *widgetToGetKey = qwidget;
+
+ QWidget *popup = qAppInstance()->activePopupWidget();
+ bool sendToPopup = false;
+ if (popup && popup != qwidget->window()) {
+ widgetToGetKey = popup->focusWidget() ? popup->focusWidget() : popup;
+ sendToPopup = true;
+ }
+
+ if (widgetToGetKey->testAttribute(Qt::WA_InputMethodEnabled)) {
+ [qt_mac_nativeview_for(widgetToGetKey) interpretKeyEvents:[NSArray arrayWithObject: theEvent]];
+ }
+ if (sendKeyEvents && !composing) {
+ bool keyOK = qt_dispatchKeyEvent(theEvent, widgetToGetKey);
+ if (!keyOK && !sendToPopup)
+ [super keyDown:theEvent];
+ }
+}
+
+
+- (void)keyUp:(NSEvent *)theEvent
+{
+ if (sendKeyEvents) {
+ bool keyOK = qt_dispatchKeyEvent(theEvent, qwidget);
+ if (!keyOK)
+ [super keyUp:theEvent];
+ }
+}
+
+// NSTextInput Protocol implementation
+
+- (void) insertText:(id)aString
+{
+ if (composing) {
+ // Send the commit string to the widget.
+ QString commitText;
+ if ([aString isKindOfClass:[NSAttributedString class]]) {
+ commitText = QCFString::toQString(reinterpret_cast<CFStringRef>([aString string]));
+ } else {
+ commitText = QCFString::toQString(reinterpret_cast<CFStringRef>(aString));
+ };
+ composing = false;
+ sendKeyEvents = false;
+ QInputMethodEvent e;
+ e.setCommitString(commitText);
+ qt_sendSpontaneousEvent(qwidget, &e);
+ }
+}
+
+- (void) setMarkedText:(id)aString selectedRange:(NSRange)selRange
+{
+ // Generate the QInputMethodEvent with preedit string and the attributes
+ // for rendering it. The attributes handled here are 'underline',
+ // 'underline color' and 'cursor position'.
+ sendKeyEvents = false;
+ composing = true;
+ QString qtText;
+ // Cursor position is retrived from the range.
+ QList<QInputMethodEvent::Attribute> attrs;
+ attrs<<QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, selRange.location, 1, QVariant());
+ if ([aString isKindOfClass:[NSAttributedString class]]) {
+ qtText = QCFString::toQString(reinterpret_cast<CFStringRef>([aString string]));
+ composingLength = qtText.length();
+ int index = 0;
+ // Create attributes for individual sections of preedit text
+ while (index < composingLength) {
+ NSRange effectiveRange;
+ NSRange range = NSMakeRange(index, composingLength-index);
+ NSDictionary *attributes = [aString attributesAtIndex:index
+ longestEffectiveRange:&effectiveRange
+ inRange:range];
+ NSNumber *underlineStyle = [attributes objectForKey:NSUnderlineStyleAttributeName];
+ if (underlineStyle) {
+ QColor clr (Qt::black);
+ NSColor *color = [attributes objectForKey:NSUnderlineColorAttributeName];
+ if (color) {
+ clr = colorFrom(color);
+ }
+ QTextCharFormat format;
+ format.setFontUnderline(true);
+ format.setUnderlineColor(clr);
+ attrs<<QInputMethodEvent::Attribute(QInputMethodEvent::TextFormat,
+ effectiveRange.location,
+ effectiveRange.length,
+ format);
+ }
+ index = effectiveRange.location + effectiveRange.length;
+ }
+ } else {
+ // No attributes specified, take only the preedit text.
+ qtText = QCFString::toQString(reinterpret_cast<CFStringRef>(aString));
+ composingLength = qtText.length();
+ }
+ // Make sure that we have at least one text format.
+ if (attrs.size() <= 1) {
+ QTextCharFormat format;
+ format.setFontUnderline(true);
+ attrs<<QInputMethodEvent::Attribute(QInputMethodEvent::TextFormat,
+ 0, composingLength, format);
+ }
+ QInputMethodEvent e(qtText, attrs);
+ qt_sendSpontaneousEvent(qwidget, &e);
+}
+
+- (void) unmarkText
+{
+ composing = false;
+}
+
+- (BOOL) hasMarkedText
+{
+ return (composing ? YES: NO);
+}
+
+- (void) doCommandBySelector:(SEL)aSelector
+{
+ Q_UNUSED(aSelector);
+}
+
+- (BOOL)isComposing
+{
+ return composing;
+}
+
+- (NSInteger) conversationIdentifier
+{
+ // Return a unique identifier fot this ime conversation
+ return (NSInteger)self;
+}
+
+- (NSAttributedString *) attributedSubstringFromRange:(NSRange)theRange
+{
+ QString selectedText(qwidget->inputMethodQuery(Qt::ImCurrentSelection).toString());
+ if (!selectedText.isEmpty()) {
+ QCFString string(selectedText.mid(theRange.location, theRange.length));
+ const NSString *tmpString = reinterpret_cast<const NSString *>((CFStringRef)string);
+ return [[[NSAttributedString alloc] initWithString:tmpString] autorelease];
+ } else {
+ return nil;
+ }
+}
+
+- (NSRange) markedRange
+{
+ NSRange range;
+ if (composing) {
+ range.location = 0;
+ range.length = composingLength;
+ } else {
+ range.location = NSNotFound;
+ range.length = 0;
+ }
+ return range;
+}
+
+- (NSRange) selectedRange
+{
+ NSRange selRange;
+ QString selectedText(qwidget->inputMethodQuery(Qt::ImCurrentSelection).toString());
+ if (!selectedText.isEmpty()) {
+ // Consider only the selected text.
+ selRange.location = 0;
+ selRange.length = selectedText.length();
+ } else {
+ // No selected text.
+ selRange.location = NSNotFound;
+ selRange.length = 0;
+ }
+ return selRange;
+
+}
+
+- (NSRect) firstRectForCharacterRange:(NSRange)theRange
+{
+ Q_UNUSED(theRange);
+ // The returned rect is always based on the internal cursor.
+ QRect mr(qwidget->inputMethodQuery(Qt::ImMicroFocus).toRect());
+ QPoint mp(qwidget->mapToGlobal(QPoint(mr.bottomLeft())));
+ NSRect rect ;
+ rect.origin.x = mp.x();
+ rect.origin.y = flipYCoordinate(mp.y());
+ rect.size.width = mr.width();
+ rect.size.height = mr.height();
+ return rect;
+}
+
+- (NSUInteger)characterIndexForPoint:(NSPoint)thePoint
+{
+ // We dont support cursor movements using mouse while composing.
+ Q_UNUSED(thePoint);
+ return NSNotFound;
+}
+
+- (NSArray*) validAttributesForMarkedText
+{
+ if (!qwidget->testAttribute(Qt::WA_InputMethodEnabled))
+ return nil; // Not sure if that's correct, but it's saves a malloc.
+
+ // Support only underline color/style.
+ return [NSArray arrayWithObjects:NSUnderlineColorAttributeName,
+ NSUnderlineStyleAttributeName, nil];
+}
+@end
+
+QT_BEGIN_NAMESPACE
+void QMacInputContext::reset()
+{
+ QWidget *w = QInputContext::focusWidget();
+ if (w) {
+ NSView *view = qt_mac_nativeview_for(w);
+ if ([view isKindOfClass:[QT_MANGLE_NAMESPACE(QCocoaView) class]]) {
+ QMacCocoaAutoReleasePool pool;
+ QT_MANGLE_NAMESPACE(QCocoaView) *qc = static_cast<QT_MANGLE_NAMESPACE(QCocoaView) *>(view);
+ NSInputManager *currentIManager = [NSInputManager currentInputManager];
+ if (currentIManager) {
+ [currentIManager markedTextAbandoned:view];
+ [qc unmarkText];
+ }
+ }
+ }
+}
+
+bool QMacInputContext::isComposing() const
+{
+ QWidget *w = QInputContext::focusWidget();
+ if (w) {
+ NSView *view = qt_mac_nativeview_for(w);
+ if ([view isKindOfClass:[QT_MANGLE_NAMESPACE(QCocoaView) class]]) {
+ return [static_cast<QT_MANGLE_NAMESPACE(QCocoaView) *>(view) isComposing];
+ }
+ }
+ return false;
+}
+
+extern bool qt_mac_in_drag;
+void * /*NSImage */qt_mac_create_nsimage(const QPixmap &pm);
+static const int default_pm_hotx = -2;
+static const int default_pm_hoty = -16;
+static const char* default_pm[] = {
+ "13 9 3 1",
+ ". c None",
+ " c #000000",
+ "X c #FFFFFF",
+ "X X X X X X X",
+ " X X X X X X ",
+ "X ......... X",
+ " X.........X ",
+ "X ......... X",
+ " X.........X ",
+ "X ......... X",
+ " X X X X X X ",
+ "X X X X X X X",
+};
+
+Qt::DropAction QDragManager::drag(QDrag *o)
+{
+ if(qt_mac_in_drag) { //just make sure..
+ qWarning("Qt: Internal error: WH0A, unexpected condition reached");
+ return Qt::IgnoreAction;
+ }
+ if(object == o)
+ return Qt::IgnoreAction;
+ /* At the moment it seems clear that Mac OS X does not want to drag with a non-left button
+ so we just bail early to prevent it */
+ if(!(GetCurrentEventButtonState() & kEventMouseButtonPrimary))
+ return Qt::IgnoreAction;
+
+ if(object) {
+ dragPrivate()->source->removeEventFilter(this);
+ cancel();
+ beingCancelled = false;
+ }
+
+ object = o;
+ dragPrivate()->target = 0;
+
+#ifndef QT_NO_ACCESSIBILITY
+ QAccessible::updateAccessibility(this, 0, QAccessible::DragDropStart);
+#endif
+
+ // setup the data
+ QMacPasteboard dragBoard((CFStringRef) NSDragPboard, QMacPasteboardMime::MIME_DND);
+ dragPrivate()->data->setData(QLatin1String("application/x-qt-mime-type-name"), QByteArray());
+ dragBoard.setMimeData(dragPrivate()->data);
+
+ // create the image
+ QPoint hotspot;
+ QPixmap pix = dragPrivate()->pixmap;
+ if(pix.isNull()) {
+ if(dragPrivate()->data->hasText() || dragPrivate()->data->hasUrls()) {
+ // get the string
+ QString s = dragPrivate()->data->hasText() ? dragPrivate()->data->text()
+ : dragPrivate()->data->urls().first().toString();
+ if(s.length() > 26)
+ s = s.left(23) + QChar(0x2026);
+ if(!s.isEmpty()) {
+ // draw it
+ QFont f(qApp->font());
+ f.setPointSize(12);
+ QFontMetrics fm(f);
+ QPixmap tmp(fm.width(s), fm.height());
+ if(!tmp.isNull()) {
+ QPainter p(&tmp);
+ p.fillRect(0, 0, tmp.width(), tmp.height(), Qt::color0);
+ p.setPen(Qt::color1);
+ p.setFont(f);
+ p.drawText(0, fm.ascent(), s);
+ // save it
+ pix = tmp;
+ hotspot = QPoint(tmp.width() / 2, tmp.height() / 2);
+ }
+ }
+ } else {
+ pix = QPixmap(default_pm);
+ hotspot = QPoint(default_pm_hotx, default_pm_hoty);
+ }
+ } else {
+ hotspot = dragPrivate()->hotspot;
+ }
+ // convert the image to NSImage.
+ NSImage *image = (NSImage *)qt_mac_create_nsimage(pix);
+ [image retain];
+ DnDParams *dndParams = [QT_MANGLE_NAMESPACE(QCocoaView) currentMouseEvent];
+ // save supported actions
+ [dndParams->view setSupportedActions: qt_mac_mapDropActions(dragPrivate()->possible_actions)];
+ NSPoint imageLoc = {dndParams->localPoint.x - hotspot.x(),
+ dndParams->localPoint.y + pix.height() - hotspot.y()};
+ NSSize mouseOffset = {0.0, 0.0};
+ NSPasteboard *pboard = [NSPasteboard pasteboardWithName:NSDragPboard];
+ NSPoint windowPoint = [dndParams->theEvent locationInWindow];
+ // do the drag
+ [dndParams->view retain];
+ [dndParams->view dragImage:image
+ at:imageLoc
+ offset:mouseOffset
+ event:dndParams->theEvent
+ pasteboard:pboard
+ source:dndParams->view
+ slideBack:YES];
+ [dndParams->view release];
+ [image release];
+ object = 0;
+ Qt::DropAction performedAction(qt_mac_mapNSDragOperation(dndParams->performedAction));
+ // do post drag processing, if required.
+ if(performedAction != Qt::IgnoreAction) {
+ // check if the receiver points us to a file location.
+ // if so, we need to do the file copy/move ourselves.
+ QCFType<CFURLRef> pasteLocation = 0;
+ PasteboardCopyPasteLocation(dragBoard.pasteBoard(), &pasteLocation);
+ if (pasteLocation) {
+ QList<QUrl> urls = o->mimeData()->urls();
+ for (int i = 0; i < urls.size(); ++i) {
+ QUrl fromUrl = urls.at(i);
+ QString filename = QFileInfo(fromUrl.path()).fileName();
+ QUrl toUrl(QCFString::toQString(CFURLGetString(pasteLocation)) + filename);
+ if (performedAction == Qt::MoveAction)
+ QFile::rename(fromUrl.path(), toUrl.path());
+ else if (performedAction == Qt::CopyAction)
+ QFile::copy(fromUrl.path(), toUrl.path());
+ }
+ }
+ }
+ return performedAction;
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_MAC_USE_COCOA
diff --git a/src/gui/kernel/qcocoaview_mac_p.h b/src/gui/kernel/qcocoaview_mac_p.h
new file mode 100644
index 0000000000..9de94d5fc7
--- /dev/null
+++ b/src/gui/kernel/qcocoaview_mac_p.h
@@ -0,0 +1,109 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//
+// 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 <qevent.h>
+#ifdef QT_MAC_USE_COCOA
+#import <Cocoa/Cocoa.h>
+
+@class QT_MANGLE_NAMESPACE(QCocoaView);
+QT_FORWARD_DECLARE_CLASS(QWidgetPrivate);
+QT_FORWARD_DECLARE_CLASS(QWidget);
+QT_FORWARD_DECLARE_CLASS(QEvent);
+QT_FORWARD_DECLARE_CLASS(QCocoaDropData);
+
+QT_BEGIN_NAMESPACE
+struct DnDParams
+{
+ QT_MANGLE_NAMESPACE(QCocoaView) *view;
+ NSEvent *theEvent;
+ NSPoint localPoint;
+ NSDragOperation performedAction;
+};
+
+QT_END_NAMESPACE
+
+QT_FORWARD_DECLARE_STRUCT(DnDParams);
+
+Q_GUI_EXPORT
+@interface QT_MANGLE_NAMESPACE(QCocoaView) : NSControl <NSTextInput> {
+ QWidget *qwidget;
+ QWidgetPrivate *qwidgetprivate;
+ bool leftButtonIsRightButton;
+ QCocoaDropData *dropData;
+ NSDragOperation supportedActions;
+ bool composing;
+ int composingLength;
+ bool sendKeyEvents;
+}
+- (id)initWithQWidget:(QWidget *)widget widgetPrivate:(QWidgetPrivate *)widgetprivate;
+- (void) finishInitWithQWidget:(QWidget *)widget widgetPrivate:(QWidgetPrivate *)widgetprivate;
+- (void)frameDidChange:(NSNotification *)note;
+- (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender;
+- (NSDragOperation)draggingUpdated:(id < NSDraggingInfo >)sender;
+- (void)draggingExited:(id < NSDraggingInfo >)sender;
+- (BOOL)performDragOperation:(id <NSDraggingInfo>)sender;
+- (void)registerDragTypes:(bool)accept;
+- (void)removeDropData;
+- (void)addDropData:(id <NSDraggingInfo>)sender;
+- (void)setSupportedActions:(NSDragOperation)actions;
+- (NSDragOperation)draggingSourceOperationMaskForLocal:(BOOL)isLocal;
+- (void)draggedImage:(NSImage *)anImage endedAt:(NSPoint)aPoint operation:(NSDragOperation)operation;
+- (BOOL)isComposing;
+- (QWidget *)qt_qwidget;
+- (BOOL)qt_leftButtonIsRightButton;
+- (void)qt_setLeftButtonIsRightButton:(BOOL)isSwapped;
+- (NSView *)viewUnderTransparentForMouseView:(NSView *)mouseView widget:(QWidget *)widgetToGetMouse
+ withWindowPoint:(NSPoint)windowPoint;
++ (DnDParams*)currentMouseEvent;
+
+@end
+#endif
diff --git a/src/gui/kernel/qcocoawindow_mac.mm b/src/gui/kernel/qcocoawindow_mac.mm
new file mode 100644
index 0000000000..6b304440a7
--- /dev/null
+++ b/src/gui/kernel/qcocoawindow_mac.mm
@@ -0,0 +1,185 @@
+/****************************************************************************
+ **
+ ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+ **
+ ** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+ **
+ ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
+ ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ **
+ ****************************************************************************/
+
+#include "qmacdefines_mac.h"
+#ifdef QT_MAC_USE_COCOA
+#import <private/qcocoawindow_mac_p.h>
+#import <private/qcocoawindowdelegate_mac_p.h>
+#import <private/qcocoaview_mac_p.h>
+#import <private/qt_cocoa_helpers_mac_p.h>
+#import <private/qcocoawindowcustomthemeframe_mac_p.h>
+
+#include <QtGui/QWidget>
+
+QT_FORWARD_DECLARE_CLASS(QWidget);
+QT_USE_NAMESPACE
+
+extern Qt::MouseButton cocoaButton2QtButton(NSInteger buttonNum); // qcocoaview.mm
+
+@implementation NSWindow (QT_MANGLE_NAMESPACE(QWidgetIntegration))
+
+- (id)QT_MANGLE_NAMESPACE(qt_initWithQWidget):(QWidget*)widget contentRect:(NSRect)rect styleMask:(NSUInteger)mask;
+{
+ self = [self initWithContentRect:rect styleMask:mask backing:NSBackingStoreBuffered defer:YES];
+ if (self) {
+ [[QT_MANGLE_NAMESPACE(QCocoaWindowDelegate) sharedDelegate] becomeDelegteForWindow:self widget:widget];
+ [self setReleasedWhenClosed:NO];
+ }
+ return self;
+}
+
+- (QWidget *)QT_MANGLE_NAMESPACE(qt_qwidget)
+{
+ QWidget *widget = 0;
+ if ([self delegate] == [QT_MANGLE_NAMESPACE(QCocoaWindowDelegate) sharedDelegate])
+ widget = [[QT_MANGLE_NAMESPACE(QCocoaWindowDelegate) sharedDelegate] qt_qwidgetForWindow:self];
+ return widget;
+}
+
+@end
+
+@implementation QT_MANGLE_NAMESPACE(QCocoaWindow)
+
+- (BOOL)canBecomeKeyWindow
+{
+ return YES;
+}
+
+/*
+ The methods keyDown, keyUp, and flagsChanged... These really shouldn't ever
+ get hit. We automatically say we can be first responder if we are a window.
+ So, the handling should get handled by the view. This is here more as a
+ last resort (i.e., this is code that can potentially be removed).
+ */
+
+- (void)keyDown:(NSEvent *)theEvent
+{
+ bool keyOK = qt_dispatchKeyEvent(theEvent, [self QT_MANGLE_NAMESPACE(qt_qwidget)]);
+ if (!keyOK)
+ [super keyDown:theEvent];
+}
+
+- (void)keyUp:(NSEvent *)theEvent
+{
+ bool keyOK = qt_dispatchKeyEvent(theEvent, [self QT_MANGLE_NAMESPACE(qt_qwidget)]);
+ if (!keyOK)
+ [super keyUp:theEvent];
+}
+
+- (void)flagsChanged:(NSEvent *)theEvent
+{
+ qt_dispatchModifiersChanged(theEvent, [self QT_MANGLE_NAMESPACE(qt_qwidget)]);
+ [super flagsChanged:theEvent];
+}
+
+
+- (void)tabletProximity:(NSEvent *)tabletEvent
+{
+ qt_dispatchTabletProximityEvent(tabletEvent);
+}
+
+- (void)sendEvent:(NSEvent *)event
+{
+ [self retain];
+
+ QWidget *widget = [[QT_MANGLE_NAMESPACE(QCocoaWindowDelegate) sharedDelegate] qt_qwidgetForWindow:self];
+ QCocoaView *view = static_cast<QCocoaView *>(qt_mac_nativeview_for(widget));
+ Qt::MouseButton mouseButton = cocoaButton2QtButton([event buttonNumber]);
+
+ // sometimes need to redirect mouse events to the popup.
+ QWidget *popup = qAppInstance()->activePopupWidget();
+ if (popup && popup != widget) {
+ switch([event type])
+ {
+ case NSLeftMouseDown:
+ qt_mac_handleMouseEvent(view, event, QEvent::MouseButtonPress, mouseButton);
+ // Don't call super here. This prevents us from getting the mouseUp event,
+ // which we need to send even if the mouseDown event was not accepted.
+ // (this is standard Qt behavior.)
+ break;
+ case NSRightMouseDown:
+ case NSOtherMouseDown:
+ if (!qt_mac_handleMouseEvent(view, event, QEvent::MouseButtonPress, mouseButton))
+ [super sendEvent:event];
+ break;
+ case NSLeftMouseUp:
+ case NSRightMouseUp:
+ case NSOtherMouseUp:
+ if (!qt_mac_handleMouseEvent(view, event, QEvent::MouseButtonRelease, mouseButton))
+ [super sendEvent:event];
+ break;
+ case NSMouseMoved:
+ qt_mac_handleMouseEvent(view, event, QEvent::MouseMove, Qt::NoButton);
+ break;
+ case NSLeftMouseDragged:
+ case NSRightMouseDragged:
+ case NSOtherMouseDragged:
+ [QT_MANGLE_NAMESPACE(QCocoaView) currentMouseEvent]->view = view;
+ [QT_MANGLE_NAMESPACE(QCocoaView) currentMouseEvent]->theEvent = event;
+ if (!qt_mac_handleMouseEvent(view, event, QEvent::MouseMove, mouseButton))
+ [super sendEvent:event];
+ break;
+ default:
+ [super sendEvent:event];
+ break;
+ }
+ } else {
+ [super sendEvent:event];
+ }
+ qt_mac_dispatchNCMouseMessage(self, event, [self QT_MANGLE_NAMESPACE(qt_qwidget)], leftButtonIsRightButton);
+
+
+ [self release];
+}
+
++ (Class)frameViewClassForStyleMask:(NSUInteger)styleMask
+{
+ if (styleMask & QtMacCustomizeWindow)
+ return [QT_MANGLE_NAMESPACE(QCocoaWindowCustomThemeFrame) class];
+ return [super frameViewClassForStyleMask:styleMask];
+}
+
+@end
+
+#endif
diff --git a/src/gui/kernel/qcocoawindow_mac_p.h b/src/gui/kernel/qcocoawindow_mac_p.h
new file mode 100644
index 0000000000..4f207ac411
--- /dev/null
+++ b/src/gui/kernel/qcocoawindow_mac_p.h
@@ -0,0 +1,72 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//
+// 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 "qmacdefines_mac.h"
+#ifdef QT_MAC_USE_COCOA
+#import <Cocoa/Cocoa.h>
+
+enum { QtMacCustomizeWindow = 1 << 21 }; // This will one day be run over by
+
+QT_FORWARD_DECLARE_CLASS(QWidget);
+
+@interface NSWindow (QT_MANGLE_NAMESPACE(QWidgetIntegration))
+- (id)QT_MANGLE_NAMESPACE(qt_initWithQWidget):(QWidget *)widget contentRect:(NSRect)rect styleMask:(NSUInteger)mask;
+- (QWidget *)QT_MANGLE_NAMESPACE(qt_qwidget);
+@end
+
+@interface QT_MANGLE_NAMESPACE(QCocoaWindow) : NSWindow {
+ bool leftButtonIsRightButton;
+}
+
++ (Class)frameViewClassForStyleMask:(NSUInteger)styleMask;
+@end
+#endif
diff --git a/src/gui/kernel/qcocoawindowcustomthemeframe_mac.mm b/src/gui/kernel/qcocoawindowcustomthemeframe_mac.mm
new file mode 100644
index 0000000000..98625e47e4
--- /dev/null
+++ b/src/gui/kernel/qcocoawindowcustomthemeframe_mac.mm
@@ -0,0 +1,62 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qmacdefines_mac.h"
+
+#ifdef QT_MAC_USE_COCOA
+
+#import "private/qcocoawindowcustomthemeframe_mac_p.h"
+#import "private/qcocoawindow_mac_p.h"
+#include "private/qt_cocoa_helpers_mac_p.h"
+#include "qwidget.h"
+
+@implementation QT_MANGLE_NAMESPACE(QCocoaWindowCustomThemeFrame)
+
+- (void)_updateButtons
+{
+ [super _updateButtons];
+ NSWindow *window = [self window];
+ qt_syncCocoaTitleBarButtons(window, [window QT_MANGLE_NAMESPACE(qt_qwidget)]);
+}
+
+@end
+
+#endif
diff --git a/src/gui/kernel/qcocoawindowcustomthemeframe_mac_p.h b/src/gui/kernel/qcocoawindowcustomthemeframe_mac_p.h
new file mode 100644
index 0000000000..c30b67acd0
--- /dev/null
+++ b/src/gui/kernel/qcocoawindowcustomthemeframe_mac_p.h
@@ -0,0 +1,61 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of qapplication_*.cpp, qwidget*.cpp, qcolor_x11.cpp, qfiledialog.cpp
+// and many other. This header file may change from version to version
+// without notice, or even be removed.
+//
+// We mean it.
+//
+#import <Cocoa/Cocoa.h>
+#include "qmacdefines_mac.h"
+#import "qnsthemeframe_mac_p.h"
+
+@interface QT_MANGLE_NAMESPACE(QCocoaWindowCustomThemeFrame) : NSThemeFrame
+{
+}
+
+@end
diff --git a/src/gui/kernel/qcocoawindowdelegate_mac.mm b/src/gui/kernel/qcocoawindowdelegate_mac.mm
new file mode 100644
index 0000000000..848017915f
--- /dev/null
+++ b/src/gui/kernel/qcocoawindowdelegate_mac.mm
@@ -0,0 +1,349 @@
+/****************************************************************************
+ **
+ ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+ **
+ ** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+ **
+ ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
+ ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ **
+ ****************************************************************************/
+
+#import "private/qcocoawindowdelegate_mac_p.h"
+#ifdef QT_MAC_USE_COCOA
+#include <private/qwidget_p.h>
+#include <private/qapplication_p.h>
+#include <private/qt_cocoa_helpers_mac_p.h>
+#include <qevent.h>
+#include <qlayout.h>
+#include <qcoreapplication.h>
+#include <qmenubar.h>
+
+QT_BEGIN_NAMESPACE
+extern QWidgetData *qt_qwidget_data(QWidget *); // qwidget.cpp
+extern void onApplicationWindowChangedActivation(QWidget *, bool); //qapplication_mac.mm
+extern bool qt_sendSpontaneousEvent(QObject *, QEvent *); // qapplication.cpp
+QT_END_NAMESPACE
+
+QT_USE_NAMESPACE
+
+static QT_MANGLE_NAMESPACE(QCocoaWindowDelegate) *sharedCocoaWindowDelegate = nil;
+
+// This is a singleton, but unlike most Cocoa singletons, it lives in a library and could be
+// pontentially loaded and unloaded. This means we should at least attempt to do the
+// memory management correctly.
+
+static void cleanupCocoaWindowDelegate()
+{
+ [sharedCocoaWindowDelegate release];
+}
+
+@implementation QT_MANGLE_NAMESPACE(QCocoaWindowDelegate)
+
+- (id)init
+{
+ self = [super init];
+ if (self != nil) {
+ m_windowHash = new QHash<NSWindow *, QWidget *>();
+ m_drawerHash = new QHash<NSDrawer *, QWidget *>();
+ }
+ return self;
+}
+
+- (void)dealloc
+{
+ sharedCocoaWindowDelegate = nil;
+ QHash<NSWindow *, QWidget *>::const_iterator windowIt = m_windowHash->constBegin();
+ while (windowIt != m_windowHash->constEnd()) {
+ [windowIt.key() setDelegate:nil];
+ ++windowIt;
+ }
+ delete m_windowHash;
+ QHash<NSDrawer *, QWidget *>::const_iterator drawerIt = m_drawerHash->constBegin();
+ while (drawerIt != m_drawerHash->constEnd()) {
+ [drawerIt.key() setDelegate:nil];
+ ++drawerIt;
+ }
+ delete m_drawerHash;
+ [super dealloc];
+}
+
++ (id)allocWithZone:(NSZone *)zone
+{
+ @synchronized(self) {
+ if (sharedCocoaWindowDelegate == nil) {
+ sharedCocoaWindowDelegate = [super allocWithZone:zone];
+ return sharedCocoaWindowDelegate;
+ qAddPostRoutine(cleanupCocoaWindowDelegate);
+ }
+ }
+ return nil;
+}
+
++ (QT_MANGLE_NAMESPACE(QCocoaWindowDelegate)*)sharedDelegate
+{
+ @synchronized(self) {
+ if (sharedCocoaWindowDelegate == nil)
+ [[self alloc] init];
+ }
+ return [[sharedCocoaWindowDelegate retain] autorelease];
+}
+
+-(void)syncSizeForWidget:(QWidget *)qwidget toSize:(const QSize &)newSize fromSize:(const QSize &)oldSize
+{
+ qt_qwidget_data(qwidget)->crect.setSize(newSize);
+ // ### static contents optimization needs to go here
+ const OSViewRef view = qt_mac_nativeview_for(qwidget);
+ [view setFrameSize:NSMakeSize(newSize.width(), newSize.height())];
+ if (!qwidget->isVisible()) {
+ qwidget->setAttribute(Qt::WA_PendingResizeEvent, true);
+ } else {
+ QResizeEvent qre(newSize, oldSize);
+ qt_sendSpontaneousEvent(qwidget, &qre);
+ }
+}
+
+- (void)dumpMaximizedStateforWidget:(QWidget*)qwidget window:(NSWindow *)window;
+{
+ if (!window)
+ return; // Nothing to do.
+ QWidgetData *widgetData = qt_qwidget_data(qwidget);
+ if ((widgetData->window_state & Qt::WindowMaximized) && ![window isZoomed]) {
+ widgetData->window_state &= ~Qt::WindowMaximized;
+ QWindowStateChangeEvent e(Qt::WindowState(widgetData->window_state | Qt::WindowMaximized));
+ qt_sendSpontaneousEvent(qwidget, &e);
+ }
+}
+
+- (NSSize)closestAcceptableSizeForWidget:(QWidget *)qwidget window:(NSWindow *)window
+ withNewSize:(NSSize)proposedSize
+{
+ [self dumpMaximizedStateforWidget:qwidget window:window];
+ QSize newSize = QLayout::closestAcceptableSize(qwidget,
+ QSize(proposedSize.width, proposedSize.height));
+ return [NSWindow frameRectForContentRect:
+ NSMakeRect(0., 0., newSize.width(), newSize.height())
+ styleMask:[window styleMask]].size;
+}
+
+- (NSSize)windowWillResize:(NSWindow *)windowToResize toSize:(NSSize)proposedFrameSize
+{
+ QWidget *qwidget = m_windowHash->value(windowToResize);
+ return [self closestAcceptableSizeForWidget:qwidget window:windowToResize
+ withNewSize:[NSWindow contentRectForFrameRect:
+ NSMakeRect(0, 0,
+ proposedFrameSize.width,
+ proposedFrameSize.height)
+ styleMask:[windowToResize styleMask]].size];
+}
+
+- (NSSize)drawerWillResizeContents:(NSDrawer *)sender toSize:(NSSize)contentSize
+{
+ QWidget *qwidget = m_drawerHash->value(sender);
+ return [self closestAcceptableSizeForWidget:qwidget window:nil withNewSize:contentSize];
+}
+
+-(void)windowDidMiniaturize:(NSNotification*)notification
+{
+ QWidget *qwidget = m_windowHash->value([notification object]);
+ if (!qwidget->isMinimized()) {
+ QWidgetData *widgetData = qt_qwidget_data(qwidget);
+ widgetData->window_state = widgetData->window_state | Qt::WindowMinimized;
+ QWindowStateChangeEvent e(Qt::WindowStates(widgetData->window_state & ~Qt::WindowMinimized));
+ qt_sendSpontaneousEvent(qwidget, &e);
+ }
+ // Send hide to match Qt on X11 and Windows
+ QEvent e(QEvent::Hide);
+ qt_sendSpontaneousEvent(qwidget, &e);
+}
+
+- (void)windowDidResize:(NSNotification *)notification
+{
+ NSWindow *window = [notification object];
+ QWidget *qwidget = m_windowHash->value(window);
+ // Just here to handle the is zoomed method.
+ QWidgetData *widgetData = qt_qwidget_data(qwidget);
+ if (!(qwidget->windowState() & (Qt::WindowMaximized | Qt::WindowFullScreen)) && [window isZoomed]) {
+ widgetData->window_state = widgetData->window_state | Qt::WindowMaximized;
+ QWindowStateChangeEvent e(Qt::WindowStates(widgetData->window_state
+ & ~Qt::WindowMaximized));
+ qt_sendSpontaneousEvent(qwidget, &e);
+ }
+ [self checkForMove:[window frame] forWidget:qwidget];
+ NSRect rect = [[window contentView] frame];
+ const QSize newSize(rect.size.width, rect.size.height);
+ const QSize &oldSize = widgetData->crect.size();
+ if (newSize != oldSize) {
+ QWidgetPrivate::qt_mac_update_sizer(qwidget);
+ [self syncSizeForWidget:qwidget toSize:newSize fromSize:oldSize];
+ }
+}
+
+- (void)checkForMove:(const NSRect &)newRect forWidget:(QWidget *)qwidget
+{
+ // newRect's origin is bottom left.
+ QPoint qtPoint = flipPoint(NSMakePoint(newRect.origin.x,
+ newRect.origin.y + newRect.size.height)).toPoint();
+ const QRect &oldRect = qwidget->frameGeometry();
+ if (qtPoint.x() != oldRect.x() || qtPoint.y() != oldRect.y()) {
+ QWidgetData *widgetData = qt_qwidget_data(qwidget);
+ QRect oldCRect = widgetData->crect;
+ QWidgetPrivate *widgetPrivate = qt_widget_private(qwidget);
+ const QRect &fStrut = widgetPrivate->frameStrut();
+ widgetData->crect.moveTo(qtPoint.x() + fStrut.left(), qtPoint.y() + fStrut.top());
+ if (!qwidget->isVisible()) {
+ qwidget->setAttribute(Qt::WA_PendingMoveEvent, true);
+ } else {
+ QMoveEvent qme(qtPoint, oldRect.topLeft());
+ qt_sendSpontaneousEvent(qwidget, &qme);
+ }
+ }
+}
+
+- (void)windowDidMove:(NSNotification *)notification
+{
+ [self windowDidResize:notification];
+}
+
+-(BOOL)windowShouldClose:(id)windowThatWantsToClose
+{
+ QWidget *qwidget = m_windowHash->value(windowThatWantsToClose);
+ QScopedLoopLevelCounter counter(qt_widget_private(qwidget)->threadData);
+ return qt_widget_private(qwidget)->close_helper(QWidgetPrivate::CloseWithSpontaneousEvent);
+}
+
+-(void)windowDidDeminiaturize:(NSNotification *)notification
+{
+ QWidget *qwidget = m_windowHash->value([notification object]);
+ QWidgetData *widgetData = qt_qwidget_data(qwidget);
+ Qt::WindowStates currState = Qt::WindowStates(widgetData->window_state);
+ Qt::WindowStates newState = currState;
+ if (currState & Qt::WindowMinimized)
+ newState &= ~Qt::WindowMinimized;
+ if (!(currState & Qt::WindowActive))
+ newState |= Qt::WindowActive;
+ if (newState != currState) {
+ widgetData->window_state = newState;
+ QWindowStateChangeEvent e(currState);
+ qt_sendSpontaneousEvent(qwidget, &e);
+ }
+ QShowEvent qse;
+ qt_sendSpontaneousEvent(qwidget, &qse);
+}
+
+-(void)windowDidBecomeMain:(NSNotification*)notification
+{
+ QWidget *qwidget = m_windowHash->value([notification object]);
+ Q_ASSERT(qwidget);
+ if (qwidget->isActiveWindow())
+ return; // Widget is already active, no need to go through re-activation.
+
+ onApplicationWindowChangedActivation(qwidget, true);
+}
+
+-(void)windowDidResignMain:(NSNotification*)notification
+{
+ QWidget *qwidget = m_windowHash->value([notification object]);
+ Q_ASSERT(qwidget);
+ onApplicationWindowChangedActivation(qwidget, false);
+}
+
+// These are the same as main, but they are probably better to keep separate since there is a
+// tiny difference between main and key windows.
+-(void)windowDidBecomeKey:(NSNotification*)notification
+{
+ QWidget *qwidget = m_windowHash->value([notification object]);
+ Q_ASSERT(qwidget);
+ if (qwidget->isActiveWindow())
+ return; // Widget is already active, no need to go through re-activation
+
+
+ onApplicationWindowChangedActivation(qwidget, true);
+}
+
+-(void)windowDidResignKey:(NSNotification*)notification
+{
+ QWidget *qwidget = m_windowHash->value([notification object]);
+ Q_ASSERT(qwidget);
+ onApplicationWindowChangedActivation(qwidget, false);
+}
+
+-(QWidget *)qt_qwidgetForWindow:(NSWindow *)window
+{
+ return m_windowHash->value(window);
+}
+
+- (NSRect)windowWillUseStandardFrame:(NSWindow *)window defaultFrame:(NSRect)defaultFrame
+{
+ NSRect frameToReturn = defaultFrame;
+ QWidget *qwidget = m_windowHash->value(window);
+ QSizeF size = qwidget->maximumSize();
+ frameToReturn.size.width = qMin<CGFloat>(frameToReturn.size.width, size.width());
+ frameToReturn.size.height = qMin<CGFloat>(frameToReturn.size.height, size.height());
+ return frameToReturn;
+}
+
+- (void)becomeDelegteForWindow:(NSWindow *)window widget:(QWidget *)widget
+{
+ m_windowHash->insert(window, widget);
+ [window setDelegate:self];
+}
+
+- (void)resignDelegateForWindow:(NSWindow *)window
+{
+ [window setDelegate:nil];
+ m_windowHash->remove(window);
+}
+
+- (void)becomeDelegateForDrawer:(NSDrawer *)drawer widget:(QWidget *)widget
+{
+ m_drawerHash->insert(drawer, widget);
+ [drawer setDelegate:self];
+ NSWindow *window = [[drawer contentView] window];
+ [self becomeDelegteForWindow:window widget:widget];
+}
+
+- (void)resignDelegateForDrawer:(NSDrawer *)drawer
+{
+ QWidget *widget = m_drawerHash->value(drawer);
+ [drawer setDelegate:nil];
+ if (widget)
+ [self resignDelegateForWindow:[[drawer contentView] window]];
+ m_drawerHash->remove(drawer);
+}
+
+@end
+#endif// QT_MAC_USE_COCOA
diff --git a/src/gui/kernel/qcocoawindowdelegate_mac_p.h b/src/gui/kernel/qcocoawindowdelegate_mac_p.h
new file mode 100644
index 0000000000..7456ff75dc
--- /dev/null
+++ b/src/gui/kernel/qcocoawindowdelegate_mac_p.h
@@ -0,0 +1,95 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//
+// 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 "qmacdefines_mac.h"
+
+#ifdef QT_MAC_USE_COCOA
+#import <Cocoa/Cocoa.h>
+
+QT_BEGIN_NAMESPACE
+template <class Key, class T> class QHash;
+QT_END_NAMESPACE
+using QT_PREPEND_NAMESPACE(QHash);
+QT_FORWARD_DECLARE_CLASS(QWidget)
+QT_FORWARD_DECLARE_CLASS(QSize)
+QT_FORWARD_DECLARE_CLASS(QWidgetData)
+
+@interface QT_MANGLE_NAMESPACE(QCocoaWindowDelegate) : NSObject {
+ QHash<NSWindow *, QWidget *> *m_windowHash;
+ QHash<NSDrawer *, QWidget *> *m_drawerHash;
+}
++ (QT_MANGLE_NAMESPACE(QCocoaWindowDelegate)*)sharedDelegate;
+- (void)becomeDelegteForWindow:(NSWindow *)window widget:(QWidget *)widget;
+- (void)resignDelegateForWindow:(NSWindow *)window;
+- (void)becomeDelegateForDrawer:(NSDrawer *)drawer widget:(QWidget *)widget;
+- (void)resignDelegateForDrawer:(NSDrawer *)drawer;
+- (NSSize)windowWillResize:(NSWindow *)window toSize:(NSSize)proposedFrameSize;
+- (void)windowDidMiniaturize:(NSNotification*)notification;
+- (void)windowDidResize:(NSNotification *)notification;
+- (NSRect)windowWillUseStandardFrame:(NSWindow *)window defaultFrame:(NSRect)defaultFrame;
+- (void)windowDidMove:(NSNotification *)notification;
+- (BOOL)windowShouldClose:(id)window;
+- (void)windowDidDeminiaturize:(NSNotification *)notification;
+- (void)windowDidBecomeMain:(NSNotification*)notification;
+- (void)windowDidResignMain:(NSNotification*)notification;
+- (void)windowDidBecomeKey:(NSNotification*)notification;
+- (void)windowDidResignKey:(NSNotification*)notification;
+- (void)dumpMaximizedStateforWidget:(QWidget*)qwidget window:(NSWindow *)window;
+- (void)syncSizeForWidget:(QWidget *)qwidget
+ toSize:(const QSize &)newSize
+ fromSize:(const QSize &)oldSize;
+- (NSSize)closestAcceptableSizeForWidget:(QWidget *)qwidget
+ window:(NSWindow *)window withNewSize:(NSSize)proposedSize;
+- (QWidget *)qt_qwidgetForWindow:(NSWindow *)window;
+- (void)checkForMove:(const NSRect &)newRect forWidget:(QWidget *)qwidget;
+@end
+#endif
diff --git a/src/gui/kernel/qcursor.cpp b/src/gui/kernel/qcursor.cpp
new file mode 100644
index 0000000000..ed7e020d23
--- /dev/null
+++ b/src/gui/kernel/qcursor.cpp
@@ -0,0 +1,565 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qcursor.h"
+
+#ifndef QT_NO_CURSOR
+
+#include <qapplication.h>
+#include <qbitmap.h>
+#include <qimage.h>
+#include <qdatastream.h>
+#include <qvariant.h>
+#include <private/qcursor_p.h>
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QCursor
+
+ \brief The QCursor class provides a mouse cursor with an arbitrary
+ shape.
+
+ \ingroup appearance
+ \ingroup shared
+ \mainclass
+
+ This class is mainly used to create mouse cursors that are
+ associated with particular widgets and to get and set the position
+ of the mouse cursor.
+
+ Qt has a number of standard cursor shapes, but you can also make
+ custom cursor shapes based on a QBitmap, a mask and a hotspot.
+
+ To associate a cursor with a widget, use QWidget::setCursor(). To
+ associate a cursor with all widgets (normally for a short period
+ of time), use QApplication::setOverrideCursor().
+
+ To set a cursor shape use QCursor::setShape() or use the QCursor
+ constructor which takes the shape as argument, or you can use one
+ of the predefined cursors defined in the \l Qt::CursorShape enum.
+
+ If you want to create a cursor with your own bitmap, either use
+ the QCursor constructor which takes a bitmap and a mask or the
+ constructor which takes a pixmap as arguments.
+
+ To set or get the position of the mouse cursor use the static
+ methods QCursor::pos() and QCursor::setPos().
+
+ \bold{Note:} It is possible to create a QCursor before
+ QApplication, but it is not useful except as a place-holder for a
+ real QCursor created after QApplication. Attempting to use a
+ QCursor that was created before QApplication will result in a
+ crash.
+
+ \section1 A Note for X11 Users
+
+ On X11, Qt supports the \link
+ http://www.xfree86.org/4.3.0/Xcursor.3.html Xcursor\endlink
+ library, which allows for full color icon themes. The table below
+ shows the cursor name used for each Qt::CursorShape value. If a
+ cursor cannot be found using the name shown below, a standard X11
+ cursor will be used instead. Note: X11 does not provide
+ appropriate cursors for all possible Qt::CursorShape values. It
+ is possible that some cursors will be taken from the Xcursor
+ theme, while others will use an internal bitmap cursor.
+
+ \table
+ \header \o Shape \o Qt::CursorShape Value \o Cursor Name
+ \o Shape \o Qt::CursorShape Value \o Cursor Name
+ \row \o \inlineimage cursor-arrow.png
+ \o Qt::ArrowCursor \o \c left_ptr
+ \o \inlineimage cursor-sizev.png
+ \o Qt::SizeVerCursor \o \c size_ver
+ \row \o \inlineimage cursor-uparrow.png
+ \o Qt::UpArrowCursor \o \c up_arrow
+ \o \inlineimage cursor-sizeh.png
+ \o Qt::SizeHorCursor \o \c size_hor
+ \row \o \inlineimage cursor-cross.png
+ \o Qt::CrossCursor \o \c cross
+ \o \inlineimage cursor-sizeb.png
+ \o Qt::SizeBDiagCursor \o \c size_bdiag
+ \row \o \inlineimage cursor-ibeam.png
+ \o Qt::IBeamCursor \o \c ibeam
+ \o \inlineimage cursor-sizef.png
+ \o Qt::SizeFDiagCursor \o \c size_fdiag
+ \row \o \inlineimage cursor-wait.png
+ \o Qt::WaitCursor \o \c wait
+ \o \inlineimage cursor-sizeall.png
+ \o Qt::SizeAllCursor \o \c size_all
+ \row \o \inlineimage cursor-busy.png
+ \o Qt::BusyCursor \o \c left_ptr_watch
+ \o \inlineimage cursor-hsplit.png
+ \o Qt::SplitVCursor \o \c split_v
+ \row \o \inlineimage cursor-forbidden.png
+ \o Qt::ForbiddenCursor \o \c forbidden
+ \o \inlineimage cursor-vsplit.png
+ \o Qt::SplitHCursor \o \c split_h
+ \row \o \inlineimage cursor-hand.png
+ \o Qt::PointingHandCursor \o \c pointing_hand
+ \o \inlineimage cursor-openhand.png
+ \o Qt::OpenHandCursor \o \c openhand
+ \row \o \inlineimage cursor-whatsthis.png
+ \o Qt::WhatsThisCursor \o \c whats_this
+ \o \inlineimage cursor-closedhand.png
+ \o Qt::ClosedHandCursor \o \c closedhand
+ \endtable
+
+ \sa QWidget, {fowler}{GUI Design Handbook: Cursors}
+*/
+
+/*!
+ \fn HCURSOR_or_HANDLE QCursor::handle() const
+
+ Returns a platform-specific cursor handle. The \c
+ HCURSOR_or_HANDLE type is \c HCURSOR on Windows and Qt::HANDLE on X11
+ and Mac OS X. On \l{Qt for Embedded Linux} it is an integer.
+
+ \warning Using the value returned by this function is not
+ portable.
+*/
+
+/*!
+ \fn QCursor::QCursor(HCURSOR cursor)
+
+ Constructs a Qt cursor from the given Windows \a cursor.
+
+ \warning This function is only available on Windows.
+
+ \sa handle()
+*/
+
+/*!
+ \fn QCursor::QCursor(Qt::HANDLE handle)
+
+ Constructs a Qt cursor from the given \a handle.
+
+ \warning This function is only available on X11.
+
+ \sa handle()
+*/
+
+/*!
+ \fn QPoint QCursor::pos()
+
+ Returns the position of the cursor (hot spot) in global screen
+ coordinates.
+
+ You can call QWidget::mapFromGlobal() to translate it to widget
+ coordinates.
+
+ \sa setPos(), QWidget::mapFromGlobal(), QWidget::mapToGlobal()
+*/
+
+/*!
+ \fn void QCursor::setPos(int x, int y)
+
+ Moves the cursor (hot spot) to the global screen position (\a x,
+ \a y).
+
+ You can call QWidget::mapToGlobal() to translate widget
+ coordinates to global screen coordinates.
+
+ \sa pos(), QWidget::mapFromGlobal(), QWidget::mapToGlobal()
+*/
+
+/*!
+ \fn void QCursor::setPos (const QPoint &p)
+
+ \overload
+
+ Moves the cursor (hot spot) to the global screen position at point
+ \a p.
+*/
+
+/*****************************************************************************
+ QCursor stream functions
+ *****************************************************************************/
+
+#ifndef QT_NO_DATASTREAM
+
+
+/*!
+ \fn QDataStream &operator<<(QDataStream &stream, const QCursor &cursor)
+ \relates QCursor
+
+ Writes the \a cursor to the \a stream.
+
+ \sa {Format of the QDataStream operators}
+*/
+
+QDataStream &operator<<(QDataStream &s, const QCursor &c)
+{
+ s << (qint16)c.shape(); // write shape id to stream
+ if (c.shape() == Qt::BitmapCursor) { // bitmap cursor
+ bool isPixmap = false;
+ if (s.version() >= 7) {
+ isPixmap = !c.pixmap().isNull();
+ s << isPixmap;
+ }
+ if (isPixmap)
+ s << c.pixmap();
+ else
+ s << *c.bitmap() << *c.mask();
+ s << c.hotSpot();
+ }
+ return s;
+}
+
+/*!
+ \fn QDataStream &operator>>(QDataStream &stream, QCursor &cursor)
+ \relates QCursor
+
+ Reads the \a cursor from the \a stream.
+
+ \sa {Format of the QDataStream operators}
+*/
+
+QDataStream &operator>>(QDataStream &s, QCursor &c)
+{
+ qint16 shape;
+ s >> shape; // read shape id from stream
+ if (shape == Qt::BitmapCursor) { // read bitmap cursor
+ bool isPixmap = false;
+ if (s.version() >= 7)
+ s >> isPixmap;
+ if (isPixmap) {
+ QPixmap pm;
+ QPoint hot;
+ s >> pm >> hot;
+ c = QCursor(pm, hot.x(), hot.y());
+ } else {
+ QBitmap bm, bmm;
+ QPoint hot;
+ s >> bm >> bmm >> hot;
+ c = QCursor(bm, bmm, hot.x(), hot.y());
+ }
+ } else {
+ c.setShape((Qt::CursorShape)shape); // create cursor with shape
+ }
+ return s;
+}
+#endif // QT_NO_DATASTREAM
+
+
+/*!
+ Constructs a custom pixmap cursor.
+
+ \a pixmap is the image. It is usual to give it a mask (set using
+ QPixmap::setMask()). \a hotX and \a hotY define the cursor's hot
+ spot.
+
+ If \a hotX is negative, it is set to the \c{pixmap().width()/2}.
+ If \a hotY is negative, it is set to the \c{pixmap().height()/2}.
+
+ Valid cursor sizes depend on the display hardware (or the
+ underlying window system). We recommend using 32 x 32 cursors,
+ because this size is supported on all platforms. Some platforms
+ also support 16 x 16, 48 x 48, and 64 x 64 cursors.
+
+ \note On Windows CE, the cursor size is fixed. If the pixmap
+ is bigger than the system size, it will be scaled.
+
+ \sa QPixmap::QPixmap(), QPixmap::setMask()
+*/
+
+QCursor::QCursor(const QPixmap &pixmap, int hotX, int hotY)
+ : d(0)
+{
+ QImage img = pixmap.toImage().convertToFormat(QImage::Format_Indexed8, Qt::ThresholdDither|Qt::AvoidDither);
+ QBitmap bm = QBitmap::fromImage(img, Qt::ThresholdDither|Qt::AvoidDither);
+ QBitmap bmm = pixmap.mask();
+ if (!bmm.isNull()) {
+ QBitmap nullBm;
+ bm.setMask(nullBm);
+ }
+ else if (!pixmap.mask().isNull()) {
+ QImage mimg = pixmap.mask().toImage().convertToFormat(QImage::Format_Indexed8, Qt::ThresholdDither|Qt::AvoidDither);
+ bmm = QBitmap::fromImage(mimg, Qt::ThresholdDither|Qt::AvoidDither);
+ }
+ else {
+ bmm = QBitmap(bm.size());
+ bmm.fill(Qt::color1);
+ }
+
+ d = QCursorData::setBitmap(bm, bmm, hotX, hotY);
+ d->pixmap = pixmap;
+}
+
+
+
+/*!
+ Constructs a custom bitmap cursor.
+
+ \a bitmap and
+ \a mask make up the bitmap.
+ \a hotX and
+ \a hotY define the cursor's hot spot.
+
+ If \a hotX is negative, it is set to the \c{bitmap().width()/2}.
+ If \a hotY is negative, it is set to the \c{bitmap().height()/2}.
+
+ The cursor \a bitmap (B) and \a mask (M) bits are combined like this:
+ \list
+ \o B=1 and M=1 gives black.
+ \o B=0 and M=1 gives white.
+ \o B=0 and M=0 gives transparent.
+ \o B=1 and M=0 gives an XOR'd result.
+ \endlist
+
+ Use the global Qt color Qt::color0 to draw 0-pixels and Qt::color1 to
+ draw 1-pixels in the bitmaps.
+
+ Valid cursor sizes depend on the display hardware (or the
+ underlying window system). We recommend using 32 x 32 cursors,
+ because this size is supported on all platforms. Some platforms
+ also support 16 x 16, 48 x 48, and 64 x 64 cursors.
+
+ \note On Windows CE, the cursor size is fixed. If the pixmap
+ is bigger than the system size, it will be scaled.
+
+ \sa QBitmap::QBitmap(), QBitmap::setMask()
+*/
+
+QCursor::QCursor(const QBitmap &bitmap, const QBitmap &mask, int hotX, int hotY)
+ : d(0)
+{
+ d = QCursorData::setBitmap(bitmap, mask, hotX, hotY);
+}
+
+QCursorData *qt_cursorTable[Qt::LastCursor + 1];
+bool QCursorData::initialized = false;
+
+/*! \internal */
+void QCursorData::cleanup()
+{
+ if(!QCursorData::initialized)
+ return;
+
+ for (int shape = 0; shape <= Qt::LastCursor; ++shape) {
+ delete qt_cursorTable[shape];
+ qt_cursorTable[shape] = 0;
+ }
+ QCursorData::initialized = false;
+}
+
+/*! \internal */
+void QCursorData::initialize()
+{
+ if (QCursorData::initialized)
+ return;
+#ifdef Q_WS_MAC
+ // DRSWAT - Not Needed Cocoa or Carbon
+ //InitCursor();
+#endif
+ for (int shape = 0; shape <= Qt::LastCursor; ++shape)
+ qt_cursorTable[shape] = new QCursorData((Qt::CursorShape)shape);
+ QCursorData::initialized = true;
+}
+
+/*!
+ Constructs a cursor with the default arrow shape.
+*/
+QCursor::QCursor()
+{
+ if (!QCursorData::initialized) {
+ if (qApp->startingUp()) {
+ d = 0;
+ return;
+ }
+ QCursorData::initialize();
+ }
+ QCursorData *c = qt_cursorTable[0];
+ c->ref.ref();
+ d = c;
+}
+
+/*!
+ Constructs a cursor with the specified \a shape.
+
+ See \l Qt::CursorShape for a list of shapes.
+
+ \sa setShape()
+*/
+QCursor::QCursor(Qt::CursorShape shape)
+ : d(0)
+{
+ if (!QCursorData::initialized)
+ QCursorData::initialize();
+ setShape(shape);
+}
+
+
+/*!
+ Returns the cursor shape identifier. The return value is one of
+ the \l Qt::CursorShape enum values (cast to an int).
+
+ \sa setShape()
+*/
+Qt::CursorShape QCursor::shape() const
+{
+ if (!QCursorData::initialized)
+ QCursorData::initialize();
+ return d->cshape;
+}
+
+/*!
+ Sets the cursor to the shape identified by \a shape.
+
+ See \l Qt::CursorShape for the list of cursor shapes.
+
+ \sa shape()
+*/
+void QCursor::setShape(Qt::CursorShape shape)
+{
+ if (!QCursorData::initialized)
+ QCursorData::initialize();
+ QCursorData *c = uint(shape) <= Qt::LastCursor ? qt_cursorTable[shape] : 0;
+ if (!c)
+ c = qt_cursorTable[0];
+ c->ref.ref();
+ if (!d) {
+ d = c;
+ } else {
+ if (!d->ref.deref())
+ delete d;
+ d = c;
+ }
+}
+
+/*!
+ Returns the cursor bitmap, or 0 if it is one of the standard
+ cursors.
+*/
+const QBitmap *QCursor::bitmap() const
+{
+ if (!QCursorData::initialized)
+ QCursorData::initialize();
+ return d->bm;
+}
+
+/*!
+ Returns the cursor bitmap mask, or 0 if it is one of the standard
+ cursors.
+*/
+
+const QBitmap *QCursor::mask() const
+{
+ if (!QCursorData::initialized)
+ QCursorData::initialize();
+ return d->bmm;
+}
+
+/*!
+ Returns the cursor pixmap. This is only valid if the cursor is a
+ pixmap cursor.
+*/
+
+QPixmap QCursor::pixmap() const
+{
+ if (!QCursorData::initialized)
+ QCursorData::initialize();
+ return d->pixmap;
+}
+
+/*!
+ Returns the cursor hot spot, or (0, 0) if it is one of the
+ standard cursors.
+*/
+
+QPoint QCursor::hotSpot() const
+{
+ if (!QCursorData::initialized)
+ QCursorData::initialize();
+ return QPoint(d->hx, d->hy);
+}
+
+/*!
+ Constructs a copy of the cursor \a c.
+*/
+
+QCursor::QCursor(const QCursor &c)
+{
+ if (!QCursorData::initialized)
+ QCursorData::initialize();
+ d = c.d;
+ d->ref.ref();
+}
+
+/*!
+ Destroys the cursor.
+*/
+
+QCursor::~QCursor()
+{
+ if (d && !d->ref.deref())
+ delete d;
+}
+
+
+/*!
+ Assigns \a c to this cursor and returns a reference to this
+ cursor.
+*/
+
+QCursor &QCursor::operator=(const QCursor &c)
+{
+ if (!QCursorData::initialized)
+ QCursorData::initialize();
+ if (c.d)
+ c.d->ref.ref();
+ if (d && !d->ref.deref())
+ delete d;
+ d = c.d;
+ return *this;
+}
+
+/*!
+ Returns the cursor as a QVariant.
+*/
+QCursor::operator QVariant() const
+{
+ return QVariant(QVariant::Cursor, this);
+}
+
+#endif // QT_NO_CURSOR
+
+QT_END_NAMESPACE
diff --git a/src/gui/kernel/qcursor.h b/src/gui/kernel/qcursor.h
new file mode 100644
index 0000000000..15b4597193
--- /dev/null
+++ b/src/gui/kernel/qcursor.h
@@ -0,0 +1,160 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QCURSOR_H
+#define QCURSOR_H
+
+#include <QtCore/qpoint.h>
+#include <QtGui/qwindowdefs.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+class QVariant;
+
+/*
+ ### The fake cursor has to go first with old qdoc.
+*/
+#ifdef QT_NO_CURSOR
+
+class Q_GUI_EXPORT QCursor
+{
+public:
+ static QPoint pos();
+ static void setPos(int x, int y);
+ inline static void setPos(const QPoint &p) { setPos(p.x(), p.y()); }
+private:
+ QCursor();
+};
+
+#endif // QT_NO_CURSOR
+
+#ifndef QT_NO_CURSOR
+
+struct QCursorData;
+class QBitmap;
+class QPixmap;
+
+#if defined(Q_WS_MAC)
+void qt_mac_set_cursor(const QCursor *c, const QPoint &p);
+#endif
+
+class Q_GUI_EXPORT QCursor
+{
+public:
+ QCursor();
+ QCursor(Qt::CursorShape shape);
+ QCursor(const QBitmap &bitmap, const QBitmap &mask, int hotX=-1, int hotY=-1);
+ QCursor(const QPixmap &pixmap, int hotX=-1, int hotY=-1);
+ QCursor(const QCursor &cursor);
+ ~QCursor();
+ QCursor &operator=(const QCursor &cursor);
+ operator QVariant() const;
+
+ Qt::CursorShape shape() const;
+ void setShape(Qt::CursorShape newShape);
+
+ const QBitmap *bitmap() const;
+ const QBitmap *mask() const;
+ QPixmap pixmap() const;
+ QPoint hotSpot() const;
+
+ static QPoint pos();
+ static void setPos(int x, int y);
+ inline static void setPos(const QPoint &p) { setPos(p.x(), p.y()); }
+
+#ifdef qdoc
+ HCURSOR_or_HANDLE handle() const;
+ QCursor(HCURSOR cursor);
+ QCursor(Qt::HANDLE cursor);
+#endif
+
+#ifndef qdoc
+#if defined(Q_WS_WIN)
+ HCURSOR handle() const;
+ QCursor(HCURSOR cursor);
+#elif defined(Q_WS_X11)
+ Qt::HANDLE handle() const;
+ QCursor(Qt::HANDLE cursor);
+ static int x11Screen();
+#elif defined(Q_WS_MAC)
+ Qt::HANDLE handle() const;
+#elif defined(Q_WS_QWS)
+ int handle() const;
+#endif
+#endif
+
+private:
+ QCursorData *d;
+#if defined(Q_WS_MAC)
+ friend void qt_mac_set_cursor(const QCursor *c, const QPoint &p);
+#endif
+};
+
+#ifdef QT3_SUPPORT
+// CursorShape is defined in X11/X.h
+#ifdef CursorShape
+#define X_CursorShape CursorShape
+#undef CursorShape
+#endif
+typedef Qt::CursorShape QCursorShape;
+#ifdef X_CursorShape
+#define CursorShape X_CursorShape
+#endif
+#endif
+
+/*****************************************************************************
+ QCursor stream functions
+ *****************************************************************************/
+#ifndef QT_NO_DATASTREAM
+Q_GUI_EXPORT QDataStream &operator<<(QDataStream &outS, const QCursor &cursor);
+Q_GUI_EXPORT QDataStream &operator>>(QDataStream &inS, QCursor &cursor);
+#endif
+#endif // QT_NO_CURSOR
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QCURSOR_H
diff --git a/src/gui/kernel/qcursor_mac.mm b/src/gui/kernel/qcursor_mac.mm
new file mode 100644
index 0000000000..d632eb7949
--- /dev/null
+++ b/src/gui/kernel/qcursor_mac.mm
@@ -0,0 +1,556 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <private/qcursor_p.h>
+#include <private/qpixmap_mac_p.h>
+#include <qapplication.h>
+#include <qbitmap.h>
+#include <qcursor.h>
+#include <qevent.h>
+#include <string.h>
+#include <unistd.h>
+#include <AppKit/NSCursor.h>
+#include <qpainter.h>
+#include <private/qt_cocoa_helpers_mac_p.h>
+
+QT_BEGIN_NAMESPACE
+
+/*****************************************************************************
+ Externals
+ *****************************************************************************/
+extern QCursorData *qt_cursorTable[Qt::LastCursor + 1];
+extern OSWindowRef qt_mac_window_for(const QWidget *); //qwidget_mac.cpp
+extern GrafPtr qt_mac_qd_context(const QPaintDevice *); //qpaintdevice_mac.cpp
+extern bool qt_sendSpontaneousEvent(QObject *, QEvent *); //qapplication_mac.cpp
+
+/*****************************************************************************
+ Internal QCursorData class
+ *****************************************************************************/
+
+class QMacAnimateCursor : public QObject
+{
+ int timerId, step;
+ ThemeCursor curs;
+public:
+ QMacAnimateCursor() : QObject(), timerId(-1) { }
+ void start(ThemeCursor c) {
+ step = 1;
+ if(timerId != -1)
+ killTimer(timerId);
+ timerId = startTimer(300);
+ curs = c;
+ }
+ void stop() {
+ if(timerId != -1) {
+ killTimer(timerId);
+ timerId = -1;
+ }
+ }
+protected:
+ void timerEvent(QTimerEvent *e) {
+ if(e->timerId() == timerId) {
+ /*
+ if(SetAnimatedThemeCursor(curs, step++) == themeBadCursorIndexErr)
+ stop();
+ */
+ }
+ }
+};
+
+static QCursorData *currentCursor = 0; //current cursor
+void qt_mac_set_cursor(const QCursor *c, const QPoint &)
+{
+ if (!c) {
+ currentCursor = 0;
+ return;
+ }
+ c->handle(); //force the cursor to get loaded, if it's not
+
+ if(1 || currentCursor != c->d) {
+ if(currentCursor && currentCursor->type == QCursorData::TYPE_ThemeCursor
+ && currentCursor->curs.tc.anim)
+ currentCursor->curs.tc.anim->stop();
+ QMacCocoaAutoReleasePool pool;
+ if(c->d->type == QCursorData::TYPE_ImageCursor) {
+ [static_cast<NSCursor *>(c->d->curs.cp.nscursor) set];
+ } else if(c->d->type == QCursorData::TYPE_ThemeCursor) {
+#ifdef QT_MAC_USE_COCOA
+ if (c->d->curs.cp.nscursor == 0)
+ [[NSCursor arrowCursor] set];
+ [static_cast<NSCursor *>(c->d->curs.cp.nscursor) set];
+#else
+ if(SetAnimatedThemeCursor(c->d->curs.tc.curs, 0) == themeBadCursorIndexErr) {
+ SetThemeCursor(c->d->curs.tc.curs);
+ } else {
+ if(!c->d->curs.tc.anim)
+ c->d->curs.tc.anim = new QMacAnimateCursor;
+ c->d->curs.tc.anim->start(c->d->curs.tc.curs);
+ }
+#endif
+ }
+ }
+ currentCursor = c->d;
+}
+
+void qt_mac_update_cursor_at_global_pos(const QPoint &globalPos)
+{
+ QCursor cursor(Qt::ArrowCursor);
+ if (QApplication::overrideCursor()) {
+ cursor = *QApplication::overrideCursor();
+ } else {
+ for(QWidget *w = QApplication::widgetAt(globalPos); w; w = w->parentWidget()) {
+ if(w->testAttribute(Qt::WA_SetCursor)) {
+ cursor = w->cursor();
+ break;
+ }
+ }
+ }
+ qt_mac_set_cursor(&cursor, globalPos);
+}
+
+void qt_mac_update_cursor()
+{
+ qt_mac_update_cursor_at_global_pos(QCursor::pos());
+}
+
+static int nextCursorId = Qt::BitmapCursor;
+
+QCursorData::QCursorData(Qt::CursorShape s)
+ : cshape(s), bm(0), bmm(0), hx(-1), hy(-1), mId(s), type(TYPE_None)
+{
+ ref = 1;
+ memset(&curs, '\0', sizeof(curs));
+}
+
+QCursorData::~QCursorData()
+{
+ if (type == TYPE_ImageCursor) {
+ if (curs.cp.my_cursor) {
+ QMacCocoaAutoReleasePool pool;
+ [static_cast<NSCursor *>(curs.cp.nscursor) release];
+ }
+ } else if(type == TYPE_ThemeCursor) {
+ delete curs.tc.anim;
+ }
+ type = TYPE_None;
+
+ delete bm;
+ delete bmm;
+ if(currentCursor == this)
+ currentCursor = 0;
+}
+
+QCursorData *QCursorData::setBitmap(const QBitmap &bitmap, const QBitmap &mask, int hotX, int hotY)
+{
+ if (!QCursorData::initialized)
+ QCursorData::initialize();
+ if (bitmap.depth() != 1 || mask.depth() != 1 || bitmap.size() != mask.size()) {
+ qWarning("Qt: QCursor: Cannot create bitmap cursor; invalid bitmap(s)");
+ QCursorData *c = qt_cursorTable[0];
+ c->ref.ref();
+ return c;
+ }
+ // This is silly, but this is apparently called outside the constructor, so we have
+ // to be ready for that case.
+ QCursorData *x = new QCursorData;
+ x->ref = 1;
+ x->mId = ++nextCursorId;
+ x->bm = new QBitmap(bitmap);
+ x->bmm = new QBitmap(mask);
+ x->cshape = Qt::BitmapCursor;
+ x->hx = hotX >= 0 ? hotX : bitmap.width() / 2;
+ x->hy = hotY >= 0 ? hotY : bitmap.height() / 2;
+ return x;
+}
+
+Qt::HANDLE QCursor::handle() const
+{
+ if(!QCursorData::initialized)
+ QCursorData::initialize();
+ if(d->type == QCursorData::TYPE_None)
+ d->update();
+ return (Qt::HANDLE)d->mId;
+}
+
+QPoint QCursor::pos()
+{
+ return flipPoint([NSEvent mouseLocation]).toPoint();
+}
+
+void QCursor::setPos(int x, int y)
+{
+ CGWarpMouseCursorPosition(CGPointMake(x, y));
+
+ /* I'm not too keen on doing this, but this makes it a lot easier, so I just
+ send the event back through the event system and let it get propagated correctly
+ ideally this would not really need to be faked --Sam
+ */
+ QWidget *widget = 0;
+ if(QWidget *grb = QWidget::mouseGrabber())
+ widget = grb;
+ else
+ widget = QApplication::widgetAt(QPoint(x, y));
+ if(widget) {
+ QMouseEvent me(QMouseEvent::MouseMove, widget->mapFromGlobal(QPoint(x, y)), Qt::NoButton,
+ QApplication::mouseButtons(), QApplication::keyboardModifiers());
+ qt_sendSpontaneousEvent(widget, &me);
+ }
+}
+
+void QCursorData::initCursorFromBitmap()
+{
+ NSImage *nsimage;
+ QImage finalCursor(bm->size(), QImage::Format_ARGB32);
+ QImage bmi = bm->toImage().convertToFormat(QImage::Format_RGB32);
+ QImage bmmi = bmm->toImage().convertToFormat(QImage::Format_RGB32);
+ for (int row = 0; row < finalCursor.height(); ++row) {
+ QRgb *bmData = reinterpret_cast<QRgb *>(bmi.scanLine(row));
+ QRgb *bmmData = reinterpret_cast<QRgb *>(bmmi.scanLine(row));
+ QRgb *finalData = reinterpret_cast<QRgb *>(finalCursor.scanLine(row));
+ for (int col = 0; col < finalCursor.width(); ++col) {
+ if (bmmData[col] == 0xff000000 && bmData[col] == 0xffffffff) {
+ finalData[col] = 0xffffffff;
+ } else if (bmmData[col] == 0xff000000 && bmData[col] == 0xffffffff) {
+ finalData[col] = 0x7f000000;
+ } else if (bmmData[col] == 0xffffffff && bmData[col] == 0xffffffff) {
+ finalData[col] = 0x00000000;
+ } else {
+ finalData[col] = 0xff000000;
+ }
+ }
+ }
+ type = QCursorData::TYPE_ImageCursor;
+ curs.cp.my_cursor = true;
+ QPixmap bmCopy = QPixmap::fromImage(finalCursor);
+ NSPoint hotSpot = { hx, hy };
+ nsimage = static_cast<NSImage*>(qt_mac_create_nsimage(bmCopy));
+ curs.cp.nscursor = [[NSCursor alloc] initWithImage:nsimage hotSpot: hotSpot];
+ [nsimage release];
+}
+
+void QCursorData::initCursorFromPixmap()
+{
+ type = QCursorData::TYPE_ImageCursor;
+ curs.cp.my_cursor = true;
+ NSPoint hotSpot = { hx, hy };
+ NSImage *nsimage;
+ nsimage = static_cast<NSImage *>(qt_mac_create_nsimage(pixmap));
+ curs.cp.nscursor = [[NSCursor alloc] initWithImage:nsimage hotSpot: hotSpot];
+ [nsimage release];
+}
+
+void QCursorData::update()
+{
+ if(!QCursorData::initialized)
+ QCursorData::initialize();
+ if(type != QCursorData::TYPE_None)
+ return;
+
+ /* Note to self... ***
+ * mask x data
+ * 0xFF x 0x00 == fully opaque white
+ * 0x00 x 0xFF == xor'd black
+ * 0xFF x 0xFF == fully opaque black
+ * 0x00 x 0x00 == fully transparent
+ */
+
+ if (hx < 0)
+ hx = 0;
+ if (hy < 0)
+ hy = 0;
+
+#define QT_USE_APPROXIMATE_CURSORS
+#ifdef QT_USE_APPROXIMATE_CURSORS
+ static const uchar cur_ver_bits[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x80, 0x03, 0xc0, 0x07, 0xe0, 0x0f, 0xf0,
+ 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x0f, 0xf0,
+ 0x07, 0xe0, 0x03, 0xc0, 0x01, 0x80, 0x00, 0x00 };
+ static const uchar mcur_ver_bits[] = {
+ 0x00, 0x00, 0x03, 0x80, 0x07, 0xc0, 0x0f, 0xe0, 0x1f, 0xf0, 0x3f, 0xf8,
+ 0x7f, 0xfc, 0x07, 0xc0, 0x07, 0xc0, 0x07, 0xc0, 0x7f, 0xfc, 0x3f, 0xf8,
+ 0x1f, 0xf0, 0x0f, 0xe0, 0x07, 0xc0, 0x03, 0x80 };
+
+ static const uchar cur_hor_bits[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x20, 0x18, 0x30,
+ 0x38, 0x38, 0x7f, 0xfc, 0x7f, 0xfc, 0x38, 0x38, 0x18, 0x30, 0x08, 0x20,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+ static const uchar mcur_hor_bits[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x04, 0x40, 0x0c, 0x60, 0x1c, 0x70, 0x3c, 0x78,
+ 0x7f, 0xfc, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xfe, 0x7f, 0xfc, 0x3c, 0x78,
+ 0x1c, 0x70, 0x0c, 0x60, 0x04, 0x40, 0x00, 0x00 };
+
+ static const uchar cur_fdiag_bits[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xf8, 0x00, 0xf8, 0x00, 0x78,
+ 0x00, 0xf8, 0x01, 0xd8, 0x23, 0x88, 0x37, 0x00, 0x3e, 0x00, 0x3c, 0x00,
+ 0x3e, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00 };
+ static const uchar mcur_fdiag_bits[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x07, 0xfc, 0x03, 0xfc, 0x01, 0xfc, 0x00, 0xfc,
+ 0x41, 0xfc, 0x63, 0xfc, 0x77, 0xdc, 0x7f, 0x8c, 0x7f, 0x04, 0x7e, 0x00,
+ 0x7f, 0x00, 0x7f, 0x80, 0x7f, 0xc0, 0x00, 0x00 };
+
+ static const uchar cur_bdiag_bits[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x3e, 0x00, 0x3c, 0x00, 0x3e, 0x00,
+ 0x37, 0x00, 0x23, 0x88, 0x01, 0xd8, 0x00, 0xf8, 0x00, 0x78, 0x00, 0xf8,
+ 0x01, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+ static const uchar mcur_bdiag_bits[] = {
+ 0x00, 0x00, 0x7f, 0xc0, 0x7f, 0x80, 0x7f, 0x00, 0x7e, 0x00, 0x7f, 0x04,
+ 0x7f, 0x8c, 0x77, 0xdc, 0x63, 0xfc, 0x41, 0xfc, 0x00, 0xfc, 0x01, 0xfc,
+ 0x03, 0xfc, 0x07, 0xfc, 0x00, 0x00, 0x00, 0x00 };
+
+ static const unsigned char cur_up_arrow_bits[] = {
+ 0x00, 0x80, 0x01, 0x40, 0x01, 0x40, 0x02, 0x20, 0x02, 0x20, 0x04, 0x10,
+ 0x04, 0x10, 0x08, 0x08, 0x0f, 0x78, 0x01, 0x40, 0x01, 0x40, 0x01, 0x40,
+ 0x01, 0x40, 0x01, 0x40, 0x01, 0x40, 0x01, 0xc0 };
+ static const unsigned char mcur_up_arrow_bits[] = {
+ 0x00, 0x80, 0x01, 0xc0, 0x01, 0xc0, 0x03, 0xe0, 0x03, 0xe0, 0x07, 0xf0,
+ 0x07, 0xf0, 0x0f, 0xf8, 0x0f, 0xf8, 0x01, 0xc0, 0x01, 0xc0, 0x01, 0xc0,
+ 0x01, 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x01, 0xc0 };
+#endif
+ const uchar *cursorData = 0;
+ const uchar *cursorMaskData = 0;
+#ifdef QT_MAC_USE_COCOA
+ switch (cshape) { // map Q cursor to MAC cursor
+ case Qt::BitmapCursor: {
+ if (pixmap.isNull())
+ initCursorFromBitmap();
+ else
+ initCursorFromPixmap();
+ break; }
+ case Qt::BlankCursor: {
+ pixmap = QPixmap(16, 16);
+ pixmap.fill(Qt::transparent);
+ initCursorFromPixmap();
+ break; }
+ case Qt::ArrowCursor: {
+ type = QCursorData::TYPE_ThemeCursor;
+ curs.cp.nscursor = [NSCursor arrowCursor];
+ break; }
+ case Qt::CrossCursor: {
+ type = QCursorData::TYPE_ThemeCursor;
+ curs.cp.nscursor = [NSCursor crosshairCursor];
+ break; }
+ case Qt::WaitCursor: {
+ pixmap = QPixmap(QLatin1String(":/trolltech/mac/cursors/images/spincursor.png"));
+ initCursorFromPixmap();
+ break; }
+ case Qt::IBeamCursor: {
+ type = QCursorData::TYPE_ThemeCursor;
+ curs.cp.nscursor = [NSCursor IBeamCursor];
+ break; }
+ case Qt::SizeAllCursor: {
+ pixmap = QPixmap(QLatin1String(":/trolltech/mac/cursors/images/pluscursor.png"));
+ initCursorFromPixmap();
+ break; }
+ case Qt::WhatsThisCursor: { //for now just use the pointing hand
+ case Qt::PointingHandCursor:
+ type = QCursorData::TYPE_ThemeCursor;
+ curs.cp.nscursor = [NSCursor pointingHandCursor];
+ break; }
+ case Qt::BusyCursor: {
+ pixmap = QPixmap(QLatin1String(":/trolltech/mac/cursors/images/waitcursor.png"));
+ initCursorFromPixmap();
+ break; }
+ case Qt::SplitVCursor: {
+ type = QCursorData::TYPE_ThemeCursor;
+ curs.cp.nscursor = [NSCursor resizeUpDownCursor];
+ break; }
+ case Qt::SplitHCursor: {
+ type = QCursorData::TYPE_ThemeCursor;
+ curs.cp.nscursor = [NSCursor resizeLeftRightCursor];
+ break; }
+ case Qt::ForbiddenCursor: {
+ pixmap = QPixmap(QLatin1String(":/trolltech/mac/cursors/images/forbiddencursor.png"));
+ initCursorFromPixmap();
+ break; }
+ case Qt::OpenHandCursor:
+ type = QCursorData::TYPE_ThemeCursor;
+ curs.cp.nscursor = [NSCursor openHandCursor];
+ break;
+ case Qt::ClosedHandCursor:
+ type = QCursorData::TYPE_ThemeCursor;
+ curs.cp.nscursor = [NSCursor closedHandCursor];
+ break;
+#define QT_USE_APPROXIMATE_CURSORS
+#ifdef QT_USE_APPROXIMATE_CURSORS
+ case Qt::SizeVerCursor:
+ cursorData = cur_ver_bits;
+ cursorMaskData = mcur_ver_bits;
+ hx = hy = 8;
+ break;
+ case Qt::SizeHorCursor:
+ cursorData = cur_hor_bits;
+ cursorMaskData = mcur_hor_bits;
+ hx = hy = 8;
+ break;
+ case Qt::SizeBDiagCursor:
+ cursorData = cur_fdiag_bits;
+ cursorMaskData = mcur_fdiag_bits;
+ hx = hy = 8;
+ break;
+ case Qt::SizeFDiagCursor:
+ cursorData = cur_bdiag_bits;
+ cursorMaskData = mcur_bdiag_bits;
+ hx = hy = 8;
+ break;
+ case Qt::UpArrowCursor:
+ cursorData = cur_up_arrow_bits;
+ cursorMaskData = mcur_up_arrow_bits;
+ hx = 8;
+ break;
+#endif
+ default:
+ qWarning("Qt: QCursor::update: Invalid cursor shape %d", cshape);
+ return;
+ }
+#else
+ // Carbon
+ switch (cshape) { // map Q cursor to MAC cursor
+ case Qt::BitmapCursor: {
+ if (pixmap.isNull())
+ initCursorFromBitmap();
+ else
+ initCursorFromPixmap();
+ break; }
+ case Qt::BlankCursor: {
+ pixmap = QPixmap(16, 16);
+ pixmap.fill(Qt::transparent);
+ initCursorFromPixmap();
+ break; }
+ case Qt::ArrowCursor: {
+ type = QCursorData::TYPE_ThemeCursor;
+ curs.tc.curs = kThemeArrowCursor;
+ break; }
+ case Qt::CrossCursor: {
+ type = QCursorData::TYPE_ThemeCursor;
+ curs.tc.curs = kThemeCrossCursor;
+ break; }
+ case Qt::WaitCursor: {
+ type = QCursorData::TYPE_ThemeCursor;
+ curs.tc.curs = kThemeWatchCursor;
+ break; }
+ case Qt::IBeamCursor: {
+ type = QCursorData::TYPE_ThemeCursor;
+ curs.tc.curs = kThemeIBeamCursor;
+ break; }
+ case Qt::SizeAllCursor: {
+ type = QCursorData::TYPE_ThemeCursor;
+ curs.tc.curs = kThemePlusCursor;
+ break; }
+ case Qt::WhatsThisCursor: { //for now just use the pointing hand
+ case Qt::PointingHandCursor:
+ type = QCursorData::TYPE_ThemeCursor;
+ curs.tc.curs = kThemePointingHandCursor;
+ break; }
+ case Qt::BusyCursor: {
+ type = QCursorData::TYPE_ThemeCursor;
+ curs.tc.curs = kThemeSpinningCursor;
+ break; }
+ case Qt::SplitVCursor: {
+ type = QCursorData::TYPE_ThemeCursor;
+ curs.tc.curs = kThemeResizeUpDownCursor;
+ break; }
+ case Qt::SplitHCursor: {
+ type = QCursorData::TYPE_ThemeCursor;
+ curs.tc.curs = kThemeResizeLeftRightCursor;
+ break; }
+ case Qt::ForbiddenCursor: {
+ type = QCursorData::TYPE_ThemeCursor;
+ curs.tc.curs = kThemeNotAllowedCursor;
+ break; }
+ case Qt::OpenHandCursor:
+ type = QCursorData::TYPE_ThemeCursor;
+ curs.tc.curs = kThemeOpenHandCursor;
+ break;
+ case Qt::ClosedHandCursor:
+ type = QCursorData::TYPE_ThemeCursor;
+ curs.tc.curs = kThemeClosedHandCursor;
+ break;
+#define QT_USE_APPROXIMATE_CURSORS
+#ifdef QT_USE_APPROXIMATE_CURSORS
+ case Qt::SizeVerCursor:
+ cursorData = cur_ver_bits;
+ cursorMaskData = mcur_ver_bits;
+ hx = hy = 8;
+ break;
+ case Qt::SizeHorCursor:
+ cursorData = cur_hor_bits;
+ cursorMaskData = mcur_hor_bits;
+ hx = hy = 8;
+ break;
+ case Qt::SizeBDiagCursor:
+ cursorData = cur_fdiag_bits;
+ cursorMaskData = mcur_fdiag_bits;
+ hx = hy = 8;
+ break;
+ case Qt::SizeFDiagCursor:
+ cursorData = cur_bdiag_bits;
+ cursorMaskData = mcur_bdiag_bits;
+ hx = hy = 8;
+ break;
+ case Qt::UpArrowCursor:
+ cursorData = cur_up_arrow_bits;
+ cursorMaskData = mcur_up_arrow_bits;
+ hx = 8;
+ break;
+#endif
+ default:
+ qWarning("Qt: QCursor::update: Invalid cursor shape %d", cshape);
+ return;
+ }
+#endif
+
+ if (cursorData) {
+ bm = new QBitmap(QBitmap::fromData(QSize(16, 16), cursorData,
+ QImage::Format_Mono));
+ bmm = new QBitmap(QBitmap::fromData(QSize(16, 16), cursorMaskData,
+ QImage::Format_Mono));
+ initCursorFromBitmap();
+ }
+
+#if 0
+ if(type == QCursorData::TYPE_CursPtr && curs.cp.hcurs && curs.cp.my_cursor) {
+ curs.cp.hcurs->hotSpot.h = hx >= 0 ? hx : 8;
+ curs.cp.hcurs->hotSpot.v = hy >= 0 ? hy : 8;
+ }
+#endif
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/kernel/qcursor_p.h b/src/gui/kernel/qcursor_p.h
new file mode 100644
index 0000000000..42682ac702
--- /dev/null
+++ b/src/gui/kernel/qcursor_p.h
@@ -0,0 +1,121 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QCURSOR_P_H
+#define QCURSOR_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/qatomic.h"
+#include "QtCore/qglobal.h"
+#include "QtCore/qnamespace.h"
+#include "QtGui/qpixmap.h"
+
+# if defined (Q_WS_MAC)
+# include "private/qt_mac_p.h"
+# elif defined(Q_WS_X11)
+# include "private/qt_x11_p.h"
+# elif defined(Q_WS_WIN)
+# include "QtCore/qt_windows.h"
+#endif
+
+QT_BEGIN_NAMESPACE
+
+#if defined (Q_WS_MAC)
+class QMacAnimateCursor;
+#endif
+
+class QBitmap;
+struct QCursorData {
+ QCursorData(Qt::CursorShape s = Qt::ArrowCursor);
+ ~QCursorData();
+
+ static void initialize();
+ static void cleanup();
+
+ QAtomicInt ref;
+ Qt::CursorShape cshape;
+ QBitmap *bm, *bmm;
+ QPixmap pixmap;
+ short hx, hy;
+#if defined (Q_WS_MAC)
+ int mId;
+#elif defined(Q_WS_QWS)
+ int id;
+#endif
+#if defined (Q_WS_WIN)
+ HCURSOR hcurs;
+#elif defined (Q_WS_X11)
+ XColor fg, bg;
+ Cursor hcurs;
+ Pixmap pm, pmm;
+#elif defined (Q_WS_MAC)
+ enum { TYPE_None, TYPE_ImageCursor, TYPE_ThemeCursor } type;
+ union {
+ struct {
+ uint my_cursor:1;
+ void *nscursor;
+ } cp;
+ struct {
+ QMacAnimateCursor *anim;
+ ThemeCursor curs;
+ } tc;
+ } curs;
+ void initCursorFromBitmap();
+ void initCursorFromPixmap();
+#endif
+ static bool initialized;
+ void update();
+ static QCursorData *setBitmap(const QBitmap &bitmap, const QBitmap &mask, int hotX, int hotY);
+};
+
+QT_END_NAMESPACE
+
+#endif // QCURSOR_P_H
diff --git a/src/gui/kernel/qcursor_qws.cpp b/src/gui/kernel/qcursor_qws.cpp
new file mode 100644
index 0000000000..097b982041
--- /dev/null
+++ b/src/gui/kernel/qcursor_qws.cpp
@@ -0,0 +1,136 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <qcursor.h>
+#include <private/qcursor_p.h>
+#include <qbitmap.h>
+#include <qwsdisplay_qws.h>
+
+QT_BEGIN_NAMESPACE
+
+QT_USE_NAMESPACE
+
+#ifndef QT_NO_CURSOR
+
+static int nextCursorId = Qt::BitmapCursor;
+
+/*****************************************************************************
+ Internal QCursorData class
+ *****************************************************************************/
+
+QCursorData::QCursorData(Qt::CursorShape s)
+ : cshape(s), bm(0), bmm(0), hx(0), hy(0), id(s)
+{
+ ref = 1;
+}
+
+QCursorData::~QCursorData()
+{
+ delete bm;
+ delete bmm;
+ QPaintDevice::qwsDisplay()->destroyCursor(id);
+}
+
+
+/*****************************************************************************
+ Global cursors
+ *****************************************************************************/
+
+extern QCursorData *qt_cursorTable[Qt::LastCursor + 1]; // qcursor.cpp
+
+int QCursor::handle() const
+{
+ return d->id;
+}
+
+
+QCursorData *QCursorData::setBitmap(const QBitmap &bitmap, const QBitmap &mask, int hotX, int hotY)
+{
+ if (!QCursorData::initialized)
+ QCursorData::initialize();
+ if (bitmap.depth() != 1 || mask.depth() != 1 || bitmap.size() != mask.size()) {
+ qWarning("QCursor: Cannot create bitmap cursor; invalid bitmap(s)");
+ QCursorData *c = qt_cursorTable[0];
+ c->ref.ref();
+ return c;
+ }
+ QCursorData *d = new QCursorData;
+ d->bm = new QBitmap(bitmap);
+ d->bmm = new QBitmap(mask);
+ d->cshape = Qt::BitmapCursor;
+ d->id = ++nextCursorId;
+ d->hx = hotX >= 0 ? hotX : bitmap.width() / 2;
+ d->hy = hotY >= 0 ? hotY : bitmap.height() / 2;
+
+ QPaintDevice::qwsDisplay()->defineCursor(d->id, *d->bm, *d->bmm, d->hx, d->hy);
+ return d;
+}
+
+void QCursorData::update()
+{
+}
+
+#endif //QT_NO_CURSOR
+
+extern int *qt_last_x,*qt_last_y;
+
+QPoint QCursor::pos()
+{
+ // This doesn't know about hotspots yet so we disable it
+ //qt_accel_update_cursor();
+ if (qt_last_x)
+ return QPoint(*qt_last_x, *qt_last_y);
+ else
+ return QPoint();
+}
+
+void QCursor::setPos(int x, int y)
+{
+ // Need to check, since some X servers generate null mouse move
+ // events, causing looping in applications which call setPos() on
+ // every mouse move event.
+ //
+ if (pos() == QPoint(x, y))
+ return;
+ QPaintDevice::qwsDisplay()->setCursorPosition(x, y);
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/kernel/qcursor_win.cpp b/src/gui/kernel/qcursor_win.cpp
new file mode 100644
index 0000000000..85d5a11500
--- /dev/null
+++ b/src/gui/kernel/qcursor_win.cpp
@@ -0,0 +1,493 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <private/qcursor_p.h>
+#include <qbitmap.h>
+#include <qcursor.h>
+
+#ifndef QT_NO_CURSOR
+
+#include <qimage.h>
+#include <qt_windows.h>
+
+QT_BEGIN_NAMESPACE
+
+extern QCursorData *qt_cursorTable[Qt::LastCursor + 1]; // qcursor.cpp
+
+/*****************************************************************************
+ Internal QCursorData class
+ *****************************************************************************/
+
+QCursorData::QCursorData(Qt::CursorShape s)
+ : cshape(s), bm(0), bmm(0), hx(0), hy(0), hcurs(0)
+{
+ ref = 1;
+}
+
+QCursorData::~QCursorData()
+{
+ delete bm;
+ delete bmm;
+#if !defined(Q_OS_WINCE) || defined(GWES_ICONCURS)
+ if (hcurs)
+ DestroyCursor(hcurs);
+#endif
+}
+
+
+QCursorData *QCursorData::setBitmap(const QBitmap &bitmap, const QBitmap &mask, int hotX, int hotY)
+{
+ if (!QCursorData::initialized)
+ QCursorData::initialize();
+ if (bitmap.depth() != 1 || mask.depth() != 1 || bitmap.size() != mask.size()) {
+ qWarning("QCursor: Cannot create bitmap cursor; invalid bitmap(s)");
+ QCursorData *c = qt_cursorTable[0];
+ c->ref.ref();
+ return c;
+ }
+ QCursorData *d = new QCursorData;
+ d->bm = new QBitmap(bitmap);
+ d->bmm = new QBitmap(mask);
+ d->hcurs = 0;
+ d->cshape = Qt::BitmapCursor;
+ d->hx = hotX >= 0 ? hotX : bitmap.width()/2;
+ d->hy = hotY >= 0 ? hotY : bitmap.height()/2;
+ return d;
+}
+
+HCURSOR QCursor::handle() const
+{
+ if (!QCursorData::initialized)
+ QCursorData::initialize();
+ if (!d->hcurs)
+ d->update();
+ return d->hcurs;
+}
+
+QCursor::QCursor(HCURSOR handle)
+{
+ d = new QCursorData(Qt::CustomCursor);
+ d->hcurs = handle;
+}
+
+#endif //QT_NO_CURSOR
+
+QPoint QCursor::pos()
+{
+ POINT p;
+ GetCursorPos(&p);
+ return QPoint(p.x, p.y);
+}
+
+void QCursor::setPos(int x, int y)
+{
+ SetCursorPos(x, y);
+}
+
+#ifndef QT_NO_CURSOR
+
+extern HBITMAP qt_createIconMask(const QBitmap &bitmap);
+
+static HCURSOR create32BitCursor(const QPixmap &pixmap, int hx, int hy)
+{
+ HCURSOR cur = 0;
+#if !defined(Q_OS_WINCE)
+ QBitmap mask = pixmap.mask();
+ if (mask.isNull()) {
+ mask = QBitmap(pixmap.size());
+ mask.fill(Qt::color1);
+ }
+
+ HBITMAP ic = pixmap.toWinHBITMAP(QPixmap::Alpha);
+ HBITMAP im = qt_createIconMask(mask);
+
+ ICONINFO ii;
+ ii.fIcon = 0;
+ ii.xHotspot = hx;
+ ii.yHotspot = hy;
+ ii.hbmMask = im;
+ ii.hbmColor = ic;
+
+ cur = CreateIconIndirect(&ii);
+
+ DeleteObject(ic);
+ DeleteObject(im);
+#elif defined(GWES_ICONCURS)
+ QImage bbits, mbits;
+ bool invb, invm;
+ bbits = pixmap.toImage().convertToFormat(QImage::Format_Mono);
+ mbits = pixmap.toImage().convertToFormat(QImage::Format_Mono);
+ invb = bbits.numColors() > 1 && qGray(bbits.color(0)) < qGray(bbits.color(1));
+ invm = mbits.numColors() > 1 && qGray(mbits.color(0)) < qGray(mbits.color(1));
+
+ int sysW = GetSystemMetrics(SM_CXCURSOR);
+ int sysH = GetSystemMetrics(SM_CYCURSOR);
+ int sysN = qMax(1, sysW / 8);
+ int n = qMax(1, bbits.width() / 8);
+ int h = bbits.height();
+
+ uchar* xBits = new uchar[sysH * sysN];
+ uchar* xMask = new uchar[sysH * sysN];
+ int x = 0;
+ for (int i = 0; i < sysH; ++i) {
+ if (i >= h) {
+ memset(&xBits[x] , 255, sysN);
+ memset(&xMask[x] , 0, sysN);
+ x += sysN;
+ } else {
+ int fillWidth = n > sysN ? sysN : n;
+ uchar *bits = bbits.scanLine(i);
+ uchar *mask = mbits.scanLine(i);
+ for (int j = 0; j < fillWidth; ++j) {
+ uchar b = bits[j];
+ uchar m = mask[j];
+ if (invb)
+ b ^= 0xFF;
+ if (invm)
+ m ^= 0xFF;
+ xBits[x] = ~m;
+ xMask[x] = b ^ m;
+ ++x;
+ }
+ for (int j = fillWidth; j < sysN; ++j ) {
+ xBits[x] = 255;
+ xMask[x] = 0;
+ ++x;
+ }
+ }
+ }
+
+ cur = CreateCursor(qWinAppInst(), hx, hy, sysW, sysH,
+ xBits, xMask);
+#else
+ Q_UNUSED(pixmap);
+ Q_UNUSED(hx);
+ Q_UNUSED(hy);
+#endif
+ return cur;
+}
+
+void QCursorData::update()
+{
+ if (!QCursorData::initialized)
+ QCursorData::initialize();
+ if (hcurs)
+ return;
+
+ if (cshape == Qt::BitmapCursor && !pixmap.isNull()) {
+ hcurs = create32BitCursor(pixmap, hx, hy);
+ if (hcurs)
+ return;
+ }
+
+
+ // Non-standard Windows cursors are created from bitmaps
+
+ static const uchar vsplit_bits[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x80, 0x00, 0x00, 0x00, 0xc0, 0x01, 0x00, 0x00, 0xe0, 0x03, 0x00,
+ 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00,
+ 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0xff, 0x7f, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x7f, 0x00,
+ 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00,
+ 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0xe0, 0x03, 0x00,
+ 0x00, 0xc0, 0x01, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+ static const uchar vsplitm_bits[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00,
+ 0x00, 0xc0, 0x01, 0x00, 0x00, 0xe0, 0x03, 0x00, 0x00, 0xf0, 0x07, 0x00,
+ 0x00, 0xf8, 0x0f, 0x00, 0x00, 0xc0, 0x01, 0x00, 0x00, 0xc0, 0x01, 0x00,
+ 0x00, 0xc0, 0x01, 0x00, 0x80, 0xff, 0xff, 0x00, 0x80, 0xff, 0xff, 0x00,
+ 0x80, 0xff, 0xff, 0x00, 0x80, 0xff, 0xff, 0x00, 0x80, 0xff, 0xff, 0x00,
+ 0x80, 0xff, 0xff, 0x00, 0x00, 0xc0, 0x01, 0x00, 0x00, 0xc0, 0x01, 0x00,
+ 0x00, 0xc0, 0x01, 0x00, 0x00, 0xf8, 0x0f, 0x00, 0x00, 0xf0, 0x07, 0x00,
+ 0x00, 0xe0, 0x03, 0x00, 0x00, 0xc0, 0x01, 0x00, 0x00, 0x80, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+ static const uchar hsplit_bits[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x02, 0x00, 0x00, 0x40, 0x02, 0x00,
+ 0x00, 0x40, 0x02, 0x00, 0x00, 0x40, 0x02, 0x00, 0x00, 0x40, 0x02, 0x00,
+ 0x00, 0x41, 0x82, 0x00, 0x80, 0x41, 0x82, 0x01, 0xc0, 0x7f, 0xfe, 0x03,
+ 0x80, 0x41, 0x82, 0x01, 0x00, 0x41, 0x82, 0x00, 0x00, 0x40, 0x02, 0x00,
+ 0x00, 0x40, 0x02, 0x00, 0x00, 0x40, 0x02, 0x00, 0x00, 0x40, 0x02, 0x00,
+ 0x00, 0x40, 0x02, 0x00, 0x00, 0x40, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+ static const uchar hsplitm_bits[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0xe0, 0x07, 0x00, 0x00, 0xe0, 0x07, 0x00, 0x00, 0xe0, 0x07, 0x00,
+ 0x00, 0xe0, 0x07, 0x00, 0x00, 0xe2, 0x47, 0x00, 0x00, 0xe3, 0xc7, 0x00,
+ 0x80, 0xe3, 0xc7, 0x01, 0xc0, 0xff, 0xff, 0x03, 0xe0, 0xff, 0xff, 0x07,
+ 0xc0, 0xff, 0xff, 0x03, 0x80, 0xe3, 0xc7, 0x01, 0x00, 0xe3, 0xc7, 0x00,
+ 0x00, 0xe2, 0x47, 0x00, 0x00, 0xe0, 0x07, 0x00, 0x00, 0xe0, 0x07, 0x00,
+ 0x00, 0xe0, 0x07, 0x00, 0x00, 0xe0, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+ static const uchar phand_bits[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x80, 0x04, 0x00, 0x00,
+ 0x80, 0x04, 0x00, 0x00, 0x80, 0x04, 0x00, 0x00, 0x80, 0x04, 0x00, 0x00,
+ 0x80, 0x1c, 0x00, 0x00, 0x80, 0xe4, 0x00, 0x00, 0x80, 0x24, 0x03, 0x00,
+ 0x80, 0x24, 0x05, 0x00, 0xb8, 0x24, 0x09, 0x00, 0xc8, 0x00, 0x09, 0x00,
+ 0x88, 0x00, 0x08, 0x00, 0x90, 0x00, 0x08, 0x00, 0xa0, 0x00, 0x08, 0x00,
+ 0x20, 0x00, 0x08, 0x00, 0x40, 0x00, 0x08, 0x00, 0x40, 0x00, 0x04, 0x00,
+ 0x80, 0x00, 0x04, 0x00, 0x80, 0x00, 0x04, 0x00, 0x00, 0x01, 0x02, 0x00,
+ 0x00, 0x01, 0x02, 0x00, 0x00, 0xff, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+
+ static const uchar phandm_bits[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x80, 0x07, 0x00, 0x00,
+ 0x80, 0x07, 0x00, 0x00, 0x80, 0x07, 0x00, 0x00, 0x80, 0x07, 0x00, 0x00,
+ 0x80, 0x1f, 0x00, 0x00, 0x80, 0xff, 0x00, 0x00, 0x80, 0xff, 0x03, 0x00,
+ 0x80, 0xff, 0x07, 0x00, 0xb8, 0xff, 0x0f, 0x00, 0xf8, 0xff, 0x0f, 0x00,
+ 0xf8, 0xff, 0x0f, 0x00, 0xf0, 0xff, 0x0f, 0x00, 0xe0, 0xff, 0x0f, 0x00,
+ 0xe0, 0xff, 0x0f, 0x00, 0xc0, 0xff, 0x0f, 0x00, 0xc0, 0xff, 0x07, 0x00,
+ 0x80, 0xff, 0x07, 0x00, 0x80, 0xff, 0x07, 0x00, 0x00, 0xff, 0x03, 0x00,
+ 0x00, 0xff, 0x03, 0x00, 0x00, 0xff, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+
+ static const uchar openhand_bits[] = {
+ 0x80,0x01,0x58,0x0e,0x64,0x12,0x64,0x52,0x48,0xb2,0x48,0x92,
+ 0x16,0x90,0x19,0x80,0x11,0x40,0x02,0x40,0x04,0x40,0x04,0x20,
+ 0x08,0x20,0x10,0x10,0x20,0x10,0x00,0x00};
+ static const uchar openhandm_bits[] = {
+ 0x80,0x01,0xd8,0x0f,0xfc,0x1f,0xfc,0x5f,0xf8,0xff,0xf8,0xff,
+ 0xf6,0xff,0xff,0xff,0xff,0x7f,0xfe,0x7f,0xfc,0x7f,0xfc,0x3f,
+ 0xf8,0x3f,0xf0,0x1f,0xe0,0x1f,0x00,0x00};
+ static const uchar closedhand_bits[] = {
+ 0x00,0x00,0x00,0x00,0x00,0x00,0xb0,0x0d,0x48,0x32,0x08,0x50,
+ 0x10,0x40,0x18,0x40,0x04,0x40,0x04,0x20,0x08,0x20,0x10,0x10,
+ 0x20,0x10,0x20,0x10,0x00,0x00,0x00,0x00};
+ static const uchar closedhandm_bits[] = {
+ 0x00,0x00,0x00,0x00,0x00,0x00,0xb0,0x0d,0xf8,0x3f,0xf8,0x7f,
+ 0xf0,0x7f,0xf8,0x7f,0xfc,0x7f,0xfc,0x3f,0xf8,0x3f,0xf0,0x1f,
+ 0xe0,0x1f,0xe0,0x1f,0x00,0x00,0x00,0x00};
+
+ static const uchar * const cursor_bits32[] = {
+ vsplit_bits, vsplitm_bits, hsplit_bits, hsplitm_bits,
+ phand_bits, phandm_bits
+ };
+
+ unsigned short *sh;
+ switch (cshape) { // map to windows cursor
+ case Qt::ArrowCursor:
+ sh = (unsigned short*)IDC_ARROW;
+ break;
+ case Qt::UpArrowCursor:
+ sh = (unsigned short*)IDC_UPARROW;
+ break;
+ case Qt::CrossCursor:
+ sh = (unsigned short*)IDC_CROSS;
+ break;
+ case Qt::WaitCursor:
+ sh = (unsigned short*)IDC_WAIT;
+ break;
+ case Qt::IBeamCursor:
+ sh = (unsigned short*)IDC_IBEAM;
+ break;
+ case Qt::SizeVerCursor:
+ sh = (unsigned short*)IDC_SIZENS;
+ break;
+ case Qt::SizeHorCursor:
+ sh = (unsigned short*)IDC_SIZEWE;
+ break;
+ case Qt::SizeBDiagCursor:
+ sh = (unsigned short*)IDC_SIZENESW;
+ break;
+ case Qt::SizeFDiagCursor:
+ sh = (unsigned short*)IDC_SIZENWSE;
+ break;
+ case Qt::SizeAllCursor:
+ sh = (unsigned short*)IDC_SIZEALL;
+ break;
+ case Qt::ForbiddenCursor:
+ sh = (unsigned short*)IDC_NO;
+ break;
+ case Qt::WhatsThisCursor:
+ sh = (unsigned short*)IDC_HELP;
+ break;
+ case Qt::BusyCursor:
+ sh = (unsigned short*)IDC_APPSTARTING;
+ break;
+ case Qt::PointingHandCursor:
+ if ((QSysInfo::WindowsVersion & QSysInfo::WV_DOS_based) > QSysInfo::WV_95 ||
+ (QSysInfo::WindowsVersion & QSysInfo::WV_NT_based) > QSysInfo::WV_NT) {
+ sh = (unsigned short*)IDC_HAND;
+ break;
+ }
+ // fall through
+ case Qt::BlankCursor:
+ case Qt::SplitVCursor:
+ case Qt::SplitHCursor:
+ case Qt::OpenHandCursor:
+ case Qt::ClosedHandCursor:
+ case Qt::BitmapCursor: {
+ QImage bbits, mbits;
+ bool invb, invm;
+ if (cshape == Qt::BlankCursor) {
+ bbits = QImage(32, 32, QImage::Format_Mono);
+ bbits.fill(0); // ignore color table
+ mbits = bbits.copy();
+ hx = hy = 16;
+ invb = invm = false;
+ } else if (cshape == Qt::OpenHandCursor || cshape == Qt::ClosedHandCursor) {
+ bool open = cshape == Qt::OpenHandCursor;
+ QBitmap cb = QBitmap::fromData(QSize(16, 16), open ? openhand_bits : closedhand_bits);
+ QBitmap cm = QBitmap::fromData(QSize(16, 16), open ? openhandm_bits : closedhandm_bits);
+ bbits = cb.toImage().convertToFormat(QImage::Format_Mono);
+ mbits = cm.toImage().convertToFormat(QImage::Format_Mono);
+ hx = hy = 8;
+ invb = invm = false;
+ } else if (cshape != Qt::BitmapCursor) {
+ int i = cshape - Qt::SplitVCursor;
+ QBitmap cb = QBitmap::fromData(QSize(32, 32), cursor_bits32[i * 2]);
+ QBitmap cm = QBitmap::fromData(QSize(32, 32), cursor_bits32[i * 2 + 1]);
+ bbits = cb.toImage().convertToFormat(QImage::Format_Mono);
+ mbits = cm.toImage().convertToFormat(QImage::Format_Mono);
+ if (cshape == Qt::PointingHandCursor) {
+ hx = 7;
+ hy = 0;
+ } else
+ hx = hy = 16;
+ invb = invm = false;
+ } else {
+ bbits = bm->toImage().convertToFormat(QImage::Format_Mono);
+ mbits = bmm->toImage().convertToFormat(QImage::Format_Mono);
+ invb = bbits.numColors() > 1 && qGray(bbits.color(0)) < qGray(bbits.color(1));
+ invm = mbits.numColors() > 1 && qGray(mbits.color(0)) < qGray(mbits.color(1));
+ }
+ int n = qMax(1, bbits.width() / 8);
+ int h = bbits.height();
+#if !defined(Q_OS_WINCE)
+ uchar* xBits = new uchar[h * n];
+ uchar* xMask = new uchar[h * n];
+ int x = 0;
+ for (int i = 0; i < h; ++i) {
+ uchar *bits = bbits.scanLine(i);
+ uchar *mask = mbits.scanLine(i);
+ for (int j = 0; j < n; ++j) {
+ uchar b = bits[j];
+ uchar m = mask[j];
+ if (invb)
+ b ^= 0xff;
+ if (invm)
+ m ^= 0xff;
+ xBits[x] = ~m;
+ xMask[x] = b ^ m;
+ ++x;
+ }
+ }
+ hcurs = CreateCursor(qWinAppInst(), hx, hy, bbits.width(), bbits.height(),
+ xBits, xMask);
+ delete [] xBits;
+ delete [] xMask;
+#elif defined(GWES_ICONCURS) // Q_OS_WINCE
+ // Windows CE only supports fixed cursor size.
+ int sysW = GetSystemMetrics(SM_CXCURSOR);
+ int sysH = GetSystemMetrics(SM_CYCURSOR);
+ int sysN = qMax(1, sysW / 8);
+ uchar* xBits = new uchar[sysH * sysN];
+ uchar* xMask = new uchar[sysH * sysN];
+ int x = 0;
+ for (int i = 0; i < sysH; ++i) {
+ if (i >= h) {
+ memset(&xBits[x] , 255, sysN);
+ memset(&xMask[x] , 0, sysN);
+ x += sysN;
+ } else {
+ int fillWidth = n > sysN ? sysN : n;
+ uchar *bits = bbits.scanLine(i);
+ uchar *mask = mbits.scanLine(i);
+ for (int j = 0; j < fillWidth; ++j) {
+ uchar b = bits[j];
+ uchar m = mask[j];
+ if (invb)
+ b ^= 0xFF;
+ if (invm)
+ m ^= 0xFF;
+ xBits[x] = ~m;
+ xMask[x] = b ^ m;
+ ++x;
+ }
+ for (int j = fillWidth; j < sysN; ++j ) {
+ xBits[x] = 255;
+ xMask[x] = 0;
+ ++x;
+ }
+ }
+ }
+
+ hcurs = CreateCursor(qWinAppInst(), hx, hy, sysW, sysH,
+ xBits, xMask);
+ delete [] xBits;
+ delete [] xMask;
+#else
+ Q_UNUSED(n);
+ Q_UNUSED(h);
+#endif
+ return;
+ }
+ default:
+ qWarning("QCursor::update: Invalid cursor shape %d", cshape);
+ return;
+ }
+ // ### From MSDN:
+ // ### LoadCursor has been superseded by the LoadImage function.
+ QT_WA({
+ hcurs = LoadCursorW(0, reinterpret_cast<const TCHAR *>(sh));
+ } , {
+ hcurs = LoadCursorA(0, reinterpret_cast<const char*>(sh));
+ });
+}
+
+QT_END_NAMESPACE
+#endif // QT_NO_CURSOR
diff --git a/src/gui/kernel/qcursor_x11.cpp b/src/gui/kernel/qcursor_x11.cpp
new file mode 100644
index 0000000000..252b4ee584
--- /dev/null
+++ b/src/gui/kernel/qcursor_x11.cpp
@@ -0,0 +1,594 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <qdatastream.h>
+#include <private/qcursor_p.h>
+#include <private/qt_x11_p.h>
+#include <qbitmap.h>
+#include <qcursor.h>
+#include <X11/cursorfont.h>
+
+#include <qlibrary.h>
+
+#ifndef QT_NO_XCURSOR
+# include <X11/Xcursor/Xcursor.h>
+#endif // QT_NO_XCURSOR
+
+#ifndef QT_NO_XFIXES
+# include <X11/extensions/Xfixes.h>
+#endif // QT_NO_XFIXES
+
+#include "qx11info_x11.h"
+
+QT_BEGIN_NAMESPACE
+
+// Define QT_USE_APPROXIMATE_CURSORS when compiling if you REALLY want to
+// use the ugly X11 cursors.
+
+extern QCursorData *qt_cursorTable[Qt::LastCursor + 1]; // qcursor.cpp
+
+/*****************************************************************************
+ Internal QCursorData class
+ *****************************************************************************/
+
+QCursorData::QCursorData(Qt::CursorShape s)
+ : cshape(s), bm(0), bmm(0), hx(0), hy(0), hcurs(0), pm(0), pmm(0)
+{
+ ref = 1;
+}
+
+QCursorData::~QCursorData()
+{
+ Display *dpy = X11 ? X11->display : (Display*)0;
+
+ // Add in checking for the display too as on HP-UX
+ // we seem to get a core dump as the cursor data is
+ // deleted again from main() on exit...
+ if (hcurs && dpy)
+ XFreeCursor(dpy, hcurs);
+ if (pm && dpy)
+ XFreePixmap(dpy, pm);
+ if (pmm && dpy)
+ XFreePixmap(dpy, pmm);
+ delete bm;
+ delete bmm;
+}
+
+#ifndef QT_NO_CURSOR
+QCursor::QCursor(Qt::HANDLE cursor)
+{
+ if (!QCursorData::initialized)
+ QCursorData::initialize();
+ d = new QCursorData(Qt::CustomCursor);
+ d->hcurs = cursor;
+}
+#endif
+
+QCursorData *QCursorData::setBitmap(const QBitmap &bitmap, const QBitmap &mask, int hotX, int hotY)
+{
+ if (!QCursorData::initialized)
+ QCursorData::initialize();
+ if (bitmap.depth() != 1 || mask.depth() != 1 || bitmap.size() != mask.size()) {
+ qWarning("QCursor: Cannot create bitmap cursor; invalid bitmap(s)");
+ QCursorData *c = qt_cursorTable[0];
+ c->ref.ref();
+ return c;
+ }
+ QCursorData *d = new QCursorData;
+ d->ref = 1;
+
+ extern QPixmap qt_toX11Pixmap(const QPixmap &pixmap); // qpixmap_x11.cpp
+ d->bm = new QBitmap(qt_toX11Pixmap(bitmap));
+ d->bmm = new QBitmap(qt_toX11Pixmap(mask));
+
+ d->hcurs = 0;
+ d->cshape = Qt::BitmapCursor;
+ d->hx = hotX >= 0 ? hotX : bitmap.width() / 2;
+ d->hy = hotY >= 0 ? hotY : bitmap.height() / 2;
+ d->fg.red = 0x0000;
+ d->fg.green = 0x0000;
+ d->fg.blue = 0x0000;
+ d->bg.red = 0xffff;
+ d->bg.green = 0xffff;
+ d->bg.blue = 0xffff;
+ return d;
+}
+
+
+
+#ifndef QT_NO_CURSOR
+Qt::HANDLE QCursor::handle() const
+{
+ if (!QCursorData::initialized)
+ QCursorData::initialize();
+ if (!d->hcurs)
+ d->update();
+ return d->hcurs;
+}
+#endif
+
+QPoint QCursor::pos()
+{
+ Window root;
+ Window child;
+ int root_x, root_y, win_x, win_y;
+ uint buttons;
+ Display* dpy = X11->display;
+ for (int i = 0; i < ScreenCount(dpy); ++i) {
+ if (XQueryPointer(dpy, QX11Info::appRootWindow(i), &root, &child, &root_x, &root_y,
+ &win_x, &win_y, &buttons))
+
+ return QPoint(root_x, root_y);
+ }
+ return QPoint();
+}
+
+/*! \internal
+*/
+#ifndef QT_NO_CURSOR
+int QCursor::x11Screen()
+{
+ Window root;
+ Window child;
+ int root_x, root_y, win_x, win_y;
+ uint buttons;
+ Display* dpy = X11->display;
+ for (int i = 0; i < ScreenCount(dpy); ++i) {
+ if (XQueryPointer(dpy, QX11Info::appRootWindow(i), &root, &child, &root_x, &root_y,
+ &win_x, &win_y, &buttons))
+ return i;
+ }
+ return -1;
+}
+#endif
+
+void QCursor::setPos(int x, int y)
+{
+ QPoint current, target(x, y);
+
+ // this is copied from pos(), since we need the screen number for the correct
+ // root window in the XWarpPointer call
+ Window root;
+ Window child;
+ int root_x, root_y, win_x, win_y;
+ uint buttons;
+ Display* dpy = X11->display;
+ int screen;
+ for (screen = 0; screen < ScreenCount(dpy); ++screen) {
+ if (XQueryPointer(dpy, QX11Info::appRootWindow(screen), &root, &child, &root_x, &root_y,
+ &win_x, &win_y, &buttons)) {
+ current = QPoint(root_x, root_y);
+ break;
+ }
+ }
+
+ if (screen >= ScreenCount(dpy))
+ return;
+
+ // Need to check, since some X servers generate null mouse move
+ // events, causing looping in applications which call setPos() on
+ // every mouse move event.
+ //
+ if (current == target)
+ return;
+
+ XWarpPointer(X11->display, XNone, QX11Info::appRootWindow(screen), 0, 0, 0, 0, x, y);
+}
+
+
+/*!
+ \internal
+
+ Creates the cursor.
+*/
+
+void QCursorData::update()
+{
+ if (!QCursorData::initialized)
+ QCursorData::initialize();
+ if (hcurs)
+ return;
+
+ Display *dpy = X11->display;
+ Window rootwin = QX11Info::appRootWindow();
+
+ if (cshape == Qt::BitmapCursor) {
+ extern QPixmap qt_toX11Pixmap(const QPixmap &pixmap); // qpixmap_x11.cpp
+#ifndef QT_NO_XRENDER
+ if (!pixmap.isNull() && X11->use_xrender) {
+ pixmap = qt_toX11Pixmap(pixmap);
+ hcurs = XRenderCreateCursor (X11->display, pixmap.x11PictureHandle(), hx, hy);
+ } else
+#endif
+ {
+ hcurs = XCreatePixmapCursor(dpy, bm->handle(), bmm->handle(), &fg, &bg, hx, hy);
+ }
+ return;
+ }
+
+ static const char *cursorNames[] = {
+ "left_ptr",
+ "up_arrow",
+ "cross",
+ "wait",
+ "ibeam",
+ "size_ver",
+ "size_hor",
+ "size_bdiag",
+ "size_fdiag",
+ "size_all",
+ "blank",
+ "split_v",
+ "split_h",
+ "pointing_hand",
+ "forbidden",
+ "whats_this",
+ "left_ptr_watch",
+ "openhand",
+ "closedhand"
+ };
+
+#ifndef QT_NO_XCURSOR
+ if (X11->ptrXcursorLibraryLoadCursor)
+ hcurs = X11->ptrXcursorLibraryLoadCursor(dpy, cursorNames[cshape]);
+ if (hcurs)
+ return;
+#endif // QT_NO_XCURSOR
+
+ static const char cur_blank_bits[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+
+ // Non-standard X11 cursors are created from bitmaps
+
+#ifndef QT_USE_APPROXIMATE_CURSORS
+ static const char cur_ver_bits[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x80, 0x01, 0xc0, 0x03, 0xe0, 0x07, 0xf0, 0x0f,
+ 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0xf0, 0x0f,
+ 0xe0, 0x07, 0xc0, 0x03, 0x80, 0x01, 0x00, 0x00 };
+ static const char mcur_ver_bits[] = {
+ 0x00, 0x00, 0x80, 0x03, 0xc0, 0x07, 0xe0, 0x0f, 0xf0, 0x1f, 0xf8, 0x3f,
+ 0xfc, 0x7f, 0xc0, 0x07, 0xc0, 0x07, 0xc0, 0x07, 0xfc, 0x7f, 0xf8, 0x3f,
+ 0xf0, 0x1f, 0xe0, 0x0f, 0xc0, 0x07, 0x80, 0x03 };
+ static const char cur_hor_bits[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x08, 0x30, 0x18,
+ 0x38, 0x38, 0xfc, 0x7f, 0xfc, 0x7f, 0x38, 0x38, 0x30, 0x18, 0x20, 0x08,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+ static const char mcur_hor_bits[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x40, 0x04, 0x60, 0x0c, 0x70, 0x1c, 0x78, 0x3c,
+ 0xfc, 0x7f, 0xfe, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xfc, 0x7f, 0x78, 0x3c,
+ 0x70, 0x1c, 0x60, 0x0c, 0x40, 0x04, 0x00, 0x00 };
+ static const char cur_bdiag_bits[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x3e, 0x00, 0x3c, 0x00, 0x3e,
+ 0x00, 0x37, 0x88, 0x23, 0xd8, 0x01, 0xf8, 0x00, 0x78, 0x00, 0xf8, 0x00,
+ 0xf8, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+ static const char mcur_bdiag_bits[] = {
+ 0x00, 0x00, 0xc0, 0x7f, 0x80, 0x7f, 0x00, 0x7f, 0x00, 0x7e, 0x04, 0x7f,
+ 0x8c, 0x7f, 0xdc, 0x77, 0xfc, 0x63, 0xfc, 0x41, 0xfc, 0x00, 0xfc, 0x01,
+ 0xfc, 0x03, 0xfc, 0x07, 0x00, 0x00, 0x00, 0x00 };
+ static const char cur_fdiag_bits[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x01, 0xf8, 0x00, 0x78, 0x00,
+ 0xf8, 0x00, 0xd8, 0x01, 0x88, 0x23, 0x00, 0x37, 0x00, 0x3e, 0x00, 0x3c,
+ 0x00, 0x3e, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x00 };
+ static const char mcur_fdiag_bits[] = {
+ 0x00, 0x00, 0x00, 0x00, 0xfc, 0x07, 0xfc, 0x03, 0xfc, 0x01, 0xfc, 0x00,
+ 0xfc, 0x41, 0xfc, 0x63, 0xdc, 0x77, 0x8c, 0x7f, 0x04, 0x7f, 0x00, 0x7e,
+ 0x00, 0x7f, 0x80, 0x7f, 0xc0, 0x7f, 0x00, 0x00 };
+ static const char *cursor_bits16[] = {
+ cur_ver_bits, mcur_ver_bits, cur_hor_bits, mcur_hor_bits,
+ cur_bdiag_bits, mcur_bdiag_bits, cur_fdiag_bits, mcur_fdiag_bits,
+ 0, 0, cur_blank_bits, cur_blank_bits };
+
+ static const char vsplit_bits[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x80, 0x00, 0x00, 0x00, 0xc0, 0x01, 0x00, 0x00, 0xe0, 0x03, 0x00,
+ 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00,
+ 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0xff, 0x7f, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x7f, 0x00,
+ 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00,
+ 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0xe0, 0x03, 0x00,
+ 0x00, 0xc0, 0x01, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+ static const char vsplitm_bits[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00,
+ 0x00, 0xc0, 0x01, 0x00, 0x00, 0xe0, 0x03, 0x00, 0x00, 0xf0, 0x07, 0x00,
+ 0x00, 0xf8, 0x0f, 0x00, 0x00, 0xc0, 0x01, 0x00, 0x00, 0xc0, 0x01, 0x00,
+ 0x00, 0xc0, 0x01, 0x00, 0x80, 0xff, 0xff, 0x00, 0x80, 0xff, 0xff, 0x00,
+ 0x80, 0xff, 0xff, 0x00, 0x80, 0xff, 0xff, 0x00, 0x80, 0xff, 0xff, 0x00,
+ 0x80, 0xff, 0xff, 0x00, 0x00, 0xc0, 0x01, 0x00, 0x00, 0xc0, 0x01, 0x00,
+ 0x00, 0xc0, 0x01, 0x00, 0x00, 0xf8, 0x0f, 0x00, 0x00, 0xf0, 0x07, 0x00,
+ 0x00, 0xe0, 0x03, 0x00, 0x00, 0xc0, 0x01, 0x00, 0x00, 0x80, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+ static const char hsplit_bits[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x02, 0x00, 0x00, 0x40, 0x02, 0x00,
+ 0x00, 0x40, 0x02, 0x00, 0x00, 0x40, 0x02, 0x00, 0x00, 0x40, 0x02, 0x00,
+ 0x00, 0x41, 0x82, 0x00, 0x80, 0x41, 0x82, 0x01, 0xc0, 0x7f, 0xfe, 0x03,
+ 0x80, 0x41, 0x82, 0x01, 0x00, 0x41, 0x82, 0x00, 0x00, 0x40, 0x02, 0x00,
+ 0x00, 0x40, 0x02, 0x00, 0x00, 0x40, 0x02, 0x00, 0x00, 0x40, 0x02, 0x00,
+ 0x00, 0x40, 0x02, 0x00, 0x00, 0x40, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+ static const char hsplitm_bits[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0xe0, 0x07, 0x00, 0x00, 0xe0, 0x07, 0x00, 0x00, 0xe0, 0x07, 0x00,
+ 0x00, 0xe0, 0x07, 0x00, 0x00, 0xe2, 0x47, 0x00, 0x00, 0xe3, 0xc7, 0x00,
+ 0x80, 0xe3, 0xc7, 0x01, 0xc0, 0xff, 0xff, 0x03, 0xe0, 0xff, 0xff, 0x07,
+ 0xc0, 0xff, 0xff, 0x03, 0x80, 0xe3, 0xc7, 0x01, 0x00, 0xe3, 0xc7, 0x00,
+ 0x00, 0xe2, 0x47, 0x00, 0x00, 0xe0, 0x07, 0x00, 0x00, 0xe0, 0x07, 0x00,
+ 0x00, 0xe0, 0x07, 0x00, 0x00, 0xe0, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+ static const char whatsthis_bits[] = {
+ 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x05, 0xf0, 0x07, 0x00,
+ 0x09, 0x18, 0x0e, 0x00, 0x11, 0x1c, 0x0e, 0x00, 0x21, 0x1c, 0x0e, 0x00,
+ 0x41, 0x1c, 0x0e, 0x00, 0x81, 0x1c, 0x0e, 0x00, 0x01, 0x01, 0x07, 0x00,
+ 0x01, 0x82, 0x03, 0x00, 0xc1, 0xc7, 0x01, 0x00, 0x49, 0xc0, 0x01, 0x00,
+ 0x95, 0xc0, 0x01, 0x00, 0x93, 0xc0, 0x01, 0x00, 0x21, 0x01, 0x00, 0x00,
+ 0x20, 0xc1, 0x01, 0x00, 0x40, 0xc2, 0x01, 0x00, 0x40, 0x02, 0x00, 0x00,
+ 0x80, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, };
+ static const char whatsthism_bits[] = {
+ 0x01, 0x00, 0x00, 0x00, 0x03, 0xf0, 0x07, 0x00, 0x07, 0xf8, 0x0f, 0x00,
+ 0x0f, 0xfc, 0x1f, 0x00, 0x1f, 0x3e, 0x1f, 0x00, 0x3f, 0x3e, 0x1f, 0x00,
+ 0x7f, 0x3e, 0x1f, 0x00, 0xff, 0x3e, 0x1f, 0x00, 0xff, 0x9d, 0x0f, 0x00,
+ 0xff, 0xc3, 0x07, 0x00, 0xff, 0xe7, 0x03, 0x00, 0x7f, 0xe0, 0x03, 0x00,
+ 0xf7, 0xe0, 0x03, 0x00, 0xf3, 0xe0, 0x03, 0x00, 0xe1, 0xe1, 0x03, 0x00,
+ 0xe0, 0xe1, 0x03, 0x00, 0xc0, 0xe3, 0x03, 0x00, 0xc0, 0xe3, 0x03, 0x00,
+ 0x80, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, };
+ static const char busy_bits[] = {
+ 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00,
+ 0x09, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00,
+ 0x41, 0xe0, 0xff, 0x00, 0x81, 0x20, 0x80, 0x00, 0x01, 0xe1, 0xff, 0x00,
+ 0x01, 0x42, 0x40, 0x00, 0xc1, 0x47, 0x40, 0x00, 0x49, 0x40, 0x55, 0x00,
+ 0x95, 0x80, 0x2a, 0x00, 0x93, 0x00, 0x15, 0x00, 0x21, 0x01, 0x0a, 0x00,
+ 0x20, 0x01, 0x11, 0x00, 0x40, 0x82, 0x20, 0x00, 0x40, 0x42, 0x44, 0x00,
+ 0x80, 0x41, 0x4a, 0x00, 0x00, 0x40, 0x55, 0x00, 0x00, 0xe0, 0xff, 0x00,
+ 0x00, 0x20, 0x80, 0x00, 0x00, 0xe0, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+ static const char busym_bits[] = {
+ 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00,
+ 0x0f, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00,
+ 0x7f, 0xe0, 0xff, 0x00, 0xff, 0xe0, 0xff, 0x00, 0xff, 0xe1, 0xff, 0x00,
+ 0xff, 0xc3, 0x7f, 0x00, 0xff, 0xc7, 0x7f, 0x00, 0x7f, 0xc0, 0x7f, 0x00,
+ 0xf7, 0x80, 0x3f, 0x00, 0xf3, 0x00, 0x1f, 0x00, 0xe1, 0x01, 0x0e, 0x00,
+ 0xe0, 0x01, 0x1f, 0x00, 0xc0, 0x83, 0x3f, 0x00, 0xc0, 0xc3, 0x7f, 0x00,
+ 0x80, 0xc1, 0x7f, 0x00, 0x00, 0xc0, 0x7f, 0x00, 0x00, 0xe0, 0xff, 0x00,
+ 0x00, 0xe0, 0xff, 0x00, 0x00, 0xe0, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+
+ static const char * const cursor_bits32[] = {
+ vsplit_bits, vsplitm_bits, hsplit_bits, hsplitm_bits,
+ 0, 0, 0, 0, whatsthis_bits, whatsthism_bits, busy_bits, busym_bits
+ };
+
+ static const char forbidden_bits[] = {
+ 0x00,0x00,0x00,0x80,0x1f,0x00,0xe0,0x7f,0x00,0xf0,0xf0,0x00,0x38,0xc0,0x01,
+ 0x7c,0x80,0x03,0xec,0x00,0x03,0xce,0x01,0x07,0x86,0x03,0x06,0x06,0x07,0x06,
+ 0x06,0x0e,0x06,0x06,0x1c,0x06,0x0e,0x38,0x07,0x0c,0x70,0x03,0x1c,0xe0,0x03,
+ 0x38,0xc0,0x01,0xf0,0xe0,0x00,0xe0,0x7f,0x00,0x80,0x1f,0x00,0x00,0x00,0x00 };
+
+ static const char forbiddenm_bits[] = {
+ 0x80,0x1f,0x00,0xe0,0x7f,0x00,0xf0,0xff,0x00,0xf8,0xff,0x01,0xfc,0xf0,0x03,
+ 0xfe,0xc0,0x07,0xfe,0x81,0x07,0xff,0x83,0x0f,0xcf,0x07,0x0f,0x8f,0x0f,0x0f,
+ 0x0f,0x1f,0x0f,0x0f,0x3e,0x0f,0x1f,0xfc,0x0f,0x1e,0xf8,0x07,0x3e,0xf0,0x07,
+ 0xfc,0xe0,0x03,0xf8,0xff,0x01,0xf0,0xff,0x00,0xe0,0x7f,0x00,0x80,0x1f,0x00};
+
+ static const char openhand_bits[] = {
+ 0x80,0x01,0x58,0x0e,0x64,0x12,0x64,0x52,0x48,0xb2,0x48,0x92,
+ 0x16,0x90,0x19,0x80,0x11,0x40,0x02,0x40,0x04,0x40,0x04,0x20,
+ 0x08,0x20,0x10,0x10,0x20,0x10,0x00,0x00};
+ static const char openhandm_bits[] = {
+ 0x80,0x01,0xd8,0x0f,0xfc,0x1f,0xfc,0x5f,0xf8,0xff,0xf8,0xff,
+ 0xf6,0xff,0xff,0xff,0xff,0x7f,0xfe,0x7f,0xfc,0x7f,0xfc,0x3f,
+ 0xf8,0x3f,0xf0,0x1f,0xe0,0x1f,0x00,0x00};
+ static const char closedhand_bits[] = {
+ 0x00,0x00,0x00,0x00,0x00,0x00,0xb0,0x0d,0x48,0x32,0x08,0x50,
+ 0x10,0x40,0x18,0x40,0x04,0x40,0x04,0x20,0x08,0x20,0x10,0x10,
+ 0x20,0x10,0x20,0x10,0x00,0x00,0x00,0x00};
+ static const char closedhandm_bits[] = {
+ 0x00,0x00,0x00,0x00,0x00,0x00,0xb0,0x0d,0xf8,0x3f,0xf8,0x7f,
+ 0xf0,0x7f,0xf8,0x7f,0xfc,0x7f,0xfc,0x3f,0xf8,0x3f,0xf0,0x1f,
+ 0xe0,0x1f,0xe0,0x1f,0x00,0x00,0x00,0x00};
+
+ static const char * const cursor_bits20[] = {
+ forbidden_bits, forbiddenm_bits
+ };
+
+ if ((cshape >= Qt::SizeVerCursor && cshape < Qt::SizeAllCursor)
+ || cshape == Qt::BlankCursor) {
+ XColor bg, fg;
+ bg.red = 255 << 8;
+ bg.green = 255 << 8;
+ bg.blue = 255 << 8;
+ fg.red = 0;
+ fg.green = 0;
+ fg.blue = 0;
+ int i = (cshape - Qt::SizeVerCursor) * 2;
+ pm = XCreateBitmapFromData(dpy, rootwin, cursor_bits16[i], 16, 16);
+ pmm = XCreateBitmapFromData(dpy, rootwin, cursor_bits16[i + 1], 16, 16);
+ hcurs = XCreatePixmapCursor(dpy, pm, pmm, &fg, &bg, 8, 8);
+ } else if ((cshape >= Qt::SplitVCursor && cshape <= Qt::SplitHCursor)
+ || cshape == Qt::WhatsThisCursor || cshape == Qt::BusyCursor) {
+ XColor bg, fg;
+ bg.red = 255 << 8;
+ bg.green = 255 << 8;
+ bg.blue = 255 << 8;
+ fg.red = 0;
+ fg.green = 0;
+ fg.blue = 0;
+ int i = (cshape - Qt::SplitVCursor) * 2;
+ pm = XCreateBitmapFromData(dpy, rootwin, cursor_bits32[i], 32, 32);
+ pmm = XCreateBitmapFromData(dpy, rootwin, cursor_bits32[i + 1], 32, 32);
+ int hs = (cshape == Qt::PointingHandCursor || cshape == Qt::WhatsThisCursor
+ || cshape == Qt::BusyCursor) ? 0 : 16;
+ hcurs = XCreatePixmapCursor(dpy, pm, pmm, &fg, &bg, hs, hs);
+ } else if (cshape == Qt::ForbiddenCursor) {
+ XColor bg, fg;
+ bg.red = 255 << 8;
+ bg.green = 255 << 8;
+ bg.blue = 255 << 8;
+ fg.red = 0;
+ fg.green = 0;
+ fg.blue = 0;
+ int i = (cshape - Qt::ForbiddenCursor) * 2;
+ pm = XCreateBitmapFromData(dpy, rootwin, cursor_bits20[i], 20, 20);
+ pmm = XCreateBitmapFromData(dpy, rootwin, cursor_bits20[i + 1], 20, 20);
+ hcurs = XCreatePixmapCursor(dpy, pm, pmm, &fg, &bg, 10, 10);
+ } else if (cshape == Qt::OpenHandCursor || cshape == Qt::ClosedHandCursor) {
+ XColor bg, fg;
+ bg.red = 255 << 8;
+ bg.green = 255 << 8;
+ bg.blue = 255 << 8;
+ fg.red = 0;
+ fg.green = 0;
+ fg.blue = 0;
+ bool open = cshape == Qt::OpenHandCursor;
+ pm = XCreateBitmapFromData(dpy, rootwin, open ? openhand_bits : closedhand_bits, 16, 16);
+ pmm = XCreateBitmapFromData(dpy, rootwin, open ? openhandm_bits : closedhandm_bits, 16, 16);
+ hcurs = XCreatePixmapCursor(dpy, pm, pmm, &fg, &bg, 8, 8);
+ }
+
+ if (hcurs)
+ {
+#ifndef QT_NO_XFIXES
+ if (X11->use_xfixes && X11->ptrXFixesSetCursorName)
+ X11->ptrXFixesSetCursorName(dpy, hcurs, cursorNames[cshape]);
+#endif /* ! QT_NO_XFIXES */
+ return;
+ }
+
+#endif /* ! QT_USE_APPROXIMATE_CURSORS */
+
+ uint sh;
+ switch (cshape) { // map Q cursor to X cursor
+ case Qt::ArrowCursor:
+ sh = XC_left_ptr;
+ break;
+ case Qt::UpArrowCursor:
+ sh = XC_center_ptr;
+ break;
+ case Qt::CrossCursor:
+ sh = XC_crosshair;
+ break;
+ case Qt::WaitCursor:
+ sh = XC_watch;
+ break;
+ case Qt::IBeamCursor:
+ sh = XC_xterm;
+ break;
+ case Qt::SizeAllCursor:
+ sh = XC_fleur;
+ break;
+ case Qt::PointingHandCursor:
+ sh = XC_hand2;
+ break;
+#ifdef QT_USE_APPROXIMATE_CURSORS
+ case Qt::SizeBDiagCursor:
+ sh = XC_top_right_corner;
+ break;
+ case Qt::SizeFDiagCursor:
+ sh = XC_bottom_right_corner;
+ break;
+ case Qt::BlankCursor:
+ XColor bg, fg;
+ bg.red = 255 << 8;
+ bg.green = 255 << 8;
+ bg.blue = 255 << 8;
+ fg.red = 0;
+ fg.green = 0;
+ fg.blue = 0;
+ pm = XCreateBitmapFromData(dpy, rootwin, cur_blank_bits, 16, 16);
+ pmm = XCreateBitmapFromData(dpy, rootwin, cur_blank_bits, 16, 16);
+ hcurs = XCreatePixmapCursor(dpy, pm, pmm, &fg, &bg, 8, 8);
+ return;
+ break;
+ case Qt::SizeVerCursor:
+ case Qt::SplitVCursor:
+ sh = XC_sb_v_double_arrow;
+ break;
+ case Qt::SizeHorCursor:
+ case Qt::SplitHCursor:
+ sh = XC_sb_h_double_arrow;
+ break;
+ case Qt::WhatsThisCursor:
+ sh = XC_question_arrow;
+ break;
+ case Qt::ForbiddenCursor:
+ sh = XC_circle;
+ break;
+ case Qt::BusyCursor:
+ sh = XC_watch;
+ break;
+#endif /* QT_USE_APPROXIMATE_CURSORS */
+ default:
+ qWarning("QCursor::update: Invalid cursor shape %d", cshape);
+ return;
+ }
+ hcurs = XCreateFontCursor(dpy, sh);
+
+#ifndef QT_NO_XFIXES
+ if (X11->use_xfixes && X11->ptrXFixesSetCursorName)
+ X11->ptrXFixesSetCursorName(dpy, hcurs, cursorNames[cshape]);
+#endif /* ! QT_NO_XFIXES */
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/kernel/qdesktopwidget.h b/src/gui/kernel/qdesktopwidget.h
new file mode 100644
index 0000000000..c107ee0091
--- /dev/null
+++ b/src/gui/kernel/qdesktopwidget.h
@@ -0,0 +1,104 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QDESKTOPWIDGET_H
+#define QDESKTOPWIDGET_H
+
+#include <QtGui/qwidget.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+class QApplication;
+class QDesktopWidgetPrivate;
+
+class Q_GUI_EXPORT QDesktopWidget : public QWidget
+{
+ Q_OBJECT
+public:
+ QDesktopWidget();
+ ~QDesktopWidget();
+
+ bool isVirtualDesktop() const;
+
+ int numScreens() const;
+ int primaryScreen() const;
+
+ int screenNumber(const QWidget *widget = 0) const;
+ int screenNumber(const QPoint &) const;
+
+ QWidget *screen(int screen = -1);
+
+ const QRect screenGeometry(int screen = -1) const;
+ const QRect screenGeometry(const QWidget *widget) const
+ { return screenGeometry(screenNumber(widget)); }
+ const QRect screenGeometry(const QPoint &point) const
+ { return screenGeometry(screenNumber(point)); }
+
+ const QRect availableGeometry(int screen = -1) const;
+ const QRect availableGeometry(const QWidget *widget) const
+ { return availableGeometry(screenNumber(widget)); }
+ const QRect availableGeometry(const QPoint &point) const
+ { return availableGeometry(screenNumber(point)); }
+
+Q_SIGNALS:
+ void resized(int);
+ void workAreaResized(int);
+
+protected:
+ void resizeEvent(QResizeEvent *e);
+
+private:
+ Q_DISABLE_COPY(QDesktopWidget)
+ Q_DECLARE_PRIVATE(QDesktopWidget)
+
+ friend class QApplication;
+ friend class QApplicationPrivate;
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QDESKTOPWIDGET_H
diff --git a/src/gui/kernel/qdesktopwidget_mac.mm b/src/gui/kernel/qdesktopwidget_mac.mm
new file mode 100644
index 0000000000..77989cb663
--- /dev/null
+++ b/src/gui/kernel/qdesktopwidget_mac.mm
@@ -0,0 +1,244 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/****************************************************************************
+**
+** Copyright (c) 2007-2008, Apple, Inc.
+**
+** All rights reserved.
+**
+** Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are met:
+**
+** * Redistributions of source code must retain the above copyright notice,
+** this list of conditions and the following disclaimer.
+**
+** * Redistributions in binary form must reproduce the above copyright notice,
+** this list of conditions and the following disclaimer in the documentation
+** and/or other materials provided with the distribution.
+**
+** * Neither the name of Apple, Inc. nor the names of its contributors
+** may be used to endorse or promote products derived from this software
+** without specific prior written permission.
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+** CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+** EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+** PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+** PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+** LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+** NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+** SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+**
+****************************************************************************/
+
+#import <Cocoa/Cocoa.h>
+
+#include "qapplication.h"
+#include "qdesktopwidget.h"
+#include <private/qt_mac_p.h>
+#include "qwidget_p.h"
+#include <private/qt_cocoa_helpers_mac_p.h>
+#include <private/qdesktopwidget_mac_p.h>
+
+QT_BEGIN_NAMESPACE
+
+QT_USE_NAMESPACE
+
+/*****************************************************************************
+ Externals
+ *****************************************************************************/
+
+/*****************************************************************************
+ QDesktopWidget member functions
+ *****************************************************************************/
+
+Q_GLOBAL_STATIC(QDesktopWidgetImplementation, qdesktopWidgetImplementation)
+
+QDesktopWidgetImplementation::QDesktopWidgetImplementation()
+ : appScreen(0), displays(0)
+{
+ onResize();
+}
+
+QDesktopWidgetImplementation::~QDesktopWidgetImplementation()
+{
+ if (displays)
+ [displays release];
+}
+
+QDesktopWidgetImplementation *QDesktopWidgetImplementation::instance()
+{
+ return qdesktopWidgetImplementation();
+}
+
+QRect QDesktopWidgetImplementation::availableRect(int screenIndex) const
+{
+ if (screenIndex < 0 || screenIndex >= screenCount)
+ screenIndex = appScreen;
+
+ NSRect r = [[displays objectAtIndex:screenIndex] visibleFrame];
+ NSRect primaryRect = [[displays objectAtIndex:0] frame];
+
+ const int flippedY = - r.origin.y + // account for position offset and
+ primaryRect.size.height - r.size.height; // height difference.
+ return QRectF(r.origin.x, flippedY,
+ r.size.width, r.size.height).toRect();
+}
+
+QRect QDesktopWidgetImplementation::screenRect(int screenIndex) const
+{
+ if (screenIndex < 0 || screenIndex >= screenCount)
+ screenIndex = appScreen;
+
+ NSRect r = [[displays objectAtIndex:screenIndex] frame];
+ NSRect primaryRect = [[displays objectAtIndex:0] frame];
+
+ const int flippedY = - r.origin.y + // account for position offset and
+ primaryRect.size.height - r.size.height; // height difference.
+ return QRectF(r.origin.x, flippedY,
+ r.size.width, r.size.height).toRect();
+}
+
+void QDesktopWidgetImplementation::onResize()
+{
+ if (displays)
+ [displays release];
+
+ displays = [[NSScreen screens] retain];
+ screenCount = [displays count];
+}
+
+
+
+QDesktopWidget::QDesktopWidget()
+ : QWidget(0, Qt::Desktop)
+{
+ setObjectName(QLatin1String("desktop"));
+ setAttribute(Qt::WA_WState_Visible);
+}
+
+QDesktopWidget::~QDesktopWidget()
+{
+}
+
+bool QDesktopWidget::isVirtualDesktop() const
+{
+ return true;
+}
+
+int QDesktopWidget::primaryScreen() const
+{
+ return qdesktopWidgetImplementation()->appScreen;
+}
+
+int QDesktopWidget::numScreens() const
+{
+ return qdesktopWidgetImplementation()->screenCount;
+}
+
+QWidget *QDesktopWidget::screen(int)
+{
+ return this;
+}
+
+const QRect QDesktopWidget::availableGeometry(int screen) const
+{
+ return qdesktopWidgetImplementation()->availableRect(screen);
+}
+
+const QRect QDesktopWidget::screenGeometry(int screen) const
+{
+ return qdesktopWidgetImplementation()->screenRect(screen);
+}
+
+int QDesktopWidget::screenNumber(const QWidget *widget) const
+{
+ QDesktopWidgetImplementation *d = qdesktopWidgetImplementation();
+ if (!widget)
+ return d->appScreen;
+ QRect frame = widget->frameGeometry();
+ if (!widget->isWindow())
+ frame.moveTopLeft(widget->mapToGlobal(QPoint(0,0)));
+ int maxSize = -1, maxScreen = -1;
+ for (int i = 0; i < d->screenCount; ++i) {
+ QRect rr = d->screenRect(i);
+ QRect sect = rr.intersected(frame);
+ int size = sect.width() * sect.height();
+ if (size > maxSize && sect.width() > 0 && sect.height() > 0) {
+ maxSize = size;
+ maxScreen = i;
+ }
+ }
+ return maxScreen;
+}
+
+int QDesktopWidget::screenNumber(const QPoint &point) const
+{
+ QDesktopWidgetImplementation *d = qdesktopWidgetImplementation();
+ int closestScreen = -1;
+ int shortestDistance = INT_MAX;
+ for (int i = 0; i < d->screenCount; ++i) {
+ QRect rr = d->screenRect(i);
+ int thisDistance = QWidgetPrivate::pointToRect(point, rr);
+ if (thisDistance < shortestDistance) {
+ shortestDistance = thisDistance;
+ closestScreen = i;
+ }
+ }
+ return closestScreen;
+}
+
+void QDesktopWidget::resizeEvent(QResizeEvent *)
+{
+ QDesktopWidgetImplementation *d = qdesktopWidgetImplementation();
+
+ d->onResize();
+
+ for (int i = 0; i < d->screenCount; ++i) {
+ emit resized(i);
+ }
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/kernel/qdesktopwidget_mac_p.h b/src/gui/kernel/qdesktopwidget_mac_p.h
new file mode 100644
index 0000000000..09cbacc214
--- /dev/null
+++ b/src/gui/kernel/qdesktopwidget_mac_p.h
@@ -0,0 +1,74 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of the Mac developers at Qt Software. This header file may change
+// from version to version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <qrect.h>
+
+QT_BEGIN_NAMESPACE
+
+class QDesktopWidgetImplementation
+{
+public:
+ QDesktopWidgetImplementation();
+ ~QDesktopWidgetImplementation();
+ static QDesktopWidgetImplementation *instance();
+
+ int appScreen;
+ int screenCount;
+
+ NSArray *displays;
+
+ QRect availableRect(int screenIndex) const;
+ QRect screenRect(int screenIndex) const;
+ void onResize();
+};
+
+QT_END_NAMESPACE
diff --git a/src/gui/kernel/qdesktopwidget_qws.cpp b/src/gui/kernel/qdesktopwidget_qws.cpp
new file mode 100644
index 0000000000..cfeaee9915
--- /dev/null
+++ b/src/gui/kernel/qdesktopwidget_qws.cpp
@@ -0,0 +1,159 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qdesktopwidget.h"
+#include "qscreen_qws.h"
+#include "private/qapplication_p.h"
+
+QT_BEGIN_NAMESPACE
+
+QT_USE_NAMESPACE
+
+QDesktopWidget::QDesktopWidget()
+ : QWidget(0, Qt::Desktop)
+{
+ setObjectName(QLatin1String("desktop"));
+}
+
+QDesktopWidget::~QDesktopWidget()
+{
+}
+
+bool QDesktopWidget::isVirtualDesktop() const
+{
+ return true;
+}
+
+int QDesktopWidget::primaryScreen() const
+{
+ return 0;
+}
+
+int QDesktopWidget::numScreens() const
+{
+ QScreen *screen = QScreen::instance();
+ if (!screen)
+ return 0;
+
+ const QList<QScreen*> subScreens = screen->subScreens();
+ return qMax(subScreens.size(), 1);
+}
+
+QWidget *QDesktopWidget::screen(int)
+{
+ return this;
+}
+
+const QRect QDesktopWidget::availableGeometry(int screenNo) const
+{
+ const QScreen *screen = QScreen::instance();
+ if (screenNo == -1)
+ screenNo = 0;
+ if (!screen || screenNo < 0)
+ return QRect();
+
+ const QList<QScreen*> subScreens = screen->subScreens();
+ if (!subScreens.isEmpty()) {
+ if (screenNo >= subScreens.size())
+ return QRect();
+ screen = subScreens.at(screenNo);
+ }
+
+ QApplicationPrivate *ap = QApplicationPrivate::instance();
+ const QRect r = ap->maxWindowRect(screen);
+ if (!r.isEmpty())
+ return r;
+
+ return screen->region().boundingRect();
+}
+
+const QRect QDesktopWidget::screenGeometry(int screenNo) const
+{
+ const QScreen *screen = QScreen::instance();
+ if (screenNo == -1)
+ screenNo = 0;
+ if (!screen || screenNo < 0)
+ return QRect();
+
+ const QList<QScreen*> subScreens = screen->subScreens();
+ if (subScreens.size() == 0 && screenNo == 0)
+ return screen->region().boundingRect();
+
+ if (screenNo >= subScreens.size())
+ return QRect();
+
+ return subScreens.at(screenNo)->region().boundingRect();
+}
+
+int QDesktopWidget::screenNumber(const QWidget *w) const
+{
+ if (!w)
+ return 0;
+
+ QRect frame = w->frameGeometry();
+ if (!w->isWindow())
+ frame.moveTopLeft(w->mapToGlobal(QPoint(0, 0)));
+ const QPoint midpoint = (frame.topLeft() + frame.bottomRight()) / 2;
+ return screenNumber(midpoint);
+}
+
+int QDesktopWidget::screenNumber(const QPoint &p) const
+{
+ const QScreen *screen = QScreen::instance();
+ if (!screen || !screen->region().contains(p))
+ return -1;
+
+ const QList<QScreen*> subScreens = screen->subScreens();
+ if (subScreens.size() == 0)
+ return 0;
+
+ for (int i = 0; i < subScreens.size(); ++i)
+ if (subScreens.at(i)->region().contains(p))
+ return i;
+
+ return -1;
+}
+
+void QDesktopWidget::resizeEvent(QResizeEvent *)
+{
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/kernel/qdesktopwidget_win.cpp b/src/gui/kernel/qdesktopwidget_win.cpp
new file mode 100644
index 0000000000..725e985870
--- /dev/null
+++ b/src/gui/kernel/qdesktopwidget_win.cpp
@@ -0,0 +1,412 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qdesktopwidget.h"
+#include "qt_windows.h"
+#include "qapplication_p.h"
+#include "qlibrary.h"
+#include <qvector.h>
+#include <limits.h>
+#ifdef Q_OS_WINCE
+#include <sipapi.h>
+#endif
+#include "qwidget_p.h"
+#include "qdebug.h"
+
+QT_BEGIN_NAMESPACE
+
+class QDesktopWidgetPrivate : public QWidgetPrivate
+{
+public:
+ QDesktopWidgetPrivate();
+ ~QDesktopWidgetPrivate();
+
+ static void init(QDesktopWidget *that);
+ static void cleanup();
+ static int screenCount;
+ static int primaryScreen;
+
+ static QVector<QRect> *rects;
+ static QVector<QRect> *workrects;
+
+ struct MONITORINFO
+ {
+ DWORD cbSize;
+ RECT rcMonitor;
+ RECT rcWork;
+ DWORD dwFlags;
+ };
+
+ typedef BOOL (WINAPI *InfoFunc)(HMONITOR, MONITORINFO*);
+ typedef BOOL (CALLBACK *EnumProc)(HMONITOR, HDC, LPRECT, LPARAM);
+ typedef BOOL (WINAPI *EnumFunc)(HDC, LPCRECT, EnumProc, LPARAM);
+
+ static EnumFunc enumDisplayMonitors;
+ static InfoFunc getMonitorInfo;
+ static int refcount;
+};
+
+int QDesktopWidgetPrivate::screenCount = 1;
+int QDesktopWidgetPrivate::primaryScreen = 0;
+QDesktopWidgetPrivate::EnumFunc QDesktopWidgetPrivate::enumDisplayMonitors = 0;
+QDesktopWidgetPrivate::InfoFunc QDesktopWidgetPrivate::getMonitorInfo = 0;
+QVector<QRect> *QDesktopWidgetPrivate::rects = 0;
+QVector<QRect> *QDesktopWidgetPrivate::workrects = 0;
+static int screen_number = 0;
+int QDesktopWidgetPrivate::refcount = 0;
+#ifdef Q_OS_WINCE_WM
+// Use SIP information, if available
+// SipGetInfo is not supported by SSDK (no definition!).
+static inline void qt_get_sip_info(QRect &rect)
+{
+ SIPINFO sip;
+ memset(&sip, 0, sizeof(SIPINFO));
+ sip.cbSize = sizeof(SIPINFO);
+ if (SipGetInfo(&sip))
+ rect = QRect(QPoint(sip.rcVisibleDesktop.left, sip.rcVisibleDesktop.top),
+ QPoint(sip.rcVisibleDesktop.right - 1, sip.rcVisibleDesktop.bottom - 1));
+}
+#endif
+
+
+BOOL CALLBACK enumCallback(HMONITOR hMonitor, HDC, LPRECT, LPARAM)
+{
+ QDesktopWidgetPrivate::screenCount++;
+ QDesktopWidgetPrivate::rects->resize(QDesktopWidgetPrivate::screenCount);
+ QDesktopWidgetPrivate::workrects->resize(QDesktopWidgetPrivate::screenCount);
+ // Get the MONITORINFO block
+ QDesktopWidgetPrivate::MONITORINFO info;
+ memset(&info, 0, sizeof(QDesktopWidgetPrivate::MONITORINFO));
+ info.cbSize = sizeof(QDesktopWidgetPrivate::MONITORINFO);
+ BOOL res = QDesktopWidgetPrivate::getMonitorInfo(hMonitor, &info);
+ if (!res) {
+ (*QDesktopWidgetPrivate::rects)[screen_number] = QRect();
+ (*QDesktopWidgetPrivate::workrects)[screen_number] = QRect();
+ return true;
+ }
+
+ // Fill list of rects
+ RECT r = info.rcMonitor;
+ QRect qr(QPoint(r.left, r.top), QPoint(r.right - 1, r.bottom - 1));
+ (*QDesktopWidgetPrivate::rects)[screen_number] = qr;
+
+ r = info.rcWork;
+ qr = QRect(QPoint(r.left, r.top), QPoint(r.right - 1, r.bottom - 1));
+ (*QDesktopWidgetPrivate::workrects)[screen_number] = qr;
+
+ if (info.dwFlags & 0x00000001) //MONITORINFOF_PRIMARY
+ QDesktopWidgetPrivate::primaryScreen = screen_number;
+
+ ++screen_number;
+ // Stop the enumeration if we have them all
+ return true;
+}
+
+QDesktopWidgetPrivate::QDesktopWidgetPrivate()
+{
+ ++refcount;
+}
+
+void QDesktopWidgetPrivate::init(QDesktopWidget *that)
+{
+ if (rects)
+ return;
+
+ rects = new QVector<QRect>();
+ workrects = new QVector<QRect>();
+
+#ifndef Q_OS_WINCE
+ if (QSysInfo::WindowsVersion != QSysInfo::WV_95 && QSysInfo::WindowsVersion != QSysInfo::WV_NT) {
+ screenCount = 0;
+ // Trying to get the function pointers to Win98/2000 only functions
+ QLibrary user32Lib(QLatin1String("user32"));
+ if (!user32Lib.load())
+ return;
+ enumDisplayMonitors = (EnumFunc)user32Lib.resolve("EnumDisplayMonitors");
+ QT_WA({
+ getMonitorInfo = (InfoFunc)user32Lib.resolve("GetMonitorInfoW");
+ } , {
+ getMonitorInfo = (InfoFunc)user32Lib.resolve("GetMonitorInfoA");
+ });
+
+ if (!enumDisplayMonitors || !getMonitorInfo) {
+ screenCount = GetSystemMetrics(80); // SM_CMONITORS
+ rects->resize(screenCount);
+ for (int i = 0; i < screenCount; ++i)
+ rects->replace(i, that->rect());
+ return;
+ }
+ // Calls enumCallback
+ enumDisplayMonitors(0, 0, enumCallback, 0);
+ enumDisplayMonitors = 0;
+ getMonitorInfo = 0;
+ } else {
+ rects->resize(1);
+ rects->replace(0, that->rect());
+ workrects->resize(1);
+ workrects->replace(0, that->rect());
+ }
+#else
+ screenCount = 0;
+
+ QLibrary coreLib(QLatin1String("coredll"));
+ if (coreLib.load()) {
+ // CE >= 4.0 case
+ enumDisplayMonitors = (EnumFunc)coreLib.resolve("EnumDisplayMonitors");
+ getMonitorInfo = (InfoFunc)coreLib.resolve("GetMonitorInfo");
+ }
+
+ if ((!enumDisplayMonitors || !getMonitorInfo)) {
+ screenCount = GetSystemMetrics(SM_CMONITORS);
+ return;
+ }
+
+ if (!coreLib.isLoaded() || !enumDisplayMonitors || !getMonitorInfo) {
+ rects->resize(screenCount);
+ for (int i = 0; i < screenCount; ++i)
+ (*rects)[i] = that->rect();
+
+ RECT r;
+ SystemParametersInfo(SPI_GETWORKAREA, 0, &r, 0);
+ QRect qr = QRect(QPoint(r.left, r.top), QPoint(r.right - 1, r.bottom - 1));
+
+#if defined(Q_OS_WINCE_WM)
+ qt_get_sip_info(qr);
+#endif
+
+ workrects->resize(screenCount);
+ for (int j = 0; j < screenCount; ++j)
+ (*workrects)[j] = qr;
+ return;
+ }
+
+ // Calls enumCallback
+ enumDisplayMonitors(0, 0, enumCallback, 0);
+ enumDisplayMonitors = 0;
+ getMonitorInfo = 0;
+#endif // Q_OS_WINCE
+}
+
+QDesktopWidgetPrivate::~QDesktopWidgetPrivate()
+{
+ if (!--refcount)
+ cleanup();
+}
+
+void QDesktopWidgetPrivate::cleanup()
+{
+ screen_number = 0;
+ screenCount = 1;
+ primaryScreen = 0;
+ enumDisplayMonitors = 0;
+ getMonitorInfo = 0;
+ delete rects;
+ rects = 0;
+ delete workrects;
+ workrects = 0;
+}
+
+/*
+ \omit
+ Function is commented out in header
+ \fn void *QDesktopWidget::handle(int screen) const
+
+ Returns the window system handle of the display device with the
+ index \a screen, for low-level access. Using this function is not
+ portable.
+
+ The return type varies with platform; see qwindowdefs.h for details.
+
+ \sa x11Display(), QPaintDevice::handle()
+ \endomit
+*/
+
+QDesktopWidget::QDesktopWidget()
+ : QWidget(*new QDesktopWidgetPrivate, 0, Qt::Desktop)
+{
+ setObjectName(QLatin1String("desktop"));
+ QDesktopWidgetPrivate::init(this);
+}
+
+QDesktopWidget::~QDesktopWidget()
+{
+}
+
+bool QDesktopWidget::isVirtualDesktop() const
+{
+ return true;
+}
+
+int QDesktopWidget::primaryScreen() const
+{
+ return d_func()->primaryScreen;
+}
+
+int QDesktopWidget::numScreens() const
+{
+ return d_func()->screenCount;
+}
+
+QWidget *QDesktopWidget::screen(int /* screen */)
+{
+ // It seems that a Qt::WType_Desktop cannot be moved?
+ return this;
+}
+
+//
+// MSVC 7.10 warns that d (the result of the expanded Q_D macro) as a local variable that is not referenced.
+// Therefore, we ignore that warning with the following pragmas
+// I've also tried to eliminate the macro, but to no use...
+// We pop it further down
+#ifdef Q_CC_MSVC
+# pragma warning(push)
+# pragma warning(disable : 4189)
+#endif
+const QRect QDesktopWidget::availableGeometry(int screen) const
+{
+ Q_D(const QDesktopWidget);
+#ifdef Q_OS_WINCE_WM
+ for(int i=0; i < d->workrects->size(); ++i)
+ qt_get_sip_info((*d->workrects)[i]);
+#endif
+ if (QSysInfo::WindowsVersion != QSysInfo::WV_95 && QSysInfo::WindowsVersion != QSysInfo::WV_NT) {
+ if (screen < 0 || screen >= d->screenCount)
+ screen = d->primaryScreen;
+
+ return d->workrects->at(screen);
+ } else {
+ return d->workrects->at(d->primaryScreen);
+ }
+}
+
+const QRect QDesktopWidget::screenGeometry(int screen) const
+{
+ const QDesktopWidgetPrivate *d = d_func();
+ if (QSysInfo::WindowsVersion != QSysInfo::WV_95 && QSysInfo::WindowsVersion != QSysInfo::WV_NT) {
+ if (screen < 0 || screen >= d->screenCount)
+ screen = d->primaryScreen;
+
+ return d->rects->at(screen);
+ } else {
+ return d->rects->at(d->primaryScreen);
+ }
+}
+
+int QDesktopWidget::screenNumber(const QWidget *widget) const
+{
+ Q_D(const QDesktopWidget);
+ if (QSysInfo::WindowsVersion != QSysInfo::WV_95 && QSysInfo::WindowsVersion != QSysInfo::WV_NT) {
+ if (!widget)
+ return d->primaryScreen;
+ QRect frame = widget->frameGeometry();
+ if (!widget->isWindow())
+ frame.moveTopLeft(widget->mapToGlobal(QPoint(0,0)));
+
+ int maxSize = -1;
+ int maxScreen = -1;
+
+ for (int i = 0; i < d->screenCount; ++i) {
+ QRect sect = d->rects->at(i).intersected(frame);
+ int size = sect.width() * sect.height();
+ if (size > maxSize && sect.width() > 0 && sect.height() > 0) {
+ maxSize = size;
+ maxScreen = i;
+ }
+ }
+ return maxScreen;
+ } else {
+ return d->primaryScreen;
+ }
+}
+
+int QDesktopWidget::screenNumber(const QPoint &point) const
+{
+ Q_D(const QDesktopWidget);
+ int closestScreen = -1;
+ if (QSysInfo::WindowsVersion != QSysInfo::WV_95 && QSysInfo::WindowsVersion != QSysInfo::WV_NT) {
+ int shortestDistance = INT_MAX;
+ for (int i = 0; i < d->screenCount; ++i) {
+ int thisDistance = d->pointToRect(point, d->rects->at(i));
+ if (thisDistance < shortestDistance) {
+ shortestDistance = thisDistance;
+ closestScreen = i;
+ }
+ }
+ }
+ return closestScreen;
+}
+
+void QDesktopWidget::resizeEvent(QResizeEvent *)
+{
+ Q_D(QDesktopWidget);
+ QVector<QRect> oldrects;
+ oldrects = *d->rects;
+ QVector<QRect> oldworkrects;
+ oldworkrects = *d->workrects;
+ int oldscreencount = d->screenCount;
+
+ QDesktopWidgetPrivate::cleanup();
+ QDesktopWidgetPrivate::init(this);
+#ifdef Q_OS_WINCE_WM
+ for(int i=0; i < d->workrects->size(); ++i)
+ qt_get_sip_info((*d->workrects)[i]);
+#endif
+
+ for (int i = 0; i < qMin(oldscreencount, d->screenCount); ++i) {
+ QRect oldrect = oldrects[i];
+ QRect newrect = d->rects->at(i);
+ if (oldrect != newrect)
+ emit resized(i);
+ }
+
+ for (int j = 0; j < qMin(oldscreencount, d->screenCount); ++j) {
+ QRect oldrect = oldworkrects[j];
+ QRect newrect = d->workrects->at(j);
+ if (oldrect != newrect)
+ emit workAreaResized(j);
+ }
+}
+
+#ifdef Q_CC_MSVC
+# pragma warning(pop)
+#endif
+
+QT_END_NAMESPACE
diff --git a/src/gui/kernel/qdesktopwidget_x11.cpp b/src/gui/kernel/qdesktopwidget_x11.cpp
new file mode 100644
index 0000000000..7b823f0d38
--- /dev/null
+++ b/src/gui/kernel/qdesktopwidget_x11.cpp
@@ -0,0 +1,380 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qapplication.h"
+#include "qdesktopwidget.h"
+#include "qlibrary.h"
+#include "qt_x11_p.h"
+#include "qvariant.h"
+#include "qwidget_p.h"
+#include "qx11info_x11.h"
+#include <limits.h>
+
+QT_BEGIN_NAMESPACE
+
+// defined in qwidget_x11.cpp
+extern int qt_x11_create_desktop_on_screen;
+
+
+// function to update the workarea of the screen
+static bool qt_desktopwidget_workarea_dirty = true;
+void qt_desktopwidget_update_workarea()
+{
+ qt_desktopwidget_workarea_dirty = true;
+}
+
+
+class QSingleDesktopWidget : public QWidget
+{
+public:
+ QSingleDesktopWidget();
+ ~QSingleDesktopWidget();
+};
+
+QSingleDesktopWidget::QSingleDesktopWidget()
+ : QWidget(0, Qt::Desktop)
+{
+}
+
+QSingleDesktopWidget::~QSingleDesktopWidget()
+{
+ const QObjectList &childList = children();
+ for (int i = childList.size(); i > 0 ;) {
+ --i;
+ childList.at(i)->setParent(0);
+ }
+}
+
+
+class QDesktopWidgetPrivate : public QWidgetPrivate
+{
+public:
+ QDesktopWidgetPrivate();
+ ~QDesktopWidgetPrivate();
+
+ void init();
+
+ bool use_xinerama;
+ int defaultScreen;
+ int screenCount;
+
+ QWidget **screens;
+ QRect *rects;
+ QRect *workareas;
+};
+
+QDesktopWidgetPrivate::QDesktopWidgetPrivate()
+ : use_xinerama(false), defaultScreen(0), screenCount(1),
+ screens(0), rects(0), workareas(0)
+{
+}
+
+QDesktopWidgetPrivate::~QDesktopWidgetPrivate()
+{
+ if (screens) {
+ for (int i = 0; i < screenCount; ++i) {
+ if (i == defaultScreen) continue;
+ delete screens[i];
+ screens[i] = 0;
+ }
+
+ free (screens);
+ }
+
+ if (rects) delete [] rects;
+ if (workareas) delete [] workareas;
+}
+
+void QDesktopWidgetPrivate::init()
+{
+ // get the screen count
+ int newScreenCount = ScreenCount(X11->display);
+#ifndef QT_NO_XINERAMA
+
+ XineramaScreenInfo *xinerama_screeninfo = 0;
+
+ // we ignore the Xinerama extension when using the display is
+ // using traditional multi-screen (with multiple root windows)
+ if (newScreenCount == 1
+ && X11->ptrXineramaQueryExtension
+ && X11->ptrXineramaIsActive
+ && X11->ptrXineramaQueryScreens) {
+ int unused;
+ use_xinerama = (X11->ptrXineramaQueryExtension(X11->display, &unused, &unused)
+ && X11->ptrXineramaIsActive(X11->display));
+ }
+
+ if (use_xinerama) {
+ xinerama_screeninfo =
+ X11->ptrXineramaQueryScreens(X11->display, &newScreenCount);
+ }
+
+ if (xinerama_screeninfo) {
+ defaultScreen = 0;
+ } else
+#endif // QT_NO_XINERAMA
+ {
+ defaultScreen = DefaultScreen(X11->display);
+ newScreenCount = ScreenCount(X11->display);
+ use_xinerama = false;
+ }
+
+ delete [] rects;
+ rects = new QRect[newScreenCount];
+ delete [] workareas;
+ workareas = new QRect[newScreenCount];
+
+ // get the geometry of each screen
+ int i, j, x, y, w, h;
+ for (i = 0, j = 0; i < newScreenCount; i++, j++) {
+
+#ifndef QT_NO_XINERAMA
+ if (use_xinerama) {
+ x = xinerama_screeninfo[i].x_org;
+ y = xinerama_screeninfo[i].y_org;
+ w = xinerama_screeninfo[i].width;
+ h = xinerama_screeninfo[i].height;
+ } else
+#endif // QT_NO_XINERAMA
+ {
+ x = 0;
+ y = 0;
+ w = WidthOfScreen(ScreenOfDisplay(X11->display, i));
+ h = HeightOfScreen(ScreenOfDisplay(X11->display, i));
+ }
+
+ rects[j].setRect(x, y, w, h);
+
+ if (use_xinerama && j > 0 && rects[j-1].intersects(rects[j])) {
+ // merge a "cloned" screen with the previous, hiding all crtcs
+ // that are currently showing a sub-rect of the previous screen
+ if ((rects[j].width()*rects[j].height()) >
+ (rects[j-1].width()*rects[j-1].height()))
+ rects[j-1] = rects[j];
+ j--;
+ }
+
+ workareas[i] = QRect();
+ }
+
+ if (screens) {
+ // leaks QWidget* pointers on purpose, can't delete them as pointer escapes
+ screens = (QWidget**) realloc(screens, j * sizeof(QWidget*));
+ if (j > screenCount)
+ memset(&screens[screenCount], 0, (j-screenCount) * sizeof(QWidget*));
+ }
+
+ screenCount = j;
+
+#ifndef QT_NO_XINERAMA
+ if (use_xinerama && screenCount == 1)
+ use_xinerama = false;
+
+ if (xinerama_screeninfo)
+ XFree(xinerama_screeninfo);
+#endif // QT_NO_XINERAMA
+
+}
+
+// the QDesktopWidget itself will be created on the default screen
+// as qt_x11_create_desktop_on_screen defaults to -1
+QDesktopWidget::QDesktopWidget()
+ : QWidget(*new QDesktopWidgetPrivate, 0, Qt::Desktop)
+{
+ Q_D(QDesktopWidget);
+ d->init();
+}
+
+QDesktopWidget::~QDesktopWidget()
+{
+}
+
+bool QDesktopWidget::isVirtualDesktop() const
+{
+ Q_D(const QDesktopWidget);
+ return d->use_xinerama;
+}
+
+int QDesktopWidget::primaryScreen() const
+{
+ Q_D(const QDesktopWidget);
+ return d->defaultScreen;
+}
+
+int QDesktopWidget::numScreens() const
+{
+ Q_D(const QDesktopWidget);
+ return d->screenCount;
+}
+
+QWidget *QDesktopWidget::screen(int screen)
+{
+ Q_D(QDesktopWidget);
+ if (d->use_xinerama)
+ return this;
+
+ if (screen < 0 || screen >= d->screenCount)
+ screen = d->defaultScreen;
+
+ if (! d->screens) {
+ d->screens = (QWidget**) calloc( d->screenCount, sizeof(QWidget*));
+ d->screens[d->defaultScreen] = this;
+ }
+
+ if (! d->screens[screen] || // not created yet
+ ! (d->screens[screen]->windowType() == Qt::Desktop)) { // reparented away
+ qt_x11_create_desktop_on_screen = screen;
+ d->screens[screen] = new QSingleDesktopWidget;
+ qt_x11_create_desktop_on_screen = -1;
+ }
+
+ return d->screens[screen];
+}
+
+const QRect QDesktopWidget::availableGeometry(int screen) const
+{
+ Q_D(const QDesktopWidget);
+ if (qt_desktopwidget_workarea_dirty) {
+ // the workareas are dirty, invalidate them
+ for (int i = 0; i < d->screenCount; ++i)
+ d->workareas[i] = QRect();
+ qt_desktopwidget_workarea_dirty = false;
+ }
+
+ if (screen < 0 || screen >= d->screenCount)
+ screen = d->defaultScreen;
+
+ if (d->workareas[screen].isValid())
+ return d->workareas[screen];
+
+ if ((d->screenCount == 1 || !isVirtualDesktop())
+ && X11->isSupportedByWM(ATOM(_NET_WORKAREA))) {
+ Atom ret;
+ int format, e;
+ unsigned char *data = 0;
+ unsigned long nitems, after;
+
+ e = XGetWindowProperty(X11->display,
+ QX11Info::appRootWindow(screen),
+ ATOM(_NET_WORKAREA), 0, 4, False, XA_CARDINAL,
+ &ret, &format, &nitems, &after, &data);
+
+ if (e == Success && ret == XA_CARDINAL &&
+ format == 32 && nitems == 4) {
+ long *workarea = (long *) data;
+ d->workareas[screen].setRect(workarea[0], workarea[1],
+ workarea[2], workarea[3]);
+ } else {
+ d->workareas[screen] = screenGeometry(screen);
+ }
+ if (data)
+ XFree(data);
+ } else {
+ d->workareas[screen] = screenGeometry(screen);
+ }
+
+ return d->workareas[screen];
+}
+
+const QRect QDesktopWidget::screenGeometry(int screen) const
+{
+ Q_D(const QDesktopWidget);
+ if (screen < 0 || screen >= d->screenCount)
+ screen = d->defaultScreen;
+
+ return d->rects[screen];
+}
+
+int QDesktopWidget::screenNumber(const QWidget *widget) const
+{
+ Q_D(const QDesktopWidget);
+ if (!widget)
+ return d->defaultScreen;
+
+#ifndef QT_NO_XINERAMA
+ if (d->use_xinerama) {
+ // this is how we do it for xinerama
+ QRect frame = widget->frameGeometry();
+ if (!widget->isWindow())
+ frame.moveTopLeft(widget->mapToGlobal(QPoint(0, 0)));
+
+ int maxSize = -1;
+ int maxScreen = -1;
+
+ for (int i = 0; i < d->screenCount; ++i) {
+ QRect sect = d->rects[i].intersected(frame);
+ int size = sect.width() * sect.height();
+ if (size > maxSize && sect.width() > 0 && sect.height() > 0) {
+ maxSize = size;
+ maxScreen = i;
+ }
+ }
+ return maxScreen;
+ }
+#endif // QT_NO_XINERAMA
+
+ return widget->x11Info().screen();
+}
+
+int QDesktopWidget::screenNumber(const QPoint &point) const
+{
+ Q_D(const QDesktopWidget);
+ int closestScreen = -1;
+ int shortestDistance = INT_MAX;
+ for (int i = 0; i < d->screenCount; ++i) {
+ int thisDistance = d->pointToRect(point, d->rects[i]);
+ if (thisDistance < shortestDistance) {
+ shortestDistance = thisDistance;
+ closestScreen = i;
+ }
+ }
+ return closestScreen;
+}
+
+void QDesktopWidget::resizeEvent(QResizeEvent *event)
+{
+ Q_D(QDesktopWidget);
+ d->init();
+ qt_desktopwidget_workarea_dirty = true;
+ QWidget::resizeEvent(event);
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/kernel/qdnd.cpp b/src/gui/kernel/qdnd.cpp
new file mode 100644
index 0000000000..a5092a0be0
--- /dev/null
+++ b/src/gui/kernel/qdnd.cpp
@@ -0,0 +1,697 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qplatformdefs.h"
+
+#include "qbitmap.h"
+#include "qdrag.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 "qimage.h"
+#include "qregexp.h"
+#include "qdir.h"
+#include "qdnd_p.h"
+#include "qimagereader.h"
+#include "qimagewriter.h"
+#include "qdebug.h"
+#include <ctype.h>
+
+#ifndef QT_NO_DRAGANDDROP
+
+QT_BEGIN_NAMESPACE
+
+// These pixmaps approximate the images in the Windows User Interface Guidelines.
+
+// XPM
+
+static const char * const move_xpm[] = {
+"11 20 3 1",
+". c None",
+#if defined(Q_WS_WIN)
+"a c #000000",
+"X c #FFFFFF", // Windows cursor is traditionally white
+#else
+"a c #FFFFFF",
+"X c #000000", // X11 cursor is traditionally black
+#endif
+"aa.........",
+"aXa........",
+"aXXa.......",
+"aXXXa......",
+"aXXXXa.....",
+"aXXXXXa....",
+"aXXXXXXa...",
+"aXXXXXXXa..",
+"aXXXXXXXXa.",
+"aXXXXXXXXXa",
+"aXXXXXXaaaa",
+"aXXXaXXa...",
+"aXXaaXXa...",
+"aXa..aXXa..",
+"aa...aXXa..",
+"a.....aXXa.",
+"......aXXa.",
+".......aXXa",
+".......aXXa",
+"........aa."};
+
+#ifdef Q_WS_WIN
+/* XPM */
+static const char * const ignore_xpm[] = {
+"24 30 3 1",
+". c None",
+"a c #000000",
+"X c #FFFFFF",
+"aa......................",
+"aXa.....................",
+"aXXa....................",
+"aXXXa...................",
+"aXXXXa..................",
+"aXXXXXa.................",
+"aXXXXXXa................",
+"aXXXXXXXa...............",
+"aXXXXXXXXa..............",
+"aXXXXXXXXXa.............",
+"aXXXXXXaaaa.............",
+"aXXXaXXa................",
+"aXXaaXXa................",
+"aXa..aXXa...............",
+"aa...aXXa...............",
+"a.....aXXa..............",
+"......aXXa.....XXXX.....",
+".......aXXa..XXaaaaXX...",
+".......aXXa.XaaaaaaaaX..",
+"........aa.XaaaXXXXaaaX.",
+"...........XaaaaX..XaaX.",
+"..........XaaXaaaX..XaaX",
+"..........XaaXXaaaX.XaaX",
+"..........XaaX.XaaaXXaaX",
+"..........XaaX..XaaaXaaX",
+"...........XaaX..XaaaaX.",
+"...........XaaaXXXXaaaX.",
+"............XaaaaaaaaX..",
+".............XXaaaaXX...",
+"...............XXXX....."};
+#endif
+
+/* XPM */
+static const char * const copy_xpm[] = {
+"24 30 3 1",
+". c None",
+"a c #000000",
+"X c #FFFFFF",
+#if defined(Q_WS_WIN) // Windows cursor is traditionally white
+"aa......................",
+"aXa.....................",
+"aXXa....................",
+"aXXXa...................",
+"aXXXXa..................",
+"aXXXXXa.................",
+"aXXXXXXa................",
+"aXXXXXXXa...............",
+"aXXXXXXXXa..............",
+"aXXXXXXXXXa.............",
+"aXXXXXXaaaa.............",
+"aXXXaXXa................",
+"aXXaaXXa................",
+"aXa..aXXa...............",
+"aa...aXXa...............",
+"a.....aXXa..............",
+"......aXXa..............",
+".......aXXa.............",
+".......aXXa.............",
+"........aa...aaaaaaaaaaa",
+#else
+"XX......................",
+"XaX.....................",
+"XaaX....................",
+"XaaaX...................",
+"XaaaaX..................",
+"XaaaaaX.................",
+"XaaaaaaX................",
+"XaaaaaaaX...............",
+"XaaaaaaaaX..............",
+"XaaaaaaaaaX.............",
+"XaaaaaaXXXX.............",
+"XaaaXaaX................",
+"XaaXXaaX................",
+"XaX..XaaX...............",
+"XX...XaaX...............",
+"X.....XaaX..............",
+"......XaaX..............",
+".......XaaX.............",
+".......XaaX.............",
+"........XX...aaaaaaaaaaa",
+#endif
+".............aXXXXXXXXXa",
+".............aXXXXXXXXXa",
+".............aXXXXaXXXXa",
+".............aXXXXaXXXXa",
+".............aXXaaaaaXXa",
+".............aXXXXaXXXXa",
+".............aXXXXaXXXXa",
+".............aXXXXXXXXXa",
+".............aXXXXXXXXXa",
+".............aaaaaaaaaaa"};
+
+/* XPM */
+static const char * const link_xpm[] = {
+"24 30 3 1",
+". c None",
+"a c #000000",
+"X c #FFFFFF",
+#if defined(Q_WS_WIN) // Windows cursor is traditionally white
+"aa......................",
+"aXa.....................",
+"aXXa....................",
+"aXXXa...................",
+"aXXXXa..................",
+"aXXXXXa.................",
+"aXXXXXXa................",
+"aXXXXXXXa...............",
+"aXXXXXXXXa..............",
+"aXXXXXXXXXa.............",
+"aXXXXXXaaaa.............",
+"aXXXaXXa................",
+"aXXaaXXa................",
+"aXa..aXXa...............",
+"aa...aXXa...............",
+"a.....aXXa..............",
+"......aXXa..............",
+".......aXXa.............",
+".......aXXa.............",
+"........aa...aaaaaaaaaaa",
+#else
+"XX......................",
+"XaX.....................",
+"XaaX....................",
+"XaaaX...................",
+"XaaaaX..................",
+"XaaaaaX.................",
+"XaaaaaaX................",
+"XaaaaaaaX...............",
+"XaaaaaaaaX..............",
+"XaaaaaaaaaX.............",
+"XaaaaaaXXXX.............",
+"XaaaXaaX................",
+"XaaXXaaX................",
+"XaX..XaaX...............",
+"XX...XaaX...............",
+"X.....XaaX..............",
+"......XaaX..............",
+".......XaaX.............",
+".......XaaX.............",
+"........XX...aaaaaaaaaaa",
+#endif
+".............aXXXXXXXXXa",
+".............aXXXaaaaXXa",
+".............aXXXXaaaXXa",
+".............aXXXaaaaXXa",
+".............aXXaaaXaXXa",
+".............aXXaaXXXXXa",
+".............aXXaXXXXXXa",
+".............aXXXaXXXXXa",
+".............aXXXXXXXXXa",
+".............aaaaaaaaaaa"};
+
+#ifndef QT_NO_DRAGANDDROP
+
+//#define QDND_DEBUG
+
+#ifdef QDND_DEBUG
+QString dragActionsToString(Qt::DropActions actions)
+{
+ QString str;
+ if (actions == Qt::IgnoreAction) {
+ if (!str.isEmpty())
+ str += " | ";
+ str += "IgnoreAction";
+ }
+ if (actions & Qt::LinkAction) {
+ if (!str.isEmpty())
+ str += " | ";
+ str += "LinkAction";
+ }
+ if (actions & Qt::CopyAction) {
+ if (!str.isEmpty())
+ str += " | ";
+ str += "CopyAction";
+ }
+ if (actions & Qt::MoveAction) {
+ if (!str.isEmpty())
+ str += " | ";
+ str += "MoveAction";
+ }
+ if ((actions & Qt::TargetMoveAction) == Qt::TargetMoveAction ) {
+ if (!str.isEmpty())
+ str += " | ";
+ str += "TargetMoveAction";
+ }
+ return str;
+}
+
+QString KeyboardModifiersToString(Qt::KeyboardModifiers moderfies)
+{
+ QString str;
+ if (moderfies & Qt::ControlModifier) {
+ if (!str.isEmpty())
+ str += " | ";
+ str += Qt::ControlModifier;
+ }
+ if (moderfies & Qt::AltModifier) {
+ if (!str.isEmpty())
+ str += " | ";
+ str += Qt::AltModifier;
+ }
+ if (moderfies & Qt::ShiftModifier) {
+ if (!str.isEmpty())
+ str += " | ";
+ str += Qt::ShiftModifier;
+ }
+ return str;
+}
+#endif
+
+
+// the universe's only drag manager
+QDragManager *QDragManager::instance = 0;
+
+
+QDragManager::QDragManager()
+ : QObject(qApp)
+{
+ Q_ASSERT(!instance);
+
+#ifdef Q_WS_WIN
+ n_cursor = 4;
+#else
+ n_cursor = 3;
+#endif
+
+#ifdef Q_WS_QWS
+ currentActionForOverrideCursor = Qt::IgnoreAction;
+#endif
+ pm_cursor = new QPixmap[n_cursor];
+ pm_cursor[0] = QPixmap((const char **)move_xpm);
+ pm_cursor[1] = QPixmap((const char **)copy_xpm);
+ pm_cursor[2] = QPixmap((const char **)link_xpm);
+#ifdef Q_WS_WIN
+ pm_cursor[3] = QPixmap((const char **)ignore_xpm);
+#endif
+ object = 0;
+ beingCancelled = false;
+ restoreCursor = false;
+ willDrop = false;
+ eventLoop = 0;
+ dropData = new QDropData();
+ currentDropTarget = 0;
+#ifdef Q_WS_X11
+ xdndMimeTransferedPixmapIndex = 0;
+#endif
+}
+
+
+QDragManager::~QDragManager()
+{
+#ifndef QT_NO_CURSOR
+ if (restoreCursor)
+ QApplication::restoreOverrideCursor();
+#endif
+ instance = 0;
+ delete [] pm_cursor;
+ delete dropData;
+}
+
+QDragManager *QDragManager::self()
+{
+ if (!instance && qApp && !qApp->closingDown())
+ instance = new QDragManager;
+ return instance;
+}
+
+QPixmap QDragManager::dragCursor(Qt::DropAction action) const
+{
+ QDragPrivate * d = dragPrivate();
+ if (d && d->customCursors.contains(action))
+ return d->customCursors[action];
+ else if (action == Qt::MoveAction)
+ return pm_cursor[0];
+ else if (action == Qt::CopyAction)
+ return pm_cursor[1];
+ else if (action == Qt::LinkAction)
+ return pm_cursor[2];
+#ifdef Q_WS_WIN
+ else if (action == Qt::IgnoreAction)
+ return pm_cursor[3];
+#endif
+ return QPixmap();
+}
+
+bool QDragManager::hasCustomDragCursors() const
+{
+ QDragPrivate * d = dragPrivate();
+ return d && !d->customCursors.isEmpty();
+}
+
+Qt::DropAction QDragManager::defaultAction(Qt::DropActions possibleActions,
+ Qt::KeyboardModifiers modifiers) const
+{
+#ifdef QDND_DEBUG
+ qDebug("QDragManager::defaultAction(Qt::DropActions possibleActions)");
+ qDebug("keyboard modifiers : %s", KeyboardModifiersToString(modifiers).latin1());
+#endif
+
+ QDragPrivate *d = dragPrivate();
+ Qt::DropAction defaultAction = d ? d->defaultDropAction : Qt::IgnoreAction;
+
+ if (defaultAction == Qt::IgnoreAction) {
+ //This means that the drag was initiated by QDrag::start and we need to
+ //preserve the old behavior
+#ifdef Q_WS_MAC
+ defaultAction = Qt::MoveAction;
+#else
+ defaultAction = Qt::CopyAction;
+#endif
+ }
+
+#ifdef Q_WS_MAC
+ if (modifiers & Qt::ControlModifier && modifiers & Qt::AltModifier)
+ defaultAction = Qt::LinkAction;
+ else if (modifiers & Qt::AltModifier)
+ defaultAction = Qt::CopyAction;
+ else if (modifiers & Qt::ControlModifier)
+ defaultAction = Qt::MoveAction;
+#else
+ if (modifiers & Qt::ControlModifier && modifiers & Qt::ShiftModifier)
+ defaultAction = Qt::LinkAction;
+ else if (modifiers & Qt::ControlModifier)
+ defaultAction = Qt::CopyAction;
+ else if (modifiers & Qt::ShiftModifier)
+ defaultAction = Qt::MoveAction;
+ else if (modifiers & Qt::AltModifier)
+ defaultAction = Qt::LinkAction;
+#endif
+
+ // if the object is set take the list of possibles from it
+ if (object)
+ possibleActions = object->d_func()->possible_actions;
+
+#ifdef QDND_DEBUG
+ qDebug("possible actions : %s", dragActionsToString(possibleActions).latin1());
+#endif
+
+ // Check if the action determined is allowed
+ if (!(possibleActions & defaultAction)) {
+ if (possibleActions & Qt::CopyAction)
+ defaultAction = Qt::CopyAction;
+ else if (possibleActions & Qt::MoveAction)
+ defaultAction = Qt::MoveAction;
+ else if (possibleActions & Qt::LinkAction)
+ defaultAction = Qt::LinkAction;
+ else
+ defaultAction = Qt::IgnoreAction;
+ }
+
+#ifdef QDND_DEBUG
+ qDebug("default action : %s", dragActionsToString(defaultAction).latin1());
+#endif
+
+ return defaultAction;
+}
+
+void QDragManager::setCurrentTarget(QWidget *target, bool dropped)
+{
+ if (currentDropTarget == target)
+ return;
+
+ currentDropTarget = target;
+ if (!dropped && object) {
+ object->d_func()->target = target;
+ emit object->targetChanged(target);
+ }
+
+}
+
+QWidget *QDragManager::currentTarget()
+{
+ return currentDropTarget;
+}
+
+#endif
+
+QDropData::QDropData()
+ : QInternalMimeData()
+{
+}
+
+QDropData::~QDropData()
+{
+}
+#endif // QT_NO_DRAGANDDROP
+
+#if !(defined(QT_NO_DRAGANDDROP) && defined(QT_NO_CLIPBOARD))
+
+static QStringList imageReadMimeFormats()
+{
+ QStringList formats;
+ QList<QByteArray> imageFormats = QImageReader::supportedImageFormats();
+ for (int i = 0; i < imageFormats.size(); ++i) {
+ QString format = QLatin1String("image/");
+ format += QString::fromLatin1(imageFormats.at(i).toLower());
+ formats.append(format);
+ }
+
+ //put png at the front because it is best
+ int pngIndex = formats.indexOf(QLatin1String("image/png"));
+ if (pngIndex != -1 && pngIndex != 0)
+ formats.move(pngIndex, 0);
+
+ return formats;
+}
+
+
+static QStringList imageWriteMimeFormats()
+{
+ QStringList formats;
+ QList<QByteArray> imageFormats = QImageWriter::supportedImageFormats();
+ for (int i = 0; i < imageFormats.size(); ++i) {
+ QString format = QLatin1String("image/");
+ format += QString::fromLatin1(imageFormats.at(i).toLower());
+ formats.append(format);
+ }
+
+ //put png at the front because it is best
+ int pngIndex = formats.indexOf(QLatin1String("image/png"));
+ if (pngIndex != -1 && pngIndex != 0)
+ formats.move(pngIndex, 0);
+
+ return formats;
+}
+
+QInternalMimeData::QInternalMimeData()
+ : QMimeData()
+{
+}
+
+QInternalMimeData::~QInternalMimeData()
+{
+}
+
+bool QInternalMimeData::hasFormat(const QString &mimeType) const
+{
+ bool foundFormat = hasFormat_sys(mimeType);
+ if (!foundFormat && mimeType == QLatin1String("application/x-qt-image")) {
+ QStringList imageFormats = imageReadMimeFormats();
+ for (int i = 0; i < imageFormats.size(); ++i) {
+ if ((foundFormat = hasFormat_sys(imageFormats.at(i))))
+ break;
+ }
+ }
+ return foundFormat;
+}
+
+QStringList QInternalMimeData::formats() const
+{
+ QStringList realFormats = formats_sys();
+ if (!realFormats.contains(QLatin1String("application/x-qt-image"))) {
+ QStringList imageFormats = imageReadMimeFormats();
+ for (int i = 0; i < imageFormats.size(); ++i) {
+ if (realFormats.contains(imageFormats.at(i))) {
+ realFormats += QLatin1String("application/x-qt-image");
+ break;
+ }
+ }
+ }
+ return realFormats;
+}
+
+QVariant QInternalMimeData::retrieveData(const QString &mimeType, QVariant::Type type) const
+{
+ QVariant data = retrieveData_sys(mimeType, type);
+ if (mimeType == QLatin1String("application/x-qt-image")) {
+ if (data.isNull() || (data.type() == QVariant::ByteArray && data.toByteArray().isEmpty())) {
+ // try to find an image
+ QStringList imageFormats = imageReadMimeFormats();
+ for (int i = 0; i < imageFormats.size(); ++i) {
+ data = retrieveData_sys(imageFormats.at(i), type);
+ if (data.isNull() || (data.type() == QVariant::ByteArray && data.toByteArray().isEmpty()))
+ continue;
+ break;
+ }
+ }
+ // we wanted some image type, but all we got was a byte array. Convert it to an image.
+ if (data.type() == QVariant::ByteArray
+ && (type == QVariant::Image || type == QVariant::Pixmap || type == QVariant::Bitmap))
+ data = QImage::fromData(data.toByteArray());
+
+ } else if (mimeType == QLatin1String("application/x-color") && data.type() == QVariant::ByteArray) {
+ QColor c;
+ QByteArray ba = data.toByteArray();
+ if (ba.size() == 8) {
+ ushort * colBuf = (ushort *)ba.data();
+ c.setRgbF(qreal(colBuf[0]) / qreal(0xFFFF),
+ qreal(colBuf[1]) / qreal(0xFFFF),
+ qreal(colBuf[2]) / qreal(0xFFFF),
+ qreal(colBuf[3]) / qreal(0xFFFF));
+ data = c;
+ } else {
+ qWarning("Qt: Invalid color format");
+ }
+ } else if (data.type() != type && data.type() == QVariant::ByteArray) {
+ // try to use mime data's internal conversion stuf.
+ QInternalMimeData *that = const_cast<QInternalMimeData *>(this);
+ that->setData(mimeType, data.toByteArray());
+ data = QMimeData::retrieveData(mimeType, type);
+ that->clear();
+ }
+ return data;
+}
+
+bool QInternalMimeData::canReadData(const QString &mimeType)
+{
+ return imageReadMimeFormats().contains(mimeType);
+}
+
+// helper functions for rendering mimedata to the system, this is needed because QMimeData is in core.
+QStringList QInternalMimeData::formatsHelper(const QMimeData *data)
+{
+ QStringList realFormats = data->formats();
+ if (realFormats.contains(QLatin1String("application/x-qt-image"))) {
+ // add all supported image formats
+ QStringList imageFormats = imageWriteMimeFormats();
+ for (int i = 0; i < imageFormats.size(); ++i) {
+ if (!realFormats.contains(imageFormats.at(i)))
+ realFormats.append(imageFormats.at(i));
+ }
+ }
+ return realFormats;
+}
+
+bool QInternalMimeData::hasFormatHelper(const QString &mimeType, const QMimeData *data)
+{
+
+ bool foundFormat = data->hasFormat(mimeType);
+ if (!foundFormat) {
+ if (mimeType == QLatin1String("application/x-qt-image")) {
+ // check all supported image formats
+ QStringList imageFormats = imageWriteMimeFormats();
+ for (int i = 0; i < imageFormats.size(); ++i) {
+ if ((foundFormat = data->hasFormat(imageFormats.at(i))))
+ break;
+ }
+ } else if (mimeType.startsWith(QLatin1String("image/"))) {
+ return data->hasImage() && imageWriteMimeFormats().contains(mimeType);
+ }
+ }
+ return foundFormat;
+}
+
+QByteArray QInternalMimeData::renderDataHelper(const QString &mimeType, const QMimeData *data)
+{
+ QByteArray ba;
+ if (mimeType == QLatin1String("application/x-color")) {
+ /* QMimeData can only provide colors as QColor or the name
+ of a color as a QByteArray or a QString. So we need to do
+ the conversion to application/x-color here.
+ The application/x-color format is :
+ type: application/x-color
+ format: 16
+ data[0]: red
+ data[1]: green
+ data[2]: blue
+ data[3]: opacity
+ */
+ ba.resize(8);
+ ushort * colBuf = (ushort *)ba.data();
+ QColor c = qvariant_cast<QColor>(data->colorData());
+ colBuf[0] = ushort(c.redF() * 0xFFFF);
+ colBuf[1] = ushort(c.greenF() * 0xFFFF);
+ colBuf[2] = ushort(c.blueF() * 0xFFFF);
+ colBuf[3] = ushort(c.alphaF() * 0xFFFF);
+ } else {
+ ba = data->data(mimeType);
+ if (ba.isEmpty()) {
+ if (mimeType == QLatin1String("application/x-qt-image") && data->hasImage()) {
+ QImage image = qvariant_cast<QImage>(data->imageData());
+ QBuffer buf(&ba);
+ buf.open(QBuffer::WriteOnly);
+ // would there not be PNG ??
+ image.save(&buf, "PNG");
+ } else if (mimeType.startsWith(QLatin1String("image/")) && data->hasImage()) {
+ QImage image = qvariant_cast<QImage>(data->imageData());
+ QBuffer buf(&ba);
+ buf.open(QBuffer::WriteOnly);
+ image.save(&buf, mimeType.mid(mimeType.indexOf(QLatin1Char('/')) + 1).toLatin1().toUpper());
+ }
+ }
+ }
+ return ba;
+}
+
+#endif // QT_NO_DRAGANDDROP && QT_NO_CLIPBOARD
+
+QT_END_NAMESPACE
diff --git a/src/gui/kernel/qdnd_mac.mm b/src/gui/kernel/qdnd_mac.mm
new file mode 100644
index 0000000000..0b8a48569e
--- /dev/null
+++ b/src/gui/kernel/qdnd_mac.mm
@@ -0,0 +1,749 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qapplication.h"
+#ifndef QT_NO_DRAGANDDROP
+#include "qbitmap.h"
+#include "qcursor.h"
+#include "qevent.h"
+#include "qpainter.h"
+#include "qurl.h"
+#include "qwidget.h"
+#include "qfile.h"
+#include "qfileinfo.h"
+#include <stdlib.h>
+#include <string.h>
+#ifndef QT_NO_ACCESSIBILITY
+# include "qaccessible.h"
+#endif
+
+#include <private/qapplication_p.h>
+#include <private/qwidget_p.h>
+#include <private/qdnd_p.h>
+#include <private/qt_mac_p.h>
+
+QT_BEGIN_NAMESPACE
+
+QT_USE_NAMESPACE
+
+QMacDndAnswerRecord qt_mac_dnd_answer_rec;
+
+/*****************************************************************************
+ QDnD debug facilities
+ *****************************************************************************/
+//#define DEBUG_DRAG_EVENTS
+//#define DEBUG_DRAG_PROMISES
+
+/*****************************************************************************
+ QDnD globals
+ *****************************************************************************/
+bool qt_mac_in_drag = false;
+#ifndef QT_MAC_USE_COCOA
+static DragReference qt_mac_current_dragRef = 0;
+#endif
+
+/*****************************************************************************
+ Externals
+ *****************************************************************************/
+extern void qt_mac_send_modifiers_changed(quint32, QObject *); //qapplication_mac.cpp
+extern uint qGlobalPostedEventsCount(); //qapplication.cpp
+extern RgnHandle qt_mac_get_rgn(); // qregion_mac.cpp
+extern void qt_mac_dispose_rgn(RgnHandle); // qregion_mac.cpp
+/*****************************************************************************
+ QDnD utility functions
+ *****************************************************************************/
+
+//default pixmap
+static const int default_pm_hotx = -2;
+static const int default_pm_hoty = -16;
+#ifndef QT_MAC_USE_COCOA
+static const char * const default_pm[] = {
+ "13 9 3 1",
+ ". c None",
+ " c #000000",
+ "X c #FFFFFF",
+ "X X X X X X X",
+ " X X X X X X ",
+ "X ......... X",
+ " X.........X ",
+ "X ......... X",
+ " X.........X ",
+ "X ......... X",
+ " X X X X X X ",
+ "X X X X X X X",
+};
+#endif
+
+//action management
+#ifdef DEBUG_DRAG_EVENTS
+# define MAP_MAC_ENUM(x) x, #x
+#else
+# define MAP_MAC_ENUM(x) x
+#endif
+struct mac_enum_mapper
+{
+ int mac_code;
+ int qt_code;
+#ifdef DEBUG_DRAG_EVENTS
+ char *qt_desc;
+#endif
+};
+#ifndef QT_MAC_USE_COCOA
+static mac_enum_mapper dnd_action_symbols[] = {
+ { kDragActionAlias, MAP_MAC_ENUM(Qt::LinkAction) },
+ { kDragActionMove, MAP_MAC_ENUM(Qt::MoveAction) },
+ { kDragActionGeneric, MAP_MAC_ENUM(Qt::CopyAction) },
+ { kDragActionCopy, MAP_MAC_ENUM(Qt::CopyAction) },
+ { 0, MAP_MAC_ENUM(0) }
+};
+static DragActions qt_mac_dnd_map_qt_actions(Qt::DropActions qActions)
+{
+ DragActions ret = kDragActionNothing;
+ for(int i = 0; dnd_action_symbols[i].qt_code; ++i) {
+ if(qActions & dnd_action_symbols[i].qt_code)
+ ret |= dnd_action_symbols[i].mac_code;
+ }
+ return ret;
+}
+static Qt::DropActions qt_mac_dnd_map_mac_actions(DragActions macActions)
+{
+#ifdef DEBUG_DRAG_EVENTS
+ qDebug("Converting DND ActionList: 0x%lx", macActions);
+#endif
+ Qt::DropActions ret = Qt::IgnoreAction;
+ for(int i = 0; dnd_action_symbols[i].qt_code; ++i) {
+#ifdef DEBUG_DRAG_EVENTS
+ qDebug(" %d) [%s] : %s", i, dnd_action_symbols[i].qt_desc,
+ (macActions & dnd_action_symbols[i].mac_code) ? "true" : "false");
+#endif
+ if(macActions & dnd_action_symbols[i].mac_code)
+ ret |= Qt::DropAction(dnd_action_symbols[i].qt_code);
+ }
+ return ret;
+}
+static Qt::DropAction qt_mac_dnd_map_mac_default_action(DragActions macActions)
+{
+ static Qt::DropAction preferred_actions[] = { Qt::CopyAction, Qt::LinkAction, //in order
+ Qt::MoveAction, Qt::IgnoreAction };
+ Qt::DropAction ret = Qt::IgnoreAction;
+ const Qt::DropActions qtActions = qt_mac_dnd_map_mac_actions(macActions);
+ for(int i = 0; preferred_actions[i] != Qt::IgnoreAction; ++i) {
+ if(qtActions & preferred_actions[i]) {
+ ret = preferred_actions[i];
+ break;
+ }
+ }
+#ifdef DEBUG_DRAG_EVENTS
+ for(int i = 0; dnd_action_symbols[i].qt_code; ++i) {
+ if(dnd_action_symbols[i].qt_code == ret) {
+ qDebug("Got default action: %s [0x%lx]", dnd_action_symbols[i].qt_desc, macActions);
+ break;
+ }
+ }
+#endif
+ return ret;
+}
+static void qt_mac_dnd_update_action(DragReference dragRef) {
+ SInt16 modifiers;
+ GetDragModifiers(dragRef, &modifiers, 0, 0);
+ qt_mac_send_modifiers_changed(modifiers, qApp);
+
+ Qt::DropAction qtAction = Qt::IgnoreAction;
+ {
+ DragActions macAllowed = kDragActionNothing;
+ GetDragDropAction(dragRef, &macAllowed);
+ Qt::DropActions qtAllowed = qt_mac_dnd_map_mac_actions(macAllowed);
+ qtAction = QDragManager::self()->defaultAction(qtAllowed, QApplication::keyboardModifiers());
+#if 1
+ if(!(qtAllowed & qtAction))
+ qtAction = qt_mac_dnd_map_mac_default_action(macAllowed);
+#endif
+ }
+ DragActions macAction = qt_mac_dnd_map_qt_actions(qtAction);
+
+ DragActions currentActions;
+ GetDragDropAction(dragRef, &currentActions);
+ if(currentActions != macAction) {
+ SetDragDropAction(dragRef, macAction);
+ QDragManager::self()->emitActionChanged(qtAction);
+ }
+}
+#endif // !QT_MAC_USE_COCOA
+
+/*****************************************************************************
+ DnD functions
+ *****************************************************************************/
+bool QDropData::hasFormat_sys(const QString &mime) const
+{
+#ifndef QT_MAC_USE_COCOA
+ OSPasteboardRef board;
+ if(GetDragPasteboard(qt_mac_current_dragRef, &board) != noErr) {
+ qDebug("DnD: Cannot get PasteBoard!");
+ return false;
+ }
+ return QMacPasteboard(board, QMacPasteboardMime::MIME_DND).hasFormat(mime);
+#else
+ Q_UNUSED(mime);
+ return false;
+#endif // !QT_MAC_USE_COCOA
+}
+
+QVariant QDropData::retrieveData_sys(const QString &mime, QVariant::Type type) const
+{
+#ifndef QT_MAC_USE_COCOA
+ OSPasteboardRef board;
+ if(GetDragPasteboard(qt_mac_current_dragRef, &board) != noErr) {
+ qDebug("DnD: Cannot get PasteBoard!");
+ return QVariant();
+ }
+ return QMacPasteboard(board, QMacPasteboardMime::MIME_DND).retrieveData(mime, type);
+#else
+ Q_UNUSED(mime);
+ Q_UNUSED(type);
+ return QVariant();
+#endif // !QT_MAC_USE_COCOA
+}
+
+QStringList QDropData::formats_sys() const
+{
+#ifndef QT_MAC_USE_COCOA
+ OSPasteboardRef board;
+ if(GetDragPasteboard(qt_mac_current_dragRef, &board) != noErr) {
+ qDebug("DnD: Cannot get PasteBoard!");
+ return QStringList();
+ }
+ return QMacPasteboard(board, QMacPasteboardMime::MIME_DND).formats();
+#else
+ return QStringList();
+#endif
+}
+
+void QDragManager::timerEvent(QTimerEvent*)
+{
+}
+
+bool QDragManager::eventFilter(QObject *, QEvent *)
+{
+ return false;
+}
+
+void QDragManager::updateCursor()
+{
+}
+
+void QDragManager::cancel(bool)
+{
+ if(object) {
+ beingCancelled = true;
+ object = 0;
+ }
+#ifndef QT_NO_ACCESSIBILITY
+ QAccessible::updateAccessibility(this, 0, QAccessible::DragDropEnd);
+#endif
+}
+
+void QDragManager::move(const QPoint &)
+{
+}
+
+void QDragManager::drop()
+{
+}
+
+/**
+ If a drop action is already set on the carbon event
+ (from e.g. an earlier enter event), we insert the same
+ action on the new Qt event that has yet to be sendt.
+*/
+static inline bool qt_mac_set_existing_drop_action(const DragRef &dragRef, QDropEvent &event)
+{
+#ifndef QT_MAC_USE_COCOA
+ DragActions currentAction = kDragActionNothing;
+ OSStatus err = GetDragDropAction(dragRef, &currentAction);
+ if (err == noErr && currentAction != kDragActionNothing) {
+ // This looks a bit evil, but we only ever set one action, so it's OK.
+ event.setDropAction(Qt::DropAction(int(qt_mac_dnd_map_mac_actions(currentAction))));
+ return true;
+ }
+#else
+ Q_UNUSED(dragRef);
+ Q_UNUSED(event);
+#endif
+ return false;
+}
+
+/**
+ If an answer rect has been set on the event (after being sent
+ to the global event processor), we store that rect so we can
+ check if the mouse is in the same area upon next drag move event.
+*/
+void qt_mac_copy_answer_rect(const QDragMoveEvent &event)
+{
+ if (!event.answerRect().isEmpty()) {
+ qt_mac_dnd_answer_rec.rect = event.answerRect();
+ qt_mac_dnd_answer_rec.buttons = event.mouseButtons();
+ qt_mac_dnd_answer_rec.modifiers = event.keyboardModifiers();
+ qt_mac_dnd_answer_rec.lastAction = event.dropAction();
+ }
+}
+
+bool qt_mac_mouse_inside_answer_rect(QPoint mouse)
+{
+ if (!qt_mac_dnd_answer_rec.rect.isEmpty()
+ && qt_mac_dnd_answer_rec.rect.contains(mouse)
+ && QApplication::mouseButtons() == qt_mac_dnd_answer_rec.buttons
+ && QApplication::keyboardModifiers() == qt_mac_dnd_answer_rec.modifiers)
+ return true;
+ else
+ return false;
+}
+
+bool QWidgetPrivate::qt_mac_dnd_event(uint kind, DragRef dragRef)
+{
+#ifndef QT_MAC_USE_COCOA
+ Q_Q(QWidget);
+ qt_mac_current_dragRef = dragRef;
+ if (kind != kEventControlDragLeave)
+ qt_mac_dnd_update_action(dragRef);
+
+ Point mouse;
+ GetDragMouse(dragRef, &mouse, 0L);
+ if(!mouse.h && !mouse.v)
+ GetGlobalMouse(&mouse);
+
+ if(QApplicationPrivate::modalState()) {
+ for(QWidget *modal = q; modal; modal = modal->parentWidget()) {
+ if(modal->isWindow()) {
+ if(modal != QApplication::activeModalWidget())
+ return noErr;
+ break;
+ }
+ }
+ }
+
+ //lookup the possible actions
+ DragActions allowed = kDragActionNothing;
+ GetDragAllowableActions(dragRef, &allowed);
+ Qt::DropActions qtAllowed = qt_mac_dnd_map_mac_actions(allowed);
+
+ //lookup the source dragAccepted
+ QMimeData *dropdata = QDragManager::self()->dropData;
+ if(QDragManager::self()->source())
+ dropdata = QDragManager::self()->dragPrivate()->data;
+
+ // 'interrestedInDrag' should end up being 'true' if a later drop
+ // will be accepted by the widget for the current mouse position
+ bool interrestedInDrag = true;
+
+ //Dispatch events
+ if (kind == kEventControlDragWithin) {
+ if (qt_mac_mouse_inside_answer_rect(q->mapFromGlobal(QPoint(mouse.h, mouse.v))))
+ return qt_mac_dnd_answer_rec.lastAction == Qt::IgnoreAction;
+ else
+ qt_mac_dnd_answer_rec.clear();
+
+ QDragMoveEvent qDMEvent(q->mapFromGlobal(QPoint(mouse.h, mouse.v)), qtAllowed, dropdata,
+ QApplication::mouseButtons(), QApplication::keyboardModifiers());
+
+ // Accept the event by default if a
+ // drag action exists on the carbon event
+ if (qt_mac_set_existing_drop_action(dragRef, qDMEvent))
+ qDMEvent.accept();
+
+ QApplication::sendEvent(q, &qDMEvent);
+ if (!qDMEvent.isAccepted() || qDMEvent.dropAction() == Qt::IgnoreAction)
+ interrestedInDrag = false;
+
+ qt_mac_copy_answer_rect(qDMEvent);
+ SetDragDropAction(dragRef, qt_mac_dnd_map_qt_actions(qDMEvent.dropAction()));
+
+ } else if (kind == kEventControlDragEnter) {
+ qt_mac_dnd_answer_rec.clear();
+
+ QDragEnterEvent qDEEvent(q->mapFromGlobal(QPoint(mouse.h, mouse.v)), qtAllowed, dropdata,
+ QApplication::mouseButtons(), QApplication::keyboardModifiers());
+ qt_mac_set_existing_drop_action(dragRef, qDEEvent);
+ QApplication::sendEvent(q, &qDEEvent);
+ SetDragDropAction(dragRef, qt_mac_dnd_map_qt_actions(qDEEvent.dropAction()));
+
+ if (!qDEEvent.isAccepted())
+ // The widget is simply not interrested in this
+ // drag. So tell carbon this by returning 'false'. We will then
+ // not receive any further move, drop or leave events for this widget.
+ return false;
+ else {
+ // Documentation states that a drag move event is sendt immidiatly after
+ // a drag enter event. So we do that. This will honor widgets overriding
+ // 'dragMoveEvent' only, and not 'dragEnterEvent'
+ QDragMoveEvent qDMEvent(q->mapFromGlobal(QPoint(mouse.h, mouse.v)), qtAllowed, dropdata,
+ QApplication::mouseButtons(), QApplication::keyboardModifiers());
+ qDMEvent.accept(); // accept by default, since enter event was accepted.
+ qDMEvent.setDropAction(qDEEvent.dropAction());
+ QApplication::sendEvent(q, &qDMEvent);
+ if (!qDMEvent.isAccepted() || qDMEvent.dropAction() == Qt::IgnoreAction)
+ interrestedInDrag = false;
+
+ qt_mac_copy_answer_rect(qDMEvent);
+ SetDragDropAction(dragRef, qt_mac_dnd_map_qt_actions(qDMEvent.dropAction()));
+ }
+
+ } else if(kind == kEventControlDragLeave) {
+ QDragLeaveEvent de;
+ QApplication::sendEvent(q, &de);
+ } else if(kind == kEventControlDragReceive) {
+ QDropEvent de(q->mapFromGlobal(QPoint(mouse.h, mouse.v)), qtAllowed, dropdata,
+ QApplication::mouseButtons(), QApplication::keyboardModifiers());
+ if(QDragManager::self()->object)
+ QDragManager::self()->dragPrivate()->target = q;
+ QApplication::sendEvent(q, &de);
+ if(!de.isAccepted()) {
+ interrestedInDrag = false;
+ SetDragDropAction(dragRef, kDragActionNothing);
+ } else {
+ if(QDragManager::self()->object)
+ QDragManager::self()->dragPrivate()->executed_action = de.dropAction();
+ SetDragDropAction(dragRef, qt_mac_dnd_map_qt_actions(de.dropAction()));
+ }
+ }
+
+#ifdef DEBUG_DRAG_EVENTS
+ {
+ const char *desc = 0;
+ switch(kind) {
+ case kEventControlDragWithin: desc = "DragMove"; break;
+ case kEventControlDragEnter: desc = "DragEnter"; break;
+ case kEventControlDragLeave: desc = "DragLeave"; break;
+ case kEventControlDragReceive: desc = "DragDrop"; break;
+ }
+ if(desc) {
+ QPoint pos(q->mapFromGlobal(QPoint(mouse.h, mouse.v)));
+ qDebug("Sending <%s>(%d, %d) event to %p(%s::%s) [%d] (%p)",
+ desc, pos.x(), pos.y(), q, q->metaObject()->className(),
+ q->objectName().toLocal8Bit().constData(), ret, dragRef);
+ }
+ }
+#endif
+
+ //set the cursor
+ bool found_cursor = false;
+ if(kind == kEventControlDragWithin || kind == kEventControlDragEnter) {
+ ThemeCursor cursor = kThemeNotAllowedCursor;
+ found_cursor = true;
+ if (interrestedInDrag) {
+ DragActions action = kDragActionNothing;
+ GetDragDropAction(dragRef, &action);
+ switch(qt_mac_dnd_map_mac_default_action(action)) {
+ case Qt::IgnoreAction:
+ cursor = kThemeNotAllowedCursor;
+ break;
+ case Qt::MoveAction:
+ cursor = kThemeArrowCursor;
+ break;
+ case Qt::CopyAction:
+ cursor = kThemeCopyArrowCursor;
+ break;
+ case Qt::LinkAction:
+ cursor = kThemeAliasArrowCursor;
+ break;
+ default:
+ cursor = kThemeNotAllowedCursor;
+ break;
+ }
+ }
+ SetThemeCursor(cursor);
+ }
+ if(found_cursor) {
+ qt_mac_set_cursor(0, QPoint()); //just use our's
+ } else {
+ QCursor cursor(Qt::ArrowCursor);
+ if(qApp && qApp->overrideCursor()) {
+ cursor = *qApp->overrideCursor();
+ } else if(q) {
+ for(QWidget *p = q; p; p = p->parentWidget()) {
+ if(p->isEnabled() && p->testAttribute(Qt::WA_SetCursor)) {
+ cursor = p->cursor();
+ break;
+ }
+ }
+ }
+ qt_mac_set_cursor(&cursor, QPoint(mouse.h, mouse.v));
+ }
+
+ //idle things
+ if(qGlobalPostedEventsCount()) {
+ QApplication::sendPostedEvents();
+ QApplication::flush();
+ }
+
+ // If this was not a drop, tell carbon that we will be interresed in receiving more
+ // events for the current drag. We do that by returning true.
+ if (kind == kEventControlDragReceive)
+ return interrestedInDrag;
+ else
+ return true;
+#else
+ Q_UNUSED(kind);
+ Q_UNUSED(dragRef);
+ return false;
+#endif // !QT_MAC_USE_COCOA
+}
+
+#ifndef QT_MAC_USE_COCOA
+Qt::DropAction QDragManager::drag(QDrag *o)
+{
+
+ if(qt_mac_in_drag) { //just make sure..
+ qWarning("Qt: Internal error: WH0A, unexpected condition reached");
+ return Qt::IgnoreAction;
+ }
+ if(object == o)
+ return Qt::IgnoreAction;
+ /* At the moment it seems clear that Mac OS X does not want to drag with a non-left button
+ so we just bail early to prevent it */
+ if(!(GetCurrentEventButtonState() & kEventMouseButtonPrimary))
+ return Qt::IgnoreAction;
+
+ if(object) {
+ dragPrivate()->source->removeEventFilter(this);
+ cancel();
+ beingCancelled = false;
+ }
+
+ object = o;
+ dragPrivate()->target = 0;
+
+#ifndef QT_NO_ACCESSIBILITY
+ QAccessible::updateAccessibility(this, 0, QAccessible::DragDropStart);
+#endif
+
+ //setup the data
+ QMacPasteboard dragBoard(QMacPasteboardMime::MIME_DND);
+ dragBoard.setMimeData(dragPrivate()->data);
+
+ //create the drag
+ OSErr result;
+ DragRef dragRef;
+ if((result = NewDragWithPasteboard(dragBoard.pasteBoard(), &dragRef)))
+ return Qt::IgnoreAction;
+ //setup the actions
+ DragActions possibleActions = qt_mac_dnd_map_qt_actions(dragPrivate()->possible_actions);
+ SetDragAllowableActions(dragRef, //local
+ possibleActions,
+ true);
+ SetDragAllowableActions(dragRef, //remote (same as local)
+ possibleActions,
+ false);
+
+
+ QPoint hotspot;
+ QPixmap pix = dragPrivate()->pixmap;
+ if(pix.isNull()) {
+ if(dragPrivate()->data->hasText() || dragPrivate()->data->hasUrls()) {
+ //get the string
+ QString s = dragPrivate()->data->hasText() ? dragPrivate()->data->text()
+ : dragPrivate()->data->urls().first().toString();
+ if(s.length() > 26)
+ s = s.left(23) + QChar(0x2026);
+ if(!s.isEmpty()) {
+ //draw it
+ QFont f(qApp->font());
+ f.setPointSize(12);
+ QFontMetrics fm(f);
+ const int width = fm.width(s);
+ const int height = fm.height();
+ if(width > 0 && height > 0) {
+ QPixmap tmp(width, height);
+ QPainter p(&tmp);
+ p.fillRect(0, 0, tmp.width(), tmp.height(), Qt::color0);
+ p.setPen(Qt::color1);
+ p.setFont(f);
+ p.drawText(0, fm.ascent(), s);
+ p.end();
+ //save it
+ pix = tmp;
+ hotspot = QPoint(tmp.width() / 2, tmp.height() / 2);
+ }
+ }
+ } else {
+ pix = QPixmap(default_pm);
+ hotspot = QPoint(default_pm_hotx, default_pm_hoty);
+ }
+ } else {
+ hotspot = dragPrivate()->hotspot;
+ }
+
+ //so we must fake an event
+ EventRecord fakeEvent;
+ GetGlobalMouse(&(fakeEvent.where));
+ fakeEvent.message = 0;
+ fakeEvent.what = mouseDown;
+ fakeEvent.when = EventTimeToTicks(GetCurrentEventTime());
+ fakeEvent.modifiers = GetCurrentKeyModifiers();
+ if(GetCurrentEventButtonState() & 2)
+ fakeEvent.modifiers |= controlKey;
+
+ //find the hotspot in relation to the pixmap
+ Point boundsPoint;
+ boundsPoint.h = fakeEvent.where.h - hotspot.x();
+ boundsPoint.v = fakeEvent.where.v - hotspot.y();
+ Rect boundsRect;
+ SetRect(&boundsRect, boundsPoint.h, boundsPoint.v, boundsPoint.h + pix.width(), boundsPoint.v + pix.height());
+
+ //set the drag image
+ QRegion dragRegion(boundsPoint.h, boundsPoint.v, pix.width(), pix.height()), pixRegion;
+ if(!pix.isNull()) {
+ HIPoint hipoint = { -hotspot.x(), -hotspot.y() };
+ CGImageRef img = (CGImageRef)pix.macCGHandle();
+ SetDragImageWithCGImage(dragRef, img, &hipoint, 0);
+ CGImageRelease(img);
+ }
+
+ SetDragItemBounds(dragRef, (ItemReference)1 , &boundsRect);
+ { //do the drag
+ qt_mac_in_drag = true;
+ qt_mac_dnd_update_action(dragRef);
+ result = TrackDrag(dragRef, &fakeEvent, QMacSmartQuickDrawRegion(dragRegion.toQDRgn()));
+ qt_mac_in_drag = false;
+ }
+ object = 0;
+ if(result == noErr) {
+ // Check if the receiver points us to
+ // a file location. If so, we need to do
+ // the file copy/move ourselves:
+ QCFType<CFURLRef> pasteLocation = 0;
+ PasteboardCopyPasteLocation(dragBoard.pasteBoard(), &pasteLocation);
+ if (pasteLocation){
+ Qt::DropAction action = o->d_func()->defaultDropAction;
+ if (action == Qt::IgnoreAction){
+ if (o->d_func()->possible_actions & Qt::MoveAction)
+ action = Qt::MoveAction;
+ else if (o->d_func()->possible_actions & Qt::CopyAction)
+ action = Qt::CopyAction;
+
+ }
+ bool atleastOne = false;
+ QList<QUrl> urls = o->mimeData()->urls();
+ for (int i = 0; i < urls.size(); ++i){
+ QUrl fromUrl = urls.at(i);
+ QString filename = QFileInfo(fromUrl.path()).fileName();
+ QUrl toUrl(QCFString::toQString(CFURLGetString(pasteLocation)) + filename);
+ if (action == Qt::MoveAction){
+ if (QFile::rename(fromUrl.path(), toUrl.path()))
+ atleastOne = true;
+ } else if (action == Qt::CopyAction){
+ if (QFile::copy(fromUrl.path(), toUrl.path()))
+ atleastOne = true;
+ }
+ }
+ if (atleastOne){
+ DisposeDrag(dragRef);
+ return action;
+ }
+ }
+
+ DragActions ret = kDragActionNothing;
+ GetDragDropAction(dragRef, &ret);
+ DisposeDrag(dragRef); //cleanup
+ return qt_mac_dnd_map_mac_default_action(ret);
+ }
+ DisposeDrag(dragRef); //cleanup
+ return Qt::IgnoreAction;
+}
+#endif
+
+void QDragManager::updatePixmap()
+{
+}
+
+QCocoaDropData::QCocoaDropData(CFStringRef pasteboard)
+ : QInternalMimeData()
+{
+ NSString* pasteboardName = (NSString*)pasteboard;
+ [pasteboardName retain];
+ dropPasteboard = pasteboard;
+}
+
+QCocoaDropData::~QCocoaDropData()
+{
+ NSString* pasteboardName = (NSString*)dropPasteboard;
+ [pasteboardName release];
+}
+
+QStringList QCocoaDropData::formats_sys() const
+{
+ QStringList formats;
+ OSPasteboardRef board;
+ if (PasteboardCreate(dropPasteboard, &board) != noErr) {
+ qDebug("DnD: Cannot get PasteBoard!");
+ return formats;
+ }
+ formats = QMacPasteboard(board, QMacPasteboardMime::MIME_DND).formats();
+ return formats;
+}
+
+QVariant QCocoaDropData::retrieveData_sys(const QString &mimeType, QVariant::Type type) const
+{
+ QVariant data;
+ OSPasteboardRef board;
+ if (PasteboardCreate(dropPasteboard, &board) != noErr) {
+ qDebug("DnD: Cannot get PasteBoard!");
+ return data;
+ }
+ data = QMacPasteboard(board, QMacPasteboardMime::MIME_DND).retrieveData(mimeType, type);
+ CFRelease(board);
+ return data;
+}
+
+bool QCocoaDropData::hasFormat_sys(const QString &mimeType) const
+{
+ bool has = false;
+ OSPasteboardRef board;
+ if (PasteboardCreate(dropPasteboard, &board) != noErr) {
+ qDebug("DnD: Cannot get PasteBoard!");
+ return has;
+ }
+ has = QMacPasteboard(board, QMacPasteboardMime::MIME_DND).hasFormat(mimeType);
+ CFRelease(board);
+ return has;
+}
+
+#endif // QT_NO_DRAGANDDROP
+QT_END_NAMESPACE
diff --git a/src/gui/kernel/qdnd_p.h b/src/gui/kernel/qdnd_p.h
new file mode 100644
index 0000000000..7a0cb7ac45
--- /dev/null
+++ b/src/gui/kernel/qdnd_p.h
@@ -0,0 +1,333 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QDND_P_H
+#define QDND_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/qobject.h"
+#include "QtCore/qmap.h"
+#include "QtGui/qmime.h"
+#include "QtGui/qdrag.h"
+#include "QtGui/qpixmap.h"
+#include "QtCore/qpoint.h"
+#include "private/qobject_p.h"
+#ifdef Q_WS_MAC
+# include "private/qt_mac_p.h"
+#endif
+
+#if defined(Q_WS_WIN)
+# include <windows.h>
+# include <objidl.h>
+#endif
+
+QT_BEGIN_NAMESPACE
+
+class QEventLoop;
+
+#if !(defined(QT_NO_DRAGANDDROP) && defined(QT_NO_CLIPBOARD))
+
+class QInternalMimeData : public QMimeData
+{
+ Q_OBJECT
+public:
+ QInternalMimeData();
+ ~QInternalMimeData();
+
+ bool hasFormat(const QString &mimeType) const;
+ QStringList formats() const;
+ static bool canReadData(const QString &mimeType);
+
+
+ static QStringList formatsHelper(const QMimeData *data);
+ static bool hasFormatHelper(const QString &mimeType, const QMimeData *data);
+ static QByteArray renderDataHelper(const QString &mimeType, const QMimeData *data);
+
+protected:
+ QVariant retrieveData(const QString &mimeType, QVariant::Type type) const;
+
+ virtual bool hasFormat_sys(const QString &mimeType) const = 0;
+ virtual QStringList formats_sys() const = 0;
+ virtual QVariant retrieveData_sys(const QString &mimeType, QVariant::Type type) const = 0;
+};
+
+#ifdef Q_WS_WIN
+class QOleDataObject : public IDataObject
+{
+public:
+ explicit QOleDataObject(QMimeData *mimeData);
+ virtual ~QOleDataObject();
+
+ void releaseQt();
+ const QMimeData *mimeData() const;
+ DWORD reportedPerformedEffect() const;
+
+ // IUnknown methods
+ STDMETHOD(QueryInterface)(REFIID riid, void FAR* FAR* ppvObj);
+ STDMETHOD_(ULONG,AddRef)(void);
+ STDMETHOD_(ULONG,Release)(void);
+
+ // IDataObject methods
+ STDMETHOD(GetData)(LPFORMATETC pformatetcIn, LPSTGMEDIUM pmedium);
+ STDMETHOD(GetDataHere)(LPFORMATETC pformatetc, LPSTGMEDIUM pmedium);
+ STDMETHOD(QueryGetData)(LPFORMATETC pformatetc);
+ STDMETHOD(GetCanonicalFormatEtc)(LPFORMATETC pformatetc, LPFORMATETC pformatetcOut);
+ STDMETHOD(SetData)(LPFORMATETC pformatetc, STGMEDIUM FAR * pmedium,
+ BOOL fRelease);
+ STDMETHOD(EnumFormatEtc)(DWORD dwDirection, LPENUMFORMATETC FAR* ppenumFormatEtc);
+ STDMETHOD(DAdvise)(FORMATETC FAR* pFormatetc, DWORD advf,
+ LPADVISESINK pAdvSink, DWORD FAR* pdwConnection);
+ STDMETHOD(DUnadvise)(DWORD dwConnection);
+ STDMETHOD(EnumDAdvise)(LPENUMSTATDATA FAR* ppenumAdvise);
+
+private:
+ ULONG m_refs;
+ QPointer<QMimeData> data;
+ int CF_PERFORMEDDROPEFFECT;
+ DWORD performedEffect;
+};
+
+class QOleEnumFmtEtc : public IEnumFORMATETC
+{
+public:
+ explicit QOleEnumFmtEtc(const QVector<FORMATETC> &fmtetcs);
+ explicit QOleEnumFmtEtc(const QVector<LPFORMATETC> &lpfmtetcs);
+ virtual ~QOleEnumFmtEtc();
+
+ bool isNull() const;
+
+ // IUnknown methods
+ STDMETHOD(QueryInterface)(REFIID riid, void FAR* FAR* ppvObj);
+ STDMETHOD_(ULONG,AddRef)(void);
+ STDMETHOD_(ULONG,Release)(void);
+
+ // IEnumFORMATETC methods
+ STDMETHOD(Next)(ULONG celt, LPFORMATETC rgelt, ULONG FAR* pceltFetched);
+ STDMETHOD(Skip)(ULONG celt);
+ STDMETHOD(Reset)(void);
+ STDMETHOD(Clone)(LPENUMFORMATETC FAR* newEnum);
+
+private:
+ bool copyFormatEtc(LPFORMATETC dest, LPFORMATETC src) const;
+
+ ULONG m_dwRefs;
+ ULONG m_nIndex;
+ QVector<LPFORMATETC> m_lpfmtetcs;
+ bool m_isNull;
+};
+
+#endif
+
+#endif //QT_NO_DRAGANDDROP && QT_NO_CLIPBOARD
+
+#ifndef QT_NO_DRAGANDDROP
+
+class QDragPrivate : public QObjectPrivate
+{
+public:
+ QWidget *source;
+ QWidget *target;
+ QMimeData *data;
+ QPixmap pixmap;
+ QPoint hotspot;
+ Qt::DropActions possible_actions;
+ Qt::DropAction executed_action;
+ QMap<Qt::DropAction, QPixmap> customCursors;
+ Qt::DropAction defaultDropAction;
+};
+
+class QDropData : public QInternalMimeData
+{
+ Q_OBJECT
+public:
+ QDropData();
+ ~QDropData();
+
+protected:
+ bool hasFormat_sys(const QString &mimeType) const;
+ QStringList formats_sys() const;
+ QVariant retrieveData_sys(const QString &mimeType, QVariant::Type type) const;
+
+#if defined(Q_WS_WIN)
+public:
+ LPDATAOBJECT currentDataObject;
+#endif
+};
+
+class Q_GUI_EXPORT QDragManager: public QObject {
+ Q_OBJECT
+
+ QDragManager();
+ ~QDragManager();
+ // only friend classes can use QDragManager.
+ friend class QDrag;
+ friend class QDragMoveEvent;
+ friend class QDropEvent;
+ friend class QApplication;
+#ifdef Q_WS_MAC
+ friend class QWidgetPrivate; //dnd is implemented here
+#endif
+
+ bool eventFilter(QObject *, QEvent *);
+ void timerEvent(QTimerEvent*);
+
+public:
+ Qt::DropAction drag(QDrag *);
+
+ void cancel(bool deleteSource = true);
+ void move(const QPoint &);
+ void drop();
+ void updatePixmap();
+ QWidget *source() const { return object ? object->d_func()->source : 0; }
+ QDragPrivate *dragPrivate() const { return object ? object->d_func() : 0; }
+ static QDragPrivate *dragPrivate(QDrag *drag) { return drag ? drag->d_func() : 0; }
+
+ static QDragManager *self();
+ Qt::DropAction defaultAction(Qt::DropActions possibleActions,
+ Qt::KeyboardModifiers modifiers) const;
+
+ QDrag *object;
+
+ void updateCursor();
+
+ bool beingCancelled;
+ bool restoreCursor;
+ bool willDrop;
+ QEventLoop *eventLoop;
+
+ QPixmap dragCursor(Qt::DropAction action) const;
+
+ bool hasCustomDragCursors() const;
+
+ QDropData *dropData;
+
+ void emitActionChanged(Qt::DropAction newAction) { if (object) emit object->actionChanged(newAction); }
+
+ void setCurrentTarget(QWidget *target, bool dropped = false);
+ QWidget *currentTarget();
+
+#ifdef Q_WS_X11
+ QPixmap xdndMimeTransferedPixmap[2];
+ int xdndMimeTransferedPixmapIndex;
+#endif
+
+private:
+ QPixmap *pm_cursor;
+ int n_cursor;
+#ifdef Q_WS_QWS
+ Qt::DropAction currentActionForOverrideCursor;
+#endif
+
+ QWidget *currentDropTarget;
+
+ static QDragManager *instance;
+ Q_DISABLE_COPY(QDragManager)
+};
+
+
+#if defined(Q_WS_WIN)
+
+class QOleDropTarget : public IDropTarget
+{
+public:
+ QOleDropTarget(QWidget* w);
+ virtual ~QOleDropTarget() {}
+
+ void releaseQt();
+
+ // IUnknown methods
+ STDMETHOD(QueryInterface)(REFIID riid, void FAR* FAR* ppvObj);
+ STDMETHOD_(ULONG, AddRef)(void);
+ STDMETHOD_(ULONG, Release)(void);
+
+ // IDropTarget methods
+ STDMETHOD(DragEnter)(LPDATAOBJECT pDataObj, DWORD grfKeyState, POINTL pt, LPDWORD pdwEffect);
+ STDMETHOD(DragOver)(DWORD grfKeyState, POINTL pt, LPDWORD pdwEffect);
+ STDMETHOD(DragLeave)();
+ STDMETHOD(Drop)(LPDATAOBJECT pDataObj, DWORD grfKeyState, POINTL pt, LPDWORD pdwEffect);
+
+private:
+ ULONG m_refs;
+ QWidget* widget;
+ QPointer<QWidget> currentWidget;
+ QRect answerRect;
+ QPoint lastPoint;
+ DWORD chosenEffect;
+ DWORD lastKeyState;
+
+ void sendDragEnterEvent(QWidget *to, DWORD grfKeyState, POINTL pt, LPDWORD pdwEffect);
+};
+
+#endif
+
+#if defined (Q_WS_MAC)
+class QCocoaDropData : public QInternalMimeData
+{
+ Q_OBJECT
+public:
+ QCocoaDropData(CFStringRef pasteboard);
+ ~QCocoaDropData();
+
+protected:
+ bool hasFormat_sys(const QString &mimeType) const;
+ QStringList formats_sys() const;
+ QVariant retrieveData_sys(const QString &mimeType, QVariant::Type type) const;
+public:
+ CFStringRef dropPasteboard;
+};
+#endif
+
+#endif // !QT_NO_DRAGANDDROP
+
+
+QT_END_NAMESPACE
+
+#endif // QDND_P_H
diff --git a/src/gui/kernel/qdnd_qws.cpp b/src/gui/kernel/qdnd_qws.cpp
new file mode 100644
index 0000000000..98a60a1f5f
--- /dev/null
+++ b/src/gui/kernel/qdnd_qws.cpp
@@ -0,0 +1,422 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qapplication.h"
+
+#ifndef QT_NO_DRAGANDDROP
+
+#include "qwidget.h"
+#include "qdatetime.h"
+#include "qbitmap.h"
+#include "qcursor.h"
+#include "qevent.h"
+#include "qpainter.h"
+#include "qdnd_p.h"
+
+QT_BEGIN_NAMESPACE
+
+QT_USE_NAMESPACE
+
+static QPixmap *defaultPm = 0;
+static const int default_pm_hotx = -2;
+static const int default_pm_hoty = -16;
+static const char *const default_pm[] = {
+"13 9 3 1",
+". c None",
+" c #000000",
+"X c #FFFFFF",
+"X X X X X X X",
+" X X X X X X ",
+"X ......... X",
+" X.........X ",
+"X ......... X",
+" X.........X ",
+"X ......... X",
+" X X X X X X ",
+"X X X X X X X",
+};
+
+// Shift/Ctrl handling, and final drop status
+static Qt::DropAction global_accepted_action = Qt::CopyAction;
+static Qt::DropActions possible_actions = Qt::IgnoreAction;
+
+
+// static variables in place of a proper cross-process solution
+static QDrag *drag_object;
+static bool qt_qws_dnd_dragging = false;
+
+
+static Qt::KeyboardModifiers oldstate;
+
+class QShapedPixmapWidget : public QWidget {
+ QPixmap pixmap;
+public:
+ QShapedPixmapWidget() :
+ QWidget(0, Qt::Tool | Qt::FramelessWindowHint | Qt::X11BypassWindowManagerHint)
+ {
+ // ### Temporary workaround for 4.2-rc1!!! To prevent flickering when
+ // using drag'n drop in a client application. (task 126956)
+ // setAttribute() should be done unconditionally!
+ if (QApplication::type() == QApplication::GuiServer)
+ setAttribute(Qt::WA_TransparentForMouseEvents);
+ }
+
+ void setPixmap(QPixmap pm)
+ {
+ pixmap = pm;
+ if (!pixmap.mask().isNull()) {
+ setMask(pixmap.mask());
+ } else {
+ clearMask();
+ }
+ resize(pm.width(),pm.height());
+ }
+
+ void paintEvent(QPaintEvent*)
+ {
+ QPainter p(this);
+ p.drawPixmap(0,0,pixmap);
+ }
+};
+
+
+static QShapedPixmapWidget *qt_qws_dnd_deco = 0;
+
+
+void QDragManager::updatePixmap()
+{
+ if (qt_qws_dnd_deco) {
+ QPixmap pm;
+ QPoint pm_hot(default_pm_hotx,default_pm_hoty);
+ if (drag_object) {
+ pm = drag_object->pixmap();
+ if (!pm.isNull())
+ pm_hot = drag_object->hotSpot();
+ }
+ if (pm.isNull()) {
+ if (!defaultPm)
+ defaultPm = new QPixmap(default_pm);
+ pm = *defaultPm;
+ }
+ qt_qws_dnd_deco->setPixmap(pm);
+ qt_qws_dnd_deco->move(QCursor::pos()-pm_hot);
+ if (willDrop) {
+ qt_qws_dnd_deco->show();
+ } else {
+ qt_qws_dnd_deco->hide();
+ }
+ }
+}
+
+void QDragManager::timerEvent(QTimerEvent *) { }
+
+void QDragManager::move(const QPoint &) { }
+
+void QDragManager::updateCursor()
+{
+#ifndef QT_NO_CURSOR
+ if (willDrop) {
+ if (qt_qws_dnd_deco)
+ qt_qws_dnd_deco->show();
+ if (currentActionForOverrideCursor != global_accepted_action) {
+ QApplication::changeOverrideCursor(QCursor(dragCursor(global_accepted_action), 0, 0));
+ currentActionForOverrideCursor = global_accepted_action;
+ }
+ } else {
+ QCursor *overrideCursor = QApplication::overrideCursor();
+ if (!overrideCursor || overrideCursor->shape() != Qt::ForbiddenCursor) {
+ QApplication::changeOverrideCursor(QCursor(Qt::ForbiddenCursor));
+ currentActionForOverrideCursor = Qt::IgnoreAction;
+ }
+ if (qt_qws_dnd_deco)
+ qt_qws_dnd_deco->hide();
+ }
+#endif
+}
+
+
+bool QDragManager::eventFilter(QObject *o, QEvent *e)
+{
+ if (beingCancelled) {
+ if (e->type() == QEvent::KeyRelease && static_cast<QKeyEvent*>(e)->key() == Qt::Key_Escape) {
+ qApp->removeEventFilter(this);
+ Q_ASSERT(object == 0);
+ beingCancelled = false;
+ eventLoop->exit();
+ return true; // block the key release
+ }
+ return false;
+ }
+
+
+
+ if (!o->isWidgetType())
+ return false;
+
+ switch(e->type()) {
+
+ case QEvent::KeyPress:
+ case QEvent::KeyRelease:
+ {
+ QKeyEvent *ke = ((QKeyEvent*)e);
+ if (ke->key() == Qt::Key_Escape && e->type() == QEvent::KeyPress) {
+ cancel();
+ qApp->removeEventFilter(this);
+ beingCancelled = false;
+ eventLoop->exit();
+ } else {
+ updateCursor();
+ }
+ return true; // Eat all key events
+ }
+
+ case QEvent::MouseButtonPress:
+ case QEvent::MouseMove:
+ {
+ if (!object) { //#### this should not happen
+ qWarning("QDragManager::eventFilter: No object");
+ return true;
+ }
+
+ QDragManager *manager = QDragManager::self();
+ QMimeData *dropData = manager->object ? manager->dragPrivate()->data : manager->dropData;
+ if (manager->object)
+ possible_actions = manager->dragPrivate()->possible_actions;
+ else
+ possible_actions = Qt::IgnoreAction;
+
+ QMouseEvent *me = (QMouseEvent *)e;
+ if (me->buttons()) {
+ Qt::DropAction prevAction = global_accepted_action;
+ QWidget *cw = QApplication::widgetAt(me->globalPos());
+
+ // Fix for when we move mouse on to the deco widget
+ if (qt_qws_dnd_deco && cw == qt_qws_dnd_deco)
+ cw = object->target();
+
+ while (cw && !cw->acceptDrops() && !cw->isWindow())
+ cw = cw->parentWidget();
+
+ if (object->target() != cw) {
+ if (object->target()) {
+ QDragLeaveEvent dle;
+ QApplication::sendEvent(object->target(), &dle);
+ willDrop = false;
+ global_accepted_action = Qt::IgnoreAction;
+ updateCursor();
+ restoreCursor = true;
+ object->d_func()->target = 0;
+ }
+ if (cw && cw->acceptDrops()) {
+ object->d_func()->target = cw;
+ QDragEnterEvent dee(cw->mapFromGlobal(me->globalPos()), possible_actions, dropData,
+ me->buttons(), me->modifiers());
+ QApplication::sendEvent(object->target(), &dee);
+ willDrop = dee.isAccepted() && dee.dropAction() != Qt::IgnoreAction;
+ global_accepted_action = willDrop ? dee.dropAction() : Qt::IgnoreAction;
+ updateCursor();
+ restoreCursor = true;
+ }
+ } else if (cw) {
+ QDragMoveEvent dme(cw->mapFromGlobal(me->globalPos()), possible_actions, dropData,
+ me->buttons(), me->modifiers());
+ if (global_accepted_action != Qt::IgnoreAction) {
+ dme.setDropAction(global_accepted_action);
+ dme.accept();
+ }
+ QApplication::sendEvent(cw, &dme);
+ willDrop = dme.isAccepted();
+ global_accepted_action = willDrop ? dme.dropAction() : Qt::IgnoreAction;
+ updatePixmap();
+ updateCursor();
+ }
+ if (global_accepted_action != prevAction)
+ emitActionChanged(global_accepted_action);
+ }
+ return true; // Eat all mouse events
+ }
+
+ case QEvent::MouseButtonRelease:
+ {
+ qApp->removeEventFilter(this);
+ if (restoreCursor) {
+ willDrop = false;
+#ifndef QT_NO_CURSOR
+ QApplication::restoreOverrideCursor();
+#endif
+ restoreCursor = false;
+ }
+ if (object && object->target()) {
+ QMouseEvent *me = (QMouseEvent *)e;
+
+ QDragManager *manager = QDragManager::self();
+ QMimeData *dropData = manager->object ? manager->dragPrivate()->data : manager->dropData;
+
+ QDropEvent de(object->target()->mapFromGlobal(me->globalPos()), possible_actions, dropData,
+ me->buttons(), me->modifiers());
+ QApplication::sendEvent(object->target(), &de);
+ if (de.isAccepted())
+ global_accepted_action = de.dropAction();
+ else
+ global_accepted_action = Qt::IgnoreAction;
+
+ if (object)
+ object->deleteLater();
+ drag_object = object = 0;
+ }
+ eventLoop->exit();
+ return true; // Eat all mouse events
+ }
+
+ default:
+ break;
+ }
+
+ return false;
+}
+
+Qt::DropAction QDragManager::drag(QDrag *o)
+{
+ if (object == o || !o || !o->source())
+ return Qt::IgnoreAction;
+
+ if (object) {
+ cancel();
+ qApp->removeEventFilter(this);
+ beingCancelled = false;
+ }
+
+ object = drag_object = o;
+ qt_qws_dnd_deco = new QShapedPixmapWidget();
+ oldstate = Qt::NoModifier; // #### Should use state that caused the drag
+// drag_mode = mode;
+
+ willDrop = false;
+ updatePixmap();
+ updateCursor();
+ restoreCursor = true;
+ object->d_func()->target = 0;
+ qApp->installEventFilter(this);
+
+ global_accepted_action = Qt::CopyAction;
+#ifndef QT_NO_CURSOR
+ qApp->setOverrideCursor(Qt::ArrowCursor);
+ restoreCursor = true;
+ updateCursor();
+#endif
+
+ qt_qws_dnd_dragging = true;
+
+ eventLoop = new QEventLoop;
+ (void) eventLoop->exec();
+ delete eventLoop;
+ eventLoop = 0;
+
+ delete qt_qws_dnd_deco;
+ qt_qws_dnd_deco = 0;
+ qt_qws_dnd_dragging = false;
+
+
+ return global_accepted_action;
+}
+
+
+void QDragManager::cancel(bool deleteSource)
+{
+// qDebug("QDragManager::cancel");
+ beingCancelled = true;
+
+ if (object->target()) {
+ QDragLeaveEvent dle;
+ QApplication::sendEvent(object->target(), &dle);
+ }
+
+#ifndef QT_NO_CURSOR
+ if (restoreCursor) {
+ QApplication::restoreOverrideCursor();
+ restoreCursor = false;
+ }
+#endif
+
+ if (drag_object) {
+ if (deleteSource)
+ object->deleteLater();
+ drag_object = object = 0;
+ }
+
+ delete qt_qws_dnd_deco;
+ qt_qws_dnd_deco = 0;
+
+ global_accepted_action = Qt::IgnoreAction;
+}
+
+
+void QDragManager::drop()
+{
+}
+
+QVariant QDropData::retrieveData_sys(const QString &mimetype, QVariant::Type type) const
+{
+ if (!drag_object)
+ return QVariant();
+ QByteArray data = drag_object->mimeData()->data(mimetype);
+ if (type == QVariant::String)
+ return QString::fromUtf8(data);
+ return data;
+}
+
+bool QDropData::hasFormat_sys(const QString &format) const
+{
+ return formats().contains(format);
+}
+
+QStringList QDropData::formats_sys() const
+{
+ if (drag_object)
+ return drag_object->mimeData()->formats();
+ return QStringList();
+}
+
+
+#endif // QT_NO_DRAGANDDROP
+
+
+QT_END_NAMESPACE
diff --git a/src/gui/kernel/qdnd_win.cpp b/src/gui/kernel/qdnd_win.cpp
new file mode 100644
index 0000000000..8bf5c846a8
--- /dev/null
+++ b/src/gui/kernel/qdnd_win.cpp
@@ -0,0 +1,1036 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qapplication.h"
+
+#include "qapplication_p.h"
+#include "qevent.h"
+#include "qpainter.h"
+#include "qwidget.h"
+#include "qbuffer.h"
+#include "qdatastream.h"
+#include "qcursor.h"
+#include "qt_windows.h"
+#include <shlobj.h>
+#ifndef QT_NO_ACCESSIBILITY
+#include "qaccessible.h"
+#endif
+#include "qdnd_p.h"
+#include "qdebug.h"
+
+#if defined(Q_OS_WINCE)
+#include "qguifunctions_wince.h"
+#endif
+
+// support for xbuttons
+#ifndef MK_XBUTTON1
+#define MK_XBUTTON1 0x0020
+#define MK_XBUTTON2 0x0040
+#endif
+
+QT_BEGIN_NAMESPACE
+
+#if !(defined(QT_NO_DRAGANDDROP) && defined(QT_NO_CLIPBOARD))
+
+//---------------------------------------------------------------------
+// QOleDataObject Constructor
+//---------------------------------------------------------------------
+
+QOleDataObject::QOleDataObject(QMimeData *mimeData)
+{
+ m_refs = 1;
+ data = mimeData;
+ CF_PERFORMEDDROPEFFECT = RegisterClipboardFormat(CFSTR_PERFORMEDDROPEFFECT);
+ performedEffect = DROPEFFECT_NONE;
+}
+
+QOleDataObject::~QOleDataObject()
+{
+}
+
+void QOleDataObject::releaseQt()
+{
+ data = 0;
+}
+
+const QMimeData *QOleDataObject::mimeData() const
+{
+ return data;
+}
+
+DWORD QOleDataObject::reportedPerformedEffect() const
+{
+ return performedEffect;
+}
+
+//---------------------------------------------------------------------
+// IUnknown Methods
+//---------------------------------------------------------------------
+
+STDMETHODIMP
+QOleDataObject::QueryInterface(REFIID iid, void FAR* FAR* ppv)
+{
+ if (iid == IID_IUnknown || iid == IID_IDataObject) {
+ *ppv = this;
+ AddRef();
+ return NOERROR;
+ }
+ *ppv = NULL;
+ return ResultFromScode(E_NOINTERFACE);
+}
+
+STDMETHODIMP_(ULONG)
+QOleDataObject::AddRef(void)
+{
+ return ++m_refs;
+}
+
+STDMETHODIMP_(ULONG)
+QOleDataObject::Release(void)
+{
+ if (--m_refs == 0) {
+ releaseQt();
+ delete this;
+ return 0;
+ }
+ return m_refs;
+}
+
+//---------------------------------------------------------------------
+// IDataObject Methods
+//
+// The following methods are NOT supported for data transfer using the
+// clipboard or drag-drop:
+//
+// IDataObject::SetData -- return E_NOTIMPL
+// IDataObject::DAdvise -- return OLE_E_ADVISENOTSUPPORTED
+// ::DUnadvise
+// ::EnumDAdvise
+// IDataObject::GetCanonicalFormatEtc -- return E_NOTIMPL
+// (NOTE: must set pformatetcOut->ptd = NULL)
+//---------------------------------------------------------------------
+
+STDMETHODIMP
+QOleDataObject::GetData(LPFORMATETC pformatetc, LPSTGMEDIUM pmedium)
+{
+#ifdef QDND_DEBUG
+ qDebug("QOleDataObject::GetData(LPFORMATETC pformatetc, LPSTGMEDIUM pmedium)");
+#ifndef Q_OS_WINCE
+ char buf[256] = {0};
+ GetClipboardFormatNameA(pformatetc->cfFormat, buf, 255);
+ qDebug("CF = %d : %s", pformatetc->cfFormat, buf);
+#endif
+#endif
+
+ if (!data)
+ return ResultFromScode(DATA_E_FORMATETC);
+
+ QWindowsMime *converter = QWindowsMime::converterFromMime(*pformatetc, data);
+
+ if (converter && converter->convertFromMime(*pformatetc, data, pmedium))
+ return ResultFromScode(S_OK);
+ else
+ return ResultFromScode(DATA_E_FORMATETC);
+}
+
+STDMETHODIMP
+QOleDataObject::GetDataHere(LPFORMATETC, LPSTGMEDIUM)
+{
+ return ResultFromScode(DATA_E_FORMATETC);
+}
+
+STDMETHODIMP
+QOleDataObject::QueryGetData(LPFORMATETC pformatetc)
+{
+#ifdef QDND_DEBUG
+ qDebug("QOleDataObject::QueryGetData(LPFORMATETC pformatetc)");
+#endif
+
+ if (!data)
+ return ResultFromScode(DATA_E_FORMATETC);
+
+ if (QWindowsMime::converterFromMime(*pformatetc, data))
+ return ResultFromScode(S_OK);
+ return ResultFromScode(S_FALSE);
+}
+
+STDMETHODIMP
+QOleDataObject::GetCanonicalFormatEtc(LPFORMATETC, LPFORMATETC pformatetcOut)
+{
+ pformatetcOut->ptd = NULL;
+ return ResultFromScode(E_NOTIMPL);
+}
+
+STDMETHODIMP
+QOleDataObject::SetData(LPFORMATETC pFormatetc, STGMEDIUM *pMedium, BOOL fRelease)
+{
+ if (pFormatetc->cfFormat == CF_PERFORMEDDROPEFFECT && pMedium->tymed == TYMED_HGLOBAL) {
+ DWORD * val = (DWORD*)GlobalLock(pMedium->hGlobal);
+ performedEffect = *val;
+ GlobalUnlock(pMedium->hGlobal);
+ if (fRelease)
+ ReleaseStgMedium(pMedium);
+ return ResultFromScode(S_OK);
+ }
+ return ResultFromScode(E_NOTIMPL);
+}
+
+
+STDMETHODIMP
+QOleDataObject::EnumFormatEtc(DWORD dwDirection, LPENUMFORMATETC FAR* ppenumFormatEtc)
+{
+#ifdef QDND_DEBUG
+ qDebug("QOleDataObject::EnumFormatEtc(DWORD dwDirection, LPENUMFORMATETC FAR* ppenumFormatEtc)");
+#endif
+
+ if (!data)
+ return ResultFromScode(DATA_E_FORMATETC);
+
+ SCODE sc = S_OK;
+
+ QVector<FORMATETC> fmtetcs;
+ if (dwDirection == DATADIR_GET) {
+ fmtetcs = QWindowsMime::allFormatsForMime(data);
+ } else {
+ FORMATETC formatetc;
+ formatetc.cfFormat = CF_PERFORMEDDROPEFFECT;
+ formatetc.dwAspect = DVASPECT_CONTENT;
+ formatetc.lindex = -1;
+ formatetc.ptd = NULL;
+ formatetc.tymed = TYMED_HGLOBAL;
+ fmtetcs.append(formatetc);
+ }
+
+ QOleEnumFmtEtc *enumFmtEtc = new QOleEnumFmtEtc(fmtetcs);
+ *ppenumFormatEtc = enumFmtEtc;
+ if (enumFmtEtc->isNull()) {
+ delete enumFmtEtc;
+ *ppenumFormatEtc = NULL;
+ sc = E_OUTOFMEMORY;
+ }
+
+ return ResultFromScode(sc);
+}
+
+STDMETHODIMP
+QOleDataObject::DAdvise(FORMATETC FAR*, DWORD,
+ LPADVISESINK, DWORD FAR*)
+{
+ return ResultFromScode(OLE_E_ADVISENOTSUPPORTED);
+}
+
+
+STDMETHODIMP
+QOleDataObject::DUnadvise(DWORD)
+{
+ return ResultFromScode(OLE_E_ADVISENOTSUPPORTED);
+}
+
+STDMETHODIMP
+QOleDataObject::EnumDAdvise(LPENUMSTATDATA FAR*)
+{
+ return ResultFromScode(OLE_E_ADVISENOTSUPPORTED);
+}
+
+#endif // QT_NO_DRAGANDDROP && QT_NO_CLIPBOARD
+
+#ifndef QT_NO_DRAGANDDROP
+
+//#define QDND_DEBUG
+
+#ifdef QDND_DEBUG
+extern QString dragActionsToString(Qt::DropActions actions);
+#endif
+
+Qt::DropActions translateToQDragDropActions(DWORD pdwEffects)
+{
+ Qt::DropActions actions = Qt::IgnoreAction;
+ if (pdwEffects & DROPEFFECT_LINK)
+ actions |= Qt::LinkAction;
+ if (pdwEffects & DROPEFFECT_COPY)
+ actions |= Qt::CopyAction;
+ if (pdwEffects & DROPEFFECT_MOVE)
+ actions |= Qt::MoveAction;
+ return actions;
+}
+
+Qt::DropAction translateToQDragDropAction(DWORD pdwEffect)
+{
+ if (pdwEffect & DROPEFFECT_LINK)
+ return Qt::LinkAction;
+ if (pdwEffect & DROPEFFECT_COPY)
+ return Qt::CopyAction;
+ if (pdwEffect & DROPEFFECT_MOVE)
+ return Qt::MoveAction;
+ return Qt::IgnoreAction;
+}
+
+DWORD translateToWinDragEffects(Qt::DropActions action)
+{
+ DWORD effect = DROPEFFECT_NONE;
+ if (action & Qt::LinkAction)
+ effect |= DROPEFFECT_LINK;
+ if (action & Qt::CopyAction)
+ effect |= DROPEFFECT_COPY;
+ if (action & Qt::MoveAction)
+ effect |= DROPEFFECT_MOVE;
+ return effect;
+}
+
+Qt::KeyboardModifiers toQtKeyboardModifiers(DWORD keyState)
+{
+ Qt::KeyboardModifiers modifiers = Qt::NoModifier;
+
+ if (keyState & MK_SHIFT)
+ modifiers |= Qt::ShiftModifier;
+ if (keyState & MK_CONTROL)
+ modifiers |= Qt::ControlModifier;
+ if (keyState & MK_ALT)
+ modifiers |= Qt::AltModifier;
+
+ return modifiers;
+}
+
+Qt::MouseButtons toQtMouseButtons(DWORD keyState)
+{
+ Qt::MouseButtons buttons = Qt::NoButton;
+
+ if (keyState & MK_LBUTTON)
+ buttons |= Qt::LeftButton;
+ if (keyState & MK_RBUTTON)
+ buttons |= Qt::RightButton;
+ if (keyState & MK_MBUTTON)
+ buttons |= Qt::MidButton;
+
+ return buttons;
+}
+
+class QOleDropSource : public IDropSource
+{
+public:
+ QOleDropSource();
+ virtual ~QOleDropSource();
+
+ void createCursors();
+
+ // IUnknown methods
+ STDMETHOD(QueryInterface)(REFIID riid, void ** ppvObj);
+ STDMETHOD_(ULONG,AddRef)(void);
+ STDMETHOD_(ULONG,Release)(void);
+
+ // IDropSource methods
+ STDMETHOD(QueryContinueDrag)(BOOL fEscapePressed, DWORD grfKeyState);
+ STDMETHOD(GiveFeedback)(DWORD dwEffect);
+
+private:
+ Qt::MouseButtons currentButtons;
+ Qt::DropAction currentAction;
+ QMap <Qt::DropAction, QCursor> cursors;
+
+ ULONG m_refs;
+};
+
+
+QOleDropSource::QOleDropSource()
+{
+ currentButtons = Qt::NoButton;
+ m_refs = 1;
+ currentAction = Qt::IgnoreAction;
+}
+
+QOleDropSource::~QOleDropSource()
+{
+}
+
+void QOleDropSource::createCursors()
+{
+ QDragManager *manager = QDragManager::self();
+ if (manager && manager->object
+ && (!manager->object->pixmap().isNull()
+ || manager->hasCustomDragCursors())) {
+ QPixmap pm = manager->object->pixmap();
+ QList<Qt::DropAction> actions;
+ actions << Qt::MoveAction << Qt::CopyAction << Qt::LinkAction;
+ if (!manager->object->pixmap().isNull())
+ actions << Qt::IgnoreAction;
+ QPoint hotSpot = manager->object->hotSpot();
+ for (int cnum = 0; cnum < actions.size(); ++cnum) {
+ QPixmap cpm = manager->dragCursor(actions.at(cnum));
+ int w = cpm.width();
+ int h = cpm.height();
+
+ if (!pm.isNull()) {
+ int x1 = qMin(-hotSpot.x(),0);
+ int x2 = qMax(pm.width()-hotSpot.x(),cpm.width());
+ int y1 = qMin(-hotSpot.y(),0);
+ int y2 = qMax(pm.height()-hotSpot.y(),cpm.height());
+
+ w = x2-x1+1;
+ h = y2-y1+1;
+ }
+
+ QRect srcRect = pm.rect();
+ QPoint pmDest = QPoint(qMax(0, -hotSpot.x()), qMax(0, -hotSpot.y()));
+ QPoint newHotSpot = hotSpot;
+
+#if !defined(Q_OS_WINCE) || defined(GWES_ICONCURS)
+ bool limitedCursorSize = (QSysInfo::WindowsVersion & QSysInfo::WV_DOS_based)
+ || (QSysInfo::WindowsVersion == QSysInfo::WV_NT)
+ || (QSysInfo::WindowsVersion == QSysInfo::WV_CE);
+
+ if (limitedCursorSize) {
+ // Limited cursor size
+ int reqw = GetSystemMetrics(SM_CXCURSOR);
+ int reqh = GetSystemMetrics(SM_CYCURSOR);
+
+ QPoint hotspotInPM = newHotSpot - pmDest;
+ if (reqw < w) {
+ // Not wide enough - move objectpm right
+ qreal r = qreal(newHotSpot.x()) / w;
+ newHotSpot = QPoint(int(r * reqw), newHotSpot.y());
+ if (newHotSpot.x() + cpm.width() > reqw)
+ newHotSpot.setX(reqw - cpm.width());
+
+ srcRect = QRect(QPoint(hotspotInPM.x() - newHotSpot.x(), srcRect.top()), QSize(reqw, srcRect.height()));
+ }
+ if (reqh < h) {
+ qreal r = qreal(newHotSpot.y()) / h;
+ newHotSpot = QPoint(newHotSpot.x(), int(r * reqh));
+ if (newHotSpot.y() + cpm.height() > reqh)
+ newHotSpot.setY(reqh - cpm.height());
+
+ srcRect = QRect(QPoint(srcRect.left(), hotspotInPM.y() - newHotSpot.y()), QSize(srcRect.width(), reqh));
+ }
+ // Always use system cursor size
+ w = reqw;
+ h = reqh;
+ }
+#endif
+ QPixmap newCursor(w, h);
+ if (!pm.isNull()) {
+ newCursor.fill(QColor(0, 0, 0, 0));
+ QPainter p(&newCursor);
+ p.drawPixmap(pmDest, pm, srcRect);
+ p.drawPixmap(qMax(0,newHotSpot.x()),qMax(0,newHotSpot.y()),cpm);
+ } else {
+ newCursor = cpm;
+ }
+
+#ifndef QT_NO_CURSOR
+ cursors[actions.at(cnum)] = QCursor(newCursor, pm.isNull() ? 0 : qMax(0,newHotSpot.x()),
+ pm.isNull() ? 0 : qMax(0,newHotSpot.y()));
+#endif
+ }
+ }
+}
+
+
+
+//---------------------------------------------------------------------
+// IUnknown Methods
+//---------------------------------------------------------------------
+
+
+STDMETHODIMP
+QOleDropSource::QueryInterface(REFIID iid, void FAR* FAR* ppv)
+{
+ if(iid == IID_IUnknown || iid == IID_IDropSource)
+ {
+ *ppv = this;
+ ++m_refs;
+ return NOERROR;
+ }
+ *ppv = NULL;
+ return ResultFromScode(E_NOINTERFACE);
+}
+
+
+STDMETHODIMP_(ULONG)
+QOleDropSource::AddRef(void)
+{
+ return ++m_refs;
+}
+
+
+STDMETHODIMP_(ULONG)
+QOleDropSource::Release(void)
+{
+ if(--m_refs == 0)
+ {
+ delete this;
+ return 0;
+ }
+ return m_refs;
+}
+
+static inline Qt::MouseButtons keystate_to_mousebutton(DWORD grfKeyState)
+{
+ Qt::MouseButtons result;
+ if (grfKeyState & MK_LBUTTON)
+ result |= Qt::LeftButton;
+ if (grfKeyState & MK_MBUTTON)
+ result |= Qt::MidButton;
+ if (grfKeyState & MK_RBUTTON)
+ result |= Qt::RightButton;
+ if (grfKeyState & MK_XBUTTON1)
+ result |= Qt::XButton1;
+ if (grfKeyState & MK_XBUTTON2)
+ result |= Qt::XButton2;
+ return result;
+}
+
+//---------------------------------------------------------------------
+// IDropSource Methods
+//---------------------------------------------------------------------
+STDMETHODIMP
+QOleDropSource::QueryContinueDrag(BOOL fEscapePressed, DWORD grfKeyState)
+{
+#ifdef QDND_DEBUG
+ qDebug("QOleDropSource::QueryContinueDrag(fEscapePressed %d, grfKeyState %d)", fEscapePressed, grfKeyState);
+#endif
+
+ if (fEscapePressed) {
+ return ResultFromScode(DRAGDROP_S_CANCEL);
+ } else if (!(grfKeyState & (MK_LBUTTON|MK_MBUTTON|MK_RBUTTON))) {
+ return ResultFromScode(DRAGDROP_S_DROP);
+ } else {
+#if defined(Q_OS_WINCE)
+ // grfKeyState is broken on CE, therefore need to check
+ // the state manually
+ if ((GetAsyncKeyState(VK_LBUTTON) == 0) &&
+ (GetAsyncKeyState(VK_MBUTTON) == 0) &&
+ (GetAsyncKeyState(VK_RBUTTON) == 0)) {
+ return ResultFromScode(DRAGDROP_S_DROP);
+ }
+#else
+ if (currentButtons == Qt::NoButton) {
+ currentButtons = keystate_to_mousebutton(grfKeyState);
+ } else {
+ Qt::MouseButtons buttons = keystate_to_mousebutton(grfKeyState);
+ if (!(currentButtons & buttons))
+ return ResultFromScode(DRAGDROP_S_DROP);
+ }
+#endif
+ qApp->processEvents();
+ return NOERROR;
+ }
+}
+
+STDMETHODIMP
+QOleDropSource::GiveFeedback(DWORD dwEffect)
+{
+ Qt::DropAction action = translateToQDragDropAction(dwEffect);
+
+#ifdef QDND_DEBUG
+ qDebug("QOleDropSource::GiveFeedback(DWORD dwEffect)");
+ qDebug("dwEffect = %s", dragActionsToString(action).toLatin1().data());
+#endif
+
+ if (currentAction != action) {
+ currentAction = action;
+ QDragManager::self()->emitActionChanged(currentAction);
+ }
+
+ if (cursors.contains(currentAction)) {
+#ifndef QT_NO_CURSOR
+ SetCursor(cursors[currentAction].handle());
+#endif
+ return ResultFromScode(S_OK);
+ }
+
+ return ResultFromScode(DRAGDROP_S_USEDEFAULTCURSORS);
+}
+
+//---------------------------------------------------------------------
+// QOleDropTarget
+//---------------------------------------------------------------------
+
+QOleDropTarget::QOleDropTarget(QWidget* w)
+: widget(w)
+{
+ m_refs = 1;
+}
+
+void QOleDropTarget::releaseQt()
+{
+ widget = 0;
+}
+
+//---------------------------------------------------------------------
+// IUnknown Methods
+//---------------------------------------------------------------------
+
+
+STDMETHODIMP
+QOleDropTarget::QueryInterface(REFIID iid, void FAR* FAR* ppv)
+{
+ if(iid == IID_IUnknown || iid == IID_IDropTarget)
+ {
+ *ppv = this;
+ AddRef();
+ return NOERROR;
+ }
+ *ppv = NULL;
+ return ResultFromScode(E_NOINTERFACE);
+}
+
+
+STDMETHODIMP_(ULONG)
+QOleDropTarget::AddRef(void)
+{
+ return ++m_refs;
+}
+
+
+STDMETHODIMP_(ULONG)
+QOleDropTarget::Release(void)
+{
+ if(--m_refs == 0)
+ {
+ delete this;
+ return 0;
+ }
+ return m_refs;
+}
+
+//---------------------------------------------------------------------
+// IDropTarget Methods
+//---------------------------------------------------------------------
+
+STDMETHODIMP
+QOleDropTarget::DragEnter(LPDATAOBJECT pDataObj, DWORD grfKeyState, POINTL pt, LPDWORD pdwEffect)
+{
+#ifdef QDND_DEBUG
+ qDebug("QOleDropTarget::DragEnter(LPDATAOBJECT pDataObj, DWORD grfKeyState, POINTL pt, LPDWORD pdwEffect)");
+#endif
+
+ if (!QApplicationPrivate::tryModalHelper(widget)) {
+ *pdwEffect = DROPEFFECT_NONE;
+ return NOERROR;
+ }
+
+ QDragManager *manager = QDragManager::self();
+ manager->dropData->currentDataObject = pDataObj;
+ manager->dropData->currentDataObject->AddRef();
+ sendDragEnterEvent(widget, grfKeyState, pt, pdwEffect);
+ *pdwEffect = chosenEffect;
+
+ return NOERROR;
+}
+
+void QOleDropTarget::sendDragEnterEvent(QWidget *dragEnterWidget, DWORD grfKeyState,
+ POINTL pt, LPDWORD pdwEffect)
+{
+ Q_ASSERT(dragEnterWidget);
+ lastPoint = dragEnterWidget->mapFromGlobal(QPoint(pt.x,pt.y));
+ lastKeyState = grfKeyState;
+
+ chosenEffect = DROPEFFECT_NONE;
+ currentWidget = dragEnterWidget;
+
+ QDragManager *manager = QDragManager::self();
+ QMimeData * md = manager->source() ? manager->dragPrivate()->data : manager->dropData;
+ QDragEnterEvent enterEvent(lastPoint, translateToQDragDropActions(*pdwEffect), md,
+ toQtMouseButtons(grfKeyState), toQtKeyboardModifiers(grfKeyState));
+ QApplication::sendEvent(dragEnterWidget, &enterEvent);
+ answerRect = enterEvent.answerRect();
+
+ if (enterEvent.isAccepted()) {
+ chosenEffect = translateToWinDragEffects(enterEvent.dropAction());
+ }
+
+ // Documentation states that a drag move event is sendt immidiatly after
+ // a drag enter event. This will honor widgets overriding dragMoveEvent only:
+ if (enterEvent.isAccepted()) {
+ QDragMoveEvent moveEvent(lastPoint, translateToQDragDropActions(*pdwEffect), md,
+ toQtMouseButtons(grfKeyState), toQtKeyboardModifiers(grfKeyState));
+ answerRect = enterEvent.answerRect();
+ moveEvent.setDropAction(enterEvent.dropAction());
+ moveEvent.accept(); // accept by default, since enter event was accepted.
+
+ QApplication::sendEvent(dragEnterWidget, &moveEvent);
+ if (moveEvent.isAccepted()) {
+ answerRect = moveEvent.answerRect();
+ chosenEffect = translateToWinDragEffects(moveEvent.dropAction());
+ } else {
+ chosenEffect = DROPEFFECT_NONE;
+ }
+ }
+
+}
+
+STDMETHODIMP
+QOleDropTarget::DragOver(DWORD grfKeyState, POINTL pt, LPDWORD pdwEffect)
+{
+#ifdef QDND_DEBUG
+ qDebug("QOleDropTarget::DragOver(grfKeyState %d, pt (%d,%d), pdwEffect %d)", grfKeyState, pt.x, pt.y, pdwEffect);
+#endif
+
+ QWidget *dragOverWidget = widget->childAt(widget->mapFromGlobal(QPoint(pt.x, pt.y)));
+ if (!dragOverWidget)
+ dragOverWidget = widget;
+
+
+ if (!QApplicationPrivate::tryModalHelper(dragOverWidget)
+ || !dragOverWidget->testAttribute(Qt::WA_DropSiteRegistered)) {
+ *pdwEffect = DROPEFFECT_NONE;
+ return NOERROR;
+ }
+
+ QPoint tmpPoint = dragOverWidget->mapFromGlobal(QPoint(pt.x, pt.y));
+ // see if we should compress this event
+ if ((tmpPoint == lastPoint || answerRect.contains(tmpPoint)) && lastKeyState == grfKeyState) {
+ *pdwEffect = chosenEffect;
+ return NOERROR;
+ }
+
+ if (!dragOverWidget->internalWinId() && dragOverWidget != currentWidget) {
+ QPointer<QWidget> dragOverWidgetGuard(dragOverWidget);
+ // Send drag leave event to the previous drag widget.
+ QDragLeaveEvent dragLeave;
+ if (currentWidget)
+ QApplication::sendEvent(currentWidget, &dragLeave);
+ if (!dragOverWidgetGuard) {
+ dragOverWidget = widget->childAt(widget->mapFromGlobal(QPoint(pt.x, pt.y)));
+ if (!dragOverWidget)
+ dragOverWidget = widget;
+ }
+ // Send drag enter event to the current drag widget.
+ sendDragEnterEvent(dragOverWidget, grfKeyState, pt, pdwEffect);
+ }
+
+ QDragManager *manager = QDragManager::self();
+ QMimeData *md = manager->source() ? manager->dragPrivate()->data : manager->dropData;
+
+ QDragMoveEvent oldEvent(lastPoint, translateToQDragDropActions(*pdwEffect), md,
+ toQtMouseButtons(lastKeyState), toQtKeyboardModifiers(lastKeyState));
+
+
+ lastPoint = tmpPoint;
+ lastKeyState = grfKeyState;
+
+ QDragMoveEvent e(lastPoint, translateToQDragDropActions(*pdwEffect), md,
+ toQtMouseButtons(grfKeyState), toQtKeyboardModifiers(grfKeyState));
+ if (chosenEffect != DROPEFFECT_NONE) {
+ if (oldEvent.dropAction() == e.dropAction() &&
+ oldEvent.keyboardModifiers() == e.keyboardModifiers())
+ e.setDropAction(translateToQDragDropAction(chosenEffect));
+ e.accept();
+ }
+ QApplication::sendEvent(dragOverWidget, &e);
+
+ answerRect = e.answerRect();
+ if (e.isAccepted())
+ chosenEffect = translateToWinDragEffects(e.dropAction());
+ else
+ chosenEffect = DROPEFFECT_NONE;
+ *pdwEffect = chosenEffect;
+
+ return NOERROR;
+}
+
+STDMETHODIMP
+QOleDropTarget::DragLeave()
+{
+#ifdef QDND_DEBUG
+ qDebug("QOleDropTarget::DragLeave()");
+#endif
+
+ if (!QApplicationPrivate::tryModalHelper(widget)) {
+ return NOERROR;
+ }
+
+ currentWidget = 0;
+ QDragLeaveEvent e;
+ QApplication::sendEvent(widget, &e);
+
+ QDragManager *manager = QDragManager::self();
+
+ if (manager->dropData->currentDataObject) { // Sanity
+ manager->dropData->currentDataObject->Release();
+ manager->dropData->currentDataObject = 0;
+ }
+
+ return NOERROR;
+}
+
+#define KEY_STATE_BUTTON_MASK (MK_LBUTTON | MK_MBUTTON | MK_RBUTTON)
+
+STDMETHODIMP
+QOleDropTarget::Drop(LPDATAOBJECT /*pDataObj*/, DWORD grfKeyState, POINTL pt, LPDWORD pdwEffect)
+{
+#ifdef QDND_DEBUG
+ qDebug("QOleDropTarget::Drop(LPDATAOBJECT /*pDataObj*/, grfKeyState %d, POINTL pt, LPDWORD pdwEffect)", grfKeyState);
+#endif
+
+ QWidget *dropWidget = widget->childAt(widget->mapFromGlobal(QPoint(pt.x, pt.y)));
+ if (!dropWidget)
+ dropWidget = widget;
+
+ if (!QApplicationPrivate::tryModalHelper(dropWidget)
+ || !dropWidget->testAttribute(Qt::WA_DropSiteRegistered)) {
+ *pdwEffect = DROPEFFECT_NONE;
+ return NOERROR;
+ }
+
+ lastPoint = dropWidget->mapFromGlobal(QPoint(pt.x,pt.y));
+ // grfKeyState does not all ways contain button state in the drop so if
+ // it doesn't then use the last known button state;
+ if ((grfKeyState & KEY_STATE_BUTTON_MASK) == 0)
+ grfKeyState |= lastKeyState & KEY_STATE_BUTTON_MASK;
+ lastKeyState = grfKeyState;
+
+ QDragManager *manager = QDragManager::self();
+ QMimeData *md = manager->source() ? manager->dragPrivate()->data : manager->dropData;
+ QDropEvent e(lastPoint, translateToQDragDropActions(*pdwEffect), md,
+ toQtMouseButtons(grfKeyState), toQtKeyboardModifiers(grfKeyState));
+ if (chosenEffect != DROPEFFECT_NONE) {
+ e.setDropAction(translateToQDragDropAction(chosenEffect));
+ }
+ QApplication::sendEvent(dropWidget, &e);
+
+ if (chosenEffect != DROPEFFECT_NONE) {
+ e.accept();
+ }
+
+
+ if (e.isAccepted()) {
+ if (e.dropAction() == Qt::MoveAction || e.dropAction() == Qt::TargetMoveAction) {
+ if (e.dropAction() == Qt::MoveAction)
+ chosenEffect = DROPEFFECT_MOVE;
+ else
+ chosenEffect = DROPEFFECT_COPY;
+ HGLOBAL hData = GlobalAlloc(0, sizeof(DWORD));
+ if (hData) {
+ DWORD *moveEffect = (DWORD *)GlobalLock(hData);;
+ *moveEffect = DROPEFFECT_MOVE;
+ GlobalUnlock(hData);
+ STGMEDIUM medium;
+ memset(&medium, 0, sizeof(STGMEDIUM));
+ medium.tymed = TYMED_HGLOBAL;
+ medium.hGlobal = hData;
+ FORMATETC format;
+ format.cfFormat = RegisterClipboardFormat(CFSTR_PERFORMEDDROPEFFECT);
+ format.tymed = TYMED_HGLOBAL;
+ format.ptd = 0;
+ format.dwAspect = 1;
+ format.lindex = -1;
+ manager->dropData->currentDataObject->SetData(&format, &medium, true);
+ }
+ } else {
+ chosenEffect = translateToWinDragEffects(e.dropAction());
+ }
+ } else {
+ chosenEffect = DROPEFFECT_NONE;
+ }
+ *pdwEffect = chosenEffect;
+
+
+ if (manager->dropData->currentDataObject) {
+ manager->dropData->currentDataObject->Release();
+ manager->dropData->currentDataObject = 0;
+ }
+
+ return NOERROR;
+
+ // We won't get any mouserelease-event, so manually adjust qApp state:
+///### test this QApplication::winMouseButtonUp();
+}
+
+//---------------------------------------------------------------------
+// QDropData
+//---------------------------------------------------------------------
+
+bool QDropData::hasFormat_sys(const QString &mimeType) const
+{
+ if (!currentDataObject) // Sanity
+ return false;
+
+ return QWindowsMime::converterToMime(mimeType, currentDataObject) != 0;
+}
+
+QStringList QDropData::formats_sys() const
+{
+ QStringList fmts;
+ if (!currentDataObject) // Sanity
+ return fmts;
+
+ fmts = QWindowsMime::allMimesForFormats(currentDataObject);
+
+ return fmts;
+}
+
+QVariant QDropData::retrieveData_sys(const QString &mimeType, QVariant::Type type) const
+{
+ QVariant result;
+
+ if (!currentDataObject) // Sanity
+ return result;
+
+ QWindowsMime *converter = QWindowsMime::converterToMime(mimeType, currentDataObject);
+
+ if (converter)
+ result = converter->convertToMime(mimeType, currentDataObject, type);
+
+ return result;
+}
+
+Qt::DropAction QDragManager::drag(QDrag *o)
+
+{
+#ifdef QDND_DEBUG
+ qDebug("QDragManager::drag(QDrag *drag)");
+#endif
+
+ if (object == o || !o || !o->d_func()->source)
+ return Qt::IgnoreAction;
+
+ if (object) {
+ cancel();
+ qApp->removeEventFilter(this);
+ beingCancelled = false;
+ }
+
+ object = o;
+
+#ifdef QDND_DEBUG
+ qDebug("actions = %s", dragActionsToString(dragPrivate()->possible_actions).toLatin1().data());
+#endif
+
+ dragPrivate()->target = 0;
+
+#ifndef QT_NO_ACCESSIBILITY
+ QAccessible::updateAccessibility(this, 0, QAccessible::DragDropStart);
+#endif
+
+ DWORD resultEffect;
+ QOleDropSource *src = new QOleDropSource();
+ src->createCursors();
+ QOleDataObject *obj = new QOleDataObject(o->mimeData());
+ DWORD allowedEffects = translateToWinDragEffects(dragPrivate()->possible_actions);
+
+#if !defined(Q_OS_WINCE) || defined(GWES_ICONCURS)
+ HRESULT r = DoDragDrop(obj, src, allowedEffects, &resultEffect);
+#else
+ HRESULT r = DRAGDROP_S_CANCEL;
+ resultEffect = DROPEFFECT_MOVE;
+#endif
+
+ Qt::DropAction ret = Qt::IgnoreAction;
+ if (r == DRAGDROP_S_DROP) {
+ if (obj->reportedPerformedEffect() == DROPEFFECT_MOVE && resultEffect != DROPEFFECT_MOVE) {
+ ret = Qt::TargetMoveAction;
+ resultEffect = DROPEFFECT_MOVE;
+ } else {
+ ret = translateToQDragDropAction(resultEffect);
+ }
+ // Force it to be a copy if an unsupported operation occurred.
+ // This indicates a bug in the drop target.
+ if (resultEffect != DROPEFFECT_NONE && !(resultEffect & allowedEffects))
+ ret = Qt::CopyAction;
+ } else {
+ dragPrivate()->target = 0;
+ }
+
+ // clean up
+ obj->releaseQt();
+ obj->Release(); // Will delete obj if refcount becomes 0
+ src->Release(); // Will delete src if refcount becomes 0
+ object = 0;
+ o->setMimeData(0);
+ o->deleteLater();
+
+#ifndef QT_NO_ACCESSIBILITY
+ QAccessible::updateAccessibility(this, 0, QAccessible::DragDropEnd);
+#endif
+
+ return ret;
+}
+
+void QDragManager::cancel(bool /* deleteSource */)
+{
+ if (object) {
+ beingCancelled = true;
+ object = 0;
+ }
+
+#ifndef QT_NO_CURSOR
+ // insert cancel code here ######## todo
+
+ if (restoreCursor) {
+ QApplication::restoreOverrideCursor();
+ restoreCursor = false;
+ }
+#endif
+#ifndef QT_NO_ACCESSIBILITY
+ QAccessible::updateAccessibility(this, 0, QAccessible::DragDropEnd);
+#endif
+}
+
+void QDragManager::updatePixmap()
+{
+ // not used in windows implementation
+}
+
+bool QDragManager::eventFilter(QObject *, QEvent *)
+{
+ // not used in windows implementation
+ return false;
+}
+
+void QDragManager::timerEvent(QTimerEvent*)
+{
+ // not used in windows implementation
+}
+
+void QDragManager::move(const QPoint &)
+{
+ // not used in windows implementation
+}
+
+void QDragManager::drop()
+{
+ // not used in windows implementation
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_DRAGANDDROP
diff --git a/src/gui/kernel/qdnd_x11.cpp b/src/gui/kernel/qdnd_x11.cpp
new file mode 100644
index 0000000000..4c9c73c466
--- /dev/null
+++ b/src/gui/kernel/qdnd_x11.cpp
@@ -0,0 +1,2064 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qplatformdefs.h"
+
+#include "qapplication.h"
+
+#ifndef QT_NO_DRAGANDDROP
+
+#include "qwidget.h"
+#include "qpainter.h"
+#include "qpixmap.h"
+#include "qbitmap.h"
+#include "qdesktopwidget.h"
+#include "qevent.h"
+#include "qdatetime.h"
+#include "qiodevice.h"
+#include "qpointer.h"
+#include "qcursor.h"
+#include "qvariant.h"
+#include "qvector.h"
+#include "qurl.h"
+#include "qdebug.h"
+#include "qimagewriter.h"
+#include "qbuffer.h"
+#include "qtextcodec.h"
+
+#include "qdnd_p.h"
+#include "qt_x11_p.h"
+#include "qx11info_x11.h"
+
+#include "qwidget_p.h"
+#include "qcursor_p.h"
+
+QT_BEGIN_NAMESPACE
+
+// #define DND_DEBUG
+#ifdef DND_DEBUG
+#define DEBUG qDebug
+#else
+#define DEBUG if(0) qDebug
+#endif
+
+#ifdef DND_DEBUG
+#define DNDDEBUG qDebug()
+#else
+#define DNDDEBUG if(0) qDebug()
+#endif
+
+static int findXdndDropTransactionByWindow(Window window)
+{
+ int at = -1;
+ for (int i = 0; i < X11->dndDropTransactions.count(); ++i) {
+ const QXdndDropTransaction &t = X11->dndDropTransactions.at(i);
+ if (t.target == window || t.proxy_target == window) {
+ at = i;
+ break;
+ }
+ }
+ return at;
+}
+
+static int findXdndDropTransactionByTime(Time timestamp)
+{
+ int at = -1;
+ for (int i = 0; i < X11->dndDropTransactions.count(); ++i) {
+ const QXdndDropTransaction &t = X11->dndDropTransactions.at(i);
+ if (t.timestamp == timestamp) {
+ at = i;
+ break;
+ }
+ }
+ return at;
+}
+
+// timer used to discard old XdndDrop transactions
+static int transaction_expiry_timer = -1;
+enum { XdndDropTransactionTimeout = 5000 }; // 5 seconds
+
+static void restartXdndDropExpiryTimer()
+{
+ if (transaction_expiry_timer != -1)
+ QDragManager::self()->killTimer(transaction_expiry_timer);
+ transaction_expiry_timer = QDragManager::self()->startTimer(XdndDropTransactionTimeout);
+}
+
+
+// find an ancestor with XdndAware on it
+static Window findXdndAwareParent(Window window)
+{
+ Window target = 0;
+ forever {
+ // check if window has XdndAware
+ Atom type = 0;
+ int f;
+ unsigned long n, a;
+ unsigned char *data = 0;
+ if (XGetWindowProperty(X11->display, window, ATOM(XdndAware), 0, 0, False,
+ AnyPropertyType, &type, &f,&n,&a,&data) == Success) {
+ if (data)
+ XFree(data);
+ if (type) {
+ target = window;
+ break;
+ }
+ }
+
+ // try window's parent
+ Window root;
+ Window parent;
+ Window *children;
+ uint unused;
+ if (!XQueryTree(X11->display, window, &root, &parent, &children, &unused))
+ break;
+ if (children)
+ XFree(children);
+ if (window == root)
+ break;
+ window = parent;
+ }
+ return target;
+}
+
+
+
+
+// and all this stuff is copied -into- qapp_x11.cpp
+
+static void handle_xdnd_position(QWidget *, const XEvent *, bool);
+static void handle_xdnd_status(QWidget * w, const XEvent * xe, bool /*passive*/);
+
+const int xdnd_version = 5;
+
+static Qt::DropAction xdndaction_to_qtaction(Atom atom)
+{
+ if (atom == ATOM(XdndActionCopy) || atom == 0)
+ return Qt::CopyAction;
+ if (atom == ATOM(XdndActionLink))
+ return Qt::LinkAction;
+ if (atom == ATOM(XdndActionMove))
+ return Qt::MoveAction;
+ return Qt::CopyAction;
+}
+
+static int qtaction_to_xdndaction(Qt::DropAction a)
+{
+ switch (a) {
+ case Qt::CopyAction:
+ return ATOM(XdndActionCopy);
+ case Qt::LinkAction:
+ return ATOM(XdndActionLink);
+ case Qt::MoveAction:
+ case Qt::TargetMoveAction:
+ return ATOM(XdndActionMove);
+ case Qt::IgnoreAction:
+ return XNone;
+ default:
+ return ATOM(XdndActionCopy);
+ }
+}
+
+// clean up the stuff used.
+static void qt_xdnd_cleanup();
+
+static void qt_xdnd_send_leave();
+
+// real variables:
+// xid of current drag source
+static Atom qt_xdnd_dragsource_xid = 0;
+
+// the types in this drop. 100 is no good, but at least it's big.
+const int qt_xdnd_max_type = 100;
+static Atom qt_xdnd_types[qt_xdnd_max_type + 1];
+
+// timer used when target wants "continuous" move messages (eg. scroll)
+static int heartbeat = -1;
+// rectangle in which the answer will be the same
+static QRect qt_xdnd_source_sameanswer;
+// top-level window we sent position to last.
+static Window qt_xdnd_current_target;
+// window to send events to (always valid if qt_xdnd_current_target)
+static Window qt_xdnd_current_proxy_target;
+static Time qt_xdnd_source_current_time;
+
+// widget we forwarded position to last, and local position
+static QPointer<QWidget> qt_xdnd_current_widget;
+static QPoint qt_xdnd_current_position;
+// timestamp from the XdndPosition and XdndDrop
+static Time qt_xdnd_target_current_time;
+// screen number containing the pointer... -1 means default
+static int qt_xdnd_current_screen = -1;
+// state of dragging... true if dragging, false if not
+bool qt_xdnd_dragging = false;
+
+static bool waiting_for_status = false;
+
+// used to preset each new QDragMoveEvent
+static Qt::DropAction last_target_accepted_action = Qt::IgnoreAction;
+
+// Shift/Ctrl handling, and final drop status
+static Qt::DropAction global_accepted_action = Qt::CopyAction;
+static Qt::DropActions possible_actions = Qt::IgnoreAction;
+
+// for embedding only
+static QWidget* current_embedding_widget = 0;
+static XEvent last_enter_event;
+
+// cursors
+static QCursor *noDropCursor = 0;
+static QCursor *moveCursor = 0;
+static QCursor *copyCursor = 0;
+static QCursor *linkCursor = 0;
+
+static QPixmap *defaultPm = 0;
+
+static const int default_pm_hotx = -2;
+static const int default_pm_hoty = -16;
+static const char* const default_pm[] = {
+"13 9 3 1",
+". c None",
+" c #000000",
+"X c #FFFFFF",
+"X X X X X X X",
+" X X X X X X ",
+"X ......... X",
+" X.........X ",
+"X ......... X",
+" X.........X ",
+"X ......... X",
+" X X X X X X ",
+"X X X X X X X"
+};
+
+class QShapedPixmapWidget : public QWidget
+{
+ Q_OBJECT
+public:
+ QShapedPixmapWidget(QWidget* w) :
+ QWidget(w,
+ Qt::Tool | Qt::FramelessWindowHint
+ | Qt::X11BypassWindowManagerHint
+ | Qt::BypassGraphicsProxyWidget)
+ {
+ setAttribute(Qt::WA_X11NetWmWindowTypeDND);
+ }
+
+ void setPixmap(const QPixmap &pm)
+ {
+ QBitmap mask = pm.mask();
+ if (!mask.isNull()) {
+ setMask(mask);
+ } else {
+ clearMask();
+ }
+ resize(pm.width(),pm.height());
+ pixmap = pm;
+ update();
+ }
+ QPoint pm_hot;
+
+protected:
+ QPixmap pixmap;
+ void paintEvent(QPaintEvent*)
+ {
+ QPainter p(this);
+ p.drawPixmap(0, 0, pixmap);
+ }
+};
+
+#include "qdnd_x11.moc"
+
+struct XdndData {
+ QShapedPixmapWidget *deco;
+ QWidget* desktop_proxy;
+};
+
+static XdndData xdnd_data = { 0, 0 };
+
+class QExtraWidget : public QWidget
+{
+ Q_DECLARE_PRIVATE(QWidget)
+public:
+ inline QWExtra* extraData();
+ inline QTLWExtra* topData();
+};
+
+inline QWExtra* QExtraWidget::extraData() { return d_func()->extraData(); }
+inline QTLWExtra* QExtraWidget::topData() { return d_func()->topData(); }
+
+
+static WId xdndProxy(WId w)
+{
+ Atom type = XNone;
+ int f;
+ unsigned long n, a;
+ unsigned char *retval = 0;
+ XGetWindowProperty(X11->display, w, ATOM(XdndProxy), 0, 1, False,
+ XA_WINDOW, &type, &f,&n,&a,&retval);
+ WId *proxy_id_ptr = (WId *)retval;
+ WId proxy_id = 0;
+ if (type == XA_WINDOW && proxy_id_ptr) {
+ proxy_id = *proxy_id_ptr;
+ XFree(proxy_id_ptr);
+ proxy_id_ptr = 0;
+ // Already exists. Real?
+ X11->ignoreBadwindow();
+ XGetWindowProperty(X11->display, proxy_id, ATOM(XdndProxy), 0, 1, False,
+ XA_WINDOW, &type, &f,&n,&a,&retval);
+ proxy_id_ptr = (WId *)retval;
+ if (X11->badwindow() || type != XA_WINDOW || !proxy_id_ptr || *proxy_id_ptr != proxy_id)
+ // Bogus - we will overwrite.
+ proxy_id = 0;
+ }
+ if (proxy_id_ptr)
+ XFree(proxy_id_ptr);
+ return proxy_id;
+}
+
+static bool xdndEnable(QWidget* w, bool on)
+{
+ DNDDEBUG << "xdndEnable" << w << on;
+ if (on) {
+ QWidget * xdnd_widget = 0;
+ if ((w->windowType() == Qt::Desktop)) {
+ if (xdnd_data.desktop_proxy) // *WE* already have one.
+ return false;
+
+ // As per Xdnd4, use XdndProxy
+ XGrabServer(X11->display);
+ Q_ASSERT(w->testAttribute(Qt::WA_WState_Created));
+ WId proxy_id = xdndProxy(w->effectiveWinId());
+
+ if (!proxy_id) {
+ xdnd_widget = xdnd_data.desktop_proxy = new QWidget;
+ proxy_id = xdnd_data.desktop_proxy->effectiveWinId();
+ XChangeProperty (X11->display, w->effectiveWinId(), ATOM(XdndProxy),
+ XA_WINDOW, 32, PropModeReplace, (unsigned char *)&proxy_id, 1);
+ XChangeProperty (X11->display, proxy_id, ATOM(XdndProxy),
+ XA_WINDOW, 32, PropModeReplace, (unsigned char *)&proxy_id, 1);
+ }
+
+ XUngrabServer(X11->display);
+ } else {
+ xdnd_widget = w->window();
+ }
+ if (xdnd_widget) {
+ DNDDEBUG << "setting XdndAware for" << xdnd_widget << xdnd_widget->effectiveWinId();
+ Atom atm = (Atom)xdnd_version;
+ Q_ASSERT(xdnd_widget->testAttribute(Qt::WA_WState_Created));
+ XChangeProperty(X11->display, xdnd_widget->effectiveWinId(), ATOM(XdndAware),
+ XA_ATOM, 32, PropModeReplace, (unsigned char *)&atm, 1);
+ return true;
+ } else {
+ return false;
+ }
+ } else {
+ if ((w->windowType() == Qt::Desktop)) {
+ XDeleteProperty(X11->display, w->internalWinId(), ATOM(XdndProxy));
+ delete xdnd_data.desktop_proxy;
+ xdnd_data.desktop_proxy = 0;
+ } else {
+ DNDDEBUG << "not deleting XDndAware";
+ }
+ return true;
+ }
+}
+
+QByteArray QX11Data::xdndAtomToString(Atom a)
+{
+ if (!a) return 0;
+
+ if (a == XA_STRING || a == ATOM(UTF8_STRING)) {
+ return "text/plain"; // some Xdnd clients are dumb
+ }
+ char *atom = XGetAtomName(display, a);
+ QByteArray result = atom;
+ XFree(atom);
+ return result;
+}
+
+Atom QX11Data::xdndStringToAtom(const char *mimeType)
+{
+ if (!mimeType || !*mimeType)
+ return 0;
+ return XInternAtom(display, mimeType, False);
+}
+
+//$$$
+QString QX11Data::xdndMimeAtomToString(Atom a)
+{
+ QString atomName;
+ if (a) {
+ char *atom = XGetAtomName(display, a);
+ atomName = QString::fromLatin1(atom);
+ XFree(atom);
+ }
+ return atomName;
+}
+
+//$$$
+Atom QX11Data::xdndMimeStringToAtom(const QString &mimeType)
+{
+ if (mimeType.isEmpty())
+ return 0;
+ return XInternAtom(display, mimeType.toLatin1().constData(), False);
+}
+
+//$$$ replace ccxdndAtomToString()
+QStringList QX11Data::xdndMimeFormatsForAtom(Atom a)
+{
+ QStringList formats;
+ if (a) {
+ QString atomName = xdndMimeAtomToString(a);
+ formats.append(atomName);
+
+ // special cases for string type
+ if (a == ATOM(UTF8_STRING) || a == XA_STRING
+ || a == ATOM(TEXT) || a == ATOM(COMPOUND_TEXT))
+ formats.append(QLatin1String("text/plain"));
+
+ // special cases for uris
+ if (atomName == QLatin1String("text/x-moz-url"))
+ formats.append(QLatin1String("text/uri-list"));
+
+ // special case for images
+ if (a == XA_PIXMAP)
+ formats.append(QLatin1String("image/ppm"));
+ }
+ return formats;
+}
+
+//$$$
+bool QX11Data::xdndMimeDataForAtom(Atom a, QMimeData *mimeData, QByteArray *data, Atom *atomFormat, int *dataFormat)
+{
+ bool ret = false;
+ *atomFormat = a;
+ *dataFormat = 8;
+ QString atomName = xdndMimeAtomToString(a);
+ if (QInternalMimeData::hasFormatHelper(atomName, mimeData)) {
+ *data = QInternalMimeData::renderDataHelper(atomName, mimeData);
+ if (atomName == QLatin1String("application/x-color"))
+ *dataFormat = 16;
+ ret = true;
+ } else {
+ if ((a == ATOM(UTF8_STRING) || a == XA_STRING
+ || a == ATOM(TEXT) || a == ATOM(COMPOUND_TEXT))
+ && QInternalMimeData::hasFormatHelper(QLatin1String("text/plain"), mimeData)) {
+ if (a == ATOM(UTF8_STRING)){
+ *data = QInternalMimeData::renderDataHelper(QLatin1String("text/plain"), mimeData);
+ ret = true;
+ } else if (a == XA_STRING) {
+ *data = QString::fromUtf8(QInternalMimeData::renderDataHelper(
+ QLatin1String("text/plain"), mimeData)).toLocal8Bit();
+ ret = true;
+ } else if (a == ATOM(TEXT) || a == ATOM(COMPOUND_TEXT)) {
+ // the ICCCM states that TEXT and COMPOUND_TEXT are in the
+ // encoding of choice, so we choose the encoding of the locale
+ QByteArray strData = QString::fromUtf8(QInternalMimeData::renderDataHelper(
+ QLatin1String("text/plain"), mimeData)).toLocal8Bit();
+ char *list[] = { strData.data(), NULL };
+
+ XICCEncodingStyle style = (a == ATOM(COMPOUND_TEXT))
+ ? XCompoundTextStyle : XStdICCTextStyle;
+ XTextProperty textprop;
+ if (list[0] != NULL
+ && XmbTextListToTextProperty(X11->display, list, 1, style,
+ &textprop) == Success) {
+ *atomFormat = textprop.encoding;
+ *dataFormat = textprop.format;
+ *data = QByteArray((const char *) textprop.value, textprop.nitems * textprop.format / 8);
+
+ DEBUG(" textprop type %lx\n"
+ " textprop name '%s'\n"
+ " format %d\n"
+ " %ld items\n"
+ " %d bytes\n",
+ textprop.encoding,
+ X11->xdndMimeAtomToString(textprop.encoding).toLatin1().data(),
+ textprop.format, textprop.nitems, data->size());
+
+ XFree(textprop.value);
+ }
+ }
+ } else if (atomName == QLatin1String("text/x-moz-url") &&
+ QInternalMimeData::hasFormatHelper(QLatin1String("text/uri-list"), mimeData)) {
+ QByteArray uri = QInternalMimeData::renderDataHelper(
+ QLatin1String("text/uri-list"), mimeData).split('\n').first();
+ QString mozUri = QString::fromLatin1(uri, uri.size());
+ mozUri += QLatin1Char('\n');
+ *data = QByteArray(reinterpret_cast<const char *>(mozUri.utf16()), mozUri.length() * 2);
+ ret = true;
+ } else if ((a == XA_PIXMAP || a == XA_BITMAP) && mimeData->hasImage()) {
+ QPixmap pm = qvariant_cast<QPixmap>(mimeData->imageData());
+ if (a == XA_BITMAP && pm.depth() != 1) {
+ QImage img = pm.toImage();
+ img = img.convertToFormat(QImage::Format_MonoLSB);
+ pm = QPixmap::fromImage(img);
+ }
+ QDragManager *dm = QDragManager::self();
+ if (dm) {
+ Pixmap handle = pm.handle();
+ *data = QByteArray((const char *) &handle, sizeof(Pixmap));
+ dm->xdndMimeTransferedPixmap[dm->xdndMimeTransferedPixmapIndex] = pm;
+ dm->xdndMimeTransferedPixmapIndex =
+ (dm->xdndMimeTransferedPixmapIndex + 1) % 2;
+ }
+ }
+ }
+ return data;
+}
+
+//$$$
+QList<Atom> QX11Data::xdndMimeAtomsForFormat(const QString &format)
+{
+ QList<Atom> atoms;
+ atoms.append(xdndMimeStringToAtom(format));
+
+ // special cases for strings
+ if (format == QLatin1String("text/plain")) {
+ atoms.append(ATOM(UTF8_STRING));
+ atoms.append(XA_STRING);
+ atoms.append(ATOM(TEXT));
+ atoms.append(ATOM(COMPOUND_TEXT));
+ }
+
+ // special cases for uris
+ if (format == QLatin1String("text/uri-list")) {
+ atoms.append(xdndMimeStringToAtom(QLatin1String("text/x-moz-url")));
+ }
+
+ //special cases for images
+ if (format == QLatin1String("image/ppm"))
+ atoms.append(XA_PIXMAP);
+ if (format == QLatin1String("image/pbm"))
+ atoms.append(XA_BITMAP);
+
+ return atoms;
+}
+
+//$$$
+QVariant QX11Data::xdndMimeConvertToFormat(Atom a, const QByteArray &data, const QString &format, QVariant::Type requestedType, const QByteArray &encoding)
+{
+ QString atomName = xdndMimeAtomToString(a);
+ if (atomName == format)
+ return data;
+
+ if (!encoding.isEmpty()
+ && atomName == format + QLatin1String(";charset=") + QString::fromLatin1(encoding)) {
+
+ if (requestedType == QVariant::String) {
+ QTextCodec *codec = QTextCodec::codecForName(encoding);
+ if (codec)
+ return codec->toUnicode(data);
+ }
+
+ return data;
+ }
+
+ // special cases for string types
+ if (format == QLatin1String("text/plain")) {
+ if (a == ATOM(UTF8_STRING))
+ return QString::fromUtf8(data);
+ if (a == XA_STRING)
+ return QString::fromLatin1(data);
+ if (a == ATOM(TEXT) || a == ATOM(COMPOUND_TEXT))
+ // #### might be wrong for COMPUND_TEXT
+ return QString::fromLocal8Bit(data, data.size());
+ }
+
+ // special case for uri types
+ if (format == QLatin1String("text/uri-list")) {
+ if (atomName == QLatin1String("text/x-moz-url")) {
+ // we expect this as utf16 <url><space><title>
+ // the first part is a url that should only contain ascci char
+ // so it should be safe to check that the second char is 0
+ // to verify that it is utf16
+ if (data.size() > 1 && data.at(1) == 0)
+ return QString::fromUtf16(reinterpret_cast<const ushort *>(data.constData()),
+ data.size() / 2).split(QLatin1Char('\n')).first().toLatin1();
+ }
+ }
+
+ // special cas for images
+ if (format == QLatin1String("image/ppm")) {
+ if (a == XA_PIXMAP && data.size() == sizeof(Pixmap)) {
+ Pixmap xpm = *((Pixmap*)data.data());
+ Display *dpy = display;
+ Window r;
+ int x,y;
+ uint w,h,bw,d;
+ if (!xpm)
+ return QByteArray();
+ XGetGeometry(dpy,xpm, &r,&x,&y,&w,&h,&bw,&d);
+ QImageWriter imageWriter;
+ GC gc = XCreateGC(dpy, xpm, 0, 0);
+ QImage imageToWrite;
+ if (d == 1) {
+ QBitmap qbm(w,h);
+ XCopyArea(dpy,xpm,qbm.handle(),gc,0,0,w,h,0,0);
+ imageWriter.setFormat("PBMRAW");
+ imageToWrite = qbm.toImage();
+ } else {
+ QPixmap qpm(w,h);
+ XCopyArea(dpy,xpm,qpm.handle(),gc,0,0,w,h,0,0);
+ imageWriter.setFormat("PPMRAW");
+ imageToWrite = qpm.toImage();
+ }
+ XFreeGC(dpy,gc);
+ QBuffer buf;
+ buf.open(QIODevice::WriteOnly);
+ imageWriter.setDevice(&buf);
+ imageWriter.write(imageToWrite);
+ return buf.buffer();
+ }
+ }
+ return QVariant();
+}
+
+//$$$ middle of xdndObtainData
+Atom QX11Data::xdndMimeAtomForFormat(const QString &format, QVariant::Type requestedType, const QList<Atom> &atoms, QByteArray *encoding)
+{
+ encoding->clear();
+
+ // find matches for string types
+ if (format == QLatin1String("text/plain")) {
+ if (atoms.contains(ATOM(UTF8_STRING)))
+ return ATOM(UTF8_STRING);
+ if (atoms.contains(ATOM(COMPOUND_TEXT)))
+ return ATOM(COMPOUND_TEXT);
+ if (atoms.contains(ATOM(TEXT)))
+ return ATOM(TEXT);
+ if (atoms.contains(XA_STRING))
+ return XA_STRING;
+ }
+
+ // find matches for uri types
+ if (format == QLatin1String("text/uri-list")) {
+ Atom a = xdndMimeStringToAtom(format);
+ if (a && atoms.contains(a))
+ return a;
+ a = xdndMimeStringToAtom(QLatin1String("text/x-moz-url"));
+ if (a && atoms.contains(a))
+ return a;
+ }
+
+ // find match for image
+ if (format == QLatin1String("image/ppm")) {
+ if (atoms.contains(XA_PIXMAP))
+ return XA_PIXMAP;
+ }
+
+ // for string/text requests try to use a format with a well-defined charset
+ // first to avoid encoding problems
+ if (requestedType == QVariant::String
+ && format.startsWith(QLatin1String("text/"))
+ && !format.contains(QLatin1String("charset="))) {
+
+ QString formatWithCharset = format;
+ formatWithCharset.append(QLatin1String(";charset=utf-8"));
+
+ Atom a = xdndMimeStringToAtom(formatWithCharset);
+ if (a && atoms.contains(a)) {
+ *encoding = "utf-8";
+ return a;
+ }
+ }
+
+ Atom a = xdndMimeStringToAtom(format);
+ if (a && atoms.contains(a))
+ return a;
+
+ return 0;
+}
+
+void QX11Data::xdndSetup() {
+ QCursorData::initialize();
+ qAddPostRoutine(qt_xdnd_cleanup);
+}
+
+
+void qt_xdnd_cleanup()
+{
+ delete noDropCursor;
+ noDropCursor = 0;
+ delete copyCursor;
+ copyCursor = 0;
+ delete moveCursor;
+ moveCursor = 0;
+ delete linkCursor;
+ linkCursor = 0;
+ delete defaultPm;
+ defaultPm = 0;
+ delete xdnd_data.desktop_proxy;
+ xdnd_data.desktop_proxy = 0;
+ delete xdnd_data.deco;
+ xdnd_data.deco = 0;
+}
+
+
+static QWidget *find_child(QWidget *tlw, QPoint & p)
+{
+ QWidget *widget = tlw;
+
+ p = widget->mapFromGlobal(p);
+ bool done = false;
+ while (!done) {
+ done = true;
+ if (((QExtraWidget*)widget)->extraData() &&
+ ((QExtraWidget*)widget)->extraData()->xDndProxy != 0)
+ break; // stop searching for widgets under the mouse cursor if found widget is a proxy.
+ QObjectList children = widget->children();
+ if (!children.isEmpty()) {
+ for(int i = children.size(); i > 0;) {
+ --i;
+ QWidget *w = qobject_cast<QWidget *>(children.at(i));
+ if (!w)
+ continue;
+ if (w->testAttribute(Qt::WA_TransparentForMouseEvents))
+ continue;
+ if (w->isVisible() &&
+ w->geometry().contains(p) &&
+ !w->isWindow()) {
+ widget = w;
+ done = false;
+ p = widget->mapFromParent(p);
+ break;
+ }
+ }
+ }
+ }
+ return widget;
+}
+
+
+static bool checkEmbedded(QWidget* w, const XEvent* xe)
+{
+ if (!w)
+ return false;
+
+ if (current_embedding_widget != 0 && current_embedding_widget != w) {
+ qt_xdnd_current_target = ((QExtraWidget*)current_embedding_widget)->extraData()->xDndProxy;
+ qt_xdnd_current_proxy_target = qt_xdnd_current_target;
+ qt_xdnd_send_leave();
+ qt_xdnd_current_target = 0;
+ qt_xdnd_current_proxy_target = 0;
+ current_embedding_widget = 0;
+ }
+
+ QWExtra* extra = ((QExtraWidget*)w)->extraData();
+ if (extra && extra->xDndProxy != 0) {
+
+ if (current_embedding_widget != w) {
+
+ last_enter_event.xany.window = extra->xDndProxy;
+ XSendEvent(X11->display, extra->xDndProxy, False, NoEventMask, &last_enter_event);
+ current_embedding_widget = w;
+ }
+
+ ((XEvent*)xe)->xany.window = extra->xDndProxy;
+ XSendEvent(X11->display, extra->xDndProxy, False, NoEventMask, (XEvent*)xe);
+ if (qt_xdnd_current_widget != w) {
+ qt_xdnd_current_widget = w;
+ }
+ return true;
+ }
+ current_embedding_widget = 0;
+ return false;
+}
+
+void QX11Data::xdndHandleEnter(QWidget *, const XEvent * xe, bool /*passive*/)
+{
+ motifdnd_active = false;
+
+ last_enter_event.xclient = xe->xclient;
+
+ const long *l = xe->xclient.data.l;
+ int version = (int)(((unsigned long)(l[1])) >> 24);
+
+ if (version > xdnd_version)
+ return;
+
+ qt_xdnd_dragsource_xid = l[0];
+
+ int j = 0;
+ if (l[1] & 1) {
+ // get the types from XdndTypeList
+ Atom type = XNone;
+ int f;
+ unsigned long n, a;
+ unsigned char *retval;
+ XGetWindowProperty(X11->display, qt_xdnd_dragsource_xid, ATOM(XdndTypelist), 0,
+ qt_xdnd_max_type, False, XA_ATOM, &type, &f,&n,&a,&retval);
+ Atom *data = (Atom *)retval;
+ for (; j<qt_xdnd_max_type && j < (int)n; j++) {
+ qt_xdnd_types[j] = data[j];
+ }
+ if (data)
+ XFree((uchar*)data);
+ } else {
+ // get the types from the message
+ int i;
+ for(i=2; i < 5; i++) {
+ qt_xdnd_types[j++] = l[i];
+ }
+ }
+ qt_xdnd_types[j] = 0;
+}
+
+static void handle_xdnd_position(QWidget *w, const XEvent * xe, bool passive)
+{
+ const unsigned long *l = (const unsigned long *)xe->xclient.data.l;
+
+ QPoint p((l[2] & 0xffff0000) >> 16, l[2] & 0x0000ffff);
+ QWidget * c = find_child(w, p); // changes p to to c-local coordinates
+
+ if (!passive && checkEmbedded(c, xe))
+ return;
+
+ if (!c || (!c->acceptDrops() && (c->windowType() == Qt::Desktop)))
+ return;
+
+ if (l[0] != qt_xdnd_dragsource_xid) {
+ DEBUG("xdnd drag position from unexpected source (%08lx not %08lx)", l[0], qt_xdnd_dragsource_xid);
+ return;
+ }
+
+ // timestamp from the source
+ if (l[3] != 0) {
+ // Some X server/client combination swallow the first 32 bit and
+ // interpret a set bit 31 as negative sign.
+ qt_xdnd_target_current_time = X11->userTime =
+ ((sizeof(Time) == 8 && xe->xclient.data.l[3] < 0)
+ ? uint(l[3])
+ : l[3]);
+ }
+
+ QDragManager *manager = QDragManager::self();
+ QMimeData *dropData = manager->object ? manager->dragPrivate()->data : manager->dropData;
+
+ XClientMessageEvent response;
+ response.type = ClientMessage;
+ response.window = qt_xdnd_dragsource_xid;
+ response.format = 32;
+ response.message_type = ATOM(XdndStatus);
+ response.data.l[0] = w->effectiveWinId();
+ response.data.l[1] = 0; // flags
+ response.data.l[2] = 0; // x, y
+ response.data.l[3] = 0; // w, h
+ response.data.l[4] = 0; // action
+
+ if (!passive) { // otherwise just reject
+ while (c && !c->acceptDrops() && !c->isWindow()) {
+ p = c->mapToParent(p);
+ c = c->parentWidget();
+ }
+ QWidget *target_widget = c && c->acceptDrops() ? c : 0;
+
+ QRect answerRect(c->mapToGlobal(p), QSize(1,1));
+
+ if (manager->object) {
+ possible_actions = manager->dragPrivate()->possible_actions;
+ } else {
+ possible_actions = Qt::DropActions(xdndaction_to_qtaction(l[4]));
+// possible_actions |= Qt::CopyAction;
+ }
+ QDragMoveEvent me(p, possible_actions, dropData, QApplication::mouseButtons(), QApplication::keyboardModifiers());
+
+ Qt::DropAction accepted_action = Qt::IgnoreAction;
+
+
+ if (target_widget != qt_xdnd_current_widget) {
+ if (qt_xdnd_current_widget) {
+ QDragLeaveEvent e;
+ QApplication::sendEvent(qt_xdnd_current_widget, &e);
+ }
+ if (qt_xdnd_current_widget != target_widget) {
+ qt_xdnd_current_widget = target_widget;
+ }
+ if (target_widget) {
+ qt_xdnd_current_position = p;
+
+ last_target_accepted_action = Qt::IgnoreAction;
+ QDragEnterEvent de(p, possible_actions, dropData, QApplication::mouseButtons(), QApplication::keyboardModifiers());
+ QApplication::sendEvent(target_widget, &de);
+ if (de.isAccepted() && de.dropAction() != Qt::IgnoreAction)
+ last_target_accepted_action = de.dropAction();
+ }
+ }
+
+ DEBUG() << "qt_handle_xdnd_position action=" << X11->xdndAtomToString(l[4]);
+ if (!target_widget) {
+ answerRect = QRect(p, QSize(1, 1));
+ } else {
+ qt_xdnd_current_widget = c;
+ qt_xdnd_current_position = p;
+
+ if (last_target_accepted_action != Qt::IgnoreAction) {
+ me.setDropAction(last_target_accepted_action);
+ me.accept();
+ }
+ QApplication::sendEvent(c, &me);
+ if (me.isAccepted()) {
+ response.data.l[1] = 1; // yes
+ accepted_action = me.dropAction();
+ last_target_accepted_action = accepted_action;
+ } else {
+ response.data.l[0] = 0;
+ last_target_accepted_action = Qt::IgnoreAction;
+ }
+ answerRect = me.answerRect().intersected(c->rect());
+ }
+ answerRect = QRect(c->mapToGlobal(answerRect.topLeft()), answerRect.size());
+
+ if (answerRect.left() < 0)
+ answerRect.setLeft(0);
+ if (answerRect.right() > 4096)
+ answerRect.setRight(4096);
+ if (answerRect.top() < 0)
+ answerRect.setTop(0);
+ if (answerRect.bottom() > 4096)
+ answerRect.setBottom(4096);
+ if (answerRect.width() < 0)
+ answerRect.setWidth(0);
+ if (answerRect.height() < 0)
+ answerRect.setHeight(0);
+
+ response.data.l[2] = (answerRect.x() << 16) + answerRect.y();
+ response.data.l[3] = (answerRect.width() << 16) + answerRect.height();
+ response.data.l[4] = qtaction_to_xdndaction(accepted_action);
+ }
+
+ // reset
+ qt_xdnd_target_current_time = CurrentTime;
+
+ QWidget * source = QWidget::find(qt_xdnd_dragsource_xid);
+ if (source && (source->windowType() == Qt::Desktop) && !source->acceptDrops())
+ source = 0;
+
+ DEBUG() << "sending XdndStatus";
+ if (source)
+ handle_xdnd_status(source, (const XEvent *)&response, passive);
+ else
+ XSendEvent(X11->display, qt_xdnd_dragsource_xid, False, NoEventMask, (XEvent*)&response);
+}
+
+static Bool xdnd_position_scanner(Display *, XEvent *event, XPointer)
+{
+ if (event->type != ClientMessage)
+ return false;
+ XClientMessageEvent *ev = &event->xclient;
+
+ if (ev->message_type == ATOM(XdndPosition))
+ return true;
+
+ return false;
+}
+
+void QX11Data::xdndHandlePosition(QWidget * w, const XEvent * xe, bool passive)
+{
+ DEBUG("xdndHandlePosition");
+ while (XCheckIfEvent(X11->display, (XEvent *)xe, xdnd_position_scanner, 0))
+ ;
+
+ handle_xdnd_position(w, xe, passive);
+}
+
+
+static void handle_xdnd_status(QWidget *, const XEvent * xe, bool)
+{
+ const unsigned long *l = (const unsigned long *)xe->xclient.data.l;
+ // ignore late status messages
+ if (l[0] && l[0] != qt_xdnd_current_proxy_target)
+ return;
+ Qt::DropAction newAction = (l[1] & 0x1) ? xdndaction_to_qtaction(l[4]) : Qt::IgnoreAction;
+
+ if ((int)(l[1] & 2) == 0) {
+ QPoint p((l[2] & 0xffff0000) >> 16, l[2] & 0x0000ffff);
+ QSize s((l[3] & 0xffff0000) >> 16, l[3] & 0x0000ffff);
+ qt_xdnd_source_sameanswer = QRect(p, s);
+ } else {
+ qt_xdnd_source_sameanswer = QRect();
+ }
+ QDragManager *manager = QDragManager::self();
+ manager->willDrop = (l[1] & 0x1);
+ if (global_accepted_action != newAction)
+ manager->emitActionChanged(newAction);
+ global_accepted_action = newAction;
+ manager->updateCursor();
+ waiting_for_status = false;
+}
+
+static Bool xdnd_status_scanner(Display *, XEvent *event, XPointer)
+{
+ if (event->type != ClientMessage)
+ return false;
+ XClientMessageEvent *ev = &event->xclient;
+
+ if (ev->message_type == ATOM(XdndStatus))
+ return true;
+
+ return false;
+}
+
+void QX11Data::xdndHandleStatus(QWidget * w, const XEvent * xe, bool passive)
+{
+ DEBUG("xdndHandleStatus");
+ while (XCheckIfEvent(X11->display, (XEvent *)xe, xdnd_status_scanner, 0))
+ ;
+
+ handle_xdnd_status(w, xe, passive);
+ DEBUG("xdndHandleStatus end");
+}
+
+void QX11Data::xdndHandleLeave(QWidget *w, const XEvent * xe, bool /*passive*/)
+{
+ DEBUG("xdnd leave");
+ if (!qt_xdnd_current_widget ||
+ w->window() != qt_xdnd_current_widget->window()) {
+ return; // sanity
+ }
+
+ if (checkEmbedded(current_embedding_widget, xe)) {
+ current_embedding_widget = 0;
+ qt_xdnd_current_widget = 0;
+ return;
+ }
+
+ const unsigned long *l = (const unsigned long *)xe->xclient.data.l;
+
+ QDragLeaveEvent e;
+ QApplication::sendEvent(qt_xdnd_current_widget, &e);
+
+ if (l[0] != qt_xdnd_dragsource_xid) {
+ // This often happens - leave other-process window quickly
+ DEBUG("xdnd drag leave from unexpected source (%08lx not %08lx", l[0], qt_xdnd_dragsource_xid);
+ qt_xdnd_current_widget = 0;
+ return;
+ }
+
+ qt_xdnd_dragsource_xid = 0;
+ qt_xdnd_types[0] = 0;
+ qt_xdnd_current_widget = 0;
+}
+
+
+void qt_xdnd_send_leave()
+{
+ if (!qt_xdnd_current_target)
+ return;
+
+ XClientMessageEvent leave;
+ leave.type = ClientMessage;
+ leave.window = qt_xdnd_current_target;
+ leave.format = 32;
+ leave.message_type = ATOM(XdndLeave);
+ leave.data.l[0] = qt_xdnd_dragsource_xid;
+ leave.data.l[1] = 0; // flags
+ leave.data.l[2] = 0; // x, y
+ leave.data.l[3] = 0; // w, h
+ leave.data.l[4] = 0; // just null
+
+ QWidget * w = QWidget::find(qt_xdnd_current_proxy_target);
+
+ if (w && (w->windowType() == Qt::Desktop) && !w->acceptDrops())
+ w = 0;
+
+ if (w)
+ X11->xdndHandleLeave(w, (const XEvent *)&leave, false);
+ else
+ XSendEvent(X11->display, qt_xdnd_current_proxy_target, False,
+ NoEventMask, (XEvent*)&leave);
+ // reset the drag manager state
+ QDragManager *manager = QDragManager::self();
+ manager->willDrop = false;
+ if (global_accepted_action != Qt::IgnoreAction)
+ manager->emitActionChanged(Qt::IgnoreAction);
+ global_accepted_action = Qt::IgnoreAction;
+ manager->updateCursor();
+ qt_xdnd_current_target = 0;
+ qt_xdnd_current_proxy_target = 0;
+ qt_xdnd_source_current_time = 0;
+ waiting_for_status = false;
+}
+
+
+
+void QX11Data::xdndHandleDrop(QWidget *, const XEvent * xe, bool passive)
+{
+ DEBUG("xdndHandleDrop");
+ if (!qt_xdnd_current_widget) {
+ qt_xdnd_dragsource_xid = 0;
+ return; // sanity
+ }
+
+ if (!passive && checkEmbedded(qt_xdnd_current_widget, xe)){
+ current_embedding_widget = 0;
+ qt_xdnd_dragsource_xid = 0;
+ qt_xdnd_current_widget = 0;
+ return;
+ }
+ const unsigned long *l = (const unsigned long *)xe->xclient.data.l;
+
+ QDragManager *manager = QDragManager::self();
+ DEBUG("xdnd drop");
+
+ if (l[0] != qt_xdnd_dragsource_xid) {
+ DEBUG("xdnd drop from unexpected source (%08lx not %08lx", l[0], qt_xdnd_dragsource_xid);
+ return;
+ }
+
+ // update the "user time" from the timestamp in the event.
+ if (l[2] != 0) {
+ // Some X server/client combination swallow the first 32 bit and
+ // interpret a set bit 31 as negative sign.
+ qt_xdnd_target_current_time = X11->userTime =
+ ((sizeof(Time) == 8 && xe->xclient.data.l[2] < 0)
+ ? uint(l[2])
+ : l[2]);
+ }
+
+ if (!passive) {
+ // this could be a same-application drop, just proxied due to
+ // some XEMBEDding, so try to find the real QMimeData used
+ // based on the timestamp for this drop.
+ QMimeData *dropData = 0;
+ int at = findXdndDropTransactionByTime(qt_xdnd_target_current_time);
+ if (at != -1)
+ dropData = QDragManager::dragPrivate(X11->dndDropTransactions.at(at).object)->data;
+ // if we can't find it, then use the data in the drag manager
+ if (!dropData)
+ dropData = (manager->object) ? manager->dragPrivate()->data : manager->dropData;
+
+ QDropEvent de(qt_xdnd_current_position, possible_actions, dropData,
+ QApplication::mouseButtons(), QApplication::keyboardModifiers());
+ QApplication::sendEvent(qt_xdnd_current_widget, &de);
+ if (!de.isAccepted()) {
+ // Ignore a failed drag
+ global_accepted_action = Qt::IgnoreAction;
+ } else {
+ global_accepted_action = de.dropAction();
+ }
+ XClientMessageEvent finished;
+ finished.type = ClientMessage;
+ finished.window = qt_xdnd_dragsource_xid;
+ finished.format = 32;
+ finished.message_type = ATOM(XdndFinished);
+ DNDDEBUG << "xdndHandleDrop"
+ << "qt_xdnd_current_widget" << qt_xdnd_current_widget
+ << (qt_xdnd_current_widget ? qt_xdnd_current_widget->effectiveWinId() : 0)
+ << "t_xdnd_current_widget->window()"
+ << (qt_xdnd_current_widget ? qt_xdnd_current_widget->window() : 0)
+ << (qt_xdnd_current_widget ? qt_xdnd_current_widget->window()->internalWinId() : 0);
+ finished.data.l[0] = qt_xdnd_current_widget?qt_xdnd_current_widget->window()->internalWinId():0;
+ finished.data.l[1] = de.isAccepted() ? 1 : 0; // flags
+ finished.data.l[2] = qtaction_to_xdndaction(global_accepted_action);
+ XSendEvent(X11->display, qt_xdnd_dragsource_xid, False,
+ NoEventMask, (XEvent*)&finished);
+ } else {
+ QDragLeaveEvent e;
+ QApplication::sendEvent(qt_xdnd_current_widget, &e);
+ }
+ qt_xdnd_dragsource_xid = 0;
+ qt_xdnd_current_widget = 0;
+ waiting_for_status = false;
+
+ // reset
+ qt_xdnd_target_current_time = CurrentTime;
+}
+
+
+void QX11Data::xdndHandleFinished(QWidget *, const XEvent * xe, bool passive)
+{
+ DEBUG("xdndHandleFinished");
+ const unsigned long *l = (const unsigned long *)xe->xclient.data.l;
+
+ DNDDEBUG << "xdndHandleFinished, l[0]" << l[0]
+ << "qt_xdnd_current_target" << qt_xdnd_current_target
+ << "qt_xdnd_current_proxy_targe" << qt_xdnd_current_proxy_target;
+
+ if (l[0]) {
+ int at = findXdndDropTransactionByWindow(l[0]);
+ if (at != -1) {
+ restartXdndDropExpiryTimer();
+
+ QXdndDropTransaction t = X11->dndDropTransactions.takeAt(at);
+ QDragManager *manager = QDragManager::self();
+
+ Window target = qt_xdnd_current_target;
+ Window proxy_target = qt_xdnd_current_proxy_target;
+ QWidget *embedding_widget = current_embedding_widget;
+ QDrag *currentObject = manager->object;
+
+ qt_xdnd_current_target = t.target;
+ qt_xdnd_current_proxy_target = t.proxy_target;
+ current_embedding_widget = t.embedding_widget;
+ manager->object = t.object;
+
+ if (!passive)
+ (void) checkEmbedded(qt_xdnd_current_widget, xe);
+
+ current_embedding_widget = 0;
+ qt_xdnd_current_target = 0;
+ qt_xdnd_current_proxy_target = 0;
+
+ if (t.object)
+ t.object->deleteLater();
+
+ qt_xdnd_current_target = target;
+ qt_xdnd_current_proxy_target = proxy_target;
+ current_embedding_widget = embedding_widget;
+ manager->object = currentObject;
+ }
+ }
+ waiting_for_status = false;
+}
+
+
+void QDragManager::timerEvent(QTimerEvent* e)
+{
+ if (e->timerId() == heartbeat && qt_xdnd_source_sameanswer.isNull()) {
+ move(QCursor::pos());
+ } else if (e->timerId() == transaction_expiry_timer) {
+ for (int i = 0; i < X11->dndDropTransactions.count(); ++i) {
+ const QXdndDropTransaction &t = X11->dndDropTransactions.at(i);
+ if (t.targetWidget) {
+ // dnd within the same process, don't delete these
+ continue;
+ }
+ t.object->deleteLater();
+ X11->dndDropTransactions.removeAt(i--);
+ }
+
+ killTimer(transaction_expiry_timer);
+ transaction_expiry_timer = -1;
+ }
+}
+
+bool QDragManager::eventFilter(QObject * o, QEvent * e)
+{
+ if (beingCancelled) {
+ if (e->type() == QEvent::KeyRelease && ((QKeyEvent*)e)->key() == Qt::Key_Escape) {
+ qApp->removeEventFilter(this);
+ Q_ASSERT(object == 0);
+ beingCancelled = false;
+ eventLoop->exit();
+ return true; // block the key release
+ }
+ return false;
+ }
+
+ Q_ASSERT(object != 0);
+
+ if (!o->isWidgetType())
+ return false;
+
+ if (e->type() == QEvent::MouseMove) {
+ QMouseEvent* me = (QMouseEvent *)e;
+ move(me->globalPos());
+ return true;
+ } else if (e->type() == QEvent::MouseButtonRelease) {
+ DEBUG("pre drop");
+ qApp->removeEventFilter(this);
+ if (willDrop)
+ drop();
+ else
+ cancel();
+ DEBUG("drop, resetting object");
+ beingCancelled = false;
+ eventLoop->exit();
+ return true;
+ }
+
+ if (e->type() == QEvent::KeyPress || e->type() == QEvent::KeyRelease) {
+ QKeyEvent *ke = ((QKeyEvent*)e);
+ if (ke->key() == Qt::Key_Escape && e->type() == QEvent::KeyPress) {
+ cancel();
+ qApp->removeEventFilter(this);
+ beingCancelled = false;
+ eventLoop->exit();
+ } else {
+ qt_xdnd_source_sameanswer = QRect(); // force move
+ move(QCursor::pos());
+ }
+ return true; // Eat all key events
+ }
+
+ // ### We bind modality to widgets, so we have to do this
+ // ### "manually".
+ // DnD is modal - eat all other interactive events
+ switch (e->type()) {
+ case QEvent::MouseButtonPress:
+ case QEvent::MouseButtonRelease:
+ case QEvent::MouseButtonDblClick:
+ case QEvent::MouseMove:
+ case QEvent::KeyPress:
+ case QEvent::KeyRelease:
+ case QEvent::Wheel:
+ case QEvent::ShortcutOverride:
+#ifdef QT3_SUPPORT
+ case QEvent::Accel:
+ case QEvent::AccelAvailable:
+#endif
+ return true;
+ default:
+ return false;
+ }
+}
+
+void QDragManager::updateCursor()
+{
+ if (!noDropCursor) {
+#ifndef QT_NO_CURSOR
+ noDropCursor = new QCursor(Qt::ForbiddenCursor);
+ moveCursor = new QCursor(dragCursor(Qt::MoveAction), 0,0);
+ copyCursor = new QCursor(dragCursor(Qt::CopyAction), 0,0);
+ linkCursor = new QCursor(dragCursor(Qt::LinkAction), 0,0);
+#endif
+ }
+
+ QCursor *c;
+ if (willDrop) {
+ if (global_accepted_action == Qt::CopyAction) {
+ c = copyCursor;
+ } else if (global_accepted_action == Qt::LinkAction) {
+ c = linkCursor;
+ } else {
+ c = moveCursor;
+ }
+ if (xdnd_data.deco) {
+ xdnd_data.deco->show();
+ xdnd_data.deco->raise();
+ }
+ } else {
+ c = noDropCursor;
+ //if (qt_xdnd_deco)
+ // qt_xdnd_deco->hide();
+ }
+#ifndef QT_NO_CURSOR
+ if (c)
+ qApp->changeOverrideCursor(*c);
+#endif
+}
+
+
+void QDragManager::cancel(bool deleteSource)
+{
+ DEBUG("QDragManager::cancel");
+ Q_ASSERT(heartbeat != -1);
+ killTimer(heartbeat);
+ heartbeat = -1;
+ beingCancelled = true;
+ qt_xdnd_dragging = false;
+
+ if (qt_xdnd_current_target)
+ qt_xdnd_send_leave();
+
+#ifndef QT_NO_CURSOR
+ if (restoreCursor) {
+ QApplication::restoreOverrideCursor();
+ restoreCursor = false;
+ }
+#endif
+
+ if (deleteSource && object)
+ object->deleteLater();
+ object = 0;
+ qDeleteInEventHandler(xdnd_data.deco);
+ xdnd_data.deco = 0;
+
+ global_accepted_action = Qt::IgnoreAction;
+}
+
+static
+Window findRealWindow(const QPoint & pos, Window w, int md)
+{
+ if (xdnd_data.deco && w == xdnd_data.deco->effectiveWinId())
+ return 0;
+
+ if (md) {
+ X11->ignoreBadwindow();
+ XWindowAttributes attr;
+ XGetWindowAttributes(X11->display, w, &attr);
+ if (X11->badwindow())
+ return 0;
+
+ if (attr.map_state == IsViewable
+ && QRect(attr.x,attr.y,attr.width,attr.height).contains(pos)) {
+ {
+ Atom type = XNone;
+ int f;
+ unsigned long n, a;
+ unsigned char *data;
+
+ XGetWindowProperty(X11->display, w, ATOM(XdndAware), 0, 0, False,
+ AnyPropertyType, &type, &f,&n,&a,&data);
+ if (data) XFree(data);
+ if (type)
+ return w;
+ }
+
+ Window r, p;
+ Window* c;
+ uint nc;
+ if (XQueryTree(X11->display, w, &r, &p, &c, &nc)) {
+ r=0;
+ for (uint i=nc; !r && i--;) {
+ r = findRealWindow(pos-QPoint(attr.x,attr.y),
+ c[i], md-1);
+ }
+ XFree(c);
+ if (r)
+ return r;
+
+ // We didn't find a client window! Just use the
+ // innermost window.
+ }
+
+ // No children!
+ return w;
+ }
+ }
+ return 0;
+}
+
+void QDragManager::move(const QPoint & globalPos)
+{
+#ifdef QT_NO_CURSOR
+ Q_UNUSED(globalPos);
+ return;
+#else
+ DEBUG() << "QDragManager::move enter";
+ if (!object) {
+ // perhaps the target crashed?
+ return;
+ }
+
+ int screen = QCursor::x11Screen();
+ if ((qt_xdnd_current_screen == -1 && screen != X11->defaultScreen) || (screen != qt_xdnd_current_screen)) {
+ // recreate the pixmap on the new screen...
+ delete xdnd_data.deco;
+ QWidget* parent = object->source()->window()->x11Info().screen() == screen
+ ? object->source()->window() : QApplication::desktop()->screen(screen);
+ xdnd_data.deco = new QShapedPixmapWidget(parent);
+ if (!QWidget::mouseGrabber()) {
+ updatePixmap();
+ xdnd_data.deco->grabMouse();
+ }
+ }
+ xdnd_data.deco->move(QCursor::pos() - xdnd_data.deco->pm_hot);
+
+ if (qt_xdnd_source_sameanswer.contains(globalPos) && qt_xdnd_source_sameanswer.isValid())
+ return;
+
+ qt_xdnd_current_screen = screen;
+ Window rootwin = QX11Info::appRootWindow(qt_xdnd_current_screen);
+ Window target = 0;
+ int lx = 0, ly = 0;
+ if (!XTranslateCoordinates(X11->display, rootwin, rootwin, globalPos.x(), globalPos.y(), &lx, &ly, &target))
+ // some weird error...
+ return;
+
+ if (target == rootwin) {
+ // Ok.
+ } else if (target) {
+ //me
+ Window src = rootwin;
+ while (target != 0) {
+ DNDDEBUG << "checking target for XdndAware" << QWidget::find(target) << target;
+ int lx2, ly2;
+ Window t;
+ // translate coordinates
+ if (!XTranslateCoordinates(X11->display, src, target, lx, ly, &lx2, &ly2, &t)) {
+ target = 0;
+ break;
+ }
+ lx = lx2;
+ ly = ly2;
+ src = target;
+
+ // check if it has XdndAware
+ Atom type = 0;
+ int f;
+ unsigned long n, a;
+ unsigned char *data = 0;
+ XGetWindowProperty(X11->display, target, ATOM(XdndAware), 0, 0, False,
+ AnyPropertyType, &type, &f,&n,&a,&data);
+ if (data)
+ XFree(data);
+ if (type) {
+ DNDDEBUG << "Found XdndAware on " << QWidget::find(target) << target;
+ break;
+ }
+
+ // find child at the coordinates
+ if (!XTranslateCoordinates(X11->display, src, src, lx, ly, &lx2, &ly2, &target)) {
+ target = 0;
+ break;
+ }
+ }
+ if (xdnd_data.deco && (!target || target == xdnd_data.deco->effectiveWinId())) {
+ DNDDEBUG << "need to find real window";
+ target = findRealWindow(globalPos, rootwin, 6);
+ DNDDEBUG << "real window found" << QWidget::find(target) << target;
+ }
+ }
+
+ QWidget* w;
+ if (target) {
+ w = QWidget::find((WId)target);
+ if (w && (w->windowType() == Qt::Desktop) && !w->acceptDrops())
+ w = 0;
+ } else {
+ w = 0;
+ target = rootwin;
+ }
+
+ DNDDEBUG << "and the final target is " << QWidget::find(target) << target;
+ DNDDEBUG << "the widget w is" << w;
+
+ WId proxy_target = xdndProxy(target);
+ if (!proxy_target)
+ proxy_target = target;
+ int target_version = 1;
+
+ if (proxy_target) {
+ Atom type = XNone;
+ int r, f;
+ unsigned long n, a;
+ unsigned char *retval;
+ X11->ignoreBadwindow();
+ r = XGetWindowProperty(X11->display, proxy_target, ATOM(XdndAware), 0,
+ 1, False, AnyPropertyType, &type, &f,&n,&a,&retval);
+ int *tv = (int *)retval;
+ if (r != Success || X11->badwindow()) {
+ target = 0;
+ } else {
+ target_version = qMin(xdnd_version,tv ? *tv : 1);
+ if (tv)
+ XFree(tv);
+// if (!(!X11->badwindow() && type))
+// target = 0;
+ }
+ }
+
+ if (target != qt_xdnd_current_target) {
+ if (qt_xdnd_current_target)
+ qt_xdnd_send_leave();
+
+ qt_xdnd_current_target = target;
+ qt_xdnd_current_proxy_target = proxy_target;
+ if (target) {
+ QVector<Atom> types;
+ int flags = target_version << 24;
+ QStringList fmts = QInternalMimeData::formatsHelper(dragPrivate()->data);
+ for (int i = 0; i < fmts.size(); ++i) {
+ QList<Atom> atoms = X11->xdndMimeAtomsForFormat(fmts.at(i));
+ for (int j = 0; j < atoms.size(); ++j) {
+ if (!types.contains(atoms.at(j)))
+ types.append(atoms.at(j));
+ }
+ }
+ if (types.size() > 3) {
+ XChangeProperty(X11->display,
+ dragPrivate()->source->effectiveWinId(), ATOM(XdndTypelist),
+ XA_ATOM, 32, PropModeReplace,
+ (unsigned char *)types.data(),
+ types.size());
+ flags |= 0x0001;
+ }
+ XClientMessageEvent enter;
+ enter.type = ClientMessage;
+ enter.window = target;
+ enter.format = 32;
+ enter.message_type = ATOM(XdndEnter);
+ enter.data.l[0] = dragPrivate()->source->effectiveWinId();
+ enter.data.l[1] = flags;
+ enter.data.l[2] = types.size()>0 ? types.at(0) : 0;
+ enter.data.l[3] = types.size()>1 ? types.at(1) : 0;
+ enter.data.l[4] = types.size()>2 ? types.at(2) : 0;
+ // provisionally set the rectangle to 5x5 pixels...
+ qt_xdnd_source_sameanswer = QRect(globalPos.x() - 2,
+ globalPos.y() -2 , 5, 5);
+
+ DEBUG("sending Xdnd enter");
+ if (w)
+ X11->xdndHandleEnter(w, (const XEvent *)&enter, false);
+ else if (target)
+ XSendEvent(X11->display, proxy_target, False, NoEventMask, (XEvent*)&enter);
+ waiting_for_status = false;
+ }
+ }
+ if (waiting_for_status)
+ return;
+
+ if (target) {
+ waiting_for_status = true;
+
+ XClientMessageEvent move;
+ move.type = ClientMessage;
+ move.window = target;
+ move.format = 32;
+ move.message_type = ATOM(XdndPosition);
+ move.window = target;
+ move.data.l[0] = dragPrivate()->source->effectiveWinId();
+ move.data.l[1] = 0; // flags
+ move.data.l[2] = (globalPos.x() << 16) + globalPos.y();
+ move.data.l[3] = X11->time;
+ move.data.l[4] = qtaction_to_xdndaction(defaultAction(dragPrivate()->possible_actions, QApplication::keyboardModifiers()));
+ DEBUG("sending Xdnd position");
+
+ qt_xdnd_source_current_time = X11->time;
+
+ if (w)
+ handle_xdnd_position(w, (const XEvent *)&move, false);
+ else
+ XSendEvent(X11->display, proxy_target, False, NoEventMask,
+ (XEvent*)&move);
+ } else {
+ if (willDrop) {
+ willDrop = false;
+ updateCursor();
+ }
+ }
+ DEBUG() << "QDragManager::move leave";
+#endif
+}
+
+
+void QDragManager::drop()
+{
+ Q_ASSERT(heartbeat != -1);
+ killTimer(heartbeat);
+ heartbeat = -1;
+ qt_xdnd_dragging = false;
+
+ if (!qt_xdnd_current_target)
+ return;
+
+ qDeleteInEventHandler(xdnd_data.deco);
+ xdnd_data.deco = 0;
+
+ XClientMessageEvent drop;
+ drop.type = ClientMessage;
+ drop.window = qt_xdnd_current_target;
+ drop.format = 32;
+ drop.message_type = ATOM(XdndDrop);
+ drop.data.l[0] = dragPrivate()->source->effectiveWinId();
+ drop.data.l[1] = 0; // flags
+ drop.data.l[2] = X11->time;
+
+ drop.data.l[3] = 0;
+ drop.data.l[4] = 0;
+
+ QWidget * w = QWidget::find(qt_xdnd_current_proxy_target);
+
+ if (w && (w->windowType() == Qt::Desktop) && !w->acceptDrops())
+ w = 0;
+
+ QXdndDropTransaction t = {
+ X11->time,
+ qt_xdnd_current_target,
+ qt_xdnd_current_proxy_target,
+ w,
+ current_embedding_widget,
+ object
+ };
+ X11->dndDropTransactions.append(t);
+ restartXdndDropExpiryTimer();
+
+ if (w)
+ X11->xdndHandleDrop(w, (const XEvent *)&drop, false);
+ else
+ XSendEvent(X11->display, qt_xdnd_current_proxy_target, False,
+ NoEventMask, (XEvent*)&drop);
+
+ qt_xdnd_current_target = 0;
+ qt_xdnd_current_proxy_target = 0;
+ qt_xdnd_source_current_time = 0;
+ current_embedding_widget = 0;
+ object = 0;
+
+#ifndef QT_NO_CURSOR
+ if (restoreCursor) {
+ QApplication::restoreOverrideCursor();
+ restoreCursor = false;
+ }
+#endif
+}
+
+
+
+bool QX11Data::xdndHandleBadwindow()
+{
+ if (qt_xdnd_current_target) {
+ QDragManager *manager = QDragManager::self();
+ if (manager->object) {
+ qt_xdnd_current_target = 0;
+ qt_xdnd_current_proxy_target = 0;
+ manager->object->deleteLater();
+ manager->object = 0;
+ delete xdnd_data.deco;
+ xdnd_data.deco = 0;
+ return true;
+ }
+ }
+ if (qt_xdnd_dragsource_xid) {
+ qt_xdnd_dragsource_xid = 0;
+ if (qt_xdnd_current_widget) {
+ QApplication::postEvent(qt_xdnd_current_widget, new QDragLeaveEvent);
+ qt_xdnd_current_widget = 0;
+ }
+ return true;
+ }
+ return false;
+}
+
+void QX11Data::xdndHandleSelectionRequest(const XSelectionRequestEvent * req)
+{
+ if (!req)
+ return;
+ XEvent evt;
+ evt.xselection.type = SelectionNotify;
+ evt.xselection.display = req->display;
+ evt.xselection.requestor = req->requestor;
+ evt.xselection.selection = req->selection;
+ evt.xselection.target = XNone;
+ evt.xselection.property = XNone;
+ evt.xselection.time = req->time;
+
+ QDragManager *manager = QDragManager::self();
+ QDrag *currentObject = manager->object;
+
+ // which transaction do we use? (note: -2 means use current manager->object)
+ int at = -1;
+
+ // figure out which data the requestor is really interested in
+ if (manager->object && req->time == qt_xdnd_source_current_time) {
+ // requestor wants the current drag data
+ at = -2;
+ } else {
+ // if someone has requested data in response to XdndDrop, find the corresponding transaction. the
+ // spec says to call XConvertSelection() using the timestamp from the XdndDrop
+ at = findXdndDropTransactionByTime(req->time);
+ if (at == -1) {
+ // no dice, perhaps the client was nice enough to use the same window id in XConvertSelection()
+ // that we sent the XdndDrop event to.
+ at = findXdndDropTransactionByWindow(req->requestor);
+ }
+ if (at == -1 && req->time == CurrentTime) {
+ // previous Qt versions always requested the data on a child of the target window
+ // using CurrentTime... but it could be asking for either drop data or the current drag's data
+ Window target = findXdndAwareParent(req->requestor);
+ if (target) {
+ if (qt_xdnd_current_target && qt_xdnd_current_target == target)
+ at = -2;
+ else
+ at = findXdndDropTransactionByWindow(target);
+ }
+ }
+ }
+ if (at >= 0) {
+ restartXdndDropExpiryTimer();
+
+ // use the drag object from an XdndDrop tansaction
+ manager->object = X11->dndDropTransactions.at(at).object;
+ } else if (at != -2) {
+ // no transaction found, we'll have to reject the request
+ manager->object = 0;
+ }
+ if (manager->object) {
+ Atom atomFormat = req->target;
+ int dataFormat = 0;
+ QByteArray data;
+ if (X11->xdndMimeDataForAtom(req->target, manager->dragPrivate()->data,
+ &data, &atomFormat, &dataFormat)) {
+ int dataSize = data.size() / (dataFormat / 8);
+ XChangeProperty (X11->display, req->requestor, req->property,
+ atomFormat, dataFormat, PropModeReplace,
+ (unsigned char *)data.data(), dataSize);
+ evt.xselection.property = req->property;
+ evt.xselection.target = atomFormat;
+ }
+ }
+
+ // reset manager->object in case we modified it above
+ manager->object = currentObject;
+
+ // ### this can die if req->requestor crashes at the wrong
+ // ### moment
+ XSendEvent(X11->display, req->requestor, False, 0, &evt);
+}
+
+static QVariant xdndObtainData(const char *format, QVariant::Type requestedType)
+{
+ QByteArray result;
+
+ QWidget* w;
+ QDragManager *manager = QDragManager::self();
+ if (qt_xdnd_dragsource_xid && manager->object &&
+ (w=QWidget::find(qt_xdnd_dragsource_xid))
+ && (!(w->windowType() == Qt::Desktop) || w->acceptDrops()))
+ {
+ QDragPrivate * o = QDragManager::self()->dragPrivate();
+ if (o->data->hasFormat(QLatin1String(format)))
+ result = o->data->data(QLatin1String(format));
+ return result;
+ }
+
+ QList<Atom> atoms;
+ int i = 0;
+ while ((qt_xdnd_types[i])) {
+ atoms.append(qt_xdnd_types[i]);
+ ++i;
+ }
+ QByteArray encoding;
+ Atom a = X11->xdndMimeAtomForFormat(QLatin1String(format), requestedType, atoms, &encoding);
+ if (!a)
+ return result;
+
+ if (XGetSelectionOwner(X11->display, ATOM(XdndSelection)) == XNone)
+ return result; // should never happen?
+
+ QWidget* tw = qt_xdnd_current_widget;
+ if (!qt_xdnd_current_widget || (qt_xdnd_current_widget->windowType() == Qt::Desktop))
+ tw = new QWidget;
+
+ XConvertSelection(X11->display, ATOM(XdndSelection), a, ATOM(XdndSelection), tw->effectiveWinId(),
+ qt_xdnd_target_current_time);
+ XFlush(X11->display);
+
+ XEvent xevent;
+ bool got=X11->clipboardWaitForEvent(tw->effectiveWinId(), SelectionNotify, &xevent, 5000);
+ if (got) {
+ Atom type;
+
+ if (X11->clipboardReadProperty(tw->effectiveWinId(), ATOM(XdndSelection), true, &result, 0, &type, 0, false)) {
+ if (type == ATOM(INCR)) {
+ int nbytes = result.size() >= 4 ? *((int*)result.data()) : 0;
+ result = X11->clipboardReadIncrementalProperty(tw->effectiveWinId(), ATOM(XdndSelection), nbytes, false);
+ } else if (type != a && type != XNone) {
+ DEBUG("Qt clipboard: unknown atom %ld", type);
+ }
+ }
+ }
+ if (!qt_xdnd_current_widget || (qt_xdnd_current_widget->windowType() == Qt::Desktop))
+ delete tw;
+
+ return X11->xdndMimeConvertToFormat(a, result, QLatin1String(format), requestedType, encoding);
+}
+
+
+/*
+ Enable drag and drop for widget w by installing the proper
+ properties on w's toplevel widget.
+*/
+bool QX11Data::dndEnable(QWidget* w, bool on)
+{
+ w = w->window();
+
+ if (bool(((QExtraWidget*)w)->topData()->dnd) == on)
+ return true; // been there, done that
+ ((QExtraWidget*)w)->topData()->dnd = on ? 1 : 0;
+
+ motifdndEnable(w, on);
+ return xdndEnable(w, on);
+}
+
+Qt::DropAction QDragManager::drag(QDrag * o)
+{
+ if (object == o || !o || !o->d_func()->source)
+ return Qt::IgnoreAction;
+
+ if (object) {
+ cancel();
+ qApp->removeEventFilter(this);
+ beingCancelled = false;
+ }
+
+ if (object) {
+ // the last drag and drop operation hasn't finished, so we are going to wait
+ // for one second to see if it does... if the finish message comes after this,
+ // then we could still have problems, but this is highly unlikely
+ QApplication::flush();
+
+ QTime started = QTime::currentTime();
+ QTime now = started;
+ do {
+ XEvent event;
+ if (XCheckTypedEvent(X11->display, ClientMessage, &event))
+ qApp->x11ProcessEvent(&event);
+
+ now = QTime::currentTime();
+ if (started > now) // crossed midnight
+ started = now;
+
+ // sleep 50 ms, so we don't use up CPU cycles all the time.
+ struct timeval usleep_tv;
+ usleep_tv.tv_sec = 0;
+ usleep_tv.tv_usec = 50000;
+ select(0, 0, 0, 0, &usleep_tv);
+ } while (object && started.msecsTo(now) < 1000);
+ }
+
+ object = o;
+ object->d_func()->target = 0;
+ xdnd_data.deco = new QShapedPixmapWidget(object->source()->window());
+
+ willDrop = false;
+
+ updatePixmap();
+
+ qApp->installEventFilter(this);
+ XSetSelectionOwner(X11->display, ATOM(XdndSelection), dragPrivate()->source->window()->internalWinId(), X11->time);
+ global_accepted_action = Qt::CopyAction;
+ qt_xdnd_source_sameanswer = QRect();
+#ifndef QT_NO_CURSOR
+ // set the override cursor (must be done here, since it is updated
+ // in the call to move() below)
+ qApp->setOverrideCursor(Qt::ArrowCursor);
+ restoreCursor = true;
+#endif
+ move(QCursor::pos());
+ heartbeat = startTimer(200);
+
+ qt_xdnd_dragging = true;
+
+ if (!QWidget::mouseGrabber())
+ xdnd_data.deco->grabMouse();
+
+ eventLoop = new QEventLoop;
+ (void) eventLoop->exec();
+ delete eventLoop;
+ eventLoop = 0;
+
+#ifndef QT_NO_CURSOR
+ if (restoreCursor) {
+ qApp->restoreOverrideCursor();
+ restoreCursor = false;
+ }
+#endif
+
+ // delete cursors as they may be different next drag.
+ delete noDropCursor;
+ noDropCursor = 0;
+ delete copyCursor;
+ copyCursor = 0;
+ delete moveCursor;
+ moveCursor = 0;
+ delete linkCursor;
+ linkCursor = 0;
+
+ delete xdnd_data.deco;
+ xdnd_data.deco = 0;
+ if (heartbeat != -1)
+ killTimer(heartbeat);
+ heartbeat = -1;
+ qt_xdnd_current_screen = -1;
+ qt_xdnd_dragging = false;
+
+ return global_accepted_action;
+ // object persists until we get an xdnd_finish message
+}
+
+void QDragManager::updatePixmap()
+{
+ if (xdnd_data.deco) {
+ QPixmap pm;
+ QPoint pm_hot(default_pm_hotx,default_pm_hoty);
+ if (object) {
+ pm = dragPrivate()->pixmap;
+ if (!pm.isNull())
+ pm_hot = dragPrivate()->hotspot;
+ }
+ if (pm.isNull()) {
+ if (!defaultPm)
+ defaultPm = new QPixmap(default_pm);
+ pm = *defaultPm;
+ }
+ xdnd_data.deco->pm_hot = pm_hot;
+ xdnd_data.deco->setPixmap(pm);
+ xdnd_data.deco->move(QCursor::pos()-pm_hot);
+ xdnd_data.deco->show();
+ }
+}
+
+QVariant QDropData::retrieveData_sys(const QString &mimetype, QVariant::Type requestedType) const
+{
+ QByteArray mime = mimetype.toLatin1();
+ QVariant data = X11->motifdnd_active
+ ? X11->motifdndObtainData(mime)
+ : xdndObtainData(mime, requestedType);
+ return data;
+}
+
+bool QDropData::hasFormat_sys(const QString &format) const
+{
+ return formats().contains(format);
+}
+
+QStringList QDropData::formats_sys() const
+{
+ QStringList formats;
+ if (X11->motifdnd_active) {
+ int i = 0;
+ QByteArray fmt;
+ while (!(fmt = X11->motifdndFormat(i)).isEmpty()) {
+ formats.append(QLatin1String(fmt));
+ ++i;
+ }
+ } else {
+ int i = 0;
+ while ((qt_xdnd_types[i])) {
+ QStringList formatsForAtom = X11->xdndMimeFormatsForAtom(qt_xdnd_types[i]);
+ for (int j = 0; j < formatsForAtom.size(); ++j) {
+ if (!formats.contains(formatsForAtom.at(j)))
+ formats.append(formatsForAtom.at(j));
+ }
+ ++i;
+ }
+ }
+ return formats;
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_DRAGANDDROP
diff --git a/src/gui/kernel/qdrag.cpp b/src/gui/kernel/qdrag.cpp
new file mode 100644
index 0000000000..e980d1cdbe
--- /dev/null
+++ b/src/gui/kernel/qdrag.cpp
@@ -0,0 +1,357 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <qwidget.h>
+#include <qdrag.h>
+#include <qpixmap.h>
+#include <qpoint.h>
+#include "qdnd_p.h"
+
+#ifndef QT_NO_DRAGANDDROP
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QDrag
+ \brief The QDrag class provides support for MIME-based drag and drop data
+ transfer.
+
+ Drag and drop is an intuitive way for users to copy or move data around in an
+ application, and is used in many desktop environments as a mechanism for copying
+ data between applications. Drag and drop support in Qt is centered around the
+ QDrag class that handles most of the details of a drag and drop operation.
+
+ The data to be transferred by the drag and drop operation is contained in a
+ QMimeData object. This is specified with the setMimeData() function in the
+ following way:
+
+ \snippet doc/src/snippets/dragging/mainwindow.cpp 1
+
+ Note that setMimeData() assigns ownership of the QMimeData object to the
+ QDrag object. The QDrag must be constructed on the heap with a parent QWidget
+ to ensure that Qt can clean up after the drag and drop operation has been
+ completed.
+
+ A pixmap can be used to represent the data while the drag is in
+ progress, and will move with the cursor to the drop target. This
+ pixmap typically shows an icon that represents the MIME type of
+ the data being transferred, but any pixmap can be set with
+ setPixmap(). The cursor's hot spot can be given a position
+ relative to the top-left corner of the pixmap with the
+ setHotSpot() function. The following code positions the pixmap so
+ that the cursor's hot spot points to the center of its bottom
+ edge:
+
+ \snippet doc/src/snippets/separations/finalwidget.cpp 2
+
+ \note On X11, the pixmap may not be able to keep up with the mouse
+ movements if the hot spot causes the pixmap to be displayed
+ directly under the cursor.
+
+ The source and target widgets can be found with source() and target().
+ These functions are often used to determine whether drag and drop operations
+ started and finished at the same widget, so that special behavior can be
+ implemented.
+
+ QDrag only deals with the drag and drop operation itself. It is up to the
+ developer to decide when a drag operation begins, and how a QDrag object should
+ be constructed and used. For a given widget, it is often necessary to
+ reimplement \l{QWidget::mousePressEvent()}{mousePressEvent()} to determine
+ whether the user has pressed a mouse button, and reimplement
+ \l{QWidget::mouseMoveEvent()}{mouseMoveEvent()} to check whether a QDrag is
+ required.
+
+ \sa {Drag and Drop}, QClipboard, QMimeData, QWindowsMime, QMacPasteboardMime,
+ {Draggable Icons Example}, {Draggable Text Example}, {Drop Site Example},
+ {Fridge Magnets Example}
+*/
+
+/*!
+ Constructs a new drag object for the widget specified by \a dragSource.
+*/
+QDrag::QDrag(QWidget *dragSource)
+ : QObject(*new QDragPrivate, dragSource)
+{
+ Q_D(QDrag);
+ d->source = dragSource;
+ d->target = 0;
+ d->data = 0;
+ d->hotspot = QPoint(-10, -10);
+ d->possible_actions = Qt::CopyAction;
+ d->executed_action = Qt::IgnoreAction;
+ d->defaultDropAction = Qt::IgnoreAction;
+}
+
+/*!
+ Destroys the drag object.
+*/
+QDrag::~QDrag()
+{
+ Q_D(QDrag);
+ delete d->data;
+ QDragManager *manager = QDragManager::self();
+ if (manager && manager->object == this)
+ manager->cancel(false);
+}
+
+/*!
+ Sets the data to be sent to the given MIME \a data. Ownership of the data is
+ transferred to the QDrag object.
+*/
+void QDrag::setMimeData(QMimeData *data)
+{
+ Q_D(QDrag);
+ if (d->data == data)
+ return;
+ if (d->data != 0)
+ delete d->data;
+ d->data = data;
+}
+
+/*!
+ Returns the MIME data that is encapsulated by the drag object.
+*/
+QMimeData *QDrag::mimeData() const
+{
+ Q_D(const QDrag);
+ return d->data;
+}
+
+/*!
+ Sets \a pixmap as the pixmap used to represent the data in a drag
+ and drop operation. You can only set a pixmap before the drag is
+ started.
+*/
+void QDrag::setPixmap(const QPixmap &pixmap)
+{
+ Q_D(QDrag);
+ d->pixmap = pixmap;
+}
+
+/*!
+ Returns the pixmap used to represent the data in a drag and drop operation.
+*/
+QPixmap QDrag::pixmap() const
+{
+ Q_D(const QDrag);
+ return d->pixmap;
+}
+
+/*!
+ Sets the position of the hot spot relative to the top-left corner of the
+ pixmap used to the point specified by \a hotspot.
+
+ \bold{Note:} on X11, the pixmap may not be able to keep up with the mouse
+ movements if the hot spot causes the pixmap to be displayed
+ directly under the cursor.
+*/
+void QDrag::setHotSpot(const QPoint& hotspot)
+{
+ Q_D(QDrag);
+ d->hotspot = hotspot;
+}
+
+/*!
+ Returns the position of the hot spot relative to the top-left corner of the
+ cursor.
+*/
+QPoint QDrag::hotSpot() const
+{
+ Q_D(const QDrag);
+ return d->hotspot;
+}
+
+/*!
+ Returns the source of the drag object. This is the widget where the drag
+ and drop operation originated.
+*/
+QWidget *QDrag::source() const
+{
+ Q_D(const QDrag);
+ return d->source;
+}
+
+/*!
+ Returns the target of the drag and drop operation. This is the widget where
+ the drag object was dropped.
+*/
+QWidget *QDrag::target() const
+{
+ Q_D(const QDrag);
+ return d->target;
+}
+
+/*!
+ \since 4.3
+
+ Starts the drag and drop operation and returns a value indicating the requested
+ drop action when it is completed. The drop actions that the user can choose
+ from are specified in \a supportedActions. The default proposed action will be selected
+ among the allowed actions in the following order: Move, Copy and Link.
+
+ \bold{Note:} On Linux and Mac OS X, the drag and drop operation
+ can take some time, but this function does not block the event
+ loop. Other events are still delivered to the application while
+ the operation is performed. On Windows, the Qt event loop is
+ blocked while during the operation.
+*/
+
+Qt::DropAction QDrag::exec(Qt::DropActions supportedActions)
+{
+ return exec(supportedActions, Qt::IgnoreAction);
+}
+
+/*!
+ \since 4.3
+
+ Starts the drag and drop operation and returns a value indicating the requested
+ drop action when it is completed. The drop actions that the user can choose
+ from are specified in \a supportedActions.
+
+ The \a defaultDropAction determines which action will be proposed when the user performs a
+ drag without using modifier keys.
+
+ \bold{Note:} On Linux and Mac OS X, the drag and drop operation
+ can take some time, but this function does not block the event
+ loop. Other events are still delivered to the application while
+ the operation is performed. On Windows, the Qt event loop is
+ blocked while during the operation.
+*/
+
+Qt::DropAction QDrag::exec(Qt::DropActions supportedActions, Qt::DropAction defaultDropAction)
+{
+ Q_D(QDrag);
+ if (!d->data) {
+ qWarning("QDrag: No mimedata set before starting the drag");
+ return d->executed_action;
+ }
+ QDragManager *manager = QDragManager::self();
+ d->defaultDropAction = Qt::IgnoreAction;
+ d->possible_actions = supportedActions;
+
+ if (manager) {
+ if (defaultDropAction == Qt::IgnoreAction) {
+ if (supportedActions & Qt::MoveAction) {
+ d->defaultDropAction = Qt::MoveAction;
+ } else if (supportedActions & Qt::CopyAction) {
+ d->defaultDropAction = Qt::CopyAction;
+ } else if (supportedActions & Qt::LinkAction) {
+ d->defaultDropAction = Qt::LinkAction;
+ }
+ } else {
+ d->defaultDropAction = defaultDropAction;
+ }
+ d->executed_action = manager->drag(this);
+ }
+
+ return d->executed_action;
+}
+
+/*!
+ \obsolete
+
+ \bold{Note:} It is recommended to use exec() instead of this function.
+
+ Starts the drag and drop operation and returns a value indicating the requested
+ drop action when it is completed. The drop actions that the user can choose
+ from are specified in \a request. Qt::CopyAction is always allowed.
+
+ \bold{Note:} Although the drag and drop operation can take some time, this function
+ does not block the event loop. Other events are still delivered to the application
+ while the operation is performed.
+
+ \sa exec()
+*/
+Qt::DropAction QDrag::start(Qt::DropActions request)
+{
+ Q_D(QDrag);
+ if (!d->data) {
+ qWarning("QDrag: No mimedata set before starting the drag");
+ return d->executed_action;
+ }
+ QDragManager *manager = QDragManager::self();
+ d->defaultDropAction = Qt::IgnoreAction;
+ d->possible_actions = request | Qt::CopyAction;
+ if (manager)
+ d->executed_action = manager->drag(this);
+ return d->executed_action;
+}
+
+/*!
+ Sets the drag \a cursor for the \a action. This allows you
+ to override the default native cursors. To revert to using the
+ native cursor for \a action pass in a null QPixmap as \a cursor.
+
+ The \a action can only be CopyAction, MoveAction or LinkAction.
+ All other values of DropAction are ignored.
+*/
+void QDrag::setDragCursor(const QPixmap &cursor, Qt::DropAction action)
+{
+ Q_D(QDrag);
+ if (action != Qt::CopyAction && action != Qt::MoveAction && action != Qt::LinkAction)
+ return;
+ if (cursor.isNull())
+ d->customCursors.remove(action);
+ else
+ d->customCursors[action] = cursor;
+}
+
+/*!
+ \fn void QDrag::actionChanged(Qt::DropAction action)
+
+ This signal is emitted when the \a action associated with the
+ drag changes.
+
+ \sa targetChanged()
+*/
+
+/*!
+ \fn void QDrag::targetChanged(QWidget *newTarget)
+
+ This signal is emitted when the target of the drag and drop
+ operation changes, with \a newTarget the new target.
+
+ \sa target(), actionChanged()
+*/
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_DRAGANDDROP
diff --git a/src/gui/kernel/qdrag.h b/src/gui/kernel/qdrag.h
new file mode 100644
index 0000000000..9f0854403b
--- /dev/null
+++ b/src/gui/kernel/qdrag.h
@@ -0,0 +1,105 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QDRAG_H
+#define QDRAG_H
+
+#include <QtCore/qobject.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+#ifndef QT_NO_DRAGANDDROP
+class QMimeData;
+class QDragPrivate;
+class QWidget;
+class QPixmap;
+class QPoint;
+class QDragManager;
+
+class Q_GUI_EXPORT QDrag : public QObject
+{
+ Q_OBJECT
+ Q_DECLARE_PRIVATE(QDrag)
+public:
+ explicit QDrag(QWidget *dragSource);
+ ~QDrag();
+
+ void setMimeData(QMimeData *data);
+ QMimeData *mimeData() const;
+
+ void setPixmap(const QPixmap &);
+ QPixmap pixmap() const;
+
+ void setHotSpot(const QPoint &hotspot);
+ QPoint hotSpot() const;
+
+ QWidget *source() const;
+ QWidget *target() const;
+
+ Qt::DropAction start(Qt::DropActions supportedActions = Qt::CopyAction);
+ Qt::DropAction exec(Qt::DropActions supportedActions = Qt::MoveAction);
+ Qt::DropAction exec(Qt::DropActions supportedActions, Qt::DropAction defaultAction);
+
+ void setDragCursor(const QPixmap &cursor, Qt::DropAction action);
+
+Q_SIGNALS:
+ void actionChanged(Qt::DropAction action);
+ void targetChanged(QWidget *newTarget);
+
+private:
+#ifdef Q_WS_MAC
+ friend class QWidgetPrivate;
+#endif
+ friend class QDragManager;
+ Q_DISABLE_COPY(QDrag)
+};
+
+#endif // QT_NO_DRAGANDDROP
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QDRAG_H
diff --git a/src/gui/kernel/qevent.cpp b/src/gui/kernel/qevent.cpp
new file mode 100644
index 0000000000..3f1df5e5a2
--- /dev/null
+++ b/src/gui/kernel/qevent.cpp
@@ -0,0 +1,3509 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qevent.h"
+#include "qcursor.h"
+#include "qapplication.h"
+#include "private/qapplication_p.h"
+#include "private/qkeysequence_p.h"
+#include "qwidget.h"
+#include "qdebug.h"
+#include "qmime.h"
+#include "qdnd_p.h"
+#include "qevent_p.h"
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QInputEvent
+ \ingroup events
+
+ \brief The QInputEvent class is the base class for events that
+ describe user input.
+*/
+
+/*!
+ \internal
+*/
+QInputEvent::QInputEvent(Type type, Qt::KeyboardModifiers modifiers)
+ : QEvent(type), modState(modifiers)
+{}
+
+/*!
+ \internal
+*/
+QInputEvent::~QInputEvent()
+{
+}
+
+/*!
+ \fn Qt::KeyboardModifiers QInputEvent::modifiers() const
+
+ Returns the keyboard modifier flags that existed immediately
+ before the event occurred.
+
+ \sa QApplication::keyboardModifiers()
+*/
+
+/*!
+ \class QMouseEvent
+ \ingroup events
+
+ \brief The QMouseEvent class contains parameters that describe a mouse event.
+
+ Mouse events occur when a mouse button is pressed or released
+ inside a widget, or when the mouse cursor is moved.
+
+ Mouse move events will occur only when a mouse button is pressed
+ down, unless mouse tracking has been enabled with
+ QWidget::setMouseTracking().
+
+ Qt automatically grabs the mouse when a mouse button is pressed
+ inside a widget; the widget will continue to receive mouse events
+ until the last mouse button is released.
+
+ A mouse event contains a special accept flag that indicates
+ whether the receiver wants the event. You should call ignore() if
+ the mouse event is not handled by your widget. A mouse event is
+ propagated up the parent widget chain until a widget accepts it
+ with accept(), or an event filter consumes it.
+
+ The state of the keyboard modifier keys can be found by calling the
+ \l{QInputEvent::modifiers()}{modifiers()} function, inhertied from
+ QInputEvent.
+
+ The functions pos(), x(), and y() give the cursor position
+ relative to the widget that receives the mouse event. If you
+ move the widget as a result of the mouse event, use the global
+ position returned by globalPos() to avoid a shaking motion.
+
+ The QWidget::setEnabled() function can be used to enable or
+ disable mouse and keyboard events for a widget.
+
+ Reimplement the QWidget event handlers, QWidget::mousePressEvent(),
+ QWidget::mouseReleaseEvent(), QWidget::mouseDoubleClickEvent(),
+ and QWidget::mouseMoveEvent() to receive mouse events in your own
+ widgets.
+
+ \sa QWidget::setMouseTracking() QWidget::grabMouse()
+ QCursor::pos()
+*/
+
+/*!
+ Constructs a mouse event object.
+
+ The \a type parameter must be one of QEvent::MouseButtonPress,
+ QEvent::MouseButtonRelease, QEvent::MouseButtonDblClick,
+ or QEvent::MouseMove.
+
+ The \a position is the mouse cursor's position relative to the
+ receiving widget.
+ The \a button that caused the event is given as a value from
+ the Qt::MouseButton enum. If the event \a type is
+ \l MouseMove, the appropriate button for this event is Qt::NoButton.
+ The mouse and keyboard states at the time of the event are specified by
+ \a buttons and \a modifiers.
+
+ The globalPos() is initialized to QCursor::pos(), which may not
+ be appropriate. Use the other constructor to specify the global
+ position explicitly.
+*/
+
+QMouseEvent::QMouseEvent(Type type, const QPoint &position, Qt::MouseButton button,
+ Qt::MouseButtons buttons, Qt::KeyboardModifiers modifiers)
+ : QInputEvent(type, modifiers), p(position), b(button), mouseState(buttons)
+{
+ g = QCursor::pos();
+}
+
+/*!
+ \internal
+*/
+QMouseEvent::~QMouseEvent()
+{
+}
+
+#ifdef QT3_SUPPORT
+/*!
+ Use QMouseEvent(\a type, \a pos, \a button, \c buttons, \c
+ modifiers) instead, where \c buttons is \a state &
+ Qt::MouseButtonMask and \c modifiers is \a state &
+ Qt::KeyButtonMask.
+*/
+QMouseEvent::QMouseEvent(Type type, const QPoint &pos, Qt::ButtonState button, int state)
+ : QInputEvent(type), p(pos), b((Qt::MouseButton)button)
+{
+ g = QCursor::pos();
+ mouseState = Qt::MouseButtons((state ^ b) & Qt::MouseButtonMask);
+ modState = Qt::KeyboardModifiers(state & (int)Qt::KeyButtonMask);
+}
+
+/*!
+ Use QMouseEvent(\a type, \a pos, \a globalPos, \a button,
+ \c buttons, \c modifiers) instead, where
+ \c buttons is \a state & Qt::MouseButtonMask and
+ \c modifiers is \a state & Qt::KeyButtonMask.
+*/
+QMouseEvent::QMouseEvent(Type type, const QPoint &pos, const QPoint &globalPos,
+ Qt::ButtonState button, int state)
+ : QInputEvent(type), p(pos), g(globalPos), b((Qt::MouseButton)button)
+{
+ mouseState = Qt::MouseButtons((state ^ b) & Qt::MouseButtonMask);
+ modState = Qt::KeyboardModifiers(state & (int)Qt::KeyButtonMask);
+}
+#endif
+
+
+/*!
+ Constructs a mouse event object.
+
+ The \a type parameter must be QEvent::MouseButtonPress,
+ QEvent::MouseButtonRelease, QEvent::MouseButtonDblClick,
+ or QEvent::MouseMove.
+
+ The \a pos is the mouse cursor's position relative to the
+ receiving widget. The cursor's position in global coordinates is
+ specified by \a globalPos. The \a button that caused the event is
+ given as a value from the \l Qt::MouseButton enum. If the event \a
+ type is \l MouseMove, the appropriate button for this event is
+ Qt::NoButton. \a buttons is the state of all buttons at the
+ time of the event, \a modifiers the state of all keyboard
+ modifiers.
+
+*/
+QMouseEvent::QMouseEvent(Type type, const QPoint &pos, const QPoint &globalPos,
+ Qt::MouseButton button, Qt::MouseButtons buttons,
+ Qt::KeyboardModifiers modifiers)
+ : QInputEvent(type, modifiers), p(pos), g(globalPos), b(button), mouseState(buttons)
+{}
+
+/*!
+ \internal
+*/
+QMouseEvent *QMouseEvent::createExtendedMouseEvent(Type type, const QPointF &pos,
+ const QPoint &globalPos, Qt::MouseButton button,
+ Qt::MouseButtons buttons, Qt::KeyboardModifiers modifiers)
+{
+ return new QMouseEventEx(type, pos, globalPos, button, buttons, modifiers);
+}
+
+/*!
+ \fn bool QMouseEvent::hasExtendedInfo() const
+ \internal
+*/
+
+/*!
+ \since 4.4
+
+ Returns the position of the mouse cursor as a QPointF, relative to the
+ widget that received the event.
+
+ If you move the widget as a result of the mouse event, use the
+ global position returned by globalPos() to avoid a shaking
+ motion.
+
+ \sa x() y() pos() globalPos()
+*/
+QPointF QMouseEvent::posF() const
+{
+ return hasExtendedInfo() ? reinterpret_cast<const QMouseEventEx *>(this)->posF : QPointF(pos());
+}
+
+/*!
+ \internal
+*/
+QMouseEventEx::QMouseEventEx(Type type, const QPointF &pos, const QPoint &globalPos,
+ Qt::MouseButton button, Qt::MouseButtons buttons,
+ Qt::KeyboardModifiers modifiers)
+ : QMouseEvent(type, pos.toPoint(), globalPos, button, buttons, modifiers), posF(pos)
+{
+ d = reinterpret_cast<QEventPrivate *>(this);
+}
+
+/*!
+ \internal
+*/
+QMouseEventEx::~QMouseEventEx()
+{
+}
+
+/*!
+ \fn const QPoint &QMouseEvent::pos() const
+
+ Returns the position of the mouse cursor, relative to the widget
+ that received the event.
+
+ If you move the widget as a result of the mouse event, use the
+ global position returned by globalPos() to avoid a shaking
+ motion.
+
+ \sa x() y() globalPos()
+*/
+
+/*!
+ \fn const QPoint &QMouseEvent::globalPos() const
+
+ Returns the global position of the mouse cursor \e{at the time
+ of the event}. This is important on asynchronous window systems
+ like X11. Whenever you move your widgets around in response to
+ mouse events, globalPos() may differ a lot from the current
+ pointer position QCursor::pos(), and from
+ QWidget::mapToGlobal(pos()).
+
+ \sa globalX() globalY()
+*/
+
+/*!
+ \fn int QMouseEvent::x() const
+
+ Returns the x position of the mouse cursor, relative to the
+ widget that received the event.
+
+ \sa y() pos()
+*/
+
+/*!
+ \fn int QMouseEvent::y() const
+
+ Returns the y position of the mouse cursor, relative to the
+ widget that received the event.
+
+ \sa x() pos()
+*/
+
+/*!
+ \fn int QMouseEvent::globalX() const
+
+ Returns the global x position of the mouse cursor at the time of
+ the event.
+
+ \sa globalY() globalPos()
+*/
+
+/*!
+ \fn int QMouseEvent::globalY() const
+
+ Returns the global y position of the mouse cursor at the time of
+ the event.
+
+ \sa globalX() globalPos()
+*/
+
+/*!
+ \fn Qt::MouseButton QMouseEvent::button() const
+
+ Returns the button that caused the event.
+
+ Note that the returned value is always Qt::NoButton for mouse
+ move events.
+
+ \sa buttons() Qt::MouseButton
+*/
+
+/*!
+ \fn Qt::MouseButton QMouseEvent::buttons() const
+
+ Returns the button state when the event was generated. The button
+ state is a combination of Qt::LeftButton, Qt::RightButton,
+ Qt::MidButton using the OR operator. For mouse move events,
+ this is all buttons that are pressed down. For mouse press and
+ double click events this includes the button that caused the
+ event. For mouse release events this excludes the button that
+ caused the event.
+
+ \sa button() Qt::MouseButton
+*/
+
+
+/*!
+ \fn Qt::ButtonState QMouseEvent::state() const
+
+ Returns the button state immediately before the event was
+ generated. The button state is a combination of mouse buttons
+ (see Qt::ButtonState) and keyboard modifiers (Qt::MouseButtons).
+
+ Use buttons() and/or modifiers() instead. Be aware that buttons()
+ return the state immediately \e after the event was generated.
+*/
+
+/*!
+ \fn Qt::ButtonState QMouseEvent::stateAfter() const
+
+ Returns the button state immediately after the event was
+ generated. The button state is a combination of mouse buttons
+ (see Qt::ButtonState) and keyboard modifiers (Qt::MouseButtons).
+
+ Use buttons() and/or modifiers() instead.
+*/
+
+/*!
+ \class QHoverEvent
+ \ingroup events
+
+ \brief The QHoverEvent class contains parameters that describe a mouse event.
+
+ Mouse events occur when a mouse cursor is moved into, out of, or within a
+ widget, and if the widget has the Qt::WA_Hover attribute.
+
+ The function pos() gives the current cursor position, while oldPos() gives
+ the old mouse position.
+*/
+
+/*!
+ \fn const QPoint &QHoverEvent::pos() const
+
+ Returns the position of the mouse cursor, relative to the widget
+ that received the event.
+
+ On QEvent::HoverLeave events, this position will always be
+ QPoint(-1, -1).
+
+ \sa oldPos()
+*/
+
+/*!
+ \fn const QPoint &QHoverEvent::oldPos() const
+
+ Returns the previous position of the mouse cursor, relative to the widget
+ that received the event. If there is no previous position, oldPos() will
+ return the same position as pos().
+
+ On QEvent::HoverEnter events, this position will always be
+ QPoint(-1, -1).
+
+ \sa pos()
+*/
+
+/*!
+ Constructs a hover event object.
+
+ The \a type parameter must be QEvent::HoverEnter,
+ QEvent::HoverLeave, or QEvent::HoverMove.
+
+ The \a pos is the current mouse cursor's position relative to the
+ receiving widget, while \a oldPos is the previous mouse cursor's
+ position relative to the receiving widget.
+*/
+QHoverEvent::QHoverEvent(Type type, const QPoint &pos, const QPoint &oldPos)
+ : QEvent(type), p(pos), op(oldPos)
+{
+}
+
+/*!
+ \internal
+*/
+QHoverEvent::~QHoverEvent()
+{
+}
+
+
+/*!
+ \class QWheelEvent
+ \brief The QWheelEvent class contains parameters that describe a wheel event.
+
+ \ingroup events
+
+ Wheel events are sent to the widget under the mouse cursor, but
+ if that widget does not handle the event they are sent to the
+ focus widget. The rotation distance is provided by delta().
+ The functions pos() and globalPos() return the mouse cursor's
+ location at the time of the event.
+
+ A wheel event contains a special accept flag that indicates
+ whether the receiver wants the event. You should call ignore() if
+ you do not handle the wheel event; this ensures that it will be
+ sent to the parent widget.
+
+ The QWidget::setEnabled() function can be used to enable or
+ disable mouse and keyboard events for a widget.
+
+ The event handler QWidget::wheelEvent() receives wheel events.
+
+ \sa QMouseEvent QWidget::grabMouse()
+*/
+
+/*!
+ \fn Qt::MouseButtons QWheelEvent::buttons() const
+
+ Returns the mouse state when the event occurred.
+*/
+
+/*!
+ \fn Qt::Orientation QWheelEvent::orientation() const
+
+ Returns the wheel's orientation.
+*/
+
+/*!
+ Constructs a wheel event object.
+
+ The position, \a pos, is the location of the mouse cursor within
+ the widget. The globalPos() is initialized to QCursor::pos()
+ which is usually, but not always, correct.
+ Use the other constructor if you need to specify the global
+ position explicitly.
+
+ The \a buttons describe the state of the mouse buttons at the time
+ of the event, \a delta contains the rotation distance,
+ \a modifiers holds the keyboard modifier flags at the time of the
+ event, and \a orient holds the wheel's orientation.
+
+ \sa pos() delta() state()
+*/
+#ifndef QT_NO_WHEELEVENT
+QWheelEvent::QWheelEvent(const QPoint &pos, int delta,
+ Qt::MouseButtons buttons, Qt::KeyboardModifiers modifiers,
+ Qt::Orientation orient)
+ : QInputEvent(Wheel, modifiers), p(pos), d(delta), mouseState(buttons), o(orient)
+{
+ g = QCursor::pos();
+}
+
+/*!
+ \internal
+*/
+QWheelEvent::~QWheelEvent()
+{
+}
+
+#ifdef QT3_SUPPORT
+/*!
+ Use one of the other constructors instead.
+*/
+QWheelEvent::QWheelEvent(const QPoint &pos, int delta, int state, Qt::Orientation orient)
+ : QInputEvent(Wheel), p(pos), d(delta), o(orient)
+{
+ g = QCursor::pos();
+ mouseState = Qt::MouseButtons(state & Qt::MouseButtonMask);
+ modState = Qt::KeyboardModifiers(state & (int)Qt::KeyButtonMask);
+}
+#endif
+
+/*!
+ Constructs a wheel event object.
+
+ The \a pos provides the location of the mouse cursor
+ within the widget. The position in global coordinates is specified
+ by \a globalPos. \a delta contains the rotation distance, \a modifiers
+ holds the keyboard modifier flags at the time of the event, and
+ \a orient holds the wheel's orientation.
+
+ \sa pos() globalPos() delta() state()
+*/
+QWheelEvent::QWheelEvent(const QPoint &pos, const QPoint& globalPos, int delta,
+ Qt::MouseButtons buttons, Qt::KeyboardModifiers modifiers,
+ Qt::Orientation orient)
+ : QInputEvent(Wheel, modifiers), p(pos), g(globalPos), d(delta), mouseState(buttons), o(orient)
+{}
+
+#ifdef QT3_SUPPORT
+/*!
+ Use one of the other constructors instead.
+*/
+QWheelEvent::QWheelEvent(const QPoint &pos, const QPoint& globalPos, int delta, int state,
+ Qt::Orientation orient)
+ : QInputEvent(Wheel), p(pos), g(globalPos), d(delta), o(orient)
+{
+ mouseState = Qt::MouseButtons(state & Qt::MouseButtonMask);
+ modState = Qt::KeyboardModifiers(state & (int) Qt::KeyButtonMask);
+}
+#endif
+#endif // QT_NO_WHEELEVENT
+
+/*!
+ \fn int QWheelEvent::delta() const
+
+ Returns the distance that the wheel is rotated, in eighths of a
+ degree. A positive value indicates that the wheel was rotated
+ forwards away from the user; a negative value indicates that the
+ wheel was rotated backwards toward the user.
+
+ Most mouse types work in steps of 15 degrees, in which case the
+ delta value is a multiple of 120; i.e., 120 units * 1/8 = 15 degrees.
+
+ However, some mice have finer-resolution wheels and send delta values
+ that are less than 120 units (less than 15 degrees). To support this
+ possibility, you can either cumulatively add the delta values from events
+ until the value of 120 is reached, then scroll the widget, or you can
+ partially scroll the widget in response to each wheel event.
+
+ Example:
+
+ \snippet doc/src/snippets/code/src_gui_kernel_qevent.cpp 0
+*/
+
+/*!
+ \fn const QPoint &QWheelEvent::pos() const
+
+ Returns the position of the mouse cursor relative to the widget
+ that received the event.
+
+ If you move your widgets around in response to mouse events,
+ use globalPos() instead of this function.
+
+ \sa x() y() globalPos()
+*/
+
+/*!
+ \fn int QWheelEvent::x() const
+
+ Returns the x position of the mouse cursor, relative to the
+ widget that received the event.
+
+ \sa y() pos()
+*/
+
+/*!
+ \fn int QWheelEvent::y() const
+
+ Returns the y position of the mouse cursor, relative to the
+ widget that received the event.
+
+ \sa x() pos()
+*/
+
+
+/*!
+ \fn const QPoint &QWheelEvent::globalPos() const
+
+ Returns the global position of the mouse pointer \e{at the time
+ of the event}. This is important on asynchronous window systems
+ such as X11; whenever you move your widgets around in response to
+ mouse events, globalPos() can differ a lot from the current
+ cursor position returned by QCursor::pos().
+
+ \sa globalX() globalY()
+*/
+
+/*!
+ \fn int QWheelEvent::globalX() const
+
+ Returns the global x position of the mouse cursor at the time of
+ the event.
+
+ \sa globalY() globalPos()
+*/
+
+/*!
+ \fn int QWheelEvent::globalY() const
+
+ Returns the global y position of the mouse cursor at the time of
+ the event.
+
+ \sa globalX() globalPos()
+*/
+
+
+/*! \obsolete
+ \fn Qt::ButtonState QWheelEvent::state() const
+
+ Returns the keyboard modifier flags at the time of the event.
+
+ The returned value is a selection of the following values,
+ combined using the OR operator: Qt::ShiftButton,
+ Qt::ControlButton, and Qt::AltButton.
+*/
+
+
+/*!
+ \class QKeyEvent
+ \brief The QKeyEvent class describes a key event.
+
+ \ingroup events
+
+ Key events are sent to the widget with keyboard input focus
+ when keys are pressed or released.
+
+ A key event contains a special accept flag that indicates whether
+ the receiver will handle the key event. You should call ignore()
+ if the key press or release event is not handled by your widget.
+ A key event is propagated up the parent widget chain until a
+ widget accepts it with accept() or an event filter consumes it.
+ Key events for multimedia keys are ignored by default. You should
+ call accept() if your widget handles those events.
+
+ The QWidget::setEnable() function can be used to enable or disable
+ mouse and keyboard events for a widget.
+
+ The event handlers QWidget::keyPressEvent() and
+ QWidget::keyReleaseEvent() receive key events.
+
+ \sa QFocusEvent, QWidget::grabKeyboard()
+*/
+
+/*!
+ Constructs a key event object.
+
+ The \a type parameter must be QEvent::KeyPress, QEvent::KeyRelease,
+ or QEvent::ShortcutOverride.
+
+ If \a key is 0, the event is not a result of
+ a known key; for example, it may be the result of a compose
+ sequence or keyboard macro. The \a modifiers holds the keyboard
+ modifiers, and the given \a text is the Unicode text that the
+ key generated. If \a autorep is true, isAutoRepeat() will be
+ true. \a count is the number of keys involved in the event.
+*/
+QKeyEvent::QKeyEvent(Type type, int key, Qt::KeyboardModifiers modifiers, const QString& text,
+ bool autorep, ushort count)
+ : QInputEvent(type, modifiers), txt(text), k(key), c(count), autor(autorep)
+{
+}
+
+/*!
+ \internal
+*/
+QKeyEvent::~QKeyEvent()
+{
+}
+
+/*!
+ \internal
+*/
+QKeyEvent *QKeyEvent::createExtendedKeyEvent(Type type, int key, Qt::KeyboardModifiers modifiers,
+ quint32 nativeScanCode, quint32 nativeVirtualKey,
+ quint32 nativeModifiers,
+ const QString& text, bool autorep, ushort count)
+{
+ return new QKeyEventEx(type, key, modifiers, text, autorep, count,
+ nativeScanCode, nativeVirtualKey, nativeModifiers);
+}
+
+/*!
+ \fn bool QKeyEvent::hasExtendedInfo() const
+ \internal
+*/
+
+/*!
+ \since 4.2
+
+ Returns the native scan code of the key event. If the key event
+ does not contain this data 0 is returned.
+
+ Note: The native scan code may be 0, even if the key event contains
+ extended information.
+
+ Note: On Mac OS/X, this function is not useful, because there is no
+ way to get the scan code from Carbon or Cocoa. The function always
+ returns 1 (or 0 in the case explained above).
+*/
+quint32 QKeyEvent::nativeScanCode() const
+{
+ return (reinterpret_cast<const QKeyEvent*>(d) != this
+ ? 0 : reinterpret_cast<const QKeyEventEx*>(this)->nScanCode);
+}
+
+/*!
+ \since 4.2
+
+ Returns the native virtual key, or key sym of the key event.
+ If the key event does not contain this data 0 is returned.
+
+ Note: The native virtual key may be 0, even if the key event contains extended information.
+*/
+quint32 QKeyEvent::nativeVirtualKey() const
+{
+ return (reinterpret_cast<const QKeyEvent*>(d) != this
+ ? 0 : reinterpret_cast<const QKeyEventEx*>(this)->nVirtualKey);
+}
+
+/*!
+ \since 4.2
+
+ Returns the native modifiers of a key event.
+ If the key event does not contain this data 0 is returned.
+
+ Note: The native modifiers may be 0, even if the key event contains extended information.
+*/
+quint32 QKeyEvent::nativeModifiers() const
+{
+ return (reinterpret_cast<const QKeyEvent*>(d) != this
+ ? 0 : reinterpret_cast<const QKeyEventEx*>(this)->nModifiers);
+}
+
+/*!
+ \internal
+ Creates an extended key event object, which in addition to the normal key event data, also
+ contains the native scan code, virtual key and modifiers. This extra data is used by the
+ shortcut system, to determine which shortcuts to trigger.
+*/
+QKeyEventEx::QKeyEventEx(Type type, int key, Qt::KeyboardModifiers modifiers,
+ const QString &text, bool autorep, ushort count,
+ quint32 nativeScanCode, quint32 nativeVirtualKey, quint32 nativeModifiers)
+ : QKeyEvent(type, key, modifiers, text, autorep, count),
+ nScanCode(nativeScanCode), nVirtualKey(nativeVirtualKey), nModifiers(nativeModifiers)
+{
+ d = reinterpret_cast<QEventPrivate*>(this);
+}
+
+/*!
+ \internal
+ Creates a copy of an other extended key event.
+*/
+QKeyEventEx::QKeyEventEx(const QKeyEventEx &other)
+ : QKeyEvent(QEvent::Type(other.t), other.k, other.modState, other.txt, other.autor, other.c),
+ nScanCode(other.nScanCode), nVirtualKey(other.nVirtualKey), nModifiers(other.nModifiers)
+{
+ d = reinterpret_cast<QEventPrivate*>(this);
+}
+
+/*!
+ \internal
+*/
+QKeyEventEx::~QKeyEventEx()
+{
+}
+
+/*!
+ \fn int QKeyEvent::key() const
+
+ Returns the code of the key that was pressed or released.
+
+ See \l Qt::Key for the list of keyboard codes. These codes are
+ independent of the underlying window system. Note that this
+ function does not distinguish between capital and non-capital
+ letters, use the text() function (returning the Unicode text the
+ key generated) for this purpose.
+
+ A value of either 0 or Qt::Key_unknown means that the event is not
+ the result of a known key; for example, it may be the result of
+ a compose sequence, a keyboard macro, or due to key event
+ compression.
+
+ \sa Qt::WA_KeyCompression
+*/
+
+/*!
+ \fn QString QKeyEvent::text() const
+
+ Returns the Unicode text that this key generated. The text
+ returned can be an empty string in cases
+ where modifier keys, such as Shift, Control, Alt, and Meta,
+ are being pressed or released. In such cases key() will contain
+ a valid value.
+
+ \sa Qt::WA_KeyCompression
+*/
+
+/*!
+ Returns the keyboard modifier flags that existed immediately
+ after the event occurred.
+
+ \warning This function cannot always be trusted. The user can
+ confuse it by pressing both \key{Shift} keys simultaneously and
+ releasing one of them, for example.
+
+ \sa QApplication::keyboardModifiers()
+*/
+//###### We must check with XGetModifierMapping
+Qt::KeyboardModifiers QKeyEvent::modifiers() const
+{
+ if (key() == Qt::Key_Shift)
+ return Qt::KeyboardModifiers(QInputEvent::modifiers()^Qt::ShiftModifier);
+ if (key() == Qt::Key_Control)
+ return Qt::KeyboardModifiers(QInputEvent::modifiers()^Qt::ControlModifier);
+ if (key() == Qt::Key_Alt)
+ return Qt::KeyboardModifiers(QInputEvent::modifiers()^Qt::AltModifier);
+ if (key() == Qt::Key_Meta)
+ return Qt::KeyboardModifiers(QInputEvent::modifiers()^Qt::MetaModifier);
+ return QInputEvent::modifiers();
+}
+
+#ifndef QT_NO_SHORTCUT
+/*!
+ \fn bool QKeyEvent::matches(QKeySequence::StandardKey key) const
+ \since 4.2
+
+ Returns true if the key event matches the given standard \a key;
+ otherwise returns false.
+*/
+bool QKeyEvent::matches(QKeySequence::StandardKey matchKey) const
+{
+ uint searchkey = (modifiers() | key()) & ~(Qt::KeypadModifier); //The keypad modifier should not make a difference
+ uint platform = QApplicationPrivate::currentPlatform();
+
+ uint N = QKeySequencePrivate::numberOfKeyBindings;
+ int first = 0;
+ int last = N - 1;
+
+ while (first <= last) {
+ int mid = (first + last) / 2;
+ QKeyBinding midVal = QKeySequencePrivate::keyBindings[mid];
+
+ if (searchkey > midVal.shortcut){
+ first = mid + 1; // Search in top half
+ }
+ else if (searchkey < midVal.shortcut){
+ last = mid - 1; // Search in bottom half
+ }
+ else {
+ //found correct shortcut value, now we must check for platform match
+ if ((midVal.platform & platform) && (midVal.standardKey == matchKey)) {
+ return true;
+ } else { //We may have several equal values for different platforms, so we must search in both directions
+
+ //search forward
+ for ( unsigned int i = mid + 1 ; i < N - 1 ; ++i) {
+ QKeyBinding current = QKeySequencePrivate::keyBindings[i];
+ if (current.shortcut != searchkey)
+ break;
+ else if (current.platform & platform && current.standardKey == matchKey)
+ return true;
+ }
+
+ //search back
+ for ( int i = mid - 1 ; i >= 0 ; --i) {
+ QKeyBinding current = QKeySequencePrivate::keyBindings[i];
+ if (current.shortcut != searchkey)
+ break;
+ else if (current.platform & platform && current.standardKey == matchKey)
+ return true;
+ }
+ return false; //we could not find it among the matching keySequences
+ }
+ }
+ }
+ return false; //we could not find matching keySequences at all
+}
+#endif // QT_NO_SHORTCUT
+
+
+/*!
+ \fn bool QKeyEvent::isAutoRepeat() const
+
+ Returns true if this event comes from an auto-repeating key;
+ returns false if it comes from an initial key press.
+
+ Note that if the event is a multiple-key compressed event that is
+ partly due to auto-repeat, this function could return either true
+ or false indeterminately.
+*/
+
+/*!
+ \fn int QKeyEvent::count() const
+
+ Returns the number of keys involved in this event. If text()
+ is not empty, this is simply the length of the string.
+
+ \sa Qt::WA_KeyCompression
+*/
+
+#ifdef QT3_SUPPORT
+/*!
+ \fn QKeyEvent::QKeyEvent(Type type, int key, int ascii,
+ int modifiers, const QString &text,
+ bool autorep, ushort count)
+
+ Use one of the other constructors instead.
+*/
+
+/*!
+ \fn int QKeyEvent::ascii() const
+
+ Use text() instead.
+*/
+
+/*!
+ \fn Qt::ButtonState QKeyEvent::state() const
+
+ Use QInputEvent::modifiers() instead.
+*/
+
+/*!
+ \fn Qt::ButtonState QKeyEvent::stateAfter() const
+
+ Use modifiers() instead.
+*/
+#endif
+
+/*!
+ \class QFocusEvent
+ \brief The QFocusEvent class contains event parameters for widget focus
+ events.
+
+ \ingroup events
+
+ Focus events are sent to widgets when the keyboard input focus
+ changes. Focus events occur due to mouse actions, key presses
+ (such as \gui{Tab} or \gui{Backtab}), the window system, popup
+ menus, keyboard shortcuts, or other application-specific reasons.
+ The reason for a particular focus event is returned by reason()
+ in the appropriate event handler.
+
+ The event handlers QWidget::focusInEvent() and
+ QWidget::focusOutEvent() receive focus events.
+
+ \sa QWidget::setFocus(), QWidget::setFocusPolicy(), {Keyboard Focus}
+*/
+
+/*!
+ Constructs a focus event object.
+
+ The \a type parameter must be either QEvent::FocusIn or
+ QEvent::FocusOut. The \a reason describes the cause of the change
+ in focus.
+*/
+QFocusEvent::QFocusEvent(Type type, Qt::FocusReason reason)
+ : QEvent(type), m_reason(reason)
+{}
+
+/*!
+ \internal
+*/
+QFocusEvent::~QFocusEvent()
+{
+}
+
+// ### Qt 5: remove
+/*!
+ \internal
+ */
+Qt::FocusReason QFocusEvent::reason()
+{
+ return m_reason;
+}
+
+/*!
+ Returns the reason for this focus event.
+ */
+Qt::FocusReason QFocusEvent::reason() const
+{
+ return m_reason;
+}
+
+/*!
+ \fn bool QFocusEvent::gotFocus() const
+
+ Returns true if type() is QEvent::FocusIn; otherwise returns
+ false.
+*/
+
+/*!
+ \fn bool QFocusEvent::lostFocus() const
+
+ Returns true if type() is QEvent::FocusOut; otherwise returns
+ false.
+*/
+
+#ifdef QT3_SUPPORT
+/*!
+ \enum QFocusEvent::Reason
+ \compat
+
+ Use Qt::FocusReason instead.
+
+ \value Mouse Same as Qt::MouseFocusReason.
+ \value Tab Same as Qt::TabFocusReason.
+ \value Backtab Same as Qt::BacktabFocusReason.
+ \value MenuBar Same as Qt::MenuBarFocusReason.
+ \value ActiveWindow Same as Qt::ActiveWindowFocusReason
+ \value Other Same as Qt::OtherFocusReason
+ \value Popup Same as Qt::PopupFocusReason
+ \value Shortcut Same as Qt::ShortcutFocusReason
+*/
+#endif
+
+/*!
+ \class QPaintEvent
+ \brief The QPaintEvent class contains event parameters for paint events.
+
+ \ingroup events
+
+ Paint events are sent to widgets that need to update themselves,
+ for instance when part of a widget is exposed because a covering
+ widget was moved.
+
+ The event contains a region() that needs to be updated, and a
+ rect() that is the bounding rectangle of that region. Both are
+ provided because many widgets can't make much use of region(),
+ and rect() can be much faster than region().boundingRect().
+ Painting is clipped to region() during the processing of a paint
+ event.
+
+ \sa QPainter, QWidget::update(), QWidget::repaint(),
+ QWidget::paintEvent()
+*/
+
+/*!
+ \fn bool QPaintEvent::erased() const
+ \compat
+
+ Returns true if the paint event region (or rectangle) has been
+ erased with the widget's background; otherwise returns false.
+
+ Qt 4 \e always erases regions that require painting. The exception
+ to this rule is if the widget sets the Qt::WA_OpaquePaintEvent or
+ Qt::WA_NoSystemBackground attributes. If either one of those
+ attributes is set \e and the window system does not make use of
+ subwidget alpha composition (currently X11 and Windows, but this
+ may change), then the region is not erased.
+*/
+
+/*!
+ \fn void QPaintEvent::setErased(bool b) { m_erased = b; }
+ \internal
+*/
+
+/*!
+ Constructs a paint event object with the region that needs to
+ be updated. The region is specified by \a paintRegion.
+*/
+QPaintEvent::QPaintEvent(const QRegion& paintRegion)
+ : QEvent(Paint), m_rect(paintRegion.boundingRect()), m_region(paintRegion), m_erased(false)
+{}
+
+/*!
+ Constructs a paint event object with the rectangle that needs
+ to be updated. The region is specified by \a paintRect.
+*/
+QPaintEvent::QPaintEvent(const QRect &paintRect)
+ : QEvent(Paint), m_rect(paintRect),m_region(paintRect), m_erased(false)
+{}
+
+
+#ifdef QT3_SUPPORT
+ /*!
+ Constructs a paint event object with both a \a paintRegion and a
+ \a paintRect, both of which represent the area of the widget that
+ needs to be updated.
+
+*/
+QPaintEvent::QPaintEvent(const QRegion &paintRegion, const QRect &paintRect)
+ : QEvent(Paint), m_rect(paintRect), m_region(paintRegion), m_erased(false)
+{}
+#endif
+
+/*!
+ \internal
+*/
+QPaintEvent::~QPaintEvent()
+{
+}
+
+/*!
+ \fn const QRect &QPaintEvent::rect() const
+
+ Returns the rectangle that needs to be updated.
+
+ \sa region() QPainter::setClipRect()
+*/
+
+/*!
+ \fn const QRegion &QPaintEvent::region() const
+
+ Returns the region that needs to be updated.
+
+ \sa rect() QPainter::setClipRegion()
+*/
+
+
+QUpdateLaterEvent::QUpdateLaterEvent(const QRegion& paintRegion)
+ : QEvent(UpdateLater), m_region(paintRegion)
+{
+}
+
+QUpdateLaterEvent::~QUpdateLaterEvent()
+{
+}
+
+/*!
+ \class QMoveEvent
+ \brief The QMoveEvent class contains event parameters for move events.
+
+ \ingroup events
+
+ Move events are sent to widgets that have been moved to a new
+ position relative to their parent.
+
+ The event handler QWidget::moveEvent() receives move events.
+
+ \sa QWidget::move(), QWidget::setGeometry()
+*/
+
+/*!
+ Constructs a move event with the new and old widget positions,
+ \a pos and \a oldPos respectively.
+*/
+QMoveEvent::QMoveEvent(const QPoint &pos, const QPoint &oldPos)
+ : QEvent(Move), p(pos), oldp(oldPos)
+{}
+
+/*!
+ \internal
+*/
+QMoveEvent::~QMoveEvent()
+{
+}
+
+/*!
+ \fn const QPoint &QMoveEvent::pos() const
+
+ Returns the new position of the widget. This excludes the window
+ frame for top level widgets.
+*/
+
+/*!
+ \fn const QPoint &QMoveEvent::oldPos() const
+
+ Returns the old position of the widget.
+*/
+
+
+/*!
+ \class QResizeEvent
+ \brief The QResizeEvent class contains event parameters for resize events.
+
+ \ingroup events
+
+ Resize events are sent to widgets that have been resized.
+
+ The event handler QWidget::resizeEvent() receives resize events.
+
+ \sa QWidget::resize() QWidget::setGeometry()
+*/
+
+/*!
+ Constructs a resize event with the new and old widget sizes, \a
+ size and \a oldSize respectively.
+*/
+QResizeEvent::QResizeEvent(const QSize &size, const QSize &oldSize)
+ : QEvent(Resize), s(size), olds(oldSize)
+{}
+
+/*!
+ \internal
+*/
+QResizeEvent::~QResizeEvent()
+{
+}
+
+/*!
+ \fn const QSize &QResizeEvent::size() const
+
+ Returns the new size of the widget. This is the same as
+ QWidget::size().
+*/
+
+/*!
+ \fn const QSize &QResizeEvent::oldSize() const
+
+ Returns the old size of the widget.
+*/
+
+
+/*!
+ \class QCloseEvent
+ \brief The QCloseEvent class contains parameters that describe a close event.
+
+ \ingroup events
+
+ Close events are sent to widgets that the user wants to close,
+ usually by choosing "Close" from the window menu, or by clicking
+ the \gui{X} title bar button. They are also sent when you call
+ QWidget::close() to close a widget programmatically.
+
+ Close events contain a flag that indicates whether the receiver
+ wants the widget to be closed or not. When a widget accepts the
+ close event, it is hidden (and destroyed if it was created with
+ the Qt::WA_DeleteOnClose flag). If it refuses to accept the close
+ event nothing happens. (Under X11 it is possible that the window
+ manager will forcibly close the window; but at the time of writing
+ we are not aware of any window manager that does this.)
+
+ The event handler QWidget::closeEvent() receives close events. The
+ default implementation of this event handler accepts the close
+ event. If you do not want your widget to be hidden, or want some
+ special handing, you should reimplement the event handler and
+ ignore() the event.
+
+ The \l{mainwindows/application#close event handler}{closeEvent() in the
+ Application example} shows a close event handler that
+ asks whether to save a document before closing.
+
+ If you want the widget to be deleted when it is closed, create it
+ with the Qt::WA_DeleteOnClose flag. This is very useful for
+ independent top-level windows in a multi-window application.
+
+ \l{QObject}s emits the \l{QObject::destroyed()}{destroyed()}
+ signal when they are deleted.
+
+ If the last top-level window is closed, the
+ QApplication::lastWindowClosed() signal is emitted.
+
+ The isAccepted() function returns true if the event's receiver has
+ agreed to close the widget; call accept() to agree to close the
+ widget and call ignore() if the receiver of this event does not
+ want the widget to be closed.
+
+ \sa QWidget::close(), QWidget::hide(), QObject::destroyed(),
+ QCoreApplication::exec(), QCoreApplication::quit(),
+ QApplication::lastWindowClosed()
+*/
+
+/*!
+ Constructs a close event object.
+
+ \sa accept()
+*/
+QCloseEvent::QCloseEvent()
+ : QEvent(Close)
+{}
+
+/*! \internal
+*/
+QCloseEvent::~QCloseEvent()
+{
+}
+
+/*!
+ \class QIconDragEvent
+ \brief The QIconDragEvent class indicates that a main icon drag has begun.
+
+ \ingroup events
+
+ Icon drag events are sent to widgets when the main icon of a window
+ has been dragged away. On Mac OS X, this happens when the proxy
+ icon of a window is dragged off the title bar.
+
+ It is normal to begin using drag and drop in response to this
+ event.
+
+ \sa {Drag and Drop}, QMimeData, QDrag
+*/
+
+/*!
+ Constructs an icon drag event object with the accept flag set to
+ false.
+
+ \sa accept()
+*/
+QIconDragEvent::QIconDragEvent()
+ : QEvent(IconDrag)
+{ ignore(); }
+
+/*! \internal */
+QIconDragEvent::~QIconDragEvent()
+{
+}
+
+/*!
+ \class QContextMenuEvent
+ \brief The QContextMenuEvent class contains parameters that describe a context menu event.
+
+ \ingroup events
+
+ Context menu events are sent to widgets when a user performs
+ an action associated with opening a context menu.
+ The actions required to open context menus vary between platforms;
+ for example, on Windows, pressing the menu button or clicking the
+ right mouse button will cause this event to be sent.
+
+ When this event occurs it is customary to show a QMenu with a
+ context menu, if this is relevant to the context.
+
+ Context menu events contain a special accept flag that indicates
+ whether the receiver accepted the event. If the event handler does
+ not accept the event then, if possible, whatever triggered the event will be
+ handled as a regular input event.
+*/
+
+#ifndef QT_NO_CONTEXTMENU
+/*!
+ Constructs a context menu event object with the accept parameter
+ flag set to false.
+
+ The \a reason parameter must be QContextMenuEvent::Mouse or
+ QContextMenuEvent::Keyboard.
+
+ The \a pos parameter specifies the mouse position relative to the
+ receiving widget. \a globalPos is the mouse position in absolute
+ coordinates.
+*/
+QContextMenuEvent::QContextMenuEvent(Reason reason, const QPoint &pos, const QPoint &globalPos)
+ : QInputEvent(ContextMenu), p(pos), gp(globalPos), reas(reason)
+{}
+
+/*!
+ Constructs a context menu event object with the accept parameter
+ flag set to false.
+
+ The \a reason parameter must be QContextMenuEvent::Mouse or
+ QContextMenuEvent::Keyboard.
+
+ The \a pos parameter specifies the mouse position relative to the
+ receiving widget. \a globalPos is the mouse position in absolute
+ coordinates. The \a modifiers holds the keyboard modifiers.
+*/
+QContextMenuEvent::QContextMenuEvent(Reason reason, const QPoint &pos, const QPoint &globalPos,
+ Qt::KeyboardModifiers modifiers)
+ : QInputEvent(ContextMenu, modifiers), p(pos), gp(globalPos), reas(reason)
+{}
+
+#ifdef QT3_SUPPORT
+/*!
+ Constructs a context menu event with the given \a reason for the
+ position specified by \a pos in widget coordinates and \a globalPos
+ in global screen coordinates. \a dummy is ignored.
+*/
+QContextMenuEvent::QContextMenuEvent(Reason reason, const QPoint &pos, const QPoint &globalPos,
+ int /* dummy */)
+ : QInputEvent(ContextMenu), p(pos), gp(globalPos), reas(reason)
+{}
+#endif
+
+/*! \internal */
+QContextMenuEvent::~QContextMenuEvent()
+{
+}
+/*!
+ Constructs a context menu event object with the accept parameter
+ flag set to false.
+
+ The \a reason parameter must be QContextMenuEvent::Mouse or
+ QContextMenuEvent::Keyboard.
+
+ The \a pos parameter specifies the mouse position relative to the
+ receiving widget.
+
+ The globalPos() is initialized to QCursor::pos(), which may not be
+ appropriate. Use the other constructor to specify the global
+ position explicitly.
+*/
+QContextMenuEvent::QContextMenuEvent(Reason reason, const QPoint &pos)
+ : QInputEvent(ContextMenu), p(pos), reas(reason)
+{
+ gp = QCursor::pos();
+}
+
+#ifdef QT3_SUPPORT
+/*!
+ Constructs a context menu event with the given \a reason for the
+ position specified by \a pos in widget coordinates. \a dummy is
+ ignored.
+*/
+QContextMenuEvent::QContextMenuEvent(Reason reason, const QPoint &pos, int /* dummy */)
+ : QInputEvent(ContextMenu), p(pos), reas(reason)
+{
+ gp = QCursor::pos();
+}
+
+Qt::ButtonState QContextMenuEvent::state() const
+{
+ return Qt::ButtonState(int(QApplication::keyboardModifiers())|QApplication::mouseButtons());
+}
+#endif
+
+/*!
+ \fn const QPoint &QContextMenuEvent::pos() const
+
+ Returns the position of the mouse pointer relative to the widget
+ that received the event.
+
+ \sa x(), y(), globalPos()
+*/
+
+/*!
+ \fn int QContextMenuEvent::x() const
+
+ Returns the x position of the mouse pointer, relative to the
+ widget that received the event.
+
+ \sa y(), pos()
+*/
+
+/*!
+ \fn int QContextMenuEvent::y() const
+
+ Returns the y position of the mouse pointer, relative to the
+ widget that received the event.
+
+ \sa x(), pos()
+*/
+
+/*!
+ \fn const QPoint &QContextMenuEvent::globalPos() const
+
+ Returns the global position of the mouse pointer at the time of
+ the event.
+
+ \sa x(), y(), pos()
+*/
+
+/*!
+ \fn int QContextMenuEvent::globalX() const
+
+ Returns the global x position of the mouse pointer at the time of
+ the event.
+
+ \sa globalY(), globalPos()
+*/
+
+/*!
+ \fn int QContextMenuEvent::globalY() const
+
+ Returns the global y position of the mouse pointer at the time of
+ the event.
+
+ \sa globalX(), globalPos()
+*/
+#endif // QT_NO_CONTEXTMENU
+
+/*!
+ \fn Qt::ButtonState QContextMenuEvent::state() const
+
+ Returns the button state (a combination of mouse buttons
+ and keyboard modifiers) immediately before the event was
+ generated.
+
+ The returned value is a selection of the following values,
+ combined with the OR operator:
+ Qt::LeftButton, Qt::RightButton, Qt::MidButton,
+ Qt::ShiftButton, Qt::ControlButton, and Qt::AltButton.
+*/
+
+/*!
+ \enum QContextMenuEvent::Reason
+
+ This enum describes the reason why the event was sent.
+
+ \value Mouse The mouse caused the event to be sent. Normally this
+ means the right mouse button was clicked, but this is platform
+ dependent.
+
+ \value Keyboard The keyboard caused this event to be sent. On
+ Windows, this means the menu button was pressed.
+
+ \value Other The event was sent by some other means (i.e. not by
+ the mouse or keyboard).
+*/
+
+
+/*!
+ \fn QContextMenuEvent::Reason QContextMenuEvent::reason() const
+
+ Returns the reason for this context event.
+*/
+
+
+/*!
+ \class QInputMethodEvent
+ \brief The QInputMethodEvent class provides parameters for input method events.
+
+ \ingroup events
+
+ Input method events are sent to widgets when an input method is
+ used to enter text into a widget. Input methods are widely used
+ to enter text for languages with non-Latin alphabets.
+
+ Note that when creating custom text editing widgets, the
+ Qt::WA_InputMethodEnabled window attribute must be set explicitly
+ (using the QWidget::setAttribute() function) in order to receive
+ input method events.
+
+ The events are of interest to authors of keyboard entry widgets
+ who want to be able to correctly handle languages with complex
+ character input. Text input in such languages is usually a three
+ step process:
+
+ \list 1
+ \o \bold{Starting to Compose}
+
+ When the user presses the first key on a keyboard, an input
+ context is created. This input context will contain a string
+ of the typed characters.
+
+ \o \bold{Composing}
+
+ With every new key pressed, the input method will try to create a
+ matching string for the text typed so far called preedit
+ string. While the input context is active, the user can only move
+ the cursor inside the string belonging to this input context.
+
+ \o \bold{Completing}
+
+ At some point, the user will activate a user interface component
+ (perhaps using a particular key) where they can choose from a
+ number of strings matching the text they have typed so far. The
+ user can either confirm their choice cancel the input; in either
+ case the input context will be closed.
+ \endlist
+
+ QInputMethodEvent models these three stages, and transfers the
+ information needed to correctly render the intermediate result. A
+ QInputMethodEvent has two main parameters: preeditString() and
+ commitString(). The preeditString() parameter gives the currently
+ active preedit string. The commitString() parameter gives a text
+ that should get added to (or replace parts of) the text of the
+ editor widget. It usually is a result of the input operations and
+ has to be inserted to the widgets text directly before the preedit
+ string.
+
+ If the commitString() should replace parts of the of the text in
+ the editor, replacementLength() will contain the number of
+ characters to be replaced. replacementStart() contains the position
+ at which characters are to be replaced relative from the start of
+ the preedit string.
+
+ A number of attributes control the visual appearance of the
+ preedit string (the visual appearance of text outside the preedit
+ string is controlled by the widget only). The AttributeType enum
+ describes the different attributes that can be set.
+
+ A class implementing QWidget::inputMethodEvent() should at least
+ understand and honor the \l TextFormat and \l Cursor attributes.
+
+ Since input methods need to be able to query certain properties
+ from the widget, the widget must also implement
+ QWidget::inputMethodQuery().
+
+ When receiving an input method event, the text widget has to performs the
+ following steps:
+
+ \list 1
+ \o If the widget has selected text, the selected text should get
+ removed.
+
+ \o Remove the text starting at replacementStart() with length
+ replacementLength() and replace it by the commitString(). If
+ replacementLength() is 0, replacementStart() gives the insertion
+ position for the commitString().
+
+ When doing replacement the area of the preedit
+ string is ignored, thus a replacement starting at -1 with a length
+ of 2 will remove the last character before the preedit string and
+ the first character afterwards, and insert the commit string
+ directly before the preedit string.
+
+ If the widget implements undo/redo, this operation gets added to
+ the undo stack.
+
+ \o If there is no current preedit string, insert the
+ preeditString() at the current cursor position; otherwise replace
+ the previous preeditString with the one received from this event.
+
+ If the widget implements undo/redo, the preeditString() should not
+ influence the undo/redo stack in any way.
+
+ The widget should examine the list of attributes to apply to the
+ preedit string. It has to understand at least the TextFormat and
+ Cursor attributes and render them as specified.
+ \endlist
+
+ \sa QInputContext
+*/
+
+/*!
+ \enum QInputMethodEvent::AttributeType
+
+ \value TextFormat
+ A QTextCharFormat for the part of the preedit string specified by
+ start and length. value contains a QVariant of type QTextFormat
+ specifying rendering of this part of the preedit string. There
+ should be at most one format for every part of the preedit
+ string. If several are specified for any character in the string the
+ behaviour is undefined. A conforming implementation has to at least
+ honor the backgroundColor, textColor and fontUnderline properties
+ of the format.
+
+ \value Cursor If set, a cursor should be shown inside the preedit
+ string at position start. The length variable determines whether
+ the cursor is visible or not. If the length is 0 the cursor is
+ invisible. If value is a QVariant of type QColor this color will
+ be used for rendering the cursor, otherwise the color of the
+ surrounding text will be used. There should be at most one Cursor
+ attribute per event. If several are specified the behaviour is
+ undefined.
+
+ \value Language
+ The variant contains a QLocale object specifying the language of a
+ certain part of the preedit string. There should be at most one
+ language set for every part of the preedit string. If several are
+ specified for any character in the string the behaviour is undefined.
+
+ \value Ruby
+ The ruby text for a part of the preedit string. There should be at
+ most one ruby text set for every part of the preedit string. If
+ several are specified for any character in the string the behaviour
+ is undefined.
+
+ \sa Attribute
+*/
+
+/*!
+ \class QInputMethodEvent::Attribute
+ \brief The QInputMethodEvent::Attribute class stores an input method attribute.
+*/
+
+/*!
+ \fn QInputMethodEvent::Attribute::Attribute(AttributeType type, int start, int length, QVariant value)
+
+ Constructs an input method attribute. \a type specifies the type
+ of attribute, \a start and \a length the position of the
+ attribute, and \a value the value of the attribute.
+*/
+
+/*!
+ Constructs an event of type QEvent::InputMethod. The
+ attributes(), preeditString(), commitString(), replacementStart(),
+ and replacementLength() are initialized to default values.
+
+ \sa setCommitString()
+*/
+QInputMethodEvent::QInputMethodEvent()
+ : QEvent(QEvent::InputMethod), replace_from(0), replace_length(0)
+{
+}
+
+/*!
+ Construcs an event of type QEvent::InputMethod. The
+ preedit text is set to \a preeditText, the attributes to
+ \a attributes.
+
+ The commitString(), replacementStart(), and replacementLength()
+ values can be set using setCommitString().
+
+ \sa preeditString(), attributes()
+*/
+QInputMethodEvent::QInputMethodEvent(const QString &preeditText, const QList<Attribute> &attributes)
+ : QEvent(QEvent::InputMethod), preedit(preeditText), attrs(attributes),
+ replace_from(0), replace_length(0)
+{
+}
+
+/*!
+ Constructs a copy of \a other.
+*/
+QInputMethodEvent::QInputMethodEvent(const QInputMethodEvent &other)
+ : QEvent(QEvent::InputMethod), preedit(other.preedit), attrs(other.attrs),
+ commit(other.commit), replace_from(other.replace_from), replace_length(other.replace_length)
+{
+}
+
+/*!
+ Sets the commit string to \a commitString.
+
+ The commit string is the text that should get added to (or
+ replace parts of) the text of the editor widget. It usually is a
+ result of the input operations and has to be inserted to the
+ widgets text directly before the preedit string.
+
+ If the commit string should replace parts of the of the text in
+ the editor, \a replaceLength specifies the number of
+ characters to be replaced. \a replaceFrom specifies the position
+ at which characters are to be replaced relative from the start of
+ the preedit string.
+
+ \sa commitString(), replacementStart(), replacementLength()
+*/
+void QInputMethodEvent::setCommitString(const QString &commitString, int replaceFrom, int replaceLength)
+{
+ commit = commitString;
+ replace_from = replaceFrom;
+ replace_length = replaceLength;
+}
+
+/*!
+ \fn const QList<Attribute> &QInputMethodEvent::attributes() const
+
+ Returns the list of attributes passed to the QInputMethodEvent
+ constructor. The attributes control the visual appearance of the
+ preedit string (the visual appearance of text outside the preedit
+ string is controlled by the widget only).
+
+ \sa preeditString(), Attribute
+*/
+
+/*!
+ \fn const QString &QInputMethodEvent::preeditString() const
+
+ Returns the preedit text, i.e. the text before the user started
+ editing it.
+
+ \sa commitString(), attributes()
+*/
+
+/*!
+ \fn const QString &QInputMethodEvent::commitString() const
+
+ Returns the text that should get added to (or replace parts of)
+ the text of the editor widget. It usually is a result of the
+ input operations and has to be inserted to the widgets text
+ directly before the preedit string.
+
+ \sa setCommitString(), preeditString(), replacementStart(), replacementLength()
+*/
+
+/*!
+ \fn int QInputMethodEvent::replacementStart() const
+
+ Returns the position at which characters are to be replaced relative
+ from the start of the preedit string.
+
+ \sa replacementLength(), setCommitString()
+*/
+
+/*!
+ \fn int QInputMethodEvent::replacementLength() const
+
+ Returns the number of characters to be replaced in the preedit
+ string.
+
+ \sa replacementStart(), setCommitString()
+*/
+
+#ifndef QT_NO_TABLETEVENT
+
+/*!
+ \class QTabletEvent
+ \brief The QTabletEvent class contains parameters that describe a Tablet event.
+
+ \ingroup events
+
+ Tablet Events are generated from a Wacom tablet. Most of the time you will
+ want to deal with events from the tablet as if they were events from a
+ mouse; for example, you would retrieve the cursor position with x(), y(),
+ pos(), globalX(), globalY(), and globalPos(). In some situations you may
+ wish to retrieve the extra information provided by the tablet device
+ driver; for example, you might want to do subpixeling with higher
+ resolution coordinates or you may want to adjust color brightness based on
+ pressure. QTabletEvent allows you to read the pressure(), the xTilt(), and
+ yTilt(), as well as the type of device being used with device() (see
+ \l{TabletDevice}). It can also give you the minimum and maximum values for
+ each device's pressure and high resolution coordinates.
+
+ A tablet event contains a special accept flag that indicates whether the
+ receiver wants the event. You should call QTabletEvent::accept() if you
+ handle the tablet event; otherwise it will be sent to the parent widget.
+ The exception are TabletEnterProximity and TabletLeaveProximity events,
+ these are only sent to QApplication and don't check whether or not they are
+ accepted.
+
+ The QWidget::setEnabled() function can be used to enable or
+ disable mouse and keyboard events for a widget.
+
+ The event handler QWidget::tabletEvent() receives all three types of
+ tablet events. Qt will first send a tabletEvent then, if it is not
+ accepted, it will send a mouse event. This allows applications that
+ don't utilize tablets to use a tablet like a mouse, while also
+ enabling those who want to use both tablets and mouses differently.
+
+ \section1 Notes for X11 Users
+
+ Qt uses the following hard-coded names to identify tablet
+ devices from the xorg.conf file on X11 (apart from IRIX):
+ 'stylus', 'pen', and 'eraser'. If the devices have other names,
+ they will not be picked up Qt.
+*/
+
+/*!
+ \enum QTabletEvent::TabletDevice
+
+ This enum defines what type of device is generating the event.
+
+ \value NoDevice No device, or an unknown device.
+ \value Puck A Puck (a device that is similar to a flat mouse with
+ a transparent circle with cross-hairs).
+ \value Stylus A Stylus.
+ \value Airbrush An airbrush
+ \value FourDMouse A 4D Mouse.
+ \value RotationStylus A special stylus that also knows about rotation
+ (a 6D stylus). \since 4.1
+ \omitvalue XFreeEraser
+*/
+
+/*!
+ \enum QTabletEvent::PointerType
+
+ This enum defines what type of point is generating the event.
+
+ \value UnknownPointer An unknown device.
+ \value Pen Tip end of a stylus-like device (the narrow end of the pen).
+ \value Cursor Any puck-like device.
+ \value Eraser Eraser end of a stylus-like device (the broad end of the pen).
+
+ \sa pointerType()
+*/
+
+/*!
+ Construct a tablet event of the given \a type.
+
+ The \a pos parameter indicates where the event occurred in the
+ widget; \a globalPos is the corresponding position in absolute
+ coordinates. The \a hiResGlobalPos contains a high resolution
+ measurement of the position.
+
+ \a pressure contains the pressure exerted on the \a device.
+
+ \a pointerType describes the type of pen that is being used.
+
+ \a xTilt and \a yTilt contain the device's degree of tilt from the
+ x and y axes respectively.
+
+ \a keyState specifies which keyboard modifiers are pressed (e.g.,
+ \key{Ctrl}).
+
+ The \a uniqueID parameter contains the unique ID for the current device.
+
+ The \a z parameter contains the coordinate of the device on the tablet, this
+ is usually given by a wheel on 4D mouse. If the device does not support a
+ Z-axis, pass zero here.
+
+ The \a tangentialPressure paramater contins the tangential pressure of an air
+ brush. If the device does not support tangential pressure, pass 0 here.
+
+ \a rotation contains the device's rotation in degrees. 4D mice support
+ rotation. If the device does not support rotation, pass 0 here.
+
+ \sa pos() globalPos() device() pressure() xTilt() yTilt() uniqueId(), rotation(), tangentialPressure(), z()
+*/
+
+QTabletEvent::QTabletEvent(Type type, const QPoint &pos, const QPoint &globalPos,
+ const QPointF &hiResGlobalPos, int device, int pointerType,
+ qreal pressure, int xTilt, int yTilt, qreal tangentialPressure,
+ qreal rotation, int z, Qt::KeyboardModifiers keyState, qint64 uniqueID)
+ : QInputEvent(type, keyState),
+ mPos(pos),
+ mGPos(globalPos),
+ mHiResGlobalPos(hiResGlobalPos),
+ mDev(device),
+ mPointerType(pointerType),
+ mXT(xTilt),
+ mYT(yTilt),
+ mZ(z),
+ mPress(pressure),
+ mTangential(tangentialPressure),
+ mRot(rotation),
+ mUnique(uniqueID),
+ mExtra(0)
+{
+}
+
+/*!
+ \internal
+*/
+QTabletEvent::~QTabletEvent()
+{
+}
+
+/*!
+ \fn TabletDevices QTabletEvent::device() const
+
+ Returns the type of device that generated the event.
+
+ \sa TabletDevice
+*/
+
+/*!
+ \fn PointerType QTabletEvent::pointerType() const
+
+ Returns the type of point that generated the event.
+*/
+
+/*!
+ \fn qreal QTabletEvent::tangentialPressure() const
+
+ Returns the tangential pressure for the device. This is typically given by a finger
+ wheel on an airbrush tool. The range is from -1.0 to 1.0. 0.0 indicates a
+ neutral position. Current airbrushes can only move in the positive
+ direction from the neutrual position. If the device does not support
+ tangential pressure, this value is always 0.0.
+
+ \sa pressure()
+*/
+
+/*!
+ \fn qreal QTabletEvent::rotation() const
+
+ Returns the rotation of the current device in degress. This is usually
+ given by a 4D Mouse. If the device doesn't support rotation this value is
+ always 0.0.
+
+*/
+
+/*!
+ \fn qreal QTabletEvent::pressure() const
+
+ Returns the pressure for the device. 0.0 indicates that the stylus is not
+ on the tablet, 1.0 indicates the maximum amount of pressure for the stylus.
+
+ \sa tangentialPressure()
+*/
+
+/*!
+ \fn int QTabletEvent::xTilt() const
+
+ Returns the angle between the device (a pen, for example) and the
+ perpendicular in the direction of the x axis.
+ Positive values are towards the tablet's physical right. The angle
+ is in the range -60 to +60 degrees.
+
+ \img qtabletevent-tilt.png
+
+ \sa yTilt()
+*/
+
+/*!
+ \fn int QTabletEvent::yTilt() const
+
+ Returns the angle between the device (a pen, for example) and the
+ perpendicular in the direction of the y axis.
+ Positive values are towards the bottom of the tablet. The angle is
+ within the range -60 to +60 degrees.
+
+ \sa xTilt()
+*/
+
+/*!
+ \fn const QPoint &QTabletEvent::pos() const
+
+ Returns the position of the device, relative to the widget that
+ received the event.
+
+ If you move widgets around in response to mouse events, use
+ globalPos() instead of this function.
+
+ \sa x() y() globalPos()
+*/
+
+/*!
+ \fn int QTabletEvent::x() const
+
+ Returns the x position of the device, relative to the widget that
+ received the event.
+
+ \sa y() pos()
+*/
+
+/*!
+ \fn int QTabletEvent::y() const
+
+ Returns the y position of the device, relative to the widget that
+ received the event.
+
+ \sa x() pos()
+*/
+
+/*!
+ \fn int QTabletEvent::z() const
+
+ Returns the z position of the device. Typically this is represented by a
+ wheel on a 4D Mouse. If the device does not support a Z-axis, this value is
+ always zero. This is \bold not the same as pressure.
+
+ \sa pressure()
+*/
+
+/*!
+ \fn const QPoint &QTabletEvent::globalPos() const
+
+ Returns the global position of the device \e{at the time of the
+ event}. This is important on asynchronous windows systems like X11;
+ whenever you move your widgets around in response to mouse events,
+ globalPos() can differ significantly from the current position
+ QCursor::pos().
+
+ \sa globalX() globalY() hiResGlobalPos()
+*/
+
+/*!
+ \fn int QTabletEvent::globalX() const
+
+ Returns the global x position of the mouse pointer at the time of
+ the event.
+
+ \sa globalY() globalPos() hiResGlobalX()
+*/
+
+/*!
+ \fn int QTabletEvent::globalY() const
+
+ Returns the global y position of the tablet device at the time of
+ the event.
+
+ \sa globalX() globalPos() hiResGlobalY()
+*/
+
+/*!
+ \fn qint64 QTabletEvent::uniqueId() const
+
+ Returns a unique ID for the current device, making it possible
+ to differentiate between multiple devices being used at the same
+ time on the tablet.
+
+ Support of this feature is dependent on the tablet.
+
+ Values for the same device may vary from OS to OS.
+
+ Later versions of the Wacom driver for Linux will now report
+ the ID information. If you have a tablet that supports unique ID
+ and are not getting the information on Linux, consider upgrading
+ your driver.
+
+ As of Qt 4.2, the unique ID is the same regardless of the orientation
+ of the pen. Earlier versions would report a different value when using
+ the eraser-end versus the pen-end of the stylus on some OS's.
+
+ \sa pointerType()
+*/
+
+/*!
+ \fn const QPointF &QTabletEvent::hiResGlobalPos() const
+
+ The high precision coordinates delivered from the tablet expressed.
+ Sub pixeling information is in the fractional part of the QPointF.
+
+ \sa globalPos() hiResGlobalX() hiResGlobalY()
+*/
+
+/*!
+ \fn qreal &QTabletEvent::hiResGlobalX() const
+
+ The high precision x position of the tablet device.
+*/
+
+/*!
+ \fn qreal &QTabletEvent::hiResGlobalY() const
+
+ The high precision y position of the tablet device.
+*/
+
+#endif // QT_NO_TABLETEVENT
+
+#ifndef QT_NO_DRAGANDDROP
+/*!
+ Creates a QDragMoveEvent of the required \a type indicating
+ that the mouse is at position \a pos given within a widget.
+
+ The mouse and keyboard states are specified by \a buttons and
+ \a modifiers, and the \a actions describe the types of drag
+ and drop operation that are possible.
+ The drag data is passed as MIME-encoded information in \a data.
+
+ \warning Do not attempt to create a QDragMoveEvent yourself.
+ These objects rely on Qt's internal state.
+*/
+QDragMoveEvent::QDragMoveEvent(const QPoint& pos, Qt::DropActions actions, const QMimeData *data,
+ Qt::MouseButtons buttons, Qt::KeyboardModifiers modifiers, Type type)
+ : QDropEvent(pos, actions, data, buttons, modifiers, type)
+ , rect(pos, QSize(1, 1))
+{}
+
+/*!
+ Destroys the event.
+*/
+QDragMoveEvent::~QDragMoveEvent()
+{
+}
+
+/*!
+ \fn void QDragMoveEvent::accept(bool y)
+
+ Calls setAccepted(\a y) instead.
+*/
+
+/*!
+ \fn void QDragMoveEvent::accept(const QRect &rectangle)
+
+ The same as accept(), but also notifies that future moves will
+ also be acceptable if they remain within the \a rectangle
+ given on the widget. This can improve performance, but may
+ also be ignored by the underlying system.
+
+ If the rectangle is empty, drag move events will be sent
+ continuously. This is useful if the source is scrolling in a
+ timer event.
+*/
+
+/*!
+ \fn void QDragMoveEvent::accept()
+
+ \overload
+
+ Calls QDropEvent::accept().
+*/
+
+/*!
+ \fn void QDragMoveEvent::ignore()
+
+ \overload
+
+ Calls QDropEvent::ignore().
+*/
+
+/*!
+ \fn void QDragMoveEvent::ignore(const QRect &rectangle)
+
+ The opposite of the accept(const QRect&) function.
+ Moves within the \a rectangle are not acceptable, and will be
+ ignored.
+*/
+
+/*!
+ \fn QRect QDragMoveEvent::answerRect() const
+
+ Returns the rectangle in the widget where the drop will occur if accepted.
+ You can use this information to restrict drops to certain places on the
+ widget.
+*/
+
+
+/*!
+ \class QDropEvent
+ \ingroup events
+ \ingroup draganddrop
+
+ \brief The QDropEvent class provides an event which is sent when a
+ drag and drop action is completed.
+
+ When a widget \l{QWidget::setAcceptDrops()}{accepts drop events}, it will
+ receive this event if it has accepted the most recent QDragEnterEvent or
+ QDragMoveEvent sent to it.
+
+ The drop event contains a proposed action, available from proposedAction(), for
+ the widget to either accept or ignore. If the action can be handled by the
+ widget, you should call the acceptProposedAction() function. Since the
+ proposed action can be a combination of \l Qt::DropAction values, it may be
+ useful to either select one of these values as a default action or ask
+ the user to select their preferred action.
+
+ If the proposed drop action is not suitable, perhaps because your custom
+ widget does not support that action, you can replace it with any of the
+ \l{possibleActions()}{possible drop actions} by calling setDropAction()
+ with your preferred action. If you set a value that is not present in the
+ bitwise OR combination of values returned by possibleActions(), the default
+ copy action will be used. Once a replacement drop action has been set, call
+ accept() instead of acceptProposedAction() to complete the drop operation.
+
+ The mimeData() function provides the data dropped on the widget in a QMimeData
+ object. This contains information about the MIME type of the data in addition to
+ the data itself.
+
+ \sa QMimeData, QDrag, {Drag and Drop}
+*/
+
+/*!
+ \fn const QMimeData *QDropEvent::mimeData() const
+
+ Returns the data that was dropped on the widget and its associated MIME
+ type information.
+*/
+
+/*!
+ Constructs a drop event of a certain \a type corresponding to a
+ drop at the point specified by \a pos in the destination widget's
+ coordinate system.
+
+ The \a actions indicate which types of drag and drop operation can
+ be performed, and the drag data is stored as MIME-encoded data in \a data.
+
+ The states of the mouse buttons and keyboard modifiers at the time of
+ the drop are specified by \a buttons and \a modifiers.
+*/ // ### pos is in which coordinate system?
+QDropEvent::QDropEvent(const QPoint& pos, Qt::DropActions actions, const QMimeData *data,
+ Qt::MouseButtons buttons, Qt::KeyboardModifiers modifiers, Type type)
+ : QEvent(type), p(pos), mouseState(buttons),
+ modState(modifiers), act(actions),
+ mdata(data)
+{
+ default_action = QDragManager::self()->defaultAction(act, modifiers);
+ drop_action = default_action;
+ ignore();
+}
+
+/*! \internal */
+QDropEvent::~QDropEvent()
+{
+}
+
+/*!
+ \compat
+ Returns a byte array containing the drag's data, in \a format.
+
+ data() normally needs to get the data from the drag source, which
+ is potentially very slow, so it's advisable to call this function
+ only if you're sure that you will need the data in that
+ particular \a format.
+
+ The resulting data will have a size of 0 if the format was not
+ available.
+
+ \sa format() QByteArray::size()
+*/
+
+QByteArray QDropEvent::encodedData(const char *format) const
+{
+ return mdata->data(QLatin1String(format));
+}
+
+/*!
+ \compat
+ Returns a string describing one of the available data types for
+ this drag. Common examples are "text/plain" and "image/gif".
+ If \a n is less than zero or greater than the number of available
+ data types, format() returns 0.
+
+ This function is provided mainly for debugging. Most drop targets
+ will use provides().
+
+ \sa data() provides()
+*/
+
+const char* QDropEvent::format(int n) const
+{
+ if (fmts.isEmpty()) {
+ QStringList formats = mdata->formats();
+ for (int i = 0; i < formats.size(); ++i)
+ fmts.append(formats.at(i).toLatin1());
+ }
+ if (n < 0 || n >= fmts.size())
+ return 0;
+ return fmts.at(n).constData();
+}
+
+/*!
+ \compat
+ Returns true if this event provides format \a mimeType; otherwise
+ returns false.
+
+ \sa data()
+*/
+
+bool QDropEvent::provides(const char *mimeType) const
+{
+ return mdata->formats().contains(QLatin1String(mimeType));
+}
+
+/*!
+ If the source of the drag operation is a widget in this
+ application, this function returns that source; otherwise it
+ returns 0. The source of the operation is the first parameter to
+ the QDrag object used instantiate the drag.
+
+ This is useful if your widget needs special behavior when dragging
+ to itself.
+
+ \sa QDrag::QDrag()
+*/
+QWidget* QDropEvent::source() const
+{
+ QDragManager *manager = QDragManager::self();
+ return manager ? manager->source() : 0;
+}
+
+
+void QDropEvent::setDropAction(Qt::DropAction action)
+{
+ if (!(action & act) && action != Qt::IgnoreAction)
+ action = default_action;
+ drop_action = action;
+}
+
+/*!
+ \fn const QPoint& QDropEvent::pos() const
+
+ Returns the position where the drop was made.
+*/
+
+/*!
+ \fn Qt::MouseButtons QDropEvent::mouseButtons() const
+
+ Returns the mouse buttons that are pressed..
+*/
+
+/*!
+ \fn Qt::KeyboardModifiers QDropEvent::keyboardModifiers() const
+
+ Returns the modifier keys that are pressed.
+*/
+
+/*!
+ \fn void QDropEvent::accept()
+ \internal
+*/
+
+/*!
+ \fn void QDropEvent::accept(bool accept)
+
+ Call setAccepted(\a accept) instead.
+*/
+
+/*!
+ \fn void QDropEvent::acceptAction(bool accept = true)
+
+ Call this to indicate that the action described by action() is
+ accepted (i.e. if \a accept is true, which is the default), not merely
+ the default copy action. If you call acceptAction(true), there is
+ no need to also call accept(true).
+*/
+
+/*!
+ \enum QDropEvent::Action
+ \compat
+
+ When a drag and drop action is completed, the target is expected
+ to perform an action on the data provided by the source. This
+ will be one of the following:
+
+ \value Copy The default action. The source simply uses the data
+ provided in the operation.
+ \value Link The source should somehow create a link to the
+ location specified by the data.
+ \value Move The source should somehow move the object from the
+ location specified by the data to a new location.
+ \value Private The target has special knowledge of the MIME type,
+ which the source should respond to in a similar way to
+ a Copy.
+ \value UserAction The source and target can co-operate using
+ special actions. This feature is not currently
+ supported.
+
+ The Link and Move actions only makes sense if the data is a
+ reference, for example, text/uri-list file lists (see QUriDrag).
+*/
+
+/*!
+ \fn void QDropEvent::setDropAction(Qt::DropAction action)
+
+ Sets the \a action to be performed on the data by the target.
+ Use this to override the \l{proposedAction()}{proposed action}
+ with one of the \l{possibleActions()}{possible actions}.
+
+ If you set a drop action that is not one of the possible actions, the
+ drag and drop operation will default to a copy operation.
+
+ Once you have supplied a replacement drop action, call accept()
+ instead of acceptProposedAction().
+
+ \sa dropAction()
+*/
+
+/*!
+ \fn Qt::DropAction QDropEvent::dropAction() const
+
+ Returns the action to be performed on the data by the target. This may be
+ different from the action supplied in proposedAction() if you have called
+ setDropAction() to explicitly choose a drop action.
+
+ \sa setDropAction()
+*/
+
+/*!
+ \fn Qt::DropActions QDropEvent::possibleActions() const
+
+ Returns an OR-combination of possible drop actions.
+
+ \sa dropAction()
+*/
+
+/*!
+ \fn Qt::DropAction QDropEvent::proposedAction() const
+
+ Returns the proposed drop action.
+
+ \sa dropAction()
+*/
+
+/*!
+ \fn void QDropEvent::acceptProposedAction()
+
+ Sets the drop action to be the proposed action.
+
+ \sa setDropAction(), proposedAction(), {QEvent::accept()}{accept()}
+*/
+
+#ifdef QT3_SUPPORT
+/*!
+ Use dropAction() instead.
+
+ The table below shows the correspondance between the return type
+ of action() and the return type of dropAction().
+
+ \table
+ \header \i Old enum value \i New enum value
+ \row \i QDropEvent::Copy \i Qt::CopyAction
+ \row \i QDropEvent::Move \i Qt::MoveAction
+ \row \i QDropEvent::Link \i Qt::LinkAction
+ \row \i other \i Qt::CopyAction
+ \endtable
+*/
+
+QT3_SUPPORT QDropEvent::Action QDropEvent::action() const
+{
+ switch(drop_action) {
+ case Qt::CopyAction:
+ return Copy;
+ case Qt::MoveAction:
+ return Move;
+ case Qt::LinkAction:
+ return Link;
+ default:
+ return Copy;
+ }
+}
+#endif
+
+/*!
+ \fn void QDropEvent::setPoint(const QPoint &point)
+ \compat
+
+ Sets the drop to happen at the given \a point. You do not normally
+ need to use this as it will be set internally before your widget
+ receives the drop event.
+*/ // ### here too - what coordinate system?
+
+
+/*!
+ \class QDragEnterEvent
+ \brief The QDragEnterEvent class provides an event which is sent
+ to a widget when a drag and drop action enters it.
+
+ \ingroup events
+ \ingroup draganddrop
+
+ A widget must accept this event in order to receive the \l
+ {QDragMoveEvent}{drag move events} that are sent while the drag
+ and drop action is in progress. The drag enter event is always
+ immediately followed by a drag move event.
+
+ QDragEnterEvent inherits most of its functionality from
+ QDragMoveEvent, which in turn inherits most of its functionality
+ from QDropEvent.
+
+ \sa QDragLeaveEvent, QDragMoveEvent, QDropEvent
+*/
+
+/*!
+ Constructs a QDragEnterEvent that represents a drag entering a
+ widget at the given \a point with mouse and keyboard states specified by
+ \a buttons and \a modifiers.
+
+ The drag data is passed as MIME-encoded information in \a data, and the
+ specified \a actions describe the possible types of drag and drop
+ operation that can be performed.
+
+ \warning Do not create a QDragEnterEvent yourself since these
+ objects rely on Qt's internal state.
+*/
+QDragEnterEvent::QDragEnterEvent(const QPoint& point, Qt::DropActions actions, const QMimeData *data,
+ Qt::MouseButtons buttons, Qt::KeyboardModifiers modifiers)
+ : QDragMoveEvent(point, actions, data, buttons, modifiers, DragEnter)
+{}
+
+/*! \internal
+*/
+QDragEnterEvent::~QDragEnterEvent()
+{
+}
+
+/*!
+ Constructs a drag response event containing the \a accepted value,
+ indicating whether the drag and drop operation was accepted by the
+ recipient.
+*/
+QDragResponseEvent::QDragResponseEvent(bool accepted)
+ : QEvent(DragResponse), a(accepted)
+{}
+
+/*! \internal
+*/
+QDragResponseEvent::~QDragResponseEvent()
+{
+}
+
+/*!
+ \class QDragMoveEvent
+ \brief The QDragMoveEvent class provides an event which is sent while a drag and drop action is in progress.
+
+ \ingroup events
+ \ingroup draganddrop
+
+ A widget will receive drag move events repeatedly while the drag
+ is within its boundaries, if it accepts
+ \l{QWidget::setAcceptDrops()}{drop events} and \l
+ {QWidget::dragEnterEvent()}{enter events}. The widget should
+ examine the event to see what kind of data it
+ \l{QDragMoveEvent::provides()}{provides}, and call the accept()
+ function to accept the drop if appropriate.
+
+ The rectangle supplied by the answerRect() function can be used to restrict
+ drops to certain parts of the widget. For example, we can check whether the
+ rectangle intersects with the geometry of a certain child widget and only
+ call \l{QDropEvent::acceptProposedAction()}{acceptProposedAction()} if that
+ is the case.
+
+ Note that this class inherits most of its functionality from
+ QDropEvent.
+
+ \sa QDragEnterEvent, QDragLeaveEvent, QDropEvent
+*/
+
+/*!
+ \class QDragLeaveEvent
+ \brief The QDragLeaveEvent class provides an event that is sent to a widget when a drag and drop action leaves it.
+
+ \ingroup events
+ \ingroup draganddrop
+
+ This event is always preceded by a QDragEnterEvent and a series
+ of \l{QDragMoveEvent}s. It is not sent if a QDropEvent is sent
+ instead.
+
+ \sa QDragEnterEvent, QDragMoveEvent, QDropEvent
+*/
+
+/*!
+ Constructs a QDragLeaveEvent.
+
+ \warning Do not create a QDragLeaveEvent yourself since these
+ objects rely on Qt's internal state.
+*/
+QDragLeaveEvent::QDragLeaveEvent()
+ : QEvent(DragLeave)
+{}
+
+/*! \internal
+*/
+QDragLeaveEvent::~QDragLeaveEvent()
+{
+}
+#endif // QT_NO_DRAGANDDROP
+
+/*!
+ \class QHelpEvent
+ \brief The QHelpEvent class provides an event that is used to request helpful information
+ about a particular point in a widget.
+
+ \ingroup events
+ \ingroup helpsystem
+
+ This event can be intercepted in applications to provide tooltips
+ or "What's This?" help for custom widgets. The type() can be
+ either QEvent::ToolTip or QEvent::WhatsThis.
+
+ \sa QToolTip, QWhatsThis, QStatusTipEvent, QWhatsThisClickedEvent
+*/
+
+/*!
+ Constructs a help event with the given \a type corresponding to the
+ widget-relative position specified by \a pos and the global position
+ specified by \a globalPos.
+
+ \a type must be either QEvent::ToolTip or QEvent::WhatsThis.
+
+ \sa pos(), globalPos()
+*/
+QHelpEvent::QHelpEvent(Type type, const QPoint &pos, const QPoint &globalPos)
+ : QEvent(type), p(pos), gp(globalPos)
+{}
+
+/*!
+ \fn int QHelpEvent::x() const
+
+ Same as pos().x().
+
+ \sa y(), pos(), globalPos()
+*/
+
+/*!
+ \fn int QHelpEvent::y() const
+
+ Same as pos().y().
+
+ \sa x(), pos(), globalPos()
+*/
+
+/*!
+ \fn int QHelpEvent::globalX() const
+
+ Same as globalPos().x().
+
+ \sa x(), globalY(), globalPos()
+*/
+
+/*!
+ \fn int QHelpEvent::globalY() const
+
+ Same as globalPos().y().
+
+ \sa y(), globalX(), globalPos()
+*/
+
+/*!
+ \fn const QPoint &QHelpEvent::pos() const
+
+ Returns the mouse cursor position when the event was generated,
+ relative to the widget to which the event is dispatched.
+
+ \sa globalPos(), x(), y()
+*/
+
+/*!
+ \fn const QPoint &QHelpEvent::globalPos() const
+
+ Returns the mouse cursor position when the event was generated
+ in global coordinates.
+
+ \sa pos(), globalX(), globalY()
+*/
+
+/*! \internal
+*/
+QHelpEvent::~QHelpEvent()
+{
+}
+
+#ifndef QT_NO_STATUSTIP
+
+/*!
+ \class QStatusTipEvent
+ \brief The QStatusTipEvent class provides an event that is used to show messages in a status bar.
+
+ \ingroup events
+ \ingroup helpsystem
+
+ Status tips can be set on a widget using the
+ QWidget::setStatusTip() function. They are shown in the status
+ bar when the mouse cursor enters the widget. For example:
+
+ \table 100%
+ \row
+ \o
+ \snippet doc/src/snippets/qstatustipevent/main.cpp 1
+ \dots
+ \snippet doc/src/snippets/qstatustipevent/main.cpp 3
+ \o
+ \image qstatustipevent-widget.png Widget with status tip.
+ \endtable
+
+ Status tips can also be set on actions using the
+ QAction::setStatusTip() function:
+
+ \table 100%
+ \row
+ \o
+ \snippet doc/src/snippets/qstatustipevent/main.cpp 0
+ \snippet doc/src/snippets/qstatustipevent/main.cpp 2
+ \dots
+ \snippet doc/src/snippets/qstatustipevent/main.cpp 3
+ \o
+ \image qstatustipevent-action.png Action with status tip.
+ \endtable
+
+ Finally, status tips are supported for the item view classes
+ through the Qt::StatusTipRole enum value.
+
+ \sa QStatusBar, QHelpEvent, QWhatsThisClickedEvent
+*/
+
+/*!
+ Constructs a status tip event with the text specified by \a tip.
+
+ \sa tip()
+*/
+QStatusTipEvent::QStatusTipEvent(const QString &tip)
+ : QEvent(StatusTip), s(tip)
+{}
+
+/*! \internal
+*/
+QStatusTipEvent::~QStatusTipEvent()
+{
+}
+
+/*!
+ \fn QString QStatusTipEvent::tip() const
+
+ Returns the message to show in the status bar.
+
+ \sa QStatusBar::showMessage()
+*/
+
+#endif // QT_NO_STATUSTIP
+
+#ifndef QT_NO_WHATSTHIS
+
+/*!
+ \class QWhatsThisClickedEvent
+ \brief The QWhatsThisClickedEvent class provides an event that
+ can be used to handle hyperlinks in a "What's This?" text.
+
+ \ingroup events
+ \ingroup helpsystem
+
+ \sa QWhatsThis, QHelpEvent, QStatusTipEvent
+*/
+
+/*!
+ Constructs an event containing a URL specified by \a href when a link
+ is clicked in a "What's This?" message.
+
+ \sa href()
+*/
+QWhatsThisClickedEvent::QWhatsThisClickedEvent(const QString &href)
+ : QEvent(WhatsThisClicked), s(href)
+{}
+
+/*! \internal
+*/
+QWhatsThisClickedEvent::~QWhatsThisClickedEvent()
+{
+}
+
+/*!
+ \fn QString QWhatsThisClickedEvent::href() const
+
+ Returns the URL that was clicked by the user in the "What's
+ This?" text.
+*/
+
+#endif // QT_NO_WHATSTHIS
+
+#ifndef QT_NO_ACTION
+
+/*!
+ \class QActionEvent
+ \brief The QActionEvent class provides an event that is generated
+ when a QAction is added, removed, or changed.
+
+ \ingroup events
+
+ Actions can be added to widgets using QWidget::addAction(). This
+ generates an \l ActionAdded event, which you can handle to provide
+ custom behavior. For example, QToolBar reimplements
+ QWidget::actionEvent() to create \l{QToolButton}s for the
+ actions.
+
+ \sa QAction, QWidget::addAction(), QWidget::removeAction(), QWidget::actions()
+*/
+
+/*!
+ Constructs an action event. The \a type can be \l ActionChanged,
+ \l ActionAdded, or \l ActionRemoved.
+
+ \a action is the action that is changed, added, or removed. If \a
+ type is ActionAdded, the action is to be inserted before the
+ action \a before. If \a before is 0, the action is appended.
+*/
+QActionEvent::QActionEvent(int type, QAction *action, QAction *before)
+ : QEvent(static_cast<QEvent::Type>(type)), act(action), bef(before)
+{}
+
+/*! \internal
+*/
+QActionEvent::~QActionEvent()
+{
+}
+
+/*!
+ \fn QAction *QActionEvent::action() const
+
+ Returns the action that is changed, added, or removed.
+
+ \sa before()
+*/
+
+/*!
+ \fn QAction *QActionEvent::before() const
+
+ If type() is \l ActionAdded, returns the action that should
+ appear before action(). If this function returns 0, the action
+ should be appended to already existing actions on the same
+ widget.
+
+ \sa action(), QWidget::actions()
+*/
+
+#endif // QT_NO_ACTION
+
+/*!
+ \class QHideEvent
+ \brief The QHideEvent class provides an event which is sent after a widget is hidden.
+
+ \ingroup events
+
+ This event is sent just before QWidget::hide() returns, and also
+ when a top-level window has been hidden (iconified) by the user.
+
+ If spontaneous() is true, the event originated outside the
+ application. In this case, the user hid the window using the
+ window manager controls, either by iconifying the window or by
+ switching to another virtual desktop where the window isn't
+ visible. The window will become hidden but not withdrawn. If the
+ window was iconified, QWidget::isMinimized() returns true.
+
+ \sa QShowEvent
+*/
+
+/*!
+ Constructs a QHideEvent.
+*/
+QHideEvent::QHideEvent()
+ : QEvent(Hide)
+{}
+
+/*! \internal
+*/
+QHideEvent::~QHideEvent()
+{
+}
+
+/*!
+ \class QShowEvent
+ \brief The QShowEvent class provides an event that is sent when a widget is shown.
+
+ \ingroup events
+
+ There are two kinds of show events: show events caused by the
+ window system (spontaneous), and internal show events. Spontaneous (QEvent::spontaneous())
+ show events are sent just after the window system shows the
+ window; they are also sent when a top-level window is redisplayed
+ after being iconified. Internal show events are delivered just
+ before the widget becomes visible.
+
+ \sa QHideEvent
+*/
+
+/*!
+ Constructs a QShowEvent.
+*/
+QShowEvent::QShowEvent()
+ : QEvent(Show)
+{}
+
+/*! \internal
+*/
+QShowEvent::~QShowEvent()
+{
+}
+
+/*!
+ \fn QByteArray QDropEvent::data(const char* f) const
+
+ \obsolete
+
+ The encoded data is in \a f.
+ Use QDropEvent::encodedData().
+*/
+
+/*!
+ \class QFileOpenEvent
+ \brief The QFileOpenEvent class provides an event that will be
+ sent when there is a request to open a file.
+
+ \ingroup events
+
+ File open events will be sent to the QApplication::instance()
+ when the operating system requests that a file be opened. This is
+ a high-level event that can be caused by different user actions
+ depending on the user's desktop environment; for example, double
+ clicking on an file icon in the Finder on Mac OS X.
+
+ This event is only used to notify the application of a request.
+ It may be safely ignored.
+
+ \note This class is currently supported for Mac Os X only.
+*/
+
+/*!
+ \internal
+
+ Constructs a file open event for the given \a file.
+*/
+QFileOpenEvent::QFileOpenEvent(const QString &file)
+ : QEvent(FileOpen), f(file)
+{}
+
+/*! \internal
+*/
+QFileOpenEvent::~QFileOpenEvent()
+{
+}
+
+/*!
+ \fn QString QFileOpenEvent::file() const
+
+ Returns the file that is being opened.
+*/
+
+#ifndef QT_NO_TOOLBAR
+/*!
+ \internal
+ \class QToolBarChangeEvent
+ \brief The QToolBarChangeEvent class provides an event that is
+ sent whenever a the toolbar button is clicked on Mac OS X.
+
+ \ingroup events
+
+ The QToolBarChangeEvent is sent when the toolbar button is clicked. On Mac
+ OS X, this is the long oblong button on the right side of the window
+ title bar. The default implementation is to toggle the appearance (hidden or
+ shown) of the associated toolbars for the window.
+*/
+
+/*!
+ \internal
+
+ Construct a QToolBarChangeEvent given the current button state in \a state.
+*/
+QToolBarChangeEvent::QToolBarChangeEvent(bool t)
+ : QEvent(ToolBarChange), tog(t)
+{}
+
+/*! \internal
+*/
+QToolBarChangeEvent::~QToolBarChangeEvent()
+{
+}
+
+/*!
+ \fn bool QToolBarChangeEvent::toggle() const
+ \internal
+*/
+
+/*
+ \fn Qt::ButtonState QToolBarChangeEvent::state() const
+
+ Returns the keyboard modifier flags at the time of the event.
+
+ The returned value is a selection of the following values,
+ combined using the OR operator:
+ Qt::ShiftButton, Qt::ControlButton, Qt::MetaButton, and Qt::AltButton.
+*/
+
+#endif // QT_NO_TOOLBAR
+
+#ifndef QT_NO_SHORTCUT
+
+/*!
+ Constructs a shortcut event for the given \a key press,
+ associated with the QShortcut ID \a id.
+
+ \a ambiguous specifies whether there is more than one QShortcut
+ for the same key sequence.
+*/
+QShortcutEvent::QShortcutEvent(const QKeySequence &key, int id, bool ambiguous)
+ : QEvent(Shortcut), sequence(key), ambig(ambiguous), sid(id)
+{
+}
+
+/*!
+ Destroys the event object.
+*/
+QShortcutEvent::~QShortcutEvent()
+{
+}
+
+#endif // QT_NO_SHORTCUT
+
+#ifndef QT_NO_DEBUG_STREAM
+QDebug operator<<(QDebug dbg, const QEvent *e) {
+#ifndef Q_BROKEN_DEBUG_STREAM
+ // More useful event output could be added here
+ if (!e)
+ return dbg << "QEvent(this = 0x0)";
+ const char *n = 0;
+ switch (e->type()) {
+ case QEvent::Timer:
+ n = "Timer";
+ break;
+ case QEvent::MouseButtonPress:
+ case QEvent::MouseMove:
+ case QEvent::MouseButtonRelease:
+ case QEvent::MouseButtonDblClick:
+ {
+ const QMouseEvent *me = static_cast<const QMouseEvent*>(e);
+ switch(me->type()) {
+ case QEvent::MouseButtonPress:
+ n = "MouseButtonPress";
+ break;
+ case QEvent::MouseMove:
+ n = "MouseMove";
+ break;
+ case QEvent::MouseButtonRelease:
+ n = "MouseButtonRelease";
+ break;
+ case QEvent::MouseButtonDblClick:
+ default:
+ n = "MouseButtonDblClick";
+ break;
+ }
+ dbg.nospace() << "QMouseEvent(" << n
+ << ", " << me->button()
+ << ", " << hex << (int)me->buttons()
+ << ", " << hex << (int)me->modifiers()
+ << ")";
+ }
+ return dbg.space();
+
+#ifndef QT_NO_TOOLTIP
+ case QEvent::ToolTip:
+ n = "ToolTip";
+ break;
+#endif
+ case QEvent::WindowActivate:
+ n = "WindowActivate";
+ break;
+ case QEvent::WindowDeactivate:
+ n = "WindowDeactivate";
+ break;
+ case QEvent::ActivationChange:
+ n = "ActivationChange";
+ break;
+#ifndef QT_NO_WHEELEVENT
+ case QEvent::Wheel:
+ dbg.nospace() << "QWheelEvent(" << static_cast<const QWheelEvent *>(e)->delta()
+ << ")";
+ return dbg.space();
+#endif
+ case QEvent::KeyPress:
+ case QEvent::KeyRelease:
+ case QEvent::ShortcutOverride:
+ {
+ const QKeyEvent *ke = static_cast<const QKeyEvent*>(e);
+ switch(ke->type()) {
+ case QEvent::ShortcutOverride:
+ n = "ShortcutOverride";
+ break;
+ case QEvent::KeyRelease:
+ n = "KeyRelease";
+ break;
+ case QEvent::KeyPress:
+ default:
+ n = "KeyPress";
+ break;
+ }
+ dbg.nospace() << "QKeyEvent(" << n
+ << ", " << hex << ke->key()
+ << ", " << hex << (int)ke->modifiers()
+ << ", \"" << ke->text()
+ << "\", " << ke->isAutoRepeat()
+ << ", " << ke->count()
+ << ")";
+ }
+ return dbg.space();
+ case QEvent::FocusIn:
+ n = "FocusIn";
+ break;
+ case QEvent::FocusOut:
+ n = "FocusOut";
+ break;
+ case QEvent::Enter:
+ n = "Enter";
+ break;
+ case QEvent::Leave:
+ n = "Leave";
+ break;
+ case QEvent::PaletteChange:
+ n = "PaletteChange";
+ break;
+ case QEvent::PolishRequest:
+ n = "PolishRequest";
+ break;
+ case QEvent::Polish:
+ n = "Polish";
+ break;
+ case QEvent::UpdateRequest:
+ n = "UpdateRequest";
+ break;
+ case QEvent::Paint:
+ n = "Paint";
+ break;
+ case QEvent::Move:
+ n = "Move";
+ break;
+ case QEvent::Resize:
+ n = "Resize";
+ break;
+ case QEvent::Create:
+ n = "Create";
+ break;
+ case QEvent::Destroy:
+ n = "Destroy";
+ break;
+ case QEvent::Close:
+ n = "Close";
+ break;
+ case QEvent::Quit:
+ n = "Quit";
+ break;
+ case QEvent::FileOpen:
+ n = "FileOpen";
+ break;
+ case QEvent::Show:
+ n = "Show";
+ break;
+ case QEvent::ShowToParent:
+ n = "ShowToParent";
+ break;
+ case QEvent::Hide:
+ n = "Hide";
+ break;
+ case QEvent::HideToParent:
+ n = "HideToParent";
+ break;
+ case QEvent::None:
+ n = "None";
+ break;
+ case QEvent::ParentChange:
+ n = "ParentChange";
+ break;
+ case QEvent::ParentAboutToChange:
+ n = "ParentAboutToChange";
+ break;
+ case QEvent::HoverEnter:
+ n = "HoverEnter";
+ break;
+ case QEvent::HoverMove:
+ n = "HoverMove";
+ break;
+ case QEvent::HoverLeave:
+ n = "HoverLeave";
+ break;
+ case QEvent::ZOrderChange:
+ n = "ZOrderChange";
+ break;
+ case QEvent::StyleChange:
+ n = "StyleChange";
+ break;
+ case QEvent::DragEnter:
+ n = "DragEnter";
+ break;
+ case QEvent::DragMove:
+ n = "DragMove";
+ break;
+ case QEvent::DragLeave:
+ n = "DragLeave";
+ break;
+ case QEvent::Drop:
+ n = "Drop";
+ break;
+ case QEvent::GraphicsSceneMouseMove:
+ n = "GraphicsSceneMouseMove";
+ break;
+ case QEvent::GraphicsSceneMousePress:
+ n = "GraphicsSceneMousePress";
+ break;
+ case QEvent::GraphicsSceneMouseRelease:
+ n = "GraphicsSceneMouseRelease";
+ break;
+ case QEvent::GraphicsSceneMouseDoubleClick:
+ n = "GraphicsSceneMouseDoubleClick";
+ break;
+ case QEvent::GraphicsSceneContextMenu:
+ n = "GraphicsSceneContextMenu";
+ break;
+ case QEvent::GraphicsSceneHoverEnter:
+ n = "GraphicsSceneHoverEnter";
+ break;
+ case QEvent::GraphicsSceneHoverMove:
+ n = "GraphicsSceneHoverMove";
+ break;
+ case QEvent::GraphicsSceneHoverLeave:
+ n = "GraphicsSceneHoverLeave";
+ break;
+ case QEvent::GraphicsSceneHelp:
+ n = "GraphicsSceneHelp";
+ break;
+ case QEvent::GraphicsSceneDragEnter:
+ n = "GraphicsSceneDragEnter";
+ break;
+ case QEvent::GraphicsSceneDragMove:
+ n = "GraphicsSceneDragMove";
+ break;
+ case QEvent::GraphicsSceneDragLeave:
+ n = "GraphicsSceneDragLeave";
+ break;
+ case QEvent::GraphicsSceneDrop:
+ n = "GraphicsSceneDrop";
+ break;
+ case QEvent::GraphicsSceneWheel:
+ n = "GraphicsSceneWheel";
+ break;
+ case QEvent::GraphicsSceneResize:
+ n = "GraphicsSceneResize";
+ break;
+ case QEvent::GraphicsSceneMove:
+ n = "GraphicsSceneMove";
+ break;
+ case QEvent::CursorChange:
+ n = "CursorChange";
+ break;
+ case QEvent::ToolTipChange:
+ n = "ToolTipChange";
+ break;
+ case QEvent::StatusTip:
+ n = "StatusTip";
+ break;
+ case QEvent::WhatsThis:
+ n = "WhatsThis";
+ break;
+ case QEvent::FontChange:
+ n = "FontChange";
+ break;
+ case QEvent::Style:
+ n = "Style";
+ break;
+ case QEvent::KeyboardLayoutChange:
+ n = "KeyboardLayoutChange";
+ break;
+ case QEvent::DynamicPropertyChange:
+ n = "DynamicPropertyChange";
+ break;
+ case QEvent::GrabMouse:
+ n = "GrabMouse";
+ break;
+ case QEvent::UngrabMouse:
+ n = "UngrabMouse";
+ break;
+ case QEvent::GrabKeyboard:
+ n = "GrabKeyboard";
+ break;
+ case QEvent::UngrabKeyboard:
+ n = "UngrabKeyboard";
+ break;
+#ifdef QT3_SUPPORT
+ case QEvent::ChildInsertedRequest:
+ n = "ChildInsertedRequest";
+ break;
+ case QEvent::ChildInserted: n = "ChildInserted";
+#endif
+ case QEvent::ChildAdded: n = n ? n : "ChildAdded";
+ case QEvent::ChildPolished: n = n ? n : "ChildPolished";
+ case QEvent::ChildRemoved: n = n ? n : "ChildRemoved";
+ dbg.nospace() << "QChildEvent(" << n << ", " << (static_cast<const QChildEvent*>(e))->child();
+ return dbg.space();
+ default:
+ dbg.nospace() << "QEvent(" << (const void *)e << ", type = " << e->type() << ')';
+ return dbg.space();
+ }
+
+ dbg.nospace() << 'Q' << n << "Event(" << (const void *)e << ')';
+ return dbg.space();
+#else
+ qWarning("This compiler doesn't support streaming QEvent to QDebug");
+ return dbg;
+ Q_UNUSED(e);
+#endif
+}
+#endif
+
+#ifndef QT_NO_CLIPBOARD
+/*!
+ \class QClipboardEvent
+ \ingroup events
+ \internal
+
+ \brief The QClipboardEvent class provides the parameters used in a clipboard event.
+
+ This class is for internal use only, and exists to aid the clipboard on various
+ platforms to get all the information it needs. Use QEvent::Clipboard instead.
+
+ \sa QClipboard
+*/
+
+QClipboardEvent::QClipboardEvent(QEventPrivate *data)
+ : QEvent(QEvent::Clipboard)
+{
+ d = data;
+}
+
+QClipboardEvent::~QClipboardEvent()
+{
+}
+#endif // QT_NO_CLIPBOARD
+
+/*!
+ \class QShortcutEvent
+ \brief The QShortcutEvent class provides an event which is generated when
+ the user presses a key combination.
+
+ \ingroup events
+
+ Normally you don't need to use this class directly; QShortcut
+ provides a higher-level interface to handle shortcut keys.
+
+ \sa QShortcut
+*/
+
+/*!
+ \fn const QKeySequence &QShortcutEvent::key() const
+
+ Returns the key sequence that triggered the event.
+*/
+
+// ### Qt 5: remove
+/*!
+ \fn const QKeySequence &QShortcutEvent::key()
+
+ \internal
+*/
+
+/*!
+ \fn int QShortcutEvent::shortcutId() const
+
+ Returns the ID of the QShortcut object for which this event was
+ generated.
+
+ \sa QShortcut::id()
+*/
+
+// ### Qt 5: remove
+/*!
+ \fn int QShortcutEvent::shortcutId()
+ \overload
+
+ \internal
+*/
+
+/*!
+ \fn bool QShortcutEvent::isAmbiguous() const
+
+ Returns true if the key sequence that triggered the event is
+ ambiguous.
+
+ \sa QShortcut::activatedAmbiguously()
+*/
+
+// ### Qt 5: remove
+/*!
+ \fn bool QShortcutEvent::isAmbiguous()
+
+ \internal
+*/
+
+/*!
+ \class QWindowStateChangeEvent
+ \ingroup events
+
+ \brief The QWindowStateChangeEvent class provides the window state before a
+ window state change.
+*/
+
+/*! \fn Qt::WindowStates QWindowStateChangeEvent::oldState() const
+
+ Returns the state of the window before the change.
+*/
+
+/*! \internal
+ */
+QWindowStateChangeEvent::QWindowStateChangeEvent(Qt::WindowStates s)
+ : QEvent(WindowStateChange), ostate(s)
+{
+}
+
+/*! \internal
+ */
+QWindowStateChangeEvent::QWindowStateChangeEvent(Qt::WindowStates s, bool isOverride)
+ : QEvent(WindowStateChange), ostate(s)
+{
+ if (isOverride)
+ d = (QEventPrivate*)(this);
+}
+
+/*! \internal
+ */
+bool QWindowStateChangeEvent::isOverride() const
+{
+ return (d != 0);
+}
+
+/*! \internal
+*/
+QWindowStateChangeEvent::~QWindowStateChangeEvent()
+{
+}
+
+#ifdef QT3_SUPPORT
+
+/*!
+ \class QMenubarUpdatedEvent
+ \internal
+ Event sent by QMenuBar to tell Q3Workspace to update itself.
+*/
+
+/*! \internal
+
+*/
+QMenubarUpdatedEvent::QMenubarUpdatedEvent(QMenuBar * const menuBar)
+:QEvent(QEvent::MenubarUpdated), m_menuBar(menuBar) {}
+
+/*!
+ \fn QMenuBar *QMenubarUpdatedEvent::menuBar()
+ \internal
+*/
+
+/*!
+ \fn bool operator==(QKeyEvent *e, QKeySequence::StandardKey key)
+
+ \relates QKeyEvent
+
+ Returns true if \a key is currently bound to the key combination
+ specified by \a e.
+
+ Equivalent to \c {e->matches(key)}.
+*/
+
+/*!
+ \fn bool operator==(QKeySequence::StandardKey key, QKeyEvent *e)
+
+ \relates QKeyEvent
+
+ Returns true if \a key is currently bound to the key combination
+ specified by \a e.
+
+ Equivalent to \c {e->matches(key)}.
+*/
+
+/*!
+ \internal
+
+ \class QKeyEventEx
+ \ingroup events
+
+ \brief The QKeyEventEx class provides more extended information about a keyevent.
+
+ This class is for internal use only, and exists to aid the shortcut system on
+ various platforms to get all the information it needs.
+*/
+
+#endif
+
+QT_END_NAMESPACE
diff --git a/src/gui/kernel/qevent.h b/src/gui/kernel/qevent.h
new file mode 100644
index 0000000000..449730da2d
--- /dev/null
+++ b/src/gui/kernel/qevent.h
@@ -0,0 +1,726 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QEVENT_H
+#define QEVENT_H
+
+#include <QtGui/qwindowdefs.h>
+#include <QtCore/qobject.h>
+#include <QtGui/qregion.h>
+#include <QtCore/qnamespace.h>
+#include <QtCore/qstring.h>
+#include <QtGui/qkeysequence.h>
+#include <QtCore/qcoreevent.h>
+#include <QtGui/qmime.h>
+#include <QtGui/qdrag.h>
+#include <QtCore/qvariant.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+class QAction;
+
+class Q_GUI_EXPORT QInputEvent : public QEvent
+{
+public:
+ QInputEvent(Type type, Qt::KeyboardModifiers modifiers = Qt::NoModifier);
+ ~QInputEvent();
+ inline Qt::KeyboardModifiers modifiers() const { return modState; }
+protected:
+ Qt::KeyboardModifiers modState;
+};
+
+class Q_GUI_EXPORT QMouseEvent : public QInputEvent
+{
+public:
+ QMouseEvent(Type type, const QPoint &pos, Qt::MouseButton button,
+ Qt::MouseButtons buttons, Qt::KeyboardModifiers modifiers);
+ QMouseEvent(Type type, const QPoint &pos, const QPoint &globalPos,
+ Qt::MouseButton button, Qt::MouseButtons buttons,
+ Qt::KeyboardModifiers modifiers);
+ ~QMouseEvent();
+
+ inline const QPoint &pos() const { return p; }
+ inline const QPoint &globalPos() const { return g; }
+ inline int x() const { return p.x(); }
+ inline int y() const { return p.y(); }
+ inline int globalX() const { return g.x(); }
+ inline int globalY() const { return g.y(); }
+ inline Qt::MouseButton button() const { return b; }
+ inline Qt::MouseButtons buttons() const { return mouseState; }
+
+ static QMouseEvent *createExtendedMouseEvent(Type type, const QPointF &pos,
+ const QPoint &globalPos, Qt::MouseButton button,
+ Qt::MouseButtons buttons, Qt::KeyboardModifiers modifiers);
+ inline bool hasExtendedInfo() const { return reinterpret_cast<const QMouseEvent *>(d) == this; }
+ QPointF posF() const;
+
+#ifdef QT3_SUPPORT
+ QT3_SUPPORT_CONSTRUCTOR QMouseEvent(Type type, const QPoint &pos, Qt::ButtonState button, int state);
+ QT3_SUPPORT_CONSTRUCTOR QMouseEvent(Type type, const QPoint &pos, const QPoint &globalPos,
+ Qt::ButtonState button, int state);
+ inline QT3_SUPPORT Qt::ButtonState state() const
+ { return Qt::ButtonState((mouseState^b)|int(modifiers())); }
+ inline QT3_SUPPORT Qt::ButtonState stateAfter() const
+ { return Qt::ButtonState(int(mouseState)|int(modifiers())); }
+#endif
+protected:
+ QPoint p, g;
+ Qt::MouseButton b;
+ Qt::MouseButtons mouseState;
+};
+
+class Q_GUI_EXPORT QHoverEvent : public QEvent
+{
+public:
+ QHoverEvent(Type type, const QPoint &pos, const QPoint &oldPos);
+ ~QHoverEvent();
+
+ inline const QPoint &pos() const { return p; }
+ inline const QPoint &oldPos() const { return op; }
+
+protected:
+ QPoint p, op;
+};
+
+#ifndef QT_NO_WHEELEVENT
+class Q_GUI_EXPORT QWheelEvent : public QInputEvent
+{
+public:
+ QWheelEvent(const QPoint &pos, int delta,
+ Qt::MouseButtons buttons, Qt::KeyboardModifiers modifiers,
+ Qt::Orientation orient = Qt::Vertical);
+ QWheelEvent(const QPoint &pos, const QPoint& globalPos, int delta,
+ Qt::MouseButtons buttons, Qt::KeyboardModifiers modifiers,
+ Qt::Orientation orient = Qt::Vertical);
+ ~QWheelEvent();
+
+ inline int delta() const { return d; }
+ inline const QPoint &pos() const { return p; }
+ inline const QPoint &globalPos() const { return g; }
+ inline int x() const { return p.x(); }
+ inline int y() const { return p.y(); }
+ inline int globalX() const { return g.x(); }
+ inline int globalY() const { return g.y(); }
+
+ inline Qt::MouseButtons buttons() const { return mouseState; }
+ Qt::Orientation orientation() const { return o; }
+
+#ifdef QT3_SUPPORT
+ QT3_SUPPORT_CONSTRUCTOR QWheelEvent(const QPoint &pos, int delta, int state,
+ Qt::Orientation orient = Qt::Vertical);
+ QT3_SUPPORT_CONSTRUCTOR QWheelEvent(const QPoint &pos, const QPoint& globalPos, int delta, int state,
+ Qt::Orientation orient = Qt::Vertical);
+ inline QT3_SUPPORT Qt::ButtonState state() const
+ { return static_cast<Qt::ButtonState>(int(buttons())|int(modifiers())); }
+#endif
+protected:
+ QPoint p;
+ QPoint g;
+ int d;
+ Qt::MouseButtons mouseState;
+ Qt::Orientation o;
+};
+#endif
+
+#ifndef QT_NO_TABLETEVENT
+class Q_GUI_EXPORT QTabletEvent : public QInputEvent
+{
+public:
+ enum TabletDevice { NoDevice, Puck, Stylus, Airbrush, FourDMouse,
+ XFreeEraser /*internal*/, RotationStylus };
+ enum PointerType { UnknownPointer, Pen, Cursor, Eraser };
+ QTabletEvent(Type t, const QPoint &pos, const QPoint &globalPos, const QPointF &hiResGlobalPos,
+ int device, int pointerType, qreal pressure, int xTilt, int yTilt,
+ qreal tangentialPressure, qreal rotation, int z,
+ Qt::KeyboardModifiers keyState, qint64 uniqueID);
+ ~QTabletEvent();
+
+ inline const QPoint &pos() const { return mPos; }
+ inline const QPoint &globalPos() const { return mGPos; }
+ inline const QPointF &hiResGlobalPos() const { return mHiResGlobalPos; }
+ inline int x() const { return mPos.x(); }
+ inline int y() const { return mPos.y(); }
+ inline int globalX() const { return mGPos.x(); }
+ inline int globalY() const { return mGPos.y(); }
+ inline qreal hiResGlobalX() const { return mHiResGlobalPos.x(); }
+ inline qreal hiResGlobalY() const { return mHiResGlobalPos.y(); }
+ inline TabletDevice device() const { return TabletDevice(mDev); }
+ inline PointerType pointerType() const { return PointerType(mPointerType); }
+ inline qint64 uniqueId() const { return mUnique; }
+ inline qreal pressure() const { return mPress; }
+ inline int z() const { return mZ; }
+ inline qreal tangentialPressure() const { return mTangential; }
+ inline qreal rotation() const { return mRot; }
+ inline int xTilt() const { return mXT; }
+ inline int yTilt() const { return mYT; }
+
+protected:
+ QPoint mPos, mGPos;
+ QPointF mHiResGlobalPos;
+ int mDev, mPointerType, mXT, mYT, mZ;
+ qreal mPress, mTangential, mRot;
+ qint64 mUnique;
+
+ // I don't know what the future holds for tablets but there could be some
+ // new devices coming along, and there seem to be "holes" in the
+ // OS-specific events for this.
+ void *mExtra;
+};
+#endif // QT_NO_TABLETEVENT
+
+class Q_GUI_EXPORT QKeyEvent : public QInputEvent
+{
+public:
+ QKeyEvent(Type type, int key, Qt::KeyboardModifiers modifiers, const QString& text = QString(),
+ bool autorep = false, ushort count = 1);
+ ~QKeyEvent();
+
+ int key() const { return k; }
+#ifndef QT_NO_SHORTCUT
+ bool matches(QKeySequence::StandardKey key) const;
+#endif
+ Qt::KeyboardModifiers modifiers() const;
+ inline QString text() const { return txt; }
+ inline bool isAutoRepeat() const { return autor; }
+ inline int count() const { return int(c); }
+
+ // Functions for the extended key event information
+ static QKeyEvent *createExtendedKeyEvent(Type type, int key, Qt::KeyboardModifiers modifiers,
+ quint32 nativeScanCode, quint32 nativeVirtualKey,
+ quint32 nativeModifiers,
+ const QString& text = QString(), bool autorep = false,
+ ushort count = 1);
+ inline bool hasExtendedInfo() const { return reinterpret_cast<const QKeyEvent*>(d) == this; }
+ quint32 nativeScanCode() const;
+ quint32 nativeVirtualKey() const;
+ quint32 nativeModifiers() const;
+
+#ifdef QT3_SUPPORT
+ inline QT3_SUPPORT_CONSTRUCTOR QKeyEvent(Type type, int key, int /*ascii*/,
+ int modifiers, const QString& text = QString(),
+ bool autorep = false, ushort count = 1)
+ : QInputEvent(type, (Qt::KeyboardModifiers)(modifiers & (int)Qt::KeyButtonMask)), txt(text), k(key),
+ c(count), autor(autorep)
+ {
+ if (key >= Qt::Key_Back && key <= Qt::Key_MediaLast)
+ ignore();
+ }
+ inline QT3_SUPPORT int ascii() const
+ { return (txt.length() ? txt.unicode()->toLatin1() : 0); }
+ inline QT3_SUPPORT Qt::ButtonState state() const { return Qt::ButtonState(QInputEvent::modifiers()); }
+ inline QT3_SUPPORT Qt::ButtonState stateAfter() const { return Qt::ButtonState(modifiers()); }
+#endif
+
+protected:
+ QString txt;
+ int k;
+ ushort c;
+ uint autor:1;
+};
+
+
+class Q_GUI_EXPORT QFocusEvent : public QEvent
+{
+public:
+ QFocusEvent(Type type, Qt::FocusReason reason=Qt::OtherFocusReason);
+ ~QFocusEvent();
+
+ inline bool gotFocus() const { return type() == FocusIn; }
+ inline bool lostFocus() const { return type() == FocusOut; }
+
+#ifdef QT3_SUPPORT
+ enum Reason { Mouse=Qt::MouseFocusReason, Tab=Qt::TabFocusReason,
+ Backtab=Qt::BacktabFocusReason, MenuBar=Qt::MenuBarFocusReason,
+ ActiveWindow=Qt::ActiveWindowFocusReason, Other=Qt::OtherFocusReason,
+ Popup=Qt::PopupFocusReason, Shortcut=Qt::ShortcutFocusReason };
+#endif
+ Qt::FocusReason reason();
+ Qt::FocusReason reason() const;
+
+private:
+ Qt::FocusReason m_reason;
+};
+
+
+class Q_GUI_EXPORT QPaintEvent : public QEvent
+{
+public:
+ QPaintEvent(const QRegion& paintRegion);
+ QPaintEvent(const QRect &paintRect);
+ ~QPaintEvent();
+
+ inline const QRect &rect() const { return m_rect; }
+ inline const QRegion &region() const { return m_region; }
+
+#ifdef QT3_SUPPORT
+ QT3_SUPPORT_CONSTRUCTOR QPaintEvent(const QRegion &paintRegion, const QRect &paintRect);
+ inline QT3_SUPPORT bool erased() const { return m_erased; }
+ inline QT3_SUPPORT void setErased(bool b) { m_erased = b; }
+#endif
+
+protected:
+ friend class QApplication;
+ friend class QCoreApplication;
+ QRect m_rect;
+ QRegion m_region;
+ bool m_erased;
+};
+
+class QUpdateLaterEvent : public QEvent
+{
+public:
+ QUpdateLaterEvent(const QRegion& paintRegion);
+ ~QUpdateLaterEvent();
+
+ inline const QRegion &region() const { return m_region; }
+
+protected:
+ QRegion m_region;
+};
+
+class Q_GUI_EXPORT QMoveEvent : public QEvent
+{
+public:
+ QMoveEvent(const QPoint &pos, const QPoint &oldPos);
+ ~QMoveEvent();
+
+ inline const QPoint &pos() const { return p; }
+ inline const QPoint &oldPos() const { return oldp;}
+protected:
+ QPoint p, oldp;
+ friend class QApplication;
+ friend class QCoreApplication;
+};
+
+
+class Q_GUI_EXPORT QResizeEvent : public QEvent
+{
+public:
+ QResizeEvent(const QSize &size, const QSize &oldSize);
+ ~QResizeEvent();
+
+ inline const QSize &size() const { return s; }
+ inline const QSize &oldSize()const { return olds;}
+protected:
+ QSize s, olds;
+ friend class QApplication;
+ friend class QCoreApplication;
+};
+
+
+class Q_GUI_EXPORT QCloseEvent : public QEvent
+{
+public:
+ QCloseEvent();
+ ~QCloseEvent();
+};
+
+
+class Q_GUI_EXPORT QIconDragEvent : public QEvent
+{
+public:
+ QIconDragEvent();
+ ~QIconDragEvent();
+};
+
+
+class Q_GUI_EXPORT QShowEvent : public QEvent
+{
+public:
+ QShowEvent();
+ ~QShowEvent();
+};
+
+
+class Q_GUI_EXPORT QHideEvent : public QEvent
+{
+public:
+ QHideEvent();
+ ~QHideEvent();
+};
+
+#ifndef QT_NO_CONTEXTMENU
+class Q_GUI_EXPORT QContextMenuEvent : public QInputEvent
+{
+public:
+ enum Reason { Mouse, Keyboard, Other };
+
+ QContextMenuEvent(Reason reason, const QPoint &pos, const QPoint &globalPos,
+ Qt::KeyboardModifiers modifiers);
+ QContextMenuEvent(Reason reason, const QPoint &pos, const QPoint &globalPos);
+ QContextMenuEvent(Reason reason, const QPoint &pos);
+ ~QContextMenuEvent();
+
+ inline int x() const { return p.x(); }
+ inline int y() const { return p.y(); }
+ inline int globalX() const { return gp.x(); }
+ inline int globalY() const { return gp.y(); }
+
+ inline const QPoint& pos() const { return p; }
+ inline const QPoint& globalPos() const { return gp; }
+
+ inline Reason reason() const { return Reason(reas); }
+
+#ifdef QT3_SUPPORT
+ QT3_SUPPORT_CONSTRUCTOR QContextMenuEvent(Reason reason, const QPoint &pos, const QPoint &globalPos, int);
+ QT3_SUPPORT_CONSTRUCTOR QContextMenuEvent(Reason reason, const QPoint &pos, int);
+
+ QT3_SUPPORT Qt::ButtonState state() const;
+#endif
+protected:
+ QPoint p;
+ QPoint gp;
+ uint reas : 8;
+};
+#endif // QT_NO_CONTEXTMENU
+
+#ifndef QT_NO_INPUTMETHOD
+class Q_GUI_EXPORT QInputMethodEvent : public QEvent
+{
+public:
+ enum AttributeType {
+ TextFormat,
+ Cursor,
+ Language,
+ Ruby
+ };
+ class Attribute {
+ public:
+ Attribute(AttributeType t, int s, int l, QVariant val) : type(t), start(s), length(l), value(val) {}
+ AttributeType type;
+
+ int start;
+ int length;
+ QVariant value;
+ };
+ QInputMethodEvent();
+ QInputMethodEvent(const QString &preeditText, const QList<Attribute> &attributes);
+ void setCommitString(const QString &commitString, int replaceFrom = 0, int replaceLength = 0);
+
+ inline const QList<Attribute> &attributes() const { return attrs; }
+ inline const QString &preeditString() const { return preedit; }
+
+ inline const QString &commitString() const { return commit; }
+ inline int replacementStart() const { return replace_from; }
+ inline int replacementLength() const { return replace_length; }
+
+ QInputMethodEvent(const QInputMethodEvent &other);
+
+private:
+ QString preedit;
+ QList<Attribute> attrs;
+ QString commit;
+ int replace_from;
+ int replace_length;
+};
+#endif // QT_NO_INPUTMETHOD
+
+#ifndef QT_NO_DRAGANDDROP
+
+class QMimeData;
+
+class Q_GUI_EXPORT QDropEvent : public QEvent
+// QT3_SUPPORT
+ , public QMimeSource
+// END QT3_SUPPORT
+{
+public:
+ QDropEvent(const QPoint& pos, Qt::DropActions actions, const QMimeData *data,
+ Qt::MouseButtons buttons, Qt::KeyboardModifiers modifiers, Type type = Drop);
+ ~QDropEvent();
+
+ inline const QPoint &pos() const { return p; }
+ inline Qt::MouseButtons mouseButtons() const { return mouseState; }
+ inline Qt::KeyboardModifiers keyboardModifiers() const { return modState; }
+
+ inline Qt::DropActions possibleActions() const { return act; }
+ inline Qt::DropAction proposedAction() const { return default_action; }
+ inline void acceptProposedAction() { drop_action = default_action; accept(); }
+
+ inline Qt::DropAction dropAction() const { return drop_action; }
+ void setDropAction(Qt::DropAction action);
+
+ QWidget* source() const;
+ inline const QMimeData *mimeData() const { return mdata; }
+
+// QT3_SUPPORT
+ const char* format(int n = 0) const;
+ QByteArray encodedData(const char*) const;
+ bool provides(const char*) const;
+// END QT3_SUPPORT
+#ifdef QT3_SUPPORT
+ inline void accept() { QEvent::accept(); }
+ inline QT3_SUPPORT void accept(bool y) { setAccepted(y); }
+ inline QT3_SUPPORT QByteArray data(const char* f) const { return encodedData(f); }
+
+ enum Action { Copy, Link, Move, Private, UserAction = Private };
+ QT3_SUPPORT Action action() const;
+ inline QT3_SUPPORT void acceptAction(bool y = true) { if (y) { drop_action = default_action; accept(); } }
+ inline QT3_SUPPORT void setPoint(const QPoint& np) { p = np; }
+#endif
+
+
+protected:
+ friend class QApplication;
+ QPoint p;
+ Qt::MouseButtons mouseState;
+ Qt::KeyboardModifiers modState;
+ Qt::DropActions act;
+ Qt::DropAction drop_action;
+ Qt::DropAction default_action;
+ const QMimeData *mdata;
+ mutable QList<QByteArray> fmts; // only used for QT3_SUPPORT
+};
+
+
+class Q_GUI_EXPORT QDragMoveEvent : public QDropEvent
+{
+public:
+ QDragMoveEvent(const QPoint &pos, Qt::DropActions actions, const QMimeData *data,
+ Qt::MouseButtons buttons, Qt::KeyboardModifiers modifiers, Type type = DragMove);
+ ~QDragMoveEvent();
+
+ inline QRect answerRect() const { return rect; }
+
+ inline void accept() { QDropEvent::accept(); }
+ inline void ignore() { QDropEvent::ignore(); }
+
+ inline void accept(const QRect & r) { accept(); rect = r; }
+ inline void ignore(const QRect & r) { ignore(); rect = r; }
+
+#ifdef QT3_SUPPORT
+ inline QT3_SUPPORT void accept(bool y) { setAccepted(y); }
+#endif
+
+protected:
+ friend class QApplication;
+ QRect rect;
+};
+
+
+class Q_GUI_EXPORT QDragEnterEvent : public QDragMoveEvent
+{
+public:
+ QDragEnterEvent(const QPoint &pos, Qt::DropActions actions, const QMimeData *data,
+ Qt::MouseButtons buttons, Qt::KeyboardModifiers modifiers);
+ ~QDragEnterEvent();
+};
+
+
+/* An internal class */
+class Q_GUI_EXPORT QDragResponseEvent : public QEvent
+{
+public:
+ QDragResponseEvent(bool accepted);
+ ~QDragResponseEvent();
+
+ inline bool dragAccepted() const { return a; }
+protected:
+ bool a;
+};
+
+
+class Q_GUI_EXPORT QDragLeaveEvent : public QEvent
+{
+public:
+ QDragLeaveEvent();
+ ~QDragLeaveEvent();
+};
+#endif // QT_NO_DRAGANDDROP
+
+
+class Q_GUI_EXPORT QHelpEvent : public QEvent
+{
+public:
+ QHelpEvent(Type type, const QPoint &pos, const QPoint &globalPos);
+ ~QHelpEvent();
+
+ inline int x() const { return p.x(); }
+ inline int y() const { return p.y(); }
+ inline int globalX() const { return gp.x(); }
+ inline int globalY() const { return gp.y(); }
+
+ inline const QPoint& pos() const { return p; }
+ inline const QPoint& globalPos() const { return gp; }
+
+private:
+ QPoint p;
+ QPoint gp;
+};
+
+#ifndef QT_NO_STATUSTIP
+class Q_GUI_EXPORT QStatusTipEvent : public QEvent
+{
+public:
+ QStatusTipEvent(const QString &tip);
+ ~QStatusTipEvent();
+
+ inline QString tip() const { return s; }
+private:
+ QString s;
+};
+#endif
+
+#ifndef QT_NO_WHATSTHIS
+class Q_GUI_EXPORT QWhatsThisClickedEvent : public QEvent
+{
+public:
+ QWhatsThisClickedEvent(const QString &href);
+ ~QWhatsThisClickedEvent();
+
+ inline QString href() const { return s; }
+private:
+ QString s;
+};
+#endif
+
+#ifndef QT_NO_ACTION
+class Q_GUI_EXPORT QActionEvent : public QEvent
+{
+ QAction *act, *bef;
+public:
+ QActionEvent(int type, QAction *action, QAction *before = 0);
+ ~QActionEvent();
+
+ inline QAction *action() const { return act; }
+ inline QAction *before() const { return bef; }
+};
+#endif
+
+class Q_GUI_EXPORT QFileOpenEvent : public QEvent
+{
+public:
+ QFileOpenEvent(const QString &file);
+ ~QFileOpenEvent();
+
+ inline QString file() const { return f; }
+private:
+ QString f;
+};
+
+#ifndef QT_NO_TOOLBAR
+class Q_GUI_EXPORT QToolBarChangeEvent : public QEvent
+{
+public:
+ QToolBarChangeEvent(bool t);
+ ~QToolBarChangeEvent();
+
+ inline bool toggle() const { return tog; }
+private:
+ uint tog : 1;
+};
+#endif
+
+#ifndef QT_NO_SHORTCUT
+class Q_GUI_EXPORT QShortcutEvent : public QEvent
+{
+public:
+ QShortcutEvent(const QKeySequence &key, int id, bool ambiguous = false);
+ ~QShortcutEvent();
+
+ inline const QKeySequence &key() { return sequence; }
+ inline const QKeySequence &key() const { return sequence; }
+ inline int shortcutId() { return sid; }
+ inline int shortcutId() const { return sid; }
+ inline bool isAmbiguous() { return ambig; }
+ inline bool isAmbiguous() const { return ambig; }
+protected:
+ QKeySequence sequence;
+ bool ambig;
+ int sid;
+};
+#endif
+
+#ifndef QT_NO_CLIPBOARD
+class Q_GUI_EXPORT QClipboardEvent : public QEvent
+{
+public:
+ QClipboardEvent(QEventPrivate *data);
+ ~QClipboardEvent();
+
+ QEventPrivate *data() { return d; };
+};
+#endif
+
+class Q_GUI_EXPORT QWindowStateChangeEvent: public QEvent
+{
+public:
+ QWindowStateChangeEvent(Qt::WindowStates aOldState);
+ QWindowStateChangeEvent(Qt::WindowStates aOldState, bool isOverride);
+ ~QWindowStateChangeEvent();
+
+ inline Qt::WindowStates oldState() const { return ostate; }
+ bool isOverride() const;
+
+private:
+ Qt::WindowStates ostate;
+};
+
+#ifdef QT3_SUPPORT
+class QMenuBar;
+class Q_GUI_EXPORT QMenubarUpdatedEvent: public QEvent
+{
+public:
+ QMenubarUpdatedEvent(QMenuBar * const menBar);
+ inline QMenuBar *menuBar() { return m_menuBar; }
+private:
+ QMenuBar *m_menuBar;
+};
+#endif
+
+#ifndef QT_NO_DEBUG_STREAM
+Q_GUI_EXPORT QDebug operator<<(QDebug, const QEvent *);
+#endif
+
+#ifndef QT_NO_SHORTCUT
+inline bool operator==(QKeyEvent *e, QKeySequence::StandardKey key){return (e ? e->matches(key) : false);}
+inline bool operator==(QKeySequence::StandardKey key, QKeyEvent *e){return (e ? e->matches(key) : false);}
+#endif // QT_NO_SHORTCUT
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QEVENT_H
diff --git a/src/gui/kernel/qevent_p.h b/src/gui/kernel/qevent_p.h
new file mode 100644
index 0000000000..cc94aade58
--- /dev/null
+++ b/src/gui/kernel/qevent_p.h
@@ -0,0 +1,94 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QEVENT_P_H
+#define QEVENT_P_H
+
+#include <QtCore/qglobal.h>
+
+QT_BEGIN_NAMESPACE
+
+//
+// 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.
+//
+
+// ### Qt 5: remove
+class Q_GUI_EXPORT QKeyEventEx : public QKeyEvent
+{
+public:
+ QKeyEventEx(Type type, int key, Qt::KeyboardModifiers modifiers,
+ const QString &text, bool autorep, ushort count,
+ quint32 nativeScanCode, quint32 nativeVirtualKey, quint32 nativeModifiers);
+ QKeyEventEx(const QKeyEventEx &other);
+
+ ~QKeyEventEx();
+
+protected:
+ quint32 nScanCode;
+ quint32 nVirtualKey;
+ quint32 nModifiers;
+ friend class QKeyEvent;
+};
+
+// ### Qt 5: remove
+class Q_GUI_EXPORT QMouseEventEx : public QMouseEvent
+{
+public:
+ QMouseEventEx(Type type, const QPointF &pos, const QPoint &globalPos,
+ Qt::MouseButton button, Qt::MouseButtons buttons,
+ Qt::KeyboardModifiers modifiers);
+ ~QMouseEventEx();
+
+protected:
+ QPointF posF;
+ friend class QMouseEvent;
+};
+
+QT_END_NAMESPACE
+
+#endif // QEVENT_P_H
diff --git a/src/gui/kernel/qeventdispatcher_glib_qws.cpp b/src/gui/kernel/qeventdispatcher_glib_qws.cpp
new file mode 100644
index 0000000000..1e740c3f69
--- /dev/null
+++ b/src/gui/kernel/qeventdispatcher_glib_qws.cpp
@@ -0,0 +1,195 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qeventdispatcher_glib_qws_p.h"
+
+#include "qapplication.h"
+
+#include "qplatformdefs.h"
+#include "qapplication.h"
+#include "private/qwscommand_qws_p.h"
+#include "qwsdisplay_qws.h"
+#include "qwsevent_qws.h"
+#include "qwindowsystem_qws.h"
+
+#include <glib.h>
+
+QT_BEGIN_NAMESPACE
+
+// from qapplication_qws.cpp
+extern QWSDisplay* qt_fbdpy; // QWS `display'
+
+//from qwindowsystem_qws.cpp
+extern QList<QWSCommand*> *qt_get_server_queue();
+
+struct GQWSEventSource
+{
+ GSource source;
+ QEventLoop::ProcessEventsFlags flags;
+ QWSEventDispatcherGlib *q;
+ QWSEventDispatcherGlibPrivate *d;
+};
+
+class QWSEventDispatcherGlibPrivate : public QEventDispatcherGlibPrivate
+{
+ Q_DECLARE_PUBLIC(QWSEventDispatcherGlib)
+
+public:
+ QWSEventDispatcherGlibPrivate();
+ GQWSEventSource *qwsEventSource;
+ QList<QWSEvent*> queuedUserInputEvents;
+};
+
+static gboolean qwsEventSourcePrepare(GSource *s, gint *timeout)
+{
+ if (timeout)
+ *timeout = -1;
+ GQWSEventSource *source = reinterpret_cast<GQWSEventSource *>(s);
+ return qt_fbdpy->eventPending() || !source->d->queuedUserInputEvents.isEmpty()
+ || !qt_get_server_queue()->isEmpty() ;
+}
+
+static gboolean qwsEventSourceCheck(GSource *s)
+{
+ GQWSEventSource *source = reinterpret_cast<GQWSEventSource *>(s);
+ return qt_fbdpy->eventPending() || !source->d->queuedUserInputEvents.isEmpty()
+ || !qt_get_server_queue()->isEmpty() ;
+}
+
+static gboolean qwsEventSourceDispatch(GSource *s, GSourceFunc callback, gpointer user_data)
+{
+ GQWSEventSource *source = reinterpret_cast<GQWSEventSource *>(s);
+
+ //??? ulong marker = XNextRequest(X11->display);
+ do {
+ QWSEvent *event;
+ if (!(source->flags & QEventLoop::ExcludeUserInputEvents)
+ && !source->d->queuedUserInputEvents.isEmpty()) {
+ // process a pending user input event
+ event = source->d->queuedUserInputEvents.takeFirst();
+ } else if (qt_fbdpy->eventPending()) {
+ event = qt_fbdpy->getEvent();
+
+ if (source->flags & QEventLoop::ExcludeUserInputEvents) {
+ // queue user input events
+
+ if (event->type == QWSEvent::Mouse || event->type == QWSEvent::Key) {
+ source->d->queuedUserInputEvents.append(event);
+ continue;
+ }
+ }
+ } else {
+ // no event to process
+ break;
+ }
+
+ // send through event filter
+ if (source->q->filterEvent(event)) {
+ delete event;
+ continue;
+ }
+
+ bool ret = qApp->qwsProcessEvent(event) == 1;
+ delete event;
+ if (ret) {
+ return true;
+ }
+
+ } while (qt_fbdpy->eventPending());
+
+ if (callback)
+ callback(user_data);
+ return true;
+}
+
+static GSourceFuncs qwsEventSourceFuncs = {
+ qwsEventSourcePrepare,
+ qwsEventSourceCheck,
+ qwsEventSourceDispatch,
+ NULL,
+ NULL,
+ NULL
+};
+
+QWSEventDispatcherGlibPrivate::QWSEventDispatcherGlibPrivate()
+{
+ qwsEventSource = reinterpret_cast<GQWSEventSource *>(g_source_new(&qwsEventSourceFuncs,
+ sizeof(GQWSEventSource)));
+ g_source_set_can_recurse(&qwsEventSource->source, true);
+
+ qwsEventSource->flags = QEventLoop::AllEvents;
+ qwsEventSource->q = 0;
+ qwsEventSource->d = 0;
+
+ g_source_attach(&qwsEventSource->source, mainContext);
+}
+
+QWSEventDispatcherGlib::QWSEventDispatcherGlib(QObject *parent)
+ : QEventDispatcherGlib(*new QWSEventDispatcherGlibPrivate, parent)
+{
+}
+
+QWSEventDispatcherGlib::~QWSEventDispatcherGlib()
+{
+ Q_D(QWSEventDispatcherGlib);
+
+ g_source_destroy(&d->qwsEventSource->source);
+ d->qwsEventSource = 0;
+}
+
+bool QWSEventDispatcherGlib::processEvents(QEventLoop::ProcessEventsFlags flags)
+{
+ Q_D(QWSEventDispatcherGlib);
+ QEventLoop::ProcessEventsFlags saved_flags = d->qwsEventSource->flags;
+ d->qwsEventSource->flags = flags;
+ bool returnValue = QEventDispatcherGlib::processEvents(flags);
+ d->qwsEventSource->flags = saved_flags;
+ return returnValue;
+}
+
+void QWSEventDispatcherGlib::startingUp()
+{
+ Q_D(QWSEventDispatcherGlib);
+ d->qwsEventSource->q = this;
+ d->qwsEventSource->d = d;
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/kernel/qeventdispatcher_glib_qws_p.h b/src/gui/kernel/qeventdispatcher_glib_qws_p.h
new file mode 100644
index 0000000000..1589aef687
--- /dev/null
+++ b/src/gui/kernel/qeventdispatcher_glib_qws_p.h
@@ -0,0 +1,78 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QWSEVENTDISPATCHER_GLIB_P_H
+#define QWSEVENTDISPATCHER_GLIB_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of the QLibrary class. This header file may change from
+// version to version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtCore/private/qeventdispatcher_glib_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QWSEventDispatcherGlibPrivate;
+
+class QWSEventDispatcherGlib : public QEventDispatcherGlib
+{
+ Q_OBJECT
+ Q_DECLARE_PRIVATE(QWSEventDispatcherGlib)
+
+public:
+ explicit QWSEventDispatcherGlib(QObject *parent = 0);
+ ~QWSEventDispatcherGlib();
+
+ bool processEvents(QEventLoop::ProcessEventsFlags flags);
+
+ void startingUp();
+};
+
+QT_END_NAMESPACE
+
+#endif // QWSEVENTDISPATCHER_GLIB_P_H
diff --git a/src/gui/kernel/qeventdispatcher_mac.mm b/src/gui/kernel/qeventdispatcher_mac.mm
new file mode 100644
index 0000000000..e28f170666
--- /dev/null
+++ b/src/gui/kernel/qeventdispatcher_mac.mm
@@ -0,0 +1,926 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/****************************************************************************
+**
+** Copyright (c) 2007-2008, Apple, Inc.
+**
+** All rights reserved.
+**
+** Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are met:
+**
+** * Redistributions of source code must retain the above copyright notice,
+** this list of conditions and the following disclaimer.
+**
+** * Redistributions in binary form must reproduce the above copyright notice,
+** this list of conditions and the following disclaimer in the documentation
+** and/or other materials provided with the distribution.
+**
+** * Neither the name of Apple, Inc. nor the names of its contributors
+** may be used to endorse or promote products derived from this software
+** without specific prior written permission.
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+** CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+** EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+** PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+** PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+** LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+** NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+** SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+**
+****************************************************************************/
+
+#include "qplatformdefs.h"
+#include "private/qt_mac_p.h"
+#include "qeventdispatcher_mac_p.h"
+#include "qapplication.h"
+#include "qevent.h"
+#include "qdialog.h"
+#include "qhash.h"
+#include "qsocketnotifier.h"
+#include "private/qwidget_p.h"
+#include "private/qthread_p.h"
+#include "private/qapplication_p.h"
+
+#include <private/qcocoaapplication_mac_p.h>
+#include "private/qt_cocoa_helpers_mac_p.h"
+
+#ifndef QT_NO_THREAD
+# include "qmutex.h"
+
+QT_BEGIN_NAMESPACE
+
+QT_USE_NAMESPACE
+#endif
+
+/*****************************************************************************
+ Externals
+ *****************************************************************************/
+extern void qt_event_request_timer(MacTimerInfo *); //qapplication_mac.cpp
+extern MacTimerInfo *qt_event_get_timer(EventRef); //qapplication_mac.cpp
+extern void qt_event_request_select(QEventDispatcherMac *); //qapplication_mac.cpp
+extern void qt_event_request_updates(); //qapplication_mac.cpp
+extern OSWindowRef qt_mac_window_for(const QWidget *); //qwidget_mac.cpp
+extern bool qt_is_gui_used; //qapplication.cpp
+extern bool qt_sendSpontaneousEvent(QObject*, QEvent*); // qapplication.cpp
+extern bool qt_mac_is_macsheet(const QWidget *); //qwidget_mac.cpp
+
+static inline CFRunLoopRef mainRunLoop()
+{
+#ifndef QT_MAC_USE_COCOA
+ return reinterpret_cast<CFRunLoopRef>(const_cast<void *>(GetCFRunLoopFromEventLoop(GetMainEventLoop())));
+#else
+ return CFRunLoopGetMain();
+#endif
+}
+
+/*****************************************************************************
+ Timers stuff
+ *****************************************************************************/
+
+/* timer call back */
+void QEventDispatcherMacPrivate::activateTimer(CFRunLoopTimerRef, void *info)
+{
+ int timerID =
+#ifdef Q_OS_MAC64
+ qint64(info);
+#else
+ int(info);
+#endif
+
+ MacTimerInfo *tmr;
+ tmr = macTimerHash.value(timerID);
+ if (tmr == 0 || tmr->pending == true)
+ return; // Can't send another timer event if it's pending.
+
+ tmr->pending = true;
+ QTimerEvent e(tmr->id);
+ qt_sendSpontaneousEvent(tmr->obj, &e);
+
+ // Get the value again in case the timer gets unregistered during the sendEvent.
+ tmr = macTimerHash.value(timerID);
+ if (tmr != 0)
+ tmr->pending = false;
+}
+
+void QEventDispatcherMac::registerTimer(int timerId, int interval, QObject *obj)
+{
+#ifndef QT_NO_DEBUG
+ if (timerId < 1 || interval < 0 || !obj) {
+ qWarning("QEventDispatcherMac::registerTimer: invalid arguments");
+ return;
+ } else if (obj->thread() != thread() || thread() != QThread::currentThread()) {
+ qWarning("QObject::startTimer: timers cannot be started from another thread");
+ return;
+ }
+#endif
+
+ MacTimerInfo *t = new MacTimerInfo();
+ t->id = timerId;
+ t->interval = interval;
+ t->obj = obj;
+ t->runLoopTimer = 0;
+ t->pending = false;
+
+ CFAbsoluteTime fireDate = CFAbsoluteTimeGetCurrent();
+ CFTimeInterval cfinterval = qMax(CFTimeInterval(interval) / 1000, 0.0000001);
+ fireDate += cfinterval;
+ QEventDispatcherMacPrivate::macTimerHash.insert(timerId, t);
+ CFRunLoopTimerContext info = { 0, (void *)timerId, 0, 0, 0 };
+ t->runLoopTimer = CFRunLoopTimerCreate(0, fireDate, cfinterval, 0, 0,
+ QEventDispatcherMacPrivate::activateTimer, &info);
+ if (t->runLoopTimer == 0) {
+ qFatal("QEventDispatcherMac::registerTimer: Cannot create timer");
+ }
+ CFRunLoopAddTimer(mainRunLoop(), t->runLoopTimer, kCFRunLoopCommonModes);
+}
+
+bool QEventDispatcherMac::unregisterTimer(int identifier)
+{
+#ifndef QT_NO_DEBUG
+ if (identifier < 1) {
+ qWarning("QEventDispatcherMac::unregisterTimer: invalid argument");
+ return false;
+ } else if (thread() != QThread::currentThread()) {
+ qWarning("QObject::killTimer: timers cannot be stopped from another thread");
+ return false;
+ }
+#endif
+ if (identifier <= 0)
+ return false; // not init'd or invalid timer
+
+ MacTimerInfo *timerInfo = QEventDispatcherMacPrivate::macTimerHash.take(identifier);
+ if (timerInfo == 0)
+ return false;
+
+ if (!QObjectPrivate::get(timerInfo->obj)->inThreadChangeEvent)
+ QAbstractEventDispatcherPrivate::releaseTimerId(identifier);
+ CFRunLoopTimerInvalidate(timerInfo->runLoopTimer);
+ CFRelease(timerInfo->runLoopTimer);
+ delete timerInfo;
+
+ return true;
+}
+
+bool QEventDispatcherMac::unregisterTimers(QObject *obj)
+{
+#ifndef QT_NO_DEBUG
+ if (!obj) {
+ qWarning("QEventDispatcherMac::unregisterTimers: invalid argument");
+ return false;
+ } else if (obj->thread() != thread() || thread() != QThread::currentThread()) {
+ qWarning("QObject::killTimers: timers cannot be stopped from another thread");
+ return false;
+ }
+#endif
+
+ MacTimerHash::iterator it = QEventDispatcherMacPrivate::macTimerHash.begin();
+ while (it != QEventDispatcherMacPrivate::macTimerHash.end()) {
+ MacTimerInfo *timerInfo = it.value();
+ if (timerInfo->obj != obj) {
+ ++it;
+ } else {
+ if (!QObjectPrivate::get(timerInfo->obj)->inThreadChangeEvent)
+ QAbstractEventDispatcherPrivate::releaseTimerId(timerInfo->id);
+ CFRunLoopTimerInvalidate(timerInfo->runLoopTimer);
+ CFRelease(timerInfo->runLoopTimer);
+ delete timerInfo;
+ it = QEventDispatcherMacPrivate::macTimerHash.erase(it);
+ }
+ }
+ return true;
+}
+
+QList<QEventDispatcherMac::TimerInfo>
+QEventDispatcherMac::registeredTimers(QObject *object) const
+{
+ if (!object) {
+ qWarning("QEventDispatcherMac:registeredTimers: invalid argument");
+ return QList<TimerInfo>();
+ }
+
+ QList<TimerInfo> list;
+
+ MacTimerHash::const_iterator it = QEventDispatcherMacPrivate::macTimerHash.constBegin();
+ while (it != QEventDispatcherMacPrivate::macTimerHash.constEnd()) {
+ MacTimerInfo *t = it.value();
+ if (t->obj == object)
+ list << TimerInfo(t->id, t->interval);
+ ++it;
+ }
+ return list;
+}
+
+/**************************************************************************
+ Socket Notifiers
+ *************************************************************************/
+void qt_mac_socket_callback(CFSocketRef s, CFSocketCallBackType callbackType, CFDataRef,
+ const void *, void *info) {
+ QEventDispatcherMacPrivate *const eventDispatcher
+ = static_cast<QEventDispatcherMacPrivate *>(info);
+ int nativeSocket = CFSocketGetNative(s);
+ MacSocketInfo *socketInfo = eventDispatcher->macSockets.value(nativeSocket);
+ QEvent notifierEvent(QEvent::SockAct);
+ if (callbackType == kCFSocketReadCallBack) {
+ Q_ASSERT(socketInfo->readNotifier);
+ QApplication::sendEvent(socketInfo->readNotifier, &notifierEvent);
+ } else if (callbackType == kCFSocketWriteCallBack) {
+ // ### Bug in Apple socket notifiers seems to send write even
+ // ### after the notifier has been disabled, need to investigate further.
+ if (socketInfo->writeNotifier)
+ QApplication::sendEvent(socketInfo->writeNotifier, &notifierEvent);
+ }
+}
+
+/*
+ Adds a loop source for the given socket to the current run loop.
+*/
+CFRunLoopSourceRef qt_mac_add_socket_to_runloop(const CFSocketRef socket)
+{
+ CFRunLoopSourceRef loopSource = CFSocketCreateRunLoopSource(kCFAllocatorDefault, socket, 0);
+ if (!loopSource)
+ return 0;
+
+ CFRunLoopAddSource(mainRunLoop(), loopSource, kCFRunLoopCommonModes);
+ return loopSource;
+}
+
+/*
+ Removes the loop source for the given socket from the current run loop.
+*/
+void qt_mac_remove_socket_from_runloop(const CFSocketRef socket, CFRunLoopSourceRef runloop)
+{
+ Q_ASSERT(runloop);
+ CFRunLoopRemoveSource(mainRunLoop(), runloop, kCFRunLoopCommonModes);
+ CFSocketDisableCallBacks(socket, kCFSocketReadCallBack);
+ CFSocketDisableCallBacks(socket, kCFSocketWriteCallBack);
+ CFRunLoopSourceInvalidate(runloop);
+}
+
+/*
+ Register a QSocketNotifier with the mac event system by creating a CFSocket with
+ with a read/write callback.
+
+ Qt has separate socket notifiers for reading and writing, but on the mac there is
+ a limitation of one CFSocket object for each native socket.
+*/
+void QEventDispatcherMac::registerSocketNotifier(QSocketNotifier *notifier)
+{
+ Q_ASSERT(notifier);
+ int nativeSocket = notifier->socket();
+ int type = notifier->type();
+#ifndef QT_NO_DEBUG
+ if (nativeSocket < 0 || nativeSocket > FD_SETSIZE) {
+ qWarning("QSocketNotifier: Internal error");
+ return;
+ } else if (notifier->thread() != thread()
+ || thread() != QThread::currentThread()) {
+ qWarning("QSocketNotifier: socket notifiers cannot be enabled from another thread");
+ return;
+ }
+#endif
+
+ Q_D(QEventDispatcherMac);
+
+ if (type == QSocketNotifier::Exception) {
+ qWarning("QSocketNotifier::Exception is not supported on Mac OS X");
+ return;
+ }
+
+ // Check if we have a CFSocket for the native socket, create one if not.
+ MacSocketInfo *socketInfo = d->macSockets.value(nativeSocket);
+ if (!socketInfo) {
+ socketInfo = new MacSocketInfo();
+
+ // Create CFSocket, specify that we want both read and write callbacks (the callbacks
+ // are enabled/disabled later on).
+ const int callbackTypes = kCFSocketReadCallBack | kCFSocketWriteCallBack;
+ CFSocketContext context = {0, d, 0, 0, 0};
+ socketInfo->socket = CFSocketCreateWithNative(kCFAllocatorDefault, nativeSocket, callbackTypes, qt_mac_socket_callback, &context);
+ if (CFSocketIsValid(socketInfo->socket) == false) {
+ qWarning("QEventDispatcherMac::registerSocketNotifier: Failed to create CFSocket");
+ return;
+ }
+
+ CFOptionFlags flags = CFSocketGetSocketFlags(socketInfo->socket);
+ flags |= kCFSocketAutomaticallyReenableWriteCallBack; //QSocketNotifier stays enabled after a write
+ flags &= ~kCFSocketCloseOnInvalidate; //QSocketNotifier doesn't close the socket upon destruction/invalidation
+ CFSocketSetSocketFlags(socketInfo->socket, flags);
+
+ // Add CFSocket to runloop.
+ if(!(socketInfo->runloop = qt_mac_add_socket_to_runloop(socketInfo->socket))) {
+ qWarning("QEventDispatcherMac::registerSocketNotifier: Failed to add CFSocket to runloop");
+ CFSocketInvalidate(socketInfo->socket);
+ CFRelease(socketInfo->socket);
+ return;
+ }
+
+ // Disable both callback types by default. This must be done after
+ // we add the CFSocket to the runloop, or else these calls will have
+ // no effect.
+ CFSocketDisableCallBacks(socketInfo->socket, kCFSocketReadCallBack);
+ CFSocketDisableCallBacks(socketInfo->socket, kCFSocketWriteCallBack);
+
+ d->macSockets.insert(nativeSocket, socketInfo);
+ }
+
+ // Increment read/write counters and select enable callbacks if necessary.
+ if (type == QSocketNotifier::Read) {
+ Q_ASSERT(socketInfo->readNotifier == 0);
+ socketInfo->readNotifier = notifier;
+ CFSocketEnableCallBacks(socketInfo->socket, kCFSocketReadCallBack);
+ } else if (type == QSocketNotifier::Write) {
+ Q_ASSERT(socketInfo->writeNotifier == 0);
+ socketInfo->writeNotifier = notifier;
+ CFSocketEnableCallBacks(socketInfo->socket, kCFSocketWriteCallBack);
+ }
+}
+
+/*
+ Unregister QSocketNotifer. The CFSocket correspoding to this notifier is
+ removed from the runloop of this is the last notifier that users
+ that CFSocket.
+*/
+void QEventDispatcherMac::unregisterSocketNotifier(QSocketNotifier *notifier)
+{
+ Q_ASSERT(notifier);
+ int nativeSocket = notifier->socket();
+ int type = notifier->type();
+#ifndef QT_NO_DEBUG
+ if (nativeSocket < 0 || nativeSocket > FD_SETSIZE) {
+ qWarning("QSocketNotifier: Internal error");
+ return;
+ } else if (notifier->thread() != thread() || thread() != QThread::currentThread()) {
+ qWarning("QSocketNotifier: socket notifiers cannot be disabled from another thread");
+ return;
+ }
+#endif
+
+ Q_D(QEventDispatcherMac);
+
+ if (type == QSocketNotifier::Exception) {
+ qWarning("QSocketNotifier::Exception is not supported on Mac OS X");
+ return;
+ }
+ MacSocketInfo *socketInfo = d->macSockets.value(nativeSocket);
+ if (!socketInfo) {
+ qWarning("QEventDispatcherMac::unregisterSocketNotifier: Tried to unregister a not registered notifier");
+ return;
+ }
+
+ // Decrement read/write counters and disable callbacks if necessary.
+ if (type == QSocketNotifier::Read) {
+ Q_ASSERT(notifier == socketInfo->readNotifier);
+ socketInfo->readNotifier = 0;
+ CFSocketDisableCallBacks(socketInfo->socket, kCFSocketReadCallBack);
+ } else if (type == QSocketNotifier::Write) {
+ Q_ASSERT(notifier == socketInfo->writeNotifier);
+ socketInfo->writeNotifier = 0;
+ CFSocketDisableCallBacks(socketInfo->socket, kCFSocketWriteCallBack);
+ }
+
+ // Remove CFSocket from runloop if this was the last QSocketNotifier.
+ if (socketInfo->readNotifier == 0 && socketInfo->writeNotifier == 0) {
+ if (CFSocketIsValid(socketInfo->socket))
+ qt_mac_remove_socket_from_runloop(socketInfo->socket, socketInfo->runloop);
+ CFRunLoopSourceInvalidate(socketInfo->runloop);
+ CFRelease(socketInfo->runloop);
+ CFSocketInvalidate(socketInfo->socket);
+ CFRelease(socketInfo->socket);
+ delete socketInfo;
+ d->macSockets.remove(nativeSocket);
+ }
+}
+
+bool QEventDispatcherMac::hasPendingEvents()
+{
+ extern uint qGlobalPostedEventsCount();
+ return qGlobalPostedEventsCount() || (qt_is_gui_used && GetNumEventsInQueue(GetMainEventQueue()));
+}
+
+
+static bool qt_mac_send_event(QEventLoop::ProcessEventsFlags, OSEventRef event, OSWindowRef pt)
+{
+#ifndef QT_MAC_USE_COCOA
+ if(pt && SendEventToWindow(event, pt) != eventNotHandledErr)
+ return true;
+ return !SendEventToEventTarget(event, GetEventDispatcherTarget());
+#else // QT_MAC_USE_COCOA
+ if (pt)
+ [pt sendEvent:event];
+ else
+ [NSApp sendEvent:event];
+ return true;
+#endif
+}
+
+#ifdef QT_MAC_USE_COCOA
+static bool IsMouseOrKeyEvent( NSEvent* event )
+{
+ bool result = false;
+
+ switch( [event type] )
+ {
+ case NSLeftMouseDown:
+ case NSLeftMouseUp:
+ case NSRightMouseDown:
+ case NSRightMouseUp:
+ case NSMouseMoved: // ??
+ case NSLeftMouseDragged:
+ case NSRightMouseDragged:
+ case NSMouseEntered:
+ case NSMouseExited:
+ case NSKeyDown:
+ case NSKeyUp:
+ case NSFlagsChanged: // key modifiers changed?
+ case NSCursorUpdate: // ??
+ case NSScrollWheel:
+ case NSTabletPoint:
+ case NSTabletProximity:
+ case NSOtherMouseDown:
+ case NSOtherMouseUp:
+ case NSOtherMouseDragged:
+ result = true;
+ break;
+
+ default:
+ break;
+ }
+ return result;
+}
+#endif
+
+bool QEventDispatcherMac::processEvents(QEventLoop::ProcessEventsFlags flags)
+{
+ Q_D(QEventDispatcherMac);
+ d->interrupt = false;
+ // In case we end up recursing while we now process events, make sure
+ // that we send remaining posted Qt events before this call returns:
+ wakeUp();
+ emit awake();
+
+#ifndef QT_MAC_NO_QUICKDRAW
+ if(!qt_mac_safe_pdev) { //create an empty widget and this can be used for a port anytime
+ QWidget *tlw = new QWidget;
+ tlw->setAttribute(Qt::WA_DeleteOnClose);
+ tlw->setObjectName(QLatin1String("empty_widget"));
+ tlw->hide();
+ qt_mac_safe_pdev = tlw;
+ }
+#endif
+
+ bool retVal = false;
+ forever {
+ if (d->interrupt)
+ break;
+
+#ifdef QT_MAC_USE_COCOA
+ QMacCocoaAutoReleasePool pool;
+ NSEvent* event = 0;
+
+ if (flags & QEventLoop::DialogExec || flags & QEventLoop::EventLoopExec) {
+ // The point of the CocoaRequestModal event is to make sure that a
+ // non-execed app modal window recurses into it's own dialog exec
+ // once cocoa is spinning the event loop for us (e.g on top of [NSApp run]).
+ // We expect only one event to notify us about this, regardless of how many
+ // widgets that are waiting to be modal. So we remove all other pending
+ // events, if any. And since cocoa will now take over event processing for us,
+ // we allow new app modal widgets to recurse on top of us, hence the release of
+ // the block:
+ QBoolBlocker block(d->blockCocoaRequestModal, false);
+ QCoreApplication::removePostedEvents(qApp, QEvent::CocoaRequestModal);
+
+ if (NSModalSession session = d->activeModalSession())
+ while ([NSApp runModalSession:session] == NSRunContinuesResponse) {
+ // runModalSession will not wait for events, so we do it
+ // ourselves (otherwise we would spend 100% CPU inside this loop):
+ event = [NSApp nextEventMatchingMask:NSAnyEventMask
+ untilDate:[NSDate distantFuture] inMode:NSModalPanelRunLoopMode dequeue:YES];
+ if (event)
+ [NSApp postEvent:event atStart:YES];
+ }
+ else
+ [NSApp run];
+
+ d->rebuildModalSessionStack(false);
+ retVal = true;
+ } else do {
+ // Since we now are going to spin the event loop just _one_ round
+ // we need to block all incoming CocoaRequestModal events to ensure
+ // that we don't recurse into a new exec-ing event loop while doing
+ // so (and as such, 'hang' the thread inside the recursion):
+ QBoolBlocker block(d->blockCocoaRequestModal, true);
+ bool mustRelease = false;
+
+ if (!(flags & QEventLoop::ExcludeUserInputEvents) && !d->queuedUserInputEvents.isEmpty()) {
+ // process a pending user input event
+ mustRelease = true;
+ event = static_cast<NSEvent *>(d->queuedUserInputEvents.takeFirst());
+ } else {
+ if (NSModalSession session = d->activeModalSession()) {
+ // There's s a modal widget showing, run it's session:
+ if (flags & QEventLoop::WaitForMoreEvents) {
+ // Wait for at least one event
+ // before spinning the session:
+ event = [NSApp nextEventMatchingMask:NSAnyEventMask
+ untilDate:[NSDate distantFuture] inMode:NSModalPanelRunLoopMode dequeue:YES];
+ if (event)
+ [NSApp postEvent:event atStart:YES];
+ }
+ [NSApp runModalSession:session];
+ retVal = true;
+ break;
+ } else {
+ event = [NSApp nextEventMatchingMask:NSAnyEventMask
+ untilDate:nil
+ inMode:NSDefaultRunLoopMode
+ dequeue: YES];
+
+ if (event != nil) {
+ if (flags & QEventLoop::ExcludeUserInputEvents) {
+ if (IsMouseOrKeyEvent(event)) {
+ // retain event here?
+ [event retain];
+ d->queuedUserInputEvents.append(event);
+ continue;
+ }
+ }
+ }
+ }
+ }
+ if (event) {
+ if (!filterEvent(event) && qt_mac_send_event(flags, event, 0))
+ retVal = true;
+ if (mustRelease)
+ [event release];
+ }
+ } while(!d->interrupt && event != nil);
+
+#else
+ do {
+ EventRef event;
+ if (!(flags & QEventLoop::ExcludeUserInputEvents)
+ && !d->queuedUserInputEvents.isEmpty()) {
+ // process a pending user input event
+ event = static_cast<EventRef>(d->queuedUserInputEvents.takeFirst());
+ } else {
+ OSStatus err = ReceiveNextEvent(0,0, kEventDurationNoWait, true, &event);
+ if(err != noErr)
+ continue;
+ // else
+ if (flags & QEventLoop::ExcludeUserInputEvents) {
+ UInt32 ekind = GetEventKind(event),
+ eclass = GetEventClass(event);
+ switch(eclass) {
+ case kEventClassQt:
+ if(ekind != kEventQtRequestContext)
+ break;
+ // fall through
+ case kEventClassMouse:
+ case kEventClassKeyboard:
+ d->queuedUserInputEvents.append(event);
+ continue;
+ }
+ }
+ }
+
+ if (!filterEvent(&event) && qt_mac_send_event(flags, event, 0))
+ retVal = true;
+ ReleaseEvent(event);
+ } while(!d->interrupt && GetNumEventsInQueue(GetMainEventQueue()) > 0);
+
+#endif
+
+ bool canWait = (d->threadData->canWait
+ && !retVal
+ && !d->interrupt
+ && (flags & QEventLoop::WaitForMoreEvents));
+ if (canWait) {
+ // INVARIANT: We haven't processed any events yet. And we're told
+ // to stay inside this function until at least one event is processed
+ // (WaitForMoreEvents). So we wait on the window server:
+#ifndef QT_MAC_USE_COCOA
+ while(CFRunLoopRunInMode(kCFRunLoopDefaultMode, 1.0e20, true) == kCFRunLoopRunTimedOut);
+#else
+ QMacCocoaAutoReleasePool pool;
+ NSEvent *manualEvent = [NSApp nextEventMatchingMask:NSAnyEventMask
+ untilDate:[NSDate distantFuture] inMode:NSDefaultRunLoopMode
+ dequeue:YES];
+ if (manualEvent)
+ [NSApp sendEvent:manualEvent];
+#endif
+ flags &= ~QEventLoop::WaitForMoreEvents;
+ } else {
+ // Done with event processing for now. Leave the function:
+ break;
+ }
+ }
+
+ // Because pending deffered-delete events are only sendt after
+ // returning from the loop level they were posted in, we schedule
+ // an extra wakup to force the _current_ run loop to process them (in
+ // case the application stands idle waiting for the delete event):
+ wakeUp();
+
+ if (d->interrupt){
+ // We restart NSApplication by first stopping it, and then call 'run'
+ // again (NSApplication is actually already stopped, hence the need
+ // for a restart, but calling stop again will also make the call
+ // return from the current recursion). When the call returns to
+ // QEventLoop (mind, not from this recursion, but from the one we're
+ // about to stop), it will just call QEventDispatcherMac::processEvents()
+ // again.
+ interrupt();
+ }
+ return retVal;
+}
+
+void QEventDispatcherMac::wakeUp()
+{
+ Q_D(QEventDispatcherMac);
+ d->serialNumber.ref();
+ CFRunLoopSourceSignal(d->postedEventsSource);
+ CFRunLoopWakeUp(mainRunLoop());
+}
+
+void QEventDispatcherMac::flush()
+{
+ if(qApp) {
+ QWidgetList tlws = QApplication::topLevelWidgets();
+ for(int i = 0; i < tlws.size(); i++) {
+ QWidget *tlw = tlws.at(i);
+ if(tlw->isVisible())
+ macWindowFlush(qt_mac_window_for(tlw));
+ }
+ }
+}
+
+/*****************************************************************************
+ QEventDispatcherMac Implementation
+ *****************************************************************************/
+MacTimerHash QEventDispatcherMacPrivate::macTimerHash;
+bool QEventDispatcherMacPrivate::blockSendPostedEvents = false;
+
+#ifdef QT_MAC_USE_COCOA
+QStack<QCocoaModalSessionInfo> QEventDispatcherMacPrivate::cocoaModalSessionStack;
+bool QEventDispatcherMacPrivate::blockCocoaRequestModal = false;
+
+static void qt_mac_setChildDialogsResponsive(QWidget *widget, bool responsive)
+{
+ QList<QDialog *> dialogs = widget->findChildren<QDialog *>();
+ for (int i=0; i<dialogs.size(); ++i){
+ NSWindow *window = qt_mac_window_for(dialogs[i]);
+ if (window && [window isKindOfClass:[NSPanel class]]) {
+ [static_cast<NSPanel *>(window) setWorksWhenModal:responsive];
+ if (responsive && dialogs[i]->isVisible()){
+ [window orderFront:window];
+ }
+ }
+ }
+}
+
+NSModalSession QEventDispatcherMacPrivate::activeModalSession()
+{
+ // Create (if needed) and return the modal session
+ // for the top-most modal dialog, if any:
+ if (cocoaModalSessionStack.isEmpty())
+ return 0;
+ QCocoaModalSessionInfo &info = cocoaModalSessionStack.last();
+ if (!info.widget)
+ return 0;
+ if (info.widget->testAttribute(Qt::WA_DontShowOnScreen)){
+ // INVARIANT: We have a modal widget, but it's not visible on screen.
+ // This will e.g. be true for native dialogs. Make the dialog children
+ // of the previous modal dialog unresponsive, so that the current dialog
+ // (native or not) is the only reponsive dialog on screen:
+ int size = cocoaModalSessionStack.size();
+ if (size > 1){
+ if (QWidget *prevModal = cocoaModalSessionStack[size-2].widget)
+ qt_mac_setChildDialogsResponsive(prevModal, false);
+ }
+ return 0;
+ }
+
+ if (!info.session) {
+ QMacCocoaAutoReleasePool pool;
+ NSWindow *window = qt_mac_window_for(info.widget);
+ if (!window)
+ return 0;
+ // 'beginModalSessionForWindow' will give the event loop a spin, and as
+ // such, deliver Qt events. This might lead to inconsistent behaviour
+ // (especially if CocoaRequestModal is delivered), so we need to block:
+ QBoolBlocker block(blockSendPostedEvents, true);
+ info.session = [NSApp beginModalSessionForWindow:window];
+ // Make the dialog children of the current modal dialog
+ // responsive. And make the dialog children of
+ // the previous modal dialog unresponsive again:
+ qt_mac_setChildDialogsResponsive(info.widget, true);
+ int size = cocoaModalSessionStack.size();
+ if (size > 1){
+ if (QWidget *prevModal = cocoaModalSessionStack[size-2].widget)
+ qt_mac_setChildDialogsResponsive(prevModal, false);
+ }
+ }
+ return info.session;
+}
+
+void QEventDispatcherMacPrivate::rebuildModalSessionStack(bool pop)
+{
+ // Calling [NSApp stopModal], or [NSApp stop], will stop all modal dialogs
+ // in one go. So to to not confuse cocoa, we need to stop all our modal
+ // sessions as well. QMacEventDispatcher will make them modal again
+ // in the correct order as long as they are left on the cocoaModalSessionStack
+ // and a CocoaRequestModal is posted:
+ if (cocoaModalSessionStack.isEmpty())
+ return;
+
+ QMacCocoaAutoReleasePool pool;
+ [NSApp stopModal];
+ [NSApp postEvent:[NSEvent otherEventWithType:NSApplicationDefined location:NSZeroPoint
+ modifierFlags:0 timestamp:0. windowNumber:0 context:0
+ subtype:SHRT_MAX data1:0 data2:0] atStart:NO];
+
+ for (int i=0; i<cocoaModalSessionStack.size(); ++i){
+ QCocoaModalSessionInfo &info = cocoaModalSessionStack[i];
+ if (info.session) {
+ [NSApp endModalSession:info.session];
+ info.session = 0;
+ }
+ }
+
+ if (pop) {
+ QCocoaModalSessionInfo info = cocoaModalSessionStack.pop();
+ if (info.widget)
+ qt_mac_setChildDialogsResponsive(info.widget, false);
+ }
+
+ if (!cocoaModalSessionStack.isEmpty()) {
+ // Since we now have pending modal sessions again, make
+ // sure that we enter modal for the one on the top later:
+ qApp->postEvent(qApp, new QEvent(QEvent::CocoaRequestModal));
+ } else {
+ QCoreApplication::removePostedEvents(qApp, QEvent::CocoaRequestModal);
+ }
+}
+
+#endif
+
+QEventDispatcherMacPrivate::QEventDispatcherMacPrivate()
+ : interrupt(false)
+{
+}
+
+QEventDispatcherMac::QEventDispatcherMac(QObject *parent)
+ : QAbstractEventDispatcher(*new QEventDispatcherMacPrivate, parent)
+{
+ Q_D(QEventDispatcherMac);
+ CFRunLoopSourceContext context;
+ bzero(&context, sizeof(CFRunLoopSourceContext));
+ context.info = d;
+ context.equal = QEventDispatcherMacPrivate::postedEventSourceEqualCallback;
+ context.perform = QEventDispatcherMacPrivate::postedEventsSourcePerformCallback;
+ d->postedEventsSource = CFRunLoopSourceCreate(0, 0, &context);
+ Q_ASSERT(d->postedEventsSource);
+ CFRunLoopAddSource(mainRunLoop(), d->postedEventsSource, kCFRunLoopCommonModes);
+
+ CFRunLoopObserverContext observerContext;
+ bzero(&observerContext, sizeof(CFRunLoopObserverContext));
+ observerContext.info = this;
+ d->waitingObserver = CFRunLoopObserverCreate(kCFAllocatorDefault,
+ kCFRunLoopBeforeWaiting | kCFRunLoopAfterWaiting,
+ true, 0,
+ QEventDispatcherMacPrivate::waitingObserverCallback,
+ &observerContext);
+ CFRunLoopAddObserver(mainRunLoop(), d->waitingObserver, kCFRunLoopCommonModes);
+}
+
+void QEventDispatcherMacPrivate::waitingObserverCallback(CFRunLoopObserverRef,
+ CFRunLoopActivity activity, void *info)
+{
+ if (activity == kCFRunLoopBeforeWaiting)
+ emit static_cast<QEventDispatcherMac*>(info)->aboutToBlock();
+ else
+ emit static_cast<QEventDispatcherMac*>(info)->awake();
+}
+
+Boolean QEventDispatcherMacPrivate::postedEventSourceEqualCallback(const void *info1, const void *info2)
+{
+ return info1 == info2;
+}
+
+void QEventDispatcherMacPrivate::postedEventsSourcePerformCallback(void *info)
+{
+ QEventDispatcherMacPrivate *d = static_cast<QEventDispatcherMacPrivate *>(info);
+ if (blockSendPostedEvents) {
+ CFRunLoopSourceSignal(d->postedEventsSource);
+ } else {
+ if (!d->threadData->canWait || (d->serialNumber != d->lastSerial)) {
+ d->lastSerial = d->serialNumber;
+ QApplicationPrivate::sendPostedEvents(0, 0, d->threadData);
+ }
+ }
+}
+
+#ifdef QT_MAC_USE_COCOA
+static void stopNSApp()
+{
+ QMacCocoaAutoReleasePool pool;
+ static const short NSAppShouldStopForQt = SHRT_MAX;
+ [NSApp stop:NSApp];
+ [NSApp postEvent:[NSEvent otherEventWithType:NSApplicationDefined location:NSZeroPoint
+ modifierFlags:0 timestamp:0. windowNumber:0 context:0
+ subtype:NSAppShouldStopForQt data1:0 data2:0] atStart:NO];
+}
+#endif
+
+void QEventDispatcherMac::interrupt()
+{
+ Q_D(QEventDispatcherMac);
+ d->interrupt = true;
+ wakeUp();
+#ifndef QT_MAC_USE_COCOA
+ CFRunLoopStop(mainRunLoop());
+#else
+ stopNSApp();
+#endif
+}
+
+QEventDispatcherMac::~QEventDispatcherMac()
+{
+ Q_D(QEventDispatcherMac);
+ //timer cleanup
+ MacTimerHash::iterator it = QEventDispatcherMacPrivate::macTimerHash.begin();
+ while (it != QEventDispatcherMacPrivate::macTimerHash.end()) {
+ MacTimerInfo *t = it.value();
+ if (t->runLoopTimer) {
+ CFRunLoopTimerInvalidate(t->runLoopTimer);
+ CFRelease(t->runLoopTimer);
+ }
+ delete t;
+ ++it;
+ }
+ QEventDispatcherMacPrivate::macTimerHash.clear();
+
+ // Remove CFSockets from the runloop.
+ for (MacSocketHash::ConstIterator it = d->macSockets.constBegin(); it != d->macSockets.constEnd(); ++it) {
+ MacSocketInfo *socketInfo = (*it);
+ if (CFSocketIsValid(socketInfo->socket)) {
+ qt_mac_remove_socket_from_runloop(socketInfo->socket, socketInfo->runloop);
+ CFRunLoopSourceInvalidate(socketInfo->runloop);
+ CFRelease(socketInfo->runloop);
+ CFSocketInvalidate(socketInfo->socket);
+ CFRelease(socketInfo->socket);
+ }
+ }
+ CFRunLoopRemoveSource(mainRunLoop(), d->postedEventsSource, kCFRunLoopCommonModes);
+ CFRelease(d->postedEventsSource);
+
+ CFRunLoopObserverInvalidate(d->waitingObserver);
+ CFRelease(d->waitingObserver);
+}
+
+
+QT_END_NAMESPACE
diff --git a/src/gui/kernel/qeventdispatcher_mac_p.h b/src/gui/kernel/qeventdispatcher_mac_p.h
new file mode 100644
index 0000000000..455d4236bb
--- /dev/null
+++ b/src/gui/kernel/qeventdispatcher_mac_p.h
@@ -0,0 +1,197 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/****************************************************************************
+**
+** Copyright (c) 2007-2008, Apple, Inc.
+**
+** All rights reserved.
+**
+** Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are met:
+**
+** * Redistributions of source code must retain the above copyright notice,
+** this list of conditions and the following disclaimer.
+**
+** * Redistributions in binary form must reproduce the above copyright notice,
+** this list of conditions and the following disclaimer in the documentation
+** and/or other materials provided with the distribution.
+**
+** * Neither the name of Apple, Inc. nor the names of its contributors
+** may be used to endorse or promote products derived from this software
+** without specific prior written permission.
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+** CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+** EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+** PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+** PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+** LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+** NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+** SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+**
+****************************************************************************/
+
+#ifndef QEVENTDISPATCHER_MAC_P_H
+#define QEVENTDISPATCHER_MAC_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 <QtGui/qwindowdefs.h>
+#include <QtCore/qhash.h>
+#include <QtCore/qstack.h>
+#include "private/qabstracteventdispatcher_p.h"
+#include "private/qt_mac_p.h"
+
+QT_BEGIN_NAMESPACE
+
+#ifdef QT_MAC_USE_COCOA
+typedef struct _NSModalSession *NSModalSession;
+typedef struct _QCocoaModalSessionInfo {
+ QPointer<QWidget> widget;
+ NSModalSession session;
+} QCocoaModalSessionInfo;
+#endif
+
+class QEventDispatcherMacPrivate;
+
+class QEventDispatcherMac : public QAbstractEventDispatcher
+{
+ Q_OBJECT
+ Q_DECLARE_PRIVATE(QEventDispatcherMac)
+
+public:
+ explicit QEventDispatcherMac(QObject *parent = 0);
+ ~QEventDispatcherMac();
+
+ bool processEvents(QEventLoop::ProcessEventsFlags flags);
+ bool hasPendingEvents();
+
+ void registerSocketNotifier(QSocketNotifier *notifier);
+ void unregisterSocketNotifier(QSocketNotifier *notifier);
+
+ void registerTimer(int timerId, int interval, QObject *object);
+ bool unregisterTimer(int timerId);
+ bool unregisterTimers(QObject *object);
+ QList<TimerInfo> registeredTimers(QObject *object) const;
+
+ void wakeUp();
+ void flush();
+ void interrupt();
+
+private:
+ friend void qt_mac_select_timer_callbk(__EventLoopTimer*, void*);
+ friend class QApplicationPrivate;
+};
+
+struct MacTimerInfo {
+ int id;
+ int interval;
+ QObject *obj;
+ bool pending;
+ CFRunLoopTimerRef runLoopTimer;
+ bool operator==(const MacTimerInfo &other)
+ {
+ return (id == other.id);
+ }
+};
+typedef QHash<int, MacTimerInfo *> MacTimerHash;
+
+struct MacSocketInfo {
+ MacSocketInfo() : socket(0), runloop(0), readNotifier(0), writeNotifier(0) {}
+ CFSocketRef socket;
+ CFRunLoopSourceRef runloop;
+ QObject *readNotifier;
+ QObject *writeNotifier;
+};
+typedef QHash<int, MacSocketInfo *> MacSocketHash;
+
+class QEventDispatcherMacPrivate : public QAbstractEventDispatcherPrivate
+{
+ Q_DECLARE_PUBLIC(QEventDispatcherMac)
+
+public:
+ QEventDispatcherMacPrivate();
+
+ static MacTimerHash macTimerHash;
+ // Set 'blockSendPostedEvents' to true if you _really_ need
+ // to make sure that qt events are not posted while calling
+ // low-level cocoa functions (like beginModalForWindow). And
+ // use a QBoolBlocker to be safe:
+ static bool blockSendPostedEvents;
+#ifdef QT_MAC_USE_COCOA
+ // The following variables help organizing modal sessions:
+ static QStack<QCocoaModalSessionInfo> cocoaModalSessionStack;
+ static bool blockCocoaRequestModal;
+ static NSModalSession activeModalSession();
+ static void rebuildModalSessionStack(bool pop);
+#endif
+
+ MacSocketHash macSockets;
+ QList<void *> queuedUserInputEvents; // List of EventRef in Carbon, and NSEvent * in Cocoa
+ CFRunLoopSourceRef postedEventsSource;
+ CFRunLoopObserverRef waitingObserver;
+ QAtomicInt serialNumber;
+ int lastSerial;
+ bool interrupt;
+private:
+ static Boolean postedEventSourceEqualCallback(const void *info1, const void *info2);
+ static void postedEventsSourcePerformCallback(void *info);
+ static void activateTimer(CFRunLoopTimerRef, void *info);
+ static void waitingObserverCallback(CFRunLoopObserverRef observer,
+ CFRunLoopActivity activity, void *info);
+};
+
+QT_END_NAMESPACE
+
+#endif // QEVENTDISPATCHER_MAC_P_H
diff --git a/src/gui/kernel/qeventdispatcher_qws.cpp b/src/gui/kernel/qeventdispatcher_qws.cpp
new file mode 100644
index 0000000000..6e2cc2b4b6
--- /dev/null
+++ b/src/gui/kernel/qeventdispatcher_qws.cpp
@@ -0,0 +1,168 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qplatformdefs.h"
+#include "qapplication.h"
+#include "private/qwscommand_qws_p.h"
+#include "qwsdisplay_qws.h"
+#include "qwsevent_qws.h"
+#include "qwindowsystem_qws.h"
+#include "qeventdispatcher_qws_p.h"
+#include "private/qeventdispatcher_unix_p.h"
+#ifndef QT_NO_THREAD
+# include "qmutex.h"
+#endif
+
+#include <errno.h>
+
+QT_BEGIN_NAMESPACE
+
+QT_USE_NAMESPACE
+
+class QEventDispatcherQWSPrivate : public QEventDispatcherUNIXPrivate
+{
+ Q_DECLARE_PUBLIC(QEventDispatcherQWS)
+public:
+ inline QEventDispatcherQWSPrivate()
+ { }
+ QList<QWSEvent*> queuedUserInputEvents;
+};
+
+
+QEventDispatcherQWS::QEventDispatcherQWS(QObject *parent)
+ : QEventDispatcherUNIX(*new QEventDispatcherQWSPrivate, parent)
+{ }
+
+QEventDispatcherQWS::~QEventDispatcherQWS()
+{ }
+
+
+
+// from qapplication_qws.cpp
+extern QWSDisplay* qt_fbdpy; // QWS `display'
+
+//#define ZERO_FOR_THE_MOMENT
+
+bool QEventDispatcherQWS::processEvents(QEventLoop::ProcessEventsFlags flags)
+{
+ Q_D(QEventDispatcherQWS);
+ // process events from the QWS server
+ int nevents = 0;
+
+ // handle gui and posted events
+ d->interrupt = false;
+ QApplication::sendPostedEvents();
+
+ while (!d->interrupt) { // also flushes output buffer ###can be optimized
+ QWSEvent *event;
+ if (!(flags & QEventLoop::ExcludeUserInputEvents)
+ && !d->queuedUserInputEvents.isEmpty()) {
+ // process a pending user input event
+ event = d->queuedUserInputEvents.takeFirst();
+ } else if (qt_fbdpy->eventPending()) {
+ event = qt_fbdpy->getEvent(); // get next event
+ if (flags & QEventLoop::ExcludeUserInputEvents) {
+ // queue user input events
+ if (event->type == QWSEvent::Mouse || event->type == QWSEvent::Key) {
+ d->queuedUserInputEvents.append(event);
+ continue;
+ }
+ }
+ } else {
+ break;
+ }
+
+ if (filterEvent(event)) {
+ delete event;
+ continue;
+ }
+ nevents++;
+
+ bool ret = qApp->qwsProcessEvent(event) == 1;
+ delete event;
+ if (ret) {
+ return true;
+ }
+ }
+
+ if (!d->interrupt) {
+ extern QList<QWSCommand*> *qt_get_server_queue();
+ if (!qt_get_server_queue()->isEmpty()) {
+ QWSServer::processEventQueue();
+ }
+
+ if (QEventDispatcherUNIX::processEvents(flags))
+ return true;
+ }
+ return (nevents > 0);
+}
+
+bool QEventDispatcherQWS::hasPendingEvents()
+{
+ extern uint qGlobalPostedEventsCount(); // from qapplication.cpp
+ return qGlobalPostedEventsCount() || qt_fbdpy->eventPending();
+}
+
+void QEventDispatcherQWS::startingUp()
+{
+
+}
+
+void QEventDispatcherQWS::closingDown()
+{
+
+}
+
+void QEventDispatcherQWS::flush()
+{
+ if(qApp)
+ qApp->sendPostedEvents();
+ (void)qt_fbdpy->eventPending(); // flush
+}
+
+
+int QEventDispatcherQWS::select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds,
+ timeval *timeout)
+{
+ return QEventDispatcherUNIX::select(nfds, readfds, writefds, exceptfds, timeout);
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/kernel/qeventdispatcher_qws_p.h b/src/gui/kernel/qeventdispatcher_qws_p.h
new file mode 100644
index 0000000000..e0c00d1732
--- /dev/null
+++ b/src/gui/kernel/qeventdispatcher_qws_p.h
@@ -0,0 +1,86 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QEVENTDISPATCHER_QWS_P_H
+#define QEVENTDISPATCHER_QWS_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/qeventdispatcher_unix_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class QEventDispatcherQWSPrivate;
+
+class QEventDispatcherQWS : public QEventDispatcherUNIX
+{
+ Q_OBJECT
+ Q_DECLARE_PRIVATE(QEventDispatcherQWS)
+
+public:
+ explicit QEventDispatcherQWS(QObject *parent = 0);
+ ~QEventDispatcherQWS();
+
+ bool processEvents(QEventLoop::ProcessEventsFlags flags);
+ bool hasPendingEvents();
+
+ void flush();
+
+ void startingUp();
+ void closingDown();
+
+protected:
+ int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds,
+ timeval *timeout);
+};
+
+QT_END_NAMESPACE
+
+#endif // QEVENTDISPATCHER_QWS_P_H
diff --git a/src/gui/kernel/qeventdispatcher_x11.cpp b/src/gui/kernel/qeventdispatcher_x11.cpp
new file mode 100644
index 0000000000..2d4dbba16f
--- /dev/null
+++ b/src/gui/kernel/qeventdispatcher_x11.cpp
@@ -0,0 +1,191 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qeventdispatcher_x11_p.h"
+
+#include "qapplication.h"
+#include "qx11info_x11.h"
+
+#include "qt_x11_p.h"
+#include <private/qeventdispatcher_unix_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QEventDispatcherX11Private : public QEventDispatcherUNIXPrivate
+{
+ Q_DECLARE_PUBLIC(QEventDispatcherX11)
+public:
+ inline QEventDispatcherX11Private()
+ : xfd(-1)
+ { }
+ int xfd;
+ QList<XEvent> queuedUserInputEvents;
+};
+
+QEventDispatcherX11::QEventDispatcherX11(QObject *parent)
+ : QEventDispatcherUNIX(*new QEventDispatcherX11Private, parent)
+{ }
+
+QEventDispatcherX11::~QEventDispatcherX11()
+{ }
+
+bool QEventDispatcherX11::processEvents(QEventLoop::ProcessEventsFlags flags)
+{
+ Q_D(QEventDispatcherX11);
+
+ d->interrupt = false;
+ QApplication::sendPostedEvents();
+
+ ulong marker = XNextRequest(X11->display);
+ int nevents = 0;
+ do {
+ while (!d->interrupt) {
+ XEvent event;
+ if (!(flags & QEventLoop::ExcludeUserInputEvents)
+ && !d->queuedUserInputEvents.isEmpty()) {
+ // process a pending user input event
+ event = d->queuedUserInputEvents.takeFirst();
+ } else if (XEventsQueued(X11->display, QueuedAlready)) {
+ // process events from the X server
+ XNextEvent(X11->display, &event);
+
+ if (flags & QEventLoop::ExcludeUserInputEvents) {
+ // queue user input events
+ switch (event.type) {
+ case ButtonPress:
+ case ButtonRelease:
+ case MotionNotify:
+ case XKeyPress:
+ case XKeyRelease:
+ case EnterNotify:
+ case LeaveNotify:
+ d->queuedUserInputEvents.append(event);
+ continue;
+
+ case ClientMessage:
+ // only keep the wm_take_focus and
+ // _qt_scrolldone protocols, queue all other
+ // client messages
+ if (event.xclient.format == 32) {
+ if (event.xclient.message_type == ATOM(WM_PROTOCOLS) ||
+ (Atom) event.xclient.data.l[0] == ATOM(WM_TAKE_FOCUS)) {
+ break;
+ } else if (event.xclient.message_type == ATOM(_QT_SCROLL_DONE)) {
+ break;
+ }
+ }
+ d->queuedUserInputEvents.append(event);
+ continue;
+
+ default:
+ break;
+ }
+ }
+ } else {
+ // no event to process
+ break;
+ }
+
+ // send through event filter
+ if (filterEvent(&event))
+ continue;
+
+ nevents++;
+ if (qApp->x11ProcessEvent(&event) == 1)
+ return true;
+
+ if (event.xany.serial >= marker) {
+ if (XEventsQueued(X11->display, QueuedAfterFlush))
+ flags &= ~QEventLoop::WaitForMoreEvents;
+ goto out;
+ }
+ }
+ } while (!d->interrupt && XEventsQueued(X11->display, QueuedAfterFlush));
+
+ out:
+ if (!d->interrupt) {
+ const uint exclude_all =
+ QEventLoop::ExcludeSocketNotifiers | QEventLoop::X11ExcludeTimers | QEventLoop::WaitForMoreEvents;
+ if (nevents > 0 && ((uint)flags & exclude_all) == exclude_all) {
+ QApplication::sendPostedEvents();
+ return nevents > 0;
+ }
+ // return true if we handled events, false otherwise
+ return QEventDispatcherUNIX::processEvents(flags) || (nevents > 0);
+ }
+ return nevents > 0;
+}
+
+bool QEventDispatcherX11::hasPendingEvents()
+{
+ extern uint qGlobalPostedEventsCount(); // from qapplication.cpp
+ return (qGlobalPostedEventsCount() || XPending(X11->display));
+}
+
+void QEventDispatcherX11::flush()
+{
+ XFlush(X11->display);
+}
+
+void QEventDispatcherX11::startingUp()
+{
+ Q_D(QEventDispatcherX11);
+ d->xfd = XConnectionNumber(X11->display);
+}
+
+void QEventDispatcherX11::closingDown()
+{
+ Q_D(QEventDispatcherX11);
+ d->xfd = -1;
+}
+
+int QEventDispatcherX11::select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds,
+ timeval *timeout)
+{
+ Q_D(QEventDispatcherX11);
+ if (d->xfd > 0) {
+ nfds = qMax(nfds - 1, d->xfd) + 1;
+ FD_SET(d->xfd, readfds);
+ }
+ return QEventDispatcherUNIX::select(nfds, readfds, writefds, exceptfds, timeout);
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/kernel/qeventdispatcher_x11_p.h b/src/gui/kernel/qeventdispatcher_x11_p.h
new file mode 100644
index 0000000000..1b40a84377
--- /dev/null
+++ b/src/gui/kernel/qeventdispatcher_x11_p.h
@@ -0,0 +1,86 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QEVENTDISPATCHER_X11_P_H
+#define QEVENTDISPATCHER_X11_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/qeventdispatcher_unix_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class QEventDispatcherX11Private;
+
+class QEventDispatcherX11 : public QEventDispatcherUNIX
+{
+ Q_OBJECT
+ Q_DECLARE_PRIVATE(QEventDispatcherX11)
+
+public:
+ explicit QEventDispatcherX11(QObject *parent = 0);
+ ~QEventDispatcherX11();
+
+ bool processEvents(QEventLoop::ProcessEventsFlags flags);
+ bool hasPendingEvents();
+
+ void flush();
+
+ void startingUp();
+ void closingDown();
+
+protected:
+ int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds,
+ timeval *timeout);
+};
+
+QT_END_NAMESPACE
+
+#endif // QEVENTDISPATCHER_X11_P_H
diff --git a/src/gui/kernel/qformlayout.cpp b/src/gui/kernel/qformlayout.cpp
new file mode 100644
index 0000000000..e2d6108f98
--- /dev/null
+++ b/src/gui/kernel/qformlayout.cpp
@@ -0,0 +1,2080 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qapplication.h"
+#include "qdebug.h"
+#include "qformlayout.h"
+#include "qlabel.h"
+#include "qlayout_p.h"
+#include "qlayoutengine_p.h"
+#include "qrect.h"
+#include "qvector.h"
+#include "qwidget.h"
+
+QT_BEGIN_NAMESPACE
+
+namespace {
+// Fixed column matrix, stores items as [i11, i12, i21, i22...],
+// with FORTRAN-style index operator(r, c).
+template <class T, int NumColumns>
+class FixedColumnMatrix {
+public:
+ typedef QVector<T> Storage;
+
+ FixedColumnMatrix() { }
+
+ void clear() { m_storage.clear(); }
+
+ const T &operator()(int r, int c) const { return m_storage[r * NumColumns + c]; }
+ T &operator()(int r, int c) { return m_storage[r * NumColumns + c]; }
+
+ int rowCount() const { return m_storage.size() / NumColumns; }
+ void addRow(const T &value);
+ void insertRow(int r, const T &value);
+ void removeRow(int r);
+
+ bool find(const T &value, int *rowPtr, int *colPtr) const ;
+ int count(const T &value) const { return m_storage.count(value); }
+
+ // Hmmpf.. Some things are faster that way.
+ const Storage &storage() const { return m_storage; }
+
+ static void storageIndexToPosition(int idx, int *rowPtr, int *colPtr);
+
+private:
+ Storage m_storage;
+};
+
+template <class T, int NumColumns>
+void FixedColumnMatrix<T, NumColumns>::addRow(const T &value)
+{
+ for (int i = 0; i < NumColumns; ++i)
+ m_storage.append(value);
+}
+
+template <class T, int NumColumns>
+void FixedColumnMatrix<T, NumColumns>::insertRow(int r, const T &value)
+{
+ Q_TYPENAME Storage::iterator it = m_storage.begin();
+ it += r * NumColumns;
+ m_storage.insert(it, NumColumns, value);
+}
+
+template <class T, int NumColumns>
+void FixedColumnMatrix<T, NumColumns>::removeRow(int r)
+{
+ m_storage.remove(r * NumColumns, NumColumns);
+}
+
+template <class T, int NumColumns>
+bool FixedColumnMatrix<T, NumColumns>::find(const T &value, int *rowPtr, int *colPtr) const
+{
+ const int idx = m_storage.indexOf(value);
+ if (idx == -1)
+ return false;
+ storageIndexToPosition(idx, rowPtr, colPtr);
+ return true;
+}
+
+template <class T, int NumColumns>
+void FixedColumnMatrix<T, NumColumns>::storageIndexToPosition(int idx, int *rowPtr, int *colPtr)
+{
+ *rowPtr = idx / NumColumns;
+ *colPtr = idx % NumColumns;
+}
+} // namespace
+
+// special values for unset fields; must not clash with values of FieldGrowthPolicy or
+// RowWrapPolicy
+const uint DefaultFieldGrowthPolicy = 255;
+const uint DefaultRowWrapPolicy = 255;
+
+enum { ColumnCount = 2 };
+
+// -- our data structure for our items
+// This owns the QLayoutItem
+struct QFormLayoutItem
+{
+ QFormLayoutItem(QLayoutItem* i) : item(i), fullRow(false), isHfw(false) { }
+ ~QFormLayoutItem() { delete item; }
+
+ // Wrappers
+ QWidget *widget() const { return item->widget(); }
+ QLayout *layout() const { return item->layout(); }
+
+ bool hasHeightForWidth() const { return item->hasHeightForWidth(); }
+ int heightForWidth(int width) const { return item->heightForWidth(width); }
+ int minimumHeightForWidth(int width) const { return item->minimumHeightForWidth(width); }
+ Qt::Orientations expandingDirections() const { return item->expandingDirections(); }
+ QSizePolicy::ControlTypes controlTypes() const { return item->controlTypes(); }
+ int vStretch() const { return widget() ? widget()->sizePolicy().verticalStretch() : 0; }
+
+ void setGeometry(const QRect& r) { item->setGeometry(r); }
+ QRect geometry() const { return item->geometry(); }
+
+ // For use with FixedColumnMatrix
+ bool operator==(const QFormLayoutItem& other) { return item == other.item; }
+
+ QLayoutItem *item;
+ bool fullRow;
+
+ // set by updateSizes
+ bool isHfw;
+ QSize minSize;
+ QSize sizeHint;
+ QSize maxSize;
+
+ // also set by updateSizes
+ int sbsHSpace; // only used for side by side, for the field item only (not label)
+ int vSpace; // This is the spacing to the item in the row above
+
+ // set by setupVerticalLayoutData
+ bool sideBySide;
+ int vLayoutIndex;
+
+ // set by setupHorizontalLayoutData
+ int layoutPos;
+ int layoutWidth;
+};
+
+class QFormLayoutPrivate : public QLayoutPrivate
+{
+ Q_DECLARE_PUBLIC(QFormLayout)
+
+public:
+ typedef FixedColumnMatrix<QFormLayoutItem *, ColumnCount> ItemMatrix;
+
+ QFormLayoutPrivate();
+ ~QFormLayoutPrivate() { }
+
+ int insertRow(int row);
+ void insertRows(int row, int count);
+ void setItem(int row, QFormLayout::ItemRole role, QLayoutItem *item);
+ void setLayout(int row, QFormLayout::ItemRole role, QLayout *layout);
+ void setWidget(int row, QFormLayout::ItemRole role, QWidget *widget);
+
+ void arrangeWidgets(const QVector<QLayoutStruct>& layouts, QRect &rect);
+
+ void updateSizes();
+
+ void setupVerticalLayoutData(int width);
+ void setupHorizontalLayoutData(int width);
+
+ QStyle* getStyle() const;
+
+ inline bool haveHfwCached(int width) const
+ {
+ return (hfw_width == width) || (width == sh_width && hfw_sh_height >= 0);
+ }
+
+ void recalcHFW(int w);
+ void setupHfwLayoutData();
+
+ uint fieldGrowthPolicy : 8;
+ uint rowWrapPolicy : 8;
+ uint has_hfw : 2;
+ uint dirty : 2; // have we laid out yet?
+ uint sizesDirty : 2; // have we (not) gathered layout item sizes?
+ uint expandVertical : 1; // Do we expand vertically?
+ uint expandHorizontal : 1; // Do we expand horizonally?
+ Qt::Alignment labelAlignment;
+ Qt::Alignment formAlignment;
+
+ ItemMatrix m_matrix;
+ QList<QFormLayoutItem *> m_things;
+
+ int layoutWidth; // the last width that we called setupVerticalLayoutData on (for vLayouts)
+
+ int hfw_width; // the last width we calculated HFW for
+ int hfw_height; // what that height was
+ int hfw_minheight; // what that minheight was
+
+ int hfw_sh_height; // the hfw for sh_width
+ int hfw_sh_minheight; // the minhfw for sh_width
+
+ int min_width; // the width that gets turned into minSize (from updateSizes)
+ int sh_width; // the width that gets turned into prefSize (from updateSizes)
+ int thresh_width; // the width that we start splitting label/field pairs at (from updateSizes)
+ QSize minSize;
+ QSize prefSize;
+ int formMaxWidth;
+ void calcSizeHints();
+
+ QVector<QLayoutStruct> vLayouts; // set by setupVerticalLayoutData;
+ int vLayoutCount; // Number of rows we calculated in setupVerticalLayoutData
+ int maxLabelWidth; // the label width we calculated in setupVerticalLayoutData
+
+ QVector<QLayoutStruct> hfwLayouts;
+
+ int hSpacing;
+ int vSpacing;
+};
+
+QFormLayoutPrivate::QFormLayoutPrivate()
+ : fieldGrowthPolicy(DefaultFieldGrowthPolicy),
+ rowWrapPolicy(DefaultRowWrapPolicy), has_hfw(false), dirty(true), sizesDirty(true),
+ expandVertical(0), expandHorizontal(0), labelAlignment(0), formAlignment(0),
+ hfw_width(-1), hfw_sh_height(-1), min_width(-1),
+ sh_width(-1), thresh_width(QLAYOUTSIZE_MAX), hSpacing(-1), vSpacing(-1)
+{
+}
+
+static Qt::Alignment fixedAlignment(Qt::Alignment alignment, Qt::LayoutDirection layoutDirection)
+{
+ if (layoutDirection == Qt::RightToLeft && alignment & Qt::AlignAbsolute) {
+ // swap left and right, and eliminate absolute flag
+ return Qt::Alignment((alignment & ~(Qt::AlignLeft | Qt::AlignRight | Qt::AlignAbsolute))
+ | ((alignment & Qt::AlignRight) ? Qt::AlignLeft : 0)
+ | ((alignment & Qt::AlignLeft) ? Qt::AlignRight : 0));
+ } else {
+ return alignment & ~Qt::AlignAbsolute;
+ }
+}
+
+static int storageIndexFromLayoutItem(const QFormLayoutPrivate::ItemMatrix &m,
+ QFormLayoutItem *item)
+{
+ if (item) {
+ return m.storage().indexOf(item);
+ } else {
+ return -1;
+ }
+}
+
+static void updateFormLayoutItem(QFormLayoutItem *item, int userVSpacing,
+ QFormLayout::FieldGrowthPolicy fieldGrowthPolicy,
+ bool fullRow)
+{
+ item->minSize = item->item->minimumSize();
+ item->sizeHint = item->item->sizeHint();
+ item->maxSize = item->item->maximumSize();
+
+ if (!fullRow && (fieldGrowthPolicy == QFormLayout::FieldsStayAtSizeHint
+ || (fieldGrowthPolicy == QFormLayout::ExpandingFieldsGrow
+ && !(item->item->expandingDirections() & Qt::Horizontal))))
+ item->maxSize.setWidth(item->sizeHint.width());
+
+ item->isHfw = item->item->hasHeightForWidth();
+ item->vSpace = userVSpacing;
+}
+
+/*
+ Iterate over all the controls and gather their size information
+ (min, sizeHint and max). Also work out what the spacing between
+ pairs of controls should be, and figure out the min and sizeHint
+ widths.
+*/
+void QFormLayoutPrivate::updateSizes()
+{
+ Q_Q(QFormLayout);
+
+ if (sizesDirty) {
+ QFormLayout::RowWrapPolicy wrapPolicy = q->rowWrapPolicy();
+ bool wrapAllRows = (wrapPolicy == QFormLayout::WrapAllRows);
+ bool dontWrapRows = (wrapPolicy == QFormLayout::DontWrapRows);
+ int rr = m_matrix.rowCount();
+
+ has_hfw = false;
+
+ // If any control can expand, so can this layout
+ // Wrapping doesn't affect expansion, though, just the minsize
+ bool expandH = false;
+ bool expandV = false;
+
+ QFormLayoutItem *prevLbl = 0;
+ QFormLayoutItem *prevFld = 0;
+
+ QWidget *parent = q->parentWidget();
+ QStyle *style = parent ? parent->style() : 0;
+
+ int userVSpacing = q->verticalSpacing();
+ int userHSpacing = wrapAllRows ? 0 : q->horizontalSpacing();
+
+ int maxMinLblWidth = 0;
+ int maxMinFldWidth = 0; // field with label
+ int maxMinIfldWidth = 0; // independent field
+
+ int maxShLblWidth = 0;
+ int maxShFldWidth = 0;
+ int maxShIfldWidth = 0;
+
+ for (int i = 0; i < rr; ++i) {
+ QFormLayoutItem *label = m_matrix(i, 0);
+ QFormLayoutItem *field = m_matrix(i, 1);
+
+ // Skip empty rows
+ if (!label && !field)
+ continue;
+
+ if (label) {
+ updateFormLayoutItem(label, userVSpacing, q->fieldGrowthPolicy(), false);
+ if (label->isHfw)
+ has_hfw = true;
+ Qt::Orientations o = label->expandingDirections();
+
+ if (o & Qt::Vertical)
+ expandV = true;
+ if (o & Qt::Horizontal)
+ expandH = true;
+ }
+ if (field) {
+ updateFormLayoutItem(field, userVSpacing, q->fieldGrowthPolicy(), !label && field->fullRow);
+ field->sbsHSpace = (!label && field->fullRow) ? 0 : userHSpacing;
+ if (field->isHfw)
+ has_hfw = true;
+
+ Qt::Orientations o = field->expandingDirections();
+
+ if (o & Qt::Vertical)
+ expandV = true;
+ if (o & Qt::Horizontal)
+ expandH = true;
+ }
+
+ // See if we need to calculate default spacings
+ if ((userHSpacing < 0 || userVSpacing < 0) && style) {
+ QSizePolicy::ControlTypes lbltypes =
+ QSizePolicy::ControlTypes(label ? label->controlTypes() : QSizePolicy::DefaultType);
+ QSizePolicy::ControlTypes fldtypes =
+ QSizePolicy::ControlTypes(field ? field->controlTypes() : QSizePolicy::DefaultType);
+
+ // VSpacing
+ if (userVSpacing < 0) {
+ if (wrapAllRows) {
+ // label spacing is to a previous item
+ QFormLayoutItem *lbltop = prevFld ? prevFld : prevLbl;
+ // field spacing is to the label (or a previous item)
+ QFormLayoutItem *fldtop = label ? label : lbltop;
+ QSizePolicy::ControlTypes lbltoptypes =
+ QSizePolicy::ControlTypes(lbltop ? lbltop->controlTypes() : QSizePolicy::DefaultType);
+ QSizePolicy::ControlTypes fldtoptypes =
+ QSizePolicy::ControlTypes(fldtop ? fldtop->controlTypes() : QSizePolicy::DefaultType);
+ if (label && lbltop)
+ label->vSpace = style->combinedLayoutSpacing(lbltoptypes, lbltypes, Qt::Vertical, 0, parent);
+ if (field && fldtop)
+ field->vSpace = style->combinedLayoutSpacing(fldtoptypes, fldtypes, Qt::Vertical, 0, parent);
+ } else {
+ // Side by side.. we have to also consider the spacings to empty cells, which can strangely be more than
+ // non empty cells..
+ QFormLayoutItem *lbltop = prevLbl ? prevLbl : prevFld;
+ QFormLayoutItem *fldtop = prevFld;
+ QSizePolicy::ControlTypes lbltoptypes =
+ QSizePolicy::ControlTypes(lbltop ? lbltop->controlTypes() : QSizePolicy::DefaultType);
+ QSizePolicy::ControlTypes fldtoptypes =
+ QSizePolicy::ControlTypes(fldtop ? fldtop->controlTypes() : QSizePolicy::DefaultType);
+
+ // To be compatible to QGridLayout, we have to compare solitary labels & fields with both predecessors
+ if (label) {
+ if (!field) {
+ int lblspacing = style->combinedLayoutSpacing(lbltoptypes, lbltypes, Qt::Vertical, 0, parent);
+ int fldspacing = style->combinedLayoutSpacing(fldtoptypes, lbltypes, Qt::Vertical, 0, parent);
+ label->vSpace = qMax(lblspacing, fldspacing);
+ } else
+ label->vSpace = style->combinedLayoutSpacing(lbltoptypes, lbltypes, Qt::Vertical, 0, parent);
+ }
+
+ if (field) {
+ // check spacing against both the previous label and field
+ if (!label) {
+ int lblspacing = style->combinedLayoutSpacing(lbltoptypes, fldtypes, Qt::Vertical, 0, parent);
+ int fldspacing = style->combinedLayoutSpacing(fldtoptypes, fldtypes, Qt::Vertical, 0, parent);
+ field->vSpace = qMax(lblspacing, fldspacing);
+ } else
+ field->vSpace = style->combinedLayoutSpacing(fldtoptypes, fldtypes, Qt::Vertical, 0, parent);
+ }
+ }
+ }
+
+ // HSpacing
+ // hard-coded the left and right control types so that all the rows have the same
+ // inter-column spacing (otherwise the right column isn't always left aligned)
+ if (userHSpacing < 0 && !wrapAllRows && (label || !field->fullRow) && field)
+ field->sbsHSpace = style->combinedLayoutSpacing(QSizePolicy::Label, QSizePolicy::LineEdit, Qt::Horizontal, 0, parent);
+ }
+
+ // Now update our min/sizehint widths
+ // We choose to put the spacing in the field side in sbs, so
+ // the right edge of the labels will align, but fields may
+ // be a little ragged.. since different controls may have
+ // different appearances, a slight raggedness in the left
+ // edges of fields can be tolerated.
+ // (Note - field->sbsHSpace is 0 for WrapAllRows mode)
+ if (label) {
+ maxMinLblWidth = qMax(maxMinLblWidth, label->minSize.width());
+ maxShLblWidth = qMax(maxShLblWidth, label->sizeHint.width());
+ if (field) {
+ maxMinFldWidth = qMax(maxMinFldWidth, field->minSize.width() + field->sbsHSpace);
+ maxShFldWidth = qMax(maxShFldWidth, field->sizeHint.width() + field->sbsHSpace);
+ }
+ } else if (field) {
+ maxMinIfldWidth = qMax(maxMinIfldWidth, field->minSize.width());
+ maxShIfldWidth = qMax(maxShIfldWidth, field->sizeHint.width());
+ }
+
+ prevLbl = label;
+ prevFld = field;
+ }
+
+ // Now, finally update the min/sizeHint widths
+ if (wrapAllRows) {
+ sh_width = qMax(maxShLblWidth, qMax(maxShIfldWidth, maxShFldWidth));
+ min_width = qMax(maxMinLblWidth, qMax(maxMinIfldWidth, maxMinFldWidth));
+ // in two line, we don't care as much about the threshold width
+ thresh_width = 0;
+ } else if (dontWrapRows) {
+ // This is just the max widths glommed together
+ sh_width = qMax(maxShLblWidth + maxShFldWidth, maxShIfldWidth);
+ min_width = qMax(maxMinLblWidth + maxMinFldWidth, maxMinIfldWidth);
+ thresh_width = QWIDGETSIZE_MAX;
+ } else {
+ // This is just the max widths glommed together
+ sh_width = qMax(maxShLblWidth + maxShFldWidth, maxShIfldWidth);
+ // min width needs to be the min when everything is wrapped,
+ // otherwise we'll never get set with a width that causes wrapping
+ min_width = qMax(maxMinLblWidth, qMax(maxMinIfldWidth, maxMinFldWidth));
+ // We split a pair at label sh + field min (### for now..)
+ thresh_width = maxShLblWidth + maxMinFldWidth;
+ }
+
+ // Update the expansions
+ expandVertical = expandV;
+ expandHorizontal = expandH;
+ }
+ sizesDirty = false;
+}
+
+void QFormLayoutPrivate::recalcHFW(int w)
+{
+ setupHfwLayoutData();
+
+ int h = 0;
+ int mh = 0;
+
+ for (int r = 0; r < vLayoutCount; ++r) {
+ int spacing = hfwLayouts.at(r).spacing;
+ h += hfwLayouts.at(r).sizeHint + spacing;
+ mh += hfwLayouts.at(r).minimumSize + spacing;
+ }
+
+ if (sh_width > 0 && sh_width == w) {
+ hfw_sh_height = qMin(QLAYOUTSIZE_MAX, h);
+ hfw_sh_minheight = qMin(QLAYOUTSIZE_MAX, mh);
+ } else {
+ hfw_width = w;
+ hfw_height = qMin(QLAYOUTSIZE_MAX, h);
+ hfw_minheight = qMin(QLAYOUTSIZE_MAX, mh);
+ }
+}
+
+void QFormLayoutPrivate::setupHfwLayoutData()
+{
+ // setupVerticalLayoutData must be called before this
+ // setupHorizontalLayoutData must also be called before this
+ // copies non hfw data into hfw
+ // then updates size and min
+
+
+ // Note: QGridLayout doesn't call minimumHeightForWidth,
+ // but instead uses heightForWidth for both min and sizeHint.
+ // For the common case where minimumHeightForWidth just calls
+ // heightForWidth, we do the calculation twice, which can be
+ // very expensive for word wrapped QLabels/QTextEdits, for example.
+ // So we just use heightForWidth as well.
+ int i;
+ int rr = m_matrix.rowCount();
+
+ hfwLayouts.clear();
+ hfwLayouts.resize(vLayoutCount);
+ for (i = 0; i < vLayoutCount; ++i)
+ hfwLayouts[i] = vLayouts.at(i);
+
+ for (i = 0; i < rr; ++i) {
+ QFormLayoutItem *label = m_matrix(i, 0);
+ QFormLayoutItem *field = m_matrix(i, 1);
+
+ if (label) {
+ if (label->isHfw) {
+ // We don't check sideBySide here, since a label is only
+ // ever side by side with its field
+ int hfw = label->heightForWidth(label->layoutWidth);
+ hfwLayouts[label->vLayoutIndex].minimumSize = hfw;
+ hfwLayouts[label->vLayoutIndex].sizeHint = hfw;
+ } else {
+ // Reset these here, so the field can do a qMax below (the previous value may have
+ // been the fields non-hfw values, which are often larger than hfw)
+ hfwLayouts[label->vLayoutIndex].sizeHint = label->sizeHint.height();
+ hfwLayouts[label->vLayoutIndex].minimumSize = label->minSize.height();
+ }
+ }
+
+ if (field) {
+ int hfw = field->isHfw ? field->heightForWidth(field->layoutWidth) : 0;
+ int h = field->isHfw ? hfw : field->sizeHint.height();
+ int mh = field->isHfw ? hfw : field->minSize.height();
+
+ if (field->sideBySide) {
+ int oh = hfwLayouts.at(field->vLayoutIndex).sizeHint;
+ int omh = hfwLayouts.at(field->vLayoutIndex).minimumSize;
+
+ hfwLayouts[field->vLayoutIndex].sizeHint = qMax(h, oh);
+ hfwLayouts[field->vLayoutIndex].minimumSize = qMax(mh, omh);
+ } else {
+ hfwLayouts[field->vLayoutIndex].sizeHint = h;
+ hfwLayouts[field->vLayoutIndex].minimumSize = mh;
+ }
+ }
+ }
+}
+
+/*
+ Given up to four items involved in a vertical spacing calculation
+ (two rows * two columns), return the max vertical spacing for the
+ row containing item1 (which may also include item2)
+ We assume parent and item1 are not null.
+
+ If a particular row is split, then the spacings for that row and
+ the following row are affected, and this function should be
+ called with recalculate = true for both rows (note: only rows with both
+ a label and a field can be split).
+
+ In particular:
+
+ 1) the split label's row vspace needs to be changed to qMax(label/prevLabel, label/prevField)
+ [call with item1 = label, item2 = null, prevItem1 & prevItem2 as before]
+ 2) the split field's row vspace needs to be changed to the label/field spacing
+ [call with item1 = field, item2 = null, prevItem1 = label, prevItem2 = null]
+
+ [if the next row has one item, 'item']
+ 3a) the following row's vspace needs to be changed to item/field spacing (would
+ previously been the qMax(item/label, item/field) spacings)
+ [call with item1 = item, item2 = null, prevItem1 = field, prevItem2 = null]
+
+ [if the next row has two items, 'label2' and 'field2']
+ 3b) the following row's vspace needs to be changed to be qMax(field/label2, field/field2) spacing
+ [call with item1 = label2, item2 = field2, prevItem1 = field, prevItem2 = null]
+
+ In the (common) non split case, we can just use the precalculated vspace (possibly qMaxed between
+ label and field).
+
+ If recalculate is true, we expect:
+ - parent != null
+ - item1 != null
+ - item2 can be null
+ - prevItem1 can be null
+ - if item2 is not null, prevItem2 will be null (e.g. steps 1 or 3 above)
+ - if prevItem1 is null, prevItem2 will be null
+*/
+static inline int spacingHelper(QWidget* parent, QStyle *style, int userVSpacing, bool recalculate, QFormLayoutItem* item1, QFormLayoutItem* item2, QFormLayoutItem* prevItem1, QFormLayoutItem *prevItem2)
+{
+ int spacing = userVSpacing;
+ if (spacing < 0) {
+ if (!recalculate) {
+ if (item1)
+ spacing = item1->vSpace;
+ if (item2)
+ spacing = qMax(spacing, item2->vSpace);
+ } else {
+ if (style && prevItem1) {
+ QSizePolicy::ControlTypes itemtypes =
+ QSizePolicy::ControlTypes(item1 ? item1->controlTypes() : QSizePolicy::DefaultType);
+ int spacing2 = 0;
+
+ spacing = style->combinedLayoutSpacing(itemtypes, prevItem1->controlTypes(), Qt::Vertical, 0, parent);
+
+ // At most of one of item2 and prevItem2 will be nonnull
+ if (item2)
+ spacing2 = style->combinedLayoutSpacing(item2->controlTypes(), prevItem1->controlTypes(), Qt::Vertical, 0, parent);
+ else if (prevItem2)
+ spacing2 = style->combinedLayoutSpacing(itemtypes, prevItem2->controlTypes(), Qt::Vertical, 0, parent);
+
+ spacing = qMax(spacing, spacing2);
+ }
+ }
+ } else {
+ if (prevItem1) {
+ QWidget *wid = prevItem1->item->widget();
+ if (wid)
+ spacing = qMax(spacing, prevItem1->geometry().top() - wid->geometry().top() );
+ }
+ if (prevItem2) {
+ QWidget *wid = prevItem2->item->widget();
+ if (wid)
+ spacing = qMax(spacing, prevItem2->geometry().top() - wid->geometry().top() );
+ }
+ }
+ return spacing;
+}
+
+static inline void initLayoutStruct(QLayoutStruct& sl, QFormLayoutItem* item)
+{
+ sl.init(item->vStretch(), item->minSize.height());
+ sl.sizeHint = item->sizeHint.height();
+ sl.maximumSize = item->maxSize.height();
+ sl.expansive = (item->expandingDirections() & Qt::Vertical);
+ sl.empty = false;
+}
+
+void QFormLayoutPrivate::setupVerticalLayoutData(int width)
+{
+ Q_Q(QFormLayout);
+
+ // Early out if we have no changes that would cause a change in vertical layout
+ if ((width == layoutWidth || (width >= thresh_width && layoutWidth >= thresh_width)) && !dirty && !sizesDirty)
+ return;
+
+ layoutWidth = width;
+
+ int rr = m_matrix.rowCount();
+ int vidx = 1;
+ QFormLayout::RowWrapPolicy rowWrapPolicy = q->rowWrapPolicy();
+ bool wrapAllRows = (rowWrapPolicy == QFormLayout::WrapAllRows);
+ bool addTopBottomStretch = true;
+
+ vLayouts.clear();
+ vLayouts.resize((2 * rr) + 2); // a max, some may be unused
+
+ QStyle *style = 0;
+
+ int userVSpacing = q->verticalSpacing();
+
+ if (userVSpacing < 0) {
+ if (QWidget *widget = q->parentWidget())
+ style = widget->style();
+ }
+
+ // make sure our sizes are up to date
+ updateSizes();
+
+ // Grab the widest label width here
+ // This might be different from the value computed during
+ // sizeHint/minSize, since we don't count label/field pairs that
+ // are split.
+ maxLabelWidth = 0;
+ if (!wrapAllRows) {
+ for (int i = 0; i < rr; ++i) {
+ const QFormLayoutItem *label = m_matrix(i, 0);
+ const QFormLayoutItem *field = m_matrix(i, 1);
+ if (label && (label->sizeHint.width() + (field ? field->minSize.width() : 0) <= width))
+ maxLabelWidth = qMax(maxLabelWidth, label->sizeHint.width());
+ }
+ } else {
+ maxLabelWidth = width;
+ }
+
+ QFormLayoutItem *prevItem1 = 0;
+ QFormLayoutItem *prevItem2 = 0;
+ bool prevRowSplit = false;
+
+ for (int i = 0; i < rr; ++i) {
+ QFormLayoutItem *label = m_matrix(i, 0);
+ QFormLayoutItem *field = m_matrix(i, 1);
+
+ // Totally ignore empty rows...
+ if (!label && !field)
+ continue;
+
+ QSize min1;
+ QSize min2;
+ QSize sh1;
+ QSize sh2;
+ if (label) {
+ min1 = label->minSize;
+ sh1 = label->sizeHint;
+ }
+ if (field) {
+ min2 = field->minSize;
+ sh2 = field->sizeHint;
+ }
+
+ // In separate lines, we make a vLayout for everything that isn't null
+ // in side by side, we only separate label/field if we're going to wrap it
+ bool splitSideBySide = (rowWrapPolicy == QFormLayout::WrapLongRows)
+ && ((maxLabelWidth < sh1.width()) || (width < (maxLabelWidth + min2.width())));
+
+ if (wrapAllRows || splitSideBySide) {
+ if (label) {
+ initLayoutStruct(vLayouts[vidx], label);
+
+ if (vidx > 1)
+ vLayouts[vidx - 1].spacing = spacingHelper(q->parentWidget(), style, userVSpacing, splitSideBySide || prevRowSplit, label, 0, prevItem1, prevItem2);
+
+ label->vLayoutIndex = vidx;
+ label->sideBySide = false;
+
+ prevItem1 = label;
+ prevItem2 = 0;
+
+ if (vLayouts[vidx].stretch > 0)
+ addTopBottomStretch = false;
+
+ ++vidx;
+ }
+
+ if (field) {
+ initLayoutStruct(vLayouts[vidx], field);
+
+ if (vidx > 1)
+ vLayouts[vidx - 1].spacing = spacingHelper(q->parentWidget(), style, userVSpacing, splitSideBySide || prevRowSplit, field, 0, prevItem1, prevItem2);
+
+ field->vLayoutIndex = vidx;
+ field->sideBySide = false;
+
+ prevItem1 = field;
+ prevItem2 = 0;
+
+ if (vLayouts[vidx].stretch > 0)
+ addTopBottomStretch = false;
+
+ ++vidx;
+ }
+
+ prevRowSplit = splitSideBySide;
+ } else {
+ // we're in side by side mode, and we have enough space to do that
+ QSize max1(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX);
+ QSize max2(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX);
+
+ int stretch1 = 0;
+ int stretch2 = 0;
+ bool expanding = false;
+
+ if (label) {
+ max1 = label->maxSize;
+ if (label->expandingDirections() & Qt::Vertical)
+ expanding = true;
+
+ label->sideBySide = (field != 0);
+ label->vLayoutIndex = vidx;
+ stretch1 = label->vStretch();
+ }
+
+ if (field) {
+ max2 = field->maxSize;
+ if (field->expandingDirections() & Qt::Vertical)
+ expanding = true;
+
+ field->sideBySide = (label || !field->fullRow);
+ field->vLayoutIndex = vidx;
+ stretch2 = field->vStretch();
+ }
+
+ vLayouts[vidx].init(qMax(stretch1, stretch2), qMax(min1.height(), min2.height()));
+ vLayouts[vidx].sizeHint = qMax(sh1.height(), sh2.height());
+ vLayouts[vidx].maximumSize = qMin(max1.height(), max2.height());
+ vLayouts[vidx].expansive = expanding || (vLayouts[vidx].stretch > 0);
+ vLayouts[vidx].empty = false;
+
+ if (vLayouts[vidx].stretch > 0)
+ addTopBottomStretch = false;
+
+ if (vidx > 1)
+ vLayouts[vidx - 1].spacing = spacingHelper(q->parentWidget(), style, userVSpacing, prevRowSplit, label, field, prevItem1, prevItem2);
+
+ if (label) {
+ prevItem1 = label;
+ prevItem2 = field;
+ } else {
+ prevItem1 = field;
+ prevItem2 = 0;
+ }
+
+ prevRowSplit = false;
+ ++vidx;
+ }
+ }
+
+ if (addTopBottomStretch) {
+ Qt::Alignment formAlignment = q->formAlignment();
+
+ if (!(formAlignment & Qt::AlignBottom)) {
+ // AlignTop (default if unspecified) or AlignVCenter: We add a stretch at the bottom
+ vLayouts[vidx].init(1, 0);
+ vLayouts[vidx].expansive = true;
+ ++vidx;
+ }
+
+ if (formAlignment & (Qt::AlignVCenter | Qt::AlignBottom)) {
+ // AlignVCenter or AlignBottom: We add a stretch at the top
+ vLayouts[0].init(1, 0);
+ vLayouts[0].expansive = true;
+ } else {
+ vLayouts[0].init(0, 0);
+ }
+ } else {
+ vLayouts[0].init(0, 0);
+ }
+
+ vLayoutCount = vidx;
+ dirty = false;
+}
+
+void QFormLayoutPrivate::setupHorizontalLayoutData(int width)
+{
+ Q_Q(QFormLayout);
+
+ // requires setupVerticalLayoutData to be called first
+
+ int fieldMaxWidth = 0;
+
+ int rr = m_matrix.rowCount();
+ bool wrapAllRows = (q->rowWrapPolicy() == QFormLayout::WrapAllRows);
+
+ for (int i = 0; i < rr; ++i) {
+ QFormLayoutItem *label = m_matrix(i, 0);
+ QFormLayoutItem *field = m_matrix(i, 1);
+
+ // Totally ignore empty rows...
+ if (!label && !field)
+ continue;
+
+ if (label) {
+ // if there is a field, and we're side by side, we use maxLabelWidth
+ // otherwise we just use the sizehint
+ label->layoutWidth = (field && label->sideBySide) ? maxLabelWidth : label->sizeHint.width();
+ label->layoutPos = 0;
+ }
+
+ if (field) {
+ // This is the default amount allotted to fields in sbs
+ int fldwidth = width - maxLabelWidth - field->sbsHSpace;
+
+ // If we've split a row, we still decide to align
+ // the field with all the other field if it will fit
+ // Fields in sbs mode get the remnants of the maxLabelWidth
+ if (!field->sideBySide) {
+ if (wrapAllRows || (!label && field->fullRow) || field->sizeHint.width() > fldwidth) {
+ field->layoutWidth = width;
+ field->layoutPos = 0;
+ } else {
+ field->layoutWidth = fldwidth;
+ field->layoutPos = width - fldwidth;
+ }
+ } else {
+ // We're sbs, so we should have a label
+ field->layoutWidth = fldwidth;
+ field->layoutPos = width - fldwidth;
+ }
+
+ fieldMaxWidth = qMax(fieldMaxWidth, field->maxSize.width());
+ }
+ }
+
+ formMaxWidth = maxLabelWidth + fieldMaxWidth;
+}
+
+void QFormLayoutPrivate::calcSizeHints()
+{
+ Q_Q(QFormLayout);
+
+ int leftMargin, topMargin, rightMargin, bottomMargin;
+ q->getContentsMargins(&leftMargin, &topMargin, &rightMargin, &bottomMargin);
+
+ updateSizes();
+ setupVerticalLayoutData(QLAYOUTSIZE_MAX);
+ // Don't need to call setupHorizontal here
+
+ int h = topMargin + bottomMargin;
+ int mh = topMargin + bottomMargin;
+
+ // The following are set in updateSizes
+ int w = sh_width + leftMargin + rightMargin;
+ int mw = min_width + leftMargin + rightMargin;
+
+ for (int i = 0; i < vLayoutCount; ++i) {
+ int spacing = vLayouts.at(i).spacing;
+ h += vLayouts.at(i).sizeHint + spacing;
+ mh += vLayouts.at(i).minimumSize + spacing;
+ }
+
+ minSize.rwidth() = qMin(mw, QLAYOUTSIZE_MAX);
+ minSize.rheight() = qMin(mh, QLAYOUTSIZE_MAX);
+ prefSize.rwidth() = qMin(w, QLAYOUTSIZE_MAX);
+ prefSize.rheight() = qMin(h, QLAYOUTSIZE_MAX);
+}
+
+int QFormLayoutPrivate::insertRow(int row)
+{
+ int rowCnt = m_matrix.rowCount();
+ if (uint(row) > uint(rowCnt))
+ row = rowCnt;
+
+ insertRows(row, 1);
+ return row;
+}
+
+void QFormLayoutPrivate::insertRows(int row, int count)
+{
+ while (count > 0) {
+ m_matrix.insertRow(row, 0);
+ --count;
+ }
+}
+
+void QFormLayoutPrivate::setItem(int row, QFormLayout::ItemRole role, QLayoutItem *item)
+{
+ const bool fullRow = role == QFormLayout::SpanningRole;
+ const int column = role == QFormLayout::SpanningRole ? 1 : static_cast<int>(role);
+ if (uint(row) >= uint(m_matrix.rowCount()) || uint(column) > 1U) {
+ qWarning("QFormLayoutPrivate::setItem: Invalid cell (%d, %d)", row, column);
+ return;
+ }
+
+ if (!item)
+ return;
+
+ if (m_matrix(row, column)) {
+ qWarning("QFormLayoutPrivate::setItem: Cell (%d, %d) already occupied", row, column);
+ return;
+ }
+
+ QFormLayoutItem *i = new QFormLayoutItem(item);
+ i->fullRow = fullRow;
+ m_matrix(row, column) = i;
+
+ m_things.append(i);
+}
+
+void QFormLayoutPrivate::setLayout(int row, QFormLayout::ItemRole role, QLayout *layout)
+{
+ if (layout) {
+ Q_Q(QFormLayout);
+ q->addChildLayout(layout);
+ setItem(row, role, layout);
+ }
+}
+
+void QFormLayoutPrivate::setWidget(int row, QFormLayout::ItemRole role, QWidget *widget)
+{
+ if (widget) {
+ Q_Q(QFormLayout);
+ q->addChildWidget(widget);
+ setItem(row, role, QLayoutPrivate::createWidgetItem(q, widget));
+ }
+}
+
+QStyle* QFormLayoutPrivate::getStyle() const
+{
+ Q_Q(const QFormLayout);
+
+ // ### cache
+ if (QWidget *parentWidget = q->parentWidget())
+ return parentWidget->style();
+ else
+ return QApplication::style();
+}
+
+/*!
+ \class QFormLayout
+ \since 4.4
+ \brief The QFormLayout class manages forms of input widgets and their associated labels.
+
+ \ingroup appearance
+ \ingroup geomanagement
+
+ \mainclass
+
+ QFormLayout is a convenience layout class that lays out its
+ children in a two-column form. The left column consists of labels
+ and the right column consists of "field" widgets (line editors,
+ spin boxes, etc.).
+
+ Traditionally, such two-column form layouts were achieved using
+ QGridLayout. QFormLayout is a higher-level alternative that
+ provides the following advantages:
+
+ \list
+ \o \bold{Adherence to the different platform's look and feel guidelines.}
+
+ For example, the
+ \l{Mac OS X Aqua} and KDE guidelines specify that the
+ labels should be right-aligned, whereas Windows and GNOME
+ applications normally use left-alignment.
+
+ \o \bold{Support for wrapping long rows.}
+
+ For devices with small displays, QFormLayout can be set to
+ \l{WrapLongRows}{wrap long rows}, or even to
+ \l{WrapAllRows}{wrap all rows}.
+
+ \o \bold{Convenient API for creating label--field pairs.}
+
+ The addRow() overload that takes a QString and a QWidget *
+ creates a QLabel behind the scenes and automatically set up
+ its buddy. We can then write code like this:
+
+ \snippet doc/src/snippets/code/src_gui_kernel_qformlayout.cpp 0
+
+ Compare this with the following code, written using QGridLayout:
+
+ \snippet doc/src/snippets/code/src_gui_kernel_qformlayout.cpp 1
+ \endlist
+
+ The table below shows the default appearance in different styles.
+
+ \table
+ \header
+ \o QCommonStyle derived styles (except QPlastiqueStyle)
+ \o QMacStyle
+ \o QPlastiqueStyle
+ \o Qt Extended styles
+ \row
+ \o \inlineimage qformlayout-win.png
+ \o \inlineimage qformlayout-mac.png
+ \o \inlineimage qformlayout-kde.png
+ \o \inlineimage qformlayout-qpe.png
+ \row
+ \o Traditional style used for Windows, GNOME, and earlier
+ versions of KDE. Labels are left aligned, and expanding
+ fields grow to fill the available space. (This normally
+ corresponds to what we would get using a two-column
+ QGridLayout.)
+ \o Style based on the
+ \l{Mac OS X Aqua} guidelines. Labels are right-aligned,
+ the fields don't grow beyond their size hint, and the
+ form is horizontally centered.
+ \o Recommended style for
+ \l{KDE applications}. Similar to MacStyle, except that the form
+ is left-aligned and all fields grow to fill the available
+ space.
+ \o Default style for Qt Extended styles. Labels are right-aligned,
+ expanding fields grow to fill the available space, and row
+ wrapping is enabled for long lines.
+ \endtable
+
+ The form styles can be also be overridden individually by calling
+ setLabelAlignment(), setFormAlignment(), setFieldGrowthPolicy(),
+ and setRowWrapPolicy(). For example, to simulate the form layout
+ appearance of QMacStyle on all platforms, but with left-aligned
+ labels, you could write:
+
+ \snippet doc/src/snippets/code/src_gui_kernel_qformlayout.cpp 2
+
+ \sa QGridLayout, QBoxLayout, QStackedLayout
+*/
+
+
+/*!
+ \enum QFormLayout::FieldGrowthPolicy
+
+ This enum specifies the different policies that can be used to
+ control the way in which the form's fields grow.
+
+ \value FieldsStayAtSizeHint
+ The fields never grow beyond their
+ \l{QWidgetItem::sizeHint()}{effective size hint}. This is
+ the default for QMacStyle.
+
+ \value ExpandingFieldsGrow
+ Fields with an horizontal \l{QSizePolicy}{size policy} of
+ \l{QSizePolicy::}{Expanding} or
+ \l{QSizePolicy::}{MinimumExpanding} will grow to fill the
+ available space. The other fields will not grow beyond
+ their effective size hint. This is the default policy for
+ Plastique.
+
+ \value AllNonFixedFieldsGrow
+ All fields with a size policy that allows them to grow
+ will grow to fill the available space. This is the default
+ policy for most styles.
+
+ \sa fieldGrowthPolicy
+*/
+
+/*!
+ \enum QFormLayout::RowWrapPolicy
+
+ This enum specifies the different policies that can be used to
+ control the way in which the form's rows wrap.
+
+ \value DontWrapRows
+ Fields are always laid out next to their label. This is
+ the default policy for all styles except Qt Extended styles.
+
+ \value WrapLongRows
+ Labels are given enough horizontal space to fit the widest label,
+ and the rest of the space is given to the fields. If the minimum
+ size of a field pair is wider than the available space, the field
+ is wrapped to the next line. This is the default policy for
+ Qt Extended styles.
+
+ \value WrapAllRows
+ Fields are always laid out below their label.
+
+ \sa rowWrapPolicy
+*/
+
+/*!
+ \enum QFormLayout::ItemRole
+
+ This enum specifies the types of widgets (or other layout items)
+ that may appear in a row.
+
+ \value LabelRole A label widget.
+ \value FieldRole A field widget.
+ \value SpanningRole A widget that spans label and field columns.
+
+ \sa itemAt(), getItemPosition()
+*/
+
+/*!
+ Constructs a new form layout with the given \a parent widget.
+
+ \sa QWidget::setLayout()
+*/
+QFormLayout::QFormLayout(QWidget *parent)
+ : QLayout(*new QFormLayoutPrivate, 0, parent)
+{
+}
+
+/*!
+ Destroys the form layout.
+*/
+QFormLayout::~QFormLayout()
+{
+ Q_D(QFormLayout);
+
+ /*
+ The clearing and destruction order here is important. We start by clearing
+ m_things so that QLayout and the rest of the world know that we don't babysit
+ the layout items anymore and don't care if they are destroyed.
+ */
+ d->m_things.clear();
+ qDeleteAll(d->m_matrix.storage());
+ d->m_matrix.clear();
+}
+
+/*!
+ Adds a new row to the bottom of this form layout, with the given
+ \a label and \a field.
+
+ \sa insertRow()
+*/
+void QFormLayout::addRow(QWidget *label, QWidget *field)
+{
+ insertRow(-1, label, field);
+}
+
+/*!
+ \overload
+*/
+void QFormLayout::addRow(QWidget *label, QLayout *field)
+{
+ insertRow(-1, label, field);
+}
+
+/*!
+ \overload
+
+ This overload automatically creates a QLabel behind the scenes
+ with \a labelText as its text. The \a field is set as the new
+ QLabel's \l{QLabel::setBuddy()}{buddy}.
+*/
+void QFormLayout::addRow(const QString &labelText, QWidget *field)
+{
+ insertRow(-1, labelText, field);
+}
+
+/*!
+ \overload
+
+ This overload automatically creates a QLabel behind the scenes
+ with \a labelText as its text.
+*/
+void QFormLayout::addRow(const QString &labelText, QLayout *field)
+{
+ insertRow(-1, labelText, field);
+}
+
+/*!
+ \overload
+
+ Adds the specified \a widget at the end of this form layout. The
+ \a widget spans both columns.
+*/
+void QFormLayout::addRow(QWidget *widget)
+{
+ insertRow(-1, widget);
+}
+
+/*!
+ \overload
+
+ Adds the specified \a layout at the end of this form layout. The
+ \a layout spans both columns.
+*/
+void QFormLayout::addRow(QLayout *layout)
+{
+ insertRow(-1, layout);
+}
+
+/*!
+ Inserts a new row at position \a row in this form layout, with
+ the given \a label and \a field. If \a row is out of bounds, the
+ new row is added at the end.
+
+ \sa addRow()
+*/
+void QFormLayout::insertRow(int row, QWidget *label, QWidget *field)
+{
+ Q_D(QFormLayout);
+
+ row = d->insertRow(row);
+ if (label)
+ d->setWidget(row, LabelRole, label);
+ if (field)
+ d->setWidget(row, FieldRole, field);
+ invalidate();
+}
+
+/*!
+ \overload
+*/
+void QFormLayout::insertRow(int row, QWidget *label, QLayout *field)
+{
+ Q_D(QFormLayout);
+
+ row = d->insertRow(row);
+ if (label)
+ d->setWidget(row, LabelRole, label);
+ if (field)
+ d->setLayout(row, FieldRole, field);
+ invalidate();
+}
+
+/*!
+ \overload
+
+ This overload automatically creates a QLabel behind the scenes
+ with \a labelText as its text. The \a field is set as the new
+ QLabel's \l{QLabel::setBuddy()}{buddy}.
+*/
+void QFormLayout::insertRow(int row, const QString &labelText, QWidget *field)
+{
+ QLabel *label = 0;
+ if (!labelText.isEmpty()) {
+ label = new QLabel(labelText);
+#ifndef QT_NO_SHORTCUT
+ label->setBuddy(field);
+#endif
+ }
+ insertRow(row, label, field);
+}
+
+/*!
+ \overload
+
+ This overload automatically creates a QLabel behind the scenes
+ with \a labelText as its text.
+*/
+void QFormLayout::insertRow(int row, const QString &labelText, QLayout *field)
+{
+ insertRow(row, labelText.isEmpty() ? 0 : new QLabel(labelText), field);
+}
+
+/*!
+ \overload
+
+ Inserts the specified \a widget at position \a row in this form
+ layout. The \a widget spans both columns. If \a row is out of
+ bounds, the widget is added at the end.
+*/
+void QFormLayout::insertRow(int row, QWidget *widget)
+{
+ Q_D(QFormLayout);
+
+ if (!widget) {
+ qWarning("QFormLayout: Cannot add null field to %s", qPrintable(objectName()));
+ return;
+ }
+
+ row = d->insertRow(row);
+ d->setWidget(row, SpanningRole, widget);
+ invalidate();
+}
+
+/*!
+ \overload
+
+ Inserts the specified \a layout at position \a row in this form
+ layout. The \a layout spans both columns. If \a row is out of
+ bounds, the widget is added at the end.
+*/
+void QFormLayout::insertRow(int row, QLayout *layout)
+{
+ Q_D(QFormLayout);
+
+ if (!layout) {
+ qWarning("QFormLayout: Cannot add null field to %s", qPrintable(objectName()));
+ return;
+ }
+
+ row = d->insertRow(row);
+ d->setLayout(row, SpanningRole, layout);
+ invalidate();
+}
+
+/*!
+ \reimp
+*/
+void QFormLayout::addItem(QLayoutItem *item)
+{
+ Q_D(QFormLayout);
+
+ int row = d->insertRow(d->m_matrix.rowCount());
+ d->setItem(row, FieldRole, item);
+ invalidate();
+}
+
+/*!
+ \reimp
+*/
+int QFormLayout::count() const
+{
+ Q_D(const QFormLayout);
+ return d->m_things.count();
+}
+
+/*!
+ \reimp
+*/
+QLayoutItem *QFormLayout::itemAt(int index) const
+{
+ Q_D(const QFormLayout);
+ if (QFormLayoutItem *formItem = d->m_things.value(index))
+ return formItem->item;
+ return 0;
+}
+
+/*!
+ \reimp
+*/
+QLayoutItem *QFormLayout::takeAt(int index)
+{
+ Q_D(QFormLayout);
+
+ const int storageIndex = storageIndexFromLayoutItem(d->m_matrix, d->m_things.value(index));
+ if (storageIndex == -1) {
+ qWarning("QFormLayout::takeAt: Invalid index %d", index);
+ return 0;
+ }
+
+ int row, col;
+ QFormLayoutPrivate::ItemMatrix::storageIndexToPosition(storageIndex, &row, &col);
+ Q_ASSERT(d->m_matrix(row, col));
+
+ QFormLayoutItem *item = d->m_matrix(row, col);
+ Q_ASSERT(item);
+ d->m_things.removeAt(index);
+ d->m_matrix(row, col) = 0;
+
+ invalidate();
+
+ // grab ownership back from the QFormLayoutItem
+ QLayoutItem *i = item->item;
+ item->item = 0;
+ delete item;
+ return i;
+}
+
+/*!
+ \reimp
+*/
+Qt::Orientations QFormLayout::expandingDirections() const
+{
+ Q_D(const QFormLayout);
+ QFormLayoutPrivate *e = const_cast<QFormLayoutPrivate *>(d);
+ e->updateSizes();
+
+ Qt::Orientations o = 0;
+ if (e->expandHorizontal)
+ o = Qt::Horizontal;
+ if (e->expandVertical)
+ o |= Qt::Vertical;
+ return o;
+}
+
+/*!
+ \reimp
+*/
+bool QFormLayout::hasHeightForWidth() const
+{
+ Q_D(const QFormLayout);
+ QFormLayoutPrivate *e = const_cast<QFormLayoutPrivate *>(d);
+ e->updateSizes();
+ return (d->has_hfw || rowWrapPolicy() == WrapLongRows);
+}
+
+/*!
+ \reimp
+*/
+int QFormLayout::heightForWidth(int width) const
+{
+ Q_D(const QFormLayout);
+ if (!hasHeightForWidth())
+ return -1;
+
+ int leftMargin, topMargin, rightMargin, bottomMargin;
+ getContentsMargins(&leftMargin, &topMargin, &rightMargin, &bottomMargin);
+
+ int targetWidth = width - leftMargin - rightMargin;
+
+ if (!d->haveHfwCached(targetWidth)) {
+ QFormLayoutPrivate *dat = const_cast<QFormLayoutPrivate *>(d);
+ dat->setupVerticalLayoutData(targetWidth);
+ dat->setupHorizontalLayoutData(targetWidth);
+ dat->recalcHFW(targetWidth);
+ }
+ if (targetWidth == d->sh_width)
+ return d->hfw_sh_height + topMargin + bottomMargin;
+ else
+ return d->hfw_height + topMargin + bottomMargin;
+}
+
+/*!
+ \reimp
+*/
+void QFormLayout::setGeometry(const QRect &rect)
+{
+ Q_D(QFormLayout);
+ if (d->dirty || rect != geometry()) {
+ QRect cr = rect;
+ int leftMargin, topMargin, rightMargin, bottomMargin;
+ getContentsMargins(&leftMargin, &topMargin, &rightMargin, &bottomMargin);
+ cr.adjust(+leftMargin, +topMargin, -rightMargin, -bottomMargin);
+
+ bool hfw = hasHeightForWidth();
+ d->setupVerticalLayoutData(cr.width());
+ d->setupHorizontalLayoutData(cr.width());
+ if (hfw && (!d->haveHfwCached(cr.width()) || d->hfwLayouts.size() != d->vLayoutCount))
+ d->recalcHFW(cr.width());
+ if (hfw) {
+ qGeomCalc(d->hfwLayouts, 0, d->vLayoutCount, cr.y(), cr.height());
+ d->arrangeWidgets(d->hfwLayouts, cr);
+ } else {
+ qGeomCalc(d->vLayouts, 0, d->vLayoutCount, cr.y(), cr.height());
+ d->arrangeWidgets(d->vLayouts, cr);
+ }
+ QLayout::setGeometry(rect);
+ }
+}
+
+/*!
+ \reimp
+*/
+QSize QFormLayout::sizeHint() const
+{
+ Q_D(const QFormLayout);
+ if (!d->prefSize.isValid()) {
+ QFormLayoutPrivate *dat = const_cast<QFormLayoutPrivate *>(d);
+ dat->calcSizeHints();
+ }
+ return d->prefSize;
+}
+
+/*!
+ \reimp
+*/
+QSize QFormLayout::minimumSize() const
+{
+ // ### fix minimumSize if hfw
+ Q_D(const QFormLayout);
+ if (!d->minSize.isValid()) {
+ QFormLayoutPrivate *dat = const_cast<QFormLayoutPrivate *>(d);
+ dat->calcSizeHints();
+ }
+ return d->minSize;
+}
+
+/*!
+ \reimp
+*/
+void QFormLayout::invalidate()
+{
+ Q_D(QFormLayout);
+ d->dirty = true;
+ d->sizesDirty = true;
+ d->minSize = QSize();
+ d->prefSize = QSize();
+ d->formMaxWidth = -1;
+ d->hfw_width = -1;
+ d->sh_width = -1;
+ d->layoutWidth = -1;
+ d->hfw_sh_height = -1;
+ QLayout::invalidate();
+}
+
+/*!
+ Returns the number of rows in the form.
+
+ \sa QLayout::count()
+*/
+int QFormLayout::rowCount() const
+{
+ Q_D(const QFormLayout);
+ return d->m_matrix.rowCount();
+}
+
+/*!
+ Returns the layout item in the given \a row with the specified \a
+ role (column). Returns 0 if there is no such item.
+
+ \sa QLayout::itemAt(), setItem()
+*/
+QLayoutItem *QFormLayout::itemAt(int row, ItemRole role) const
+{
+ Q_D(const QFormLayout);
+ if (uint(row) >= uint(d->m_matrix.rowCount()))
+ return 0;
+ switch (role) {
+ case SpanningRole:
+ if (QFormLayoutItem *item = d->m_matrix(row, 1))
+ if (item->fullRow)
+ return item->item;
+ break;
+ case LabelRole:
+ case FieldRole:
+ if (QFormLayoutItem *item = d->m_matrix(row, (role == LabelRole) ? 0 : 1))
+ return item->item;
+ break;
+ }
+ return 0;
+}
+
+/*!
+ Retrieves the row and role (column) of the item at the specified
+ \a index. If \a index is out of bounds, *\a rowPtr is set to -1;
+ otherwise the row is stored in *\a rowPtr and the role is stored
+ in *\a rolePtr.
+
+ \sa itemAt(), count(), getLayoutPosition(), getWidgetPosition()
+*/
+void QFormLayout::getItemPosition(int index, int *rowPtr, ItemRole *rolePtr) const
+{
+ Q_D(const QFormLayout);
+ int col = -1;
+ int row = -1;
+
+ const int storageIndex = storageIndexFromLayoutItem(d->m_matrix, d->m_things.value(index));
+ if (storageIndex != -1)
+ QFormLayoutPrivate::ItemMatrix::storageIndexToPosition(storageIndex, &row, &col);
+
+ if (rowPtr)
+ *rowPtr = row;
+ if (rolePtr && col != -1) {
+ const bool spanning = col == 1 && d->m_matrix(row, col)->fullRow;
+ if (spanning) {
+ *rolePtr = SpanningRole;
+ } else {
+ *rolePtr = ItemRole(col);
+ }
+ }
+}
+
+/*!
+ Retrieves the row and role (column) of the specified child \a
+ layout. If \a layout is not in the form layout, *\a rowPtr is set
+ to -1; otherwise the row is stored in *\a rowPtr and the role is stored
+ in *\a rolePtr.
+*/
+void QFormLayout::getLayoutPosition(QLayout *layout, int *rowPtr, ItemRole *rolePtr) const
+{
+ int n = count();
+ int index = 0;
+ while (index < n) {
+ if (itemAt(index) == layout)
+ break;
+ ++index;
+ }
+ getItemPosition(index, rowPtr, rolePtr);
+}
+
+/*!
+ Retrieves the row and role (column) of the specified \a widget in
+ the layout. If \a widget is not in the layout, *\a rowPtr is set
+ to -1; otherwise the row is stored in *\a rowPtr and the role is stored
+ in *\a rolePtr.
+
+ \sa getItemPosition(), itemAt()
+*/
+void QFormLayout::getWidgetPosition(QWidget *widget, int *rowPtr, ItemRole *rolePtr) const
+{
+ getItemPosition(indexOf(widget), rowPtr, rolePtr);
+}
+
+// ### eliminate labelForField()
+
+/*!
+ Returns the label associated with the given \a field.
+
+ \sa itemAt()
+*/
+QWidget *QFormLayout::labelForField(QWidget *field) const
+{
+ Q_D(const QFormLayout);
+
+ int row;
+ ItemRole role;
+
+ getWidgetPosition(field, &row, &role);
+
+ if (row != -1 && role == FieldRole) {
+ if (QFormLayoutItem *label = d->m_matrix(row, LabelRole))
+ return label->widget();
+ }
+ return 0;
+}
+
+/*!
+ \overload
+*/
+QWidget *QFormLayout::labelForField(QLayout *field) const
+{
+ Q_D(const QFormLayout);
+
+ int row;
+ ItemRole role;
+
+ getLayoutPosition(field, &row, &role);
+
+ if (row != -1 && role == FieldRole) {
+ if (QFormLayoutItem *label = d->m_matrix(row, LabelRole))
+ return label->widget();
+ }
+ return 0;
+}
+
+/*!
+ \property QFormLayout::fieldGrowthPolicy
+ \brief the way in which the form's fields grow
+
+ The default value depends on the widget or application style. For
+ QMacStyle, the default is FieldsStayAtSizeHint; for QCommonStyle
+ derived styles (like Plastique and Windows), the default
+ is ExpandingFieldsGrow; for Qt Extended styles, the default is
+ AllNonFixedFieldsGrow.
+
+ If none of the fields can grow and the form is resized, extra
+ space is distributed according to the current
+ \l{formAlignment}{form alignment}.
+
+ \sa formAlignment, rowWrapPolicy
+*/
+
+void QFormLayout::setFieldGrowthPolicy(FieldGrowthPolicy policy)
+{
+ Q_D(QFormLayout);
+ if (FieldGrowthPolicy(d->fieldGrowthPolicy) != policy) {
+ d->fieldGrowthPolicy = policy;
+ invalidate();
+ }
+}
+
+QFormLayout::FieldGrowthPolicy QFormLayout::fieldGrowthPolicy() const
+{
+ Q_D(const QFormLayout);
+ if (d->fieldGrowthPolicy == DefaultFieldGrowthPolicy) {
+ return QFormLayout::FieldGrowthPolicy(d->getStyle()->styleHint(QStyle::SH_FormLayoutFieldGrowthPolicy));
+ } else {
+ return QFormLayout::FieldGrowthPolicy(d->fieldGrowthPolicy);
+ }
+}
+
+/*!
+ \property QFormLayout::rowWrapPolicy
+ \brief the way in which the form's rows wrap
+
+ The default value depends on the widget or application style. For
+ Qt Extended styles, the default is WrapLongRows; for the other styles,
+ the default is DontWrapRows.
+
+ If you want to display each label above its associated field
+ (instead of next to it), set this property to WrapAllRows.
+
+ \sa fieldGrowthPolicy
+*/
+
+void QFormLayout::setRowWrapPolicy(RowWrapPolicy policy)
+{
+ Q_D(QFormLayout);
+ if (RowWrapPolicy(d->rowWrapPolicy) != policy) {
+ d->rowWrapPolicy = policy;
+ invalidate();
+ }
+}
+
+QFormLayout::RowWrapPolicy QFormLayout::rowWrapPolicy() const
+{
+ Q_D(const QFormLayout);
+ if (d->rowWrapPolicy == DefaultRowWrapPolicy) {
+ return QFormLayout::RowWrapPolicy(d->getStyle()->styleHint(QStyle::SH_FormLayoutWrapPolicy));
+ } else {
+ return QFormLayout::RowWrapPolicy(d->rowWrapPolicy);
+ }
+}
+
+/*!
+ \property QFormLayout::labelAlignment
+ \brief the horizontal alignment of the labels
+
+ The default value depends on the widget or application style. For
+ QCommonStyle derived styles, except for QPlastiqueStyle, the
+ default is Qt::AlignLeft; for the other styles, the default is
+ Qt::AlignRight.
+
+ \sa formAlignment
+*/
+
+void QFormLayout::setLabelAlignment(Qt::Alignment alignment)
+{
+ Q_D(QFormLayout);
+ if (d->labelAlignment != alignment) {
+ d->labelAlignment = alignment;
+ invalidate();
+ }
+}
+
+Qt::Alignment QFormLayout::labelAlignment() const
+{
+ Q_D(const QFormLayout);
+ if (!d->labelAlignment) {
+ return Qt::Alignment(d->getStyle()->styleHint(QStyle::SH_FormLayoutLabelAlignment));
+ } else {
+ return d->labelAlignment;
+ }
+}
+
+/*!
+ \property QFormLayout::formAlignment
+ \brief the alignment of the form layout's contents within the layout's geometry
+
+ The default value depends on the widget or application style. For
+ QMacStyle, the default is Qt::AlignHCenter | Qt::AlignTop; for the
+ other styles, the default is Qt::AlignLeft | Qt::AlignTop.
+
+ \sa labelAlignment, rowWrapPolicy
+*/
+
+void QFormLayout::setFormAlignment(Qt::Alignment alignment)
+{
+ Q_D(QFormLayout);
+ if (d->formAlignment != alignment) {
+ d->formAlignment = alignment;
+ invalidate();
+ }
+}
+
+Qt::Alignment QFormLayout::formAlignment() const
+{
+ Q_D(const QFormLayout);
+ if (!d->formAlignment) {
+ return Qt::Alignment(d->getStyle()->styleHint(QStyle::SH_FormLayoutFormAlignment));
+ } else {
+ return d->formAlignment;
+ }
+}
+
+/*!
+ \property QFormLayout::horizontalSpacing
+ \brief the spacing between widgets that are laid out side by side
+
+ By default, if no value is explicitly set, the layout's horizontal
+ spacing is inherited from the parent layout, or from the style settings
+ for the parent widget.
+
+ \sa verticalSpacing, QStyle::pixelMetric(), {QStyle::}{PM_LayoutHorizontalSpacing}
+*/
+void QFormLayout::setHorizontalSpacing(int spacing)
+{
+ Q_D(QFormLayout);
+ if (spacing != d->hSpacing) {
+ d->hSpacing = spacing;
+ invalidate();
+ }
+}
+
+int QFormLayout::horizontalSpacing() const
+{
+ Q_D(const QFormLayout);
+ if (d->hSpacing >= 0) {
+ return d->hSpacing;
+ } else {
+ return qSmartSpacing(this, QStyle::PM_LayoutHorizontalSpacing);
+ }
+}
+
+/*!
+ \property QFormLayout::verticalSpacing
+ \brief the spacing between widgets that are laid out vertically
+
+ By default, if no value is explicitly set, the layout's vertical spacing is
+ inherited from the parent layout, or from the style settings for the parent
+ widget.
+
+ \sa horizontalSpacing, QStyle::pixelMetric(), {QStyle::}{PM_LayoutHorizontalSpacing}
+*/
+void QFormLayout::setVerticalSpacing(int spacing)
+{
+ Q_D(QFormLayout);
+ if (spacing != d->vSpacing) {
+ d->vSpacing = spacing;
+ invalidate();
+ }
+}
+
+int QFormLayout::verticalSpacing() const
+{
+ Q_D(const QFormLayout);
+ if (d->vSpacing >= 0) {
+ return d->vSpacing;
+ } else {
+ return qSmartSpacing(this, QStyle::PM_LayoutVerticalSpacing);
+ }
+}
+
+/*!
+ This function sets both the vertical and horizontal spacing to
+ \a spacing.
+
+ \sa setVerticalSpacing(), setHorizontalSpacing()
+*/
+void QFormLayout::setSpacing(int spacing)
+{
+ Q_D(QFormLayout);
+ d->vSpacing = d->hSpacing = spacing;
+ invalidate();
+}
+
+/*!
+ If the vertical spacing is equal to the horizontal spacing,
+ this function returns that value; otherwise it returns -1.
+
+ \sa setSpacing(), verticalSpacing(), horizontalSpacing()
+*/
+int QFormLayout::spacing() const
+{
+ int hSpacing = horizontalSpacing();
+ if (hSpacing == verticalSpacing()) {
+ return hSpacing;
+ } else {
+ return -1;
+ }
+}
+
+void QFormLayoutPrivate::arrangeWidgets(const QVector<QLayoutStruct>& layouts, QRect &rect)
+{
+ Q_Q(QFormLayout);
+
+ int i;
+ const int rr = m_matrix.rowCount();
+ QWidget *w = q->parentWidget();
+ Qt::LayoutDirection layoutDirection = w ? w->layoutDirection() : QApplication::layoutDirection();
+
+ Qt::Alignment formAlignment = fixedAlignment(q->formAlignment(), layoutDirection);
+ int leftOffset = 0;
+ int delta = rect.width() - formMaxWidth;
+ if (formAlignment & (Qt::AlignHCenter | Qt::AlignRight) && delta > 0) {
+ leftOffset = delta;
+ if (formAlignment & Qt::AlignHCenter)
+ leftOffset >>= 1;
+ }
+
+ for (i = 0; i < rr; ++i) {
+ QFormLayoutItem *label = m_matrix(i, 0);
+ QFormLayoutItem *field = m_matrix(i, 1);
+
+ if (label) {
+ int height = layouts.at(label->vLayoutIndex).size;
+ if ((label->expandingDirections() & Qt::Vertical) == 0) {
+ /*
+ If the field on the right-hand side is tall,
+ we want the label to be top-aligned, but not too
+ much. So we introduce a 5 / 4 factor so that it
+ gets a few extra pixels at the top.
+ */
+ height = qMin(height,
+ qMin(label->sizeHint.height() * 5 / 4,
+ label->maxSize.height()));
+ }
+
+ QSize sz(qMin(label->layoutWidth, label->sizeHint.width()), height);
+ int x = leftOffset + rect.x() + label->layoutPos;
+ if (fixedAlignment(q->labelAlignment(), layoutDirection) & Qt::AlignRight)
+ x += label->layoutWidth - sz.width();
+ QPoint p(x, layouts.at(label->vLayoutIndex).pos);
+ // ### expansion & sizepolicy stuff
+
+ label->setGeometry(QStyle::visualRect(layoutDirection, rect, QRect(p, sz)));
+ }
+
+ if (field) {
+ QSize sz(field->layoutWidth, layouts.at(field->vLayoutIndex).size);
+ QPoint p(field->layoutPos + leftOffset + rect.x(), layouts.at(field->vLayoutIndex).pos);
+/*
+ if ((field->widget() && field->widget()->sizePolicy().horizontalPolicy() & (QSizePolicy::GrowFlag | QSizePolicy::ExpandFlag | QSizePolicy::IgnoreFlag))
+ || (field->layout() && sz.width() < field->maxSize.width())) {
+ sz.rwidth() = field->layoutWidth;
+ }
+*/
+ if (field->maxSize.isValid())
+ sz = sz.boundedTo(field->maxSize);
+
+ field->setGeometry(QStyle::visualRect(layoutDirection, rect, QRect(p, sz)));
+ }
+ }
+}
+
+/*!
+ Sets the widget in the given \a row for the given \a role to \a widget, extending the
+ layout with empty rows if necessary.
+
+ If the cell is already occupied, the \a widget is not inserted and an error message is
+ sent to the console.
+
+ \bold{Note:} For most applications, addRow() or insertRow() should be used instead of setWidget().
+
+ \sa setLayout()
+*/
+void QFormLayout::setWidget(int row, ItemRole role, QWidget *widget)
+{
+ Q_D(QFormLayout);
+ int rowCnt = rowCount();
+ if (row >= rowCnt)
+ d->insertRows(rowCnt, row - rowCnt + 1);
+ d->setWidget(row, role, widget);
+}
+
+/*!
+ Sets the sub-layout in the given \a row for the given \a role to \a layout, extending the
+ form layout with empty rows if necessary.
+
+ If the cell is already occupied, the \a layout is not inserted and an error message is
+ sent to the console.
+
+ \bold{Note:} For most applications, addRow() or insertRow() should be used instead of setLayout().
+
+ \sa setWidget()
+*/
+void QFormLayout::setLayout(int row, ItemRole role, QLayout *layout)
+{
+ Q_D(QFormLayout);
+ int rowCnt = rowCount();
+ if (row >= rowCnt)
+ d->insertRows(rowCnt, row - rowCnt + 1);
+ d->setLayout(row, role, layout);
+}
+
+/*!
+ Sets the item in the given \a row for the given \a role to \a item, extending the
+ layout with empty rows if necessary.
+
+ If the cell is already occupied, the \a item is not inserted and an error message is
+ sent to the console.
+ The \a item spans both columns.
+
+ \warning Do not use this function to add child layouts or child
+ widget items. Use setLayout() or setWidget() instead.
+
+ \sa setLayout()
+*/
+void QFormLayout::setItem(int row, ItemRole role, QLayoutItem *item)
+{
+ Q_D(QFormLayout);
+ int rowCnt = rowCount();
+ if (row >= rowCnt)
+ d->insertRows(rowCnt, row - rowCnt + 1);
+ d->setItem(row, role, item);
+}
+
+/*!
+ \internal
+ */
+
+void QFormLayout::resetFieldGrowthPolicy()
+{
+ Q_D(QFormLayout);
+ d->fieldGrowthPolicy = DefaultFieldGrowthPolicy;
+}
+
+/*!
+ \internal
+ */
+
+void QFormLayout::resetRowWrapPolicy()
+{
+ Q_D(QFormLayout);
+ d->rowWrapPolicy = DefaultRowWrapPolicy;
+}
+
+/*!
+ \internal
+ */
+
+void QFormLayout::resetFormAlignment()
+{
+ Q_D(QFormLayout);
+ d->formAlignment = 0;
+}
+
+/*!
+ \internal
+ */
+
+void QFormLayout::resetLabelAlignment()
+{
+ Q_D(QFormLayout);
+ d->labelAlignment = 0;
+}
+
+#if 0
+void QFormLayout::dump() const
+{
+ Q_D(const QFormLayout);
+ for (int i = 0; i < rowCount(); ++i) {
+ for (int j = 0; j < 2; ++j) {
+ qDebug("m_matrix(%d, %d) = %p", i, j, d->m_matrix(i, j));
+ }
+ }
+ for (int i = 0; i < d->m_things.count(); ++i)
+ qDebug("m_things[%d] = %p", i, d->m_things.at(i));
+}
+#endif
+
+QT_END_NAMESPACE
diff --git a/src/gui/kernel/qformlayout.h b/src/gui/kernel/qformlayout.h
new file mode 100644
index 0000000000..e787f0f810
--- /dev/null
+++ b/src/gui/kernel/qformlayout.h
@@ -0,0 +1,163 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QFORMLAYOUT_H
+#define QFORMLAYOUT_H
+
+#include <QtGui/QLayout>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+class QFormLayoutPrivate;
+
+class Q_GUI_EXPORT QFormLayout : public QLayout
+{
+ Q_OBJECT
+ Q_ENUMS(FormStyle FieldGrowthPolicy RowWrapPolicy ItemRole)
+ Q_DECLARE_PRIVATE(QFormLayout)
+ Q_PROPERTY(FieldGrowthPolicy fieldGrowthPolicy READ fieldGrowthPolicy WRITE setFieldGrowthPolicy RESET resetFieldGrowthPolicy)
+ Q_PROPERTY(RowWrapPolicy rowWrapPolicy READ rowWrapPolicy WRITE setRowWrapPolicy RESET resetRowWrapPolicy)
+ Q_PROPERTY(Qt::Alignment labelAlignment READ labelAlignment WRITE setLabelAlignment RESET resetLabelAlignment)
+ Q_PROPERTY(Qt::Alignment formAlignment READ formAlignment WRITE setFormAlignment RESET resetFormAlignment)
+ Q_PROPERTY(int horizontalSpacing READ horizontalSpacing WRITE setHorizontalSpacing)
+ Q_PROPERTY(int verticalSpacing READ verticalSpacing WRITE setVerticalSpacing)
+
+public:
+ enum FieldGrowthPolicy {
+ FieldsStayAtSizeHint,
+ ExpandingFieldsGrow,
+ AllNonFixedFieldsGrow
+ };
+
+ enum RowWrapPolicy {
+ DontWrapRows,
+ WrapLongRows,
+ WrapAllRows
+ };
+
+ enum ItemRole {
+ LabelRole = 0,
+ FieldRole = 1,
+ SpanningRole = 2
+ };
+
+ explicit QFormLayout(QWidget *parent = 0);
+ ~QFormLayout();
+
+ void setFieldGrowthPolicy(FieldGrowthPolicy policy);
+ FieldGrowthPolicy fieldGrowthPolicy() const;
+ void setRowWrapPolicy(RowWrapPolicy policy);
+ RowWrapPolicy rowWrapPolicy() const;
+ void setLabelAlignment(Qt::Alignment alignment);
+ Qt::Alignment labelAlignment() const;
+ void setFormAlignment(Qt::Alignment alignment);
+ Qt::Alignment formAlignment() const;
+
+ void setHorizontalSpacing(int spacing);
+ int horizontalSpacing() const;
+ void setVerticalSpacing(int spacing);
+ int verticalSpacing() const;
+
+ int spacing() const;
+ void setSpacing(int);
+
+ void addRow(QWidget *label, QWidget *field);
+ void addRow(QWidget *label, QLayout *field);
+ void addRow(const QString &labelText, QWidget *field);
+ void addRow(const QString &labelText, QLayout *field);
+ void addRow(QWidget *widget);
+ void addRow(QLayout *layout);
+
+ void insertRow(int row, QWidget *label, QWidget *field);
+ void insertRow(int row, QWidget *label, QLayout *field);
+ void insertRow(int row, const QString &labelText, QWidget *field);
+ void insertRow(int row, const QString &labelText, QLayout *field);
+ void insertRow(int row, QWidget *widget);
+ void insertRow(int row, QLayout *layout);
+
+ void setItem(int row, ItemRole role, QLayoutItem *item);
+ void setWidget(int row, ItemRole role, QWidget *widget);
+ void setLayout(int row, ItemRole role, QLayout *layout);
+
+ QLayoutItem *itemAt(int row, ItemRole role) const;
+ void getItemPosition(int index, int *rowPtr, ItemRole *rolePtr) const;
+ void getWidgetPosition(QWidget *widget, int *rowPtr, ItemRole *rolePtr) const;
+ void getLayoutPosition(QLayout *layout, int *rowPtr, ItemRole *rolePtr) const;
+ QWidget *labelForField(QWidget *field) const;
+ QWidget *labelForField(QLayout *field) const;
+
+ // reimplemented from QLayout
+ void addItem(QLayoutItem *item);
+ QLayoutItem *itemAt(int index) const;
+ QLayoutItem *takeAt(int index);
+
+ void setGeometry(const QRect &rect);
+ QSize minimumSize() const;
+ QSize sizeHint() const;
+ void invalidate();
+
+ bool hasHeightForWidth() const;
+ int heightForWidth(int width) const;
+ Qt::Orientations expandingDirections() const;
+ int count() const;
+
+ int rowCount() const;
+
+#if 0
+ void dump() const;
+#endif
+
+private:
+ void resetFieldGrowthPolicy();
+ void resetRowWrapPolicy();
+ void resetLabelAlignment();
+ void resetFormAlignment();
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif
diff --git a/src/gui/kernel/qgridlayout.cpp b/src/gui/kernel/qgridlayout.cpp
new file mode 100644
index 0000000000..f872829725
--- /dev/null
+++ b/src/gui/kernel/qgridlayout.cpp
@@ -0,0 +1,1889 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qgridlayout.h"
+#include "qapplication.h"
+#include "qwidget.h"
+#include "qlist.h"
+#include "qsizepolicy.h"
+#include "qvector.h"
+#include "qvarlengtharray.h"
+#include "qlayoutengine_p.h"
+#include "qlayout_p.h"
+
+QT_BEGIN_NAMESPACE
+
+struct QGridLayoutSizeTriple
+{
+ QSize minS;
+ QSize hint;
+ QSize maxS;
+};
+
+/*
+ Three internal classes related to QGridLayout: (1) QGridBox is a
+ QLayoutItem with (row, column) information and (torow, tocolumn) information; (3) QGridLayoutData is
+ the internal representation of a QGridLayout.
+*/
+
+class QGridBox
+{
+public:
+ QGridBox(QLayoutItem *lit) { item_ = lit; }
+
+ QGridBox(const QLayout *l, QWidget *wid) { item_ = QLayoutPrivate::createWidgetItem(l, wid); }
+ ~QGridBox() { delete item_; }
+
+ QSize sizeHint() const { return item_->sizeHint(); }
+ QSize minimumSize() const { return item_->minimumSize(); }
+ QSize maximumSize() const { return item_->maximumSize(); }
+ Qt::Orientations expandingDirections() const { return item_->expandingDirections(); }
+ bool isEmpty() const { return item_->isEmpty(); }
+
+ bool hasHeightForWidth() const { return item_->hasHeightForWidth(); }
+ int heightForWidth(int w) const { return item_->heightForWidth(w); }
+
+ void setAlignment(Qt::Alignment a) { item_->setAlignment(a); }
+ void setGeometry(const QRect &r) { item_->setGeometry(r); }
+ Qt::Alignment alignment() const { return item_->alignment(); }
+ QLayoutItem *item() { return item_; }
+ QLayoutItem *takeItem() { QLayoutItem *i = item_; item_ = 0; return i; }
+
+ int hStretch() { return item_->widget() ?
+ item_->widget()->sizePolicy().horizontalStretch() : 0; }
+ int vStretch() { return item_->widget() ?
+ item_->widget()->sizePolicy().verticalStretch() : 0; }
+
+private:
+ friend class QGridLayoutPrivate;
+ friend class QGridLayout;
+
+ inline int toRow(int rr) const { return torow >= 0 ? torow : rr - 1; }
+ inline int toCol(int cc) const { return tocol >= 0 ? tocol : cc - 1; }
+
+ QLayoutItem *item_;
+ int row, col;
+ int torow, tocol;
+};
+
+class QGridLayoutPrivate : public QLayoutPrivate
+{
+ Q_DECLARE_PUBLIC(QGridLayout)
+public:
+ QGridLayoutPrivate();
+
+ void add(QGridBox*, int row, int col);
+ void add(QGridBox*, int row1, int row2, int col1, int col2);
+ QSize sizeHint(int hSpacing, int vSpacing) const;
+ QSize minimumSize(int hSpacing, int vSpacing) const;
+ QSize maximumSize(int hSpacing, int vSpacing) const;
+
+ Qt::Orientations expandingDirections(int hSpacing, int vSpacing) const;
+
+ void distribute(QRect rect, int hSpacing, int vSpacing);
+ inline int numRows() const { return rr; }
+ inline int numCols() const { return cc; }
+ inline void expand(int rows, int cols)
+ { setSize(qMax(rows, rr), qMax(cols, cc)); }
+ inline void setRowStretch(int r, int s)
+ { expand(r + 1, 0); rStretch[r] = s; setDirty(); }
+ inline void setColStretch(int c, int s)
+ { expand(0, c + 1); cStretch[c] = s; setDirty(); }
+ inline int rowStretch(int r) const { return rStretch.at(r); }
+ inline int colStretch(int c) const { return cStretch.at(c); }
+ inline void setRowMinimumHeight(int r, int s)
+ { expand(r + 1, 0); rMinHeights[r] = s; setDirty(); }
+ inline void setColumnMinimumWidth(int c, int s)
+ { expand(0, c + 1); cMinWidths[c] = s; setDirty(); }
+ inline int rowSpacing(int r) const { return rMinHeights.at(r); }
+ inline int colSpacing(int c) const { return cMinWidths.at(c); }
+
+ inline void setReversed(bool r, bool c) { hReversed = c; vReversed = r; }
+ inline bool horReversed() const { return hReversed; }
+ inline bool verReversed() const { return vReversed; }
+ inline void setDirty() { needRecalc = true; hfw_width = -1; }
+ inline bool isDirty() const { return needRecalc; }
+ bool hasHeightForWidth(int hSpacing, int vSpacing);
+ int heightForWidth(int width, int hSpacing, int vSpacing);
+ int minimumHeightForWidth(int width, int hSpacing, int vSpacing);
+
+ inline void getNextPos(int &row, int &col) { row = nextR; col = nextC; }
+ inline int count() const { return things.count(); }
+ QRect cellRect(int row, int col) const;
+
+ inline QLayoutItem *itemAt(int index) const {
+ if (index < things.count())
+ return things.at(index)->item();
+ else
+ return 0;
+ }
+ inline QLayoutItem *takeAt(int index) {
+ QLayoutItem *item = 0;
+ if (index < things.count()) {
+ QGridBox *b = things.takeAt(index);
+ if (b) {
+ item = b->takeItem();
+ delete b;
+ }
+ }
+ return item;
+ }
+
+ void getItemPosition(int index, int *row, int *column, int *rowSpan, int *columnSpan) {
+ if (index < things.count()) {
+ QGridBox *b = things.at(index);
+ int toRow = b->toRow(rr);
+ int toCol = b->toCol(cc);
+ *row = b->row;
+ *column = b->col;
+ *rowSpan = toRow - *row + 1;
+ *columnSpan = toCol - *column +1;
+ }
+ }
+ void deleteAll();
+
+private:
+ void setNextPosAfter(int r, int c);
+ void recalcHFW(int w);
+ void addHfwData(QGridBox *box, int width);
+ void init();
+ QSize findSize(int QLayoutStruct::*, int hSpacing, int vSpacing) const;
+ void addData(QGridBox *b, const QGridLayoutSizeTriple &sizes, bool r, bool c);
+ void setSize(int rows, int cols);
+ void setupSpacings(QVector<QLayoutStruct> &chain, QGridBox *grid[], int fixedSpacing,
+ Qt::Orientation orientation);
+ void setupLayoutData(int hSpacing, int vSpacing);
+ void setupHfwLayoutData();
+ void effectiveMargins(int *left, int *top, int *right, int *bottom) const;
+
+ int rr;
+ int cc;
+ QVector<QLayoutStruct> rowData;
+ QVector<QLayoutStruct> colData;
+ QVector<QLayoutStruct> *hfwData;
+ QVector<int> rStretch;
+ QVector<int> cStretch;
+ QVector<int> rMinHeights;
+ QVector<int> cMinWidths;
+ QList<QGridBox *> things;
+
+ int hfw_width;
+ int hfw_height;
+ int hfw_minheight;
+ int nextR;
+ int nextC;
+
+ int horizontalSpacing;
+ int verticalSpacing;
+ int leftMargin;
+ int topMargin;
+ int rightMargin;
+ int bottomMargin;
+
+ uint hReversed : 1;
+ uint vReversed : 1;
+ uint needRecalc : 1;
+ uint has_hfw : 1;
+ uint addVertical : 1;
+};
+
+void QGridLayoutPrivate::effectiveMargins(int *left, int *top, int *right, int *bottom) const
+{
+ int l = leftMargin;
+ int t = topMargin;
+ int r = rightMargin;
+ int b = bottomMargin;
+#ifdef Q_WS_MAC
+ int leftMost = INT_MAX;
+ int topMost = INT_MAX;
+ int rightMost = 0;
+ int bottomMost = 0;
+
+ QWidget *w = 0;
+ const int n = things.count();
+ for (int i = 0; i < n; ++i) {
+ QGridBox *box = things.at(i);
+ QLayoutItem *itm = box->item();
+ w = itm->widget();
+ if (w) {
+ bool visualHReversed = hReversed != (w->layoutDirection() == Qt::RightToLeft);
+ QRect lir = itm->geometry();
+ QRect wr = w->geometry();
+ if (box->col <= leftMost) {
+ if (box->col < leftMost) {
+ // we found an item even closer to the margin, discard.
+ leftMost = box->col;
+ if (visualHReversed)
+ r = rightMargin;
+ else
+ l = leftMargin;
+ }
+ if (visualHReversed) {
+ r = qMax(r, wr.right() - lir.right());
+ } else {
+ l = qMax(l, lir.left() - wr.left());
+ }
+ }
+ if (box->row <= topMost) {
+ if (box->row < topMost) {
+ // we found an item even closer to the margin, discard.
+ topMost = box->row;
+ if (vReversed)
+ b = bottomMargin;
+ else
+ t = topMargin;
+ }
+ if (vReversed)
+ b = qMax(b, wr.bottom() - lir.bottom());
+ else
+ t = qMax(t, lir.top() - wr.top());
+ }
+ if (box->toCol(cc) >= rightMost) {
+ if (box->toCol(cc) > rightMost) {
+ // we found an item even closer to the margin, discard.
+ rightMost = box->toCol(cc);
+ if (visualHReversed)
+ l = leftMargin;
+ else
+ r = rightMargin;
+ }
+ if (visualHReversed) {
+ l = qMax(l, lir.left() - wr.left());
+ } else {
+ r = qMax(r, wr.right() - lir.right());
+ }
+
+ }
+ if (box->toRow(rr) >= bottomMost) {
+ if (box->toRow(rr) > bottomMost) {
+ // we found an item even closer to the margin, discard.
+ bottomMost = box->toRow(rr);
+ if (vReversed)
+ t = topMargin;
+ else
+ b = bottomMargin;
+ }
+ if (vReversed)
+ t = qMax(t, lir.top() - wr.top());
+ else
+ b = qMax(b, wr.bottom() - lir.bottom());
+ }
+ }
+ }
+
+#endif
+ if (left)
+ *left = l;
+ if (top)
+ *top = t;
+ if (right)
+ *right = r;
+ if (bottom)
+ *bottom = b;
+}
+
+QGridLayoutPrivate::QGridLayoutPrivate()
+{
+ addVertical = false;
+ setDirty();
+ rr = cc = 0;
+ nextR = nextC = 0;
+ hfwData = 0;
+ hReversed = false;
+ vReversed = false;
+ horizontalSpacing = -1;
+ verticalSpacing = -1;
+}
+
+#if 0
+QGridLayoutPrivate::QGridLayoutPrivate(int nRows, int nCols)
+ : rowData(0), colData(0)
+{
+ init();
+ if (nRows < 0) {
+ nRows = 1;
+ addVertical = false;
+ }
+ if (nCols < 0) {
+ nCols = 1;
+ addVertical = true;
+ }
+ setSize(nRows, nCols);
+}
+#endif
+
+void QGridLayoutPrivate::deleteAll()
+{
+ while (!things.isEmpty())
+ delete things.takeFirst();
+ delete hfwData;
+}
+
+bool QGridLayoutPrivate::hasHeightForWidth(int hSpacing, int vSpacing)
+{
+ setupLayoutData(hSpacing, vSpacing);
+ return has_hfw;
+}
+
+/*
+ Assumes that setupLayoutData() has been called, and that
+ qGeomCalc() has filled in colData with appropriate values.
+*/
+void QGridLayoutPrivate::recalcHFW(int w)
+{
+ /*
+ Go through all children, using colData and heightForWidth()
+ and put the results in hfwData.
+ */
+ if (!hfwData)
+ hfwData = new QVector<QLayoutStruct>(rr);
+ setupHfwLayoutData();
+ QVector<QLayoutStruct> &rData = *hfwData;
+
+ int h = 0;
+ int mh = 0;
+ for (int r = 0; r < rr; r++) {
+ int spacing = rData.at(r).spacing;
+ h += rData.at(r).sizeHint + spacing;
+ mh += rData.at(r).minimumSize + spacing;
+ }
+
+ hfw_width = w;
+ hfw_height = qMin(QLAYOUTSIZE_MAX, h);
+ hfw_minheight = qMin(QLAYOUTSIZE_MAX, mh);
+}
+
+int QGridLayoutPrivate::heightForWidth(int w, int hSpacing, int vSpacing)
+{
+ setupLayoutData(hSpacing, vSpacing);
+ if (!has_hfw)
+ return -1;
+ int left, top, right, bottom;
+ effectiveMargins(&left, &top, &right, &bottom);
+
+ int hMargins = left + right;
+ if (w - hMargins != hfw_width) {
+ qGeomCalc(colData, 0, cc, 0, w - hMargins);
+ recalcHFW(w - hMargins);
+ }
+ return hfw_height + top + bottom;
+}
+
+int QGridLayoutPrivate::minimumHeightForWidth(int w, int hSpacing, int vSpacing)
+{
+ (void)heightForWidth(w, hSpacing, vSpacing);
+ if (!has_hfw)
+ return -1;
+ int top, bottom;
+ effectiveMargins(0, &top, 0, &bottom);
+ return hfw_minheight + top + bottom;
+}
+
+QSize QGridLayoutPrivate::findSize(int QLayoutStruct::*size, int hSpacing, int vSpacing) const
+{
+ QGridLayoutPrivate *that = const_cast<QGridLayoutPrivate*>(this);
+ that->setupLayoutData(hSpacing, vSpacing);
+
+ int w = 0;
+ int h = 0;
+
+ for (int r = 0; r < rr; r++)
+ h += rowData.at(r).*size + rowData.at(r).spacing;
+ for (int c = 0; c < cc; c++)
+ w += colData.at(c).*size + colData.at(c).spacing;
+
+ w = qMin(QLAYOUTSIZE_MAX, w);
+ h = qMin(QLAYOUTSIZE_MAX, h);
+
+ return QSize(w, h);
+}
+
+Qt::Orientations QGridLayoutPrivate::expandingDirections(int hSpacing, int vSpacing) const
+{
+ QGridLayoutPrivate *that = const_cast<QGridLayoutPrivate*>(this);
+ that->setupLayoutData(hSpacing, vSpacing);
+ Qt::Orientations ret;
+
+ for (int r = 0; r < rr; r++) {
+ if (rowData.at(r).expansive) {
+ ret |= Qt::Vertical;
+ break;
+ }
+ }
+ for (int c = 0; c < cc; c++) {
+ if (colData.at(c).expansive) {
+ ret |= Qt::Horizontal;
+ break;
+ }
+ }
+ return ret;
+}
+
+QSize QGridLayoutPrivate::sizeHint(int hSpacing, int vSpacing) const
+{
+ return findSize(&QLayoutStruct::sizeHint, hSpacing, vSpacing);
+}
+
+QSize QGridLayoutPrivate::maximumSize(int hSpacing, int vSpacing) const
+{
+ return findSize(&QLayoutStruct::maximumSize, hSpacing, vSpacing);
+}
+
+QSize QGridLayoutPrivate::minimumSize(int hSpacing, int vSpacing) const
+{
+ return findSize(&QLayoutStruct::minimumSize, hSpacing, vSpacing);
+}
+
+void QGridLayoutPrivate::setSize(int r, int c)
+{
+ if ((int)rowData.size() < r) {
+ int newR = qMax(r, rr * 2);
+ rowData.resize(newR);
+ rStretch.resize(newR);
+ rMinHeights.resize(newR);
+ for (int i = rr; i < newR; i++) {
+ rowData[i].init();
+ rowData[i].maximumSize = 0;
+ rowData[i].pos = 0;
+ rowData[i].size = 0;
+ rStretch[i] = 0;
+ rMinHeights[i] = 0;
+ }
+ }
+ if ((int)colData.size() < c) {
+ int newC = qMax(c, cc * 2);
+ colData.resize(newC);
+ cStretch.resize(newC);
+ cMinWidths.resize(newC);
+ for (int i = cc; i < newC; i++) {
+ colData[i].init();
+ colData[i].maximumSize = 0;
+ colData[i].pos = 0;
+ colData[i].size = 0;
+ cStretch[i] = 0;
+ cMinWidths[i] = 0;
+ }
+ }
+
+ if (hfwData && (int)hfwData->size() < r) {
+ delete hfwData;
+ hfwData = 0;
+ hfw_width = -1;
+ }
+ rr = r;
+ cc = c;
+}
+
+void QGridLayoutPrivate::setNextPosAfter(int row, int col)
+{
+ if (addVertical) {
+ if (col > nextC || (col == nextC && row >= nextR)) {
+ nextR = row + 1;
+ nextC = col;
+ if (nextR >= rr) {
+ nextR = 0;
+ nextC++;
+ }
+ }
+ } else {
+ if (row > nextR || (row == nextR && col >= nextC)) {
+ nextR = row;
+ nextC = col + 1;
+ if (nextC >= cc) {
+ nextC = 0;
+ nextR++;
+ }
+ }
+ }
+}
+
+void QGridLayoutPrivate::add(QGridBox *box, int row, int col)
+{
+ expand(row + 1, col + 1);
+ box->row = box->torow = row;
+ box->col = box->tocol = col;
+ things.append(box);
+ setDirty();
+ setNextPosAfter(row, col);
+}
+
+void QGridLayoutPrivate::add(QGridBox *box, int row1, int row2, int col1, int col2)
+{
+ if (row2 >= 0 && row2 < row1)
+ qWarning("QGridLayout: Multi-cell fromRow greater than toRow");
+ if (col2 >= 0 && col2 < col1)
+ qWarning("QGridLayout: Multi-cell fromCol greater than toCol");
+ if (row1 == row2 && col1 == col2) {
+ add(box, row1, col1);
+ return;
+ }
+ expand(row2 + 1, col2 + 1);
+ box->row = row1;
+ box->col = col1;
+
+ box->torow = row2;
+ box->tocol = col2;
+
+ things.append(box);
+ setDirty();
+ if (col2 < 0)
+ col2 = cc - 1;
+
+ setNextPosAfter(row2, col2);
+}
+
+void QGridLayoutPrivate::addData(QGridBox *box, const QGridLayoutSizeTriple &sizes, bool r, bool c)
+{
+ const QWidget *widget = box->item()->widget();
+
+ if (box->isEmpty() && widget)
+ return;
+
+ if (c) {
+ QLayoutStruct *data = &colData[box->col];
+ if (!cStretch.at(box->col))
+ data->stretch = qMax(data->stretch, box->hStretch());
+ data->sizeHint = qMax(sizes.hint.width(), data->sizeHint);
+ data->minimumSize = qMax(sizes.minS.width(), data->minimumSize);
+
+ qMaxExpCalc(data->maximumSize, data->expansive, data->empty, sizes.maxS.width(),
+ box->expandingDirections() & Qt::Horizontal, box->isEmpty());
+ }
+ if (r) {
+ QLayoutStruct *data = &rowData[box->row];
+ if (!rStretch.at(box->row))
+ data->stretch = qMax(data->stretch, box->vStretch());
+ data->sizeHint = qMax(sizes.hint.height(), data->sizeHint);
+ data->minimumSize = qMax(sizes.minS.height(), data->minimumSize);
+
+ qMaxExpCalc(data->maximumSize, data->expansive, data->empty, sizes.maxS.height(),
+ box->expandingDirections() & Qt::Vertical, box->isEmpty());
+ }
+}
+
+static void initEmptyMultiBox(QVector<QLayoutStruct> &chain, int start, int end)
+{
+ for (int i = start; i <= end; i++) {
+ QLayoutStruct *data = &chain[i];
+ if (data->empty && data->maximumSize == 0) // truly empty box
+ data->maximumSize = QWIDGETSIZE_MAX;
+ data->empty = false;
+ }
+}
+
+static void distributeMultiBox(QVector<QLayoutStruct> &chain, int start, int end, int minSize,
+ int sizeHint, QVector<int> &stretchArray, int stretch)
+{
+ int i;
+ int w = 0;
+ int wh = 0;
+ int max = 0;
+
+ for (i = start; i <= end; i++) {
+ QLayoutStruct *data = &chain[i];
+ w += data->minimumSize;
+ wh += data->sizeHint;
+ max += data->maximumSize;
+ if (stretchArray.at(i) == 0)
+ data->stretch = qMax(data->stretch, stretch);
+
+ if (i != end) {
+ int spacing = data->spacing;
+ w += spacing;
+ wh += spacing;
+ max += spacing;
+ }
+ }
+
+ if (max < minSize) { // implies w < minSize
+ /*
+ We must increase the maximum size of at least one of the
+ items. qGeomCalc() will put the extra space in between the
+ items. We must recover that extra space and put it
+ somewhere. It does not really matter where, since the user
+ can always specify stretch factors and avoid this code.
+ */
+ qGeomCalc(chain, start, end - start + 1, 0, minSize);
+ int pos = 0;
+ for (i = start; i <= end; i++) {
+ QLayoutStruct *data = &chain[i];
+ int nextPos = (i == end) ? minSize : chain.at(i + 1).pos;
+ int realSize = nextPos - pos;
+ if (i != end)
+ realSize -= data->spacing;
+ if (data->minimumSize < realSize)
+ data->minimumSize = realSize;
+ if (data->maximumSize < data->minimumSize)
+ data->maximumSize = data->minimumSize;
+ pos = nextPos;
+ }
+ } else if (w < minSize) {
+ qGeomCalc(chain, start, end - start + 1, 0, minSize);
+ for (i = start; i <= end; i++) {
+ QLayoutStruct *data = &chain[i];
+ if (data->minimumSize < data->size)
+ data->minimumSize = data->size;
+ }
+ }
+
+ if (wh < sizeHint) {
+ qGeomCalc(chain, start, end - start + 1, 0, sizeHint);
+ for (i = start; i <= end; i++) {
+ QLayoutStruct *data = &chain[i];
+ if (data->sizeHint < data->size)
+ data->sizeHint = data->size;
+ }
+ }
+}
+
+static QGridBox *&gridAt(QGridBox *grid[], int r, int c, int cc,
+ Qt::Orientation orientation = Qt::Vertical)
+{
+ if (orientation == Qt::Horizontal)
+ qSwap(r, c);
+ return grid[(r * cc) + c];
+}
+
+void QGridLayoutPrivate::setupSpacings(QVector<QLayoutStruct> &chain,
+ QGridBox *grid[], int fixedSpacing,
+ Qt::Orientation orientation)
+{
+ Q_Q(QGridLayout);
+ int numRows = rr; // or columns if orientation is horizontal
+ int numColumns = cc; // or rows if orientation is horizontal
+
+ if (orientation == Qt::Horizontal) {
+ qSwap(numRows, numColumns);
+ }
+
+ QStyle *style = 0;
+ if (fixedSpacing < 0) {
+ if (QWidget *parentWidget = q->parentWidget())
+ style = parentWidget->style();
+ }
+
+ for (int c = 0; c < numColumns; ++c) {
+ QGridBox *previousBox = 0;
+ int previousRow = -1; // previous *non-empty* row
+
+ for (int r = 0; r < numRows; ++r) {
+ if (chain.at(r).empty)
+ continue;
+
+ QGridBox *box = gridAt(grid, r, c, cc, orientation);
+ if (previousRow != -1 && (!box || previousBox != box)) {
+ int spacing = fixedSpacing;
+ if (spacing < 0) {
+ QSizePolicy::ControlTypes controlTypes1 = QSizePolicy::DefaultType;
+ QSizePolicy::ControlTypes controlTypes2 = QSizePolicy::DefaultType;
+ if (previousBox)
+ controlTypes1 = previousBox->item()->controlTypes();
+ if (box)
+ controlTypes2 = box->item()->controlTypes();
+
+ if ((orientation == Qt::Horizontal && hReversed)
+ || (orientation == Qt::Vertical && vReversed))
+ qSwap(controlTypes1, controlTypes2);
+
+ if (style)
+ spacing = style->combinedLayoutSpacing(controlTypes1, controlTypes2,
+ orientation, 0, q->parentWidget());
+ } else {
+ if (orientation == Qt::Vertical) {
+ QGridBox *sibling = vReversed ? previousBox : box;
+ if (sibling) {
+ QWidget *wid = sibling->item()->widget();
+ if (wid)
+ spacing = qMax(spacing, sibling->item()->geometry().top() - wid->geometry().top() );
+ }
+ }
+ }
+
+ if (spacing > chain.at(previousRow).spacing)
+ chain[previousRow].spacing = spacing;
+ }
+
+ previousBox = box;
+ previousRow = r;
+ }
+ }
+}
+
+//#define QT_LAYOUT_DISABLE_CACHING
+
+void QGridLayoutPrivate::setupLayoutData(int hSpacing, int vSpacing)
+{
+ Q_Q(QGridLayout);
+
+#ifndef QT_LAYOUT_DISABLE_CACHING
+ if (!needRecalc)
+ return;
+#endif
+ has_hfw = false;
+ int i;
+
+ for (i = 0; i < rr; i++) {
+ rowData[i].init(rStretch.at(i), rMinHeights.at(i));
+ rowData[i].maximumSize = rStretch.at(i) ? QLAYOUTSIZE_MAX : rMinHeights.at(i);
+ }
+ for (i = 0; i < cc; i++) {
+ colData[i].init(cStretch.at(i), cMinWidths.at(i));
+ colData[i].maximumSize = cStretch.at(i) ? QLAYOUTSIZE_MAX : cMinWidths.at(i);
+ }
+
+ int n = things.size();
+ QVarLengthArray<QGridLayoutSizeTriple> sizes(n);
+
+ bool has_multi = false;
+
+ /*
+ Grid of items. We use it to determine which items are
+ adjacent to which and compute the spacings correctly.
+ */
+ QVarLengthArray<QGridBox *> grid(rr * cc);
+ qMemSet(grid.data(), 0, rr * cc * sizeof(QGridBox *));
+
+ /*
+ Initialize 'sizes' and 'grid' data structures, and insert
+ non-spanning items to our row and column data structures.
+ */
+ for (i = 0; i < n; ++i) {
+ QGridBox * const box = things.at(i);
+ sizes[i].minS = box->minimumSize();
+ sizes[i].hint = box->sizeHint();
+ sizes[i].maxS = box->maximumSize();
+
+ if (box->hasHeightForWidth())
+ has_hfw = true;
+
+ if (box->row == box->toRow(rr)) {
+ addData(box, sizes[i], true, false);
+ } else {
+ initEmptyMultiBox(rowData, box->row, box->toRow(rr));
+ has_multi = true;
+ }
+
+ if (box->col == box->toCol(cc)) {
+ addData(box, sizes[i], false, true);
+ } else {
+ initEmptyMultiBox(colData, box->col, box->toCol(cc));
+ has_multi = true;
+ }
+
+ for (int r = box->row; r <= box->toRow(rr); ++r) {
+ for (int c = box->col; c <= box->toCol(cc); ++c) {
+ gridAt(grid.data(), r, c, cc) = box;
+ }
+ }
+ }
+
+ setupSpacings(colData, grid.data(), hSpacing, Qt::Horizontal);
+ setupSpacings(rowData, grid.data(), vSpacing, Qt::Vertical);
+
+ /*
+ Insert multicell items to our row and column data structures.
+ This must be done after the non-spanning items to obtain a
+ better distribution in distributeMultiBox().
+ */
+ if (has_multi) {
+ for (i = 0; i < n; ++i) {
+ QGridBox * const box = things.at(i);
+
+ if (box->row != box->toRow(rr))
+ distributeMultiBox(rowData, box->row, box->toRow(rr), sizes[i].minS.height(),
+ sizes[i].hint.height(), rStretch, box->vStretch());
+ if (box->col != box->toCol(cc))
+ distributeMultiBox(colData, box->col, box->toCol(cc), sizes[i].minS.width(),
+ sizes[i].hint.width(), cStretch, box->hStretch());
+ }
+ }
+
+ for (i = 0; i < rr; i++)
+ rowData[i].expansive = rowData.at(i).expansive || rowData.at(i).stretch > 0;
+ for (i = 0; i < cc; i++)
+ colData[i].expansive = colData.at(i).expansive || colData.at(i).stretch > 0;
+
+ q->getContentsMargins(&leftMargin, &topMargin, &rightMargin, &bottomMargin);
+
+ needRecalc = false;
+}
+
+void QGridLayoutPrivate::addHfwData(QGridBox *box, int width)
+{
+ QVector<QLayoutStruct> &rData = *hfwData;
+ if (box->hasHeightForWidth()) {
+ int hint = box->heightForWidth(width);
+ rData[box->row].sizeHint = qMax(hint, rData.at(box->row).sizeHint);
+ rData[box->row].minimumSize = qMax(hint, rData.at(box->row).minimumSize);
+ } else {
+ QSize hint = box->sizeHint();
+ QSize minS = box->minimumSize();
+ rData[box->row].sizeHint = qMax(hint.height(), rData.at(box->row).sizeHint);
+ rData[box->row].minimumSize = qMax(minS.height(), rData.at(box->row).minimumSize);
+ }
+}
+
+/*
+ Similar to setupLayoutData(), but uses heightForWidth(colData)
+ instead of sizeHint(). Assumes that setupLayoutData() and
+ qGeomCalc(colData) has been called.
+*/
+void QGridLayoutPrivate::setupHfwLayoutData()
+{
+ QVector<QLayoutStruct> &rData = *hfwData;
+ for (int i = 0; i < rr; i++) {
+ rData[i] = rowData.at(i);
+ rData[i].minimumSize = rData[i].sizeHint = rMinHeights.at(i);
+ }
+
+ for (int pass = 0; pass < 2; ++pass) {
+ for (int i = 0; i < things.size(); ++i) {
+ QGridBox *box = things.at(i);
+ int r1 = box->row;
+ int c1 = box->col;
+ int r2 = box->toRow(rr);
+ int c2 = box->toCol(cc);
+ int w = colData.at(c2).pos + colData.at(c2).size - colData.at(c1).pos;
+
+ if (r1 == r2) {
+ if (pass == 0)
+ addHfwData(box, w);
+ } else {
+ if (pass == 0) {
+ initEmptyMultiBox(rData, r1, r2);
+ } else {
+ QSize hint = box->sizeHint();
+ QSize min = box->minimumSize();
+ if (box->hasHeightForWidth()) {
+ int hfwh = box->heightForWidth(w);
+ if (hfwh > hint.height())
+ hint.setHeight(hfwh);
+ if (hfwh > min.height())
+ min.setHeight(hfwh);
+ }
+ distributeMultiBox(rData, r1, r2, min.height(), hint.height(),
+ rStretch, box->vStretch());
+ }
+ }
+ }
+ }
+ for (int i = 0; i < rr; i++)
+ rData[i].expansive = rData.at(i).expansive || rData.at(i).stretch > 0;
+}
+
+void QGridLayoutPrivate::distribute(QRect r, int hSpacing, int vSpacing)
+{
+ Q_Q(QGridLayout);
+ bool visualHReversed = hReversed;
+ QWidget *parent = q->parentWidget();
+ if (parent && parent->isRightToLeft())
+ visualHReversed = !visualHReversed;
+
+ setupLayoutData(hSpacing, vSpacing);
+
+ int left, top, right, bottom;
+ effectiveMargins(&left, &top, &right, &bottom);
+ r.adjust(+left, +top, -right, -bottom);
+
+ qGeomCalc(colData, 0, cc, r.x(), r.width());
+ QVector<QLayoutStruct> *rDataPtr;
+ if (has_hfw) {
+ recalcHFW(r.width());
+ qGeomCalc(*hfwData, 0, rr, r.y(), r.height());
+ rDataPtr = hfwData;
+ } else {
+ qGeomCalc(rowData, 0, rr, r.y(), r.height());
+ rDataPtr = &rowData;
+ }
+ QVector<QLayoutStruct> &rData = *rDataPtr;
+ int i;
+
+ bool reverse = ((r.bottom() > rect.bottom()) || (r.bottom() == rect.bottom()
+ && ((r.right() > rect.right()) != visualHReversed)));
+ int n = things.size();
+ for (i = 0; i < n; ++i) {
+ QGridBox *box = things.at(reverse ? n-i-1 : i);
+ int r2 = box->toRow(rr);
+ int c2 = box->toCol(cc);
+
+ int x = colData.at(box->col).pos;
+ int y = rData.at(box->row).pos;
+ int x2p = colData.at(c2).pos + colData.at(c2).size; // x2+1
+ int y2p = rData.at(r2).pos + rData.at(r2).size; // y2+1
+ int w = x2p - x;
+ int h = y2p - y;
+
+ if (visualHReversed)
+ x = r.left() + r.right() - x - w + 1;
+ if (vReversed)
+ y = r.top() + r.bottom() - y - h + 1;
+
+ box->setGeometry(QRect(x, y, w, h));
+ }
+}
+
+QRect QGridLayoutPrivate::cellRect(int row, int col) const
+{
+ if (row < 0 || row >= rr || col < 0 || col >= cc)
+ return QRect();
+
+ const QVector<QLayoutStruct> *rDataPtr;
+ if (has_hfw && hfwData)
+ rDataPtr = hfwData;
+ else
+ rDataPtr = &rowData;
+ return QRect(colData.at(col).pos, rDataPtr->at(row).pos,
+ colData.at(col).size, rDataPtr->at(row).size);
+}
+
+/*!
+ \class QGridLayout
+
+ \brief The QGridLayout class lays out widgets in a grid.
+
+ \ingroup geomanagement
+ \ingroup appearance
+ \mainclass
+
+ QGridLayout takes the space made available to it (by its parent
+ layout or by the parentWidget()), divides it up into rows and
+ columns, and puts each widget it manages into the correct cell.
+
+ Columns and rows behave identically; we will discuss columns, but
+ there are equivalent functions for rows.
+
+ Each column has a minimum width and a stretch factor. The minimum
+ width is the greatest of that set using setColumnMinimumWidth() and the
+ minimum width of each widget in that column. The stretch factor is
+ set using setColumnStretch() and determines how much of the available
+ space the column will get over and above its necessary minimum.
+
+ Normally, each managed widget or layout is put into a cell of its
+ own using addWidget(). It is also possible for a widget to occupy
+ multiple cells using the row and column spanning overloads of
+ addItem() and addWidget(). If you do this, QGridLayout will guess
+ how to distribute the size over the columns/rows (based on the
+ stretch factors).
+
+ To remove a widget from a layout, call removeWidget(). Calling
+ QWidget::hide() on a widget also effectively removes the widget
+ from the layout until QWidget::show() is called.
+
+ This illustration shows a fragment of a dialog with a five-column,
+ three-row grid (the grid is shown overlaid in magenta):
+
+ \image gridlayout.png A grid layout
+
+ Columns 0, 2 and 4 in this dialog fragment are made up of a
+ QLabel, a QLineEdit, and a QListBox. Columns 1 and 3 are
+ placeholders made with setColumnMinimumWidth(). Row 0 consists of three
+ QLabel objects, row 1 of three QLineEdit objects and row 2 of
+ three QListBox objects. We used placeholder columns (1 and 3) to
+ get the right amount of space between the columns.
+
+ Note that the columns and rows are not equally wide or tall. If
+ you want two columns to have the same width, you must set their
+ minimum widths and stretch factors to be the same yourself. You do
+ this using setColumnMinimumWidth() and setColumnStretch().
+
+ If the QGridLayout is not the top-level layout (i.e. does not
+ manage all of the widget's area and children), you must add it to
+ its parent layout when you create it, but before you do anything
+ with it. The normal way to add a layout is by calling
+ addLayout() on the parent layout.
+
+ Once you have added your layout you can start putting widgets and
+ other layouts into the cells of your grid layout using
+ addWidget(), addItem(), and addLayout().
+
+ QGridLayout also includes two margin widths: the margin() and the
+ spacing(). The margin is the width of the reserved space along
+ each of the QGridLayout's four sides. The spacing is the width of
+ the automatically allocated spacing between neighboring boxes.
+
+ The default margin() and spacing() values are provided by the
+ style. The default margin Qt styles specify is 9 for child
+ widgets and 11 for windows. The spacing defaults to the same as
+ the margin width for a top-level layout, or to the same as the
+ parent layout.
+
+ \sa QBoxLayout, QStackedLayout, {Layout Classes}, {Basic Layouts Example}
+*/
+
+
+/*!
+ Constructs a new QGridLayout with parent widget, \a parent. The
+ layout has one row and one column initially, and will expand when
+ new items are inserted.
+*/
+QGridLayout::QGridLayout(QWidget *parent)
+ : QLayout(*new QGridLayoutPrivate, 0, parent)
+{
+ Q_D(QGridLayout);
+ d->expand(1, 1);
+}
+
+/*!
+ Constructs a new grid layout.
+
+ You must insert this grid into another layout. You can insert
+ widgets and layouts into this layout at any time, but laying out
+ will not be performed before this is inserted into another layout.
+*/
+QGridLayout::QGridLayout()
+ : QLayout(*new QGridLayoutPrivate, 0, 0)
+{
+ Q_D(QGridLayout);
+ d->expand(1, 1);
+}
+
+
+#ifdef QT3_SUPPORT
+/*!
+ \obsolete
+ Constructs a new QGridLayout with \a nRows rows, \a nCols columns
+ and parent widget, \a parent. \a parent may not be 0. The grid
+ layout is called \a name.
+
+ \a margin is the number of pixels between the edge of the widget
+ and its managed children. \a space is the default number of pixels
+ between cells. If \a space is -1, the value of \a margin is used.
+*/
+QGridLayout::QGridLayout(QWidget *parent, int nRows, int nCols, int margin,
+ int space, const char *name)
+ : QLayout(*new QGridLayoutPrivate, 0, parent)
+{
+ Q_D(QGridLayout);
+ d->expand(nRows, nCols);
+ setMargin(margin);
+ setSpacing(space < 0 ? margin : space);
+ setObjectName(QString::fromAscii(name));
+}
+
+/*!
+ \obsolete
+
+ Constructs a new grid with \a nRows rows and \a nCols columns. If
+ \a spacing is -1, this QGridLayout inherits its parent's
+ spacing(); otherwise \a spacing is used. The grid layout is called
+ \a name.
+
+ You must insert this grid into another layout. You can insert
+ widgets and layouts into this layout at any time, but laying out
+ will not be performed before this is inserted into another layout.
+*/
+QGridLayout::QGridLayout(QLayout *parentLayout, int nRows, int nCols,
+ int spacing, const char *name)
+ : QLayout(*new QGridLayoutPrivate, parentLayout, 0)
+{
+ Q_D(QGridLayout);
+ d->expand(nRows, nCols);
+ setSpacing(spacing);
+ setObjectName(QString::fromAscii(name));
+}
+
+/*!
+ \obsolete
+
+ Constructs a new grid with \a nRows rows and \a nCols columns. If
+ \a spacing is -1, this QGridLayout inherits its parent's
+ spacing(); otherwise \a spacing is used. The grid layout is called
+ \a name.
+
+ You must insert this grid into another layout. You can insert
+ widgets and layouts into this layout at any time, but laying out
+ will not be performed before this is inserted into another layout.
+*/
+QGridLayout::QGridLayout(int nRows, int nCols, int spacing, const char *name)
+ : QLayout(*new QGridLayoutPrivate, 0, 0)
+{
+ Q_D(QGridLayout);
+ d->expand(nRows, nCols);
+ setSpacing(spacing);
+ setObjectName(QString::fromAscii(name));
+}
+#endif
+
+
+/*!
+\internal (mostly)
+
+Sets the positioning mode used by addItem(). If \a orient is
+Qt::Horizontal, this layout is expanded to \a n columns, and items
+will be added columns-first. Otherwise it is expanded to \a n rows and
+items will be added rows-first.
+*/
+
+void QGridLayout::setDefaultPositioning(int n, Qt::Orientation orient)
+{
+ Q_D(QGridLayout);
+ if (orient == Qt::Horizontal) {
+ d->expand(1, n);
+ d->addVertical = false;
+ } else {
+ d->expand(n,1);
+ d->addVertical = true;
+ }
+}
+
+
+/*!
+ Destroys the grid layout. Geometry management is terminated if
+ this is a top-level grid.
+
+ The layout's widgets aren't destroyed.
+*/
+QGridLayout::~QGridLayout()
+{
+ Q_D(QGridLayout);
+ d->deleteAll();
+}
+
+/*!
+ \property QGridLayout::horizontalSpacing
+ \brief the spacing between widgets that are laid out side by side
+ \since 4.3
+
+ If no value is explicitly set, the layout's horizontal spacing is
+ inherited from the parent layout, or from the style settings for
+ the parent widget.
+
+ \sa verticalSpacing, QStyle::pixelMetric(), {QStyle::}{PM_LayoutHorizontalSpacing}
+*/
+void QGridLayout::setHorizontalSpacing(int spacing)
+{
+ Q_D(QGridLayout);
+ d->horizontalSpacing = spacing;
+ invalidate();
+}
+
+int QGridLayout::horizontalSpacing() const
+{
+ Q_D(const QGridLayout);
+ if (d->horizontalSpacing >= 0) {
+ return d->horizontalSpacing;
+ } else {
+ return qSmartSpacing(this, QStyle::PM_LayoutHorizontalSpacing);
+ }
+}
+
+/*!
+ \property QGridLayout::verticalSpacing
+ \brief the spacing between widgets that are laid out on top of each other
+ \since 4.3
+
+ If no value is explicitly set, the layout's vertical spacing is
+ inherited from the parent layout, or from the style settings for
+ the parent widget.
+
+ \sa horizontalSpacing, QStyle::pixelMetric(), {QStyle::}{PM_LayoutHorizontalSpacing}
+*/
+void QGridLayout::setVerticalSpacing(int spacing)
+{
+ Q_D(QGridLayout);
+ d->verticalSpacing = spacing;
+ invalidate();
+}
+
+int QGridLayout::verticalSpacing() const
+{
+ Q_D(const QGridLayout);
+ if (d->verticalSpacing >= 0) {
+ return d->verticalSpacing;
+ } else {
+ return qSmartSpacing(this, QStyle::PM_LayoutVerticalSpacing);
+ }
+}
+
+/*!
+ This function sets both the vertical and horizontal spacing to
+ \a spacing.
+
+ \sa setVerticalSpacing(), setHorizontalSpacing()
+*/
+void QGridLayout::setSpacing(int spacing)
+{
+ Q_D(QGridLayout);
+ d->horizontalSpacing = d->verticalSpacing = spacing;
+ invalidate();
+}
+
+/*!
+ If the vertical spacing is equal to the horizontal spacing,
+ this function returns that value; otherwise it return -1.
+
+ \sa setSpacing(), verticalSpacing(), horizontalSpacing()
+*/
+int QGridLayout::spacing() const
+{
+ int hSpacing = horizontalSpacing();
+ if (hSpacing == verticalSpacing()) {
+ return hSpacing;
+ } else {
+ return -1;
+ }
+}
+
+/*!
+ Returns the number of rows in this grid.
+*/
+int QGridLayout::rowCount() const
+{
+ Q_D(const QGridLayout);
+ return d->numRows();
+}
+
+/*!
+ Returns the number of columns in this grid.
+*/
+int QGridLayout::columnCount() const
+{
+ Q_D(const QGridLayout);
+ return d->numCols();
+}
+
+/*!
+ \reimp
+*/
+QSize QGridLayout::sizeHint() const
+{
+ Q_D(const QGridLayout);
+ QSize result(d->sizeHint(horizontalSpacing(), verticalSpacing()));
+ int left, top, right, bottom;
+ d->effectiveMargins(&left, &top, &right, &bottom);
+ result += QSize(left + right, top + bottom);
+ return result;
+}
+
+/*!
+ \reimp
+*/
+QSize QGridLayout::minimumSize() const
+{
+ Q_D(const QGridLayout);
+ QSize result(d->minimumSize(horizontalSpacing(), verticalSpacing()));
+ int left, top, right, bottom;
+ d->effectiveMargins(&left, &top, &right, &bottom);
+ result += QSize(left + right, top + bottom);
+ return result;
+}
+
+/*!
+ \reimp
+*/
+QSize QGridLayout::maximumSize() const
+{
+ Q_D(const QGridLayout);
+
+ QSize s = d->maximumSize(horizontalSpacing(), verticalSpacing());
+ int left, top, right, bottom;
+ d->effectiveMargins(&left, &top, &right, &bottom);
+ s += QSize(left + right, top + bottom);
+ s = s.boundedTo(QSize(QLAYOUTSIZE_MAX, QLAYOUTSIZE_MAX));
+ if (alignment() & Qt::AlignHorizontal_Mask)
+ s.setWidth(QLAYOUTSIZE_MAX);
+ if (alignment() & Qt::AlignVertical_Mask)
+ s.setHeight(QLAYOUTSIZE_MAX);
+ return s;
+}
+
+/*!
+ \reimp
+*/
+bool QGridLayout::hasHeightForWidth() const
+{
+ return ((QGridLayout*)this)->d_func()->hasHeightForWidth(horizontalSpacing(), verticalSpacing());
+}
+
+/*!
+ \reimp
+*/
+int QGridLayout::heightForWidth(int w) const
+{
+ Q_D(const QGridLayout);
+ QGridLayoutPrivate *dat = const_cast<QGridLayoutPrivate *>(d);
+ return dat->heightForWidth(w, horizontalSpacing(), verticalSpacing());
+}
+
+/*!
+ \reimp
+*/
+int QGridLayout::minimumHeightForWidth(int w) const
+{
+ Q_D(const QGridLayout);
+ QGridLayoutPrivate *dat = const_cast<QGridLayoutPrivate *>(d);
+ return dat->minimumHeightForWidth(w, horizontalSpacing(), verticalSpacing());
+}
+
+#ifdef QT3_SUPPORT
+/*!
+ \compat
+
+ Searches for widget \a w in this layout (not including child
+ layouts). If \a w is found, it sets \c{*}\a{row} and
+ \c{*}\a{column} to the row and column that the widget
+ occupies and returns true; otherwise returns false.
+
+ If the widget spans multiple rows/columns, the top-left cell
+ is returned.
+
+ Use indexOf() and getItemPosition() instead.
+*/
+bool QGridLayout::findWidget(QWidget* w, int *row, int *column)
+{
+ Q_D(QGridLayout);
+ int index = indexOf(w);
+ if (index < 0)
+ return false;
+ int dummy1, dummy2;
+ d->getItemPosition(index, row, column, &dummy1, &dummy2);
+ return true;
+}
+#endif
+/*!
+ \reimp
+*/
+int QGridLayout::count() const
+{
+ Q_D(const QGridLayout);
+ return d->count();
+}
+
+
+/*!
+ \reimp
+*/
+QLayoutItem *QGridLayout::itemAt(int index) const
+{
+ Q_D(const QGridLayout);
+ return d->itemAt(index);
+}
+
+/*!
+ \since 4.4
+
+ Returns the layout item that occupies cell (\a row, \a column), or 0 if
+ the cell is empty.
+
+ \sa getItemPosition(), indexOf()
+*/
+QLayoutItem *QGridLayout::itemAtPosition(int row, int column) const
+{
+ Q_D(const QGridLayout);
+ int n = d->things.count();
+ for (int i = 0; i < n; ++i) {
+ QGridBox *box = d->things.at(i);
+ if (row >= box->row && row <= box->toRow(d->rr)
+ && column >= box->col && column <= box->toCol(d->cc)) {
+ return box->item();
+ }
+ }
+ return 0;
+}
+
+/*!
+ \reimp
+*/
+QLayoutItem *QGridLayout::takeAt(int index)
+{
+ Q_D(QGridLayout);
+ return d->takeAt(index);
+}
+
+/*!
+ Returns the position information of the item with the given \a index.
+
+ The variables passed as \a row and \a column are updated with the position of the
+ item in the layout, and the \a rowSpan and \a columnSpan variables are updated
+ with the vertical and horizontal spans of the item.
+
+ \sa itemAtPosition(), itemAt()
+*/
+void QGridLayout::getItemPosition(int index, int *row, int *column, int *rowSpan, int *columnSpan)
+{
+ Q_D(QGridLayout);
+ d->getItemPosition(index, row, column, rowSpan, columnSpan);
+}
+
+
+/*!
+ \reimp
+*/
+void QGridLayout::setGeometry(const QRect &rect)
+{
+ Q_D(QGridLayout);
+ if (d->isDirty() || rect != geometry()) {
+ QRect cr = alignment() ? alignmentRect(rect) : rect;
+ d->distribute(cr, horizontalSpacing(), verticalSpacing());
+ QLayout::setGeometry(rect);
+ }
+}
+
+/*!
+ Returns the geometry of the cell with row \a row and column \a column
+ in the grid. Returns an invalid rectangle if \a row or \a column is
+ outside the grid.
+
+ \warning in the current version of Qt this function does not
+ return valid results until setGeometry() has been called, i.e.
+ after the parentWidget() is visible.
+*/
+QRect QGridLayout::cellRect(int row, int column) const
+{
+ Q_D(const QGridLayout);
+ return d->cellRect(row, column);
+}
+#ifdef QT3_SUPPORT
+/*!
+ \obsolete
+ Expands this grid so that it will have \a nRows rows and \a nCols
+ columns. Will not shrink the grid. You should not need to call
+ this function because QGridLayout expands automatically as new
+ items are inserted.
+*/
+void QGridLayout::expand(int nRows, int nCols)
+{
+ Q_D(QGridLayout);
+ d->expand(nRows, nCols);
+}
+#endif
+
+/*!
+ \reimp
+*/
+void QGridLayout::addItem(QLayoutItem *item)
+{
+ Q_D(QGridLayout);
+ int r, c;
+ d->getNextPos(r, c);
+ addItem(item, r, c);
+}
+
+/*!
+ Adds \a item at position \a row, \a column, spanning \a rowSpan
+ rows and \a columnSpan columns, and aligns it according to \a
+ alignment. If \a rowSpan and/or \a columnSpan is -1, then the item
+ will extend to the bottom and/or right edge, respectively. The
+ layout takes ownership of the \a item.
+
+ \warning Do not use this function to add child layouts or child
+ widget items. Use addLayout() or addWidget() instead.
+*/
+void QGridLayout::addItem(QLayoutItem *item, int row, int column, int rowSpan, int columnSpan, Qt::Alignment alignment)
+{
+ Q_D(QGridLayout);
+ QGridBox *b = new QGridBox(item);
+ b->setAlignment(alignment);
+ d->add(b, row, (rowSpan < 0) ? -1 : row + rowSpan - 1, column, (columnSpan < 0) ? -1 : column + columnSpan - 1);
+ invalidate();
+}
+
+/*
+ Returns true if the widget \a w can be added to the layout \a l;
+ otherwise returns false.
+*/
+static bool checkWidget(QLayout *l, QWidget *w)
+{
+ if (!w) {
+ qWarning("QLayout: Cannot add null widget to %s/%s", l->metaObject()->className(),
+ l->objectName().toLocal8Bit().data());
+ return false;
+ }
+ return true;
+}
+
+/*!
+ Adds the given \a widget to the cell grid at \a row, \a column. The
+ top-left position is (0, 0) by default.
+
+ The alignment is specified by \a alignment. The default
+ alignment is 0, which means that the widget fills the entire cell.
+
+*/
+void QGridLayout::addWidget(QWidget *widget, int row, int column, Qt::Alignment alignment)
+{
+ if (!checkWidget(this, widget))
+ return;
+ if (row < 0 || column < 0) {
+ qWarning("QGridLayout: Cannot add %s/%s to %s/%s at row %d column %d",
+ widget->metaObject()->className(), widget->objectName().toLocal8Bit().data(),
+ metaObject()->className(), objectName().toLocal8Bit().data(), row, column);
+ return;
+ }
+ addChildWidget(widget);
+ QWidgetItem *b = QLayoutPrivate::createWidgetItem(this, widget);
+ addItem(b, row, column, 1, 1, alignment);
+}
+
+/*!
+ \overload
+
+ This version adds the given \a widget to the cell grid, spanning
+ multiple rows/columns. The cell will start at \a fromRow, \a
+ fromColumn spanning \a rowSpan rows and \a columnSpan columns. The
+ \a widget will have the given \a alignment.
+
+ If \a rowSpan and/or \a columnSpan is -1, then the widget will
+ extend to the bottom and/or right edge, respectively.
+
+*/
+void QGridLayout::addWidget(QWidget *widget, int fromRow, int fromColumn,
+ int rowSpan, int columnSpan, Qt::Alignment alignment)
+{
+ Q_D(QGridLayout);
+ if (!checkWidget(this, widget))
+ return;
+ int toRow = (rowSpan < 0) ? -1 : fromRow + rowSpan - 1;
+ int toColumn = (columnSpan < 0) ? -1 : fromColumn + columnSpan - 1;
+ addChildWidget(widget);
+ QGridBox *b = new QGridBox(this, widget);
+ b->setAlignment(alignment);
+ d->add(b, fromRow, toRow, fromColumn, toColumn);
+ invalidate();
+}
+
+/*!
+ \fn void QGridLayout::addWidget(QWidget *widget)
+
+ \overload
+ \internal
+*/
+
+/*!
+ Places the \a layout at position (\a row, \a column) in the grid. The
+ top-left position is (0, 0).
+
+ The alignment is specified by \a alignment. The default
+ alignment is 0, which means that the widget fills the entire cell.
+
+ A non-zero alignment indicates that the layout should not grow to
+ fill the available space but should be sized according to
+ sizeHint().
+
+
+ \a layout becomes a child of the grid layout.
+*/
+void QGridLayout::addLayout(QLayout *layout, int row, int column, Qt::Alignment alignment)
+{
+ Q_D(QGridLayout);
+ addChildLayout(layout);
+ QGridBox *b = new QGridBox(layout);
+ b->setAlignment(alignment);
+ d->add(b, row, column);
+}
+
+/*!
+ \overload
+ This version adds the layout \a layout to the cell grid, spanning multiple
+ rows/columns. The cell will start at \a row, \a column spanning \a
+ rowSpan rows and \a columnSpan columns.
+
+ If \a rowSpan and/or \a columnSpan is -1, then the layout will extend to the bottom
+ and/or right edge, respectively.
+*/
+void QGridLayout::addLayout(QLayout *layout, int row, int column,
+ int rowSpan, int columnSpan, Qt::Alignment alignment)
+{
+ Q_D(QGridLayout);
+ addChildLayout(layout);
+ QGridBox *b = new QGridBox(layout);
+ b->setAlignment(alignment);
+ d->add(b, row, (rowSpan < 0) ? -1 : row + rowSpan - 1, column, (columnSpan < 0) ? -1 : column + columnSpan - 1);
+}
+
+/*!
+ Sets the stretch factor of row \a row to \a stretch. The first row
+ is number 0.
+
+ The stretch factor is relative to the other rows in this grid.
+ Rows with a higher stretch factor take more of the available
+ space.
+
+ The default stretch factor is 0. If the stretch factor is 0 and no
+ other row in this table can grow at all, the row may still grow.
+
+ \sa rowStretch(), setRowMinimumHeight(), setColumnStretch()
+*/
+void QGridLayout::setRowStretch(int row, int stretch)
+{
+ Q_D(QGridLayout);
+ d->setRowStretch(row, stretch);
+ invalidate();
+}
+
+/*!
+ Returns the stretch factor for row \a row.
+
+ \sa setRowStretch()
+*/
+int QGridLayout::rowStretch(int row) const
+{
+ Q_D(const QGridLayout);
+ return d->rowStretch(row);
+}
+
+/*!
+ Returns the stretch factor for column \a column.
+
+ \sa setColumnStretch()
+*/
+int QGridLayout::columnStretch(int column) const
+{
+ Q_D(const QGridLayout);
+ return d->colStretch(column);
+}
+
+/*!
+ Sets the stretch factor of column \a column to \a stretch. The first
+ column is number 0.
+
+ The stretch factor is relative to the other columns in this grid.
+ Columns with a higher stretch factor take more of the available
+ space.
+
+ The default stretch factor is 0. If the stretch factor is 0 and no
+ other column in this table can grow at all, the column may still
+ grow.
+
+ An alternative approach is to add spacing using addItem() with a
+ QSpacerItem.
+
+ \sa columnStretch(), setRowStretch()
+*/
+void QGridLayout::setColumnStretch(int column, int stretch)
+{
+ Q_D(QGridLayout);
+ d->setColStretch(column, stretch);
+ invalidate();
+}
+
+
+
+/*!
+ Sets the minimum height of row \a row to \a minSize pixels.
+
+ \sa rowMinimumHeight(), setColumnMinimumWidth()
+*/
+void QGridLayout::setRowMinimumHeight(int row, int minSize)
+{
+ Q_D(QGridLayout);
+ d->setRowMinimumHeight(row, minSize);
+ invalidate();
+}
+
+/*!
+ Returns the minimum width set for row \a row.
+
+ \sa setRowMinimumHeight()
+*/
+int QGridLayout::rowMinimumHeight(int row) const
+{
+ Q_D(const QGridLayout);
+ return d->rowSpacing(row);
+}
+
+/*!
+ Sets the minimum width of column \a column to \a minSize pixels.
+
+ \sa columnMinimumWidth(), setRowMinimumHeight()
+*/
+void QGridLayout::setColumnMinimumWidth(int column, int minSize)
+{
+ Q_D(QGridLayout);
+ d->setColumnMinimumWidth(column, minSize);
+ invalidate();
+}
+
+/*!
+ Returns the column spacing for column \a column.
+
+ \sa setColumnMinimumWidth()
+*/
+int QGridLayout::columnMinimumWidth(int column) const
+{
+ Q_D(const QGridLayout);
+ return d->colSpacing(column);
+}
+
+/*!
+ \reimp
+*/
+Qt::Orientations QGridLayout::expandingDirections() const
+{
+ Q_D(const QGridLayout);
+ return d->expandingDirections(horizontalSpacing(), verticalSpacing());
+}
+
+/*!
+ Sets the grid's origin corner, i.e. position (0, 0), to \a corner.
+*/
+void QGridLayout::setOriginCorner(Qt::Corner corner)
+{
+ Q_D(QGridLayout);
+ d->setReversed(corner == Qt::BottomLeftCorner || corner == Qt::BottomRightCorner,
+ corner == Qt::TopRightCorner || corner == Qt::BottomRightCorner);
+}
+
+/*!
+ Returns the corner that's used for the grid's origin, i.e. for
+ position (0, 0).
+*/
+Qt::Corner QGridLayout::originCorner() const
+{
+ Q_D(const QGridLayout);
+ if (d->horReversed()) {
+ return d->verReversed() ? Qt::BottomRightCorner : Qt::TopRightCorner;
+ } else {
+ return d->verReversed() ? Qt::BottomLeftCorner : Qt::TopLeftCorner;
+ }
+}
+
+/*!
+ \reimp
+*/
+void QGridLayout::invalidate()
+{
+ Q_D(QGridLayout);
+ d->setDirty();
+ QLayout::invalidate();
+}
+
+/*!
+ \fn void QGridLayout::addRowSpacing(int row, int minsize)
+
+ Use addItem(new QSpacerItem(0, minsize), row, 0) instead.
+*/
+
+/*!
+ \fn void QGridLayout::addColSpacing(int col, int minsize)
+
+ Use addItem(new QSpacerItem(minsize, 0), 0, col) instead.
+*/
+
+/*!
+ \fn void QGridLayout::addMultiCellWidget(QWidget *widget, int fromRow, int toRow, int fromCol, int toCol, Qt::Alignment align = 0)
+
+ Use an addWidget() overload that allows you to specify row and
+ column spans instead.
+*/
+
+/*!
+ \fn void QGridLayout::addMultiCell(QLayoutItem *l, int fromRow, int toRow, int fromCol, int toCol, Qt::Alignment align = 0)
+
+ Use an addItem() overload that allows you to specify row and
+ column spans instead.
+*/
+
+/*!
+ \fn void QGridLayout::addMultiCellLayout(QLayout *layout, int fromRow, int toRow, int fromCol, int toCol, Qt::Alignment align = 0)
+
+ Use an addLayout() overload that allows you to specify row and
+ column spans instead.
+*/
+
+/*!
+ \fn int QGridLayout::numRows() const
+
+ Use rowCount() instead.
+*/
+
+/*!
+ \fn int QGridLayout::numCols() const
+
+ Use columnCount() instead.
+*/
+
+/*!
+ \fn void QGridLayout::setColStretch(int col, int stretch)
+
+ Use setColumnStretch() instead.
+*/
+
+/*!
+ \fn int QGridLayout::colStretch(int col) const
+
+ Use columnStretch() instead.
+*/
+
+/*!
+ \fn void QGridLayout::setColSpacing(int col, int minSize)
+
+ Use setColumnMinimumWidth() instead.
+*/
+
+/*!
+ \fn int QGridLayout::colSpacing(int col) const
+
+ Use columnSpacing() instead.
+*/
+
+/*!
+ \fn void QGridLayout::setRowSpacing(int row, int minSize)
+
+ Use setRowMinimumHeight(\a row, \a minSize) instead.
+*/
+
+/*!
+ \fn int QGridLayout::rowSpacing(int row) const
+
+ Use rowMinimumHeight(\a row) instead.
+*/
+
+/*!
+ \fn QRect QGridLayout::cellGeometry(int row, int column) const
+
+ Use cellRect(\a row, \a column) instead.
+*/
+
+/*!
+ \fn void QGridLayout::setOrigin(Qt::Corner corner)
+
+ Use setOriginCorner(\a corner) instead.
+*/
+
+/*!
+ \fn Qt::Corner QGridLayout::origin() const
+
+ Use originCorner() instead.
+*/
+
+
+QT_END_NAMESPACE
diff --git a/src/gui/kernel/qgridlayout.h b/src/gui/kernel/qgridlayout.h
new file mode 100644
index 0000000000..8bc1570d38
--- /dev/null
+++ b/src/gui/kernel/qgridlayout.h
@@ -0,0 +1,176 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QGRIDLAYOUT_H
+#define QGRIDLAYOUT_H
+
+#include <QtGui/qlayout.h>
+#ifdef QT_INCLUDE_COMPAT
+#include <QtGui/qwidget.h>
+#endif
+
+#include <limits.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+class QGridLayoutPrivate;
+
+class Q_GUI_EXPORT QGridLayout : public QLayout
+{
+ Q_OBJECT
+ Q_DECLARE_PRIVATE(QGridLayout)
+ QDOC_PROPERTY(int horizontalSpacing READ horizontalSpacing WRITE setHorizontalSpacing)
+ QDOC_PROPERTY(int verticalSpacing READ verticalSpacing WRITE setVerticalSpacing)
+
+public:
+ explicit QGridLayout(QWidget *parent);
+ QGridLayout();
+
+#ifdef QT3_SUPPORT
+ QT3_SUPPORT_CONSTRUCTOR QGridLayout(QWidget *parent, int nRows , int nCols = 1, int border = 0,
+ int spacing = -1, const char *name = 0);
+ QT3_SUPPORT_CONSTRUCTOR QGridLayout(int nRows , int nCols = 1, int spacing = -1, const char *name = 0);
+ QT3_SUPPORT_CONSTRUCTOR QGridLayout(QLayout *parentLayout, int nRows = 1, int nCols = 1, int spacing = -1,
+ const char *name = 0);
+#endif
+ ~QGridLayout();
+
+ QSize sizeHint() const;
+ QSize minimumSize() const;
+ QSize maximumSize() const;
+
+ void setHorizontalSpacing(int spacing);
+ int horizontalSpacing() const;
+ void setVerticalSpacing(int spacing);
+ int verticalSpacing() const;
+ void setSpacing(int spacing);
+ int spacing() const;
+
+ void setRowStretch(int row, int stretch);
+ void setColumnStretch(int column, int stretch);
+ int rowStretch(int row) const;
+ int columnStretch(int column) const;
+
+ void setRowMinimumHeight(int row, int minSize);
+ void setColumnMinimumWidth(int column, int minSize);
+ int rowMinimumHeight(int row) const;
+ int columnMinimumWidth(int column) const;
+
+ int columnCount() const;
+ int rowCount() const;
+
+ QRect cellRect(int row, int column) const;
+#ifdef QT3_SUPPORT
+ inline QT3_SUPPORT QRect cellGeometry(int row, int column) const {return cellRect(row, column);}
+#endif
+
+ bool hasHeightForWidth() const;
+ int heightForWidth(int) const;
+ int minimumHeightForWidth(int) const;
+
+ Qt::Orientations expandingDirections() const;
+ void invalidate();
+
+ inline void addWidget(QWidget *w) { QLayout::addWidget(w); }
+ void addWidget(QWidget *, int row, int column, Qt::Alignment = 0);
+ void addWidget(QWidget *, int row, int column, int rowSpan, int columnSpan, Qt::Alignment = 0);
+ void addLayout(QLayout *, int row, int column, Qt::Alignment = 0);
+ void addLayout(QLayout *, int row, int column, int rowSpan, int columnSpan, Qt::Alignment = 0);
+
+ void setOriginCorner(Qt::Corner);
+ Qt::Corner originCorner() const;
+
+#ifdef QT3_SUPPORT
+ inline QT3_SUPPORT void setOrigin(Qt::Corner corner) { setOriginCorner(corner); }
+ inline QT3_SUPPORT Qt::Corner origin() const { return originCorner(); }
+#endif
+ QLayoutItem *itemAt(int index) const;
+ QLayoutItem *itemAtPosition(int row, int column) const;
+ QLayoutItem *takeAt(int index);
+ int count() const;
+ void setGeometry(const QRect&);
+
+ void addItem(QLayoutItem *item, int row, int column, int rowSpan = 1, int columnSpan = 1, Qt::Alignment = 0);
+
+ void setDefaultPositioning(int n, Qt::Orientation orient);
+ void getItemPosition(int idx, int *row, int *column, int *rowSpan, int *columnSpan);
+
+protected:
+#ifdef QT3_SUPPORT
+ QT3_SUPPORT bool findWidget(QWidget* w, int *r, int *c);
+#endif
+ void addItem(QLayoutItem *);
+
+private:
+ Q_DISABLE_COPY(QGridLayout)
+
+#ifdef QT3_SUPPORT
+public:
+ QT3_SUPPORT void expand(int rows, int cols);
+ inline QT3_SUPPORT void addRowSpacing(int row, int minsize) { addItem(new QSpacerItem(0,minsize), row, 0); }
+ inline QT3_SUPPORT void addColSpacing(int col, int minsize) { addItem(new QSpacerItem(minsize,0), 0, col); }
+ inline QT3_SUPPORT void addMultiCellWidget(QWidget *w, int fromRow, int toRow, int fromCol, int toCol, Qt::Alignment _align = 0)
+ { addWidget(w, fromRow, fromCol, (toRow < 0) ? -1 : toRow - fromRow + 1, (toCol < 0) ? -1 : toCol - fromCol + 1, _align); }
+ inline QT3_SUPPORT void addMultiCell(QLayoutItem *l, int fromRow, int toRow, int fromCol, int toCol, Qt::Alignment _align = 0)
+ { addItem(l, fromRow, fromCol, (toRow < 0) ? -1 : toRow - fromRow + 1, (toCol < 0) ? -1 : toCol - fromCol + 1, _align); }
+ inline QT3_SUPPORT void addMultiCellLayout(QLayout *layout, int fromRow, int toRow, int fromCol, int toCol, Qt::Alignment _align = 0)
+ { addLayout(layout, fromRow, fromCol, (toRow < 0) ? -1 : toRow - fromRow + 1, (toCol < 0) ? -1 : toCol - fromCol + 1, _align); }
+
+ inline QT3_SUPPORT int numRows() const { return rowCount(); }
+ inline QT3_SUPPORT int numCols() const { return columnCount(); }
+ inline QT3_SUPPORT void setColStretch(int col, int stretch) {setColumnStretch(col, stretch); }
+ inline QT3_SUPPORT int colStretch(int col) const {return columnStretch(col); }
+ inline QT3_SUPPORT void setColSpacing(int col, int minSize) { setColumnMinimumWidth(col, minSize); }
+ inline QT3_SUPPORT int colSpacing(int col) const { return columnMinimumWidth(col); }
+ inline QT3_SUPPORT void setRowSpacing(int row, int minSize) {setRowMinimumHeight(row, minSize); }
+ inline QT3_SUPPORT int rowSpacing(int row) const {return rowMinimumHeight(row); }
+#endif
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QGRIDLAYOUT_H
diff --git a/src/gui/kernel/qguieventdispatcher_glib.cpp b/src/gui/kernel/qguieventdispatcher_glib.cpp
new file mode 100644
index 0000000000..6222240a7e
--- /dev/null
+++ b/src/gui/kernel/qguieventdispatcher_glib.cpp
@@ -0,0 +1,217 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qguieventdispatcher_glib_p.h"
+
+#include "qapplication.h"
+#include "qx11info_x11.h"
+
+#include "qt_x11_p.h"
+
+#include <glib.h>
+
+QT_BEGIN_NAMESPACE
+
+struct GX11EventSource
+{
+ GSource source;
+ GPollFD pollfd;
+ QEventLoop::ProcessEventsFlags flags;
+ QGuiEventDispatcherGlib *q;
+ QGuiEventDispatcherGlibPrivate *d;
+};
+
+class QGuiEventDispatcherGlibPrivate : public QEventDispatcherGlibPrivate
+{
+ Q_DECLARE_PUBLIC(QGuiEventDispatcherGlib)
+
+public:
+ QGuiEventDispatcherGlibPrivate();
+ GX11EventSource *x11EventSource;
+ QList<XEvent> queuedUserInputEvents;
+};
+
+static gboolean x11EventSourcePrepare(GSource *s, gint *timeout)
+{
+ if (timeout)
+ *timeout = -1;
+ GX11EventSource *source = reinterpret_cast<GX11EventSource *>(s);
+ return (XEventsQueued(X11->display, QueuedAfterFlush)
+ || (!(source->flags & QEventLoop::ExcludeUserInputEvents)
+ && !source->d->queuedUserInputEvents.isEmpty()));
+}
+
+static gboolean x11EventSourceCheck(GSource *s)
+{
+ GX11EventSource *source = reinterpret_cast<GX11EventSource *>(s);
+ return (XEventsQueued(X11->display, QueuedAfterFlush)
+ || (!(source->flags & QEventLoop::ExcludeUserInputEvents)
+ && !source->d->queuedUserInputEvents.isEmpty()));
+}
+
+static gboolean x11EventSourceDispatch(GSource *s, GSourceFunc callback, gpointer user_data)
+{
+ GX11EventSource *source = reinterpret_cast<GX11EventSource *>(s);
+
+ ulong marker = XNextRequest(X11->display);
+ do {
+ XEvent event;
+ if (!(source->flags & QEventLoop::ExcludeUserInputEvents)
+ && !source->d->queuedUserInputEvents.isEmpty()) {
+ // process a pending user input event
+ event = source->d->queuedUserInputEvents.takeFirst();
+ } else if (XEventsQueued(X11->display, QueuedAlready)) {
+ // process events from the X server
+ XNextEvent(X11->display, &event);
+
+ if (source->flags & QEventLoop::ExcludeUserInputEvents) {
+ // queue user input events
+ switch (event.type) {
+ case ButtonPress:
+ case ButtonRelease:
+ case MotionNotify:
+ case XKeyPress:
+ case XKeyRelease:
+ case EnterNotify:
+ case LeaveNotify:
+ source->d->queuedUserInputEvents.append(event);
+ continue;
+
+ case ClientMessage:
+ // only keep the wm_take_focus and
+ // _qt_scrolldone protocols, queue all other
+ // client messages
+ if (event.xclient.format == 32) {
+ if (event.xclient.message_type == ATOM(WM_PROTOCOLS) ||
+ (Atom) event.xclient.data.l[0] == ATOM(WM_TAKE_FOCUS)) {
+ break;
+ } else if (event.xclient.message_type == ATOM(_QT_SCROLL_DONE)) {
+ break;
+ }
+ }
+ source->d->queuedUserInputEvents.append(event);
+ continue;
+
+ default:
+ break;
+ }
+ }
+ } else {
+ // no event to process
+ break;
+ }
+
+ // send through event filter
+ if (source->q->filterEvent(&event))
+ continue;
+
+ if (qApp->x11ProcessEvent(&event) == 1)
+ return true;
+
+ if (event.xany.serial >= marker)
+ goto out;
+ } while (XEventsQueued(X11->display, QueuedAfterFlush));
+
+ out:
+
+ if (callback)
+ callback(user_data);
+ return true;
+}
+
+static GSourceFuncs x11EventSourceFuncs = {
+ x11EventSourcePrepare,
+ x11EventSourceCheck,
+ x11EventSourceDispatch,
+ NULL,
+ NULL,
+ NULL
+};
+
+QGuiEventDispatcherGlibPrivate::QGuiEventDispatcherGlibPrivate()
+{
+ x11EventSource = reinterpret_cast<GX11EventSource *>(g_source_new(&x11EventSourceFuncs,
+ sizeof(GX11EventSource)));
+ g_source_set_can_recurse(&x11EventSource->source, true);
+
+ memset(&x11EventSource->pollfd, 0, sizeof(GPollFD));
+ x11EventSource->flags = QEventLoop::AllEvents;
+ x11EventSource->q = 0;
+ x11EventSource->d = 0;
+
+ g_source_attach(&x11EventSource->source, mainContext);
+}
+
+QGuiEventDispatcherGlib::QGuiEventDispatcherGlib(QObject *parent)
+ : QEventDispatcherGlib(*new QGuiEventDispatcherGlibPrivate, parent)
+{
+}
+
+QGuiEventDispatcherGlib::~QGuiEventDispatcherGlib()
+{
+ Q_D(QGuiEventDispatcherGlib);
+
+ g_source_remove_poll(&d->x11EventSource->source, &d->x11EventSource->pollfd);
+ g_source_destroy(&d->x11EventSource->source);
+ d->x11EventSource = 0;
+}
+
+bool QGuiEventDispatcherGlib::processEvents(QEventLoop::ProcessEventsFlags flags)
+{
+ Q_D(QGuiEventDispatcherGlib);
+ QEventLoop::ProcessEventsFlags saved_flags = d->x11EventSource->flags;
+ d->x11EventSource->flags = flags;
+ bool returnValue = QEventDispatcherGlib::processEvents(flags);
+ d->x11EventSource->flags = saved_flags;
+ return returnValue;
+}
+
+void QGuiEventDispatcherGlib::startingUp()
+{
+ Q_D(QGuiEventDispatcherGlib);
+ d->x11EventSource->pollfd.fd = XConnectionNumber(X11->display);
+ d->x11EventSource->pollfd.events = G_IO_IN | G_IO_HUP | G_IO_ERR;
+ d->x11EventSource->q = this;
+ d->x11EventSource->d = d;
+ g_source_add_poll(&d->x11EventSource->source, &d->x11EventSource->pollfd);
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/kernel/qguieventdispatcher_glib_p.h b/src/gui/kernel/qguieventdispatcher_glib_p.h
new file mode 100644
index 0000000000..06a11c7c0e
--- /dev/null
+++ b/src/gui/kernel/qguieventdispatcher_glib_p.h
@@ -0,0 +1,78 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QGUIEVENTDISPATCHER_GLIB_P_H
+#define QGUIEVENTDISPATCHER_GLIB_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of the QLibrary class. This header file may change from
+// version to version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtCore/private/qeventdispatcher_glib_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QGuiEventDispatcherGlibPrivate;
+
+class QGuiEventDispatcherGlib : public QEventDispatcherGlib
+{
+ Q_OBJECT
+ Q_DECLARE_PRIVATE(QGuiEventDispatcherGlib)
+
+public:
+ explicit QGuiEventDispatcherGlib(QObject *parent = 0);
+ ~QGuiEventDispatcherGlib();
+
+ bool processEvents(QEventLoop::ProcessEventsFlags flags);
+
+ void startingUp();
+};
+
+QT_END_NAMESPACE
+
+#endif // QGUIEVENTDISPATCHER_GLIB_P_H
diff --git a/src/gui/kernel/qguifunctions_wince.cpp b/src/gui/kernel/qguifunctions_wince.cpp
new file mode 100644
index 0000000000..b6f825ba4f
--- /dev/null
+++ b/src/gui/kernel/qguifunctions_wince.cpp
@@ -0,0 +1,377 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#include "qguifunctions_wince.h"
+#include <shellapi.h>
+#include <QtCore/qlibrary.h>
+
+QT_USE_NAMESPACE
+
+struct AygSHINITDLGINFO
+{
+ DWORD dwMask;
+ HWND hDlg;
+ DWORD dwFlags;
+};
+
+struct AygSIPINFO
+{
+ DWORD cbSize;
+ DWORD fdwFlags;
+ RECT rcVisibleDesktop;
+ RECT rcSipRect;
+ DWORD dwImDataSize;
+ void *pvImData;
+};
+
+#ifndef SHIDIF_CANCELBUTTON
+#define SHIDIF_CANCELBUTTON 0x0080
+#endif
+
+#ifndef SHIDIM_FLAGS
+#define SHIDIM_FLAGS 0x0001
+#endif
+
+#ifndef SHIDIF_DONEBUTTON
+#define SHIDIF_DONEBUTTON 0x0001
+#endif
+#ifndef SHIDIF_SIZEDLGFULLSCREEN
+#define SHIDIF_SIZEDLGFULLSCREEN 0x0004
+#endif
+
+#ifndef SHFS_SHOWTASKBAR
+#define SHFS_SHOWTASKBAR 0x0001
+#endif
+#ifndef SHFS_HIDETASKBAR
+#define SHFS_HIDETASKBAR 0x0002
+#endif
+#ifndef SHFS_SHOWSIPBUTTON
+#define SHFS_SHOWSIPBUTTON 0x0004
+#endif
+#ifndef SHFS_HIDESIPBUTTON
+#define SHFS_HIDESIPBUTTON 0x0008
+#endif
+#ifndef SHFS_SHOWSTARTICON
+#define SHFS_SHOWSTARTICON 0x0010
+#endif
+#ifndef SHFS_HIDESTARTICON
+#define SHFS_HIDESTARTICON 0x0020
+#endif
+
+#ifndef SIPF_OFF
+#define SIPF_OFF 0x00000000
+#endif
+#ifndef SIPF_ON
+#define SIPF_ON 0x00000001
+#endif
+
+#ifndef SPI_SETSIPINFO
+#define SPI_SETSIPINFO 224
+#endif
+#ifndef SPI_GETSIPINFO
+#define SPI_GETSIPINFO 225
+#endif
+
+typedef BOOL (*AygInitDialog)(AygSHINITDLGINFO*);
+typedef BOOL (*AygFullScreen)(HWND, DWORD);
+typedef BOOL (*AygSHSipInfo)(UINT, UINT, PVOID, UINT);
+
+static AygInitDialog ptrAygInitDialog = 0;
+static AygFullScreen ptrAygFullScreen = 0;
+static AygSHSipInfo ptrAygSHSipInfo = 0;
+static bool aygResolved = false;
+
+static void resolveAygLibs()
+{
+ if (!aygResolved) {
+ aygResolved = true;
+ QLibrary ayglib(QLatin1String("aygshell"));
+ if (!ayglib.load())
+ return;
+ ptrAygInitDialog = (AygInitDialog) ayglib.resolve("SHInitDialog");
+ ptrAygFullScreen = (AygFullScreen) ayglib.resolve("SHFullScreen");
+ ptrAygSHSipInfo = (AygSHSipInfo) ayglib.resolve("SHSipInfo");
+ }
+}
+
+struct DIBINFO : public BITMAPINFO
+{
+ RGBQUAD arColors[255];
+
+ operator LPBITMAPINFO() { return (LPBITMAPINFO) this; }
+ operator LPBITMAPINFOHEADER() { return &bmiHeader; }
+ RGBQUAD* ColorTable() { return bmiColors; }
+};
+
+int qt_wince_GetDIBits(HDC /*hdc*/ , HBITMAP hSourceBitmap, uint, uint, LPVOID lpvBits, LPBITMAPINFO, uint)
+{
+ if (!lpvBits) {
+ qWarning("::GetDIBits(), lpvBits NULL");
+ return 0;
+ }
+ BITMAP bm;
+ GetObject(hSourceBitmap, sizeof(BITMAP), &bm);
+ bm.bmHeight = qAbs(bm.bmHeight);
+
+ HBITMAP hTargetBitmap;
+ void *pixels;
+
+ BITMAPINFO dibInfo;
+ memset(&dibInfo, 0, sizeof(dibInfo));
+ dibInfo.bmiHeader.biBitCount = 32;
+ dibInfo.bmiHeader.biClrImportant = 0;
+ dibInfo.bmiHeader.biClrUsed = 0;
+ dibInfo.bmiHeader.biCompression = BI_RGB;;
+ dibInfo.bmiHeader.biHeight = -bm.bmHeight;
+ dibInfo.bmiHeader.biWidth = bm.bmWidth;
+ dibInfo.bmiHeader.biPlanes = 1;
+ dibInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
+ dibInfo.bmiHeader.biSizeImage = bm.bmWidth * bm.bmHeight * 4;
+
+ HDC displayDC = GetDC(NULL);
+ if (!displayDC) {
+ qWarning("::GetDIBits(), failed to GetDC");
+ return 0;
+ }
+
+ int ret = bm.bmHeight;
+
+ hTargetBitmap = CreateDIBSection(displayDC, (const BITMAPINFO*) &dibInfo, DIB_RGB_COLORS,
+ (void**)&pixels, NULL, 0);
+ if (!hTargetBitmap) {
+ qWarning("::GetDIBits(), failed to CreateDIBSection");
+ return 0;
+ }
+
+ HDC hdcSrc = CreateCompatibleDC(displayDC);
+ HDC hdcDst = CreateCompatibleDC(displayDC);
+
+ if (!(hdcDst && hdcSrc)) {
+ qWarning("::GetDIBits(), failed to CreateCompatibleDC");
+ ret = 0;
+ }
+
+ HBITMAP hOldBitmap1 = (HBITMAP) SelectObject(hdcSrc, hSourceBitmap);
+ HBITMAP hOldBitmap2 = (HBITMAP) SelectObject(hdcDst, hTargetBitmap);
+
+ if (!(hOldBitmap1 && hOldBitmap2)) {
+ qWarning("::GetDIBits(), failed to SelectObject for bitmaps");
+ ret = 0;
+ }
+
+ if (!BitBlt(hdcDst, 0, 0, bm.bmWidth, bm.bmHeight, hdcSrc, 0, 0, SRCCOPY)) {
+ qWarning("::GetDIBits(), BitBlt failed");
+ ret = 0;
+ }
+
+ SelectObject(hdcSrc, hOldBitmap1);
+ SelectObject(hdcDst, hOldBitmap2);
+
+ DeleteDC(hdcSrc);
+ DeleteDC(hdcDst);
+
+ ReleaseDC(NULL, displayDC);
+
+ memcpy(lpvBits, pixels, dibInfo.bmiHeader.biSizeImage);
+
+ DeleteObject(hTargetBitmap);
+ return ret;
+}
+
+bool qt_wince_TextOutW(HDC hdc, int x, int y, LPWSTR lpString, UINT c)
+{
+ return ExtTextOutW(hdc, x, y, 0, NULL, lpString, c, NULL);
+}
+
+
+HINSTANCE qt_wince_ShellExecute(HWND hwnd, LPCTSTR, LPCTSTR file, LPCTSTR params, LPCTSTR dir, int showCmd)
+{
+ SHELLEXECUTEINFO info;
+ info.hwnd = hwnd;
+ info.lpVerb = L"Open";
+ info.lpFile = file;
+ info.lpParameters = params;
+ info.lpDirectory = dir;
+ info.nShow = showCmd;
+ info.cbSize = sizeof(info);
+ ShellExecuteEx(&info);
+ return info.hInstApp;
+}
+
+// Clipboard --------------------------------------------------------
+BOOL qt_wince_ChangeClipboardChain( HWND /*hWndRemove*/, HWND /*hWndNewNext*/ )
+{
+ return FALSE;
+}
+
+HWND qt_wince_SetClipboardViewer( HWND /*hWndNewViewer*/ )
+{
+ return NULL;
+}
+
+
+// Graphics ---------------------------------------------------------
+COLORREF qt_wince_PALETTEINDEX( WORD /*wPaletteIndex*/)
+{
+ return 0;
+}
+
+BOOL qt_wince_TextOut( HDC hdc, int nXStart, int nYStart, LPCTSTR lpString, int cbString )
+{
+ return ExtTextOut( hdc, nXStart, nYStart - 16, 0, NULL, lpString, cbString, NULL );
+}
+
+// Internal Qt -----------------------------------------------------
+bool qt_wince_is_platform(const QString &platformString) {
+ TCHAR tszPlatform[64];
+ if (SystemParametersInfo(SPI_GETPLATFORMTYPE,
+ sizeof(tszPlatform)/sizeof(*tszPlatform),tszPlatform,0))
+ if (0 == _tcsicmp(reinterpret_cast<const wchar_t *> (platformString.utf16()), tszPlatform))
+ return true;
+ return false;
+}
+
+bool qt_wince_is_pocket_pc() {
+ return qt_wince_is_platform(QString::fromLatin1("PocketPC"));
+}
+
+bool qt_wince_is_smartphone() {
+ return qt_wince_is_platform(QString::fromLatin1("Smartphone"));
+}
+bool qt_wince_is_mobile() {
+ return (qt_wince_is_smartphone() || qt_wince_is_pocket_pc());
+}
+
+bool qt_wince_is_high_dpi() {
+ if (!qt_wince_is_pocket_pc())
+ return false;
+ HDC deviceContext = GetDC(0);
+ int dpi = GetDeviceCaps(deviceContext, LOGPIXELSX);
+ ReleaseDC(0, deviceContext);
+ if ((dpi < 1000) && (dpi > 0))
+ return dpi > 96;
+ else
+ return false;
+}
+
+void qt_wince_maximize(QWidget *widget)
+{
+ HWND hwnd = widget->winId();
+ if (qt_wince_is_mobile()) {
+ AygSHINITDLGINFO shidi;
+ shidi.dwMask = SHIDIM_FLAGS;
+ shidi.hDlg = hwnd;
+ shidi.dwFlags = SHIDIF_SIZEDLGFULLSCREEN;
+ if (widget->windowFlags() & Qt::WindowCancelButtonHint)
+ shidi.dwFlags |= SHIDIF_CANCELBUTTON;
+ if (widget->windowFlags() & Qt::WindowOkButtonHint)
+ shidi.dwFlags |= SHIDIF_DONEBUTTON;
+ resolveAygLibs();
+ if (ptrAygInitDialog)
+ ptrAygInitDialog(&shidi);
+ } else {
+ RECT r;
+ SystemParametersInfo(SPI_GETWORKAREA, 0, &r, 0);
+ MoveWindow(hwnd, r.top, r.left, r.right - r.left, r.bottom - r.top, true);
+ SetWindowLong(hwnd, GWL_EXSTYLE, GetWindowLong (hwnd, GWL_EXSTYLE) | WS_EX_NODRAG);
+ }
+}
+
+void qt_wince_minimize(HWND hwnd) {
+
+ uint exstyle = GetWindowLongW(hwnd, GWL_EXSTYLE);
+ uint style = GetWindowLongW(hwnd, GWL_STYLE);
+ RECT rect;
+ RECT crect = {0,0,0,0};
+ GetWindowRect(hwnd, &rect);
+ AdjustWindowRectEx(&crect, style & ~WS_OVERLAPPED, FALSE, exstyle);
+ MoveWindow(hwnd, rect.left - crect.left, rect.top - crect.top, 0, 0, true);
+ SetWindowLong(hwnd, GWL_EXSTYLE, GetWindowLong (hwnd, GWL_EXSTYLE) | WS_EX_NODRAG);
+ ShowWindow(hwnd, SW_HIDE);
+}
+
+
+void qt_wince_hide_taskbar(HWND hwnd) {
+ if (ptrAygFullScreen)
+ ptrAygFullScreen(hwnd, SHFS_HIDETASKBAR | SHFS_HIDESIPBUTTON | SHFS_HIDESTARTICON);
+}
+
+void qt_wince_full_screen(HWND hwnd, bool fullScreen, UINT swpf) {
+ resolveAygLibs();
+ if (fullScreen) {
+ QRect r = qApp->desktop()->screenGeometry(QWidget::find(hwnd));
+ SetWindowPos(hwnd, HWND_TOP, r.left(), r.top(), r.width(), r.height(), swpf);
+ if (ptrAygFullScreen)
+ ptrAygFullScreen(hwnd, SHFS_HIDETASKBAR | SHFS_HIDESIPBUTTON | SHFS_HIDESTARTICON);
+ if (!qt_wince_is_mobile()) {
+ HWND handle = FindWindow(L"HHTaskBar", L"");
+ if (handle) {
+ ShowWindow(handle, 0);
+ EnableWindow(handle, false);
+ }
+ }
+ } else {
+ if (ptrAygFullScreen)
+ ptrAygFullScreen(hwnd, SHFS_SHOWTASKBAR | SHFS_SHOWSIPBUTTON | SHFS_SHOWSTARTICON);
+ SetWindowPos(hwnd, 0, 0, 0, 0, 0, swpf);
+ if (!qt_wince_is_mobile()) {
+ HWND handle = FindWindow(L"HHTaskBar", L"");
+ if (handle) {
+ ShowWindow(handle, 1);
+ EnableWindow(handle, true);
+ }
+ }
+ }
+}
+
+void qt_wince_show_SIP(bool show)
+{
+ resolveAygLibs();
+ if (!ptrAygSHSipInfo)
+ return;
+
+ AygSIPINFO si;
+ memset(&si, 0, sizeof(si));
+ si.cbSize = sizeof(si);
+ ptrAygSHSipInfo(SPI_GETSIPINFO, 0, &si, 0);
+ si.cbSize = sizeof(si);
+ si.fdwFlags = (show ? SIPF_ON : SIPF_OFF);
+ ptrAygSHSipInfo(SPI_SETSIPINFO, 0, &si, 0);
+}
diff --git a/src/gui/kernel/qguifunctions_wince.h b/src/gui/kernel/qguifunctions_wince.h
new file mode 100644
index 0000000000..aca2253fc9
--- /dev/null
+++ b/src/gui/kernel/qguifunctions_wince.h
@@ -0,0 +1,157 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#ifndef QGUIFUNCTIONS_WCE_H
+#define QGUIFUNCTIONS_WCE_H
+#ifdef Q_OS_WINCE
+#include <QtCore/qfunctions_wince.h>
+#define UNDER_NT
+#include <wingdi.h>
+
+#ifdef QT_BUILD_GUI_LIB
+QT_BEGIN_HEADER
+QT_BEGIN_NAMESPACE
+QT_MODULE(Gui)
+QT_END_NAMESPACE
+QT_END_HEADER
+#endif
+
+// application defines
+#define SPI_SETNONCLIENTMETRICS 72
+#define SPI_SETICONTITLELOGFONT 0x0022
+#define WM_ACTIVATEAPP 0x001c
+#define SW_PARENTCLOSING 1
+#define SW_OTHERMAXIMIZED 2
+#define SW_PARENTOPENING 3
+#define SW_OTHERRESTORED 4
+#define GET_XBUTTON_WPARAM(wParam) (HIWORD(wParam))
+
+// drag n drop
+#ifndef CFSTR_PERFORMEDDROPEFFECT
+#define CFSTR_PERFORMEDDROPEFFECT TEXT("Performed DropEffect")
+#endif
+int qt_wince_GetDIBits(HDC, HBITMAP, uint, uint, void*, LPBITMAPINFO, uint);
+#define GetDIBits(a,b,c,d,e,f,g) qt_wince_GetDIBits(a,b,c,d,e,f,g)
+
+// QWidget
+#define SW_SHOWMINIMIZED SW_MINIMIZE
+
+// QPaintEngine
+bool qt_wince_TextOutW(HDC, int, int, LPWSTR, UINT);
+#define TextOutW(a,b,c,d,e) qt_wince_TextOutW(a,b,c,d,e)
+
+// QRegion
+#define ALTERNATE 0
+#define WINDING 1
+
+// QFontEngine
+typedef struct _FIXED {
+ WORD fract;
+ short value;
+} FIXED;
+
+typedef struct tagPOINTFX {
+ FIXED x;
+ FIXED y;
+} POINTFX;
+
+typedef struct _MAT2 {
+ FIXED eM11;
+ FIXED eM12;
+ FIXED eM21;
+ FIXED eM22;
+} MAT2;
+
+typedef struct _GLYPHMETRICS {
+ UINT gmBlackBoxX;
+ UINT gmBlackBoxY;
+ POINT gmptGlyphOrigin;
+ short gmCellIncX;
+ short gmCellIncY;
+} GLYPHMETRICS;
+
+typedef struct tagTTPOLYGONHEADER
+{
+ DWORD cb;
+ DWORD dwType;
+ POINTFX pfxStart;
+} TTPOLYGONHEADER;
+
+typedef struct tagTTPOLYCURVE
+{
+ WORD wType;
+ WORD cpfx;
+ POINTFX apfx[1];
+} TTPOLYCURVE;
+
+#define GGO_NATIVE 2
+#define GGO_GLYPH_INDEX 0x0080
+#define TT_PRIM_LINE 1
+#define TT_PRIM_QSPLINE 2
+#define TT_PRIM_CSPLINE 3
+#define ANSI_VAR_FONT 12
+
+HINSTANCE qt_wince_ShellExecute(HWND hwnd, LPCTSTR operation, LPCTSTR file, LPCTSTR params, LPCTSTR dir, int showCmd);
+#define ShellExecute(a,b,c,d,e,f) qt_wince_ShellExecute(a,b,c,d,e,f)
+
+
+// Clipboard --------------------------------------------------------
+#define WM_CHANGECBCHAIN 1
+#define WM_DRAWCLIPBOARD 2
+
+BOOL qt_wince_ChangeClipboardChain(
+ HWND hWndRemove, // handle to window to remove
+ HWND hWndNewNext // handle to next window
+);
+#define ChangeClipboardChain(a,b) qt_wince_ChangeClipboardChain(a,b);
+
+HWND qt_wince_SetClipboardViewer(
+ HWND hWndNewViewer // handle to clipboard viewer window
+);
+#define SetClipboardViewer(a) qt_wince_SetClipboardViewer(a)
+
+// Graphics ---------------------------------------------------------
+COLORREF qt_wince_PALETTEINDEX( WORD wPaletteIndex );
+#define PALETTEINDEX(a) qt_wince_PALETTEINDEX(a)
+BOOL qt_wince_TextOut( HDC hdc, int nXStart, int nYStart, LPCTSTR lpString, int cbString );
+#define TextOut(a,b,c,d,e) qt_wince_TextOut(a,b,c,d,e)
+
+#endif // Q_OS_WINCE
+#endif // QGUIFUNCTIONS_WCE_H
diff --git a/src/gui/kernel/qguivariant.cpp b/src/gui/kernel/qguivariant.cpp
new file mode 100644
index 0000000000..7ad908f3a7
--- /dev/null
+++ b/src/gui/kernel/qguivariant.cpp
@@ -0,0 +1,669 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qvariant.h"
+
+#include "qbitmap.h"
+#include "qbrush.h"
+#include "qcolor.h"
+#include "qcursor.h"
+#include "qdatastream.h"
+#include "qdebug.h"
+#include "qfont.h"
+#include "qicon.h"
+#include "qimage.h"
+#include "qkeysequence.h"
+#include "qtransform.h"
+#include "qmatrix.h"
+#include "qpalette.h"
+#include "qpen.h"
+#include "qpixmap.h"
+#include "qpolygon.h"
+#include "qregion.h"
+#include "qsizepolicy.h"
+#include "qtextformat.h"
+
+#include "private/qvariant_p.h"
+
+QT_BEGIN_NAMESPACE
+
+#ifdef QT3_SUPPORT
+extern QDataStream &qt_stream_out_qcolorgroup(QDataStream &s, const QColorGroup &g);
+extern QDataStream &qt_stream_in_qcolorgroup(QDataStream &s, QColorGroup &g);
+#endif
+
+Q_CORE_EXPORT const QVariant::Handler *qcoreVariantHandler();
+
+static void construct(QVariant::Private *x, const void *copy)
+{
+ switch (x->type) {
+ case QVariant::Bitmap:
+ v_construct<QBitmap>(x, copy);
+ break;
+ case QVariant::Region:
+ v_construct<QRegion>(x, copy);
+ break;
+ case QVariant::Polygon:
+ v_construct<QPolygon>(x, copy);
+ break;
+ case QVariant::Font:
+ v_construct<QFont>(x, copy);
+ break;
+ case QVariant::Pixmap:
+ v_construct<QPixmap>(x, copy);
+ break;
+ case QVariant::Image:
+ v_construct<QImage>(x, copy);
+ break;
+ case QVariant::Brush:
+ v_construct<QBrush>(x, copy);
+ break;
+ case QVariant::Color:
+ v_construct<QColor>(x, copy);
+ break;
+ case QVariant::Palette:
+ v_construct<QPalette>(x, copy);
+ break;
+#ifdef QT3_SUPPORT
+ case QVariant::ColorGroup:
+ v_construct<QColorGroup>(x, copy);
+ break;
+#endif
+#ifndef QT_NO_ICON
+ case QVariant::Icon:
+ v_construct<QIcon>(x, copy);
+ break;
+#endif
+ case QVariant::Matrix:
+ v_construct<QMatrix>(x, copy);
+ break;
+ case QVariant::Transform:
+ v_construct<QTransform>(x, copy);
+ break;
+ case QVariant::TextFormat:
+ v_construct<QTextFormat>(x, copy);
+ break;
+ case QVariant::TextLength:
+ v_construct<QTextLength>(x, copy);
+ break;
+#ifndef QT_NO_SHORTCUT
+ case QVariant::KeySequence:
+ v_construct<QKeySequence>(x, copy);
+ break;
+#endif
+ case QVariant::Pen:
+ v_construct<QPen>(x, copy);
+ break;
+ case QVariant::SizePolicy:
+ v_construct<QSizePolicy>(x, copy);
+ break;
+#ifndef QT_NO_CURSOR
+ case QVariant::Cursor:
+ v_construct<QCursor>(x, copy);
+ break;
+#endif
+ case 62: {
+ // small 'trick' to let a QVariant(Qt::blue) create a variant
+ // of type QColor
+ x->type = QVariant::Color;
+ QColor color(*reinterpret_cast<const Qt::GlobalColor *>(copy));
+ v_construct<QColor>(x, &color);
+ break;
+ }
+ default:
+ qcoreVariantHandler()->construct(x, copy);
+ return;
+ }
+ x->is_null = !copy;
+}
+
+static void clear(QVariant::Private *d)
+{
+ switch (d->type) {
+ case QVariant::Bitmap:
+ v_clear<QBitmap>(d);
+ break;
+ case QVariant::Cursor:
+ v_clear<QCursor>(d);
+ break;
+ case QVariant::Region:
+ v_clear<QRegion>(d);
+ break;
+ case QVariant::Polygon:
+ v_clear<QPolygon>(d);
+ break;
+ case QVariant::Font:
+ v_clear<QFont>(d);
+ break;
+ case QVariant::Pixmap:
+ v_clear<QPixmap>(d);
+ break;
+ case QVariant::Image:
+ v_clear<QImage>(d);
+ break;
+ case QVariant::Brush:
+ v_clear<QBrush>(d);
+ break;
+ case QVariant::Color:
+ v_clear<QColor>(d);
+ break;
+ case QVariant::Palette:
+ v_clear<QPalette>(d);
+ break;
+#ifdef QT3_SUPPORT
+ case QVariant::ColorGroup:
+ v_clear<QColorGroup>(d);
+ break;
+#endif
+#ifndef QT_NO_ICON
+ case QVariant::Icon:
+ v_clear<QIcon>(d);
+ break;
+#endif
+ case QVariant::Matrix:
+ v_clear<QMatrix>(d);
+ break;
+ case QVariant::Transform:
+ v_clear<QTransform>(d);
+ break;
+ case QVariant::TextFormat:
+ v_clear<QTextFormat>(d);
+ break;
+ case QVariant::TextLength:
+ v_clear<QTextLength>(d);
+ break;
+ case QVariant::SizePolicy:
+ v_clear<QSizePolicy>(d);
+ break;
+#ifndef QT_NO_SHORTCUT
+ case QVariant::KeySequence:
+ v_clear<QKeySequence>(d);
+ break;
+#endif
+ case QVariant::Pen:
+ v_clear<QPen>(d);
+ break;
+ default:
+ qcoreVariantHandler()->clear(d);
+ return;
+ }
+
+ d->type = QVariant::Invalid;
+ d->is_null = true;
+ d->is_shared = false;
+}
+
+
+static bool isNull(const QVariant::Private *d)
+{
+ switch(d->type) {
+ case QVariant::Bitmap:
+ return v_cast<QBitmap>(d)->isNull();
+ case QVariant::Region:
+ return v_cast<QRegion>(d)->isEmpty();
+ case QVariant::Polygon:
+ return v_cast<QPolygon>(d)->isEmpty();
+ case QVariant::Pixmap:
+ return v_cast<QPixmap>(d)->isNull();
+ case QVariant::Image:
+ return v_cast<QImage>(d)->isNull();
+#ifndef QT_NO_ICON
+ case QVariant::Icon:
+ return v_cast<QIcon>(d)->isNull();
+#endif
+ case QVariant::Matrix:
+ case QVariant::TextFormat:
+ case QVariant::TextLength:
+ case QVariant::Cursor:
+ case QVariant::StringList:
+ case QVariant::Font:
+ case QVariant::Brush:
+ case QVariant::Color:
+ case QVariant::Palette:
+#ifdef QT3_SUPPORT
+ case QVariant::ColorGroup:
+#endif
+ case QVariant::SizePolicy:
+#ifndef QT_NO_SHORTCUT
+ case QVariant::KeySequence:
+#endif
+ case QVariant::Pen:
+ break;
+ default:
+ return qcoreVariantHandler()->isNull(d);
+ }
+ return d->is_null;
+}
+
+static bool compare(const QVariant::Private *a, const QVariant::Private *b)
+{
+ Q_ASSERT(a->type == b->type);
+ switch(a->type) {
+ case QVariant::Cursor:
+#ifndef QT_NO_CURSOR
+ return v_cast<QCursor>(a)->shape() == v_cast<QCursor>(b)->shape();
+#endif
+ case QVariant::Bitmap:
+ return v_cast<QBitmap>(a)->cacheKey()
+ == v_cast<QBitmap>(b)->cacheKey();
+ case QVariant::Polygon:
+ return *v_cast<QPolygon>(a) == *v_cast<QPolygon>(b);
+ case QVariant::Region:
+ return *v_cast<QRegion>(a) == *v_cast<QRegion>(b);
+ case QVariant::Font:
+ return *v_cast<QFont>(a) == *v_cast<QFont>(b);
+ case QVariant::Pixmap:
+ return v_cast<QPixmap>(a)->cacheKey() == v_cast<QPixmap>(b)->cacheKey();
+ case QVariant::Image:
+ return *v_cast<QImage>(a) == *v_cast<QImage>(b);
+ case QVariant::Brush:
+ return *v_cast<QBrush>(a) == *v_cast<QBrush>(b);
+ case QVariant::Color:
+ return *v_cast<QColor>(a) == *v_cast<QColor>(b);
+ case QVariant::Palette:
+ return *v_cast<QPalette>(a) == *v_cast<QPalette>(b);
+#ifdef QT3_SUPPORT
+ case QVariant::ColorGroup:
+ return *v_cast<QColorGroup>(a) == *v_cast<QColorGroup>(b);
+#endif
+#ifndef QT_NO_ICON
+ case QVariant::Icon:
+ /* QIcon::operator==() cannot be reasonably implemented for QIcon,
+ * so we always return false. */
+ return false;
+#endif
+ case QVariant::Matrix:
+ return *v_cast<QMatrix>(a) == *v_cast<QMatrix>(b);
+ case QVariant::Transform:
+ return *v_cast<QTransform>(a) == *v_cast<QTransform>(b);
+ case QVariant::TextFormat:
+ return *v_cast<QTextFormat>(a) == *v_cast<QTextFormat>(b);
+ case QVariant::TextLength:
+ return *v_cast<QTextLength>(a) == *v_cast<QTextLength>(b);
+ case QVariant::SizePolicy:
+ return *v_cast<QSizePolicy>(a) == *v_cast<QSizePolicy>(b);
+#ifndef QT_NO_SHORTCUT
+ case QVariant::KeySequence:
+ return *v_cast<QKeySequence>(a) == *v_cast<QKeySequence>(b);
+#endif
+ case QVariant::Pen:
+ return *v_cast<QPen>(a) == *v_cast<QPen>(b);
+ default:
+ break;
+ }
+ return qcoreVariantHandler()->compare(a, b);
+}
+
+
+
+static bool convert(const QVariant::Private *d, QVariant::Type t,
+ void *result, bool *ok)
+{
+ switch (t) {
+ case QVariant::ByteArray:
+ if (d->type == QVariant::Color) {
+ *static_cast<QByteArray *>(result) = v_cast<QColor>(d)->name().toLatin1();
+ return true;
+ }
+ break;
+ case QVariant::String: {
+ QString *str = static_cast<QString *>(result);
+ switch (d->type) {
+#ifndef QT_NO_SHORTCUT
+ case QVariant::KeySequence:
+ *str = QString(*v_cast<QKeySequence>(d));
+ return true;
+#endif
+ case QVariant::Font:
+ *str = v_cast<QFont>(d)->toString();
+ return true;
+ case QVariant::Color:
+ *str = v_cast<QColor>(d)->name();
+ return true;
+ default:
+ break;
+ }
+ break;
+ }
+ case QVariant::Pixmap:
+ if (d->type == QVariant::Image) {
+ *static_cast<QPixmap *>(result) = QPixmap::fromImage(*v_cast<QImage>(d));
+ return true;
+ } else if (d->type == QVariant::Bitmap) {
+ *static_cast<QPixmap *>(result) = *v_cast<QBitmap>(d);
+ return true;
+ } else if (d->type == QVariant::Brush) {
+ if (v_cast<QBrush>(d)->style() == Qt::TexturePattern) {
+ *static_cast<QPixmap *>(result) = v_cast<QBrush>(d)->texture();
+ return true;
+ }
+ }
+ break;
+ case QVariant::Image:
+ if (d->type == QVariant::Pixmap) {
+ *static_cast<QImage *>(result) = v_cast<QPixmap>(d)->toImage();
+ return true;
+ } else if (d->type == QVariant::Bitmap) {
+ *static_cast<QImage *>(result) = v_cast<QBitmap>(d)->toImage();
+ return true;
+ }
+ break;
+ case QVariant::Bitmap:
+ if (d->type == QVariant::Pixmap) {
+ *static_cast<QBitmap *>(result) = *v_cast<QPixmap>(d);
+ return true;
+ } else if (d->type == QVariant::Image) {
+ *static_cast<QBitmap *>(result) = QBitmap::fromImage(*v_cast<QImage>(d));
+ return true;
+ }
+ break;
+#ifndef QT_NO_SHORTCUT
+ case QVariant::Int:
+ if (d->type == QVariant::KeySequence) {
+ *static_cast<int *>(result) = (int)(*(v_cast<QKeySequence>(d)));
+ return true;
+ }
+ break;
+#endif
+ case QVariant::Font:
+ if (d->type == QVariant::String) {
+ QFont *f = static_cast<QFont *>(result);
+ f->fromString(*v_cast<QString>(d));
+ return true;
+ }
+ break;
+ case QVariant::Color:
+ if (d->type == QVariant::String) {
+ static_cast<QColor *>(result)->setNamedColor(*v_cast<QString>(d));
+ return static_cast<QColor *>(result)->isValid();
+ } else if (d->type == QVariant::ByteArray) {
+ static_cast<QColor *>(result)->setNamedColor(QString::fromLatin1(
+ *v_cast<QByteArray>(d)));
+ return true;
+ } else if (d->type == QVariant::Brush) {
+ if (v_cast<QBrush>(d)->style() == Qt::SolidPattern) {
+ *static_cast<QColor *>(result) = v_cast<QBrush>(d)->color();
+ return true;
+ }
+ }
+ break;
+ case QVariant::Brush:
+ if (d->type == QVariant::Color) {
+ *static_cast<QBrush *>(result) = QBrush(*v_cast<QColor>(d));
+ return true;
+ } else if (d->type == QVariant::Pixmap) {
+ *static_cast<QBrush *>(result) = QBrush(*v_cast<QPixmap>(d));
+ return true;
+ }
+ break;
+#ifndef QT_NO_SHORTCUT
+ case QVariant::KeySequence: {
+ QKeySequence *seq = static_cast<QKeySequence *>(result);
+ switch (d->type) {
+ case QVariant::String:
+ *seq = QKeySequence(*v_cast<QString>(d));
+ return true;
+ case QVariant::Int:
+ *seq = QKeySequence(d->data.i);
+ return true;
+ default:
+ break;
+ }
+ }
+#endif
+ default:
+ break;
+ }
+ return qcoreVariantHandler()->convert(d, t, result, ok);
+}
+
+#if !defined(QT_NO_DEBUG_STREAM) && !defined(Q_BROKEN_DEBUG_STREAM)
+static void streamDebug(QDebug dbg, const QVariant &v)
+{
+ switch(v.type()) {
+ case QVariant::Cursor:
+#ifndef QT_NO_CURSOR
+// dbg.nospace() << qvariant_cast<QCursor>(v); //FIXME
+#endif
+ break;
+ case QVariant::Bitmap:
+// dbg.nospace() << qvariant_cast<QBitmap>(v); //FIXME
+ break;
+ case QVariant::Polygon:
+ dbg.nospace() << qvariant_cast<QPolygon>(v);
+ break;
+ case QVariant::Region:
+ dbg.nospace() << qvariant_cast<QRegion>(v);
+ break;
+ case QVariant::Font:
+// dbg.nospace() << qvariant_cast<QFont>(v); //FIXME
+ break;
+ case QVariant::Matrix:
+ dbg.nospace() << qvariant_cast<QMatrix>(v);
+ break;
+ case QVariant::Transform:
+ dbg.nospace() << qvariant_cast<QTransform>(v);
+ break;
+ case QVariant::Pixmap:
+// dbg.nospace() << qvariant_cast<QPixmap>(v); //FIXME
+ break;
+ case QVariant::Image:
+// dbg.nospace() << qvariant_cast<QImage>(v); //FIXME
+ break;
+ case QVariant::Brush:
+ dbg.nospace() << qvariant_cast<QBrush>(v);
+ break;
+ case QVariant::Color:
+ dbg.nospace() << qvariant_cast<QColor>(v);
+ break;
+ case QVariant::Palette:
+// dbg.nospace() << qvariant_cast<QPalette>(v); //FIXME
+ break;
+#ifndef QT_NO_ICON
+ case QVariant::Icon:
+// dbg.nospace() << qvariant_cast<QIcon>(v); // FIXME
+ break;
+#endif
+ case QVariant::SizePolicy:
+// dbg.nospace() << qvariant_cast<QSizePolicy>(v); //FIXME
+ break;
+#ifndef QT_NO_SHORTCUT
+ case QVariant::KeySequence:
+ dbg.nospace() << qvariant_cast<QKeySequence>(v);
+ break;
+#endif
+ case QVariant::Pen:
+ dbg.nospace() << qvariant_cast<QPen>(v);
+ break;
+ default:
+ qcoreVariantHandler()->debugStream(dbg, v);
+ break;
+ }
+}
+#endif
+
+const QVariant::Handler qt_gui_variant_handler = {
+ construct,
+ clear,
+ isNull,
+#ifndef QT_NO_DATASTREAM
+ 0,
+ 0,
+#endif
+ compare,
+ convert,
+ 0,
+#if !defined(QT_NO_DEBUG_STREAM) && !defined(Q_BROKEN_DEBUG_STREAM)
+ streamDebug
+#else
+ 0
+#endif
+};
+
+struct QMetaTypeGuiHelper
+{
+ QMetaType::Constructor constr;
+ QMetaType::Destructor destr;
+#ifndef QT_NO_DATASTREAM
+ QMetaType::SaveOperator saveOp;
+ QMetaType::LoadOperator loadOp;
+#endif
+};
+
+extern Q_CORE_EXPORT const QMetaTypeGuiHelper *qMetaTypeGuiHelper;
+
+
+#ifdef QT_NO_DATASTREAM
+# define Q_DECL_METATYPE_HELPER(TYPE) \
+ typedef void *(*QConstruct##TYPE)(const TYPE *); \
+ static const QConstruct##TYPE qConstruct##TYPE = qMetaTypeConstructHelper<TYPE>; \
+ typedef void (*QDestruct##TYPE)(TYPE *); \
+ static const QDestruct##TYPE qDestruct##TYPE = qMetaTypeDeleteHelper<TYPE>;
+#else
+# define Q_DECL_METATYPE_HELPER(TYPE) \
+ typedef void *(*QConstruct##TYPE)(const TYPE *); \
+ static const QConstruct##TYPE qConstruct##TYPE = qMetaTypeConstructHelper<TYPE>; \
+ typedef void (*QDestruct##TYPE)(TYPE *); \
+ static const QDestruct##TYPE qDestruct##TYPE = qMetaTypeDeleteHelper<TYPE>; \
+ typedef void (*QSave##TYPE)(QDataStream &, const TYPE *); \
+ static const QSave##TYPE qSave##TYPE = qMetaTypeSaveHelper<TYPE>; \
+ typedef void (*QLoad##TYPE)(QDataStream &, TYPE *); \
+ static const QLoad##TYPE qLoad##TYPE = qMetaTypeLoadHelper<TYPE>;
+#endif
+
+#ifdef QT3_SUPPORT
+Q_DECL_METATYPE_HELPER(QColorGroup)
+#endif
+Q_DECL_METATYPE_HELPER(QFont)
+Q_DECL_METATYPE_HELPER(QPixmap)
+Q_DECL_METATYPE_HELPER(QBrush)
+Q_DECL_METATYPE_HELPER(QColor)
+Q_DECL_METATYPE_HELPER(QPalette)
+#ifndef QT_NO_ICON
+Q_DECL_METATYPE_HELPER(QIcon)
+#endif
+Q_DECL_METATYPE_HELPER(QImage)
+Q_DECL_METATYPE_HELPER(QPolygon)
+Q_DECL_METATYPE_HELPER(QRegion)
+Q_DECL_METATYPE_HELPER(QBitmap)
+#ifndef QT_NO_CURSOR
+Q_DECL_METATYPE_HELPER(QCursor)
+#endif
+Q_DECL_METATYPE_HELPER(QSizePolicy)
+#ifndef QT_NO_SHORTCUT
+Q_DECL_METATYPE_HELPER(QKeySequence)
+#endif
+Q_DECL_METATYPE_HELPER(QPen)
+Q_DECL_METATYPE_HELPER(QTextLength)
+Q_DECL_METATYPE_HELPER(QTextFormat)
+Q_DECL_METATYPE_HELPER(QMatrix)
+Q_DECL_METATYPE_HELPER(QTransform)
+
+#ifdef QT_NO_DATASTREAM
+# define Q_IMPL_METATYPE_HELPER(TYPE) \
+ { reinterpret_cast<QMetaType::Constructor>(qConstruct##TYPE), \
+ reinterpret_cast<QMetaType::Destructor>(qDestruct##TYPE) }
+#else
+# define Q_IMPL_METATYPE_HELPER(TYPE) \
+ { reinterpret_cast<QMetaType::Constructor>(qConstruct##TYPE), \
+ reinterpret_cast<QMetaType::Destructor>(qDestruct##TYPE), \
+ reinterpret_cast<QMetaType::SaveOperator>(qSave##TYPE), \
+ reinterpret_cast<QMetaType::LoadOperator>(qLoad##TYPE) \
+ }
+#endif
+
+static const QMetaTypeGuiHelper qVariantGuiHelper[] = {
+#ifdef QT3_SUPPORT
+ Q_IMPL_METATYPE_HELPER(QColorGroup),
+#else
+ {0, 0, 0, 0},
+#endif
+ Q_IMPL_METATYPE_HELPER(QFont),
+ Q_IMPL_METATYPE_HELPER(QPixmap),
+ Q_IMPL_METATYPE_HELPER(QBrush),
+ Q_IMPL_METATYPE_HELPER(QColor),
+ Q_IMPL_METATYPE_HELPER(QPalette),
+#ifdef QT_NO_ICON
+ {0, 0, 0, 0},
+#else
+ Q_IMPL_METATYPE_HELPER(QIcon),
+#endif
+ Q_IMPL_METATYPE_HELPER(QImage),
+ Q_IMPL_METATYPE_HELPER(QPolygon),
+ Q_IMPL_METATYPE_HELPER(QRegion),
+ Q_IMPL_METATYPE_HELPER(QBitmap),
+#ifdef QT_NO_CURSOR
+ {0, 0, 0, 0},
+#else
+ Q_IMPL_METATYPE_HELPER(QCursor),
+#endif
+ Q_IMPL_METATYPE_HELPER(QSizePolicy),
+#ifdef QT_NO_SHORTCUT
+ {0, 0, 0, 0},
+#else
+ Q_IMPL_METATYPE_HELPER(QKeySequence),
+#endif
+ Q_IMPL_METATYPE_HELPER(QPen),
+ Q_IMPL_METATYPE_HELPER(QTextLength),
+ Q_IMPL_METATYPE_HELPER(QTextFormat),
+ Q_IMPL_METATYPE_HELPER(QMatrix),
+ Q_IMPL_METATYPE_HELPER(QTransform)
+};
+
+static const QVariant::Handler *qt_guivariant_last_handler = 0;
+int qRegisterGuiVariant()
+{
+ qt_guivariant_last_handler = QVariant::handler;
+ QVariant::handler = &qt_gui_variant_handler;
+ qMetaTypeGuiHelper = qVariantGuiHelper;
+ return 1;
+}
+Q_CONSTRUCTOR_FUNCTION(qRegisterGuiVariant)
+
+int qUnregisterGuiVariant()
+{
+ QVariant::handler = qt_guivariant_last_handler;
+ qMetaTypeGuiHelper = 0;
+ return 1;
+}
+Q_DESTRUCTOR_FUNCTION(qUnregisterGuiVariant)
+
+QT_END_NAMESPACE
diff --git a/src/gui/kernel/qkeymapper.cpp b/src/gui/kernel/qkeymapper.cpp
new file mode 100644
index 0000000000..535d0094fd
--- /dev/null
+++ b/src/gui/kernel/qkeymapper.cpp
@@ -0,0 +1,121 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+
+#include "qapplication.h"
+
+#include <private/qobject_p.h>
+#include "qkeymapper_p.h"
+#include <qwidget.h>
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QKeyMapper
+ \since 4.2
+ \ingroup application
+ \internal
+
+ \sa QObject
+*/
+
+/*!
+ Constructs a new key mapper.
+*/
+QKeyMapper::QKeyMapper()
+ : QObject(*new QKeyMapperPrivate, 0)
+{
+}
+
+/*!
+ Destroys the key mapper.
+*/
+QKeyMapper::~QKeyMapper()
+{
+}
+
+QList<int> QKeyMapper::possibleKeys(QKeyEvent *e)
+{
+ QList<int> result;
+
+ if (!e->nativeScanCode()) {
+ if (e->key() && (e->key() != Qt::Key_unknown))
+ result << int(e->key() + e->modifiers());
+ else if (!e->text().isEmpty())
+ result << int(e->text().at(0).unicode() + e->modifiers());
+ return result;
+ }
+
+ return instance()->d_func()->possibleKeys(e);
+}
+
+extern bool qt_sendSpontaneousEvent(QObject *receiver, QEvent *event); // in qapplication_*.cpp
+void QKeyMapper::changeKeyboard()
+{
+ instance()->d_func()->clearMappings();
+
+ // inform all toplevel widgets of the change
+ QEvent e(QEvent::KeyboardLayoutChange);
+ QWidgetList list = QApplication::topLevelWidgets();
+ for (int i = 0; i < list.size(); ++i) {
+ QWidget *w = list.at(i);
+ qt_sendSpontaneousEvent(w, &e);
+ }
+}
+
+Q_GLOBAL_STATIC(QKeyMapper, keymapper)
+/*!
+ Returns the pointer to the single instance of QKeyMapper in the application.
+ If none yet exists, the function ensures that one is created.
+*/
+QKeyMapper *QKeyMapper::instance()
+{
+ return keymapper();
+}
+
+QKeyMapperPrivate *qt_keymapper_private()
+{
+ return QKeyMapper::instance()->d_func();
+}
+
+Q_GUI_EXPORT QList<int> qt_keymapper_possibleKeys(QKeyEvent *e) { return QKeyMapper::instance()->possibleKeys(e); }
+
+QT_END_NAMESPACE
diff --git a/src/gui/kernel/qkeymapper_mac.cpp b/src/gui/kernel/qkeymapper_mac.cpp
new file mode 100644
index 0000000000..1a0fb08433
--- /dev/null
+++ b/src/gui/kernel/qkeymapper_mac.cpp
@@ -0,0 +1,931 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <private/qt_mac_p.h>
+#include <qdebug.h>
+#include <qevent.h>
+#include <private/qevent_p.h>
+#include <qtextcodec.h>
+#include <qapplication.h>
+#include <qinputcontext.h>
+#include <private/qkeymapper_p.h>
+#include <private/qapplication_p.h>
+
+QT_BEGIN_NAMESPACE
+
+QT_USE_NAMESPACE
+
+/*****************************************************************************
+ QKeyMapper debug facilities
+ *****************************************************************************/
+//#define DEBUG_KEY_BINDINGS
+//#define DEBUG_KEY_BINDINGS_MODIFIERS
+//#define DEBUG_KEY_MAPS
+
+/*****************************************************************************
+ Internal variables and functions
+ *****************************************************************************/
+bool qt_mac_eat_unicode_key = false;
+extern bool qt_sendSpontaneousEvent(QObject *obj, QEvent *event); //qapplication_mac.cpp
+
+Q_GUI_EXPORT void qt_mac_secure_keyboard(bool b)
+{
+ static bool secure = false;
+ if (b != secure){
+ b ? EnableSecureEventInput() : DisableSecureEventInput();
+ secure = b;
+ }
+}
+
+/*
+ \internal
+ A Mac KeyboardLayoutItem has 8 possible states:
+ 1. Unmodified
+ 2. Shift
+ 3. Control
+ 4. Control + Shift
+ 5. Alt
+ 6. Alt + Shift
+ 7. Alt + Control
+ 8. Alt + Control + Shift
+ 9. Meta
+ 10. Meta + Shift
+ 11. Meta + Control
+ 12. Meta + Control + Shift
+ 13. Meta + Alt
+ 14. Meta + Alt + Shift
+ 15. Meta + Alt + Control
+ 16. Meta + Alt + Control + Shift
+*/
+struct KeyboardLayoutItem {
+ bool dirty;
+ quint32 qtKey[16]; // Can by any Qt::Key_<foo>, or unicode character
+};
+
+// Possible modifier states.
+// NOTE: The order of these states match the order in QKeyMapperPrivate::updatePossibleKeyCodes()!
+static const Qt::KeyboardModifiers ModsTbl[] = {
+ Qt::NoModifier, // 0
+ Qt::ShiftModifier, // 1
+ Qt::ControlModifier, // 2
+ Qt::ControlModifier | Qt::ShiftModifier, // 3
+ Qt::AltModifier, // 4
+ Qt::AltModifier | Qt::ShiftModifier, // 5
+ Qt::AltModifier | Qt::ControlModifier, // 6
+ Qt::AltModifier | Qt::ShiftModifier | Qt::ControlModifier, // 7
+ Qt::MetaModifier, // 8
+ Qt::MetaModifier | Qt::ShiftModifier, // 9
+ Qt::MetaModifier | Qt::ControlModifier, // 10
+ Qt::MetaModifier | Qt::ControlModifier | Qt::ShiftModifier,// 11
+ Qt::MetaModifier | Qt::AltModifier, // 12
+ Qt::MetaModifier | Qt::AltModifier | Qt::ShiftModifier, // 13
+ Qt::MetaModifier | Qt::AltModifier | Qt::ControlModifier, // 14
+ Qt::MetaModifier | Qt::AltModifier | Qt::ShiftModifier | Qt::ControlModifier, // 15
+};
+
+/* key maps */
+struct qt_mac_enum_mapper
+{
+ int mac_code;
+ int qt_code;
+#if defined(DEBUG_KEY_BINDINGS)
+# define QT_MAC_MAP_ENUM(x) x, #x
+ const char *desc;
+#else
+# define QT_MAC_MAP_ENUM(x) x
+#endif
+};
+
+//modifiers
+static qt_mac_enum_mapper qt_mac_modifier_symbols[] = {
+ { shiftKey, QT_MAC_MAP_ENUM(Qt::ShiftModifier) },
+ { rightShiftKey, QT_MAC_MAP_ENUM(Qt::ShiftModifier) },
+ { controlKey, QT_MAC_MAP_ENUM(Qt::MetaModifier) },
+ { rightControlKey, QT_MAC_MAP_ENUM(Qt::MetaModifier) },
+ { cmdKey, QT_MAC_MAP_ENUM(Qt::ControlModifier) },
+ { optionKey, QT_MAC_MAP_ENUM(Qt::AltModifier) },
+ { rightOptionKey, QT_MAC_MAP_ENUM(Qt::AltModifier) },
+ { kEventKeyModifierNumLockMask, QT_MAC_MAP_ENUM(Qt::KeypadModifier) },
+ { 0, QT_MAC_MAP_ENUM(0) }
+};
+Qt::KeyboardModifiers qt_mac_get_modifiers(int keys)
+{
+#ifdef DEBUG_KEY_BINDINGS_MODIFIERS
+ qDebug("Qt: internal: **Mapping modifiers: %d (0x%04x)", keys, keys);
+#endif
+ Qt::KeyboardModifiers ret = Qt::NoModifier;
+ for (int i = 0; qt_mac_modifier_symbols[i].qt_code; i++) {
+ if (keys & qt_mac_modifier_symbols[i].mac_code) {
+#ifdef DEBUG_KEY_BINDINGS_MODIFIERS
+ qDebug("Qt: internal: got modifier: %s", qt_mac_modifier_symbols[i].desc);
+#endif
+ ret |= Qt::KeyboardModifier(qt_mac_modifier_symbols[i].qt_code);
+ }
+ }
+ return ret;
+}
+static int qt_mac_get_mac_modifiers(Qt::KeyboardModifiers keys)
+{
+#ifdef DEBUG_KEY_BINDINGS_MODIFIERS
+ qDebug("Qt: internal: **Mapping modifiers: %d (0x%04x)", (int)keys, (int)keys);
+#endif
+ int ret = 0;
+ for (int i = 0; qt_mac_modifier_symbols[i].qt_code; i++) {
+ if (keys & qt_mac_modifier_symbols[i].qt_code) {
+#ifdef DEBUG_KEY_BINDINGS_MODIFIERS
+ qDebug("Qt: internal: got modifier: %s", qt_mac_modifier_symbols[i].desc);
+#endif
+ ret |= qt_mac_modifier_symbols[i].mac_code;
+ }
+ }
+ return ret;
+}
+void qt_mac_send_modifiers_changed(quint32 modifiers, QObject *object)
+{
+ static quint32 cachedModifiers = 0;
+ quint32 lastModifiers = cachedModifiers,
+ changedModifiers = lastModifiers ^ modifiers;
+ cachedModifiers = modifiers;
+
+ //check the bits
+ static qt_mac_enum_mapper modifier_key_symbols[] = {
+ { shiftKeyBit, QT_MAC_MAP_ENUM(Qt::Key_Shift) },
+ { rightShiftKeyBit, QT_MAC_MAP_ENUM(Qt::Key_Shift) }, //???
+ { controlKeyBit, QT_MAC_MAP_ENUM(Qt::Key_Meta) },
+ { rightControlKeyBit, QT_MAC_MAP_ENUM(Qt::Key_Meta) }, //???
+ { cmdKeyBit, QT_MAC_MAP_ENUM(Qt::Key_Control) },
+ { optionKeyBit, QT_MAC_MAP_ENUM(Qt::Key_Alt) },
+ { rightOptionKeyBit, QT_MAC_MAP_ENUM(Qt::Key_Alt) }, //???
+ { alphaLockBit, QT_MAC_MAP_ENUM(Qt::Key_CapsLock) },
+ { kEventKeyModifierNumLockBit, QT_MAC_MAP_ENUM(Qt::Key_NumLock) },
+ { 0, QT_MAC_MAP_ENUM(0) } };
+ for (int i = 0; i <= 32; i++) { //just check each bit
+ if (!(changedModifiers & (1 << i)))
+ continue;
+ QEvent::Type etype = QEvent::KeyPress;
+ if (lastModifiers & (1 << i))
+ etype = QEvent::KeyRelease;
+ int key = 0;
+ for (uint x = 0; modifier_key_symbols[x].mac_code; x++) {
+ if (modifier_key_symbols[x].mac_code == i) {
+#ifdef DEBUG_KEY_BINDINGS_MODIFIERS
+ qDebug("got modifier changed: %s", modifier_key_symbols[x].desc);
+#endif
+ key = modifier_key_symbols[x].qt_code;
+ break;
+ }
+ }
+ if (!key) {
+#ifdef DEBUG_KEY_BINDINGS_MODIFIERS
+ qDebug("could not get modifier changed: %d", i);
+#endif
+ continue;
+ }
+#ifdef DEBUG_KEY_BINDINGS_MODIFIERS
+ qDebug("KeyEvent (modif): Sending %s to %s::%s: %d - 0x%08x",
+ etype == QEvent::KeyRelease ? "KeyRelease" : "KeyPress",
+ object ? object->metaObject()->className() : "none",
+ object ? object->objectName().toLatin1().constData() : "",
+ key, (int)modifiers);
+#endif
+ QKeyEvent ke(etype, key, qt_mac_get_modifiers(modifiers ^ (1 << i)), QLatin1String(""));
+ qt_sendSpontaneousEvent(object, &ke);
+ }
+}
+
+//keyboard keys (non-modifiers)
+static qt_mac_enum_mapper qt_mac_keyboard_symbols[] = {
+ { kHomeCharCode, QT_MAC_MAP_ENUM(Qt::Key_Home) },
+ { kEnterCharCode, QT_MAC_MAP_ENUM(Qt::Key_Enter) },
+ { kEndCharCode, QT_MAC_MAP_ENUM(Qt::Key_End) },
+ { kBackspaceCharCode, QT_MAC_MAP_ENUM(Qt::Key_Backspace) },
+ { kTabCharCode, QT_MAC_MAP_ENUM(Qt::Key_Tab) },
+ { kPageUpCharCode, QT_MAC_MAP_ENUM(Qt::Key_PageUp) },
+ { kPageDownCharCode, QT_MAC_MAP_ENUM(Qt::Key_PageDown) },
+ { kReturnCharCode, QT_MAC_MAP_ENUM(Qt::Key_Return) },
+ { kEscapeCharCode, QT_MAC_MAP_ENUM(Qt::Key_Escape) },
+ { kLeftArrowCharCode, QT_MAC_MAP_ENUM(Qt::Key_Left) },
+ { kRightArrowCharCode, QT_MAC_MAP_ENUM(Qt::Key_Right) },
+ { kUpArrowCharCode, QT_MAC_MAP_ENUM(Qt::Key_Up) },
+ { kDownArrowCharCode, QT_MAC_MAP_ENUM(Qt::Key_Down) },
+ { kHelpCharCode, QT_MAC_MAP_ENUM(Qt::Key_Help) },
+ { kDeleteCharCode, QT_MAC_MAP_ENUM(Qt::Key_Delete) },
+//ascii maps, for debug
+ { ':', QT_MAC_MAP_ENUM(Qt::Key_Colon) },
+ { ';', QT_MAC_MAP_ENUM(Qt::Key_Semicolon) },
+ { '<', QT_MAC_MAP_ENUM(Qt::Key_Less) },
+ { '=', QT_MAC_MAP_ENUM(Qt::Key_Equal) },
+ { '>', QT_MAC_MAP_ENUM(Qt::Key_Greater) },
+ { '?', QT_MAC_MAP_ENUM(Qt::Key_Question) },
+ { '@', QT_MAC_MAP_ENUM(Qt::Key_At) },
+ { ' ', QT_MAC_MAP_ENUM(Qt::Key_Space) },
+ { '!', QT_MAC_MAP_ENUM(Qt::Key_Exclam) },
+ { '"', QT_MAC_MAP_ENUM(Qt::Key_QuoteDbl) },
+ { '#', QT_MAC_MAP_ENUM(Qt::Key_NumberSign) },
+ { '$', QT_MAC_MAP_ENUM(Qt::Key_Dollar) },
+ { '%', QT_MAC_MAP_ENUM(Qt::Key_Percent) },
+ { '&', QT_MAC_MAP_ENUM(Qt::Key_Ampersand) },
+ { '\'', QT_MAC_MAP_ENUM(Qt::Key_Apostrophe) },
+ { '(', QT_MAC_MAP_ENUM(Qt::Key_ParenLeft) },
+ { ')', QT_MAC_MAP_ENUM(Qt::Key_ParenRight) },
+ { '*', QT_MAC_MAP_ENUM(Qt::Key_Asterisk) },
+ { '+', QT_MAC_MAP_ENUM(Qt::Key_Plus) },
+ { ',', QT_MAC_MAP_ENUM(Qt::Key_Comma) },
+ { '-', QT_MAC_MAP_ENUM(Qt::Key_Minus) },
+ { '.', QT_MAC_MAP_ENUM(Qt::Key_Period) },
+ { '/', QT_MAC_MAP_ENUM(Qt::Key_Slash) },
+ { '[', QT_MAC_MAP_ENUM(Qt::Key_BracketLeft) },
+ { ']', QT_MAC_MAP_ENUM(Qt::Key_BracketRight) },
+ { '\\', QT_MAC_MAP_ENUM(Qt::Key_Backslash) },
+ { '_', QT_MAC_MAP_ENUM(Qt::Key_Underscore) },
+ { '`', QT_MAC_MAP_ENUM(Qt::Key_QuoteLeft) },
+ { '{', QT_MAC_MAP_ENUM(Qt::Key_BraceLeft) },
+ { '}', QT_MAC_MAP_ENUM(Qt::Key_BraceRight) },
+ { '|', QT_MAC_MAP_ENUM(Qt::Key_Bar) },
+ { '~', QT_MAC_MAP_ENUM(Qt::Key_AsciiTilde) },
+ { '^', QT_MAC_MAP_ENUM(Qt::Key_AsciiCircum) },
+ { 0, QT_MAC_MAP_ENUM(0) }
+};
+
+static qt_mac_enum_mapper qt_mac_keyvkey_symbols[] = { //real scan codes
+ { 122, QT_MAC_MAP_ENUM(Qt::Key_F1) },
+ { 120, QT_MAC_MAP_ENUM(Qt::Key_F2) },
+ { 99, QT_MAC_MAP_ENUM(Qt::Key_F3) },
+ { 118, QT_MAC_MAP_ENUM(Qt::Key_F4) },
+ { 96, QT_MAC_MAP_ENUM(Qt::Key_F5) },
+ { 97, QT_MAC_MAP_ENUM(Qt::Key_F6) },
+ { 98, QT_MAC_MAP_ENUM(Qt::Key_F7) },
+ { 100, QT_MAC_MAP_ENUM(Qt::Key_F8) },
+ { 101, QT_MAC_MAP_ENUM(Qt::Key_F9) },
+ { 109, QT_MAC_MAP_ENUM(Qt::Key_F10) },
+ { 103, QT_MAC_MAP_ENUM(Qt::Key_F11) },
+ { 111, QT_MAC_MAP_ENUM(Qt::Key_F12) },
+ { 105, QT_MAC_MAP_ENUM(Qt::Key_F13) },
+ { 107, QT_MAC_MAP_ENUM(Qt::Key_F14) },
+ { 113, QT_MAC_MAP_ENUM(Qt::Key_F15) },
+ { 106, QT_MAC_MAP_ENUM(Qt::Key_F16) },
+ { 0, QT_MAC_MAP_ENUM(0) }
+};
+
+static int qt_mac_get_key(int modif, const QChar &key, int virtualKey)
+{
+#ifdef DEBUG_KEY_BINDINGS
+ qDebug("**Mapping key: %d (0x%04x) - %d (0x%04x)", key.unicode(), key.unicode(), virtualKey, virtualKey);
+#endif
+
+ if (key == kClearCharCode && virtualKey == 0x47)
+ return Qt::Key_Clear;
+
+ if (key.isDigit()) {
+#ifdef DEBUG_KEY_BINDINGS
+ qDebug("%d: got key: %d", __LINE__, key.digitValue());
+#endif
+ return key.digitValue() + Qt::Key_0;
+ }
+
+ if (key.isLetter()) {
+#ifdef DEBUG_KEY_BINDINGS
+ qDebug("%d: got key: %d", __LINE__, (key.toUpper().unicode() - 'A'));
+#endif
+ return (key.toUpper().unicode() - 'A') + Qt::Key_A;
+ }
+ if (key.isSymbol()) {
+#ifdef DEBUG_KEY_BINDINGS
+ qDebug("%d: got key: %d", __LINE__, (key.unicode()));
+#endif
+ return key.unicode();
+ }
+
+ for (int i = 0; qt_mac_keyboard_symbols[i].qt_code; i++) {
+ if (qt_mac_keyboard_symbols[i].mac_code == key) {
+ /* To work like Qt for X11 we issue Backtab when Shift + Tab are pressed */
+ if (qt_mac_keyboard_symbols[i].qt_code == Qt::Key_Tab && (modif & Qt::ShiftModifier)) {
+#ifdef DEBUG_KEY_BINDINGS
+ qDebug("%d: got key: Qt::Key_Backtab", __LINE__);
+#endif
+ return Qt::Key_Backtab;
+ }
+
+#ifdef DEBUG_KEY_BINDINGS
+ qDebug("%d: got key: %s", __LINE__, qt_mac_keyboard_symbols[i].desc);
+#endif
+ return qt_mac_keyboard_symbols[i].qt_code;
+ }
+ }
+
+ //last ditch try to match the scan code
+ for (int i = 0; qt_mac_keyvkey_symbols[i].qt_code; i++) {
+ if (qt_mac_keyvkey_symbols[i].mac_code == virtualKey) {
+#ifdef DEBUG_KEY_BINDINGS
+ qDebug("%d: got key: %s", __LINE__, qt_mac_keyvkey_symbols[i].desc);
+#endif
+ return qt_mac_keyvkey_symbols[i].qt_code;
+ }
+ }
+
+ //oh well
+#ifdef DEBUG_KEY_BINDINGS
+ qDebug("Unknown case.. %s:%d %d[%d] %d", __FILE__, __LINE__, key.unicode(), key.toLatin1(), virtualKey);
+#endif
+ return Qt::Key_unknown;
+}
+
+static Boolean qt_KeyEventComparatorProc(EventRef inEvent, void *data)
+{
+ UInt32 ekind = GetEventKind(inEvent),
+ eclass = GetEventClass(inEvent);
+ return (eclass == kEventClassKeyboard && (void *)ekind == data);
+}
+
+static bool translateKeyEventInternal(EventHandlerCallRef er, EventRef keyEvent, int *qtKey,
+ QChar *outChar, Qt::KeyboardModifiers *outModifiers, bool *outHandled)
+{
+#if !defined(QT_MAC_USE_COCOA) || defined(Q_OS_MAC64)
+ Q_UNUSED(er);
+ Q_UNUSED(outHandled);
+#endif
+ const UInt32 ekind = GetEventKind(keyEvent);
+ {
+ UInt32 mac_modifiers = 0;
+ GetEventParameter(keyEvent, kEventParamKeyModifiers, typeUInt32, 0,
+ sizeof(mac_modifiers), 0, &mac_modifiers);
+#ifdef DEBUG_KEY_BINDINGS_MODIFIERS
+ qDebug("************ Mapping modifiers and key ***********");
+#endif
+ *outModifiers = qt_mac_get_modifiers(mac_modifiers);
+#ifdef DEBUG_KEY_BINDINGS_MODIFIERS
+ qDebug("------------ Mapping modifiers and key -----------");
+#endif
+ }
+
+ //get keycode
+ UInt32 keyCode = 0;
+ GetEventParameter(keyEvent, kEventParamKeyCode, typeUInt32, 0, sizeof(keyCode), 0, &keyCode);
+
+ //get mac mapping
+ static UInt32 tmp_unused_state = 0L;
+ const UCKeyboardLayout *uchrData = 0;
+#if defined(Q_OS_MAC32)
+ KeyboardLayoutRef keyLayoutRef = 0;
+ KLGetCurrentKeyboardLayout(&keyLayoutRef);
+ OSStatus err;
+ if (keyLayoutRef != 0) {
+ err = KLGetKeyboardLayoutProperty(keyLayoutRef, kKLuchrData,
+ (reinterpret_cast<const void **>(&uchrData)));
+ if (err != noErr) {
+ qWarning("Qt::internal::unable to get keyboardlayout %ld %s:%d",
+ long(err), __FILE__, __LINE__);
+ }
+ }
+#else
+ QCFType<TISInputSourceRef> inputSource = TISCopyCurrentKeyboardInputSource();
+ Q_ASSERT(inputSource != 0);
+ CFDataRef data = static_cast<CFDataRef>(TISGetInputSourceProperty(inputSource,
+ kTISPropertyUnicodeKeyLayoutData));
+ uchrData = data ? reinterpret_cast<const UCKeyboardLayout *>(CFDataGetBytePtr(data)) : 0;
+#endif
+ *qtKey = Qt::Key_unknown;
+ if (uchrData) {
+ // The easy stuff; use the unicode stuff!
+ UniChar string[4];
+ UniCharCount actualLength;
+ UInt32 currentModifiers = GetCurrentEventKeyModifiers();
+ UInt32 currentModifiersWOAltOrControl = currentModifiers & ~(controlKey | optionKey);
+ int keyAction;
+ switch (ekind) {
+ default:
+ case kEventRawKeyDown:
+ keyAction = kUCKeyActionDown;
+ break;
+ case kEventRawKeyUp:
+ keyAction = kUCKeyActionUp;
+ break;
+ case kEventRawKeyRepeat:
+ keyAction = kUCKeyActionAutoKey;
+ break;
+ }
+ OSStatus err = UCKeyTranslate(uchrData, keyCode, keyAction,
+ ((currentModifiersWOAltOrControl >> 8) & 0xff), LMGetKbdType(),
+ kUCKeyTranslateNoDeadKeysMask, &tmp_unused_state, 4, &actualLength,
+ string);
+ if (err == noErr) {
+ *outChar = QChar(string[0]);
+ *qtKey = qt_mac_get_key(*outModifiers, *outChar, keyCode);
+ if (currentModifiersWOAltOrControl != currentModifiers) {
+ // Now get the real char.
+ err = UCKeyTranslate(uchrData, keyCode, keyAction,
+ ((currentModifiers >> 8) & 0xff), LMGetKbdType(),
+ kUCKeyTranslateNoDeadKeysMask, &tmp_unused_state, 4, &actualLength,
+ string);
+ if (err == noErr)
+ *outChar = QChar(string[0]);
+ }
+ } else {
+ qWarning("Qt::internal::UCKeyTranslate is returnining %ld %s:%d",
+ long(err), __FILE__, __LINE__);
+ }
+ }
+#ifdef Q_OS_MAC32
+ else {
+ // The road less travelled; use KeyTranslate
+ const void *keyboard_layout;
+ KeyboardLayoutRef keyLayoutRef = 0;
+ KLGetCurrentKeyboardLayout(&keyLayoutRef);
+ err = KLGetKeyboardLayoutProperty(keyLayoutRef, kKLKCHRData,
+ reinterpret_cast<const void **>(&keyboard_layout));
+
+ int translatedChar = KeyTranslate(keyboard_layout, (GetCurrentEventKeyModifiers() &
+ (kEventKeyModifierNumLockMask|shiftKey|cmdKey|
+ rightShiftKey|alphaLock)) | keyCode,
+ &tmp_unused_state);
+ if (!translatedChar) {
+#ifdef QT_MAC_USE_COCOA
+ if (outHandled) {
+ qt_mac_eat_unicode_key = false;
+ CallNextEventHandler(er, keyEvent);
+ *outHandled = qt_mac_eat_unicode_key;
+ }
+#endif
+ return false;
+ }
+
+ //map it into qt keys
+ *qtKey = qt_mac_get_key(*outModifiers, QChar(translatedChar), keyCode);
+ if (*outModifiers & (Qt::AltModifier | Qt::ControlModifier)) {
+ if (translatedChar & (1 << 7)) //high ascii
+ translatedChar = 0;
+ } else { //now get the real ascii value
+ UInt32 tmp_mod = 0L;
+ static UInt32 tmp_state = 0L;
+ if (*outModifiers & Qt::ShiftModifier)
+ tmp_mod |= shiftKey;
+ if (*outModifiers & Qt::MetaModifier)
+ tmp_mod |= controlKey;
+ if (*outModifiers & Qt::ControlModifier)
+ tmp_mod |= cmdKey;
+ if (GetCurrentEventKeyModifiers() & alphaLock) //no Qt mapper
+ tmp_mod |= alphaLock;
+ if (*outModifiers & Qt::AltModifier)
+ tmp_mod |= optionKey;
+ if (*outModifiers & Qt::KeypadModifier)
+ tmp_mod |= kEventKeyModifierNumLockMask;
+ translatedChar = KeyTranslate(keyboard_layout, tmp_mod | keyCode, &tmp_state);
+ }
+ {
+ ByteCount unilen = 0;
+ if (GetEventParameter(keyEvent, kEventParamKeyUnicodes, typeUnicodeText, 0, 0, &unilen, 0)
+ == noErr && unilen == 2) {
+ GetEventParameter(keyEvent, kEventParamKeyUnicodes, typeUnicodeText, 0, unilen, 0, outChar);
+ } else if (translatedChar) {
+ static QTextCodec *c = 0;
+ if (!c)
+ c = QTextCodec::codecForName("Apple Roman");
+ char tmpChar = (char)translatedChar; // **sigh**
+ *outChar = c->toUnicode(&tmpChar, 1).at(0);
+ } else {
+ *qtKey = qt_mac_get_key(*outModifiers, QChar(translatedChar), keyCode);
+ }
+ }
+ }
+#endif
+ if (*qtKey == Qt::Key_unknown)
+ *qtKey = qt_mac_get_key(*outModifiers, *outChar, keyCode);
+ return true;
+}
+
+QKeyMapperPrivate::QKeyMapperPrivate()
+{
+ memset(keyLayout, 0, sizeof(keyLayout));
+ keyboard_layout_format.unicode = 0;
+#ifdef Q_OS_MAC32
+ keyboard_mode = NullMode;
+#else
+ currentInputSource = 0;
+#endif
+}
+
+QKeyMapperPrivate::~QKeyMapperPrivate()
+{
+ clearMappings();
+}
+
+bool
+QKeyMapperPrivate::updateKeyboard()
+{
+ const UCKeyboardLayout *uchrData = 0;
+#ifdef Q_OS_MAC32
+ KeyboardLayoutRef keyLayoutRef = 0;
+ KLGetCurrentKeyboardLayout(&keyLayoutRef);
+
+ if (keyboard_mode != NullMode && currentKeyboardLayout == keyLayoutRef)
+ return false;
+
+ OSStatus err;
+ if (keyLayoutRef != 0) {
+ err = KLGetKeyboardLayoutProperty(keyLayoutRef, kKLuchrData,
+ const_cast<const void **>(reinterpret_cast<const void **>(&uchrData)));
+ if (err != noErr) {
+ qWarning("Qt::internal::unable to get unicode keyboardlayout %ld %s:%d",
+ long(err), __FILE__, __LINE__);
+ }
+ }
+#else
+ QCFType<TISInputSourceRef> source = TISCopyCurrentKeyboardInputSource();
+ if (keyboard_mode != NullMode && source == currentInputSource) {
+ return false;
+ }
+ Q_ASSERT(source != 0);
+ CFDataRef data = static_cast<CFDataRef>(TISGetInputSourceProperty(source,
+ kTISPropertyUnicodeKeyLayoutData));
+ uchrData = data ? reinterpret_cast<const UCKeyboardLayout *>(CFDataGetBytePtr(data)) : 0;
+#endif
+
+ keyboard_kind = LMGetKbdType();
+ if (uchrData) {
+ keyboard_layout_format.unicode = uchrData;
+ keyboard_mode = UnicodeMode;
+ }
+#ifdef Q_OS_MAC32
+ else {
+ void *happy;
+ err = KLGetKeyboardLayoutProperty(keyLayoutRef, kKLKCHRData,
+ const_cast<const void **>(reinterpret_cast<void **>(&happy)));
+ if (err != noErr) {
+ qFatal("Qt::internal::unable to get non-unicode layout, cannot procede %ld %s:%d",
+ long(err), __FILE__, __LINE__);
+ }
+ keyboard_layout_format.other = happy;
+ keyboard_mode = OtherMode;
+ }
+
+ currentKeyboardLayout = keyLayoutRef;
+#else
+ currentInputSource = source;
+#endif
+ keyboard_dead = 0;
+ CFStringRef iso639Code;
+#ifdef Q_OS_MAC32
+# ifndef kKLLanguageCode
+# define kKLLanguageCode 9
+# endif
+ KLGetKeyboardLayoutProperty(currentKeyboardLayout, kKLLanguageCode,
+ reinterpret_cast<const void **>(&iso639Code));
+#else
+ CFArrayRef array = static_cast<CFArrayRef>(TISGetInputSourceProperty(currentInputSource, kTISPropertyInputSourceLanguages));
+ iso639Code = static_cast<CFStringRef>(CFArrayGetValueAtIndex(array, 0)); // Actually a RFC3066bis, but it's close enough
+#endif
+ if (iso639Code) {
+ keyboardInputLocale = QLocale(QCFString::toQString(iso639Code));
+ QString monday = keyboardInputLocale.dayName(1);
+ bool rtl = false;
+ for (int i = 0; i < monday.length(); ++i) {
+ switch (monday.at(i).direction()) {
+ default:
+ break;
+ case QChar::DirR:
+ case QChar::DirAL:
+ case QChar::DirRLE:
+ case QChar::DirRLO:
+ rtl = true;
+ break;
+ }
+ if (rtl)
+ break;
+ }
+ keyboardInputDirection = rtl ? Qt::RightToLeft : Qt::LeftToRight;
+ } else {
+ keyboardInputLocale = QLocale::c();
+ keyboardInputDirection = Qt::LeftToRight;
+ }
+ return true;
+}
+
+void
+QKeyMapperPrivate::clearMappings()
+{
+ keyboard_mode = NullMode;
+ for (int i = 0; i < 255; ++i) {
+ if (keyLayout[i]) {
+ delete keyLayout[i];
+ keyLayout[i] = 0;
+ }
+ }
+ updateKeyboard();
+}
+
+QList<int>
+QKeyMapperPrivate::possibleKeys(QKeyEvent *e)
+{
+ QList<int> ret;
+
+ KeyboardLayoutItem *kbItem = keyLayout[e->nativeVirtualKey()];
+ if (!kbItem) // Key is not in any keyboard layout (e.g. eisu-key on Japanese keyboard)
+ return ret;
+
+ int baseKey = kbItem->qtKey[0];
+ Qt::KeyboardModifiers keyMods = e->modifiers();
+ ret << int(baseKey + keyMods); // The base key is _always_ valid, of course
+
+ for (int i = 1; i < 8; ++i) {
+ Qt::KeyboardModifiers neededMods = ModsTbl[i];
+ int key = kbItem->qtKey[i];
+ if (key && key != baseKey && ((keyMods & neededMods) == neededMods))
+ ret << int(key + (keyMods & ~neededMods));
+ }
+
+ return ret;
+}
+
+bool QKeyMapperPrivate::translateKeyEvent(QWidget *widget, EventHandlerCallRef er, EventRef event,
+ void *info, bool grab)
+{
+ Q_ASSERT(GetEventClass(event) == kEventClassKeyboard);
+ bool handled_event=true;
+ UInt32 ekind = GetEventKind(event);
+
+ // unfortunately modifiers changed event looks quite different, so I have a separate
+ // code path
+ if (ekind == kEventRawKeyModifiersChanged) {
+ //figure out changed modifiers, wish Apple would just send a delta
+ UInt32 modifiers = 0;
+ GetEventParameter(event, kEventParamKeyModifiers, typeUInt32, 0,
+ sizeof(modifiers), 0, &modifiers);
+ qt_mac_send_modifiers_changed(modifiers, widget);
+ return true;
+ }
+
+ if (qApp->inputContext() && qApp->inputContext()->isComposing())
+ return false;
+ //get modifiers
+ Qt::KeyboardModifiers modifiers;
+ int qtKey;
+ QChar ourChar;
+ if (translateKeyEventInternal(er, event, &qtKey, &ourChar, &modifiers,
+ &handled_event) == false)
+ return handled_event;
+ QString text(ourChar);
+ /* This is actually wrong - but unfortunatly it is the best that can be
+ done for now because of the Control/Meta mapping problems */
+ if (modifiers & (Qt::ControlModifier | Qt::MetaModifier))
+ text = QString();
+
+
+ if (widget) {
+#ifndef QT_MAC_USE_COCOA
+ Q_UNUSED(info);
+ // Try not to call "other" event handlers if we have a popup,
+ // However, if the key has text
+ // then we should pass it along because otherwise then people
+ // can use input method stuff.
+ if (!qApp->activePopupWidget()
+ || (qApp->activePopupWidget() && !text.isEmpty())) {
+ //Find out if someone else wants the event, namely
+ //is it of use to text services? If so we won't bother
+ //with a QKeyEvent.
+ qt_mac_eat_unicode_key = false;
+ CallNextEventHandler(er, event);
+ extern bool qt_mac_menubar_is_open();
+ if (qt_mac_eat_unicode_key || qt_mac_menubar_is_open()) {
+ return true;
+ }
+ }
+#endif
+ // Try to compress key events.
+ if (!text.isEmpty() && widget->testAttribute(Qt::WA_KeyCompression)) {
+ EventTime lastTime = GetEventTime(event);
+ for (;;) {
+ EventRef releaseEvent = FindSpecificEventInQueue(GetMainEventQueue(),
+ qt_KeyEventComparatorProc,
+ (void*)kEventRawKeyUp);
+ if (!releaseEvent)
+ break;
+ const EventTime releaseTime = GetEventTime(releaseEvent);
+ if (releaseTime < lastTime)
+ break;
+ lastTime = releaseTime;
+
+ EventRef pressEvent = FindSpecificEventInQueue(GetMainEventQueue(),
+ qt_KeyEventComparatorProc,
+ (void*)kEventRawKeyDown);
+ if (!pressEvent)
+ break;
+ const EventTime pressTime = GetEventTime(pressEvent);
+ if (pressTime < lastTime)
+ break;
+ lastTime = pressTime;
+
+ Qt::KeyboardModifiers compressMod;
+ int compressQtKey = 0;
+ QChar compressChar;
+ if (translateKeyEventInternal(er, pressEvent,
+ &compressQtKey, &compressChar, &compressMod, 0)
+ == false) {
+ break;
+ }
+ // Copied from qapplication_x11.cpp (change both).
+
+ bool stopCompression =
+ // 1) misc keys
+ (compressQtKey >= Qt::Key_Escape && compressQtKey <= Qt::Key_SysReq)
+ // 2) cursor movement
+ || (compressQtKey >= Qt::Key_Home && compressQtKey <= Qt::Key_PageDown)
+ // 3) extra keys
+ || (compressQtKey >= Qt::Key_Super_L && compressQtKey <= Qt::Key_Direction_R)
+ // 4) something that a) doesn't translate to text or b) translates
+ // to newline text
+ || (compressQtKey == 0)
+ || (compressChar == QLatin1Char('\n'))
+ || (compressQtKey == Qt::Key_unknown);
+
+ if (compressMod == modifiers && !compressChar.isNull() && !stopCompression) {
+#ifdef DEBUG_KEY_BINDINGS
+ qDebug("compressing away %c", compressChar.toLatin1());
+#endif
+ text += compressChar;
+ // Clean up
+ RemoveEventFromQueue(GetMainEventQueue(), releaseEvent);
+ RemoveEventFromQueue(GetMainEventQueue(), pressEvent);
+ } else {
+#ifdef DEBUG_KEY_BINDINGS
+ qDebug("stoping compression..");
+#endif
+ break;
+ }
+ }
+ }
+
+ // There is no way to get the scan code from carbon. But we cannot use the value 0, since
+ // it indicates that the event originates from somewhere else than the keyboard
+ UInt32 macScanCode = 1;
+ UInt32 macVirtualKey = 0;
+ GetEventParameter(event, kEventParamKeyCode, typeUInt32, 0, sizeof(macVirtualKey), 0, &macVirtualKey);
+ UInt32 macModifiers = 0;
+ GetEventParameter(event, kEventParamKeyModifiers, typeUInt32, 0,
+ sizeof(macModifiers), 0, &macModifiers);
+ handled_event = QKeyMapper::sendKeyEvent(widget, grab,
+ (ekind == kEventRawKeyUp) ? QEvent::KeyRelease : QEvent::KeyPress,
+ qtKey, modifiers, text, ekind == kEventRawKeyRepeat, 0,
+ macScanCode, macVirtualKey, macModifiers
+#ifdef QT_MAC_USE_COCOA
+ ,static_cast<bool *>(info)
+#endif
+ );
+ }
+ return handled_event;
+}
+
+void
+QKeyMapperPrivate::updateKeyMap(EventHandlerCallRef, EventRef event, void *)
+{
+ UInt32 macVirtualKey = 0;
+ GetEventParameter(event, kEventParamKeyCode, typeUInt32, 0, sizeof(macVirtualKey), 0, &macVirtualKey);
+ if (updateKeyboard())
+ QKeyMapper::changeKeyboard();
+ else if (keyLayout[macVirtualKey])
+ return;
+
+ UniCharCount buffer_size = 10;
+ UniChar buffer[buffer_size];
+ keyLayout[macVirtualKey] = new KeyboardLayoutItem;
+ for (int i = 0; i < 16; ++i) {
+ UniCharCount out_buffer_size = 0;
+ keyLayout[macVirtualKey]->qtKey[i] = 0;
+#ifdef Q_WS_MAC32
+ if (keyboard_mode == UnicodeMode) {
+#endif
+ const UInt32 keyModifier = ((qt_mac_get_mac_modifiers(ModsTbl[i]) >> 8) & 0xFF);
+ OSStatus err = UCKeyTranslate(keyboard_layout_format.unicode, macVirtualKey, kUCKeyActionDown, keyModifier,
+ keyboard_kind, 0, &keyboard_dead, buffer_size, &out_buffer_size, buffer);
+ if (err == noErr && out_buffer_size) {
+ const QChar unicode(buffer[0]);
+ int qtkey = qt_mac_get_key(keyModifier, unicode, macVirtualKey);
+ if (qtkey == Qt::Key_unknown)
+ qtkey = unicode.unicode();
+ keyLayout[macVirtualKey]->qtKey[i] = qtkey;
+ }
+#ifdef Q_WS_MAC32
+ } else {
+ const UInt32 keyModifier = (qt_mac_get_mac_modifiers(ModsTbl[i]));
+
+ uchar translatedChar = KeyTranslate(keyboard_layout_format.other, keyModifier | macVirtualKey, &keyboard_dead);
+ if (translatedChar) {
+ static QTextCodec *c = 0;
+ if (!c)
+ c = QTextCodec::codecForName("Apple Roman");
+ const QChar unicode(c->toUnicode((const char *)&translatedChar, 1).at(0));
+ int qtkey = qt_mac_get_key(keyModifier, unicode, macVirtualKey);
+ if (qtkey == Qt::Key_unknown)
+ qtkey = unicode.unicode();
+ keyLayout[macVirtualKey]->qtKey[i] = qtkey;
+ }
+ }
+#endif
+ }
+#ifdef DEBUG_KEY_MAPS
+ qDebug("updateKeyMap for virtual key = 0x%02x!", (uint)macVirtualKey);
+ for (int i = 0; i < 16; ++i) {
+ qDebug(" [%d] (%d,0x%02x,'%c')", i,
+ keyLayout[macVirtualKey]->qtKey[i],
+ keyLayout[macVirtualKey]->qtKey[i],
+ keyLayout[macVirtualKey]->qtKey[i]);
+ }
+#endif
+}
+
+bool
+QKeyMapper::sendKeyEvent(QWidget *widget, bool grab,
+ QEvent::Type type, int code, Qt::KeyboardModifiers modifiers,
+ const QString &text, bool autorepeat, int count,
+ quint32 nativeScanCode, quint32 nativeVirtualKey,
+ quint32 nativeModifiers, bool *isAccepted)
+{
+ Q_UNUSED(count);
+ if (widget && widget->isEnabled()) {
+ bool key_event = true;
+#if defined(QT3_SUPPORT) && !defined(QT_NO_SHORTCUT)
+ if (type == QEvent::KeyPress && !grab
+ && QApplicationPrivate::instance()->use_compat()) {
+ QKeyEventEx accel_ev(type, code, modifiers,
+ text, autorepeat, qMax(1, int(text.length())),
+ nativeScanCode, nativeVirtualKey, nativeModifiers);
+ if (QApplicationPrivate::instance()->qt_tryAccelEvent(widget, &accel_ev)) {
+#if defined(DEBUG_KEY_BINDINGS) || defined(DEBUG_KEY_BINDINGS_MODIFIERS)
+ qDebug("KeyEvent: %s::%s consumed Accel: %s",
+ widget ? widget->metaObject()->className() : "none",
+ widget ? widget->objectName().toLatin1().constData() : "",
+ text.toLatin1().constData());
+#endif
+ key_event = false;
+ } else {
+ if (accel_ev.isAccepted()) {
+#if defined(DEBUG_KEY_BINDINGS) || defined(DEBUG_KEY_BINDINGS_MODIFIERS)
+ qDebug("KeyEvent: %s::%s overrode Accel: %s",
+ widget ? widget->metaObject()->className() : "none",
+ widget ? widget->objectName().toLatin1().constData() : "",
+ text.toLatin1().constData());
+#endif
+ }
+ }
+ }
+#else
+Q_UNUSED(grab);
+#endif // QT3_SUPPORT && !QT_NO_SHORTCUT
+ if (key_event) {
+#if defined(DEBUG_KEY_BINDINGS) || defined(DEBUG_KEY_BINDINGS_MODIFIERS)
+ qDebug("KeyEvent: Sending %s to %s::%s: %s 0x%08x%s",
+ type == QEvent::KeyRelease ? "KeyRelease" : "KeyPress",
+ widget ? widget->metaObject()->className() : "none",
+ widget ? widget->objectName().toLatin1().constData() : "",
+ text.toLatin1().constData(), int(modifiers),
+ autorepeat ? " Repeat" : "");
+#endif
+ QKeyEventEx ke(type, code, modifiers, text, autorepeat, qMax(1, text.length()),
+ nativeScanCode, nativeVirtualKey, nativeModifiers);
+ bool retMe = qt_sendSpontaneousEvent(widget,&ke);
+ if (isAccepted)
+ *isAccepted = ke.isAccepted();
+ return retMe;
+ }
+ }
+ return false;
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/kernel/qkeymapper_p.h b/src/gui/kernel/qkeymapper_p.h
new file mode 100644
index 0000000000..f1d938c170
--- /dev/null
+++ b/src/gui/kernel/qkeymapper_p.h
@@ -0,0 +1,213 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#ifndef QKEYMAPPER_P_H
+#define QKEYMAPPER_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 <qobject.h>
+#include <private/qobject_p.h>
+#include <qkeysequence.h>
+#include <qlist.h>
+#include <qlocale.h>
+#include <qevent.h>
+
+#if defined (Q_WS_MAC64)
+# include <private/qt_mac_p.h>
+#endif
+
+QT_BEGIN_NAMESPACE
+
+class QKeyMapperPrivate;
+class QKeyMapper : public QObject
+{
+ Q_OBJECT
+public:
+ explicit QKeyMapper();
+ ~QKeyMapper();
+
+ static QKeyMapper *instance();
+ static void changeKeyboard();
+ static bool sendKeyEvent(QWidget *widget, bool grab,
+ QEvent::Type type, int code, Qt::KeyboardModifiers modifiers,
+ const QString &text, bool autorepeat, int count,
+ quint32 nativeScanCode, quint32 nativeVirtualKey, quint32 nativeModifiers,
+ bool *unusedExceptForCocoa = 0);
+ static QList<int> possibleKeys(QKeyEvent *e);
+
+private:
+ friend QKeyMapperPrivate *qt_keymapper_private();
+ Q_DECLARE_PRIVATE(QKeyMapper)
+ Q_DISABLE_COPY(QKeyMapper)
+};
+
+
+
+#if defined(Q_OS_WIN)
+enum WindowsNativeModifiers {
+ ShiftLeft = 0x00000001,
+ ControlLeft = 0x00000002,
+ AltLeft = 0x00000004,
+ MetaLeft = 0x00000008,
+ ShiftRight = 0x00000010,
+ ControlRight = 0x00000020,
+ AltRight = 0x00000040,
+ MetaRight = 0x00000080,
+ CapsLock = 0x00000100,
+ NumLock = 0x00000200,
+ ScrollLock = 0x00000400,
+ ExtendedKey = 0x01000000,
+
+ // Convenience mappings
+ ShiftAny = 0x00000011,
+ ControlAny = 0x00000022,
+ AltAny = 0x00000044,
+ MetaAny = 0x00000088,
+ LockAny = 0x00000700
+};
+# if !defined(tagMSG)
+ typedef struct tagMSG MSG;
+# endif
+#elif defined(Q_WS_MAC)
+QT_BEGIN_INCLUDE_NAMESPACE
+# include <private/qt_mac_p.h>
+QT_END_INCLUDE_NAMESPACE
+#elif defined(Q_WS_X11)
+
+QT_BEGIN_INCLUDE_NAMESPACE
+typedef ulong XID;
+typedef XID KeySym;
+QT_END_INCLUDE_NAMESPACE
+
+struct QXCoreDesc {
+ int min_keycode;
+ int max_keycode;
+ int keysyms_per_keycode;
+ KeySym *keysyms;
+ uchar mode_switch;
+ uchar num_lock;
+ KeySym lock_meaning;
+};
+
+#endif
+
+struct KeyboardLayoutItem;
+typedef struct __TISInputSource * TISInputSourceRef;
+class QKeyEvent;
+class QKeyMapperPrivate : public QObjectPrivate
+{
+ Q_DECLARE_PUBLIC(QKeyMapper)
+public:
+ QKeyMapperPrivate();
+ ~QKeyMapperPrivate();
+
+ void clearMappings();
+ QList<int> possibleKeys(QKeyEvent *e);
+
+ QLocale keyboardInputLocale;
+ Qt::LayoutDirection keyboardInputDirection;
+
+#if defined(Q_OS_WIN)
+ void clearRecordedKeys();
+ void updateKeyMap(const MSG &msg);
+ bool translateKeyEvent(QWidget *receiver, const MSG &msg, bool grab);
+ void updatePossibleKeyCodes(unsigned char *kbdBuffer, quint32 scancode, quint32 vk_key);
+ bool isADeadKey(unsigned int vk_key, unsigned int modifiers);
+
+ KeyboardLayoutItem *keyLayout[256];
+
+#elif defined(Q_WS_X11)
+
+ QList<int> possibleKeysXKB(QKeyEvent *event);
+ QList<int> possibleKeysCore(QKeyEvent *event);
+
+ bool translateKeyEventInternal(QWidget *keywidget,
+ const XEvent *,
+ KeySym &keysym,
+ int& count,
+ QString& text,
+ Qt::KeyboardModifiers& modifiers,
+ int &code,
+ QEvent::Type &type,
+ bool statefulTranslation = true);
+ bool translateKeyEvent(QWidget *keywidget,
+ const XEvent *,
+ bool grab);
+
+ bool useXKB;
+ QXCoreDesc coreDesc;
+
+#elif defined(Q_WS_MAC)
+ bool updateKeyboard();
+ void updateKeyMap(EventHandlerCallRef, EventRef, void *);
+ bool translateKeyEvent(QWidget *, EventHandlerCallRef, EventRef, void *, bool);
+
+ enum { NullMode, UnicodeMode, OtherMode } keyboard_mode;
+ union {
+ const UCKeyboardLayout *unicode;
+ void *other;
+ } keyboard_layout_format;
+#ifdef Q_WS_MAC64
+ QCFType<TISInputSourceRef> currentInputSource;
+#else
+ KeyboardLayoutRef currentKeyboardLayout;
+#endif
+ KeyboardLayoutKind keyboard_kind;
+ UInt32 keyboard_dead;
+ KeyboardLayoutItem *keyLayout[256];
+#elif defined(Q_WS_QWS)
+#endif
+};
+
+QKeyMapperPrivate *qt_keymapper_private(); // from qkeymapper.cpp
+
+QT_END_NAMESPACE
+
+#endif // QKEYMAPPER_P_H
diff --git a/src/gui/kernel/qkeymapper_qws.cpp b/src/gui/kernel/qkeymapper_qws.cpp
new file mode 100644
index 0000000000..4bd18af7af
--- /dev/null
+++ b/src/gui/kernel/qkeymapper_qws.cpp
@@ -0,0 +1,77 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qkeymapper_p.h"
+#include <qdebug.h>
+#include <private/qevent_p.h>
+#include <private/qlocale_p.h>
+
+QT_BEGIN_NAMESPACE
+
+QT_USE_NAMESPACE
+
+
+QKeyMapperPrivate::QKeyMapperPrivate()
+{
+ keyboardInputLocale = QLocale::system();
+ keyboardInputDirection = Qt::RightToLeft;
+}
+
+QKeyMapperPrivate::~QKeyMapperPrivate()
+{
+ // clearMappings();
+}
+
+void QKeyMapperPrivate::clearMappings()
+{
+}
+
+QList<int> QKeyMapperPrivate::possibleKeys(QKeyEvent *e)
+{
+ QList<int> result;
+ if (e->key() && (e->key() != Qt::Key_unknown))
+ result << int(e->key() + e->modifiers());
+ else if (!e->text().isEmpty())
+ result << int(e->text().at(0).unicode() + e->modifiers());
+ return result;
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/kernel/qkeymapper_win.cpp b/src/gui/kernel/qkeymapper_win.cpp
new file mode 100644
index 0000000000..5f840442f7
--- /dev/null
+++ b/src/gui/kernel/qkeymapper_win.cpp
@@ -0,0 +1,1259 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qkeymapper_p.h"
+
+#include <windows.h>
+#include <qdebug.h>
+#include <private/qevent_p.h>
+#include <private/qlocale_p.h>
+#include <private/qapplication_p.h>
+#include <qwidget.h>
+#include <qapplication.h>
+#include <ctype.h>
+
+QT_BEGIN_NAMESPACE
+
+// Uncommend, to show debugging information for the keymapper
+//#define DEBUG_KEYMAPPER
+
+// Implemented elsewhere
+extern "C" LRESULT CALLBACK QtWndProc(HWND, UINT, WPARAM, LPARAM);
+Q_CORE_EXPORT bool winPostMessage(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
+Q_CORE_EXPORT bool winPeekMessage(MSG* msg, HWND hWnd, UINT wMsgFilterMin, UINT wMsgFilterMax,
+ UINT wRemoveMsg);
+extern Q_CORE_EXPORT QLocale qt_localeFromLCID(LCID id);
+#ifndef LANG_PASHTO
+#define LANG_PASHTO 0x63
+#endif
+#ifndef LANG_SYRIAC
+#define LANG_SYRIAC 0x5a
+#endif
+#ifndef LANG_DIVEHI
+#define LANG_DIVEHI 0x65
+#endif
+#ifndef VK_OEM_PLUS
+#define VK_OEM_PLUS 0xBB
+#endif
+#ifndef VK_OEM_3
+#define VK_OEM_3 0xC0
+#endif
+
+#if defined(Q_OS_WINCE)
+bool GetKeyboardState(unsigned char* kbuffer)
+{
+ for (int i=0; i< 256; ++i)
+ kbuffer[i] = GetAsyncKeyState(i);
+ return true;
+}
+#endif
+// Key recorder ------------------------------------------------------------------------[ start ] --
+struct KeyRecord {
+ KeyRecord(int c, int a, int s, const QString &t) : code(c), ascii(a), state(s), text(t) {}
+ KeyRecord() {}
+
+ int code;
+ int ascii;
+ int state;
+ QString text;
+};
+
+static const int QT_MAX_KEY_RECORDINGS = 64; // User has LOTS of fingers...
+struct KeyRecorder
+{
+ KeyRecorder() : nrecs(0) {}
+
+ inline KeyRecord *findKey(int code, bool remove);
+ inline void storeKey(int code, int ascii, int state, const QString& text);
+ inline void clearKeys();
+
+ int nrecs;
+ KeyRecord deleted_record; // A copy of last entry removed from records[]
+ KeyRecord records[QT_MAX_KEY_RECORDINGS];
+};
+static KeyRecorder key_recorder;
+
+KeyRecord *KeyRecorder::findKey(int code, bool remove)
+{
+ KeyRecord *result = 0;
+ for (int i = 0; i < nrecs; ++i) {
+ if (records[i].code == code) {
+ if (remove) {
+ deleted_record = records[i];
+ // Move rest down, and decrease count
+ while (i + 1 < nrecs) {
+ records[i] = records[i + 1];
+ ++i;
+ }
+ --nrecs;
+ result = &deleted_record;
+ } else {
+ result = &records[i];
+ }
+ break;
+ }
+ }
+ return result;
+}
+
+void KeyRecorder::storeKey(int code, int ascii, int state, const QString& text)
+{
+ Q_ASSERT_X(nrecs != QT_MAX_KEY_RECORDINGS,
+ "Internal KeyRecorder",
+ "Keyboard recorder buffer overflow, consider increasing QT_MAX_KEY_RECORDINGS");
+
+ if (nrecs == QT_MAX_KEY_RECORDINGS) {
+ qWarning("Qt: Internal keyboard buffer overflow");
+ return;
+ }
+ records[nrecs++] = KeyRecord(code,ascii,state,text);
+}
+
+void KeyRecorder::clearKeys()
+{
+ nrecs = 0;
+}
+// Key recorder --------------------------------------------------------------------------[ end ] --
+
+
+// Key translation ---------------------------------------------------------------------[ start ] --
+// Meaning of values:
+// 0 = Character output key, needs keyboard driver mapping
+// Key_unknown = Unknown Virtual Key, no translation possible, ignore
+static const uint KeyTbl[] = { // Keyboard mapping table
+ // Dec | Hex | Windows Virtual key
+ Qt::Key_unknown, // 0 0x00
+ Qt::Key_unknown, // 1 0x01 VK_LBUTTON | Left mouse button
+ Qt::Key_unknown, // 2 0x02 VK_RBUTTON | Right mouse button
+ Qt::Key_Cancel, // 3 0x03 VK_CANCEL | Control-Break processing
+ Qt::Key_unknown, // 4 0x04 VK_MBUTTON | Middle mouse button
+ Qt::Key_unknown, // 5 0x05 VK_XBUTTON1 | X1 mouse button
+ Qt::Key_unknown, // 6 0x06 VK_XBUTTON2 | X2 mouse button
+ Qt::Key_unknown, // 7 0x07 -- unassigned --
+ Qt::Key_Backspace, // 8 0x08 VK_BACK | BackSpace key
+ Qt::Key_Tab, // 9 0x09 VK_TAB | Tab key
+ Qt::Key_unknown, // 10 0x0A -- reserved --
+ Qt::Key_unknown, // 11 0x0B -- reserved --
+ Qt::Key_Clear, // 12 0x0C VK_CLEAR | Clear key
+ Qt::Key_Return, // 13 0x0D VK_RETURN | Enter key
+ Qt::Key_unknown, // 14 0x0E -- unassigned --
+ Qt::Key_unknown, // 15 0x0F -- unassigned --
+ Qt::Key_Shift, // 16 0x10 VK_SHIFT | Shift key
+ Qt::Key_Control, // 17 0x11 VK_CONTROL | Ctrl key
+ Qt::Key_Alt, // 18 0x12 VK_MENU | Alt key
+ Qt::Key_Pause, // 19 0x13 VK_PAUSE | Pause key
+ Qt::Key_CapsLock, // 20 0x14 VK_CAPITAL | Caps-Lock
+ Qt::Key_unknown, // 21 0x15 VK_KANA / VK_HANGUL | IME Kana or Hangul mode
+ Qt::Key_unknown, // 22 0x16 -- unassigned --
+ Qt::Key_unknown, // 23 0x17 VK_JUNJA | IME Junja mode
+ Qt::Key_unknown, // 24 0x18 VK_FINAL | IME final mode
+ Qt::Key_unknown, // 25 0x19 VK_HANJA / VK_KANJI | IME Hanja or Kanji mode
+ Qt::Key_unknown, // 26 0x1A -- unassigned --
+ Qt::Key_Escape, // 27 0x1B VK_ESCAPE | Esc key
+ Qt::Key_unknown, // 28 0x1C VK_CONVERT | IME convert
+ Qt::Key_unknown, // 29 0x1D VK_NONCONVERT | IME non-convert
+ Qt::Key_unknown, // 30 0x1E VK_ACCEPT | IME accept
+ Qt::Key_Mode_switch,// 31 0x1F VK_MODECHANGE | IME mode change request
+ Qt::Key_Space, // 32 0x20 VK_SPACE | Spacebar
+ Qt::Key_PageUp, // 33 0x21 VK_PRIOR | Page Up key
+ Qt::Key_PageDown, // 34 0x22 VK_NEXT | Page Down key
+ Qt::Key_End, // 35 0x23 VK_END | End key
+ Qt::Key_Home, // 36 0x24 VK_HOME | Home key
+ Qt::Key_Left, // 37 0x25 VK_LEFT | Left arrow key
+ Qt::Key_Up, // 38 0x26 VK_UP | Up arrow key
+ Qt::Key_Right, // 39 0x27 VK_RIGHT | Right arrow key
+ Qt::Key_Down, // 40 0x28 VK_DOWN | Down arrow key
+ Qt::Key_Select, // 41 0x29 VK_SELECT | Select key
+ Qt::Key_Printer, // 42 0x2A VK_PRINT | Print key
+ Qt::Key_Execute, // 43 0x2B VK_EXECUTE | Execute key
+ Qt::Key_Print, // 44 0x2C VK_SNAPSHOT | Print Screen key
+ Qt::Key_Insert, // 45 0x2D VK_INSERT | Ins key
+ Qt::Key_Delete, // 46 0x2E VK_DELETE | Del key
+ Qt::Key_Help, // 47 0x2F VK_HELP | Help key
+ 0, // 48 0x30 (VK_0) | 0 key
+ 0, // 49 0x31 (VK_1) | 1 key
+ 0, // 50 0x32 (VK_2) | 2 key
+ 0, // 51 0x33 (VK_3) | 3 key
+ 0, // 52 0x34 (VK_4) | 4 key
+ 0, // 53 0x35 (VK_5) | 5 key
+ 0, // 54 0x36 (VK_6) | 6 key
+ 0, // 55 0x37 (VK_7) | 7 key
+ 0, // 56 0x38 (VK_8) | 8 key
+ 0, // 57 0x39 (VK_9) | 9 key
+ Qt::Key_unknown, // 58 0x3A -- unassigned --
+ Qt::Key_unknown, // 59 0x3B -- unassigned --
+ Qt::Key_unknown, // 60 0x3C -- unassigned --
+ Qt::Key_unknown, // 61 0x3D -- unassigned --
+ Qt::Key_unknown, // 62 0x3E -- unassigned --
+ Qt::Key_unknown, // 63 0x3F -- unassigned --
+ Qt::Key_unknown, // 64 0x40 -- unassigned --
+ 0, // 65 0x41 (VK_A) | A key
+ 0, // 66 0x42 (VK_B) | B key
+ 0, // 67 0x43 (VK_C) | C key
+ 0, // 68 0x44 (VK_D) | D key
+ 0, // 69 0x45 (VK_E) | E key
+ 0, // 70 0x46 (VK_F) | F key
+ 0, // 71 0x47 (VK_G) | G key
+ 0, // 72 0x48 (VK_H) | H key
+ 0, // 73 0x49 (VK_I) | I key
+ 0, // 74 0x4A (VK_J) | J key
+ 0, // 75 0x4B (VK_K) | K key
+ 0, // 76 0x4C (VK_L) | L key
+ 0, // 77 0x4D (VK_M) | M key
+ 0, // 78 0x4E (VK_N) | N key
+ 0, // 79 0x4F (VK_O) | O key
+ 0, // 80 0x50 (VK_P) | P key
+ 0, // 81 0x51 (VK_Q) | Q key
+ 0, // 82 0x52 (VK_R) | R key
+ 0, // 83 0x53 (VK_S) | S key
+ 0, // 84 0x54 (VK_T) | T key
+ 0, // 85 0x55 (VK_U) | U key
+ 0, // 86 0x56 (VK_V) | V key
+ 0, // 87 0x57 (VK_W) | W key
+ 0, // 88 0x58 (VK_X) | X key
+ 0, // 89 0x59 (VK_Y) | Y key
+ 0, // 90 0x5A (VK_Z) | Z key
+ Qt::Key_Meta, // 91 0x5B VK_LWIN | Left Windows - MS Natural kbd
+ Qt::Key_Meta, // 92 0x5C VK_RWIN | Right Windows - MS Natural kbd
+ Qt::Key_Menu, // 93 0x5D VK_APPS | Application key-MS Natural kbd
+ Qt::Key_unknown, // 94 0x5E -- reserved --
+ Qt::Key_Sleep, // 95 0x5F VK_SLEEP
+ Qt::Key_0, // 96 0x60 VK_NUMPAD0 | Numeric keypad 0 key
+ Qt::Key_1, // 97 0x61 VK_NUMPAD1 | Numeric keypad 1 key
+ Qt::Key_2, // 98 0x62 VK_NUMPAD2 | Numeric keypad 2 key
+ Qt::Key_3, // 99 0x63 VK_NUMPAD3 | Numeric keypad 3 key
+ Qt::Key_4, // 100 0x64 VK_NUMPAD4 | Numeric keypad 4 key
+ Qt::Key_5, // 101 0x65 VK_NUMPAD5 | Numeric keypad 5 key
+ Qt::Key_6, // 102 0x66 VK_NUMPAD6 | Numeric keypad 6 key
+ Qt::Key_7, // 103 0x67 VK_NUMPAD7 | Numeric keypad 7 key
+ Qt::Key_8, // 104 0x68 VK_NUMPAD8 | Numeric keypad 8 key
+ Qt::Key_9, // 105 0x69 VK_NUMPAD9 | Numeric keypad 9 key
+ Qt::Key_Asterisk, // 106 0x6A VK_MULTIPLY | Multiply key
+ Qt::Key_Plus, // 107 0x6B VK_ADD | Add key
+ Qt::Key_Comma, // 108 0x6C VK_SEPARATOR | Separator key
+ Qt::Key_Minus, // 109 0x6D VK_SUBTRACT | Subtract key
+ Qt::Key_Period, // 110 0x6E VK_DECIMAL | Decimal key
+ Qt::Key_Slash, // 111 0x6F VK_DIVIDE | Divide key
+ Qt::Key_F1, // 112 0x70 VK_F1 | F1 key
+ Qt::Key_F2, // 113 0x71 VK_F2 | F2 key
+ Qt::Key_F3, // 114 0x72 VK_F3 | F3 key
+ Qt::Key_F4, // 115 0x73 VK_F4 | F4 key
+ Qt::Key_F5, // 116 0x74 VK_F5 | F5 key
+ Qt::Key_F6, // 117 0x75 VK_F6 | F6 key
+ Qt::Key_F7, // 118 0x76 VK_F7 | F7 key
+ Qt::Key_F8, // 119 0x77 VK_F8 | F8 key
+ Qt::Key_F9, // 120 0x78 VK_F9 | F9 key
+ Qt::Key_F10, // 121 0x79 VK_F10 | F10 key
+ Qt::Key_F11, // 122 0x7A VK_F11 | F11 key
+ Qt::Key_F12, // 123 0x7B VK_F12 | F12 key
+ Qt::Key_F13, // 124 0x7C VK_F13 | F13 key
+ Qt::Key_F14, // 125 0x7D VK_F14 | F14 key
+ Qt::Key_F15, // 126 0x7E VK_F15 | F15 key
+ Qt::Key_F16, // 127 0x7F VK_F16 | F16 key
+ Qt::Key_F17, // 128 0x80 VK_F17 | F17 key
+ Qt::Key_F18, // 129 0x81 VK_F18 | F18 key
+ Qt::Key_F19, // 130 0x82 VK_F19 | F19 key
+ Qt::Key_F20, // 131 0x83 VK_F20 | F20 key
+ Qt::Key_F21, // 132 0x84 VK_F21 | F21 key
+ Qt::Key_F22, // 133 0x85 VK_F22 | F22 key
+ Qt::Key_F23, // 134 0x86 VK_F23 | F23 key
+ Qt::Key_F24, // 135 0x87 VK_F24 | F24 key
+ Qt::Key_unknown, // 136 0x88 -- unassigned --
+ Qt::Key_unknown, // 137 0x89 -- unassigned --
+ Qt::Key_unknown, // 138 0x8A -- unassigned --
+ Qt::Key_unknown, // 139 0x8B -- unassigned --
+ Qt::Key_unknown, // 140 0x8C -- unassigned --
+ Qt::Key_unknown, // 141 0x8D -- unassigned --
+ Qt::Key_unknown, // 142 0x8E -- unassigned --
+ Qt::Key_unknown, // 143 0x8F -- unassigned --
+ Qt::Key_NumLock, // 144 0x90 VK_NUMLOCK | Num Lock key
+ Qt::Key_ScrollLock, // 145 0x91 VK_SCROLL | Scroll Lock key
+ // Fujitsu/OASYS kbd --------------------
+ 0, //Qt::Key_Jisho, // 146 0x92 VK_OEM_FJ_JISHO | 'Dictionary' key /
+ // VK_OEM_NEC_EQUAL = key on numpad on NEC PC-9800 kbd
+ Qt::Key_Massyo, // 147 0x93 VK_OEM_FJ_MASSHOU | 'Unregister word' key
+ Qt::Key_Touroku, // 148 0x94 VK_OEM_FJ_TOUROKU | 'Register word' key
+ 0, //Qt::Key_Oyayubi_Left,//149 0x95 VK_OEM_FJ_LOYA | 'Left OYAYUBI' key
+ 0, //Qt::Key_Oyayubi_Right,//150 0x96 VK_OEM_FJ_ROYA | 'Right OYAYUBI' key
+ Qt::Key_unknown, // 151 0x97 -- unassigned --
+ Qt::Key_unknown, // 152 0x98 -- unassigned --
+ Qt::Key_unknown, // 153 0x99 -- unassigned --
+ Qt::Key_unknown, // 154 0x9A -- unassigned --
+ Qt::Key_unknown, // 155 0x9B -- unassigned --
+ Qt::Key_unknown, // 156 0x9C -- unassigned --
+ Qt::Key_unknown, // 157 0x9D -- unassigned --
+ Qt::Key_unknown, // 158 0x9E -- unassigned --
+ Qt::Key_unknown, // 159 0x9F -- unassigned --
+ Qt::Key_Shift, // 160 0xA0 VK_LSHIFT | Left Shift key
+ Qt::Key_Shift, // 161 0xA1 VK_RSHIFT | Right Shift key
+ Qt::Key_Control, // 162 0xA2 VK_LCONTROL | Left Ctrl key
+ Qt::Key_Control, // 163 0xA3 VK_RCONTROL | Right Ctrl key
+ Qt::Key_Alt, // 164 0xA4 VK_LMENU | Left Menu key
+ Qt::Key_Alt, // 165 0xA5 VK_RMENU | Right Menu key
+ Qt::Key_Back, // 166 0xA6 VK_BROWSER_BACK | Browser Back key
+ Qt::Key_Forward, // 167 0xA7 VK_BROWSER_FORWARD | Browser Forward key
+ Qt::Key_Refresh, // 168 0xA8 VK_BROWSER_REFRESH | Browser Refresh key
+ Qt::Key_Stop, // 169 0xA9 VK_BROWSER_STOP | Browser Stop key
+ Qt::Key_Search, // 170 0xAA VK_BROWSER_SEARCH | Browser Search key
+ Qt::Key_Favorites, // 171 0xAB VK_BROWSER_FAVORITES| Browser Favorites key
+ Qt::Key_HomePage, // 172 0xAC VK_BROWSER_HOME | Browser Start and Home key
+ Qt::Key_VolumeMute, // 173 0xAD VK_VOLUME_MUTE | Volume Mute key
+ Qt::Key_VolumeDown, // 174 0xAE VK_VOLUME_DOWN | Volume Down key
+ Qt::Key_VolumeUp, // 175 0xAF VK_VOLUME_UP | Volume Up key
+ Qt::Key_MediaNext, // 176 0xB0 VK_MEDIA_NEXT_TRACK | Next Track key
+ Qt::Key_MediaPrevious, //177 0xB1 VK_MEDIA_PREV_TRACK | Previous Track key
+ Qt::Key_MediaStop, // 178 0xB2 VK_MEDIA_STOP | Stop Media key
+ Qt::Key_MediaPlay, // 179 0xB3 VK_MEDIA_PLAY_PAUSE | Play/Pause Media key
+ Qt::Key_LaunchMail, // 180 0xB4 VK_LAUNCH_MAIL | Start Mail key
+ Qt::Key_LaunchMedia,// 181 0xB5 VK_LAUNCH_MEDIA_SELECT Select Media key
+ Qt::Key_Launch0, // 182 0xB6 VK_LAUNCH_APP1 | Start Application 1 key
+ Qt::Key_Launch1, // 183 0xB7 VK_LAUNCH_APP2 | Start Application 2 key
+ Qt::Key_unknown, // 184 0xB8 -- reserved --
+ Qt::Key_unknown, // 185 0xB9 -- reserved --
+ 0, // 186 0xBA VK_OEM_1 | ';:' for US
+ 0, // 187 0xBB VK_OEM_PLUS | '+' any country
+ 0, // 188 0xBC VK_OEM_COMMA | ',' any country
+ 0, // 189 0xBD VK_OEM_MINUS | '-' any country
+ 0, // 190 0xBE VK_OEM_PERIOD | '.' any country
+ 0, // 191 0xBF VK_OEM_2 | '/?' for US
+ 0, // 192 0xC0 VK_OEM_3 | '`~' for US
+ Qt::Key_unknown, // 193 0xC1 -- reserved --
+ Qt::Key_unknown, // 194 0xC2 -- reserved --
+ Qt::Key_unknown, // 195 0xC3 -- reserved --
+ Qt::Key_unknown, // 196 0xC4 -- reserved --
+ Qt::Key_unknown, // 197 0xC5 -- reserved --
+ Qt::Key_unknown, // 198 0xC6 -- reserved --
+ Qt::Key_unknown, // 199 0xC7 -- reserved --
+ Qt::Key_unknown, // 200 0xC8 -- reserved --
+ Qt::Key_unknown, // 201 0xC9 -- reserved --
+ Qt::Key_unknown, // 202 0xCA -- reserved --
+ Qt::Key_unknown, // 203 0xCB -- reserved --
+ Qt::Key_unknown, // 204 0xCC -- reserved --
+ Qt::Key_unknown, // 205 0xCD -- reserved --
+ Qt::Key_unknown, // 206 0xCE -- reserved --
+ Qt::Key_unknown, // 207 0xCF -- reserved --
+ Qt::Key_unknown, // 208 0xD0 -- reserved --
+ Qt::Key_unknown, // 209 0xD1 -- reserved --
+ Qt::Key_unknown, // 210 0xD2 -- reserved --
+ Qt::Key_unknown, // 211 0xD3 -- reserved --
+ Qt::Key_unknown, // 212 0xD4 -- reserved --
+ Qt::Key_unknown, // 213 0xD5 -- reserved --
+ Qt::Key_unknown, // 214 0xD6 -- reserved --
+ Qt::Key_unknown, // 215 0xD7 -- reserved --
+ Qt::Key_unknown, // 216 0xD8 -- unassigned --
+ Qt::Key_unknown, // 217 0xD9 -- unassigned --
+ Qt::Key_unknown, // 218 0xDA -- unassigned --
+ 0, // 219 0xDB VK_OEM_4 | '[{' for US
+ 0, // 220 0xDC VK_OEM_5 | '\|' for US
+ 0, // 221 0xDD VK_OEM_6 | ']}' for US
+ 0, // 222 0xDE VK_OEM_7 | ''"' for US
+ 0, // 223 0xDF VK_OEM_8
+ Qt::Key_unknown, // 224 0xE0 -- reserved --
+ Qt::Key_unknown, // 225 0xE1 VK_OEM_AX | 'AX' key on Japanese AX kbd
+ Qt::Key_unknown, // 226 0xE2 VK_OEM_102 | "<>" or "\|" on RT 102-key kbd
+ Qt::Key_unknown, // 227 0xE3 VK_ICO_HELP | Help key on ICO
+ Qt::Key_unknown, // 228 0xE4 VK_ICO_00 | 00 key on ICO
+ Qt::Key_unknown, // 229 0xE5 VK_PROCESSKEY | IME Process key
+ Qt::Key_unknown, // 230 0xE6 VK_ICO_CLEAR |
+ Qt::Key_unknown, // 231 0xE7 VK_PACKET | Unicode char as keystrokes
+ Qt::Key_unknown, // 232 0xE8 -- unassigned --
+ // Nokia/Ericsson definitions ---------------
+ Qt::Key_unknown, // 233 0xE9 VK_OEM_RESET
+ Qt::Key_unknown, // 234 0xEA VK_OEM_JUMP
+ Qt::Key_unknown, // 235 0xEB VK_OEM_PA1
+ Qt::Key_unknown, // 236 0xEC VK_OEM_PA2
+ Qt::Key_unknown, // 237 0xED VK_OEM_PA3
+ Qt::Key_unknown, // 238 0xEE VK_OEM_WSCTRL
+ Qt::Key_unknown, // 239 0xEF VK_OEM_CUSEL
+ Qt::Key_unknown, // 240 0xF0 VK_OEM_ATTN
+ Qt::Key_unknown, // 241 0xF1 VK_OEM_FINISH
+ Qt::Key_unknown, // 242 0xF2 VK_OEM_COPY
+ Qt::Key_unknown, // 243 0xF3 VK_OEM_AUTO
+ Qt::Key_unknown, // 244 0xF4 VK_OEM_ENLW
+ Qt::Key_unknown, // 245 0xF5 VK_OEM_BACKTAB
+ Qt::Key_unknown, // 246 0xF6 VK_ATTN | Attn key
+ Qt::Key_unknown, // 247 0xF7 VK_CRSEL | CrSel key
+ Qt::Key_unknown, // 248 0xF8 VK_EXSEL | ExSel key
+ Qt::Key_unknown, // 249 0xF9 VK_EREOF | Erase EOF key
+ Qt::Key_Play, // 250 0xFA VK_PLAY | Play key
+ Qt::Key_Zoom, // 251 0xFB VK_ZOOM | Zoom key
+ Qt::Key_unknown, // 252 0xFC VK_NONAME | Reserved
+ Qt::Key_unknown, // 253 0xFD VK_PA1 | PA1 key
+ Qt::Key_Clear, // 254 0xFE VK_OEM_CLEAR | Clear key
+ 0
+};
+
+// Possible modifier states.
+// NOTE: The order of these states match the order in QKeyMapperPrivate::updatePossibleKeyCodes()!
+static const Qt::KeyboardModifiers ModsTbl[] = {
+ Qt::NoModifier, // 0
+ Qt::ShiftModifier, // 1
+ Qt::ControlModifier, // 2
+ Qt::ControlModifier | Qt::ShiftModifier, // 3
+ Qt::AltModifier, // 4
+ Qt::AltModifier | Qt::ShiftModifier, // 5
+ Qt::AltModifier | Qt::ControlModifier, // 6
+ Qt::AltModifier | Qt::ShiftModifier | Qt::ControlModifier, // 7
+ Qt::NoModifier, // Fall-back to raw Key_*
+};
+
+#if defined(Q_OS_WINCE)
+ // Use the KeyTbl to resolve a Qt::Key out of the virtual keys.
+ // In case it is not resolvable, continue using the virtual key itself.
+
+QT_BEGIN_INCLUDE_NAMESPACE
+
+int ToUnicode(UINT vk, int /*scancode*/, unsigned char* /*kbdBuffer*/, LPWSTR unicodeBuffer, int, int)
+{
+ QT_USE_NAMESPACE
+ QChar* buf = reinterpret_cast< QChar*>(unicodeBuffer);
+ if (KeyTbl[vk] == 0) {
+ buf[0] = vk;
+ return 1;
+ }
+ return 0;
+}
+
+int ToAscii(UINT vk, int scancode, unsigned char *kbdBuffer, LPWORD unicodeBuffer, int flag)
+{
+ return ToUnicode(vk, scancode, kbdBuffer, (LPWSTR) unicodeBuffer, 0, flag);
+
+}
+QT_END_INCLUDE_NAMESPACE
+
+#endif
+
+// Translate a VK into a Qt key code, or unicode character
+static inline int toKeyOrUnicode(int vk, int scancode, unsigned char *kbdBuffer, bool *isDeadkey = 0)
+{
+ Q_ASSERT(vk > 0 && vk < 256);
+ int code = 0;
+ QChar unicodeBuffer[5];
+ int res = 0;
+ if (QSysInfo::WindowsVersion < QSysInfo::WV_NT)
+ res = ToAscii(vk, scancode, kbdBuffer, reinterpret_cast<LPWORD>(unicodeBuffer), 0);
+ else
+ res = ToUnicode(vk, scancode, kbdBuffer, reinterpret_cast<LPWSTR>(unicodeBuffer), 5, 0);
+
+ if (res)
+ code = unicodeBuffer[0].toUpper().unicode();
+
+ // Qt::Key_*'s are not encoded below 0x20, so try again, and DEL keys (0x7f) is encoded with a
+ // proper Qt::Key_ code
+ if (code < 0x20 || code == 0x7f) // Handles res==0 too
+ code = KeyTbl[vk];
+
+ if (isDeadkey)
+ *isDeadkey = (res == -1);
+
+ return code == Qt::Key_unknown ? 0 : code;
+}
+
+Q_GUI_EXPORT int qt_translateKeyCode(int vk)
+{
+ int code = (vk < 0 || vk > 255) ? 0 : KeyTbl[vk];
+ return code == Qt::Key_unknown ? 0 : code;
+}
+
+static inline int asciiToKeycode(char a, int state)
+{
+ if (a >= 'a' && a <= 'z')
+ a = toupper(a);
+ if ((state & Qt::ControlModifier) != 0) {
+ if (a >= 0 && a <= 31) // Ctrl+@..Ctrl+A..CTRL+Z..Ctrl+_
+ a += '@'; // to @..A..Z.._
+ }
+ return a & 0xff;
+}
+
+static int inputcharset = CP_ACP;
+static inline QChar wmchar_to_unicode(DWORD c)
+{
+ // qt_winMB2QString is the generalization of this function.
+ QT_WA({
+ return QChar((ushort)c);
+ } , {
+ char mb[2];
+ mb[0] = c & 0xff;
+ mb[1] = 0;
+ WCHAR wc[1];
+ MultiByteToWideChar(inputcharset, MB_PRECOMPOSED, mb, -1, wc, 1);
+ return QChar(wc[0]);
+ });
+}
+
+static inline QChar imechar_to_unicode(DWORD c)
+{
+ // qt_winMB2QString is the generalization of this function.
+ QT_WA({
+ return QChar((ushort)c);
+ } , {
+ char mb[3];
+ mb[0] = (c >> 8) & 0xff;
+ mb[1] = c & 0xff;
+ mb[2] = 0;
+ WCHAR wc[1];
+ MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, mb, -1, wc, 1);
+ return QChar(wc[0]);
+ });
+}
+
+static inline bool isModifierKey(int code)
+{
+ return (code >= Qt::Key_Shift) && (code <= Qt::Key_ScrollLock);
+}
+// Key translation -----------------------------------------------------------------------[ end ]---
+
+
+static void qt_show_system_menu(QWidget* tlw)
+{
+ Q_ASSERT(tlw->testAttribute(Qt::WA_WState_Created));
+ HMENU menu = GetSystemMenu(tlw->internalWinId(), FALSE);
+ if (!menu)
+ return; // no menu for this window
+
+#define enabled (MF_BYCOMMAND | MF_ENABLED)
+#define disabled (MF_BYCOMMAND | MF_GRAYED)
+
+#ifndef Q_OS_WINCE
+ EnableMenuItem(menu, SC_MINIMIZE, (tlw->windowFlags() & Qt::WindowMinimizeButtonHint)?enabled:disabled);
+ bool maximized = IsZoomed(tlw->internalWinId());
+
+ EnableMenuItem(menu, SC_MAXIMIZE, ! (tlw->windowFlags() & Qt::WindowMaximizeButtonHint) || maximized?disabled:enabled);
+ EnableMenuItem(menu, SC_RESTORE, maximized?enabled:disabled);
+
+ // We should _not_ check with the setFixedSize(x,y) case here, since Windows is not able to check
+ // this and our menu here would be out-of-sync with the menu produced by mouse-click on the
+ // System Menu, or right-click on the title bar.
+ EnableMenuItem(menu, SC_SIZE, (tlw->windowFlags() & Qt::MSWindowsFixedSizeDialogHint) || maximized?disabled:enabled);
+ EnableMenuItem(menu, SC_MOVE, maximized?disabled:enabled);
+ EnableMenuItem(menu, SC_CLOSE, enabled);
+ // Set bold on close menu item
+ MENUITEMINFO closeItem;
+ closeItem.cbSize = sizeof(MENUITEMINFO);
+ closeItem.fMask = MIIM_STATE;
+ closeItem.fState = MFS_DEFAULT;
+ SetMenuItemInfo(menu, SC_CLOSE, FALSE, &closeItem);
+#endif
+
+#undef enabled
+#undef disabled
+ int ret = TrackPopupMenuEx(menu,
+ TPM_LEFTALIGN | TPM_TOPALIGN | TPM_NONOTIFY | TPM_RETURNCMD,
+ tlw->geometry().x(), tlw->geometry().y(),
+ tlw->internalWinId(),
+ 0);
+ if (ret)
+ QtWndProc(tlw->internalWinId(), WM_SYSCOMMAND, ret, 0);
+}
+
+
+// QETWidget class is only for accessing the sendSpontaneousEvent function in QApplication
+class QETWidget : public QWidget {
+public:
+ static bool sendSpontaneousEvent(QObject *r, QEvent *e)
+ { return QApplication::sendSpontaneousEvent(r, e); }
+};
+
+
+// Keyboard map private ----------------------------------------------------------------[ start ]---
+
+/*
+ \internal
+ A Windows KeyboardLayoutItem has 8 possible states:
+ 1. Unmodified
+ 2. Shift
+ 3. Control
+ 4. Control + Shift
+ 5. Alt
+ 6. Alt + Shift
+ 7. Alt + Control
+ 8. Alt + Control + Shift
+*/
+struct KeyboardLayoutItem {
+ bool dirty;
+ quint8 deadkeys;
+ quint32 qtKey[9]; // Can by any Qt::Key_<foo>, or unicode character
+};
+
+QKeyMapperPrivate::QKeyMapperPrivate()
+{
+ memset(keyLayout, 0, sizeof(keyLayout));
+}
+
+QKeyMapperPrivate::~QKeyMapperPrivate()
+{
+ clearMappings();
+}
+
+void QKeyMapperPrivate::clearMappings()
+{
+ for (int i = 0; i < 255; ++i) {
+ if (keyLayout[i]) {
+ delete keyLayout[i];
+ keyLayout[i] = 0;
+ }
+ }
+
+ /* MAKELCID()'s first argument is a WORD, and GetKeyboardLayout()
+ * returns a DWORD. */
+// LCID newLCID = MAKELCID(DWORD(GetKeyboardLayout(0)), SORT_DEFAULT);
+// keyboardInputLocale = qt_localeFromLCID(newLCID);
+ LCID newLCID = MAKELCID(
+ reinterpret_cast<long>(GetKeyboardLayout(0)),
+ SORT_DEFAULT
+ );
+ keyboardInputLocale = qt_localeFromLCID(newLCID);
+ bool bidi = false;
+#ifdef UNICODE
+ if (QSysInfo::WindowsVersion >= QSysInfo::WV_2000) {
+ WCHAR wchLCIDFontSig[16];
+ if (GetLocaleInfoW(newLCID,
+ LOCALE_FONTSIGNATURE,
+ &wchLCIDFontSig[0],
+ (sizeof(wchLCIDFontSig)/sizeof(WCHAR)))
+ && (wchLCIDFontSig[7] & (WCHAR)0x0800))
+ bidi = true;
+ } else
+#endif //UNICODE
+ {
+ if (newLCID == 0x0859 || //Sindhi (Arabic script)
+ newLCID == 0x0460) //Kashmiri (Arabic script)
+ bidi = true;;
+
+ switch (PRIMARYLANGID(newLCID))
+ {
+ case LANG_ARABIC:
+ case LANG_HEBREW:
+ case LANG_URDU:
+ case LANG_FARSI:
+ case LANG_PASHTO:
+ //case LANG_UIGHUR:
+ case LANG_SYRIAC:
+ case LANG_DIVEHI:
+ bidi = true;
+ }
+ }
+
+ keyboardInputDirection = bidi ? Qt::RightToLeft : Qt::LeftToRight;
+}
+
+void QKeyMapperPrivate::clearRecordedKeys()
+{
+ key_recorder.clearKeys();
+}
+
+
+inline void setKbdState(unsigned char *kbd, bool shift, bool ctrl, bool alt)
+{
+ kbd[VK_LSHIFT ] = (shift ? 0x80 : 0);
+ kbd[VK_SHIFT ] = (shift ? 0x80 : 0);
+ kbd[VK_LCONTROL] = (ctrl ? 0x80 : 0);
+ kbd[VK_CONTROL ] = (ctrl ? 0x80 : 0);
+ kbd[VK_RMENU ] = (alt ? 0x80 : 0);
+ kbd[VK_MENU ] = (alt ? 0x80 : 0);
+}
+
+void QKeyMapperPrivate::updateKeyMap(const MSG &msg)
+{
+ unsigned char kbdBuffer[256]; // Will hold the complete keyboard state
+ GetKeyboardState(kbdBuffer);
+ quint32 scancode = (msg.lParam >> 16) & 0xfff;
+ updatePossibleKeyCodes(kbdBuffer, scancode, msg.wParam);
+}
+
+void QKeyMapperPrivate::updatePossibleKeyCodes(unsigned char *kbdBuffer, quint32 scancode,
+ quint32 vk_key)
+{
+ if (!vk_key || (keyLayout[vk_key] && !keyLayout[vk_key]->dirty))
+ return;
+
+ if (!keyLayout[vk_key])
+ keyLayout[vk_key] = new KeyboardLayoutItem;
+
+ // Copy keyboard state, so we can modify and query output for each possible permutation
+ unsigned char buffer[256];
+ memcpy(buffer, kbdBuffer, sizeof(buffer));
+ // Always 0, as Windows doesn't treat these as modifiers;
+ buffer[VK_LWIN ] = 0;
+ buffer[VK_RWIN ] = 0;
+ buffer[VK_CAPITAL ] = 0;
+ buffer[VK_NUMLOCK ] = 0;
+ buffer[VK_SCROLL ] = 0;
+ // Always 0, since we'll only change the other versions
+ buffer[VK_RSHIFT ] = 0;
+ buffer[VK_RCONTROL] = 0;
+ buffer[VK_LMENU ] = 0; // Use right Alt, since left Ctrl + right Alt is considered AltGraph
+
+ bool isDeadKey = false;
+ keyLayout[vk_key]->deadkeys = 0;
+ keyLayout[vk_key]->dirty = false;
+ setKbdState(buffer, false, false, false);
+ keyLayout[vk_key]->qtKey[0] = toKeyOrUnicode(vk_key, scancode, buffer, &isDeadKey);
+ keyLayout[vk_key]->deadkeys |= isDeadKey ? 0x01 : 0;
+ setKbdState(buffer, true, false, false);
+ keyLayout[vk_key]->qtKey[1] = toKeyOrUnicode(vk_key, scancode, buffer, &isDeadKey);
+ keyLayout[vk_key]->deadkeys |= isDeadKey ? 0x02 : 0;
+ setKbdState(buffer, false, true, false);
+ keyLayout[vk_key]->qtKey[2] = toKeyOrUnicode(vk_key, scancode, buffer, &isDeadKey);
+ keyLayout[vk_key]->deadkeys |= isDeadKey ? 0x04 : 0;
+ setKbdState(buffer, true, true, false);
+ keyLayout[vk_key]->qtKey[3] = toKeyOrUnicode(vk_key, scancode, buffer, &isDeadKey);
+ keyLayout[vk_key]->deadkeys |= isDeadKey ? 0x08 : 0;
+ setKbdState(buffer, false, false, true);
+ keyLayout[vk_key]->qtKey[4] = toKeyOrUnicode(vk_key, scancode, buffer, &isDeadKey);
+ keyLayout[vk_key]->deadkeys |= isDeadKey ? 0x10 : 0;
+ setKbdState(buffer, true, false, true);
+ keyLayout[vk_key]->qtKey[5] = toKeyOrUnicode(vk_key, scancode, buffer, &isDeadKey);
+ keyLayout[vk_key]->deadkeys |= isDeadKey ? 0x20 : 0;
+ setKbdState(buffer, false, true, true);
+ keyLayout[vk_key]->qtKey[6] = toKeyOrUnicode(vk_key, scancode, buffer, &isDeadKey);
+ keyLayout[vk_key]->deadkeys |= isDeadKey ? 0x40 : 0;
+ setKbdState(buffer, true, true, true);
+ keyLayout[vk_key]->qtKey[7] = toKeyOrUnicode(vk_key, scancode, buffer, &isDeadKey);
+ keyLayout[vk_key]->deadkeys |= isDeadKey ? 0x80 : 0;
+ // Add a fall back key for layouts which don't do composition and show non-latin1 characters
+ int fallbackKey = KeyTbl[vk_key];
+ if (!fallbackKey || fallbackKey == Qt::Key_unknown) {
+ fallbackKey = 0;
+ if (vk_key != keyLayout[vk_key]->qtKey[0] && vk_key < 0x5B && vk_key > 0x2F)
+ fallbackKey = vk_key;
+ }
+ keyLayout[vk_key]->qtKey[8] = fallbackKey;
+
+ // If this vk_key a Dead Key
+ if (MapVirtualKey(vk_key, 2) & 0x80008000) { // (High-order dead key on Win 95 is 0x8000)
+ // Push a Space, then the original key through the low-level ToAscii functions.
+ // We do this because these functions (ToAscii / ToUnicode) will alter the internal state of
+ // the keyboard driver By doing the following, we set the keyboard driver state back to what
+ // it was before we wrecked it with the code above.
+ // We need to push the space with an empty keystate map, since the driver checks the map for
+ // transitions in modifiers, so this helps us capture all possible deadkeys.
+ unsigned char emptyBuffer[256];
+ memset(emptyBuffer, 0, sizeof(emptyBuffer));
+ ::ToAscii(VK_SPACE, 0, emptyBuffer, reinterpret_cast<LPWORD>(&buffer), 0);
+ ::ToAscii(vk_key, scancode, kbdBuffer, reinterpret_cast<LPWORD>(&buffer), 0);
+ }
+
+#ifdef DEBUG_KEYMAPPER
+ qDebug("updatePossibleKeyCodes for virtual key = 0x%02x!", vk_key);
+ for (int i = 0; i < 9; ++i) {
+ qDebug(" [%d] (%d,0x%02x,'%c') %s", i,
+ keyLayout[vk_key]->qtKey[i],
+ keyLayout[vk_key]->qtKey[i],
+ keyLayout[vk_key]->qtKey[i] ? keyLayout[vk_key]->qtKey[i] : 0x03,
+ keyLayout[vk_key]->deadkeys & (1<<i) ? "deadkey" : "");
+ }
+#endif // DEBUG_KEYMAPPER
+}
+
+bool QKeyMapperPrivate::isADeadKey(unsigned int vk_key, unsigned int modifiers)
+{
+ if (keyLayout && (vk_key < 256) && keyLayout[vk_key]) {
+ for(register int i = 0; i < 9; ++i) {
+ if (uint(ModsTbl[i]) == modifiers)
+ return bool(keyLayout[vk_key]->deadkeys & 1<<i);
+ }
+ }
+ return false;
+}
+
+extern bool qt_use_rtl_extensions;
+
+QList<int> QKeyMapperPrivate::possibleKeys(QKeyEvent *e)
+{
+ QList<int> result;
+
+ KeyboardLayoutItem *kbItem = keyLayout[e->nativeVirtualKey()];
+ if(!kbItem)
+ return result;
+
+ quint32 baseKey = kbItem->qtKey[0];
+ Qt::KeyboardModifiers keyMods = e->modifiers();
+ if (baseKey == Qt::Key_Return && (e->nativeModifiers() & ExtendedKey)) {
+ result << int(Qt::Key_Enter + keyMods);
+ return result;
+ }
+ result << int(baseKey + keyMods); // The base key is _always_ valid, of course
+
+ for(int i = 1; i < 9; ++i) {
+ Qt::KeyboardModifiers neededMods = ModsTbl[i];
+ quint32 key = kbItem->qtKey[i];
+ if (key && key != baseKey && ((keyMods & neededMods) == neededMods))
+ result << int(key + (keyMods & ~neededMods));
+ }
+
+ return result;
+}
+
+bool QKeyMapperPrivate::translateKeyEvent(QWidget *widget, const MSG &msg, bool grab)
+{
+ Q_Q(QKeyMapper);
+ Q_UNUSED(q); // Strange, but the compiler complains on q not being referenced, even if it is..
+ bool k0 = false;
+ bool k1 = false;
+ int msgType = msg.message;
+
+ quint32 scancode = (msg.lParam >> 16) & 0xfff;
+ quint32 vk_key = MapVirtualKey(scancode, 1);
+ bool isNumpad = (msg.wParam >= VK_NUMPAD0 && msg.wParam <= VK_NUMPAD9);
+ quint32 nModifiers = 0;
+
+ if (QSysInfo::WindowsVersion < QSysInfo::WV_NT || QSysInfo::WindowsVersion & QSysInfo::WV_CE_based) {
+ nModifiers |= (GetKeyState(VK_SHIFT ) < 0 ? ShiftAny : 0);
+ nModifiers |= (GetKeyState(VK_CONTROL) < 0 ? ControlAny : 0);
+ nModifiers |= (GetKeyState(VK_MENU ) < 0 ? AltAny : 0);
+ nModifiers |= (GetKeyState(VK_LWIN ) < 0 ? MetaLeft : 0);
+ nModifiers |= (GetKeyState(VK_RWIN ) < 0 ? MetaRight : 0);
+ } else {
+ // Map native modifiers to some bit representation
+ nModifiers |= (GetKeyState(VK_LSHIFT ) & 0x80 ? ShiftLeft : 0);
+ nModifiers |= (GetKeyState(VK_RSHIFT ) & 0x80 ? ShiftRight : 0);
+ nModifiers |= (GetKeyState(VK_LCONTROL) & 0x80 ? ControlLeft : 0);
+ nModifiers |= (GetKeyState(VK_RCONTROL) & 0x80 ? ControlRight : 0);
+ nModifiers |= (GetKeyState(VK_LMENU ) & 0x80 ? AltLeft : 0);
+ nModifiers |= (GetKeyState(VK_RMENU ) & 0x80 ? AltRight : 0);
+ nModifiers |= (GetKeyState(VK_LWIN ) & 0x80 ? MetaLeft : 0);
+ nModifiers |= (GetKeyState(VK_RWIN ) & 0x80 ? MetaRight : 0);
+ // Add Lock keys to the same bits
+ nModifiers |= (GetKeyState(VK_CAPITAL ) & 0x01 ? CapsLock : 0);
+ nModifiers |= (GetKeyState(VK_NUMLOCK ) & 0x01 ? NumLock : 0);
+ nModifiers |= (GetKeyState(VK_SCROLL ) & 0x01 ? ScrollLock : 0);
+ }
+ if (msg.lParam & ExtendedKey)
+ nModifiers |= msg.lParam & ExtendedKey;
+
+ // Get the modifier states (may be altered later, depending on key code)
+ int state = 0;
+ state |= (nModifiers & ShiftAny ? Qt::ShiftModifier : 0);
+ state |= (nModifiers & ControlAny ? Qt::ControlModifier : 0);
+ state |= (nModifiers & AltAny ? Qt::AltModifier : 0);
+ state |= (nModifiers & MetaAny ? Qt::MetaModifier : 0);
+
+ // Now we know enough to either have MapVirtualKey or our own keymap tell us if it's a deadkey
+ bool isDeadKey = isADeadKey(msg.wParam, state)
+ || MapVirtualKey(msg.wParam, 2) & 0x80008000; // High-order on 95 is 0x8000
+
+ // A multi-character key not found by our look-ahead
+ if (msgType == WM_CHAR) {
+ QString s;
+ QChar ch = wmchar_to_unicode(msg.wParam);
+ if (!ch.isNull())
+ s += ch;
+
+ k0 = q->sendKeyEvent(widget, grab, QEvent::KeyPress, 0, Qt::KeyboardModifier(state), s, false, 0, scancode, vk_key, nModifiers);
+ k1 = q->sendKeyEvent(widget, grab, QEvent::KeyRelease, 0, Qt::KeyboardModifier(state), s, false, 0, scancode, vk_key, nModifiers);
+ }
+
+ // Input method characters not found by our look-ahead
+ else if (msgType == WM_IME_CHAR) {
+ QString s;
+ QChar ch = imechar_to_unicode(msg.wParam);
+ if (!ch.isNull())
+ s += ch;
+
+ k0 = q->sendKeyEvent(widget, grab, QEvent::KeyPress, 0, Qt::KeyboardModifier(state), s, false, 0, scancode, vk_key, nModifiers);
+ k1 = q->sendKeyEvent(widget, grab, QEvent::KeyRelease, 0, Qt::KeyboardModifier(state), s, false, 0, scancode, vk_key, nModifiers);
+ }
+
+ else {
+ // handle Directionality changes (BiDi) with RTL extensions
+ if (qt_use_rtl_extensions) {
+ static int dirStatus = 0;
+ if (!dirStatus && state == Qt::ControlModifier
+ && msg.wParam == VK_CONTROL
+ && msgType == WM_KEYDOWN) {
+ if (GetKeyState(VK_LCONTROL) < 0)
+ dirStatus = VK_LCONTROL;
+ else if (GetKeyState(VK_RCONTROL) < 0)
+ dirStatus = VK_RCONTROL;
+ } else if (dirStatus) {
+ if (msgType == WM_KEYDOWN) {
+ if (msg.wParam == VK_SHIFT) {
+ if (dirStatus == VK_LCONTROL && GetKeyState(VK_LSHIFT) < 0)
+ dirStatus = VK_LSHIFT;
+ else if (dirStatus == VK_RCONTROL && GetKeyState(VK_RSHIFT) < 0)
+ dirStatus = VK_RSHIFT;
+ } else {
+ dirStatus = 0;
+ }
+ } else if (msgType == WM_KEYUP) {
+ if (dirStatus == VK_LSHIFT
+ && (msg.wParam == VK_SHIFT && GetKeyState(VK_LCONTROL)
+ || msg.wParam == VK_CONTROL && GetKeyState(VK_LSHIFT))) {
+ k0 = q->sendKeyEvent(widget, grab, QEvent::KeyPress, Qt::Key_Direction_L, 0,
+ QString(), false, 0,
+ scancode, msg.wParam, nModifiers);
+ k1 = q->sendKeyEvent(widget, grab, QEvent::KeyRelease, Qt::Key_Direction_L, 0,
+ QString(), false, 0,
+ scancode, msg.wParam, nModifiers);
+ dirStatus = 0;
+ } else if (dirStatus == VK_RSHIFT
+ && (msg.wParam == VK_SHIFT && GetKeyState(VK_RCONTROL)
+ || msg.wParam == VK_CONTROL && GetKeyState(VK_RSHIFT))) {
+ k0 = q->sendKeyEvent(widget, grab, QEvent::KeyPress, Qt::Key_Direction_R,
+ 0, QString(), false, 0,
+ scancode, msg.wParam, nModifiers);
+ k1 = q->sendKeyEvent(widget, grab, QEvent::KeyRelease, Qt::Key_Direction_R,
+ 0, QString(), false, 0,
+ scancode, msg.wParam, nModifiers);
+ dirStatus = 0;
+ } else {
+ dirStatus = 0;
+ }
+ } else {
+ dirStatus = 0;
+ }
+ }
+ }
+
+ // IME will process these keys, so simply return
+ if(msg.wParam == VK_PROCESSKEY)
+ return true;
+
+ // Ignore invalid virtual keycode (see bug 127424)
+ if (msg.wParam == 0xFF)
+ return true;
+
+ // Translate VK_* (native) -> Key_* (Qt) keys
+ // If it's a dead key, we cannot use the toKeyOrUnicode() function, since that will change
+ // the internal state of the keyboard driver, resulting in that dead keys no longer works.
+ // ..also if we're typing numbers on the keypad, while holding down the Alt modifier.
+ int code = 0;
+ if (isNumpad && (nModifiers & AltAny)) {
+ code = KeyTbl[msg.wParam];
+ } else if (!isDeadKey) {
+ unsigned char kbdBuffer[256]; // Will hold the complete keyboard state
+ GetKeyboardState(kbdBuffer);
+ code = toKeyOrUnicode(msg.wParam, scancode, kbdBuffer);
+ }
+
+ // Invert state logic:
+ // If the key actually pressed is a modifier key, then we remove its modifier key from the
+ // state, since a modifier-key can't have itself as a modifier
+ if (code == Qt::Key_Control)
+ state = state ^ Qt::ControlModifier;
+ else if (code == Qt::Key_Shift)
+ state = state ^ Qt::ShiftModifier;
+ else if (code == Qt::Key_Alt)
+ state = state ^ Qt::AltModifier;
+
+ // If the bit 24 of lParm is set you received a enter,
+ // otherwise a Return. (This is the extended key bit)
+ if ((code == Qt::Key_Return) && (msg.lParam & 0x1000000))
+ code = Qt::Key_Enter;
+
+ // All cursor keys without extended bit
+ if (!(msg.lParam & 0x1000000)) {
+ switch (code) {
+ case Qt::Key_Left:
+ case Qt::Key_Right:
+ case Qt::Key_Up:
+ case Qt::Key_Down:
+ case Qt::Key_PageUp:
+ case Qt::Key_PageDown:
+ case Qt::Key_Home:
+ case Qt::Key_End:
+ case Qt::Key_Insert:
+ case Qt::Key_Delete:
+ case Qt::Key_Asterisk:
+ case Qt::Key_Plus:
+ case Qt::Key_Minus:
+ case Qt::Key_Period:
+ case Qt::Key_0:
+ case Qt::Key_1:
+ case Qt::Key_2:
+ case Qt::Key_3:
+ case Qt::Key_4:
+ case Qt::Key_5:
+ case Qt::Key_6:
+ case Qt::Key_7:
+ case Qt::Key_8:
+ case Qt::Key_9:
+ state |= ((msg.wParam >= '0' && msg.wParam <= '9')
+ || (msg.wParam >= VK_OEM_PLUS && msg.wParam <= VK_OEM_3))
+ ? 0 : Qt::KeypadModifier;
+ default:
+ if ((uint)msg.lParam == 0x004c0001 || (uint)msg.lParam == 0xc04c0001)
+ state |= Qt::KeypadModifier;
+ break;
+ }
+ }
+ // Other keys with with extended bit
+ else {
+ switch (code) {
+ case Qt::Key_Enter:
+ case Qt::Key_Slash:
+ case Qt::Key_NumLock:
+ state |= Qt::KeypadModifier;
+ default:
+ break;
+ }
+ }
+
+ // KEYDOWN ---------------------------------------------------------------------------------
+ if (msgType == WM_KEYDOWN || msgType == WM_IME_KEYDOWN || msgType == WM_SYSKEYDOWN) {
+ // Get the last record of this key press, so we can validate the current state
+ // The record is not removed from the list
+ KeyRecord *rec = key_recorder.findKey(msg.wParam, false);
+
+ // If rec's state doesn't match the current state, something has changed behind our back
+ // (Consumed by modal widget is one possibility) So, remove the record from the list
+ // This will stop the auto-repeat of the key, should a modifier change, for example
+ if (rec && rec->state != state) {
+ key_recorder.findKey(msg.wParam, true);
+ rec = 0;
+ }
+
+ // Find unicode character from Windows Message Queue
+ MSG wm_char;
+ UINT charType = (msgType == WM_KEYDOWN
+ ? WM_CHAR
+ : msgType == WM_IME_KEYDOWN ? WM_IME_CHAR : WM_SYSCHAR);
+
+ QChar uch;
+ if (winPeekMessage(&wm_char, 0, charType, charType, PM_REMOVE)) {
+ // Found a ?_CHAR
+ uch = charType == WM_IME_CHAR
+ ? imechar_to_unicode(wm_char.wParam)
+ : wmchar_to_unicode(wm_char.wParam);
+ if (msgType == WM_SYSKEYDOWN && uch.isLetter() && (msg.lParam & KF_ALTDOWN))
+ uch = uch.toLower(); // (See doc of WM_SYSCHAR) Alt-letter
+ if (!code && !uch.row())
+ code = asciiToKeycode(uch.cell(), state);
+ }
+
+ // Special handling for the WM_IME_KEYDOWN message. Microsoft IME (Korean) will not
+ // generate a WM_IME_CHAR message corresponding to this message. We might get wrong
+ // results, if we map this virtual key-code directly (for eg '?' US layouts). So try
+ // to find the correct key using the current message parameters & keyboard state.
+ if (uch.isNull() && msgType == WM_IME_KEYDOWN) {
+ BYTE keyState[256];
+ WCHAR newKey[3] = {0};
+ GetKeyboardState(keyState);
+ int val = ToUnicode(vk_key, scancode, keyState, newKey, 2, 0);
+ if (val == 1) {
+ uch = QChar(newKey[0]);
+ } else {
+ // If we are still not able to find a unicode key, pass the WM_IME_KEYDOWN
+ // message to DefWindowProc() for generating a proper WM_KEYDOWN.
+ return false;
+ }
+ }
+
+ // If no ?_CHAR was found in the queue; deduct character from the ?_KEYDOWN parameters
+ if (uch.isNull()) {
+ if (msg.wParam == VK_DELETE) {
+ uch = QChar(QLatin1Char(0x7f)); // Windows doesn't know this one.
+ } else {
+ if (msgType != WM_SYSKEYDOWN || !code) {
+ UINT map;
+ QT_WA({
+ map = MapVirtualKey(msg.wParam, 2);
+ } , {
+ map = MapVirtualKeyA(msg.wParam, 2);
+ // High-order bit is 0x8000 on '95
+ if (map & 0x8000)
+ map = (map^0x8000)|0x80000000;
+ });
+ // If the high bit of the return value is set, it's a deadkey
+ if (!(map & 0x80000000))
+ uch = wmchar_to_unicode((DWORD)map);
+ }
+ }
+ if (!code && !uch.row())
+ code = asciiToKeycode(uch.cell(), state);
+ }
+
+ // Special handling of global Windows hotkeys
+ if (state == Qt::AltModifier) {
+ switch (code) {
+ case Qt::Key_Escape:
+ case Qt::Key_Tab:
+ case Qt::Key_Enter:
+ case Qt::Key_F4:
+ return false; // Send the event on to Windows
+ case Qt::Key_Space:
+ // do not pass this key to windows, we will process it ourselves
+ qt_show_system_menu(widget->window());
+ return true;
+ default:
+ break;
+ }
+ }
+
+ // Map SHIFT + Tab to SHIFT + BackTab, QShortcutMap knows about this translation
+ if (code == Qt::Key_Tab && (state & Qt::ShiftModifier) == Qt::ShiftModifier)
+ code = Qt::Key_Backtab;
+
+ // If we have a record, it means that the key is already pressed, the state is the same
+ // so, we have an auto-repeating key
+ if (rec) {
+ if (code < Qt::Key_Shift || code > Qt::Key_ScrollLock) {
+ k0 = q->sendKeyEvent(widget, grab, QEvent::KeyRelease, code,
+ Qt::KeyboardModifier(state), rec->text, true, 0,
+ scancode, msg.wParam, nModifiers);
+ k1 = q->sendKeyEvent(widget, grab, QEvent::KeyPress, code,
+ Qt::KeyboardModifier(state), rec->text, true, 0,
+ scancode, msg.wParam, nModifiers);
+ }
+ }
+ // No record of the key being previous pressed, so we now send a QEvent::KeyPress event,
+ // and store the key data into our records.
+ else {
+ QString text;
+ if (!uch.isNull())
+ text += uch;
+ char a = uch.row() ? 0 : uch.cell();
+ key_recorder.storeKey(msg.wParam, a, state, text);
+ k0 = q->sendKeyEvent(widget, grab, QEvent::KeyPress, code, Qt::KeyboardModifier(state),
+ text, false, 0, scancode, msg.wParam, nModifiers);
+
+ bool store = true;
+ // Alt+<alphanumerical> go to the Win32 menu system if unhandled by Qt
+#if !defined(Q_OS_WINCE)
+ if (msgType == WM_SYSKEYDOWN && !k0 && a) {
+ HWND parent = GetParent(widget->internalWinId());
+ while (parent) {
+ if (GetMenu(parent)) {
+ SendMessage(parent, WM_SYSCOMMAND, SC_KEYMENU, a);
+ store = false;
+ k0 = true;
+ break;
+ }
+ parent = GetParent(parent);
+ }
+ }
+#endif
+ if (!store)
+ key_recorder.findKey(msg.wParam, true);
+ }
+ }
+
+ // KEYUP -----------------------------------------------------------------------------------
+ else {
+ // Try to locate the key in our records, and remove it if it exists.
+ // The key may not be in our records if, for example, the down event was handled by
+ // win32 natively, or our window gets focus while a key is already press, but now gets
+ // the key release event.
+ KeyRecord* rec = key_recorder.findKey(msg.wParam, true);
+ if (!rec && !(code == Qt::Key_Shift
+ || code == Qt::Key_Control
+ || code == Qt::Key_Meta
+ || code == Qt::Key_Alt)) {
+ // Someone ate the key down event
+ } else {
+ if (!code)
+ code = asciiToKeycode(rec->ascii ? rec->ascii : msg.wParam, state);
+
+ // Map SHIFT + Tab to SHIFT + BackTab, QShortcutMap knows about this translation
+ if (code == Qt::Key_Tab && (state & Qt::ShiftModifier) == Qt::ShiftModifier)
+ code = Qt::Key_Backtab;
+
+ k0 = q->sendKeyEvent(widget, grab, QEvent::KeyRelease, code, Qt::KeyboardModifier(state),
+ (rec ? rec->text : QString()), false, 0, scancode, msg.wParam, nModifiers);
+
+ // don't pass Alt to Windows unless we are embedded in a non-Qt window
+#if !defined(Q_OS_WINCE)
+ if (code == Qt::Key_Alt) {
+ k0 = true;
+ HWND parent = GetParent(widget->internalWinId());
+ while (parent) {
+ if (!QWidget::find(parent) && GetMenu(parent)) {
+ k0 = false;
+ break;
+ }
+ parent = GetParent(parent);
+ }
+ }
+#endif
+ }
+ }
+ }
+
+ // Return true, if a QKeyEvent was sent to a widget
+ return k0 || k1;
+}
+
+
+// QKeyMapper (Windows) implementation -------------------------------------------------[ start ]---
+
+bool QKeyMapper::sendKeyEvent(QWidget *widget, bool grab,
+ QEvent::Type type, int code, Qt::KeyboardModifiers modifiers,
+ const QString &text, bool autorepeat, int count,
+ quint32 nativeScanCode, quint32 nativeVirtualKey, quint32 nativeModifiers,
+ bool *)
+{
+#if defined(Q_OS_WINCE)
+ Q_UNUSED(grab);
+#endif
+ Q_UNUSED(count);
+#if defined QT3_SUPPORT && !defined(QT_NO_SHORTCUT)
+ if (type == QEvent::KeyPress
+ && !grab
+ && QApplicationPrivate::instance()->use_compat()) {
+ // send accel events if the keyboard is not grabbed
+ QKeyEventEx a(type, code, modifiers,
+ text, autorepeat, qMax(1, int(text.length())),
+ nativeScanCode, nativeVirtualKey, nativeModifiers);
+ if (QApplicationPrivate::instance()->qt_tryAccelEvent(widget, &a))
+ return true;
+ }
+#endif
+ if (!widget->isEnabled())
+ return false;
+
+ QKeyEventEx e(type, code, modifiers,
+ text, autorepeat, qMax(1, int(text.length())),
+ nativeScanCode, nativeVirtualKey, nativeModifiers);
+ QETWidget::sendSpontaneousEvent(widget, &e);
+
+ if (!isModifierKey(code)
+ && modifiers == Qt::AltModifier
+ && ((code >= Qt::Key_A && code <= Qt::Key_Z) || (code >= Qt::Key_0 && code <= Qt::Key_9))
+ && type == QEvent::KeyPress
+ && !e.isAccepted())
+ QApplication::beep(); // Emulate windows behavior
+
+ return e.isAccepted();
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/kernel/qkeymapper_x11.cpp b/src/gui/kernel/qkeymapper_x11.cpp
new file mode 100644
index 0000000000..49b8566cfe
--- /dev/null
+++ b/src/gui/kernel/qkeymapper_x11.cpp
@@ -0,0 +1,1678 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qkeymapper_p.h"
+
+#include "qdebug.h"
+#include "qtextcodec.h"
+#include "qwidget.h"
+
+#include "qapplication_p.h"
+#include "qevent_p.h"
+#include "qt_x11_p.h"
+
+#ifndef QT_NO_XKB
+# include <X11/XKBlib.h>
+#endif
+
+#define XK_MISCELLANY
+#define XK_LATIN1
+#define XK_KOREAN
+#define XK_XKB_KEYS
+#include <X11/keysymdef.h>
+
+#include <ctype.h>
+
+QT_BEGIN_NAMESPACE
+
+#ifndef QT_NO_XKB
+
+// bring in the auto-generated xkbLayoutData
+#include "qkeymapper_x11_p.cpp"
+
+#ifdef QT_LINUXBASE
+// LSB's IsKeypadKey define is wrong - see
+// http://bugs.linuxbase.org/show_bug.cgi?id=2521
+#undef IsKeypadKey
+#define IsKeypadKey(keysym) \
+ (((KeySym)(keysym) >= XK_KP_Space) && ((KeySym)(keysym) <= XK_KP_Equal))
+
+#undef IsPrivateKeypadKey
+#define IsPrivateKeypadKey(keysym) \
+ (((KeySym)(keysym) >= 0x11000000) && ((KeySym)(keysym) <= 0x1100FFFF))
+#endif
+
+static void getLocaleAndDirection(QLocale *locale,
+ Qt::LayoutDirection *direction,
+ const QByteArray &layoutName,
+ const QByteArray &variantName)
+{
+ int i = 0;
+ while (xkbLayoutData[i].layout != 0) {
+ if (layoutName == xkbLayoutData[i].layout && variantName == xkbLayoutData[i].variant) {
+ *locale = QLocale(xkbLayoutData[i].language, xkbLayoutData[i].country);
+ *direction = xkbLayoutData[i].direction;
+ return;
+ }
+ ++i;
+ }
+ *locale = QLocale::c();
+ *direction = Qt::LeftToRight;
+}
+#endif // QT_NO_XKB
+
+
+// from qapplication_x11.cpp
+extern uchar qt_alt_mask;
+extern uchar qt_meta_mask;
+extern uchar qt_super_mask;
+extern uchar qt_hyper_mask;
+extern uchar qt_mode_switch_mask;
+uchar qt_num_lock_mask = 0;
+extern bool qt_sendSpontaneousEvent(QObject*, QEvent*);
+
+// ### we should really resolve conflicts with other masks by
+// ### decomposing the Qt::KeyboardModifers in possibleKeys()
+#define SETMASK(sym, mask) \
+ do { \
+ if (qt_alt_mask == 0 \
+ && qt_meta_mask != mask \
+ && qt_super_mask != mask \
+ && qt_hyper_mask != mask \
+ && (sym == XK_Alt_L || sym == XK_Alt_R)) { \
+ qt_alt_mask = mask; \
+ } \
+ if (qt_meta_mask == 0 \
+ && qt_alt_mask != mask \
+ && qt_super_mask != mask \
+ && qt_hyper_mask != mask \
+ && (sym == XK_Meta_L || sym == XK_Meta_R)) { \
+ qt_meta_mask = mask; \
+ } \
+ if (qt_super_mask == 0 \
+ && qt_alt_mask != mask \
+ && qt_meta_mask != mask \
+ && qt_hyper_mask != mask \
+ && (sym == XK_Super_L || sym == XK_Super_R)) { \
+ qt_super_mask = mask; \
+ } \
+ if (qt_hyper_mask == 0 \
+ && qt_alt_mask != mask \
+ && qt_meta_mask != mask \
+ && qt_super_mask != mask \
+ && (sym == XK_Hyper_L || sym == XK_Hyper_R)) { \
+ qt_hyper_mask = mask; \
+ } \
+ if (qt_mode_switch_mask == 0 \
+ && qt_alt_mask != mask \
+ && qt_meta_mask != mask \
+ && qt_super_mask != mask \
+ && qt_hyper_mask != mask \
+ && sym == XK_Mode_switch) { \
+ qt_mode_switch_mask = mask; \
+ } \
+ if (qt_num_lock_mask == 0 \
+ && sym == XK_Num_Lock) { \
+ qt_num_lock_mask = mask; \
+ } \
+ } while(false)
+
+// qt_XTranslateKey() is based on _XTranslateKey() taken from:
+
+/* $Xorg: KeyBind.c,v 1.4 2001/02/09 02:03:34 xorgcvs Exp $ */
+
+/*
+
+Copyright 1985, 1987, 1998 The Open Group
+
+Permission to use, copy, modify, distribute, and sell this software and its
+documentation for any purpose is hereby granted without fee, provided that
+the above copyright notice appear in all copies and that both that
+copyright notice and this permission notice appear in supporting
+documentation.
+
+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
+OPEN GROUP 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 Open Group 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 Open Group.
+
+*/
+static int
+qt_XTranslateKey(register QXCoreDesc *dpy,
+ KeyCode keycode,
+ register unsigned int modifiers,
+ unsigned int *modifiers_return,
+ KeySym *keysym_return)
+{
+ int per;
+ register KeySym *syms;
+ KeySym sym, lsym, usym;
+
+ if (! dpy->keysyms)
+ return 0;
+ *modifiers_return = ((ShiftMask|LockMask)
+ | dpy->mode_switch | dpy->num_lock);
+ if (((int)keycode < dpy->min_keycode) || ((int)keycode > dpy->max_keycode))
+ {
+ *keysym_return = NoSymbol;
+ return 1;
+ }
+ per = dpy->keysyms_per_keycode;
+ syms = &dpy->keysyms[(keycode - dpy->min_keycode) * per];
+ while ((per > 2) && (syms[per - 1] == NoSymbol))
+ per--;
+ if ((per > 2) && (modifiers & dpy->mode_switch)) {
+ syms += 2;
+ per -= 2;
+ }
+ if ((modifiers & dpy->num_lock) &&
+ (per > 1 && (IsKeypadKey(syms[1]) || IsPrivateKeypadKey(syms[1])))) {
+ if ((modifiers & ShiftMask) ||
+ ((modifiers & LockMask) && (dpy->lock_meaning == XK_Shift_Lock)))
+ *keysym_return = syms[0];
+ else
+ *keysym_return = syms[1];
+ } else if (!(modifiers & ShiftMask) &&
+ (!(modifiers & LockMask) || (dpy->lock_meaning == NoSymbol))) {
+ if ((per == 1) || (syms[1] == NoSymbol))
+ XConvertCase(syms[0], keysym_return, &usym);
+ else
+ *keysym_return = syms[0];
+ } else if (!(modifiers & LockMask) ||
+ (dpy->lock_meaning != XK_Caps_Lock)) {
+ if ((per == 1) || ((usym = syms[1]) == NoSymbol))
+ XConvertCase(syms[0], &lsym, &usym);
+ *keysym_return = usym;
+ } else {
+ if ((per == 1) || ((sym = syms[1]) == NoSymbol))
+ sym = syms[0];
+ XConvertCase(sym, &lsym, &usym);
+ if (!(modifiers & ShiftMask) && (sym != syms[0]) &&
+ ((sym != usym) || (lsym == usym)))
+ XConvertCase(syms[0], &lsym, &usym);
+ *keysym_return = usym;
+ }
+ if (*keysym_return == XK_VoidSymbol)
+ *keysym_return = NoSymbol;
+ return 1;
+}
+
+
+
+
+QKeyMapperPrivate::QKeyMapperPrivate()
+ : keyboardInputDirection(Qt::LeftToRight), useXKB(false)
+{
+ memset(&coreDesc, 0, sizeof(coreDesc));
+
+#ifndef QT_NO_XKB
+ int opcode = -1;
+ int xkbEventBase = -1;
+ int xkbErrorBase = -1;
+ int xkblibMajor = XkbMajorVersion;
+ int xkblibMinor = XkbMinorVersion;
+ if (XkbQueryExtension(X11->display, &opcode, &xkbEventBase, &xkbErrorBase, &xkblibMajor, &xkblibMinor))
+ useXKB = true;
+#endif
+
+#if 0
+ qDebug() << "useXKB =" << useXKB;
+#endif
+}
+
+QKeyMapperPrivate::~QKeyMapperPrivate()
+{
+ if (coreDesc.keysyms)
+ XFree(coreDesc.keysyms);
+}
+
+QList<int> QKeyMapperPrivate::possibleKeys(QKeyEvent *event)
+{
+#ifndef QT_NO_XKB
+ if (useXKB)
+ return possibleKeysXKB(event);
+#endif
+ return possibleKeysCore(event);
+}
+
+enum { MaxBits = sizeof(uint) * 8 };
+static QString translateKeySym(KeySym keysym, uint xmodifiers,
+ int &code, Qt::KeyboardModifiers &modifiers,
+ QByteArray &chars, int &count);
+
+QList<int> QKeyMapperPrivate::possibleKeysXKB(QKeyEvent *event)
+{
+#ifndef QT_NO_XKB
+ const int xkeycode = event->nativeScanCode();
+ const uint xmodifiers = event->nativeModifiers();
+
+ // first, translate key only using lock modifiers (there are no Qt equivalents for these, so we must
+ // always use them when determining the baseKeySym)
+ KeySym baseKeySym;
+ uint consumedModifiers;
+ if (!XkbLookupKeySym(X11->display, xkeycode, (xmodifiers & (LockMask | qt_num_lock_mask)),
+ &consumedModifiers, &baseKeySym))
+ return QList<int>();
+
+ QList<int> result;
+
+ // translate sym -> code
+ Qt::KeyboardModifiers baseModifiers = 0;
+ int baseCode = -1;
+ QByteArray chars;
+ int count = 0;
+ QString text = translateKeySym(baseKeySym, xmodifiers, baseCode, baseModifiers, chars, count);
+ if (baseCode == -1) {
+ if (text.isEmpty())
+ return QList<int>();
+ baseCode = text.at(0).unicode();
+ }
+
+ if (baseCode && baseCode < 0xfffe)
+ baseCode = QChar(baseCode).toUpper().unicode();
+ result += (baseCode | baseModifiers);
+
+ int pos1Bits[MaxBits];
+ int num1Bits = 0;
+
+ for (int i = 0; i < MaxBits; ++i) {
+ if (consumedModifiers & (1 << i))
+ pos1Bits[num1Bits++] = i;
+ }
+
+ const int numPerms = (1 << num1Bits);
+
+ // translate the key again using each permutation of consumedModifiers
+ for (int i = 1; i < numPerms; ++i) {
+ uint val = 0;
+ for (int j = 0; j < num1Bits; ++j) {
+ if (i & (1 << j))
+ val |= (1 << pos1Bits[j]);
+ }
+
+ if ((xmodifiers & val) != val)
+ continue;
+
+ KeySym sym;
+ uint mods;
+ if (!XkbLookupKeySym(X11->display, xkeycode, val, &mods, &sym))
+ continue;
+
+ // translate sym -> code
+ Qt::KeyboardModifiers modifiers = 0;
+ int code = -1;
+ chars.clear();
+ count = 0;
+ // mask out the modifiers needed to translate keycode
+ text = translateKeySym(sym, xmodifiers & ~val, code, modifiers, chars, count);
+ if (code == -1) {
+ if (text.isEmpty())
+ continue;
+ code = text.at(0).unicode();
+ }
+
+ if (code && code < 0xfffe)
+ code = QChar(code).toUpper().unicode();
+ if (code == baseCode)
+ continue;
+
+ result += (code | modifiers);
+ }
+
+#if 0
+ qDebug() << "possibleKeysXKB()" << hex << result;
+#endif
+ return result;
+#else
+ Q_UNUSED(event);
+ return QList<int>();
+#endif // QT_NO_XKB
+}
+
+QList<int> QKeyMapperPrivate::possibleKeysCore(QKeyEvent *event)
+{
+ const int xkeycode = event->nativeScanCode();
+ const uint xmodifiers = event->nativeModifiers();
+
+ // first, translate key only using lock modifiers (there are no Qt equivalents for these, so we must
+ // always use them when determining the baseKeySym)
+ KeySym baseKeySym;
+ uint consumedModifiers;
+ if (!qt_XTranslateKey(&coreDesc, xkeycode, (xmodifiers & (LockMask | qt_num_lock_mask)),
+ &consumedModifiers, &baseKeySym))
+ return QList<int>();
+
+ QList<int> result;
+
+ // translate sym -> code
+ Qt::KeyboardModifiers baseModifiers = 0;
+ int baseCode = -1;
+ QByteArray chars;
+ int count = 0;
+ QString text = translateKeySym(baseKeySym, xmodifiers, baseCode, baseModifiers, chars, count);
+ if (baseCode == -1) {
+ if (text.isEmpty())
+ return QList<int>();
+ baseCode = text.at(0).unicode();
+ }
+
+ if (baseCode && baseCode < 0xfffe)
+ baseCode = QChar(baseCode).toUpper().unicode();
+ result += (baseCode | baseModifiers);
+
+ int pos1Bits[MaxBits];
+ int num1Bits = 0;
+
+ for (int i = 0; i < MaxBits; ++i) {
+ if (consumedModifiers & (1 << i))
+ pos1Bits[num1Bits++] = i;
+ }
+
+ const int numPerms = (1 << num1Bits);
+
+ // translate the key again using each permutation of consumedModifiers
+ for (int i = 1; i < numPerms; ++i) {
+ uint val = 0;
+ for (int j = 0; j < num1Bits; ++j) {
+ if (i & (1 << j))
+ val |= (1 << pos1Bits[j]);
+ }
+
+ if ((xmodifiers & val) != val)
+ continue;
+
+ KeySym sym;
+ uint mods;
+ if (!qt_XTranslateKey(&coreDesc, xkeycode, val, &mods, &sym))
+ continue;
+
+ // translate sym -> code
+ Qt::KeyboardModifiers modifiers = 0;
+ int code = -1;
+ chars.clear();
+ count = 0;
+ // mask out the modifiers needed to translate keycode
+ text = translateKeySym(sym, xmodifiers & ~val, code, modifiers, chars, count);
+ if (code == -1) {
+ if (text.isEmpty())
+ continue;
+ code = text.at(0).unicode();
+ }
+
+ if (code && code < 0xfffe)
+ code = QChar(code).toUpper().unicode();
+ if (code == baseCode)
+ continue;
+
+ result += (code | modifiers);
+ }
+
+#if 0
+ qDebug() << "possibleKeysCore()" << hex << result;
+#endif
+ return result;
+}
+
+// for parsing the _XKB_RULES_NAMES property
+enum {
+ RulesFileIndex = 0,
+ ModelIndex = 1,
+ LayoutIndex = 2,
+ VariantIndex = 3,
+ OptionsIndex = 4
+};
+
+void QKeyMapperPrivate::clearMappings()
+{
+#ifndef QT_NO_XKB
+ if (useXKB) {
+ // try to determine the layout name and input direction by reading the _XKB_RULES_NAMES property off
+ // the root window
+ QByteArray layoutName;
+ QByteArray variantName;
+
+ Atom type = XNone;
+ int format = 0;
+ ulong nitems = 0;
+ ulong bytesAfter = 0;
+ uchar *data = 0;
+ if (XGetWindowProperty(X11->display, RootWindow(X11->display, 0), ATOM(_XKB_RULES_NAMES), 0, 1024,
+ false, XA_STRING, &type, &format, &nitems, &bytesAfter, &data) == Success
+ && type == XA_STRING && format == 8 && nitems > 2) {
+ /*
+ index 0 == rules file name
+ index 1 == model name
+ index 2 == layout name
+ index 3 == variant name
+ index 4 == options
+ */
+ char *names[5] = { 0, 0, 0, 0, 0 };
+ char *p = reinterpret_cast<char *>(data), *end = p + nitems;
+ int i = 0;
+ do {
+ names[i++] = p;
+ p += qstrlen(p) + 1;
+ } while (p < end);
+
+ layoutName = QByteArray::fromRawData(names[2], qstrlen(names[2]));
+ variantName = QByteArray::fromRawData(names[3], qstrlen(names[3]));
+ }
+
+ // ### ???
+ // if (keyboardLayoutName.isEmpty())
+ // qWarning("Qt: unable to determine keyboard layout, please talk to qt-bugs@trolltech.com"); ?
+
+ getLocaleAndDirection(&keyboardInputLocale,
+ &keyboardInputDirection,
+ layoutName,
+ variantName);
+
+#if 0
+ qDebug() << "keyboard input locale ="
+ << keyboardInputLocale.name()
+ << "direction ="
+ << keyboardInputDirection;
+#endif
+
+ if (data)
+ XFree(data);
+ } else
+#endif // QT_NO_XKB
+ {
+ if (coreDesc.keysyms)
+ XFree(coreDesc.keysyms);
+
+ coreDesc.min_keycode = 8;
+ coreDesc.max_keycode = 255;
+ XDisplayKeycodes(X11->display, &coreDesc.min_keycode, &coreDesc.max_keycode);
+
+ coreDesc.keysyms_per_keycode = 0;
+ coreDesc.keysyms = XGetKeyboardMapping(X11->display,
+ coreDesc.min_keycode,
+ coreDesc.max_keycode - coreDesc.min_keycode,
+ &coreDesc.keysyms_per_keycode);
+
+#if 0
+ qDebug() << "min_keycode =" << coreDesc.min_keycode;
+ qDebug() << "max_keycode =" << coreDesc.max_keycode;
+ qDebug() << "keysyms_per_keycode =" << coreDesc.keysyms_per_keycode;
+ qDebug() << "keysyms =" << coreDesc.keysyms;
+#endif
+
+ // ### cannot get/guess the locale with the core protocol
+ keyboardInputLocale = QLocale::c();
+ // ### could examine group 0 for RTL keys
+ keyboardInputDirection = Qt::LeftToRight;
+ }
+
+ qt_alt_mask = 0;
+ qt_meta_mask = 0;
+ qt_super_mask = 0;
+ qt_hyper_mask = 0;
+ qt_mode_switch_mask = 0;
+
+ // look at the modifier mapping, and get the correct masks for alt, meta, super, hyper, and mode_switch
+#ifndef QT_NO_XKB
+ if (useXKB) {
+ XkbDescPtr xkbDesc = XkbGetMap(X11->display, XkbAllClientInfoMask, XkbUseCoreKbd);
+ for (int i = xkbDesc->min_key_code; i < xkbDesc->max_key_code; ++i) {
+ const uint mask = xkbDesc->map->modmap ? xkbDesc->map->modmap[i] : 0;
+ if (mask == 0) {
+ // key is not bound to a modifier
+ continue;
+ }
+
+ for (int j = 0; j < XkbKeyGroupsWidth(xkbDesc, i); ++j) {
+ KeySym keySym = XkbKeySym(xkbDesc, i, j);
+ if (keySym == NoSymbol)
+ continue;
+ SETMASK(keySym, mask);
+ }
+ }
+ XkbFreeKeyboard(xkbDesc, XkbAllComponentsMask, true);
+ } else
+#endif // QT_NO_XKB
+ {
+ coreDesc.lock_meaning = NoSymbol;
+
+ XModifierKeymap *map = XGetModifierMapping(X11->display);
+
+ if (map) {
+ int i, maskIndex = 0, mapIndex = 0;
+ for (maskIndex = 0; maskIndex < 8; maskIndex++) {
+ for (i = 0; i < map->max_keypermod; i++) {
+ if (map->modifiermap[mapIndex]) {
+ KeySym sym;
+ int x = 0;
+ do {
+ sym = XKeycodeToKeysym(X11->display, map->modifiermap[mapIndex], x++);
+ } while (sym == NoSymbol && x < coreDesc.keysyms_per_keycode);
+ const uchar mask = 1 << maskIndex;
+ SETMASK(sym, mask);
+ }
+ mapIndex++;
+ }
+ }
+
+ // determine the meaning of the Lock modifier
+ for (i = 0; i < map->max_keypermod; ++i) {
+ for (int x = 0; x < coreDesc.keysyms_per_keycode; ++x) {
+ KeySym sym = XKeycodeToKeysym(X11->display, map->modifiermap[LockMapIndex], x);
+ if (sym == XK_Caps_Lock || sym == XK_ISO_Lock) {
+ coreDesc.lock_meaning = XK_Caps_Lock;
+ break;
+ } else if (sym == XK_Shift_Lock) {
+ coreDesc.lock_meaning = XK_Shift_Lock;
+ }
+ }
+ }
+
+ XFreeModifiermap(map);
+ }
+
+ // for qt_XTranslateKey()
+ coreDesc.num_lock = qt_num_lock_mask;
+ coreDesc.mode_switch = qt_mode_switch_mask;
+
+#if 0
+ qDebug() << "lock_meaning =" << coreDesc.lock_meaning;
+ qDebug() << "num_lock =" << coreDesc.num_lock;
+ qDebug() << "mode_switch =" << coreDesc.mode_switch;
+#endif
+ }
+
+ // set default modifier masks if needed
+ if( qt_alt_mask == 0 )
+ qt_alt_mask = Mod1Mask;
+ if( qt_meta_mask == 0 )
+ qt_meta_mask = Mod4Mask;
+
+ // if we don't have a meta key (or it's hidden behind alt), use super or hyper to generate
+ // Qt::Key_Meta and Qt::MetaModifier, since most newer XFree86/Xorg installations map the Windows
+ // key to Super
+ if (qt_meta_mask == 0 || qt_meta_mask == qt_alt_mask) {
+ // no meta keys... s,meta,super,
+ qt_meta_mask = qt_super_mask;
+ if (qt_meta_mask == 0 || qt_meta_mask == qt_alt_mask) {
+ // no super keys either? guess we'll use hyper then
+ qt_meta_mask = qt_hyper_mask;
+ }
+ }
+
+#if 0
+ qDebug() << "qt_alt_mask =" << hex << qt_alt_mask;
+ qDebug() << "qt_meta_mask =" << hex << qt_meta_mask;
+ qDebug() << "qt_super_mask =" << hex << qt_super_mask;
+ qDebug() << "qt_hyper_mask =" << hex << qt_hyper_mask;
+ qDebug() << "qt_mode_switch_mask =" << hex << qt_mode_switch_mask;
+ qDebug() << "qt_num_lock_mask =" << hex << qt_num_lock_mask;
+#endif
+}
+
+extern bool qt_sm_blockUserInput;
+
+//
+// Keyboard event translation
+//
+
+#ifndef XK_ISO_Left_Tab
+#define XK_ISO_Left_Tab 0xFE20
+#endif
+
+#ifndef XK_dead_hook
+#define XK_dead_hook 0xFE61
+#endif
+
+#ifndef XK_dead_horn
+#define XK_dead_horn 0xFE62
+#endif
+
+#ifndef XK_Codeinput
+#define XK_Codeinput 0xFF37
+#endif
+
+#ifndef XK_Kanji_Bangou
+#define XK_Kanji_Bangou 0xFF37 /* same as codeinput */
+#endif
+
+// Fix old X libraries
+#ifndef XK_KP_Home
+#define XK_KP_Home 0xFF95
+#endif
+#ifndef XK_KP_Left
+#define XK_KP_Left 0xFF96
+#endif
+#ifndef XK_KP_Up
+#define XK_KP_Up 0xFF97
+#endif
+#ifndef XK_KP_Right
+#define XK_KP_Right 0xFF98
+#endif
+#ifndef XK_KP_Down
+#define XK_KP_Down 0xFF99
+#endif
+#ifndef XK_KP_Prior
+#define XK_KP_Prior 0xFF9A
+#endif
+#ifndef XK_KP_Next
+#define XK_KP_Next 0xFF9B
+#endif
+#ifndef XK_KP_End
+#define XK_KP_End 0xFF9C
+#endif
+#ifndef XK_KP_Insert
+#define XK_KP_Insert 0xFF9E
+#endif
+#ifndef XK_KP_Delete
+#define XK_KP_Delete 0xFF9F
+#endif
+
+// the next lines are taken from XFree > 4.0 (X11/XF86keysyms.h), defining some special
+// multimedia keys. They are included here as not every system has them.
+#define XF86XK_Standby 0x1008FF10
+#define XF86XK_AudioLowerVolume 0x1008FF11
+#define XF86XK_AudioMute 0x1008FF12
+#define XF86XK_AudioRaiseVolume 0x1008FF13
+#define XF86XK_AudioPlay 0x1008FF14
+#define XF86XK_AudioStop 0x1008FF15
+#define XF86XK_AudioPrev 0x1008FF16
+#define XF86XK_AudioNext 0x1008FF17
+#define XF86XK_HomePage 0x1008FF18
+#define XF86XK_Calculator 0x1008FF1D
+#define XF86XK_Mail 0x1008FF19
+#define XF86XK_Start 0x1008FF1A
+#define XF86XK_Search 0x1008FF1B
+#define XF86XK_AudioRecord 0x1008FF1C
+#define XF86XK_Back 0x1008FF26
+#define XF86XK_Forward 0x1008FF27
+#define XF86XK_Stop 0x1008FF28
+#define XF86XK_Refresh 0x1008FF29
+#define XF86XK_Favorites 0x1008FF30
+#define XF86XK_AudioPause 0x1008FF31
+#define XF86XK_AudioMedia 0x1008FF32
+#define XF86XK_MyComputer 0x1008FF33
+#define XF86XK_OpenURL 0x1008FF38
+#define XF86XK_Launch0 0x1008FF40
+#define XF86XK_Launch1 0x1008FF41
+#define XF86XK_Launch2 0x1008FF42
+#define XF86XK_Launch3 0x1008FF43
+#define XF86XK_Launch4 0x1008FF44
+#define XF86XK_Launch5 0x1008FF45
+#define XF86XK_Launch6 0x1008FF46
+#define XF86XK_Launch7 0x1008FF47
+#define XF86XK_Launch8 0x1008FF48
+#define XF86XK_Launch9 0x1008FF49
+#define XF86XK_LaunchA 0x1008FF4A
+#define XF86XK_LaunchB 0x1008FF4B
+#define XF86XK_LaunchC 0x1008FF4C
+#define XF86XK_LaunchD 0x1008FF4D
+#define XF86XK_LaunchE 0x1008FF4E
+#define XF86XK_LaunchF 0x1008FF4F
+// end of XF86keysyms.h
+
+// Special keys used by Qtopia, mapped into the X11 private keypad range.
+#define QTOPIAXK_Select 0x11000601
+#define QTOPIAXK_Yes 0x11000602
+#define QTOPIAXK_No 0x11000603
+#define QTOPIAXK_Cancel 0x11000604
+#define QTOPIAXK_Printer 0x11000605
+#define QTOPIAXK_Execute 0x11000606
+#define QTOPIAXK_Sleep 0x11000607
+#define QTOPIAXK_Play 0x11000608
+#define QTOPIAXK_Zoom 0x11000609
+#define QTOPIAXK_Context1 0x1100060A
+#define QTOPIAXK_Context2 0x1100060B
+#define QTOPIAXK_Context3 0x1100060C
+#define QTOPIAXK_Context4 0x1100060D
+#define QTOPIAXK_Call 0x1100060E
+#define QTOPIAXK_Hangup 0x1100060F
+#define QTOPIAXK_Flip 0x11000610
+
+// keyboard mapping table
+static const unsigned int KeyTbl[] = {
+
+ // misc keys
+
+ XK_Escape, Qt::Key_Escape,
+ XK_Tab, Qt::Key_Tab,
+ XK_ISO_Left_Tab, Qt::Key_Backtab,
+ XK_BackSpace, Qt::Key_Backspace,
+ XK_Return, Qt::Key_Return,
+ XK_Insert, Qt::Key_Insert,
+ XK_Delete, Qt::Key_Delete,
+ XK_Clear, Qt::Key_Delete,
+ XK_Pause, Qt::Key_Pause,
+ XK_Print, Qt::Key_Print,
+ 0x1005FF60, Qt::Key_SysReq, // hardcoded Sun SysReq
+ 0x1007ff00, Qt::Key_SysReq, // hardcoded X386 SysReq
+
+ // cursor movement
+
+ XK_Home, Qt::Key_Home,
+ XK_End, Qt::Key_End,
+ XK_Left, Qt::Key_Left,
+ XK_Up, Qt::Key_Up,
+ XK_Right, Qt::Key_Right,
+ XK_Down, Qt::Key_Down,
+ XK_Prior, Qt::Key_PageUp,
+ XK_Next, Qt::Key_PageDown,
+
+ // modifiers
+
+ XK_Shift_L, Qt::Key_Shift,
+ XK_Shift_R, Qt::Key_Shift,
+ XK_Shift_Lock, Qt::Key_Shift,
+ XK_Control_L, Qt::Key_Control,
+ XK_Control_R, Qt::Key_Control,
+ XK_Meta_L, Qt::Key_Meta,
+ XK_Meta_R, Qt::Key_Meta,
+ XK_Alt_L, Qt::Key_Alt,
+ XK_Alt_R, Qt::Key_Alt,
+ XK_Caps_Lock, Qt::Key_CapsLock,
+ XK_Num_Lock, Qt::Key_NumLock,
+ XK_Scroll_Lock, Qt::Key_ScrollLock,
+ XK_Super_L, Qt::Key_Super_L,
+ XK_Super_R, Qt::Key_Super_R,
+ XK_Menu, Qt::Key_Menu,
+ XK_Hyper_L, Qt::Key_Hyper_L,
+ XK_Hyper_R, Qt::Key_Hyper_R,
+ XK_Help, Qt::Key_Help,
+ 0x1000FF74, Qt::Key_Backtab, // hardcoded HP backtab
+ 0x1005FF10, Qt::Key_F11, // hardcoded Sun F36 (labeled F11)
+ 0x1005FF11, Qt::Key_F12, // hardcoded Sun F37 (labeled F12)
+
+ // numeric and function keypad keys
+
+ XK_KP_Space, Qt::Key_Space,
+ XK_KP_Tab, Qt::Key_Tab,
+ XK_KP_Enter, Qt::Key_Enter,
+ //XK_KP_F1, Qt::Key_F1,
+ //XK_KP_F2, Qt::Key_F2,
+ //XK_KP_F3, Qt::Key_F3,
+ //XK_KP_F4, Qt::Key_F4,
+ XK_KP_Home, Qt::Key_Home,
+ XK_KP_Left, Qt::Key_Left,
+ XK_KP_Up, Qt::Key_Up,
+ XK_KP_Right, Qt::Key_Right,
+ XK_KP_Down, Qt::Key_Down,
+ XK_KP_Prior, Qt::Key_PageUp,
+ XK_KP_Next, Qt::Key_PageDown,
+ XK_KP_End, Qt::Key_End,
+ XK_KP_Begin, Qt::Key_Clear,
+ XK_KP_Insert, Qt::Key_Insert,
+ XK_KP_Delete, Qt::Key_Delete,
+ XK_KP_Equal, Qt::Key_Equal,
+ XK_KP_Multiply, Qt::Key_Asterisk,
+ XK_KP_Add, Qt::Key_Plus,
+ XK_KP_Separator, Qt::Key_Comma,
+ XK_KP_Subtract, Qt::Key_Minus,
+ XK_KP_Decimal, Qt::Key_Period,
+ XK_KP_Divide, Qt::Key_Slash,
+
+ // International input method support keys
+
+ // International & multi-key character composition
+ XK_ISO_Level3_Shift, Qt::Key_AltGr,
+ XK_Multi_key, Qt::Key_Multi_key,
+ XK_Codeinput, Qt::Key_Codeinput,
+ XK_SingleCandidate, Qt::Key_SingleCandidate,
+ XK_MultipleCandidate, Qt::Key_MultipleCandidate,
+ XK_PreviousCandidate, Qt::Key_PreviousCandidate,
+
+ // Misc Functions
+ XK_Mode_switch, Qt::Key_Mode_switch,
+ XK_script_switch, Qt::Key_Mode_switch,
+
+ // Japanese keyboard support
+ XK_Kanji, Qt::Key_Kanji,
+ XK_Muhenkan, Qt::Key_Muhenkan,
+ //XK_Henkan_Mode, Qt::Key_Henkan_Mode,
+ XK_Henkan_Mode, Qt::Key_Henkan,
+ XK_Henkan, Qt::Key_Henkan,
+ XK_Romaji, Qt::Key_Romaji,
+ XK_Hiragana, Qt::Key_Hiragana,
+ XK_Katakana, Qt::Key_Katakana,
+ XK_Hiragana_Katakana, Qt::Key_Hiragana_Katakana,
+ XK_Zenkaku, Qt::Key_Zenkaku,
+ XK_Hankaku, Qt::Key_Hankaku,
+ XK_Zenkaku_Hankaku, Qt::Key_Zenkaku_Hankaku,
+ XK_Touroku, Qt::Key_Touroku,
+ XK_Massyo, Qt::Key_Massyo,
+ XK_Kana_Lock, Qt::Key_Kana_Lock,
+ XK_Kana_Shift, Qt::Key_Kana_Shift,
+ XK_Eisu_Shift, Qt::Key_Eisu_Shift,
+ XK_Eisu_toggle, Qt::Key_Eisu_toggle,
+ //XK_Kanji_Bangou, Qt::Key_Kanji_Bangou,
+ //XK_Zen_Koho, Qt::Key_Zen_Koho,
+ //XK_Mae_Koho, Qt::Key_Mae_Koho,
+ XK_Kanji_Bangou, Qt::Key_Codeinput,
+ XK_Zen_Koho, Qt::Key_MultipleCandidate,
+ XK_Mae_Koho, Qt::Key_PreviousCandidate,
+
+#ifdef XK_KOREAN
+ // Korean keyboard support
+ XK_Hangul, Qt::Key_Hangul,
+ XK_Hangul_Start, Qt::Key_Hangul_Start,
+ XK_Hangul_End, Qt::Key_Hangul_End,
+ XK_Hangul_Hanja, Qt::Key_Hangul_Hanja,
+ XK_Hangul_Jamo, Qt::Key_Hangul_Jamo,
+ XK_Hangul_Romaja, Qt::Key_Hangul_Romaja,
+ //XK_Hangul_Codeinput, Qt::Key_Hangul_Codeinput,
+ XK_Hangul_Codeinput, Qt::Key_Codeinput,
+ XK_Hangul_Jeonja, Qt::Key_Hangul_Jeonja,
+ XK_Hangul_Banja, Qt::Key_Hangul_Banja,
+ XK_Hangul_PreHanja, Qt::Key_Hangul_PreHanja,
+ XK_Hangul_PostHanja, Qt::Key_Hangul_PostHanja,
+ //XK_Hangul_SingleCandidate,Qt::Key_Hangul_SingleCandidate,
+ //XK_Hangul_MultipleCandidate,Qt::Key_Hangul_MultipleCandidate,
+ //XK_Hangul_PreviousCandidate,Qt::Key_Hangul_PreviousCandidate,
+ XK_Hangul_SingleCandidate, Qt::Key_SingleCandidate,
+ XK_Hangul_MultipleCandidate,Qt::Key_MultipleCandidate,
+ XK_Hangul_PreviousCandidate,Qt::Key_PreviousCandidate,
+ XK_Hangul_Special, Qt::Key_Hangul_Special,
+ //XK_Hangul_switch, Qt::Key_Hangul_switch,
+ XK_Hangul_switch, Qt::Key_Mode_switch,
+#endif // XK_KOREAN
+
+ // dead keys
+ XK_dead_grave, Qt::Key_Dead_Grave,
+ XK_dead_acute, Qt::Key_Dead_Acute,
+ XK_dead_circumflex, Qt::Key_Dead_Circumflex,
+ XK_dead_tilde, Qt::Key_Dead_Tilde,
+ XK_dead_macron, Qt::Key_Dead_Macron,
+ XK_dead_breve, Qt::Key_Dead_Breve,
+ XK_dead_abovedot, Qt::Key_Dead_Abovedot,
+ XK_dead_diaeresis, Qt::Key_Dead_Diaeresis,
+ XK_dead_abovering, Qt::Key_Dead_Abovering,
+ XK_dead_doubleacute, Qt::Key_Dead_Doubleacute,
+ XK_dead_caron, Qt::Key_Dead_Caron,
+ XK_dead_cedilla, Qt::Key_Dead_Cedilla,
+ XK_dead_ogonek, Qt::Key_Dead_Ogonek,
+ XK_dead_iota, Qt::Key_Dead_Iota,
+ XK_dead_voiced_sound, Qt::Key_Dead_Voiced_Sound,
+ XK_dead_semivoiced_sound, Qt::Key_Dead_Semivoiced_Sound,
+ XK_dead_belowdot, Qt::Key_Dead_Belowdot,
+ XK_dead_hook, Qt::Key_Dead_Hook,
+ XK_dead_horn, Qt::Key_Dead_Horn,
+
+ // Special multimedia keys
+ // currently only tested with MS internet keyboard
+
+ // browsing keys
+ XF86XK_Back, Qt::Key_Back,
+ XF86XK_Forward, Qt::Key_Forward,
+ XF86XK_Stop, Qt::Key_Stop,
+ XF86XK_Refresh, Qt::Key_Refresh,
+ XF86XK_Favorites, Qt::Key_Favorites,
+ XF86XK_AudioMedia, Qt::Key_LaunchMedia,
+ XF86XK_OpenURL, Qt::Key_OpenUrl,
+ XF86XK_HomePage, Qt::Key_HomePage,
+ XF86XK_Search, Qt::Key_Search,
+
+ // media keys
+ XF86XK_AudioLowerVolume, Qt::Key_VolumeDown,
+ XF86XK_AudioMute, Qt::Key_VolumeMute,
+ XF86XK_AudioRaiseVolume, Qt::Key_VolumeUp,
+ XF86XK_AudioPlay, Qt::Key_MediaPlay,
+ XF86XK_AudioStop, Qt::Key_MediaStop,
+ XF86XK_AudioPrev, Qt::Key_MediaPrevious,
+ XF86XK_AudioNext, Qt::Key_MediaNext,
+ XF86XK_AudioRecord, Qt::Key_MediaRecord,
+
+ // launch keys
+ XF86XK_Mail, Qt::Key_LaunchMail,
+ XF86XK_MyComputer, Qt::Key_Launch0,
+ XF86XK_Calculator, Qt::Key_Launch1,
+ XF86XK_Standby, Qt::Key_Standby,
+
+ XF86XK_Launch0, Qt::Key_Launch2,
+ XF86XK_Launch1, Qt::Key_Launch3,
+ XF86XK_Launch2, Qt::Key_Launch4,
+ XF86XK_Launch3, Qt::Key_Launch5,
+ XF86XK_Launch4, Qt::Key_Launch6,
+ XF86XK_Launch5, Qt::Key_Launch7,
+ XF86XK_Launch6, Qt::Key_Launch8,
+ XF86XK_Launch7, Qt::Key_Launch9,
+ XF86XK_Launch8, Qt::Key_LaunchA,
+ XF86XK_Launch9, Qt::Key_LaunchB,
+ XF86XK_LaunchA, Qt::Key_LaunchC,
+ XF86XK_LaunchB, Qt::Key_LaunchD,
+ XF86XK_LaunchC, Qt::Key_LaunchE,
+ XF86XK_LaunchD, Qt::Key_LaunchF,
+
+ // Qtopia keys
+ QTOPIAXK_Select, Qt::Key_Select,
+ QTOPIAXK_Yes, Qt::Key_Yes,
+ QTOPIAXK_No, Qt::Key_No,
+ QTOPIAXK_Cancel, Qt::Key_Cancel,
+ QTOPIAXK_Printer, Qt::Key_Printer,
+ QTOPIAXK_Execute, Qt::Key_Execute,
+ QTOPIAXK_Sleep, Qt::Key_Sleep,
+ QTOPIAXK_Play, Qt::Key_Play,
+ QTOPIAXK_Zoom, Qt::Key_Zoom,
+ QTOPIAXK_Context1, Qt::Key_Context1,
+ QTOPIAXK_Context2, Qt::Key_Context2,
+ QTOPIAXK_Context3, Qt::Key_Context3,
+ QTOPIAXK_Context4, Qt::Key_Context4,
+ QTOPIAXK_Call, Qt::Key_Call,
+ QTOPIAXK_Hangup, Qt::Key_Hangup,
+ QTOPIAXK_Flip, Qt::Key_Flip,
+
+ 0, 0
+};
+
+static int translateKeySym(uint key)
+{
+ int code = -1;
+ int i = 0; // any other keys
+ while (KeyTbl[i]) {
+ if (key == KeyTbl[i]) {
+ code = (int)KeyTbl[i+1];
+ break;
+ }
+ i += 2;
+ }
+ if (qt_meta_mask) {
+ // translate Super/Hyper keys to Meta if we're using them as the MetaModifier
+ if (qt_meta_mask == qt_super_mask && (code == Qt::Key_Super_L || code == Qt::Key_Super_R)) {
+ code = Qt::Key_Meta;
+ } else if (qt_meta_mask == qt_hyper_mask && (code == Qt::Key_Hyper_L || code == Qt::Key_Hyper_R)) {
+ code = Qt::Key_Meta;
+ }
+ }
+ return code;
+}
+
+#if !defined(QT_NO_XIM)
+static const unsigned short katakanaKeysymsToUnicode[] = {
+ 0x0000, 0x3002, 0x300C, 0x300D, 0x3001, 0x30FB, 0x30F2, 0x30A1,
+ 0x30A3, 0x30A5, 0x30A7, 0x30A9, 0x30E3, 0x30E5, 0x30E7, 0x30C3,
+ 0x30FC, 0x30A2, 0x30A4, 0x30A6, 0x30A8, 0x30AA, 0x30AB, 0x30AD,
+ 0x30AF, 0x30B1, 0x30B3, 0x30B5, 0x30B7, 0x30B9, 0x30BB, 0x30BD,
+ 0x30BF, 0x30C1, 0x30C4, 0x30C6, 0x30C8, 0x30CA, 0x30CB, 0x30CC,
+ 0x30CD, 0x30CE, 0x30CF, 0x30D2, 0x30D5, 0x30D8, 0x30DB, 0x30DE,
+ 0x30DF, 0x30E0, 0x30E1, 0x30E2, 0x30E4, 0x30E6, 0x30E8, 0x30E9,
+ 0x30EA, 0x30EB, 0x30EC, 0x30ED, 0x30EF, 0x30F3, 0x309B, 0x309C
+};
+
+static const unsigned short cyrillicKeysymsToUnicode[] = {
+ 0x0000, 0x0452, 0x0453, 0x0451, 0x0454, 0x0455, 0x0456, 0x0457,
+ 0x0458, 0x0459, 0x045a, 0x045b, 0x045c, 0x0000, 0x045e, 0x045f,
+ 0x2116, 0x0402, 0x0403, 0x0401, 0x0404, 0x0405, 0x0406, 0x0407,
+ 0x0408, 0x0409, 0x040a, 0x040b, 0x040c, 0x0000, 0x040e, 0x040f,
+ 0x044e, 0x0430, 0x0431, 0x0446, 0x0434, 0x0435, 0x0444, 0x0433,
+ 0x0445, 0x0438, 0x0439, 0x043a, 0x043b, 0x043c, 0x043d, 0x043e,
+ 0x043f, 0x044f, 0x0440, 0x0441, 0x0442, 0x0443, 0x0436, 0x0432,
+ 0x044c, 0x044b, 0x0437, 0x0448, 0x044d, 0x0449, 0x0447, 0x044a,
+ 0x042e, 0x0410, 0x0411, 0x0426, 0x0414, 0x0415, 0x0424, 0x0413,
+ 0x0425, 0x0418, 0x0419, 0x041a, 0x041b, 0x041c, 0x041d, 0x041e,
+ 0x041f, 0x042f, 0x0420, 0x0421, 0x0422, 0x0423, 0x0416, 0x0412,
+ 0x042c, 0x042b, 0x0417, 0x0428, 0x042d, 0x0429, 0x0427, 0x042a
+};
+
+static const unsigned short greekKeysymsToUnicode[] = {
+ 0x0000, 0x0386, 0x0388, 0x0389, 0x038a, 0x03aa, 0x0000, 0x038c,
+ 0x038e, 0x03ab, 0x0000, 0x038f, 0x0000, 0x0000, 0x0385, 0x2015,
+ 0x0000, 0x03ac, 0x03ad, 0x03ae, 0x03af, 0x03ca, 0x0390, 0x03cc,
+ 0x03cd, 0x03cb, 0x03b0, 0x03ce, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0391, 0x0392, 0x0393, 0x0394, 0x0395, 0x0396, 0x0397,
+ 0x0398, 0x0399, 0x039a, 0x039b, 0x039c, 0x039d, 0x039e, 0x039f,
+ 0x03a0, 0x03a1, 0x03a3, 0x0000, 0x03a4, 0x03a5, 0x03a6, 0x03a7,
+ 0x03a8, 0x03a9, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x03b1, 0x03b2, 0x03b3, 0x03b4, 0x03b5, 0x03b6, 0x03b7,
+ 0x03b8, 0x03b9, 0x03ba, 0x03bb, 0x03bc, 0x03bd, 0x03be, 0x03bf,
+ 0x03c0, 0x03c1, 0x03c3, 0x03c2, 0x03c4, 0x03c5, 0x03c6, 0x03c7,
+ 0x03c8, 0x03c9, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000
+};
+
+static const unsigned short technicalKeysymsToUnicode[] = {
+ 0x0000, 0x23B7, 0x250C, 0x2500, 0x2320, 0x2321, 0x2502, 0x23A1,
+ 0x23A3, 0x23A4, 0x23A6, 0x239B, 0x239D, 0x239E, 0x23A0, 0x23A8,
+ 0x23AC, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x2264, 0x2260, 0x2265, 0x222B,
+ 0x2234, 0x221D, 0x221E, 0x0000, 0x0000, 0x2207, 0x0000, 0x0000,
+ 0x223C, 0x2243, 0x0000, 0x0000, 0x0000, 0x21D4, 0x21D2, 0x2261,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x221A, 0x0000,
+ 0x0000, 0x0000, 0x2282, 0x2283, 0x2229, 0x222A, 0x2227, 0x2228,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x2202,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0192, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x2190, 0x2191, 0x2192, 0x2193, 0x0000
+};
+
+static const unsigned short specialKeysymsToUnicode[] = {
+ 0x25C6, 0x2592, 0x2409, 0x240C, 0x240D, 0x240A, 0x0000, 0x0000,
+ 0x2424, 0x240B, 0x2518, 0x2510, 0x250C, 0x2514, 0x253C, 0x23BA,
+ 0x23BB, 0x2500, 0x23BC, 0x23BD, 0x251C, 0x2524, 0x2534, 0x252C,
+ 0x2502, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000
+};
+
+static const unsigned short publishingKeysymsToUnicode[] = {
+ 0x0000, 0x2003, 0x2002, 0x2004, 0x2005, 0x2007, 0x2008, 0x2009,
+ 0x200a, 0x2014, 0x2013, 0x0000, 0x0000, 0x0000, 0x2026, 0x2025,
+ 0x2153, 0x2154, 0x2155, 0x2156, 0x2157, 0x2158, 0x2159, 0x215a,
+ 0x2105, 0x0000, 0x0000, 0x2012, 0x2329, 0x0000, 0x232a, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x215b, 0x215c, 0x215d, 0x215e, 0x0000,
+ 0x0000, 0x2122, 0x2613, 0x0000, 0x25c1, 0x25b7, 0x25cb, 0x25af,
+ 0x2018, 0x2019, 0x201c, 0x201d, 0x211e, 0x0000, 0x2032, 0x2033,
+ 0x0000, 0x271d, 0x0000, 0x25ac, 0x25c0, 0x25b6, 0x25cf, 0x25ae,
+ 0x25e6, 0x25ab, 0x25ad, 0x25b3, 0x25bd, 0x2606, 0x2022, 0x25aa,
+ 0x25b2, 0x25bc, 0x261c, 0x261e, 0x2663, 0x2666, 0x2665, 0x0000,
+ 0x2720, 0x2020, 0x2021, 0x2713, 0x2717, 0x266f, 0x266d, 0x2642,
+ 0x2640, 0x260e, 0x2315, 0x2117, 0x2038, 0x201a, 0x201e, 0x0000
+};
+
+static const unsigned short aplKeysymsToUnicode[] = {
+ 0x0000, 0x0000, 0x0000, 0x003c, 0x0000, 0x0000, 0x003e, 0x0000,
+ 0x2228, 0x2227, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x00af, 0x0000, 0x22a5, 0x2229, 0x230a, 0x0000, 0x005f, 0x0000,
+ 0x0000, 0x0000, 0x2218, 0x0000, 0x2395, 0x0000, 0x22a4, 0x25cb,
+ 0x0000, 0x0000, 0x0000, 0x2308, 0x0000, 0x0000, 0x222a, 0x0000,
+ 0x2283, 0x0000, 0x2282, 0x0000, 0x22a2, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x22a3, 0x0000, 0x0000, 0x0000
+};
+
+static const unsigned short koreanKeysymsToUnicode[] = {
+ 0x0000, 0x3131, 0x3132, 0x3133, 0x3134, 0x3135, 0x3136, 0x3137,
+ 0x3138, 0x3139, 0x313a, 0x313b, 0x313c, 0x313d, 0x313e, 0x313f,
+ 0x3140, 0x3141, 0x3142, 0x3143, 0x3144, 0x3145, 0x3146, 0x3147,
+ 0x3148, 0x3149, 0x314a, 0x314b, 0x314c, 0x314d, 0x314e, 0x314f,
+ 0x3150, 0x3151, 0x3152, 0x3153, 0x3154, 0x3155, 0x3156, 0x3157,
+ 0x3158, 0x3159, 0x315a, 0x315b, 0x315c, 0x315d, 0x315e, 0x315f,
+ 0x3160, 0x3161, 0x3162, 0x3163, 0x11a8, 0x11a9, 0x11aa, 0x11ab,
+ 0x11ac, 0x11ad, 0x11ae, 0x11af, 0x11b0, 0x11b1, 0x11b2, 0x11b3,
+ 0x11b4, 0x11b5, 0x11b6, 0x11b7, 0x11b8, 0x11b9, 0x11ba, 0x11bb,
+ 0x11bc, 0x11bd, 0x11be, 0x11bf, 0x11c0, 0x11c1, 0x11c2, 0x316d,
+ 0x3171, 0x3178, 0x317f, 0x3181, 0x3184, 0x3186, 0x318d, 0x318e,
+ 0x11eb, 0x11f0, 0x11f9, 0x0000, 0x0000, 0x0000, 0x0000, 0x20a9
+};
+
+static QChar keysymToUnicode(unsigned char byte3, unsigned char byte4)
+{
+ switch (byte3) {
+ case 0x04:
+ // katakana
+ if (byte4 > 0xa0 && byte4 < 0xe0)
+ return QChar(katakanaKeysymsToUnicode[byte4 - 0xa0]);
+ else if (byte4 == 0x7e)
+ return QChar(0x203e); // Overline
+ break;
+ case 0x06:
+ // russian, use lookup table
+ if (byte4 > 0xa0)
+ return QChar(cyrillicKeysymsToUnicode[byte4 - 0xa0]);
+ break;
+ case 0x07:
+ // greek
+ if (byte4 > 0xa0)
+ return QChar(greekKeysymsToUnicode[byte4 - 0xa0]);
+ break;
+ case 0x08:
+ // technical
+ if (byte4 > 0xa0)
+ return QChar(technicalKeysymsToUnicode[byte4 - 0xa0]);
+ break;
+ case 0x09:
+ // special
+ if (byte4 >= 0xe0)
+ return QChar(specialKeysymsToUnicode[byte4 - 0xe0]);
+ break;
+ case 0x0a:
+ // publishing
+ if (byte4 > 0xa0)
+ return QChar(publishingKeysymsToUnicode[byte4 - 0xa0]);
+ break;
+ case 0x0b:
+ // APL
+ if (byte4 > 0xa0)
+ return QChar(aplKeysymsToUnicode[byte4 - 0xa0]);
+ break;
+ case 0x0e:
+ // Korean
+ if (byte4 > 0xa0)
+ return QChar(koreanKeysymsToUnicode[byte4 - 0xa0]);
+ break;
+ default:
+ break;
+ }
+ return QChar(0x0);
+}
+#endif
+
+static QString translateKeySym(KeySym keysym, uint xmodifiers,
+ int &code, Qt::KeyboardModifiers &modifiers,
+ QByteArray &chars, int &count)
+{
+ // all keysyms smaller than 0xff00 are actally keys that can be mapped to unicode chars
+
+ extern QTextCodec *qt_input_mapper; // from qapplication_x11.cpp
+ QTextCodec *mapper = qt_input_mapper;
+ QChar converted;
+
+ if (count == 0 && keysym < 0xff00) {
+ unsigned char byte3 = (unsigned char)(keysym >> 8);
+ int mib = -1;
+ switch(byte3) {
+ case 0: // Latin 1
+ case 1: // Latin 2
+ case 2: //latin 3
+ case 3: // latin4
+ mib = byte3 + 4; break;
+ case 5: // arabic
+ mib = 82; break;
+ case 12: // Hebrew
+ mib = 85; break;
+ case 13: // Thai
+ mib = 2259; break;
+ case 4: // kana
+ case 6: // cyrillic
+ case 7: // greek
+ case 8: // technical, no mapping here at the moment
+ case 9: // Special
+ case 10: // Publishing
+ case 11: // APL
+ case 14: // Korean, no mapping
+ mib = -1; // manual conversion
+ mapper = 0;
+#if !defined(QT_NO_XIM)
+ converted = keysymToUnicode(byte3, keysym & 0xff);
+#endif
+ case 0x20:
+ // currency symbols
+ if (keysym >= 0x20a0 && keysym <= 0x20ac) {
+ mib = -1; // manual conversion
+ mapper = 0;
+ converted = (uint)keysym;
+ }
+ break;
+ default:
+ break;
+ }
+ if (mib != -1) {
+ mapper = QTextCodec::codecForMib(mib);
+ if (chars.isEmpty())
+ chars.resize(1);
+ chars[0] = (unsigned char) (keysym & 0xff); // get only the fourth bit for conversion later
+ count++;
+ }
+ } else if (keysym >= 0x1000000 && keysym <= 0x100ffff) {
+ converted = (ushort) (keysym - 0x1000000);
+ mapper = 0;
+ }
+ if (count < (int)chars.size()-1)
+ chars[count] = '\0';
+
+ QString text;
+ if (!mapper && converted.unicode() != 0x0) {
+ text = converted;
+ } else if (!chars.isEmpty()) {
+ // convert chars (8bit) to text (unicode).
+ if (mapper)
+ text = mapper->toUnicode(chars.data(), count, 0);
+ if (text.isEmpty()) {
+ // no mapper, or codec couldn't convert to unicode (this
+ // can happen when running in the C locale or with no LANG
+ // set). try converting from latin-1
+ text = QString::fromLatin1(chars);
+ }
+ }
+
+ modifiers = X11->translateModifiers(xmodifiers);
+
+ // Commentary in X11/keysymdef says that X codes match ASCII, so it
+ // is safe to use the locale functions to process X codes in ISO8859-1.
+ //
+ // This is mainly for compatibility - applications should not use the
+ // Qt keycodes between 128 and 255, but should rather use the
+ // QKeyEvent::text().
+ //
+ extern QTextCodec *qt_input_mapper; // from qapplication_x11.cpp
+ if (keysym < 128 || (keysym < 256 && (!qt_input_mapper || qt_input_mapper->mibEnum()==4))) {
+ // upper-case key, if known
+ code = isprint((int)keysym) ? toupper((int)keysym) : 0;
+ } else if (keysym >= XK_F1 && keysym <= XK_F35) {
+ // function keys
+ code = Qt::Key_F1 + ((int)keysym - XK_F1);
+ } else if (keysym >= XK_KP_Space && keysym <= XK_KP_9) {
+ if (keysym >= XK_KP_0) {
+ // numeric keypad keys
+ code = Qt::Key_0 + ((int)keysym - XK_KP_0);
+ } else {
+ code = translateKeySym(keysym);
+ }
+ modifiers |= Qt::KeypadModifier;
+ } else if (text.length() == 1 && text.unicode()->unicode() > 0x1f && text.unicode()->unicode() != 0x7f && !(keysym >= XK_dead_grave && keysym <= XK_dead_horn)) {
+ code = text.unicode()->toUpper().unicode();
+ } else {
+ // any other keys
+ code = translateKeySym(keysym);
+
+ if (code == Qt::Key_Tab && (modifiers & Qt::ShiftModifier)) {
+ // map shift+tab to shift+backtab, QShortcutMap knows about it
+ // and will handle it.
+ code = Qt::Key_Backtab;
+ text = QString();
+ }
+ }
+
+ return text;
+}
+
+extern bool qt_use_rtl_extensions; // from qapplication_x11.cpp
+
+bool QKeyMapperPrivate::translateKeyEventInternal(QWidget *keyWidget,
+ const XEvent *event,
+ KeySym &keysym,
+ int& count,
+ QString& text,
+ Qt::KeyboardModifiers &modifiers,
+ int& code,
+ QEvent::Type &type,
+ bool statefulTranslation)
+{
+ XKeyEvent xkeyevent = event->xkey;
+ int keycode = event->xkey.keycode;
+ // save the modifier state, we will use the keystate uint later by passing
+ // it to translateButtonState
+ uint keystate = event->xkey.state;
+
+ type = (event->type == XKeyPress) ? QEvent::KeyPress : QEvent::KeyRelease;
+
+ static int directionKeyEvent = 0;
+ static unsigned int lastWinId = 0;
+
+ // translate pending direction change
+ if (statefulTranslation && qt_use_rtl_extensions && type == QEvent::KeyRelease) {
+ if (directionKeyEvent == Qt::Key_Direction_R || directionKeyEvent == Qt::Key_Direction_L) {
+ type = QEvent::KeyPress;
+ code = directionKeyEvent;
+ text = QString();
+ directionKeyEvent = 0;
+ lastWinId = 0;
+ return true;
+ } else {
+ directionKeyEvent = 0;
+ lastWinId = 0;
+ }
+ }
+
+ // some XmbLookupString implementations don't return buffer overflow correctly,
+ // so we increase the input buffer to allow for long strings...
+ // 256 chars * 2 bytes + 1 null-term == 513 bytes
+ QByteArray chars;
+ chars.resize(513);
+
+ count = XLookupString(&xkeyevent, chars.data(), chars.size(), &keysym, 0);
+ if (count && !keycode) {
+ extern int qt_ximComposingKeycode; // from qapplication_x11.cpp
+ keycode = qt_ximComposingKeycode;
+ qt_ximComposingKeycode = 0;
+ }
+
+ // translate the keysym + xmodifiers to Qt::Key_* + Qt::KeyboardModifiers
+ text = translateKeySym(keysym, keystate, code, modifiers, chars, count);
+
+ // Watch for keypresses and if its a key belonging to the Ctrl-Shift
+ // direction-changing accel, remember it.
+ // We keep track of those keys instead of using the event's state
+ // (to figure out whether the Ctrl modifier is held while Shift is pressed,
+ // or Shift is held while Ctrl is pressed) since the 'state' doesn't tell
+ // us whether the modifier held is Left or Right.
+ if (statefulTranslation && qt_use_rtl_extensions && type == QEvent::KeyPress) {
+ if (keysym == XK_Control_L || keysym == XK_Control_R
+ || keysym == XK_Shift_L || keysym == XK_Shift_R) {
+ if (!directionKeyEvent) {
+ directionKeyEvent = keysym;
+ // This code exists in order to check that
+ // the event is occurred in the same widget.
+ lastWinId = keyWidget->internalWinId();
+ }
+ } else {
+ // this can no longer be a direction-changing accel.
+ // if any other key was pressed.
+ directionKeyEvent = Qt::Key_Space;
+ }
+
+ if (directionKeyEvent && lastWinId == keyWidget->internalWinId()) {
+ if ((keysym == XK_Shift_L && directionKeyEvent == XK_Control_L)
+ || (keysym == XK_Control_L && directionKeyEvent == XK_Shift_L)) {
+ directionKeyEvent = Qt::Key_Direction_L;
+ } else if ((keysym == XK_Shift_R && directionKeyEvent == XK_Control_R)
+ || (keysym == XK_Control_R && directionKeyEvent == XK_Shift_R)) {
+ directionKeyEvent = Qt::Key_Direction_R;
+ }
+ } else if (directionKeyEvent == Qt::Key_Direction_L
+ || directionKeyEvent == Qt::Key_Direction_R) {
+ directionKeyEvent = Qt::Key_Space; // invalid
+ }
+ }
+
+ return true;
+}
+
+
+struct qt_auto_repeat_data
+{
+ // match the window and keycode with timestamp delta of 10 ms
+ Window window;
+ KeyCode keycode;
+ Time timestamp;
+
+ // queue scanner state
+ bool release;
+ bool error;
+};
+
+#if defined(Q_C_CALLBACKS)
+extern "C" {
+#endif
+
+static Bool qt_keypress_scanner(Display *, XEvent *event, XPointer arg)
+{
+ if (event->type != XKeyPress && event->type != XKeyRelease)
+ return false;
+
+ qt_auto_repeat_data *data = (qt_auto_repeat_data *) arg;
+ if (data->error)
+ return false;
+
+ if (event->xkey.window != data->window ||
+ event->xkey.keycode != data->keycode) {
+ // deal breakers: key events in a different window or an event
+ // with a different key code
+ data->error = true;
+ return false;
+ }
+
+ if (event->type == XKeyPress) {
+ data->error = (! data->release || event->xkey.time - data->timestamp > 10);
+ return (! data->error);
+ }
+
+ // must be XKeyRelease event
+ if (data->release) {
+ // found a second release
+ data->error = true;
+ return false;
+ }
+
+ // found a single release
+ data->release = true;
+ data->timestamp = event->xkey.time;
+
+ return false;
+}
+
+static Bool qt_keyrelease_scanner(Display *, XEvent *event, XPointer arg)
+{
+ const qt_auto_repeat_data *data = (const qt_auto_repeat_data *) arg;
+ return (event->type == XKeyRelease &&
+ event->xkey.window == data->window &&
+ event->xkey.keycode == data->keycode);
+}
+
+#if defined(Q_C_CALLBACKS)
+}
+#endif
+
+bool QKeyMapperPrivate::translateKeyEvent(QWidget *keyWidget, const XEvent *event, bool grab)
+{
+ int code = -1;
+ int count = 0;
+ Qt::KeyboardModifiers modifiers;
+
+ if (qt_sm_blockUserInput) // block user interaction during session management
+ return true;
+
+ Display *dpy = X11->display;
+
+ if (!keyWidget->isEnabled())
+ return true;
+
+ QEvent::Type type;
+ bool autor = false;
+ QString text;
+
+ KeySym keysym = 0;
+ translateKeyEventInternal(keyWidget, event, keysym, count, text, modifiers, code, type);
+
+ // was this the last auto-repeater?
+ qt_auto_repeat_data auto_repeat_data;
+ auto_repeat_data.window = event->xkey.window;
+ auto_repeat_data.keycode = event->xkey.keycode;
+ auto_repeat_data.timestamp = event->xkey.time;
+
+ static uint curr_autorep = 0;
+ if (event->type == XKeyPress) {
+ if (curr_autorep == event->xkey.keycode) {
+ autor = true;
+ curr_autorep = 0;
+ }
+ } else {
+ // look ahead for auto-repeat
+ XEvent nextpress;
+
+ auto_repeat_data.release = true;
+ auto_repeat_data.error = false;
+ if (XCheckIfEvent(dpy, &nextpress, &qt_keypress_scanner,
+ (XPointer) &auto_repeat_data)) {
+ autor = true;
+
+ // Put it back... we COULD send the event now and not need
+ // the static curr_autorep variable.
+ XPutBackEvent(dpy,&nextpress);
+ }
+ curr_autorep = autor ? event->xkey.keycode : 0;
+ }
+
+#if defined QT3_SUPPORT && !defined(QT_NO_SHORTCUT)
+ // process accelerators before doing key compression
+ if (type == QEvent::KeyPress && !grab
+ && QApplicationPrivate::instance()->use_compat()) {
+ // send accel events if the keyboard is not grabbed
+ QKeyEventEx a(type, code, modifiers, text, autor, qMax(qMax(count,1), int(text.length())),
+ event->xkey.keycode, keysym, event->xkey.state);
+ if (QApplicationPrivate::instance()->qt_tryAccelEvent(keyWidget, &a))
+ return true;
+ }
+#endif
+
+#ifndef QT_NO_IM
+ QInputContext *qic = keyWidget->inputContext();
+#endif
+
+ // compress keys
+ if (!text.isEmpty() && keyWidget->testAttribute(Qt::WA_KeyCompression) &&
+#ifndef QT_NO_IM
+ // Ordinary input methods require discrete key events to work
+ // properly, so key compression has to be disabled when input
+ // context exists.
+ //
+ // And further consideration, some complex input method
+ // require all key press/release events discretely even if
+ // the input method awares of key compression and compressed
+ // keys are ordinary alphabets. For example, the uim project
+ // is planning to implement "combinational shift" feature for
+ // a Japanese input method, uim-skk. It will work as follows.
+ //
+ // 1. press "r"
+ // 2. press "u"
+ // 3. release both "r" and "u" in arbitrary order
+ // 4. above key sequence generates "Ru"
+ //
+ // Of course further consideration about other participants
+ // such as key repeat mechanism is required to implement such
+ // feature.
+ !qic &&
+#endif // QT_NO_IM
+ // do not compress keys if the key event we just got above matches
+ // one of the key ranges used to compute stopCompression
+ !((code >= Qt::Key_Escape && code <= Qt::Key_SysReq)
+ || (code >= Qt::Key_Home && code <= Qt::Key_PageDown)
+ || (code >= Qt::Key_Super_L && code <= Qt::Key_Direction_R)
+ || (code == 0)
+ || (text.length() == 1 && text.unicode()->unicode() == '\n'))) {
+ // the widget wants key compression so it gets it
+
+ // sync the event queue, this makes key compress work better
+ XSync(dpy, false);
+
+ for (;;) {
+ XEvent evRelease;
+ XEvent evPress;
+ if (!XCheckTypedWindowEvent(dpy,event->xkey.window,
+ XKeyRelease,&evRelease))
+ break;
+ if (!XCheckTypedWindowEvent(dpy,event->xkey.window,
+ XKeyPress,&evPress)) {
+ XPutBackEvent(dpy, &evRelease);
+ break;
+ }
+ QString textIntern;
+ int codeIntern = -1;
+ int countIntern = 0;
+ Qt::KeyboardModifiers modifiersIntern;
+ QEvent::Type t;
+ KeySym keySymIntern;
+ translateKeyEventInternal(keyWidget, &evPress, keySymIntern, countIntern, textIntern,
+ modifiersIntern, codeIntern, t);
+ // use stopCompression to stop key compression for the following
+ // key event ranges:
+ bool stopCompression =
+ // 1) misc keys
+ (codeIntern >= Qt::Key_Escape && codeIntern <= Qt::Key_SysReq)
+ // 2) cursor movement
+ || (codeIntern >= Qt::Key_Home && codeIntern <= Qt::Key_PageDown)
+ // 3) extra keys
+ || (codeIntern >= Qt::Key_Super_L && codeIntern <= Qt::Key_Direction_R)
+ // 4) something that a) doesn't translate to text or b) translates
+ // to newline text
+ || (codeIntern == 0)
+ || (textIntern.length() == 1 && textIntern.unicode()->unicode() == '\n')
+ || (codeIntern == Qt::Key_unknown);
+
+ if (modifiersIntern == modifiers && !textIntern.isEmpty() && !stopCompression) {
+ text += textIntern;
+ count += countIntern;
+ } else {
+ XPutBackEvent(dpy, &evPress);
+ XPutBackEvent(dpy, &evRelease);
+ break;
+ }
+ }
+ }
+
+ // autorepeat compression makes sense for all widgets (Windows
+ // does it automatically ....)
+ if (event->type == XKeyPress && text.length() <= 1
+#ifndef QT_NO_IM
+ // input methods need discrete key events
+ && !qic
+#endif// QT_NO_IM
+ ) {
+ XEvent dummy;
+
+ for (;;) {
+ auto_repeat_data.release = false;
+ auto_repeat_data.error = false;
+ if (! XCheckIfEvent(dpy, &dummy, &qt_keypress_scanner,
+ (XPointer) &auto_repeat_data))
+ break;
+ if (! XCheckIfEvent(dpy, &dummy, &qt_keyrelease_scanner,
+ (XPointer) &auto_repeat_data))
+ break;
+
+ count++;
+ if (!text.isEmpty())
+ text += text[0];
+ }
+ }
+
+ return QKeyMapper::sendKeyEvent(keyWidget, grab, type, code, modifiers, text, autor,
+ qMax(qMax(count,1), int(text.length())),
+ event->xkey.keycode, keysym, event->xkey.state);
+}
+
+bool QKeyMapper::sendKeyEvent(QWidget *keyWidget, bool grab,
+ QEvent::Type type, int code, Qt::KeyboardModifiers modifiers,
+ const QString &text, bool autorepeat, int count,
+ quint32 nativeScanCode, quint32 nativeVirtualKey, quint32 nativeModifiers,
+ bool *)
+{
+ // try the menukey first
+ if (type == QEvent::KeyPress && code == Qt::Key_Menu) {
+ QVariant v = keyWidget->inputMethodQuery(Qt::ImMicroFocus);
+ QPoint globalPos;
+ QPoint pos;
+ if (v.isNull()) {
+ globalPos = QCursor::pos();
+ pos = keyWidget->mapFromGlobal(globalPos);
+ } else {
+ pos = v.toRect().center();
+ globalPos = keyWidget->mapToGlobal(pos);
+ }
+ QContextMenuEvent e(QContextMenuEvent::Keyboard, pos, globalPos);
+ qt_sendSpontaneousEvent(keyWidget, &e);
+ if(e.isAccepted())
+ return true;
+ }
+
+ Q_UNUSED(grab);
+ QKeyEventEx e(type, code, modifiers, text, autorepeat, qMax(qMax(count,1), int(text.length())),
+ nativeScanCode, nativeVirtualKey, nativeModifiers);
+ return qt_sendSpontaneousEvent(keyWidget, &e);
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/kernel/qkeymapper_x11_p.cpp b/src/gui/kernel/qkeymapper_x11_p.cpp
new file mode 100644
index 0000000000..cbf26caf6d
--- /dev/null
+++ b/src/gui/kernel/qkeymapper_x11_p.cpp
@@ -0,0 +1,488 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+// This file is auto-generated, do not edit!
+
+static struct {
+ const char *layout;
+ const char *variant; // 0 means any variant
+ Qt::LayoutDirection direction;
+ QLocale::Language language;
+ QLocale::Country country;
+} xkbLayoutData[] = {
+ // name = us, description = U.S. English
+ { "us", "", Qt::LeftToRight, QLocale::English, QLocale::UnitedStates },
+ // name = us:intl, description = U.S. English
+ { "us", "intl", Qt::LeftToRight, QLocale::English, QLocale::UnitedStates },
+ // name = us:alt-intl, description = U.S. English
+ { "us", "alt-intl", Qt::LeftToRight, QLocale::English, QLocale::UnitedStates },
+ // name = us:dvorak, description = U.S. English
+ { "us", "dvorak", Qt::LeftToRight, QLocale::English, QLocale::UnitedStates },
+ // name = us:rus, description = U.S. English
+ { "us", "rus", Qt::LeftToRight, QLocale::Russian, QLocale::UnitedStates },
+ // name = ara, description = Arabic
+ { "ara", "", Qt::RightToLeft, QLocale::Arabic, QLocale::UnitedArabEmirates },
+ // name = ara:azerty, description = Arabic
+ { "ara", "azerty", Qt::RightToLeft, QLocale::Arabic, QLocale::UnitedArabEmirates },
+ // name = ara:azerty_digits, description = Arabic
+ { "ara", "azerty_digits", Qt::RightToLeft, QLocale::Arabic, QLocale::UnitedArabEmirates },
+ // name = ara:digits, description = Arabic
+ { "ara", "digits", Qt::RightToLeft, QLocale::Arabic, QLocale::UnitedArabEmirates },
+ // name = ara:qwerty, description = Arabic
+ { "ara", "qwerty", Qt::RightToLeft, QLocale::Arabic, QLocale::UnitedArabEmirates },
+ // name = ara:qwerty_digits, description = Arabic
+ { "ara", "qwerty_digits", Qt::RightToLeft, QLocale::Arabic, QLocale::UnitedArabEmirates },
+ // name = al, description = Albania
+ { "al", "", Qt::LeftToRight, QLocale::Albanian, QLocale::Albania },
+ // name = am, description = Armenia
+ { "am", "", Qt::LeftToRight, QLocale::Armenian, QLocale::Armenia },
+ // name = am:phonetic, description = Armenia
+ { "am", "phonetic", Qt::LeftToRight, QLocale::Armenian, QLocale::Armenia },
+ // name = az, description = Azerbaijan
+ { "az", "", Qt::LeftToRight, QLocale::Azerbaijani, QLocale::Azerbaijan },
+ // name = az:cyrillic, description = Azerbaijan
+ { "az", "cyrillic", Qt::LeftToRight, QLocale::Azerbaijani, QLocale::Azerbaijan },
+ // name = by, description = Belarus
+ { "by", "", Qt::LeftToRight, QLocale::Byelorussian, QLocale::Belarus },
+ // name = by:winkeys, description = Belarus
+ { "by", "winkeys", Qt::LeftToRight, QLocale::Byelorussian, QLocale::Belarus },
+ // name = be, description = Belgium
+ { "be", "", Qt::LeftToRight, QLocale::Dutch, QLocale::Belgium },
+ // name = be:iso-alternate, description = Belgium
+ { "be", "iso-alternate", Qt::LeftToRight, QLocale::Dutch, QLocale::Belgium },
+ // name = be:nodeadkeys, description = Belgium
+ { "be", "nodeadkeys", Qt::LeftToRight, QLocale::Dutch, QLocale::Belgium },
+ // name = be:sundeadkeys, description = Belgium
+ { "be", "sundeadkeys", Qt::LeftToRight, QLocale::Dutch, QLocale::Belgium },
+ // name = bd, description = Bangladesh
+ { "bd", "", Qt::LeftToRight, QLocale::Bengali, QLocale::Bangladesh },
+ // name = bd:probhat, description = Bangladesh
+ { "bd", "probhat", Qt::LeftToRight, QLocale::Bengali, QLocale::Bangladesh },
+ // name = in, description = India
+ { "in", "", Qt::LeftToRight, QLocale::Hindi, QLocale::India },
+ // name = in:ben, description = India
+ { "in", "ben", Qt::LeftToRight, QLocale::Bengali, QLocale::India },
+ // name = in:ben_probhat, description = India
+ { "in", "ben_probhat", Qt::LeftToRight, QLocale::Bengali, QLocale::India },
+ // name = in:guj, description = India
+ { "in", "guj", Qt::LeftToRight, QLocale::Gujarati, QLocale::India },
+ // name = in:guru, description = India
+ { "in", "guru", Qt::LeftToRight, QLocale::Punjabi, QLocale::India },
+ // name = in:kan, description = India
+ { "in", "kan", Qt::LeftToRight, QLocale::Kannada, QLocale::India },
+ // name = in:mal, description = India
+ { "in", "mal", Qt::LeftToRight, QLocale::Malayalam, QLocale::India },
+ // name = in:ori, description = India
+ { "in", "ori", Qt::LeftToRight, QLocale::Oriya, QLocale::India },
+ // name = in:tam_unicode, description = India
+ { "in", "tam_unicode", Qt::LeftToRight, QLocale::Tamil, QLocale::India },
+ // name = in:tam_TAB, description = India
+ { "in", "tam_TAB", Qt::LeftToRight, QLocale::Tamil, QLocale::India },
+ // name = in:tam_TSCII, description = India
+ { "in", "tam_TSCII", Qt::LeftToRight, QLocale::Tamil, QLocale::India },
+ // name = in:tam, description = India
+ { "in", "tam", Qt::LeftToRight, QLocale::Tamil, QLocale::India },
+ // name = in:tel, description = India
+ { "in", "tel", Qt::LeftToRight, QLocale::Telugu, QLocale::India },
+ // name = in:urd, description = India
+ { "in", "urd", Qt::RightToLeft, QLocale::Urdu, QLocale::India },
+ // name = ba, description = Bosnia and Herzegovina
+ { "ba", "", Qt::LeftToRight, QLocale::Bosnian, QLocale::BosniaAndHerzegowina },
+ // name = br, description = Brazil
+ { "br", "", Qt::LeftToRight, QLocale::Portuguese, QLocale::Brazil },
+ // name = br:nodeadkeys, description = Brazil
+ { "br", "nodeadkeys", Qt::LeftToRight, QLocale::Portuguese, QLocale::Brazil },
+ // name = bg, description = Bulgaria
+ { "bg", "", Qt::LeftToRight, QLocale::Bulgarian, QLocale::Bulgaria },
+ // name = bg:phonetic, description = Bulgaria
+ { "bg", "phonetic", Qt::LeftToRight, QLocale::Bulgarian, QLocale::Bulgaria },
+ // name = mm, description = Myanmar
+ { "mm", "", Qt::LeftToRight, QLocale::Burmese, QLocale::Myanmar },
+ // name = ca, description = Canada
+ { "ca", "", Qt::LeftToRight, QLocale::English, QLocale::Canada },
+ // name = ca:fr-dvorak, description = Canada
+ { "ca", "fr-dvorak", Qt::LeftToRight, QLocale::French, QLocale::Canada },
+ // name = ca:fr-legacy, description = Canada
+ { "ca", "fr-legacy", Qt::LeftToRight, QLocale::French, QLocale::Canada },
+ // name = ca:multi, description = Canada
+ { "ca", "multi", Qt::LeftToRight, QLocale::English, QLocale::Canada },
+ // name = ca:multi-2gr, description = Canada
+ { "ca", "multi-2gr", Qt::LeftToRight, QLocale::English, QLocale::Canada },
+ // name = ca:ike, description = Canada
+ { "ca", "ike", Qt::LeftToRight, QLocale::Inuktitut, QLocale::Canada },
+ // name = hr, description = Croatia
+ { "hr", "", Qt::LeftToRight, QLocale::Croatian, QLocale::Croatia },
+ // name = hr:us, description = Croatia
+ { "hr", "us", Qt::LeftToRight, QLocale::Croatian, QLocale::Croatia },
+ // name = cz, description = Czechia
+ { "cz", "", Qt::LeftToRight, QLocale::Czech, QLocale::CzechRepublic },
+ // name = cz:bksl, description = Czechia
+ { "cz", "bksl", Qt::LeftToRight, QLocale::Czech, QLocale::CzechRepublic },
+ // name = cz:qwerty, description = Czechia
+ { "cz", "qwerty", Qt::LeftToRight, QLocale::Czech, QLocale::CzechRepublic },
+ // name = cz:qwerty_bksl, description = Czechia
+ { "cz", "qwerty_bksl", Qt::LeftToRight, QLocale::Czech, QLocale::CzechRepublic },
+ // name = dk, description = Denmark
+ { "dk", "", Qt::LeftToRight, QLocale::Danish, QLocale::Denmark },
+ // name = dk:nodeadkeys, description = Denmark
+ { "dk", "nodeadkeys", Qt::LeftToRight, QLocale::Danish, QLocale::Denmark },
+ // name = nl, description = Netherlands
+ { "nl", "", Qt::LeftToRight, QLocale::Dutch, QLocale::Netherlands },
+ // name = bt, description = Bhutan
+ { "bt", "", Qt::LeftToRight, QLocale::Bhutani, QLocale::Bhutan },
+ // name = ee, description = Estonia
+ { "ee", "", Qt::LeftToRight, QLocale::Estonian, QLocale::Estonia },
+ // name = ee:nodeadkeys, description = Estonia
+ { "ee", "nodeadkeys", Qt::LeftToRight, QLocale::Estonian, QLocale::Estonia },
+ // name = ir, description = Iran
+ { "ir", "", Qt::RightToLeft, QLocale::Persian, QLocale::Iran },
+ // name = fo, description = Faroe Islands
+ { "fo", "", Qt::LeftToRight, QLocale::Faroese, QLocale::FaroeIslands },
+ // name = fo:nodeadkeys, description = Faroe Islands
+ { "fo", "nodeadkeys", Qt::LeftToRight, QLocale::Faroese, QLocale::FaroeIslands },
+ // name = fi, description = Finland
+ { "fi", "", Qt::LeftToRight, QLocale::Finnish, QLocale::Finland },
+ // name = fi:nodeadkeys, description = Finland
+ { "fi", "nodeadkeys", Qt::LeftToRight, QLocale::Finnish, QLocale::Finland },
+ // name = fi:smi, description = Finland
+ { "fi", "smi", Qt::LeftToRight, QLocale::Finnish, QLocale::Finland },
+ // name = fr, description = France
+ { "fr", "", Qt::LeftToRight, QLocale::French, QLocale::France },
+ // name = fr:nodeadkeys, description = France
+ { "fr", "nodeadkeys", Qt::LeftToRight, QLocale::French, QLocale::France },
+ // name = fr:sundeadkeys, description = France
+ { "fr", "sundeadkeys", Qt::LeftToRight, QLocale::French, QLocale::France },
+ // name = fr:latin9, description = France
+ { "fr", "latin9", Qt::LeftToRight, QLocale::French, QLocale::France },
+ // name = fr:latin9_nodeadkeys, description = France
+ { "fr", "latin9_nodeadkeys", Qt::LeftToRight, QLocale::French, QLocale::France },
+ // name = fr:latin9_sundeadkeys, description = France
+ { "fr", "latin9_sundeadkeys", Qt::LeftToRight, QLocale::French, QLocale::France },
+ // name = fr:dvorak, description = France
+ { "fr", "dvorak", Qt::LeftToRight, QLocale::French, QLocale::France },
+ // name = ge, description = Georgia
+ { "ge", "", Qt::LeftToRight, QLocale::Georgian, QLocale::Georgia },
+ // name = ge:ru, description = Georgia
+ { "ge", "ru", Qt::LeftToRight, QLocale::Russian, QLocale::Georgia },
+ // name = de, description = Germany
+ { "de", "", Qt::LeftToRight, QLocale::German, QLocale::Germany },
+ // name = de:deadacute, description = Germany
+ { "de", "deadacute", Qt::LeftToRight, QLocale::German, QLocale::Germany },
+ // name = de:deadgraveacute, description = Germany
+ { "de", "deadgraveacute", Qt::LeftToRight, QLocale::German, QLocale::Germany },
+ // name = de:nodeadkeys, description = Germany
+ { "de", "nodeadkeys", Qt::LeftToRight, QLocale::German, QLocale::Germany },
+ // name = de:ro, description = Germany
+ { "de", "ro", Qt::LeftToRight, QLocale::Romanian, QLocale::Germany },
+ // name = de:ro_nodeadkeys, description = Germany
+ { "de", "ro_nodeadkeys", Qt::LeftToRight, QLocale::Romanian, QLocale::Germany },
+ // name = de:dvorak, description = Germany
+ { "de", "dvorak", Qt::LeftToRight, QLocale::German, QLocale::Germany },
+ // name = gr, description = Greece
+ { "gr", "", Qt::LeftToRight, QLocale::Greek, QLocale::Greece },
+ // name = gr:extended, description = Greece
+ { "gr", "extended", Qt::LeftToRight, QLocale::Greek, QLocale::Greece },
+ // name = gr:nodeadkeys, description = Greece
+ { "gr", "nodeadkeys", Qt::LeftToRight, QLocale::Greek, QLocale::Greece },
+ // name = gr:polytonic, description = Greece
+ { "gr", "polytonic", Qt::LeftToRight, QLocale::Greek, QLocale::Greece },
+ // name = hu, description = Hungary
+ { "hu", "", Qt::LeftToRight, QLocale::Hungarian, QLocale::Hungary },
+ // name = hu:standard, description = Hungary
+ { "hu", "standard", Qt::LeftToRight, QLocale::Hungarian, QLocale::Hungary },
+ // name = hu:nodeadkeys, description = Hungary
+ { "hu", "nodeadkeys", Qt::LeftToRight, QLocale::Hungarian, QLocale::Hungary },
+ // name = hu:qwerty, description = Hungary
+ { "hu", "qwerty", Qt::LeftToRight, QLocale::Hungarian, QLocale::Hungary },
+ // name = hu:101_qwertz_comma_dead, description = Hungary
+ { "hu", "101_qwertz_comma_dead", Qt::LeftToRight, QLocale::Hungarian, QLocale::Hungary },
+ // name = hu:101_qwertz_comma_nodead, description = Hungary
+ { "hu", "101_qwertz_comma_nodead", Qt::LeftToRight, QLocale::Hungarian, QLocale::Hungary },
+ // name = hu:101_qwertz_dot_dead, description = Hungary
+ { "hu", "101_qwertz_dot_dead", Qt::LeftToRight, QLocale::Hungarian, QLocale::Hungary },
+ // name = hu:101_qwertz_dot_nodead, description = Hungary
+ { "hu", "101_qwertz_dot_nodead", Qt::LeftToRight, QLocale::Hungarian, QLocale::Hungary },
+ // name = hu:101_qwerty_comma_dead, description = Hungary
+ { "hu", "101_qwerty_comma_dead", Qt::LeftToRight, QLocale::Hungarian, QLocale::Hungary },
+ // name = hu:101_qwerty_comma_nodead, description = Hungary
+ { "hu", "101_qwerty_comma_nodead", Qt::LeftToRight, QLocale::Hungarian, QLocale::Hungary },
+ // name = hu:101_qwerty_dot_dead, description = Hungary
+ { "hu", "101_qwerty_dot_dead", Qt::LeftToRight, QLocale::Hungarian, QLocale::Hungary },
+ // name = hu:101_qwerty_dot_nodead, description = Hungary
+ { "hu", "101_qwerty_dot_nodead", Qt::LeftToRight, QLocale::Hungarian, QLocale::Hungary },
+ // name = hu:102_qwertz_comma_dead, description = Hungary
+ { "hu", "102_qwertz_comma_dead", Qt::LeftToRight, QLocale::Hungarian, QLocale::Hungary },
+ // name = hu:102_qwertz_comma_nodead, description = Hungary
+ { "hu", "102_qwertz_comma_nodead", Qt::LeftToRight, QLocale::Hungarian, QLocale::Hungary },
+ // name = hu:102_qwertz_dot_dead, description = Hungary
+ { "hu", "102_qwertz_dot_dead", Qt::LeftToRight, QLocale::Hungarian, QLocale::Hungary },
+ // name = hu:102_qwertz_dot_nodead, description = Hungary
+ { "hu", "102_qwertz_dot_nodead", Qt::LeftToRight, QLocale::Hungarian, QLocale::Hungary },
+ // name = hu:102_qwerty_comma_dead, description = Hungary
+ { "hu", "102_qwerty_comma_dead", Qt::LeftToRight, QLocale::Hungarian, QLocale::Hungary },
+ // name = hu:102_qwerty_comma_nodead, description = Hungary
+ { "hu", "102_qwerty_comma_nodead", Qt::LeftToRight, QLocale::Hungarian, QLocale::Hungary },
+ // name = hu:102_qwerty_dot_dead, description = Hungary
+ { "hu", "102_qwerty_dot_dead", Qt::LeftToRight, QLocale::Hungarian, QLocale::Hungary },
+ // name = hu:102_qwerty_dot_nodead, description = Hungary
+ { "hu", "102_qwerty_dot_nodead", Qt::LeftToRight, QLocale::Hungarian, QLocale::Hungary },
+ // name = is, description = Iceland
+ { "is", "", Qt::LeftToRight, QLocale::Icelandic, QLocale::Iceland },
+ // name = is:Sundeadkeys, description = Iceland
+ { "is", "Sundeadkeys", Qt::LeftToRight, QLocale::Icelandic, QLocale::Iceland },
+ // name = is:nodeadkeys, description = Iceland
+ { "is", "nodeadkeys", Qt::LeftToRight, QLocale::Icelandic, QLocale::Iceland },
+ // name = il, description = Israel
+ { "il", "", Qt::LeftToRight, QLocale::Hebrew, QLocale::Israel },
+ // name = il:lyx, description = Israel
+ { "il", "lyx", Qt::LeftToRight, QLocale::Hebrew, QLocale::Israel },
+ // name = il:si1452, description = Israel
+ { "il", "si1452", Qt::LeftToRight, QLocale::Hebrew, QLocale::Israel },
+ // name = il:phonetic, description = Israel
+ { "il", "phonetic", Qt::LeftToRight, QLocale::Hebrew, QLocale::Israel },
+ // name = it, description = Italy
+ { "it", "", Qt::LeftToRight, QLocale::Italian, QLocale::Italy },
+ // name = it:nodeadkeys, description = Italy
+ { "it", "nodeadkeys", Qt::LeftToRight, QLocale::Italian, QLocale::Italy },
+ // name = jp, description = Japan
+ { "jp", "", Qt::LeftToRight, QLocale::Japanese, QLocale::Japan },
+ // name = kg, description = Kyrgyzstan
+ { "kg", "", Qt::LeftToRight, QLocale::Kirghiz, QLocale::Kyrgyzstan },
+ // name = la, description = Laos
+ { "la", "", Qt::LeftToRight, QLocale::Laothian, QLocale::Lao },
+ // name = latam, description = Latin American
+ { "latam", "", Qt::LeftToRight, QLocale::Spanish, QLocale::Mexico },
+ // name = latam:nodeadkeys, description = Latin American
+ { "latam", "nodeadkeys", Qt::LeftToRight, QLocale::Spanish, QLocale::Mexico },
+ // name = latam:sundeadkeys, description = Latin American
+ { "latam", "sundeadkeys", Qt::LeftToRight, QLocale::Spanish, QLocale::Mexico },
+ // name = lt, description = Lithuania
+ { "lt", "", Qt::LeftToRight, QLocale::Lithuanian, QLocale::Lithuania },
+ // name = lt:std, description = Lithuania
+ { "lt", "std", Qt::LeftToRight, QLocale::Lithuanian, QLocale::Lithuania },
+ // name = lt:us, description = Lithuania
+ { "lt", "us", Qt::LeftToRight, QLocale::Lithuanian, QLocale::Lithuania },
+ // name = lv, description = Latvia
+ { "lv", "", Qt::LeftToRight, QLocale::Latvian, QLocale::Latvia },
+ // name = lv:apostrophe, description = Latvia
+ { "lv", "apostrophe", Qt::LeftToRight, QLocale::Latvian, QLocale::Latvia },
+ // name = lv:tilde, description = Latvia
+ { "lv", "tilde", Qt::LeftToRight, QLocale::Latvian, QLocale::Latvia },
+ // name = lv:fkey, description = Latvia
+ { "lv", "fkey", Qt::LeftToRight, QLocale::Latvian, QLocale::Latvia },
+ // name = mao, description = Maori
+ { "mao", "", Qt::LeftToRight, QLocale::Maori, QLocale::NewZealand },
+ // name = mkd, description = Macedonian
+ { "mkd", "", Qt::LeftToRight, QLocale::Macedonian, QLocale::Macedonia },
+ // name = mkd:nodeadkeys, description = Macedonian
+ { "mkd", "nodeadkeys", Qt::LeftToRight, QLocale::Macedonian, QLocale::Macedonia },
+ // name = mt, description = Malta
+ { "mt", "", Qt::LeftToRight, QLocale::Maltese, QLocale::Malta },
+ // name = mt:us, description = Malta
+ { "mt", "us", Qt::LeftToRight, QLocale::Maltese, QLocale::Malta },
+ // name = mn, description = Mongolia
+ { "mn", "", Qt::LeftToRight, QLocale::Mongolian, QLocale::Mongolia },
+ // name = no, description = Norway
+ { "no", "", Qt::LeftToRight, QLocale::Norwegian, QLocale::Norway },
+ // name = no:nodeadkeys, description = Norway
+ { "no", "nodeadkeys", Qt::LeftToRight, QLocale::Norwegian, QLocale::Norway },
+ // name = no:dvorak, description = Norway
+ { "no", "dvorak", Qt::LeftToRight, QLocale::Norwegian, QLocale::Norway },
+ // name = no:smi, description = Norway
+ { "no", "smi", Qt::LeftToRight, QLocale::Norwegian, QLocale::Norway },
+ // name = no:smi_nodeadkeys, description = Norway
+ { "no", "smi_nodeadkeys", Qt::LeftToRight, QLocale::Norwegian, QLocale::Norway },
+ // name = pl, description = Poland
+ { "pl", "", Qt::LeftToRight, QLocale::Polish, QLocale::Poland },
+ // name = pl:qwertz, description = Poland
+ { "pl", "qwertz", Qt::LeftToRight, QLocale::Polish, QLocale::Poland },
+ // name = pl:dvorak, description = Poland
+ { "pl", "dvorak", Qt::LeftToRight, QLocale::Polish, QLocale::Poland },
+ // name = pl:dvorak_quotes, description = Poland
+ { "pl", "dvorak_quotes", Qt::LeftToRight, QLocale::Polish, QLocale::Poland },
+ // name = pl:dvorak_altquotes, description = Poland
+ { "pl", "dvorak_altquotes", Qt::LeftToRight, QLocale::Polish, QLocale::Poland },
+ // name = pt, description = Portugal
+ { "pt", "", Qt::LeftToRight, QLocale::Portuguese, QLocale::Portugal },
+ // name = pt:nodeadkeys, description = Portugal
+ { "pt", "nodeadkeys", Qt::LeftToRight, QLocale::Portuguese, QLocale::Portugal },
+ // name = pt:sundeadkeys, description = Portugal
+ { "pt", "sundeadkeys", Qt::LeftToRight, QLocale::Portuguese, QLocale::Portugal },
+ // name = ro, description = Romania
+ { "ro", "", Qt::LeftToRight, QLocale::Romanian, QLocale::Romania },
+ // name = ro:us, description = Romania
+ { "ro", "us", Qt::LeftToRight, QLocale::English, QLocale::Romania },
+ // name = ro:de, description = Romania
+ { "ro", "de", Qt::LeftToRight, QLocale::German, QLocale::Romania },
+ // name = ru, description = Russia
+ { "ru", "", Qt::LeftToRight, QLocale::Russian, QLocale::RussianFederation },
+ // name = ru:phonetic, description = Russia
+ { "ru", "phonetic", Qt::LeftToRight, QLocale::Russian, QLocale::RussianFederation },
+ // name = ru:typewriter, description = Russia
+ { "ru", "typewriter", Qt::LeftToRight, QLocale::Russian, QLocale::RussianFederation },
+ // name = ru:winkeys, description = Russia
+ { "ru", "winkeys", Qt::LeftToRight, QLocale::Russian, QLocale::RussianFederation },
+ // name = srp, description = Serbian
+ { "srp", "", Qt::LeftToRight, QLocale::Serbian, QLocale::SerbiaAndMontenegro },
+ // name = srp:yz, description = Serbian
+ { "srp", "yz", Qt::LeftToRight, QLocale::Serbian, QLocale::SerbiaAndMontenegro },
+ // name = srp:latin, description = Serbian
+ { "srp", "latin", Qt::LeftToRight, QLocale::Serbian, QLocale::SerbiaAndMontenegro },
+ // name = srp:latinunicode, description = Serbian
+ { "srp", "latinunicode", Qt::LeftToRight, QLocale::Serbian, QLocale::SerbiaAndMontenegro },
+ // name = srp:latinyz, description = Serbian
+ { "srp", "latinyz", Qt::LeftToRight, QLocale::Serbian, QLocale::SerbiaAndMontenegro },
+ // name = srp:latinunicodeyz, description = Serbian
+ { "srp", "latinunicodeyz", Qt::LeftToRight, QLocale::Serbian, QLocale::SerbiaAndMontenegro },
+ // name = srp:alternatequotes, description = Serbian
+ { "srp", "alternatequotes", Qt::LeftToRight, QLocale::Serbian, QLocale::SerbiaAndMontenegro },
+ // name = srp:latinalternatequotes, description = Serbian
+ { "srp", "latinalternatequotes", Qt::LeftToRight, QLocale::Serbian, QLocale::SerbiaAndMontenegro },
+ // name = si, description = Slovenia
+ { "si", "", Qt::LeftToRight, QLocale::Slovenian, QLocale::Slovenia },
+ // name = sk, description = Slovakia
+ { "sk", "", Qt::LeftToRight, QLocale::Slovak, QLocale::Slovakia },
+ // name = sk:bksl, description = Slovakia
+ { "sk", "bksl", Qt::LeftToRight, QLocale::Slovak, QLocale::Slovakia },
+ // name = sk:qwerty, description = Slovakia
+ { "sk", "qwerty", Qt::LeftToRight, QLocale::Slovak, QLocale::Slovakia },
+ // name = sk:qwerty_bksl, description = Slovakia
+ { "sk", "qwerty_bksl", Qt::LeftToRight, QLocale::Slovak, QLocale::Slovakia },
+ // name = es, description = Spain
+ { "es", "", Qt::LeftToRight, QLocale::Spanish, QLocale::Spain },
+ // name = es:nodeadkeys, description = Spain
+ { "es", "nodeadkeys", Qt::LeftToRight, QLocale::Spanish, QLocale::Spain },
+ // name = es:sundeadkeys, description = Spain
+ { "es", "sundeadkeys", Qt::LeftToRight, QLocale::Spanish, QLocale::Spain },
+ // name = es:dvorak, description = Spain
+ { "es", "dvorak", Qt::LeftToRight, QLocale::Spanish, QLocale::Spain },
+ // name = se, description = Sweden
+ { "se", "", Qt::LeftToRight, QLocale::Swedish, QLocale::Sweden },
+ // name = se:nodeadkeys, description = Sweden
+ { "se", "nodeadkeys", Qt::LeftToRight, QLocale::Swedish, QLocale::Sweden },
+ // name = se:dvorak, description = Sweden
+ { "se", "dvorak", Qt::LeftToRight, QLocale::Swedish, QLocale::Sweden },
+ // name = se:rus, description = Sweden
+ { "se", "rus", Qt::LeftToRight, QLocale::Russian, QLocale::Sweden },
+ // name = se:rus_nodeadkeys, description = Sweden
+ { "se", "rus_nodeadkeys", Qt::LeftToRight, QLocale::Russian, QLocale::Sweden },
+ // name = se:smi, description = Sweden
+ { "se", "smi", Qt::LeftToRight, QLocale::Swedish, QLocale::Sweden },
+ // name = ch, description = Switzerland
+ { "ch", "", Qt::LeftToRight, QLocale::German, QLocale::Switzerland },
+ // name = ch:de_nodeadkeys, description = Switzerland
+ { "ch", "de_nodeadkeys", Qt::LeftToRight, QLocale::German, QLocale::Switzerland },
+ // name = ch:de_sundeadkeys, description = Switzerland
+ { "ch", "de_sundeadkeys", Qt::LeftToRight, QLocale::German, QLocale::Switzerland },
+ // name = ch:fr, description = Switzerland
+ { "ch", "fr", Qt::LeftToRight, QLocale::French, QLocale::Switzerland },
+ // name = ch:fr_nodeadkeys, description = Switzerland
+ { "ch", "fr_nodeadkeys", Qt::LeftToRight, QLocale::French, QLocale::Switzerland },
+ // name = ch:fr_sundeadkeys, description = Switzerland
+ { "ch", "fr_sundeadkeys", Qt::LeftToRight, QLocale::French, QLocale::Switzerland },
+ // name = sy, description = Syria
+ { "sy", "", Qt::RightToLeft, QLocale::Arabic, QLocale::SyrianArabRepublic },
+ // name = sy:syc, description = Syria
+ { "sy", "syc", Qt::RightToLeft, QLocale::Arabic, QLocale::SyrianArabRepublic },
+ // name = sy:syc_phonetic, description = Syria
+ { "sy", "syc_phonetic", Qt::RightToLeft, QLocale::Arabic, QLocale::SyrianArabRepublic },
+ // name = tj, description = Tajikistan
+ { "tj", "", Qt::LeftToRight, QLocale::Tajik, QLocale::Tajikistan },
+ // name = lk, description = Sri Lanka
+ { "lk", "", Qt::LeftToRight, QLocale::Singhalese, QLocale::SriLanka },
+ // name = lk:tam_unicode, description = Sri Lanka
+ { "lk", "tam_unicode", Qt::LeftToRight, QLocale::Tamil, QLocale::SriLanka },
+ // name = lk:tam_TAB, description = Sri Lanka
+ { "lk", "tam_TAB", Qt::LeftToRight, QLocale::Tamil, QLocale::SriLanka },
+ // name = lk:tam_TSCII, description = Sri Lanka
+ { "lk", "tam_TSCII", Qt::LeftToRight, QLocale::Tamil, QLocale::SriLanka },
+ // name = lk:sin_phonetic, description = Sri Lanka
+ { "lk", "sin_phonetic", Qt::LeftToRight, QLocale::Singhalese, QLocale::SriLanka },
+ // name = th, description = Thailand
+ { "th", "", Qt::LeftToRight, QLocale::Thai, QLocale::Thailand },
+ // name = th:tis, description = Thailand
+ { "th", "tis", Qt::LeftToRight, QLocale::Thai, QLocale::Thailand },
+ // name = th:pat, description = Thailand
+ { "th", "pat", Qt::LeftToRight, QLocale::Thai, QLocale::Thailand },
+ // name = tr, description = Turkish
+ { "tr", "", Qt::LeftToRight, QLocale::Turkish, QLocale::Turkey },
+ // name = tr:f, description = Turkish
+ { "tr", "f", Qt::LeftToRight, QLocale::Turkish, QLocale::Turkey },
+ // name = tr:alt, description = Turkish
+ { "tr", "alt", Qt::LeftToRight, QLocale::Turkish, QLocale::Turkey },
+ // name = ua, description = Ukraine
+ { "ua", "", Qt::LeftToRight, QLocale::Ukrainian, QLocale::Ukraine },
+ // name = ua:phonetic, description = Ukraine
+ { "ua", "phonetic", Qt::LeftToRight, QLocale::Ukrainian, QLocale::Ukraine },
+ // name = ua:typewriter, description = Ukraine
+ { "ua", "typewriter", Qt::LeftToRight, QLocale::Ukrainian, QLocale::Ukraine },
+ // name = ua:winkeys, description = Ukraine
+ { "ua", "winkeys", Qt::LeftToRight, QLocale::Ukrainian, QLocale::Ukraine },
+ // name = ua:rstu, description = Ukraine
+ { "ua", "rstu", Qt::LeftToRight, QLocale::Ukrainian, QLocale::Ukraine },
+ // name = ua:rstu_ru, description = Ukraine
+ { "ua", "rstu_ru", Qt::LeftToRight, QLocale::Ukrainian, QLocale::Ukraine },
+ // name = gb, description = United Kingdom
+ { "gb", "", Qt::LeftToRight, QLocale::English, QLocale::UnitedKingdom },
+ // name = gb:intl, description = United Kingdom
+ { "gb", "intl", Qt::LeftToRight, QLocale::English, QLocale::UnitedKingdom },
+ // name = gb:dvorak, description = United Kingdom
+ { "gb", "dvorak", Qt::LeftToRight, QLocale::English, QLocale::UnitedKingdom },
+ // name = uz, description = Uzbekistan
+ { "uz", "", Qt::LeftToRight, QLocale::Uzbek, QLocale::Uzbekistan },
+ // name = vn, description = Vietnam
+ { "vn", "", Qt::LeftToRight, QLocale::Vietnamese, QLocale::VietNam },
+ // name = nec_vndr/jp, description = PC-98xx Series
+ { "nec_vndr/jp", "", Qt::LeftToRight, QLocale::Japanese, QLocale::Japan },
+ // name = ie, description = Ireland
+ { "ie", "", Qt::LeftToRight, QLocale::Irish, QLocale::Ireland },
+ // name = ie:CloGaelach, description = Ireland
+ { "ie", "CloGaelach", Qt::LeftToRight, QLocale::Gaelic, QLocale::Ireland },
+ // name = ie:UnicodeExpert, description = Ireland
+ { "ie", "UnicodeExpert", Qt::LeftToRight, QLocale::Irish, QLocale::Ireland },
+ // name = ie:ogam, description = Ireland
+ { "ie", "ogam", Qt::LeftToRight, QLocale::Gaelic, QLocale::Ireland },
+ // name = ie:ogam_is434, description = Ireland
+ { "ie", "ogam_is434", Qt::LeftToRight, QLocale::Gaelic, QLocale::Ireland },
+ // name = pk, description = Pakistan
+ { "pk", "", Qt::RightToLeft, QLocale::Urdu, QLocale::Pakistan },
+ { 0, 0, Qt::LeftToRight, QLocale::C, QLocale::AnyCountry }
+};
diff --git a/src/gui/kernel/qkeysequence.cpp b/src/gui/kernel/qkeysequence.cpp
new file mode 100644
index 0000000000..352d26a659
--- /dev/null
+++ b/src/gui/kernel/qkeysequence.cpp
@@ -0,0 +1,1469 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qkeysequence.h"
+#include "qkeysequence_p.h"
+#include "private/qapplication_p.h"
+
+#ifndef QT_NO_SHORTCUT
+
+#include "qshortcut.h"
+#include "qdebug.h"
+#ifndef QT_NO_REGEXP
+# include "qregexp.h"
+#endif
+#ifndef QT_NO_DATASTREAM
+# include "qdatastream.h"
+#endif
+#include "qvariant.h"
+
+#ifdef Q_WS_MAC
+# include <private/qt_mac_p.h>
+
+#endif
+
+QT_BEGIN_NAMESPACE
+
+#ifdef Q_WS_MAC
+static bool qt_sequence_no_mnemonics = true;
+struct MacSpecialKey {
+ int key;
+ ushort macSymbol;
+};
+
+static const int NumEntries = 21;
+static const MacSpecialKey entries[NumEntries] = {
+ { Qt::Key_Escape, 0x238B },
+ { Qt::Key_Tab, 0x21E5 },
+ { Qt::Key_Backtab, 0x21E4 },
+ { Qt::Key_Backspace, 0x232B },
+ { Qt::Key_Return, 0x21B5 },
+ { Qt::Key_Enter, 0x21B5 },
+ { Qt::Key_Delete, 0x2326 },
+ { Qt::Key_Home, 0x2196 },
+ { Qt::Key_End, 0x2198 },
+ { Qt::Key_Left, 0x2190 },
+ { Qt::Key_Up, 0x2191 },
+ { Qt::Key_Right, 0x2192 },
+ { Qt::Key_Down, 0x2193 },
+ { Qt::Key_PageUp, 0x21DE },
+ { Qt::Key_PageDown, 0x21DF },
+ { Qt::Key_Shift, kShiftUnicode },
+ { Qt::Key_Control, kCommandUnicode },
+ { Qt::Key_Meta, kControlUnicode },
+ { Qt::Key_Alt, kOptionUnicode },
+ { Qt::Key_CapsLock, 0x21EA },
+};
+
+static bool operator<(const MacSpecialKey &entry, int key)
+{
+ return entry.key < key;
+}
+
+static bool operator<(int key, const MacSpecialKey &entry)
+{
+ return key < entry.key;
+}
+
+static const MacSpecialKey * const MacSpecialKeyEntriesEnd = entries + NumEntries;
+
+static QChar macSymbolForQtKey(int key)
+{
+ const MacSpecialKey *i = qBinaryFind(entries, MacSpecialKeyEntriesEnd, key);
+ if (i == MacSpecialKeyEntriesEnd)
+ return QChar();
+ return QChar(i->macSymbol);
+}
+
+static int qtkeyForMacSymbol(const QChar ch)
+{
+ for (int i = 0; i < NumEntries; ++i) {
+ const MacSpecialKey &entry = entries[i];
+ if (entry.macSymbol == ch.unicode())
+ return entry.key;
+ }
+ return -1;
+}
+
+#else
+static bool qt_sequence_no_mnemonics = false;
+#endif
+void Q_GUI_EXPORT qt_set_sequence_auto_mnemonic(bool b) { qt_sequence_no_mnemonics = !b; }
+
+/*!
+ \class QKeySequence
+ \brief The QKeySequence class encapsulates a key sequence as used
+ by shortcuts.
+
+ \ingroup misc
+ \ingroup shared
+ \mainclass
+
+ In its most common form, a key sequence describes a combination of
+ keys that must be used together to perform some action. Key sequences
+ are used with QAction objects to specify which keyboard shortcuts can
+ be used to trigger actions.
+
+ Key sequences can be constructed for use as keyboard shortcuts in
+ three different ways:
+
+ \list
+ \o For standard shortcuts, a \l{QKeySequence::StandardKey}{standard key}
+ can be used to request the platform-specific key sequence associated
+ with each shortcut.
+ \o For custom shortcuts, human-readable strings such as "Ctrl+X" can
+ be used, and these can be translated into the appropriate shortcuts
+ for users of different languages. Translations are made in the
+ "QShortcut" context.
+ \o For hard-coded shortcuts, integer key codes can be specified with
+ a combination of values defined by the Qt::Key and Qt::Modifier enum
+ values. Each key code consists of a single Qt::Key value and zero or
+ more modifiers, such as Qt::SHIFT, Qt::CTRL, Qt::ALT and Qt::META.
+ \endlist
+
+ For example, \gui{Ctrl P} might be a sequence used as a shortcut for
+ printing a document, and can be specified in any of the following
+ ways:
+
+ \snippet doc/src/snippets/code/src_gui_kernel_qkeysequence.cpp 0
+
+ Note that, for letters, the case used in the specification string
+ does not matter. In the above examples, the user does not need to
+ hold down the \key{Shift} key to activate a shortcut specified
+ with "Ctrl+P". However, for other keys, the use of \key{Shift} as
+ an unspecified extra modifier key can lead to confusion for users
+ of an application whose keyboards have different layouts to those
+ used by the developers. See the \l{Keyboard Layout Issues} section
+ below for more details.
+
+ It is preferable to use standard shortcuts where possible.
+ When creating key sequences for non-standard shortcuts, you should use
+ human-readable strings in preference to hard-coded integer values.
+
+ QKeySequence objects can be cast to a QString to obtain a human-readable
+ translated version of the sequence. Similarly, the toString() function
+ produces human-readable strings for use in menus. On Mac OS X, the
+ appropriate symbols are used to describe keyboard shortcuts using special
+ keys on the Macintosh keyboard.
+
+ An alternative way to specify hard-coded key codes is to use the Unicode
+ code point of the character; for example, 'A' gives the same key sequence
+ as Qt::Key_A.
+
+ \bold{Note:} On Mac OS X, references to "Ctrl", Qt::CTRL, Qt::Control
+ and Qt::ControlModifier correspond to the \key Command keys on the
+ Macintosh keyboard, and references to "Meta", Qt::META, Qt::Meta and
+ Qt::MetaModifier correspond to the \key Control keys. Developers on
+ Mac OS X can use the same shortcut descriptions across all platforms,
+ and their applications will automatically work as expected on Mac OS X.
+
+ \section1 Standard Shortcuts
+
+ QKeySequence defines many \l{QKeySequence::StandardKey} {standard
+ keyboard shortcuts} to reduce the amount of effort required when
+ setting up actions in a typical application. The table below shows
+ some common key sequences that are often used for these standard
+ shortcuts by applications on four widely-used platforms. Note
+ that on Mac OS X, the \key Ctrl value corresponds to the \key
+ Command keys on the Macintosh keyboard, and the \key Meta value
+ corresponds to the \key Control keys.
+
+ \table
+ \header \i StandardKey \i Windows \i Mac OS X \i KDE \i GNOME
+ \row \i HelpContents \i F1 \i Ctrl+? \i F1 \i F1
+ \row \i WhatsThis \i Shift+F1 \i Shift+F1 \i Shift+F1 \i Shift+F1
+ \row \i Open \i Ctrl+O \i Ctrl+O \i Ctrl+O \i Ctrl+O
+ \row \i Close \i Ctrl+F4, Ctrl+W \i Ctrl+W, Ctrl+F4 \i Ctrl+W \i Ctrl+W
+ \row \i Save \i Ctrl+S \i Ctrl+S \i Ctrl+S \i Ctrl+S
+ \row \i SaveAs \i \i Ctrl+Shift+S \i \i Ctrl+Shift+S
+ \row \i New \i Ctrl+N \i Ctrl+N \i Ctrl+N \i Ctrl+N
+ \row \i Delete \i Del \i Del, Meta+D \i Del, Ctrl+D \i Del, Ctrl+D
+ \row \i Cut \i Ctrl+X, Shift+Del \i Ctrl+X \i Ctrl+X, F20, Shift+Del \i Ctrl+X, F20, Shift+Del
+ \row \i Copy \i Ctrl+C, Ctrl+Ins \i Ctrl+C \i Ctrl+C, F16, Ctrl+Ins \i Ctrl+C, F16, Ctrl+Ins
+ \row \i Paste \i Ctrl+V, Shift+Ins \i Ctrl+V \i Ctrl+V, F18, Shift+Ins \i Ctrl+V, F18, Shift+Ins
+ \row \i Undo \i Ctrl+Z, Alt+Backspace \i Ctrl+Z \i Ctrl+Z, F14 \i Ctrl+Z, F14
+ \row \i Redo \i Ctrl+Y, Shift+Ctrl+Z, Alt+Shift+Backspace \i Ctrl+Shift+Z, Ctrl+Y \i Ctrl+Shift+Z \i Ctrl+Shift+Z
+ \row \i Back \i Alt+Left, Backspace \i Ctrl+[ \i Alt+Left \i Alt+Left
+ \row \i Forward \i Alt+Right, Shift+Backspace \i Ctrl+] \i Alt+Right \i Alt+Right
+ \row \i Refresh \i F5 \i F5 \i F5 \i Ctrl+R, F5
+ \row \i ZoomIn \i Ctrl+Plus \i Ctrl+Plus \i Ctrl+Plus \i Ctrl+Plus
+ \row \i ZoomOut \i Ctrl+Minus \i Ctrl+Minus \i Ctrl+Minus \i Ctrl+Minus
+ \row \i Print \i Ctrl+P \i Ctrl+P \i Ctrl+P \i Ctrl+P
+ \row \i AddTab \i Ctrl+T \i Ctrl+T \i Ctrl+Shift+N, Ctrl+T \i Ctrl+T
+ \row \i NextChild \i Ctrl+Tab, Forward, Ctrl+F6 \i Ctrl+}, Forward, Ctrl+Tab \i Ctrl+Tab, Forward, Ctrl+Comma \i Ctrl+Tab, Forward
+ \row \i PreviousChild \i Ctrl+Shift+Tab, Back, Ctrl+Shift+F6 \i Ctrl+{, Back, Ctrl+Shift+Tab \i Ctrl+Shift+Tab, Back, Ctrl+Period \i Ctrl+Shift+Tab, Back
+ \row \i Find \i Ctrl+F \i Ctrl+F \i Ctrl+F \i Ctrl+F
+ \row \i FindNext \i F3, Ctrl+G \i Ctrl+G \i F3 \i Ctrl+G, F3
+ \row \i FindPrevious \i Shift+F3, Ctrl+Shift+G \i Ctrl+Shift+G \i Shift+F3 \i Ctrl+Shift+G, Shift+F3
+ \row \i Replace \i Ctrl+H \i (none) \i Ctrl+R \i Ctrl+H
+ \row \i SelectAll \i Ctrl+A \i Ctrl+A \i Ctrl+A \i Ctrl+A
+ \row \i Bold \i Ctrl+B \i Ctrl+B \i Ctrl+B \i Ctrl+B
+ \row \i Italic \i Ctrl+I \i Ctrl+I \i Ctrl+I \i Ctrl+I
+ \row \i Underline \i Ctrl+U \i Ctrl+U \i Ctrl+U \i Ctrl+U
+ \row \i MoveToNextChar \i Right \i Right \i Right \i Right
+ \row \i MoveToPreviousChar \i Left \i Left \i Left \i Left
+ \row \i MoveToNextWord \i Ctrl+Right \i Alt+Right \i Ctrl+Right \i Ctrl+Right
+ \row \i MoveToPreviousWord \i Ctrl+Left \i Alt+Left \i Ctrl+Left \i Ctrl+Left
+ \row \i MoveToNextLine \i Down \i Down \i Down \i Down
+ \row \i MoveToPreviousLine \i Up \i Up \i Up \i Up
+ \row \i MoveToNextPage \i PgDown \i PgDown, Alt+PgDown, Meta+Down, Meta+PgDown\i PgDown \i PgDown
+ \row \i MoveToPreviousPage \i PgUp \i PgUp, Alt+PgUp, Meta+Up, Meta+PgUp \i PgUp \i PgUp
+ \row \i MoveToStartOfLine \i Home \i Ctrl+Left, Meta+Left \i Home \i Home
+ \row \i MoveToEndOfLine \i End \i Ctrl+Right, Meta+Right \i End \i End
+ \row \i MoveToStartOfBlock \i (none) \i Alt+Up, Meta+A \i (none) \i (none)
+ \row \i MoveToEndOfBlock \i (none) \i Alt+Down, Meta+E \i (none) \i (none)
+ \row \i MoveToStartOfDocument\i Ctrl+Home \i Ctrl+Up, Home \i Ctrl+Home \i Ctrl+Home
+ \row \i MoveToEndOfDocument \i Ctrl+End \i Ctrl+Down, End \i Ctrl+End \i Ctrl+End
+ \row \i SelectNextChar \i Shift+Right \i Shift+Right \i Shift+Right \i Shift+Right
+ \row \i SelectPreviousChar \i Shift+Left \i Shift+Left \i Shift+Left \i Shift?left
+ \row \i SelectNextWord \i Ctrl+Shift+Right \i Alt+Shift+Right \i Ctrl+Shift+Right \i Ctrl+Shift+Right
+ \row \i SelectPreviousWord \i Ctrl+Shift+Left \i Alt+Shift+Left \i Ctrl+Shift+Left \i Ctrl+Shift+Left
+ \row \i SelectNextLine \i Shift+Down \i Shift+Down \i Shift+Down \i Shift+Down
+ \row \i SelectPreviousLine \i Shift+Up \i Shift+Up \i Shift+Up \i Shift+Up
+ \row \i SelectNextPage \i Shift+PgDown \i Shift+PgDown \i Shift+PgDown \i Shift+PgDown
+ \row \i SelectPreviousPage \i Shift+PgUp \i Shift+PgUp \i Shift+PgUp \i Shift+PgUp
+ \row \i SelectStartOfLine \i Shift+Home \i Ctrl+Shift+Left \i Shift+Home \i Shift+Home
+ \row \i SelectEndOfLine \i Shift+End \i Ctrl+Shift+Right \i Shift+End \i Shift+End
+ \row \i SelectStartOfBlock \i (none) \i Alt+Shift+Up \i (none) \i (none)
+ \row \i SelectEndOfBlock \i (none) \i Alt+Shift+Down \i (none) \i (none)
+ \row \i SelectStartOfDocument\i Ctrl+Shift+Home \i Ctrl+Shift+Up, Shift+Home \i Ctrl+Shift+Home\i Ctrl+Shift+Home
+ \row \i SelectEndOfDocument \i Ctrl+Shift+End \i Ctrl+Shift+Down, Shift+End \i Ctrl+Shift+End \i Ctrl+Shift+End
+ \row \i DeleteStartOfWord \i Ctrl+Backspace \i Alt+Backspace \i Ctrl+Backspace \i Ctrl+Backspace
+ \row \i DeleteEndOfWord \i Ctrl+Del \i (none) \i Ctrl+Del \i Ctrl+Del
+ \row \i DeleteEndOfLine \i (none) \i (none) \i Ctrl+K \i Ctrl+K
+ \row \i InsertParagraphSeparator \i Enter \i Enter \i Enter \i Enter
+ \row \i InsertLineSeparator \i Shift+Enter \i Meta+Enter \i Shift+Enter \i Shift+Enter
+ \endtable
+
+ Note that, since the key sequences used for the standard shortcuts differ
+ between platforms, you still need to test your shortcuts on each platform
+ to ensure that you do not unintentionally assign the same key sequence to
+ many actions.
+
+ \section1 Keyboard Layout Issues
+
+ Many key sequence specifications are chosen by developers based on the
+ layout of certain types of keyboard, rather than choosing keys that
+ represent the first letter of an action's name, such as \key{Ctrl S}
+ ("Ctrl+S") or \key{Ctrl C} ("Ctrl+C").
+ Additionally, because certain symbols can only be entered with the
+ help of modifier keys on certain keyboard layouts, key sequences intended
+ for use with one keyboard layout may map to a different key, map to no
+ keys at all, or require an additional modifier key to be used on
+ different keyboard layouts.
+
+ For example, the shortcuts, \key{Ctrl plus} and \key{Ctrl minus}, are often
+ used as shortcuts for zoom operations in graphics applications, and these
+ may be specified as "Ctrl++" and "Ctrl+-" respectively. However, the way
+ these shortcuts are specified and interpreted depends on the keyboard layout.
+ Users of Norwegian keyboards will note that the \key{+} and \key{-} keys
+ are not adjacent on the keyboard, but will still be able to activate both
+ shortcuts without needing to press the \key{Shift} key. However, users
+ with British keyboards will need to hold down the \key{Shift} key
+ to enter the \key{+} symbol, making the shortcut effectively the same as
+ "Ctrl+Shift+=".
+
+ Although some developers might resort to fully specifying all the modifiers
+ they use on their keyboards to activate a shortcut, this will also result
+ in unexpected behavior for users of different keyboard layouts.
+
+ For example, a developer using a British keyboard may decide to specify
+ "Ctrl+Shift+=" as the key sequence in order to create a shortcut that
+ coincidentally behaves in the same way as \key{Ctrl plus}. However, the
+ \key{=} key needs to be accessed using the \key{Shift} key on Norwegian
+ keyboard, making the required shortcut effectively \key{Ctrl Shift Shift =}
+ (an impossible key combination).
+
+ As a result, both human-readable strings and hard-coded key codes
+ can both be problematic to use when specifying a key sequence that
+ can be used on a variety of different keyboard layouts. Only the
+ use of \l{QKeySequence::StandardKey} {standard shortcuts}
+ guarantees that the user will be able to use the shortcuts that
+ the developer intended.
+
+ Despite this, we can address this issue by ensuring that human-readable
+ strings are used, making it possible for translations of key sequences to
+ be made for users of different languages. This approach will be successful
+ for users whose keyboards have the most typical layout for the language
+ they are using.
+
+ \section1 GNU Emacs Style Key Sequences
+
+ Key sequences similar to those used in \l{GNU Emacs}, allowing up to four
+ key codes, can be created by using the multiple argument constructor,
+ or by passing a human-readable string of comma-separated key sequences.
+
+ For example, the key sequence, \key{Ctrl X} followed by \key{Ctrl C}, can
+ be specified using either of the following ways:
+
+ \snippet doc/src/snippets/code/src_gui_kernel_qkeysequence.cpp 1
+
+ \warning A QApplication instance must have been constructed before a
+ QKeySequence is created; otherwise, your application may crash.
+
+ \sa QShortcut
+*/
+
+/*!
+ \enum QKeySequence::SequenceMatch
+
+ \value NoMatch The key sequences are different; not even partially
+ matching.
+ \value PartialMatch The key sequences match partially, but are not
+ the same.
+ \value ExactMatch The key sequences are the same.
+ \omitvalue Identical
+*/
+
+/*!
+ \enum QKeySequence::SequenceFormat
+
+ \value NativeText The key sequence as a platform specific string.
+ This means that it will be shown translated and on the Mac it will
+ resemble a key sequence from the menu bar. This enum is best used when you
+ want to display the string to the user.
+
+ \value PortableText The key sequence is given in a "portable" format,
+ suitable for reading and writing to a file. In many cases, it will look
+ similar to the native text on Windows and X11.
+*/
+
+static const struct {
+ int key;
+ const char* name;
+} keyname[] = {
+ { Qt::Key_Space, QT_TRANSLATE_NOOP("QShortcut", "Space") },
+ { Qt::Key_Escape, QT_TRANSLATE_NOOP("QShortcut", "Esc") },
+ { Qt::Key_Tab, QT_TRANSLATE_NOOP("QShortcut", "Tab") },
+ { Qt::Key_Backtab, QT_TRANSLATE_NOOP("QShortcut", "Backtab") },
+ { Qt::Key_Backspace, QT_TRANSLATE_NOOP("QShortcut", "Backspace") },
+ { Qt::Key_Return, QT_TRANSLATE_NOOP("QShortcut", "Return") },
+ { Qt::Key_Enter, QT_TRANSLATE_NOOP("QShortcut", "Enter") },
+ { Qt::Key_Insert, QT_TRANSLATE_NOOP("QShortcut", "Ins") },
+ { Qt::Key_Delete, QT_TRANSLATE_NOOP("QShortcut", "Del") },
+ { Qt::Key_Pause, QT_TRANSLATE_NOOP("QShortcut", "Pause") },
+ { Qt::Key_Print, QT_TRANSLATE_NOOP("QShortcut", "Print") },
+ { Qt::Key_SysReq, QT_TRANSLATE_NOOP("QShortcut", "SysReq") },
+ { Qt::Key_Home, QT_TRANSLATE_NOOP("QShortcut", "Home") },
+ { Qt::Key_End, QT_TRANSLATE_NOOP("QShortcut", "End") },
+ { Qt::Key_Left, QT_TRANSLATE_NOOP("QShortcut", "Left") },
+ { Qt::Key_Up, QT_TRANSLATE_NOOP("QShortcut", "Up") },
+ { Qt::Key_Right, QT_TRANSLATE_NOOP("QShortcut", "Right") },
+ { Qt::Key_Down, QT_TRANSLATE_NOOP("QShortcut", "Down") },
+ { Qt::Key_PageUp, QT_TRANSLATE_NOOP("QShortcut", "PgUp") },
+ { Qt::Key_PageDown, QT_TRANSLATE_NOOP("QShortcut", "PgDown") },
+ { Qt::Key_CapsLock, QT_TRANSLATE_NOOP("QShortcut", "CapsLock") },
+ { Qt::Key_NumLock, QT_TRANSLATE_NOOP("QShortcut", "NumLock") },
+ { Qt::Key_ScrollLock, QT_TRANSLATE_NOOP("QShortcut", "ScrollLock") },
+ { Qt::Key_Menu, QT_TRANSLATE_NOOP("QShortcut", "Menu") },
+ { Qt::Key_Help, QT_TRANSLATE_NOOP("QShortcut", "Help") },
+
+ // Multimedia keys
+ { Qt::Key_Back, QT_TRANSLATE_NOOP("QShortcut", "Back") },
+ { Qt::Key_Forward, QT_TRANSLATE_NOOP("QShortcut", "Forward") },
+ { Qt::Key_Stop, QT_TRANSLATE_NOOP("QShortcut", "Stop") },
+ { Qt::Key_Refresh, QT_TRANSLATE_NOOP("QShortcut", "Refresh") },
+ { Qt::Key_VolumeDown, QT_TRANSLATE_NOOP("QShortcut", "Volume Down") },
+ { Qt::Key_VolumeMute, QT_TRANSLATE_NOOP("QShortcut", "Volume Mute") },
+ { Qt::Key_VolumeUp, QT_TRANSLATE_NOOP("QShortcut", "Volume Up") },
+ { Qt::Key_BassBoost, QT_TRANSLATE_NOOP("QShortcut", "Bass Boost") },
+ { Qt::Key_BassUp, QT_TRANSLATE_NOOP("QShortcut", "Bass Up") },
+ { Qt::Key_BassDown, QT_TRANSLATE_NOOP("QShortcut", "Bass Down") },
+ { Qt::Key_TrebleUp, QT_TRANSLATE_NOOP("QShortcut", "Treble Up") },
+ { Qt::Key_TrebleDown, QT_TRANSLATE_NOOP("QShortcut", "Treble Down") },
+ { Qt::Key_MediaPlay, QT_TRANSLATE_NOOP("QShortcut", "Media Play") },
+ { Qt::Key_MediaStop, QT_TRANSLATE_NOOP("QShortcut", "Media Stop") },
+ { Qt::Key_MediaPrevious,QT_TRANSLATE_NOOP("QShortcut", "Media Previous") },
+ { Qt::Key_MediaNext, QT_TRANSLATE_NOOP("QShortcut", "Media Next") },
+ { Qt::Key_MediaRecord, QT_TRANSLATE_NOOP("QShortcut", "Media Record") },
+ { Qt::Key_HomePage, QT_TRANSLATE_NOOP("QShortcut", "Home Page") },
+ { Qt::Key_Favorites, QT_TRANSLATE_NOOP("QShortcut", "Favorites") },
+ { Qt::Key_Search, QT_TRANSLATE_NOOP("QShortcut", "Search") },
+ { Qt::Key_Standby, QT_TRANSLATE_NOOP("QShortcut", "Standby") },
+ { Qt::Key_OpenUrl, QT_TRANSLATE_NOOP("QShortcut", "Open URL") },
+ { Qt::Key_LaunchMail, QT_TRANSLATE_NOOP("QShortcut", "Launch Mail") },
+ { Qt::Key_LaunchMedia, QT_TRANSLATE_NOOP("QShortcut", "Launch Media") },
+ { Qt::Key_Launch0, QT_TRANSLATE_NOOP("QShortcut", "Launch (0)") },
+ { Qt::Key_Launch1, QT_TRANSLATE_NOOP("QShortcut", "Launch (1)") },
+ { Qt::Key_Launch2, QT_TRANSLATE_NOOP("QShortcut", "Launch (2)") },
+ { Qt::Key_Launch3, QT_TRANSLATE_NOOP("QShortcut", "Launch (3)") },
+ { Qt::Key_Launch4, QT_TRANSLATE_NOOP("QShortcut", "Launch (4)") },
+ { Qt::Key_Launch5, QT_TRANSLATE_NOOP("QShortcut", "Launch (5)") },
+ { Qt::Key_Launch6, QT_TRANSLATE_NOOP("QShortcut", "Launch (6)") },
+ { Qt::Key_Launch7, QT_TRANSLATE_NOOP("QShortcut", "Launch (7)") },
+ { Qt::Key_Launch8, QT_TRANSLATE_NOOP("QShortcut", "Launch (8)") },
+ { Qt::Key_Launch9, QT_TRANSLATE_NOOP("QShortcut", "Launch (9)") },
+ { Qt::Key_LaunchA, QT_TRANSLATE_NOOP("QShortcut", "Launch (A)") },
+ { Qt::Key_LaunchB, QT_TRANSLATE_NOOP("QShortcut", "Launch (B)") },
+ { Qt::Key_LaunchC, QT_TRANSLATE_NOOP("QShortcut", "Launch (C)") },
+ { Qt::Key_LaunchD, QT_TRANSLATE_NOOP("QShortcut", "Launch (D)") },
+ { Qt::Key_LaunchE, QT_TRANSLATE_NOOP("QShortcut", "Launch (E)") },
+ { Qt::Key_LaunchF, QT_TRANSLATE_NOOP("QShortcut", "Launch (F)") },
+
+ // --------------------------------------------------------------
+ // More consistent namings
+ { Qt::Key_Print, QT_TRANSLATE_NOOP("QShortcut", "Print Screen") },
+ { Qt::Key_PageUp, QT_TRANSLATE_NOOP("QShortcut", "Page Up") },
+ { Qt::Key_PageDown, QT_TRANSLATE_NOOP("QShortcut", "Page Down") },
+ { Qt::Key_CapsLock, QT_TRANSLATE_NOOP("QShortcut", "Caps Lock") },
+ { Qt::Key_NumLock, QT_TRANSLATE_NOOP("QShortcut", "Num Lock") },
+ { Qt::Key_NumLock, QT_TRANSLATE_NOOP("QShortcut", "Number Lock") },
+ { Qt::Key_ScrollLock, QT_TRANSLATE_NOOP("QShortcut", "Scroll Lock") },
+ { Qt::Key_Insert, QT_TRANSLATE_NOOP("QShortcut", "Insert") },
+ { Qt::Key_Delete, QT_TRANSLATE_NOOP("QShortcut", "Delete") },
+ { Qt::Key_Escape, QT_TRANSLATE_NOOP("QShortcut", "Escape") },
+ { Qt::Key_SysReq, QT_TRANSLATE_NOOP("QShortcut", "System Request") },
+
+ // --------------------------------------------------------------
+ // Keypad navigation keys
+ { Qt::Key_Select, QT_TRANSLATE_NOOP("QShortcut", "Select") },
+ { Qt::Key_Yes, QT_TRANSLATE_NOOP("QShortcut", "Yes") },
+ { Qt::Key_No, QT_TRANSLATE_NOOP("QShortcut", "No") },
+
+ // --------------------------------------------------------------
+ // Device keys
+ { Qt::Key_Context1, QT_TRANSLATE_NOOP("QShortcut", "Context1") },
+ { Qt::Key_Context2, QT_TRANSLATE_NOOP("QShortcut", "Context2") },
+ { Qt::Key_Context3, QT_TRANSLATE_NOOP("QShortcut", "Context3") },
+ { Qt::Key_Context4, QT_TRANSLATE_NOOP("QShortcut", "Context4") },
+ { Qt::Key_Call, QT_TRANSLATE_NOOP("QShortcut", "Call") },
+ { Qt::Key_Hangup, QT_TRANSLATE_NOOP("QShortcut", "Hangup") },
+ { Qt::Key_Flip, QT_TRANSLATE_NOOP("QShortcut", "Flip") },
+
+
+ { 0, 0 }
+};
+
+//Table of key bindings. It must be sorted on key sequence.
+//A priority of 1 indicates that this is the primary key binding when multiple are defined.
+
+const QKeyBinding QKeySequencePrivate::keyBindings[] = {
+// StandardKey Priority Key Sequence Platforms
+ {QKeySequence::Back, 0, Qt::Key_Backspace, QApplicationPrivate::KB_Win},
+ {QKeySequence::InsertParagraphSeparator,0, Qt::Key_Return, QApplicationPrivate::KB_All},
+ {QKeySequence::InsertParagraphSeparator,0, Qt::Key_Enter, QApplicationPrivate::KB_All},
+ {QKeySequence::Delete, 1, Qt::Key_Delete, QApplicationPrivate::KB_All},
+ {QKeySequence::MoveToStartOfLine, 0, Qt::Key_Home, QApplicationPrivate::KB_Win | QApplicationPrivate::KB_X11},
+ {QKeySequence::MoveToStartOfDocument, 0, Qt::Key_Home, QApplicationPrivate::KB_Mac},
+ {QKeySequence::MoveToEndOfLine, 0, Qt::Key_End, QApplicationPrivate::KB_Win | QApplicationPrivate::KB_X11},
+ {QKeySequence::MoveToEndOfDocument, 0, Qt::Key_End, QApplicationPrivate::KB_Mac},
+ {QKeySequence::MoveToPreviousChar, 0, Qt::Key_Left, QApplicationPrivate::KB_All},
+ {QKeySequence::MoveToPreviousLine, 0, Qt::Key_Up, QApplicationPrivate::KB_All},
+ {QKeySequence::MoveToNextChar, 0, Qt::Key_Right, QApplicationPrivate::KB_All},
+ {QKeySequence::MoveToNextLine, 0, Qt::Key_Down, QApplicationPrivate::KB_All},
+ {QKeySequence::MoveToPreviousPage, 1, Qt::Key_PageUp, QApplicationPrivate::KB_All},
+ {QKeySequence::MoveToNextPage, 1, Qt::Key_PageDown, QApplicationPrivate::KB_All},
+ {QKeySequence::HelpContents, 0, Qt::Key_F1, QApplicationPrivate::KB_Win | QApplicationPrivate::KB_X11},
+ {QKeySequence::FindNext, 0, Qt::Key_F3, QApplicationPrivate::KB_X11},
+ {QKeySequence::FindNext, 1, Qt::Key_F3, QApplicationPrivate::KB_Win},
+ {QKeySequence::Refresh, 0, Qt::Key_F5, QApplicationPrivate::KB_Win | QApplicationPrivate::KB_X11},
+ {QKeySequence::Undo, 0, Qt::Key_F14, QApplicationPrivate::KB_X11}, //Undo on sun keyboards
+ {QKeySequence::Copy, 0, Qt::Key_F16, QApplicationPrivate::KB_X11}, //Copy on sun keyboards
+ {QKeySequence::Paste, 0, Qt::Key_F18, QApplicationPrivate::KB_X11}, //Paste on sun keyboards
+ {QKeySequence::Cut, 0, Qt::Key_F20, QApplicationPrivate::KB_X11}, //Cut on sun keyboards
+ {QKeySequence::PreviousChild, 0, Qt::Key_Back, QApplicationPrivate::KB_All},
+ {QKeySequence::NextChild, 0, Qt::Key_Forward, QApplicationPrivate::KB_All},
+ {QKeySequence::Forward, 0, Qt::SHIFT | Qt::Key_Backspace, QApplicationPrivate::KB_Win},
+ {QKeySequence::InsertLineSeparator, 0, Qt::SHIFT | Qt::Key_Return, QApplicationPrivate::KB_All},
+ {QKeySequence::InsertLineSeparator, 0, Qt::SHIFT | Qt::Key_Enter, QApplicationPrivate::KB_All},
+ {QKeySequence::Paste, 0, Qt::SHIFT | Qt::Key_Insert, QApplicationPrivate::KB_Win | QApplicationPrivate::KB_X11},
+ {QKeySequence::Cut, 0, Qt::SHIFT | Qt::Key_Delete, QApplicationPrivate::KB_Win | QApplicationPrivate::KB_X11}, //## Check if this should work on mac
+ {QKeySequence::SelectStartOfLine, 0, Qt::SHIFT | Qt::Key_Home, QApplicationPrivate::KB_Win | QApplicationPrivate::KB_X11},
+ {QKeySequence::SelectStartOfDocument, 0, Qt::SHIFT | Qt::Key_Home, QApplicationPrivate::KB_Mac},
+ {QKeySequence::SelectEndOfLine, 0, Qt::SHIFT | Qt::Key_End, QApplicationPrivate::KB_Win | QApplicationPrivate::KB_X11},
+ {QKeySequence::SelectEndOfDocument, 0, Qt::SHIFT | Qt::Key_End, QApplicationPrivate::KB_Mac},
+ {QKeySequence::SelectPreviousChar, 0, Qt::SHIFT | Qt::Key_Left, QApplicationPrivate::KB_All},
+ {QKeySequence::SelectPreviousLine, 0, Qt::SHIFT | Qt::Key_Up, QApplicationPrivate::KB_All},
+ {QKeySequence::SelectNextChar, 0, Qt::SHIFT | Qt::Key_Right, QApplicationPrivate::KB_All},
+ {QKeySequence::SelectNextLine, 0, Qt::SHIFT | Qt::Key_Down, QApplicationPrivate::KB_All},
+ {QKeySequence::SelectPreviousPage, 0, Qt::SHIFT | Qt::Key_PageUp, QApplicationPrivate::KB_All},
+ {QKeySequence::SelectNextPage, 0, Qt::SHIFT | Qt::Key_PageDown, QApplicationPrivate::KB_All},
+ {QKeySequence::WhatsThis, 1, Qt::SHIFT | Qt::Key_F1, QApplicationPrivate::KB_All},
+ {QKeySequence::FindPrevious, 0, Qt::SHIFT | Qt::Key_F3, QApplicationPrivate::KB_X11},
+ {QKeySequence::FindPrevious, 1, Qt::SHIFT | Qt::Key_F3, QApplicationPrivate::KB_Win},
+ {QKeySequence::ZoomIn, 1, Qt::CTRL | Qt::Key_Plus, QApplicationPrivate::KB_All},
+ {QKeySequence::NextChild, 0, Qt::CTRL | Qt::Key_Comma, QApplicationPrivate::KB_KDE},
+ {QKeySequence::ZoomOut, 1, Qt::CTRL | Qt::Key_Minus, QApplicationPrivate::KB_All},
+ {QKeySequence::PreviousChild, 0, Qt::CTRL | Qt::Key_Period, QApplicationPrivate::KB_KDE},
+ {QKeySequence::HelpContents, 1, Qt::CTRL | Qt::Key_Question, QApplicationPrivate::KB_Mac},
+ {QKeySequence::SelectAll, 1, Qt::CTRL | Qt::Key_A, QApplicationPrivate::KB_All},
+ {QKeySequence::Bold, 1, Qt::CTRL | Qt::Key_B, QApplicationPrivate::KB_All},
+ {QKeySequence::Copy, 1, Qt::CTRL | Qt::Key_C, QApplicationPrivate::KB_All},
+ {QKeySequence::Delete, 0, Qt::CTRL | Qt::Key_D, QApplicationPrivate::KB_X11}, //emacs (line edit only)
+ {QKeySequence::Find, 0, Qt::CTRL | Qt::Key_F, QApplicationPrivate::KB_All},
+ {QKeySequence::FindNext, 1, Qt::CTRL | Qt::Key_G, QApplicationPrivate::KB_Gnome | QApplicationPrivate::KB_Mac},
+ {QKeySequence::FindNext, 0, Qt::CTRL | Qt::Key_G, QApplicationPrivate::KB_Win},
+ {QKeySequence::Replace, 0, Qt::CTRL | Qt::Key_H, QApplicationPrivate::KB_Win},
+ {QKeySequence::Replace, 0, Qt::CTRL | Qt::Key_H, QApplicationPrivate::KB_Gnome},
+ {QKeySequence::Italic, 0, Qt::CTRL | Qt::Key_I, QApplicationPrivate::KB_All},
+ {QKeySequence::DeleteEndOfLine, 0, Qt::CTRL | Qt::Key_K, QApplicationPrivate::KB_X11}, //emacs (line edit only)
+ {QKeySequence::New, 1, Qt::CTRL | Qt::Key_N, QApplicationPrivate::KB_All},
+ {QKeySequence::Open, 1, Qt::CTRL | Qt::Key_O, QApplicationPrivate::KB_All},
+ {QKeySequence::Print, 1, Qt::CTRL | Qt::Key_P, QApplicationPrivate::KB_All},
+ {QKeySequence::Refresh, 1, Qt::CTRL | Qt::Key_R, QApplicationPrivate::KB_Gnome | QApplicationPrivate::KB_Mac},
+ {QKeySequence::Replace, 0, Qt::CTRL | Qt::Key_R, QApplicationPrivate::KB_KDE},
+ {QKeySequence::Save, 1, Qt::CTRL | Qt::Key_S, QApplicationPrivate::KB_All},
+ {QKeySequence::AddTab, 0, Qt::CTRL | Qt::Key_T, QApplicationPrivate::KB_All},
+ {QKeySequence::Underline, 1, Qt::CTRL | Qt::Key_U, QApplicationPrivate::KB_All},
+ {QKeySequence::Paste, 1, Qt::CTRL | Qt::Key_V, QApplicationPrivate::KB_All},
+ {QKeySequence::Close, 0, Qt::CTRL | Qt::Key_W, QApplicationPrivate::KB_Win | QApplicationPrivate::KB_X11},
+ {QKeySequence::Close, 1, Qt::CTRL | Qt::Key_W, QApplicationPrivate::KB_Mac},
+ {QKeySequence::Cut, 1, Qt::CTRL | Qt::Key_X, QApplicationPrivate::KB_All},
+ {QKeySequence::Redo, 1, Qt::CTRL | Qt::Key_Y, QApplicationPrivate::KB_Win},
+ {QKeySequence::Redo, 0, Qt::CTRL | Qt::Key_Y, QApplicationPrivate::KB_Mac},//different priority from above
+ {QKeySequence::Undo, 1, Qt::CTRL | Qt::Key_Z, QApplicationPrivate::KB_All},
+ {QKeySequence::Back, 1, Qt::CTRL | Qt::Key_BracketLeft, QApplicationPrivate::KB_Mac},
+ {QKeySequence::Forward, 1, Qt::CTRL | Qt::Key_BracketRight, QApplicationPrivate::KB_Mac},
+ {QKeySequence::PreviousChild, 1, Qt::CTRL | Qt::Key_BraceLeft, QApplicationPrivate::KB_Mac},
+ {QKeySequence::NextChild, 1, Qt::CTRL | Qt::Key_BraceRight, QApplicationPrivate::KB_Mac},
+ {QKeySequence::NextChild, 1, Qt::CTRL | Qt::Key_Tab, QApplicationPrivate::KB_Win | QApplicationPrivate::KB_X11},
+ {QKeySequence::NextChild, 0, Qt::CTRL | Qt::Key_Tab, QApplicationPrivate::KB_Mac}, //different priority from above
+ {QKeySequence::DeleteStartOfWord, 0, Qt::CTRL | Qt::Key_Backspace, QApplicationPrivate::KB_X11 | QApplicationPrivate::KB_Win},
+ {QKeySequence::Copy, 0, Qt::CTRL | Qt::Key_Insert, QApplicationPrivate::KB_X11 | QApplicationPrivate::KB_Win},
+ {QKeySequence::DeleteEndOfWord, 0, Qt::CTRL | Qt::Key_Delete, QApplicationPrivate::KB_X11 | QApplicationPrivate::KB_Win},
+ {QKeySequence::MoveToStartOfDocument, 0, Qt::CTRL | Qt::Key_Home, QApplicationPrivate::KB_Win | QApplicationPrivate::KB_X11},
+ {QKeySequence::MoveToEndOfDocument, 0, Qt::CTRL | Qt::Key_End, QApplicationPrivate::KB_Win | QApplicationPrivate::KB_X11},
+ {QKeySequence::Back, 0, Qt::CTRL | Qt::Key_Left, QApplicationPrivate::KB_Mac},
+ {QKeySequence::MoveToPreviousWord, 0, Qt::CTRL | Qt::Key_Left, QApplicationPrivate::KB_Win | QApplicationPrivate::KB_X11},
+ {QKeySequence::MoveToStartOfLine, 0, Qt::CTRL | Qt::Key_Left, QApplicationPrivate::KB_Mac },
+ {QKeySequence::MoveToStartOfDocument, 1, Qt::CTRL | Qt::Key_Up, QApplicationPrivate::KB_Mac},
+ {QKeySequence::Forward, 0, Qt::CTRL | Qt::Key_Right, QApplicationPrivate::KB_Mac},
+ {QKeySequence::MoveToEndOfLine, 0, Qt::CTRL | Qt::Key_Right, QApplicationPrivate::KB_Mac },
+ {QKeySequence::MoveToNextWord, 0, Qt::CTRL | Qt::Key_Right, QApplicationPrivate::KB_Win | QApplicationPrivate::KB_X11},
+ {QKeySequence::MoveToEndOfDocument, 1, Qt::CTRL | Qt::Key_Down, QApplicationPrivate::KB_Mac},
+ {QKeySequence::Close, 1, Qt::CTRL | Qt::Key_F4, QApplicationPrivate::KB_Win},
+ {QKeySequence::Close, 0, Qt::CTRL | Qt::Key_F4, QApplicationPrivate::KB_Mac},
+ {QKeySequence::NextChild, 0, Qt::CTRL | Qt::Key_F6, QApplicationPrivate::KB_Win},
+ {QKeySequence::FindPrevious, 1, Qt::CTRL | Qt::SHIFT | Qt::Key_G, QApplicationPrivate::KB_Gnome | QApplicationPrivate::KB_Mac},
+ {QKeySequence::FindPrevious, 0, Qt::CTRL | Qt::SHIFT | Qt::Key_G, QApplicationPrivate::KB_Win},
+ {QKeySequence::AddTab, 1, Qt::CTRL | Qt::SHIFT | Qt::Key_N, QApplicationPrivate::KB_KDE},
+ {QKeySequence::SaveAs, 0, Qt::CTRL | Qt::SHIFT | Qt::Key_S, QApplicationPrivate::KB_Gnome | QApplicationPrivate::KB_Mac},
+ {QKeySequence::Redo, 0, Qt::CTRL | Qt::SHIFT | Qt::Key_Z, QApplicationPrivate::KB_Win | QApplicationPrivate::KB_X11},
+ {QKeySequence::Redo, 1, Qt::CTRL | Qt::SHIFT | Qt::Key_Z, QApplicationPrivate::KB_Mac}, //different priority from above
+ {QKeySequence::PreviousChild, 1, Qt::CTRL | Qt::SHIFT | Qt::Key_Backtab, QApplicationPrivate::KB_Win | QApplicationPrivate::KB_X11},
+ {QKeySequence::PreviousChild, 0, Qt::CTRL | Qt::SHIFT | Qt::Key_Backtab, QApplicationPrivate::KB_Mac },//different priority from above
+ {QKeySequence::SelectStartOfDocument, 0, Qt::CTRL | Qt::SHIFT | Qt::Key_Home, QApplicationPrivate::KB_Win | QApplicationPrivate::KB_X11},
+ {QKeySequence::SelectEndOfDocument, 0, Qt::CTRL | Qt::SHIFT | Qt::Key_End, QApplicationPrivate::KB_Win | QApplicationPrivate::KB_X11},
+ {QKeySequence::SelectPreviousWord, 0, Qt::CTRL | Qt::SHIFT | Qt::Key_Left, QApplicationPrivate::KB_Win | QApplicationPrivate::KB_X11},
+ {QKeySequence::SelectStartOfLine, 1, Qt::CTRL | Qt::SHIFT | Qt::Key_Left, QApplicationPrivate::KB_Mac },
+ {QKeySequence::SelectStartOfDocument, 1, Qt::CTRL | Qt::SHIFT | Qt::Key_Up, QApplicationPrivate::KB_Mac},
+ {QKeySequence::SelectNextWord, 0, Qt::CTRL | Qt::SHIFT | Qt::Key_Right, QApplicationPrivate::KB_Win | QApplicationPrivate::KB_X11},
+ {QKeySequence::SelectEndOfLine, 1, Qt::CTRL | Qt::SHIFT | Qt::Key_Right, QApplicationPrivate::KB_Mac },
+ {QKeySequence::SelectEndOfDocument, 1, Qt::CTRL | Qt::SHIFT | Qt::Key_Down, QApplicationPrivate::KB_Mac},
+ {QKeySequence::PreviousChild, 0, Qt::CTRL | Qt::SHIFT | Qt::Key_F6, QApplicationPrivate::KB_Win},
+ {QKeySequence::Undo, 0, Qt::ALT | Qt::Key_Backspace, QApplicationPrivate::KB_Win},
+ {QKeySequence::DeleteStartOfWord, 0, Qt::ALT | Qt::Key_Backspace, QApplicationPrivate::KB_Mac},
+ {QKeySequence::DeleteEndOfWord, 0, Qt::ALT | Qt::Key_Delete, QApplicationPrivate::KB_Mac},
+ {QKeySequence::Back, 1, Qt::ALT | Qt::Key_Left, QApplicationPrivate::KB_Win | QApplicationPrivate::KB_X11},
+ {QKeySequence::MoveToPreviousWord, 0, Qt::ALT | Qt::Key_Left, QApplicationPrivate::KB_Mac},
+ {QKeySequence::MoveToStartOfBlock, 0, Qt::ALT | Qt::Key_Up, QApplicationPrivate::KB_Mac}, //mac only
+ {QKeySequence::MoveToNextWord, 0, Qt::ALT | Qt::Key_Right, QApplicationPrivate::KB_Mac},
+ {QKeySequence::Forward, 1, Qt::ALT | Qt::Key_Right, QApplicationPrivate::KB_Win | QApplicationPrivate::KB_X11},
+ {QKeySequence::MoveToEndOfBlock, 0, Qt::ALT | Qt::Key_Down, QApplicationPrivate::KB_Mac}, //mac only
+ {QKeySequence::MoveToPreviousPage, 0, Qt::ALT | Qt::Key_PageUp, QApplicationPrivate::KB_Mac },
+ {QKeySequence::MoveToNextPage, 0, Qt::ALT | Qt::Key_PageDown, QApplicationPrivate::KB_Mac },
+ {QKeySequence::Redo, 0, Qt::ALT | Qt::SHIFT | Qt::Key_Backspace,QApplicationPrivate::KB_Win},
+ {QKeySequence::SelectPreviousWord, 0, Qt::ALT | Qt::SHIFT | Qt::Key_Left, QApplicationPrivate::KB_Mac},
+ {QKeySequence::SelectStartOfBlock, 0, Qt::ALT | Qt::SHIFT | Qt::Key_Up, QApplicationPrivate::KB_Mac}, //mac only
+ {QKeySequence::SelectNextWord, 0, Qt::ALT | Qt::SHIFT | Qt::Key_Right, QApplicationPrivate::KB_Mac},
+ {QKeySequence::SelectEndOfBlock, 0, Qt::ALT | Qt::SHIFT | Qt::Key_Down, QApplicationPrivate::KB_Mac}, //mac only
+ {QKeySequence::MoveToStartOfBlock, 0, Qt::META | Qt::Key_A, QApplicationPrivate::KB_Mac},
+ {QKeySequence::Delete, 0, Qt::META | Qt::Key_D, QApplicationPrivate::KB_Mac},
+ {QKeySequence::MoveToEndOfBlock, 0, Qt::META | Qt::Key_E, QApplicationPrivate::KB_Mac},
+ {QKeySequence::InsertLineSeparator, 0, Qt::META | Qt::Key_Return, QApplicationPrivate::KB_Mac},
+ {QKeySequence::InsertLineSeparator, 0, Qt::META | Qt::Key_Enter, QApplicationPrivate::KB_Mac},
+ {QKeySequence::MoveToStartOfLine, 0, Qt::META | Qt::Key_Left, QApplicationPrivate::KB_Mac},
+ {QKeySequence::MoveToPreviousPage, 0, Qt::META | Qt::Key_Up, QApplicationPrivate::KB_Mac},
+ {QKeySequence::MoveToEndOfLine, 0, Qt::META | Qt::Key_Right, QApplicationPrivate::KB_Mac},
+ {QKeySequence::MoveToNextPage, 0, Qt::META | Qt::Key_Down, QApplicationPrivate::KB_Mac},
+ {QKeySequence::MoveToPreviousPage, 0, Qt::META | Qt::Key_PageUp, QApplicationPrivate::KB_Mac},
+ {QKeySequence::MoveToNextPage, 0, Qt::META | Qt::Key_PageDown, QApplicationPrivate::KB_Mac},
+ {QKeySequence::SelectStartOfLine, 0, Qt::META | Qt::SHIFT | Qt::Key_Left, QApplicationPrivate::KB_Mac},
+ {QKeySequence::SelectEndOfLine, 0, Qt::META | Qt::SHIFT | Qt::Key_Right, QApplicationPrivate::KB_Mac}
+};
+
+const uint QKeySequencePrivate::numberOfKeyBindings = sizeof(QKeySequencePrivate::keyBindings)/(sizeof(QKeyBinding));
+
+
+/*!
+ \enum QKeySequence::StandardKey
+ \since 4.2
+
+ This enum represent standard key bindings. They can be used to
+ assign platform dependent keyboard shortcuts to a QAction.
+ QKeyEvent also provides the function QKeyEvent::standardKey() to
+ query if it matches an existing key binding.
+
+ Note that the key bindings are platform dependent. The currently
+ bound shortcuts can be queried using keyBindings().
+
+ \value AddTab Add new tab.
+ \value Back Navigate back.
+ \value Bold Bold text.
+ \value Close Close document/tab.
+ \value Copy Copy.
+ \value Cut Cut.
+ \value Delete Delete.
+ \value DeleteEndOfLine Delete end of line.
+ \value DeleteEndOfWord Delete word from the end of the cursor.
+ \value DeleteStartOfWord Delete the beginning of a word up to the cursor.
+ \value Find Find in document.
+ \value FindNext Find next result.
+ \value FindPrevious Find previous result.
+ \value Forward Navigate forward.
+ \value HelpContents Open help contents.
+ \value InsertLineSeparator Insert a new line.
+ \value InsertParagraphSeparator Insert a new paragraph.
+ \value Italic Italic text.
+ \value MoveToEndOfBlock Move cursor to end of block. This shortcut is only used on the OS X.
+ \value MoveToEndOfDocument Move cursor to end of document.
+ \value MoveToEndOfLine Move cursor to end of line.
+ \value MoveToNextChar Move cursor to next character.
+ \value MoveToNextLine Move cursor to next line.
+ \value MoveToNextPage Move cursor to next page.
+ \value MoveToNextWord Move cursor to next word.
+ \value MoveToPreviousChar Move cursor to previous character.
+ \value MoveToPreviousLine Move cursor to previous line.
+ \value MoveToPreviousPage Move cursor to previous page.
+ \value MoveToPreviousWord Move cursor to previous word.
+ \value MoveToStartOfBlock Move cursor to start of a block. This shortcut is only used on OS X.
+ \value MoveToStartOfDocument Move cursor to start of document.
+ \value MoveToStartOfLine Move cursor to start of line.
+ \value New Create new document.
+ \value NextChild Navigate to next tab or child window.
+ \value Open Open document.
+ \value Paste Paste.
+ \value PreviousChild Navigate to previous tab or child window.
+ \value Print Print document.
+ \value Redo Redo.
+ \value Refresh Refresh or reload current document.
+ \value Replace Find and replace.
+ \value SaveAs Save document after prompting the user for a file name.
+ \value Save Save document.
+ \value SelectAll Select all text.
+ \value SelectEndOfBlock Extend selection to the end of a text block. This shortcut is only used on OS X.
+ \value SelectEndOfDocument Extend selection to end of document.
+ \value SelectEndOfLine Extend selection to end of line.
+ \value SelectNextChar Extend selection to next character.
+ \value SelectNextLine Extend selection to next line.
+ \value SelectNextPage Extend selection to next page.
+ \value SelectNextWord Extend selection to next word.
+ \value SelectPreviousChar Extend selection to previous character.
+ \value SelectPreviousLine Extend selection to previous line.
+ \value SelectPreviousPage Extend selection to previous page.
+ \value SelectPreviousWord Extend selection to previous word.
+ \value SelectStartOfBlock Extend selection to the start of a text block. This shortcut is only used on OS X.
+ \value SelectStartOfDocument Extend selection to start of document.
+ \value SelectStartOfLine Extend selection to start of line.
+ \value Underline Underline text.
+ \value Undo Undo.
+ \value UnknownKey Unbound key.
+ \value WhatsThis Activate whats this.
+ \value ZoomIn Zoom in.
+ \value ZoomOut Zoom out.
+*/
+
+/*!
+ \since 4.2
+
+ Constructs a QKeySequence object for the given \a key.
+ The result will depend on the currently running platform.
+
+ The resulting object will be based on the first element in the
+ list of key bindings for the \a key.
+*/
+QKeySequence::QKeySequence(StandardKey key)
+{
+ const QList <QKeySequence> bindings = keyBindings(key);
+ //pick only the first/primary shortcut from current bindings
+ if (bindings.size() > 0) {
+ d = bindings.first().d;
+ d->ref.ref();
+ }
+ else
+ d = new QKeySequencePrivate();
+}
+
+
+/*!
+ Constructs an empty key sequence.
+*/
+QKeySequence::QKeySequence()
+{
+ d = new QKeySequencePrivate();
+}
+
+/*!
+ Creates a key sequence from the \a key string. For example
+ "Ctrl+O" gives CTRL+'O'. The strings "Ctrl",
+ "Shift", "Alt" and "Meta" are recognized, as well as their
+ translated equivalents in the "QShortcut" context (using
+ QObject::tr()).
+
+ Up to four key codes may be entered by separating them with
+ commas, e.g. "Alt+X,Ctrl+S,Q".
+
+ This constructor is typically used with \link QObject::tr() tr
+ \endlink(), so that shortcut keys can be replaced in
+ translations:
+
+ \snippet doc/src/snippets/code/src_gui_kernel_qkeysequence.cpp 2
+
+ Note the "File|Open" translator comment. It is by no means
+ necessary, but it provides some context for the human translator.
+*/
+QKeySequence::QKeySequence(const QString &key)
+{
+ d = new QKeySequencePrivate();
+ assign(key);
+}
+
+/*!
+ Constructs a key sequence with up to 4 keys \a k1, \a k2,
+ \a k3 and \a k4.
+
+ The key codes are listed in Qt::Key and can be combined with
+ modifiers (see Qt::Modifier) such as Qt::SHIFT, Qt::CTRL,
+ Qt::ALT, or Qt::META.
+*/
+QKeySequence::QKeySequence(int k1, int k2, int k3, int k4)
+{
+ d = new QKeySequencePrivate();
+ d->key[0] = k1;
+ d->key[1] = k2;
+ d->key[2] = k3;
+ d->key[3] = k4;
+}
+
+/*!
+ Copy constructor. Makes a copy of \a keysequence.
+ */
+QKeySequence::QKeySequence(const QKeySequence& keysequence)
+ : d(keysequence.d)
+{
+ d->ref.ref();
+}
+
+/*!
+ \since 4.2
+
+ Returns a list of key bindings for the given \a key.
+ The result of calling this function will vary based on the target platform.
+ The first element of the list indicates the primary shortcut for the given platform.
+ If the result contains more than one result, these can
+ be considered alternative shortcuts on the same platform for the given \a key.
+*/
+QList<QKeySequence> QKeySequence::keyBindings(StandardKey key)
+{
+ uint platform = QApplicationPrivate::currentPlatform();
+ QList <QKeySequence> list;
+ for (uint i = 0; i < QKeySequencePrivate::numberOfKeyBindings ; ++i) {
+ QKeyBinding keyBinding = QKeySequencePrivate::keyBindings[i];
+ if (keyBinding.standardKey == key && (keyBinding.platform & platform)) {
+ if (keyBinding.priority > 0)
+ list.prepend(QKeySequence(QKeySequencePrivate::keyBindings[i].shortcut));
+ else
+ list.append(QKeySequence(QKeySequencePrivate::keyBindings[i].shortcut));
+ }
+ }
+ return list;
+}
+
+/*!
+ Destroys the key sequence.
+ */
+QKeySequence::~QKeySequence()
+{
+ if (!d->ref.deref())
+ delete d;
+}
+
+/*!
+ \internal
+ KeySequences should never be modified, but rather just created.
+ Internally though we do need to modify to keep pace in event
+ delivery.
+*/
+
+void QKeySequence::setKey(int key, int index)
+{
+ Q_ASSERT_X(index >= 0 && index < 4, "QKeySequence::setKey", "index out of range");
+ qAtomicDetach(d);
+ d->key[index] = key;
+}
+
+/*!
+ Returns the number of keys in the key sequence.
+ The maximum is 4.
+ */
+uint QKeySequence::count() const
+{
+ if (!d->key[0])
+ return 0;
+ if (!d->key[1])
+ return 1;
+ if (!d->key[2])
+ return 2;
+ if (!d->key[3])
+ return 3;
+ return 4;
+}
+
+
+/*!
+ Returns true if the key sequence is empty; otherwise returns
+ false.
+*/
+bool QKeySequence::isEmpty() const
+{
+ return !d->key[0];
+}
+
+
+/*!
+ Returns the shortcut key sequence for the mnemonic in \a text,
+ or an empty key sequence if no mnemonics are found.
+
+ For example, mnemonic("E&xit") returns \c{Qt::ALT+Qt::Key_X},
+ mnemonic("&Quit") returns \c{ALT+Key_Q}, and mnemonic("Quit")
+ returns an empty QKeySequence.
+
+ We provide a \l{accelerators.html}{list of common mnemonics}
+ in English. At the time of writing, Microsoft and Open Group do
+ not appear to have issued equivalent recommendations for other
+ languages.
+
+ \sa qt_set_sequence_auto_mnemonic()
+*/
+QKeySequence QKeySequence::mnemonic(const QString &text)
+{
+ if(qt_sequence_no_mnemonics)
+ return QKeySequence();
+
+ int p = 0;
+ while (p >= 0) {
+ p = text.indexOf(QLatin1Char('&'), p) + 1;
+ if (p <= 0 || p >= (int)text.length())
+ break;
+ if (text.at(p) != QLatin1Char('&')) {
+ QChar c = text.at(p);
+ if (c.isPrint()) {
+ c = c.toUpper();
+ return QKeySequence(c.unicode() + Qt::ALT);
+ }
+ }
+ p++;
+ }
+ return QKeySequence();
+}
+
+/*!
+ \fn int QKeySequence::assign(const QString &keys)
+
+ Adds the given \a keys to the key sequence. \a keys may
+ contain up to four key codes, provided they are separated by a
+ comma; for example, "Alt+X,Ctrl+S,Z". The return value is the
+ number of key codes added.
+*/
+int QKeySequence::assign(const QString &ks)
+{
+ QString keyseq = ks;
+ QString part;
+ int n = 0;
+ int p = 0, diff = 0;
+
+ // Run through the whole string, but stop
+ // if we have 4 keys before the end.
+ while (keyseq.length() && n < 4) {
+ // We MUST use something to separate each sequence, and space
+ // does not cut it, since some of the key names have space
+ // in them.. (Let's hope no one translate with a comma in it:)
+ p = keyseq.indexOf(QLatin1Char(','));
+ if (-1 != p) {
+ if (p == keyseq.count() - 1) { // Last comma 'Ctrl+,'
+ p = -1;
+ } else {
+ if (QLatin1Char(',') == keyseq.at(p+1)) // e.g. 'Ctrl+,, Shift+,,'
+ p++;
+ if (QLatin1Char(' ') == keyseq.at(p+1)) { // Space after comma
+ diff = 1;
+ p++;
+ } else {
+ diff = 0;
+ }
+ }
+ }
+ part = keyseq.left(-1 == p ? keyseq.length() : p - diff);
+ keyseq = keyseq.right(-1 == p ? 0 : keyseq.length() - (p + 1));
+ d->key[n] = decodeString(part);
+ ++n;
+ }
+ return n;
+}
+
+struct QModifKeyName {
+ QModifKeyName() { }
+ QModifKeyName(int q, QChar n) : qt_key(q), name(n) { }
+ QModifKeyName(int q, const QString &n) : qt_key(q), name(n) { }
+ int qt_key;
+ QString name;
+};
+
+Q_GLOBAL_STATIC(QList<QModifKeyName>, globalModifs)
+Q_GLOBAL_STATIC(QList<QModifKeyName>, globalPortableModifs)
+
+/*!
+ Constructs a single key from the string \a str.
+*/
+int QKeySequence::decodeString(const QString &str)
+{
+ return QKeySequencePrivate::decodeString(str, NativeText);
+}
+
+int QKeySequencePrivate::decodeString(const QString &str, QKeySequence::SequenceFormat format)
+{
+ int ret = 0;
+ QString accel = str.toLower();
+ bool nativeText = (format == QKeySequence::NativeText);
+
+ QList<QModifKeyName> *gmodifs;
+ if (nativeText) {
+ gmodifs = globalModifs();
+ if (gmodifs->isEmpty()) {
+#ifdef Q_WS_MAC
+ *gmodifs << QModifKeyName(Qt::CTRL, QChar(kCommandUnicode));
+ *gmodifs << QModifKeyName(Qt::ALT, QChar(kOptionUnicode));
+ *gmodifs << QModifKeyName(Qt::META, QChar(kControlUnicode));
+ *gmodifs << QModifKeyName(Qt::SHIFT, QChar(kShiftUnicode));
+#endif
+ *gmodifs << QModifKeyName(Qt::CTRL, QLatin1String("ctrl+"))
+ << QModifKeyName(Qt::SHIFT, QLatin1String("shift+"))
+ << QModifKeyName(Qt::ALT, QLatin1String("alt+"))
+ << QModifKeyName(Qt::META, QLatin1String("meta+"));
+ }
+ } else {
+ gmodifs = globalPortableModifs();
+ if (gmodifs->isEmpty()) {
+ *gmodifs << QModifKeyName(Qt::CTRL, QLatin1String("ctrl+"))
+ << QModifKeyName(Qt::SHIFT, QLatin1String("shift+"))
+ << QModifKeyName(Qt::ALT, QLatin1String("alt+"))
+ << QModifKeyName(Qt::META, QLatin1String("meta+"));
+ }
+ }
+ if (!gmodifs) return ret;
+
+
+ QList<QModifKeyName> modifs;
+ if (nativeText) {
+ modifs << QModifKeyName(Qt::CTRL, QShortcut::tr("Ctrl").toLower().append(QLatin1Char('+')))
+ << QModifKeyName(Qt::SHIFT, QShortcut::tr("Shift").toLower().append(QLatin1Char('+')))
+ << QModifKeyName(Qt::ALT, QShortcut::tr("Alt").toLower().append(QLatin1Char('+')))
+ << QModifKeyName(Qt::META, QShortcut::tr("Meta").toLower().append(QLatin1Char('+')));
+ }
+ modifs += *gmodifs; // Test non-translated ones last
+
+ QString sl = accel;
+#ifdef Q_WS_MAC
+ for (int i = 0; i < modifs.size(); ++i) {
+ const QModifKeyName &mkf = modifs.at(i);
+ if (sl.contains(mkf.name)) {
+ ret |= mkf.qt_key;
+ accel.remove(mkf.name);
+ sl = accel;
+ }
+ }
+#else
+ int i = 0;
+ int lastI = 0;
+ while ((i = sl.indexOf(QLatin1Char('+'), i + 1)) != -1) {
+ const QString sub = sl.mid(lastI, i - lastI + 1);
+ // Just shortcut the check here if we only have one character.
+ // Rational: A modifier will contain the name AND +, so longer than 1, a length of 1 is just
+ // the remaining part of the shortcut (ei. The 'C' in "Ctrl+C"), so no need to check that.
+ if (sub.length() > 1) {
+ for (int j = 0; j < modifs.size(); ++j) {
+ const QModifKeyName &mkf = modifs.at(j);
+ if (sub == mkf.name) {
+ ret |= mkf.qt_key;
+ break; // Shortcut, since if we find an other it would/should just be a dup
+ }
+ }
+ }
+ lastI = i + 1;
+ }
+#endif
+
+ int p = accel.lastIndexOf(QLatin1Char('+'), str.length() - 2); // -2 so that Ctrl++ works
+ if(p > 0)
+ accel = accel.mid(p + 1);
+
+ int fnum = 0;
+ if (accel.length() == 1) {
+#ifdef Q_WS_MAC
+ int qtKey = qtkeyForMacSymbol(accel[0]);
+ if (qtKey != -1) {
+ ret |= qtKey;
+ } else
+#endif
+ {
+ ret |= accel[0].toUpper().unicode();
+ }
+ } else if (accel[0] == QLatin1Char('f') && (fnum = accel.mid(1).toInt()) && (fnum >= 1) && (fnum <= 35)) {
+ ret |= Qt::Key_F1 + fnum - 1;
+ } else {
+ // For NativeText, check the traslation table first,
+ // if we don't find anything then try it out with just the untranlated stuff.
+ // PortableText will only try the untranlated table.
+ bool found = false;
+ for (int tran = 0; tran < 2; ++tran) {
+ if (!nativeText)
+ ++tran;
+ for (int i = 0; keyname[i].name; ++i) {
+ QString keyName(tran == 0
+ ? QShortcut::tr(keyname[i].name)
+ : QString::fromLatin1(keyname[i].name));
+ if (accel == keyName.toLower()) {
+ ret |= keyname[i].key;
+ found = true;
+ break;
+ }
+ }
+ if (found)
+ break;
+ }
+#ifdef Q_WS_MAC
+#endif
+ }
+ return ret;
+}
+
+/*!
+ Creates a shortcut string for \a key. For example,
+ Qt::CTRL+Qt::Key_O gives "Ctrl+O". The strings, "Ctrl", "Shift", etc. are
+ translated (using QObject::tr()) in the "QShortcut" context.
+ */
+QString QKeySequence::encodeString(int key)
+{
+ return QKeySequencePrivate::encodeString(key, NativeText);
+}
+
+static inline void addKey(QString &str, const QString &theKey, QKeySequence::SequenceFormat format)
+{
+ if (!str.isEmpty())
+ str += (format == QKeySequence::NativeText) ? QShortcut::tr("+")
+ : QString::fromLatin1("+");
+ str += theKey;
+}
+
+QString QKeySequencePrivate::encodeString(int key, QKeySequence::SequenceFormat format)
+{
+ bool nativeText = (format == QKeySequence::NativeText);
+ QString s;
+#if defined(Q_WS_MAC)
+ if (nativeText) {
+ // On MAC the order is Meta, Alt, Shift, Control.
+ if ((key & Qt::META) == Qt::META)
+ s += macSymbolForQtKey(Qt::Key_Meta);
+ if ((key & Qt::ALT) == Qt::ALT)
+ s += macSymbolForQtKey(Qt::Key_Alt);
+ if ((key & Qt::SHIFT) == Qt::SHIFT)
+ s += macSymbolForQtKey(Qt::Key_Shift);
+ if ((key & Qt::CTRL) == Qt::CTRL)
+ s += macSymbolForQtKey(Qt::Key_Control);
+ } else
+#endif
+ {
+ // On other systems the order is Meta, Control, Alt, Shift
+ if ((key & Qt::META) == Qt::META)
+ s = nativeText ? QShortcut::tr("Meta") : QString::fromLatin1("Meta");
+ if ((key & Qt::CTRL) == Qt::CTRL)
+ addKey(s, nativeText ? QShortcut::tr("Ctrl") : QString::fromLatin1("Ctrl"), format);
+ if ((key & Qt::ALT) == Qt::ALT)
+ addKey(s, nativeText ? QShortcut::tr("Alt") : QString::fromLatin1("Alt"), format);
+ if ((key & Qt::SHIFT) == Qt::SHIFT)
+ addKey(s, nativeText ? QShortcut::tr("Shift") : QString::fromLatin1("Shift"), format);
+ }
+
+
+ key &= ~(Qt::ShiftModifier | Qt::ControlModifier | Qt::AltModifier | Qt::MetaModifier);
+ QString p;
+
+ if (key && key < Qt::Key_Escape && key != Qt::Key_Space) {
+ if (key < 0x10000) {
+ p = QChar(key & 0xffff).toUpper();
+ } else {
+ p = QChar((key-0x10000)/0x400+0xd800);
+ p += QChar((key-0x10000)%400+0xdc00);
+ }
+ } else if (key >= Qt::Key_F1 && key <= Qt::Key_F35) {
+ p = nativeText ? QShortcut::tr("F%1").arg(key - Qt::Key_F1 + 1)
+ : QString::fromLatin1("F%1").arg(key - Qt::Key_F1 + 1);
+ } else if (key) {
+ int i=0;
+#if defined(Q_WS_MAC)
+ if (nativeText) {
+ QChar ch = macSymbolForQtKey(key);
+ if (!ch.isNull())
+ p = ch;
+ else
+ goto NonSymbol;
+ } else
+#endif
+ {
+#ifdef Q_WS_MAC
+NonSymbol:
+#endif
+ while (keyname[i].name) {
+ if (key == keyname[i].key) {
+ p = nativeText ? QShortcut::tr(keyname[i].name)
+ : QString::fromLatin1(keyname[i].name);
+ break;
+ }
+ ++i;
+ }
+ // If we can't find the actual translatable keyname,
+ // fall back on the unicode representation of it...
+ // Or else characters like Qt::Key_aring may not get displayed
+ // (Really depends on you locale)
+ if (!keyname[i].name) {
+ if (key < 0x10000) {
+ p = QChar(key & 0xffff).toUpper();
+ } else {
+ p = QChar((key-0x10000)/0x400+0xd800);
+ p += QChar((key-0x10000)%400+0xdc00);
+ }
+ }
+ }
+ }
+
+#ifdef Q_WS_MAC
+ if (nativeText)
+ s += p;
+ else
+#endif
+ addKey(s, p, format);
+ return s;
+}
+/*!
+ Matches the sequence with \a seq. Returns ExactMatch if
+ successful, PartialMatch if \a seq matches incompletely,
+ and NoMatch if the sequences have nothing in common.
+ Returns NoMatch if \a seq is shorter.
+*/
+QKeySequence::SequenceMatch QKeySequence::matches(const QKeySequence &seq) const
+{
+ uint userN = count(),
+ seqN = seq.count();
+
+ if (userN > seqN)
+ return NoMatch;
+
+ // If equal in length, we have a potential ExactMatch sequence,
+ // else we already know it can only be partial.
+ SequenceMatch match = (userN == seqN ? ExactMatch : PartialMatch);
+
+ for (uint i = 0; i < userN; ++i) {
+ int userKey = (*this)[i],
+ sequenceKey = seq[i];
+ if (userKey != sequenceKey)
+ return NoMatch;
+ }
+ return match;
+}
+
+
+/*!
+ \obsolete
+
+ Use toString() instead.
+
+ Returns the key sequence as a QString. This is equivalent to
+ calling toString(QKeySequence::NativeText). Note that the
+ result is not platform independent.
+*/
+QKeySequence::operator QString() const
+{
+ return QKeySequence::toString(QKeySequence::NativeText);
+}
+
+/*!
+ Returns the key sequence as a QVariant
+*/
+QKeySequence::operator QVariant() const
+{
+ return QVariant(QVariant::KeySequence, this);
+}
+
+/*!
+ \obsolete
+ For backward compatibility: returns the first keycode
+ as integer. If the key sequence is empty, 0 is returned.
+ */
+QKeySequence::operator int () const
+{
+ if (1 <= count())
+ return d->key[0];
+ return 0;
+}
+
+
+/*!
+ Returns a reference to the element at position \a index in the key
+ sequence. This can only be used to read an element.
+ */
+int QKeySequence::operator[](uint index) const
+{
+ Q_ASSERT_X(index < 4, "QKeySequence::operator[]", "index out of range");
+ return d->key[index];
+}
+
+
+/*!
+ Assignment operator. Assigns the \a other key sequence to this
+ object.
+ */
+QKeySequence &QKeySequence::operator=(const QKeySequence &other)
+{
+ qAtomicAssign(d, other.d);
+ return *this;
+}
+
+/*!
+ \fn bool QKeySequence::operator!=(const QKeySequence &other) const
+
+ Returns true if this key sequence is not equal to the \a other
+ key sequence; otherwise returns false.
+*/
+
+
+/*!
+ Returns true if this key sequence is equal to the \a other
+ key sequence; otherwise returns false.
+ */
+bool QKeySequence::operator==(const QKeySequence &other) const
+{
+ return (d->key[0] == other.d->key[0] &&
+ d->key[1] == other.d->key[1] &&
+ d->key[2] == other.d->key[2] &&
+ d->key[3] == other.d->key[3]);
+}
+
+
+/*!
+ Provides an arbitrary comparison of this key sequence and
+ \a other key sequence. All that is guaranteed is that the
+ operator returns false if both key sequences are equal and
+ that (ks1 \< ks2) == !( ks2 \< ks1) if the key sequences
+ are not equal.
+
+ This function is useful in some circumstances, for example
+ if you want to use QKeySequence objects as keys in a QMap.
+
+ \sa operator==() operator!=() operator>() operator<=() operator>=()
+*/
+bool QKeySequence::operator< (const QKeySequence &other) const
+{
+ for (int i = 0; i < 4; ++i)
+ if (d->key[i] != other.d->key[i])
+ return d->key[i] < other.d->key[i];
+ return false;
+}
+
+/*!
+ \fn bool QKeySequence::operator> (const QKeySequence &other) const
+
+ Returns true if this key sequence is larger than the \a other key
+ sequence; otherwise returns false.
+
+ \sa operator==() operator!=() operator<() operator<=() operator>=()
+*/
+
+/*!
+ \fn bool QKeySequence::operator<= (const QKeySequence &other) const
+
+ Returns true if this key sequence is smaller or equal to the
+ \a other key sequence; otherwise returns false.
+
+ \sa operator==() operator!=() operator<() operator>() operator>=()
+*/
+
+/*!
+ \fn bool QKeySequence::operator>= (const QKeySequence &other) const
+
+ Returns true if this key sequence is larger or equal to the
+ \a other key sequence; otherwise returns false.
+
+ \sa operator==() operator!=() operator<() operator>() operator<=()
+*/
+
+/*!
+ \internal
+*/
+bool QKeySequence::isDetached() const
+{
+ return d->ref == 1;
+}
+
+/*!
+ \since 4.1
+
+ Return a string representation of the key sequence,
+ based on \a format.
+
+ For example, the value Qt::CTRL+Qt::Key_O results in "Ctrl+O".
+ If the key sequence has multiple key codes, each is separated
+ by commas in the string returned, such as "Alt+X, Ctrl+Y, Z".
+ The strings, "Ctrl", "Shift", etc. are translated using
+ QObject::tr() in the "QShortcut" context.
+
+ If the key sequence has no keys, an empty string is returned.
+
+ On Mac OS X, the string returned resembles the sequence that is
+ shown in the menu bar.
+
+ \sa fromString()
+*/
+QString QKeySequence::toString(SequenceFormat format) const
+{
+ QString finalString;
+ // A standard string, with no translation or anything like that. In some ways it will
+ // look like our latin case on Windows and X11
+ int end = count();
+ for (int i = 0; i < end; ++i) {
+ finalString += d->encodeString(d->key[i], format);
+ finalString += QLatin1String(", ");
+ }
+ finalString.truncate(finalString.length() - 2);
+ return finalString;
+}
+
+/*!
+ \since 4.1
+
+ Return a QKeySequence from the string \a str based on \a format.
+
+ \sa toString()
+*/
+QKeySequence QKeySequence::fromString(const QString &str, SequenceFormat format)
+{
+ QStringList sl = str.split(QLatin1String(", "));
+ int keys[4] = {0, 0, 0, 0};
+ int total = qMin(sl.count(), 4);
+ for (int i = 0; i < total; ++i)
+ keys[i] = QKeySequencePrivate::decodeString(sl[i], format);
+ return QKeySequence(keys[0], keys[1], keys[2], keys[3]);
+}
+
+/*****************************************************************************
+ QKeySequence stream functions
+ *****************************************************************************/
+#if !defined(QT_NO_DATASTREAM)
+/*!
+ \fn QDataStream &operator<<(QDataStream &stream, const QKeySequence &sequence)
+ \relates QKeySequence
+
+ Writes the key \a sequence to the \a stream.
+
+ \sa \link datastreamformat.html Format of the QDataStream operators \endlink
+*/
+QDataStream &operator<<(QDataStream &s, const QKeySequence &keysequence)
+{
+ QList<quint32> list;
+ list << keysequence.d->key[0];
+
+ if (s.version() >= 5 && keysequence.count() > 1) {
+ list << keysequence.d->key[1];
+ list << keysequence.d->key[2];
+ list << keysequence.d->key[3];
+ }
+ s << list;
+ return s;
+}
+
+
+/*!
+ \fn QDataStream &operator>>(QDataStream &stream, QKeySequence &sequence)
+ \relates QKeySequence
+
+ Reads a key sequence from the \a stream into the key \a sequence.
+
+ \sa \link datastreamformat.html Format of the QDataStream operators \endlink
+*/
+QDataStream &operator>>(QDataStream &s, QKeySequence &keysequence)
+{
+ qAtomicDetach(keysequence.d);
+ QList<quint32> list;
+ s >> list;
+ for (int i = 0; i < 4; ++i)
+ keysequence.d->key[i] = list.value(i);
+ return s;
+}
+
+#endif //QT_NO_DATASTREAM
+
+#ifndef QT_NO_DEBUG_STREAM
+QDebug operator<<(QDebug dbg, const QKeySequence &p)
+{
+#ifndef Q_BROKEN_DEBUG_STREAM
+ dbg.nospace() << "QKeySequence(" << p.toString() << ')';
+ return dbg.space();
+#else
+ qWarning("This compiler doesn't support streaming QKeySequence to QDebug");
+ return dbg;
+ Q_UNUSED(p);
+#endif
+}
+#endif
+
+#endif // QT_NO_SHORTCUT
+
+
+/*!
+ \typedef QKeySequence::DataPtr
+ \internal
+*/
+
+ /*!
+ \fn DataPtr &QKeySequence::data_ptr()
+ \internal
+*/
+
+QT_END_NAMESPACE
diff --git a/src/gui/kernel/qkeysequence.h b/src/gui/kernel/qkeysequence.h
new file mode 100644
index 0000000000..1c4776fab8
--- /dev/null
+++ b/src/gui/kernel/qkeysequence.h
@@ -0,0 +1,231 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QKEYSEQUENCE_H
+#define QKEYSEQUENCE_H
+
+#include <QtCore/qnamespace.h>
+#include <QtCore/qstring.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+#ifndef QT_NO_SHORTCUT
+
+/*****************************************************************************
+ QKeySequence stream functions
+ *****************************************************************************/
+#ifndef QT_NO_DATASTREAM
+class QKeySequence;
+Q_GUI_EXPORT QDataStream &operator<<(QDataStream &in, const QKeySequence &ks);
+Q_GUI_EXPORT QDataStream &operator>>(QDataStream &out, QKeySequence &ks);
+#endif
+
+#ifdef qdoc
+void qt_set_sequence_auto_mnemonic(bool b);
+#endif
+
+class QVariant;
+class QKeySequencePrivate;
+
+class Q_GUI_EXPORT QKeySequence
+{
+public:
+ enum StandardKey {
+ UnknownKey,
+ HelpContents,
+ WhatsThis,
+ Open,
+ Close,
+ Save,
+ New,
+ Delete,
+ Cut,
+ Copy,
+ Paste,
+ Undo,
+ Redo,
+ Back,
+ Forward,
+ Refresh,
+ ZoomIn,
+ ZoomOut,
+ Print,
+ AddTab,
+ NextChild,
+ PreviousChild,
+ Find,
+ FindNext,
+ FindPrevious,
+ Replace,
+ SelectAll,
+ Bold,
+ Italic,
+ Underline,
+ MoveToNextChar,
+ MoveToPreviousChar,
+ MoveToNextWord,
+ MoveToPreviousWord,
+ MoveToNextLine,
+ MoveToPreviousLine,
+ MoveToNextPage,
+ MoveToPreviousPage,
+ MoveToStartOfLine,
+ MoveToEndOfLine,
+ MoveToStartOfBlock,
+ MoveToEndOfBlock,
+ MoveToStartOfDocument,
+ MoveToEndOfDocument,
+ SelectNextChar,
+ SelectPreviousChar,
+ SelectNextWord,
+ SelectPreviousWord,
+ SelectNextLine,
+ SelectPreviousLine,
+ SelectNextPage,
+ SelectPreviousPage,
+ SelectStartOfLine,
+ SelectEndOfLine,
+ SelectStartOfBlock,
+ SelectEndOfBlock,
+ SelectStartOfDocument,
+ SelectEndOfDocument,
+ DeleteStartOfWord,
+ DeleteEndOfWord,
+ DeleteEndOfLine,
+ InsertParagraphSeparator,
+ InsertLineSeparator,
+ SaveAs
+ };
+
+ QKeySequence();
+ QKeySequence(const QString &key);
+ QKeySequence(int k1, int k2 = 0, int k3 = 0, int k4 = 0);
+ QKeySequence(const QKeySequence &ks);
+ QKeySequence(StandardKey key);
+ ~QKeySequence();
+
+ uint count() const; // ### Qt 5: return 'int'
+ bool isEmpty() const;
+
+ enum SequenceMatch {
+ NoMatch,
+ PartialMatch,
+ ExactMatch
+#ifdef QT3_SUPPORT
+ , Identical = ExactMatch
+#endif
+ };
+
+ enum SequenceFormat {
+ NativeText,
+ PortableText
+ };
+
+ QString toString(SequenceFormat format = PortableText) const;
+ static QKeySequence fromString(const QString &str, SequenceFormat format = PortableText);
+
+ SequenceMatch matches(const QKeySequence &seq) const;
+ static QKeySequence mnemonic(const QString &text);
+ static QList<QKeySequence> keyBindings(StandardKey key);
+
+ // ### Qt 5: kill 'operator QString' - it's evil
+ operator QString() const;
+ operator QVariant() const;
+ operator int() const;
+ int operator[](uint i) const;
+ QKeySequence &operator=(const QKeySequence &other);
+ bool operator==(const QKeySequence &other) const;
+ inline bool operator!= (const QKeySequence &other) const
+ { return !(*this == other); }
+ bool operator< (const QKeySequence &ks) const;
+ inline bool operator> (const QKeySequence &other) const
+ { return other < *this; }
+ inline bool operator<= (const QKeySequence &other) const
+ { return !(other < *this); }
+ inline bool operator>= (const QKeySequence &other) const
+ { return !(*this < other); }
+
+ bool isDetached() const;
+private:
+ static int decodeString(const QString &ks);
+ static QString encodeString(int key);
+ int assign(const QString &str);
+ void setKey(int key, int index);
+
+ QKeySequencePrivate *d;
+
+ friend Q_GUI_EXPORT QDataStream &operator<<(QDataStream &in, const QKeySequence &ks);
+ friend Q_GUI_EXPORT QDataStream &operator>>(QDataStream &in, QKeySequence &ks);
+ friend class Q3AccelManager;
+ friend class QShortcutMap;
+ friend class QShortcut;
+
+public:
+ typedef QKeySequencePrivate * DataPtr;
+ inline DataPtr &data_ptr() { return d; }
+};
+Q_DECLARE_TYPEINFO(QKeySequence, Q_MOVABLE_TYPE);
+Q_DECLARE_SHARED(QKeySequence)
+
+#ifndef QT_NO_DEBUG_STREAM
+Q_GUI_EXPORT QDebug operator<<(QDebug, const QKeySequence &);
+#endif
+
+#else
+
+class Q_GUI_EXPORT QKeySequence
+{
+public:
+ QKeySequence() {}
+ QKeySequence(int) {}
+};
+
+#endif // QT_NO_SHORTCUT
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QKEYSEQUENCE_H
diff --git a/src/gui/kernel/qkeysequence_p.h b/src/gui/kernel/qkeysequence_p.h
new file mode 100644
index 0000000000..197e62a460
--- /dev/null
+++ b/src/gui/kernel/qkeysequence_p.h
@@ -0,0 +1,98 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QKEYSEQUENCE_P_H
+#define QKEYSEQUENCE_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of qapplication_*.cpp, qwidget*.cpp and qfiledialog.cpp. This header
+// file may change from version to version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qkeysequence.h"
+
+QT_BEGIN_NAMESPACE
+
+#ifndef QT_NO_SHORTCUT
+struct Q_AUTOTEST_EXPORT QKeyBinding
+{
+ QKeySequence::StandardKey standardKey;
+ uchar priority;
+ uint shortcut;
+ uint platform;
+};
+
+class Q_AUTOTEST_EXPORT QKeySequencePrivate
+{
+public:
+ inline QKeySequencePrivate()
+ {
+ ref = 1;
+ key[0] = key[1] = key[2] = key[3] = 0;
+ }
+ inline QKeySequencePrivate(const QKeySequencePrivate &copy)
+ {
+ ref = 1;
+ key[0] = copy.key[0];
+ key[1] = copy.key[1];
+ key[2] = copy.key[2];
+ key[3] = copy.key[3];
+ }
+ QAtomicInt ref;
+ int key[4];
+ static QString encodeString(int key, QKeySequence::SequenceFormat format);
+ static int decodeString(const QString &keyStr, QKeySequence::SequenceFormat format);
+
+ static const QKeyBinding keyBindings[];
+ static const uint numberOfKeyBindings;
+
+};
+#endif // QT_NO_SHORTCUT
+
+QT_END_NAMESPACE
+
+#endif //QKEYSEQUENCE_P_H
diff --git a/src/gui/kernel/qlayout.cpp b/src/gui/kernel/qlayout.cpp
new file mode 100644
index 0000000000..aa4624942d
--- /dev/null
+++ b/src/gui/kernel/qlayout.cpp
@@ -0,0 +1,1585 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qlayout.h"
+
+#include "qapplication.h"
+#include "qlayoutengine_p.h"
+#include "qmenubar.h"
+#include "qtoolbar.h"
+#include "qsizegrip.h"
+#include "qevent.h"
+#include "qstyle.h"
+#include "qvariant.h"
+#include "qwidget_p.h"
+#include "qlayout_p.h"
+#include "qformlayout.h"
+
+QT_BEGIN_NAMESPACE
+
+static int menuBarHeightForWidth(QWidget *menubar, int w)
+{
+ if (menubar && !menubar->isHidden() && !menubar->isWindow()) {
+ int result = menubar->heightForWidth(qMax(w, menubar->minimumWidth()));
+ if (result != -1)
+ return result;
+ result = menubar->sizeHint().height();
+ if (result != -1)
+ return result;
+ }
+ return 0;
+}
+
+/*!
+ \class QLayout
+ \brief The QLayout class is the base class of geometry managers.
+
+ \ingroup appearance
+ \ingroup geomanagement
+
+ This is an abstract base class inherited by the concrete classes
+ QBoxLayout, QGridLayout, QFormLayout, and QStackedLayout.
+
+ For users of QLayout subclasses or of QMainWindow there is seldom
+ any need to use the basic functions provided by QLayout, such as
+ setSizeConstraint() or setMenuBar(). See \l{Layout Classes}
+ for more information.
+
+ To make your own layout manager, implement the functions
+ addItem(), sizeHint(), setGeometry(), itemAt() and takeAt(). You
+ should also implement minimumSize() to ensure your layout isn't
+ resized to zero size if there is too little space. To support
+ children whose heights depend on their widths, implement
+ hasHeightForWidth() and heightForWidth(). See the
+ \l{layouts/borderlayout}{Border Layout} and
+ \l{layouts/flowlayout}{Flow Layout} examples for
+ more information about implementing custom layout managers.
+
+ Geometry management stops when the layout manager is deleted.
+
+ \sa QLayoutItem, {Layout Classes}, {Basic Layouts Example},
+ {Border Layout Example}, {Flow Layout Example}
+*/
+
+
+/*!
+ Constructs a new top-level QLayout, with parent \a parent.
+ \a parent may not be 0.
+
+ There can be only one top-level layout for a widget. It is
+ returned by QWidget::layout().
+*/
+QLayout::QLayout(QWidget *parent)
+ : QObject(*new QLayoutPrivate, parent)
+{
+ if (!parent)
+ return;
+ parent->setLayout(this);
+}
+
+/*!
+ Constructs a new child QLayout.
+
+ This layout has to be inserted into another layout before geometry
+ management will work.
+*/
+QLayout::QLayout()
+ : QObject(*new QLayoutPrivate, 0)
+{
+}
+
+
+/*! \internal
+ */
+QLayout::QLayout(QLayoutPrivate &dd, QLayout *lay, QWidget *w)
+ : QObject(dd, lay ? static_cast<QObject*>(lay) : static_cast<QObject*>(w))
+{
+ Q_D(QLayout);
+ if (lay) {
+ lay->addItem(this);
+ } else if (w) {
+ if (w->layout()) {
+ qWarning("QLayout: Attempting to add QLayout \"%s\" to %s \"%s\", which"
+ " already has a layout",
+ qPrintable(QObject::objectName()), w->metaObject()->className(),
+ w->objectName().toLocal8Bit().data());
+ setParent(0);
+ } else {
+ d->topLevel = true;
+ w->d_func()->layout = this;
+ invalidate();
+ }
+ }
+}
+
+QLayoutPrivate::QLayoutPrivate()
+ : QObjectPrivate(), insideSpacing(-1), userLeftMargin(-1), userTopMargin(-1), userRightMargin(-1),
+ userBottomMargin(-1), topLevel(false), enabled(true), activated(true), autoNewChild(false),
+ constraint(QLayout::SetDefaultConstraint), menubar(0)
+{
+}
+
+void QLayoutPrivate::getMargin(int *result, int userMargin, QStyle::PixelMetric pm) const
+{
+ if (!result)
+ return;
+
+ Q_Q(const QLayout);
+ if (userMargin >= 0) {
+ *result = userMargin;
+ } else if (!topLevel) {
+ *result = 0;
+ } else if (QWidget *pw = q->parentWidget()) {
+ *result = pw->style()->pixelMetric(pm, 0, pw);
+ } else {
+ *result = 0;
+ }
+}
+
+// Static item factory functions that allow for hooking things in Designer
+
+QLayoutPrivate::QWidgetItemFactoryMethod QLayoutPrivate::widgetItemFactoryMethod = 0;
+QLayoutPrivate::QSpacerItemFactoryMethod QLayoutPrivate::spacerItemFactoryMethod = 0;
+
+QWidgetItem *QLayoutPrivate::createWidgetItem(const QLayout *layout, QWidget *widget)
+{
+ if (widgetItemFactoryMethod)
+ if (QWidgetItem *wi = (*widgetItemFactoryMethod)(layout, widget))
+ return wi;
+ return new QWidgetItemV2(widget);
+}
+
+QSpacerItem *QLayoutPrivate::createSpacerItem(const QLayout *layout, int w, int h, QSizePolicy::Policy hPolicy, QSizePolicy::Policy vPolicy)
+{
+ if (spacerItemFactoryMethod)
+ if (QSpacerItem *si = (*spacerItemFactoryMethod)(layout, w, h, hPolicy, vPolicy))
+ return si;
+ return new QSpacerItem(w, h, hPolicy, vPolicy);
+}
+
+#ifdef QT3_SUPPORT
+/*!
+ Constructs a new top-level QLayout called \a name, with parent
+ widget \a parent. \a parent may not be 0.
+
+ The \a margin is the number of pixels between the edge of the
+ widget and the managed children. The \a spacing sets the value of
+ spacing(), which gives the spacing between the managed widgets. If
+ \a spacing is -1 (the default), spacing is set to the value of \a
+ margin.
+
+ There can be only one top-level layout for a widget. It is
+ returned by QWidget::layout()
+
+ \sa QWidget::setLayout()
+*/
+QLayout::QLayout(QWidget *parent, int margin, int spacing, const char *name)
+ : QObject(*new QLayoutPrivate,parent)
+{
+ Q_D(QLayout);
+ setObjectName(QString::fromAscii(name));
+ setMargin(margin);
+ if (spacing < 0)
+ d->insideSpacing = margin;
+ else
+ d->insideSpacing = spacing;
+ if (parent) {
+ if (parent->layout()) {
+ qWarning("QLayout \"%s\" added to %s \"%s\", which already has a layout",
+ QObject::objectName().toLocal8Bit().data(), parent->metaObject()->className(),
+ parent->objectName().toLocal8Bit().data());
+ parent->layout()->setParent(0);
+ } else {
+ d->topLevel = true;
+ parent->d_func()->layout = this;
+ invalidate();
+ }
+ }
+}
+
+/*!
+ Constructs a new child QLayout called \a name, and places it
+ inside \a parentLayout by using the default placement defined by
+ addItem().
+
+ If \a spacing is -1, this QLayout inherits \a parentLayout's
+ spacing(), otherwise the value of \a spacing is used.
+*/
+QLayout::QLayout(QLayout *parentLayout, int spacing, const char *name)
+ : QObject(*new QLayoutPrivate,parentLayout)
+
+{
+ Q_D(QLayout);
+ setObjectName(QString::fromAscii(name));
+ d->insideSpacing = spacing;
+ parentLayout->addItem(this);
+}
+
+/*!
+ Constructs a new child QLayout called \a name. If \a spacing is
+ -1, this QLayout inherits its parent's spacing(); otherwise the
+ value of \a spacing is used.
+
+ This layout has to be inserted into another layout before geometry
+ management will work.
+*/
+QLayout::QLayout(int spacing, const char *name)
+ : QObject(*new QLayoutPrivate, 0)
+{
+ Q_D(QLayout);
+ setObjectName(QString::fromAscii(name));
+ d->insideSpacing = spacing;
+}
+
+/*!
+ Automatically adding widgets is deprecated. Use addWidget() or
+ addLayout() instead.
+*/
+void QLayout::setAutoAdd(bool a) { Q_D(QLayout); d->autoNewChild = a; }
+
+/*!
+ Automatically adding widgets is deprecated. Use addWidget() or
+ addLayout() instead.
+*/
+bool QLayout::autoAdd() const { Q_D(const QLayout); return d->autoNewChild; }
+#endif
+
+
+/*!
+ \fn void QLayout::addItem(QLayoutItem *item)
+
+ Implemented in subclasses to add an \a item. How it is added is
+ specific to each subclass.
+
+ This function is not usually called in application code. To add a widget
+ to a layout, use the addWidget() function; to add a child layout, use the
+ addLayout() function provided by the relevant QLayout subclass.
+
+ \bold{Note:} The ownership of \a item is transferred to the layout, and it's
+ the layout's responsibility to delete it.
+
+ \sa addWidget(), QBoxLayout::addLayout(), QGridLayout::addLayout()
+*/
+
+/*!
+ Adds widget \a w to this layout in a manner specific to the
+ layout. This function uses addItem().
+*/
+void QLayout::addWidget(QWidget *w)
+{
+ addChildWidget(w);
+ addItem(QLayoutPrivate::createWidgetItem(this, w));
+}
+
+
+
+/*!
+ Sets the alignment for widget \a w to \a alignment and returns
+ true if \a w is found in this layout (not including child
+ layouts); otherwise returns false.
+*/
+bool QLayout::setAlignment(QWidget *w, Qt::Alignment alignment)
+{
+ int i = 0;
+ QLayoutItem *item = itemAt(i);
+ while (item) {
+ if (item->widget() == w) {
+ item->setAlignment(alignment);
+ invalidate();
+ return true;
+ }
+ ++i;
+ item = itemAt(i);
+ }
+ return false;
+}
+
+/*!
+ \overload
+
+ Sets the alignment for the layout \a l to \a alignment and
+ returns true if \a l is found in this layout (not including child
+ layouts); otherwise returns false.
+*/
+bool QLayout::setAlignment(QLayout *l, Qt::Alignment alignment)
+{
+ int i = 0;
+ QLayoutItem *item = itemAt(i);
+ while (item) {
+ if (item->layout() == l) {
+ item->setAlignment(alignment);
+ invalidate();
+ return true;
+ }
+ ++i;
+ item = itemAt(i);
+ }
+ return false;
+}
+
+/*!
+ \fn void QLayout::setAlignment(Qt::Alignment alignment)
+
+ Sets the alignment of this item to \a alignment.
+
+ \sa QLayoutItem::setAlignment()
+*/
+
+/*!
+ \fn bool QLayout::isTopLevel() const
+
+ Returns true if this layout is a top-level layout, i.e. not a
+ child of another layout; otherwise returns false.
+*/
+
+/*!
+ \property QLayout::margin
+ \brief the width of the outside border of the layout
+ \obsolete
+
+ Use setContentsMargins() and getContentsMargins() instead.
+
+ \sa contentsRect(), spacing
+*/
+
+/*!
+ \obsolete
+*/
+int QLayout::margin() const
+{
+ int left, top, right, bottom;
+ getContentsMargins(&left, &top, &right, &bottom);
+ if (left == top && top == right && right == bottom) {
+ return left;
+ } else {
+ return -1;
+ }
+}
+
+/*!
+ \property QLayout::spacing
+ \brief the spacing between widgets inside the layout
+
+ If no value is explicitly set, the layout's spacing is inherited
+ from the parent layout, or from the style settings for the parent
+ widget.
+
+ For QGridLayout and QFormLayout, it is possible to set different horizontal and
+ vertical spacings using \l{QGridLayout::}{setHorizontalSpacing()}
+ and \l{QGridLayout::}{setVerticalSpacing()}. In that case,
+ spacing() returns -1.
+
+ \sa contentsRect(), getContentsMargins(), QStyle::layoutSpacing(),
+ QStyle::pixelMetric()
+*/
+
+int QLayout::spacing() const
+{
+ if (const QBoxLayout* boxlayout = qobject_cast<const QBoxLayout*>(this)) {
+ return boxlayout->spacing();
+ } else if (const QGridLayout* gridlayout = qobject_cast<const QGridLayout*>(this)) {
+ return gridlayout->spacing();
+ } else if (const QFormLayout* formlayout = qobject_cast<const QFormLayout*>(this)) {
+ return formlayout->spacing();
+ } else {
+ Q_D(const QLayout);
+ if (d->insideSpacing >=0) {
+ return d->insideSpacing;
+ } else {
+ // arbitrarily prefer horizontal spacing to vertical spacing
+ return qSmartSpacing(this, QStyle::PM_LayoutHorizontalSpacing);
+ }
+ }
+}
+
+/*!
+ \obsolete
+*/
+void QLayout::setMargin(int margin)
+{
+ setContentsMargins(margin, margin, margin, margin);
+}
+
+void QLayout::setSpacing(int spacing)
+{
+ if (QBoxLayout* boxlayout = qobject_cast<QBoxLayout*>(this)) {
+ boxlayout->setSpacing(spacing);
+ } else if (QGridLayout* gridlayout = qobject_cast<QGridLayout*>(this)) {
+ gridlayout->setSpacing(spacing);
+ } else if (QFormLayout* formlayout = qobject_cast<QFormLayout*>(this)) {
+ formlayout->setSpacing(spacing);
+ } else {
+ Q_D(QLayout);
+ d->insideSpacing = spacing;
+ invalidate();
+ }
+}
+
+/*!
+ \since 4.3
+
+ Sets the \a left, \a top, \a right, and \a bottom margins to use
+ around the layout.
+
+ By default, QLayout uses the values provided by the style. On
+ most platforms, the margin is 11 pixels in all directions.
+
+ \sa getContentsMargins(), QStyle::pixelMetric(),
+ {QStyle::}{PM_LayoutLeftMargin},
+ {QStyle::}{PM_LayoutTopMargin},
+ {QStyle::}{PM_LayoutRightMargin},
+ {QStyle::}{PM_LayoutBottomMargin}
+*/
+void QLayout::setContentsMargins(int left, int top, int right, int bottom)
+{
+ Q_D(QLayout);
+
+ if (d->userLeftMargin == left && d->userTopMargin == top &&
+ d->userRightMargin == right && d->userBottomMargin == bottom)
+ return;
+
+ d->userLeftMargin = left;
+ d->userTopMargin = top;
+ d->userRightMargin = right;
+ d->userBottomMargin = bottom;
+ invalidate();
+}
+
+/*!
+ \since 4.3
+
+ Extracts the left, top, right, and bottom margins used around the
+ layout, and assigns them to *\a left, *\a top, *\a right, and *\a
+ bottom (unless they are null pointers).
+
+ By default, QLayout uses the values provided by the style. On
+ most platforms, the margin is 11 pixels in all directions.
+
+ \sa setContentsMargins(), QStyle::pixelMetric(),
+ {QStyle::}{PM_LayoutLeftMargin},
+ {QStyle::}{PM_LayoutTopMargin},
+ {QStyle::}{PM_LayoutRightMargin},
+ {QStyle::}{PM_LayoutBottomMargin}
+*/
+void QLayout::getContentsMargins(int *left, int *top, int *right, int *bottom) const
+{
+ Q_D(const QLayout);
+ d->getMargin(left, d->userLeftMargin, QStyle::PM_LayoutLeftMargin);
+ d->getMargin(top, d->userTopMargin, QStyle::PM_LayoutTopMargin);
+ d->getMargin(right, d->userRightMargin, QStyle::PM_LayoutRightMargin);
+ d->getMargin(bottom, d->userBottomMargin, QStyle::PM_LayoutBottomMargin);
+}
+
+/*!
+ \since 4.3
+
+ Returns the layout's geometry() rectangle, but taking into account the
+ contents margins.
+
+ \sa setContentsMargins(), getContentsMargins()
+*/
+QRect QLayout::contentsRect() const
+{
+ Q_D(const QLayout);
+ int left, top, right, bottom;
+ getContentsMargins(&left, &top, &right, &bottom);
+ return d->rect.adjusted(+left, +top, -right, -bottom);
+}
+
+#ifdef QT3_SUPPORT
+bool QLayout::isTopLevel() const
+{
+ Q_D(const QLayout);
+ return d->topLevel;
+}
+#endif
+
+/*!
+ Returns the parent widget of this layout, or 0 if this layout is
+ not installed on any widget.
+
+ If the layout is a sub-layout, this function returns the parent
+ widget of the parent layout.
+
+ \sa parent()
+*/
+QWidget *QLayout::parentWidget() const
+{
+ Q_D(const QLayout);
+ if (!d->topLevel) {
+ if (parent()) {
+ QLayout *parentLayout = qobject_cast<QLayout*>(parent());
+ if (!parentLayout) {
+ qWarning("QLayout::parentWidget: A layout can only have another layout as a parent.");
+ return 0;
+ }
+ return parentLayout->parentWidget();
+ } else {
+ return 0;
+ }
+ } else {
+ Q_ASSERT(parent() && parent()->isWidgetType());
+ return static_cast<QWidget *>(parent());
+ }
+}
+
+/*!
+ \reimp
+*/
+bool QLayout::isEmpty() const
+{
+ int i = 0;
+ QLayoutItem *item = itemAt(i);
+ while (item) {
+ if (!item->isEmpty())
+ return false;
+ ++i;
+ item = itemAt(i);
+ }
+ return true;
+}
+
+/*!
+ \reimp
+*/
+void QLayout::setGeometry(const QRect &r)
+{
+ Q_D(QLayout);
+ d->rect = r;
+}
+
+/*!
+ \reimp
+*/
+QRect QLayout::geometry() const
+{
+ Q_D(const QLayout);
+ return d->rect;
+}
+
+/*!
+ \reimp
+*/
+void QLayout::invalidate()
+{
+ Q_D(QLayout);
+ d->rect = QRect();
+ update();
+}
+
+static bool removeWidgetRecursively(QLayoutItem *li, QWidget *w)
+{
+ QLayout *lay = li->layout();
+ if (!lay)
+ return false;
+ int i = 0;
+ QLayoutItem *child;
+ while ((child = lay->itemAt(i))) {
+ if (child->widget() == w) {
+ delete lay->takeAt(i);
+ lay->invalidate();
+ return true;
+ } else if (removeWidgetRecursively(child, w)) {
+ return true;
+ } else {
+ ++i;
+ }
+ }
+ return false;
+}
+
+
+void QLayoutPrivate::doResize(const QSize &r)
+{
+ Q_Q(QLayout);
+ int mbh = menuBarHeightForWidth(menubar, r.width());
+ QWidget *mw = q->parentWidget();
+ QRect rect = mw->testAttribute(Qt::WA_LayoutOnEntireRect) ? mw->rect() : mw->contentsRect();
+ rect.setTop(rect.top() + mbh);
+ q->setGeometry(rect);
+#ifndef QT_NO_MENUBAR
+ if (menubar)
+ menubar->setGeometry(0,0,r.width(), mbh);
+#endif
+}
+
+
+/*!
+ \internal
+ Performs child widget layout when the parent widget is
+ resized. Also handles removal of widgets. \a e is the
+ event
+*/
+void QLayout::widgetEvent(QEvent *e)
+{
+ Q_D(QLayout);
+ if (!d->enabled)
+ return;
+
+ switch (e->type()) {
+ case QEvent::Resize:
+ if (d->activated) {
+ QResizeEvent *r = (QResizeEvent *)e;
+ d->doResize(r->size());
+ } else {
+ activate();
+ }
+ break;
+ case QEvent::ChildRemoved:
+ {
+ QChildEvent *c = (QChildEvent *)e;
+ if (c->child()->isWidgetType()) {
+ QWidget *w = (QWidget *)c->child();
+#ifndef QT_NO_MENUBAR
+ if (w == d->menubar)
+ d->menubar = 0;
+#endif
+ removeWidgetRecursively(this, w);
+ }
+ }
+ break;
+#ifdef QT3_SUPPORT
+ case QEvent::ChildInserted:
+ if (d->topLevel && d->autoNewChild) {
+ QChildEvent *c = (QChildEvent *)e;
+ if (c->child()->isWidgetType()) {
+ QWidget *w = (QWidget *)c->child();
+ if (!w->isWindow()) {
+#if !defined(QT_NO_MENUBAR) && !defined(QT_NO_TOOLBAR)
+ if (qobject_cast<QMenuBar*>(w) && !qobject_cast<QToolBar*>(w->parentWidget())) {
+ d->menubar = (QMenuBar *)w;
+ invalidate();
+ } else
+#endif
+#ifndef QT_NO_SIZEGRIP
+ if (qobject_cast<QSizeGrip*>(w) ) {
+ //SizeGrip is handled by the dialog itself.
+ } else
+#endif
+ addItem(QLayoutPrivate::createWidgetItem(this, w));
+ }
+ }
+ }
+ break;
+ case QEvent::LayoutHint:
+ d->activated = false;
+ // fall through
+#endif
+ case QEvent::LayoutRequest:
+ if (static_cast<QWidget *>(parent())->isVisible())
+ activate();
+ break;
+ default:
+ break;
+ }
+}
+
+/*!
+ \reimp
+*/
+void QLayout::childEvent(QChildEvent *e)
+{
+ Q_D(QLayout);
+ if (!d->enabled)
+ return;
+
+ if (e->type() == QEvent::ChildRemoved) {
+ QChildEvent *c = (QChildEvent*)e;
+ int i = 0;
+
+ QLayoutItem *item;
+ while ((item = itemAt(i))) {
+ if (item == static_cast<QLayout*>(c->child())) {
+ takeAt(i);
+ invalidate();
+ break;
+ } else {
+ ++i;
+ }
+ }
+ }
+}
+
+/*!
+ \internal
+ Also takes contentsMargins and menu bar into account.
+*/
+int QLayout::totalHeightForWidth(int w) const
+{
+ Q_D(const QLayout);
+ int side=0, top=0;
+ if (d->topLevel) {
+ QWidget *parent = parentWidget();
+ parent->ensurePolished();
+ QWidgetPrivate *wd = parent->d_func();
+ side += wd->leftmargin + wd->rightmargin;
+ top += wd->topmargin + wd->bottommargin;
+ }
+ int h = heightForWidth(w - side) + top;
+#ifndef QT_NO_MENUBAR
+ h += menuBarHeightForWidth(d->menubar, w);
+#endif
+ return h;
+}
+
+/*!
+ \internal
+ Also takes contentsMargins and menu bar into account.
+*/
+QSize QLayout::totalMinimumSize() const
+{
+ Q_D(const QLayout);
+ int side=0, top=0;
+ if (d->topLevel) {
+ QWidget *pw = parentWidget();
+ pw->ensurePolished();
+ QWidgetPrivate *wd = pw->d_func();
+ side += wd->leftmargin + wd->rightmargin;
+ top += wd->topmargin + wd->bottommargin;
+ }
+
+ QSize s = minimumSize();
+#ifndef QT_NO_MENUBAR
+ top += menuBarHeightForWidth(d->menubar, s.width() + side);
+#endif
+ return s + QSize(side, top);
+}
+
+/*!
+ \internal
+ Also takes contentsMargins and menu bar into account.
+*/
+QSize QLayout::totalSizeHint() const
+{
+ Q_D(const QLayout);
+ int side=0, top=0;
+ if (d->topLevel) {
+ QWidget *pw = parentWidget();
+ pw->ensurePolished();
+ QWidgetPrivate *wd = pw->d_func();
+ side += wd->leftmargin + wd->rightmargin;
+ top += wd->topmargin + wd->bottommargin;
+ }
+
+ QSize s = sizeHint();
+ if (hasHeightForWidth())
+ s.setHeight(heightForWidth(s.width() + side));
+#ifndef QT_NO_MENUBAR
+ top += menuBarHeightForWidth(d->menubar, s.width());
+#endif
+ return s + QSize(side, top);
+}
+
+/*!
+ \internal
+ Also takes contentsMargins and menu bar into account.
+*/
+QSize QLayout::totalMaximumSize() const
+{
+ Q_D(const QLayout);
+ int side=0, top=0;
+ if (d->topLevel) {
+ QWidget *pw = parentWidget();
+ pw->ensurePolished();
+ QWidgetPrivate *wd = pw->d_func();
+ side += wd->leftmargin + wd->rightmargin;
+ top += wd->topmargin + wd->bottommargin;
+ }
+
+ QSize s = maximumSize();
+#ifndef QT_NO_MENUBAR
+ top += menuBarHeightForWidth(d->menubar, s.width());
+#endif
+
+ if (d->topLevel)
+ s = QSize(qMin(s.width() + side, QLAYOUTSIZE_MAX),
+ qMin(s.height() + top, QLAYOUTSIZE_MAX));
+ return s;
+}
+
+/*!
+ \internal
+ Destroys the layout, deleting all child layouts.
+ Geometry management stops when a top-level layout is deleted.
+
+ The layout classes will probably be fatally confused if you delete
+ a sublayout.
+*/
+QLayout::~QLayout()
+{
+ Q_D(QLayout);
+ /*
+ This function may be called during the QObject destructor,
+ when the parent no longer is a QWidget.
+ */
+ if (d->topLevel && parent() && parent()->isWidgetType() &&
+ ((QWidget*)parent())->layout() == this)
+ ((QWidget*)parent())->d_func()->layout = 0;
+}
+
+#ifdef QT3_SUPPORT
+/*!
+ Removes and deletes all items in this layout.
+*/
+void QLayout::deleteAllItems()
+{
+ QLayoutItem *l;
+ while ((l = takeAt(0)))
+ delete l;
+}
+#endif
+
+/*!
+ This function is called from \c addLayout() or \c insertLayout() functions in
+ subclasses to add layout \a l as a sub-layout.
+
+ The only scenario in which you need to call it directly is if you
+ implement a custom layout that supports nested layouts.
+
+ \sa QBoxLayout::addLayout(), QBoxLayout::insertLayout(), QGridLayout::addLayout()
+*/
+void QLayout::addChildLayout(QLayout *l)
+{
+ if (l->parent()) {
+ qWarning("QLayout::addChildLayout: layout \"%s\" already has a parent",
+ l->objectName().toLocal8Bit().data());
+ return;
+ }
+ l->setParent(this);
+
+ if (QWidget *mw = parentWidget()) {
+ l->d_func()->reparentChildWidgets(mw);
+ }
+
+}
+
+#ifdef QT_DEBUG
+static bool layoutDebug()
+{
+ static int checked_env = -1;
+ if(checked_env == -1)
+ checked_env = !!qgetenv("QT_LAYOUT_DEBUG").toInt();
+
+ return checked_env;
+}
+#endif
+
+void QLayoutPrivate::reparentChildWidgets(QWidget *mw)
+{
+ Q_Q(QLayout);
+ int n = q->count();
+
+#ifndef QT_NO_MENUBAR
+ if (menubar && menubar->parentWidget() != mw) {
+ menubar->setParent(mw);
+ }
+#endif
+ bool mwVisible = mw && mw->isVisible();
+ for (int i = 0; i < n; ++i) {
+ QLayoutItem *item = q->itemAt(i);
+ if (QWidget *w = item->widget()) {
+ QWidget *pw = w->parentWidget();
+#ifdef QT_DEBUG
+ if (pw && pw != mw && layoutDebug()) {
+ qWarning("QLayout::addChildLayout: widget %s \"%s\" in wrong parent; moved to correct parent",
+ w->metaObject()->className(), w->objectName().toLocal8Bit().data());
+ }
+#endif
+ bool needShow = mwVisible && !(w->isHidden() && w->testAttribute(Qt::WA_WState_ExplicitShowHide));
+ if (pw != mw)
+ w->setParent(mw);
+ if (needShow)
+ QMetaObject::invokeMethod(w, "_q_showIfNotHidden", Qt::QueuedConnection); //show later
+ } else if (QLayout *l = item->layout()) {
+ l->d_func()->reparentChildWidgets(mw);
+ }
+ }
+}
+
+/*!
+ This function is called from \c addWidget() functions in
+ subclasses to add \a w as a child widget.
+
+ If \a w is already in a layout, this function will give a warning
+ and remove \a w from the layout. This function must therefore be
+ called before adding \a w to the layout's data structure.
+*/
+void QLayout::addChildWidget(QWidget *w)
+{
+ QWidget *mw = parentWidget();
+ QWidget *pw = w->parentWidget();
+
+ //Qt::WA_LaidOut is never reset. It only means that the widget at some point has
+ //been in a layout.
+ if (pw && w->testAttribute(Qt::WA_LaidOut)) {
+ QLayout *l = pw->layout();
+ if (l && removeWidgetRecursively(l, w)) {
+#ifdef QT_DEBUG
+ if (layoutDebug())
+ qWarning("QLayout::addChildWidget: %s \"%s\" is already in a layout; moved to new layout",
+ w->metaObject()->className(), w->objectName().toLocal8Bit().data());
+#endif
+ }
+ }
+ if (pw && mw && pw != mw) {
+#ifdef QT_DEBUG
+ if (layoutDebug())
+ qWarning("QLayout::addChildWidget: %s \"%s\" in wrong parent; moved to correct parent",
+ w->metaObject()->className(), w->objectName().toLocal8Bit().data());
+#endif
+ pw = 0;
+ }
+ bool needShow = mw && mw->isVisible() && !(w->isHidden() && w->testAttribute(Qt::WA_WState_ExplicitShowHide));
+ if (!pw && mw)
+ w->setParent(mw);
+ w->setAttribute(Qt::WA_LaidOut);
+ if (needShow)
+ QMetaObject::invokeMethod(w, "_q_showIfNotHidden", Qt::QueuedConnection); //show later
+}
+
+#ifdef QT3_SUPPORT
+/*!
+ \compat
+
+ Sets this layout's parent widget to a fixed size with width \a w
+ and height \a h, stopping the user from resizing it, and also
+ prevents the layout from resizing it, even if the layout's size
+ hint should change. Does nothing if this is not a top-level
+ layout (i.e., if parent()->isWidgetType()).
+
+ As a special case, if both \a w and \a h are 0, then the layout's
+ current sizeHint() is used.
+
+ Use \c setResizeMode(Fixed) to stop the widget from being resized
+ by the user, while still allowing the layout to resize it when
+ the sizeHint() changes.
+
+ Use \c setResizeMode(FreeResize) to allow the user to resize the
+ widget, while preventing the layout from resizing it.
+
+*/
+void QLayout::freeze(int w, int h)
+{
+ Q_D(QLayout);
+ if (!d->topLevel)
+ return;
+ if (w <= 0 || h <= 0) {
+ QSize s = totalSizeHint();
+ w = s.width();
+ h = s.height();
+ }
+ setSizeConstraint(SetNoConstraint); // layout will not change min/max size
+ QWidget *parent = parentWidget();
+ if (parent)
+ parent->setFixedSize(w, h);
+}
+
+#endif
+
+
+
+
+
+
+
+/*!
+ Tells the geometry manager to place the menu bar \a widget at the
+ top of parentWidget(), outside QWidget::contentsMargins(). All
+ child widgets are placed below the bottom edge of the menu bar.
+*/
+void QLayout::setMenuBar(QWidget *widget)
+{
+ Q_D(QLayout);
+ if (widget)
+ addChildWidget(widget);
+ d->menubar = widget;
+}
+
+/*!
+ Returns the menu bar set for this layout, or 0 if no menu bar is
+ set.
+*/
+
+QWidget *QLayout::menuBar() const
+{
+ Q_D(const QLayout);
+ return d->menubar;
+}
+
+
+/*!
+ Returns the minimum size of this layout. This is the smallest
+ size that the layout can have while still respecting the
+ specifications.
+
+ The returned value doesn't include the space required by
+ QWidget::setContentsMargins() or menuBar().
+
+ The default implementation allows unlimited resizing.
+*/
+QSize QLayout::minimumSize() const
+{
+ return QSize(0, 0);
+}
+
+/*!
+ Returns the maximum size of this layout. This is the largest size
+ that the layout can have while still respecting the
+ specifications.
+
+ The returned value doesn't include the space required by
+ QWidget::setContentsMargins() or menuBar().
+
+ The default implementation allows unlimited resizing.
+*/
+QSize QLayout::maximumSize() const
+{
+ return QSize(QLAYOUTSIZE_MAX, QLAYOUTSIZE_MAX);
+}
+
+/*!
+ Returns whether this layout can make use of more space than
+ sizeHint(). A value of Qt::Vertical or Qt::Horizontal means that
+ it wants to grow in only one dimension, whereas Qt::Vertical |
+ Qt::Horizontal means that it wants to grow in both dimensions.
+
+ The default implementation returns Qt::Horizontal | Qt::Vertical.
+ Subclasses reimplement it to return a meaningful value based on
+ their child widgets's \l{QSizePolicy}{size policies}.
+
+ \sa sizeHint()
+*/
+Qt::Orientations QLayout::expandingDirections() const
+{
+ return Qt::Horizontal | Qt::Vertical;
+}
+
+void QLayout::activateRecursiveHelper(QLayoutItem *item)
+{
+ item->invalidate();
+ QLayout *layout = item->layout();
+ if (layout) {
+ QLayoutItem *child;
+ int i=0;
+ while ((child = layout->itemAt(i++)))
+ activateRecursiveHelper(child);
+ layout->d_func()->activated = true;
+ }
+}
+
+/*!
+ Updates the layout for parentWidget().
+
+ You should generally not need to call this because it is
+ automatically called at the most appropriate times.
+
+ \sa activate(), invalidate()
+*/
+
+void QLayout::update()
+{
+ QLayout *layout = this;
+ while (layout && layout->d_func()->activated) {
+ layout->d_func()->activated = false;
+ if (layout->d_func()->topLevel) {
+ Q_ASSERT(layout->parent()->isWidgetType());
+ QWidget *mw = static_cast<QWidget*>(layout->parent());
+ QApplication::postEvent(mw, new QEvent(QEvent::LayoutRequest));
+ break;
+ }
+ layout = static_cast<QLayout*>(layout->parent());
+ }
+}
+
+/*!
+ Redoes the layout for parentWidget() if necessary.
+
+ You should generally not need to call this because it is
+ automatically called at the most appropriate times. It returns
+ true if the layout was redone.
+
+ \sa update(), QWidget::updateGeometry()
+*/
+bool QLayout::activate()
+{
+ Q_D(QLayout);
+ if (!d->enabled || !parent())
+ return false;
+ if (!d->topLevel)
+ return static_cast<QLayout*>(parent())->activate();
+ if (d->activated)
+ return false;
+ QWidget *mw = static_cast<QWidget*>(parent());
+ if (mw == 0) {
+ qWarning("QLayout::activate: %s \"%s\" does not have a main widget",
+ QObject::metaObject()->className(), QObject::objectName().toLocal8Bit().data());
+ return false;
+ }
+ activateRecursiveHelper(this);
+
+ QWidgetPrivate *md = mw->d_func();
+ uint explMin = md->extra ? md->extra->explicitMinSize : 0;
+ uint explMax = md->extra ? md->extra->explicitMaxSize : 0;
+
+ switch (d->constraint) {
+ case SetFixedSize:
+ // will trigger resize
+ mw->setFixedSize(totalSizeHint());
+ break;
+ case SetMinimumSize:
+ mw->setMinimumSize(totalMinimumSize());
+ break;
+ case SetMaximumSize:
+ mw->setMaximumSize(totalMaximumSize());
+ break;
+ case SetMinAndMaxSize:
+ mw->setMinimumSize(totalMinimumSize());
+ mw->setMaximumSize(totalMaximumSize());
+ break;
+ case SetDefaultConstraint: {
+ bool widthSet = explMin & Qt::Horizontal;
+ bool heightSet = explMin & Qt::Vertical;
+ if (mw->isWindow()) {
+ QSize ms = totalMinimumSize();
+ if (widthSet)
+ ms.setWidth(mw->minimumSize().width());
+ if (heightSet)
+ ms.setHeight(mw->minimumSize().height());
+ if ((!heightSet || !widthSet) && hasHeightForWidth()) {
+ int h = minimumHeightForWidth(ms.width());
+ if (h > ms.height()) {
+ if (!heightSet)
+ ms.setHeight(0);
+ if (!widthSet)
+ ms.setWidth(0);
+ }
+ }
+ mw->setMinimumSize(ms);
+ } else if (!widthSet || !heightSet) {
+ QSize ms = mw->minimumSize();
+ if (!widthSet)
+ ms.setWidth(0);
+ if (!heightSet)
+ ms.setHeight(0);
+ mw->setMinimumSize(ms);
+ }
+ break;
+ }
+ case SetNoConstraint:
+ break;
+ }
+
+ d->doResize(mw->size());
+
+ if (md->extra) {
+ md->extra->explicitMinSize = explMin;
+ md->extra->explicitMaxSize = explMax;
+ }
+ // ideally only if sizeHint() or sizePolicy() has changed
+ mw->updateGeometry();
+ return true;
+}
+
+/*!
+ \fn QLayoutItem *QLayout::itemAt(int index) const
+
+ Must be implemented in subclasses to return the layout item at \a
+ index. If there is no such item, the function must return 0.
+ Items are numbered consecutively from 0. If an item is deleted, other items will be renumbered.
+
+ This function can be used to iterate over a layout. The following
+ code will draw a rectangle for each layout item in the layout structure of the widget.
+
+ \snippet doc/src/snippets/code/src_gui_kernel_qlayout.cpp 0
+
+ \sa count(), takeAt()
+*/
+
+/*!
+ \fn QLayoutItem *QLayout::takeAt(int index)
+
+ Must be implemented in subclasses to remove the layout item at \a
+ index from the layout, and return the item. If there is no such
+ item, the function must do nothing and return 0. Items are numbered
+ consecutively from 0. If an item is deleted, other items will be
+ renumbered.
+
+ The following code fragment shows a safe way to remove all items
+ from a layout:
+
+ \snippet doc/src/snippets/code/src_gui_kernel_qlayout.cpp 1
+
+ \sa itemAt(), count()
+*/
+
+/*!
+ \fn int *QLayout::count() const
+
+ Must be implemented in subclasses to return the number of items
+ in the layout.
+
+ \sa itemAt()
+*/
+
+/*!
+ Searches for widget \a widget in this layout (not including child
+ layouts).
+
+ Returns the index of \a widget, or -1 if \a widget is not found.
+
+ The default implementation iterates over all items using itemAt()
+*/
+int QLayout::indexOf(QWidget *widget) const
+{
+ int i = 0;
+ QLayoutItem *item = itemAt(i);
+ while (item) {
+ if (item->widget() == widget)
+ return i;
+ ++i;
+ item = itemAt(i);
+ }
+ return -1;
+}
+
+/*!
+ \enum QLayout::SizeConstraint
+
+ The possible values are:
+
+ \value SetDefaultConstraint The main widget's minimum size is set
+ to minimumSize(), unless the widget already has
+ a minimum size.
+
+ \value SetFixedSize The main widget's size is set to sizeHint(); it
+ cannot be resized at all.
+ \value SetMinimumSize The main widget's minimum size is set to
+ minimumSize(); it cannot be smaller.
+
+ \value SetMaximumSize The main widget's maximum size is set to
+ maximumSize(); it cannot be larger.
+
+ \value SetMinAndMaxSize The main widget's minimum size is set to
+ minimumSize() and its maximum size is set to
+ maximumSize().
+
+ \value SetNoConstraint The widget is not constrained.
+
+ \omitvalue Auto
+ \omitvalue FreeResize
+ \omitvalue Minimum
+ \omitvalue Fixed
+
+ \sa setSizeConstraint()
+*/
+
+/*!
+ \property QLayout::sizeConstraint
+ \brief the resize mode of the layout
+
+ The default mode is \l {QLayout::SetDefaultConstraint}
+ {SetDefaultConstraint}.
+*/
+void QLayout::setSizeConstraint(SizeConstraint constraint)
+{
+ Q_D(QLayout);
+ if (constraint == d->constraint)
+ return;
+
+ d->constraint = constraint;
+ invalidate();
+}
+
+QLayout::SizeConstraint QLayout::sizeConstraint() const
+{
+ Q_D(const QLayout);
+ return d->constraint;
+}
+
+/*!
+ Returns the rectangle that should be covered when the geometry of
+ this layout is set to \a r, provided that this layout supports
+ setAlignment().
+
+ The result is derived from sizeHint() and expanding(). It is never
+ larger than \a r.
+*/
+QRect QLayout::alignmentRect(const QRect &r) const
+{
+ QSize s = sizeHint();
+ Qt::Alignment a = alignment();
+
+ /*
+ This is a hack to obtain the real maximum size, not
+ QSize(QLAYOUTSIZE_MAX, QLAYOUTSIZE_MAX), the value consistently
+ returned by QLayoutItems that have an alignment.
+ */
+ QLayout *that = const_cast<QLayout *>(this);
+ that->setAlignment(0);
+ QSize ms = that->maximumSize();
+ that->setAlignment(a);
+
+ if ((expandingDirections() & Qt::Horizontal) ||
+ !(a & Qt::AlignHorizontal_Mask)) {
+ s.setWidth(qMin(r.width(), ms.width()));
+ }
+ if ((expandingDirections() & Qt::Vertical) ||
+ !(a & Qt::AlignVertical_Mask)) {
+ s.setHeight(qMin(r.height(), ms.height()));
+ } else if (hasHeightForWidth()) {
+ int hfw = heightForWidth(s.width());
+ if (hfw < s.height())
+ s.setHeight(qMin(hfw, ms.height()));
+ }
+
+ s = s.boundedTo(r.size());
+ int x = r.x();
+ int y = r.y();
+
+ if (a & Qt::AlignBottom)
+ y += (r.height() - s.height());
+ else if (!(a & Qt::AlignTop))
+ y += (r.height() - s.height()) / 2;
+
+ QWidget *parent = parentWidget();
+ a = QStyle::visualAlignment(parent ? parent->layoutDirection() : QApplication::layoutDirection(), a);
+ if (a & Qt::AlignRight)
+ x += (r.width() - s.width());
+ else if (!(a & Qt::AlignLeft))
+ x += (r.width() - s.width()) / 2;
+
+ return QRect(x, y, s.width(), s.height());
+}
+
+/*!
+ Removes the widget \a widget from the layout. After this call, it
+ is the caller's responsibility to give the widget a reasonable
+ geometry or to put the widget back into a layout.
+
+ \bold{Note:} The ownership of \a widget remains the same as
+ when it was added.
+
+ \sa removeItem(), QWidget::setGeometry(), addWidget()
+*/
+void QLayout::removeWidget(QWidget *widget)
+{
+ int i = 0;
+ QLayoutItem *child;
+ while ((child = itemAt(i))) {
+ if (child->widget() == widget) {
+ delete takeAt(i);
+ invalidate();
+ } else {
+ ++i;
+ }
+ }
+}
+
+/*!
+ Removes the layout item \a item from the layout. It is the
+ caller's responsibility to delete the item.
+
+ Notice that \a item can be a layout (since QLayout inherits
+ QLayoutItem).
+
+ \sa removeWidget(), addItem()
+*/
+void QLayout::removeItem(QLayoutItem *item)
+{
+ int i = 0;
+ QLayoutItem *child;
+ while ((child = itemAt(i))) {
+ if (child == item) {
+ takeAt(i);
+ invalidate();
+ } else {
+ ++i;
+ }
+ }
+}
+
+/*!
+ Enables this layout if \a enable is true, otherwise disables it.
+
+ An enabled layout adjusts dynamically to changes; a disabled
+ layout acts as if it did not exist.
+
+ By default all layouts are enabled.
+
+ \sa isEnabled()
+*/
+void QLayout::setEnabled(bool enable)
+{
+ Q_D(QLayout);
+ d->enabled = enable;
+}
+
+/*!
+ Returns true if the layout is enabled; otherwise returns false.
+
+ \sa setEnabled()
+*/
+bool QLayout::isEnabled() const
+{
+ Q_D(const QLayout);
+ return d->enabled;
+}
+
+/*!
+ Returns a size that satisfies all size constraints on \a widget,
+ including heightForWidth() and that is as close as possible to \a
+ size.
+*/
+
+QSize QLayout::closestAcceptableSize(const QWidget *widget, const QSize &size)
+{
+ QSize result = size.boundedTo(qSmartMaxSize(widget));
+ result = result.expandedTo(qSmartMinSize(widget));
+ QLayout *l = widget->layout();
+ if (l && l->hasHeightForWidth() && result.height() < l->minimumHeightForWidth(result.width()) ) {
+ QSize current = widget->size();
+ int currentHfw = l->minimumHeightForWidth(current.width());
+ int newHfw = l->minimumHeightForWidth(result.width());
+ if (current.height() < currentHfw || currentHfw == newHfw) {
+ //handle the constant hfw case and the vertical-only case, as well as the
+ // current-size-is-not-correct case
+ result.setHeight(newHfw);
+ } else {
+ // binary search; assume hfw is decreasing ###
+
+ int maxw = qMax(widget->width(),result.width());
+ int maxh = qMax(widget->height(), result.height());
+ int minw = qMin(widget->width(),result.width());
+ int minh = qMin(widget->height(), result.height());
+
+ int minhfw = l->minimumHeightForWidth(minw);
+ int maxhfw = l->minimumHeightForWidth(maxw);
+ while (minw < maxw) {
+ if (minhfw > maxh) { //assume decreasing
+ minw = maxw - (maxw-minw)/2;
+ minhfw = l->minimumHeightForWidth(minw);
+ } else if (maxhfw < minh ) { //assume decreasing
+ maxw = minw + (maxw-minw)/2;
+ maxhfw = l->minimumHeightForWidth(maxw);
+ } else {
+ break;
+ }
+ }
+ result = result.expandedTo(QSize(minw, minhfw));
+ }
+ }
+ return result;
+}
+
+/*!
+ \fn void QLayout::setResizeMode(SizeConstraint constraint)
+
+ Use setSizeConstraint(\a constraint) instead.
+*/
+
+/*!
+ \fn QLayout::SizeConstraint QLayout::resizeMode() const
+
+ Use sizeConstraint() instead.
+*/
+
+void QSizePolicy::setControlType(ControlType type)
+{
+ /*
+ The control type is a flag type, with values 0x1, 0x2, 0x4, 0x8, 0x10,
+ etc. In memory, we pack it onto the available bits (CTSize) in
+ setControlType(), and unpack it here.
+
+ Example:
+
+ 0x00000001 maps to 0x00000000
+ 0x00000002 maps to 0x00000200
+ 0x00000004 maps to 0x00000400
+ 0x00000008 maps to 0x00000600
+ etc.
+ */
+
+ int i = 0;
+ while (true) {
+ if (type & (0x1 << i)) {
+ data = (data & ~CTMask) | (i << CTShift);
+ return;
+ }
+ ++i;
+ }
+}
+
+QSizePolicy::ControlType QSizePolicy::controlType() const
+{
+ return QSizePolicy::ControlType(0x1 << ((data & CTMask) >> CTShift));
+}
+
+#ifndef QT_NO_DATASTREAM
+/*!
+ \relates QSizePolicy
+ \since 4.2
+
+ Writes the size \a policy to the data stream \a stream.
+
+ \sa \link datastreamformat.html Format of the QDataStream operators \endlink
+*/
+QDataStream &operator<<(QDataStream &stream, const QSizePolicy &policy)
+{
+ return stream << policy.data;
+}
+
+/*!
+ \relates QSizePolicy
+ \since 4.2
+
+ Reads the size \a policy from the data stream \a stream.
+
+ \sa \link datastreamformat.html Format of the QDataStream operators \endlink
+*/
+QDataStream &operator>>(QDataStream &stream, QSizePolicy &policy)
+{
+ return stream >> policy.data;
+}
+
+#endif
+
+
+QT_END_NAMESPACE
diff --git a/src/gui/kernel/qlayout.h b/src/gui/kernel/qlayout.h
new file mode 100644
index 0000000000..a68a96c214
--- /dev/null
+++ b/src/gui/kernel/qlayout.h
@@ -0,0 +1,242 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QLAYOUT_H
+#define QLAYOUT_H
+
+#include <QtCore/qobject.h>
+#include <QtGui/qlayoutitem.h>
+#include <QtGui/qsizepolicy.h>
+#include <QtCore/qrect.h>
+
+#include <limits.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+class QLayout;
+class QSize;
+
+#ifdef QT3_SUPPORT
+class Q_GUI_EXPORT QLayoutIterator
+{
+public:
+ inline QT3_SUPPORT_CONSTRUCTOR QLayoutIterator(QLayout *i) : layout(i), index(0) {}
+ inline QLayoutIterator(const QLayoutIterator &i)
+ : layout(i.layout), index(i.index) {}
+ inline QLayoutIterator &operator=(const QLayoutIterator &i) {
+ layout = i.layout;
+ index = i.index;
+ return *this;
+ }
+ inline QT3_SUPPORT QLayoutItem *operator++();
+ inline QT3_SUPPORT QLayoutItem *current();
+ inline QT3_SUPPORT QLayoutItem *takeCurrent();
+ inline QT3_SUPPORT void deleteCurrent();
+
+private:
+ // hack to avoid deprecated warning
+ friend class QLayout;
+ inline QLayoutIterator(QLayout *i, bool) : layout(i), index(0) {}
+ QLayout *layout;
+ int index;
+};
+#endif
+
+class QLayoutPrivate;
+
+class Q_GUI_EXPORT QLayout : public QObject, public QLayoutItem
+{
+ Q_OBJECT
+ Q_DECLARE_PRIVATE(QLayout)
+
+ Q_ENUMS(SizeConstraint)
+ Q_PROPERTY(int margin READ margin WRITE setMargin)
+ Q_PROPERTY(int spacing READ spacing WRITE setSpacing)
+ Q_PROPERTY(SizeConstraint sizeConstraint READ sizeConstraint WRITE setSizeConstraint)
+public:
+ enum SizeConstraint {
+ SetDefaultConstraint,
+ SetNoConstraint,
+ SetMinimumSize,
+ SetFixedSize,
+ SetMaximumSize,
+ SetMinAndMaxSize
+#if defined(QT3_SUPPORT) && !defined(Q_MOC_RUN)
+ , Auto = SetDefaultConstraint,
+ FreeResize = SetNoConstraint,
+ Minimum = SetMinimumSize,
+ Fixed = SetFixedSize
+#endif
+ };
+
+ QLayout(QWidget *parent);
+ QLayout();
+ ~QLayout();
+
+ int margin() const;
+ int spacing() const;
+
+ void setMargin(int);
+ void setSpacing(int);
+
+ void setContentsMargins(int left, int top, int right, int bottom);
+ void getContentsMargins(int *left, int *top, int *right, int *bottom) const;
+ QRect contentsRect() const;
+
+ bool setAlignment(QWidget *w, Qt::Alignment alignment);
+ bool setAlignment(QLayout *l, Qt::Alignment alignment);
+#ifdef Q_NO_USING_KEYWORD
+ inline void setAlignment(Qt::Alignment alignment) { QLayoutItem::setAlignment(alignment); }
+#else
+ using QLayoutItem::setAlignment;
+#endif
+
+ void setSizeConstraint(SizeConstraint);
+ SizeConstraint sizeConstraint() const;
+#ifdef QT3_SUPPORT
+ inline QT3_SUPPORT void setResizeMode(SizeConstraint s) {setSizeConstraint(s);}
+ inline QT3_SUPPORT SizeConstraint resizeMode() const {return sizeConstraint();}
+#endif
+ void setMenuBar(QWidget *w);
+ QWidget *menuBar() const;
+
+ QWidget *parentWidget() const;
+
+ void invalidate();
+ QRect geometry() const;
+ bool activate();
+ void update();
+
+ void addWidget(QWidget *w);
+ virtual void addItem(QLayoutItem *) = 0;
+
+ void removeWidget(QWidget *w);
+ void removeItem(QLayoutItem *);
+
+ Qt::Orientations expandingDirections() const;
+ QSize minimumSize() const;
+ QSize maximumSize() const;
+ virtual void setGeometry(const QRect&);
+ virtual QLayoutItem *itemAt(int index) const = 0;
+ virtual QLayoutItem *takeAt(int index) = 0;
+ virtual int indexOf(QWidget *) const;
+ virtual int count() const = 0;
+ bool isEmpty() const;
+
+ int totalHeightForWidth(int w) const;
+ QSize totalMinimumSize() const;
+ QSize totalMaximumSize() const;
+ QSize totalSizeHint() const;
+ QLayout *layout();
+
+ void setEnabled(bool);
+ bool isEnabled() const;
+
+#ifdef QT3_SUPPORT
+ QT3_SUPPORT void freeze(int w=0, int h=0);
+ QT3_SUPPORT bool isTopLevel() const;
+#endif
+
+ static QSize closestAcceptableSize(const QWidget *w, const QSize &s);
+
+protected:
+ void widgetEvent(QEvent *);
+ void childEvent(QChildEvent *e);
+ void addChildLayout(QLayout *l);
+ void addChildWidget(QWidget *w);
+#ifdef QT3_SUPPORT
+ QT3_SUPPORT void deleteAllItems();
+#endif
+
+ QRect alignmentRect(const QRect&) const;
+protected:
+ QLayout(QLayoutPrivate &d, QLayout*, QWidget*);
+
+private:
+ Q_DISABLE_COPY(QLayout)
+
+ static void activateRecursiveHelper(QLayoutItem *item);
+
+ friend class QApplicationPrivate;
+ friend class QWidget;
+
+#ifdef QT3_SUPPORT
+public:
+ QT3_SUPPORT_CONSTRUCTOR QLayout(QWidget *parent, int margin, int spacing = -1,
+ const char *name = 0);
+ QT3_SUPPORT_CONSTRUCTOR QLayout(QLayout *parentLayout, int spacing = -1, const char *name = 0);
+ QT3_SUPPORT_CONSTRUCTOR QLayout(int spacing, const char *name = 0);
+ inline QT3_SUPPORT QWidget *mainWidget() const { return parentWidget(); }
+ inline QT3_SUPPORT void remove(QWidget *w) { removeWidget(w); }
+ inline QT3_SUPPORT void add(QWidget *w) { addWidget(w); }
+
+ QT3_SUPPORT void setAutoAdd(bool a);
+ QT3_SUPPORT bool autoAdd() const;
+ inline QT3_SUPPORT QLayoutIterator iterator() { return QLayoutIterator(this,true); }
+
+ inline QT3_SUPPORT int defaultBorder() const { return spacing(); }
+#endif
+};
+
+#ifdef QT3_SUPPORT
+inline QLayoutItem *QLayoutIterator::operator++() { return layout->itemAt(++index); }
+inline QLayoutItem *QLayoutIterator::current() { return layout->itemAt(index); }
+inline QLayoutItem *QLayoutIterator::takeCurrent() { return layout->takeAt(index); }
+inline void QLayoutIterator::deleteCurrent() { delete layout->takeAt(index); }
+#endif
+
+//### support old includes
+#if 1 //def QT3_SUPPORT
+QT_BEGIN_INCLUDE_NAMESPACE
+#include <QtGui/qboxlayout.h>
+#include <QtGui/qgridlayout.h>
+QT_END_INCLUDE_NAMESPACE
+#endif
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QLAYOUT_H
diff --git a/src/gui/kernel/qlayout_p.h b/src/gui/kernel/qlayout_p.h
new file mode 100644
index 0000000000..5c2546da16
--- /dev/null
+++ b/src/gui/kernel/qlayout_p.h
@@ -0,0 +1,101 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QLAYOUT_P_H
+#define QLAYOUT_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of qlayout*.cpp, and qabstractlayout.cpp. This header
+// file may change from version to version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "private/qobject_p.h"
+#include "qstyle.h"
+#include "qsizepolicy.h"
+
+QT_BEGIN_NAMESPACE
+
+class QWidgetItem;
+class QSpacerItem;
+
+class Q_GUI_EXPORT QLayoutPrivate : public QObjectPrivate
+{
+ Q_DECLARE_PUBLIC(QLayout)
+
+public:
+ typedef QWidgetItem * (*QWidgetItemFactoryMethod)(const QLayout *layout, QWidget *widget);
+ typedef QSpacerItem * (*QSpacerItemFactoryMethod)(const QLayout *layout, int w, int h, QSizePolicy::Policy hPolicy, QSizePolicy::Policy);
+
+ QLayoutPrivate();
+
+ void getMargin(int *result, int userMargin, QStyle::PixelMetric pm) const;
+ void doResize(const QSize &);
+ void reparentChildWidgets(QWidget *mw);
+
+ static QWidgetItem *createWidgetItem(const QLayout *layout, QWidget *widget);
+ static QSpacerItem *createSpacerItem(const QLayout *layout, int w, int h, QSizePolicy::Policy hPolicy = QSizePolicy::Minimum, QSizePolicy::Policy vPolicy = QSizePolicy::Minimum);
+
+ static QWidgetItemFactoryMethod widgetItemFactoryMethod;
+ static QSpacerItemFactoryMethod spacerItemFactoryMethod;
+
+ int insideSpacing;
+ int userLeftMargin;
+ int userTopMargin;
+ int userRightMargin;
+ int userBottomMargin;
+ uint topLevel : 1;
+ uint enabled : 1;
+ uint activated : 1;
+ uint autoNewChild : 1;
+ QLayout::SizeConstraint constraint;
+ QRect rect;
+ QWidget *menubar;
+};
+
+QT_END_NAMESPACE
+
+#endif // QLAYOUT_P_H
diff --git a/src/gui/kernel/qlayoutengine.cpp b/src/gui/kernel/qlayoutengine.cpp
new file mode 100644
index 0000000000..fbaa388bf7
--- /dev/null
+++ b/src/gui/kernel/qlayoutengine.cpp
@@ -0,0 +1,436 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qlayout.h"
+#include "private/qlayoutengine_p.h"
+
+#include "qvector.h"
+#include "qwidget.h"
+
+#include <qlist.h>
+#include <qalgorithms.h>
+
+#include <qdebug.h>
+
+QT_BEGIN_NAMESPACE
+
+//#define QLAYOUT_EXTRA_DEBUG
+
+typedef qint64 Fixed64;
+static inline Fixed64 toFixed(int i) { return (Fixed64)i * 256; }
+static inline int fRound(Fixed64 i) {
+ return (i % 256 < 128) ? i / 256 : 1 + i / 256;
+}
+
+/*
+ This is the main workhorse of the QGridLayout. It portions out
+ available space to the chain's children.
+
+ The calculation is done in fixed point: "fixed" variables are
+ scaled by a factor of 256.
+
+ If the layout runs "backwards" (i.e. RightToLeft or Up) the layout
+ is computed mirror-reversed, and it's the caller's responsibility
+ do reverse the values before use.
+
+ chain contains input and output parameters describing the geometry.
+ count is the count of items in the chain; pos and space give the
+ interval (relative to parentWidget topLeft).
+*/
+void qGeomCalc(QVector<QLayoutStruct> &chain, int start, int count,
+ int pos, int space, int spacer)
+{
+ int cHint = 0;
+ int cMin = 0;
+ int cMax = 0;
+ int sumStretch = 0;
+ int sumSpacing = 0;
+
+ bool wannaGrow = false; // anyone who really wants to grow?
+ // bool canShrink = false; // anyone who could be persuaded to shrink?
+
+ bool allEmptyNonstretch = true;
+ int pendingSpacing = -1;
+ int spacerCount = 0;
+ int i;
+
+ for (i = start; i < start + count; i++) {
+ QLayoutStruct *data = &chain[i];
+
+ data->done = false;
+ cHint += data->smartSizeHint();
+ cMin += data->minimumSize;
+ cMax += data->maximumSize;
+ sumStretch += data->stretch;
+ if (!data->empty) {
+ /*
+ Using pendingSpacing, we ensure that the spacing for the last
+ (non-empty) item is ignored.
+ */
+ if (pendingSpacing >= 0) {
+ sumSpacing += pendingSpacing;
+ ++spacerCount;
+ }
+ pendingSpacing = data->effectiveSpacer(spacer);
+ }
+ wannaGrow = wannaGrow || data->expansive || data->stretch > 0;
+ allEmptyNonstretch = allEmptyNonstretch && !wannaGrow && data->empty;
+ }
+
+ int extraspace = 0;
+
+ if (space < cMin + sumSpacing) {
+ /*
+ Less space than minimumSize; take from the biggest first
+ */
+
+ int minSize = cMin + sumSpacing;
+
+ // shrink the spacers proportionally
+ if (spacer >= 0) {
+ spacer = minSize > 0 ? spacer * space / minSize : 0;
+ sumSpacing = spacer * spacerCount;
+ }
+
+ QList<int> list;
+
+ for (i = start; i < start + count; i++)
+ list << chain.at(i).minimumSize;
+
+ qSort(list);
+
+ int space_left = space - sumSpacing;
+
+ int sum = 0;
+ int idx = 0;
+ int space_used=0;
+ int current = 0;
+ while (idx < count && space_used < space_left) {
+ current = list.at(idx);
+ space_used = sum + current * (count - idx);
+ sum += current;
+ ++idx;
+ }
+ --idx;
+ int deficit = space_used - space_left;
+
+ int items = count - idx;
+ /*
+ * If we truncate all items to "current", we would get "deficit" too many pixels. Therefore, we have to remove
+ * deficit/items from each item bigger than maxval. The actual value to remove is deficitPerItem + remainder/items
+ * "rest" is the accumulated error from using integer arithmetic.
+ */
+ int deficitPerItem = deficit/items;
+ int remainder = deficit % items;
+ int maxval = current - deficitPerItem;
+
+ int rest = 0;
+ for (i = start; i < start + count; i++) {
+ int maxv = maxval;
+ rest += remainder;
+ if (rest >= items) {
+ maxv--;
+ rest-=items;
+ }
+ QLayoutStruct *data = &chain[i];
+ data->size = qMin(data->minimumSize, maxv);
+ data->done = true;
+ }
+ } else if (space < cHint + sumSpacing) {
+ /*
+ Less space than smartSizeHint(), but more than minimumSize.
+ Currently take space equally from each, as in Qt 2.x.
+ Commented-out lines will give more space to stretchier
+ items.
+ */
+ int n = count;
+ int space_left = space - sumSpacing;
+ int overdraft = cHint - space_left;
+
+ // first give to the fixed ones:
+ for (i = start; i < start + count; i++) {
+ QLayoutStruct *data = &chain[i];
+ if (!data->done
+ && data->minimumSize >= data->smartSizeHint()) {
+ data->size = data->smartSizeHint();
+ data->done = true;
+ space_left -= data->smartSizeHint();
+ // sumStretch -= data->stretch;
+ n--;
+ }
+ }
+ bool finished = n == 0;
+ while (!finished) {
+ finished = true;
+ Fixed64 fp_over = toFixed(overdraft);
+ Fixed64 fp_w = 0;
+
+ for (i = start; i < start+count; i++) {
+ QLayoutStruct *data = &chain[i];
+ if (data->done)
+ continue;
+ // if (sumStretch <= 0)
+ fp_w += fp_over / n;
+ // else
+ // fp_w += (fp_over * data->stretch) / sumStretch;
+ int w = fRound(fp_w);
+ data->size = data->smartSizeHint() - w;
+ fp_w -= toFixed(w); // give the difference to the next
+ if (data->size < data->minimumSize) {
+ data->done = true;
+ data->size = data->minimumSize;
+ finished = false;
+ overdraft -= data->smartSizeHint() - data->minimumSize;
+ // sumStretch -= data->stretch;
+ n--;
+ break;
+ }
+ }
+ }
+ } else { // extra space
+ int n = count;
+ int space_left = space - sumSpacing;
+ // first give to the fixed ones, and handle non-expansiveness
+ for (i = start; i < start + count; i++) {
+ QLayoutStruct *data = &chain[i];
+ if (!data->done
+ && (data->maximumSize <= data->smartSizeHint()
+ || (wannaGrow && !data->expansive && data->stretch == 0)
+ || (!allEmptyNonstretch && data->empty &&
+ !data->expansive && data->stretch == 0))) {
+ data->size = data->smartSizeHint();
+ data->done = true;
+ space_left -= data->size;
+ sumStretch -= data->stretch;
+ n--;
+ }
+ }
+ extraspace = space_left;
+
+ /*
+ Do a trial distribution and calculate how much it is off.
+ If there are more deficit pixels than surplus pixels, give
+ the minimum size items what they need, and repeat.
+ Otherwise give to the maximum size items, and repeat.
+
+ Paul Olav Tvete has a wonderful mathematical proof of the
+ correctness of this principle, but unfortunately this
+ comment is too small to contain it.
+ */
+ int surplus, deficit;
+ do {
+ surplus = deficit = 0;
+ Fixed64 fp_space = toFixed(space_left);
+ Fixed64 fp_w = 0;
+ for (i = start; i < start + count; i++) {
+ QLayoutStruct *data = &chain[i];
+ if (data->done)
+ continue;
+ extraspace = 0;
+ if (sumStretch <= 0)
+ fp_w += fp_space / n;
+ else
+ fp_w += (fp_space * data->stretch) / sumStretch;
+ int w = fRound(fp_w);
+ data->size = w;
+ fp_w -= toFixed(w); // give the difference to the next
+ if (w < data->smartSizeHint()) {
+ deficit += data->smartSizeHint() - w;
+ } else if (w > data->maximumSize) {
+ surplus += w - data->maximumSize;
+ }
+ }
+ if (deficit > 0 && surplus <= deficit) {
+ // give to the ones that have too little
+ for (i = start; i < start+count; i++) {
+ QLayoutStruct *data = &chain[i];
+ if (!data->done && data->size < data->smartSizeHint()) {
+ data->size = data->smartSizeHint();
+ data->done = true;
+ space_left -= data->smartSizeHint();
+ sumStretch -= data->stretch;
+ n--;
+ }
+ }
+ }
+ if (surplus > 0 && surplus >= deficit) {
+ // take from the ones that have too much
+ for (i = start; i < start + count; i++) {
+ QLayoutStruct *data = &chain[i];
+ if (!data->done && data->size > data->maximumSize) {
+ data->size = data->maximumSize;
+ data->done = true;
+ space_left -= data->maximumSize;
+ sumStretch -= data->stretch;
+ n--;
+ }
+ }
+ }
+ } while (n > 0 && surplus != deficit);
+ if (n == 0)
+ extraspace = space_left;
+ }
+
+ /*
+ As a last resort, we distribute the unwanted space equally
+ among the spacers (counting the start and end of the chain). We
+ could, but don't, attempt a sub-pixel allocation of the extra
+ space.
+ */
+ int extra = extraspace / (spacerCount + 2);
+ int p = pos + extra;
+ for (i = start; i < start+count; i++) {
+ QLayoutStruct *data = &chain[i];
+ data->pos = p;
+ p += data->size;
+ if (!data->empty)
+ p += data->effectiveSpacer(spacer) + extra;
+ }
+
+#ifdef QLAYOUT_EXTRA_DEBUG
+ qDebug() << "qGeomCalc" << "start" << start << "count" << count << "pos" << pos
+ << "space" << space << "spacer" << spacer;
+ for (i = start; i < start + count; ++i) {
+ qDebug() << i << ":" << chain[i].minimumSize << chain[i].smartSizeHint()
+ << chain[i].maximumSize << "stretch" << chain[i].stretch
+ << "empty" << chain[i].empty << "expansive" << chain[i].expansive
+ << "spacing" << chain[i].spacing;
+ qDebug() << "result pos" << chain[i].pos << "size" << chain[i].size;
+ }
+#endif
+}
+
+Q_GUI_EXPORT QSize qSmartMinSize(const QSize &sizeHint, const QSize &minSizeHint,
+ const QSize &minSize, const QSize &maxSize,
+ const QSizePolicy &sizePolicy)
+{
+ QSize s(0, 0);
+
+ if (sizePolicy.horizontalPolicy() != QSizePolicy::Ignored) {
+ if (sizePolicy.horizontalPolicy() & QSizePolicy::ShrinkFlag)
+ s.setWidth(minSizeHint.width());
+ else
+ s.setWidth(qMax(sizeHint.width(), minSizeHint.width()));
+ }
+
+ if (sizePolicy.verticalPolicy() != QSizePolicy::Ignored) {
+ if (sizePolicy.verticalPolicy() & QSizePolicy::ShrinkFlag) {
+ s.setHeight(minSizeHint.height());
+ } else {
+ s.setHeight(qMax(sizeHint.height(), minSizeHint.height()));
+ }
+ }
+
+ s = s.boundedTo(maxSize);
+ if (minSize.width() > 0)
+ s.setWidth(minSize.width());
+ if (minSize.height() > 0)
+ s.setHeight(minSize.height());
+
+ return s.expandedTo(QSize(0,0));
+}
+
+Q_GUI_EXPORT QSize qSmartMinSize(const QWidgetItem *i)
+{
+ QWidget *w = ((QWidgetItem *)i)->widget();
+ return qSmartMinSize(w->sizeHint(), w->minimumSizeHint(),
+ w->minimumSize(), w->maximumSize(),
+ w->sizePolicy());
+}
+
+Q_GUI_EXPORT QSize qSmartMinSize(const QWidget *w)
+{
+ return qSmartMinSize(w->sizeHint(), w->minimumSizeHint(),
+ w->minimumSize(), w->maximumSize(),
+ w->sizePolicy());
+}
+
+Q_GUI_EXPORT QSize qSmartMaxSize(const QSize &sizeHint,
+ const QSize &minSize, const QSize &maxSize,
+ const QSizePolicy &sizePolicy, Qt::Alignment align)
+{
+ if (align & Qt::AlignHorizontal_Mask && align & Qt::AlignVertical_Mask)
+ return QSize(QLAYOUTSIZE_MAX, QLAYOUTSIZE_MAX);
+ QSize s = maxSize;
+ QSize hint = sizeHint.expandedTo(minSize);
+ if (s.width() == QWIDGETSIZE_MAX && !(align & Qt::AlignHorizontal_Mask))
+ if (!(sizePolicy.horizontalPolicy() & QSizePolicy::GrowFlag))
+ s.setWidth(hint.width());
+
+ if (s.height() == QWIDGETSIZE_MAX && !(align & Qt::AlignVertical_Mask))
+ if (!(sizePolicy.verticalPolicy() & QSizePolicy::GrowFlag))
+ s.setHeight(hint.height());
+
+ if (align & Qt::AlignHorizontal_Mask)
+ s.setWidth(QLAYOUTSIZE_MAX);
+ if (align & Qt::AlignVertical_Mask)
+ s.setHeight(QLAYOUTSIZE_MAX);
+ return s;
+}
+
+Q_GUI_EXPORT QSize qSmartMaxSize(const QWidgetItem *i, Qt::Alignment align)
+{
+ QWidget *w = ((QWidgetItem*)i)->widget();
+
+ return qSmartMaxSize(w->sizeHint().expandedTo(w->minimumSizeHint()), w->minimumSize(), w->maximumSize(),
+ w->sizePolicy(), align);
+}
+
+Q_GUI_EXPORT QSize qSmartMaxSize(const QWidget *w, Qt::Alignment align)
+{
+ return qSmartMaxSize(w->sizeHint().expandedTo(w->minimumSizeHint()), w->minimumSize(), w->maximumSize(),
+ w->sizePolicy(), align);
+}
+
+Q_GUI_EXPORT int qSmartSpacing(const QLayout *layout, QStyle::PixelMetric pm)
+{
+ QObject *parent = layout->parent();
+ if (!parent) {
+ return -1;
+ } else if (parent->isWidgetType()) {
+ QWidget *pw = static_cast<QWidget *>(parent);
+ return pw->style()->pixelMetric(pm, 0, pw);
+ } else {
+ return static_cast<QLayout *>(parent)->spacing();
+ }
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/kernel/qlayoutengine_p.h b/src/gui/kernel/qlayoutengine_p.h
new file mode 100644
index 0000000000..4a6b8bd865
--- /dev/null
+++ b/src/gui/kernel/qlayoutengine_p.h
@@ -0,0 +1,140 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QLAYOUTENGINE_P_H
+#define QLAYOUTENGINE_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 "QtGui/qlayoutitem.h"
+#include "QtGui/qstyle.h"
+
+QT_BEGIN_NAMESPACE
+
+template <typename T> class QVector;
+
+struct QLayoutStruct
+{
+ inline void init(int stretchFactor = 0, int minSize = 0) {
+ stretch = stretchFactor;
+ minimumSize = sizeHint = minSize;
+ maximumSize = QLAYOUTSIZE_MAX;
+ expansive = false;
+ empty = true;
+ spacing = 0;
+ }
+
+ int smartSizeHint() {
+ return (stretch > 0) ? minimumSize : sizeHint;
+ }
+ int effectiveSpacer(int uniformSpacer) const {
+ Q_ASSERT(uniformSpacer >= 0 || spacing >= 0);
+ return (uniformSpacer >= 0) ? uniformSpacer : spacing;
+ }
+
+ // parameters
+ int stretch;
+ int sizeHint;
+ int maximumSize;
+ int minimumSize;
+ bool expansive;
+ bool empty;
+ int spacing;
+
+ // temporary storage
+ bool done;
+
+ // result
+ int pos;
+ int size;
+};
+
+
+Q_GUI_EXPORT void qGeomCalc(QVector<QLayoutStruct> &chain, int start, int count,
+ int pos, int space, int spacer = -1);
+Q_GUI_EXPORT QSize qSmartMinSize(const QSize &sizeHint, const QSize &minSizeHint,
+ const QSize &minSize, const QSize &maxSize,
+ const QSizePolicy &sizePolicy);
+Q_GUI_EXPORT QSize qSmartMinSize(const QWidgetItem *i);
+Q_GUI_EXPORT QSize qSmartMinSize(const QWidget *w);
+Q_GUI_EXPORT QSize qSmartMaxSize(const QSize &sizeHint,
+ const QSize &minSize, const QSize &maxSize,
+ const QSizePolicy &sizePolicy, Qt::Alignment align = 0);
+Q_GUI_EXPORT QSize qSmartMaxSize(const QWidgetItem *i, Qt::Alignment align = 0);
+Q_GUI_EXPORT QSize qSmartMaxSize(const QWidget *w, Qt::Alignment align = 0);
+
+Q_GUI_EXPORT int qSmartSpacing(const QLayout *layout, QStyle::PixelMetric pm);
+
+/*
+ Modify total maximum (max), total expansion (exp), and total empty
+ when adding boxmax/boxexp.
+
+ Expansive boxes win over non-expansive boxes.
+ Non-empty boxes win over empty boxes.
+*/
+static inline void qMaxExpCalc(int & max, bool &exp, bool &empty,
+ int boxmax, bool boxexp, bool boxempty)
+{
+ if (exp) {
+ if (boxexp)
+ max = qMax(max, boxmax);
+ } else {
+ if (boxexp || (empty && (!boxempty || max == 0)))
+ max = boxmax;
+ else if (empty == boxempty)
+ max = qMin(max, boxmax);
+ }
+ exp = exp || boxexp;
+ empty = empty && boxempty;
+}
+
+QT_END_NAMESPACE
+
+#endif // QLAYOUTENGINE_P_H
diff --git a/src/gui/kernel/qlayoutitem.cpp b/src/gui/kernel/qlayoutitem.cpp
new file mode 100644
index 0000000000..0fd73b8f88
--- /dev/null
+++ b/src/gui/kernel/qlayoutitem.cpp
@@ -0,0 +1,837 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qlayout.h"
+
+#include "qapplication.h"
+#include "qlayoutengine_p.h"
+#include "qmenubar.h"
+#include "qtoolbar.h"
+#include "qevent.h"
+#include "qstyle.h"
+#include "qvariant.h"
+#include "qwidget_p.h"
+
+QT_BEGIN_NAMESPACE
+
+inline static QRect fromLayoutItemRect(QWidgetPrivate *priv, const QRect &rect)
+{
+ return priv->fromOrToLayoutItemRect(rect, -1);
+}
+
+inline static QSize fromLayoutItemSize(QWidgetPrivate *priv, const QSize &size)
+{
+ return fromLayoutItemRect(priv, QRect(QPoint(0, 0), size)).size();
+}
+
+inline static QRect toLayoutItemRect(QWidgetPrivate *priv, const QRect &rect)
+{
+ return priv->fromOrToLayoutItemRect(rect, +1);
+}
+
+inline static QSize toLayoutItemSize(QWidgetPrivate *priv, const QSize &size)
+{
+ return toLayoutItemRect(priv, QRect(QPoint(0, 0), size)).size();
+}
+
+/*!
+ Returns a QVariant storing this QSizePolicy.
+*/
+QSizePolicy::operator QVariant() const
+{
+ return QVariant(QVariant::SizePolicy, this);
+}
+
+/*!
+ \class QLayoutItem
+ \brief The QLayoutItem class provides an abstract item that a
+ QLayout manipulates.
+
+ \ingroup appearance
+ \ingroup geomanagement
+
+ This is used by custom layouts.
+
+ Pure virtual functions are provided to return information about
+ the layout, including, sizeHint(), minimumSize(), maximumSize()
+ and expanding().
+
+ The layout's geometry can be set and retrieved with setGeometry()
+ and geometry(), and its alignment with setAlignment() and
+ alignment().
+
+ isEmpty() returns whether the layout item is empty. If the
+ concrete item is a QWidget, it can be retrieved using widget().
+ Similarly for layout() and spacerItem().
+
+ Some layouts have width and height interdependencies. These can
+ be expressed using hasHeightForWidth(), heightForWidth(), and
+ minimumHeightForWidth(). For more explanation see the \e{Qt
+ Quarterly} article
+ \l{http://doc.trolltech.com/qq/qq04-height-for-width.html}{Trading
+ Height for Width}.
+
+ \sa QLayout
+*/
+
+/*!
+ \class QSpacerItem
+ \ingroup appearance
+ \ingroup geomanagement
+ \brief The QSpacerItem class provides blank space in a layout.
+
+ Normally, you don't need to use this class directly. Qt's
+ built-in layout managers provide the following functions for
+ manipulating empty space in layouts:
+
+ \table
+ \header \o Class
+ \o Functions
+ \row \o QHBoxLayout
+ \o \l{QBoxLayout::addSpacing()}{addSpacing()},
+ \l{QBoxLayout::addStretch()}{addStretch()},
+ \l{QBoxLayout::insertSpacing()}{insertSpacing()},
+ \l{QBoxLayout::insertStretch()}{insertStretch()}
+ \row \o QGridLayout
+ \o \l{QGridLayout::setRowMinimumHeight()}{setRowMinimumHeight()},
+ \l{QGridLayout::setRowStretch()}{setRowStretch()},
+ \l{QGridLayout::setColumnMinimumWidth()}{setColumnMinimumWidth()},
+ \l{QGridLayout::setColumnStretch()}{setColumnStretch()}
+ \endtable
+
+ \sa QLayout, QWidgetItem, QLayoutItem::spacerItem()
+*/
+
+/*!
+ \class QWidgetItem
+ \ingroup appearance
+ \ingroup geomanagement
+ \brief The QWidgetItem class is a layout item that represents a widget.
+
+ Normally, you don't need to use this class directly. Qt's
+ built-in layout managers provide the following functions for
+ manipulating widgets in layouts:
+
+ \table
+ \header \o Class
+ \o Functions
+ \row \o QBoxLayout
+ \o \l{QBoxLayout::addWidget()}{addWidget()},
+ \l{QBoxLayout::insertWidget()}{insertWidget()},
+ \l{QBoxLayout::setStretchFactor()}{setStretchFactor()}
+ \row \o QGridLayout
+ \o \l{QGridLayout::addWidget()}{addWidget()}
+ \row \o QStackedLayout
+ \o \l{QStackedLayout::addWidget()}{addWidget()},
+ \l{QStackedLayout::insertWidget()}{insertWidget()},
+ \l{QStackedLayout::currentWidget()}{currentWidget()},
+ \l{QStackedLayout::setCurrentWidget()}{setCurrentWidget()},
+ \l{QStackedLayout::widget()}{widget()}
+ \endtable
+
+ \sa QLayout, QSpacerItem, QLayoutItem::widget()
+*/
+
+/*!
+ \fn QLayoutItem::QLayoutItem(Qt::Alignment alignment)
+
+ Constructs a layout item with an \a alignment.
+ Not all subclasses support alignment.
+*/
+
+/*!
+ \fn Qt::Alignment QLayoutItem::alignment() const
+
+ Returns the alignment of this item.
+*/
+
+/*!
+ Sets the alignment of this item to \a alignment.
+
+ \bold{Note:} Item alignment is only supported by QLayoutItem subclasses
+ where it would have a visual effect. Except for QSpacerItem, which provides
+ blank space for layouts, all public Qt classes that inherit QLayoutItem
+ support item alignment.
+*/
+void QLayoutItem::setAlignment(Qt::Alignment alignment)
+{
+ align = alignment;
+}
+
+/*!
+ \fn QSize QLayoutItem::maximumSize() const
+
+ Implemented in subclasses to return the maximum size of this item.
+*/
+
+/*!
+ \fn QSize QLayoutItem::minimumSize() const
+
+ Implemented in subclasses to return the minimum size of this item.
+*/
+
+/*!
+ \fn QSize QLayoutItem::sizeHint() const
+
+ Implemented in subclasses to return the preferred size of this item.
+*/
+
+/*!
+ \fn Qt::Orientations QLayoutItem::expandingDirections() const
+
+ Returns whether this layout item can make use of more space than
+ sizeHint(). A value of Qt::Vertical or Qt::Horizontal means that
+ it wants to grow in only one dimension, whereas Qt::Vertical |
+ Qt::Horizontal means that it wants to grow in both dimensions.
+*/
+
+/*!
+ \fn void QLayoutItem::setGeometry(const QRect &r)
+
+ Implemented in subclasses to set this item's geometry to \a r.
+
+ \sa geometry()
+*/
+
+/*!
+ \fn QRect QLayoutItem::geometry() const
+
+ Returns the rectangle covered by this layout item.
+
+ \sa setGeometry()
+*/
+
+/*!
+ \fn virtual bool QLayoutItem::isEmpty() const
+
+ Implemented in subclasses to return whether this item is empty,
+ i.e. whether it contains any widgets.
+*/
+
+/*!
+ \fn QSpacerItem::QSpacerItem(int w, int h, QSizePolicy::Policy hPolicy, QSizePolicy::Policy vPolicy)
+
+ Constructs a spacer item with preferred width \a w, preferred
+ height \a h, horizontal size policy \a hPolicy and vertical size
+ policy \a vPolicy.
+
+ The default values provide a gap that is able to stretch if
+ nothing else wants the space.
+*/
+
+/*!
+ Changes this spacer item to have preferred width \a w, preferred
+ height \a h, horizontal size policy \a hPolicy and vertical size
+ policy \a vPolicy.
+
+ The default values provide a gap that is able to stretch if
+ nothing else wants the space.
+
+ Note that if changeSize() is called after the spacer item has been added
+ to a layout, it is necessary to invalidate the layout in order for the
+ spacer item's new size to take effect.
+
+ \sa QSpacerItem::invalidate()
+*/
+void QSpacerItem::changeSize(int w, int h, QSizePolicy::Policy hPolicy,
+ QSizePolicy::Policy vPolicy)
+{
+ width = w;
+ height = h;
+ sizeP = QSizePolicy(hPolicy, vPolicy);
+}
+
+/*!
+ \fn QWidgetItem::QWidgetItem(QWidget *widget)
+
+ Creates an item containing the given \a widget.
+*/
+
+/*!
+ Destroys the QLayoutItem.
+*/
+QLayoutItem::~QLayoutItem()
+{
+}
+
+/*!
+ Invalidates any cached information in this layout item.
+*/
+void QLayoutItem::invalidate()
+{
+}
+
+/*!
+ If this item is a QLayout, it is returned as a QLayout; otherwise
+ 0 is returned. This function provides type-safe casting.
+*/
+QLayout * QLayoutItem::layout()
+{
+ return 0;
+}
+
+/*!
+ If this item is a QSpacerItem, it is returned as a QSpacerItem;
+ otherwise 0 is returned. This function provides type-safe casting.
+*/
+QSpacerItem * QLayoutItem::spacerItem()
+{
+ return 0;
+}
+
+/*!
+ \reimp
+*/
+QLayout * QLayout::layout()
+{
+ return this;
+}
+
+/*!
+ Returns a pointer to this object.
+*/
+QSpacerItem * QSpacerItem::spacerItem()
+{
+ return this;
+}
+
+/*!
+ If this item is a QWidget, it is returned as a QWidget; otherwise
+ 0 is returned. This function provides type-safe casting.
+*/
+QWidget * QLayoutItem::widget()
+{
+ return 0;
+}
+
+/*!
+ Returns the widget managed by this item.
+*/
+QWidget *QWidgetItem::widget()
+{
+ return wid;
+}
+
+/*!
+ Returns true if this layout's preferred height depends on its
+ width; otherwise returns false. The default implementation returns
+ false.
+
+ Reimplement this function in layout managers that support height
+ for width.
+
+ \sa heightForWidth(), QWidget::heightForWidth()
+*/
+bool QLayoutItem::hasHeightForWidth() const
+{
+ return false;
+}
+
+/*!
+ Returns the minimum height this widget needs for the given width,
+ \a w. The default implementation simply returns heightForWidth(\a
+ w).
+*/
+int QLayoutItem::minimumHeightForWidth(int w) const
+{
+ return heightForWidth(w);
+}
+
+
+/*!
+ Returns the preferred height for this layout item, given the width
+ \a w.
+
+ The default implementation returns -1, indicating that the
+ preferred height is independent of the width of the item. Using
+ the function hasHeightForWidth() will typically be much faster
+ than calling this function and testing for -1.
+
+ Reimplement this function in layout managers that support height
+ for width. A typical implementation will look like this:
+ \snippet doc/src/snippets/code/src_gui_kernel_qlayoutitem.cpp 0
+
+ Caching is strongly recommended; without it layout will take
+ exponential time.
+
+ \sa hasHeightForWidth()
+*/
+int QLayoutItem::heightForWidth(int /* w */) const
+{
+ return -1;
+}
+
+/*!
+ Returns the control type(s) for the layout item. For a
+ QWidgetItem, the control type comes from the widget's size
+ policy; for a QLayoutItem, the control types is derived from the
+ layout's contents.
+
+ \sa QSizePolicy::controlType()
+*/
+QSizePolicy::ControlTypes QLayoutItem::controlTypes() const
+{
+ // ### Qt 5: This function should probably be virtual instead
+ if (const QWidget *widget = const_cast<QLayoutItem*>(this)->widget()) {
+ return widget->sizePolicy().controlType();
+ } else if (const QLayout *layout = const_cast<QLayoutItem*>(this)->layout()) {
+ if (layout->count() == 0)
+ return QSizePolicy::DefaultType;
+ QSizePolicy::ControlTypes types;
+ for (int i = layout->count() - 1; i >= 0; --i)
+ types |= layout->itemAt(i)->controlTypes();
+ return types;
+ }
+ return QSizePolicy::DefaultType;
+}
+
+/*!
+ \reimp
+*/
+void QSpacerItem::setGeometry(const QRect &r)
+{
+ rect = r;
+}
+
+/*!
+ \reimp
+*/
+void QWidgetItem::setGeometry(const QRect &rect)
+{
+ if (isEmpty())
+ return;
+
+ QRect r = !wid->testAttribute(Qt::WA_LayoutUsesWidgetRect)
+ ? fromLayoutItemRect(wid->d_func(), rect)
+ : rect;
+ const QSize widgetRectSurplus = r.size() - rect.size();
+
+ /*
+ For historical reasons, this code is done using widget rect
+ coordinates, not layout item rect coordinates. However,
+ QWidgetItem's sizeHint(), maximumSize(), and heightForWidth()
+ all work in terms of layout item rect coordinates, so we have to
+ add or subtract widgetRectSurplus here and there. The code could
+ be much simpler if we did everything using layout item rect
+ coordinates and did the conversion right before the call to
+ QWidget::setGeometry().
+ */
+
+ QSize s = r.size().boundedTo(maximumSize() + widgetRectSurplus);
+ int x = r.x();
+ int y = r.y();
+ if (align & (Qt::AlignHorizontal_Mask | Qt::AlignVertical_Mask)) {
+ QSize pref(sizeHint());
+ QSizePolicy sp = wid->sizePolicy();
+ if (sp.horizontalPolicy() == QSizePolicy::Ignored)
+ pref.setWidth(wid->sizeHint().expandedTo(wid->minimumSize()).width());
+ if (sp.verticalPolicy() == QSizePolicy::Ignored)
+ pref.setHeight(wid->sizeHint().expandedTo(wid->minimumSize()).height());
+ pref += widgetRectSurplus;
+ if (align & Qt::AlignHorizontal_Mask)
+ s.setWidth(qMin(s.width(), pref.width()));
+ if (align & Qt::AlignVertical_Mask) {
+ if (hasHeightForWidth())
+ s.setHeight(qMin(s.height(),
+ heightForWidth(s.width() - widgetRectSurplus.width())
+ + widgetRectSurplus.height()));
+ else
+ s.setHeight(qMin(s.height(), pref.height()));
+ }
+ }
+ Qt::Alignment alignHoriz = QStyle::visualAlignment(wid->layoutDirection(), align);
+ if (alignHoriz & Qt::AlignRight)
+ x = x + (r.width() - s.width());
+ else if (!(alignHoriz & Qt::AlignLeft))
+ x = x + (r.width() - s.width()) / 2;
+
+ if (align & Qt::AlignBottom)
+ y = y + (r.height() - s.height());
+ else if (!(align & Qt::AlignTop))
+ y = y + (r.height() - s.height()) / 2;
+
+ wid->setGeometry(x, y, s.width(), s.height());
+}
+
+/*!
+ \reimp
+*/
+QRect QSpacerItem::geometry() const
+{
+ return rect;
+}
+
+/*!
+ \reimp
+*/
+QRect QWidgetItem::geometry() const
+{
+ return !wid->testAttribute(Qt::WA_LayoutUsesWidgetRect)
+ ? toLayoutItemRect(wid->d_func(), wid->geometry())
+ : wid->geometry();
+}
+
+
+/*!
+ \reimp
+*/
+bool QWidgetItem::hasHeightForWidth() const
+{
+ if (isEmpty())
+ return false;
+ if (wid->layout())
+ return wid->layout()->hasHeightForWidth();
+ return wid->sizePolicy().hasHeightForWidth();
+}
+
+/*!
+ \reimp
+*/
+int QWidgetItem::heightForWidth(int w) const
+{
+ if (isEmpty())
+ return -1;
+
+ w = !wid->testAttribute(Qt::WA_LayoutUsesWidgetRect)
+ ? fromLayoutItemSize(wid->d_func(), QSize(w, 0)).width()
+ : w;
+
+ int hfw;
+ if (wid->layout())
+ hfw = wid->layout()->totalHeightForWidth(w);
+ else
+ hfw = wid->heightForWidth(w);
+
+ if (hfw > wid->maximumHeight())
+ hfw = wid->maximumHeight();
+ if (hfw < wid->minimumHeight())
+ hfw = wid->minimumHeight();
+
+ hfw = !wid->testAttribute(Qt::WA_LayoutUsesWidgetRect)
+ ? toLayoutItemSize(wid->d_func(), QSize(0, hfw)).height()
+ : hfw;
+
+ if (hfw < 0)
+ hfw = 0;
+ return hfw;
+}
+
+/*!
+ \reimp
+*/
+Qt::Orientations QSpacerItem::expandingDirections() const
+{
+ return sizeP.expandingDirections();
+}
+
+/*!
+ \reimp
+*/
+Qt::Orientations QWidgetItem::expandingDirections() const
+{
+ if (isEmpty())
+ return Qt::Orientations(0);
+
+ Qt::Orientations e = wid->sizePolicy().expandingDirections();
+ /*
+ ### Qt 4.0:
+ If the layout is expanding, we make the widget expanding, even if
+ its own size policy isn't expanding. This behavior should be
+ reconsidered.
+ */
+ if (wid->layout()) {
+ if (wid->sizePolicy().horizontalPolicy() & QSizePolicy::GrowFlag
+ && (wid->layout()->expandingDirections() & Qt::Horizontal))
+ e |= Qt::Horizontal;
+ if (wid->sizePolicy().verticalPolicy() & QSizePolicy::GrowFlag
+ && (wid->layout()->expandingDirections() & Qt::Vertical))
+ e |= Qt::Vertical;
+ }
+
+ if (align & Qt::AlignHorizontal_Mask)
+ e &= ~Qt::Horizontal;
+ if (align & Qt::AlignVertical_Mask)
+ e &= ~Qt::Vertical;
+ return e;
+}
+
+/*!
+ \reimp
+*/
+QSize QSpacerItem::minimumSize() const
+{
+ return QSize(sizeP.horizontalPolicy() & QSizePolicy::ShrinkFlag ? 0 : width,
+ sizeP.verticalPolicy() & QSizePolicy::ShrinkFlag ? 0 : height);
+}
+
+/*!
+ \reimp
+*/
+QSize QWidgetItem::minimumSize() const
+{
+ if (isEmpty())
+ return QSize(0, 0);
+ return !wid->testAttribute(Qt::WA_LayoutUsesWidgetRect)
+ ? toLayoutItemSize(wid->d_func(), qSmartMinSize(this))
+ : qSmartMinSize(this);
+}
+
+/*!
+ \reimp
+*/
+QSize QSpacerItem::maximumSize() const
+{
+ return QSize(sizeP.horizontalPolicy() & QSizePolicy::GrowFlag ? QLAYOUTSIZE_MAX : width,
+ sizeP.verticalPolicy() & QSizePolicy::GrowFlag ? QLAYOUTSIZE_MAX : height);
+}
+
+/*!
+ \reimp
+*/
+QSize QWidgetItem::maximumSize() const
+{
+ if (isEmpty()) {
+ return QSize(0, 0);
+ } else {
+ return !wid->testAttribute(Qt::WA_LayoutUsesWidgetRect)
+ ? toLayoutItemSize(wid->d_func(), qSmartMaxSize(this, align))
+ : qSmartMaxSize(this, align);
+ }
+}
+
+/*!
+ \reimp
+*/
+QSize QSpacerItem::sizeHint() const
+{
+ return QSize(width, height);
+}
+
+/*!
+ \reimp
+*/
+QSize QWidgetItem::sizeHint() const
+{
+ QSize s(0, 0);
+ if (!isEmpty()) {
+ s = wid->sizeHint().expandedTo(wid->minimumSizeHint());
+ s = s.boundedTo(wid->maximumSize())
+ .expandedTo(wid->minimumSize());
+ s = !wid->testAttribute(Qt::WA_LayoutUsesWidgetRect)
+ ? toLayoutItemSize(wid->d_func(), s)
+ : s;
+
+ if (wid->sizePolicy().horizontalPolicy() == QSizePolicy::Ignored)
+ s.setWidth(0);
+ if (wid->sizePolicy().verticalPolicy() == QSizePolicy::Ignored)
+ s.setHeight(0);
+ }
+ return s;
+}
+
+/*!
+ Returns true.
+*/
+bool QSpacerItem::isEmpty() const
+{
+ return true;
+}
+
+/*!
+ Returns true if the widget is hidden; otherwise returns false.
+
+ \sa QWidget::isHidden()
+*/
+bool QWidgetItem::isEmpty() const
+{
+ return wid->isHidden() || wid->isWindow();
+}
+
+/*!
+ \class QWidgetItemV2
+ \internal
+*/
+
+inline bool QWidgetItemV2::useSizeCache() const
+{
+ return wid->d_func()->widgetItem == this;
+}
+
+void QWidgetItemV2::updateCacheIfNecessary() const
+{
+ if (q_cachedMinimumSize.width() != Dirty)
+ return;
+
+ const QSize sizeHint(wid->sizeHint());
+ const QSize minimumSizeHint(wid->minimumSizeHint());
+ const QSize minimumSize(wid->minimumSize());
+ const QSize maximumSize(wid->maximumSize());
+ const QSizePolicy sizePolicy(wid->sizePolicy());
+ const QSize expandedSizeHint(sizeHint.expandedTo(minimumSizeHint));
+
+ const QSize smartMinSize(qSmartMinSize(sizeHint, minimumSizeHint, minimumSize, maximumSize, sizePolicy));
+ const QSize smartMaxSize(qSmartMaxSize(expandedSizeHint, minimumSize, maximumSize, sizePolicy, align));
+
+ const bool useLayoutItemRect = !wid->testAttribute(Qt::WA_LayoutUsesWidgetRect);
+
+ q_cachedMinimumSize = useLayoutItemRect
+ ? toLayoutItemSize(wid->d_func(), smartMinSize)
+ : smartMinSize;
+
+ q_cachedSizeHint = expandedSizeHint;
+ q_cachedSizeHint = q_cachedSizeHint.boundedTo(maximumSize)
+ .expandedTo(minimumSize);
+ q_cachedSizeHint = useLayoutItemRect
+ ? toLayoutItemSize(wid->d_func(), q_cachedSizeHint)
+ : q_cachedSizeHint;
+
+ if (wid->sizePolicy().horizontalPolicy() == QSizePolicy::Ignored)
+ q_cachedSizeHint.setWidth(0);
+ if (wid->sizePolicy().verticalPolicy() == QSizePolicy::Ignored)
+ q_cachedSizeHint.setHeight(0);
+
+ q_cachedMaximumSize = useLayoutItemRect
+ ? toLayoutItemSize(wid->d_func(), smartMaxSize)
+ : smartMaxSize;
+}
+
+QWidgetItemV2::QWidgetItemV2(QWidget *widget)
+ : QWidgetItem(widget),
+ q_cachedMinimumSize(Dirty, Dirty),
+ q_cachedSizeHint(Dirty, Dirty),
+ q_cachedMaximumSize(Dirty, Dirty),
+ q_firstCachedHfw(0),
+ q_hfwCacheSize(0),
+ d(0)
+{
+ QWidgetPrivate *wd = wid->d_func();
+ if (!wd->widgetItem)
+ wd->widgetItem = this;
+}
+
+QWidgetItemV2::~QWidgetItemV2()
+{
+ if (wid) {
+ QWidgetPrivate *wd = wid->d_func();
+ if (wd->widgetItem == this)
+ wd->widgetItem = 0;
+ }
+}
+
+QSize QWidgetItemV2::sizeHint() const
+{
+ if (isEmpty())
+ return QSize(0, 0);
+
+ if (useSizeCache()) {
+ updateCacheIfNecessary();
+ return q_cachedSizeHint;
+ } else {
+ return QWidgetItem::sizeHint();
+ }
+}
+
+QSize QWidgetItemV2::minimumSize() const
+{
+ if (isEmpty())
+ return QSize(0, 0);
+
+ if (useSizeCache()) {
+ updateCacheIfNecessary();
+ return q_cachedMinimumSize;
+ } else {
+ return QWidgetItem::minimumSize();
+ }
+}
+
+QSize QWidgetItemV2::maximumSize() const
+{
+ if (isEmpty())
+ return QSize(0, 0);
+
+ if (useSizeCache()) {
+ updateCacheIfNecessary();
+ return q_cachedMaximumSize;
+ } else {
+ return QWidgetItem::maximumSize();
+ }
+}
+
+/*
+ The height-for-width cache is organized as a circular buffer. The entries
+
+ q_hfwCachedHfws[q_firstCachedHfw],
+ ...,
+ q_hfwCachedHfws[(q_firstCachedHfw + q_hfwCacheSize - 1) % HfwCacheMaxSize]
+
+ contain the last cached values. When the cache is full, the first entry to
+ be erased is the entry before q_hfwCachedHfws[q_firstCachedHfw]. When
+ values are looked up, we try to move q_firstCachedHfw to point to that new
+ entry (unless the cache is not full, in which case it would leave the cache
+ in a broken state), so that the most recently used entry is also the last
+ to be erased.
+*/
+
+int QWidgetItemV2::heightForWidth(int width) const
+{
+ if (isEmpty())
+ return -1;
+
+ for (int i = 0; i < q_hfwCacheSize; ++i) {
+ int offset = q_firstCachedHfw + i;
+ const QSize &size = q_cachedHfws[offset % HfwCacheMaxSize];
+ if (size.width() == width) {
+ if (q_hfwCacheSize == HfwCacheMaxSize)
+ q_firstCachedHfw = offset;
+ return size.height();
+ }
+ }
+
+ if (q_hfwCacheSize < HfwCacheMaxSize)
+ ++q_hfwCacheSize;
+ q_firstCachedHfw = (q_firstCachedHfw + HfwCacheMaxSize - 1) % HfwCacheMaxSize;
+
+ int height = QWidgetItem::heightForWidth(width);
+ q_cachedHfws[q_firstCachedHfw] = QSize(width, height);
+ return height;
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/kernel/qlayoutitem.h b/src/gui/kernel/qlayoutitem.h
new file mode 100644
index 0000000000..d913472ab8
--- /dev/null
+++ b/src/gui/kernel/qlayoutitem.h
@@ -0,0 +1,182 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QLAYOUTITEM_H
+#define QLAYOUTITEM_H
+
+#include <QtGui/qsizepolicy.h>
+#include <QtCore/qrect.h>
+
+#include <limits.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+static const int QLAYOUTSIZE_MAX = INT_MAX/256/16;
+
+class QLayout;
+class QLayoutItem;
+class QSpacerItem;
+class QWidget;
+class QSize;
+
+class Q_GUI_EXPORT QLayoutItem
+{
+public:
+ inline explicit QLayoutItem(Qt::Alignment alignment = 0);
+ virtual ~QLayoutItem();
+ virtual QSize sizeHint() const = 0;
+ virtual QSize minimumSize() const = 0;
+ virtual QSize maximumSize() const = 0;
+ virtual Qt::Orientations expandingDirections() const = 0;
+ virtual void setGeometry(const QRect&) = 0;
+ virtual QRect geometry() const = 0;
+ virtual bool isEmpty() const = 0;
+ virtual bool hasHeightForWidth() const;
+ virtual int heightForWidth(int) const;
+ virtual int minimumHeightForWidth(int) const;
+ virtual void invalidate();
+
+ virtual QWidget *widget();
+ virtual QLayout *layout();
+ virtual QSpacerItem *spacerItem();
+
+ Qt::Alignment alignment() const { return align; }
+ void setAlignment(Qt::Alignment a);
+ QSizePolicy::ControlTypes controlTypes() const;
+
+protected:
+ Qt::Alignment align;
+};
+
+inline QLayoutItem::QLayoutItem(Qt::Alignment aalignment)
+ : align(aalignment) { }
+
+class Q_GUI_EXPORT QSpacerItem : public QLayoutItem
+{
+public:
+ QSpacerItem(int w, int h,
+ QSizePolicy::Policy hData = QSizePolicy::Minimum,
+ QSizePolicy::Policy vData = QSizePolicy::Minimum)
+ : width(w), height(h), sizeP(hData, vData) { }
+ void changeSize(int w, int h,
+ QSizePolicy::Policy hData = QSizePolicy::Minimum,
+ QSizePolicy::Policy vData = QSizePolicy::Minimum);
+ QSize sizeHint() const;
+ QSize minimumSize() const;
+ QSize maximumSize() const;
+ Qt::Orientations expandingDirections() const;
+ bool isEmpty() const;
+ void setGeometry(const QRect&);
+ QRect geometry() const;
+ QSpacerItem *spacerItem();
+
+private:
+ int width;
+ int height;
+ QSizePolicy sizeP;
+ QRect rect;
+};
+
+class Q_GUI_EXPORT QWidgetItem : public QLayoutItem
+{
+ Q_DISABLE_COPY(QWidgetItem)
+
+public:
+ explicit QWidgetItem(QWidget *w) : wid(w) { }
+ QSize sizeHint() const;
+ QSize minimumSize() const;
+ QSize maximumSize() const;
+ Qt::Orientations expandingDirections() const;
+ bool isEmpty() const;
+ void setGeometry(const QRect&);
+ QRect geometry() const;
+ virtual QWidget *widget();
+
+ bool hasHeightForWidth() const;
+ int heightForWidth(int) const;
+
+protected:
+ QWidget *wid;
+};
+
+class Q_GUI_EXPORT QWidgetItemV2 : public QWidgetItem
+{
+public:
+ explicit QWidgetItemV2(QWidget *widget);
+ ~QWidgetItemV2();
+
+ QSize sizeHint() const;
+ QSize minimumSize() const;
+ QSize maximumSize() const;
+ int heightForWidth(int width) const;
+
+private:
+ enum { Dirty = -123, HfwCacheMaxSize = 3 };
+
+ inline bool useSizeCache() const;
+ void updateCacheIfNecessary() const;
+ inline void invalidateSizeCache() {
+ q_cachedMinimumSize.setWidth(Dirty);
+ q_hfwCacheSize = 0;
+ }
+
+ mutable QSize q_cachedMinimumSize;
+ mutable QSize q_cachedSizeHint;
+ mutable QSize q_cachedMaximumSize;
+ mutable QSize q_cachedHfws[HfwCacheMaxSize];
+ mutable short q_firstCachedHfw;
+ mutable short q_hfwCacheSize;
+ void *d;
+
+ friend class QWidgetPrivate;
+
+ Q_DISABLE_COPY(QWidgetItemV2)
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QLAYOUTITEM_H
diff --git a/src/gui/kernel/qmacdefines_mac.h b/src/gui/kernel/qmacdefines_mac.h
new file mode 100644
index 0000000000..97ec544493
--- /dev/null
+++ b/src/gui/kernel/qmacdefines_mac.h
@@ -0,0 +1,192 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/****************************************************************************
+**
+** Copyright (c) 2007-2008, Apple, Inc.
+**
+** All rights reserved.
+**
+** Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are met:
+**
+** * Redistributions of source code must retain the above copyright notice,
+** this list of conditions and the following disclaimer.
+**
+** * Redistributions in binary form must reproduce the above copyright notice,
+** this list of conditions and the following disclaimer in the documentation
+** and/or other materials provided with the distribution.
+**
+** * Neither the name of Apple, Inc. nor the names of its contributors
+** may be used to endorse or promote products derived from this software
+** without specific prior written permission.
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+** CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+** EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+** PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+** PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+** LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+** NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+** SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+**
+****************************************************************************/
+
+/*
+ * qmacdefines_mac_p.h
+ * All the defines you'll ever need for Qt/Mac :-)
+ */
+
+/* This is just many defines. Therefore it doesn't need things like:
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+Yes, it is an informative comment ;-)
+*/
+
+#include <QtCore/qglobal.h>
+
+#undef OLD_DEBUG
+#ifdef DEBUG
+# define OLD_DEBUG DEBUG
+# undef DEBUG
+#endif
+#define DEBUG 0
+#ifdef qDebug
+# define old_qDebug qDebug
+# undef qDebug
+#endif
+
+#if __LP64__
+typedef signed int OSStatus;
+#else
+typedef signed long OSStatus;
+#endif
+
+#ifdef __OBJC__
+# ifdef slots
+# define old_slots slots
+# undef slots
+# endif
+#include <Cocoa/Cocoa.h>
+# ifdef old_slots
+# undef slots
+# define slots
+# undef old_slots
+# endif
+#endif
+#ifdef QT_MAC_USE_COCOA
+ typedef struct OpaqueEventHandlerCallRef * EventHandlerCallRef;
+ typedef struct OpaqueEventRef * EventRef;
+ typedef struct OpaqueMenuRef * MenuRef;
+ typedef struct OpaquePasteboardRef* PasteboardRef;
+ typedef struct OpaqueRgnHandle * RgnHandle;
+ typedef const struct __HIShape *HIShapeRef;
+ typedef struct __HIShape *HIMutableShapeRef;
+ typedef struct CGRect CGRect;
+ typedef struct CGImage *CGImageRef;
+ typedef struct CGContext *CGContextRef;
+ typedef struct GDevice * GDPtr;
+ typedef GDPtr * GDHandle;
+ typedef struct OpaqueIconRef * IconRef;
+# ifdef __OBJC__
+ typedef NSWindow* OSWindowRef;
+ typedef NSView *OSViewRef;
+ typedef NSMenu *OSMenuRef;
+ typedef NSEvent *OSEventRef;
+# else
+ typedef void *OSWindowRef;
+ typedef void *OSViewRef;
+ typedef void *OSMenuRef;
+ typedef void *OSEventRef;
+# endif
+#else // Carbon
+ typedef struct OpaqueEventHandlerCallRef * EventHandlerCallRef;
+ typedef struct OpaqueEventRef * EventRef;
+ typedef struct OpaqueMenuRef * MenuRef;
+ typedef struct OpaquePasteboardRef* PasteboardRef;
+ typedef struct OpaqueRgnHandle * RgnHandle;
+ typedef const struct __HIShape *HIShapeRef;
+ typedef struct __HIShape *HIMutableShapeRef;
+ typedef struct CGRect CGRect;
+ typedef struct CGImage *CGImageRef;
+ typedef struct CGContext *CGContextRef;
+ typedef struct GDevice * GDPtr;
+ typedef GDPtr * GDHandle;
+ typedef struct OpaqueIconRef * IconRef;
+ typedef struct OpaqueWindowPtr * WindowRef;
+ typedef struct OpaqueControlRef * HIViewRef;
+ typedef WindowRef OSWindowRef;
+ typedef HIViewRef OSViewRef;
+ typedef MenuRef OSMenuRef;
+ typedef EventRef OSEventRef;
+#endif // QT_MAC_USE_COCOA
+
+typedef PasteboardRef OSPasteboardRef;
+typedef struct AEDesc AEDescList;
+typedef AEDescList AERecord;
+typedef AERecord AppleEvent;
+
+#ifdef check
+#undef check
+#endif
+
+#undef DEBUG
+#ifdef OLD_DEBUG
+# define DEBUG OLD_DEBUG
+# undef OLD_DEBUG
+#endif
+
+#ifdef old_qDebug
+# undef qDebug
+# define qDebug QT_NO_QDEBUG_MACRO
+# undef old_qDebug
+#endif
diff --git a/src/gui/kernel/qmime.cpp b/src/gui/kernel/qmime.cpp
new file mode 100644
index 0000000000..1f071a7a7b
--- /dev/null
+++ b/src/gui/kernel/qmime.cpp
@@ -0,0 +1,97 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qmime.h"
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QMimeSource
+ \brief The QMimeSource class is an abstraction of objects that
+ provided formatted data of a certain MIME type.
+
+ \obsolete
+
+ The preferred approach to drag and drop is to use QDrag in
+ conjunction with QMimeData. See \l{Drag and Drop} for details.
+
+ \sa QMimeData, QDrag
+*/
+
+/*!
+ Destroys the MIME source.
+*/
+QMimeSource::~QMimeSource()
+{
+}
+
+/*!
+ \fn const char *QMimeSource::format(int i) const
+
+ Returns the (\a i - 1)-th supported MIME format, or 0.
+*/
+
+/*!
+ \fn QByteArray QMimeSource::encodedData(const char *format) const
+
+ Returns the encoded data of this object in the specified MIME
+ \a format.
+*/
+
+/*!
+ Returns true if the object can provide the data in format \a
+ mimeType; otherwise returns false.
+
+ If you inherit from QMimeSource, for consistency reasons it is
+ better to implement the more abstract canDecode() functions such
+ as QTextDrag::canDecode() and QImageDrag::canDecode().
+*/
+bool QMimeSource::provides(const char* mimeType) const
+{
+ const char* fmt;
+ for (int i=0; (fmt = format(i)); i++) {
+ if (!qstricmp(mimeType,fmt))
+ return true;
+ }
+ return false;
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/kernel/qmime.h b/src/gui/kernel/qmime.h
new file mode 100644
index 0000000000..42347a5282
--- /dev/null
+++ b/src/gui/kernel/qmime.h
@@ -0,0 +1,176 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QMIME_H
+#define QMIME_H
+
+#include <QtCore/qmimedata.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+class Q_GUI_EXPORT QMimeSource
+{
+public:
+ virtual ~QMimeSource();
+ virtual const char* format(int n = 0) const = 0;
+ virtual bool provides(const char*) const;
+ virtual QByteArray encodedData(const char*) const = 0;
+};
+
+
+#if defined(Q_WS_WIN)
+
+QT_BEGIN_INCLUDE_NAMESPACE
+typedef struct tagFORMATETC FORMATETC;
+typedef struct tagSTGMEDIUM STGMEDIUM;
+struct IDataObject;
+
+#include <QtCore/qvariant.h>
+QT_END_INCLUDE_NAMESPACE
+
+/*
+ Encapsulation of conversion between MIME and Windows CLIPFORMAT.
+ Not need on X11, as the underlying protocol uses the MIME standard
+ directly.
+*/
+
+class Q_GUI_EXPORT QWindowsMime
+{
+public:
+ QWindowsMime();
+ virtual ~QWindowsMime();
+
+ // for converting from Qt
+ virtual bool canConvertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData) const = 0;
+ virtual bool convertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData, STGMEDIUM * pmedium) const = 0;
+ virtual QVector<FORMATETC> formatsForMime(const QString &mimeType, const QMimeData *mimeData) const = 0;
+
+ // for converting to Qt
+ virtual bool canConvertToMime(const QString &mimeType, IDataObject *pDataObj) const = 0;
+ virtual QVariant convertToMime(const QString &mimeType, IDataObject *pDataObj, QVariant::Type preferredType) const = 0;
+ virtual QString mimeForFormat(const FORMATETC &formatetc) const = 0;
+
+ static int registerMimeType(const QString &mime);
+
+private:
+ friend class QClipboardWatcher;
+ friend class QDragManager;
+ friend class QDropData;
+ friend class QOleDataObject;
+
+ static QWindowsMime *converterToMime(const QString &mimeType, IDataObject *pDataObj);
+ static QStringList allMimesForFormats(IDataObject *pDataObj);
+ static QWindowsMime *converterFromMime(const FORMATETC &formatetc, const QMimeData *mimeData);
+ static QVector<FORMATETC> allFormatsForMime(const QMimeData *mimeData);
+};
+
+#endif
+#if defined(Q_WS_MAC)
+
+/*
+ Encapsulation of conversion between MIME and Mac flavor.
+ Not needed on X11, as the underlying protocol uses the MIME standard
+ directly.
+*/
+
+class Q_GUI_EXPORT QMacMime { //Obsolete
+ char type;
+public:
+ enum QMacMimeType { MIME_DND=0x01, MIME_CLIP=0x02, MIME_QT_CONVERTOR=0x04, MIME_ALL=MIME_DND|MIME_CLIP };
+ explicit QMacMime(char) { }
+ virtual ~QMacMime() { }
+
+ static void initialize() { }
+
+ static QList<QMacMime*> all(QMacMimeType) { return QList<QMacMime*>(); }
+ static QMacMime *convertor(QMacMimeType, const QString &, int) { return 0; }
+ static QString flavorToMime(QMacMimeType, int) { return QString(); }
+
+ virtual QString convertorName()=0;
+ virtual int countFlavors()=0;
+ virtual int flavor(int index)=0;
+ virtual bool canConvert(const QString &mime, int flav)=0;
+ virtual QString mimeFor(int flav)=0;
+ virtual int flavorFor(const QString &mime)=0;
+ virtual QVariant convertToMime(const QString &mime, QList<QByteArray> data, int flav)=0;
+ virtual QList<QByteArray> convertFromMime(const QString &mime, QVariant data, int flav)=0;
+};
+
+class Q_GUI_EXPORT QMacPasteboardMime {
+ char type;
+public:
+ enum QMacPasteboardMimeType { MIME_DND=0x01,
+ MIME_CLIP=0x02,
+ MIME_QT_CONVERTOR=0x04,
+ MIME_QT3_CONVERTOR=0x08,
+ MIME_ALL=MIME_DND|MIME_CLIP
+ };
+ explicit QMacPasteboardMime(char);
+ virtual ~QMacPasteboardMime();
+
+ static void initialize();
+
+ static QList<QMacPasteboardMime*> all(uchar);
+ static QMacPasteboardMime *convertor(uchar, const QString &mime, QString flav);
+ static QString flavorToMime(uchar, QString flav);
+
+ virtual QString convertorName() = 0;
+
+ virtual bool canConvert(const QString &mime, QString flav) = 0;
+ virtual QString mimeFor(QString flav) = 0;
+ virtual QString flavorFor(const QString &mime) = 0;
+ virtual QVariant convertToMime(const QString &mime, QList<QByteArray> data, QString flav) = 0;
+ virtual QList<QByteArray> convertFromMime(const QString &mime, QVariant data, QString flav) = 0;
+};
+
+// ### Qt 5: Add const QStringList& QMacPasteboardMime::supportedFlavours()
+Q_GUI_EXPORT void qRegisterDraggedTypes(const QStringList &types);
+#endif // Q_WS_MAC
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QMIME_H
diff --git a/src/gui/kernel/qmime_mac.cpp b/src/gui/kernel/qmime_mac.cpp
new file mode 100644
index 0000000000..cf1d74749b
--- /dev/null
+++ b/src/gui/kernel/qmime_mac.cpp
@@ -0,0 +1,1181 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qmime.h"
+
+//#define USE_INTERNET_CONFIG
+
+#ifndef USE_INTERNET_CONFIG
+# include "qfile.h"
+# include "qfileinfo.h"
+# include "qtextstream.h"
+# include "qdir.h"
+# include <unistd.h>
+# include <sys/types.h>
+# include <sys/stat.h>
+# include <sys/fcntl.h>
+#endif
+
+#include "qdebug.h"
+#include "qpixmap.h"
+#include "qimagewriter.h"
+#include "qimagereader.h"
+#include "qdatastream.h"
+#include "qbuffer.h"
+#include "qdatetime.h"
+#include "qapplication_p.h"
+#include "qtextcodec.h"
+#include "qregexp.h"
+#include "qurl.h"
+#include "qmap.h"
+#include <private/qt_mac_p.h>
+
+#ifdef Q_WS_MAC32
+#include <QuickTime/QuickTime.h>
+#include "qlibrary.h"
+#endif
+
+QT_BEGIN_NAMESPACE
+
+extern CGImageRef qt_mac_createCGImageFromQImage(const QImage &img, const QImage **imagePtr = 0); // qpaintengine_mac.cpp
+
+typedef QList<QMacPasteboardMime*> MimeList;
+Q_GLOBAL_STATIC(MimeList, globalMimeList)
+
+static void cleanup_mimes()
+{
+ MimeList *mimes = globalMimeList();
+ while (!mimes->isEmpty())
+ delete mimes->takeFirst();
+}
+
+Q_GLOBAL_STATIC(QStringList, globalDraggedTypesList)
+
+/*!
+ \fn void qRegisterDraggedTypes(const QStringList &types)
+ \relates QMacPasteboardMime
+
+ Registers the given \a types as custom pasteboard types.
+
+ This function should be called to enable the Drag and Drop events
+ for custom pasteboard types on Cocoa implementations. This is required
+ in addition to a QMacPasteboardMime subclass implementation. By default
+ drag and drop is enabled for all standard pasteboard types.
+
+ \sa QMacPasteboardMime
+*/
+Q_GUI_EXPORT void qRegisterDraggedTypes(const QStringList &types)
+{
+ (*globalDraggedTypesList()) += types;
+}
+
+const QStringList& qEnabledDraggedTypes()
+{
+ return (*globalDraggedTypesList());
+}
+
+
+/*****************************************************************************
+ QDnD debug facilities
+ *****************************************************************************/
+//#define DEBUG_MIME_MAPS
+
+//functions
+extern QString qt_mac_from_pascal_string(const Str255); //qglobal.cpp
+extern void qt_mac_from_pascal_string(QString, Str255, TextEncoding encoding=0, int len=-1); //qglobal.cpp
+
+ScrapFlavorType qt_mac_mime_type = 'CUTE';
+CFStringRef qt_mac_mime_typeUTI = CFSTR("com.pasteboard.trolltech.marker");
+
+/*!
+ \class QMacPasteboardMime
+ \brief The QMacPasteboardMime class maps open-standard MIME to Mac flavors.
+ \since 4.2
+ \ingroup io
+ \ingroup draganddrop
+ \ingroup misc
+
+ Qt's drag and drop support and clipboard facilities use the MIME
+ standard. On X11, this maps trivially to the Xdnd protocol, but on
+ Mac although some applications use MIME types to describe clipboard
+ formats, others use arbitrary non-standardized naming conventions,
+ or unnamed built-in Mac formats.
+
+ By instantiating subclasses of QMacPasteboardMime that provide conversions
+ between Mac flavors and MIME formats, you can convert proprietary
+ clipboard formats to MIME formats.
+
+ Qt has predefined support for the following Mac flavors:
+ \list
+ \i kScrapFlavorTypeUnicode - converted to "text/plain;charset=ISO-10646-UCS-2"
+ \i kScrapFlavorTypeText - converted to "text/plain;charset=system" or "text/plain"
+ \i kScrapFlavorTypePicture - converted to "application/x-qt-image"
+ \i typeFileURL - converted to "text/uri-list"
+ \endlist
+
+ You can check if a MIME type is convertible using canConvert() and
+ can perform conversions with convertToMime() and convertFromMime().
+*/
+
+/*! \enum QMacPasteboardMime::QMacPasteboardMimeType
+ \internal
+*/
+
+/*!
+ Constructs a new conversion object of type \a t, adding it to the
+ globally accessed list of available convertors.
+*/
+QMacPasteboardMime::QMacPasteboardMime(char t) : type(t)
+{
+ globalMimeList()->append(this);
+}
+
+/*!
+ Destroys a conversion object, removing it from the global
+ list of available convertors.
+*/
+QMacPasteboardMime::~QMacPasteboardMime()
+{
+ if(!QApplication::closingDown())
+ globalMimeList()->removeAll(this);
+}
+
+class QMacPasteboardMimeAny : public QMacPasteboardMime {
+private:
+
+public:
+ QMacPasteboardMimeAny() : QMacPasteboardMime(MIME_QT_CONVERTOR|MIME_ALL) {
+ }
+ ~QMacPasteboardMimeAny() {
+ }
+ QString convertorName();
+
+ QString flavorFor(const QString &mime);
+ QString mimeFor(QString flav);
+ bool canConvert(const QString &mime, QString flav);
+ QVariant convertToMime(const QString &mime, QList<QByteArray> data, QString flav);
+ QList<QByteArray> convertFromMime(const QString &mime, QVariant data, QString flav);
+};
+
+QString QMacPasteboardMimeAny::convertorName()
+{
+ return QLatin1String("Any-Mime");
+}
+
+QString QMacPasteboardMimeAny::flavorFor(const QString &mime)
+{
+ // do not handle the mime type name in the drag pasteboard
+ if(mime == QLatin1String("application/x-qt-mime-type-name"))
+ return QString();
+ QString ret = QLatin1String("com.trolltech.anymime.") + mime;
+ return ret.replace(QLatin1String("/"), QLatin1String("--"));
+}
+
+QString QMacPasteboardMimeAny::mimeFor(QString flav)
+{
+ const QString any_prefix = QLatin1String("com.trolltech.anymime.");
+ if(flav.size() > any_prefix.length() && flav.startsWith(any_prefix))
+ return flav.mid(any_prefix.length()).replace(QLatin1String("--"), QLatin1String("/"));
+ return QString();
+}
+
+bool QMacPasteboardMimeAny::canConvert(const QString &mime, QString flav)
+{
+ return mimeFor(flav) == mime;
+}
+
+QVariant QMacPasteboardMimeAny::convertToMime(const QString &mime, QList<QByteArray> data, QString)
+{
+ if(data.count() > 1)
+ qWarning("QMacPasteboardMimeAny: Cannot handle multiple member data");
+ QVariant ret;
+ if (mime == QLatin1String("text/plain"))
+ ret = QString::fromUtf8(data.first());
+ else
+ ret = data.first();
+ return ret;
+}
+
+QList<QByteArray> QMacPasteboardMimeAny::convertFromMime(const QString &mime, QVariant data, QString)
+{
+ QList<QByteArray> ret;
+ if (mime == QLatin1String("text/plain"))
+ ret.append(data.toString().toUtf8());
+ else
+ ret.append(data.toByteArray());
+ return ret;
+}
+
+class QMacPasteboardMimeTypeName : public QMacPasteboardMime {
+private:
+
+public:
+ QMacPasteboardMimeTypeName() : QMacPasteboardMime(MIME_QT_CONVERTOR|MIME_ALL) {
+ }
+ ~QMacPasteboardMimeTypeName() {
+ }
+ QString convertorName();
+
+ QString flavorFor(const QString &mime);
+ QString mimeFor(QString flav);
+ bool canConvert(const QString &mime, QString flav);
+ QVariant convertToMime(const QString &mime, QList<QByteArray> data, QString flav);
+ QList<QByteArray> convertFromMime(const QString &mime, QVariant data, QString flav);
+};
+
+QString QMacPasteboardMimeTypeName::convertorName()
+{
+ return QLatin1String("Qt-Mime-Type");
+}
+
+QString QMacPasteboardMimeTypeName::flavorFor(const QString &mime)
+{
+ if(mime == QLatin1String("application/x-qt-mime-type-name"))
+ return QLatin1String("com.trolltech.qt.MimeTypeName");
+ return QString();
+}
+
+QString QMacPasteboardMimeTypeName::mimeFor(QString)
+{
+ return QString();
+}
+
+bool QMacPasteboardMimeTypeName::canConvert(const QString &, QString)
+{
+ return false;
+}
+
+QVariant QMacPasteboardMimeTypeName::convertToMime(const QString &, QList<QByteArray>, QString)
+{
+ QVariant ret;
+ return ret;
+}
+
+QList<QByteArray> QMacPasteboardMimeTypeName::convertFromMime(const QString &, QVariant, QString)
+{
+ QList<QByteArray> ret;
+ ret.append(QString("x-qt-mime-type-name").toUtf8());
+ return ret;
+}
+
+class QMacPasteboardMimePlainText : public QMacPasteboardMime {
+public:
+ QMacPasteboardMimePlainText() : QMacPasteboardMime(MIME_ALL) { }
+ QString convertorName();
+
+ QString flavorFor(const QString &mime);
+ QString mimeFor(QString flav);
+ bool canConvert(const QString &mime, QString flav);
+ QVariant convertToMime(const QString &mime, QList<QByteArray> data, QString flav);
+ QList<QByteArray> convertFromMime(const QString &mime, QVariant data, QString flav);
+};
+
+QString QMacPasteboardMimePlainText::convertorName()
+{
+ return QLatin1String("PlainText");
+}
+
+QString QMacPasteboardMimePlainText::flavorFor(const QString &mime)
+{
+ if (mime == QLatin1String("text/plain"))
+ return QLatin1String("com.apple.traditional-mac-plain-text");
+ return QString();
+}
+
+QString QMacPasteboardMimePlainText::mimeFor(QString flav)
+{
+ if (flav == QLatin1String("com.apple.traditional-mac-plain-text"))
+ return QLatin1String("text/plain");
+ return QString();
+}
+
+bool QMacPasteboardMimePlainText::canConvert(const QString &mime, QString flav)
+{
+ return flavorFor(mime) == flav;
+}
+
+QVariant QMacPasteboardMimePlainText::convertToMime(const QString &mimetype, QList<QByteArray> data, QString flavor)
+{
+ if(data.count() > 1)
+ qWarning("QMacPasteboardMimePlainText: Cannot handle multiple member data");
+ const QByteArray &firstData = data.first();
+ QVariant ret;
+ if(flavor == QCFString(QLatin1String("com.apple.traditional-mac-plain-text"))) {
+ QCFString str(CFStringCreateWithBytes(kCFAllocatorDefault,
+ reinterpret_cast<const UInt8 *>(firstData.constData()),
+ firstData.size(), CFStringGetSystemEncoding(), false));
+ ret = QString(str);
+ } else {
+ qWarning("QMime::convertToMime: unhandled mimetype: %s", qPrintable(mimetype));
+ }
+ return ret;
+}
+
+QList<QByteArray> QMacPasteboardMimePlainText::convertFromMime(const QString &, QVariant data, QString flavor)
+{
+ QList<QByteArray> ret;
+ QString string = data.toString();
+ if(flavor == QCFString(QLatin1String("com.apple.traditional-mac-plain-text")))
+ ret.append(string.toLatin1());
+ return ret;
+}
+
+class QMacPasteboardMimeUnicodeText : public QMacPasteboardMime {
+public:
+ QMacPasteboardMimeUnicodeText() : QMacPasteboardMime(MIME_ALL) { }
+ QString convertorName();
+
+ QString flavorFor(const QString &mime);
+ QString mimeFor(QString flav);
+ bool canConvert(const QString &mime, QString flav);
+ QVariant convertToMime(const QString &mime, QList<QByteArray> data, QString flav);
+ QList<QByteArray> convertFromMime(const QString &mime, QVariant data, QString flav);
+};
+
+QString QMacPasteboardMimeUnicodeText::convertorName()
+{
+ return QLatin1String("UnicodeText");
+}
+
+QString QMacPasteboardMimeUnicodeText::flavorFor(const QString &mime)
+{
+ if (mime == QLatin1String("text/plain"))
+ return QLatin1String("public.utf16-plain-text");
+ int i = mime.indexOf(QLatin1String("charset="));
+ if (i >= 0) {
+ QString cs(mime.mid(i+8).toLower());
+ i = cs.indexOf(QLatin1String(";"));
+ if (i>=0)
+ cs = cs.left(i);
+ if (cs == QLatin1String("system"))
+ return QLatin1String("public.utf8-plain-text");
+ else if (cs == QLatin1String("iso-10646-ucs-2")
+ || cs == QLatin1String("utf16"))
+ return QLatin1String("public.utf16-plain-text");
+ }
+ return QString();
+}
+
+QString QMacPasteboardMimeUnicodeText::mimeFor(QString flav)
+{
+ if (flav == QLatin1String("public.utf16-plain-text") || flav == QLatin1String("public.utf8-plain-text"))
+ return QLatin1String("text/plain");
+ return QString();
+}
+
+bool QMacPasteboardMimeUnicodeText::canConvert(const QString &mime, QString flav)
+{
+ return flavorFor(mime) == flav;
+}
+
+QVariant QMacPasteboardMimeUnicodeText::convertToMime(const QString &mimetype, QList<QByteArray> data, QString flavor)
+{
+ if(data.count() > 1)
+ qWarning("QMacPasteboardMimeUnicodeText: Cannot handle multiple member data");
+ const QByteArray &firstData = data.first();
+ // I can only handle two types (system and unicode) so deal with them that way
+ QVariant ret;
+ if(flavor == QLatin1String("public.utf8-plain-text")) {
+ QCFString str(CFStringCreateWithBytes(kCFAllocatorDefault,
+ reinterpret_cast<const UInt8 *>(firstData.constData()),
+ firstData.size(), CFStringGetSystemEncoding(), false));
+ ret = QString(str);
+ } else if (flavor == QLatin1String("public.utf16-plain-text")) {
+ ret = QString::fromUtf16(reinterpret_cast<const ushort *>(firstData.constData()),
+ firstData.size() / sizeof(ushort));
+ } else {
+ qWarning("QMime::convertToMime: unhandled mimetype: %s", qPrintable(mimetype));
+ }
+ return ret;
+}
+
+QList<QByteArray> QMacPasteboardMimeUnicodeText::convertFromMime(const QString &, QVariant data, QString flavor)
+{
+ QList<QByteArray> ret;
+ QString string = data.toString();
+ if(flavor == QLatin1String("public.utf8-plain-text"))
+ ret.append(string.toUtf8());
+ else if (flavor == QLatin1String("public.utf16-plain-text"))
+ ret.append(QByteArray((char*)string.utf16(), string.length()*2));
+ return ret;
+}
+
+class QMacPasteboardMimeHTMLText : public QMacPasteboardMime {
+public:
+ QMacPasteboardMimeHTMLText() : QMacPasteboardMime(MIME_ALL) { }
+ QString convertorName();
+
+ QString flavorFor(const QString &mime);
+ QString mimeFor(QString flav);
+ bool canConvert(const QString &mime, QString flav);
+ QVariant convertToMime(const QString &mime, QList<QByteArray> data, QString flav);
+ QList<QByteArray> convertFromMime(const QString &mime, QVariant data, QString flav);
+};
+
+QString QMacPasteboardMimeHTMLText::convertorName()
+{
+ return QLatin1String("HTML");
+}
+
+QString QMacPasteboardMimeHTMLText::flavorFor(const QString &mime)
+{
+ if (mime == QLatin1String("text/html"))
+ return QLatin1String("public.html");
+ return QString();
+}
+
+QString QMacPasteboardMimeHTMLText::mimeFor(QString flav)
+{
+ if (flav == QLatin1String("public.html"))
+ return QLatin1String("text/html");
+ return QString();
+}
+
+bool QMacPasteboardMimeHTMLText::canConvert(const QString &mime, QString flav)
+{
+ return flavorFor(mime) == flav;
+}
+
+QVariant QMacPasteboardMimeHTMLText::convertToMime(const QString &mimeType, QList<QByteArray> data, QString flavor)
+{
+ if (!canConvert(mimeType, flavor))
+ return QVariant();
+ if (data.count() > 1)
+ qWarning("QMacPasteboardMimeHTMLText: Cannot handle multiple member data");
+ return data.first();
+}
+
+QList<QByteArray> QMacPasteboardMimeHTMLText::convertFromMime(const QString &mime, QVariant data, QString flavor)
+{
+ QList<QByteArray> ret;
+ if (!canConvert(mime, flavor))
+ return ret;
+ ret.append(data.toByteArray());
+ return ret;
+}
+
+
+#ifdef Q_WS_MAC32
+
+typedef ComponentResult (*PtrGraphicsImportSetDataHandle)(GraphicsImportComponent, Handle);
+typedef ComponentResult (*PtrGraphicsImportCreateCGImage)(GraphicsImportComponent, CGImageRef*, UInt32);
+typedef ComponentResult (*PtrGraphicsExportSetInputCGImage)(GraphicsExportComponent, CGImageRef);
+typedef ComponentResult (*PtrGraphicsExportSetOutputHandle)(GraphicsExportComponent, Handle);
+typedef ComponentResult (*PtrGraphicsExportDoExport)(GraphicsExportComponent, unsigned long *);
+
+static PtrGraphicsImportSetDataHandle ptrGraphicsImportSetDataHandle = 0;
+static PtrGraphicsImportCreateCGImage ptrGraphicsImportCreateCGImage = 0;
+static PtrGraphicsExportSetInputCGImage ptrGraphicsExportSetInputCGImage = 0;
+static PtrGraphicsExportSetOutputHandle ptrGraphicsExportSetOutputHandle = 0;
+static PtrGraphicsExportDoExport ptrGraphicsExportDoExport = 0;
+
+static bool resolveMimeQuickTimeSymbols()
+{
+ if (ptrGraphicsImportSetDataHandle == 0) {
+ QLibrary library(QLatin1String("/System/Library/Frameworks/QuickTime.framework/QuickTime"));
+ ptrGraphicsImportSetDataHandle = reinterpret_cast<PtrGraphicsImportSetDataHandle>(library.resolve("GraphicsImportSetDataHandle"));
+ ptrGraphicsImportCreateCGImage = reinterpret_cast<PtrGraphicsImportCreateCGImage>(library.resolve("GraphicsImportCreateCGImage"));
+ ptrGraphicsExportSetInputCGImage = reinterpret_cast<PtrGraphicsExportSetInputCGImage>(library.resolve("GraphicsExportSetInputCGImage"));
+ ptrGraphicsExportSetOutputHandle = reinterpret_cast<PtrGraphicsExportSetOutputHandle>(library.resolve("GraphicsExportSetOutputHandle"));
+ ptrGraphicsExportDoExport = reinterpret_cast<PtrGraphicsExportDoExport>(library.resolve("GraphicsExportDoExport"));
+ }
+
+ return ptrGraphicsImportSetDataHandle != 0
+ && ptrGraphicsImportCreateCGImage != 0 && ptrGraphicsExportSetInputCGImage != 0
+ && ptrGraphicsExportSetOutputHandle != 0 && ptrGraphicsExportDoExport != 0;
+}
+
+class QMacPasteboardMimePict : public QMacPasteboardMime {
+public:
+ QMacPasteboardMimePict() : QMacPasteboardMime(MIME_ALL) { }
+ QString convertorName();
+
+ QString flavorFor(const QString &mime);
+ QString mimeFor(QString flav);
+ bool canConvert(const QString &mime, QString flav);
+ QVariant convertToMime(const QString &mime, QList<QByteArray> data, QString flav);
+ QList<QByteArray> convertFromMime(const QString &mime, QVariant data, QString flav);
+};
+
+QString QMacPasteboardMimePict::convertorName()
+{
+ return QLatin1String("Pict");
+}
+
+QString QMacPasteboardMimePict::flavorFor(const QString &mime)
+{
+ if(mime.startsWith(QLatin1String("application/x-qt-image")))
+ return QLatin1String("com.apple.pict");
+ return QString();
+}
+
+QString QMacPasteboardMimePict::mimeFor(QString flav)
+{
+ if(flav == QLatin1String("com.apple.pict"))
+ return QLatin1String("application/x-qt-image");
+ return QString();
+}
+
+bool QMacPasteboardMimePict::canConvert(const QString &mime, QString flav)
+{
+ return flav == QLatin1String("com.apple.pict")
+ && mime == QLatin1String("application/x-qt-image");
+}
+
+
+QVariant QMacPasteboardMimePict::convertToMime(const QString &mime, QList<QByteArray> data, QString flav)
+{
+ if(data.count() > 1)
+ qWarning("QMacPasteboardMimePict: Cannot handle multiple member data");
+ QVariant ret;
+ if (!resolveMimeQuickTimeSymbols())
+ return ret;
+
+ if(!canConvert(mime, flav))
+ return ret;
+ const QByteArray &a = data.first();
+
+ // This function expects the 512 header (just to skip it, so create the extra space for it).
+ Handle pic = NewHandle(a.size() + 512);
+ memcpy(*pic + 512, a.constData(), a.size());
+
+ GraphicsImportComponent graphicsImporter;
+ ComponentResult result = OpenADefaultComponent(GraphicsImporterComponentType,
+ kQTFileTypePicture, &graphicsImporter);
+ QCFType<CGImageRef> cgImage;
+ if (!result)
+ result = ptrGraphicsImportSetDataHandle(graphicsImporter, pic);
+ if (!result)
+ result = ptrGraphicsImportCreateCGImage(graphicsImporter, &cgImage,
+ kGraphicsImportCreateCGImageUsingCurrentSettings);
+ if (!result)
+ ret = QVariant(QPixmap::fromMacCGImageRef(cgImage).toImage());
+ CloseComponent(graphicsImporter);
+ DisposeHandle(pic);
+ return ret;
+}
+
+QList<QByteArray> QMacPasteboardMimePict::convertFromMime(const QString &mime, QVariant variant,
+ QString flav)
+{
+ QList<QByteArray> ret;
+ if (!resolveMimeQuickTimeSymbols())
+ return ret;
+
+ if (!canConvert(mime, flav))
+ return ret;
+ QCFType<CGImageRef> cgimage = qt_mac_createCGImageFromQImage(qvariant_cast<QImage>(variant));
+ Handle pic = NewHandle(0);
+ GraphicsExportComponent graphicsExporter;
+ ComponentResult result = OpenADefaultComponent(GraphicsExporterComponentType,
+ kQTFileTypePicture, &graphicsExporter);
+ if (!result) {
+ unsigned long sizeWritten;
+ result = ptrGraphicsExportSetInputCGImage(graphicsExporter, cgimage);
+ if (!result)
+ result = ptrGraphicsExportSetOutputHandle(graphicsExporter, pic);
+ if (!result)
+ result = ptrGraphicsExportDoExport(graphicsExporter, &sizeWritten);
+
+ CloseComponent(graphicsExporter);
+ }
+
+ int size = GetHandleSize((Handle)pic);
+ // Skip the Picture File header (512 bytes) and feed the raw data
+ QByteArray ar(reinterpret_cast<char *>(*pic + 512), size - 512);
+ ret.append(ar);
+ DisposeHandle(pic);
+ return ret;
+}
+#endif
+
+class QMacPasteboardMimeTiff : public QMacPasteboardMime {
+public:
+ QMacPasteboardMimeTiff() : QMacPasteboardMime(MIME_ALL) { }
+ QString convertorName();
+
+ QString flavorFor(const QString &mime);
+ QString mimeFor(QString flav);
+ bool canConvert(const QString &mime, QString flav);
+ QVariant convertToMime(const QString &mime, QList<QByteArray> data, QString flav);
+ QList<QByteArray> convertFromMime(const QString &mime, QVariant data, QString flav);
+};
+
+QString QMacPasteboardMimeTiff::convertorName()
+{
+ return QLatin1String("Tiff");
+}
+
+QString QMacPasteboardMimeTiff::flavorFor(const QString &mime)
+{
+ if(mime.startsWith(QLatin1String("application/x-qt-image")))
+ return QLatin1String("public.tiff");
+ return QString();
+}
+
+QString QMacPasteboardMimeTiff::mimeFor(QString flav)
+{
+ if(flav == QLatin1String("public.tiff"))
+ return QLatin1String("application/x-qt-image");
+ return QString();
+}
+
+bool QMacPasteboardMimeTiff::canConvert(const QString &mime, QString flav)
+{
+ return flav == QLatin1String("public.tiff") && mime == QLatin1String("application/x-qt-image");
+}
+
+QVariant QMacPasteboardMimeTiff::convertToMime(const QString &mime, QList<QByteArray> data, QString flav)
+{
+ if(data.count() > 1)
+ qWarning("QMacPasteboardMimeTiff: Cannot handle multiple member data");
+ QVariant ret;
+ if (!canConvert(mime, flav))
+ return ret;
+ const QByteArray &a = data.first();
+ QCFType<CGImageRef> image;
+#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4
+ if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_4) {
+ QCFType<CFDataRef> data = CFDataCreateWithBytesNoCopy(0,
+ reinterpret_cast<const UInt8 *>(a.constData()),
+ a.size(), kCFAllocatorNull);
+ QCFType<CGImageSourceRef> imageSource = CGImageSourceCreateWithData(data, 0);
+ image = CGImageSourceCreateImageAtIndex(imageSource, 0, 0);
+ } else
+#endif
+ {
+#ifdef Q_WS_MAC32
+ if (resolveMimeQuickTimeSymbols()) {
+ Handle tiff = NewHandle(a.size());
+ memcpy(*tiff, a.constData(), a.size());
+ GraphicsImportComponent graphicsImporter;
+ ComponentResult result = OpenADefaultComponent(GraphicsImporterComponentType,
+ kQTFileTypeTIFF, &graphicsImporter);
+ if (!result)
+ result = ptrGraphicsImportSetDataHandle(graphicsImporter, tiff);
+ if (!result)
+ result = ptrGraphicsImportCreateCGImage(graphicsImporter, &image,
+ kGraphicsImportCreateCGImageUsingCurrentSettings);
+ CloseComponent(graphicsImporter);
+ DisposeHandle(tiff);
+ }
+#endif
+ }
+
+ if (image != 0)
+ ret = QVariant(QPixmap::fromMacCGImageRef(image).toImage());
+ return ret;
+}
+
+QList<QByteArray> QMacPasteboardMimeTiff::convertFromMime(const QString &mime, QVariant variant, QString flav)
+{
+ QList<QByteArray> ret;
+ if (!canConvert(mime, flav))
+ return ret;
+
+ QImage img = qvariant_cast<QImage>(variant);
+ QCFType<CGImageRef> cgimage = qt_mac_createCGImageFromQImage(img);
+#if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4)
+ if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_4) {
+ QCFType<CFMutableDataRef> data = CFDataCreateMutable(0, 0);
+ QCFType<CGImageDestinationRef> imageDestination = CGImageDestinationCreateWithData(data, kUTTypeTIFF, 1, 0);
+ if (imageDestination != 0) {
+ CFTypeRef keys[2];
+ QCFType<CFTypeRef> values[2];
+ QCFType<CFDictionaryRef> options;
+ keys[0] = kCGImagePropertyPixelWidth;
+ keys[1] = kCGImagePropertyPixelHeight;
+ int width = img.width();
+ int height = img.height();
+ values[0] = CFNumberCreate(0, kCFNumberIntType, &width);
+ values[1] = CFNumberCreate(0, kCFNumberIntType, &height);
+ options = CFDictionaryCreate(0, reinterpret_cast<const void **>(keys),
+ reinterpret_cast<const void **>(values), 2,
+ &kCFTypeDictionaryKeyCallBacks,
+ &kCFTypeDictionaryValueCallBacks);
+ CGImageDestinationAddImage(imageDestination, cgimage, options);
+ CGImageDestinationFinalize(imageDestination);
+ }
+ QByteArray ar(CFDataGetLength(data), 0);
+ CFDataGetBytes(data,
+ CFRangeMake(0, ar.size()),
+ reinterpret_cast<UInt8 *>(ar.data()));
+ ret.append(ar);
+ } else
+#endif
+ {
+#ifdef Q_WS_MAC32
+ Handle tiff = NewHandle(0);
+ if (resolveMimeQuickTimeSymbols()) {
+ GraphicsExportComponent graphicsExporter;
+ ComponentResult result = OpenADefaultComponent(GraphicsExporterComponentType,
+ kQTFileTypeTIFF, &graphicsExporter);
+ if (!result) {
+ unsigned long sizeWritten;
+ result = ptrGraphicsExportSetInputCGImage(graphicsExporter, cgimage);
+ if (!result)
+ result = ptrGraphicsExportSetOutputHandle(graphicsExporter, tiff);
+ if (!result)
+ result = ptrGraphicsExportDoExport(graphicsExporter, &sizeWritten);
+
+ CloseComponent(graphicsExporter);
+ }
+ }
+ int size = GetHandleSize((Handle)tiff);
+ QByteArray ar(reinterpret_cast<char *>(*tiff), size);
+ ret.append(ar);
+ DisposeHandle(tiff);
+#endif
+ }
+ return ret;
+}
+
+
+class QMacPasteboardMimeFileUri : public QMacPasteboardMime {
+public:
+ QMacPasteboardMimeFileUri() : QMacPasteboardMime(MIME_ALL) { }
+ QString convertorName();
+
+ QString flavorFor(const QString &mime);
+ QString mimeFor(QString flav);
+ bool canConvert(const QString &mime, QString flav);
+ QVariant convertToMime(const QString &mime, QList<QByteArray> data, QString flav);
+ QList<QByteArray> convertFromMime(const QString &mime, QVariant data, QString flav);
+};
+
+QString QMacPasteboardMimeFileUri::convertorName()
+{
+ return QLatin1String("FileURL");
+}
+
+QString QMacPasteboardMimeFileUri::flavorFor(const QString &mime)
+{
+ if (mime == QLatin1String("text/uri-list"))
+ return QCFString(UTTypeCreatePreferredIdentifierForTag(kUTTagClassOSType, CFSTR("furl"), 0));
+ return QString();
+}
+
+QString QMacPasteboardMimeFileUri::mimeFor(QString flav)
+{
+ if (flav == QCFString(UTTypeCreatePreferredIdentifierForTag(kUTTagClassOSType, CFSTR("furl"), 0)))
+ return QLatin1String("text/uri-list");
+ return QString();
+}
+
+bool QMacPasteboardMimeFileUri::canConvert(const QString &mime, QString flav)
+{
+ return mime == QLatin1String("text/uri-list")
+ && flav == QCFString(UTTypeCreatePreferredIdentifierForTag(kUTTagClassOSType, CFSTR("furl"), 0));
+}
+
+QVariant QMacPasteboardMimeFileUri::convertToMime(const QString &mime, QList<QByteArray> data, QString flav)
+{
+ if(!canConvert(mime, flav))
+ return QVariant();
+ QList<QVariant> ret;
+ for(int i = 0; i < data.size(); ++i) {
+ QUrl url = QUrl::fromEncoded(data.at(i));
+ if (url.host().toLower() == QLatin1String("localhost"))
+ url.setHost(QString());
+ url.setPath(url.path().normalized(QString::NormalizationForm_C));
+ ret.append(url);
+ }
+ return QVariant(ret);
+}
+
+QList<QByteArray> QMacPasteboardMimeFileUri::convertFromMime(const QString &mime, QVariant data, QString flav)
+{
+ QList<QByteArray> ret;
+ if (!canConvert(mime, flav))
+ return ret;
+ QList<QVariant> urls = data.toList();
+ for(int i = 0; i < urls.size(); ++i) {
+ QUrl url = urls.at(i).toUrl();
+ if (url.scheme().isEmpty())
+ url.setScheme(QLatin1String("file"));
+ if (url.scheme().toLower() == QLatin1String("file")) {
+ if (url.host().isEmpty())
+ url.setHost(QLatin1String("localhost"));
+ url.setPath(url.path().normalized(QString::NormalizationForm_D));
+ }
+ ret.append(url.toEncoded());
+ }
+ return ret;
+}
+
+#ifdef QT3_SUPPORT
+class QMacPasteboardMimeQt3Any : public QMacPasteboardMime {
+private:
+ int current_max;
+ QFile library_file;
+ QDateTime mime_registry_loaded;
+ QMap<QString, int> mime_registry;
+ int registerMimeType(const QString &mime);
+ bool loadMimeRegistry();
+
+public:
+ QMacPasteboardMimeQt3Any() : QMacPasteboardMime(MIME_QT3_CONVERTOR) {
+ current_max = 'QT00';
+ }
+ ~QMacPasteboardMimeQt3Any() {
+ }
+ QString convertorName();
+
+ QString flavorFor(const QString &mime);
+ QString mimeFor(QString flav);
+ bool canConvert(const QString &mime, QString flav);
+ QVariant convertToMime(const QString &mime, QList<QByteArray> data, QString flav);
+ QList<QByteArray> convertFromMime(const QString &mime, QVariant data, QString flav);
+};
+
+static bool qt_mac_openMimeRegistry(bool global, QIODevice::OpenMode mode, QFile &file)
+{
+ QString dir = QLatin1String("/Library/Qt");
+ if(!global)
+ dir.prepend(QDir::homePath());
+ file.setFileName(dir + QLatin1String("/.mime_types"));
+ if(mode != QIODevice::ReadOnly) {
+ if(!QFile::exists(dir)) {
+ // Do it with a system call as I don't see much worth in
+ // doing it with QDir since we have to chmod anyway.
+ bool success = ::mkdir(dir.toLocal8Bit().constData(), S_IRUSR | S_IWUSR | S_IXUSR) == 0;
+ if (success)
+ success = ::chmod(dir.toLocal8Bit().constData(), S_IRUSR | S_IWUSR | S_IXUSR
+ | S_IRGRP | S_IWGRP | S_IXGRP | S_IROTH | S_IWOTH | S_IXOTH) == 0;
+ if (!success)
+ return false;
+ }
+ if (!file.exists()) {
+ // Create the file and chmod it so that everyone can write to it.
+ int fd = ::open(file.fileName().toLocal8Bit().constData(), O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR);
+ bool success = fd != -1;
+ if (success)
+ success = ::fchmod(fd, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH) == 0;
+ if (fd != -1)
+ ::close(fd);
+ if(!success)
+ return false;
+ }
+ }
+ return file.open(mode);
+}
+
+static void qt_mac_loadMimeRegistry(QFile &file, QMap<QString, int> &registry, int &max)
+{
+ file.reset();
+ QTextStream stream(&file);
+ while(!stream.atEnd()) {
+ QString mime = stream.readLine();
+ int mactype = stream.readLine().toInt();
+ if(mactype > max)
+ max = mactype;
+ registry.insert(mime, mactype);
+ }
+}
+
+bool QMacPasteboardMimeQt3Any::loadMimeRegistry()
+{
+ if(!library_file.isOpen()) {
+ if(!qt_mac_openMimeRegistry(true, QIODevice::ReadWrite, library_file)) {
+ QFile global;
+ if(qt_mac_openMimeRegistry(true, QIODevice::ReadOnly, global)) {
+ qt_mac_loadMimeRegistry(global, mime_registry, current_max);
+ global.close();
+ }
+ if(!qt_mac_openMimeRegistry(false, QIODevice::ReadWrite, library_file)) {
+ qWarning("QMacPasteboardMimeAnyQt3Mime: Failure to open mime resources %s -- %s", library_file.fileName().toLatin1().constData(),
+ library_file.errorString().toLatin1().constData());
+ return false;
+ }
+ }
+ }
+
+ QFileInfo fi(library_file);
+ if(!mime_registry_loaded.isNull() && mime_registry_loaded == fi.lastModified())
+ return true;
+ mime_registry_loaded = fi.lastModified();
+ qt_mac_loadMimeRegistry(library_file, mime_registry, current_max);
+ return true;
+}
+
+int QMacPasteboardMimeQt3Any::registerMimeType(const QString &mime)
+{
+ if(!mime_registry.contains(mime)) {
+ if(!loadMimeRegistry()) {
+ qWarning("QMacPasteboardMimeAnyQt3Mime: Internal error");
+ return 0;
+ }
+ if(!mime_registry.contains(mime)) {
+ if(!library_file.isOpen()) {
+ if(!library_file.open(QIODevice::WriteOnly)) {
+ qWarning("QMacPasteboardMimeAnyQt3Mime: Failure to open %s -- %s", library_file.fileName().toLatin1().constData(),
+ library_file.errorString().toLatin1().constData());
+ return false;
+ }
+ }
+ int ret = ++current_max;
+ mime_registry_loaded = QFileInfo(library_file).lastModified();
+ QTextStream stream(&library_file);
+ stream << mime << endl;
+ stream << ret << endl;
+ mime_registry.insert(mime, ret);
+ library_file.flush(); //flush and set mtime
+ return ret;
+ }
+ }
+ return mime_registry[mime];
+}
+
+QString QMacPasteboardMimeQt3Any::convertorName()
+{
+ return QLatin1String("Qt3-Any-Mime");
+}
+
+QString QMacPasteboardMimeQt3Any::flavorFor(const QString &mime)
+{
+ const int os_flav = registerMimeType(mime);
+ QCFType<CFArrayRef> ids = UTTypeCreateAllIdentifiersForTag(0, kUTTagClassOSType,
+ QCFString(UTCreateStringForOSType(os_flav)));
+ if(ids) {
+ const int type_count = CFArrayGetCount(ids);
+ if(type_count) {
+ if(type_count > 1)
+ qDebug("Can't happen!");
+ return QCFString::toQString((CFStringRef)CFArrayGetValueAtIndex(ids, 0));
+ }
+ }
+ return QString();
+}
+
+QString QMacPasteboardMimeQt3Any::mimeFor(QString flav)
+{
+ loadMimeRegistry();
+ const int os_flav = UTGetOSTypeFromString(UTTypeCopyPreferredTagWithClass(QCFString(flav), kUTTagClassOSType));
+ for(QMap<QString, int>::const_iterator it = mime_registry.constBegin();
+ it != mime_registry.constEnd(); ++it) {
+ if(it.value() == os_flav)
+ return QString::fromLatin1(it.key().toLatin1());
+ }
+ return QString();
+}
+
+bool QMacPasteboardMimeQt3Any::canConvert(const QString &mime, QString flav)
+{
+ loadMimeRegistry();
+ const int os_flav = UTGetOSTypeFromString(UTTypeCopyPreferredTagWithClass(QCFString(flav), kUTTagClassOSType));
+ if(mime_registry.contains(mime) && mime_registry[mime] == os_flav)
+ return true;
+ return false;
+}
+
+QVariant QMacPasteboardMimeQt3Any::convertToMime(const QString &, QList<QByteArray>, QString)
+{
+ qWarning("QMacPasteboardMimeAnyQt3Mime: Cannot write anything!");
+ return QVariant();
+}
+
+QList<QByteArray> QMacPasteboardMimeQt3Any::convertFromMime(const QString &mime, QVariant data, QString)
+{
+ QList<QByteArray> ret;
+ if (mime == QLatin1String("text/plain")) {
+ ret.append(data.toString().toUtf8());
+ } else {
+ ret.append(data.toByteArray());
+ }
+ return ret;
+}
+#endif
+
+/*!
+ \internal
+
+ This is an internal function.
+*/
+void QMacPasteboardMime::initialize()
+{
+ if(globalMimeList()->isEmpty()) {
+ qAddPostRoutine(cleanup_mimes);
+
+ //standard types that we wrap
+ new QMacPasteboardMimeTiff;
+#ifdef Q_WS_MAC32
+ new QMacPasteboardMimePict;
+#endif
+ new QMacPasteboardMimeUnicodeText;
+ new QMacPasteboardMimePlainText;
+ new QMacPasteboardMimeHTMLText;
+ new QMacPasteboardMimeFileUri;
+ new QMacPasteboardMimeTypeName;
+ //make sure our "non-standard" types are always last! --Sam
+ new QMacPasteboardMimeAny;
+#ifdef QT3_SUPPORT
+ new QMacPasteboardMimeQt3Any;
+#endif
+ }
+}
+
+/*!
+ Returns the most-recently created QMacPasteboardMime of type \a t that can convert
+ between the \a mime and \a flav formats. Returns 0 if no such convertor
+ exists.
+*/
+QMacPasteboardMime*
+QMacPasteboardMime::convertor(uchar t, const QString &mime, QString flav)
+{
+ MimeList *mimes = globalMimeList();
+ for(MimeList::const_iterator it = mimes->constBegin(); it != mimes->constEnd(); ++it) {
+#ifdef DEBUG_MIME_MAPS
+ qDebug("QMacPasteboardMime::convertor: seeing if %s (%d) can convert %s to %d[%c%c%c%c] [%d]",
+ (*it)->convertorName().toLatin1().constData(),
+ (*it)->type & t, mime.toLatin1().constData(),
+ flav, (flav >> 24) & 0xFF, (flav >> 16) & 0xFF, (flav >> 8) & 0xFF, (flav) & 0xFF,
+ (*it)->canConvert(mime,flav));
+ for(int i = 0; i < (*it)->countFlavors(); ++i) {
+ int f = (*it)->flavor(i);
+ qDebug(" %d) %d[%c%c%c%c] [%s]", i, f,
+ (f >> 24) & 0xFF, (f >> 16) & 0xFF, (f >> 8) & 0xFF, (f) & 0xFF,
+ (*it)->convertorName().toLatin1().constData());
+ }
+#endif
+ if(((*it)->type & t) && (*it)->canConvert(mime, flav))
+ return (*it);
+ }
+ return 0;
+}
+/*!
+ Returns a MIME type of type \a t for \a flav, or 0 if none exists.
+*/
+QString QMacPasteboardMime::flavorToMime(uchar t, QString flav)
+{
+ MimeList *mimes = globalMimeList();
+ for(MimeList::const_iterator it = mimes->constBegin(); it != mimes->constEnd(); ++it) {
+#ifdef DEBUG_MIME_MAPS
+ qDebug("QMacMIme::flavorToMime: attempting %s (%d) for flavor %d[%c%c%c%c] [%s]",
+ (*it)->convertorName().toLatin1().constData(),
+ (*it)->type & t, flav, (flav >> 24) & 0xFF, (flav >> 16) & 0xFF, (flav >> 8) & 0xFF, (flav) & 0xFF,
+ (*it)->mimeFor(flav).toLatin1().constData());
+
+#endif
+ if((*it)->type & t) {
+ QString mimeType = (*it)->mimeFor(flav);
+ if(!mimeType.isNull())
+ return mimeType;
+ }
+ }
+ return QString();
+}
+
+/*!
+ Returns a list of all currently defined QMacPasteboardMime objects of type \a t.
+*/
+QList<QMacPasteboardMime*> QMacPasteboardMime::all(uchar t)
+{
+ MimeList ret;
+ MimeList *mimes = globalMimeList();
+ for(MimeList::const_iterator it = mimes->constBegin(); it != mimes->constEnd(); ++it) {
+ if((*it)->type & t)
+ ret.append((*it));
+ }
+ return ret;
+}
+
+
+/*!
+ \fn QString QMacPasteboardMime::convertorName()
+
+ Returns a name for the convertor.
+
+ All subclasses must reimplement this pure virtual function.
+*/
+
+/*!
+ \fn bool QMacPasteboardMime::canConvert(const QString &mime, QString flav)
+
+ Returns true if the convertor can convert (both ways) between
+ \a mime and \a flav; otherwise returns false.
+
+ All subclasses must reimplement this pure virtual function.
+*/
+
+/*!
+ \fn QString QMacPasteboardMime::mimeFor(QString flav)
+
+ Returns the MIME UTI used for Mac flavor \a flav, or 0 if this
+ convertor does not support \a flav.
+
+ All subclasses must reimplement this pure virtual function.
+*/
+
+/*!
+ \fn QString QMacPasteboardMime::flavorFor(const QString &mime)
+
+ Returns the Mac UTI used for MIME type \a mime, or 0 if this
+ convertor does not support \a mime.
+
+ All subclasses must reimplement this pure virtual function.
+*/
+
+/*!
+ \fn QVariant QMacPasteboardMime::convertToMime(const QString &mime, QList<QByteArray> data, QString flav)
+
+ Returns \a data converted from Mac UTI \a flav to MIME type \a
+ mime.
+
+ Note that Mac flavors must all be self-terminating. The input \a
+ data may contain trailing data.
+
+ All subclasses must reimplement this pure virtual function.
+*/
+
+/*!
+ \fn QList<QByteArray> QMacPasteboardMime::convertFromMime(const QString &mime, QVariant data, QString flav)
+
+ Returns \a data converted from MIME type \a mime
+ to Mac UTI \a flav.
+
+ Note that Mac flavors must all be self-terminating. The return
+ value may contain trailing data.
+
+ All subclasses must reimplement this pure virtual function.
+*/
+
+
+QT_END_NAMESPACE
diff --git a/src/gui/kernel/qmime_win.cpp b/src/gui/kernel/qmime_win.cpp
new file mode 100644
index 0000000000..cd0aae6ddb
--- /dev/null
+++ b/src/gui/kernel/qmime_win.cpp
@@ -0,0 +1,1594 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qmime.h"
+
+#include "qimagereader.h"
+#include "qimagewriter.h"
+#include "qdatastream.h"
+#include "qbuffer.h"
+#include "qt_windows.h"
+#include "qapplication_p.h"
+#include "qtextcodec.h"
+#include "qregexp.h"
+#include "qalgorithms.h"
+#include "qmap.h"
+#include "qdnd_p.h"
+#include <shlobj.h>
+#include "qurl.h"
+#include "qvariant.h"
+#include "qtextdocument.h"
+#include "qdir.h"
+
+#if defined(Q_OS_WINCE)
+#include "qguifunctions_wince.h"
+#endif
+
+QT_BEGIN_NAMESPACE
+
+#ifndef QT_NO_IMAGEFORMAT_BMP
+#ifndef CF_DIBV5
+#define CF_DIBV5 17
+#endif
+/* The MSVC compilers allows multi-byte characters, that has the behavior of
+ * that each character gets shifted into position. 0x73524742 below is for MSVC
+ * equivalent to doing 'sRGB', but this does of course not work
+ * on conformant C++ compilers. */
+#define BMP_LCS_sRGB 0x73524742
+#define BMP_LCS_GM_IMAGES 0x00000004L
+
+struct _CIEXYZ {
+ long ciexyzX, ciexyzY, ciexyzZ;
+};
+
+struct _CIEXYZTRIPLE {
+ _CIEXYZ ciexyzRed, ciexyzGreen, ciexyzBlue;
+};
+
+struct BMP_BITMAPV5HEADER {
+ DWORD bV5Size;
+ LONG bV5Width;
+ LONG bV5Height;
+ WORD bV5Planes;
+ WORD bV5BitCount;
+ DWORD bV5Compression;
+ DWORD bV5SizeImage;
+ LONG bV5XPelsPerMeter;
+ LONG bV5YPelsPerMeter;
+ DWORD bV5ClrUsed;
+ DWORD bV5ClrImportant;
+ DWORD bV5RedMask;
+ DWORD bV5GreenMask;
+ DWORD bV5BlueMask;
+ DWORD bV5AlphaMask;
+ DWORD bV5CSType;
+ _CIEXYZTRIPLE bV5Endpoints;
+ DWORD bV5GammaRed;
+ DWORD bV5GammaGreen;
+ DWORD bV5GammaBlue;
+ DWORD bV5Intent;
+ DWORD bV5ProfileData;
+ DWORD bV5ProfileSize;
+ DWORD bV5Reserved;
+};
+static const int BMP_BITFIELDS = 3;
+
+extern bool qt_read_dib(QDataStream&, QImage&); // qimage.cpp
+extern bool qt_write_dib(QDataStream&, QImage); // qimage.cpp
+static bool qt_write_dibv5(QDataStream &s, QImage image);
+static bool qt_read_dibv5(QDataStream &s, QImage &image);
+#endif
+
+//#define QMIME_DEBUG
+
+
+// helpers for using global memory
+
+static int getCf(const FORMATETC &formatetc)
+{
+ return formatetc.cfFormat;
+}
+
+static FORMATETC setCf(int cf)
+{
+ FORMATETC formatetc;
+ formatetc.cfFormat = cf;
+ formatetc.dwAspect = DVASPECT_CONTENT;
+ formatetc.lindex = -1;
+ formatetc.ptd = NULL;
+ formatetc.tymed = TYMED_HGLOBAL;
+ return formatetc;
+}
+
+static bool setData(const QByteArray &data, STGMEDIUM *pmedium)
+{
+ HGLOBAL hData = GlobalAlloc(0, data.size());
+ if (!hData)
+ return false;
+
+ void *out = GlobalLock(hData);
+ memcpy(out, data.data(), data.size());
+ GlobalUnlock(hData);
+ pmedium->tymed = TYMED_HGLOBAL;
+ pmedium->hGlobal = hData;
+ pmedium->pUnkForRelease = 0;
+ return true;
+}
+
+static QByteArray getData(int cf, IDataObject *pDataObj)
+{
+ QByteArray data;
+ FORMATETC formatetc = setCf(cf);
+ STGMEDIUM s;
+ if (pDataObj->GetData(&formatetc, &s) == S_OK) {
+ DWORD * val = (DWORD*)GlobalLock(s.hGlobal);
+ data = QByteArray::fromRawData((char*)val, GlobalSize(s.hGlobal));
+ data.detach();
+ GlobalUnlock(s.hGlobal);
+ ReleaseStgMedium(&s);
+ } else {
+ //Try reading IStream data
+ formatetc.tymed = TYMED_ISTREAM;
+ if (pDataObj->GetData(&formatetc, &s) == S_OK) {
+ char szBuffer[4096];
+ ULONG actualRead = 0;
+ LARGE_INTEGER pos = {0, 0};
+ //Move to front (can fail depending on the data model implemented)
+ HRESULT hr = s.pstm->Seek(pos, STREAM_SEEK_SET, NULL);
+ while(SUCCEEDED(hr)){
+ hr = s.pstm->Read(szBuffer, sizeof(szBuffer), &actualRead);
+ if (SUCCEEDED(hr) && actualRead > 0) {
+ data += QByteArray::fromRawData(szBuffer, actualRead);
+ }
+ if (actualRead != sizeof(szBuffer))
+ break;
+ }
+ data.detach();
+ ReleaseStgMedium(&s);
+ }
+ }
+ return data;
+}
+
+static bool canGetData(int cf, IDataObject * pDataObj)
+{
+ FORMATETC formatetc = setCf(cf);
+ if (pDataObj->QueryGetData(&formatetc) != S_OK){
+ formatetc.tymed = TYMED_ISTREAM;
+ return pDataObj->QueryGetData(&formatetc) == S_OK;
+ }
+ return true;
+}
+
+class QWindowsMimeList
+{
+public:
+ QWindowsMimeList();
+ ~QWindowsMimeList();
+ void addWindowsMime(QWindowsMime * mime);
+ void removeWindowsMime(QWindowsMime * mime);
+ QList<QWindowsMime*> windowsMimes();
+
+private:
+ void init();
+ bool initialized;
+ QList<QWindowsMime*> mimes;
+};
+
+Q_GLOBAL_STATIC(QWindowsMimeList, theMimeList);
+
+
+/*!
+ \class QWindowsMime
+ \brief The QWindowsMime class maps open-standard MIME to Window Clipboard formats.
+ \ingroup io
+ \ingroup draganddrop
+ \ingroup misc
+
+ Qt's drag-and-drop and clipboard facilities use the MIME standard.
+ On X11, this maps trivially to the Xdnd protocol, but on Windows
+ although some applications use MIME types to describe clipboard
+ formats, others use arbitrary non-standardized naming conventions,
+ or unnamed built-in formats of Windows.
+
+ By instantiating subclasses of QWindowsMime that provide conversions
+ between Windows Clipboard and MIME formats, you can convert
+ proprietary clipboard formats to MIME formats.
+
+ Qt has predefined support for the following Windows Clipboard formats:
+
+ \table
+ \header \o Windows Format \o Equivalent MIME type
+ \row \o \c CF_UNICODETEXT \o \c text/plain
+ \row \o \c CF_TEXT \o \c text/plain
+ \row \o \c CF_DIB \o \c{image/xyz}, where \c xyz is
+ a \l{QImageWriter::supportedImageFormats()}{Qt image format}
+ \row \o \c CF_HDROP \o \c text/uri-list
+ \row \o \c CF_INETURL \o \c text/uri-list
+ \row \o \c CF_HTML \o \c text/html
+ \endtable
+
+ An example use of this class would be to map the Windows Metafile
+ clipboard format (\c CF_METAFILEPICT) to and from the MIME type
+ \c{image/x-wmf}. This conversion might simply be adding or removing
+ a header, or even just passing on the data. See \l{Drag and Drop}
+ for more information on choosing and definition MIME types.
+
+ You can check if a MIME type is convertible using canConvertFromMime() and
+ can perform conversions with convertToMime() and convertFromMime().
+*/
+
+/*!
+Constructs a new conversion object, adding it to the globally accessed
+list of available converters.
+*/
+QWindowsMime::QWindowsMime()
+{
+ theMimeList()->addWindowsMime(this);
+}
+
+/*!
+Destroys a conversion object, removing it from the global
+list of available converters.
+*/
+QWindowsMime::~QWindowsMime()
+{
+ theMimeList()->removeWindowsMime(this);
+}
+
+
+/*!
+ Registers the MIME type \a mime, and returns an ID number
+ identifying the format on Windows.
+*/
+int QWindowsMime::registerMimeType(const QString &mime)
+{
+#ifdef Q_OS_WINCE
+ int f = RegisterClipboardFormat(reinterpret_cast<const wchar_t *> (mime.utf16()));
+#else
+ int f = RegisterClipboardFormatA(mime.toLocal8Bit());
+#endif
+ if (!f)
+ qErrnoWarning("QWindowsMime::registerMimeType: Failed to register clipboard format");
+
+ return f;
+}
+
+
+/*!
+\fn bool QWindowsMime::canConvertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData) const
+
+ Returns true if the converter can convert from the \a mimeData to
+ the format specified in \a formatetc.
+
+ All subclasses must reimplement this pure virtual function.
+*/
+
+/*!
+ \fn bool QWindowsMime::canConvertToMime(const QString &mimeType, IDataObject *pDataObj) const
+
+ Returns true if the converter can convert to the \a mimeType from
+ the available formats in \a pDataObj.
+
+ All subclasses must reimplement this pure virtual function.
+*/
+
+/*!
+\fn QString QWindowsMime::mimeForFormat(const FORMATETC &formatetc) const
+
+ Returns the mime type that will be created form the format specified
+ in \a formatetc, or an empty string if this converter does not support
+ \a formatetc.
+
+ All subclasses must reimplement this pure virtual function.
+*/
+
+/*!
+\fn QVector<FORMATETC> QWindowsMime::formatsForMime(const QString &mimeType, const QMimeData *mimeData) const
+
+ Returns a QVector of FORMATETC structures representing the different windows clipboard
+ formats that can be provided for the \a mimeType from the \a mimeData.
+
+ All subclasses must reimplement this pure virtual function.
+*/
+
+/*!
+ \fn QVariant QWindowsMime::convertToMime(const QString &mimeType, IDataObject *pDataObj,
+ QVariant::Type preferredType) const
+
+ Returns a QVariant containing the converted data for \a mimeType from \a pDataObj.
+ If possible the QVariant should be of the \a preferredType to avoid needless conversions.
+
+ All subclasses must reimplement this pure virtual function.
+*/
+
+/*!
+\fn bool QWindowsMime::convertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData, STGMEDIUM * pmedium) const
+
+ Convert the \a mimeData to the format specified in \a formatetc.
+ The converted data should then be placed in \a pmedium structure.
+
+ Return true if the conversion was successful.
+
+ All subclasses must reimplement this pure virtual function.
+*/
+
+
+QWindowsMime *QWindowsMime::converterFromMime(const FORMATETC &formatetc, const QMimeData *mimeData)
+{
+ QList<QWindowsMime*> mimes = theMimeList()->windowsMimes();
+ for (int i=mimes.size()-1; i>=0; --i) {
+ if (mimes.at(i)->canConvertFromMime(formatetc, mimeData))
+ return mimes.at(i);
+ }
+ return 0;
+}
+
+QWindowsMime *QWindowsMime::converterToMime(const QString &mimeType, IDataObject *pDataObj)
+{
+ QList<QWindowsMime*> mimes = theMimeList()->windowsMimes();
+ for (int i=mimes.size()-1; i>=0; --i) {
+ if (mimes.at(i)->canConvertToMime(mimeType, pDataObj))
+ return mimes.at(i);
+ }
+ return 0;
+}
+
+QVector<FORMATETC> QWindowsMime::allFormatsForMime(const QMimeData *mimeData)
+{
+ QList<QWindowsMime*> mimes = theMimeList()->windowsMimes();
+ QVector<FORMATETC> formatics;
+ formatics.reserve(20);
+#ifndef QT_NO_DRAGANDDROP
+ QStringList formats = QInternalMimeData::formatsHelper(mimeData);
+ for (int f=0; f<formats.size(); ++f) {
+ for (int i=mimes.size()-1; i>=0; --i)
+ formatics += mimes.at(i)->formatsForMime(formats.at(f), mimeData);
+ }
+#else
+ Q_UNUSED(mimeData);
+#endif //QT_NO_DRAGANDDROP
+ return formatics;
+}
+
+QStringList QWindowsMime::allMimesForFormats(IDataObject *pDataObj)
+{
+ QList<QWindowsMime*> mimes = theMimeList()->windowsMimes();
+ QStringList formats;
+ LPENUMFORMATETC FAR fmtenum;
+ HRESULT hr = pDataObj->EnumFormatEtc(DATADIR_GET, &fmtenum);
+
+ if (hr == NOERROR) {
+ FORMATETC fmtetc;
+ while (S_OK == fmtenum->Next(1, &fmtetc, 0)) {
+#if defined(QMIME_DEBUG) && !defined(Q_OS_WINCE)
+ qDebug("QWindowsMime::allMimesForFormats()");
+ char buf[256] = {0};
+ GetClipboardFormatNameA(fmtetc.cfFormat, buf, 255);
+ qDebug("CF = %d : %s", fmtetc.cfFormat, buf);
+#endif
+ for (int i=mimes.size()-1; i>=0; --i) {
+ QString format = mimes.at(i)->mimeForFormat(fmtetc);
+ if (!format.isEmpty() && !formats.contains(format)) {
+ formats += format;
+ }
+ }
+ // as documented in MSDN to avoid possible memleak
+ if (fmtetc.ptd)
+ CoTaskMemFree(fmtetc.ptd);
+ }
+ fmtenum->Release();
+ }
+
+ return formats;
+}
+
+
+class QWindowsMimeText : public QWindowsMime
+{
+public:
+ bool canConvertToMime(const QString &mimeType, IDataObject *pDataObj) const;
+ QVariant convertToMime(const QString &mime, LPDATAOBJECT pDataObj, QVariant::Type preferredType) const;
+ QString mimeForFormat(const FORMATETC &formatetc) const;
+ bool canConvertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData) const;
+ bool convertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData, STGMEDIUM *pmedium) const;
+ QVector<FORMATETC> formatsForMime(const QString &mimeType, const QMimeData *mimeData) const;
+};
+
+bool QWindowsMimeText::canConvertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData) const
+{
+ int cf = getCf(formatetc);
+ return (cf == CF_UNICODETEXT || cf == CF_TEXT) && mimeData->hasText();
+}
+
+/*
+text/plain is defined as using CRLF, but so many programs don't,
+and programmers just look for '\n' in strings.
+Windows really needs CRLF, so we ensure it here.
+*/
+bool QWindowsMimeText::convertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData, STGMEDIUM *pmedium) const
+{
+ if (canConvertFromMime(formatetc, mimeData)) {
+ QByteArray data;
+ int cf = getCf(formatetc);
+ if (cf == CF_TEXT) {
+ data = mimeData->text().toLocal8Bit();
+ // Anticipate required space for CRLFs at 1/40
+ int maxsize=data.size()+data.size()/40+3;
+ QByteArray r(maxsize, '\0');
+ char* o = r.data();
+ const char* d = data.data();
+ const int s = data.size();
+ bool cr=false;
+ int j=0;
+ for (int i=0; i<s; i++) {
+ char c = d[i];
+ if (c=='\r')
+ cr=true;
+ else {
+ if (c=='\n') {
+ if (!cr)
+ o[j++]='\r';
+ }
+ cr=false;
+ }
+ o[j++]=c;
+ if (j+3 >= maxsize) {
+ maxsize += maxsize/4;
+ r.resize(maxsize);
+ o = r.data();
+ }
+ }
+ o[j]=0;
+ return setData(r, pmedium);
+ } else if (cf == CF_UNICODETEXT) {
+ QString str = mimeData->text();
+ const QChar *u = str.unicode();
+ QString res;
+ const int s = str.length();
+ int maxsize = s + s/40 + 3;
+ res.resize(maxsize);
+ int ri = 0;
+ bool cr = false;
+ for (int i=0; i < s; ++i) {
+ if (*u == QLatin1Char('\r'))
+ cr = true;
+ else {
+ if (*u == QLatin1Char('\n') && !cr)
+ res[ri++] = QLatin1Char('\r');
+ cr = false;
+ }
+ res[ri++] = *u;
+ if (ri+3 >= maxsize) {
+ maxsize += maxsize/4;
+ res.resize(maxsize);
+ }
+ ++u;
+ }
+ res.truncate(ri);
+ const int byteLength = res.length()*2;
+ QByteArray r(byteLength + 2, '\0');
+ memcpy(r.data(), res.unicode(), byteLength);
+ r[byteLength] = 0;
+ r[byteLength+1] = 0;
+ return setData(r, pmedium);
+ }
+ }
+ return false;
+}
+
+bool QWindowsMimeText::canConvertToMime(const QString &mimeType, IDataObject *pDataObj) const
+{
+ return mimeType.startsWith(QLatin1String("text/plain"))
+ && (canGetData(CF_UNICODETEXT, pDataObj)
+ || canGetData(CF_TEXT, pDataObj));
+}
+
+QString QWindowsMimeText::mimeForFormat(const FORMATETC &formatetc) const
+{
+ int cf = getCf(formatetc);
+ if (cf == CF_UNICODETEXT || cf == CF_TEXT)
+ return QLatin1String("text/plain");
+ return QString();
+}
+
+
+QVector<FORMATETC> QWindowsMimeText::formatsForMime(const QString &mimeType, const QMimeData *mimeData) const
+{
+ QVector<FORMATETC> formatics;
+ if (mimeType.startsWith(QLatin1String("text/plain")) && mimeData->hasText()) {
+ formatics += setCf(CF_UNICODETEXT);
+ formatics += setCf(CF_TEXT);
+ }
+ return formatics;
+}
+
+QVariant QWindowsMimeText::convertToMime(const QString &mime, LPDATAOBJECT pDataObj, QVariant::Type preferredType) const
+{
+ QVariant ret;
+
+ if (canConvertToMime(mime, pDataObj)) {
+ QString str;
+ QByteArray data = getData(CF_UNICODETEXT, pDataObj);
+ if (!data.isEmpty()) {
+ str = QString::fromUtf16((const unsigned short *)data.data());
+ str.replace(QLatin1String("\r\n"), QLatin1String("\n"));
+ } else {
+ data = getData(CF_TEXT, pDataObj);
+ if (!data.isEmpty()) {
+ const char* d = data.data();
+ const int s = qstrlen(d);
+ QByteArray r(data.size()+1, '\0');
+ char* o = r.data();
+ int j=0;
+ for (int i=0; i<s; i++) {
+ char c = d[i];
+ if (c!='\r')
+ o[j++]=c;
+ }
+ o[j]=0;
+ str = QString::fromLocal8Bit(r);
+ }
+ }
+ if (preferredType == QVariant::String)
+ ret = str;
+ else
+ ret = str.toUtf8();
+ }
+
+ return ret;
+}
+
+class QWindowsMimeURI : public QWindowsMime
+{
+public:
+ QWindowsMimeURI();
+ bool canConvertToMime(const QString &mimeType, IDataObject *pDataObj) const;
+ QVariant convertToMime(const QString &mime, LPDATAOBJECT pDataObj, QVariant::Type preferredType) const;
+ QString mimeForFormat(const FORMATETC &formatetc) const;
+ bool canConvertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData) const;
+ bool convertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData, STGMEDIUM *pmedium) const;
+ QVector<FORMATETC> formatsForMime(const QString &mimeType, const QMimeData *mimeData) const;
+private:
+ int CF_INETURL_W; // wide char version
+ int CF_INETURL;
+};
+
+QWindowsMimeURI::QWindowsMimeURI()
+{
+ CF_INETURL_W = QWindowsMime::registerMimeType(QLatin1String("UniformResourceLocatorW"));
+ CF_INETURL = QWindowsMime::registerMimeType(QLatin1String("UniformResourceLocator"));
+}
+
+bool QWindowsMimeURI::canConvertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData) const
+{
+ if (getCf(formatetc) == CF_HDROP) {
+ QList<QUrl> urls = mimeData->urls();
+ for (int i=0; i<urls.size(); i++) {
+ if (!urls.at(i).toLocalFile().isEmpty())
+ return true;
+ }
+ }
+ return (getCf(formatetc) == CF_INETURL_W || getCf(formatetc) == CF_INETURL) && mimeData->hasFormat(QLatin1String("text/uri-list"));
+}
+
+bool QWindowsMimeURI::convertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData, STGMEDIUM *pmedium) const
+{
+ if (canConvertFromMime(formatetc, mimeData)) {
+ if (getCf(formatetc) == CF_HDROP) {
+ QList<QUrl> urls = mimeData->urls();
+ QStringList fileNames;
+ int size = sizeof(DROPFILES)+2;
+ for (int i=0; i<urls.size(); i++) {
+ QString fn = QDir::toNativeSeparators(urls.at(i).toLocalFile());
+ if (!fn.isEmpty()) {
+ QT_WA({
+ size += sizeof(TCHAR)*(fn.length()+1);
+ } , {
+ size += fn.toLocal8Bit().length()+1;
+ });
+ fileNames.append(fn);
+ }
+ }
+
+ QByteArray result(size, '\0');
+ DROPFILES* d = (DROPFILES*)result.data();
+ d->pFiles = sizeof(DROPFILES);
+ GetCursorPos(&d->pt); // try
+ d->fNC = true;
+ char* files = ((char*)d) + d->pFiles;
+
+ QT_WA({
+ d->fWide = true;
+ TCHAR* f = (TCHAR*)files;
+ for (int i=0; i<fileNames.size(); i++) {
+ int l = fileNames.at(i).length();
+ memcpy(f, fileNames.at(i).utf16(), l*sizeof(TCHAR));
+ f += l;
+ *f++ = 0;
+ }
+ *f = 0;
+ } , {
+ d->fWide = false;
+ char* f = files;
+ for (int i=0; i<fileNames.size(); i++) {
+ QByteArray c = fileNames.at(i).toLocal8Bit();
+ if (!c.isEmpty()) {
+ int l = c.length();
+ memcpy(f, c.constData(), l);
+ f += l;
+ *f++ = 0;
+ }
+ }
+ *f = 0;
+ });
+ return setData(result, pmedium);
+ } else if (getCf(formatetc) == CF_INETURL_W) {
+ QList<QUrl> urls = mimeData->urls();
+ QByteArray result;
+ QString url = urls.at(0).toString();
+ result = QByteArray((const char *)url.utf16(), url.length() * 2);
+ result.append('\0');
+ result.append('\0');
+ return setData(result, pmedium);
+ } else if (getCf(formatetc) == CF_INETURL) {
+ QList<QUrl> urls = mimeData->urls();
+ QByteArray result = urls.at(0).toString().toLocal8Bit();
+ return setData(result, pmedium);
+ }
+ }
+
+ return false;
+}
+
+bool QWindowsMimeURI::canConvertToMime(const QString &mimeType, IDataObject *pDataObj) const
+{
+ return mimeType == QLatin1String("text/uri-list")
+ && (canGetData(CF_HDROP, pDataObj) || canGetData(CF_INETURL_W, pDataObj) || canGetData(CF_INETURL, pDataObj));
+}
+
+QString QWindowsMimeURI::mimeForFormat(const FORMATETC &formatetc) const
+{
+ QString format;
+ if (getCf(formatetc) == CF_HDROP || getCf(formatetc) == CF_INETURL_W || getCf(formatetc) == CF_INETURL)
+ format = QLatin1String("text/uri-list");
+ return format;
+}
+
+QVector<FORMATETC> QWindowsMimeURI::formatsForMime(const QString &mimeType, const QMimeData *mimeData) const
+{
+ QVector<FORMATETC> formatics;
+ if (mimeType == QLatin1String("text/uri-list")) {
+ if (canConvertFromMime(setCf(CF_HDROP), mimeData))
+ formatics += setCf(CF_HDROP);
+ if (canConvertFromMime(setCf(CF_INETURL_W), mimeData))
+ formatics += setCf(CF_INETURL_W);
+ if (canConvertFromMime(setCf(CF_INETURL), mimeData))
+ formatics += setCf(CF_INETURL);
+ }
+ return formatics;
+}
+
+QVariant QWindowsMimeURI::convertToMime(const QString &mimeType, LPDATAOBJECT pDataObj, QVariant::Type preferredType) const
+{
+ if (mimeType == QLatin1String("text/uri-list")) {
+ if (canGetData(CF_HDROP, pDataObj)) {
+ QByteArray texturi;
+ QList<QVariant> urls;
+
+ QByteArray data = getData(CF_HDROP, pDataObj);
+ if (data.isEmpty())
+ return QVariant();
+
+ LPDROPFILES hdrop = (LPDROPFILES)data.data();
+ if (hdrop->fWide) {
+ const ushort* filesw = (const ushort*)(data.data() + hdrop->pFiles);
+ int i=0;
+ while (filesw[i]) {
+ QString fileurl = QString::fromUtf16(filesw+i);
+ urls += QUrl::fromLocalFile(fileurl);
+ i += fileurl.length()+1;
+ }
+ } else {
+ const char* files = (const char*)data.data() + hdrop->pFiles;
+ int i=0;
+ while (files[i]) {
+ urls += QUrl::fromLocalFile(QString::fromLocal8Bit(files+i));
+ i += int(strlen(files+i))+1;
+ }
+ }
+
+ if (preferredType == QVariant::Url && urls.size() == 1)
+ return urls.at(0);
+ else if (!urls.isEmpty())
+ return urls;
+ } else if (canGetData(CF_INETURL_W, pDataObj)) {
+ QByteArray data = getData(CF_INETURL_W, pDataObj);
+ if (data.isEmpty())
+ return QVariant();
+ return QUrl(QString::fromUtf16((const unsigned short *)data.constData()));
+ } else if (canGetData(CF_INETURL, pDataObj)) {
+ QByteArray data = getData(CF_INETURL, pDataObj);
+ if (data.isEmpty())
+ return QVariant();
+ return QUrl(QString::fromLocal8Bit(data.constData()));
+ }
+ }
+ return QVariant();
+}
+
+class QWindowsMimeHtml : public QWindowsMime
+{
+public:
+ QWindowsMimeHtml();
+
+ // for converting from Qt
+ bool canConvertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData) const;
+ bool convertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData, STGMEDIUM * pmedium) const;
+ QVector<FORMATETC> formatsForMime(const QString &mimeType, const QMimeData *mimeData) const;
+
+ // for converting to Qt
+ bool canConvertToMime(const QString &mimeType, IDataObject *pDataObj) const;
+ QVariant convertToMime(const QString &mime, IDataObject *pDataObj, QVariant::Type preferredType) const;
+ QString mimeForFormat(const FORMATETC &formatetc) const;
+
+private:
+ int CF_HTML;
+};
+
+QWindowsMimeHtml::QWindowsMimeHtml()
+{
+ CF_HTML = QWindowsMime::registerMimeType(QLatin1String("HTML Format"));
+}
+
+QVector<FORMATETC> QWindowsMimeHtml::formatsForMime(const QString &mimeType, const QMimeData *mimeData) const
+{
+ QVector<FORMATETC> formatetcs;
+ if (mimeType == QLatin1String("text/html") && (!mimeData->html().isEmpty()))
+ formatetcs += setCf(CF_HTML);
+ return formatetcs;
+}
+
+QString QWindowsMimeHtml::mimeForFormat(const FORMATETC &formatetc) const
+{
+ if (getCf(formatetc) == CF_HTML)
+ return QLatin1String("text/html");
+ return QString();
+}
+
+bool QWindowsMimeHtml::canConvertToMime(const QString &mimeType, IDataObject *pDataObj) const
+{
+ return mimeType == QLatin1String("text/html") && canGetData(CF_HTML, pDataObj);
+}
+
+
+bool QWindowsMimeHtml::canConvertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData) const
+{
+ return getCf(formatetc) == CF_HTML && (!mimeData->html().isEmpty());
+}
+
+/*
+The windows HTML clipboard format is as follows (xxxxxxxxxx is a 10 integer number giving the positions
+in bytes). Charset used is mostly utf8, but can be different, ie. we have to look for the <meta> charset tag
+
+ Version: 1.0
+ StartHTML:xxxxxxxxxx
+ EndHTML:xxxxxxxxxx
+ StartFragment:xxxxxxxxxx
+ EndFragment:xxxxxxxxxx
+ ...html...
+
+*/
+QVariant QWindowsMimeHtml::convertToMime(const QString &mime, IDataObject *pDataObj, QVariant::Type preferredType) const
+{
+ Q_UNUSED(preferredType);
+ QVariant result;
+ if (canConvertToMime(mime, pDataObj)) {
+ QByteArray html = getData(CF_HTML, pDataObj);
+#ifdef QMIME_DEBUG
+ qDebug("QWindowsMimeHtml::convertToMime");
+ qDebug("raw :");
+ qDebug(html);
+#endif
+ int start = html.indexOf("StartFragment:");
+ int end = html.indexOf("EndFragment:");
+
+ if (start != -1) {
+ int startOffset = start + 14;
+ int i = startOffset;
+ while (html.at(i) != '\r' && html.at(i) != '\n')
+ ++i;
+ QByteArray bytecount = html.mid(startOffset, i - startOffset);
+ start = bytecount.toInt();
+ }
+
+ if (end != -1) {
+ int endOffset = end + 12;
+ int i = endOffset ;
+ while (html.at(i) != '\r' && html.at(i) != '\n')
+ ++i;
+ QByteArray bytecount = html.mid(endOffset , i - endOffset);
+ end = bytecount.toInt();
+ }
+
+ if (end > start && start > 0) {
+ html = "<!--StartFragment-->" + html.mid(start, end - start);
+ html += "<!--EndFragment-->";
+ html.replace("\r", "");
+ result = QString::fromUtf8(html);
+ }
+ }
+ return result;
+}
+
+bool QWindowsMimeHtml::convertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData, STGMEDIUM * pmedium) const
+{
+ if (canConvertFromMime(formatetc, mimeData)) {
+ QByteArray data = mimeData->html().toUtf8();
+ QByteArray result =
+ "Version:1.0\r\n" // 0-12
+ "StartHTML:0000000105\r\n" // 13-35
+ "EndHTML:0000000000\r\n" // 36-55
+ "StartFragment:0000000000\r\n" // 58-86
+ "EndFragment:0000000000\r\n\r\n"; // 87-105
+
+ if (data.indexOf("<!--StartFragment-->") == -1)
+ result += "<!--StartFragment-->";
+ result += data;
+ if (data.indexOf("<!--EndFragment-->") == -1)
+ result += "<!--EndFragment-->";
+
+ // set the correct number for EndHTML
+ QByteArray pos = QString::number(result.size()).toLatin1();
+ memcpy((char *)(result.data() + 53 - pos.length()), pos.constData(), pos.length());
+
+ // set correct numbers for StartFragment and EndFragment
+ pos = QString::number(result.indexOf("<!--StartFragment-->") + 20).toLatin1();
+ memcpy((char *)(result.data() + 79 - pos.length()), pos.constData(), pos.length());
+ pos = QString::number(result.indexOf("<!--EndFragment-->")).toLatin1();
+ memcpy((char *)(result.data() + 103 - pos.length()), pos.constData(), pos.length());
+
+ return setData(result, pmedium);
+ }
+ return false;
+}
+
+
+#ifndef QT_NO_IMAGEFORMAT_BMP
+class QWindowsMimeImage : public QWindowsMime
+{
+public:
+ QWindowsMimeImage();
+ // for converting from Qt
+ bool canConvertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData) const;
+ bool convertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData, STGMEDIUM * pmedium) const;
+ QVector<FORMATETC> formatsForMime(const QString &mimeType, const QMimeData *mimeData) const;
+
+ // for converting to Qt
+ bool canConvertToMime(const QString &mimeType, IDataObject *pDataObj) const;
+ QVariant convertToMime(const QString &mime, IDataObject *pDataObj, QVariant::Type preferredType) const;
+ QString mimeForFormat(const FORMATETC &formatetc) const;
+private:
+ bool hasOriginalDIBV5(IDataObject *pDataObj) const;
+ UINT CF_PNG;
+};
+
+QWindowsMimeImage::QWindowsMimeImage()
+{
+#ifdef Q_OS_WINCE
+ CF_PNG = RegisterClipboardFormat(reinterpret_cast<const wchar_t *> (QString::fromLatin1("PNG").utf16()));
+#else
+ CF_PNG = RegisterClipboardFormatA("PNG");
+#endif
+}
+
+QVector<FORMATETC> QWindowsMimeImage::formatsForMime(const QString &mimeType, const QMimeData *mimeData) const
+{
+ QVector<FORMATETC> formatetcs;
+ if (mimeData->hasImage() && mimeType == QLatin1String("application/x-qt-image")) {
+ //add DIBV5 if image has alpha channel
+ QImage image = qvariant_cast<QImage>(mimeData->imageData());
+ if (!image.isNull() && image.hasAlphaChannel())
+ formatetcs += setCf(CF_DIBV5);
+ formatetcs += setCf(CF_DIB);
+ }
+ return formatetcs;
+}
+
+QString QWindowsMimeImage::mimeForFormat(const FORMATETC &formatetc) const
+{
+ int cf = getCf(formatetc);
+ if (cf == CF_DIB || cf == CF_DIBV5 || cf == int(CF_PNG))
+ return QLatin1String("application/x-qt-image");
+ return QString();
+}
+
+bool QWindowsMimeImage::canConvertToMime(const QString &mimeType, IDataObject *pDataObj) const
+{
+ if ((mimeType == QLatin1String("application/x-qt-image")) &&
+ (canGetData(CF_DIB, pDataObj) || canGetData(CF_PNG, pDataObj)))
+ return true;
+ return false;
+}
+
+bool QWindowsMimeImage::canConvertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData) const
+{
+ int cf = getCf(formatetc);
+ if (mimeData->hasImage()) {
+ if (cf == CF_DIB)
+ return true;
+ else if (cf == CF_DIBV5) {
+ //support DIBV5 conversion only if the image has alpha channel
+ QImage image = qvariant_cast<QImage>(mimeData->imageData());
+ if (!image.isNull() && image.hasAlphaChannel())
+ return true;
+ }
+ }
+ return false;
+}
+
+bool QWindowsMimeImage::convertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData, STGMEDIUM * pmedium) const
+{
+ int cf = getCf(formatetc);
+ if ((cf == CF_DIB || cf == CF_DIBV5) && mimeData->hasImage()) {
+ QImage img = qvariant_cast<QImage>(mimeData->imageData());
+ if (img.isNull())
+ return false;
+ QByteArray ba;
+ QDataStream s(&ba, QIODevice::WriteOnly);
+ s.setByteOrder(QDataStream::LittleEndian);// Intel byte order ####
+ if (cf == CF_DIB) {
+ if (qt_write_dib(s, img))
+ return setData(ba, pmedium);
+ } else {
+ if (qt_write_dibv5(s, img))
+ return setData(ba, pmedium);
+ }
+ }
+ return false;
+}
+
+bool QWindowsMimeImage::hasOriginalDIBV5(IDataObject *pDataObj) const
+{
+ bool isSynthesized = true;
+ IEnumFORMATETC *pEnum =NULL;
+ HRESULT res = pDataObj->EnumFormatEtc(1, &pEnum);
+ if (res == S_OK && pEnum) {
+ FORMATETC fc;
+ while ((res = pEnum->Next(1, &fc, 0)) == S_OK) {
+ if (fc.ptd)
+ CoTaskMemFree(fc.ptd);
+ if (fc.cfFormat == CF_DIB)
+ break;
+ else if (fc.cfFormat == CF_DIBV5) {
+ isSynthesized = false;
+ break;
+ }
+ }
+ pEnum->Release();
+ }
+ return !isSynthesized;
+}
+
+QVariant QWindowsMimeImage::convertToMime(const QString &mimeType, IDataObject *pDataObj, QVariant::Type preferredType) const
+{
+ Q_UNUSED(preferredType);
+ QVariant result;
+ if (mimeType != QLatin1String("application/x-qt-image"))
+ return result;
+ //Try to convert from a format which has more data
+ //DIBV5, use only if its is not synthesized
+ if (canGetData(CF_DIBV5, pDataObj) && hasOriginalDIBV5(pDataObj)) {
+ QImage img;
+ QByteArray data = getData(CF_DIBV5, pDataObj);
+ QDataStream s(&data, QIODevice::ReadOnly);
+ s.setByteOrder(QDataStream::LittleEndian);
+ if (qt_read_dibv5(s, img)) { // #### supports only 32bit DIBV5
+ return img;
+ }
+ }
+ //PNG, MS Office place this (undocumented)
+ if (canGetData(CF_PNG, pDataObj)) {
+ QImage img;
+ QByteArray data = getData(CF_PNG, pDataObj);
+ if (img.loadFromData(data, "PNG")) {
+ return img;
+ }
+ }
+ //Fallback to DIB
+ if (canGetData(CF_DIB, pDataObj)) {
+ QImage img;
+ QByteArray data = getData(CF_DIB, pDataObj);
+ QDataStream s(&data, QIODevice::ReadOnly);
+ s.setByteOrder(QDataStream::LittleEndian);// Intel byte order ####
+ if (qt_read_dib(s, img)) { // ##### encaps "-14"
+ return img;
+ }
+ }
+ // Failed
+ return result;
+}
+#endif
+
+class QBuiltInMimes : public QWindowsMime
+{
+public:
+ QBuiltInMimes();
+
+ // for converting from Qt
+ bool canConvertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData) const;
+ bool convertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData, STGMEDIUM * pmedium) const;
+ QVector<FORMATETC> formatsForMime(const QString &mimeType, const QMimeData *mimeData) const;
+
+ // for converting to Qt
+ bool canConvertToMime(const QString &mimeType, IDataObject *pDataObj) const;
+ QVariant convertToMime(const QString &mime, IDataObject *pDataObj, QVariant::Type preferredType) const;
+ QString mimeForFormat(const FORMATETC &formatetc) const;
+
+private:
+ QMap<int, QString> outFormats;
+ QMap<int, QString> inFormats;
+};
+
+QBuiltInMimes::QBuiltInMimes()
+: QWindowsMime()
+{
+ outFormats.insert(QWindowsMime::registerMimeType(QLatin1String("application/x-color")), QLatin1String("application/x-color"));
+ inFormats.insert(QWindowsMime::registerMimeType(QLatin1String("application/x-color")), QLatin1String("application/x-color"));
+}
+
+bool QBuiltInMimes::canConvertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData) const
+{
+ // really check
+ return formatetc.tymed & TYMED_HGLOBAL
+ && outFormats.contains(formatetc.cfFormat)
+ && mimeData->formats().contains(outFormats.value(formatetc.cfFormat));
+}
+
+bool QBuiltInMimes::convertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData, STGMEDIUM * pmedium) const
+{
+ if (canConvertFromMime(formatetc, mimeData)) {
+ QByteArray data;
+ if (outFormats.value(getCf(formatetc)) == QLatin1String("text/html")) {
+ // text/html is in wide chars on windows (compatible with mozillia)
+ QString html = mimeData->html();
+ // same code as in the text converter up above
+ const QChar *u = html.unicode();
+ QString res;
+ const int s = html.length();
+ int maxsize = s + s/40 + 3;
+ res.resize(maxsize);
+ int ri = 0;
+ bool cr = false;
+ for (int i=0; i < s; ++i) {
+ if (*u == QLatin1Char('\r'))
+ cr = true;
+ else {
+ if (*u == QLatin1Char('\n') && !cr)
+ res[ri++] = QLatin1Char('\r');
+ cr = false;
+ }
+ res[ri++] = *u;
+ if (ri+3 >= maxsize) {
+ maxsize += maxsize/4;
+ res.resize(maxsize);
+ }
+ ++u;
+ }
+ res.truncate(ri);
+ const int byteLength = res.length()*2;
+ QByteArray r(byteLength + 2, '\0');
+ memcpy(r.data(), res.unicode(), byteLength);
+ r[byteLength] = 0;
+ r[byteLength+1] = 0;
+ data = r;
+ } else {
+#ifndef QT_NO_DRAGANDDROP
+ data = QInternalMimeData::renderDataHelper(outFormats.value(getCf(formatetc)), mimeData);
+#endif //QT_NO_DRAGANDDROP
+ }
+ return setData(data, pmedium);
+ }
+ return false;
+}
+
+QVector<FORMATETC> QBuiltInMimes::formatsForMime(const QString &mimeType, const QMimeData *mimeData) const
+{
+ QVector<FORMATETC> formatetcs;
+ if (!outFormats.keys(mimeType).isEmpty() && mimeData->formats().contains(mimeType))
+ formatetcs += setCf(outFormats.key(mimeType));
+ return formatetcs;
+}
+
+bool QBuiltInMimes::canConvertToMime(const QString &mimeType, IDataObject *pDataObj) const
+{
+ return (!inFormats.keys(mimeType).isEmpty())
+ && canGetData(inFormats.key(mimeType), pDataObj);
+}
+
+QVariant QBuiltInMimes::convertToMime(const QString &mimeType, IDataObject *pDataObj, QVariant::Type preferredType) const
+{
+ QVariant val;
+ if (canConvertToMime(mimeType, pDataObj)) {
+ QByteArray data = getData(inFormats.key(mimeType), pDataObj);
+ if (!data.isEmpty()) {
+#ifdef QMIME_DEBUG
+ qDebug("QBuiltInMimes::convertToMime()");
+#endif
+ if (mimeType == QLatin1String("text/html") && preferredType == QVariant::String) {
+ // text/html is in wide chars on windows (compatible with mozillia)
+ val = QString::fromUtf16((const unsigned short *)data.data());
+ } else {
+ val = data; // it should be enough to return the data and let QMimeData do the rest.
+ }
+ }
+ }
+ return val;
+}
+
+QString QBuiltInMimes::mimeForFormat(const FORMATETC &formatetc) const
+{
+ return inFormats.value(getCf(formatetc));
+}
+
+
+class QLastResortMimes : public QWindowsMime
+{
+public:
+
+ QLastResortMimes();
+ // for converting from Qt
+ bool canConvertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData) const;
+ bool convertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData, STGMEDIUM * pmedium) const;
+ QVector<FORMATETC> formatsForMime(const QString &mimeType, const QMimeData *mimeData) const;
+
+ // for converting to Qt
+ bool canConvertToMime(const QString &mimeType, IDataObject *pDataObj) const;
+ QVariant convertToMime(const QString &mime, IDataObject *pDataObj, QVariant::Type preferredType) const;
+ QString mimeForFormat(const FORMATETC &formatetc) const;
+
+private:
+ QMap<int, QString> formats;
+ static QStringList ianaTypes;
+ static QStringList excludeList;
+};
+
+QStringList QLastResortMimes::ianaTypes;
+QStringList QLastResortMimes::excludeList;
+
+QLastResortMimes::QLastResortMimes()
+{
+ //MIME Media-Types
+ if (!ianaTypes.size()) {
+ ianaTypes.append(QLatin1String("application/"));
+ ianaTypes.append(QLatin1String("audio/"));
+ ianaTypes.append(QLatin1String("example/"));
+ ianaTypes.append(QLatin1String("image/"));
+ ianaTypes.append(QLatin1String("message/"));
+ ianaTypes.append(QLatin1String("model/"));
+ ianaTypes.append(QLatin1String("multipart/"));
+ ianaTypes.append(QLatin1String("text/"));
+ ianaTypes.append(QLatin1String("video/"));
+ }
+ //Types handled by other classes
+ if (!excludeList.size()) {
+ excludeList.append(QLatin1String("HTML Format"));
+ excludeList.append(QLatin1String("UniformResourceLocator"));
+ excludeList.append(QLatin1String("text/html"));
+ excludeList.append(QLatin1String("text/plain"));
+ excludeList.append(QLatin1String("text/uri-list"));
+ excludeList.append(QLatin1String("application/x-qt-image"));
+ excludeList.append(QLatin1String("application/x-color"));
+ }
+}
+
+bool QLastResortMimes::canConvertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData) const
+{
+ // really check
+#ifndef QT_NO_DRAGANDDROP
+ return formatetc.tymed & TYMED_HGLOBAL
+ && (formats.contains(formatetc.cfFormat)
+ && QInternalMimeData::hasFormatHelper(formats.value(formatetc.cfFormat), mimeData));
+#else
+ Q_UNUSED(mimeData);
+ Q_UNUSED(formatetc);
+ return formatetc.tymed & TYMED_HGLOBAL
+ && formats.contains(formatetc.cfFormat);
+#endif //QT_NO_DRAGANDDROP
+}
+
+bool QLastResortMimes::convertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData, STGMEDIUM * pmedium) const
+{
+#ifndef QT_NO_DRAGANDDROP
+ return canConvertFromMime(formatetc, mimeData)
+ && setData(QInternalMimeData::renderDataHelper(formats.value(getCf(formatetc)), mimeData), pmedium);
+#else
+ Q_UNUSED(mimeData);
+ Q_UNUSED(formatetc);
+ Q_UNUSED(pmedium);
+ return false;
+#endif //QT_NO_DRAGANDDROP
+}
+
+QVector<FORMATETC> QLastResortMimes::formatsForMime(const QString &mimeType, const QMimeData * /*mimeData*/) const
+{
+ QVector<FORMATETC> formatetcs;
+ if (!formats.keys(mimeType).isEmpty()) {
+ formatetcs += setCf(formats.key(mimeType));
+ } else if (!excludeList.contains(mimeType, Qt::CaseInsensitive)){
+ // register any other available formats
+ int cf = QWindowsMime::registerMimeType(mimeType);
+ QLastResortMimes *that = const_cast<QLastResortMimes *>(this);
+ that->formats.insert(cf, mimeType);
+ formatetcs += setCf(cf);
+ }
+ return formatetcs;
+}
+static const char *x_qt_windows_mime = "application/x-qt-windows-mime;value=\"";
+
+bool isCustomMimeType(const QString &mimeType)
+{
+ return mimeType.startsWith(QLatin1String(x_qt_windows_mime), Qt::CaseInsensitive);
+}
+
+QString customMimeType(const QString &mimeType)
+{
+ int len = QString(QLatin1String(x_qt_windows_mime)).length();
+ int n = mimeType.lastIndexOf(QLatin1Char('\"'))-len;
+ return mimeType.mid(len, n);
+}
+
+bool QLastResortMimes::canConvertToMime(const QString &mimeType, IDataObject *pDataObj) const
+{
+ if (isCustomMimeType(mimeType)) {
+ QString clipFormat = customMimeType(mimeType);
+#ifdef Q_OS_WINCE
+ int cf = RegisterClipboardFormat(reinterpret_cast<const wchar_t *> (clipFormat.utf16()));
+#else
+ int cf = RegisterClipboardFormatA(clipFormat.toLocal8Bit());
+#endif
+ return canGetData(cf, pDataObj);
+ } else if (formats.keys(mimeType).isEmpty()) {
+ // if it is not in there then register it an see if we can get it
+ int cf = QWindowsMime::registerMimeType(mimeType);
+ return canGetData(cf, pDataObj);
+ } else {
+ return canGetData(formats.key(mimeType), pDataObj);
+ }
+ return false;
+}
+
+QVariant QLastResortMimes::convertToMime(const QString &mimeType, IDataObject *pDataObj, QVariant::Type preferredType) const
+{
+ Q_UNUSED(preferredType);
+ QVariant val;
+ if (canConvertToMime(mimeType, pDataObj)) {
+ QByteArray data;
+ if (isCustomMimeType(mimeType)) {
+ QString clipFormat = customMimeType(mimeType);
+#ifdef Q_OS_WINCE
+ int cf = RegisterClipboardFormat(reinterpret_cast<const wchar_t *> (clipFormat.utf16()));
+#else
+ int cf = RegisterClipboardFormatA(clipFormat.toLocal8Bit());
+#endif
+ data = getData(cf, pDataObj);
+ } else if (formats.keys(mimeType).isEmpty()) {
+ int cf = QWindowsMime::registerMimeType(mimeType);
+ data = getData(cf, pDataObj);
+ } else {
+ data = getData(formats.key(mimeType), pDataObj);
+ }
+ if (!data.isEmpty())
+ val = data; // it should be enough to return the data and let QMimeData do the rest.
+ }
+ return val;
+}
+
+QString QLastResortMimes::mimeForFormat(const FORMATETC &formatetc) const
+{
+ QString format = formats.value(getCf(formatetc));
+ if (format.isEmpty()) {
+ QByteArray ba;
+ QString clipFormat;
+ int len;
+ QT_WA({
+ ba.resize(256*2);
+ len = GetClipboardFormatNameW(getCf(formatetc), (TCHAR*)ba.data(), 255);
+ if (len)
+ clipFormat = QString::fromUtf16((ushort *)ba.data(), len);
+ } , {
+ ba.resize(256);
+ len = GetClipboardFormatNameA(getCf(formatetc), ba.data(), 255);
+ if (len)
+ clipFormat = QString::fromLocal8Bit(ba.data(), len);
+ });
+ if (len) {
+#ifndef QT_NO_DRAGANDDROP
+ if (QInternalMimeData::canReadData(clipFormat))
+ format = clipFormat;
+ else if((formatetc.cfFormat >= 0xC000)){
+ //create the mime as custom. not registered.
+ if (!excludeList.contains(clipFormat, Qt::CaseInsensitive)) {
+ //check if this is a mime type
+ bool ianaType = false;
+ int sz = ianaTypes.size();
+ for (int i = 0; i < sz; i++) {
+ if (clipFormat.startsWith(ianaTypes[i], Qt::CaseInsensitive)) {
+ ianaType = true;
+ break;
+ }
+ }
+ if (!ianaType)
+ format = QLatin1String(x_qt_windows_mime) + clipFormat + QLatin1Char('\"');
+ else
+ format = clipFormat;
+ }
+ }
+#endif //QT_NO_DRAGANDDROP
+ }
+ }
+ return format;
+}
+
+QWindowsMimeList::QWindowsMimeList()
+ : initialized(false)
+{
+}
+
+QWindowsMimeList::~QWindowsMimeList()
+{
+ while (mimes.size())
+ delete mimes.first();
+}
+
+
+void QWindowsMimeList::init()
+{
+ if (!initialized) {
+ initialized = true;
+#ifndef QT_NO_IMAGEFORMAT_BMP
+ new QWindowsMimeImage;
+#endif
+ new QLastResortMimes;
+ new QWindowsMimeText;
+ new QWindowsMimeURI;
+
+ new QWindowsMimeHtml;
+ new QBuiltInMimes;
+ }
+}
+
+void QWindowsMimeList::addWindowsMime(QWindowsMime * mime)
+{
+ init();
+ mimes.append(mime);
+}
+
+void QWindowsMimeList::removeWindowsMime(QWindowsMime * mime)
+{
+ init();
+ mimes.removeAll(mime);
+}
+
+QList<QWindowsMime*> QWindowsMimeList::windowsMimes()
+{
+ init();
+ return mimes;
+}
+
+#ifndef QT_NO_IMAGEFORMAT_BMP
+static bool qt_write_dibv5(QDataStream &s, QImage image)
+{
+ QIODevice* d = s.device();
+ if (!d->isWritable())
+ return false;
+
+ //depth will be always 32
+ int bpl_bmp = image.width()*4;
+
+ BMP_BITMAPV5HEADER bi ={0};
+ bi.bV5Size = sizeof(BMP_BITMAPV5HEADER);
+ bi.bV5Width = image.width();
+ bi.bV5Height = image.height();
+ bi.bV5Planes = 1;
+ bi.bV5BitCount = 32;
+ bi.bV5Compression = BI_BITFIELDS;
+ bi.bV5SizeImage = bpl_bmp*image.height();
+ bi.bV5XPelsPerMeter = 0;
+ bi.bV5YPelsPerMeter = 0;
+ bi.bV5ClrUsed = 0;
+ bi.bV5ClrImportant = 0;
+ bi.bV5BlueMask = 0x000000ff;
+ bi.bV5GreenMask = 0x0000ff00;
+ bi.bV5RedMask = 0x00ff0000;
+ bi.bV5AlphaMask = 0xff000000;
+ bi.bV5CSType = BMP_LCS_sRGB; //LCS_sRGB
+ bi.bV5Intent = BMP_LCS_GM_IMAGES; //LCS_GM_IMAGES
+
+ d->write(reinterpret_cast<const char*>(&bi), bi.bV5Size);
+ if (s.status() != QDataStream::Ok)
+ return false;
+
+ DWORD colorSpace[3] = {0x00ff0000,0x0000ff00,0x000000ff};
+ d->write(reinterpret_cast<const char*>(colorSpace), sizeof(colorSpace));
+ if (s.status() != QDataStream::Ok)
+ return false;
+
+ if (image.format() != QImage::Format_ARGB32)
+ image = image.convertToFormat(QImage::Format_ARGB32);
+
+ uchar *buf = new uchar[bpl_bmp];
+ uchar *b;
+
+ memset(buf, 0, bpl_bmp);
+ for (int y=image.height()-1; y>=0; y--) {
+ // write the image bits
+ QRgb *p = (QRgb *)image.scanLine(y);
+ QRgb *end = p + image.width();
+ b = buf;
+ while (p < end) {
+ int alpha = qAlpha(*p);
+ if (alpha) {
+ *b++ = qBlue(*p);
+ *b++ = qGreen(*p);
+ *b++ = qRed(*p);
+ } else {
+ //white for fully transparent pixels.
+ *b++ = 0xff;
+ *b++ = 0xff;
+ *b++ = 0xff;
+ }
+ *b++ = alpha;
+ p++;
+ }
+ d->write((char*)buf, bpl_bmp);
+ if (s.status() != QDataStream::Ok) {
+ delete[] buf;
+ return false;
+ }
+ }
+ delete[] buf;
+ return true;
+}
+
+static int calc_shift(int mask)
+{
+ int result = 0;
+ while (!(mask & 1)) {
+ result++;
+ mask >>= 1;
+ }
+ return result;
+}
+
+//Supports only 32 bit DIBV5
+static bool qt_read_dibv5(QDataStream &s, QImage &image)
+{
+ BMP_BITMAPV5HEADER bi;
+ QIODevice* d = s.device();
+ if (d->atEnd())
+ return false;
+
+ d->read((char *)&bi, sizeof(bi)); // read BITMAPV5HEADER header
+ if (s.status() != QDataStream::Ok)
+ return false;
+
+ int nbits = bi.bV5BitCount;
+ int comp = bi.bV5Compression;
+ if (nbits != 32 || bi.bV5Planes != 1 || comp != BMP_BITFIELDS)
+ return false; //Unsupported DIBV5 format
+
+ int w = bi.bV5Width, h = bi.bV5Height;
+ int red_mask = bi.bV5RedMask;
+ int green_mask = bi.bV5GreenMask;
+ int blue_mask = bi.bV5BlueMask;
+ int alpha_mask = bi.bV5AlphaMask;
+ int red_shift = 0;
+ int green_shift = 0;
+ int blue_shift = 0;
+ int alpha_shift = 0;
+ QImage::Format format = QImage::Format_ARGB32;
+
+ if (bi.bV5Height < 0)
+ h = -h; // support images with negative height
+ if (image.size() != QSize(w, h) || image.format() != format) {
+ image = QImage(w, h, format);
+ if (image.isNull()) // could not create image
+ return false;
+ }
+ image.setDotsPerMeterX(bi.bV5XPelsPerMeter);
+ image.setDotsPerMeterY(bi.bV5YPelsPerMeter);
+ // read color table
+ DWORD colorSpace[3];
+ if (d->read((char *)colorSpace, sizeof(colorSpace)) != sizeof(colorSpace))
+ return false;
+
+ red_shift = calc_shift(red_mask);
+ green_shift = calc_shift(green_mask);
+ blue_shift = calc_shift(blue_mask);
+ if (alpha_mask) {
+ alpha_shift = calc_shift(alpha_mask);
+ }
+
+ int bpl = image.bytesPerLine();
+ uchar *data = image.bits();
+ register QRgb *p;
+ QRgb *end;
+ uchar *buf24 = new uchar[bpl];
+ int bpl24 = ((w*nbits+31)/32)*4;
+ uchar *b;
+ unsigned int c;
+
+ while (--h >= 0) {
+ p = (QRgb *)(data + h*bpl);
+ end = p + w;
+ if (d->read((char *)buf24,bpl24) != bpl24)
+ break;
+ b = buf24;
+ while (p < end) {
+ c = *b | (*(b+1))<<8 | (*(b+2))<<16 | (*(b+3))<<24;
+ *p++ = qRgba(((c & red_mask) >> red_shift) ,
+ ((c & green_mask) >> green_shift),
+ ((c & blue_mask) >> blue_shift),
+ ((c & alpha_mask) >> alpha_shift));
+ b += 4;
+ }
+ }
+ delete[] buf24;
+
+ if (bi.bV5Height < 0) {
+ // Flip the image
+ uchar *buf = new uchar[bpl];
+ h = -bi.bV5Height;
+ for (int y = 0; y < h/2; ++y) {
+ memcpy(buf, data + y*bpl, bpl);
+ memcpy(data + y*bpl, data + (h-y-1)*bpl, bpl);
+ memcpy(data + (h-y-1)*bpl, buf, bpl);
+ }
+ delete [] buf;
+ }
+
+ return true;
+}
+
+#endif
+
+QT_END_NAMESPACE
diff --git a/src/gui/kernel/qmotifdnd_x11.cpp b/src/gui/kernel/qmotifdnd_x11.cpp
new file mode 100644
index 0000000000..5c6790419a
--- /dev/null
+++ b/src/gui/kernel/qmotifdnd_x11.cpp
@@ -0,0 +1,1028 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/* The following copyright notice pertains to the code as contributed
+to Trolltech, not to Trolltech's modifications. It is replicated
+in doc/dnd.doc, where the documentation system can see it. */
+
+/* Copyright 1996 Daniel Dardailler.
+
+ Permission to use, copy, modify, distribute, and sell this software
+ for any purpose is hereby granted without fee, 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 Daniel Dardailler not be used in advertising or
+ publicity pertaining to distribution of the software without specific,
+ written prior permission. Daniel Dardailler makes no representations
+ about the suitability of this software for any purpose. It is
+ provided "as is" without express or implied warranty.
+
+ Modifications Copyright 1999 Matt Koss, under the same license as
+ above.
+************************************************************/
+
+/***********************************************************/
+/* Motif Drag&Drop Dynamic Protocol messaging API code */
+/* Only requires Xlib layer - not MT safe */
+/* Author: Daniel Dardailler, daniel@x.org */
+/* Adapted by: Matt Koss, koss@napri.sk */
+/* Further adaptions by: Nokia Corporation and/or its subsidiary(-ies) */
+/***********************************************************/
+
+#include "qplatformdefs.h"
+
+#include "qapplication.h"
+
+#ifndef QT_NO_DRAGANDDROP
+
+#include "qdebug.h"
+#include "qtextcodec.h"
+#include "qwidget.h"
+#include "qevent.h"
+#include "qt_x11_p.h"
+#include "qx11info_x11.h"
+#include "qiodevice.h"
+#include "qdnd_p.h"
+
+#include <stdlib.h>
+
+QT_BEGIN_NAMESPACE
+
+static Window sourceWindow = XNone;
+static QWidget *dropWidget = 0;
+static Qt::DropAction lastAcceptedAction = Qt::IgnoreAction;
+
+static Atom Dnd_selection = 0;
+static Time Dnd_selection_time;
+
+static Atom * src_targets ;
+static ushort num_src_targets ;
+
+// Motif definitions
+#define DndVersion 1
+#define DndRevision 0
+#define DndIncludeVersion (DndVersion * 10 + DndRevision)
+
+/* The following values are used in the DndData structure */
+
+/* protocol style */
+#define DND_DRAG_NONE 0
+#define DND_DRAG_DROP_ONLY 1
+#define DND_DRAG_DYNAMIC 5
+
+/* message type */
+#define DND_TOP_LEVEL_ENTER 0
+#define DND_TOP_LEVEL_LEAVE 1
+#define DND_DRAG_MOTION 2
+#define DND_DROP_SITE_ENTER 3
+#define DND_DROP_SITE_LEAVE 4
+#define DND_DROP_START 5
+#define DND_OPERATION_CHANGED 8
+
+/* operation(s) */
+#define DND_NOOP 0L
+#define DND_MOVE (1L << 0)
+#define DND_COPY (1L << 1)
+#define DND_LINK (1L << 2)
+
+static Qt::DropActions DndOperationsToQtDropActions(uchar op)
+{
+ Qt::DropActions actions = Qt::IgnoreAction;
+ if (op | DND_MOVE)
+ actions |= Qt::MoveAction;
+ if (op | DND_COPY)
+ actions |= Qt::CopyAction;
+ if (op | DND_LINK)
+ actions |= Qt::LinkAction;
+ return actions;
+}
+
+static uchar QtDropActionToDndOperation(Qt::DropAction action)
+{
+ switch (action & Qt::ActionMask) {
+ case Qt::CopyAction:
+ default:
+ return DND_COPY;
+ case Qt::MoveAction:
+ return DND_MOVE;
+ case Qt::LinkAction:
+ return DND_LINK;
+ }
+}
+
+
+/* status */
+#define DND_NO_DROP_SITE 1
+#define DND_INVALID_DROP_SITE 2
+#define DND_VALID_DROP_SITE 3
+
+/* completion */
+#define DND_DROP 0
+#define DND_DROP_HELP 1
+#define DND_DROP_CANCEL 2
+
+#define BYTE unsigned char
+#define CARD32 unsigned int
+#define CARD16 unsigned short
+#define INT16 signed short
+
+/* Client side structure used in the API */
+typedef struct {
+ unsigned char reason; /* message type: DND_TOP_LEVEL_ENTER, etc */
+ Time time ;
+ unsigned char operation;
+ unsigned char operations;
+ unsigned char status;
+ unsigned char completion;
+ short x ;
+ short y ;
+ Window src_window ;
+ Atom property ;
+} DndData ;
+
+
+typedef struct _DndSrcProp {
+ BYTE byte_order ;
+ BYTE protocol_version ;
+ CARD16 target_index ;
+ CARD32 selection ;
+} DndSrcProp ;
+
+typedef struct _DndReceiverProp {
+ BYTE byte_order ;
+ BYTE protocol_version ;
+ BYTE protocol_style ;
+ BYTE pad1;
+ CARD32 proxy_window;
+ CARD16 num_drop_sites ;
+ CARD16 pad2;
+ CARD32 total_size;
+} DndReceiverProp ;
+
+/* need to use some union hack since window and property are in
+ different order depending on the message ... */
+typedef struct _DndTop {
+ CARD32 src_window;
+ CARD32 property;
+} DndTop ;
+
+typedef struct _DndPot {
+ INT16 x;
+ INT16 y;
+ CARD32 property;
+ CARD32 src_window;
+} DndPot ;
+
+typedef struct _DndMessage {
+ BYTE reason;
+ BYTE byte_order;
+ CARD16 flags;
+ CARD32 time;
+ union {
+ DndTop top ;
+ DndPot pot ;
+ } data ;
+} DndMessage ;
+
+typedef struct {
+ BYTE byte_order;
+ BYTE protocol_version;
+ CARD16 num_target_lists;
+ CARD32 data_size;
+ /* then come series of CARD16,CARD32,CARD32,CARD32... */
+} DndTargets;
+
+
+/* protocol version */
+#define DND_PROTOCOL_VERSION 0
+
+
+#define DND_EVENT_TYPE_MASK ((BYTE)0x80)
+#define DND_EVENT_TYPE_SHIFT 7
+#define DND_CLEAR_EVENT_TYPE ((BYTE)0x7F)
+
+/* message_type is data[0] of the client_message
+ this return 1 (receiver bit up) or 0 (initiator) */
+#define DND_GET_EVENT_TYPE(message_type) \
+((char) (((message_type) & DND_EVENT_TYPE_MASK) >> DND_EVENT_TYPE_SHIFT))
+
+/* event_type can be 0 (initiator) or 1 (receiver) */
+#define DND_SET_EVENT_TYPE(event_type) \
+(((BYTE)(event_type) << DND_EVENT_TYPE_SHIFT) & DND_EVENT_TYPE_MASK)
+
+
+#define DND_OPERATION_MASK ((CARD16) 0x000F)
+#define DND_OPERATION_SHIFT 0
+#define DND_STATUS_MASK ((CARD16) 0x00F0)
+#define DND_STATUS_SHIFT 4
+#define DND_OPERATIONS_MASK ((CARD16) 0x0F00)
+#define DND_OPERATIONS_SHIFT 8
+#define DND_COMPLETION_MASK ((CARD16) 0xF000)
+#define DND_COMPLETION_SHIFT 12
+
+#define DND_GET_OPERATION(flags) \
+((unsigned char) \
+(((flags) & DND_OPERATION_MASK) >> DND_OPERATION_SHIFT))
+
+#define DND_SET_OPERATION(operation) \
+(((CARD16)(operation) << DND_OPERATION_SHIFT)\
+& DND_OPERATION_MASK)
+
+#define DND_GET_STATUS(flags) \
+((unsigned char) \
+(((flags) & DND_STATUS_MASK) >> DND_STATUS_SHIFT))
+
+#define DND_SET_STATUS(status) \
+(((CARD16)(status) << DND_STATUS_SHIFT)\
+& DND_STATUS_MASK)
+
+#define DND_GET_OPERATIONS(flags) \
+((unsigned char) \
+(((flags) & DND_OPERATIONS_MASK) >> DND_OPERATIONS_SHIFT))
+
+#define DND_SET_OPERATIONS(operation) \
+(((CARD16)(operation) << DND_OPERATIONS_SHIFT)\
+& DND_OPERATIONS_MASK)
+
+#define DND_GET_COMPLETION(flags) \
+((unsigned char) \
+(((flags) & DND_COMPLETION_MASK) >> DND_COMPLETION_SHIFT))
+
+#define DND_SET_COMPLETION(completion) \
+(((CARD16)(completion) << DND_COMPLETION_SHIFT)\
+& DND_COMPLETION_MASK)
+
+
+#define SWAP4BYTES(l) {\
+struct { unsigned t :32;} bit32;\
+char n, *tp = (char *) &bit32;\
+bit32.t = l;\
+n = tp[0]; tp[0] = tp[3]; tp[3] = n;\
+n = tp[1]; tp[1] = tp[2]; tp[2] = n;\
+l = bit32.t;\
+}
+
+#define SWAP2BYTES(s) {\
+struct { unsigned t :16; } bit16;\
+char n, *tp = (char *) &bit16;\
+bit16.t = s;\
+n = tp[0]; tp[0] = tp[1]; tp[1] = n;\
+s = bit16.t;\
+}
+
+
+/** Private extern functions */
+
+static unsigned char DndByteOrder ();
+
+
+/***** Targets/Index stuff */
+
+typedef struct {
+ int num_targets;
+ Atom *targets;
+} DndTargetsTableEntryRec, * DndTargetsTableEntry;
+
+typedef struct {
+ int num_entries;
+ DndTargetsTableEntry entries;
+} DndTargetsTableRec, * DndTargetsTable;
+
+
+static ushort _DndIndexToTargets(Display * display,
+ int index,
+ Atom ** targets);
+
+extern void qt_x11_intern_atom(const char *, Atom *);
+
+/////////////////////////////////////////////////////////////////
+
+static unsigned char DndByteOrder ()
+{
+ static unsigned char byte_order = 0;
+
+ if (!byte_order) {
+ unsigned int endian = 1;
+ byte_order = (*((char *)&endian))?'l':'B';
+ }
+ return byte_order ;
+}
+
+
+
+static void DndReadSourceProperty(Display * dpy,
+ Window window, Atom dnd_selection,
+ Atom ** targets, unsigned short * num_targets)
+{
+ unsigned char *retval = 0;
+ Atom type ;
+ int format ;
+ unsigned long bytesafter, lengthRtn;
+
+ if ((XGetWindowProperty (dpy, window, dnd_selection, 0L, 100000L,
+ False, ATOM(_MOTIF_DRAG_INITIATOR_INFO), &type,
+ &format, &lengthRtn, &bytesafter,
+ &retval) != Success)
+ || (type == XNone)) {
+ *num_targets = 0;
+ return ;
+ }
+
+ DndSrcProp * src_prop = (DndSrcProp *)retval;
+
+ if (src_prop->byte_order != DndByteOrder()) {
+ SWAP2BYTES(src_prop->target_index);
+ SWAP4BYTES(src_prop->selection);
+ }
+
+ *num_targets = _DndIndexToTargets(dpy, src_prop->target_index, targets);
+
+ XFree((char*)src_prop);
+}
+
+
+/* Position the _MOTIF_DRAG_RECEIVER_INFO property on the dropsite window.
+ Called by the receiver of the drop to indicate the
+ supported protocol style : dynamic, drop_only or none */
+static void DndWriteReceiverProperty(Display * dpy, Window window,
+ unsigned char protocol_style)
+{
+ DndReceiverProp receiver_prop ;
+
+ receiver_prop.byte_order = DndByteOrder() ;
+ receiver_prop.protocol_version = DND_PROTOCOL_VERSION;
+ receiver_prop.protocol_style = protocol_style ;
+ receiver_prop.proxy_window = XNone ;
+ receiver_prop.num_drop_sites = 0 ;
+ receiver_prop.total_size = sizeof(DndReceiverProp);
+
+ /* write the buffer to the property */
+ XChangeProperty (dpy, window, ATOM(_MOTIF_DRAG_RECEIVER_INFO), ATOM(_MOTIF_DRAG_RECEIVER_INFO),
+ 8, PropModeReplace,
+ (unsigned char *)&receiver_prop,
+ sizeof(DndReceiverProp));
+}
+
+
+/* protocol style equiv (preregister stuff really) */
+#define DND_DRAG_DROP_ONLY_EQUIV 3
+#define DND_DRAG_DYNAMIC_EQUIV1 2
+#define DND_DRAG_DYNAMIC_EQUIV2 4
+
+
+/* Produce a client message to be sent by the caller */
+static void DndFillClientMessage(Display * dpy, Window window,
+ XClientMessageEvent *cm,
+ DndData * dnd_data,
+ char receiver)
+{
+ DndMessage * dnd_message = (DndMessage*)&cm->data.b[0] ;
+
+ cm->display = dpy;
+ cm->type = ClientMessage;
+ cm->serial = LastKnownRequestProcessed(dpy);
+ cm->send_event = True;
+ cm->window = window;
+ cm->format = 8;
+ cm->message_type = ATOM(_MOTIF_DRAG_AND_DROP_MESSAGE);
+
+ dnd_message->reason = dnd_data->reason | DND_SET_EVENT_TYPE(receiver);
+
+ dnd_message->byte_order = DndByteOrder();
+
+ /* we're filling in flags with more stuff that necessary,
+ depending on the reason, but it doesn't matter */
+ dnd_message->flags = 0 ;
+ dnd_message->flags |= DND_SET_STATUS(dnd_data->status) ;
+ dnd_message->flags |= DND_SET_OPERATION(dnd_data->operation) ;
+ dnd_message->flags |= DND_SET_OPERATIONS(dnd_data->operations) ;
+ dnd_message->flags |= DND_SET_COMPLETION(dnd_data->completion) ;
+
+ dnd_message->time = dnd_data->time ;
+
+ switch(dnd_data->reason) {
+ case DND_DROP_SITE_LEAVE: break ;
+ case DND_TOP_LEVEL_ENTER:
+ case DND_TOP_LEVEL_LEAVE:
+ dnd_message->data.top.src_window = dnd_data->src_window ;
+ dnd_message->data.top.property = dnd_data->property ;
+ break ; /* cannot fall through since the byte layout is different in
+ both set of messages, see top and pot union stuff */
+
+ case DND_DRAG_MOTION:
+ case DND_OPERATION_CHANGED:
+ case DND_DROP_SITE_ENTER:
+ case DND_DROP_START:
+ dnd_message->data.pot.x = dnd_data->x ; /* mouse position */
+ dnd_message->data.pot.y = dnd_data->y ;
+ dnd_message->data.pot.src_window = dnd_data->src_window ;
+ dnd_message->data.pot.property = dnd_data->property ;
+ break ;
+ default:
+ break ;
+ }
+
+}
+
+static Bool DndParseClientMessage(XClientMessageEvent *cm, DndData * dnd_data,
+ char * receiver)
+{
+ DndMessage * dnd_message = (DndMessage*)&cm->data.b[0] ;
+
+ if (cm->message_type != ATOM(_MOTIF_DRAG_AND_DROP_MESSAGE)) {
+ return False ;
+ }
+
+ if (dnd_message->byte_order != DndByteOrder()) {
+ SWAP2BYTES(dnd_message->flags);
+ SWAP4BYTES(dnd_message->time);
+ } /* do the rest in the switch */
+
+ dnd_data->reason = dnd_message->reason ;
+ if (DND_GET_EVENT_TYPE(dnd_data->reason))
+ *receiver = 1 ;
+ else
+ *receiver = 0 ;
+ dnd_data->reason &= DND_CLEAR_EVENT_TYPE ;
+
+ dnd_data->time = dnd_message->time ;
+
+ /* we're reading in more stuff that necessary. but who cares */
+ dnd_data->status = DND_GET_STATUS(dnd_message->flags) ;
+ dnd_data->operation = DND_GET_OPERATION(dnd_message->flags) ;
+ dnd_data->operations = DND_GET_OPERATIONS(dnd_message->flags) ;
+ dnd_data->completion = DND_GET_COMPLETION(dnd_message->flags) ;
+
+ switch(dnd_data->reason) {
+ case DND_TOP_LEVEL_ENTER:
+ case DND_TOP_LEVEL_LEAVE:
+ if (dnd_message->byte_order != DndByteOrder()) {
+ SWAP4BYTES(dnd_message->data.top.src_window);
+ SWAP4BYTES(dnd_message->data.top.property);
+ }
+ dnd_data->src_window = dnd_message->data.top.src_window ;
+ dnd_data->property = dnd_message->data.top.property ;
+ break ; /* cannot fall through, see above comment in write msg */
+
+ case DND_DRAG_MOTION:
+ case DND_OPERATION_CHANGED:
+ case DND_DROP_SITE_ENTER:
+ case DND_DROP_START:
+ if (dnd_message->byte_order != DndByteOrder()) {
+ SWAP2BYTES(dnd_message->data.pot.x);
+ SWAP2BYTES(dnd_message->data.pot.y);
+ SWAP4BYTES(dnd_message->data.pot.property);
+ SWAP4BYTES(dnd_message->data.pot.src_window);
+ }
+ dnd_data->x = dnd_message->data.pot.x ;
+ dnd_data->y = dnd_message->data.pot.y ;
+ dnd_data->property = dnd_message->data.pot.property ;
+ dnd_data->src_window = dnd_message->data.pot.src_window ;
+ break ;
+
+ case DND_DROP_SITE_LEAVE:
+ break;
+ default:
+ break ;
+ }
+
+ return True ;
+}
+
+
+static Window MotifWindow(Display *display)
+{
+ Atom type;
+ int format;
+ unsigned long size;
+ unsigned long bytes_after;
+ unsigned char *property = 0;
+ Window motif_window ;
+
+ /* this version does no caching, so it's slow: round trip each time */
+
+ if ((XGetWindowProperty (display, RootWindow(display, 0),
+ ATOM(_MOTIF_DRAG_WINDOW),
+ 0L, 100000L, False, AnyPropertyType,
+ &type, &format, &size, &bytes_after,
+ &property) == Success) &&
+ (type != XNone)) {
+ motif_window = *(Window *)property;
+ } else {
+ XSetWindowAttributes sAttributes;
+
+ /* really, this should be done on a separate connection,
+ with XSetCloseDownMode (RetainPermanent), so that
+ others don't have to recreate it; hopefully, some real
+ Motif application will be around to do it */
+
+ sAttributes.override_redirect = True;
+ sAttributes.event_mask = PropertyChangeMask;
+ motif_window = XCreateWindow (display,
+ RootWindow (display, 0),
+ -170, -560, 1, 1, 0, 0,
+ InputOnly, CopyFromParent,
+ (CWOverrideRedirect |CWEventMask),
+ &sAttributes);
+ XMapWindow (display, motif_window);
+ }
+
+ if (property) {
+ XFree ((char *)property);
+ }
+
+ return (motif_window);
+}
+
+
+static DndTargetsTable TargetsTable(Display *display)
+{
+ Atom type;
+ int format;
+ unsigned long size;
+ unsigned long bytes_after;
+ Window motif_window = MotifWindow(display) ;
+ unsigned char *retval;
+ DndTargetsTable targets_table ;
+ int i,j ;
+ char * target_data ;
+
+ /* this version does no caching, so it's slow: round trip each time */
+ /* ideally, register for property notify on this target_list
+ atom and update when necessary only */
+
+ if ((XGetWindowProperty (display, motif_window,
+ ATOM(_MOTIF_DRAG_TARGETS), 0L, 100000L,
+ False, ATOM(_MOTIF_DRAG_TARGETS),
+ &type, &format, &size, &bytes_after,
+ &retval) != Success) ||
+ type == XNone) {
+ qWarning("QMotifDND: Cannot get property on Motif window");
+ return 0;
+ }
+
+ DndTargets * target_prop = (DndTargets *)retval;
+
+ if (target_prop->protocol_version != DND_PROTOCOL_VERSION) {
+ qWarning("QMotifDND: Protocol mismatch");
+ }
+
+ if (target_prop->byte_order != DndByteOrder()) {
+ /* need to swap num_target_lists and size */
+ SWAP2BYTES(target_prop->num_target_lists);
+ SWAP4BYTES(target_prop->data_size);
+ }
+
+ /* now parse DndTarget prop data in a TargetsTable */
+
+ targets_table = (DndTargetsTable)malloc(sizeof(DndTargetsTableRec));
+ targets_table->num_entries = target_prop->num_target_lists ;
+ targets_table->entries = (DndTargetsTableEntry)
+ malloc(sizeof(DndTargetsTableEntryRec) * target_prop->num_target_lists);
+
+ target_data = (char*)target_prop + sizeof(*target_prop) ;
+
+ for (i = 0 ; i < targets_table->num_entries; i++) {
+ CARD16 num_targets ;
+ CARD32 atom ;
+
+ memcpy(&num_targets, target_data, 2);
+ target_data += 2;
+
+ /* potential swap needed here */
+ if (target_prop->byte_order != DndByteOrder())
+ SWAP2BYTES(num_targets);
+
+ targets_table->entries[i].num_targets = num_targets ;
+ targets_table->entries[i].targets = (Atom *)
+ malloc(sizeof(Atom) * targets_table->entries[i].num_targets);
+
+
+ for (j = 0; j < num_targets; j++) {
+ memcpy(&atom, target_data, 4);
+ target_data += 4;
+
+ /* another potential swap needed here */
+ if (target_prop->byte_order != DndByteOrder())
+ SWAP4BYTES(atom);
+
+ targets_table->entries[i].targets[j] = (Atom) atom ;
+ }
+ }
+
+ if (target_prop) {
+ XFree((char *)target_prop);
+ }
+
+ return targets_table ;
+}
+
+
+static ushort _DndIndexToTargets(Display * display,
+ int index,
+ Atom ** targets)
+{
+ DndTargetsTable targets_table;
+ int i ;
+
+ /* again, slow: no caching here, alloc/free each time */
+
+ if (!(targets_table = TargetsTable (display)) ||
+ (index >= targets_table->num_entries)) {
+ if (targets_table)
+ XFree((char*)targets_table);
+ return 0;
+ }
+
+ /* transfer the correct target list index */
+ *targets = (Atom*)malloc(sizeof(Atom)*targets_table->
+ entries[index].num_targets);
+ memcpy((char*)*targets,
+ (char*)targets_table->entries[index].targets,
+ sizeof(Atom)*targets_table->entries[index].num_targets);
+
+ /* free the target table and its guts */
+ for (i=0 ; i < targets_table->num_entries; i++)
+ XFree((char*)targets_table->entries[i].targets);
+
+ int tmp = targets_table->entries[index].num_targets;
+ XFree((char*)targets_table);
+
+ return tmp; // targets_table->entries[index].num_targets;
+}
+
+
+QByteArray QX11Data::motifdndFormat(int n)
+{
+ if (!motifdnd_active)
+ return 0; // should not happen
+
+ if (n >= num_src_targets)
+ return 0;
+
+ Atom target = src_targets[n];
+
+ if (target == XA_STRING)
+ return "text/plain;charset=ISO-8859-1";
+ if (target == ATOM(UTF8_STRING))
+ return "text/plain;charset=UTF-8";
+ if (target == ATOM(COMPOUND_TEXT))
+ return QByteArray("text/plain;charset=") + QTextCodec::codecForLocale()->name();
+ if (target == ATOM(TEXT))
+ return "text/plain";
+
+ return ("x-motif-dnd/" + X11->xdndAtomToString(target));
+}
+
+
+QVariant QX11Data::motifdndObtainData(const char *mimeType)
+{
+ QByteArray result;
+
+ if (Dnd_selection == 0 || !dropWidget)
+ return result;
+
+ // try to convert the selection to the requested property
+ // qDebug("trying to convert to '%s'", mimeType);
+
+ int n=0;
+ QByteArray f;
+ do {
+ f = motifdndFormat(n);
+ if (f.isEmpty())
+ return result;
+ n++;
+ } while(qstricmp(mimeType, f.data()));
+
+ Atom conversion_type = XNone;
+ if (f == "text/plain;charset=ISO-8859-1") {
+ conversion_type = XA_STRING;
+ } else if (f == "text/plain;charset=UTF-8") {
+ conversion_type = ATOM(UTF8_STRING);
+ } else if (f == (QByteArray("text/plain;charset=") + QTextCodec::codecForLocale()->name())) {
+ conversion_type = ATOM(COMPOUND_TEXT);
+ } else if (f == "text/plain") {
+ conversion_type = ATOM(TEXT);
+ } else if (f.startsWith("x-motif-dnd/")) {
+ // strip off the "x-motif-dnd/" prefix
+ conversion_type = X11->xdndStringToAtom(f.remove(0, 12));
+ }
+
+ if (XGetSelectionOwner(X11->display, Dnd_selection) == XNone) {
+ return result; // should never happen?
+ }
+
+ QWidget* tw = dropWidget;
+ if ((dropWidget->windowType() == Qt::Desktop)) {
+ tw = new QWidget;
+ }
+
+ // convert selection to the appropriate type
+ XConvertSelection (X11->display, Dnd_selection, conversion_type,
+ Dnd_selection, tw->internalWinId(), Dnd_selection_time);
+
+ XFlush(X11->display);
+
+ XEvent xevent;
+ bool got=X11->clipboardWaitForEvent(tw->internalWinId(), SelectionNotify, &xevent, 5000);
+ if (got) {
+ Atom type;
+
+ if (X11->clipboardReadProperty(tw->internalWinId(), Dnd_selection, true, &result, 0, &type, 0, false)) {
+ }
+ }
+
+ // we have to convert selection in order to indicate success to the initiator
+ XConvertSelection (X11->display, Dnd_selection, ATOM(XmTRANSFER_SUCCESS),
+ Dnd_selection, tw->internalWinId(), Dnd_selection_time);
+
+ // wait again for SelectionNotify event
+ X11->clipboardWaitForEvent(tw->internalWinId(), SelectionNotify, &xevent, 5000);
+
+ if ((dropWidget->windowType() == Qt::Desktop)) {
+ delete tw;
+ }
+
+ return result;
+}
+
+
+void QX11Data::motifdndEnable(QWidget *widget, bool)
+{
+ DndWriteReceiverProperty(display, widget->internalWinId(), DND_DRAG_DYNAMIC);
+}
+
+
+void QX11Data::motifdndHandle(QWidget *widget, const XEvent * xe, bool /* passive */)
+{
+ XEvent event = *xe;
+ XClientMessageEvent cm ;
+ DndData dnd_data ;
+ char receiver ;
+
+ if (!(DndParseClientMessage ((XClientMessageEvent*)&event,
+ &dnd_data, &receiver))) {
+ return;
+ }
+
+ switch (dnd_data.reason) {
+
+ case DND_DRAG_MOTION:
+ {
+ QPoint p = widget->mapFromGlobal(QPoint(dnd_data.x, dnd_data.y));
+ QWidget *c = widget->childAt(p);
+
+ if (!c || !c->acceptDrops()) {
+ // not over a drop site
+ if (dropWidget) {
+ QDragLeaveEvent dragLeaveEvent;
+ QApplication::sendEvent(dropWidget, &dragLeaveEvent);
+
+ dropWidget = 0;
+ lastAcceptedAction = Qt::IgnoreAction;
+
+ dnd_data.reason = DND_DROP_SITE_LEAVE;
+ dnd_data.time = X11->time;
+ DndFillClientMessage (event.xclient.display, sourceWindow, &cm, &dnd_data, receiver);
+ XSendEvent(event.xbutton.display, sourceWindow, False, 0, (XEvent *)&cm) ;
+ } else {
+ dnd_data.reason = DND_DRAG_MOTION;
+ dnd_data.status = DND_NO_DROP_SITE;
+ dnd_data.time = X11->time;
+ dnd_data.operation = DND_NOOP;
+ dnd_data.operations = DND_NOOP;
+ DndFillClientMessage (event.xclient.display, sourceWindow, &cm, &dnd_data, receiver);
+ XSendEvent(event.xbutton.display, sourceWindow, False, 0, (XEvent *)&cm) ;
+ }
+ } else {
+ Q_ASSERT(c != 0);
+ p = c->mapFrom(widget, p);
+
+ if (dropWidget != c) {
+ if (dropWidget) {
+ QDragLeaveEvent le;
+ QApplication::sendEvent(dropWidget, &le);
+ }
+
+ dropWidget = c;
+ lastAcceptedAction = Qt::IgnoreAction;
+
+ const Qt::DropActions possibleActions =
+ DndOperationsToQtDropActions(dnd_data.operations);
+ QDragEnterEvent de(p, possibleActions, QDragManager::self()->dropData,
+ QApplication::mouseButtons(), QApplication::keyboardModifiers());
+ QApplication::sendEvent(dropWidget, &de);
+
+ dnd_data.reason = DND_DROP_SITE_ENTER;
+ dnd_data.time = X11->time;
+ if (de.isAccepted()) {
+ lastAcceptedAction = de.dropAction();
+
+ dnd_data.status = DND_VALID_DROP_SITE;
+ dnd_data.operation = QtDropActionToDndOperation(lastAcceptedAction);
+ } else {
+ dnd_data.status = DND_INVALID_DROP_SITE;
+ dnd_data.operation = DND_NOOP;
+ dnd_data.operations = DND_NOOP;
+ }
+ DndFillClientMessage (event.xclient.display, sourceWindow, &cm, &dnd_data, receiver);
+ XSendEvent(event.xbutton.display, sourceWindow, False, 0, (XEvent *)&cm);
+ } else {
+ const Qt::DropActions possibleActions =
+ DndOperationsToQtDropActions(dnd_data.operations);
+ QDragMoveEvent me(p, possibleActions, QDragManager::self()->dropData,
+ QApplication::mouseButtons(), QApplication::keyboardModifiers());
+ if (lastAcceptedAction != Qt::IgnoreAction) {
+ me.setDropAction(lastAcceptedAction);
+ me.accept();
+ }
+ QApplication::sendEvent(dropWidget, &me);
+
+ dnd_data.reason = DND_DRAG_MOTION;
+ dnd_data.time = X11->time;
+
+ if (me.isAccepted()) {
+ lastAcceptedAction = me.dropAction();
+
+ dnd_data.status = DND_VALID_DROP_SITE;
+ dnd_data.operation = QtDropActionToDndOperation(lastAcceptedAction);
+ } else {
+ dnd_data.status = DND_INVALID_DROP_SITE;
+ dnd_data.operation = DND_NOOP;
+ dnd_data.operations = DND_NOOP;
+ }
+
+ DndFillClientMessage (event.xclient.display, sourceWindow, &cm, &dnd_data, receiver);
+ XSendEvent(event.xbutton.display, sourceWindow, False, 0, (XEvent *)&cm);
+ }
+ }
+
+ break;
+ }
+
+ case DND_TOP_LEVEL_ENTER:
+ {
+ /* get the size of our drop site for later use */
+
+ motifdnd_active = true;
+ sourceWindow = dnd_data.src_window;
+
+ /* no answer needed, just read source property */
+ DndReadSourceProperty (event.xclient.display,
+ sourceWindow,
+ dnd_data.property,
+ &src_targets, &num_src_targets);
+
+ break;
+ }
+
+ case DND_TOP_LEVEL_LEAVE:
+ {
+ XEvent nextEvent;
+ if (XCheckTypedWindowEvent(X11->display, widget->winId(), ClientMessage, &nextEvent)) {
+ // we just want to check, not eat (should use XPeekIfEvent)
+ XPutBackEvent(X11->display, &nextEvent);
+
+ if (DndParseClientMessage (&nextEvent.xclient, &dnd_data, &receiver)
+ && dnd_data.reason == DND_DROP_START) {
+ // expecting drop next, keeping DnD alive
+ break;
+ }
+ }
+
+ // not expecting drop, need to send drag leave events and such here
+ if (dropWidget) {
+ QDragLeaveEvent le;
+ QApplication::sendEvent(dropWidget, &le);
+ }
+
+ sourceWindow = XNone;
+ dropWidget = 0;
+ lastAcceptedAction = Qt::IgnoreAction;
+
+ motifdnd_active = false;
+
+ break;
+ }
+
+ case DND_OPERATION_CHANGED:
+ // ### need to echo
+ break;
+
+ case DND_DROP_START:
+ {
+ Q_ASSERT(motifdnd_active);
+ Q_ASSERT(sourceWindow == dnd_data.src_window);
+
+ if (!dropWidget || lastAcceptedAction == Qt::IgnoreAction) {
+ // echo DROP_START
+ dnd_data.reason = DND_DROP_START;
+ dnd_data.status = DND_NO_DROP_SITE;
+ dnd_data.operation = DND_NOOP;
+ dnd_data.operations = DND_NOOP;
+ DndFillClientMessage (event.xclient.display, sourceWindow, &cm, &dnd_data, 0);
+ XSendEvent(event.xbutton.display, sourceWindow, False, 0, (XEvent *)&cm);
+
+ // we have to convert selection in order to indicate failure to the initiator
+ XConvertSelection (X11->display, dnd_data.property, ATOM(XmTRANSFER_FAILURE),
+ dnd_data.property, dnd_data.src_window, dnd_data.time);
+
+ if (dropWidget) {
+ QDragLeaveEvent e;
+ QApplication::sendEvent(dropWidget, &e);
+ }
+
+ motifdnd_active = false;
+ sourceWindow = XNone;
+ dropWidget = 0;
+ lastAcceptedAction = Qt::IgnoreAction;
+
+ return;
+ }
+
+ // store selection and its time
+ Dnd_selection = dnd_data.property;
+ Dnd_selection_time = dnd_data.time;
+
+ QPoint p(dnd_data.x, dnd_data.y);
+ QDropEvent de(dropWidget->mapFromGlobal(p), Qt::CopyAction, QDragManager::self()->dropData,
+ QApplication::mouseButtons(), QApplication::keyboardModifiers());
+ if (lastAcceptedAction != Qt::IgnoreAction) {
+ de.setDropAction(lastAcceptedAction);
+ de.accept();
+ }
+ QApplication::sendEvent(dropWidget, &de);
+
+ // reset
+ Dnd_selection = XNone;
+ Dnd_selection_time = 0;
+
+ // echo DROP_START depending on the result of the dropEvent
+ if (de.isAccepted()) {
+ dnd_data.reason = DND_DROP_START;
+ dnd_data.status = DND_VALID_DROP_SITE;
+ dnd_data.operation = QtDropActionToDndOperation(de.dropAction());
+ } else {
+ dnd_data.reason = DND_DROP_START;
+ dnd_data.status = DND_NO_DROP_SITE;
+ dnd_data.operation = DND_NOOP;
+ dnd_data.operations = DND_NOOP;
+ }
+ DndFillClientMessage (event.xclient.display, sourceWindow, &cm, &dnd_data, 0);
+ XSendEvent(event.xbutton.display, sourceWindow, False, 0, (XEvent *)&cm);
+
+ sourceWindow = XNone;
+ dropWidget = 0;
+ lastAcceptedAction = Qt::IgnoreAction;
+
+ motifdnd_active = false;
+
+ break;
+ }
+
+ default:
+ break;
+ } // end of switch (dnd_data.reason)
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_DRAGANDDROP
diff --git a/src/gui/kernel/qnsframeview_mac_p.h b/src/gui/kernel/qnsframeview_mac_p.h
new file mode 100644
index 0000000000..14e8763eab
--- /dev/null
+++ b/src/gui/kernel/qnsframeview_mac_p.h
@@ -0,0 +1,154 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of qapplication_*.cpp, qwidget*.cpp, qcolor_x11.cpp, qfiledialog.cpp
+// and many other. This header file may change from version to version
+// without notice, or even be removed.
+//
+// We mean it.
+//
+
+// Private AppKit class (dumped from classdump).
+
+#import <Cocoa/Cocoa.h>
+
+@interface NSFrameView : NSView
+{
+ unsigned int styleMask;
+ NSString *_title;
+ NSCell *titleCell;
+ NSButton *closeButton;
+ NSButton *zoomButton;
+ NSButton *minimizeButton;
+ char resizeByIncrement;
+ char frameNeedsDisplay;
+ unsigned char tabViewCount;
+ NSSize resizeParameter;
+ int shadowState;
+}
+
++ (void)initialize;
++ (void)initTitleCell:fp8 styleMask:(unsigned int)fp12;
++ (struct _NSRect)frameRectForContentRect:(struct _NSRect)fp8 styleMask:(unsigned int)fp24;
++ (struct _NSRect)contentRectForFrameRect:(struct _NSRect)fp8 styleMask:(unsigned int)fp24;
++ (struct _NSSize)minFrameSizeForMinContentSize:(struct _NSSize)fp8 styleMask:(unsigned int)fp16;
++ (struct _NSSize)minContentSizeForMinFrameSize:(struct _NSSize)fp8 styleMask:(unsigned int)fp16;
++ (float)minFrameWidthWithTitle:fp8 styleMask:(unsigned int)fp12;
++ (unsigned int)_validateStyleMask:(unsigned int)fp8;
+- initWithFrame:(struct _NSRect)fp8 styleMask:(unsigned int)fp24 owner:fp28;
+- initWithFrame:(struct _NSRect)fp8;
+- (void)dealloc;
+- (void)shapeWindow;
+- (void)tileAndSetWindowShape:(char)fp8;
+- (void)tile;
+- (void)drawRect:(struct _NSRect)fp8;
+- (void)_drawFrameRects:(struct _NSRect)fp8;
+- (void)drawFrame:(struct _NSRect)fp8;
+- (void)drawThemeContentFill:(struct _NSRect)fp8 inView:fp24;
+- (void)drawWindowBackgroundRect:(struct _NSRect)fp8;
+- (void)drawWindowBackgroundRegion:(void *)fp8;
+- (float)contentAlpha;
+- (void)_windowChangedKeyState;
+- (void)_updateButtonState;
+- (char)_isSheet;
+- (char)_isUtility;
+- (void)setShadowState:(int)fp8;
+- (int)shadowState;
+- (char)_canHaveToolbar;
+- (char)_toolbarIsInTransition;
+- (char)_toolbarIsShown;
+- (char)_toolbarIsHidden;
+- (void)_showToolbarWithAnimation:(char)fp8;
+- (void)_hideToolbarWithAnimation:(char)fp8;
+- (float)_distanceFromToolbarBaseToTitlebar;
+- (int)_shadowType;
+- (unsigned int)_shadowFlags;
+- (void)_setShadowParameters;
+- (void)_drawFrameShadowAndFlushContext:fp8;
+- (void)setUpGState;
+- (void)adjustHalftonePhase;
+- (void)systemColorsDidChange:fp8;
+- frameColor;
+- contentFill;
+- (void)tabViewAdded;
+- (void)tabViewRemoved;
+- title;
+- (void)setTitle:fp8;
+- titleCell;
+- (void)initTitleCell:fp8;
+- (void)setResizeIncrements:(struct _NSSize)fp8;
+- (struct _NSSize)resizeIncrements;
+- (void)setAspectRatio:(struct _NSSize)fp8;
+- (struct _NSSize)aspectRatio;
+- (unsigned int)styleMask;
+- representedFilename;
+- (void)setRepresentedFilename:fp8;
+- (void)setDocumentEdited:(char)fp8;
+- (void)_setFrameNeedsDisplay:(char)fp8;
+- (char)frameNeedsDisplay;
+- titleFont;
+- (struct _NSRect)_maxTitlebarTitleRect;
+- (struct _NSRect)titlebarRect;
+- (void)_setUtilityWindow:(char)fp8;
+- (void)_setNonactivatingPanel:(char)fp8;
+- (void)setIsClosable:(char)fp8;
+- (void)setIsResizable:(char)fp8;
+- closeButton;
+- minimizeButton;
+- zoomButton;
+- (struct _NSSize)miniaturizedSize;
+- (void)_clearDragMargins;
+- (void)_resetDragMargins;
+- (void)setTitle:fp8 andDefeatWrap:(char)fp12;
+- (struct _NSRect)frameRectForContentRect:(struct _NSRect)fp8 styleMask:(unsigned int)fp24;
+- (struct _NSRect)contentRectForFrameRect:(struct _NSRect)fp8 styleMask:(unsigned int)fp24;
+- (struct _NSSize)minFrameSizeForMinContentSize:(struct _NSSize)fp8 styleMask:(unsigned int)fp16;
+- (struct _NSRect)dragRectForFrameRect:(struct _NSRect)fp8;
+- (struct _NSRect)contentRect;
+- (struct _NSSize)minFrameSize;
+- (void)_recursiveDisplayRectIfNeededIgnoringOpacity:(struct _NSRect)fp8 isVisibleRect:(char)fp24 rectIsVisibleRectForView:fp28 topView:(char)fp32;
+
+@end
diff --git a/src/gui/kernel/qnsthemeframe_mac_p.h b/src/gui/kernel/qnsthemeframe_mac_p.h
new file mode 100644
index 0000000000..785a6a862f
--- /dev/null
+++ b/src/gui/kernel/qnsthemeframe_mac_p.h
@@ -0,0 +1,246 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of qapplication_*.cpp, qwidget*.cpp, qcolor_x11.cpp, qfiledialog.cpp
+// and many other. This header file may change from version to version
+// without notice, or even be removed.
+//
+// We mean it.
+//
+
+// Private AppKit class (dumped from classdump).
+
+#import <Cocoa/Cocoa.h>
+#import "qnstitledframe_mac_p.h"
+
+@interface NSThemeFrame : NSTitledFrame
+{
+ NSButton *toolbarButton;
+ int toolbarVisibleStatus;
+ NSImage *showToolbarTransitionImage;
+ NSSize showToolbarPreWindowSize;
+ NSButton *modeButton;
+ int leftGroupTrackingTagNum;
+ int rightGroupTrackingTagNum;
+ char mouseInsideLeftGroup;
+ char mouseInsideRightGroup;
+ int widgetState;
+ NSString *displayName;
+}
+
++ (void)initialize;
++ (float)_windowBorderThickness:(unsigned int)fp8;
++ (float)_minXWindowBorderWidth:(unsigned int)fp8;
++ (float)_maxXWindowBorderWidth:(unsigned int)fp8;
++ (float)_minYWindowBorderHeight:(unsigned int)fp8;
++ (float)_windowTitlebarButtonSpacingWidth:(unsigned int)fp8;
++ (float)_windowFileButtonSpacingWidth:(unsigned int)fp8;
++ (float)_minXTitlebarWidgetInset:(unsigned int)fp8;
++ (float)_maxXTitlebarWidgetInset:(unsigned int)fp8;
++ (float)minFrameWidthWithTitle:fp8 styleMask:(unsigned int)fp12;
++ (float)_windowSideTitlebarTitleMinWidth:(unsigned int)fp8;
++ (float)_windowTitlebarTitleMinHeight:(unsigned int)fp8;
++ (float)_sideTitlebarWidth:(unsigned int)fp8;
++ (float)_titlebarHeight:(unsigned int)fp8;
++ (float)_resizeHeight:(unsigned int)fp8;
++ (char)_resizeFromEdge;
++ (struct _NSSize)sizeOfTitlebarButtons:(unsigned int)fp8;
++ (float)_contentToFrameMinXWidth:(unsigned int)fp8;
++ (float)_contentToFrameMaxXWidth:(unsigned int)fp8;
++ (float)_contentToFrameMinYHeight:(unsigned int)fp8;
++ (float)_contentToFrameMaxYHeight:(unsigned int)fp8;
++ (unsigned int)_validateStyleMask:(unsigned int)fp8;
+- (struct _NSSize)_topCornerSize;
+- (struct _NSSize)_bottomCornerSize;
+- (void *)_createWindowOpaqueShape;
+- (void)shapeWindow;
+- (void)_recursiveDisplayRectIfNeededIgnoringOpacity:(NSRect)fp8 isVisibleRect:(char)fp24 rectIsVisibleRectForView:fp28 topView:(char)fp32;
+- (void *)_regionForOpaqueDescendants:(NSRect)fp8 forMove:(char)fp24;
+- (void)_drawFrameInterior:(NSRect *)fp8 clip:(NSRect)fp12;
+- (void)_setTextShadow:(char)fp8;
+- (void)_drawTitleBar:(NSRect)fp8;
+- (void)_drawResizeIndicators:(NSRect)fp8;
+- (void)_drawFrameRects:(NSRect)fp8;
+- (void)drawFrame:(NSRect)fp8;
+- contentFill;
+- (void)viewDidEndLiveResize;
+- (float)contentAlpha;
+- (void)setThemeFrameWidgetState:(int)fp8;
+- (char)constrainResizeEdge:(int *)fp8 withDelta:(struct _NSSize)fp12 elapsedTime:(float)fp20;
+- (void)addFileButton:fp8;
+- (void)_updateButtons;
+- (void)_updateButtonState;
+- newCloseButton;
+- newZoomButton;
+- newMiniaturizeButton;
+- newToolbarButton;
+- newFileButton;
+- (void)_resetTitleBarButtons;
+- (void)setDocumentEdited:(char)fp8;
+- toolbarButton;
+- modeButton;
+- initWithFrame:(NSRect)fp8 styleMask:(unsigned int)fp24 owner:fp28;
+- (void)dealloc;
+- (void)setFrameSize:(struct _NSSize)fp8;
+- (char)_canHaveToolbar;
+- (char)_toolbarIsInTransition;
+- (char)_toolbarIsShown;
+- (char)_toolbarIsHidden;
+- _toolbarView;
+- _toolbar;
+- (float)_distanceFromToolbarBaseToTitlebar;
+- (unsigned int)_shadowFlags;
+- (NSRect)frameRectForContentRect:(NSRect)fp8 styleMask:(unsigned int)fp24;
+- (NSRect)contentRectForFrameRect:(NSRect)fp8 styleMask:(unsigned int)fp24;
+- (struct _NSSize)minFrameSizeForMinContentSize:(struct _NSSize)fp8 styleMask:(unsigned int)fp16;
+- (NSRect)contentRect;
+- (NSRect)_contentRectExcludingToolbar;
+- (NSRect)_contentRectIncludingToolbarAtHome;
+- (void)_setToolbarShowHideResizeWeightingOptimizationOn:(char)fp8;
+- (char)_usingToolbarShowHideWeightingOptimization;
+- (void)handleSetFrameCommonRedisplay;
+- (void)_startLiveResizeAsTopLevel;
+- (void)_endLiveResizeAsTopLevel;
+- (void)_growContentReshapeContentAndToolbarView:(int)fp8 animate:(char)fp12;
+- (char)_growWindowReshapeContentAndToolbarView:(int)fp8 animate:(char)fp12;
+- (void)_reshapeContentAndToolbarView:(int)fp8 resizeWindow:(char)fp12 animate:(char)fp16;
+- (void)_toolbarFrameSizeChanged:fp8 oldSize:(struct _NSSize)fp12;
+- (void)_syncToolbarPosition;
+- (void)_showHideToolbar:(int)fp8 resizeWindow:(char)fp12 animate:(char)fp16;
+- (void)_showToolbarWithAnimation:(char)fp8;
+- (void)_hideToolbarWithAnimation:(char)fp8;
+- (void)_drawToolbarTransitionIfNecessary;
+- (void)drawRect:(NSRect)fp8;
+- (void)resetCursorRects;
+- (char)shouldBeTreatedAsInkEvent:fp8;
+- (char)_shouldBeTreatedAsInkEventInInactiveWindow:fp8;
+- hitTest:(struct _NSPoint)fp8;
+- (NSRect)_leftGroupRect;
+- (NSRect)_rightGroupRect;
+- (void)_updateWidgets;
+- (void)_updateMouseTracking;
+- (void)mouseEntered:fp8;
+- (void)mouseExited:fp8;
+- (void)_setMouseEnteredGroup:(char)fp8 entered:(char)fp12;
+- (char)_mouseInGroup:fp8;
+- (struct _NSSize)miniaturizedSize;
+- (float)_minXTitlebarDecorationMinWidth;
+- (float)_maxXTitlebarDecorationMinWidth;
+- (struct _NSSize)minFrameSize;
+- (float)_windowBorderThickness;
+- (float)_windowTitlebarXResizeBorderThickness;
+- (float)_windowTitlebarYResizeBorderThickness;
+- (float)_windowResizeBorderThickness;
+- (float)_minXWindowBorderWidth;
+- (float)_maxXWindowBorderWidth;
+- (float)_minYWindowBorderHeight;
+- (float)_maxYWindowBorderHeight;
+- (float)_minYTitlebarButtonsOffset;
+- (float)_minYTitlebarTitleOffset;
+- (float)_sideTitlebarWidth;
+- (float)_titlebarHeight;
+- (NSRect)_titlebarTitleRect;
+- (NSRect)titlebarRect;
+- (float)_windowTitlebarTitleMinHeight;
+- (struct _NSSize)_sizeOfTitlebarFileButton;
+- (struct _NSSize)sizeOfTitlebarToolbarButton;
+- (float)_windowTitlebarButtonSpacingWidth;
+- (float)_windowFileButtonSpacingWidth;
+- (float)_minXTitlebarWidgetInset;
+- (float)_maxXTitlebarWidgetInset;
+- (float)_minXTitlebarButtonsWidth;
+- (float)_maxXTitlebarButtonsWidth;
+- (struct _NSPoint)_closeButtonOrigin;
+- (struct _NSPoint)_zoomButtonOrigin;
+- (struct _NSPoint)_collapseButtonOrigin;
+- (struct _NSPoint)_toolbarButtonOrigin;
+- (struct _NSPoint)_fileButtonOrigin;
+- (void)_tileTitlebar;
+- (NSRect)_commandPopupRect;
+- (void)_resetDragMargins;
+- (float)_maxYTitlebarDragHeight;
+- (float)_minXTitlebarDragWidth;
+- (float)_maxXTitlebarDragWidth;
+- (float)_contentToFrameMinXWidth;
+- (float)_contentToFrameMaxXWidth;
+- (float)_contentToFrameMinYHeight;
+- (float)_contentToFrameMaxYHeight;
+- (float)_windowResizeCornerThickness;
+- (NSRect)_minYResizeRect;
+- (NSRect)_minYminXResizeRect;
+- (NSRect)_minYmaxXResizeRect;
+- (NSRect)_minXResizeRect;
+- (NSRect)_minXminYResizeRect;
+- (NSRect)_minXmaxYResizeRect;
+- (NSRect)_maxYResizeRect;
+- (NSRect)_maxYminXResizeRect;
+- (NSRect)_maxYmaxXResizeRect;
+- (NSRect)_maxXResizeRect;
+- (NSRect)_maxXminYResizeRect;
+- (NSRect)_maxXmaxYResizeRect;
+- (NSRect)_minXTitlebarResizeRect;
+- (NSRect)_maxXTitlebarResizeRect;
+- (NSRect)_minXBorderRect;
+- (NSRect)_maxXBorderRect;
+- (NSRect)_maxYBorderRect;
+- (NSRect)_minYBorderRect;
+- (void)_setUtilityWindow:(char)fp8;
+- (char)_isUtility;
+- (float)_sheetHeightAdjustment;
+- (void)_setSheet:(char)fp8;
+- (char)_isSheet;
+- (char)_isResizable;
+- (char)_isClosable;
+- (char)_isMiniaturizable;
+- (char)_hasToolbar;
+- (NSRect)_growBoxRect;
+- (void)_drawGrowBoxWithClip:(NSRect)fp8;
+- (char)_inactiveButtonsNeedMask;
+- (void)mouseDown:fp8;
+- _displayName;
+- (void)_setDisplayName:fp8;
+
+@end
diff --git a/src/gui/kernel/qnstitledframe_mac_p.h b/src/gui/kernel/qnstitledframe_mac_p.h
new file mode 100644
index 0000000000..0eb431ecec
--- /dev/null
+++ b/src/gui/kernel/qnstitledframe_mac_p.h
@@ -0,0 +1,205 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of qapplication_*.cpp, qwidget*.cpp, qcolor_x11.cpp, qfiledialog.cpp
+// and many other. This header file may change from version to version
+// without notice, or even be removed.
+//
+// We mean it.
+//
+
+// Private AppKit class (dumped from classdump).
+
+#import <Cocoa/Cocoa.h>
+#import "qnsframeview_mac_p.h"
+
+
+@interface NSTitledFrame : NSFrameView
+{
+ int resizeFlags;
+ id fileButton; /* NSDocumentDragButton* */
+ NSSize titleCellSize;
+}
+
++ (float)_windowBorderThickness:(unsigned int)fp8;
++ (float)_minXWindowBorderWidth:(unsigned int)fp8;
++ (float)_maxXWindowBorderWidth:(unsigned int)fp8;
++ (float)_minYWindowBorderHeight:(unsigned int)fp8;
++ (char)_resizeFromEdge;
++ (NSRect)frameRectForContentRect:(NSRect)fp8 styleMask:(unsigned int)fp24;
++ (NSRect)contentRectForFrameRect:(NSRect)fp8 styleMask:(unsigned int)fp24;
++ (struct _NSSize)minFrameSizeForMinContentSize:(struct _NSSize)fp8 styleMask:(unsigned int)fp16;
++ (struct _NSSize)minContentSizeForMinFrameSize:(struct _NSSize)fp8 styleMask:(unsigned int)fp16;
++ (float)minFrameWidthWithTitle:fp8 styleMask:(unsigned int)fp12;
++ (struct _NSSize)_titleCellSizeForTitle:fp8 styleMask:(unsigned int)fp12;
++ (float)_titleCellHeight:(unsigned int)fp8;
++ (float)_windowTitlebarTitleMinHeight:(unsigned int)fp8;
++ (float)_titlebarHeight:(unsigned int)fp8;
++ (struct _NSSize)sizeOfTitlebarButtons:(unsigned int)fp8;
++ (float)windowTitlebarLinesSpacingWidth:(unsigned int)fp8;
++ (float)windowTitlebarTitleLinesSpacingWidth:(unsigned int)fp8;
++ (float)_contentToFrameMinXWidth:(unsigned int)fp8;
++ (float)_contentToFrameMaxXWidth:(unsigned int)fp8;
++ (float)_contentToFrameMinYHeight:(unsigned int)fp8;
++ (float)_contentToFrameMaxYHeight:(unsigned int)fp8;
+- initWithFrame:(NSRect)fp8 styleMask:(unsigned int)fp24 owner:fp28;
+- (void)dealloc;
+- (void)setIsClosable:(char)fp8;
+- (void)setIsResizable:(char)fp8;
+- (void)_resetTitleFont;
+- (void)_setUtilityWindow:(char)fp8;
+- (char)isOpaque;
+- (char)worksWhenModal;
+- (void)propagateFrameDirtyRects:(NSRect)fp8;
+- (void)_showDrawRect:(NSRect)fp8;
+- (void)_drawFrameInterior:(NSRect *)fp8 clip:(NSRect)fp12;
+- (void)drawFrame:(NSRect)fp8;
+- (void)_drawFrameRects:(NSRect)fp8;
+- (void)_drawTitlebar:(NSRect)fp8;
+- (void)_drawTitlebarPattern:(int)fp8 inRect:(NSRect)fp12 clippedByRect:(NSRect)fp28 forKey:(char)fp44 alignment:(int)fp48;
+- (void)_drawTitlebarLines:(int)fp8 inRect:(NSRect)fp12 clippedByRect:(NSRect)fp28;
+- frameHighlightColor;
+- frameShadowColor;
+- (void)setFrameSize:(struct _NSSize)fp8;
+- (void)setFrameOrigin:(struct _NSPoint)fp8;
+- (void)tileAndSetWindowShape:(char)fp8;
+- (void)tile;
+- (void)_tileTitlebar;
+- (void)setTitle:fp8;
+- (char)_shouldRepresentFilename;
+- (void)setRepresentedFilename:fp8;
+- (void)_drawTitleStringIn:(NSRect)fp8 withColor:fp24;
+- titleFont;
+- (void)_drawResizeIndicators:(NSRect)fp8;
+- titleButtonOfClass:(Class)fp8;
+- initTitleButton:fp8;
+- newCloseButton;
+- newZoomButton;
+- newMiniaturizeButton;
+- newFileButton;
+- fileButton;
+- (void)_removeButtons;
+- (void)_updateButtons;
+- (char)_eventInTitlebar:fp8;
+- (char)acceptsFirstMouse:fp8;
+- (void)mouseDown:fp8;
+- (void)mouseUp:fp8;
+- (void)rightMouseDown:fp8;
+- (void)rightMouseUp:fp8;
+- (int)resizeEdgeForEvent:fp8;
+- (struct _NSSize)_resizeDeltaFromPoint:(struct _NSPoint)fp8 toEvent:fp16;
+- (NSRect)_validFrameForResizeFrame:(NSRect)fp8 fromResizeEdge:(int)fp24;
+- (NSRect)frame:(NSRect)fp8 resizedFromEdge:(int)fp24 withDelta:(struct _NSSize)fp28;
+- (char)constrainResizeEdge:(int *)fp8 withDelta:(struct _NSSize)fp12 elapsedTime:(float)fp20;
+- (void)resizeWithEvent:fp8;
+- (int)resizeFlags;
+- (void)resetCursorRects;
+- (void)setDocumentEdited:(char)fp8;
+- (struct _NSSize)miniaturizedSize;
+- (struct _NSSize)minFrameSize;
+- (float)_windowBorderThickness;
+- (float)_windowTitlebarXResizeBorderThickness;
+- (float)_windowTitlebarYResizeBorderThickness;
+- (float)_windowResizeBorderThickness;
+- (float)_minXWindowBorderWidth;
+- (float)_maxXWindowBorderWidth;
+- (float)_minYWindowBorderHeight;
+- (void)_invalidateTitleCellSize;
+- (void)_invalidateTitleCellWidth;
+- (float)_titleCellHeight;
+- (struct _NSSize)_titleCellSize;
+- (float)_titlebarHeight;
+- (NSRect)titlebarRect;
+- (NSRect)_maxTitlebarTitleRect;
+- (NSRect)_titlebarTitleRect;
+- (float)_windowTitlebarTitleMinHeight;
+- (NSRect)dragRectForFrameRect:(NSRect)fp8;
+- (struct _NSSize)sizeOfTitlebarButtons;
+- (struct _NSSize)_sizeOfTitlebarFileButton;
+- (float)_windowTitlebarButtonSpacingWidth;
+- (float)_minXTitlebarButtonsWidth;
+- (float)_maxXTitlebarButtonsWidth;
+- (int)_numberOfTitlebarLines;
+- (float)windowTitlebarLinesSpacingWidth;
+- (float)windowTitlebarTitleLinesSpacingWidth;
+- (float)_minLinesWidthWithSpace;
+- (NSRect)_minXTitlebarLinesRectWithTitleCellRect:(NSRect)fp8;
+- (NSRect)_maxXTitlebarLinesRectWithTitleCellRect:(NSRect)fp8;
+- (float)_minXTitlebarDecorationMinWidth;
+- (float)_maxXTitlebarDecorationMinWidth;
+- (struct _NSPoint)_closeButtonOrigin;
+- (struct _NSPoint)_zoomButtonOrigin;
+- (struct _NSPoint)_collapseButtonOrigin;
+- (struct _NSPoint)_fileButtonOrigin;
+- (float)_maxYTitlebarDragHeight;
+- (float)_minXTitlebarDragWidth;
+- (float)_maxXTitlebarDragWidth;
+- (float)_contentToFrameMinXWidth;
+- (float)_contentToFrameMaxXWidth;
+- (float)_contentToFrameMinYHeight;
+- (float)_contentToFrameMaxYHeight;
+- (NSRect)contentRect;
+- (float)_windowResizeCornerThickness;
+- (NSRect)_minYResizeRect;
+- (NSRect)_minYminXResizeRect;
+- (NSRect)_minYmaxXResizeRect;
+- (NSRect)_minXResizeRect;
+- (NSRect)_minXminYResizeRect;
+- (NSRect)_minXmaxYResizeRect;
+- (NSRect)_maxYResizeRect;
+- (NSRect)_maxYminXResizeRect;
+- (NSRect)_maxYmaxXResizeRect;
+- (NSRect)_maxXResizeRect;
+- (NSRect)_maxXminYResizeRect;
+- (NSRect)_maxXmaxYResizeRect;
+- (NSRect)_minXTitlebarResizeRect;
+- (NSRect)_maxXTitlebarResizeRect;
+- (NSRect)_minXBorderRect;
+- (NSRect)_maxXBorderRect;
+- (NSRect)_maxYBorderRect;
+- (NSRect)_minYBorderRect;
+
+@end
diff --git a/src/gui/kernel/qole_win.cpp b/src/gui/kernel/qole_win.cpp
new file mode 100644
index 0000000000..143b9316ec
--- /dev/null
+++ b/src/gui/kernel/qole_win.cpp
@@ -0,0 +1,255 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qdnd_p.h"
+
+#if !(defined(QT_NO_DRAGANDDROP) && defined(QT_NO_CLIPBOARD))
+
+#if defined(Q_OS_WINCE)
+#include <shlobj.h>
+#include "qguifunctions_wince.h"
+#endif
+
+QT_BEGIN_NAMESPACE
+
+QOleEnumFmtEtc::QOleEnumFmtEtc(const QVector<FORMATETC> &fmtetcs)
+{
+ m_isNull = false;
+ m_dwRefs = 1;
+ m_nIndex = 0;
+
+ for (int idx = 0; idx < fmtetcs.count(); ++idx) {
+ LPFORMATETC destetc = new FORMATETC();
+ if (copyFormatEtc(destetc, (LPFORMATETC)&(fmtetcs.at(idx)))) {
+ m_lpfmtetcs.append(destetc);
+ } else {
+ m_isNull = true;
+ delete destetc;
+ break;
+ }
+ }
+}
+
+QOleEnumFmtEtc::QOleEnumFmtEtc(const QVector<LPFORMATETC> &lpfmtetcs)
+{
+ m_isNull = false;
+ m_dwRefs = 1;
+ m_nIndex = 0;
+
+ for (int idx = 0; idx < lpfmtetcs.count(); ++idx) {
+ LPFORMATETC srcetc = lpfmtetcs.at(idx);
+ LPFORMATETC destetc = new FORMATETC();
+ if (copyFormatEtc(destetc, srcetc)) {
+ m_lpfmtetcs.append(destetc);
+ } else {
+ m_isNull = true;
+ delete destetc;
+ break;
+ }
+ }
+}
+
+QOleEnumFmtEtc::~QOleEnumFmtEtc()
+{
+ LPMALLOC pmalloc;
+
+#if !defined(Q_OS_WINCE)
+ if (CoGetMalloc(MEMCTX_TASK, &pmalloc) == NOERROR) {
+#else
+ if (SHGetMalloc(&pmalloc) == NOERROR) {
+#endif
+ for (int idx = 0; idx < m_lpfmtetcs.count(); ++idx) {
+ LPFORMATETC tmpetc = m_lpfmtetcs.at(idx);
+ if (tmpetc->ptd)
+ pmalloc->Free(tmpetc->ptd);
+ delete tmpetc;
+ }
+
+ pmalloc->Release();
+ }
+ m_lpfmtetcs.clear();
+}
+
+bool QOleEnumFmtEtc::isNull() const
+{
+ return m_isNull;
+}
+
+// IUnknown methods
+STDMETHODIMP
+QOleEnumFmtEtc::QueryInterface(REFIID riid, void FAR* FAR* ppvObj)
+{
+ if (riid == IID_IUnknown || riid == IID_IEnumFORMATETC) {
+ *ppvObj = this;
+ AddRef();
+ return NOERROR;
+ }
+ *ppvObj = NULL;
+ return ResultFromScode(E_NOINTERFACE);
+}
+
+STDMETHODIMP_(ULONG)
+QOleEnumFmtEtc::AddRef(void)
+{
+ return ++m_dwRefs;
+}
+
+STDMETHODIMP_(ULONG)
+QOleEnumFmtEtc::Release(void)
+{
+ if (--m_dwRefs == 0) {
+ delete this;
+ return 0;
+ }
+ return m_dwRefs;
+}
+
+// IEnumFORMATETC methods
+STDMETHODIMP
+QOleEnumFmtEtc::Next(ULONG celt, LPFORMATETC rgelt, ULONG FAR* pceltFetched)
+{
+ ULONG i=0;
+ ULONG nOffset;
+
+ if (rgelt == NULL)
+ return ResultFromScode(E_INVALIDARG);
+
+ while (i < celt) {
+ nOffset = m_nIndex + i;
+
+ if (nOffset < ULONG(m_lpfmtetcs.count())) {
+ copyFormatEtc((LPFORMATETC)&(rgelt[i]), m_lpfmtetcs.at(nOffset));
+ i++;
+ } else {
+ break;
+ }
+ }
+
+ m_nIndex += (WORD)i;
+
+ if (pceltFetched != NULL)
+ *pceltFetched = i;
+
+ if (i != celt)
+ return ResultFromScode(S_FALSE);
+
+ return NOERROR;
+}
+
+STDMETHODIMP
+QOleEnumFmtEtc::Skip(ULONG celt)
+{
+ ULONG i=0;
+ ULONG nOffset;
+
+ while (i < celt) {
+ nOffset = m_nIndex + i;
+
+ if (nOffset < ULONG(m_lpfmtetcs.count())) {
+ i++;
+ } else {
+ break;
+ }
+ }
+
+ m_nIndex += (WORD)i;
+
+ if (i != celt)
+ return ResultFromScode(S_FALSE);
+
+ return NOERROR;
+}
+
+STDMETHODIMP
+QOleEnumFmtEtc::Reset()
+{
+ m_nIndex = 0;
+ return NOERROR;
+}
+
+STDMETHODIMP
+QOleEnumFmtEtc::Clone(LPENUMFORMATETC FAR* newEnum)
+{
+ if (newEnum == NULL)
+ return ResultFromScode(E_INVALIDARG);
+
+ QOleEnumFmtEtc *result = new QOleEnumFmtEtc(m_lpfmtetcs);
+ result->m_nIndex = m_nIndex;
+
+ if (result->isNull()) {
+ delete result;
+ return ResultFromScode(E_OUTOFMEMORY);
+ } else {
+ *newEnum = result;
+ }
+
+ return NOERROR;
+}
+
+bool QOleEnumFmtEtc::copyFormatEtc(LPFORMATETC dest, LPFORMATETC src) const
+{
+ if (dest == NULL || src == NULL)
+ return false;
+
+ *dest = *src;
+
+ if (src->ptd) {
+ LPVOID pout;
+ LPMALLOC pmalloc;
+
+#if !defined(Q_OS_WINCE)
+ if (CoGetMalloc(MEMCTX_TASK, &pmalloc) != NOERROR)
+#else
+ if (SHGetMalloc(&pmalloc) != NOERROR)
+#endif
+ return false;
+
+ pout = (LPVOID)pmalloc->Alloc(src->ptd->tdSize);
+ memcpy(dest->ptd, src->ptd, size_t(src->ptd->tdSize));
+
+ pmalloc->Release();
+ }
+
+ return true;
+}
+
+QT_END_NAMESPACE
+#endif // QT_NO_DRAGANDDROP && QT_NO_CLIPBOARD
diff --git a/src/gui/kernel/qpalette.cpp b/src/gui/kernel/qpalette.cpp
new file mode 100644
index 0000000000..6541510e6f
--- /dev/null
+++ b/src/gui/kernel/qpalette.cpp
@@ -0,0 +1,1396 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qpalette.h"
+#include "qapplication.h"
+#include "qdatastream.h"
+#include "qvariant.h"
+
+QT_BEGIN_NAMESPACE
+
+static int qt_palette_count = 1;
+
+class QPalettePrivate {
+public:
+ QPalettePrivate() : ref(1), ser_no(qt_palette_count++), detach_no(0) { }
+ QAtomicInt ref;
+ QBrush br[QPalette::NColorGroups][QPalette::NColorRoles];
+ int ser_no;
+ int detach_no;
+};
+
+static QColor qt_mix_colors(QColor a, QColor b)
+{
+ return QColor((a.red() + b.red()) / 2, (a.green() + b.green()) / 2,
+ (a.blue() + b.blue()) / 2, (a.alpha() + b.alpha()) / 2);
+}
+
+#ifdef QT3_SUPPORT
+
+#ifndef QT_NO_DATASTREAM
+QDataStream &qt_stream_out_qcolorgroup(QDataStream &s, const QColorGroup &g)
+{
+ if(s.version() == 1) {
+ // Qt 1.x
+ s << g.color(QPalette::Foreground) << g.color(QPalette::Background)
+ << g.color(QPalette::Light) << g.color(QPalette::Dark)
+ << g.color(QPalette::Mid) << g.color(QPalette::Text) << g.color(QPalette::Base);
+ } else {
+ int max = QPalette::NColorRoles;
+ if (s.version() <= QDataStream::Qt_2_1)
+ max = QPalette::HighlightedText + 1;
+ else if (s.version() <= QDataStream::Qt_4_3)
+ max = QPalette::AlternateBase + 1;
+ for(int r = 0 ; r < max ; r++)
+ s << g.brush((QPalette::ColorRole)r);
+ }
+ return s;
+}
+
+QDataStream &qt_stream_in_qcolorgroup(QDataStream &s, QColorGroup &g)
+{
+ if(s.version() == 1) { // Qt 1.x
+ QColor fg, bg, light, dark, mid, text, base;
+ s >> fg >> bg >> light >> dark >> mid >> text >> base;
+ QPalette p(bg);
+ p.setColor(QPalette::Active, QPalette::Foreground, fg);
+ p.setColor(QPalette::Active, QPalette::Light, light);
+ p.setColor(QPalette::Active, QPalette::Dark, dark);
+ p.setColor(QPalette::Active, QPalette::Mid, mid);
+ p.setColor(QPalette::Active, QPalette::Text, text);
+ p.setColor(QPalette::Active, QPalette::Base, base);
+ g = p;
+ g.setCurrentColorGroup(QPalette::Active);
+ } else {
+ int max = QPalette::NColorRoles;
+ if (s.version() <= QDataStream::Qt_2_1)
+ max = QPalette::HighlightedText + 1;
+ else if (s.version() <= QDataStream::Qt_3_0)
+ max = QPalette::LinkVisited + 1;
+ else if (s.version() <= QDataStream::Qt_4_3)
+ max = QPalette::AlternateBase + 1;
+ QBrush tmp;
+ for(int r = 0 ; r < max; r++) {
+ s >> tmp;
+ g.setBrush((QPalette::ColorRole)r, tmp);
+ }
+ }
+ return s;
+}
+
+QDataStream &operator<<(QDataStream &s, const QColorGroup &g)
+{
+ return qt_stream_out_qcolorgroup(s, g);
+}
+
+QDataStream &operator>>(QDataStream &s, QColorGroup &g)
+{
+ return qt_stream_in_qcolorgroup(s, g);
+}
+#endif
+
+/*!
+ Constructs a palette with the specified \a active, \a disabled and
+ \a inactive color groups.
+*/
+QPalette::QPalette(const QColorGroup &active, const QColorGroup &disabled,
+ const QColorGroup &inactive)
+{
+ Q_ASSERT(QPalette::NColorRoles == QPalette::ToolTipText + 1);
+ init();
+ setColorGroup(Active, active);
+ setColorGroup(Disabled, disabled);
+ setColorGroup(Inactive, inactive);
+}
+
+QColorGroup QPalette::createColorGroup(ColorGroup cr) const
+{
+ QColorGroup ret(*this);
+ ret.setCurrentColorGroup(cr);
+ return ret;
+}
+
+void QPalette::setColorGroup(ColorGroup cg, const QColorGroup &g)
+{
+ setColorGroup(cg, g.brush(WindowText), g.brush(Button), g.brush(Light),
+ g.brush(Dark), g.brush(Mid), g.brush(Text), g.brush(BrightText),
+ g.brush(Base), g.brush(AlternateBase), g.brush(Window),
+ g.brush(Midlight), g.brush(ButtonText), g.brush(Shadow),
+ g.brush(Highlight), g.brush(HighlightedText), g.brush(Link),
+ g.brush(LinkVisited), g.brush(ToolTipBase), g.brush(ToolTipText));
+}
+
+#endif
+
+/*!
+ \fn const QColor &QPalette::color(ColorRole role) const
+
+ \overload
+
+ Returns the color that has been set for the given color \a role in
+ the current ColorGroup.
+
+ \sa brush() ColorRole
+ */
+
+/*!
+ \fn const QBrush &QPalette::brush(ColorRole role) const
+
+ \overload
+
+ Returns the brush that has been set for the given color \a role in
+ the current ColorGroup.
+
+ \sa color() setBrush() ColorRole
+*/
+
+/*!
+ \fn void QPalette::setColor(ColorRole role, const QColor &color)
+
+ \overload
+
+ Sets the color used for the given color \a role, in all color
+ groups, to the specified solid \a color.
+
+ \sa brush() setColor() ColorRole
+*/
+
+/*!
+ \fn void QPalette::setBrush(ColorRole role, const QBrush &brush)
+
+ Sets the brush for the given color \a role to the specified \a
+ brush for all groups in the palette.
+
+ \sa brush() setColor() ColorRole
+*/
+
+/*!
+ \fn const QBrush & QPalette::foreground() const
+ \obsolete
+
+ Use windowText() instead.
+*/
+
+/*!
+ \fn const QBrush & QPalette::windowText() const
+
+ Returns the window text (general foreground) brush of the
+ current color group.
+
+ \sa ColorRole brush()
+*/
+
+/*!
+ \fn const QBrush & QPalette::button() const
+
+ Returns the button brush of the current color group.
+
+ \sa ColorRole brush()
+*/
+
+/*!
+ \fn const QBrush & QPalette::light() const
+
+ Returns the light brush of the current color group.
+
+ \sa ColorRole brush()
+*/
+
+/*!
+ \fn const QBrush& QPalette::midlight() const
+
+ Returns the midlight brush of the current color group.
+
+ \sa ColorRole brush()
+*/
+
+/*!
+ \fn const QBrush & QPalette::dark() const
+
+ Returns the dark brush of the current color group.
+
+ \sa ColorRole brush()
+*/
+
+/*!
+ \fn const QBrush & QPalette::mid() const
+
+ Returns the mid brush of the current color group.
+
+ \sa ColorRole brush()
+*/
+
+/*!
+ \fn const QBrush & QPalette::text() const
+
+ Returns the text foreground brush of the current color group.
+
+ \sa ColorRole brush()
+*/
+
+/*!
+ \fn const QBrush & QPalette::brightText() const
+
+ Returns the bright text foreground brush of the current color group.
+
+ \sa ColorRole brush()
+*/
+
+/*!
+ \fn const QBrush & QPalette::buttonText() const
+
+ Returns the button text foreground brush of the current color group.
+
+ \sa ColorRole brush()
+*/
+
+/*!
+ \fn const QBrush & QPalette::base() const
+
+ Returns the base brush of the current color group.
+
+ \sa ColorRole brush()
+*/
+
+/*!
+ \fn const QBrush & QPalette::alternateBase() const
+
+ Returns the alternate base brush of the current color group.
+
+ \sa ColorRole brush()
+*/
+
+/*!
+ \fn const QBrush & QPalette::toolTipBase() const
+ \since 4.4
+
+ Returns the tool tip base brush of the current color group. This brush is
+ used by QToolTip and QWhatsThis.
+
+ \note Tool tips use the Inactive color group of QPalette, because tool
+ tips are not active windows.
+
+ \sa ColorRole brush()
+*/
+
+/*!
+ \fn const QBrush & QPalette::toolTipText() const
+ \since 4.4
+
+ Returns the tool tip text brush of the current color group. This brush is
+ used by QToolTip and QWhatsThis.
+
+ \note Tool tips use the Inactive color group of QPalette, because tool
+ tips are not active windows.
+
+ \sa ColorRole brush()
+*/
+
+/*!
+ \fn const QBrush & QPalette::background() const
+ \obsolete
+
+ Use window() instead.
+*/
+
+/*!
+ \fn const QBrush & QPalette::window() const
+
+ Returns the window (general background) brush of the current
+ color group.
+
+ \sa ColorRole brush()
+*/
+
+/*!
+ \fn const QBrush & QPalette::shadow() const
+
+ Returns the shadow brush of the current color group.
+
+ \sa ColorRole brush()
+*/
+
+/*!
+ \fn const QBrush & QPalette::highlight() const
+
+ Returns the highlight brush of the current color group.
+
+ \sa ColorRole brush()
+*/
+
+/*!
+ \fn const QBrush & QPalette::highlightedText() const
+
+ Returns the highlighted text brush of the current color group.
+
+ \sa ColorRole brush()
+*/
+
+/*!
+ \fn const QBrush & QPalette::link() const
+
+ Returns the unvisited link text brush of the current color group.
+
+ \sa ColorRole brush()
+*/
+
+/*!
+ \fn const QBrush & QPalette::linkVisited() const
+
+ Returns the visited link text brush of the current color group.
+
+ \sa ColorRole brush()
+*/
+
+/*!
+ \fn ColorGroup QPalette::currentColorGroup() const
+
+ Returns the palette's current color group.
+*/
+
+/*!
+ \fn void QPalette::setCurrentColorGroup(ColorGroup cg)
+
+ Set the palette's current color group to \a cg.
+*/
+
+/*!
+ \class QPalette
+
+ \brief The QPalette class contains color groups for each widget state.
+
+ \ingroup appearance
+ \ingroup shared
+ \ingroup multimedia
+ \mainclass
+
+ A palette consists of three color groups: \e Active, \e Disabled,
+ and \e Inactive. All widgets in Qt contain a palette and
+ use their palette to draw themselves. This makes the user
+ interface easily configurable and easier to keep consistent.
+
+
+ If you create a new widget we strongly recommend that you use the
+ colors in the palette rather than hard-coding specific colors.
+
+ The color groups:
+ \list
+ \i The Active group is used for the window that has keyboard focus.
+ \i The Inactive group is used for other windows.
+ \i The Disabled group is used for widgets (not windows) that are
+ disabled for some reason.
+ \endlist
+
+ Both active and inactive windows can contain disabled widgets.
+ (Disabled widgets are often called \e inaccessible or \e{grayed
+ out}.)
+
+ In most styles, Active and Inactive look the same.
+
+ Colors and brushes can be set for particular roles in any of a palette's
+ color groups with setColor() and setBrush(). A color group contains a
+ group of colors used by widgets for drawing themselves. We recommend that
+ widgets use color group roles from the palette such as "foreground" and
+ "base" rather than literal colors like "red" or "turquoise". The color
+ roles are enumerated and defined in the \l ColorRole documentation.
+
+ We strongly recommend that you use the default palette of the
+ current style (returned by QApplication::palette()) and
+ modify that as necessary. This is done by Qt's widgets when they
+ are drawn.
+
+ To modify a color group you call the functions
+ setColor() and setBrush(), depending on whether you want a pure
+ color or a pixmap pattern.
+
+ There are also corresponding color() and brush() getters, and a
+ commonly used convenience function to get the ColorRole for the current ColorGroup:
+ window(), windowText(), base(), etc.
+
+
+ You can copy a palette using the copy constructor and test to see
+ if two palettes are \e identical using isCopyOf().
+
+ QPalette is optimized by the use of \l{implicit sharing},
+ so it is very efficient to pass QPalette objects as arguments.
+
+ \warning Some styles do not use the palette for all drawing, for
+ instance, if they make use of native theme engines. This is the
+ case for both the Windows XP, Windows Vista, and the Mac OS X
+ styles.
+
+ \sa QApplication::setPalette(), QWidget::setPalette(), QColor
+*/
+
+/*!
+ \enum QPalette::ColorGroup
+
+ \value Disabled
+ \value Active
+ \value Inactive
+ \value Normal synonym for Active
+
+ \omitvalue All
+ \omitvalue NColorGroups
+ \omitvalue Current
+*/
+
+/*!
+ \enum QPalette::ColorRole
+
+ \img palette.png Color Roles
+
+ The ColorRole enum defines the different symbolic color roles used
+ in current GUIs.
+
+ The central roles are:
+
+ \value Window A general background color.
+
+ \value Background This value is obsolete. Use Window instead.
+
+ \value WindowText A general foreground color.
+
+ \value Foreground This value is obsolete. Use WindowText instead.
+
+ \value Base Used mostly as the background color for text entry widgets,
+ but can also be used for other painting - such as the
+ background of combobox drop down lists and toolbar handles.
+ It is usually white or another light color.
+
+ \value AlternateBase Used as the alternate background color in views with
+ alternating row colors (see
+ QAbstractItemView::setAlternatingRowColors()).
+
+ \value ToolTipBase Used as the background color for QToolTip and
+ QWhatsThis. Tool tips use the Inactive color group
+ of QPalette, because tool tips are not active
+ windows.
+
+ \value ToolTipText Used as the foreground color for QToolTip and
+ QWhatsThis. Tool tips use the Inactive color group
+ of QPalette, because tool tips are not active
+ windows.
+
+ \value Text The foreground color used with \c Base. This is usually
+ the same as the \c WindowText, in which case it must provide
+ good contrast with \c Window and \c Base.
+
+ \value Button The general button background color. This background can be different from
+ \c Window as some styles require a different background color for buttons.
+
+ \value ButtonText A foreground color used with the \c Button color.
+
+ \value BrightText A text color that is very different from
+ \c WindowText, and contrasts well with e.g. \c
+ Dark. Typically used for text that needs to be
+ drawn where \c Text or \c WindowText would give
+ poor contrast, such as on pressed push buttons.
+ Note that text colors can be used for things
+ other than just words; text colors are \e
+ usually used for text, but it's quite common to
+ use the text color roles for lines, icons, etc.
+
+
+ There are some color roles used mostly for 3D bevel and shadow effects.
+ All of these are normally derived from \c Window, and used in ways that
+ depend on that relationship. For example, buttons depend on it to make the
+ bevels look attractive, and Motif scroll bars depend on \c Mid to be
+ slightly different from \c Window.
+
+ \value Light Lighter than \c Button color.
+
+ \value Midlight Between \c Button and \c Light.
+
+ \value Dark Darker than \c Button.
+
+ \value Mid Between \c Button and \c Dark.
+
+ \value Shadow A very dark color. By default, the shadow color is
+ Qt::black.
+
+
+ Selected (marked) items have two roles:
+
+ \value Highlight A color to indicate a selected item or the current
+ item. By default, the highlight color is
+ Qt::darkBlue.
+
+ \value HighlightedText A text color that contrasts with \c Highlight.
+ By default, the highlighted text color is Qt::white.
+
+ There are two color roles related to hyperlinks:
+
+ \value Link A text color used for unvisited hyperlinks.
+ By default, the link color is Qt::blue.
+
+ \value LinkVisited A text color used for already visited hyperlinks.
+ By default, the linkvisited color is Qt::magenta.
+
+ Note that we do not use the \c Link and \c LinkVisited roles when
+ rendering rich text in Qt, and that we recommend that you use CSS
+ and the QTextDocument::setDefaultStyleSheet() function to alter
+ the appearance of links. For example:
+
+ \snippet doc/src/snippets/textdocument-css/main.cpp 0
+
+ \value NoRole No role; this special role is often used to indicate that a
+ role has not been assigned.
+
+ \omitvalue NColorRoles
+*/
+
+/*!
+ Constructs a palette object that uses the application's default palette.
+
+ \sa QApplication::setPalette(), QApplication::palette()
+*/
+QPalette::QPalette()
+ : d(QApplication::palette().d),
+ current_group(Active),
+ resolve_mask(0)
+{
+ d->ref.ref();
+}
+
+static void qt_palette_from_color(QPalette &pal, const QColor & button)
+{
+ QColor bg = button,
+ btn = button,
+ fg, base;
+ int h, s, v;
+ bg.getHsv(&h, &s, &v);
+ if(v > 128) {
+ fg = Qt::black;
+ base = Qt::white;
+ } else {
+ fg = Qt::white;
+ base = Qt::black;
+ }
+ //inactive and active are the same..
+ pal.setColorGroup(QPalette::Active, QBrush(fg), QBrush(btn), QBrush(btn.lighter(150)),
+ QBrush(btn.darker()), QBrush(btn.darker(150)), QBrush(fg), QBrush(Qt::white),
+ QBrush(base), QBrush(bg));
+ pal.setColorGroup(QPalette::Inactive, QBrush(fg), QBrush(btn), QBrush(btn.lighter(150)),
+ QBrush(btn.darker()), QBrush(btn.darker(150)), QBrush(fg), QBrush(Qt::white),
+ QBrush(base), QBrush(bg));
+ pal.setColorGroup(QPalette::Disabled, QBrush(btn.darker()), QBrush(btn), QBrush(btn.lighter(150)),
+ QBrush(btn.darker()), QBrush(btn.darker(150)), QBrush(btn.darker()),
+ QBrush(Qt::white), QBrush(bg), QBrush(bg));
+}
+
+
+/*!
+ Constructs a palette from the \a button color. The other colors are
+ automatically calculated, based on this color. \c Window will be
+ the button color as well.
+*/
+QPalette::QPalette(const QColor &button)
+{
+ init();
+ qt_palette_from_color(*this, button);
+}
+
+/*!
+ Constructs a palette from the \a button color. The other colors are
+ automatically calculated, based on this color. \c Window will be
+ the button color as well.
+*/
+QPalette::QPalette(Qt::GlobalColor button)
+{
+ init();
+ qt_palette_from_color(*this, button);
+}
+
+/*!
+ Constructs a palette. You can pass either brushes, pixmaps or
+ plain colors for \a windowText, \a button, \a light, \a dark, \a
+ mid, \a text, \a bright_text, \a base and \a window.
+
+ \sa QBrush
+*/
+QPalette::QPalette(const QBrush &windowText, const QBrush &button,
+ const QBrush &light, const QBrush &dark,
+ const QBrush &mid, const QBrush &text,
+ const QBrush &bright_text, const QBrush &base,
+ const QBrush &window)
+{
+ init();
+ setColorGroup(All, windowText, button, light, dark, mid, text, bright_text,
+ base, window);
+}
+
+
+/*!\obsolete
+
+ Constructs a palette with the specified \a windowText, \a
+ window, \a light, \a dark, \a mid, \a text, and \a base colors.
+ The button color will be set to the window color.
+*/
+QPalette::QPalette(const QColor &windowText, const QColor &window,
+ const QColor &light, const QColor &dark, const QColor &mid,
+ const QColor &text, const QColor &base)
+{
+ init();
+ setColorGroup(All, QBrush(windowText), QBrush(window), QBrush(light),
+ QBrush(dark), QBrush(mid), QBrush(text), QBrush(light),
+ QBrush(base), QBrush(window));
+}
+
+/*!
+ Constructs a palette from a \a button color and a \a window.
+ The other colors are automatically calculated, based on these
+ colors.
+*/
+QPalette::QPalette(const QColor &button, const QColor &window)
+{
+ init();
+ QColor bg = window, btn = button, fg, base, disfg;
+ int h, s, v;
+ bg.getHsv(&h, &s, &v);
+ if(v > 128) {
+ fg = Qt::black;
+ base = Qt::white;
+ disfg = Qt::darkGray;
+ } else {
+ fg = Qt::white;
+ base = Qt::black;
+ disfg = Qt::darkGray;
+ }
+ //inactive and active are identical
+ setColorGroup(Inactive, QBrush(fg), QBrush(btn), QBrush(btn.lighter(150)), QBrush(btn.darker()),
+ QBrush(btn.darker(150)), QBrush(fg), QBrush(Qt::white), QBrush(base),
+ QBrush(bg));
+ setColorGroup(Active, QBrush(fg), QBrush(btn), QBrush(btn.lighter(150)), QBrush(btn.darker()),
+ QBrush(btn.darker(150)), QBrush(fg), QBrush(Qt::white), QBrush(base),
+ QBrush(bg));
+ setColorGroup(Disabled, QBrush(disfg), QBrush(btn), QBrush(btn.lighter(150)),
+ QBrush(btn.darker()), QBrush(btn.darker(150)), QBrush(disfg),
+ QBrush(Qt::white), QBrush(base), QBrush(bg));
+}
+
+/*!
+ Constructs a copy of \a p.
+
+ This constructor is fast thanks to \l{implicit sharing}.
+*/
+QPalette::QPalette(const QPalette &p)
+{
+ d = p.d;
+ d->ref.ref();
+ resolve_mask = p.resolve_mask;
+ current_group = p.current_group;
+}
+
+/*!
+ Destroys the palette.
+*/
+QPalette::~QPalette()
+{
+ if(!d->ref.deref())
+ delete d;
+}
+
+/*!\internal*/
+void QPalette::init() {
+ d = new QPalettePrivate;
+ resolve_mask = 0;
+ current_group = Active; //as a default..
+}
+
+/*!
+ Assigns \a p to this palette and returns a reference to this
+ palette.
+
+ This operation is fast thanks to \l{implicit sharing}.
+*/
+QPalette &QPalette::operator=(const QPalette &p)
+{
+ p.d->ref.ref();
+ resolve_mask = p.resolve_mask;
+ current_group = p.current_group;
+ if(!d->ref.deref())
+ delete d;
+ d = p.d;
+ return *this;
+}
+
+/*!
+ Returns the palette as a QVariant
+*/
+QPalette::operator QVariant() const
+{
+ return QVariant(QVariant::Palette, this);
+}
+
+/*!
+ \fn const QColor &QPalette::color(ColorGroup group, ColorRole role) const
+
+ Returns the color in the specified color \a group, used for the
+ given color \a role.
+
+ \sa brush() setColor() ColorRole
+*/
+
+/*!
+ \fn const QBrush &QPalette::brush(ColorGroup group, ColorRole role) const
+
+ Returns the brush in the specified color \a group, used for the
+ given color \a role.
+
+ \sa color() setBrush() ColorRole
+*/
+const QBrush &QPalette::brush(ColorGroup gr, ColorRole cr) const
+{
+ Q_ASSERT(cr < NColorRoles);
+ if(gr >= (int)NColorGroups) {
+ if(gr == Current) {
+ gr = (ColorGroup)current_group;
+ } else {
+ qWarning("QPalette::brush: Unknown ColorGroup: %d", (int)gr);
+ gr = Active;
+ }
+ }
+ return d->br[gr][cr];
+}
+
+/*!
+ \fn void QPalette::setColor(ColorGroup group, ColorRole role, const QColor &color)
+
+ Sets the brush in the specified color \a group, used for the given
+ color \a role, to the specified solid \a color.
+
+ \sa setBrush() color() ColorRole
+*/
+
+/*!
+ \fn void QPalette::setBrush(ColorGroup group, ColorRole role, const QBrush &brush)
+ \overload
+
+ Sets the brush in the specified color \a group, used for the given
+ color \a role, to \a brush.
+
+ \sa brush() setColor() ColorRole
+*/
+void QPalette::setBrush(ColorGroup cg, ColorRole cr, const QBrush &b)
+{
+ Q_ASSERT(cr < NColorRoles);
+ detach();
+ if(cg >= (int)NColorGroups) {
+ if(cg == All) {
+ for(int i = 0; i < (int)NColorGroups; i++)
+ d->br[i][cr] = b;
+ resolve_mask |= (1<<cr);
+ return;
+ } else if(cg == Current) {
+ cg = (ColorGroup)current_group;
+ } else {
+ qWarning("QPalette::setBrush: Unknown ColorGroup: %d", (int)cg);
+ cg = Active;
+ }
+ }
+ d->br[cg][cr] = b;
+ resolve_mask |= (1<<cr);
+}
+
+/*!
+ \since 4.2
+
+ Returns true if the ColorGroup \a cg and ColorRole \a cr has been
+ set previously on this palette; otherwise returns false.
+
+ \sa setBrush()
+*/
+bool QPalette::isBrushSet(ColorGroup cg, ColorRole cr) const
+{
+ Q_UNUSED(cg);
+ return (resolve_mask & (1<<cr));
+}
+
+/*!
+ \internal
+*/
+void QPalette::detach()
+{
+ if (d->ref != 1) {
+ QPalettePrivate *x = new QPalettePrivate;
+ for(int grp = 0; grp < (int)NColorGroups; grp++) {
+ for(int role = 0; role < (int)NColorRoles; role++)
+ x->br[grp][role] = d->br[grp][role];
+ }
+ if(!d->ref.deref())
+ delete d;
+ d = x;
+ }
+ ++d->detach_no;
+}
+
+/*!
+ \fn bool QPalette::operator!=(const QPalette &p) const
+
+ Returns true (slowly) if this palette is different from \a p;
+ otherwise returns false (usually quickly).
+*/
+
+/*!
+ Returns true (usually quickly) if this palette is equal to \a p;
+ otherwise returns false (slowly).
+*/
+bool QPalette::operator==(const QPalette &p) const
+{
+ if (isCopyOf(p))
+ return true;
+ for(int grp = 0; grp < (int)NColorGroups; grp++) {
+ for(int role = 0; role < (int)NColorRoles; role++) {
+ if(d->br[grp][role] != p.d->br[grp][role])
+ return false;
+ }
+ }
+ return true;
+}
+
+#ifdef QT3_SUPPORT
+bool QColorGroup::operator==(const QColorGroup &other) const
+{
+ if (isCopyOf(other))
+ return true;
+ for (int role = 0; role < int(NColorRoles); role++) {
+ if(d->br[current_group][role] != other.d->br[other.current_group][role])
+ return false;
+ }
+ return true;
+}
+
+/*!
+ Returns the color group as a QVariant
+*/
+QColorGroup::operator QVariant() const
+{
+ return QVariant(QVariant::ColorGroup, this);
+}
+#endif
+
+/*!
+ \fn bool QPalette::isEqual(ColorGroup cg1, ColorGroup cg2) const
+
+ Returns true (usually quickly) if color group \a cg1 is equal to
+ \a cg2; otherwise returns false.
+*/
+bool QPalette::isEqual(QPalette::ColorGroup group1, QPalette::ColorGroup group2) const
+{
+ if(group1 >= (int)NColorGroups) {
+ if(group1 == Current) {
+ group1 = (ColorGroup)current_group;
+ } else {
+ qWarning("QPalette::brush: Unknown ColorGroup(1): %d", (int)group1);
+ group1 = Active;
+ }
+ }
+ if(group2 >= (int)NColorGroups) {
+ if(group2 == Current) {
+ group2 = (ColorGroup)current_group;
+ } else {
+ qWarning("QPalette::brush: Unknown ColorGroup(2): %d", (int)group2);
+ group2 = Active;
+ }
+ }
+ if(group1 == group2)
+ return true;
+ for(int role = 0; role < (int)NColorRoles; role++) {
+ if(d->br[group1][role] != d->br[group2][role])
+ return false;
+ }
+ return true;
+}
+
+/*! \obsolete
+
+ Returns a number that identifies the contents of this QPalette
+ object. Distinct QPalette objects can only have the same serial
+ number if they refer to the same contents (but they don't have
+ to). Also, the serial number of a QPalette may change during the
+ lifetime of the object.
+
+ Use cacheKey() instead.
+
+ \warning The serial number doesn't necessarily change when the
+ palette is altered. This means that it may be dangerous to use it
+ as a cache key.
+
+ \sa operator==()
+*/
+int QPalette::serialNumber() const
+{
+ return d->ser_no;
+}
+
+/*!
+ Returns a number that identifies the contents of this QPalette
+ object. Distinct QPalette objects can have the same key if
+ they refer to the same contents.
+
+ The cacheKey() will change when the palette is altered.
+*/
+qint64 QPalette::cacheKey() const
+{
+ return (((qint64) d->ser_no) << 32) | ((qint64) (d->detach_no));
+}
+
+/*!
+ Returns a new QPalette that has attributes copied from \a other.
+*/
+QPalette QPalette::resolve(const QPalette &other) const
+{
+ if ((*this == other && resolve_mask == other.resolve_mask)
+ || resolve_mask == 0) {
+ QPalette o = other;
+ o.resolve_mask = resolve_mask;
+ return o;
+ }
+
+ QPalette palette(*this);
+ palette.detach();
+
+ for(int role = 0; role < (int)NColorRoles; role++)
+ if (!(resolve_mask & (1<<role)))
+ for(int grp = 0; grp < (int)NColorGroups; grp++)
+ palette.d->br[grp][role] = other.d->br[grp][role];
+
+ return palette;
+}
+
+/*!
+ \fn uint QPalette::resolve() const
+ \internal
+*/
+
+/*!
+ \fn void QPalette::resolve(uint mask)
+ \internal
+*/
+
+
+/*****************************************************************************
+ QPalette stream functions
+ *****************************************************************************/
+
+#ifndef QT_NO_DATASTREAM
+
+static const int NumOldRoles = 7;
+static const int oldRoles[7] = { QPalette::Foreground, QPalette::Background, QPalette::Light,
+ QPalette::Dark, QPalette::Mid, QPalette::Text, QPalette::Base };
+
+/*!
+ \relates QPalette
+
+ Writes the palette, \a p to the stream \a s and returns a
+ reference to the stream.
+
+ \sa \link datastreamformat.html Format of the QDataStream operators \endlink
+*/
+
+QDataStream &operator<<(QDataStream &s, const QPalette &p)
+{
+ for (int grp = 0; grp < (int)QPalette::NColorGroups; grp++) {
+ if (s.version() == 1) {
+ // Qt 1.x
+ for (int i = 0; i < NumOldRoles; ++i)
+ s << p.d->br[grp][oldRoles[i]].color();
+ } else {
+ int max = QPalette::ToolTipText + 1;
+ if (s.version() <= QDataStream::Qt_2_1)
+ max = QPalette::HighlightedText + 1;
+ else if (s.version() <= QDataStream::Qt_4_3)
+ max = QPalette::AlternateBase + 1;
+ for (int r = 0; r < max; r++)
+ s << p.d->br[grp][r];
+ }
+ }
+ return s;
+}
+
+static void readV1ColorGroup(QDataStream &s, QPalette &pal, QPalette::ColorGroup grp)
+{
+ for (int i = 0; i < NumOldRoles; ++i) {
+ QColor col;
+ s >> col;
+ pal.setColor(grp, (QPalette::ColorRole)oldRoles[i], col);
+ }
+}
+
+/*!
+ \relates QPalette
+
+ Reads a palette from the stream, \a s into the palette \a p, and
+ returns a reference to the stream.
+
+ \sa \link datastreamformat.html Format of the QDataStream operators \endlink
+*/
+
+QDataStream &operator>>(QDataStream &s, QPalette &p)
+{
+ if(s.version() == 1) {
+ p = QPalette();
+ readV1ColorGroup(s, p, QPalette::Active);
+ readV1ColorGroup(s, p, QPalette::Disabled);
+ readV1ColorGroup(s, p, QPalette::Inactive);
+ } else {
+ int max = QPalette::NColorRoles;
+ if (s.version() <= QDataStream::Qt_2_1) {
+ p = QPalette();
+ max = QPalette::HighlightedText + 1;
+ } else if (s.version() <= QDataStream::Qt_4_3) {
+ p = QPalette();
+ max = QPalette::AlternateBase + 1;
+ }
+
+ QBrush tmp;
+ for(int grp = 0; grp < (int)QPalette::NColorGroups; ++grp) {
+ for(int role = 0; role < max; ++role) {
+ s >> tmp;
+ p.setBrush((QPalette::ColorGroup)grp, (QPalette::ColorRole)role, tmp);
+ }
+ }
+ }
+ return s;
+}
+#endif //QT_NO_DATASTREAM
+
+/*!
+ Returns true if this palette and \a p are copies of each other,
+ i.e. one of them was created as a copy of the other and neither
+ was subsequently modified; otherwise returns false. This is much
+ stricter than equality.
+
+ \sa operator=() operator==()
+*/
+
+bool QPalette::isCopyOf(const QPalette &p) const
+{
+ return d == p.d;
+}
+
+/*!
+
+ Sets a the group at \a cg. You can pass either brushes, pixmaps or
+ plain colors for \a windowText, \a button, \a light, \a dark, \a
+ mid, \a text, \a bright_text, \a base and \a window.
+
+ \sa QBrush
+*/
+void QPalette::setColorGroup(ColorGroup cg, const QBrush &windowText, const QBrush &button,
+ const QBrush &light, const QBrush &dark, const QBrush &mid,
+ const QBrush &text, const QBrush &bright_text, const QBrush &base,
+ const QBrush &window)
+{
+ QBrush alt_base = QBrush(qt_mix_colors(base.color(), button.color()));
+ QBrush mid_light = QBrush(qt_mix_colors(button.color(), light.color()));
+ QColor toolTipBase(255, 255, 220);
+ QColor toolTipText(0, 0, 0);
+
+ setColorGroup(cg, windowText, button, light, dark, mid, text, bright_text, base,
+ alt_base, window, mid_light, text,
+ QBrush(Qt::black), QBrush(Qt::darkBlue), QBrush(Qt::white),
+ QBrush(Qt::blue), QBrush(Qt::magenta), QBrush(toolTipBase),
+ QBrush(toolTipText));
+
+ resolve_mask &= ~(1 << Highlight);
+ resolve_mask &= ~(1 << HighlightedText);
+ resolve_mask &= ~(1 << LinkVisited);
+ resolve_mask &= ~(1 << Link);
+}
+
+
+/*!\internal*/
+void
+QPalette::setColorGroup(ColorGroup cg, const QBrush &foreground, const QBrush &button,
+ const QBrush &light, const QBrush &dark, const QBrush &mid,
+ const QBrush &text, const QBrush &bright_text,
+ const QBrush &base, const QBrush &alternate_base,
+ const QBrush &background, const QBrush &midlight,
+ const QBrush &button_text, const QBrush &shadow,
+ const QBrush &highlight, const QBrush &highlighted_text,
+ const QBrush &link, const QBrush &link_visited)
+{
+ setColorGroup(cg, foreground, button, light, dark, mid,
+ text, bright_text, base, alternate_base, background,
+ midlight, button_text, shadow, highlight, highlighted_text,
+ link, link_visited, background, foreground);
+}
+
+/*!\internal*/
+void QPalette::setColorGroup(ColorGroup cg, const QBrush &foreground, const QBrush &button,
+ const QBrush &light, const QBrush &dark, const QBrush &mid,
+ const QBrush &text, const QBrush &bright_text,
+ const QBrush &base, const QBrush &alternate_base,
+ const QBrush &background, const QBrush &midlight,
+ const QBrush &button_text, const QBrush &shadow,
+ const QBrush &highlight, const QBrush &highlighted_text,
+ const QBrush &link, const QBrush &link_visited,
+ const QBrush &toolTipBase, const QBrush &toolTipText)
+{
+ detach();
+ setBrush(cg, WindowText, foreground);
+ setBrush(cg, Button, button);
+ setBrush(cg, Light, light);
+ setBrush(cg, Dark, dark);
+ setBrush(cg, Mid, mid);
+ setBrush(cg, Text, text);
+ setBrush(cg, BrightText, bright_text);
+ setBrush(cg, Base, base);
+ setBrush(cg, AlternateBase, alternate_base);
+ setBrush(cg, Window, background);
+ setBrush(cg, Midlight, midlight);
+ setBrush(cg, ButtonText, button_text);
+ setBrush(cg, Shadow, shadow);
+ setBrush(cg, Highlight, highlight);
+ setBrush(cg, HighlightedText, highlighted_text);
+ setBrush(cg, Link, link);
+ setBrush(cg, LinkVisited, link_visited);
+ setBrush(cg, ToolTipBase, toolTipBase);
+ setBrush(cg, ToolTipText, toolTipText);
+}
+
+/*!
+ \fn QPalette QPalette::copy() const
+
+ Use simple assignment instead.
+*/
+
+/*!
+ \fn QColorGroup QPalette::normal() const
+ \obsolete
+
+ Returns the active color group. Use active() instead.
+
+ Use createColorGroup(Active) instead.
+*/
+
+/*!
+ \fn void QPalette::setNormal(const QColorGroup &colorGroup)
+
+ Sets the normal color group to \a colorGroup.
+
+ \sa QColorGroup
+*/
+
+/*!
+ \fn QColorGroup QPalette::active() const
+
+ Returns the active color group.
+ \sa QColorGroup
+*/
+
+/*!
+ \fn QColorGroup QPalette::disabled() const
+
+ Returns the disabled color group.
+ \sa QColorGroup
+*/
+
+/*!
+ \fn QColorGroup QPalette::inactive() const
+
+ Returns the inactive color group.
+ \sa QColorGroup
+*/
+
+/*!
+ \fn void QPalette::setActive(const QColorGroup &colorGroup)
+
+ Sets the active color group to \a colorGroup.
+ \sa QColorGroup
+*/
+
+/*!
+ \fn void QPalette::setDisabled(const QColorGroup &colorGroup)
+
+ Sets the disabled color group to \a colorGroup.
+ \sa QColorGroup
+*/
+
+/*!
+ \fn void QPalette::setInactive(const QColorGroup &colorGroup)
+
+ Sets the inactive color group.
+ \sa QColorGroup
+*/
+
+/*! \class QColorGroup
+ \brief The QColorGroup class contains color groups for each widget state.
+ \compat
+*/
+
+/*! \fn QColorGroup::QColorGroup()
+
+ Use QPalette() instead.
+*/
+
+/*! \fn QColorGroup::QColorGroup(const QBrush &foreground, const QBrush &button, \
+ const QBrush &light, const QBrush &dark, const QBrush &mid, \
+ const QBrush &text, const QBrush &bright_text,
+ const QBrush &base, const QBrush &background)
+
+ Use QPalette(\a foreground, \a button, \a light, \a dark, \a mid,
+ \a text, \a bright_text, \a base, \a background) instead.
+*/
+
+/*! \fn QColorGroup::QColorGroup(const QColor &foreground, const QColor &background, \
+ const QColor &light, const QColor &dark, const QColor &mid, \
+ const QColor &text, const QColor &base)
+
+ Use QColorGroup(\a foreground, \a background, \a light, \a dark,
+ \a mid, \a text, \a base) instead.
+*/
+
+/*! \fn QColorGroup::QColorGroup(const QColorGroup &other)
+
+ Use QPalette(\a other) instead.
+*/
+
+/*! \fn QColorGroup::QColorGroup(const QPalette &pal)
+
+ Use QPalette(\a pal) instead.
+*/
+
+/*! \fn const QColor &QColorGroup::foreground() const
+
+ Use QPalette::windowText().color() instead.
+*/
+
+/*! \fn const QColor &QColorGroup::button() const
+
+ Use QPalette::button().color() instead.
+*/
+
+/*! \fn const QColor &QColorGroup::light() const
+
+ Use QPalette::light().color() instead.
+*/
+
+/*! \fn const QColor &QColorGroup::dark() const
+
+ Use QPalette::dark().color() instead.
+*/
+
+/*! \fn const QColor &QColorGroup::mid() const
+
+ Use QPalette::mid().color() instead.
+*/
+
+/*! \fn const QColor &QColorGroup::text() const
+
+ Use QPalette::text().color() instead.
+*/
+
+/*! \fn const QColor &QColorGroup::base() const
+
+ Use QPalette::base().color() instead.
+*/
+
+/*! \fn const QColor &QColorGroup::background() const
+
+ Use QPalette::window().color() instead.
+*/
+
+/*! \fn const QColor &QColorGroup::midlight() const
+
+ Use QPalette::midlight().color() instead.
+*/
+
+/*! \fn const QColor &QColorGroup::brightText() const
+
+ Use QPalette::brightText().color() instead.
+*/
+
+/*! \fn const QColor &QColorGroup::buttonText() const
+
+ Use QPalette::buttonText().color() instead.
+*/
+
+/*! \fn const QColor &QColorGroup::shadow() const
+
+ Use QPalette::shadow().color() instead.
+*/
+
+/*! \fn const QColor &QColorGroup::highlight() const
+
+ Use QPalette::highlight().color() instead.
+*/
+
+/*! \fn const QColor &QColorGroup::highlightedText() const
+
+ Use QPalette::highlightedText().color() instead.
+*/
+
+/*! \fn const QColor &QColorGroup::link() const
+
+ Use QPalette::link().color() instead.
+*/
+
+/*! \fn const QColor &QColorGroup::linkVisited() const
+
+ Use QPalette::linkVisited().color() instead.
+*/
+
+/*! \fn QDataStream &operator<<(QDataStream &ds, const QColorGroup &colorGroup)
+ \relates QColorGroup
+ \compat
+*/
+
+/*! \fn QDataStream &operator>>(QDataStream &ds, QColorGroup &colorGroup)
+ \relates QColorGroup
+ \compat
+*/
+
+/*! \fn bool QColorGroup::operator==(const QColorGroup &other) const
+
+ Returns true if this color group is equal to \a other; otherwise
+ returns false.
+*/
+
+/*! \fn bool QColorGroup::operator!=(const QColorGroup &other) const
+
+ Returns true if this color group is not equal to \a other;
+ otherwise returns false.
+*/
+
+QT_END_NAMESPACE
diff --git a/src/gui/kernel/qpalette.h b/src/gui/kernel/qpalette.h
new file mode 100644
index 0000000000..e9534c3180
--- /dev/null
+++ b/src/gui/kernel/qpalette.h
@@ -0,0 +1,262 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QPALETTE_H
+#define QPALETTE_H
+
+#include <QtGui/qwindowdefs.h>
+#include <QtGui/qcolor.h>
+#include <QtGui/qbrush.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+#ifdef QT3_SUPPORT
+class QColorGroup;
+#endif
+class QPalettePrivate;
+class QVariant;
+
+class Q_GUI_EXPORT QPalette
+{
+ Q_GADGET
+ Q_ENUMS(ColorGroup ColorRole)
+public:
+ QPalette();
+ QPalette(const QColor &button);
+ QPalette(Qt::GlobalColor button);
+ QPalette(const QColor &button, const QColor &window);
+ QPalette(const QBrush &windowText, const QBrush &button, const QBrush &light,
+ const QBrush &dark, const QBrush &mid, const QBrush &text,
+ const QBrush &bright_text, const QBrush &base, const QBrush &window);
+ QPalette(const QColor &windowText, const QColor &window, const QColor &light,
+ const QColor &dark, const QColor &mid, const QColor &text, const QColor &base);
+#ifdef QT3_SUPPORT
+ QT3_SUPPORT_CONSTRUCTOR QPalette(const QColorGroup &active, const QColorGroup &disabled, const QColorGroup &inactive);
+#endif
+ QPalette(const QPalette &palette);
+ ~QPalette();
+ QPalette &operator=(const QPalette &palette);
+ operator QVariant() const;
+
+ // Do not change the order, the serialization format depends on it
+ enum ColorGroup { Active, Disabled, Inactive, NColorGroups, Current, All, Normal = Active };
+ enum ColorRole { WindowText, Button, Light, Midlight, Dark, Mid,
+ Text, BrightText, ButtonText, Base, Window, Shadow,
+ Highlight, HighlightedText,
+ Link, LinkVisited, // ### Qt 5: remove
+ AlternateBase,
+ NoRole, // ### Qt 5: value should be 0 or -1
+ ToolTipBase, ToolTipText,
+ NColorRoles = ToolTipText + 1,
+ Foreground = WindowText, Background = Window // ### Qt 5: remove
+ };
+
+ inline ColorGroup currentColorGroup() const { return static_cast<ColorGroup>(current_group); }
+ inline void setCurrentColorGroup(ColorGroup cg) { current_group = cg; }
+
+ inline const QColor &color(ColorGroup cg, ColorRole cr) const
+ { return brush(cg, cr).color(); }
+ const QBrush &brush(ColorGroup cg, ColorRole cr) const;
+ inline void setColor(ColorGroup cg, ColorRole cr, const QColor &color);
+ inline void setColor(ColorRole cr, const QColor &color);
+ inline void setBrush(ColorRole cr, const QBrush &brush);
+ bool isBrushSet(ColorGroup cg, ColorRole cr) const;
+ void setBrush(ColorGroup cg, ColorRole cr, const QBrush &brush);
+ void setColorGroup(ColorGroup cr, const QBrush &windowText, const QBrush &button,
+ const QBrush &light, const QBrush &dark, const QBrush &mid,
+ const QBrush &text, const QBrush &bright_text, const QBrush &base,
+ const QBrush &window);
+ bool isEqual(ColorGroup cr1, ColorGroup cr2) const;
+
+ inline const QColor &color(ColorRole cr) const { return color(Current, cr); }
+ inline const QBrush &brush(ColorRole cr) const { return brush(Current, cr); }
+ inline const QBrush &foreground() const { return brush(WindowText); }
+ inline const QBrush &windowText() const { return brush(WindowText); }
+ inline const QBrush &button() const { return brush(Button); }
+ inline const QBrush &light() const { return brush(Light); }
+ inline const QBrush &dark() const { return brush(Dark); }
+ inline const QBrush &mid() const { return brush(Mid); }
+ inline const QBrush &text() const { return brush(Text); }
+ inline const QBrush &base() const { return brush(Base); }
+ inline const QBrush &alternateBase() const { return brush(AlternateBase); }
+ inline const QBrush &toolTipBase() const { return brush(ToolTipBase); }
+ inline const QBrush &toolTipText() const { return brush(ToolTipText); }
+ inline const QBrush &background() const { return brush(Window); }
+ inline const QBrush &window() const { return brush(Window); }
+ inline const QBrush &midlight() const { return brush(Midlight); }
+ inline const QBrush &brightText() const { return brush(BrightText); }
+ inline const QBrush &buttonText() const { return brush(ButtonText); }
+ inline const QBrush &shadow() const { return brush(Shadow); }
+ inline const QBrush &highlight() const { return brush(Highlight); }
+ inline const QBrush &highlightedText() const { return brush(HighlightedText); }
+ inline const QBrush &link() const { return brush(Link); }
+ inline const QBrush &linkVisited() const { return brush(LinkVisited); }
+
+#ifdef QT3_SUPPORT
+ inline QT3_SUPPORT QPalette copy() const { QPalette p = *this; p.detach(); return p; }
+ QT3_SUPPORT QColorGroup normal() const;
+ inline QT3_SUPPORT void setNormal(const QColorGroup &cg) { setColorGroup(Active, cg); }
+
+ QT3_SUPPORT QColorGroup active() const;
+ QT3_SUPPORT QColorGroup disabled() const;
+ QT3_SUPPORT QColorGroup inactive() const;
+ inline QT3_SUPPORT void setActive(const QColorGroup &cg) { setColorGroup(Active, cg); }
+ inline QT3_SUPPORT void setDisabled(const QColorGroup &cg) { setColorGroup(Disabled, cg); }
+ inline QT3_SUPPORT void setInactive(const QColorGroup &cg) { setColorGroup(Inactive, cg); }
+#endif
+
+ bool operator==(const QPalette &p) const;
+ inline bool operator!=(const QPalette &p) const { return !(operator==(p)); }
+ bool isCopyOf(const QPalette &p) const;
+
+ int serialNumber() const;
+ qint64 cacheKey() const;
+
+ QPalette resolve(const QPalette &) const;
+ inline uint resolve() const { return resolve_mask; }
+ inline void resolve(uint mask) { resolve_mask = mask; }
+
+private:
+ void setColorGroup(ColorGroup cr, const QBrush &windowText, const QBrush &button,
+ const QBrush &light, const QBrush &dark, const QBrush &mid,
+ const QBrush &text, const QBrush &bright_text,
+ const QBrush &base, const QBrush &alternate_base,
+ const QBrush &window, const QBrush &midlight,
+ const QBrush &button_text, const QBrush &shadow,
+ const QBrush &highlight, const QBrush &highlighted_text,
+ const QBrush &link, const QBrush &link_visited);
+ void setColorGroup(ColorGroup cr, const QBrush &windowText, const QBrush &button,
+ const QBrush &light, const QBrush &dark, const QBrush &mid,
+ const QBrush &text, const QBrush &bright_text,
+ const QBrush &base, const QBrush &alternate_base,
+ const QBrush &window, const QBrush &midlight,
+ const QBrush &button_text, const QBrush &shadow,
+ const QBrush &highlight, const QBrush &highlighted_text,
+ const QBrush &link, const QBrush &link_visited,
+ const QBrush &toolTipBase, const QBrush &toolTipText);
+#ifdef QT3_SUPPORT
+ friend class QColorGroup;
+ void setColorGroup(ColorGroup, const QColorGroup &);
+ QColorGroup createColorGroup(ColorGroup) const;
+#endif
+ void init();
+ void detach();
+
+ QPalettePrivate *d;
+ uint current_group : 4;
+ uint resolve_mask : 28;
+ friend Q_GUI_EXPORT QDataStream &operator<<(QDataStream &s, const QPalette &p);
+};
+
+inline void QPalette::setColor(ColorGroup acg, ColorRole acr,
+ const QColor &acolor)
+{ setBrush(acg, acr, QBrush(acolor)); }
+inline void QPalette::setColor(ColorRole acr, const QColor &acolor)
+{ setColor(All, acr, acolor); }
+inline void QPalette::setBrush(ColorRole acr, const QBrush &abrush)
+{ setBrush(All, acr, abrush); }
+
+#ifdef QT3_SUPPORT
+class Q_GUI_EXPORT QColorGroup : public QPalette
+{
+public:
+ inline QColorGroup() : QPalette() {}
+ inline QColorGroup(const QBrush &foreground, const QBrush &button, const QBrush &light,
+ const QBrush &dark, const QBrush &mid, const QBrush &text,
+ const QBrush &bright_text, const QBrush &base, const QBrush &background)
+ : QPalette(foreground, button, light, dark, mid, text, bright_text, base, background)
+ {}
+ inline QColorGroup(const QColor &foreground, const QColor &background, const QColor &light,
+ const QColor &dark, const QColor &mid, const QColor &text, const QColor &base)
+ : QPalette(foreground, background, light, dark, mid, text, base) {}
+ inline QColorGroup(const QColorGroup &cg) : QPalette(cg) {}
+ inline QColorGroup(const QPalette &pal) : QPalette(pal) {}
+ bool operator==(const QColorGroup &other) const;
+ inline bool operator!=(const QColorGroup &other) const { return !(operator==(other)); }
+ operator QVariant() const;
+
+ inline QT3_SUPPORT const QColor &foreground() const { return color(WindowText); }
+ inline QT3_SUPPORT const QColor &button() const { return color(Button); }
+ inline QT3_SUPPORT const QColor &light() const { return color(Light); }
+ inline QT3_SUPPORT const QColor &dark() const { return color(Dark); }
+ inline QT3_SUPPORT const QColor &mid() const { return color(Mid); }
+ inline QT3_SUPPORT const QColor &text() const { return color(Text); }
+ inline QT3_SUPPORT const QColor &base() const { return color(Base); }
+ inline QT3_SUPPORT const QColor &background() const { return color(Window); }
+ inline QT3_SUPPORT const QColor &midlight() const { return color(Midlight); }
+ inline QT3_SUPPORT const QColor &brightText() const { return color(BrightText); }
+ inline QT3_SUPPORT const QColor &buttonText() const { return color(ButtonText); }
+ inline QT3_SUPPORT const QColor &shadow() const { return color(Shadow); }
+ inline QT3_SUPPORT const QColor &highlight() const { return color(Highlight); }
+ inline QT3_SUPPORT const QColor &highlightedText() const { return color(HighlightedText); }
+ inline QT3_SUPPORT const QColor &link() const { return color(Link); }
+ inline QT3_SUPPORT const QColor &linkVisited() const { return color(LinkVisited); }
+};
+
+#ifndef QT_NO_DATASTREAM
+Q_GUI_EXPORT QT3_SUPPORT QDataStream &operator<<(QDataStream &ds, const QColorGroup &cg);
+Q_GUI_EXPORT QT3_SUPPORT QDataStream &operator>>(QDataStream &ds, QColorGroup &cg);
+#endif
+
+inline QColorGroup QPalette::inactive() const { return createColorGroup(Inactive); }
+inline QColorGroup QPalette::disabled() const { return createColorGroup(Disabled); }
+inline QColorGroup QPalette::active() const { return createColorGroup(Active); }
+inline QColorGroup QPalette::normal() const { return createColorGroup(Active); }
+
+#endif
+
+/*****************************************************************************
+ QPalette stream functions
+ *****************************************************************************/
+#ifndef QT_NO_DATASTREAM
+Q_GUI_EXPORT QDataStream &operator<<(QDataStream &ds, const QPalette &p);
+Q_GUI_EXPORT QDataStream &operator>>(QDataStream &ds, QPalette &p);
+#endif // QT_NO_DATASTREAM
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QPALETTE_H
diff --git a/src/gui/kernel/qsessionmanager.h b/src/gui/kernel/qsessionmanager.h
new file mode 100644
index 0000000000..560741d662
--- /dev/null
+++ b/src/gui/kernel/qsessionmanager.h
@@ -0,0 +1,111 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSESSIONMANAGER_H
+#define QSESSIONMANAGER_H
+
+#include <QtCore/qobject.h>
+#include <QtGui/qwindowdefs.h>
+#include <QtCore/qstring.h>
+#include <QtCore/qstringlist.h>
+
+#ifndef QT_NO_SESSIONMANAGER
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+class QSessionManagerPrivate;
+
+class Q_GUI_EXPORT QSessionManager : public QObject
+{
+ Q_OBJECT
+ Q_DECLARE_PRIVATE(QSessionManager)
+ QSessionManager(QApplication *app, QString &id, QString &key);
+ ~QSessionManager();
+public:
+ QString sessionId() const;
+ QString sessionKey() const;
+#if defined(Q_WS_X11) || defined(Q_WS_MAC)
+ void *handle() const;
+#endif
+
+ bool allowsInteraction();
+ bool allowsErrorInteraction();
+ void release();
+
+ void cancel();
+
+ enum RestartHint {
+ RestartIfRunning,
+ RestartAnyway,
+ RestartImmediately,
+ RestartNever
+ };
+ void setRestartHint(RestartHint);
+ RestartHint restartHint() const;
+
+ void setRestartCommand(const QStringList&);
+ QStringList restartCommand() const;
+ void setDiscardCommand(const QStringList&);
+ QStringList discardCommand() const;
+
+ void setManagerProperty(const QString& name, const QString& value);
+ void setManagerProperty(const QString& name, const QStringList& value);
+
+ bool isPhase2() const;
+ void requestPhase2();
+
+private:
+ friend class QApplication;
+ friend class QApplicationPrivate;
+ friend class QBaseApplication;
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QT_NO_SESSIONMANAGER
+
+#endif // QSESSIONMANAGER_H
diff --git a/src/gui/kernel/qsessionmanager_qws.cpp b/src/gui/kernel/qsessionmanager_qws.cpp
new file mode 100644
index 0000000000..ae521bb204
--- /dev/null
+++ b/src/gui/kernel/qsessionmanager_qws.cpp
@@ -0,0 +1,171 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <qsessionmanager.h>
+
+#ifndef QT_NO_SESSIONMANAGER
+
+QT_BEGIN_NAMESPACE
+
+class QSessionManagerPrivate : public QObjectPrivate
+{
+public:
+ QSessionManagerPrivate(QSessionManager *m, const QString &id,
+ const QString &key);
+
+ QStringList restartCommand;
+ QStringList discardCommand;
+ const QString sessionId;
+ const QString sessionKey;
+ QSessionManager::RestartHint restartHint;
+};
+
+QSessionManagerPrivate::QSessionManagerPrivate(QSessionManager*,
+ const QString &id,
+ const QString &key)
+ : QObjectPrivate(), sessionId(id), sessionKey(key)
+{
+}
+
+QSessionManager::QSessionManager(QApplication *app, QString &id, QString &key)
+ : QObject(*new QSessionManagerPrivate(this, id, key), app)
+{
+ Q_D(QSessionManager);
+ d->restartHint = RestartIfRunning;
+}
+
+QSessionManager::~QSessionManager()
+{
+}
+
+QString QSessionManager::sessionId() const
+{
+ Q_D(const QSessionManager);
+ return d->sessionId;
+}
+
+QString QSessionManager::sessionKey() const
+{
+ Q_D(const QSessionManager);
+ return d->sessionKey;
+}
+
+
+bool QSessionManager::allowsInteraction()
+{
+ return false;
+}
+
+bool QSessionManager::allowsErrorInteraction()
+{
+ return false;
+}
+
+void QSessionManager::release()
+{
+}
+
+void QSessionManager::cancel()
+{
+}
+
+void QSessionManager::setRestartHint(QSessionManager::RestartHint hint)
+{
+ Q_D(QSessionManager);
+ d->restartHint = hint;
+}
+
+QSessionManager::RestartHint QSessionManager::restartHint() const
+{
+ Q_D(const QSessionManager);
+ return d->restartHint;
+}
+
+void QSessionManager::setRestartCommand(const QStringList &command)
+{
+ Q_D(QSessionManager);
+ d->restartCommand = command;
+}
+
+QStringList QSessionManager::restartCommand() const
+{
+ Q_D(const QSessionManager);
+ return d->restartCommand;
+}
+
+void QSessionManager::setDiscardCommand(const QStringList &command)
+{
+ Q_D(QSessionManager);
+ d->discardCommand = command;
+}
+
+QStringList QSessionManager::discardCommand() const
+{
+ Q_D(const QSessionManager);
+ return d->discardCommand;
+}
+
+void QSessionManager::setManagerProperty(const QString &name,
+ const QString &value)
+{
+ Q_UNUSED(name);
+ Q_UNUSED(value);
+}
+
+void QSessionManager::setManagerProperty(const QString &name,
+ const QStringList &value)
+{
+ Q_UNUSED(name);
+ Q_UNUSED(value);
+}
+
+bool QSessionManager::isPhase2() const
+{
+ return false;
+}
+
+void QSessionManager::requestPhase2()
+{
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_SESSIONMANAGER
diff --git a/src/gui/kernel/qshortcut.cpp b/src/gui/kernel/qshortcut.cpp
new file mode 100644
index 0000000000..50b6e59cd4
--- /dev/null
+++ b/src/gui/kernel/qshortcut.cpp
@@ -0,0 +1,408 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qshortcut.h"
+#include "private/qwidget_p.h"
+
+#ifndef QT_NO_SHORTCUT
+#include <qevent.h>
+#include <qwhatsthis.h>
+#include <qmenu.h>
+#include <qapplication.h>
+#include <private/qapplication_p.h>
+#include <private/qshortcutmap_p.h>
+
+QT_BEGIN_NAMESPACE
+
+#define QAPP_CHECK(functionName) \
+ if (!qApp) { \
+ qWarning("QShortcut: Initialize QApplication before calling '" functionName "'."); \
+ return; \
+ }
+
+/*!
+ \class QShortcut
+ \brief The QShortcut class is used to create keyboard shortcuts.
+
+ \ingroup events
+ \mainclass
+
+ The QShortcut class provides a way of connecting keyboard
+ shortcuts to Qt's \l{signals and slots} mechanism, so that
+ objects can be informed when a shortcut is executed. The shortcut
+ can be set up to contain all the key presses necessary to
+ describe a keyboard shortcut, including the states of modifier
+ keys such as \gui Shift, \gui Ctrl, and \gui Alt.
+
+ \target mnemonic
+
+ On certain widgets, using '&' in front of a character will
+ automatically create a mnemonic (a shortcut) for that character,
+ e.g. "E&xit" will create the shortcut \gui Alt+X (use '&&' to
+ display an actual ampersand). The widget might consume and perform
+ an action on a given shortcut. On X11 the ampersand will not be
+ shown and the character will be underlined. On Windows, shortcuts
+ are normally not displayed until the user presses the \gui Alt
+ key, but this is a setting the user can change. On Mac, shortcuts
+ are disabled by default. Call qt_set_sequence_auto_mnemonic() to
+ enable them. However, because mnemonic shortcuts do not fit in
+ with Aqua's guidelines, Qt will not show the shortcut character
+ underlined.
+
+ For applications that use menus, it may be more convenient to
+ use the convenience functions provided in the QMenu class to
+ assign keyboard shortcuts to menu items as they are created.
+ Alternatively, shortcuts may be associated with other types of
+ actions in the QAction class.
+
+ The simplest way to create a shortcut for a particular widget is
+ to construct the shortcut with a key sequence. For example:
+
+ \snippet doc/src/snippets/code/src_gui_kernel_qshortcut.cpp 0
+
+ When the user types the \l{QKeySequence}{key sequence}
+ for a given shortcut, the shortcut's activated() signal is
+ emitted. (In the case of ambiguity, the activatedAmbiguously()
+ signal is emitted.) A shortcut is "listened for" by Qt's event
+ loop when the shortcut's parent widget is receiving events.
+
+ A shortcut's key sequence can be set with setKey() and retrieved
+ with key(). A shortcut can be enabled or disabled with
+ setEnabled(), and can have "What's This?" help text set with
+ setWhatsThis().
+
+ \sa QShortcutEvent, QKeySequence, QAction
+*/
+
+/*!
+ \fn QWidget *QShortcut::parentWidget() const
+
+ Returns the shortcut's parent widget.
+*/
+
+/*!
+ \fn void QShortcut::activated()
+
+ This signal is emitted when the user types the shortcut's key
+ sequence.
+
+ \sa activatedAmbiguously()
+*/
+
+/*!
+ \fn void QShortcut::activatedAmbiguously()
+
+ When a key sequence is being typed at the keyboard, it is said to
+ be ambiguous as long as it matches the start of more than one
+ shortcut.
+
+ When a shortcut's key sequence is completed,
+ activatedAmbiguously() is emitted if the key sequence is still
+ ambiguous (i.e., it is the start of one or more other shortcuts).
+ The activated() signal is not emitted in this case.
+
+ \sa activated()
+*/
+
+/*
+ \internal
+ Private data accessed through d-pointer.
+*/
+class QShortcutPrivate : public QObjectPrivate
+{
+ Q_DECLARE_PUBLIC(QShortcut)
+public:
+ QShortcutPrivate() : sc_context(Qt::WindowShortcut), sc_enabled(true), sc_autorepeat(true), sc_id(0) {}
+ QKeySequence sc_sequence;
+ Qt::ShortcutContext sc_context;
+ bool sc_enabled;
+ bool sc_autorepeat;
+ int sc_id;
+ QString sc_whatsthis;
+ void redoGrab(QShortcutMap &map);
+};
+
+void QShortcutPrivate::redoGrab(QShortcutMap &map)
+{
+ Q_Q(QShortcut);
+ QWidget *parent = q->parentWidget();
+ if (!parent) {
+ qWarning("QShortcut: No widget parent defined");
+ return;
+ }
+
+ if (sc_id)
+ map.removeShortcut(sc_id, q);
+ if (sc_sequence.isEmpty())
+ return;
+ sc_id = map.addShortcut(q, sc_sequence, sc_context);
+ if (!sc_enabled)
+ map.setShortcutEnabled(false, sc_id, q);
+ if (!sc_autorepeat)
+ map.setShortcutAutoRepeat(false, sc_id, q);
+}
+
+/*!
+ Constructs a QShortcut object for the \a parent widget. Since no
+ shortcut key sequence is specified, the shortcut will not emit any
+ signals.
+
+ \sa setKey()
+*/
+QShortcut::QShortcut(QWidget *parent)
+ : QObject(*new QShortcutPrivate, parent)
+{
+ Q_ASSERT(parent != 0);
+}
+
+/*!
+ Constructs a QShortcut object for the \a parent widget. The shortcut
+ operates on its parent, listening for \l{QShortcutEvent}s that
+ match the \a key sequence. Depending on the ambiguity of the
+ event, the shortcut will call the \a member function, or the \a
+ ambiguousMember function, if the key press was in the shortcut's
+ \a context.
+*/
+QShortcut::QShortcut(const QKeySequence &key, QWidget *parent,
+ const char *member, const char *ambiguousMember,
+ Qt::ShortcutContext context)
+ : QObject(*new QShortcutPrivate, parent)
+{
+ QAPP_CHECK("QShortcut");
+
+ Q_D(QShortcut);
+ Q_ASSERT(parent != 0);
+ d->sc_context = context;
+ d->sc_sequence = key;
+ d->redoGrab(qApp->d_func()->shortcutMap);
+ if (member)
+ connect(this, SIGNAL(activated()), parent, member);
+ if (ambiguousMember)
+ connect(this, SIGNAL(activatedAmbiguously()), parent, ambiguousMember);
+}
+
+/*!
+ Destroys the shortcut.
+*/
+QShortcut::~QShortcut()
+{
+ Q_D(QShortcut);
+ if (qApp)
+ qApp->d_func()->shortcutMap.removeShortcut(d->sc_id, this);
+}
+
+/*!
+ \property QShortcut::key
+ \brief the shortcut's key sequence
+
+ This is a key sequence with an optional combination of Shift, Ctrl,
+ and Alt. The key sequence may be supplied in a number of ways:
+
+ \snippet doc/src/snippets/code/src_gui_kernel_qshortcut.cpp 1
+
+ By default, this property contains an empty key sequence.
+*/
+void QShortcut::setKey(const QKeySequence &key)
+{
+ Q_D(QShortcut);
+ if (d->sc_sequence == key)
+ return;
+ QAPP_CHECK("setKey");
+ d->sc_sequence = key;
+ d->redoGrab(qApp->d_func()->shortcutMap);
+}
+
+QKeySequence QShortcut::key() const
+{
+ Q_D(const QShortcut);
+ return d->sc_sequence;
+}
+
+/*!
+ \property QShortcut::enabled
+ \brief whether the shortcut is enabled
+
+ An enabled shortcut emits the activated() or activatedAmbiguously()
+ signal when a QShortcutEvent occurs that matches the shortcut's
+ key() sequence.
+
+ If the application is in \c WhatsThis mode the shortcut will not emit
+ the signals, but will show the "What's This?" text instead.
+
+ By default, this property is true.
+
+ \sa whatsThis
+*/
+void QShortcut::setEnabled(bool enable)
+{
+ Q_D(QShortcut);
+ if (d->sc_enabled == enable)
+ return;
+ QAPP_CHECK("setEnabled");
+ d->sc_enabled = enable;
+ qApp->d_func()->shortcutMap.setShortcutEnabled(enable, d->sc_id, this);
+}
+
+bool QShortcut::isEnabled() const
+{
+ Q_D(const QShortcut);
+ return d->sc_enabled;
+}
+
+/*!
+ \property QShortcut::context
+ \brief the context in which the shortcut is valid
+
+ A shortcut's context decides in which circumstances a shortcut is
+ allowed to be triggered. The normal context is Qt::WindowShortcut,
+ which allows the shortcut to trigger if the parent (the widget
+ containing the shortcut) is a subwidget of the active top-level
+ window.
+
+ By default, this property is set to Qt::WindowShortcut.
+*/
+void QShortcut::setContext(Qt::ShortcutContext context)
+{
+ Q_D(QShortcut);
+ if(d->sc_context == context)
+ return;
+ QAPP_CHECK("setContext");
+ d->sc_context = context;
+ d->redoGrab(qApp->d_func()->shortcutMap);
+}
+
+Qt::ShortcutContext QShortcut::context()
+{
+ Q_D(QShortcut);
+ return d->sc_context;
+}
+
+/*!
+ \property QShortcut::whatsThis
+ \brief the shortcut's "What's This?" help text
+
+ The text will be shown when the application is in "What's
+ This?" mode and the user types the shortcut key() sequence.
+
+ To set "What's This?" help on a menu item (with or without a
+ shortcut key), set the help on the item's action.
+
+ By default, this property contains an empty string.
+
+ \sa QWhatsThis::inWhatsThisMode(), QAction::setWhatsThis()
+*/
+void QShortcut::setWhatsThis(const QString &text)
+{
+ Q_D(QShortcut);
+ d->sc_whatsthis = text;
+}
+
+QString QShortcut::whatsThis() const
+{
+ Q_D(const QShortcut);
+ return d->sc_whatsthis;
+}
+
+/*!
+ \property QShortcut::autoRepeat
+ \brief whether the shortcut can auto repeat
+ \since 4.2
+
+ If true, the shortcut will auto repeat when the keyboard shortcut
+ combination is held down, provided that keyboard auto repeat is
+ enabled on the system.
+ The default value is true.
+*/
+void QShortcut::setAutoRepeat(bool on)
+{
+ Q_D(QShortcut);
+ if (d->sc_autorepeat == on)
+ return;
+ QAPP_CHECK("setAutoRepeat");
+ d->sc_autorepeat = on;
+ qApp->d_func()->shortcutMap.setShortcutAutoRepeat(on, d->sc_id, this);
+}
+
+bool QShortcut::autoRepeat() const
+{
+ Q_D(const QShortcut);
+ return d->sc_autorepeat;
+}
+
+/*!
+ Returns the shortcut's ID.
+
+ \sa QShortcutEvent::shortcutId()
+*/
+int QShortcut::id() const
+{
+ Q_D(const QShortcut);
+ return d->sc_id;
+}
+
+/*!
+ \internal
+*/
+bool QShortcut::event(QEvent *e)
+{
+ Q_D(QShortcut);
+ bool handled = false;
+ if (d->sc_enabled && e->type() == QEvent::Shortcut) {
+ QShortcutEvent *se = static_cast<QShortcutEvent *>(e);
+ if (se->shortcutId() == d->sc_id && se->key() == d->sc_sequence){
+#ifndef QT_NO_WHATSTHIS
+ if (QWhatsThis::inWhatsThisMode()) {
+ QWhatsThis::showText(QCursor::pos(), d->sc_whatsthis);
+ handled = true;
+ } else
+#endif
+ if (se->isAmbiguous())
+ emit activatedAmbiguously();
+ else
+ emit activated();
+ handled = true;
+ }
+ }
+ return handled;
+}
+#endif // QT_NO_SHORTCUT
+
+QT_END_NAMESPACE
diff --git a/src/gui/kernel/qshortcut.h b/src/gui/kernel/qshortcut.h
new file mode 100644
index 0000000000..1219f5af40
--- /dev/null
+++ b/src/gui/kernel/qshortcut.h
@@ -0,0 +1,107 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSHORTCUT_H
+#define QSHORTCUT_H
+
+#include <QtGui/qwidget.h>
+#include <QtGui/qkeysequence.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+#ifndef QT_NO_SHORTCUT
+
+class QShortcutPrivate;
+class Q_GUI_EXPORT QShortcut : public QObject
+{
+ Q_OBJECT
+ Q_DECLARE_PRIVATE(QShortcut)
+ Q_PROPERTY(QKeySequence key READ key WRITE setKey)
+ Q_PROPERTY(QString whatsThis READ whatsThis WRITE setWhatsThis)
+ Q_PROPERTY(bool enabled READ isEnabled WRITE setEnabled)
+ Q_PROPERTY(bool autoRepeat READ autoRepeat WRITE setAutoRepeat)
+ Q_PROPERTY(Qt::ShortcutContext context READ context WRITE setContext)
+public:
+ explicit QShortcut(QWidget *parent);
+ QShortcut(const QKeySequence& key, QWidget *parent,
+ const char *member = 0, const char *ambiguousMember = 0,
+ Qt::ShortcutContext context = Qt::WindowShortcut);
+ ~QShortcut();
+
+ void setKey(const QKeySequence& key);
+ QKeySequence key() const;
+
+ void setEnabled(bool enable);
+ bool isEnabled() const;
+
+ void setContext(Qt::ShortcutContext context);
+ Qt::ShortcutContext context();
+
+ void setWhatsThis(const QString &text);
+ QString whatsThis() const;
+
+ void setAutoRepeat(bool on);
+ bool autoRepeat() const;
+
+ int id() const;
+
+ inline QWidget *parentWidget() const
+ { return static_cast<QWidget *>(QObject::parent()); }
+
+Q_SIGNALS:
+ void activated();
+ void activatedAmbiguously();
+
+protected:
+ bool event(QEvent *e);
+};
+
+#endif // QT_NO_SHORTCUT
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QSHORTCUT_H
diff --git a/src/gui/kernel/qshortcutmap.cpp b/src/gui/kernel/qshortcutmap.cpp
new file mode 100644
index 0000000000..ed9654b425
--- /dev/null
+++ b/src/gui/kernel/qshortcutmap.cpp
@@ -0,0 +1,901 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qshortcutmap_p.h"
+#include "private/qobject_p.h"
+#include "qkeysequence.h"
+#include "qgraphicsscene.h"
+#include "qgraphicsview.h"
+#include "qdebug.h"
+#include "qevent.h"
+#include "qwidget.h"
+#include "qapplication.h"
+#include "qvector.h"
+#include "qmenu.h"
+#include "qmenubar.h"
+#include "qshortcut.h"
+#include "qapplication_p.h"
+#include <private/qaction_p.h>
+#include <private/qkeymapper_p.h>
+#include <private/qwidget_p.h>
+
+#ifndef QT_NO_SHORTCUT
+
+QT_BEGIN_NAMESPACE
+
+extern bool qt_mac_no_native_menubar; // qmenu_mac.cpp
+
+// To enable verbose output uncomment below
+//#define DEBUG_QSHORTCUTMAP
+
+/* \internal
+ Entry data for QShortcutMap
+ Contains:
+ Keysequence for entry
+ Pointer to parent owning the sequence
+*/
+struct QShortcutEntry
+{
+ QShortcutEntry()
+ : keyseq(0), context(Qt::WindowShortcut), enabled(false), autorepeat(1), id(0), owner(0)
+ {}
+
+ QShortcutEntry(const QKeySequence &k)
+ : keyseq(k), context(Qt::WindowShortcut), enabled(false), autorepeat(1), id(0), owner(0)
+ {}
+
+ QShortcutEntry(QObject *o, const QKeySequence &k, Qt::ShortcutContext c, int i)
+ : keyseq(k), context(c), enabled(true), autorepeat(1), id(i), owner(o)
+ {}
+
+ QShortcutEntry(QObject *o, const QKeySequence &k, Qt::ShortcutContext c, int i, bool a)
+ : keyseq(k), context(c), enabled(true), autorepeat(a), id(i), owner(o)
+ {}
+
+ bool operator<(const QShortcutEntry &f) const
+ { return keyseq < f.keyseq; }
+
+ QKeySequence keyseq;
+ Qt::ShortcutContext context;
+ bool enabled : 1;
+ bool autorepeat : 1;
+ signed int id;
+ QObject *owner;
+};
+
+#ifndef QT_NO_DEBUG_STREAM
+/*! \internal
+ QDebug operator<< for easy debug output of the shortcut entries.
+*/
+QDebug &operator<<(QDebug &dbg, const QShortcutEntry *se) {
+ if (!se)
+ return dbg << "QShortcutEntry(0x0)";
+ dbg.nospace()
+ << "QShortcutEntry(" << se->keyseq
+ << "), id(" << se->id << "), enabled(" << se->enabled << "), autorepeat(" << se->autorepeat
+ << "), owner(" << se->owner << ")";
+ return dbg.space();
+}
+#endif // QT_NO_DEBUGSTREAM
+
+/* \internal
+ Private data for QShortcutMap
+*/
+class QShortcutMapPrivate
+{
+ Q_DECLARE_PUBLIC(QShortcutMap)
+
+public:
+ QShortcutMapPrivate(QShortcutMap* parent)
+ : q_ptr(parent), currentId(0), ambigCount(0), currentState(QKeySequence::NoMatch)
+ {
+ identicals.reserve(10);
+ currentSequences.reserve(10);
+ }
+ QShortcutMap *q_ptr; // Private's parent
+
+ QList<QShortcutEntry> sequences; // All sequences!
+
+ int currentId; // Global shortcut ID number
+ int ambigCount; // Index of last enabled ambiguous dispatch
+ QKeySequence::SequenceMatch currentState;
+ QVector<QKeySequence> currentSequences; // Sequence for the current state
+ QVector<QKeySequence> newEntries;
+ QKeySequence prevSequence; // Sequence for the previous identical match
+ QVector<const QShortcutEntry*> identicals; // Last identical matches
+};
+
+
+/*! \internal
+ QShortcutMap constructor.
+*/
+QShortcutMap::QShortcutMap()
+{
+ d_ptr = new QShortcutMapPrivate(this);
+ Q_ASSERT(d_ptr != 0);
+ resetState();
+}
+
+/*! \internal
+ QShortcutMap destructor.
+*/
+QShortcutMap::~QShortcutMap()
+{
+ delete d_ptr;
+ d_ptr = 0;
+}
+
+/*! \internal
+ Adds a shortcut to the global map.
+ Returns the id of the newly added shortcut.
+*/
+int QShortcutMap::addShortcut(QObject *owner, const QKeySequence &key, Qt::ShortcutContext context)
+{
+ Q_ASSERT_X(owner, "QShortcutMap::addShortcut", "All shortcuts need an owner");
+ Q_ASSERT_X(!key.isEmpty(), "QShortcutMap::addShortcut", "Cannot add keyless shortcuts to map");
+ Q_D(QShortcutMap);
+
+ QShortcutEntry newEntry(owner, key, context, --(d->currentId), true);
+ QList<QShortcutEntry>::iterator it = qUpperBound(d->sequences.begin(), d->sequences.end(), newEntry);
+ d->sequences.insert(it, newEntry); // Insert sorted
+#if defined(DEBUG_QSHORTCUTMAP)
+ qDebug().nospace()
+ << "QShortcutMap::addShortcut(" << owner << ", "
+ << key << ", " << context << ") = " << d->currentId;
+#endif
+ return d->currentId;
+}
+
+/*! \internal
+ Removes a shortcut from the global map.
+ If \a owner is 0, all entries in the map with the key sequence specified
+ is removed. If \a key is null, all sequences for \a owner is removed from
+ the map. If \a id is 0, any identical \a key sequences owned by \a owner
+ are removed.
+ Returns the number of sequences removed from the map.
+*/
+
+int QShortcutMap::removeShortcut(int id, QObject *owner, const QKeySequence &key)
+{
+ Q_D(QShortcutMap);
+ int itemsRemoved = 0;
+ bool allOwners = (owner == 0);
+ bool allKeys = key.isEmpty();
+ bool allIds = id == 0;
+
+ // Special case, remove everything
+ if (allOwners && allKeys && id == 0) {
+ itemsRemoved = d->sequences.size();
+ d->sequences.clear();
+ return itemsRemoved;
+ }
+
+ int i = d->sequences.size()-1;
+ while (i>=0)
+ {
+ const QShortcutEntry &entry = d->sequences.at(i);
+ int entryId = entry.id;
+ if ((allOwners || entry.owner == owner)
+ && (allIds || entry.id == id)
+ && (allKeys || entry.keyseq == key)) {
+ d->sequences.removeAt(i);
+ ++itemsRemoved;
+ }
+ if (id == entryId)
+ return itemsRemoved;
+ --i;
+ }
+#if defined(DEBUG_QSHORTCUTMAP)
+ qDebug().nospace()
+ << "QShortcutMap::removeShortcut(" << id << ", " << owner << ", "
+ << key << ") = " << itemsRemoved;
+#endif
+ return itemsRemoved;
+}
+
+/*! \internal
+ Changes the enable state of a shortcut to \a enable.
+ If \a owner is 0, all entries in the map with the key sequence specified
+ is removed. If \a key is null, all sequences for \a owner is removed from
+ the map. If \a id is 0, any identical \a key sequences owned by \a owner
+ are changed.
+ Returns the number of sequences which are matched in the map.
+*/
+int QShortcutMap::setShortcutEnabled(bool enable, int id, QObject *owner, const QKeySequence &key)
+{
+ Q_D(QShortcutMap);
+ int itemsChanged = 0;
+ bool allOwners = (owner == 0);
+ bool allKeys = key.isEmpty();
+ bool allIds = id == 0;
+
+ int i = d->sequences.size()-1;
+ while (i>=0)
+ {
+ QShortcutEntry entry = d->sequences.at(i);
+ if ((allOwners || entry.owner == owner)
+ && (allIds || entry.id == id)
+ && (allKeys || entry.keyseq == key)) {
+ d->sequences[i].enabled = enable;
+ ++itemsChanged;
+ }
+ if (id == entry.id)
+ return itemsChanged;
+ --i;
+ }
+#if defined(DEBUG_QSHORTCUTMAP)
+ qDebug().nospace()
+ << "QShortcutMap::setShortcutEnabled(" << enable << ", " << id << ", "
+ << owner << ", " << key << ") = " << itemsChanged;
+#endif
+ return itemsChanged;
+}
+
+/*! \internal
+ Changes the auto repeat state of a shortcut to \a enable.
+ If \a owner is 0, all entries in the map with the key sequence specified
+ is removed. If \a key is null, all sequences for \a owner is removed from
+ the map. If \a id is 0, any identical \a key sequences owned by \a owner
+ are changed.
+ Returns the number of sequences which are matched in the map.
+*/
+int QShortcutMap::setShortcutAutoRepeat(bool on, int id, QObject *owner, const QKeySequence &key)
+{
+ Q_D(QShortcutMap);
+ int itemsChanged = 0;
+ bool allOwners = (owner == 0);
+ bool allKeys = key.isEmpty();
+ bool allIds = id == 0;
+
+ int i = d->sequences.size()-1;
+ while (i>=0)
+ {
+ QShortcutEntry entry = d->sequences.at(i);
+ if ((allOwners || entry.owner == owner)
+ && (allIds || entry.id == id)
+ && (allKeys || entry.keyseq == key)) {
+ d->sequences[i].autorepeat = on;
+ ++itemsChanged;
+ }
+ if (id == entry.id)
+ return itemsChanged;
+ --i;
+ }
+#if defined(DEBUG_QSHORTCUTMAP)
+ qDebug().nospace()
+ << "QShortcutMap::setShortcutAutoRepeat(" << on << ", " << id << ", "
+ << owner << ", " << key << ") = " << itemsChanged;
+#endif
+ return itemsChanged;
+}
+
+/*! \internal
+ Resets the state of the statemachine to NoMatch
+*/
+void QShortcutMap::resetState()
+{
+ Q_D(QShortcutMap);
+ d->currentState = QKeySequence::NoMatch;
+ clearSequence(d->currentSequences);
+}
+
+/*! \internal
+ Returns the current state of the statemachine
+*/
+QKeySequence::SequenceMatch QShortcutMap::state()
+{
+ Q_D(QShortcutMap);
+ return d->currentState;
+}
+
+/*! \internal
+ Uses ShortcutOverride event to see if any widgets want to override
+ the event. If not, uses nextState(QKeyEvent) to check for a grabbed
+ Shortcut, and dispatchEvent() is found an identical.
+ \sa nextState dispatchEvent
+*/
+bool QShortcutMap::tryShortcutEvent(QObject *o, QKeyEvent *e)
+{
+ Q_D(QShortcutMap);
+
+ bool wasAccepted = e->isAccepted();
+ bool wasSpontaneous = e->spont;
+ if (d->currentState == QKeySequence::NoMatch) {
+ ushort orgType = e->t;
+ e->t = QEvent::ShortcutOverride;
+ e->ignore();
+ QApplication::sendEvent(o, e);
+ e->t = orgType;
+ e->spont = wasSpontaneous;
+ if (e->isAccepted()) {
+ if (!wasAccepted)
+ e->ignore();
+ return false;
+ }
+ }
+
+ QKeySequence::SequenceMatch result = nextState(e);
+ bool stateWasAccepted = e->isAccepted();
+ if (wasAccepted)
+ e->accept();
+ else
+ e->ignore();
+
+ int identicalMatches = d->identicals.count();
+
+ switch(result) {
+ case QKeySequence::NoMatch:
+ return stateWasAccepted;
+ case QKeySequence::ExactMatch:
+ resetState();
+ dispatchEvent(e);
+ default:
+ break;
+ }
+ // If nextState is QKeySequence::ExactMatch && identicals.count == 0
+ // we've only found disabled shortcuts
+ return identicalMatches > 0 || result == QKeySequence::PartialMatch;
+}
+
+/*! \internal
+ Returns the next state of the statemachine
+ If return value is SequenceMatch::ExactMatch, then a call to matches()
+ will return a QObjects* list of all matching objects for the last matching
+ sequence.
+*/
+QKeySequence::SequenceMatch QShortcutMap::nextState(QKeyEvent *e)
+{
+ Q_D(QShortcutMap);
+ // Modifiers can NOT be shortcuts...
+ if (e->key() >= Qt::Key_Shift &&
+ e->key() <= Qt::Key_Alt)
+ return d->currentState;
+
+ QKeySequence::SequenceMatch result = QKeySequence::NoMatch;
+
+ // We start fresh each time..
+ d->identicals.resize(0);
+
+ result = find(e);
+ if (result == QKeySequence::NoMatch && e->modifiers() & Qt::ShiftModifier) {
+ // If Shift + Key_Backtab, also try Shift + Qt::Key_Tab
+ if (e->key() == Qt::Key_Backtab) {
+ QKeyEvent pe = QKeyEvent(e->type(), Qt::Key_Tab, e->modifiers(), e->text());
+ result = find(&pe);
+ }
+ }
+
+ // Should we eat this key press?
+ if (d->currentState == QKeySequence::PartialMatch
+ || (d->currentState == QKeySequence::ExactMatch && d->identicals.count()))
+ e->accept();
+ // Does the new state require us to clean up?
+ if (result == QKeySequence::NoMatch)
+ clearSequence(d->currentSequences);
+ d->currentState = result;
+
+#if defined(DEBUG_QSHORTCUTMAP)
+ qDebug().nospace() << "QShortcutMap::nextState(" << e << ") = " << result;
+#endif
+ return result;
+}
+
+
+/*! \internal
+ Determines if an enabled shortcut has a matcing key sequence.
+*/
+bool QShortcutMap::hasShortcutForKeySequence(const QKeySequence &seq) const
+{
+ Q_D(const QShortcutMap);
+ QShortcutEntry entry(seq); // needed for searching
+ QList<QShortcutEntry>::ConstIterator itEnd = d->sequences.constEnd();
+ QList<QShortcutEntry>::ConstIterator it = qLowerBound(d->sequences.constBegin(), itEnd, entry);
+
+ for (;it != itEnd; ++it) {
+ if (matches(entry.keyseq, (*it).keyseq) == QKeySequence::ExactMatch && correctContext(*it) && (*it).enabled) {
+ return true;
+ }
+ }
+
+ //end of the loop: we didn't find anything
+ return false;
+}
+
+/*! \internal
+ Returns the next state of the statemachine, based
+ on the new key event \a e.
+ Matches are appended to the vector of identicals,
+ which can be access through matches().
+ \sa matches
+*/
+QKeySequence::SequenceMatch QShortcutMap::find(QKeyEvent *e)
+{
+ Q_D(QShortcutMap);
+ if (!d->sequences.count())
+ return QKeySequence::NoMatch;
+
+ createNewSequences(e, d->newEntries);
+#if defined(DEBUG_QSHORTCUTMAP)
+ qDebug() << "Possible shortcut key sequences:" << d->newEntries;
+#endif
+
+ // Should never happen
+ if (d->newEntries == d->currentSequences) {
+ Q_ASSERT_X(e->key() != Qt::Key_unknown || e->text().length(),
+ "QShortcutMap::find", "New sequence to find identical to previous");
+ return QKeySequence::NoMatch;
+ }
+
+ // Looking for new identicals, scrap old
+ d->identicals.resize(0);
+
+ bool partialFound = false;
+ bool identicalDisabledFound = false;
+ QVector<QKeySequence> okEntries;
+ int result = QKeySequence::NoMatch;
+ for (int i = d->newEntries.count()-1; i >= 0 ; --i) {
+ QShortcutEntry entry(d->newEntries.at(i)); // needed for searching
+ QList<QShortcutEntry>::ConstIterator itEnd = d->sequences.constEnd();
+ QList<QShortcutEntry>::ConstIterator it =
+ qLowerBound(d->sequences.constBegin(), itEnd, entry);
+
+ int oneKSResult = QKeySequence::NoMatch;
+ int tempRes = QKeySequence::NoMatch;
+ do {
+ if (it == itEnd)
+ break;
+ tempRes = matches(entry.keyseq, (*it).keyseq);
+ oneKSResult = qMax(oneKSResult, tempRes);
+ if (tempRes != QKeySequence::NoMatch && correctContext(*it)) {
+ if (tempRes == QKeySequence::ExactMatch) {
+ if ((*it).enabled)
+ d->identicals.append(&*it);
+ else
+ identicalDisabledFound = true;
+ } else if (tempRes == QKeySequence::PartialMatch) {
+ // We don't need partials, if we have identicals
+ if (d->identicals.size())
+ break;
+ // We only care about enabled partials, so we don't consume
+ // key events when all partials are disabled!
+ partialFound |= (*it).enabled;
+ }
+ }
+ ++it;
+ // If we got a valid match on this run, there might still be more keys to check against,
+ // so we'll loop once more. If we get NoMatch, there's guaranteed no more possible
+ // matches in the shortcutmap.
+ } while (tempRes != QKeySequence::NoMatch);
+
+ // If the type of match improves (ergo, NoMatch->Partial, or Partial->Exact), clear the
+ // previous list. If this match is equal or better than the last match, append to the list
+ if (oneKSResult > result) {
+ okEntries.clear();
+#if defined(DEBUG_QSHORTCUTMAP)
+ qDebug() << "Found better match (" << d->newEntries << "), clearing key sequence list";
+#endif
+ }
+ if (oneKSResult && oneKSResult >= result) {
+ okEntries << d->newEntries.at(i);
+#if defined(DEBUG_QSHORTCUTMAP)
+ qDebug() << "Added ok key sequence" << d->newEntries;
+#endif
+ }
+ }
+
+ if (d->identicals.size()) {
+ result = QKeySequence::ExactMatch;
+ } else if (partialFound) {
+ result = QKeySequence::PartialMatch;
+ } else if (identicalDisabledFound) {
+ result = QKeySequence::ExactMatch;
+ } else {
+ clearSequence(d->currentSequences);
+ result = QKeySequence::NoMatch;
+ }
+ if (result != QKeySequence::NoMatch)
+ d->currentSequences = okEntries;
+#if defined(DEBUG_QSHORTCUTMAP)
+ qDebug() << "Returning shortcut match == " << result;
+#endif
+ return QKeySequence::SequenceMatch(result);
+}
+
+/*! \internal
+ Clears \a seq to an empty QKeySequence.
+ Same as doing (the slower)
+ \snippet doc/src/snippets/code/src_gui_kernel_qshortcutmap.cpp 0
+*/
+void QShortcutMap::clearSequence(QVector<QKeySequence> &ksl)
+{
+ ksl.clear();
+ d_func()->newEntries.clear();
+}
+
+/*! \internal
+ Alters \a seq to the new sequence state, based on the
+ current sequence state, and the new key event \a e.
+*/
+void QShortcutMap::createNewSequences(QKeyEvent *e, QVector<QKeySequence> &ksl)
+{
+ Q_D(QShortcutMap);
+ QList<int> possibleKeys = QKeyMapper::possibleKeys(e);
+ int pkTotal = possibleKeys.count();
+ if (!pkTotal)
+ return;
+
+ int ssActual = d->currentSequences.count();
+ int ssTotal = qMax(1, ssActual);
+ // Resize to possible permutations of the current sequence(s).
+ ksl.resize(pkTotal * ssTotal);
+
+ int index = ssActual ? d->currentSequences.at(0).count() : 0;
+ for (int pkNum = 0; pkNum < pkTotal; ++pkNum) {
+ for (int ssNum = 0; ssNum < ssTotal; ++ssNum) {
+ int i = (pkNum * ssTotal) + ssNum;
+ QKeySequence &curKsl = ksl[i];
+ if (ssActual) {
+ const QKeySequence &curSeq = d->currentSequences.at(ssNum);
+ curKsl.setKey(curSeq[0], 0);
+ curKsl.setKey(curSeq[1], 1);
+ curKsl.setKey(curSeq[2], 2);
+ curKsl.setKey(curSeq[3], 3);
+ } else {
+ curKsl.setKey(0, 0);
+ curKsl.setKey(0, 1);
+ curKsl.setKey(0, 2);
+ curKsl.setKey(0, 3);
+ }
+ // Filtering keycode here with 0xdfffffff to ignore the Keypad modifier
+ curKsl.setKey(possibleKeys.at(pkNum) & 0xdfffffff, index);
+ }
+ }
+}
+
+/*! \internal
+ Basically the same function as QKeySequence::matches(const QKeySequence &seq) const
+ only that is specially handles Key_hyphen as Key_Minus, as people mix these up all the time and
+ they conceptually the same.
+*/
+QKeySequence::SequenceMatch QShortcutMap::matches(const QKeySequence &seq1,
+ const QKeySequence &seq2) const
+{
+ uint userN = seq1.count(),
+ seqN = seq2.count();
+
+ if (userN > seqN)
+ return QKeySequence::NoMatch;
+
+ // If equal in length, we have a potential ExactMatch sequence,
+ // else we already know it can only be partial.
+ QKeySequence::SequenceMatch match = (userN == seqN
+ ? QKeySequence::ExactMatch
+ : QKeySequence::PartialMatch);
+
+ for (uint i = 0; i < userN; ++i) {
+ int userKey = seq1[i],
+ sequenceKey = seq2[i];
+ if ((userKey & Qt::Key_unknown) == Qt::Key_hyphen)
+ userKey = (userKey & Qt::KeyboardModifierMask) | Qt::Key_Minus;
+ if ((sequenceKey & Qt::Key_unknown) == Qt::Key_hyphen)
+ sequenceKey = (sequenceKey & Qt::KeyboardModifierMask) | Qt::Key_Minus;
+ if (userKey != sequenceKey)
+ return QKeySequence::NoMatch;
+ }
+ return match;
+}
+
+/*! \internal
+ Returns true if the widget \a w is a logical sub window of the current
+ top-level widget.
+*/
+bool QShortcutMap::correctContext(const QShortcutEntry &item) const {
+ Q_ASSERT_X(item.owner, "QShortcutMap", "Shortcut has no owner. Illegal map state!");
+
+ QWidget *active_window = qApp->activeWindow();
+
+ // popups do not become the active window,
+ // so we fake it here to get the correct context
+ // for the shortcut system.
+ if (qApp->activePopupWidget())
+ active_window = qApp->activePopupWidget();
+
+ if (!active_window)
+ return false;
+#ifndef QT_NO_ACTION
+ if (QAction *a = qobject_cast<QAction *>(item.owner))
+ return correctContext(item.context, a, active_window);
+#endif
+#ifndef QT_NO_GRAPHICSVIEW
+ if (QGraphicsWidget *gw = qobject_cast<QGraphicsWidget *>(item.owner))
+ return correctGraphicsWidgetContext(item.context, gw, active_window);
+#endif
+ QWidget *w = qobject_cast<QWidget *>(item.owner);
+ if (!w) {
+ QShortcut *s = qobject_cast<QShortcut *>(item.owner);
+ w = s->parentWidget();
+ }
+ return correctWidgetContext(item.context, w, active_window);
+}
+
+bool QShortcutMap::correctWidgetContext(Qt::ShortcutContext context, QWidget *w, QWidget *active_window) const
+{
+ bool visible = w->isVisible();
+#ifdef Q_WS_MAC
+ if (!qt_mac_no_native_menubar && qobject_cast<QMenuBar *>(w))
+ visible = true;
+#endif
+
+ if (!visible || !w->isEnabled())
+ return false;
+
+ if (context == Qt::ApplicationShortcut)
+ return QApplicationPrivate::tryModalHelper(w, 0); // true, unless w is shadowed by a modal dialog
+
+ if (context == Qt::WidgetShortcut)
+ return w == QApplication::focusWidget();
+
+ if (context == Qt::WidgetWithChildrenShortcut) {
+ const QWidget *tw = QApplication::focusWidget();
+ while (tw && tw != w && (tw->windowType() == Qt::Widget || tw->windowType() == Qt::Popup))
+ tw = tw->parentWidget();
+ return tw == w;
+ }
+
+ // Below is Qt::WindowShortcut context
+ QWidget *tlw = w->window();
+#ifndef QT_NO_GRAPHICSVIEW
+ if (QWExtra *topData = tlw->d_func()->extra) {
+ if (topData->proxyWidget) {
+ bool res = correctGraphicsWidgetContext(context, (QGraphicsWidget *)topData->proxyWidget, active_window);
+ return res;
+ }
+ }
+#endif
+
+ /* if a floating tool window is active, keep shortcuts on the
+ * parent working */
+ if (active_window != tlw && active_window && active_window->windowType() == Qt::Tool && active_window->parentWidget()) {
+ active_window = active_window->parentWidget()->window();
+ }
+
+ if (active_window != tlw)
+ return false;
+
+ /* if we live in a MDI subwindow, ignore the event if we are
+ not the active document window */
+ const QWidget* sw = w;
+ while (sw && !(sw->windowType() == Qt::SubWindow) && !sw->isWindow())
+ sw = sw->parentWidget();
+ if (sw && (sw->windowType() == Qt::SubWindow)) {
+ QWidget *focus_widget = QApplication::focusWidget();
+ while (focus_widget && focus_widget != sw)
+ focus_widget = focus_widget->parentWidget();
+ return sw == focus_widget;
+ }
+
+#if defined(DEBUG_QSHORTCUTMAP)
+ qDebug().nospace() << "..true [Pass-through]";
+#endif
+ return true;
+}
+
+#ifndef QT_NO_GRAPHICSVIEW
+bool QShortcutMap::correctGraphicsWidgetContext(Qt::ShortcutContext context, QGraphicsWidget *w, QWidget *active_window) const
+{
+ bool visible = w->isVisible();
+#ifdef Q_WS_MAC
+ if (!qt_mac_no_native_menubar && qobject_cast<QMenuBar *>(w))
+ visible = true;
+#endif
+
+ if (!visible || !w->isEnabled() || !w->scene())
+ return false;
+
+ if (context == Qt::ApplicationShortcut) {
+ // Applicationwide shortcuts are always reachable unless their owner
+ // is shadowed by modality. In QGV there's no modality concept, but we
+ // must still check if all views are shadowed.
+ QList<QGraphicsView *> views = w->scene()->views();
+ for (int i = 0; i < views.size(); ++i) {
+ if (QApplicationPrivate::tryModalHelper(views.at(i), 0))
+ return true;
+ }
+ return false;
+ }
+
+ if (context == Qt::WidgetShortcut)
+ return static_cast<QGraphicsItem *>(w) == w->scene()->focusItem();
+
+ if (context == Qt::WidgetWithChildrenShortcut) {
+ const QGraphicsItem *ti = w->scene()->focusItem();
+ if (ti && ti->isWidget()) {
+ const QGraphicsWidget *tw = static_cast<const QGraphicsWidget *>(ti);
+ while (tw && tw != w && (tw->windowType() == Qt::Widget || tw->windowType() == Qt::Popup))
+ tw = tw->parentWidget();
+ return tw == w;
+ }
+ }
+
+ // Below is Qt::WindowShortcut context
+
+ // Find the active view (if any).
+ QList<QGraphicsView *> views = w->scene()->views();
+ QGraphicsView *activeView = 0;
+ for (int i = 0; i < views.size(); ++i) {
+ QGraphicsView *view = views.at(i);
+ if (view->window() == active_window) {
+ activeView = view;
+ break;
+ }
+ }
+ if (!activeView)
+ return false;
+
+ // The shortcut is reachable if owned by a windowless widget, or if the
+ // widget's window is the same as the focus item's window.
+ QGraphicsWidget *a = w->scene()->activeWindow();
+ return !w->window() || a == w->window();
+}
+#endif
+
+#ifndef QT_NO_ACTION
+bool QShortcutMap::correctContext(Qt::ShortcutContext context, QAction *a, QWidget *active_window) const
+{
+ const QList<QWidget *> &widgets = a->d_func()->widgets;
+#if defined(DEBUG_QSHORTCUTMAP)
+ if (widgets.isEmpty())
+ qDebug() << a << "not connected to any widgets; won't trigger";
+#endif
+ for (int i = 0; i < widgets.size(); ++i) {
+ QWidget *w = widgets.at(i);
+#ifndef QT_NO_MENU
+ if (QMenu *menu = qobject_cast<QMenu *>(w)) {
+ QAction *a = menu->menuAction();
+ if (correctContext(context, a, active_window))
+ return true;
+ } else
+#endif
+ if (correctWidgetContext(context, w, active_window))
+ return true;
+ }
+
+#ifndef QT_NO_GRAPHICSVIEW
+ const QList<QGraphicsWidget *> &graphicsWidgets = a->d_func()->graphicsWidgets;
+#if defined(DEBUG_QSHORTCUTMAP)
+ if (graphicsWidgets.isEmpty())
+ qDebug() << a << "not connected to any widgets; won't trigger";
+#endif
+ for (int i = 0; i < graphicsWidgets.size(); ++i) {
+ QGraphicsWidget *w = graphicsWidgets.at(i);
+ if (correctGraphicsWidgetContext(context, w, active_window))
+ return true;
+ }
+#endif
+ return false;
+}
+#endif // QT_NO_ACTION
+
+/*! \internal
+ Converts keyboard button states into modifier states
+*/
+int QShortcutMap::translateModifiers(Qt::KeyboardModifiers modifiers)
+{
+ int result = 0;
+ if (modifiers & Qt::ShiftModifier)
+ result |= Qt::SHIFT;
+ if (modifiers & Qt::ControlModifier)
+ result |= Qt::CTRL;
+ if (modifiers & Qt::MetaModifier)
+ result |= Qt::META;
+ if (modifiers & Qt::AltModifier)
+ result |= Qt::ALT;
+ return result;
+}
+
+/*! \internal
+ Returns the vector of QShortcutEntry's matching the last Identical state.
+*/
+QVector<const QShortcutEntry*> QShortcutMap::matches() const
+{
+ Q_D(const QShortcutMap);
+ return d->identicals;
+}
+
+/*! \internal
+ Dispatches QShortcutEvents to widgets who grabbed the matched key sequence.
+*/
+void QShortcutMap::dispatchEvent(QKeyEvent *e)
+{
+ Q_D(QShortcutMap);
+ if (!d->identicals.size())
+ return;
+
+ const QKeySequence &curKey = d->identicals.at(0)->keyseq;
+ if (d->prevSequence != curKey) {
+ d->ambigCount = 0;
+ d->prevSequence = curKey;
+ }
+ // Find next
+ const QShortcutEntry *current = 0, *next = 0;
+ int i = 0, enabledShortcuts = 0;
+ while(i < d->identicals.size()) {
+ current = d->identicals.at(i);
+ if (current->enabled || !next){
+ ++enabledShortcuts;
+ if (enabledShortcuts > d->ambigCount + 1)
+ break;
+ next = current;
+ }
+ ++i;
+ }
+ d->ambigCount = (d->identicals.size() == i ? 0 : d->ambigCount + 1);
+ // Don't trigger shortcut if we're autorepeating and the shortcut is
+ // grabbed with not accepting autorepeats.
+ if (!next || (e->isAutoRepeat() && !next->autorepeat))
+ return;
+ // Dispatch next enabled
+#if defined(DEBUG_QSHORTCUTMAP)
+ qDebug().nospace()
+ << "QShortcutMap::dispatchEvent(): Sending QShortcutEvent(\""
+ << (QString)next->keyseq << "\", " << next->id << ", "
+ << (bool)(enabledShortcuts>1) << ") to object(" << next->owner << ")";
+#endif
+ QShortcutEvent se(next->keyseq, next->id, enabledShortcuts>1);
+ QApplication::sendEvent(const_cast<QObject *>(next->owner), &se);
+}
+
+/* \internal
+ QShortcutMap dump function, only available when DEBUG_QSHORTCUTMAP is
+ defined.
+*/
+#if defined(Dump_QShortcutMap)
+void QShortcutMap::dumpMap() const
+{
+ Q_D(const QShortcutMap);
+ for (int i = 0; i < d->sequences.size(); ++i)
+ qDebug().nospace() << &(d->sequences.at(i));
+}
+#endif
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_SHORTCUT
diff --git a/src/gui/kernel/qshortcutmap_p.h b/src/gui/kernel/qshortcutmap_p.h
new file mode 100644
index 0000000000..3fe954630f
--- /dev/null
+++ b/src/gui/kernel/qshortcutmap_p.h
@@ -0,0 +1,122 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSHORTCUTMAP_P_H
+#define QSHORTCUTMAP_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 "QtGui/qkeysequence.h"
+#include "QtCore/qvector.h"
+
+QT_BEGIN_NAMESPACE
+
+#ifndef QT_NO_SHORTCUT
+
+// To enable dump output uncomment below
+//#define Dump_QShortcutMap
+
+class QKeyEvent;
+struct QShortcutEntry;
+class QShortcutMapPrivate;
+class QGraphicsWidget;
+class QWidget;
+class QAction;
+class QObject;
+
+class QShortcutMap
+{
+ Q_DECLARE_PRIVATE(QShortcutMap)
+public:
+ QShortcutMap();
+ ~QShortcutMap();
+
+ int addShortcut(QObject *owner, const QKeySequence &key, Qt::ShortcutContext context);
+ int removeShortcut(int id, QObject *owner, const QKeySequence &key = QKeySequence());
+ int setShortcutEnabled(bool enable, int id, QObject *owner, const QKeySequence &key = QKeySequence());
+ int setShortcutAutoRepeat(bool on, int id, QObject *owner, const QKeySequence &key = QKeySequence());
+
+ void resetState();
+ QKeySequence::SequenceMatch nextState(QKeyEvent *e);
+ QKeySequence::SequenceMatch state();
+ void dispatchEvent(QKeyEvent *e);
+ bool tryShortcutEvent(QObject *o, QKeyEvent *e);
+
+#ifdef Dump_QShortcutMap
+ void dumpMap() const;
+#endif
+
+ bool hasShortcutForKeySequence(const QKeySequence &seq) const;
+
+
+private:
+ bool correctWidgetContext(Qt::ShortcutContext context, QWidget *w, QWidget *active_window) const;
+#ifndef QT_NO_GRAPHICSVIEW
+ bool correctGraphicsWidgetContext(Qt::ShortcutContext context, QGraphicsWidget *w, QWidget *active_window) const;
+#endif
+#ifndef QT_NO_ACTION
+ bool correctContext(Qt::ShortcutContext context,QAction *a, QWidget *active_window) const;
+#endif
+ QShortcutMapPrivate *d_ptr;
+
+ QKeySequence::SequenceMatch find(QKeyEvent *e);
+ QKeySequence::SequenceMatch matches(const QKeySequence &seq1, const QKeySequence &seq2) const;
+ QVector<const QShortcutEntry *> matches() const;
+ void createNewSequences(QKeyEvent *e, QVector<QKeySequence> &ksl);
+ void clearSequence(QVector<QKeySequence> &ksl);
+ bool correctContext(const QShortcutEntry &item) const;
+ int translateModifiers(Qt::KeyboardModifiers modifiers);
+};
+
+#endif // QT_NO_SHORTCUT
+
+QT_END_NAMESPACE
+
+#endif // QSHORTCUTMAP_P_H
diff --git a/src/gui/kernel/qsizepolicy.h b/src/gui/kernel/qsizepolicy.h
new file mode 100644
index 0000000000..652fda178d
--- /dev/null
+++ b/src/gui/kernel/qsizepolicy.h
@@ -0,0 +1,225 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSIZEPOLICY_H
+#define QSIZEPOLICY_H
+
+#include <QtCore/qobject.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+class QVariant;
+
+class Q_GUI_EXPORT QSizePolicy
+{
+ Q_GADGET
+ Q_ENUMS(Policy)
+
+private:
+ enum SizePolicyMasks {
+ HSize = 4,
+ HMask = 0x0f,
+ VMask = HMask << HSize,
+ CTShift = 9,
+ CTSize = 5,
+ CTMask = ((0x1 << CTSize) - 1) << CTShift,
+ UnusedShift = CTShift + CTSize,
+ UnusedSize = 2
+ };
+
+public:
+ enum PolicyFlag {
+ GrowFlag = 1,
+ ExpandFlag = 2,
+ ShrinkFlag = 4,
+ IgnoreFlag = 8
+ };
+
+ enum Policy {
+ Fixed = 0,
+ Minimum = GrowFlag,
+ Maximum = ShrinkFlag,
+ Preferred = GrowFlag | ShrinkFlag,
+ MinimumExpanding = GrowFlag | ExpandFlag,
+ Expanding = GrowFlag | ShrinkFlag | ExpandFlag,
+ Ignored = ShrinkFlag | GrowFlag | IgnoreFlag
+ };
+
+ enum ControlType {
+ DefaultType = 0x00000001,
+ ButtonBox = 0x00000002,
+ CheckBox = 0x00000004,
+ ComboBox = 0x00000008,
+ Frame = 0x00000010,
+ GroupBox = 0x00000020,
+ Label = 0x00000040,
+ Line = 0x00000080,
+ LineEdit = 0x00000100,
+ PushButton = 0x00000200,
+ RadioButton = 0x00000400,
+ Slider = 0x00000800,
+ SpinBox = 0x00001000,
+ TabWidget = 0x00002000,
+ ToolButton = 0x00004000
+ };
+ Q_DECLARE_FLAGS(ControlTypes, ControlType)
+
+ QSizePolicy() : data(0) { }
+
+ // ### Qt 5: merge these two constructors (with type == DefaultType)
+ QSizePolicy(Policy horizontal, Policy vertical)
+ : data(horizontal | (vertical << HSize)) { }
+ QSizePolicy(Policy horizontal, Policy vertical, ControlType type)
+ : data(horizontal | (vertical << HSize)) { setControlType(type); }
+
+ Policy horizontalPolicy() const { return static_cast<Policy>(data & HMask); }
+ Policy verticalPolicy() const { return static_cast<Policy>((data & VMask) >> HSize); }
+ ControlType controlType() const;
+
+ void setHorizontalPolicy(Policy d) { data = (data & ~HMask) | d; }
+ void setVerticalPolicy(Policy d) { data = (data & ~(HMask << HSize)) | (d << HSize); }
+ void setControlType(ControlType type);
+
+ Qt::Orientations expandingDirections() const {
+ Qt::Orientations result;
+ if (verticalPolicy() & ExpandFlag)
+ result |= Qt::Vertical;
+ if (horizontalPolicy() & ExpandFlag)
+ result |= Qt::Horizontal;
+ return result;
+ }
+
+ void setHeightForWidth(bool b) { data = b ? (data | (1 << 2*HSize)) : (data & ~(1 << 2*HSize)); }
+ bool hasHeightForWidth() const { return data & (1 << 2*HSize); }
+
+ bool operator==(const QSizePolicy& s) const { return data == s.data; }
+ bool operator!=(const QSizePolicy& s) const { return data != s.data; }
+ operator QVariant() const; // implemented in qabstractlayout.cpp
+
+ int horizontalStretch() const { return data >> 24; }
+ int verticalStretch() const { return (data >> 16) & 0xff; }
+ void setHorizontalStretch(uchar stretchFactor) { data = (data&0x00ffffff) | (uint(stretchFactor)<<24); }
+ void setVerticalStretch(uchar stretchFactor) { data = (data&0xff00ffff) | (uint(stretchFactor)<<16); }
+
+ void transpose();
+
+#ifdef QT3_SUPPORT
+ typedef Policy SizeType;
+#ifndef qdoc
+ typedef Qt::Orientations ExpandData;
+ enum {
+ NoDirection = 0,
+ Horizontally = 1,
+ Vertically = 2,
+ BothDirections = Horizontally | Vertically
+ };
+#else
+ enum ExpandData {
+ NoDirection = 0x0,
+ Horizontally = 0x1,
+ Vertically = 0x2,
+ BothDirections = 0x3
+ };
+#endif // qdoc
+
+ inline QT3_SUPPORT bool mayShrinkHorizontally() const
+ { return horizontalPolicy() & ShrinkFlag; }
+ inline QT3_SUPPORT bool mayShrinkVertically() const { return verticalPolicy() & ShrinkFlag; }
+ inline QT3_SUPPORT bool mayGrowHorizontally() const { return horizontalPolicy() & GrowFlag; }
+ inline QT3_SUPPORT bool mayGrowVertically() const { return verticalPolicy() & GrowFlag; }
+ inline QT3_SUPPORT Qt::Orientations expanding() const { return expandingDirections(); }
+
+ QT3_SUPPORT_CONSTRUCTOR QSizePolicy(Policy hor, Policy ver, bool hfw)
+ : data(hor | (ver<<HSize) | (hfw ? (1U<<2*HSize) : 0)) { }
+
+ QT3_SUPPORT_CONSTRUCTOR QSizePolicy(Policy hor, Policy ver, uchar hors, uchar vers, bool hfw = false)
+ : data(hor | (ver<<HSize) | (hfw ? (1U<<2*HSize) : 0)) {
+ setHorizontalStretch(hors);
+ setVerticalStretch(vers);
+ }
+
+ inline QT3_SUPPORT Policy horData() const { return static_cast<Policy>(data & HMask); }
+ inline QT3_SUPPORT Policy verData() const { return static_cast<Policy>((data & VMask) >> HSize); }
+ inline QT3_SUPPORT void setHorData(Policy d) { setHorizontalPolicy(d); }
+ inline QT3_SUPPORT void setVerData(Policy d) { setVerticalPolicy(d); }
+
+ inline QT3_SUPPORT uint horStretch() const { return horizontalStretch(); }
+ inline QT3_SUPPORT uint verStretch() const { return verticalStretch(); }
+ inline QT3_SUPPORT void setHorStretch(uchar sf) { setHorizontalStretch(sf); }
+ inline QT3_SUPPORT void setVerStretch(uchar sf) { setVerticalStretch(sf); }
+#endif
+
+private:
+#ifndef QT_NO_DATASTREAM
+ friend Q_GUI_EXPORT QDataStream &operator<<(QDataStream &, const QSizePolicy &);
+ friend Q_GUI_EXPORT QDataStream &operator>>(QDataStream &, QSizePolicy &);
+#endif
+ QSizePolicy(int i) : data(i) { }
+
+ quint32 data;
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(QSizePolicy::ControlTypes)
+
+// implemented in qlayout.cpp
+Q_GUI_EXPORT QDataStream &operator<<(QDataStream &, const QSizePolicy &);
+Q_GUI_EXPORT QDataStream &operator>>(QDataStream &, QSizePolicy &);
+
+inline void QSizePolicy::transpose() {
+ Policy hData = horizontalPolicy();
+ Policy vData = verticalPolicy();
+ uchar hStretch = horizontalStretch();
+ uchar vStretch = verticalStretch();
+ setHorizontalPolicy(vData);
+ setVerticalPolicy(hData);
+ setHorizontalStretch(vStretch);
+ setVerticalStretch(hStretch);
+}
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QSIZEPOLICY_H
diff --git a/src/gui/kernel/qsound.cpp b/src/gui/kernel/qsound.cpp
new file mode 100644
index 0000000000..fb152cfa40
--- /dev/null
+++ b/src/gui/kernel/qsound.cpp
@@ -0,0 +1,386 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qsound.h"
+
+#ifndef QT_NO_SOUND
+
+#include "qlist.h"
+#include <private/qobject_p.h>
+#include "qsound_p.h"
+
+QT_BEGIN_NAMESPACE
+
+static QList<QAuServer*> *servers=0;
+
+QAuServer::QAuServer(QObject* parent)
+ : QObject(parent)
+{
+ if (!servers)
+ servers = new QList<QAuServer*>;
+ servers->prepend(this);
+}
+
+QAuServer::~QAuServer()
+{
+ servers->removeAll(this);
+ if (servers->count() == 0) {
+ delete servers;
+ servers = 0;
+ }
+}
+
+void QAuServer::play(const QString& filename)
+{
+ QSound s(filename);
+ play(&s);
+}
+
+extern QAuServer* qt_new_audio_server();
+
+static QAuServer& server()
+{
+ if (!servers) qt_new_audio_server();
+ return *servers->first();
+}
+
+class QSoundPrivate : public QObjectPrivate
+{
+public:
+ QSoundPrivate(const QString& fname)
+ : filename(fname), bucket(0), looprem(0), looptotal(1)
+ {
+ }
+
+ ~QSoundPrivate()
+ {
+ delete bucket;
+ }
+
+ QString filename;
+ QAuBucket* bucket;
+ int looprem;
+ int looptotal;
+};
+
+/*!
+ \class QSound
+ \brief The QSound class provides access to the platform audio facilities.
+
+ \ingroup multimedia
+ \mainclass
+
+ Qt provides the most commonly required audio operation in GUI
+ applications: asynchronously playing a sound file. This is most
+ easily accomplished using the static play() function:
+
+ \snippet doc/src/snippets/code/src_gui_kernel_qsound.cpp 0
+
+ Alternatively, create a QSound object from the sound file first
+ and then call the play() slot:
+
+ \snippet doc/src/snippets/code/src_gui_kernel_qsound.cpp 1
+
+ Once created a QSound object can be queried for its fileName() and
+ total number of loops() (i.e. the number of times the sound will
+ play). The number of repetitions can be altered using the
+ setLoops() function. While playing the sound, the loopsRemaining()
+ function returns the remaining number of repetitions. Use the
+ isFinished() function to determine whether the sound has finished
+ playing.
+
+ Sounds played using a QSound object may use more memory than the
+ static play() function, but it may also play more immediately
+ (depending on the underlying platform audio facilities). Use the
+ static isAvailable() function to determine whether sound
+ facilities exist on the platform. Which facilities that are
+ actually used varies:
+
+ \table
+ \header \o Platform \o Audio Facility
+ \row
+ \o Microsoft Windows
+ \o The underlying multimedia system is used; only WAVE format sound files
+ are supported.
+ \row
+ \o X11
+ \o The \l{ftp://ftp.x.org/contrib/audio/nas/}{Network Audio System}
+ is used if available, otherwise all operations work silently. NAS
+ supports WAVE and AU files.
+ \row
+ \o Mac OS X
+ \o NSSound is used. All formats that NSSound supports, including QuickTime formats,
+ are supported by Qt for Mac OS X.
+ \row
+ \o Qt for Embedded Linux
+ \o A built-in mixing sound server is used, accessing \c /dev/dsp
+ directly. Only the WAVE format is supported.
+ \endtable
+
+ Note that QSound does not support \l{resources.html}{resources}.
+ This might be fixed in a future Qt version.
+*/
+
+/*!
+ Plays the sound stored in the file specified by the given \a filename.
+
+ \sa stop(), loopsRemaining(), isFinished()
+*/
+void QSound::play(const QString& filename)
+{
+ server().play(filename);
+}
+
+/*!
+ Constructs a QSound object from the file specified by the given \a
+ filename and with the given \a parent.
+
+ This may use more memory than the static play() function, but it
+ may also play more immediately (depending on the underlying
+ platform audio facilities).
+
+ \sa play()
+*/
+QSound::QSound(const QString& filename, QObject* parent)
+ : QObject(*new QSoundPrivate(filename), parent)
+{
+ server().init(this);
+}
+
+#ifdef QT3_SUPPORT
+/*!
+ \obsolete
+
+ Constructs a QSound object from the file specified by the given \a
+ filename and with the given \a parent and \a name. Use the
+ QSound() construcor and QObject::setObjectName() instead.
+
+ \oldcode
+ QSound *mySound = new QSound(filename, parent, name);
+ \newcode
+ QSounc *mySound = new QSound(filename, parent);
+ mySound->setObjectName(name);
+ \endcode
+*/
+QSound::QSound(const QString& filename, QObject* parent, const char* name)
+ : QObject(*new QSoundPrivate(filename), parent)
+{
+ setObjectName(QString::fromAscii(name));
+ server().init(this);
+}
+#endif
+
+/*!
+ Destroys this sound object. If the sound is not finished playing,
+ the stop() function is called before the sound object is
+ destructed.
+
+ \sa stop(), isFinished()
+*/
+QSound::~QSound()
+{
+ if (!isFinished())
+ stop();
+}
+
+/*!
+ Returns true if the sound has finished playing; otherwise returns false.
+
+ \warning On Windows this function always returns true for unlooped sounds.
+*/
+bool QSound::isFinished() const
+{
+ Q_D(const QSound);
+ return d->looprem == 0;
+}
+
+/*!
+ \overload
+
+ Starts playing the sound specified by this QSound object.
+
+ The function returns immediately. Depending on the platform audio
+ facilities, other sounds may stop or be mixed with the new
+ sound. The sound can be played again at any time, possibly mixing
+ or replacing previous plays of the sound.
+
+ \sa fileName()
+*/
+void QSound::play()
+{
+ Q_D(QSound);
+ d->looprem = d->looptotal;
+ server().play(this);
+}
+
+/*!
+ Returns the number of times the sound will play.
+
+ \sa loopsRemaining(), setLoops()
+*/
+int QSound::loops() const
+{
+ Q_D(const QSound);
+ return d->looptotal;
+}
+
+/*!
+ Returns the remaining number of times the sound will loop (this
+ value decreases each time the sound is played).
+
+ \sa loops(), isFinished()
+*/
+int QSound::loopsRemaining() const
+{
+ Q_D(const QSound);
+ return d->looprem;
+}
+
+/*!
+ \fn void QSound::setLoops(int number)
+
+ Sets the sound to repeat the given \a number of times when it is
+ played.
+
+ Note that passing the value -1 will cause the sound to loop
+ indefinitely.
+
+ \sa loops()
+*/
+void QSound::setLoops(int n)
+{
+ Q_D(QSound);
+ d->looptotal = n;
+}
+
+/*!
+ Returns the filename associated with this QSound object.
+
+ \sa QSound()
+*/
+QString QSound::fileName() const
+{
+ Q_D(const QSound);
+ return d->filename;
+}
+
+/*!
+ Stops the sound playing.
+
+ Note that on Windows the current loop will finish if a sound is
+ played in a loop.
+
+ \sa play()
+*/
+void QSound::stop()
+{
+ Q_D(QSound);
+ server().stop(this);
+ d->looprem = 0;
+}
+
+
+/*!
+ Returns true if sound facilities exist on the platform; otherwise
+ returns false.
+
+ If no sound is available, all QSound operations work silently and
+ quickly. An application may choose either to notify the user if
+ sound is crucial to the application or to operate silently without
+ bothering the user.
+
+ Note: On Windows this always returns true because some sound card
+ drivers do not implement a way to find out whether it is available
+ or not.
+*/
+bool QSound::isAvailable()
+{
+ return server().okay();
+}
+
+/*!
+ Sets the internal bucket record of sound \a s to \a b, deleting
+ any previous setting.
+*/
+void QAuServer::setBucket(QSound* s, QAuBucket* b)
+{
+ delete s->d_func()->bucket;
+ s->d_func()->bucket = b;
+}
+
+/*!
+ Returns the internal bucket record of sound \a s.
+*/
+QAuBucket* QAuServer::bucket(QSound* s)
+{
+ return s->d_func()->bucket;
+}
+
+/*!
+ Decrements the QSound::loopRemaining() value for sound \a s,
+ returning the result.
+*/
+int QAuServer::decLoop(QSound* s)
+{
+ if (s->d_func()->looprem > 0)
+ --s->d_func()->looprem;
+ return s->d_func()->looprem;
+}
+
+/*!
+ Initializes the sound. The default implementation does nothing.
+*/
+void QAuServer::init(QSound*)
+{
+}
+
+QAuBucket::~QAuBucket()
+{
+}
+/*!
+ \fn bool QSound::available()
+
+ Use the isAvailable() function instead.
+*/
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_SOUND
diff --git a/src/gui/kernel/qsound.h b/src/gui/kernel/qsound.h
new file mode 100644
index 0000000000..3a9465313c
--- /dev/null
+++ b/src/gui/kernel/qsound.h
@@ -0,0 +1,95 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSOUND_H
+#define QSOUND_H
+
+#include <QtCore/qobject.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+#ifndef QT_NO_SOUND
+
+class QSoundPrivate;
+
+class Q_GUI_EXPORT QSound : public QObject
+{
+ Q_OBJECT
+
+public:
+ static bool isAvailable();
+ static void play(const QString& filename);
+
+ explicit QSound(const QString& filename, QObject* parent = 0);
+ ~QSound();
+
+ int loops() const;
+ int loopsRemaining() const;
+ void setLoops(int);
+ QString fileName() const;
+
+ bool isFinished() const;
+
+public Q_SLOTS:
+ void play();
+ void stop();
+
+public:
+#ifdef QT3_SUPPORT
+ QT3_SUPPORT_CONSTRUCTOR QSound(const QString& filename, QObject* parent, const char* name);
+ static inline QT3_SUPPORT bool available() { return isAvailable(); }
+#endif
+private:
+ Q_DECLARE_PRIVATE(QSound)
+ friend class QAuServer;
+};
+
+#endif // QT_NO_SOUND
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QSOUND_H
diff --git a/src/gui/kernel/qsound_mac.mm b/src/gui/kernel/qsound_mac.mm
new file mode 100644
index 0000000000..a58d48bf25
--- /dev/null
+++ b/src/gui/kernel/qsound_mac.mm
@@ -0,0 +1,184 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#include <qapplication.h>
+#include "qsound.h"
+#include "qsound_p.h"
+#include <private/qt_mac_p.h>
+#include <qhash.h>
+#include <qdebug.h>
+#import <AppKit/AppKit.h>
+
+#include <AppKit/NSSound.h>
+
+QT_BEGIN_NAMESPACE
+
+void qt_mac_beep()
+{
+ NSBeep();
+}
+
+QT_END_NAMESPACE
+
+#ifndef QT_NO_SOUND
+
+QT_BEGIN_NAMESPACE
+
+typedef QHash<QSound *, NSSound const *> Sounds;
+static Sounds sounds;
+
+class QAuServerMac : public QAuServer
+{
+ Q_OBJECT
+public:
+ QAuServerMac(QObject* parent) : QAuServer(parent) { }
+ void play(const QString& filename);
+ void play(QSound *s);
+ void stop(QSound*);
+ bool okay() { return true; }
+ using QAuServer::decLoop; // promote to public.
+protected:
+ NSSound *createNSSound(const QString &filename, QSound *qSound);
+};
+
+QT_END_NAMESPACE
+
+QT_USE_NAMESPACE
+
+@interface QMacSoundDelegate : NSObject {
+ QSound *qSound; // may be null.
+ QAuServerMac* server;
+}
+-(id)initWithQSound:(QSound*)sound:(QAuServerMac*)server;
+-(void)sound:(NSSound *)sound didFinishPlaying:(BOOL)aBool;
+@end
+
+@implementation QMacSoundDelegate
+-(id)initWithQSound:(QSound*)s:(QAuServerMac*)serv {
+ self = [super init];
+ if(self) {
+ qSound = s;
+ server = serv;
+ }
+ return self;
+}
+
+// Delegate function that gets called each time a sound finishes.
+-(void)sound:(NSSound *)sound didFinishPlaying:(BOOL)finishedOk
+{
+ // qSound is null if this sound was started by play(QString),
+ // in which case there is no corresponding QSound object.
+ if (qSound == 0) {
+ [sound release];
+ [self release];
+ return;
+ }
+
+ // finishedOk is false if the sound cold not be played or
+ // if it was interrupted by stop().
+ if (finishedOk == false) {
+ sounds.remove(qSound);
+ [sound release];
+ [self release];
+ return;
+ }
+
+ // Check if the sound should loop "forever" (until stop).
+ if (qSound->loops() == -1) {
+ [sound play];
+ return;
+ }
+
+ const int remainingIterations = server->decLoop(qSound);
+ if (remainingIterations > 0) {
+ [sound play];
+ } else {
+ sounds.remove(qSound);
+ [sound release];
+ [self release];
+ }
+}
+@end
+
+QT_BEGIN_NAMESPACE
+
+void QAuServerMac::play(const QString &fileName)
+{
+ QMacCocoaAutoReleasePool pool;
+ NSSound * const nsSound = createNSSound(fileName, 0);
+ [nsSound play];
+}
+
+void QAuServerMac::play(QSound *qSound)
+{
+ QMacCocoaAutoReleasePool pool;
+ NSSound * const nsSound = createNSSound(qSound->fileName(), qSound);
+ [nsSound play];
+ // Keep track of the nsSound object so we can find it again in stop().
+ sounds[qSound] = nsSound;
+}
+
+void QAuServerMac::stop(QSound *qSound)
+{
+ Sounds::const_iterator it = sounds.constFind(qSound);
+ if (it != sounds.constEnd())
+ [*it stop];
+}
+
+// Creates an NSSound object and installs a "sound finished" callack delegate on it.
+NSSound *QAuServerMac::createNSSound(const QString &fileName, QSound *qSound)
+{
+ NSString *nsFileName = const_cast<NSString *>(reinterpret_cast<const NSString *>(QCFString::toCFStringRef(fileName)));
+ NSSound * const nsSound = [[NSSound alloc] initWithContentsOfFile: nsFileName byReference:YES];
+ QMacSoundDelegate * const delegate = [[QMacSoundDelegate alloc] initWithQSound:qSound:this];
+ [nsSound setDelegate:delegate];
+ return nsSound;
+}
+
+QAuServer* qt_new_audio_server()
+{
+ return new QAuServerMac(qApp);
+}
+
+QT_END_NAMESPACE
+
+#include "qsound_mac.moc"
+
+#endif // QT_NO_SOUND
diff --git a/src/gui/kernel/qsound_p.h b/src/gui/kernel/qsound_p.h
new file mode 100644
index 0000000000..44f5c033e0
--- /dev/null
+++ b/src/gui/kernel/qsound_p.h
@@ -0,0 +1,100 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSOUND_P_H
+#define QSOUND_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/qobject.h"
+
+QT_BEGIN_NAMESPACE
+
+#ifndef QT_NO_SOUND
+
+class QSound;
+/*
+ QAuServer is an INTERNAL class. If you wish to provide support for
+ additional audio servers, you can make a subclass of QAuServer to do
+ so, HOWEVER, your class may need to be re-engineered to some degree
+ with each new Qt release, including minor releases.
+
+ QAuBucket is whatever you want.
+*/
+
+class QAuBucket {
+public:
+ virtual ~QAuBucket();
+};
+
+class QAuServer : public QObject {
+ Q_OBJECT
+
+public:
+ explicit QAuServer(QObject* parent);
+ ~QAuServer();
+
+ virtual void init(QSound*);
+ virtual void play(const QString& filename);
+ virtual void play(QSound*)=0;
+ virtual void stop(QSound*)=0;
+ virtual bool okay()=0;
+
+protected:
+ void setBucket(QSound*, QAuBucket*);
+ QAuBucket* bucket(QSound*);
+ int decLoop(QSound*);
+};
+
+#endif // QT_NO_SOUND
+
+QT_END_NAMESPACE
+
+#endif // QSOUND_P_H
diff --git a/src/gui/kernel/qsound_qws.cpp b/src/gui/kernel/qsound_qws.cpp
new file mode 100644
index 0000000000..e83935fce0
--- /dev/null
+++ b/src/gui/kernel/qsound_qws.cpp
@@ -0,0 +1,350 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qapplication.h"
+
+#ifndef QT_NO_SOUND
+
+#include "qsound.h"
+#include "qpaintdevice.h"
+#include "qwsdisplay_qws.h"
+#include "qsound_p.h"
+
+#include "qsoundqss_qws.h"
+
+#include "qhash.h"
+#include "qfileinfo.h"
+
+#ifdef MEDIA_SERVER
+#include "qbytearray.h"
+#include "quuid.h"
+#include "qdatastream.h"
+#include "qcopchannel_qws.h"
+#include "qbuffer.h"
+
+QT_BEGIN_NAMESPACE
+
+QT_USE_NAMESPACE
+
+#define SERVER_CHANNEL "QPE/MediaServer"
+
+class QCopMessage : public QDataStream
+{
+public:
+ QCopMessage( const QString& channel, const QString& message )
+ : QDataStream( new QBuffer ), m_channel( channel ), m_message( message )
+ {
+ device()->open( QIODevice::WriteOnly );
+ }
+
+ ~QCopMessage()
+ {
+ QCopChannel::send( m_channel, m_message, ((QBuffer*)device())->buffer() );
+ delete device();
+ }
+
+private:
+ QString m_channel;
+ QString m_message;
+};
+
+#endif // MEDIA_SERVER
+
+class QAuServerQWS;
+
+class QAuBucketQWS : public QObject, public QAuBucket
+{
+ Q_OBJECT
+public:
+ QAuBucketQWS( QAuServerQWS*, QSound*, QObject* parent = 0 );
+
+ ~QAuBucketQWS();
+
+#ifndef MEDIA_SERVER
+ int id() const { return id_; }
+#endif
+
+ QSound* sound() const { return sound_; }
+
+#ifdef MEDIA_SERVER
+ void play();
+
+ void stop();
+#endif
+
+signals:
+ // Only for Media Server
+ void done( QAuBucketQWS* );
+
+private slots:
+ // Only for Media Server
+ void processMessage( const QString& msg, const QByteArray& data );
+
+private:
+#ifdef MEDIA_SERVER
+ QCopChannel *m_channel;
+ QUuid m_id;
+#endif
+
+#ifndef MEDIA_SERVER
+ int id_;
+#endif
+ QSound *sound_;
+ QAuServerQWS *server_;
+
+ static int next;
+};
+
+int QAuBucketQWS::next = 0;
+
+class QAuServerQWS : public QAuServer
+{
+ Q_OBJECT
+public:
+ QAuServerQWS( QObject* parent );
+
+ void init( QSound* s )
+ {
+ QAuBucketQWS *bucket = new QAuBucketQWS( this, s );
+#ifdef MEDIA_SERVER
+ connect( bucket, SIGNAL(done(QAuBucketQWS*)),
+ this, SLOT(complete(QAuBucketQWS*)) );
+#endif
+ setBucket( s, bucket );
+ }
+
+#ifndef MEDIA_SERVER
+ // Register bucket
+ void insert( QAuBucketQWS *bucket )
+ {
+ buckets.insert( bucket->id(), bucket );
+ }
+
+ // Remove bucket from register
+ void remove( QAuBucketQWS *bucket )
+ {
+ buckets.remove( bucket->id() );
+ }
+#endif
+
+ void play( QSound* s )
+ {
+ QString filepath = QFileInfo( s->fileName() ).absoluteFilePath();
+#if defined(QT_NO_QWS_SOUNDSERVER)
+ server->playFile( bucket( s )->id(), filepath );
+#elif defined(MEDIA_SERVER)
+ bucket( s )->play();
+#else
+ client->play( bucket( s )->id(), filepath );
+#endif
+ }
+
+ void stop( QSound* s )
+ {
+#if defined(QT_NO_QWS_SOUNDSERVER)
+ server->stopFile( bucket( s )->id() );
+#elif defined(MEDIA_SERVER)
+ bucket( s )->stop();
+#else
+ client->stop( bucket( s )->id() );
+#endif
+ }
+
+ bool okay() { return true; }
+
+private slots:
+ // Continue playing sound if loops remain
+ void complete( int id )
+ {
+#ifndef MEDIA_SERVER
+ QAuBucketQWS *bucket = find( id );
+ if( bucket ) {
+ QSound *sound = bucket->sound();
+ if( decLoop( sound ) ) {
+ play( sound );
+ }
+ }
+#else
+ Q_UNUSED(id);
+#endif
+ }
+
+ // Only for Media Server
+ void complete( QAuBucketQWS* bucket )
+ {
+#ifndef MEDIA_SERVER
+ Q_UNUSED(bucket);
+#else
+ QSound *sound = bucket->sound();
+ if( decLoop( sound ) ) {
+ play( sound );
+ }
+#endif
+ }
+
+protected:
+ QAuBucketQWS* bucket( QSound *s )
+ {
+ return (QAuBucketQWS*)QAuServer::bucket( s );
+ }
+
+private:
+#ifndef MEDIA_SERVER
+ // Find registered bucket with given id, return null if none found
+ QAuBucketQWS* find( int id )
+ {
+ QHash<int, QAuBucketQWS*>::Iterator it = buckets.find( id );
+ if( it != buckets.end() ) {
+ return it.value();
+ }
+
+ return 0;
+ }
+
+ QHash<int, QAuBucketQWS*> buckets; // ### possible problem with overlapping keys
+
+#ifdef QT_NO_QWS_SOUNDSERVER
+ QWSSoundServer *server;
+#else
+ QWSSoundClient *client;
+#endif
+
+#endif // MEDIA_SERVER
+};
+
+QAuServerQWS::QAuServerQWS(QObject* parent) :
+ QAuServer(parent)
+{
+#ifndef MEDIA_SERVER
+ setObjectName(QLatin1String("qauserverqws"));
+
+#ifdef QT_NO_QWS_SOUNDSERVER
+ server = new QWSSoundServer( this ); // ### only suitable for single application
+
+ connect( server, SIGNAL(soundCompleted(int)),
+ this, SLOT(complete(int)) );
+#else
+ client = new QWSSoundClient( this ); // ### requires successful connection
+
+ connect( client, SIGNAL(soundCompleted(int)),
+ this, SLOT(complete(int)) );
+#endif
+
+#endif // MEDIA_SERVER
+}
+
+QAuBucketQWS::QAuBucketQWS( QAuServerQWS *server, QSound *sound, QObject* parent )
+ : QObject( parent ), sound_( sound ), server_( server )
+{
+#ifdef MEDIA_SERVER
+ m_id = QUuid::createUuid();
+
+ sound->setObjectName( m_id.toString() );
+
+ m_channel = new QCopChannel(QString( QLatin1String("QPE/QSound/") ).append( m_id ), this );
+ connect( m_channel, SIGNAL(received(QString,QByteArray)),
+ this, SLOT(processMessage(QString,QByteArray)) );
+
+ {
+ QCopMessage message( QLatin1String(SERVER_CHANNEL), QLatin1String("subscribe(QUuid)") );
+ message << m_id;
+ }
+
+ {
+ QString filepath = QFileInfo( sound_->fileName() ).absoluteFilePath();
+ QCopMessage message( QLatin1String(SERVER_CHANNEL), QLatin1String("open(QUuid,QString)") );
+ message << m_id << filepath;
+ }
+#else
+ id_ = next++;
+ server_->insert( this );
+#endif
+}
+
+#ifdef MEDIA_SERVER
+void QAuBucketQWS::play()
+{
+ QString filepath = QFileInfo( sound_->fileName() ).absoluteFilePath();
+
+ QCopMessage message( QLatin1String(SERVER_CHANNEL), QLatin1String("play(QUuid)") );
+ message << m_id;
+}
+
+void QAuBucketQWS::stop()
+{
+ QCopMessage message( QLatin1String(SERVER_CHANNEL), QLatin1String("stop(QUuid)") );
+ message << m_id;
+}
+#endif // MEDIA_SERVER
+
+void QAuBucketQWS::processMessage( const QString& msg, const QByteArray& data )
+{
+ Q_UNUSED(data);
+#ifndef MEDIA_SERVER
+ Q_UNUSED(msg);
+#else
+ if( msg == QLatin1String("done()") ) {
+ emit done( this );
+ }
+#endif
+}
+
+QAuBucketQWS::~QAuBucketQWS()
+{
+#ifdef MEDIA_SERVER
+ QCopMessage message( QLatin1String(SERVER_CHANNEL), QLatin1String("revoke(QUuid)") );
+ message << m_id;
+#else
+ server_->remove( this );
+#endif
+}
+
+
+QAuServer* qt_new_audio_server()
+{
+ return new QAuServerQWS(qApp);
+}
+
+#include "qsound_qws.moc"
+
+#endif // QT_NO_SOUND
+
+QT_END_NAMESPACE
diff --git a/src/gui/kernel/qsound_win.cpp b/src/gui/kernel/qsound_win.cpp
new file mode 100644
index 0000000000..9d03d18435
--- /dev/null
+++ b/src/gui/kernel/qsound_win.cpp
@@ -0,0 +1,219 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qsound.h"
+
+#ifndef QT_NO_SOUND
+
+#include "qapplication.h"
+#include "qapplication_p.h"
+#include <qfile.h>
+#include "qpointer.h"
+#include "qsound_p.h"
+
+#include <qt_windows.h>
+
+QT_BEGIN_NAMESPACE
+
+class QAuServerWindows : public QAuServer {
+ Q_OBJECT
+
+public:
+ QAuServerWindows(QObject* parent);
+ ~QAuServerWindows();
+
+ void playHelper(const QString &filename, int loop, QSound *snd);
+ void play(const QString& filename, int loop);
+ void play(QSound*);
+
+ void stop(QSound*);
+ bool okay();
+
+ int decLoop(QSound *snd) { return QAuServer::decLoop(snd); }
+
+ HANDLE current;
+ HANDLE mutex;
+ HANDLE event;
+};
+
+QAuServerWindows::QAuServerWindows(QObject* parent) :
+ QAuServer(parent), current(0)
+{
+ QT_WA({
+ mutex = CreateMutexW(0, 0, 0);
+ event = CreateEventW(0, FALSE, FALSE, 0);
+ } , {
+ mutex = CreateMutexA(0, 0, 0);
+ event = CreateEventA(0, FALSE, FALSE, 0);
+ });
+}
+
+QAuServerWindows::~QAuServerWindows()
+{
+ HANDLE mtx = mutex;
+ WaitForSingleObject(mtx, INFINITE);
+ mutex = 0;
+
+ ReleaseMutex(mtx);
+ CloseHandle(mtx);
+ CloseHandle(event);
+}
+
+struct SoundInfo
+{
+ SoundInfo(const QString &fn, int lp, QSound *snd, QAuServerWindows *srv)
+ : sound(snd), server(srv), filename(fn), loops(lp)
+ {
+ }
+
+ QSound *sound;
+ QAuServerWindows *server;
+ QString filename;
+ int loops;
+};
+
+DWORD WINAPI SoundPlayProc(LPVOID param)
+{
+ SoundInfo *info = (SoundInfo*)param;
+
+ // copy data before waking up GUI thread
+ QAuServerWindows *server = info->server;
+ QSound *sound = info->sound;
+ int loops = info->loops;
+ QString filename = info->filename;
+ HANDLE mutex = server->mutex;
+ HANDLE event = server->event;
+ info = 0;
+
+ // server must not be destroyed until thread finishes
+ // and all other sounds have to wait
+ WaitForSingleObject(mutex, INFINITE);
+
+ if (loops <= 1) {
+ server->current = 0;
+ int flags = SND_FILENAME|SND_ASYNC;
+ if (loops == -1)
+ flags |= SND_LOOP;
+
+ QT_WA({
+ PlaySoundW((TCHAR*)filename.utf16(), 0, flags);
+ } , {
+ PlaySoundA(QFile::encodeName(filename).data(), 0, flags);
+ });
+ if (sound && loops == 1)
+ server->decLoop(sound);
+
+ // GUI thread continues, but we are done as well.
+ SetEvent(event);
+ } else {
+ // signal GUI thread to continue - sound might be reset!
+ QPointer<QSound> guarded_sound = sound;
+ SetEvent(event);
+
+ for (int l = 0; l < loops && server->current; ++l) {
+ QT_WA( {
+ PlaySoundW( (TCHAR*)filename.utf16(), 0, SND_FILENAME|SND_SYNC );
+ } , {
+ PlaySoundA( QFile::encodeName(filename).data(), 0,
+ SND_FILENAME|SND_SYNC );
+ } );
+
+ if (guarded_sound)
+ server->decLoop(guarded_sound);
+ }
+ server->current = 0;
+ }
+ ReleaseMutex(mutex);
+
+ return 0;
+}
+
+void QAuServerWindows::playHelper(const QString &filename, int loop, QSound *snd)
+{
+ if (loop == 0)
+ return;
+ // busy?
+ if (WaitForSingleObject(mutex, 0) == WAIT_TIMEOUT)
+ return;
+ ReleaseMutex(mutex);
+
+ DWORD threadid = 0;
+ SoundInfo info(filename, loop, snd, this);
+ current = CreateThread(0, 0, SoundPlayProc, &info, 0, &threadid);
+ CloseHandle(current);
+
+ WaitForSingleObject(event, INFINITE);
+}
+
+void QAuServerWindows::play(const QString& filename, int loop)
+{
+ playHelper(filename, loop, 0);
+}
+
+void QAuServerWindows::play(QSound* s)
+{
+ playHelper(s->fileName(), s->loops(), s);
+}
+
+void QAuServerWindows::stop(QSound*)
+{
+ // stop unlooped sound
+ if (!current)
+ PlaySound(0, 0, 0);
+ // stop after loop is done
+ current = 0;
+}
+
+bool QAuServerWindows::okay()
+{
+ return true;
+}
+
+QAuServer* qt_new_audio_server()
+{
+ return new QAuServerWindows(qApp);
+}
+
+QT_END_NAMESPACE
+
+#include "qsound_win.moc"
+
+#endif // QT_NO_SOUND
diff --git a/src/gui/kernel/qsound_x11.cpp b/src/gui/kernel/qsound_x11.cpp
new file mode 100644
index 0000000000..f2aeea8291
--- /dev/null
+++ b/src/gui/kernel/qsound_x11.cpp
@@ -0,0 +1,296 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qsound.h"
+
+#ifndef QT_NO_SOUND
+
+#include "qhash.h"
+#include "qsocketnotifier.h"
+#include "qapplication.h"
+#include "qsound_p.h"
+
+QT_BEGIN_NAMESPACE
+
+#ifndef QT_NO_NAS
+
+QT_BEGIN_INCLUDE_NAMESPACE
+#include <audio/audiolib.h>
+#include <audio/soundlib.h>
+QT_END_INCLUDE_NAMESPACE
+
+static AuServer *nas=0;
+
+static AuBool eventPred(AuServer *, AuEvent *e, AuPointer p)
+{
+ if (e && (e->type == AuEventTypeElementNotify)) {
+ if (e->auelementnotify.flow == *((AuFlowID *)p))
+ return true;
+ }
+ return false;
+}
+
+class QAuBucketNAS : public QAuBucket {
+public:
+ QAuBucketNAS(AuBucketID b, AuFlowID f = 0) : id(b), flow(f), stopped(true), numplaying(0) { }
+ ~QAuBucketNAS()
+ {
+ if (nas) {
+ AuSync(nas, false);
+ AuDestroyBucket(nas, id, NULL);
+
+ AuEvent ev;
+ while (AuScanEvents(nas, AuEventsQueuedAfterFlush, true, eventPred, &flow, &ev))
+ ;
+ }
+ }
+
+ AuBucketID id;
+ AuFlowID flow;
+ bool stopped;
+ int numplaying;
+};
+
+class QAuServerNAS : public QAuServer {
+ Q_OBJECT
+
+ QSocketNotifier* sn;
+
+public:
+ QAuServerNAS(QObject* parent);
+ ~QAuServerNAS();
+
+ void init(QSound*);
+ void play(const QString& filename);
+ void play(QSound*);
+ void stop(QSound*);
+ bool okay();
+ void setDone(QSound*);
+
+public slots:
+ void dataReceived();
+ void soundDestroyed(QObject *o);
+
+private:
+ QAuBucketNAS* bucket(QSound* s)
+ {
+ return (QAuBucketNAS*)QAuServer::bucket(s);
+ }
+};
+
+QAuServerNAS::QAuServerNAS(QObject* parent) :
+ QAuServer(parent)
+{
+ setObjectName(QLatin1String("Network Audio System"));
+ nas = AuOpenServer(NULL, 0, NULL, 0, NULL, NULL);
+ if (nas) {
+ AuSetCloseDownMode(nas, AuCloseDownDestroy, NULL);
+ // Ask Qt for async messages...
+ sn=new QSocketNotifier(AuServerConnectionNumber(nas),
+ QSocketNotifier::Read);
+ QObject::connect(sn, SIGNAL(activated(int)),
+ this, SLOT(dataReceived()));
+ } else {
+ sn = 0;
+ }
+}
+
+QAuServerNAS::~QAuServerNAS()
+{
+ if (nas)
+ AuCloseServer(nas);
+ delete sn;
+ nas = 0;
+}
+
+typedef QHash<void*,QAuServerNAS*> AuServerHash;
+static AuServerHash *inprogress=0;
+
+void QAuServerNAS::soundDestroyed(QObject *o)
+{
+ if (inprogress) {
+ QSound *so = static_cast<QSound *>(o);
+ while (inprogress->remove(so))
+ ; // Loop while remove returns true
+ }
+}
+
+void QAuServerNAS::play(const QString& filename)
+{
+ if (nas) {
+ int iv=100;
+ AuFixedPoint volume=AuFixedPointFromFraction(iv,100);
+ AuSoundPlayFromFile(nas, filename.toLocal8Bit().constData(), AuNone, volume,
+ NULL, NULL, NULL, NULL, NULL, NULL);
+ AuFlush(nas);
+ dataReceived();
+ AuFlush(nas);
+ qApp->flush();
+ }
+}
+
+static void callback(AuServer*, AuEventHandlerRec*, AuEvent* e, AuPointer p)
+{
+ if (inprogress->contains(p) && e) {
+ if (e->type==AuEventTypeElementNotify &&
+ e->auelementnotify.kind==AuElementNotifyKindState) {
+ if (e->auelementnotify.cur_state == AuStateStop) {
+ AuServerHash::Iterator it = inprogress->find(p);
+ if (it != inprogress->end())
+ (*it)->setDone((QSound*)p);
+ }
+ }
+ }
+}
+
+void QAuServerNAS::setDone(QSound* s)
+{
+ if (nas) {
+ decLoop(s);
+ if (s->loopsRemaining() && !bucket(s)->stopped) {
+ bucket(s)->stopped = true;
+ play(s);
+ } else {
+ if (--(bucket(s)->numplaying) == 0)
+ bucket(s)->stopped = true;
+ inprogress->remove(s);
+ }
+ }
+}
+
+void QAuServerNAS::play(QSound* s)
+{
+ if (nas) {
+ ++(bucket(s)->numplaying);
+ if (!bucket(s)->stopped) {
+ stop(s);
+ }
+
+ bucket(s)->stopped = false;
+ if (!inprogress)
+ inprogress = new AuServerHash;
+ inprogress->insert(s,this);
+ int iv=100;
+ AuFixedPoint volume=AuFixedPointFromFraction(iv,100);
+ QAuBucketNAS *b = bucket(s);
+ AuSoundPlayFromBucket(nas, b->id, AuNone, volume,
+ callback, s, 0, &b->flow, NULL, NULL, NULL);
+ AuFlush(nas);
+ dataReceived();
+ AuFlush(nas);
+ qApp->flush();
+ }
+}
+
+void QAuServerNAS::stop(QSound* s)
+{
+ if (nas && !bucket(s)->stopped) {
+ bucket(s)->stopped = true;
+ AuStopFlow(nas, bucket(s)->flow, NULL);
+ AuFlush(nas);
+ dataReceived();
+ AuFlush(nas);
+ qApp->flush();
+ }
+}
+
+void QAuServerNAS::init(QSound* s)
+{
+ connect(s, SIGNAL(destroyed(QObject*)),
+ this, SLOT(soundDestroyed(QObject*)));
+
+ if (nas) {
+ AuBucketID b_id =
+ AuSoundCreateBucketFromFile(nas, s->fileName().toLocal8Bit().constData(),
+ 0 /*AuAccessAllMasks*/, NULL, NULL);
+ setBucket(s, new QAuBucketNAS(b_id));
+ }
+}
+
+bool QAuServerNAS::okay()
+{
+ return !!nas;
+}
+
+void QAuServerNAS::dataReceived()
+{
+ AuHandleEvents(nas);
+}
+
+QT_BEGIN_INCLUDE_NAMESPACE
+#include "qsound_x11.moc"
+QT_END_INCLUDE_NAMESPACE
+
+#endif
+
+
+class QAuServerNull : public QAuServer
+{
+public:
+ QAuServerNull(QObject* parent);
+
+ void play(const QString&) { }
+ void play(QSound*s) { while(decLoop(s) > 0) /* nothing */ ; }
+ void stop(QSound*) { }
+ bool okay() { return false; }
+};
+
+QAuServerNull::QAuServerNull(QObject* parent)
+ : QAuServer(parent)
+{
+}
+
+
+QAuServer* qt_new_audio_server()
+{
+#ifndef QT_NO_NAS
+ QAuServer* s = new QAuServerNAS(qApp);
+ if (s->okay())
+ return s;
+ else
+ delete s;
+#endif
+ return new QAuServerNull(qApp);
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_SOUND
diff --git a/src/gui/kernel/qstackedlayout.cpp b/src/gui/kernel/qstackedlayout.cpp
new file mode 100644
index 0000000000..eb985b2dd9
--- /dev/null
+++ b/src/gui/kernel/qstackedlayout.cpp
@@ -0,0 +1,545 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qstackedlayout.h"
+#include "qlayout_p.h"
+
+#include <qlist.h>
+#include <qwidget.h>
+#include "private/qlayoutengine_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class QStackedLayoutPrivate : public QLayoutPrivate
+{
+ Q_DECLARE_PUBLIC(QStackedLayout)
+public:
+ QStackedLayoutPrivate() : index(-1), stackingMode(QStackedLayout::StackOne) {}
+ QList<QLayoutItem *> list;
+ int index;
+ QStackedLayout::StackingMode stackingMode;
+};
+
+/*!
+ \class QStackedLayout
+
+ \brief The QStackedLayout class provides a stack of widgets where
+ only one widget is visible at a time.
+
+ \ingroup geomanagement
+ \ingroup appearance
+ \mainclass
+
+ QStackedLayout can be used to create a user interface similar to
+ the one provided by QTabWidget. There is also a convenience
+ QStackedWidget class built on top of QStackedLayout.
+
+ A QStackedLayout can be populated with a number of child widgets
+ ("pages"). For example:
+
+ \snippet doc/src/snippets/qstackedlayout/main.cpp 0
+ \codeline
+ \snippet doc/src/snippets/qstackedlayout/main.cpp 2
+ \snippet doc/src/snippets/qstackedlayout/main.cpp 3
+
+ QStackedLayout provides no intrinsic means for the user to switch
+ page. This is typically done through a QComboBox or a QListWidget
+ that stores the titles of the QStackedLayout's pages. For
+ example:
+
+ \snippet doc/src/snippets/qstackedlayout/main.cpp 1
+
+ When populating a layout, the widgets are added to an internal
+ list. The indexOf() function returns the index of a widget in that
+ list. The widgets can either be added to the end of the list using
+ the addWidget() function, or inserted at a given index using the
+ insertWidget() function. The removeWidget() function removes the
+ widget at the given index from the layout. The number of widgets
+ contained in the layout, can be obtained using the count()
+ function.
+
+ The widget() function returns the widget at a given index
+ position. The index of the widget that is shown on screen is given
+ by currentIndex() and can be changed using setCurrentIndex(). In a
+ similar manner, the currently shown widget can be retrieved using
+ the currentWidget() function, and altered using the
+ setCurrentWidget() function.
+
+ Whenever the current widget in the layout changes or a widget is
+ removed from the layout, the currentChanged() and widgetRemoved()
+ signals are emitted respectively.
+
+ \sa QStackedWidget, QTabWidget
+*/
+
+/*!
+ \fn void QStackedLayout::currentChanged(int index)
+
+ This signal is emitted whenever the current widget in the layout
+ changes. The \a index specifies the index of the new current
+ widget, or -1 if there isn't a new one (for example, if there
+ are no widgets in the QStackedLayout)
+
+ \sa currentWidget(), setCurrentWidget()
+*/
+
+/*!
+ \fn void QStackedLayout::widgetRemoved(int index)
+
+ This signal is emitted whenever a widget is removed from the
+ layout. The widget's \a index is passed as parameter.
+
+ \sa removeWidget()
+*/
+
+/*!
+ \fn QWidget *QStackedLayout::widget()
+ \internal
+*/
+
+/*!
+ Constructs a QStackedLayout with no parent.
+
+ This QStackedLayout must be installed on a widget later on to
+ become effective.
+
+ \sa addWidget(), insertWidget()
+*/
+QStackedLayout::QStackedLayout()
+ : QLayout(*new QStackedLayoutPrivate, 0, 0)
+{
+}
+
+/*!
+ Constructs a new QStackedLayout with the given \a parent.
+
+ This layout will install itself on the \a parent widget and
+ manage the geometry of its children.
+*/
+QStackedLayout::QStackedLayout(QWidget *parent)
+ : QLayout(*new QStackedLayoutPrivate, 0, parent)
+{
+}
+
+/*!
+ Constructs a new QStackedLayout and inserts it into
+ the given \a parentLayout.
+*/
+QStackedLayout::QStackedLayout(QLayout *parentLayout)
+ : QLayout(*new QStackedLayoutPrivate, parentLayout, 0)
+{
+}
+
+/*!
+ Destroys this QStackedLayout. Note that the layout's widgets are
+ \e not destroyed.
+*/
+QStackedLayout::~QStackedLayout()
+{
+ Q_D(QStackedLayout);
+ qDeleteAll(d->list);
+}
+
+/*!
+ Adds the given \a widget to the end of this layout and returns the
+ index position of the \a widget.
+
+ If the QStackedLayout is empty before this function is called,
+ the given \a widget becomes the current widget.
+
+ \sa insertWidget(), removeWidget(), setCurrentWidget()
+*/
+int QStackedLayout::addWidget(QWidget *widget)
+{
+ Q_D(QStackedLayout);
+ return insertWidget(d->list.count(), widget);
+}
+
+/*!
+ Inserts the given \a widget at the given \a index in this
+ QStackedLayout. If \a index is out of range, the widget is
+ appended (in which case it is the actual index of the \a widget
+ that is returned).
+
+ If the QStackedLayout is empty before this function is called, the
+ given \a widget becomes the current widget.
+
+ Inserting a new widget at an index less than or equal to the current index
+ will increment the current index, but keep the current widget.
+
+ \sa addWidget(), removeWidget(), setCurrentWidget()
+*/
+int QStackedLayout::insertWidget(int index, QWidget *widget)
+{
+ Q_D(QStackedLayout);
+ addChildWidget(widget);
+ index = qMin(index, d->list.count());
+ if (index < 0)
+ index = d->list.count();
+ QWidgetItem *wi = QLayoutPrivate::createWidgetItem(this, widget);
+ d->list.insert(index, wi);
+ invalidate();
+ if (d->index < 0) {
+ setCurrentIndex(index);
+ } else {
+ if (index <= d->index)
+ ++d->index;
+ if (d->stackingMode == StackOne)
+ widget->hide();
+ widget->lower();
+ }
+ return index;
+}
+
+/*!
+ \reimp
+*/
+QLayoutItem *QStackedLayout::itemAt(int index) const
+{
+ Q_D(const QStackedLayout);
+ return d->list.value(index);
+}
+
+// Code that enables proper handling of the case that takeAt() is
+// called somewhere inside QObject destructor (can't call hide()
+// on the object then)
+
+class QtFriendlyLayoutWidget : public QWidget
+{
+public:
+ inline bool wasDeleted() const { return d_ptr->wasDeleted; }
+};
+
+static bool qt_wasDeleted(const QWidget *w) { return static_cast<const QtFriendlyLayoutWidget*>(w)->wasDeleted(); }
+
+
+/*!
+ \reimp
+*/
+QLayoutItem *QStackedLayout::takeAt(int index)
+{
+ Q_D(QStackedLayout);
+ if (index <0 || index >= d->list.size())
+ return 0;
+ QLayoutItem *item = d->list.takeAt(index);
+ if (index == d->index) {
+ d->index = -1;
+ if ( d->list.count() > 0 ) {
+ int newIndex = (index == d->list.count()) ? index-1 : index;
+ setCurrentIndex(newIndex);
+ } else {
+ emit currentChanged(-1);
+ }
+ } else if (index < d->index) {
+ --d->index;
+ }
+ emit widgetRemoved(index);
+ if (item->widget() && !qt_wasDeleted(item->widget()))
+ item->widget()->hide();
+ return item;
+}
+
+/*!
+ \property QStackedLayout::currentIndex
+ \brief the index position of the widget that is visible
+
+ The current index is -1 if there is no current widget.
+
+ \sa currentWidget(), indexOf()
+*/
+void QStackedLayout::setCurrentIndex(int index)
+{
+ Q_D(QStackedLayout);
+ QWidget *prev = currentWidget();
+ QWidget *next = widget(index);
+ if (!next || next == prev)
+ return;
+
+ bool reenableUpdates = false;
+ QWidget *parent = parentWidget();
+
+ if (parent && parent->updatesEnabled()) {
+ reenableUpdates = true;
+ parent->setUpdatesEnabled(false);
+ }
+
+ QWidget *fw = parent ? parent->window()->focusWidget() : 0;
+ if (prev) {
+ prev->clearFocus();
+ if (d->stackingMode == StackOne)
+ prev->hide();
+ }
+
+ d->index = index;
+ next->raise();
+ next->show();
+
+ // try to move focus onto the incoming widget if focus
+ // was somewhere on the outgoing widget.
+
+ if (parent) {
+ if (fw && (prev && prev->isAncestorOf(fw))) { // focus was on old page
+ // look for the best focus widget we can find
+ if (QWidget *nfw = next->focusWidget())
+ nfw->setFocus();
+ else {
+ // 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(next) && i->isEnabled()
+ && next->isAncestorOf(i)) {
+ i->setFocus();
+ break;
+ }
+ }
+ // third best: incoming widget
+ if (i == fw )
+ next->setFocus();
+ }
+ }
+ }
+ if (reenableUpdates)
+ parent->setUpdatesEnabled(true);
+ emit currentChanged(index);
+}
+
+int QStackedLayout::currentIndex() const
+{
+ Q_D(const QStackedLayout);
+ return d->index;
+}
+
+
+/*!
+ \fn void QStackedLayout::setCurrentWidget(QWidget *widget)
+
+ Sets the current widget to be the specified \a widget. The new
+ current widget must already be contained in this stacked layout.
+
+ \sa setCurrentIndex(), currentWidget()
+ */
+void QStackedLayout::setCurrentWidget(QWidget *widget)
+{
+ int index = indexOf(widget);
+ if (index == -1) {
+ qWarning("QStackedLayout::setCurrentWidget: Widget %p not contained in stack", widget);
+ return;
+ }
+ setCurrentIndex(index);
+}
+
+
+/*!
+ Returns the current widget, or 0 if there are no widgets in this
+ layout.
+
+ \sa currentIndex(), setCurrentWidget()
+*/
+QWidget *QStackedLayout::currentWidget() const
+{
+ Q_D(const QStackedLayout);
+ return d->index >= 0 ? d->list.at(d->index)->widget() : 0;
+}
+
+/*!
+ Returns the widget at the given \a index, or 0 if there is no
+ widget at the given position.
+
+ \sa currentWidget(), indexOf()
+*/
+QWidget *QStackedLayout::widget(int index) const
+{
+ Q_D(const QStackedLayout);
+ if (index < 0 || index >= d->list.size())
+ return 0;
+ return d->list.at(index)->widget();
+}
+
+/*!
+ \property QStackedLayout::count
+ \brief the number of widgets contained in the layout
+
+ \sa currentIndex(), widget()
+*/
+int QStackedLayout::count() const
+{
+ Q_D(const QStackedLayout);
+ return d->list.size();
+}
+
+
+/*!
+ \reimp
+*/
+void QStackedLayout::addItem(QLayoutItem *item)
+{
+ QWidget *widget = item->widget();
+ if (widget) {
+ addWidget(widget);
+ delete item;
+ } else {
+ qWarning("QStackedLayout::addItem: Only widgets can be added");
+ }
+}
+
+/*!
+ \reimp
+*/
+QSize QStackedLayout::sizeHint() const
+{
+ Q_D(const QStackedLayout);
+ QSize s(0, 0);
+ int n = d->list.count();
+
+ for (int i = 0; i < n; ++i)
+ if (QWidget *widget = d->list.at(i)->widget()) {
+ QSize ws(widget->sizeHint());
+ if (widget->sizePolicy().horizontalPolicy() == QSizePolicy::Ignored)
+ ws.setWidth(0);
+ if (widget->sizePolicy().verticalPolicy() == QSizePolicy::Ignored)
+ ws.setHeight(0);
+ s = s.expandedTo(ws);
+ }
+ return s;
+}
+
+/*!
+ \reimp
+*/
+QSize QStackedLayout::minimumSize() const
+{
+ Q_D(const QStackedLayout);
+ QSize s(0, 0);
+ int n = d->list.count();
+
+ for (int i = 0; i < n; ++i)
+ if (QWidget *widget = d->list.at(i)->widget())
+ s = s.expandedTo(qSmartMinSize(widget));
+ return s;
+}
+
+/*!
+ \reimp
+*/
+void QStackedLayout::setGeometry(const QRect &rect)
+{
+ Q_D(QStackedLayout);
+ switch (d->stackingMode) {
+ case StackOne:
+ if (QWidget *widget = currentWidget())
+ widget->setGeometry(rect);
+ break;
+ case StackAll:
+ if (const int n = d->list.count())
+ for (int i = 0; i < n; ++i)
+ if (QWidget *widget = d->list.at(i)->widget())
+ widget->setGeometry(rect);
+ break;
+ }
+}
+
+/*!
+ \enum QStackedLayout::StackingMode
+ \since 4.4
+
+ This enum specifies how the layout handles its child widgets
+ regarding their visibility.
+
+ \value StackOne
+ Only the current widget is visible. This is the default.
+
+ \value StackAll
+ All widgets are visible. The current widget is merely raised.
+*/
+
+
+/*!
+ \property QStackedLayout::stackingMode
+ \brief determines the way visibility of child widgets are handled.
+ \since 4.4
+
+ The default value is StackOne. Setting the property to StackAll
+ allows you to make use of the layout for overlay widgets
+ that do additional drawing on top of other widgets, for example,
+ graphical editors.
+*/
+
+QStackedLayout::StackingMode QStackedLayout::stackingMode() const
+{
+ Q_D(const QStackedLayout);
+ return d->stackingMode;
+}
+
+void QStackedLayout::setStackingMode(StackingMode stackingMode)
+{
+ Q_D(QStackedLayout);
+ if (d->stackingMode == stackingMode)
+ return;
+ d->stackingMode = stackingMode;
+
+ const int n = d->list.count();
+ if (n == 0)
+ return;
+
+ switch (d->stackingMode) {
+ case StackOne:
+ if (const int idx = currentIndex())
+ for (int i = 0; i < n; ++i)
+ if (QWidget *widget = d->list.at(i)->widget())
+ widget->setVisible(i == idx);
+ break;
+ case StackAll: { // Turn overlay on: Make sure all widgets are the same size
+ QRect geometry;
+ if (const QWidget *widget = currentWidget())
+ geometry = widget->geometry();
+ for (int i = 0; i < n; ++i)
+ if (QWidget *widget = d->list.at(i)->widget()) {
+ if (!geometry.isNull())
+ widget->setGeometry(geometry);
+ widget->setVisible(true);
+ }
+ }
+ break;
+ }
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/kernel/qstackedlayout.h b/src/gui/kernel/qstackedlayout.h
new file mode 100644
index 0000000000..668c1cc7dc
--- /dev/null
+++ b/src/gui/kernel/qstackedlayout.h
@@ -0,0 +1,115 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSTACKEDLAYOUT_H
+#define QSTACKEDLAYOUT_H
+
+#include <QtGui/qlayout.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+class QStackedLayoutPrivate;
+
+class Q_GUI_EXPORT QStackedLayout : public QLayout
+{
+ Q_OBJECT
+ Q_DECLARE_PRIVATE(QStackedLayout)
+ Q_ENUMS(StackingMode)
+ Q_PROPERTY(int currentIndex READ currentIndex WRITE setCurrentIndex NOTIFY currentChanged)
+ Q_PROPERTY(StackingMode stackingMode READ stackingMode WRITE setStackingMode)
+ QDOC_PROPERTY(int count READ count)
+
+public:
+ enum StackingMode {
+ StackOne,
+ StackAll
+ };
+
+ QStackedLayout();
+ explicit QStackedLayout(QWidget *parent);
+ explicit QStackedLayout(QLayout *parentLayout);
+ ~QStackedLayout();
+
+ int addWidget(QWidget *w);
+ int insertWidget(int index, QWidget *w);
+
+ QWidget *currentWidget() const;
+ int currentIndex() const;
+#ifdef Q_NO_USING_KEYWORD
+ inline QWidget *widget() { return QLayout::widget(); }
+#else
+ using QLayout::widget;
+#endif
+ QWidget *widget(int) const;
+ int count() const;
+
+ StackingMode stackingMode() const;
+ void setStackingMode(StackingMode stackingMode);
+
+ // abstract virtual functions:
+ void addItem(QLayoutItem *item);
+ QSize sizeHint() const;
+ QSize minimumSize() const;
+ QLayoutItem *itemAt(int) const;
+ QLayoutItem *takeAt(int);
+ void setGeometry(const QRect &rect);
+
+Q_SIGNALS:
+ void widgetRemoved(int index);
+ void currentChanged(int index);
+
+public Q_SLOTS:
+ void setCurrentIndex(int index);
+ void setCurrentWidget(QWidget *w);
+
+private:
+ Q_DISABLE_COPY(QStackedLayout)
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QSTACKEDLAYOUT_H
diff --git a/src/gui/kernel/qt_cocoa_helpers_mac.mm b/src/gui/kernel/qt_cocoa_helpers_mac.mm
new file mode 100644
index 0000000000..9c381b4362
--- /dev/null
+++ b/src/gui/kernel/qt_cocoa_helpers_mac.mm
@@ -0,0 +1,1096 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/****************************************************************************
+**
+** Copyright (c) 2007-2008, Apple, Inc.
+**
+** All rights reserved.
+**
+** Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are met:
+**
+** * Redistributions of source code must retain the above copyright notice,
+** this list of conditions and the following disclaimer.
+**
+** * Redistributions in binary form must reproduce the above copyright notice,
+** this list of conditions and the following disclaimer in the documentation
+** and/or other materials provided with the distribution.
+**
+** * Neither the name of Apple, Inc. nor the names of its contributors
+** may be used to endorse or promote products derived from this software
+** without specific prior written permission.
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+** CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+** EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+** PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+** PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+** LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+** NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+** SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+**
+****************************************************************************/
+
+#include <private/qcore_mac_p.h>
+#include <qwidget.h>
+#include <qdesktopwidget.h>
+#include <qevent.h>
+#include <private/qevent_p.h>
+#include <private/qt_cocoa_helpers_mac_p.h>
+#include <private/qt_mac_p.h>
+#include <private/qapplication_p.h>
+#include <private/qcocoawindow_mac_p.h>
+#include <private/qcocoaview_mac_p.h>
+#include <private/qkeymapper_p.h>
+#include <private/qwidget_p.h>
+
+QT_BEGIN_NAMESPACE
+
+extern bool qt_sendSpontaneousEvent(QObject *receiver, QEvent *event); // qapplication.cpp;
+extern Qt::MouseButton cocoaButton2QtButton(NSInteger buttonNum); // qcocoaview.mm
+extern QWidget * mac_mouse_grabber;
+
+void macWindowFade(void * /*OSWindowRef*/ window, float durationSeconds)
+{
+ OSWindowRef wnd = static_cast<OSWindowRef>(window);
+ if( wnd ) {
+#if QT_MAC_USE_COCOA
+ QMacCocoaAutoReleasePool pool;
+ [NSAnimationContext beginGrouping];
+ [[wnd animator] setAlphaValue:0.0];
+ if (durationSeconds > 0) {
+ [[NSAnimationContext currentContext] setDuration:NSTimeInterval(durationSeconds)];
+ } else {
+ durationSeconds = [[NSAnimationContext currentContext] duration];
+ }
+ [NSAnimationContext endGrouping];
+ QTimer::singleShot(qRound(durationSeconds * 1000), [wnd QT_MANGLE_NAMESPACE(qt_qwidget)], SLOT(hide()));
+#else
+ if (durationSeconds <= 0)
+ durationSeconds = 0.15;
+ TransitionWindowOptions options = {0, durationSeconds, 0, 0};
+ TransitionWindowWithOptions(wnd, kWindowFadeTransitionEffect, kWindowHideTransitionAction, 0, 1, &options);
+#endif
+ }
+}
+
+
+
+bool macWindowIsTextured( void * /*OSWindowRef*/ window )
+{
+ OSWindowRef wnd = static_cast<OSWindowRef>(window);
+#if QT_MAC_USE_COCOA
+ return ( [wnd styleMask] & NSTexturedBackgroundWindowMask ) ? true : false;
+#else
+ WindowAttributes currentAttributes;
+ GetWindowAttributes(wnd, &currentAttributes);
+ return (currentAttributes & kWindowMetalAttribute) ? true : false;
+#endif
+}
+
+void macWindowToolbarShow(const QWidget *widget, bool show )
+{
+ OSWindowRef wnd = qt_mac_window_for(widget);
+#if QT_MAC_USE_COCOA
+ NSToolbar *toolbar = [wnd toolbar];
+ if (toolbar) {
+ QMacCocoaAutoReleasePool pool;
+ if (show != [toolbar isVisible]) {
+ [wnd toggleToolbarShown:wnd];
+ } else {
+ // The toolbar may be in sync, but we are not, update our framestrut.
+ qt_widget_private(const_cast<QWidget *>(widget))->updateFrameStrut();
+ }
+ }
+#else
+ ShowHideWindowToolbar(wnd, show, false);
+#endif
+}
+
+
+void macWindowToolbarSet( void * /*OSWindowRef*/ window, void *toolbarRef )
+{
+ OSWindowRef wnd = static_cast<OSWindowRef>(window);
+#if QT_MAC_USE_COCOA
+ [wnd setToolbar:static_cast<NSToolbar *>(toolbarRef)];
+#else
+ SetWindowToolbar(wnd, static_cast<HIToolbarRef>(toolbarRef));
+#endif
+}
+
+bool macWindowToolbarVisible( void * /*OSWindowRef*/ window )
+{
+ OSWindowRef wnd = static_cast<OSWindowRef>(window);
+#if QT_MAC_USE_COCOA
+ NSToolbar *toolbar = [wnd toolbar];
+ if (toolbar)
+ return [toolbar isVisible];
+ return false;
+#else
+ return IsWindowToolbarVisible(wnd);
+#endif
+}
+
+void macWindowSetHasShadow( void * /*OSWindowRef*/ window, bool hasShadow )
+{
+ OSWindowRef wnd = static_cast<OSWindowRef>(window);
+#if QT_MAC_USE_COCOA
+ [wnd setHasShadow:BOOL(hasShadow)];
+#else
+ if (hasShadow)
+ ChangeWindowAttributes(wnd, 0, kWindowNoShadowAttribute);
+ else
+ ChangeWindowAttributes(wnd, kWindowNoShadowAttribute, 0);
+#endif
+}
+
+void macWindowFlush(void * /*OSWindowRef*/ window)
+{
+ OSWindowRef wnd = static_cast<OSWindowRef>(window);
+#if QT_MAC_USE_COCOA
+ [wnd flushWindowIfNeeded];
+#else
+ HIWindowFlush(wnd);
+#endif
+}
+
+void * /*NSImage */qt_mac_create_nsimage(const QPixmap &pm)
+{
+ QMacCocoaAutoReleasePool pool;
+ if(QCFType<CGImageRef> image = pm.toMacCGImageRef()) {
+ NSImage *newImage = 0;
+ NSRect imageRect = NSMakeRect(0.0, 0.0, CGImageGetWidth(image), CGImageGetHeight(image));
+ newImage = [[NSImage alloc] initWithSize:imageRect.size];
+ [newImage lockFocus];
+ {
+ CGContextRef imageContext = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];
+ CGContextDrawImage(imageContext, *(CGRect*)&imageRect, image);
+ }
+ [newImage unlockFocus];
+ return newImage;
+ }
+ return 0;
+}
+
+void qt_mac_update_mouseTracking(QWidget *widget)
+{
+#ifdef QT_MAC_USE_COCOA
+ [qt_mac_nativeview_for(widget) updateTrackingAreas];
+#else
+ Q_UNUSED(widget);
+#endif
+}
+
+OSStatus qt_mac_drawCGImage(CGContextRef inContext, const CGRect *inBounds, CGImageRef inImage)
+{
+ // Verbatim copy if HIViewDrawCGImage (as shown on Carbon-Dev)
+ OSStatus err = noErr;
+
+ require_action(inContext != NULL, InvalidContext, err = paramErr);
+ require_action(inBounds != NULL, InvalidBounds, err = paramErr);
+ require_action(inImage != NULL, InvalidImage, err = paramErr);
+
+ CGContextSaveGState( inContext );
+ CGContextTranslateCTM (inContext, 0, inBounds->origin.y + CGRectGetMaxY(*inBounds));
+ CGContextScaleCTM(inContext, 1, -1);
+
+ CGContextDrawImage(inContext, *inBounds, inImage);
+
+ CGContextRestoreGState(inContext);
+InvalidImage:
+InvalidBounds:
+InvalidContext:
+ return err;
+}
+
+bool qt_mac_checkForNativeSizeGrip(const QWidget *widget)
+{
+#ifndef QT_MAC_USE_COCOA
+ OSViewRef nativeSizeGrip = 0;
+ HIViewFindByID(HIViewGetRoot(HIViewGetWindow(HIViewRef(widget->winId()))), kHIViewWindowGrowBoxID, &nativeSizeGrip);
+ return (nativeSizeGrip != 0);
+#else
+ return [[reinterpret_cast<NSView *>(widget->winId()) window] showsResizeIndicator];
+#endif
+}
+struct qt_mac_enum_mapper
+{
+ int mac_code;
+ int qt_code;
+#if defined(DEBUG_MOUSE_MAPS)
+# define QT_MAC_MAP_ENUM(x) x, #x
+ const char *desc;
+#else
+# define QT_MAC_MAP_ENUM(x) x
+#endif
+};
+
+//mouse buttons
+static qt_mac_enum_mapper qt_mac_mouse_symbols[] = {
+{ kEventMouseButtonPrimary, QT_MAC_MAP_ENUM(Qt::LeftButton) },
+{ kEventMouseButtonSecondary, QT_MAC_MAP_ENUM(Qt::RightButton) },
+{ kEventMouseButtonTertiary, QT_MAC_MAP_ENUM(Qt::MidButton) },
+{ 4, QT_MAC_MAP_ENUM(Qt::XButton1) },
+{ 5, QT_MAC_MAP_ENUM(Qt::XButton2) },
+{ 0, QT_MAC_MAP_ENUM(0) }
+};
+Qt::MouseButtons qt_mac_get_buttons(int buttons)
+{
+#ifdef DEBUG_MOUSE_MAPS
+ qDebug("Qt: internal: **Mapping buttons: %d (0x%04x)", buttons, buttons);
+#endif
+ Qt::MouseButtons ret = Qt::NoButton;
+ for(int i = 0; qt_mac_mouse_symbols[i].qt_code; i++) {
+ if (buttons & (0x01<<(qt_mac_mouse_symbols[i].mac_code-1))) {
+#ifdef DEBUG_MOUSE_MAPS
+ qDebug("Qt: internal: got button: %s", qt_mac_mouse_symbols[i].desc);
+#endif
+ ret |= Qt::MouseButtons(qt_mac_mouse_symbols[i].qt_code);
+ }
+ }
+ return ret;
+}
+Qt::MouseButton qt_mac_get_button(EventMouseButton button)
+{
+#ifdef DEBUG_MOUSE_MAPS
+ qDebug("Qt: internal: **Mapping button: %d (0x%04x)", button, button);
+#endif
+ Qt::MouseButtons ret = 0;
+ for(int i = 0; qt_mac_mouse_symbols[i].qt_code; i++) {
+ if (button == qt_mac_mouse_symbols[i].mac_code) {
+#ifdef DEBUG_MOUSE_MAPS
+ qDebug("Qt: internal: got button: %s", qt_mac_mouse_symbols[i].desc);
+#endif
+ return Qt::MouseButton(qt_mac_mouse_symbols[i].qt_code);
+ }
+ }
+ return Qt::NoButton;
+}
+
+Q_GLOBAL_STATIC(QMacTabletHash, tablet_hash)
+QMacTabletHash *qt_mac_tablet_hash()
+{
+ return tablet_hash();
+}
+
+#ifdef QT_MAC_USE_COCOA
+void qt_dispatchTabletProximityEvent(void * /*NSEvent * */ tabletEvent)
+{
+ NSEvent *proximityEvent = static_cast<NSEvent *>(tabletEvent);
+ // simply construct a Carbon proximity record and handle it all in one spot.
+ TabletProximityRec carbonProximityRec = { [proximityEvent vendorID],
+ [proximityEvent tabletID],
+ [proximityEvent pointingDeviceID],
+ [proximityEvent deviceID],
+ [proximityEvent systemTabletID],
+ [proximityEvent vendorPointingDeviceType],
+ [proximityEvent pointingDeviceSerialNumber],
+ [proximityEvent uniqueID],
+ [proximityEvent capabilityMask],
+ [proximityEvent pointingDeviceType],
+ [proximityEvent isEnteringProximity] };
+ qt_dispatchTabletProximityEvent(carbonProximityRec);
+}
+#endif // QT_MAC_USE_COCOA
+
+void qt_dispatchTabletProximityEvent(const ::TabletProximityRec &proxRec)
+{
+ QTabletDeviceData proximityDevice;
+ proximityDevice.tabletUniqueID = proxRec.uniqueID;
+ proximityDevice.capabilityMask = proxRec.capabilityMask;
+
+ switch (proxRec.pointerType) {
+ case NSUnknownPointingDevice:
+ default:
+ proximityDevice.tabletPointerType = QTabletEvent::UnknownPointer;
+ break;
+ case NSPenPointingDevice:
+ proximityDevice.tabletPointerType = QTabletEvent::Pen;
+ break;
+ case NSCursorPointingDevice:
+ proximityDevice.tabletPointerType = QTabletEvent::Cursor;
+ break;
+ case NSEraserPointingDevice:
+ proximityDevice.tabletPointerType = QTabletEvent::Eraser;
+ break;
+ }
+ uint bits = proxRec.vendorPointerType;
+ if (bits == 0 && proximityDevice.tabletUniqueID != 0) {
+ // Fallback. It seems that the driver doesn't always include all the information.
+ // High-End Wacom devices store their "type" in the uper bits of the Unique ID.
+ // I'm not sure how to handle it for consumer devices, but I'll test that in a bit.
+ bits = proximityDevice.tabletUniqueID >> 32;
+ }
+ // Defined in the "EN0056-NxtGenImpGuideX"
+ // on Wacom's Developer Website (www.wacomeng.com)
+ if (((bits & 0x0006) == 0x0002) && ((bits & 0x0F06) != 0x0902)) {
+ proximityDevice.tabletDeviceType = QTabletEvent::Stylus;
+ } else {
+ switch (bits & 0x0F06) {
+ case 0x0802:
+ proximityDevice.tabletDeviceType = QTabletEvent::Stylus;
+ break;
+ case 0x0902:
+ proximityDevice.tabletDeviceType = QTabletEvent::Airbrush;
+ break;
+ case 0x0004:
+ proximityDevice.tabletDeviceType = QTabletEvent::FourDMouse;
+ break;
+ case 0x0006:
+ proximityDevice.tabletDeviceType = QTabletEvent::Puck;
+ break;
+ case 0x0804:
+ proximityDevice.tabletDeviceType = QTabletEvent::RotationStylus;
+ break;
+ default:
+ proximityDevice.tabletDeviceType = QTabletEvent::NoDevice;
+ }
+ }
+ // The deviceID is "unique" while in the proximity, it's a key that we can use for
+ // linking up TabletDeviceData to an event (especially if there are two devices in action).
+ bool entering = proxRec.enterProximity;
+ if (entering) {
+ qt_mac_tablet_hash()->insert(proxRec.deviceID, proximityDevice);
+ } else {
+ qt_mac_tablet_hash()->remove(proxRec.deviceID);
+ }
+
+ QTabletEvent qtabletProximity(entering ? QEvent::TabletEnterProximity
+ : QEvent::TabletLeaveProximity,
+ QPoint(), QPoint(), QPointF(), proximityDevice.tabletDeviceType,
+ proximityDevice.tabletPointerType, 0., 0, 0, 0., 0., 0, 0,
+ proximityDevice.tabletUniqueID);
+
+ qt_sendSpontaneousEvent(qApp, &qtabletProximity);
+}
+
+#ifdef QT_MAC_USE_COCOA
+// Use this method to keep all the information in the TextSegment. As long as it is ordered
+// we are in OK shape, and we can influence that ourselves.
+struct KeyPair
+{
+ QChar cocoaKey;
+ Qt::Key qtKey;
+};
+
+bool operator==(const KeyPair &entry, QChar qchar)
+{
+ return entry.cocoaKey == qchar;
+}
+
+bool operator<(const KeyPair &entry, QChar qchar)
+{
+ return entry.cocoaKey < qchar;
+}
+
+bool operator<(QChar qchar, const KeyPair &entry)
+{
+ return qchar < entry.cocoaKey;
+}
+
+static Qt::Key cocoaKey2QtKey(QChar keyCode)
+{
+ static const int NumEntries = 57;
+ static const KeyPair entries[NumEntries] = {
+ { NSEnterCharacter, Qt::Key_Enter },
+ { NSTabCharacter, Qt::Key_Tab },
+ { NSCarriageReturnCharacter, Qt::Key_Return },
+ { NSBackTabCharacter, Qt::Key_Backtab },
+ { kEscapeCharCode, Qt::Key_Escape },
+ { NSDeleteCharacter, Qt::Key_Backspace },
+ { NSUpArrowFunctionKey, Qt::Key_Up },
+ { NSDownArrowFunctionKey, Qt::Key_Down },
+ { NSLeftArrowFunctionKey, Qt::Key_Left },
+ { NSRightArrowFunctionKey, Qt::Key_Right },
+ { NSF1FunctionKey, Qt::Key_F1 },
+ { NSF2FunctionKey, Qt::Key_F2 },
+ { NSF3FunctionKey, Qt::Key_F3 },
+ { NSF4FunctionKey, Qt::Key_F4 },
+ { NSF5FunctionKey, Qt::Key_F5 },
+ { NSF6FunctionKey, Qt::Key_F6 },
+ { NSF7FunctionKey, Qt::Key_F7 },
+ { NSF8FunctionKey, Qt::Key_F8 },
+ { NSF9FunctionKey, Qt::Key_F8 },
+ { NSF10FunctionKey, Qt::Key_F10 },
+ { NSF11FunctionKey, Qt::Key_F11 },
+ { NSF12FunctionKey, Qt::Key_F12 },
+ { NSF13FunctionKey, Qt::Key_F13 },
+ { NSF14FunctionKey, Qt::Key_F14 },
+ { NSF15FunctionKey, Qt::Key_F15 },
+ { NSF16FunctionKey, Qt::Key_F16 },
+ { NSF17FunctionKey, Qt::Key_F17 },
+ { NSF18FunctionKey, Qt::Key_F18 },
+ { NSF19FunctionKey, Qt::Key_F19 },
+ { NSF20FunctionKey, Qt::Key_F20 },
+ { NSF21FunctionKey, Qt::Key_F21 },
+ { NSF22FunctionKey, Qt::Key_F22 },
+ { NSF23FunctionKey, Qt::Key_F23 },
+ { NSF24FunctionKey, Qt::Key_F24 },
+ { NSF25FunctionKey, Qt::Key_F25 },
+ { NSF26FunctionKey, Qt::Key_F26 },
+ { NSF27FunctionKey, Qt::Key_F27 },
+ { NSF28FunctionKey, Qt::Key_F28 },
+ { NSF29FunctionKey, Qt::Key_F29 },
+ { NSF30FunctionKey, Qt::Key_F30 },
+ { NSF31FunctionKey, Qt::Key_F31 },
+ { NSF32FunctionKey, Qt::Key_F32 },
+ { NSF33FunctionKey, Qt::Key_F33 },
+ { NSF34FunctionKey, Qt::Key_F34 },
+ { NSF35FunctionKey, Qt::Key_F35 },
+ { NSInsertFunctionKey, Qt::Key_Insert },
+ { NSDeleteFunctionKey, Qt::Key_Delete },
+ { NSHomeFunctionKey, Qt::Key_Home },
+ { NSEndFunctionKey, Qt::Key_End },
+ { NSPageUpFunctionKey, Qt::Key_PageUp },
+ { NSPageDownFunctionKey, Qt::Key_PageDown },
+ { NSPrintScreenFunctionKey, Qt::Key_Print },
+ { NSScrollLockFunctionKey, Qt::Key_ScrollLock },
+ { NSPauseFunctionKey, Qt::Key_Pause },
+ { NSSysReqFunctionKey, Qt::Key_SysReq },
+ { NSMenuFunctionKey, Qt::Key_Menu },
+ { NSHelpFunctionKey, Qt::Key_Help },
+ };
+ static const KeyPair * const end = entries + NumEntries;
+ const KeyPair *i = qBinaryFind(entries, end, keyCode);
+ if (i == end)
+ return Qt::Key(keyCode.unicode());
+ return i->qtKey;
+}
+
+Qt::KeyboardModifiers qt_cocoaModifiers2QtModifiers(ulong modifierFlags)
+{
+ Qt::KeyboardModifiers qtMods =Qt::NoModifier;
+ if (modifierFlags & NSShiftKeyMask)
+ qtMods |= Qt::ShiftModifier;
+ if (modifierFlags & NSControlKeyMask)
+ qtMods |= Qt::MetaModifier;
+ if (modifierFlags & NSAlternateKeyMask)
+ qtMods |= Qt::AltModifier;
+ if (modifierFlags & NSCommandKeyMask)
+ qtMods |= Qt::ControlModifier;
+ if (modifierFlags & NSNumericPadKeyMask)
+ qtMods |= Qt::KeypadModifier;
+ return qtMods;
+}
+
+static inline QEvent::Type cocoaEvent2QtEvent(NSUInteger eventType)
+{
+ // Handle the trivial cases that can be determined from the type.
+ switch (eventType) {
+ case NSKeyDown:
+ return QEvent::KeyPress;
+ case NSKeyUp:
+ return QEvent::KeyRelease;
+ case NSLeftMouseDown:
+ case NSRightMouseDown:
+ case NSOtherMouseDown:
+ return QEvent::MouseButtonPress;
+ case NSLeftMouseUp:
+ case NSRightMouseUp:
+ case NSOtherMouseUp:
+ return QEvent::MouseButtonRelease;
+ case NSMouseMoved:
+ case NSLeftMouseDragged:
+ case NSRightMouseDragged:
+ case NSOtherMouseDragged:
+ return QEvent::MouseMove;
+ case NSScrollWheel:
+ return QEvent::Wheel;
+ }
+ return QEvent::None;
+}
+
+static bool mustUseCocoaKeyEvent()
+{
+ QCFType<TISInputSourceRef> source = TISCopyCurrentKeyboardInputSource();
+ return TISGetInputSourceProperty(source, kTISPropertyUnicodeKeyLayoutData) == 0;
+}
+
+bool qt_dispatchKeyEventWithCocoa(void * /*NSEvent * */ keyEvent, QWidget *widgetToGetEvent)
+{
+ NSEvent *event = static_cast<NSEvent *>(keyEvent);
+ NSString *keyChars = [event charactersIgnoringModifiers];
+ int keyLength = [keyChars length];
+ if (keyLength == 0)
+ return false; // Dead Key, nothing to do!
+ Qt::Key qtKey = Qt::Key_unknown;
+ if (keyLength == 1) {
+ QChar ch([keyChars characterAtIndex:0]);
+ if (ch.isLower())
+ ch = ch.toUpper();
+ qtKey = cocoaKey2QtKey(ch);
+ }
+ Qt::KeyboardModifiers keyMods = qt_cocoaModifiers2QtModifiers([event modifierFlags]);
+ QString text;
+
+ // To quote from the Carbon port: This is actually wrong--but it is the best that
+ // can be done for now because of the Control/Meta mapping issues
+ // (we always get text on the Mac)
+ if (!(keyMods & (Qt::ControlModifier | Qt::MetaModifier)))
+ text = QCFString::toQString(reinterpret_cast<CFStringRef>(keyChars));
+
+ UInt32 macScanCode = 1;
+ QKeyEventEx ke(cocoaEvent2QtEvent([event type]), qtKey, keyMods, text, [event isARepeat], qMax(1, keyLength),
+ macScanCode, [event keyCode], [event modifierFlags]);
+ qt_sendSpontaneousEvent(widgetToGetEvent, &ke);
+ return ke.isAccepted();
+}
+#endif
+
+// Helper to share code between QCocoaWindow and QCocoaView
+bool qt_dispatchKeyEvent(void * /*NSEvent * */ keyEvent, QWidget *widgetToGetEvent)
+{
+#ifndef QT_MAC_USE_COCOA
+ Q_UNUSED(keyEvent);
+ Q_UNUSED(widgetToGetEvent);
+ return false;
+#else
+ NSEvent *event = static_cast<NSEvent *>(keyEvent);
+ EventRef key_event = static_cast<EventRef>(const_cast<void *>([event eventRef]));
+ Q_ASSERT(key_event);
+ if ([event type] == NSKeyDown) {
+ qt_keymapper_private()->updateKeyMap(0, key_event, 0);
+ }
+ if (widgetToGetEvent == 0)
+ return false;
+
+ if (qt_mac_sendMacEventToWidget(widgetToGetEvent, key_event))
+ return true;
+
+ if (mustUseCocoaKeyEvent())
+ return qt_dispatchKeyEventWithCocoa(keyEvent, widgetToGetEvent);
+ bool isAccepted;
+ qt_keymapper_private()->translateKeyEvent(widgetToGetEvent, 0, key_event, &isAccepted, true);
+ return isAccepted;
+#endif
+}
+
+void qt_dispatchModifiersChanged(void * /*NSEvent * */flagsChangedEvent, QWidget *widgetToGetEvent)
+{
+#ifndef QT_MAC_USE_COCOA
+ Q_UNUSED(flagsChangedEvent);
+ Q_UNUSED(widgetToGetEvent);
+#else
+ UInt32 modifiers = 0;
+ // Sync modifiers with Qt
+ NSEvent *event = static_cast<NSEvent *>(flagsChangedEvent);
+ EventRef key_event = static_cast<EventRef>(const_cast<void *>([event eventRef]));
+ Q_ASSERT(key_event);
+ GetEventParameter(key_event, kEventParamKeyModifiers, typeUInt32, 0,
+ sizeof(modifiers), 0, &modifiers);
+ extern void qt_mac_send_modifiers_changed(quint32 modifiers, QObject *object);
+ qt_mac_send_modifiers_changed(modifiers, widgetToGetEvent);
+#endif
+}
+
+
+QPointF flipPoint(const NSPoint &p)
+{
+ return QPointF(p.x, flipYCoordinate(p.y));
+}
+
+NSPoint flipPoint(const QPoint &p)
+{
+ return NSMakePoint(p.x(), flipYCoordinate(p.y()));
+}
+
+NSPoint flipPoint(const QPointF &p)
+{
+ return NSMakePoint(p.x(), flipYCoordinate(p.y()));
+}
+
+void qt_mac_dispatchNCMouseMessage(void * /* NSWindow* */eventWindow, void * /* NSEvent* */mouseEvent,
+ QWidget *widgetToGetEvent, bool &leftButtonIsRightButton)
+{
+#ifndef QT_MAC_USE_COCOA
+ Q_UNUSED(eventWindow);
+ Q_UNUSED(mouseEvent);
+ Q_UNUSED(widgetToGetEvent);
+ Q_UNUSED(leftButtonIsRightButton);
+#else
+ if (widgetToGetEvent == 0)
+ return;
+ NSWindow *window = static_cast<NSWindow *>(eventWindow);
+ NSEvent *event = static_cast<NSEvent *>(mouseEvent);
+ NSEventType evtType = [event type];
+
+ QPoint qlocalPoint;
+ QPoint qglobalPoint;
+ bool processThisEvent = false;
+ bool fakeNCEvents = false;
+ bool fakeMouseEvents = false;
+
+ // Check if this is a mouse event.
+ if (evtType == NSLeftMouseDown || evtType == NSLeftMouseUp
+ || evtType == NSRightMouseDown || evtType == NSRightMouseUp
+ || evtType == NSOtherMouseDown || evtType == NSOtherMouseUp
+ || evtType == NSMouseMoved || evtType == NSLeftMouseDragged
+ || evtType == NSRightMouseDragged || evtType == NSOtherMouseDragged) {
+ // Check if we want to pass this message to another window
+ if (mac_mouse_grabber && mac_mouse_grabber != widgetToGetEvent) {
+ NSWindow *grabWindow = static_cast<NSWindow *>(qt_mac_window_for(mac_mouse_grabber));
+ if (window != grabWindow) {
+ window = grabWindow;
+ widgetToGetEvent = mac_mouse_grabber;
+ fakeNCEvents = true;
+ }
+ }
+ // Dont generate normal NC mouse events for Left Button dragged
+ if(evtType != NSLeftMouseDragged || fakeNCEvents) {
+ NSPoint windowPoint = [event locationInWindow];
+ NSPoint globalPoint = [[event window] convertBaseToScreen:windowPoint];
+ NSRect frameRect = [window frame];
+ if (fakeNCEvents || NSMouseInRect(globalPoint, frameRect, NO)) {
+ NSRect contentRect = [window contentRectForFrameRect:frameRect];
+ if (fakeNCEvents || !NSMouseInRect(globalPoint, contentRect, NO)) {
+ qglobalPoint = QPoint(flipPoint(globalPoint).toPoint());
+ qlocalPoint = widgetToGetEvent->mapFromGlobal(qglobalPoint);
+ processThisEvent = true;
+ }
+ }
+ }
+ }
+ // This is not an NC area mouse message.
+ if (!processThisEvent)
+ return;
+ // If the window is frame less, generate fake mouse events instead. (floating QToolBar)
+ if (fakeNCEvents && (widgetToGetEvent->window()->windowFlags() & Qt::FramelessWindowHint))
+ fakeMouseEvents = true;
+
+ Qt::MouseButton button;
+ QEvent::Type eventType;
+ // Convert to Qt::Event type
+ switch (evtType) {
+ case NSLeftMouseDown:
+ button = Qt::LeftButton;
+ eventType = (!fakeMouseEvents) ? QEvent::NonClientAreaMouseButtonPress
+ : QEvent::MouseButtonPress;
+ break;
+ case NSLeftMouseUp:
+ button = Qt::LeftButton;
+ eventType = (!fakeMouseEvents) ? QEvent::NonClientAreaMouseButtonRelease
+ : QEvent::MouseButtonRelease;
+ break;
+ case NSRightMouseDown:
+ button = Qt::RightButton;
+ eventType = (!fakeMouseEvents) ? QEvent::NonClientAreaMouseButtonPress
+ : QEvent::MouseButtonPress;
+ break;
+ case NSRightMouseUp:
+ button = Qt::RightButton;
+ eventType = (!fakeMouseEvents) ? QEvent::NonClientAreaMouseButtonRelease
+ : QEvent::MouseButtonRelease;
+ break;
+ case NSOtherMouseDown:
+ button = cocoaButton2QtButton([event buttonNumber]);
+ eventType = (!fakeMouseEvents) ? QEvent::NonClientAreaMouseButtonPress
+ : QEvent::MouseButtonPress;
+ break;
+ case NSOtherMouseUp:
+ button = cocoaButton2QtButton([event buttonNumber]);
+ eventType = (!fakeMouseEvents) ? QEvent::NonClientAreaMouseButtonRelease
+ : QEvent::MouseButtonRelease;
+ break;
+ case NSMouseMoved:
+ button = Qt::NoButton;
+ eventType = (!fakeMouseEvents) ? QEvent::NonClientAreaMouseMove
+ : QEvent::MouseMove;
+ break;
+ case NSLeftMouseDragged:
+ button = Qt::LeftButton;
+ eventType = (!fakeMouseEvents) ? QEvent::NonClientAreaMouseMove
+ : QEvent::MouseMove;
+ break;
+ case NSRightMouseDragged:
+ button = Qt::RightButton;
+ eventType = (!fakeMouseEvents) ? QEvent::NonClientAreaMouseMove
+ : QEvent::MouseMove;
+ break;
+ case NSOtherMouseDragged:
+ button = cocoaButton2QtButton([event buttonNumber]);
+ eventType = (!fakeMouseEvents) ? QEvent::NonClientAreaMouseMove
+ : QEvent::MouseMove;
+ break;
+ default:
+ qWarning("not handled! Non client area mouse message");
+ return;
+ }
+
+ Qt::KeyboardModifiers keyMods = qt_cocoaModifiers2QtModifiers([event modifierFlags]);
+ if (eventType == QEvent::NonClientAreaMouseButtonPress || eventType == QEvent::MouseButtonPress) {
+ NSInteger clickCount = [event clickCount];
+ if (clickCount % 2 == 0)
+ eventType = (!fakeMouseEvents) ? QEvent::NonClientAreaMouseButtonDblClick
+ : QEvent::MouseButtonDblClick;
+ if (button == Qt::LeftButton && (keyMods & Qt::MetaModifier)) {
+ button = Qt::RightButton;
+ leftButtonIsRightButton = true;
+ }
+ } else if (eventType == QEvent::NonClientAreaMouseButtonRelease || eventType == QEvent::MouseButtonRelease) {
+ if (button == Qt::LeftButton && leftButtonIsRightButton) {
+ button = Qt::RightButton;
+ leftButtonIsRightButton = false;
+ }
+ }
+ QMouseEvent qme(eventType, qlocalPoint, qglobalPoint, button, button, keyMods);
+ qt_sendSpontaneousEvent(widgetToGetEvent, &qme);
+#endif
+}
+
+bool qt_mac_handleMouseEvent(void * /* NSView * */view, void * /* NSEvent * */event, QEvent::Type eventType, Qt::MouseButton button)
+{
+#ifndef QT_MAC_USE_COCOA
+ Q_UNUSED(view);
+ Q_UNUSED(event);
+ Q_UNUSED(eventType);
+ Q_UNUSED(button);
+ return false;
+#else
+ QT_MANGLE_NAMESPACE(QCocoaView) *theView = static_cast<QT_MANGLE_NAMESPACE(QCocoaView) *>(view);
+ NSEvent *theEvent = static_cast<NSEvent *>(event);
+
+ // Give the Input Manager a chance to process the mouse events.
+ NSInputManager *currentIManager = [NSInputManager currentInputManager];
+ if (currentIManager && [currentIManager wantsToHandleMouseEvents]) {
+ [currentIManager handleMouseEvent:theEvent];
+ }
+
+ // Handle tablet events (if any) first.
+ if (qt_mac_handleTabletEvent(theView, theEvent)) {
+ // Tablet event was handled. In Qt we aren't supposed to send the mouse event.
+ return true;
+ }
+
+ NSPoint windowPoint = [theEvent locationInWindow];
+ NSPoint globalPoint = [[theEvent window] convertBaseToScreen:windowPoint];
+
+ // Find the widget that *should* get the event (e.g., maybe it was a pop-up,
+ // they always get the mouse event).
+ QWidget *qwidget = [theView qt_qwidget];
+ QWidget *widgetToGetMouse = qwidget;
+ QWidget *popup = qAppInstance()->activePopupWidget();
+ if (popup && popup != qwidget->window())
+ widgetToGetMouse = popup;
+ NSView *tmpView = theView;
+ if (widgetToGetMouse != qwidget) {
+ tmpView = qt_mac_nativeview_for(widgetToGetMouse);
+ windowPoint = [[tmpView window] convertScreenToBase:globalPoint];
+ }
+ NSPoint localPoint = [tmpView convertPoint:windowPoint fromView:nil];
+ QPoint qlocalPoint(localPoint.x, localPoint.y);
+
+ if (widgetToGetMouse->testAttribute(Qt::WA_TransparentForMouseEvents)) {
+ // Simulate passing the event through since Cocoa doesn't do that for us.
+ // Start by building a tree up.
+ NSView *candidateView = [theView viewUnderTransparentForMouseView:tmpView
+ widget:widgetToGetMouse
+ withWindowPoint:windowPoint];
+ if (candidateView != nil) {
+ // Fast-track our views, since dispatching trough the normal ways
+ // would just end up going through here anyway.
+ if ([candidateView isKindOfClass:[QT_MANGLE_NAMESPACE(QCocoaView) class]]) {
+ return qt_mac_handleMouseEvent(candidateView, theEvent, eventType, button);
+ } else {
+ switch (eventType) {
+ default:
+ qWarning("not handled! %d", eventType);
+ break;
+ case QEvent::MouseMove:
+ [candidateView mouseMoved:theEvent];
+ break;
+ case QEvent::MouseButtonPress:
+ if (button == Qt::LeftButton)
+ [candidateView mouseDown:theEvent];
+ else if (button == Qt::RightButton)
+ [candidateView rightMouseDown:theEvent];
+ else
+ [candidateView otherMouseDown:theEvent];
+ break;
+ case QEvent::MouseButtonRelease:
+ if (button == Qt::LeftButton)
+ [candidateView mouseUp:theEvent];
+ else if (button == Qt::RightButton)
+ [candidateView rightMouseUp:theEvent];
+ else
+ [candidateView otherMouseUp:theEvent];
+ break;
+ }
+ return true; // We've done the dispatching, no need go further.
+ }
+ }
+ // Nothing below me return false
+ return false;
+ }
+
+
+ EventRef carbonEvent = static_cast<EventRef>(const_cast<void *>([theEvent eventRef]));
+ if (qt_mac_sendMacEventToWidget(widgetToGetMouse, carbonEvent))
+ return true;
+
+ // Yay! All the special cases are handled, it really is just a normal mouse event.
+ Qt::KeyboardModifiers keyMods = qt_cocoaModifiers2QtModifiers([theEvent modifierFlags]);
+ NSInteger clickCount = [theEvent clickCount];
+ Qt::MouseButtons buttons = 0;
+ {
+ UInt32 mac_buttons;
+ if (GetEventParameter(carbonEvent, kEventParamMouseChord, typeUInt32, 0,
+ sizeof(mac_buttons), 0, &mac_buttons) == noErr)
+ buttons = qt_mac_get_buttons(mac_buttons);
+ }
+ switch (eventType) {
+ default:
+ qWarning("not handled! %d", eventType);
+ break;
+ case QEvent::MouseMove:
+ break;
+ case QEvent::MouseButtonPress:
+ [QT_MANGLE_NAMESPACE(QCocoaView) currentMouseEvent]->view = theView;
+ [QT_MANGLE_NAMESPACE(QCocoaView) currentMouseEvent]->theEvent = theEvent;
+#ifndef QT_NAMESPACE
+ Q_ASSERT(clickCount > 0);
+#endif
+ if (clickCount % 2 == 0)
+ eventType = QEvent::MouseButtonDblClick;
+ if (button == Qt::LeftButton && (keyMods & Qt::MetaModifier)) {
+ button = Qt::RightButton;
+ [theView qt_setLeftButtonIsRightButton: true];
+ }
+ break;
+ case QEvent::MouseButtonRelease:
+ if (button == Qt::LeftButton && [theView qt_leftButtonIsRightButton]) {
+ button = Qt::RightButton;
+ [theView qt_setLeftButtonIsRightButton: false];
+ }
+ break;
+ }
+ [QT_MANGLE_NAMESPACE(QCocoaView) currentMouseEvent]->localPoint = localPoint;
+ QPoint qglobalPoint(flipPoint(globalPoint).toPoint());
+ QMouseEvent qme(eventType, qlocalPoint, qglobalPoint, button, buttons, keyMods);
+ qt_sendSpontaneousEvent(widgetToGetMouse, &qme);
+ if (eventType == QEvent::MouseButtonPress && button == Qt::RightButton) {
+ QContextMenuEvent qcme(QContextMenuEvent::Mouse, qlocalPoint, qglobalPoint, keyMods);
+ qt_sendSpontaneousEvent(widgetToGetMouse, &qcme);
+ }
+ return qme.isAccepted();
+#endif
+}
+
+bool qt_mac_handleTabletEvent(void * /*QCocoaView * */view, void * /*NSEvent * */tabletEvent)
+{
+#ifndef QT_MAC_USE_COCOA
+ Q_UNUSED(view);
+ Q_UNUSED(tabletEvent);
+ return false;
+#else
+ QT_MANGLE_NAMESPACE(QCocoaView) *theView = static_cast<QT_MANGLE_NAMESPACE(QCocoaView) *>(view);
+ NSView *theNSView = static_cast<NSView *>(view);
+ NSEvent *theTabletEvent = static_cast<NSEvent *>(tabletEvent);
+
+ NSEventType eventType = [theTabletEvent type];
+ if (eventType != NSTabletPoint && [theTabletEvent subtype] != NSTabletPointEventSubtype)
+ return false; // Not a tablet event.
+
+ NSPoint windowPoint = [theTabletEvent locationInWindow];
+ NSPoint globalPoint = [[theTabletEvent window] convertBaseToScreen:windowPoint];
+
+ QWidget *qwidget = [theView qt_qwidget];
+ QWidget *widgetToGetMouse = qwidget;
+ QWidget *popup = qAppInstance()->activePopupWidget();
+ if (popup && popup != qwidget->window())
+ widgetToGetMouse = popup;
+
+ if (qt_mac_sendMacEventToWidget(widgetToGetMouse,
+ static_cast<EventRef>(const_cast<void *>([theTabletEvent eventRef]))))
+ return true;
+ if (widgetToGetMouse != qwidget) {
+ theNSView = qt_mac_nativeview_for(widgetToGetMouse);
+ windowPoint = [[theNSView window] convertScreenToBase:globalPoint];
+ }
+ NSPoint localPoint = [theNSView convertPoint:windowPoint fromView:nil];
+ // Tablet events do not handle WA_TransparentForMouseEvents ATM
+ // In theory, people who set the WA_TransparentForMouseEvents attribute won't handle
+ // tablet events either in which case they will fall into the mouse event case and get
+ // them passed on. This will NOT handle the raw events, but that might not be a big problem.
+
+ const QMacTabletHash *tabletHash = qt_mac_tablet_hash();
+ if (!tabletHash->contains([theTabletEvent deviceID])) {
+ qWarning("QCocoaView handleTabletEvent: This tablet device is unknown"
+ " (received no proximity event for it). Discarding event.");
+ return false;
+ }
+ const QTabletDeviceData &deviceData = tabletHash->value([theTabletEvent deviceID]);
+
+
+ QEvent::Type qType;
+ switch (eventType) {
+ case NSLeftMouseDown:
+ case NSRightMouseDown:
+ qType = QEvent::TabletPress;
+ break;
+ case NSLeftMouseUp:
+ case NSRightMouseUp:
+ qType = QEvent::TabletRelease;
+ break;
+ case NSMouseMoved:
+ case NSTabletPoint:
+ case NSLeftMouseDragged:
+ case NSRightMouseDragged:
+ default:
+ qType = QEvent::TabletMove;
+ break;
+ }
+
+ qreal pressure;
+ if (eventType != NSMouseMoved) {
+ pressure = [theTabletEvent pressure];
+ } else {
+ pressure = 0.0;
+ }
+
+ NSPoint tilt = [theTabletEvent tilt];
+ int xTilt = qRound(tilt.x * 60.0);
+ int yTilt = qRound(tilt.y * -60.0);
+ qreal tangentialPressure = 0;
+ qreal rotation = 0;
+ int z = 0;
+ if (deviceData.capabilityMask & 0x0200)
+ z = [theTabletEvent absoluteZ];
+
+ if (deviceData.capabilityMask & 0x0800)
+ tangentialPressure = [theTabletEvent tangentialPressure];
+
+ rotation = [theTabletEvent rotation];
+ QPointF hiRes = flipPoint(globalPoint);
+ QTabletEvent qtabletEvent(qType, QPoint(localPoint.x, localPoint.y),
+ hiRes.toPoint(), hiRes,
+ deviceData.tabletDeviceType, deviceData.tabletPointerType,
+ pressure, xTilt, yTilt, tangentialPressure, rotation, z,
+ qt_cocoaModifiers2QtModifiers([theTabletEvent modifierFlags]),
+ deviceData.tabletUniqueID);
+
+ qt_sendSpontaneousEvent(widgetToGetMouse, &qtabletEvent);
+ return qtabletEvent.isAccepted();
+#endif
+}
+
+void qt_mac_updateContentBorderMetricts(void * /*OSWindowRef */window, const ::HIContentBorderMetrics &metrics)
+{
+ OSWindowRef theWindow = static_cast<OSWindowRef>(window);
+#if !defined(QT_MAC_USE_COCOA)
+# if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5
+ if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_5) {
+ ::HIWindowSetContentBorderThickness(theWindow, &metrics);
+ }
+# else
+ Q_UNUSED(window);
+ Q_UNUSED(metrics);
+# endif
+#else
+ if ([theWindow styleMask] & NSTexturedBackgroundWindowMask)
+ [theWindow setContentBorderThickness:metrics.top forEdge:NSMaxYEdge];
+ [theWindow setContentBorderThickness:metrics.bottom forEdge:NSMinYEdge];
+#endif
+}
+
+QStringList qt_mac_NSArrayToQStringList(void *nsarray)
+{
+ QStringList result;
+ NSArray *array = static_cast<NSArray *>(nsarray);
+ for (NSUInteger i=0; i<[array count]; ++i)
+ result << qt_mac_NSStringToQString([array objectAtIndex:i]);
+ return result;
+}
+
+void *qt_mac_QStringListToNSMutableArrayVoid(const QStringList &list)
+{
+ NSMutableArray *result = [NSMutableArray arrayWithCapacity:list.size()];
+ for (int i=0; i<list.size(); ++i){
+ [result addObject:reinterpret_cast<const NSString *>(QCFString::toCFStringRef(list[i]))];
+ }
+ return result;
+}
+
+void qt_syncCocoaTitleBarButtons(OSWindowRef window, QWidget *widgetForWindow)
+{
+ if (!widgetForWindow)
+ return;
+
+ Qt::WindowFlags flags = widgetForWindow->windowFlags();
+ bool customize = flags & Qt::CustomizeWindowHint;
+
+ NSButton *btn = [window standardWindowButton:NSWindowZoomButton];
+ // BOOL is not an int, so the bitwise AND doesn't work.
+ bool go = uint(customize && !(flags & Qt::WindowMaximizeButtonHint)) == 0;
+ [btn setEnabled:go];
+
+ btn = [window standardWindowButton:NSWindowMiniaturizeButton];
+ go = uint(customize && !(flags & Qt::WindowMinimizeButtonHint)) == 0;
+ [btn setEnabled:go];
+
+ btn = [window standardWindowButton:NSWindowCloseButton];
+ go = uint(customize && !(flags & Qt::WindowSystemMenuHint
+ || flags & Qt::WindowCloseButtonHint)) == 0;
+ [btn setEnabled:go];
+
+ [window setShowsToolbarButton:uint(flags & Qt::MacWindowToolBarButtonHint) != 0];
+}
+
+// Carbon: Make sure you call QDEndContext on the context when done with it.
+CGContextRef qt_mac_graphicsContextFor(QWidget *widget)
+{
+ if (!widget)
+ return 0;
+
+#ifndef QT_MAC_USE_COCOA
+ CGContextRef context;
+ CGrafPtr port = GetWindowPort(qt_mac_window_for(widget));
+ QDBeginCGContext(port, &context);
+#else
+ CGContextRef context = (CGContextRef)[[NSGraphicsContext graphicsContextWithWindow:qt_mac_window_for(widget)] graphicsPort];
+#endif
+ return context;
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/kernel/qt_cocoa_helpers_mac_p.h b/src/gui/kernel/qt_cocoa_helpers_mac_p.h
new file mode 100644
index 0000000000..ef55aa44c4
--- /dev/null
+++ b/src/gui/kernel/qt_cocoa_helpers_mac_p.h
@@ -0,0 +1,174 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/****************************************************************************
+**
+** Copyright (c) 2007-2008, Apple, Inc.
+**
+** All rights reserved.
+**
+** Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are met:
+**
+** * Redistributions of source code must retain the above copyright notice,
+** this list of conditions and the following disclaimer.
+**
+** * Redistributions in binary form must reproduce the above copyright notice,
+** this list of conditions and the following disclaimer in the documentation
+** and/or other materials provided with the distribution.
+**
+** * Neither the name of Apple, Inc. nor the names of its contributors
+** may be used to endorse or promote products derived from this software
+** without specific prior written permission.
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+** CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+** EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+** PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+** PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+** LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+** NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+** SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+**
+****************************************************************************/
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of qapplication_*.cpp, qwidget*.cpp, qcolor_x11.cpp, qfiledialog.cpp
+// and many other. This header file may change from version to version
+// without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <private/qt_mac_p.h>
+
+#include <qapplication.h>
+#include <qdesktopwidget.h>
+#include <qwidget.h>
+#include <qevent.h>
+#include <qhash.h>
+#include <qlabel.h>
+#include <qpointer.h>
+#include <qstyle.h>
+#include <qstyleoption.h>
+#include <qstylepainter.h>
+#include <qtimer.h>
+#include <qtooltip.h>
+#include <private/qeffects_p.h>
+#include <qtextdocument.h>
+#include <qdebug.h>
+#include <qpoint.h>
+#include "private/qt_mac_p.h"
+
+#ifdef Q_WS_MAC32
+typedef struct _NSPoint NSPoint; // Just redefine here so I don't have to pull in all of Cocoa.
+#else
+typedef struct CGPoint NSPoint;
+#endif
+
+QT_BEGIN_NAMESPACE
+Qt::MouseButtons qt_mac_get_buttons(int buttons);
+Qt::MouseButton qt_mac_get_button(EventMouseButton button);
+void macWindowFade(void * /*OSWindowRef*/ window, float durationSeconds = 0);
+bool macWindowIsTextured(void * /*OSWindowRef*/ window);
+void macWindowToolbarShow(const QWidget *widget, bool show );
+void macWindowToolbarSet( void * /*OSWindowRef*/ window, void* toolbarRef );
+bool macWindowToolbarVisible( void * /*OSWindowRef*/ window );
+void macWindowSetHasShadow( void * /*OSWindowRef*/ window, bool hasShadow );
+void macWindowFlush(void * /*OSWindowRef*/ window);
+struct HIContentBorderMetrics;
+void qt_mac_updateContentBorderMetricts(void * /*OSWindowRef */window, const ::HIContentBorderMetrics &metrics);
+void * /*NSImage */qt_mac_create_nsimage(const QPixmap &pm);
+void qt_mac_update_mouseTracking(QWidget *widget);
+OSStatus qt_mac_drawCGImage(CGContextRef cg, const CGRect *inbounds, CGImageRef);
+bool qt_mac_checkForNativeSizeGrip(const QWidget *widget);
+void qt_dispatchTabletProximityEvent(void * /*NSEvent * */ tabletEvent);
+#ifdef QT_MAC_USE_COCOA
+bool qt_dispatchKeyEventWithCocoa(void * /*NSEvent * */ keyEvent, QWidget *widgetToGetEvent);
+#endif
+bool qt_dispatchKeyEvent(void * /*NSEvent * */ keyEvent, QWidget *widgetToGetEvent);
+void qt_dispatchModifiersChanged(void * /*NSEvent * */flagsChangedEvent, QWidget *widgetToGetEvent);
+void qt_mac_dispatchNCMouseMessage(void */* NSWindow* */eventWindow, void */* NSEvent* */mouseEvent,
+ QWidget *widgetToGetEvent, bool &leftButtonIsRightButton);
+bool qt_mac_handleMouseEvent(void * /*QCocoaView * */view, void * /*NSEvent * */event, QEvent::Type eventType, Qt::MouseButton button);
+bool qt_mac_handleTabletEvent(void * /*QCocoaView * */view, void * /*NSEvent * */event);
+inline QApplication *qAppInstance() { return static_cast<QApplication *>(QCoreApplication::instance()); }
+struct ::TabletProximityRec;
+void qt_dispatchTabletProximityEvent(const ::TabletProximityRec &proxRec);
+Qt::KeyboardModifiers qt_cocoaModifiers2QtModifiers(ulong modifierFlags);
+inline int flipYCoordinate(int y)
+{
+ return QApplication::desktop()->screenGeometry(0).height() - y;
+}
+
+inline qreal flipYCoordinate(qreal y)
+{
+ return QApplication::desktop()->screenGeometry(0).height() - y;
+}
+
+QPointF flipPoint(const NSPoint &p);
+NSPoint flipPoint(const QPoint &p);
+NSPoint flipPoint(const QPointF &p);
+
+QStringList qt_mac_NSArrayToQStringList(void *nsarray);
+void *qt_mac_QStringListToNSMutableArrayVoid(const QStringList &list);
+
+void qt_syncCocoaTitleBarButtons(OSWindowRef window, QWidget *widgetForWindow);
+
+#ifdef __OBJC__
+inline NSMutableArray *qt_mac_QStringListToNSMutableArray(const QStringList &qstrlist)
+{ return reinterpret_cast<NSMutableArray *>(qt_mac_QStringListToNSMutableArrayVoid(qstrlist)); }
+
+inline QString qt_mac_NSStringToQString(const NSString *nsstr)
+{ return QCFString::toQString(reinterpret_cast<const CFStringRef>(nsstr)); }
+
+inline NSString *qt_mac_QStringToNSString(const QString &qstr)
+{ return [reinterpret_cast<const NSString *>(QCFString::toCFStringRef(qstr)) autorelease]; }
+#endif
+
+QT_END_NAMESPACE
diff --git a/src/gui/kernel/qt_gui_pch.h b/src/gui/kernel/qt_gui_pch.h
new file mode 100644
index 0000000000..9ce1a9281f
--- /dev/null
+++ b/src/gui/kernel/qt_gui_pch.h
@@ -0,0 +1,85 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@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.
+ */
+
+// from corelib/global/qt_pch.h
+#if defined __cplusplus
+#include <qglobal.h>
+
+
+#ifdef Q_WS_WIN
+# define _POSIX_
+# include <limits.h>
+# undef _POSIX_
+#endif
+
+#include <qcoreapplication.h>
+#include <qlist.h>
+#include <qvariant.h> // All moc genereated code has this include
+#include <qobject.h>
+#include <qregexp.h>
+#include <qstring.h>
+#include <qstringlist.h>
+#include <qtextcodec.h>
+
+#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 <qstyle.h>
+#include <qtimer.h>
+#include <qwidget.h>
+
+#include <stdlib.h>
+
+#endif
diff --git a/src/gui/kernel/qt_mac.cpp b/src/gui/kernel/qt_mac.cpp
new file mode 100644
index 0000000000..b462b13480
--- /dev/null
+++ b/src/gui/kernel/qt_mac.cpp
@@ -0,0 +1,144 @@
+/****************************************************************************
+ **
+ ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+ **
+ ** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+ **
+ ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
+ ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ **
+ ****************************************************************************/
+
+#include <private/qt_mac_p.h>
+#include <private/qpixmap_mac_p.h>
+#include <private/qnativeimage_p.h>
+#include <qdebug.h>
+
+QT_BEGIN_NAMESPACE
+#ifdef QT_MAC_USE_COCOA
+static CTFontRef CopyCTThemeFont(ThemeFontID themeID)
+{
+ CTFontUIFontType ctID = HIThemeGetUIFontType(themeID);
+ return CTFontCreateUIFontForLanguage(ctID, 0, 0);
+}
+#endif
+
+QFont qfontForThemeFont(ThemeFontID themeID)
+{
+#ifndef QT_MAC_USE_COCOA
+ static const ScriptCode Script = smRoman;
+ Str255 f_name;
+ SInt16 f_size;
+ Style f_style;
+ GetThemeFont(themeID, Script, f_name, &f_size, &f_style);
+ extern QString qt_mac_from_pascal_string(const Str255); //qglobal.cpp
+ return QFont(qt_mac_from_pascal_string(f_name), f_size,
+ (f_style & ::bold) ? QFont::Bold : QFont::Normal,
+ (bool)(f_style & ::italic));
+#else
+ QCFType<CTFontRef> ctfont = CopyCTThemeFont(themeID);
+ QString familyName = QCFString(CTFontCopyFamilyName(ctfont));
+ QCFType<CFDictionaryRef> dict = CTFontCopyTraits(ctfont);
+ CFNumberRef num = static_cast<CFNumberRef>(CFDictionaryGetValue(dict, kCTFontWeightTrait));
+ float fW;
+ CFNumberGetValue(num, kCFNumberFloat32Type, &fW);
+ QFont::Weight wght = fW > 0. ? QFont::Bold : QFont::Normal;
+ num = static_cast<CFNumberRef>(CFDictionaryGetValue(dict, kCTFontSlantTrait));
+ CFNumberGetValue(num, kCFNumberFloatType, &fW);
+ bool italic = (fW != 0.0);
+ return QFont(familyName, CTFontGetSize(ctfont), wght, italic);
+#endif
+}
+
+#if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5)
+static QColor qcolorFromCGColor(CGColorRef cgcolor)
+{
+ QColor pc;
+ CGColorSpaceModel model = CGColorSpaceGetModel(CGColorGetColorSpace(cgcolor));
+ const CGFloat *components = CGColorGetComponents(cgcolor);
+ if (model == kCGColorSpaceModelRGB) {
+ pc.setRgbF(components[0], components[1], components[2], components[3]);
+ } else if (model == kCGColorSpaceModelCMYK) {
+ pc.setCmykF(components[0], components[1], components[2], components[3]);
+ } else {
+ // Colorspace we can't deal with.
+ qWarning("Qt: qcolorFromCGColor: cannot convert from colorspace model: %d", model);
+ Q_ASSERT(false);
+ }
+ return pc;
+}
+
+static inline QColor leopardBrush(ThemeBrush brush)
+{
+ QCFType<CGColorRef> cgClr = 0;
+ HIThemeBrushCreateCGColor(brush, &cgClr);
+ return qcolorFromCGColor(cgClr);
+}
+#endif
+
+QColor qcolorForTheme(ThemeBrush brush)
+{
+#ifndef QT_MAC_USE_COCOA
+# if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5)
+ if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_5) {
+ return leopardBrush(brush);
+ } else
+# endif
+ {
+ RGBColor rgbcolor;
+ GetThemeBrushAsColor(brush, 32, true, &rgbcolor);
+ return QColor(rgbcolor.red / 256, rgbcolor.green / 256, rgbcolor.blue / 256);
+ }
+#else
+ return leopardBrush(brush);
+#endif
+}
+
+QColor qcolorForThemeTextColor(ThemeTextColor themeColor)
+{
+#ifndef QT_MAC_USE_COCOA
+ RGBColor c;
+ GetThemeTextColor(themeColor, 32, true, &c);
+ return QColor(c.red / 265, c.green / 256, c.blue / 256);
+#else
+ QNativeImage nativeImage(16,16, QNativeImage::systemFormat());
+ CGRect cgrect = CGRectMake(0, 0, 16, 16);
+ HIThemeSetTextFill(themeColor, 0, nativeImage.cg, kHIThemeOrientationNormal);
+ CGContextFillRect(nativeImage.cg, cgrect);
+ return QColor(nativeImage.image.pixel(0 , 0));
+#endif
+}
+QT_END_NAMESPACE
diff --git a/src/gui/kernel/qt_mac_p.h b/src/gui/kernel/qt_mac_p.h
new file mode 100644
index 0000000000..3aec23fcf3
--- /dev/null
+++ b/src/gui/kernel/qt_mac_p.h
@@ -0,0 +1,265 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QT_MAC_P_H
+#define QT_MAC_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 "qmacdefines_mac.h"
+
+#ifdef __OBJC__
+#include <Cocoa/Cocoa.h>
+#endif
+
+#include <CoreServices/CoreServices.h>
+
+#include "QtCore/qglobal.h"
+#include "QtCore/qvariant.h"
+#include "QtCore/qmimedata.h"
+#include "QtCore/qpointer.h"
+#include "private/qcore_mac_p.h"
+
+
+#include "QtGui/qpainter.h"
+
+#include <Carbon/Carbon.h>
+
+QT_BEGIN_NAMESPACE
+class QWidget;
+class QDragMoveEvent;
+
+/* Event masks */
+// internal Qt types
+
+ // Event class for our own Carbon events.
+#if defined(QT_NAMESPACE) && defined(QT_NAMESPACE_MAC_CRC)
+// Take the CRC we generated at configure time. This *may* result in a
+// collision with another value If that is the case, please change the value
+// here to something other than 'Cute'.
+const UInt32 kEventClassQt = QT_NAMESPACE_MAC_CRC;
+#else
+const UInt32 kEventClassQt = 'Cute';
+#endif
+
+enum {
+ //AE types
+ typeAEClipboardChanged = 1,
+ //types
+ typeQWidget = 1, /* QWidget * */
+ //params
+ kEventParamQWidget = 'qwid', /* typeQWidget */
+ //events
+ kEventQtRequestContext = 13,
+ kEventQtRequestMenubarUpdate = 14,
+ kEventQtRequestShowSheet = 17,
+ kEventQtRequestActivate = 18,
+ kEventQtRequestWindowChange = 20
+};
+
+// Simple class to manage short-lived regions
+class QMacSmartQuickDrawRegion
+{
+ RgnHandle qdRgn;
+ Q_DISABLE_COPY(QMacSmartQuickDrawRegion)
+public:
+ explicit QMacSmartQuickDrawRegion(RgnHandle rgn) : qdRgn(rgn) {}
+ ~QMacSmartQuickDrawRegion() {
+ extern void qt_mac_dispose_rgn(RgnHandle); // qregion_mac.cpp
+ qt_mac_dispose_rgn(qdRgn);
+ }
+ operator RgnHandle() {
+ return qdRgn;
+ }
+};
+
+class Q_GUI_EXPORT QMacCocoaAutoReleasePool
+{
+private:
+ void *pool;
+public:
+ QMacCocoaAutoReleasePool();
+ ~QMacCocoaAutoReleasePool();
+
+ inline void *handle() const { return pool; }
+};
+
+QString qt_mac_removeMnemonics(const QString &original); //implemented in qmacstyle_mac.cpp
+
+class Q_GUI_EXPORT QMacWindowChangeEvent
+{
+private:
+ static QList<QMacWindowChangeEvent*> *change_events;
+public:
+ QMacWindowChangeEvent() {
+ }
+ virtual ~QMacWindowChangeEvent() {
+ }
+ static inline void exec(bool ) {
+ }
+protected:
+ virtual void windowChanged() = 0;
+ virtual void flushWindowChanged() = 0;
+};
+
+class QMacCGContext
+{
+ CGContextRef context;
+public:
+ QMacCGContext(QPainter *p); //qpaintengine_mac.cpp
+ inline QMacCGContext() { context = 0; }
+ inline QMacCGContext(const QPaintDevice *pdev) {
+ extern CGContextRef qt_mac_cg_context(const QPaintDevice *);
+ context = qt_mac_cg_context(pdev);
+ }
+ inline QMacCGContext(CGContextRef cg, bool takeOwnership=false) {
+ context = cg;
+ if(!takeOwnership)
+ CGContextRetain(context);
+ }
+ inline QMacCGContext(const QMacCGContext &copy) : context(0) { *this = copy; }
+ inline ~QMacCGContext() {
+ if(context)
+ CGContextRelease(context);
+ }
+ inline bool isNull() const { return context; }
+ inline operator CGContextRef() { return context; }
+ inline QMacCGContext &operator=(const QMacCGContext &copy) {
+ if(context)
+ CGContextRelease(context);
+ context = copy.context;
+ CGContextRetain(context);
+ return *this;
+ }
+ inline QMacCGContext &operator=(CGContextRef cg) {
+ if(context)
+ CGContextRelease(context);
+ context = cg;
+ CGContextRetain(context); //we do not take ownership
+ return *this;
+ }
+};
+
+class QMacPasteboardMime;
+class QMimeData;
+
+class QMacPasteboard
+{
+ struct Promise {
+ Promise() : itemId(0), convertor(0) { }
+ Promise(int itemId, QMacPasteboardMime *c, QString m, QVariant d, int o=0) : itemId(itemId), offset(o), convertor(c), mime(m), data(d) { }
+ int itemId, offset;
+ QMacPasteboardMime *convertor;
+ QString mime;
+ QVariant data;
+ };
+ QList<Promise> promises;
+
+ OSPasteboardRef paste;
+ uchar mime_type;
+ mutable QPointer<QMimeData> mime;
+ mutable bool mac_mime_source;
+ static OSStatus promiseKeeper(OSPasteboardRef, PasteboardItemID, CFStringRef, void *);
+ void clear_helper();
+public:
+ QMacPasteboard(OSPasteboardRef p, uchar mime_type=0);
+ QMacPasteboard(uchar mime_type);
+ QMacPasteboard(CFStringRef name=0, uchar mime_type=0);
+ ~QMacPasteboard();
+
+ bool hasFlavor(QString flavor) const;
+ bool hasOSType(int c_flavor) const;
+
+ OSPasteboardRef pasteBoard() const;
+ QMimeData *mimeData() const;
+ void setMimeData(QMimeData *mime);
+
+ QStringList formats() const;
+ bool hasFormat(const QString &format) const;
+ QVariant retrieveData(const QString &format, QVariant::Type) const;
+
+ void clear();
+ bool sync() const;
+};
+
+extern QPaintDevice *qt_mac_safe_pdev; //qapplication_mac.cpp
+
+extern OSWindowRef qt_mac_window_for(const QWidget*); //qwidget_mac.mm
+extern OSViewRef qt_mac_nativeview_for(const QWidget *); //qwidget_mac.mm
+
+#ifdef check
+# undef check
+#endif
+
+QFont qfontForThemeFont(ThemeFontID themeID);
+
+QColor qcolorForTheme(ThemeBrush brush);
+
+QColor qcolorForThemeTextColor(ThemeTextColor themeColor);
+
+struct QMacDndAnswerRecord {
+ QRect rect;
+ Qt::KeyboardModifiers modifiers;
+ Qt::MouseButtons buttons;
+ Qt::DropAction lastAction;
+ void clear() {
+ rect = QRect();
+ modifiers = Qt::NoModifier;
+ buttons = Qt::NoButton;
+ lastAction = Qt::IgnoreAction;
+ }
+};
+extern QMacDndAnswerRecord qt_mac_dnd_answer_rec;
+void qt_mac_copy_answer_rect(const QDragMoveEvent &event);
+bool qt_mac_mouse_inside_answer_rect(QPoint mouse);
+
+QT_END_NAMESPACE
+
+#endif // QT_MAC_P_H
diff --git a/src/gui/kernel/qt_x11_p.h b/src/gui/kernel/qt_x11_p.h
new file mode 100644
index 0000000000..563b7e96e3
--- /dev/null
+++ b/src/gui/kernel/qt_x11_p.h
@@ -0,0 +1,723 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QT_X11_P_H
+#define QT_X11_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 "QtGui/qwindowdefs.h"
+#include "QtCore/qlist.h"
+#include "QtCore/qvariant.h"
+
+// the following is necessary to work around breakage in many versions
+// of XFree86's Xlib.h still in use
+// ### which versions?
+#if defined(_XLIB_H_) // crude hack, but...
+#error "cannot include <X11/Xlib.h> before this file"
+#endif
+#define XRegisterIMInstantiateCallback qt_XRegisterIMInstantiateCallback
+#define XUnregisterIMInstantiateCallback qt_XUnregisterIMInstantiateCallback
+#define XSetIMValues qt_XSetIMValues
+#include <X11/Xlib.h>
+#undef XRegisterIMInstantiateCallback
+#undef XUnregisterIMInstantiateCallback
+#undef XSetIMValues
+
+#include <X11/Xutil.h>
+#include <X11/Xos.h>
+#ifdef index
+# undef index
+#endif
+#ifdef rindex
+# undef rindex
+#endif
+#include <X11/Xatom.h>
+
+//#define QT_NO_SHAPE
+#ifdef QT_NO_SHAPE
+# define XShapeCombineRegion(a,b,c,d,e,f,g)
+# define XShapeCombineMask(a,b,c,d,e,f,g)
+#else
+# include <X11/extensions/shape.h>
+#endif // QT_NO_SHAPE
+
+
+#if !defined (QT_NO_TABLET)
+# include <X11/extensions/XInput.h>
+#if defined (Q_OS_IRIX)
+# include <X11/extensions/SGIMisc.h>
+# include <wacom.h>
+#endif
+#endif // QT_NO_TABLET
+
+
+// #define QT_NO_XINERAMA
+#ifndef QT_NO_XINERAMA
+# if 0 // ### Xsun, but how to detect it?
+// Xinerama is only supported in Solaris 7 with patches 107648/108376 and
+// Solaris 8 or above which introduce the X11R6.4 Xserver.
+// To switch the Xinerama functionality on, you need to add the "+xinerama"
+// argument to the Xsun start line.
+// At least Solaris 7 and 8 are missing Xinerama system headers and function
+// declarations (bug 4284701).
+// The Xinerama API is not documented. In theory it could change but it
+// probably won't because Sun are using it in at least dtlogin (bug 4221829).
+extern "C" Bool XPanoramiXQueryExtension(
+ Display*,
+ int*,
+ int*
+);
+extern "C" Status XPanoramiXQueryVersion(
+ Display*,
+ int*,
+ int*
+);
+extern "C" Status XPanoramiXGetState(
+ Display*,
+ Drawable,
+ XPanoramiXInfo*
+);
+extern "C" Status XPanoramiXGetScreenCount(
+ Display *,
+ Drawable,
+ XPanoramiXInfo*
+);
+extern "C" Status XPanoramiXGetScreenSize(
+ Display*,
+ Drawable,
+ int,
+ XPanoramiXInfo*
+);
+# else // XFree86
+// XFree86 does not C++ify Xinerama (at least up to XFree86 4.0.3).
+extern "C" {
+# include <X11/extensions/Xinerama.h>
+}
+# endif
+#endif // QT_NO_XINERAMA
+
+// #define QT_NO_XRANDR
+#ifndef QT_NO_XRANDR
+# include <X11/extensions/Xrandr.h>
+#endif // QT_NO_XRANDR
+
+// #define QT_NO_XRENDER
+#ifndef QT_NO_XRENDER
+# include <X11/extensions/Xrender.h>
+#endif // QT_NO_XRENDER
+
+// #define QT_NO_XKB
+#ifndef QT_NO_XKB
+# include <X11/XKBlib.h>
+#endif // QT_NO_XKB
+
+
+#if !defined(XlibSpecificationRelease)
+# define X11R4
+typedef char *XPointer;
+#else
+# undef X11R4
+#endif
+
+// #define QT_NO_XIM
+#if defined(X11R4)
+// X11R4 does not have XIM
+#define QT_NO_XIM
+#elif defined(Q_OS_OSF) && (XlibSpecificationRelease < 6)
+// broken in Xlib up to OSF/1 3.2
+#define QT_NO_XIM
+#elif defined(Q_OS_AIX)
+// broken in Xlib up to what version of AIX?
+#define QT_NO_XIM
+#elif defined(QT_NO_DEBUG) && defined(Q_OS_IRIX)
+// XmbLookupString broken on IRIX
+// XCreateIC broken when compiling -64 on IRIX 6.5.2
+#define QT_NO_XIM
+#elif defined(Q_OS_HPUX) && defined(__LP64__)
+// XCreateIC broken when compiling 64-bit ELF on HP-UX 11.0
+#define QT_NO_XIM
+#elif defined(Q_OS_SCO)
+// ### suggested by user...
+// ### #define QT_NO_XIM
+#endif // QT_NO_XIM
+
+#ifndef QT_NO_XFIXES
+typedef Bool (*PtrXFixesQueryExtension)(Display *, int *, int *);
+typedef Status (*PtrXFixesQueryVersion)(Display *, int *, int *);
+typedef void (*PtrXFixesSetCursorName)(Display *dpy, Cursor cursor, const char *name);
+typedef void (*PtrXFixesSelectSelectionInput)(Display *dpy, Window win, Atom selection, unsigned long eventMask);
+#endif // QT_NO_XFIXES
+
+#ifndef QT_NO_XCURSOR
+#include <X11/Xcursor/Xcursor.h>
+typedef Cursor (*PtrXcursorLibraryLoadCursor)(Display *, const char *);
+#endif // QT_NO_XCURSOR
+
+#ifndef QT_NO_XINERAMA
+typedef Bool (*PtrXineramaQueryExtension)(Display *dpy, int *event_base, int *error_base);
+typedef Bool (*PtrXineramaIsActive)(Display *dpy);
+typedef XineramaScreenInfo *(*PtrXineramaQueryScreens)(Display *dpy, int *number);
+#endif // QT_NO_XINERAMA
+
+#ifndef QT_NO_XRANDR
+typedef void (*PtrXRRSelectInput)(Display *, Window, int);
+typedef int (*PtrXRRUpdateConfiguration)(XEvent *);
+typedef int (*PtrXRRRootToScreen)(Display *, Window);
+typedef Bool (*PtrXRRQueryExtension)(Display *, int *, int *);
+#endif // QT_NO_XRANDR
+
+#ifndef QT_NO_XINPUT
+typedef int (*PtrXCloseDevice)(Display *, XDevice *);
+typedef XDeviceInfo* (*PtrXListInputDevices)(Display *, int *);
+typedef XDevice* (*PtrXOpenDevice)(Display *, XID);
+typedef void (*PtrXFreeDeviceList)(XDeviceInfo *);
+typedef int (*PtrXSelectExtensionEvent)(Display *, Window, XEventClass *, int);
+#endif // QT_NO_XINPUT
+
+/*
+ * Solaris patch 108652-47 and higher fixes crases in
+ * XRegisterIMInstantiateCallback, but the function doesn't seem to
+ * work.
+ *
+ * Instead, we disabled R6 input, and open the input method
+ * immediately at application start.
+ */
+#if !defined(QT_NO_XIM) && (XlibSpecificationRelease >= 6) && \
+ !defined(Q_OS_SOLARIS)
+#define USE_X11R6_XIM
+
+//######### XFree86 has wrong declarations for XRegisterIMInstantiateCallback
+//######### and XUnregisterIMInstantiateCallback in at least version 3.3.2.
+//######### Many old X11R6 header files lack XSetIMValues.
+//######### Therefore, we have to declare these functions ourselves.
+
+extern "C" Bool XRegisterIMInstantiateCallback(
+ Display*,
+ struct _XrmHashBucketRec*,
+ char*,
+ char*,
+ XIMProc, //XFree86 has XIDProc, which has to be wrong
+ XPointer
+);
+
+extern "C" Bool XUnregisterIMInstantiateCallback(
+ Display*,
+ struct _XrmHashBucketRec*,
+ char*,
+ char*,
+ XIMProc, //XFree86 has XIDProc, which has to be wrong
+ XPointer
+);
+
+extern "C" char *XSetIMValues(XIM /* im */, ...);
+
+#endif
+
+#ifndef QT_NO_FONTCONFIG
+#include <fontconfig/fontconfig.h>
+#endif
+
+#ifndef QT_NO_XIM
+// some platforms (eg. Solaris 2.51) don't have these defines in Xlib.h
+#ifndef XNResetState
+#define XNResetState "resetState"
+#endif
+#ifndef XIMPreserveState
+#define XIMPreserveState (1L<<1)
+#endif
+#endif
+
+
+#ifndef X11R4
+# include <X11/Xlocale.h>
+#endif // X11R4
+
+
+#ifndef QT_NO_MITSHM
+# include <X11/extensions/XShm.h>
+#endif // QT_NO_MITSHM
+
+QT_BEGIN_NAMESPACE
+
+class QWidget;
+
+struct QX11InfoData {
+ uint ref;
+ int screen;
+ int dpiX;
+ int dpiY;
+ int depth;
+ int cells;
+ Colormap colormap;
+ Visual *visual;
+ bool defaultColormap;
+ bool defaultVisual;
+ int subpixel;
+};
+
+class QDrag;
+struct QXdndDropTransaction
+{
+ Time timestamp;
+ Window target;
+ Window proxy_target;
+ QWidget *targetWidget;
+ QWidget *embedding_widget;
+ QDrag *object;
+};
+
+class QMimeData;
+
+struct QX11Data;
+extern QX11Data *qt_x11Data;
+
+enum DesktopEnvironment {
+ DE_UNKNOWN,
+ DE_KDE,
+ DE_GNOME,
+ DE_CDE,
+ DE_4DWM
+};
+
+struct QX11Data
+{
+ static Qt::KeyboardModifiers translateModifiers(int s);
+
+ Window findClientWindow(Window, Atom, bool);
+
+ // from qclipboard_x11.cpp
+ bool clipboardWaitForEvent(Window win, int type, XEvent *event, int timeout);
+ bool clipboardReadProperty(Window win, Atom property, bool deleteProperty,
+ QByteArray *buffer, int *size, Atom *type, int *format, bool nullterm);
+ QByteArray clipboardReadIncrementalProperty(Window win, Atom property, int nbytes, bool nullterm);
+
+ // from qdnd_x11.cpp
+ bool dndEnable(QWidget* w, bool on);
+ static void xdndSetup();
+ void xdndHandleEnter(QWidget *, const XEvent *, bool);
+ void xdndHandlePosition(QWidget *, const XEvent *, bool);
+ void xdndHandleStatus(QWidget *, const XEvent *, bool);
+ void xdndHandleLeave(QWidget *, const XEvent *, bool);
+ void xdndHandleDrop(QWidget *, const XEvent *, bool);
+ void xdndHandleFinished(QWidget *, const XEvent *, bool);
+ void xdndHandleSelectionRequest(const XSelectionRequestEvent *);
+ static bool xdndHandleBadwindow();
+ QByteArray xdndAtomToString(Atom a);
+ Atom xdndStringToAtom(const char *);
+
+ QString xdndMimeAtomToString(Atom a);
+ Atom xdndMimeStringToAtom(const QString &mimeType);
+ QStringList xdndMimeFormatsForAtom(Atom a);
+ bool xdndMimeDataForAtom(Atom a, QMimeData *mimeData, QByteArray *data, Atom *atomFormat, int *dataFormat);
+ QList<Atom> xdndMimeAtomsForFormat(const QString &format);
+ QVariant xdndMimeConvertToFormat(Atom a, const QByteArray &data, const QString &format, QVariant::Type requestedType, const QByteArray &encoding);
+ Atom xdndMimeAtomForFormat(const QString &format, QVariant::Type requestedType, const QList<Atom> &atoms, QByteArray *requestedEncoding);
+
+ QList<QXdndDropTransaction> dndDropTransactions;
+
+ // from qmotifdnd_x11.cpp
+ void motifdndHandle(QWidget *, const XEvent *, bool);
+ void motifdndEnable(QWidget *, bool);
+ QVariant motifdndObtainData(const char *format);
+ QByteArray motifdndFormat(int n);
+ bool motifdnd_active;
+
+ Display *display;
+ char *displayName;
+ bool foreignDisplay;
+ // current focus model
+ enum {
+ FM_Unknown = -1,
+ FM_Other = 0,
+ FM_PointerRoot = 1
+ };
+ int focus_model;
+
+ // true if Qt is compiled w/ RANDR support and RANDR is supported on the connected Display
+ bool use_xrandr;
+ int xrandr_major;
+ int xrandr_eventbase;
+ int xrandr_errorbase;
+
+ // true if Qt is compiled w/ RENDER support and RENDER is supported on the connected Display
+ bool use_xrender;
+ int xrender_major;
+ int xrender_version;
+
+ // true if Qt is compiled w/ XFIXES support and XFIXES is supported on the connected Display
+ bool use_xfixes;
+ int xfixes_major;
+ int xfixes_eventbase;
+ int xfixes_errorbase;
+
+#ifndef QT_NO_XFIXES
+ PtrXFixesQueryExtension ptrXFixesQueryExtension;
+ PtrXFixesQueryVersion ptrXFixesQueryVersion;
+ PtrXFixesSetCursorName ptrXFixesSetCursorName;
+ PtrXFixesSelectSelectionInput ptrXFixesSelectSelectionInput;
+#endif
+
+#ifndef QT_NO_XINPUT
+ PtrXCloseDevice ptrXCloseDevice;
+ PtrXListInputDevices ptrXListInputDevices;
+ PtrXOpenDevice ptrXOpenDevice;
+ PtrXFreeDeviceList ptrXFreeDeviceList;
+ PtrXSelectExtensionEvent ptrXSelectExtensionEvent;
+#endif // QT_NO_XINPUT
+
+
+ // true if Qt is compiled w/ MIT-SHM support and MIT-SHM is supported on the connected Display
+ bool use_mitshm;
+ int mitshm_major;
+
+ // true if Qt is compiled w/ Tablet support and we have a tablet.
+ bool use_xinput;
+ int xinput_major;
+ int xinput_eventbase;
+ int xinput_errorbase;
+
+ QList<QWidget *> deferred_map;
+ struct ScrollInProgress {
+ long id;
+ QWidget* scrolled_widget;
+ int dx, dy;
+ };
+ long sip_serial;
+ QList<ScrollInProgress> sip_list;
+
+ // window managers list of supported "stuff"
+ Atom *net_supported_list;
+ // list of virtual root windows
+ Window *net_virtual_root_list;
+ // client leader window
+ Window wm_client_leader;
+
+ QX11InfoData *screens;
+ Visual **argbVisuals;
+ Colormap *argbColormaps;
+ int screenCount;
+ int defaultScreen;
+
+ Time time;
+ Time userTime;
+
+ QString default_im;
+
+ // starts to ignore bad window errors from X
+ static inline void ignoreBadwindow() {
+ qt_x11Data->ignore_badwindow = true;
+ qt_x11Data->seen_badwindow = false;
+ }
+
+ // ends ignoring bad window errors and returns whether an error had happened.
+ static inline bool badwindow() {
+ qt_x11Data->ignore_badwindow = false;
+ return qt_x11Data->seen_badwindow;
+ }
+
+ bool ignore_badwindow;
+ bool seen_badwindow;
+
+ // options
+ int visual_class;
+ int visual_id;
+ int color_count;
+ bool custom_cmap;
+
+ // outside visual/colormap
+ Visual *visual;
+ Colormap colormap;
+
+#ifndef QT_NO_XRENDER
+ enum { solid_fill_count = 16 };
+ struct SolidFills {
+ XRenderColor color;
+ int screen;
+ Picture picture;
+ } solid_fills[solid_fill_count];
+ enum { pattern_fill_count = 16 };
+ struct PatternFills {
+ XRenderColor color;
+ XRenderColor bg_color;
+ int screen;
+ int style;
+ bool opaque;
+ Picture picture;
+ } pattern_fills[pattern_fill_count];
+ Picture getSolidFill(int screen, const QColor &c);
+ XRenderColor preMultiply(const QColor &c);
+#endif
+
+ bool has_fontconfig;
+ qreal fc_scale;
+ bool fc_antialias;
+ int fc_hint_style;
+
+ char *startupId;
+ char *originalStartupId;
+
+ DesktopEnvironment desktopEnvironment;
+
+ /* Warning: if you modify this list, modify the names of atoms in qapplication_x11.cpp as well! */
+ enum X11Atom {
+ // window-manager <-> client protocols
+ WM_PROTOCOLS,
+ WM_DELETE_WINDOW,
+ WM_TAKE_FOCUS,
+ _NET_WM_PING,
+ _NET_WM_CONTEXT_HELP,
+
+ // ICCCM window state
+ WM_STATE,
+ WM_CHANGE_STATE,
+
+ // Session management
+ WM_CLIENT_LEADER,
+ WM_WINDOW_ROLE,
+ SM_CLIENT_ID,
+
+ // Clipboard
+ CLIPBOARD,
+ INCR,
+ TARGETS,
+ MULTIPLE,
+ TIMESTAMP,
+ CLIP_TEMPORARY,
+ _QT_SELECTION,
+ _QT_CLIPBOARD_SENTINEL,
+ _QT_SELECTION_SENTINEL,
+
+ RESOURCE_MANAGER,
+
+ _XSETROOT_ID,
+
+ _QT_SCROLL_DONE,
+ _QT_INPUT_ENCODING,
+
+ _MOTIF_WM_HINTS,
+
+ DTWM_IS_RUNNING,
+ KDE_FULL_SESSION,
+ KWIN_RUNNING,
+ KWM_RUNNING,
+ GNOME_BACKGROUND_PROPERTIES,
+ ENLIGHTENMENT_DESKTOP,
+ _SGI_DESKS_MANAGER,
+
+ // EWMH (aka NETWM)
+ _NET_SUPPORTED,
+ _NET_VIRTUAL_ROOTS,
+ _NET_WORKAREA,
+
+ _NET_MOVERESIZE_WINDOW,
+ _NET_WM_MOVERESIZE,
+
+ _NET_WM_NAME,
+ _NET_WM_ICON_NAME,
+ _NET_WM_ICON,
+
+ _NET_WM_PID,
+
+ _NET_WM_WINDOW_OPACITY,
+
+ _NET_WM_STATE,
+ _NET_WM_STATE_ABOVE,
+ _NET_WM_STATE_BELOW,
+ _NET_WM_STATE_FULLSCREEN,
+ _NET_WM_STATE_MAXIMIZED_HORZ,
+ _NET_WM_STATE_MAXIMIZED_VERT,
+ _NET_WM_STATE_MODAL,
+ _NET_WM_STATE_STAYS_ON_TOP,
+ _NET_WM_STATE_DEMANDS_ATTENTION,
+
+ _NET_WM_USER_TIME,
+ _NET_WM_USER_TIME_WINDOW,
+ _NET_WM_FULL_PLACEMENT,
+
+ _NET_WM_WINDOW_TYPE,
+ _NET_WM_WINDOW_TYPE_DESKTOP,
+ _NET_WM_WINDOW_TYPE_DOCK,
+ _NET_WM_WINDOW_TYPE_TOOLBAR,
+ _NET_WM_WINDOW_TYPE_MENU,
+ _NET_WM_WINDOW_TYPE_UTILITY,
+ _NET_WM_WINDOW_TYPE_SPLASH,
+ _NET_WM_WINDOW_TYPE_DIALOG,
+ _NET_WM_WINDOW_TYPE_DROPDOWN_MENU,
+ _NET_WM_WINDOW_TYPE_POPUP_MENU,
+ _NET_WM_WINDOW_TYPE_TOOLTIP,
+ _NET_WM_WINDOW_TYPE_NOTIFICATION,
+ _NET_WM_WINDOW_TYPE_COMBO,
+ _NET_WM_WINDOW_TYPE_DND,
+ _NET_WM_WINDOW_TYPE_NORMAL,
+ _KDE_NET_WM_WINDOW_TYPE_OVERRIDE,
+
+ _KDE_NET_WM_FRAME_STRUT,
+
+ _NET_STARTUP_INFO,
+ _NET_STARTUP_INFO_BEGIN,
+
+ _NET_SUPPORTING_WM_CHECK,
+
+ _NET_WM_CM_S0,
+
+ _NET_SYSTEM_TRAY_VISUAL,
+
+ // Property formats
+ COMPOUND_TEXT,
+ TEXT,
+ UTF8_STRING,
+
+ // Xdnd
+ XdndEnter,
+ XdndPosition,
+ XdndStatus,
+ XdndLeave,
+ XdndDrop,
+ XdndFinished,
+ XdndTypelist,
+ XdndActionList,
+
+ XdndSelection,
+
+ XdndAware,
+ XdndProxy,
+
+ XdndActionCopy,
+ XdndActionLink,
+ XdndActionMove,
+ XdndActionPrivate,
+
+ // Motif DND
+ _MOTIF_DRAG_AND_DROP_MESSAGE,
+ _MOTIF_DRAG_INITIATOR_INFO,
+ _MOTIF_DRAG_RECEIVER_INFO,
+ _MOTIF_DRAG_WINDOW,
+ _MOTIF_DRAG_TARGETS,
+
+ XmTRANSFER_SUCCESS,
+ XmTRANSFER_FAILURE,
+
+ // Xkb
+ _XKB_RULES_NAMES,
+
+ // XEMBED
+ _XEMBED,
+ _XEMBED_INFO,
+
+ NPredefinedAtoms,
+
+ _QT_SETTINGS_TIMESTAMP = NPredefinedAtoms,
+ NAtoms
+ };
+ Atom atoms[NAtoms];
+
+ bool isSupportedByWM(Atom atom);
+
+ bool compositingManagerRunning;
+
+#ifndef QT_NO_XCURSOR
+ PtrXcursorLibraryLoadCursor ptrXcursorLibraryLoadCursor;
+#endif // QT_NO_XCURSOR
+
+#ifndef QT_NO_XINERAMA
+ PtrXineramaQueryExtension ptrXineramaQueryExtension;
+ PtrXineramaIsActive ptrXineramaIsActive;
+ PtrXineramaQueryScreens ptrXineramaQueryScreens;
+#endif // QT_NO_XINERAMA
+
+#ifndef QT_NO_XRANDR
+ PtrXRRSelectInput ptrXRRSelectInput;
+ PtrXRRUpdateConfiguration ptrXRRUpdateConfiguration;
+ PtrXRRRootToScreen ptrXRRRootToScreen;
+ PtrXRRQueryExtension ptrXRRQueryExtension;
+#endif // QT_NO_XRANDR
+};
+
+extern QX11Data *qt_x11Data;
+#define ATOM(x) qt_x11Data->atoms[QX11Data::x]
+#define X11 qt_x11Data
+
+// rename a couple of X defines to get rid of name clashes
+// resolve the conflict between X11's FocusIn and QEvent::FocusIn
+enum {
+ XFocusOut = FocusOut,
+ XFocusIn = FocusIn,
+ XKeyPress = KeyPress,
+ XKeyRelease = KeyRelease,
+ XNone = None,
+ XRevertToParent = RevertToParent,
+ XGrayScale = GrayScale,
+ XCursorShape = CursorShape
+};
+#undef FocusOut
+#undef FocusIn
+#undef KeyPress
+#undef KeyRelease
+#undef None
+#undef RevertToParent
+#undef GrayScale
+#undef CursorShape
+
+#ifdef FontChange
+#undef FontChange
+#endif
+
+Q_DECLARE_TYPEINFO(XPoint, Q_PRIMITIVE_TYPE);
+Q_DECLARE_TYPEINFO(XRectangle, Q_PRIMITIVE_TYPE);
+Q_DECLARE_TYPEINFO(XChar2b, Q_PRIMITIVE_TYPE);
+#ifndef QT_NO_XRENDER
+Q_DECLARE_TYPEINFO(XGlyphElt32, Q_PRIMITIVE_TYPE);
+#endif
+
+
+QT_END_NAMESPACE
+
+#endif // QT_X11_P_H
diff --git a/src/gui/kernel/qtooltip.cpp b/src/gui/kernel/qtooltip.cpp
new file mode 100644
index 0000000000..15a3dd24e2
--- /dev/null
+++ b/src/gui/kernel/qtooltip.cpp
@@ -0,0 +1,609 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#ifdef Q_WS_MAC
+# include <private/qcore_mac_p.h>
+#endif
+
+#include <qapplication.h>
+#include <qdesktopwidget.h>
+#include <qevent.h>
+#include <qhash.h>
+#include <qlabel.h>
+#include <qpointer.h>
+#include <qstyle.h>
+#include <qstyleoption.h>
+#include <qstylepainter.h>
+#include <qtimer.h>
+#include <qtooltip.h>
+#include <private/qeffects_p.h>
+#include <qtextdocument.h>
+#include <qdebug.h>
+#include <private/qstylesheetstyle_p.h>
+#ifndef QT_NO_TOOLTIP
+
+#ifdef Q_WS_MAC
+# include <private/qcore_mac_p.h>
+#include <private/qt_cocoa_helpers_mac_p.h>
+#endif
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QToolTip
+
+ \brief The QToolTip class provides tool tips (balloon help) for any
+ widget.
+
+ \ingroup helpsystem
+ \mainclass
+
+ The tip is a short piece of text reminding the user of the
+ widget's function. It is drawn immediately below the given
+ position in a distinctive black-on-yellow color combination. The
+ tip can be any \l{QTextEdit}{rich text} formatted string.
+
+ Rich text displayed in a tool tip is implicitly word-wrapped unless
+ specified differently with \c{<p style='white-space:pre'>}.
+
+ The simplest and most common way to set a widget's tool tip is by
+ calling its QWidget::setToolTip() function.
+
+ It is also possible to show different tool tips for different
+ regions of a widget, by using a QHelpEvent of type
+ QEvent::ToolTip. Intercept the help event in your widget's \l
+ {QWidget::}{event()} function and call QToolTip::showText() with
+ the text you want to display. The \l{widgets/tooltips}{Tooltips}
+ example illustrates this technique.
+
+ If you are calling QToolTip::hideText(), or QToolTip::showText()
+ with an empty string, as a result of a \l{QEvent::}{ToolTip}-event you
+ should also call \l{QEvent::}{ignore()} on the event, to signal
+ that you don't want to start any tooltip specific modes.
+
+ Note that, if you want to show tooltips in an item view, the
+ model/view architecture provides functionality to set an item's
+ tool tip; e.g., the QTableWidgetItem::setToolTip() function.
+ However, if you want to provide custom tool tips in an item view,
+ you must intercept the help event in the
+ QAbstractItemView::viewportEvent() function and handle it yourself.
+
+ The default tool tip color and font can be customized with
+ setPalette() and setFont(). When a tooltip is currently on
+ display, isVisible() returns true and text() the currently visible
+ text.
+
+ \note Tool tips use the inactive color group of QPalette, because tool
+ tips are not active windows.
+
+ \sa QWidget::toolTip, QAction::toolTip, {Tool Tips Example}
+*/
+
+class QTipLabel : public QLabel
+{
+ Q_OBJECT
+public:
+ QTipLabel(const QString &text, QWidget *w);
+ ~QTipLabel();
+ static QTipLabel *instance;
+
+ bool eventFilter(QObject *, QEvent *);
+
+ QBasicTimer hideTimer;
+ bool fadingOut;
+
+ void reuseTip(const QString &text);
+ void hideTip();
+ void hideTipImmediately();
+ void setTipRect(QWidget *w, const QRect &r);
+ void restartHideTimer();
+ bool tipChanged(const QPoint &pos, const QString &text, QObject *o);
+ void placeTip(const QPoint &pos, QWidget *w);
+
+ static int getTipScreen(const QPoint &pos, QWidget *w);
+protected:
+ void timerEvent(QTimerEvent *e);
+ void paintEvent(QPaintEvent *e);
+ void mouseMoveEvent(QMouseEvent *e);
+ void resizeEvent(QResizeEvent *e);
+
+#ifndef QT_NO_STYLE_STYLESHEET
+public slots:
+ /** \internal
+ Cleanup the _q_stylesheet_parent propery.
+ */
+ void styleSheetParentDestroyed() {
+ setProperty("_q_stylesheet_parent", QVariant());
+ styleSheetParent = 0;
+ }
+
+private:
+ QWidget *styleSheetParent;
+#endif
+
+private:
+ QWidget *widget;
+ QRect rect;
+};
+
+QTipLabel *QTipLabel::instance = 0;
+
+QTipLabel::QTipLabel(const QString &text, QWidget *w)
+#ifndef QT_NO_STYLE_STYLESHEET
+ : QLabel(w, Qt::ToolTip), styleSheetParent(0), widget(0)
+#else
+ : QLabel(w, Qt::ToolTip), widget(0)
+#endif
+{
+ delete instance;
+ instance = this;
+ setForegroundRole(QPalette::ToolTipText);
+ setBackgroundRole(QPalette::ToolTipBase);
+ setPalette(QToolTip::palette());
+ ensurePolished();
+ setMargin(1 + style()->pixelMetric(QStyle::PM_ToolTipLabelFrameWidth, 0, this));
+ setFrameStyle(QFrame::NoFrame);
+ setAlignment(Qt::AlignLeft);
+ setIndent(1);
+ setWordWrap(Qt::mightBeRichText(text));
+ qApp->installEventFilter(this);
+ setWindowOpacity(style()->styleHint(QStyle::SH_ToolTipLabel_Opacity, 0, this) / 255.0);
+ setMouseTracking(true);
+ fadingOut = false;
+ reuseTip(text);
+}
+
+void QTipLabel::restartHideTimer()
+{
+ int time = 10000 + 40 * qMax(0, text().length()-100);
+ hideTimer.start(time, this);
+}
+
+void QTipLabel::reuseTip(const QString &text)
+{
+#ifndef QT_NO_STYLE_STYLESHEET
+ if (styleSheetParent) {
+ disconnect(styleSheetParent, SIGNAL(destroyed()),
+ QTipLabel::instance, SLOT(styleSheetParentDestroyed()));
+ styleSheetParent = 0;
+ }
+#endif
+
+ setText(text);
+ QFontMetrics fm(font());
+ QSize extra(1, 0);
+ // Make it look good with the default ToolTip font on Mac, which has a small descent.
+ if (fm.descent() == 2 && fm.ascent() >= 11)
+ ++extra.rheight();
+ resize(sizeHint() + extra);
+ restartHideTimer();
+}
+
+void QTipLabel::paintEvent(QPaintEvent *ev)
+{
+ QStylePainter p(this);
+ QStyleOptionFrame opt;
+ opt.init(this);
+ p.drawPrimitive(QStyle::PE_PanelTipLabel, opt);
+ p.end();
+
+ QLabel::paintEvent(ev);
+}
+
+void QTipLabel::resizeEvent(QResizeEvent *e)
+{
+ QStyleHintReturnMask frameMask;
+ QStyleOption option;
+ option.init(this);
+ if (style()->styleHint(QStyle::SH_ToolTip_Mask, &option, this, &frameMask))
+ setMask(frameMask.region);
+
+ QLabel::resizeEvent(e);
+}
+
+void QTipLabel::mouseMoveEvent(QMouseEvent *e)
+{
+ if (rect.isNull())
+ return;
+ QPoint pos = e->globalPos();
+ if (widget)
+ pos = widget->mapFromGlobal(pos);
+ if (!rect.contains(pos))
+ hideTip();
+ QLabel::mouseMoveEvent(e);
+}
+
+QTipLabel::~QTipLabel()
+{
+ instance = 0;
+}
+
+void QTipLabel::hideTip()
+{
+ hideTimer.start(300, this);
+}
+
+void QTipLabel::hideTipImmediately()
+{
+ close(); // to trigger QEvent::Close which stops the animation
+ deleteLater();
+}
+
+void QTipLabel::setTipRect(QWidget *w, const QRect &r)
+{
+ if (!rect.isNull() && !w)
+ qWarning("QToolTip::setTipRect: Cannot pass null widget if rect is set");
+ else{
+ widget = w;
+ rect = r;
+ }
+}
+
+void QTipLabel::timerEvent(QTimerEvent *e)
+{
+ if (e->timerId() == hideTimer.timerId()){
+ hideTimer.stop();
+#if defined(Q_WS_MAC) && !defined(QT_NO_EFFECTS)
+ if (QApplication::isEffectEnabled(Qt::UI_FadeTooltip)){
+ // Fade out tip on mac (makes it invisible).
+ // The tip will not be deleted until a new tip is shown.
+
+ // DRSWAT - Cocoa
+ macWindowFade(qt_mac_window_for(this));
+ QTipLabel::instance->fadingOut = true; // will never be false again.
+ }
+ else
+ hideTipImmediately();
+#else
+ hideTipImmediately();
+#endif
+ }
+}
+
+bool QTipLabel::eventFilter(QObject *o, QEvent *e)
+{
+ switch (e->type()) {
+#ifdef Q_WS_MAC
+ case QEvent::KeyPress:
+ case QEvent::KeyRelease: {
+ int key = static_cast<QKeyEvent *>(e)->key();
+ Qt::KeyboardModifiers mody = static_cast<QKeyEvent *>(e)->modifiers();
+ if (!(mody & Qt::KeyboardModifierMask)
+ && key != Qt::Key_Shift && key != Qt::Key_Control
+ && key != Qt::Key_Alt && key != Qt::Key_Meta)
+ hideTip();
+ break;
+ }
+#endif
+ case QEvent::Leave:
+ hideTip();
+ break;
+ case QEvent::WindowActivate:
+ case QEvent::WindowDeactivate:
+ case QEvent::MouseButtonPress:
+ case QEvent::MouseButtonRelease:
+ case QEvent::MouseButtonDblClick:
+ case QEvent::FocusIn:
+ case QEvent::FocusOut:
+ case QEvent::Wheel:
+ hideTipImmediately();
+ break;
+
+ case QEvent::MouseMove:
+ if (o == widget && !rect.isNull() && !rect.contains(static_cast<QMouseEvent*>(e)->pos()))
+ hideTip();
+ default:
+ break;
+ }
+ return false;
+}
+
+int QTipLabel::getTipScreen(const QPoint &pos, QWidget *w)
+{
+ if (QApplication::desktop()->isVirtualDesktop())
+ return QApplication::desktop()->screenNumber(pos);
+ else
+ return QApplication::desktop()->screenNumber(w);
+}
+
+void QTipLabel::placeTip(const QPoint &pos, QWidget *w)
+{
+#ifndef QT_NO_STYLE_STYLESHEET
+ if (testAttribute(Qt::WA_StyleSheet) || (w && qobject_cast<QStyleSheetStyle *>(w->style()))) {
+ //the stylesheet need to know the real parent
+ QTipLabel::instance->setProperty("_q_stylesheet_parent", qVariantFromValue(w));
+ //we force the style to be the QStyleSheetStyle, and force to clear the cache as well.
+ QTipLabel::instance->setStyleSheet(QLatin1String("/* */"));
+
+ // Set up for cleaning up this later...
+ QTipLabel::instance->styleSheetParent = w;
+ if (w) {
+ connect(w, SIGNAL(destroyed()),
+ QTipLabel::instance, SLOT(styleSheetParentDestroyed()));
+ }
+ }
+#endif //QT_NO_STYLE_STYLESHEET
+
+
+#ifdef Q_WS_MAC
+ QRect screen = QApplication::desktop()->availableGeometry(getTipScreen(pos, w));
+#else
+ QRect screen = QApplication::desktop()->screenGeometry(getTipScreen(pos, w));
+#endif
+
+ QPoint p = pos;
+ p += QPoint(2,
+#ifdef Q_WS_WIN
+ 21
+#else
+ 16
+#endif
+ );
+ if (p.x() + this->width() > screen.x() + screen.width())
+ p.rx() -= 4 + this->width();
+ if (p.y() + this->height() > screen.y() + screen.height())
+ p.ry() -= 24 + this->height();
+ if (p.y() < screen.y())
+ p.setY(screen.y());
+ if (p.x() + this->width() > screen.x() + screen.width())
+ p.setX(screen.x() + screen.width() - this->width());
+ if (p.x() < screen.x())
+ p.setX(screen.x());
+ if (p.y() + this->height() > screen.y() + screen.height())
+ p.setY(screen.y() + screen.height() - this->height());
+ this->move(p);
+}
+
+bool QTipLabel::tipChanged(const QPoint &pos, const QString &text, QObject *o)
+{
+ if (QTipLabel::instance->text() != text)
+ return true;
+
+ if (o != widget)
+ return true;
+
+ if (!rect.isNull())
+ return !rect.contains(pos);
+ else
+ return false;
+}
+
+/*!
+ Shows \a text as a tool tip, with the global position \a pos as
+ the point of interest. The tool tip will be shown with a platform
+ specific offset from this point of interest.
+
+ If you specify a non-empty rect the tip will be hidden as soon
+ as you move your cursor out of this area.
+
+ The \a rect is in the coordinates of the widget you specify with
+ \a w. If the \a rect is not empty you must specify a widget.
+ Otherwise this argument can be 0 but it is used to determine the
+ appropriate screen on multi-head systems.
+
+ If \a text is empty the tool tip is hidden. If the text is the
+ same as the currently shown tooltip, the tip will \e not move.
+ You can force moving by first hiding the tip with an empty text,
+ and then showing the new tip at the new position.
+*/
+
+void QToolTip::showText(const QPoint &pos, const QString &text, QWidget *w, const QRect &rect)
+{
+ if (QTipLabel::instance){ // a tip does already exist
+ if (text.isEmpty()){ // empty text means hide current tip
+ QTipLabel::instance->hideTip();
+ return;
+ }
+ else if (!QTipLabel::instance->fadingOut){
+ // If the tip has changed, reuse the one
+ // that is showing (removes flickering)
+ QPoint localPos = pos;
+ if (w)
+ localPos = w->mapFromGlobal(pos);
+ if (QTipLabel::instance->tipChanged(localPos, text, w)){
+ QTipLabel::instance->reuseTip(text);
+ QTipLabel::instance->setTipRect(w, rect);
+ QTipLabel::instance->placeTip(pos, w);
+ }
+ return;
+ }
+ }
+
+ if (!text.isEmpty()){ // no tip can be reused, create new tip:
+#ifndef Q_WS_WIN
+ new QTipLabel(text, w); // sets QTipLabel::instance to itself
+#else
+ // On windows, we can't use the widget as parent otherwise the window will be
+ // raised when the tooltip will be shown
+ new QTipLabel(text, QApplication::desktop()->screen(QTipLabel::getTipScreen(pos, w)));
+#endif
+ QTipLabel::instance->setTipRect(w, rect);
+ QTipLabel::instance->placeTip(pos, w);
+ QTipLabel::instance->setObjectName(QLatin1String("qtooltip_label"));
+
+
+#if !defined(QT_NO_EFFECTS) && !defined(Q_WS_MAC)
+ if (QApplication::isEffectEnabled(Qt::UI_FadeTooltip))
+ qFadeEffect(QTipLabel::instance);
+ else if (QApplication::isEffectEnabled(Qt::UI_AnimateTooltip))
+ qScrollEffect(QTipLabel::instance);
+ else
+ QTipLabel::instance->show();
+#else
+ QTipLabel::instance->show();
+#endif
+ }
+}
+
+/*!
+ \overload
+
+ This is analogous to calling QToolTip::showText(\a pos, \a text, \a w, QRect())
+*/
+
+void QToolTip::showText(const QPoint &pos, const QString &text, QWidget *w)
+{
+ QToolTip::showText(pos, text, w, QRect());
+}
+
+
+/*!
+ \fn void QToolTip::hideText()
+ \since 4.2
+
+ Hides the tool tip. This is the same as calling showText() with an
+ empty string.
+
+ \sa showText()
+*/
+
+
+/*!
+ \since 4.4
+
+ Returns true if this tooltip is currently shown.
+
+ \sa showText()
+ */
+bool QToolTip::isVisible()
+{
+ return (QTipLabel::instance != 0 && QTipLabel::instance->isVisible());
+}
+
+/*!
+ \since 4.4
+
+ Returns the tooltip text, if a tooltip is visible, or an
+ empty string if a tooltip is not visible.
+ */
+QString QToolTip::text()
+{
+ if (QTipLabel::instance)
+ return QTipLabel::instance->text();
+ return QString();
+}
+
+
+Q_GLOBAL_STATIC(QPalette, tooltip_palette)
+
+/*!
+ Returns the palette used to render tooltips.
+
+ \note Tool tips use the inactive color group of QPalette, because tool
+ tips are not active windows.
+*/
+QPalette QToolTip::palette()
+{
+ return *tooltip_palette();
+}
+
+/*!
+ \since 4.2
+
+ Returns the font used to render tooltips.
+*/
+QFont QToolTip::font()
+{
+ return QApplication::font("QTipLabel");
+}
+
+/*!
+ \since 4.2
+
+ Sets the \a palette used to render tooltips.
+
+ \note Tool tips use the inactive color group of QPalette, because tool
+ tips are not active windows.
+*/
+void QToolTip::setPalette(const QPalette &palette)
+{
+ *tooltip_palette() = palette;
+ if (QTipLabel::instance)
+ QTipLabel::instance->setPalette(palette);
+}
+
+/*!
+ \since 4.2
+
+ Sets the \a font used to render tooltips.
+*/
+void QToolTip::setFont(const QFont &font)
+{
+ QApplication::setFont(font, "QTipLabel");
+}
+
+
+/*!
+ \fn void QToolTip::add(QWidget *widget, const QString &text)
+
+ Use QWidget::setToolTip() instead.
+
+ \oldcode
+ tip->add(widget, text);
+ \newcode
+ widget->setToolTip(text);
+ \endcode
+*/
+
+/*!
+ \fn void QToolTip::add(QWidget *widget, const QRect &rect, const QString &text)
+
+ Intercept the QEvent::ToolTip events in your widget's
+ QWidget::event() function and call QToolTip::showText() with the
+ text you want to display. The \l{widgets/tooltips}{Tooltips}
+ example illustrates this technique.
+*/
+
+/*!
+ \fn void QToolTip::remove(QWidget *widget)
+
+ Use QWidget::setToolTip() instead.
+
+ \oldcode
+ tip->remove(widget);
+ \newcode
+ widget->setToolTip("");
+ \endcode
+*/
+
+QT_END_NAMESPACE
+
+#include "qtooltip.moc"
+#endif // QT_NO_TOOLTIP
diff --git a/src/gui/kernel/qtooltip.h b/src/gui/kernel/qtooltip.h
new file mode 100644
index 0000000000..464a8adf06
--- /dev/null
+++ b/src/gui/kernel/qtooltip.h
@@ -0,0 +1,84 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QTOOLTIP_H
+#define QTOOLTIP_H
+
+#include <QtGui/qwidget.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+#ifndef QT_NO_TOOLTIP
+
+class Q_GUI_EXPORT QToolTip
+{
+ QToolTip();
+public:
+ static void showText(const QPoint &pos, const QString &text, QWidget *w = 0);
+ static void showText(const QPoint &pos, const QString &text, QWidget *w, const QRect &rect);
+ static inline void hideText() { showText(QPoint(), QString()); }
+
+ static bool isVisible();
+ static QString text();
+
+ static QPalette palette();
+ static void setPalette(const QPalette &);
+ static QFont font();
+ static void setFont(const QFont &);
+#ifdef QT3_SUPPORT
+ static inline QT3_SUPPORT void add(QWidget *w, const QString &s) { w->setToolTip(s); }
+ static inline QT3_SUPPORT void add(QWidget *w, const QRect &, const QString &s)
+ { w->setToolTip(s); }
+ static inline QT3_SUPPORT void remove(QWidget *w) { w->setToolTip(QString()); }
+#endif
+};
+
+#endif // QT_NO_TOOLTIP
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QTOOLTIP_H
diff --git a/src/gui/kernel/qwhatsthis.cpp b/src/gui/kernel/qwhatsthis.cpp
new file mode 100644
index 0000000000..4024777bf3
--- /dev/null
+++ b/src/gui/kernel/qwhatsthis.cpp
@@ -0,0 +1,772 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qwhatsthis.h"
+#ifndef QT_NO_WHATSTHIS
+#include "qpointer.h"
+#include "qapplication.h"
+#include "qdesktopwidget.h"
+#include "qevent.h"
+#include "qpixmap.h"
+#include "qpainter.h"
+#include "qtimer.h"
+#include "qhash.h"
+#include "qaction.h"
+#include "qcursor.h"
+#include "qbitmap.h"
+#include "qtextdocument.h"
+#include "../text/qtextdocumentlayout_p.h"
+#include "qtoolbutton.h"
+#include "qdebug.h"
+#ifndef QT_NO_ACCESSIBILITY
+#include "qaccessible.h"
+#endif
+#if defined(Q_WS_WIN)
+#include "qt_windows.h"
+#ifndef SPI_GETDROPSHADOW
+#define SPI_GETDROPSHADOW 0x1024
+#endif
+#endif
+#if defined(Q_WS_X11)
+#include "qx11info_x11.h"
+#include <qwidget.h>
+#endif
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QWhatsThis
+ \brief The QWhatsThis class provides a simple description of any
+ widget, i.e. answering the question "What's This?".
+
+ \ingroup helpsystem
+ \mainclass
+
+ "What's This?" help is part of an application's online help
+ system, and provides users with information about the
+ functionality and usage of a particular widget. "What's This?"
+ help texts are typically longer and more detailed than \link
+ QToolTip tooltips\endlink, but generally provide less information
+ than that supplied by separate help windows.
+
+ QWhatsThis provides a single window with an explanatory text that
+ pops up when the user asks "What's This?". The default way for
+ users to ask the question is to move the focus to the relevant
+ widget and press Shift+F1. The help text appears immediately; it
+ goes away as soon as the user does something else.
+ (Note that if there is a shortcut for Shift+F1, this mechanism
+ will not work.) Some dialogs provide a "?" button that users can
+ click to enter "What's This?" mode; they then click the relevant
+ widget to pop up the "What's This?" window. It is also possible to
+ provide a a menu option or toolbar button to switch into "What's
+ This?" mode.
+
+ To add "What's This?" text to a widget or an action, you simply
+ call QWidget::setWhatsThis() or QAction::setWhatsThis().
+
+ The text can be either rich text or plain text. If you specify a
+ rich text formatted string, it will be rendered using the default
+ stylesheet, making it possible to embed images in the displayed
+ text. To be as fast as possible, the default stylesheet uses a
+ simple method to determine whether the text can be rendered as
+ plain text. See Qt::mightBeRichText() for details.
+
+ \snippet doc/src/snippets/whatsthis/whatsthis.cpp 0
+
+ An alternative way to enter "What's This?" mode is to call
+ createAction(), and add the returned QAction to either a menu or
+ a tool bar. By invoking this context help action (in the picture
+ below, the button with the arrow and question mark icon) the user
+ switches into "What's This?" mode. If they now click on a widget
+ the appropriate help text is shown. The mode is left when help is
+ given or when the user presses Esc.
+
+ \img whatsthis.png
+
+ You can enter "What's This?" mode programmatically with
+ enterWhatsThisMode(), check the mode with inWhatsThisMode(), and
+ return to normal mode with leaveWhatsThisMode().
+
+ If you want to control the "What's This?" behavior of a widget
+ manually see Qt::WA_CustomWhatsThis.
+
+ It is also possible to show different help texts for different
+ regions of a widget, by using a QHelpEvent of type
+ QEvent::WhatsThis. Intercept the help event in your widget's
+ QWidget::event() function and call QWhatsThis::showText() with the
+ text you want to display for the position specified in
+ QHelpEvent::pos(). If the text is rich text and the user clicks
+ on a link, the widget also receives a QWhatsThisClickedEvent with
+ the link's reference as QWhatsThisClickedEvent::href(). If a
+ QWhatsThisClickedEvent is handled (i.e. QWidget::event() returns
+ true), the help window remains visible. Call
+ QWhatsThis::hideText() to hide it explicitly.
+
+ \sa QToolTip
+*/
+
+extern void qDeleteInEventHandler(QObject *o);
+
+class QWhatsThat : public QWidget
+{
+ Q_OBJECT
+
+public:
+ QWhatsThat(const QString& txt, QWidget* parent, QWidget *showTextFor);
+ ~QWhatsThat() ;
+
+ static QWhatsThat *instance;
+
+protected:
+ void showEvent(QShowEvent *e);
+ void mousePressEvent(QMouseEvent*);
+ void mouseReleaseEvent(QMouseEvent*);
+ void mouseMoveEvent(QMouseEvent*);
+ void keyPressEvent(QKeyEvent*);
+ void paintEvent(QPaintEvent*);
+
+private:
+ QPointer<QWidget>widget;
+ bool pressed;
+ QString text;
+ QTextDocument* doc;
+ QString anchor;
+ QPixmap background;
+};
+
+QWhatsThat *QWhatsThat::instance = 0;
+
+// shadowWidth not const, for XP drop-shadow-fu turns it to 0
+static int shadowWidth = 6; // also used as '5' and '6' and even '8' below
+static const int vMargin = 8;
+static const int hMargin = 12;
+
+QWhatsThat::QWhatsThat(const QString& txt, QWidget* parent, QWidget *showTextFor)
+ : QWidget(parent, Qt::Popup),
+ widget(showTextFor), pressed(false), text(txt)
+{
+ delete instance;
+ instance = this;
+ setAttribute(Qt::WA_DeleteOnClose, true);
+ setAttribute(Qt::WA_NoSystemBackground, true);
+ if (parent)
+ setPalette(parent->palette());
+ setMouseTracking(true);
+ setFocusPolicy(Qt::StrongFocus);
+#ifndef QT_NO_CURSOR
+ setCursor(Qt::ArrowCursor);
+#endif
+
+ QRect r;
+ doc = 0;
+ if (Qt::mightBeRichText(text)) {
+ doc = new QTextDocument();
+ doc->setUndoRedoEnabled(false);
+ doc->setDefaultFont(QApplication::font(this));
+#ifdef QT_NO_TEXTHTMLPARSER
+ doc->setPlainText(text);
+#else
+ doc->setHtml(text);
+#endif
+ doc->setUndoRedoEnabled(false);
+ doc->adjustSize();
+ r.setTop(0);
+ r.setLeft(0);
+ r.setSize(doc->size().toSize());
+ }
+ else
+ {
+ int sw = QApplication::desktop()->width() / 3;
+ if (sw < 200)
+ sw = 200;
+ else if (sw > 300)
+ sw = 300;
+
+ r = fontMetrics().boundingRect(0, 0, sw, 1000,
+ Qt::AlignLeft + Qt::AlignTop
+ + Qt::TextWordWrap + Qt::TextExpandTabs,
+ text);
+ }
+#if defined(Q_WS_WIN)
+ if ((QSysInfo::WindowsVersion&QSysInfo::WV_NT_based) > QSysInfo::WV_2000) {
+ BOOL shadow;
+ SystemParametersInfo(SPI_GETDROPSHADOW, 0, &shadow, 0);
+ shadowWidth = shadow ? 0 : 6;
+ }
+#endif
+ resize(r.width() + 2*hMargin + shadowWidth, r.height() + 2*vMargin + shadowWidth);
+}
+
+QWhatsThat::~QWhatsThat()
+{
+ instance = 0;
+ if (doc)
+ delete doc;
+}
+
+void QWhatsThat::showEvent(QShowEvent *)
+{
+ background = QPixmap::grabWindow(QApplication::desktop()->internalWinId(),
+ x(), y(), width(), height());
+}
+
+void QWhatsThat::mousePressEvent(QMouseEvent* e)
+{
+ pressed = true;
+ if (e->button() == Qt::LeftButton && rect().contains(e->pos())) {
+ if (doc)
+ anchor = doc->documentLayout()->anchorAt(e->pos() - QPoint(hMargin, vMargin));
+ return;
+ }
+ close();
+}
+
+void QWhatsThat::mouseReleaseEvent(QMouseEvent* e)
+{
+ if (!pressed)
+ return;
+ if (widget && e->button() == Qt::LeftButton && doc && rect().contains(e->pos())) {
+ QString a = doc->documentLayout()->anchorAt(e->pos() - QPoint(hMargin, vMargin));
+ QString href;
+ if (anchor == a)
+ href = a;
+ anchor.clear();
+ if (!href.isEmpty()) {
+ QWhatsThisClickedEvent e(href);
+ if (QApplication::sendEvent(widget, &e))
+ return;
+ }
+ }
+ close();
+}
+
+void QWhatsThat::mouseMoveEvent(QMouseEvent* e)
+{
+#ifdef QT_NO_CURSOR
+ Q_UNUSED(e);
+#else
+ if (!doc)
+ return;
+ QString a = doc->documentLayout()->anchorAt(e->pos() - QPoint(hMargin, vMargin));
+ if (!a.isEmpty())
+ setCursor(Qt::PointingHandCursor);
+ else
+ setCursor(Qt::ArrowCursor);
+#endif
+}
+
+void QWhatsThat::keyPressEvent(QKeyEvent*)
+{
+ close();
+}
+
+void QWhatsThat::paintEvent(QPaintEvent*)
+{
+ bool drawShadow = true;
+#if defined(Q_WS_WIN)
+ if ((QSysInfo::WindowsVersion&QSysInfo::WV_NT_based) > QSysInfo::WV_2000) {
+ BOOL shadow;
+ SystemParametersInfo(SPI_GETDROPSHADOW, 0, &shadow, 0);
+ drawShadow = !shadow;
+ }
+#elif defined(Q_WS_MAC) || defined(Q_WS_QWS)
+ drawShadow = false; // never draw it on OS X or QWS, as we get it for free
+#endif
+
+ QRect r = rect();
+ r.adjust(0, 0, -1, -1);
+ if (drawShadow)
+ r.adjust(0, 0, -shadowWidth, -shadowWidth);
+ QPainter p(this);
+ p.drawPixmap(0, 0, background);
+ p.setPen(QPen(palette().toolTipText(), 0));
+ p.setBrush(palette().toolTipBase());
+ p.drawRect(r);
+ int w = r.width();
+ int h = r.height();
+ p.setPen(palette().brush(QPalette::Dark).color());
+ p.drawRect(1, 1, w-2, h-2);
+ if (drawShadow) {
+ p.setPen(palette().shadow().color());
+ 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);
+ }
+ r.adjust(0, 0, 1, 1);
+ p.setPen(palette().toolTipText().color());
+ r.adjust(hMargin, vMargin, -hMargin, -vMargin);
+
+ if (doc) {
+ p.translate(r.x(), r.y());
+ QRect rect = r;
+ rect.translate(-r.x(), -r.y());
+ p.setClipRect(rect);
+ QAbstractTextDocumentLayout::PaintContext context;
+ doc->documentLayout()->draw(&p, context);
+ }
+ else
+ {
+ p.drawText(r, Qt::AlignLeft + Qt::AlignTop + Qt::TextWordWrap + Qt::TextExpandTabs, text);
+ }
+}
+
+static const char * const button_image[] = {
+"16 16 3 1",
+" c None",
+"o c #000000",
+"a c #000080",
+"o aaaaa ",
+"oo aaa aaa ",
+"ooo aaa aaa",
+"oooo aa aa",
+"ooooo aa aa",
+"oooooo a aaa",
+"ooooooo aaa ",
+"oooooooo aaa ",
+"ooooooooo aaa ",
+"ooooo aaa ",
+"oo ooo ",
+"o ooo aaa ",
+" ooo aaa ",
+" ooo ",
+" ooo ",
+" ooo "};
+
+class QWhatsThisPrivate : public QObject
+{
+ public:
+ QWhatsThisPrivate();
+ ~QWhatsThisPrivate();
+ static QWhatsThisPrivate *instance;
+ bool eventFilter(QObject *, QEvent *);
+ QPointer<QAction> action;
+#ifdef QT3_SUPPORT
+ QPointer<QToolButton> button;
+#endif
+ static void say(QWidget *, const QString &, int x = 0, int y = 0);
+ static void notifyToplevels(QEvent *e);
+ bool leaveOnMouseRelease;
+};
+
+void QWhatsThisPrivate::notifyToplevels(QEvent *e)
+{
+ QWidgetList toplevels = QApplication::topLevelWidgets();
+ for (int i = 0; i < toplevels.count(); ++i) {
+ register QWidget *w = toplevels.at(i);
+ QApplication::sendEvent(w, e);
+ }
+}
+
+QWhatsThisPrivate *QWhatsThisPrivate::instance = 0;
+
+QWhatsThisPrivate::QWhatsThisPrivate()
+ : leaveOnMouseRelease(false)
+{
+ instance = this;
+ qApp->installEventFilter(this);
+
+ QPoint pos = QCursor::pos();
+ if (QWidget *w = QApplication::widgetAt(pos)) {
+ QHelpEvent e(QEvent::QueryWhatsThis, w->mapFromGlobal(pos), pos);
+ bool sentEvent = QApplication::sendEvent(w, &e);
+#ifdef QT_NO_CURSOR
+ Q_UNUSED(sentEvent);
+#else
+ QApplication::setOverrideCursor((!sentEvent || !e.isAccepted())?
+ Qt::ForbiddenCursor:Qt::WhatsThisCursor);
+ } else {
+ QApplication::setOverrideCursor(Qt::WhatsThisCursor);
+#endif
+ }
+#ifndef QT_NO_ACCESSIBILITY
+ QAccessible::updateAccessibility(this, 0, QAccessible::ContextHelpStart);
+#endif
+}
+
+QWhatsThisPrivate::~QWhatsThisPrivate()
+{
+ if (action)
+ action->setChecked(false);
+#ifdef QT3_SUPPORT
+ if (button)
+ button->setChecked(false);
+#endif
+#ifndef QT_NO_CURSOR
+ QApplication::restoreOverrideCursor();
+#endif
+#ifndef QT_NO_ACCESSIBILITY
+ QAccessible::updateAccessibility(this, 0, QAccessible::ContextHelpEnd);
+#endif
+ instance = 0;
+}
+
+bool QWhatsThisPrivate::eventFilter(QObject *o, QEvent *e)
+{
+ if (!o->isWidgetType())
+ return false;
+ QWidget * w = static_cast<QWidget *>(o);
+ bool customWhatsThis = w->testAttribute(Qt::WA_CustomWhatsThis);
+ switch (e->type()) {
+ case QEvent::MouseButtonPress:
+ {
+ QMouseEvent *me = static_cast<QMouseEvent*>(e);
+ if (me->button() == Qt::RightButton || customWhatsThis)
+ return false;
+ QHelpEvent e(QEvent::WhatsThis, me->pos(), me->globalPos());
+ if (!QApplication::sendEvent(w, &e) || !e.isAccepted())
+ leaveOnMouseRelease = true;
+
+ } break;
+
+ case QEvent::MouseMove:
+ {
+ QMouseEvent *me = static_cast<QMouseEvent*>(e);
+ QHelpEvent e(QEvent::QueryWhatsThis, me->pos(), me->globalPos());
+ bool sentEvent = QApplication::sendEvent(w, &e);
+#ifdef QT_NO_CURSOR
+ Q_UNUSED(sentEvent);
+#else
+ QApplication::changeOverrideCursor((!sentEvent || !e.isAccepted())?
+ Qt::ForbiddenCursor:Qt::WhatsThisCursor);
+#endif
+ }
+ // fall through
+ case QEvent::MouseButtonRelease:
+ case QEvent::MouseButtonDblClick:
+ if (leaveOnMouseRelease && e->type() == QEvent::MouseButtonRelease)
+ QWhatsThis::leaveWhatsThisMode();
+ if (static_cast<QMouseEvent*>(e)->button() == Qt::RightButton || customWhatsThis)
+ return false; // ignore RMB release
+ break;
+ case QEvent::KeyPress:
+ {
+ QKeyEvent* kev = (QKeyEvent*)e;
+
+ if (kev->key() == Qt::Key_Escape) {
+ QWhatsThis::leaveWhatsThisMode();
+ return true;
+ } else if (customWhatsThis) {
+ return false;
+ } else if (kev->key() == Qt::Key_Menu ||
+ (kev->key() == Qt::Key_F10 &&
+ kev->modifiers() == Qt::ShiftModifier)) {
+ // we don't react to these keys, they are used for context menus
+ return false;
+ } else if (kev->key() != Qt::Key_Shift && kev->key() != Qt::Key_Alt // not a modifier key
+ && kev->key() != Qt::Key_Control && kev->key() != Qt::Key_Meta) {
+ QWhatsThis::leaveWhatsThisMode();
+ }
+ } break;
+ default:
+ return false;
+ }
+ return true;
+}
+
+class QWhatsThisAction: public QAction
+{
+ Q_OBJECT
+
+public:
+ explicit QWhatsThisAction(QObject* parent = 0);
+
+private slots:
+ void actionTriggered();
+};
+
+QWhatsThisAction::QWhatsThisAction(QObject *parent) : QAction(tr("What's This?"), parent)
+{
+#ifndef QT_NO_IMAGEFORMAT_XPM
+ QPixmap p((const char**)button_image);
+ setIcon(p);
+#endif
+ setCheckable(true);
+ connect(this, SIGNAL(triggered()), this, SLOT(actionTriggered()));
+#ifndef QT_NO_SHORTCUT
+ setShortcut(Qt::ShiftModifier + Qt::Key_F1);
+#endif
+}
+
+void QWhatsThisAction::actionTriggered()
+{
+ if (isChecked()) {
+ QWhatsThis::enterWhatsThisMode();
+ QWhatsThisPrivate::instance->action = this;
+ }
+}
+
+QWhatsThis::QWhatsThis()
+{
+}
+
+#ifdef QT3_SUPPORT
+/*!
+ \obsolete
+
+ Sets the What's This text \a s for the widget \a w.
+
+ Use QWidget::setWhatsThis() or QAction::setWhatsThis() instead.
+*/
+void QWhatsThis::add(QWidget *w, const QString &s)
+{
+ w->setWhatsThis(s);
+}
+
+/*!
+ \obsolete
+
+ Remove's the What's This text for the widget \a w.
+
+ Use QWidget::setWhatsThis() or QAction::setWhatsThis() instead.
+*/
+void QWhatsThis::remove(QWidget *w)
+{
+ w->setWhatsThis(QString());
+}
+
+class QWhatsThisButton : public QToolButton
+{
+ Q_OBJECT
+public:
+ QWhatsThisButton(QWidget *p) : QToolButton(p) {
+ setCheckable(true);
+ QPixmap pix( const_cast<const char**>(button_image) );
+ setIcon( pix );
+ QObject::connect(this, SIGNAL(toggled(bool)), this, SLOT(whatToggled(bool)));
+ setAutoRaise(true);
+ setFocusPolicy(Qt::NoFocus);
+ }
+
+public slots:
+ void whatToggled(bool b) {
+ if (b) {
+ QWhatsThis::enterWhatsThisMode();
+ QWhatsThisPrivate::instance->button = this;
+ }
+ }
+};
+
+/*!
+ Returns a new "What's This?" QToolButton with the given \a
+ parent. To do this now, create your own QToolButton and a
+ QWhatsThis object and call the QWhatsThis object's showText()
+ function when the QToolButton is invoked.
+
+ Use createAction() instead.
+*/
+QToolButton * QWhatsThis::whatsThisButton(QWidget * parent)
+{
+ return new QWhatsThisButton(parent);
+}
+#endif
+
+/*!
+ This function switches the user interface into "What's This?"
+ mode. The user interface can be switched back into normal mode by
+ the user (e.g. by them clicking or pressing Esc), or
+ programmatically by calling leaveWhatsThisMode().
+
+ When entering "What's This?" mode, a QEvent of type
+ Qt::EnterWhatsThisMode is sent to all toplevel widgets.
+
+ \sa inWhatsThisMode() leaveWhatsThisMode()
+*/
+void QWhatsThis::enterWhatsThisMode()
+{
+ if (QWhatsThisPrivate::instance)
+ return;
+ (void) new QWhatsThisPrivate;
+ QEvent e(QEvent::EnterWhatsThisMode);
+ QWhatsThisPrivate::notifyToplevels(&e);
+ }
+
+/*!
+ Returns true if the user interface is in "What's This?" mode;
+ otherwise returns false.
+
+ \sa enterWhatsThisMode()
+*/
+bool QWhatsThis::inWhatsThisMode()
+{
+ return (QWhatsThisPrivate::instance != 0);
+}
+
+/*!
+ If the user interface is in "What's This?" mode, this function
+ switches back to normal mode; otherwise it does nothing.
+
+ When leaving "What's This?" mode, a QEvent of type
+ Qt::LeaveWhatsThisMode is sent to all toplevel widgets.
+
+ \sa enterWhatsThisMode() inWhatsThisMode()
+*/
+void QWhatsThis::leaveWhatsThisMode()
+{
+ delete QWhatsThisPrivate::instance;
+ QEvent e(QEvent::LeaveWhatsThisMode);
+ QWhatsThisPrivate::notifyToplevels(&e);
+}
+
+void QWhatsThisPrivate::say(QWidget * widget, const QString &text, int x, int y)
+{
+ if (text.size() == 0)
+ return;
+ // make a fresh widget, and set it up
+ QWhatsThat *whatsThat = new QWhatsThat(
+ text,
+#if defined(Q_WS_X11) && !defined(QT_NO_CURSOR)
+ QApplication::desktop()->screen(widget ? widget->x11Info().screen() : QCursor::x11Screen()),
+#else
+ 0,
+#endif
+ widget
+ );
+
+
+ // okay, now to find a suitable location
+
+ int scr = (widget ?
+ QApplication::desktop()->screenNumber(widget) :
+#if defined(Q_WS_X11) && !defined(QT_NO_CURSOR)
+ QCursor::x11Screen()
+#else
+ QApplication::desktop()->screenNumber(QPoint(x,y))
+#endif // Q_WS_X11
+ );
+ QRect screen = QApplication::desktop()->screenGeometry(scr);
+
+ int w = whatsThat->width();
+ int h = whatsThat->height();
+ int sx = screen.x();
+ int sy = screen.y();
+
+ // first try locating the widget immediately above/below,
+ // with nice alignment if possible.
+ QPoint pos;
+ if (widget)
+ pos = widget->mapToGlobal(QPoint(0,0));
+
+ if (widget && w > widget->width() + 16)
+ x = pos.x() + widget->width()/2 - w/2;
+ else
+ x = x - w/2;
+
+ // squeeze it in if that would result in part of what's this
+ // being only partially visible
+ if (x + w + shadowWidth > sx+screen.width())
+ x = (widget? (qMin(screen.width(),
+ pos.x() + widget->width())
+ ) : screen.width())
+ - w;
+
+ if (x < sx)
+ x = sx;
+
+ if (widget && h > widget->height() + 16) {
+ y = pos.y() + widget->height() + 2; // below, two pixels spacing
+ // what's this is above or below, wherever there's most space
+ if (y + h + 10 > sy+screen.height())
+ y = pos.y() + 2 - shadowWidth - h; // above, overlap
+ }
+ y = y + 2;
+
+ // squeeze it in if that would result in part of what's this
+ // being only partially visible
+ if (y + h + shadowWidth > sy+screen.height())
+ y = (widget ? (qMin(screen.height(),
+ pos.y() + widget->height())
+ ) : screen.height())
+ - h;
+ if (y < sy)
+ y = sy;
+
+ whatsThat->move(x, y);
+ whatsThat->show();
+ whatsThat->grabKeyboard();
+}
+
+/*!
+ Shows \a text as a "What's This?" window, at global position \a
+ pos. The optional widget argument, \a w, is used to determine the
+ appropriate screen on multi-head systems.
+
+ \sa hideText()
+*/
+void QWhatsThis::showText(const QPoint &pos, const QString &text, QWidget *w)
+{
+ leaveWhatsThisMode();
+ QWhatsThisPrivate::say(w, text, pos.x(), pos.y());
+}
+
+/*!
+ If a "What's This?" window is showing, this destroys it.
+
+ \sa showText()
+*/
+void QWhatsThis::hideText()
+{
+ qDeleteInEventHandler(QWhatsThat::instance);
+}
+
+/*!
+ Returns a ready-made QAction, used to invoke "What's This?" context
+ help, with the given \a parent.
+
+ The returned QAction provides a convenient way to let users enter
+ "What's This?" mode.
+*/
+QAction *QWhatsThis::createAction(QObject *parent)
+{
+ return new QWhatsThisAction(parent);
+}
+
+QT_END_NAMESPACE
+
+#include "qwhatsthis.moc"
+
+#endif // QT_NO_WHATSTHIS
diff --git a/src/gui/kernel/qwhatsthis.h b/src/gui/kernel/qwhatsthis.h
new file mode 100644
index 0000000000..5011c54ae0
--- /dev/null
+++ b/src/gui/kernel/qwhatsthis.h
@@ -0,0 +1,88 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QWHATSTHIS_H
+#define QWHATSTHIS_H
+
+#include <QtCore/qobject.h>
+#include <QtGui/qcursor.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+#ifndef QT_NO_WHATSTHIS
+
+class QAction;
+#ifdef QT3_SUPPORT
+class QToolButton;
+#endif
+
+class Q_GUI_EXPORT QWhatsThis
+{
+ QWhatsThis();
+
+public:
+ static void enterWhatsThisMode();
+ static bool inWhatsThisMode();
+ static void leaveWhatsThisMode();
+
+ static void showText(const QPoint &pos, const QString &text, QWidget *w = 0);
+ static void hideText();
+
+ static QAction *createAction(QObject *parent = 0);
+
+#ifdef QT3_SUPPORT
+ static QT3_SUPPORT void add(QWidget *w, const QString &s);
+ static QT3_SUPPORT void remove(QWidget *);
+ static QT3_SUPPORT QToolButton *whatsThisButton(QWidget *parent);
+#endif
+};
+
+#endif // QT_NO_WHATSTHIS
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QWHATSTHIS_H
diff --git a/src/gui/kernel/qwidget.cpp b/src/gui/kernel/qwidget.cpp
new file mode 100644
index 0000000000..06810e0947
--- /dev/null
+++ b/src/gui/kernel/qwidget.cpp
@@ -0,0 +1,11398 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qapplication.h"
+#include "qapplication_p.h"
+#include "qbrush.h"
+#include "qcursor.h"
+#include "qdesktopwidget.h"
+#include "qevent.h"
+#include "qhash.h"
+#include "qlayout.h"
+#include "qmenu.h"
+#include "qmetaobject.h"
+#include "qpixmap.h"
+#include "qpointer.h"
+#include "qstack.h"
+#include "qstyle.h"
+#include "qstylefactory.h"
+#include "qvariant.h"
+#include "qwidget.h"
+#include "qstyleoption.h"
+#ifndef QT_NO_ACCESSIBILITY
+# include "qaccessible.h"
+#endif
+#if defined(Q_WS_WIN)
+# include "qt_windows.h"
+#endif
+#ifdef Q_WS_MAC
+# include "qt_mac_p.h"
+# include "qt_cocoa_helpers_mac_p.h"
+#endif
+#if defined(Q_WS_QWS)
+# include "qwsdisplay_qws.h"
+# include "qwsmanager_qws.h"
+# include "qpaintengine.h" // for PorterDuff
+# include "private/qwindowsurface_qws_p.h"
+#endif
+#include "qpainter.h"
+#include "qtooltip.h"
+#include "qwhatsthis.h"
+#include "qdebug.h"
+#include "private/qstylesheetstyle_p.h"
+#include "private/qstyle_p.h"
+#include "private/qinputcontext_p.h"
+#include "qfileinfo.h"
+
+#if defined (Q_WS_WIN)
+# include <private/qwininputcontext_p.h>
+#endif
+
+#if defined(Q_WS_X11)
+# include <private/qpaintengine_x11_p.h>
+# include "qx11info_x11.h"
+#endif
+
+#include <private/qwindowsurface_p.h>
+#include <private/qbackingstore_p.h>
+#ifdef Q_WS_MAC
+# include <private/qpaintengine_mac_p.h>
+#endif
+#include <private/qpaintengine_raster_p.h>
+
+#include "qwidget_p.h"
+#include "qaction_p.h"
+#include "qlayout_p.h"
+#include "QtGui/qgraphicsproxywidget.h"
+#include "QtGui/qgraphicsscene.h"
+#include "private/qgraphicsproxywidget_p.h"
+#include "QtGui/qabstractscrollarea.h"
+#include "private/qabstractscrollarea_p.h"
+
+#include "private/qgraphicssystem_p.h"
+
+// widget/widget data creation count
+//#define QWIDGET_EXTRA_DEBUG
+//#define ALIEN_DEBUG
+
+QT_BEGIN_NAMESPACE
+
+#if !defined(Q_WS_QWS)
+static bool qt_enable_backingstore = true;
+#endif
+#ifdef Q_WS_X11
+// for compatibility with Qt 4.0
+Q_GUI_EXPORT void qt_x11_set_global_double_buffer(bool enable)
+{
+ qt_enable_backingstore = enable;
+}
+#endif
+
+static inline bool qRectIntersects(const QRect &r1, const QRect &r2)
+{
+ return (qMax(r1.left(), r2.left()) <= qMin(r1.right(), r2.right()) &&
+ qMax(r1.top(), r2.top()) <= qMin(r1.bottom(), r2.bottom()));
+}
+
+static inline bool hasBackingStoreSupport()
+{
+#ifdef Q_WS_MAC
+ return QApplicationPrivate::graphicsSystem() != 0;
+#else
+ return true;
+#endif
+}
+
+/*!
+ \internal
+
+ Returns true if \a p or any of its parents enable the
+ Qt::BypassGraphicsProxyWidget window flag. Used in QWidget::show() and
+ QWidget::setParent() to determine whether it's necessary to embed the
+ widget into a QGraphicsProxyWidget or not.
+*/
+static inline bool bypassGraphicsProxyWidget(QWidget *p)
+{
+ while (p) {
+ if (p->windowFlags() & Qt::BypassGraphicsProxyWidget)
+ return true;
+ p = p->parentWidget();
+ }
+ return false;
+}
+
+#ifdef Q_WS_MAC
+# define QT_NO_PAINT_DEBUG
+#endif
+
+extern bool qt_sendSpontaneousEvent(QObject*, QEvent*); // qapplication.cpp
+
+QWidgetPrivate::QWidgetPrivate(int version) :
+ QObjectPrivate(version), extra(0), focus_child(0)
+ ,layout(0), widgetItem(0)
+ ,leftmargin(0), topmargin(0), rightmargin(0), bottommargin(0)
+ ,leftLayoutItemMargin(0), topLayoutItemMargin(0), rightLayoutItemMargin(0)
+ ,bottomLayoutItemMargin(0)
+ ,fg_role(QPalette::NoRole)
+ ,bg_role(QPalette::NoRole)
+ ,hd(0)
+ ,dirty(0)
+ ,needsFlush(0)
+ ,dirtyOpaqueChildren(1)
+ ,isOpaque(0)
+ ,inDirtyList(0)
+ ,isScrolled(0)
+ ,isMoved(0)
+#ifdef Q_WS_WIN
+ ,noPaintOnScreen(0)
+#endif
+ ,inheritedFontResolveMask(0)
+ ,inheritedPaletteResolveMask(0)
+#if defined(Q_WS_X11)
+ ,picture(0)
+#endif
+#ifdef Q_WS_MAC
+ ,needWindowChange(0)
+ ,isGLWidget(0)
+#endif
+ ,polished(0)
+
+ , size_policy(QSizePolicy::Preferred, QSizePolicy::Preferred)
+ , redirectDev(0)
+{
+ if (!qApp) {
+ qFatal("QWidget: Must construct a QApplication before a QPaintDevice");
+ return;
+ }
+
+ if (version != QObjectPrivateVersion)
+ qFatal("Cannot mix incompatible Qt libraries");
+
+ isWidget = true;
+ memset(high_attributes, 0, sizeof(high_attributes));
+#ifdef QWIDGET_EXTRA_DEBUG
+ static int count = 0;
+ qDebug() << "widgets" << ++count;
+#endif
+}
+
+
+QWidgetPrivate::~QWidgetPrivate()
+{
+ if (widgetItem)
+ widgetItem->wid = 0;
+
+ if (extra)
+ deleteExtra();
+}
+
+QWindowSurface *QWidgetPrivate::createDefaultWindowSurface()
+{
+ Q_Q(QWidget);
+ if (QApplicationPrivate::graphicsSystem())
+ return QApplicationPrivate::graphicsSystem()->createWindowSurface(q);
+ return createDefaultWindowSurface_sys();
+}
+
+/*!
+ \internal
+ This is an internal function, you should never call this.
+
+ This function is called to focus associated input context. The
+ code intends to eliminate duplicate focus for the context even if
+ the context is shared between widgets
+
+ \sa QInputContext::setFocus()
+ */
+void QWidgetPrivate::focusInputContext()
+{
+#ifndef QT_NO_IM
+ Q_Q(QWidget);
+ QInputContext *qic = q->inputContext();
+ if (qic) {
+ if(qic->focusWidget() != q)
+ qic->setFocusWidget(q);
+ }
+#endif // QT_NO_IM
+}
+
+/*!
+ \internal
+*/
+void QWidgetPrivate::scrollChildren(int dx, int dy)
+{
+ Q_Q(QWidget);
+ if (q->children().size() > 0) { // scroll children
+ QPoint pd(dx, dy);
+ QObjectList childObjects = q->children();
+ for (int i = 0; i < childObjects.size(); ++i) { // move all children
+ QWidget *w = qobject_cast<QWidget*>(childObjects.at(i));
+ if (w && !w->isWindow()) {
+ QPoint oldp = w->pos();
+ QRect r(w->pos() + pd, w->size());
+ w->data->crect = r;
+#ifndef Q_WS_QWS
+ if (w->testAttribute(Qt::WA_WState_Created))
+ w->d_func()->setWSGeometry();
+#endif
+ w->d_func()->setDirtyOpaqueRegion();
+ QMoveEvent e(r.topLeft(), oldp);
+ QApplication::sendEvent(w, &e);
+ }
+ }
+ }
+}
+
+QInputContext *QWidgetPrivate::inputContext() const
+{
+#ifndef QT_NO_IM
+ if (ic)
+ return ic;
+#endif
+ return qApp->inputContext();
+}
+
+/*!
+ This function returns the QInputContext for this widget. By
+ default the input context is inherited from the widgets
+ parent. For toplevels it is inherited from QApplication.
+
+ You can override this and set a special input context for this
+ widget by using the setInputContext() method.
+
+ \sa setInputContext()
+*/
+QInputContext *QWidget::inputContext()
+{
+ Q_D(QWidget);
+ if (!testAttribute(Qt::WA_InputMethodEnabled))
+ return 0;
+
+ return d->inputContext();
+}
+
+/*!
+ This function sets the input context \a context
+ on this widget.
+
+ \sa inputContext()
+*/
+void QWidget::setInputContext(QInputContext *context)
+{
+ Q_D(QWidget);
+ if (!testAttribute(Qt::WA_InputMethodEnabled))
+ return;
+#ifndef QT_NO_IM
+ if (d->ic)
+ delete d->ic;
+ d->ic = context;
+#endif
+}
+
+
+/*!
+ This function can be called on the widget that currently has focus
+ to reset the input method operating on it.
+
+ \sa QInputContext, QInputContext::reset()
+*/
+void QWidget::resetInputContext()
+{
+ if (!hasFocus())
+ return;
+#ifndef QT_NO_IM
+ if (!d_func()->ic)
+ return;
+ QInputContext *qic = this->inputContext();
+ if( qic )
+ qic->reset();
+#endif // QT_NO_IM
+}
+
+#ifdef QT_KEYPAD_NAVIGATION
+QPointer<QWidget> QWidgetPrivate::editingWidget;
+
+/*!
+ Returns true if this widget currently has edit focus; otherwise false.
+
+ This feature is only available in Qt for Embedded Linux.
+
+ \sa setEditFocus(), QApplication::keypadNavigationEnabled()
+*/
+bool QWidget::hasEditFocus() const
+{
+ const QWidget* w = this;
+ while (w->d_func()->extra && w->d_func()->extra->focus_proxy)
+ w = w->d_func()->extra->focus_proxy;
+ return QWidgetPrivate::editingWidget == w;
+}
+
+/*!
+ \fn void QWidget::setEditFocus(bool enable)
+
+ If \a enable is true, make this widget have edit focus, in which
+ case Qt::Key_Up and Qt::Key_Down will be delivered to the widget
+ normally; otherwise, Qt::Key_Up and Qt::Key_Down are used to
+ change focus.
+
+ This feature is only available in Qt for Embedded Linux.
+
+ \sa hasEditFocus(), QApplication::keypadNavigationEnabled()
+*/
+void QWidget::setEditFocus(bool on)
+{
+ QWidget *f = this;
+ while (f->d_func()->extra && f->d_func()->extra->focus_proxy)
+ f = f->d_func()->extra->focus_proxy;
+
+ if (QWidgetPrivate::editingWidget && QWidgetPrivate::editingWidget != f)
+ QWidgetPrivate::editingWidget->setEditFocus(false);
+
+ if (on && !f->hasFocus())
+ f->setFocus();
+
+ if ((!on && !QWidgetPrivate::editingWidget)
+ || (on && QWidgetPrivate::editingWidget == f)) {
+ return;
+ }
+
+ if (!on && QWidgetPrivate::editingWidget == f) {
+ QWidgetPrivate::editingWidget = 0;
+ QEvent event(QEvent::LeaveEditFocus);
+ QApplication::sendEvent(f, &event);
+ QApplication::sendEvent(f->style(), &event);
+ } else if (on) {
+ QWidgetPrivate::editingWidget = f;
+ QEvent event(QEvent::EnterEditFocus);
+ QApplication::sendEvent(f, &event);
+ QApplication::sendEvent(f->style(), &event);
+ }
+}
+#endif
+
+/*!
+ \property QWidget::autoFillBackground
+ \brief whether the widget background is filled automatically
+ \since 4.1
+
+ If enabled, this property will cause Qt to fill the background of the
+ widget before invoking the paint event. The color used is defined by the
+ QPalette::Window color role from the widget's \l{QPalette}{palette}.
+
+ In addition, Windows are always filled with QPalette::Window, unless the
+ WA_OpaquePaintEvent or WA_NoSystemBackground attributes are set.
+
+ This property cannot be turned off (i.e., set to false) if a widget's
+ parent has a static gradient for its background.
+
+ \warning Use this property with caution in conjunction with
+ \l{Qt Style Sheets}. When a widget has a style sheet with a valid
+ background or a border-image, this property is automatically disabled.
+
+ By default, this property is false.
+
+ \sa Qt::WA_OpaquePaintEvent, Qt::WA_NoSystemBackground,
+ {QWidget#Transparency and Double Buffering}{Transparency and Double Buffering}
+*/
+bool QWidget::autoFillBackground() const
+{
+ Q_D(const QWidget);
+ return d->extra && d->extra->autoFillBackground;
+}
+
+void QWidget::setAutoFillBackground(bool enabled)
+{
+ Q_D(QWidget);
+ if (!d->extra)
+ d->createExtra();
+ if (d->extra->autoFillBackground == enabled)
+ return;
+
+ d->extra->autoFillBackground = enabled;
+ d->updateIsOpaque();
+ update();
+ d->updateIsOpaque();
+}
+
+/*!
+ \class QWidget
+ \brief The QWidget class is the base class of all user interface objects.
+
+ \ingroup basicwidgets
+ \mainclass
+
+ The widget is the atom of the user interface: it receives mouse, keyboard
+ and other events from the window system, and paints a representation of
+ itself on the screen. Every widget is rectangular, and they are sorted in a
+ Z-order. A widget is clipped by its parent and by the widgets in front of
+ it.
+
+ A widget that is not embedded in a parent widget is called a window.
+ Usually, windows have a frame and a title bar, although it is also possible
+ to create windows without such decoration using suitable
+ \l{Qt::WindowFlags}{window flags}). In Qt, QMainWindow and the various
+ subclasses of QDialog are the most common window types.
+
+ Every widget's constructor accepts one or two standard arguments:
+
+ \list 1
+ \i \c{QWidget *parent = 0} is the parent of the new widget. If it is 0
+ (the default), the new widget will be a window. If not, it will be
+ a child of \e parent, and be constrained by \e parent's geometry
+ (unless you specify Qt::Window as window flag).
+ \i \c{Qt::WindowFlags f = 0} (where available) sets the window flags;
+ the default is suitable for almost all widgets, but to get, for
+ example, a window without a window system frame, you must use
+ special flags.
+ \endlist
+
+ QWidget has many member functions, but some of them have little direct
+ functionality; for example, QWidget has a font property, but never uses
+ this itself. There are many subclasses which provide real functionality,
+ such as QLabel, QPushButton, QListWidget, and QTabWidget.
+
+
+ \section1 Top-Level and Child Widgets
+
+ A widget without a parent widget is always an independent window (top-level
+ widget). For these widgets, setWindowTitle() and setWindowIcon() set the
+ title bar and icon respectively.
+
+ Non-window widgets are child widgets, displayed within their parent
+ widgets. Most widgets in Qt are mainly useful as child widgets. For
+ example, it is possible to display a button as a top-level window, but most
+ people prefer to put their buttons inside other widgets, such as QDialog.
+
+ \image parent-child-widgets.png A parent widget containing various child widgets.
+
+ The diagram above shows a QGroupBox widget being used to hold various child
+ widgets in a layout provided by QGridLayout. The QLabel child widgets have
+ been outlined to indicate their full sizes.
+
+ If you want to use a QWidget to hold child widgets you will usually want to
+ add a layout to the parent QWidget. See \l{Layout Classes} for more
+ information.
+
+
+ \section1 Composite Widgets
+
+ When a widget is used as a container to group a number of child widgets, it
+ is known as a composite widget. These can be created by constructing a
+ widget with the required visual properties - a QFrame, for example - and
+ adding child widgets to it, usually managed by a layout. The above diagram
+ shows such a composite widget that was created using \l{Qt Designer}.
+
+ Composite widgets can also be created by subclassing a standard widget,
+ such as QWidget or QFrame, and adding the necessary layout and child
+ widgets in the constructor of the subclass. Many of the \l{Qt Examples}
+ {examples provided with Qt} use this approach, and it is also covered in
+ the Qt \l{Tutorials}.
+
+
+ \section1 Custom Widgets and Painting
+
+ Since QWidget is a subclass of QPaintDevice, subclasses can be used to
+ display custom content that is composed using a series of painting
+ operations with an instance of the QPainter class. This approach contrasts
+ with the canvas-style approach used by the \l{Graphics View}
+ {Graphics View Framework} where items are added to a scene by the
+ application and are rendered by the framework itself.
+
+ Each widget performs all painting operations from within its paintEvent()
+ function. This is called whenever the widget needs to be redrawn, either
+ as a result of some external change or when requested by the application.
+
+ The \l{widgets/analogclock}{Analog Clock example} shows how a simple widget
+ can handle paint events.
+
+
+ \section1 Size Hints and Size Policies
+
+ When implementing a new widget, it is almost always useful to reimplement
+ sizeHint() to provide a reasonable default size for the widget and to set
+ the correct size policy with setSizePolicy().
+
+ By default, composite widgets which do not provide a size hint will be
+ sized according to the space requirements of their child widgets.
+
+ The size policy lets you supply good default behavior for the layout
+ management system, so that other widgets can contain and manage yours
+ easily. The default size policy indicates that the size hint represents
+ the preferred size of the widget, and this is often good enough for many
+ widgets.
+
+ \note The size of top-level widgets are constrained to 2/3 of the desktop's
+ height and width. You can resize() the widget manually if these bounds are
+ inadequate.
+
+
+ \section1 Events
+
+ Widgets respond to events that are typically caused by user actions. Qt
+ delivers events to widgets by calling specific event handler functions with
+ instances of QEvent subclasses containing information about each event.
+
+ If your widget only contains child widgets, you probably do not need to
+ implement any event handlers. If you want to detect a mouse click in a
+ child widget call the child's underMouse() function inside the widget's
+ mousePressEvent().
+
+ The \l{widgets/scribble}{Scribble example} implements a wider set of
+ events to handle mouse movement, button presses, and window resizing.
+
+ You will need to supply the behavior and content for your own widgets, but
+ here is a brief overview of the events that are relevant to QWidget,
+ starting with the most common ones:
+
+ \list
+ \i paintEvent() is called whenever the widget needs to be repainted.
+ Every widget displaying custom content must implement it. Painting
+ using a QPainter can only take place in a paintEvent() or a
+ function called by a paintEvent().
+ \i resizeEvent() is called when the widget has been resized.
+ \i mousePressEvent() is called when a mouse button is pressed while
+ the mouse cursor is inside the widget, or when the widget has
+ grabbed the mouse using grabMouse(). Pressing the mouse without
+ releasing it is effectively the same as calling grabMouse().
+ \i mouseReleaseEvent() is called when a mouse button is released. A
+ widget receives mouse release events when it has received the
+ corresponding mouse press event. This means that if the user
+ presses the mouse inside \e your widget, then drags the mouse
+ somewhere else before releasing the mouse button, \e your widget
+ receives the release event. There is one exception: if a popup menu
+ appears while the mouse button is held down, this popup immediately
+ steals the mouse events.
+ \i mouseDoubleClickEvent() is called when the user double-clicks in
+ the widget. If the user double-clicks, the widget receives a mouse
+ press event, a mouse release event and finally this event instead
+ of a second mouse press event. (Some mouse move events may also be
+ received if the mouse is not held steady during this operation.) It
+ is \e{not possible} to distinguish a click from a double-click
+ until the second click arrives. (This is one reason why most GUI
+ books recommend that double-clicks be an extension of
+ single-clicks, rather than trigger a different action.)
+ \endlist
+
+ Widgets that accept keyboard input need to reimplement a few more event
+ handlers:
+
+ \list
+ \i keyPressEvent() is called whenever a key is pressed, and again when
+ a key has been held down long enough for it to auto-repeat. The
+ \key Tab and \key Shift+Tab keys are only passed to the widget if
+ they are not used by the focus-change mechanisms. To force those
+ keys to be processed by your widget, you must reimplement
+ QWidget::event().
+ \i focusInEvent() is called when the widget gains keyboard focus
+ (assuming you have called setFocusPolicy()). Well-behaved widgets
+ indicate that they own the keyboard focus in a clear but discreet
+ way.
+ \i focusOutEvent() is called when the widget loses keyboard focus.
+ \endlist
+
+ You may be required to also reimplement some of the less common event
+ handlers:
+
+ \list
+ \i mouseMoveEvent() is called whenever the mouse moves while a mouse
+ button is held down. This can be useful during drag and drop
+ operations. If you call setMouseTracking(true), you get mouse move
+ events even when no buttons are held down. (See also the \l{Drag
+ and Drop} guide.)
+ \i keyReleaseEvent() is called whenever a key is released and while it
+ is held down (if the key is auto-repeating). In that case, the
+ widget will receive a pair of key release and key press event for
+ every repeat. The \key Tab and \key Shift+Tab keys are only passed
+ to the widget if they are not used by the focus-change mechanisms.
+ To force those keys to be processed by your widget, you must
+ reimplement QWidget::event().
+ \i wheelEvent() is called whenever the user turns the mouse wheel
+ while the widget has the focus.
+ \i enterEvent() is called when the mouse enters the widget's screen
+ space. (This excludes screen space owned by any of the widget's
+ children.)
+ \i leaveEvent() is called when the mouse leaves the widget's screen
+ space. If the mouse enters a child widget it will not cause a
+ leaveEvent().
+ \i moveEvent() is called when the widget has been moved relative to
+ its parent.
+ \i closeEvent() is called when the user closes the widget (or when
+ close() is called).
+ \endlist
+
+ There are also some rather obscure events described in the documentation
+ for QEvent::Type. To handle these events, you need to reimplement event()
+ directly.
+
+ The default implementation of event() handles \key Tab and \key Shift+Tab
+ (to move the keyboard focus), and passes on most of the other events to
+ one of the more specialized handlers above.
+
+ Events and the mechanism used to deliver them are covered in the
+ \l{Events and Event Filters} document.
+
+ \section1 Groups of Functions and Properties
+
+ \table
+ \header \i Context \i Functions and Properties
+
+ \row \i Window functions \i
+ show(),
+ hide(),
+ raise(),
+ lower(),
+ close().
+
+ \row \i Top-level windows \i
+ \l windowModified, \l windowTitle, \l windowIcon, \l windowIconText,
+ \l isActiveWindow, activateWindow(), \l minimized, showMinimized(),
+ \l maximized, showMaximized(), \l fullScreen, showFullScreen(),
+ showNormal().
+
+ \row \i Window contents \i
+ update(),
+ repaint(),
+ scroll().
+
+ \row \i Geometry \i
+ \l pos, x(), y(), \l rect, \l size, width(), height(), move(), resize(),
+ \l sizePolicy, sizeHint(), minimumSizeHint(),
+ updateGeometry(), layout(),
+ \l frameGeometry, \l geometry, \l childrenRect, \l childrenRegion,
+ adjustSize(),
+ mapFromGlobal(), mapToGlobal(),
+ mapFromParent(), mapToParent(),
+ \l maximumSize, \l minimumSize, \l sizeIncrement,
+ \l baseSize, setFixedSize()
+
+ \row \i Mode \i
+ \l visible, isVisibleTo(),
+ \l enabled, isEnabledTo(),
+ \l modal,
+ isWindow(),
+ \l mouseTracking,
+ \l updatesEnabled,
+ visibleRegion().
+
+ \row \i Look and feel \i
+ style(),
+ setStyle(),
+ \l styleSheet,
+ \l cursor,
+ \l font,
+ \l palette,
+ backgroundRole(), setBackgroundRole(),
+ fontInfo(), fontMetrics().
+
+ \row \i Keyboard focus functions \i
+ \l focus, \l focusPolicy,
+ setFocus(), clearFocus(), setTabOrder(), setFocusProxy(),
+ focusNextChild(), focusPreviousChild().
+
+ \row \i Mouse and keyboard grabbing \i
+ grabMouse(), releaseMouse(),
+ grabKeyboard(), releaseKeyboard(),
+ mouseGrabber(), keyboardGrabber().
+
+ \row \i Event handlers \i
+ event(),
+ mousePressEvent(),
+ mouseReleaseEvent(),
+ mouseDoubleClickEvent(),
+ mouseMoveEvent(),
+ keyPressEvent(),
+ keyReleaseEvent(),
+ focusInEvent(),
+ focusOutEvent(),
+ wheelEvent(),
+ enterEvent(),
+ leaveEvent(),
+ paintEvent(),
+ moveEvent(),
+ resizeEvent(),
+ closeEvent(),
+ dragEnterEvent(),
+ dragMoveEvent(),
+ dragLeaveEvent(),
+ dropEvent(),
+ childEvent(),
+ showEvent(),
+ hideEvent(),
+ customEvent().
+ changeEvent(),
+
+ \row \i System functions \i
+ parentWidget(), window(), setParent(), winId(),
+ find(), metric().
+
+ \row \i Interactive help \i
+ setToolTip(), setWhatsThis()
+
+ \endtable
+
+
+ \section1 Widget Style Sheets
+
+ In addition to the standard widget styles for each platform, widgets can
+ also be styled according to rules specified in a \l{styleSheet}
+ {style sheet}. This feature enables you to customize the appearance of
+ specific widgets to provide visual cues to users about their purpose. For
+ example, a button could be styled in a particular way to indicate that it
+ performs a destructive action.
+
+ The use of widget style sheets is described in more detail in the
+ \l{Qt Style Sheets} document.
+
+
+ \section1 Transparency and Double Buffering
+
+ Since Qt 4.0, QWidget automatically double-buffers its painting, so there
+ is no need to write double-buffering code in paintEvent() to avoid
+ flicker.
+
+ Since Qt 4.1, the Qt::WA_ContentsPropagated widget attribute has been
+ deprecated. Instead, the contents of parent widgets are propagated by
+ default to each of their children as long as Qt::WA_PaintOnScreen is not
+ set. Custom widgets can be written to take advantage of this feature by
+ updating irregular regions (to create non-rectangular child widgets), or
+ painting with colors that have less than full alpha component. The
+ following diagram shows how attributes and properties of a custom widget
+ can be fine-tuned to achieve different effects.
+
+ \image propagation-custom.png
+
+ In the above diagram, a semi-transparent rectangular child widget with an
+ area removed is constructed and added to a parent widget (a QLabel showing
+ a pixmap). Then, different properties and widget attributes are set to
+ achieve different effects:
+
+ \list
+ \i The left widget has no additional properties or widget attributes
+ set. This default state suits most custom widgets using
+ transparency, are irregularly-shaped, or do not paint over their
+ entire area with an opaque brush.
+ \i The center widget has the \l autoFillBackground property set. This
+ property is used with custom widgets that rely on the widget to
+ supply a default background, and do not paint over their entire
+ area with an opaque brush.
+ \i The right widget has the Qt::WA_OpaquePaintEvent widget attribute
+ set. This indicates that the widget will paint over its entire area
+ with opaque colors. The widget's area will initially be
+ \e{uninitialized}, represented in the diagram with a red diagonal
+ grid pattern that shines through the overpainted area. The
+ Qt::WA_OpaquePaintArea attribute is useful for widgets that need to
+ paint their own specialized contents quickly and do not need a
+ default filled background.
+ \endlist
+
+ To rapidly update custom widgets with simple background colors, such as
+ real-time plotting or graphing widgets, it is better to define a suitable
+ background color (using setBackgroundRole() with the
+ QPalette::Window role), set the \l autoFillBackground property, and only
+ implement the necessary drawing functionality in the widget's paintEvent().
+
+ To rapidly update custom widgets that constantly paint over their entire
+ areas with opaque content, e.g., video streaming widgets, it is better to
+ set the widget's Qt::WA_OpaquePaintEvent, avoiding any unnecessary overhead
+ associated with repainting the widget's background.
+
+ If a widget has both the Qt::WA_OpaquePaintEvent widget attribute \e{and}
+ the \l autoFillBackground property set, the Qt::WA_OpaquePaintEvent
+ attribute takes precedence. Depending on your requirements, you should
+ choose either one of them.
+
+ Since Qt 4.1, the contents of parent widgets are also propagated to
+ standard Qt widgets. This can lead to some unexpected results if the
+ parent widget is decorated in a non-standard way, as shown in the diagram
+ below.
+
+ \image propagation-standard.png
+
+ The scope for customizing the painting behavior of standard Qt widgets,
+ without resorting to subclassing, is slightly less than that possible for
+ custom widgets. Usually, the desired appearance of a standard widget can be
+ achieved by setting its \l autoFillBackground property.
+
+
+ \section1 Creating Translucent Windows
+
+ Since Qt 4.5, it has been possible to create windows with translucent regions
+ on window systems that support compositing.
+
+ To enable this feature in a top-level widget, set its Qt::WA_TranslucentBackground
+ attribute with setAttribute() and ensure that its background is painted with
+ non-opaque colors in the regions you want to be partially transparent.
+
+ Platform notes:
+
+ \list
+ \o X11: This feature relies on the use of an X server that supports ARGB visuals
+ and a compositing window manager.
+ \o Windows: This feature requires Windows 2000 or later. The widget needs to have
+ the Qt::FramelessWindowHint window flag set for the translucency to work.
+ \endlist
+
+
+ \section1 Native Widgets vs Alien Widgets
+
+ Introduced in Qt 4.4, alien widgets are widgets unknown to the windowing
+ system. They do not have a native window handle associated with them. This
+ feature significantly speeds up widget painting, resizing, and removes flicker.
+
+ Should you require the old behavior with native windows, you can choose
+ one of the following options:
+
+ \list 1
+ \i Use the \c{QT_USE_NATIVE_WINDOWS=1} in your environment.
+ \i Set the Qt::AA_NativeWindows attribute on your application. All
+ widgets will be native widgets.
+ \i Set the Qt::WA_NativeWindow attribute on widgets: The widget itself
+ and all of its ancestors will become native (unless
+ Qt::WA_DontCreateNativeAncestors is set).
+ \i Call QWidget::winId to enforce a native window (this implies 3).
+ \i Set the Qt::WA_PaintOnScreen attribute to enforce a native window
+ (this implies 3).
+ \endlist
+
+ \sa QEvent, QPainter, QGridLayout, QBoxLayout
+*/
+
+QWidgetMapper *QWidgetPrivate::mapper = 0; // widget with wid
+QWidgetSet *QWidgetPrivate::uncreatedWidgets = 0; // widgets with no wid
+
+
+/*****************************************************************************
+ QWidget utility functions
+ *****************************************************************************/
+
+QRegion qt_dirtyRegion(QWidget *widget)
+{
+ if (!widget)
+ return QRegion();
+
+ QWidgetBackingStore *bs = qt_widget_private(widget)->maybeBackingStore();
+ if (!bs)
+ return QRegion();
+
+ return bs->dirtyRegion(widget);
+}
+
+/*****************************************************************************
+ QWidget member functions
+ *****************************************************************************/
+
+/*
+ Widget state flags:
+ \list
+ \i Qt::WA_WState_Created The widget has a valid winId().
+ \i Qt::WA_WState_Visible The widget is currently visible.
+ \i Qt::WA_WState_Hidden The widget is hidden, i.e. it won't
+ become visible unless you call show() on it. Qt::WA_WState_Hidden
+ implies !Qt::WA_WState_Visible.
+ \i Qt::WA_WState_CompressKeys Compress keyboard events.
+ \i Qt::WA_WState_BlockUpdates Repaints and updates are disabled.
+ \i Qt::WA_WState_InPaintEvent Currently processing a paint event.
+ \i Qt::WA_WState_Reparented The widget has been reparented.
+ \i Qt::WA_WState_ConfigPending A configuration (resize/move) event is pending.
+ \i Qt::WA_WState_DND (Deprecated) The widget supports drag and drop, see setAcceptDrops().
+ \endlist
+*/
+
+/*!
+ Constructs a widget which is a child of \a parent, with widget
+ flags set to \a f.
+
+ If \a parent is 0, the new widget becomes a window. If
+ \a parent is another widget, this widget becomes a child window
+ inside \a parent. The new widget is deleted when its \a parent is
+ deleted.
+
+ The widget flags argument, \a f, is normally 0, but it can be set
+ to customize the frame of a window (i.e. \a
+ parent must be 0). To customize the frame, use a value composed
+ from the bitwise OR of any of the \l{Qt::WindowFlags}{window flags}.
+
+ If you add a child widget to an already visible widget you must
+ explicitly show the child to make it visible.
+
+ Note that the X11 version of Qt may not be able to deliver all
+ combinations of style flags on all systems. This is because on
+ X11, Qt can only ask the window manager, and the window manager
+ can override the application's settings. On Windows, Qt can set
+ whatever flags you want.
+
+ \sa windowFlags
+*/
+
+QWidget::QWidget(QWidget *parent, Qt::WindowFlags f)
+ : QObject(*new QWidgetPrivate, 0), QPaintDevice()
+{
+ d_func()->init(parent, f);
+}
+
+#ifdef QT3_SUPPORT
+/*!
+ \overload
+ \obsolete
+ */
+QWidget::QWidget(QWidget *parent, const char *name, Qt::WindowFlags f)
+ : QObject(*new QWidgetPrivate, 0), QPaintDevice()
+{
+ d_func()->init(parent , f);
+ setObjectName(QString::fromAscii(name));
+}
+#endif
+
+/*! \internal
+*/
+QWidget::QWidget(QWidgetPrivate &dd, QWidget* parent, Qt::WindowFlags f)
+ : QObject(dd, 0), QPaintDevice()
+{
+ d_func()->init(parent, f);
+}
+
+/*!
+ \internal
+*/
+int QWidget::devType() const
+{
+ return QInternal::Widget;
+}
+
+
+//### w is a "this" ptr, passed as a param because QWorkspace needs special logic
+void QWidgetPrivate::adjustFlags(Qt::WindowFlags &flags, QWidget *w)
+{
+ bool customize = (flags & (Qt::CustomizeWindowHint
+ | Qt::FramelessWindowHint
+ | Qt::WindowTitleHint
+ | Qt::WindowSystemMenuHint
+ | Qt::WindowMinimizeButtonHint
+ | Qt::WindowMaximizeButtonHint
+ | Qt::WindowCloseButtonHint
+ | Qt::WindowContextHelpButtonHint));
+
+ uint type = (flags & Qt::WindowType_Mask);
+
+ if ((type == Qt::Widget || type == Qt::SubWindow) && w && !w->parent()) {
+ type = Qt::Window;
+ flags |= Qt::Window;
+ }
+
+ if (flags & Qt::CustomizeWindowHint) {
+ // modify window flags to make them consistent.
+ // Only enable this on non-Mac platforms. Since the old way of doing this would
+ // interpret WindowSystemMenuHint as a close button and we can't change that behavior
+ // we can't just add this in.
+#ifndef Q_WS_MAC
+ if (flags & (Qt::WindowMinMaxButtonsHint | Qt::WindowCloseButtonHint | Qt::WindowContextHelpButtonHint)) {
+ flags |= Qt::WindowSystemMenuHint;
+#else
+ if (flags & (Qt::WindowMinMaxButtonsHint | Qt::WindowCloseButtonHint
+ | Qt::WindowSystemMenuHint)) {
+#endif
+ flags |= Qt::WindowTitleHint;
+ flags &= ~Qt::FramelessWindowHint;
+ }
+ } else if (customize && !(flags & Qt::FramelessWindowHint)) {
+ // if any of the window hints that affect the titlebar are set
+ // and the window is supposed to have frame, we add a titlebar
+ // and system menu by default.
+ flags |= Qt::WindowSystemMenuHint;
+ flags |= Qt::WindowTitleHint;
+ }
+ if (customize)
+ ; // don't modify window flags if the user explicitely set them.
+ else if (type == Qt::Dialog || type == Qt::Sheet)
+#ifndef Q_OS_WINCE
+ flags |= Qt::WindowTitleHint | Qt::WindowSystemMenuHint | Qt::WindowContextHelpButtonHint | Qt::WindowCloseButtonHint;
+#else
+ flags |= Qt::WindowTitleHint | Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint;
+#endif
+ else if (type == Qt::Tool)
+ flags |= Qt::WindowTitleHint | Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint;
+ else
+ flags |= Qt::WindowTitleHint | Qt::WindowSystemMenuHint | Qt::WindowMinimizeButtonHint | Qt::WindowMaximizeButtonHint | Qt::WindowCloseButtonHint;
+
+
+}
+
+void QWidgetPrivate::init(QWidget *parentWidget, Qt::WindowFlags f)
+{
+ Q_Q(QWidget);
+ if (qApp->type() == QApplication::Tty)
+ qFatal("QWidget: Cannot create a QWidget when no GUI is being used");
+
+ Q_ASSERT(uncreatedWidgets);
+ uncreatedWidgets->insert(q);
+
+ QWidget *desktopWidget = 0;
+ if (parentWidget && parentWidget->windowType() == Qt::Desktop) {
+ desktopWidget = parentWidget;
+ parentWidget = 0;
+ }
+
+ q->data = &data;
+
+#ifndef QT_NO_THREAD
+ if (!q->parent()) {
+ Q_ASSERT_X(q->thread() == qApp->thread(), "QWidget",
+ "Widgets must be created in the GUI thread.");
+ }
+#endif
+
+#if defined(Q_WS_X11)
+ if (desktopWidget) {
+ // make sure the widget is created on the same screen as the
+ // programmer specified desktop widget
+ xinfo = desktopWidget->d_func()->xinfo;
+ }
+#else
+ Q_UNUSED(desktopWidget);
+#endif
+
+ data.fstrut_dirty = true;
+
+ data.winid = 0;
+ data.widget_attributes = 0;
+ data.window_flags = f;
+ data.window_state = 0;
+ data.focus_policy = 0;
+ data.context_menu_policy = Qt::DefaultContextMenu;
+ data.window_modality = Qt::NonModal;
+
+ data.sizehint_forced = 0;
+ data.is_closing = 0;
+ data.in_show = 0;
+ data.in_set_window_state = 0;
+ data.in_destructor = false;
+
+ // Widgets with Qt::MSWindowsOwnDC (typically QGLWidget) must have a window handle.
+ if (f & Qt::MSWindowsOwnDC)
+ q->setAttribute(Qt::WA_NativeWindow);
+
+#ifdef Q_OS_WINCE
+ data.window_state_internal = 0;
+#endif
+
+ q->setAttribute(Qt::WA_QuitOnClose); // might be cleared in adjustQuitOnCloseAttribute()
+ adjustQuitOnCloseAttribute();
+
+ q->setAttribute(Qt::WA_WState_Hidden);
+
+ //give potential windows a bigger "pre-initial" size; create_sys() will give them a new size later
+ data.crect = parentWidget ? QRect(0,0,100,30) : QRect(0,0,640,480);
+
+ focus_next = focus_prev = q;
+
+ if ((f & Qt::WindowType_Mask) == Qt::Desktop)
+ q->create();
+ else if (parentWidget)
+ q->setParent(parentWidget, data.window_flags);
+ else {
+ adjustFlags(data.window_flags, q);
+ resolveLayoutDirection();
+ // opaque system background?
+ const QBrush &background = q->palette().brush(QPalette::Window);
+ setOpaque(q->isWindow() && background.style() != Qt::NoBrush && background.isOpaque());
+ }
+ data.fnt = QFont(data.fnt, q);
+#if defined(Q_WS_X11)
+ data.fnt.x11SetScreen(xinfo.screen());
+#endif // Q_WS_X11
+
+ q->setAttribute(Qt::WA_PendingMoveEvent);
+ q->setAttribute(Qt::WA_PendingResizeEvent);
+
+ if (++QWidgetPrivate::instanceCounter > QWidgetPrivate::maxInstances)
+ QWidgetPrivate::maxInstances = QWidgetPrivate::instanceCounter;
+
+ if (QApplicationPrivate::app_compile_version < 0x040200
+ || QApplicationPrivate::testAttribute(Qt::AA_ImmediateWidgetCreation))
+ q->create();
+
+
+ QEvent e(QEvent::Create);
+ QApplication::sendEvent(q, &e);
+ QApplication::postEvent(q, new QEvent(QEvent::PolishRequest));
+
+ extraPaintEngine = 0;
+}
+
+
+
+void QWidgetPrivate::createRecursively()
+{
+ Q_Q(QWidget);
+ q->create(0, true, true);
+ for (int i = 0; i < children.size(); ++i) {
+ QWidget *child = qobject_cast<QWidget *>(children.at(i));
+ if (child && !child->isHidden() && !child->isWindow() && !child->testAttribute(Qt::WA_WState_Created))
+ child->d_func()->createRecursively();
+ }
+}
+
+
+
+
+/*!
+ Creates a new widget window if \a window is 0, otherwise sets the
+ widget's window to \a window.
+
+ Initializes the window (sets the geometry etc.) if \a
+ initializeWindow is true. If \a initializeWindow is false, no
+ initialization is performed. This parameter only makes sense if \a
+ window is a valid window.
+
+ Destroys the old window if \a destroyOldWindow is true. If \a
+ destroyOldWindow is false, you are responsible for destroying the
+ window yourself (using platform native code).
+
+ The QWidget constructor calls create(0,true,true) to create a
+ window for this widget.
+*/
+
+void QWidget::create(WId window, bool initializeWindow, bool destroyOldWindow)
+{
+ Q_D(QWidget);
+ if (testAttribute(Qt::WA_WState_Created) && window == 0 && internalWinId())
+ return;
+
+ if (d->data.in_destructor)
+ return;
+
+ Qt::WindowType type = windowType();
+ Qt::WindowFlags &flags = data->window_flags;
+
+ if ((type == Qt::Widget || type == Qt::SubWindow) && !parentWidget()) {
+ type = Qt::Window;
+ flags |= Qt::Window;
+ }
+
+ if (QWidget *parent = parentWidget()) {
+ if (type & Qt::Window) {
+ if (!parent->testAttribute(Qt::WA_WState_Created))
+ parent->createWinId();
+ } else if (testAttribute(Qt::WA_NativeWindow) && !parent->internalWinId()
+ && !testAttribute(Qt::WA_DontCreateNativeAncestors)) {
+ // We're about to create a native child widget that doesn't have a native parent;
+ // enforce a native handle for the parent unless the Qt::WA_DontCreateNativeAncestors
+ // attribute is set.
+ d->createWinId(window);
+ // Nothing more to do.
+ Q_ASSERT(testAttribute(Qt::WA_WState_Created));
+ Q_ASSERT(internalWinId());
+ return;
+ }
+ }
+
+#ifdef QT3_SUPPORT
+ if (flags & Qt::WStaticContents)
+ setAttribute(Qt::WA_StaticContents);
+ if (flags & Qt::WDestructiveClose)
+ setAttribute(Qt::WA_DeleteOnClose);
+ if (flags & Qt::WShowModal)
+ setWindowModality(Qt::ApplicationModal);
+ if (flags & Qt::WMouseNoMask)
+ setAttribute(Qt::WA_MouseNoMask);
+ if (flags & Qt::WGroupLeader)
+ setAttribute(Qt::WA_GroupLeader);
+ if (flags & Qt::WNoMousePropagation)
+ setAttribute(Qt::WA_NoMousePropagation);
+#endif
+
+ static int paintOnScreenEnv = -1;
+ if (paintOnScreenEnv == -1)
+ paintOnScreenEnv = qgetenv("QT_ONSCREEN_PAINT").toInt() > 0 ? 1 : 0;
+ if (paintOnScreenEnv == 1)
+ setAttribute(Qt::WA_PaintOnScreen);
+
+ if (QApplicationPrivate::testAttribute(Qt::AA_NativeWindows))
+ setAttribute(Qt::WA_NativeWindow);
+
+#ifdef ALIEN_DEBUG
+ qDebug() << "QWidget::create:" << this << "parent:" << parentWidget()
+ << "Alien?" << !testAttribute(Qt::WA_NativeWindow);
+#endif
+
+#if defined (Q_WS_WIN) && !defined(QT_NO_DRAGANDDROP)
+ // Unregister the dropsite (if already registered) before we
+ // re-create the widget with a native window.
+ if (testAttribute(Qt::WA_WState_Created) && !internalWinId() && testAttribute(Qt::WA_NativeWindow)
+ && d->extra && d->extra->dropTarget) {
+ d->registerDropSite(false);
+ }
+#endif // defined (Q_WS_WIN) && !defined(QT_NO_DRAGANDDROP)
+
+ d->updateIsOpaque();
+
+ setAttribute(Qt::WA_WState_Created); // set created flag
+ d->create_sys(window, initializeWindow, destroyOldWindow);
+
+ // a real toplevel window needs a backing store
+ if (isWindow()) {
+ delete d->topData()->backingStore;
+ // QWidgetBackingStore will check this variable, hence it must be 0
+ d->topData()->backingStore = 0;
+ if (hasBackingStoreSupport())
+ d->topData()->backingStore = new QWidgetBackingStore(this);
+ }
+
+ d->setModal_sys();
+
+ if (!isWindow() && parentWidget() && parentWidget()->testAttribute(Qt::WA_DropSiteRegistered))
+ setAttribute(Qt::WA_DropSiteRegistered, true);
+
+#ifdef QT_EVAL
+ extern void qt_eval_init_widget(QWidget *w);
+ qt_eval_init_widget(this);
+#endif
+
+ // need to force the resting of the icon after changing parents
+ if (testAttribute(Qt::WA_SetWindowIcon))
+ d->setWindowIcon_sys(true);
+ if (isWindow() && !d->topData()->iconText.isEmpty())
+ d->setWindowIconText_helper(d->topData()->iconText);
+ if (windowType() != Qt::Desktop) {
+ d->updateSystemBackground();
+
+ if (isWindow() && !testAttribute(Qt::WA_SetWindowIcon))
+ d->setWindowIcon_sys();
+ }
+}
+
+/*!
+ Destroys the widget.
+
+ All this widget's children are deleted first. The application
+ exits if this widget is the main widget.
+*/
+
+QWidget::~QWidget()
+{
+ Q_D(QWidget);
+ d->data.in_destructor = true;
+
+#if defined (QT_CHECK_STATE)
+ if (paintingActive())
+ qWarning("QWidget: %s (%s) deleted while being painted", className(), name());
+#endif
+
+ // force acceptDrops false before winId is destroyed.
+ d->registerDropSite(false);
+
+#ifndef QT_NO_ACTION
+ // remove all actions from this widget
+ for (int i = 0; i < d->actions.size(); ++i) {
+ QActionPrivate *apriv = d->actions.at(i)->d_func();
+ apriv->widgets.removeAll(this);
+ }
+ d->actions.clear();
+#endif
+
+#ifndef QT_NO_SHORTCUT
+ // Remove all shortcuts grabbed by this
+ // widget, unless application is closing
+ if (!QApplicationPrivate::is_app_closing && testAttribute(Qt::WA_GrabbedShortcut))
+ qApp->d_func()->shortcutMap.removeShortcut(0, this, QKeySequence());
+#endif
+
+ // delete layout while we still are a valid widget
+ delete d->layout;
+ // Remove myself from focus list
+
+ Q_ASSERT(d->focus_next->d_func()->focus_prev == this);
+ Q_ASSERT(d->focus_prev->d_func()->focus_next == this);
+
+ if (d->focus_next != this) {
+ d->focus_next->d_func()->focus_prev = d->focus_prev;
+ d->focus_prev->d_func()->focus_next = d->focus_next;
+ d->focus_next = d->focus_prev = 0;
+ }
+
+#ifdef QT3_SUPPORT
+ if (QApplicationPrivate::main_widget == this) { // reset main widget
+ QApplicationPrivate::main_widget = 0;
+ qApp->quit();
+ }
+#endif
+
+ clearFocus();
+
+ d->setDirtyOpaqueRegion();
+
+ if (isWindow() && isVisible() && internalWinId())
+ hide();
+#if defined(Q_WS_WIN) || defined(Q_WS_X11)
+ else if (!internalWinId() && isVisible())
+ qApp->d_func()->sendSyntheticEnterLeave(this);
+#endif
+
+ if (QWidgetBackingStore *bs = d->maybeBackingStore()) {
+ bs->removeDirtyWidget(this);
+ if (testAttribute(Qt::WA_StaticContents))
+ bs->removeStaticWidget(this);
+ }
+
+ delete d->needsFlush;
+ d->needsFlush = 0;
+
+ // set all QPointers for this object to zero
+ QObjectPrivate::clearGuards(this);
+
+ if (!d->children.isEmpty())
+ d->deleteChildren();
+
+ QApplication::removePostedEvents(this);
+
+ destroy(); // platform-dependent cleanup
+
+ --QWidgetPrivate::instanceCounter;
+
+ if (QWidgetPrivate::uncreatedWidgets) // might have been deleted by ~QApplication
+ QWidgetPrivate::uncreatedWidgets->remove(this);
+
+ QEvent e(QEvent::Destroy);
+ QCoreApplication::sendEvent(this, &e);
+}
+
+int QWidgetPrivate::instanceCounter = 0; // Current number of widget instances
+int QWidgetPrivate::maxInstances = 0; // Maximum number of widget instances
+
+void QWidgetPrivate::setWinId(WId id) // set widget identifier
+{
+ Q_Q(QWidget);
+ if (mapper && data.winid) {
+ mapper->remove(data.winid);
+ uncreatedWidgets->insert(q);
+ }
+
+ data.winid = id;
+#if defined(Q_WS_X11)
+ hd = id; // X11: hd == ident
+#endif
+ if (mapper && id) {
+ mapper->insert(data.winid, q);
+ uncreatedWidgets->remove(q);
+ }
+}
+
+void QWidgetPrivate::createTLExtra()
+{
+ if (!extra)
+ createExtra();
+ if (!extra->topextra) {
+ QTLWExtra* x = extra->topextra = new QTLWExtra;
+ x->windowSurface = 0;
+ x->opacity = 255;
+ x->posFromMove = false;
+ x->sizeAdjusted = false;
+ x->inTopLevelResize = false;
+ x->inRepaint = false;
+ x->backingStore = 0;
+ x->icon = 0;
+ x->iconPixmap = 0;
+ x->frameStrut.setCoords(0, 0, 0, 0);
+ x->incw = x->inch = 0;
+ x->basew = x->baseh = 0;
+ x->normalGeometry = QRect(0,0,-1,-1);
+#if defined(Q_WS_WIN) || defined(Q_WS_X11) || defined(Q_WS_MAC)
+ x->embedded = 0;
+#endif
+#if defined(Q_WS_X11)
+ x->parentWinId = 0;
+ x->spont_unmapped = 0;
+ x->dnd = 0;
+#endif
+ x->savedFlags = 0;
+#if defined(Q_WS_QWS) && !defined(QT_NO_QWS_MANAGER)
+ x->qwsManager = 0;
+#endif
+ x->sharedPainter = 0;
+ createTLSysExtra();
+#ifdef QWIDGET_EXTRA_DEBUG
+ static int count = 0;
+ qDebug() << "tlextra" << ++count;
+#endif
+ }
+}
+
+/*!
+ \internal
+ Creates the widget extra data.
+*/
+
+void QWidgetPrivate::createExtra()
+{
+ if (!extra) { // if not exists
+ extra = new QWExtra;
+ extra->minw = extra->minh = 0;
+ extra->maxw = extra->maxh = QWIDGETSIZE_MAX;
+ extra->explicitMinSize = 0;
+ extra->explicitMaxSize = 0;
+ extra->autoFillBackground = 0;
+ extra->nativeChildrenForced = 0;
+ extra->inRenderWithPainter = 0;
+ extra->hasMask = 0;
+#ifndef QT_NO_CURSOR
+ extra->curs = 0;
+#endif
+ extra->style = 0;
+ extra->topextra = 0;
+ extra->proxyWidget = 0;
+ extra->glContext = 0;
+ extra->customDpiX = 0;
+ extra->customDpiY = 0;
+ createSysExtra();
+#ifdef QWIDGET_EXTRA_DEBUG
+ static int count = 0;
+ qDebug() << "extra" << ++count;
+#endif
+ }
+}
+
+
+/*!
+ \internal
+ Deletes the widget extra data.
+*/
+
+void QWidgetPrivate::deleteExtra()
+{
+ if (extra) { // if exists
+#ifndef QT_NO_CURSOR
+ delete extra->curs;
+#endif
+ deleteSysExtra();
+#ifndef QT_NO_STYLE_STYLESHEET
+ // dereference the stylesheet style
+ if (QStyleSheetStyle *proxy = qobject_cast<QStyleSheetStyle *>(extra->style))
+ proxy->deref();
+#endif
+ if (extra->topextra) {
+ deleteTLSysExtra();
+ delete extra->topextra->backingStore;
+ delete extra->topextra->icon;
+ delete extra->topextra->iconPixmap;
+#if defined(Q_WS_QWS) && !defined(QT_NO_QWS_MANAGER)
+ delete extra->topextra->qwsManager;
+#endif
+ delete extra->topextra->windowSurface;
+ delete extra->topextra;
+ }
+ delete extra;
+ // extra->xic destroyed in QWidget::destroy()
+ extra = 0;
+ }
+}
+
+/*
+ Returns true if the background is inherited; otherwise returns
+ false.
+
+ Mainly used in the paintOnScreen case.
+*/
+
+bool QWidgetPrivate::isBackgroundInherited() const
+{
+ Q_Q(const QWidget);
+
+ // windows do not inherit their background
+ if (q->isWindow() || q->windowType() == Qt::SubWindow)
+ return false;
+
+ if (q->testAttribute(Qt::WA_NoSystemBackground) || q->testAttribute(Qt::WA_OpaquePaintEvent))
+ return false;
+
+ const QPalette &pal = q->palette();
+ QPalette::ColorRole bg = q->backgroundRole();
+ QBrush brush = pal.brush(bg);
+
+ // non opaque brushes leaves us no choice, we must inherit
+ if (!q->autoFillBackground() || !brush.isOpaque())
+ return true;
+
+ if (brush.style() == Qt::SolidPattern) {
+ // the background is just a solid color. If there is no
+ // propagated contents, then we claim as performance
+ // optimization that it was not inheritet. This is the normal
+ // case in standard Windows or Motif style.
+ const QWidget *w = q->parentWidget();
+ if (!w->d_func()->isBackgroundInherited())
+ return false;
+ }
+
+ return true;
+}
+
+/*
+ Returns true if there are widgets above this which overlap with
+ \a rect, which is in parent's coordinate system (same as crect).
+*/
+
+bool QWidgetPrivate::isOverlapped(const QRect &rect) const
+{
+ Q_Q(const QWidget);
+
+ const QWidget *w = q;
+ QRect r = rect;
+ while (w) {
+ if (w->isWindow())
+ return false;
+ QWidgetPrivate *pd = w->parentWidget()->d_func();
+ bool above = false;
+ for (int i = 0; i < pd->children.size(); ++i) {
+ QWidget *sibling = qobject_cast<QWidget *>(pd->children.at(i));
+ if (!sibling || !sibling->isVisible() || sibling->isWindow())
+ continue;
+ if (!above) {
+ above = (sibling == w);
+ continue;
+ }
+
+ if (qRectIntersects(sibling->data->crect, r)) {
+ const QWExtra *siblingExtra = sibling->d_func()->extra;
+ if (siblingExtra && siblingExtra->hasMask
+ && !siblingExtra->mask.translated(sibling->data->crect.topLeft()).intersects(r)) {
+ continue;
+ }
+ return true;
+ }
+ }
+ w = w->parentWidget();
+ r.translate(pd->data.crect.topLeft());
+ }
+ return false;
+}
+
+void QWidgetPrivate::syncBackingStore()
+{
+ if (paintOnScreen()) {
+ repaint_sys(dirty);
+ dirty = QRegion();
+ } else if (QWidgetBackingStore *bs = maybeBackingStore()) {
+ bs->sync();
+ }
+}
+
+void QWidgetPrivate::syncBackingStore(const QRegion &region)
+{
+ if (paintOnScreen())
+ repaint_sys(region);
+ else if (QWidgetBackingStore *bs = maybeBackingStore())
+ bs->sync(q_func(), region);
+}
+
+void QWidgetPrivate::setUpdatesEnabled_helper(bool enable)
+{
+ Q_Q(QWidget);
+
+ if (enable && !q->isWindow() && q->parentWidget() && !q->parentWidget()->updatesEnabled())
+ return; // nothing we can do
+
+ if (enable != q->testAttribute(Qt::WA_UpdatesDisabled))
+ return; // nothing to do
+
+ q->setAttribute(Qt::WA_UpdatesDisabled, !enable);
+ if (enable)
+ q->update();
+
+ Qt::WidgetAttribute attribute = enable ? Qt::WA_ForceUpdatesDisabled : Qt::WA_UpdatesDisabled;
+ for (int i = 0; i < children.size(); ++i) {
+ QWidget *w = qobject_cast<QWidget *>(children.at(i));
+ if (w && !w->isWindow() && !w->testAttribute(attribute))
+ w->d_func()->setUpdatesEnabled_helper(enable);
+ }
+}
+
+/*!
+ \internal
+
+ Propagate this widget's palette to all children, except style sheet
+ widgets, and windows that don't enable window propagation (palettes don't
+ normally propagate to windows).
+*/
+void QWidgetPrivate::propagatePaletteChange()
+{
+ Q_Q(QWidget);
+ // Propagate a new inherited mask to all children.
+ if (!q->parentWidget() && extra && extra->proxyWidget) {
+#ifndef QT_NO_GRAPHICSVIEW
+ QGraphicsProxyWidget *p = extra->proxyWidget;
+ inheritedPaletteResolveMask = p->d_func()->inheritedPaletteResolveMask | p->palette().resolve();
+#endif //QT_NO_GRAPHICSVIEW
+ } else if (q->isWindow() && !q->testAttribute(Qt::WA_WindowPropagation)) {
+ inheritedPaletteResolveMask = 0;
+ }
+ int mask = data.pal.resolve() | inheritedPaletteResolveMask;
+
+ QEvent pc(QEvent::PaletteChange);
+ QApplication::sendEvent(q, &pc);
+ for (int i = 0; i < children.size(); ++i) {
+ QWidget *w = qobject_cast<QWidget*>(children.at(i));
+ if (w && !w->testAttribute(Qt::WA_StyleSheet)
+ && (!w->isWindow() || w->testAttribute(Qt::WA_WindowPropagation))) {
+ QWidgetPrivate *wd = w->d_func();
+ wd->inheritedPaletteResolveMask = mask;
+ wd->resolvePalette();
+ }
+ }
+#if defined(QT3_SUPPORT)
+ q->paletteChange(q->palette()); // compatibility
+#endif
+}
+
+/*
+ Returns the widget's clipping rectangle.
+*/
+QRect QWidgetPrivate::clipRect() const
+{
+ Q_Q(const QWidget);
+ const QWidget * w = q;
+ if (!w->isVisible())
+ return QRect();
+ QRect r = q->rect();
+ int ox = 0;
+ int oy = 0;
+ while (w
+ && w->isVisible()
+ && !w->isWindow()
+ && w->parentWidget()) {
+ ox -= w->x();
+ oy -= w->y();
+ w = w->parentWidget();
+ r &= QRect(ox, oy, w->width(), w->height());
+ }
+ return r;
+}
+
+/*
+ Returns the widget's clipping region (without siblings).
+*/
+QRegion QWidgetPrivate::clipRegion() const
+{
+ Q_Q(const QWidget);
+ if (!q->isVisible())
+ return QRegion();
+ QRegion r(q->rect());
+ const QWidget * w = q;
+ const QWidget *ignoreUpTo;
+ int ox = 0;
+ int oy = 0;
+ while (w
+ && w->isVisible()
+ && !w->isWindow()
+ && w->parentWidget()) {
+ ox -= w->x();
+ oy -= w->y();
+ ignoreUpTo = w;
+ w = w->parentWidget();
+ r &= QRegion(ox, oy, w->width(), w->height());
+
+ int i = 0;
+ while(w->d_func()->children.at(i++) != static_cast<const QObject *>(ignoreUpTo))
+ ;
+ for ( ; i < w->d_func()->children.size(); ++i) {
+ if(QWidget *sibling = qobject_cast<QWidget *>(w->d_func()->children.at(i))) {
+ if(sibling->isVisible() && !sibling->isWindow()) {
+ QRect siblingRect(ox+sibling->x(), oy+sibling->y(),
+ sibling->width(), sibling->height());
+ if (qRectIntersects(siblingRect, q->rect()))
+ r -= QRegion(siblingRect);
+ }
+ }
+ }
+ }
+ return r;
+}
+
+void QWidgetPrivate::setDirtyOpaqueRegion()
+{
+ Q_Q(QWidget);
+
+ dirtyOpaqueChildren = true;
+
+ if (q->isWindow())
+ return;
+
+ QWidget *parent = q->parentWidget();
+ if (!parent)
+ return;
+
+ // TODO: instead of setting dirtyflag, manipulate the dirtyregion directly?
+ QWidgetPrivate *pd = parent->d_func();
+ if (!pd->dirtyOpaqueChildren)
+ pd->setDirtyOpaqueRegion();
+}
+
+QRegion QWidgetPrivate::getOpaqueRegion() const
+{
+ Q_Q(const QWidget);
+
+ QRegion r = isOpaque ? q->rect() : getOpaqueChildren();
+ if (extra && extra->hasMask)
+ r &= extra->mask;
+ if (r.isEmpty())
+ return r;
+ return r & clipRect();
+}
+
+const QRegion &QWidgetPrivate::getOpaqueChildren() const
+{
+ if (!dirtyOpaqueChildren)
+ return opaqueChildren;
+
+ QWidgetPrivate *that = const_cast<QWidgetPrivate*>(this);
+ that->opaqueChildren = QRegion();
+
+ for (int i = 0; i < children.size(); ++i) {
+ QWidget *child = qobject_cast<QWidget *>(children.at(i));
+ if (!child || !child->isVisible() || child->isWindow())
+ continue;
+
+ const QPoint offset = child->geometry().topLeft();
+ that->opaqueChildren += child->d_func()->getOpaqueRegion().translated(offset);
+ }
+
+ that->dirtyOpaqueChildren = false;
+
+ return that->opaqueChildren;
+}
+
+void QWidgetPrivate::subtractOpaqueChildren(QRegion &source, const QRect &clipRect) const
+{
+ if (children.isEmpty() || clipRect.isEmpty())
+ return;
+
+ const QRegion &r = getOpaqueChildren();
+ if (!r.isEmpty())
+ source -= (r & clipRect);
+}
+
+//subtract any relatives that are higher up than me --- this is too expensive !!!
+void QWidgetPrivate::subtractOpaqueSiblings(QRegion &sourceRegion, bool *hasDirtySiblingsAbove,
+ bool alsoNonOpaque) const
+{
+ Q_Q(const QWidget);
+ static int disableSubtractOpaqueSiblings = qgetenv("QT_NO_SUBTRACTOPAQUESIBLINGS").toInt();
+ if (disableSubtractOpaqueSiblings || q->isWindow())
+ return;
+
+ QRect clipBoundingRect;
+ bool dirtyClipBoundingRect = true;
+
+ QRegion parentClip;
+ bool dirtyParentClip = true;
+
+ QPoint parentOffset = data.crect.topLeft();
+
+ const QWidget *w = q;
+
+ while (w) {
+ if (w->isWindow())
+ break;
+ QWidgetPrivate *pd = w->parentWidget()->d_func();
+ const int myIndex = pd->children.indexOf(const_cast<QWidget *>(w));
+ for (int i = myIndex + 1; i < pd->children.size(); ++i) {
+ QWidget *sibling = qobject_cast<QWidget *>(pd->children.at(i));
+ if (!sibling || !sibling->isVisible() || sibling->isWindow())
+ continue;
+
+ if (!qRectIntersects(sibling->data->crect, w->data->crect))
+ continue;
+
+ if (dirtyClipBoundingRect) {
+ clipBoundingRect = sourceRegion.boundingRect();
+ dirtyClipBoundingRect = false;
+ }
+
+ if (!qRectIntersects(sibling->data->crect, clipBoundingRect.translated(parentOffset)))
+ continue;
+
+ if (dirtyParentClip) {
+ parentClip = sourceRegion.translated(parentOffset);
+ dirtyParentClip = false;
+ }
+
+ const QPoint siblingPos(sibling->data->crect.topLeft());
+ const QRect siblingClipRect(sibling->d_func()->clipRect());
+ QRegion siblingDirty(parentClip);
+ siblingDirty &= (siblingClipRect.translated(siblingPos));
+ const bool hasMask = sibling->d_func()->extra && sibling->d_func()->extra->hasMask;
+ if (hasMask)
+ siblingDirty &= sibling->d_func()->extra->mask.translated(siblingPos);
+ if (siblingDirty.isEmpty())
+ continue;
+
+ if (sibling->d_func()->isOpaque || alsoNonOpaque) {
+ if (hasMask) {
+ siblingDirty.translate(-parentOffset);
+ sourceRegion -= siblingDirty;
+ } else {
+ sourceRegion -= sibling->data->crect.translated(-parentOffset);
+ }
+ } else {
+ if (hasDirtySiblingsAbove)
+ *hasDirtySiblingsAbove = true;
+ if (sibling->d_func()->children.isEmpty())
+ continue;
+ QRegion opaqueSiblingChildren(sibling->d_func()->getOpaqueChildren());
+ opaqueSiblingChildren.translate(-parentOffset + siblingPos);
+ sourceRegion -= opaqueSiblingChildren;
+ }
+ if (sourceRegion.isEmpty())
+ return;
+
+ dirtyClipBoundingRect = true;
+ dirtyParentClip = true;
+ }
+
+ w = w->parentWidget();
+ parentOffset += pd->data.crect.topLeft();
+ dirtyParentClip = true;
+ }
+}
+
+void QWidgetPrivate::clipToEffectiveMask(QRegion &region) const
+{
+ Q_Q(const QWidget);
+
+ const QWidget *w = q;
+ QPoint offset;
+
+ while (w) {
+ const QWidgetPrivate *wd = w->d_func();
+ if (wd->extra && wd->extra->hasMask)
+ region &= (w != q) ? wd->extra->mask.translated(offset) : wd->extra->mask;
+ if (w->isWindow())
+ return;
+ offset -= wd->data.crect.topLeft();
+ w = w->parentWidget();
+ }
+}
+
+bool QWidgetPrivate::hasBackground() const
+{
+ Q_Q(const QWidget);
+ if (!q->isWindow() && q->parentWidget() && q->parentWidget()->testAttribute(Qt::WA_PaintOnScreen))
+ return true;
+ if (q->testAttribute(Qt::WA_PaintOnScreen))
+ return true;
+ if (!q->testAttribute(Qt::WA_OpaquePaintEvent) && !q->testAttribute(Qt::WA_NoSystemBackground)) {
+ const QPalette &pal = q->palette();
+ QPalette::ColorRole bg = q->backgroundRole();
+ QBrush bgBrush = pal.brush(bg);
+ return (bgBrush.style() != Qt::NoBrush &&
+ ((q->isWindow() || q->windowType() == Qt::SubWindow)
+ || (QPalette::ColorRole(bg_role) != QPalette::NoRole || (pal.resolve() & (1<<bg)))));
+ }
+ return false;
+}
+
+bool QWidgetPrivate::paintOnScreen() const
+{
+#if defined(Q_WS_QWS)
+ return false;
+#elif defined(QT_NO_BACKINGSTORE)
+ return true;
+#else
+ Q_Q(const QWidget);
+ if (q->testAttribute(Qt::WA_PaintOnScreen)
+ || (!q->isWindow() && q->window()->testAttribute(Qt::WA_PaintOnScreen))) {
+ return true;
+ }
+
+ return !qt_enable_backingstore;
+#endif
+}
+
+void QWidgetPrivate::updateIsOpaque()
+{
+ // hw: todo: only needed if opacity actually changed
+ setDirtyOpaqueRegion();
+
+ Q_Q(QWidget);
+#ifdef Q_WS_X11
+ if (q->testAttribute(Qt::WA_X11OpenGLOverlay)) {
+ setOpaque(false);
+ return;
+ }
+#endif
+
+ if (q->testAttribute(Qt::WA_OpaquePaintEvent) || q->testAttribute(Qt::WA_PaintOnScreen)) {
+ setOpaque(true);
+ return;
+ }
+
+ const QPalette &pal = q->palette();
+
+ if (q->autoFillBackground()) {
+ const QBrush &autoFillBrush = pal.brush(q->backgroundRole());
+ if (autoFillBrush.style() != Qt::NoBrush && autoFillBrush.isOpaque()) {
+ setOpaque(true);
+ return;
+ }
+ }
+
+ if (q->isWindow() && !q->testAttribute(Qt::WA_NoSystemBackground)) {
+ const QBrush &windowBrush = q->palette().brush(QPalette::Window);
+ if (windowBrush.style() != Qt::NoBrush && windowBrush.isOpaque()) {
+ setOpaque(true);
+ return;
+ }
+ }
+ setOpaque(false);
+}
+
+void QWidgetPrivate::setOpaque(bool opaque)
+{
+ if (isOpaque == opaque)
+ return;
+ isOpaque = opaque;
+#ifdef Q_WS_MAC
+ macUpdateIsOpaque();
+#endif
+#ifdef Q_WS_X11
+ x11UpdateIsOpaque();
+#endif
+#ifdef Q_WS_WIN
+ winUpdateIsOpaque();
+#endif
+}
+
+void QWidgetPrivate::updateIsTranslucent()
+{
+#ifdef Q_WS_MAC
+ macUpdateIsOpaque();
+#endif
+#ifdef Q_WS_X11
+ x11UpdateIsOpaque();
+#endif
+#ifdef Q_WS_WIN
+ winUpdateIsOpaque();
+#endif
+}
+
+/*!
+ \fn void QPixmap::fill(const QWidget *widget, const QPoint &offset)
+
+ Fills the pixmap with the \a widget's background color or pixmap
+ according to the given offset.
+
+ The QPoint \a offset defines a point in widget coordinates to
+ which the pixmap's top-left pixel will be mapped to. This is only
+ significant if the widget has a background pixmap; otherwise the
+ pixmap will simply be filled with the background color of the
+ widget.
+*/
+
+void QPixmap::fill( const QWidget *widget, const QPoint &off )
+{
+ QPainter p(this);
+ p.translate(-off);
+ widget->d_func()->paintBackground(&p, QRect(off, size()));
+
+}
+
+static inline void fillRegion(QPainter *painter, const QRegion &rgn, const QPoint &offset, const QBrush &brush)
+{
+ Q_ASSERT(painter);
+
+ if (brush.style() == Qt::TexturePattern) {
+#ifdef Q_WS_MAC
+ // Optimize pattern filling on mac by using HITheme directly
+ // when filling with the standard widget background.
+ // Defined in qmacstyle_mac.cpp
+ extern void qt_mac_fill_background(QPainter *painter, const QRegion &rgn, const QPoint &offset, const QBrush &brush);
+ qt_mac_fill_background(painter, rgn, offset, brush);
+#else
+ const QRegion translated = rgn.translated(offset);
+ const QRect rect(translated.boundingRect());
+ painter->setClipRegion(translated);
+ painter->drawTiledPixmap(rect, brush.texture(), rect.topLeft());
+#endif
+ } else {
+ const QVector<QRect> &rects = rgn.rects();
+ for (int i = 0; i < rects.size(); ++i)
+ painter->fillRect(rects.at(i).translated(offset), brush);
+ }
+}
+
+
+void QWidgetPrivate::paintBackground(QPainter *painter, const QRegion &rgn, const QPoint &offset, int flags) const
+{
+ Q_Q(const QWidget);
+
+ const QBrush autoFillBrush = q->palette().brush(q->backgroundRole());
+
+ if ((flags & DrawAsRoot) && !(q->autoFillBackground() && autoFillBrush.isOpaque())) {
+ const QBrush bg = q->palette().brush(QPalette::Window);
+#ifdef Q_WS_QWS
+ if (!(flags & DontSetCompositionMode) && painter->paintEngine()->hasFeature(QPaintEngine::PorterDuff))
+ painter->setCompositionMode(QPainter::CompositionMode_Source); //copy alpha straight in
+#endif
+ fillRegion(painter, rgn, offset, bg);
+ }
+
+ if (q->autoFillBackground())
+ fillRegion(painter, rgn, offset, autoFillBrush);
+
+ if (q->testAttribute(Qt::WA_StyledBackground)) {
+ painter->setClipRegion(rgn.translated(offset));
+ QStyleOption opt;
+ opt.initFrom(q);
+ q->style()->drawPrimitive(QStyle::PE_Widget, &opt, painter, q);
+ }
+}
+
+/*
+ \internal
+ This function is called when a widget is hidden or destroyed.
+ It resets some application global pointers that should only refer active,
+ visible widgets.
+*/
+
+#ifdef Q_WS_MAC
+ extern QPointer<QWidget> qt_button_down;
+#else
+ extern QWidget *qt_button_down;
+#endif
+
+void QWidgetPrivate::deactivateWidgetCleanup()
+{
+ Q_Q(QWidget);
+ // If this was the active application window, reset it
+ if (qApp->activeWindow() == q)
+ qApp->setActiveWindow(0);
+ // If the is the active mouse press widget, reset it
+ if (q == qt_button_down)
+ qt_button_down = 0;
+}
+
+
+/*!
+ Returns a pointer to the widget with window identifer/handle \a
+ id.
+
+ The window identifier type depends on the underlying window
+ system, see \c qwindowdefs.h for the actual definition. If there
+ is no widget with this identifier, 0 is returned.
+*/
+
+QWidget *QWidget::find(WId id)
+{
+ return QWidgetPrivate::mapper ? QWidgetPrivate::mapper->value(id, 0) : 0;
+}
+
+
+
+/*!
+ \fn WId QWidget::internalWinId() const
+ \internal
+ Returns the window system identifier of the widget, or 0 if the widget is not created yet.
+
+*/
+
+/*!
+ \fn WId QWidget::winId() const
+
+ Returns the window system identifier of the widget.
+
+ Portable in principle, but if you use it you are probably about to
+ do something non-portable. Be careful.
+
+ If a widget is non-native (alien) and winId() is invoked on it, that widget
+ will be provided a native handle.
+
+ \note We recommend that you do not store this value as it is likely to
+ change at run-time.
+
+ \sa find()
+*/
+WId QWidget::winId() const
+{
+ if (!testAttribute(Qt::WA_WState_Created) || !internalWinId()) {
+ QWidget *that = const_cast<QWidget*>(this);
+ that->setAttribute(Qt::WA_NativeWindow);
+ that->d_func()->createWinId();
+ return that->data->winid;
+ }
+ return data->winid;
+}
+
+
+void QWidgetPrivate::createWinId(WId winid)
+{
+ Q_Q(QWidget);
+ const bool forceNativeWindow = q->testAttribute(Qt::WA_NativeWindow);
+ if (!q->testAttribute(Qt::WA_WState_Created) || (forceNativeWindow && !q->internalWinId())) {
+ if (!q->isWindow()) {
+ QWidget *parent = q->parentWidget();
+ QWidgetPrivate *pd = parent->d_func();
+ if (forceNativeWindow && !q->testAttribute(Qt::WA_DontCreateNativeAncestors))
+ parent->setAttribute(Qt::WA_NativeWindow);
+ if (!parent->internalWinId()) {
+ pd->createWinId();
+ }
+
+ for (int i = 0; i < pd->children.size(); ++i) {
+ QWidget *w = qobject_cast<QWidget *>(pd->children.at(i));
+ if (w && !w->isWindow() && (!w->testAttribute(Qt::WA_WState_Created)
+ || (!w->internalWinId() && w->testAttribute(Qt::WA_NativeWindow)))) {
+ if (w!=q) {
+ w->create();
+ } else {
+ w->create(winid);
+ // if the window has already been created, we
+ // need to raise it to its proper stacking position
+ if (winid)
+ w->raise();
+ }
+ }
+ }
+ } else {
+ q->create();
+ }
+ }
+}
+
+
+/*!
+\internal
+Ensures that the widget has a window system identifier, i.e. that it is known to the windowing system.
+
+*/
+
+void QWidget::createWinId()
+{
+ Q_D(QWidget);
+// qWarning("QWidget::createWinId is obsolete, please fix your code.");
+ d->createWinId();
+}
+
+/*!
+ \since 4.4
+
+ Returns the effective window system identifier of the widget, i.e. the
+ native parent's window system identifier.
+
+ If the widget is native, this function returns the native widget ID.
+ Otherwise, the window ID of the first native parent widget, i.e., the
+ top-level widget that contains this widget, is returned.
+
+ \note We recommend that you do not store this value as it is likely to
+ change at run-time.
+
+ \sa nativeParentWidget()
+*/
+WId QWidget::effectiveWinId() const
+{
+ WId id = internalWinId();
+ if (id || !testAttribute(Qt::WA_WState_Created))
+ return id;
+ QWidget *realParent = nativeParentWidget();
+ Q_ASSERT(realParent);
+ Q_ASSERT(realParent->internalWinId());
+ return realParent->internalWinId();
+}
+
+#ifndef QT_NO_STYLE_STYLESHEET
+
+/*!
+ \property QWidget::styleSheet
+ \brief the widget's style sheet
+ \since 4.2
+
+ The style sheet contains a textual description of customizations to the
+ widget's style, as described in the \l{Qt Style Sheets} document.
+
+ \note Qt style sheets are currently not supported for QMacStyle
+ (the default style on Mac OS X). We plan to address this in some future
+ release.
+
+ \sa setStyle(), QApplication::styleSheet, {Qt Style Sheets}
+*/
+QString QWidget::styleSheet() const
+{
+ Q_D(const QWidget);
+ if (!d->extra)
+ return QString();
+ return d->extra->styleSheet;
+}
+
+void QWidget::setStyleSheet(const QString& styleSheet)
+{
+ Q_D(QWidget);
+ d->createExtra();
+
+ QStyleSheetStyle *proxy = qobject_cast<QStyleSheetStyle *>(d->extra->style);
+ d->extra->styleSheet = styleSheet;
+ if (styleSheet.isEmpty()) { // stylesheet removed
+ if (!proxy)
+ return;
+
+ d->inheritStyle();
+ return;
+ }
+
+ if (proxy) { // style sheet update
+ proxy->repolish(this);
+ return;
+ }
+
+ if (testAttribute(Qt::WA_SetStyle)) {
+ d->setStyle_helper(new QStyleSheetStyle(d->extra->style), true);
+ } else {
+ d->setStyle_helper(new QStyleSheetStyle(0), true);
+ }
+}
+
+#endif // QT_NO_STYLE_STYLESHEET
+
+/*!
+ \sa QWidget::setStyle(), QApplication::setStyle(), QApplication::style()
+*/
+
+QStyle *QWidget::style() const
+{
+ Q_D(const QWidget);
+
+ if (d->extra && d->extra->style)
+ return d->extra->style;
+ return qApp->style();
+}
+
+/*!
+ Sets the widget's GUI style to \a style. The ownership of the style
+ object is not transferred.
+
+ If no style is set, the widget uses the application's style,
+ QApplication::style() instead.
+
+ Setting a widget's style has no effect on existing or future child
+ widgets.
+
+ \warning This function is particularly useful for demonstration
+ purposes, where you want to show Qt's styling capabilities. Real
+ applications should avoid it and use one consistent GUI style
+ instead.
+
+ \warning Qt style sheets are currently not supported for custom QStyle
+ subclasses. We plan to address this in some future release.
+
+ \sa style(), QStyle, QApplication::style(), QApplication::setStyle()
+*/
+
+void QWidget::setStyle(QStyle *style)
+{
+ Q_D(QWidget);
+ setAttribute(Qt::WA_SetStyle, style != 0);
+ d->createExtra();
+#ifndef QT_NO_STYLE_STYLESHEET
+ if (QStyleSheetStyle *proxy = qobject_cast<QStyleSheetStyle *>(style)) {
+ //if for some reason someone try to set a QStyleSheetStyle, ref it
+ //(this may happen for exemple in QButtonDialogBox which propagates its style)
+ proxy->ref();
+ d->setStyle_helper(style, false);
+ } else if (qobject_cast<QStyleSheetStyle *>(d->extra->style) || !qApp->styleSheet().isEmpty()) {
+ // if we have an application stylesheet or have a proxy already, propagate
+ d->setStyle_helper(new QStyleSheetStyle(style), true);
+ } else
+#endif
+ d->setStyle_helper(style, false);
+}
+
+void QWidgetPrivate::setStyle_helper(QStyle *newStyle, bool propagate, bool
+#ifdef Q_WS_MAC
+ metalHack
+#endif
+ )
+{
+ Q_Q(QWidget);
+ createExtra();
+
+ QStyle *oldStyle = q->style();
+#ifndef QT_NO_STYLE_STYLESHEET
+ QStyle *origStyle = extra->style;
+#endif
+ extra->style = newStyle;
+
+ // repolish
+ if (q->windowType() != Qt::Desktop) {
+ if (polished) {
+ oldStyle->unpolish(q);
+#ifdef Q_WS_MAC
+ if (metalHack)
+ macUpdateMetalAttribute();
+#endif
+ q->style()->polish(q);
+#ifdef Q_WS_MAC
+ } else if (metalHack) {
+ macUpdateMetalAttribute();
+#endif
+ }
+ }
+
+ if (propagate) {
+ for (int i = 0; i < children.size(); ++i) {
+ QWidget *c = qobject_cast<QWidget*>(children.at(i));
+ if (c)
+ c->d_func()->inheritStyle();
+ }
+ }
+
+ QEvent e(QEvent::StyleChange);
+ QApplication::sendEvent(q, &e);
+#ifdef QT3_SUPPORT
+ q->styleChange(*oldStyle);
+#endif
+
+#ifndef QT_NO_STYLE_STYLESHEET
+ if (!qobject_cast<QStyleSheetStyle*>(newStyle)) {
+ if (const QStyleSheetStyle* cssStyle = qobject_cast<QStyleSheetStyle*>(origStyle)) {
+ cssStyle->clearWidgetFont(q);
+ }
+ }
+#endif
+
+#ifndef QT_NO_STYLE_STYLESHEET
+ // dereference the old stylesheet style
+ if (QStyleSheetStyle *proxy = qobject_cast<QStyleSheetStyle *>(origStyle))
+ proxy->deref();
+#endif
+}
+
+// Inherits style from the current parent and propagates it as necessary
+void QWidgetPrivate::inheritStyle()
+{
+#ifndef QT_NO_STYLE_STYLESHEET
+ Q_Q(QWidget);
+
+ QStyleSheetStyle *proxy = extra ? qobject_cast<QStyleSheetStyle *>(extra->style) : 0;
+
+ if (!q->styleSheet().isEmpty()) {
+ Q_ASSERT(proxy);
+ proxy->repolish(q);
+ return;
+ }
+
+ QStyle *origStyle = proxy ? proxy->base : (extra ? (QStyle*)extra->style : 0);
+ QWidget *parent = q->parentWidget();
+ QStyle *parentStyle = (parent && parent->d_func()->extra) ? (QStyle*)parent->d_func()->extra->style : 0;
+ // If we have stylesheet on app or parent has stylesheet style, we need
+ // to be running a proxy
+ if (!qApp->styleSheet().isEmpty() || qobject_cast<QStyleSheetStyle *>(parentStyle)) {
+ QStyle *newStyle = parentStyle;
+ if (q->testAttribute(Qt::WA_SetStyle))
+ newStyle = new QStyleSheetStyle(origStyle);
+ else if (QStyleSheetStyle *newProxy = qobject_cast<QStyleSheetStyle *>(parentStyle))
+ newProxy->ref();
+
+ setStyle_helper(newStyle, true);
+ return;
+ }
+
+ // So, we have no stylesheet on parent/app and we have an empty stylesheet
+ // we just need our original style back
+ if (origStyle == (extra ? (QStyle*)extra->style : 0)) // is it any different?
+ return;
+
+ // We could have inherited the proxy from our parent (which has a custom style)
+ // In such a case we need to start following the application style (i.e revert
+ // the propagation behavior of QStyleSheetStyle)
+ if (!q->testAttribute(Qt::WA_SetStyle))
+ origStyle = 0;
+
+ setStyle_helper(origStyle, true);
+#endif // QT_NO_STYLE_STYLESHEET
+}
+
+#ifdef QT3_SUPPORT
+/*!
+ \overload
+
+ Sets the widget's GUI style to \a style using the QStyleFactory.
+*/
+QStyle* QWidget::setStyle(const QString &style)
+{
+ QStyle *s = QStyleFactory::create(style);
+ setStyle(s);
+ return s;
+}
+#endif
+
+/*!
+ \fn bool QWidget::isWindow() const
+
+ Returns true if the widget is an independent window, otherwise
+ returns false.
+
+ A window is a widget that isn't visually the child of any other
+ widget and that usually has a frame and a
+ \l{QWidget::setWindowTitle()}{window title}.
+
+ A window can have a \l{QWidget::parentWidget()}{parent widget}.
+ It will then be grouped with its parent and deleted when the
+ parent is deleted, minimized when the parent is minimized etc. If
+ supported by the window manager, it will also have a common
+ taskbar entry with its parent.
+
+ QDialog and QMainWindow widgets are by default windows, even if a
+ parent widget is specified in the constructor. This behavior is
+ specified by the Qt::Window flag.
+
+ \sa window(), isModal(), parentWidget()
+*/
+
+/*!
+ \property QWidget::modal
+ \brief whether the widget is a modal widget
+
+ This property only makes sense for windows. A modal widget
+ prevents widgets in all other windows from getting any input.
+
+ By default, this property is false.
+
+ \sa isWindow(), windowModality, QDialog
+*/
+
+/*!
+ \property QWidget::windowModality
+ \brief which windows are blocked by the modal widget
+ \since 4.1
+
+ This property only makes sense for windows. A modal widget
+ prevents widgets in other windows from getting input. The value of
+ this property controls which windows are blocked when the widget
+ is visible. Changing this property while the window is visible has
+ no effect; you must hide() the widget first, then show() it again.
+
+ By default, this property is Qt::NonModal.
+
+ \sa isWindow(), QWidget::modal, QDialog
+*/
+
+Qt::WindowModality QWidget::windowModality() const
+{
+ return static_cast<Qt::WindowModality>(data->window_modality);
+}
+
+void QWidget::setWindowModality(Qt::WindowModality windowModality)
+{
+ data->window_modality = windowModality;
+ // setModal_sys() will be called by setAttribute()
+ setAttribute(Qt::WA_ShowModal, (data->window_modality != Qt::NonModal));
+ setAttribute(Qt::WA_SetWindowModality, true);
+}
+
+/*!
+ \fn bool QWidget::underMouse() const
+
+ Returns true if the widget is under the mouse cursor; otherwise
+ returns false.
+
+ This value is not updated properly during drag and drop
+ operations.
+
+ \sa enterEvent(), leaveEvent()
+*/
+
+/*!
+ \property QWidget::minimized
+ \brief whether this widget is minimized (iconified)
+
+ This property is only relevant for windows.
+
+ By default, this property is false.
+
+ \sa showMinimized(), visible, show(), hide(), showNormal(), maximized
+*/
+bool QWidget::isMinimized() const
+{ return data->window_state & Qt::WindowMinimized; }
+
+/*!
+ Shows the widget minimized, as an icon.
+
+ Calling this function only affects \l{isWindow()}{windows}.
+
+ \sa showNormal(), showMaximized(), show(), hide(), isVisible(),
+ isMinimized()
+*/
+void QWidget::showMinimized()
+{
+ bool isMin = isMinimized();
+ if (isMin && isVisible())
+ return;
+
+ ensurePolished();
+#ifdef QT3_SUPPORT
+ if (parent())
+ QApplication::sendPostedEvents(parent(), QEvent::ChildInserted);
+#endif
+
+ if (!isMin)
+ setWindowState((windowState() & ~Qt::WindowActive) | Qt::WindowMinimized);
+ show();
+}
+
+/*!
+ \property QWidget::maximized
+ \brief whether this widget is maximized
+
+ This property is only relevant for windows.
+
+ \note Due to limitations on some window systems, this does not always
+ report the expected results (e.g., if the user on X11 maximizes the
+ window via the window manager, Qt has no way of distinguishing this
+ from any other resize). This is expected to improve as window manager
+ protocols evolve.
+
+ By default, this property is false.
+
+ \sa windowState(), showMaximized(), visible, show(), hide(), showNormal(), minimized
+*/
+bool QWidget::isMaximized() const
+{ return data->window_state & Qt::WindowMaximized; }
+
+
+
+/*!
+ Returns the current window state. The window state is a OR'ed
+ combination of Qt::WindowState: Qt::WindowMinimized,
+ Qt::WindowMaximized, Qt::WindowFullScreen, and Qt::WindowActive.
+
+ \sa Qt::WindowState setWindowState()
+ */
+Qt::WindowStates QWidget::windowState() const
+{
+ return (Qt::WindowStates)data->window_state;
+}
+
+/*!\internal
+
+ The function sets the window state on child widgets similar to
+ setWindowState(). The difference is that the window state changed
+ event has the isOverride() flag set. It exists mainly to keep
+ Q3Workspace working.
+ */
+void QWidget::overrideWindowState(Qt::WindowStates newstate)
+{
+ QWindowStateChangeEvent e((Qt::WindowStates)data->window_state, true);
+ data->window_state = newstate;
+ QApplication::sendEvent(this, &e);
+}
+
+/*!
+ \fn void QWidget::setWindowState(Qt::WindowStates windowState)
+
+ Sets the window state to \a windowState. The window state is a OR'ed
+ combination of Qt::WindowState: Qt::WindowMinimized,
+ Qt::WindowMaximized, Qt::WindowFullScreen, and Qt::WindowActive.
+
+ If the window is not visible (i.e. isVisible() returns false), the
+ window state will take effect when show() is called. For visible
+ windows, the change is immediate. For example, to toggle between
+ full-screen and normal mode, use the following code:
+
+ \snippet doc/src/snippets/code/src_gui_kernel_qwidget.cpp 0
+
+ In order to restore and activate a minimized window (while
+ preserving its maximized and/or full-screen state), use the following:
+
+ \snippet doc/src/snippets/code/src_gui_kernel_qwidget.cpp 1
+
+ Calling this function will hide the widget. You must call show() to make
+ the widget visible again.
+
+ \note On some window systems Qt::WindowActive is not immediate, and may be
+ ignored in certain cases.
+
+ When the window state changes, the widget receives a changeEvent()
+ of type QEvent::WindowStateChange.
+
+ \sa Qt::WindowState windowState()
+*/
+
+/*!
+ \property QWidget::fullScreen
+ \brief whether the widget is shown in full screen mode
+
+ A widget in full screen mode occupies the whole screen area and does not
+ display window decorations, such as a title bar.
+
+ By default, this property is false.
+
+ \sa windowState(), minimized, maximized
+*/
+bool QWidget::isFullScreen() const
+{ return data->window_state & Qt::WindowFullScreen; }
+
+/*!
+ Shows the widget in full-screen mode.
+
+ Calling this function only affects \l{isWindow()}{windows}.
+
+ To return from full-screen mode, call showNormal().
+
+ Full-screen mode works fine under Windows, but has certain
+ problems under X. These problems are due to limitations of the
+ ICCCM protocol that specifies the communication between X11
+ clients and the window manager. ICCCM simply does not understand
+ the concept of non-decorated full-screen windows. Therefore, the
+ best we can do is to request a borderless window and place and
+ resize it to fill the entire screen. Depending on the window
+ manager, this may or may not work. The borderless window is
+ requested using MOTIF hints, which are at least partially
+ supported by virtually all modern window managers.
+
+ An alternative would be to bypass the window manager entirely and
+ create a window with the Qt::X11BypassWindowManagerHint flag. This
+ has other severe problems though, like totally broken keyboard focus
+ and very strange effects on desktop changes or when the user raises
+ other windows.
+
+ X11 window managers that follow modern post-ICCCM specifications
+ support full-screen mode properly.
+
+ \sa showNormal(), showMaximized(), show(), hide(), isVisible()
+*/
+void QWidget::showFullScreen()
+{
+ ensurePolished();
+#ifdef QT3_SUPPORT
+ if (parent())
+ QApplication::sendPostedEvents(parent(), QEvent::ChildInserted);
+#endif
+
+ setWindowState((windowState() & ~(Qt::WindowMinimized | Qt::WindowMaximized))
+ | Qt::WindowFullScreen);
+ show();
+ activateWindow();
+}
+
+/*!
+ Shows the widget maximized.
+
+ Calling this function only affects \l{isWindow()}{windows}.
+
+ On X11, this function may not work properly with certain window
+ managers. See \l{geometry.html}{Window Geometry} for an explanation.
+
+ \sa setWindowState(), showNormal(), showMinimized(), show(), hide(), isVisible()
+*/
+void QWidget::showMaximized()
+{
+ ensurePolished();
+#ifdef QT3_SUPPORT
+ if (parent())
+ QApplication::sendPostedEvents(parent(), QEvent::ChildInserted);
+#endif
+
+ setWindowState((windowState() & ~(Qt::WindowMinimized | Qt::WindowFullScreen))
+ | Qt::WindowMaximized);
+ show();
+}
+
+/*!
+ Restores the widget after it has been maximized or minimized.
+
+ Calling this function only affects \l{isWindow()}{windows}.
+
+ \sa setWindowState(), showMinimized(), showMaximized(), show(), hide(), isVisible()
+*/
+void QWidget::showNormal()
+{
+ ensurePolished();
+#ifdef QT3_SUPPORT
+ if (parent())
+ QApplication::sendPostedEvents(parent(), QEvent::ChildInserted);
+#endif
+
+ setWindowState(windowState() & ~(Qt::WindowMinimized
+ | Qt::WindowMaximized
+ | Qt::WindowFullScreen));
+ show();
+}
+
+/*!
+ Returns true if this widget would become enabled if \a ancestor is
+ enabled; otherwise returns false.
+
+
+
+ This is the case if neither the widget itself nor every parent up
+ to but excluding \a ancestor has been explicitly disabled.
+
+ isEnabledTo(0) is equivalent to isEnabled().
+
+ \sa setEnabled() enabled
+*/
+
+bool QWidget::isEnabledTo(QWidget* ancestor) const
+{
+ const QWidget * w = this;
+ while (w && !w->testAttribute(Qt::WA_ForceDisabled)
+ && !w->isWindow()
+ && w->parentWidget()
+ && w->parentWidget() != ancestor)
+ w = w->parentWidget();
+ return !w->testAttribute(Qt::WA_ForceDisabled);
+}
+
+#ifndef QT_NO_ACTION
+/*!
+ Appends the action \a action to this widget's list of actions.
+
+ All QWidgets have a list of \l{QAction}s, however they can be
+ represented graphically in many different ways. The default use of
+ the QAction list (as returned by actions()) is to create a context
+ QMenu.
+
+ A QWidget should only have one of each action and adding an action
+ it already has will not cause the same action to be in the widget twice.
+
+ The ownership of \a action is not transferred to this QWidget.
+
+ \sa removeAction(), insertAction(), actions(), QMenu
+*/
+void QWidget::addAction(QAction *action)
+{
+ insertAction(0, action);
+}
+
+/*!
+ Appends the actions \a actions to this widget's list of actions.
+
+ \sa removeAction(), QMenu, addAction()
+*/
+void QWidget::addActions(QList<QAction*> actions)
+{
+ for(int i = 0; i < actions.count(); i++)
+ insertAction(0, actions.at(i));
+}
+
+/*!
+ Inserts the action \a action to this widget's list of actions,
+ before the action \a before. It appends the action if \a before is 0 or
+ \a before is not a valid action for this widget.
+
+ A QWidget should only have one of each action.
+
+ \sa removeAction(), addAction(), QMenu, contextMenuPolicy, actions()
+*/
+void QWidget::insertAction(QAction *before, QAction *action)
+{
+ if(!action) {
+ qWarning("QWidget::insertAction: Attempt to insert null action");
+ return;
+ }
+
+ Q_D(QWidget);
+ if(d->actions.contains(action))
+ removeAction(action);
+
+ int pos = d->actions.indexOf(before);
+ if (pos < 0) {
+ before = 0;
+ pos = d->actions.size();
+ }
+ d->actions.insert(pos, action);
+
+ QActionPrivate *apriv = action->d_func();
+ apriv->widgets.append(this);
+
+ QActionEvent e(QEvent::ActionAdded, action, before);
+ QApplication::sendEvent(this, &e);
+}
+
+/*!
+ Inserts the actions \a actions to this widget's list of actions,
+ before the action \a before. It appends the action if \a before is 0 or
+ \a before is not a valid action for this widget.
+
+ A QWidget can have at most one of each action.
+
+ \sa removeAction(), QMenu, insertAction(), contextMenuPolicy
+*/
+void QWidget::insertActions(QAction *before, QList<QAction*> actions)
+{
+ for(int i = 0; i < actions.count(); ++i)
+ insertAction(before, actions.at(i));
+}
+
+/*!
+ Removes the action \a action from this widget's list of actions.
+ \sa insertAction(), actions(), insertAction()
+*/
+void QWidget::removeAction(QAction *action)
+{
+ if (!action)
+ return;
+
+ Q_D(QWidget);
+
+ QActionPrivate *apriv = action->d_func();
+ apriv->widgets.removeAll(this);
+
+ if (d->actions.removeAll(action)) {
+ QActionEvent e(QEvent::ActionRemoved, action);
+ QApplication::sendEvent(this, &e);
+ }
+}
+
+/*!
+ Returns the (possibly empty) list of this widget's actions.
+
+ \sa contextMenuPolicy, insertAction(), removeAction()
+*/
+QList<QAction*> QWidget::actions() const
+{
+ Q_D(const QWidget);
+ return d->actions;
+}
+#endif // QT_NO_ACTION
+
+/*!
+ \fn bool QWidget::isEnabledToTLW() const
+ \obsolete
+
+ This function is deprecated. It is equivalent to isEnabled()
+*/
+
+/*!
+ \property QWidget::enabled
+ \brief whether the widget is enabled
+
+ An enabled widget handles keyboard and mouse events; a disabled
+ widget does not.
+
+ Some widgets display themselves differently when they are
+ disabled. For example a button might draw its label grayed out. If
+ your widget needs to know when it becomes enabled or disabled, you
+ can use the changeEvent() with type QEvent::EnabledChange.
+
+ Disabling a widget implicitly disables all its children. Enabling
+ respectively enables all child widgets unless they have been
+ explicitly disabled.
+
+ By default, this property is true.
+
+ \sa isEnabledTo(), QKeyEvent, QMouseEvent, changeEvent()
+*/
+void QWidget::setEnabled(bool enable)
+{
+ Q_D(QWidget);
+ setAttribute(Qt::WA_ForceDisabled, !enable);
+ d->setEnabled_helper(enable);
+}
+
+void QWidgetPrivate::setEnabled_helper(bool enable)
+{
+ Q_Q(QWidget);
+
+ if (enable && !q->isWindow() && q->parentWidget() && !q->parentWidget()->isEnabled())
+ return; // nothing we can do
+
+ if (enable != q->testAttribute(Qt::WA_Disabled))
+ return; // nothing to do
+
+ q->setAttribute(Qt::WA_Disabled, !enable);
+ updateSystemBackground();
+
+ if (!enable && q->window()->focusWidget() == q) {
+ bool parentIsEnabled = (!q->parentWidget() || q->parentWidget()->isEnabled());
+ if (!parentIsEnabled || !q->focusNextChild())
+ q->clearFocus();
+ }
+
+ Qt::WidgetAttribute attribute = enable ? Qt::WA_ForceDisabled : Qt::WA_Disabled;
+ for (int i = 0; i < children.size(); ++i) {
+ QWidget *w = qobject_cast<QWidget *>(children.at(i));
+ if (w && !w->testAttribute(attribute))
+ w->d_func()->setEnabled_helper(enable);
+ }
+#if defined(Q_WS_X11)
+ if (q->testAttribute(Qt::WA_SetCursor) || q->isWindow()) {
+ // enforce the windows behavior of clearing the cursor on
+ // disabled widgets
+ extern void qt_x11_enforce_cursor(QWidget * w); // defined in qwidget_x11.cpp
+ qt_x11_enforce_cursor(q);
+ }
+#endif
+#if defined(Q_WS_MAC)
+ setEnabled_helper_sys(enable);
+#endif
+#if defined (Q_WS_WIN)
+ if (q->hasFocus())
+ QInputContextPrivate::updateImeStatus(q, true);
+#endif
+ QEvent e(QEvent::EnabledChange);
+ QApplication::sendEvent(q, &e);
+#ifdef QT3_SUPPORT
+ q->enabledChange(!enable); // compatibility
+#endif
+}
+
+/*!
+ \property QWidget::acceptDrops
+ \brief whether drop events are enabled for this widget
+
+ Setting this property to true announces to the system that this
+ widget \e may be able to accept drop events.
+
+ If the widget is the desktop (windowType() == Qt::Desktop), this may
+ fail if another application is using the desktop; you can call
+ acceptDrops() to test if this occurs.
+
+ \warning Do not modify this property in a drag and drop event handler.
+
+ By default, this property is false.
+
+ \sa {Drag and Drop}
+*/
+bool QWidget::acceptDrops() const
+{
+ return testAttribute(Qt::WA_AcceptDrops);
+}
+
+void QWidget::setAcceptDrops(bool on)
+{
+ setAttribute(Qt::WA_AcceptDrops, on);
+
+}
+
+/*!
+ \fn void QWidget::enabledChange(bool)
+
+ \internal
+ \obsolete
+*/
+
+/*!
+ \fn void QWidget::paletteChange(const QPalette &)
+
+ \internal
+ \obsolete
+*/
+
+/*!
+ \fn void QWidget::fontChange(const QFont &)
+
+ \internal
+ \obsolete
+*/
+
+/*!
+ \fn void QWidget::windowActivationChange(bool)
+
+ \internal
+ \obsolete
+*/
+
+/*!
+ \fn void QWidget::languageChange()
+
+ \obsolete
+*/
+
+/*!
+ \fn void QWidget::styleChange(QStyle& style)
+
+ \internal
+ \obsolete
+*/
+
+/*!
+ Disables widget input events if \a disable is true; otherwise
+ enables input events.
+
+ See the \l enabled documentation for more information.
+
+ \sa isEnabledTo(), QKeyEvent, QMouseEvent, changeEvent()
+*/
+void QWidget::setDisabled(bool disable)
+{
+ setEnabled(!disable);
+}
+
+/*!
+ \property QWidget::frameGeometry
+ \brief geometry of the widget relative to its parent including any
+ window frame
+
+ See the \link geometry.html Window Geometry documentation\endlink
+ for an overview of geometry issues with windows.
+
+ By default, this property contains a value that depends on the user's
+ platform and screen geometry.
+
+ \sa geometry() x() y() pos()
+*/
+QRect QWidget::frameGeometry() const
+{
+ Q_D(const QWidget);
+ if (isWindow() && ! (windowType() == Qt::Popup)) {
+ QRect fs = d->frameStrut();
+ return QRect(data->crect.x() - fs.left(),
+ data->crect.y() - fs.top(),
+ data->crect.width() + fs.left() + fs.right(),
+ data->crect.height() + fs.top() + fs.bottom());
+ }
+ return data->crect;
+}
+
+/*!
+ \property QWidget::x
+
+ \brief the x coordinate of the widget relative to its parent including
+ any window frame
+
+ See the \link geometry.html Window Geometry documentation\endlink
+ for an overview of window geometry.
+
+ By default, this property has a value of 0.
+
+ \sa frameGeometry, y, pos
+*/
+int QWidget::x() const
+{
+ Q_D(const QWidget);
+ if (isWindow() && ! (windowType() == Qt::Popup))
+ return data->crect.x() - d->frameStrut().left();
+ return data->crect.x();
+}
+
+/*!
+ \property QWidget::y
+ \brief the y coordinate of the widget relative to its parent and
+ including any window frame
+
+ See the \link geometry.html Window Geometry documentation\endlink
+ for an overview of window geometry.
+
+ By default, this property has a value of 0.
+
+ \sa frameGeometry, x, pos
+*/
+int QWidget::y() const
+{
+ Q_D(const QWidget);
+ if (isWindow() && ! (windowType() == Qt::Popup))
+ return data->crect.y() - d->frameStrut().top();
+ return data->crect.y();
+}
+
+/*!
+ \property QWidget::pos
+ \brief the position of the widget within its parent widget
+
+ If the widget is a window, the position is that of the widget on
+ the desktop, including its frame.
+
+ When changing the position, the widget, if visible, receives a
+ move event (moveEvent()) immediately. If the widget is not
+ currently visible, it is guaranteed to receive an event before it
+ is shown.
+
+ By default, this property contains a position that refers to the
+ origin.
+
+ \warning Calling move() or setGeometry() inside moveEvent() can
+ lead to infinite recursion.
+
+ See the \link geometry.html Window Geometry documentation\endlink
+ for an overview of window geometry.
+
+ \sa frameGeometry, size x(), y()
+*/
+QPoint QWidget::pos() const
+{
+ Q_D(const QWidget);
+ if (isWindow() && ! (windowType() == Qt::Popup)) {
+ QRect fs = d->frameStrut();
+ return QPoint(data->crect.x() - fs.left(), data->crect.y() - fs.top());
+ }
+ return data->crect.topLeft();
+}
+
+/*!
+ \property QWidget::geometry
+ \brief the geometry of the widget relative to its parent and
+ excluding the window frame
+
+ When changing the geometry, the widget, if visible, receives a
+ move event (moveEvent()) and/or a resize event (resizeEvent())
+ immediately. If the widget is not currently visible, it is
+ guaranteed to receive appropriate events before it is shown.
+
+ The size component is adjusted if it lies outside the range
+ defined by minimumSize() and maximumSize().
+
+ \warning Calling setGeometry() inside resizeEvent() or moveEvent()
+ can lead to infinite recursion.
+
+ See the \link geometry.html Window Geometry documentation\endlink
+ for an overview of window geometry.
+
+ By default, this property contains a value that depends on the user's
+ platform and screen geometry.
+
+ \sa frameGeometry(), rect(), move(), resize(), moveEvent(),
+ resizeEvent(), minimumSize(), maximumSize()
+*/
+
+/*!
+ \property QWidget::normalGeometry
+
+ \brief the geometry of the widget as it will appear when shown as
+ a normal (not maximized or full screen) top-level widget
+
+ For child widgets this property always holds an empty rectangle.
+
+ By default, this property contains an empty rectangle.
+
+ \sa QWidget::windowState(), QWidget::geometry
+*/
+
+/*!
+ \property QWidget::size
+ \brief the size of the widget excluding any window frame
+
+ If the widget is visible when it is being resized, it receives a resize event
+ (resizeEvent()) immediately. If the widget is not currently
+ visible, it is guaranteed to receive an event before it is shown.
+
+ The size is adjusted if it lies outside the range defined by
+ minimumSize() and maximumSize().
+
+ By default, this property contains a value that depends on the user's
+ platform and screen geometry.
+
+ \warning Calling resize() or setGeometry() inside resizeEvent() can
+ lead to infinite recursion.
+
+ \note Setting the size to \c{QSize(0, 0)} will cause the widget to not
+ appear on screen. This also applies to windows.
+
+ \sa pos, geometry, minimumSize, maximumSize, resizeEvent()
+*/
+
+/*!
+ \property QWidget::width
+ \brief the width of the widget excluding any window frame
+
+ See the \link geometry.html Window Geometry documentation\endlink
+ for an overview of window geometry.
+
+ \note Do not use this function to find the width of a screen on
+ a \l{QDesktopWidget}{multiple screen desktop}. Read
+ \l{multiple screens note}{this note} for details.
+
+ By default, this property contains a value that depends on the user's
+ platform and screen geometry.
+
+ \sa geometry, height, size
+*/
+
+/*!
+ \property QWidget::height
+ \brief the height of the widget excluding any window frame
+
+ See the \link geometry.html Window Geometry documentation\endlink
+ for an overview of window geometry.
+
+ \note Do not use this function to find the height of a screen
+ on a \l {QDesktopWidget} {multiple screen desktop}. Read
+ \l {multiple screens note} {this note} for details.
+
+ By default, this property contains a value that depends on the user's
+ platform and screen geometry.
+
+ \sa geometry, width, size
+*/
+
+/*!
+ \property QWidget::rect
+ \brief the internal geometry of the widget excluding any window
+ frame
+
+ The rect property equals QRect(0, 0, width(), height()).
+
+ See the \link geometry.html Window Geometry documentation\endlink
+ for an overview of window geometry.
+
+ By default, this property contains a value that depends on the user's
+ platform and screen geometry.
+
+ \sa size
+*/
+
+
+QRect QWidget::normalGeometry() const
+{
+ Q_D(const QWidget);
+ if (!d->extra || !d->extra->topextra)
+ return QRect();
+
+ if (!isMaximized() && !isFullScreen())
+ return geometry();
+
+ return d->topData()->normalGeometry;
+}
+
+
+/*!
+ \property QWidget::childrenRect
+ \brief the bounding rectangle of the widget's children
+
+ Hidden children are excluded.
+
+ By default, for a widget with no children, this property contains a
+ rectangle with zero width and height located at the origin.
+
+ \sa childrenRegion() geometry()
+*/
+
+QRect QWidget::childrenRect() const
+{
+ Q_D(const QWidget);
+ QRect r(0, 0, 0, 0);
+ for (int i = 0; i < d->children.size(); ++i) {
+ QWidget *w = qobject_cast<QWidget *>(d->children.at(i));
+ if (w && !w->isWindow() && !w->isHidden())
+ r |= w->geometry();
+ }
+ return r;
+}
+
+/*!
+ \property QWidget::childrenRegion
+ \brief the combined region occupied by the widget's children
+
+ Hidden children are excluded.
+
+ By default, for a widget with no children, this property contains an
+ empty region.
+
+ \sa childrenRect() geometry() mask()
+*/
+
+QRegion QWidget::childrenRegion() const
+{
+ Q_D(const QWidget);
+ QRegion r;
+ for (int i = 0; i < d->children.size(); ++i) {
+ QWidget *w = qobject_cast<QWidget *>(d->children.at(i));
+ if (w && !w->isWindow() && !w->isHidden()) {
+ QRegion mask = w->mask();
+ if (mask.isEmpty())
+ r |= w->geometry();
+ else
+ r |= mask.translated(w->pos());
+ }
+ }
+ return r;
+}
+
+
+/*!
+ \property QWidget::minimumSize
+ \brief the widget's minimum size
+
+ The widget cannot be resized to a smaller size than the minimum
+ widget size. The widget's size is forced to the minimum size if
+ the current size is smaller.
+
+ The minimum size set by this function will override the minimum size
+ defined by QLayout. In order to unset the minimum size, use a
+ value of \c{QSize(0, 0)}.
+
+ By default, this property contains a size with zero width and height.
+
+ \sa minimumWidth, minimumHeight, maximumSize, sizeIncrement
+*/
+
+QSize QWidget::minimumSize() const
+{
+ Q_D(const QWidget);
+ return d->extra ? QSize(d->extra->minw, d->extra->minh) : QSize(0, 0);
+}
+
+/*!
+ \property QWidget::maximumSize
+ \brief the widget's maximum size in pixels
+
+ The widget cannot be resized to a larger size than the maximum
+ widget size.
+
+ By default, this property contains a size in which both width and height
+ have values of 16777215.
+
+ \note The definition of the \c QWIDGETSIZE_MAX macro limits the maximum size
+ of widgets.
+
+ \sa maximumWidth, maximumHeight, minimumSize, sizeIncrement
+*/
+
+QSize QWidget::maximumSize() const
+{
+ Q_D(const QWidget);
+ return d->extra ? QSize(d->extra->maxw, d->extra->maxh)
+ : QSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX);
+}
+
+
+/*!
+ \property QWidget::minimumWidth
+ \brief the widget's minimum width in pixels
+
+ This property corresponds to the width held by the \l minimumSize property.
+
+ By default, this property has a value of 0.
+
+ \sa minimumSize, minimumHeight
+*/
+
+/*!
+ \property QWidget::minimumHeight
+ \brief the widget's minimum height in pixels
+
+ This property corresponds to the height held by the \l minimumSize property.
+
+ By default, this property has a value of 0.
+
+ \sa minimumSize, minimumWidth
+*/
+
+/*!
+ \property QWidget::maximumWidth
+ \brief the widget's maximum width in pixels
+
+ This property corresponds to the width held by the \l maximumSize property.
+
+ By default, this property contains a value of 16777215.
+
+ \note The definition of the \c QWIDGETSIZE_MAX macro limits the maximum size
+ of widgets.
+
+ \sa maximumSize, maximumHeight
+*/
+
+/*!
+ \property QWidget::maximumHeight
+ \brief the widget's maximum height in pixels
+
+ This property corresponds to the height held by the \l maximumSize property.
+
+ By default, this property contains a value of 16777215.
+
+ \note The definition of the \c QWIDGETSIZE_MAX macro limits the maximum size
+ of widgets.
+
+ \sa maximumSize, maximumWidth
+*/
+
+/*!
+ \property QWidget::sizeIncrement
+ \brief the size increment of the widget
+
+ When the user resizes the window, the size will move in steps of
+ sizeIncrement().width() pixels horizontally and
+ sizeIncrement.height() pixels vertically, with baseSize() as the
+ basis. Preferred widget sizes are for non-negative integers \e i
+ and \e j:
+ \snippet doc/src/snippets/code/src_gui_kernel_qwidget.cpp 2
+
+ Note that while you can set the size increment for all widgets, it
+ only affects windows.
+
+ By default, this property contains a size with zero width and height.
+
+ \warning The size increment has no effect under Windows, and may
+ be disregarded by the window manager on X11.
+
+ \sa size, minimumSize, maximumSize
+*/
+QSize QWidget::sizeIncrement() const
+{
+ Q_D(const QWidget);
+ return (d->extra && d->extra->topextra)
+ ? QSize(d->extra->topextra->incw, d->extra->topextra->inch)
+ : QSize(0, 0);
+}
+
+/*!
+ \property QWidget::baseSize
+ \brief the base size of the widget
+
+ The base size is used to calculate a proper widget size if the
+ widget defines sizeIncrement().
+
+ By default, for a newly-created widget, this property contains a size with
+ zero width and height.
+
+ \sa setSizeIncrement()
+*/
+
+QSize QWidget::baseSize() const
+{
+ Q_D(const QWidget);
+ return (d->extra != 0 && d->extra->topextra != 0)
+ ? QSize(d->extra->topextra->basew, d->extra->topextra->baseh)
+ : QSize(0, 0);
+}
+
+bool QWidgetPrivate::setMinimumSize_helper(int &minw, int &minh)
+{
+ Q_Q(QWidget);
+
+#ifdef Q_WS_QWS
+ if (q->isWindow()) {
+ const QRect maxWindowRect = QApplication::desktop()->availableGeometry(QApplication::desktop()->screenNumber(q));
+ if (!maxWindowRect.isEmpty()) {
+ // ### This is really just a work-around. Layout shouldn't be
+ // asking for minimum sizes bigger than the screen.
+ if (minw > maxWindowRect.width())
+ minw = maxWindowRect.width();
+ if (minh > maxWindowRect.height())
+ minh = maxWindowRect.height();
+ }
+ }
+#endif
+ if (minw > QWIDGETSIZE_MAX || minh > QWIDGETSIZE_MAX) {
+ qWarning("QWidget::setMinimumSize: (%s/%s) "
+ "The largest allowed size is (%d,%d)",
+ q->objectName().toLocal8Bit().data(), q->metaObject()->className(), QWIDGETSIZE_MAX,
+ QWIDGETSIZE_MAX);
+ minw = qMin<int>(minw, QWIDGETSIZE_MAX);
+ minh = qMin<int>(minh, QWIDGETSIZE_MAX);
+ }
+ if (minw < 0 || minh < 0) {
+ qWarning("QWidget::setMinimumSize: (%s/%s) Negative sizes (%d,%d) "
+ "are not possible",
+ q->objectName().toLocal8Bit().data(), q->metaObject()->className(), minw, minh);
+ minw = qMax(minw, 0);
+ minh = qMax(minh, 0);
+ }
+ createExtra();
+ if (extra->minw == minw && extra->minh == minh)
+ return false;
+ extra->minw = minw;
+ extra->minh = minh;
+ extra->explicitMinSize = (minw ? Qt::Horizontal : 0) | (minh ? Qt::Vertical : 0);
+ return true;
+}
+
+/*!
+ \overload
+
+ This function corresponds to setMinimumSize(QSize(minw, minh)).
+ Sets the minimum width to \a minw and the minimum height to \a
+ minh.
+*/
+
+void QWidget::setMinimumSize(int minw, int minh)
+{
+ Q_D(QWidget);
+ if (!d->setMinimumSize_helper(minw, minh))
+ return;
+
+ if (isWindow())
+ d->setConstraints_sys();
+ if (minw > width() || minh > height()) {
+ bool resized = testAttribute(Qt::WA_Resized);
+ bool maximized = isMaximized();
+ resize(qMax(minw,width()), qMax(minh,height()));
+ setAttribute(Qt::WA_Resized, resized); //not a user resize
+ if (maximized)
+ data->window_state = data->window_state | Qt::WindowMaximized;
+ }
+#ifndef QT_NO_GRAPHICSVIEW
+ if (d->extra) {
+ if (d->extra->proxyWidget)
+ d->extra->proxyWidget->setMinimumSize(minw, minh);
+ }
+#endif
+ d->updateGeometry_helper(d->extra->minw == d->extra->maxw && d->extra->minh == d->extra->maxh);
+}
+
+bool QWidgetPrivate::setMaximumSize_helper(int &maxw, int &maxh)
+{
+ Q_Q(QWidget);
+ if (maxw > QWIDGETSIZE_MAX || maxh > QWIDGETSIZE_MAX) {
+ qWarning("QWidget::setMaximumSize: (%s/%s) "
+ "The largest allowed size is (%d,%d)",
+ q->objectName().toLocal8Bit().data(), q->metaObject()->className(), QWIDGETSIZE_MAX,
+ QWIDGETSIZE_MAX);
+ maxw = qMin<int>(maxw, QWIDGETSIZE_MAX);
+ maxh = qMin<int>(maxh, QWIDGETSIZE_MAX);
+ }
+ if (maxw < 0 || maxh < 0) {
+ qWarning("QWidget::setMaximumSize: (%s/%s) Negative sizes (%d,%d) "
+ "are not possible",
+ q->objectName().toLocal8Bit().data(), q->metaObject()->className(), maxw, maxh);
+ maxw = qMax(maxw, 0);
+ maxh = qMax(maxh, 0);
+ }
+ createExtra();
+ if (extra->maxw == maxw && extra->maxh == maxh)
+ return false;
+ extra->maxw = maxw;
+ extra->maxh = maxh;
+ extra->explicitMaxSize = (maxw != QWIDGETSIZE_MAX ? Qt::Horizontal : 0) | (maxh != QWIDGETSIZE_MAX ? Qt::Vertical : 0);
+ return true;
+}
+
+/*!
+ \overload
+
+ This function corresponds to setMaximumSize(QSize(\a maxw, \a
+ maxh)). Sets the maximum width to \a maxw and the maximum height
+ to \a maxh.
+*/
+void QWidget::setMaximumSize(int maxw, int maxh)
+{
+ Q_D(QWidget);
+ if (!d->setMaximumSize_helper(maxw, maxh))
+ return;
+
+ if (isWindow())
+ d->setConstraints_sys();
+ if (maxw < width() || maxh < height()) {
+ bool resized = testAttribute(Qt::WA_Resized);
+ resize(qMin(maxw,width()), qMin(maxh,height()));
+ setAttribute(Qt::WA_Resized, resized); //not a user resize
+ }
+
+#ifndef QT_NO_GRAPHICSVIEW
+ if (d->extra) {
+ if (d->extra->proxyWidget)
+ d->extra->proxyWidget->setMaximumSize(maxw, maxh);
+ }
+#endif
+
+ d->updateGeometry_helper(d->extra->minw == d->extra->maxw && d->extra->minh == d->extra->maxh);
+}
+
+/*!
+ \overload
+
+ Sets the x (width) size increment to \a w and the y (height) size
+ increment to \a h.
+*/
+void QWidget::setSizeIncrement(int w, int h)
+{
+ Q_D(QWidget);
+ d->createTLExtra();
+ QTLWExtra* x = d->topData();
+ if (x->incw == w && x->inch == h)
+ return;
+ x->incw = w;
+ x->inch = h;
+ if (isWindow())
+ d->setConstraints_sys();
+}
+
+/*!
+ \overload
+
+ This corresponds to setBaseSize(QSize(\a basew, \a baseh)). Sets
+ the widgets base size to width \a basew and height \a baseh.
+*/
+void QWidget::setBaseSize(int basew, int baseh)
+{
+ Q_D(QWidget);
+ d->createTLExtra();
+ QTLWExtra* x = d->topData();
+ if (x->basew == basew && x->baseh == baseh)
+ return;
+ x->basew = basew;
+ x->baseh = baseh;
+ if (isWindow())
+ d->setConstraints_sys();
+}
+
+/*!
+ Sets both the minimum and maximum sizes of the widget to \a s,
+ thereby preventing it from ever growing or shrinking.
+
+ This will override the default size constraints set by QLayout.
+
+ Alternatively, if you want the widget to have a
+ fixed size based on its contents, you can call
+ QLayout::setSizeConstraint(QLayout::SetFixedSize);
+
+ \sa maximumSize, minimumSize
+*/
+
+void QWidget::setFixedSize(const QSize & s)
+{
+ setFixedSize(s.width(), s.height());
+}
+
+
+/*!
+ \fn void QWidget::setFixedSize(int w, int h)
+ \overload
+
+ Sets the width of the widget to \a w and the height to \a h.
+*/
+
+void QWidget::setFixedSize(int w, int h)
+{
+ Q_D(QWidget);
+#ifdef Q_WS_QWS
+ // temporary fix for 4.3.x.
+ // Should move the embedded spesific contraints in setMinimumSize_helper into QLayout
+ int tmpW = w;
+ int tmpH = h;
+ bool minSizeSet = d->setMinimumSize_helper(tmpW, tmpH);
+#else
+ bool minSizeSet = d->setMinimumSize_helper(w, h);
+#endif
+ bool maxSizeSet = d->setMaximumSize_helper(w, h);
+ if (!minSizeSet && !maxSizeSet)
+ return;
+
+ if (isWindow())
+ d->setConstraints_sys();
+ else
+ d->updateGeometry_helper(true);
+
+ resize(w, h);
+}
+
+void QWidget::setMinimumWidth(int w)
+{
+ Q_D(QWidget);
+ d->createExtra();
+ uint expl = d->extra->explicitMinSize | (w ? Qt::Horizontal : 0);
+ setMinimumSize(w, minimumSize().height());
+ d->extra->explicitMinSize = expl;
+}
+
+void QWidget::setMinimumHeight(int h)
+{
+ Q_D(QWidget);
+ d->createExtra();
+ uint expl = d->extra->explicitMinSize | (h ? Qt::Vertical : 0);
+ setMinimumSize(minimumSize().width(), h);
+ d->extra->explicitMinSize = expl;
+}
+
+void QWidget::setMaximumWidth(int w)
+{
+ Q_D(QWidget);
+ d->createExtra();
+ uint expl = d->extra->explicitMaxSize | (w == QWIDGETSIZE_MAX ? 0 : Qt::Horizontal);
+ setMaximumSize(w, maximumSize().height());
+ d->extra->explicitMaxSize = expl;
+}
+
+void QWidget::setMaximumHeight(int h)
+{
+ Q_D(QWidget);
+ d->createExtra();
+ uint expl = d->extra->explicitMaxSize | (h == QWIDGETSIZE_MAX ? 0 : Qt::Vertical);
+ setMaximumSize(maximumSize().width(), h);
+ d->extra->explicitMaxSize = expl;
+}
+
+/*!
+ Sets both the minimum and maximum width of the widget to \a w
+ without changing the heights. Provided for convenience.
+
+ \sa sizeHint() minimumSize() maximumSize() setFixedSize()
+*/
+
+void QWidget::setFixedWidth(int w)
+{
+ Q_D(QWidget);
+ d->createExtra();
+ uint explMin = d->extra->explicitMinSize | Qt::Horizontal;
+ uint explMax = d->extra->explicitMaxSize | Qt::Horizontal;
+ setMinimumSize(w, minimumSize().height());
+ setMaximumSize(w, maximumSize().height());
+ d->extra->explicitMinSize = explMin;
+ d->extra->explicitMaxSize = explMax;
+}
+
+
+/*!
+ Sets both the minimum and maximum heights of the widget to \a h
+ without changing the widths. Provided for convenience.
+
+ \sa sizeHint() minimumSize() maximumSize() setFixedSize()
+*/
+
+void QWidget::setFixedHeight(int h)
+{
+ Q_D(QWidget);
+ d->createExtra();
+ uint explMin = d->extra->explicitMinSize | Qt::Vertical;
+ uint explMax = d->extra->explicitMaxSize | Qt::Vertical;
+ setMinimumSize(minimumSize().width(), h);
+ setMaximumSize(maximumSize().width(), h);
+ d->extra->explicitMinSize = explMin;
+ d->extra->explicitMaxSize = explMax;
+}
+
+
+/*!
+ Translates the widget coordinate \a pos to the coordinate system
+ of \a parent. The \a parent must not be 0 and must be a parent
+ of the calling widget.
+
+ \sa mapFrom() mapToParent() mapToGlobal() underMouse()
+*/
+
+QPoint QWidget::mapTo(QWidget * parent, const QPoint & pos) const
+{
+ QPoint p = pos;
+ if (parent) {
+ const QWidget * w = this;
+ while (w != parent) {
+ Q_ASSERT_X(w, "QWidget::mapTo(QWidget *parent, const QPoint &pos)",
+ "parent must be in parent hierarchy");
+ p = w->mapToParent(p);
+ w = w->parentWidget();
+ }
+ }
+ return p;
+}
+
+
+/*!
+ Translates the widget coordinate \a pos from the coordinate system
+ of \a parent to this widget's coordinate system. The \a parent
+ must not be 0 and must be a parent of the calling widget.
+
+ \sa mapTo() mapFromParent() mapFromGlobal() underMouse()
+*/
+
+QPoint QWidget::mapFrom(QWidget * parent, const QPoint & pos) const
+{
+ QPoint p(pos);
+ if (parent) {
+ const QWidget * w = this;
+ while (w != parent) {
+ Q_ASSERT_X(w, "QWidget::mapFrom(QWidget *parent, const QPoint &pos)",
+ "parent must be in parent hierarchy");
+
+ p = w->mapFromParent(p);
+ w = w->parentWidget();
+ }
+ }
+ return p;
+}
+
+
+/*!
+ Translates the widget coordinate \a pos to a coordinate in the
+ parent widget.
+
+ Same as mapToGlobal() if the widget has no parent.
+
+ \sa mapFromParent() mapTo() mapToGlobal() underMouse()
+*/
+
+QPoint QWidget::mapToParent(const QPoint &pos) const
+{
+ return pos + data->crect.topLeft();
+}
+
+/*!
+ Translates the parent widget coordinate \a pos to widget
+ coordinates.
+
+ Same as mapFromGlobal() if the widget has no parent.
+
+ \sa mapToParent() mapFrom() mapFromGlobal() underMouse()
+*/
+
+QPoint QWidget::mapFromParent(const QPoint &pos) const
+{
+ return pos - data->crect.topLeft();
+}
+
+
+/*!
+ Returns the window for this widget, i.e. the next ancestor widget
+ that has (or could have) a window-system frame.
+
+ If the widget is a window, the widget itself is returned.
+
+ Typical usage is changing the window title:
+
+ \snippet doc/src/snippets/code/src_gui_kernel_qwidget.cpp 3
+
+ \sa isWindow()
+*/
+
+QWidget *QWidget::window() const
+{
+ QWidget *w = (QWidget *)this;
+ QWidget *p = w->parentWidget();
+ while (!w->isWindow() && p) {
+ w = p;
+ p = p->parentWidget();
+ }
+ return w;
+}
+
+/*!
+ \since 4.4
+
+ Returns the native parent for this widget, i.e. the next ancestor widget
+ that has a system identifier, or 0 if it does not have any native parent.
+
+ \sa effectiveWinId()
+*/
+QWidget *QWidget::nativeParentWidget() const
+{
+ QWidget *parent = parentWidget();
+ while (parent && !parent->internalWinId())
+ parent = parent->parentWidget();
+ return parent;
+}
+
+/*! \fn QWidget *QWidget::topLevelWidget() const
+ \obsolete
+
+ Use window() instead.
+*/
+
+#ifdef QT3_SUPPORT
+/*!
+ Returns the color role used for painting the widget's background.
+
+ Use QPalette(backgroundRole(()) instead.
+*/
+Qt::BackgroundMode QWidget::backgroundMode() const
+{
+ if (testAttribute(Qt::WA_NoSystemBackground))
+ return Qt::NoBackground;
+ switch(backgroundRole()) {
+ case QPalette::WindowText:
+ return Qt::PaletteForeground;
+ case QPalette::Button:
+ return Qt::PaletteButton;
+ case QPalette::Light:
+ return Qt::PaletteLight;
+ case QPalette::Midlight:
+ return Qt::PaletteMidlight;
+ case QPalette::Dark:
+ return Qt::PaletteDark;
+ case QPalette::Mid:
+ return Qt::PaletteMid;
+ case QPalette::Text:
+ return Qt::PaletteText;
+ case QPalette::BrightText:
+ return Qt::PaletteBrightText;
+ case QPalette::Base:
+ return Qt::PaletteBase;
+ case QPalette::Window:
+ return Qt::PaletteBackground;
+ case QPalette::Shadow:
+ return Qt::PaletteShadow;
+ case QPalette::Highlight:
+ return Qt::PaletteHighlight;
+ case QPalette::HighlightedText:
+ return Qt::PaletteHighlightedText;
+ case QPalette::ButtonText:
+ return Qt::PaletteButtonText;
+ case QPalette::Link:
+ return Qt::PaletteLink;
+ case QPalette::LinkVisited:
+ return Qt::PaletteLinkVisited;
+ default:
+ break;
+ }
+ return Qt::NoBackground;
+}
+
+/*!
+ \fn void QWidget::setBackgroundMode(Qt::BackgroundMode
+ widgetBackground, Qt::BackgroundMode paletteBackground)
+
+ Sets the color role used for painting the widget's background to
+ background mode \a widgetBackground. The \a paletteBackground mode
+ parameter is ignored.
+*/
+void QWidget::setBackgroundMode(Qt::BackgroundMode m, Qt::BackgroundMode)
+{
+ Q_D(QWidget);
+ if(m == Qt::NoBackground) {
+ setAttribute(Qt::WA_NoSystemBackground, true);
+ return;
+ }
+ setAttribute(Qt::WA_NoSystemBackground, false);
+ d->fg_role = QPalette::NoRole;
+ QPalette::ColorRole role = d->bg_role;
+ switch(m) {
+ case Qt::FixedColor:
+ case Qt::FixedPixmap:
+ break;
+ case Qt::PaletteForeground:
+ role = QPalette::WindowText;
+ break;
+ case Qt::PaletteButton:
+ role = QPalette::Button;
+ break;
+ case Qt::PaletteLight:
+ role = QPalette::Light;
+ break;
+ case Qt::PaletteMidlight:
+ role = QPalette::Midlight;
+ break;
+ case Qt::PaletteDark:
+ role = QPalette::Dark;
+ break;
+ case Qt::PaletteMid:
+ role = QPalette::Mid;
+ break;
+ case Qt::PaletteText:
+ role = QPalette::Text;
+ break;
+ case Qt::PaletteBrightText:
+ role = QPalette::BrightText;
+ break;
+ case Qt::PaletteBase:
+ role = QPalette::Base;
+ break;
+ case Qt::PaletteBackground:
+ role = QPalette::Window;
+ break;
+ case Qt::PaletteShadow:
+ role = QPalette::Shadow;
+ break;
+ case Qt::PaletteHighlight:
+ role = QPalette::Highlight;
+ break;
+ case Qt::PaletteHighlightedText:
+ role = QPalette::HighlightedText;
+ break;
+ case Qt::PaletteButtonText:
+ role = QPalette::ButtonText;
+ break;
+ case Qt::PaletteLink:
+ role = QPalette::Link;
+ break;
+ case Qt::PaletteLinkVisited:
+ role = QPalette::LinkVisited;
+ break;
+ case Qt::X11ParentRelative:
+ d->fg_role = role = QPalette::NoRole;
+ default:
+ break;
+ }
+ setBackgroundRole(role);
+}
+
+/*!
+ The widget mapper is no longer part of the public API.
+*/
+QT3_SUPPORT QWidgetMapper *QWidget::wmapper() { return QWidgetPrivate::mapper; }
+
+#endif
+
+
+/*!
+ Returns the background role of the widget.
+
+ The background role defines the brush from the widget's \l palette that
+ is used to render the background.
+
+ If no explicit background role is set, the widget inherts its parent
+ widget's background role.
+
+ \sa setBackgroundRole(), foregroundRole()
+ */
+QPalette::ColorRole QWidget::backgroundRole() const
+{
+
+ const QWidget *w = this;
+ do {
+ QPalette::ColorRole role = w->d_func()->bg_role;
+ if (role != QPalette::NoRole)
+ return role;
+ if (w->isWindow() || w->windowType() == Qt::SubWindow)
+ break;
+ w = w->parentWidget();
+ } while (w);
+ return QPalette::Window;
+}
+
+/*!
+ Sets the background role of the widget to \a role.
+
+ The background role defines the brush from the widget's \l palette that
+ is used to render the background.
+
+ If \a role is QPalette::NoRole, then the widget inherits its
+ parent's background role.
+
+ \sa backgroundRole(), foregroundRole()
+ */
+
+void QWidget::setBackgroundRole(QPalette::ColorRole role)
+{
+ Q_D(QWidget);
+ d->bg_role = role;
+ d->updateSystemBackground();
+ d->propagatePaletteChange();
+ d->updateIsOpaque();
+}
+
+/*!
+ Returns the foreground role.
+
+ The foreground role defines the color from the widget's \l palette that
+ is used to draw the foreground.
+
+ If no explicit foreground role is set, the function returns a role
+ that contrasts with the background role.
+
+ \sa setForegroundRole(), backgroundRole()
+ */
+QPalette::ColorRole QWidget::foregroundRole() const
+{
+ Q_D(const QWidget);
+ QPalette::ColorRole rl = QPalette::ColorRole(d->fg_role);
+ if (rl != QPalette::NoRole)
+ return rl;
+ QPalette::ColorRole role = QPalette::WindowText;
+ switch (backgroundRole()) {
+ case QPalette::Button:
+ role = QPalette::ButtonText;
+ break;
+ case QPalette::Base:
+ role = QPalette::Text;
+ break;
+ case QPalette::Dark:
+ case QPalette::Shadow:
+ role = QPalette::Light;
+ break;
+ case QPalette::Highlight:
+ role = QPalette::HighlightedText;
+ break;
+ case QPalette::ToolTipBase:
+ role = QPalette::ToolTipText;
+ break;
+ default:
+ ;
+ }
+ return role;
+}
+
+/*!
+ Sets the foreground role of the widget to \a role.
+
+ The foreground role defines the color from the widget's \l palette that
+ is used to draw the foreground.
+
+ If \a role is QPalette::NoRole, the widget uses a foreground role
+ that contrasts with the background role.
+
+ \sa foregroundRole(), backgroundRole()
+ */
+void QWidget::setForegroundRole(QPalette::ColorRole role)
+{
+ Q_D(QWidget);
+ d->fg_role = role;
+ d->updateSystemBackground();
+ d->propagatePaletteChange();
+}
+
+/*!
+ \property QWidget::palette
+ \brief the widget's palette
+
+ This property describes the widget's palette. The palette is used by the
+ widget's style when rendering standard components, and is available as a
+ means to ensure that custom widgets can maintain consistency with the
+ native platform's look and feel. It's common that different platforms, or
+ different styles, have different palettes.
+
+ When you assign a new palette to a widget, the color roles from this
+ palette are combined with the widget's default palette to form the
+ widget's final palette. The palette entry for the widget's background role
+ is used to fill the widget's background (see QWidget::autoFillBackground),
+ and the foreground role initializes QPainter's pen.
+
+ The default depends on the system environment. QApplication maintains a
+ system/theme palette which serves as a default for all widgets. There may
+ also be special palette defaults for certain types of widgets (e.g., on
+ Windows XP and Vista, all classes that derive from QMenuBar have a special
+ default palette). You can also define default palettes for widgets
+ yourself by passing a custom palette and the name of a widget to
+ QApplication::setPalette(). Finally, the style always has the option of
+ polishing the palette as it's assigned (see QStyle::polish()).
+
+ QWidget propagates explicit palette roles from parent to child. If you
+ assign a brush or color to a specific role on a palette and assign that
+ palette to a widget, that role will propagate to all the widget's
+ children, overriding any system defaults for that role. Note that palettes
+ by default don't propagate to windows (see isWindow()) unless the
+ Qt::WA_WindowPropagation attribute is enabled.
+
+ QWidget's palette propagation is similar to its font propagation.
+
+ The current style, which is used to render the content of all standard Qt
+ widgets, is free to choose colors and brushes from the widget palette, or
+ in some cases, to ignore the palette (partially, or completely). In
+ particular, certain styles like GTK style, Mac style, Windows XP, and
+ Vista style, depend on third party APIs to render the content of widgets,
+ and these styles typically do not follow the palette. Because of this,
+ assigning roles to a widget's palette is not guaranteed to change the
+ appearance of the widget. Instead, you may choose to apply a \l
+ styleSheet. You can refer to our Knowledge Base article
+ \l{http://qtsoftware.com/developer/knowledgebase/22}{here} for more
+ information.
+
+ \warning Do not use this function in conjunction with \l{Qt Style Sheets}.
+ When using style sheets, the palette of a widget can be customized using
+ the "color", "background-color", "selection-color",
+ "selection-background-color" and "alternate-background-color".
+
+ \sa QApplication::palette(), QWidget::font()
+*/
+const QPalette &QWidget::palette() const
+{
+ if (!isEnabled()) {
+ data->pal.setCurrentColorGroup(QPalette::Disabled);
+ } else if ((!isVisible() || isActiveWindow())
+#if defined(Q_OS_WIN) && !defined(Q_OS_WINCE)
+ && !QApplicationPrivate::isBlockedByModal(const_cast<QWidget *>(this))
+#endif
+ ) {
+ data->pal.setCurrentColorGroup(QPalette::Active);
+ } else {
+#ifdef Q_WS_MAC
+ extern bool qt_mac_can_clickThrough(const QWidget *); //qwidget_mac.cpp
+ if (qt_mac_can_clickThrough(this))
+ data->pal.setCurrentColorGroup(QPalette::Active);
+ else
+#endif
+ data->pal.setCurrentColorGroup(QPalette::Inactive);
+ }
+ return data->pal;
+}
+
+void QWidget::setPalette(const QPalette &palette)
+{
+ Q_D(QWidget);
+ setAttribute(Qt::WA_SetPalette, palette.resolve() != 0);
+
+ // Determine which palette is inherited from this widget's ancestors and
+ // QApplication::palette, resolve this against \a palette (attributes from
+ // the inherited palette are copied over this widget's palette). Then
+ // propagate this palette to this widget's children.
+ QPalette naturalPalette = d->naturalWidgetPalette(d->inheritedPaletteResolveMask);
+ QPalette resolvedPalette = palette.resolve(naturalPalette);
+ d->setPalette_helper(resolvedPalette);
+}
+
+/*!
+ \internal
+
+ Returns the palette that the widget \a w inherits from its ancestors and
+ QApplication::palette. \a inheritedMask is the combination of the widget's
+ ancestors palette request masks (i.e., which attributes from the parent
+ widget's palette are implicitly imposed on this widget by the user). Note
+ that this font does not take into account the palette set on \a w itself.
+*/
+QPalette QWidgetPrivate::naturalWidgetPalette(uint inheritedMask) const
+{
+ Q_Q(const QWidget);
+ QPalette naturalPalette = QApplication::palette(q);
+ if (!q->testAttribute(Qt::WA_StyleSheet)
+ && (!q->isWindow() || q->testAttribute(Qt::WA_WindowPropagation) || (extra && extra->proxyWidget))) {
+ if (QWidget *p = q->parentWidget()) {
+ if (!p->testAttribute(Qt::WA_StyleSheet)) {
+ if (!naturalPalette.isCopyOf(QApplication::palette())) {
+ QPalette inheritedPalette = p->palette();
+ inheritedPalette.resolve(inheritedMask);
+ naturalPalette = inheritedPalette.resolve(naturalPalette);
+ } else {
+ naturalPalette = p->palette();
+ }
+ }
+ } else if (extra && extra->proxyWidget) {
+#ifndef QT_NO_GRAPHICSVIEW
+ QPalette inheritedPalette = extra->proxyWidget->palette();
+ inheritedPalette.resolve(inheritedMask);
+ naturalPalette = inheritedPalette.resolve(naturalPalette);
+#endif //QT_NO_GRAPHICSVIEW
+ }
+ }
+ naturalPalette.resolve(0);
+ return naturalPalette;
+}
+/*!
+ \internal
+
+ Determine which palette is inherited from this widget's ancestors and
+ QApplication::palette, resolve this against this widget's palette
+ (attributes from the inherited palette are copied over this widget's
+ palette). Then propagate this palette to this widget's children.
+*/
+void QWidgetPrivate::resolvePalette()
+{
+ QPalette naturalPalette = naturalWidgetPalette(inheritedPaletteResolveMask);
+ QPalette resolvedPalette = data.pal.resolve(naturalPalette);
+ setPalette_helper(resolvedPalette);
+}
+
+void QWidgetPrivate::setPalette_helper(const QPalette &palette)
+{
+ Q_Q(QWidget);
+ if (data.pal == palette && data.pal.resolve() == palette.resolve())
+ return;
+ data.pal = palette;
+ updateSystemBackground();
+ propagatePaletteChange();
+ updateIsOpaque();
+ q->update();
+ updateIsOpaque();
+}
+
+/*!
+ \property QWidget::font
+ \brief the font currently set for the widget
+
+ This property describes the widget's requested font. The font is used by
+ the widget's style when rendering standard components, and is available as
+ a means to ensure that custom widgets can maintain consistency with the
+ native platform's look and feel. It's common that different platforms, or
+ different styles, define different fonts for an application.
+
+ When you assign a new font to a widget, the properties from this font are
+ combined with the widget's default font to form the widget's final
+ font. You can call fontInfo() to get a copy of the widget's final
+ font. The final font is also used to initialize QPainter's font.
+
+ The default depends on the system environment. QApplication maintains a
+ system/theme font which serves as a default for all widgets. There may
+ also be special font defaults for certain types of widgets. You can also
+ define default fonts for widgets yourself by passing a custom font and the
+ name of a widget to QApplication::setFont(). Finally, the font is matched
+ against Qt's font database to find the best match.
+
+ QWidget propagates explicit font properties from parent to child. If you
+ change a specific property on a font and assign that font to a widget,
+ that property will propagate to all the widget's children, overriding any
+ system defaults for that property. Note that fonts by default don't
+ propagate to windows (see isWindow()) unless the Qt::WA_WindowPropagation
+ attribute is enabled.
+
+ QWidget's font propagation is similar to its palette propagation.
+
+ The current style, which is used to render the content of all standard Qt
+ widgets, is free to choose to use the widget font, or in some cases, to
+ ignore it (partially, or completely). In particular, certain styles like
+ GTK style, Mac style, Windows XP, and Vista style, apply special
+ modifications to the widget font to match the platform's native look and
+ feel. Because of this, assigning properties to a widget's font is not
+ guaranteed to change the appearance of the widget. Instead, you may choose
+ to apply a \l styleSheet.
+
+ \note If \l{Qt Style Sheets} are used on the same widget as setFont(),
+ style sheets will take precedence if the settings conflict.
+
+ \sa fontInfo(), fontMetrics()
+*/
+
+void QWidget::setFont(const QFont &font)
+{
+ Q_D(QWidget);
+
+#ifndef QT_NO_STYLE_STYLESHEET
+ const QStyleSheetStyle* style;
+ if (d->extra && (style = qobject_cast<const QStyleSheetStyle*>(d->extra->style))) {
+ style->saveWidgetFont(this, font);
+ }
+#endif
+
+ setAttribute(Qt::WA_SetFont, font.resolve() != 0);
+
+ // Determine which font is inherited from this widget's ancestors and
+ // QApplication::font, resolve this against \a font (attributes from the
+ // inherited font are copied over). Then propagate this font to this
+ // widget's children.
+ QFont naturalFont = d->naturalWidgetFont(d->inheritedFontResolveMask);
+ QFont resolvedFont = font.resolve(naturalFont);
+ d->setFont_helper(resolvedFont);
+}
+
+/*
+ \internal
+
+ Returns the font that the widget \a w inherits from its ancestors and
+ QApplication::font. \a inheritedMask is the combination of the widget's
+ ancestors font request masks (i.e., which attributes from the parent
+ widget's font are implicitly imposed on this widget by the user). Note
+ that this font does not take into account the font set on \a w itself.
+
+ ### Stylesheet has a different font propagation mechanism. When a stylesheet
+ is applied, fonts are not propagated anymore
+*/
+QFont QWidgetPrivate::naturalWidgetFont(uint inheritedMask) const
+{
+ Q_Q(const QWidget);
+ QFont naturalFont = QApplication::font(q);
+ if (!q->testAttribute(Qt::WA_StyleSheet)
+ && (!q->isWindow() || q->testAttribute(Qt::WA_WindowPropagation) || (extra && extra->proxyWidget))) {
+ if (QWidget *p = q->parentWidget()) {
+ if (!p->testAttribute(Qt::WA_StyleSheet)) {
+ if (!naturalFont.isCopyOf(QApplication::font())) {
+ QFont inheritedFont = p->font();
+ inheritedFont.resolve(inheritedMask);
+ naturalFont = inheritedFont.resolve(naturalFont);
+ } else {
+ naturalFont = p->font();
+ }
+ }
+ } else if (extra && extra->proxyWidget) {
+#ifndef QT_NO_GRAPHICSVIEW
+ QFont inheritedFont = extra->proxyWidget->font();
+ inheritedFont.resolve(inheritedMask);
+ naturalFont = inheritedFont.resolve(naturalFont);
+#endif //QT_NO_GRAPHICSVIEW
+ }
+ }
+ naturalFont.resolve(0);
+ return naturalFont;
+}
+
+/*!
+ \internal
+
+ Determine which font is implicitly imposed on this widget by its ancestors
+ and QApplication::font, resolve this against its own font (attributes from
+ the implicit font are copied over). Then propagate this font to this
+ widget's children.
+*/
+void QWidgetPrivate::resolveFont()
+{
+ QFont naturalFont = naturalWidgetFont(inheritedFontResolveMask);
+ QFont resolvedFont = data.fnt.resolve(naturalFont);
+ setFont_helper(resolvedFont);
+}
+
+/*!
+ \internal
+
+ Assign \a font to this widget, and propagate it to all children, except
+ style sheet widgets (handled differently) and windows that don't enable
+ window propagation. \a implicitMask is the union of all ancestor widgets'
+ font request masks, and determines which attributes from this widget's
+ font should propagate.
+*/
+void QWidgetPrivate::updateFont(const QFont &font)
+{
+ Q_Q(QWidget);
+#ifndef QT_NO_STYLE_STYLESHEET
+ const QStyleSheetStyle* cssStyle;
+ cssStyle = extra ? qobject_cast<const QStyleSheetStyle*>(extra->style) : 0;
+#endif
+
+#ifdef QT3_SUPPORT
+ QFont old = data.fnt;
+#endif
+ data.fnt = QFont(font, q);
+#if defined(Q_WS_X11)
+ // make sure the font set on this widget is associated with the correct screen
+ data.fnt.x11SetScreen(xinfo.screen());
+#endif
+ // Combine new mask with natural mask and propagate to children.
+ if (!q->parentWidget() && extra && extra->proxyWidget) {
+#ifndef QT_NO_GRAPHICSVIEW
+ QGraphicsProxyWidget *p = extra->proxyWidget;
+ inheritedFontResolveMask = p->d_func()->inheritedFontResolveMask | p->font().resolve();
+#endif //QT_NO_GRAPHICSVIEW
+ } else if (q->isWindow() && !q->testAttribute(Qt::WA_WindowPropagation)) {
+ inheritedFontResolveMask = 0;
+ }
+ uint newMask = data.fnt.resolve() | inheritedFontResolveMask;
+
+ for (int i = 0; i < children.size(); ++i) {
+ QWidget *w = qobject_cast<QWidget*>(children.at(i));
+ if (w) {
+ if (0) {
+#ifndef QT_NO_STYLE_STYLESHEET
+ } else if (w->testAttribute(Qt::WA_StyleSheet)) {
+ // Style sheets follow a different font propagation scheme.
+ if (cssStyle)
+ cssStyle->updateStyleSheetFont(w);
+#endif
+ } else if ((!w->isWindow() || w->testAttribute(Qt::WA_WindowPropagation))) {
+ // Propagate font changes.
+ QWidgetPrivate *wd = w->d_func();
+ wd->inheritedFontResolveMask = newMask;
+ wd->resolveFont();
+ }
+ }
+ }
+
+#ifndef QT_NO_STYLE_STYLESHEET
+ if (cssStyle) {
+ cssStyle->updateStyleSheetFont(q);
+ }
+#endif
+
+ QEvent e(QEvent::FontChange);
+ QApplication::sendEvent(q, &e);
+#ifdef QT3_SUPPORT
+ q->fontChange(old);
+#endif
+}
+
+void QWidgetPrivate::setLayoutDirection_helper(Qt::LayoutDirection direction)
+{
+ Q_Q(QWidget);
+
+ if ( (direction == Qt::RightToLeft) == q->testAttribute(Qt::WA_RightToLeft))
+ return;
+ q->setAttribute(Qt::WA_RightToLeft, (direction == Qt::RightToLeft));
+ if (!children.isEmpty()) {
+ for (int i = 0; i < children.size(); ++i) {
+ QWidget *w = qobject_cast<QWidget*>(children.at(i));
+ if (w && !w->isWindow() && !w->testAttribute(Qt::WA_SetLayoutDirection))
+ w->d_func()->setLayoutDirection_helper(direction);
+ }
+ }
+ QEvent e(QEvent::LayoutDirectionChange);
+ QApplication::sendEvent(q, &e);
+}
+
+void QWidgetPrivate::resolveLayoutDirection()
+{
+ Q_Q(const QWidget);
+ if (!q->testAttribute(Qt::WA_SetLayoutDirection))
+ setLayoutDirection_helper(q->isWindow() ? QApplication::layoutDirection() : q->parentWidget()->layoutDirection());
+}
+
+/*!
+ \property QWidget::layoutDirection
+
+ \brief the layout direction for this widget
+
+ By default, this property is set to Qt::LeftToRight.
+
+ When the layout direction is set on a widget, it will propagate to
+ the widget's children. Children added after the call to \c
+ setLayoutDirection() will not inherit the parent's layout
+ direction.
+
+ \sa QApplication::layoutDirection
+*/
+void QWidget::setLayoutDirection(Qt::LayoutDirection direction)
+{
+ Q_D(QWidget);
+
+ setAttribute(Qt::WA_SetLayoutDirection);
+ d->setLayoutDirection_helper(direction);
+}
+
+Qt::LayoutDirection QWidget::layoutDirection() const
+{
+ return testAttribute(Qt::WA_RightToLeft) ? Qt::RightToLeft : Qt::LeftToRight;
+}
+
+void QWidget::unsetLayoutDirection()
+{
+ Q_D(QWidget);
+ setAttribute(Qt::WA_SetLayoutDirection, false);
+ d->resolveLayoutDirection();
+}
+
+/*!
+ \fn QFontMetrics QWidget::fontMetrics() const
+
+ Returns the font metrics for the widget's current font.
+ Equivalent to QFontMetrics(widget->font()).
+
+ \sa font(), fontInfo(), setFont()
+*/
+
+/*!
+ \fn QFontInfo QWidget::fontInfo() const
+
+ Returns the font info for the widget's current font.
+ Equivalent to QFontInto(widget->font()).
+
+ \sa font(), fontMetrics(), setFont()
+*/
+
+
+/*!
+ \property QWidget::cursor
+ \brief the cursor shape for this widget
+
+ The mouse cursor will assume this shape when it's over this
+ widget. See the \link Qt::CursorShape list of predefined cursor
+ objects\endlink for a range of useful shapes.
+
+ An editor widget might use an I-beam cursor:
+ \snippet doc/src/snippets/code/src_gui_kernel_qwidget.cpp 6
+
+ If no cursor has been set, or after a call to unsetCursor(), the
+ parent's cursor is used.
+
+ By default, this property contains a cursor with the Qt::ArrowCursor
+ shape.
+
+ \sa QApplication::setOverrideCursor()
+*/
+
+#ifndef QT_NO_CURSOR
+QCursor QWidget::cursor() const
+{
+ Q_D(const QWidget);
+ if (testAttribute(Qt::WA_SetCursor))
+ return (d->extra && d->extra->curs)
+ ? *d->extra->curs
+ : QCursor(Qt::ArrowCursor);
+ if (isWindow() || !parentWidget())
+ return QCursor(Qt::ArrowCursor);
+ return parentWidget()->cursor();
+}
+
+void QWidget::setCursor(const QCursor &cursor)
+{
+ Q_D(QWidget);
+// On Mac we must set the cursor even if it is the ArrowCursor.
+#if !defined(Q_WS_MAC) && !defined(Q_WS_QWS)
+ if (cursor.shape() != Qt::ArrowCursor
+ || (d->extra && d->extra->curs))
+#endif
+ {
+ d->createExtra();
+ delete d->extra->curs;
+ d->extra->curs = new QCursor(cursor);
+ }
+ setAttribute(Qt::WA_SetCursor);
+ d->setCursor_sys(cursor);
+
+ QEvent event(QEvent::CursorChange);
+ QApplication::sendEvent(this, &event);
+}
+
+void QWidget::unsetCursor()
+{
+ Q_D(QWidget);
+ if (d->extra) {
+ delete d->extra->curs;
+ d->extra->curs = 0;
+ }
+ if (!isWindow())
+ setAttribute(Qt::WA_SetCursor, false);
+ d->unsetCursor_sys();
+
+ QEvent event(QEvent::CursorChange);
+ QApplication::sendEvent(this, &event);
+}
+
+#endif
+
+/*!
+ \enum QWidget::RenderFlag
+
+ This enum describes how to render the widget when calling QWidget::render().
+
+ \value DrawWindowBackground If you enable this option, the widget's background
+ is rendered into the target even if autoFillBackground is not set. By default,
+ this option is enabled.
+
+ \value DrawChildren If you enable this option, the widget's children
+ are rendered recursively into the target. By default, this option is enabled.
+
+ \value IgnoreMask If you enable this option, the widget's QWidget::mask()
+ is ignored when rendering into the target. By default, this option is disabled.
+
+ \since 4.3
+*/
+
+/*!
+ \since 4.3
+
+ Renders the \a sourceRegion of this widget into the \a target
+ using \a renderFlags to determine how to render. Rendering
+ starts at \a targetOffset in the \a target. For example:
+
+ \snippet doc/src/snippets/code/src_gui_kernel_qwidget.cpp 7
+
+ If \a sourceRegion is a null region, this function will use QWidget::rect() as
+ the region, i.e. the entire widget.
+
+ Ensure that you call QPainter::end() for the \a target device's
+ active painter (if any) before rendering. For example:
+
+ \snippet doc/src/snippets/code/src_gui_kernel_qwidget.cpp 8
+
+ \note To obtain the contents of an OpenGL widget, use QGLWidget::grabFrameBuffer()
+ or QGLWidget::renderPixmap() instead.
+*/
+void QWidget::render(QPaintDevice *target, const QPoint &targetOffset,
+ const QRegion &sourceRegion, RenderFlags renderFlags)
+{
+ Q_D(QWidget);
+ if (!target) {
+ qWarning("QWidget::render: null pointer to paint device");
+ return;
+ }
+
+ const bool inRenderWithPainter = d->extra && d->extra->inRenderWithPainter;
+ QRegion paintRegion = !inRenderWithPainter ? d->prepareToRender(sourceRegion, renderFlags)
+ : sourceRegion;
+ if (paintRegion.isEmpty())
+ return;
+
+#ifndef Q_WS_MAC
+ QPainter *oldSharedPainter = inRenderWithPainter ? d->sharedPainter() : 0;
+
+ // Use the target's shared painter if set (typically set when doing
+ // "other->render(widget);" in the widget's paintEvent.
+ if (target->devType() == QInternal::Widget) {
+ QWidgetPrivate *targetPrivate = static_cast<QWidget *>(target)->d_func();
+ if (targetPrivate->extra && targetPrivate->extra->inRenderWithPainter) {
+ QPainter *targetPainter = targetPrivate->sharedPainter();
+ if (targetPainter && targetPainter->isActive())
+ d->setSharedPainter(targetPainter);
+ }
+ }
+#endif
+
+ // Use the target's redirected device if set and adjust offset and paint
+ // region accordingly. This is typically the case when people call render
+ // from the paintEvent.
+ QPoint offset = targetOffset;
+ offset -= paintRegion.boundingRect().topLeft();
+ QPoint redirectionOffset;
+ QPaintDevice *redirected = 0;
+
+ if (target->devType() == QInternal::Widget)
+ redirected = static_cast<QWidget *>(target)->d_func()->redirected(&redirectionOffset);
+ if (!redirected)
+ redirected = QPainter::redirected(target, &redirectionOffset);
+
+ if (redirected) {
+ target = redirected;
+ offset -= redirectionOffset;
+ if (!inRenderWithPainter) { // Clip handled by shared painter (in qpainter.cpp).
+ const QRegion redirectedSystemClip = redirected->paintEngine()->systemClip();
+ if (!redirectedSystemClip.isEmpty())
+ paintRegion &= redirectedSystemClip.translated(-offset);
+ }
+ }
+
+ // Set backingstore flags.
+ int flags = QWidgetPrivate::DrawPaintOnScreen | QWidgetPrivate::DrawInvisible;
+ if (renderFlags & DrawWindowBackground)
+ flags |= QWidgetPrivate::DrawAsRoot;
+
+ if (renderFlags & DrawChildren)
+ flags |= QWidgetPrivate::DrawRecursive;
+ else
+ flags |= QWidgetPrivate::DontSubtractOpaqueChildren;
+
+#ifdef Q_WS_QWS
+ flags |= QWidgetPrivate::DontSetCompositionMode;
+#endif
+
+ if (target->devType() == QInternal::Printer) {
+ QPainter p(target);
+ d->render_helper(&p, targetOffset, paintRegion, renderFlags);
+ return;
+ }
+
+#ifndef Q_WS_MAC
+ // Render via backingstore.
+ d->drawWidget(target, paintRegion, offset, flags, d->sharedPainter());
+
+ // Restore shared painter.
+ if (oldSharedPainter)
+ d->setSharedPainter(oldSharedPainter);
+#else
+ // Render via backingstore (no shared painter).
+ d->drawWidget(target, paintRegion, offset, flags, 0);
+#endif
+}
+
+/*!
+ \overload
+
+ Renders the widget into the \a painter's QPainter::device().
+
+ Transformations and settings applied to the \a painter will be used
+ when rendering.
+
+ \note The \a painter must be active. On Mac OS X the widget will be
+ rendered into a QPixmap and then drawn by the \a painter.
+
+ \sa QPainter::device()
+*/
+void QWidget::render(QPainter *painter, const QPoint &targetOffset,
+ const QRegion &sourceRegion, RenderFlags renderFlags)
+{
+ if (!painter) {
+ qWarning("QWidget::render: Null pointer to painter");
+ return;
+ }
+
+ if (!painter->isActive()) {
+ qWarning("QWidget::render: Cannot render with an inactive painter");
+ return;
+ }
+
+ const qreal opacity = painter->opacity();
+ if (qFuzzyCompare(opacity + 1, qreal(1.0)))
+ return; // Fully transparent.
+
+ Q_D(QWidget);
+ const bool inRenderWithPainter = d->extra && d->extra->inRenderWithPainter;
+ const QRegion toBePainted = !inRenderWithPainter ? d->prepareToRender(sourceRegion, renderFlags)
+ : sourceRegion;
+ if (toBePainted.isEmpty())
+ return;
+
+ if (!d->extra)
+ d->createExtra();
+ d->extra->inRenderWithPainter = true;
+
+#ifdef Q_WS_MAC
+ d->render_helper(painter, targetOffset, toBePainted, renderFlags);
+#else
+ QPaintEngine *engine = painter->paintEngine();
+ Q_ASSERT(engine);
+ QPaintEnginePrivate *enginePriv = engine->d_func();
+ Q_ASSERT(enginePriv);
+ QPaintDevice *target = painter->worldMatrixEnabled() ? engine->paintDevice() : painter->device();
+ Q_ASSERT(target);
+
+ // Render via a pixmap when dealing with non-opaque painters or printers.
+ if (!inRenderWithPainter && (opacity < 1.0 || (target->devType() == QInternal::Printer))) {
+ d->render_helper(painter, targetOffset, toBePainted, renderFlags);
+ d->extra->inRenderWithPainter = false;
+ return;
+ }
+
+ // Set new shared painter.
+ QPainter *oldPainter = d->sharedPainter();
+ d->setSharedPainter(painter);
+
+ // Save current system clip, viewport and transform,
+ const QTransform oldTransform = enginePriv->systemTransform;
+ const QRegion oldSystemClip = enginePriv->systemClip;
+ const QRegion oldSystemViewport = enginePriv->systemViewport;
+
+ // This ensures that transformed system clips are inside the current system clip.
+ enginePriv->setSystemViewport(oldSystemClip);
+
+ render(target, targetOffset, toBePainted, renderFlags);
+
+ // Restore system clip, viewport and transform.
+ enginePriv->systemClip = oldSystemClip;
+ enginePriv->setSystemViewport(oldSystemViewport);
+ enginePriv->setSystemTransform(oldTransform);
+
+ // Restore shared painter.
+ d->setSharedPainter(oldPainter);
+#endif
+
+ d->extra->inRenderWithPainter = false;
+}
+
+bool QWidgetPrivate::isAboutToShow() const
+{
+ if (data.in_show)
+ return true;
+
+ Q_Q(const QWidget);
+ if (q->isHidden())
+ return false;
+
+ // The widget will be shown if any of its ancestors are about to show.
+ QWidget *parent = q->parentWidget();
+ return parent ? parent->d_func()->isAboutToShow() : false;
+}
+
+QRegion QWidgetPrivate::prepareToRender(const QRegion &region, QWidget::RenderFlags renderFlags)
+{
+ Q_Q(QWidget);
+ const bool isVisible = q->isVisible();
+
+ // Make sure the widget is laid out correctly.
+ if (!isVisible && !isAboutToShow()) {
+ QWidget *topLevel = q->window();
+ (void)topLevel->d_func()->topData(); // Make sure we at least have top-data.
+ topLevel->ensurePolished();
+
+ // Invalidate the layout of hidden ancestors (incl. myself) and pretend
+ // they're not explicitly hidden.
+ QWidget *widget = q;
+ QWidgetList hiddenWidgets;
+ while (widget) {
+ if (widget->isHidden()) {
+ widget->setAttribute(Qt::WA_WState_Hidden, false);
+ hiddenWidgets.append(widget);
+ if (!widget->isWindow() && widget->parentWidget()->d_func()->layout)
+ widget->d_func()->updateGeometry_helper(true);
+ }
+ widget = widget->parentWidget();
+ }
+
+ // Activate top-level layout.
+ if (topLevel->d_func()->layout)
+ topLevel->d_func()->layout->activate();
+
+ // Adjust size if necessary.
+ QTLWExtra *topLevelExtra = topLevel->d_func()->maybeTopData();
+ if (topLevelExtra && !topLevelExtra->sizeAdjusted
+ && !topLevel->testAttribute(Qt::WA_Resized)) {
+ topLevel->adjustSize();
+ topLevel->setAttribute(Qt::WA_Resized, false);
+ }
+
+ // Activate child layouts.
+ topLevel->d_func()->activateChildLayoutsRecursively();
+
+ // We're not cheating with WA_WState_Hidden anymore.
+ for (int i = 0; i < hiddenWidgets.size(); ++i) {
+ QWidget *widget = hiddenWidgets.at(i);
+ widget->setAttribute(Qt::WA_WState_Hidden);
+ if (!widget->isWindow() && widget->parentWidget()->d_func()->layout)
+ widget->parentWidget()->d_func()->layout->invalidate();
+ }
+ } else if (isVisible) {
+ q->window()->d_func()->sendPendingMoveAndResizeEvents(true, true);
+ }
+
+ // Calculate the region to be painted.
+ QRegion toBePainted = !region.isEmpty() ? region : QRegion(q->rect());
+ if (!(renderFlags & QWidget::IgnoreMask) && extra && extra->hasMask)
+ toBePainted &= extra->mask;
+ return toBePainted;
+}
+
+void QWidgetPrivate::render_helper(QPainter *painter, const QPoint &targetOffset, const QRegion &toBePainted,
+ QWidget::RenderFlags renderFlags)
+{
+ Q_ASSERT(painter);
+ Q_ASSERT(!toBePainted.isEmpty());
+
+ Q_Q(QWidget);
+#ifndef Q_WS_MAC
+ const QTransform originalTransform = painter->worldTransform();
+ const bool useDeviceCoordinates = originalTransform.isScaling();
+ if (!useDeviceCoordinates) {
+#endif
+ // Render via a pixmap.
+ const QRect rect = toBePainted.boundingRect();
+ const QSize size = rect.size();
+ if (size.isNull())
+ return;
+
+ QPixmap pixmap(size);
+ if (!(renderFlags & QWidget::DrawWindowBackground))
+ pixmap.fill(Qt::transparent);
+ q->render(&pixmap, QPoint(), toBePainted, renderFlags);
+
+ const bool restore = !(painter->renderHints() & QPainter::SmoothPixmapTransform);
+ painter->setRenderHints(QPainter::SmoothPixmapTransform, true);
+
+ painter->drawPixmap(targetOffset, pixmap);
+
+ if (restore)
+ painter->setRenderHints(QPainter::SmoothPixmapTransform, false);
+
+#ifndef Q_WS_MAC
+ } else {
+ // Render via a pixmap in device coordinates (to avoid pixmap scaling).
+ QTransform transform = originalTransform;
+ transform.translate(targetOffset.x(), targetOffset.y());
+
+ QPaintDevice *device = painter->device();
+ Q_ASSERT(device);
+
+ // Calculate device rect.
+ const QRectF rect(toBePainted.boundingRect());
+ QRect deviceRect = transform.mapRect(QRectF(0, 0, rect.width(), rect.height())).toAlignedRect();
+ deviceRect &= QRect(0, 0, device->width(), device->height());
+
+ QPixmap pixmap(deviceRect.size());
+ pixmap.fill(Qt::transparent);
+
+ // Create a pixmap device coordinate painter.
+ QPainter pixmapPainter(&pixmap);
+ pixmapPainter.setRenderHints(painter->renderHints());
+ transform *= QTransform::fromTranslate(-deviceRect.x(), -deviceRect.y());
+ pixmapPainter.setTransform(transform);
+
+ q->render(&pixmapPainter, QPoint(), toBePainted, renderFlags);
+ pixmapPainter.end();
+
+ // And then draw the pixmap.
+ painter->setTransform(QTransform());
+ painter->drawPixmap(deviceRect.topLeft(), pixmap);
+ painter->setTransform(originalTransform);
+ }
+#endif
+}
+
+void QWidgetPrivate::drawWidget(QPaintDevice *pdev, const QRegion &rgn, const QPoint &offset, int flags,
+ QPainter *sharedPainter, QWidgetBackingStore *backingStore)
+{
+ if (rgn.isEmpty())
+ return;
+
+ Q_Q(QWidget);
+ const bool asRoot = flags & DrawAsRoot;
+ const bool alsoOnScreen = flags & DrawPaintOnScreen;
+ const bool recursive = flags & DrawRecursive;
+ const bool alsoInvisible = flags & DrawInvisible;
+
+ Q_ASSERT(sharedPainter ? sharedPainter->isActive() : true);
+
+ QRegion toBePainted(rgn);
+ if (asRoot && !alsoInvisible)
+ toBePainted &= clipRect(); //(rgn & visibleRegion());
+ if (!(flags & DontSubtractOpaqueChildren))
+ subtractOpaqueChildren(toBePainted, q->rect());
+
+ if (!toBePainted.isEmpty()) {
+ bool onScreen = paintOnScreen();
+ if (!onScreen || alsoOnScreen) {
+ //update the "in paint event" flag
+ if (q->testAttribute(Qt::WA_WState_InPaintEvent))
+ qWarning("QWidget::repaint: Recursive repaint detected");
+ q->setAttribute(Qt::WA_WState_InPaintEvent);
+
+ //clip away the new area
+#ifndef QT_NO_PAINT_DEBUG
+ bool flushed = QWidgetBackingStore::flushPaint(q, toBePainted);
+#endif
+ QPaintEngine *paintEngine = pdev->paintEngine();
+ if (paintEngine) {
+ setRedirected(pdev, -offset);
+
+ if (sharedPainter)
+ paintEngine->d_func()->systemClip = toBePainted;
+ else
+ paintEngine->setSystemRect(q->data->crect);
+
+ //paint the background
+ if ((asRoot || q->autoFillBackground() || onScreen || q->testAttribute(Qt::WA_StyledBackground))
+ && !q->testAttribute(Qt::WA_OpaquePaintEvent) && !q->testAttribute(Qt::WA_NoSystemBackground)) {
+
+ QPainter p(q);
+ QPoint scrollAreaOffset;
+
+#ifndef QT_NO_SCROLLAREA
+ QAbstractScrollArea *scrollArea = qobject_cast<QAbstractScrollArea *>(q->parent());
+ if (scrollArea && scrollArea->viewport() == q) {
+ QObjectData *scrollPrivate = static_cast<QWidget *>(scrollArea)->d_ptr;
+ QAbstractScrollAreaPrivate *priv = static_cast<QAbstractScrollAreaPrivate *>(scrollPrivate);
+ scrollAreaOffset = priv->contentsOffset();
+ p.translate(-scrollAreaOffset);
+ }
+#endif // QT_NO_SCROLLAREA
+
+ paintBackground(&p, toBePainted, scrollAreaOffset, (asRoot || onScreen) ? flags | DrawAsRoot : 0);
+ }
+
+ if (!sharedPainter)
+ paintEngine->d_func()->systemClip = toBePainted.translated(offset);
+
+ if (!onScreen && !asRoot && !isOpaque && q->testAttribute(Qt::WA_TintedBackground)) {
+ QPainter p(q);
+ QColor tint = q->palette().window().color();
+ tint.setAlphaF(qreal(.6));
+ p.fillRect(toBePainted.boundingRect(), tint);
+ }
+ }
+
+#if 0
+ qDebug() << "painting" << q << "opaque ==" << isOpaque();
+ qDebug() << "clipping to" << toBePainted << "location == " << offset
+ << "geometry ==" << QRect(q->mapTo(q->window(), QPoint(0, 0)), q->size());
+#endif
+
+ //actually send the paint event
+ QPaintEvent e(toBePainted);
+ QCoreApplication::sendSpontaneousEvent(q, &e);
+#if !defined(Q_WS_MAC) && !defined(Q_WS_QWS)
+ if (backingStore && !onScreen && !asRoot && (q->internalWinId() || !q->nativeParentWidget()->isWindow()))
+ backingStore->markDirtyOnScreen(toBePainted, q, offset);
+#endif
+
+ //restore
+ if (paintEngine) {
+ restoreRedirected();
+ if (!sharedPainter)
+ paintEngine->setSystemRect(QRect());
+ else
+ paintEngine->d_func()->currentClipWidget = 0;
+ paintEngine->d_func()->systemClip = QRegion();
+ }
+ q->setAttribute(Qt::WA_WState_InPaintEvent, false);
+ if (q->paintingActive() && !q->testAttribute(Qt::WA_PaintOutsidePaintEvent))
+ qWarning("QWidget::repaint: It is dangerous to leave painters active on a widget outside of the PaintEvent");
+
+ if (paintEngine && paintEngine->autoDestruct()) {
+ delete paintEngine;
+ }
+
+#ifndef QT_NO_PAINT_DEBUG
+ if (flushed)
+ QWidgetBackingStore::unflushPaint(q, toBePainted);
+#endif
+ } else if (q->isWindow()) {
+ QPaintEngine *engine = pdev->paintEngine();
+ if (engine) {
+ QPainter p(pdev);
+ p.setClipRegion(toBePainted);
+ const QBrush bg = q->palette().brush(QPalette::Window);
+ if (bg.style() == Qt::TexturePattern)
+ p.drawTiledPixmap(q->rect(), bg.texture());
+ else
+ p.fillRect(q->rect(), bg);
+
+ if (engine->autoDestruct())
+ delete engine;
+ }
+ }
+ }
+
+ if (recursive && !children.isEmpty()) {
+ paintSiblingsRecursive(pdev, children, children.size() - 1, rgn, offset, flags & ~DrawAsRoot
+#ifdef Q_BACKINGSTORE_SUBSURFACES
+ , q->windowSurface()
+#endif
+ , sharedPainter, backingStore);
+ }
+}
+
+void QWidgetPrivate::paintSiblingsRecursive(QPaintDevice *pdev, const QObjectList& siblings, int index, const QRegion &rgn,
+ const QPoint &offset, int flags
+#ifdef Q_BACKINGSTORE_SUBSURFACES
+ , const QWindowSurface *currentSurface
+#endif
+ , QPainter *sharedPainter, QWidgetBackingStore *backingStore)
+{
+ QWidget *w = 0;
+ QRect boundingRect;
+ bool dirtyBoundingRect = true;
+ const bool exludeOpaqueChildren = (flags & DontDrawOpaqueChildren);
+
+ do {
+ QWidget *x = qobject_cast<QWidget*>(siblings.at(index));
+ if (x && !(exludeOpaqueChildren && x->d_func()->isOpaque) && !x->isHidden() && !x->isWindow()) {
+ if (dirtyBoundingRect) {
+ boundingRect = rgn.boundingRect();
+ dirtyBoundingRect = false;
+ }
+
+ if (qRectIntersects(boundingRect, x->data->crect)) {
+#ifdef Q_BACKINGSTORE_SUBSURFACES
+ if (x->windowSurface() == currentSurface)
+#endif
+ {
+ w = x;
+ break;
+ }
+ }
+ }
+ --index;
+ } while (index >= 0);
+
+ if (!w)
+ return;
+
+ QWidgetPrivate *wd = w->d_func();
+ const QPoint widgetPos(w->data->crect.topLeft());
+ const bool hasMask = wd->extra && wd->extra->hasMask;
+
+ if (index > 0) {
+ QRegion wr(rgn);
+ if (wd->isOpaque)
+ wr -= hasMask ? wd->extra->mask.translated(widgetPos) : w->data->crect;
+ paintSiblingsRecursive(pdev, siblings, --index, wr, offset, flags
+#ifdef Q_BACKINGSTORE_SUBSURFACES
+ , currentSurface
+#endif
+ , sharedPainter, backingStore);
+ }
+
+ if (w->updatesEnabled() && (!w->d_func()->extra || !w->d_func()->extra->proxyWidget)) {
+ QRegion wRegion(rgn);
+ wRegion &= w->data->crect;
+ wRegion.translate(-widgetPos);
+ if (hasMask)
+ wRegion &= wd->extra->mask;
+ wd->drawWidget(pdev, wRegion, offset + widgetPos, flags, sharedPainter, backingStore);
+ }
+}
+
+/*!
+ \internal
+
+ Finds the nearest widget embedded in a graphics proxy widget along the chain formed by this
+ widget and its ancestors. The search starts at \a origin (inclusive).
+ If successful, the function returns the proxy that embeds the widget, or 0 if no embedded
+ widget was found.
+*/
+QGraphicsProxyWidget * QWidgetPrivate::nearestGraphicsProxyWidget(QWidget *origin)
+{
+ if (origin) {
+ QWExtra *extra = origin->d_func()->extra;
+ if (extra && extra->proxyWidget)
+ return extra->proxyWidget;
+ return nearestGraphicsProxyWidget(origin->parentWidget());
+ }
+ return 0;
+}
+
+/*!
+ \property QWidget::locale
+ \brief the widget's locale
+ \since 4.3
+
+ As long as no special locale has been set, this is either
+ the parent's locale or (if this widget is a top level widget),
+ the default locale.
+
+ If the widget displays dates or numbers, these should be formatted
+ using the widget's locale.
+
+ \sa QLocale QLocale::setDefault()
+*/
+
+void QWidgetPrivate::setLocale_helper(const QLocale &loc, bool forceUpdate)
+{
+ Q_Q(QWidget);
+ if (locale == loc && !forceUpdate)
+ return;
+
+ locale = loc;
+
+ if (!children.isEmpty()) {
+ for (int i = 0; i < children.size(); ++i) {
+ QWidget *w = qobject_cast<QWidget*>(children.at(i));
+ if (!w)
+ continue;
+ if (w->testAttribute(Qt::WA_SetLocale))
+ continue;
+ if (w->isWindow() && !w->testAttribute(Qt::WA_WindowPropagation))
+ continue;
+ w->d_func()->setLocale_helper(loc, forceUpdate);
+ }
+ }
+ QEvent e(QEvent::LocaleChange);
+ QApplication::sendEvent(q, &e);
+}
+
+void QWidget::setLocale(const QLocale &locale)
+{
+ Q_D(QWidget);
+
+ setAttribute(Qt::WA_SetLocale);
+ d->setLocale_helper(locale);
+}
+
+QLocale QWidget::locale() const
+{
+ Q_D(const QWidget);
+
+ return d->locale;
+}
+
+void QWidgetPrivate::resolveLocale()
+{
+ Q_Q(const QWidget);
+
+ if (!q->testAttribute(Qt::WA_SetLocale)) {
+ setLocale_helper(q->isWindow()
+ ? QLocale()
+ : q->parentWidget()->locale());
+ }
+}
+
+void QWidget::unsetLocale()
+{
+ Q_D(QWidget);
+ setAttribute(Qt::WA_SetLocale, false);
+ d->resolveLocale();
+}
+
+static QString constructWindowTitleFromFilePath(const QString &filePath)
+{
+ QFileInfo fi(filePath);
+ QString windowTitle = fi.fileName() + QLatin1String("[*]");
+#ifndef Q_WS_MAC
+ QString appName = QApplication::applicationName();
+ if (!appName.isEmpty())
+ windowTitle += QLatin1String(" ") + QChar(0x2014) + QLatin1String(" ") + appName;
+#endif
+ return windowTitle;
+}
+
+/*!
+ \property QWidget::windowTitle
+ \brief the window title (caption)
+
+ This property only makes sense for top-level widgets, such as
+ windows and dialogs. If no caption has been set, the title is based of the
+ \l windowFilePath. If neither of these is set, then the title is
+ an empty string.
+
+ If you use the \l windowModified mechanism, the window title must
+ contain a "[*]" placeholder, which indicates where the '*' should
+ appear. Normally, it should appear right after the file name
+ (e.g., "document1.txt[*] - Text Editor"). If the \l
+ windowModified property is false (the default), the placeholder
+ is simply removed.
+
+ \sa windowIcon, windowIconText, windowModified, windowFilePath
+*/
+QString QWidget::windowTitle() const
+{
+ Q_D(const QWidget);
+ if (d->extra && d->extra->topextra) {
+ if (!d->extra->topextra->caption.isEmpty())
+ return d->extra->topextra->caption;
+ if (!d->extra->topextra->filePath.isEmpty())
+ return constructWindowTitleFromFilePath(d->extra->topextra->filePath);
+ }
+ return QString();
+}
+
+QString qt_setWindowTitle_helperHelper(const QString &title, const QWidget *widget)
+{
+ Q_ASSERT(widget);
+
+#ifdef QT_EVAL
+ extern QString qt_eval_adapt_window_title(const QString &title);
+ QString cap = qt_eval_adapt_window_title(title);
+#else
+ QString cap = title;
+#endif
+
+ QString placeHolder(QLatin1String("[*]"));
+
+ int index = cap.indexOf(placeHolder);
+
+ while (index != -1) {
+ index += placeHolder.length();
+ int count = 1;
+ while (cap.indexOf(placeHolder, index) == index) {
+ ++count;
+ index += placeHolder.length();
+ }
+
+ if (count%2) { // odd number of [*] -> replace last one
+ int lastIndex = cap.lastIndexOf(placeHolder, index - 1);
+ if (widget->isWindowModified()
+ && widget->style()->styleHint(QStyle::SH_TitleBar_ModifyNotification, 0, widget))
+ cap.replace(lastIndex, 3, QWidget::tr("*"));
+ else
+ cap.replace(lastIndex, 3, QLatin1String(""));
+ }
+
+ index = cap.indexOf(placeHolder, index);
+ }
+
+ cap.replace(QLatin1String("[*][*]"), QLatin1String("[*]"));
+
+ return cap;
+}
+
+void QWidgetPrivate::setWindowTitle_helper(const QString &title)
+{
+ Q_Q(QWidget);
+ if (!q->testAttribute(Qt::WA_WState_Created))
+ createWinId();
+ setWindowTitle_sys(qt_setWindowTitle_helperHelper(title, q));
+}
+
+void QWidgetPrivate::setWindowIconText_helper(const QString &title)
+{
+ Q_Q(QWidget);
+ if (q->testAttribute(Qt::WA_WState_Created))
+ setWindowIconText_sys(qt_setWindowTitle_helperHelper(title, q));
+}
+
+void QWidget::setWindowIconText(const QString &iconText)
+{
+ if (QWidget::windowIconText() == iconText)
+ return;
+
+ Q_D(QWidget);
+ d->topData()->iconText = iconText;
+ d->setWindowIconText_helper(iconText);
+
+ QEvent e(QEvent::IconTextChange);
+ QApplication::sendEvent(this, &e);
+}
+
+void QWidget::setWindowTitle(const QString &title)
+{
+ if (QWidget::windowTitle() == title)
+ return;
+
+ Q_D(QWidget);
+ d->topData()->caption = title;
+ d->setWindowTitle_helper(title);
+
+ QEvent e(QEvent::WindowTitleChange);
+ QApplication::sendEvent(this, &e);
+}
+
+
+/*!
+ \property QWidget::windowIcon
+ \brief the widget's icon
+
+ This property only makes sense for windows. If no icon
+ has been set, windowIcon() returns the application icon
+ (QApplication::windowIcon()).
+
+ \sa windowIconText, windowTitle
+*/
+QIcon QWidget::windowIcon() const
+{
+ const QWidget *w = this;
+ while (w) {
+ const QWidgetPrivate *d = w->d_func();
+ if (d->extra && d->extra->topextra && d->extra->topextra->icon)
+ return *d->extra->topextra->icon;
+ w = w->parentWidget();
+ }
+ return qApp->windowIcon();
+}
+
+void QWidgetPrivate::setWindowIcon_helper()
+{
+ QEvent e(QEvent::WindowIconChange);
+ QApplication::sendEvent(q_func(), &e);
+ for (int i = 0; i < children.size(); ++i) {
+ QWidget *w = qobject_cast<QWidget *>(children.at(i));
+ if (w && !w->isWindow())
+ QApplication::sendEvent(w, &e);
+ }
+}
+
+void QWidget::setWindowIcon(const QIcon &icon)
+{
+ Q_D(QWidget);
+
+ setAttribute(Qt::WA_SetWindowIcon, !icon.isNull());
+ d->createTLExtra();
+
+ if (!d->extra->topextra->icon)
+ d->extra->topextra->icon = new QIcon();
+ *d->extra->topextra->icon = icon;
+
+ delete d->extra->topextra->iconPixmap;
+ d->extra->topextra->iconPixmap = 0;
+
+ d->setWindowIcon_sys();
+ d->setWindowIcon_helper();
+}
+
+
+/*!
+ \property QWidget::windowIconText
+ \brief the widget's icon text
+
+ This property only makes sense for windows. If no icon
+ text has been set, this functions returns an empty string.
+
+ \sa windowIcon, windowTitle
+*/
+
+QString QWidget::windowIconText() const
+{
+ Q_D(const QWidget);
+ return (d->extra && d->extra->topextra) ? d->extra->topextra->iconText : QString();
+}
+
+/*!
+ \property QWidget::windowFilePath
+ \since 4.4
+ \brief the file path associated with a widget
+
+ This property only makes sense for windows. It associates a file path with
+ a window. If you set the file path, but have not set the window title, Qt
+ sets the window title to contain a string created using the following
+ components.
+
+ On Mac OS X:
+
+ \list
+ \o The file name of the specified path, obtained using QFileInfo::fileName().
+ \o An optional \c{*} character, if the \l windowModified property is set,
+ as per the Apple Human Interface Guidelines.
+ \endlist
+
+ On Windows and X11:
+
+ \list
+ \o The file name of the specified path, obtained using QFileInfo::fileName().
+ \o An optional \c{*} character, if the \l windowModified property is set.
+ \o The \c{0x2014} unicode character, padded either side by spaces.
+ \o The application name, obtained from the application's
+ \l{QCoreApplication::}{applicationName} property.
+ \endlist
+
+ If the window title is set at any point, then the window title takes precedence and
+ will be shown instead of the file path string.
+
+ Additionally, on Mac OS X, this has an added benefit that it sets the
+ \l{http://developer.apple.com/documentation/UserExperience/Conceptual/OSXHIGuidelines/XHIGWindows/chapter_17_section_3.html}{proxy icon}
+ for the window, assuming that the file path exists.
+
+ If no file path is set, this property contains an empty string.
+
+ By default, this property contains an empty string.
+
+ \sa windowTitle, windowIcon
+*/
+
+QString QWidget::windowFilePath() const
+{
+ Q_D(const QWidget);
+ return (d->extra && d->extra->topextra) ? d->extra->topextra->filePath : QString();
+}
+
+void QWidget::setWindowFilePath(const QString &filePath)
+{
+ if (filePath == windowFilePath())
+ return;
+
+ Q_D(QWidget);
+
+ d->createTLExtra();
+ d->extra->topextra->filePath = filePath;
+ d->setWindowFilePath_helper(filePath);
+}
+
+void QWidgetPrivate::setWindowFilePath_helper(const QString &filePath)
+{
+ if (extra->topextra && extra->topextra->caption.isEmpty()) {
+#ifdef Q_WS_MAC
+ setWindowTitle_helper(filePath);
+#else
+ Q_Q(QWidget);
+ Q_UNUSED(filePath);
+ setWindowTitle_helper(q->windowTitle());
+#endif
+ }
+#ifdef Q_WS_MAC
+ setWindowFilePath_sys(filePath);
+#endif
+}
+
+/*!
+ Returns the window's role, or an empty string.
+
+ \sa windowIcon, windowTitle
+*/
+
+QString QWidget::windowRole() const
+{
+ Q_D(const QWidget);
+ return (d->extra && d->extra->topextra) ? d->extra->topextra->role : QString();
+}
+
+/*!
+ Sets the window's role to \a role. This only makes sense for
+ windows on X11.
+*/
+void QWidget::setWindowRole(const QString &role)
+{
+#if defined(Q_WS_X11)
+ Q_D(QWidget);
+ d->topData()->role = role;
+ d->setWindowRole();
+#else
+ Q_UNUSED(role)
+#endif
+}
+
+/*!
+ \property QWidget::mouseTracking
+ \brief whether mouse tracking is enabled for the widget
+
+ If mouse tracking is disabled (the default), the widget only
+ receives mouse move events when at least one mouse button is
+ pressed while the mouse is being moved.
+
+ If mouse tracking is enabled, the widget receives mouse move
+ events even if no buttons are pressed.
+
+ \sa mouseMoveEvent()
+*/
+
+
+/*!
+ Sets the widget's focus proxy to widget \a w. If \a w is 0, the
+ function resets this widget to have no focus proxy.
+
+ Some widgets can "have focus", but create a child widget, such as
+ QLineEdit, to actually handle the focus. In this case, the widget
+ can set the line edit to be its focus proxy.
+
+ setFocusProxy() sets the widget which will actually get focus when
+ "this widget" gets it. If there is a focus proxy, setFocus() and
+ hasFocus() operate on the focus proxy.
+
+ \sa focusProxy()
+*/
+
+void QWidget::setFocusProxy(QWidget * w)
+{
+ Q_D(QWidget);
+ if (!w && !d->extra)
+ return;
+
+ for (QWidget* fp = w; fp; fp = fp->focusProxy()) {
+ if (fp == this) {
+ qWarning("QWidget: %s (%s) already in focus proxy chain", metaObject()->className(), objectName().toLocal8Bit().constData());
+ return;
+ }
+ }
+
+ d->createExtra();
+ d->extra->focus_proxy = w;
+}
+
+
+/*!
+ Returns the focus proxy, or 0 if there is no focus proxy.
+
+ \sa setFocusProxy()
+*/
+
+QWidget * QWidget::focusProxy() const
+{
+ Q_D(const QWidget);
+ return d->extra ? (QWidget *)d->extra->focus_proxy : 0;
+}
+
+
+/*!
+ \property QWidget::focus
+ \brief whether this widget (or its focus proxy) has the keyboard
+ input focus
+
+ By default, this property is false.
+
+ \note Obtaining the value of this property for a widget is effectively equivalent
+ to checking whether QApplication::focusWidget() refers to the widget.
+
+ \sa setFocus(), clearFocus(), setFocusPolicy(), QApplication::focusWidget()
+*/
+bool QWidget::hasFocus() const
+{
+ const QWidget* w = this;
+ while (w->d_func()->extra && w->d_func()->extra->focus_proxy)
+ w = w->d_func()->extra->focus_proxy;
+ if (QWidget *window = w->window()) {
+#ifndef QT_NO_GRAPHICSVIEW
+ QWExtra *e = window->d_func()->extra;
+ if (e && e->proxyWidget && e->proxyWidget->hasFocus() && window->focusWidget() == w)
+ return true;
+#endif
+ }
+ return (QApplication::focusWidget() == w);
+}
+
+/*!
+ Gives the keyboard input focus to this widget (or its focus
+ proxy) if this widget or one of its parents is the \link
+ isActiveWindow() active window\endlink. The \a reason argument will
+ be passed into any focus event sent from this function, it is used
+ to give an explanation of what caused the widget to get focus.
+
+ First, a focus out event is sent to the focus widget (if any) to
+ tell it that it is about to lose the focus. Then a focus in event
+ is sent to this widget to tell it that it just received the focus.
+ (Nothing happens if the focus in and focus out widgets are the
+ same.)
+
+ setFocus() gives focus to a widget regardless of its focus policy,
+ but does not clear any keyboard grab (see grabKeyboard()).
+
+ Be aware that if the widget is hidden, it will not accept focus
+ until it is shown.
+
+ \warning If you call setFocus() in a function which may itself be
+ called from focusOutEvent() or focusInEvent(), you may get an
+ infinite recursion.
+
+ \sa hasFocus(), clearFocus(), focusInEvent(), focusOutEvent(),
+ setFocusPolicy(), QApplication::focusWidget(), grabKeyboard(),
+ grabMouse(), {Keyboard Focus}
+*/
+
+void QWidget::setFocus(Qt::FocusReason reason)
+{
+ if (!isEnabled())
+ return;
+
+ QWidget *f = this;
+ while (f->d_func()->extra && f->d_func()->extra->focus_proxy)
+ f = f->d_func()->extra->focus_proxy;
+
+ if (QApplication::focusWidget() == f
+#if defined(Q_WS_WIN)
+ && GetFocus() == f->internalWinId()
+#endif
+ )
+ return;
+
+#ifndef QT_NO_GRAPHICSVIEW
+ QWidget *previousProxyFocus = 0;
+ if (QWExtra *topData = window()->d_func()->extra) {
+ if (topData->proxyWidget && topData->proxyWidget->hasFocus()) {
+ previousProxyFocus = topData->proxyWidget->widget()->focusWidget();
+ if (previousProxyFocus && previousProxyFocus->focusProxy())
+ previousProxyFocus = previousProxyFocus->focusProxy();
+ }
+ }
+#endif
+
+ QWidget *w = f;
+ if (isHidden()) {
+ while (w && w->isHidden()) {
+ w->d_func()->focus_child = f;
+ w = w->isWindow() ? 0 : w->parentWidget();
+ }
+ } else {
+ while (w) {
+ w->d_func()->focus_child = f;
+ w = w->isWindow() ? 0 : w->parentWidget();
+ }
+ }
+
+#ifndef QT_NO_GRAPHICSVIEW
+ // Update proxy state
+ if (QWExtra *topData = window()->d_func()->extra) {
+ if (topData->proxyWidget && !topData->proxyWidget->hasFocus()) {
+ topData->proxyWidget->d_func()->focusFromWidgetToProxy = 1;
+ topData->proxyWidget->setFocus(reason);
+ topData->proxyWidget->d_func()->focusFromWidgetToProxy = 0;
+ }
+ }
+#endif
+
+ if (f->isActiveWindow()) {
+ QApplicationPrivate::setFocusWidget(f, reason);
+#ifndef QT_NO_ACCESSIBILITY
+# ifdef Q_OS_WIN
+ // The negation of the condition in setFocus_sys
+ if (!(testAttribute(Qt::WA_WState_Created) && window()->windowType() != Qt::Popup && internalWinId()))
+ //setFocusWidget will already post a focus event for us (that the AT client receives) on Windows
+# endif
+ QAccessible::updateAccessibility(f, 0, QAccessible::Focus);
+#endif
+#ifndef QT_NO_GRAPHICSVIEW
+ if (QWExtra *topData = window()->d_func()->extra) {
+ if (topData->proxyWidget) {
+ if (previousProxyFocus && previousProxyFocus != f) {
+ // Send event to self
+ QFocusEvent event(QEvent::FocusOut, reason);
+ QPointer<QWidget> that = previousProxyFocus;
+ QApplication::sendEvent(previousProxyFocus, &event);
+ if (that)
+ QApplication::sendEvent(that->style(), &event);
+ }
+ if (!isHidden()) {
+ // Send event to self
+ QFocusEvent event(QEvent::FocusIn, reason);
+ QPointer<QWidget> that = f;
+ QApplication::sendEvent(f, &event);
+ if (that)
+ QApplication::sendEvent(that->style(), &event);
+ }
+ }
+ }
+#endif
+ }
+}
+
+/*!
+ \fn void QWidget::setFocus()
+ \overload
+
+ Gives the keyboard input focus to this widget (or its focus
+ proxy) if this widget or one of its parents is the
+ \l{isActiveWindow()}{active window}.
+*/
+
+/*!
+ Takes keyboard input focus from the widget.
+
+ If the widget has active focus, a \link focusOutEvent() focus out
+ event\endlink is sent to this widget to tell it that it is about
+ to lose the focus.
+
+ This widget must enable focus setting in order to get the keyboard
+ input focus, i.e. it must call setFocusPolicy().
+
+ \sa hasFocus(), setFocus(), focusInEvent(), focusOutEvent(),
+ setFocusPolicy(), QApplication::focusWidget()
+*/
+
+void QWidget::clearFocus()
+{
+ QWidget *w = this;
+ while (w && w->d_func()->focus_child == this) {
+ w->d_func()->focus_child = 0;
+ w = w->parentWidget();
+ }
+#ifndef QT_NO_GRAPHICSVIEW
+ QWExtra *topData = d_func()->extra;
+ if (topData && topData->proxyWidget)
+ topData->proxyWidget->clearFocus();
+#endif
+
+ if (hasFocus()) {
+ // Update proxy state
+ QApplicationPrivate::setFocusWidget(0, Qt::OtherFocusReason);
+#if defined(Q_WS_WIN)
+ if (!(windowType() == Qt::Popup) && GetFocus() == internalWinId())
+ SetFocus(0);
+ else
+#endif
+ {
+#ifndef QT_NO_ACCESSIBILITY
+ QAccessible::updateAccessibility(this, 0, QAccessible::Focus);
+#endif
+ }
+ }
+}
+
+
+/*!
+ \fn bool QWidget::focusNextChild()
+
+ Finds a new widget to give the keyboard focus to, as appropriate
+ for \key Tab, and returns true if it can find a new widget, or
+ false if it can't.
+
+ \sa focusPreviousChild()
+*/
+
+/*!
+ \fn bool QWidget::focusPreviousChild()
+
+ Finds a new widget to give the keyboard focus to, as appropriate
+ for \key Shift+Tab, and returns true if it can find a new widget,
+ or false if it can't.
+
+ \sa focusNextChild()
+*/
+
+/*!
+ Finds a new widget to give the keyboard focus to, as appropriate
+ for Tab and Shift+Tab, and returns true if it can find a new
+ widget, or false if it can't.
+
+ If \a next is true, this function searches forward, if \a next
+ is false, it searches backward.
+
+ Sometimes, you will want to reimplement this function. For
+ example, a web browser might reimplement it to move its "current
+ active link" forward or backward, and call
+ focusNextPrevChild() only when it reaches the last or
+ first link on the "page".
+
+ Child widgets call focusNextPrevChild() on their parent widgets,
+ but only the window that contains the child widgets decides where
+ to redirect focus. By reimplementing this function for an object,
+ you thus gain control of focus traversal for all child widgets.
+
+ \sa focusNextChild(), focusPreviousChild()
+*/
+
+bool QWidget::focusNextPrevChild(bool next)
+{
+ Q_D(QWidget);
+ QWidget* p = parentWidget();
+ bool isSubWindow = (windowType() == Qt::SubWindow);
+ if (!isWindow() && !isSubWindow && p)
+ return p->focusNextPrevChild(next);
+#ifndef QT_NO_GRAPHICSVIEW
+ if (d->extra && d->extra->proxyWidget)
+ return d->extra->proxyWidget->focusNextPrevChild(next);
+#endif
+ QWidget *w = QApplicationPrivate::focusNextPrevChild_helper(this, next);
+ if (!w) return false;
+
+ w->setFocus(next ? Qt::TabFocusReason : Qt::BacktabFocusReason);
+ return true;
+}
+
+/*!
+ Returns the last child of this widget that setFocus had been
+ called on. For top level widgets this is the widget that will get
+ focus in case this window gets activated
+
+ This is not the same as QApplication::focusWidget(), which returns
+ the focus widget in the currently active window.
+*/
+
+QWidget *QWidget::focusWidget() const
+{
+ return const_cast<QWidget *>(d_func()->focus_child);
+}
+
+/*!
+ Returns the next widget in this widget's focus chain.
+*/
+QWidget *QWidget::nextInFocusChain() const
+{
+ return const_cast<QWidget *>(d_func()->focus_next);
+}
+
+/*!
+ \property QWidget::isActiveWindow
+ \brief whether this widget's window is the active window
+
+ The active window is the window that contains the widget that has
+ keyboard focus (The window may still have focus if it has no
+ widgets or none of its widgets accepts keyboard focus).
+
+ When popup windows are visible, this property is true for both the
+ active window \e and for the popup.
+
+ By default, this property is false.
+
+ \sa activateWindow(), QApplication::activeWindow()
+*/
+bool QWidget::isActiveWindow() const
+{
+ QWidget *tlw = window();
+ if(tlw == qApp->activeWindow() || (isVisible() && (tlw->windowType() == Qt::Popup)))
+ return true;
+
+#ifndef QT_NO_GRAPHICSVIEW
+ if (QWExtra *tlwExtra = tlw->d_func()->extra) {
+ if (isVisible() && tlwExtra->proxyWidget)
+ return tlwExtra->proxyWidget->isActiveWindow();
+ }
+#endif
+
+#ifdef Q_WS_MAC
+ extern bool qt_mac_is_macdrawer(const QWidget *); //qwidget_mac.cpp
+ if(qt_mac_is_macdrawer(tlw) &&
+ tlw->parentWidget() && tlw->parentWidget()->isActiveWindow())
+ return true;
+#endif
+ if(style()->styleHint(QStyle::SH_Widget_ShareActivation, 0, this)) {
+ if(tlw->windowType() == Qt::Tool &&
+ !tlw->isModal() &&
+ (!tlw->parentWidget() || tlw->parentWidget()->isActiveWindow()))
+ return true;
+ QWidget *w = qApp->activeWindow();
+ while(w && tlw->windowType() == Qt::Tool &&
+ !w->isModal() && w->parentWidget()) {
+ w = w->parentWidget()->window();
+ if(w == tlw)
+ return true;
+ }
+ }
+#if defined(Q_WS_WIN32)
+ HWND active = GetActiveWindow();
+ if (!tlw->testAttribute(Qt::WA_WState_Created))
+ return false;
+ return active == tlw->internalWinId() || ::IsChild(active, tlw->internalWinId());
+#else
+ return false;
+#endif
+}
+
+/*!
+ Puts the \a second widget after the \a first widget in the focus order.
+
+ Note that since the tab order of the \a second widget is changed, you
+ should order a chain like this:
+
+ \snippet doc/src/snippets/code/src_gui_kernel_qwidget.cpp 9
+
+ \e not like this:
+
+ \snippet doc/src/snippets/code/src_gui_kernel_qwidget.cpp 10
+
+ If \a first or \a second has a focus proxy, setTabOrder()
+ correctly substitutes the proxy.
+
+ \sa setFocusPolicy(), setFocusProxy(), {Keyboard Focus}
+*/
+void QWidget::setTabOrder(QWidget* first, QWidget *second)
+{
+ if (!first || !second || first->focusPolicy() == Qt::NoFocus || second->focusPolicy() == Qt::NoFocus)
+ return;
+
+ if (first->window() != second->window()) {
+ qWarning("QWidget::setTabOrder: 'first' and 'second' must be in the same window");
+ return;
+ }
+
+ QWidget *fp = first->focusProxy();
+ if (fp) {
+ // If first is redirected, set first to the last child of first
+ // that can take keyboard focus so that second is inserted after
+ // that last child, and the focus order within first is (more
+ // likely to be) preserved.
+ QList<QWidget *> l = qFindChildren<QWidget *>(first);
+ for (int i = l.size()-1; i >= 0; --i) {
+ QWidget * next = l.at(i);
+ if (next->window() == fp->window()) {
+ fp = next;
+ if (fp->focusPolicy() != Qt::NoFocus)
+ break;
+ }
+ }
+ first = fp;
+ }
+
+
+ if (QWidget *sp = second->focusProxy())
+ second = sp;
+
+// QWidget *fp = first->d_func()->focus_prev;
+ QWidget *fn = first->d_func()->focus_next;
+
+ if (fn == second)
+ return;
+
+ QWidget *sp = second->d_func()->focus_prev;
+ QWidget *sn = second->d_func()->focus_next;
+
+ fn->d_func()->focus_prev = second;
+ first->d_func()->focus_next = second;
+
+ second->d_func()->focus_next = fn;
+ second->d_func()->focus_prev = first;
+
+ sp->d_func()->focus_next = sn;
+ sn->d_func()->focus_prev = sp;
+
+
+ Q_ASSERT(first->d_func()->focus_next->d_func()->focus_prev == first);
+ Q_ASSERT(first->d_func()->focus_prev->d_func()->focus_next == first);
+
+ Q_ASSERT(second->d_func()->focus_next->d_func()->focus_prev == second);
+ Q_ASSERT(second->d_func()->focus_prev->d_func()->focus_next == second);
+}
+
+/*!\internal
+
+ Moves the relevant subwidgets of this widget from the \a oldtlw's
+ tab chain to that of the new parent, if there's anything to move and
+ we're really moving
+
+ This function is called from QWidget::reparent() *after* the widget
+ has been reparented.
+
+ \sa reparent()
+*/
+
+void QWidgetPrivate::reparentFocusWidgets(QWidget * oldtlw)
+{
+ Q_Q(QWidget);
+ if (oldtlw == q->window())
+ return; // nothing to do
+
+ if(focus_child)
+ focus_child->clearFocus();
+
+ // separate the focus chain into new (children of myself) and old (the rest)
+ QWidget *firstOld = 0;
+ //QWidget *firstNew = q; //invariant
+ QWidget *o = 0; // last in the old list
+ QWidget *n = q; // last in the new list
+
+ bool prevWasNew = true;
+ QWidget *w = focus_next;
+
+ //Note: for efficiency, we do not maintain the list invariant inside the loop
+ //we append items to the relevant list, and we optimize by not changing pointers
+ //when subsequent items are going into the same list.
+ while (w != q) {
+ bool currentIsNew = q->isAncestorOf(w);
+ if (currentIsNew) {
+ if (!prevWasNew) {
+ //prev was old -- append to new list
+ n->d_func()->focus_next = w;
+ w->d_func()->focus_prev = n;
+ }
+ n = w;
+ } else {
+ if (prevWasNew) {
+ //prev was new -- append to old list, if there is one
+ if (o) {
+ o->d_func()->focus_next = w;
+ w->d_func()->focus_prev = o;
+ } else {
+ // "create" the old list
+ firstOld = w;
+ }
+ }
+ o = w;
+ }
+ w = w->d_func()->focus_next;
+ prevWasNew = currentIsNew;
+ }
+
+ //repair the old list:
+ if (firstOld) {
+ o->d_func()->focus_next = firstOld;
+ firstOld->d_func()->focus_prev = o;
+ }
+
+ if (!q->isWindow()) {
+ QWidget *topLevel = q->window();
+ //insert new chain into toplevel's chain
+
+ QWidget *prev = topLevel->d_func()->focus_prev;
+
+ topLevel->d_func()->focus_prev = n;
+ prev->d_func()->focus_next = q;
+
+ focus_prev = prev;
+ n->d_func()->focus_next = topLevel;
+ } else {
+ //repair the new list
+ n->d_func()->focus_next = q;
+ focus_prev = n;
+ }
+
+}
+
+/*!\internal
+
+ Measures the shortest distance from a point to a rect.
+
+ This function is called from QDesktopwidget::screen(QPoint) to find the
+ closest screen for a point.
+*/
+int QWidgetPrivate::pointToRect(const QPoint &p, const QRect &r)
+{
+ int dx = 0;
+ int dy = 0;
+ if (p.x() < r.left())
+ dx = r.left() - p.x();
+ else if (p.x() > r.right())
+ dx = p.x() - r.right();
+ if (p.y() < r.top())
+ dy = r.top() - p.y();
+ else if (p.y() > r.bottom())
+ dy = p.y() - r.bottom();
+ return dx + dy;
+}
+
+QRect QWidgetPrivate::fromOrToLayoutItemRect(const QRect &rect, int sign) const
+{
+ QRect r = rect;
+ r.adjust(-sign * leftLayoutItemMargin, -sign * topLayoutItemMargin,
+ +sign * rightLayoutItemMargin, +sign * bottomLayoutItemMargin);
+ return r;
+}
+
+/*!
+ \property QWidget::frameSize
+ \brief the size of the widget including any window frame
+
+ By default, this property contains a value that depends on the user's
+ platform and screen geometry.
+*/
+QSize QWidget::frameSize() const
+{
+ Q_D(const QWidget);
+ if (isWindow() && !(windowType() == Qt::Popup)) {
+ QRect fs = d->frameStrut();
+ return QSize(data->crect.width() + fs.left() + fs.right(),
+ data->crect.height() + fs.top() + fs.bottom());
+ }
+ return data->crect.size();
+}
+
+/*! \fn void QWidget::move(int x, int y)
+
+ \overload
+
+ This corresponds to move(QPoint(\a x, \a y)).
+*/
+
+void QWidget::move(const QPoint &p)
+{
+ Q_D(QWidget);
+ setAttribute(Qt::WA_Moved);
+ if (isWindow())
+ d->topData()->posFromMove = true;
+ if (testAttribute(Qt::WA_WState_Created)) {
+ d->setGeometry_sys(p.x() + geometry().x() - QWidget::x(),
+ p.y() + geometry().y() - QWidget::y(),
+ width(), height(), true);
+ d->setDirtyOpaqueRegion();
+ } else {
+ data->crect.moveTopLeft(p); // no frame yet
+ setAttribute(Qt::WA_PendingMoveEvent);
+ }
+}
+
+/*! \fn void QWidget::resize(int w, int h)
+ \overload
+
+ This corresponds to resize(QSize(\a w, \a h)).
+*/
+
+void QWidget::resize(const QSize &s)
+{
+ Q_D(QWidget);
+ setAttribute(Qt::WA_Resized);
+ if (testAttribute(Qt::WA_WState_Created)) {
+ d->setGeometry_sys(geometry().x(), geometry().y(), s.width(), s.height(), false);
+ d->setDirtyOpaqueRegion();
+ } else {
+ data->crect.setSize(s.boundedTo(maximumSize()).expandedTo(minimumSize()));
+ setAttribute(Qt::WA_PendingResizeEvent);
+ }
+}
+
+void QWidget::setGeometry(const QRect &r)
+{
+ Q_D(QWidget);
+ setAttribute(Qt::WA_Resized);
+ setAttribute(Qt::WA_Moved);
+ if (isWindow())
+ d->topData()->posFromMove = false;
+ if (testAttribute(Qt::WA_WState_Created)) {
+ d->setGeometry_sys(r.x(), r.y(), r.width(), r.height(), true);
+ d->setDirtyOpaqueRegion();
+ } else {
+ data->crect.setTopLeft(r.topLeft());
+ data->crect.setSize(r.size().boundedTo(maximumSize()).expandedTo(minimumSize()));
+ setAttribute(Qt::WA_PendingMoveEvent);
+ setAttribute(Qt::WA_PendingResizeEvent);
+ }
+}
+
+/*!
+ \since 4.2
+ Saves the current geometry and state for top-level widgets.
+
+ To save the geometry when the window closes, you can
+ implement a close event like this:
+
+ \snippet doc/src/snippets/code/src_gui_kernel_qwidget.cpp 11
+
+ See the \link geometry.html Window Geometry documentation\endlink
+ for an overview of geometry issues with windows.
+
+ Use QMainWindow::saveState() to save the geometry and the state of
+ toolbars and dock widgets.
+
+ \sa restoreGeometry(), QMainWindow::saveState(), QMainWindow::restoreState()
+*/
+QByteArray QWidget::saveGeometry() const
+{
+ QByteArray array;
+ QDataStream stream(&array, QIODevice::WriteOnly);
+ stream.setVersion(QDataStream::Qt_4_0);
+ const quint32 magicNumber = 0x1D9D0CB;
+ quint16 majorVersion = 1;
+ quint16 minorVersion = 0;
+ stream << magicNumber
+ << majorVersion
+ << minorVersion
+ << frameGeometry()
+ << normalGeometry()
+ << qint32(QApplication::desktop()->screenNumber(this))
+ << quint8(windowState() & Qt::WindowMaximized)
+ << quint8(windowState() & Qt::WindowFullScreen);
+ return array;
+}
+
+/*!
+ \since 4.2
+
+ Restores the geometry and state top-level widgets stored in the
+ byte array \a geometry. Returns true on success; otherwise
+ returns false.
+
+ If the restored geometry is off-screen, it will be modified to be
+ inside the the available screen geometry.
+
+ To restore geometry saved using QSettings, you can use code like
+ this:
+
+ \snippet doc/src/snippets/code/src_gui_kernel_qwidget.cpp 12
+
+ See the \link geometry.html Window Geometry documentation\endlink
+ for an overview of geometry issues with windows.
+
+ Use QMainWindow::restoreState() to restore the geometry and the
+ state of toolbars and dock widgets.
+
+ \sa saveGeometry(), QSettings, QMainWindow::saveState(), QMainWindow::restoreState()
+*/
+bool QWidget::restoreGeometry(const QByteArray &geometry)
+{
+ if (geometry.size() < 4)
+ return false;
+ QDataStream stream(geometry);
+ stream.setVersion(QDataStream::Qt_4_0);
+
+ const quint32 magicNumber = 0x1D9D0CB;
+ quint32 storedMagicNumber;
+ stream >> storedMagicNumber;
+ if (storedMagicNumber != magicNumber)
+ return false;
+
+ const quint16 currentMajorVersion = 1;
+ quint16 majorVersion = 0;
+ quint16 minorVersion = 0;
+
+ stream >> majorVersion >> minorVersion;
+
+ if (majorVersion != currentMajorVersion)
+ return false;
+ // (Allow all minor versions.)
+
+ QRect restoredFrameGeometry;
+ QRect restoredNormalGeometry;
+ qint32 restoredScreenNumber;
+ quint8 maximized;
+ quint8 fullScreen;
+
+ stream >> restoredFrameGeometry
+ >> restoredNormalGeometry
+ >> restoredScreenNumber
+ >> maximized
+ >> fullScreen;
+
+ const int frameHeight = 20;
+ if (!restoredFrameGeometry.isValid())
+ restoredFrameGeometry = QRect(QPoint(0,0), sizeHint());
+
+ if (!restoredNormalGeometry.isValid())
+ restoredNormalGeometry = QRect(QPoint(0, frameHeight), sizeHint());
+ if (!restoredNormalGeometry.isValid()) {
+ // use the widget's adjustedSize if the sizeHint() doesn't help
+ restoredNormalGeometry.setSize(restoredNormalGeometry
+ .size()
+ .expandedTo(d_func()->adjustedSize()));
+ }
+
+ const QDesktopWidget * const desktop = QApplication::desktop();
+ if (restoredScreenNumber >= desktop->numScreens())
+ restoredScreenNumber = desktop->primaryScreen();
+
+ const QRect availableGeometry = desktop->availableGeometry(restoredScreenNumber);
+
+ // Modify the restored geometry if we are about to restore to coordinates
+ // that would make the window "lost". This happens if:
+ // - The restored geometry is completely oustside the available geometry
+ // - The title bar is outside the available geometry.
+ // - (Mac only) The window is higher than the available geometry. It must
+ // be possible to bring the size grip on screen by moving the window.
+#ifdef Q_WS_MAC
+ restoredFrameGeometry.setHeight(qMin(restoredFrameGeometry.height(), availableGeometry.height()));
+ restoredNormalGeometry.setHeight(qMin(restoredNormalGeometry.height(), availableGeometry.height() - frameHeight));
+#endif
+
+ if (!restoredFrameGeometry.intersects(availableGeometry)) {
+ restoredFrameGeometry.moveBottom(qMin(restoredFrameGeometry.bottom(), availableGeometry.bottom()));
+ restoredFrameGeometry.moveLeft(qMax(restoredFrameGeometry.left(), availableGeometry.left()));
+ restoredFrameGeometry.moveRight(qMin(restoredFrameGeometry.right(), availableGeometry.right()));
+ }
+ restoredFrameGeometry.moveTop(qMax(restoredFrameGeometry.top(), availableGeometry.top()));
+
+ if (!restoredNormalGeometry.intersects(availableGeometry)) {
+ restoredNormalGeometry.moveBottom(qMin(restoredNormalGeometry.bottom(), availableGeometry.bottom()));
+ restoredNormalGeometry.moveLeft(qMax(restoredNormalGeometry.left(), availableGeometry.left()));
+ restoredNormalGeometry.moveRight(qMin(restoredNormalGeometry.right(), availableGeometry.right()));
+ }
+ restoredNormalGeometry.moveTop(qMax(restoredNormalGeometry.top(), availableGeometry.top() + frameHeight));
+
+ if (maximized || fullScreen) {
+ // set geomerty before setting the window state to make
+ // sure the window is maximized to the right screen.
+ setGeometry(restoredNormalGeometry);
+ Qt::WindowStates ws = windowState();
+ if (maximized)
+ ws |= Qt::WindowMaximized;
+ if (fullScreen)
+ ws |= Qt::WindowFullScreen;
+ setWindowState(ws);
+ d_func()->topData()->normalGeometry = restoredNormalGeometry;
+ } else {
+ QPoint offset;
+#ifdef Q_WS_X11
+ if (isFullScreen())
+ offset = d_func()->topData()->fullScreenOffset;
+#endif
+ setWindowState(windowState() & ~(Qt::WindowMaximized | Qt::WindowFullScreen));
+ move(restoredFrameGeometry.topLeft() + offset);
+ resize(restoredNormalGeometry.size());
+ }
+ return true;
+}
+
+/*!\fn void QWidget::setGeometry(int x, int y, int w, int h)
+ \overload
+
+ This corresponds to setGeometry(QRect(\a x, \a y, \a w, \a h)).
+*/
+
+/*!
+ Sets the margins around the contents of the widget to have the
+ sizes \a left, \a top, \a right, and \a bottom. The margins are
+ used by the layout system, and may be used by subclasses to
+ specify the area to draw in (e.g. excluding the frame).
+
+ Changing the margins will trigger a resizeEvent().
+
+ \sa contentsRect(), getContentsMargins()
+*/
+void QWidget::setContentsMargins(int left, int top, int right, int bottom)
+{
+ Q_D(QWidget);
+ if (left == d->leftmargin && top == d->topmargin
+ && right == d->rightmargin && bottom == d->bottommargin)
+ return;
+ d->leftmargin = left;
+ d->topmargin = top;
+ d->rightmargin = right;
+ d->bottommargin = bottom;
+
+ if (QLayout *l=d->layout)
+ l->update(); //force activate; will do updateGeometry
+ else
+ updateGeometry();
+
+ // ### Qt 5: compat, remove
+ if (isVisible()) {
+ update();
+ QResizeEvent e(data->crect.size(), data->crect.size());
+ QApplication::sendEvent(this, &e);
+ } else {
+ setAttribute(Qt::WA_PendingResizeEvent, true);
+ }
+
+ QEvent e(QEvent::ContentsRectChange);
+ QApplication::sendEvent(this, &e);
+}
+
+/*! Returns the widget's contents margins for \a left, \a top, \a
+ right, and \a bottom.
+
+ \sa setContentsMargins(), contentsRect()
+ */
+void QWidget::getContentsMargins(int *left, int *top, int *right, int *bottom) const
+{
+ Q_D(const QWidget);
+ if (left)
+ *left = d->leftmargin;
+ if (top)
+ *top = d->topmargin;
+ if (right)
+ *right = d->rightmargin;
+ if (bottom)
+ *bottom = d->bottommargin;
+}
+
+/*!
+ Returns the area inside the widget's margins.
+
+ \sa setContentsMargins(), getContentsMargins()
+*/
+QRect QWidget::contentsRect() const
+{
+ Q_D(const QWidget);
+ return QRect(QPoint(d->leftmargin, d->topmargin),
+ QPoint(data->crect.width() - 1 - d->rightmargin,
+ data->crect.height() - 1 - d->bottommargin));
+
+}
+
+
+
+/*!
+ \fn void QWidget::customContextMenuRequested(const QPoint &pos)
+
+ This signal is emitted when the widget's \l contextMenuPolicy is
+ Qt::CustomContextMenu, and the user has requested a context menu on
+ the widget. The position \a pos is the position of the context menu
+ event that the widget receives. Normally this is in widget
+ coordinates. The exception to this rule is QAbstractScrollArea and
+ its subclasses that map the context menu event to coordinates of the
+ \link QAbstractScrollArea::viewport() viewport() \endlink .
+
+
+ \sa mapToGlobal() QMenu contextMenuPolicy
+*/
+
+
+/*!
+ \property QWidget::contextMenuPolicy
+ \brief how the widget shows a context menu
+
+ The default value of this property is Qt::DefaultContextMenu,
+ which means the contextMenuEvent() handler is called. Other values
+ are Qt::NoContextMenu, Qt::PreventContextMenu,
+ Qt::ActionsContextMenu, and Qt::CustomContextMenu. With
+ Qt::CustomContextMenu, the signal customContextMenuRequested() is
+ emitted.
+
+ \sa contextMenuEvent(), customContextMenuRequested(), actions()
+*/
+
+Qt::ContextMenuPolicy QWidget::contextMenuPolicy() const
+{
+ return (Qt::ContextMenuPolicy)data->context_menu_policy;
+}
+
+void QWidget::setContextMenuPolicy(Qt::ContextMenuPolicy policy)
+{
+ data->context_menu_policy = (uint) policy;
+}
+
+/*!
+ \property QWidget::focusPolicy
+ \brief the way the widget accepts keyboard focus
+
+ The policy is Qt::TabFocus if the widget accepts keyboard
+ focus by tabbing, Qt::ClickFocus if the widget accepts
+ focus by clicking, Qt::StrongFocus if it accepts both, and
+ Qt::NoFocus (the default) if it does not accept focus at
+ all.
+
+ You must enable keyboard focus for a widget if it processes
+ keyboard events. This is normally done from the widget's
+ constructor. For instance, the QLineEdit constructor calls
+ setFocusPolicy(Qt::StrongFocus).
+
+ If the widget has a focus proxy, then the focus policy will
+ be propagated to it.
+
+ \sa focusInEvent(), focusOutEvent(), keyPressEvent(), keyReleaseEvent(), enabled
+*/
+
+
+Qt::FocusPolicy QWidget::focusPolicy() const
+{
+ return (Qt::FocusPolicy)data->focus_policy;
+}
+
+void QWidget::setFocusPolicy(Qt::FocusPolicy policy)
+{
+ data->focus_policy = (uint) policy;
+ Q_D(QWidget);
+ if (d->extra && d->extra->focus_proxy)
+ d->extra->focus_proxy->setFocusPolicy(policy);
+}
+
+/*!
+ \property QWidget::updatesEnabled
+ \brief whether updates are enabled
+
+ An updates enabled widget receives paint events and has a system
+ background; a disabled widget does not. This also implies that
+ calling update() and repaint() has no effect if updates are
+ disabled.
+
+ By default, this property is true.
+
+ setUpdatesEnabled() is normally used to disable updates for a
+ short period of time, for instance to avoid screen flicker during
+ large changes. In Qt, widgets normally do not generate screen
+ flicker, but on X11 the server might erase regions on the screen
+ when widgets get hidden before they can be replaced by other
+ widgets. Disabling updates solves this.
+
+ Example:
+ \snippet doc/src/snippets/code/src_gui_kernel_qwidget.cpp 13
+
+ Disabling a widget implicitly disables all its children. Enabling a widget
+ enables all child widgets \e except top-level widgets or those that
+ have been explicitly disabled. Re-enabling updates implicitly calls
+ update() on the widget.
+
+ \sa paintEvent()
+*/
+void QWidget::setUpdatesEnabled(bool enable)
+{
+ Q_D(QWidget);
+ setAttribute(Qt::WA_ForceUpdatesDisabled, !enable);
+ d->setUpdatesEnabled_helper(enable);
+}
+
+/*! \fn void QWidget::show()
+
+ Shows the widget and its child widgets. This function is
+ equivalent to setVisible(true).
+
+ \sa raise(), showEvent(), hide(), setVisible(), showMinimized(), showMaximized(),
+ showNormal(), isVisible()
+*/
+
+
+/*! \internal
+
+ Makes the widget visible in the isVisible() meaning of the word.
+ It is only called for toplevels or widgets with visible parents.
+ */
+void QWidgetPrivate::show_recursive()
+{
+ Q_Q(QWidget);
+ // polish if necessary
+
+ if (!q->testAttribute(Qt::WA_WState_Created))
+ createRecursively();
+ q->ensurePolished();
+
+#ifdef QT3_SUPPORT
+ if(sendChildEvents)
+ QApplication::sendPostedEvents(q, QEvent::ChildInserted);
+#endif
+ if (!q->isWindow() && q->parentWidget()->d_func()->layout && !q->parentWidget()->data->in_show)
+ q->parentWidget()->d_func()->layout->activate();
+ // activate our layout before we and our children become visible
+ if (layout)
+ layout->activate();
+
+ show_helper();
+}
+
+void QWidgetPrivate::sendPendingMoveAndResizeEvents(bool recursive, bool disableUpdates)
+{
+ Q_Q(QWidget);
+
+ disableUpdates = disableUpdates && q->updatesEnabled();
+ if (disableUpdates)
+ q->setAttribute(Qt::WA_UpdatesDisabled);
+
+ if (q->testAttribute(Qt::WA_PendingMoveEvent)) {
+ QMoveEvent e(data.crect.topLeft(), data.crect.topLeft());
+ QApplication::sendEvent(q, &e);
+ q->setAttribute(Qt::WA_PendingMoveEvent, false);
+ }
+
+ if (q->testAttribute(Qt::WA_PendingResizeEvent)) {
+ QResizeEvent e(data.crect.size(), QSize());
+ QApplication::sendEvent(q, &e);
+ q->setAttribute(Qt::WA_PendingResizeEvent, false);
+ }
+
+ if (disableUpdates)
+ q->setAttribute(Qt::WA_UpdatesDisabled, false);
+
+ if (!recursive)
+ return;
+
+ for (int i = 0; i < children.size(); ++i) {
+ if (QWidget *child = qobject_cast<QWidget *>(children.at(i)))
+ child->d_func()->sendPendingMoveAndResizeEvents(recursive, disableUpdates);
+ }
+}
+
+void QWidgetPrivate::activateChildLayoutsRecursively()
+{
+ sendPendingMoveAndResizeEvents(false, true);
+
+ for (int i = 0; i < children.size(); ++i) {
+ QWidget *child = qobject_cast<QWidget *>(children.at(i));
+ if (!child || child->isHidden() || child->isWindow())
+ continue;
+
+ child->ensurePolished();
+
+ // Activate child's layout
+ QWidgetPrivate *childPrivate = child->d_func();
+ if (childPrivate->layout)
+ childPrivate->layout->activate();
+
+ // Pretend we're visible.
+ const bool wasVisible = child->isVisible();
+ if (!wasVisible)
+ child->setAttribute(Qt::WA_WState_Visible);
+
+ // Do the same for all my children.
+ childPrivate->activateChildLayoutsRecursively();
+
+ // We're not cheating anymore.
+ if (!wasVisible)
+ child->setAttribute(Qt::WA_WState_Visible, false);
+ }
+}
+
+void QWidgetPrivate::show_helper()
+{
+ Q_Q(QWidget);
+ data.in_show = true; // qws optimization
+ // make sure we receive pending move and resize events
+ sendPendingMoveAndResizeEvents();
+
+ // become visible before showing all children
+ q->setAttribute(Qt::WA_WState_Visible);
+
+ // finally show all children recursively
+ showChildren(false);
+
+#ifdef QT3_SUPPORT
+ if (q->parentWidget() && sendChildEvents)
+ QApplication::sendPostedEvents(q->parentWidget(),
+ QEvent::ChildInserted);
+#endif
+
+
+ // popup handling: new popups and tools need to be raised, and
+ // existing popups must be closed. Also propagate the current
+ // windows's KeyboardFocusChange status.
+ if (q->isWindow()) {
+ if ((q->windowType() == Qt::Tool) || (q->windowType() == Qt::Popup) || q->windowType() == Qt::ToolTip) {
+ q->raise();
+ if (q->parentWidget() && q->parentWidget()->window()->testAttribute(Qt::WA_KeyboardFocusChange))
+ q->setAttribute(Qt::WA_KeyboardFocusChange);
+ } else {
+ while (QApplication::activePopupWidget()) {
+ if (!QApplication::activePopupWidget()->close())
+ break;
+ }
+ }
+ }
+
+ // Automatic embedding of child windows of widgets already embedded into
+ // QGraphicsProxyWidget when they are shown the first time.
+ bool isEmbedded = false;
+#ifndef QT_NO_GRAPHICSVIEW
+ if (q->isWindow()) {
+ isEmbedded = q->graphicsProxyWidget() ? true : false;
+ if (!isEmbedded && !bypassGraphicsProxyWidget(q)) {
+ QGraphicsProxyWidget *ancestorProxy = nearestGraphicsProxyWidget(q->parentWidget());
+ if (ancestorProxy) {
+ isEmbedded = true;
+ ancestorProxy->d_func()->embedSubWindow(q);
+ }
+ }
+ }
+#else
+ Q_UNUSED(isEmbedded);
+#endif
+
+ // On Windows, show the popup now so that our own focus handling
+ // stores the correct old focus widget even if it's stolen in the
+ // showevent
+#if defined(Q_WS_WIN) || defined(Q_WS_MAC)
+ if (!isEmbedded && q->windowType() == Qt::Popup)
+ qApp->d_func()->openPopup(q);
+#endif
+
+ // send the show event before showing the window
+ QShowEvent showEvent;
+ QApplication::sendEvent(q, &showEvent);
+
+ if (!isEmbedded && q->isModal() && q->isWindow())
+ // QApplicationPrivate::enterModal *before* show, otherwise the initial
+ // stacking might be wrong
+ QApplicationPrivate::enterModal(q);
+
+
+ show_sys();
+
+#if !defined(Q_WS_WIN) && !defined(Q_WS_MAC)
+ if (!isEmbedded && q->windowType() == Qt::Popup)
+ qApp->d_func()->openPopup(q);
+#endif
+
+#ifndef QT_NO_ACCESSIBILITY
+ if (q->windowType() != Qt::ToolTip) // Tooltips are read aloud twice in MS narrator.
+ QAccessible::updateAccessibility(q, 0, QAccessible::ObjectShow);
+#endif
+
+ if (QApplicationPrivate::hidden_focus_widget == q) {
+ QApplicationPrivate::hidden_focus_widget = 0;
+ q->setFocus(Qt::OtherFocusReason);
+ }
+
+ // Process events when showing a Qt::SplashScreen widget before the event loop
+ // is spinnning; otherwise it might not show up on particular platforms.
+ // This makes QSplashScreen behave the same on all platforms.
+ if (!qApp->d_func()->in_exec && q->windowType() == Qt::SplashScreen)
+ QApplication::processEvents();
+
+ data.in_show = false; // reset qws optimization
+}
+
+/*! \fn void QWidget::hide()
+
+ Hides the widget. This function is equivalent to
+ setVisible(false).
+
+
+ \note If you are working with QDialog or its subclasses and you invoke
+ the show() function after this function, the dialog will be displayed in
+ its original position.
+
+ \sa hideEvent(), isHidden(), show(), setVisible(), isVisible(), close()
+*/
+
+/*!\internal
+ */
+void QWidgetPrivate::hide_helper()
+{
+ Q_Q(QWidget);
+
+ bool isEmbedded = false;
+#if !defined QT_NO_GRAPHICSVIEW
+ isEmbedded = q->isWindow() && nearestGraphicsProxyWidget(q->parentWidget()) != 0;
+#else
+ Q_UNUSED(isEmbedded);
+#endif
+
+ if (!isEmbedded && (q->windowType() == Qt::Popup))
+ qApp->d_func()->closePopup(q);
+
+ // Move test modal here. Otherwise, a modal dialog could get
+ // destroyed and we lose all access to its parent because we haven't
+ // left modality. (Eg. modal Progress Dialog)
+ if (!isEmbedded && q->isModal() && q->isWindow())
+ QApplicationPrivate::leaveModal(q);
+
+#if defined(Q_WS_WIN)
+ if (q->isWindow() && !(q->windowType() == Qt::Popup) && q->parentWidget()
+ && !q->parentWidget()->isHidden() && q->isActiveWindow())
+ q->parentWidget()->activateWindow(); // Activate parent
+#endif
+
+ q->setAttribute(Qt::WA_Mapped, false);
+ hide_sys();
+
+ bool wasVisible = q->testAttribute(Qt::WA_WState_Visible);
+
+ if (wasVisible) {
+ q->setAttribute(Qt::WA_WState_Visible, false);
+
+ }
+
+ QHideEvent hideEvent;
+ QApplication::sendEvent(q, &hideEvent);
+ hideChildren(false);
+
+ // next bit tries to move the focus if the focus widget is now
+ // hidden.
+ if (wasVisible) {
+#if defined(Q_WS_WIN) || defined(Q_WS_X11)
+ qApp->d_func()->sendSyntheticEnterLeave(q);
+#endif
+
+ QWidget *fw = QApplication::focusWidget();
+ while (fw && !fw->isWindow()) {
+ if (fw == q) {
+ q->focusNextPrevChild(true);
+ break;
+ }
+ fw = fw->parentWidget();
+ }
+ }
+
+ if (QWidgetBackingStore *bs = maybeBackingStore())
+ bs->removeDirtyWidget(q);
+
+#ifndef QT_NO_ACCESSIBILITY
+ if (wasVisible)
+ QAccessible::updateAccessibility(q, 0, QAccessible::ObjectHide);
+#endif
+}
+
+/*!
+ \fn bool QWidget::isHidden() const
+
+ Returns true if the widget is hidden, otherwise returns false.
+
+ A hidden widget will only become visible when show() is called on
+ it. It will not be automatically shown when the parent is shown.
+
+ To check visiblity, use !isVisible() instead (notice the exclamation mark).
+
+ isHidden() implies !isVisible(), but a widget can be not visible
+ and not hidden at the same time. This is the case for widgets that are children of
+ widgets that are not visible.
+
+
+ Widgets are hidden if they were created as independent
+ windows or as children of visible widgets, or if hide() or setVisible(false) was called.
+
+*/
+
+
+void QWidget::setVisible(bool visible)
+{
+ if (visible) { // show
+ if (testAttribute(Qt::WA_WState_ExplicitShowHide) && !testAttribute(Qt::WA_WState_Hidden))
+ return;
+
+ Q_D(QWidget);
+
+ // Designer uses a trick to make grabWidget work without showing
+ if (!isWindow() && parentWidget() && parentWidget()->isVisible()
+ && !parentWidget()->testAttribute(Qt::WA_WState_Created))
+ parentWidget()->window()->d_func()->createRecursively();
+
+ //we have to at least create toplevels before applyX11SpecificCommandLineArguments
+ //but not children of non-visible parents
+ QWidget *pw = parentWidget();
+ if (!testAttribute(Qt::WA_WState_Created)
+ && (isWindow() || pw->testAttribute(Qt::WA_WState_Created))) {
+ create();
+ }
+
+#if defined(Q_WS_X11)
+ if (windowType() == Qt::Window)
+ QApplicationPrivate::applyX11SpecificCommandLineArguments(this);
+#elif defined(Q_WS_QWS)
+ if (windowType() == Qt::Window)
+ QApplicationPrivate::applyQWSSpecificCommandLineArguments(this);
+#endif
+
+ bool wasResized = testAttribute(Qt::WA_Resized);
+ Qt::WindowStates initialWindowState = windowState();
+
+ // polish if necessary
+ ensurePolished();
+
+ // remember that show was called explicitly
+ setAttribute(Qt::WA_WState_ExplicitShowHide);
+ // whether we need to inform the parent widget immediately
+ bool needUpdateGeometry = !isWindow() && testAttribute(Qt::WA_WState_Hidden);
+ // we are no longer hidden
+ setAttribute(Qt::WA_WState_Hidden, false);
+
+ if (needUpdateGeometry)
+ d->updateGeometry_helper(true);
+
+#ifdef QT3_SUPPORT
+ QApplication::sendPostedEvents(this, QEvent::ChildInserted);
+#endif
+ // activate our layout before we and our children become visible
+ if (d->layout)
+ d->layout->activate();
+
+ if (!isWindow()) {
+ QWidget *parent = parentWidget();
+ while (parent && parent->isVisible() && parent->d_func()->layout && !parent->data->in_show) {
+ parent->d_func()->layout->activate();
+ if (parent->isWindow())
+ break;
+ parent = parent->parentWidget();
+ }
+ if (parent && !d->getOpaqueRegion().isEmpty())
+ parent->d_func()->setDirtyOpaqueRegion();
+ }
+
+ // adjust size if necessary
+ if (!wasResized
+ && (isWindow() || !parentWidget()->d_func()->layout)) {
+ if (isWindow()) {
+ adjustSize();
+ if (windowState() != initialWindowState)
+ setWindowState(initialWindowState);
+ } else {
+ adjustSize();
+ }
+ setAttribute(Qt::WA_Resized, false);
+ }
+
+ setAttribute(Qt::WA_KeyboardFocusChange, false);
+
+ if (isWindow() || parentWidget()->isVisible()) {
+ // remove posted quit events when showing a new window
+ QCoreApplication::removePostedEvents(qApp, QEvent::Quit);
+
+ d->show_helper();
+
+#if defined(Q_WS_WIN) || defined(Q_WS_X11)
+ qApp->d_func()->sendSyntheticEnterLeave(this);
+#endif
+ }
+
+ QEvent showToParentEvent(QEvent::ShowToParent);
+ QApplication::sendEvent(this, &showToParentEvent);
+ } else { // hide
+ if (testAttribute(Qt::WA_WState_ExplicitShowHide) && testAttribute(Qt::WA_WState_Hidden))
+ return;
+#if defined(Q_WS_WIN)
+ // reset WS_DISABLED style in a Blocked window
+ if(isWindow() && testAttribute(Qt::WA_WState_Created)
+ && QApplicationPrivate::isBlockedByModal(this))
+ {
+ LONG dwStyle = GetWindowLong(winId(), GWL_STYLE);
+ dwStyle &= ~WS_DISABLED;
+ SetWindowLong(winId(), GWL_STYLE, dwStyle);
+ }
+#endif
+ if (QApplicationPrivate::hidden_focus_widget == this)
+ QApplicationPrivate::hidden_focus_widget = 0;
+
+ Q_D(QWidget);
+
+ // hw: The test on getOpaqueRegion() needs to be more intelligent
+ // currently it doesn't work if the widget is hidden (the region will
+ // be clipped). The real check should be testing the cached region
+ // (and dirty flag) directly.
+ if (!isWindow() && parentWidget()) // && !d->getOpaqueRegion().isEmpty())
+ parentWidget()->d_func()->setDirtyOpaqueRegion();
+
+ setAttribute(Qt::WA_WState_Hidden);
+ setAttribute(Qt::WA_WState_ExplicitShowHide);
+ if (testAttribute(Qt::WA_WState_Created))
+ d->hide_helper();
+
+ // invalidate layout similar to updateGeometry()
+ if (!isWindow() && parentWidget()) {
+ if (parentWidget()->d_func()->layout)
+ parentWidget()->d_func()->layout->invalidate();
+ else if (parentWidget()->isVisible())
+ QApplication::postEvent(parentWidget(), new QEvent(QEvent::LayoutRequest));
+ }
+
+ QEvent hideToParentEvent(QEvent::HideToParent);
+ QApplication::sendEvent(this, &hideToParentEvent);
+ }
+}
+
+/*!\fn void QWidget::setHidden(bool hidden)
+
+ Convenience function, equivalent to setVisible(!\a hidden).
+*/
+
+/*!\fn void QWidget::setShown(bool shown)
+
+ Use setVisible(\a shown) instead.
+*/
+
+
+void QWidgetPrivate::_q_showIfNotHidden()
+{
+ Q_Q(QWidget);
+ if ( !(q->isHidden() && q->testAttribute(Qt::WA_WState_ExplicitShowHide)) )
+ q->setVisible(true);
+}
+
+void QWidgetPrivate::showChildren(bool spontaneous)
+{
+ QList<QObject*> childList = children;
+ for (int i = 0; i < childList.size(); ++i) {
+ QWidget *widget = qobject_cast<QWidget*>(childList.at(i));
+ if (!widget
+ || widget->isWindow()
+ || widget->testAttribute(Qt::WA_WState_Hidden))
+ continue;
+ if (spontaneous) {
+ widget->setAttribute(Qt::WA_Mapped);
+ widget->d_func()->showChildren(true);
+ QShowEvent e;
+ QApplication::sendSpontaneousEvent(widget, &e);
+ } else {
+ if (widget->testAttribute(Qt::WA_WState_ExplicitShowHide))
+ widget->d_func()->show_recursive();
+ else
+ widget->show();
+ }
+ }
+}
+
+void QWidgetPrivate::hideChildren(bool spontaneous)
+{
+ QList<QObject*> childList = children;
+ for (int i = 0; i < childList.size(); ++i) {
+ QWidget *widget = qobject_cast<QWidget*>(childList.at(i));
+ if (!widget || widget->isWindow() || widget->testAttribute(Qt::WA_WState_Hidden))
+ continue;
+ if (spontaneous)
+ widget->setAttribute(Qt::WA_Mapped, false);
+ else
+ widget->setAttribute(Qt::WA_WState_Visible, false);
+ widget->d_func()->hideChildren(spontaneous);
+ QHideEvent e;
+ if (spontaneous) {
+ QApplication::sendSpontaneousEvent(widget, &e);
+ } else {
+ QApplication::sendEvent(widget, &e);
+ if (widget->internalWinId()
+ && widget->testAttribute(Qt::WA_DontCreateNativeAncestors)) {
+ // hide_sys() on an ancestor won't have any affect on this
+ // widget, so it needs an explicit hide_sys() of its own
+ widget->d_func()->hide_sys();
+ }
+ }
+#if defined(Q_WS_WIN) || defined(Q_WS_X11)
+ qApp->d_func()->sendSyntheticEnterLeave(widget);
+#endif
+#ifndef QT_NO_ACCESSIBILITY
+ if (!spontaneous)
+ QAccessible::updateAccessibility(widget, 0, QAccessible::ObjectHide);
+#endif
+ }
+}
+
+bool QWidgetPrivate::close_helper(CloseMode mode)
+{
+ if (data.is_closing)
+ return true;
+
+ Q_Q(QWidget);
+ data.is_closing = 1;
+
+ QPointer<QWidget> that = q;
+ QPointer<QWidget> parentWidget = q->parentWidget();
+
+#ifdef QT3_SUPPORT
+ bool isMain = (QApplicationPrivate::main_widget == q);
+#endif
+ bool quitOnClose = q->testAttribute(Qt::WA_QuitOnClose);
+ if (mode != CloseNoEvent) {
+ QCloseEvent e;
+ if (mode == CloseWithSpontaneousEvent)
+ QApplication::sendSpontaneousEvent(q, &e);
+ else
+ QApplication::sendEvent(q, &e);
+ if (!that.isNull() && !e.isAccepted()) {
+ data.is_closing = 0;
+ return false;
+ }
+ }
+
+ if (!that.isNull() && !q->isHidden())
+ q->hide();
+
+#ifdef QT3_SUPPORT
+ if (isMain)
+ qApp->quit();
+#endif
+ // Attempt to close the application only if this widget has the
+ // WA_QuitOnClose flag set set and has a non-visible parent
+ quitOnClose = quitOnClose && (parentWidget.isNull() || !parentWidget->isVisible() || parentWidget->testAttribute(Qt::WA_DontShowOnScreen));
+
+ if (quitOnClose) {
+ // If there is no non-withdrawn primary window left (except
+ // the ones without QuitOnClose or with WA_DontShowOnScreen),
+ // we emit the lastWindowClosed signal
+ QWidgetList list = QApplication::topLevelWidgets();
+ bool lastWindowClosed = true;
+ for (int i = 0; i < list.size(); ++i) {
+ QWidget *w = list.at(i);
+ if ((w->isVisible() && !w->testAttribute(Qt::WA_DontShowOnScreen))
+ && !w->parentWidget() && w->testAttribute(Qt::WA_QuitOnClose)) {
+ lastWindowClosed = false;
+ break;
+ }
+ }
+ if (lastWindowClosed)
+ QApplicationPrivate::emitLastWindowClosed();
+ }
+
+ if (!that.isNull()) {
+ data.is_closing = 0;
+ if (q->testAttribute(Qt::WA_DeleteOnClose)) {
+ q->setAttribute(Qt::WA_DeleteOnClose, false);
+ q->deleteLater();
+ }
+ }
+ return true;
+}
+
+
+/*!
+ Closes this widget. Returns true if the widget was closed;
+ otherwise returns false.
+
+ First it sends the widget a QCloseEvent. The widget is \link
+ hide() hidden\endlink if it \link QCloseEvent::accept()
+ accepts\endlink the close event. If it \link QCloseEvent::ignore()
+ ignores\endlink the event, nothing happens. The default
+ implementation of QWidget::closeEvent() accepts the close event.
+
+ If the widget has the Qt::WA_DeleteOnClose flag, the widget
+ is also deleted. A close events is delivered to the widget no
+ matter if the widget is visible or not.
+
+ The \l QApplication::lastWindowClosed() signal is emitted when the
+ last visible primary window (i.e. window with no parent) with the
+ Qt::WA_QuitOnClose attribute set is closed. By default this
+ attribute is set for all widgets except transient windows such as
+ splash screens, tool windows, and popup menus.
+
+*/
+
+bool QWidget::close()
+{
+ return d_func()->close_helper(QWidgetPrivate::CloseWithEvent);
+}
+
+/*!
+ \property QWidget::visible
+ \brief whether the widget is visible
+
+ Calling setVisible(true) or show() sets the widget to visible
+ status if all its parent widgets up to the window are visible. If
+ an ancestor is not visible, the widget won't become visible until
+ all its ancestors are shown. If its size or position has changed,
+ Qt guarantees that a widget gets move and resize events just
+ before it is shown. If the widget has not been resized yet, Qt
+ will adjust the widget's size to a useful default using
+ adjustSize().
+
+ Calling setVisible(false) or hide() hides a widget explicitly. An
+ explicitly hidden widget will never become visible, even if all
+ its ancestors become visible, unless you show it.
+
+ A widget receives show and hide events when its visibility status
+ changes. Between a hide and a show event, there is no need to
+ waste CPU cycles preparing or displaying information to the user.
+ A video application, for example, might simply stop generating new
+ frames.
+
+ A widget that happens to be obscured by other windows on the
+ screen is considered to be visible. The same applies to iconified
+ windows and windows that exist on another virtual
+ desktop (on platforms that support this concept). A widget
+ receives spontaneous show and hide events when its mapping status
+ is changed by the window system, e.g. a spontaneous hide event
+ when the user minimizes the window, and a spontaneous show event
+ when the window is restored again.
+
+ You almost never have to reimplement the setVisible() function. If
+ you need to change some settings before a widget is shown, use
+ showEvent() instead. If you need to do some delayed initialization
+ use the Polish event delivered to the event() function.
+
+ \sa show(), hide(), isHidden(), isVisibleTo(), isMinimized(),
+ showEvent(), hideEvent()
+*/
+
+
+/*!
+ Returns true if this widget would become visible if \a ancestor is
+ shown; otherwise returns false.
+
+ The true case occurs if neither the widget itself nor any parent
+ up to but excluding \a ancestor has been explicitly hidden.
+
+ This function will still return true if the widget is obscured by
+ other windows on the screen, but could be physically visible if it
+ or they were to be moved.
+
+ isVisibleTo(0) is identical to isVisible().
+
+ \sa show() hide() isVisible()
+*/
+
+bool QWidget::isVisibleTo(QWidget* ancestor) const
+{
+ if (!ancestor)
+ return isVisible();
+ const QWidget * w = this;
+ while (w
+ && !w->isHidden()
+ && !w->isWindow()
+ && w->parentWidget()
+ && w->parentWidget() != ancestor)
+ w = w->parentWidget();
+ return !w->isHidden();
+}
+
+#ifdef QT3_SUPPORT
+/*!
+ Use visibleRegion() instead.
+*/
+QRect QWidget::visibleRect() const
+{
+ return d_func()->clipRect();
+}
+#endif
+
+/*!
+ Returns the unobscured region where paint events can occur.
+
+ For visible widgets, this is an approximation of the area not
+ covered by other widgets; otherwise, this is an empty region.
+
+ The repaint() function calls this function if necessary, so in
+ general you do not need to call it.
+
+*/
+QRegion QWidget::visibleRegion() const
+{
+ Q_D(const QWidget);
+
+ QRect clipRect = d->clipRect();
+ if (clipRect.isEmpty())
+ return QRegion();
+ QRegion r(clipRect);
+ d->subtractOpaqueChildren(r, clipRect);
+ d->subtractOpaqueSiblings(r);
+#ifdef Q_WS_QWS
+ const QWSWindowSurface *surface = static_cast<const QWSWindowSurface*>(windowSurface());
+ if (surface) {
+ const QPoint offset = mapTo(surface->window(), QPoint());
+ r &= surface->clipRegion().translated(-offset);
+ }
+#endif
+ return r;
+}
+
+
+QSize QWidgetPrivate::adjustedSize() const
+{
+ Q_Q(const QWidget);
+
+ QSize s = q->sizeHint();
+
+ if (q->isWindow()) {
+ Qt::Orientations exp;
+ if (QLayout *l = q->layout()) {
+ if (l->hasHeightForWidth())
+ s.setHeight(l->totalHeightForWidth(s.width()));
+ exp = l->expandingDirections();
+ } else
+ {
+ if (q->sizePolicy().hasHeightForWidth())
+ s.setHeight(q->heightForWidth(s.width()));
+ exp = q->sizePolicy().expandingDirections();
+ }
+ if (exp & Qt::Horizontal)
+ s.setWidth(qMax(s.width(), 200));
+ if (exp & Qt::Vertical)
+ s.setHeight(qMax(s.height(), 100));
+#if defined(Q_WS_X11)
+ QRect screen = QApplication::desktop()->screenGeometry(q->x11Info().screen());
+#else // all others
+ QRect screen = QApplication::desktop()->screenGeometry(q->pos());
+#endif
+#if defined (Q_OS_WINCE)
+ s.setWidth(qMin(s.width(), screen.width()));
+ s.setHeight(qMin(s.height(), screen.height()));
+#else
+ s.setWidth(qMin(s.width(), screen.width()*2/3));
+ s.setHeight(qMin(s.height(), screen.height()*2/3));
+#endif
+ if (QTLWExtra *extra = maybeTopData())
+ extra->sizeAdjusted = true;
+ }
+
+ if (!s.isValid()) {
+ QRect r = q->childrenRect(); // get children rectangle
+ if (r.isNull())
+ return s;
+ s = r.size() + QSize(2 * r.x(), 2 * r.y());
+ }
+
+ return s;
+}
+
+/*!
+ Adjusts the size of the widget to fit its contents.
+
+ This function uses sizeHint() if it is valid, i.e., the size hint's width
+ and height are \>= 0. Otherwise, it sets the size to the children
+ rectangle that covers all child widgets (the union of all child widget
+ rectangles).
+
+ For windows, the screen size is also taken into account. If the sizeHint()
+ is less than (200, 100) and the size policy is \l{QSizePolicy::Expanding}
+ {expanding}, the window will be at least (200, 100). The maximum size of
+ a window is 2/3 of the screen's width and height.
+
+ \sa sizeHint(), childrenRect()
+*/
+
+void QWidget::adjustSize()
+{
+ Q_D(QWidget);
+ ensurePolished();
+ QSize s = d->adjustedSize();
+ if (s.isValid())
+ resize(s);
+}
+
+
+/*!
+ \property QWidget::sizeHint
+ \brief the recommended size for the widget
+
+ If the value of this property is an invalid size, no size is
+ recommended.
+
+ The default implementation of sizeHint() returns an invalid size
+ if there is no layout for this widget, and returns the layout's
+ preferred size otherwise.
+
+ \sa QSize::isValid(), minimumSizeHint(), sizePolicy(),
+ setMinimumSize(), updateGeometry()
+*/
+
+QSize QWidget::sizeHint() const
+{
+ Q_D(const QWidget);
+ if (d->layout)
+ return d->layout->totalSizeHint();
+ return QSize(-1, -1);
+}
+
+/*!
+ \property QWidget::minimumSizeHint
+ \brief the recommended minimum size for the widget
+
+ If the value of this property is an invalid size, no minimum size
+ is recommended.
+
+ The default implementation of minimumSizeHint() returns an invalid
+ size if there is no layout for this widget, and returns the
+ layout's minimum size otherwise. Most built-in widgets reimplement
+ minimumSizeHint().
+
+ \l QLayout will never resize a widget to a size smaller than the
+ minimum size hint unless minimumSize() is set or the size policy is
+ set to QSizePolicy::Ignore. If minimumSize() is set, the minimum
+ size hint will be ignored.
+
+ \sa QSize::isValid(), resize(), setMinimumSize(), sizePolicy()
+*/
+QSize QWidget::minimumSizeHint() const
+{
+ Q_D(const QWidget);
+ if (d->layout)
+ return d->layout->totalMinimumSize();
+ return QSize(-1, -1);
+}
+
+
+/*!
+ \fn QWidget *QWidget::parentWidget() const
+
+ Returns the parent of this widget, or 0 if it does not have any
+ parent widget.
+*/
+
+
+/*!
+ Returns true if this widget is a parent, (or grandparent and so on
+ to any level), of the given \a child, and both widgets are within
+ the same window; otherwise returns false.
+*/
+
+bool QWidget::isAncestorOf(const QWidget *child) const
+{
+ while (child) {
+ if (child == this)
+ return true;
+ if (child->isWindow())
+ return false;
+ child = child->parentWidget();
+ }
+ return false;
+}
+
+#if defined(Q_WS_WIN)
+inline void setDisabledStyle(QWidget *w, bool setStyle)
+{
+ // set/reset WS_DISABLED style.
+ if(w && w->isWindow() && w->isVisible() && w->isEnabled()) {
+ LONG dwStyle = GetWindowLong(w->winId(), GWL_STYLE);
+ if (setStyle)
+ dwStyle |= WS_DISABLED;
+ else
+ dwStyle &= ~WS_DISABLED;
+ SetWindowLong(w->winId(), GWL_STYLE, dwStyle);
+ // we might need to repaint in some situations (eg. menu)
+ w->repaint();
+ }
+}
+#endif
+
+/*****************************************************************************
+ QWidget event handling
+ *****************************************************************************/
+
+/*!
+ This is the main event handler; it handles event \a event. You can
+ reimplement this function in a subclass, but we recommend using
+ one of the specialized event handlers instead.
+
+ Key press and release events are treated differently from other
+ events. event() checks for Tab and Shift+Tab and tries to move the
+ focus appropriately. If there is no widget to move the focus to
+ (or the key press is not Tab or Shift+Tab), event() calls
+ keyPressEvent().
+
+ Mouse and tablet event handling is also slightly special: only
+ when the widget is \l enabled, event() will call the specialized
+ handlers such as mousePressEvent(); otherwise it will discard the
+ event.
+
+ This function returns true if the event was recognized, otherwise
+ it returns false. If the recognized event was accepted (see \l
+ QEvent::accepted), any further processing such as event
+ propagation to the parent widget stops.
+
+ \sa closeEvent(), focusInEvent(), focusOutEvent(), enterEvent(),
+ keyPressEvent(), keyReleaseEvent(), leaveEvent(),
+ mouseDoubleClickEvent(), mouseMoveEvent(), mousePressEvent(),
+ mouseReleaseEvent(), moveEvent(), paintEvent(), resizeEvent(),
+ QObject::event(), QObject::timerEvent()
+*/
+
+bool QWidget::event(QEvent *event)
+{
+ Q_D(QWidget);
+
+ // ignore mouse events when disabled
+ if (!isEnabled()) {
+ switch(event->type()) {
+ case QEvent::TabletPress:
+ case QEvent::TabletRelease:
+ case QEvent::TabletMove:
+ case QEvent::MouseButtonPress:
+ case QEvent::MouseButtonRelease:
+ case QEvent::MouseButtonDblClick:
+ case QEvent::MouseMove:
+ case QEvent::ContextMenu:
+#ifndef QT_NO_WHEELEVENT
+ case QEvent::Wheel:
+#endif
+ return false;
+ default:
+ break;
+ }
+ }
+ switch (event->type()) {
+ case QEvent::MouseMove:
+ mouseMoveEvent((QMouseEvent*)event);
+ break;
+
+ case QEvent::MouseButtonPress:
+ // Don't reset input context here. Whether reset or not is
+ // a responsibility of input method. reset() will be
+ // called by mouseHandler() of input method if necessary
+ // via mousePressEvent() of text widgets.
+#if 0
+ resetInputContext();
+#endif
+ mousePressEvent((QMouseEvent*)event);
+ break;
+
+ case QEvent::MouseButtonRelease:
+ mouseReleaseEvent((QMouseEvent*)event);
+ break;
+
+ case QEvent::MouseButtonDblClick:
+ mouseDoubleClickEvent((QMouseEvent*)event);
+ break;
+#ifndef QT_NO_WHEELEVENT
+ case QEvent::Wheel:
+ wheelEvent((QWheelEvent*)event);
+ break;
+#endif
+#ifndef QT_NO_TABLETEVENT
+ case QEvent::TabletMove:
+ case QEvent::TabletPress:
+ case QEvent::TabletRelease:
+ tabletEvent((QTabletEvent*)event);
+ break;
+#endif
+#ifdef QT3_SUPPORT
+ case QEvent::Accel:
+ event->ignore();
+ return false;
+#endif
+ case QEvent::KeyPress: {
+ QKeyEvent *k = (QKeyEvent *)event;
+ bool res = false;
+ if (!(k->modifiers() & (Qt::ControlModifier | Qt::AltModifier))) { //### Add MetaModifier?
+ if (k->key() == Qt::Key_Backtab
+ || (k->key() == Qt::Key_Tab && (k->modifiers() & Qt::ShiftModifier)))
+ res = focusNextPrevChild(false);
+ else if (k->key() == Qt::Key_Tab)
+ res = focusNextPrevChild(true);
+ if (res)
+ break;
+ }
+ keyPressEvent(k);
+#ifdef QT_KEYPAD_NAVIGATION
+ if (!k->isAccepted() && QApplication::keypadNavigationEnabled()
+ && !(k->modifiers() & (Qt::ControlModifier | Qt::AltModifier | Qt::ShiftModifier))) {
+ if (k->key() == Qt::Key_Up)
+ res = focusNextPrevChild(false);
+ else if (k->key() == Qt::Key_Down)
+ res = focusNextPrevChild(true);
+ if (res) {
+ k->accept();
+ break;
+ }
+ }
+#endif
+#ifndef QT_NO_WHATSTHIS
+ if (!k->isAccepted()
+ && k->modifiers() & Qt::ShiftModifier && k->key() == Qt::Key_F1
+ && d->whatsThis.size()) {
+ QWhatsThis::showText(mapToGlobal(inputMethodQuery(Qt::ImMicroFocus).toRect().center()), d->whatsThis, this);
+ k->accept();
+ }
+#endif
+ }
+ break;
+
+ case QEvent::KeyRelease:
+ keyReleaseEvent((QKeyEvent*)event);
+ // fall through
+ case QEvent::ShortcutOverride:
+ break;
+
+ case QEvent::InputMethod:
+ inputMethodEvent((QInputMethodEvent *) event);
+ break;
+
+ case QEvent::PolishRequest:
+ ensurePolished();
+ break;
+
+ case QEvent::Polish: {
+ style()->polish(this);
+ setAttribute(Qt::WA_WState_Polished);
+ if (!QApplication::font(this).isCopyOf(QApplication::font()))
+ d->resolveFont();
+ if (!QApplication::palette(this).isCopyOf(QApplication::palette()))
+ d->resolvePalette();
+#ifdef QT3_SUPPORT
+ if(d->sendChildEvents)
+ QApplication::sendPostedEvents(this, QEvent::ChildInserted);
+#endif
+ }
+ break;
+
+ case QEvent::ApplicationWindowIconChange:
+ if (isWindow() && !testAttribute(Qt::WA_SetWindowIcon)) {
+ d->setWindowIcon_sys();
+ d->setWindowIcon_helper();
+ }
+ break;
+ case QEvent::FocusIn:
+#if defined(Q_WS_WIN)
+ QInputContextPrivate::updateImeStatus(this, true);
+#endif
+ focusInEvent((QFocusEvent*)event);
+ break;
+
+ case QEvent::FocusOut:
+#if defined(Q_WS_WIN)
+ QInputContextPrivate::updateImeStatus(this, false);
+#endif
+ focusOutEvent((QFocusEvent*)event);
+ break;
+
+ case QEvent::Enter:
+#ifndef QT_NO_STATUSTIP
+ if (d->statusTip.size()) {
+ QStatusTipEvent tip(d->statusTip);
+ QApplication::sendEvent(const_cast<QWidget *>(this), &tip);
+ }
+#endif
+ enterEvent(event);
+ break;
+
+ case QEvent::Leave:
+#ifndef QT_NO_STATUSTIP
+ if (d->statusTip.size()) {
+ QString empty;
+ QStatusTipEvent tip(empty);
+ QApplication::sendEvent(const_cast<QWidget *>(this), &tip);
+ }
+#endif
+ leaveEvent(event);
+ break;
+
+ case QEvent::HoverEnter:
+ case QEvent::HoverLeave:
+ update();
+ break;
+
+ case QEvent::Paint:
+ // At this point the event has to be delivered, regardless
+ // whether the widget isVisible() or not because it
+ // already went through the filters
+ paintEvent((QPaintEvent*)event);
+ break;
+
+ case QEvent::Move:
+ moveEvent((QMoveEvent*)event);
+ break;
+
+ case QEvent::Resize:
+ resizeEvent((QResizeEvent*)event);
+ break;
+
+ case QEvent::Close:
+ closeEvent((QCloseEvent *)event);
+ break;
+
+#ifndef QT_NO_CONTEXTMENU
+ case QEvent::ContextMenu:
+ switch (data->context_menu_policy) {
+ case Qt::PreventContextMenu:
+ break;
+ case Qt::DefaultContextMenu:
+ contextMenuEvent(static_cast<QContextMenuEvent *>(event));
+ break;
+ case Qt::CustomContextMenu:
+ emit customContextMenuRequested(static_cast<QContextMenuEvent *>(event)->pos());
+ break;
+#ifndef QT_NO_MENU
+ case Qt::ActionsContextMenu:
+ if (d->actions.count()) {
+ QMenu::exec(d->actions, static_cast<QContextMenuEvent *>(event)->globalPos(),
+ 0, this);
+ break;
+ }
+ // fall through
+#endif
+ default:
+ event->ignore();
+ break;
+ }
+ break;
+#endif // QT_NO_CONTEXTMENU
+
+#ifndef QT_NO_DRAGANDDROP
+ case QEvent::Drop:
+ dropEvent((QDropEvent*) event);
+ break;
+
+ case QEvent::DragEnter:
+ dragEnterEvent((QDragEnterEvent*) event);
+ break;
+
+ case QEvent::DragMove:
+ dragMoveEvent((QDragMoveEvent*) event);
+ break;
+
+ case QEvent::DragLeave:
+ dragLeaveEvent((QDragLeaveEvent*) event);
+ break;
+#endif
+
+ case QEvent::Show:
+ showEvent((QShowEvent*) event);
+ break;
+
+ case QEvent::Hide:
+ hideEvent((QHideEvent*) event);
+ break;
+
+ case QEvent::ShowWindowRequest:
+ if (!isHidden())
+ d->show_sys();
+ break;
+
+ case QEvent::ApplicationFontChange:
+ d->resolveFont();
+ break;
+ case QEvent::ApplicationPaletteChange:
+ if (!(windowType() == Qt::Desktop))
+ d->resolvePalette();
+ break;
+
+ case QEvent::ToolBarChange:
+ case QEvent::ActivationChange:
+ case QEvent::EnabledChange:
+ case QEvent::FontChange:
+ case QEvent::StyleChange:
+ case QEvent::PaletteChange:
+ case QEvent::WindowTitleChange:
+ case QEvent::IconTextChange:
+ case QEvent::ModifiedChange:
+ case QEvent::MouseTrackingChange:
+ case QEvent::ParentChange:
+ case QEvent::WindowStateChange:
+ case QEvent::LocaleChange:
+ case QEvent::MacSizeChange:
+ case QEvent::ContentsRectChange:
+ changeEvent(event);
+ break;
+
+ case QEvent::WindowActivate:
+ case QEvent::WindowDeactivate: {
+#ifdef QT3_SUPPORT
+ windowActivationChange(event->type() != QEvent::WindowActivate);
+#endif
+ if (isVisible() && !palette().isEqual(QPalette::Active, QPalette::Inactive))
+ update();
+ QList<QObject*> childList = d->children;
+ for (int i = 0; i < childList.size(); ++i) {
+ QWidget *w = qobject_cast<QWidget *>(childList.at(i));
+ if (w && w->isVisible() && !w->isWindow())
+ QApplication::sendEvent(w, event);
+ }
+ break; }
+
+ case QEvent::LanguageChange:
+#ifdef QT3_SUPPORT
+ languageChange();
+#endif
+ changeEvent(event);
+ {
+ QList<QObject*> childList = d->children;
+ for (int i = 0; i < childList.size(); ++i) {
+ QObject *o = childList.at(i);
+ QApplication::sendEvent(o, event);
+ }
+ }
+ update();
+ break;
+
+ case QEvent::ApplicationLayoutDirectionChange:
+ d->resolveLayoutDirection();
+ break;
+
+ case QEvent::LayoutDirectionChange:
+ if (d->layout)
+ d->layout->invalidate();
+ update();
+ changeEvent(event);
+ break;
+ case QEvent::UpdateRequest:
+ d->syncBackingStore();
+ break;
+ case QEvent::UpdateLater:
+ update(static_cast<QUpdateLaterEvent*>(event)->region());
+ break;
+
+ case QEvent::WindowBlocked:
+ case QEvent::WindowUnblocked:
+ {
+ QList<QObject*> childList = d->children;
+ for (int i = 0; i < childList.size(); ++i) {
+ QObject *o = childList.at(i);
+ if (o != qApp->activeModalWidget()) {
+ if (qobject_cast<QWidget *>(o) && static_cast<QWidget *>(o)->isWindow()) {
+ // do not forward the event to child windows,
+ // QApplication does this for us
+ continue;
+ }
+ QApplication::sendEvent(o, event);
+ }
+ }
+#if defined(Q_WS_WIN)
+ setDisabledStyle(this, (event->type() == QEvent::WindowBlocked));
+#endif
+ }
+ break;
+#ifndef QT_NO_TOOLTIP
+ case QEvent::ToolTip:
+ if (!d->toolTip.isEmpty())
+ QToolTip::showText(static_cast<QHelpEvent*>(event)->globalPos(), d->toolTip, this);
+ else
+ event->ignore();
+ break;
+#endif
+#ifndef QT_NO_WHATSTHIS
+ case QEvent::WhatsThis:
+ if (d->whatsThis.size())
+ QWhatsThis::showText(static_cast<QHelpEvent *>(event)->globalPos(), d->whatsThis, this);
+ else
+ event->ignore();
+ break;
+ case QEvent::QueryWhatsThis:
+ if (d->whatsThis.isEmpty())
+ event->ignore();
+ break;
+#endif
+#ifndef QT_NO_ACCESSIBILITY
+ case QEvent::AccessibilityDescription:
+ case QEvent::AccessibilityHelp: {
+ QAccessibleEvent *ev = static_cast<QAccessibleEvent *>(event);
+ if (ev->child())
+ return false;
+ switch (ev->type()) {
+#ifndef QT_NO_TOOLTIP
+ case QEvent::AccessibilityDescription:
+ ev->setValue(d->toolTip);
+ break;
+#endif
+#ifndef QT_NO_WHATSTHIS
+ case QEvent::AccessibilityHelp:
+ ev->setValue(d->whatsThis);
+ break;
+#endif
+ default:
+ return false;
+ }
+ break; }
+#endif
+ case QEvent::EmbeddingControl:
+ d->topData()->frameStrut.setCoords(0 ,0, 0, 0);
+ data->fstrut_dirty = false;
+#if defined(Q_WS_WIN) || defined(Q_WS_X11)
+ d->topData()->embedded = 1;
+#endif
+ break;
+#ifndef QT_NO_ACTION
+ case QEvent::ActionAdded:
+ case QEvent::ActionRemoved:
+ case QEvent::ActionChanged:
+ actionEvent((QActionEvent*)event);
+ break;
+#endif
+
+ case QEvent::KeyboardLayoutChange:
+ {
+ changeEvent(event);
+
+ // inform children of the change
+ QList<QObject*> childList = d->children;
+ for (int i = 0; i < childList.size(); ++i) {
+ QWidget *w = qobject_cast<QWidget *>(childList.at(i));
+ if (w && w->isVisible() && !w->isWindow())
+ QApplication::sendEvent(w, event);
+ }
+ break;
+ }
+#ifdef Q_WS_MAC
+ case QEvent::MacGLWindowChange:
+ d->needWindowChange = false;
+ break;
+#endif
+#ifndef QT_NO_PROPERTIES
+ case QEvent::DynamicPropertyChange: {
+ const QByteArray &propName = static_cast<QDynamicPropertyChangeEvent *>(event)->propertyName();
+ if (!qstrncmp(propName, "_q_customDpi", 12) && propName.length() == 13) {
+ uint value = property(propName.constData()).toUInt();
+ if (!d->extra)
+ d->createExtra();
+ const char axis = propName.at(12);
+ if (axis == 'X')
+ d->extra->customDpiX = value;
+ else if (axis == 'Y')
+ d->extra->customDpiY = value;
+ d->updateFont(d->data.fnt);
+ }
+ // fall through
+ }
+#endif
+ default:
+ return QObject::event(event);
+ }
+ return true;
+}
+
+/*!
+ This event handler can be reimplemented to handle state changes.
+
+ The state being changed in this event can be retrieved through event \a
+ event.
+
+ Change events include: QEvent::ToolBarChange,
+ QEvent::ActivationChange, QEvent::EnabledChange, QEvent::FontChange,
+ QEvent::StyleChange, QEvent::PaletteChange,
+ QEvent::WindowTitleChange, QEvent::IconTextChange,
+ QEvent::ModifiedChange, QEvent::MouseTrackingChange,
+ QEvent::ParentChange, QEvent::WindowStateChange,
+ QEvent::LanguageChange, QEvent::LocaleChange,
+ QEvent::LayoutDirectionChange.
+
+*/
+void QWidget::changeEvent(QEvent * event)
+{
+ switch(event->type()) {
+ case QEvent::EnabledChange:
+ update();
+#ifndef QT_NO_ACCESSIBILITY
+ QAccessible::updateAccessibility(this, 0, QAccessible::StateChanged);
+#endif
+ break;
+
+ case QEvent::FontChange:
+ case QEvent::StyleChange: {
+ update();
+ updateGeometry();
+#ifdef Q_WS_QWS
+ Q_D(QWidget);
+ if (isWindow())
+ d->data.fstrut_dirty = true;
+#endif
+ break;
+ }
+
+ case QEvent::PaletteChange:
+ update();
+ break;
+
+#ifdef Q_WS_MAC
+ case QEvent::MacSizeChange:
+ updateGeometry();
+ break;
+ case QEvent::ToolTipChange:
+ case QEvent::MouseTrackingChange:
+ qt_mac_update_mouseTracking(this);
+ break;
+#endif
+
+ default:
+ break;
+ }
+}
+
+/*!
+ This event handler, for event \a event, can be reimplemented in a
+ subclass to receive mouse move events for the widget.
+
+ If mouse tracking is switched off, mouse move events only occur if
+ a mouse button is pressed while the mouse is being moved. If mouse
+ tracking is switched on, mouse move events occur even if no mouse
+ button is pressed.
+
+ QMouseEvent::pos() reports the position of the mouse cursor,
+ relative to this widget. For press and release events, the
+ position is usually the same as the position of the last mouse
+ move event, but it might be different if the user's hand shakes.
+ This is a feature of the underlying window system, not Qt.
+
+ If you want to show a tooltip immediately, while the mouse is
+ moving (e.g., to get the mouse coordinates with QMouseEvent::pos()
+ and show them as a tooltip), you must first enable mouse tracking
+ as described above. Then, to ensure that the tooltip is updated
+ immediately, you must call QToolTip::showText() instead of
+ setToolTip() in your implementation of mouseMoveEvent().
+
+ \sa setMouseTracking(), mousePressEvent(), mouseReleaseEvent(),
+ mouseDoubleClickEvent(), event(), QMouseEvent, {Scribble Example}
+*/
+
+void QWidget::mouseMoveEvent(QMouseEvent *event)
+{
+ event->ignore();
+}
+
+/*!
+ This event handler, for event \a event, can be reimplemented in a
+ subclass to receive mouse press events for the widget.
+
+ If you create new widgets in the mousePressEvent() the
+ mouseReleaseEvent() may not end up where you expect, depending on
+ the underlying window system (or X11 window manager), the widgets'
+ location and maybe more.
+
+ The default implementation implements the closing of popup widgets
+ when you click outside the window. For other widget types it does
+ nothing.
+
+ \sa mouseReleaseEvent(), mouseDoubleClickEvent(),
+ mouseMoveEvent(), event(), QMouseEvent, {Scribble Example}
+*/
+
+void QWidget::mousePressEvent(QMouseEvent *event)
+{
+ event->ignore();
+ if ((windowType() == Qt::Popup)) {
+ event->accept();
+ QWidget* w;
+ while ((w = qApp->activePopupWidget()) && w != this){
+ w->close();
+ if (qApp->activePopupWidget() == w) // widget does not want to dissappear
+ w->hide(); // hide at least
+ }
+ if (!rect().contains(event->pos())){
+ close();
+ }
+ }
+}
+
+/*!
+ This event handler, for event \a event, can be reimplemented in a
+ subclass to receive mouse release events for the widget.
+
+ \sa mousePressEvent(), mouseDoubleClickEvent(),
+ mouseMoveEvent(), event(), QMouseEvent, {Scribble Example}
+*/
+
+void QWidget::mouseReleaseEvent(QMouseEvent *event)
+{
+ event->ignore();
+}
+
+/*!
+ This event handler, for event \a event, can be reimplemented in a
+ subclass to receive mouse double click events for the widget.
+
+ The default implementation generates a normal mouse press event.
+
+ Note that the widgets gets a mousePressEvent() and a
+ mouseReleaseEvent() before the mouseDoubleClickEvent().
+
+ \sa mousePressEvent(), mouseReleaseEvent() mouseMoveEvent(),
+ event(), QMouseEvent
+*/
+
+void QWidget::mouseDoubleClickEvent(QMouseEvent *event)
+{
+ mousePressEvent(event); // try mouse press event
+}
+
+#ifndef QT_NO_WHEELEVENT
+/*!
+ This event handler, for event \a event, can be reimplemented in a
+ subclass to receive wheel events for the widget.
+
+ If you reimplement this handler, it is very important that you
+ \link QWheelEvent ignore()\endlink the event if you do not handle
+ it, so that the widget's parent can interpret it.
+
+ The default implementation ignores the event.
+
+ \sa QWheelEvent::ignore(), QWheelEvent::accept(), event(),
+ QWheelEvent
+*/
+
+void QWidget::wheelEvent(QWheelEvent *event)
+{
+ event->ignore();
+}
+#endif // QT_NO_WHEELEVENT
+
+#ifndef QT_NO_TABLETEVENT
+/*!
+ This event handler, for event \a event, can be reimplemented in a
+ subclass to receive tablet events for the widget.
+
+ If you reimplement this handler, it is very important that you
+ \link QTabletEvent ignore()\endlink the event if you do not handle
+ it, so that the widget's parent can interpret it.
+
+ The default implementation ignores the event.
+
+ \sa QTabletEvent::ignore(), QTabletEvent::accept(), event(),
+ QTabletEvent
+*/
+
+void QWidget::tabletEvent(QTabletEvent *event)
+{
+ event->ignore();
+}
+#endif // QT_NO_TABLETEVENT
+
+/*!
+ This event handler, for event \a event, can be reimplemented in a
+ subclass to receive key press events for the widget.
+
+ A widget must call setFocusPolicy() to accept focus initially and
+ have focus in order to receive a key press event.
+
+ If you reimplement this handler, it is very important that you
+ call the base class implementation if you do not act upon the key.
+
+ The default implementation closes popup widgets if the user
+ presses Esc. Otherwise the event is ignored, so that the widget's
+ parent can interpret it.
+
+ Note that QKeyEvent starts with isAccepted() == true, so you do not
+ need to call QKeyEvent::accept() - just do not call the base class
+ implementation if you act upon the key.
+
+ \sa keyReleaseEvent(), setFocusPolicy(),
+ focusInEvent(), focusOutEvent(), event(), QKeyEvent, {Tetrix Example}
+*/
+
+void QWidget::keyPressEvent(QKeyEvent *event)
+{
+ if ((windowType() == Qt::Popup) && event->key() == Qt::Key_Escape) {
+ event->accept();
+ close();
+ } else {
+ event->ignore();
+ }
+}
+
+/*!
+ This event handler, for event \a event, can be reimplemented in a
+ subclass to receive key release events for the widget.
+
+ A widget must \link setFocusPolicy() accept focus\endlink
+ initially and \link hasFocus() have focus\endlink in order to
+ receive a key release event.
+
+ If you reimplement this handler, it is very important that you
+ call the base class implementation if you do not act upon the key.
+
+ The default implementation ignores the event, so that the widget's
+ parent can interpret it.
+
+ Note that QKeyEvent starts with isAccepted() == true, so you do not
+ need to call QKeyEvent::accept() - just do not call the base class
+ implementation if you act upon the key.
+
+ \sa keyPressEvent(), QKeyEvent::ignore(), setFocusPolicy(),
+ focusInEvent(), focusOutEvent(), event(), QKeyEvent
+*/
+
+void QWidget::keyReleaseEvent(QKeyEvent *event)
+{
+ event->ignore();
+}
+
+/*!
+ \fn void QWidget::focusInEvent(QFocusEvent *event)
+
+ This event handler can be reimplemented in a subclass to receive
+ keyboard focus events (focus received) for the widget. The event
+ is passed in the \a event parameter
+
+ A widget normally must setFocusPolicy() to something other than
+ Qt::NoFocus in order to receive focus events. (Note that the
+ application programmer can call setFocus() on any widget, even
+ those that do not normally accept focus.)
+
+ The default implementation updates the widget (except for windows
+ that do not specify a focusPolicy()).
+
+ \sa focusOutEvent(), setFocusPolicy(), keyPressEvent(),
+ keyReleaseEvent(), event(), QFocusEvent
+*/
+
+void QWidget::focusInEvent(QFocusEvent *)
+{
+ if (focusPolicy() != Qt::NoFocus || !isWindow()) {
+ update();
+ }
+}
+
+/*!
+ \fn void QWidget::focusOutEvent(QFocusEvent *event)
+
+ This event handler can be reimplemented in a subclass to receive
+ keyboard focus events (focus lost) for the widget. The events is
+ passed in the \a event parameter.
+
+ A widget normally must setFocusPolicy() to something other than
+ Qt::NoFocus in order to receive focus events. (Note that the
+ application programmer can call setFocus() on any widget, even
+ those that do not normally accept focus.)
+
+ The default implementation updates the widget (except for windows
+ that do not specify a focusPolicy()).
+
+ \sa focusInEvent(), setFocusPolicy(), keyPressEvent(),
+ keyReleaseEvent(), event(), QFocusEvent
+*/
+
+void QWidget::focusOutEvent(QFocusEvent *)
+{
+ if (focusPolicy() != Qt::NoFocus || !isWindow())
+ update();
+}
+
+/*!
+ \fn void QWidget::enterEvent(QEvent *event)
+
+ This event handler can be reimplemented in a subclass to receive
+ widget enter events which are passed in the \a event parameter.
+
+ An event is sent to the widget when the mouse cursor enters the
+ widget.
+
+ \sa leaveEvent(), mouseMoveEvent(), event()
+*/
+
+void QWidget::enterEvent(QEvent *)
+{
+}
+
+/*!
+ \fn void QWidget::leaveEvent(QEvent *event)
+
+ This event handler can be reimplemented in a subclass to receive
+ widget leave events which are passed in the \a event parameter.
+
+ A leave event is sent to the widget when the mouse cursor leaves
+ the widget.
+
+ \sa enterEvent(), mouseMoveEvent(), event()
+*/
+
+void QWidget::leaveEvent(QEvent *)
+{
+}
+
+/*!
+ \fn void QWidget::paintEvent(QPaintEvent *event)
+
+ This event handler can be reimplemented in a subclass to receive paint
+ events passed in \a event.
+
+ A paint event is a request to repaint all or part of a widget. It can
+ happen for one of the following reasons:
+
+ \list
+ \o repaint() or update() was invoked,
+ \o the widget was obscured and has now been uncovered, or
+ \o many other reasons.
+ \endlist
+
+ Many widgets can simply repaint their entire surface when asked to, but
+ some slow widgets need to optimize by painting only the requested region:
+ QPaintEvent::region(). This speed optimization does not change the result,
+ as painting is clipped to that region during event processing. QListView
+ and QTableView do this, for example.
+
+ Qt also tries to speed up painting by merging multiple paint events into
+ one. When update() is called several times or the window system sends
+ several paint events, Qt merges these events into one event with a larger
+ region (see QRegion::united()). The repaint() function does not permit this
+ optimization, so we suggest using update() whenever possible.
+
+ When the paint event occurs, the update region has normally been erased, so
+ you are painting on the widget's background.
+
+ The background can be set using setBackgroundRole() and setPalette().
+
+ Since Qt 4.0, QWidget automatically double-buffers its painting, so there
+ is no need to write double-buffering code in paintEvent() to avoid flicker.
+
+ \bold{Note for the X11 platform}: It is possible to toggle global double
+ buffering by calling \c qt_x11_set_global_double_buffer(). For example,
+
+ \snippet doc/src/snippets/code/src_gui_kernel_qwidget.cpp 14
+
+ \note Generally, you should refrain from calling update() or repaint()
+ \bold{inside} a paintEvent(). For example, calling update() or repaint() on
+ children inside a paintevent() results in undefined behavior; the child may
+ or may not get a paint event.
+
+ \warning If you are using a custom paint engine without Qt's backingstore,
+ Qt::WA_PaintOnScreen must be set. Otherwise, QWidget::paintEngine() will
+ never be called; the backingstore will be used instead.
+
+ \sa event(), repaint(), update(), QPainter, QPixmap, QPaintEvent,
+ {Analog Clock Example}
+*/
+
+void QWidget::paintEvent(QPaintEvent *)
+{
+}
+
+
+/*!
+ \fn void QWidget::moveEvent(QMoveEvent *event)
+
+ This event handler can be reimplemented in a subclass to receive
+ widget move events which are passed in the \a event parameter.
+ When the widget receives this event, it is already at the new
+ position.
+
+ The old position is accessible through QMoveEvent::oldPos().
+
+ \sa resizeEvent(), event(), move(), QMoveEvent
+*/
+
+void QWidget::moveEvent(QMoveEvent *)
+{
+}
+
+
+/*!
+ This event handler can be reimplemented in a subclass to receive
+ widget resize events which are passed in the \a event parameter.
+ When resizeEvent() is called, the widget already has its new
+ geometry. The old size is accessible through
+ QResizeEvent::oldSize().
+
+ The widget will be erased and receive a paint event immediately
+ after processing the resize event. No drawing need be (or should
+ be) done inside this handler.
+
+
+ \sa moveEvent(), event(), resize(), QResizeEvent, paintEvent(),
+ {Scribble Example}
+*/
+
+void QWidget::resizeEvent(QResizeEvent * /* event */)
+{
+}
+
+#ifndef QT_NO_ACTION
+/*!
+ \fn void QWidget::actionEvent(QActionEvent *event)
+
+ This event handler is called with the given \a event whenever the
+ widget's actions are changed.
+
+ \sa addAction(), insertAction(), removeAction(), actions(), QActionEvent
+*/
+void QWidget::actionEvent(QActionEvent *)
+{
+
+}
+#endif
+
+/*!
+ This event handler is called with the given \a event when Qt receives a window
+ close request for a top-level widget from the window system.
+
+ By default, the event is accepted and the widget is closed. You can reimplement
+ this function to change the way the widget responds to window close requests.
+ For example, you can prevent the window from closing by calling \l{QEvent::}{ignore()}
+ on all events.
+
+ Main window applications typically use reimplementations of this function to check
+ whether the user's work has been saved and ask for permission before closing.
+ For example, the \l{Application Example} uses a helper function to determine whether
+ or not to close the window:
+
+ \snippet mainwindows/application/mainwindow.cpp 3
+ \snippet mainwindows/application/mainwindow.cpp 4
+
+ \sa event(), hide(), close(), QCloseEvent, {Application Example}
+*/
+
+void QWidget::closeEvent(QCloseEvent *event)
+{
+ event->accept();
+}
+
+#ifndef QT_NO_CONTEXTMENU
+/*!
+ This event handler, for event \a event, can be reimplemented in a
+ subclass to receive widget context menu events.
+
+ The handler is called when the widget's \l contextMenuPolicy is
+ Qt::DefaultContextMenu.
+
+ The default implementation ignores the context event.
+ See the \l QContextMenuEvent documentation for more details.
+
+ \sa event(), QContextMenuEvent customContextMenuRequested()
+*/
+
+void QWidget::contextMenuEvent(QContextMenuEvent *event)
+{
+ event->ignore();
+}
+#endif // QT_NO_CONTEXTMENU
+
+
+/*!
+ This event handler, for event \a event, can be reimplemented in a
+ subclass to receive Input Method composition events. This handler
+ is called when the state of the input method changes.
+
+ Note that when creating custom text editing widgets, the
+ Qt::WA_InputMethodEnabled window attribute must be set explicitly
+ (using the setAttribute() function) in order to receive input
+ method events.
+
+ The default implementation calls event->ignore(), which rejects the
+ Input Method event. See the \l QInputMethodEvent documentation for more
+ details.
+
+ \sa event(), QInputMethodEvent
+*/
+void QWidget::inputMethodEvent(QInputMethodEvent *event)
+{
+ event->ignore();
+}
+
+/*!
+ This method is only relevant for input widgets. It is used by the
+ input method to query a set of properties of the widget to be
+ able to support complex input method operations as support for
+ surrounding text and reconversions.
+
+ \a query specifies which property is queried.
+
+ \sa inputMethodEvent(), QInputMethodEvent, QInputContext
+*/
+QVariant QWidget::inputMethodQuery(Qt::InputMethodQuery query) const
+{
+ switch(query) {
+ case Qt::ImMicroFocus:
+ return QRect(width()/2, 0, 1, height());
+ case Qt::ImFont:
+ return font();
+ default:
+ return QVariant();
+ }
+}
+
+#ifndef QT_NO_DRAGANDDROP
+
+/*!
+ \fn void QWidget::dragEnterEvent(QDragEnterEvent *event)
+
+ This event handler is called when a drag is in progress and the
+ mouse enters this widget. The event is passed in the \a event parameter.
+
+ If the event is ignored, the widget won't receive any \l{dragMoveEvent()}{drag
+ move events}.
+
+ See the \link dnd.html Drag-and-drop documentation\endlink for an
+ overview of how to provide drag-and-drop in your application.
+
+ \sa QDrag, QDragEnterEvent
+*/
+void QWidget::dragEnterEvent(QDragEnterEvent *)
+{
+}
+
+/*!
+ \fn void QWidget::dragMoveEvent(QDragMoveEvent *event)
+
+ This event handler is called if a drag is in progress, and when
+ any of the following conditions occur: the cursor enters this widget,
+ the cursor moves within this widget, or a modifier key is pressed on
+ the keyboard while this widget has the focus. The event is passed
+ in the \a event parameter.
+
+ See the \link dnd.html Drag-and-drop documentation\endlink for an
+ overview of how to provide drag-and-drop in your application.
+
+ \sa QDrag, QDragMoveEvent
+*/
+void QWidget::dragMoveEvent(QDragMoveEvent *)
+{
+}
+
+/*!
+ \fn void QWidget::dragLeaveEvent(QDragLeaveEvent *event)
+
+ This event handler is called when a drag is in progress and the
+ mouse leaves this widget. The event is passed in the \a event
+ parameter.
+
+ See the \link dnd.html Drag-and-drop documentation\endlink for an
+ overview of how to provide drag-and-drop in your application.
+
+ \sa QDrag, QDragLeaveEvent
+*/
+void QWidget::dragLeaveEvent(QDragLeaveEvent *)
+{
+}
+
+/*!
+ \fn void QWidget::dropEvent(QDropEvent *event)
+
+ This event handler is called when the drag is dropped on this
+ widget. The event is passed in the \a event parameter.
+
+ See the \link dnd.html Drag-and-drop documentation\endlink for an
+ overview of how to provide drag-and-drop in your application.
+
+ \sa QDrag, QDropEvent
+*/
+void QWidget::dropEvent(QDropEvent *)
+{
+}
+
+#endif // QT_NO_DRAGANDDROP
+
+/*!
+ \fn void QWidget::showEvent(QShowEvent *event)
+
+ This event handler can be reimplemented in a subclass to receive
+ widget show events which are passed in the \a event parameter.
+
+ Non-spontaneous show events are sent to widgets immediately
+ before they are shown. The spontaneous show events of windows are
+ delivered afterwards.
+
+ Note: A widget receives spontaneous show and hide events when its
+ mapping status is changed by the window system, e.g. a spontaneous
+ hide event when the user minimizes the window, and a spontaneous
+ show event when the window is restored again. After receiving a
+ spontaneous hide event, a widget is still considered visible in
+ the sense of isVisible().
+
+ \sa visible, event(), QShowEvent
+*/
+void QWidget::showEvent(QShowEvent *)
+{
+}
+
+/*!
+ \fn void QWidget::hideEvent(QHideEvent *event)
+
+ This event handler can be reimplemented in a subclass to receive
+ widget hide events. The event is passed in the \a event parameter.
+
+ Hide events are sent to widgets immediately after they have been
+ hidden.
+
+ Note: A widget receives spontaneous show and hide events when its
+ mapping status is changed by the window system, e.g. a spontaneous
+ hide event when the user minimizes the window, and a spontaneous
+ show event when the window is restored again. After receiving a
+ spontaneous hide event, a widget is still considered visible in
+ the sense of isVisible().
+
+ \sa visible, event(), QHideEvent
+*/
+void QWidget::hideEvent(QHideEvent *)
+{
+}
+
+/*
+ \fn QWidget::x11Event(MSG *)
+
+ This special event handler can be reimplemented in a subclass to receive
+ native X11 events.
+
+ In your reimplementation of this function, if you want to stop Qt from
+ handling the event, return true. If you return false, this native event
+ is passed back to Qt, which translates it into a Qt event and sends it to
+ the widget.
+
+ \note Events are only delivered to this event handler if the widget is
+ native.
+
+ \warning This function is not portable.
+
+ \sa QApplication::x11EventFilter(), QWidget::winId()
+*/
+
+
+#if defined(Q_WS_MAC)
+
+/*!
+ \fn bool QWidget::macEvent(EventHandlerCallRef caller, EventRef event)
+
+ This special event handler can be reimplemented in a subclass to
+ receive native Macintosh events.
+
+ The parameters are a bit different depending if Qt is build against Carbon
+ or Cocoa. In Carbon, \a caller and \a event are the corresponding
+ EventHandlerCallRef and EventRef that correspond to the Carbon event
+ handlers that are installed. In Cocoa, \a caller is always 0 and the
+ EventRef is the EventRef generated from the NSEvent.
+
+ In your reimplementation of this function, if you want to stop the
+ event being handled by Qt, return true. If you return false, this
+ native event is passed back to Qt, which translates the event into
+ a Qt event and sends it to the widget.
+
+ \warning This function is not portable.
+
+ \warning This function was not called inside of Qt until Qt 4.4.
+ If you need compatibility with earlier versions of Qt, consider QApplication::macEventFilter() instead.
+
+ \sa QApplication::macEventFilter()
+*/
+
+bool QWidget::macEvent(EventHandlerCallRef, EventRef)
+{
+ return false;
+}
+
+#endif
+#if defined(Q_WS_WIN)
+
+/*!
+ This special event handler can be reimplemented in a subclass to
+ receive native Windows events which are passed in the \a message
+ parameter.
+
+ In your reimplementation of this function, if you want to stop the
+ event being handled by Qt, return true and set \a result to the value
+ that the window procedure should return. If you return false, this
+ native event is passed back to Qt, which translates the event into
+ a Qt event and sends it to the widget.
+
+ \warning This function is not portable.
+
+ \sa QApplication::winEventFilter()
+*/
+bool QWidget::winEvent(MSG *message, long *result)
+{
+ Q_UNUSED(message);
+ Q_UNUSED(result);
+ return false;
+}
+
+#endif
+#if defined(Q_WS_X11)
+
+/*!
+ \fn bool QWidget::x11Event(XEvent *event)
+
+ This special event handler can be reimplemented in a subclass to receive
+ native X11 events passed in the \a event parameter.
+
+ In your reimplementation of this function, if you want to stop Qt from
+ handling the event, return true. If you return false, this native event
+ is passed back to Qt, which translates it into a Qt event and sends it to
+ the widget.
+
+ \note Events are only delivered to this event handler if the widget is
+ native.
+
+ \warning This function is not portable.
+
+ \sa QApplication::x11EventFilter(), QWidget::winId()
+*/
+bool QWidget::x11Event(XEvent *)
+{
+ return false;
+}
+
+#endif
+#if defined(Q_WS_QWS)
+
+/*!
+ \fn bool QWidget::qwsEvent(QWSEvent *event)
+
+ This special event handler can be reimplemented in a subclass to
+ receive native Qt for Embedded Linux events which are passed in the
+ \a event parameter.
+
+ In your reimplementation of this function, if you want to stop the
+ event being handled by Qt, return true. If you return false, this
+ native event is passed back to Qt, which translates the event into
+ a Qt event and sends it to the widget.
+
+ \warning This function is not portable.
+
+ \sa QApplication::qwsEventFilter()
+*/
+bool QWidget::qwsEvent(QWSEvent *)
+{
+ return false;
+}
+
+#endif
+
+
+/*!
+ Ensures that the widget has been polished by QStyle (i.e., has a
+ proper font and palette).
+
+ QWidget calls this function after it has been fully constructed
+ but before it is shown the very first time. You can call this
+ function if you want to ensure that the widget is polished before
+ doing an operation, e.g., the correct font size might be needed in
+ the widget's sizeHint() reimplementation. Note that this function
+ \e is called from the default implementation of sizeHint().
+
+ Polishing is useful for final initialization that must happen after
+ all constructors (from base classes as well as from subclasses)
+ have been called.
+
+ If you need to change some settings when a widget is polished,
+ reimplement event() and handle the QEvent::Polish event type.
+
+ \bold{Note:} The function is declared const so that it can be called from
+ other const functions (e.g., sizeHint()).
+
+ \sa event()
+*/
+void QWidget::ensurePolished() const
+{
+ Q_D(const QWidget);
+
+ const QMetaObject *m = metaObject();
+ if (m == d->polished)
+ return;
+ d->polished = m;
+
+ QEvent e(QEvent::Polish);
+ QCoreApplication::sendEvent(const_cast<QWidget *>(this), &e);
+
+ // polish children after 'this'
+ QList<QObject*> children = d->children;
+ for (int i = 0; i < children.size(); ++i) {
+ QObject *o = children.at(i);
+ if(!o->isWidgetType())
+ continue;
+ if (QWidget *w = qobject_cast<QWidget *>(o))
+ w->ensurePolished();
+ }
+
+ if (d->parent && d->sendChildEvents) {
+ QChildEvent e(QEvent::ChildPolished, const_cast<QWidget *>(this));
+ QCoreApplication::sendEvent(d->parent, &e);
+ }
+}
+
+/*!
+ Returns the mask currently set on a widget. If no mask is set the
+ return value will be an empty region.
+
+ \sa setMask(), clearMask(), QRegion::isEmpty(), {Shaped Clock Example}
+*/
+QRegion QWidget::mask() const
+{
+ Q_D(const QWidget);
+ return d->extra ? d->extra->mask : QRegion();
+}
+
+/*!
+ Returns the layout manager that is installed on this widget, or 0
+ if no layout manager is installed.
+
+ The layout manager sets the geometry of the widget's children
+ that have been added to the layout.
+
+ \sa setLayout(), sizePolicy(), {Layout Classes}
+*/
+QLayout *QWidget::layout() const
+{
+ return d_func()->layout;
+}
+
+
+/*!
+ \fn void QWidget::setLayout(QLayout *layout)
+
+ Sets the layout manager for this widget to \a layout.
+
+ If there already is a layout manager installed on this widget,
+ QWidget won't let you install another. You must first delete the
+ existing layout manager (returned by layout()) before you can
+ call setLayout() with the new layout.
+
+ If \a layout is the layout manger on a different widget, setLayout()
+ will reparent the layout and make it the layout manager for this widget.
+
+ Example:
+
+ \snippet examples/uitools/textfinder/textfinder.cpp 3b
+
+ An alternative to calling this function is to pass this widget to
+ the layout's constructor.
+
+ The QWidget will take ownership of \a layout.
+
+ \sa layout(), {Layout Classes}
+*/
+
+void QWidget::setLayout(QLayout *l)
+{
+ if (!l) {
+ qWarning("QWidget::setLayout: Cannot set layout to 0");
+ return;
+ }
+ if (layout()) {
+ if (layout() != l)
+ qWarning("QWidget::setLayout: Attempting to set QLayout \"%s\" on %s \"%s\", which already has a"
+ " layout", l->objectName().toLocal8Bit().data(), metaObject()->className(),
+ objectName().toLocal8Bit().data());
+ return;
+ }
+
+ QObject *oldParent = l->parent();
+ if (oldParent && oldParent != this) {
+ if (oldParent->isWidgetType()) {
+ // Steal the layout off a widget parent. Takes effect when
+ // morphing laid-out container widgets in Designer.
+ QWidget *oldParentWidget = static_cast<QWidget *>(oldParent);
+ oldParentWidget->takeLayout();
+ } else {
+ qWarning("QWidget::setLayout: Attempting to set QLayout \"%s\" on %s \"%s\", when the QLayout already has a parent",
+ l->objectName().toLocal8Bit().data(), metaObject()->className(),
+ objectName().toLocal8Bit().data());
+ return;
+ }
+ }
+
+ Q_D(QWidget);
+ l->d_func()->topLevel = true;
+ d->layout = l;
+ if (oldParent != this) {
+ l->setParent(this);
+ l->d_func()->reparentChildWidgets(this);
+ l->invalidate();
+ }
+
+ if (isWindow() && d->maybeTopData())
+ d->topData()->sizeAdjusted = false;
+}
+
+/*!
+ \fn QLayout *QWidget::takeLayout()
+
+ Remove the layout from the widget.
+ \since 4.5
+*/
+
+QLayout *QWidget::takeLayout()
+{
+ Q_D(QWidget);
+ QLayout *l = layout();
+ if (!l)
+ return 0;
+ d->layout = 0;
+ l->setParent(0);
+ return l;
+}
+
+/*!
+ \property QWidget::sizePolicy
+ \brief the default layout behavior of the widget
+
+ If there is a QLayout that manages this widget's children, the
+ size policy specified by that layout is used. If there is no such
+ QLayout, the result of this function is used.
+
+ The default policy is Preferred/Preferred, which means that the
+ widget can be freely resized, but prefers to be the size
+ sizeHint() returns. Button-like widgets set the size policy to
+ specify that they may stretch horizontally, but are fixed
+ vertically. The same applies to lineedit controls (such as
+ QLineEdit, QSpinBox or an editable QComboBox) and other
+ horizontally orientated widgets (such as QProgressBar).
+ QToolButton's are normally square, so they allow growth in both
+ directions. Widgets that support different directions (such as
+ QSlider, QScrollBar or QHeader) specify stretching in the
+ respective direction only. Widgets that can provide scroll bars
+ (usually subclasses of QScrollArea) tend to specify that they can
+ use additional space, and that they can make do with less than
+ sizeHint().
+
+ \sa sizeHint() QLayout QSizePolicy updateGeometry()
+*/
+QSizePolicy QWidget::sizePolicy() const
+{
+ Q_D(const QWidget);
+ return d->size_policy;
+}
+
+void QWidget::setSizePolicy(QSizePolicy policy)
+{
+ Q_D(QWidget);
+ setAttribute(Qt::WA_WState_OwnSizePolicy);
+ if (policy == d->size_policy)
+ return;
+ d->size_policy = policy;
+
+#ifndef QT_NO_GRAPHICSVIEW
+ if (QWExtra *extra = d->extra) {
+ if (extra->proxyWidget)
+ extra->proxyWidget->setSizePolicy(policy);
+ }
+#endif
+
+ updateGeometry();
+
+ if (isWindow() && d->maybeTopData())
+ d->topData()->sizeAdjusted = false;
+}
+
+/*!
+ \fn void QWidget::setSizePolicy(QSizePolicy::Policy horizontal, QSizePolicy::Policy vertical)
+ \overload
+
+ Sets the size policy of the widget to \a horizontal and \a
+ vertical, with standard stretch and no height-for-width.
+
+ \sa QSizePolicy::QSizePolicy()
+*/
+
+/*!
+ Returns the preferred height for this widget, given the width \a w.
+
+ If this widget has a layout, the default implementation returns
+ the layout's preferred height. if there is no layout, the default
+ implementation returns -1 indicating that the preferred height
+ does not depend on the width.
+*/
+
+int QWidget::heightForWidth(int w) const
+{
+ if (layout() && layout()->hasHeightForWidth())
+ return layout()->totalHeightForWidth(w);
+ return -1;
+}
+
+/*!
+ \fn QWidget *QWidget::childAt(int x, int y) const
+
+ Returns the visible child widget at the position (\a{x}, \a{y})
+ in the widget's coordinate system. If there is no visible child
+ widget at the specified position, the function returns 0.
+*/
+
+/*!
+ \overload
+
+ Returns the visible child widget at point \a p in the widget's own
+ coordinate system.
+*/
+
+QWidget *QWidget::childAt(const QPoint &p) const
+{
+ return d_func()->childAt_helper(p, false);
+}
+
+QWidget *QWidgetPrivate::childAt_helper(const QPoint &p, bool ignoreChildrenInDestructor) const
+{
+ Q_Q(const QWidget);
+ if (!q->rect().contains(p))
+ return 0;
+ for (int i = children.size(); i > 0 ;) {
+ --i;
+ QWidget *w = qobject_cast<QWidget *>(children.at(i));
+ if (w && !w->isWindow() && !w->isHidden() && w->geometry().contains(p)) {
+ if (ignoreChildrenInDestructor && w->data->in_destructor)
+ continue;
+ if (w->testAttribute(Qt::WA_TransparentForMouseEvents))
+ continue;
+ QPoint childPoint = w->mapFromParent(p);
+ if (QWidget *t = w->d_func()->childAt_helper(childPoint, ignoreChildrenInDestructor))
+ return t;
+ // if WMouseNoMask is set the widget mask is ignored, if
+ // the widget has no mask then the WMouseNoMask flag has no
+ // effect
+ if (w->testAttribute(Qt::WA_MouseNoMask) || w->mask().contains(childPoint)
+ || w->mask().isEmpty())
+ return w;
+ }
+ }
+ return 0;
+}
+
+void QWidgetPrivate::updateGeometry_helper(bool forceUpdate)
+{
+ Q_Q(QWidget);
+ if (widgetItem)
+ widgetItem->invalidateSizeCache();
+ QWidget *parent;
+ if (forceUpdate || !extra || extra->minw != extra->maxw || extra->minh != extra->maxh) {
+ if (!q->isWindow() && !q->isHidden() && (parent = q->parentWidget())) {
+ if (parent->d_func()->layout)
+ parent->d_func()->layout->invalidate();
+ else if (parent->isVisible())
+ QApplication::postEvent(parent, new QEvent(QEvent::LayoutRequest));
+ }
+ }
+}
+
+/*!
+ Notifies the layout system that this widget has changed and may
+ need to change geometry.
+
+ Call this function if the sizeHint() or sizePolicy() have changed.
+
+ For explicitly hidden widgets, updateGeometry() is a no-op. The
+ layout system will be notified as soon as the widget is shown.
+*/
+
+void QWidget::updateGeometry()
+{
+ Q_D(QWidget);
+ d->updateGeometry_helper(false);
+}
+
+/*! \property QWidget::windowFlags
+
+ Window flags are a combination of a type (e.g. Qt::Dialog) and
+ zero or more hints to the window system (e.g.
+ Qt::FramelessWindowHint).
+
+ If the widget had type Qt::Widget or Qt::SubWindow and becomes a
+ window (Qt::Window, Qt::Dialog, etc.), it is put at position (0,
+ 0) on the desktop. If the widget is a window and becomes a
+ Qt::Widget or Qt::SubWindow, it is put at position (0, 0)
+ relative to its parent widget.
+
+ \note This function calls setParent() when changing the flags for
+ a window, causing the widget to be hidden. You must call show() to make
+ the widget visible again..
+
+ \sa windowType(), {Window Flags Example}
+*/
+void QWidget::setWindowFlags(Qt::WindowFlags flags)
+{
+ if (data->window_flags == flags)
+ return;
+
+ Q_D(QWidget);
+
+ if ((data->window_flags | flags) & Qt::Window) {
+ // the old type was a window and/or the new type is a window
+ QPoint oldPos = pos();
+ bool visible = isVisible();
+ setParent(parentWidget(), flags);
+
+ // if both types are windows or neither of them are, we restore
+ // the old position
+ if (!((data->window_flags ^ flags) & Qt::Window)
+ && (visible || testAttribute(Qt::WA_Moved))) {
+ move(oldPos);
+ }
+ // for backward-compatibility we change Qt::WA_QuitOnClose attribute value only when the window was recreated.
+ d->adjustQuitOnCloseAttribute();
+ } else {
+ data->window_flags = flags;
+ }
+}
+
+/*!
+ Sets the window flags for the widget to \a flags,
+ \e without telling the window system.
+
+ \warning Do not call this function unless you really know what
+ you're doing.
+
+ \sa setWindowFlags()
+*/
+void QWidget::overrideWindowFlags(Qt::WindowFlags flags)
+{
+ data->window_flags = flags;
+}
+
+/*!
+ \fn Qt::WindowType QWidget::windowType() const
+
+ Returns the window type of this widget. This is identical to
+ windowFlags() & Qt::WindowType_Mask.
+
+ \sa windowFlags
+*/
+
+/*!
+ Sets the parent of the widget to \a parent, and resets the window
+ flags. The widget is moved to position (0, 0) in its new parent.
+
+ If the new parent widget is in a different window, the
+ reparented widget and its children are appended to the end of the
+ \l{setFocusPolicy()}{tab chain} of the new parent
+ widget, in the same internal order as before. If one of the moved
+ widgets had keyboard focus, setParent() calls clearFocus() for that
+ widget.
+
+ If the new parent widget is in the same window as the
+ old parent, setting the parent doesn't change the tab order or
+ keyboard focus.
+
+ If the "new" parent widget is the old parent widget, this function
+ does nothing.
+
+ \note The widget becomes invisible as part of changing its parent,
+ even if it was previously visible. You must call show() to make the
+ widget visible again.
+
+ \warning It is very unlikely that you will ever need this
+ function. If you have a widget that changes its content
+ dynamically, it is far easier to use \l QStackedWidget.
+
+ \sa setWindowFlags()
+*/
+void QWidget::setParent(QWidget *parent)
+{
+ if (parent == parentWidget())
+ return;
+ setParent((QWidget*)parent, windowFlags() & ~Qt::WindowType_Mask);
+}
+
+/*!
+ \overload
+
+ This function also takes widget flags, \a f as an argument.
+*/
+
+void QWidget::setParent(QWidget *parent, Qt::WindowFlags f)
+{
+ Q_D(QWidget);
+ bool resized = testAttribute(Qt::WA_Resized);
+ bool wasCreated = testAttribute(Qt::WA_WState_Created);
+ QWidget *oldtlw = window();
+
+ QWidget *desktopWidget = 0;
+ if (parent && parent->windowType() == Qt::Desktop)
+ desktopWidget = parent;
+ bool newParent = (parent != parentWidget()) || !wasCreated || desktopWidget;
+
+#if defined(Q_WS_X11) || defined(Q_WS_WIN)
+ if (newParent && parent && !desktopWidget) {
+ if (testAttribute(Qt::WA_NativeWindow) && !qApp->testAttribute(Qt::AA_DontCreateNativeWidgetSiblings))
+ parent->d_func()->enforceNativeChildren();
+ else if (parent->d_func()->nativeChildrenForced() || parent->testAttribute(Qt::WA_PaintOnScreen))
+ setAttribute(Qt::WA_NativeWindow);
+ }
+#endif
+
+ if (wasCreated) {
+ if (!testAttribute(Qt::WA_WState_Hidden)) {
+ hide();
+ setAttribute(Qt::WA_WState_ExplicitShowHide, false);
+ }
+ if (newParent) {
+ QEvent e(QEvent::ParentAboutToChange);
+ QApplication::sendEvent(this, &e);
+ }
+ }
+ if (newParent && isAncestorOf(focusWidget()))
+ focusWidget()->clearFocus();
+
+ d->setParent_sys(parent, f);
+ if (desktopWidget)
+ parent = 0;
+
+#ifdef Q_BACKINGSTORE_SUBSURFACES
+ QTLWExtra *extra = d->maybeTopData();
+ QWindowSurface *windowSurface = (extra ? extra->windowSurface : 0);
+ if (newParent && windowSurface) {
+ QWidgetBackingStore *oldBs = oldtlw->d_func()->maybeBackingStore();
+ if (oldBs)
+ oldBs->subSurfaces.removeAll(windowSurface);
+
+ if (parent) {
+ QWidgetBackingStore *newBs = parent->d_func()->maybeBackingStore();
+ if (newBs)
+ newBs->subSurfaces.append(windowSurface);
+ }
+ }
+#endif
+
+ if (newParent) {
+ if (QWidgetBackingStore *oldBs = oldtlw->d_func()->maybeBackingStore()) {
+ oldBs->removeDirtyWidget(this);
+ // Move the widget and all its static children from
+ // the old backing store to the new one.
+ oldBs->moveStaticWidgets(this);
+ }
+ }
+
+ if ((QApplicationPrivate::app_compile_version < 0x040200
+ || QApplicationPrivate::testAttribute(Qt::AA_ImmediateWidgetCreation))
+ && !testAttribute(Qt::WA_WState_Created))
+ create();
+
+ d->reparentFocusWidgets(oldtlw);
+ setAttribute(Qt::WA_Resized, resized);
+ if (!testAttribute(Qt::WA_StyleSheet)
+ && (!parent || !parent->testAttribute(Qt::WA_StyleSheet))) {
+ d->resolveFont();
+ d->resolvePalette();
+ }
+ d->resolveLayoutDirection();
+ d->resolveLocale();
+
+ // Note: GL widgets under Windows will always need a ParentChange
+ // event to handle recreation/rebinding of the GL context, hence
+ // the (f & Qt::MSWindowsOwnDC) clause
+ if (newParent
+#ifdef Q_WS_WIN
+ || (f & Qt::MSWindowsOwnDC)
+#endif
+ ) {
+ // propagate enabled updates enabled state to non-windows
+ if (!isWindow()) {
+ if (!testAttribute(Qt::WA_ForceDisabled))
+ d->setEnabled_helper(parent ? parent->isEnabled() : true);
+ if (!testAttribute(Qt::WA_ForceUpdatesDisabled))
+ d->setUpdatesEnabled_helper(parent ? parent->updatesEnabled() : true);
+ }
+ d->inheritStyle();
+
+ // send and post remaining QObject events
+ if (parent && d->sendChildEvents) {
+ QChildEvent e(QEvent::ChildAdded, this);
+ QApplication::sendEvent(parent, &e);
+#ifdef QT3_SUPPORT
+ if (parent->d_func()->pendingChildInsertedEvents.isEmpty()) {
+ QApplication::postEvent(parent,
+ new QEvent(QEvent::ChildInsertedRequest),
+ Qt::HighEventPriority);
+ }
+ parent->d_func()->pendingChildInsertedEvents.append(this);
+#endif
+ }
+
+//### already hidden above ---> must probably do something smart on the mac
+// #ifdef Q_WS_MAC
+// extern bool qt_mac_is_macdrawer(const QWidget *); //qwidget_mac.cpp
+// if(!qt_mac_is_macdrawer(q)) //special case
+// q->setAttribute(Qt::WA_WState_Hidden);
+// #else
+// q->setAttribute(Qt::WA_WState_Hidden);
+//#endif
+
+ if (parent && d->sendChildEvents && d->polished) {
+ QChildEvent e(QEvent::ChildPolished, this);
+ QCoreApplication::sendEvent(parent, &e);
+ }
+
+ QEvent e(QEvent::ParentChange);
+ QApplication::sendEvent(this, &e);
+ }
+
+ if (!wasCreated) {
+ if (isWindow() || parentWidget()->isVisible())
+ setAttribute(Qt::WA_WState_Hidden, true);
+ else if (!testAttribute(Qt::WA_WState_ExplicitShowHide))
+ setAttribute(Qt::WA_WState_Hidden, false);
+ }
+
+ d->updateIsOpaque();
+
+#ifndef QT_NO_GRAPHICSVIEW
+ // Embed the widget into a proxy if the parent is embedded.
+ // ### Doesn't handle reparenting out of an embedded widget.
+ if (oldtlw->graphicsProxyWidget()) {
+ if (QGraphicsProxyWidget *ancestorProxy = d->nearestGraphicsProxyWidget(oldtlw))
+ ancestorProxy->d_func()->unembedSubWindow(this);
+ }
+ if (isWindow() && parent && !graphicsProxyWidget() && !bypassGraphicsProxyWidget(this)) {
+ if (QGraphicsProxyWidget *ancestorProxy = d->nearestGraphicsProxyWidget(parent))
+ ancestorProxy->d_func()->embedSubWindow(this);
+ }
+#endif
+}
+
+/*!
+ Scrolls the widget including its children \a dx pixels to the
+ right and \a dy downward. Both \a dx and \a dy may be negative.
+
+ After scrolling, the widgets will receive paint events for
+ the areas that need to be repainted. For widgets that Qt knows to
+ be opaque, this is only the newly exposed parts.
+ For example, if an opaque widget is scrolled 8 pixels to the left,
+ only an 8-pixel wide stripe at the right edge needs updating.
+
+ Since widgets propagate the contents of their parents by default,
+ you need to set the \l autoFillBackground property, or use
+ setAttribute() to set the Qt::WA_OpaquePaintEvent attribute, to make
+ a widget opaque.
+
+ For widgets that use contents propagation, a scroll will cause an
+ update of the entire scroll area.
+
+ \sa {Transparency and Double Buffering}
+*/
+
+void QWidget::scroll(int dx, int dy)
+{
+ if ((!updatesEnabled() && children().size() == 0) || !isVisible())
+ return;
+ if (dx == 0 && dy == 0)
+ return;
+ Q_D(QWidget);
+#ifndef QT_NO_GRAPHICSVIEW
+ if (QGraphicsProxyWidget *proxy = QWidgetPrivate::nearestGraphicsProxyWidget(this)) {
+ // Graphics View maintains its own dirty region as a list of rects;
+ // until we can connect item updates directly to the view, we must
+ // separately add a translated dirty region.
+ if (!d->dirty.isEmpty()) {
+ foreach (const QRect &rect, (d->dirty.translated(dx, dy)).rects())
+ proxy->update(rect);
+ }
+ proxy->scroll(dx, dy, proxy->subWidgetRect(this));
+ return;
+ }
+#endif
+ d->setDirtyOpaqueRegion();
+ d->scroll_sys(dx, dy);
+}
+
+/*!
+ \overload
+
+ This version only scrolls \a r and does not move the children of
+ the widget.
+
+ If \a r is empty or invalid, the result is undefined.
+
+ \sa QScrollArea
+*/
+void QWidget::scroll(int dx, int dy, const QRect &r)
+{
+
+ if ((!updatesEnabled() && children().size() == 0) || !isVisible())
+ return;
+ if (dx == 0 && dy == 0)
+ return;
+ Q_D(QWidget);
+#ifndef QT_NO_GRAPHICSVIEW
+ if (QGraphicsProxyWidget *proxy = QWidgetPrivate::nearestGraphicsProxyWidget(this)) {
+ // Graphics View maintains its own dirty region as a list of rects;
+ // until we can connect item updates directly to the view, we must
+ // separately add a translated dirty region.
+ if (!d->dirty.isEmpty()) {
+ foreach (const QRect &rect, (d->dirty.translated(dx, dy) & r).rects())
+ proxy->update(rect);
+ }
+ proxy->scroll(dx, dy, r.translated(proxy->subWidgetRect(this).topLeft().toPoint()));
+ return;
+ }
+#endif
+ d->scroll_sys(dx, dy, r);
+}
+
+/*!
+ Repaints the widget directly by calling paintEvent() immediately,
+ unless updates are disabled or the widget is hidden.
+
+ We suggest only using repaint() if you need an immediate repaint,
+ for example during animation. In almost all circumstances update()
+ is better, as it permits Qt to optimize for speed and minimize
+ flicker.
+
+ \warning If you call repaint() in a function which may itself be
+ called from paintEvent(), you may get infinite recursion. The
+ update() function never causes recursion.
+
+ \sa update(), paintEvent(), setUpdatesEnabled()
+*/
+
+void QWidget::repaint()
+{
+ repaint(rect());
+}
+
+/*! \overload
+
+ This version repaints a rectangle (\a x, \a y, \a w, \a h) inside
+ the widget.
+
+ If \a w is negative, it is replaced with \c{width() - x}, and if
+ \a h is negative, it is replaced width \c{height() - y}.
+*/
+void QWidget::repaint(int x, int y, int w, int h)
+{
+ if (x > data->crect.width() || y > data->crect.height())
+ return;
+
+ if (w < 0)
+ w = data->crect.width() - x;
+ if (h < 0)
+ h = data->crect.height() - y;
+
+ repaint(QRect(x, y, w, h));
+}
+
+/*! \overload
+
+ This version repaints a rectangle \a rect inside the widget.
+*/
+void QWidget::repaint(const QRect &rect)
+{
+ Q_D(QWidget);
+
+ if (testAttribute(Qt::WA_WState_ConfigPending)) {
+ update(rect);
+ return;
+ }
+
+ if (!isVisible() || !updatesEnabled() || rect.isEmpty())
+ return;
+
+ QTLWExtra *tlwExtra = !d->paintOnScreen() ? window()->d_func()->maybeTopData() : 0;
+ if (tlwExtra && !tlwExtra->inTopLevelResize && tlwExtra->backingStore) {
+ tlwExtra->inRepaint = true;
+ tlwExtra->backingStore->markDirty(rect, this, true);
+ tlwExtra->inRepaint = false;
+ } else {
+ d->repaint_sys(rect);
+ }
+}
+
+/*!
+ \overload
+
+ This version repaints a region \a rgn inside the widget.
+*/
+void QWidget::repaint(const QRegion &rgn)
+{
+ Q_D(QWidget);
+
+ if (testAttribute(Qt::WA_WState_ConfigPending)) {
+ update(rgn);
+ return;
+ }
+
+ if (!isVisible() || !updatesEnabled() || rgn.isEmpty())
+ return;
+
+ QTLWExtra *tlwExtra = !d->paintOnScreen() ? window()->d_func()->maybeTopData() : 0;
+ if (tlwExtra && !tlwExtra->inTopLevelResize && tlwExtra->backingStore) {
+ tlwExtra->inRepaint = true;
+ tlwExtra->backingStore->markDirty(rgn, this, true);
+ tlwExtra->inRepaint = false;
+ } else {
+ d->repaint_sys(rgn);
+ }
+}
+
+/*!
+ Updates the widget unless updates are disabled or the widget is
+ hidden.
+
+ This function does not cause an immediate repaint; instead it
+ schedules a paint event for processing when Qt returns to the main
+ event loop. This permits Qt to optimize for more speed and less
+ flicker than a call to repaint() does.
+
+ Calling update() several times normally results in just one
+ paintEvent() call.
+
+ Qt normally erases the widget's area before the paintEvent() call.
+ If the Qt::WA_OpaquePaintEvent widget attribute is set, the widget is
+ responsible for painting all its pixels with an opaque color.
+
+ \sa repaint() paintEvent(), setUpdatesEnabled(), {Analog Clock Example}
+*/
+void QWidget::update()
+{
+ update(rect());
+}
+
+/*! \fn void QWidget::update(int x, int y, int w, int h)
+ \overload
+
+ This version updates a rectangle (\a x, \a y, \a w, \a h) inside
+ the widget.
+*/
+
+/*!
+ \overload
+
+ This version updates a rectangle \a rect inside the widget.
+*/
+void QWidget::update(const QRect &rect)
+{
+ if (!isVisible() || !updatesEnabled() || rect.isEmpty())
+ return;
+
+ if (testAttribute(Qt::WA_WState_InPaintEvent)) {
+ QApplication::postEvent(this, new QUpdateLaterEvent(rect));
+ return;
+ }
+
+ if (hasBackingStoreSupport()) {
+ QTLWExtra *tlwExtra = window()->d_func()->maybeTopData();
+ if (tlwExtra && !tlwExtra->inTopLevelResize && tlwExtra->backingStore)
+ tlwExtra->backingStore->markDirty(rect, this);
+ } else {
+ d_func()->repaint_sys(rect);
+ }
+}
+
+/*!
+ \overload
+
+ This version repaints a region \a rgn inside the widget.
+*/
+void QWidget::update(const QRegion &rgn)
+{
+ if (!isVisible() || !updatesEnabled() || rgn.isEmpty())
+ return;
+
+ if (testAttribute(Qt::WA_WState_InPaintEvent)) {
+ QApplication::postEvent(this, new QUpdateLaterEvent(rgn));
+ return;
+ }
+
+ if (hasBackingStoreSupport()) {
+ QTLWExtra *tlwExtra = window()->d_func()->maybeTopData();
+ if (tlwExtra && !tlwExtra->inTopLevelResize && tlwExtra->backingStore)
+ tlwExtra->backingStore->markDirty(rgn, this);
+ } else {
+ d_func()->repaint_sys(rgn);
+ }
+}
+
+#ifdef QT3_SUPPORT
+/*!
+ Clear the rectangle at point (\a x, \a y) of width \a w and height
+ \a h.
+
+ \warning This is best done in a paintEvent().
+*/
+void QWidget::erase_helper(int x, int y, int w, int h)
+{
+ if (testAttribute(Qt::WA_NoSystemBackground) || testAttribute(Qt::WA_UpdatesDisabled) || !testAttribute(Qt::WA_WState_Visible))
+ return;
+ if (w < 0)
+ w = data->crect.width() - x;
+ if (h < 0)
+ h = data->crect.height() - y;
+ if (w != 0 && h != 0) {
+ QPainter p(this);
+ p.eraseRect(QRect(x, y, w, h));
+ }
+}
+
+/*!
+ \overload
+
+ Clear the given region, \a rgn.
+
+ Drawing may only take place in a QPaintEvent. Overload
+ paintEvent() to do your erasing and call update() to schedule a
+ replaint whenever necessary. See also QPainter.
+*/
+void QWidget::erase(const QRegion& rgn)
+{
+ if (testAttribute(Qt::WA_NoSystemBackground) || testAttribute(Qt::WA_UpdatesDisabled) || !testAttribute(Qt::WA_WState_Visible))
+ return;
+
+ QPainter p(this);
+ p.setClipRegion(rgn);
+ p.eraseRect(rgn.boundingRect());
+}
+
+void QWidget::drawText_helper(int x, int y, const QString &str)
+{
+ if(!testAttribute(Qt::WA_WState_Visible))
+ return;
+ QPainter paint(this);
+ paint.drawText(x, y, str);
+}
+
+
+/*!
+ Closes the widget.
+
+ Use the no-argument overload instead.
+*/
+bool QWidget::close(bool alsoDelete)
+{
+ QPointer<QWidget> that = this;
+ bool accepted = close();
+ if (alsoDelete && accepted && that)
+ deleteLater();
+ return accepted;
+}
+
+void QWidget::setIcon(const QPixmap &i)
+{
+ setWindowIcon(i);
+}
+
+/*!
+ Return's the widget's icon.
+
+ Use windowIcon() instead.
+*/
+const QPixmap *QWidget::icon() const
+{
+ Q_D(const QWidget);
+ return (d->extra && d->extra->topextra) ? d->extra->topextra->iconPixmap : 0;
+}
+
+#endif // QT3_SUPPORT
+
+/*!
+ Sets the attribute \a attribute on this widget if \a on is true;
+ otherwise clears the attribute.
+
+ \sa testAttribute()
+*/
+void QWidget::setAttribute(Qt::WidgetAttribute attribute, bool on)
+{
+ if (testAttribute(attribute) == on)
+ return;
+
+ Q_D(QWidget);
+ Q_ASSERT_X(sizeof(d->high_attributes)*8 >= (Qt::WA_AttributeCount - sizeof(uint)*8),
+ "QWidget::setAttribute(WidgetAttribute, bool)",
+ "QWidgetPrivate::high_attributes[] too small to contain all attributes in WidgetAttribute");
+
+#ifdef Q_WS_WIN
+ if (attribute == Qt::WA_PaintOnScreen && on) {
+ // see qwidget_win.cpp, ::paintEngine for details
+ paintEngine();
+ if (d->noPaintOnScreen)
+ return;
+ }
+#endif
+
+ if (attribute < int(8*sizeof(uint))) {
+ if (on)
+ data->widget_attributes |= (1<<attribute);
+ else
+ data->widget_attributes &= ~(1<<attribute);
+ } else {
+ const int x = attribute - 8*sizeof(uint);
+ const int int_off = x / (8*sizeof(uint));
+ if (on)
+ d->high_attributes[int_off] |= (1<<(x-(int_off*8*sizeof(uint))));
+ else
+ d->high_attributes[int_off] &= ~(1<<(x-(int_off*8*sizeof(uint))));
+ }
+
+ switch (attribute) {
+
+#ifndef QT_NO_DRAGANDDROP
+ case Qt::WA_AcceptDrops: {
+ if (on && !testAttribute(Qt::WA_DropSiteRegistered))
+ setAttribute(Qt::WA_DropSiteRegistered, true);
+ else if (!on && (isWindow() || !parentWidget() || !parentWidget()->testAttribute(Qt::WA_DropSiteRegistered)))
+ setAttribute(Qt::WA_DropSiteRegistered, false);
+ QEvent e(QEvent::AcceptDropsChange);
+ QApplication::sendEvent(this, &e);
+ break;
+ }
+ case Qt::WA_DropSiteRegistered: {
+ d->registerDropSite(on);
+ for (int i = 0; i < d->children.size(); ++i) {
+ QWidget *w = qobject_cast<QWidget *>(d->children.at(i));
+ if (w && !w->isWindow() && !w->testAttribute(Qt::WA_AcceptDrops) && w->testAttribute(Qt::WA_DropSiteRegistered) != on)
+ w->setAttribute(Qt::WA_DropSiteRegistered, on);
+ }
+ break;
+ }
+#endif
+
+ case Qt::WA_NoChildEventsForParent:
+ d->sendChildEvents = !on;
+ break;
+ case Qt::WA_NoChildEventsFromChildren:
+ d->receiveChildEvents = !on;
+ break;
+ case Qt::WA_MacBrushedMetal:
+#ifdef Q_WS_MAC
+ d->setStyle_helper(style(), false, true); // Make sure things get unpolished/polished correctly.
+ // fall through since changing the metal attribute affects the opaque size grip.
+ case Qt::WA_MacOpaqueSizeGrip:
+ d->macUpdateOpaqueSizeGrip();
+ break;
+ case Qt::WA_MacShowFocusRect:
+ if (hasFocus()) {
+ clearFocus();
+ setFocus();
+ }
+ break;
+ case Qt::WA_Hover:
+ qt_mac_update_mouseTracking(this);
+ break;
+#endif
+ case Qt::WA_MacAlwaysShowToolWindow:
+#ifdef Q_WS_MAC
+ d->macUpdateHideOnSuspend();
+#endif
+ break;
+ case Qt::WA_MacNormalSize:
+ case Qt::WA_MacSmallSize:
+ case Qt::WA_MacMiniSize:
+#ifdef Q_WS_MAC
+ {
+ // We can only have one of these set at a time
+ static const int MacSizes[] = { Qt::WA_MacNormalSize, Qt::WA_MacSmallSize,
+ Qt::WA_MacMiniSize, 0 };
+ for (int i = 0; MacSizes[i] != 0; ++i) {
+ if (MacSizes[i] == attribute)
+ continue;
+ int macsize_x = MacSizes[i] - 8*sizeof(uint);
+ int macsize_int_off = macsize_x / (8*sizeof(uint));
+ d->high_attributes[macsize_int_off] &= ~(1<<(macsize_x-(macsize_int_off*8*sizeof(uint))));
+ }
+ d->macUpdateSizeAttribute();
+ }
+#endif
+ break;
+ case Qt::WA_ShowModal:
+ if (!on) {
+ if (isVisible())
+ QApplicationPrivate::leaveModal(this);
+ // reset modality type to Modeless when clearing WA_ShowModal
+ data->window_modality = Qt::NonModal;
+ } else if (data->window_modality == Qt::NonModal) {
+ // determine the modality type if it hasn't been set prior
+ // to setting WA_ShowModal. set the default to WindowModal
+ // if we are the child of a group leader; otherwise use
+ // ApplicationModal.
+ QWidget *w = parentWidget();
+ if (w)
+ w = w->window();
+ while (w && !w->testAttribute(Qt::WA_GroupLeader)) {
+ w = w->parentWidget();
+ if (w)
+ w = w->window();
+ }
+ data->window_modality = (w && w->testAttribute(Qt::WA_GroupLeader))
+ ? Qt::WindowModal
+ : Qt::ApplicationModal;
+ }
+ if (testAttribute(Qt::WA_WState_Created)) {
+ // don't call setModal_sys() before create_sys()
+ d->setModal_sys();
+ }
+ break;
+ case Qt::WA_MouseTracking: {
+ QEvent e(QEvent::MouseTrackingChange);
+ QApplication::sendEvent(this, &e);
+ break; }
+#if !defined(QT_NO_DIRECT3D) && defined(Q_WS_WIN)
+ case Qt::WA_MSWindowsUseDirect3D:
+ if (!qApp->testAttribute(Qt::AA_MSWindowsUseDirect3DByDefault)) {
+ if (on) {
+ if (!d->extra)
+ d->createExtra();
+ d->extra->had_auto_fill_bg = d->extra->autoFillBackground;
+ d->extra->had_no_system_bg = testAttribute(Qt::WA_NoSystemBackground);
+ d->extra->had_paint_on_screen = testAttribute(Qt::WA_PaintOnScreen);
+ // enforce the opaque widget state D3D needs
+ d->extra->autoFillBackground = true;
+ setAttribute(Qt::WA_PaintOnScreen);
+ setAttribute(Qt::WA_NoSystemBackground);
+ } else if (d->extra) {
+ d->extra->autoFillBackground = d->extra->had_auto_fill_bg;
+ setAttribute(Qt::WA_PaintOnScreen, d->extra->had_paint_on_screen);
+ setAttribute(Qt::WA_NoSystemBackground, d->extra->had_no_system_bg);
+ }
+ }
+ break;
+#endif
+ case Qt::WA_NativeWindow:
+ if (!qApp->testAttribute(Qt::AA_DontCreateNativeWidgetSiblings) && parentWidget())
+ parentWidget()->d_func()->enforceNativeChildren();
+ if (on && !internalWinId() && testAttribute(Qt::WA_WState_Created))
+ d->createWinId();
+ break;
+ case Qt::WA_PaintOnScreen:
+ d->updateIsOpaque();
+#if defined(Q_WS_WIN) || defined(Q_WS_X11)
+ // Recreate the widget if it's already created as an alien widget and
+ // WA_PaintOnScreen is enabled. Paint on screen widgets must have win id.
+ // So must their children.
+ if (on) {
+ setAttribute(Qt::WA_NativeWindow);
+ d->enforceNativeChildren();
+ }
+#endif
+ // fall through
+ case Qt::WA_OpaquePaintEvent:
+ d->updateIsOpaque();
+ break;
+ case Qt::WA_NoSystemBackground:
+ d->updateIsOpaque();
+ // fall through...
+ case Qt::WA_UpdatesDisabled:
+ d->updateSystemBackground();
+ break;
+ case Qt::WA_TransparentForMouseEvents:
+#ifdef Q_WS_MAC
+ d->macUpdateIgnoreMouseEvents();
+#endif
+ break;
+ case Qt::WA_InputMethodEnabled: {
+#if defined(Q_WS_WIN) || (defined(Q_WS_QWS) && !defined(QT_NO_QWS_INPUTMETHODS))
+ if (hasFocus())
+ QInputContextPrivate::updateImeStatus(this, true);
+#endif
+ QInputContext *ic = d->ic;
+ if (!ic) {
+ // implicitly create input context only if we have a focus
+ if (hasFocus())
+ ic = d->inputContext();
+ }
+ if (ic) {
+ if (on && hasFocus() && ic->focusWidget() != this) {
+ ic->setFocusWidget(this);
+ } else if (!on && ic->focusWidget() == this) {
+ ic->reset();
+ ic->setFocusWidget(0);
+ }
+ }
+ break;
+ }
+ case Qt::WA_WindowPropagation:
+ d->resolvePalette();
+ d->resolveFont();
+ d->resolveLocale();
+ break;
+#ifdef Q_WS_X11
+ case Qt::WA_NoX11EventCompression:
+ if (!d->extra)
+ d->createExtra();
+ d->extra->compress_events = on;
+ break;
+ case Qt::WA_X11OpenGLOverlay:
+ d->updateIsOpaque();
+ break;
+#endif
+ case Qt::WA_DontShowOnScreen: {
+ if (on && isVisible()) {
+ // Make sure we keep the current state and only hide the widget
+ // from the desktop. show_sys will only update platform specific
+ // attributes at this point.
+ d->hide_sys();
+#ifdef Q_WS_QWS
+ // Release the region for this window from qws if the widget has
+ // been shown before the attribute was set.
+ if (QWSWindowSurface *surface = static_cast<QWSWindowSurface *>(windowSurface())) {
+ QWidget::qwsDisplay()->requestRegion(surface->winId(), surface->key(),
+ surface->permanentState(), QRegion());
+ }
+#endif
+ d->show_sys();
+ }
+ break;
+ }
+
+#ifdef Q_WS_X11
+ case Qt::WA_X11NetWmWindowTypeDesktop:
+ case Qt::WA_X11NetWmWindowTypeDock:
+ case Qt::WA_X11NetWmWindowTypeToolBar:
+ case Qt::WA_X11NetWmWindowTypeMenu:
+ case Qt::WA_X11NetWmWindowTypeUtility:
+ case Qt::WA_X11NetWmWindowTypeSplash:
+ case Qt::WA_X11NetWmWindowTypeDialog:
+ case Qt::WA_X11NetWmWindowTypeDropDownMenu:
+ case Qt::WA_X11NetWmWindowTypePopupMenu:
+ case Qt::WA_X11NetWmWindowTypeToolTip:
+ case Qt::WA_X11NetWmWindowTypeNotification:
+ case Qt::WA_X11NetWmWindowTypeCombo:
+ case Qt::WA_X11NetWmWindowTypeDND:
+ if (testAttribute(Qt::WA_WState_Created))
+ d->setNetWmWindowTypes();
+ break;
+#endif
+
+ case Qt::WA_StaticContents:
+ if (QWidgetBackingStore *bs = d->maybeBackingStore()) {
+ if (on)
+ bs->addStaticWidget(this);
+ else
+ bs->removeStaticWidget(this);
+ }
+ break;
+ case Qt::WA_TranslucentBackground:
+ if (on) {
+ setAttribute(Qt::WA_NoSystemBackground);
+ d->updateIsTranslucent();
+ }
+
+ break;
+ default:
+ break;
+ }
+}
+
+/*! \fn bool QWidget::testAttribute(Qt::WidgetAttribute attribute) const
+
+ Returns true if attribute \a attribute is set on this widget;
+ otherwise returns false.
+
+ \sa setAttribute()
+ */
+bool QWidget::testAttribute_helper(Qt::WidgetAttribute attribute) const
+{
+ Q_D(const QWidget);
+ const int x = attribute - 8*sizeof(uint);
+ const int int_off = x / (8*sizeof(uint));
+ return (d->high_attributes[int_off] & (1<<(x-(int_off*8*sizeof(uint)))));
+}
+
+/*!
+ \property QWidget::windowOpacity
+
+ \brief The level of opacity for the window.
+
+ The valid range of opacity is from 1.0 (completely opaque) to
+ 0.0 (completely transparent).
+
+ By default the value of this property is 1.0.
+
+ This feature is available on Embedded Linux, Mac OS X, X11 platforms that
+ support the Composite extension, and Windows 2000 and later.
+
+ This feature is not available on Windows CE.
+
+ Note that under X11 you need to have a composite manager running,
+ and the X11 specific _NET_WM_WINDOW_OPACITY atom needs to be
+ supported by the window manager you are using.
+
+ \warning Changing this property from opaque to transparent might issue a
+ paint event that needs to be processed before the window is displayed
+ correctly. This affects mainly the use of QPixmap::grabWindow(). Also note
+ that semi-transparent windows update and resize significantly slower than
+ opaque windows.
+
+ \sa setMask()
+*/
+qreal QWidget::windowOpacity() const
+{
+ Q_D(const QWidget);
+ return (isWindow() && d->maybeTopData()) ? d->maybeTopData()->opacity / 255. : 1.0;
+}
+
+void QWidget::setWindowOpacity(qreal opacity)
+{
+ Q_D(QWidget);
+ if (!isWindow())
+ return;
+
+ opacity = qBound(qreal(0.0), opacity, qreal(1.0));
+ QTLWExtra *extra = d->topData();
+ extra->opacity = uint(opacity * 255);
+ setAttribute(Qt::WA_WState_WindowOpacitySet);
+
+#ifndef Q_WS_QWS
+ if (!testAttribute(Qt::WA_WState_Created))
+ return;
+#endif
+
+#ifndef QT_NO_GRAPHICSVIEW
+ if (QGraphicsProxyWidget *proxy = graphicsProxyWidget()) {
+ // Avoid invalidating the cache if set.
+ if (proxy->cacheMode() == QGraphicsItem::NoCache)
+ proxy->update();
+ else if (QGraphicsScene *scene = proxy->scene())
+ scene->update(proxy->sceneBoundingRect());
+ return;
+ }
+#endif
+
+ d->setWindowOpacity_sys(opacity);
+}
+
+/*!
+ \property QWidget::windowModified
+ \brief whether the document shown in the window has unsaved changes
+
+ A modified window is a window whose content has changed but has
+ not been saved to disk. This flag will have different effects
+ varied by the platform. On Mac OS X the close button will have a
+ modified look; on other platforms, the window title will have an
+ '*' (asterisk).
+
+ The window title must contain a "[*]" placeholder, which
+ indicates where the '*' should appear. Normally, it should appear
+ right after the file name (e.g., "document1.txt[*] - Text
+ Editor"). If the window isn't modified, the placeholder is simply
+ removed.
+
+ Note that if a widget is set as modified, all its ancestors will
+ also be set as modified. However, if you call \c
+ {setWindowModified(false)} on a widget, this will not propagate to
+ its parent because other children of the parent might have been
+ modified.
+
+ \sa windowTitle, {Application Example}, {SDI Example}, {MDI Example}
+*/
+bool QWidget::isWindowModified() const
+{
+ return testAttribute(Qt::WA_WindowModified);
+}
+
+void QWidget::setWindowModified(bool mod)
+{
+ Q_D(QWidget);
+ setAttribute(Qt::WA_WindowModified, mod);
+
+#ifndef Q_WS_MAC
+ if (!windowTitle().contains(QLatin1String("[*]")) && mod)
+ qWarning("QWidget::setWindowModified: The window title does not contain a '[*]' placeholder");
+#endif
+ d->setWindowTitle_helper(windowTitle());
+ d->setWindowIconText_helper(windowIconText());
+#ifdef Q_WS_MAC
+ d->setWindowModified_sys(mod);
+#endif
+
+ QEvent e(QEvent::ModifiedChange);
+ QApplication::sendEvent(this, &e);
+}
+
+#ifndef QT_NO_TOOLTIP
+/*!
+ \property QWidget::toolTip
+
+ \brief the widget's tooltip
+
+ Note that by default tooltips are only shown for widgets that are
+ children of the active window. You can change this behavior by
+ setting the attribute Qt::WA_AlwaysShowToolTips on the \e window,
+ not on the widget with the tooltip.
+
+ If you want to control a tooltip's behavior, you can intercept the
+ event() function and catch the QEvent::ToolTip event (e.g., if you
+ want to customize the area for which the tooltip should be shown).
+
+ By default, this property contains an empty string.
+
+ \sa QToolTip statusTip whatsThis
+*/
+void QWidget::setToolTip(const QString &s)
+{
+ Q_D(QWidget);
+ d->toolTip = s;
+
+ QEvent event(QEvent::ToolTipChange);
+ QApplication::sendEvent(this, &event);
+}
+
+QString QWidget::toolTip() const
+{
+ Q_D(const QWidget);
+ return d->toolTip;
+}
+#endif // QT_NO_TOOLTIP
+
+
+#ifndef QT_NO_STATUSTIP
+/*!
+ \property QWidget::statusTip
+ \brief the widget's status tip
+
+ By default, this property contains an empty string.
+
+ \sa toolTip whatsThis
+*/
+void QWidget::setStatusTip(const QString &s)
+{
+ Q_D(QWidget);
+ d->statusTip = s;
+}
+
+QString QWidget::statusTip() const
+{
+ Q_D(const QWidget);
+ return d->statusTip;
+}
+#endif // QT_NO_STATUSTIP
+
+#ifndef QT_NO_WHATSTHIS
+/*!
+ \property QWidget::whatsThis
+
+ \brief the widget's What's This help text.
+
+ By default, this property contains an empty string.
+
+ \sa QWhatsThis QWidget::toolTip QWidget::statusTip
+*/
+void QWidget::setWhatsThis(const QString &s)
+{
+ Q_D(QWidget);
+ d->whatsThis = s;
+}
+
+QString QWidget::whatsThis() const
+{
+ Q_D(const QWidget);
+ return d->whatsThis;
+}
+#endif // QT_NO_WHATSTHIS
+
+#ifndef QT_NO_ACCESSIBILITY
+/*!
+ \property QWidget::accessibleName
+
+ \brief the widget's name as seen by assistive technologies
+
+ This property is used by accessible clients to identify, find, or announce
+ the widget for accessible clients.
+
+ By default, this property contains an empty string.
+
+ \sa QAccessibleInterface::text()
+*/
+void QWidget::setAccessibleName(const QString &name)
+{
+ Q_D(QWidget);
+ d->accessibleName = name;
+}
+
+QString QWidget::accessibleName() const
+{
+ Q_D(const QWidget);
+ return d->accessibleName;
+}
+
+/*!
+ \property QWidget::accessibleDescription
+
+ \brief the widget's description as seen by assistive technologies
+
+ By default, this property contains an empty string.
+
+ \sa QAccessibleInterface::text()
+*/
+void QWidget::setAccessibleDescription(const QString &description)
+{
+ Q_D(QWidget);
+ d->accessibleDescription = description;
+}
+
+QString QWidget::accessibleDescription() const
+{
+ Q_D(const QWidget);
+ return d->accessibleDescription;
+}
+#endif // QT_NO_ACCESSIBILITY
+
+#ifndef QT_NO_SHORTCUT
+/*!
+ Adds a shortcut to Qt's shortcut system that watches for the given
+ \a key sequence in the given \a context. If the \a context is
+ Qt::ApplicationShortcut, the shortcut applies to the application as a
+ whole. Otherwise, it is either local to this widget, Qt::WidgetShortcut,
+ or to the window itself, Qt::WindowShortcut.
+
+ If the same \a key sequence has been grabbed by several widgets,
+ when the \a key sequence occurs a QEvent::Shortcut event is sent
+ to all the widgets to which it applies in a non-deterministic
+ order, but with the ``ambiguous'' flag set to true.
+
+ \warning You should not normally need to use this function;
+ instead create \l{QAction}s with the shortcut key sequences you
+ require (if you also want equivalent menu options and toolbar
+ buttons), or create \l{QShortcut}s if you just need key sequences.
+ Both QAction and QShortcut handle all the event filtering for you,
+ and provide signals which are triggered when the user triggers the
+ key sequence, so are much easier to use than this low-level
+ function.
+
+ \sa releaseShortcut() setShortcutEnabled()
+*/
+int QWidget::grabShortcut(const QKeySequence &key, Qt::ShortcutContext context)
+{
+ Q_ASSERT(qApp);
+ if (key.isEmpty())
+ return 0;
+ setAttribute(Qt::WA_GrabbedShortcut);
+ return qApp->d_func()->shortcutMap.addShortcut(this, key, context);
+}
+
+/*!
+ Removes the shortcut with the given \a id from Qt's shortcut
+ system. The widget will no longer receive QEvent::Shortcut events
+ for the shortcut's key sequence (unless it has other shortcuts
+ with the same key sequence).
+
+ \warning You should not normally need to use this function since
+ Qt's shortcut system removes shortcuts automatically when their
+ parent widget is destroyed. It is best to use QAction or
+ QShortcut to handle shortcuts, since they are easier to use than
+ this low-level function. Note also that this is an expensive
+ operation.
+
+ \sa grabShortcut() setShortcutEnabled()
+*/
+void QWidget::releaseShortcut(int id)
+{
+ Q_ASSERT(qApp);
+ if (id)
+ qApp->d_func()->shortcutMap.removeShortcut(id, this, 0);
+}
+
+/*!
+ If \a enable is true, the shortcut with the given \a id is
+ enabled; otherwise the shortcut is disabled.
+
+ \warning You should not normally need to use this function since
+ Qt's shortcut system enables/disables shortcuts automatically as
+ widgets become hidden/visible and gain or lose focus. It is best
+ to use QAction or QShortcut to handle shortcuts, since they are
+ easier to use than this low-level function.
+
+ \sa grabShortcut() releaseShortcut()
+*/
+void QWidget::setShortcutEnabled(int id, bool enable)
+{
+ Q_ASSERT(qApp);
+ if (id)
+ qApp->d_func()->shortcutMap.setShortcutEnabled(enable, id, this, 0);
+}
+
+/*!
+ \since 4.2
+
+ If \a enable is true, auto repeat of the shortcut with the
+ given \a id is enabled; otherwise it is disabled.
+
+ \sa grabShortcut() releaseShortcut()
+*/
+void QWidget::setShortcutAutoRepeat(int id, bool enable)
+{
+ Q_ASSERT(qApp);
+ if (id)
+ qApp->d_func()->shortcutMap.setShortcutAutoRepeat(enable, id, this, 0);
+}
+#endif // QT_NO_SHORTCUT
+/*!
+ Updates the widget's micro focus.
+
+ \sa QInputContext
+*/
+void QWidget::updateMicroFocus()
+{
+#if !defined(QT_NO_IM) && (defined(Q_WS_X11) || defined(Q_WS_QWS))
+ Q_D(QWidget);
+ // and optimisation to update input context only it has already been created.
+ if (d->ic || qApp->d_func()->inputContext) {
+ QInputContext *ic = inputContext();
+ if (ic)
+ ic->update();
+ }
+#endif
+#ifndef QT_NO_ACCESSIBILITY
+ // ##### is this correct
+ QAccessible::updateAccessibility(this, 0, QAccessible::StateChanged);
+#endif
+}
+
+
+#if defined (Q_WS_WIN)
+/*!
+ Returns the window system handle of the widget, for low-level
+ access. Using this function is not portable.
+
+ An HDC acquired with getDC() has to be released with releaseDC().
+
+ \warning Using this function is not portable.
+*/
+HDC QWidget::getDC() const
+{
+ Q_D(const QWidget);
+ if (d->hd)
+ return (HDC) d->hd;
+ return GetDC(winId());
+}
+
+/*!
+ Releases the HDC \a hdc acquired by a previous call to getDC().
+
+ \warning Using this function is not portable.
+*/
+void QWidget::releaseDC(HDC hdc) const
+{
+ Q_D(const QWidget);
+ // If its the widgets own dc, it will be released elsewhere. If
+ // its a different HDC we release it and issue a warning if it
+ // fails.
+ if (hdc != d->hd && !ReleaseDC(winId(), hdc))
+ qErrnoWarning("QWidget::releaseDC(): failed to release HDC");
+}
+#else
+/*!
+ Returns the window system handle of the widget, for low-level
+ access. Using this function is not portable.
+
+ The HANDLE type varies with platform; see \c qwindowdefs.h for
+ details.
+*/
+Qt::HANDLE QWidget::handle() const
+{
+ Q_D(const QWidget);
+ if (!internalWinId() && testAttribute(Qt::WA_WState_Created))
+ (void)winId(); // enforce native window
+ return d->hd;
+}
+#endif
+
+
+/*!
+ Raises this widget to the top of the parent widget's stack.
+
+ After this call the widget will be visually in front of any
+ overlapping sibling widgets.
+
+ \note When using activateWindow(), you can call this function to
+ ensure that the window is stacked on top.
+
+ \sa lower(), stackUnder()
+*/
+
+void QWidget::raise()
+{
+ Q_D(QWidget);
+ if (!isWindow()) {
+ QWidget *p = parentWidget();
+ const int parentChildCount = p->d_func()->children.size();
+ if (parentChildCount < 2)
+ return;
+ const int from = p->d_func()->children.indexOf(this);
+ Q_ASSERT(from >= 0);
+ // Do nothing if the widget is already in correct stacking order _and_ created.
+ if (from != parentChildCount -1)
+ p->d_func()->children.move(from, parentChildCount - 1);
+ if (!testAttribute(Qt::WA_WState_Created) && p->testAttribute(Qt::WA_WState_Created))
+ create();
+ else if (from == parentChildCount - 1)
+ return;
+
+ QRegion region(rect());
+ d->subtractOpaqueSiblings(region);
+ d->invalidateBuffer(region);
+ }
+ if (testAttribute(Qt::WA_WState_Created))
+ d->raise_sys();
+
+ QEvent e(QEvent::ZOrderChange);
+ QApplication::sendEvent(this, &e);
+}
+
+/*!
+ Lowers the widget to the bottom of the parent widget's stack.
+
+ After this call the widget will be visually behind (and therefore
+ obscured by) any overlapping sibling widgets.
+
+ \sa raise(), stackUnder()
+*/
+
+void QWidget::lower()
+{
+ Q_D(QWidget);
+ if (!isWindow()) {
+ QWidget *p = parentWidget();
+ const int parentChildCount = p->d_func()->children.size();
+ if (parentChildCount < 2)
+ return;
+ const int from = p->d_func()->children.indexOf(this);
+ Q_ASSERT(from >= 0);
+ // Do nothing if the widget is already in correct stacking order _and_ created.
+ if (from != 0)
+ p->d_func()->children.move(from, 0);
+ if (!testAttribute(Qt::WA_WState_Created) && p->testAttribute(Qt::WA_WState_Created))
+ create();
+ else if (from == 0)
+ return;
+ }
+ if (testAttribute(Qt::WA_WState_Created))
+ d->lower_sys();
+
+ QEvent e(QEvent::ZOrderChange);
+ QApplication::sendEvent(this, &e);
+}
+
+
+/*!
+ Places the widget under \a w in the parent widget's stack.
+
+ To make this work, the widget itself and \a w must be siblings.
+
+ \sa raise(), lower()
+*/
+void QWidget::stackUnder(QWidget* w)
+{
+ Q_D(QWidget);
+ QWidget *p = parentWidget();
+ if (!w || isWindow() || p != w->parentWidget() || this == w)
+ return;
+ if (p) {
+ int from = p->d_func()->children.indexOf(this);
+ int to = p->d_func()->children.indexOf(w);
+ Q_ASSERT(from >= 0);
+ Q_ASSERT(to >= 0);
+ if (from < to)
+ --to;
+ // Do nothing if the widget is already in correct stacking order _and_ created.
+ if (from != to)
+ p->d_func()->children.move(from, to);
+ if (!testAttribute(Qt::WA_WState_Created) && p->testAttribute(Qt::WA_WState_Created))
+ create();
+ else if (from == to)
+ return;
+ }
+ if (testAttribute(Qt::WA_WState_Created))
+ d->stackUnder_sys(w);
+
+ QEvent e(QEvent::ZOrderChange);
+ QApplication::sendEvent(this, &e);
+}
+
+void QWidget::styleChange(QStyle&) { }
+void QWidget::enabledChange(bool) { } // compat
+void QWidget::paletteChange(const QPalette &) { } // compat
+void QWidget::fontChange(const QFont &) { } // compat
+void QWidget::windowActivationChange(bool) { } // compat
+void QWidget::languageChange() { } // compat
+
+
+/*!
+ \enum QWidget::BackgroundOrigin
+
+ \compat
+
+ \value WidgetOrigin
+ \value ParentOrigin
+ \value WindowOrigin
+ \value AncestorOrigin
+
+*/
+
+/*!
+ \fn bool QWidget::isVisibleToTLW() const
+
+ Use isVisible() instead.
+*/
+
+/*!
+ \fn void QWidget::iconify()
+
+ Use showMinimized() instead.
+*/
+
+/*!
+ \fn void QWidget::constPolish() const
+
+ Use ensurePolished() instead.
+*/
+
+/*!
+ \fn void QWidget::reparent(QWidget *parent, Qt::WindowFlags f, const QPoint &p, bool showIt)
+
+ Use setParent() to change the parent or the widget's widget flags;
+ use move() to move the widget, and use show() to show the widget.
+*/
+
+/*!
+ \fn void QWidget::reparent(QWidget *parent, const QPoint &p, bool showIt)
+
+ Use setParent() to change the parent; use move() to move the
+ widget, and use show() to show the widget.
+*/
+
+/*!
+ \fn void QWidget::recreate(QWidget *parent, Qt::WindowFlags f, const QPoint & p, bool showIt)
+
+ Use setParent() to change the parent or the widget's widget flags;
+ use move() to move the widget, and use show() to show the widget.
+*/
+
+/*!
+ \fn bool QWidget::hasMouse() const
+
+ Use testAttribute(Qt::WA_UnderMouse) instead.
+*/
+
+/*!
+ \fn bool QWidget::ownCursor() const
+
+ Use testAttribute(Qt::WA_SetCursor) instead.
+*/
+
+/*!
+ \fn bool QWidget::ownFont() const
+
+ Use testAttribute(Qt::WA_SetFont) instead.
+*/
+
+/*!
+ \fn void QWidget::unsetFont()
+
+ Use setFont(QFont()) instead.
+*/
+
+/*!
+ \fn bool QWidget::ownPalette() const
+
+ Use testAttribute(Qt::WA_SetPalette) instead.
+*/
+
+/*!
+ \fn void QWidget::unsetPalette()
+
+ Use setPalette(QPalette()) instead.
+*/
+
+/*!
+ \fn void QWidget::setEraseColor(const QColor &color)
+
+ Use the palette instead.
+
+ \oldcode
+ widget->setEraseColor(color);
+ \newcode
+ QPalette palette;
+ palette.setColor(widget->backgroundRole(), color);
+ widget->setPalette(palette);
+ \endcode
+*/
+
+/*!
+ \fn void QWidget::setErasePixmap(const QPixmap &pixmap)
+
+ Use the palette instead.
+
+ \oldcode
+ widget->setErasePixmap(pixmap);
+ \newcode
+ QPalette palette;
+ palette.setBrush(widget->backgroundRole(), QBrush(pixmap));
+ widget->setPalette(palette);
+ \endcode
+*/
+
+/*!
+ \fn void QWidget::setPaletteForegroundColor(const QColor &color)
+
+ Use the palette directly.
+
+ \oldcode
+ widget->setPaletteForegroundColor(color);
+ \newcode
+ QPalette palette;
+ palette.setColor(widget->foregroundRole(), color);
+ widget->setPalette(palette);
+ \endcode
+*/
+
+/*!
+ \fn void QWidget::setPaletteBackgroundColor(const QColor &color)
+
+ Use the palette directly.
+
+ \oldcode
+ widget->setPaletteBackgroundColor(color);
+ \newcode
+ QPalette palette;
+ palette.setColor(widget->backgroundRole(), color);
+ widget->setPalette(palette);
+ \endcode
+*/
+
+/*!
+ \fn void QWidget::setPaletteBackgroundPixmap(const QPixmap &pixmap)
+
+ Use the palette directly.
+
+ \oldcode
+ widget->setPaletteBackgroundPixmap(pixmap);
+ \newcode
+ QPalette palette;
+ palette.setBrush(widget->backgroundRole(), QBrush(pixmap));
+ widget->setPalette(palette);
+ \endcode
+*/
+
+/*!
+ \fn void QWidget::setBackgroundPixmap(const QPixmap &pixmap)
+
+ Use the palette instead.
+
+ \oldcode
+ widget->setBackgroundPixmap(pixmap);
+ \newcode
+ QPalette palette;
+ palette.setBrush(widget->backgroundRole(), QBrush(pixmap));
+ widget->setPalette(palette);
+ \endcode
+*/
+
+/*!
+ \fn void QWidget::setBackgroundColor(const QColor &color)
+
+ Use the palette instead.
+
+ \oldcode
+ widget->setBackgroundColor(color);
+ \newcode
+ QPalette palette;
+ palette.setColor(widget->backgroundRole(), color);
+ widget->setPalette(palette);
+ \endcode
+*/
+
+/*!
+ \fn QColorGroup QWidget::colorGroup() const
+
+ Use QColorGroup(palette()) instead.
+*/
+
+/*!
+ \fn QWidget *QWidget::parentWidget(bool sameWindow) const
+
+ Use the no-argument overload instead.
+*/
+
+/*!
+ \fn void QWidget::setKeyCompression(bool b)
+
+ Use setAttribute(Qt::WA_KeyCompression, b) instead.
+*/
+
+/*!
+ \fn void QWidget::setFont(const QFont &f, bool b)
+
+ Use the single-argument overload instead.
+*/
+
+/*!
+ \fn void QWidget::setPalette(const QPalette &p, bool b)
+
+ Use the single-argument overload instead.
+*/
+
+/*!
+ \fn void QWidget::setBackgroundOrigin(BackgroundOrigin background)
+
+ \obsolete
+*/
+
+/*!
+ \fn BackgroundOrigin QWidget::backgroundOrigin() const
+
+ \obsolete
+
+ Always returns \c WindowOrigin.
+*/
+
+/*!
+ \fn QPoint QWidget::backgroundOffset() const
+
+ \obsolete
+
+ Always returns QPoint().
+*/
+
+/*!
+ \fn void QWidget::repaint(bool b)
+
+ The boolean parameter \a b is ignored. Use the no-argument overload instead.
+*/
+
+/*!
+ \fn void QWidget::repaint(int x, int y, int w, int h, bool b)
+
+ The boolean parameter \a b is ignored. Use the four-argument overload instead.
+*/
+
+/*!
+ \fn void QWidget::repaint(const QRect &r, bool b)
+
+ The boolean parameter \a b is ignored. Use the single rect-argument overload instead.
+*/
+
+/*!
+ \fn void QWidget::repaint(const QRegion &rgn, bool b)
+
+ The boolean parameter \a b is ignored. Use the single region-argument overload instead.
+*/
+
+/*!
+ \fn void QWidget::erase()
+
+ Drawing may only take place in a QPaintEvent. Overload
+ paintEvent() to do your erasing and call update() to schedule a
+ replaint whenever necessary. See also QPainter.
+*/
+
+/*!
+ \fn void QWidget::erase(int x, int y, int w, int h)
+
+ Drawing may only take place in a QPaintEvent. Overload
+ paintEvent() to do your erasing and call update() to schedule a
+ replaint whenever necessary. See also QPainter.
+*/
+
+/*!
+ \fn void QWidget::erase(const QRect &rect)
+
+ Drawing may only take place in a QPaintEvent. Overload
+ paintEvent() to do your erasing and call update() to schedule a
+ replaint whenever necessary. See also QPainter.
+*/
+
+/*!
+ \fn void QWidget::drawText(const QPoint &p, const QString &s)
+
+ Drawing may only take place in a QPaintEvent. Overload
+ paintEvent() to do your drawing and call update() to schedule a
+ replaint whenever necessary. See also QPainter.
+*/
+
+/*!
+ \fn void QWidget::drawText(int x, int y, const QString &s)
+
+ Drawing may only take place in a QPaintEvent. Overload
+ paintEvent() to do your drawing and call update() to schedule a
+ replaint whenever necessary. See also QPainter.
+*/
+
+/*!
+ \fn QWidget *QWidget::childAt(const QPoint &p, bool includeThis) const
+
+ Use the single point argument overload instead.
+*/
+
+/*!
+ \fn void QWidget::setCaption(const QString &c)
+
+ Use setWindowTitle() instead.
+*/
+
+/*!
+ \fn void QWidget::setIcon(const QPixmap &i)
+
+ Use setWindowIcon() instead.
+*/
+
+/*!
+ \fn void QWidget::setIconText(const QString &it)
+
+ Use setWindowIconText() instead.
+*/
+
+/*!
+ \fn QString QWidget::caption() const
+
+ Use windowTitle() instead.
+*/
+
+/*!
+ \fn QString QWidget::iconText() const
+
+ Use windowIconText() instead.
+*/
+
+/*!
+ \fn bool QWidget::isTopLevel() const
+ \obsolete
+
+ Use isWindow() instead.
+*/
+
+/*!
+ \fn bool QWidget::isRightToLeft() const
+ \internal
+*/
+
+/*!
+ \fn bool QWidget::isLeftToRight() const
+ \internal
+*/
+
+/*!
+ \fn void QWidget::setInputMethodEnabled(bool enabled)
+
+ Use setAttribute(Qt::WA_InputMethodEnabled, \a enabled) instead.
+*/
+
+/*!
+ \fn bool QWidget::isInputMethodEnabled() const
+
+ Use testAttribute(Qt::WA_InputMethodEnabled) instead.
+*/
+
+/*!
+ \fn void QWidget::setActiveWindow()
+
+ Use activateWindow() instead.
+*/
+
+/*!
+ \fn bool QWidget::isShown() const
+
+ Use !isHidden() instead (notice the exclamation mark), or use isVisible() to check whether the widget is visible.
+*/
+
+/*!
+ \fn bool QWidget::isDialog() const
+
+ Use windowType() == Qt::Dialog instead.
+*/
+
+/*!
+ \fn bool QWidget::isPopup() const
+
+ Use windowType() == Qt::Popup instead.
+*/
+
+/*!
+ \fn bool QWidget::isDesktop() const
+
+ Use windowType() == Qt::Desktop instead.
+*/
+
+/*!
+ \fn void QWidget::polish()
+
+ Use ensurePolished() instead.
+*/
+
+/*!
+ \fn QWidget *QWidget::childAt(int x, int y, bool includeThis) const
+
+ Use the childAt() overload that doesn't have an \a includeThis parameter.
+
+ \oldcode
+ return widget->childAt(x, y, true);
+ \newcode
+ QWidget *child = widget->childAt(x, y, true);
+ if (child)
+ return child;
+ if (widget->rect().contains(x, y))
+ return widget;
+ \endcode
+*/
+
+/*!
+ \fn void QWidget::setSizePolicy(QSizePolicy::Policy hor, QSizePolicy::Policy ver, bool hfw)
+ \compat
+
+ Use the \l sizePolicy property and heightForWidth() function instead.
+*/
+
+/*!
+ \fn bool QWidget::isUpdatesEnabled() const
+ \compat
+
+ Use the \l updatesEnabled property instead.
+*/
+
+/*!
+ \macro QWIDGETSIZE_MAX
+ \relates QWidget
+
+ Defines the maximum size for a QWidget object.
+
+ The largest allowed size for a widget is QSize(QWIDGETSIZE_MAX,
+ QWIDGETSIZE_MAX), i.e. QSize (16777215,16777215).
+
+ \sa QWidget::setMaximumSize()
+*/
+
+QRect QWidgetPrivate::frameStrut() const
+{
+ Q_Q(const QWidget);
+ if (!q->isWindow() || (q->windowType() == Qt::Desktop) || q->testAttribute(Qt::WA_DontShowOnScreen)) {
+ // x2 = x1 + w - 1, so w/h = 1
+ return QRect(0, 0, 1, 1);
+ }
+
+ if (data.fstrut_dirty
+#ifndef Q_WS_WIN
+ // ### Fix properly for 4.3
+ && q->isVisible()
+#endif
+ && q->testAttribute(Qt::WA_WState_Created))
+ const_cast<QWidgetPrivate *>(this)->updateFrameStrut();
+
+ return maybeTopData() ? maybeTopData()->frameStrut : QRect();
+}
+
+/*!
+ \preliminary
+ \since 4.2
+ \obsolete
+
+ Sets the window surface to be the \a surface specified.
+ The QWidget takes will ownership of the \a surface.
+ widget itself is deleted.
+*/
+void QWidget::setWindowSurface(QWindowSurface *surface)
+{
+ // ### createWinId() ??
+
+#ifndef Q_BACKINGSTORE_SUBSURFACES
+ if (!isTopLevel())
+ return;
+#endif
+
+ Q_D(QWidget);
+
+ QTLWExtra *topData = d->topData();
+ if (topData->windowSurface == surface)
+ return;
+
+ QWindowSurface *oldSurface = topData->windowSurface;
+ delete topData->windowSurface;
+ topData->windowSurface = surface;
+
+ QWidgetBackingStore *bs = d->maybeBackingStore();
+ if (!bs)
+ return;
+
+ if (isTopLevel()) {
+ if (bs->windowSurface != oldSurface && bs->windowSurface != surface)
+ delete bs->windowSurface;
+ bs->windowSurface = surface;
+ }
+#ifdef Q_BACKINGSTORE_SUBSURFACES
+ else {
+ bs->subSurfaces.append(surface);
+ }
+ bs->subSurfaces.removeOne(oldSurface);
+#endif
+}
+
+/*!
+ \preliminary
+ \since 4.2
+
+ Returns the QWindowSurface this widget will be drawn into.
+*/
+QWindowSurface *QWidget::windowSurface() const
+{
+ Q_D(const QWidget);
+ QTLWExtra *extra = d->maybeTopData();
+ if (extra && extra->windowSurface)
+ return extra->windowSurface;
+
+ QWidgetBackingStore *bs = d->maybeBackingStore();
+
+#ifdef Q_BACKINGSTORE_SUBSURFACES
+ if (bs && bs->subSurfaces.isEmpty())
+ return bs->windowSurface;
+
+ if (!isTopLevel()) {
+ const QWidget *w = parentWidget();
+ while (w) {
+ QTLWExtra *extra = w->d_func()->maybeTopData();
+ if (extra && extra->windowSurface)
+ return extra->windowSurface;
+ if (w->isTopLevel())
+ break;
+ w = w->parentWidget();
+ }
+ }
+#endif // Q_BACKINGSTORE_SUBSURFACES
+
+ return bs ? bs->windowSurface : 0;
+}
+
+void QWidgetPrivate::getLayoutItemMargins(int *left, int *top, int *right, int *bottom) const
+{
+ if (left)
+ *left = (int)leftLayoutItemMargin;
+ if (top)
+ *top = (int)topLayoutItemMargin;
+ if (right)
+ *right = (int)rightLayoutItemMargin;
+ if (bottom)
+ *bottom = (int)bottomLayoutItemMargin;
+}
+
+void QWidgetPrivate::setLayoutItemMargins(int left, int top, int right, int bottom)
+{
+ if (leftLayoutItemMargin == left
+ && topLayoutItemMargin == top
+ && rightLayoutItemMargin == right
+ && bottomLayoutItemMargin == bottom)
+ return;
+
+ Q_Q(QWidget);
+ leftLayoutItemMargin = (signed char)left;
+ topLayoutItemMargin = (signed char)top;
+ rightLayoutItemMargin = (signed char)right;
+ bottomLayoutItemMargin = (signed char)bottom;
+ q->updateGeometry();
+}
+
+void QWidgetPrivate::setLayoutItemMargins(QStyle::SubElement element, const QStyleOption *opt)
+{
+ Q_Q(QWidget);
+ QStyleOption myOpt;
+ if (!opt) {
+ myOpt.initFrom(q);
+ myOpt.rect.setRect(0, 0, 32768, 32768); // arbitrary
+ opt = &myOpt;
+ }
+
+ QRect liRect = q->style()->subElementRect(element, opt, q);
+ if (liRect.isValid()) {
+ leftLayoutItemMargin = (signed char)(opt->rect.left() - liRect.left());
+ topLayoutItemMargin = (signed char)(opt->rect.top() - liRect.top());
+ rightLayoutItemMargin = (signed char)(liRect.right() - opt->rect.right());
+ bottomLayoutItemMargin = (signed char)(liRect.bottom() - opt->rect.bottom());
+ } else {
+ leftLayoutItemMargin = 0;
+ topLayoutItemMargin = 0;
+ rightLayoutItemMargin = 0;
+ bottomLayoutItemMargin = 0;
+ }
+}
+// resets the Qt::WA_QuitOnClose attribute to the default value for transient widgets.
+void QWidgetPrivate::adjustQuitOnCloseAttribute()
+{
+ Q_Q(QWidget);
+
+ if (!q->parentWidget()) {
+ Qt::WindowType type = q->windowType();
+ if (type == Qt::Widget || type == Qt::SubWindow)
+ type = Qt::Window;
+ if (type != Qt::Widget && type != Qt::Window && type != Qt::Dialog)
+ q->setAttribute(Qt::WA_QuitOnClose, false);
+ }
+}
+
+
+
+Q_GUI_EXPORT QWidgetData *qt_qwidget_data(QWidget *widget)
+{
+ return widget->data;
+}
+
+Q_GUI_EXPORT QWidgetPrivate *qt_widget_private(QWidget *widget)
+{
+ return widget->d_func();
+}
+
+
+
+
+#ifndef QT_NO_GRAPHICSVIEW
+/*!
+ \since 4.5
+
+ Returns the proxy widget for the corresponding embedded widget in a graphics
+ view; otherwise returns 0.
+
+ \sa QGraphicsProxyWidget::createProxyForChildWidget(),
+ QGraphicsScene::addWidget()
+ */
+QGraphicsProxyWidget *QWidget::graphicsProxyWidget() const
+{
+ Q_D(const QWidget);
+ if (d->extra) {
+ return d->extra->proxyWidget;
+ }
+ return 0;
+}
+#endif
+
+
+/*!
+ \typedef QWidgetList
+ \relates QWidget
+
+ Synonym for QList<QWidget *>.
+*/
+
+QT_END_NAMESPACE
+
+#include "moc_qwidget.cpp"
+
+/*!
+ \typedef WId
+ \relates QWidget
+
+ Platform dependent window identifier.
+*/
+
+/*!
+ \fn void QWidget::destroy(bool destroyWindow, bool destroySubWindows)
+
+ Frees up window system resources. Destroys the widget window if \a
+ destroyWindow is true.
+
+ destroy() calls itself recursively for all the child widgets,
+ passing \a destroySubWindows for the \a destroyWindow parameter.
+ To have more control over destruction of subwidgets, destroy
+ subwidgets selectively first.
+
+ This function is usually called from the QWidget destructor.
+*/
+
+/*!
+ \fn QPaintEngine *QWidget::paintEngine() const
+
+ Returns the widget's paint engine.
+
+ Note that this function should not be called explicitly by the
+ user, since it's meant for reimplementation purposes only. The
+ function is called by Qt internally, and the default
+ implementation may not always return a valid pointer.
+*/
+
+/*!
+ \fn QPoint QWidget::mapToGlobal(const QPoint &pos) const
+
+ Translates the widget coordinate \a pos to global screen
+ coordinates. For example, \c{mapToGlobal(QPoint(0,0))} would give
+ the global coordinates of the top-left pixel of the widget.
+
+ \sa mapFromGlobal() mapTo() mapToParent()
+*/
+
+/*!
+ \fn QPoint QWidget::mapFromGlobal(const QPoint &pos) const
+
+ Translates the global screen coordinate \a pos to widget
+ coordinates.
+
+ \sa mapToGlobal() mapFrom() mapFromParent()
+*/
+
+/*!
+ \fn void QWidget::grabMouse()
+
+ Grabs the mouse input.
+
+ This widget receives all mouse events until releaseMouse() is
+ called; other widgets get no mouse events at all. Keyboard
+ events are not affected. Use grabKeyboard() if you want to grab
+ that.
+
+ \warning Bugs in mouse-grabbing applications very often lock the
+ terminal. Use this function with extreme caution, and consider
+ using the \c -nograb command line option while debugging.
+
+ It is almost never necessary to grab the mouse when using Qt, as
+ Qt grabs and releases it sensibly. In particular, Qt grabs the
+ mouse when a mouse button is pressed and keeps it until the last
+ button is released.
+
+ Note that only visible widgets can grab mouse input. If
+ isVisible() returns false for a widget, that widget cannot call
+ grabMouse().
+
+ \sa releaseMouse() grabKeyboard() releaseKeyboard() grabKeyboard()
+ focusWidget()
+*/
+
+/*!
+ \fn void QWidget::grabMouse(const QCursor &cursor)
+ \overload
+
+ Grabs the mouse input and changes the cursor shape.
+
+ The cursor will assume shape \a cursor (for as long as the mouse
+ focus is grabbed) and this widget will be the only one to receive
+ mouse events until releaseMouse() is called().
+
+ \warning Grabbing the mouse might lock the terminal.
+
+ \sa releaseMouse(), grabKeyboard(), releaseKeyboard(), setCursor()
+*/
+
+/*!
+ \fn void QWidget::releaseMouse()
+
+ Releases the mouse grab.
+
+ \sa grabMouse(), grabKeyboard(), releaseKeyboard()
+*/
+
+/*!
+ \fn void QWidget::grabKeyboard()
+
+ Grabs the keyboard input.
+
+ This widget receives all keyboard events until releaseKeyboard()
+ is called; other widgets get no keyboard events at all. Mouse
+ events are not affected. Use grabMouse() if you want to grab that.
+
+ The focus widget is not affected, except that it doesn't receive
+ any keyboard events. setFocus() moves the focus as usual, but the
+ new focus widget receives keyboard events only after
+ releaseKeyboard() is called.
+
+ If a different widget is currently grabbing keyboard input, that
+ widget's grab is released first.
+
+ \sa releaseKeyboard() grabMouse() releaseMouse() focusWidget()
+*/
+
+/*!
+ \fn void QWidget::releaseKeyboard()
+
+ Releases the keyboard grab.
+
+ \sa grabKeyboard(), grabMouse(), releaseMouse()
+*/
+
+/*!
+ \fn QWidget *QWidget::mouseGrabber()
+
+ Returns the widget that is currently grabbing the mouse input.
+
+ If no widget in this application is currently grabbing the mouse,
+ 0 is returned.
+
+ \sa grabMouse(), keyboardGrabber()
+*/
+
+/*!
+ \fn QWidget *QWidget::keyboardGrabber()
+
+ Returns the widget that is currently grabbing the keyboard input.
+
+ If no widget in this application is currently grabbing the
+ keyboard, 0 is returned.
+
+ \sa grabMouse(), mouseGrabber()
+*/
+
+/*!
+ \fn void QWidget::activateWindow()
+
+ Sets the top-level widget containing this widget to be the active
+ window.
+
+ An active window is a visible top-level window that has the
+ keyboard input focus.
+
+ This function performs the same operation as clicking the mouse on
+ the title bar of a top-level window. On X11, the result depends on
+ the Window Manager. If you want to ensure that the window is
+ stacked on top as well you should also call raise(). Note that the
+ window must be visible, otherwise activateWindow() has no effect.
+
+ On Windows, if you are calling this when the application is not
+ currently the active one then it will not make it the active
+ window. It will change the color of the taskbar entry to indicate
+ that the window has changed in some way. This is because Microsoft
+ does not allow an application to interrupt what the user is currently
+ doing in another application.
+
+ \sa isActiveWindow(), window(), show()
+*/
+
+/*!
+ \fn int QWidget::metric(PaintDeviceMetric m) const
+
+ Internal implementation of the virtual QPaintDevice::metric()
+ function.
+
+ \a m is the metric to get.
+*/
+
+/*!
+ \fn void QWidget::setMask(const QRegion &region)
+ \overload
+
+ Causes only the parts of the widget which overlap \a region to be
+ visible. If the region includes pixels outside the rect() of the
+ widget, window system controls in that area may or may not be
+ visible, depending on the platform.
+
+ Note that this effect can be slow if the region is particularly
+ complex.
+
+ \sa windowOpacity
+*/
+void QWidget::setMask(const QRegion &newMask)
+{
+ Q_D(QWidget);
+
+ d->createExtra();
+ if (newMask == d->extra->mask)
+ return;
+
+#ifndef QT_NO_BACKINGSTORE
+ const QRegion oldMask(d->extra->mask);
+#endif
+
+ d->extra->mask = newMask;
+ d->extra->hasMask = !newMask.isEmpty();
+
+#ifndef QT_MAC_USE_COCOA
+ if (!testAttribute(Qt::WA_WState_Created))
+ return;
+#endif
+
+ d->setMask_sys(newMask);
+
+#ifndef QT_NO_BACKINGSTORE
+ if (!isVisible())
+ return;
+
+ if (!d->extra->hasMask) {
+ // Mask was cleared; update newly exposed area.
+ QRegion expose(rect());
+ expose -= oldMask;
+ if (!expose.isEmpty()) {
+ d->setDirtyOpaqueRegion();
+ update(expose);
+ }
+ return;
+ }
+
+ if (!isWindow()) {
+ // Update newly exposed area on the parent widget.
+ QRegion parentExpose(rect());
+ parentExpose -= newMask;
+ if (!parentExpose.isEmpty()) {
+ d->setDirtyOpaqueRegion();
+ parentExpose.translate(data->crect.topLeft());
+ parentWidget()->update(parentExpose);
+ }
+
+ // Update newly exposed area on this widget
+ if (!oldMask.isEmpty())
+ update(newMask - oldMask);
+ }
+#endif
+}
+
+/*!
+ \fn void QWidget::setMask(const QBitmap &bitmap)
+
+ Causes only the pixels of the widget for which \a bitmap has a
+ corresponding 1 bit to be visible. If the region includes pixels
+ outside the rect() of the widget, window system controls in that
+ area may or may not be visible, depending on the platform.
+
+ Note that this effect can be slow if the region is particularly
+ complex.
+
+ The following code shows how an image with an alpha channel can be
+ used to generate a mask for a widget:
+
+ \snippet doc/src/snippets/widget-mask/main.cpp 0
+
+ The label shown by this code is masked using the image it contains,
+ giving the appearance that an irregularly-shaped image is being drawn
+ directly onto the screen.
+
+ Masked widgets receive mouse events only on their visible
+ portions.
+
+ \sa clearMask(), windowOpacity(), {Shaped Clock Example}
+*/
+void QWidget::setMask(const QBitmap &bitmap)
+{
+ setMask(QRegion(bitmap));
+}
+
+/*!
+ \fn void QWidget::clearMask()
+
+ Removes any mask set by setMask().
+
+ \sa setMask()
+*/
+void QWidget::clearMask()
+{
+ setMask(QRegion());
+}
+
diff --git a/src/gui/kernel/qwidget.h b/src/gui/kernel/qwidget.h
new file mode 100644
index 0000000000..f54ebf9e09
--- /dev/null
+++ b/src/gui/kernel/qwidget.h
@@ -0,0 +1,1045 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QWIDGET_H
+#define QWIDGET_H
+
+#include <QtGui/qwindowdefs.h>
+#include <QtCore/qobject.h>
+#include <QtGui/qpaintdevice.h>
+#include <QtGui/qpalette.h>
+#include <QtGui/qfont.h>
+#include <QtGui/qfontmetrics.h>
+#include <QtGui/qfontinfo.h>
+#include <QtGui/qsizepolicy.h>
+#include <QtGui/qregion.h>
+#include <QtGui/qbrush.h>
+#include <QtGui/qcursor.h>
+#include <QtGui/qkeysequence.h>
+
+#ifdef QT_INCLUDE_COMPAT
+#include <QtGui/qevent.h>
+#endif
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+class QLayout;
+class QWSRegionManager;
+class QStyle;
+class QAction;
+class QVariant;
+
+class QActionEvent;
+class QMouseEvent;
+class QWheelEvent;
+class QHoverEvent;
+class QKeyEvent;
+class QFocusEvent;
+class QPaintEvent;
+class QMoveEvent;
+class QResizeEvent;
+class QCloseEvent;
+class QContextMenuEvent;
+class QInputMethodEvent;
+class QTabletEvent;
+class QDragEnterEvent;
+class QDragMoveEvent;
+class QDragLeaveEvent;
+class QDropEvent;
+class QShowEvent;
+class QHideEvent;
+class QInputContext;
+class QIcon;
+class QWindowSurface;
+class QLocale;
+class QGraphicsProxyWidget;
+#if defined(Q_WS_X11)
+class QX11Info;
+#endif
+
+class QWidgetData
+{
+public:
+ WId winid;
+ uint widget_attributes;
+ Qt::WindowFlags window_flags;
+ uint window_state : 4;
+ uint focus_policy : 4;
+ uint sizehint_forced :1;
+ uint is_closing :1;
+ uint in_show : 1;
+ uint in_set_window_state : 1;
+ mutable uint fstrut_dirty : 1;
+ uint context_menu_policy : 3;
+ uint window_modality : 2;
+ uint in_destructor : 1;
+ uint unused : 13;
+ QRect crect;
+ mutable QPalette pal;
+ QFont fnt;
+#if defined(Q_WS_QWS)
+// QRegion req_region; // Requested region
+// mutable QRegion paintable_region; // Paintable region
+// mutable bool paintable_region_dirty;// needs to be recalculated
+// mutable QRegion alloc_region; // Allocated region
+// mutable bool alloc_region_dirty; // needs to be recalculated
+// mutable int overlapping_children; // Handle overlapping children
+
+ int alloc_region_index;
+// int alloc_region_revision;
+#endif
+#if defined(Q_OS_WINCE)
+ uint window_state_internal : 4;
+#endif
+ QRect wrect;
+};
+
+class QWidgetPrivate;
+
+class Q_GUI_EXPORT QWidget : public QObject, public QPaintDevice
+{
+ Q_OBJECT
+ Q_DECLARE_PRIVATE(QWidget)
+
+ Q_PROPERTY(bool modal READ isModal)
+ Q_PROPERTY(Qt::WindowModality windowModality READ windowModality WRITE setWindowModality)
+ Q_PROPERTY(bool enabled READ isEnabled WRITE setEnabled)
+ Q_PROPERTY(QRect geometry READ geometry WRITE setGeometry)
+ Q_PROPERTY(QRect frameGeometry READ frameGeometry)
+ Q_PROPERTY(QRect normalGeometry READ normalGeometry)
+ Q_PROPERTY(int x READ x)
+ Q_PROPERTY(int y READ y)
+ Q_PROPERTY(QPoint pos READ pos WRITE move DESIGNABLE false STORED false)
+ Q_PROPERTY(QSize frameSize READ frameSize)
+ Q_PROPERTY(QSize size READ size WRITE resize DESIGNABLE false STORED false)
+ Q_PROPERTY(int width READ width)
+ Q_PROPERTY(int height READ height)
+ Q_PROPERTY(QRect rect READ rect)
+ Q_PROPERTY(QRect childrenRect READ childrenRect)
+ Q_PROPERTY(QRegion childrenRegion READ childrenRegion)
+ Q_PROPERTY(QSizePolicy sizePolicy READ sizePolicy WRITE setSizePolicy)
+ Q_PROPERTY(QSize minimumSize READ minimumSize WRITE setMinimumSize)
+ Q_PROPERTY(QSize maximumSize READ maximumSize WRITE setMaximumSize)
+ Q_PROPERTY(int minimumWidth READ minimumWidth WRITE setMinimumWidth STORED false DESIGNABLE false)
+ Q_PROPERTY(int minimumHeight READ minimumHeight WRITE setMinimumHeight STORED false DESIGNABLE false)
+ Q_PROPERTY(int maximumWidth READ maximumWidth WRITE setMaximumWidth STORED false DESIGNABLE false)
+ Q_PROPERTY(int maximumHeight READ maximumHeight WRITE setMaximumHeight STORED false DESIGNABLE false)
+ Q_PROPERTY(QSize sizeIncrement READ sizeIncrement WRITE setSizeIncrement)
+ Q_PROPERTY(QSize baseSize READ baseSize WRITE setBaseSize)
+ Q_PROPERTY(QPalette palette READ palette WRITE setPalette)
+ Q_PROPERTY(QFont font READ font WRITE setFont)
+#ifndef QT_NO_CURSOR
+ Q_PROPERTY(QCursor cursor READ cursor WRITE setCursor RESET unsetCursor)
+#endif
+ Q_PROPERTY(bool mouseTracking READ hasMouseTracking WRITE setMouseTracking)
+ Q_PROPERTY(bool isActiveWindow READ isActiveWindow)
+ Q_PROPERTY(Qt::FocusPolicy focusPolicy READ focusPolicy WRITE setFocusPolicy)
+ Q_PROPERTY(bool focus READ hasFocus)
+ Q_PROPERTY(Qt::ContextMenuPolicy contextMenuPolicy READ contextMenuPolicy WRITE setContextMenuPolicy)
+ Q_PROPERTY(bool updatesEnabled READ updatesEnabled WRITE setUpdatesEnabled DESIGNABLE false)
+ Q_PROPERTY(bool visible READ isVisible WRITE setVisible DESIGNABLE false)
+ Q_PROPERTY(bool minimized READ isMinimized)
+ Q_PROPERTY(bool maximized READ isMaximized)
+ Q_PROPERTY(bool fullScreen READ isFullScreen)
+ Q_PROPERTY(QSize sizeHint READ sizeHint)
+ Q_PROPERTY(QSize minimumSizeHint READ minimumSizeHint)
+ Q_PROPERTY(bool acceptDrops READ acceptDrops WRITE setAcceptDrops)
+ Q_PROPERTY(QString windowTitle READ windowTitle WRITE setWindowTitle DESIGNABLE isWindow)
+ Q_PROPERTY(QIcon windowIcon READ windowIcon WRITE setWindowIcon DESIGNABLE isWindow)
+ Q_PROPERTY(QString windowIconText READ windowIconText WRITE setWindowIconText DESIGNABLE isWindow)
+ Q_PROPERTY(double windowOpacity READ windowOpacity WRITE setWindowOpacity DESIGNABLE isWindow)
+ Q_PROPERTY(bool windowModified READ isWindowModified WRITE setWindowModified DESIGNABLE isWindow)
+#ifndef QT_NO_TOOLTIP
+ Q_PROPERTY(QString toolTip READ toolTip WRITE setToolTip)
+#endif
+#ifndef QT_NO_STATUSTIP
+ Q_PROPERTY(QString statusTip READ statusTip WRITE setStatusTip)
+#endif
+#ifndef QT_NO_WHATSTHIS
+ Q_PROPERTY(QString whatsThis READ whatsThis WRITE setWhatsThis)
+#endif
+#ifndef QT_NO_ACCESSIBILITY
+ Q_PROPERTY(QString accessibleName READ accessibleName WRITE setAccessibleName)
+ Q_PROPERTY(QString accessibleDescription READ accessibleDescription WRITE setAccessibleDescription)
+#endif
+ Q_PROPERTY(Qt::LayoutDirection layoutDirection READ layoutDirection WRITE setLayoutDirection RESET unsetLayoutDirection)
+ QDOC_PROPERTY(Qt::WindowFlags windowFlags READ windowFlags WRITE setWindowFlags)
+ Q_PROPERTY(bool autoFillBackground READ autoFillBackground WRITE setAutoFillBackground)
+#ifndef QT_NO_STYLE_STYLESHEET
+ Q_PROPERTY(QString styleSheet READ styleSheet WRITE setStyleSheet)
+#endif
+ Q_PROPERTY(QLocale locale READ locale WRITE setLocale RESET unsetLocale)
+ Q_PROPERTY(QString windowFilePath READ windowFilePath WRITE setWindowFilePath DESIGNABLE isWindow)
+
+public:
+ enum RenderFlag {
+ DrawWindowBackground = 0x1,
+ DrawChildren = 0x2,
+ IgnoreMask = 0x4
+ };
+ Q_DECLARE_FLAGS(RenderFlags, RenderFlag)
+
+ explicit QWidget(QWidget* parent = 0, Qt::WindowFlags f = 0);
+#ifdef QT3_SUPPORT
+ QT3_SUPPORT_CONSTRUCTOR QWidget(QWidget* parent, const char *name, Qt::WindowFlags f = 0);
+#endif
+ ~QWidget();
+
+ int devType() const;
+
+ WId winId() const;
+ void createWinId(); // internal, going away
+ inline WId internalWinId() const { return data->winid; }
+ WId effectiveWinId() const;
+
+ // GUI style setting
+ QStyle *style() const;
+ void setStyle(QStyle *);
+ // Widget types and states
+
+ bool isTopLevel() const;
+ bool isWindow() const;
+
+ bool isModal() const;
+ Qt::WindowModality windowModality() const;
+ void setWindowModality(Qt::WindowModality windowModality);
+
+ bool isEnabled() const;
+ bool isEnabledTo(QWidget*) const;
+ bool isEnabledToTLW() const;
+
+public Q_SLOTS:
+ void setEnabled(bool);
+ void setDisabled(bool);
+ void setWindowModified(bool);
+
+ // Widget coordinates
+
+public:
+ QRect frameGeometry() const;
+ const QRect &geometry() const;
+ QRect normalGeometry() const;
+
+ int x() const;
+ int y() const;
+ QPoint pos() const;
+ QSize frameSize() const;
+ QSize size() const;
+ inline int width() const;
+ inline int height() const;
+ inline QRect rect() const;
+ QRect childrenRect() const;
+ QRegion childrenRegion() const;
+
+ QSize minimumSize() const;
+ QSize maximumSize() const;
+ int minimumWidth() const;
+ int minimumHeight() const;
+ int maximumWidth() const;
+ int maximumHeight() const;
+ void setMinimumSize(const QSize &);
+ void setMinimumSize(int minw, int minh);
+ void setMaximumSize(const QSize &);
+ void setMaximumSize(int maxw, int maxh);
+ void setMinimumWidth(int minw);
+ void setMinimumHeight(int minh);
+ void setMaximumWidth(int maxw);
+ void setMaximumHeight(int maxh);
+
+ QSize sizeIncrement() const;
+ void setSizeIncrement(const QSize &);
+ void setSizeIncrement(int w, int h);
+ QSize baseSize() const;
+ void setBaseSize(const QSize &);
+ void setBaseSize(int basew, int baseh);
+
+ void setFixedSize(const QSize &);
+ void setFixedSize(int w, int h);
+ void setFixedWidth(int w);
+ void setFixedHeight(int h);
+
+ // Widget coordinate mapping
+
+ QPoint mapToGlobal(const QPoint &) const;
+ QPoint mapFromGlobal(const QPoint &) const;
+ QPoint mapToParent(const QPoint &) const;
+ QPoint mapFromParent(const QPoint &) const;
+ QPoint mapTo(QWidget *, const QPoint &) const;
+ QPoint mapFrom(QWidget *, const QPoint &) const;
+
+ QWidget *window() const;
+ QWidget *nativeParentWidget() const;
+ inline QWidget *topLevelWidget() const { return window(); }
+
+ // Widget appearance functions
+ const QPalette &palette() const;
+ void setPalette(const QPalette &);
+
+ void setBackgroundRole(QPalette::ColorRole);
+ QPalette::ColorRole backgroundRole() const;
+
+ void setForegroundRole(QPalette::ColorRole);
+ QPalette::ColorRole foregroundRole() const;
+
+ const QFont &font() const;
+ void setFont(const QFont &);
+ QFontMetrics fontMetrics() const;
+ QFontInfo fontInfo() const;
+
+#ifndef QT_NO_CURSOR
+ QCursor cursor() const;
+ void setCursor(const QCursor &);
+ void unsetCursor();
+#endif
+
+ void setMouseTracking(bool enable);
+ bool hasMouseTracking() const;
+ bool underMouse() const;
+
+ void setMask(const QBitmap &);
+ void setMask(const QRegion &);
+ QRegion mask() const;
+ void clearMask();
+
+ void render(QPaintDevice *target, const QPoint &targetOffset = QPoint(),
+ const QRegion &sourceRegion = QRegion(),
+ RenderFlags renderFlags = RenderFlags(DrawWindowBackground | DrawChildren));
+
+ void render(QPainter *painter, const QPoint &targetOffset = QPoint(),
+ const QRegion &sourceRegion = QRegion(),
+ RenderFlags renderFlags = RenderFlags(DrawWindowBackground | DrawChildren));
+
+public Q_SLOTS:
+ void setWindowTitle(const QString &);
+#ifndef QT_NO_STYLE_STYLESHEET
+ void setStyleSheet(const QString& styleSheet);
+#endif
+public:
+#ifndef QT_NO_STYLE_STYLESHEET
+ QString styleSheet() const;
+#endif
+ QString windowTitle() const;
+ void setWindowIcon(const QIcon &icon);
+ QIcon windowIcon() const;
+ void setWindowIconText(const QString &);
+ QString windowIconText() const;
+ void setWindowRole(const QString &);
+ QString windowRole() const;
+ void setWindowFilePath(const QString &filePath);
+ QString windowFilePath() const;
+
+ void setWindowOpacity(qreal level);
+ qreal windowOpacity() const;
+
+ bool isWindowModified() const;
+#ifndef QT_NO_TOOLTIP
+ void setToolTip(const QString &);
+ QString toolTip() const;
+#endif
+#ifndef QT_NO_STATUSTIP
+ void setStatusTip(const QString &);
+ QString statusTip() const;
+#endif
+#ifndef QT_NO_WHATSTHIS
+ void setWhatsThis(const QString &);
+ QString whatsThis() const;
+#endif
+#ifndef QT_NO_ACCESSIBILITY
+ QString accessibleName() const;
+ void setAccessibleName(const QString &name);
+ QString accessibleDescription() const;
+ void setAccessibleDescription(const QString &description);
+#endif
+
+ void setLayoutDirection(Qt::LayoutDirection direction);
+ Qt::LayoutDirection layoutDirection() const;
+ void unsetLayoutDirection();
+
+ void setLocale(const QLocale &locale);
+ QLocale locale() const;
+ void unsetLocale();
+
+ inline bool isRightToLeft() const { return layoutDirection() == Qt::RightToLeft; }
+ inline bool isLeftToRight() const { return layoutDirection() == Qt::LeftToRight; }
+
+public Q_SLOTS:
+ inline void setFocus() { setFocus(Qt::OtherFocusReason); }
+
+public:
+ bool isActiveWindow() const;
+ void activateWindow();
+ void clearFocus();
+
+ void setFocus(Qt::FocusReason reason);
+ Qt::FocusPolicy focusPolicy() const;
+ void setFocusPolicy(Qt::FocusPolicy policy);
+ bool hasFocus() const;
+ static void setTabOrder(QWidget *, QWidget *);
+ void setFocusProxy(QWidget *);
+ QWidget *focusProxy() const;
+ Qt::ContextMenuPolicy contextMenuPolicy() const;
+ void setContextMenuPolicy(Qt::ContextMenuPolicy policy);
+
+ // Grab functions
+ void grabMouse();
+#ifndef QT_NO_CURSOR
+ void grabMouse(const QCursor &);
+#endif
+ void releaseMouse();
+ void grabKeyboard();
+ void releaseKeyboard();
+#ifndef QT_NO_SHORTCUT
+ int grabShortcut(const QKeySequence &key, Qt::ShortcutContext context = Qt::WindowShortcut);
+ void releaseShortcut(int id);
+ void setShortcutEnabled(int id, bool enable = true);
+ void setShortcutAutoRepeat(int id, bool enable = true);
+#endif
+ static QWidget *mouseGrabber();
+ static QWidget *keyboardGrabber();
+
+ // Update/refresh functions
+ inline bool updatesEnabled() const;
+ void setUpdatesEnabled(bool enable);
+
+#if 0 //def Q_WS_QWS
+ void repaintUnclipped(const QRegion &, bool erase = true);
+#endif
+
+#ifndef QT_NO_GRAPHICSVIEW
+ QGraphicsProxyWidget *graphicsProxyWidget() const;
+#endif
+
+public Q_SLOTS:
+ void update();
+ void repaint();
+
+public:
+ inline void update(int x, int y, int w, int h);
+ void update(const QRect&);
+ void update(const QRegion&);
+
+ void repaint(int x, int y, int w, int h);
+ void repaint(const QRect &);
+ void repaint(const QRegion &);
+
+public Q_SLOTS:
+ // Widget management functions
+
+ virtual void setVisible(bool visible);
+ inline void setHidden(bool hidden) { setVisible(!hidden); }
+#ifndef Q_OS_WINCE
+ inline void show() { setVisible(true); }
+#else
+ void show();
+#endif
+ inline void hide() { setVisible(false); }
+ inline QT_MOC_COMPAT void setShown(bool shown) { setVisible(shown); }
+
+ void showMinimized();
+ void showMaximized();
+ void showFullScreen();
+ void showNormal();
+
+ bool close();
+ void raise();
+ void lower();
+
+public:
+ void stackUnder(QWidget*);
+ void move(int x, int y);
+ void move(const QPoint &);
+ void resize(int w, int h);
+ void resize(const QSize &);
+ inline void setGeometry(int x, int y, int w, int h);
+ void setGeometry(const QRect &);
+ QByteArray saveGeometry() const;
+ bool restoreGeometry(const QByteArray &geometry);
+ void adjustSize();
+ bool isVisible() const;
+ bool isVisibleTo(QWidget*) const;
+ // ### Qt 5: bool isVisibleTo(_const_ QWidget *) const
+ inline bool isHidden() const;
+
+ bool isMinimized() const;
+ bool isMaximized() const;
+ bool isFullScreen() const;
+
+ Qt::WindowStates windowState() const;
+ void setWindowState(Qt::WindowStates state);
+ void overrideWindowState(Qt::WindowStates state);
+
+ virtual QSize sizeHint() const;
+ virtual QSize minimumSizeHint() const;
+
+ QSizePolicy sizePolicy() const;
+ void setSizePolicy(QSizePolicy);
+ inline void setSizePolicy(QSizePolicy::Policy horizontal, QSizePolicy::Policy vertical);
+ virtual int heightForWidth(int) const;
+
+ QRegion visibleRegion() const;
+
+ void setContentsMargins(int left, int top, int right, int bottom);
+ void getContentsMargins(int *left, int *top, int *right, int *bottom) const;
+ QRect contentsRect() const;
+
+public:
+ QLayout *layout() const;
+ void setLayout(QLayout *);
+ void updateGeometry();
+
+ void setParent(QWidget *parent);
+ void setParent(QWidget *parent, Qt::WindowFlags f);
+
+ void scroll(int dx, int dy);
+ void scroll(int dx, int dy, const QRect&);
+
+ // Misc. functions
+
+ QWidget *focusWidget() const;
+ QWidget *nextInFocusChain() const;
+
+ // drag and drop
+ bool acceptDrops() const;
+ void setAcceptDrops(bool on);
+
+#ifndef QT_NO_ACTION
+ //actions
+ void addAction(QAction *action);
+ void addActions(QList<QAction*> actions);
+ void insertAction(QAction *before, QAction *action);
+ void insertActions(QAction *before, QList<QAction*> actions);
+ void removeAction(QAction *action);
+ QList<QAction*> actions() const;
+#endif
+
+ QWidget *parentWidget() const;
+
+ void setWindowFlags(Qt::WindowFlags type);
+ inline Qt::WindowFlags windowFlags() const;
+ void overrideWindowFlags(Qt::WindowFlags type);
+
+ inline Qt::WindowType windowType() const;
+
+ static QWidget *find(WId);
+#ifdef QT3_SUPPORT
+ static QT3_SUPPORT QWidgetMapper *wmapper();
+#endif
+ inline QWidget *childAt(int x, int y) const;
+ QWidget *childAt(const QPoint &p) const;
+
+#if defined(Q_WS_X11)
+ const QX11Info &x11Info() const;
+ Qt::HANDLE x11PictureHandle() const;
+#endif
+
+#if defined(Q_WS_MAC)
+ Qt::HANDLE macQDHandle() const;
+ Qt::HANDLE macCGHandle() const;
+#endif
+
+#if defined(Q_WS_WIN)
+ HDC getDC() const;
+ void releaseDC(HDC) const;
+#else
+ Qt::HANDLE handle() const;
+#endif
+
+ void setAttribute(Qt::WidgetAttribute, bool on = true);
+ inline bool testAttribute(Qt::WidgetAttribute) const;
+
+ QPaintEngine *paintEngine() const;
+
+ void ensurePolished() const;
+
+ QInputContext *inputContext();
+ void setInputContext(QInputContext *);
+
+ bool isAncestorOf(const QWidget *child) const;
+
+#ifdef QT_KEYPAD_NAVIGATION
+ bool hasEditFocus() const;
+ void setEditFocus(bool on);
+#endif
+
+ bool autoFillBackground() const;
+ void setAutoFillBackground(bool enabled);
+
+ void setWindowSurface(QWindowSurface *surface);
+ QWindowSurface *windowSurface() const;
+
+Q_SIGNALS:
+ void customContextMenuRequested(const QPoint &pos);
+
+protected:
+ // Event handlers
+ bool event(QEvent *);
+ virtual void mousePressEvent(QMouseEvent *);
+ virtual void mouseReleaseEvent(QMouseEvent *);
+ virtual void mouseDoubleClickEvent(QMouseEvent *);
+ virtual void mouseMoveEvent(QMouseEvent *);
+#ifndef QT_NO_WHEELEVENT
+ virtual void wheelEvent(QWheelEvent *);
+#endif
+ virtual void keyPressEvent(QKeyEvent *);
+ virtual void keyReleaseEvent(QKeyEvent *);
+ virtual void focusInEvent(QFocusEvent *);
+ virtual void focusOutEvent(QFocusEvent *);
+ virtual void enterEvent(QEvent *);
+ virtual void leaveEvent(QEvent *);
+ virtual void paintEvent(QPaintEvent *);
+ virtual void moveEvent(QMoveEvent *);
+ virtual void resizeEvent(QResizeEvent *);
+ virtual void closeEvent(QCloseEvent *);
+#ifndef QT_NO_CONTEXTMENU
+ virtual void contextMenuEvent(QContextMenuEvent *);
+#endif
+#ifndef QT_NO_TABLETEVENT
+ virtual void tabletEvent(QTabletEvent *);
+#endif
+#ifndef QT_NO_ACTION
+ virtual void actionEvent(QActionEvent *);
+#endif
+
+#ifndef QT_NO_DRAGANDDROP
+ virtual void dragEnterEvent(QDragEnterEvent *);
+ virtual void dragMoveEvent(QDragMoveEvent *);
+ virtual void dragLeaveEvent(QDragLeaveEvent *);
+ virtual void dropEvent(QDropEvent *);
+#endif
+
+ virtual void showEvent(QShowEvent *);
+ virtual void hideEvent(QHideEvent *);
+
+#if defined(Q_WS_MAC)
+ virtual bool macEvent(EventHandlerCallRef, EventRef);
+#endif
+#if defined(Q_WS_WIN)
+ virtual bool winEvent(MSG *message, long *result);
+#endif
+#if defined(Q_WS_X11)
+ virtual bool x11Event(XEvent *);
+#endif
+#if defined(Q_WS_QWS)
+ virtual bool qwsEvent(QWSEvent *);
+#endif
+
+ // Misc. protected functions
+ virtual void changeEvent(QEvent *);
+
+ int metric(PaintDeviceMetric) const;
+
+ virtual void inputMethodEvent(QInputMethodEvent *);
+public:
+ virtual QVariant inputMethodQuery(Qt::InputMethodQuery) const;
+protected:
+ void resetInputContext();
+protected Q_SLOTS:
+ void updateMicroFocus();
+protected:
+
+ void create(WId = 0, bool initializeWindow = true,
+ bool destroyOldWindow = true);
+ void destroy(bool destroyWindow = true,
+ bool destroySubWindows = true);
+
+ virtual bool focusNextPrevChild(bool next);
+ inline bool focusNextChild() { return focusNextPrevChild(true); }
+ inline bool focusPreviousChild() { return focusNextPrevChild(false); }
+
+protected:
+ QWidget(QWidgetPrivate &d, QWidget* parent, Qt::WindowFlags f);
+private:
+
+ bool testAttribute_helper(Qt::WidgetAttribute) const;
+
+ QLayout *takeLayout();
+
+ friend class QBackingStoreDevice;
+ friend class QWidgetBackingStore;
+ friend class QApplication;
+ friend class QApplicationPrivate;
+ friend class QBaseApplication;
+ friend class QPainter;
+ friend class QPainterPrivate;
+ friend class QPixmap; // for QPixmap::fill()
+ friend class QFontMetrics;
+ friend class QFontInfo;
+ friend class QETWidget;
+ friend class QLayout;
+ friend class QWidgetItem;
+ friend class QWidgetItemV2;
+ friend class QGLContext;
+ friend class QGLWidget;
+ friend class QGLWindowSurface;
+ friend class QVGWindowSurface;
+ friend class QX11PaintEngine;
+ friend class QWin32PaintEngine;
+ friend class QShortcutPrivate;
+ friend class QShortcutMap;
+ friend class QWindowSurface;
+ friend class QD3DWindowSurface;
+ friend class QGraphicsProxyWidget;
+ friend class QGraphicsProxyWidgetPrivate;
+ friend class QStyleSheetStyle;
+
+#ifdef Q_WS_MAC
+ friend class QCoreGraphicsPaintEnginePrivate;
+ friend QPoint qt_mac_posInWindow(const QWidget *w);
+ friend OSWindowRef qt_mac_window_for(const QWidget *w);
+ friend bool qt_mac_is_metal(const QWidget *w);
+ friend OSViewRef qt_mac_nativeview_for(const QWidget *w);
+ friend void qt_event_request_window_change(QWidget *widget);
+ friend bool qt_mac_sendMacEventToWidget(QWidget *widget, EventRef ref);
+#endif
+#ifdef Q_WS_QWS
+ friend class QWSBackingStore;
+ friend class QWSManager;
+ friend class QWSManagerPrivate;
+ friend class QDecoration;
+ friend class QWSWindowSurface;
+ friend class QScreen;
+ friend class QVNCScreen;
+ friend bool isWidgetOpaque(const QWidget *);
+ friend class QGLWidgetPrivate;
+#endif
+#ifdef Q_WS_X11
+ friend void qt_net_update_user_time(QWidget *tlw, unsigned long timestamp);
+ friend void qt_net_remove_user_time(QWidget *tlw);
+#endif
+
+ friend Q_GUI_EXPORT QWidgetData *qt_qwidget_data(QWidget *widget);
+ friend Q_GUI_EXPORT QWidgetPrivate *qt_widget_private(QWidget *widget);
+
+private:
+ Q_DISABLE_COPY(QWidget)
+ Q_PRIVATE_SLOT(d_func(), void _q_showIfNotHidden())
+
+ QWidgetData *data;
+
+#ifdef QT3_SUPPORT
+public:
+ inline QT3_SUPPORT bool isUpdatesEnabled() const { return updatesEnabled(); }
+ QT3_SUPPORT QStyle *setStyle(const QString&);
+ inline QT3_SUPPORT bool isVisibleToTLW() const;
+ QT3_SUPPORT QRect visibleRect() const;
+ inline QT3_SUPPORT void iconify() { showMinimized(); }
+ inline QT3_SUPPORT void constPolish() const { ensurePolished(); }
+ inline QT3_SUPPORT void polish() { ensurePolished(); }
+ inline QT3_SUPPORT void reparent(QWidget *parent, Qt::WindowFlags f, const QPoint &p, bool showIt=false)
+ { setParent(parent, f); setGeometry(p.x(),p.y(),width(),height()); if (showIt) show(); }
+ inline QT3_SUPPORT void reparent(QWidget *parent, const QPoint &p, bool showIt=false)
+ { setParent(parent, windowFlags() & ~Qt::WindowType_Mask); setGeometry(p.x(),p.y(),width(),height()); if (showIt) show(); }
+ inline QT3_SUPPORT void recreate(QWidget *parent, Qt::WindowFlags f, const QPoint & p, bool showIt=false)
+ { setParent(parent, f); setGeometry(p.x(),p.y(),width(),height()); if (showIt) show(); }
+ inline QT3_SUPPORT void setSizePolicy(QSizePolicy::Policy hor, QSizePolicy::Policy ver, bool hfw)
+ { QSizePolicy sp(hor, ver); sp.setHeightForWidth(hfw); setSizePolicy(sp);}
+ inline QT3_SUPPORT bool hasMouse() const { return testAttribute(Qt::WA_UnderMouse); }
+#ifndef QT_NO_CURSOR
+ inline QT3_SUPPORT bool ownCursor() const { return testAttribute(Qt::WA_SetCursor); }
+#endif
+ inline QT3_SUPPORT bool ownFont() const { return testAttribute(Qt::WA_SetFont); }
+ inline QT3_SUPPORT void unsetFont() { setFont(QFont()); }
+ inline QT3_SUPPORT bool ownPalette() const { return testAttribute(Qt::WA_SetPalette); }
+ inline QT3_SUPPORT void unsetPalette() { setPalette(QPalette()); }
+ Qt::BackgroundMode QT3_SUPPORT backgroundMode() const;
+ void QT3_SUPPORT setBackgroundMode(Qt::BackgroundMode, Qt::BackgroundMode = Qt::PaletteBackground);
+ const QT3_SUPPORT QColor &eraseColor() const;
+ void QT3_SUPPORT setEraseColor(const QColor &);
+ const QT3_SUPPORT QColor &foregroundColor() const;
+ const QT3_SUPPORT QPixmap *erasePixmap() const;
+ void QT3_SUPPORT setErasePixmap(const QPixmap &);
+ const QT3_SUPPORT QColor &paletteForegroundColor() const;
+ void QT3_SUPPORT setPaletteForegroundColor(const QColor &);
+ const QT3_SUPPORT QColor &paletteBackgroundColor() const;
+ void QT3_SUPPORT setPaletteBackgroundColor(const QColor &);
+ const QT3_SUPPORT QPixmap *paletteBackgroundPixmap() const;
+ void QT3_SUPPORT setPaletteBackgroundPixmap(const QPixmap &);
+ const QT3_SUPPORT QBrush& backgroundBrush() const;
+ const QT3_SUPPORT QColor &backgroundColor() const;
+ const QT3_SUPPORT QPixmap *backgroundPixmap() const;
+ void QT3_SUPPORT setBackgroundPixmap(const QPixmap &);
+ QT3_SUPPORT void setBackgroundColor(const QColor &);
+ QT3_SUPPORT QColorGroup colorGroup() const;
+ QT3_SUPPORT QWidget *parentWidget(bool sameWindow) const;
+ inline QT3_SUPPORT void setKeyCompression(bool b) { setAttribute(Qt::WA_KeyCompression, b); }
+ inline QT3_SUPPORT void setFont(const QFont &f, bool) { setFont(f); }
+ inline QT3_SUPPORT void setPalette(const QPalette &p, bool) { setPalette(p); }
+ enum BackgroundOrigin { WidgetOrigin, ParentOrigin, WindowOrigin, AncestorOrigin };
+ inline QT3_SUPPORT void setBackgroundOrigin(BackgroundOrigin){};
+ inline QT3_SUPPORT BackgroundOrigin backgroundOrigin() const { return WindowOrigin; }
+ inline QT3_SUPPORT QPoint backgroundOffset() const { return QPoint(); }
+ inline QT3_SUPPORT void repaint(bool) { repaint(); }
+ inline QT3_SUPPORT void repaint(int x, int y, int w, int h, bool) { repaint(x,y,w,h); }
+ inline QT3_SUPPORT void repaint(const QRect &r, bool) { repaint(r); }
+ inline QT3_SUPPORT void repaint(const QRegion &rgn, bool) { repaint(rgn); }
+ QT3_SUPPORT void erase();
+ inline QT3_SUPPORT void erase(int x, int y, int w, int h) { erase_helper(x, y, w, h); }
+ QT3_SUPPORT void erase(const QRect &);
+ QT3_SUPPORT void erase(const QRegion &);
+ QT3_SUPPORT void drawText(const QPoint &p, const QString &s)
+ { drawText_helper(p.x(), p.y(), s); }
+ inline QT3_SUPPORT void drawText(int x, int y, const QString &s)
+ { drawText_helper(x, y, s); }
+ QT3_SUPPORT bool close(bool);
+ inline QT3_SUPPORT QWidget *childAt(int x, int y, bool includeThis) const
+ {
+ QWidget *w = childAt(x, y);
+ return w ? w : ((includeThis && rect().contains(x,y))?const_cast<QWidget*>(this):0);
+ }
+ inline QT3_SUPPORT QWidget *childAt(const QPoint &p, bool includeThis) const
+ {
+ QWidget *w = childAt(p);
+ return w ? w : ((includeThis && rect().contains(p))?const_cast<QWidget*>(this):0);
+ }
+ inline QT3_SUPPORT void setCaption(const QString &c) { setWindowTitle(c); }
+ QT3_SUPPORT void setIcon(const QPixmap &i);
+ inline QT3_SUPPORT void setIconText(const QString &it) { setWindowIconText(it); }
+ inline QT3_SUPPORT QString caption() const { return windowTitle(); }
+ QT3_SUPPORT const QPixmap *icon() const;
+ inline QT3_SUPPORT QString iconText() const { return windowIconText(); }
+ inline QT3_SUPPORT void setInputMethodEnabled(bool b) { setAttribute(Qt::WA_InputMethodEnabled, b); }
+ inline QT3_SUPPORT bool isInputMethodEnabled() const { return testAttribute(Qt::WA_InputMethodEnabled); }
+ inline QT3_SUPPORT void setActiveWindow() { activateWindow(); }
+ inline QT3_SUPPORT bool isShown() const { return !isHidden(); }
+ inline QT3_SUPPORT bool isDialog() const { return windowType() == Qt::Dialog; }
+ inline QT3_SUPPORT bool isPopup() const { return windowType() == Qt::Popup; }
+ inline QT3_SUPPORT bool isDesktop() const { return windowType() == Qt::Desktop; }
+
+
+private:
+ void drawText_helper(int x, int y, const QString &);
+ void erase_helper(int x, int y, int w, int h);
+#endif // QT3_SUPPORT
+
+protected:
+ virtual void styleChange(QStyle&); // compat
+ virtual void enabledChange(bool); // compat
+ virtual void paletteChange(const QPalette &); // compat
+ virtual void fontChange(const QFont &); // compat
+ virtual void windowActivationChange(bool); // compat
+ virtual void languageChange(); // compat
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(QWidget::RenderFlags)
+
+#if defined Q_CC_MSVC && _MSC_VER < 1300
+template <> inline QWidget *qobject_cast_helper<QWidget*>(QObject *o, QWidget *)
+{
+ if (!o || !o->isWidgetType()) return 0;
+ return (QWidget*)(o);
+}
+#else
+template <> inline QWidget *qobject_cast<QWidget*>(QObject *o)
+{
+ if (!o || !o->isWidgetType()) return 0;
+ return static_cast<QWidget*>(o);
+}
+template <> inline const QWidget *qobject_cast<const QWidget*>(const QObject *o)
+{
+ if (!o || !o->isWidgetType()) return 0;
+ return static_cast<const QWidget*>(o);
+}
+#endif
+
+inline QWidget *QWidget::childAt(int ax, int ay) const
+{ return childAt(QPoint(ax, ay)); }
+
+inline Qt::WindowType QWidget::windowType() const
+{ return static_cast<Qt::WindowType>(int(data->window_flags & Qt::WindowType_Mask)); }
+inline Qt::WindowFlags QWidget::windowFlags() const
+{ return data->window_flags; }
+
+inline bool QWidget::isTopLevel() const
+{ return (windowType() & Qt::Window); }
+
+inline bool QWidget::isWindow() const
+{ return (windowType() & Qt::Window); }
+
+inline bool QWidget::isEnabled() const
+{ return !testAttribute(Qt::WA_Disabled); }
+
+inline bool QWidget::isModal() const
+{ return data->window_modality != Qt::NonModal; }
+
+inline bool QWidget::isEnabledToTLW() const
+{ return isEnabled(); }
+
+inline int QWidget::minimumWidth() const
+{ return minimumSize().width(); }
+
+inline int QWidget::minimumHeight() const
+{ return minimumSize().height(); }
+
+inline int QWidget::maximumWidth() const
+{ return maximumSize().width(); }
+
+inline int QWidget::maximumHeight() const
+{ return maximumSize().height(); }
+
+inline void QWidget::setMinimumSize(const QSize &s)
+{ setMinimumSize(s.width(),s.height()); }
+
+inline void QWidget::setMaximumSize(const QSize &s)
+{ setMaximumSize(s.width(),s.height()); }
+
+inline void QWidget::setSizeIncrement(const QSize &s)
+{ setSizeIncrement(s.width(),s.height()); }
+
+inline void QWidget::setBaseSize(const QSize &s)
+{ setBaseSize(s.width(),s.height()); }
+
+inline const QFont &QWidget::font() const
+{ return data->fnt; }
+
+inline QFontMetrics QWidget::fontMetrics() const
+{ return QFontMetrics(data->fnt); }
+
+inline QFontInfo QWidget::fontInfo() const
+{ return QFontInfo(data->fnt); }
+
+inline void QWidget::setMouseTracking(bool enable)
+{ setAttribute(Qt::WA_MouseTracking, enable); }
+
+inline bool QWidget::hasMouseTracking() const
+{ return testAttribute(Qt::WA_MouseTracking); }
+
+inline bool QWidget::underMouse() const
+{ return testAttribute(Qt::WA_UnderMouse); }
+
+inline bool QWidget::updatesEnabled() const
+{ return !testAttribute(Qt::WA_UpdatesDisabled); }
+
+inline void QWidget::update(int ax, int ay, int aw, int ah)
+{ update(QRect(ax, ay, aw, ah)); }
+
+inline bool QWidget::isVisible() const
+{ return testAttribute(Qt::WA_WState_Visible); }
+
+inline bool QWidget::isHidden() const
+{ return testAttribute(Qt::WA_WState_Hidden); }
+
+inline void QWidget::move(int ax, int ay)
+{ move(QPoint(ax, ay)); }
+
+inline void QWidget::resize(int w, int h)
+{ resize(QSize(w, h)); }
+
+inline void QWidget::setGeometry(int ax, int ay, int aw, int ah)
+{ setGeometry(QRect(ax, ay, aw, ah)); }
+
+inline QRect QWidget::rect() const
+{ return QRect(0,0,data->crect.width(),data->crect.height()); }
+
+inline const QRect &QWidget::geometry() const
+{ return data->crect; }
+
+inline QSize QWidget::size() const
+{ return data->crect.size(); }
+
+inline int QWidget::width() const
+{ return data->crect.width(); }
+
+inline int QWidget::height() const
+{ return data->crect.height(); }
+
+inline QWidget *QWidget::parentWidget() const
+{ return static_cast<QWidget *>(QObject::parent()); }
+
+inline void QWidget::setSizePolicy(QSizePolicy::Policy hor, QSizePolicy::Policy ver)
+{ setSizePolicy(QSizePolicy(hor, ver)); }
+
+inline bool QWidget::testAttribute(Qt::WidgetAttribute attribute) const
+{
+ if (attribute < int(8*sizeof(uint)))
+ return data->widget_attributes & (1<<attribute);
+ return testAttribute_helper(attribute);
+}
+
+#ifdef QT3_SUPPORT
+inline bool QWidget::isVisibleToTLW() const
+{ return isVisible(); }
+inline QWidget *QWidget::parentWidget(bool sameWindow) const
+{
+ if (sameWindow && isWindow())
+ return 0;
+ return static_cast<QWidget *>(QObject::parent());
+}
+inline QColorGroup QWidget::colorGroup() const
+{ return QColorGroup(palette()); }
+inline void QWidget::setPaletteForegroundColor(const QColor &c)
+{ QPalette p = palette(); p.setColor(foregroundRole(), c); setPalette(p); }
+inline const QBrush& QWidget::backgroundBrush() const { return palette().brush(backgroundRole()); }
+inline void QWidget::setBackgroundPixmap(const QPixmap &pm)
+{ QPalette p = palette(); p.setBrush(backgroundRole(), QBrush(pm)); setPalette(p); }
+inline const QPixmap *QWidget::backgroundPixmap() const { return 0; }
+inline void QWidget::setBackgroundColor(const QColor &c)
+{ QPalette p = palette(); p.setColor(backgroundRole(), c); setPalette(p); }
+inline const QColor & QWidget::backgroundColor() const { return palette().color(backgroundRole()); }
+inline const QColor &QWidget::foregroundColor() const { return palette().color(foregroundRole());}
+inline const QColor &QWidget::eraseColor() const { return palette().color(backgroundRole()); }
+inline void QWidget::setEraseColor(const QColor &c)
+{ QPalette p = palette(); p.setColor(backgroundRole(), c); setPalette(p); }
+inline const QPixmap *QWidget::erasePixmap() const { return 0; }
+inline void QWidget::setErasePixmap(const QPixmap &pm)
+{ QPalette p = palette(); p.setBrush(backgroundRole(), QBrush(pm)); setPalette(p); }
+inline const QColor &QWidget::paletteForegroundColor() const { return palette().color(foregroundRole());}
+inline const QColor &QWidget::paletteBackgroundColor() const { return palette().color(backgroundRole()); }
+inline void QWidget::setPaletteBackgroundColor(const QColor &c)
+{ QPalette p = palette(); p.setColor(backgroundRole(), c); setPalette(p); }
+inline const QPixmap *QWidget::paletteBackgroundPixmap() const
+{ return 0; }
+inline void QWidget::setPaletteBackgroundPixmap(const QPixmap &pm)
+{ QPalette p = palette(); p.setBrush(backgroundRole(), QBrush(pm)); setPalette(p); }
+inline QT3_SUPPORT void QWidget::erase() { erase_helper(0, 0, data->crect.width(), data->crect.height()); }
+inline QT3_SUPPORT void QWidget::erase(const QRect &r) { erase_helper(r.x(), r.y(), r.width(), r.height()); }
+#endif
+
+#define QWIDGETSIZE_MAX ((1<<24)-1)
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QWIDGET_H
diff --git a/src/gui/kernel/qwidget_mac.mm b/src/gui/kernel/qwidget_mac.mm
new file mode 100644
index 0000000000..f599b7cf25
--- /dev/null
+++ b/src/gui/kernel/qwidget_mac.mm
@@ -0,0 +1,4842 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/****************************************************************************
+**
+** Copyright (c) 2007-2008, Apple, Inc.
+**
+** All rights reserved.
+**
+** Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are met:
+**
+** * Redistributions of source code must retain the above copyright notice,
+** this list of conditions and the following disclaimer.
+**
+** * Redistributions in binary form must reproduce the above copyright notice,
+** this list of conditions and the following disclaimer in the documentation
+** and/or other materials provided with the distribution.
+**
+** * Neither the name of Apple, Inc. nor the names of its contributors
+** may be used to endorse or promote products derived from this software
+** without specific prior written permission.
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+** CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+** EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+** PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+** PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+** LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+** NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+** SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+**
+****************************************************************************/
+//#define QT_RASTER_PAINTENGINE
+
+#include <private/qt_mac_p.h>
+#include <private/qeventdispatcher_mac_p.h>
+
+#include "qapplication.h"
+#include "qapplication_p.h"
+#include "qbitmap.h"
+#include "qcursor.h"
+#include "qdesktopwidget.h"
+#include "qevent.h"
+#include "qimage.h"
+#include "qlayout.h"
+#include "qmenubar.h"
+#include <private/qbackingstore_p.h>
+#ifdef QT_RASTER_PAINTENGINE
+# include <private/qpaintengine_raster_p.h>
+#endif
+#include <private/qwindowsurface_mac_p.h>
+#include <private/qpaintengine_mac_p.h>
+#include "qpainter.h"
+#include "qstyle.h"
+#include "qtimer.h"
+#include "qfocusframe.h"
+#include "qdebug.h"
+#include <private/qmainwindowlayout_p.h>
+
+#include <private/qabstractscrollarea_p.h>
+#include <qabstractscrollarea.h>
+#include <ApplicationServices/ApplicationServices.h>
+#include <limits.h>
+#include <private/qt_cocoa_helpers_mac_p.h>
+#include <private/qcocoaview_mac_p.h>
+#include <private/qcocoawindow_mac_p.h>
+#include <private/qcocoawindowdelegate_mac_p.h>
+#include <private/qcocoapanel_mac_p.h>
+
+#include "qwidget_p.h"
+#include "qdnd_p.h"
+#include <QtGui/qgraphicsproxywidget.h>
+
+QT_BEGIN_NAMESPACE
+
+#define XCOORD_MAX 16383
+#define WRECT_MAX 8191
+
+#ifndef QT_MAC_USE_COCOA
+
+extern "C" {
+ extern OSStatus _HIViewScrollRectWithOptions(HIViewRef, const HIRect *, CGFloat, CGFloat,
+ OptionBits);
+}
+#define kHIViewScrollRectAdjustInvalid 1
+#define kHIViewScrollRectDontInvalidateRevealedArea 2
+#endif
+
+
+/*****************************************************************************
+ QWidget debug facilities
+ *****************************************************************************/
+//#define DEBUG_WINDOW_RGNS
+//#define DEBUG_WINDOW_CREATE
+//#define DEBUG_WINDOW_STATE
+//#define DEBUG_WIDGET_PAINT
+
+/*****************************************************************************
+ QWidget globals
+ *****************************************************************************/
+#ifndef QT_MAC_USE_COCOA
+typedef QHash<Qt::WindowFlags, WindowGroupRef> WindowGroupHash;
+Q_GLOBAL_STATIC(WindowGroupHash, qt_mac_window_groups)
+const UInt32 kWidgetCreatorQt = kEventClassQt;
+enum {
+ kWidgetPropertyQWidget = 'QWId' //QWidget *
+};
+#endif
+
+static bool qt_mac_raise_process = true;
+static OSWindowRef qt_root_win = 0;
+QWidget *mac_mouse_grabber = 0;
+QWidget *mac_keyboard_grabber = 0;
+
+#ifndef QT_MAC_USE_COCOA
+#ifdef QT_NAMESPACE
+
+// produce the string "com.trolltech.qt-namespace.widget", where "namespace" is the contents of QT_NAMESPACE.
+#define SS(x) #x
+#define S0(x) SS(x)
+#define S "com.trolltech.qt-" S0(QT_NAMESPACE) ".widget"
+
+static CFStringRef kObjectQWidget = CFSTR(S);
+
+#undef SS
+#undef S0
+#undef S
+
+#else
+static CFStringRef kObjectQWidget = CFSTR("com.trolltech.qt.widget");
+#endif // QT_NAMESPACE
+#endif // QT_MAC_USE_COCOA
+
+/*****************************************************************************
+ Externals
+ *****************************************************************************/
+extern QWidget *qt_mac_modal_blocked(QWidget *); //qapplication_mac.mm
+extern void qt_event_request_activate(QWidget *); //qapplication_mac.mm
+extern bool qt_event_remove_activate(); //qapplication_mac.mm
+extern void qt_mac_event_release(QWidget *w); //qapplication_mac.mm
+extern void qt_event_request_showsheet(QWidget *); //qapplication_mac.mm
+extern void qt_event_request_window_change(QWidget *); //qapplication_mac.mm
+extern QPointer<QWidget> qt_mouseover; //qapplication_mac.mm
+extern IconRef qt_mac_create_iconref(const QPixmap &); //qpixmap_mac.cpp
+extern void qt_mac_set_cursor(const QCursor *, const QPoint &); //qcursor_mac.mm
+extern void qt_mac_update_cursor(); //qcursor_mac.mm
+extern bool qt_nograb();
+extern CGImageRef qt_mac_create_cgimage(const QPixmap &, bool); //qpixmap_mac.cpp
+extern RgnHandle qt_mac_get_rgn(); //qregion_mac.cpp
+extern QRegion qt_mac_convert_mac_region(RgnHandle rgn); //qregion_mac.cpp
+
+/*****************************************************************************
+ QWidget utility functions
+ *****************************************************************************/
+void Q_GUI_EXPORT qt_mac_set_raise_process(bool b) { qt_mac_raise_process = b; }
+static QSize qt_mac_desktopSize()
+{
+ int w = 0, h = 0;
+ CGDisplayCount cg_count;
+ CGGetActiveDisplayList(0, 0, &cg_count);
+ QVector<CGDirectDisplayID> displays(cg_count);
+ CGGetActiveDisplayList(cg_count, displays.data(), &cg_count);
+ Q_ASSERT(cg_count == (CGDisplayCount)displays.size());
+ for(int i = 0; i < (int)cg_count; ++i) {
+ CGRect r = CGDisplayBounds(displays.at(i));
+ w = qMax<int>(w, qRound(r.origin.x + r.size.width));
+ h = qMax<int>(h, qRound(r.origin.y + r.size.height));
+ }
+ return QSize(w, h);
+}
+
+#ifdef QT_MAC_USE_COCOA
+static NSDrawer *qt_mac_drawer_for(const QWidget *widget)
+{
+ // This only goes one level below the content view so start with the window.
+ // This works fine for straight Qt stuff, but runs into problems if we are
+ // embedding, but if that's the case, they probably want to be using
+ // NSDrawer directly.
+ NSView *widgetView = reinterpret_cast<NSView *>(widget->window()->winId());
+ NSArray *windows = [NSApp windows];
+ for (NSWindow *window in windows) {
+ NSArray *drawers = [window drawers];
+ for (NSDrawer *drawer in drawers) {
+ NSArray *views = [[drawer contentView] subviews];
+ for (NSView *view in views) {
+ if (view == widgetView)
+ return drawer;
+ }
+ }
+ }
+ return 0;
+}
+#endif
+
+static void qt_mac_destructView(OSViewRef view)
+{
+#ifdef QT_MAC_USE_COCOA
+ [view removeFromSuperview];
+ [view release];
+#else
+ HIViewRemoveFromSuperview(view);
+ CFRelease(view);
+#endif
+}
+
+static void qt_mac_destructWindow(OSWindowRef window)
+{
+#ifdef QT_MAC_USE_COCOA
+ if ([window isVisible] && [window isSheet]){
+ [NSApp endSheet:window];
+ [window orderOut:window];
+ }
+
+ [[QT_MANGLE_NAMESPACE(QCocoaWindowDelegate) sharedDelegate] resignDelegateForWindow:window];
+ [window release];
+#else
+ // Remove property to clean up memory:
+ RemoveWindowProperty(window, kWidgetCreatorQt, kWidgetPropertyQWidget);
+ CFRelease(window);
+#endif
+}
+
+static void qt_mac_destructDrawer(NSDrawer *drawer)
+{
+#ifdef QT_MAC_USE_COCOA
+ [[QT_MANGLE_NAMESPACE(QCocoaWindowDelegate) sharedDelegate] resignDelegateForDrawer:drawer];
+ [drawer release];
+#else
+ Q_UNUSED(drawer);
+#endif
+}
+
+bool qt_mac_can_clickThrough(const QWidget *w)
+{
+ static int qt_mac_carbon_clickthrough = -1;
+ if (qt_mac_carbon_clickthrough < 0)
+ qt_mac_carbon_clickthrough = !qgetenv("QT_MAC_NO_COCOA_CLICKTHROUGH").isEmpty();
+ bool ret = !qt_mac_carbon_clickthrough;
+ for ( ; w; w = w->parentWidget()) {
+ if (w->testAttribute(Qt::WA_MacNoClickThrough)) {
+ ret = false;
+ break;
+ }
+ }
+ return ret;
+}
+
+bool qt_mac_is_macsheet(const QWidget *w)
+{
+ if (!w)
+ return false;
+
+ Qt::WindowModality modality = w->windowModality();
+ if (modality == Qt::ApplicationModal)
+ return false;
+ if (modality == Qt::WindowModal || w->windowType() == Qt::Sheet)
+ return true;
+ return false;
+}
+
+bool qt_mac_is_macdrawer(const QWidget *w)
+{
+ return (w && w->parentWidget() && w->windowType() == Qt::Drawer);
+}
+
+bool qt_mac_set_drawer_preferred_edge(QWidget *w, Qt::DockWidgetArea where) //users of Qt for Mac OS X can use this..
+{
+ if(!qt_mac_is_macdrawer(w))
+ return false;
+
+#if QT_MAC_USE_COCOA
+ NSDrawer *drawer = qt_mac_drawer_for(w);
+ if (!drawer)
+ return false;
+ NSRectEdge edge;
+ if (where & Qt::LeftDockWidgetArea)
+ edge = NSMinXEdge;
+ else if (where & Qt::RightDockWidgetArea)
+ edge = NSMaxXEdge;
+ else if (where & Qt::TopDockWidgetArea)
+ edge = NSMaxYEdge;
+ else if (where & Qt::BottomDockWidgetArea)
+ edge = NSMinYEdge;
+ else
+ return false;
+
+ if (edge == [drawer preferredEdge]) //no-op
+ return false;
+
+ if (w->isVisible()) {
+ [drawer close];
+ [drawer openOnEdge:edge];
+ }
+ [drawer setPreferredEdge:edge];
+#else
+ OSWindowRef window = qt_mac_window_for(w);
+ OptionBits edge;
+ if(where & Qt::LeftDockWidgetArea)
+ edge = kWindowEdgeLeft;
+ else if(where & Qt::RightDockWidgetArea)
+ edge = kWindowEdgeRight;
+ else if(where & Qt::TopDockWidgetArea)
+ edge = kWindowEdgeTop;
+ else if(where & Qt::BottomDockWidgetArea)
+ edge = kWindowEdgeBottom;
+ else
+ return false;
+
+ if(edge == GetDrawerPreferredEdge(window)) //no-op
+ return false;
+
+ //do it
+ SetDrawerPreferredEdge(window, edge);
+ if(w->isVisible()) {
+ CloseDrawer(window, false);
+ OpenDrawer(window, edge, true);
+ }
+#endif
+ return true;
+}
+
+#ifndef QT_MAC_USE_COCOA
+Q_GUI_EXPORT
+#endif
+QPoint qt_mac_posInWindow(const QWidget *w)
+{
+ QPoint ret = w->data->wrect.topLeft();
+ while(w && !w->isWindow()) {
+ ret += w->pos();
+ w = w->parentWidget();
+ }
+ return ret;
+}
+
+//find a QWidget from a OSWindowRef
+QWidget *qt_mac_find_window(OSWindowRef window)
+{
+#ifdef QT_MAC_USE_COCOA
+ return [window QT_MANGLE_NAMESPACE(qt_qwidget)];
+#else
+ if(!window)
+ return 0;
+
+ QWidget *ret;
+ if(GetWindowProperty(window, kWidgetCreatorQt, kWidgetPropertyQWidget, sizeof(ret), 0, &ret) == noErr)
+ return ret;
+ return 0;
+#endif
+}
+
+inline static void qt_mac_set_fullscreen_mode(bool b)
+{
+ extern bool qt_mac_app_fullscreen; //qapplication_mac.cpp
+ if(qt_mac_app_fullscreen == b)
+ return;
+ qt_mac_app_fullscreen = b;
+#if QT_MAC_USE_COCOA
+ if(b)
+ SetSystemUIMode(kUIModeAllHidden, kUIOptionAutoShowMenuBar);
+ else
+ SetSystemUIMode(kUIModeNormal, 0);
+#else
+ if(b)
+ HideMenuBar();
+ else
+ ShowMenuBar();
+#endif
+}
+
+Q_GUI_EXPORT OSViewRef qt_mac_nativeview_for(const QWidget *w)
+{
+ return reinterpret_cast<OSViewRef>(w->data->winid);
+}
+
+Q_GUI_EXPORT OSViewRef qt_mac_get_contentview_for(OSWindowRef w)
+{
+#ifdef QT_MAC_USE_COCOA
+ return [w contentView];
+#else
+ HIViewRef contentView = 0;
+ OSStatus err = GetRootControl(w, &contentView); // Returns the window's content view (Apple QA1214)
+ if (err == errUnknownControl) {
+ contentView = HIViewGetRoot(w);
+ } else if (err != noErr) {
+ qWarning("Qt:Could not get content or root view of window! %s:%d [%ld]",
+ __FILE__, __LINE__, err);
+ }
+ return contentView;
+#endif
+}
+
+bool qt_mac_sendMacEventToWidget(QWidget *widget, EventRef ref)
+{
+ return widget->macEvent(0, ref);
+}
+
+Q_GUI_EXPORT OSWindowRef qt_mac_window_for(OSViewRef view)
+{
+#ifdef QT_MAC_USE_COCOA
+ if (view)
+ return [view window];
+ return 0;
+#else
+ return HIViewGetWindow(view);
+#endif
+}
+
+static bool qt_isGenuineQWidget(OSViewRef ref)
+{
+#ifdef QT_MAC_USE_COCOA
+ return [ref isKindOfClass:[QT_MANGLE_NAMESPACE(QCocoaView) class]];
+#else
+ return HIObjectIsOfClass(HIObjectRef(ref), kObjectQWidget);
+#endif
+}
+
+bool qt_isGenuineQWidget(const QWidget *window)
+{
+ return window && qt_isGenuineQWidget(OSViewRef(window->winId()));
+}
+
+Q_GUI_EXPORT OSWindowRef qt_mac_window_for(const QWidget *w)
+{
+ OSViewRef hiview = qt_mac_nativeview_for(w);
+ if (hiview){
+ OSWindowRef window = qt_mac_window_for(hiview);
+ if (!window && qt_isGenuineQWidget(hiview)) {
+ w->window()->d_func()->createWindow_sys();
+ // Reget the hiview since the "create window could potentially move the view (I guess).
+ hiview = qt_mac_nativeview_for(w);
+ window = qt_mac_window_for(hiview);
+ }
+ return window;
+ }
+ return 0;
+}
+#ifndef QT_MAC_USE_COCOA
+/* Checks if the current group is a 'stay on top' group. If so, the
+ group gets removed from the hash table */
+static void qt_mac_release_stays_on_top_group(WindowGroupRef group)
+{
+ for (WindowGroupHash::iterator it = qt_mac_window_groups()->begin(); it != qt_mac_window_groups()->end(); ++it) {
+ if (it.value() == group) {
+ qt_mac_window_groups()->remove(it.key());
+ return;
+ }
+ }
+}
+
+/* Use this function instead of ReleaseWindowGroup, this will be sure to release the
+ stays on top window group (created with qt_mac_get_stays_on_top_group below) */
+static void qt_mac_release_window_group(WindowGroupRef group)
+{
+ ReleaseWindowGroup(group);
+ if (GetWindowGroupRetainCount(group) == 0)
+ qt_mac_release_stays_on_top_group(group);
+}
+#define ReleaseWindowGroup(x) Are you sure you wanted to do that? (you wanted qt_mac_release_window_group)
+
+SInt32 qt_mac_get_group_level(WindowClass wclass)
+{
+ SInt32 group_level;
+#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4
+ if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_4) {
+ CGWindowLevel tmpLevel;
+ GetWindowGroupLevelOfType(GetWindowGroupOfClass(wclass), kWindowGroupLevelActive, &tmpLevel);
+ group_level = tmpLevel;
+ } else
+#endif
+ {
+ GetWindowGroupLevel(GetWindowGroupOfClass(wclass), &group_level);
+ }
+ return group_level;
+}
+#endif
+
+#ifndef QT_MAC_USE_COCOA
+static void qt_mac_set_window_group(OSWindowRef window, Qt::WindowFlags flags, int level)
+{
+ WindowGroupRef group = 0;
+ if (qt_mac_window_groups()->contains(flags)) {
+ group = qt_mac_window_groups()->value(flags);
+ RetainWindowGroup(group);
+ } else {
+ CreateWindowGroup(kWindowActivationScopeNone, &group);
+ SetWindowGroupLevel(group, level);
+ SetWindowGroupParent(group, GetWindowGroupOfClass(kAllWindowClasses));
+ qt_mac_window_groups()->insert(flags, group);
+ }
+ SetWindowGroup(window, group);
+}
+
+inline static void qt_mac_set_window_group_to_stays_on_top(OSWindowRef window, Qt::WindowType type)
+{
+ // We create one static stays on top window group so that
+ // all stays on top (aka popups) will fall into the same
+ // group and be able to be raise()'d with releation to one another (from
+ // within the same window group).
+ qt_mac_set_window_group(window, type|Qt::WindowStaysOnTopHint, qt_mac_get_group_level(kOverlayWindowClass));
+}
+
+inline static void qt_mac_set_window_group_to_tooltip(OSWindowRef window)
+{
+ // Since new groups are created for 'stays on top' windows, the
+ // same must be done for tooltips. Otherwise, tooltips would be drawn
+ // below 'stays on top' widgets even tough they are on the same level.
+ // Also, add 'two' to the group level to make sure they also get on top of popups.
+ qt_mac_set_window_group(window, Qt::ToolTip, qt_mac_get_group_level(kHelpWindowClass)+2);
+}
+
+inline static void qt_mac_set_window_group_to_popup(OSWindowRef window)
+{
+ // In Qt, a popup is seen as a 'stay on top' window.
+ // Since new groups are created for 'stays on top' windows, the
+ // same must be done for popups. Otherwise, popups would be drawn
+ // below 'stays on top' windows. Add 1 to get above pure stay-on-top windows.
+ qt_mac_set_window_group(window, Qt::Popup, qt_mac_get_group_level(kOverlayWindowClass)+1);
+}
+#endif
+
+inline static bool updateRedirectedToGraphicsProxyWidget(QWidget *widget, const QRect &rect)
+{
+ if (!widget)
+ return false;
+
+#ifndef QT_NO_GRAPHICSVIEW
+ QWidget *tlw = widget->window();
+ QWExtra *extra = qt_widget_private(tlw)->extra;
+ if (extra && extra->proxyWidget) {
+ extra->proxyWidget->update(rect.translated(widget->mapTo(tlw, QPoint())));
+ return true;
+ }
+#endif
+
+ return false;
+}
+
+inline static bool updateRedirectedToGraphicsProxyWidget(QWidget *widget, const QRegion &rgn)
+{
+ if (!widget)
+ return false;
+
+#ifndef QT_NO_GRAPHICSVIEW
+ QWidget *tlw = widget->window();
+ QWExtra *extra = qt_widget_private(tlw)->extra;
+ if (extra && extra->proxyWidget) {
+ const QPoint offset(widget->mapTo(tlw, QPoint()));
+ const QVector<QRect> rects = rgn.rects();
+ for (int i = 0; i < rects.size(); ++i)
+ extra->proxyWidget->update(rects.at(i).translated(offset));
+ return true;
+ }
+#endif
+
+ return false;
+}
+
+void QWidgetPrivate::macUpdateIsOpaque()
+{
+ Q_Q(QWidget);
+ if (!q->testAttribute(Qt::WA_WState_Created))
+ return;
+#ifndef QT_MAC_USE_COCOA
+ HIViewFeatures bits;
+ HIViewRef hiview = qt_mac_nativeview_for(q);
+ HIViewGetFeatures(hiview, &bits);
+ if ((bits & kHIViewIsOpaque) == isOpaque)
+ return;
+ if (isOpaque) {
+ HIViewChangeFeatures(hiview, kHIViewIsOpaque, 0);
+ } else {
+ HIViewChangeFeatures(hiview, 0, kHIViewIsOpaque);
+ }
+ if (q->isVisible())
+ HIViewReshapeStructure(qt_mac_nativeview_for(q));
+#else
+ if (isRealWindow() && !q->testAttribute(Qt::WA_MacBrushedMetal)) {
+ bool opaque = isOpaque;
+ if (extra && extra->imageMask)
+ opaque = false; // we are never opaque when we have a mask.
+ [qt_mac_window_for(q) setOpaque:opaque];
+ }
+#endif
+}
+#ifdef QT_MAC_USE_COCOA
+static OSWindowRef qt_mac_create_window(QWidget *widget, WindowClass wclass,
+ NSUInteger wattr, const QRect &crect)
+{
+ // Determine if we need to add in our "custom window" attribute. Cocoa is rather clever
+ // in deciding if we need the maximize button or not (i.e., it's resizeable, so you
+ // must need a maximize button). So, the only buttons we have control over are the
+ // close and minimize buttons. If someone wants to customize and NOT have the maximize
+ // button, then we have to do our hack. We only do it for these cases because otherwise
+ // the window looks different when activated. This "QtMacCustomizeWindow" attribute is
+ // intruding on a public space and WILL BREAK in the future.
+ // One can hope that there is a more public API available by that time.
+ Qt::WindowFlags flags = widget ? widget->windowFlags() : Qt::WindowFlags(0);
+ if ((flags & Qt::CustomizeWindowHint)) {
+ if ((flags & (Qt::WindowCloseButtonHint | Qt::WindowSystemMenuHint
+ | Qt::WindowMinimizeButtonHint | Qt::WindowTitleHint))
+ && !(flags & Qt::WindowMaximizeButtonHint))
+ wattr |= QtMacCustomizeWindow;
+ }
+
+ // If we haven't created the desktop widget, you have to pass the rectangle
+ // in "cocoa coordinates" (i.e., top points to the lower left coordinate).
+ // Otherwise, we do the conversion for you. Since we are the only ones that
+ // create the desktop widget, this is OK (but confusing).
+ NSRect geo = NSMakeRect(crect.left(),
+ (qt_root_win != 0) ? flipYCoordinate(crect.bottom() + 1) : crect.top(),
+ crect.width(), crect.height());
+ QMacCocoaAutoReleasePool pool;
+ OSWindowRef window;
+ switch (wclass) {
+ case kMovableModalWindowClass:
+ case kModalWindowClass:
+ case kSheetWindowClass:
+ case kFloatingWindowClass:
+ case kOverlayWindowClass:
+ case kHelpWindowClass: {
+ NSPanel *panel;
+ BOOL needFloating = NO;
+ BOOL worksWhenModal = widget && (widget->windowType() == Qt::Popup);
+ // Add in the extra flags if necessary.
+ switch (wclass) {
+ case kSheetWindowClass:
+ wattr |= NSDocModalWindowMask;
+ break;
+ case kFloatingWindowClass:
+ case kHelpWindowClass:
+ needFloating = YES;
+ wattr |= NSUtilityWindowMask;
+ break;
+ default:
+ break;
+ }
+ panel = [[QT_MANGLE_NAMESPACE(QCocoaPanel) alloc] QT_MANGLE_NAMESPACE(qt_initWithQWidget):widget contentRect:geo styleMask:wattr];
+ [panel setFloatingPanel:needFloating];
+ [panel setWorksWhenModal:worksWhenModal];
+ window = panel;
+ break;
+ }
+ case kDrawerWindowClass: {
+ NSDrawer *drawer = [[NSDrawer alloc] initWithContentSize:geo.size preferredEdge:NSMinXEdge];
+ [[QT_MANGLE_NAMESPACE(QCocoaWindowDelegate) sharedDelegate] becomeDelegateForDrawer:drawer widget:widget];
+ QWidget *parentWidget = widget->parentWidget();
+ if (parentWidget)
+ [drawer setParentWindow:qt_mac_window_for(parentWidget)];
+ [drawer setLeadingOffset:0.0];
+ [drawer setTrailingOffset:25.0];
+ window = [[drawer contentView] window]; // Just to make sure we actually return a window
+ break;
+ }
+ default:
+ window = [[QT_MANGLE_NAMESPACE(QCocoaWindow) alloc] QT_MANGLE_NAMESPACE(qt_initWithQWidget):widget contentRect:geo styleMask:wattr];
+ break;
+ }
+ qt_syncCocoaTitleBarButtons(window, widget);
+ return window;
+}
+#else
+static OSWindowRef qt_mac_create_window(QWidget *, WindowClass wclass, WindowAttributes wattr,
+ const QRect &crect)
+{
+ OSWindowRef window;
+ Rect geo;
+ SetRect(&geo, crect.left(), crect.top(), crect.right() + 1, crect.bottom() + 1);
+ OSStatus err;
+ if(geo.right <= geo.left) geo.right = geo.left + 1;
+ if(geo.bottom <= geo.top) geo.bottom = geo.top + 1;
+ Rect null_rect;
+ SetRect(&null_rect, 0, 0, 1, 1);
+ err = CreateNewWindow(wclass, wattr, &null_rect, &window);
+ if(err == noErr) {
+ err = SetWindowBounds(window, kWindowContentRgn, &geo);
+ if(err != noErr)
+ qWarning("QWidget: Internal error (%s:%d)", __FILE__, __LINE__);
+ }
+ return window;
+}
+
+// window events
+static EventTypeSpec window_events[] = {
+ { kEventClassWindow, kEventWindowClose },
+ { kEventClassWindow, kEventWindowExpanded },
+ { kEventClassWindow, kEventWindowZoomed },
+ { kEventClassWindow, kEventWindowCollapsed },
+ { kEventClassWindow, kEventWindowToolbarSwitchMode },
+ { kEventClassWindow, kEventWindowProxyBeginDrag },
+ { kEventClassWindow, kEventWindowProxyEndDrag },
+ { kEventClassWindow, kEventWindowResizeCompleted },
+ { kEventClassWindow, kEventWindowBoundsChanging },
+ { kEventClassWindow, kEventWindowBoundsChanged },
+ { kEventClassWindow, kEventWindowGetRegion },
+#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4
+ { kEventClassWindow, kEventWindowGetClickModality },
+#endif
+ { kEventClassWindow, kEventWindowTransitionCompleted },
+ { kEventClassMouse, kEventMouseDown }
+};
+static EventHandlerUPP mac_win_eventUPP = 0;
+static void cleanup_win_eventUPP()
+{
+ DisposeEventHandlerUPP(mac_win_eventUPP);
+ mac_win_eventUPP = 0;
+}
+static const EventHandlerUPP make_win_eventUPP()
+{
+ if(mac_win_eventUPP)
+ return mac_win_eventUPP;
+ qAddPostRoutine(cleanup_win_eventUPP);
+ return mac_win_eventUPP = NewEventHandlerUPP(QWidgetPrivate::qt_window_event);
+}
+OSStatus QWidgetPrivate::qt_window_event(EventHandlerCallRef er, EventRef event, void *)
+{
+ QScopedLoopLevelCounter loopLevelCounter(qApp->d_func()->threadData);
+ bool handled_event = true;
+ UInt32 ekind = GetEventKind(event), eclass = GetEventClass(event);
+ switch(eclass) {
+ case kEventClassWindow: {
+ WindowRef wid = 0;
+ GetEventParameter(event, kEventParamDirectObject, typeWindowRef, 0,
+ sizeof(WindowRef), 0, &wid);
+ QWidget *widget = qt_mac_find_window(wid);
+ if(!widget) {
+ handled_event = false;
+#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4
+ } else if(ekind == kEventWindowGetClickModality) {
+ // Carbon will send us kEventWindowGetClickModality before every
+ // mouse press / release event. By returning 'true', we tell Carbon
+ // that we would like the event target to receive the mouse event even
+ // if the target is modally shaddowed. In Qt, this makes sense when we
+ // e.g. have a popup showing, as the popup will grab the event
+ // and perhaps use it to close itself.
+ // By also setting the current modal window back into the event, we
+ // help Carbon determining which window is supposed to be raised.
+ handled_event = qApp->activePopupWidget() ? true : false;
+ QWidget *top = 0;
+ if (!QApplicationPrivate::tryModalHelper(widget, &top) && top && top != widget){
+ if(!qt_mac_is_macsheet(top) || top->parentWidget() != widget) {
+ handled_event = true;
+ WindowPtr topWindowRef = qt_mac_window_for(top);
+ SetEventParameter(event, kEventParamModalWindow, typeWindowRef, sizeof(topWindowRef), &topWindowRef);
+ HIModalClickResult clickResult = kHIModalClickIsModal;
+ SetEventParameter(event, kEventParamModalClickResult, typeModalClickResult, sizeof(clickResult), &clickResult);
+ }
+ }
+#endif
+ } else if(ekind == kEventWindowClose) {
+ widget->d_func()->close_helper(QWidgetPrivate::CloseWithSpontaneousEvent);
+ QMenuBar::macUpdateMenuBar();
+ } else if (ekind == kEventWindowTransitionCompleted) {
+ WindowTransitionAction transitionAction;
+ GetEventParameter(event, kEventParamWindowTransitionAction, typeWindowTransitionAction,
+ 0, sizeof(transitionAction), 0, &transitionAction);
+ if (transitionAction == kWindowHideTransitionAction)
+ widget->hide();
+ } else if(ekind == kEventWindowExpanded) {
+ Qt::WindowStates currState = Qt::WindowStates(widget->data->window_state);
+ Qt::WindowStates newState = currState;
+ if (currState & Qt::WindowMinimized)
+ newState &= ~Qt::WindowMinimized;
+ if (!(currState & Qt::WindowActive))
+ newState |= Qt::WindowActive;
+ if (newState != currState) {
+ // newState will differ from currState if the window
+ // was expanded after clicking on the jewels (as opposed
+ // to calling QWidget::setWindowState)
+ widget->data->window_state = newState;
+ QWindowStateChangeEvent e(currState);
+ QApplication::sendSpontaneousEvent(widget, &e);
+ }
+
+ QShowEvent qse;
+ QApplication::sendSpontaneousEvent(widget, &qse);
+ } else if(ekind == kEventWindowZoomed) {
+ WindowPartCode windowPart;
+ GetEventParameter(event, kEventParamWindowPartCode,
+ typeWindowPartCode, 0, sizeof(windowPart), 0, &windowPart);
+ if(windowPart == inZoomIn && widget->isMaximized()) {
+
+ widget->data->window_state = widget->data->window_state & ~Qt::WindowMaximized;
+ QWindowStateChangeEvent e(Qt::WindowStates(widget->data->window_state | Qt::WindowMaximized));
+ QApplication::sendSpontaneousEvent(widget, &e);
+ } else if(windowPart == inZoomOut && !widget->isMaximized()) {
+ widget->data->window_state = widget->data->window_state | Qt::WindowMaximized;
+ QWindowStateChangeEvent e(Qt::WindowStates(widget->data->window_state
+ & ~Qt::WindowMaximized));
+ QApplication::sendSpontaneousEvent(widget, &e);
+ }
+ extern QPointer<QWidget> qt_button_down; //qapplication_mac.cpp
+ qt_button_down = 0;
+ } else if(ekind == kEventWindowCollapsed) {
+ if (!widget->isMinimized()) {
+ widget->data->window_state = widget->data->window_state | Qt::WindowMinimized;
+ QWindowStateChangeEvent e(Qt::WindowStates(widget->data->window_state & ~Qt::WindowMinimized));
+ QApplication::sendSpontaneousEvent(widget, &e);
+ }
+
+ // Deactivate this window:
+ if (widget->isActiveWindow() && !(widget->windowType() == Qt::Popup)) {
+ QWidget *w = 0;
+ if (widget->parentWidget())
+ w = widget->parentWidget()->window();
+ if (!w || (!w->isVisible() && !w->isMinimized())) {
+ for (WindowPtr wp = GetFrontWindowOfClass(kDocumentWindowClass, true);
+ wp; wp = GetNextWindowOfClass(wp, kDocumentWindowClass, true)) {
+ if ((w = qt_mac_find_window(wp)))
+ break;
+ }
+ }
+ if(!(w && w->isVisible() && !w->isMinimized()))
+ qApp->setActiveWindow(0);
+ }
+
+ //we send a hide to be like X11/Windows
+ QEvent e(QEvent::Hide);
+ QApplication::sendSpontaneousEvent(widget, &e);
+ extern QPointer<QWidget> qt_button_down; //qapplication_mac.cpp
+ qt_button_down = 0;
+ } else if(ekind == kEventWindowToolbarSwitchMode) {
+ QToolBarChangeEvent ev(!(GetCurrentKeyModifiers() & cmdKey));
+ QApplication::sendSpontaneousEvent(widget, &ev);
+ HIToolbarRef toolbar;
+ if (GetWindowToolbar(wid, &toolbar) == noErr) {
+ if (toolbar) {
+ // Let HIToolbar do its thang, but things like the OpenGL context
+ // needs to know about it.
+ CallNextEventHandler(er, event);
+ qt_event_request_window_change(widget);
+ widget->data->fstrut_dirty = true;
+ }
+ }
+ } else if(ekind == kEventWindowGetRegion) {
+ WindowRef window;
+ GetEventParameter(event, kEventParamDirectObject, typeWindowRef, 0,
+ sizeof(window), 0, &window);
+ WindowRegionCode wcode;
+ GetEventParameter(event, kEventParamWindowRegionCode, typeWindowRegionCode, 0,
+ sizeof(wcode), 0, &wcode);
+ if (wcode != kWindowOpaqueRgn){
+ // If the region is kWindowOpaqueRgn, don't call next
+ // event handler cause this will make the shadow of
+ // masked windows become offset. Unfortunately, we're not sure why.
+ CallNextEventHandler(er, event);
+ }
+ RgnHandle rgn;
+ GetEventParameter(event, kEventParamRgnHandle, typeQDRgnHandle, 0,
+ sizeof(rgn), 0, &rgn);
+
+ if(QWidgetPrivate::qt_widget_rgn(qt_mac_find_window(window), wcode, rgn, false))
+ SetEventParameter(event, kEventParamRgnHandle, typeQDRgnHandle, sizeof(rgn), &rgn);
+ } else if(ekind == kEventWindowProxyBeginDrag) {
+ QIconDragEvent e;
+ QApplication::sendSpontaneousEvent(widget, &e);
+ } else if(ekind == kEventWindowResizeCompleted) {
+ // Create a mouse up event, since such an event is not send by carbon to the
+ // application event handler (while a mouse down <b>is</b> on kEventWindowResizeStarted)
+ EventRef mouseUpEvent;
+ CreateEvent(0, kEventClassMouse, kEventMouseUp, 0, kEventAttributeUserEvent, &mouseUpEvent);
+ UInt16 mbutton = kEventMouseButtonPrimary;
+ SetEventParameter(mouseUpEvent, kEventParamMouseButton, typeMouseButton, sizeof(mbutton), &mbutton);
+ WindowRef window;
+ GetEventParameter(event, kEventParamDirectObject, typeWindowRef, 0, sizeof(window), 0, &window);
+ Rect dragRect;
+ GetWindowBounds(window, kWindowGrowRgn, &dragRect);
+ Point pos = {dragRect.bottom, dragRect.right};
+ SetEventParameter(mouseUpEvent, kEventParamMouseLocation, typeQDPoint, sizeof(pos), &pos);
+ SendEventToApplication(mouseUpEvent);
+ ReleaseEvent(mouseUpEvent);
+ } else if(ekind == kEventWindowBoundsChanging || ekind == kEventWindowBoundsChanged) {
+ // Panther doesn't send Changing for sheets, only changed, so only
+ // bother handling Changed event if we are on 10.3 and we are a
+ // sheet.
+ if (ekind == kEventWindowBoundsChanged
+ && (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_4
+ || !(widget->windowFlags() & Qt::Sheet))) {
+ handled_event = false;
+ } else {
+ UInt32 flags = 0;
+ GetEventParameter(event, kEventParamAttributes, typeUInt32, 0,
+ sizeof(flags), 0, &flags);
+ Rect nr;
+ GetEventParameter(event, kEventParamCurrentBounds, typeQDRectangle, 0,
+ sizeof(nr), 0, &nr);
+
+ QRect newRect(nr.left, nr.top, nr.right - nr.left, nr.bottom - nr.top);
+
+ QTLWExtra * const tlwExtra = widget->d_func()->maybeTopData();
+ if (tlwExtra && tlwExtra->isSetGeometry == 1) {
+ widget->d_func()->setGeometry_sys_helper(newRect.left(), newRect.top(), newRect.width(), newRect.height(), tlwExtra->isMove);
+ } else {
+ //implicitly removes the maximized bit
+ if((widget->data->window_state & Qt::WindowMaximized) &&
+ IsWindowInStandardState(wid, 0, 0)) {
+ widget->data->window_state &= ~Qt::WindowMaximized;
+ QWindowStateChangeEvent e(Qt::WindowStates(widget->data->window_state
+ | Qt::WindowMaximized));
+ QApplication::sendSpontaneousEvent(widget, &e);
+
+ }
+
+ handled_event = false;
+ const QRect oldRect = widget->data->crect;
+ if((flags & kWindowBoundsChangeOriginChanged)) {
+ if(nr.left != oldRect.x() || nr.top != oldRect.y()) {
+ widget->data->crect.moveTo(nr.left, nr.top);
+ QMoveEvent qme(widget->data->crect.topLeft(), oldRect.topLeft());
+ QApplication::sendSpontaneousEvent(widget, &qme);
+ }
+ }
+ if((flags & kWindowBoundsChangeSizeChanged)) {
+ if (widget->isWindow()) {
+ QSize newSize = QLayout::closestAcceptableSize(widget, newRect.size());
+ int dh = newSize.height() - newRect.height();
+ int dw = newSize.width() - newRect.width();
+ if (dw != 0 || dh != 0) {
+ handled_event = true; // We want to change the bounds, so we handle the event
+
+ // set the rect, so we can also do the resize down below (yes, we need to resize).
+ newRect.setBottom(newRect.bottom() + dh);
+ newRect.setRight(newRect.right() + dw);
+
+ nr.left = newRect.x();
+ nr.top = newRect.y();
+ nr.right = nr.left + newRect.width();
+ nr.bottom = nr.top + newRect.height();
+ SetEventParameter(event, kEventParamCurrentBounds, typeQDRectangle, sizeof(Rect), &nr);
+ }
+ }
+
+ if (oldRect.width() != newRect.width() || oldRect.height() != newRect.height()) {
+ widget->data->crect.setSize(newRect.size());
+ HIRect bounds = CGRectMake(0, 0, newRect.width(), newRect.height());
+
+ // If the WA_StaticContents attribute is set we can optimize the resize
+ // by only repainting the newly exposed area. We do this by disabling
+ // painting when setting the size of the view. The OS will invalidate
+ // the newly exposed area for us.
+ const bool staticContents = widget->testAttribute(Qt::WA_StaticContents);
+ const HIViewRef view = qt_mac_nativeview_for(widget);
+ if (staticContents)
+ HIViewSetDrawingEnabled(view, false);
+ HIViewSetFrame(view, &bounds);
+ if (staticContents)
+ HIViewSetDrawingEnabled(view, true);
+
+ QResizeEvent qre(newRect.size(), oldRect.size());
+ QApplication::sendSpontaneousEvent(widget, &qre);
+ qt_event_request_window_change(widget);
+ }
+ }
+ }
+ }
+ } else {
+ handled_event = false;
+ }
+ break; }
+ case kEventClassMouse: {
+#if 0
+ return SendEventToApplication(event);
+#endif
+
+ bool send_to_app = false;
+ {
+ WindowPartCode wpc;
+ if (GetEventParameter(event, kEventParamWindowPartCode, typeWindowPartCode, 0,
+ sizeof(wpc), 0, &wpc) == noErr && wpc != inContent)
+ send_to_app = true;
+ }
+ if(!send_to_app) {
+ WindowRef window;
+ if(GetEventParameter(event, kEventParamWindowRef, typeWindowRef, 0,
+ sizeof(window), 0, &window) == noErr) {
+ HIViewRef hiview;
+ if(HIViewGetViewForMouseEvent(HIViewGetRoot(window), event, &hiview) == noErr) {
+ if(QWidget *w = QWidget::find((WId)hiview)) {
+#if 0
+ send_to_app = !w->isActiveWindow();
+#else
+ Q_UNUSED(w);
+ send_to_app = true;
+#endif
+ }
+ }
+ }
+ }
+ if(send_to_app)
+ return SendEventToApplication(event);
+ handled_event = false;
+ break; }
+ default:
+ handled_event = false;
+ }
+ if(!handled_event) //let the event go through
+ return eventNotHandledErr;
+ return noErr; //we eat the event
+}
+
+// widget events
+static HIObjectClassRef widget_class = 0;
+static EventTypeSpec widget_events[] = {
+ { kEventClassHIObject, kEventHIObjectConstruct },
+ { kEventClassHIObject, kEventHIObjectDestruct },
+
+ { kEventClassControl, kEventControlDraw },
+ { kEventClassControl, kEventControlInitialize },
+ { kEventClassControl, kEventControlGetPartRegion },
+ { kEventClassControl, kEventControlGetClickActivation },
+ { kEventClassControl, kEventControlSetFocusPart },
+ { kEventClassControl, kEventControlDragEnter },
+ { kEventClassControl, kEventControlDragWithin },
+ { kEventClassControl, kEventControlDragLeave },
+ { kEventClassControl, kEventControlDragReceive },
+ { kEventClassControl, kEventControlOwningWindowChanged },
+ { kEventClassControl, kEventControlBoundsChanged },
+ { kEventClassControl, kEventControlGetSizeConstraints },
+ { kEventClassControl, kEventControlVisibilityChanged },
+
+ { kEventClassMouse, kEventMouseDown },
+ { kEventClassMouse, kEventMouseUp },
+ { kEventClassMouse, kEventMouseMoved },
+ { kEventClassMouse, kEventMouseDragged }
+};
+static EventHandlerUPP mac_widget_eventUPP = 0;
+static void cleanup_widget_eventUPP()
+{
+ DisposeEventHandlerUPP(mac_widget_eventUPP);
+ mac_widget_eventUPP = 0;
+}
+static const EventHandlerUPP make_widget_eventUPP()
+{
+ if(mac_widget_eventUPP)
+ return mac_widget_eventUPP;
+ qAddPostRoutine(cleanup_widget_eventUPP);
+ return mac_widget_eventUPP = NewEventHandlerUPP(QWidgetPrivate::qt_widget_event);
+}
+OSStatus QWidgetPrivate::qt_widget_event(EventHandlerCallRef er, EventRef event, void *)
+{
+ QScopedLoopLevelCounter loopLevelCounter(QApplicationPrivate::instance()->threadData);
+
+ bool handled_event = true;
+ UInt32 ekind = GetEventKind(event), eclass = GetEventClass(event);
+ switch(eclass) {
+ case kEventClassHIObject: {
+ HIViewRef view = 0;
+ GetEventParameter(event, kEventParamHIObjectInstance, typeHIObjectRef,
+ 0, sizeof(view), 0, &view);
+ if(ekind == kEventHIObjectConstruct) {
+ if(view) {
+ HIViewChangeFeatures(view, kHIViewAllowsSubviews, 0);
+ SetEventParameter(event, kEventParamHIObjectInstance,
+ typeVoidPtr, sizeof(view), &view);
+ }
+ } else if(ekind == kEventHIObjectDestruct) {
+ //nothing to really do.. or is there?
+ } else {
+ handled_event = false;
+ }
+ break; }
+ case kEventClassControl: {
+ QWidget *widget = 0;
+ HIViewRef hiview = 0;
+ if(GetEventParameter(event, kEventParamDirectObject, typeControlRef,
+ 0, sizeof(hiview), 0, &hiview) == noErr)
+ widget = QWidget::find((WId)hiview);
+ if (widget && widget->macEvent(er, event))
+ return noErr;
+ if(ekind == kEventControlDraw) {
+ if(widget && qt_isGenuineQWidget(hiview)) {
+
+ // if there is a window change event pending for any gl child wigets,
+ // send it immediately. (required for flicker-free resizing)
+ extern void qt_mac_send_posted_gl_updates(QWidget *widget);
+ qt_mac_send_posted_gl_updates(widget);
+
+ if (QApplicationPrivate::graphicsSystem() && !widget->d_func()->paintOnScreen()) {
+ widget->d_func()->syncBackingStore();
+ widget->d_func()->dirtyOnWidget = QRegion();
+ return noErr;
+ }
+
+ //requested rgn
+ RgnHandle rgn;
+ GetEventParameter(event, kEventParamRgnHandle, typeQDRgnHandle, 0, sizeof(rgn), 0, &rgn);
+ QRegion qrgn(qt_mac_convert_mac_region(rgn));
+
+ //update handles
+ GrafPtr qd = 0;
+ CGContextRef cg = 0;
+#ifndef QT_MAC_NO_QUICKDRAW
+ {
+ if(GetEventParameter(event, kEventParamGrafPort, typeGrafPtr, 0, sizeof(qd), 0, &qd) != noErr) {
+ GDHandle dev = 0;
+ GetGWorld(&qd, &dev); //just use the global port..
+ }
+ }
+ bool end_cg_context = false;
+ if(GetEventParameter(event, kEventParamCGContextRef, typeCGContextRef, 0, sizeof(cg), 0, &cg) != noErr && qd) {
+ end_cg_context = true;
+ QDBeginCGContext(qd, &cg);
+ }
+#else
+ if(GetEventParameter(event, kEventParamCGContextRef, typeCGContextRef, 0, sizeof(cg), 0, &cg) != noErr) {
+ Q_ASSERT(false);
+ }
+#endif
+ widget->d_func()->hd = cg;
+ widget->d_func()->qd_hd = qd;
+ CGContextSaveGState(cg);
+
+#ifdef DEBUG_WIDGET_PAINT
+ const bool doDebug = true;
+ if(doDebug) {
+ qDebug("asked to draw %p[%p] [%s::%s] %p[%p] [%d] [%dx%d]", widget, hiview, widget->metaObject()->className(),
+ widget->objectName().local8Bit().data(), widget->parentWidget(),
+ (HIViewRef)(widget->parentWidget() ? qt_mac_nativeview_for(widget->parentWidget()) : (HIViewRef)0),
+ HIViewIsCompositingEnabled(hiview), qt_mac_posInWindow(widget).x(), qt_mac_posInWindow(widget).y());
+#if 0
+ QVector<QRect> region_rects = qrgn.rects();
+ qDebug("Region! %d", region_rects.count());
+ for(int i = 0; i < region_rects.count(); i++)
+ qDebug("%d %d %d %d", region_rects[i].x(), region_rects[i].y(),
+ region_rects[i].width(), region_rects[i].height());
+ region_rects = widget->d_func()->clp.rects();
+ qDebug("Widget Region! %d", region_rects.count());
+ for(int i = 0; i < region_rects.count(); i++)
+ qDebug("%d %d %d %d", region_rects[i].x(), region_rects[i].y(),
+ region_rects[i].width(), region_rects[i].height());
+#endif
+ }
+#endif
+ if (widget->isVisible() && widget->updatesEnabled()) { //process the actual paint event.
+ if(widget->testAttribute(Qt::WA_WState_InPaintEvent))
+ qWarning("QWidget::repaint: Recursive repaint detected");
+
+ QPoint redirectionOffset(0, 0);
+ QWidget *tl = widget->window();
+ if (tl) {
+ Qt::WindowFlags flags = tl->windowFlags();
+ if (flags & Qt::FramelessWindowHint
+ || (flags & Qt::CustomizeWindowHint && !(flags & Qt::WindowTitleHint))) {
+ if(tl->d_func()->extra && !tl->d_func()->extra->mask.isEmpty())
+ redirectionOffset += tl->d_func()->extra->mask.boundingRect().topLeft();
+ }
+ }
+
+ //setup the context
+ widget->setAttribute(Qt::WA_WState_InPaintEvent);
+ QPaintEngine *engine = widget->paintEngine();
+ if (engine)
+ engine->setSystemClip(qrgn);
+
+ //handle the erase
+ if (engine && (!widget->testAttribute(Qt::WA_NoSystemBackground)
+ && (widget->isWindow() || widget->autoFillBackground())
+ || widget->testAttribute(Qt::WA_TintedBackground)
+ || widget->testAttribute(Qt::WA_StyledBackground))) {
+#ifdef DEBUG_WIDGET_PAINT
+ if(doDebug)
+ qDebug(" Handling erase for [%s::%s]", widget->metaObject()->className(),
+ widget->objectName().local8Bit().data());
+#endif
+ if (!redirectionOffset.isNull())
+ widget->d_func()->setRedirected(widget, redirectionOffset);
+
+ bool was_unclipped = widget->testAttribute(Qt::WA_PaintUnclipped);
+ widget->setAttribute(Qt::WA_PaintUnclipped, false);
+ QPainter p(widget);
+ p.setClipping(false);
+ if(was_unclipped)
+ widget->setAttribute(Qt::WA_PaintUnclipped);
+
+ QAbstractScrollArea *scrollArea = qobject_cast<QAbstractScrollArea *>(widget->parent());
+ QPoint scrollAreaOffset;
+ if (scrollArea && scrollArea->viewport() == widget) {
+ QAbstractScrollAreaPrivate *priv = static_cast<QAbstractScrollAreaPrivate *>(static_cast<QWidget *>(scrollArea)->d_ptr);
+ scrollAreaOffset = priv->contentsOffset();
+ p.translate(-scrollAreaOffset);
+ }
+
+ widget->d_func()->paintBackground(&p, qrgn, scrollAreaOffset, widget->isWindow() ? DrawAsRoot : 0);
+ if (widget->testAttribute(Qt::WA_TintedBackground)) {
+ QColor tint = widget->palette().window().color();
+ tint.setAlphaF(.6);
+ const QVector<QRect> &rects = qrgn.rects();
+ for (int i = 0; i < rects.size(); ++i)
+ p.fillRect(rects.at(i).translated(scrollAreaOffset), tint);
+ }
+ p.end();
+ if (!redirectionOffset.isNull())
+ widget->d_func()->restoreRedirected();
+ }
+
+ if (widget->isWindow() && !widget->d_func()->isOpaque
+ && !widget->testAttribute(Qt::WA_MacBrushedMetal)) {
+ QRect qrgnRect = qrgn.boundingRect();
+ CGContextClearRect(cg, CGRectMake(qrgnRect.x(), qrgnRect.y(), qrgnRect.width(), qrgnRect.height()));
+ }
+
+
+ if(!HIObjectIsOfClass((HIObjectRef)hiview, kObjectQWidget))
+ CallNextEventHandler(er, event);
+
+ //send the paint
+ redirectionOffset += widget->data->wrect.topLeft(); // Map from system to qt coordinates
+ if (!redirectionOffset.isNull())
+ widget->d_func()->setRedirected(widget, redirectionOffset);
+ qrgn.translate(redirectionOffset);
+ QPaintEvent e(qrgn);
+ widget->d_func()->dirtyOnWidget = QRegion();
+#ifdef QT3_SUPPORT
+ e.setErased(true);
+#endif
+ QApplication::sendSpontaneousEvent(widget, &e);
+ if (!redirectionOffset.isNull())
+ widget->d_func()->restoreRedirected();
+#ifdef QT_RASTER_PAINTENGINE
+ if(engine && engine->type() == QPaintEngine::Raster)
+ static_cast<QRasterPaintEngine*>(engine)->flush(widget,
+ qrgn.boundingRect().topLeft());
+#endif
+
+ //cleanup
+ if (engine)
+ engine->setSystemClip(QRegion());
+
+ widget->setAttribute(Qt::WA_WState_InPaintEvent, false);
+ if(!widget->testAttribute(Qt::WA_PaintOutsidePaintEvent) && widget->paintingActive())
+ qWarning("QWidget: It is dangerous to leave painters active on a widget outside of the PaintEvent");
+ }
+
+ widget->d_func()->hd = 0;
+ widget->d_func()->qd_hd = 0;
+ CGContextRestoreGState(cg);
+#ifndef QT_MAC_NO_QUICKDRAW
+ if(end_cg_context)
+ QDEndCGContext(qd, &cg);
+#endif
+ } else if(!HIObjectIsOfClass((HIObjectRef)hiview, kObjectQWidget)) {
+ CallNextEventHandler(er, event);
+ }
+ } else if(ekind == kEventControlInitialize) {
+ if(HIObjectIsOfClass((HIObjectRef)hiview, kObjectQWidget)) {
+ UInt32 features = kControlSupportsDragAndDrop | kControlSupportsClickActivation | kControlSupportsFocus;
+ SetEventParameter(event, kEventParamControlFeatures, typeUInt32, sizeof(features), &features);
+ } else {
+ handled_event = false;
+ }
+ } else if(ekind == kEventControlSetFocusPart) {
+ if(widget) {
+ ControlPartCode part;
+ GetEventParameter(event, kEventParamControlPart, typeControlPartCode, 0,
+ sizeof(part), 0, &part);
+ if(part == kControlFocusNoPart){
+ if (widget->hasFocus())
+ QApplicationPrivate::setFocusWidget(0, Qt::OtherFocusReason);
+ } else
+ widget->setFocus();
+ }
+ if(!HIObjectIsOfClass((HIObjectRef)hiview, kObjectQWidget))
+ CallNextEventHandler(er, event);
+ } else if(ekind == kEventControlGetClickActivation) {
+ ClickActivationResult clickT = kActivateAndIgnoreClick;
+ SetEventParameter(event, kEventParamClickActivation, typeClickActivationResult,
+ sizeof(clickT), &clickT);
+ } else if(ekind == kEventControlGetPartRegion) {
+ handled_event = false;
+ if(!HIObjectIsOfClass((HIObjectRef)hiview, kObjectQWidget) && CallNextEventHandler(er, event) == noErr) {
+ handled_event = true;
+ break;
+ }
+ if(widget && !widget->isWindow()) {
+ ControlPartCode part;
+ GetEventParameter(event, kEventParamControlPart, typeControlPartCode, 0,
+ sizeof(part), 0, &part);
+ if(part == kControlClickableMetaPart && widget->testAttribute(Qt::WA_TransparentForMouseEvents)) {
+ RgnHandle rgn;
+ GetEventParameter(event, kEventParamControlRegion, typeQDRgnHandle, 0,
+ sizeof(rgn), 0, &rgn);
+ SetEmptyRgn(rgn);
+ handled_event = true;
+ } else if(part == kControlStructureMetaPart || part == kControlClickableMetaPart) {
+ RgnHandle rgn;
+ GetEventParameter(event, kEventParamControlRegion, typeQDRgnHandle, 0,
+ sizeof(rgn), 0, &rgn);
+ SetRectRgn(rgn, 0, 0, widget->width(), widget->height());
+ if(QWidgetPrivate::qt_widget_rgn(widget, kWindowStructureRgn, rgn, false))
+ handled_event = true;
+ } else if(part == kControlOpaqueMetaPart) {
+ if(widget->d_func()->isOpaque) {
+ RgnHandle rgn;
+ GetEventParameter(event, kEventParamControlRegion, typeQDRgnHandle, 0,
+ sizeof(RgnHandle), 0, &rgn);
+ SetRectRgn(rgn, 0, 0, widget->width(), widget->height());
+ QWidgetPrivate::qt_widget_rgn(widget, kWindowStructureRgn, rgn, false);
+ SetEventParameter(event, kEventParamControlRegion, typeQDRgnHandle,
+ sizeof(RgnHandle), &rgn);
+ handled_event = true;
+ }
+ }
+ }
+ } else if(ekind == kEventControlOwningWindowChanged) {
+ if(!HIObjectIsOfClass((HIObjectRef)hiview, kObjectQWidget))
+ CallNextEventHandler(er, event);
+ if(widget && qt_mac_window_for(hiview)) {
+ WindowRef foo = 0;
+ GetEventParameter(event, kEventParamControlCurrentOwningWindow, typeWindowRef, 0,
+ sizeof(foo), 0, &foo);
+ widget->d_func()->initWindowPtr();
+ }
+ if (widget)
+ qt_event_request_window_change(widget);
+ } else if(ekind == kEventControlDragEnter || ekind == kEventControlDragWithin ||
+ ekind == kEventControlDragLeave || ekind == kEventControlDragReceive) {
+ // dnd are really handled in qdnd_mac.cpp,
+ // just modularize the code a little...
+ DragRef drag;
+ GetEventParameter(event, kEventParamDragRef, typeDragRef, 0, sizeof(drag), 0, &drag);
+ handled_event = false;
+ bool drag_allowed = false;
+
+ QWidget *dropWidget = widget;
+ if (qobject_cast<QFocusFrame *>(widget)){
+ // We might shadow widgets underneath the focus
+ // frame, so stay interrested, and let the dnd through
+ drag_allowed = true;
+ handled_event = true;
+ Point where;
+ GetDragMouse(drag, &where, 0);
+ dropWidget = QApplication::widgetAt(QPoint(where.h, where.v));
+
+ if (dropWidget != QDragManager::self()->currentTarget()) {
+ // We have to 'fake' enter and leave events for the shaddowed widgets:
+ if (ekind == kEventControlDragEnter) {
+ if (QDragManager::self()->currentTarget())
+ QDragManager::self()->currentTarget()->d_func()->qt_mac_dnd_event(kEventControlDragLeave, drag);
+ if (dropWidget) {
+ dropWidget->d_func()->qt_mac_dnd_event(kEventControlDragEnter, drag);
+ }
+ // Set dropWidget to zero, so qt_mac_dnd_event
+ // doesn't get called a second time below:
+ dropWidget = 0;
+ }
+ }
+ }
+
+ // Send the dnd event to the widget:
+ if (dropWidget && dropWidget->d_func()->qt_mac_dnd_event(ekind, drag)) {
+ drag_allowed = true;
+ handled_event = true;
+ }
+
+ if (ekind == kEventControlDragEnter) {
+ // If we don't accept the enter event, we will
+ // receive no more drag events for this widget
+ const Boolean wouldAccept = drag_allowed ? true : false;
+ SetEventParameter(event, kEventParamControlWouldAcceptDrop, typeBoolean,
+ sizeof(wouldAccept), &wouldAccept);
+ }
+ } else if (ekind == kEventControlBoundsChanged) {
+ if (!widget || widget->isWindow() || widget->testAttribute(Qt::WA_Moved) || widget->testAttribute(Qt::WA_Resized)) {
+ handled_event = false;
+ } else {
+ // Sync our view in case some other (non-Qt) view is controlling us.
+ handled_event = true;
+ Rect newBounds;
+ GetEventParameter(event, kEventParamCurrentBounds,
+ typeQDRectangle, 0, sizeof(Rect), 0, &newBounds);
+ QRect rect(newBounds.left, newBounds.top,
+ newBounds.right - newBounds.left, newBounds.bottom - newBounds.top);
+
+ bool moved = widget->testAttribute(Qt::WA_Moved);
+ bool resized = widget->testAttribute(Qt::WA_Resized);
+ widget->setGeometry(rect);
+ widget->setAttribute(Qt::WA_Moved, moved);
+ widget->setAttribute(Qt::WA_Resized, resized);
+ qt_event_request_window_change(widget);
+ }
+ } else if (ekind == kEventControlGetSizeConstraints) {
+ if (!widget || !qt_isGenuineQWidget(widget)) {
+ handled_event = false;
+ } else {
+ handled_event = true;
+ QWidgetItem item(widget);
+ QSize size = item.minimumSize();
+ HISize hisize = { size.width(), size.height() };
+ SetEventParameter(event, kEventParamMinimumSize, typeHISize, sizeof(HISize), &hisize);
+ size = item.maximumSize();
+ hisize.width = size.width() + 2; // ### shouldn't have to add 2 (but it works).
+ hisize.height = size.height();
+ SetEventParameter(event, kEventParamMaximumSize, typeHISize, sizeof(HISize), &hisize);
+ }
+ } else if (ekind == kEventControlVisibilityChanged) {
+ handled_event = false;
+ if (widget) {
+ qt_event_request_window_change(widget);
+ if (!HIViewIsVisible(HIViewRef(widget->winId()))) {
+ extern QPointer<QWidget> qt_button_down; //qapplication_mac.cpp
+ if (widget == qt_button_down)
+ qt_button_down = 0;
+ }
+ }
+ }
+ break; }
+ case kEventClassMouse: {
+ bool send_to_app = false;
+ extern QPointer<QWidget> qt_button_down; //qapplication_mac.cpp
+ if(qt_button_down)
+ send_to_app = true;
+ if(send_to_app) {
+ OSStatus err = SendEventToApplication(event);
+ if(err != noErr)
+ handled_event = false;
+ } else {
+ CallNextEventHandler(er, event);
+ }
+ break; }
+ default:
+ handled_event = false;
+ break;
+ }
+ if(!handled_event) //let the event go through
+ return eventNotHandledErr;
+ return noErr; //we eat the event
+}
+#endif
+
+OSViewRef qt_mac_create_widget(QWidget *widget, QWidgetPrivate *widgetPrivate, OSViewRef parent)
+{
+#ifdef QT_MAC_USE_COCOA
+ QMacCocoaAutoReleasePool pool;
+ QT_MANGLE_NAMESPACE(QCocoaView) *view = [[QT_MANGLE_NAMESPACE(QCocoaView) alloc] initWithQWidget:widget widgetPrivate:widgetPrivate];
+ if (view && parent)
+ [parent addSubview:view];
+ return view;
+#else
+ Q_UNUSED(widget);
+ Q_UNUSED(widgetPrivate);
+ if(!widget_class) {
+ OSStatus err = HIObjectRegisterSubclass(kObjectQWidget, kHIViewClassID, 0, make_widget_eventUPP(),
+ GetEventTypeCount(widget_events), widget_events,
+ 0, &widget_class);
+ if (err && err != hiObjectClassExistsErr)
+ qWarning("QWidget: Internal error (%d)", __LINE__);
+ }
+ HIViewRef ret = 0;
+ if(HIObjectCreate(kObjectQWidget, 0, (HIObjectRef*)&ret) != noErr)
+ qWarning("QWidget: Internal error (%d)", __LINE__);
+ if(ret && parent)
+ HIViewAddSubview(parent, ret);
+ return ret;
+#endif
+}
+
+void qt_mac_unregister_widget()
+{
+#ifndef QT_MAC_USE_COCOA
+ HIObjectUnregisterClass(widget_class);
+ widget_class = 0;
+#endif
+}
+
+void QWidgetPrivate::toggleDrawers(bool visible)
+{
+ for (int i = 0; i < children.size(); ++i) {
+ register QObject *object = children.at(i);
+ if (!object->isWidgetType())
+ continue;
+ QWidget *widget = static_cast<QWidget*>(object);
+ if(qt_mac_is_macdrawer(widget)) {
+ if(visible) {
+ if (!widget->testAttribute(Qt::WA_WState_ExplicitShowHide))
+ widget->show();
+ } else {
+ widget->hide();
+ widget->setAttribute(Qt::WA_WState_ExplicitShowHide, false);
+ }
+ }
+ }
+}
+
+/*****************************************************************************
+ QWidgetPrivate member functions
+ *****************************************************************************/
+bool QWidgetPrivate::qt_mac_update_sizer(QWidget *w, int up)
+{
+ if(!w || !w->isWindow())
+ return false;
+
+ QTLWExtra *topData = w->d_func()->topData();
+ QWExtra *extraData = w->d_func()->extraData();
+ topData->resizer += up;
+ OSWindowRef windowRef = qt_mac_window_for(OSViewRef(w->winId()));
+ {
+#ifndef QT_MAC_USE_COCOA
+ WindowClass wclass;
+ GetWindowClass(windowRef, &wclass);
+ if(!(GetAvailableWindowAttributes(wclass) & kWindowResizableAttribute))
+ return true;
+#endif
+ }
+ bool remove_grip = (topData->resizer || (w->windowFlags() & Qt::FramelessWindowHint)
+ || (extraData->maxw && extraData->maxh &&
+ extraData->maxw == extraData->minw && extraData->maxh == extraData->minh));
+
+#ifndef QT_MAC_USE_COCOA
+ WindowAttributes attr;
+ GetWindowAttributes(windowRef, &attr);
+ if(remove_grip) {
+ if(attr & kWindowResizableAttribute) {
+ ChangeWindowAttributes(qt_mac_window_for(w), kWindowNoAttributes,
+ kWindowResizableAttribute);
+ ReshapeCustomWindow(qt_mac_window_for(w));
+ }
+ } else if(!(attr & kWindowResizableAttribute)) {
+ ChangeWindowAttributes(windowRef, kWindowResizableAttribute,
+ kWindowNoAttributes);
+ ReshapeCustomWindow(windowRef);
+ }
+#else
+ [windowRef setShowsResizeIndicator:!remove_grip];
+#endif
+ return true;
+}
+
+void QWidgetPrivate::qt_clean_root_win()
+{
+#ifdef QT_MAC_USE_COCOA
+ [qt_root_win release];
+#else
+ if(!qt_root_win)
+ return;
+ CFRelease(qt_root_win);
+#endif
+ qt_root_win = 0;
+}
+
+bool QWidgetPrivate::qt_create_root_win()
+{
+ if(qt_root_win)
+ return false;
+ const QSize desktopSize = qt_mac_desktopSize();
+ QRect desktopRect(QPoint(0, 0), desktopSize);
+#ifdef QT_MAC_USE_COCOA
+ qt_root_win = qt_mac_create_window(0, kOverlayWindowClass, NSBorderlessWindowMask, desktopRect);
+#else
+ WindowAttributes wattr = (kWindowCompositingAttribute | kWindowStandardHandlerAttribute);
+ qt_root_win = qt_mac_create_window(0, kOverlayWindowClass, wattr, desktopRect);
+#endif
+ if(!qt_root_win)
+ return false;
+ qAddPostRoutine(qt_clean_root_win);
+ return true;
+}
+
+bool QWidgetPrivate::qt_recreate_root_win()
+{
+ if(!qt_root_win) //sanity check
+ return false;
+ //store old
+ OSWindowRef old_root_win = qt_root_win;
+ //recreate
+ qt_root_win = 0;
+ qt_create_root_win();
+ //cleanup old window
+#ifdef QT_MAC_USE_COCOA
+ [old_root_win release];
+#else
+ CFRelease(old_root_win);
+#endif
+ return true;
+}
+
+bool QWidgetPrivate::qt_widget_rgn(QWidget *widget, short wcode, RgnHandle rgn, bool force = false)
+{
+ bool ret = false;
+#ifndef QT_MAC_USE_COCOA
+ switch(wcode) {
+ case kWindowStructureRgn: {
+ if(widget) {
+ if(widget->d_func()->extra && !widget->d_func()->extra->mask.isEmpty()) {
+ QRegion rin = qt_mac_convert_mac_region(rgn);
+ if(!rin.isEmpty()) {
+ QPoint rin_tl = rin.boundingRect().topLeft(); //in offset
+ rin.translate(-rin_tl.x(), -rin_tl.y()); //bring into same space as below
+ QRegion mask = widget->d_func()->extra->mask;
+ Qt::WindowFlags flags = widget->windowFlags();
+ if(widget->isWindow()
+ && !(flags & Qt::FramelessWindowHint
+ || (flags & Qt::CustomizeWindowHint && !(flags & Qt::WindowTitleHint)))) {
+ QRegion title;
+ {
+ QMacSmartQuickDrawRegion rgn(qt_mac_get_rgn());
+ GetWindowRegion(qt_mac_window_for(widget), kWindowTitleBarRgn, rgn);
+ title = qt_mac_convert_mac_region(rgn);
+ }
+ QRect br = title.boundingRect();
+ mask.translate(0, br.height()); //put the mask 'under' the title bar..
+ title.translate(-br.x(), -br.y());
+ mask += title;
+ }
+
+ QRegion cr = rin & mask;
+ cr.translate(rin_tl.x(), rin_tl.y()); //translate back to incoming space
+ CopyRgn(QMacSmartQuickDrawRegion(cr.toQDRgn()), rgn);
+ }
+ ret = true;
+ } else if(force) {
+ QRegion cr(widget->geometry());
+ CopyRgn(QMacSmartQuickDrawRegion(cr.toQDRgn()), rgn);
+ ret = true;
+ }
+ }
+ break; }
+ default: break;
+ }
+ //qDebug() << widget << ret << wcode << qt_mac_convert_mac_region(rgn);
+#else
+ Q_UNUSED(widget);
+ Q_UNUSED(wcode);
+ Q_UNUSED(rgn);
+ Q_UNUSED(force);
+#endif
+ return ret;
+}
+
+/*****************************************************************************
+ QWidget member functions
+ *****************************************************************************/
+void QWidgetPrivate::determineWindowClass()
+{
+ Q_Q(QWidget);
+#ifndef QT_MAC_USE_COCOA
+// ### COCOA:Interleave these better!
+
+ const Qt::WindowType type = q->windowType();
+ Qt::WindowFlags &flags = data.window_flags;
+ const bool popup = (type == Qt::Popup);
+ if (type == Qt::ToolTip || type == Qt::SplashScreen || popup)
+ flags |= Qt::FramelessWindowHint;
+
+ WindowClass wclass = kSheetWindowClass;
+ if(qt_mac_is_macdrawer(q))
+ wclass = kDrawerWindowClass;
+ else if (q->testAttribute(Qt::WA_ShowModal) && flags & Qt::CustomizeWindowHint)
+ wclass = kDocumentWindowClass;
+ else if(popup || (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_5 && type == Qt::SplashScreen))
+ wclass = kModalWindowClass;
+ else if(q->testAttribute(Qt::WA_ShowModal))
+ wclass = kMovableModalWindowClass;
+ else if(type == Qt::ToolTip)
+ wclass = kHelpWindowClass;
+ else if(type == Qt::Tool || (QSysInfo::MacintoshVersion < QSysInfo::MV_10_5
+ && type == Qt::SplashScreen))
+ wclass = kFloatingWindowClass;
+ else
+ wclass = kDocumentWindowClass;
+
+ WindowGroupRef grp = 0;
+ WindowAttributes wattr = (kWindowCompositingAttribute | kWindowStandardHandlerAttribute);
+ if (q->testAttribute(Qt::WA_MacFrameworkScaled))
+ wattr |= kWindowFrameworkScaledAttribute;
+ if(qt_mac_is_macsheet(q)) {
+ //grp = GetWindowGroupOfClass(kMovableModalWindowClass);
+ wclass = kSheetWindowClass;
+ } else {
+ grp = GetWindowGroupOfClass(wclass);
+ // Shift things around a bit to get the correct window class based on the presence
+ // (or lack) of the border.
+ bool customize = flags & Qt::CustomizeWindowHint;
+ bool framelessWindow = (flags & Qt::FramelessWindowHint || (customize && !(flags & Qt::WindowTitleHint)));
+ if (framelessWindow) {
+ if(wclass == kDocumentWindowClass) {
+ if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_4)
+ wattr |= kWindowNoTitleBarAttribute;
+ else
+ wclass = kPlainWindowClass;
+ } else if(wclass == kFloatingWindowClass) {
+ if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_4)
+ wattr |= kWindowNoTitleBarAttribute;
+ else
+ wclass = kToolbarWindowClass;
+ } else if (wclass == kMovableModalWindowClass) {
+ wclass = kModalWindowClass;
+ }
+ } else {
+ if(wclass != kModalWindowClass)
+ wattr |= kWindowResizableAttribute;
+ }
+ // Only add extra decorations (well, buttons) for widgets that can have them
+ // and have an actual border we can put them on.
+ if(wclass != kModalWindowClass && wclass != kMovableModalWindowClass
+ && wclass != kSheetWindowClass && wclass != kPlainWindowClass
+ && !framelessWindow && wclass != kDrawerWindowClass
+ && wclass != kHelpWindowClass) {
+ if (flags & Qt::WindowMaximizeButtonHint)
+ wattr |= kWindowFullZoomAttribute;
+ if (flags & Qt::WindowMinimizeButtonHint)
+ wattr |= kWindowCollapseBoxAttribute;
+ if (flags & Qt::WindowSystemMenuHint || flags & Qt::WindowCloseButtonHint)
+ wattr |= kWindowCloseBoxAttribute;
+ if (flags & Qt::MacWindowToolBarButtonHint)
+ wattr |= kWindowToolbarButtonAttribute;
+ } else {
+ // Clear these hints so that we aren't call them on invalid windows
+ flags &= ~(Qt::WindowMaximizeButtonHint | Qt::WindowMinimizeButtonHint
+ | Qt::WindowCloseButtonHint | Qt::WindowSystemMenuHint);
+ }
+ }
+ if((popup || type == Qt::Tool) && !q->isModal())
+ wattr |= kWindowHideOnSuspendAttribute;
+ wattr |= kWindowLiveResizeAttribute;
+
+#ifdef DEBUG_WINDOW_CREATE
+#define ADD_DEBUG_WINDOW_NAME(x) { x, #x }
+ struct {
+ UInt32 tag;
+ const char *name;
+ } known_attribs[] = {
+ ADD_DEBUG_WINDOW_NAME(kWindowCompositingAttribute),
+ ADD_DEBUG_WINDOW_NAME(kWindowStandardHandlerAttribute),
+ ADD_DEBUG_WINDOW_NAME(kWindowMetalAttribute),
+ ADD_DEBUG_WINDOW_NAME(kWindowHideOnSuspendAttribute),
+ ADD_DEBUG_WINDOW_NAME(kWindowStandardHandlerAttribute),
+ ADD_DEBUG_WINDOW_NAME(kWindowCollapseBoxAttribute),
+ ADD_DEBUG_WINDOW_NAME(kWindowHorizontalZoomAttribute),
+ ADD_DEBUG_WINDOW_NAME(kWindowVerticalZoomAttribute),
+ ADD_DEBUG_WINDOW_NAME(kWindowResizableAttribute),
+ ADD_DEBUG_WINDOW_NAME(kWindowNoActivatesAttribute),
+ ADD_DEBUG_WINDOW_NAME(kWindowNoUpdatesAttribute),
+ ADD_DEBUG_WINDOW_NAME(kWindowOpaqueForEventsAttribute),
+ ADD_DEBUG_WINDOW_NAME(kWindowLiveResizeAttribute),
+ ADD_DEBUG_WINDOW_NAME(kWindowCloseBoxAttribute),
+ ADD_DEBUG_WINDOW_NAME(kWindowHideOnSuspendAttribute),
+ { 0, 0 }
+ }, known_classes[] = {
+ ADD_DEBUG_WINDOW_NAME(kHelpWindowClass),
+ ADD_DEBUG_WINDOW_NAME(kPlainWindowClass),
+ ADD_DEBUG_WINDOW_NAME(kDrawerWindowClass),
+ ADD_DEBUG_WINDOW_NAME(kUtilityWindowClass),
+ ADD_DEBUG_WINDOW_NAME(kToolbarWindowClass),
+ ADD_DEBUG_WINDOW_NAME(kSheetWindowClass),
+ ADD_DEBUG_WINDOW_NAME(kFloatingWindowClass),
+ ADD_DEBUG_WINDOW_NAME(kUtilityWindowClass),
+ ADD_DEBUG_WINDOW_NAME(kDocumentWindowClass),
+ ADD_DEBUG_WINDOW_NAME(kToolbarWindowClass),
+ ADD_DEBUG_WINDOW_NAME(kMovableModalWindowClass),
+ ADD_DEBUG_WINDOW_NAME(kModalWindowClass),
+ { 0, 0 }
+ };
+ qDebug("Qt: internal: ************* Creating new window %p (%s::%s)", q, q->metaObject()->className(),
+ q->objectName().toLocal8Bit().constData());
+ bool found_class = false;
+ for(int i = 0; known_classes[i].name; i++) {
+ if(wclass == known_classes[i].tag) {
+ found_class = true;
+ qDebug("Qt: internal: ** Class: %s", known_classes[i].name);
+ break;
+ }
+ }
+ if(!found_class)
+ qDebug("Qt: internal: !! Class: Unknown! (%d)", (int)wclass);
+ if(wattr) {
+ WindowAttributes tmp_wattr = wattr;
+ qDebug("Qt: internal: ** Attributes:");
+ for(int i = 0; tmp_wattr && known_attribs[i].name; i++) {
+ if((tmp_wattr & known_attribs[i].tag) == known_attribs[i].tag) {
+ tmp_wattr ^= known_attribs[i].tag;
+ qDebug("Qt: internal: * %s %s", known_attribs[i].name,
+ (GetAvailableWindowAttributes(wclass) & known_attribs[i].tag) ? "" : "(*)");
+ }
+ }
+ if(tmp_wattr)
+ qDebug("Qt: internal: !! Attributes: Unknown (%d)", (int)tmp_wattr);
+ }
+#endif
+
+ /* Just to be extra careful we will change to the kUtilityWindowClass if the
+ requested attributes cannot be used */
+ if((GetAvailableWindowAttributes(wclass) & wattr) != wattr) {
+ WindowClass tmp_class = wclass;
+ if(wclass == kToolbarWindowClass || wclass == kUtilityWindowClass)
+ wclass = kFloatingWindowClass;
+ if(tmp_class != wclass) {
+ if(!grp)
+ grp = GetWindowGroupOfClass(wclass);
+ wclass = tmp_class;
+ }
+ }
+ topData()->wclass = wclass;
+ topData()->wattr = wattr;
+#else
+ const Qt::WindowType type = q->windowType();
+ Qt::WindowFlags &flags = data.window_flags;
+ const bool popup = (type == Qt::Popup);
+ if (type == Qt::ToolTip || type == Qt::SplashScreen || popup)
+ flags |= Qt::FramelessWindowHint;
+
+ WindowClass wclass = kSheetWindowClass;
+ if(qt_mac_is_macdrawer(q))
+ wclass = kDrawerWindowClass;
+ else if (q->testAttribute(Qt::WA_ShowModal) && flags & Qt::CustomizeWindowHint)
+ wclass = kDocumentWindowClass;
+ else if(popup || (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_5 && type == Qt::SplashScreen))
+ wclass = kModalWindowClass;
+ else if(q->testAttribute(Qt::WA_ShowModal) || type == Qt::Dialog)
+ wclass = kMovableModalWindowClass;
+ else if(type == Qt::ToolTip)
+ wclass = kHelpWindowClass;
+ else if(type == Qt::Tool || (QSysInfo::MacintoshVersion < QSysInfo::MV_10_5
+ && type == Qt::SplashScreen))
+ wclass = kFloatingWindowClass;
+ else
+ wclass = kDocumentWindowClass;
+
+ WindowAttributes wattr = NSBorderlessWindowMask;
+ if(qt_mac_is_macsheet(q)) {
+ //grp = GetWindowGroupOfClass(kMovableModalWindowClass);
+ wclass = kSheetWindowClass;
+ wattr = NSTitledWindowMask | NSResizableWindowMask;
+ } else {
+#ifndef QT_MAC_USE_COCOA
+ grp = GetWindowGroupOfClass(wclass);
+#endif
+ // Shift things around a bit to get the correct window class based on the presence
+ // (or lack) of the border.
+ bool customize = flags & Qt::CustomizeWindowHint;
+ bool framelessWindow = (flags & Qt::FramelessWindowHint || (customize && !(flags & Qt::WindowTitleHint)));
+ if (framelessWindow) {
+ if (wclass == kDocumentWindowClass) {
+ wclass = kSimpleWindowClass;
+ } else if (wclass == kFloatingWindowClass) {
+ wclass = kToolbarWindowClass;
+ } else if (wclass == kMovableModalWindowClass) {
+ wclass = kModalWindowClass;
+ }
+ } else {
+ wattr |= NSTitledWindowMask;
+ if (wclass != kModalWindowClass)
+ wattr |= NSResizableWindowMask;
+ }
+ // Only add extra decorations (well, buttons) for widgets that can have them
+ // and have an actual border we can put them on.
+ if (wclass != kModalWindowClass
+ && wclass != kSheetWindowClass && wclass != kPlainWindowClass
+ && !framelessWindow && wclass != kDrawerWindowClass
+ && wclass != kHelpWindowClass) {
+ if (flags & Qt::WindowMinimizeButtonHint)
+ wattr |= NSMiniaturizableWindowMask;
+ if (flags & Qt::WindowSystemMenuHint || flags & Qt::WindowCloseButtonHint)
+ wattr |= NSClosableWindowMask;
+ } else {
+ // Clear these hints so that we aren't call them on invalid windows
+ flags &= ~(Qt::WindowMaximizeButtonHint | Qt::WindowMinimizeButtonHint
+ | Qt::WindowCloseButtonHint | Qt::WindowSystemMenuHint);
+ }
+ }
+ if (q->testAttribute(Qt::WA_MacBrushedMetal))
+ wattr |= NSTexturedBackgroundWindowMask;
+
+#ifdef DEBUG_WINDOW_CREATE
+#define ADD_DEBUG_WINDOW_NAME(x) { x, #x }
+ struct {
+ UInt32 tag;
+ const char *name;
+ } known_attribs[] = {
+ ADD_DEBUG_WINDOW_NAME(kWindowCompositingAttribute),
+ ADD_DEBUG_WINDOW_NAME(kWindowStandardHandlerAttribute),
+ ADD_DEBUG_WINDOW_NAME(kWindowMetalAttribute),
+ ADD_DEBUG_WINDOW_NAME(kWindowHideOnSuspendAttribute),
+ ADD_DEBUG_WINDOW_NAME(kWindowStandardHandlerAttribute),
+ ADD_DEBUG_WINDOW_NAME(kWindowCollapseBoxAttribute),
+ ADD_DEBUG_WINDOW_NAME(kWindowHorizontalZoomAttribute),
+ ADD_DEBUG_WINDOW_NAME(kWindowVerticalZoomAttribute),
+ ADD_DEBUG_WINDOW_NAME(kWindowResizableAttribute),
+ ADD_DEBUG_WINDOW_NAME(kWindowNoActivatesAttribute),
+ ADD_DEBUG_WINDOW_NAME(kWindowNoUpdatesAttribute),
+ ADD_DEBUG_WINDOW_NAME(kWindowOpaqueForEventsAttribute),
+ ADD_DEBUG_WINDOW_NAME(kWindowLiveResizeAttribute),
+ ADD_DEBUG_WINDOW_NAME(kWindowCloseBoxAttribute),
+ ADD_DEBUG_WINDOW_NAME(kWindowHideOnSuspendAttribute),
+ { 0, 0 }
+ }, known_classes[] = {
+ ADD_DEBUG_WINDOW_NAME(kHelpWindowClass),
+ ADD_DEBUG_WINDOW_NAME(kPlainWindowClass),
+ ADD_DEBUG_WINDOW_NAME(kDrawerWindowClass),
+ ADD_DEBUG_WINDOW_NAME(kUtilityWindowClass),
+ ADD_DEBUG_WINDOW_NAME(kToolbarWindowClass),
+ ADD_DEBUG_WINDOW_NAME(kSheetWindowClass),
+ ADD_DEBUG_WINDOW_NAME(kFloatingWindowClass),
+ ADD_DEBUG_WINDOW_NAME(kUtilityWindowClass),
+ ADD_DEBUG_WINDOW_NAME(kDocumentWindowClass),
+ ADD_DEBUG_WINDOW_NAME(kToolbarWindowClass),
+ ADD_DEBUG_WINDOW_NAME(kMovableModalWindowClass),
+ ADD_DEBUG_WINDOW_NAME(kModalWindowClass),
+ { 0, 0 }
+ };
+ qDebug("Qt: internal: ************* Creating new window %p (%s::%s)", q, q->metaObject()->className(),
+ q->objectName().toLocal8Bit().constData());
+ bool found_class = false;
+ for(int i = 0; known_classes[i].name; i++) {
+ if(wclass == known_classes[i].tag) {
+ found_class = true;
+ qDebug("Qt: internal: ** Class: %s", known_classes[i].name);
+ break;
+ }
+ }
+ if(!found_class)
+ qDebug("Qt: internal: !! Class: Unknown! (%d)", (int)wclass);
+ if(wattr) {
+ WindowAttributes tmp_wattr = wattr;
+ qDebug("Qt: internal: ** Attributes:");
+ for(int i = 0; tmp_wattr && known_attribs[i].name; i++) {
+ if((tmp_wattr & known_attribs[i].tag) == known_attribs[i].tag) {
+ tmp_wattr ^= known_attribs[i].tag;
+ qDebug("Qt: internal: * %s %s", known_attribs[i].name,
+ (GetAvailableWindowAttributes(wclass) & known_attribs[i].tag) ? "" : "(*)");
+ }
+ }
+ if(tmp_wattr)
+ qDebug("Qt: internal: !! Attributes: Unknown (%d)", (int)tmp_wattr);
+ }
+#endif
+
+#ifndef QT_MAC_USE_COCOA
+ /* Just to be extra careful we will change to the kUtilityWindowClass if the
+ requested attributes cannot be used */
+ if((GetAvailableWindowAttributes(wclass) & wattr) != wattr) {
+ WindowClass tmp_class = wclass;
+ if(wclass == kToolbarWindowClass || wclass == kUtilityWindowClass)
+ wclass = kFloatingWindowClass;
+ if(tmp_class != wclass) {
+ if(!grp)
+ grp = GetWindowGroupOfClass(wclass);
+ wclass = tmp_class;
+ }
+ }
+#endif
+#endif
+ topData()->wclass = wclass;
+ topData()->wattr = wattr;
+}
+
+#ifndef QT_MAC_USE_COCOA // This is handled in Cocoa via our category.
+void QWidgetPrivate::initWindowPtr()
+{
+ Q_Q(QWidget);
+ OSWindowRef windowRef = qt_mac_window_for(qt_mac_nativeview_for(q)); //do not create!
+ if(!windowRef)
+ return;
+ QWidget *window = q->window(), *oldWindow = 0;
+ if(GetWindowProperty(windowRef, kWidgetCreatorQt, kWidgetPropertyQWidget, sizeof(oldWindow), 0, &oldWindow) == noErr) {
+ Q_ASSERT(window == oldWindow);
+ return;
+ }
+
+ if(SetWindowProperty(windowRef, kWidgetCreatorQt, kWidgetPropertyQWidget, sizeof(window), &window) != noErr)
+ qWarning("Qt:Internal error (%s:%d)", __FILE__, __LINE__); //no real way to recover
+ if(!q->windowType() != Qt::Desktop) { //setup an event callback handler on the window
+ InstallWindowEventHandler(windowRef, make_win_eventUPP(), GetEventTypeCount(window_events),
+ window_events, static_cast<void *>(qApp), &window_event);
+ }
+}
+
+void QWidgetPrivate::finishCreateWindow_sys_Carbon(OSWindowRef windowRef)
+{
+ Q_Q(QWidget);
+ const Qt::WindowType type = q->windowType();
+ Qt::WindowFlags &flags = data.window_flags;
+ QWidget *parentWidget = q->parentWidget();
+
+ const bool desktop = (type == Qt::Desktop);
+ const bool dialog = (type == Qt::Dialog
+ || type == Qt::Sheet
+ || type == Qt::Drawer
+ || (flags & Qt::MSWindowsFixedSizeDialogHint));
+ QTLWExtra *topExtra = topData();
+ quint32 wattr = topExtra->wattr;
+ if (!desktop)
+ SetAutomaticControlDragTrackingEnabledForWindow(windowRef, true);
+ HIWindowChangeFeatures(windowRef, kWindowCanCollapse, 0);
+#if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4)
+ if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_4) {
+ if (wattr & kWindowHideOnSuspendAttribute)
+ HIWindowChangeAvailability(windowRef, kHIWindowExposeHidden, 0);
+ else
+ HIWindowChangeAvailability(windowRef, 0, kHIWindowExposeHidden);
+ }
+#endif
+ if ((flags & Qt::WindowStaysOnTopHint))
+ ChangeWindowAttributes(windowRef, kWindowNoAttributes, kWindowHideOnSuspendAttribute);
+ if (qt_mac_is_macdrawer(q) && parentWidget)
+ SetDrawerParent(windowRef, qt_mac_window_for (parentWidget));
+ if (topExtra->group) {
+ qt_mac_release_window_group(topExtra->group);
+ topExtra->group = 0;
+ }
+ if (type == Qt::ToolTip)
+ qt_mac_set_window_group_to_tooltip(windowRef);
+ else if (type == Qt::Popup && (flags & Qt::WindowStaysOnTopHint))
+ qt_mac_set_window_group_to_popup(windowRef);
+ else if (flags & Qt::WindowStaysOnTopHint)
+ qt_mac_set_window_group_to_stays_on_top(windowRef, type);
+ else if (dialog)
+ SetWindowGroup(windowRef, GetWindowGroupOfClass(kMovableModalWindowClass));
+
+#ifdef DEBUG_WINDOW_CREATE
+ if (WindowGroupRef grpf = GetWindowGroup(windowRef)) {
+ QCFString cfname;
+ CopyWindowGroupName(grpf, &cfname);
+ SInt32 lvl;
+ GetWindowGroupLevel(grpf, &lvl);
+ const char *from = "Default";
+ if (topExtra && grpf == topData()->group)
+ from = "Created";
+ else if (grpf == grp)
+ from = "Copied";
+ qDebug("Qt: internal: With window group '%s' [%p] @ %d: %s",
+ static_cast<QString>(cfname).toLatin1().constData(), grpf, (int)lvl, from);
+ } else {
+ qDebug("Qt: internal: No window group!!!");
+ }
+ HIWindowAvailability hi_avail = 0;
+ if (HIWindowGetAvailability(windowRef, &hi_avail) == noErr) {
+ struct {
+ UInt32 tag;
+ const char *name;
+ } known_avail[] = {
+ ADD_DEBUG_WINDOW_NAME(kHIWindowExposeHidden),
+ { 0, 0 }
+ };
+ qDebug("Qt: internal: ** HIWindowAvailibility:");
+ for (int i = 0; hi_avail && known_avail[i].name; i++) {
+ if ((hi_avail & known_avail[i].tag) == known_avail[i].tag) {
+ hi_avail ^= known_avail[i].tag;
+ qDebug("Qt: internal: * %s", known_avail[i].name);
+ }
+ }
+ if (hi_avail)
+ qDebug("Qt: internal: !! Attributes: Unknown (%d)", (int)hi_avail);
+ }
+#undef ADD_DEBUG_WINDOW_NAME
+#endif
+ if (extra && !extra->mask.isEmpty())
+ ReshapeCustomWindow(windowRef);
+ SetWindowModality(windowRef, kWindowModalityNone, 0);
+ if (qt_mac_is_macdrawer(q))
+ SetDrawerOffsets(windowRef, 0.0, 25.0);
+ data.fstrut_dirty = true; // when we create a toplevel widget, the frame strut should be dirty
+ HIViewRef hiview = (HIViewRef)data.winid;
+ HIViewRef window_hiview = qt_mac_get_contentview_for(windowRef);
+ if(!hiview) {
+ hiview = qt_mac_create_widget(q, this, window_hiview);
+ setWinId((WId)hiview);
+ } else {
+ HIViewAddSubview(window_hiview, hiview);
+ }
+ if (hiview) {
+ Rect win_rect;
+ GetWindowBounds(qt_mac_window_for (window_hiview), kWindowContentRgn, &win_rect);
+ HIRect bounds = CGRectMake(0, 0, win_rect.right-win_rect.left, win_rect.bottom-win_rect.top);
+ HIViewSetFrame(hiview, &bounds);
+ HIViewSetVisible(hiview, true);
+ if (q->testAttribute(Qt::WA_DropSiteRegistered))
+ registerDropSite(true);
+ transferChildren();
+ }
+ initWindowPtr();
+
+ if (topExtra->posFromMove) {
+ updateFrameStrut();
+ const QRect &fStrut = frameStrut();
+ Rect r;
+ SetRect(&r, data.crect.left(), data.crect.top(), data.crect.right() + 1, data.crect.bottom() + 1);
+ SetRect(&r, r.left + fStrut.left(), r.top + fStrut.top(),
+ (r.left + fStrut.left() + data.crect.width()) - fStrut.right(),
+ (r.top + fStrut.top() + data.crect.height()) - fStrut.bottom());
+ SetWindowBounds(windowRef, kWindowContentRgn, &r);
+ topExtra->posFromMove = false;
+ }
+
+ if (q->testAttribute(Qt::WA_WState_WindowOpacitySet)){
+ q->setWindowOpacity(topExtra->opacity / 255.0f);
+ } else if (qt_mac_is_macsheet(q)){
+ SetThemeWindowBackground(qt_mac_window_for(q), kThemeBrushSheetBackgroundTransparent, true);
+ CGFloat alpha = 0;
+ GetWindowAlpha(qt_mac_window_for(q), &alpha);
+ if (alpha == 1){
+ // For some reason the 'SetThemeWindowBackground' does not seem
+ // to work. So we do this little hack until it hopefully starts to
+ // work in newer versions of mac OS.
+ q->setWindowOpacity(0.95f);
+ q->setAttribute(Qt::WA_WState_WindowOpacitySet, false);
+ }
+ } else{
+ // If the window has been recreated after beeing e.g. a sheet,
+ // make sure that we don't report a faulty opacity:
+ q->setWindowOpacity(1.0f);
+ q->setAttribute(Qt::WA_WState_WindowOpacitySet, false);
+ }
+
+ // Since we only now have a window, sync our state.
+ macUpdateHideOnSuspend();
+ macUpdateOpaqueSizeGrip();
+ macUpdateMetalAttribute();
+ macUpdateIgnoreMouseEvents();
+ setWindowTitle_helper(extra->topextra->caption);
+ setWindowIconText_helper(extra->topextra->iconText);
+ setWindowFilePath_helper(extra->topextra->filePath);
+ setWindowModified_sys(q->isWindowModified());
+ updateFrameStrut();
+ qt_mac_update_sizer(q);
+}
+#else // QT_MAC_USE_COCOA
+void QWidgetPrivate::finishCreateWindow_sys_Cocoa(void * /*NSWindow * */ voidWindowRef)
+{
+ Q_Q(QWidget);
+ QMacCocoaAutoReleasePool pool;
+ NSWindow *windowRef = static_cast<NSWindow *>(voidWindowRef);
+ const Qt::WindowType type = q->windowType();
+ Qt::WindowFlags &flags = data.window_flags;
+ QWidget *parentWidget = q->parentWidget();
+
+ const bool popup = (type == Qt::Popup);
+ const bool dialog = (type == Qt::Dialog
+ || type == Qt::Sheet
+ || type == Qt::Drawer
+ || (flags & Qt::MSWindowsFixedSizeDialogHint));
+ QTLWExtra *topExtra = topData();
+
+ if ((popup || type == Qt::Tool || type == Qt::ToolTip) && !q->isModal()) {
+ [windowRef setHidesOnDeactivate:YES];
+ [windowRef setHasShadow:YES];
+ }
+ Q_UNUSED(parentWidget);
+ Q_UNUSED(dialog);
+
+ data.fstrut_dirty = true; // when we create a toplevel widget, the frame strut should be dirty
+ OSViewRef nsview = (OSViewRef)data.winid;
+ OSViewRef window_contentview = qt_mac_get_contentview_for(windowRef);
+ if (!nsview) {
+ nsview = qt_mac_create_widget(q, this, window_contentview);
+ setWinId(WId(nsview));
+ } else {
+ [window_contentview addSubview:nsview];
+ }
+ if (nsview) {
+ NSRect bounds = [window_contentview bounds];
+ [nsview setFrame:bounds];
+ [nsview setHidden:NO];
+ if (q->testAttribute(Qt::WA_DropSiteRegistered))
+ registerDropSite(true);
+ transferChildren();
+ }
+
+ if (topExtra->posFromMove) {
+ updateFrameStrut();
+
+ const QRect &fStrut = frameStrut();
+ const QRect &crect = data.crect;
+ const QRect frameRect(QPoint(crect.left(), crect.top()),
+ QSize(fStrut.left() + fStrut.right() + crect.width(),
+ fStrut.top() + fStrut.bottom() + crect.height()));
+ NSRect cocoaFrameRect = NSMakeRect(frameRect.x(), flipYCoordinate(frameRect.bottom() + 1),
+ frameRect.width(), frameRect.height());
+ [windowRef setFrame:cocoaFrameRect display:NO];
+ topExtra->posFromMove = false;
+ }
+
+ if (q->testAttribute(Qt::WA_WState_WindowOpacitySet)){
+ q->setWindowOpacity(topExtra->opacity / 255.0f);
+ } else if (qt_mac_is_macsheet(q)){
+ CGFloat alpha = [qt_mac_window_for(q) alphaValue];
+ if (alpha >= 1.0) {
+ q->setWindowOpacity(0.95f);
+ q->setAttribute(Qt::WA_WState_WindowOpacitySet, false);
+ }
+ } else{
+ // If the window has been recreated after beeing e.g. a sheet,
+ // make sure that we don't report a faulty opacity:
+ q->setWindowOpacity(1.0f);
+ q->setAttribute(Qt::WA_WState_WindowOpacitySet, false);
+ }
+
+ macUpdateHideOnSuspend();
+ macUpdateOpaqueSizeGrip();
+ macUpdateIgnoreMouseEvents();
+ setWindowTitle_helper(extra->topextra->caption);
+ setWindowIconText_helper(extra->topextra->iconText);
+ setWindowModified_sys(q->isWindowModified());
+ updateFrameStrut();
+ syncCocoaMask();
+ macUpdateIsOpaque();
+ qt_mac_update_sizer(q);
+}
+
+#endif // QT_MAC_USE_COCOA
+
+/*
+ Recreates widget window. Useful if immutable
+ properties for it has changed.
+ */
+void QWidgetPrivate::recreateMacWindow()
+{
+ Q_Q(QWidget);
+ OSViewRef myView = qt_mac_nativeview_for(q);
+ OSWindowRef oldWindow = qt_mac_window_for(myView);
+#ifndef QT_MAC_USE_COCOA
+ HIViewRemoveFromSuperview(myView);
+ determineWindowClass();
+ createWindow_sys();
+ if (QMainWindowLayout *mwl = qobject_cast<QMainWindowLayout *>(q->layout())) {
+ mwl->updateHIToolBarStatus();
+ }
+
+ if (IsWindowVisible(oldWindow))
+ show_sys();
+#else
+ QMacCocoaAutoReleasePool pool;
+ [myView removeFromSuperview];
+ determineWindowClass();
+ createWindow_sys();
+ if (NSToolbar *toolbar = [oldWindow toolbar]) {
+ OSWindowRef newWindow = qt_mac_window_for(myView);
+ [newWindow setToolbar:toolbar];
+ [toolbar setVisible:[toolbar isVisible]];
+ }
+ if ([oldWindow isVisible]){
+ if ([oldWindow isSheet])
+ [NSApp endSheet:oldWindow];
+ [oldWindow orderOut:oldWindow];
+ show_sys();
+ }
+#endif // QT_MAC_USE_COCOA
+
+ // Release the window after creating the new window, because releasing it early
+ // may cause the app to quit ("close on last window closed attribute")
+ qt_mac_destructWindow(oldWindow);
+}
+
+void QWidgetPrivate::createWindow_sys()
+{
+ Q_Q(QWidget);
+ Qt::WindowFlags &flags = data.window_flags;
+ QWidget *parentWidget = q->parentWidget();
+
+ QTLWExtra *topExtra = topData();
+ if (topExtra->embedded)
+ return; // Simply return because this view "is" the top window.
+ quint32 wattr = topExtra->wattr;
+
+ if(parentWidget && (parentWidget->window()->windowFlags() & Qt::WindowStaysOnTopHint)) // If our parent has Qt::WStyle_StaysOnTop, so must we
+ flags |= Qt::WindowStaysOnTopHint;
+
+ data.fstrut_dirty = true;
+
+ OSWindowRef windowRef = qt_mac_create_window(q, topExtra->wclass, wattr, data.crect);
+ if (windowRef == 0)
+ qWarning("QWidget: Internal error: %s:%d: If you reach this error please contact Trolltech and include the\n"
+ " WidgetFlags used in creating the widget.", __FILE__, __LINE__);
+#ifndef QT_MAC_USE_COCOA
+ finishCreateWindow_sys_Carbon(windowRef);
+#else
+ finishCreateWindow_sys_Cocoa(windowRef);
+#endif
+}
+
+void QWidgetPrivate::create_sys(WId window, bool initializeWindow, bool destroyOldWindow)
+{
+ Q_Q(QWidget);
+ OSViewRef destroyid = 0;
+#ifndef QT_MAC_USE_COCOA
+ window_event = 0;
+#endif
+
+ Qt::WindowType type = q->windowType();
+ Qt::WindowFlags flags = data.window_flags;
+ QWidget *parentWidget = q->parentWidget();
+
+ bool topLevel = (flags & Qt::Window);
+ bool popup = (type == Qt::Popup);
+ bool dialog = (type == Qt::Dialog
+ || type == Qt::Sheet
+ || type == Qt::Drawer
+ || (flags & Qt::MSWindowsFixedSizeDialogHint));
+ bool desktop = (type == Qt::Desktop);
+
+ // Determine this early for top-levels so, we can use it later.
+ if (topLevel)
+ determineWindowClass();
+
+ if (desktop) {
+ QSize desktopSize = qt_mac_desktopSize();
+ q->setAttribute(Qt::WA_WState_Visible);
+ data.crect.setRect(0, 0, desktopSize.width(), desktopSize.height());
+ dialog = popup = false; // force these flags off
+ } else {
+ q->setAttribute(Qt::WA_WState_Visible, false);
+
+ if (topLevel && (type != Qt::Drawer)) {
+ if (QDesktopWidget *dsk = QApplication::desktop()) { // calc pos/size from screen
+ const bool wasResized = q->testAttribute(Qt::WA_Resized);
+ const bool wasMoved = q->testAttribute(Qt::WA_Moved);
+ int deskn = dsk->primaryScreen();
+ if (parentWidget && parentWidget->windowType() != Qt::Desktop)
+ deskn = dsk->screenNumber(parentWidget);
+ QRect screenGeo = dsk->screenGeometry(deskn);
+ if (!wasResized) {
+#ifndef QT_MAC_USE_COCOA
+ data.crect.setSize(QSize(screenGeo.width()/2, 4*screenGeo.height()/10));
+#else
+ NSRect newRect = [NSWindow frameRectForContentRect:NSMakeRect(0, 0,
+ screenGeo.width() / 2.,
+ 4 * screenGeo.height() / 10.)
+ styleMask:topData()->wattr];
+ data.crect.setSize(QSize(newRect.size.width, newRect.size.height));
+#endif
+ // Constrain to minimums and maximums we've set
+ if (extra->minw > 0)
+ data.crect.setWidth(qMax(extra->minw, data.crect.width()));
+ if (extra->minh > 0)
+ data.crect.setHeight(qMax(extra->minh, data.crect.height()));
+ if (extra->maxw > 0)
+ data.crect.setWidth(qMin(extra->maxw, data.crect.width()));
+ if (extra->maxh > 0)
+ data.crect.setHeight(qMin(extra->maxh, data.crect.height()));
+ }
+ if (!wasMoved && !q->testAttribute(Qt::WA_DontShowOnScreen))
+ data.crect.moveTopLeft(QPoint(screenGeo.width()/4,
+ 3 * screenGeo.height() / 10));
+ }
+ }
+ }
+
+
+ if(!window) // always initialize
+ initializeWindow=true;
+
+ hd = 0;
+ if(window) { // override the old window (with a new NSView)
+ OSViewRef nativeView = OSViewRef(window);
+ OSViewRef parent = 0;
+#ifndef QT_MAC_USE_COCOA
+ CFRetain(nativeView);
+#else
+ [nativeView retain];
+#endif
+ if (destroyOldWindow)
+ destroyid = qt_mac_nativeview_for(q);
+ bool transfer = false;
+ setWinId((WId)nativeView);
+#ifndef QT_MAC_USE_COCOA
+#ifndef HIViewInstallEventHandler
+ // Macro taken from the CarbonEvents Header on Tiger
+#define HIViewInstallEventHandler( target, handler, numTypes, list, userData, outHandlerRef ) \
+ InstallEventHandler( HIObjectGetEventTarget( (HIObjectRef) (target) ), (handler), (numTypes), (list), (userData), (outHandlerRef) )
+#endif
+ HIViewInstallEventHandler(nativeView, make_widget_eventUPP(), GetEventTypeCount(widget_events), widget_events, 0, 0);
+#endif
+ if(topLevel) {
+ for(int i = 0; i < 2; ++i) {
+ if(i == 1) {
+ if(!initializeWindow)
+ break;
+ createWindow_sys();
+ }
+ if(OSWindowRef windowref = qt_mac_window_for(nativeView)) {
+#ifndef QT_MAC_USE_COCOA
+ CFRetain(windowref);
+#else
+ [windowref retain];
+#endif
+ if (initializeWindow) {
+ parent = qt_mac_get_contentview_for(windowref);
+ } else {
+#ifndef QT_MAC_USE_COCOA
+ parent = HIViewGetSuperview(nativeView);
+#else
+ parent = [nativeView superview];
+#endif
+ }
+ break;
+ }
+ }
+ if(!parent)
+ transfer = true;
+ } else if (parentWidget) {
+ // I need to be added to my parent, therefore my parent needs an NSView
+ parentWidget->createWinId();
+ parent = qt_mac_nativeview_for(parentWidget);
+ }
+ if(parent != nativeView && parent) {
+#ifndef QT_MAC_USE_COCOA
+ HIViewAddSubview(parent, nativeView);
+#else
+ [parent addSubview:nativeView];
+#endif
+ }
+ if(transfer)
+ transferChildren();
+ data.fstrut_dirty = true; // we'll re calculate this later
+ q->setAttribute(Qt::WA_WState_Visible,
+#ifndef QT_MAC_USE_COCOA
+ HIViewIsVisible(nativeView)
+#else
+ ![nativeView isHidden]
+#endif
+ );
+ if(initializeWindow) {
+#ifndef QT_MAC_USE_COCOA
+ HIRect bounds = CGRectMake(data.crect.x(), data.crect.y(), data.crect.width(), data.crect.height());
+ HIViewSetFrame(nativeView, &bounds);
+ q->setAttribute(Qt::WA_WState_Visible, HIViewIsVisible(nativeView));
+#else
+ NSRect bounds = NSMakeRect(data.crect.x(), data.crect.y(), data.crect.width(), data.crect.height());
+ [nativeView setFrame:bounds];
+ q->setAttribute(Qt::WA_WState_Visible, [nativeView isHidden]);
+#endif
+ }
+#ifndef QT_MAC_USE_COCOA
+ initWindowPtr();
+#endif
+ } else if (desktop) { // desktop widget
+ if (!qt_root_win)
+ QWidgetPrivate::qt_create_root_win();
+ Q_ASSERT(qt_root_win);
+ WId rootWinID = 0;
+#ifndef QT_MAC_USE_COCOA
+ CFRetain(qt_root_win);
+ if(HIViewRef rootContentView = HIViewGetRoot(qt_root_win)) {
+ rootWinID = (WId)rootContentView;
+ CFRetain(rootContentView);
+ }
+#else
+ [qt_root_win retain];
+ if (OSViewRef rootContentView = [qt_root_win contentView]) {
+ rootWinID = (WId)rootContentView;
+ [rootContentView retain];
+ }
+#endif
+ setWinId(rootWinID);
+ } else if (topLevel) {
+ determineWindowClass();
+ if(OSViewRef osview = qt_mac_create_widget(q, this, 0)) {
+#ifndef QT_MAC_USE_COCOA
+ HIRect bounds = CGRectMake(data.crect.x(), data.crect.y(),
+ data.crect.width(), data.crect.height());
+ HIViewSetFrame(osview, &bounds);
+#else
+ NSRect bounds = NSMakeRect(data.crect.x(), flipYCoordinate(data.crect.y()),
+ data.crect.width(), data.crect.height());
+ [osview setFrame:bounds];
+#endif
+ setWinId((WId)osview);
+ }
+ } else {
+ data.fstrut_dirty = false; // non-toplevel widgets don't have a frame, so no need to update the strut
+ if(OSViewRef osview = qt_mac_create_widget(q, this, qt_mac_nativeview_for(parentWidget))) {
+#ifndef QT_MAC_USE_COCOA
+ HIRect bounds = CGRectMake(data.crect.x(), data.crect.y(), data.crect.width(), data.crect.height());
+ HIViewSetFrame(osview, &bounds);
+ setWinId((WId)osview);
+#else
+ NSRect bounds = NSMakeRect(data.crect.x(), data.crect.y(), data.crect.width(), data.crect.height());
+ [osview setFrame:bounds];
+ setWinId((WId)osview);
+#endif
+ if (q->testAttribute(Qt::WA_DropSiteRegistered))
+ registerDropSite(true);
+ }
+ }
+
+ updateIsOpaque();
+ if (!topLevel && initializeWindow)
+ setWSGeometry();
+
+ if (destroyid)
+ qt_mac_destructView(destroyid);
+}
+
+/*!
+ Returns the QuickDraw handle of the widget. Use of this function is not
+ portable. This function will return 0 if QuickDraw is not supported, or
+ if the handle could not be created.
+
+ \warning This function is only available on Mac OS X.
+*/
+
+Qt::HANDLE
+QWidget::macQDHandle() const
+{
+#ifndef QT_MAC_USE_COCOA
+ return d_func()->qd_hd;
+#else
+ return 0;
+#endif
+}
+
+/*!
+ Returns the CoreGraphics handle of the widget. Use of this function is
+ not portable. This function will return 0 if no painter context can be
+ established, or if the handle could not be created.
+
+ \warning This function is only available on Mac OS X.
+*/
+Qt::HANDLE
+QWidget::macCGHandle() const
+{
+ return handle();
+}
+
+void QWidget::destroy(bool destroyWindow, bool destroySubWindows)
+{
+ Q_D(QWidget);
+ if (!isWindow() && parentWidget())
+ parentWidget()->d_func()->invalidateBuffer(geometry());
+ d->deactivateWidgetCleanup();
+ qt_mac_event_release(this);
+ if(testAttribute(Qt::WA_WState_Created)) {
+ QMacCocoaAutoReleasePool pool;
+ setAttribute(Qt::WA_WState_Created, false);
+ QObjectList chldrn = children();
+ for(int i = 0; i < chldrn.size(); i++) { // destroy all widget children
+ QObject *obj = chldrn.at(i);
+ if(obj->isWidgetType())
+ static_cast<QWidget*>(obj)->destroy(destroySubWindows, destroySubWindows);
+ }
+ if(mac_mouse_grabber == this)
+ releaseMouse();
+ if(mac_keyboard_grabber == this)
+ releaseKeyboard();
+ if(acceptDrops())
+ setAcceptDrops(false);
+
+ if(testAttribute(Qt::WA_ShowModal)) // just be sure we leave modal
+ QApplicationPrivate::leaveModal(this);
+ else if((windowType() == Qt::Popup))
+ qApp->d_func()->closePopup(this);
+ if (destroyWindow) {
+ if(OSViewRef hiview = qt_mac_nativeview_for(this)) {
+ OSWindowRef window = 0;
+ NSDrawer *drawer = nil;
+#ifdef QT_MAC_USE_COCOA
+ if (qt_mac_is_macdrawer(this)) {
+ drawer = qt_mac_drawer_for(this);
+ } else
+#endif
+ if (isWindow())
+ window = qt_mac_window_for(hiview);
+
+ // Because of how "destruct" works, we have to do just a normal release for the root_win.
+ if (window && window == qt_root_win) {
+#ifndef QT_MAC_USE_COCOA
+ CFRelease(hiview);
+#else
+ [hiview release];
+#endif
+ } else {
+ qt_mac_destructView(hiview);
+ }
+ if (drawer)
+ qt_mac_destructDrawer(drawer);
+ if (window)
+ qt_mac_destructWindow(window);
+ }
+ }
+ d->setWinId(0);
+ }
+}
+
+void QWidgetPrivate::transferChildren()
+{
+ Q_Q(QWidget);
+ if (!q->testAttribute(Qt::WA_WState_Created))
+ return; // Can't add any views anyway
+
+ QObjectList chlist = q->children();
+ for (int i = 0; i < chlist.size(); ++i) {
+ QObject *obj = chlist.at(i);
+ if (obj->isWidgetType()) {
+ QWidget *w = (QWidget *)obj;
+ if (!w->isWindow()) {
+ // This seems weird, no need to call it in a loop right?
+ if (!topData()->caption.isEmpty())
+ setWindowTitle_helper(extra->topextra->caption);
+ if (w->testAttribute(Qt::WA_WState_Created)) {
+#ifndef QT_MAC_USE_COCOA
+ HIViewAddSubview(qt_mac_nativeview_for(q), qt_mac_nativeview_for(w));
+#else
+ // New NSWindows get an extra reference when drops are
+ // registered (at least in 10.5) which means that we may
+ // access the window later and get a crash (becasue our
+ // widget is dead). Work around this be having the drop
+ // site disabled until it is part of the new hierarchy.
+ bool oldRegistered = w->testAttribute(Qt::WA_DropSiteRegistered);
+ w->setAttribute(Qt::WA_DropSiteRegistered, false);
+ [qt_mac_nativeview_for(q) addSubview:qt_mac_nativeview_for(w)];
+ w->setAttribute(Qt::WA_DropSiteRegistered, oldRegistered);
+#endif
+ }
+ }
+ }
+ }
+}
+
+void QWidgetPrivate::setParent_sys(QWidget *parent, Qt::WindowFlags f)
+{
+ Q_Q(QWidget);
+ QMacCocoaAutoReleasePool pool;
+ QTLWExtra *topData = maybeTopData();
+ bool wasCreated = q->testAttribute(Qt::WA_WState_Created);
+#ifdef QT_MAC_USE_COCOA
+ bool wasWindow = q->isWindow();
+#endif
+ OSViewRef old_id = 0;
+
+ if (q->isVisible() && q->parentWidget() && parent != q->parentWidget())
+ q->parentWidget()->d_func()->invalidateBuffer(q->geometry());
+
+ // Maintain the glWidgets list on parent change: remove "our" gl widgets
+ // from the list on the old parent and grandparents.
+ if (glWidgets.isEmpty() == false) {
+ QWidget *current = q->parentWidget();
+ while (current) {
+ for (QList<QWidgetPrivate::GlWidgetInfo>::const_iterator it = glWidgets.constBegin();
+ it != glWidgets.constEnd(); ++it)
+ current->d_func()->glWidgets.removeAll(*it);
+
+ if (current->isWindow())
+ break;
+ current = current->parentWidget();
+ }
+ }
+
+#ifndef QT_MAC_USE_COCOA
+ EventHandlerRef old_window_event = 0;
+#else
+ bool oldToolbarVisible = false;
+ NSDrawer *oldDrawer = nil;
+ NSToolbar *oldToolbar = 0;
+#endif
+ if (wasCreated && !(q->windowType() == Qt::Desktop)) {
+ old_id = qt_mac_nativeview_for(q);
+#ifndef QT_MAC_USE_COCOA
+ old_window_event = window_event;
+#else
+ OSWindowRef oldWindow = qt_mac_window_for(old_id);
+ if (qt_mac_is_macdrawer(q)) {
+ oldDrawer = qt_mac_drawer_for(q);
+ }
+ if (wasWindow) {
+ oldToolbar = [oldWindow toolbar];
+ oldToolbarVisible = [oldToolbar isVisible];
+ }
+#endif
+ }
+ QWidget* oldtlw = q->window();
+
+ if (q->testAttribute(Qt::WA_DropSiteRegistered))
+ q->setAttribute(Qt::WA_DropSiteRegistered, false);
+
+ //recreate and setup flags
+ QObjectPrivate::setParent_helper(parent);
+ QPoint pt = q->pos();
+ bool explicitlyHidden = q->testAttribute(Qt::WA_WState_Hidden) && q->testAttribute(Qt::WA_WState_ExplicitShowHide);
+ if (wasCreated && !qt_isGenuineQWidget(q))
+ return;
+
+ if ((data.window_flags & Qt::Sheet) && topData && topData->opacity == 242)
+ q->setWindowOpacity(1.0f);
+
+ setWinId(0); //do after the above because they may want the id
+
+ data.window_flags = f;
+ q->setAttribute(Qt::WA_WState_Created, false);
+ q->setAttribute(Qt::WA_WState_Visible, false);
+ q->setAttribute(Qt::WA_WState_Hidden, false);
+ adjustFlags(data.window_flags, q);
+ // keep compatibility with previous versions, we need to preserve the created state
+ // (but we recreate the winId for the widget being reparented, again for compatibility)
+ if (wasCreated || (!q->isWindow() && parent->testAttribute(Qt::WA_WState_Created))) {
+ createWinId();
+ if (q->isWindow()) {
+#ifndef QT_MAC_USE_COCOA
+ if (QMainWindowLayout *mwl = qobject_cast<QMainWindowLayout *>(q->layout())) {
+ mwl->updateHIToolBarStatus();
+ }
+#else
+ if (oldToolbar && !(f & Qt::FramelessWindowHint)) {
+ OSWindowRef newWindow = qt_mac_window_for(q);
+ [newWindow setToolbar:oldToolbar];
+ [oldToolbar setVisible:oldToolbarVisible];
+ }
+#endif
+ }
+ }
+ if (q->isWindow() || (!parent || parent->isVisible()) || explicitlyHidden)
+ q->setAttribute(Qt::WA_WState_Hidden);
+ q->setAttribute(Qt::WA_WState_ExplicitShowHide, explicitlyHidden);
+
+ if (wasCreated) {
+ transferChildren();
+ if (topData &&
+ (!topData->caption.isEmpty() || !topData->filePath.isEmpty()))
+ setWindowTitle_helper(q->windowTitle());
+ }
+
+ if (q->testAttribute(Qt::WA_AcceptDrops)
+ || (!q->isWindow() && q->parentWidget()
+ && q->parentWidget()->testAttribute(Qt::WA_DropSiteRegistered)))
+ q->setAttribute(Qt::WA_DropSiteRegistered, true);
+
+ //cleanup
+#ifndef QT_MAC_USE_COCOA
+ if (old_window_event)
+ RemoveEventHandler(old_window_event);
+#endif
+ if (old_id) { //don't need old window anymore
+ OSWindowRef window = (oldtlw == q) ? qt_mac_window_for(old_id) : 0;
+ qt_mac_destructView(old_id);
+
+#ifdef QT_MAC_USE_COCOA
+ if (oldDrawer) {
+ qt_mac_destructDrawer(oldDrawer);
+ } else
+#endif
+ if (window)
+ qt_mac_destructWindow(window);
+ }
+
+ // Maintain the glWidgets list on parent change: add "our" gl widgets
+ // to the list on the new parent and grandparents.
+ if (glWidgets.isEmpty() == false) {
+ QWidget *current = q->parentWidget();
+ while (current) {
+ current->d_func()->glWidgets += glWidgets;
+ if (current->isWindow())
+ break;
+ current = current->parentWidget();
+ }
+ }
+
+ invalidateBuffer(q->rect());
+ qt_event_request_window_change(q);
+}
+
+QPoint QWidget::mapToGlobal(const QPoint &pos) const
+{
+ Q_D(const QWidget);
+ if (!testAttribute(Qt::WA_WState_Created)) {
+ QPoint p = pos + data->crect.topLeft();
+ return isWindow() ? p : parentWidget()->mapToGlobal(p);
+ }
+#ifndef QT_MAC_USE_COCOA
+ QPoint tmp = d->mapToWS(pos);
+ HIPoint hi_pos = CGPointMake(tmp.x(), tmp.y());
+ HIViewConvertPoint(&hi_pos, qt_mac_nativeview_for(this), 0);
+ Rect win_rect;
+ GetWindowBounds(qt_mac_window_for(this), kWindowStructureRgn, &win_rect);
+ return QPoint((int)hi_pos.x+win_rect.left, (int)hi_pos.y+win_rect.top);
+#else
+ QPoint tmp = d->mapToWS(pos);
+ NSPoint hi_pos = NSMakePoint(tmp.x(), tmp.y());
+ hi_pos = [qt_mac_nativeview_for(this) convertPoint:hi_pos toView:nil];
+ NSRect win_rect = [qt_mac_window_for(this) frame];
+ hi_pos.x += win_rect.origin.x;
+ hi_pos.y += win_rect.origin.y;
+ // If we aren't the desktop we need to flip, if you flip the desktop on itself, you get the other problem.
+ return ((window()->windowFlags() & Qt::Desktop) == Qt::Desktop) ? QPointF(hi_pos.x, hi_pos.y).toPoint()
+ : flipPoint(hi_pos).toPoint();
+#endif
+}
+
+QPoint QWidget::mapFromGlobal(const QPoint &pos) const
+{
+ Q_D(const QWidget);
+ if (!testAttribute(Qt::WA_WState_Created)) {
+ QPoint p = isWindow() ? pos : parentWidget()->mapFromGlobal(pos);
+ return p - data->crect.topLeft();
+ }
+#ifndef QT_MAC_USE_COCOA
+ Rect win_rect;
+ GetWindowBounds(qt_mac_window_for(this), kWindowStructureRgn, &win_rect);
+ HIPoint hi_pos = CGPointMake(pos.x()-win_rect.left, pos.y()-win_rect.top);
+ HIViewConvertPoint(&hi_pos, 0, qt_mac_nativeview_for(this));
+ return d->mapFromWS(QPoint((int)hi_pos.x, (int)hi_pos.y));
+#else
+ NSRect win_rect = [qt_mac_window_for(this) frame];
+ // The Window point is in "Cocoa coordinates," but the view is in "Qt coordinates"
+ // so make sure to keep them in sync.
+ NSPoint hi_pos = NSMakePoint(pos.x()-win_rect.origin.x,
+ flipYCoordinate(pos.y())-win_rect.origin.y);
+ hi_pos = [qt_mac_nativeview_for(this) convertPoint:hi_pos fromView:0];
+ return d->mapFromWS(QPoint(qRound(hi_pos.x), qRound(hi_pos.y)));
+#endif
+}
+
+void QWidgetPrivate::updateSystemBackground()
+{
+}
+
+void QWidgetPrivate::setCursor_sys(const QCursor &)
+{
+ qt_mac_update_cursor();
+}
+
+void QWidgetPrivate::unsetCursor_sys()
+{
+ qt_mac_update_cursor();
+}
+
+void QWidgetPrivate::setWindowTitle_sys(const QString &caption)
+{
+ Q_Q(QWidget);
+ if (q->isWindow()) {
+#ifndef QT_MAC_USE_COCOA
+ SetWindowTitleWithCFString(qt_mac_window_for(q), QCFString(caption));
+#else
+ QMacCocoaAutoReleasePool pool;
+ [qt_mac_window_for(q)
+ setTitle:reinterpret_cast<const NSString *>(static_cast<CFStringRef>(QCFString(caption)))];
+#endif
+ }
+}
+
+void QWidgetPrivate::setWindowModified_sys(bool mod)
+{
+ Q_Q(QWidget);
+ if (q->isWindow() && q->testAttribute(Qt::WA_WState_Created)) {
+#ifndef QT_MAC_USE_COCOA
+ SetWindowModified(qt_mac_window_for(q), mod);
+#else
+ [qt_mac_window_for(q) setDocumentEdited:mod];
+#endif
+ }
+}
+
+void QWidgetPrivate::setWindowFilePath_sys(const QString &filePath)
+{
+ Q_Q(QWidget);
+#ifdef QT_MAC_USE_COCOA
+ QMacCocoaAutoReleasePool pool;
+ [qt_mac_window_for(q) setRepresentedFilename:reinterpret_cast<const NSString *>(static_cast<CFStringRef>(QCFString(filePath)))];
+#else
+ bool validRef = false;
+ FSRef ref;
+ bzero(&ref, sizeof(ref));
+ OSStatus status;
+
+ if (!filePath.isEmpty()) {
+ status = FSPathMakeRef(reinterpret_cast<const UInt8 *>(filePath.toUtf8().constData()), &ref, 0);
+ validRef = (status == noErr);
+ }
+ // Set the proxy regardless, since this is our way of clearing it as well, but ignore the
+ // return value as well.
+ if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_4) {
+ if (validRef) {
+ status = HIWindowSetProxyFSRef(qt_mac_window_for(q), &ref);
+ } else {
+ status = RemoveWindowProxy(qt_mac_window_for(q));
+ }
+ } else {
+ // Convert to an FSSpec and set it. It's deprecated but it works for where we don't have the other call.
+ if (validRef) {
+ FSSpec fsspec;
+ FSGetCatalogInfo(&ref, kFSCatInfoNone, 0, 0, &fsspec, 0);
+ status = SetWindowProxyFSSpec(qt_mac_window_for(q), &fsspec);
+ } else {
+ status = RemoveWindowProxy(qt_mac_window_for(q));
+ }
+ }
+ if (status != noErr)
+ qWarning("QWidget::setWindowFilePath: Error setting proxyicon for path (%s):%ld",
+ qPrintable(filePath), status);
+#endif
+}
+
+void QWidgetPrivate::setWindowIcon_sys(bool forceReset)
+{
+ Q_Q(QWidget);
+
+ if (!q->testAttribute(Qt::WA_WState_Created))
+ return;
+
+ QTLWExtra *topData = this->topData();
+ if (topData->iconPixmap && !forceReset) // already set
+ return;
+
+ QIcon icon = q->windowIcon();
+ QPixmap *pm = 0;
+ if (!icon.isNull()) {
+ // now create the extra
+ if (!topData->iconPixmap) {
+ pm = new QPixmap(icon.pixmap(QSize(22, 22)));
+ topData->iconPixmap = pm;
+ } else {
+ pm = topData->iconPixmap;
+ }
+ }
+ if (q->isWindow()) {
+#ifndef QT_MAC_USE_COCOA
+ IconRef previousIcon = 0;
+ if (icon.isNull()) {
+ RemoveWindowProxy(qt_mac_window_for(q));
+ previousIcon = topData->windowIcon;
+ topData->windowIcon = 0;
+ } else {
+ WindowClass wclass;
+ GetWindowClass(qt_mac_window_for(q), &wclass);
+
+ if (wclass == kDocumentWindowClass) {
+ IconRef newIcon = qt_mac_create_iconref(*pm);
+ previousIcon = topData->windowIcon;
+ topData->windowIcon = newIcon;
+ SetWindowProxyIcon(qt_mac_window_for(q), newIcon);
+ }
+ }
+
+ // Release the previous icon if it was set by this function.
+ if (previousIcon != 0)
+ ReleaseIconRef(previousIcon);
+#else
+ QMacCocoaAutoReleasePool pool;
+ NSButton *iconButton = [qt_mac_window_for(q) standardWindowButton:NSWindowDocumentIconButton];
+ if (icon.isNull()) {
+ [iconButton setImage:nil];
+ } else {
+ NSImage *image = static_cast<NSImage *>(qt_mac_create_nsimage(*pm));
+ [iconButton setImage:image];
+ [image release];
+ }
+#endif
+ }
+}
+
+void QWidgetPrivate::setWindowIconText_sys(const QString &iconText)
+{
+ Q_Q(QWidget);
+ if(q->isWindow() && !iconText.isEmpty()) {
+#ifndef QT_MAC_USE_COCOA
+ SetWindowAlternateTitle(qt_mac_window_for(q), QCFString(iconText));
+#else
+ QMacCocoaAutoReleasePool pool;
+ [qt_mac_window_for(q)
+ setMiniwindowTitle:reinterpret_cast<const NSString *>(static_cast<CFStringRef>(QCFString(iconText)))];
+#endif
+ }
+}
+
+void QWidget::grabMouse()
+{
+ if(isVisible() && !qt_nograb()) {
+ if(mac_mouse_grabber)
+ mac_mouse_grabber->releaseMouse();
+ mac_mouse_grabber=this;
+ }
+}
+
+void QWidget::grabMouse(const QCursor &)
+{
+ if(isVisible() && !qt_nograb()) {
+ if(mac_mouse_grabber)
+ mac_mouse_grabber->releaseMouse();
+ mac_mouse_grabber=this;
+ }
+}
+
+void QWidget::releaseMouse()
+{
+ if(!qt_nograb() && mac_mouse_grabber == this)
+ mac_mouse_grabber = 0;
+}
+
+void QWidget::grabKeyboard()
+{
+ if(!qt_nograb()) {
+ if(mac_keyboard_grabber)
+ mac_keyboard_grabber->releaseKeyboard();
+ mac_keyboard_grabber = this;
+ }
+}
+
+void QWidget::releaseKeyboard()
+{
+ if(!qt_nograb() && mac_keyboard_grabber == this)
+ mac_keyboard_grabber = 0;
+}
+
+QWidget *QWidget::mouseGrabber()
+{
+ return mac_mouse_grabber;
+}
+
+QWidget *QWidget::keyboardGrabber()
+{
+ return mac_keyboard_grabber;
+}
+
+void QWidget::activateWindow()
+{
+ QWidget *tlw = window();
+ if(!tlw->isVisible() || !tlw->isWindow() || (tlw->windowType() == Qt::Desktop))
+ return;
+ qt_event_remove_activate();
+
+ QWidget *fullScreenWidget = tlw;
+ QWidget *parentW = tlw;
+ // Find the oldest parent or the parent with fullscreen, whichever comes first.
+ while (parentW) {
+ fullScreenWidget = parentW->window();
+ if (fullScreenWidget->windowState() & Qt::WindowFullScreen)
+ break;
+ parentW = fullScreenWidget->parentWidget();
+ }
+
+ if (fullScreenWidget->windowType() != Qt::ToolTip) {
+ qt_mac_set_fullscreen_mode((fullScreenWidget->windowState() & Qt::WindowFullScreen) &&
+ qApp->desktop()->screenNumber(this) == 0);
+ }
+
+ bool windowActive;
+ OSWindowRef win = qt_mac_window_for(tlw);
+#ifndef QT_MAC_USE_COCOA
+ windowActive = IsWindowActive(win);
+#else
+ QMacCocoaAutoReleasePool pool;
+ windowActive = [win isKeyWindow];
+#endif
+ if ((tlw->windowType() == Qt::Popup)
+ || (tlw->windowType() == Qt::Tool)
+ || qt_mac_is_macdrawer(tlw)
+ || windowActive) {
+#ifndef QT_MAC_USE_COCOA
+ ActivateWindow(win, true);
+#else
+ [win makeKeyWindow];
+#endif
+ qApp->setActiveWindow(tlw);
+ } else if(!isMinimized()) {
+#ifndef QT_MAC_USE_COCOA
+ SelectWindow(win);
+#else
+ [win makeKeyAndOrderFront:win];
+#endif
+ }
+}
+
+QWindowSurface *QWidgetPrivate::createDefaultWindowSurface_sys()
+{
+ return new QMacWindowSurface(q_func());
+}
+
+void QWidgetPrivate::update_sys(const QRect &r)
+{
+ Q_Q(QWidget);
+ if (r == q->rect()) {
+ if (updateRedirectedToGraphicsProxyWidget(q, r))
+ return;
+ dirtyOnWidget += r;
+#ifndef QT_MAC_USE_COCOA
+ HIViewSetNeedsDisplay(qt_mac_nativeview_for(q), true);
+#else
+ [qt_mac_nativeview_for(q) setNeedsDisplay:YES];
+#endif
+ return;
+ }
+
+ int x = r.x(), y = r.y(), w = r.width(), h = r.height();
+ if (w < 0)
+ w = q->data->crect.width() - x;
+ if (h < 0)
+ h = q->data->crect.height() - y;
+ if (w && h) {
+ const QRect updateRect = QRect(x, y, w, h);
+ if (updateRedirectedToGraphicsProxyWidget(q, updateRect))
+ return;
+#ifndef QT_MAC_USE_COCOA
+# if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4
+ if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_4) {
+ dirtyOnWidget += updateRect;
+ HIRect r = CGRectMake(x, y, w, h);
+ HIViewSetNeedsDisplayInRect(qt_mac_nativeview_for(q), &r, true);
+ } else
+ #endif
+ {
+ q->update(QRegion(updateRect));
+ }
+#else
+ [qt_mac_nativeview_for(q) setNeedsDisplayInRect:NSMakeRect(x, y, w, h)];
+#endif
+ }
+}
+
+void QWidgetPrivate::update_sys(const QRegion &rgn)
+{
+ Q_Q(QWidget);
+ if (updateRedirectedToGraphicsProxyWidget(q, rgn))
+ return;
+ dirtyOnWidget += rgn;
+#ifndef QT_MAC_USE_COCOA
+ HIViewSetNeedsDisplayInRegion(qt_mac_nativeview_for(q), QMacSmartQuickDrawRegion(rgn.toQDRgn()), true);
+#else
+ // Cocoa doesn't do regions, it seems more efficient to just update the bounding rect instead of a potential number of message passes for each rect.
+ const QRect &boundingRect = rgn.boundingRect();
+ [qt_mac_nativeview_for(q) setNeedsDisplayInRect:NSMakeRect(boundingRect.x(),
+ boundingRect.y(), boundingRect.width(),
+ boundingRect.height())];
+#endif
+}
+
+bool QWidgetPrivate::isRealWindow() const
+{
+ return q_func()->isWindow() && !topData()->embedded;
+}
+
+void QWidgetPrivate::show_sys()
+{
+ Q_Q(QWidget);
+ if ((q->windowType() == Qt::Desktop)) //desktop is always visible
+ return;
+
+ invalidateBuffer(q->rect());
+ if (q->testAttribute(Qt::WA_OutsideWSRange))
+ return;
+ QMacCocoaAutoReleasePool pool;
+ q->setAttribute(Qt::WA_Mapped);
+ if (q->testAttribute(Qt::WA_DontShowOnScreen))
+ return;
+
+ bool realWindow = isRealWindow();
+ if (realWindow && !q->testAttribute(Qt::WA_Moved)) {
+ q->createWinId();
+ if (QWidget *p = q->parentWidget()) {
+ p->createWinId();
+#ifndef QT_MAC_USE_COCOA
+ RepositionWindow(qt_mac_window_for(q), qt_mac_window_for(p), kWindowCenterOnParentWindow);
+#else
+ CGRect parentFrame = NSRectToCGRect([qt_mac_window_for(p) frame]);
+ OSWindowRef windowRef = qt_mac_window_for(q);
+ NSRect windowFrame = [windowRef frame];
+ NSPoint parentCenter = NSMakePoint(CGRectGetMidX(parentFrame), CGRectGetMidY(parentFrame));
+ [windowRef setFrameTopLeftPoint:NSMakePoint(parentCenter.x - (windowFrame.size.width / 2),
+ (parentCenter.y + (windowFrame.size.height / 2)))];
+#endif
+ } else {
+#ifndef QT_MAC_USE_COCOA
+ RepositionWindow(qt_mac_window_for(q), 0, kWindowCenterOnMainScreen);
+#else
+ // Ideally we would do a "center" here, but NSWindow's center is more equivalent to
+ // kWindowAlertPositionOnMainScreen instead of kWindowCenterOnMainScreen.
+ QRect availGeo = QApplication::desktop()->availableGeometry(q);
+ // Center the content only.
+ data.crect.moveCenter(availGeo.center());
+ QRect fStrut = frameStrut();
+ QRect frameRect(data.crect.x() - fStrut.left(), data.crect.y() - fStrut.top(),
+ fStrut.left() + fStrut.right() + data.crect.width(),
+ fStrut.top() + fStrut.bottom() + data.crect.height());
+ NSRect cocoaFrameRect = NSMakeRect(frameRect.x(), flipYCoordinate(frameRect.bottom() + 1), frameRect.width(), frameRect.height());
+ [qt_mac_window_for(q) setFrame:cocoaFrameRect display:NO];
+#endif
+ }
+ }
+ data.fstrut_dirty = true;
+ if (realWindow) {
+ // Delegates can change window state, so record some things earlier.
+ bool isCurrentlyMinimized = (q->windowState() & Qt::WindowMinimized);
+ setModal_sys();
+ OSWindowRef window = qt_mac_window_for(q);
+#ifndef QT_MAC_USE_COCOA
+ SizeWindow(window, q->width(), q->height(), true);
+#endif
+ if(qt_mac_is_macsheet(q)) {
+ qt_event_request_showsheet(q);
+ } else if(qt_mac_is_macdrawer(q)) {
+#ifndef QT_MAC_USE_COCOA
+ OpenDrawer(window, kWindowEdgeDefault, false);
+#else
+ NSDrawer *drawer = qt_mac_drawer_for(q);
+ [drawer openOnEdge:[drawer preferredEdge]];
+#endif
+ } else {
+#ifndef QT_MAC_USE_COCOA
+ ShowHide(window, true);
+#else
+ // sync the opacity value back (in case of a fade).
+ [window setAlphaValue:q->windowOpacity()];
+
+ [window makeKeyAndOrderFront:window];
+ if (data.window_modality == Qt::ApplicationModal)
+ QCoreApplication::postEvent(qApp, new QEvent(QEvent::CocoaRequestModal));
+#endif
+ if (q->windowType() == Qt::Popup) {
+ if (q->focusWidget())
+ q->focusWidget()->d_func()->setFocus_sys();
+ else
+ setFocus_sys();
+ }
+ toggleDrawers(true);
+ }
+ if (isCurrentlyMinimized) { //show in collapsed state
+#ifndef QT_MAC_USE_COCOA
+ CollapseWindow(window, true);
+#else
+ [window miniaturize:window];
+#endif
+ } else if (!q->testAttribute(Qt::WA_ShowWithoutActivating)) {
+ qt_event_request_activate(q);
+#ifdef QT_MAC_USE_COCOA
+ if (q->windowModality() == Qt::ApplicationModal) {
+ // We call 'activeModalSession' early to force creation of q's modal
+ // session. This seems neccessary for child dialogs to pop to front:
+ QEventDispatcherMacPrivate::activeModalSession();
+ }
+#endif
+ }
+ } else if(topData()->embedded || !q->parentWidget() || q->parentWidget()->isVisible()) {
+#ifndef QT_MAC_USE_COCOA
+ HIViewSetVisible(qt_mac_nativeview_for(q), true);
+#else
+ [qt_mac_nativeview_for(q) setHidden:NO];
+
+#endif
+ }
+
+ if (!QWidget::mouseGrabber()){
+ QWidget *enterWidget = QApplication::widgetAt(QCursor::pos());
+ QApplicationPrivate::dispatchEnterLeave(enterWidget, qt_mouseover);
+ qt_mouseover = enterWidget;
+ }
+
+ qt_event_request_window_change(q);
+}
+
+void QWidgetPrivate::hide_sys()
+{
+ Q_Q(QWidget);
+ if((q->windowType() == Qt::Desktop)) //you can't hide the desktop!
+ return;
+
+ QMacCocoaAutoReleasePool pool;
+ if(q->isWindow()) {
+ OSWindowRef window = qt_mac_window_for(q);
+ if(qt_mac_is_macsheet(q)) {
+#ifndef QT_MAC_USE_COCOA
+ WindowRef parent = 0;
+ if(GetSheetWindowParent(window, &parent) != noErr || !parent)
+ ShowHide(window, false);
+ else
+ HideSheetWindow(window);
+#else
+ [NSApp endSheet:window];
+ [window orderOut:window];
+#endif
+ } else if(qt_mac_is_macdrawer(q)) {
+#ifndef QT_MAC_USE_COCOA
+ CloseDrawer(window, false);
+#else
+ [qt_mac_drawer_for(q) close];
+#endif
+ } else {
+#ifndef QT_MAC_USE_COCOA
+ ShowHide(window, false);
+#else
+ [window orderOut:window];
+#endif
+ toggleDrawers(false);
+#ifndef QT_MAC_USE_COCOA
+ // Clear modality (because it seems something that we've always done).
+ if (data.window_modality != Qt::NonModal) {
+ SetWindowModality(window, kWindowModalityNone,
+ q->parentWidget() ? qt_mac_window_for(q->parentWidget()->window()) : 0);
+ }
+#endif
+ }
+ if(q->isActiveWindow() && !(q->windowType() == Qt::Popup)) {
+ QWidget *w = 0;
+ if(q->parentWidget())
+ w = q->parentWidget()->window();
+ if(!w || (!w->isVisible() && !w->isMinimized())) {
+#ifndef QT_MAC_USE_COCOA
+ for(WindowPtr wp = GetFrontWindowOfClass(kDocumentWindowClass, true);
+ wp; wp = GetNextWindowOfClass(wp, kDocumentWindowClass, true)) {
+ if((w = qt_mac_find_window(wp)))
+ break;
+ }
+ if (!w){
+ for(WindowPtr wp = GetFrontWindowOfClass(kSimpleWindowClass, true);
+ wp; wp = GetNextWindowOfClass(wp, kSimpleWindowClass, true)) {
+ if((w = qt_mac_find_window(wp)))
+ break;
+ }
+ }
+#else
+ NSArray *windows = [NSApp windows];
+ NSUInteger totalWindows = [windows count];
+ for (NSUInteger i = 0; i < totalWindows; ++i) {
+ OSWindowRef wp = [windows objectAtIndex:i];
+ if ((w = qt_mac_find_window(wp)))
+ break;
+ }
+#endif
+ }
+ if(w && w->isVisible() && !w->isMinimized())
+ qt_event_request_activate(w);
+ }
+ } else {
+ invalidateBuffer(q->rect());
+#ifndef QT_MAC_USE_COCOA
+ HIViewSetVisible(qt_mac_nativeview_for(q), false);
+#else
+ [qt_mac_nativeview_for(q) setHidden:YES];
+#endif
+ }
+
+ if (!QWidget::mouseGrabber()){
+ QWidget *enterWidget = QApplication::widgetAt(QCursor::pos());
+ QApplicationPrivate::dispatchEnterLeave(enterWidget, qt_mouseover);
+ qt_mouseover = enterWidget;
+ }
+
+ qt_event_request_window_change(q);
+ deactivateWidgetCleanup();
+ qt_mac_event_release(q);
+}
+
+void QWidget::setWindowState(Qt::WindowStates newstate)
+{
+ Q_D(QWidget);
+ bool needShow = false;
+ Qt::WindowStates oldstate = windowState();
+ if (oldstate == newstate)
+ return;
+
+#ifdef QT_MAC_USE_COCOA
+ QMacCocoaAutoReleasePool pool;
+#endif
+ bool needSendStateChange = true;
+ if(isWindow()) {
+ if((oldstate & Qt::WindowFullScreen) != (newstate & Qt::WindowFullScreen)) {
+ if(newstate & Qt::WindowFullScreen) {
+ if(QTLWExtra *tlextra = d->topData()) {
+ if(tlextra->normalGeometry.width() < 0) {
+ if(!testAttribute(Qt::WA_Resized))
+ adjustSize();
+ tlextra->normalGeometry = geometry();
+ }
+ tlextra->savedFlags = windowFlags();
+ }
+ needShow = isVisible();
+ const QRect fullscreen(qApp->desktop()->screenGeometry(qApp->desktop()->screenNumber(this)));
+ setParent(parentWidget(), Qt::Window | Qt::FramelessWindowHint | (windowFlags() & 0xffff0000)); //save
+ setGeometry(fullscreen);
+ if(!qApp->desktop()->screenNumber(this))
+ qt_mac_set_fullscreen_mode(true);
+ } else {
+ needShow = isVisible();
+ setParent(parentWidget(), d->topData()->savedFlags);
+ setGeometry(d->topData()->normalGeometry);
+ if(!qApp->desktop()->screenNumber(this))
+ qt_mac_set_fullscreen_mode(false);
+ d->topData()->normalGeometry.setRect(0, 0, -1, -1);
+ }
+ }
+
+ d->createWinId();
+
+ OSWindowRef window = qt_mac_window_for(this);
+ if((oldstate & Qt::WindowMinimized) != (newstate & Qt::WindowMinimized)) {
+ if (newstate & Qt::WindowMinimized) {
+#ifndef QT_MAC_USE_COCOA
+ CollapseWindow(window, true);
+#else
+ [window miniaturize:window];
+#endif
+ } else {
+#ifndef QT_MAC_USE_COCOA
+ CollapseWindow(window, false);
+#else
+ [window deminiaturize:window];
+#endif
+ }
+ needSendStateChange = oldstate == windowState(); // Collapse didn't change our flags.
+ }
+
+ if((newstate & Qt::WindowMaximized) && !((newstate & Qt::WindowFullScreen))) {
+ if(QTLWExtra *tlextra = d->topData()) {
+ if(tlextra->normalGeometry.width() < 0) {
+ if(!testAttribute(Qt::WA_Resized))
+ adjustSize();
+ tlextra->normalGeometry = geometry();
+ }
+ }
+ } else if(!(newstate & Qt::WindowFullScreen)) {
+// d->topData()->normalGeometry = QRect(0, 0, -1, -1);
+ }
+
+#ifdef DEBUG_WINDOW_STATE
+#define WSTATE(x) qDebug("%s -- %s --> %s", #x, (oldstate & x) ? "true" : "false", (newstate & x) ? "true" : "false")
+ WSTATE(Qt::WindowMinimized);
+ WSTATE(Qt::WindowMaximized);
+ WSTATE(Qt::WindowFullScreen);
+#undef WSTATE
+#endif
+ if(!(newstate & (Qt::WindowMinimized|Qt::WindowFullScreen)) &&
+ ((oldstate & Qt::WindowFullScreen) || (oldstate & Qt::WindowMinimized) ||
+ (oldstate & Qt::WindowMaximized) != (newstate & Qt::WindowMaximized))) {
+ if(newstate & Qt::WindowMaximized) {
+ data->fstrut_dirty = true;
+#ifndef QT_MAC_USE_COCOA
+ HIToolbarRef toolbarRef;
+ if (GetWindowToolbar(window, &toolbarRef) == noErr && toolbarRef
+ && !isVisible() && !IsWindowToolbarVisible(window)) {
+ // HIToolbar, needs to be shown so that it's in the structure window
+ // Typically this is part of a main window and will get shown
+ // during the show, but it's will make the maximize all wrong.
+ ShowHideWindowToolbar(window, true, false);
+ d->updateFrameStrut(); // In theory the dirty would work, but it's optimized out if the window is not visible :(
+ }
+ Rect bounds;
+ QDesktopWidget *dsk = QApplication::desktop();
+ QRect avail = dsk->availableGeometry(dsk->screenNumber(this));
+ SetRect(&bounds, avail.x(), avail.y(), avail.x() + avail.width(), avail.y() + avail.height());
+ if(QWExtra *extra = d->extraData()) {
+ if(bounds.right - bounds.left > extra->maxw)
+ bounds.right = bounds.left + extra->maxw;
+ if(bounds.bottom - bounds.top > extra->maxh)
+ bounds.bottom = bounds.top + extra->maxh;
+ }
+ if(d->topData()) {
+ QRect fs = d->frameStrut();
+ bounds.left += fs.left();
+ if(bounds.right < avail.x()+avail.width())
+ bounds.right = qMin<short>((uint)avail.x()+avail.width(), bounds.right+fs.left());
+ if(bounds.bottom < avail.y()+avail.height())
+ bounds.bottom = qMin<short>((uint)avail.y()+avail.height(), bounds.bottom+fs.top());
+ bounds.top += fs.top();
+ bounds.right -= fs.right();
+ bounds.bottom -= fs.bottom();
+ }
+ QRect orect(geometry().x(), geometry().y(), width(), height()),
+ nrect(bounds.left, bounds.top, bounds.right - bounds.left,
+ bounds.bottom - bounds.top);
+ if(orect != nrect) { // the new rect differ from the old
+ Point idealSize = { nrect.height(), nrect.width() };
+ ZoomWindowIdeal(window, inZoomOut, &idealSize);
+ }
+#else
+ NSToolbar *toolbarRef = [window toolbar];
+ if (toolbarRef && !isVisible() && ![toolbarRef isVisible]) {
+ // HIToolbar, needs to be shown so that it's in the structure window
+ // Typically this is part of a main window and will get shown
+ // during the show, but it's will make the maximize all wrong.
+ // ### Not sure this is right for NSToolbar...
+ [toolbarRef setVisible:true];
+// ShowHideWindowToolbar(window, true, false);
+ d->updateFrameStrut(); // In theory the dirty would work, but it's optimized out if the window is not visible :(
+ }
+ // Everything should be handled by Cocoa.
+ [window zoom:window];
+#endif
+ needSendStateChange = oldstate == windowState(); // Zoom didn't change flags.
+ } else if(oldstate & Qt::WindowMaximized) {
+#ifndef QT_MAC_USE_COCOA
+ Point idealSize;
+ ZoomWindowIdeal(window, inZoomIn, &idealSize);
+#else
+ [window zoom:window];
+#endif
+ if(QTLWExtra *tlextra = d->topData()) {
+ setGeometry(tlextra->normalGeometry);
+ tlextra->normalGeometry.setRect(0, 0, -1, -1);
+ }
+ }
+ }
+ }
+
+ data->window_state = newstate;
+
+ if(needShow)
+ show();
+
+ if(newstate & Qt::WindowActive)
+ activateWindow();
+
+ qt_event_request_window_change(this);
+ if (needSendStateChange) {
+ QWindowStateChangeEvent e(oldstate);
+ QApplication::sendEvent(this, &e);
+ }
+}
+
+void QWidgetPrivate::setFocus_sys()
+{
+ Q_Q(QWidget);
+ if (q->testAttribute(Qt::WA_WState_Created)) {
+#ifdef QT_MAC_USE_COCOA
+ QMacCocoaAutoReleasePool pool;
+ NSView *view = qt_mac_nativeview_for(q);
+ [[view window] makeFirstResponder:view];
+#else
+ SetKeyboardFocus(qt_mac_window_for(q), qt_mac_nativeview_for(q), 1);
+#endif
+ }
+}
+
+void QWidgetPrivate::raise_sys()
+{
+ Q_Q(QWidget);
+ if((q->windowType() == Qt::Desktop))
+ return;
+
+#if QT_MAC_USE_COCOA
+ QMacCocoaAutoReleasePool pool;
+ if (isRealWindow()) {
+ // Calling orderFront shows the window on Cocoa too.
+ if (!q->testAttribute(Qt::WA_DontShowOnScreen)) {
+ [qt_mac_window_for(q) orderFront:qt_mac_window_for(q)];
+ }
+ if (qt_mac_raise_process) { //we get to be the active process now
+ ProcessSerialNumber psn;
+ GetCurrentProcess(&psn);
+ SetFrontProcessWithOptions(&psn, kSetFrontProcessFrontWindowOnly);
+ }
+ } else {
+ // Cocoa doesn't really have an idea of Z-ordering, but you can
+ // fake it by changing the order of it.
+ NSView *view = qt_mac_nativeview_for(q);
+ NSView *parentView = [view superview];
+ [view removeFromSuperview];
+ [parentView addSubview:view];
+ }
+#else
+ if(q->isWindow()) {
+ //raise this window
+ BringToFront(qt_mac_window_for(q));
+ if(qt_mac_raise_process) { //we get to be the active process now
+ ProcessSerialNumber psn;
+ GetCurrentProcess(&psn);
+ SetFrontProcessWithOptions(&psn, kSetFrontProcessFrontWindowOnly);
+ }
+ } else if(q->parentWidget()) {
+ HIViewSetZOrder(qt_mac_nativeview_for(q), kHIViewZOrderAbove, 0);
+ qt_event_request_window_change(q);
+ }
+#endif
+}
+
+void QWidgetPrivate::lower_sys()
+{
+ Q_Q(QWidget);
+ if((q->windowType() == Qt::Desktop))
+ return;
+#ifdef QT_MAC_USE_COCOA
+ QMacCocoaAutoReleasePool pool;
+ if (isRealWindow()) {
+ OSWindowRef window = qt_mac_window_for(q);
+ [window orderBack:window];
+ } else {
+ // Cocoa doesn't really have an idea of Z-ordering, but you can
+ // fake it by changing the order of it. In this case
+ // we put the item at the beginning of the list, but that means
+ // we must re-insert everything since we cannot modify the list directly.
+ NSView *myview = qt_mac_nativeview_for(q);
+ NSView *parentView = [myview superview];
+ NSArray *tmpViews = [parentView subviews];
+ NSMutableArray *subviews = [[NSMutableArray alloc] initWithCapacity:[tmpViews count]];
+ [subviews addObjectsFromArray:tmpViews];
+ // Implicit assumption that myViewIndex is included in subviews, that's why I'm not checking
+ // myViewIndex.
+ NSUInteger index = 0;
+ NSUInteger myViewIndex = 0;
+ bool foundMyView = false;
+ for (NSView *subview in subviews) {
+ [subview removeFromSuperview];
+ if (subview == myview) {
+ foundMyView = true;
+ myViewIndex = index;
+ }
+ ++index;
+ }
+ [parentView addSubview:myview];
+ if (foundMyView)
+ [subviews removeObjectAtIndex:myViewIndex];
+ for (NSView *subview in subviews)
+ [parentView addSubview:subview];
+ [subviews release];
+ }
+#else
+ if(q->isWindow()) {
+ SendBehind(qt_mac_window_for(q), 0);
+ } else if(q->parentWidget()) {
+ invalidateBuffer(q->rect());
+ HIViewSetZOrder(qt_mac_nativeview_for(q), kHIViewZOrderBelow, 0);
+ qt_event_request_window_change(q);
+ }
+#endif
+}
+
+void QWidgetPrivate::stackUnder_sys(QWidget *w)
+{
+ // stackUnder
+ Q_Q(QWidget);
+ if(!w || q->isWindow() || (q->windowType() == Qt::Desktop))
+ return;
+#ifdef QT_MAC_USE_COCOA
+ // Do the same trick as lower_sys() and put this widget before the widget passed in.
+ QMacCocoaAutoReleasePool pool;
+ NSView *myview = qt_mac_nativeview_for(q);
+ NSView *wView = qt_mac_nativeview_for(w);
+ NSView *parentView = [myview superview];
+ NSArray *tmpViews = [parentView subviews];
+ NSMutableArray *subviews = [[NSMutableArray alloc] initWithCapacity:[tmpViews count]];
+ [subviews addObjectsFromArray:tmpViews];
+ // Implicit assumption that myViewIndex and wViewIndex is included in subviews,
+ // that's why I'm not checking myViewIndex.
+ NSUInteger index = 0;
+ NSUInteger myViewIndex = 0;
+ NSUInteger wViewIndex = 0;
+ for (NSView *subview in subviews) {
+ [subview removeFromSuperview];
+ if (subview == myview)
+ myViewIndex = index;
+ else if (subview == wView)
+ wViewIndex = index;
+ ++index;
+ }
+
+ index = 0;
+ for (NSView *subview in subviews) {
+ if (index == myViewIndex)
+ continue;
+ if (index == wViewIndex)
+ [parentView addSubview:myview];
+ [parentView addSubview:subview];
+ ++index;
+ }
+ [subviews release];
+#else
+ QWidget *p = q->parentWidget();
+ if(!p || p != w->parentWidget())
+ return;
+ invalidateBuffer(q->rect());
+ HIViewSetZOrder(qt_mac_nativeview_for(q), kHIViewZOrderBelow, qt_mac_nativeview_for(w));
+ qt_event_request_window_change(q);
+#endif
+}
+
+/*
+ Modifies the bounds for a widgets backing HIView during moves and resizes. Also updates the
+ widget, either by scrolling its contents or repainting, depending on the WA_StaticContents
+ and QWidgetPrivate::isOpaque flags.
+*/
+static void qt_mac_update_widget_posisiton(QWidget *q, QRect oldRect, QRect newRect)
+{
+#ifndef QT_MAC_USE_COCOA
+ HIRect bounds = CGRectMake(newRect.x(), newRect.y(),
+ newRect.width(), newRect.height());
+
+ const HIViewRef view = qt_mac_nativeview_for(q);
+ const bool isMove = (oldRect.topLeft() != newRect.topLeft());
+ const bool isResize = (oldRect.size() != newRect.size());
+
+// qDebug() << oldRect << newRect << isMove << isResize << q->testAttribute(Qt::WA_OpaquePaintEvent) << q->testAttribute(Qt::WA_StaticContents);
+ QWidgetPrivate *qd = qt_widget_private(q);
+
+ // Perform a normal (complete repaint) update in some cases:
+ if (
+ // move-by-scroll requires QWidgetPrivate::isOpaque set
+ (isMove && qd->isOpaque == false) ||
+
+ // limited update on resize requires WA_StaticContents.
+ (isResize && q->testAttribute(Qt::WA_StaticContents) == false) ||
+
+ // one of the rects are invalid
+ (oldRect.isValid() == false || newRect.isValid() == false) ||
+
+ // the position update is a part of a drag-and-drop operation
+ QDragManager::self()->object
+ ){
+ HIViewSetFrame(view, &bounds);
+ return;
+ }
+
+ const int dx = newRect.x() - oldRect.x();
+ const int dy = newRect.y() - oldRect.y();
+
+ if (isMove) {
+ // HIViewScrollRect silently fails if we try to scroll anything under the grow box.
+ // Check if there's one present within the widget rect, and if there is fall back
+ // to repainting the entire widget.
+ QWidget const * const parentWidget = q->parentWidget();
+ const HIViewRef parentView = qt_mac_nativeview_for(parentWidget);
+ HIViewRef nativeSizeGrip = 0;
+ if (q->testAttribute(Qt::WA_WState_Created))
+ HIViewFindByID(HIViewGetRoot(HIViewGetWindow(HIViewRef(q->winId()))), kHIViewWindowGrowBoxID, &nativeSizeGrip);
+ if (nativeSizeGrip) {
+ QWidget * const window = q->window();
+
+ const int sizeGripSize = 20;
+ const QRect oldWidgetRect = QRect(q->mapTo(window, QPoint(0, 0)), QSize(oldRect.width(), oldRect.height()));
+ const QRect newWidgetRect = QRect(q->mapTo(window, QPoint(0, 0)), QSize(newRect.width(), newRect.height()));
+ const QRect sizeGripRect = QRect(window->rect().bottomRight() - QPoint(sizeGripSize, sizeGripSize),
+ window->rect().bottomRight());
+
+ if (sizeGripRect.intersects(oldWidgetRect) || sizeGripRect.intersects(newWidgetRect)) {
+ HIViewSetFrame(view, &bounds);
+ return;
+ }
+ }
+
+ // Don't scroll anything outside the parent widget rect.
+ const QRect scrollRect = (oldRect | newRect) & parentWidget->rect();
+ const HIRect scrollBounds =
+ CGRectMake(scrollRect.x(), scrollRect.y(), scrollRect.width(), scrollRect.height());
+
+ // We cannot scroll when the widget has a mask as that would
+ // scroll the masked out areas too
+ if (qd->extra && qd->extra->hasMask) {
+ HIViewMoveBy(view, dx, dy);
+ return;
+ }
+
+ OSStatus err = HIViewScrollRect(parentView, &scrollBounds, dx, dy);
+ if (err != noErr) {
+ HIViewSetNeedsDisplay(view, true);
+ qWarning("QWidget: Internal error (%s:%d)", __FILE__, __LINE__);
+ }
+ }
+ // Set the view bounds with drawing disabled to prevent repaints.
+ HIViewSetDrawingEnabled(view, false);
+ HIViewSetFrame(view, &bounds);
+ HIViewSetDrawingEnabled(view, true);
+
+ // Update any newly exposed areas due to resizing.
+ const int startx = oldRect.width();
+ const int stopx = newRect.width();
+ const int starty = oldRect.height();
+ const int stopy = newRect.height();
+
+ const HIRect verticalSlice = CGRectMake(startx, 0, stopx , stopy);
+ HIViewSetNeedsDisplayInRect(view, &verticalSlice, true);
+ const HIRect horizontalSlice = CGRectMake(0, starty, startx, stopy);
+ HIViewSetNeedsDisplayInRect(view, &horizontalSlice, true);
+#else
+ Q_UNUSED(oldRect);
+ NSRect bounds = NSMakeRect(newRect.x(), newRect.y(),
+ newRect.width(), newRect.height());
+ [qt_mac_nativeview_for(q) setFrame:bounds];
+#endif
+}
+
+/*
+ Helper function for non-toplevel widgets. Helps to map Qt's 32bit
+ coordinate system to OS X's 16bit coordinate system.
+
+ Sets the geometry of the widget to data.crect, but clipped to sizes
+ that OS X can handle. Unmaps widgets that are completely outside the
+ valid range.
+
+ Maintains data.wrect, which is the geometry of the OS X widget,
+ measured in this widget's coordinate system.
+
+ if the parent is not clipped, parentWRect is empty, otherwise
+ parentWRect is the geometry of the parent's OS X rect, measured in
+ parent's coord sys
+*/
+void QWidgetPrivate::setWSGeometry(bool dontShow, const QRect &oldRect)
+{
+ Q_Q(QWidget);
+ Q_ASSERT(q->testAttribute(Qt::WA_WState_Created));
+ Q_UNUSED(oldRect);
+ /*
+ There are up to four different coordinate systems here:
+ Qt coordinate system for this widget.
+ X coordinate system for this widget (relative to wrect).
+ Qt coordinate system for parent
+ X coordinate system for parent (relative to parent's wrect).
+ */
+ QRect validRange(-XCOORD_MAX,-XCOORD_MAX, 2*XCOORD_MAX, 2*XCOORD_MAX);
+ QRect wrectRange(-WRECT_MAX,-WRECT_MAX, 2*WRECT_MAX, 2*WRECT_MAX);
+ QRect wrect;
+ //xrect is the X geometry of my X widget. (starts out in parent's Qt coord sys, and ends up in parent's X coord sys)
+ QRect xrect = data.crect;
+
+ QRect parentWRect;
+ if (q->isWindow() && topData()->embedded) {
+#ifndef QT_MAC_USE_COCOA
+ HIViewRef parentView = HIViewGetSuperview(qt_mac_nativeview_for(q));
+#else
+ NSView *parentView = [qt_mac_nativeview_for(q) superview];
+#endif
+ if (parentView) {
+#ifndef QT_MAC_USE_COCOA
+ HIRect tmpRect;
+ HIViewGetFrame(parentView, &tmpRect);
+#else
+ NSRect tmpRect = [parentView frame];
+#endif
+ parentWRect = QRect(tmpRect.origin.x, tmpRect.origin.y,
+ tmpRect.size.width, tmpRect.size.height);
+ } else {
+ parentWRect = wrectRange;
+ }
+ } else {
+ parentWRect = q->parentWidget()->data->wrect;
+ }
+
+ if (parentWRect.isValid()) {
+ // parent is clipped, and we have to clip to the same limit as parent
+ if (!parentWRect.contains(xrect)) {
+ xrect &= parentWRect;
+ wrect = xrect;
+ //translate from parent's to my Qt coord sys
+ wrect.translate(-data.crect.topLeft());
+ }
+ //translate from parent's Qt coords to parent's X coords
+ xrect.translate(-parentWRect.topLeft());
+
+ } else {
+ // parent is not clipped, we may or may not have to clip
+
+ if (data.wrect.isValid() && QRect(QPoint(),data.crect.size()).contains(data.wrect)) {
+ // This is where the main optimization is: we are already
+ // clipped, and if our clip is still valid, we can just
+ // move our window, and do not need to move or clip
+ // children
+
+ QRect vrect = xrect & q->parentWidget()->rect();
+ vrect.translate(-data.crect.topLeft()); //the part of me that's visible through parent, in my Qt coords
+ if (data.wrect.contains(vrect)) {
+ xrect = data.wrect;
+ xrect.translate(data.crect.topLeft());
+#ifndef QT_MAC_USE_COCOA
+ HIRect bounds = CGRectMake(xrect.x(), xrect.y(),
+ xrect.width(), xrect.height());
+ HIViewSetFrame(qt_mac_nativeview_for(q), &bounds);
+#else
+ NSRect bounds = NSMakeRect(xrect.x(), xrect.y(),
+ xrect.width(), xrect.height());
+ [qt_mac_nativeview_for(q) setFrame:bounds];
+#endif
+ if (q->testAttribute(Qt::WA_OutsideWSRange)) {
+ q->setAttribute(Qt::WA_OutsideWSRange, false);
+ if (!dontShow) {
+ q->setAttribute(Qt::WA_Mapped);
+#ifndef QT_MAC_USE_COCOA
+ HIViewSetVisible(qt_mac_nativeview_for(q), true);
+#else
+ [qt_mac_nativeview_for(q) setHidden:NO];
+#endif
+ }
+ }
+ return;
+ }
+ }
+
+ if (!validRange.contains(xrect)) {
+ // we are too big, and must clip
+ xrect &=wrectRange;
+ wrect = xrect;
+ wrect.translate(-data.crect.topLeft());
+ //parent's X coord system is equal to parent's Qt coord
+ //sys, so we don't need to map xrect.
+ }
+
+ }
+
+ // unmap if we are outside the valid window system coord system
+ bool outsideRange = !xrect.isValid();
+ bool mapWindow = false;
+ if (q->testAttribute(Qt::WA_OutsideWSRange) != outsideRange) {
+ q->setAttribute(Qt::WA_OutsideWSRange, outsideRange);
+ if (outsideRange) {
+#ifndef QT_MAC_USE_COCOA
+ HIViewSetVisible(qt_mac_nativeview_for(q), false);
+#else
+ [qt_mac_nativeview_for(q) setHidden:YES];
+#endif
+ q->setAttribute(Qt::WA_Mapped, false);
+ } else if (!q->isHidden()) {
+ mapWindow = true;
+ }
+ }
+
+ if (outsideRange)
+ return;
+
+ bool jump = (data.wrect != wrect);
+ data.wrect = wrect;
+
+
+ // and now recursively for all children...
+ // ### can be optimized
+ for (int i = 0; i < children.size(); ++i) {
+ QObject *object = children.at(i);
+ if (object->isWidgetType()) {
+ QWidget *w = static_cast<QWidget *>(object);
+ if (!w->isWindow() && w->testAttribute(Qt::WA_WState_Created))
+ w->d_func()->setWSGeometry();
+ }
+ }
+
+ qt_mac_update_widget_posisiton(q, oldRect, xrect);
+
+ if (jump) {
+ updateSystemBackground();
+ q->update();
+ }
+ if (mapWindow && !dontShow) {
+ q->setAttribute(Qt::WA_Mapped);
+#ifndef QT_MAC_USE_COCOA
+ HIViewSetVisible(qt_mac_nativeview_for(q), true);
+#else
+ [qt_mac_nativeview_for(q) setHidden:NO];
+#endif
+ }
+}
+
+void QWidgetPrivate::setGeometry_sys(int x, int y, int w, int h, bool isMove)
+{
+ Q_Q(QWidget);
+ Q_ASSERT(q->testAttribute(Qt::WA_WState_Created));
+
+ if(q->windowType() == Qt::Desktop)
+ return;
+
+ QMacCocoaAutoReleasePool pool;
+ bool realWindow = isRealWindow();
+ if (realWindow && !(w == 0 && h == 0) && !q->testAttribute(Qt::WA_DontShowOnScreen)) {
+ topData()->isSetGeometry = 1;
+ topData()->isMove = isMove;
+#ifndef QT_MAC_USE_COCOA
+ Rect r; SetRect(&r, x, y, x + w, y + h);
+ SetWindowBounds(qt_mac_window_for(q), kWindowContentRgn, &r);
+#else
+ NSWindow *window = qt_mac_window_for(q);
+ const QRect &fStrut = frameStrut();
+ const QRect frameRect(QPoint(x - fStrut.left(), y - fStrut.top()),
+ QSize(fStrut.left() + fStrut.right() + w,
+ fStrut.top() + fStrut.bottom() + h));
+ NSRect cocoaFrameRect = NSMakeRect(frameRect.x(), flipYCoordinate(frameRect.bottom() + 1),
+ frameRect.width(), frameRect.height());
+ [window setFrame:cocoaFrameRect display:NO];
+#endif
+ topData()->isSetGeometry = 0;
+ } else {
+ setGeometry_sys_helper(x, y, w, h, isMove);
+ }
+}
+
+void QWidgetPrivate::setGeometry_sys_helper(int x, int y, int w, int h, bool isMove)
+{
+ Q_Q(QWidget);
+ bool realWindow = isRealWindow();
+ if(QWExtra *extra = extraData()) { // any size restrictions?
+ if(realWindow) {
+ qt_mac_update_sizer(q);
+ if(q->windowFlags() & Qt::WindowMaximizeButtonHint) {
+#ifndef QT_MAC_USE_COCOA
+ OSWindowRef window = qt_mac_window_for(q);
+ if(extra->maxw && extra->maxh && extra->maxw == extra->minw
+ && extra->maxh == extra->minh) {
+ ChangeWindowAttributes(window, kWindowNoAttributes, kWindowFullZoomAttribute);
+ } else {
+ ChangeWindowAttributes(window, kWindowFullZoomAttribute, kWindowNoAttributes);
+ }
+#endif
+ }
+ }
+
+ w = qMin(w,extra->maxw);
+ h = qMin(h,extra->maxh);
+ w = qMax(w,extra->minw);
+ h = qMax(h,extra->minh);
+
+ // Deal with size increment
+ if(QTLWExtra *top = topData()) {
+ if(top->incw) {
+ w = w/top->incw;
+ w *= top->incw;
+ }
+ if(top->inch) {
+ h = h/top->inch;
+ h *= top->inch;
+ }
+ }
+ }
+
+ if (realWindow) {
+ w = qMax(0, w);
+ h = qMax(0, h);
+ }
+
+ QPoint oldp = q->pos();
+ QSize olds = q->size();
+ const bool isResize = (olds != QSize(w, h));
+ if(!realWindow && !isResize && QPoint(x, y) == oldp)
+ return;
+ if(isResize && q->isMaximized())
+ data.window_state = data.window_state & ~Qt::WindowMaximized;
+ const bool visible = q->isVisible();
+ data.crect = QRect(x, y, w, h);
+
+ if(realWindow) {
+ if(QWExtra *extra = extraData()) { //set constraints
+ const float max_f(20000);
+#ifndef QT_MAC_USE_COCOA
+#define SF(x) ((x > max_f) ? max_f : x)
+ HISize max = CGSizeMake(SF(extra->maxw), SF(extra->maxh));
+ HISize min = CGSizeMake(SF(extra->minw), SF(extra->minh));
+#undef SF
+ SetWindowResizeLimits(qt_mac_window_for(q), &min, &max);
+#else
+#define SF(x) ((x > max_f) ? max_f : x)
+ NSSize max = NSMakeSize(SF(extra->maxw), SF(extra->maxh));
+ NSSize min = NSMakeSize(SF(extra->minw), SF(extra->minh));
+#undef SF
+ [qt_mac_window_for(q) setMinSize:min];
+ [qt_mac_window_for(q) setMaxSize:max];
+#endif
+ }
+#ifndef QT_MAC_USE_COCOA
+ HIRect bounds = CGRectMake(0, 0, w, h);
+ HIViewSetFrame(qt_mac_nativeview_for(q), &bounds);
+#else
+ [qt_mac_nativeview_for(q) setFrame:NSMakeRect(0, 0, w, h)];
+#endif
+ } else {
+ const QRect oldRect(oldp, olds);
+ if (!isResize && QApplicationPrivate::graphicsSystem())
+ moveRect(oldRect, x - oldp.x(), y - oldp.y());
+ setWSGeometry(false, oldRect);
+ if (isResize && QApplicationPrivate::graphicsSystem()) {
+ invalidateBuffer(q->rect());
+ if (extra && !extra->mask.isEmpty()) {
+ QRegion oldRegion(extra->mask.translated(oldp));
+ oldRegion &= oldRect;
+ q->parentWidget()->d_func()->invalidateBuffer(oldRegion);
+ } else {
+ q->parentWidget()->d_func()->invalidateBuffer(oldRect);
+ }
+ }
+ }
+
+ if(isMove || isResize) {
+ if(!visible) {
+ if(isMove && q->pos() != oldp)
+ q->setAttribute(Qt::WA_PendingMoveEvent, true);
+ if(isResize)
+ q->setAttribute(Qt::WA_PendingResizeEvent, true);
+ } else {
+ if(isResize) { //send the resize event..
+ QResizeEvent e(q->size(), olds);
+ QApplication::sendEvent(q, &e);
+ }
+ if(isMove && q->pos() != oldp) { //send the move event..
+ QMoveEvent e(q->pos(), oldp);
+ QApplication::sendEvent(q, &e);
+ }
+ }
+ }
+ qt_event_request_window_change(q);
+}
+
+void QWidgetPrivate::setConstraints_sys()
+{
+ updateMaximizeButton_sys();
+}
+
+void QWidgetPrivate::updateMaximizeButton_sys()
+{
+ Q_Q(QWidget);
+ if (q->data->window_flags & Qt::CustomizeWindowHint)
+ return;
+
+ OSWindowRef window = qt_mac_window_for(q);
+ QTLWExtra * tlwExtra = topData();
+#ifdef QT_MAC_USE_COCOA
+ QMacCocoaAutoReleasePool pool;
+ NSButton *maximizeButton = [window standardWindowButton:NSWindowZoomButton];
+#endif
+ if (extra->maxw && extra->maxh
+ && extra->maxw == extra->minw
+ && extra->maxh == extra->minh) {
+ // The window has a fixed size, so gray out the maximize button:
+ if (!tlwExtra->savedWindowAttributesFromMaximized) {
+#ifndef QT_MAC_USE_COCOA
+ GetWindowAttributes(window,
+ (WindowAttributes*)&extra->topextra->savedWindowAttributesFromMaximized);
+
+#else
+ tlwExtra->savedWindowAttributesFromMaximized = (![maximizeButton isHidden] && [maximizeButton isEnabled]);
+#endif
+ }
+#ifndef QT_MAC_USE_COCOA
+ ChangeWindowAttributes(window, kWindowNoAttributes, kWindowFullZoomAttribute);
+#else
+ [maximizeButton setEnabled:NO];
+#endif
+
+
+ } else {
+ if (tlwExtra->savedWindowAttributesFromMaximized) {
+#ifndef QT_MAC_USE_COCOA
+ ChangeWindowAttributes(window,
+ extra->topextra->savedWindowAttributesFromMaximized,
+ kWindowNoAttributes);
+#else
+ [maximizeButton setEnabled:YES];
+#endif
+ tlwExtra->savedWindowAttributesFromMaximized = 0;
+ }
+ }
+
+
+}
+
+void QWidgetPrivate::scroll_sys(int dx, int dy)
+{
+ if (QApplicationPrivate::graphicsSystem() && !paintOnScreen()) {
+ scrollChildren(dx, dy);
+ scrollRect(q_func()->rect(), dx, dy);
+ } else {
+ scroll_sys(dx, dy, QRect());
+ }
+}
+
+void QWidgetPrivate::scroll_sys(int dx, int dy, const QRect &r)
+{
+ Q_Q(QWidget);
+
+ if (QApplicationPrivate::graphicsSystem() && !paintOnScreen()) {
+ scrollRect(r, dx, dy);
+ return;
+ }
+
+ const bool valid_rect = r.isValid();
+ if (!q->updatesEnabled() && (valid_rect || q->children().isEmpty()))
+ return;
+
+ qt_event_request_window_change(q);
+
+#ifdef QT_MAC_USE_COCOA
+ QMacCocoaAutoReleasePool pool;
+#endif
+
+ if(!valid_rect) { // scroll children
+ QPoint pd(dx, dy);
+ QWidgetList moved;
+ QObjectList chldrn = q->children();
+ for(int i = 0; i < chldrn.size(); i++) { //first move all children
+ QObject *obj = chldrn.at(i);
+ if(obj->isWidgetType()) {
+ QWidget *w = (QWidget*)obj;
+ if(!w->isWindow()) {
+ w->data->crect = QRect(w->pos() + pd, w->size());
+ if (w->testAttribute(Qt::WA_WState_Created)) {
+#ifndef QT_MAC_USE_COCOA
+ HIRect bounds = CGRectMake(w->data->crect.x(), w->data->crect.y(),
+ w->data->crect.width(), w->data->crect.height());
+ HIViewRef hiview = qt_mac_nativeview_for(w);
+ const bool opaque = qt_widget_private(w)->isOpaque;
+
+ if (opaque)
+ HIViewSetDrawingEnabled(hiview, false);
+ HIViewSetFrame(hiview, &bounds);
+ if (opaque)
+ HIViewSetDrawingEnabled(hiview, true);
+#else
+ [qt_mac_nativeview_for(w)
+ setFrame:NSMakeRect(w->data->crect.x(), w->data->crect.y(),
+ w->data->crect.width(), w->data->crect.height())];
+#endif
+ }
+ moved.append(w);
+ }
+ }
+ }
+ //now send move events (do not do this in the above loop, breaks QAquaFocusWidget)
+ for(int i = 0; i < moved.size(); i++) {
+ QWidget *w = moved.at(i);
+ QMoveEvent e(w->pos(), w->pos() - pd);
+ QApplication::sendEvent(w, &e);
+ }
+ }
+
+ if (!q->testAttribute(Qt::WA_WState_Created) || !q->isVisible())
+ return;
+
+ OSViewRef view = qt_mac_nativeview_for(q);
+#ifndef QT_MAC_USE_COCOA
+ HIRect scrollrect = CGRectMake(r.x(), r.y(), r.width(), r.height());
+# if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4
+ if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_4) {
+ OSStatus err = _HIViewScrollRectWithOptions(view, valid_rect ? &scrollrect : 0, dx, dy, kHIViewScrollRectAdjustInvalid);
+ if (err) {
+ // The only parameter that can go wrong, is the rect.
+ qWarning("QWidget::scroll: Your rectangle was too big for the widget, clipping rect");
+ scrollrect = CGRectMake(qMax(r.x(), 0), qMax(r.y(), 0),
+ qMin(r.width(), q->width()), qMin(r.height(), q->height()));
+ _HIViewScrollRectWithOptions(view, valid_rect ? &scrollrect : 0, dx, dy, kHIViewScrollRectAdjustInvalid);
+ }
+ } else {
+ if (HIViewGetNeedsDisplay(view)) {
+ q->update(valid_rect ? r : q->rect());
+ return;
+ }
+ HIRect scrollrect = CGRectMake(r.x(), r.y(), r.width(), r.height());
+ OSStatus err = HIViewScrollRect(view, valid_rect ? &scrollrect : 0, dx, dy);
+ if (err) {
+ // The only parameter that can go wrong, is the rect.
+ qWarning("QWidget::scroll: Your rectangle was too big for the widget, clipping rect");
+ scrollrect = CGRectMake(qMax(r.x(), 0), qMax(r.y(), 0),
+ qMin(r.width(), q->width()), qMin(r.height(), q->height()));
+ HIViewScrollRect(view, valid_rect ? &scrollrect : 0, dx, dy);
+ }
+ }
+# endif
+#else
+ NSRect scrollRect = valid_rect ? NSMakeRect(r.x(), r.y(), r.width(), r.height())
+ : NSMakeRect(0, 0, q->width(), q->height());
+
+
+ // calc the updateRect
+ NSRect deltaXRect = { {0, 0}, {0, 0} };
+ NSRect deltaYRect = { {0, 0}, {0, 0} };
+ if (dy != 0) {
+ deltaYRect.size.width = scrollRect.size.width;
+ if (dy > 0) {
+ deltaYRect.size.height = dy;
+ } else {
+ deltaYRect.size.height = -dy;
+ deltaYRect.origin.y = scrollRect.size.height + dy;
+ }
+ }
+ if (dx != 0) {
+ deltaXRect.size.height = scrollRect.size.height;
+ if (dx > 0) {
+ deltaXRect.size.width = dx;
+ } else {
+ deltaXRect.size.width = -dx;
+ deltaXRect.origin.x = scrollRect.size.width + dx;
+ }
+ }
+
+ // ### Scroll the dirty regions as well, the following is not correct.
+ QRegion displayRegion = r.isNull() ? dirtyOnWidget : (dirtyOnWidget & r);
+ const QVector<QRect> &rects = dirtyOnWidget.rects();
+ const QVector<QRect>::const_iterator end = rects.end();
+ QVector<QRect>::const_iterator it = rects.begin();
+ while (it != end) {
+ const QRect rect = *it;
+ const NSRect dirtyRect = NSMakeRect(rect.x() + dx, rect.y() + dy,
+ rect.width(), rect.height());
+ [view setNeedsDisplayInRect:dirtyRect];
+ ++it;
+ }
+ [view scrollRect:scrollRect by:NSMakeSize(dx, dy)];
+ // Yes, we potentially send a duplicate area, but I think Cocoa can handle it.
+ [view setNeedsDisplayInRect:deltaXRect];
+ [view setNeedsDisplayInRect:deltaYRect];
+#endif // QT_MAC_USE_COCOA
+}
+
+int QWidget::metric(PaintDeviceMetric m) const
+{
+ switch(m) {
+ case PdmHeightMM:
+ return qRound(metric(PdmHeight) * 25.4 / qreal(metric(PdmDpiY)));
+ case PdmWidthMM:
+ return qRound(metric(PdmWidth) * 25.4 / qreal(metric(PdmDpiX)));
+ case PdmHeight:
+ case PdmWidth: {
+#ifndef QT_MAC_USE_COCOA
+ HIRect rect;
+ HIViewGetFrame(qt_mac_nativeview_for(this), &rect);
+#else
+ NSRect rect = [qt_mac_nativeview_for(this) frame];
+#endif
+ if(m == PdmWidth)
+ return (int)rect.size.width;
+ return (int)rect.size.height; }
+ case PdmDepth:
+ return 32;
+ case PdmNumColors:
+ return INT_MAX;
+ case PdmDpiX:
+ case PdmPhysicalDpiX: {
+ Q_D(const QWidget);
+ if (d->extra && d->extra->customDpiX)
+ return d->extra->customDpiX;
+ else if (d->parent)
+ return static_cast<QWidget *>(d->parent)->metric(m);
+ extern float qt_mac_defaultDpi_x(); //qpaintdevice_mac.cpp
+ return int(qt_mac_defaultDpi_x()); }
+ case PdmDpiY:
+ case PdmPhysicalDpiY: {
+ Q_D(const QWidget);
+ if (d->extra && d->extra->customDpiY)
+ return d->extra->customDpiY;
+ else if (d->parent)
+ return static_cast<QWidget *>(d->parent)->metric(m);
+ extern float qt_mac_defaultDpi_y(); //qpaintdevice_mac.cpp
+ return int(qt_mac_defaultDpi_y()); }
+ default: //leave this so the compiler complains when new ones are added
+ qWarning("QWidget::metric: Unhandled parameter %d", m);
+ return QPaintDevice::metric(m);
+ }
+ return 0;
+}
+
+void QWidgetPrivate::createSysExtra()
+{
+#ifdef QT_MAC_USE_COCOA
+ extra->imageMask = 0;
+#endif
+}
+
+void QWidgetPrivate::deleteSysExtra()
+{
+#ifdef QT_MAC_USE_COCOA
+ if (extra->imageMask)
+ CFRelease(extra->imageMask);
+#endif
+}
+
+void QWidgetPrivate::createTLSysExtra()
+{
+ extra->topextra->wclass = 0;
+ extra->topextra->group = 0;
+ extra->topextra->windowIcon = 0;
+ extra->topextra->resizer = 0;
+ extra->topextra->isSetGeometry = 0;
+ extra->topextra->savedWindowAttributesFromMaximized = 0;
+}
+
+void QWidgetPrivate::deleteTLSysExtra()
+{
+#ifndef QT_MAC_USE_COCOA
+ if(extra->topextra->group) {
+ qt_mac_release_window_group(extra->topextra->group);
+ extra->topextra->group = 0;
+ }
+#endif
+}
+
+void QWidgetPrivate::updateFrameStrut()
+{
+ Q_Q(QWidget);
+
+ QWidgetPrivate *that = const_cast<QWidgetPrivate*>(this);
+
+ that->data.fstrut_dirty = false;
+ QTLWExtra *top = that->topData();
+
+#if QT_MAC_USE_COCOA
+ // 1 Get the window frame
+ OSWindowRef oswnd = qt_mac_window_for(q);
+ NSRect frameW = [oswnd frame];
+ // 2 Get the content frame - so now
+ NSRect frameC = [oswnd contentRectForFrameRect:frameW];
+ top->frameStrut.setCoords(frameC.origin.x - frameW.origin.x,
+ (frameW.origin.y + frameW.size.height) - (frameC.origin.y + frameC.size.height),
+ (frameW.origin.x + frameW.size.width) - (frameC.origin.x + frameC.size.width),
+ frameC.origin.y - frameW.origin.y);
+#else
+ Rect window_r;
+ GetWindowStructureWidths(qt_mac_window_for(q), &window_r);
+ top->frameStrut.setCoords(window_r.left, window_r.top, window_r.right, window_r.bottom);
+#endif
+}
+
+void QWidgetPrivate::registerDropSite(bool on)
+{
+ Q_Q(QWidget);
+ if (!q->testAttribute(Qt::WA_WState_Created))
+ return;
+#ifndef QT_MAC_USE_COCOA
+ SetControlDragTrackingEnabled(qt_mac_nativeview_for(q), on);
+#else
+ NSView *view = qt_mac_nativeview_for(q);
+ if ([view isKindOfClass:[QT_MANGLE_NAMESPACE(QCocoaView) class]]) {
+ [static_cast<QT_MANGLE_NAMESPACE(QCocoaView) *>(view) registerDragTypes:on];
+ }
+#endif
+}
+
+void QWidgetPrivate::setMask_sys(const QRegion &region)
+{
+ Q_UNUSED(region);
+#ifndef QT_MAC_USE_COCOA
+ Q_Q(QWidget);
+ if (q->isWindow())
+ ReshapeCustomWindow(qt_mac_window_for(q));
+ else
+ HIViewReshapeStructure(qt_mac_nativeview_for(q));
+#else
+ if (extra->mask.isEmpty()) {
+ extra->maskBits = QImage();
+ finishCocoaMaskSetup();
+ } else {
+ syncCocoaMask();
+ }
+
+#endif
+}
+
+extern "C" {
+ typedef struct CGSConnection *CGSConnectionRef;
+ typedef struct CGSWindow *CGSWindowRef;
+ extern OSStatus CGSSetWindowAlpha(CGSConnectionRef, CGSWindowRef, float);
+ extern CGSWindowRef GetNativeWindowFromWindowRef(WindowRef);
+ extern CGSConnectionRef _CGSDefaultConnection();
+}
+
+void QWidgetPrivate::setWindowOpacity_sys(qreal level)
+{
+ Q_Q(QWidget);
+
+ if (!q->isWindow())
+ return;
+
+ level = qBound(0.0, level, 1.0);
+ topData()->opacity = (uchar)(level * 255);
+ if (!q->testAttribute(Qt::WA_WState_Created))
+ return;
+
+#if QT_MAC_USE_COCOA
+ OSWindowRef oswindow = qt_mac_window_for(q);
+ [oswindow setAlphaValue:level];
+#else
+ CGSSetWindowAlpha(_CGSDefaultConnection(),
+ GetNativeWindowFromWindowRef(qt_mac_window_for(q)), level);
+#endif
+}
+
+#ifdef QT_MAC_USE_COCOA
+void QWidgetPrivate::syncCocoaMask()
+{
+ Q_Q(QWidget);
+ if (!q->testAttribute(Qt::WA_WState_Created) || !extra)
+ return;
+
+ if (extra->hasMask && extra->maskBits.size() != q->size()) {
+ extra->maskBits = QImage(q->size(), QImage::Format_Mono);
+ extra->maskBits.fill(QColor(Qt::color1).rgba());
+ extra->maskBits.setNumColors(2);
+ extra->maskBits.setColor(0, QColor(Qt::color0).rgba());
+ extra->maskBits.setColor(1, QColor(Qt::color1).rgba());
+ QPainter painter(&extra->maskBits);
+ painter.setBrush(Qt::color1);
+ painter.setPen(Qt::NoPen);
+ painter.drawRects(extra->mask.rects());
+ painter.end();
+ finishCocoaMaskSetup();
+ }
+}
+
+void QWidgetPrivate::finishCocoaMaskSetup()
+{
+ Q_Q(QWidget);
+
+ if (!q->testAttribute(Qt::WA_WState_Created) || !extra)
+ return;
+
+ // Technically this is too late to release, because the data behind the image
+ // has already been released. But it's more tidy to do it here.
+ // If you are seeing a crash, consider doing a CFRelease before changing extra->maskBits.
+ if (extra->imageMask) {
+ CFRelease(extra->imageMask);
+ extra->imageMask = 0;
+ }
+
+ if (!extra->maskBits.isNull()) {
+ QCFType<CGDataProviderRef> dataProvider = CGDataProviderCreateWithData(0,
+ extra->maskBits.bits(),
+ extra->maskBits.numBytes(),
+ 0); // shouldn't need to release.
+ CGFloat decode[2] = {1, 0};
+ extra->imageMask = CGImageMaskCreate(extra->maskBits.width(), extra->maskBits.height(),
+ 1, 1, extra->maskBits.bytesPerLine(), dataProvider,
+ decode, false);
+ }
+ if (q->isWindow()) {
+ NSWindow *window = qt_mac_window_for(q);
+ [window setOpaque:(extra->imageMask == 0)];
+ [window invalidateShadow];
+ }
+ [qt_mac_nativeview_for(q) setNeedsDisplay:YES];
+}
+#endif
+
+struct QPaintEngineCleanupHandler
+{
+ inline QPaintEngineCleanupHandler() : engine(0) {}
+ inline ~QPaintEngineCleanupHandler() { delete engine; }
+ QPaintEngine *engine;
+};
+
+Q_GLOBAL_STATIC(QPaintEngineCleanupHandler, engineHandler)
+
+QPaintEngine *QWidget::paintEngine() const
+{
+ QPaintEngine *&pe = engineHandler()->engine;
+#ifdef QT_RASTER_PAINTENGINE
+ if (!pe) {
+ if(qgetenv("QT_MAC_USE_COREGRAPHICS").isNull())
+ pe = new QRasterPaintEngine();
+ else
+ pe = new QCoreGraphicsPaintEngine();
+ }
+ if (pe->isActive()) {
+ QPaintEngine *engine =
+ qgetenv("QT_MAC_USE_COREGRAPHICS").isNull()
+ ? (QPaintEngine*)new QRasterPaintEngine() : (QPaintEngine*)new QCoreGraphicsPaintEngine();
+ engine->setAutoDestruct(true);
+ return engine;
+ }
+#else
+ if (!pe)
+ pe = new QCoreGraphicsPaintEngine();
+ if (pe->isActive()) {
+ QPaintEngine *engine = new QCoreGraphicsPaintEngine();
+ engine->setAutoDestruct(true);
+ return engine;
+ }
+#endif
+ return pe;
+}
+
+void QWidgetPrivate::setModal_sys()
+{
+ Q_Q(QWidget);
+ if (!q->testAttribute(Qt::WA_WState_Created) || !q->isWindow())
+ return;
+ const QWidget * const windowParent = q->window()->parentWidget();
+ const QWidget * const primaryWindow = windowParent ? windowParent->window() : 0;
+ OSWindowRef windowRef = qt_mac_window_for(q);
+
+#ifdef QT_MAC_USE_COCOA
+ bool windowIsSheet = [windowRef styleMask] & NSDocModalWindowMask;
+
+ if (q->windowModality() == Qt::WindowModal){
+ // Window should be window-modal, which implies a sheet.
+ if (!windowIsSheet)
+ recreateMacWindow();
+ if ([windowRef isKindOfClass:[NSPanel class]]){
+ // If the primary window of the sheet parent is a child of a modal dialog,
+ // the sheet parent should not be modally shaddowed.
+ // This goes for the sheet as well:
+ OSWindowRef ref = primaryWindow ? qt_mac_window_for(primaryWindow) : 0;
+ bool isDialog = ref ? [ref isKindOfClass:[NSPanel class]] : false;
+ bool worksWhenModal = isDialog ? [static_cast<NSPanel *>(ref) worksWhenModal] : false;
+ if (worksWhenModal)
+ [static_cast<NSPanel *>(windowRef) setWorksWhenModal:YES];
+ }
+ } else {
+ // Window shold not be window-modal, and as such, not a sheet.
+ if (windowIsSheet){
+ // NB: the following call will call setModal_sys recursivly:
+ recreateMacWindow();
+ windowRef = qt_mac_window_for(q);
+ }
+ if (q->windowModality() == Qt::ApplicationModal) {
+ [windowRef setLevel:NSModalPanelWindowLevel];
+ } else if (primaryWindow && primaryWindow->windowModality() == Qt::ApplicationModal) {
+ // INVARIANT: Our window is a dialog that has a dialog parent that is
+ // application modal, or . This means that q is supposed to be on top of this
+ // dialog and not be modally shaddowed:
+ [windowRef setLevel:NSModalPanelWindowLevel];
+ if ([windowRef isKindOfClass:[NSPanel class]])
+ [static_cast<NSPanel *>(windowRef) setWorksWhenModal:YES];
+ } else {
+ // INVARIANT: q should not be modal.
+ NSInteger winLevel = -1;
+ if (q->windowType() == Qt::Popup) {
+ winLevel = NSPopUpMenuWindowLevel;
+ // Popup should be in at least the same level as its parent.
+ if (primaryWindow) {
+ OSWindowRef parentRef = qt_mac_window_for(primaryWindow);
+ winLevel = qMax([parentRef level], winLevel);
+ }
+ } else if (q->windowType() == Qt::Tool) {
+ winLevel = NSFloatingWindowLevel;
+ } else if (q->windowType() == Qt::Dialog) {
+ winLevel = NSModalPanelWindowLevel;
+ }
+
+ // StayOnTop window should appear above Tool windows.
+ if (data.window_flags & Qt::WindowStaysOnTopHint)
+ winLevel = NSPopUpMenuWindowLevel;
+ // Tooltips should appear above StayOnTop windows.
+ if (q->windowType() == Qt::ToolTip)
+ winLevel = NSScreenSaverWindowLevel;
+ // All other types are Normal level.
+ if (winLevel == -1)
+ winLevel = NSNormalWindowLevel;
+ [windowRef setLevel:winLevel];
+ }
+ }
+
+#else
+ const bool primaryWindowModal = primaryWindow ? primaryWindow->testAttribute(Qt::WA_ShowModal) : false;
+ const bool modal = q->testAttribute(Qt::WA_ShowModal);
+
+ WindowClass old_wclass;
+ GetWindowClass(windowRef, &old_wclass);
+
+ if (modal || primaryWindowModal) {
+ if (q->windowModality() == Qt::WindowModal
+ || (primaryWindow && primaryWindow->windowModality() == Qt::WindowModal)){
+ // Window should be window-modal (which implies a sheet).
+ if (old_wclass != kSheetWindowClass){
+ // We cannot convert a created window to a sheet. So we recreate the window:
+ recreateMacWindow();
+ return;
+ }
+ } else if (!(q->data->window_flags & Qt::CustomizeWindowHint)) {
+ if (old_wclass == kDocumentWindowClass || old_wclass == kFloatingWindowClass || old_wclass == kUtilityWindowClass){
+ // Only change the class to kMovableModalWindowClass if the no explicit jewels
+ // are set (kMovableModalWindowClass can't contain them), and the current window class
+ // can be converted to modal (according to carbon doc). Mind the order of
+ // HIWindowChangeClass and ChangeWindowAttributes.
+ WindowGroupRef group = GetWindowGroup(windowRef);
+ HIWindowChangeClass(windowRef, kMovableModalWindowClass);
+ quint32 tmpWattr = kWindowCloseBoxAttribute | kWindowHorizontalZoomAttribute;
+ ChangeWindowAttributes(windowRef, tmpWattr, kWindowNoAttributes);
+ ChangeWindowAttributes(windowRef, kWindowNoAttributes, tmpWattr);
+ // If the window belongs to a qt-created group, set that group once more:
+ if (data.window_flags & Qt::WindowStaysOnTopHint
+ || q->windowType() == Qt::Popup
+ || q->windowType() == Qt::ToolTip)
+ SetWindowGroup(windowRef, group);
+ }
+ // Popups are usually handled "special" and are never modal.
+ Qt::WindowType winType = q->windowType();
+ if (winType != Qt::Popup && winType != Qt::ToolTip)
+ SetWindowModality(windowRef, kWindowModalityAppModal, 0);
+ }
+ } else if (windowRef) {
+ if (old_wclass == kSheetWindowClass){
+ // Converting a sheet to a window is complex. It's easier to recreate:
+ recreateMacWindow();
+ return;
+ }
+
+ SetWindowModality(windowRef, kWindowModalityNone, 0);
+ if (!(q->data->window_flags & Qt::CustomizeWindowHint)) {
+ if (q->window()->d_func()->topData()->wattr |= kWindowCloseBoxAttribute)
+ ChangeWindowAttributes(windowRef, kWindowCloseBoxAttribute, kWindowNoAttributes);
+ if (q->window()->d_func()->topData()->wattr |= kWindowHorizontalZoomAttribute)
+ ChangeWindowAttributes(windowRef, kWindowHorizontalZoomAttribute, kWindowNoAttributes);
+ if (q->window()->d_func()->topData()->wattr |= kWindowCollapseBoxAttribute)
+ ChangeWindowAttributes(windowRef, kWindowCollapseBoxAttribute, kWindowNoAttributes);
+ }
+
+ WindowClass newClass = q->window()->d_func()->topData()->wclass;
+ if (old_wclass != newClass && newClass != 0){
+ WindowGroupRef group = GetWindowGroup(windowRef);
+ HIWindowChangeClass(windowRef, newClass);
+ // If the window belongs to a qt-created group, set that group once more:
+ if (data.window_flags & Qt::WindowStaysOnTopHint
+ || q->windowType() == Qt::Popup
+ || q->windowType() == Qt::ToolTip)
+ SetWindowGroup(windowRef, group);
+ }
+ }
+
+ // Make sure that HIWindowChangeClass didn't remove drag support
+ // or reset the opaque size grip setting:
+ SetAutomaticControlDragTrackingEnabledForWindow(windowRef, true);
+ macUpdateOpaqueSizeGrip();
+#endif
+}
+
+void QWidgetPrivate::macUpdateHideOnSuspend()
+{
+ Q_Q(QWidget);
+ if (!q->testAttribute(Qt::WA_WState_Created) || !q->isWindow() || q->windowType() != Qt::Tool)
+ return;
+#ifndef QT_MAC_USE_COCOA
+ if(q->testAttribute(Qt::WA_MacAlwaysShowToolWindow))
+ ChangeWindowAttributes(qt_mac_window_for(q), 0, kWindowHideOnSuspendAttribute);
+ else
+ ChangeWindowAttributes(qt_mac_window_for(q), kWindowHideOnSuspendAttribute, 0);
+#else
+ if(q->testAttribute(Qt::WA_MacAlwaysShowToolWindow))
+ [qt_mac_window_for(q) setHidesOnDeactivate:NO];
+ else
+ [qt_mac_window_for(q) setHidesOnDeactivate:YES];
+#endif
+}
+
+void QWidgetPrivate::macUpdateOpaqueSizeGrip()
+{
+ Q_Q(QWidget);
+
+ if (!q->testAttribute(Qt::WA_WState_Created) || !q->isWindow())
+ return;
+
+#ifndef QT_MAC_USE_COCOA // Growbox is always transparent on Cocoa. Can emulate with setting a QSizeGrip
+ HIViewRef growBox;
+ HIViewFindByID(HIViewGetRoot(qt_mac_window_for(q)), kHIViewWindowGrowBoxID, &growBox);
+ if (!growBox)
+ return;
+ HIGrowBoxViewSetTransparent(growBox, !q->testAttribute(Qt::WA_MacOpaqueSizeGrip));
+#endif
+}
+
+void QWidgetPrivate::macUpdateSizeAttribute()
+{
+ Q_Q(QWidget);
+ QEvent event(QEvent::MacSizeChange);
+ QApplication::sendEvent(q, &event);
+ for (int i = 0; i < children.size(); ++i) {
+ QWidget *w = qobject_cast<QWidget *>(children.at(i));
+ if (w && (!w->isWindow() || w->testAttribute(Qt::WA_WindowPropagation))
+ && !q->testAttribute(Qt::WA_MacMiniSize) // no attribute set? inherit from parent
+ && !w->testAttribute(Qt::WA_MacSmallSize)
+ && !w->testAttribute(Qt::WA_MacNormalSize))
+ w->d_func()->macUpdateSizeAttribute();
+ }
+ resolveFont();
+}
+
+void QWidgetPrivate::macUpdateIgnoreMouseEvents()
+{
+#ifndef QT_MAC_USE_COCOA // This is handled inside the mouse handler on Cocoa.
+ Q_Q(QWidget);
+ if (!q->testAttribute(Qt::WA_WState_Created))
+ return;
+
+ if(q->isWindow())
+ {
+ if(q->testAttribute(Qt::WA_TransparentForMouseEvents))
+ ChangeWindowAttributes(qt_mac_window_for(q), kWindowIgnoreClicksAttribute, 0);
+ else
+ ChangeWindowAttributes(qt_mac_window_for(q), 0, kWindowIgnoreClicksAttribute);
+ ReshapeCustomWindow(qt_mac_window_for(q));
+ } else {
+#ifndef kHIViewFeatureIgnoresClicks
+#define kHIViewFeatureIgnoresClicks kHIViewIgnoresClicks
+#endif
+ if(q->testAttribute(Qt::WA_TransparentForMouseEvents))
+ HIViewChangeFeatures(qt_mac_nativeview_for(q), kHIViewFeatureIgnoresClicks, 0);
+ else
+ HIViewChangeFeatures(qt_mac_nativeview_for(q), 0, kHIViewFeatureIgnoresClicks);
+ HIViewReshapeStructure(qt_mac_nativeview_for(q));
+ }
+#endif
+}
+
+void QWidgetPrivate::macUpdateMetalAttribute()
+{
+ Q_Q(QWidget);
+ bool realWindow = isRealWindow();
+ if (!q->testAttribute(Qt::WA_WState_Created) || !realWindow)
+ return;
+
+ if (realWindow) {
+#if QT_MAC_USE_COCOA
+ // Cocoa doesn't let us change the style mask once it's been changed
+ // So, that means we need to recreate the window.
+ OSWindowRef cocoaWindow = qt_mac_window_for(q);
+ if ([cocoaWindow styleMask] & NSTexturedBackgroundWindowMask)
+ return;
+ recreateMacWindow();
+#else
+ QMainWindowLayout *layout = qobject_cast<QMainWindowLayout *>(q->layout());
+ if (q->testAttribute(Qt::WA_MacBrushedMetal)) {
+ if (layout)
+ layout->updateHIToolBarStatus();
+ ChangeWindowAttributes(qt_mac_window_for(q), kWindowMetalAttribute, 0);
+ if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_4)
+ ChangeWindowAttributes(qt_mac_window_for(q), kWindowMetalNoContentSeparatorAttribute, 0);
+ } else {
+ if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_4)
+ ChangeWindowAttributes(qt_mac_window_for(q), 0, kWindowMetalNoContentSeparatorAttribute);
+ ChangeWindowAttributes(qt_mac_window_for(q), 0, kWindowMetalAttribute);
+ if (layout)
+ layout->updateHIToolBarStatus();
+ }
+#endif
+ }
+}
+
+void QWidgetPrivate::setEnabled_helper_sys(bool enable)
+{
+#ifdef QT_MAC_USE_COCOA
+ Q_Q(QWidget);
+ NSView *view = qt_mac_nativeview_for(q);
+ if ([view isKindOfClass:[NSControl class]])
+ [static_cast<NSControl *>(view) setEnabled:enable];
+#else
+ Q_UNUSED(enable);
+#endif
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/kernel/qwidget_p.h b/src/gui/kernel/qwidget_p.h
new file mode 100644
index 0000000000..f254c4ac73
--- /dev/null
+++ b/src/gui/kernel/qwidget_p.h
@@ -0,0 +1,712 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QWIDGET_P_H
+#define QWIDGET_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of qapplication_*.cpp, qwidget*.cpp and qfiledialog.cpp. This header
+// file may change from version to version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "QtGui/qwidget.h"
+#include "private/qobject_p.h"
+#include "QtCore/qrect.h"
+#include "QtCore/qlocale.h"
+#include "QtGui/qregion.h"
+#include "QtGui/qsizepolicy.h"
+#include "QtGui/qstyle.h"
+
+#ifdef Q_WS_WIN
+#include "QtCore/qt_windows.h"
+#include <private/qdnd_p.h>
+#endif // Q_WS_WIN
+
+#ifdef Q_WS_X11
+#include "QtGui/qx11info_x11.h"
+#endif
+
+#ifdef Q_WS_MAC
+#include <private/qt_mac_p.h>
+#endif
+
+#if defined(Q_WS_QWS)
+#include "QtGui/qinputcontext.h"
+#include "QtGui/qscreen_qws.h"
+#endif
+
+QT_BEGIN_NAMESPACE
+
+// Extra QWidget data
+// - to minimize memory usage for members that are seldom used.
+// - top-level widgets have extra extra data to reduce cost further
+#if defined(Q_WS_QWS)
+class QWSManager;
+#endif
+#if defined(Q_WS_MAC)
+class QCoreGraphicsPaintEnginePrivate;
+#endif
+class QPaintEngine;
+class QPixmap;
+class QWidgetBackingStore;
+class QGraphicsProxyWidget;
+class QWidgetItemV2;
+
+class QStyle;
+
+struct QTLWExtra {
+ QString caption; // widget caption
+ QString iconText; // widget icon text
+ QString role; // widget role
+ QString filePath; // widget file path
+ QIcon *icon; // widget icon
+ QPixmap *iconPixmap;
+ short incw, inch; // size increments
+ // frame strut, don't use these directly, use QWidgetPrivate::frameStrut() instead.
+ QRect frameStrut;
+ uint opacity : 8;
+ uint posFromMove : 1;
+ uint sizeAdjusted : 1;
+ uint inTopLevelResize : 1;
+ uint inRepaint : 1;
+ QWidgetBackingStore *backingStore;
+#if defined(Q_WS_WIN)
+ ulong savedFlags; // Save window flags while showing fullscreen
+ uint embedded : 1; // window is embedded in another application
+#else
+ Qt::WindowFlags savedFlags; // Save widget flags while showing fullscreen
+#endif
+ short basew, baseh; // base sizes
+#if defined(Q_WS_X11)
+ WId parentWinId; // parent window Id (valid after reparenting)
+ uint embedded : 1; // window is embedded in another Qt application
+ uint spont_unmapped: 1; // window was spontaneously unmapped
+ uint dnd : 1; // DND properties installed
+ uint validWMState : 1; // is WM_STATE valid?
+ uint waitingForMapNotify : 1; // show() has been called, haven't got the MapNotify yet
+ WId userTimeWindow; // window id that contains user-time timestamp when WM supports a _NET_WM_USER_TIME_WINDOW atom
+ QPoint fullScreenOffset;
+#endif
+#if defined(Q_WS_MAC)
+ quint32 wattr;
+ quint32 wclass;
+ WindowGroupRef group;
+ IconRef windowIcon; // the current window icon, if set with setWindowIcon_sys.
+ quint32 savedWindowAttributesFromMaximized; // Saved attributes from when the calling updateMaximizeButton_sys()
+ uint resizer : 4;
+ uint isSetGeometry : 1;
+ uint isMove : 1;
+ uint embedded : 1;
+#endif
+#if defined(Q_WS_QWS) && !defined (QT_NO_QWS_MANAGER)
+ QWSManager *qwsManager;
+#endif
+#if defined(Q_WS_WIN)
+ HICON winIconBig; // internal big Windows icon
+ HICON winIconSmall; // internal small Windows icon
+#endif
+ QRect normalGeometry; // used by showMin/maximized/FullScreen
+ QWindowSurface *windowSurface;
+ QPainter *sharedPainter;
+};
+
+struct QWExtra {
+ qint32 minw, minh; // minimum size
+ qint32 maxw, maxh; // maximum size
+ QPointer<QWidget> focus_proxy;
+#ifndef QT_NO_CURSOR
+ QCursor *curs;
+#endif
+ QTLWExtra *topextra; // only useful for TLWs
+ QGraphicsProxyWidget *proxyWidget; // if the widget is embedded
+ void *glContext; // if the widget is hijacked by QGLWindowSurface
+#if defined(Q_WS_WIN) && !defined(QT_NO_DRAGANDDROP)
+ QOleDropTarget *dropTarget; // drop target
+ QList<QPointer<QWidget> > oleDropWidgets;
+#endif
+#if defined(Q_WS_X11)
+ WId xDndProxy; // XDND forwarding to embedded windows
+#endif
+ QRegion mask; // widget mask
+ QSize staticContentsSize;
+
+//bit flags at the end to improve packing
+#if defined(Q_WS_WIN)
+ uint shown_mode : 8; // widget show mode
+#ifndef QT_NO_DIRECT3D
+ uint had_paint_on_screen : 1;
+ uint had_no_system_bg : 1;
+ uint had_auto_fill_bg : 1;
+#endif
+#endif
+#if defined(Q_WS_X11)
+ uint compress_events : 1;
+#endif
+ uint explicitMinSize : 2;
+ uint explicitMaxSize : 2;
+ uint autoFillBackground : 1;
+ uint nativeChildrenForced : 1;
+ uint inRenderWithPainter : 1;
+ uint hasMask : 1;
+
+ QPointer<QStyle> style;
+ QString styleSheet;
+
+ quint16 customDpiX;
+ quint16 customDpiY;
+#if defined(Q_WS_MAC) && defined(QT_MAC_USE_COCOA)
+ // Cocoa Mask stuff
+ QImage maskBits;
+ CGImageRef imageMask;
+#endif
+};
+
+class Q_GUI_EXPORT QWidgetPrivate : public QObjectPrivate
+{
+ Q_DECLARE_PUBLIC(QWidget)
+
+public:
+ explicit QWidgetPrivate(int version = QObjectPrivateVersion);
+ ~QWidgetPrivate();
+
+ QWExtra *extraData() const;
+ QTLWExtra *topData() const;
+ QTLWExtra *maybeTopData() const;
+ QPainter *sharedPainter() const;
+ void setSharedPainter(QPainter *painter);
+ QWidgetBackingStore *maybeBackingStore() const;
+#ifdef Q_WS_QWS
+ void setMaxWindowState_helper();
+ void setFullScreenSize_helper();
+#endif
+ void init(QWidget *desktopWidget, Qt::WindowFlags f);
+ void create_sys(WId window, bool initializeWindow, bool destroyOldWindow);
+ void createRecursively();
+ void createWinId(WId id = 0);
+
+ void createTLExtra();
+ void createExtra();
+ void deleteExtra();
+ void createSysExtra();
+ void deleteSysExtra();
+ void createTLSysExtra();
+ void deleteTLSysExtra();
+ void updateSystemBackground();
+ void propagatePaletteChange();
+
+ void setPalette_helper(const QPalette &);
+ void resolvePalette();
+ QPalette naturalWidgetPalette(uint inheritedMask) const;
+
+ void setMask_sys(const QRegion &);
+#ifdef Q_WS_WIN
+ bool shouldShowMaximizeButton();
+ void winUpdateIsOpaque();
+#endif
+
+#ifdef Q_WS_MAC
+ void macUpdateSizeAttribute();
+ void macUpdateHideOnSuspend();
+ void macUpdateOpaqueSizeGrip();
+ void macUpdateIgnoreMouseEvents();
+ void macUpdateMetalAttribute();
+ void macUpdateIsOpaque();
+ void setEnabled_helper_sys(bool enable);
+ bool isRealWindow() const;
+#endif
+
+ void raise_sys();
+ void lower_sys();
+ void stackUnder_sys(QWidget *);
+
+ void setFocus_sys();
+
+ void updateFont(const QFont &);
+ inline void setFont_helper(const QFont &font) {
+ if (data.fnt == font && data.fnt.resolve() == font.resolve())
+ return;
+ updateFont(font);
+ }
+ void resolveFont();
+ QFont naturalWidgetFont(uint inheritedMask) const;
+
+ void setLayoutDirection_helper(Qt::LayoutDirection);
+ void resolveLayoutDirection();
+
+ void setLocale_helper(const QLocale &l, bool forceUpdate = false);
+ void resolveLocale();
+
+ void setStyle_helper(QStyle *newStyle, bool propagate, bool metalHack = false);
+ void inheritStyle();
+
+ bool isBackgroundInherited() const;
+
+ void setUpdatesEnabled_helper(bool );
+
+ void paintBackground(QPainter *, const QRegion &, const QPoint & = QPoint(), int flags = DrawAsRoot) const;
+ enum DrawWidgetFlags {
+ DrawAsRoot = 0x01,
+ DrawPaintOnScreen = 0x02,
+ DrawRecursive = 0x04,
+ DrawInvisible = 0x08,
+ DontSubtractOpaqueChildren = 0x10,
+ DontSetCompositionMode = 0x20,
+ DontDrawOpaqueChildren = 0x40
+ };
+ bool isAboutToShow() const;
+ QRegion prepareToRender(const QRegion &region, QWidget::RenderFlags renderFlags);
+ void render_helper(QPainter *painter, const QPoint &targetOffset, const QRegion &sourceRegion,
+ QWidget::RenderFlags renderFlags);
+ void drawWidget(QPaintDevice *pdev, const QRegion &rgn, const QPoint &offset, int flags,
+ QPainter *sharedPainter = 0, QWidgetBackingStore *backingStore = 0);
+
+
+ void paintSiblingsRecursive(QPaintDevice *pdev, const QObjectList& children, int index,
+ const QRegion &rgn, const QPoint &offset, int flags
+#ifdef Q_BACKINGSTORE_SUBSURFACES
+ , const QWindowSurface *currentSurface
+#endif
+ , QPainter *sharedPainter, QWidgetBackingStore *backingStore);
+
+
+ QPainter *beginSharedPainter();
+ bool endSharedPainter();
+ static QGraphicsProxyWidget * nearestGraphicsProxyWidget(QWidget *origin);
+ QWindowSurface *createDefaultWindowSurface();
+ QWindowSurface *createDefaultWindowSurface_sys();
+ void repaint_sys(const QRegion &rgn);
+#ifdef Q_WS_MAC
+ void update_sys(const QRect &rect);
+ void update_sys(const QRegion &rgn);
+#endif
+
+ QRect clipRect() const;
+ QRegion clipRegion() const;
+ void subtractOpaqueChildren(QRegion &rgn, const QRect &clipRect) const;
+ void subtractOpaqueSiblings(QRegion &source, bool *hasDirtySiblingsAbove = 0,
+ bool alsoNonOpaque = false) const;
+ void clipToEffectiveMask(QRegion &region) const;
+ void updateIsOpaque();
+ void setOpaque(bool opaque);
+ void updateIsTranslucent();
+ bool hasBackground() const;
+ bool paintOnScreen() const;
+
+ QRegion getOpaqueRegion() const;
+ const QRegion &getOpaqueChildren() const;
+ void setDirtyOpaqueRegion();
+
+ QRegion opaqueChildren;
+
+ enum CloseMode {
+ CloseNoEvent,
+ CloseWithEvent,
+ CloseWithSpontaneousEvent
+ };
+ bool close_helper(CloseMode mode);
+
+ bool compositeEvent(QEvent *e);
+ void setWindowIcon_helper();
+ void setWindowIcon_sys(bool forceReset = false);
+ void setWindowOpacity_sys(qreal opacity);
+
+ void focusInputContext();
+
+ void adjustQuitOnCloseAttribute();
+
+#if defined(Q_WS_X11)
+ void setWindowRole();
+ void sendStartupMessage(const char *message) const;
+ void setNetWmWindowTypes();
+ void x11UpdateIsOpaque();
+#endif
+
+#if defined (Q_WS_WIN)
+ void reparentChildren();
+#endif
+
+ void scrollChildren(int dx, int dy);
+
+ void moveRect(const QRect &, int dx, int dy);
+ void scrollRect(const QRect &, int dx, int dy);
+ void invalidateBuffer_resizeHelper(const QPoint &oldPos, const QSize &oldSize);
+ // ### Qt 4.6: Merge into a template function (after MSVC isn't supported anymore).
+ void invalidateBuffer(const QRegion &);
+ void invalidateBuffer(const QRect &);
+ bool isOverlapped(const QRect&) const;
+ void syncBackingStore();
+ void syncBackingStore(const QRegion &region);
+
+ void reparentFocusWidgets(QWidget *oldtlw);
+
+ static int pointToRect(const QPoint &p, const QRect &r);
+ QRect fromOrToLayoutItemRect(const QRect &rect, int sign) const;
+
+ void setWinId(WId);
+ void showChildren(bool spontaneous);
+ void hideChildren(bool spontaneous);
+ void setParent_sys(QWidget *parent, Qt::WindowFlags);
+ void scroll_sys(int dx, int dy);
+ void scroll_sys(int dx, int dy, const QRect &r);
+ void deactivateWidgetCleanup();
+ void setGeometry_sys(int, int, int, int, bool);
+#ifdef Q_WS_MAC
+ void setGeometry_sys_helper(int, int, int, int, bool);
+#endif
+ void sendPendingMoveAndResizeEvents(bool recursive = false, bool disableUpdates = false);
+ void activateChildLayoutsRecursively();
+ void show_recursive();
+ void show_helper();
+ void show_sys();
+ void hide_sys();
+ void hide_helper();
+ void _q_showIfNotHidden();
+
+ void setEnabled_helper(bool);
+ void registerDropSite(bool);
+#if defined(Q_WS_WIN) && !defined(QT_NO_DRAGANDDROP)
+ QOleDropTarget *registerOleDnd(QWidget *widget);
+ void unregisterOleDnd(QWidget *widget, QOleDropTarget *target);
+#endif
+ static void adjustFlags(Qt::WindowFlags &flags, QWidget *w = 0);
+
+ void updateFrameStrut();
+ QRect frameStrut() const;
+
+ void setWindowIconText_sys(const QString &cap);
+ void setWindowIconText_helper(const QString &cap);
+ void setWindowTitle_sys(const QString &cap);
+
+#ifdef Q_OS_WIN
+ void grabMouseWhileInWindow();
+#endif
+
+#ifndef QT_NO_CURSOR
+ void setCursor_sys(const QCursor &cursor);
+ void unsetCursor_sys();
+#endif
+
+#ifdef Q_WS_MAC
+ void setWindowModified_sys(bool b);
+ void updateMaximizeButton_sys();
+ void setWindowFilePath_sys(const QString &filePath);
+ void createWindow_sys();
+ void recreateMacWindow();
+#ifndef QT_MAC_USE_COCOA
+ void initWindowPtr();
+ void finishCreateWindow_sys_Carbon(OSWindowRef windowRef);
+#else
+ void finishCreateWindow_sys_Cocoa(void * /*NSWindow * */ windowRef);
+ void syncCocoaMask();
+ void finishCocoaMaskSetup();
+#endif
+ void determineWindowClass();
+ void transferChildren();
+#endif
+ void setWindowTitle_helper(const QString &cap);
+ void setWindowFilePath_helper(const QString &filePath);
+
+ bool setMinimumSize_helper(int &minw, int &minh);
+ bool setMaximumSize_helper(int &maxw, int &maxh);
+ void setConstraints_sys();
+ QWidget *childAt_helper(const QPoint &, bool) const;
+ void updateGeometry_helper(bool forceUpdate);
+
+ void getLayoutItemMargins(int *left, int *top, int *right, int *bottom) const;
+ void setLayoutItemMargins(int left, int top, int right, int bottom);
+ void setLayoutItemMargins(QStyle::SubElement element, const QStyleOption *opt = 0);
+
+ QInputContext *inputContext() const;
+
+#if defined(Q_WS_QWS)
+ void moveSurface(QWindowSurface *surface, const QPoint &offset);
+
+ QRegion localRequestedRegion() const;
+ QRegion localAllocatedRegion() const;
+
+ void blitToScreen(const QRegion &globalrgn);
+#ifndef QT_NO_CURSOR
+ void updateCursor() const;
+#endif
+
+ QScreen* getScreen() const;
+
+ friend class QWSManager;
+ friend class QWSManagerPrivate;
+ friend class QDecoration;
+#endif
+
+ static int instanceCounter; // Current number of widget instances
+ static int maxInstances; // Maximum number of widget instances
+
+#ifdef QT_KEYPAD_NAVIGATION
+ static QPointer<QWidget> editingWidget;
+#endif
+
+ QWidgetData data;
+
+ QWExtra *extra;
+ QWidget *focus_next;
+ QWidget *focus_prev;
+ QWidget *focus_child;
+#ifndef QT_NO_ACTION
+ QList<QAction*> actions;
+#endif
+ QLayout *layout;
+ QWidgetItemV2 *widgetItem;
+#if !defined(QT_NO_IM)
+ QPointer<QInputContext> ic;
+#endif
+ // All widgets are initially added into the uncreatedWidgets set. Once
+ // they receive a window id they are removed and added to the mapper
+ static QWidgetMapper *mapper;
+ static QWidgetSet *uncreatedWidgets;
+
+ short leftmargin, topmargin, rightmargin, bottommargin;
+
+ signed char leftLayoutItemMargin;
+ signed char topLayoutItemMargin;
+ signed char rightLayoutItemMargin;
+ signed char bottomLayoutItemMargin;
+
+ // ### TODO: reorganize private/extra/topextra to save memory
+ QPointer<QWidget> compositeChildGrab;
+#ifndef QT_NO_TOOLTIP
+ QString toolTip;
+#endif
+#ifndef QT_NO_STATUSTIP
+ QString statusTip;
+#endif
+#ifndef QT_NO_WHATSTHIS
+ QString whatsThis;
+#endif
+ QString accessibleName, accessibleDescription;
+
+ QPalette::ColorRole fg_role : 8;
+ QPalette::ColorRole bg_role : 8;
+ uint high_attributes[3]; // the low ones are in QWidget::widget_attributes
+ Qt::HANDLE hd;
+ QRegion dirty;
+ QRegion *needsFlush;
+ uint dirtyOpaqueChildren : 1;
+ uint isOpaque : 1;
+ uint inDirtyList : 1;
+ uint isScrolled : 1;
+ uint isMoved : 1;
+
+#ifdef Q_WS_WIN
+ uint noPaintOnScreen : 1; // see qwidget_win.cpp ::paintEngine()
+#endif
+
+ uint inheritedFontResolveMask;
+ uint inheritedPaletteResolveMask;
+#if defined(Q_WS_X11)
+ QX11Info xinfo;
+ Qt::HANDLE picture;
+#endif
+#if defined(Q_WS_MAC)
+ enum PaintChildrenOPs {
+ PC_None = 0x00,
+ PC_Now = 0x01,
+ PC_NoPaint = 0x04,
+ PC_Later = 0x10
+ };
+ EventHandlerRef window_event;
+ bool qt_mac_dnd_event(uint, DragRef);
+ void toggleDrawers(bool);
+ //mac event functions
+ static bool qt_create_root_win();
+ static void qt_clean_root_win();
+ static bool qt_recreate_root_win();
+ static bool qt_mac_update_sizer(QWidget *, int up = 0);
+ static OSStatus qt_window_event(EventHandlerCallRef er, EventRef event, void *);
+ static OSStatus qt_widget_event(EventHandlerCallRef er, EventRef event, void *);
+ static bool qt_widget_rgn(QWidget *, short, RgnHandle, bool);
+ static bool qt_widget_shape(QWidget *, short, HIMutableShapeRef, bool);
+
+ // Each wiget keeps a list of all its child and grandchild OpenGL widgets.
+ // This list is used to update the gl context whenever a parent and a granparent
+ // moves, and also to check for intersections with gl widgets within the window
+ // when a widget moves.
+ struct GlWidgetInfo
+ {
+ GlWidgetInfo(QWidget *widget) : widget(widget), lastUpdateWidget(0) { }
+ bool operator==(const GlWidgetInfo &other) const { return (widget == other.widget); }
+ QWidget * widget;
+ QWidget * lastUpdateWidget;
+ };
+ QList<GlWidgetInfo> glWidgets;
+
+ // dirtyOnWidget contains the areas in the widget that needs to be repained,
+ // in the same way as dirtyOnScreen does for the window. Areas are added in
+ // dirtyWidget_sys and cleared in the paint event. In scroll_sys we then use
+ // this information repaint invalid areas when widgets are scrolled.
+ QRegion dirtyOnWidget;
+
+ //these are here just for code compat (HIViews)
+ Qt::HANDLE qd_hd;
+
+ // This is new stuff
+ uint needWindowChange : 1;
+ uint isGLWidget : 1;
+#endif
+
+#if defined(Q_WS_X11) || defined (Q_WS_WIN) || defined(Q_WS_MAC)
+#ifdef Q_WS_MAC
+ void setWSGeometry(bool dontShow=false, const QRect &oldRect = QRect());
+#else
+ void setWSGeometry(bool dontShow=false);
+#endif
+
+ inline QPoint mapToWS(const QPoint &p) const
+ { return p - data.wrect.topLeft(); }
+
+ inline QPoint mapFromWS(const QPoint &p) const
+ { return p + data.wrect.topLeft(); }
+
+ inline QRect mapToWS(const QRect &r) const
+ { QRect rr(r); rr.translate(-data.wrect.topLeft()); return rr; }
+
+ inline QRect mapFromWS(const QRect &r) const
+ { QRect rr(r); rr.translate(data.wrect.topLeft()); return rr; }
+#endif
+
+ QPaintEngine *extraPaintEngine;
+
+ mutable const QMetaObject *polished;
+
+ void setModal_sys();
+ QSizePolicy size_policy;
+ QLocale locale;
+
+#ifdef Q_WS_X11
+ static QWidget *mouseGrabber;
+ static QWidget *keyboardGrabber;
+#endif
+ QPaintDevice *redirectDev;
+ QPoint redirectOffset;
+
+ inline void setRedirected(QPaintDevice *replacement, const QPoint &offset)
+ {
+ Q_ASSERT(q_func()->testAttribute(Qt::WA_WState_InPaintEvent));
+ redirectDev = replacement;
+ redirectOffset = offset;
+ }
+
+ inline QPaintDevice *redirected(QPoint *offset) const
+ {
+ if (offset)
+ *offset = redirectDev ? redirectOffset : QPoint();
+ return redirectDev;
+ }
+
+ inline void restoreRedirected()
+ { redirectDev = 0; }
+
+ inline void enforceNativeChildren()
+ {
+ if (!extra)
+ createExtra();
+
+ if (extra->nativeChildrenForced)
+ return;
+ extra->nativeChildrenForced = 1;
+
+ for (int i = 0; i < children.size(); ++i) {
+ if (QWidget *child = qobject_cast<QWidget *>(children.at(i)))
+ child->setAttribute(Qt::WA_NativeWindow);
+ }
+ }
+
+ inline bool nativeChildrenForced() const
+ {
+ return extra ? extra->nativeChildrenForced : false;
+ }
+
+ QSize adjustedSize() const;
+};
+
+inline QWExtra *QWidgetPrivate::extraData() const
+{
+ return extra;
+}
+
+inline QTLWExtra *QWidgetPrivate::topData() const
+{
+ const_cast<QWidgetPrivate *>(this)->createTLExtra();
+ return extra->topextra;
+}
+
+inline QTLWExtra *QWidgetPrivate::maybeTopData() const
+{
+ return extra ? extra->topextra : 0;
+}
+
+inline QPainter *QWidgetPrivate::sharedPainter() const
+{
+ Q_Q(const QWidget);
+ QTLWExtra *x = q->window()->d_func()->maybeTopData();
+ return x ? x->sharedPainter : 0;
+}
+
+inline void QWidgetPrivate::setSharedPainter(QPainter *painter)
+{
+ Q_Q(QWidget);
+ QTLWExtra *x = q->window()->d_func()->topData();
+ x->sharedPainter = painter;
+}
+
+inline QWidgetBackingStore *QWidgetPrivate::maybeBackingStore() const
+{
+ Q_Q(const QWidget);
+ QTLWExtra *x = q->window()->d_func()->maybeTopData();
+ return x ? x->backingStore : 0;
+}
+
+QT_END_NAMESPACE
+
+#endif // QWIDGET_P_H
diff --git a/src/gui/kernel/qwidget_qws.cpp b/src/gui/kernel/qwidget_qws.cpp
new file mode 100644
index 0000000000..1445f57ae4
--- /dev/null
+++ b/src/gui/kernel/qwidget_qws.cpp
@@ -0,0 +1,1198 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qcursor.h"
+#include "qapplication.h"
+#include "qapplication_p.h"
+#include "qpainter.h"
+#include "qbitmap.h"
+#include "qimage.h"
+#include "qhash.h"
+#include "qstack.h"
+#include "qlayout.h"
+#include "qtextcodec.h"
+#include "qinputcontext.h"
+#include "qdesktopwidget.h"
+
+#include "qwsdisplay_qws.h"
+#include "private/qwsdisplay_qws_p.h"
+#include "qscreen_qws.h"
+#include "qwsmanager_qws.h"
+#include <private/qwsmanager_p.h>
+#include <private/qbackingstore_p.h>
+#include <private/qwindowsurface_qws_p.h>
+#include <private/qwslock_p.h>
+#include "qpaintengine.h"
+
+#include "qdebug.h"
+
+#include "qwidget_p.h"
+
+QT_BEGIN_NAMESPACE
+
+QT_USE_NAMESPACE
+
+extern int *qt_last_x;
+extern int *qt_last_y;
+extern WId qt_last_cursor;
+extern bool qws_overrideCursor;
+extern QWidget *qt_pressGrab;
+extern QWidget *qt_mouseGrb;
+
+static QWidget *keyboardGrb = 0;
+
+static int takeLocalId()
+{
+ static int n=-1000;
+ return --n;
+}
+
+class QWSServer;
+extern QWSServer *qwsServer;
+
+static inline bool isServerProcess()
+{
+ return (qwsServer != 0);
+}
+
+/*****************************************************************************
+ QWidget member functions
+ *****************************************************************************/
+
+void QWidgetPrivate::create_sys(WId window, bool initializeWindow, bool /*destroyOldWindow*/)
+{
+ Q_Q(QWidget);
+ Qt::WindowType type = q->windowType();
+
+ // Make sure the WindowTitleHint is on if any of the title bar hints are set
+ // Note: This might be moved to cross-platform QWidgetPrivate::adjustFlags()
+ if ( !(data.window_flags & Qt::CustomizeWindowHint) && (
+ (data.window_flags & Qt::WindowSystemMenuHint) ||
+ (data.window_flags & Qt::WindowContextHelpButtonHint) ||
+ (data.window_flags & Qt::WindowMinimizeButtonHint) ||
+ (data.window_flags & Qt::WindowMaximizeButtonHint) ||
+ (data.window_flags & Qt::WindowCloseButtonHint) ) ) {
+ data.window_flags |= Qt::WindowTitleHint;
+ }
+
+ // Decoration plugins on QWS don't support switching on the close button on its own
+ if (data.window_flags & Qt::WindowCloseButtonHint)
+ data.window_flags |= Qt::WindowSystemMenuHint;
+
+ Qt::WindowFlags flags = data.window_flags;
+
+ data.alloc_region_index = -1;
+
+ // we don't have a "Drawer" window type
+ if (type == Qt::Drawer) {
+ type = Qt::Widget;
+ flags &= ~Qt::WindowType_Mask;
+ }
+
+
+ bool topLevel = (flags & Qt::Window);
+ bool popup = (type == Qt::Popup);
+ bool dialog = (type == Qt::Dialog
+ || type == Qt::Sheet
+ || (flags & Qt::MSWindowsFixedSizeDialogHint));
+ bool desktop = (type == Qt::Desktop);
+ bool tool = (type == Qt::Tool || type == Qt::SplashScreen || type == Qt::ToolTip);
+
+
+#ifndef QT_NO_WARNING_OUTPUT
+ static bool toolWarningShown = false;
+ if (!toolWarningShown && type == Qt::Tool && !(flags & Qt::FramelessWindowHint)) {
+ qWarning("Qt for Embedded Linux " QT_VERSION_STR " does not support tool windows with frames.\n"
+ "This behavior will change in a later release. To ensure compatibility with\n"
+ "future versions, use (Qt::Tool | Qt::FramelessWindowHint).");
+ toolWarningShown = true;
+ }
+#endif
+
+ WId id;
+ QWSDisplay* dpy = QWidget::qwsDisplay();
+
+ if (!window) // always initialize
+ initializeWindow = true;
+
+ // use the size of the primary screen to determine the default window size
+ QList<QScreen *> screens = qt_screen->subScreens();
+ if (screens.isEmpty())
+ screens.append(qt_screen);
+ int sw = screens[0]->width();
+ int sh = screens[0]->height();
+
+ if (desktop) { // desktop widget
+ dialog = popup = false; // force these flags off
+ data.crect.setRect(0, 0, sw, sh);
+ } else if (topLevel && !q->testAttribute(Qt::WA_Resized)) {
+ int width = sw / 2;
+ int height = 4 * sh / 10;
+ if (extra) {
+ width = qMax(qMin(width, extra->maxw), extra->minw);
+ height = qMax(qMin(height, extra->maxh), extra->minh);
+ }
+ data.crect.setSize(QSize(width, height));
+ }
+
+ if (window) { // override the old window
+ id = window;
+ setWinId(window);
+ } else if (desktop) { // desktop widget
+ id = (WId)-2; // id = root window
+#if 0
+ QWidget *otherDesktop = q->find(id); // is there another desktop?
+ if (otherDesktop && otherDesktop->testWFlags(Qt::WPaintDesktop)) {
+ otherDesktop->d_func()->setWinId(0); // remove id from widget mapper
+ setWinId(id); // make sure otherDesktop is
+ otherDesktop->d_func()->setWinId(id); // found first
+ } else
+#endif
+ {
+ setWinId(id);
+ }
+ } else {
+ id = topLevel ? dpy->takeId() : takeLocalId();
+ setWinId(id); // set widget id/handle + hd
+ }
+
+
+ bool hasFrame = true;
+ if (topLevel) {
+ if (desktop || popup || tool || q->testAttribute(Qt::WA_DontShowOnScreen))
+ hasFrame = false;
+ else
+ hasFrame = !(flags & Qt::FramelessWindowHint);
+ }
+ if (desktop) {
+ q->setAttribute(Qt::WA_WState_Visible);
+ } else if (topLevel) { // set X cursor
+ //QCursor *oc = QApplication::overrideCursor();
+ if (initializeWindow) {
+ //XXX XDefineCursor(dpy, winid, oc ? oc->handle() : cursor().handle());
+ }
+ QWidget::qwsDisplay()->nameRegion(q->internalWinId(), q->objectName(), q->windowTitle());
+ }
+
+ if (topLevel) {
+ createTLExtra();
+ QTLWExtra *topextra = extra->topextra;
+#ifndef QT_NO_QWS_MANAGER
+ if (hasFrame) {
+ // get size of wm decoration and make the old crect the new frect
+ QRect cr = data.crect;
+ QRegion r = QApplication::qwsDecoration().region(q, cr) | cr;
+ QRect br(r.boundingRect());
+ topextra->frameStrut.setCoords(cr.x() - br.x(),
+ cr.y() - br.y(),
+ br.right() - cr.right(),
+ br.bottom() - cr.bottom());
+ if (!q->testAttribute(Qt::WA_Moved) || topextra->posFromMove)
+ data.crect.translate(topextra->frameStrut.left(), topextra->frameStrut.top());
+ if (!topData()->qwsManager) {
+ topData()->qwsManager = new QWSManager(q);
+ if((q->data->window_state & ~Qt::WindowActive) == Qt::WindowMaximized)
+ topData()->qwsManager->maximize();
+ }
+
+ } else if (topData()->qwsManager) {
+ delete topData()->qwsManager;
+ topData()->qwsManager = 0;
+ data.crect.translate(-topextra->frameStrut.left(), -topextra->frameStrut.top());
+ topextra->frameStrut.setCoords(0, 0, 0, 0);
+ }
+#endif
+ if (!topextra->caption.isEmpty())
+ setWindowTitle_helper(topextra->caption);
+
+ //XXX If we are session managed, inform the window manager about it
+ } else {
+ if (extra && extra->topextra) { // already allocated due to reparent?
+ extra->topextra->frameStrut.setCoords(0, 0, 0, 0);
+ }
+ //updateRequestedRegion(mapToGlobal(QPoint(0,0)));
+ }
+}
+
+
+void QWidget::destroy(bool destroyWindow, bool destroySubWindows)
+{
+ Q_D(QWidget);
+
+ if (!isWindow() && parentWidget())
+ parentWidget()->d_func()->invalidateBuffer(geometry());
+
+ d->deactivateWidgetCleanup();
+ if (testAttribute(Qt::WA_WState_Created)) {
+ setAttribute(Qt::WA_WState_Created, false);
+ QObjectList childObjects = children();
+ for (int i = 0; i < childObjects.size(); ++i) {
+ QObject *obj = childObjects.at(i);
+ if (obj->isWidgetType())
+ static_cast<QWidget*>(obj)->destroy(destroySubWindows,
+ destroySubWindows);
+ }
+ releaseMouse();
+ if (qt_pressGrab == this)
+ qt_pressGrab = 0;
+
+ if (keyboardGrb == this)
+ releaseKeyboard();
+ if (testAttribute(Qt::WA_ShowModal)) // just be sure we leave modal
+ QApplicationPrivate::leaveModal(this);
+ else if ((windowType() == Qt::Popup))
+ qApp->d_func()->closePopup(this);
+
+ if (d->ic) {
+ delete d->ic;
+ d->ic =0;
+ } else {
+ // release previous focus information participating with
+ // preedit preservation of qic -- while we still have a winId
+ QInputContext *qic = inputContext();
+ if (qic)
+ qic->widgetDestroyed(this);
+ }
+
+ if ((windowType() == Qt::Desktop)) {
+ } else {
+ if (parentWidget() && parentWidget()->testAttribute(Qt::WA_WState_Created)) {
+ d->hide_sys();
+ }
+ if (destroyWindow && isWindow()) {
+ d->extra->topextra->backingStore->windowSurface->setGeometry(QRect());
+ qwsDisplay()->destroyRegion(internalWinId());
+ }
+ }
+ d->setWinId(0);
+ }
+}
+
+
+void QWidgetPrivate::setParent_sys(QWidget *newparent, Qt::WindowFlags f)
+{
+ Q_Q(QWidget);
+ bool wasCreated = q->testAttribute(Qt::WA_WState_Created);
+ if (q->isVisible() && q->parentWidget() && parent != q->parentWidget())
+ q->parentWidget()->d_func()->invalidateBuffer(q->geometry());
+#ifndef QT_NO_CURSOR
+ QCursor oldcurs;
+ bool setcurs=q->testAttribute(Qt::WA_SetCursor);
+ if (setcurs) {
+ oldcurs = q->cursor();
+ q->unsetCursor();
+ }
+#endif
+
+ WId old_winid = data.winid;
+ if ((q->windowType() == Qt::Desktop))
+ old_winid = 0;
+
+ if (!q->isWindow() && q->parentWidget() && q->parentWidget()->testAttribute(Qt::WA_WState_Created))
+ hide_sys();
+
+ setWinId(0);
+
+ if (parent != newparent) {
+ QWidget *oldparent = q->parentWidget();
+ QObjectPrivate::setParent_helper(newparent);
+ if (oldparent) {
+// oldparent->d_func()->setChildrenAllocatedDirty();
+// oldparent->data->paintable_region_dirty = true;
+ }
+ if (newparent) {
+// newparent->d_func()->setChildrenAllocatedDirty();
+// newparent->data->paintable_region_dirty = true;
+ //@@@@@@@
+ }
+ }
+ Qt::FocusPolicy fp = q->focusPolicy();
+ QSize s = q->size();
+ //QBrush bgc = background(); // save colors
+ bool explicitlyHidden = q->testAttribute(Qt::WA_WState_Hidden) && q->testAttribute(Qt::WA_WState_ExplicitShowHide);
+
+ data.window_flags = f;
+ q->setAttribute(Qt::WA_WState_Created, false);
+ q->setAttribute(Qt::WA_WState_Visible, false);
+ q->setAttribute(Qt::WA_WState_Hidden, false);
+ adjustFlags(data.window_flags, q);
+ // keep compatibility with previous versions, we need to preserve the created state
+ // (but we recreate the winId for the widget being reparented, again for compatibility)
+ if (wasCreated || (!q->isWindow() && newparent->testAttribute(Qt::WA_WState_Created)))
+ createWinId();
+ if (q->isWindow() || (!newparent || newparent->isVisible()) || explicitlyHidden)
+ q->setAttribute(Qt::WA_WState_Hidden);
+ q->setAttribute(Qt::WA_WState_ExplicitShowHide, explicitlyHidden);
+
+ if (q->isWindow()) {
+ QRect fs = frameStrut();
+ data.crect = QRect(fs.left(), fs.top(), s.width(), s.height());
+ if ((data.window_flags & Qt::FramelessWindowHint) && extra && extra->topextra)
+ extra->topextra->frameStrut.setCoords(0, 0, 0, 0);
+ } else {
+ data.crect = QRect(0, 0, s.width(), s.height());
+ }
+
+ q->setFocusPolicy(fp);
+ if (extra && !extra->mask.isEmpty()) {
+ QRegion r = extra->mask;
+ extra->mask = QRegion();
+ q->setMask(r);
+ }
+ if ((int)old_winid > 0) {
+ QWidget::qwsDisplay()->destroyRegion(old_winid);
+ extra->topextra->backingStore->windowSurface->setGeometry(QRect());
+ }
+#ifndef QT_NO_CURSOR
+ if (setcurs) {
+ q->setCursor(oldcurs);
+ }
+#endif
+}
+
+
+QPoint QWidget::mapToGlobal(const QPoint &pos) const
+{
+ int x=pos.x(), y=pos.y();
+ const QWidget* w = this;
+ while (w) {
+ x += w->data->crect.x();
+ y += w->data->crect.y();
+ w = w->isWindow() ? 0 : w->parentWidget();
+ }
+ return QPoint(x, y);
+}
+
+QPoint QWidget::mapFromGlobal(const QPoint &pos) const
+{
+ int x=pos.x(), y=pos.y();
+ const QWidget* w = this;
+ while (w) {
+ x -= w->data->crect.x();
+ y -= w->data->crect.y();
+ w = w->isWindow() ? 0 : w->parentWidget();
+ }
+ return QPoint(x, y);
+}
+
+#if 0 // #####
+void QWidget::setMicroFocusHint(int x, int y, int width, int height,
+ bool text, QFont *)
+{
+ if (QRect(x, y, width, height) != microFocusHint()) {
+ d->createExtra();
+ d->extra->micro_focus_hint.setRect(x, y, width, height);
+ }
+#ifndef QT_NO_QWS_INPUTMETHODS
+ if (text) {
+ QWidget *tlw = window();
+ int winid = tlw->internalWinId();
+ QPoint p(x, y + height);
+ QPoint gp = mapToGlobal(p);
+
+ QRect r = QRect(mapToGlobal(QPoint(0,0)),
+ size());
+
+ r.setBottom(tlw->geometry().bottom());
+
+ //qDebug("QWidget::setMicroFocusHint %d %d %d %d", r.x(),
+ // r.y(), r.width(), r.height());
+ QInputContext::setMicroFocusWidget(this);
+
+ qwsDisplay()->setIMInfo(winid, gp.x(), gp.y(), r);
+
+ //send font info, ###if necessary
+ qwsDisplay()->setInputFont(winid, font());
+ }
+#endif
+}
+#endif
+
+void QWidgetPrivate::updateSystemBackground() {}
+
+#ifndef QT_NO_CURSOR
+void QWidgetPrivate::setCursor_sys(const QCursor &cursor)
+{
+ Q_UNUSED(cursor);
+ Q_Q(QWidget);
+ if (q->isVisible())
+ updateCursor();
+}
+
+void QWidgetPrivate::unsetCursor_sys()
+{
+ Q_Q(QWidget);
+ if (q->isVisible())
+ updateCursor();
+}
+#endif //QT_NO_CURSOR
+
+void QWidgetPrivate::setWindowTitle_sys(const QString &caption)
+{
+ Q_Q(QWidget);
+ QWidget::qwsDisplay()->setWindowCaption(q, caption);
+}
+
+void QWidgetPrivate::setWindowIcon_sys(bool /*forceReset*/)
+{
+#if 0
+ QTLWExtra* x = d->topData();
+ delete x->icon;
+ x->icon = 0;
+ QBitmap mask;
+ if (unscaledPixmap.isNull()) {
+ } else {
+ QImage unscaledIcon = unscaledPixmap.toImage();
+ QPixmap pixmap =
+ QPixmap::fromImage(unscaledIcon.scale(16, 16, Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
+ x->icon = new QPixmap(pixmap);
+ mask = pixmap.mask() ? *pixmap.mask() : pixmap.createHeuristicMask();
+ }
+#endif
+}
+
+void QWidgetPrivate::setWindowIconText_sys(const QString &iconText)
+{
+ Q_UNUSED(iconText);
+}
+
+void QWidget::grabMouse()
+{
+ if (qt_mouseGrb)
+ qt_mouseGrb->releaseMouse();
+
+ qwsDisplay()->grabMouse(this,true);
+
+ qt_mouseGrb = this;
+ qt_pressGrab = 0;
+}
+
+#ifndef QT_NO_CURSOR
+void QWidget::grabMouse(const QCursor &cursor)
+{
+ if (qt_mouseGrb)
+ qt_mouseGrb->releaseMouse();
+
+ qwsDisplay()->grabMouse(this,true);
+ qwsDisplay()->selectCursor(this, cursor.handle());
+ qt_mouseGrb = this;
+ qt_pressGrab = 0;
+}
+#endif
+
+void QWidget::releaseMouse()
+{
+ if (qt_mouseGrb == this) {
+ qwsDisplay()->grabMouse(this,false);
+ qt_mouseGrb = 0;
+ }
+}
+
+void QWidget::grabKeyboard()
+{
+ if (keyboardGrb)
+ keyboardGrb->releaseKeyboard();
+ qwsDisplay()->grabKeyboard(this, true);
+ keyboardGrb = this;
+}
+
+void QWidget::releaseKeyboard()
+{
+ if (keyboardGrb == this) {
+ qwsDisplay()->grabKeyboard(this, false);
+ keyboardGrb = 0;
+ }
+}
+
+
+QWidget *QWidget::mouseGrabber()
+{
+ if (qt_mouseGrb)
+ return qt_mouseGrb;
+ return qt_pressGrab;
+}
+
+
+QWidget *QWidget::keyboardGrabber()
+{
+ return keyboardGrb;
+}
+
+void QWidget::activateWindow()
+{
+ QWidget *tlw = window();
+ if (tlw->isVisible()) {
+ Q_ASSERT(tlw->testAttribute(Qt::WA_WState_Created));
+ qwsDisplay()->requestFocus(tlw->internalWinId(), true);
+ }
+}
+
+/*
+ Should we require that q is a toplevel window ???
+
+ Used by QWSManager
+ */
+void QWidgetPrivate::blitToScreen(const QRegion &globalrgn)
+{
+ Q_Q(QWidget);
+ QWidget *win = q->window();
+ QBrush bgBrush = win->palette().brush(win->backgroundRole());
+ bool opaque = bgBrush.style() == Qt::NoBrush || bgBrush.isOpaque();
+ QWidget::qwsDisplay()->repaintRegion(win->data->winid, win->windowFlags(), opaque, globalrgn);
+}
+
+void QWidgetPrivate::show_sys()
+{
+ Q_Q(QWidget);
+ q->setAttribute(Qt::WA_Mapped);
+ if (q->testAttribute(Qt::WA_DontShowOnScreen)) {
+ invalidateBuffer(q->rect());
+ return;
+ }
+
+ if (q->isWindow()) {
+
+ if (QWindowSurface *surface = q->windowSurface()) {
+ const QRect frameRect = q->frameGeometry();
+ if (surface->geometry() != frameRect)
+ surface->setGeometry(frameRect);
+ }
+
+ QRegion r = localRequestedRegion();
+#ifndef QT_NO_QWS_MANAGER
+ if (extra && extra->topextra && extra->topextra->qwsManager) {
+ r.translate(data.crect.topLeft());
+ r += extra->topextra->qwsManager->region();
+ r.translate(-data.crect.topLeft());
+ }
+#endif
+ data.fstrut_dirty = true;
+ invalidateBuffer(r);
+ if (!q->testAttribute(Qt::WA_ShowWithoutActivating)
+ && q->windowType() != Qt::Popup
+ && q->windowType() != Qt::Tool
+ && q->windowType() != Qt::ToolTip) {
+ QWidget::qwsDisplay()->requestFocus(data.winid,true);
+ }
+ bool staysontop =
+ (q->windowFlags() & Qt::WindowStaysOnTopHint)
+ || q->windowType() == Qt::Popup;
+ if (!staysontop && q->parentWidget()) { // if our parent stays on top, so must we
+ QWidget *ptl = q->parentWidget()->window();
+ if (ptl && (ptl->windowFlags() & Qt::WindowStaysOnTopHint))
+ staysontop = true;
+ }
+
+ QWSChangeAltitudeCommand::Altitude altitude;
+ altitude = staysontop ? QWSChangeAltitudeCommand::StaysOnTop : QWSChangeAltitudeCommand::Raise;
+ QWidget::qwsDisplay()->setAltitude(data.winid, altitude, true);
+ if (!q->objectName().isEmpty()) {
+ QWidget::qwsDisplay()->setWindowCaption(q, q->windowTitle());
+ }
+ }
+#ifdef Q_BACKINGSTORE_SUBSURFACES
+ else if ( extra && extra->topextra && extra->topextra->windowSurface) {
+ QWSWindowSurface *surface;
+ surface = static_cast<QWSWindowSurface*>(q->windowSurface());
+ const QPoint p = q->mapToGlobal(QPoint());
+ surface->setGeometry(QRect(p, q->size()));
+ }
+#endif
+
+ if (!q->window()->data->in_show) {
+ invalidateBuffer(q->rect());
+ }
+}
+
+
+void QWidgetPrivate::hide_sys()
+{
+ Q_Q(QWidget);
+ deactivateWidgetCleanup();
+
+ if (q->isWindow()) {
+ q->releaseMouse();
+// requestWindowRegion(QRegion());
+
+ extra->topextra->backingStore->releaseBuffer();
+
+
+ QWidget::qwsDisplay()->requestFocus(data.winid,false);
+ } else {
+ QWidget *p = q->parentWidget();
+ if (p &&p->isVisible()) {
+ invalidateBuffer(q->rect());
+ }
+ }
+}
+
+
+
+static Qt::WindowStates effectiveState(Qt::WindowStates state)
+ {
+ if (state & Qt::WindowMinimized)
+ return Qt::WindowMinimized;
+ else if (state & Qt::WindowFullScreen)
+ return Qt::WindowFullScreen;
+ else if (state & Qt::WindowMaximized)
+ return Qt::WindowMaximized;
+ return Qt::WindowNoState;
+ }
+
+void QWidgetPrivate::setMaxWindowState_helper()
+{
+ // in_set_window_state is usually set in setWindowState(), but this
+ // function is used in other functions as well
+ // (e.g QApplicationPrivate::setMaxWindowRect())
+ const uint old_state = data.in_set_window_state;
+ data.in_set_window_state = 1;
+
+#ifndef QT_NO_QWS_MANAGER
+ if (extra && extra->topextra && extra->topextra->qwsManager)
+ extra->topextra->qwsManager->maximize();
+ else
+#endif
+ {
+ Q_Q(QWidget);
+ const QDesktopWidget *desktop = QApplication::desktop();
+ const int screen = desktop->screenNumber(q);
+ const QRect maxWindowRect = desktop->availableGeometry(screen);
+ q->setGeometry(maxWindowRect);
+ }
+ data.in_set_window_state = old_state;
+}
+
+void QWidgetPrivate::setFullScreenSize_helper()
+{
+ Q_Q(QWidget);
+
+ const uint old_state = data.in_set_window_state;
+ data.in_set_window_state = 1;
+
+ const QRect screen = qApp->desktop()->screenGeometry(qApp->desktop()->screenNumber(q));
+ q->move(screen.topLeft());
+ q->resize(screen.size());
+
+ data.in_set_window_state = old_state;
+}
+
+void QWidget::setWindowState(Qt::WindowStates newstate)
+{
+ Q_D(QWidget);
+ Qt::WindowStates oldstate = windowState();
+ if (oldstate == newstate)
+ return;
+ if (isWindow() && !testAttribute(Qt::WA_WState_Created))
+ create();
+
+ data->window_state = newstate;
+ data->in_set_window_state = 1;
+ bool needShow = false;
+ Qt::WindowStates newEffectiveState = effectiveState(newstate);
+ Qt::WindowStates oldEffectiveState = effectiveState(oldstate);
+ if (isWindow() && newEffectiveState != oldEffectiveState) {
+ d->createTLExtra();
+ if (oldEffectiveState == Qt::WindowNoState) { //normal
+ d->topData()->normalGeometry = geometry();
+ } else if (oldEffectiveState == Qt::WindowFullScreen) {
+ setParent(0, d->topData()->savedFlags);
+ needShow = true;
+ } else if (oldEffectiveState == Qt::WindowMinimized) {
+ needShow = true;
+ }
+
+ if (newEffectiveState == Qt::WindowMinimized) {
+ //### not ideal...
+ hide();
+ needShow = false;
+ } else if (newEffectiveState == Qt::WindowFullScreen) {
+ d->topData()->savedFlags = windowFlags();
+ setParent(0, Qt::FramelessWindowHint | (windowFlags() & Qt::WindowStaysOnTopHint));
+ d->setFullScreenSize_helper();
+ raise();
+ needShow = true;
+ } else if (newEffectiveState == Qt::WindowMaximized) {
+ createWinId();
+ d->setMaxWindowState_helper();
+ } else { //normal
+ QRect r = d->topData()->normalGeometry;
+ if (r.width() >= 0) {
+ d->topData()->normalGeometry = QRect(0,0,-1,-1);
+ setGeometry(r);
+ }
+ }
+ }
+ data->in_set_window_state = 0;
+
+ if (needShow)
+ show();
+
+ if (newstate & Qt::WindowActive)
+ activateWindow();
+
+ QWindowStateChangeEvent e(oldstate);
+ QApplication::sendEvent(this, &e);
+}
+
+void QWidgetPrivate::setFocus_sys()
+{
+
+}
+
+void QWidgetPrivate::raise_sys()
+{
+ Q_Q(QWidget);
+ //@@@ transaction
+ if (q->isWindow()) {
+ Q_ASSERT(q->testAttribute(Qt::WA_WState_Created));
+ QWidget::qwsDisplay()->setAltitude(q->internalWinId(),
+ QWSChangeAltitudeCommand::Raise);
+ // XXX: subsurfaces?
+#ifdef QT_NO_WINDOWGROUPHINT
+#else
+ QObjectList childObjects = q->children();
+ if (!childObjects.isEmpty()) {
+ QWidgetList toraise;
+ for (int i = 0; i < childObjects.size(); ++i) {
+ QObject *obj = childObjects.at(i);
+ if (obj->isWidgetType()) {
+ QWidget* w = static_cast<QWidget*>(obj);
+ if (w->isWindow())
+ toraise.append(w);
+ }
+ }
+
+ for (int i = 0; i < toraise.size(); ++i) {
+ QWidget *w = toraise.at(i);
+ if (w->isVisible())
+ w->raise();
+ }
+ }
+#endif // QT_NO_WINDOWGROUPHINT
+ }
+}
+
+void QWidgetPrivate::lower_sys()
+{
+ Q_Q(QWidget);
+ if (q->isWindow()) {
+ Q_ASSERT(q->testAttribute(Qt::WA_WState_Created));
+ QWidget::qwsDisplay()->setAltitude(data.winid,
+ QWSChangeAltitudeCommand::Lower);
+ } else if (QWidget *p = q->parentWidget()) {
+ setDirtyOpaqueRegion();
+ p->d_func()->invalidateBuffer(q->geometry());
+ }
+}
+
+void QWidgetPrivate::stackUnder_sys(QWidget*)
+{
+ Q_Q(QWidget);
+ if (QWidget *p = q->parentWidget()) {
+ setDirtyOpaqueRegion();
+ p->d_func()->invalidateBuffer(q->geometry());
+ }
+}
+
+void QWidgetPrivate::moveSurface(QWindowSurface *surface, const QPoint &offset)
+{
+ QWSWindowSurface *s = static_cast<QWSWindowSurface*>(surface);
+ if (!s->move(offset))
+ s->invalidateBuffer();
+
+ QWSDisplay::instance()->moveRegion(s->winId(), offset.x(), offset.y());
+}
+
+void QWidgetPrivate::setGeometry_sys(int x, int y, int w, int h, bool isMove)
+{
+ Q_Q(QWidget);
+ if (extra) { // any size restrictions?
+ w = qMin(w,extra->maxw);
+ h = qMin(h,extra->maxh);
+ w = qMax(w,extra->minw);
+ h = qMax(h,extra->minh);
+ }
+
+ QPoint oldp = q->geometry().topLeft();
+ QSize olds = q->size();
+ QRect r(x, y, w, h);
+
+ bool isResize = olds != r.size();
+ isMove = oldp != r.topLeft(); //### why do we have isMove as a parameter?
+
+ // We only care about stuff that changes the geometry, or may
+ // cause the window manager to change its state
+ if (r.size() == olds && oldp == r.topLeft())
+ return;
+
+ if (!data.in_set_window_state) {
+ q->data->window_state &= ~Qt::WindowMaximized;
+ q->data->window_state &= ~Qt::WindowFullScreen;
+ if (q->isWindow())
+ topData()->normalGeometry = QRect(0, 0, -1, -1);
+ }
+ QPoint oldPos = q->pos();
+ data.crect = r;
+
+ if ((q->windowType() == Qt::Desktop))
+ return;
+
+ if (q->isVisible()) {
+
+ bool toplevelMove = false;
+ QWSWindowSurface *surface = 0;
+
+ if (q->isWindow()) {
+ //### ConfigPending not implemented, do we need it?
+ //setAttribute(Qt::WA_WState_ConfigPending);
+ const QWidgetBackingStore *bs = maybeBackingStore();
+ if (bs)
+ surface = static_cast<QWSWindowSurface*>(bs->windowSurface);
+ if (isMove && !isResize && surface) {
+ const QPoint offset(x - oldp.x(), y - oldp.y());
+ moveSurface(surface, offset);
+ toplevelMove = true; //server moves window, but we must send moveEvent, which might trigger painting
+
+#ifdef Q_BACKINGSTORE_SUBSURFACES
+ QList<QWindowSurface*> surfaces = bs->subSurfaces;
+ for (int i = 0; i < surfaces.size(); ++i)
+ moveSurface(surfaces.at(i), offset);
+#endif
+ } else {
+ updateFrameStrut();
+ }
+ }
+
+ if (!toplevelMove) {
+ if (q->isWindow()) {
+ if (surface)
+ surface->setGeometry(q->frameGeometry());
+ else
+ invalidateBuffer(q->rect()); //###
+
+#ifdef Q_BACKINGSTORE_SUBSURFACES
+ // XXX: should not resize subsurfaces. Children within a layout
+ // will be resized automatically while children with a static
+ // geometry should get a new clip region instead.
+ const QRect clipRect = q->geometry();
+ QWidgetBackingStore *bs = maybeBackingStore();
+ QList<QWindowSurface*> surfaces = bs->subSurfaces;
+ for (int i = 0; i < surfaces.size(); ++i) {
+ QWSWindowSurface *s = static_cast<QWSWindowSurface*>(surfaces.at(i));
+ QRect srect = s->geometry();
+ s->setGeometry(clipRect & srect);
+ }
+#endif
+ }
+#ifdef Q_BACKINGSTORE_SUBSURFACES
+ // XXX: merge this case with the isWindow() case
+ else if (maybeTopData() && maybeTopData()->windowSurface) {
+ QWSWindowSurface *surface;
+ surface = static_cast<QWSWindowSurface*>(q->windowSurface());
+ if (isMove && !isResize) {
+ moveSurface(surface, QPoint(x - oldp.x(), y - oldp.y()));
+ } else {
+ const QPoint p = q->mapToGlobal(QPoint());
+ surface->setGeometry(QRect(p, QSize(w, h)));
+ }
+ }
+#endif
+ else {
+ if (isMove && !isResize)
+ moveRect(QRect(oldPos, olds), x - oldPos.x(), y - oldPos.y());
+ else
+ invalidateBuffer_resizeHelper(oldPos, olds);
+ }
+ }
+
+ //### must have frame geometry correct before sending move/resize events
+ if (isMove) {
+ QMoveEvent e(q->pos(), oldPos);
+ QApplication::sendEvent(q, &e);
+ }
+ if (isResize) {
+ QResizeEvent e(r.size(), olds);
+ QApplication::sendEvent(q, &e);
+ }
+
+ } else { // not visible
+ if (isMove && q->pos() != oldPos)
+ q->setAttribute(Qt::WA_PendingMoveEvent, true);
+ if (isResize)
+ q->setAttribute(Qt::WA_PendingResizeEvent, true);
+ }
+}
+
+void QWidgetPrivate::setConstraints_sys()
+{
+}
+
+QScreen* QWidgetPrivate::getScreen() const
+{
+ Q_Q(const QWidget);
+
+ const QList<QScreen*> subScreens = qt_screen->subScreens();
+ if (subScreens.isEmpty() || q->windowType() == Qt::Desktop)
+ return qt_screen;
+
+ const int screen = QApplication::desktop()->screenNumber(q);
+
+ return qt_screen->subScreens().at(screen < 0 ? 0 : screen);
+}
+
+void QWidgetPrivate::scroll_sys(int dx, int dy)
+{
+ Q_Q(QWidget);
+ scrollChildren(dx, dy);
+ scrollRect(q->rect(), dx, dy);
+}
+
+void QWidgetPrivate::scroll_sys(int dx, int dy, const QRect &r)
+{
+ scrollRect(r, dx, dy);
+}
+
+int QWidget::metric(PaintDeviceMetric m) const
+{
+ Q_D(const QWidget);
+
+ int val;
+ if (m == PdmWidth) {
+ val = data->crect.width();
+ } else if (m == PdmWidthMM) {
+ const QScreen *screen = d->getScreen();
+ val = data->crect.width() * screen->physicalWidth() / screen->width();
+ } else if (m == PdmHeight) {
+ val = data->crect.height();
+ } else if (m == PdmHeightMM) {
+ const QScreen *screen = d->getScreen();
+ val = data->crect.height() * screen->physicalHeight() / screen->height();
+ } else if (m == PdmDepth) {
+ return qwsDisplay()->depth();
+ } else if (m == PdmDpiX || m == PdmPhysicalDpiX) {
+ if (d->extra && d->extra->customDpiX)
+ return d->extra->customDpiX;
+ else if (d->parent)
+ return static_cast<QWidget *>(d->parent)->metric(m);
+ const QScreen *screen = d->getScreen();
+ return qRound(screen->width() / double(screen->physicalWidth() / 25.4));
+ } else if (m == PdmDpiY || m == PdmPhysicalDpiY) {
+ if (d->extra && d->extra->customDpiY)
+ return d->extra->customDpiY;
+ else if (d->parent)
+ return static_cast<QWidget *>(d->parent)->metric(m);
+ const QScreen *screen = d->getScreen();
+ return qRound(screen->height() / double(screen->physicalHeight() / 25.4));
+ } else {
+ val = QPaintDevice::metric(m);// XXX
+ }
+ return val;
+}
+
+void QWidgetPrivate::createSysExtra()
+{
+}
+
+void QWidgetPrivate::deleteSysExtra()
+{
+}
+
+void QWidgetPrivate::createTLSysExtra()
+{
+}
+
+void QWidgetPrivate::deleteTLSysExtra()
+{
+}
+
+void QWidgetPrivate::registerDropSite(bool on)
+{
+ Q_UNUSED(on);
+}
+
+QRegion QWidgetPrivate::localRequestedRegion() const
+{
+ Q_Q(const QWidget);
+ QRegion r(q->rect());
+ if (extra && !extra->mask.isEmpty())
+ r &= extra->mask;
+
+ return r;
+}
+
+QRegion QWidgetPrivate::localAllocatedRegion() const
+{
+ Q_Q(const QWidget);
+
+ QWidgetBackingStore *wbs = q->window()->d_func()->maybeBackingStore();
+
+ QWindowSurface *ws = wbs ? wbs->windowSurface : 0;
+ if (!ws)
+ return QRegion();
+ QRegion r = static_cast<QWSWindowSurface*>(ws)->clipRegion();
+ if (!q->isWindow()) {
+ QPoint off = q->mapTo(q->window(), QPoint());
+ r &= localRequestedRegion().translated(off);
+ r.translate(-off);
+ }
+ return r;
+}
+
+inline bool QRect::intersects(const QRect &r) const
+{
+ return (qMax(x1, r.x1) <= qMin(x2, r.x2) &&
+ qMax(y1, r.y1) <= qMin(y2, r.y2));
+}
+
+void QWidgetPrivate::setMask_sys(const QRegion &region)
+{
+ Q_UNUSED(region);
+ Q_Q(QWidget);
+
+ if (!q->isVisible() || !q->isWindow())
+ return;
+
+ data.fstrut_dirty = true;
+ invalidateBuffer(q->rect());
+ QWindowSurface *surface = extra->topextra->backingStore->windowSurface;
+ if (surface) {
+ // QWSWindowSurface::setGeometry() returns without doing anything
+ // if old geom == new geom. Therefore, we need to reset the old value.
+ surface->QWindowSurface::setGeometry(QRect());
+ surface->setGeometry(q->frameGeometry());
+ }
+}
+
+void QWidgetPrivate::updateFrameStrut()
+{
+ Q_Q(QWidget);
+
+ if(!q->isVisible() || (q->windowType() == Qt::Desktop)) {
+ data.fstrut_dirty = q->isVisible();
+ return;
+ }
+
+#ifndef QT_NO_QWS_MANAGER
+ if (extra && extra->topextra && extra->topextra->qwsManager) {
+ QTLWExtra *topextra = extra->topextra;
+ const QRect oldFrameStrut = topextra->frameStrut;
+ const QRect contents = data.crect;
+ QRegion r = localRequestedRegion().translated(contents.topLeft());
+ r += extra->topextra->qwsManager->region();
+ const QRect frame = r.boundingRect();
+
+ topextra->frameStrut.setCoords(contents.left() - frame.left(),
+ contents.top() - frame.top(),
+ frame.right() - contents.right(),
+ frame.bottom() - contents.bottom());
+ topextra->qwsManager->repaintRegion(QDecoration::All, QDecoration::Normal);
+ }
+#endif
+ data.fstrut_dirty = false;
+}
+
+#ifndef QT_NO_CURSOR
+void QWidgetPrivate::updateCursor() const
+{
+ Q_Q(const QWidget);
+
+ if (QApplication::overrideCursor())
+ return;
+
+ if (qt_last_x
+ && (!QWidget::mouseGrabber() || QWidget::mouseGrabber() == q)
+ && qt_last_cursor != (WId)q->cursor().handle())
+ {
+ const QPoint pos(*qt_last_x, *qt_last_y);
+ const QPoint offset = q->mapToGlobal(QPoint());
+ if (!localAllocatedRegion().contains(pos - offset))
+ return;
+
+ const QWidget *w = q->childAt(q->mapFromGlobal(pos));
+ if (!w || w->cursor().handle() == q->cursor().handle())
+ QWidget::qwsDisplay()->selectCursor(const_cast<QWidget*>(q),
+ q->cursor().handle());
+ }
+}
+#endif
+
+void QWidgetPrivate::setWindowOpacity_sys(qreal level)
+{
+ Q_Q(QWidget);
+ Q_UNUSED(level);
+ createWinId();
+ QWidget::qwsDisplay()->setOpacity(q->data->winid, topData()->opacity);
+}
+
+//static QSingleCleanupHandler<QWSPaintEngine> qt_paintengine_cleanup_handler;
+//static QWSPaintEngine *qt_widget_paintengine = 0;
+QPaintEngine *QWidget::paintEngine() const
+{
+ qWarning("QWidget::paintEngine: Should no longer be called");
+ return 0; //##### @@@
+// if (!qt_widget_paintengine) {
+// qt_widget_paintengine = new QRasterPaintEngine();
+// qt_paintengine_cleanup_handler.set(&qt_widget_paintengine);
+// }
+// if (qt_widget_paintengine->isActive()) {
+// if (d->extraPaintEngine)
+// return d->extraPaintEngine;
+// const_cast<QWidget *>(this)->d_func()->extraPaintEngine = new QRasterPaintEngine();
+// return d->extraPaintEngine;
+// }
+// return qt_widget_paintengine;
+}
+
+QWindowSurface *QWidgetPrivate::createDefaultWindowSurface_sys()
+{
+ Q_Q(QWidget);
+ if (q->windowType() == Qt::Desktop)
+ return 0;
+ q->ensurePolished();
+ return qt_screen->createSurface(q);
+}
+
+void QWidgetPrivate::setModal_sys()
+{
+}
+
+
+QT_END_NAMESPACE
diff --git a/src/gui/kernel/qwidget_win.cpp b/src/gui/kernel/qwidget_win.cpp
new file mode 100644
index 0000000000..ffbb34146f
--- /dev/null
+++ b/src/gui/kernel/qwidget_win.cpp
@@ -0,0 +1,2124 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qapplication.h"
+#include "qapplication_p.h"
+#include "qbitmap.h"
+#include "qcursor.h"
+#include "qdesktopwidget.h"
+#include "qevent.h"
+#include "qimage.h"
+#include "qlayout.h"
+#include "qlibrary.h"
+#include "qpainter.h"
+#include "qstack.h"
+#include "qt_windows.h"
+#include "qwidget.h"
+#include "qwidget_p.h"
+#include "private/qbackingstore_p.h"
+#include "private/qwindowsurface_raster_p.h"
+
+#ifndef QT_NO_DIRECT3D
+#include "private/qpaintengine_d3d_p.h"
+#include "private/qwindowsurface_d3d_p.h"
+#endif
+
+
+#include <qdebug.h>
+
+#include <private/qapplication_p.h>
+#include <private/qwininputcontext_p.h>
+#include <private/qpaintengine_raster_p.h>
+
+#if defined(Q_OS_WINCE)
+#include "qguifunctions_wince.h"
+QT_USE_NAMESPACE
+extern void qt_wince_maximize(QWidget *widget); //defined in qguifunctions_wince.cpp
+extern void qt_wince_minimize(HWND hwnd); //defined in qguifunctions_wince.cpp
+extern void qt_wince_full_screen(HWND hwnd, bool fullScreen, UINT swpf); //defined in qguifunctions_wince.cpp
+extern bool qt_wince_is_mobile(); //defined in qguifunctions_wince.cpp
+#endif
+
+typedef BOOL (WINAPI *PtrSetLayeredWindowAttributes)(HWND hwnd, COLORREF crKey, BYTE bAlpha, DWORD dwFlags);
+static PtrSetLayeredWindowAttributes ptrSetLayeredWindowAttributes = 0;
+
+#ifndef QT_NO_DIRECTDRAW
+#include <ddraw.h>
+#include <private/qimage_p.h>
+static IDirectDraw *qt_ddraw_object;
+static IDirectDrawSurface *qt_ddraw_primary;
+#endif
+
+
+
+#if defined(QT_NON_COMMERCIAL)
+#include "qnc_win.h"
+#endif
+
+#if !defined(WS_EX_TOOLWINDOW)
+#define WS_EX_TOOLWINDOW 0x00000080
+#endif
+
+#if !defined(GWLP_WNDPROC)
+#define GWLP_WNDPROC GWL_WNDPROC
+#endif
+
+//#define TABLET_DEBUG
+#define PACKETDATA (PK_X | PK_Y | PK_BUTTONS | PK_NORMAL_PRESSURE | PK_TANGENT_PRESSURE \
+ | PK_ORIENTATION | PK_CURSOR | PK_Z)
+#define PACKETMODE 0
+#include <wintab.h>
+#include <pktdef.h>
+
+QT_BEGIN_NAMESPACE
+
+typedef HCTX (API *PtrWTOpen)(HWND, LPLOGCONTEXT, BOOL);
+typedef BOOL (API *PtrWTClose)(HCTX);
+typedef UINT (API *PtrWTInfo)(UINT, UINT, LPVOID);
+typedef BOOL (API *PtrWTEnable)(HCTX, BOOL);
+typedef BOOL (API *PtrWTOverlap)(HCTX, BOOL);
+typedef int (API *PtrWTPacketsGet)(HCTX, int, LPVOID);
+typedef BOOL (API *PtrWTGet)(HCTX, LPLOGCONTEXT);
+typedef int (API *PtrWTQueueSizeGet)(HCTX);
+typedef BOOL (API *PtrWTQueueSizeSet)(HCTX, int);
+
+static PtrWTOpen ptrWTOpen = 0;
+static PtrWTClose ptrWTClose = 0;
+static PtrWTInfo ptrWTInfo = 0;
+static PtrWTQueueSizeGet ptrWTQueueSizeGet = 0;
+static PtrWTQueueSizeSet ptrWTQueueSizeSet = 0;
+static void init_wintab_functions();
+static void qt_tablet_init();
+static void qt_tablet_cleanup();
+extern HCTX qt_tablet_context;
+extern bool qt_tablet_tilt_support;
+
+static QWidget *qt_tablet_widget = 0;
+QWidget* qt_get_tablet_widget()
+{
+ return qt_tablet_widget;
+}
+
+extern bool qt_is_gui_used;
+static void init_wintab_functions()
+{
+#if defined(Q_OS_WINCE)
+ return;
+#else
+ if (!qt_is_gui_used)
+ return;
+ QLibrary library(QLatin1String("wintab32"));
+ QT_WA({
+ ptrWTOpen = (PtrWTOpen)library.resolve("WTOpenW");
+ ptrWTInfo = (PtrWTInfo)library.resolve("WTInfoW");
+ } , {
+ ptrWTOpen = (PtrWTOpen)library.resolve("WTOpenA");
+ ptrWTInfo = (PtrWTInfo)library.resolve("WTInfoA");
+ });
+
+ ptrWTClose = (PtrWTClose)library.resolve("WTClose");
+ ptrWTQueueSizeGet = (PtrWTQueueSizeGet)library.resolve("WTQueueSizeGet");
+ ptrWTQueueSizeSet = (PtrWTQueueSizeSet)library.resolve("WTQueueSizeSet");
+#endif // Q_OS_WINCE
+}
+
+static void qt_tablet_init()
+{
+ static bool firstTime = true;
+ if (!firstTime)
+ return;
+ firstTime = false;
+ qt_tablet_widget = new QWidget(0);
+ qt_tablet_widget->createWinId();
+ qt_tablet_widget->setObjectName(QLatin1String("Qt internal tablet widget"));
+ LOGCONTEXT lcMine;
+ qAddPostRoutine(qt_tablet_cleanup);
+ struct tagAXIS tpOri[3];
+ init_wintab_functions();
+ if (ptrWTInfo && ptrWTOpen && ptrWTQueueSizeGet && ptrWTQueueSizeSet) {
+ // make sure we have WinTab
+ if (!ptrWTInfo(0, 0, NULL)) {
+#ifdef TABLET_DEBUG
+ qWarning("QWidget: Wintab services not available");
+#endif
+ return;
+ }
+
+ // some tablets don't support tilt, check if it is possible,
+ qt_tablet_tilt_support = ptrWTInfo(WTI_DEVICES, DVC_ORIENTATION, &tpOri);
+ if (qt_tablet_tilt_support) {
+ // check for azimuth and altitude
+ qt_tablet_tilt_support = tpOri[0].axResolution && tpOri[1].axResolution;
+ }
+ // build our context from the default context
+ ptrWTInfo(WTI_DEFSYSCTX, 0, &lcMine);
+ // Go for the raw coordinates, the tablet event will return good stuff
+ lcMine.lcOptions |= CXO_MESSAGES | CXO_CSRMESSAGES;
+ lcMine.lcPktData = PACKETDATA;
+ lcMine.lcPktMode = PACKETMODE;
+ lcMine.lcMoveMask = PACKETDATA;
+ lcMine.lcOutOrgX = 0;
+ lcMine.lcOutExtX = lcMine.lcInExtX;
+ lcMine.lcOutOrgY = 0;
+ lcMine.lcOutExtY = -lcMine.lcInExtY;
+ qt_tablet_context = ptrWTOpen(qt_tablet_widget->winId(), &lcMine, true);
+#ifdef TABLET_DEBUG
+ qDebug("Tablet is %p", qt_tablet_context);
+#endif
+ if (!qt_tablet_context) {
+#ifdef TABLET_DEBUG
+ qWarning("QWidget: Failed to open the tablet");
+#endif
+ return;
+ }
+ // Set the size of the Packet Queue to the correct size...
+ int currSize = ptrWTQueueSizeGet(qt_tablet_context);
+ if (!ptrWTQueueSizeSet(qt_tablet_context, QT_TABLET_NPACKETQSIZE)) {
+ // Ideally one might want to use a smaller
+ // multiple, but for now, since we managed to destroy
+ // the existing Q with the previous call, set it back
+ // to the other size, which should work. If not,
+ // there will be trouble.
+ if (!ptrWTQueueSizeSet(qt_tablet_context, currSize)) {
+ Q_ASSERT_X(0, "Qt::Internal", "There is no packet queue for"
+ " the tablet. The tablet will not work");
+ }
+ }
+ }
+}
+
+static void qt_tablet_cleanup()
+{
+ if (ptrWTClose)
+ ptrWTClose(qt_tablet_context);
+ delete qt_tablet_widget;
+ qt_tablet_widget = 0;
+}
+
+const QString qt_reg_winclass(QWidget *w); // defined in qapplication_win.cpp
+
+#ifndef QT_NO_DRAGANDDROP
+void qt_olednd_unregister(QWidget* widget, QOleDropTarget *dst); // dnd_win
+QOleDropTarget* qt_olednd_register(QWidget* widget);
+#endif
+
+extern bool qt_nograb();
+extern HRGN qt_win_bitmapToRegion(const QBitmap& bitmap);
+
+static QWidget *mouseGrb = 0;
+static QCursor *mouseGrbCur = 0;
+static QWidget *keyboardGrb = 0;
+static HHOOK journalRec = 0;
+
+extern "C" LRESULT CALLBACK QtWndProc(HWND, UINT, WPARAM, LPARAM);
+
+#define XCOORD_MAX 16383
+#define WRECT_MAX 16383
+
+/*****************************************************************************
+ QWidget member functions
+ *****************************************************************************/
+
+#ifndef Q_OS_WINCE
+void QWidgetPrivate::create_sys(WId window, bool initializeWindow, bool destroyOldWindow)
+{
+ Q_Q(QWidget);
+ static int sw = -1, sh = -1;
+
+ Qt::WindowType type = q->windowType();
+ Qt::WindowFlags flags = data.window_flags;
+
+ bool topLevel = (flags & Qt::Window);
+ bool popup = (type == Qt::Popup);
+ bool dialog = (type == Qt::Dialog
+ || type == Qt::Sheet
+ || (flags & Qt::MSWindowsFixedSizeDialogHint));
+ bool desktop = (type == Qt::Desktop);
+ bool tool = (type == Qt::Tool || type == Qt::Drawer);
+
+ HINSTANCE appinst = qWinAppInst();
+ HWND parentw, destroyw = 0;
+ WId id;
+
+ QString windowClassName = qt_reg_winclass(q);
+
+ if (!window) // always initialize
+ initializeWindow = true;
+
+ if (popup)
+ flags |= Qt::WindowStaysOnTopHint; // a popup stays on top
+
+ if (sw < 0) { // get the (primary) screen size
+ sw = GetSystemMetrics(SM_CXSCREEN);
+ sh = GetSystemMetrics(SM_CYSCREEN);
+ }
+
+ if (desktop && !q->testAttribute(Qt::WA_DontShowOnScreen)) { // desktop widget
+ popup = false; // force this flags off
+ if (QSysInfo::WindowsVersion != QSysInfo::WV_NT && QSysInfo::WindowsVersion != QSysInfo::WV_95)
+ data.crect.setRect(GetSystemMetrics(76 /* SM_XVIRTUALSCREEN */), GetSystemMetrics(77 /* SM_YVIRTUALSCREEN */),
+ GetSystemMetrics(78 /* SM_CXVIRTUALSCREEN */), GetSystemMetrics(79 /* SM_CYVIRTUALSCREEN */));
+ else
+ data.crect.setRect(0, 0, GetSystemMetrics(SM_CXSCREEN), GetSystemMetrics(SM_CYSCREEN));
+ }
+
+ parentw = q->parentWidget() ? q->parentWidget()->effectiveWinId() : 0;
+
+#ifdef UNICODE
+ QString title;
+ const TCHAR *ttitle = 0;
+#endif
+ QByteArray title95;
+ int style = WS_CHILD;
+ int exsty = 0;
+
+ if (window) {
+ style = GetWindowLongA(window, GWL_STYLE);
+ if (!style)
+ qErrnoWarning("QWidget::create: GetWindowLong failed");
+ topLevel = false; // #### needed for some IE plugins??
+ } else if (popup || (type == Qt::ToolTip) || (type == Qt::SplashScreen)) {
+ style = WS_POPUP;
+ } else if (topLevel && !desktop) {
+ if (flags & Qt::FramelessWindowHint)
+ style = WS_POPUP; // no border
+ else if (flags & Qt::WindowTitleHint)
+ style = WS_OVERLAPPED;
+ else
+ style = 0;
+ }
+ if (!desktop) {
+ // if (!testAttribute(Qt::WA_PaintUnclipped))
+ // ### Commented out for now as it causes some problems, but
+ // this should be correct anyway, so dig some more into this
+#ifndef Q_FLATTEN_EXPOSE
+ style |= WS_CLIPSIBLINGS | WS_CLIPCHILDREN ;
+#endif
+ if (topLevel) {
+ if ((type == Qt::Window || dialog || tool)) {
+ if (!(flags & Qt::FramelessWindowHint)) {
+ if (!(flags & Qt::MSWindowsFixedSizeDialogHint)) {
+ style |= WS_THICKFRAME;
+ if(!(flags &
+ ( Qt::WindowSystemMenuHint
+ | Qt::WindowTitleHint
+ | Qt::WindowMinMaxButtonsHint
+ | Qt::WindowCloseButtonHint
+ | Qt::WindowContextHelpButtonHint)))
+ style |= WS_POPUP;
+ } else {
+ style |= WS_POPUP | WS_DLGFRAME;
+ }
+ }
+ if (flags & Qt::WindowTitleHint)
+ style |= WS_CAPTION;
+ if (flags & Qt::WindowSystemMenuHint)
+ style |= WS_SYSMENU;
+ if (flags & Qt::WindowMinimizeButtonHint)
+ style |= WS_MINIMIZEBOX;
+ if (shouldShowMaximizeButton())
+ style |= WS_MAXIMIZEBOX;
+ if (tool)
+ exsty |= WS_EX_TOOLWINDOW;
+ if (flags & Qt::WindowContextHelpButtonHint)
+ exsty |= WS_EX_CONTEXTHELP;
+ } else {
+ exsty |= WS_EX_TOOLWINDOW;
+ }
+ }
+ }
+
+ if (flags & Qt::WindowTitleHint) {
+ QT_WA({
+ title = q->isWindow() ? qAppName() : q->objectName();
+ ttitle = (TCHAR*)title.utf16();
+ } , {
+ title95 = q->isWindow() ? qAppName().toLocal8Bit() : q->objectName().toLatin1();
+ });
+ }
+
+ // The Qt::WA_WState_Created flag is checked by translateConfigEvent() in
+ // qapplication_win.cpp. We switch it off temporarily to avoid move
+ // and resize events during creationt
+ q->setAttribute(Qt::WA_WState_Created, false);
+
+ if (window) { // override the old window
+ if (destroyOldWindow)
+ destroyw = data.winid;
+ id = window;
+ setWinId(window);
+ LONG res = SetWindowLongA(window, GWL_STYLE, style);
+ if (!res)
+ qErrnoWarning("QWidget::create: Failed to set window style");
+#ifdef _WIN64
+ res = SetWindowLongPtrA( window, GWLP_WNDPROC, (LONG_PTR)QtWndProc );
+#else
+ res = SetWindowLongA( window, GWL_WNDPROC, (LONG)QtWndProc );
+#endif
+ if (!res)
+ qErrnoWarning("QWidget::create: Failed to set window procedure");
+ } else if (desktop) { // desktop widget
+ id = GetDesktopWindow();
+// QWidget *otherDesktop = QWidget::find(id); // is there another desktop?
+// if (otherDesktop && otherDesktop->testWFlags(Qt::WPaintDesktop)) {
+// otherDesktop->d_func()->setWinId(0); // remove id from widget mapper
+// d->setWinId(id); // make sure otherDesktop is
+// otherDesktop->d_func()->setWinId(id); // found first
+// } else {
+ setWinId(id);
+// }
+ } else if (topLevel) { // create top-level widget
+ if (popup)
+ parentw = 0;
+
+ const bool wasMoved = q->testAttribute(Qt::WA_Moved);
+ int x = wasMoved ? data.crect.left() : CW_USEDEFAULT;
+ int y = wasMoved ? data.crect.top() : CW_USEDEFAULT;
+ int w = CW_USEDEFAULT;
+ int h = CW_USEDEFAULT;
+
+ // Adjust for framestrut when needed
+ RECT rect = {0,0,0,0};
+ bool isVisibleOnScreen = !q->testAttribute(Qt::WA_DontShowOnScreen);
+ if (isVisibleOnScreen && AdjustWindowRectEx(&rect, style & ~WS_OVERLAPPED, FALSE, exsty)) {
+ QTLWExtra *td = maybeTopData();
+ if (wasMoved && (td && !td->posFromMove)) {
+ x = data.crect.x() + rect.left;
+ y = data.crect.y() + rect.top;
+ }
+
+ if (q->testAttribute(Qt::WA_Resized)) {
+ w = data.crect.width() + (rect.right - rect.left);
+ h = data.crect.height() + (rect.bottom - rect.top);
+ }
+ }
+ //update position & initial size of POPUP window
+ if (isVisibleOnScreen && topLevel && initializeWindow && (style & WS_POPUP)) {
+ if (!q->testAttribute(Qt::WA_Resized)) {
+ w = sw/2;
+ h = 4*sh/10;
+ }
+ if (!wasMoved) {
+ x = sw/2 - w/2;
+ y = sh/2 - h/2;
+ }
+ }
+
+ QT_WA({
+ const TCHAR *cname = (TCHAR*)windowClassName.utf16();
+ id = CreateWindowEx(exsty, cname, ttitle, style,
+ x, y, w, h,
+ parentw, 0, appinst, 0);
+ } , {
+ id = CreateWindowExA(exsty, windowClassName.toLatin1(), title95, style,
+ x, y, w, h,
+ parentw, 0, appinst, 0);
+ });
+ if (!id)
+ qErrnoWarning("QWidget::create: Failed to create window");
+ setWinId(id);
+ if ((flags & Qt::WindowStaysOnTopHint) || (type == Qt::ToolTip)) {
+ SetWindowPos(id, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE);
+ if (flags & Qt::WindowStaysOnBottomHint)
+ qWarning() << "QWidget: Incompatible window flags: the window can't be on top and on bottom at the same time";
+ } else if (flags & Qt::WindowStaysOnBottomHint)
+ SetWindowPos(id, HWND_BOTTOM, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE);
+ winUpdateIsOpaque();
+ } else if (q->testAttribute(Qt::WA_NativeWindow) || paintOnScreen()) { // create child widget
+ QT_WA({
+ const TCHAR *cname = (TCHAR*)windowClassName.utf16();
+ id = CreateWindowEx(exsty, cname, ttitle, style,
+ data.crect.left(), data.crect.top(), data.crect.width(), data.crect.height(),
+ parentw, NULL, appinst, NULL);
+ } , {
+ id = CreateWindowExA(exsty, windowClassName.toLatin1(), title95, style,
+ data.crect.left(), data.crect.top(), data.crect.width(), data.crect.height(),
+ parentw, NULL, appinst, NULL);
+ });
+ if (!id)
+ qErrnoWarning("QWidget::create: Failed to create window");
+ SetWindowPos(id, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
+ setWinId(id);
+ }
+
+ if (desktop) {
+ q->setAttribute(Qt::WA_WState_Visible);
+ } else if (topLevel && !q->testAttribute(Qt::WA_DontShowOnScreen)) {
+ RECT cr;
+ GetClientRect(id, &cr);
+ // one cannot trust cr.left and cr.top, use a correction POINT instead
+ POINT pt;
+ pt.x = 0;
+ pt.y = 0;
+ ClientToScreen(id, &pt);
+
+ if (data.crect.width() == 0 || data.crect.height() == 0) {
+ data.crect = QRect(pt.x, pt.y, data.crect.width(), data.crect.height());
+ } else {
+ data.crect = QRect(QPoint(pt.x, pt.y),
+ QPoint(pt.x + cr.right - 1, pt.y + cr.bottom - 1));
+ }
+
+ if (data.fstrut_dirty) {
+ // be nice to activeqt
+ updateFrameStrut();
+ }
+ }
+
+ q->setAttribute(Qt::WA_WState_Created); // accept move/resize events
+ hd = 0; // no display context
+
+ if (window) { // got window from outside
+ if (IsWindowVisible(window))
+ q->setAttribute(Qt::WA_WState_Visible);
+ else
+ q->setAttribute(Qt::WA_WState_Visible, false);
+ }
+
+ if (extra && !extra->mask.isEmpty())
+ setMask_sys(extra->mask);
+
+#if defined(QT_NON_COMMERCIAL)
+ QT_NC_WIDGET_CREATE
+#endif
+
+ if (q->hasFocus() && q->testAttribute(Qt::WA_InputMethodEnabled))
+ q->inputContext()->setFocusWidget(q);
+
+ if (destroyw) {
+ DestroyWindow(destroyw);
+ }
+
+ if (q != qt_tablet_widget && QWidgetPrivate::mapper)
+ qt_tablet_init();
+
+ if (q->testAttribute(Qt::WA_DropSiteRegistered))
+ registerDropSite(true);
+
+ if (maybeTopData() && maybeTopData()->opacity != 255)
+ q->setWindowOpacity(maybeTopData()->opacity/255.);
+
+ if (topLevel && (data.crect.width() == 0 || data.crect.height() == 0)) {
+ q->setAttribute(Qt::WA_OutsideWSRange, true);
+ }
+
+ if (!topLevel && q->testAttribute(Qt::WA_NativeWindow) && q->testAttribute(Qt::WA_Mapped)) {
+ Q_ASSERT(q->internalWinId());
+ ShowWindow(q->internalWinId(), SW_SHOW);
+ }
+}
+
+#endif //Q_OS_WINCE
+
+
+void QWidget::destroy(bool destroyWindow, bool destroySubWindows)
+{
+ Q_D(QWidget);
+ if (!isWindow() && parentWidget())
+ parentWidget()->d_func()->invalidateBuffer(geometry());
+ d->deactivateWidgetCleanup();
+ if (testAttribute(Qt::WA_WState_Created)) {
+ setAttribute(Qt::WA_WState_Created, false);
+ for(int i = 0; i < d->children.size(); ++i) { // destroy all widget children
+ register QObject *obj = d->children.at(i);
+ if (obj->isWidgetType())
+ ((QWidget*)obj)->destroy(destroySubWindows,
+ destroySubWindows);
+ }
+ if (mouseGrb == this)
+ releaseMouse();
+ if (keyboardGrb == this)
+ releaseKeyboard();
+ if (testAttribute(Qt::WA_ShowModal)) // just be sure we leave modal
+ QApplicationPrivate::leaveModal(this);
+ else if ((windowType() == Qt::Popup))
+ qApp->d_func()->closePopup(this);
+ if (destroyWindow && !(windowType() == Qt::Desktop) && internalWinId()) {
+ DestroyWindow(internalWinId());
+ }
+#ifdef Q_OS_WINCE
+ if (destroyWindow && (windowType() == Qt::Desktop) && !GetDesktopWindow()) {
+ DestroyWindow(internalWinId());
+ }
+
+#endif
+ d->setWinId(0);
+ }
+}
+
+void QWidgetPrivate::reparentChildren()
+{
+ Q_Q(QWidget);
+ QObjectList chlist = q->children();
+ for(int i = 0; i < chlist.size(); ++i) { // reparent children
+ QObject *obj = chlist.at(i);
+ if (obj->isWidgetType()) {
+ QWidget *w = (QWidget *)obj;
+ if ((w->windowType() == Qt::Popup)) {
+ ;
+ } else if (w->isWindow()) {
+ bool showIt = w->isVisible();
+ QPoint old_pos = w->pos();
+ w->setParent(q, w->windowFlags());
+ w->move(old_pos);
+ if (showIt)
+ w->show();
+ } else {
+ w->d_func()->invalidateBuffer(w->rect());
+ SetParent(w->effectiveWinId(), q->effectiveWinId());
+ w->d_func()->reparentChildren();
+ }
+ }
+ }
+}
+
+void QWidgetPrivate::setParent_sys(QWidget *parent, Qt::WindowFlags f)
+{
+ Q_Q(QWidget);
+ bool wasCreated = q->testAttribute(Qt::WA_WState_Created);
+ if (q->isVisible() && q->parentWidget() && parent != q->parentWidget())
+ q->parentWidget()->d_func()->invalidateBuffer(q->geometry());
+
+ WId old_winid = data.winid;
+ // hide and reparent our own window away. Otherwise we might get
+ // destroyed when emitting the child remove event below. See QWorkspace.
+ if (q->isVisible() && data.winid) {
+ ShowWindow(data.winid, SW_HIDE);
+ SetParent(data.winid, 0);
+ }
+ bool dropSiteWasRegistered = false;
+ if (q->testAttribute(Qt::WA_DropSiteRegistered)) {
+ dropSiteWasRegistered = true;
+ q->setAttribute(Qt::WA_DropSiteRegistered, false); // ole dnd unregister (we will register again below)
+ }
+
+ if ((q->windowType() == Qt::Desktop))
+ old_winid = 0;
+ setWinId(0);
+
+ QObjectPrivate::setParent_helper(parent);
+ bool explicitlyHidden = q->testAttribute(Qt::WA_WState_Hidden) && q->testAttribute(Qt::WA_WState_ExplicitShowHide);
+
+ data.window_flags = f;
+ data.fstrut_dirty = true;
+ q->setAttribute(Qt::WA_WState_Created, false);
+ q->setAttribute(Qt::WA_WState_Visible, false);
+ q->setAttribute(Qt::WA_WState_Hidden, false);
+ adjustFlags(data.window_flags, q);
+ // keep compatibility with previous versions, we need to preserve the created state
+ // (but we recreate the winId for the widget being reparented, again for compatibility)
+ if (wasCreated || (!q->isWindow() && parent->testAttribute(Qt::WA_WState_Created)))
+ createWinId();
+ if (q->isWindow() || (!parent || parent->isVisible()) || explicitlyHidden)
+ q->setAttribute(Qt::WA_WState_Hidden);
+ q->setAttribute(Qt::WA_WState_ExplicitShowHide, explicitlyHidden);
+
+ if (wasCreated) {
+ reparentChildren();
+ }
+
+ if (extra && !extra->mask.isEmpty()) {
+ QRegion r = extra->mask;
+ extra->mask = QRegion();
+ q->setMask(r);
+ }
+ if (extra && extra->topextra && !extra->topextra->caption.isEmpty()) {
+ setWindowIcon_sys(true);
+ setWindowTitle_helper(extra->topextra->caption);
+ }
+ if (old_winid)
+ DestroyWindow(old_winid);
+
+ if (q->testAttribute(Qt::WA_AcceptDrops) || dropSiteWasRegistered
+ || (!q->isWindow() && q->parentWidget() && q->parentWidget()->testAttribute(Qt::WA_DropSiteRegistered)))
+ q->setAttribute(Qt::WA_DropSiteRegistered, true);
+
+
+ if (data.window_flags & Qt::CustomizeWindowHint
+ && data.window_flags & Qt::WindowTitleHint) {
+ HMENU systemMenu = GetSystemMenu((HWND)q->internalWinId(), FALSE);
+ if (data.window_flags & Qt::WindowCloseButtonHint)
+ EnableMenuItem(systemMenu, SC_CLOSE, MF_BYCOMMAND|MF_ENABLED);
+ else
+ EnableMenuItem(systemMenu, SC_CLOSE, MF_BYCOMMAND|MF_GRAYED);
+ }
+
+#ifdef Q_OS_WINCE
+ // Show borderless toplevel windows in tasklist & NavBar
+ if (!parent) {
+ QString txt = q->windowTitle().isEmpty()?qAppName():q->windowTitle();
+ SetWindowText(q->internalWinId(), (TCHAR*)txt.utf16());
+ }
+#endif
+ invalidateBuffer(q->rect());
+}
+
+
+QPoint QWidget::mapToGlobal(const QPoint &pos) const
+{
+ Q_D(const QWidget);
+ QWidget *parentWindow = window();
+ QWExtra *extra = parentWindow->d_func()->extra;
+ if (!isVisible() || parentWindow->isMinimized() || !testAttribute(Qt::WA_WState_Created) || !internalWinId()
+ || (extra && extra->proxyWidget)) {
+ if (extra && extra->topextra && extra->topextra->embedded) {
+ QPoint pt = mapTo(parentWindow, pos);
+ POINT p = {pt.x(), pt.y()};
+ ClientToScreen(parentWindow->effectiveWinId(), &p);
+ return QPoint(p.x, p.y);
+ } else {
+ QPoint toGlobal = mapTo(parentWindow, pos) + parentWindow->pos();
+ // Adjust for window decorations
+ toGlobal += parentWindow->geometry().topLeft() - parentWindow->frameGeometry().topLeft();
+ return toGlobal;
+ }
+ }
+ POINT p;
+ QPoint tmp = d->mapToWS(pos);
+ p.x = tmp.x();
+ p.y = tmp.y();
+ ClientToScreen(internalWinId(), &p);
+ return QPoint(p.x, p.y);
+}
+
+QPoint QWidget::mapFromGlobal(const QPoint &pos) const
+{
+ Q_D(const QWidget);
+ QWidget *parentWindow = window();
+ QWExtra *extra = parentWindow->d_func()->extra;
+ if (!isVisible() || parentWindow->isMinimized() || !testAttribute(Qt::WA_WState_Created) || !internalWinId()
+ || (extra && extra->proxyWidget)) {
+ if (extra && extra->topextra && extra->topextra->embedded) {
+ POINT p = {pos.x(), pos.y()};
+ ScreenToClient(parentWindow->effectiveWinId(), &p);
+ return mapFrom(parentWindow, QPoint(p.x, p.y));
+ } else {
+ QPoint fromGlobal = mapFrom(parentWindow, pos - parentWindow->pos());
+ // Adjust for window decorations
+ fromGlobal -= parentWindow->geometry().topLeft() - parentWindow->frameGeometry().topLeft();
+ return fromGlobal;
+ }
+ }
+ POINT p;
+ p.x = pos.x();
+ p.y = pos.y();
+ ScreenToClient(internalWinId(), &p);
+ return d->mapFromWS(QPoint(p.x, p.y));
+}
+
+void QWidgetPrivate::updateSystemBackground() {}
+
+extern void qt_win_set_cursor(QWidget *, bool); // qapplication_win.cpp
+
+#ifndef QT_NO_CURSOR
+void QWidgetPrivate::setCursor_sys(const QCursor &cursor)
+{
+ Q_UNUSED(cursor);
+ Q_Q(QWidget);
+ qt_win_set_cursor(q, false);
+}
+
+void QWidgetPrivate::unsetCursor_sys()
+{
+ Q_Q(QWidget);
+ qt_win_set_cursor(q, false);
+}
+#endif
+
+void QWidgetPrivate::setWindowTitle_sys(const QString &caption)
+{
+ Q_Q(QWidget);
+ if (!q->isWindow())
+ return;
+
+ Q_ASSERT(q->testAttribute(Qt::WA_WState_Created));
+ QT_WA({
+ SetWindowText(q->internalWinId(), (TCHAR*)caption.utf16());
+ } , {
+ SetWindowTextA(q->internalWinId(), caption.toLocal8Bit());
+ });
+}
+
+/*
+ Create an icon mask the way Windows wants it using CreateBitmap.
+*/
+
+HBITMAP qt_createIconMask(const QBitmap &bitmap)
+{
+ QImage bm = bitmap.toImage().convertToFormat(QImage::Format_Mono);
+ int w = bm.width();
+ int h = bm.height();
+ int bpl = ((w+15)/16)*2; // bpl, 16 bit alignment
+ uchar *bits = new uchar[bpl*h];
+ bm.invertPixels();
+ for (int y=0; y<h; y++)
+ memcpy(bits+y*bpl, bm.scanLine(y), bpl);
+ HBITMAP hbm = CreateBitmap(w, h, 1, 1, bits);
+ delete [] bits;
+ return hbm;
+}
+
+HICON qt_createIcon(QIcon icon, int xSize, int ySize, QPixmap **cache)
+{
+ HICON result = 0;
+ if (!icon.isNull()) { // valid icon
+ QSize size = icon.actualSize(QSize(xSize, ySize));
+ QPixmap pm = icon.pixmap(size);
+ if (pm.isNull())
+ return 0;
+
+ QBitmap mask = pm.mask();
+ if (mask.isNull()) {
+ mask = QBitmap(pm.size());
+ mask.fill(Qt::color1);
+ }
+
+ HBITMAP im = qt_createIconMask(mask);
+ ICONINFO ii;
+ ii.fIcon = true;
+ ii.hbmMask = im;
+ ii.hbmColor = pm.toWinHBITMAP(QPixmap::Alpha);
+ ii.xHotspot = 0;
+ ii.yHotspot = 0;
+ result = CreateIconIndirect(&ii);
+
+ if (cache) {
+ delete *cache;
+ *cache = new QPixmap(pm);;
+ }
+ DeleteObject(ii.hbmColor);
+ DeleteObject(im);
+ }
+ return result;
+}
+
+void QWidgetPrivate::setWindowIcon_sys(bool forceReset)
+{
+ Q_Q(QWidget);
+ if (!q->testAttribute(Qt::WA_WState_Created) || !q->isWindow())
+ return;
+ QTLWExtra* x = topData();
+ if (x->iconPixmap && !forceReset)
+ // already been set
+ return;
+
+ if (x->winIconBig) {
+ DestroyIcon(x->winIconBig);
+ x->winIconBig = 0;
+ }
+ if (x->winIconSmall) {
+ DestroyIcon(x->winIconSmall);
+ x->winIconSmall = 0;
+ }
+
+ x->winIconSmall = qt_createIcon(q->windowIcon(),
+ GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON),
+ &(x->iconPixmap));
+ x->winIconBig = qt_createIcon(q->windowIcon(),
+ GetSystemMetrics(SM_CXICON), GetSystemMetrics(SM_CYICON),
+ &(x->iconPixmap));
+ if (x->winIconBig) {
+ SendMessageA(q->internalWinId(), WM_SETICON, 0 /* ICON_SMALL */, (LPARAM)x->winIconSmall);
+ SendMessageA(q->internalWinId(), WM_SETICON, 1 /* ICON_BIG */, (LPARAM)x->winIconBig);
+ } else {
+ SendMessageA(q->internalWinId(), WM_SETICON, 0 /* ICON_SMALL */, (LPARAM)x->winIconSmall);
+ SendMessageA(q->internalWinId(), WM_SETICON, 1 /* ICON_BIG */, (LPARAM)x->winIconSmall);
+ }
+}
+
+
+void QWidgetPrivate::setWindowIconText_sys(const QString &iconText)
+{
+ Q_UNUSED(iconText);
+}
+
+
+QCursor *qt_grab_cursor()
+{
+ return mouseGrbCur;
+}
+
+// The procedure does nothing, but is required for mousegrabbing to work
+#ifndef Q_OS_WINCE
+LRESULT CALLBACK qJournalRecordProc(int nCode, WPARAM wParam, LPARAM lParam)
+{
+ return CallNextHookEx(journalRec, nCode, wParam, lParam);
+}
+#endif //Q_OS_WINCE
+
+/* Works only as long as pointer is inside the application's window,
+ which is good enough for QDockWidget.
+
+ Doesn't call SetWindowsHookExA() - this function causes a system-wide
+ freeze if any other app on the system installs a hook and fails to
+ process events. */
+void QWidgetPrivate::grabMouseWhileInWindow()
+{
+ Q_Q(QWidget);
+ if (!qt_nograb()) {
+ if (mouseGrb)
+ mouseGrb->releaseMouse();
+ Q_ASSERT(q->testAttribute(Qt::WA_WState_Created));
+ SetCapture(q->effectiveWinId());
+ mouseGrb = q;
+#ifndef QT_NO_CURSOR
+ mouseGrbCur = new QCursor(mouseGrb->cursor());
+#endif
+ }
+}
+
+#ifndef Q_OS_WINCE
+void QWidget::grabMouse()
+{
+ if (!qt_nograb()) {
+ if (mouseGrb)
+ mouseGrb->releaseMouse();
+ journalRec = SetWindowsHookExA(WH_JOURNALRECORD, (HOOKPROC)qJournalRecordProc, GetModuleHandleA(0), 0);
+ Q_ASSERT(testAttribute(Qt::WA_WState_Created));
+ SetCapture(effectiveWinId());
+ mouseGrb = this;
+ mouseGrbCur = new QCursor(mouseGrb->cursor());
+ }
+}
+
+void QWidget::grabMouse(const QCursor &cursor)
+{
+ if (!qt_nograb()) {
+ if (mouseGrb)
+ mouseGrb->releaseMouse();
+ journalRec = SetWindowsHookExA(WH_JOURNALRECORD, (HOOKPROC)qJournalRecordProc, GetModuleHandleA(0), 0);
+ Q_ASSERT(testAttribute(Qt::WA_WState_Created));
+ SetCapture(effectiveWinId());
+ mouseGrbCur = new QCursor(cursor);
+ SetCursor(mouseGrbCur->handle());
+ mouseGrb = this;
+ }
+}
+
+void QWidget::releaseMouse()
+{
+ if (!qt_nograb() && mouseGrb == this) {
+ ReleaseCapture();
+ if (journalRec) {
+ UnhookWindowsHookEx(journalRec);
+ journalRec = 0;
+ }
+ if (mouseGrbCur) {
+ delete mouseGrbCur;
+ mouseGrbCur = 0;
+ }
+ mouseGrb = 0;
+ }
+}
+#endif
+
+void QWidget::grabKeyboard()
+{
+ if (!qt_nograb()) {
+ if (keyboardGrb)
+ keyboardGrb->releaseKeyboard();
+ keyboardGrb = this;
+ }
+}
+
+void QWidget::releaseKeyboard()
+{
+ if (!qt_nograb() && keyboardGrb == this)
+ keyboardGrb = 0;
+}
+
+
+QWidget *QWidget::mouseGrabber()
+{
+ return mouseGrb;
+}
+
+QWidget *QWidget::keyboardGrabber()
+{
+ return keyboardGrb;
+}
+
+void QWidget::activateWindow()
+{
+ window()->createWinId();
+ SetForegroundWindow(window()->internalWinId());
+}
+
+#ifndef Q_OS_WINCE
+void QWidget::setWindowState(Qt::WindowStates newstate)
+{
+ Q_D(QWidget);
+ Qt::WindowStates oldstate = windowState();
+ if (oldstate == newstate)
+ return;
+
+ int max = SW_MAXIMIZE;
+ int min = SW_MINIMIZE;
+
+ int normal = SW_SHOWNOACTIVATE;
+ if (newstate & Qt::WindowActive) {
+ max = SW_SHOWMAXIMIZED;
+ min = SW_SHOWMINIMIZED;
+ normal = SW_SHOWNORMAL;
+ }
+
+ if (isWindow()) {
+ createWinId();
+ Q_ASSERT(testAttribute(Qt::WA_WState_Created));
+
+ // Ensure the initial size is valid, since we store it as normalGeometry below.
+ if (!testAttribute(Qt::WA_Resized) && !isVisible())
+ adjustSize();
+
+ if ((oldstate & Qt::WindowMaximized) != (newstate & Qt::WindowMaximized)) {
+ if (newstate & Qt::WindowMaximized && !(oldstate & Qt::WindowFullScreen))
+ d->topData()->normalGeometry = geometry();
+ if (isVisible() && !(newstate & Qt::WindowMinimized)) {
+ ShowWindow(internalWinId(), (newstate & Qt::WindowMaximized) ? max : normal);
+ if (!(newstate & Qt::WindowFullScreen)) {
+ QRect r = d->topData()->normalGeometry;
+ if (!(newstate & Qt::WindowMaximized) && r.width() >= 0) {
+ if (pos() != r.topLeft() || size() !=r.size()) {
+ d->topData()->normalGeometry = QRect(0,0,-1,-1);
+ setGeometry(r);
+ }
+ }
+ } else {
+ d->updateFrameStrut();
+ }
+ }
+ }
+
+ if ((oldstate & Qt::WindowFullScreen) != (newstate & Qt::WindowFullScreen)) {
+ if (newstate & Qt::WindowFullScreen) {
+ if (d->topData()->normalGeometry.width() < 0 && !(oldstate & Qt::WindowMaximized))
+ d->topData()->normalGeometry = geometry();
+ d->topData()->savedFlags = GetWindowLongA(internalWinId(), GWL_STYLE);
+#ifndef Q_FLATTEN_EXPOSE
+ UINT style = WS_CLIPCHILDREN | WS_CLIPSIBLINGS | WS_POPUP;
+#else
+ UINT style = WS_POPUP;
+#endif
+ if (d->topData()->savedFlags & WS_SYSMENU)
+ style |= WS_SYSMENU;
+ if (isVisible())
+ style |= WS_VISIBLE;
+ SetWindowLongA(internalWinId(), GWL_STYLE, style);
+ QRect r = qApp->desktop()->screenGeometry(this);
+ UINT swpf = SWP_FRAMECHANGED;
+ if (newstate & Qt::WindowActive)
+ swpf |= SWP_NOACTIVATE;
+
+ SetWindowPos(internalWinId(), HWND_TOP, r.left(), r.top(), r.width(), r.height(), swpf);
+ d->updateFrameStrut();
+ } else {
+ UINT style = d->topData()->savedFlags;
+ if (isVisible())
+ style |= WS_VISIBLE;
+ SetWindowLongA(internalWinId(), GWL_STYLE, style);
+
+ UINT swpf = SWP_FRAMECHANGED | SWP_NOZORDER | SWP_NOSIZE | SWP_NOMOVE;
+ if (newstate & Qt::WindowActive)
+ swpf |= SWP_NOACTIVATE;
+ SetWindowPos(internalWinId(), 0, 0, 0, 0, 0, swpf);
+ d->updateFrameStrut();
+
+ // preserve maximized state
+ if (isVisible())
+ ShowWindow(internalWinId(), (newstate & Qt::WindowMaximized) ? max : normal);
+
+ if (!(newstate & Qt::WindowMaximized)) {
+ QRect r = d->topData()->normalGeometry;
+ d->topData()->normalGeometry = QRect(0,0,-1,-1);
+ if (r.isValid())
+ setGeometry(r);
+ }
+ }
+ }
+
+ if ((oldstate & Qt::WindowMinimized) != (newstate & Qt::WindowMinimized)) {
+ if (isVisible())
+ ShowWindow(internalWinId(), (newstate & Qt::WindowMinimized) ? min :
+ (newstate & Qt::WindowMaximized) ? max : normal);
+ }
+ }
+ data->window_state = newstate;
+ QWindowStateChangeEvent e(oldstate);
+ QApplication::sendEvent(this, &e);
+}
+#endif //Q_OS_WINCE
+
+
+/*
+ \internal
+ Platform-specific part of QWidget::hide().
+*/
+
+void QWidgetPrivate::hide_sys()
+{
+ Q_Q(QWidget);
+ deactivateWidgetCleanup();
+ Q_ASSERT(q->testAttribute(Qt::WA_WState_Created));
+#ifdef Q_OS_WINCE
+ if (!qt_wince_is_mobile() && q->isFullScreen()) {
+ HWND handle = FindWindow(L"HHTaskBar", L"");
+ if (handle) {
+ ShowWindow(handle, 1);
+ EnableWindow(handle, true);
+ }
+ }
+#endif
+ if (q->windowFlags() != Qt::Desktop) {
+ if ((q->windowFlags() & Qt::Popup) && q->internalWinId())
+ ShowWindow(q->internalWinId(), SW_HIDE);
+ else if (q->internalWinId())
+ SetWindowPos(q->internalWinId(),0, 0,0,0,0, SWP_HIDEWINDOW|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER);
+ }
+ if (q->isWindow()) {
+ if (QWidgetBackingStore *bs = maybeBackingStore())
+ bs->releaseBuffer();
+ } else {
+ invalidateBuffer(q->rect());
+ }
+ q->setAttribute(Qt::WA_Mapped, false);
+}
+
+
+/*
+ \internal
+ Platform-specific part of QWidget::show().
+*/
+#ifndef Q_OS_WINCE
+void QWidgetPrivate::show_sys()
+{
+ Q_Q(QWidget);
+#if defined(QT_NON_COMMERCIAL)
+ QT_NC_SHOW_WINDOW
+#endif
+ if (q->testAttribute(Qt::WA_OutsideWSRange))
+ return;
+ q->setAttribute(Qt::WA_Mapped);
+ Q_ASSERT(q->testAttribute(Qt::WA_WState_Created));
+
+ if (q->testAttribute(Qt::WA_DontShowOnScreen)) {
+ invalidateBuffer(q->rect());
+ return;
+ }
+
+ int sm = SW_SHOWNORMAL;
+ bool fakedMaximize = false;
+ if (q->isWindow()) {
+ if (q->isMinimized()) {
+ sm = SW_SHOWMINIMIZED;
+ if (!IsWindowVisible(q->internalWinId()))
+ sm = SW_SHOWMINNOACTIVE;
+ } else if (q->isMaximized()) {
+ sm = SW_SHOWMAXIMIZED;
+ // Windows will not behave correctly when we try to maximize a window which does not
+ // have minimize nor maximize buttons in the window frame. Windows would then ignore
+ // non-available geometry, and rather maximize the widget to the full screen, minus the
+ // window frame (caption). So, we do a trick here, by adding a maximize button before
+ // maximizing the widget, and then remove the maximize button afterwards.
+ Qt::WindowFlags &flags = data.window_flags;
+ if (flags & Qt::WindowTitleHint &&
+ !(flags & (Qt::WindowMinMaxButtonsHint | Qt::FramelessWindowHint))) {
+ fakedMaximize = TRUE;
+ int style = GetWindowLong(q->internalWinId(), GWL_STYLE);
+ SetWindowLong(q->internalWinId(), GWL_STYLE, style | WS_MAXIMIZEBOX);
+ }
+ }
+ }
+ if (q->testAttribute(Qt::WA_ShowWithoutActivating)
+ || (q->windowType() == Qt::Popup)
+ || (q->windowType() == Qt::ToolTip)
+ || (q->windowType() == Qt::Tool)) {
+ sm = SW_SHOWNOACTIVATE;
+ }
+
+
+ if (q->internalWinId())
+ ShowWindow(q->internalWinId(), sm);
+
+ if (fakedMaximize) {
+ int style = GetWindowLong(q->internalWinId(), GWL_STYLE);
+ SetWindowLong(q->internalWinId(), GWL_STYLE, style & ~WS_MAXIMIZEBOX);
+ SetWindowPos(q->internalWinId(), 0, 0, 0, 0, 0,
+ SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_NOOWNERZORDER
+ | SWP_FRAMECHANGED);
+ }
+
+ if (q->internalWinId()) {
+ if (IsIconic(q->internalWinId()))
+ data.window_state |= Qt::WindowMinimized;
+ if (IsZoomed(q->internalWinId()))
+ data.window_state |= Qt::WindowMaximized;
+ }
+
+ invalidateBuffer(q->rect());
+}
+#endif //Q_OS_WINCE
+
+void QWidgetPrivate::setFocus_sys()
+{
+ Q_Q(QWidget);
+ if (q->testAttribute(Qt::WA_WState_Created) && q->window()->windowType() != Qt::Popup)
+ SetFocus(q->effectiveWinId());
+}
+
+void QWidgetPrivate::raise_sys()
+{
+ Q_Q(QWidget);
+ Q_ASSERT(q->testAttribute(Qt::WA_WState_Created));
+ if (q->internalWinId())
+ SetWindowPos(q->internalWinId(), HWND_TOP, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE);
+}
+
+void QWidgetPrivate::lower_sys()
+{
+ Q_Q(QWidget);
+ Q_ASSERT(q->testAttribute(Qt::WA_WState_Created));
+ if (q->internalWinId())
+ SetWindowPos(q->internalWinId(), HWND_BOTTOM, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE);
+ invalidateBuffer(q->rect());
+}
+
+void QWidgetPrivate::stackUnder_sys(QWidget* w)
+{
+ Q_Q(QWidget);
+ Q_ASSERT(q->testAttribute(Qt::WA_WState_Created));
+ if (q->internalWinId() && w->internalWinId())
+ SetWindowPos(q->internalWinId(), w->internalWinId() , 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
+ invalidateBuffer(q->rect());
+}
+
+
+/*
+ Helper function for non-toplevel widgets. Helps to map Qt's 32bit
+ coordinate system to Windpws's 16bit coordinate system.
+
+ This code is duplicated from the X11 code, so any changes there
+ should also (most likely) be reflected here.
+
+ (In all comments below: s/X/Windows/g)
+ */
+
+void QWidgetPrivate::setWSGeometry(bool dontShow)
+{
+ Q_Q(QWidget);
+ Q_ASSERT(q->testAttribute(Qt::WA_WState_Created));
+
+ /*
+ There are up to four different coordinate systems here:
+ Qt coordinate system for this widget.
+ X coordinate system for this widget (relative to wrect).
+ Qt coordinate system for parent
+ X coordinate system for parent (relative to parent's wrect).
+ */
+ QRect validRange(-XCOORD_MAX,-XCOORD_MAX, 2*XCOORD_MAX, 2*XCOORD_MAX);
+ QRect wrectRange(-WRECT_MAX,-WRECT_MAX, 2*WRECT_MAX, 2*WRECT_MAX);
+ QRect wrect;
+ //xrect is the X geometry of my X widget. (starts out in parent's Qt coord sys, and ends up in parent's X coord sys)
+ QRect xrect = data.crect;
+
+ const QWidget *const parent = q->parentWidget();
+ QRect parentWRect = parent->data->wrect;
+
+ if (parentWRect.isValid()) {
+ // parent is clipped, and we have to clip to the same limit as parent
+ if (!parentWRect.contains(xrect)) {
+ xrect &= parentWRect;
+ wrect = xrect;
+ //translate from parent's to my Qt coord sys
+ wrect.translate(-data.crect.topLeft());
+ }
+ //translate from parent's Qt coords to parent's X coords
+ xrect.translate(-parentWRect.topLeft());
+
+ } else {
+ // parent is not clipped, we may or may not have to clip
+
+ if (data.wrect.isValid() && QRect(QPoint(),data.crect.size()).contains(data.wrect)) {
+ // This is where the main optimization is: we are already
+ // clipped, and if our clip is still valid, we can just
+ // move our window, and do not need to move or clip
+ // children
+
+ QRect vrect = xrect & parent->rect();
+ vrect.translate(-data.crect.topLeft()); //the part of me that's visible through parent, in my Qt coords
+ if (data.wrect.contains(vrect)) {
+ xrect = data.wrect;
+ xrect.translate(data.crect.topLeft());
+ if (q->internalWinId())
+ MoveWindow(q->internalWinId(), xrect.x(), xrect.y(), xrect.width(), xrect.height(), true);
+ return;
+ }
+ }
+
+ if (!validRange.contains(xrect)) {
+ // we are too big, and must clip
+ xrect &=wrectRange;
+ wrect = xrect;
+ wrect.translate(-data.crect.topLeft());
+ //parent's X coord system is equal to parent's Qt coord
+ //sys, so we don't need to map xrect.
+ }
+
+ }
+
+
+ // unmap if we are outside the valid window system coord system
+ bool outsideRange = !xrect.isValid();
+ bool mapWindow = false;
+ if (q->testAttribute(Qt::WA_OutsideWSRange) != outsideRange) {
+ q->setAttribute(Qt::WA_OutsideWSRange, outsideRange);
+ if (outsideRange) {
+ if (q->internalWinId())
+ ShowWindow(q->internalWinId(), SW_HIDE);
+ q->setAttribute(Qt::WA_Mapped, false);
+ } else if (!q->isHidden()) {
+ mapWindow = true;
+ }
+ }
+
+ if (outsideRange)
+ return;
+
+ bool jump = (data.wrect != wrect);
+ data.wrect = wrect;
+
+ // and now recursively for all children...
+ for (int i = 0; i < children.size(); ++i) {
+ QObject *object = children.at(i);
+ if (object->isWidgetType()) {
+ QWidget *w = static_cast<QWidget *>(object);
+ if (!w->isWindow() && w->testAttribute(Qt::WA_WState_Created))
+ w->d_func()->setWSGeometry();
+ }
+ }
+
+ // move ourselves to the new position and map (if necessary) after
+ // the movement. Rationale: moving unmapped windows is much faster
+ // than moving mapped windows
+ if (q->internalWinId()) {
+ if (!parent->internalWinId())
+ xrect.translate(parent->mapTo(q->nativeParentWidget(), QPoint(0, 0)));
+ MoveWindow(q->internalWinId(), xrect.x(), xrect.y(), xrect.width(), xrect.height(), !jump);
+ }
+ if (mapWindow && !dontShow) {
+ q->setAttribute(Qt::WA_Mapped);
+ if (q->internalWinId())
+ ShowWindow(q->internalWinId(), SW_SHOWNOACTIVATE);
+ }
+
+ if (jump && q->internalWinId())
+ InvalidateRect(q->internalWinId(), 0, false);
+
+}
+
+//
+// The internal qWinRequestConfig, defined in qapplication_win.cpp, stores move,
+// resize and setGeometry requests for a widget that is already
+// processing a config event. The purpose is to avoid recursion.
+//
+void qWinRequestConfig(WId, int, int, int, int, int);
+
+void QWidgetPrivate::setGeometry_sys(int x, int y, int w, int h, bool isMove)
+{
+ Q_Q(QWidget);
+ Q_ASSERT(q->testAttribute(Qt::WA_WState_Created));
+ if (extra) { // any size restrictions?
+ w = qMin(w,extra->maxw);
+ h = qMin(h,extra->maxh);
+ w = qMax(w,extra->minw);
+ h = qMax(h,extra->minh);
+ }
+ if (q->isWindow())
+ topData()->normalGeometry = QRect(0, 0, -1, -1);
+
+ QSize oldSize(q->size());
+ QPoint oldPos(q->pos());
+
+ if (!q->isWindow())
+ isMove = (data.crect.topLeft() != QPoint(x, y));
+ bool isResize = w != oldSize.width() || h != oldSize.height();
+
+ if (!isMove && !isResize)
+ return;
+
+ if (isResize && !q->testAttribute(Qt::WA_StaticContents) && q->internalWinId())
+ ValidateRgn(q->internalWinId(), 0);
+
+ if (isResize)
+ data.window_state &= ~Qt::WindowMaximized;
+
+ if (data.window_state & Qt::WindowFullScreen) {
+ QTLWExtra *top = topData();
+
+ if (q->isWindow()) {
+ // We need to update these flags when we remove the full screen state
+ // or the frame will not be updated
+ UINT style = top->savedFlags;
+ if (q->isVisible())
+ style |= WS_VISIBLE;
+ SetWindowLongA(q->internalWinId(), GWL_STYLE, style);
+
+ UINT swpf = SWP_FRAMECHANGED | SWP_NOZORDER | SWP_NOSIZE | SWP_NOMOVE;
+ if (data.window_state & Qt::WindowActive)
+ swpf |= SWP_NOACTIVATE;
+ SetWindowPos(q->internalWinId(), 0, 0, 0, 0, 0, swpf);
+ updateFrameStrut();
+ }
+ data.window_state &= ~Qt::WindowFullScreen;
+ topData()->savedFlags = 0;
+ }
+
+ QTLWExtra *tlwExtra = q->window()->d_func()->maybeTopData();
+ const bool inTopLevelResize = tlwExtra ? tlwExtra->inTopLevelResize : false;
+ const bool isTranslucentWindow = !isOpaque && ptrUpdateLayeredWindowIndirect && (data.window_flags & Qt::FramelessWindowHint)
+ && GetWindowLongA(q->internalWinId(), GWL_EXSTYLE) & Q_WS_EX_LAYERED;
+
+ if (q->testAttribute(Qt::WA_WState_ConfigPending)) { // processing config event
+ if (q->internalWinId())
+ qWinRequestConfig(q->internalWinId(), isMove ? 2 : 1, x, y, w, h);
+ } else {
+ if (!q->testAttribute(Qt::WA_DontShowOnScreen))
+ q->setAttribute(Qt::WA_WState_ConfigPending);
+ if (q->windowType() == Qt::Desktop) {
+ data.crect.setRect(x, y, w, h);
+ } else if (q->isWindow()) {
+ QRect fs(frameStrut());
+ if (extra) {
+ fs.setLeft(x - fs.left());
+ fs.setTop(y - fs.top());
+ fs.setRight((x + w - 1) + fs.right());
+ fs.setBottom((y + h - 1) + fs.bottom());
+ }
+ if (w == 0 || h == 0) {
+ q->setAttribute(Qt::WA_OutsideWSRange, true);
+ if (q->isVisible() && q->testAttribute(Qt::WA_Mapped))
+ hide_sys();
+ data.crect = QRect(x, y, w, h);
+ } else if (q->isVisible() && q->testAttribute(Qt::WA_OutsideWSRange)) {
+ q->setAttribute(Qt::WA_OutsideWSRange, false);
+
+ // put the window in its place and show it
+ MoveWindow(q->internalWinId(), fs.x(), fs.y(), fs.width(), fs.height(), true);
+ RECT rect;
+ if (!q->testAttribute(Qt::WA_DontShowOnScreen)) {
+ GetClientRect(q->internalWinId(), &rect);
+ data.crect.setRect(x, y, rect.right - rect.left, rect.bottom - rect.top);
+ } else {
+ data.crect.setRect(x, y, w, h);
+ }
+
+ show_sys();
+ } else if (!q->testAttribute(Qt::WA_DontShowOnScreen)) {
+ q->setAttribute(Qt::WA_OutsideWSRange, false);
+#ifndef Q_OS_WINCE
+ // If the window is hidden and in maximized state or minimized, instead of moving the
+ // window, set the normal position of the window.
+ WINDOWPLACEMENT wndpl;
+ GetWindowPlacement(q->internalWinId(), &wndpl);
+ if ((wndpl.showCmd == SW_MAXIMIZE && !IsWindowVisible(q->internalWinId())) || wndpl.showCmd == SW_SHOWMINIMIZED) {
+ RECT normal = {fs.x(), fs.y(), fs.x()+fs.width(), fs.y()+fs.height()};
+ wndpl.rcNormalPosition = normal;
+ wndpl.showCmd = wndpl.showCmd == SW_SHOWMINIMIZED ? SW_SHOWMINIMIZED : SW_HIDE;
+ SetWindowPlacement(q->internalWinId(), &wndpl);
+ } else {
+#else
+ if (data.window_state_internal & Qt::WindowMaximized) {
+ qt_wince_maximize(q);
+ } else {
+#endif
+ if (!isTranslucentWindow)
+ MoveWindow(q->internalWinId(), fs.x(), fs.y(), fs.width(), fs.height(), true);
+ else if (isMove && !isResize)
+ SetWindowPos(q->internalWinId(), 0, fs.x(), fs.y(), 0, 0, SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOZORDER);
+ }
+ if (!q->isVisible())
+ InvalidateRect(q->internalWinId(), 0, FALSE);
+ RECT rect;
+ // If the layout has heightForWidth, the MoveWindow() above can
+ // change the size/position, so refresh them.
+
+ if (isTranslucentWindow) {
+ data.crect.setRect(x, y, w, h);
+ } else {
+ GetClientRect(q->internalWinId(), &rect);
+ RECT rcNormalPosition ={0};
+ // Use (0,0) as window position for embedded ActiveQt controls.
+ if (!tlwExtra || !tlwExtra->embedded)
+ GetWindowRect(q->internalWinId(), &rcNormalPosition);
+ QRect fStrut(frameStrut());
+ data.crect.setRect(rcNormalPosition.left + fStrut.left(),
+ rcNormalPosition.top + fStrut.top(),
+ rect.right - rect.left,
+ rect.bottom - rect.top);
+ isResize = data.crect.size() != oldSize;
+ }
+ } else {
+ q->setAttribute(Qt::WA_OutsideWSRange, false);
+ data.crect.setRect(x, y, w, h);
+ }
+ } else {
+ QRect oldGeom(data.crect);
+ data.crect.setRect(x, y, w, h);
+ if (q->isVisible() && (!inTopLevelResize || q->internalWinId())) {
+ // Top-level resize optimization does not work for native child widgets;
+ // disable it for this particular widget.
+ if (inTopLevelResize)
+ tlwExtra->inTopLevelResize = false;
+
+ if (!isResize)
+ moveRect(QRect(oldPos, oldSize), x - oldPos.x(), y - oldPos.y());
+ else
+ invalidateBuffer_resizeHelper(oldPos, oldSize);
+
+ if (inTopLevelResize)
+ tlwExtra->inTopLevelResize = true;
+ }
+ if (q->testAttribute(Qt::WA_WState_Created))
+ setWSGeometry();
+ }
+ q->setAttribute(Qt::WA_WState_ConfigPending, false);
+ }
+
+ if (q->isWindow() && q->isVisible() && isResize && !inTopLevelResize) {
+ invalidateBuffer(q->rect()); //after the resize
+ }
+
+ // Process events immediately rather than in translateConfigEvent to
+ // avoid windows message process delay.
+ if (q->isVisible()) {
+ if (isMove && q->pos() != oldPos) {
+ QMoveEvent e(q->pos(), oldPos);
+ QApplication::sendEvent(q, &e);
+ }
+ if (isResize) {
+ static bool slowResize = qgetenv("QT_SLOW_TOPLEVEL_RESIZE").toInt();
+ // If we have a backing store with static contents, we have to disable the top-level
+ // resize optimization in order to get invalidated regions for resized widgets.
+ // The optimization discards all invalidateBuffer() calls since we're going to
+ // repaint everything anyways, but that's not the case with static contents.
+ const bool setTopLevelResize = !slowResize && q->isWindow() && extra && extra->topextra
+ && !extra->topextra->inTopLevelResize
+ && (!extra->topextra->backingStore
+ || !extra->topextra->backingStore->hasStaticContents());
+ if (setTopLevelResize)
+ extra->topextra->inTopLevelResize = true;
+ QResizeEvent e(q->size(), oldSize);
+ QApplication::sendEvent(q, &e);
+ if (setTopLevelResize)
+ extra->topextra->inTopLevelResize = false;
+ }
+ } else {
+ if (isMove && q->pos() != oldPos)
+ q->setAttribute(Qt::WA_PendingMoveEvent, true);
+ if (isResize)
+ q->setAttribute(Qt::WA_PendingResizeEvent, true);
+ }
+}
+
+bool QWidgetPrivate::shouldShowMaximizeButton()
+{
+ if (data.window_flags & Qt::MSWindowsFixedSizeDialogHint)
+ return false;
+ if (extra) {
+ if ((extra->maxw && extra->maxw != QWIDGETSIZE_MAX && extra->maxw != QLAYOUTSIZE_MAX)
+ || (extra->maxh && extra->maxh != QWIDGETSIZE_MAX && extra->maxh != QLAYOUTSIZE_MAX))
+ return false;
+ }
+ return data.window_flags & Qt::WindowMaximizeButtonHint;
+}
+
+void QWidgetPrivate::winUpdateIsOpaque()
+{
+#ifndef Q_OS_WINCE
+ Q_Q(QWidget);
+
+ if (!q->isWindow() || !q->testAttribute(Qt::WA_TranslucentBackground))
+ return;
+
+ if ((data.window_flags & Qt::FramelessWindowHint) == 0)
+ return;
+
+ if (!isOpaque && ptrUpdateLayeredWindowIndirect) {
+ SetWindowLongA(q->internalWinId(), GWL_EXSTYLE,
+ GetWindowLongA(q->internalWinId(), GWL_EXSTYLE) | Q_WS_EX_LAYERED);
+ } else {
+ SetWindowLongA(q->internalWinId(), GWL_EXSTYLE,
+ GetWindowLongA(q->internalWinId(), GWL_EXSTYLE) & ~Q_WS_EX_LAYERED);
+ }
+#endif
+}
+
+void QWidgetPrivate::setConstraints_sys()
+{
+#ifndef Q_OS_WINCE_WM
+ Q_Q(QWidget);
+ if (q->isWindow() && q->testAttribute(Qt::WA_WState_Created)) {
+ int style = GetWindowLongA(q->internalWinId(), GWL_STYLE);
+ if (shouldShowMaximizeButton())
+ style |= WS_MAXIMIZEBOX;
+ else
+ style &= ~WS_MAXIMIZEBOX;
+ SetWindowLongA(q->internalWinId(), GWL_STYLE, style);
+ }
+#endif
+}
+
+void QWidgetPrivate::scroll_sys(int dx, int dy)
+{
+ Q_Q(QWidget);
+ scrollChildren(dx, dy);
+
+ if (!paintOnScreen()) {
+ scrollRect(q->rect(), dx, dy);
+ } else {
+ UINT flags = SW_INVALIDATE;
+ if (!q->testAttribute(Qt::WA_OpaquePaintEvent))
+ flags |= SW_ERASE;
+ Q_ASSERT(q->testAttribute(Qt::WA_WState_Created));
+ ScrollWindowEx(q->internalWinId(), dx, dy, 0, 0, 0, 0, flags);
+ UpdateWindow(q->internalWinId());
+ }
+}
+
+void QWidgetPrivate::scroll_sys(int dx, int dy, const QRect &r)
+{
+ Q_Q(QWidget);
+
+ if (!paintOnScreen()) {
+ scrollRect(r, dx, dy);
+ } else {
+ RECT wr;
+ wr.top = r.top();
+ wr.left = r.left();
+ wr.bottom = r.bottom()+1;
+ wr.right = r.right()+1;
+
+ UINT flags = SW_INVALIDATE;
+ if (!q->testAttribute(Qt::WA_OpaquePaintEvent))
+ flags |= SW_ERASE;
+ Q_ASSERT(q->testAttribute(Qt::WA_WState_Created));
+ ScrollWindowEx(q->internalWinId(), dx, dy, &wr, &wr, 0, 0, flags);
+ UpdateWindow(q->internalWinId());
+ }
+}
+
+extern Q_GUI_EXPORT HDC qt_win_display_dc();
+
+int QWidget::metric(PaintDeviceMetric m) const
+{
+ Q_D(const QWidget);
+ int val;
+ if (m == PdmWidth) {
+ val = data->crect.width();
+ } else if (m == PdmHeight) {
+ val = data->crect.height();
+ } else {
+ HDC gdc = qt_win_display_dc();
+ switch (m) {
+ case PdmDpiX:
+ case PdmPhysicalDpiX:
+ if (d->extra && d->extra->customDpiX)
+ val = d->extra->customDpiX;
+ else if (d->parent)
+ val = static_cast<QWidget *>(d->parent)->metric(m);
+ else
+ val = GetDeviceCaps(gdc, LOGPIXELSX);
+ break;
+ case PdmDpiY:
+ case PdmPhysicalDpiY:
+ if (d->extra && d->extra->customDpiY)
+ val = d->extra->customDpiY;
+ else if (d->parent)
+ val = static_cast<QWidget *>(d->parent)->metric(m);
+ else
+ val = GetDeviceCaps(gdc, LOGPIXELSY);
+ break;
+ case PdmWidthMM:
+ val = data->crect.width()
+ * GetDeviceCaps(gdc, HORZSIZE)
+ / GetDeviceCaps(gdc, HORZRES);
+ break;
+ case PdmHeightMM:
+ val = data->crect.height()
+ * GetDeviceCaps(gdc, VERTSIZE)
+ / GetDeviceCaps(gdc, VERTRES);
+ break;
+ case PdmNumColors:
+ if (GetDeviceCaps(gdc, RASTERCAPS) & RC_PALETTE)
+ val = GetDeviceCaps(gdc, SIZEPALETTE);
+ else {
+ HDC hd = d->hd ? HDC(d->hd) : gdc;
+ int bpp = GetDeviceCaps(hd, BITSPIXEL);
+ if (bpp == 32)
+ val = INT_MAX; // ### this is bogus, it should be 2^24 colors for 32 bit as well
+ else if(bpp<=8)
+ val = GetDeviceCaps(hd, NUMCOLORS);
+ else
+ val = 1 << (bpp * GetDeviceCaps(hd, PLANES));
+ }
+ break;
+ case PdmDepth:
+ val = GetDeviceCaps(gdc, BITSPIXEL);
+ break;
+ default:
+ val = 0;
+ qWarning("QWidget::metric: Invalid metric command");
+ }
+ }
+ return val;
+}
+
+#ifndef Q_OS_WINCE
+void QWidgetPrivate::createSysExtra()
+{
+#ifndef QT_NO_DRAGANDDROP
+ extra->dropTarget = 0;
+#endif
+#ifndef QT_NO_DIRECT3D
+ extra->had_auto_fill_bg = 0;
+ extra->had_paint_on_screen = 0;
+ extra->had_no_system_bg = 0;
+#endif
+}
+
+void QWidgetPrivate::deleteSysExtra()
+{
+}
+#endif //Q_OS_WINCE
+
+void QWidgetPrivate::createTLSysExtra()
+{
+ extra->topextra->winIconSmall = 0;
+ extra->topextra->winIconBig = 0;
+}
+
+void QWidgetPrivate::deleteTLSysExtra()
+{
+ if (extra->topextra->winIconSmall)
+ DestroyIcon(extra->topextra->winIconSmall);
+ if (extra->topextra->winIconBig)
+ DestroyIcon(extra->topextra->winIconBig);
+}
+
+void QWidgetPrivate::registerDropSite(bool on)
+{
+ Q_Q(QWidget);
+ if (!q->testAttribute(Qt::WA_WState_Created))
+ return;
+ // Enablement is defined by d->extra->dropTarget != 0.
+ if (on) {
+ // Turn on.
+ createExtra();
+#ifndef QT_NO_DRAGANDDROP
+ if (!q->internalWinId())
+ q->nativeParentWidget()->d_func()->createExtra();
+ QWExtra *extra = extraData();
+ if (!extra->dropTarget)
+ extra->dropTarget = registerOleDnd(q);
+#endif
+ } else {
+ // Turn off.
+ QWExtra *extra = extraData();
+#ifndef QT_NO_DRAGANDDROP
+ if (extra && extra->dropTarget) {
+ unregisterOleDnd(q, extra->dropTarget);
+ extra->dropTarget = 0;
+ }
+#endif
+ }
+}
+
+#ifndef QT_NO_DRAGANDDROP
+QOleDropTarget* QWidgetPrivate::registerOleDnd(QWidget *widget)
+{
+ QOleDropTarget *dropTarget = new QOleDropTarget(widget);
+ Q_ASSERT(widget->testAttribute(Qt::WA_WState_Created));
+ if (!widget->internalWinId()) {
+ QWidget *nativeParent = widget->nativeParentWidget();
+ Q_ASSERT(nativeParent);
+ QWExtra *nativeExtra = nativeParent->d_func()->extra;
+ Q_ASSERT(nativeExtra);
+ if (!nativeParent->acceptDrops())
+ nativeParent->setAcceptDrops(true);
+ if (!nativeExtra->oleDropWidgets.contains(widget))
+ nativeExtra->oleDropWidgets.append(widget);
+ if (!nativeExtra->dropTarget) {
+ nativeExtra->dropTarget = registerOleDnd(nativeParent);
+ Q_ASSERT(nativeExtra->dropTarget);
+#ifndef Q_OS_WINCE
+ CoLockObjectExternal(nativeExtra->dropTarget, false, true);
+#endif
+ RegisterDragDrop(nativeParent->internalWinId(), nativeExtra->dropTarget);
+ }
+ } else {
+ RegisterDragDrop(widget->internalWinId(), dropTarget);
+#ifndef Q_OS_WINCE
+ CoLockObjectExternal(dropTarget, true, true);
+#endif
+ }
+ return dropTarget;
+}
+
+void QWidgetPrivate::unregisterOleDnd(QWidget *widget, QOleDropTarget *dropTarget)
+{
+ dropTarget->releaseQt();
+ dropTarget->Release();
+ Q_ASSERT(widget->testAttribute(Qt::WA_WState_Created));
+ if (!widget->internalWinId()) {
+ QWidget *nativeParent = widget->nativeParentWidget();
+ Q_ASSERT(nativeParent);
+ QWExtra *nativeExtra = nativeParent->d_func()->extra;
+ Q_ASSERT(nativeExtra);
+ nativeExtra->oleDropWidgets.removeAll(widget);
+ nativeExtra->oleDropWidgets.removeAll(static_cast<QWidget *>(0));
+ if (nativeExtra->oleDropWidgets.isEmpty() && nativeExtra->dropTarget
+ && !nativeParent->testAttribute(Qt::WA_DropSiteRegistered)) {
+#ifndef Q_OS_WINCE
+ CoLockObjectExternal(nativeExtra->dropTarget, false, true);
+#endif
+ RevokeDragDrop(nativeParent->internalWinId());
+ nativeExtra->dropTarget = 0;
+ }
+ } else {
+#ifndef Q_OS_WINCE
+ CoLockObjectExternal(dropTarget, false, true);
+#endif
+ RevokeDragDrop(widget->internalWinId());
+ }
+}
+
+#endif //QT_NO_DRAGANDDROP
+
+// from qregion_win.cpp
+extern HRGN qt_tryCreateRegion(QRegion::RegionType type, int left, int top, int right, int bottom);
+void QWidgetPrivate::setMask_sys(const QRegion &region)
+{
+ Q_Q(QWidget);
+ if (!q->internalWinId())
+ return;
+
+ if (region.isEmpty()) {
+ SetWindowRgn(q->internalWinId(), 0, true);
+ return;
+ }
+
+ // Since SetWindowRegion takes ownership, and we need to translate,
+ // we take a copy.
+ HRGN wr = qt_tryCreateRegion(QRegion::Rectangle, 0,0,0,0);
+ CombineRgn(wr, region.handle(), 0, RGN_COPY);
+
+ QPoint offset = (q->isWindow()
+ ? frameStrut().topLeft()
+ : QPoint(0, 0));
+ OffsetRgn(wr, offset.x(), offset.y());
+
+ Q_ASSERT(q->testAttribute(Qt::WA_WState_Created));
+ SetWindowRgn(data.winid, wr, true);
+}
+
+void QWidgetPrivate::updateFrameStrut()
+{
+ Q_Q(QWidget);
+
+ if (!q->testAttribute(Qt::WA_WState_Created))
+ return;
+
+ if (!q->internalWinId()) {
+ data.fstrut_dirty = false;
+ return;
+ }
+
+ RECT rect = {0,0,0,0};
+
+ QTLWExtra *top = topData();
+ uint exstyle = QT_WA_INLINE(GetWindowLongW(q->internalWinId(), GWL_EXSTYLE),
+ GetWindowLongA(q->internalWinId(), GWL_EXSTYLE));
+ uint style = QT_WA_INLINE(GetWindowLongW(q->internalWinId(), GWL_STYLE),
+ GetWindowLongA(q->internalWinId(), GWL_STYLE));
+#ifndef Q_OS_WINCE
+ if (AdjustWindowRectEx(&rect, style & ~(WS_OVERLAPPED), FALSE, exstyle)) {
+#else
+ if (AdjustWindowRectEx(&rect, style, FALSE, exstyle)) {
+#endif
+ top->frameStrut.setCoords(-rect.left, -rect.top, rect.right, rect.bottom);
+ data.fstrut_dirty = false;
+ }
+}
+
+#ifndef Q_OS_WINCE
+void QWidgetPrivate::setWindowOpacity_sys(qreal level)
+{
+ Q_Q(QWidget);
+
+ if (!isOpaque && ptrUpdateLayeredWindowIndirect && (data.window_flags & Qt::FramelessWindowHint)) {
+ if (GetWindowLongA(q->internalWinId(), GWL_EXSTYLE) & Q_WS_EX_LAYERED) {
+ Q_BLENDFUNCTION blend = {AC_SRC_OVER, 0, (int)(255.0 * level), Q_AC_SRC_ALPHA};
+ Q_UPDATELAYEREDWINDOWINFO info = {sizeof(info), NULL, NULL, NULL, NULL, NULL, 0, &blend, Q_ULW_ALPHA, NULL};
+ (*ptrUpdateLayeredWindowIndirect)(q->internalWinId(), &info);
+ }
+ return;
+ }
+
+ static bool function_resolved = false;
+ if (!function_resolved) {
+ ptrSetLayeredWindowAttributes =
+ (PtrSetLayeredWindowAttributes) QLibrary::resolve(QLatin1String("user32"),
+ "SetLayeredWindowAttributes");
+ function_resolved = true;
+ }
+
+ if (!ptrSetLayeredWindowAttributes)
+ return;
+
+ int wl = GetWindowLongA(q->internalWinId(), GWL_EXSTYLE);
+
+ if (level != 1.0) {
+ if ((wl&Q_WS_EX_LAYERED) == 0)
+ SetWindowLongA(q->internalWinId(), GWL_EXSTYLE, wl|Q_WS_EX_LAYERED);
+ } else if (wl&Q_WS_EX_LAYERED) {
+ SetWindowLongA(q->internalWinId(), GWL_EXSTYLE, wl & ~Q_WS_EX_LAYERED);
+ }
+ (*ptrSetLayeredWindowAttributes)(q->internalWinId(), 0, (int)(level * 255), Q_LWA_ALPHA);
+}
+#endif //Q_OS_WINCE
+
+// class QGlobalRasterPaintEngine: public QRasterPaintEngine
+// {
+// public:
+// inline QGlobalRasterPaintEngine() : QRasterPaintEngine() { setFlushOnEnd(false); }
+// };
+// Q_GLOBAL_STATIC(QGlobalRasterPaintEngine, globalRasterPaintEngine)
+
+#ifndef QT_NO_DIRECT3D
+static void cleanup_d3d_engine();
+Q_GLOBAL_STATIC_WITH_INITIALIZER(QDirect3DPaintEngine, _qt_d3dEngine,
+ {
+ qAddPostRoutine(cleanup_d3d_engine);
+ })
+static void cleanup_d3d_engine()
+{
+ _qt_d3dEngine()->cleanup();
+}
+QDirect3DPaintEngine* qt_d3dEngine()
+{
+ return _qt_d3dEngine();
+}
+#endif
+
+
+#ifndef QT_NO_DIRECTDRAW
+static uchar *qt_primary_surface_bits;
+static int qt_primary_surface_stride;
+static QImage::Format qt_primary_surface_format;
+
+void qt_win_initialize_directdraw()
+{
+ HRESULT res;
+
+ // Some initialization...
+ if (!qt_ddraw_object) {
+ res = DirectDrawCreate(0, &qt_ddraw_object, 0);
+
+ if (res != DD_OK)
+ qWarning("DirectDrawCreate failed: %d", res);
+
+ qt_ddraw_object->SetCooperativeLevel(0, DDSCL_NORMAL);
+
+ DDSURFACEDESC surfaceDesc;
+ memset(&surfaceDesc, 0, sizeof(DDSURFACEDESC));
+
+ surfaceDesc.dwSize = sizeof(DDSURFACEDESC);
+ surfaceDesc.dwFlags = DDSD_CAPS;
+ surfaceDesc.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE;
+
+ res = qt_ddraw_object->CreateSurface(&surfaceDesc, &qt_ddraw_primary, 0);
+ if (res != DD_OK)
+ qWarning("CreateSurface failed: %d", res);
+
+ memset(&surfaceDesc, 0, sizeof(DDSURFACEDESC));
+ surfaceDesc.dwSize = sizeof(DDSURFACEDESC);
+ res = qt_ddraw_primary->Lock(0, &surfaceDesc, DDLOCK_WAIT | DDLOCK_SURFACEMEMORYPTR, 0);
+ if (res != DD_OK)
+ qWarning("Locking surface failed: %d", res);
+
+ if (surfaceDesc.ddpfPixelFormat.dwFlags == DDPF_RGB) {
+ qt_primary_surface_bits = (uchar *) surfaceDesc.lpSurface;
+ qt_primary_surface_stride = surfaceDesc.lPitch;
+ qt_primary_surface_format = QImage::Format_RGB32;
+ } else {
+ qWarning("QWidget painting: unsupported device depth for onscreen painting...\n");
+ }
+
+ qt_ddraw_primary->Unlock(0);
+ }
+}
+
+class QOnScreenRasterPaintEngine : public QRasterPaintEngine
+{
+public:
+ // The image allocated here leaks... Fix if this code is ifdef'ed
+ // in
+ QOnScreenRasterPaintEngine()
+ : QRasterPaintEngine(new QImage(qt_primary_surface_bits,
+ qApp->desktop()->width(),
+ qApp->desktop()->height(),
+ qt_primary_surface_stride,
+ qt_primary_surface_format))
+ {
+ device = static_cast<QImage *>(d_func()->device);
+ }
+
+ bool begin(QPaintDevice *)
+ {
+ QRegion clip = systemClip();
+ originalSystemClip = clip;
+ clip.translate(widget->mapToGlobal(QPoint(0, 0)));
+ setSystemClip(clip);
+
+ QRect bounds = clip.boundingRect();
+ DDSURFACEDESC surface;
+ surface.dwSize = sizeof(DDSURFACEDESC);
+ HRESULT res = qt_ddraw_primary->Lock((RECT *) &bounds, &surface, DDLOCK_WAIT, 0);
+ if (res != DD_OK) {
+ qWarning("QWidget painting: locking onscreen bits failed: %d\n", res);
+ return false;
+ }
+
+ if (surface.lpSurface == qt_primary_surface_bits) {
+ qt_primary_surface_bits = (uchar *) surface.lpSurface;
+ device->data_ptr()->data = qt_primary_surface_bits;
+ }
+
+ return QRasterPaintEngine::begin(device);
+ }
+
+ bool end()
+ {
+ HRESULT res = qt_ddraw_primary->Unlock(0);
+ if (res != DD_OK)
+ qWarning("QWidget::paint, failed to unlock DirectDraw surface: %d", res);
+ bool ok = QRasterPaintEngine::end();
+ setSystemClip(originalSystemClip);
+ return ok;
+ }
+
+ QPoint coordinateOffset() const {
+ return -widget->mapToGlobal(QPoint(0, 0));
+ }
+
+ const QWidget *widget;
+ QImage *device;
+ QRegion originalSystemClip;
+};
+Q_GLOBAL_STATIC(QOnScreenRasterPaintEngine, onScreenPaintEngine)
+#else
+void qt_win_initialize_directdraw() { }
+#endif
+
+QPaintEngine *QWidget::paintEngine() const
+{
+#ifndef QT_NO_DIRECT3D
+ if ((qApp->testAttribute(Qt::AA_MSWindowsUseDirect3DByDefault)
+ || testAttribute(Qt::WA_MSWindowsUseDirect3D))
+ && qt_d3dEngine()->hasDirect3DSupport())
+ {
+ QDirect3DPaintEngine *engine = qt_d3dEngine();
+ if (qApp->testAttribute(Qt::AA_MSWindowsUseDirect3DByDefault))
+ engine->setFlushOnEnd(false);
+ else
+ engine->setFlushOnEnd(true);
+ return engine;
+ }
+#endif
+#ifndef QT_NO_DIRECTDRAW
+ QOnScreenRasterPaintEngine *pe = onScreenPaintEngine();
+ pe->widget = this;
+ return pe;
+#endif
+
+ // We set this bit which is checked in setAttribute for
+ // Qt::WA_PaintOnScreen. We do this to allow these two scenarios:
+ //
+ // 1. Users accidentally set Qt::WA_PaintOnScreen on X and port to
+ // windows which would mean suddenly their widgets stop working.
+ //
+ // 2. Users set paint on screen and subclass paintEngine() to
+ // return 0, in which case we have a "hole" in the backingstore
+ // allowing use of GDI or DirectX directly.
+ //
+ // 1 is WRONG, but to minimize silent failures, we have set this
+ // bit to ignore the setAttribute call. 2. needs to be
+ // supported because its our only means of embeddeding native
+ // graphics stuff.
+ const_cast<QWidgetPrivate *>(d_func())->noPaintOnScreen = 1;
+
+ return 0;
+}
+
+QWindowSurface *QWidgetPrivate::createDefaultWindowSurface_sys()
+{
+ Q_Q(QWidget);
+#ifndef QT_NO_DIRECT3D
+ extern QDirect3DPaintEngine *qt_d3dEngine();
+ if (qApp->testAttribute(Qt::AA_MSWindowsUseDirect3DByDefault) && (q->windowOpacity() == 1.0f)
+ && qt_d3dEngine()->hasDirect3DSupport()) {
+ return new QD3DWindowSurface(q);
+ }
+#endif
+ return new QRasterWindowSurface(q);
+}
+
+void QWidgetPrivate::setModal_sys()
+{
+}
+
+
+
+
+QT_END_NAMESPACE
+
+#ifdef Q_OS_WINCE
+# include "qwidget_wince.cpp"
+#endif
diff --git a/src/gui/kernel/qwidget_wince.cpp b/src/gui/kernel/qwidget_wince.cpp
new file mode 100644
index 0000000000..bb9681e486
--- /dev/null
+++ b/src/gui/kernel/qwidget_wince.cpp
@@ -0,0 +1,707 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifdef Q_OS_WINCE
+
+#include "qguifunctions_wince.h"
+
+QT_BEGIN_NAMESPACE
+
+const QString qt_reg_winclass(QWidget *w); // defined in qapplication_win.cpp
+extern "C" LRESULT CALLBACK QtWndProc(HWND, UINT, WPARAM, LPARAM);
+
+//#define TABLET_DEBUG
+#define PACKETDATA (PK_X | PK_Y | PK_BUTTONS | PK_NORMAL_PRESSURE | PK_TANGENT_PRESSURE \
+ | PK_ORIENTATION | PK_CURSOR | PK_Z)
+#define PACKETMODE 0
+
+typedef HCTX (API *PtrWTOpen)(HWND, LPLOGCONTEXT, BOOL);
+typedef BOOL (API *PtrWTClose)(HCTX);
+typedef UINT (API *PtrWTInfo)(UINT, UINT, LPVOID);
+typedef BOOL (API *PtrWTEnable)(HCTX, BOOL);
+typedef BOOL (API *PtrWTOverlap)(HCTX, BOOL);
+typedef int (API *PtrWTPacketsGet)(HCTX, int, LPVOID);
+typedef BOOL (API *PtrWTGet)(HCTX, LPLOGCONTEXT);
+typedef int (API *PtrWTQueueSizeGet)(HCTX);
+typedef BOOL (API *PtrWTQueueSizeSet)(HCTX, int);
+
+static void qt_tablet_init_wce();
+static void qt_tablet_cleanup_wce();
+
+static void qt_tablet_init_wce() {
+ static bool firstTime = true;
+ if (!firstTime)
+ return;
+ firstTime = false;
+ qt_tablet_widget = new QWidget(0);
+ qt_tablet_widget->createWinId();
+ qt_tablet_widget->setObjectName(QLatin1String("Qt internal tablet widget"));
+ LOGCONTEXT lcMine;
+ qAddPostRoutine(qt_tablet_cleanup_wce);
+ struct tagAXIS tpOri[3];
+ if (ptrWTInfo && ptrWTOpen && ptrWTQueueSizeGet && ptrWTQueueSizeSet) {
+ // make sure we have WinTab
+ if (!ptrWTInfo(0, 0, NULL)) {
+#ifdef TABLET_DEBUG
+ qWarning("QWidget: Wintab services not available");
+#endif
+ return;
+ }
+
+ // some tablets don't support tilt, check if it is possible,
+ qt_tablet_tilt_support = ptrWTInfo(WTI_DEVICES, DVC_ORIENTATION, &tpOri);
+ if (qt_tablet_tilt_support) {
+ // check for azimuth and altitude
+ qt_tablet_tilt_support = tpOri[0].axResolution && tpOri[1].axResolution;
+ }
+ // build our context from the default context
+ ptrWTInfo(WTI_DEFSYSCTX, 0, &lcMine);
+ // Go for the raw coordinates, the tablet event will return good stuff
+ lcMine.lcOptions |= CXO_MESSAGES | CXO_CSRMESSAGES;
+ lcMine.lcPktData = PACKETDATA;
+ lcMine.lcPktMode = PACKETMODE;
+ lcMine.lcMoveMask = PACKETDATA;
+ lcMine.lcOutOrgX = 0;
+ lcMine.lcOutExtX = lcMine.lcInExtX;
+ lcMine.lcOutOrgY = 0;
+ lcMine.lcOutExtY = -lcMine.lcInExtY;
+ qt_tablet_context = ptrWTOpen(qt_tablet_widget->winId(), &lcMine, true);
+#ifdef TABLET_DEBUG
+ qDebug("Tablet is %p", qt_tablet_context);
+#endif
+ if (!qt_tablet_context) {
+#ifdef TABLET_DEBUG
+ qWarning("QWidget: Failed to open the tablet");
+#endif
+ return;
+ }
+ // Set the size of the Packet Queue to the correct size...
+ int currSize = ptrWTQueueSizeGet(qt_tablet_context);
+ if (!ptrWTQueueSizeSet(qt_tablet_context, QT_TABLET_NPACKETQSIZE)) {
+ // Ideally one might want to use a smaller
+ // multiple, but for now, since we managed to destroy
+ // the existing Q with the previous call, set it back
+ // to the other size, which should work. If not,
+ // there will be trouble.
+ if (!ptrWTQueueSizeSet(qt_tablet_context, currSize)) {
+ Q_ASSERT_X(0, "Qt::Internal", "There is no packet queue for"
+ " the tablet. The tablet will not work");
+ }
+ }
+ }
+}
+
+static void qt_tablet_cleanup_wce() {
+ if (ptrWTClose)
+ ptrWTClose(qt_tablet_context);
+ delete qt_tablet_widget;
+ qt_tablet_widget = 0;
+}
+
+
+// The internal qWinRequestConfig, defined in qapplication_win.cpp, stores move,
+// resize and setGeometry requests for a widget that is already
+// processing a config event. The purpose is to avoid recursion.
+//
+void qWinRequestConfig(WId, int, int, int, int, int);
+
+void QWidgetPrivate::create_sys(WId window, bool initializeWindow, bool destroyOldWindow) {
+ Q_Q(QWidget);
+ static int sw = -1, sh = -1;
+
+ Qt::WindowType type = q->windowType();
+ Qt::WindowFlags flags = data.window_flags;
+
+ bool topLevel = (flags & Qt::Window);
+ bool popup = (type == Qt::Popup);
+ bool dialog = (type == Qt::Dialog
+ || type == Qt::Sheet
+ || (flags & Qt::MSWindowsFixedSizeDialogHint));
+ bool desktop = (type == Qt::Desktop);
+ bool tool = (type == Qt::Tool || type == Qt::Drawer);
+
+ HINSTANCE appinst = qWinAppInst();
+ HWND parentw, destroyw = 0;
+ WId id;
+
+ QString windowClassName = qt_reg_winclass(q);
+
+ if (!window) // always initialize
+ initializeWindow = true;
+
+ if (popup)
+ flags |= Qt::WindowStaysOnTopHint; // a popup stays on top
+
+ if (flags & (Qt::WindowSystemMenuHint | Qt::WindowMinMaxButtonsHint | Qt::WindowContextHelpButtonHint)) {
+ flags |= Qt::WindowSystemMenuHint;
+ flags |= Qt::WindowTitleHint;
+ flags &= ~Qt::FramelessWindowHint;
+ }
+
+ if (sw < 0) { // get the (primary) screen size
+ sw = GetSystemMetrics(SM_CXSCREEN);
+ sh = GetSystemMetrics(SM_CYSCREEN);
+ }
+
+ if (desktop) { // desktop widget
+ popup = false; // force this flags off
+ data.crect.setRect(0, 0, GetSystemMetrics(SM_CXSCREEN), GetSystemMetrics(SM_CYSCREEN));
+ }
+
+ parentw = q->parentWidget() ? q->parentWidget()->effectiveWinId() : 0;
+
+#ifdef UNICODE
+ QString title;
+ const TCHAR *ttitle = 0;
+#endif
+ QByteArray title95;
+ int style = WS_CHILD;
+ int exsty = WS_EX_NOPARENTNOTIFY;
+
+ if (topLevel) {
+ if (!(flags & Qt::FramelessWindowHint) && !tool)
+ style = (WS_OVERLAPPED) | WS_SYSMENU;
+ else
+ style = WS_POPUP;
+ if ((type == Qt::ToolTip) || (type == Qt::SplashScreen)) {
+ style = WS_POPUP;
+ exsty |= WS_EX_NOANIMATION;
+ } else {
+ if (flags & Qt::WindowTitleHint)
+ style |= WS_CAPTION;
+ if (flags & Qt::WindowSystemMenuHint)
+ style |= WS_SYSMENU;
+ if (flags & Qt::WindowContextHelpButtonHint)
+ exsty |= WS_EX_CONTEXTHELP;
+#ifndef Q_OS_WINCE_WM
+ if (flags & Qt::WindowMinimizeButtonHint)
+ style |= WS_MINIMIZEBOX;
+ if (shouldShowMaximizeButton())
+ style |= WS_MAXIMIZEBOX;
+#endif
+ if (tool)
+ exsty |= WS_EX_TOOLWINDOW;
+ }
+ }
+ if (dialog) {
+ style = WS_BORDER | WS_CAPTION;
+ if (flags & Qt::WindowOkButtonHint)
+ exsty |= WS_EX_CAPTIONOKBTN;
+ if (flags & Qt::WindowCancelButtonHint || flags & Qt::WA_DeleteOnClose)
+ style |= WS_SYSMENU;
+ if (flags & Qt::WindowContextHelpButtonHint)
+ exsty |= WS_EX_CONTEXTHELP;
+ }
+ if (popup) {
+ style = WS_POPUP;
+ exsty |= WS_EX_NOANIMATION;
+ }
+
+ if (flags & Qt::WindowTitleHint) {
+ QT_WA({
+ title = q->isWindow() ? qAppName() : q->objectName();
+ ttitle = (TCHAR*)title.utf16();
+ } , {
+ title95 = q->isWindow() ? qAppName().toLocal8Bit() : q->objectName().toLatin1();
+ });
+ }
+
+ // The Qt::WA_WState_Created flag is checked by translateConfigEvent() in
+ // qapplication_win.cpp. We switch it off temporarily to avoid move
+ // and resize events during creationt
+ q->setAttribute(Qt::WA_WState_Created, false);
+
+ if (window) { // override the old window
+ if (destroyOldWindow)
+ destroyw = data.winid;
+ id = window;
+ setWinId(window);
+ LONG res = SetWindowLongA(window, GWL_STYLE, style);
+ if (!res)
+ qErrnoWarning("QWidget::create: Failed to set window style");
+
+ res = SetWindowLongA( window, GWL_WNDPROC, (LONG)QtWndProc );
+
+ if (!res)
+ qErrnoWarning("QWidget::create: Failed to set window procedure");
+ } else if (desktop) { // desktop widget
+ id = GetDesktopWindow();
+ if (!id) { //Create a dummy desktop
+ RECT r;
+ SystemParametersInfo(SPI_GETWORKAREA, 0, &r, 0);
+ const TCHAR *cname = reinterpret_cast<const TCHAR *> (windowClassName.utf16());
+ id = CreateWindow(cname, ttitle, style,
+ r.left, r.top, r.right - r.left, r.bottom - r.top,
+ 0, 0, appinst, 0);
+ }
+ setWinId(id);
+ } else if (topLevel) { // create top-level widget
+ const bool wasMoved = q->testAttribute(Qt::WA_Moved);
+
+ int x, y;
+ if (qt_wince_is_mobile()) {
+ x = wasMoved ? data.crect.left() : CW_USEDEFAULT;
+ y = wasMoved ? data.crect.top() : CW_USEDEFAULT;
+ } else {
+ x = wasMoved ? data.crect.left() : 100;
+ y = wasMoved ? data.crect.top() : 100;
+ }
+
+ int w = CW_USEDEFAULT;
+ int h = CW_USEDEFAULT;
+
+ // Adjust for framestrut when needed
+ RECT rect = {0,0,0,0};
+ if (AdjustWindowRectEx(&rect, style, FALSE, exsty)) {
+ QTLWExtra *td = maybeTopData();
+ if (wasMoved && (td && !td->posFromMove)) {
+ x = data.crect.x() + rect.left;
+ y = data.crect.y() + rect.top;
+ }
+
+ if (q->testAttribute(Qt::WA_Resized)) {
+ w = data.crect.width() + (rect.right - rect.left);
+ h = data.crect.height() + (rect.bottom - rect.top);
+ }
+ }
+
+ const TCHAR *cname = reinterpret_cast<const TCHAR *> (windowClassName.utf16());
+ id = CreateWindowEx(exsty, cname, ttitle, style,
+ x, y, w, h,
+ 0, 0, appinst, 0);
+
+ if (!id)
+ qErrnoWarning("QWidget::create: Failed to create window");
+ setWinId(id);
+ if ((flags & Qt::WindowStaysOnTopHint) || (type == Qt::ToolTip))
+ SetWindowPos(id, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE);
+ } else if (q->testAttribute(Qt::WA_NativeWindow) || paintOnScreen()) { // create child widget
+ QT_WA({
+ const TCHAR *cname = (TCHAR*)windowClassName.utf16();
+ id = CreateWindowEx(exsty, cname, ttitle, style,
+ data.crect.left(), data.crect.top(), data.crect.width(), data.crect.height(),
+ parentw, NULL, appinst, NULL);
+ } , {
+ id = CreateWindowExA(exsty, windowClassName.toLatin1(), title95, style,
+ data.crect.left(), data.crect.top(), data.crect.width(), data.crect.height(),
+ parentw, NULL, appinst, NULL);
+ });
+ if (!id)
+ qErrnoWarning("QWidget::create: Failed to create window");
+ SetWindowPos(id, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
+ setWinId(id);
+ }
+
+ if (desktop) {
+ q->setAttribute(Qt::WA_WState_Visible);
+ } else if (topLevel) {
+ RECT cr;
+ GetClientRect(id, &cr);
+ // one cannot trust cr.left and cr.top, use a correction POINT instead
+ POINT pt;
+ pt.x = 0;
+ pt.y = 0;
+ if (!q->testAttribute(Qt::WA_DontShowOnScreen) || q->testAttribute(Qt::WA_Moved))
+ ClientToScreen(id, &pt);
+ data.crect = QRect(QPoint(pt.x, pt.y),
+ QPoint(pt.x + cr.right - 1, pt.y + cr.bottom - 1));
+
+ if (data.fstrut_dirty) {
+ // be nice to activeqt
+ updateFrameStrut();
+ }
+ }
+
+ q->setAttribute(Qt::WA_WState_Created); // accept move/resize events
+ hd = 0; // no display context
+
+ if (window) { // got window from outside
+ if (IsWindowVisible(window))
+ q->setAttribute(Qt::WA_WState_Visible);
+ else
+ q->setAttribute(Qt::WA_WState_Visible, false);
+ }
+
+ if (extra && !extra->mask.isEmpty())
+ setMask_sys(extra->mask);
+
+#if defined(QT_NON_COMMERCIAL)
+ QT_NC_WIDGET_CREATE
+#endif
+
+ if (q->hasFocus() && q->testAttribute(Qt::WA_InputMethodEnabled))
+ q->inputContext()->setFocusWidget(q);
+
+ if (destroyw) {
+ DestroyWindow(destroyw);
+ }
+
+ if (q != qt_tablet_widget && QWidgetPrivate::mapper)
+ qt_tablet_init_wce();
+
+ if (q->testAttribute(Qt::WA_DropSiteRegistered))
+ registerDropSite(true);
+
+ if (maybeTopData() && maybeTopData()->opacity != 255)
+ q->setWindowOpacity(maybeTopData()->opacity/255.);
+
+ if (!topLevel && q->testAttribute(Qt::WA_NativeWindow) && q->testAttribute(Qt::WA_Mapped)) {
+ Q_ASSERT(q->internalWinId());
+ ShowWindow(q->internalWinId(), SW_SHOW);
+ }
+}
+
+/*
+ \internal
+ Platform-specific part of QWidget::show().
+*/
+void QWidgetPrivate::show_sys() {
+ Q_Q(QWidget);
+#if defined(QT_NON_COMMERCIAL)
+ QT_NC_SHOW_WINDOW
+#endif
+ if (q->testAttribute(Qt::WA_OutsideWSRange))
+ return;
+
+ q->setAttribute(Qt::WA_Mapped);
+
+ Q_ASSERT(q->testAttribute(Qt::WA_WState_Created));
+
+ if (q->testAttribute(Qt::WA_DontShowOnScreen)) {
+ invalidateBuffer(q->rect());
+ return;
+ }
+
+
+ int sm = SW_SHOW;
+ bool fakedMaximize = false;
+ if (q->isWindow()) {
+#ifndef Q_OS_WINCE_WM
+ if (q->isMinimized()) {
+ sm = SW_SHOWMINIMIZED;
+ } else if (q->isMaximized()) {
+ sm = SW_SHOWMAXIMIZED;
+ // Windows will not behave correctly when we try to maximize a window which does not
+ // have minimize nor maximize buttons in the window frame. Windows would then ignore
+ // non-available geometry, and rather maximize the widget to the full screen, minus the
+ // window frame (caption). So, we do a trick here, by adding a maximize button before
+ // maximizing the widget, and then remove the maximize button afterwards.
+ Qt::WindowFlags &flags = data.window_flags;
+ if (flags & Qt::WindowTitleHint &&
+ !(flags & (Qt::WindowMinMaxButtonsHint | Qt::FramelessWindowHint))) {
+ fakedMaximize = TRUE;
+ int style = GetWindowLong(q->internalWinId(), GWL_STYLE);
+ SetWindowLong(q->internalWinId(), GWL_STYLE, style | WS_MAXIMIZEBOX);
+ }
+ } else
+#endif
+ if (q->isHidden()) {
+ sm = SW_HIDE;
+ }
+ }
+ if (q->testAttribute(Qt::WA_ShowWithoutActivating)
+ || (q->windowType() == Qt::Popup)
+ || (q->windowType() == Qt::ToolTip)
+ || (q->windowType() == Qt::Tool)) {
+ sm = SW_SHOWNOACTIVATE;
+ }
+
+ if (!(data.window_state & Qt::WindowMinimized))
+ ShowWindow(q->internalWinId(), sm);
+
+ if (q->isMaximized() && q->isWindow())
+ qt_wince_maximize(q);
+
+#ifndef Q_OS_WINCE_WM
+ if (!qt_wince_is_mobile() && q->isFullScreen()) {
+ HWND handle = FindWindow(L"HHTaskBar", L"");
+ if (handle) {
+ ShowWindow(handle, 0);
+ EnableWindow(handle, false);
+ }
+ }
+
+ if (fakedMaximize) {
+ int style = GetWindowLong(q->internalWinId(), GWL_STYLE);
+ SetWindowLong(q->internalWinId(), GWL_STYLE, style & ~WS_MAXIMIZEBOX);
+ SetWindowPos(q->internalWinId(), 0, 0, 0, 0, 0,
+ SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_NOOWNERZORDER
+ | SWP_FRAMECHANGED);
+ }
+#else
+ Q_UNUSED(fakedMaximize);
+#endif
+
+ if (q->isWindow() && sm == SW_SHOW)
+ SetForegroundWindow(q->internalWinId());
+
+ invalidateBuffer(q->rect());
+}
+
+void QWidget::setWindowState(Qt::WindowStates newstate)
+{
+ Q_D(QWidget);
+ Qt::WindowStates oldstate = windowState();
+ if (oldstate == newstate)
+ return;
+
+ int max = SW_SHOWNORMAL;
+ int min = SW_SHOWNOACTIVATE;
+
+ int normal = SW_SHOWNOACTIVATE;
+
+ if ((oldstate & Qt::WindowMinimized) && !(newstate & Qt::WindowMinimized))
+ newstate |= Qt::WindowActive;
+ if (newstate & Qt::WindowActive) {
+ max = SW_SHOWNORMAL;
+ min = SW_SHOWNORMAL;
+ normal = SW_SHOWNORMAL;
+ }
+ if (isWindow()) {
+ createWinId();
+ Q_ASSERT(testAttribute(Qt::WA_WState_Created));
+ data->window_state_internal = newstate;
+ // Ensure the initial size is valid, since we store it as normalGeometry below.
+ if ((!testAttribute(Qt::WA_Resized) && !isVisible()))
+ adjustSize();
+ if (!d->topData()->normalGeometry.isValid()) {
+ if (newstate & Qt::WindowMaximized && !(oldstate & Qt::WindowFullScreen))
+ d->topData()->normalGeometry = geometry();
+ if (newstate & Qt::WindowMinimized && !(oldstate & Qt::WindowFullScreen))
+ d->topData()->normalGeometry = geometry();
+ }
+ if ((oldstate & Qt::WindowMaximized) != (newstate & Qt::WindowMaximized)) {
+ if (!(newstate & Qt::WindowMaximized)) {
+ int style = GetWindowLongW(internalWinId(), GWL_STYLE) | WS_BORDER | WS_POPUP | WS_CAPTION;
+ SetWindowLong(internalWinId(), GWL_STYLE, style);
+ SetWindowLong(internalWinId(), GWL_EXSTYLE, GetWindowLong (internalWinId(), GWL_EXSTYLE) & ~ WS_EX_NODRAG);
+ }
+ if (isVisible() && newstate & Qt::WindowMaximized)
+ qt_wince_maximize(this);
+ if (isVisible() && !(newstate & Qt::WindowMinimized)) {
+ ShowWindow(internalWinId(), (newstate & Qt::WindowMaximized) ? max : normal);
+ if (!(newstate & Qt::WindowFullScreen)) {
+ QRect r = d->topData()->normalGeometry;
+ if (!(newstate & Qt::WindowMaximized) && r.width() >= 0) {
+ if (pos() != r.topLeft() || size() !=r.size()) {
+ d->topData()->normalGeometry = QRect(0,0,-1,-1);
+ setGeometry(r);
+ }
+ }
+ } else {
+ d->updateFrameStrut();
+ }
+ }
+ }
+ if ((oldstate & Qt::WindowFullScreen) != (newstate & Qt::WindowFullScreen)) {
+ if (newstate & Qt::WindowFullScreen) {
+ if (d->topData()->normalGeometry.width() < 0 && !(oldstate & Qt::WindowMaximized))
+ d->topData()->normalGeometry = geometry();
+ d->topData()->savedFlags = GetWindowLongA(internalWinId(), GWL_STYLE);
+ UINT style = WS_CLIPCHILDREN | WS_CLIPSIBLINGS | WS_POPUP;
+ if (isVisible())
+ style |= WS_VISIBLE;
+ SetWindowLongA(internalWinId(), GWL_STYLE, style);
+ QRect r = qApp->desktop()->screenGeometry(this);
+ UINT swpf = SWP_FRAMECHANGED;
+ if (newstate & Qt::WindowActive)
+ swpf |= SWP_NOACTIVATE;
+ qt_wince_full_screen(internalWinId(), true, swpf);
+ d->updateFrameStrut();
+ } else {
+ UINT style = d->topData()->savedFlags;
+ if (isVisible())
+ style |= WS_VISIBLE;
+ SetWindowLongA(internalWinId(), GWL_STYLE, style);
+ UINT swpf = SWP_FRAMECHANGED | SWP_NOZORDER | SWP_NOSIZE | SWP_NOMOVE;
+ if (newstate & Qt::WindowActive)
+ swpf |= SWP_NOACTIVATE;
+ qt_wince_full_screen(internalWinId(), false, swpf);
+ d->updateFrameStrut();
+
+ // preserve maximized state
+ if (isVisible()) {
+ ShowWindow(internalWinId(), (newstate & Qt::WindowMaximized) ? max : normal);
+ if (newstate & Qt::WindowMaximized)
+ qt_wince_maximize(this);
+ }
+ if (!(newstate & Qt::WindowMaximized)) {
+ QRect r = d->topData()->normalGeometry;
+ d->topData()->normalGeometry = QRect(0,0,-1,-1);
+ if (r.isValid())
+ setGeometry(r);
+ }
+ }
+ }
+ if ((oldstate & Qt::WindowMinimized) != (newstate & Qt::WindowMinimized)) {
+ if (isVisible()) {
+ ShowWindow(internalWinId(), (newstate & Qt::WindowMinimized) ? min :
+ (newstate & Qt::WindowMaximized) ? max : normal);
+ if (newstate & Qt::WindowMaximized)
+ qt_wince_maximize(this);
+ if (newstate & Qt::WindowMinimized)
+ qt_wince_minimize(internalWinId());
+ }
+ }
+ if ((newstate & Qt::WindowMaximized) && !(newstate & Qt::WindowFullScreen)) {
+ QRect r = d->topData()->normalGeometry;
+#ifdef Q_OS_WINCE_WM
+ if (!inherits("QDialog") && !inherits("QMdiArea") && !isVisible()) {
+ d->data.crect.setRect(0, 0, -1, -1);
+ }
+#else
+ qt_wince_maximize(this);
+#endif
+ }
+ }
+ data->window_state = newstate;
+ data->window_state_internal = newstate;
+ QWindowStateChangeEvent e(oldstate);
+ QApplication::sendEvent(this, &e);
+}
+
+
+void QWidgetPrivate::createSysExtra() {
+#ifndef QT_NO_DRAGANDDROP
+ extra->dropTarget = 0;
+#endif
+}
+
+void QWidgetPrivate::deleteSysExtra()
+{
+ Q_Q(QWidget);
+ if (!qt_wince_is_mobile() && q->isFullScreen()) {
+ HWND handle = FindWindow(L"HHTaskBar", L"");
+ if (handle) {
+ ShowWindow(handle, 1);
+ EnableWindow(handle, true);
+ }
+ }
+}
+
+void QWidgetPrivate::setWindowOpacity_sys(qreal level) {
+ Q_UNUSED(level);
+ return;
+}
+
+// The procedure does nothing, but is required for mousegrabbing to work
+LRESULT CALLBACK qJournalRecordProc(int nCode, WPARAM wParam, LPARAM lParam) {
+ Q_UNUSED(nCode);
+ Q_UNUSED(wParam);
+ Q_UNUSED(lParam);
+ return 0;
+}
+
+void QWidget::grabMouse() {
+ if (!qt_nograb()) {
+ if (mouseGrb)
+ mouseGrb->releaseMouse();
+ Q_ASSERT(testAttribute(Qt::WA_WState_Created));
+ SetCapture(internalWinId());
+ mouseGrb = this;
+ }
+}
+
+#ifndef QT_NO_CURSOR
+void QWidget::grabMouse(const QCursor &cursor) {
+ if (!qt_nograb()) {
+ if (mouseGrb)
+ mouseGrb->releaseMouse();
+ Q_ASSERT(testAttribute(Qt::WA_WState_Created));
+ SetCapture(internalWinId());
+ mouseGrbCur = new QCursor(cursor);
+ SetCursor(mouseGrbCur->handle());
+ mouseGrb = this;
+ }
+}
+#endif
+
+void QWidget::releaseMouse() {
+ if (!qt_nograb() && mouseGrb == this) {
+ ReleaseCapture();
+ if (journalRec) {
+ journalRec = 0;
+ }
+ if (mouseGrbCur) {
+ delete mouseGrbCur;
+ mouseGrbCur = 0;
+ }
+ mouseGrb = 0;
+ }
+}
+
+void QWidget::show()
+{
+ Qt::WindowFlags flags = windowFlags() & 0xff;
+ int threshold = qApp->autoMaximizeThreshold();
+ if ((threshold < 0) || (windowState() & Qt::WindowFullScreen) || (windowState() & Qt::WindowMaximized)) {
+ setVisible(true);
+ return;
+ }
+ int height = sizeHint().height();
+ int screenHeight = (qreal(threshold) / 100.0f * qApp->desktop()->screenGeometry(this).height());
+ bool maximize = height > screenHeight;
+ if (!maximize) {
+ // If we do not maximize yet we check the widget and its child widgets whether they are
+ //vertically expanding. If one of the widgets is expanding we maximize.
+ QList<QWidget *> list = findChildren<QWidget *>();
+ bool expandingChild = sizePolicy().verticalPolicy () == QSizePolicy::Expanding;
+ for (int i = 0; (i < list.size()) && !expandingChild; ++i) {
+ expandingChild = list.at(i)->sizePolicy().verticalPolicy () == QSizePolicy::Expanding;
+ }
+ maximize = expandingChild;
+ }
+ if ((minimumSizeHint().height() > qApp->desktop()->screenGeometry(this).height()) || (minimumSizeHint().width() > qApp->desktop()->screenGeometry(this).width()))
+ maximize = false;
+
+ if ((flags == Qt::Window || flags == Qt::Dialog) && maximize) {
+ setWindowState((windowState() & ~(Qt::WindowMinimized | Qt::WindowFullScreen))
+ | Qt::WindowMaximized);
+ setVisible(true);
+ }
+ else {
+ setVisible(true);
+ }
+}
+
+QT_END_NAMESPACE
+
+#endif Q_OS_WINCE
diff --git a/src/gui/kernel/qwidget_x11.cpp b/src/gui/kernel/qwidget_x11.cpp
new file mode 100644
index 0000000000..a12b50c2e8
--- /dev/null
+++ b/src/gui/kernel/qwidget_x11.cpp
@@ -0,0 +1,2891 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qevent.h"
+#include "qwidget.h"
+#include "qdesktopwidget.h"
+#include "qapplication.h"
+#include "qapplication_p.h"
+#include "qnamespace.h"
+#include "qpainter.h"
+#include "qbitmap.h"
+#include "qlayout.h"
+#include "qtextcodec.h"
+#include "qdatetime.h"
+#include "qcursor.h"
+#include "qstack.h"
+#include "qcolormap.h"
+#include "qdebug.h"
+#include "qmenu.h"
+#include "private/qmenu_p.h"
+#include "private/qbackingstore_p.h"
+#include "private/qwindowsurface_x11_p.h"
+
+//extern bool qt_sendSpontaneousEvent(QObject *, QEvent *); //qapplication_x11.cpp
+
+#include <private/qpixmap_x11_p.h>
+#include <private/qpaintengine_x11_p.h>
+#include "qt_x11_p.h"
+#include "qx11info_x11.h"
+
+#include <stdlib.h>
+
+//#define ALIEN_DEBUG
+
+// defined in qapplication_x11.cpp
+//bool qt_wstate_iconified(WId);
+//void qt_updated_rootinfo();
+
+
+#if !defined(QT_NO_IM)
+#include "qinputcontext.h"
+#include "qinputcontextfactory.h"
+#endif
+
+#include "qwidget_p.h"
+
+#define XCOORD_MAX 16383
+#define WRECT_MAX 8191
+
+QT_BEGIN_NAMESPACE
+
+extern bool qt_nograb();
+
+QWidget *QWidgetPrivate::mouseGrabber = 0;
+QWidget *QWidgetPrivate::keyboardGrabber = 0;
+
+void qt_net_remove_user_time(QWidget *tlw);
+void qt_net_update_user_time(QWidget *tlw, unsigned long timestamp);
+
+int qt_x11_create_desktop_on_screen = -1;
+
+extern void qt_net_update_user_time(QWidget *tlw, unsigned long timestamp);
+
+// MWM support
+struct QtMWMHints {
+ ulong flags, functions, decorations;
+ long input_mode;
+ ulong status;
+};
+
+enum {
+ MWM_HINTS_FUNCTIONS = (1L << 0),
+
+ MWM_FUNC_ALL = (1L << 0),
+ MWM_FUNC_RESIZE = (1L << 1),
+ MWM_FUNC_MOVE = (1L << 2),
+ MWM_FUNC_MINIMIZE = (1L << 3),
+ MWM_FUNC_MAXIMIZE = (1L << 4),
+ MWM_FUNC_CLOSE = (1L << 5),
+
+ MWM_HINTS_DECORATIONS = (1L << 1),
+
+ MWM_DECOR_ALL = (1L << 0),
+ MWM_DECOR_BORDER = (1L << 1),
+ MWM_DECOR_RESIZEH = (1L << 2),
+ MWM_DECOR_TITLE = (1L << 3),
+ MWM_DECOR_MENU = (1L << 4),
+ MWM_DECOR_MINIMIZE = (1L << 5),
+ MWM_DECOR_MAXIMIZE = (1L << 6),
+
+ MWM_HINTS_INPUT_MODE = (1L << 2),
+
+ MWM_INPUT_MODELESS = 0L,
+ MWM_INPUT_PRIMARY_APPLICATION_MODAL = 1L,
+ MWM_INPUT_FULL_APPLICATION_MODAL = 3L
+};
+
+
+static QtMWMHints GetMWMHints(Display *display, Window window)
+{
+ QtMWMHints mwmhints;
+
+ Atom type;
+ int format;
+ ulong nitems, bytesLeft;
+ uchar *data = 0;
+ if ((XGetWindowProperty(display, window, ATOM(_MOTIF_WM_HINTS), 0, 5, false,
+ ATOM(_MOTIF_WM_HINTS), &type, &format, &nitems, &bytesLeft,
+ &data) == Success)
+ && (type == ATOM(_MOTIF_WM_HINTS)
+ && format == 32
+ && nitems >= 5)) {
+ mwmhints = *(reinterpret_cast<QtMWMHints *>(data));
+ } else {
+ mwmhints.flags = 0L;
+ mwmhints.functions = MWM_FUNC_ALL;
+ mwmhints.decorations = MWM_DECOR_ALL;
+ mwmhints.input_mode = 0L;
+ mwmhints.status = 0L;
+ }
+
+ if (data)
+ XFree(data);
+
+ return mwmhints;
+}
+
+static void SetMWMHints(Display *display, Window window, const QtMWMHints &mwmhints)
+{
+ if (mwmhints.flags != 0l) {
+ XChangeProperty(display, window, ATOM(_MOTIF_WM_HINTS), ATOM(_MOTIF_WM_HINTS), 32,
+ PropModeReplace, (unsigned char *) &mwmhints, 5);
+ } else {
+ XDeleteProperty(display, window, ATOM(_MOTIF_WM_HINTS));
+ }
+}
+
+// Returns true if we should set WM_TRANSIENT_FOR on \a w
+static inline bool isTransient(const QWidget *w)
+{
+ return ((w->windowType() == Qt::Dialog
+ || w->windowType() == Qt::Sheet
+ || w->windowType() == Qt::Tool
+ || w->windowType() == Qt::SplashScreen
+ || w->windowType() == Qt::ToolTip
+ || w->windowType() == Qt::Drawer
+ || w->windowType() == Qt::Popup)
+ && !w->testAttribute(Qt::WA_X11BypassTransientForHint));
+}
+
+static void do_size_hints(QWidget* widget, QWExtra *x);
+
+/*****************************************************************************
+ QWidget member functions
+ *****************************************************************************/
+
+const uint stdWidgetEventMask = // X event mask
+ (uint)(
+ KeyPressMask | KeyReleaseMask |
+ ButtonPressMask | ButtonReleaseMask |
+ KeymapStateMask |
+ ButtonMotionMask | PointerMotionMask |
+ EnterWindowMask | LeaveWindowMask |
+ FocusChangeMask |
+ ExposureMask |
+ PropertyChangeMask |
+ StructureNotifyMask
+ );
+
+const uint stdDesktopEventMask = // X event mask
+ (uint)(
+ KeymapStateMask |
+ EnterWindowMask | LeaveWindowMask |
+ PropertyChangeMask
+ );
+
+
+/*
+ The qt_ functions below are implemented in qwidgetcreate_x11.cpp.
+*/
+
+Window qt_XCreateWindow(const QWidget *creator,
+ Display *display, Window parent,
+ int x, int y, uint w, uint h,
+ int borderwidth, int depth,
+ uint windowclass, Visual *visual,
+ ulong valuemask, XSetWindowAttributes *attributes);
+Window qt_XCreateSimpleWindow(const QWidget *creator,
+ Display *display, Window parent,
+ int x, int y, uint w, uint h, int borderwidth,
+ ulong border, ulong background);
+void qt_XDestroyWindow(const QWidget *destroyer,
+ Display *display, Window window);
+
+
+static void qt_insert_sip(QWidget* scrolled_widget, int dx, int dy)
+{
+ if (!scrolled_widget->isWindow() && !scrolled_widget->internalWinId())
+ return;
+ QX11Data::ScrollInProgress sip = { X11->sip_serial++, scrolled_widget, dx, dy };
+ X11->sip_list.append(sip);
+
+ XClientMessageEvent client_message;
+ client_message.type = ClientMessage;
+ client_message.window = scrolled_widget->internalWinId();
+ client_message.format = 32;
+ client_message.message_type = ATOM(_QT_SCROLL_DONE);
+ client_message.data.l[0] = sip.id;
+
+ XSendEvent(X11->display, scrolled_widget->internalWinId(), False, NoEventMask,
+ (XEvent*)&client_message);
+}
+
+static int qt_sip_count(QWidget* scrolled_widget)
+{
+ int sips=0;
+
+ for (int i = 0; i < X11->sip_list.size(); ++i) {
+ const QX11Data::ScrollInProgress &sip = X11->sip_list.at(i);
+ if (sip.scrolled_widget == scrolled_widget)
+ sips++;
+ }
+
+ return sips;
+}
+
+static void create_wm_client_leader()
+{
+ if (X11->wm_client_leader) return;
+
+ X11->wm_client_leader =
+ XCreateSimpleWindow(X11->display,
+ QX11Info::appRootWindow(),
+ 0, 0, 1, 1, 0, 0, 0);
+
+ // set client leader property to itself
+ XChangeProperty(X11->display,
+ X11->wm_client_leader, ATOM(WM_CLIENT_LEADER),
+ XA_WINDOW, 32, PropModeReplace,
+ (unsigned char *)&X11->wm_client_leader, 1);
+
+#ifndef QT_NO_SESSIONMANAGER
+ // If we are session managed, inform the window manager about it
+ QByteArray session = qApp->sessionId().toLatin1();
+ if (!session.isEmpty()) {
+ XChangeProperty(X11->display,
+ X11->wm_client_leader, ATOM(SM_CLIENT_ID),
+ XA_STRING, 8, PropModeReplace,
+ (unsigned char *)session.data(), session.size());
+ }
+#endif
+}
+
+/*!
+ \internal
+ Update the X11 cursor of the widget w.
+ \a force is true if this function is called from dispatchEnterLeave, it means that the
+ mouse is actually directly under this widget.
+ */
+void qt_x11_enforce_cursor(QWidget * w, bool force)
+{
+ if (!w->testAttribute(Qt::WA_WState_Created))
+ return;
+
+ static QPointer<QWidget> lastUnderMouse = 0;
+ if (force) {
+ lastUnderMouse = w;
+ } else if (lastUnderMouse && lastUnderMouse->effectiveWinId() == w->effectiveWinId()) {
+ w = lastUnderMouse;
+ } else if (!w->internalWinId()) {
+ return; //the mouse is not under this widget, and it's not native, so don't change it
+ }
+
+ while (!w->internalWinId() && w->parentWidget() && !w->isWindow() && !w->testAttribute(Qt::WA_SetCursor))
+ w = w->parentWidget();
+
+ QWidget *nativeParent = w;
+ if (!w->internalWinId())
+ nativeParent = w->nativeParentWidget();
+ // This does the same as effectiveWinId(), but since it is possible
+ // to not have a native parent widget due to a special hack in
+ // qwidget for reparenting widgets to a different X11 screen,
+ // added additional check to make sure native parent widget exists.
+ if (!nativeParent || !nativeParent->internalWinId())
+ return;
+ WId winid = nativeParent->internalWinId();
+
+ if (w->isWindow() || w->testAttribute(Qt::WA_SetCursor)) {
+#ifndef QT_NO_CURSOR
+ QCursor *oc = QApplication::overrideCursor();
+ if (oc) {
+ XDefineCursor(X11->display, winid, oc->handle());
+ } else if (w->isEnabled()) {
+ XDefineCursor(X11->display, winid, w->cursor().handle());
+ } else {
+ // enforce the windows behavior of clearing the cursor on
+ // disabled widgets
+ XDefineCursor(X11->display, winid, XNone);
+ }
+#endif
+ } else {
+ XDefineCursor(X11->display, winid, XNone);
+ }
+}
+
+Q_GUI_EXPORT void qt_x11_enforce_cursor(QWidget * w)
+{
+ qt_x11_enforce_cursor(w, false);
+}
+
+
+Q_GUI_EXPORT void qt_x11_wait_for_window_manager(QWidget* w)
+{
+ if (!w || (!w->isWindow() && !w->internalWinId()))
+ return;
+ QApplication::flush();
+ XEvent ev;
+ QTime t;
+ t.start();
+ if (!w->testAttribute(Qt::WA_WState_Created))
+ return;
+ while (!XCheckTypedWindowEvent(X11->display, w->effectiveWinId(), ReparentNotify, &ev)) {
+ if (XCheckTypedWindowEvent(X11->display, w->effectiveWinId(), MapNotify, &ev))
+ break;
+ if (t.elapsed() > 500)
+ return; // give up, no event available
+ qApp->syncX(); // non-busy wait
+ }
+ qApp->x11ProcessEvent(&ev);
+ if (XCheckTypedWindowEvent(X11->display, w->effectiveWinId(), ConfigureNotify, &ev))
+ qApp->x11ProcessEvent(&ev);
+}
+
+void qt_change_net_wm_state(const QWidget* w, bool set, Atom one, Atom two = 0)
+{
+ if (!w->isVisible()) // not managed by the window manager
+ return;
+
+ XEvent e;
+ e.xclient.type = ClientMessage;
+ e.xclient.message_type = ATOM(_NET_WM_STATE);
+ e.xclient.display = X11->display;
+ e.xclient.window = w->internalWinId();
+ e.xclient.format = 32;
+ e.xclient.data.l[0] = set ? 1 : 0;
+ e.xclient.data.l[1] = one;
+ e.xclient.data.l[2] = two;
+ e.xclient.data.l[3] = 0;
+ e.xclient.data.l[4] = 0;
+ XSendEvent(X11->display, RootWindow(X11->display, w->x11Info().screen()),
+ false, (SubstructureNotifyMask | SubstructureRedirectMask), &e);
+}
+
+struct QX11WindowAttributes {
+ const XWindowAttributes *att;
+};
+
+void qt_x11_getX11InfoForWindow(QX11Info * xinfo, const XWindowAttributes &a)
+{
+ QX11WindowAttributes att;
+ att.att = &a;
+ qt_x11_getX11InfoForWindow(xinfo,att);
+}
+
+
+static QVector<Atom> getNetWmState(QWidget *w)
+{
+ QVector<Atom> returnValue;
+
+ // Don't read anything, just get the size of the property data
+ Atom actualType;
+ int actualFormat;
+ ulong propertyLength;
+ ulong bytesLeft;
+ uchar *propertyData = 0;
+ if (XGetWindowProperty(X11->display, w->internalWinId(), ATOM(_NET_WM_STATE), 0, 0,
+ False, XA_ATOM, &actualType, &actualFormat,
+ &propertyLength, &bytesLeft, &propertyData) == Success
+ && actualType == XA_ATOM && actualFormat == 32) {
+ returnValue.resize(bytesLeft / 4);
+ XFree((char*) propertyData);
+
+ // fetch all data
+ if (XGetWindowProperty(X11->display, w->internalWinId(), ATOM(_NET_WM_STATE), 0,
+ returnValue.size(), False, XA_ATOM, &actualType, &actualFormat,
+ &propertyLength, &bytesLeft, &propertyData) != Success) {
+ returnValue.clear();
+ } else if (propertyLength != (ulong)returnValue.size()) {
+ returnValue.resize(propertyLength);
+ }
+
+ // put it into netWmState
+ if (!returnValue.isEmpty()) {
+ memcpy(returnValue.data(), propertyData, returnValue.size() * sizeof(Atom));
+ }
+ XFree((char*) propertyData);
+ }
+
+ return returnValue;
+}
+
+void QWidgetPrivate::create_sys(WId window, bool initializeWindow, bool destroyOldWindow)
+{
+ Q_Q(QWidget);
+ Qt::WindowType type = q->windowType();
+ Qt::WindowFlags &flags = data.window_flags;
+ QWidget *parentWidget = q->parentWidget();
+
+ if (type == Qt::ToolTip)
+ flags |= Qt::WindowStaysOnTopHint | Qt::FramelessWindowHint | Qt::X11BypassWindowManagerHint;
+ if (type == Qt::Popup)
+ flags |= Qt::X11BypassWindowManagerHint;
+
+ bool topLevel = (flags & Qt::Window);
+ bool popup = (type == Qt::Popup);
+ bool dialog = (type == Qt::Dialog
+ || type == Qt::Sheet);
+ bool desktop = (type == Qt::Desktop);
+ bool tool = (type == Qt::Tool || type == Qt::SplashScreen
+ || type == Qt::ToolTip || type == Qt::Drawer);
+
+#ifdef ALIEN_DEBUG
+ qDebug() << "QWidgetPrivate::create_sys START:" << q << "topLevel?" << topLevel << "WId:"
+ << window << "initializeWindow:" << initializeWindow << "destroyOldWindow" << destroyOldWindow;
+#endif
+ if (topLevel) {
+ if (parentWidget) { // if our parent stays on top, so must we
+ QWidget *ptl = parentWidget->window();
+ if(ptl && (ptl->windowFlags() & Qt::WindowStaysOnTopHint))
+ flags |= Qt::WindowStaysOnTopHint;
+ }
+
+ if (type == Qt::SplashScreen) {
+ if (X11->isSupportedByWM(ATOM(_NET_WM_WINDOW_TYPE_SPLASH))) {
+ flags &= ~Qt::X11BypassWindowManagerHint;
+ } else {
+ flags |= Qt::X11BypassWindowManagerHint | Qt::FramelessWindowHint;
+ }
+ }
+ // All these buttons depend on the system menu, so we enable it
+ if (flags & (Qt::WindowMinimizeButtonHint
+ | Qt::WindowMaximizeButtonHint
+ | Qt::WindowContextHelpButtonHint))
+ flags |= Qt::WindowSystemMenuHint;
+ }
+
+
+ Window parentw, destroyw = 0;
+ WId id = 0;
+
+ // always initialize
+ if (!window)
+ initializeWindow = true;
+
+ if (desktop &&
+ qt_x11_create_desktop_on_screen >= 0 &&
+ qt_x11_create_desktop_on_screen != xinfo.screen()) {
+ // desktop on a certain screen other than the default requested
+ QX11InfoData *xd = &X11->screens[qt_x11_create_desktop_on_screen];
+ xinfo.setX11Data(xd);
+ } else if (parentWidget && parentWidget->d_func()->xinfo.screen() != xinfo.screen()) {
+ xinfo = parentWidget->d_func()->xinfo;
+ }
+
+ //get display, screen number, root window and desktop geometry for
+ //the current screen
+ Display *dpy = X11->display;
+ int scr = xinfo.screen();
+ Window root_win = RootWindow(dpy, scr);
+ int sw = DisplayWidth(dpy,scr);
+ int sh = DisplayHeight(dpy,scr);
+
+ if (desktop) { // desktop widget
+ dialog = popup = false; // force these flags off
+ data.crect.setRect(0, 0, sw, sh);
+ } else if (topLevel && !q->testAttribute(Qt::WA_Resized)) {
+ int width = sw / 2;
+ int height = 4 * sh / 10;
+ if (extra) {
+ width = qMax(qMin(width, extra->maxw), extra->minw);
+ height = qMax(qMin(height, extra->maxh), extra->minh);
+ }
+ data.crect.setSize(QSize(width, height));
+ }
+
+ parentw = topLevel ? root_win : parentWidget->effectiveWinId();
+
+ XSetWindowAttributes wsa;
+
+ if (window) { // override the old window
+ if (destroyOldWindow) {
+ if (topLevel)
+ X11->dndEnable(q, false);
+ destroyw = data.winid;
+ }
+ id = window;
+ setWinId(window);
+ XWindowAttributes a;
+ XGetWindowAttributes(dpy, window, &a);
+ data.crect.setRect(a.x, a.y, a.width, a.height);
+
+ if (a.map_state == IsUnmapped)
+ q->setAttribute(Qt::WA_WState_Visible, false);
+ else
+ q->setAttribute(Qt::WA_WState_Visible);
+
+ qt_x11_getX11InfoForWindow(&xinfo,a);
+
+ } else if (desktop) { // desktop widget
+#ifdef QWIDGET_EXTRA_DEBUG
+ qDebug() << "create desktop";
+#endif
+ id = (WId)parentw; // id = root window
+// QWidget *otherDesktop = find(id); // is there another desktop?
+// if (otherDesktop && otherDesktop->testWFlags(Qt::WPaintDesktop)) {
+// otherDesktop->d->setWinId(0); // remove id from widget mapper
+// d->setWinId(id); // make sure otherDesktop is
+// otherDesktop->d->setWinId(id); // found first
+// } else {
+ setWinId(id);
+// }
+ } else if (topLevel || q->testAttribute(Qt::WA_NativeWindow) || paintOnScreen()) {
+#ifdef QWIDGET_EXTRA_DEBUG
+ static int topLevels = 0;
+ static int children = 0;
+ if (parentw == root_win)
+ qDebug() << "create toplevel" << ++topLevels;
+ else
+ qDebug() << "create child" << ++children;
+#endif
+ QRect safeRect = data.crect; //##### must handle huge sizes as well.... i.e. wrect
+ if (safeRect.width() < 1|| safeRect.height() < 1) {
+ if (topLevel) {
+ // top-levels must be at least 1x1
+ safeRect.setSize(safeRect.size().expandedTo(QSize(1, 1)));
+ } else {
+ // create it way off screen, and rely on
+ // setWSGeometry() to do the right thing with it later
+ safeRect = QRect(-1000,-1000,1,1);
+ }
+ }
+#ifndef QT_NO_XRENDER
+ int screen = xinfo.screen();
+ if (topLevel && X11->use_xrender
+ && xinfo.depth() != 32 && X11->argbVisuals[screen]
+ && q->testAttribute(Qt::WA_TranslucentBackground))
+ {
+ QX11InfoData *xd = xinfo.getX11Data(true);
+
+ xd->screen = screen;
+ xd->visual = X11->argbVisuals[screen];
+ xd->colormap = X11->argbColormaps[screen];
+ xd->depth = 32;
+ xd->defaultVisual = false;
+ xd->defaultColormap = false;
+ xd->cells = xd->visual->map_entries;
+ xinfo.setX11Data(xd);
+ }
+#endif
+ if (xinfo.defaultVisual() && xinfo.defaultColormap()) {
+ id = (WId)qt_XCreateSimpleWindow(q, dpy, parentw,
+ safeRect.left(), safeRect.top(),
+ safeRect.width(), safeRect.height(),
+ 0,
+ BlackPixel(dpy, xinfo.screen()),
+ WhitePixel(dpy, xinfo.screen()));
+ } else {
+ wsa.background_pixel = WhitePixel(dpy, xinfo.screen());
+ wsa.border_pixel = BlackPixel(dpy, xinfo.screen());
+ wsa.colormap = xinfo.colormap();
+ id = (WId)qt_XCreateWindow(q, dpy, parentw,
+ safeRect.left(), safeRect.top(),
+ safeRect.width(), safeRect.height(),
+ 0, xinfo.depth(), InputOutput,
+ (Visual *) xinfo.visual(),
+ CWBackPixel|CWBorderPixel|CWColormap,
+ &wsa);
+ }
+
+ setWinId(id); // set widget id/handle + hd
+ }
+
+#ifndef QT_NO_XRENDER
+ if (picture) {
+ XRenderFreePicture(X11->display, picture);
+ picture = 0;
+ }
+
+ if (X11->use_xrender && !desktop && q->internalWinId()) {
+ XRenderPictFormat *format = XRenderFindVisualFormat(dpy, (Visual *) xinfo.visual());
+ if (format)
+ picture = XRenderCreatePicture(dpy, id, format, 0, 0);
+ }
+#endif // QT_NO_XRENDER
+
+ QtMWMHints mwmhints;
+ mwmhints.flags = 0L;
+ mwmhints.functions = 0L;
+ mwmhints.decorations = 0;
+ mwmhints.input_mode = 0L;
+ mwmhints.status = 0L;
+
+ if (topLevel) {
+ ulong wsa_mask = 0;
+ if (type != Qt::SplashScreen) { // && customize) {
+ mwmhints.flags |= MWM_HINTS_DECORATIONS;
+
+ bool customize = flags & Qt::CustomizeWindowHint;
+ if (!(flags & Qt::FramelessWindowHint) && !(customize && !(flags & Qt::WindowTitleHint))) {
+ mwmhints.decorations |= MWM_DECOR_BORDER;
+ mwmhints.decorations |= MWM_DECOR_RESIZEH;
+
+ if (flags & Qt::WindowTitleHint)
+ mwmhints.decorations |= MWM_DECOR_TITLE;
+
+ if (flags & Qt::WindowSystemMenuHint)
+ mwmhints.decorations |= MWM_DECOR_MENU;
+
+ if (flags & Qt::WindowMinimizeButtonHint) {
+ mwmhints.decorations |= MWM_DECOR_MINIMIZE;
+ mwmhints.functions |= MWM_FUNC_MINIMIZE;
+ }
+
+ if (flags & Qt::WindowMaximizeButtonHint) {
+ mwmhints.decorations |= MWM_DECOR_MAXIMIZE;
+ mwmhints.functions |= MWM_FUNC_MAXIMIZE;
+ }
+
+ if (flags & Qt::WindowCloseButtonHint)
+ mwmhints.functions |= MWM_FUNC_CLOSE;
+ }
+ } else {
+ // if type == Qt::SplashScreen
+ mwmhints.decorations = MWM_DECOR_ALL;
+ }
+
+ if (tool) {
+ wsa.save_under = True;
+ wsa_mask |= CWSaveUnder;
+ }
+
+ if (flags & Qt::X11BypassWindowManagerHint) {
+ wsa.override_redirect = True;
+ wsa_mask |= CWOverrideRedirect;
+ }
+
+ if (wsa_mask && initializeWindow) {
+ Q_ASSERT(id);
+ XChangeWindowAttributes(dpy, id, wsa_mask, &wsa);
+ }
+
+ if (mwmhints.functions != 0) {
+ mwmhints.flags |= MWM_HINTS_FUNCTIONS;
+ mwmhints.functions |= MWM_FUNC_MOVE | MWM_FUNC_RESIZE;
+ } else {
+ mwmhints.functions = MWM_FUNC_ALL;
+ }
+
+ if (!(flags & Qt::FramelessWindowHint)
+ && flags & Qt::CustomizeWindowHint
+ && flags & Qt::WindowTitleHint
+ && !(flags &
+ (Qt::WindowMinimizeButtonHint
+ | Qt::WindowMaximizeButtonHint
+ | Qt::WindowCloseButtonHint))) {
+ // a special case - only the titlebar without any button
+ mwmhints.flags = MWM_HINTS_FUNCTIONS;
+ mwmhints.functions = MWM_FUNC_MOVE | MWM_FUNC_RESIZE;
+ mwmhints.decorations = 0;
+ }
+ }
+
+ if (!initializeWindow) {
+ // do no initialization
+ } else if (popup) { // popup widget
+ // set EWMH window types
+ setNetWmWindowTypes();
+
+ wsa.override_redirect = True;
+ wsa.save_under = True;
+ Q_ASSERT(id);
+ XChangeWindowAttributes(dpy, id, CWOverrideRedirect | CWSaveUnder,
+ &wsa);
+ } else if (topLevel && !desktop) { // top-level widget
+ if (!X11->wm_client_leader)
+ create_wm_client_leader();
+
+ // note: WM_TRANSIENT_FOR is set in QWidgetPrivate::show_sys()
+
+ XSizeHints size_hints;
+ size_hints.flags = USSize | PSize | PWinGravity;
+ size_hints.x = data.crect.left();
+ size_hints.y = data.crect.top();
+ size_hints.width = data.crect.width();
+ size_hints.height = data.crect.height();
+ size_hints.win_gravity =
+ QApplication::isRightToLeft() ? NorthEastGravity : NorthWestGravity;
+
+ XWMHints wm_hints; // window manager hints
+ memset(&wm_hints, 0, sizeof(wm_hints)); // make valgrind happy
+ wm_hints.flags = InputHint | StateHint | WindowGroupHint;
+ wm_hints.input = True;
+ wm_hints.initial_state = NormalState;
+ wm_hints.window_group = X11->wm_client_leader;
+
+ XClassHint class_hint;
+ QByteArray appName = qAppName().toLatin1();
+ class_hint.res_name = appName.data(); // application name
+ class_hint.res_class = const_cast<char *>(QX11Info::appClass()); // application class
+
+ XSetWMProperties(dpy, id, 0, 0,
+ qApp->d_func()->argv, qApp->d_func()->argc,
+ &size_hints, &wm_hints, &class_hint);
+
+ XResizeWindow(dpy, id,
+ qBound(1, data.crect.width(), XCOORD_MAX),
+ qBound(1, data.crect.height(), XCOORD_MAX));
+ XStoreName(dpy, id, appName.data());
+ Atom protocols[4];
+ int n = 0;
+ protocols[n++] = ATOM(WM_DELETE_WINDOW); // support del window protocol
+ protocols[n++] = ATOM(WM_TAKE_FOCUS); // support take focus window protocol
+ protocols[n++] = ATOM(_NET_WM_PING); // support _NET_WM_PING protocol
+ if (flags & Qt::WindowContextHelpButtonHint)
+ protocols[n++] = ATOM(_NET_WM_CONTEXT_HELP);
+ XSetWMProtocols(dpy, id, protocols, n);
+
+ // set mwm hints
+ SetMWMHints(dpy, id, mwmhints);
+
+ // set EWMH window types
+ setNetWmWindowTypes();
+
+ // set _NET_WM_PID
+ long curr_pid = getpid();
+ XChangeProperty(dpy, id, ATOM(_NET_WM_PID), XA_CARDINAL, 32, PropModeReplace,
+ (unsigned char *) &curr_pid, 1);
+
+ // when we create a toplevel widget, the frame strut should be dirty
+ data.fstrut_dirty = 1;
+
+ // declare the widget's window role
+ if (QTLWExtra *topData = maybeTopData()) {
+ if (!topData->role.isEmpty()) {
+ QByteArray windowRole = topData->role.toUtf8();
+ XChangeProperty(dpy, id,
+ ATOM(WM_WINDOW_ROLE), XA_STRING, 8, PropModeReplace,
+ (unsigned char *)windowRole.constData(), windowRole.length());
+ }
+ }
+
+ // set client leader property
+ XChangeProperty(dpy, id, ATOM(WM_CLIENT_LEADER),
+ XA_WINDOW, 32, PropModeReplace,
+ (unsigned char *)&X11->wm_client_leader, 1);
+ } else {
+ // non-toplevel widgets don't have a frame, so no need to
+ // update the strut
+ data.fstrut_dirty = 0;
+ }
+
+ if (initializeWindow && q->internalWinId()) {
+ // don't erase when resizing
+ wsa.bit_gravity = QApplication::isRightToLeft() ? NorthEastGravity : NorthWestGravity;
+ Q_ASSERT(id);
+ XChangeWindowAttributes(dpy, id, CWBitGravity, &wsa);
+ }
+
+ // set X11 event mask
+ if (desktop) {
+// QWidget* main_desktop = find(id);
+// if (main_desktop->testWFlags(Qt::WPaintDesktop))
+// XSelectInput(dpy, id, stdDesktopEventMask | ExposureMask);
+// else
+ XSelectInput(dpy, id, stdDesktopEventMask);
+ } else if (q->internalWinId()) {
+ XSelectInput(dpy, id, stdWidgetEventMask);
+#if !defined (QT_NO_TABLET)
+ QTabletDeviceDataList *tablet_list = qt_tablet_devices();
+ if (X11->ptrXSelectExtensionEvent) {
+ for (int i = 0; i < tablet_list->size(); ++i) {
+ QTabletDeviceData tablet = tablet_list->at(i);
+ X11->ptrXSelectExtensionEvent(dpy, id, reinterpret_cast<XEventClass*>(tablet.eventList),
+ tablet.eventCount);
+ }
+ }
+#endif
+ }
+
+ if (desktop) {
+ q->setAttribute(Qt::WA_WState_Visible);
+ } else if (topLevel) { // set X cursor
+ if (initializeWindow) {
+ qt_x11_enforce_cursor(q);
+
+ if (QTLWExtra *topData = maybeTopData())
+ if (!topData->caption.isEmpty())
+ setWindowTitle_helper(topData->caption);
+
+ //always enable dnd: it's not worth the effort to maintain the state
+ // NOTE: this always creates topData()
+ X11->dndEnable(q, true);
+
+ if (maybeTopData() && maybeTopData()->opacity != 255)
+ q->setWindowOpacity(maybeTopData()->opacity/255.);
+
+ }
+ } else if (q->testAttribute(Qt::WA_SetCursor) && q->internalWinId()) {
+ qt_x11_enforce_cursor(q);
+ }
+
+ if (extra && !extra->mask.isEmpty() && q->internalWinId())
+ XShapeCombineRegion(X11->display, q->internalWinId(), ShapeBounding, 0, 0,
+ extra->mask.handle(), ShapeSet);
+
+ if (q->hasFocus() && q->testAttribute(Qt::WA_InputMethodEnabled)) {
+ QInputContext *inputContext = q->inputContext();
+ if (inputContext)
+ inputContext->setFocusWidget(q);
+ }
+
+ if (destroyw)
+ qt_XDestroyWindow(q, dpy, destroyw);
+
+ // newly created windows are positioned at the window system's
+ // (0,0) position. If the parent uses wrect mapping to expand the
+ // coordinate system, we must also adjust this widget's window
+ // system position
+ if (!topLevel && !parentWidget->data->wrect.topLeft().isNull())
+ setWSGeometry();
+ else if (topLevel && (data.crect.width() == 0 || data.crect.height() == 0))
+ q->setAttribute(Qt::WA_OutsideWSRange, true);
+
+ if (!topLevel && q->testAttribute(Qt::WA_NativeWindow) && q->testAttribute(Qt::WA_Mapped)) {
+ Q_ASSERT(q->internalWinId());
+ XMapWindow(X11->display, q->internalWinId());
+ // Ensure that mapped alien widgets are flushed immediately when re-created as native widgets.
+ if (QWindowSurface *surface = q->windowSurface())
+ surface->flush(q, q->rect(), q->mapTo(surface->window(), QPoint()));
+ }
+
+#ifdef ALIEN_DEBUG
+ qDebug() << "QWidgetPrivate::create_sys END:" << q;
+#endif
+}
+
+void QWidgetPrivate::x11UpdateIsOpaque()
+{
+#ifndef QT_NO_XRENDER
+ Q_Q(QWidget);
+ if (!q->testAttribute(Qt::WA_WState_Created) || !q->testAttribute(Qt::WA_TranslucentBackground))
+ return;
+
+ bool topLevel = (data.window_flags & Qt::Window);
+ int screen = xinfo.screen();
+ if (topLevel && X11->use_xrender
+ && X11->argbVisuals[screen] && xinfo.depth() != 32) {
+ // recreate widget
+ QPoint pos = q->pos();
+ bool visible = q->isVisible();
+ if (visible)
+ q->hide();
+ q->setParent(q->parentWidget(), q->windowFlags() & ~Qt::WindowType_Mask);
+ q->move(pos);
+ if (visible)
+ q->show();
+ }
+#endif
+}
+
+void QWidget::destroy(bool destroyWindow, bool destroySubWindows)
+{
+ Q_D(QWidget);
+ if (!isWindow() && parentWidget())
+ parentWidget()->d_func()->invalidateBuffer(geometry());
+ d->deactivateWidgetCleanup();
+ if (testAttribute(Qt::WA_WState_Created)) {
+ setAttribute(Qt::WA_WState_Created, false);
+ QObjectList childList = children();
+ for (int i = 0; i < childList.size(); ++i) { // destroy all widget children
+ register QObject *obj = childList.at(i);
+ if (obj->isWidgetType())
+ static_cast<QWidget*>(obj)->destroy(destroySubWindows,
+ destroySubWindows);
+ }
+ if (QWidgetPrivate::mouseGrabber == this)
+ releaseMouse();
+ if (QWidgetPrivate::keyboardGrabber == this)
+ releaseKeyboard();
+ if (isWindow())
+ X11->deferred_map.removeAll(this);
+ if (isModal()) {
+ // just be sure we leave modal
+ QApplicationPrivate::leaveModal(this);
+ }
+ else if ((windowType() == Qt::Popup))
+ qApp->d_func()->closePopup(this);
+
+#ifndef QT_NO_XRENDER
+ if (d->picture) {
+ if (destroyWindow)
+ XRenderFreePicture(X11->display, d->picture);
+ d->picture = 0;
+ }
+#endif // QT_NO_XRENDER
+
+ // delete the _NET_WM_USER_TIME_WINDOW
+ qt_net_remove_user_time(this);
+
+ if ((windowType() == Qt::Desktop)) {
+ if (acceptDrops())
+ X11->dndEnable(this, false);
+ } else {
+ if (isWindow())
+ X11->dndEnable(this, false);
+ if (destroyWindow)
+ qt_XDestroyWindow(this, X11->display, data->winid);
+ }
+ d->setWinId(0);
+
+ extern void qPRCleanup(QWidget *widget); // from qapplication_x11.cpp
+ if (testAttribute(Qt::WA_WState_Reparented))
+ qPRCleanup(this);
+
+ if(d->ic) {
+ delete d->ic;
+ } else {
+ // release previous focus information participating with
+ // preedit preservation of qic
+ QInputContext *qic = inputContext();
+ if (qic)
+ qic->widgetDestroyed(this);
+ }
+ }
+}
+
+void QWidgetPrivate::setParent_sys(QWidget *parent, Qt::WindowFlags f)
+{
+ Q_Q(QWidget);
+#ifdef ALIEN_DEBUG
+ qDebug() << "QWidgetPrivate::setParent_sys START" << q << "parent:" << parent;
+#endif
+ QX11Info old_xinfo = xinfo;
+ if (parent && parent->windowType() == Qt::Desktop) {
+ // make sure the widget is created on the same screen as the
+ // programmer specified desktop widget
+ xinfo = parent->d_func()->xinfo;
+ parent = 0;
+ }
+
+ QTLWExtra *topData = maybeTopData();
+ bool wasCreated = q->testAttribute(Qt::WA_WState_Created);
+ if (q->isVisible() && q->parentWidget() && parent != q->parentWidget())
+ q->parentWidget()->d_func()->invalidateBuffer(q->geometry());
+ extern void qPRCreate(const QWidget *, Window);
+#ifndef QT_NO_CURSOR
+ QCursor oldcurs;
+#endif
+
+ // dnd unregister (we will register again below)
+ if (q->testAttribute(Qt::WA_DropSiteRegistered))
+ q->setAttribute(Qt::WA_DropSiteRegistered, false);
+
+ // if we are a top then remove our dnd prop for now
+ // it will get rest later
+ if (q->isWindow() && wasCreated)
+ X11->dndEnable(q, false);
+
+ if (topData)
+ qt_net_remove_user_time(q);
+
+// QWidget *oldparent = q->parentWidget();
+ WId old_winid = wasCreated ? data.winid : 0;
+ if ((q->windowType() == Qt::Desktop))
+ old_winid = 0;
+ setWinId(0);
+
+#ifndef QT_NO_XRENDER
+ if (picture) {
+ XRenderFreePicture(X11->display, picture);
+ picture = 0;
+ }
+#endif
+
+ // hide and reparent our own window away. Otherwise we might get
+ // destroyed when emitting the child remove event below. See QWorkspace.
+ if (wasCreated && old_winid) {
+ XUnmapWindow(X11->display, old_winid);
+ if (!old_xinfo.screen() != xinfo.screen())
+ XReparentWindow(X11->display, old_winid, RootWindow(X11->display, xinfo.screen()), 0, 0);
+ }
+ if (topData) {
+ topData->parentWinId = 0;
+ // zero the frame strut and mark it dirty
+ topData->frameStrut.setCoords(0, 0, 0, 0);
+
+ // reparenting from top-level, make sure show() works again
+ topData->waitingForMapNotify = 0;
+ topData->validWMState = 0;
+ }
+ data.fstrut_dirty = (!parent || (f & Qt::Window)); // toplevels get a dirty framestrut
+
+ QObjectPrivate::setParent_helper(parent);
+ bool explicitlyHidden = q->testAttribute(Qt::WA_WState_Hidden) && q->testAttribute(Qt::WA_WState_ExplicitShowHide);
+
+ data.window_flags = f;
+ q->setAttribute(Qt::WA_WState_Created, false);
+ q->setAttribute(Qt::WA_WState_Visible, false);
+ q->setAttribute(Qt::WA_WState_Hidden, false);
+ adjustFlags(data.window_flags, q);
+ // keep compatibility with previous versions, we need to preserve the created state
+ // (but we recreate the winId for the widget being reparented, again for compatibility)
+ if (wasCreated || (!q->isWindow() && parent->testAttribute(Qt::WA_WState_Created)))
+ createWinId();
+ if (q->isWindow() || (!parent || parent->isVisible()) || explicitlyHidden)
+ q->setAttribute(Qt::WA_WState_Hidden);
+ q->setAttribute(Qt::WA_WState_ExplicitShowHide, explicitlyHidden);
+
+ if (wasCreated) {
+ QObjectList chlist = q->children();
+ for (int i = 0; i < chlist.size(); ++i) { // reparent children
+ QObject *obj = chlist.at(i);
+ if (obj->isWidgetType()) {
+ QWidget *w = (QWidget *)obj;
+ if (!w->testAttribute(Qt::WA_WState_Created))
+ continue;
+ if (xinfo.screen() != w->d_func()->xinfo.screen()) {
+ // ### force setParent() to not shortcut out (because
+ // ### we're setting the parent to the current parent)
+ // ### setParent will add child back to the list
+ // ### of children so we need to make sure the
+ // ### widget won't be added twice.
+ w->d_func()->parent = 0;
+ this->children.removeOne(w);
+ w->setParent(q);
+ } else if (!w->isWindow()) {
+ w->d_func()->invalidateBuffer(w->rect());
+ if (w->internalWinId()) {
+ if (w->testAttribute(Qt::WA_NativeWindow)) {
+ QWidget *nativeParentWidget = w->nativeParentWidget();
+ // Qt::WA_NativeWindow ensures that we always have a nativeParentWidget
+ Q_ASSERT(nativeParentWidget != 0);
+ QPoint p = w->mapTo(nativeParentWidget, QPoint());
+ XReparentWindow(X11->display,
+ w->internalWinId(),
+ nativeParentWidget->internalWinId(),
+ p.x(), p.y());
+ } else {
+ w->d_func()->setParent_sys(q, w->data->window_flags);
+ }
+ }
+ } else if (isTransient(w)) {
+ /*
+ when reparenting toplevel windows with toplevel-transient children,
+ we need to make sure that the window manager gets the updated
+ WM_TRANSIENT_FOR information... unfortunately, some window managers
+ don't handle changing WM_TRANSIENT_FOR before the toplevel window is
+ visible, so we unmap and remap all toplevel-transient children *after*
+ the toplevel parent has been mapped. thankfully, this is easy in Qt :)
+
+ note that the WM_TRANSIENT_FOR hint is actually updated in
+ QWidgetPrivate::show_sys()
+ */
+ if (w->internalWinId())
+ XUnmapWindow(X11->display, w->internalWinId());
+ QApplication::postEvent(w, new QEvent(QEvent::ShowWindowRequest));
+ }
+ }
+ }
+ qPRCreate(q, old_winid);
+ updateSystemBackground();
+
+ if (old_winid) {
+ Window *cmwret;
+ int count;
+ if (XGetWMColormapWindows(X11->display, old_winid, &cmwret, &count)) {
+ Window *cmw;
+ int cmw_size = sizeof(Window)*count;
+ cmw = new Window[count];
+ memcpy((char *)cmw, (char *)cmwret, cmw_size);
+ XFree((char *)cmwret);
+ int i;
+ for (i=0; i<count; i++) {
+ if (cmw[i] == old_winid) {
+ cmw[i] = q->internalWinId();
+ break;
+ }
+ }
+ int top_count;
+ if (XGetWMColormapWindows(X11->display, q->window()->internalWinId(),
+ &cmwret, &top_count))
+ {
+ Window *merged_cmw = new Window[count + top_count];
+ memcpy((char *)merged_cmw, (char *)cmw, cmw_size);
+ memcpy((char *)merged_cmw + cmw_size, (char *)cmwret, sizeof(Window)*top_count);
+ delete [] cmw;
+ XFree((char *)cmwret);
+ cmw = merged_cmw;
+ count += top_count;
+ }
+
+ XSetWMColormapWindows(X11->display, q->window()->internalWinId(), cmw, count);
+ delete [] cmw;
+ }
+
+ qt_XDestroyWindow(q, X11->display, old_winid);
+ }
+ }
+
+ // check if we need to register our dropsite
+ if (q->testAttribute(Qt::WA_AcceptDrops)
+ || (!q->isWindow() && q->parentWidget() && q->parentWidget()->testAttribute(Qt::WA_DropSiteRegistered))) {
+ q->setAttribute(Qt::WA_DropSiteRegistered, true);
+ }
+#if !defined(QT_NO_IM)
+ ic = 0;
+#endif
+ invalidateBuffer(q->rect());
+#ifdef ALIEN_DEBUG
+ qDebug() << "QWidgetPrivate::setParent_sys END" << q;
+#endif
+}
+
+
+QPoint QWidget::mapToGlobal(const QPoint &pos) const
+{
+ Q_D(const QWidget);
+ if (!testAttribute(Qt::WA_WState_Created) || !internalWinId()) {
+ QPoint p = pos + data->crect.topLeft();
+ //cannot trust that !isWindow() implies parentWidget() before create
+ return (isWindow() || !parentWidget()) ? p : parentWidget()->mapToGlobal(p);
+ }
+ int x, y;
+ Window child;
+ QPoint p = d->mapToWS(pos);
+ XTranslateCoordinates(X11->display, internalWinId(),
+ QApplication::desktop()->screen(d->xinfo.screen())->internalWinId(),
+ p.x(), p.y(), &x, &y, &child);
+ return QPoint(x, y);
+}
+
+
+QPoint QWidget::mapFromGlobal(const QPoint &pos) const
+{
+ Q_D(const QWidget);
+ if (!testAttribute(Qt::WA_WState_Created) || !internalWinId()) {
+ //cannot trust that !isWindow() implies parentWidget() before create
+ QPoint p = (isWindow() || !parentWidget()) ? pos : parentWidget()->mapFromGlobal(pos);
+ return p - data->crect.topLeft();
+ }
+ int x, y;
+ Window child;
+ XTranslateCoordinates(X11->display,
+ QApplication::desktop()->screen(d->xinfo.screen())->internalWinId(),
+ internalWinId(), pos.x(), pos.y(), &x, &y, &child);
+ return d->mapFromWS(QPoint(x, y));
+}
+
+void QWidgetPrivate::updateSystemBackground()
+{
+ Q_Q(QWidget);
+ if (!q->testAttribute(Qt::WA_WState_Created) || !q->internalWinId())
+ return;
+ QBrush brush = q->palette().brush(QPalette::Active, q->backgroundRole());
+ Qt::WindowType type = q->windowType();
+ if (brush.style() == Qt::NoBrush
+ || q->testAttribute(Qt::WA_NoSystemBackground)
+ || q->testAttribute(Qt::WA_UpdatesDisabled)
+ || type == Qt::Popup || type == Qt::ToolTip
+ )
+ XSetWindowBackgroundPixmap(X11->display, q->internalWinId(), XNone);
+ else if (brush.style() == Qt::SolidPattern && brush.isOpaque())
+ XSetWindowBackground(X11->display, q->internalWinId(),
+ QColormap::instance(xinfo.screen()).pixel(brush.color()));
+ else if (isBackgroundInherited())
+ XSetWindowBackgroundPixmap(X11->display, q->internalWinId(), ParentRelative);
+ else if (brush.style() == Qt::TexturePattern) {
+ extern QPixmap qt_toX11Pixmap(const QPixmap &pixmap); // qpixmap_x11.cpp
+ XSetWindowBackgroundPixmap(X11->display, q->internalWinId(),
+ static_cast<QX11PixmapData*>(qt_toX11Pixmap(brush.texture()).data)->x11ConvertToDefaultDepth());
+ } else
+ XSetWindowBackground(X11->display, q->internalWinId(),
+ QColormap::instance(xinfo.screen()).pixel(brush.color()));
+}
+
+#ifndef QT_NO_CURSOR
+void QWidgetPrivate::setCursor_sys(const QCursor &)
+{
+ Q_Q(QWidget);
+ qt_x11_enforce_cursor(q);
+ XFlush(X11->display);
+}
+
+void QWidgetPrivate::unsetCursor_sys()
+{
+ Q_Q(QWidget);
+ qt_x11_enforce_cursor(q);
+ XFlush(X11->display);
+}
+#endif
+
+static XTextProperty*
+qstring_to_xtp(const QString& s)
+{
+ static XTextProperty tp = { 0, 0, 0, 0 };
+ static bool free_prop = true; // we can't free tp.value in case it references
+ // the data of the static QCString below.
+ if (tp.value) {
+ if (free_prop)
+ XFree(tp.value);
+ tp.value = 0;
+ free_prop = true;
+ }
+
+ static const QTextCodec* mapper = QTextCodec::codecForLocale();
+ int errCode = 0;
+ if (mapper) {
+ QByteArray mapped = mapper->fromUnicode(s);
+ char* tl[2];
+ tl[0] = mapped.data();
+ tl[1] = 0;
+ errCode = XmbTextListToTextProperty(X11->display, tl, 1, XStdICCTextStyle, &tp);
+#if defined(QT_DEBUG)
+ if (errCode < 0)
+ qDebug("qstring_to_xtp result code %d", errCode);
+#endif
+ }
+ if (!mapper || errCode < 0) {
+ static QByteArray qcs;
+ qcs = s.toAscii();
+ tp.value = (uchar*)qcs.data();
+ tp.encoding = XA_STRING;
+ tp.format = 8;
+ tp.nitems = qcs.length();
+ free_prop = false;
+ }
+
+ // ### If we knew WM could understand unicode, we could use
+ // ### a much simpler, cheaper encoding...
+ /*
+ tp.value = (XChar2b*)s.unicode();
+ tp.encoding = XA_UNICODE; // wish
+ tp.format = 16;
+ tp.nitems = s.length();
+ */
+
+ return &tp;
+}
+
+void QWidgetPrivate::setWindowTitle_sys(const QString &caption)
+{
+ Q_Q(QWidget);
+ Q_ASSERT(q->testAttribute(Qt::WA_WState_Created));
+ if (!q->internalWinId())
+ return;
+ XSetWMName(X11->display, q->internalWinId(), qstring_to_xtp(caption));
+
+ QByteArray net_wm_name = caption.toUtf8();
+ XChangeProperty(X11->display, q->internalWinId(), ATOM(_NET_WM_NAME), ATOM(UTF8_STRING), 8,
+ PropModeReplace, (unsigned char *)net_wm_name.data(), net_wm_name.size());
+}
+
+void QWidgetPrivate::setWindowIcon_sys(bool forceReset)
+{
+ Q_Q(QWidget);
+ if (!q->testAttribute(Qt::WA_WState_Created))
+ return;
+ QTLWExtra *topData = this->topData();
+ if (topData->iconPixmap && !forceReset)
+ // already been set
+ return;
+
+ XWMHints *h = 0;
+ if (q->internalWinId())
+ h = XGetWMHints(X11->display, q->internalWinId());
+ XWMHints wm_hints;
+ if (!h) {
+ memset(&wm_hints, 0, sizeof(wm_hints)); // make valgrind happy
+ h = &wm_hints;
+ }
+
+ // preparing images to set the _NET_WM_ICON property
+ QIcon icon = q->windowIcon();
+ if (!icon.isNull()) {
+ QList<QSize> availableSizes = icon.availableSizes();
+ if(availableSizes.isEmpty()) {
+ // try to use default sizes since the icon can be a scalable image like svg.
+ availableSizes.push_back(QSize(16,16));
+ availableSizes.push_back(QSize(32,32));
+ availableSizes.push_back(QSize(64,64));
+ availableSizes.push_back(QSize(128,128));
+ }
+ QVector<long> icon_data;
+ for(int i = 0; i < availableSizes.size(); ++i) {
+ QSize size = availableSizes.at(i);
+ QPixmap pixmap = icon.pixmap(size);
+ if (!pixmap.isNull()) {
+ QImage image = pixmap.toImage().convertToFormat(QImage::Format_ARGB32);
+ int pos = icon_data.size();
+ icon_data.resize(pos + 2 + image.width()*image.height());
+ icon_data[pos++] = image.width();
+ icon_data[pos++] = image.height();
+ if (sizeof(long) == sizeof(quint32)) {
+ memcpy(icon_data.data() + pos, image.scanLine(0), image.numBytes());
+ } else {
+ for (int y = 0; y < image.height(); ++y) {
+ uint *scanLine = reinterpret_cast<uint *>(image.scanLine(y));
+ for (int x = 0; x < image.width(); ++x)
+ icon_data[pos + y*image.width() + x] = scanLine[x];
+ }
+ }
+ }
+ }
+ if (!icon_data.isEmpty()) {
+ if (q->internalWinId()) {
+ XChangeProperty(X11->display, q->internalWinId(), ATOM(_NET_WM_ICON), XA_CARDINAL, 32,
+ PropModeReplace, (unsigned char *) icon_data.data(),
+ icon_data.size());
+ }
+ extern QPixmap qt_toX11Pixmap(const QPixmap &pixmap);
+ /*
+ if the app is running on an unknown desktop, or it is not
+ using the default visual, convert the icon to 1bpp as stated
+ in the ICCCM section 4.1.2.4; otherwise, create the icon pixmap
+ in the default depth (even though this violates the ICCCM)
+ */
+ if (X11->desktopEnvironment == DE_UNKNOWN
+ || !QX11Info::appDefaultVisual(xinfo.screen())
+ || !QX11Info::appDefaultColormap(xinfo.screen())) {
+ // unknown DE or non-default visual/colormap, use 1bpp bitmap
+ if (!forceReset || !topData->iconPixmap)
+ topData->iconPixmap = new QBitmap(qt_toX11Pixmap(icon.pixmap(QSize(64,64))));
+ h->icon_pixmap = topData->iconPixmap->handle();
+ } else {
+ // default depth, use a normal pixmap (even though this
+ // violates the ICCCM), since this works on all DEs known to Qt
+ if (!forceReset || !topData->iconPixmap)
+ topData->iconPixmap = new QPixmap(qt_toX11Pixmap(icon.pixmap(QSize(64,64))));
+ h->icon_pixmap = static_cast<QX11PixmapData*>(topData->iconPixmap->data)->x11ConvertToDefaultDepth();
+ }
+ h->flags |= IconPixmapHint;
+ } else {
+ h->flags &= ~(IconPixmapHint | IconMaskHint);
+ }
+ }
+
+ if (q->internalWinId())
+ XSetWMHints(X11->display, q->internalWinId(), h);
+ if (h != &wm_hints)
+ XFree((char *)h);
+}
+
+void QWidgetPrivate::setWindowIconText_sys(const QString &iconText)
+{
+ Q_Q(QWidget);
+ if (!q->internalWinId())
+ return;
+ Q_ASSERT(q->testAttribute(Qt::WA_WState_Created));
+ XSetWMIconName(X11->display, q->internalWinId(), qstring_to_xtp(iconText));
+
+ QByteArray icon_name = iconText.toUtf8();
+ XChangeProperty(X11->display, q->internalWinId(), ATOM(_NET_WM_ICON_NAME), ATOM(UTF8_STRING), 8,
+ PropModeReplace, (unsigned char *) icon_name.constData(), icon_name.size());
+}
+
+
+void QWidget::grabMouse()
+{
+ if (isVisible() && !qt_nograb()) {
+ if (QWidgetPrivate::mouseGrabber && QWidgetPrivate::mouseGrabber != this)
+ QWidgetPrivate::mouseGrabber->releaseMouse();
+ Q_ASSERT(testAttribute(Qt::WA_WState_Created));
+#ifndef QT_NO_DEBUG
+ int status =
+#endif
+ XGrabPointer(X11->display, effectiveWinId(), False,
+ (uint)(ButtonPressMask | ButtonReleaseMask |
+ PointerMotionMask | EnterWindowMask |
+ LeaveWindowMask),
+ GrabModeAsync, GrabModeAsync,
+ XNone, XNone, X11->time);
+#ifndef QT_NO_DEBUG
+ if (status) {
+ const char *s =
+ status == GrabNotViewable ? "\"GrabNotViewable\"" :
+ status == AlreadyGrabbed ? "\"AlreadyGrabbed\"" :
+ status == GrabFrozen ? "\"GrabFrozen\"" :
+ status == GrabInvalidTime ? "\"GrabInvalidTime\"" :
+ "<?>";
+ qWarning("QWidget::grabMouse: Failed with %s", s);
+ }
+#endif
+ QWidgetPrivate::mouseGrabber = this;
+ }
+}
+
+
+#ifndef QT_NO_CURSOR
+void QWidget::grabMouse(const QCursor &cursor)
+{
+ if (!qt_nograb()) {
+ if (QWidgetPrivate::mouseGrabber && QWidgetPrivate::mouseGrabber != this)
+ QWidgetPrivate::mouseGrabber->releaseMouse();
+ Q_ASSERT(testAttribute(Qt::WA_WState_Created));
+#ifndef QT_NO_DEBUG
+ int status =
+#endif
+ XGrabPointer(X11->display, effectiveWinId(), False,
+ (uint)(ButtonPressMask | ButtonReleaseMask |
+ PointerMotionMask | EnterWindowMask | LeaveWindowMask),
+ GrabModeAsync, GrabModeAsync,
+ XNone, cursor.handle(), X11->time);
+#ifndef QT_NO_DEBUG
+ if (status) {
+ const char *s =
+ status == GrabNotViewable ? "\"GrabNotViewable\"" :
+ status == AlreadyGrabbed ? "\"AlreadyGrabbed\"" :
+ status == GrabFrozen ? "\"GrabFrozen\"" :
+ status == GrabInvalidTime ? "\"GrabInvalidTime\"" :
+ "<?>";
+ qWarning("QWidget::grabMouse: Failed with %s", s);
+ }
+#endif
+ QWidgetPrivate::mouseGrabber = this;
+ }
+}
+#endif
+
+
+void QWidget::releaseMouse()
+{
+ if (!qt_nograb() && QWidgetPrivate::mouseGrabber == this) {
+ XUngrabPointer(X11->display, X11->time);
+ XFlush(X11->display);
+ QWidgetPrivate::mouseGrabber = 0;
+ }
+}
+
+
+void QWidget::grabKeyboard()
+{
+ if (!qt_nograb()) {
+ if (QWidgetPrivate::keyboardGrabber && QWidgetPrivate::keyboardGrabber != this)
+ QWidgetPrivate::keyboardGrabber->releaseKeyboard();
+ XGrabKeyboard(X11->display, effectiveWinId(), False, GrabModeAsync, GrabModeAsync,
+ X11->time);
+ QWidgetPrivate::keyboardGrabber = this;
+ }
+}
+
+
+void QWidget::releaseKeyboard()
+{
+ if (!qt_nograb() && QWidgetPrivate::keyboardGrabber == this) {
+ XUngrabKeyboard(X11->display, X11->time);
+ QWidgetPrivate::keyboardGrabber = 0;
+ }
+}
+
+
+QWidget *QWidget::mouseGrabber()
+{
+ return QWidgetPrivate::mouseGrabber;
+}
+
+
+QWidget *QWidget::keyboardGrabber()
+{
+ return QWidgetPrivate::keyboardGrabber;
+}
+
+void QWidget::activateWindow()
+{
+ Q_D(QWidget);
+ QWidget *tlw = window();
+ if (tlw->isVisible() && !tlw->d_func()->topData()->embedded && !X11->deferred_map.contains(tlw)) {
+ if (X11->userTime == 0)
+ X11->userTime = X11->time;
+ qt_net_update_user_time(tlw, X11->userTime);
+ XSetInputFocus(X11->display, tlw->internalWinId(), XRevertToParent, X11->time);
+ d->focusInputContext();
+ }
+}
+
+void QWidget::setWindowState(Qt::WindowStates newstate)
+{
+ Q_D(QWidget);
+ bool needShow = false;
+ Qt::WindowStates oldstate = windowState();
+ if (oldstate == newstate)
+ return;
+ if (isWindow()) {
+ // Ensure the initial size is valid, since we store it as normalGeometry below.
+ if (!testAttribute(Qt::WA_Resized) && !isVisible())
+ adjustSize();
+
+ QTLWExtra *top = d->topData();
+
+ if ((oldstate & Qt::WindowMaximized) != (newstate & Qt::WindowMaximized)) {
+ if (X11->isSupportedByWM(ATOM(_NET_WM_STATE_MAXIMIZED_HORZ))
+ && X11->isSupportedByWM(ATOM(_NET_WM_STATE_MAXIMIZED_VERT))) {
+ if ((newstate & Qt::WindowMaximized) && !(oldstate & Qt::WindowFullScreen))
+ top->normalGeometry = geometry();
+ qt_change_net_wm_state(this, (newstate & Qt::WindowMaximized),
+ ATOM(_NET_WM_STATE_MAXIMIZED_HORZ),
+ ATOM(_NET_WM_STATE_MAXIMIZED_VERT));
+ } else if (! (newstate & Qt::WindowFullScreen)) {
+ if (newstate & Qt::WindowMaximized) {
+ // save original geometry
+ const QRect normalGeometry = geometry();
+
+ if (isVisible()) {
+ data->fstrut_dirty = true;
+ const QRect maxRect = QApplication::desktop()->availableGeometry(this);
+ const QRect r = top->normalGeometry;
+ const QRect fs = d->frameStrut();
+ setGeometry(maxRect.x() + fs.left(),
+ maxRect.y() + fs.top(),
+ maxRect.width() - fs.left() - fs.right(),
+ maxRect.height() - fs.top() - fs.bottom());
+ top->normalGeometry = r;
+ }
+
+ if (top->normalGeometry.width() < 0)
+ top->normalGeometry = normalGeometry;
+ } else {
+ // restore original geometry
+ setGeometry(top->normalGeometry);
+ }
+ }
+ }
+
+ if ((oldstate & Qt::WindowFullScreen) != (newstate & Qt::WindowFullScreen)) {
+ if (X11->isSupportedByWM(ATOM(_NET_WM_STATE_FULLSCREEN))) {
+ if (newstate & Qt::WindowFullScreen) {
+ top->normalGeometry = geometry();
+ top->fullScreenOffset = d->frameStrut().topLeft();
+ }
+ qt_change_net_wm_state(this, (newstate & Qt::WindowFullScreen),
+ ATOM(_NET_WM_STATE_FULLSCREEN));
+ } else {
+ needShow = isVisible();
+
+ if (newstate & Qt::WindowFullScreen) {
+ data->fstrut_dirty = true;
+ const QRect normalGeometry = geometry();
+ const QPoint fullScreenOffset = d->frameStrut().topLeft();
+
+ top->savedFlags = windowFlags();
+ setParent(0, Qt::Window | Qt::FramelessWindowHint);
+ const QRect r = top->normalGeometry;
+ setGeometry(qApp->desktop()->screenGeometry(this));
+ top->normalGeometry = r;
+
+ if (top->normalGeometry.width() < 0) {
+ top->normalGeometry = normalGeometry;
+ top->fullScreenOffset = fullScreenOffset;
+ }
+ } else {
+ setParent(0, top->savedFlags);
+
+ if (newstate & Qt::WindowMaximized) {
+ // from fullscreen to maximized
+ data->fstrut_dirty = true;
+ const QRect maxRect = QApplication::desktop()->availableGeometry(this);
+ const QRect r = top->normalGeometry;
+ const QRect fs = d->frameStrut();
+ setGeometry(maxRect.x() + fs.left(),
+ maxRect.y() + fs.top(),
+ maxRect.width() - fs.left() - fs.right(),
+ maxRect.height() - fs.top() - fs.bottom());
+ top->normalGeometry = r;
+ } else {
+ // restore original geometry
+ setGeometry(top->normalGeometry.adjusted(-top->fullScreenOffset.x(),
+ -top->fullScreenOffset.y(),
+ -top->fullScreenOffset.x(),
+ -top->fullScreenOffset.y()));
+ }
+ }
+ }
+ }
+
+ createWinId();
+ Q_ASSERT(testAttribute(Qt::WA_WState_Created));
+ if ((oldstate & Qt::WindowMinimized) != (newstate & Qt::WindowMinimized)) {
+ if (isVisible()) {
+ if (newstate & Qt::WindowMinimized) {
+ XEvent e;
+ e.xclient.type = ClientMessage;
+ e.xclient.message_type = ATOM(WM_CHANGE_STATE);
+ e.xclient.display = X11->display;
+ e.xclient.window = data->winid;
+ e.xclient.format = 32;
+ e.xclient.data.l[0] = IconicState;
+ e.xclient.data.l[1] = 0;
+ e.xclient.data.l[2] = 0;
+ e.xclient.data.l[3] = 0;
+ e.xclient.data.l[4] = 0;
+ XSendEvent(X11->display,
+ RootWindow(X11->display,d->xinfo.screen()),
+ False, (SubstructureNotifyMask|SubstructureRedirectMask), &e);
+ } else {
+ setAttribute(Qt::WA_Mapped);
+ XMapWindow(X11->display, effectiveWinId());
+ }
+ }
+
+ needShow = false;
+ }
+ }
+
+ data->window_state = newstate;
+
+ if (needShow)
+ show();
+
+ if (newstate & Qt::WindowActive)
+ activateWindow();
+
+ QWindowStateChangeEvent e(oldstate);
+ QApplication::sendEvent(this, &e);
+}
+
+/*!
+ \internal
+ Platform-specific part of QWidget::show().
+*/
+
+void QWidgetPrivate::show_sys()
+{
+ Q_Q(QWidget);
+ Q_ASSERT(q->testAttribute(Qt::WA_WState_Created));
+
+ if (q->testAttribute(Qt::WA_DontShowOnScreen)) {
+ invalidateBuffer(q->rect());
+ q->setAttribute(Qt::WA_Mapped);
+ if (QTLWExtra *tlwExtra = maybeTopData())
+ tlwExtra->waitingForMapNotify = 0;
+ return;
+ }
+
+ if (q->isWindow()) {
+ XWMHints *h = XGetWMHints(X11->display, q->internalWinId());
+ XWMHints wm_hints;
+ bool got_hints = h != 0;
+ if (!got_hints) {
+ memset(&wm_hints, 0, sizeof(wm_hints)); // make valgrind happy
+ h = &wm_hints;
+ }
+ h->initial_state = q->isMinimized() ? IconicState : NormalState;
+ h->flags |= StateHint;
+ XSetWMHints(X11->display, q->internalWinId(), h);
+ if (got_hints)
+ XFree((char *)h);
+
+ // update WM_NORMAL_HINTS
+ do_size_hints(q, extra);
+
+ // udpate WM_TRANSIENT_FOR
+ if (isTransient(q)) {
+ QWidget *p = q->parentWidget();
+
+#ifndef QT_NO_MENU
+ // hackish ... try to find the main window related to this QMenu
+ if (qobject_cast<QMenu *>(q)) {
+ p = static_cast<QMenuPrivate*>(this)->causedPopup.widget;
+ if (!p)
+ p = q->parentWidget();
+ if (!p)
+ p = QApplication::widgetAt(q->pos());
+ if (!p)
+ p = qApp->activeWindow();
+ }
+#endif
+ if (p)
+ p = p->window();
+ if (p) {
+ // transient for window
+ XSetTransientForHint(X11->display, q->internalWinId(), p->internalWinId());
+ } else {
+ // transient for group
+ XSetTransientForHint(X11->display, q->internalWinId(), X11->wm_client_leader);
+ }
+ }
+
+ // update _MOTIF_WM_HINTS
+ QtMWMHints mwmhints = GetMWMHints(X11->display, q->internalWinId());
+
+ if (data.window_modality != Qt::NonModal) {
+ switch (data.window_modality) {
+ case Qt::WindowModal:
+ mwmhints.input_mode = MWM_INPUT_PRIMARY_APPLICATION_MODAL;
+ break;
+ case Qt::ApplicationModal:
+ default:
+ mwmhints.input_mode = MWM_INPUT_FULL_APPLICATION_MODAL;
+ break;
+ }
+ mwmhints.flags |= MWM_HINTS_INPUT_MODE;
+ } else {
+ mwmhints.input_mode = MWM_INPUT_MODELESS;
+ mwmhints.flags &= ~MWM_HINTS_INPUT_MODE;
+ }
+
+ if (q->minimumSize() == q->maximumSize()) {
+ // fixed size, remove the resize handle (since mwm/dtwm
+ // isn't smart enough to do it itself)
+ mwmhints.flags |= MWM_HINTS_FUNCTIONS;
+ if (mwmhints.functions == MWM_FUNC_ALL) {
+ mwmhints.functions = MWM_FUNC_MOVE;
+ } else {
+ mwmhints.functions &= ~MWM_FUNC_RESIZE;
+ }
+
+ mwmhints.flags |= MWM_HINTS_DECORATIONS;
+ if (mwmhints.decorations == MWM_DECOR_ALL) {
+ mwmhints.decorations = (MWM_DECOR_BORDER
+ | MWM_DECOR_TITLE
+ | MWM_DECOR_MENU);
+ } else {
+ mwmhints.decorations &= ~MWM_DECOR_RESIZEH;
+ }
+
+ if (q->windowFlags() & Qt::WindowMinimizeButtonHint) {
+ mwmhints.decorations |= MWM_DECOR_MINIMIZE;
+ mwmhints.functions |= MWM_FUNC_MINIMIZE;
+ }
+ if (q->windowFlags() & Qt::WindowMaximizeButtonHint) {
+ mwmhints.decorations |= MWM_DECOR_MAXIMIZE;
+ mwmhints.functions |= MWM_FUNC_MAXIMIZE;
+ }
+ if (q->windowFlags() & Qt::WindowCloseButtonHint)
+ mwmhints.functions |= MWM_FUNC_CLOSE;
+ }
+
+ SetMWMHints(X11->display, q->internalWinId(), mwmhints);
+
+ // update _NET_WM_STATE
+ QVector<Atom> netWmState = getNetWmState(q);
+
+ Qt::WindowFlags flags = q->windowFlags();
+ if (flags & Qt::WindowStaysOnTopHint) {
+ if (flags & Qt::WindowStaysOnBottomHint)
+ qWarning() << "QWidget: Incompatible window flags: the window can't be on top and on bottom at the same time";
+ netWmState.append(ATOM(_NET_WM_STATE_ABOVE));
+ netWmState.append(ATOM(_NET_WM_STATE_STAYS_ON_TOP));
+ } else if (flags & Qt::WindowStaysOnBottomHint) {
+ netWmState.append(ATOM(_NET_WM_STATE_BELOW));
+ }
+ if (q->isFullScreen()) {
+ netWmState.append(ATOM(_NET_WM_STATE_FULLSCREEN));
+ }
+ if (q->isMaximized()) {
+ netWmState.append(ATOM(_NET_WM_STATE_MAXIMIZED_HORZ));
+ netWmState.append(ATOM(_NET_WM_STATE_MAXIMIZED_VERT));
+ }
+ if (data.window_modality != Qt::NonModal) {
+ netWmState.append(ATOM(_NET_WM_STATE_MODAL));
+ }
+
+ if (!netWmState.isEmpty()) {
+ XChangeProperty(X11->display, q->internalWinId(),
+ ATOM(_NET_WM_STATE), XA_ATOM, 32, PropModeReplace,
+ (unsigned char *) netWmState.data(), netWmState.size());
+ } else {
+ XDeleteProperty(X11->display, q->internalWinId(), ATOM(_NET_WM_STATE));
+ }
+
+ // set _NET_WM_USER_TIME
+ Time userTime = X11->userTime;
+ bool setUserTime = false;
+ if (q->testAttribute(Qt::WA_ShowWithoutActivating)) {
+ userTime = 0;
+ setUserTime = true;
+ } else if (userTime != CurrentTime) {
+ setUserTime = true;
+ }
+ if (setUserTime)
+ qt_net_update_user_time(q, userTime);
+
+ if (!topData()->embedded
+ && (topData()->validWMState || topData()->waitingForMapNotify)
+ && !q->isMinimized()) {
+ X11->deferred_map.append(q);
+ return;
+ }
+
+ if (q->isMaximized() && !q->isFullScreen()
+ && !(X11->isSupportedByWM(ATOM(_NET_WM_STATE_MAXIMIZED_HORZ))
+ && X11->isSupportedByWM(ATOM(_NET_WM_STATE_MAXIMIZED_VERT)))) {
+ XMapWindow(X11->display, q->internalWinId());
+ data.fstrut_dirty = true;
+ qt_x11_wait_for_window_manager(q);
+
+ // if the wm was not smart enough to adjust our size, do that manually
+ QRect maxRect = QApplication::desktop()->availableGeometry(q);
+
+ QTLWExtra *top = topData();
+ QRect normalRect = top->normalGeometry;
+ const QRect fs = frameStrut();
+
+ q->setGeometry(maxRect.x() + fs.left(),
+ maxRect.y() + fs.top(),
+ maxRect.width() - fs.left() - fs.right(),
+ maxRect.height() - fs.top() - fs.bottom());
+
+ // restore the original normalGeometry
+ top->normalGeometry = normalRect;
+ // internalSetGeometry() clears the maximized flag... make sure we set it back
+ data.window_state = data.window_state | Qt::WindowMaximized;
+ q->setAttribute(Qt::WA_Mapped);
+ return;
+ }
+
+ if (q->isFullScreen() && !X11->isSupportedByWM(ATOM(_NET_WM_STATE_FULLSCREEN))) {
+ XMapWindow(X11->display, q->internalWinId());
+ qt_x11_wait_for_window_manager(q);
+ q->setAttribute(Qt::WA_Mapped);
+ return;
+ }
+ }
+
+ invalidateBuffer(q->rect());
+
+ if (q->testAttribute(Qt::WA_OutsideWSRange))
+ return;
+ q->setAttribute(Qt::WA_Mapped);
+ if (q->isWindow())
+ topData()->waitingForMapNotify = 1;
+
+ if (!q->isWindow()
+ && (!q->autoFillBackground()
+ || q->palette().brush(q->backgroundRole()).style() == Qt::LinearGradientPattern)) {
+ if (q->internalWinId()) {
+ XSetWindowBackgroundPixmap(X11->display, q->internalWinId(), XNone);
+ XMapWindow(X11->display, q->internalWinId());
+ updateSystemBackground();
+ }
+ return;
+ }
+
+ if (q->internalWinId())
+ XMapWindow(X11->display, q->internalWinId());
+
+ // Freedesktop.org Startup Notification
+ if (X11->startupId && q->isWindow()) {
+ QByteArray message("remove: ID=");
+ message.append(X11->startupId);
+ sendStartupMessage(message.constData());
+ X11->startupId = 0;
+ }
+}
+
+/*!
+ \internal
+ Platform-specific part of QWidget::show().
+*/
+
+void QWidgetPrivate::sendStartupMessage(const char *message) const
+{
+ Q_Q(const QWidget);
+
+ if (!message)
+ return;
+
+ XEvent xevent;
+ xevent.xclient.type = ClientMessage;
+ xevent.xclient.message_type = ATOM(_NET_STARTUP_INFO_BEGIN);
+ xevent.xclient.display = X11->display;
+ xevent.xclient.window = q->internalWinId();
+ xevent.xclient.format = 8;
+
+ Window rootWindow = RootWindow(X11->display, DefaultScreen(X11->display));
+ uint sent = 0;
+ uint length = strlen(message) + 1;
+ do {
+ if (sent == 20)
+ xevent.xclient.message_type = ATOM(_NET_STARTUP_INFO);
+
+ for (uint i = 0; i < 20 && i + sent <= length; i++)
+ xevent.xclient.data.b[i] = message[i + sent++];
+
+ XSendEvent(X11->display, rootWindow, false, PropertyChangeMask, &xevent);
+ } while (sent <= length);
+}
+
+void QWidgetPrivate::setNetWmWindowTypes()
+{
+ Q_Q(QWidget);
+ Q_ASSERT(q->testAttribute(Qt::WA_WState_Created));
+
+ if (!q->isWindow()) {
+ if (q->internalWinId())
+ XDeleteProperty(X11->display, q->internalWinId(), ATOM(_NET_WM_WINDOW_TYPE));
+ return;
+ }
+
+ QVector<long> windowTypes;
+
+ // manual selection 1 (these are never set by Qt and take precedence)
+ if (q->testAttribute(Qt::WA_X11NetWmWindowTypeDesktop))
+ windowTypes.append(ATOM(_NET_WM_WINDOW_TYPE_DESKTOP));
+ if (q->testAttribute(Qt::WA_X11NetWmWindowTypeDock))
+ windowTypes.append(ATOM(_NET_WM_WINDOW_TYPE_DOCK));
+ if (q->testAttribute(Qt::WA_X11NetWmWindowTypeNotification))
+ windowTypes.append(ATOM(_NET_WM_WINDOW_TYPE_NOTIFICATION));
+
+ // manual selection 2 (Qt uses these during auto selection);
+ if (q->testAttribute(Qt::WA_X11NetWmWindowTypeUtility))
+ windowTypes.append(ATOM(_NET_WM_WINDOW_TYPE_UTILITY));
+ if (q->testAttribute(Qt::WA_X11NetWmWindowTypeSplash))
+ windowTypes.append(ATOM(_NET_WM_WINDOW_TYPE_SPLASH));
+ if (q->testAttribute(Qt::WA_X11NetWmWindowTypeDialog))
+ windowTypes.append(ATOM(_NET_WM_WINDOW_TYPE_DIALOG));
+ if (q->testAttribute(Qt::WA_X11NetWmWindowTypeToolTip))
+ windowTypes.append(ATOM(_NET_WM_WINDOW_TYPE_TOOLTIP));
+
+ // manual selection 3 (these can be set by Qt, but don't have a
+ // corresponding Qt::WindowType). note that order of the *MENU
+ // atoms is important
+ if (q->testAttribute(Qt::WA_X11NetWmWindowTypeMenu))
+ windowTypes.append(ATOM(_NET_WM_WINDOW_TYPE_MENU));
+ if (q->testAttribute(Qt::WA_X11NetWmWindowTypeDropDownMenu))
+ windowTypes.append(ATOM(_NET_WM_WINDOW_TYPE_DROPDOWN_MENU));
+ if (q->testAttribute(Qt::WA_X11NetWmWindowTypePopupMenu))
+ windowTypes.append(ATOM(_NET_WM_WINDOW_TYPE_POPUP_MENU));
+ if (q->testAttribute(Qt::WA_X11NetWmWindowTypeToolBar))
+ windowTypes.append(ATOM(_NET_WM_WINDOW_TYPE_TOOLBAR));
+ if (q->testAttribute(Qt::WA_X11NetWmWindowTypeCombo))
+ windowTypes.append(ATOM(_NET_WM_WINDOW_TYPE_COMBO));
+ if (q->testAttribute(Qt::WA_X11NetWmWindowTypeDND))
+ windowTypes.append(ATOM(_NET_WM_WINDOW_TYPE_DND));
+
+ // automatic selection
+ switch (q->windowType()) {
+ case Qt::Dialog:
+ case Qt::Sheet:
+ // dialog netwm type
+ windowTypes.append(ATOM(_NET_WM_WINDOW_TYPE_DIALOG));
+ break;
+
+ case Qt::Tool:
+ case Qt::Drawer:
+ // utility netwm type
+ windowTypes.append(ATOM(_NET_WM_WINDOW_TYPE_UTILITY));
+ break;
+
+ case Qt::ToolTip:
+ // tooltip netwm type
+ windowTypes.append(ATOM(_NET_WM_WINDOW_TYPE_TOOLTIP));
+ break;
+
+ case Qt::SplashScreen:
+ // splash netwm type
+ windowTypes.append(ATOM(_NET_WM_WINDOW_TYPE_SPLASH));
+ break;
+
+ default:
+ break;
+ }
+
+ if (q->windowFlags() & Qt::FramelessWindowHint) {
+ // override netwm type - quick and easy for KDE noborder
+ windowTypes.append(ATOM(_KDE_NET_WM_WINDOW_TYPE_OVERRIDE));
+ }
+
+ // normal netwm type - default
+ windowTypes.append(ATOM(_NET_WM_WINDOW_TYPE_NORMAL));
+
+ if (!windowTypes.isEmpty()) {
+ XChangeProperty(X11->display, q->winId(), ATOM(_NET_WM_WINDOW_TYPE), XA_ATOM, 32,
+ PropModeReplace, (unsigned char *) windowTypes.constData(),
+ windowTypes.count());
+ } else {
+ XDeleteProperty(X11->display, q->winId(), ATOM(_NET_WM_WINDOW_TYPE));
+ }
+}
+
+/*!
+ \internal
+ Platform-specific part of QWidget::hide().
+*/
+
+void QWidgetPrivate::hide_sys()
+{
+ Q_Q(QWidget);
+ Q_ASSERT(q->testAttribute(Qt::WA_WState_Created));
+ deactivateWidgetCleanup();
+ if (q->isWindow()) {
+ X11->deferred_map.removeAll(q);
+ if (q->internalWinId()) // in nsplugin, may be 0
+ XWithdrawWindow(X11->display, q->internalWinId(), xinfo.screen());
+ XFlush(X11->display);
+ } else {
+ invalidateBuffer(q->rect());
+ if (q->internalWinId()) // in nsplugin, may be 0
+ XUnmapWindow(X11->display, q->internalWinId());
+ }
+ q->setAttribute(Qt::WA_Mapped, false);
+}
+
+void QWidgetPrivate::setFocus_sys()
+{
+
+}
+
+
+void QWidgetPrivate::raise_sys()
+{
+ Q_Q(QWidget);
+ Q_ASSERT(q->testAttribute(Qt::WA_WState_Created));
+ if (q->internalWinId())
+ XRaiseWindow(X11->display, q->internalWinId());
+}
+
+void QWidgetPrivate::lower_sys()
+{
+ Q_Q(QWidget);
+ Q_ASSERT(q->testAttribute(Qt::WA_WState_Created));
+ if (q->internalWinId())
+ XLowerWindow(X11->display, q->internalWinId());
+ if(!q->isWindow())
+ invalidateBuffer(q->rect());
+}
+
+void QWidgetPrivate::stackUnder_sys(QWidget* w)
+{
+ Q_Q(QWidget);
+ Q_ASSERT(q->testAttribute(Qt::WA_WState_Created));
+ if (q->internalWinId() && w->internalWinId()) {
+ Window stack[2];
+ stack[0] = w->internalWinId();;
+ stack[1] = q->internalWinId();
+ XRestackWindows(X11->display, stack, 2);
+ }
+ if(!q->isWindow() || !w->internalWinId())
+ invalidateBuffer(q->rect());
+}
+
+
+static void do_size_hints(QWidget* widget, QWExtra *x)
+{
+ Q_ASSERT(widget->testAttribute(Qt::WA_WState_Created));
+ XSizeHints s;
+ s.flags = 0;
+ if (x) {
+ QRect g = widget->geometry();
+ s.x = g.x();
+ s.y = g.y();
+ s.width = g.width();
+ s.height = g.height();
+ if (x->minw > 0 || x->minh > 0) {
+ // add minimum size hints
+ s.flags |= PMinSize;
+ s.min_width = qMin(XCOORD_MAX, x->minw);
+ s.min_height = qMin(XCOORD_MAX, x->minh);
+ }
+ if (x->maxw < QWIDGETSIZE_MAX || x->maxh < QWIDGETSIZE_MAX) {
+ // add maximum size hints
+ s.flags |= PMaxSize;
+ s.max_width = qMin(XCOORD_MAX, x->maxw);
+ s.max_height = qMin(XCOORD_MAX, x->maxh);
+ }
+ if (x->topextra &&
+ (x->topextra->incw > 0 || x->topextra->inch > 0)) {
+ // add resize increment hints
+ s.flags |= PResizeInc | PBaseSize;
+ s.width_inc = x->topextra->incw;
+ s.height_inc = x->topextra->inch;
+ s.base_width = x->topextra->basew;
+ s.base_height = x->topextra->baseh;
+ }
+ }
+ if (widget->testAttribute(Qt::WA_Moved)) {
+ // user (i.e. command-line) specified position
+ s.flags |= USPosition;
+ s.flags |= PPosition;
+ }
+ if (widget->testAttribute(Qt::WA_Resized)) {
+ // user (i.e. command-line) specified size
+ s.flags |= USSize;
+ s.flags |= PSize;
+ }
+ s.flags |= PWinGravity;
+ if (widget->testAttribute(Qt::WA_Moved) && x && x->topextra && !x->topextra->posFromMove) {
+ // position came from setGeometry(), tell the WM that we don't
+ // want our window gravity-shifted
+ s.win_gravity = StaticGravity;
+ } else {
+ // position came from move()
+ s.x = widget->x();
+ s.y = widget->y();
+ s.win_gravity = QApplication::isRightToLeft() ? NorthEastGravity : NorthWestGravity;
+ }
+ if (widget->internalWinId())
+ XSetWMNormalHints(X11->display, widget->internalWinId(), &s);
+}
+
+
+/*
+ Helper function for non-toplevel widgets. Helps to map Qt's 32bit
+ coordinate system to X11's 16bit coordinate system.
+
+ Sets the geometry of the widget to data.crect, but clipped to sizes
+ that X can handle. Unmaps widgets that are completely outside the
+ valid range.
+
+ Maintains data.wrect, which is the geometry of the X widget,
+ measured in this widget's coordinate system.
+
+ if the parent is not clipped, parentWRect is empty, otherwise
+ parentWRect is the geometry of the parent's X rect, measured in
+ parent's coord sys
+ */
+void QWidgetPrivate::setWSGeometry(bool dontShow)
+{
+ Q_Q(QWidget);
+ Q_ASSERT(q->testAttribute(Qt::WA_WState_Created));
+
+ /*
+ There are up to four different coordinate systems here:
+ Qt coordinate system for this widget.
+ X coordinate system for this widget (relative to wrect).
+ Qt coordinate system for parent
+ X coordinate system for parent (relative to parent's wrect).
+ */
+ Display *dpy = xinfo.display();
+ QRect validRange(-XCOORD_MAX,-XCOORD_MAX, 2*XCOORD_MAX, 2*XCOORD_MAX);
+ QRect wrectRange(-WRECT_MAX,-WRECT_MAX, 2*WRECT_MAX, 2*WRECT_MAX);
+ QRect wrect;
+ //xrect is the X geometry of my X widget. (starts out in parent's Qt coord sys, and ends up in parent's X coord sys)
+ QRect xrect = data.crect;
+
+ const QWidget *const parent = q->parentWidget();
+ QRect parentWRect = parent->data->wrect;
+
+ if (parentWRect.isValid()) {
+ // parent is clipped, and we have to clip to the same limit as parent
+ if (!parentWRect.contains(xrect)) {
+ xrect &= parentWRect;
+ wrect = xrect;
+ //translate from parent's to my Qt coord sys
+ wrect.translate(-data.crect.topLeft());
+ }
+ //translate from parent's Qt coords to parent's X coords
+ xrect.translate(-parentWRect.topLeft());
+
+ } else {
+ // parent is not clipped, we may or may not have to clip
+
+ if (data.wrect.isValid() && QRect(QPoint(),data.crect.size()).contains(data.wrect)) {
+ // This is where the main optimization is: we are already
+ // clipped, and if our clip is still valid, we can just
+ // move our window, and do not need to move or clip
+ // children
+
+ QRect vrect = xrect & parent->rect();
+ vrect.translate(-data.crect.topLeft()); //the part of me that's visible through parent, in my Qt coords
+ if (data.wrect.contains(vrect)) {
+ xrect = data.wrect;
+ xrect.translate(data.crect.topLeft());
+ if (data.winid)
+ XMoveWindow(dpy, data.winid, xrect.x(), xrect.y());
+ return;
+ }
+ }
+
+ if (!validRange.contains(xrect)) {
+ // we are too big, and must clip
+ xrect &=wrectRange;
+ wrect = xrect;
+ wrect.translate(-data.crect.topLeft());
+ //parent's X coord system is equal to parent's Qt coord
+ //sys, so we don't need to map xrect.
+ }
+
+ }
+
+ // unmap if we are outside the valid window system coord system
+ bool outsideRange = !xrect.isValid();
+ bool mapWindow = false;
+ if (q->testAttribute(Qt::WA_OutsideWSRange) != outsideRange) {
+ q->setAttribute(Qt::WA_OutsideWSRange, outsideRange);
+ if (outsideRange) {
+ if (data.winid)
+ XUnmapWindow(dpy, data.winid);
+ q->setAttribute(Qt::WA_Mapped, false);
+ } else if (!q->isHidden()) {
+ mapWindow = true;
+ }
+ }
+
+ if (outsideRange)
+ return;
+
+ bool jump = (data.wrect != wrect);
+ data.wrect = wrect;
+
+
+ // and now recursively for all children...
+ // ### can be optimized
+ for (int i = 0; i < children.size(); ++i) {
+ QObject *object = children.at(i);
+ if (object->isWidgetType()) {
+ QWidget *w = static_cast<QWidget *>(object);
+ if (!w->isWindow() && w->testAttribute(Qt::WA_WState_Created))
+ w->d_func()->setWSGeometry(jump);
+ }
+ }
+
+ if (data.winid) {
+ // move ourselves to the new position and map (if necessary) after
+ // the movement. Rationale: moving unmapped windows is much faster
+ // than moving mapped windows
+ if (jump) //avoid flicker when jumping
+ XSetWindowBackgroundPixmap(dpy, data.winid, XNone);
+ if (!parent->internalWinId())
+ xrect.translate(parent->mapTo(q->nativeParentWidget(), QPoint(0, 0)));
+ XMoveResizeWindow(dpy, data.winid, xrect.x(), xrect.y(), xrect.width(), xrect.height());
+ }
+
+ //to avoid flicker, we have to show children after the helper widget has moved
+ if (jump) {
+ for (int i = 0; i < children.size(); ++i) {
+ QObject *object = children.at(i);
+ if (object->isWidgetType()) {
+ QWidget *w = static_cast<QWidget *>(object);
+ if (!w->testAttribute(Qt::WA_OutsideWSRange) && !w->testAttribute(Qt::WA_Mapped) && !w->isHidden()) {
+ w->setAttribute(Qt::WA_Mapped);
+ if (w->internalWinId())
+ XMapWindow(dpy, w->data->winid);
+ }
+ }
+ }
+ }
+
+
+ if (jump && data.winid)
+ XClearArea(dpy, data.winid, 0, 0, wrect.width(), wrect.height(), True);
+
+ if (mapWindow && !dontShow) {
+ q->setAttribute(Qt::WA_Mapped);
+ if (data.winid)
+ XMapWindow(dpy, data.winid);
+ }
+}
+
+void QWidgetPrivate::setGeometry_sys(int x, int y, int w, int h, bool isMove)
+{
+ Q_Q(QWidget);
+ Q_ASSERT(q->testAttribute(Qt::WA_WState_Created));
+ Display *dpy = X11->display;
+
+ if ((q->windowType() == Qt::Desktop))
+ return;
+ if (q->isWindow()) {
+ if (!X11->isSupportedByWM(ATOM(_NET_WM_STATE_MAXIMIZED_VERT))
+ && !X11->isSupportedByWM(ATOM(_NET_WM_STATE_MAXIMIZED_HORZ)))
+ data.window_state &= ~Qt::WindowMaximized;
+ if (!X11->isSupportedByWM(ATOM(_NET_WM_STATE_FULLSCREEN)))
+ data.window_state &= ~Qt::WindowFullScreen;
+ if (QTLWExtra *topData = maybeTopData())
+ topData->normalGeometry = QRect(0,0,-1,-1);
+ } else {
+ uint s = data.window_state;
+ s &= ~(Qt::WindowMaximized | Qt::WindowFullScreen);
+ data.window_state = s;
+ }
+ if (extra) { // any size restrictions?
+ w = qMin(w,extra->maxw);
+ h = qMin(h,extra->maxh);
+ w = qMax(w,extra->minw);
+ h = qMax(h,extra->minh);
+ }
+ QPoint oldPos(q->pos());
+ QSize oldSize(q->size());
+ QRect oldGeom(data.crect);
+ QRect r(x, y, w, h);
+
+ // We only care about stuff that changes the geometry, or may
+ // cause the window manager to change its state
+ if (!q->isWindow() && oldGeom == r)
+ return;
+
+ data.crect = r;
+ bool isResize = q->size() != oldSize;
+
+ if (q->isWindow()) {
+ if (w == 0 || h == 0) {
+ q->setAttribute(Qt::WA_OutsideWSRange, true);
+ if (q->isVisible() && q->testAttribute(Qt::WA_Mapped))
+ hide_sys();
+ } else if (q->isVisible() && q->testAttribute(Qt::WA_OutsideWSRange)) {
+ q->setAttribute(Qt::WA_OutsideWSRange, false);
+
+ // put the window in its place and show it
+ if (data.winid)
+ XMoveResizeWindow(dpy, data.winid, x, y, w, h);
+ topData()->posFromMove = false; // force StaticGravity
+ do_size_hints(q, extra);
+ show_sys();
+ } else {
+ q->setAttribute(Qt::WA_OutsideWSRange, false);
+ if (!q->isVisible())
+ do_size_hints(q, extra);
+ if (isMove) {
+ if ((data.window_flags & Qt::X11BypassWindowManagerHint) == Qt::X11BypassWindowManagerHint
+ // work around 4Dwm's incompliance with ICCCM 4.1.5
+ || X11->desktopEnvironment == DE_4DWM) {
+ if (data.winid)
+ XMoveResizeWindow(dpy, data.winid, x, y, w, h);
+ } else if (q->isVisible()
+ && topData()->validWMState
+ && X11->isSupportedByWM(ATOM(_NET_MOVERESIZE_WINDOW))) {
+ XEvent e;
+ e.xclient.type = ClientMessage;
+ e.xclient.message_type = ATOM(_NET_MOVERESIZE_WINDOW);
+ e.xclient.display = X11->display;
+ e.xclient.window = q->internalWinId();
+ e.xclient.format = 32;
+ e.xclient.data.l[0] = StaticGravity | 1<<8 | 1<<9 | 1<<10 | 1<<11 | 1<<12;
+ e.xclient.data.l[1] = x;
+ e.xclient.data.l[2] = y;
+ e.xclient.data.l[3] = w;
+ e.xclient.data.l[4] = h;
+ XSendEvent(X11->display, RootWindow(X11->display, q->x11Info().screen()),
+ false, (SubstructureNotifyMask | SubstructureRedirectMask), &e);
+ } else if (data.winid) {
+ // pos() is right according to ICCCM 4.1.5
+ XMoveResizeWindow(dpy, data.winid, q->pos().x(), q->pos().y(), w, h);
+ }
+ } else if (isResize && data.winid) {
+ if (!q->isVisible()
+ && topData()->validWMState
+ && !q->testAttribute(Qt::WA_PendingMoveEvent)) {
+ /*
+ even though we've not visible, we could be in a
+ race w/ the window manager, and it may ignore
+ our ConfigureRequest. setting posFromMove to
+ false makes sure that doDeferredMap() in
+ qapplication_x11.cpp keeps the window in the
+ right place
+ */
+ topData()->posFromMove = false;
+ }
+ XResizeWindow(dpy, data.winid, w, h);
+ }
+ }
+ if (isResize && !q->testAttribute(Qt::WA_DontShowOnScreen)) // set config pending only on resize, see qapplication_x11.cpp, translateConfigEvent()
+ q->setAttribute(Qt::WA_WState_ConfigPending);
+
+ } else {
+ QTLWExtra *tlwExtra = q->window()->d_func()->maybeTopData();
+ const bool inTopLevelResize = tlwExtra ? tlwExtra->inTopLevelResize : false;
+ const bool disableInTopLevelResize = inTopLevelResize && q->internalWinId();
+ if (disableInTopLevelResize) {
+ // Top-level resize optimization does not work for native child widgets;
+ // disable it for this particular widget.
+ tlwExtra->inTopLevelResize = false;
+ }
+
+ if (!isResize && (!inTopLevelResize || disableInTopLevelResize) && q->isVisible()) {
+ moveRect(QRect(oldPos, oldSize), x - oldPos.x(), y - oldPos.y());
+ }
+ if (q->testAttribute(Qt::WA_WState_Created))
+ setWSGeometry();
+
+ if (isResize && (!inTopLevelResize || disableInTopLevelResize) && q->isVisible())
+ invalidateBuffer_resizeHelper(oldPos, oldSize);
+
+ if (disableInTopLevelResize)
+ tlwExtra->inTopLevelResize = true;
+ }
+
+ if (q->isVisible()) {
+ if (isMove && q->pos() != oldPos) {
+ if (X11->desktopEnvironment != DE_4DWM) {
+ // pos() is right according to ICCCM 4.1.5
+ QMoveEvent e(q->pos(), oldPos);
+ QApplication::sendEvent(q, &e);
+ } else {
+ // work around 4Dwm's incompliance with ICCCM 4.1.5
+ QMoveEvent e(data.crect.topLeft(), oldGeom.topLeft());
+ QApplication::sendEvent(q, &e);
+ }
+ }
+ if (isResize) {
+ static bool slowResize = qgetenv("QT_SLOW_TOPLEVEL_RESIZE").toInt();
+ // If we have a backing store with static contents, we have to disable the top-level
+ // resize optimization in order to get invalidated regions for resized widgets.
+ // The optimization discards all invalidateBuffer() calls since we're going to
+ // repaint everything anyways, but that's not the case with static contents.
+ const bool setTopLevelResize = !slowResize && q->isWindow() && extra && extra->topextra
+ && !extra->topextra->inTopLevelResize
+ && (!extra->topextra->backingStore
+ || !extra->topextra->backingStore->hasStaticContents());
+ if (setTopLevelResize)
+ extra->topextra->inTopLevelResize = true;
+ QResizeEvent e(q->size(), oldSize);
+ QApplication::sendEvent(q, &e);
+ if (setTopLevelResize)
+ extra->topextra->inTopLevelResize = false;
+ }
+ } else {
+ if (isMove && q->pos() != oldPos)
+ q->setAttribute(Qt::WA_PendingMoveEvent, true);
+ if (isResize)
+ q->setAttribute(Qt::WA_PendingResizeEvent, true);
+ }
+}
+
+void QWidgetPrivate::setConstraints_sys()
+{
+ Q_Q(QWidget);
+#ifdef ALIEN_DEBUG
+ qDebug() << "QWidgetPrivate::setConstraints_sys START" << q;
+#endif
+ if (q->testAttribute(Qt::WA_WState_Created))
+ do_size_hints(q, extra);
+#ifdef ALIEN_DEBUG
+ qDebug() << "QWidgetPrivate::setConstraints_sys END" << q;
+#endif
+}
+
+void QWidgetPrivate::scroll_sys(int dx, int dy)
+{
+ Q_Q(QWidget);
+
+ scrollChildren(dx, dy);
+ if (!paintOnScreen()) {
+ scrollRect(q->rect(), dx, dy);
+ } else {
+ scroll_sys(dx, dy, QRect());
+ }
+}
+
+void QWidgetPrivate::scroll_sys(int dx, int dy, const QRect &r)
+{
+ Q_Q(QWidget);
+
+ if (!paintOnScreen()) {
+ scrollRect(r, dx, dy);
+ return;
+ }
+ bool valid_rect = r.isValid();
+ bool just_update = qAbs(dx) > q->width() || qAbs(dy) > q->height();
+ QRect sr = valid_rect ? r : clipRect();
+ if (just_update)
+ q->update();
+
+ int x1, y1, x2, y2, w = sr.width(), h = sr.height();
+ if (dx > 0) {
+ x1 = sr.x();
+ x2 = x1+dx;
+ w -= dx;
+ } else {
+ x2 = sr.x();
+ x1 = x2-dx;
+ w += dx;
+ }
+ if (dy > 0) {
+ y1 = sr.y();
+ y2 = y1+dy;
+ h -= dy;
+ } else {
+ y2 = sr.y();
+ y1 = y2-dy;
+ h += dy;
+ }
+
+ if (dx == 0 && dy == 0)
+ return;
+
+ Display *dpy = X11->display;
+ // Want expose events
+ if (w > 0 && h > 0 && !just_update && q->internalWinId()) {
+ GC gc = XCreateGC(dpy, q->internalWinId(), 0, 0);
+ XSetGraphicsExposures(dpy, gc, True);
+ XCopyArea(dpy, q->internalWinId(), q->internalWinId(), gc, x1, y1, w, h, x2, y2);
+ XFreeGC(dpy, gc);
+ }
+
+ if (!valid_rect && !children.isEmpty()) { // scroll children
+ QPoint pd(dx, dy);
+ for (int i = 0; i < children.size(); ++i) { // move all children
+ register QObject *object = children.at(i);
+ if (object->isWidgetType()) {
+ QWidget *w = static_cast<QWidget *>(object);
+ if (!w->isWindow())
+ w->move(w->pos() + pd);
+ }
+ }
+ }
+
+ if (just_update)
+ return;
+
+ // Don't let the server be bogged-down with repaint events
+ bool repaint_immediately = (qt_sip_count(q) < 3 && !q->testAttribute(Qt::WA_WState_InPaintEvent));
+
+ if (dx) {
+ int x = x2 == sr.x() ? sr.x()+w : sr.x();
+ if (repaint_immediately)
+ q->repaint(x, sr.y(), qAbs(dx), sr.height());
+ else if (q->internalWinId())
+ XClearArea(dpy, data.winid, x, sr.y(), qAbs(dx), sr.height(), True);
+ }
+ if (dy) {
+ int y = y2 == sr.y() ? sr.y()+h : sr.y();
+ if (repaint_immediately)
+ q->repaint(sr.x(), y, sr.width(), qAbs(dy));
+ else if (q->internalWinId())
+ XClearArea(dpy, data.winid, sr.x(), y, sr.width(), qAbs(dy), True);
+ }
+
+ qt_insert_sip(q, dx, dy); // #### ignores r
+}
+
+int QWidget::metric(PaintDeviceMetric m) const
+{
+ Q_D(const QWidget);
+ int val;
+ if (m == PdmWidth) {
+ val = data->crect.width();
+ } else if (m == PdmHeight) {
+ val = data->crect.height();
+ } else {
+ Display *dpy = X11->display;
+ int scr = d->xinfo.screen();
+ switch (m) {
+ case PdmDpiX:
+ case PdmPhysicalDpiX:
+ if (d->extra && d->extra->customDpiX)
+ val = d->extra->customDpiX;
+ else if (d->parent)
+ val = static_cast<QWidget *>(d->parent)->metric(m);
+ else
+ val = QX11Info::appDpiX(scr);
+ break;
+ case PdmDpiY:
+ case PdmPhysicalDpiY:
+ if (d->extra && d->extra->customDpiY)
+ val = d->extra->customDpiY;
+ else if (d->parent)
+ val = static_cast<QWidget *>(d->parent)->metric(m);
+ else
+ val = QX11Info::appDpiY(scr);
+ break;
+ case PdmWidthMM:
+ val = (DisplayWidthMM(dpy,scr)*data->crect.width())/
+ DisplayWidth(dpy,scr);
+ break;
+ case PdmHeightMM:
+ val = (DisplayHeightMM(dpy,scr)*data->crect.height())/
+ DisplayHeight(dpy,scr);
+ break;
+ case PdmNumColors:
+ val = d->xinfo.cells();
+ break;
+ case PdmDepth:
+ val = d->xinfo.depth();
+ break;
+ default:
+ val = 0;
+ qWarning("QWidget::metric: Invalid metric command");
+ }
+ }
+ return val;
+}
+
+void QWidgetPrivate::createSysExtra()
+{
+ extra->xDndProxy = 0;
+ extra->compress_events = true;
+}
+
+void QWidgetPrivate::deleteSysExtra()
+{
+}
+
+void QWidgetPrivate::createTLSysExtra()
+{
+ extra->topextra->validWMState = 0;
+ extra->topextra->waitingForMapNotify = 0;
+ extra->topextra->userTimeWindow = 0;
+}
+
+void QWidgetPrivate::deleteTLSysExtra()
+{
+ // don't destroy input context here. it will be destroyed in
+ // QWidget::destroy() destroyInputContext();
+}
+
+void QWidgetPrivate::registerDropSite(bool on)
+{
+ Q_UNUSED(on);
+}
+
+void QWidgetPrivate::setMask_sys(const QRegion &region)
+{
+ Q_Q(QWidget);
+ if (!q->internalWinId())
+ return;
+
+ if (region.isEmpty()) {
+ XShapeCombineMask(X11->display, q->internalWinId(), ShapeBounding, 0, 0,
+ XNone, ShapeSet);
+ } else {
+ XShapeCombineRegion(X11->display, q->internalWinId(), ShapeBounding, 0, 0,
+ region.handle(), ShapeSet);
+ }
+}
+
+/*!
+ \internal
+
+ Computes the frame rectangle when needed. This is an internal function, you
+ should never call this.
+*/
+
+void QWidgetPrivate::updateFrameStrut()
+{
+ Q_Q(QWidget);
+
+ QTLWExtra *top = topData();
+ if (!top->validWMState) {
+ return;
+ }
+ if (!q->isWindow() && !q->internalWinId()) {
+ data.fstrut_dirty = false;
+ return;
+ }
+
+ Atom type_ret;
+ Window l = q->effectiveWinId(), w = l, p, r; // target window, its parent, root
+ Window *c;
+ int i_unused;
+ unsigned int nc;
+ unsigned char *data_ret;
+ unsigned long l_unused;
+
+ while (XQueryTree(X11->display, w, &r, &p, &c, &nc)) {
+ if (c && nc > 0)
+ XFree(c);
+
+ if (! p) {
+ qWarning("QWidget::updateFrameStrut: No parent");
+ return;
+ }
+
+ // if the parent window is the root window, an Enlightenment virtual root or
+ // a NET WM virtual root window, stop here
+ data_ret = 0;
+ if (p == r ||
+ (XGetWindowProperty(X11->display, p,
+ ATOM(ENLIGHTENMENT_DESKTOP), 0, 1, False, XA_CARDINAL,
+ &type_ret, &i_unused, &l_unused, &l_unused,
+ &data_ret) == Success &&
+ type_ret == XA_CARDINAL)) {
+ if (data_ret)
+ XFree(data_ret);
+
+ break;
+ } else if (X11->isSupportedByWM(ATOM(_NET_VIRTUAL_ROOTS)) && X11->net_virtual_root_list) {
+ int i = 0;
+ while (X11->net_virtual_root_list[i] != 0) {
+ if (X11->net_virtual_root_list[i++] == p)
+ break;
+ }
+ }
+
+ l = w;
+ w = p;
+ }
+
+ // we have our window
+ int transx, transy;
+ XWindowAttributes wattr;
+ if (XTranslateCoordinates(X11->display, l, w,
+ 0, 0, &transx, &transy, &p) &&
+ XGetWindowAttributes(X11->display, w, &wattr)) {
+ top->frameStrut.setCoords(transx,
+ transy,
+ wattr.width - data.crect.width() - transx,
+ wattr.height - data.crect.height() - transy);
+
+ // add the border_width for the window managers frame... some window managers
+ // do not use a border_width of zero for their frames, and if we the left and
+ // top strut, we ensure that pos() is absolutely correct. frameGeometry()
+ // will still be incorrect though... perhaps i should have foffset as well, to
+ // indicate the frame offset (equal to the border_width on X).
+ // - Brad
+ top->frameStrut.adjust(wattr.border_width,
+ wattr.border_width,
+ wattr.border_width,
+ wattr.border_width);
+ }
+
+ data.fstrut_dirty = false;
+}
+
+void QWidgetPrivate::setWindowOpacity_sys(qreal opacity)
+{
+ Q_Q(QWidget);
+ ulong value = ulong(opacity * 0xffffffff);
+ XChangeProperty(QX11Info::display(), q->internalWinId(), ATOM(_NET_WM_WINDOW_OPACITY), XA_CARDINAL,
+ 32, PropModeReplace, (uchar*)&value, 1);
+}
+
+/*!
+ Returns information about the configuration of the X display used to display
+ the widget.
+
+ \warning This function is only available on X11.
+*/
+const QX11Info &QWidget::x11Info() const
+{
+ Q_D(const QWidget);
+ return d->xinfo;
+}
+
+void QWidgetPrivate::setWindowRole()
+{
+ Q_Q(QWidget);
+ if (!q->internalWinId())
+ return;
+ QByteArray windowRole = topData()->role.toUtf8();
+ XChangeProperty(X11->display, q->internalWinId(),
+ ATOM(WM_WINDOW_ROLE), XA_STRING, 8, PropModeReplace,
+ (unsigned char *)windowRole.constData(), windowRole.length());
+}
+
+Q_GLOBAL_STATIC(QX11PaintEngine, qt_widget_paintengine)
+QPaintEngine *QWidget::paintEngine() const
+{
+ Q_D(const QWidget);
+ if (qt_widget_paintengine()->isActive()) {
+ if (d->extraPaintEngine)
+ return d->extraPaintEngine;
+ QWidget *self = const_cast<QWidget *>(this);
+ self->d_func()->extraPaintEngine = new QX11PaintEngine();
+ return d->extraPaintEngine;
+ }
+ return qt_widget_paintengine();
+}
+
+QWindowSurface *QWidgetPrivate::createDefaultWindowSurface_sys()
+{
+ return new QX11WindowSurface(q_func());
+}
+
+/*!
+ Returns the X11 Picture handle of the widget for XRender
+ support. Use of this function is not portable. This function will
+ return 0 if XRender support is not compiled into Qt, if the
+ XRender extension is not supported on the X11 display, or if the
+ handle could not be created.
+*/
+Qt::HANDLE QWidget::x11PictureHandle() const
+{
+#ifndef QT_NO_XRENDER
+ Q_D(const QWidget);
+ if (!internalWinId() && testAttribute(Qt::WA_WState_Created))
+ (void)winId(); // enforce native window
+ return d->picture;
+#else
+ return 0;
+#endif // QT_NO_XRENDER
+}
+
+#ifndef QT_NO_XRENDER
+XRenderColor QX11Data::preMultiply(const QColor &c)
+{
+ XRenderColor color;
+ const uint A = c.alpha(),
+ R = c.red(),
+ G = c.green(),
+ B = c.blue();
+ color.alpha = (A | A << 8);
+ color.red = (R | R << 8) * color.alpha / 0x10000;
+ color.green = (G | G << 8) * color.alpha / 0x10000;
+ color.blue = (B | B << 8) * color.alpha / 0x10000;
+ return color;
+}
+Picture QX11Data::getSolidFill(int screen, const QColor &c)
+{
+ if (!X11->use_xrender)
+ return XNone;
+
+ XRenderColor color = preMultiply(c);
+ for (int i = 0; i < X11->solid_fill_count; ++i) {
+ if (X11->solid_fills[i].screen == screen
+ && X11->solid_fills[i].color.alpha == color.alpha
+ && X11->solid_fills[i].color.red == color.red
+ && X11->solid_fills[i].color.green == color.green
+ && X11->solid_fills[i].color.blue == color.blue)
+ return X11->solid_fills[i].picture;
+ }
+ // none found, replace one
+ int i = rand() % 16;
+
+ if (X11->solid_fills[i].screen != screen && X11->solid_fills[i].picture) {
+ XRenderFreePicture (X11->display, X11->solid_fills[i].picture);
+ X11->solid_fills[i].picture = 0;
+ }
+
+ if (!X11->solid_fills[i].picture) {
+ Pixmap pixmap = XCreatePixmap (X11->display, RootWindow (X11->display, screen), 1, 1, 32);
+ XRenderPictureAttributes attrs;
+ attrs.repeat = True;
+ X11->solid_fills[i].picture = XRenderCreatePicture (X11->display, pixmap,
+ XRenderFindStandardFormat(X11->display, PictStandardARGB32),
+ CPRepeat, &attrs);
+ XFreePixmap (X11->display, pixmap);
+ }
+
+ X11->solid_fills[i].color = color;
+ X11->solid_fills[i].screen = screen;
+ XRenderFillRectangle (X11->display, PictOpSrc, X11->solid_fills[i].picture, &color, 0, 0, 1, 1);
+ return X11->solid_fills[i].picture;
+}
+#endif
+
+void QWidgetPrivate::setModal_sys()
+{
+}
+
+void qt_x11_getX11InfoForWindow(QX11Info * xinfo, const QX11WindowAttributes &att)
+{
+ QX11InfoData* xd = xinfo->getX11Data(true);
+ const XWindowAttributes &a = *(att.att);
+ // find which screen the window is on...
+ xd->screen = QX11Info::appScreen(); // by default, use the default :)
+ int i;
+ for (i = 0; i < ScreenCount(X11->display); i++) {
+ if (RootWindow(X11->display, i) == a.root) {
+ xd->screen = i;
+ break;
+ }
+ }
+
+ xd->depth = a.depth;
+ xd->cells = DisplayCells(X11->display, xd->screen);
+ xd->visual = a.visual;
+ xd->defaultVisual = (XVisualIDFromVisual((Visual *) a.visual) ==
+ XVisualIDFromVisual((Visual *) QX11Info::appVisual(xinfo->screen())));
+ xd->colormap = a.colormap;
+ xd->defaultColormap = (a.colormap == QX11Info::appColormap(xinfo->screen()));
+ xinfo->setX11Data(xd);
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/kernel/qwidgetaction.cpp b/src/gui/kernel/qwidgetaction.cpp
new file mode 100644
index 0000000000..efdde5e1e6
--- /dev/null
+++ b/src/gui/kernel/qwidgetaction.cpp
@@ -0,0 +1,288 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qwidgetaction.h"
+#include "qdebug.h"
+
+#ifndef QT_NO_ACTION
+#include "qwidgetaction_p.h"
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QWidgetAction
+ \since 4.2
+ \brief The QWidgetAction class extends QAction by an interface
+ for inserting custom widgets into action based containers, such
+ as toolbars.
+
+ Most actions in an application are represented as items in menus or
+ buttons in toolbars. However sometimes more complex widgets are
+ necessary. For example a zoom action in a word processor may be
+ realized using a QComboBox in a QToolBar, presenting a range
+ of different zoom levels. QToolBar provides QToolBar::insertWidget()
+ as convenience function for inserting a single widget.
+ However if you want to implement an action that uses custom
+ widgets for visualization in multiple containers then you have to
+ subclass QWidgetAction.
+
+ If a QWidgetAction is added for example to a QToolBar then
+ QWidgetAction::createWidget() is called. Reimplementations of that
+ function should create a new custom widget with the specified parent.
+
+ If the action is removed from a container widget then
+ QWidgetAction::deleteWidget() is called with the previously created custom
+ widget as argument. The default implementation hides the widget and deletes
+ it using QObject::deleteLater().
+
+ If you have only one single custom widget then you can set it as default
+ widget using setDefaultWidget(). That widget will then be used if the
+ action is added to a QToolBar, or in general to an action container that
+ supports QWidgetAction. If a QWidgetAction with only a default widget is
+ added to two toolbars at the same time then the default widget is shown
+ only in the first toolbar the action was added to. QWidgetAction takes
+ over ownership of the default widget.
+
+ Note that it is up to the widget to activate the action, for example by
+ reimplementing mouse event handlers and calling QAction::trigger().
+
+ \bold {Mac OS X}: If you add a widget to a menu in the application's menu
+ bar on Mac OS X, the widget will be added and it will function but with some
+ limitations:
+ \list 1
+ \o The widget is reparented away from the QMenu to the native menu
+ view. If you show the menu in some other place (e.g. as a popup menu),
+ the widget will not be there.
+ \o Focus/Keyboard handling of the widget is not possible.
+ \o Due to Apple's design, mouse tracking on the widget currently does
+ not work.
+ \o Connecting the triggered() signal to a slot that opens a modal
+ dialog will cause a crash in Mac OS X 10.4 (known bug acknowledged
+ by Apple), a workaround is to use a QueuedConnection instead of a
+ DirectConnection.
+ \endlist
+
+ \ingroup application
+ \mainclass
+
+ \sa QAction, QActionGroup, QWidget
+*/
+
+/*!
+ Constructs an action with \a parent.
+*/
+QWidgetAction::QWidgetAction(QObject *parent)
+ : QAction(*(new QWidgetActionPrivate), parent)
+{
+}
+
+/*!
+ Destroys the object and frees allocated resources.
+*/
+QWidgetAction::~QWidgetAction()
+{
+ Q_D(QWidgetAction);
+ for (int i = 0; i < d->createdWidgets.count(); ++i)
+ disconnect(d->createdWidgets.at(i), SIGNAL(destroyed(QObject*)),
+ this, SLOT(_q_widgetDestroyed(QObject*)));
+ QList<QWidget *> widgetsToDelete = d->createdWidgets;
+ d->createdWidgets.clear();
+ qDeleteAll(widgetsToDelete);
+ delete d->defaultWidget;
+}
+
+/*!
+ Sets \a widget to be the default widget. The ownership is
+ transferred to QWidgetAction. Unless createWidget() is
+ reimplemented by a subclass to return a new widget the default
+ widget is used when a container widget requests a widget through
+ requestWidget().
+*/
+void QWidgetAction::setDefaultWidget(QWidget *widget)
+{
+ Q_D(QWidgetAction);
+ if (widget == d->defaultWidget || d->defaultWidgetInUse)
+ return;
+ delete d->defaultWidget;
+ d->defaultWidget = widget;
+ if (!widget)
+ return;
+
+ setVisible(!(widget->isHidden() && widget->testAttribute(Qt::WA_WState_ExplicitShowHide)));
+ d->defaultWidget->hide();
+ d->defaultWidget->setParent(0);
+ d->defaultWidgetInUse = false;
+ if (!isEnabled())
+ d->defaultWidget->setEnabled(false);
+}
+
+/*!
+ Returns the default widget.
+*/
+QWidget *QWidgetAction::defaultWidget() const
+{
+ Q_D(const QWidgetAction);
+ return d->defaultWidget;
+}
+
+/*!
+ Returns a widget that represents the action, with the given \a
+ parent.
+
+ Container widgets that support actions can call this function to
+ request a widget as visual representation of the action.
+
+ \sa releaseWidget(), createWidget(), defaultWidget()
+*/
+QWidget *QWidgetAction::requestWidget(QWidget *parent)
+{
+ Q_D(QWidgetAction);
+
+ QWidget *w = createWidget(parent);
+ if (!w) {
+ if (d->defaultWidgetInUse || !d->defaultWidget)
+ return 0;
+ d->defaultWidget->setParent(parent);
+ d->defaultWidgetInUse = true;
+ return d->defaultWidget;
+ }
+
+ connect(w, SIGNAL(destroyed(QObject*)),
+ this, SLOT(_q_widgetDestroyed(QObject*)));
+ d->createdWidgets.append(w);
+ return w;
+}
+
+/*!
+ Releases the specified \a widget.
+
+ Container widgets that support actions call this function when a widget
+ action is removed.
+
+ \sa requestWidget(), deleteWidget(), defaultWidget()
+*/
+void QWidgetAction::releaseWidget(QWidget *widget)
+{
+ Q_D(QWidgetAction);
+
+ if (widget == d->defaultWidget) {
+ d->defaultWidget->hide();
+ d->defaultWidget->setParent(0);
+ d->defaultWidgetInUse = false;
+ return;
+ }
+
+ if (!d->createdWidgets.contains(widget))
+ return;
+
+ disconnect(widget, SIGNAL(destroyed(QObject *)),
+ this, SLOT(_q_widgetDestroyed(QObject *)));
+ d->createdWidgets.removeAll(widget);
+ deleteWidget(widget);
+}
+
+/*!
+ \reimp
+*/
+bool QWidgetAction::event(QEvent *event)
+{
+ Q_D(QWidgetAction);
+ if (event->type() == QEvent::ActionChanged) {
+ if (d->defaultWidget)
+ d->defaultWidget->setEnabled(isEnabled());
+ foreach (QWidget *w, d->createdWidgets)
+ w->setEnabled(isEnabled());
+ }
+ return QAction::event(event);
+}
+
+/*!
+ \reimp
+ */
+bool QWidgetAction::eventFilter(QObject *obj, QEvent *event)
+{
+ return QAction::eventFilter(obj,event);
+}
+
+/*!
+ This function is called whenever the action is added to a container widget
+ that supports custom widgets. If you don't want a custom widget to be
+ used as representation of the action in the specified \a parent widget then
+ 0 should be returned.
+
+ \sa deleteWidget()
+*/
+QWidget *QWidgetAction::createWidget(QWidget *parent)
+{
+ Q_UNUSED(parent)
+ return 0;
+}
+
+/*!
+ This function is called whenever the action is removed from a
+ container widget that displays the action using a custom \a
+ widget previously created using createWidget(). The default
+ implementation hides the \a widget and schedules it for deletion
+ using QObject::deleteLater().
+
+ \sa createWidget()
+*/
+void QWidgetAction::deleteWidget(QWidget *widget)
+{
+ widget->hide();
+ widget->deleteLater();
+}
+
+/*!
+ Returns the list of widgets that have been using createWidget() and
+ are currently in use by widgets the action has been added to.
+*/
+QList<QWidget *> QWidgetAction::createdWidgets() const
+{
+ Q_D(const QWidgetAction);
+ return d->createdWidgets;
+}
+
+QT_END_NAMESPACE
+
+#include "moc_qwidgetaction.cpp"
+
+#endif // QT_NO_ACTION
diff --git a/src/gui/kernel/qwidgetaction.h b/src/gui/kernel/qwidgetaction.h
new file mode 100644
index 0000000000..a76222153d
--- /dev/null
+++ b/src/gui/kernel/qwidgetaction.h
@@ -0,0 +1,91 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QWIDGETACTION_H
+#define QWIDGETACTION_H
+
+#include <QtGui/qaction.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+#ifndef QT_NO_ACTION
+
+class QWidgetActionPrivate;
+
+class Q_GUI_EXPORT QWidgetAction : public QAction
+{
+ Q_OBJECT
+ Q_DECLARE_PRIVATE(QWidgetAction)
+
+public:
+ explicit QWidgetAction(QObject *parent);
+ virtual ~QWidgetAction();
+
+ void setDefaultWidget(QWidget *w);
+ QWidget *defaultWidget() const;
+
+ QWidget *requestWidget(QWidget *parent);
+ void releaseWidget(QWidget *widget);
+
+protected:
+ virtual bool event(QEvent *);
+ virtual bool eventFilter(QObject *, QEvent *);
+ virtual QWidget *createWidget(QWidget *parent);
+ virtual void deleteWidget(QWidget *widget);
+ QList<QWidget *> createdWidgets() const;
+
+private:
+ Q_DISABLE_COPY(QWidgetAction)
+ Q_PRIVATE_SLOT(d_func(), void _q_widgetDestroyed(QObject *))
+ friend class QToolBar;
+};
+
+#endif // QT_NO_ACTION
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QWIDGETACTION_H
diff --git a/src/gui/kernel/qwidgetaction_p.h b/src/gui/kernel/qwidgetaction_p.h
new file mode 100644
index 0000000000..8099f9f05c
--- /dev/null
+++ b/src/gui/kernel/qwidgetaction_p.h
@@ -0,0 +1,77 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QWIDGETACTION_P_H
+#define QWIDGETACTION_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/qaction_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class QWidgetActionPrivate : public QActionPrivate
+{
+ Q_DECLARE_PUBLIC(QWidgetAction)
+public:
+ inline QWidgetActionPrivate() : defaultWidgetInUse(false), autoCreated(false) {}
+ QPointer<QWidget> defaultWidget;
+ QList<QWidget *> createdWidgets;
+ uint defaultWidgetInUse : 1;
+ uint autoCreated : 1; // created by QToolBar::addWidget and the like
+
+ inline void _q_widgetDestroyed(QObject *o) {
+ createdWidgets.removeAll(static_cast<QWidget *>(o));
+ }
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/gui/kernel/qwidgetcreate_x11.cpp b/src/gui/kernel/qwidgetcreate_x11.cpp
new file mode 100644
index 0000000000..40546f123d
--- /dev/null
+++ b/src/gui/kernel/qwidgetcreate_x11.cpp
@@ -0,0 +1,79 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qwidget.h"
+#include "qt_x11_p.h"
+
+/*
+ Internal Qt functions to create X windows. We have put them in
+ separate functions to allow the programmer to reimplement them by
+ custom versions.
+*/
+
+QT_BEGIN_NAMESPACE
+
+Window qt_XCreateWindow(const QWidget *, Display *display, Window parent,
+ int x, int y, uint w, uint h,
+ int borderwidth, int depth,
+ uint windowclass, Visual *visual,
+ ulong valuemask, XSetWindowAttributes *attributes)
+{
+ return XCreateWindow(display, parent, x, y, w, h, borderwidth, depth,
+ windowclass, visual, valuemask, attributes);
+}
+
+
+Window qt_XCreateSimpleWindow(const QWidget *, Display *display, Window parent,
+ int x, int y, uint w, uint h, int borderwidth,
+ ulong border, ulong background)
+{
+ return XCreateSimpleWindow(display, parent, x, y, w, h, borderwidth,
+ border, background);
+}
+
+
+void qt_XDestroyWindow(const QWidget *, Display *display, Window window)
+{
+ if (window)
+ XDestroyWindow(display, window);
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/kernel/qwindowdefs.h b/src/gui/kernel/qwindowdefs.h
new file mode 100644
index 0000000000..85980300d4
--- /dev/null
+++ b/src/gui/kernel/qwindowdefs.h
@@ -0,0 +1,152 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QWINDOWDEFS_H
+#define QWINDOWDEFS_H
+
+#include <QtCore/qobjectdefs.h>
+#include <QtCore/qnamespace.h>
+
+QT_BEGIN_HEADER
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+// Class forward definitions
+
+class QPaintDevice;
+class QWidget;
+class QDialog;
+class QColor;
+class QPalette;
+#ifdef QT3_SUPPORT
+class QColorGroup;
+#endif
+class QCursor;
+class QPoint;
+class QSize;
+class QRect;
+class QPolygon;
+class QPainter;
+class QRegion;
+class QFont;
+class QFontMetrics;
+class QFontInfo;
+class QPen;
+class QBrush;
+class QMatrix;
+class QPixmap;
+class QBitmap;
+class QMovie;
+class QImage;
+class QPicture;
+class QPrinter;
+class QTimer;
+class QTime;
+class QClipboard;
+class QString;
+class QByteArray;
+class QApplication;
+
+template<typename T> class QList;
+typedef QList<QWidget *> QWidgetList;
+
+QT_END_NAMESPACE
+QT_END_HEADER
+
+// Window system dependent definitions
+
+#if defined(Q_WS_MAC) && !defined(Q_WS_QWS)
+
+#include <QtGui/qmacdefines_mac.h>
+
+#ifdef Q_WS_MAC32
+typedef int WId;
+#else
+typedef long WId;
+#endif
+
+#endif // Q_WS_MAC
+
+#if defined(Q_WS_WIN)
+#include <QtGui/qwindowdefs_win.h>
+#endif // Q_WS_WIN
+
+#if defined(Q_WS_X11)
+
+typedef struct _XDisplay Display;
+typedef union _XEvent XEvent;
+typedef struct _XGC *GC;
+typedef struct _XRegion *Region;
+typedef unsigned long WId;
+
+#endif // Q_WS_X11
+
+#if defined(Q_WS_QWS)
+
+typedef unsigned long WId;
+QT_BEGIN_HEADER
+QT_BEGIN_NAMESPACE
+struct QWSEvent;
+QT_END_NAMESPACE
+QT_END_HEADER
+
+#endif // Q_WS_QWS
+
+QT_BEGIN_HEADER
+QT_BEGIN_NAMESPACE
+
+template<class K, class V> class QHash;
+typedef QHash<WId, QWidget *> QWidgetMapper;
+
+template<class V> class QSet;
+typedef QSet<QWidget *> QWidgetSet;
+
+QT_END_NAMESPACE
+QT_END_HEADER
+
+#if defined(QT_NEEDS_QMAIN)
+#define main qMain
+#endif
+
+// Global platform-independent types and functions
+
+#endif // QWINDOWDEFS_H
diff --git a/src/gui/kernel/qwindowdefs_win.h b/src/gui/kernel/qwindowdefs_win.h
new file mode 100644
index 0000000000..3899c23aa6
--- /dev/null
+++ b/src/gui/kernel/qwindowdefs_win.h
@@ -0,0 +1,132 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QWINDOWDEFS_WIN_H
+#define QWINDOWDEFS_WIN_H
+
+#include <QtCore/qglobal.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+QT_END_NAMESPACE
+
+#if !defined(Q_NOWINSTRICT)
+#define Q_WINSTRICT
+#endif
+
+#if defined(Q_WINSTRICT)
+
+#if !defined(STRICT)
+#define STRICT
+#endif
+#undef NO_STRICT
+#define Q_DECLARE_HANDLE(name) struct name##__; typedef struct name##__ *name
+
+#else
+
+#if !defined(NO_STRICT)
+#define NO_STRICT
+#endif
+#undef STRICT
+#define Q_DECLARE_HANDLE(name) typedef HANDLE name
+
+#endif
+
+#ifndef HINSTANCE
+Q_DECLARE_HANDLE(HINSTANCE);
+#endif
+#ifndef HDC
+Q_DECLARE_HANDLE(HDC);
+#endif
+#ifndef HWND
+Q_DECLARE_HANDLE(HWND);
+#endif
+#ifndef HFONT
+Q_DECLARE_HANDLE(HFONT);
+#endif
+#ifndef HPEN
+Q_DECLARE_HANDLE(HPEN);
+#endif
+#ifndef HBRUSH
+Q_DECLARE_HANDLE(HBRUSH);
+#endif
+#ifndef HBITMAP
+Q_DECLARE_HANDLE(HBITMAP);
+#endif
+#ifndef HICON
+Q_DECLARE_HANDLE(HICON);
+#endif
+#ifndef HCURSOR
+typedef HICON HCURSOR;
+#endif
+#ifndef HPALETTE
+Q_DECLARE_HANDLE(HPALETTE);
+#endif
+#ifndef HRGN
+Q_DECLARE_HANDLE(HRGN);
+#endif
+#ifndef HMONITOR
+Q_DECLARE_HANDLE(HMONITOR);
+#endif
+#ifndef HRESULT
+typedef long HRESULT;
+#endif
+
+typedef struct tagMSG MSG;
+typedef HWND WId;
+
+
+QT_BEGIN_NAMESPACE
+
+Q_CORE_EXPORT HINSTANCE qWinAppInst();
+Q_CORE_EXPORT HINSTANCE qWinAppPrevInst();
+Q_GUI_EXPORT int qWinAppCmdShow();
+Q_GUI_EXPORT HDC qt_win_display_dc();
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QWINDOWDEFS_WIN_H
diff --git a/src/gui/kernel/qx11embed_x11.cpp b/src/gui/kernel/qx11embed_x11.cpp
new file mode 100644
index 0000000000..e49c4d65b3
--- /dev/null
+++ b/src/gui/kernel/qx11embed_x11.cpp
@@ -0,0 +1,1807 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qx11embed_x11.h"
+#include <qapplication.h>
+#include <qevent.h>
+#include <qpainter.h>
+#include <qlayout.h>
+#include <qstyle.h>
+#include <qstyleoption.h>
+#include <qdatetime.h>
+#include <qpointer.h>
+#include <qdebug.h>
+#include <qx11info_x11.h>
+#include <private/qt_x11_p.h>
+#include <private/qwidget_p.h>
+
+#define XK_MISCELLANY
+#define XK_LATIN1
+#define None 0
+#include <X11/Xlib.h>
+#include <X11/Xatom.h>
+#include <X11/Xutil.h>
+#include <X11/keysymdef.h>
+#include <X11/X.h>
+
+#ifndef XK_ISO_Left_Tab
+#define XK_ISO_Left_Tab 0xFE20
+#endif
+
+//#define QX11EMBED_DEBUG
+#ifdef QX11EMBED_DEBUG
+#include <qdebug.h>
+#endif
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QX11EmbedWidget
+ \ingroup advanced
+
+ \brief The QX11EmbedWidget class provides an XEmbed client widget.
+
+ XEmbed is an X11 protocol that supports the embedding of a widget
+ from one application into another application.
+
+ An XEmbed \e{client widget} is a window that is embedded into a
+ \e container. A container is the graphical location that embeds
+ (or \e swallows) an external application.
+
+ QX11EmbedWidget is a widget used for writing XEmbed applets or
+ plugins. When it has been embedded and the container receives tab
+ focus, focus is passed on to the widget. When the widget reaches
+ the end of its focus chain, focus is passed back to the
+ container. Window activation, accelerator support, modality and
+ drag and drop (XDND) are also handled.
+
+ The widget and container can both initiate the embedding. If the
+ widget is the initiator, the X11 window ID of the container that
+ it wants to embed itself into must be passed to embedInto().
+
+ If the container initiates the embedding, the window ID of the
+ embedded widget must be known. The container calls embed(),
+ passing the window ID.
+
+ This example shows an application that embeds a QX11EmbedWidget
+ subclass into the window whose ID is passed as a command-line
+ argument:
+
+ \snippet doc/src/snippets/qx11embedwidget/main.cpp 0
+
+ The problem of obtaining the window IDs is often solved by the
+ container invoking the application that provides the widget as a
+ separate process (as a panel invokes a docked applet), passing
+ its window ID to the new process as a command-line argument. The
+ new process can then call embedInto() with the container's window
+ ID, as shown in the example code above. Similarly, the new
+ process can report its window ID to the container through IPC, in
+ which case the container can embed the widget.
+
+ When the widget has been embedded, it emits the signal
+ embedded(). If it is closed by the container, the widget emits
+ containerClosed(). If an error occurs when embedding, error() is
+ emitted.
+
+ There are XEmbed widgets available for KDE and GTK+. The GTK+
+ equivalent of QX11EmbedWidget is GtkPlug. The KDE widget is called
+ QXEmbed.
+
+ \sa QX11EmbedContainer, {XEmbed Specification}
+*/
+
+/*!
+ \class QX11EmbedContainer
+ \ingroup advanced
+
+ \brief The QX11EmbedContainer class provides an XEmbed container
+ widget.
+
+ XEmbed is an X11 protocol that supports the embedding of a widget
+ from one application into another application.
+
+ An XEmbed \e container is the graphical location that embeds an
+ external \e {client widget}. A client widget is a window that is
+ embedded into a container.
+
+ When a widget has been embedded and the container receives tab
+ focus, focus is passed on to the widget. When the widget reaches
+ the end of its focus chain, focus is passed back to the
+ container. Window activation, accelerator support, modality and
+ drag and drop (XDND) are also handled.
+
+ QX11EmbedContainer is commonly used for writing panels or
+ toolbars that hold applets, or for \e swallowing X11
+ applications. When writing a panel application, one container
+ widget is created on the toolbar, and it can then either swallow
+ another widget using embed(), or allow an XEmbed widget to be
+ embedded into itself. The container's X11 window ID, which is
+ retrieved with winId(), must then be known to the client widget.
+ After embedding, the client's window ID can be retrieved with
+ clientWinId().
+
+ In the following example, a container widget is created as the
+ main widget. It then invokes an application called "playmovie",
+ passing its window ID as a command line argument. The "playmovie"
+ program is an XEmbed client widget. The widget embeds itself into
+ the container using the container's window ID.
+
+ \snippet doc/src/snippets/qx11embedcontainer/main.cpp 0
+
+ When the client widget is embedded, the container emits the
+ signal clientIsEmbedded(). The signal clientClosed() is emitted
+ when a widget is closed.
+
+ It is possible for QX11EmbedContainer to embed XEmbed widgets
+ from toolkits other than Qt, such as GTK+. Arbitrary (non-XEmbed)
+ X11 widgets can also be embedded, but the XEmbed-specific
+ features such as window activation and focus handling are then
+ lost.
+
+ The GTK+ equivalent of QX11EmbedContainer is GtkSocket. The KDE
+ widget is called QXEmbed.
+
+ \sa QX11EmbedWidget, {XEmbed Specification}
+*/
+
+/*! \fn void QX11EmbedWidget::embedded()
+
+ This signal is emitted by the widget that has been embedded by an
+ XEmbed container.
+*/
+
+/*! \fn void QX11EmbedWidget::containerClosed()
+
+ This signal is emitted by the client widget when the container
+ closes the widget. This can happen if the container itself
+ closes, or if the widget is rejected.
+
+ The container can reject a widget for any reason, but the most
+ common cause of a rejection is when an attempt is made to embed a
+ widget into a container that already has an embedded widget.
+*/
+
+/*! \fn void QX11EmbedContainer::clientIsEmbedded()
+
+ This signal is emitted by the container when a client widget has
+ been embedded.
+*/
+
+/*! \fn void QX11EmbedContainer::clientClosed()
+
+ This signal is emitted by the container when the client widget
+ closes.
+*/
+
+/*!
+ \fn void QX11EmbedWidget::error(QX11EmbedWidget::Error error)
+
+ This signal is emitted if an error occurred as a result of
+ embedding into or communicating with a container. The specified
+ \a error describes the problem that occurred.
+
+ \sa QX11EmbedWidget::Error
+*/
+
+/*!
+ \fn QX11EmbedContainer::Error QX11EmbedContainer::error() const
+
+ Returns the last error that occurred.
+*/
+
+/*! \fn void QX11EmbedContainer::error(QX11EmbedContainer::Error error)
+
+ This signal is emitted if an error occurred when embedding or
+ communicating with a client. The specified \a error describes the
+ problem that occurred.
+
+ \sa QX11EmbedContainer::Error
+*/
+
+/*!
+ \enum QX11EmbedWidget::Error
+
+ \value Unknown An unrecognized error occurred.
+
+ \value InvalidWindowID The X11 window ID of the container was
+ invalid. This error is usually triggered by passing an invalid
+ window ID to embedInto().
+
+ \omitvalue Internal
+*/
+
+/*!
+ \enum QX11EmbedContainer::Error
+
+ \value Unknown An unrecognized error occurred.
+
+ \value InvalidWindowID The X11 window ID of the container was
+ invalid. This error is usually triggered by passing an invalid
+ window ID to embed().
+
+ \omitvalue Internal
+*/
+
+const int XButtonPress = ButtonPress;
+const int XButtonRelease = ButtonRelease;
+#undef ButtonPress
+#undef ButtonRelease
+
+// This is a hack to move topData() out from QWidgetPrivate to public. We
+// need to to inspect window()'s embedded state.
+class QHackWidget : public QWidget
+{
+ Q_DECLARE_PRIVATE(QWidget)
+public:
+ QTLWExtra* topData() { return d_func()->topData(); }
+};
+
+static unsigned int XEMBED_VERSION = 0;
+
+enum QX11EmbedMessageType {
+ XEMBED_EMBEDDED_NOTIFY = 0,
+ XEMBED_WINDOW_ACTIVATE = 1,
+ XEMBED_WINDOW_DEACTIVATE = 2,
+ XEMBED_REQUEST_FOCUS = 3,
+ XEMBED_FOCUS_IN = 4,
+ XEMBED_FOCUS_OUT = 5,
+ XEMBED_FOCUS_NEXT = 6,
+ XEMBED_FOCUS_PREV = 7,
+ XEMBED_MODALITY_ON = 10,
+ XEMBED_MODALITY_OFF = 11,
+ XEMBED_REGISTER_ACCELERATOR = 12,
+ XEMBED_UNREGISTER_ACCELERATOR = 13,
+ XEMBED_ACTIVATE_ACCELERATOR = 14
+};
+
+enum QX11EmbedFocusInDetail {
+ XEMBED_FOCUS_CURRENT = 0,
+ XEMBED_FOCUS_FIRST = 1,
+ XEMBED_FOCUS_LAST = 2
+};
+
+enum QX11EmbedFocusInFlags {
+ XEMBED_FOCUS_OTHER = (0 << 0),
+ XEMBED_FOCUS_WRAPAROUND = (1 << 0)
+};
+
+enum QX11EmbedInfoFlags {
+ XEMBED_MAPPED = (1 << 0)
+};
+
+enum QX11EmbedAccelModifiers {
+ XEMBED_MODIFIER_SHIFT = (1 << 0),
+ XEMBED_MODIFIER_CONTROL = (1 << 1),
+ XEMBED_MODIFIER_ALT = (1 << 2),
+ XEMBED_MODIFIER_SUPER = (1 << 3),
+ XEMBED_MODIFIER_HYPER = (1 << 4)
+};
+
+enum QX11EmbedAccelFlags {
+ XEMBED_ACCELERATOR_OVERLOADED = (1 << 0)
+};
+
+// Silence the default X11 error handler.
+static int x11ErrorHandler(Display *, XErrorEvent *)
+{
+ return 0;
+}
+
+// Returns the X11 timestamp. Maintained mainly by qapplication
+// internals, but also updated by the XEmbed widgets.
+static Time x11Time()
+{
+ return qt_x11Data->time;
+}
+
+// Gives the version and flags of the supported XEmbed protocol.
+static unsigned int XEmbedVersion()
+{
+ return 0;
+}
+
+// Sends an XEmbed message.
+static void sendXEmbedMessage(WId window, Display *display, long message,
+ long detail = 0, long data1 = 0, long data2 = 0)
+{
+ XClientMessageEvent c;
+ memset(&c, 0, sizeof(c));
+ c.type = ClientMessage;
+ c.message_type = ATOM(_XEMBED);
+ c.format = 32;
+ c.display = display;
+ c.window = window;
+
+ c.data.l[0] = x11Time();
+ c.data.l[1] = message;
+ c.data.l[2] = detail;
+ c.data.l[3] = data1;
+ c.data.l[4] = data2;
+
+ XSendEvent(display, window, false, NoEventMask, (XEvent *) &c);
+}
+
+// From qapplication_x11.cpp
+static XKeyEvent lastKeyEvent;
+
+static QCoreApplication::EventFilter oldX11EventFilter;
+
+// The purpose of this global x11 filter is for one to capture the key
+// events in their original state, but most importantly this is the
+// only way to get the WM_TAKE_FOCUS message from WM_PROTOCOLS.
+static bool x11EventFilter(void *message, long *result)
+{
+ XEvent *event = reinterpret_cast<XEvent *>(message);
+ if (event->type == XKeyPress || event->type == XKeyRelease)
+ lastKeyEvent = event->xkey;
+
+ if (oldX11EventFilter && oldX11EventFilter != &x11EventFilter)
+ return oldX11EventFilter(message, result);
+ else
+ return false;
+}
+
+//
+struct functorData
+{
+ Window id, rootWindow;
+ bool clearedWmState;
+ bool reparentedToRoot;
+};
+
+static Bool functor(Display *display, XEvent *event, XPointer arg)
+{
+ functorData *data = (functorData *) arg;
+
+ if (!data->reparentedToRoot && event->type == ReparentNotify
+ && event->xreparent.window == data->id
+ && event->xreparent.parent == data->rootWindow) {
+ data->reparentedToRoot = true;
+ return true;
+ }
+
+ if (!data->clearedWmState
+ && event->type == PropertyNotify
+ && event->xproperty.window == data->id
+ && event->xproperty.atom == ATOM(WM_STATE)) {
+ if (event->xproperty.state == PropertyDelete) {
+ data->clearedWmState = true;
+ return true;
+ }
+
+ Atom ret;
+ int format, status;
+ unsigned char *retval;
+ unsigned long nitems, after;
+ status = XGetWindowProperty(display, data->id, ATOM(WM_STATE), 0, 2, False, ATOM(WM_STATE),
+ &ret, &format, &nitems, &after, &retval );
+ if (status == Success && ret == ATOM(WM_STATE) && format == 32 && nitems > 0) {
+ long *state = (long *)retval;
+ if (state[0] == WithdrawnState) {
+ data->clearedWmState = true;
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+class QX11EmbedWidgetPrivate : public QWidgetPrivate
+{
+ Q_DECLARE_PUBLIC(QX11EmbedWidget)
+public:
+ inline QX11EmbedWidgetPrivate()
+ {
+ lastError = QX11EmbedWidget::Unknown;
+ container = 0;
+ }
+
+ void setEmbedded();
+
+ void emitError(QX11EmbedWidget::Error error) {
+ Q_Q(QX11EmbedWidget);
+
+ lastError = error;
+ emit q->error(error);
+ }
+
+ enum FocusWidgets {
+ FirstFocusWidget,
+ LastFocusWidget
+ };
+
+ int focusOriginator;
+ QWidget *getFocusWidget(FocusWidgets fw);
+ void checkActivateWindow(QObject *o);
+ QX11EmbedWidget *xEmbedWidget(QObject *o) const;
+ void clearFocus();
+
+ WId container;
+ QPointer<QWidget> currentFocus;
+
+ QX11EmbedWidget::Error lastError;
+
+};
+
+/*!
+ Constructs a QX11EmbedWidget object with the given \a parent.
+*/
+QX11EmbedWidget::QX11EmbedWidget(QWidget *parent)
+ : QWidget(*new QX11EmbedWidgetPrivate, parent, 0)
+{
+ XSetErrorHandler(x11ErrorHandler);
+
+ setAttribute(Qt::WA_NativeWindow);
+ setAttribute(Qt::WA_DontCreateNativeAncestors);
+ createWinId();
+ XSelectInput(x11Info().display(), internalWinId(),
+ KeyPressMask | KeyReleaseMask | ButtonPressMask
+ | ButtonReleaseMask
+ | KeymapStateMask | ButtonMotionMask | PointerMotionMask
+ | FocusChangeMask
+ | ExposureMask | StructureNotifyMask
+ | SubstructureNotifyMask | PropertyChangeMask);
+
+ unsigned int data[] = {XEMBED_VERSION, XEMBED_MAPPED};
+ XChangeProperty(x11Info().display(), internalWinId(), ATOM(_XEMBED_INFO),
+ ATOM(_XEMBED_INFO), 32, PropModeReplace,
+ (unsigned char*) data, 2);
+
+ setFocusPolicy(Qt::StrongFocus);
+ setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
+ QApplication::instance()->installEventFilter(this);
+
+#ifdef QX11EMBED_DEBUG
+ qDebug() << "QX11EmbedWidget::QX11EmbedWidget: constructed client"
+ << (void *)this << "with winId" << winId();
+#endif
+}
+
+/*!
+ Destructs the QX11EmbedWidget object. If the widget is embedded
+ when deleted, it is hidden and then detached from its container,
+ so that the container is free to embed a new widget.
+*/
+QX11EmbedWidget::~QX11EmbedWidget()
+{
+ Q_D(QX11EmbedWidget);
+ if (d->container) {
+#ifdef QX11EMBED_DEBUG
+ qDebug() << "QX11EmbedWidget::~QX11EmbedWidget: unmapping"
+ << (void *)this << "with winId" << winId()
+ << "from container with winId" << d->container;
+#endif
+ XUnmapWindow(x11Info().display(), internalWinId());
+ XReparentWindow(x11Info().display(), internalWinId(), x11Info().appRootWindow(), 0, 0);
+ }
+
+#ifdef QX11EMBED_DEBUG
+ qDebug() << "QX11EmbedWidget::~QX11EmbedWidget: destructed client"
+ << (void *)this << "with winId" << winId();
+#endif
+}
+
+/*!
+ Returns the type of error that occurred last. This is the same error code
+ that is emitted by the error() signal.
+
+ \sa Error
+*/
+QX11EmbedWidget::Error QX11EmbedWidget::error() const
+{
+ return d_func()->lastError;
+}
+
+/*!
+ When this function is called, the widget embeds itself into the
+ container whose window ID is \a id.
+
+ If \a id is \e not the window ID of a container this function will
+ behave unpredictably.
+*/
+void QX11EmbedWidget::embedInto(WId id)
+{
+ Q_D(QX11EmbedWidget);
+#ifdef QX11EMBED_DEBUG
+ qDebug() << "QX11EmbedWidget::embedInto: embedding client"
+ << (void *)this << "with winId" << winId() << "into container"
+ << id;
+#endif
+
+ d->container = id;
+ switch (XReparentWindow(x11Info().display(), internalWinId(), d->container, 0, 0)) {
+ case BadWindow:
+ d->emitError(InvalidWindowID);
+ break;
+ case BadMatch:
+ d->emitError(Internal);
+ break;
+ case Success:
+ default:
+ break;
+ }
+ QTLWExtra* x = d->extra ? d->extra->topextra : 0;
+ if (x)
+ x->frameStrut.setCoords(0, 0, 0, 0);
+ d->data.fstrut_dirty = false;
+}
+
+/*! \internal
+
+ Gets the first or last child widget that can get focus.
+*/
+QWidget *QX11EmbedWidgetPrivate::getFocusWidget(FocusWidgets fw)
+{
+ Q_Q(QX11EmbedWidget);
+ QWidget *tlw = q;
+ QWidget *w = tlw->nextInFocusChain();
+
+ QWidget *last = tlw;
+
+ extern bool qt_tab_all_widgets;
+ uint focus_flag = qt_tab_all_widgets ? Qt::TabFocus : Qt::StrongFocus;
+
+ while (w != tlw)
+ {
+ if (((w->focusPolicy() & focus_flag) == focus_flag)
+ && w->isVisibleTo(q) && w->isEnabled())
+ {
+ last = w;
+ if (fw == FirstFocusWidget)
+ break;
+ }
+ w = w->nextInFocusChain();
+ }
+
+ return last;
+}
+
+/*! \internal
+
+ Returns the xembed widget that the widget is a child of
+*/
+QX11EmbedWidget *QX11EmbedWidgetPrivate::xEmbedWidget(QObject *o) const
+{
+ QX11EmbedWidget *xec = 0;
+
+ // Check the widget itself, then its parents, and find the first
+ // QX11EmbedWidget.
+ do {
+ if ((xec = qobject_cast<QX11EmbedWidget *>(o)))
+ return xec;
+ } while ((o = o->parent()));
+ return 0;
+}
+
+/*! \internal
+
+ Checks the active window.
+*/
+void QX11EmbedWidgetPrivate::checkActivateWindow(QObject *o)
+{
+ Q_Q(QX11EmbedWidget);
+ QX11EmbedWidget *xec = xEmbedWidget(o);
+
+ // check if we are in the right xembed client
+ if (q != xec)
+ return;
+
+ QWidget *w = qobject_cast<QWidget *>(o);
+
+ // if it is no active window, then don't do the change
+ if (!(w && qApp->activeWindow()))
+ return;
+
+ // if it already is the active window, don't do anything
+ if (w->window() != qApp->activeWindow())
+ {
+ qApp->setActiveWindow(w->window());
+ currentFocus = w;
+
+ sendXEmbedMessage(xec->containerWinId(), q->x11Info().display(), XEMBED_REQUEST_FOCUS);
+ }
+}
+
+/*! \internal
+
+ Clears the focus
+*/
+void QX11EmbedWidgetPrivate::clearFocus()
+{
+ Q_Q(QX11EmbedWidget);
+ // Setting focus on the client itself removes Qt's
+ // logical focus rectangle. We can't just do a
+ // clearFocus here, because when we send the synthetic
+ // FocusIn to ourselves on activation, Qt will set
+ // focus on focusWidget() again. This way, we "hide"
+ // focus rather than clearing it.
+
+ if (!q->window()->hasFocus())
+ q->window()->setFocus(Qt::OtherFocusReason);
+
+ currentFocus = 0;
+}
+
+/*! \internal
+
+ Sets the embedded flag on the client.
+*/
+void QX11EmbedWidgetPrivate::setEmbedded()
+{
+ Q_Q(QX11EmbedWidget);
+ ((QHackWidget *)q->window())->topData()->embedded = 1;
+}
+
+/*! \internal
+
+ Handles WindowActivate and FocusIn events for the client.
+*/
+bool QX11EmbedWidget::eventFilter(QObject *o, QEvent *event)
+{
+ Q_D(QX11EmbedWidget);
+ switch (event->type()) {
+ case QEvent::FocusIn:
+ switch (((QFocusEvent *)event)->reason()) {
+ case Qt::MouseFocusReason:
+ // If the user clicks into one of the client widget's
+ // children and we didn't have focus already, we request
+ // focus from our container.
+ if (d->xEmbedWidget(o) == this) {
+ if (d->currentFocus.isNull())
+ sendXEmbedMessage(d->container, x11Info().display(), XEMBED_REQUEST_FOCUS);
+
+ d->currentFocus = qobject_cast<QWidget *>(o);
+ }
+ break;
+ case Qt::TabFocusReason:
+ // If the xembed client receives a focus event because of
+ // a Tab, then we are at the end of our focus chain and we
+ // ask the container to move to its next focus widget.
+ if (o == this) {
+ d->clearFocus();
+ sendXEmbedMessage(d->container, x11Info().display(), XEMBED_FOCUS_NEXT);
+ return true;
+ } else {
+ // We're listening on events from qApp, so in order
+ // for us to know who to set focus on if we receive an
+ // activation event, we note the widget that got the
+ // focusin last.
+ if (d->xEmbedWidget(o) == this)
+ d->currentFocus = qobject_cast<QWidget *>(o);
+ }
+ break;
+ case Qt::BacktabFocusReason:
+ // If the window receives a focus event because of
+ // a Backtab, then we are at the start of our focus chain
+ // and we ask the container to move to its previous focus
+ // widget.
+ if (o == this) {
+ // See comment for Tab.
+ // If we receive a XEMBED_FOCUS_IN
+ // XEMBED_FOCUS_CURRENT, we will set focus in
+ // currentFocus. To avoid that in this case, we reset
+ // currentFocus.
+ d->clearFocus();
+ sendXEmbedMessage(d->container, x11Info().display(), XEMBED_FOCUS_PREV);
+ return true;
+ } else {
+ if (d->xEmbedWidget(o) == this)
+ d->currentFocus = qobject_cast<QWidget *>(o);
+ }
+ break;
+ case Qt::ActiveWindowFocusReason:
+ if (isActiveWindow()) {
+ if (!d->currentFocus.isNull()) {
+ if (!d->currentFocus->hasFocus())
+ d->currentFocus->setFocus(Qt::OtherFocusReason);
+ } else {
+ d->clearFocus();
+ return true;
+ }
+ }
+
+ break;
+ case Qt::PopupFocusReason:
+ case Qt::ShortcutFocusReason:
+ case Qt::OtherFocusReason:
+ // If focus is received to any child widget because of any
+ // other reason, remember the widget so that we can give
+ // it focus again if we're activated.
+ if (d->xEmbedWidget(o) == this) {
+ d->currentFocus = qobject_cast<QWidget *>(o);
+ }
+ break;
+ default:
+ break;
+ }
+ break;
+ case QEvent::MouseButtonPress:
+ // If we get a mouse button press event inside a embedded widget
+ // make sure this is the active window in qapp.
+ d->checkActivateWindow(o);
+ break;
+ default:
+ break;
+ }
+
+ return QWidget::eventFilter(o, event);
+}
+
+/*! \internal
+
+ Handles some notification events and client messages. Client side
+ XEmbed message receiving is also handled here.
+*/
+bool QX11EmbedWidget::x11Event(XEvent *event)
+{
+ Q_D(QX11EmbedWidget);
+ switch (event->type) {
+ case DestroyNotify:
+#ifdef QX11EMBED_DEBUG
+ qDebug() << "QX11EmbedWidget::x11Event: client"
+ << (void *)this << "with winId" << winId()
+ << "received a DestroyNotify";
+#endif
+ // If the container window is destroyed, we signal this to the user.
+ d->container = 0;
+ emit containerClosed();
+ break;
+ case ReparentNotify:
+#ifdef QX11EMBED_DEBUG
+ qDebug() << "QX11EmbedWidget::x11Event: client"
+ << (void *)this << "with winId" << winId()
+ << "received a ReparentNotify to"
+ << ((event->xreparent.parent == x11Info().appRootWindow())
+ ? QString::fromLatin1("root") : QString::number(event->xreparent.parent));
+#endif
+ // If the container shuts down, we will be reparented to the
+ // root window. We must also consider the case that we may be
+ // reparented from one container to another.
+ if (event->xreparent.parent == x11Info().appRootWindow()) {
+ if (((QHackWidget *)this)->topData()->embedded) {
+ d->container = 0;
+ emit containerClosed();
+ }
+ return true;
+ } else {
+ d->container = event->xreparent.parent;
+ }
+ break;
+ case UnmapNotify:
+ // Mapping and unmapping are handled by changes to the
+ // _XEMBED_INFO property. Any default map/unmap requests are
+ // ignored.
+ return true;
+ case PropertyNotify:
+ // The container sends us map/unmap messages through the
+ // _XEMBED_INFO property. We adhere to the XEMBED_MAPPED bit in
+ // data2.
+ if (event->xproperty.atom == ATOM(_XEMBED_INFO)) {
+ Atom actual_type_return;
+ int actual_format_return;
+ unsigned long nitems_return;
+ unsigned long bytes_after_return;
+ unsigned char *prop_return = 0;
+ if (XGetWindowProperty(x11Info().display(), internalWinId(), ATOM(_XEMBED_INFO), 0, 2,
+ false, ATOM(_XEMBED_INFO), &actual_type_return,
+ &actual_format_return, &nitems_return,
+ &bytes_after_return, &prop_return) == Success) {
+ if (nitems_return > 1) {
+ if (((int * )prop_return)[1] & XEMBED_MAPPED) {
+ XMapWindow(x11Info().display(), internalWinId());
+ } else {
+ XUnmapWindow(x11Info().display(), internalWinId());
+ }
+ }
+ }
+ }
+
+ break;
+ case ClientMessage:
+ // XEMBED messages have message_type _XEMBED
+ if (event->xclient.message_type == ATOM(_XEMBED)) {
+ // Discard XEMBED messages not to ourselves. (### dead code?)
+ if (event->xclient.window != internalWinId())
+ break;
+
+ // Update qt_x_time if necessary
+ Time msgtime = (Time) event->xclient.data.l[0];
+ if (msgtime > X11->time)
+ X11->time = msgtime;
+
+ switch (event->xclient.data.l[1]) {
+ case XEMBED_WINDOW_ACTIVATE: {
+ // When we receive an XEMBED_WINDOW_ACTIVATE, we simply send
+ // ourselves a WindowActivate event. Real activation happens
+ // when receive XEMBED_FOCUS_IN.
+ if (!isActiveWindow()) {
+ QEvent ev(QEvent::WindowActivate);
+ QApplication::sendEvent(this, &ev);
+ }
+ }
+ break;
+ case XEMBED_WINDOW_DEACTIVATE: {
+ // When we receive an XEMBED_WINDOW_DEACTIVATE, we simply send
+ // ourselves a WindowDeactivate event. Real activation happens
+ // when receive XEMBED_FOCUS_IN.
+ if (isActiveWindow()) {
+ if (!qApp->activePopupWidget())
+ QApplication::setActiveWindow(0);
+ } else {
+ QEvent ev(QEvent::WindowDeactivate);
+ QApplication::sendEvent(this, &ev);
+ }
+ }
+ break;
+ case XEMBED_EMBEDDED_NOTIFY: {
+#ifdef QX11EMBED_DEBUG
+ qDebug() << "QX11EmbedWidget::x11Event: client"
+ << (void *)this << "with winId" << winId()
+ << "received an XEMBED EMBEDDED NOTIFY message";
+#endif
+ // In this message's l[2] we have the max version
+ // supported by both the client and the
+ // container. QX11EmbedWidget does not use this field.
+
+ // We have been embedded, so we set our
+ // client's embedded flag.
+ d->setEmbedded();
+ emit embedded();
+ }
+ break;
+ case XEMBED_FOCUS_IN:
+ // don't set the focus if a modal dialog is open
+ if (qApp->activeModalWidget())
+ break;
+
+ // in case we embed more than one topLevel window inside the same
+ // host window.
+ if (window() != qApp->activeWindow())
+ qApp->setActiveWindow(this);
+
+ switch (event->xclient.data.l[2]) {
+ case XEMBED_FOCUS_CURRENT:
+ // The container sends us this message if it wants
+ // us to focus on the widget that last had focus.
+ // This is the reply when XEMBED_REQUEST_FOCUS is
+ // sent to the container.
+ if (!d->currentFocus.isNull()) {
+ if (!d->currentFocus->hasFocus())
+ d->currentFocus->setFocus(Qt::OtherFocusReason);
+ } else {
+ // No widget currently has focus. We set focus
+ // on the first widget next to the
+ // client widget. Since the setFocus will not work
+ // if the window is disabled, set the currentFocus
+ // directly so that it's set on window activate.
+ d->currentFocus = d->getFocusWidget(QX11EmbedWidgetPrivate::FirstFocusWidget);
+ d->currentFocus->setFocus(Qt::OtherFocusReason);
+ }
+ break;
+ case XEMBED_FOCUS_FIRST:
+ // The container sends this message when it wants
+ // us to focus on the first widget in our focus
+ // chain (typically because of a tab).
+ d->currentFocus = d->getFocusWidget(QX11EmbedWidgetPrivate::FirstFocusWidget);
+ d->currentFocus->setFocus(Qt::TabFocusReason);
+ break;
+ case XEMBED_FOCUS_LAST:
+ // The container sends this message when it wants
+ // us to focus on the last widget in our focus
+ // chain (typically because of a backtab).
+ d->currentFocus = d->getFocusWidget(QX11EmbedWidgetPrivate::LastFocusWidget);
+ d->currentFocus->setFocus(Qt::BacktabFocusReason);
+ break;
+ default:
+ // Ignore any other XEMBED_FOCUS_IN details.
+ break;
+ }
+ break;
+ case XEMBED_FOCUS_OUT:
+ // The container sends us this message when it wants us
+ // to lose focus and forget about the widget that last
+ // had focus. Typically sent by the container when it
+ // loses focus because of mouse or tab activity. We do
+ // then not want to set focus on anything if we're
+ // activated.
+ if (isActiveWindow())
+ d->clearFocus();
+
+ break;
+ default:
+ // Ignore any other XEMBED messages.
+ break;
+ };
+ } else {
+ // Non-XEMBED client messages are not interesting.
+ }
+
+ break;
+ default:
+ // Ignore all other x11 events.
+ break;
+ }
+
+ // Allow default handling.
+ return QWidget::x11Event(event);
+}
+
+/*!
+ \reimp
+*/
+bool QX11EmbedWidget::event(QEvent *event)
+{
+ if (event->type() == QEvent::ParentChange) {
+ XSelectInput(x11Info().display(), internalWinId(),
+ KeyPressMask | KeyReleaseMask | ButtonPressMask
+ | ButtonReleaseMask
+ | KeymapStateMask | ButtonMotionMask | PointerMotionMask
+ | FocusChangeMask
+ | ExposureMask | StructureNotifyMask
+ | SubstructureNotifyMask | PropertyChangeMask);
+ }
+ return QWidget::event(event);
+}
+
+/*!
+ \reimp
+*/
+void QX11EmbedWidget::resizeEvent(QResizeEvent *event)
+{
+ if (layout())
+ layout()->update();
+ QWidget::resizeEvent(event);
+}
+
+/*!
+ If the widget is embedded, returns the window ID of the
+ container; otherwize returns 0.
+*/
+WId QX11EmbedWidget::containerWinId() const
+{
+ Q_D(const QX11EmbedWidget);
+ return d->container;
+}
+
+class QX11EmbedContainerPrivate : public QWidgetPrivate
+{
+ Q_DECLARE_PUBLIC(QX11EmbedContainer)
+public:
+ inline QX11EmbedContainerPrivate()
+ {
+ lastError = QX11EmbedContainer::Unknown;
+ client = 0;
+ focusProxy = 0;
+ clientIsXEmbed = false;
+ xgrab = false;
+ }
+
+ bool isEmbedded() const;
+ void moveInputToProxy();
+
+ void acceptClient(WId window);
+ void rejectClient(WId window);
+
+ void checkGrab();
+
+ WId topLevelParentWinId() const;
+
+ void emitError(QX11EmbedContainer::Error error) {
+ Q_Q(QX11EmbedContainer);
+ lastError = error;
+ emit q->error(error);
+ }
+
+ WId client;
+ QWidget *focusProxy;
+ bool clientIsXEmbed;
+ bool xgrab;
+ QRect clientOriginalRect;
+ QSize wmMinimumSizeHint;
+
+ QX11EmbedContainer::Error lastError;
+
+ static QX11EmbedContainer *activeContainer;
+};
+
+QX11EmbedContainer *QX11EmbedContainerPrivate::activeContainer = 0;
+
+/*!
+ Creates a QX11EmbedContainer object with the given \a parent.
+*/
+QX11EmbedContainer::QX11EmbedContainer(QWidget *parent)
+ : QWidget(*new QX11EmbedContainerPrivate, parent, 0)
+{
+ Q_D(QX11EmbedContainer);
+ XSetErrorHandler(x11ErrorHandler);
+
+ setAttribute(Qt::WA_NativeWindow);
+ setAttribute(Qt::WA_DontCreateNativeAncestors);
+ createWinId();
+
+ setFocusPolicy(Qt::StrongFocus);
+ setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
+ // ### PORT setKeyCompression(false);
+ setAcceptDrops(true);
+ setEnabled(false);
+
+ // Everybody gets a focus proxy, but only one toplevel container's
+ // focus proxy is actually in use.
+ d->focusProxy = new QWidget(this);
+ d->focusProxy->setAttribute(Qt::WA_NativeWindow);
+ d->focusProxy->setAttribute(Qt::WA_DontCreateNativeAncestors);
+ d->focusProxy->setGeometry(-1, -1, 1, 1);
+
+ // We need events from the window (activation status) and
+ // from qApp (keypress/release).
+ qApp->installEventFilter(this);
+
+ // Install X11 event filter.
+ if (!oldX11EventFilter)
+ oldX11EventFilter = QCoreApplication::instance()->setEventFilter(x11EventFilter);
+
+ XSelectInput(x11Info().display(), internalWinId(),
+ KeyPressMask | KeyReleaseMask
+ | ButtonPressMask | ButtonReleaseMask | ButtonMotionMask
+ | KeymapStateMask
+ | PointerMotionMask
+ | EnterWindowMask | LeaveWindowMask
+ | FocusChangeMask
+ | ExposureMask
+ | StructureNotifyMask
+ | SubstructureNotifyMask);
+
+ // Make sure our new event mask takes effect as soon as possible.
+ XFlush(x11Info().display());
+
+ // Move input to our focusProxy if this widget is active, and not
+ // shaded by a modal dialog (in which case isActiveWindow() would
+ // still return true, but where we must not move input focus).
+ if (qApp->activeWindow() == window() && !d->isEmbedded())
+ d->moveInputToProxy();
+
+#ifdef QX11EMBED_DEBUG
+ qDebug() << "QX11EmbedContainer::QX11EmbedContainer: constructed container"
+ << (void *)this << "with winId" << winId();
+#endif
+}
+
+/*!
+ Destructs a QX11EmbedContainer.
+*/
+QX11EmbedContainer::~QX11EmbedContainer()
+{
+ Q_D(QX11EmbedContainer);
+ if (d->client) {
+ XUnmapWindow(x11Info().display(), d->client);
+ XReparentWindow(x11Info().display(), d->client, x11Info().appRootWindow(), 0, 0);
+ }
+
+ if (d->xgrab)
+ XUngrabButton(x11Info().display(), AnyButton, AnyModifier, internalWinId());
+}
+
+
+QX11EmbedContainer::Error QX11EmbedContainer::error() const {
+ return d_func()->lastError;
+}
+
+
+/*! \reimp
+*/
+void QX11EmbedContainer::paintEvent(QPaintEvent *)
+{
+}
+
+/*! \internal
+
+ Returns wether or not the windows' embedded flag is set.
+*/
+bool QX11EmbedContainerPrivate::isEmbedded() const
+{
+ Q_Q(const QX11EmbedContainer);
+ return ((QHackWidget *)q->window())->topData()->embedded == 1;
+}
+
+/*! \internal
+
+ Returns the parentWinId of the window.
+*/
+WId QX11EmbedContainerPrivate::topLevelParentWinId() const
+{
+ Q_Q(const QX11EmbedContainer);
+ return ((QHackWidget *)q->window())->topData()->parentWinId;
+}
+
+/*!
+ If the container has an embedded widget, this function returns
+ the X11 window ID of the client; otherwise it returns 0.
+*/
+WId QX11EmbedContainer::clientWinId() const
+{
+ Q_D(const QX11EmbedContainer);
+ return d->client;
+}
+
+/*!
+ Instructs the container to embed the X11 window with window ID \a
+ id. The client widget will then move on top of the container
+ window and be resized to fit into the container.
+
+ The \a id should be the ID of a window controlled by an XEmbed
+ enabled application, but this is not mandatory. If \a id does not
+ belong to an XEmbed client widget, then focus handling,
+ activation, accelerators and other features will not work
+ properly.
+*/
+void QX11EmbedContainer::embedClient(WId id)
+{
+ Q_D(QX11EmbedContainer);
+
+ if (id == 0) {
+ d->emitError(InvalidWindowID);
+ return;
+ }
+
+ // Walk up the tree of parent windows to prevent embedding of ancestors.
+ WId thisId = internalWinId();
+ Window rootReturn;
+ Window parentReturn;
+ Window *childrenReturn = 0;
+ unsigned int nchildrenReturn;
+ do {
+ if (XQueryTree(x11Info().display(), thisId, &rootReturn,
+ &parentReturn, &childrenReturn, &nchildrenReturn) == 0) {
+ d->emitError(InvalidWindowID);
+ return;
+ }
+ if (childrenReturn) {
+ XFree(childrenReturn);
+ childrenReturn = 0;
+ }
+
+ thisId = parentReturn;
+ if (id == thisId) {
+ d->emitError(InvalidWindowID);
+ return;
+ }
+ } while (thisId != rootReturn);
+
+ // watch for property notify events (see below)
+ XGrabServer(x11Info().display());
+ XWindowAttributes attrib;
+ if (!XGetWindowAttributes(x11Info().display(), id, &attrib)) {
+ XUngrabServer(x11Info().display());
+ d->emitError(InvalidWindowID);
+ return;
+ }
+ XSelectInput(x11Info().display(), id, attrib.your_event_mask | PropertyChangeMask | StructureNotifyMask);
+ XUngrabServer(x11Info().display());
+
+ // Put the window into WithdrawnState
+ XUnmapWindow(x11Info().display(), id);
+ XSync(x11Info().display(), False); // make sure the window is hidden
+
+ /*
+ Wait for notification from the window manager that the window is
+ in withdrawn state. According to the ICCCM section 4.1.3.1,
+ we should wait for the WM_STATE property to either be deleted or
+ set to WithdrawnState.
+
+ For safety, we will not wait more than 500 ms, so that we can
+ preemptively workaround buggy window managers.
+ */
+ QTime t;
+ t.start();
+
+ functorData data;
+ data.id = id;
+ data.rootWindow = attrib.root;
+ data.clearedWmState = false;
+ data.reparentedToRoot = false;
+
+ do {
+ if (t.elapsed() > 500) // time-out after 500 ms
+ break;
+
+ XEvent event;
+ if (!XCheckIfEvent(x11Info().display(), &event, functor, (XPointer) &data)) {
+ XSync(x11Info().display(), False);
+ usleep(50000);
+ continue;
+ }
+
+ qApp->x11ProcessEvent(&event);
+ } while (!data.clearedWmState || !data.reparentedToRoot);
+
+ // restore the event mask
+ XSelectInput(x11Info().display(), id, attrib.your_event_mask);
+
+ switch (XReparentWindow(x11Info().display(), id, internalWinId(), 0, 0)) {
+ case BadWindow:
+ case BadMatch:
+ d->emitError(InvalidWindowID);
+ break;
+ default:
+ break;
+ }
+}
+
+/*! \internal
+
+ Handles key, activation and focus events for the container.
+*/
+bool QX11EmbedContainer::eventFilter(QObject *o, QEvent *event)
+{
+ Q_D(QX11EmbedContainer);
+ switch (event->type()) {
+ case QEvent::KeyPress:
+ // Forward any keypresses to our client.
+ if (o == this && d->client) {
+ lastKeyEvent.window = d->client;
+ XSendEvent(x11Info().display(), d->client, false, KeyPressMask, (XEvent *) &lastKeyEvent);
+ return true;
+ }
+ break;
+ case QEvent::KeyRelease:
+ // Forward any keyreleases to our client.
+ if (o == this && d->client) {
+ lastKeyEvent.window = d->client;
+ XSendEvent(x11Info().display(), d->client, false, KeyReleaseMask, (XEvent *) &lastKeyEvent);
+ return true;
+ }
+ break;
+
+ case QEvent::WindowActivate:
+ // When our container window is activated, we pass the
+ // activation message on to our client. Note that X input
+ // focus is set to our focus proxy. We want to intercept all
+ // keypresses.
+ if (o == window() && d->client) {
+ if (!d->isEmbedded() && d->activeContainer == this)
+ d->moveInputToProxy();
+
+ if (d->clientIsXEmbed) {
+ sendXEmbedMessage(d->client, x11Info().display(), XEMBED_WINDOW_ACTIVATE);
+ } else {
+ d->checkGrab();
+ if (hasFocus())
+ XSetInputFocus(x11Info().display(), d->client, XRevertToParent, x11Time());
+ }
+ }
+ break;
+ case QEvent::WindowDeactivate:
+ // When our container window is deactivated, we pass the
+ // deactivation message to our client.
+ if (o == window() && d->client) {
+ if (d->clientIsXEmbed)
+ sendXEmbedMessage(d->client, x11Info().display(), XEMBED_WINDOW_DEACTIVATE);
+ else
+ d->checkGrab();
+ }
+ break;
+ case QEvent::FocusIn:
+ // When receiving FocusIn events generated by Tab or Backtab,
+ // we pass focus on to our client. Any mouse activity is sent
+ // directly to the client, and it will ask us for focus with
+ // XEMBED_REQUEST_FOCUS.
+ if (o == this && d->client) {
+ if (!d->isEmbedded())
+ d->activeContainer = this;
+
+ if (d->clientIsXEmbed) {
+ if (!d->isEmbedded())
+ d->moveInputToProxy();
+
+ QFocusEvent *fe = (QFocusEvent *)event;
+ switch (fe->reason()) {
+ case Qt::TabFocusReason:
+ sendXEmbedMessage(d->client, x11Info().display(), XEMBED_FOCUS_IN, XEMBED_FOCUS_FIRST);
+ break;
+ case Qt::BacktabFocusReason:
+ sendXEmbedMessage(d->client, x11Info().display(), XEMBED_FOCUS_IN, XEMBED_FOCUS_LAST);
+ break;
+ default:
+ sendXEmbedMessage(d->client, x11Info().display(), XEMBED_FOCUS_IN, XEMBED_FOCUS_CURRENT);
+ break;
+ }
+ } else {
+ d->checkGrab();
+ XSetInputFocus(x11Info().display(), d->client, XRevertToParent, x11Time());
+ }
+ }
+
+ break;
+ case QEvent::FocusOut: {
+ // When receiving a FocusOut, we ask our client to remove its
+ // focus.
+ if (o == this && d->client) {
+ if (!d->isEmbedded()) {
+ d->activeContainer = 0;
+ if (isActiveWindow())
+ d->moveInputToProxy();
+ }
+
+ if (d->clientIsXEmbed) {
+ QFocusEvent *fe = (QFocusEvent *)event;
+ if (o == this && d->client && fe->reason() != Qt::ActiveWindowFocusReason)
+ sendXEmbedMessage(d->client, x11Info().display(), XEMBED_FOCUS_OUT);
+ } else {
+ d->checkGrab();
+ }
+ }
+ }
+ break;
+
+ case QEvent::Close: {
+ if (o == this && d->client) {
+ // Unmap the client and reparent it to the root window.
+ // Wait until the messages have been processed. Then ask
+ // the window manager to delete the window.
+ XUnmapWindow(x11Info().display(), d->client);
+ XReparentWindow(x11Info().display(), d->client, x11Info().appRootWindow(), 0, 0);
+ XSync(x11Info().display(), false);
+
+ XEvent ev;
+ memset(&ev, 0, sizeof(ev));
+ ev.xclient.type = ClientMessage;
+ ev.xclient.window = d->client;
+ ev.xclient.message_type = ATOM(WM_PROTOCOLS);
+ ev.xclient.format = 32;
+ ev.xclient.data.s[0] = ATOM(WM_DELETE_WINDOW);
+ XSendEvent(x11Info().display(), d->client, false, NoEventMask, &ev);
+
+ XFlush(x11Info().display());
+ d->client = 0;
+ d->clientIsXEmbed = false;
+ d->wmMinimumSizeHint = QSize();
+ updateGeometry();
+ setEnabled(false);
+ update();
+
+ emit clientClosed();
+ }
+ }
+ default:
+ break;
+ }
+
+ return QWidget::eventFilter(o, event);
+}
+
+/*! \internal
+
+ Handles X11 events for the container.
+*/
+bool QX11EmbedContainer::x11Event(XEvent *event)
+{
+ Q_D(QX11EmbedContainer);
+
+ switch (event->type) {
+ case CreateNotify:
+ // The client created an embedded window.
+ if (d->client)
+ d->rejectClient(event->xcreatewindow.window);
+ else
+ d->acceptClient(event->xcreatewindow.window);
+ break;
+ case DestroyNotify:
+ if (event->xdestroywindow.window == d->client) {
+ // The client died.
+ d->client = 0;
+ d->clientIsXEmbed = false;
+ d->wmMinimumSizeHint = QSize();
+ updateGeometry();
+ update();
+ setEnabled(false);
+ emit clientClosed();
+ }
+ break;
+ case ReparentNotify:
+ // The client sends us this if it reparents itself out of our
+ // widget.
+ if (event->xreparent.window == d->client && event->xreparent.parent != internalWinId()) {
+ d->client = 0;
+ d->clientIsXEmbed = false;
+ d->wmMinimumSizeHint = QSize();
+ updateGeometry();
+ update();
+ setEnabled(false);
+ emit clientClosed();
+ } else if (event->xreparent.parent == internalWinId()) {
+ // The client reparented itself into this window.
+ if (d->client)
+ d->rejectClient(event->xreparent.window);
+ else
+ d->acceptClient(event->xreparent.window);
+ }
+ break;
+ case ClientMessage: {
+ if (event->xclient.message_type == ATOM(_XEMBED)) {
+ // Ignore XEMBED messages not to ourselves
+ if (event->xclient.window != internalWinId())
+ break;
+
+ // Receiving an XEmbed message means the client
+ // is an XEmbed client.
+ d->clientIsXEmbed = true;
+
+ Time msgtime = (Time) event->xclient.data.l[0];
+ if (msgtime > X11->time)
+ X11->time = msgtime;
+
+ switch (event->xclient.data.l[1]) {
+ case XEMBED_REQUEST_FOCUS: {
+ // This typically happens when the client gets focus
+ // because of a mouse click.
+ if (!hasFocus())
+ setFocus(Qt::OtherFocusReason);
+
+ // The message is passed along to the topmost container
+ // that eventually responds with a XEMBED_FOCUS_IN
+ // message. The focus in message is passed all the way
+ // back until it reaches the original focus
+ // requestor. In the end, not only the original client
+ // has focus, but also all its ancestor containers.
+ if (d->isEmbedded()) {
+ // If our window's embedded flag is set, then
+ // that suggests that we are part of a client. The
+ // parentWinId will then point to an container to whom
+ // we must pass this message.
+ sendXEmbedMessage(d->topLevelParentWinId(), x11Info().display(), XEMBED_REQUEST_FOCUS);
+ } else {
+ // Our window's embedded flag is not set,
+ // so we are the topmost container. We respond to
+ // the focus request message with a focus in
+ // message. This message will pass on from client
+ // to container to client until it reaches the
+ // originator of the XEMBED_REQUEST_FOCUS message.
+ sendXEmbedMessage(d->client, x11Info().display(), XEMBED_FOCUS_IN, XEMBED_FOCUS_CURRENT);
+ }
+
+ break;
+ }
+ case XEMBED_FOCUS_NEXT:
+ // Client sends this event when it received a tab
+ // forward and was at the end of its focus chain. If
+ // we are the only widget in the focus chain, we send
+ // ourselves a FocusIn event.
+ if (d->focus_next != this) {
+ focusNextPrevChild(true);
+ } else {
+ QFocusEvent event(QEvent::FocusIn, Qt::TabFocusReason);
+ qApp->sendEvent(this, &event);
+ }
+
+ break;
+ case XEMBED_FOCUS_PREV:
+ // Client sends this event when it received a backtab
+ // and was at the start of its focus chain. If we are
+ // the only widget in the focus chain, we send
+ // ourselves a FocusIn event.
+ if (d->focus_next != this) {
+ focusNextPrevChild(false);
+ } else {
+ QFocusEvent event(QEvent::FocusIn, Qt::BacktabFocusReason);
+ qApp->sendEvent(this, &event);
+ }
+
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ break;
+ case XButtonPress:
+ if (!d->clientIsXEmbed) {
+ setFocus(Qt::MouseFocusReason);
+ XAllowEvents(x11Info().display(), ReplayPointer, CurrentTime);
+ return true;
+ }
+ break;
+ case XButtonRelease:
+ if (!d->clientIsXEmbed)
+ XAllowEvents(x11Info().display(), SyncPointer, CurrentTime);
+ break;
+ default:
+ break;
+ }
+
+ return QWidget::x11Event(event);
+}
+
+/*! \internal
+
+ Whenever the container is resized, we need to resize our client.
+*/
+void QX11EmbedContainer::resizeEvent(QResizeEvent *)
+{
+ Q_D(QX11EmbedContainer);
+ if (d->client)
+ XResizeWindow(x11Info().display(), d->client, width(), height());
+}
+
+/*! \internal
+
+ We use the QShowEvent to signal to our client that we want it to
+ map itself. We do this by changing its window property
+ XEMBED_INFO. The client will get an X11 PropertyNotify.
+*/
+void QX11EmbedContainer::showEvent(QShowEvent *)
+{
+ Q_D(QX11EmbedContainer);
+ if (d->client) {
+ unsigned int data[] = {XEMBED_VERSION, XEMBED_MAPPED};
+ XChangeProperty(x11Info().display(), d->client, ATOM(_XEMBED_INFO), ATOM(_XEMBED_INFO), 32,
+ PropModeReplace, (unsigned char *) data, 2);
+ }
+}
+
+/*! \internal
+
+ We use the QHideEvent to signal to our client that we want it to
+ unmap itself. We do this by changing its window property
+ XEMBED_INFO. The client will get an X11 PropertyNotify.
+*/
+void QX11EmbedContainer::hideEvent(QHideEvent *)
+{
+ Q_D(QX11EmbedContainer);
+ if (d->client) {
+ unsigned int data[] = {XEMBED_VERSION, XEMBED_MAPPED};
+
+ XChangeProperty(x11Info().display(), d->client, ATOM(_XEMBED_INFO), ATOM(_XEMBED_INFO), 32,
+ PropModeReplace, (unsigned char *) data, 2);
+ }
+}
+
+/*!
+ \reimp
+*/
+bool QX11EmbedContainer::event(QEvent *event)
+{
+ if (event->type() == QEvent::ParentChange) {
+ XSelectInput(x11Info().display(), internalWinId(),
+ KeyPressMask | KeyReleaseMask
+ | ButtonPressMask | ButtonReleaseMask | ButtonMotionMask
+ | KeymapStateMask
+ | PointerMotionMask
+ | EnterWindowMask | LeaveWindowMask
+ | FocusChangeMask
+ | ExposureMask
+ | StructureNotifyMask
+ | SubstructureNotifyMask);
+ }
+ return QWidget::event(event);
+}
+
+/*! \internal
+
+ Rejects a client window by reparenting it to the root window. The
+ client will receive a reparentnotify, and will most likely assume
+ that the container has shut down. The XEmbed protocol does not
+ define any way to reject a client window, but this is a clean way
+ to do it.
+*/
+void QX11EmbedContainerPrivate::rejectClient(WId window)
+{
+ Q_Q(QX11EmbedContainer);
+ q->setEnabled(false);
+ XRemoveFromSaveSet(q->x11Info().display(), client);
+ XReparentWindow(q->x11Info().display(), window, q->x11Info().appRootWindow(), 0, 0);
+}
+
+/*! \internal
+
+ Accepts a client by mapping it, resizing it and optionally
+ activating and giving it logical focusing through XEMBED messages.
+*/
+void QX11EmbedContainerPrivate::acceptClient(WId window)
+{
+ Q_Q(QX11EmbedContainer);
+ client = window;
+ q->setEnabled(true);
+
+ // This tells Qt that we wish to forward DnD messages to
+ // our client.
+ if (!extra)
+ createExtra();
+ extraData()->xDndProxy = client;
+
+ unsigned int version = XEmbedVersion();
+
+ Atom actual_type_return;
+ int actual_format_return;
+ unsigned long nitems_return = 0;
+ unsigned long bytes_after_return;
+ unsigned char *prop_return = 0;
+ unsigned int clientversion = 0;
+
+ // Add this client to our saveset, so if we crash, the client window
+ // doesn't get destroyed. This is useful for containers that restart
+ // automatically after a crash, because it can simply reembed its clients
+ // without having to restart them (KDE panel).
+ XAddToSaveSet(q->x11Info().display(), client);
+
+ // XEmbed clients have an _XEMBED_INFO property in which we can
+ // fetch the version
+ if (XGetWindowProperty(q->x11Info().display(), client, ATOM(_XEMBED_INFO), 0, 2, false,
+ ATOM(_XEMBED_INFO), &actual_type_return, &actual_format_return,
+ &nitems_return, &bytes_after_return, &prop_return) == Success) {
+
+ if (actual_type_return != None && actual_format_return != 0) {
+ // Clients with the _XEMBED_INFO property are XEMBED clients.
+ clientIsXEmbed = true;
+
+ unsigned int *p = (unsigned int *)prop_return;
+ if (nitems_return >= 2)
+ clientversion = p[0];
+ }
+
+ XFree(prop_return);
+ }
+
+ // Store client window's original size and placement.
+ Window root;
+ int x_return, y_return;
+ unsigned int width_return, height_return, border_width_return, depth_return;
+ XGetGeometry(q->x11Info().display(), client, &root, &x_return, &y_return,
+ &width_return, &height_return, &border_width_return, &depth_return);
+ clientOriginalRect.setCoords(x_return, y_return,
+ x_return + width_return - 1,
+ y_return + height_return - 1);
+
+ // Ask the client for its minimum size.
+ XSizeHints size;
+ long msize;
+ if (XGetWMNormalHints(q->x11Info().display(), client, &size, &msize) && (size.flags & PMinSize)) {
+ wmMinimumSizeHint = QSize(size.min_width, size.min_height);
+ q->updateGeometry();
+ }
+
+ // The container should set the data2 field to the lowest of its
+ // supported version number and that of the client (from
+ // _XEMBED_INFO property).
+ unsigned int minversion = version > clientversion ? clientversion : version;
+ sendXEmbedMessage(client, q->x11Info().display(), XEMBED_EMBEDDED_NOTIFY, q->internalWinId(), minversion);
+ XMapWindow(q->x11Info().display(), client);
+
+ // Resize it, but no smaller than its minimum size hint.
+ XResizeWindow(q->x11Info().display(),
+ client,
+ qMax(q->width(), wmMinimumSizeHint.width()),
+ qMax(q->height(), wmMinimumSizeHint.height()));
+ q->update();
+
+ // Not mentioned in the protocol is that if the container
+ // is already active, the client must be activated to work
+ // properly.
+ if (q->window()->isActiveWindow())
+ sendXEmbedMessage(client, q->x11Info().display(), XEMBED_WINDOW_ACTIVATE);
+
+ // Also, if the container already has focus, then it must
+ // send a focus in message to its new client; otherwise we ask
+ // it to remove focus.
+ if (q->focusWidget() == q && q->hasFocus())
+ sendXEmbedMessage(client, q->x11Info().display(), XEMBED_FOCUS_IN, XEMBED_FOCUS_FIRST);
+ else
+ sendXEmbedMessage(client, q->x11Info().display(), XEMBED_FOCUS_OUT);
+
+ if (!clientIsXEmbed) {
+ checkGrab();
+ if (q->hasFocus()) {
+ XSetInputFocus(q->x11Info().display(), client, XRevertToParent, x11Time());
+ } else {
+ if (!isEmbedded())
+ moveInputToProxy();
+ }
+ }
+
+ emit q->clientIsEmbedded();
+}
+
+/*! \internal
+
+ Moves X11 keyboard input focus to the focusProxy, unless the focus
+ is there already. When X11 keyboard input focus is on the
+ focusProxy, which is a child of the container and a sibling of the
+ client, X11 keypresses and keyreleases will always go to the proxy
+ and not to the client.
+*/
+void QX11EmbedContainerPrivate::moveInputToProxy()
+{
+ Q_Q(QX11EmbedContainer);
+ WId focus;
+ int revert_to;
+ XGetInputFocus(q->x11Info().display(), &focus, &revert_to);
+ if (focus != focusProxy->internalWinId())
+ XSetInputFocus(q->x11Info().display(), focusProxy->internalWinId(), XRevertToParent, x11Time());
+}
+
+/*! \internal
+
+ Ask the window manager to give us a default minimum size.
+*/
+QSize QX11EmbedContainer::minimumSizeHint() const
+{
+ Q_D(const QX11EmbedContainer);
+ if (!d->client || !d->wmMinimumSizeHint.isValid())
+ return QWidget::minimumSizeHint();
+ return d->wmMinimumSizeHint;
+}
+
+/*! \internal
+
+*/
+void QX11EmbedContainerPrivate::checkGrab()
+{
+ Q_Q(QX11EmbedContainer);
+ if (!clientIsXEmbed && q->isActiveWindow() && !q->hasFocus()) {
+ if (!xgrab) {
+ XGrabButton(q->x11Info().display(), AnyButton, AnyModifier, q->internalWinId(),
+ true, ButtonPressMask, GrabModeSync, GrabModeAsync,
+ None, None);
+ }
+ xgrab = true;
+ } else {
+ if (xgrab)
+ XUngrabButton(q->x11Info().display(), AnyButton, AnyModifier, q->internalWinId());
+ xgrab = false;
+ }
+}
+
+/*!
+ Detaches the client from the embedder. The client will appear as a
+ standalone window on the desktop.
+*/
+void QX11EmbedContainer::discardClient()
+{
+ Q_D(QX11EmbedContainer);
+ if (d->client) {
+ XResizeWindow(x11Info().display(), d->client, d->clientOriginalRect.width(),
+ d->clientOriginalRect.height());
+
+ d->rejectClient(d->client);
+ }
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/kernel/qx11embed_x11.h b/src/gui/kernel/qx11embed_x11.h
new file mode 100644
index 0000000000..7c8ab9cc73
--- /dev/null
+++ b/src/gui/kernel/qx11embed_x11.h
@@ -0,0 +1,132 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QX11EMBED_X11_H
+#define QX11EMBED_X11_H
+
+#include <QtGui/qwidget.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+class QX11EmbedWidgetPrivate;
+class Q_GUI_EXPORT QX11EmbedWidget : public QWidget
+{
+ Q_OBJECT
+public:
+ QX11EmbedWidget(QWidget *parent = 0);
+ ~QX11EmbedWidget();
+
+ void embedInto(WId id);
+ WId containerWinId() const;
+
+ enum Error {
+ Unknown,
+ Internal,
+ InvalidWindowID
+ };
+ Error error() const;
+
+Q_SIGNALS:
+ void embedded();
+ void containerClosed();
+ void error(QX11EmbedWidget::Error error);
+
+protected:
+ bool x11Event(XEvent *);
+ bool eventFilter(QObject *, QEvent *);
+ bool event(QEvent *);
+ void resizeEvent(QResizeEvent *);
+
+private:
+ Q_DECLARE_PRIVATE(QX11EmbedWidget)
+ Q_DISABLE_COPY(QX11EmbedWidget)
+};
+
+class QX11EmbedContainerPrivate;
+class Q_GUI_EXPORT QX11EmbedContainer : public QWidget
+{
+ Q_OBJECT
+public:
+ QX11EmbedContainer(QWidget *parent = 0);
+ ~QX11EmbedContainer();
+
+ void embedClient(WId id);
+ void discardClient();
+
+ WId clientWinId() const;
+
+ QSize minimumSizeHint() const;
+
+ enum Error {
+ Unknown,
+ Internal,
+ InvalidWindowID
+ };
+ Error error() const;
+
+Q_SIGNALS:
+ void clientIsEmbedded();
+ void clientClosed();
+ void error(QX11EmbedContainer::Error);
+
+protected:
+ bool x11Event(XEvent *);
+ bool eventFilter(QObject *, QEvent *);
+ void paintEvent(QPaintEvent *e);
+ void resizeEvent(QResizeEvent *);
+ void showEvent(QShowEvent *);
+ void hideEvent(QHideEvent *);
+ bool event(QEvent *);
+
+private:
+ Q_DECLARE_PRIVATE(QX11EmbedContainer)
+ Q_DISABLE_COPY(QX11EmbedContainer)
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QX11EMBED_X11_H
diff --git a/src/gui/kernel/qx11info_x11.cpp b/src/gui/kernel/qx11info_x11.cpp
new file mode 100644
index 0000000000..d8a5c5e310
--- /dev/null
+++ b/src/gui/kernel/qx11info_x11.cpp
@@ -0,0 +1,542 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qwidget.h"
+#include "qpixmap.h"
+#include "qx11info_x11.h"
+#include "qt_x11_p.h"
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QX11Info
+ \brief The QX11Info class provides information about the X display
+ configuration.
+
+ \ingroup shared
+
+ The class provides two APIs: a set of non-static functions that
+ provide information about a specific widget or pixmap, and a set
+ of static functions that provide the default information for the
+ application.
+
+ \warning This class is only available on X11. For querying
+ per-screen information in a portable way, use QDesktopWidget.
+
+ \sa QWidget::x11Info(), QPixmap::x11Info(), QDesktopWidget
+*/
+
+/*!
+ Constructs an empty QX11Info object.
+*/
+QX11Info::QX11Info()
+ : x11data(0)
+{
+}
+
+/*!
+ Constructs a copy of \a other.
+*/
+QX11Info::QX11Info(const QX11Info &other)
+{
+ x11data = other.x11data;
+ if (x11data)
+ ++x11data->ref;
+}
+
+/*!
+ Assigns \a other to this object and returns a reference to this
+ object.
+*/
+QX11Info &QX11Info::operator=(const QX11Info &other)
+{
+ if (other.x11data)
+ ++other.x11data->ref;
+ if (x11data && !--x11data->ref)
+ delete x11data;
+ x11data = other.x11data;
+ return *this;
+}
+
+/*!
+ Destroys the QX11Info object.
+*/
+QX11Info::~QX11Info()
+{
+ if (x11data && !--x11data->ref)
+ delete x11data;
+}
+
+/*!
+ \internal
+ Makes a shallow copy of the X11-specific data of \a fromDevice, if it is not
+ null. Otherwise this function sets it to null.
+*/
+
+void QX11Info::copyX11Data(const QPaintDevice *fromDevice)
+{
+ QX11InfoData *xd = 0;
+ if (fromDevice) {
+ if (fromDevice->devType() == QInternal::Widget)
+ xd = static_cast<const QWidget *>(fromDevice)->x11Info().x11data;
+ else if (fromDevice->devType() == QInternal::Pixmap)
+ xd = static_cast<const QPixmap *>(fromDevice)->x11Info().x11data;
+ }
+ setX11Data(xd);
+}
+
+/*!
+ \internal
+ Makes a deep copy of the X11-specific data of \a fromDevice, if it is not
+ null. Otherwise this function sets it to null.
+*/
+
+void QX11Info::cloneX11Data(const QPaintDevice *fromDevice)
+{
+ QX11InfoData *d = 0;
+ if (fromDevice) {
+ QX11InfoData *xd;
+ if (fromDevice->devType() == QInternal::Widget) {
+ xd = static_cast<const QWidget *>(fromDevice)->x11Info().x11data;
+ } else {
+ Q_ASSERT(fromDevice->devType() == QInternal::Pixmap);
+ xd = static_cast<const QPixmap *>(fromDevice)->x11Info().x11data;
+ }
+ d = new QX11InfoData(*xd);
+ d->ref = 0;
+ }
+ setX11Data(d);
+}
+
+/*!
+ \internal
+ Makes a shallow copy of the X11-specific data \a d and assigns it to this
+ class. This function increments the reference code of \a d.
+*/
+
+void QX11Info::setX11Data(const QX11InfoData* d)
+{
+ if (x11data && !--x11data->ref)
+ delete x11data;
+ x11data = (QX11InfoData *)d;
+ if (x11data)
+ ++x11data->ref;
+}
+
+
+/*!
+ \internal
+ If \a def is false, returns a deep copy of the x11Data, or 0 if x11Data is 0.
+ If \a def is true, makes a QX11Data struct filled with the default
+ values.
+
+ In either case the caller is responsible for deleting the returned
+ struct. But notice that the struct is a shared class, so other
+ classes might also have a reference to it. The reference count of
+ the returned QX11Data* is 0.
+*/
+
+QX11InfoData* QX11Info::getX11Data(bool def) const
+{
+ QX11InfoData* res = 0;
+ if (def) {
+ res = new QX11InfoData;
+ res->screen = appScreen();
+ res->depth = appDepth();
+ res->cells = appCells();
+ res->colormap = colormap();
+ res->defaultColormap = appDefaultColormap();
+ res->visual = (Visual*) appVisual();
+ res->defaultVisual = appDefaultVisual();
+ } else if (x11data) {
+ res = new QX11InfoData;
+ *res = *x11data;
+ }
+ res->ref = 0;
+ return res;
+}
+
+/*!
+ Returns the horizontal resolution of the given \a screen in terms of the
+ number of dots per inch.
+
+ The \a screen argument is an X screen number. Be aware that if
+ the user's system uses Xinerama (as opposed to traditional X11
+ multiscreen), there is only one X screen. Use QDesktopWidget to
+ query for information about Xinerama screens.
+
+ \sa setAppDpiX(), appDpiY()
+*/
+int QX11Info::appDpiX(int screen)
+{
+ if (!X11)
+ return 75;
+ if (screen < 0)
+ screen = X11->defaultScreen;
+ if (screen > X11->screenCount)
+ return 0;
+ return X11->screens[screen].dpiX;
+}
+
+/*!
+ Sets the horizontal resolution of the given \a screen to the number of
+ dots per inch specified by \a xdpi.
+
+ The \a screen argument is an X screen number. Be aware that if
+ the user's system uses Xinerama (as opposed to traditional X11
+ multiscreen), there is only one X screen. Use QDesktopWidget to
+ query for information about Xinerama screens.
+
+ \sa appDpiX(), setAppDpiY()
+*/
+
+void QX11Info::setAppDpiX(int screen, int xdpi)
+{
+ if (!X11)
+ return;
+ if (screen < 0)
+ screen = X11->defaultScreen;
+ if (screen > X11->screenCount)
+ return;
+ X11->screens[screen].dpiX = xdpi;
+}
+
+/*!
+ Returns the vertical resolution of the given \a screen in terms of the
+ number of dots per inch.
+
+ The \a screen argument is an X screen number. Be aware that if
+ the user's system uses Xinerama (as opposed to traditional X11
+ multiscreen), there is only one X screen. Use QDesktopWidget to
+ query for information about Xinerama screens.
+
+ \sa setAppDpiY(), appDpiX()
+*/
+
+int QX11Info::appDpiY(int screen)
+{
+ if (!X11)
+ return 75;
+ if (screen < 0)
+ screen = X11->defaultScreen;
+ if (screen > X11->screenCount)
+ return 0;
+ return X11->screens[screen].dpiY;
+}
+
+/*!
+ Sets the vertical resolution of the given \a screen to the number of
+ dots per inch specified by \a ydpi.
+
+ The \a screen argument is an X screen number. Be aware that if
+ the user's system uses Xinerama (as opposed to traditional X11
+ multiscreen), there is only one X screen. Use QDesktopWidget to
+ query for information about Xinerama screens.
+
+ \sa appDpiY(), setAppDpiX()
+*/
+void QX11Info::setAppDpiY(int screen, int ydpi)
+{
+ if (!X11)
+ return;
+ if (screen < 0)
+ screen = X11->defaultScreen;
+ if (screen > X11->screenCount)
+ return;
+ X11->screens[screen].dpiY = ydpi;
+}
+
+/*!
+ Returns the X11 time.
+
+ \sa setAppTime(), appUserTime()
+*/
+unsigned long QX11Info::appTime()
+{
+ return X11 ? X11->time : 0;
+}
+
+/*!
+ Sets the X11 time to the value specified by \a time.
+
+ \sa appTime(), setAppUserTime()
+*/
+void QX11Info::setAppTime(unsigned long time)
+{
+ if (X11) {
+ X11->time = time;
+ }
+}
+
+/*!
+ Returns the X11 user time.
+
+ \sa setAppUserTime(), appTime()
+*/
+unsigned long QX11Info::appUserTime()
+{
+ return X11 ? X11->userTime : 0;
+}
+
+/*!
+ Sets the X11 user time as specified by \a time.
+
+ \sa appUserTime(), setAppTime()
+*/
+void QX11Info::setAppUserTime(unsigned long time)
+{
+ if (X11) {
+ X11->userTime = time;
+ }
+}
+
+
+/*!
+ \fn const char *QX11Info::appClass()
+
+ Returns the X11 application class.
+
+ \sa display()
+*/
+
+/*!
+ Returns the default display for the application.
+
+ \sa appScreen()
+*/
+
+Display *QX11Info::display()
+{
+ return X11 ? X11->display : 0;
+}
+
+/*!
+ Returns the number of the screen where the application is being
+ displayed.
+
+ \sa display(), screen()
+*/
+int QX11Info::appScreen()
+{
+ return X11 ? X11->defaultScreen : 0;
+}
+
+/*!
+ Returns a handle for the application's color map on the given \a screen.
+
+ The \a screen argument is an X screen number. Be aware that if
+ the user's system uses Xinerama (as opposed to traditional X11
+ multiscreen), there is only one X screen. Use QDesktopWidget to
+ query for information about Xinerama screens.
+
+ \sa colormap(), defaultColormap()
+*/
+Qt::HANDLE QX11Info::appColormap(int screen)
+{
+ return X11 ? X11->screens[screen == -1 ? X11->defaultScreen : screen].colormap : 0;
+}
+
+/*!
+ Returns the current visual used by the application on the given
+ \a screen.
+
+ The \a screen argument is an X screen number. Be aware that if
+ the user's system uses Xinerama (as opposed to traditional X11
+ multiscreen), there is only one X screen. Use QDesktopWidget to
+ query for information about Xinerama screens.
+
+ \sa visual(), defaultVisual()
+*/
+
+void *QX11Info::appVisual(int screen)
+{
+ return X11 ? X11->screens[screen == -1 ? X11->defaultScreen : screen].visual : 0;
+}
+
+/*!
+ Returns a handle for the applications root window on the given \a screen.
+
+ The \a screen argument is an X screen number. Be aware that if
+ the user's system uses Xinerama (as opposed to traditional X11
+ multiscreen), there is only one X screen. Use QDesktopWidget to
+ query for information about Xinerama screens.
+
+ \sa QApplication::desktop()
+*/
+Qt::HANDLE QX11Info::appRootWindow(int screen)
+{
+ return X11 ? RootWindow(X11->display, screen == -1 ? X11->defaultScreen : screen) : 0;
+}
+
+/*!
+ Returns the color depth (bits per pixel) used by the application on the
+ given \a screen.
+
+ The \a screen argument is an X screen number. Be aware that if
+ the user's system uses Xinerama (as opposed to traditional X11
+ multiscreen), there is only one X screen. Use QDesktopWidget to
+ query for information about Xinerama screens.
+
+ \sa depth()
+*/
+
+int QX11Info::appDepth(int screen)
+{
+ return X11 ? X11->screens[screen == -1 ? X11->defaultScreen : screen].depth : 32;
+}
+
+/*!
+ Returns the number of cells used by the application on the given \a screen.
+
+ The \a screen argument is an X screen number. Be aware that if
+ the user's system uses Xinerama (as opposed to traditional X11
+ multiscreen), there is only one X screen. Use QDesktopWidget to
+ query for information about Xinerama screens.
+
+ \sa cells()
+*/
+
+int QX11Info::appCells(int screen)
+{ return X11 ? X11->screens[screen == -1 ? X11->defaultScreen : screen].cells : 0; }
+
+/*!
+ Returns true if the application has a default color map on the given
+ \a screen; otherwise returns false.
+
+ The \a screen argument is an X screen number. Be aware that if
+ the user's system uses Xinerama (as opposed to traditional X11
+ multiscreen), there is only one X screen. Use QDesktopWidget to
+ query for information about Xinerama screens.
+*/
+bool QX11Info::appDefaultColormap(int screen)
+{ return X11 ? X11->screens[screen == -1 ? X11->defaultScreen : screen].defaultColormap : true; }
+
+/*!
+ Returns true if the application has a default visual on the given \a screen;
+ otherwise returns false.
+
+ The \a screen argument is an X screen number. Be aware that if
+ the user's system uses Xinerama (as opposed to traditional X11
+ multiscreen), there is only one X screen. Use QDesktopWidget to
+ query for information about Xinerama screens.
+*/
+bool QX11Info::appDefaultVisual(int screen)
+{ return X11 ? X11->screens[screen == -1 ? X11->defaultScreen : screen].defaultVisual : true; }
+
+/*!
+ Returns the number of the screen currently in use.
+
+ The return value is an X screen number. Be aware that if the
+ user's system uses Xinerama (as opposed to traditional X11
+ multiscreen), there is only one X screen. Use QDesktopWidget to
+ query for information about Xinerama screens.
+
+ \sa appScreen()
+*/
+int QX11Info::screen() const
+{ return x11data ? x11data->screen : QX11Info::appScreen(); }
+
+/*!
+ Returns the color depth (bits per pixel) of the X display.
+
+ \sa appDepth()
+*/
+
+int QX11Info::depth() const
+{ return x11data ? x11data->depth : QX11Info::appDepth(); }
+
+/*!
+ Returns the number of cells.
+
+ \sa appCells()
+*/
+
+int QX11Info::cells() const
+{ return x11data ? x11data->cells : QX11Info::appCells(); }
+
+/*!
+ Returns a handle for the color map.
+
+ \sa defaultColormap()
+*/
+
+Qt::HANDLE QX11Info::colormap() const
+{ return x11data ? x11data->colormap : QX11Info::appColormap(); }
+
+/*!
+ Returns true if there is a default color map; otherwise returns false.
+
+ \sa colormap()
+*/
+
+bool QX11Info::defaultColormap() const
+{ return x11data ? x11data->defaultColormap : QX11Info::appDefaultColormap(); }
+
+/*!
+ Returns the current visual.
+
+ \sa appVisual(), defaultVisual()
+*/
+
+void *QX11Info::visual() const
+{ return x11data ? x11data->visual : QX11Info::appVisual(); }
+
+/*!
+ Returns true if there is a default visual; otherwise returns false.
+
+ \sa visual(), appVisual()
+*/
+
+bool QX11Info::defaultVisual() const
+{ return x11data ? x11data->defaultVisual : QX11Info::appDefaultVisual(); }
+
+
+/*!
+ \since 4.4
+
+ Returns true if there is a compositing manager running.
+*/
+bool QX11Info::isCompositingManagerRunning()
+{
+ return X11 ? X11->compositingManagerRunning : false;
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/kernel/qx11info_x11.h b/src/gui/kernel/qx11info_x11.h
new file mode 100644
index 0000000000..cd1d9966ff
--- /dev/null
+++ b/src/gui/kernel/qx11info_x11.h
@@ -0,0 +1,123 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QX11INFO_X11_H
+#define QX11INFO_X11_H
+
+#include <QtCore/qnamespace.h>
+
+typedef struct _XDisplay Display;
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+struct QX11InfoData;
+class QX11Info;
+class QPaintDevice;
+class QApplicationPrivate;
+class QX11InfoPrivate;
+struct QX11WindowAttributes;
+
+void qt_x11_getX11InfoForWindow(QX11Info * xinfo, const QX11WindowAttributes &a);
+class Q_GUI_EXPORT QX11Info
+{
+public:
+ QX11Info();
+ ~QX11Info();
+ QX11Info(const QX11Info &other);
+ QX11Info &operator=(const QX11Info &other);
+
+ static Display *display();
+ static const char *appClass();
+ int screen() const;
+ int depth() const;
+ int cells() const;
+ Qt::HANDLE colormap() const;
+ bool defaultColormap() const;
+ void *visual() const;
+ bool defaultVisual() const;
+
+ static int appScreen();
+ static int appDepth(int screen = -1);
+ static int appCells(int screen = -1);
+ static Qt::HANDLE appColormap(int screen = -1);
+ static void *appVisual(int screen = -1);
+ static Qt::HANDLE appRootWindow(int screen = -1);
+ static bool appDefaultColormap(int screen = -1);
+ static bool appDefaultVisual(int screen = -1);
+ static int appDpiX(int screen = -1);
+ static int appDpiY(int screen = -1);
+ static void setAppDpiX(int screen, int dpi);
+ static void setAppDpiY(int screen, int dpi);
+ static unsigned long appTime();
+ static unsigned long appUserTime();
+ static void setAppTime(unsigned long time);
+ static void setAppUserTime(unsigned long time);
+ static bool isCompositingManagerRunning();
+
+protected:
+ void copyX11Data(const QPaintDevice *);
+ void cloneX11Data(const QPaintDevice *);
+ void setX11Data(const QX11InfoData *);
+ QX11InfoData* getX11Data(bool def = false) const;
+
+ QX11InfoData *x11data;
+
+ friend class QX11PaintEngine;
+ friend class QPixmap;
+ friend class QX11PixmapData;
+ friend class QWidget;
+ friend class QWidgetPrivate;
+ friend class QGLWidget;
+ friend void qt_init(QApplicationPrivate *priv, int, Display *display, Qt::HANDLE visual,
+ Qt::HANDLE colormap);
+ friend void qt_cleanup();
+ friend void qt_x11_getX11InfoForWindow(QX11Info * xinfo, const QX11WindowAttributes &a);
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QX11INFO_X11_H
diff --git a/src/gui/kernel/win.pri b/src/gui/kernel/win.pri
new file mode 100644
index 0000000000..5ecf4dd94a
--- /dev/null
+++ b/src/gui/kernel/win.pri
@@ -0,0 +1,4 @@
+# Qt/Windows only configuration file
+# --------------------------------------------------------------------
+
+ INCLUDEPATH += ../3rdparty/wintab
diff --git a/src/gui/kernel/x11.pri b/src/gui/kernel/x11.pri
new file mode 100644
index 0000000000..ac40f69c63
--- /dev/null
+++ b/src/gui/kernel/x11.pri
@@ -0,0 +1,4 @@
+x11 {
+ contains(QT_CONFIG, nas): LIBS += -laudio -lXt
+}
+
diff --git a/src/gui/mac/images/copyarrowcursor.png b/src/gui/mac/images/copyarrowcursor.png
new file mode 100644
index 0000000000..13dfca95bc
--- /dev/null
+++ b/src/gui/mac/images/copyarrowcursor.png
Binary files differ
diff --git a/src/gui/mac/images/forbiddencursor.png b/src/gui/mac/images/forbiddencursor.png
new file mode 100644
index 0000000000..a9f21b4a5e
--- /dev/null
+++ b/src/gui/mac/images/forbiddencursor.png
Binary files differ
diff --git a/src/gui/mac/images/pluscursor.png b/src/gui/mac/images/pluscursor.png
new file mode 100644
index 0000000000..c583c088c9
--- /dev/null
+++ b/src/gui/mac/images/pluscursor.png
Binary files differ
diff --git a/src/gui/mac/images/spincursor.png b/src/gui/mac/images/spincursor.png
new file mode 100644
index 0000000000..ca44ab50fd
--- /dev/null
+++ b/src/gui/mac/images/spincursor.png
Binary files differ
diff --git a/src/gui/mac/images/waitcursor.png b/src/gui/mac/images/waitcursor.png
new file mode 100644
index 0000000000..a9abe61320
--- /dev/null
+++ b/src/gui/mac/images/waitcursor.png
Binary files differ
diff --git a/src/gui/mac/maccursors.qrc b/src/gui/mac/maccursors.qrc
new file mode 100644
index 0000000000..d80a63b464
--- /dev/null
+++ b/src/gui/mac/maccursors.qrc
@@ -0,0 +1,9 @@
+<!DOCTYPE RCC><RCC version="1.0">
+<qresource prefix="/trolltech/mac/cursors">
+<file>images/copyarrowcursor.png</file>
+<file>images/forbiddencursor.png</file>
+<file>images/spincursor.png</file>
+<file>images/waitcursor.png</file>
+<file>images/pluscursor.png</file>
+</qresource>
+</RCC>
diff --git a/src/gui/mac/qt_menu.nib/classes.nib b/src/gui/mac/qt_menu.nib/classes.nib
new file mode 100644
index 0000000000..fed50a386c
--- /dev/null
+++ b/src/gui/mac/qt_menu.nib/classes.nib
@@ -0,0 +1,59 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>IBClasses</key>
+ <array>
+ <dict>
+ <key>CLASS</key>
+ <string>FirstResponder</string>
+ <key>LANGUAGE</key>
+ <string>ObjC</string>
+ <key>SUPERCLASS</key>
+ <string>NSObject</string>
+ </dict>
+ <dict>
+ <key>ACTIONS</key>
+ <dict>
+ <key>hide</key>
+ <string>id</string>
+ <key>hideOtherApplications</key>
+ <string>id</string>
+ <key>orderFrontStandardAboutPanel</key>
+ <string>id</string>
+ <key>qtDispatcherToQAction</key>
+ <string>id</string>
+ <key>terminate</key>
+ <string>id</string>
+ <key>unhideAllApplications</key>
+ <string>id</string>
+ </dict>
+ <key>CLASS</key>
+ <string>QCocoaMenuLoader</string>
+ <key>LANGUAGE</key>
+ <string>ObjC</string>
+ <key>OUTLETS</key>
+ <dict>
+ <key>aboutItem</key>
+ <string>NSMenuItem</string>
+ <key>aboutQtItem</key>
+ <string>NSMenuItem</string>
+ <key>appMenu</key>
+ <string>NSMenu</string>
+ <key>hideItem</key>
+ <string>NSMenuItem</string>
+ <key>preferencesItem</key>
+ <string>NSMenuItem</string>
+ <key>quitItem</key>
+ <string>NSMenuItem</string>
+ <key>theMenu</key>
+ <string>NSMenu</string>
+ </dict>
+ <key>SUPERCLASS</key>
+ <string>NSResponder</string>
+ </dict>
+ </array>
+ <key>IBVersion</key>
+ <string>1</string>
+</dict>
+</plist>
diff --git a/src/gui/mac/qt_menu.nib/info.nib b/src/gui/mac/qt_menu.nib/info.nib
new file mode 100644
index 0000000000..768cb8b9f1
--- /dev/null
+++ b/src/gui/mac/qt_menu.nib/info.nib
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>IBFramework Version</key>
+ <string>670</string>
+ <key>IBOldestOS</key>
+ <integer>5</integer>
+ <key>IBOpenObjects</key>
+ <array>
+ <integer>57</integer>
+ </array>
+ <key>IBSystem Version</key>
+ <string>9G55</string>
+ <key>targetFramework</key>
+ <string>IBCocoaFramework</string>
+</dict>
+</plist>
diff --git a/src/gui/mac/qt_menu.nib/keyedobjects.nib b/src/gui/mac/qt_menu.nib/keyedobjects.nib
new file mode 100644
index 0000000000..18a66488f5
--- /dev/null
+++ b/src/gui/mac/qt_menu.nib/keyedobjects.nib
Binary files differ
diff --git a/src/gui/painting/makepsheader.pl b/src/gui/painting/makepsheader.pl
new file mode 100755
index 0000000000..30a5eead5b
--- /dev/null
+++ b/src/gui/painting/makepsheader.pl
@@ -0,0 +1,155 @@
+#!/usr/bin/perl
+
+open(INPUT, 'qpsprinter.ps')
+ or die "Can't open qpsprinter.ps";
+
+$dontcompress = 1;
+while(<INPUT>) {
+ $line = $_;
+ chomp $line;
+ if ( /ENDUNCOMPRESS/ ) {
+ $dontcompress = 0;
+ }
+ $line =~ s/%.*$//;
+ $line = $line;
+ if ( $dontcompress eq 1 ) {
+ push(@uncompressed, $line);
+ } else {
+ push(@lines, $line);
+ }
+# print "$line\n";
+}
+
+$uc = join(" ", @uncompressed);
+$uc =~ s,\t+, ,g;
+$uc=~ s, +, ,g;
+
+$h = join(" ", @lines);
+$h =~ s,\t+, ,g;
+$h =~ s, +, ,g;
+$h = $h.' ';
+
+# now compress as much as possible
+$h =~ s/ bind def / BD /g;
+$h =~ s/ dup dup / d2 /g;
+$h =~ s/ exch def / ED /g;
+$h =~ s/ setfont / F /g;
+$h =~ s/ rlineto / RL /g;
+$h =~ s/ newpath / n /g;
+$h =~ s/ currentmatrix / CM /g;
+$h =~ s/ setmatrix / SM /g;
+$h =~ s/ translate / TR /g;
+$h =~ s/ setdash / SD /g;
+$h =~ s/ aload pop setrgbcolor / SC /g;
+$h =~ s/ currentfile read pop / CR /g;
+$h =~ s/ index / i /g;
+$h =~ s/ bitshift / bs /g;
+$h =~ s/ setcolorspace / scs /g;
+$h =~ s/ dict dup begin / DB /g;
+$h =~ s/ end def / DE /g;
+$h =~ s/ ifelse / ie /g;
+
+# PDF compatible naming
+$h =~ s/ setlinewidth / w /g;
+$h =~ s/ setdash / d /g;
+
+$h =~ s/ lineto / l /g;
+$h =~ s/ moveto / m /g;
+$h =~ s/ curveto / c /g;
+$h =~ s/ closepath / h /g;
+$h =~ s/ clip / W /g;
+$h =~ s/ eoclip / W* /g;
+
+$h =~ s/ gsave / gs /g;
+$h =~ s/ grestore / gr /g;
+
+# add the uncompressed part of the header before
+$h = $uc.' '.$h;
+
+
+
+#print $h;
+
+# wordwrap at col 76
+@head = split(' ', $h);
+$line = shift @head;
+while( @head ) {
+ $token = shift @head;
+ chomp $token;
+# print "\nl=$l, len=$len, token=$token.";
+ $newline = $line.' '.$token;
+ $newline =~ s, /,/,g;
+ $newline =~ s, \{,\{,g;
+ $newline =~ s, \},\},g;
+ $newline =~ s, \[,\[,g;
+ $newline =~ s, \],\],g;
+ $newline =~ s,\{ ,\{,g;
+ $newline =~ s,\} ,\},g;
+ $newline =~ s,\[ ,\[,g;
+ $newline =~ s,\] ,\],g;
+ if ( length( $newline ) > 76 ) {
+# print "\nline=$line\n";
+ $header = $header."\n\"".$line."\\n\"";
+ $newline = $token;
+ }
+ $line = $newline;
+}
+$header = $header."\n\"".$line."\\n\"";
+
+
+print "static const char *const ps_header =";
+print $header.";\n\n";
+
+close(INPUT);
+exit;
+
+open(INPUT, 'qpsprinter.agl')
+ or die "Can't open qpsprinter.ps";
+
+print "static const char * const agl =\n";
+
+$str = "\"";
+$string ="";
+$i = 0;
+while(<INPUT>) {
+ $line = $_;
+ chomp $line;
+ $line =~ s/#.*//;
+ if(length($line) ne 0) {
+ $num = $line;
+ $name = $line;
+ $num =~ s/,.*//;
+ $name =~ s/.*, \"//;
+ $name =~ s/\".*//;
+ push(@qchar, $num);
+ push(@index, $i);
+ if(length($str.$name) > 76) {
+ $str = $str."\"\n";
+ $string = $string.$str;
+ $str = "\"";
+ }
+ $str = $str.$name."\\0";
+ $i += length($name)+1;
+ }
+}
+
+print $string.";\n\n";
+
+print "static const struct { quint16 u; quint16 index; } unicodetoglyph[] = {\n ";
+
+$loop = 0;
+while( @qchar ) {
+ $loop = $loop + 1;
+ $ch = shift @qchar;
+ $i = shift @index;
+ print "{".$ch.", ".$i."}";
+ if($ch ne "0xFFFF") {
+ print ", ";
+ }
+ if(!($loop % 4)) {
+ print "\n ";
+ }
+};
+
+print "\n};\n\n";
+
diff --git a/src/gui/painting/painting.pri b/src/gui/painting/painting.pri
new file mode 100644
index 0000000000..528559c532
--- /dev/null
+++ b/src/gui/painting/painting.pri
@@ -0,0 +1,369 @@
+# Qt gui library, paint module
+
+HEADERS += \
+ painting/qbezier_p.h \
+ painting/qbrush.h \
+ painting/qcolor.h \
+ painting/qcolor_p.h \
+ painting/qcolormap.h \
+ painting/qdrawutil.h \
+ painting/qemulationpaintengine_p.h \
+ painting/qgraphicssystem_p.h \
+ painting/qmatrix.h \
+ painting/qmemrotate_p.h \
+ painting/qoutlinemapper_p.h \
+ painting/qpaintdevice.h \
+ painting/qpaintengine.h \
+ painting/qpaintengine_p.h \
+ painting/qpaintengine_alpha_p.h \
+ painting/qpaintengine_preview_p.h \
+ painting/qpaintengineex_p.h \
+ painting/qpainter.h \
+ painting/qpainter_p.h \
+ painting/qpainterpath.h \
+ painting/qpainterpath_p.h \
+ painting/qpathclipper_p.h \
+ painting/qpdf_p.h \
+ painting/qpen.h \
+ painting/qpolygon.h \
+ painting/qpolygonclipper_p.h \
+ painting/qprintengine.h \
+ painting/qprintengine_pdf_p.h \
+ painting/qprintengine_ps_p.h \
+ painting/qprinter.h \
+ painting/qprinter_p.h \
+ painting/qprinterinfo.h \
+ painting/qrasterizer_p.h \
+ painting/qregion.h \
+ painting/qstroker_p.h \
+ painting/qstylepainter.h \
+ painting/qtessellator_p.h \
+ painting/qtextureglyphcache_p.h \
+ painting/qtransform.h \
+ painting/qwindowsurface_p.h \
+ painting/qwmatrix.h \
+
+
+SOURCES += \
+ painting/qbezier.cpp \
+ painting/qblendfunctions.cpp \
+ painting/qbrush.cpp \
+ painting/qcolor.cpp \
+ painting/qcolor_p.cpp \
+ painting/qcssutil.cpp \
+ painting/qdrawutil.cpp \
+ painting/qemulationpaintengine.cpp \
+ painting/qgraphicssystem.cpp \
+ painting/qmatrix.cpp \
+ painting/qmemrotate.cpp \
+ painting/qoutlinemapper.cpp \
+ painting/qpaintengine.cpp \
+ painting/qpaintengine_alpha.cpp \
+ painting/qpaintengine_preview.cpp \
+ painting/qpaintengineex.cpp \
+ painting/qpainter.cpp \
+ painting/qpainterpath.cpp \
+ painting/qpathclipper.cpp \
+ painting/qpdf.cpp \
+ painting/qpen.cpp \
+ painting/qpolygon.cpp \
+ painting/qprintengine_pdf.cpp \
+ painting/qprintengine_ps.cpp \
+ painting/qprinter.cpp \
+ painting/qrasterizer.cpp \
+ painting/qregion.cpp \
+ painting/qstroker.cpp \
+ painting/qstylepainter.cpp \
+ painting/qtessellator.cpp \
+ painting/qwindowsurface.cpp \
+ painting/qtextureglyphcache.cpp \
+ painting/qtransform.cpp \
+
+ DEFINES += QT_RASTER_IMAGEENGINE
+ win32:DEFINES += QT_RASTER_PAINTENGINE
+ embedded:DEFINES += QT_RASTER_PAINTENGINE
+ SOURCES += \
+ painting/qpaintengine_raster.cpp \
+ painting/qdrawhelper.cpp \
+ painting/qimagescale.cpp \
+ painting/qgrayraster.c
+
+ HEADERS += \
+ painting/qpaintengine_raster_p.h \
+ painting/qrasterdefs_p.h \
+ painting/qgrayraster_p.h
+
+win32 {
+ HEADERS += painting/qprintengine_win_p.h
+
+ SOURCES += \
+ painting/qcolormap_win.cpp \
+ painting/qpaintdevice_win.cpp \
+ painting/qprintengine_win.cpp \
+ painting/qprinterinfo_win.cpp \
+ painting/qregion_win.cpp
+ !win32-borland:!wince*:LIBS += -lmsimg32
+ contains(QT_CONFIG, direct3d) {
+ HEADERS += painting/qpaintengine_d3d_p.h
+ SOURCES += painting/qpaintengine_d3d.cpp
+ RESOURCES += painting/qpaintengine_d3d.qrc
+ LIBS += -ldxguid
+ }
+}
+
+embedded {
+ HEADERS += \
+ painting/qgraphicssystem_qws_p.h \
+
+ SOURCES += \
+ painting/qgraphicssystem_qws.cpp \
+
+} else {
+ HEADERS += \
+ painting/qgraphicssystem_raster_p.h \
+ painting/qgraphicssystemfactory_p.h \
+ painting/qgraphicssystemplugin_p.h \
+ painting/qwindowsurface_raster_p.h \
+
+ SOURCES += \
+ painting/qgraphicssystem_raster.cpp \
+ painting/qgraphicssystemfactory.cpp \
+ painting/qgraphicssystemplugin.cpp \
+ painting/qwindowsurface_raster.cpp \
+}
+
+wince* {
+ SOURCES -= painting/qregion_win.cpp
+}
+
+unix:x11 {
+ HEADERS += \
+ painting/qpaintengine_x11_p.h
+
+ SOURCES += \
+ painting/qcolormap_x11.cpp \
+ painting/qpaintdevice_x11.cpp \
+ painting/qpaintengine_x11.cpp
+}
+
+!embedded:!x11:mac {
+ HEADERS += \
+ painting/qpaintengine_mac_p.h \
+ painting/qgraphicssystem_mac_p.h \
+ painting/qprintengine_mac_p.h
+
+ SOURCES += \
+ painting/qcolormap_mac.cpp \
+ painting/qpaintdevice_mac.cpp \
+ painting/qpaintengine_mac.cpp \
+ painting/qgraphicssystem_mac.cpp \
+ painting/qprinterinfo_mac.cpp
+ OBJECTIVE_SOURCES += \
+ painting/qprintengine_mac.mm \
+}
+
+unix:!mac {
+ HEADERS += \
+ painting/qprinterinfo_unix_p.h
+ SOURCES += \
+ painting/qprinterinfo_unix.cpp
+}
+
+win32|x11|mac|embedded {
+ SOURCES += painting/qbackingstore.cpp
+ HEADERS += painting/qbackingstore_p.h
+}
+
+embedded {
+ contains(QT_CONFIG,qtopia) {
+ DEFINES += QTOPIA_PRINTENGINE
+ HEADERS += painting/qprintengine_qws_p.h
+ SOURCES += painting/qprintengine_qws.cpp
+ }
+
+ SOURCES += \
+ painting/qcolormap_qws.cpp \
+ painting/qpaintdevice_qws.cpp
+}
+
+x11|embedded {
+ contains(QT_CONFIG,qtopia) {
+ DEFINES += QT_NO_CUPS QT_NO_LPR
+ } else {
+ SOURCES += painting/qcups.cpp
+ HEADERS += painting/qcups_p.h
+ }
+} else {
+ DEFINES += QT_NO_CUPS QT_NO_LPR
+}
+
+contains(QMAKE_MAC_XARCH, no) {
+ DEFINES += QT_NO_MAC_XARCH
+} else:if(mmx|3dnow|sse|sse2|iwmmxt) {
+ HEADERS += painting/qdrawhelper_x86_p.h \
+ painting/qdrawhelper_mmx_p.h \
+ painting/qdrawhelper_sse_p.h
+ mmx {
+ DEFINES += QT_HAVE_MMX
+ MMX_SOURCES += painting/qdrawhelper_mmx.cpp
+ }
+ 3dnow {
+ DEFINES += QT_HAVE_3DNOW
+ MMX3DNOW_SOURCES += painting/qdrawhelper_mmx3dnow.cpp
+ sse {
+ SSE3DNOW_SOURCES += painting/qdrawhelper_sse3dnow.cpp
+ }
+ }
+ sse {
+ DEFINES += QT_HAVE_SSE
+ SSE_SOURCES += painting/qdrawhelper_sse.cpp
+
+ DEFINES += QT_HAVE_MMXEXT
+ }
+ sse2 {
+ DEFINES += QT_HAVE_SSE2
+ SSE2_SOURCES += painting/qdrawhelper_sse2.cpp
+ }
+ iwmmxt {
+ DEFINES += QT_HAVE_IWMMXT
+ IWMMXT_SOURCES += painting/qdrawhelper_iwmmxt.cpp
+ }
+
+ win32-g++|!win32:!*-icc* {
+ mmx {
+ mmx_compiler.commands = $$QMAKE_CXX -c -Winline
+
+ mac {
+ mmx_compiler.commands += -Xarch_i386 -mmmx
+ mmx_compiler.commands += -Xarch_x86_64 -mmmx
+ } else {
+ mmx_compiler.commands += -mmmx
+ }
+
+ mmx_compiler.commands += $(CXXFLAGS) $(INCPATH) ${QMAKE_FILE_IN} -o ${QMAKE_FILE_OUT}
+ mmx_compiler.dependency_type = TYPE_C
+ mmx_compiler.output = ${QMAKE_VAR_OBJECTS_DIR}${QMAKE_FILE_BASE}$${first(QMAKE_EXT_OBJ)}
+ mmx_compiler.input = MMX_SOURCES
+ mmx_compiler.variable_out = OBJECTS
+ mmx_compiler.name = compiling[mmx] ${QMAKE_FILE_IN}
+ silent:mmx_compiler.commands = @echo compiling[mmx] ${QMAKE_FILE_IN} && $$mmx_compiler.commands
+ QMAKE_EXTRA_COMPILERS += mmx_compiler
+ }
+ 3dnow {
+ mmx3dnow_compiler.commands = $$QMAKE_CXX -c -Winline
+
+ mac {
+ mmx3dnow_compiler.commands += -Xarch_i386 -m3dnow -Xarch_i386 -mmmx
+ mmx3dnow_compiler.commands += -Xarch_x86_64 -m3dnow -Xarch_x86_64 -mmmx
+ } else {
+ mmx3dnow_compiler.commands += -m3dnow -mmmx
+ }
+
+ mmx3dnow_compiler.commands += $(CXXFLAGS) $(INCPATH) ${QMAKE_FILE_IN} -o ${QMAKE_FILE_OUT}
+ mmx3dnow_compiler.dependency_type = TYPE_C
+ mmx3dnow_compiler.output = ${QMAKE_VAR_OBJECTS_DIR}${QMAKE_FILE_BASE}$${first(QMAKE_EXT_OBJ)}
+ mmx3dnow_compiler.input = MMX3DNOW_SOURCES
+ mmx3dnow_compiler.variable_out = OBJECTS
+ mmx3dnow_compiler.name = compiling[mmx3dnow] ${QMAKE_FILE_IN}
+ silent:mmx3dnow_compiler.commands = @echo compiling[mmx3dnow] ${QMAKE_FILE_IN} && $$mmx3dnow_compiler.commands
+ QMAKE_EXTRA_COMPILERS += mmx3dnow_compiler
+ sse {
+ sse3dnow_compiler.commands = $$QMAKE_CXX -c -Winline
+
+ mac {
+ sse3dnow_compiler.commands += -Xarch_i386 -m3dnow -Xarch_i386 -msse
+ sse3dnow_compiler.commands += -Xarch_x86_64 -m3dnow -Xarch_x86_64 -msse
+ } else {
+ sse3dnow_compiler.commands += -m3dnow -msse
+ }
+
+ sse3dnow_compiler.commands += $(CXXFLAGS) $(INCPATH) ${QMAKE_FILE_IN} -o ${QMAKE_FILE_OUT}
+ sse3dnow_compiler.dependency_type = TYPE_C
+ sse3dnow_compiler.output = ${QMAKE_VAR_OBJECTS_DIR}${QMAKE_FILE_BASE}$${first(QMAKE_EXT_OBJ)}
+ sse3dnow_compiler.input = SSE3DNOW_SOURCES
+ sse3dnow_compiler.variable_out = OBJECTS
+ sse3dnow_compiler.name = compiling[sse3dnow] ${QMAKE_FILE_IN}
+ silent:sse3dnow_compiler.commands = @echo compiling[sse3dnow] ${QMAKE_FILE_IN} && $$sse3dnow_compiler.commands
+ QMAKE_EXTRA_COMPILERS += sse3dnow_compiler
+ }
+ }
+ sse {
+ sse_compiler.commands = $$QMAKE_CXX -c -Winline
+
+ mac {
+ sse_compiler.commands += -Xarch_i386 -msse
+ sse_compiler.commands += -Xarch_x86_64 -msse
+ } else {
+ sse_compiler.commands += -msse
+ }
+
+ sse_compiler.commands += $(CXXFLAGS) $(INCPATH) ${QMAKE_FILE_IN} -o ${QMAKE_FILE_OUT}
+ sse_compiler.dependency_type = TYPE_C
+ sse_compiler.output = ${QMAKE_VAR_OBJECTS_DIR}${QMAKE_FILE_BASE}$${first(QMAKE_EXT_OBJ)}
+ sse_compiler.input = SSE_SOURCES
+ sse_compiler.variable_out = OBJECTS
+ sse_compiler.name = compiling[sse] ${QMAKE_FILE_IN}
+ silent:sse_compiler.commands = @echo compiling[sse] ${QMAKE_FILE_IN} && $$sse_compiler.commands
+ QMAKE_EXTRA_COMPILERS += sse_compiler
+ }
+ sse2 {
+ sse2_compiler.commands = $$QMAKE_CXX -c -Winline
+
+ mac {
+ sse2_compiler.commands += -Xarch_i386 -msse2
+ sse2_compiler.commands += -Xarch_x86_64 -msse2
+ } else {
+ sse2_compiler.commands += -msse2
+ }
+
+ sse2_compiler.commands += $(CXXFLAGS) $(INCPATH) ${QMAKE_FILE_IN} -o ${QMAKE_FILE_OUT}
+ sse2_compiler.dependency_type = TYPE_C
+ sse2_compiler.output = ${QMAKE_VAR_OBJECTS_DIR}${QMAKE_FILE_BASE}$${first(QMAKE_EXT_OBJ)}
+ sse2_compiler.input = SSE2_SOURCES
+ sse2_compiler.variable_out = OBJECTS
+ sse2_compiler.name = compiling[sse2] ${QMAKE_FILE_IN}
+ silent:sse2_compiler.commands = @echo compiling[sse2] ${QMAKE_FILE_IN} && $$sse2_compiler.commands
+ QMAKE_EXTRA_COMPILERS += sse2_compiler
+ }
+ iwmmxt {
+ iwmmxt_compiler.commands = $$QMAKE_CXX -c -Winline
+ iwmmxt_compiler.commands += -mcpu=iwmmxt
+ iwmmxt_compiler.commands += $(CXXFLAGS) $(INCPATH) ${QMAKE_FILE_IN} -o ${QMAKE_FILE_OUT}
+ iwmmxt_compiler.dependency_type = TYPE_C
+ iwmmxt_compiler.output = ${QMAKE_VAR_OBJECTS_DIR}${QMAKE_FILE_BASE}$${first(QMAKE_EXT_OBJ)}
+ iwmmxt_compiler.input = IWMMXT_SOURCES
+ iwmmxt_compiler.variable_out = OBJECTS
+ iwmmxt_compiler.name = compiling[iwmmxt] ${QMAKE_FILE_IN}
+ silent:iwmmxt_compiler.commands = @echo compiling[iwmmxt] ${QMAKE_FILE_IN} && $$iwmmxt_compiler.commands
+ QMAKE_EXTRA_COMPILERS += iwmmxt_compiler
+ }
+ } else {
+ mmx: SOURCES += $$MMX_SOURCES
+ 3dnow: SOURCES += $$MMX3DNOW_SOURCES
+ 3dnow:sse: SOURCES += $$SSE3DNOW_SOURCES
+ sse: SOURCES += $$SSE_SOURCES
+ sse2: SOURCES += $$SSE2_SOURCES
+ iwmmxt: SOURCES += $$IWMMXT_SOURCES
+ }
+}
+
+x11 {
+ HEADERS += painting/qwindowsurface_x11_p.h
+ SOURCES += painting/qwindowsurface_x11.cpp
+}
+
+mac {
+ HEADERS += painting/qwindowsurface_mac_p.h
+ SOURCES += painting/qwindowsurface_mac.cpp
+}
+
+embedded {
+ HEADERS += painting/qwindowsurface_qws_p.h
+ SOURCES += painting/qwindowsurface_qws.cpp
+}
+
+win32:contains(QT_CONFIG, direct3d) {
+ HEADERS += painting/qwindowsurface_d3d_p.h
+ SOURCES += painting/qwindowsurface_d3d.cpp
+}
+
diff --git a/src/gui/painting/qbackingstore.cpp b/src/gui/painting/qbackingstore.cpp
new file mode 100644
index 0000000000..fbac811a0b
--- /dev/null
+++ b/src/gui/painting/qbackingstore.cpp
@@ -0,0 +1,1548 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qbackingstore_p.h"
+
+#include <QtCore/qglobal.h>
+#include <QtCore/qdebug.h>
+#include <QtCore/qvarlengtharray.h>
+#include <QtGui/qevent.h>
+#include <QtGui/qapplication.h>
+#include <QtGui/qpaintengine.h>
+#include <QtGui/qgraphicsproxywidget.h>
+
+#include <private/qwidget_p.h>
+#include <private/qwindowsurface_raster_p.h>
+#include <private/qapplication_p.h>
+#include <private/qpaintengine_raster_p.h>
+
+#include "qgraphicssystem_p.h"
+
+#ifdef Q_WS_QWS
+#include <QtGui/qwsmanager_qws.h>
+#include <private/qwsmanager_p.h>
+#endif
+
+QT_BEGIN_NAMESPACE
+
+extern QRegion qt_dirtyRegion(QWidget *);
+
+/*
+ A version of QRect::intersects() that does not normalize the rects.
+*/
+static inline bool qRectIntersects(const QRect &r1, const QRect &r2)
+{
+ return (qMax(r1.left(), r2.left()) <= qMin(r1.right(), r2.right())
+ && qMax(r1.top(), r2.top()) <= qMin(r1.bottom(), r2.bottom()));
+}
+
+/**
+ * Flushes the contents of the \a windowSurface into the screen area of \a widget.
+ * \a tlwOffset is the position of the top level widget relative to the window surface.
+ * \a region is the region to be updated in \a widget coordinates.
+ */
+static inline void qt_flush(QWidget *widget, const QRegion &region, QWindowSurface *windowSurface,
+ QWidget *tlw, const QPoint &tlwOffset)
+{
+ Q_ASSERT(widget);
+ Q_ASSERT(!region.isEmpty());
+ Q_ASSERT(windowSurface);
+ Q_ASSERT(tlw);
+
+#if !defined(QT_NO_PAINT_DEBUG) && !defined(Q_WS_QWS)
+ // QWS does flush update in QWindowSurface::flush (because it needs to lock the surface etc).
+ static int flushUpdate = qgetenv("QT_FLUSH_UPDATE").toInt();
+ if (flushUpdate > 0)
+ QWidgetBackingStore::showYellowThing(widget, region, flushUpdate * 10, false);
+#endif
+
+ if (widget != tlw)
+ windowSurface->flush(widget, region, tlwOffset + widget->mapTo(tlw, QPoint()));
+ else
+ windowSurface->flush(widget, region, tlwOffset);
+}
+
+#ifndef QT_NO_PAINT_DEBUG
+#ifdef Q_WS_WIN
+static void showYellowThing_win(QWidget *widget, const QRegion &region, int msec)
+{
+ HBRUSH brush;
+ static int i = 0;
+ switch (i) {
+ case 0:
+ brush = CreateSolidBrush(RGB(255, 255, 0));
+ break;
+ case 1:
+ brush = CreateSolidBrush(RGB(255, 200, 55));
+ break;
+ case 2:
+ brush = CreateSolidBrush(RGB(200, 255, 55));
+ break;
+ case 3:
+ brush = CreateSolidBrush(RGB(200, 200, 0));
+ break;
+ }
+ i = (i + 1) & 3;
+
+ HDC hdc = widget->getDC();
+
+ const QVector<QRect> &rects = region.rects();
+ foreach (QRect rect, rects) {
+ RECT winRect;
+ SetRect(&winRect, rect.left(), rect.top(), rect.right(), rect.bottom());
+ FillRect(hdc, &winRect, brush);
+ }
+
+ widget->releaseDC(hdc);
+ ::Sleep(msec);
+}
+#endif
+
+void QWidgetBackingStore::showYellowThing(QWidget *widget, const QRegion &toBePainted, int msec, bool unclipped)
+{
+#ifdef Q_WS_QWS
+ Q_UNUSED(widget);
+ Q_UNUSED(unclipped);
+ static QWSYellowSurface surface(true);
+ surface.setDelay(msec);
+ surface.flush(widget, toBePainted, QPoint());
+#else
+ QRegion paintRegion = toBePainted;
+ QRect widgetRect = widget->rect();
+
+ if (!widget->internalWinId()) {
+ QWidget *nativeParent = widget->nativeParentWidget();
+ const QPoint offset = widget->mapTo(nativeParent, QPoint(0, 0));
+ paintRegion.translate(offset);
+ widgetRect.translate(offset);
+ widget = nativeParent;
+ }
+
+#ifdef Q_WS_WIN
+ Q_UNUSED(unclipped);
+ showYellowThing_win(widget, paintRegion, msec);
+#else
+ //flags to fool painter
+ bool paintUnclipped = widget->testAttribute(Qt::WA_PaintUnclipped);
+ if (unclipped && !widget->d_func()->paintOnScreen())
+ widget->setAttribute(Qt::WA_PaintUnclipped);
+
+ const bool setFlag = !widget->testAttribute(Qt::WA_WState_InPaintEvent);
+ if (setFlag)
+ widget->setAttribute(Qt::WA_WState_InPaintEvent);
+
+ //setup the engine
+ QPaintEngine *pe = widget->paintEngine();
+ if (pe) {
+ pe->setSystemClip(paintRegion);
+ {
+ QPainter p(widget);
+ p.setClipRegion(paintRegion);
+ static int i = 0;
+ switch (i) {
+ case 0:
+ p.fillRect(widgetRect, QColor(255,255,0));
+ break;
+ case 1:
+ p.fillRect(widgetRect, QColor(255,200,55));
+ break;
+ case 2:
+ p.fillRect(widgetRect, QColor(200,255,55));
+ break;
+ case 3:
+ p.fillRect(widgetRect, QColor(200,200,0));
+ break;
+ }
+ i = (i+1) & 3;
+ p.end();
+ }
+ }
+
+ if (setFlag)
+ widget->setAttribute(Qt::WA_WState_InPaintEvent, false);
+
+ //restore
+ widget->setAttribute(Qt::WA_PaintUnclipped, paintUnclipped);
+
+ if (pe)
+ pe->setSystemClip(QRegion());
+
+ QApplication::syncX();
+
+#if defined(Q_OS_UNIX)
+ ::usleep(1000 * msec);
+#endif
+#endif // Q_WS_WIN
+#endif // Q_WS_QWS
+}
+
+bool QWidgetBackingStore::flushPaint(QWidget *widget, const QRegion &rgn)
+{
+ if (!widget)
+ return false;
+
+ int delay = 0;
+ if (widget->testAttribute(Qt::WA_WState_InPaintEvent)) {
+ static int flushPaintEvent = qgetenv("QT_FLUSH_PAINT_EVENT").toInt();
+ if (!flushPaintEvent)
+ return false;
+ delay = flushPaintEvent;
+ } else {
+ static int flushPaint = qgetenv("QT_FLUSH_PAINT").toInt();
+ if (!flushPaint)
+ return false;
+ delay = flushPaint;
+ }
+
+ QWidgetBackingStore::showYellowThing(widget, rgn, delay * 10, true);
+ return true;
+}
+
+void QWidgetBackingStore::unflushPaint(QWidget *widget, const QRegion &rgn)
+{
+ if (widget->d_func()->paintOnScreen() || rgn.isEmpty())
+ return;
+
+ QWidget *tlw = widget->window();
+ QTLWExtra *tlwExtra = tlw->d_func()->maybeTopData();
+ if (!tlwExtra)
+ return;
+
+ const QPoint offset = widget->mapTo(tlw, QPoint());
+ qt_flush(widget, rgn, tlwExtra->backingStore->windowSurface, tlw, offset);
+}
+#endif // QT_NO_PAINT_DEBUG
+
+/*
+ Moves the whole rect by (dx, dy) in widget's coordinate system.
+ Doesn't generate any updates.
+*/
+bool QWidgetBackingStore::bltRect(const QRect &rect, int dx, int dy, QWidget *widget)
+{
+ const QPoint pos(tlwOffset + widget->mapTo(tlw, rect.topLeft()));
+ return windowSurface->scroll(QRect(pos, rect.size()), dx, dy);
+}
+
+void QWidgetBackingStore::releaseBuffer()
+{
+ if (windowSurface)
+ windowSurface->setGeometry(QRect());
+#ifdef Q_BACKINGSTORE_SUBSURFACES
+ for (int i = 0; i < subSurfaces.size(); ++i)
+ subSurfaces.at(i)->setGeometry(QRect());
+#endif
+}
+
+/*!
+ Prepares the window surface to paint a\ toClean region of the \a widget and
+ updates the BeginPaintInfo struct accordingly.
+
+ The \a toClean region might be clipped by the window surface.
+*/
+void QWidgetBackingStore::beginPaint(QRegion &toClean, QWidget *widget, QWindowSurface *windowSurface,
+ BeginPaintInfo *returnInfo, bool toCleanIsInTopLevelCoordinates)
+{
+#ifdef Q_WS_QWS
+ QWSWindowSurface *surface = static_cast<QWSWindowSurface *>(windowSurface);
+ QWidget *surfaceWidget = surface->window();
+
+ if (!surface->isValid()) {
+ // this looks strange but it really just releases the surface
+ surface->releaseSurface();
+ // the old window surface is deleted in setWindowSurface, which is
+ // called from QWindowSurface constructor.
+ windowSurface = tlw->d_func()->createDefaultWindowSurface();
+ surface = static_cast<QWSWindowSurface *>(windowSurface);
+ // createDefaultWindowSurface() will set topdata->windowSurface on the
+ // widget to zero. However, if this is a sub-surface, it should point
+ // to the widget's sub windowSurface, so we set that here:
+ if (!surfaceWidget->isWindow())
+ surfaceWidget->d_func()->topData()->windowSurface = windowSurface;
+ surface->setGeometry(topLevelRect());
+ returnInfo->windowSurfaceRecreated = true;
+ }
+
+ const QRegion toCleanUnclipped(toClean);
+
+ if (surfaceWidget->isWindow())
+ tlwOffset = surface->painterOffset();
+#ifdef Q_BACKINGSTORE_SUBSURFACES
+ else if (toCleanIsInTopLevelCoordinates)
+ toClean &= surface->clipRegion().translated(surfaceWidget->mapTo(tlw, QPoint()));
+ if (!toCleanIsInTopLevelCoordinates && windowSurface == this->windowSurface)
+ toClean &= surface->clipRegion().translated(-widget->mapTo(surfaceWidget, QPoint()));
+#else
+ toClean &= surface->clipRegion();
+#endif
+
+ if (toClean.isEmpty()) {
+ if (surfaceWidget->isWindow()) {
+ dirtyFromPreviousSync += toCleanUnclipped;
+ hasDirtyFromPreviousSync = true;
+ }
+
+ returnInfo->nothingToPaint = true;
+ // Nothing to repaint. However, we might have newly exposed areas on the
+ // screen, so we have to make sure those are flushed.
+ flush();
+ return;
+ }
+
+ if (surfaceWidget->isWindow()) {
+ if (toCleanUnclipped != toClean) {
+ dirtyFromPreviousSync += (toCleanUnclipped - surface->clipRegion());
+ hasDirtyFromPreviousSync = true;
+ }
+ if (hasDirtyFromPreviousSync) {
+ dirtyFromPreviousSync -= toClean;
+ hasDirtyFromPreviousSync = !dirtyFromPreviousSync.isEmpty();
+ }
+ }
+
+#endif // Q_WS_QWS
+
+ Q_UNUSED(widget);
+ Q_UNUSED(toCleanIsInTopLevelCoordinates);
+
+ // Always flush repainted areas.
+ dirtyOnScreen += toClean;
+
+#ifdef QT_NO_PAINT_DEBUG
+ windowSurface->beginPaint(toClean);
+#else
+ returnInfo->wasFlushed = QWidgetBackingStore::flushPaint(tlw, toClean);
+ // Avoid deadlock with QT_FLUSH_PAINT: the server will wait for
+ // the BackingStore lock, so if we hold that, the server will
+ // never release the Communication lock that we are waiting for in
+ // sendSynchronousCommand
+ if (!returnInfo->wasFlushed)
+ windowSurface->beginPaint(toClean);
+#endif
+
+ Q_UNUSED(returnInfo);
+}
+
+void QWidgetBackingStore::endPaint(const QRegion &cleaned, QWindowSurface *windowSurface,
+ BeginPaintInfo *beginPaintInfo)
+{
+#ifndef QT_NO_PAINT_DEBUG
+ if (!beginPaintInfo->wasFlushed)
+ windowSurface->endPaint(cleaned);
+ else
+ QWidgetBackingStore::unflushPaint(tlw, cleaned);
+#else
+ Q_UNUSED(beginPaintInfo);
+ windowSurface->endPaint(cleaned);
+#endif
+
+#ifdef Q_BACKINGSTORE_SUBSURFACES
+ flush(static_cast<QWSWindowSurface *>(windowSurface)->window(), windowSurface);
+#else
+ flush();
+#endif
+}
+
+/*!
+ Returns the region (in top-level coordinates) that needs repaint and/or flush.
+
+ If the widget is non-zero, only the dirty region for the widget is returned
+ and the region will be in widget coordinates.
+*/
+QRegion QWidgetBackingStore::dirtyRegion(QWidget *widget) const
+{
+ const bool widgetDirty = widget && widget != tlw;
+ const QRect tlwRect(topLevelRect());
+ const QRect surfaceGeometry(windowSurface->geometry());
+ if (surfaceGeometry != tlwRect && surfaceGeometry.size() != tlwRect.size()) {
+ if (widgetDirty) {
+ const QRect dirtyTlwRect = QRect(QPoint(), tlwRect.size());
+ const QPoint offset(widget->mapTo(tlw, QPoint()));
+ const QRect dirtyWidgetRect(dirtyTlwRect & widget->rect().translated(offset));
+ return dirtyWidgetRect.translated(-offset);
+ }
+ return QRect(QPoint(), tlwRect.size());
+ }
+
+ // Calculate the region that needs repaint.
+ QRegion r(dirty);
+ for (int i = 0; i < dirtyWidgets.size(); ++i) {
+ QWidget *w = dirtyWidgets.at(i);
+ if (widgetDirty && w != widget && !widget->isAncestorOf(w))
+ continue;
+ r += w->d_func()->dirty.translated(w->mapTo(tlw, QPoint()));
+ }
+
+ // Append the region that needs flush.
+ r += dirtyOnScreen;
+
+ if (dirtyOnScreenWidgets) { // Only in use with native child widgets.
+ for (int i = 0; i < dirtyOnScreenWidgets->size(); ++i) {
+ QWidget *w = dirtyOnScreenWidgets->at(i);
+ if (widgetDirty && w != widget && !widget->isAncestorOf(w))
+ continue;
+ QWidgetPrivate *wd = w->d_func();
+ Q_ASSERT(wd->needsFlush);
+ r += wd->needsFlush->translated(w->mapTo(tlw, QPoint()));
+ }
+ }
+
+ if (widgetDirty) {
+ // Intersect with the widget geometry and translate to its coordinates.
+ const QPoint offset(widget->mapTo(tlw, QPoint()));
+ r &= widget->rect().translated(offset);
+ r.translate(-offset);
+ }
+ return r;
+}
+
+/*!
+ Returns the static content inside the \a parent if non-zero; otherwise the static content
+ for the entire backing store is returned. The content will be clipped to \a withingClipRect
+ if non-empty.
+*/
+QRegion QWidgetBackingStore::staticContents(QWidget *parent, const QRect &withinClipRect) const
+{
+ if (!parent && tlw->testAttribute(Qt::WA_StaticContents)) {
+ const QRect surfaceGeometry(windowSurface->geometry());
+ QRect surfaceRect(0, 0, surfaceGeometry.width(), surfaceGeometry.height());
+ if (!withinClipRect.isEmpty())
+ surfaceRect &= withinClipRect;
+ return QRegion(surfaceRect);
+ }
+
+ QRegion region;
+ if (parent && parent->d_func()->children.isEmpty())
+ return region;
+
+ const bool clipToRect = !withinClipRect.isEmpty();
+ const int count = staticWidgets.count();
+ for (int i = 0; i < count; ++i) {
+ QWidget *w = staticWidgets.at(i);
+ QWidgetPrivate *wd = w->d_func();
+ if (!wd->isOpaque || !wd->extra || wd->extra->staticContentsSize.isEmpty()
+ || !w->isVisible() || (parent && !parent->isAncestorOf(w))) {
+ continue;
+ }
+
+ QRect rect(0, 0, wd->extra->staticContentsSize.width(), wd->extra->staticContentsSize.height());
+ const QPoint offset = w->mapTo(parent ? parent : tlw, QPoint());
+ if (clipToRect)
+ rect &= withinClipRect.translated(-offset);
+ if (rect.isEmpty())
+ continue;
+
+ rect &= wd->clipRect();
+ if (rect.isEmpty())
+ continue;
+
+ QRegion visible(rect);
+ wd->clipToEffectiveMask(visible);
+ if (visible.isEmpty())
+ continue;
+ wd->subtractOpaqueSiblings(visible, 0, /*alsoNonOpaque=*/true);
+
+ visible.translate(offset);
+ region += visible;
+ }
+
+ return region;
+}
+
+static inline void sendUpdateRequest(QWidget *widget, bool updateImmediately)
+{
+ if (!widget)
+ return;
+
+#if defined(Q_WS_WIN) && !defined(Q_OS_WINCE)
+ if (QApplicationPrivate::inSizeMove && widget->internalWinId() && !updateImmediately) {
+ // Tell Windows to send us a paint event if we're in WM_SIZE/WM_MOVE; posted events
+ // are blocked until the mouse button is released. See task 146849.
+ const QRegion rgn(qt_dirtyRegion(widget));
+ InvalidateRgn(widget->internalWinId(), rgn.handle(), false);
+ qt_widget_private(widget)->dirty = QRegion();
+ return;
+ }
+#endif
+
+ if (updateImmediately) {
+ QEvent event(QEvent::UpdateRequest);
+ QApplication::sendEvent(widget, &event);
+ } else {
+ QApplication::postEvent(widget, new QEvent(QEvent::UpdateRequest), Qt::LowEventPriority);
+ }
+}
+
+/*!
+ Marks the region of the widget as dirty (if not already marked as dirty) and
+ posts an UpdateRequest event to the top-level widget (if not already posted).
+
+ If updateImmediately is true, the event is sent immediately instead of posted.
+
+ If invalidateBuffer is true, all widgets intersecting with the region will be dirty.
+
+ If the widget paints directly on screen, the event is sent to the widget
+ instead of the top-level widget, and invalidateBuffer is completely ignored.
+
+ ### Qt 4.6: Merge into a template function (after MSVC isn't supported anymore).
+*/
+void QWidgetBackingStore::markDirty(const QRegion &rgn, QWidget *widget, bool updateImmediately,
+ bool invalidateBuffer)
+{
+ Q_ASSERT(tlw->d_func()->extra);
+ Q_ASSERT(tlw->d_func()->extra->topextra);
+ Q_ASSERT(!tlw->d_func()->extra->topextra->inTopLevelResize);
+ Q_ASSERT(widget->isVisible() && widget->updatesEnabled());
+ Q_ASSERT(widget->window() == tlw);
+ Q_ASSERT(!rgn.isEmpty());
+
+ if (widget->d_func()->paintOnScreen()) {
+ if (widget->d_func()->dirty.isEmpty()) {
+ widget->d_func()->dirty = rgn;
+ sendUpdateRequest(widget, updateImmediately);
+ return;
+ } else if (qt_region_strictContains(widget->d_func()->dirty, widget->rect())) {
+ if (updateImmediately)
+ sendUpdateRequest(widget, updateImmediately);
+ return; // Already dirty.
+ }
+
+ const bool eventAlreadyPosted = !widget->d_func()->dirty.isEmpty();
+ widget->d_func()->dirty += rgn;
+ if (!eventAlreadyPosted || updateImmediately)
+ sendUpdateRequest(widget, updateImmediately);
+ return;
+ }
+
+ const QPoint offset = widget->mapTo(tlw, QPoint());
+ if (qt_region_strictContains(dirty, widget->rect().translated(offset))) {
+ if (updateImmediately)
+ sendUpdateRequest(tlw, updateImmediately);
+ return; // Already dirty.
+ }
+
+ if (invalidateBuffer) {
+ const bool eventAlreadyPosted = !dirty.isEmpty();
+ dirty += rgn.translated(offset);
+ if (!eventAlreadyPosted || updateImmediately)
+ sendUpdateRequest(tlw, updateImmediately);
+ return;
+ }
+
+ if (dirtyWidgets.isEmpty()) {
+ addDirtyWidget(widget, rgn);
+ sendUpdateRequest(tlw, updateImmediately);
+ return;
+ }
+
+ if (widget->d_func()->inDirtyList) {
+ if (!qt_region_strictContains(widget->d_func()->dirty, widget->rect()))
+ widget->d_func()->dirty += rgn;
+ } else {
+ addDirtyWidget(widget, rgn);
+ }
+
+ if (updateImmediately)
+ sendUpdateRequest(tlw, updateImmediately);
+}
+
+/*!
+ This function is equivalent to calling markDirty(QRegion(rect), ...), but
+ is more efficient as it eliminates QRegion operations/allocations and can
+ use the rect more precisely for additional cut-offs.
+
+ ### Qt 4.6: Merge into a template function (after MSVC isn't supported anymore).
+*/
+void QWidgetBackingStore::markDirty(const QRect &rect, QWidget *widget, bool updateImmediately,
+ bool invalidateBuffer)
+{
+ Q_ASSERT(tlw->d_func()->extra);
+ Q_ASSERT(tlw->d_func()->extra->topextra);
+ Q_ASSERT(!tlw->d_func()->extra->topextra->inTopLevelResize);
+ Q_ASSERT(widget->isVisible() && widget->updatesEnabled());
+ Q_ASSERT(widget->window() == tlw);
+ Q_ASSERT(!rect.isEmpty());
+
+ if (widget->d_func()->paintOnScreen()) {
+ if (widget->d_func()->dirty.isEmpty()) {
+ widget->d_func()->dirty = QRegion(rect);
+ sendUpdateRequest(widget, updateImmediately);
+ return;
+ } else if (qt_region_strictContains(widget->d_func()->dirty, rect)) {
+ if (updateImmediately)
+ sendUpdateRequest(widget, updateImmediately);
+ return; // Already dirty.
+ }
+
+ const bool eventAlreadyPosted = !widget->d_func()->dirty.isEmpty();
+ widget->d_func()->dirty += rect;
+ if (!eventAlreadyPosted || updateImmediately)
+ sendUpdateRequest(widget, updateImmediately);
+ return;
+ }
+
+ const QRect translatedRect(rect.translated(widget->mapTo(tlw, QPoint())));
+ if (qt_region_strictContains(dirty, translatedRect)) {
+ if (updateImmediately)
+ sendUpdateRequest(tlw, updateImmediately);
+ return; // Already dirty
+ }
+
+ if (invalidateBuffer) {
+ const bool eventAlreadyPosted = !dirty.isEmpty();
+ dirty += translatedRect;
+ if (!eventAlreadyPosted || updateImmediately)
+ sendUpdateRequest(tlw, updateImmediately);
+ return;
+ }
+
+ if (dirtyWidgets.isEmpty()) {
+ addDirtyWidget(widget, rect);
+ sendUpdateRequest(tlw, updateImmediately);
+ return;
+ }
+
+ if (widget->d_func()->inDirtyList) {
+ if (!qt_region_strictContains(widget->d_func()->dirty, rect))
+ widget->d_func()->dirty += rect;
+ } else {
+ addDirtyWidget(widget, rect);
+ }
+
+ if (updateImmediately)
+ sendUpdateRequest(tlw, updateImmediately);
+}
+
+/*!
+ Marks the \a region of the \a widget as dirty on screen. The \a region will be copied from
+ the backing store to the \a widget's native parent next time flush() is called.
+
+ Paint on screen widgets are ignored.
+*/
+void QWidgetBackingStore::markDirtyOnScreen(const QRegion &region, QWidget *widget, const QPoint &topLevelOffset)
+{
+ if (!widget || widget->d_func()->paintOnScreen() || region.isEmpty())
+ return;
+
+#if defined(Q_WS_QWS) || defined(Q_WS_MAC)
+ if (!widget->testAttribute(Qt::WA_WState_InPaintEvent))
+ dirtyOnScreen += region.translated(topLevelOffset);
+ return;
+#endif
+
+ // Top-level.
+ if (widget == tlw) {
+ if (!widget->testAttribute(Qt::WA_WState_InPaintEvent))
+ dirtyOnScreen += region;
+ return;
+ }
+
+ // Alien widgets.
+ if (!widget->internalWinId()) {
+ QWidget *nativeParent = widget->nativeParentWidget();
+ // Alien widgets with the top-level as the native parent (common case).
+ if (nativeParent == tlw) {
+ if (!widget->testAttribute(Qt::WA_WState_InPaintEvent))
+ dirtyOnScreen += region.translated(topLevelOffset);
+ return;
+ }
+
+ // Alien widgets with native parent != tlw.
+ QWidgetPrivate *nativeParentPrivate = nativeParent->d_func();
+ if (!nativeParentPrivate->needsFlush)
+ nativeParentPrivate->needsFlush = new QRegion;
+ const QPoint nativeParentOffset = widget->mapTo(nativeParent, QPoint());
+ *nativeParentPrivate->needsFlush += region.translated(nativeParentOffset);
+ appendDirtyOnScreenWidget(nativeParent);
+ return;
+ }
+
+ // Native child widgets.
+ QWidgetPrivate *widgetPrivate = widget->d_func();
+ if (!widgetPrivate->needsFlush)
+ widgetPrivate->needsFlush = new QRegion;
+ *widgetPrivate->needsFlush += region;
+ appendDirtyOnScreenWidget(widget);
+}
+
+void QWidgetBackingStore::removeDirtyWidget(QWidget *w)
+{
+ if (!w)
+ return;
+
+ dirtyWidgetsRemoveAll(w);
+ dirtyOnScreenWidgetsRemoveAll(w);
+ resetWidget(w);
+
+ QWidgetPrivate *wd = w->d_func();
+ const int n = wd->children.count();
+ for (int i = 0; i < n; ++i) {
+ if (QWidget *child = qobject_cast<QWidget*>(wd->children.at(i)))
+ removeDirtyWidget(child);
+ }
+}
+
+#if defined(Q_WS_QWS) && !defined(QT_NO_QWS_MANAGER)
+bool QWidgetBackingStore::hasDirtyWindowDecoration() const
+{
+ QTLWExtra *tlwExtra = tlw->d_func()->maybeTopData();
+ if (tlwExtra && tlwExtra->qwsManager)
+ return !tlwExtra->qwsManager->d_func()->dirtyRegions.isEmpty();
+ return false;
+}
+
+void QWidgetBackingStore::paintWindowDecoration()
+{
+ if (!hasDirtyWindowDecoration())
+ return;
+
+ QDecoration &decoration = QApplication::qwsDecoration();
+ const QRect decorationRect = tlw->rect();
+ QRegion decorationRegion = decoration.region(tlw, decorationRect);
+
+ QWSManagerPrivate *managerPrivate = tlw->d_func()->topData()->qwsManager->d_func();
+ const bool doClipping = !managerPrivate->entireDecorationNeedsRepaint
+ && !managerPrivate->dirtyClip.isEmpty();
+
+ if (doClipping) {
+ decorationRegion &= static_cast<QWSWindowSurface *>(windowSurface)->clipRegion();
+ decorationRegion &= managerPrivate->dirtyClip;
+ }
+
+ if (decorationRegion.isEmpty())
+ return;
+
+ windowSurface->beginPaint(decorationRegion);
+
+ QPaintEngine *engine = windowSurface->paintDevice()->paintEngine();
+ Q_ASSERT(engine);
+ const QRegion oldSystemClip(engine->systemClip());
+ engine->setSystemClip(decorationRegion.translated(tlwOffset));
+
+ QPainter painter(windowSurface->paintDevice());
+ painter.setFont(qApp->font());
+ painter.translate(tlwOffset);
+
+ const int numDirty = managerPrivate->dirtyRegions.size();
+ for (int i = 0; i < numDirty; ++i) {
+ const int area = managerPrivate->dirtyRegions.at(i);
+
+ QRegion clipRegion = decoration.region(tlw, decorationRect, area);
+ if (!clipRegion.isEmpty()) {
+ // Decoration styles changes the clip and assumes the old clip is non-empty,
+ // so we have to set it, but in theory it shouldn't be required.
+ painter.setClipRegion(clipRegion);
+ decoration.paint(&painter, tlw, area, managerPrivate->dirtyStates.at(i));
+ }
+ }
+ markDirtyOnScreen(decorationRegion, tlw, QPoint());
+
+ painter.end();
+ windowSurface->endPaint(decorationRegion);
+ managerPrivate->clearDirtyRegions();
+ engine->setSystemClip(oldSystemClip);
+}
+#endif
+
+void QWidgetBackingStore::updateLists(QWidget *cur)
+{
+ if (!cur)
+ return;
+
+ QList<QObject*> children = cur->children();
+ for (int i = 0; i < children.size(); ++i) {
+ QWidget *child = qobject_cast<QWidget*>(children.at(i));
+ if (!child)
+ continue;
+
+ updateLists(child);
+ }
+
+ if (cur->testAttribute(Qt::WA_StaticContents))
+ addStaticWidget(cur);
+
+#ifdef Q_BACKINGSTORE_SUBSURFACES
+ QTLWExtra *extra = cur->d_func()->maybeTopData();
+ if (extra && extra->windowSurface && cur != tlw)
+ subSurfaces.append(extra->windowSurface);
+#endif
+}
+
+QWidgetBackingStore::QWidgetBackingStore(QWidget *topLevel)
+ : tlw(topLevel), dirtyOnScreenWidgets(0), hasDirtyFromPreviousSync(false)
+{
+ windowSurface = tlw->windowSurface();
+ if (!windowSurface)
+ windowSurface = topLevel->d_func()->createDefaultWindowSurface();
+
+ // The QWindowSurface constructor will call QWidget::setWindowSurface(),
+ // but automatically created surfaces should not be added to the topdata.
+#ifdef Q_BACKINGSTORE_SUBSURFACES
+ Q_ASSERT(topLevel->d_func()->topData()->windowSurface == windowSurface);
+#endif
+ topLevel->d_func()->topData()->windowSurface = 0;
+
+ // Ensure all existing subsurfaces and static widgets are added to their respective lists.
+ updateLists(topLevel);
+}
+
+QWidgetBackingStore::~QWidgetBackingStore()
+{
+ delete windowSurface;
+ windowSurface = 0;
+ delete dirtyOnScreenWidgets;
+ dirtyOnScreenWidgets = 0;
+}
+
+//parent's coordinates; move whole rect; update parent and widget
+//assume the screen blt has already been done, so we don't need to refresh that part
+void QWidgetPrivate::moveRect(const QRect &rect, int dx, int dy)
+{
+ Q_Q(QWidget);
+ if (!q->isVisible())
+ return;
+
+ QWidget *tlw = q->window();
+ QTLWExtra* x = tlw->d_func()->topData();
+ if (x->inTopLevelResize)
+ return;
+
+ static int accelEnv = -1;
+ if (accelEnv == -1) {
+ accelEnv = qgetenv("QT_NO_FAST_MOVE").toInt() == 0;
+ }
+
+ QWidget *pw = q->parentWidget();
+ QPoint toplevelOffset = pw->mapTo(tlw, QPoint());
+ QWidgetPrivate *pd = pw->d_func();
+ QRect clipR(pd->clipRect());
+#ifdef Q_WS_QWS
+ QWidgetBackingStore *wbs = x->backingStore;
+ QWSWindowSurface *surface = static_cast<QWSWindowSurface*>(wbs->windowSurface);
+ clipR = clipR.intersected(surface->clipRegion().translated(-toplevelOffset).boundingRect());
+#endif
+ const QRect newRect(rect.translated(dx, dy));
+ QRect destRect = rect.intersected(clipR);
+ if (destRect.isValid())
+ destRect = destRect.translated(dx, dy).intersected(clipR);
+ const QRect sourceRect(destRect.translated(-dx, -dy));
+ const QRect parentRect(rect & clipR);
+
+ bool accelerateMove = accelEnv && isOpaque
+#ifndef QT_NO_GRAPHICSCVIEW
+ // No accelerate move for proxy widgets.
+ && !tlw->d_func()->extra->proxyWidget
+#endif
+ && !isOverlapped(sourceRect) && !isOverlapped(destRect);
+
+ if (!accelerateMove) {
+ QRegion parentR(parentRect);
+ if (!extra || !extra->hasMask) {
+ parentR -= newRect;
+ } else {
+ // invalidateBuffer() excludes anything outside the mask
+ parentR += newRect & clipR;
+ }
+ pd->invalidateBuffer(parentR);
+ invalidateBuffer((newRect & clipR).translated(-data.crect.topLeft()));
+ } else {
+
+ QWidgetBackingStore *wbs = x->backingStore;
+ QRegion childExpose(newRect & clipR);
+
+ if (sourceRect.isValid() && wbs->bltRect(sourceRect, dx, dy, pw))
+ childExpose -= destRect;
+
+ if (!pw->updatesEnabled())
+ return;
+
+ const bool childUpdatesEnabled = q->updatesEnabled();
+ if (childUpdatesEnabled && !childExpose.isEmpty()) {
+ childExpose.translate(-data.crect.topLeft());
+ wbs->markDirty(childExpose, q);
+ isMoved = true;
+ }
+
+ QRegion parentExpose(parentRect);
+ parentExpose -= newRect;
+ if (extra && extra->hasMask)
+ parentExpose += QRegion(newRect) - extra->mask.translated(data.crect.topLeft());
+
+ if (!parentExpose.isEmpty()) {
+ wbs->markDirty(parentExpose, pw);
+ pd->isMoved = true;
+ }
+
+ if (childUpdatesEnabled) {
+ QRegion needsFlush(sourceRect);
+ needsFlush += destRect;
+ wbs->markDirtyOnScreen(needsFlush, pw, toplevelOffset);
+ }
+ }
+}
+
+//widget's coordinates; scroll within rect; only update widget
+void QWidgetPrivate::scrollRect(const QRect &rect, int dx, int dy)
+{
+ Q_Q(QWidget);
+ QWidget *tlw = q->window();
+ QTLWExtra* x = tlw->d_func()->topData();
+ if (x->inTopLevelResize)
+ return;
+
+ QWidgetBackingStore *wbs = x->backingStore;
+
+ static int accelEnv = -1;
+ if (accelEnv == -1) {
+ accelEnv = qgetenv("QT_NO_FAST_SCROLL").toInt() == 0;
+ }
+
+ QRect scrollRect = rect & clipRect();
+ bool overlapped = false;
+ bool accelerateScroll = accelEnv && isOpaque
+ && !(overlapped = isOverlapped(scrollRect.translated(data.crect.topLeft())));
+
+#if defined(Q_WS_QWS)
+ QWSWindowSurface *surface;
+ surface = static_cast<QWSWindowSurface*>(wbs->windowSurface);
+
+ if (accelerateScroll && !surface->isBuffered()) {
+ const QRegion surfaceClip = surface->clipRegion();
+ const QRegion outsideClip = QRegion(rect) - surfaceClip;
+ if (!outsideClip.isEmpty()) {
+ const QVector<QRect> clipped = (surfaceClip & rect).rects();
+ if (clipped.size() < 8) {
+ for (int i = 0; i < clipped.size(); ++i)
+ this->scrollRect(clipped.at(i), dx, dy);
+ return;
+ } else {
+ accelerateScroll = false;
+ }
+ }
+ }
+#endif // Q_WS_QWS
+
+ if (!accelerateScroll) {
+ if (overlapped) {
+ QRegion region(scrollRect);
+ subtractOpaqueSiblings(region);
+ invalidateBuffer(region);
+ }else {
+ invalidateBuffer(scrollRect);
+ }
+ } else {
+ const QPoint toplevelOffset = q->mapTo(tlw, QPoint());
+#ifdef Q_WS_QWS
+ QWSWindowSurface *surface = static_cast<QWSWindowSurface*>(wbs->windowSurface);
+ const QRegion clip = surface->clipRegion().translated(-toplevelOffset) & scrollRect;
+ const QRect clipBoundingRect = clip.boundingRect();
+ scrollRect &= clipBoundingRect;
+#endif
+ const QRect destRect = scrollRect.translated(dx, dy) & scrollRect;
+ const QRect sourceRect = destRect.translated(-dx, -dy);
+
+ QRegion childExpose(scrollRect);
+ if (sourceRect.isValid()) {
+ if (wbs->bltRect(sourceRect, dx, dy, q))
+ childExpose -= destRect;
+ }
+
+ if (inDirtyList) {
+ if (rect == q->rect()) {
+ dirty.translate(dx, dy);
+ } else {
+ QRegion dirtyScrollRegion = dirty.intersected(scrollRect);
+ if (!dirtyScrollRegion.isEmpty()) {
+ dirty -= dirtyScrollRegion;
+ dirtyScrollRegion.translate(dx, dy);
+ dirty += dirtyScrollRegion;
+ }
+ }
+ }
+
+ if (!q->updatesEnabled())
+ return;
+
+ if (!childExpose.isEmpty()) {
+ wbs->markDirty(childExpose, q);
+ isScrolled = true;
+ }
+
+ // Instead of using native scroll-on-screen, we copy from
+ // backingstore, giving only one screen update for each
+ // scroll, and a solid appearance
+ wbs->markDirtyOnScreen(destRect, q, toplevelOffset);
+ }
+}
+
+static inline bool discardSyncRequest(QWidget *tlw, QTLWExtra *tlwExtra)
+{
+ if (!tlw || !tlwExtra)
+ return true;
+
+#ifdef Q_WS_X11
+ // Delay the sync until we get an Expose event from X11 (initial show).
+ // Qt::WA_Mapped is set to true, but the actual mapping has not yet occurred.
+ // However, we must repaint immediately regardless of the state if someone calls repaint().
+ if (tlwExtra->waitingForMapNotify && !tlwExtra->inRepaint)
+ return true;
+#endif
+
+ if (!tlw->testAttribute(Qt::WA_Mapped))
+ return true;
+
+ if (!tlw->isVisible()
+#ifndef Q_WS_X11
+ // If we're minimized on X11, WA_Mapped will be false and we
+ // will return in the case above. Some window managers on X11
+ // sends us the PropertyNotify to change the minimized state
+ // *AFTER* we've received the expose event, which is baaad.
+ || tlw->isMinimized()
+#endif
+ )
+ return true;
+
+ return false;
+}
+
+/*!
+ Synchronizes the \a exposedRegion of the \a exposedWidget with the backing store.
+
+ If there's nothing to repaint, the area is flushed and painting does not occur;
+ otherwise the area is marked as dirty on screen and will be flushed right after
+ we are done with all painting.
+*/
+void QWidgetBackingStore::sync(QWidget *exposedWidget, const QRegion &exposedRegion)
+{
+ QTLWExtra *tlwExtra = tlw->d_func()->maybeTopData();
+ if (discardSyncRequest(tlw, tlwExtra) || tlwExtra->inTopLevelResize)
+ return;
+
+ if (!exposedWidget || !exposedWidget->internalWinId() || !exposedWidget->isVisible()
+ || !exposedWidget->updatesEnabled() || exposedRegion.isEmpty()) {
+ return;
+ }
+
+ // Nothing to repaint.
+ if (!isDirty()) {
+ qt_flush(exposedWidget, exposedRegion, windowSurface, tlw, tlwOffset);
+ return;
+ }
+
+ if (exposedWidget != tlw)
+ markDirtyOnScreen(exposedRegion, exposedWidget, exposedWidget->mapTo(tlw, QPoint()));
+ else
+ markDirtyOnScreen(exposedRegion, exposedWidget, QPoint());
+ sync();
+}
+
+/*!
+ Synchronizes the backing store, i.e. dirty areas are repainted and flushed.
+*/
+void QWidgetBackingStore::sync()
+{
+ QTLWExtra *tlwExtra = tlw->d_func()->maybeTopData();
+ if (discardSyncRequest(tlw, tlwExtra)) {
+ // If the top-level is minimized, it's not visible on the screen so we can delay the
+ // update until it's shown again. In order to do that we must keep the dirty states.
+ // These will be cleared when we receive the first expose after showNormal().
+ // However, if the widget is not visible (isVisible() returns false), everything will
+ // be invalidated once the widget is shown again, so clear all dirty states.
+ if (!tlw->isVisible()) {
+ dirty = QRegion();
+ for (int i = 0; i < dirtyWidgets.size(); ++i)
+ resetWidget(dirtyWidgets.at(i));
+ dirtyWidgets.clear();
+ }
+ return;
+ }
+
+ const bool inTopLevelResize = tlwExtra->inTopLevelResize;
+ const bool updatesDisabled = !tlw->updatesEnabled();
+ const QRect tlwRect(topLevelRect());
+ const QRect surfaceGeometry(windowSurface->geometry());
+ bool repaintAllWidgets = false;
+
+ if (inTopLevelResize || surfaceGeometry != tlwRect) {
+ if ((inTopLevelResize || surfaceGeometry.size() != tlwRect.size()) && !updatesDisabled) {
+ if (hasStaticContents()) {
+ // Repaint existing dirty area and newly visible area.
+ const QRect clipRect(0, 0, surfaceGeometry.width(), surfaceGeometry.height());
+ const QRegion staticRegion(staticContents(0, clipRect));
+ QRegion newVisible(0, 0, tlwRect.width(), tlwRect.height());
+ newVisible -= staticRegion;
+ dirty += newVisible;
+ windowSurface->setStaticContents(staticRegion);
+ } else {
+ // Repaint everything.
+ dirty = QRegion(0, 0, tlwRect.width(), tlwRect.height());
+ for (int i = 0; i < dirtyWidgets.size(); ++i)
+ resetWidget(dirtyWidgets.at(i));
+ dirtyWidgets.clear();
+ repaintAllWidgets = true;
+ }
+ }
+ windowSurface->setGeometry(tlwRect);
+ }
+
+ if (updatesDisabled)
+ return;
+
+ if (hasDirtyFromPreviousSync)
+ dirty += dirtyFromPreviousSync;
+
+ // Contains everything that needs repaint.
+ QRegion toClean(dirty);
+
+ // Loop through all update() widgets and remove them from the list before they are
+ // painted (in case someone calls update() in paintEvent). If the widget is opaque
+ // and does not have transparent overlapping siblings, append it to the
+ // opaqueNonOverlappedWidgets list and paint it directly without composition.
+ QVarLengthArray<QWidget *, 32> opaqueNonOverlappedWidgets;
+ for (int i = 0; i < dirtyWidgets.size(); ++i) {
+ QWidget *w = dirtyWidgets.at(i);
+ QWidgetPrivate *wd = w->d_func();
+ if (wd->data.in_destructor)
+ continue;
+
+ // Clip with mask() and clipRect().
+ wd->dirty &= wd->clipRect();
+ wd->clipToEffectiveMask(wd->dirty);
+
+ // Subtract opaque siblings and children.
+ bool hasDirtySiblingsAbove = false;
+ // We know for sure that the widget isn't overlapped if 'isMoved' is true.
+ if (!wd->isMoved)
+ wd->subtractOpaqueSiblings(wd->dirty, &hasDirtySiblingsAbove);
+ // Scrolled and moved widgets must draw all children.
+ if (!wd->isScrolled && !wd->isMoved)
+ wd->subtractOpaqueChildren(wd->dirty, w->rect());
+
+ if (wd->dirty.isEmpty()) {
+ resetWidget(w);
+ continue;
+ }
+
+ const QRegion widgetDirty(w != tlw ? wd->dirty.translated(w->mapTo(tlw, QPoint()))
+ : wd->dirty);
+ toClean += widgetDirty;
+
+#ifndef QT_NO_GRAPHICSCVIEW
+ if (tlw->d_func()->extra->proxyWidget) {
+ resetWidget(w);
+ continue;
+ }
+#endif
+
+ if (!hasDirtySiblingsAbove && wd->isOpaque && !dirty.intersects(widgetDirty.boundingRect())) {
+ opaqueNonOverlappedWidgets.append(w);
+ } else {
+ resetWidget(w);
+ dirty += widgetDirty;
+ }
+ }
+ dirtyWidgets.clear();
+
+ if (toClean.isEmpty()) {
+ // Nothing to repaint. However, we might have newly exposed areas on the
+ // screen if this function was called from sync(QWidget *, QRegion)), so
+ // we have to make sure those are flushed.
+ flush();
+ return;
+ }
+
+#ifndef QT_NO_GRAPHICSVIEW
+ if (tlw->d_func()->extra->proxyWidget) {
+ updateStaticContentsSize();
+ dirty = QRegion();
+ const QVector<QRect> rects(toClean.rects());
+ for (int i = 0; i < rects.size(); ++i)
+ tlw->d_func()->extra->proxyWidget->update(rects.at(i));
+ return;
+ }
+#endif
+
+#ifndef Q_BACKINGSTORE_SUBSURFACES
+ BeginPaintInfo beginPaintInfo;
+ beginPaint(toClean, tlw, windowSurface, &beginPaintInfo);
+ if (beginPaintInfo.nothingToPaint) {
+ for (int i = 0; i < opaqueNonOverlappedWidgets.size(); ++i)
+ resetWidget(opaqueNonOverlappedWidgets[i]);
+ dirty = QRegion();
+ return;
+ }
+#endif
+
+ // Must do this before sending any paint events because
+ // the size may change in the paint event.
+ updateStaticContentsSize();
+ const QRegion dirtyCopy(dirty);
+ dirty = QRegion();
+
+ // Paint opaque non overlapped widgets.
+ for (int i = 0; i < opaqueNonOverlappedWidgets.size(); ++i) {
+ QWidget *w = opaqueNonOverlappedWidgets[i];
+ QWidgetPrivate *wd = w->d_func();
+
+ int flags = QWidgetPrivate::DrawRecursive;
+ // Scrolled and moved widgets must draw all children.
+ if (!wd->isScrolled && !wd->isMoved)
+ flags |= QWidgetPrivate::DontDrawOpaqueChildren;
+ if (w == tlw)
+ flags |= QWidgetPrivate::DrawAsRoot;
+
+ QRegion toBePainted(wd->dirty);
+ resetWidget(w);
+
+#ifdef Q_BACKINGSTORE_SUBSURFACES
+ QWindowSurface *subSurface = w->windowSurface();
+ BeginPaintInfo beginPaintInfo;
+ beginPaint(toBePainted, w, subSurface, &beginPaintInfo, false);
+ if (beginPaintInfo.nothingToPaint)
+ continue;
+
+ if (beginPaintInfo.windowSurfaceRecreated) {
+ // Eep the window surface has changed. The old one may have been
+ // deleted, in which case we will segfault on the call to
+ // painterOffset() below. Use the new window surface instead.
+ subSurface = w->windowSurface();
+ }
+
+ QPoint offset(tlwOffset);
+ if (subSurface == windowSurface)
+ offset += w->mapTo(tlw, QPoint());
+ else
+ offset = static_cast<QWSWindowSurface*>(subSurface)->painterOffset();
+ wd->drawWidget(subSurface->paintDevice(), toBePainted, offset, flags, 0, this);
+
+ endPaint(toBePainted, subSurface, &beginPaintInfo);
+#else
+ QPoint offset(tlwOffset);
+ if (w != tlw)
+ offset += w->mapTo(tlw, QPoint());
+ wd->drawWidget(windowSurface->paintDevice(), toBePainted, offset, flags, 0, this);
+#endif
+ }
+
+ // Paint the rest with composition.
+#ifndef Q_BACKINGSTORE_SUBSURFACES
+ if (repaintAllWidgets || !dirtyCopy.isEmpty()) {
+ const int flags = QWidgetPrivate::DrawAsRoot | QWidgetPrivate::DrawRecursive;
+ tlw->d_func()->drawWidget(windowSurface->paintDevice(), dirtyCopy, tlwOffset, flags, 0, this);
+ }
+
+ endPaint(toClean, windowSurface, &beginPaintInfo);
+#else
+ if (!repaintAllWidgets && dirtyCopy.isEmpty())
+ return; // Nothing more to paint.
+
+ QList<QWindowSurface *> surfaceList(subSurfaces);
+ surfaceList.prepend(windowSurface);
+ const QRect dirtyBoundingRect(dirtyCopy.boundingRect());
+
+ // Loop through all window surfaces (incl. the top-level surface) and
+ // repaint those intersecting with the bounding rect of the dirty region.
+ for (int i = 0; i < surfaceList.size(); ++i) {
+ QWindowSurface *subSurface = surfaceList.at(i);
+ QWidget *w = subSurface->window();
+ QWidgetPrivate *wd = w->d_func();
+
+ const QRect clipRect = wd->clipRect().translated(w->mapTo(tlw, QPoint()));
+ if (!qRectIntersects(dirtyBoundingRect, clipRect))
+ continue;
+
+ toClean = dirtyCopy;
+ BeginPaintInfo beginPaintInfo;
+ beginPaint(toClean, w, subSurface, &beginPaintInfo);
+ if (beginPaintInfo.nothingToPaint)
+ continue;
+
+ if (beginPaintInfo.windowSurfaceRecreated) {
+ // Eep the window surface has changed. The old one may have been
+ // deleted, in which case we will segfault on the call to
+ // painterOffset() below. Use the new window surface instead.
+ subSurface = w->windowSurface();
+ }
+
+ int flags = QWidgetPrivate::DrawRecursive;
+ if (w == tlw)
+ flags |= QWidgetPrivate::DrawAsRoot;
+ const QPoint painterOffset = static_cast<QWSWindowSurface*>(subSurface)->painterOffset();
+ wd->drawWidget(subSurface->paintDevice(), toClean, painterOffset, flags, 0, this);
+
+ endPaint(toClean, subSurface, &beginPaintInfo);
+ }
+#endif
+}
+
+/*!
+ Flushes the contents of the backing store into the top-level widget.
+ If the \a widget is non-zero, the content is flushed to the \a widget.
+ If the \a surface is non-zero, the content of the \a surface is flushed.
+*/
+void QWidgetBackingStore::flush(QWidget *widget, QWindowSurface *surface)
+{
+#if defined(Q_WS_QWS) && !defined(QT_NO_QWS_MANAGER)
+ paintWindowDecoration();
+#endif
+
+ if (!dirtyOnScreen.isEmpty()) {
+ QWidget *target = widget ? widget : tlw;
+ QWindowSurface *source = surface ? surface : windowSurface;
+ qt_flush(target, dirtyOnScreen, source, tlw, tlwOffset);
+ dirtyOnScreen = QRegion();
+ }
+
+ if (!dirtyOnScreenWidgets || dirtyOnScreenWidgets->isEmpty())
+ return;
+
+ for (int i = 0; i < dirtyOnScreenWidgets->size(); ++i) {
+ QWidget *w = dirtyOnScreenWidgets->at(i);
+ QWidgetPrivate *wd = w->d_func();
+ Q_ASSERT(wd->needsFlush);
+ qt_flush(w, *wd->needsFlush, windowSurface, tlw, tlwOffset);
+ *wd->needsFlush = QRegion();
+ }
+ dirtyOnScreenWidgets->clear();
+}
+
+static inline bool discardInvalidateBufferRequest(QWidget *widget, QTLWExtra *tlwExtra)
+{
+ Q_ASSERT(widget);
+ if (qApp && qApp->closingDown())
+ return true;
+
+ if (!tlwExtra || tlwExtra->inTopLevelResize || !tlwExtra->backingStore)
+ return true;
+
+ if (!widget->isVisible() || !widget->updatesEnabled())
+ return true;
+
+ return false;
+}
+
+/*!
+ Invalidates the buffer when the widget is resized.
+ Static areas are never invalidated unless absolutely needed.
+*/
+void QWidgetPrivate::invalidateBuffer_resizeHelper(const QPoint &oldPos, const QSize &oldSize)
+{
+ Q_Q(QWidget);
+ Q_ASSERT(!q->isWindow());
+ Q_ASSERT(q->parentWidget());
+
+ const bool staticContents = q->testAttribute(Qt::WA_StaticContents);
+ const bool sizeDecreased = (data.crect.width() < oldSize.width())
+ || (data.crect.height() < oldSize.height());
+
+ const QPoint offset(data.crect.x() - oldPos.x(), data.crect.y() - oldPos.y());
+ const bool parentAreaExposed = !offset.isNull() || sizeDecreased;
+ const QRect newWidgetRect(q->rect());
+ const QRect oldWidgetRect(0, 0, oldSize.width(), oldSize.height());
+
+ if (!staticContents) {
+ QRegion staticChildren;
+ QWidgetBackingStore *bs = 0;
+ if (offset.isNull() && (bs = maybeBackingStore()))
+ staticChildren = bs->staticContents(q, oldWidgetRect);
+ const bool hasStaticChildren = !staticChildren.isEmpty();
+
+ if (hasStaticChildren) {
+ QRegion dirty(newWidgetRect);
+ dirty -= staticChildren;
+ invalidateBuffer(dirty);
+ } else {
+ // Entire widget needs repaint.
+ invalidateBuffer(newWidgetRect);
+ }
+
+ if (!parentAreaExposed)
+ return;
+
+ // Invalidate newly exposed area of the parent.
+ if (extra && extra->hasMask) {
+ QRegion parentExpose(extra->mask.translated(oldPos));
+ parentExpose &= QRect(oldPos, oldSize);
+ if (hasStaticChildren)
+ parentExpose -= data.crect; // Offset is unchanged, safe to do this.
+ q->parentWidget()->d_func()->invalidateBuffer(parentExpose);
+ } else {
+ if (hasStaticChildren) {
+ QRegion parentExpose(QRect(oldPos, oldSize));
+ parentExpose -= data.crect; // Offset is unchanged, safe to do this.
+ q->parentWidget()->d_func()->invalidateBuffer(parentExpose);
+ } else {
+ q->parentWidget()->d_func()->invalidateBuffer(QRect(oldPos, oldSize));
+ }
+ }
+ return;
+ }
+
+ // Move static content to its new position.
+ if (!offset.isNull()) {
+ if (sizeDecreased) {
+ const QSize minSize(qMin(oldSize.width(), data.crect.width()),
+ qMin(oldSize.height(), data.crect.height()));
+ moveRect(QRect(oldPos, minSize), offset.x(), offset.y());
+ } else {
+ moveRect(QRect(oldPos, oldSize), offset.x(), offset.y());
+ }
+ }
+
+ // Invalidate newly visible area of the widget.
+ if (!sizeDecreased || !oldWidgetRect.contains(newWidgetRect)) {
+ QRegion newVisible(newWidgetRect);
+ newVisible -= oldWidgetRect;
+ invalidateBuffer(newVisible);
+ }
+
+ if (!parentAreaExposed)
+ return;
+
+ // Invalidate newly exposed area of the parent.
+ const QRect oldRect(oldPos, oldSize);
+ if (extra && extra->hasMask) {
+ QRegion parentExpose(oldRect);
+ parentExpose &= extra->mask.translated(oldPos);
+ parentExpose -= (extra->mask.translated(data.crect.topLeft()) & data.crect);
+ q->parentWidget()->d_func()->invalidateBuffer(parentExpose);
+ } else {
+ QRegion parentExpose(oldRect);
+ parentExpose -= data.crect;
+ q->parentWidget()->d_func()->invalidateBuffer(parentExpose);
+ }
+}
+
+/*!
+ Invalidates the \a rgn (in widget's coordinates) of the backing store, i.e.
+ all widgets intersecting with the region will be repainted when the backing store
+ is synced.
+
+ ### Qt 4.6: Merge into a template function (after MSVC isn't supported anymore).
+*/
+void QWidgetPrivate::invalidateBuffer(const QRegion &rgn)
+{
+ Q_Q(QWidget);
+
+ QTLWExtra *tlwExtra = q->window()->d_func()->maybeTopData();
+ if (discardInvalidateBufferRequest(q, tlwExtra) || rgn.isEmpty())
+ return;
+
+ QRegion wrgn(rgn);
+ wrgn &= clipRect();
+ if (extra && extra->hasMask)
+ wrgn &= extra->mask;
+ if (wrgn.isEmpty())
+ return;
+
+ tlwExtra->backingStore->markDirty(wrgn, q, false, true);
+}
+
+/*!
+ This function is equivalent to calling invalidateBuffer(QRegion(rect), ...), but
+ is more efficient as it eliminates QRegion operations/allocations and can
+ use the rect more precisely for additional cut-offs.
+
+ ### Qt 4.6: Merge into a template function (after MSVC isn't supported anymore).
+*/
+void QWidgetPrivate::invalidateBuffer(const QRect &rect)
+{
+ Q_Q(QWidget);
+
+ QTLWExtra *tlwExtra = q->window()->d_func()->maybeTopData();
+ if (discardInvalidateBufferRequest(q, tlwExtra) || rect.isEmpty())
+ return;
+
+ QRect wRect(rect);
+ wRect &= clipRect();
+ if (wRect.isEmpty())
+ return;
+
+ if (!extra || !extra->hasMask) {
+ tlwExtra->backingStore->markDirty(wRect, q, false, true);
+ return;
+ }
+
+ QRegion wRgn(extra->mask);
+ wRgn &= wRect;
+ if (wRgn.isEmpty())
+ return;
+
+ tlwExtra->backingStore->markDirty(wRgn, q, false, true);
+}
+
+void QWidgetPrivate::repaint_sys(const QRegion &rgn)
+{
+ Q_Q(QWidget);
+ if (q->testAttribute(Qt::WA_StaticContents)) {
+ if (!extra)
+ createExtra();
+ extra->staticContentsSize = data.crect.size();
+ }
+
+#ifdef Q_WS_MAC
+ // No difference between update() and repaint() on the Mac.
+ update_sys(rgn);
+ return;
+#endif
+
+ QRegion toBePainted(rgn);
+ toBePainted &= clipRect();
+ clipToEffectiveMask(toBePainted);
+ if (toBePainted.isEmpty())
+ return; // Nothing to repaint.
+
+#ifndef QT_NO_PAINT_DEBUG
+ bool flushed = QWidgetBackingStore::flushPaint(q, toBePainted);
+#endif
+
+ drawWidget(q, toBePainted, QPoint(), QWidgetPrivate::DrawAsRoot | QWidgetPrivate::DrawPaintOnScreen, 0);
+
+#ifndef QT_NO_PAINT_DEBUG
+ if (flushed)
+ QWidgetBackingStore::unflushPaint(q, toBePainted);
+#endif
+
+ if (!q->testAttribute(Qt::WA_PaintOutsidePaintEvent) && q->paintingActive())
+ qWarning("QWidget::repaint: It is dangerous to leave painters active on a widget outside of the PaintEvent");
+}
+
+
+QT_END_NAMESPACE
diff --git a/src/gui/painting/qbackingstore_p.h b/src/gui/painting/qbackingstore_p.h
new file mode 100644
index 0000000000..34131758bc
--- /dev/null
+++ b/src/gui/painting/qbackingstore_p.h
@@ -0,0 +1,268 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QBACKINGSTORE_P_H
+#define QBACKINGSTORE_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 <QDebug>
+#include <QtGui/qwidget.h>
+#include <private/qwidget_p.h>
+#include <private/qwindowsurface_p.h>
+#ifdef Q_WS_QWS
+#include <private/qwindowsurface_qws_p.h>
+#endif
+
+QT_BEGIN_NAMESPACE
+
+class QWindowSurface;
+
+struct BeginPaintInfo {
+ inline BeginPaintInfo() : wasFlushed(0), nothingToPaint(0), windowSurfaceRecreated(0) {}
+ uint wasFlushed : 1;
+ uint nothingToPaint : 1;
+ uint windowSurfaceRecreated : 1;
+};
+
+class Q_AUTOTEST_EXPORT QWidgetBackingStore
+{
+public:
+ QWidgetBackingStore(QWidget *t);
+ ~QWidgetBackingStore();
+
+ static void showYellowThing(QWidget *widget, const QRegion &rgn, int msec, bool);
+
+ void sync(QWidget *exposedWidget, const QRegion &exposedRegion);
+ void sync();
+ void flush(QWidget *widget = 0, QWindowSurface *surface = 0);
+
+ inline QPoint topLevelOffset() const { return tlwOffset; }
+
+ QWindowSurface *surface() const { return windowSurface; }
+
+ inline bool isDirty() const
+ {
+ return !(dirtyWidgets.isEmpty() && dirty.isEmpty() && !hasDirtyFromPreviousSync
+#if defined(Q_WS_QWS) && !defined(QT_NO_QWS_MANAGER)
+ && !hasDirtyWindowDecoration()
+#endif
+ );
+ }
+
+private:
+ QWidget *tlw;
+ QRegion dirtyOnScreen; // needsFlush
+ QRegion dirty; // needsRepaint
+ QRegion dirtyFromPreviousSync;
+ QVector<QWidget *> dirtyWidgets;
+ QVector<QWidget *> *dirtyOnScreenWidgets;
+ QList<QWidget *> staticWidgets;
+ QWindowSurface *windowSurface;
+#ifdef Q_BACKINGSTORE_SUBSURFACES
+ QList<QWindowSurface*> subSurfaces;
+#endif
+ bool hasDirtyFromPreviousSync;
+
+ QPoint tlwOffset;
+
+ static bool flushPaint(QWidget *widget, const QRegion &rgn);
+ static void unflushPaint(QWidget *widget, const QRegion &rgn);
+
+ bool bltRect(const QRect &rect, int dx, int dy, QWidget *widget);
+ void releaseBuffer();
+
+ void beginPaint(QRegion &toClean, QWidget *widget, QWindowSurface *windowSurface,
+ BeginPaintInfo *returnInfo, bool toCleanIsInTopLevelCoordinates = true);
+ void endPaint(const QRegion &cleaned, QWindowSurface *windowSurface, BeginPaintInfo *beginPaintInfo);
+
+ QRegion dirtyRegion(QWidget *widget = 0) const;
+ QRegion staticContents(QWidget *widget = 0, const QRect &withinClipRect = QRect()) const;
+
+ // ### Qt 4.6: Merge into a template function (after MSVC isn't supported anymore).
+ void markDirty(const QRegion &rgn, QWidget *widget, bool updateImmediately = false,
+ bool invalidateBuffer = false);
+ void markDirty(const QRect &rect, QWidget *widget, bool updateImmediately = false,
+ bool invalidateBuffer = false);
+ void markDirtyOnScreen(const QRegion &dirtyOnScreen, QWidget *widget, const QPoint &topLevelOffset);
+
+ void removeDirtyWidget(QWidget *w);
+
+#if defined(Q_WS_QWS) && !defined(QT_NO_QWS_MANAGER)
+ bool hasDirtyWindowDecoration() const;
+ void paintWindowDecoration();
+#endif
+ void updateLists(QWidget *widget);
+
+ inline void addDirtyWidget(QWidget *widget, const QRegion &rgn)
+ {
+ if (widget && !widget->d_func()->inDirtyList && !widget->data->in_destructor) {
+ widget->d_func()->dirty = rgn;
+ dirtyWidgets.append(widget);
+ widget->d_func()->inDirtyList = true;
+ }
+ }
+
+ inline void dirtyWidgetsRemoveAll(QWidget *widget)
+ {
+ int i = 0;
+ while (i < dirtyWidgets.size()) {
+ if (dirtyWidgets.at(i) == widget)
+ dirtyWidgets.remove(i);
+ else
+ ++i;
+ }
+ }
+
+ inline void addStaticWidget(QWidget *widget)
+ {
+ if (!widget)
+ return;
+
+ Q_ASSERT(widget->testAttribute(Qt::WA_StaticContents));
+ if (!staticWidgets.contains(widget))
+ staticWidgets.append(widget);
+ }
+
+ inline void removeStaticWidget(QWidget *widget)
+ { staticWidgets.removeAll(widget); }
+
+ // Move the reparented widget and all its static children from this backing store
+ // to the new backing store if reparented into another top-level / backing store.
+ inline void moveStaticWidgets(QWidget *reparented)
+ {
+ Q_ASSERT(reparented);
+ QWidgetBackingStore *newBs = reparented->d_func()->maybeBackingStore();
+ if (newBs == this)
+ return;
+
+ int i = 0;
+ while (i < staticWidgets.size()) {
+ QWidget *w = staticWidgets.at(i);
+ if (reparented == w || reparented->isAncestorOf(w)) {
+ staticWidgets.removeAt(i);
+ if (newBs)
+ newBs->addStaticWidget(w);
+ } else {
+ ++i;
+ }
+ }
+ }
+
+ inline QRect topLevelRect() const
+ {
+#ifdef Q_WS_QWS
+ return tlw->frameGeometry();
+#endif
+ return tlw->data->crect;
+ }
+
+ inline void appendDirtyOnScreenWidget(QWidget *widget)
+ {
+ if (!widget)
+ return;
+
+ if (!dirtyOnScreenWidgets) {
+ dirtyOnScreenWidgets = new QVector<QWidget *>;
+ dirtyOnScreenWidgets->append(widget);
+ } else if (!dirtyOnScreenWidgets->contains(widget)) {
+ dirtyOnScreenWidgets->append(widget);
+ }
+ }
+
+ inline void dirtyOnScreenWidgetsRemoveAll(QWidget *widget)
+ {
+ if (!widget || !dirtyOnScreenWidgets)
+ return;
+
+ int i = 0;
+ while (i < dirtyOnScreenWidgets->size()) {
+ if (dirtyOnScreenWidgets->at(i) == widget)
+ dirtyOnScreenWidgets->remove(i);
+ else
+ ++i;
+ }
+ }
+
+ inline void resetWidget(QWidget *widget)
+ {
+ if (widget) {
+ widget->d_func()->inDirtyList = false;
+ widget->d_func()->isScrolled = false;
+ widget->d_func()->isMoved = false;
+ widget->d_func()->dirty = QRegion();
+ }
+ }
+
+ inline void updateStaticContentsSize()
+ {
+ for (int i = 0; i < staticWidgets.size(); ++i) {
+ QWidgetPrivate *wd = staticWidgets.at(i)->d_func();
+ if (!wd->extra)
+ wd->createExtra();
+ wd->extra->staticContentsSize = wd->data.crect.size();
+ }
+ }
+
+ inline bool hasStaticContents() const
+ { return !staticWidgets.isEmpty() && windowSurface->hasStaticContentsSupport(); }
+
+ friend QRegion qt_dirtyRegion(QWidget *);
+ friend class QWidgetPrivate;
+ friend class QWidget;
+ friend class QWSManagerPrivate;
+ friend class QETWidget;
+ friend class QWindowSurface;
+ friend class QWSWindowSurface;
+};
+
+QT_END_NAMESPACE
+
+#endif // QBACKINGSTORE_P_H
diff --git a/src/gui/painting/qbezier.cpp b/src/gui/painting/qbezier.cpp
new file mode 100644
index 0000000000..8317dd8220
--- /dev/null
+++ b/src/gui/painting/qbezier.cpp
@@ -0,0 +1,1245 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qbezier_p.h"
+#include <qdebug.h>
+#include <qline.h>
+#include <qpolygon.h>
+#include <qvector.h>
+#include <qlist.h>
+#include <qmath.h>
+
+#include <private/qnumeric_p.h>
+#include <private/qmath_p.h>
+
+QT_BEGIN_NAMESPACE
+
+//#define QDEBUG_BEZIER
+
+#ifdef FLOAT_ACCURACY
+#define INV_EPS (1L<<23)
+#else
+/* The value of 1.0 / (1L<<14) is enough for most applications */
+#define INV_EPS (1L<<14)
+#endif
+
+#ifndef M_SQRT2
+#define M_SQRT2 1.41421356237309504880
+#endif
+
+#define log2(x) (qLn(x)/qLn(2.))
+
+static inline qreal log4(qreal x)
+{
+ return qreal(0.5) * log2(x);
+}
+
+/*!
+ \internal
+*/
+QBezier QBezier::fromPoints(const QPointF &p1, const QPointF &p2,
+ const QPointF &p3, const QPointF &p4)
+{
+ QBezier b;
+ b.x1 = p1.x();
+ b.y1 = p1.y();
+ b.x2 = p2.x();
+ b.y2 = p2.y();
+ b.x3 = p3.x();
+ b.y3 = p3.y();
+ b.x4 = p4.x();
+ b.y4 = p4.y();
+ return b;
+}
+
+/*!
+ \internal
+*/
+QPolygonF QBezier::toPolygon() const
+{
+ // flattening is done by splitting the bezier until we can replace the segment by a straight
+ // line. We split further until the control points are close enough to the line connecting the
+ // boundary points.
+ //
+ // the Distance of a point p from a line given by the points (a,b) is given by:
+ //
+ // d = abs( (bx - ax)(ay - py) - (by - ay)(ax - px) ) / line_length
+ //
+ // We can stop splitting if both control points are close enough to the line.
+ // To make the algorithm faster we use the manhattan length of the line.
+
+ QPolygonF polygon;
+ polygon.append(QPointF(x1, y1));
+ addToPolygon(&polygon);
+ return polygon;
+}
+
+//0.5 is really low
+static const qreal flatness = 0.5;
+
+//based on "Fast, precise flattening of cubic Bezier path and offset curves"
+// by T. F. Hain, A. L. Ahmad, S. V. R. Racherla and D. D. Langan
+static inline void flattenBezierWithoutInflections(QBezier &bez,
+ QPolygonF *&p)
+{
+ QBezier left;
+
+ while (1) {
+ qreal dx = bez.x2 - bez.x1;
+ qreal dy = bez.y2 - bez.y1;
+
+ qreal normalized = qSqrt(dx * dx + dy * dy);
+ if (qFuzzyCompare(normalized + 1, 1))
+ break;
+
+ qreal d = qAbs(dx * (bez.y3 - bez.y2) - dy * (bez.x3 - bez.x2));
+
+ qreal t = qSqrt(4. / 3. * normalized * flatness / d);
+ if (t > 1 || qFuzzyCompare(t, (qreal)1.))
+ break;
+ bez.parameterSplitLeft(t, &left);
+ p->append(bez.pt1());
+ }
+}
+
+
+static inline int quadraticRoots(qreal a, qreal b, qreal c,
+ qreal *x1, qreal *x2)
+{
+ if (qFuzzyCompare(a + 1, 1)) {
+ if (qFuzzyCompare(b + 1, 1))
+ return 0;
+ *x1 = *x2 = (-c / b);
+ return 1;
+ } else {
+ const qreal det = b * b - 4 * a * c;
+ if (qFuzzyCompare(det + 1, 1)) {
+ *x1 = *x2 = -b / (2 * a);
+ return 1;
+ }
+ if (det > 0) {
+ if (qFuzzyCompare(b + 1, 1)) {
+ *x2 = qSqrt(-c / a);
+ *x1 = -(*x2);
+ return 2;
+ }
+ const qreal stableA = b / (2 * a);
+ const qreal stableB = c / (a * stableA * stableA);
+ const qreal stableC = -1 - qSqrt(1 - stableB);
+ *x2 = stableA * stableC;
+ *x1 = (stableA * stableB) / stableC;
+ return 2;
+ } else
+ return 0;
+ }
+}
+
+static inline bool findInflections(qreal a, qreal b, qreal c,
+ qreal *t1 , qreal *t2, qreal *tCups)
+{
+ qreal r1 = 0, r2 = 0;
+
+ short rootsCount = quadraticRoots(a, b, c, &r1, &r2);
+
+ if (rootsCount >= 1) {
+ if (r1 < r2) {
+ *t1 = r1;
+ *t2 = r2;
+ } else {
+ *t1 = r2;
+ *t2 = r1;
+ }
+ if (!qFuzzyCompare(a + 1, 1))
+ *tCups = 0.5 * (-b / a);
+ else
+ *tCups = 2;
+
+ return true;
+ }
+
+ return false;
+}
+
+
+void QBezier::addToPolygon(QPolygonF *polygon) const
+{
+ QBezier beziers[32];
+ beziers[0] = *this;
+ QBezier *b = beziers;
+
+ while (b >= beziers) {
+ // check if we can pop the top bezier curve from the stack
+ qreal y4y1 = b->y4 - b->y1;
+ qreal x4x1 = b->x4 - b->x1;
+ qreal l = qAbs(x4x1) + qAbs(y4y1);
+ qreal d;
+ if (l > 1.) {
+ d = qAbs( (x4x1)*(b->y1 - b->y2) - (y4y1)*(b->x1 - b->x2) )
+ + qAbs( (x4x1)*(b->y1 - b->y3) - (y4y1)*(b->x1 - b->x3) );
+ } else {
+ d = qAbs(b->x1 - b->x2) + qAbs(b->y1 - b->y2) +
+ qAbs(b->x1 - b->x3) + qAbs(b->y1 - b->y3);
+ l = 1.;
+ }
+ if (d < flatness*l || b == beziers + 31) {
+ // good enough, we pop it off and add the endpoint
+ polygon->append(QPointF(b->x4, b->y4));
+ --b;
+ } else {
+ // split, second half of the polygon goes lower into the stack
+ b->split(b+1, b);
+ ++b;
+ }
+ }
+}
+
+void QBezier::addToPolygonMixed(QPolygonF *polygon) const
+{
+ qreal ax = -x1 + 3*x2 - 3*x3 + x4;
+ qreal ay = -y1 + 3*y2 - 3*y3 + y4;
+ qreal bx = 3*x1 - 6*x2 + 3*x3;
+ qreal by = 3*y1 - 6*y2 + 3*y3;
+ qreal cx = -3*x1 + 3*x2;
+ qreal cy = -3*y1 + 2*y2;
+ qreal a = 6 * (ay * bx - ax * by);
+ qreal b = 6 * (ay * cx - ax * cy);
+ qreal c = 2 * (by * cx - bx * cy);
+
+ if ((qFuzzyCompare(a + 1, 1) && qFuzzyCompare(b + 1, 1)) ||
+ (b * b - 4 * a *c) < 0) {
+ QBezier bez(*this);
+ flattenBezierWithoutInflections(bez, polygon);
+ polygon->append(QPointF(x4, y4));
+ } else {
+ QBezier beziers[32];
+ beziers[0] = *this;
+ QBezier *b = beziers;
+
+ while (b >= beziers) {
+ // check if we can pop the top bezier curve from the stack
+ qreal y4y1 = b->y4 - b->y1;
+ qreal x4x1 = b->x4 - b->x1;
+ qreal l = qAbs(x4x1) + qAbs(y4y1);
+ qreal d;
+ if (l > 1.) {
+ d = qAbs( (x4x1)*(b->y1 - b->y2) - (y4y1)*(b->x1 - b->x2) )
+ + qAbs( (x4x1)*(b->y1 - b->y3) - (y4y1)*(b->x1 - b->x3) );
+ } else {
+ d = qAbs(b->x1 - b->x2) + qAbs(b->y1 - b->y2) +
+ qAbs(b->x1 - b->x3) + qAbs(b->y1 - b->y3);
+ l = 1.;
+ }
+ if (d < .5*l || b == beziers + 31) {
+ // good enough, we pop it off and add the endpoint
+ polygon->append(QPointF(b->x4, b->y4));
+ --b;
+ } else {
+ // split, second half of the polygon goes lower into the stack
+ b->split(b+1, b);
+ ++b;
+ }
+ }
+ }
+}
+
+QRectF QBezier::bounds() const
+{
+ qreal xmin = x1;
+ qreal xmax = x1;
+ if (x2 < xmin)
+ xmin = x2;
+ else if (x2 > xmax)
+ xmax = x2;
+ if (x3 < xmin)
+ xmin = x3;
+ else if (x3 > xmax)
+ xmax = x3;
+ if (x4 < xmin)
+ xmin = x4;
+ else if (x4 > xmax)
+ xmax = x4;
+
+ qreal ymin = y1;
+ qreal ymax = y1;
+ if (y2 < ymin)
+ ymin = y2;
+ else if (y2 > ymax)
+ ymax = y2;
+ if (y3 < ymin)
+ ymin = y3;
+ else if (y3 > ymax)
+ ymax = y3;
+ if (y4 < ymin)
+ ymin = y4;
+ else if (y4 > ymax)
+ ymax = y4;
+ return QRectF(xmin, ymin, xmax-xmin, ymax-ymin);
+}
+
+
+enum ShiftResult {
+ Ok,
+ Discard,
+ Split,
+ Circle
+};
+
+static ShiftResult good_offset(const QBezier *b1, const QBezier *b2, qreal offset, qreal threshold)
+{
+ const qreal o2 = offset*offset;
+ const qreal max_dist_line = threshold*offset*offset;
+ const qreal max_dist_normal = threshold*offset;
+ const qreal spacing = 0.25;
+ for (qreal i = spacing; i < 0.99; i += spacing) {
+ QPointF p1 = b1->pointAt(i);
+ QPointF p2 = b2->pointAt(i);
+ qreal d = (p1.x() - p2.x())*(p1.x() - p2.x()) + (p1.y() - p2.y())*(p1.y() - p2.y());
+ if (qAbs(d - o2) > max_dist_line)
+ return Split;
+
+ QPointF normalPoint = b1->normalVector(i);
+ qreal l = qAbs(normalPoint.x()) + qAbs(normalPoint.y());
+ if (l != 0.) {
+ d = qAbs( normalPoint.x()*(p1.y() - p2.y()) - normalPoint.y()*(p1.x() - p2.x()) ) / l;
+ if (d > max_dist_normal)
+ return Split;
+ }
+ }
+ return Ok;
+}
+
+static inline QLineF qline_shifted(const QPointF &p1, const QPointF &p2, qreal offset)
+{
+ QLineF l(p1, p2);
+ QLineF ln = l.normalVector().unitVector();
+ l.translate(ln.dx() * offset, ln.dy() * offset);
+ return l;
+}
+
+static bool qbezier_is_line(QPointF *points, int pointCount)
+{
+ Q_ASSERT(pointCount > 2);
+
+ qreal dx13 = points[2].x() - points[0].x();
+ qreal dy13 = points[2].y() - points[0].y();
+
+ qreal dx12 = points[1].x() - points[0].x();
+ qreal dy12 = points[1].y() - points[0].y();
+
+ if (pointCount == 3) {
+ return qFuzzyCompare(dx12 * dy13, dx13 * dy12);
+ } else if (pointCount == 4) {
+ qreal dx14 = points[3].x() - points[0].x();
+ qreal dy14 = points[3].y() - points[0].y();
+
+ return (qFuzzyCompare(dx12 * dy13, dx13 * dy12) && qFuzzyCompare(dx12 * dy14, dx14 * dy12));
+ }
+
+ return false;
+}
+
+static ShiftResult shift(const QBezier *orig, QBezier *shifted, qreal offset, qreal threshold)
+{
+ int map[4];
+ bool p1_p2_equal = (orig->x1 == orig->x2 && orig->y1 == orig->y2);
+ bool p2_p3_equal = (orig->x2 == orig->x3 && orig->y2 == orig->y3);
+ bool p3_p4_equal = (orig->x3 == orig->x4 && orig->y3 == orig->y4);
+
+ QPointF points[4];
+ int np = 0;
+ points[np] = QPointF(orig->x1, orig->y1);
+ map[0] = 0;
+ ++np;
+ if (!p1_p2_equal) {
+ points[np] = QPointF(orig->x2, orig->y2);
+ ++np;
+ }
+ map[1] = np - 1;
+ if (!p2_p3_equal) {
+ points[np] = QPointF(orig->x3, orig->y3);
+ ++np;
+ }
+ map[2] = np - 1;
+ if (!p3_p4_equal) {
+ points[np] = QPointF(orig->x4, orig->y4);
+ ++np;
+ }
+ map[3] = np - 1;
+ if (np == 1)
+ return Discard;
+
+ // We need to specialcase lines of 3 or 4 points due to numerical
+ // instability in intersections below
+ if (np > 2 && qbezier_is_line(points, np)) {
+ if (points[0] == points[np-1])
+ return Discard;
+
+ QLineF l = qline_shifted(points[0], points[np-1], offset);
+ *shifted = QBezier::fromPoints(l.p1(), l.pointAt(qreal(0.33)), l.pointAt(qreal(0.66)), l.p2());
+ return Ok;
+ }
+
+ QRectF b = orig->bounds();
+ if (np == 4 && b.width() < .1*offset && b.height() < .1*offset) {
+ qreal l = (orig->x1 - orig->x2)*(orig->x1 - orig->x2) +
+ (orig->y1 - orig->y2)*(orig->y1 - orig->y1) *
+ (orig->x3 - orig->x4)*(orig->x3 - orig->x4) +
+ (orig->y3 - orig->y4)*(orig->y3 - orig->y4);
+ qreal dot = (orig->x1 - orig->x2)*(orig->x3 - orig->x4) +
+ (orig->y1 - orig->y2)*(orig->y3 - orig->y4);
+ if (dot < 0 && dot*dot < 0.8*l)
+ // the points are close and reverse dirction. Approximate the whole
+ // thing by a semi circle
+ return Circle;
+ }
+
+ QPointF points_shifted[4];
+
+ QLineF prev = QLineF(QPointF(), points[1] - points[0]);
+ QPointF prev_normal = prev.normalVector().unitVector().p2();
+
+ points_shifted[0] = points[0] + offset * prev_normal;
+
+ for (int i = 1; i < np - 1; ++i) {
+ QLineF next = QLineF(QPointF(), points[i + 1] - points[i]);
+ QPointF next_normal = next.normalVector().unitVector().p2();
+
+ QPointF normal_sum = prev_normal + next_normal;
+
+ qreal r = 1.0 + prev_normal.x() * next_normal.x()
+ + prev_normal.y() * next_normal.y();
+
+ if (qFuzzyCompare(r + 1, 1)) {
+ points_shifted[i] = points[i] + offset * prev_normal;
+ } else {
+ qreal k = offset / r;
+ points_shifted[i] = points[i] + k * normal_sum;
+ }
+
+ prev_normal = next_normal;
+ }
+
+ points_shifted[np - 1] = points[np - 1] + offset * prev_normal;
+
+ *shifted = QBezier::fromPoints(points_shifted[map[0]], points_shifted[map[1]],
+ points_shifted[map[2]], points_shifted[map[3]]);
+
+ return good_offset(orig, shifted, offset, threshold);
+}
+
+// This value is used to determine the length of control point vectors
+// when approximating arc segments as curves. The factor is multiplied
+// with the radius of the circle.
+#define KAPPA 0.5522847498
+
+
+static bool addCircle(const QBezier *b, qreal offset, QBezier *o)
+{
+ QPointF normals[3];
+
+ normals[0] = QPointF(b->y2 - b->y1, b->x1 - b->x2);
+ qreal dist = qSqrt(normals[0].x()*normals[0].x() + normals[0].y()*normals[0].y());
+ if (qFuzzyCompare(dist + 1, 1))
+ return false;
+ normals[0] /= dist;
+ normals[2] = QPointF(b->y4 - b->y3, b->x3 - b->x4);
+ dist = qSqrt(normals[2].x()*normals[2].x() + normals[2].y()*normals[2].y());
+ if (qFuzzyCompare(dist + 1, 1))
+ return false;
+ normals[2] /= dist;
+
+ normals[1] = QPointF(b->x1 - b->x2 - b->x3 + b->x4, b->y1 - b->y2 - b->y3 + b->y4);
+ normals[1] /= -1*qSqrt(normals[1].x()*normals[1].x() + normals[1].y()*normals[1].y());
+
+ qreal angles[2];
+ qreal sign = 1.;
+ for (int i = 0; i < 2; ++i) {
+ qreal cos_a = normals[i].x()*normals[i+1].x() + normals[i].y()*normals[i+1].y();
+ if (cos_a > 1.)
+ cos_a = 1.;
+ if (cos_a < -1.)
+ cos_a = -1;
+ angles[i] = acos(cos_a)/Q_PI;
+ }
+
+ if (angles[0] + angles[1] > 1.) {
+ // more than 180 degrees
+ normals[1] = -normals[1];
+ angles[0] = 1. - angles[0];
+ angles[1] = 1. - angles[1];
+ sign = -1.;
+
+ }
+
+ QPointF circle[3];
+ circle[0] = QPointF(b->x1, b->y1) + normals[0]*offset;
+ circle[1] = QPointF(0.5*(b->x1 + b->x4), 0.5*(b->y1 + b->y4)) + normals[1]*offset;
+ circle[2] = QPointF(b->x4, b->y4) + normals[2]*offset;
+
+ for (int i = 0; i < 2; ++i) {
+ qreal kappa = 2.*KAPPA * sign * offset * angles[i];
+
+ o->x1 = circle[i].x();
+ o->y1 = circle[i].y();
+ o->x2 = circle[i].x() - normals[i].y()*kappa;
+ o->y2 = circle[i].y() + normals[i].x()*kappa;
+ o->x3 = circle[i+1].x() + normals[i+1].y()*kappa;
+ o->y3 = circle[i+1].y() - normals[i+1].x()*kappa;
+ o->x4 = circle[i+1].x();
+ o->y4 = circle[i+1].y();
+
+ ++o;
+ }
+ return true;
+}
+
+int QBezier::shifted(QBezier *curveSegments, int maxSegments, qreal offset, float threshold) const
+{
+ Q_ASSERT(curveSegments);
+ Q_ASSERT(maxSegments > 0);
+
+ if (x1 == x2 && x1 == x3 && x1 == x4 &&
+ y1 == y2 && y1 == y3 && y1 == y4)
+ return 0;
+
+ --maxSegments;
+ QBezier beziers[10];
+redo:
+ beziers[0] = *this;
+ QBezier *b = beziers;
+ QBezier *o = curveSegments;
+
+ while (b >= beziers) {
+ int stack_segments = b - beziers + 1;
+ if ((stack_segments == 10) || (o - curveSegments == maxSegments - stack_segments)) {
+ threshold *= 1.5;
+ if (threshold > 2.)
+ goto give_up;
+ goto redo;
+ }
+ ShiftResult res = shift(b, o, offset, threshold);
+ if (res == Discard) {
+ --b;
+ } else if (res == Ok) {
+ ++o;
+ --b;
+ continue;
+ } else if (res == Circle && maxSegments - (o - curveSegments) >= 2) {
+ // add semi circle
+ if (addCircle(b, offset, o))
+ o += 2;
+ --b;
+ } else {
+ b->split(b+1, b);
+ ++b;
+ }
+ }
+
+give_up:
+ while (b >= beziers) {
+ ShiftResult res = shift(b, o, offset, threshold);
+
+ // if res isn't Ok or Split then *o is undefined
+ if (res == Ok || res == Split)
+ ++o;
+
+ --b;
+ }
+
+ Q_ASSERT(o - curveSegments <= maxSegments);
+ return o - curveSegments;
+}
+
+#if 0
+static inline bool IntersectBB(const QBezier &a, const QBezier &b)
+{
+ return a.bounds().intersects(b.bounds());
+}
+#else
+static int IntersectBB(const QBezier &a, const QBezier &b)
+{
+ // Compute bounding box for a
+ qreal minax, maxax, minay, maxay;
+ if (a.x1 > a.x4) // These are the most likely to be extremal
+ minax = a.x4, maxax = a.x1;
+ else
+ minax = a.x1, maxax = a.x4;
+
+ if (a.x3 < minax)
+ minax = a.x3;
+ else if (a.x3 > maxax)
+ maxax = a.x3;
+
+ if (a.x2 < minax)
+ minax = a.x2;
+ else if (a.x2 > maxax)
+ maxax = a.x2;
+
+ if (a.y1 > a.y4)
+ minay = a.y4, maxay = a.y1;
+ else
+ minay = a.y1, maxay = a.y4;
+
+ if (a.y3 < minay)
+ minay = a.y3;
+ else if (a.y3 > maxay)
+ maxay = a.y3;
+
+ if (a.y2 < minay)
+ minay = a.y2;
+ else if (a.y2 > maxay)
+ maxay = a.y2;
+
+ // Compute bounding box for b
+ qreal minbx, maxbx, minby, maxby;
+ if (b.x1 > b.x4)
+ minbx = b.x4, maxbx = b.x1;
+ else
+ minbx = b.x1, maxbx = b.x4;
+
+ if (b.x3 < minbx)
+ minbx = b.x3;
+ else if (b.x3 > maxbx)
+ maxbx = b.x3;
+
+ if (b.x2 < minbx)
+ minbx = b.x2;
+ else if (b.x2 > maxbx)
+ maxbx = b.x2;
+
+ if (b.y1 > b.y4)
+ minby = b.y4, maxby = b.y1;
+ else
+ minby = b.y1, maxby = b.y4;
+
+ if (b.y3 < minby)
+ minby = b.y3;
+ else if (b.y3 > maxby)
+ maxby = b.y3;
+
+ if (b.y2 < minby)
+ minby = b.y2;
+ else if (b.y2 > maxby)
+ maxby = b.y2;
+
+ // Test bounding box of b against bounding box of a
+ if ((minax > maxbx) || (minay > maxby) // Not >= : need boundary case
+ || (minbx > maxax) || (minby > maxay))
+ return 0; // they don't intersect
+ else
+ return 1; // they intersect
+}
+#endif
+
+
+#ifdef QDEBUG_BEZIER
+static QDebug operator<<(QDebug dbg, const QBezier &bz)
+{
+ dbg <<"["<<bz.x1<<", "<<bz.y1<<"], "
+ <<"["<<bz.x2<<", "<<bz.y2<<"], "
+ <<"["<<bz.x3<<", "<<bz.y3<<"], "
+ <<"["<<bz.x4<<", "<<bz.y4<<"]";
+ return dbg;
+}
+#endif
+
+static bool RecursivelyIntersect(const QBezier &a, qreal t0, qreal t1, int deptha,
+ const QBezier &b, qreal u0, qreal u1, int depthb,
+ QVector<QPair<qreal, qreal> > *t)
+{
+#ifdef QDEBUG_BEZIER
+ static int I = 0;
+ int currentD = I;
+ fprintf(stderr, "%d) t0 = %lf, t1 = %lf, deptha = %d\n"
+ "u0 = %lf, u1 = %lf, depthb = %d\n", I++, t0, t1, deptha,
+ u0, u1, depthb);
+#endif
+ if (deptha > 0) {
+ QBezier A[2];
+ a.split(&A[0], &A[1]);
+ qreal tmid = (t0+t1)*0.5;
+ //qDebug()<<"\t1)"<<A[0];
+ //qDebug()<<"\t2)"<<A[1];
+ deptha--;
+ if (depthb > 0) {
+ QBezier B[2];
+ b.split(&B[0], &B[1]);
+ //qDebug()<<"\t3)"<<B[0];
+ //qDebug()<<"\t4)"<<B[1];
+ qreal umid = (u0+u1)*0.5;
+ depthb--;
+ if (IntersectBB(A[0], B[0])) {
+ //fprintf(stderr, "\t 1 from %d\n", currentD);
+ if (RecursivelyIntersect(A[0], t0, tmid, deptha,
+ B[0], u0, umid, depthb,
+ t) && !t)
+ return true;
+ }
+ if (IntersectBB(A[1], B[0])) {
+ //fprintf(stderr, "\t 2 from %d\n", currentD);
+ if (RecursivelyIntersect(A[1], tmid, t1, deptha,
+ B[0], u0, umid, depthb,
+ t) && !t)
+ return true;
+ }
+ if (IntersectBB(A[0], B[1])) {
+ //fprintf(stderr, "\t 3 from %d\n", currentD);
+ if (RecursivelyIntersect(A[0], t0, tmid, deptha,
+ B[1], umid, u1, depthb,
+ t) && !t)
+ return true;
+ }
+ if (IntersectBB(A[1], B[1])) {
+ //fprintf(stderr, "\t 4 from %d\n", currentD);
+ if (RecursivelyIntersect(A[1], tmid, t1, deptha,
+ B[1], umid, u1, depthb,
+ t) && !t)
+ return true;
+ }
+ return t ? !t->isEmpty() : false;
+ } else {
+ if (IntersectBB(A[0], b)) {
+ //fprintf(stderr, "\t 5 from %d\n", currentD);
+ if (RecursivelyIntersect(A[0], t0, tmid, deptha,
+ b, u0, u1, depthb,
+ t) && !t)
+ return true;
+ }
+ if (IntersectBB(A[1], b)) {
+ //fprintf(stderr, "\t 6 from %d\n", currentD);
+ if (RecursivelyIntersect(A[1], tmid, t1, deptha,
+ b, u0, u1, depthb,
+ t) && !t)
+ return true;
+ }
+ return t ? !t->isEmpty() : false;
+ }
+ } else {
+ if (depthb > 0) {
+ QBezier B[2];
+ b.split(&B[0], &B[1]);
+ qreal umid = (u0 + u1)*0.5;
+ depthb--;
+ if (IntersectBB(a, B[0])) {
+ //fprintf(stderr, "\t 7 from %d\n", currentD);
+ if (RecursivelyIntersect(a, t0, t1, deptha,
+ B[0], u0, umid, depthb,
+ t) && !t)
+ return true;
+ }
+ if (IntersectBB(a, B[1])) {
+ //fprintf(stderr, "\t 8 from %d\n", currentD);
+ if (RecursivelyIntersect(a, t0, t1, deptha,
+ B[1], umid, u1, depthb,
+ t) && !t)
+ return true;
+ }
+ return t ? !t->isEmpty() : false;
+ }
+ else {
+ // Both segments are fully subdivided; now do line segments
+ qreal xlk = a.x4 - a.x1;
+ qreal ylk = a.y4 - a.y1;
+ qreal xnm = b.x4 - b.x1;
+ qreal ynm = b.y4 - b.y1;
+ qreal xmk = b.x1 - a.x1;
+ qreal ymk = b.y1 - a.y1;
+ qreal det = xnm * ylk - ynm * xlk;
+ if (1.0 + det == 1.0) {
+ return false;
+ } else {
+ qreal detinv = 1.0 / det;
+ qreal rs = (xnm * ymk - ynm *xmk) * detinv;
+ qreal rt = (xlk * ymk - ylk * xmk) * detinv;
+ if ((rs < 0.0) || (rs > 1.0) || (rt < 0.0) || (rt > 1.0))
+ return false;
+
+ if (t) {
+ const qreal alpha_a = t0 + rs * (t1 - t0);
+ const qreal alpha_b = u0 + rt * (u1 - u0);
+
+ *t << qMakePair(alpha_a, alpha_b);
+ }
+
+ return true;
+ }
+ }
+ }
+}
+
+QVector< QPair<qreal, qreal> > QBezier::findIntersections(const QBezier &a, const QBezier &b)
+{
+ QVector< QPair<qreal, qreal> > v(2);
+ findIntersections(a, b, &v);
+ return v;
+}
+
+bool QBezier::findIntersections(const QBezier &a, const QBezier &b,
+ QVector<QPair<qreal, qreal> > *t)
+{
+ if (IntersectBB(a, b)) {
+ QPointF la1(fabs((a.x3 - a.x2) - (a.x2 - a.x1)),
+ fabs((a.y3 - a.y2) - (a.y2 - a.y1)));
+ QPointF la2(fabs((a.x4 - a.x3) - (a.x3 - a.x2)),
+ fabs((a.y4 - a.y3) - (a.y3 - a.y2)));
+ QPointF la;
+ if (la1.x() > la2.x()) la.setX(la1.x()); else la.setX(la2.x());
+ if (la1.y() > la2.y()) la.setY(la1.y()); else la.setY(la2.y());
+ QPointF lb1(fabs((b.x3 - b.x2) - (b.x2 - b.x1)),
+ fabs((b.y3 - b.y2) - (b.y2 - b.y1)));
+ QPointF lb2(fabs((b.x4 - b.x3) - (b.x3 - b.x2)),
+ fabs((b.y4 - b.y3) - (b.y3 - b.y2)));
+ QPointF lb;
+ if (lb1.x() > lb2.x()) lb.setX(lb1.x()); else lb.setX(lb2.x());
+ if (lb1.y() > lb2.y()) lb.setY(lb1.y()); else lb.setY(lb2.y());
+ qreal l0;
+ if (la.x() > la.y())
+ l0 = la.x();
+ else
+ l0 = la.y();
+ int ra;
+ if (l0 * 0.75 * M_SQRT2 + 1.0 == 1.0)
+ ra = 0;
+ else
+ ra = qCeil(log4(M_SQRT2 * 6.0 / 8.0 * INV_EPS * l0));
+ if (lb.x() > lb.y())
+ l0 = lb.x();
+ else
+ l0 = lb.y();
+ int rb;
+ if (l0 * 0.75 * M_SQRT2 + 1.0 == 1.0)
+ rb = 0;
+ else
+ rb = qCeil(log4(M_SQRT2 * 6.0 / 8.0 * INV_EPS * l0));
+
+ // if qreal is float then halve the number of subdivisions
+ if (sizeof(qreal) == 4) {
+ ra /= 2;
+ rb /= 2;
+ }
+
+ return RecursivelyIntersect(a, 0., 1., ra, b, 0., 1., rb, t);
+ }
+
+ //Don't sort here because it breaks the orders of corresponding
+ // intersections points. this way t's at the same locations correspond
+ // to the same intersection point.
+ //qSort(parameters[0].begin(), parameters[0].end(), qLess<qreal>());
+ //qSort(parameters[1].begin(), parameters[1].end(), qLess<qreal>());
+
+ return false;
+}
+
+static inline void splitBezierAt(const QBezier &bez, qreal t,
+ QBezier *left, QBezier *right)
+{
+ left->x1 = bez.x1;
+ left->y1 = bez.y1;
+
+ left->x2 = bez.x1 + t * ( bez.x2 - bez.x1 );
+ left->y2 = bez.y1 + t * ( bez.y2 - bez.y1 );
+
+ left->x3 = bez.x2 + t * ( bez.x3 - bez.x2 ); // temporary holding spot
+ left->y3 = bez.y2 + t * ( bez.y3 - bez.y2 ); // temporary holding spot
+
+ right->x3 = bez.x3 + t * ( bez.x4 - bez.x3 );
+ right->y3 = bez.y3 + t * ( bez.y4 - bez.y3 );
+
+ right->x2 = left->x3 + t * ( right->x3 - left->x3);
+ right->y2 = left->y3 + t * ( right->y3 - left->y3);
+
+ left->x3 = left->x2 + t * ( left->x3 - left->x2 );
+ left->y3 = left->y2 + t * ( left->y3 - left->y2 );
+
+ left->x4 = right->x1 = left->x3 + t * (right->x2 - left->x3);
+ left->y4 = right->y1 = left->y3 + t * (right->y2 - left->y3);
+
+ right->x4 = bez.x4;
+ right->y4 = bez.y4;
+}
+
+QVector< QList<QBezier> > QBezier::splitAtIntersections(QBezier &b)
+{
+ QVector< QList<QBezier> > curves(2);
+
+ QVector< QPair<qreal, qreal> > allInters = findIntersections(*this, b);
+
+ QList<qreal> inters1;
+ QList<qreal> inters2;
+
+ for (int i = 0; i < allInters.size(); ++i) {
+ inters1 << allInters[i].first;
+ inters2 << allInters[i].second;
+ }
+
+ qSort(inters1.begin(), inters1.end(), qLess<qreal>());
+ qSort(inters2.begin(), inters2.end(), qLess<qreal>());
+
+ Q_ASSERT(inters1.count() == inters2.count());
+
+ int i;
+ for (i = 0; i < inters1.count(); ++i) {
+ qreal t1 = inters1.at(i);
+ qreal t2 = inters2.at(i);
+
+ QBezier curve1, curve2;
+ parameterSplitLeft(t1, &curve1);
+ b.parameterSplitLeft(t2, &curve2);
+ curves[0].append(curve1);
+ curves[0].append(curve2);
+ }
+ curves[0].append(*this);
+ curves[1].append(b);
+
+ return curves;
+}
+
+qreal QBezier::length(qreal error) const
+{
+ qreal length = 0.0;
+
+ addIfClose(&length, error);
+
+ return length;
+}
+
+void QBezier::addIfClose(qreal *length, qreal error) const
+{
+ QBezier left, right; /* bez poly splits */
+
+ qreal len = 0.0; /* arc length */
+ qreal chord; /* chord length */
+
+ len = len + QLineF(QPointF(x1, y1),QPointF(x2, y2)).length();
+ len = len + QLineF(QPointF(x2, y2),QPointF(x3, y3)).length();
+ len = len + QLineF(QPointF(x3, y3),QPointF(x4, y4)).length();
+
+ chord = QLineF(QPointF(x1, y1),QPointF(x4, y4)).length();
+
+ if((len-chord) > error) {
+ split(&left, &right); /* split in two */
+ left.addIfClose(length, error); /* try left side */
+ right.addIfClose(length, error); /* try right side */
+ return;
+ }
+
+ *length = *length + len;
+
+ return;
+}
+
+qreal QBezier::tForY(qreal t0, qreal t1, qreal y) const
+{
+ qreal py0 = pointAt(t0).y();
+ qreal py1 = pointAt(t1).y();
+
+ if (py0 > py1) {
+ qSwap(py0, py1);
+ qSwap(t0, t1);
+ }
+
+ Q_ASSERT(py0 <= py1);
+
+ if (py0 >= y)
+ return t0;
+ else if (py1 <= y)
+ return t1;
+
+ Q_ASSERT(py0 < y && y < py1);
+
+ qreal lt = t0;
+ qreal dt;
+ do {
+ qreal t = 0.5 * (t0 + t1);
+
+ qreal a, b, c, d;
+ QBezier::coefficients(t, a, b, c, d);
+ qreal yt = a * y1 + b * y2 + c * y3 + d * y4;
+
+ if (yt < y) {
+ t0 = t;
+ py0 = yt;
+ } else {
+ t1 = t;
+ py1 = yt;
+ }
+ dt = lt - t;
+ lt = t;
+ } while (qAbs(dt) > 1e-7);
+
+ return t0;
+}
+
+int QBezier::stationaryYPoints(qreal &t0, qreal &t1) const
+{
+ // y(t) = (1 - t)^3 * y1 + 3 * (1 - t)^2 * t * y2 + 3 * (1 - t) * t^2 * y3 + t^3 * y4
+ // y'(t) = 3 * (-(1-2t+t^2) * y1 + (1 - 4 * t + 3 * t^2) * y2 + (2 * t - 3 * t^2) * y3 + t^2 * y4)
+ // y'(t) = 3 * ((-y1 + 3 * y2 - 3 * y3 + y4)t^2 + (2 * y1 - 4 * y2 + 2 * y3)t + (-y1 + y2))
+
+ const qreal a = -y1 + 3 * y2 - 3 * y3 + y4;
+ const qreal b = 2 * y1 - 4 * y2 + 2 * y3;
+ const qreal c = -y1 + y2;
+
+ qreal reciprocal = b * b - 4 * a * c;
+
+ QList<qreal> result;
+
+ if (qFuzzyCompare(reciprocal + 1, 1)) {
+ t0 = -b / (2 * a);
+ return 1;
+ } else if (reciprocal > 0) {
+ qreal temp = qSqrt(reciprocal);
+
+ t0 = (-b - temp)/(2*a);
+ t1 = (-b + temp)/(2*a);
+
+ if (t1 < t0)
+ qSwap(t0, t1);
+
+ int count = 0;
+ qreal t[2] = { 0, 1 };
+
+ if (t0 > 0 && t0 < 1)
+ t[count++] = t0;
+ if (t1 > 0 && t1 < 1)
+ t[count++] = t1;
+
+ t0 = t[0];
+ t1 = t[1];
+
+ return count;
+ }
+
+ return 0;
+}
+
+qreal QBezier::tAtLength(qreal l) const
+{
+ qreal len = length();
+ qreal t = 1.0;
+ const qreal error = (qreal)0.01;
+ if (l > len || qFuzzyCompare(l, len))
+ return t;
+
+ t *= 0.5;
+ //int iters = 0;
+ //qDebug()<<"LEN is "<<l<<len;
+ qreal lastBigger = 1.;
+ while (1) {
+ //qDebug()<<"\tt is "<<t;
+ QBezier right = *this;
+ QBezier left;
+ right.parameterSplitLeft(t, &left);
+ qreal lLen = left.length();
+ if (qAbs(lLen - l) < error)
+ break;
+
+ if (lLen < l) {
+ t += (lastBigger - t)*.5;
+ } else {
+ lastBigger = t;
+ t -= t*.5;
+ }
+ //++iters;
+ }
+ //qDebug()<<"number of iters is "<<iters;
+ return t;
+}
+
+QBezier QBezier::bezierOnInterval(qreal t0, qreal t1) const
+{
+ if (t0 == 0 && t1 == 1)
+ return *this;
+
+ QBezier bezier = *this;
+
+ QBezier result;
+ bezier.parameterSplitLeft(t0, &result);
+ qreal trueT = (t1-t0)/(1-t0);
+ bezier.parameterSplitLeft(trueT, &result);
+
+ return result;
+}
+
+
+static inline void bindInflectionPoint(const QBezier &bez, const qreal t,
+ qreal *tMinus , qreal *tPlus)
+{
+ if (t <= 0) {
+ *tMinus = *tPlus = -1;
+ return;
+ } else if (t >= 1) {
+ *tMinus = *tPlus = 2;
+ return;
+ }
+
+ QBezier left, right;
+ splitBezierAt(bez, t, &left, &right);
+
+ qreal ax = -right.x1 + 3*right.x2 - 3*right.x3 + right.x4;
+ qreal ay = -right.y1 + 3*right.y2 - 3*right.y3 + right.y4;
+ qreal ex = 3 * (right.x2 - right.x3);
+ qreal ey = 3 * (right.y2 - right.y3);
+
+ qreal s4 = qAbs(6 * (ey * ax - ex * ay) / qSqrt(ex * ex + ey * ey)) + 0.00001f;
+ qreal tf = pow(qreal(9 * flatness / s4), qreal(1./3.));
+ *tMinus = t - (1 - t) * tf;
+ *tPlus = t + (1 - t) * tf;
+}
+
+void QBezier::addToPolygonIterative(QPolygonF *p) const
+{
+ qreal t1, t2, tcusp;
+ qreal t1min, t1plus, t2min, t2plus;
+
+ qreal ax = -x1 + 3*x2 - 3*x3 + x4;
+ qreal ay = -y1 + 3*y2 - 3*y3 + y4;
+ qreal bx = 3*x1 - 6*x2 + 3*x3;
+ qreal by = 3*y1 - 6*y2 + 3*y3;
+ qreal cx = -3*x1 + 3*x2;
+ qreal cy = -3*y1 + 2*y2;
+
+ if (findInflections(6 * (ay * bx - ax * by),
+ 6 * (ay * cx - ax * cy),
+ 2 * (by * cx - bx * cy),
+ &t1, &t2, &tcusp)) {
+ bindInflectionPoint(*this, t1, &t1min, &t1plus);
+ bindInflectionPoint(*this, t2, &t2min, &t2plus);
+
+ QBezier tmpBez = *this;
+ QBezier left, right, bez1, bez2, bez3;
+ if (t1min > 0) {
+ if (t1min >= 1) {
+ flattenBezierWithoutInflections(tmpBez, p);
+ } else {
+ splitBezierAt(tmpBez, t1min, &left, &right);
+ flattenBezierWithoutInflections(left, p);
+ p->append(tmpBez.pointAt(t1min));
+
+ if (t2min < t1plus) {
+ if (tcusp < 1) {
+ p->append(tmpBez.pointAt(tcusp));
+ }
+ if (t2plus < 1) {
+ splitBezierAt(tmpBez, t2plus, &left, &right);
+ flattenBezierWithoutInflections(right, p);
+ }
+ } else if (t1plus < 1) {
+ if (t2min < 1) {
+ splitBezierAt(tmpBez, t2min, &bez3, &right);
+ splitBezierAt(bez3, t1plus, &left, &bez2);
+
+ flattenBezierWithoutInflections(bez2, p);
+ p->append(tmpBez.pointAt(t2min));
+
+ if (t2plus < 1) {
+ splitBezierAt(tmpBez, t2plus, &left, &bez2);
+ flattenBezierWithoutInflections(bez2, p);
+ }
+ } else {
+ splitBezierAt(tmpBez, t1plus, &left, &bez2);
+ flattenBezierWithoutInflections(bez2, p);
+ }
+ }
+ }
+ } else if (t1plus > 0) {
+ p->append(QPointF(x1, y1));
+ if (t2min < t1plus) {
+ if (tcusp < 1) {
+ p->append(tmpBez.pointAt(tcusp));
+ }
+ if (t2plus < 1) {
+ splitBezierAt(tmpBez, t2plus, &left, &bez2);
+ flattenBezierWithoutInflections(bez2, p);
+ }
+ } else if (t1plus < 1) {
+ if (t2min < 1) {
+ splitBezierAt(tmpBez, t2min, &bez3, &right);
+ splitBezierAt(bez3, t1plus, &left, &bez2);
+
+ flattenBezierWithoutInflections(bez2, p);
+
+ p->append(tmpBez.pointAt(t2min));
+ if (t2plus < 1) {
+ splitBezierAt(tmpBez, t2plus, &left, &bez2);
+ flattenBezierWithoutInflections(bez2, p);
+ }
+ } else {
+ splitBezierAt(tmpBez, t1plus, &left, &bez2);
+ flattenBezierWithoutInflections(bez2, p);
+ }
+ }
+ } else if (t2min > 0) {
+ if (t2min < 1) {
+ splitBezierAt(tmpBez, t2min, &bez1, &right);
+ flattenBezierWithoutInflections(bez1, p);
+ p->append(tmpBez.pointAt(t2min));
+
+ if (t2plus < 1) {
+ splitBezierAt(tmpBez, t2plus, &left, &bez2);
+ flattenBezierWithoutInflections(bez2, p);
+ }
+ } else {
+ //### in here we should check whether the area of the
+ // triangle formed between pt1/pt2/pt3 is smaller
+ // or equal to 0 and then do iterative flattening
+ // if not we should fallback and do the recursive
+ // flattening.
+ flattenBezierWithoutInflections(tmpBez, p);
+ }
+ } else if (t2plus > 0) {
+ p->append(QPointF(x1, y1));
+ if (t2plus < 1) {
+ splitBezierAt(tmpBez, t2plus, &left, &bez2);
+ flattenBezierWithoutInflections(bez2, p);
+ }
+ } else {
+ flattenBezierWithoutInflections(tmpBez, p);
+ }
+ } else {
+ QBezier bez = *this;
+ flattenBezierWithoutInflections(bez, p);
+ }
+
+ p->append(QPointF(x4, y4));
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/painting/qbezier_p.h b/src/gui/painting/qbezier_p.h
new file mode 100644
index 0000000000..5c2ad5e786
--- /dev/null
+++ b/src/gui/painting/qbezier_p.h
@@ -0,0 +1,280 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QBEZIER_P_H
+#define QBEZIER_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/qpoint.h"
+#include "QtCore/qline.h"
+#include "QtCore/qrect.h"
+#include "QtCore/qvector.h"
+#include "QtCore/qlist.h"
+#include "QtCore/qpair.h"
+
+QT_BEGIN_NAMESPACE
+
+class QPolygonF;
+
+class Q_GUI_EXPORT QBezier
+{
+public:
+ static QBezier fromPoints(const QPointF &p1, const QPointF &p2,
+ const QPointF &p3, const QPointF &p4);
+
+ static void coefficients(qreal t, qreal &a, qreal &b, qreal &c, qreal &d);
+
+ inline QPointF pointAt(qreal t) const;
+ inline QPointF normalVector(qreal t) const;
+
+ inline QPointF derivedAt(qreal t) const;
+ inline QPointF secondDerivedAt(qreal t) const;
+
+ QPolygonF toPolygon() const;
+ void addToPolygon(QPolygonF *p) const;
+ void addToPolygonIterative(QPolygonF *p) const;
+ void addToPolygonMixed(QPolygonF *p) const;
+ QRectF bounds() const;
+ qreal length(qreal error = 0.01) const;
+ void addIfClose(qreal *length, qreal error) const;
+
+ qreal tAtLength(qreal len) const;
+
+ int stationaryYPoints(qreal &t0, qreal &t1) const;
+ qreal tForY(qreal t0, qreal t1, qreal y) const;
+
+ QPointF pt1() const { return QPointF(x1, y1); }
+ QPointF pt2() const { return QPointF(x2, y2); }
+ QPointF pt3() const { return QPointF(x3, y3); }
+ QPointF pt4() const { return QPointF(x4, y4); }
+
+ inline QPointF midPoint() const;
+ inline QLineF midTangent() const;
+
+ inline QLineF startTangent() const;
+ inline QLineF endTangent() const;
+
+ inline void parameterSplitLeft(qreal t, QBezier *left);
+ inline void split(QBezier *firstHalf, QBezier *secondHalf) const;
+ int shifted(QBezier *curveSegments, int maxSegmets,
+ qreal offset, float threshold) const;
+
+ QVector< QList<QBezier> > splitAtIntersections(QBezier &a);
+
+ QBezier bezierOnInterval(qreal t0, qreal t1) const;
+
+ static QVector< QPair<qreal, qreal> > findIntersections(const QBezier &a,
+ const QBezier &b);
+
+ static bool findIntersections(const QBezier &a, const QBezier &b,
+ QVector<QPair<qreal, qreal> > *t);
+
+ qreal x1, y1, x2, y2, x3, y3, x4, y4;
+};
+
+inline QPointF QBezier::midPoint() const
+{
+ return QPointF((x1 + x4 + 3*(x2 + x3))/8., (y1 + y4 + 3*(y2 + y3))/8.);
+}
+
+inline QLineF QBezier::midTangent() const
+{
+ QPointF mid = midPoint();
+ QLineF dir(QLineF(x1, y1, x2, y2).pointAt(0.5), QLineF(x3, y3, x4, y4).pointAt(0.5));
+ return QLineF(mid.x() - dir.dx(), mid.y() - dir.dy(),
+ mid.x() + dir.dx(), mid.y() + dir.dy());
+}
+
+inline QLineF QBezier::startTangent() const
+{
+ QLineF tangent(pt1(), pt2());
+ if (tangent.isNull())
+ tangent = QLineF(pt1(), pt3());
+ if (tangent.isNull())
+ tangent = QLineF(pt1(), pt4());
+ return tangent;
+}
+
+inline QLineF QBezier::endTangent() const
+{
+ QLineF tangent(pt4(), pt3());
+ if (tangent.isNull())
+ tangent = QLineF(pt4(), pt2());
+ if (tangent.isNull())
+ tangent = QLineF(pt4(), pt1());
+ return tangent;
+}
+
+inline void QBezier::coefficients(qreal t, qreal &a, qreal &b, qreal &c, qreal &d)
+{
+ qreal m_t = 1. - t;
+ b = m_t * m_t;
+ c = t * t;
+ d = c * t;
+ a = b * m_t;
+ b *= 3. * t;
+ c *= 3. * m_t;
+}
+
+inline QPointF QBezier::pointAt(qreal t) const
+{
+ Q_ASSERT(t >= 0);
+ Q_ASSERT(t <= 1);
+#if 1
+ qreal a, b, c, d;
+ coefficients(t, a, b, c, d);
+ return QPointF(a*x1 + b*x2 + c*x3 + d*x4, a*y1 + b*y2 + c*y3 + d*y4);
+#else
+ // numerically more stable:
+ qreal m_t = 1. - t;
+ qreal a = x1*m_t + x2*t;
+ qreal b = x2*m_t + x3*t;
+ qreal c = x3*m_t + x4*t;
+ a = a*m_t + b*t;
+ b = b*m_t + c*t;
+ qreal x = a*m_t + b*t;
+ qreal a = y1*m_t + y2*t;
+ qreal b = y2*m_t + y3*t;
+ qreal c = y3*m_t + y4*t;
+ a = a*m_t + b*t;
+ b = b*m_t + c*t;
+ qreal y = a*m_t + b*t;
+ return QPointF(x, y);
+#endif
+}
+
+inline QPointF QBezier::normalVector(qreal t) const
+{
+ qreal m_t = 1. - t;
+ qreal a = m_t * m_t;
+ qreal b = t * m_t;
+ qreal c = t * t;
+
+ return QPointF((y2-y1) * a + (y3-y2) * b + (y4-y3) * c, -(x2-x1) * a - (x3-x2) * b - (x4-x3) * c);
+}
+
+inline QPointF QBezier::derivedAt(qreal t) const
+{
+ // p'(t) = 3 * (-(1-2t+t^2) * p0 + (1 - 4 * t + 3 * t^2) * p1 + (2 * t - 3 * t^2) * p2 + t^2 * p3)
+
+ qreal m_t = 1. - t;
+
+ qreal d = t * t;
+ qreal a = -m_t * m_t;
+ qreal b = 1 - 4 * t + 3 * d;
+ qreal c = 2 * t - 3 * d;
+
+ return 3 * QPointF(a * x1 + b * x2 + c * x3 + d * x4,
+ a * y1 + b * y2 + c * y3 + d * y4);
+}
+
+inline QPointF QBezier::secondDerivedAt(qreal t) const
+{
+ qreal a = 2. - 2. * t;
+ qreal b = -4 + 6 * t;
+ qreal c = 2 - 6 * t;
+ qreal d = 2 * t;
+
+ return 3 * QPointF(a * x1 + b * x2 + c * x3 + d * x4,
+ a * y1 + b * y2 + c * y3 + d * y4);
+}
+
+inline void QBezier::split(QBezier *firstHalf, QBezier *secondHalf) const
+{
+ Q_ASSERT(firstHalf);
+ Q_ASSERT(secondHalf);
+
+ qreal c = (x2 + x3)*.5;
+ firstHalf->x2 = (x1 + x2)*.5;
+ secondHalf->x3 = (x3 + x4)*.5;
+ firstHalf->x1 = x1;
+ secondHalf->x4 = x4;
+ firstHalf->x3 = (firstHalf->x2 + c)*.5;
+ secondHalf->x2 = (secondHalf->x3 + c)*.5;
+ firstHalf->x4 = secondHalf->x1 = (firstHalf->x3 + secondHalf->x2)*.5;
+
+ c = (y2 + y3)/2;
+ firstHalf->y2 = (y1 + y2)*.5;
+ secondHalf->y3 = (y3 + y4)*.5;
+ firstHalf->y1 = y1;
+ secondHalf->y4 = y4;
+ firstHalf->y3 = (firstHalf->y2 + c)*.5;
+ secondHalf->y2 = (secondHalf->y3 + c)*.5;
+ firstHalf->y4 = secondHalf->y1 = (firstHalf->y3 + secondHalf->y2)*.5;
+}
+
+inline void QBezier::parameterSplitLeft(qreal t, QBezier *left)
+{
+ left->x1 = x1;
+ left->y1 = y1;
+
+ left->x2 = x1 + t * ( x2 - x1 );
+ left->y2 = y1 + t * ( y2 - y1 );
+
+ left->x3 = x2 + t * ( x3 - x2 ); // temporary holding spot
+ left->y3 = y2 + t * ( y3 - y2 ); // temporary holding spot
+
+ x3 = x3 + t * ( x4 - x3 );
+ y3 = y3 + t * ( y4 - y3 );
+
+ x2 = left->x3 + t * ( x3 - left->x3);
+ y2 = left->y3 + t * ( y3 - left->y3);
+
+ left->x3 = left->x2 + t * ( left->x3 - left->x2 );
+ left->y3 = left->y2 + t * ( left->y3 - left->y2 );
+
+ left->x4 = x1 = left->x3 + t * (x2 - left->x3);
+ left->y4 = y1 = left->y3 + t * (y2 - left->y3);
+}
+
+QT_END_NAMESPACE
+
+#endif // QBEZIER_P_H
diff --git a/src/gui/painting/qblendfunctions.cpp b/src/gui/painting/qblendfunctions.cpp
new file mode 100644
index 0000000000..d2f4ab797f
--- /dev/null
+++ b/src/gui/painting/qblendfunctions.cpp
@@ -0,0 +1,1419 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <qmath.h>
+#include "qdrawhelper_p.h"
+
+QT_BEGIN_NAMESPACE
+
+
+// This ifdef is made with the best of intention. GCC fails to
+// optimzie the code properly so the bytemul approach is the fastest
+// it gets. Both on ARM and on MSVC the code is optimized to be better
+// than the bytemul approach... On the other hand... This code is
+// almost never run on i386 so it may be downright silly to have this
+// piece of code here...
+#if defined (Q_CC_GNU) && (defined (QT_ARCH_I386) || defined (QT_ARCH_X86_64))
+# define QT_BLEND_USE_BYTEMUL
+#endif
+
+// #define QT_DEBUG_DRAW
+
+static const qreal aliasedCoordinateDelta = 0.5 - 0.015625;
+
+struct SourceOnlyAlpha
+{
+ inline uchar alpha(uchar src) const { return src; }
+ inline quint16 bytemul(quint16 spix) const { return spix; }
+};
+
+
+struct SourceAndConstAlpha
+{
+ SourceAndConstAlpha(int a) : m_alpha256(a) {
+ m_alpha255 = (m_alpha256 * 255) >> 8;
+ };
+ inline uchar alpha(uchar src) const { return (src * m_alpha256) >> 8; }
+ inline quint16 bytemul(quint16 x) const {
+ uint t = (((x & 0x07e0)*m_alpha255) >> 8) & 0x07e0;
+ t |= (((x & 0xf81f)*(m_alpha255>>2)) >> 6) & 0xf81f;
+ return t;
+ }
+ int m_alpha255;
+ int m_alpha256;
+};
+
+
+/************************************************************************
+ RGB16 (565) format target format
+ ************************************************************************/
+
+static inline quint16 convert_argb32_to_rgb16(quint32 spix)
+{
+ quint32 b = spix;
+ quint32 g = spix;
+ b >>= 8;
+ g >>= 5;
+ b &= 0x0000f800;
+ g &= 0x000007e0;
+ spix >>= 3;
+ b |= g;
+ spix &= 0x0000001f;
+ b |= spix;
+ return b;
+}
+
+struct Blend_RGB16_on_RGB16_NoAlpha {
+ inline void write(quint16 *dst, quint16 src) { *dst = src; }
+};
+
+struct Blend_RGB16_on_RGB16_ConstAlpha {
+ inline Blend_RGB16_on_RGB16_ConstAlpha(quint32 alpha) {
+ m_alpha = (alpha * 255) >> 8;
+ m_ialpha = 255 - m_alpha;
+ }
+
+ inline void write(quint16 *dst, quint16 src) {
+ *dst = BYTE_MUL_RGB16(src, m_alpha) + BYTE_MUL_RGB16(*dst, m_ialpha);
+ }
+
+ quint32 m_alpha;
+ quint32 m_ialpha;
+};
+
+struct Blend_ARGB32_on_RGB16_SourceAlpha {
+ inline void write(quint16 *dst, quint32 src) {
+ const quint8 alpha = qAlpha(src);
+ if(alpha) {
+ quint16 s = convert_argb32_to_rgb16(src);
+ if(alpha < 255)
+ s += BYTE_MUL_RGB16(*dst, 255 - alpha);
+ *dst = s;
+ }
+ }
+};
+
+struct Blend_ARGB32_on_RGB16_SourceAndConstAlpha {
+ inline Blend_ARGB32_on_RGB16_SourceAndConstAlpha(quint32 alpha) {
+ m_alpha = (alpha * 255) >> 8;
+ }
+
+ inline void write(quint16 *dst, quint32 src) {
+ src = BYTE_MUL(src, m_alpha);
+ const quint8 alpha = qAlpha(src);
+ if(alpha) {
+ quint16 s = convert_argb32_to_rgb16(src);
+ if(alpha < 255)
+ s += BYTE_MUL_RGB16(*dst, 255 - alpha);
+ *dst = s;
+ }
+ }
+
+ quint32 m_alpha;
+};
+
+template <typename SRC, typename T>
+void qt_scale_image_16bit(uchar *destPixels, int dbpl,
+ const uchar *srcPixels, int sbpl,
+ const QRectF &target,
+ const QRectF &srcRect,
+ const QRect &clip,
+ T blender)
+{
+ const QRectF targetRect = target.translated(aliasedCoordinateDelta,
+ aliasedCoordinateDelta);
+
+ qreal sx = targetRect.width() / (qreal) srcRect.width();
+ qreal sy = targetRect.height() / (qreal) srcRect.height();
+
+ int ix = 0x00010000 / sx;
+ int iy = 0x00010000 / sy;
+
+// qDebug() << "scale:" << endl
+// << " - target" << targetRect << endl
+// << " - source" << srcRect << endl
+// << " - clip" << clip << endl
+// << " - sx=" << sx << " sy=" << sy << " ix=" << ix << " iy=" << iy;
+
+ int cx1 = clip.x();
+ int cx2 = clip.x() + clip.width();
+ int cy1 = clip.top();
+ int cy2 = clip.y() + clip.height();
+
+ int tx1 = qRound(targetRect.left());
+ int tx2 = qRound(targetRect.right());
+ int ty1 = qRound(targetRect.top());
+ int ty2 = qRound(targetRect.bottom());
+
+ if (tx2 < tx1)
+ qSwap(tx2, tx1);
+
+ if (ty2 < ty1)
+ qSwap(ty2, ty1);
+
+ if (tx1 < cx1)
+ tx1 = cx1;
+
+ if (tx2 >= cx2)
+ tx2 = cx2;
+
+ if (tx1 >= tx2)
+ return;
+
+ if (ty1 < cy1)
+ ty1 = cy1;
+
+ if (ty2 >= cy2)
+ ty2 = cy2;
+
+ if (ty1 >= ty2)
+ return;
+
+ int h = ty2 - ty1;
+ int w = tx2 - tx1;
+
+ const int dstx = int((tx1 + 0.5 - qMin(targetRect.left(), targetRect.right())) * ix);
+ const int dsty = int((ty1 + 0.5 - qMin(targetRect.top(), targetRect.bottom())) * iy);
+
+ quint32 basex = quint32((sx < 0 ? srcRect.right() : srcRect.left()) * 65536) + dstx;
+ quint32 srcy = quint32((sy < 0 ? srcRect.bottom() : srcRect.top()) * 65536) + dsty;
+
+ quint16 *dst = ((quint16 *) (destPixels + ty1 * dbpl)) + tx1;
+
+ while (h--) {
+ const SRC *src = (const SRC *) (srcPixels + (srcy >> 16) * sbpl);
+ int srcx = basex;
+ for (int x=0; x<w; ++x) {
+ blender.write(&dst[x], src[srcx >> 16]);
+ srcx += ix;
+ }
+ dst = (quint16 *)(((uchar *) dst) + dbpl);
+ srcy += iy;
+ }
+}
+
+void qt_scale_image_rgb16_on_rgb16(uchar *destPixels, int dbpl,
+ const uchar *srcPixels, int sbpl,
+ const QRectF &targetRect,
+ const QRectF &sourceRect,
+ const QRect &clip,
+ int const_alpha)
+{
+#ifdef QT_DEBUG_DRAW
+ printf("qt_scale_rgb16_on_rgb16: dst=(%p, %d), src=(%p, %d), target=(%d, %d), [%d x %d], src=(%d, %d) [%d x %d] alpha=%d\n",
+ destPixels, dbpl, srcPixels, sbpl,
+ targetRect.x(), targetRect.y(), targetRect.width(), targetRect.height(),
+ sourceRect.x(), sourceRect.y(), sourceRect.width(), sourceRect.height(),
+ const_alpha);
+#endif
+ if (const_alpha == 256) {
+ Blend_RGB16_on_RGB16_NoAlpha noAlpha;
+ qt_scale_image_16bit<quint16>(destPixels, dbpl, srcPixels, sbpl,
+ targetRect, sourceRect, clip, noAlpha);
+ } else {
+ Blend_RGB16_on_RGB16_ConstAlpha constAlpha(const_alpha);
+ qt_scale_image_16bit<quint16>(destPixels, dbpl, srcPixels, sbpl,
+ targetRect, sourceRect, clip, constAlpha);
+ }
+}
+
+void qt_scale_image_argb32_on_rgb16(uchar *destPixels, int dbpl,
+ const uchar *srcPixels, int sbpl,
+ const QRectF &targetRect,
+ const QRectF &sourceRect,
+ const QRect &clip,
+ int const_alpha)
+{
+#ifdef QT_DEBUG_DRAW
+ printf("qt_scale_argb32_on_rgb16: dst=(%p, %d), src=(%p, %d), target=(%d, %d), [%d x %d], src=(%d, %d) [%d x %d] alpha=%d\n",
+ destPixels, dbpl, srcPixels, sbpl,
+ targetRect.x(), targetRect.y(), targetRect.width(), targetRect.height(),
+ sourceRect.x(), sourceRect.y(), sourceRect.width(), sourceRect.height(),
+ const_alpha);
+#endif
+ if (const_alpha == 256) {
+ Blend_ARGB32_on_RGB16_SourceAlpha noAlpha;
+ qt_scale_image_16bit<quint32>(destPixels, dbpl, srcPixels, sbpl,
+ targetRect, sourceRect, clip, noAlpha);
+ } else {
+ Blend_ARGB32_on_RGB16_SourceAndConstAlpha constAlpha(const_alpha);
+ qt_scale_image_16bit<quint32>(destPixels, dbpl, srcPixels, sbpl,
+ targetRect, sourceRect, clip, constAlpha);
+ }
+}
+
+static void qt_blend_rgb16_on_rgb16(uchar *dst, int dbpl,
+ const uchar *src, int sbpl,
+ int w, int h,
+ int const_alpha)
+{
+#ifdef QT_DEBUG_DRAW
+ printf("qt_blend_argb16_on_rgb16: dst=(%p, %d), src=(%p, %d), dim=(%d, %d) alpha=%d\n",
+ dst, dbpl, src, sbpl, w, h, const_alpha);
+#endif
+
+ if (const_alpha == 256) {
+ if (w <= 64) {
+ while (h--) {
+ QT_MEMCPY_USHORT(dst, src, w);
+ dst += dbpl;
+ src += sbpl;
+ }
+ } else {
+ int length = w << 1;
+ while (h--) {
+ memcpy(dst, src, length);
+ dst += dbpl;
+ src += sbpl;
+ }
+ }
+ } else if (const_alpha != 0) {
+ SourceAndConstAlpha alpha(const_alpha); // expects the 0-256 range
+ quint16 *d = (quint16 *) dst;
+ const quint16 *s = (const quint16 *) src;
+ quint8 a = (255 * const_alpha) >> 8;
+ quint8 ia = 255 - a;
+ while (h--) {
+ for (int x=0; x<w; ++x) {
+ d[x] = BYTE_MUL_RGB16(s[x], a) + BYTE_MUL_RGB16(d[x], ia);
+ }
+ d = (quint16 *)(((uchar *) d) + dbpl);
+ s = (const quint16 *)(((const uchar *) s) + sbpl);
+ }
+ }
+}
+
+
+template <typename T> void qt_blend_argb24_on_rgb16(uchar *destPixels, int dbpl,
+ const uchar *srcPixels, int sbpl,
+ int w, int h, const T &alphaFunc)
+{
+ int srcOffset = w*3;
+ int dstJPL = dbpl / 2;
+ quint16 *dst = (quint16 *) destPixels;
+ int dstExtraStride = dstJPL - w;
+
+ for (int y=0; y<h; ++y) {
+ const uchar *src = srcPixels + y * sbpl;
+ const uchar *srcEnd = src + srcOffset;
+ while (src < srcEnd) {
+#if defined(QT_ARCH_ARM) || defined(QT_ARCH_POWERPC) || (defined(QT_ARCH_WINDOWSCE) && !defined(_X86_))
+ // non-16-bit aligned memory access is not possible on PowerPC &
+ // ARM <v6 (QT_ARCH_ARMV6)
+ quint16 spix = (quint16(src[2])<<8) + src[1];
+#else
+ quint16 spix = *(quint16 *) (src + 1);
+#endif
+ uchar alpha = alphaFunc.alpha(*src);
+
+ if (alpha == 255) {
+ *dst = spix;
+ } else if (alpha != 0) {
+#ifdef QT_BLEND_USE_BYTEMUL
+ // truncate green channel to avoid overflow
+ *dst = (alphaFunc.bytemul(spix) & 0xffdf)
+ + (quint16) qrgb565(*dst).byte_mul(qrgb565::ialpha(alpha));
+#else
+ quint16 dpix = *dst;
+ quint32 sia = 255 - alpha;
+
+ quint16 dr = (dpix & 0x0000f800);
+ quint16 dg = (dpix & 0x000007e0);
+ quint16 db = (dpix & 0x0000001f);
+
+ quint32 siar = dr * sia;
+ quint32 siag = dg * sia;
+ quint32 siab = db * sia;
+
+ quint32 rr = ((siar + (siar>>8) + (0x80 << 11)) >> 8) & 0xf800;
+ quint32 rg = ((siag + (siag>>8) + (0x80 << 5)) >> 8) & 0x07e0;
+ quint32 rb = ((siab + (siab>>8) + (0x80 >> 3)) >> 8);
+
+ *dst = alphaFunc.bytemul(spix) + rr + rg + rb;
+#endif
+ }
+
+ ++dst;
+ src += 3;
+ }
+ dst += dstExtraStride;
+ }
+
+}
+
+static void qt_blend_argb24_on_rgb16(uchar *destPixels, int dbpl,
+ const uchar *srcPixels, int sbpl,
+ int w, int h,
+ int const_alpha)
+{
+#ifdef QT_DEBUG_DRAW
+ printf("qt_blend_argb24_on_rgb16: dst=(%p, %d), src=(%p, %d), dim=(%d, %d) alpha=%d\n",
+ destPixels, dbpl, srcPixels, sbpl, w, h, const_alpha);
+#endif
+
+ if (const_alpha != 256) {
+ SourceAndConstAlpha alphaFunc(const_alpha);
+ qt_blend_argb24_on_rgb16(destPixels, dbpl, srcPixels, sbpl, w, h, alphaFunc);
+ } else {
+ SourceOnlyAlpha alphaFunc;
+ qt_blend_argb24_on_rgb16(destPixels, dbpl, srcPixels, sbpl, w, h, alphaFunc);
+ }
+}
+
+
+
+
+static void qt_blend_argb32_on_rgb16_const_alpha(uchar *destPixels, int dbpl,
+ const uchar *srcPixels, int sbpl,
+ int w, int h,
+ int const_alpha)
+{
+ quint16 *dst = (quint16 *) destPixels;
+ const quint32 *src = (const quint32 *) srcPixels;
+
+ const_alpha = (const_alpha * 255) >> 8;
+ for (int y=0; y<h; ++y) {
+ for (int i = 0; i < w; ++i) {
+ uint s = src[i];
+ s = BYTE_MUL(s, const_alpha);
+ int alpha = qAlpha(s);
+ s = convert_argb32_to_rgb16(s);
+ s += BYTE_MUL_RGB16(dst[i], 255 - alpha);
+ dst[i] = s;
+ }
+ dst = (quint16 *)(((uchar *) dst) + dbpl);
+ src = (const quint32 *)(((const uchar *) src) + sbpl);
+ }
+}
+
+static void qt_blend_argb32_on_rgb16(uchar *destPixels, int dbpl,
+ const uchar *srcPixels, int sbpl,
+ int w, int h,
+ int const_alpha)
+{
+ if (const_alpha != 256) {
+ qt_blend_argb32_on_rgb16_const_alpha(destPixels, dbpl, srcPixels, sbpl, w, h, const_alpha);
+ return;
+ }
+
+ quint16 *dst = (quint16 *) destPixels;
+ int dstExtraStride = dbpl / 2 - w;
+
+ const quint32 *src = (const quint32 *) srcPixels;
+ int srcExtraStride = sbpl / 4 - w;
+
+ for (int y=0; y<h; ++y) {
+ int length = w;
+ const int dstAlign = ((quintptr)dst) & 0x3;
+ if (dstAlign) {
+ const quint8 alpha = qAlpha(*src);
+ if (alpha) {
+ quint16 s = convert_argb32_to_rgb16(*src);
+ if (alpha < 255)
+ s += BYTE_MUL_RGB16(*dst, 255 - alpha);
+ *dst = s;
+ }
+ ++dst;
+ ++src;
+ --length;
+ }
+
+ const int length32 = length >> 1;
+ const int srcAlign = ((quintptr)src) & 0x3;
+ if (length32) {
+ if (srcAlign) {
+ for (int i = 0; i < length32; ++i) {
+ quint32 *dest32 = reinterpret_cast<quint32*>(dst);
+ const quint8 a1 = qAlpha(src[0]);
+ const quint8 a2 = qAlpha(src[1]);
+ quint32 s;
+
+ if (!a1 && !a2) {
+ src += 2;
+ dst +=2;
+ continue;
+ }
+
+ s = convert_argb32_to_rgb16(src[0])
+ | (convert_argb32_to_rgb16(src[1]) << 16);
+
+ if (a1 == a2) {
+ if (a1 < 255) {
+ const quint8 sa = ((255 - a1)+1) >> 3;
+ s += BYTE_MUL_RGB16_32(*dest32, sa);
+ }
+ } else {
+ if (a1 < 255)
+ s += BYTE_MUL_RGB16(dst[0], 255 - a1);
+ if (a2 < 255)
+ s += BYTE_MUL_RGB16(dst[1], 255 - a2) << 16;
+ }
+
+ *dest32 = s;
+ src += 2;
+ dst += 2;
+ }
+ } else {
+ for (int i = 0; i < length32; ++i) {
+ quint32 *dest32 = reinterpret_cast<quint32*>(dst);
+ const quint8 a1 = qAlpha(src[0]);
+ const quint8 a2 = qAlpha(src[1]);
+ quint32 s;
+
+ if (!a1 && !a2) {
+ src += 2;
+ dst +=2;
+ continue;
+ }
+
+ const quint64 *src64 =
+ reinterpret_cast<const quint64*>(src);
+ s = qConvertRgb32To16x2(*src64);
+
+ if (a1 == a2) {
+ if (a1 < 255) {
+ const quint8 sa = ((255 - a1)+1) >> 3;
+ s += BYTE_MUL_RGB16_32(*dest32, sa);
+ }
+ } else {
+ if (a1 < 255)
+ s += BYTE_MUL_RGB16(dst[0], 255 - a1);
+ if (a2 < 255)
+ s += BYTE_MUL_RGB16(dst[1], 255 - a2) << 16;
+ }
+
+ *dest32 = s;
+ src += 2;
+ dst += 2;
+ }
+ }
+ }
+ const int tail = length & 0x1;
+ if (tail) {
+ const quint8 alpha = qAlpha(*src);
+ if (alpha) {
+ quint16 s = convert_argb32_to_rgb16(*src);
+ if (alpha < 255)
+ s += BYTE_MUL_RGB16(*dst, 255 - alpha);
+ *dst = s;
+ }
+ }
+ dst += dstExtraStride;
+ src += srcExtraStride;
+ }
+
+}
+
+
+static void qt_blend_rgb32_on_rgb16(uchar *destPixels, int dbpl,
+ const uchar *srcPixels, int sbpl,
+ int w, int h,
+ int const_alpha)
+{
+#ifdef QT_DEBUG_DRAW
+ printf("qt_blend_rgb32_on_rgb16: dst=(%p, %d), src=(%p, %d), dim=(%d, %d) alpha=%d\n",
+ destPixels, dbpl, srcPixels, sbpl, w, h, const_alpha);
+#endif
+
+ if (const_alpha != 256) {
+ qt_blend_argb32_on_rgb16(destPixels, dbpl, srcPixels, sbpl, w, h, const_alpha);
+ return;
+ }
+
+ const quint32 *src = (const quint32 *) srcPixels;
+ int srcExtraStride = (sbpl >> 2) - w;
+
+ int dstJPL = dbpl / 2;
+
+ quint16 *dst = (quint16 *) destPixels;
+ quint16 *dstEnd = dst + dstJPL * h;
+
+ int dstExtraStride = dstJPL - w;
+
+ while (dst < dstEnd) {
+ const quint32 *srcEnd = src + w;
+ while (src < srcEnd) {
+ *dst = convert_argb32_to_rgb16(*src);
+ ++dst;
+ ++src;
+ }
+ dst += dstExtraStride;
+ src += srcExtraStride;
+ }
+}
+
+
+
+/************************************************************************
+ RGB32 (-888) format target format
+ ************************************************************************/
+
+static void qt_blend_argb32_on_argb32(uchar *destPixels, int dbpl,
+ const uchar *srcPixels, int sbpl,
+ int w, int h,
+ int const_alpha)
+{
+#ifdef QT_DEBUG_DRAW
+ fprintf(stdout, "qt_blend_argb32_on_argb32: dst=(%p, %d), src=(%p, %d), dim=(%d, %d) alpha=%d\n",
+ destPixels, dbpl, srcPixels, sbpl, w, h, const_alpha);
+ fflush(stdout);
+#endif
+
+ const uint *src = (const uint *) srcPixels;
+ uint *dst = (uint *) destPixels;
+ if (const_alpha == 256) {
+ for (int y=0; y<h; ++y) {
+ for (int x=0; x<w; ++x) {
+ uint s = src[x];
+ if ((s & 0xff000000) == 0xff000000)
+ dst[x] = s;
+ else {
+ dst[x] = s + BYTE_MUL(dst[x], qAlpha(~s));
+ }
+ }
+ dst = (quint32 *)(((uchar *) dst) + dbpl);
+ src = (const quint32 *)(((const uchar *) src) + sbpl);
+ }
+ } else if (const_alpha != 0) {
+ const_alpha = (const_alpha * 255) >> 8;
+ for (int y=0; y<h; ++y) {
+ for (int x=0; x<w; ++x) {
+ uint s = BYTE_MUL(src[x], const_alpha);
+ dst[x] = s + BYTE_MUL(dst[x], qAlpha(~s));
+ }
+ dst = (quint32 *)(((uchar *) dst) + dbpl);
+ src = (const quint32 *)(((const uchar *) src) + sbpl);
+ }
+ }
+}
+
+
+static void qt_blend_rgb32_on_rgb32(uchar *destPixels, int dbpl,
+ const uchar *srcPixels, int sbpl,
+ int w, int h,
+ int const_alpha)
+{
+#ifdef QT_DEBUG_DRAW
+ fprintf(stdout, "qt_blend_rgb32_on_rgb32: dst=(%p, %d), src=(%p, %d), dim=(%d, %d) alpha=%d\n",
+ destPixels, dbpl, srcPixels, sbpl, w, h, const_alpha);
+ fflush(stdout);
+#endif
+
+ if (const_alpha != 256) {
+ qt_blend_argb32_on_argb32(destPixels, dbpl, srcPixels, sbpl, w, h, const_alpha);
+ return;
+ }
+
+ const uint *src = (const uint *) srcPixels;
+ uint *dst = (uint *) destPixels;
+ if (w <= 64) {
+ for (int y=0; y<h; ++y) {
+ qt_memconvert(dst, src, w);
+ dst = (quint32 *)(((uchar *) dst) + dbpl);
+ src = (const quint32 *)(((const uchar *) src) + sbpl);
+ }
+ } else {
+ int len = w * 4;
+ for (int y=0; y<h; ++y) {
+ memcpy(dst, src, len);
+ dst = (quint32 *)(((uchar *) dst) + dbpl);
+ src = (const quint32 *)(((const uchar *) src) + sbpl);
+ }
+ }
+}
+
+
+
+struct Blend_RGB32_on_RGB32_NoAlpha {
+ inline void write(quint32 *dst, quint32 src) { *dst = src; }
+};
+
+struct Blend_RGB32_on_RGB32_ConstAlpha {
+ inline Blend_RGB32_on_RGB32_ConstAlpha(quint32 alpha) {
+ m_alpha = (alpha * 255) >> 8;
+ m_ialpha = 255 - m_alpha;
+ }
+
+ inline void write(quint32 *dst, quint32 src) {
+ *dst = BYTE_MUL(src, m_alpha) + BYTE_MUL(*dst, m_ialpha);
+ }
+
+ quint32 m_alpha;
+ quint32 m_ialpha;
+};
+
+struct Blend_ARGB32_on_ARGB32_SourceAlpha {
+ inline void write(quint32 *dst, quint32 src) {
+ *dst = src + BYTE_MUL(*dst, qAlpha(~src));
+ }
+};
+
+struct Blend_ARGB32_on_ARGB32_SourceAndConstAlpha {
+ inline Blend_ARGB32_on_ARGB32_SourceAndConstAlpha(quint32 alpha) {
+ m_alpha = (alpha * 255) >> 8;
+ m_ialpha = 255 - m_alpha;
+ }
+
+ inline void write(quint32 *dst, quint32 src) {
+ src = BYTE_MUL(src, m_alpha);
+ *dst = src + BYTE_MUL(*dst, qAlpha(~src));
+ }
+
+ quint32 m_alpha;
+ quint32 m_ialpha;
+};
+
+template <typename T> void qt_scale_image_32bit(uchar *destPixels, int dbpl,
+ const uchar *srcPixels, int sbpl,
+ const QRectF &target,
+ const QRectF &srcRect,
+ const QRect &clip,
+ T blender)
+{
+ const QRectF targetRect = target.translated(aliasedCoordinateDelta,
+ aliasedCoordinateDelta);
+
+ qreal sx = targetRect.width() / (qreal) srcRect.width();
+ qreal sy = targetRect.height() / (qreal) srcRect.height();
+
+ int ix = 0x00010000 / sx;
+ int iy = 0x00010000 / sy;
+
+// qDebug() << "scale:" << endl
+// << " - target" << targetRect << endl
+// << " - source" << srcRect << endl
+// << " - clip" << clip << endl
+// << " - sx=" << sx << " sy=" << sy << " ix=" << ix << " iy=" << iy;
+
+ int cx1 = clip.x();
+ int cx2 = clip.x() + clip.width();
+ int cy1 = clip.top();
+ int cy2 = clip.y() + clip.height();
+
+ int tx1 = qRound(targetRect.left());
+ int tx2 = qRound(targetRect.right());
+ int ty1 = qRound(targetRect.top());
+ int ty2 = qRound(targetRect.bottom());
+
+ if (tx2 < tx1)
+ qSwap(tx2, tx1);
+
+ if (ty2 < ty1)
+ qSwap(ty2, ty1);
+
+ if (tx1 < cx1)
+ tx1 = cx1;
+
+ if (tx2 >= cx2)
+ tx2 = cx2;
+
+ if (tx1 >= tx2)
+ return;
+
+ if (ty1 < cy1)
+ ty1 = cy1;
+
+ if (ty2 >= cy2)
+ ty2 = cy2;
+
+ if (ty1 >= ty2)
+ return;
+
+ int h = ty2 - ty1;
+ int w = tx2 - tx1;
+
+ const int dstx = int((tx1 + 0.5 - qMin(targetRect.left(), targetRect.right())) * ix);
+ const int dsty = int((ty1 + 0.5 - qMin(targetRect.top(), targetRect.bottom())) * iy);
+
+ quint32 basex = quint32((sx < 0 ? srcRect.right() : srcRect.left()) * 65536) + dstx;
+ quint32 srcy = quint32((sy < 0 ? srcRect.bottom() : srcRect.top()) * 65536) + dsty;
+
+ quint32 *dst = ((quint32 *) (destPixels + ty1 * dbpl)) + tx1;
+
+ while (h--) {
+ const uint *src = (const quint32 *) (srcPixels + (srcy >> 16) * sbpl);
+ int srcx = basex;
+ for (int x=0; x<w; ++x) {
+ blender.write(&dst[x], src[srcx >> 16]);
+ srcx += ix;
+ }
+ dst = (quint32 *)(((uchar *) dst) + dbpl);
+ srcy += iy;
+ }
+}
+
+void qt_scale_image_rgb32_on_rgb32(uchar *destPixels, int dbpl,
+ const uchar *srcPixels, int sbpl,
+ const QRectF &targetRect,
+ const QRectF &sourceRect,
+ const QRect &clip,
+ int const_alpha)
+{
+#ifdef QT_DEBUG_DRAW
+ printf("qt_scale_rgb32_on_rgb32: dst=(%p, %d), src=(%p, %d), target=(%d, %d), [%d x %d], src=(%d, %d) [%d x %d] alpha=%d\n",
+ destPixels, dbpl, srcPixels, sbpl,
+ targetRect.x(), targetRect.y(), targetRect.width(), targetRect.height(),
+ sourceRect.x(), sourceRect.y(), sourceRect.width(), sourceRect.height(),
+ const_alpha);
+#endif
+ if (const_alpha == 256) {
+ Blend_RGB32_on_RGB32_NoAlpha noAlpha;
+ qt_scale_image_32bit(destPixels, dbpl, srcPixels, sbpl,
+ targetRect, sourceRect, clip, noAlpha);
+ } else {
+ Blend_RGB32_on_RGB32_ConstAlpha constAlpha(const_alpha);
+ qt_scale_image_32bit(destPixels, dbpl, srcPixels, sbpl,
+ targetRect, sourceRect, clip, constAlpha);
+ }
+}
+
+void qt_scale_image_argb32_on_argb32(uchar *destPixels, int dbpl,
+ const uchar *srcPixels, int sbpl,
+ const QRectF &targetRect,
+ const QRectF &sourceRect,
+ const QRect &clip,
+ int const_alpha)
+{
+#ifdef QT_DEBUG_DRAW
+ printf("qt_scale_argb32_on_argb32: dst=(%p, %d), src=(%p, %d), target=(%d, %d), [%d x %d], src=(%d, %d) [%d x %d] alpha=%d\n",
+ destPixels, dbpl, srcPixels, sbpl,
+ targetRect.x(), targetRect.y(), targetRect.width(), targetRect.height(),
+ sourceRect.x(), sourceRect.y(), sourceRect.width(), sourceRect.height(),
+ const_alpha);
+#endif
+ if (const_alpha == 256) {
+ Blend_ARGB32_on_ARGB32_SourceAlpha sourceAlpha;
+ qt_scale_image_32bit(destPixels, dbpl, srcPixels, sbpl,
+ targetRect, sourceRect, clip, sourceAlpha);
+ } else {
+ Blend_ARGB32_on_ARGB32_SourceAndConstAlpha constAlpha(const_alpha);
+ qt_scale_image_32bit(destPixels, dbpl, srcPixels, sbpl,
+ targetRect, sourceRect, clip, constAlpha);
+ }
+}
+
+
+
+SrcOverScaleFunc qScaleFunctions[QImage::NImageFormats][QImage::NImageFormats] = {
+ { // Format_Invalid
+ 0, // Format_Invalid,
+ 0, // Format_Mono,
+ 0, // Format_MonoLSB,
+ 0, // Format_Indexed8,
+ 0, // Format_RGB32,
+ 0, // Format_ARGB32,
+ 0, // Format_ARGB32_Premultiplied,
+ 0, // Format_RGB16,
+ 0, // Format_ARGB8565_Premultiplied,
+ 0, // Format_RGB666,
+ 0, // Format_ARGB6666_Premultiplied,
+ 0, // Format_RGB555,
+ 0, // Format_ARGB8555_Premultiplied,
+ 0, // Format_RGB888,
+ 0, // Format_RGB444,
+ 0 // Format_ARGB4444_Premultiplied,
+ },
+ { // Format_Mono
+ 0, // Format_Invalid,
+ 0, // Format_Mono,
+ 0, // Format_MonoLSB,
+ 0, // Format_Indexed8,
+ 0, // Format_RGB32,
+ 0, // Format_ARGB32,
+ 0, // Format_ARGB32_Premultiplied,
+ 0, // Format_RGB16,
+ 0, // Format_ARGB8565_Premultiplied,
+ 0, // Format_RGB666,
+ 0, // Format_ARGB6666_Premultiplied,
+ 0, // Format_RGB555,
+ 0, // Format_ARGB8555_Premultiplied,
+ 0, // Format_RGB888,
+ 0, // Format_RGB444,
+ 0 // Format_ARGB4444_Premultiplied,
+ },
+ { // Format_MonoLSB
+ 0, // Format_Invalid,
+ 0, // Format_Mono,
+ 0, // Format_MonoLSB,
+ 0, // Format_Indexed8,
+ 0, // Format_RGB32,
+ 0, // Format_ARGB32,
+ 0, // Format_ARGB32_Premultiplied,
+ 0, // Format_RGB16,
+ 0, // Format_ARGB8565_Premultiplied,
+ 0, // Format_RGB666,
+ 0, // Format_ARGB6666_Premultiplied,
+ 0, // Format_RGB555,
+ 0, // Format_ARGB8555_Premultiplied,
+ 0, // Format_RGB888,
+ 0, // Format_RGB444,
+ 0 // Format_ARGB4444_Premultiplied,
+ },
+ { // Format_Indexed8
+ 0, // Format_Invalid,
+ 0, // Format_Mono,
+ 0, // Format_MonoLSB,
+ 0, // Format_Indexed8,
+ 0, // Format_RGB32,
+ 0, // Format_ARGB32,
+ 0, // Format_ARGB32_Premultiplied,
+ 0, // Format_RGB16,
+ 0, // Format_ARGB8565_Premultiplied,
+ 0, // Format_RGB666,
+ 0, // Format_ARGB6666_Premultiplied,
+ 0, // Format_RGB555,
+ 0, // Format_ARGB8555_Premultiplied,
+ 0, // Format_RGB888,
+ 0, // Format_RGB444,
+ 0 // Format_ARGB4444_Premultiplied,
+ },
+ { // Format_RGB32
+ 0, // Format_Invalid,
+ 0, // Format_Mono,
+ 0, // Format_MonoLSB,
+ 0, // Format_Indexed8,
+ qt_scale_image_rgb32_on_rgb32, // Format_RGB32,
+ 0, // Format_ARGB32,
+ qt_scale_image_argb32_on_argb32, // Format_ARGB32_Premultiplied,
+ 0, // Format_RGB16,
+ 0, // Format_ARGB8565_Premultiplied,
+ 0, // Format_RGB666,
+ 0, // Format_ARGB6666_Premultiplied,
+ 0, // Format_RGB555,
+ 0, // Format_ARGB8555_Premultiplied,
+ 0, // Format_RGB888,
+ 0, // Format_RGB444,
+ 0 // Format_ARGB4444_Premultiplied,
+ },
+ { // Format_ARGB32
+ 0, // Format_Invalid,
+ 0, // Format_Mono,
+ 0, // Format_MonoLSB,
+ 0, // Format_Indexed8,
+ 0, // Format_RGB32,
+ 0, // Format_ARGB32,
+ 0, // Format_ARGB32_Premultiplied,
+ 0, // Format_RGB16,
+ 0, // Format_ARGB8565_Premultiplied,
+ 0, // Format_RGB666,
+ 0, // Format_ARGB6666_Premultiplied,
+ 0, // Format_RGB555,
+ 0, // Format_ARGB8555_Premultiplied,
+ 0, // Format_RGB888,
+ 0, // Format_RGB444,
+ 0 // Format_ARGB4444_Premultiplied,
+ },
+ { // Format_ARGB32_Premultiplied
+ 0, // Format_Invalid,
+ 0, // Format_Mono,
+ 0, // Format_MonoLSB,
+ 0, // Format_Indexed8,
+ qt_scale_image_rgb32_on_rgb32, // Format_RGB32,
+ 0, // Format_ARGB32,
+ qt_scale_image_argb32_on_argb32, // Format_ARGB32_Premultiplied,
+ 0, // Format_RGB16,
+ 0, // Format_ARGB8565_Premultiplied,
+ 0, // Format_RGB666,
+ 0, // Format_ARGB6666_Premultiplied,
+ 0, // Format_RGB555,
+ 0, // Format_ARGB8555_Premultiplied,
+ 0, // Format_RGB888,
+ 0, // Format_RGB444,
+ 0 // Format_ARGB4444_Premultiplied,
+ },
+ { // Format_RGB16
+ 0, // Format_Invalid,
+ 0, // Format_Mono,
+ 0, // Format_MonoLSB,
+ 0, // Format_Indexed8,
+ 0, // Format_RGB32,
+ 0, // Format_ARGB32,
+ qt_scale_image_argb32_on_rgb16, // Format_ARGB32_Premultiplied,
+ qt_scale_image_rgb16_on_rgb16, // Format_RGB16,
+ 0, // Format_ARGB8565_Premultiplied,
+ 0, // Format_RGB666,
+ 0, // Format_ARGB6666_Premultiplied,
+ 0, // Format_RGB555,
+ 0, // Format_ARGB8555_Premultiplied,
+ 0, // Format_RGB888,
+ 0, // Format_RGB444,
+ 0 // Format_ARGB4444_Premultiplied,
+ },
+ { // Format_ARGB8565_Premultiplied
+ 0, // Format_Invalid,
+ 0, // Format_Mono,
+ 0, // Format_MonoLSB,
+ 0, // Format_Indexed8,
+ 0, // Format_RGB32,
+ 0, // Format_ARGB32,
+ 0, // Format_ARGB32_Premultiplied,
+ 0, // Format_RGB16,
+ 0, // Format_ARGB8565_Premultiplied,
+ 0, // Format_RGB666,
+ 0, // Format_ARGB6666_Premultiplied,
+ 0, // Format_RGB555,
+ 0, // Format_ARGB8555_Premultiplied,
+ 0, // Format_RGB888,
+ 0, // Format_RGB444,
+ 0 // Format_ARGB4444_Premultiplied,
+ },
+ { // Format_RGB666
+ 0, // Format_Invalid,
+ 0, // Format_Mono,
+ 0, // Format_MonoLSB,
+ 0, // Format_Indexed8,
+ 0, // Format_RGB32,
+ 0, // Format_ARGB32,
+ 0, // Format_ARGB32_Premultiplied,
+ 0, // Format_RGB16,
+ 0, // Format_ARGB8565_Premultiplied,
+ 0, // Format_RGB666,
+ 0, // Format_ARGB6666_Premultiplied,
+ 0, // Format_RGB555,
+ 0, // Format_ARGB8555_Premultiplied,
+ 0, // Format_RGB888,
+ 0, // Format_RGB444,
+ 0 // Format_ARGB4444_Premultiplied,
+ },
+ { // Format_ARGB6666_Premultiplied
+ 0, // Format_Invalid,
+ 0, // Format_Mono,
+ 0, // Format_MonoLSB,
+ 0, // Format_Indexed8,
+ 0, // Format_RGB32,
+ 0, // Format_ARGB32,
+ 0, // Format_ARGB32_Premultiplied,
+ 0, // Format_RGB16,
+ 0, // Format_ARGB8565_Premultiplied,
+ 0, // Format_RGB666,
+ 0, // Format_ARGB6666_Premultiplied,
+ 0, // Format_RGB555,
+ 0, // Format_ARGB8555_Premultiplied,
+ 0, // Format_RGB888,
+ 0, // Format_RGB444,
+ 0 // Format_ARGB4444_Premultiplied,
+ },
+ { // Format_RGB555
+ 0, // Format_Invalid,
+ 0, // Format_Mono,
+ 0, // Format_MonoLSB,
+ 0, // Format_Indexed8,
+ 0, // Format_RGB32,
+ 0, // Format_ARGB32,
+ 0, // Format_ARGB32_Premultiplied,
+ 0, // Format_RGB16,
+ 0, // Format_ARGB8565_Premultiplied,
+ 0, // Format_RGB666,
+ 0, // Format_ARGB6666_Premultiplied,
+ 0, // Format_RGB555,
+ 0, // Format_ARGB8555_Premultiplied,
+ 0, // Format_RGB888,
+ 0, // Format_RGB444,
+ 0 // Format_ARGB4444_Premultiplied,
+ },
+ { // Format_ARGB8555_Premultiplied
+ 0, // Format_Invalid,
+ 0, // Format_Mono,
+ 0, // Format_MonoLSB,
+ 0, // Format_Indexed8,
+ 0, // Format_RGB32,
+ 0, // Format_ARGB32,
+ 0, // Format_ARGB32_Premultiplied,
+ 0, // Format_RGB16,
+ 0, // Format_ARGB8565_Premultiplied,
+ 0, // Format_RGB666,
+ 0, // Format_ARGB6666_Premultiplied,
+ 0, // Format_RGB555,
+ 0, // Format_ARGB8555_Premultiplied,
+ 0, // Format_RGB888,
+ 0, // Format_RGB444,
+ 0 // Format_ARGB4444_Premultiplied,
+ },
+ { // Format_RGB888
+ 0, // Format_Invalid,
+ 0, // Format_Mono,
+ 0, // Format_MonoLSB,
+ 0, // Format_Indexed8,
+ 0, // Format_RGB32,
+ 0, // Format_ARGB32,
+ 0, // Format_ARGB32_Premultiplied,
+ 0, // Format_RGB16,
+ 0, // Format_ARGB8565_Premultiplied,
+ 0, // Format_RGB666,
+ 0, // Format_ARGB6666_Premultiplied,
+ 0, // Format_RGB555,
+ 0, // Format_ARGB8555_Premultiplied,
+ 0, // Format_RGB888,
+ 0, // Format_RGB444,
+ 0 // Format_ARGB4444_Premultiplied,
+ },
+ { // Format_RGB444
+ 0, // Format_Invalid,
+ 0, // Format_Mono,
+ 0, // Format_MonoLSB,
+ 0, // Format_Indexed8,
+ 0, // Format_RGB32,
+ 0, // Format_ARGB32,
+ 0, // Format_ARGB32_Premultiplied,
+ 0, // Format_RGB16,
+ 0, // Format_ARGB8565_Premultiplied,
+ 0, // Format_RGB666,
+ 0, // Format_ARGB6666_Premultiplied,
+ 0, // Format_RGB555,
+ 0, // Format_ARGB8555_Premultiplied,
+ 0, // Format_RGB888,
+ 0, // Format_RGB444,
+ 0 // Format_ARGB4444_Premultiplied,
+ },
+ { // Format_ARGB4444_Premultiplied
+ 0, // Format_Invalid,
+ 0, // Format_Mono,
+ 0, // Format_MonoLSB,
+ 0, // Format_Indexed8,
+ 0, // Format_RGB32,
+ 0, // Format_ARGB32,
+ 0, // Format_ARGB32_Premultiplied,
+ 0, // Format_RGB16,
+ 0, // Format_ARGB8565_Premultiplied,
+ 0, // Format_RGB666,
+ 0, // Format_ARGB6666_Premultiplied,
+ 0, // Format_RGB555,
+ 0, // Format_ARGB8555_Premultiplied,
+ 0, // Format_RGB888,
+ 0, // Format_RGB444,
+ 0 // Format_ARGB4444_Premultiplied,
+ }
+};
+
+
+SrcOverBlendFunc qBlendFunctions[QImage::NImageFormats][QImage::NImageFormats] = {
+ { // Format_Invalid
+ 0, // Format_Invalid,
+ 0, // Format_Mono,
+ 0, // Format_MonoLSB,
+ 0, // Format_Indexed8,
+ 0, // Format_RGB32,
+ 0, // Format_ARGB32,
+ 0, // Format_ARGB32_Premultiplied,
+ 0, // Format_RGB16,
+ 0, // Format_ARGB8565_Premultiplied,
+ 0, // Format_RGB666,
+ 0, // Format_ARGB6666_Premultiplied,
+ 0, // Format_RGB555,
+ 0, // Format_ARGB8555_Premultiplied,
+ 0, // Format_RGB888,
+ 0, // Format_RGB444,
+ 0 // Format_ARGB4444_Premultiplied,
+ },
+ { // Format_Mono
+ 0, // Format_Invalid,
+ 0, // Format_Mono,
+ 0, // Format_MonoLSB,
+ 0, // Format_Indexed8,
+ 0, // Format_RGB32,
+ 0, // Format_ARGB32,
+ 0, // Format_ARGB32_Premultiplied,
+ 0, // Format_RGB16,
+ 0, // Format_ARGB8565_Premultiplied,
+ 0, // Format_RGB666,
+ 0, // Format_ARGB6666_Premultiplied,
+ 0, // Format_RGB555,
+ 0, // Format_ARGB8555_Premultiplied,
+ 0, // Format_RGB888,
+ 0, // Format_RGB444,
+ 0 // Format_ARGB4444_Premultiplied,
+ },
+ { // Format_MonoLSB
+ 0, // Format_Invalid,
+ 0, // Format_Mono,
+ 0, // Format_MonoLSB,
+ 0, // Format_Indexed8,
+ 0, // Format_RGB32,
+ 0, // Format_ARGB32,
+ 0, // Format_ARGB32_Premultiplied,
+ 0, // Format_RGB16,
+ 0, // Format_ARGB8565_Premultiplied,
+ 0, // Format_RGB666,
+ 0, // Format_ARGB6666_Premultiplied,
+ 0, // Format_RGB555,
+ 0, // Format_ARGB8555_Premultiplied,
+ 0, // Format_RGB888,
+ 0, // Format_RGB444,
+ 0 // Format_ARGB4444_Premultiplied,
+ },
+ { // Format_Indexed8
+ 0, // Format_Invalid,
+ 0, // Format_Mono,
+ 0, // Format_MonoLSB,
+ 0, // Format_Indexed8,
+ 0, // Format_RGB32,
+ 0, // Format_ARGB32,
+ 0, // Format_ARGB32_Premultiplied,
+ 0, // Format_RGB16,
+ 0, // Format_ARGB8565_Premultiplied,
+ 0, // Format_RGB666,
+ 0, // Format_ARGB6666_Premultiplied,
+ 0, // Format_RGB555,
+ 0, // Format_ARGB8555_Premultiplied,
+ 0, // Format_RGB888,
+ 0, // Format_RGB444,
+ 0 // Format_ARGB4444_Premultiplied,
+ },
+ { // Format_RGB32
+ 0, // Format_Invalid,
+ 0, // Format_Mono,
+ 0, // Format_MonoLSB,
+ 0, // Format_Indexed8,
+ qt_blend_rgb32_on_rgb32, // Format_RGB32,
+ 0, // Format_ARGB32,
+ qt_blend_argb32_on_argb32, // Format_ARGB32_Premultiplied,
+ 0, // Format_RGB16,
+ 0, // Format_ARGB8565_Premultiplied,
+ 0, // Format_RGB666,
+ 0, // Format_ARGB6666_Premultiplied,
+ 0, // Format_RGB555,
+ 0, // Format_ARGB8555_Premultiplied,
+ 0, // Format_RGB888,
+ 0, // Format_RGB444,
+ 0 // Format_ARGB4444_Premultiplied,
+ },
+ { // Format_ARGB32
+ 0, // Format_Invalid,
+ 0, // Format_Mono,
+ 0, // Format_MonoLSB,
+ 0, // Format_Indexed8,
+ 0, // Format_RGB32,
+ 0, // Format_ARGB32,
+ 0, // Format_ARGB32_Premultiplied,
+ 0, // Format_RGB16,
+ 0, // Format_ARGB8565_Premultiplied,
+ 0, // Format_RGB666,
+ 0, // Format_ARGB6666_Premultiplied,
+ 0, // Format_RGB555,
+ 0, // Format_ARGB8555_Premultiplied,
+ 0, // Format_RGB888,
+ 0, // Format_RGB444,
+ 0 // Format_ARGB4444_Premultiplied,
+ },
+ { // Format_ARGB32_Premultiplied
+ 0, // Format_Invalid,
+ 0, // Format_Mono,
+ 0, // Format_MonoLSB,
+ 0, // Format_Indexed8,
+ qt_blend_rgb32_on_rgb32, // Format_RGB32,
+ 0, // Format_ARGB32,
+ qt_blend_argb32_on_argb32, // Format_ARGB32_Premultiplied,
+ 0, // Format_RGB16,
+ 0, // Format_ARGB8565_Premultiplied,
+ 0, // Format_RGB666,
+ 0, // Format_ARGB6666_Premultiplied,
+ 0, // Format_RGB555,
+ 0, // Format_ARGB8555_Premultiplied,
+ 0, // Format_RGB888,
+ 0, // Format_RGB444,
+ 0 // Format_ARGB4444_Premultiplied,
+ },
+ { // Format_RGB16
+ 0, // Format_Invalid,
+ 0, // Format_Mono,
+ 0, // Format_MonoLSB,
+ 0, // Format_Indexed8,
+ qt_blend_rgb32_on_rgb16, // Format_RGB32,
+ 0, // Format_ARGB32,
+ qt_blend_argb32_on_rgb16, // Format_ARGB32_Premultiplied,
+ qt_blend_rgb16_on_rgb16, // Format_RGB16,
+ qt_blend_argb24_on_rgb16, // Format_ARGB8565_Premultiplied,
+ 0, // Format_RGB666,
+ 0, // Format_ARGB6666_Premultiplied,
+ 0, // Format_RGB555,
+ 0, // Format_ARGB8555_Premultiplied,
+ 0, // Format_RGB888,
+ 0, // Format_RGB444,
+ 0 // Format_ARGB4444_Premultiplied,
+ },
+ { // Format_ARGB8565_Premultiplied
+ 0, // Format_Invalid,
+ 0, // Format_Mono,
+ 0, // Format_MonoLSB,
+ 0, // Format_Indexed8,
+ 0, // Format_RGB32,
+ 0, // Format_ARGB32,
+ 0, // Format_ARGB32_Premultiplied,
+ 0, // Format_RGB16,
+ 0, // Format_ARGB8565_Premultiplied,
+ 0, // Format_RGB666,
+ 0, // Format_ARGB6666_Premultiplied,
+ 0, // Format_RGB555,
+ 0, // Format_ARGB8555_Premultiplied,
+ 0, // Format_RGB888,
+ 0, // Format_RGB444,
+ 0 // Format_ARGB4444_Premultiplied,
+ },
+ { // Format_RGB666
+ 0, // Format_Invalid,
+ 0, // Format_Mono,
+ 0, // Format_MonoLSB,
+ 0, // Format_Indexed8,
+ 0, // Format_RGB32,
+ 0, // Format_ARGB32,
+ 0, // Format_ARGB32_Premultiplied,
+ 0, // Format_RGB16,
+ 0, // Format_ARGB8565_Premultiplied,
+ 0, // Format_RGB666,
+ 0, // Format_ARGB6666_Premultiplied,
+ 0, // Format_RGB555,
+ 0, // Format_ARGB8555_Premultiplied,
+ 0, // Format_RGB888,
+ 0, // Format_RGB444,
+ 0 // Format_ARGB4444_Premultiplied,
+ },
+ { // Format_ARGB6666_Premultiplied
+ 0, // Format_Invalid,
+ 0, // Format_Mono,
+ 0, // Format_MonoLSB,
+ 0, // Format_Indexed8,
+ 0, // Format_RGB32,
+ 0, // Format_ARGB32,
+ 0, // Format_ARGB32_Premultiplied,
+ 0, // Format_RGB16,
+ 0, // Format_ARGB8565_Premultiplied,
+ 0, // Format_RGB666,
+ 0, // Format_ARGB6666_Premultiplied,
+ 0, // Format_RGB555,
+ 0, // Format_ARGB8555_Premultiplied,
+ 0, // Format_RGB888,
+ 0, // Format_RGB444,
+ 0 // Format_ARGB4444_Premultiplied,
+ },
+ { // Format_RGB555
+ 0, // Format_Invalid,
+ 0, // Format_Mono,
+ 0, // Format_MonoLSB,
+ 0, // Format_Indexed8,
+ 0, // Format_RGB32,
+ 0, // Format_ARGB32,
+ 0, // Format_ARGB32_Premultiplied,
+ 0, // Format_RGB16,
+ 0, // Format_ARGB8565_Premultiplied,
+ 0, // Format_RGB666,
+ 0, // Format_ARGB6666_Premultiplied,
+ 0, // Format_RGB555,
+ 0, // Format_ARGB8555_Premultiplied,
+ 0, // Format_RGB888,
+ 0, // Format_RGB444,
+ 0 // Format_ARGB4444_Premultiplied,
+ },
+ { // Format_ARGB8555_Premultiplied
+ 0, // Format_Invalid,
+ 0, // Format_Mono,
+ 0, // Format_MonoLSB,
+ 0, // Format_Indexed8,
+ 0, // Format_RGB32,
+ 0, // Format_ARGB32,
+ 0, // Format_ARGB32_Premultiplied,
+ 0, // Format_RGB16,
+ 0, // Format_ARGB8565_Premultiplied,
+ 0, // Format_RGB666,
+ 0, // Format_ARGB6666_Premultiplied,
+ 0, // Format_RGB555,
+ 0, // Format_ARGB8555_Premultiplied,
+ 0, // Format_RGB888,
+ 0, // Format_RGB444,
+ 0 // Format_ARGB4444_Premultiplied,
+ },
+ { // Format_RGB888
+ 0, // Format_Invalid,
+ 0, // Format_Mono,
+ 0, // Format_MonoLSB,
+ 0, // Format_Indexed8,
+ 0, // Format_RGB32,
+ 0, // Format_ARGB32,
+ 0, // Format_ARGB32_Premultiplied,
+ 0, // Format_RGB16,
+ 0, // Format_ARGB8565_Premultiplied,
+ 0, // Format_RGB666,
+ 0, // Format_ARGB6666_Premultiplied,
+ 0, // Format_RGB555,
+ 0, // Format_ARGB8555_Premultiplied,
+ 0, // Format_RGB888,
+ 0, // Format_RGB444,
+ 0 // Format_ARGB4444_Premultiplied,
+ },
+ { // Format_RGB444
+ 0, // Format_Invalid,
+ 0, // Format_Mono,
+ 0, // Format_MonoLSB,
+ 0, // Format_Indexed8,
+ 0, // Format_RGB32,
+ 0, // Format_ARGB32,
+ 0, // Format_ARGB32_Premultiplied,
+ 0, // Format_RGB16,
+ 0, // Format_ARGB8565_Premultiplied,
+ 0, // Format_RGB666,
+ 0, // Format_ARGB6666_Premultiplied,
+ 0, // Format_RGB555,
+ 0, // Format_ARGB8555_Premultiplied,
+ 0, // Format_RGB888,
+ 0, // Format_RGB444,
+ 0 // Format_ARGB4444_Premultiplied,
+ },
+ { // Format_ARGB4444_Premultiplied
+ 0, // Format_Invalid,
+ 0, // Format_Mono,
+ 0, // Format_MonoLSB,
+ 0, // Format_Indexed8,
+ 0, // Format_RGB32,
+ 0, // Format_ARGB32,
+ 0, // Format_ARGB32_Premultiplied,
+ 0, // Format_RGB16,
+ 0, // Format_ARGB8565_Premultiplied,
+ 0, // Format_RGB666,
+ 0, // Format_ARGB6666_Premultiplied,
+ 0, // Format_RGB555,
+ 0, // Format_ARGB8555_Premultiplied,
+ 0, // Format_RGB888,
+ 0, // Format_RGB444,
+ 0 // Format_ARGB4444_Premultiplied,
+ }
+};
+
+
+QT_END_NAMESPACE
diff --git a/src/gui/painting/qbrush.cpp b/src/gui/painting/qbrush.cpp
new file mode 100644
index 0000000000..854d0aa0af
--- /dev/null
+++ b/src/gui/painting/qbrush.cpp
@@ -0,0 +1,2148 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qbrush.h"
+#include "qpixmap.h"
+#include "qbitmap.h"
+#include "qpixmapcache.h"
+#include "qdatastream.h"
+#include "qvariant.h"
+#include "qline.h"
+#include "qdebug.h"
+#include <QtCore/qcoreapplication.h>
+
+QT_BEGIN_NAMESPACE
+
+const uchar *qt_patternForBrush(int brushStyle, bool invert)
+{
+ Q_ASSERT(brushStyle > Qt::SolidPattern && brushStyle < Qt::LinearGradientPattern);
+ if(invert) {
+ static const uchar dense1_pat[] = { 0xff, 0xbb, 0xff, 0xff, 0xff, 0xbb, 0xff, 0xff };
+ static const uchar dense2_pat[] = { 0x77, 0xff, 0xdd, 0xff, 0x77, 0xff, 0xdd, 0xff };
+ static const uchar dense3_pat[] = { 0x55, 0xbb, 0x55, 0xee, 0x55, 0xbb, 0x55, 0xee };
+ static const uchar dense4_pat[] = { 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55 };
+ static const uchar dense5_pat[] = { 0xaa, 0x44, 0xaa, 0x11, 0xaa, 0x44, 0xaa, 0x11 };
+ static const uchar dense6_pat[] = { 0x88, 0x00, 0x22, 0x00, 0x88, 0x00, 0x22, 0x00 };
+ static const uchar dense7_pat[] = { 0x00, 0x44, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00 };
+ static const uchar hor_pat[] = { 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00 };
+ static const uchar ver_pat[] = { 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10 };
+ static const uchar cross_pat[] = { 0x10, 0x10, 0x10, 0xff, 0x10, 0x10, 0x10, 0x10 };
+ static const uchar bdiag_pat[] = { 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01 };
+ static const uchar fdiag_pat[] = { 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80 };
+ static const uchar dcross_pat[] = { 0x81, 0x42, 0x24, 0x18, 0x18, 0x24, 0x42, 0x81 };
+ static const uchar *const pat_tbl[] = {
+ dense1_pat, dense2_pat, dense3_pat, dense4_pat, dense5_pat,
+ dense6_pat, dense7_pat,
+ hor_pat, ver_pat, cross_pat, bdiag_pat, fdiag_pat, dcross_pat };
+ return pat_tbl[brushStyle - Qt::Dense1Pattern];
+ }
+ static const uchar dense1_pat[] = { 0x00, 0x44, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00 };
+ static const uchar dense2_pat[] = { 0x88, 0x00, 0x22, 0x00, 0x88, 0x00, 0x22, 0x00 };
+ static const uchar dense3_pat[] = { 0xaa, 0x44, 0xaa, 0x11, 0xaa, 0x44, 0xaa, 0x11 };
+ static const uchar dense4_pat[] = { 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa };
+ static const uchar dense5_pat[] = { 0x55, 0xbb, 0x55, 0xee, 0x55, 0xbb, 0x55, 0xee };
+ static const uchar dense6_pat[] = { 0x77, 0xff, 0xdd, 0xff, 0x77, 0xff, 0xdd, 0xff };
+ static const uchar dense7_pat[] = { 0xff, 0xbb, 0xff, 0xff, 0xff, 0xbb, 0xff, 0xff };
+ static const uchar hor_pat[] = { 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff };
+ static const uchar ver_pat[] = { 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef };
+ static const uchar cross_pat[] = { 0xef, 0xef, 0xef, 0x00, 0xef, 0xef, 0xef, 0xef };
+ static const uchar bdiag_pat[] = { 0x7f, 0xbf, 0xdf, 0xef, 0xf7, 0xfb, 0xfd, 0xfe };
+ static const uchar fdiag_pat[] = { 0xfe, 0xfd, 0xfb, 0xf7, 0xef, 0xdf, 0xbf, 0x7f };
+ static const uchar dcross_pat[] = { 0x7e, 0xbd, 0xdb, 0xe7, 0xe7, 0xdb, 0xbd, 0x7e };
+ static const uchar *const pat_tbl[] = {
+ dense1_pat, dense2_pat, dense3_pat, dense4_pat, dense5_pat,
+ dense6_pat, dense7_pat,
+ hor_pat, ver_pat, cross_pat, bdiag_pat, fdiag_pat, dcross_pat };
+ return pat_tbl[brushStyle - Qt::Dense1Pattern];
+}
+
+QPixmap qt_pixmapForBrush(int brushStyle, bool invert)
+{
+ QPixmap pm;
+ QString key = QLatin1String("$qt-brush$") + QString::number(brushStyle)
+ + QString::number((int)invert);
+ if (!QPixmapCache::find(key, pm)) {
+ pm = QBitmap::fromData(QSize(8, 8), qt_patternForBrush(brushStyle, invert),
+ QImage::Format_MonoLSB);
+ QPixmapCache::insert(key, pm);
+ }
+
+ return pm;
+}
+
+class QBrushPatternImageCache
+{
+public:
+ QBrushPatternImageCache()
+ : m_initialized(false)
+ {
+ init();
+ }
+
+ void init()
+ {
+ for (int style = Qt::Dense1Pattern; style <= Qt::DiagCrossPattern; ++style) {
+ int i = style - Qt::Dense1Pattern;
+ m_images[i][0] = QImage(qt_patternForBrush(style, 0), 8, 8, 1, QImage::Format_MonoLSB);
+ m_images[i][1] = QImage(qt_patternForBrush(style, 1), 8, 8, 1, QImage::Format_MonoLSB);
+ }
+ m_initialized = true;
+ }
+
+ QImage getImage(int brushStyle, bool invert) const
+ {
+ Q_ASSERT(brushStyle >= Qt::Dense1Pattern && brushStyle <= Qt::DiagCrossPattern);
+ if (!m_initialized)
+ const_cast<QBrushPatternImageCache*>(this)->init();
+ return m_images[brushStyle - Qt::Dense1Pattern][invert];
+ }
+
+ void cleanup() {
+ for (int style = Qt::Dense1Pattern; style <= Qt::DiagCrossPattern; ++style) {
+ int i = style - Qt::Dense1Pattern;
+ m_images[i][0] = QImage();
+ m_images[i][1] = QImage();
+ }
+ m_initialized = false;
+ }
+
+private:
+ QImage m_images[Qt::DiagCrossPattern - Qt::Dense1Pattern + 1][2];
+ bool m_initialized;
+};
+
+static void qt_cleanup_brush_pattern_image_cache();
+Q_GLOBAL_STATIC_WITH_INITIALIZER(QBrushPatternImageCache, qt_brushPatternImageCache,
+ {
+ qAddPostRoutine(qt_cleanup_brush_pattern_image_cache);
+ })
+
+static void qt_cleanup_brush_pattern_image_cache()
+{
+ qt_brushPatternImageCache()->cleanup();
+}
+
+Q_GUI_EXPORT
+QImage qt_imageForBrush(int brushStyle, bool invert)
+{
+ return qt_brushPatternImageCache()->getImage(brushStyle, invert);
+}
+
+struct QTexturedBrushData : public QBrushData
+{
+ QTexturedBrushData() {
+ m_has_pixmap_texture = false;
+ m_pixmap = 0;
+ }
+ ~QTexturedBrushData() {
+ delete m_pixmap;
+ }
+
+ void setPixmap(const QPixmap &pm) {
+ delete m_pixmap;
+
+ if (pm.isNull()) {
+ m_pixmap = 0;
+ m_has_pixmap_texture = false;
+ } else {
+ m_pixmap = new QPixmap(pm);
+ m_has_pixmap_texture = true;
+ }
+
+ m_image = QImage();
+ }
+
+ void setImage(const QImage &image) {
+ m_image = image;
+ delete m_pixmap;
+ m_pixmap = 0;
+ m_has_pixmap_texture = false;
+ }
+
+ QPixmap &pixmap() {
+ if (!m_pixmap) {
+ m_pixmap = new QPixmap(QPixmap::fromImage(m_image));
+ }
+ return *m_pixmap;
+ }
+
+ QImage &image() {
+ if (m_image.isNull() && m_pixmap)
+ m_image = m_pixmap->toImage();
+ return m_image;
+ }
+
+ QPixmap *m_pixmap;
+ QImage m_image;
+ bool m_has_pixmap_texture;
+};
+
+// returns true if the brush has a pixmap (or bitmap) set as the
+// brush texture, false otherwise
+bool qHasPixmapTexture(const QBrush& brush)
+{
+ if (brush.style() != Qt::TexturePattern)
+ return false;
+ QTexturedBrushData *tx_data = static_cast<QTexturedBrushData *>(brush.d);
+ return tx_data->m_has_pixmap_texture;
+}
+
+struct QGradientBrushData : public QBrushData
+{
+ QGradient gradient;
+};
+
+
+/*!
+ \class QBrush
+ \ingroup multimedia
+ \ingroup shared
+
+ \brief The QBrush class defines the fill pattern of shapes drawn
+ by QPainter.
+
+ A brush has a style, a color, a gradient and a texture.
+
+ The brush style() defines the fill pattern using the
+ Qt::BrushStyle enum. The default brush style is Qt::NoBrush
+ (depending on how you construct a brush). This style tells the
+ painter to not fill shapes. The standard style for filling is
+ Qt::SolidPattern. The style can be set when the brush is created
+ using the appropriate constructor, and in addition the setStyle()
+ function provides means for altering the style once the brush is
+ constructed.
+
+ \image brush-styles.png Brush Styles
+
+ The brush color() defines the color of the fill pattern. The color
+ can either be one of Qt's predefined colors, Qt::GlobalColor, or
+ any other custom QColor. The currently set color can be retrieved
+ and altered using the color() and setColor() functions,
+ respectively.
+
+ The gradient() defines the gradient fill used when the current
+ style is either Qt::LinearGradientPattern,
+ Qt::RadialGradientPattern or Qt::ConicalGradientPattern. Gradient
+ brushes are created by giving a QGradient as a constructor
+ argument when creating the QBrush. Qt provides three different
+ gradients: QLinearGradient, QConicalGradient, and QRadialGradient
+ - all of which inherit QGradient.
+
+ \snippet doc/src/snippets/brush/gradientcreationsnippet.cpp 0
+
+ The texture() defines the pixmap used when the current style is
+ Qt::TexturePattern. You can create a brush with a texture by
+ providing the pixmap when the brush is created or by using
+ setTexture().
+
+ Note that applying setTexture() makes style() ==
+ Qt::TexturePattern, regardless of previous style
+ settings. Also, calling setColor() will not make a difference if
+ the style is a gradient. The same is the case if the style is
+ Qt::TexturePattern style unless the current texture is a QBitmap.
+
+ The isOpaque() function returns true if the brush is fully opaque
+ otherwise false. A brush is considered opaque if:
+
+ \list
+ \o The alpha component of the color() is 255.
+ \o Its texture() does not have an alpha channel and is not a QBitmap.
+ \o The colors in the gradient() all have an alpha component that is 255.
+ \endlist
+
+ \table 100%
+ \row
+ \o \inlineimage brush-outline.png Outlines
+ \o
+
+ To specify the style and color of lines and outlines, use the
+ QPainter's \l {QPen}{pen} combined with Qt::PenStyle and
+ Qt::GlobalColor:
+
+ \snippet doc/src/snippets/code/src_gui_painting_qbrush.cpp 0
+
+ Note that, by default, QPainter renders the outline (using the
+ currently set pen) when drawing shapes. Use \l {Qt::NoPen}{\c
+ painter.setPen(Qt::NoPen)} to disable this behavior.
+
+ \endtable
+
+ For more information about painting in general, see \l{The Paint
+ System} documentation.
+
+ \sa Qt::BrushStyle, QPainter, QColor
+*/
+
+#ifndef QT_NO_THREAD
+// Special deleter that only deletes if the ref-count goes to zero
+template <>
+class QGlobalStaticDeleter<QBrushData>
+{
+public:
+ QGlobalStatic<QBrushData> &globalStatic;
+ QGlobalStaticDeleter(QGlobalStatic<QBrushData> &_globalStatic)
+ : globalStatic(_globalStatic)
+ { }
+
+ inline ~QGlobalStaticDeleter()
+ {
+ if (!globalStatic.pointer->ref.deref())
+ delete globalStatic.pointer;
+ globalStatic.pointer = 0;
+ globalStatic.destroyed = true;
+ }
+};
+#endif
+
+Q_GLOBAL_STATIC_WITH_INITIALIZER(QBrushData, nullBrushInstance,
+ {
+ x->ref = 1;
+ x->style = Qt::BrushStyle(0);
+ x->color = Qt::black;
+ })
+
+static bool qbrush_check_type(Qt::BrushStyle style) {
+ switch (style) {
+ case Qt::TexturePattern:
+ qWarning("QBrush: Incorrect use of TexturePattern");
+ break;
+ case Qt::LinearGradientPattern:
+ case Qt::RadialGradientPattern:
+ case Qt::ConicalGradientPattern:
+ qWarning("QBrush: Wrong use of a gradient pattern");
+ break;
+ default:
+ return true;
+ }
+ return false;
+}
+
+/*!
+ \internal
+ Initializes the brush.
+*/
+
+void QBrush::init(const QColor &color, Qt::BrushStyle style)
+{
+ switch(style) {
+ case Qt::NoBrush:
+ d = nullBrushInstance();
+ d->ref.ref();
+ if (d->color != color) setColor(color);
+ return;
+ case Qt::TexturePattern:
+ d = new QTexturedBrushData;
+ break;
+ case Qt::LinearGradientPattern:
+ case Qt::RadialGradientPattern:
+ case Qt::ConicalGradientPattern:
+ d = new QGradientBrushData;
+ break;
+ default:
+ d = new QBrushData;
+ break;
+ }
+ d->ref = 1;
+ d->style = style;
+ d->color = color;
+}
+
+/*!
+ Constructs a default black brush with the style Qt::NoBrush
+ (i.e. this brush will not fill shapes).
+*/
+
+QBrush::QBrush()
+{
+ d = nullBrushInstance();
+ Q_ASSERT(d);
+ d->ref.ref();
+}
+
+/*!
+ Constructs a brush with a black color and a texture set to the
+ given \a pixmap. The style is set to Qt::TexturePattern.
+
+ \sa setTexture()
+*/
+
+QBrush::QBrush(const QPixmap &pixmap)
+{
+ init(Qt::black, Qt::TexturePattern);
+ setTexture(pixmap);
+}
+
+
+/*!
+ Constructs a brush with a black color and a texture set to the
+ given \a image. The style is set to Qt::TexturePattern.
+
+ \sa setTextureImage()
+*/
+
+QBrush::QBrush(const QImage &image)
+{
+ init(Qt::black, Qt::TexturePattern);
+ setTextureImage(image);
+}
+
+/*!
+ Constructs a black brush with the given \a style.
+
+ \sa setStyle()
+*/
+
+QBrush::QBrush(Qt::BrushStyle style)
+{
+ if (qbrush_check_type(style))
+ init(Qt::black, style);
+ else {
+ d = nullBrushInstance();
+ d->ref.ref();
+ }
+}
+
+/*!
+ Constructs a brush with the given \a color and \a style.
+
+ \sa setColor(), setStyle()
+*/
+
+QBrush::QBrush(const QColor &color, Qt::BrushStyle style)
+{
+ if (qbrush_check_type(style))
+ init(color, style);
+ else {
+ d = nullBrushInstance();
+ d->ref.ref();
+ }
+}
+
+/*!
+ \fn QBrush::QBrush(Qt::GlobalColor color, Qt::BrushStyle style)
+
+ Constructs a brush with the given \a color and \a style.
+
+ \sa setColor(), setStyle()
+*/
+QBrush::QBrush(Qt::GlobalColor color, Qt::BrushStyle style)
+{
+ if (qbrush_check_type(style))
+ init(color, style);
+ else {
+ d = nullBrushInstance();
+ d->ref.ref();
+ }
+}
+
+/*!
+ Constructs a brush with the given \a color and the custom pattern
+ stored in \a pixmap.
+
+ The style is set to Qt::TexturePattern. The color will only have
+ an effect for QBitmaps.
+
+ \sa setColor(), setPixmap()
+*/
+
+QBrush::QBrush(const QColor &color, const QPixmap &pixmap)
+{
+ init(color, Qt::TexturePattern);
+ setTexture(pixmap);
+}
+
+/*!
+
+ Constructs a brush with the given \a color and the custom pattern
+ stored in \a pixmap.
+
+ The style is set to Qt::TexturePattern. The color will only have
+ an effect for QBitmaps.
+
+ \sa setColor(), setPixmap()
+*/
+QBrush::QBrush(Qt::GlobalColor color, const QPixmap &pixmap)
+{
+ init(color, Qt::TexturePattern);
+ setTexture(pixmap);
+}
+
+/*!
+ Constructs a copy of \a other.
+*/
+
+QBrush::QBrush(const QBrush &other)
+{
+ d = other.d;
+ d->ref.ref();
+}
+
+/*!
+ Constructs a brush based on the given \a gradient.
+
+ The brush style is set to the corresponding gradient style (either
+ Qt::LinearGradientPattern, Qt::RadialGradientPattern or
+ Qt::ConicalGradientPattern).
+*/
+QBrush::QBrush(const QGradient &gradient)
+{
+ Q_ASSERT_X(gradient.type() != QGradient::NoGradient, "QBrush::QBrush",
+ "QGradient should not be used directly, use the linear, radial\n"
+ "or conical gradients instead");
+
+ const Qt::BrushStyle enum_table[] = {
+ Qt::LinearGradientPattern,
+ Qt::RadialGradientPattern,
+ Qt::ConicalGradientPattern
+ };
+
+ init(QColor(), enum_table[gradient.type()]);
+ QGradientBrushData *grad = static_cast<QGradientBrushData *>(d);
+ grad->gradient = gradient;
+}
+
+/*!
+ Destroys the brush.
+*/
+
+QBrush::~QBrush()
+{
+ if (!d->ref.deref())
+ cleanUp(d);
+}
+
+void QBrush::cleanUp(QBrushData *x)
+{
+ switch (x->style) {
+ case Qt::TexturePattern:
+ delete static_cast<QTexturedBrushData*>(x);
+ break;
+ case Qt::LinearGradientPattern:
+ case Qt::RadialGradientPattern:
+ case Qt::ConicalGradientPattern:
+ delete static_cast<QGradientBrushData*>(x);
+ break;
+ default:
+ delete x;
+ }
+}
+
+
+void QBrush::detach(Qt::BrushStyle newStyle)
+{
+ if (newStyle == d->style && d->ref == 1)
+ return;
+
+ QBrushData *x;
+ switch(newStyle) {
+ case Qt::TexturePattern: {
+ QTexturedBrushData *tbd = new QTexturedBrushData;
+ if (d->style == Qt::TexturePattern) {
+ QTexturedBrushData *data = static_cast<QTexturedBrushData *>(d);
+ if (data->m_has_pixmap_texture)
+ tbd->setPixmap(data->pixmap());
+ else
+ tbd->setImage(data->image());
+ }
+ x = tbd;
+ break;
+ }
+ case Qt::LinearGradientPattern:
+ case Qt::RadialGradientPattern:
+ case Qt::ConicalGradientPattern:
+ x = new QGradientBrushData;
+ static_cast<QGradientBrushData *>(x)->gradient =
+ static_cast<QGradientBrushData *>(d)->gradient;
+ break;
+ default:
+ x = new QBrushData;
+ break;
+ }
+ x->ref = 1;
+ x->style = newStyle;
+ x->color = d->color;
+ x->transform = d->transform;
+ if (!d->ref.deref())
+ cleanUp(d);
+ d = x;
+}
+
+
+/*!
+ \fn QBrush &QBrush::operator=(const QBrush &brush)
+
+ Assigns the given \a brush to \e this brush and returns a
+ reference to \e this brush.
+*/
+
+QBrush &QBrush::operator=(const QBrush &b)
+{
+ b.d->ref.ref();
+ if (!d->ref.deref())
+ cleanUp(d);
+ d = b.d;
+ return *this;
+}
+
+/*!
+ Returns the brush as a QVariant
+*/
+QBrush::operator QVariant() const
+{
+ return QVariant(QVariant::Brush, this);
+}
+
+/*!
+ \fn Qt::BrushStyle QBrush::style() const
+
+ Returns the brush style.
+
+ \sa setStyle()
+*/
+
+/*!
+ Sets the brush style to \a style.
+
+ \sa style()
+*/
+
+void QBrush::setStyle(Qt::BrushStyle style)
+{
+ if (d->style == style)
+ return;
+
+ if (qbrush_check_type(style)) {
+ detach(style);
+ d->style = style;
+ }
+}
+
+
+/*!
+ \fn const QColor &QBrush::color() const
+
+ Returns the brush color.
+
+ \sa setColor()
+*/
+
+/*!
+ \fn void QBrush::setColor(const QColor &color)
+
+ Sets the brush color to the given \a color.
+
+ Note that calling setColor() will not make a difference if the
+ style is a gradient. The same is the case if the style is
+ Qt::TexturePattern style unless the current texture is a QBitmap.
+
+ \sa color()
+*/
+
+void QBrush::setColor(const QColor &c)
+{
+ detach(d->style);
+ d->color = c;
+}
+
+/*!
+ \fn void QBrush::setColor(Qt::GlobalColor color)
+ \overload
+
+ Sets the brush color to the given \a color.
+*/
+
+
+#ifdef QT3_SUPPORT
+
+/*!
+ \fn void QBrush::setPixmap(const QPixmap &pixmap)
+
+ \compat
+
+ Sets a custom pattern for this brush.
+
+ Use setTexture() instead.
+*/
+
+/*!
+ \fn QPixmap *QBrush::pixmap() const
+
+ Returns a pointer to the custom brush pattern.
+
+ Use texture() instead.
+*/
+QPixmap *QBrush::pixmap() const
+{
+ if (d->style != Qt::TexturePattern)
+ return 0;
+ QTexturedBrushData *data = static_cast<QTexturedBrushData*>(d);
+ QPixmap &pixmap = data->pixmap();
+ return pixmap.isNull() ? 0 : &pixmap;
+}
+#endif
+
+/*!
+ \fn QPixmap QBrush::texture() const
+
+ Returns the custom brush pattern, or a null pixmap if no custom brush pattern
+ has been set.
+
+ \sa setTexture()
+*/
+QPixmap QBrush::texture() const
+{
+ return d->style == Qt::TexturePattern
+ ? ((QTexturedBrushData*) d)->pixmap()
+ : QPixmap();
+}
+
+/*!
+ Sets the brush pixmap to \a pixmap. The style is set to
+ Qt::TexturePattern.
+
+ The current brush color will only have an effect for monochrome
+ pixmaps, i.e. for QPixmap::depth() == 1 (\l {QBitmap}{QBitmaps}).
+
+ \sa texture()
+*/
+
+void QBrush::setTexture(const QPixmap &pixmap)
+{
+ if (!pixmap.isNull()) {
+ detach(Qt::TexturePattern);
+ QTexturedBrushData *data = static_cast<QTexturedBrushData *>(d);
+ data->setPixmap(pixmap);
+ } else {
+ detach(Qt::NoBrush);
+ }
+}
+
+
+/*!
+ \since 4.2
+
+ Returns the custom brush pattern, or a null image if no custom
+ brush pattern has been set.
+
+ If the texture was set as a QPixmap it will be converted to a
+ QImage.
+
+ \sa setTextureImage()
+*/
+
+QImage QBrush::textureImage() const
+{
+ return d->style == Qt::TexturePattern
+ ? ((QTexturedBrushData *) d)->image()
+ : QImage();
+}
+
+
+/*!
+ \since 4.2
+
+ Sets the brush image to \a image. The style is set to
+ Qt::TexturePattern.
+
+ Note the current brush color will \e not have any affect on
+ monochrome images, as opposed to calling setTexture() with a
+ QBitmap. If you want to change the color of monochrome image
+ brushes, either convert the image to QBitmap with \c
+ QBitmap::fromImage() and set the resulting QBitmap as a texture,
+ or change the entries in the color table for the image.
+
+ \sa textureImage(), setTexture()
+*/
+
+void QBrush::setTextureImage(const QImage &image)
+{
+ if (!image.isNull()) {
+ detach(Qt::TexturePattern);
+ QTexturedBrushData *data = static_cast<QTexturedBrushData *>(d);
+ data->setImage(image);
+ } else {
+ detach(Qt::NoBrush);
+ }
+}
+
+
+/*!
+ Returns the gradient describing this brush.
+*/
+const QGradient *QBrush::gradient() const
+{
+ if (d->style == Qt::LinearGradientPattern
+ || d->style == Qt::RadialGradientPattern
+ || d->style == Qt::ConicalGradientPattern) {
+ return &static_cast<const QGradientBrushData *>(d)->gradient;
+ }
+ return 0;
+}
+
+
+/*!
+ Returns true if the brush is fully opaque otherwise false. A brush
+ is considered opaque if:
+
+ \list
+ \i The alpha component of the color() is 255.
+ \i Its texture() does not have an alpha channel and is not a QBitmap.
+ \i The colors in the gradient() all have an alpha component that is 255.
+ \endlist
+*/
+
+bool QBrush::isOpaque() const
+{
+ bool opaqueColor = d->color.alpha() == 255;
+
+ // Test awfully simple case first
+ if (d->style == Qt::SolidPattern)
+ return opaqueColor;
+
+ if (d->style == Qt::LinearGradientPattern
+ || d->style == Qt::RadialGradientPattern
+ || d->style == Qt::ConicalGradientPattern) {
+ QGradientStops stops = gradient()->stops();
+ for (int i=0; i<stops.size(); ++i)
+ if (stops.at(i).second.alpha() != 255)
+ return false;
+ return true;
+ } else if (d->style == Qt::TexturePattern) {
+ return !texture().hasAlpha();
+ }
+
+ return false;
+}
+
+
+/*!
+ \since 4.2
+
+ Sets \a matrix as an explicit transformation matrix on the
+ current brush. The brush transformation matrix is merged with
+ QPainter transformation matrix to produce the final result.
+
+ \sa matrix()
+*/
+void QBrush::setMatrix(const QMatrix &matrix)
+{
+ setTransform(QTransform(matrix));
+}
+
+/*!
+ \since 4.3
+
+ Sets \a matrix as an explicit transformation matrix on the
+ current brush. The brush transformation matrix is merged with
+ QPainter transformation matrix to produce the final result.
+
+ \sa transform()
+*/
+void QBrush::setTransform(const QTransform &matrix)
+{
+ detach(d->style);
+ d->transform = matrix;
+}
+
+
+/*!
+ \fn void QBrush::matrix() const
+ \since 4.2
+
+ Returns the current transformation matrix for the brush.
+
+ \sa setMatrix()
+*/
+
+/*!
+ \fn bool QBrush::operator!=(const QBrush &brush) const
+
+ Returns true if the brush is different from the given \a brush;
+ otherwise returns false.
+
+ Two brushes are different if they have different styles, colors or
+ pixmaps.
+
+ \sa operator==()
+*/
+
+/*!
+ \fn bool QBrush::operator==(const QBrush &brush) const
+
+ Returns true if the brush is equal to the given \a brush;
+ otherwise returns false.
+
+ Two brushes are equal if they have equal styles, colors and
+ pixmaps.
+
+ \sa operator!=()
+*/
+
+bool QBrush::operator==(const QBrush &b) const
+{
+ if (b.d == d)
+ return true;
+ if (b.d->style == d->style && b.d->color == d->color) {
+ switch (d->style) {
+ case Qt::TexturePattern: {
+ QPixmap &us = ((QTexturedBrushData *) d)->pixmap();
+ QPixmap &them = ((QTexturedBrushData *) b.d)->pixmap();
+ return ((us.isNull() && them.isNull()) || us.cacheKey() == them.cacheKey());
+ }
+ case Qt::LinearGradientPattern:
+ case Qt::RadialGradientPattern:
+ case Qt::ConicalGradientPattern:
+ {
+ QGradientBrushData *d1 = static_cast<QGradientBrushData *>(d);
+ QGradientBrushData *d2 = static_cast<QGradientBrushData *>(b.d);
+ return d1->gradient == d2->gradient;
+ }
+ default:
+ return true;
+ }
+ }
+ return false;
+}
+
+/*!
+ \fn QBrush::operator const QColor&() const
+
+ Returns the brush's color.
+
+ Use color() instead.
+*/
+
+#ifndef QT_NO_DEBUG_STREAM
+/*!
+ \internal
+*/
+QDebug operator<<(QDebug dbg, const QBrush &b)
+{
+#ifndef Q_BROKEN_DEBUG_STREAM
+ dbg.nospace() << "QBrush(" << b.color() << ',' << b.style() << ')';
+ return dbg.space();
+#else
+ qWarning("This compiler doesn't support streaming QBrush to QDebug");
+ return dbg;
+ Q_UNUSED(b);
+#endif
+}
+#endif
+
+/*****************************************************************************
+ QBrush stream functions
+ *****************************************************************************/
+#ifndef QT_NO_DATASTREAM
+/*!
+ \fn QDataStream &operator<<(QDataStream &stream, const QBrush &brush)
+ \relates QBrush
+
+ Writes the given \a brush to the given \a stream and returns a
+ reference to the \a stream.
+
+ \sa {Format of the QDataStream Operators}
+*/
+
+QDataStream &operator<<(QDataStream &s, const QBrush &b)
+{
+ quint8 style = (quint8) b.style();
+ bool gradient_style = false;
+
+ if (style == Qt::LinearGradientPattern || style == Qt::RadialGradientPattern
+ || style == Qt::ConicalGradientPattern)
+ gradient_style = true;
+
+ if (s.version() < QDataStream::Qt_4_0 && gradient_style)
+ style = Qt::NoBrush;
+
+ s << style << b.color();
+ if (b.style() == Qt::TexturePattern) {
+ s << b.texture();
+ } else if (s.version() >= QDataStream::Qt_4_0 && gradient_style) {
+ const QGradient *gradient = b.gradient();
+ int type_as_int = int(gradient->type());
+ s << type_as_int;
+ if (s.version() >= QDataStream::Qt_4_3) {
+ s << int(gradient->spread());
+ s << int(gradient->coordinateMode());
+ }
+
+ if (s.version() >= QDataStream::Qt_4_5)
+ s << int(gradient->interpolationMode());
+
+ if (sizeof(qreal) == sizeof(double)) {
+ s << gradient->stops();
+ } else {
+ // ensure that we write doubles here instead of streaming the stops
+ // directly; otherwise, platforms that redefine qreal might generate
+ // data that cannot be read on other platforms.
+ QVector<QGradientStop> stops = gradient->stops();
+ s << quint32(stops.size());
+ for (int i = 0; i < stops.size(); ++i) {
+ const QGradientStop &stop = stops.at(i);
+ s << QPair<double, QColor>(double(stop.first), stop.second);
+ }
+ }
+
+ if (gradient->type() == QGradient::LinearGradient) {
+ s << static_cast<const QLinearGradient *>(gradient)->start();
+ s << static_cast<const QLinearGradient *>(gradient)->finalStop();
+ } else if (gradient->type() == QGradient::RadialGradient) {
+ s << static_cast<const QRadialGradient *>(gradient)->center();
+ s << static_cast<const QRadialGradient *>(gradient)->focalPoint();
+ s << (double) static_cast<const QRadialGradient *>(gradient)->radius();
+ } else { // type == Conical
+ s << static_cast<const QConicalGradient *>(gradient)->center();
+ s << (double) static_cast<const QConicalGradient *>(gradient)->angle();
+ }
+ }
+ if (s.version() >= QDataStream::Qt_4_3)
+ s << b.transform();
+ return s;
+}
+
+/*!
+ \fn QDataStream &operator>>(QDataStream &stream, QBrush &brush)
+ \relates QBrush
+
+ Reads the given \a brush from the given \a stream and returns a
+ reference to the \a stream.
+
+ \sa {Format of the QDataStream Operators}
+*/
+
+QDataStream &operator>>(QDataStream &s, QBrush &b)
+{
+ quint8 style;
+ QColor color;
+ s >> style;
+ s >> color;
+ if (style == Qt::TexturePattern) {
+ QPixmap pm;
+ s >> pm;
+ b = QBrush(color, pm);
+ } else if (style == Qt::LinearGradientPattern
+ || style == Qt::RadialGradientPattern
+ || style == Qt::ConicalGradientPattern) {
+
+ int type_as_int;
+ QGradient::Type type;
+ QGradientStops stops;
+ QGradient::CoordinateMode cmode = QGradient::LogicalMode;
+ QGradient::Spread spread = QGradient::PadSpread;
+ QGradient::InterpolationMode imode = QGradient::ColorInterpolation;
+
+ s >> type_as_int;
+ type = QGradient::Type(type_as_int);
+ if (s.version() >= QDataStream::Qt_4_3) {
+ s >> type_as_int;
+ spread = QGradient::Spread(type_as_int);
+ s >> type_as_int;
+ cmode = QGradient::CoordinateMode(type_as_int);
+ }
+
+ if (s.version() >= QDataStream::Qt_4_5) {
+ s >> type_as_int;
+ imode = QGradient::InterpolationMode(type_as_int);
+ }
+
+ if (sizeof(qreal) == sizeof(double)) {
+ s >> stops;
+ } else {
+ quint32 numStops;
+ double n;
+ QColor c;
+
+ s >> numStops;
+ for (quint32 i = 0; i < numStops; ++i) {
+ s >> n >> c;
+ stops << QPair<qreal, QColor>(n, c);
+ }
+ }
+
+ if (type == QGradient::LinearGradient) {
+ QPointF p1, p2;
+ s >> p1;
+ s >> p2;
+ QLinearGradient lg(p1, p2);
+ lg.setStops(stops);
+ lg.setSpread(spread);
+ lg.setCoordinateMode(cmode);
+ lg.setInterpolationMode(imode);
+ b = QBrush(lg);
+ } else if (type == QGradient::RadialGradient) {
+ QPointF center, focal;
+ double radius;
+ s >> center;
+ s >> focal;
+ s >> radius;
+ QRadialGradient rg(center, radius, focal);
+ rg.setStops(stops);
+ rg.setSpread(spread);
+ rg.setCoordinateMode(cmode);
+ rg.setInterpolationMode(imode);
+ b = QBrush(rg);
+ } else { // type == QGradient::ConicalGradient
+ QPointF center;
+ double angle;
+ s >> center;
+ s >> angle;
+ QConicalGradient cg(center, angle);
+ cg.setStops(stops);
+ cg.setSpread(spread);
+ cg.setCoordinateMode(cmode);
+ cg.setInterpolationMode(imode);
+ b = QBrush(cg);
+ }
+ } else {
+ b = QBrush(color, (Qt::BrushStyle)style);
+ }
+ if (s.version() >= QDataStream::Qt_4_3) {
+ QTransform transform;
+ s >> transform;
+ b.setTransform(transform);
+ }
+ return s;
+}
+#endif // QT_NO_DATASTREAM
+
+/*******************************************************************************
+ * QGradient implementations
+ */
+
+
+/*!
+ \class QGradient
+ \ingroup multimedia
+ \ingroup shared
+
+ \brief The QGradient class is used in combination with QBrush to
+ specify gradient fills.
+
+ Qt currently supports three types of gradient fills:
+
+ \list
+ \o \e Linear gradients interpolate colors between start and end points.
+ \o \e Radial gradients interpolate colors between a focal point and end
+ points on a circle surrounding it.
+ \o \e Conical gradients interpolate colors around a center point.
+ \endlist
+
+ A gradient's type can be retrieved using the type() function.
+ Each of the types is represented by a subclass of QGradient:
+
+ \table
+ \row
+ \o \inlineimage qgradient-linear.png
+ \o \inlineimage qgradient-radial.png
+ \o \inlineimage qgradient-conical.png
+ \header
+ \o QLinearGradient
+ \o QRadialGradient
+ \o QConicalGradient
+ \endtable
+
+ The colors in a gradient is defined using stop points of the
+ QGradientStop type, i.e. a position and a color. Use the
+ setColorAt() function to define a single stop
+ point. Alternatively, use the setStops() function to define
+ several stop points in one go. Note that the latter function \e
+ replaces the current set of stop points.
+
+ It is the gradient's complete set of stop points (accessible
+ through the stops() function) that describes how the gradient area
+ should be filled. If no stop points have been specified, a
+ gradient of black at 0 to white at 1 is used.
+
+ A diagonal linear gradient from black at (100, 100) to white at
+ (200, 200) could be specified like this:
+
+ \snippet doc/src/snippets/brush/brush.cpp 0
+
+ A gradient can have an arbitrary number of stop points. The
+ following would create a radial gradient starting with
+ red in the center, blue and then green on the edges:
+
+ \snippet doc/src/snippets/brush/brush.cpp 1
+
+ It is possible to repeat or reflect the gradient outside its area
+ by specifiying the \l {QGradient::Spread}{spread method} using the
+ setSpread() function. The default is to pad the outside area with
+ the color at the closest stop point. The currently set \l
+ {QGradient::Spread}{spread method} can be retrieved using the
+ spread() function. The QGradient::Spread enum defines three
+ different methods:
+
+ \table
+ \row
+ \o \inlineimage qradialgradient-pad.png
+ \o \inlineimage qradialgradient-repeat.png
+ \o \inlineimage qradialgradient-reflect.png
+ \row
+ \o \l {QGradient::PadSpread}{PadSpread}
+ \o \l {QGradient::RepeatSpread}{RepeatSpread}
+ \o \l {QGradient::ReflectSpread}{ReflectSpread}
+ \endtable
+
+ Note that the setSpread() function only has effect for linear and
+ radial gradients. The reason is that the conical gradient is
+ closed by definition, i.e. the \e conical gradient fills the
+ entire circle from 0 - 360 degrees, while the boundary of a radial
+ or a linear gradient can be specified through its radius or final
+ stop points, respectively.
+
+ The gradient coordinates can be specified in logical coordinates,
+ relative to device coordinates, or relative to object bounding box coordinates.
+ The \l {QGradient::CoordinateMode}{coordinate mode} can be set using the
+ setCoordinateMode() function. The default is LogicalMode, where the
+ gradient coordinates are specified in the same way as the object
+ coordinates. To retrieve the currently set \l {QGradient::CoordinateMode}
+ {coordinate mode} use coordinateMode().
+
+
+ \sa {demos/gradients}{The Gradients Demo}, QBrush
+*/
+
+/*!
+ \internal
+*/
+QGradient::QGradient()
+ : m_type(NoGradient), dummy(0)
+{
+}
+
+
+/*!
+ \enum QGradient::Type
+
+ Specifies the type of gradient.
+
+ \value LinearGradient Interpolates colors between start and end points
+ (QLinearGradient).
+
+ \value RadialGradient Interpolate colors between a focal point and end
+ points on a circle surrounding it (QRadialGradient).
+
+ \value ConicalGradient Interpolate colors around a center point (QConicalGradient).
+ \value NoGradient No gradient is used.
+
+ \sa type()
+*/
+
+/*!
+ \enum QGradient::Spread
+
+ Specifies how the area outside the gradient area should be
+ filled.
+
+ \value PadSpread The area is filled with the closest stop
+ color. This is the default.
+
+ \value RepeatSpread The gradient is repeated outside the gradient
+ area.
+
+ \value ReflectSpread The gradient is reflected outside the
+ gradient area.
+
+ \sa spread(), setSpread()
+*/
+
+/*!
+ \fn void QGradient::setSpread(Spread method)
+
+ Specifies the spread \a method that should be used for this
+ gradient.
+
+ Note that this function only has effect for linear and radial
+ gradients.
+
+ \sa spread()
+*/
+
+/*!
+ \fn QGradient::Spread QGradient::spread() const
+
+ Returns the spread method use by this gradient. The default is
+ PadSpread.
+
+ \sa setSpread()
+*/
+
+/*!
+ \fn QGradient::Type QGradient::type() const
+
+ Returns the type of gradient.
+*/
+
+/*!
+ \fn void QGradient::setColorAt(qreal position, const QColor &color)
+
+ Creates a stop point at the given \a position with the given \a
+ color. The given \a position must be in the range 0 to 1.
+
+ \sa setStops(), stops()
+*/
+
+void QGradient::setColorAt(qreal pos, const QColor &color)
+{
+ if (pos > 1 || pos < 0) {
+ qWarning("QGradient::setColorAt: Color position must be specified in the range 0 to 1");
+ return;
+ }
+
+ int index = 0;
+ while (index < m_stops.size() && m_stops.at(index).first < pos) ++index;
+
+ if (index < m_stops.size() && m_stops.at(index).first == pos)
+ m_stops[index].second = color;
+ else
+ m_stops.insert(index, QGradientStop(pos, color));
+}
+
+/*!
+ \fn void QGradient::setStops(const QGradientStops &stopPoints)
+
+ Replaces the current set of stop points with the given \a
+ stopPoints. The positions of the points must be in the range 0 to
+ 1, and must be sorted with the lowest point first.
+
+ \sa setColorAt(), stops()
+*/
+void QGradient::setStops(const QGradientStops &stops)
+{
+ m_stops.clear();
+ for (int i=0; i<stops.size(); ++i)
+ setColorAt(stops.at(i).first, stops.at(i).second);
+}
+
+
+/*!
+ Returns the stop points for this gradient.
+
+ If no stop points have been specified, a gradient of black at 0 to white
+ at 1 is used.
+
+ \sa setStops(), setColorAt()
+*/
+QGradientStops QGradient::stops() const
+{
+ if (m_stops.isEmpty()) {
+ QGradientStops tmp;
+ tmp << QGradientStop(0, Qt::black) << QGradientStop(1, Qt::white);
+ return tmp;
+ }
+ return m_stops;
+}
+
+#define Q_DUMMY_ACCESSOR union {void *p; uint i;}; p = dummy;
+
+/*!
+ \enum QGradient::CoordinateMode
+ \since 4.4
+
+ This enum specifies how gradient coordinates map to the paint
+ device on which the gradient is used.
+
+ \value LogicalMode This is the default mode. The gradient coordinates
+ are specified logical space just like the object coordinates.
+ \value StretchToDeviceMode In this mode the gradient coordinates
+ are relative to the bounding rectangle of the paint device,
+ with (0,0) in the top left corner, and (1,1) in the bottom right
+ corner of the paint device.
+ \value ObjectBoundingMode In this mode the gradient coordinates are
+ relative to the bounding rectangle of the object being drawn, with
+ (0,0) in the top left corner, and (1,1) in the bottom right corner
+ of the object's bounding rectangle.
+*/
+
+/*!
+ \since 4.4
+
+ Returns the coordinate mode of this gradient. The default mode is
+ LogicalMode.
+*/
+QGradient::CoordinateMode QGradient::coordinateMode() const
+{
+ Q_DUMMY_ACCESSOR
+ return CoordinateMode(i & 0x03);
+}
+
+/*!
+ \since 4.4
+
+ Sets the coordinate mode of this gradient to \a mode. The default
+ mode is LogicalMode.
+*/
+void QGradient::setCoordinateMode(CoordinateMode mode)
+{
+ Q_DUMMY_ACCESSOR
+ i &= ~0x03;
+ i |= uint(mode);
+ dummy = p;
+}
+
+/*!
+ \enum QGradient::InterpolationMode
+ \since 4.5
+ \internal
+
+ \value ComponentInterpolation The color components and the alpha component are
+ independently linearly interpolated.
+ \value ColorInterpolation The colors are linearly interpolated in
+ premultiplied color space.
+*/
+
+/*!
+ \since 4.5
+ \internal
+
+ Returns the interpolation mode of this gradient. The default mode is
+ ColorInterpolation.
+*/
+QGradient::InterpolationMode QGradient::interpolationMode() const
+{
+ Q_DUMMY_ACCESSOR
+ return InterpolationMode((i >> 2) & 0x01);
+}
+
+/*!
+ \since 4.5
+ \internal
+
+ Sets the interpolation mode of this gradient to \a mode. The default
+ mode is ColorInterpolation.
+*/
+void QGradient::setInterpolationMode(InterpolationMode mode)
+{
+ Q_DUMMY_ACCESSOR
+ i &= ~(1 << 2);
+ i |= (uint(mode) << 2);
+ dummy = p;
+}
+
+#undef Q_DUMMY_ACCESSOR
+
+/*!
+ \fn bool QGradient::operator!=(const QGradient &gradient) const
+ \since 4.2
+
+ Returns true if the gradient is the same as the other \a gradient
+ specified; otherwise returns false.
+
+ \sa operator==()
+*/
+
+/*!
+ Returns true if the gradient is the same as the other \a gradient
+ specified; otherwise returns false.
+
+ \sa operator!=()
+*/
+bool QGradient::operator==(const QGradient &gradient) const
+{
+ if (gradient.m_type != m_type
+ || gradient.m_spread != m_spread
+ || gradient.dummy != dummy) return false;
+
+ if (m_type == LinearGradient) {
+ if (m_data.linear.x1 != gradient.m_data.linear.x1
+ || m_data.linear.y1 != gradient.m_data.linear.y1
+ || m_data.linear.x2 != gradient.m_data.linear.x2
+ || m_data.linear.y2 != gradient.m_data.linear.y2)
+ return false;
+ } else if (m_type == RadialGradient) {
+ if (m_data.radial.cx != gradient.m_data.radial.cx
+ || m_data.radial.cy != gradient.m_data.radial.cy
+ || m_data.radial.fx != gradient.m_data.radial.fx
+ || m_data.radial.fy != gradient.m_data.radial.fy
+ || m_data.radial.radius != gradient.m_data.radial.radius)
+ return false;
+ } else { // m_type == ConicalGradient
+ if (m_data.conical.cx != gradient.m_data.conical.cx
+ || m_data.conical.cy != gradient.m_data.conical.cy
+ || m_data.conical.angle != gradient.m_data.conical.angle)
+ return false;
+ }
+
+ return stops() == gradient.stops();
+}
+
+/*!
+ \internal
+*/
+bool QGradient::operator==(const QGradient &gradient)
+{
+ return const_cast<const QGradient *>(this)->operator==(gradient);
+}
+
+/*!
+ \class QLinearGradient
+ \ingroup multimedia
+
+ \brief The QLinearGradient class is used in combination with QBrush to
+ specify a linear gradient brush.
+
+ Linear gradients interpolate colors between start and end
+ points. Outside these points the gradient is either padded,
+ reflected or repeated depending on the currently set \l
+ {QGradient::Spread}{spread} method:
+
+ \table
+ \row
+ \o \inlineimage qlineargradient-pad.png
+ \o \inlineimage qlineargradient-reflect.png
+ \o \inlineimage qlineargradient-repeat.png
+ \row
+ \o \l {QGradient::PadSpread}{PadSpread} (default)
+ \o \l {QGradient::ReflectSpread}{ReflectSpread}
+ \o \l {QGradient::RepeatSpread}{RepeatSpread}
+ \endtable
+
+ The colors in a gradient is defined using stop points of the
+ QGradientStop type, i.e. a position and a color. Use the
+ QGradient::setColorAt() or the QGradient::setStops() function to
+ define the stop points. It is the gradient's complete set of stop
+ points that describes how the gradient area should be filled. If
+ no stop points have been specified, a gradient of black at 0 to
+ white at 1 is used.
+
+ In addition to the functions inherited from QGradient, the
+ QLinearGradient class provides the finalStop() function which
+ returns the final stop point of the gradient, and the start()
+ function returning the start point of the gradient.
+
+ \sa QRadialGradient, QConicalGradient, {demos/gradients}{The
+ Gradients Demo}
+*/
+
+
+/*!
+ Constructs a default linear gradient with interpolation area
+ between (0, 0) and (1, 1).
+
+ \sa QGradient::setColorAt(), setStart(), setFinalStop()
+*/
+
+QLinearGradient::QLinearGradient()
+{
+ m_type = LinearGradient;
+ m_spread = PadSpread;
+ m_data.linear.x1 = 0;
+ m_data.linear.y1 = 0;
+ m_data.linear.x2 = 1;
+ m_data.linear.y2 = 1;
+}
+
+
+/*!
+ Constructs a linear gradient with interpolation area between the
+ given \a start point and \a finalStop.
+
+ \note The expected parameter values are in pixels.
+
+ \sa QGradient::setColorAt(), QGradient::setStops()
+*/
+QLinearGradient::QLinearGradient(const QPointF &start, const QPointF &finalStop)
+{
+ m_type = LinearGradient;
+ m_spread = PadSpread;
+ m_data.linear.x1 = start.x();
+ m_data.linear.y1 = start.y();
+ m_data.linear.x2 = finalStop.x();
+ m_data.linear.y2 = finalStop.y();
+}
+
+/*!
+ \fn QLinearGradient::QLinearGradient(qreal x1, qreal y1, qreal x2, qreal y2)
+
+ Constructs a linear gradient with interpolation area between (\a
+ x1, \a y1) and (\a x2, \a y2).
+
+ \note The expected parameter values are in pixels.
+
+ \sa QGradient::setColorAt(), QGradient::setStops()
+*/
+QLinearGradient::QLinearGradient(qreal xStart, qreal yStart, qreal xFinalStop, qreal yFinalStop)
+{
+ m_type = LinearGradient;
+ m_spread = PadSpread;
+ m_data.linear.x1 = xStart;
+ m_data.linear.y1 = yStart;
+ m_data.linear.x2 = xFinalStop;
+ m_data.linear.y2 = yFinalStop;
+}
+
+
+/*!
+ Returns the start point of this linear gradient in logical coordinates.
+
+ \sa QGradient::stops()
+*/
+
+QPointF QLinearGradient::start() const
+{
+ Q_ASSERT(m_type == LinearGradient);
+ return QPointF(m_data.linear.x1, m_data.linear.y1);
+}
+
+/*!
+ \fn void QLinearGradient::setStart(qreal x, qreal y)
+ \overload
+ \since 4.2
+
+ Sets the start point of this linear gradient in logical
+ coordinates to \a x, \a y.
+
+ \sa start()
+*/
+
+/*!
+ \since 4.2
+
+ Sets the start point of this linear gradient in logical
+ coordinates to \a start.
+
+ \sa start()
+*/
+
+void QLinearGradient::setStart(const QPointF &start)
+{
+ Q_ASSERT(m_type == LinearGradient);
+ m_data.linear.x1 = start.x();
+ m_data.linear.y1 = start.y();
+}
+
+
+/*!
+ \fn void QLinearGradient::setFinalStop(qreal x, qreal y)
+ \overload
+ \since 4.2
+
+ Sets the final stop point of this linear gradient in logical
+ coordinates to \a x, \a y.
+
+ \sa start()
+*/
+
+/*!
+ Returns the final stop point of this linear gradient in logical coordinates.
+
+ \sa QGradient::stops()
+*/
+
+QPointF QLinearGradient::finalStop() const
+{
+ Q_ASSERT(m_type == LinearGradient);
+ return QPointF(m_data.linear.x2, m_data.linear.y2);
+}
+
+
+/*!
+ \since 4.2
+
+ Sets the final stop point of this linear gradient in logical
+ coordinates to \a stop.
+
+ \sa finalStop()
+*/
+
+void QLinearGradient::setFinalStop(const QPointF &stop)
+{
+ Q_ASSERT(m_type == LinearGradient);
+ m_data.linear.x2 = stop.x();
+ m_data.linear.y2 = stop.y();
+}
+
+
+/*!
+ \class QRadialGradient
+ \ingroup multimedia
+
+ \brief The QRadialGradient class is used in combination with QBrush to
+ specify a radial gradient brush.
+
+ Radial gradients interpolate colors between a focal point and end
+ points on a circle surrounding it. Outside the end points the
+ gradient is either padded, reflected or repeated depending on the
+ currently set \l {QGradient::Spread}{spread} method:
+
+ \table
+ \row
+ \o \inlineimage qradialgradient-pad.png
+ \o \inlineimage qradialgradient-reflect.png
+ \o \inlineimage qradialgradient-repeat.png
+ \row
+ \o \l {QGradient::PadSpread}{PadSpread} (default)
+ \o \l {QGradient::ReflectSpread}{ReflectSpread}
+ \o \l {QGradient::RepeatSpread}{RepeatSpread}
+ \endtable
+
+ The colors in a gradient is defined using stop points of the
+ QGradientStop type, i.e. a position and a color. Use the
+ QGradient::setColorAt() or the QGradient::setStops() function to
+ define the stop points. It is the gradient's complete set of stop
+ points that describes how the gradient area should be filled. If
+ no stop points have been specified, a gradient of black at 0 to
+ white at 1 is used.
+
+ In addition to the functions inherited from QGradient, the
+ QRadialGradient class provides the center(), focalPoint() and
+ radius() functions returning the gradient's center, focal point
+ and radius respectively.
+
+ \sa QLinearGradient, QConicalGradient, {demos/gradients}{The
+ Gradients Demo}
+*/
+
+static QPointF qt_radial_gradient_adapt_focal_point(const QPointF &center,
+ qreal radius,
+ const QPointF &focalPoint)
+{
+ // We have a one pixel buffer zone to avoid numerical instability on the
+ // circle border
+ //### this is hacky because technically we should adjust based on current matrix
+ const qreal compensated_radius = radius - radius * 0.001;
+ QLineF line(center, focalPoint);
+ if (line.length() > (compensated_radius))
+ line.setLength(compensated_radius);
+ return line.p2();
+}
+
+/*!
+ Constructs a radial gradient with the given \a center, \a
+ radius and \a focalPoint.
+
+ \sa QGradient::setColorAt(), QGradient::setStops()
+*/
+
+QRadialGradient::QRadialGradient(const QPointF &center, qreal radius, const QPointF &focalPoint)
+{
+ m_type = RadialGradient;
+ m_spread = PadSpread;
+ m_data.radial.cx = center.x();
+ m_data.radial.cy = center.y();
+ m_data.radial.radius = radius;
+
+ QPointF adapted_focal = qt_radial_gradient_adapt_focal_point(center, radius, focalPoint);
+ m_data.radial.fx = adapted_focal.x();
+ m_data.radial.fy = adapted_focal.y();
+}
+
+/*!
+ Constructs a radial gradient with the given \a center, \a
+ radius and the focal point in the circle center.
+
+ \sa QGradient::setColorAt(), QGradient::setStops()
+*/
+QRadialGradient::QRadialGradient(const QPointF &center, qreal radius)
+{
+ m_type = RadialGradient;
+ m_spread = PadSpread;
+ m_data.radial.cx = center.x();
+ m_data.radial.cy = center.y();
+ m_data.radial.radius = radius;
+ m_data.radial.fx = center.x();
+ m_data.radial.fy = center.y();
+}
+
+
+/*!
+ Constructs a radial gradient with the given center (\a cx, \a cy),
+ \a radius and focal point (\a fx, \a fy).
+
+ \sa QGradient::setColorAt(), QGradient::setStops()
+*/
+
+QRadialGradient::QRadialGradient(qreal cx, qreal cy, qreal radius, qreal fx, qreal fy)
+{
+ m_type = RadialGradient;
+ m_spread = PadSpread;
+ m_data.radial.cx = cx;
+ m_data.radial.cy = cy;
+ m_data.radial.radius = radius;
+
+ QPointF adapted_focal = qt_radial_gradient_adapt_focal_point(QPointF(cx, cy),
+ radius,
+ QPointF(fx, fy));
+
+ m_data.radial.fx = adapted_focal.x();
+ m_data.radial.fy = adapted_focal.y();
+}
+
+/*!
+ Constructs a radial gradient with the center at (\a cx, \a cy) and the
+ specified \a radius. The focal point lies at the center of the circle.
+
+ \sa QGradient::setColorAt(), QGradient::setStops()
+ */
+QRadialGradient::QRadialGradient(qreal cx, qreal cy, qreal radius)
+{
+ m_type = RadialGradient;
+ m_spread = PadSpread;
+ m_data.radial.cx = cx;
+ m_data.radial.cy = cy;
+ m_data.radial.radius = radius;
+ m_data.radial.fx = cx;
+ m_data.radial.fy = cy;
+}
+
+
+/*!
+ Constructs a radial gradient with the center and focal point at
+ (0, 0) with a radius of 1.
+*/
+QRadialGradient::QRadialGradient()
+{
+ m_type = RadialGradient;
+ m_spread = PadSpread;
+ m_data.radial.cx = 0;
+ m_data.radial.cy = 0;
+ m_data.radial.radius = 1;
+ m_data.radial.fx = 0;
+ m_data.radial.fy = 0;
+}
+
+
+/*!
+ Returns the center of this radial gradient in logical coordinates.
+
+ \sa QGradient::stops()
+*/
+
+QPointF QRadialGradient::center() const
+{
+ Q_ASSERT(m_type == RadialGradient);
+ return QPointF(m_data.radial.cx, m_data.radial.cy);
+}
+
+/*!
+ \fn void QRadialGradient::setCenter(qreal x, qreal y)
+ \overload
+ \since 4.2
+
+ Sets the center of this radial gradient in logical coordinates
+ to (\a x, \a y).
+
+ \sa center()
+*/
+
+/*!
+ \since 4.2
+
+ Sets the center of this radial gradient in logical coordinates
+ to \a center.
+
+ \sa center()
+*/
+
+void QRadialGradient::setCenter(const QPointF &center)
+{
+ Q_ASSERT(m_type == RadialGradient);
+ m_data.radial.cx = center.x();
+ m_data.radial.cy = center.y();
+}
+
+
+/*!
+ Returns the radius of this radial gradient in logical coordinates.
+
+ \sa QGradient::stops()
+*/
+
+qreal QRadialGradient::radius() const
+{
+ Q_ASSERT(m_type == RadialGradient);
+ return m_data.radial.radius;
+}
+
+
+/*!
+ \since 4.2
+
+ Sets the radius of this radial gradient in logical coordinates
+ to \a radius
+*/
+void QRadialGradient::setRadius(qreal radius)
+{
+ Q_ASSERT(m_type == RadialGradient);
+ m_data.radial.radius = radius;
+}
+
+
+/*!
+ Returns the focal point of this radial gradient in logical
+ coordinates.
+
+ \sa QGradient::stops()
+*/
+
+QPointF QRadialGradient::focalPoint() const
+{
+ Q_ASSERT(m_type == RadialGradient);
+ return QPointF(m_data.radial.fx, m_data.radial.fy);
+}
+
+/*!
+ \fn void QRadialGradient::setFocalPoint(qreal x, qreal y)
+ \overload
+ \since 4.2
+
+ Sets the focal point of this radial gradient in logical
+ coordinates to (\a x, \a y).
+
+ \sa focalPoint()
+*/
+
+/*!
+ \since 4.2
+
+ Sets the focal point of this radial gradient in logical
+ coordinates to \a focalPoint.
+
+ \sa focalPoint()
+*/
+
+void QRadialGradient::setFocalPoint(const QPointF &focalPoint)
+{
+ Q_ASSERT(m_type == RadialGradient);
+ m_data.radial.fx = focalPoint.x();
+ m_data.radial.fy = focalPoint.y();
+}
+
+
+
+/*!
+ \class QConicalGradient
+ \ingroup multimedia
+
+ \brief The QConicalGradient class is used in combination with QBrush to
+ specify a conical gradient brush.
+
+ Conical gradients interpolate interpolate colors counter-clockwise
+ around a center point.
+
+ \image qconicalgradient.png
+
+ The colors in a gradient is defined using stop points of the
+ QGradientStop type, i.e. a position and a color. Use the
+ QGradient::setColorAt() or the QGradient::setStops() function to
+ define the stop points. It is the gradient's complete set of stop
+ points that describes how the gradient area should be filled. If
+ no stop points have been specified, a gradient of black at 0 to
+ white at 1 is used.
+
+ In addition to the functions inherited from QGradient, the
+ QConicalGradient class provides the angle() and center() functions
+ returning the start angle and center of the gradient.
+
+ Note that the setSpread() function has no effect for conical
+ gradients. The reason is that the conical gradient is closed by
+ definition, i.e. the conical gradient fills the entire circle from
+ 0 - 360 degrees, while the boundary of a radial or a linear
+ gradient can be specified through its radius or final stop points,
+ respectively.
+
+ \sa QLinearGradient, QRadialGradient, {demos/gradients}{The
+ Gradients Demo}
+*/
+
+
+/*!
+ Constructs a conical gradient with the given \a center, starting
+ the interpolation at the given \a angle. The \a angle must be
+ specified in degrees between 0 and 360.
+
+ \sa QGradient::setColorAt(), QGradient::setStops()
+*/
+
+QConicalGradient::QConicalGradient(const QPointF &center, qreal angle)
+{
+ m_type = ConicalGradient;
+ m_spread = PadSpread;
+ m_data.conical.cx = center.x();
+ m_data.conical.cy = center.y();
+ m_data.conical.angle = angle;
+}
+
+
+/*!
+ Constructs a conical gradient with the given center (\a cx, \a
+ cy), starting the interpolation at the given \a angle. The angle
+ must be specified in degrees between 0 and 360.
+
+ \sa QGradient::setColorAt(), QGradient::setStops()
+*/
+
+QConicalGradient::QConicalGradient(qreal cx, qreal cy, qreal angle)
+{
+ m_type = ConicalGradient;
+ m_spread = PadSpread;
+ m_data.conical.cx = cx;
+ m_data.conical.cy = cy;
+ m_data.conical.angle = angle;
+}
+
+
+/*!
+ Constructs a conical with center at (0, 0) starting the
+ interpolation at angle 0.
+
+ \sa QGradient::setColorAt(), setCenter(), setAngle()
+*/
+
+QConicalGradient::QConicalGradient()
+{
+ m_type = ConicalGradient;
+ m_spread = PadSpread;
+ m_data.conical.cx = 0;
+ m_data.conical.cy = 0;
+ m_data.conical.angle = 0;
+}
+
+
+/*!
+ Returns the center of the conical gradient in logical
+ coordinates.
+
+ \sa stops()
+*/
+
+QPointF QConicalGradient::center() const
+{
+ Q_ASSERT(m_type == ConicalGradient);
+ return QPointF(m_data.conical.cx, m_data.conical.cy);
+}
+
+
+/*!
+ \fn void QConicalGradient::setCenter(qreal x, qreal y)
+
+ \overload
+
+ Sets the center of this conical gradient in logical coordinates to
+ (\a x, \a y).
+
+ \sa center()
+*/
+
+/*!
+ Sets the center of this conical gradient in logical coordinates to
+ \a center.
+
+ \sa center()
+*/
+
+void QConicalGradient::setCenter(const QPointF &center)
+{
+ Q_ASSERT(m_type == ConicalGradient);
+ m_data.conical.cx = center.x();
+ m_data.conical.cy = center.y();
+}
+
+/*!
+ Returns the start angle of the conical gradient in logical
+ coordinates.
+
+ \sa stops()
+*/
+
+qreal QConicalGradient::angle() const
+{
+ Q_ASSERT(m_type == ConicalGradient);
+ return m_data.conical.angle;
+}
+
+
+/*!
+ \since 4.2
+
+ Sets \a angle to be the start angle for this conical gradient in
+ logical coordinates.
+
+ \sa angle()
+*/
+
+void QConicalGradient::setAngle(qreal angle)
+{
+ Q_ASSERT(m_type == ConicalGradient);
+ m_data.conical.angle = angle;
+}
+
+/*!
+ \typedef QGradientStop
+ \relates QGradient
+
+ Typedef for QPair<\l qreal, QColor>.
+*/
+
+/*!
+ \typedef QGradientStops
+ \relates QGradient
+
+ Typedef for QVector<QGradientStop>.
+*/
+
+/*!
+ \typedef QBrush::DataPtr
+ \internal
+*/
+
+/*!
+ \fn DataPtr &QBrush::data_ptr()
+ \internal
+*/
+
+
+/*!
+ \fn bool QBrush::isDetached() const
+ \internal
+*/
+
+/*!
+ \fn QTransform QBrush::transform() const
+ \since 4.3
+
+ Returns the current transformation matrix for the brush.
+
+ \sa setTransform()
+*/
+
+QT_END_NAMESPACE
diff --git a/src/gui/painting/qbrush.h b/src/gui/painting/qbrush.h
new file mode 100644
index 0000000000..d6d0da3eaf
--- /dev/null
+++ b/src/gui/painting/qbrush.h
@@ -0,0 +1,321 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QBRUSH_H
+#define QBRUSH_H
+
+#include <QtCore/qpair.h>
+#include <QtCore/qpoint.h>
+#include <QtCore/qvector.h>
+#include <QtGui/qcolor.h>
+#include <QtGui/qmatrix.h>
+#include <QtGui/qtransform.h>
+#include <QtGui/qimage.h>
+#include <QtGui/qpixmap.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+struct QBrushData;
+class QPixmap;
+class QGradient;
+class QVariant;
+
+class Q_GUI_EXPORT QBrush
+{
+public:
+ QBrush();
+ QBrush(Qt::BrushStyle bs);
+ QBrush(const QColor &color, Qt::BrushStyle bs=Qt::SolidPattern);
+ QBrush(Qt::GlobalColor color, Qt::BrushStyle bs=Qt::SolidPattern);
+
+ QBrush(const QColor &color, const QPixmap &pixmap);
+ QBrush(Qt::GlobalColor color, const QPixmap &pixmap);
+ QBrush(const QPixmap &pixmap);
+ QBrush(const QImage &image);
+
+ QBrush(const QBrush &brush);
+
+ QBrush(const QGradient &gradient);
+
+ ~QBrush();
+ QBrush &operator=(const QBrush &brush);
+ operator QVariant() const;
+
+ inline Qt::BrushStyle style() const;
+ void setStyle(Qt::BrushStyle);
+
+ inline const QMatrix &matrix() const;
+ void setMatrix(const QMatrix &mat);
+
+ inline QTransform transform() const;
+ void setTransform(const QTransform &);
+
+ QPixmap texture() const;
+ void setTexture(const QPixmap &pixmap);
+
+ QImage textureImage() const;
+ void setTextureImage(const QImage &image);
+
+ inline const QColor &color() const;
+ void setColor(const QColor &color);
+ inline void setColor(Qt::GlobalColor color);
+
+ const QGradient *gradient() const;
+
+ bool isOpaque() const;
+
+ bool operator==(const QBrush &b) const;
+ inline bool operator!=(const QBrush &b) const { return !(operator==(b)); }
+
+#ifdef QT3_SUPPORT
+ inline QT3_SUPPORT operator const QColor&() const;
+ QT3_SUPPORT QPixmap *pixmap() const;
+ inline QT3_SUPPORT void setPixmap(const QPixmap &pixmap) { setTexture(pixmap); }
+#endif
+
+private:
+#if defined(Q_WS_X11)
+ friend class QX11PaintEngine;
+#endif
+ friend class QRasterPaintEngine;
+ friend class QRasterPaintEnginePrivate;
+ friend struct QSpanData;
+ friend class QPainter;
+ friend bool qHasPixmapTexture(const QBrush& brush);
+ void detach(Qt::BrushStyle newStyle);
+ void init(const QColor &color, Qt::BrushStyle bs);
+ QBrushData *d;
+ void cleanUp(QBrushData *x);
+
+public:
+ inline bool isDetached() const;
+ typedef QBrushData * DataPtr;
+ inline DataPtr &data_ptr() { return d; }
+};
+
+inline void QBrush::setColor(Qt::GlobalColor acolor)
+{ setColor(QColor(acolor)); }
+
+Q_DECLARE_TYPEINFO(QBrush, Q_MOVABLE_TYPE);
+Q_DECLARE_SHARED(QBrush)
+
+/*****************************************************************************
+ QBrush stream functions
+ *****************************************************************************/
+
+#ifndef QT_NO_DATASTREAM
+Q_GUI_EXPORT QDataStream &operator<<(QDataStream &, const QBrush &);
+Q_GUI_EXPORT QDataStream &operator>>(QDataStream &, QBrush &);
+#endif
+
+#ifndef QT_NO_DEBUG_STREAM
+Q_GUI_EXPORT QDebug operator<<(QDebug, const QBrush &);
+#endif
+
+struct QBrushData
+{
+ QAtomicInt ref;
+ Qt::BrushStyle style;
+ QColor color;
+ QTransform transform;
+};
+
+inline Qt::BrushStyle QBrush::style() const { return d->style; }
+inline const QColor &QBrush::color() const { return d->color; }
+inline const QMatrix &QBrush::matrix() const { return d->transform.toAffine(); }
+inline QTransform QBrush::transform() const { return d->transform; }
+inline bool QBrush::isDetached() const { return d->ref == 1; }
+
+#ifdef QT3_SUPPORT
+inline QBrush::operator const QColor&() const { return d->color; }
+#endif
+
+
+/*******************************************************************************
+ * QGradients
+ */
+class QGradientPrivate;
+
+typedef QPair<qreal, QColor> QGradientStop;
+typedef QVector<QGradientStop> QGradientStops;
+
+class Q_GUI_EXPORT QGradient
+{
+ Q_GADGET
+ Q_ENUMS(Type Spread CoordinateMode)
+public:
+ enum Type {
+ LinearGradient,
+ RadialGradient,
+ ConicalGradient,
+ NoGradient
+ };
+
+ enum Spread {
+ PadSpread,
+ ReflectSpread,
+ RepeatSpread
+ };
+
+ enum CoordinateMode {
+ LogicalMode,
+ StretchToDeviceMode,
+ ObjectBoundingMode
+ };
+
+ enum InterpolationMode {
+ ColorInterpolation,
+ ComponentInterpolation
+ };
+
+ QGradient();
+
+ Type type() const { return m_type; }
+
+ inline void setSpread(Spread spread);
+ Spread spread() const { return m_spread; }
+
+ void setColorAt(qreal pos, const QColor &color);
+
+ void setStops(const QGradientStops &stops);
+ QGradientStops stops() const;
+
+ CoordinateMode coordinateMode() const;
+ void setCoordinateMode(CoordinateMode mode);
+
+ InterpolationMode interpolationMode() const;
+ void setInterpolationMode(InterpolationMode mode);
+
+ bool operator==(const QGradient &gradient) const;
+ inline bool operator!=(const QGradient &other) const
+ { return !operator==(other); }
+
+ bool operator==(const QGradient &gradient); // ### Qt 5: remove
+
+private:
+ friend class QLinearGradient;
+ friend class QRadialGradient;
+ friend class QConicalGradient;
+
+ Type m_type;
+ Spread m_spread;
+ QGradientStops m_stops;
+ union {
+ struct {
+ qreal x1, y1, x2, y2;
+ } linear;
+ struct {
+ qreal cx, cy, fx, fy, radius;
+ } radial;
+ struct {
+ qreal cx, cy, angle;
+ } conical;
+ } m_data;
+ void *dummy;
+};
+
+inline void QGradient::setSpread(Spread aspread)
+{ m_spread = aspread; }
+
+class Q_GUI_EXPORT QLinearGradient : public QGradient
+{
+public:
+ QLinearGradient();
+ QLinearGradient(const QPointF &start, const QPointF &finalStop);
+ QLinearGradient(qreal xStart, qreal yStart, qreal xFinalStop, qreal yFinalStop);
+
+ QPointF start() const;
+ void setStart(const QPointF &start);
+ inline void setStart(qreal x, qreal y) { setStart(QPointF(x, y)); }
+
+ QPointF finalStop() const;
+ void setFinalStop(const QPointF &stop);
+ inline void setFinalStop(qreal x, qreal y) { setFinalStop(QPointF(x, y)); }
+};
+
+
+class Q_GUI_EXPORT QRadialGradient : public QGradient
+{
+public:
+ QRadialGradient();
+ QRadialGradient(const QPointF &center, qreal radius, const QPointF &focalPoint);
+ QRadialGradient(qreal cx, qreal cy, qreal radius, qreal fx, qreal fy);
+
+ QRadialGradient(const QPointF &center, qreal radius);
+ QRadialGradient(qreal cx, qreal cy, qreal radius);
+
+ QPointF center() const;
+ void setCenter(const QPointF &center);
+ inline void setCenter(qreal x, qreal y) { setCenter(QPointF(x, y)); }
+
+ QPointF focalPoint() const;
+ void setFocalPoint(const QPointF &focalPoint);
+ inline void setFocalPoint(qreal x, qreal y) { setFocalPoint(QPointF(x, y)); }
+
+ qreal radius() const;
+ void setRadius(qreal radius);
+};
+
+
+class Q_GUI_EXPORT QConicalGradient : public QGradient
+{
+public:
+ QConicalGradient();
+ QConicalGradient(const QPointF &center, qreal startAngle);
+ QConicalGradient(qreal cx, qreal cy, qreal startAngle);
+
+ QPointF center() const;
+ void setCenter(const QPointF &center);
+ inline void setCenter(qreal x, qreal y) { setCenter(QPointF(x, y)); }
+
+ qreal angle() const;
+ void setAngle(qreal angle);
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QBRUSH_H
diff --git a/src/gui/painting/qcolor.cpp b/src/gui/painting/qcolor.cpp
new file mode 100644
index 0000000000..5d7d4ab5b0
--- /dev/null
+++ b/src/gui/painting/qcolor.cpp
@@ -0,0 +1,2244 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qcolor.h"
+#include "qcolor_p.h"
+#include "qnamespace.h"
+#include "qcolormap.h"
+#include "qdatastream.h"
+#include "qvariant.h"
+#include "qdebug.h"
+
+#ifdef Q_WS_X11
+# include "qapplication.h"
+# include "qx11info_x11.h"
+# include "private/qt_x11_p.h"
+
+static bool allowX11ColorNames = false;
+
+#endif
+
+#include <math.h>
+#include <stdio.h>
+#include <limits.h>
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QColor
+ \brief The QColor class provides colors based on RGB, HSV or CMYK values.
+
+ \ingroup multimedia
+ \ingroup appearance
+ \mainclass
+
+ A color is normally specified in terms of RGB (red, green, and
+ blue) components, but it is also possible to specify it in terms
+ of HSV (hue, saturation, and value) and CMYK (cyan, magenta,
+ yellow and black) components. In addition a color can be specified
+ using a color name. The color name can be any of the SVG 1.0 color
+ names.
+
+ \table
+ \row
+ \o \inlineimage qcolor-rgb.png
+ \o \inlineimage qcolor-hsv.png
+ \o \inlineimage qcolor-cmyk.png
+ \header
+ \o RGB \o HSV \o CMYK
+ \endtable
+
+ The QColor constructor creates the color based on RGB values. To
+ create a QColor based on either HSV or CMYK values, use the
+ toHsv() and toCmyk() functions respectively. These functions
+ return a copy of the color using the desired format. In addition
+ the static fromRgb(), fromHsv() and fromCmyk() functions create
+ colors from the specified values. Alternatively, a color can be
+ converted to any of the three formats using the convertTo()
+ function (returning a copy of the color in the desired format), or
+ any of the setRgb(), setHsv() and setCmyk() functions altering \e
+ this color's format. The spec() function tells how the color was
+ specified.
+
+ A color can be set by passing an RGB string (such as "#112233"),
+ or a color name (such as "blue"), to the setNamedColor() function.
+ The color names are taken from the SVG 1.0 color names. The name()
+ function returns the name of the color in the format
+ "#RRGGBB". Colors can also be set using setRgb(), setHsv() and
+ setCmyk(). To get a lighter or darker color use the lighter() and
+ darker() functions respectively.
+
+ The isValid() function indicates whether a QColor is legal at
+ all. For example, a RGB color with RGB values out of range is
+ illegal. For performance reasons, QColor mostly disregards illegal
+ colors, and for that reason, the result of using an invalid color
+ is undefined.
+
+ The color components can be retrieved individually, e.g with
+ red(), hue() and cyan(). The values of the color components can
+ also be retrieved in one go using the getRgb(), getHsv() and
+ getCmyk() functions. Using the RGB color model, the color
+ components can in addition be accessed with rgb().
+
+ There are several related non-members: QRgb is a typdef for an
+ unsigned int representing the RGB value triplet (r, g, b). Note
+ that it also can hold a value for the alpha-channel (for more
+ information, see the \l {QColor#Alpha-Blended
+ Drawing}{Alpha-Blended Drawing} section). The qRed(), qBlue() and
+ qGreen() functions return the respective component of the given
+ QRgb value, while the qRgb() and qRgba() functions create and
+ return the QRgb triplet based on the given component
+ values. Finally, the qAlpha() function returns the alpha component
+ of the provided QRgb, and the qGray() function calculates and
+ return a gray value based on the given value.
+
+ QColor is platform and device independent. The QColormap class
+ maps the color to the hardware.
+
+ For more information about painting in general, see \l{The Paint
+ System} documentation.
+
+ \tableofcontents
+
+ \section1 Integer vs. Floating Point Precision
+
+ QColor supports floating point precision and provides floating
+ point versions of all the color components functions,
+ e.g. getRgbF(), hueF() and fromCmykF(). Note that since the
+ components are stored using 16-bit integers, there might be minor
+ deviations between the values set using, for example, setRgbF()
+ and the values returned by the getRgbF() function due to rounding.
+
+ While the integer based functions take values in the range 0-255
+ (except hue() which must have values within the range 0-359),
+ the floating point functions accept values in the range 0.0 - 1.0.
+
+ \section1 Alpha-Blended Drawing
+
+ QColor also support alpha-blended outlining and filling. The
+ alpha channel of a color specifies the transparency effect, 0
+ represents a fully transparent color, while 255 represents a fully
+ opaque color. For example:
+
+ \snippet doc/src/snippets/code/src_gui_painting_qcolor.cpp 0
+
+ The code above produces the following output:
+
+ \img alphafill.png
+
+ Alpha-blended drawing is supported on Windows, Mac OS X, and on
+ X11 systems that have the X Render extension installed.
+
+ The alpha channel of a color can be retrieved and set using the
+ alpha() and setAlpha() functions if its value is an integer, and
+ alphaF() and setAlphaF() if its value is qreal (double). By
+ default, the alpha-channel is set to 255 (opaque). To retrieve and
+ set \e all the RGB color components (including the alpha-channel)
+ in one go, use the rgba() and setRgba() functions.
+
+ \section1 Predefined Colors
+
+ There are 20 predefined QColors: Qt::white, Qt::black,
+ Qt::red, Qt::darkRed, Qt::green, Qt::darkGreen, Qt::blue,
+ Qt::darkBlue, Qt::cyan, Qt::darkCyan, Qt::magenta,
+ Qt::darkMagenta, Qt::yellow, Qt::darkYellow, Qt::gray,
+ Qt::darkGray, Qt::lightGray, Qt::color0, Qt::color1, and
+ Qt::transparent.
+
+ \img qt-colors.png Qt Colors
+
+ QColor provides the static colorNames() function which returns a
+ QStringList containing the color names Qt knows about.
+
+ The colors Qt::color0 (zero pixel value) and Qt::color1 (non-zero
+ pixel value) are special colors for drawing in QBitmaps. Painting with
+ Qt::color0 sets the bitmap bits to 0 (transparent, i.e. background), and
+ painting with Qt::color1 sets the bits to 1 (opaque, i.e. foreground).
+
+ \section1 The HSV Color Model
+
+ The RGB model is hardware-oriented. Its representation is close to
+ what most monitors show. In contrast, HSV represents color in a way
+ more suited to the human perception of color. For example, the
+ relationships "stronger than", "darker than", and "the opposite of"
+ are easily expressed in HSV but are much harder to express in RGB.
+
+ HSV, like RGB, has three components:
+
+ \list
+ \o H, for hue, is in the range 0 to 359 if the color is chromatic (not
+ gray), or meaningless if it is gray. It represents degrees on the
+ color wheel familiar to most people. Red is 0 (degrees), green is
+ 120, and blue is 240.
+
+ \inlineimage qcolor-hue.png
+
+ \o S, for saturation, is in the range 0 to 255, and the bigger it is,
+ the stronger the color is. Grayish colors have saturation near 0; very
+ strong colors have saturation near 255.
+
+ \inlineimage qcolor-saturation.png
+
+ \o V, for value, is in the range 0 to 255 and represents lightness or
+ brightness of the color. 0 is black; 255 is as far from black as
+ possible.
+
+ \inlineimage qcolor-value.png
+ \endlist
+
+ Here are some examples: pure red is H=0, S=255, V=255; a dark red,
+ moving slightly towards the magenta, could be H=350 (equivalent to
+ -10), S=255, V=180; a grayish light red could have H about 0 (say
+ 350-359 or 0-10), S about 50-100, and S=255.
+
+ Qt returns a hue value of -1 for achromatic colors. If you pass a
+ hue value that is too large, Qt forces it into range. Hue 360 or 720 is
+ treated as 0; hue 540 is treated as 180.
+
+ In addition to the standard HSV model, Qt provides an
+ alpha-channel to feature \l {QColor#Alpha-Blended
+ Drawing}{alpha-blended drawing}.
+
+ \section1 The CMYK Color Model
+
+ While the RGB and HSV color models are used for display on
+ computer monitors, the CMYK model is used in the four-color
+ printing process of printing presses and some hard-copy
+ devices.
+
+ CMYK has four components, all in the range 0-255: cyan (C),
+ magenta (M), yellow (Y) and black (K). Cyan, magenta and yellow
+ are called subtractive colors; the CMYK color model creates color
+ by starting with a white surface and then subtracting color by
+ applying the appropriate components. While combining cyan, magenta
+ and yellow gives the color black, subtracting one or more will
+ yield any other color. When combined in various percentages, these
+ three colors can create the entire spectrum of colors.
+
+ Mixing 100 percent of cyan, magenta and yellow \e does produce
+ black, but the result is unsatisfactory since it wastes ink,
+ increases drying time, and gives a muddy colour when printing. For
+ that reason, black is added in professional printing to provide a
+ solid black tone; hence the term 'four color process'.
+
+ In addition to the standard CMYK model, Qt provides an
+ alpha-channel to feature \l {QColor#Alpha-Blended
+ Drawing}{alpha-blended drawing}.
+
+ \sa QPalette, QBrush, QApplication::setColorSpec()
+*/
+
+#define QCOLOR_INT_RANGE_CHECK(fn, var) \
+ do { \
+ if (var < 0 || var > 255) { \
+ qWarning(#fn": invalid value %d", var); \
+ var = qMax(0, qMin(var, 255)); \
+ } \
+ } while (0)
+
+#define QCOLOR_REAL_RANGE_CHECK(fn, var) \
+ do { \
+ if (var < qreal(0.0) || var > qreal(1.0)) { \
+ qWarning(#fn": invalid value %g", var); \
+ var = qMax(qreal(0.0), qMin(var, qreal(1.0))); \
+ } \
+ } while (0)
+
+/*****************************************************************************
+ QColor member functions
+ *****************************************************************************/
+
+/*!
+ \enum QColor::Spec
+
+ The type of color specified, either RGB, HSV or CMYK.
+
+ \value Rgb
+ \value Hsv
+ \value Cmyk
+ \value Invalid
+
+ \sa spec(), convertTo()
+*/
+
+/*!
+ \fn Spec QColor::spec() const
+
+ Returns how the color was specified.
+
+ \sa Spec, convertTo()
+*/
+
+
+/*!
+ \fn QColor::QColor()
+
+ Constructs an invalid color with the RGB value (0, 0, 0). An
+ invalid color is a color that is not properly set up for the
+ underlying window system.
+
+ The alpha value of an invalid color is unspecified.
+
+ \sa isValid()
+*/
+
+/*!
+ \overload
+
+ Constructs a new color with a color value of \a color.
+
+ \sa isValid(), {QColor#Predefined Colors}{Predefined Colors}
+ */
+QColor::QColor(Qt::GlobalColor color)
+{
+#define QRGB(r, g, b) \
+ QRgb(((0xffu << 24) | ((r & 0xff) << 16) | ((g & 0xff) << 8) | (b & 0xff)))
+#define QRGBA(r, g, b, a) \
+ QRgb(((a & 0xff) << 24) | ((r & 0xff) << 16) | ((g & 0xff) << 8) | (b & 0xff))
+
+ static const QRgb global_colors[] = {
+ QRGB(255, 255, 255), // Qt::color0
+ QRGB( 0, 0, 0), // Qt::color1
+ QRGB( 0, 0, 0), // black
+ QRGB(255, 255, 255), // white
+ /*
+ * From the "The Palette Manager: How and Why" by Ron Gery,
+ * March 23, 1992, archived on MSDN:
+ *
+ * The Windows system palette is broken up into two
+ * sections, one with fixed colors and one with colors
+ * that can be changed by applications. The system palette
+ * predefines 20 entries; these colors are known as the
+ * static or reserved colors and consist of the 16 colors
+ * found in the Windows version 3.0 VGA driver and 4
+ * additional colors chosen for their visual appeal. The
+ * DEFAULT_PALETTE stock object is, as the name implies,
+ * the default palette selected into a device context (DC)
+ * and consists of these static colors. Applications can
+ * set the remaining 236 colors using the Palette Manager.
+ *
+ * The 20 reserved entries have indices in [0,9] and
+ * [246,255]. We reuse 17 of them.
+ */
+ QRGB(128, 128, 128), // index 248 medium gray
+ QRGB(160, 160, 164), // index 247 light gray
+ QRGB(192, 192, 192), // index 7 light gray
+ QRGB(255, 0, 0), // index 249 red
+ QRGB( 0, 255, 0), // index 250 green
+ QRGB( 0, 0, 255), // index 252 blue
+ QRGB( 0, 255, 255), // index 254 cyan
+ QRGB(255, 0, 255), // index 253 magenta
+ QRGB(255, 255, 0), // index 251 yellow
+ QRGB(128, 0, 0), // index 1 dark red
+ QRGB( 0, 128, 0), // index 2 dark green
+ QRGB( 0, 0, 128), // index 4 dark blue
+ QRGB( 0, 128, 128), // index 6 dark cyan
+ QRGB(128, 0, 128), // index 5 dark magenta
+ QRGB(128, 128, 0), // index 3 dark yellow
+ QRGBA(0, 0, 0, 0) // transparent
+ };
+#undef QRGB
+#undef QRGBA
+
+ setRgb(qRed(global_colors[color]),
+ qGreen(global_colors[color]),
+ qBlue(global_colors[color]),
+ qAlpha(global_colors[color]));
+}
+
+/*!
+ \fn QColor::QColor(int r, int g, int b, int a = 255)
+
+ Constructs a color with the RGB value \a r, \a g, \a b, and the
+ alpha-channel (transparency) value of \a a.
+
+ The color is left invalid if any of the arguments are invalid.
+
+ \sa setRgba(), isValid()
+*/
+
+/*!
+ Constructs a color with the value \a color. The alpha component is
+ ignored and set to solid.
+
+ \sa fromRgb(), isValid()
+*/
+
+QColor::QColor(QRgb color)
+{
+ cspec = Rgb;
+ ct.argb.alpha = 0xffff;
+ ct.argb.red = qRed(color) * 0x101;
+ ct.argb.green = qGreen(color) * 0x101;
+ ct.argb.blue = qBlue(color) * 0x101;
+ ct.argb.pad = 0;
+}
+
+
+/*!
+ \internal
+
+ Constructs a color with the given \a spec.
+
+ This function is primarly present to avoid that QColor::Invalid
+ becomes a valid color by accident.
+*/
+
+QColor::QColor(Spec spec)
+{
+ switch (spec) {
+ case Invalid:
+ invalidate();
+ break;
+ case Rgb:
+ setRgb(0, 0, 0);
+ break;
+ case Hsv:
+ setHsv(0, 0, 0);
+ break;
+ case Cmyk:
+ setCmyk(0, 0, 0, 0);
+ break;
+ }
+}
+
+/*!
+ \fn QColor::QColor(const QString &name)
+
+ Constructs a named color in the same way as setNamedColor() using
+ the given \a name.
+
+ The color is left invalid if the \a name cannot be parsed.
+
+ \sa setNamedColor(), name(), isValid()
+*/
+
+/*!
+ \fn QColor::QColor(const char *name)
+
+ Constructs a named color in the same way as setNamedColor() using
+ the given \a name.
+
+ The color is left invalid if the \a name cannot be parsed.
+
+ \sa setNamedColor(), name(), isValid()
+*/
+
+/*!
+ \fn QColor::QColor(const QColor &color)
+
+ Constructs a color that is a copy of \a color.
+
+ \sa isValid()
+*/
+
+/*!
+ \fn bool QColor::isValid() const
+
+ Returns true if the color is valid; otherwise returns false.
+*/
+
+/*!
+ Returns the name of the color in the format "#RRGGBB"; i.e. a "#"
+ character followed by three two-digit hexadecimal numbers.
+
+ \sa setNamedColor()
+*/
+
+QString QColor::name() const
+{
+ QString s;
+ s.sprintf("#%02x%02x%02x", red(), green(), blue());
+ return s;
+}
+
+/*!
+ Sets the RGB value of this QColor to \a name, which may be in one
+ of these formats:
+
+ \list
+ \i #RGB (each of R, G, and B is a single hex digit)
+ \i #RRGGBB
+ \i #RRRGGGBBB
+ \i #RRRRGGGGBBBB
+ \i A name from the list of colors defined in the list of \l{SVG color keyword names}
+ provided by the World Wide Web Consortium; for example, "steelblue" or "gainsboro".
+ These color names work on all platforms. Note that these color names are \i not the
+ same as defined by the Qt::GlobalColor enums, e.g. "green" and Qt::green does not
+ refer to the same color.
+ \i \c transparent - representing the absence of a color.
+ \i \e{X11 only}: If allowX11ColorNames() returns true, any valid X11 color name. See
+ the documentation for \c XParseColor() for information about valid X11 color names.
+ \endlist
+
+ The color is invalid if \a name cannot be parsed.
+
+ \sa QColor(), name(), isValid(), allowX11ColorNames()
+*/
+
+void QColor::setNamedColor(const QString &name)
+{
+ if (name.isEmpty()) {
+ invalidate();
+ return;
+ }
+
+ if (name.startsWith(QLatin1Char('#'))) {
+ QRgb rgb;
+ if (qt_get_hex_rgb(name.constData(), name.length(), &rgb)) {
+ setRgb(rgb);
+ } else {
+ invalidate();
+ }
+ return;
+ }
+
+#ifndef QT_NO_COLORNAMES
+ QRgb rgb;
+ if (qt_get_named_rgb(name.constData(), name.length(), &rgb)) {
+ setRgba(rgb);
+ } else
+#endif
+ {
+#ifdef Q_WS_X11
+ XColor result;
+ if (allowX11ColorNames()
+ && QApplication::instance()
+ && QX11Info::display()
+ && XParseColor(QX11Info::display(), QX11Info::appColormap(), name.toLatin1().constData(), &result)) {
+ setRgb(result.red >> 8, result.green >> 8, result.blue >> 8);
+ } else
+#endif
+ {
+ qWarning("QColor::setNamedColor: Unknown color name '%s'", name.toLatin1().constData());
+ invalidate();
+ }
+ }
+}
+
+/*!
+ Returns a QStringList containing the color names Qt knows about.
+
+ \sa {QColor#Predefined Colors}{Predefined Colors}
+*/
+QStringList QColor::colorNames()
+{
+#ifndef QT_NO_COLORNAMES
+ return qt_get_colornames();
+#else
+ return QStringList();
+#endif
+}
+
+/*!
+ Sets the contents pointed to by \a h, \a s, \a v, and \a a, to the hue,
+ saturation, value, and alpha-channel (transparency) components of the
+ color's HSV value.
+
+ These components can be retrieved individually using the hueF(),
+ saturationF(), valueF() and alphaF() functions.
+
+ \sa setHsv() {QColor#The HSV Color Model}{The HSV Color Model}
+*/
+void QColor::getHsvF(qreal *h, qreal *s, qreal *v, qreal *a) const
+{
+ if (!h || !s || !v)
+ return;
+
+ if (cspec != Invalid && cspec != Hsv) {
+ toHsv().getHsvF(h, s, v, a);
+ return;
+ }
+
+ *h = ct.ahsv.hue == USHRT_MAX ? -1.0 : ct.ahsv.hue / 36000.0;
+ *s = ct.ahsv.saturation / qreal(USHRT_MAX);
+ *v = ct.ahsv.value / qreal(USHRT_MAX);
+
+ if (a)
+ *a = ct.ahsv.alpha / qreal(USHRT_MAX);
+}
+
+/*!
+ Sets the contents pointed to by \a h, \a s, \a v, and \a a, to the hue,
+ saturation, value, and alpha-channel (transparency) components of the
+ color's HSV value.
+
+ These components can be retrieved individually using the hue(),
+ saturation(), value() and alpha() functions.
+
+ \sa setHsv(), {QColor#The HSV Color Model}{The HSV Color Model}
+*/
+void QColor::getHsv(int *h, int *s, int *v, int *a) const
+{
+ if (!h || !s || !v)
+ return;
+
+ if (cspec != Invalid && cspec != Hsv) {
+ toHsv().getHsv(h, s, v, a);
+ return;
+ }
+
+ *h = ct.ahsv.hue == USHRT_MAX ? -1 : ct.ahsv.hue / 100;
+ *s = ct.ahsv.saturation >> 8;
+ *v = ct.ahsv.value >> 8;
+
+ if (a)
+ *a = ct.ahsv.alpha >> 8;
+}
+
+/*!
+ Sets a HSV color value; \a h is the hue, \a s is the saturation, \a v is
+ the value and \a a is the alpha component of the HSV color.
+
+ All the values must be in the range 0.0-1.0.
+
+ \sa getHsvF(), setHsv(), {QColor#The HSV Color Model}{The HSV
+ Color Model}
+*/
+void QColor::setHsvF(qreal h, qreal s, qreal v, qreal a)
+{
+ if (((h < 0.0 || h > 1.0) && h != -1.0)
+ || (s < 0.0 || s > 1.0)
+ || (v < 0.0 || v > 1.0)
+ || (a < 0.0 || a > 1.0)) {
+ qWarning("QColor::setHsvF: HSV parameters out of range");
+ return;
+ }
+
+ cspec = Hsv;
+ ct.ahsv.alpha = qRound(a * USHRT_MAX);
+ ct.ahsv.hue = h == -1.0 ? USHRT_MAX : qRound(h * 36000);
+ ct.ahsv.saturation = qRound(s * USHRT_MAX);
+ ct.ahsv.value = qRound(v * USHRT_MAX);
+ ct.ahsv.pad = 0;
+}
+
+/*!
+ Sets a HSV color value; \a h is the hue, \a s is the saturation, \a v is
+ the value and \a a is the alpha component of the HSV color.
+
+ The saturation, value and alpha-channel values must be in the range 0-255,
+ and the hue value must be greater than -1.
+
+ \sa getHsv(), setHsvF(), {QColor#The HSV Color Model}{The HSV
+ Color Model}
+*/
+void QColor::setHsv(int h, int s, int v, int a)
+{
+ if (h < -1 || (uint)s > 255 || (uint)v > 255 || (uint)a > 255) {
+ qWarning("QColor::setHsv: HSV parameters out of range");
+ invalidate();
+ return;
+ }
+
+ cspec = Hsv;
+ ct.ahsv.alpha = a * 0x101;
+ ct.ahsv.hue = h == -1 ? USHRT_MAX : (h % 360) * 100;
+ ct.ahsv.saturation = s * 0x101;
+ ct.ahsv.value = v * 0x101;
+ ct.ahsv.pad = 0;
+}
+
+/*!
+ Sets the contents pointed to by \a r, \a g, \a b, and \a a, to the red,
+ green, blue, and alpha-channel (transparency) components of the color's
+ RGB value.
+
+ These components can be retrieved individually using the redF(), greenF(),
+ blueF() and alphaF() functions.
+
+ \sa rgb(), setRgb()
+*/
+void QColor::getRgbF(qreal *r, qreal *g, qreal *b, qreal *a) const
+{
+ if (!r || !g || !b)
+ return;
+
+ if (cspec != Invalid && cspec != Rgb) {
+ toRgb().getRgbF(r, g, b, a);
+ return;
+ }
+
+ *r = ct.argb.red / qreal(USHRT_MAX);
+ *g = ct.argb.green / qreal(USHRT_MAX);
+ *b = ct.argb.blue / qreal(USHRT_MAX);
+
+ if (a)
+ *a = ct.argb.alpha / qreal(USHRT_MAX);
+
+}
+
+/*!
+ Sets the contents pointed to by \a r, \a g, \a b, and \a a, to the red,
+ green, blue, and alpha-channel (transparency) components of the color's
+ RGB value.
+
+ These components can be retrieved individually using the red(), green(),
+ blue() and alpha() functions.
+
+ \sa rgb(), setRgb()
+*/
+void QColor::getRgb(int *r, int *g, int *b, int *a) const
+{
+ if (!r || !g || !b)
+ return;
+
+ if (cspec != Invalid && cspec != Rgb) {
+ toRgb().getRgb(r, g, b, a);
+ return;
+ }
+
+ *r = ct.argb.red >> 8;
+ *g = ct.argb.green >> 8;
+ *b = ct.argb.blue >> 8;
+
+ if (a)
+ *a = ct.argb.alpha >> 8;
+}
+
+/*!
+ \obsolete
+ \fn void QColor::getRgba(int *r, int *g, int *b, int *a) const
+
+ Use getRgb() instead.
+*/
+
+/*!
+ \fn void QColor::setRgbF(qreal r, qreal g, qreal b, qreal a)
+
+ Sets the color channels of this color to \a r (red), \a g (green),
+ \a b (blue) and \a a (alpha, transparency).
+
+ All values must be in the range 0.0-1.0.
+
+ \sa rgb(), getRgbF(), setRgb()
+*/
+void QColor::setRgbF(qreal r, qreal g, qreal b, qreal a)
+{
+ if (r < 0.0 || r > 1.0
+ || g < 0.0 || g > 1.0
+ || b < 0.0 || b > 1.0
+ || a < 0.0 || a > 1.0) {
+ qWarning("QColor::setRgbF: RGB parameters out of range");
+ invalidate();
+ return;
+ }
+
+ cspec = Rgb;
+ ct.argb.alpha = qRound(a * USHRT_MAX);
+ ct.argb.red = qRound(r * USHRT_MAX);
+ ct.argb.green = qRound(g * USHRT_MAX);
+ ct.argb.blue = qRound(b * USHRT_MAX);
+ ct.argb.pad = 0;
+}
+
+/*!
+ Sets the RGB value to \a r, \a g, \a b and the alpha value to \a a.
+
+ All the values must be in the range 0-255.
+
+ \sa rgb(), getRgb(), setRgbF()
+*/
+void QColor::setRgb(int r, int g, int b, int a)
+{
+ if ((uint)r > 255 || (uint)g > 255 || (uint)b > 255 || (uint)a > 255) {
+ qWarning("QColor::setRgb: RGB parameters out of range");
+ invalidate();
+ return;
+ }
+
+ cspec = Rgb;
+ ct.argb.alpha = a * 0x101;
+ ct.argb.red = r * 0x101;
+ ct.argb.green = g * 0x101;
+ ct.argb.blue = b * 0x101;
+ ct.argb.pad = 0;
+}
+
+/*!
+ \obsolete
+ \fn void QColor::setRgba(int r, int g, int b, int a)
+
+ Use setRgb() instead.
+*/
+
+/*!
+ \fn QRgb QColor::rgba() const
+
+ Returns the RGB value of the color. Unlike rgb(), the alpha is not
+ stripped.
+
+ For an invalid color, the alpha value of the returned color is unspecified.
+
+ \sa setRgba(), rgb()
+*/
+
+QRgb QColor::rgba() const
+{
+ if (cspec != Invalid && cspec != Rgb)
+ return toRgb().rgba();
+ return qRgba(ct.argb.red >> 8, ct.argb.green >> 8, ct.argb.blue >> 8, ct.argb.alpha >> 8);
+}
+
+/*!
+ Sets the RGBA value to \a rgba. Unlike setRgb(QRgb rgb), this function does
+ not ignore the alpha.
+
+ \sa rgba(), rgb()
+*/
+void QColor::setRgba(QRgb rgba)
+{
+ cspec = Rgb;
+ ct.argb.alpha = qAlpha(rgba) * 0x101;
+ ct.argb.red = qRed(rgba) * 0x101;
+ ct.argb.green = qGreen(rgba) * 0x101;
+ ct.argb.blue = qBlue(rgba) * 0x101;
+ ct.argb.pad = 0;
+}
+
+/*!
+ \fn QRgb QColor::rgb() const
+
+ Returns the RGB value of the color. The alpha is stripped for
+ compatibility.
+
+ \sa getRgb(), rgba()
+*/
+QRgb QColor::rgb() const
+{
+ if (cspec != Invalid && cspec != Rgb)
+ return toRgb().rgb();
+ return qRgb(ct.argb.red >> 8, ct.argb.green >> 8, ct.argb.blue >> 8);
+}
+
+/*!
+ \overload
+
+ Sets the RGB value to \a rgb, ignoring the alpha.
+*/
+void QColor::setRgb(QRgb rgb)
+{
+ cspec = Rgb;
+ ct.argb.alpha = 0xffff;
+ ct.argb.red = qRed(rgb) * 0x101;
+ ct.argb.green = qGreen(rgb) * 0x101;
+ ct.argb.blue = qBlue(rgb) * 0x101;
+ ct.argb.pad = 0;
+}
+
+/*!
+ Returns the alpha color component of this color.
+
+ \sa setAlpha(), alphaF(), {QColor#Alpha-Blended
+ Drawing}{Alpha-Blended Drawing}
+*/
+int QColor::alpha() const
+{ return ct.argb.alpha >> 8; }
+
+
+/*!
+ Sets the alpha of this color to \a alpha. Integer alpha is specified in the
+ range 0-255.
+
+ \sa alpha(), alphaF(), {QColor#Alpha-Blended
+ Drawing}{Alpha-Blended Drawing}
+*/
+
+void QColor::setAlpha(int alpha)
+{
+ QCOLOR_INT_RANGE_CHECK("QColor::setAlpha", alpha);
+ ct.argb.alpha = alpha * 0x101;
+}
+
+/*!
+ Returns the alpha color component of this color.
+
+ \sa setAlphaF(), alpha(), {QColor#Alpha-Blended
+ Drawing}{Alpha-Blended Drawing}
+*/
+qreal QColor::alphaF() const
+{ return ct.argb.alpha / qreal(USHRT_MAX); }
+
+/*!
+ Sets the alpha of this color to \a alpha. qreal alpha is specified in the
+ range 0.0-1.0.
+
+ \sa alphaF(), alpha(), {QColor#Alpha-Blended
+ Drawing}{Alpha-Blended Drawing}
+
+*/
+void QColor::setAlphaF(qreal alpha)
+{
+ QCOLOR_REAL_RANGE_CHECK("QColor::setAlphaF", alpha);
+ qreal tmp = alpha * USHRT_MAX;
+ ct.argb.alpha = qRound(tmp);
+}
+
+
+/*!
+ Returns the red color component of this color.
+
+ \sa setRed(), redF(), getRgb()
+*/
+int QColor::red() const
+{
+ if (cspec != Invalid && cspec != Rgb)
+ return toRgb().red();
+ return ct.argb.red >> 8;
+}
+
+/*!
+ Sets the red color component of this color to \a red. Integer components
+ are specified in the range 0-255.
+
+ \sa red(), redF(), setRgb()
+*/
+void QColor::setRed(int red)
+{
+ QCOLOR_INT_RANGE_CHECK("QColor::setRed", red);
+ if (cspec != Rgb)
+ setRgb(red, green(), blue(), alpha());
+ else
+ ct.argb.red = red * 0x101;
+}
+
+/*!
+ Returns the green color component of this color.
+
+ \sa setGreen(), greenF(), getRgb()
+*/
+int QColor::green() const
+{
+ if (cspec != Invalid && cspec != Rgb)
+ return toRgb().green();
+ return ct.argb.green >> 8;
+}
+
+/*!
+ Sets the green color component of this color to \a green. Integer
+ components are specified in the range 0-255.
+
+ \sa green(), greenF(), setRgb()
+*/
+void QColor::setGreen(int green)
+{
+ QCOLOR_INT_RANGE_CHECK("QColor::setGreen", green);
+ if (cspec != Rgb)
+ setRgb(red(), green, blue(), alpha());
+ else
+ ct.argb.green = green * 0x101;
+}
+
+
+/*!
+ Returns the blue color component of this color.
+
+ \sa setBlue(), blueF(), getRgb()
+*/
+int QColor::blue() const
+{
+ if (cspec != Invalid && cspec != Rgb)
+ return toRgb().blue();
+ return ct.argb.blue >> 8;
+}
+
+
+/*!
+ Sets the blue color component of this color to \a blue. Integer components
+ are specified in the range 0-255.
+
+ \sa blue(), blueF(), setRgb()
+*/
+void QColor::setBlue(int blue)
+{
+ QCOLOR_INT_RANGE_CHECK("QColor::setBlue", blue);
+ if (cspec != Rgb)
+ setRgb(red(), green(), blue, alpha());
+ else
+ ct.argb.blue = blue * 0x101;
+}
+
+/*!
+ Returns the red color component of this color.
+
+ \sa setRedF(), red(), getRgbF()
+*/
+qreal QColor::redF() const
+{
+ if (cspec != Invalid && cspec != Rgb)
+ return toRgb().redF();
+ return ct.argb.red / qreal(USHRT_MAX);
+}
+
+
+/*!
+ Sets the red color component of this color to \a red. Float components
+ are specified in the range 0.0-1.0.
+
+ \sa redF(), red(), setRgbF()
+*/
+void QColor::setRedF(qreal red)
+{
+ QCOLOR_REAL_RANGE_CHECK("QColor::setRedF", red);
+ if (cspec != Rgb)
+ setRgbF(red, greenF(), blueF(), alphaF());
+ else
+ ct.argb.red = qRound(red * USHRT_MAX);
+}
+
+/*!
+ Returns the green color component of this color.
+
+ \sa setGreenF(), green(), getRgbF()
+*/
+qreal QColor::greenF() const
+{
+ if (cspec != Invalid && cspec != Rgb)
+ return toRgb().greenF();
+ return ct.argb.green / qreal(USHRT_MAX);
+}
+
+
+/*!
+ Sets the green color component of this color to \a green. Float components
+ are specified in the range 0.0-1.0.
+
+ \sa greenF(), green(), setRgbF()
+*/
+void QColor::setGreenF(qreal green)
+{
+ QCOLOR_REAL_RANGE_CHECK("QColor::setGreenF", green);
+ if (cspec != Rgb)
+ setRgbF(redF(), green, blueF(), alphaF());
+ else
+ ct.argb.green = qRound(green * USHRT_MAX);
+}
+
+/*!
+ Returns the blue color component of this color.
+
+ \sa setBlueF(), blue(), getRgbF()
+*/
+qreal QColor::blueF() const
+{
+ if (cspec != Invalid && cspec != Rgb)
+ return toRgb().blueF();
+ return ct.argb.blue / qreal(USHRT_MAX);
+}
+
+/*!
+ Sets the blue color component of this color to \a blue. Float components
+ are specified in the range 0.0-1.0.
+
+ \sa blueF(), blue(), setRgbF()
+*/
+void QColor::setBlueF(qreal blue)
+{
+ QCOLOR_REAL_RANGE_CHECK("QColor::setBlueF", blue);
+ if (cspec != Rgb)
+ setRgbF(redF(), greenF(), blue, alphaF());
+ else
+ ct.argb.blue = qRound(blue * USHRT_MAX);
+}
+
+/*!
+ Returns the hue color component of this color.
+
+ \sa hueF(), getHsv(), {QColor#The HSV Color Model}{The HSV Color
+ Model}
+*/
+int QColor::hue() const
+{
+ if (cspec != Invalid && cspec != Hsv)
+ return toHsv().hue();
+ return ct.ahsv.hue == USHRT_MAX ? -1 : ct.ahsv.hue / 100;
+}
+
+/*!
+ Returns the saturation color component of this color.
+
+ \sa saturationF(), getHsv(), {QColor#The HSV Color Model}{The HSV Color
+ Model}
+*/
+int QColor::saturation() const
+{
+ if (cspec != Invalid && cspec != Hsv)
+ return toHsv().saturation();
+ return ct.ahsv.saturation >> 8;
+}
+
+/*!
+ Returns the value color component of this color.
+
+ \sa valueF(), getHsv(), {QColor#The HSV Color Model}{The HSV Color
+ Model}
+*/
+int QColor::value() const
+{
+ if (cspec != Invalid && cspec != Hsv)
+ return toHsv().value();
+ return ct.ahsv.value >> 8;
+}
+
+/*!
+ Returns the hue color component of this color.
+
+ \sa hue(), getHsvF(), {QColor#The HSV Color Model}{The HSV Color
+ Model}
+*/
+qreal QColor::hueF() const
+{
+ if (cspec != Invalid && cspec != Hsv)
+ return toHsv().hueF();
+ return ct.ahsv.hue == USHRT_MAX ? -1.0 : ct.ahsv.hue / 36000.0;
+}
+
+/*!
+ Returns the saturation color component of this color.
+
+ \sa saturation() getHsvF(), {QColor#The HSV Color Model}{The HSV Color
+ Model}
+*/
+qreal QColor::saturationF() const
+{
+ if (cspec != Invalid && cspec != Hsv)
+ return toHsv().saturationF();
+ return ct.ahsv.saturation / qreal(USHRT_MAX);
+}
+
+/*!
+ Returns the value color component of this color.
+
+ \sa value() getHsvF(), {QColor#The HSV Color Model}{The HSV Color
+ Model}
+*/
+qreal QColor::valueF() const
+{
+ if (cspec != Invalid && cspec != Hsv)
+ return toHsv().valueF();
+ return ct.ahsv.value / qreal(USHRT_MAX);
+}
+
+/*!
+ Returns the cyan color component of this color.
+
+ \sa cyanF(), getCmyk(), {QColor#The CMYK Color Model}{The CMYK
+ Color Model}
+*/
+int QColor::cyan() const
+{
+ if (cspec != Invalid && cspec != Cmyk)
+ return toCmyk().cyan();
+ return ct.acmyk.cyan >> 8;
+}
+
+/*!
+ Returns the magenta color component of this color.
+
+ \sa magentaF(), getCmyk(), {QColor#The CMYK Color Model}{The CMYK
+ Color Model}
+*/
+int QColor::magenta() const
+{
+ if (cspec != Invalid && cspec != Cmyk)
+ return toCmyk().magenta();
+ return ct.acmyk.magenta >> 8;
+}
+
+/*!
+ Returns the yellow color component of this color.
+
+ \sa yellowF(), getCmyk(), {QColor#The CMYK Color Model}{The CMYK
+ Color Model}
+*/
+int QColor::yellow() const
+{
+ if (cspec != Invalid && cspec != Cmyk)
+ return toCmyk().yellow();
+ return ct.acmyk.yellow >> 8;
+}
+
+/*!
+ Returns the black color component of this color.
+
+ \sa blackF(), getCmyk(), {QColor#The CMYK Color Model}{The CMYK
+ Color Model}
+
+*/
+int QColor::black() const
+{
+ if (cspec != Invalid && cspec != Cmyk)
+ return toCmyk().black();
+ return ct.acmyk.black >> 8;
+}
+
+/*!
+ Returns the cyan color component of this color.
+
+ \sa cyan(), getCmykF(), {QColor#The CMYK Color Model}{The CMYK
+ Color Model}
+*/
+qreal QColor::cyanF() const
+{
+ if (cspec != Invalid && cspec != Cmyk)
+ return toCmyk().cyanF();
+ return ct.acmyk.cyan / qreal(USHRT_MAX);
+}
+
+/*!
+ Returns the magenta color component of this color.
+
+ \sa magenta(), getCmykF(), {QColor#The CMYK Color Model}{The CMYK
+ Color Model}
+*/
+qreal QColor::magentaF() const
+{
+ if (cspec != Invalid && cspec != Cmyk)
+ return toCmyk().magentaF();
+ return ct.acmyk.magenta / qreal(USHRT_MAX);
+}
+
+/*!
+ Returns the yellow color component of this color.
+
+ \sa yellow(), getCmykF(), {QColor#The CMYK Color Model}{The CMYK
+ Color Model}
+*/
+qreal QColor::yellowF() const
+{
+ if (cspec != Invalid && cspec != Cmyk)
+ return toCmyk().yellowF();
+ return ct.acmyk.yellow / qreal(USHRT_MAX);
+}
+
+/*!
+ Returns the black color component of this color.
+
+ \sa black(), getCmykF(), {QColor#The CMYK Color Model}{The CMYK
+ Color Model}
+*/
+qreal QColor::blackF() const
+{
+ if (cspec != Invalid && cspec != Cmyk)
+ return toCmyk().blackF();
+ return ct.acmyk.black / qreal(USHRT_MAX);
+}
+
+/*!
+ Create and returns an RGB QColor based on this color.
+
+ \sa fromRgb(), convertTo(), isValid()
+*/
+QColor QColor::toRgb() const
+{
+ if (!isValid() || cspec == Rgb)
+ return *this;
+
+ QColor color;
+ color.cspec = Rgb;
+ color.ct.argb.alpha = ct.argb.alpha;
+ color.ct.argb.pad = 0;
+
+ switch (cspec) {
+ case Hsv:
+ {
+ if (ct.ahsv.saturation == 0 || ct.ahsv.hue == USHRT_MAX) {
+ // achromatic case
+ color.ct.argb.red = color.ct.argb.green = color.ct.argb.blue = ct.ahsv.value;
+ break;
+ }
+
+ // chromatic case
+ const qreal h = ct.ahsv.hue == 36000 ? 0 : ct.ahsv.hue / 6000.;
+ const qreal s = ct.ahsv.saturation / qreal(USHRT_MAX);
+ const qreal v = ct.ahsv.value / qreal(USHRT_MAX);
+ const int i = int(h);
+ const qreal f = h - i;
+ const qreal p = v * (1.0 - s);
+
+ if (i & 1) {
+ const qreal q = v * (1.0 - (s * f));
+
+ switch (i) {
+ case 1:
+ color.ct.argb.red = qRound(q * USHRT_MAX);
+ color.ct.argb.green = qRound(v * USHRT_MAX);
+ color.ct.argb.blue = qRound(p * USHRT_MAX);
+ break;
+ case 3:
+ color.ct.argb.red = qRound(p * USHRT_MAX);
+ color.ct.argb.green = qRound(q * USHRT_MAX);
+ color.ct.argb.blue = qRound(v * USHRT_MAX);
+ break;
+ case 5:
+ color.ct.argb.red = qRound(v * USHRT_MAX);
+ color.ct.argb.green = qRound(p * USHRT_MAX);
+ color.ct.argb.blue = qRound(q * USHRT_MAX);
+ break;
+ }
+ } else {
+ const qreal t = v * (1.0 - (s * (1.0 - f)));
+
+ switch (i) {
+ case 0:
+ color.ct.argb.red = qRound(v * USHRT_MAX);
+ color.ct.argb.green = qRound(t * USHRT_MAX);
+ color.ct.argb.blue = qRound(p * USHRT_MAX);
+ break;
+ case 2:
+ color.ct.argb.red = qRound(p * USHRT_MAX);
+ color.ct.argb.green = qRound(v * USHRT_MAX);
+ color.ct.argb.blue = qRound(t * USHRT_MAX);
+ break;
+ case 4:
+ color.ct.argb.red = qRound(t * USHRT_MAX);
+ color.ct.argb.green = qRound(p * USHRT_MAX);
+ color.ct.argb.blue = qRound(v * USHRT_MAX);
+ break;
+ }
+ }
+ break;
+ }
+ case Cmyk:
+ {
+ const qreal c = ct.acmyk.cyan / qreal(USHRT_MAX);
+ const qreal m = ct.acmyk.magenta / qreal(USHRT_MAX);
+ const qreal y = ct.acmyk.yellow / qreal(USHRT_MAX);
+ const qreal k = ct.acmyk.black / qreal(USHRT_MAX);
+
+ color.ct.argb.red = qRound((1.0 - (c * (1.0 - k) + k)) * USHRT_MAX);
+ color.ct.argb.green = qRound((1.0 - (m * (1.0 - k) + k)) * USHRT_MAX);
+ color.ct.argb.blue = qRound((1.0 - (y * (1.0 - k) + k)) * USHRT_MAX);
+ break;
+ }
+ default:
+ break;
+ }
+
+ return color;
+}
+
+
+#define Q_MAX_3(a, b, c) ( ( a > b && a > c) ? a : (b > c ? b : c) )
+#define Q_MIN_3(a, b, c) ( ( a < b && a < c) ? a : (b < c ? b : c) )
+
+
+/*!
+ Creates and returns an HSV QColor based on this color.
+
+ \sa fromHsv(), convertTo(), isValid(), {QColor#The HSV Color
+ Model}{The HSV Color Model}
+*/
+QColor QColor::toHsv() const
+{
+ if (!isValid())
+ return *this;
+
+ if (cspec != Rgb)
+ return toRgb().toHsv();
+
+ QColor color;
+ color.cspec = Hsv;
+ color.ct.ahsv.alpha = ct.argb.alpha;
+ color.ct.ahsv.pad = 0;
+
+ const qreal r = ct.argb.red / qreal(USHRT_MAX);
+ const qreal g = ct.argb.green / qreal(USHRT_MAX);
+ const qreal b = ct.argb.blue / qreal(USHRT_MAX);
+ const qreal max = Q_MAX_3(r, g, b);
+ const qreal min = Q_MIN_3(r, g, b);
+ const qreal delta = max - min;
+ color.ct.ahsv.value = qRound(max * USHRT_MAX);
+ if (qFuzzyCompare(delta + 1, 1)) {
+ // achromatic case, hue is undefined
+ color.ct.ahsv.hue = USHRT_MAX;
+ color.ct.ahsv.saturation = 0;
+ } else {
+ // chromatic case
+ qreal hue = 0;
+ color.ct.ahsv.saturation = qRound((delta / max) * USHRT_MAX);
+ if (qFuzzyCompare(r, max)) {
+ hue = ((g - b) /delta);
+ } else if (qFuzzyCompare(g, max)) {
+ hue = (2.0 + (b - r) / delta);
+ } else if (qFuzzyCompare(b, max)) {
+ hue = (4.0 + (r - g) / delta);
+ } else {
+ Q_ASSERT_X(false, "QColor::toHsv", "internal error");
+ }
+ hue *= 60.0;
+ if (hue < 0.0)
+ hue += 360.0;
+ color.ct.ahsv.hue = qRound(hue * 100);
+ }
+
+ return color;
+}
+
+/*!
+ Creates and returns a CMYK QColor based on this color.
+
+ \sa fromCmyk(), convertTo(), isValid(), {QColor#The CMYK Color
+ Model}{The CMYK Color Model}
+*/
+QColor QColor::toCmyk() const
+{
+ if (!isValid())
+ return *this;
+ if (cspec != Rgb)
+ return toRgb().toCmyk();
+
+ QColor color;
+ color.cspec = Cmyk;
+ color.ct.acmyk.alpha = ct.argb.alpha;
+
+ // rgb -> cmy
+ const qreal r = ct.argb.red / qreal(USHRT_MAX);
+ const qreal g = ct.argb.green / qreal(USHRT_MAX);
+ const qreal b = ct.argb.blue / qreal(USHRT_MAX);
+ qreal c = 1.0 - r;
+ qreal m = 1.0 - g;
+ qreal y = 1.0 - b;
+
+ // cmy -> cmyk
+ const qreal k = qMin(c, qMin(m, y));
+
+ if (!qFuzzyCompare(k,1)) {
+ c = (c - k) / (1.0 - k);
+ m = (m - k) / (1.0 - k);
+ y = (y - k) / (1.0 - k);
+ }
+
+ color.ct.acmyk.cyan = qRound(c * USHRT_MAX);
+ color.ct.acmyk.magenta = qRound(m * USHRT_MAX);
+ color.ct.acmyk.yellow = qRound(y * USHRT_MAX);
+ color.ct.acmyk.black = qRound(k * USHRT_MAX);
+
+ return color;
+}
+
+QColor QColor::convertTo(QColor::Spec colorSpec) const
+{
+ if (colorSpec == cspec)
+ return *this;
+ switch (colorSpec) {
+ case Rgb:
+ return toRgb();
+ case Hsv:
+ return toHsv();
+ case Cmyk:
+ return toCmyk();
+ case Invalid:
+ break;
+ }
+ return QColor(); // must be invalid
+}
+
+
+/*!
+ Static convenience function that returns a QColor constructed from the
+ given QRgb value \a rgb.
+
+ The alpha component of \a rgb is ignored (i.e. it is automatically set to
+ 255), use the fromRgba() function to include the alpha-channel specified by
+ the given QRgb value.
+
+ \sa fromRgba(), fromRgbF(), toRgb(), isValid()
+*/
+
+QColor QColor::fromRgb(QRgb rgb)
+{
+ return fromRgb(qRed(rgb), qGreen(rgb), qBlue(rgb));
+}
+
+
+/*!
+ Static convenience function that returns a QColor constructed from the
+ given QRgb value \a rgba.
+
+ Unlike the fromRgb() function, the alpha-channel specified by the given
+ QRgb value is included.
+
+ \sa fromRgb(), isValid()
+*/
+
+QColor QColor::fromRgba(QRgb rgba)
+{
+ return fromRgb(qRed(rgba), qGreen(rgba), qBlue(rgba), qAlpha(rgba));
+}
+
+/*!
+ Static convenience function that returns a QColor constructed from the RGB
+ color values, \a r (red), \a g (green), \a b (blue), and \a a
+ (alpha-channel, i.e. transparency).
+
+ All the values must be in the range 0-255.
+
+ \sa toRgb(), fromRgbF(), isValid()
+*/
+QColor QColor::fromRgb(int r, int g, int b, int a)
+{
+ if (r < 0 || r > 255
+ || g < 0 || g > 255
+ || b < 0 || b > 255
+ || a < 0 || a > 255) {
+ qWarning("QColor::fromRgb: RGB parameters out of range");
+ return QColor();
+ }
+
+ QColor color;
+ color.cspec = Rgb;
+ color.ct.argb.alpha = a * 0x101;
+ color.ct.argb.red = r * 0x101;
+ color.ct.argb.green = g * 0x101;
+ color.ct.argb.blue = b * 0x101;
+ color.ct.argb.pad = 0;
+ return color;
+}
+
+/*!
+ Static convenience function that returns a QColor constructed from the RGB
+ color values, \a r (red), \a g (green), \a b (blue), and \a a
+ (alpha-channel, i.e. transparency).
+
+ All the values must be in the range 0.0-1.0.
+
+ \sa fromRgb(), toRgb(), isValid()
+*/
+QColor QColor::fromRgbF(qreal r, qreal g, qreal b, qreal a)
+{
+ if (r < 0.0 || r > 1.0
+ || g < 0.0 || g > 1.0
+ || b < 0.0 || b > 1.0
+ || a < 0.0 || a > 1.0) {
+ qWarning("QColor::fromRgbF: RGB parameters out of range");
+ return QColor();
+ }
+
+ QColor color;
+ color.cspec = Rgb;
+ color.ct.argb.alpha = qRound(a * USHRT_MAX);
+ color.ct.argb.red = qRound(r * USHRT_MAX);
+ color.ct.argb.green = qRound(g * USHRT_MAX);
+ color.ct.argb.blue = qRound(b * USHRT_MAX);
+ color.ct.argb.pad = 0;
+ return color;
+}
+
+/*!
+ Static convenience function that returns a QColor constructed from the HSV
+ color values, \a h (hue), \a s (saturation), \a v (value), and \a a
+ (alpha-channel, i.e. transparency).
+
+ The value of \a s, \a v, and \a a must all be in the range 0-255; the value
+ of \a h must be in the range 0-359.
+
+ \sa toHsv(), fromHsvF(), isValid(), {QColor#The HSV Color
+ Model}{The HSV Color Model}
+*/
+QColor QColor::fromHsv(int h, int s, int v, int a)
+{
+ if (((h < 0 || h >= 360) && h != -1)
+ || s < 0 || s > 255
+ || v < 0 || v > 255
+ || a < 0 || a > 255) {
+ qWarning("QColor::fromHsv: HSV parameters out of range");
+ return QColor();
+ }
+
+ QColor color;
+ color.cspec = Hsv;
+ color.ct.ahsv.alpha = a * 0x101;
+ color.ct.ahsv.hue = h == -1 ? USHRT_MAX : (h % 360) * 100;
+ color.ct.ahsv.saturation = s * 0x101;
+ color.ct.ahsv.value = v * 0x101;
+ color.ct.ahsv.pad = 0;
+ return color;
+}
+
+/*!
+ \overload
+
+ Static convenience function that returns a QColor constructed from the HSV
+ color values, \a h (hue), \a s (saturation), \a v (value), and \a a
+ (alpha-channel, i.e. transparency).
+
+ All the values must be in the range 0.0-1.0.
+
+ \sa toHsv(), fromHsv(), isValid(), {QColor#The HSV Color
+ Model}{The HSV Color Model}
+*/
+QColor QColor::fromHsvF(qreal h, qreal s, qreal v, qreal a)
+{
+ if (((h < 0.0 || h > 1.0) && h != -1.0)
+ || (s < 0.0 || s > 1.0)
+ || (v < 0.0 || v > 1.0)
+ || (a < 0.0 || a > 1.0)) {
+ qWarning("QColor::fromHsvF: HSV parameters out of range");
+ return QColor();
+ }
+
+ QColor color;
+ color.cspec = Hsv;
+ color.ct.ahsv.alpha = qRound(a * USHRT_MAX);
+ color.ct.ahsv.hue = h == -1.0 ? USHRT_MAX : qRound(h * 36000);
+ color.ct.ahsv.saturation = qRound(s * USHRT_MAX);
+ color.ct.ahsv.value = qRound(v * USHRT_MAX);
+ color.ct.ahsv.pad = 0;
+ return color;
+}
+
+/*!
+ Sets the contents pointed to by \a c, \a m, \a y, \a k, and \a a, to the
+ cyan, magenta, yellow, black, and alpha-channel (transparency) components
+ of the color's CMYK value.
+
+ These components can be retrieved individually using the cyan(), magenta(),
+ yellow(), black() and alpha() functions.
+
+ \sa setCmyk(), {QColor#The CMYK Color Model}{The CMYK Color Model}
+*/
+void QColor::getCmyk(int *c, int *m, int *y, int *k, int *a)
+{
+ if (!c || !m || !y || !k)
+ return;
+
+ if (cspec != Invalid && cspec != Cmyk) {
+ toCmyk().getCmyk(c, m, y, k, a);
+ return;
+ }
+
+ *c = ct.acmyk.cyan >> 8;
+ *m = ct.acmyk.magenta >> 8;
+ *y = ct.acmyk.yellow >> 8;
+ *k = ct.acmyk.black >> 8;
+
+ if (a)
+ *a = ct.acmyk.alpha >> 8;
+}
+
+/*!
+ Sets the contents pointed to by \a c, \a m, \a y, \a k, and \a a, to the
+ cyan, magenta, yellow, black, and alpha-channel (transparency) components
+ of the color's CMYK value.
+
+ These components can be retrieved individually using the cyanF(),
+ magentaF(), yellowF(), blackF() and alphaF() functions.
+
+ \sa setCmykF(), {QColor#The CMYK Color Model}{The CMYK Color Model}
+*/
+void QColor::getCmykF(qreal *c, qreal *m, qreal *y, qreal *k, qreal *a)
+{
+ if (!c || !m || !y || !k)
+ return;
+
+ if (cspec != Invalid && cspec != Cmyk) {
+ toCmyk().getCmykF(c, m, y, k, a);
+ return;
+ }
+
+ *c = ct.acmyk.cyan / qreal(USHRT_MAX);
+ *m = ct.acmyk.magenta / qreal(USHRT_MAX);
+ *y = ct.acmyk.yellow / qreal(USHRT_MAX);
+ *k = ct.acmyk.black / qreal(USHRT_MAX);
+
+ if (a)
+ *a = ct.acmyk.alpha / qreal(USHRT_MAX);
+}
+
+/*!
+ Sets the color to CMYK values, \a c (cyan), \a m (magenta), \a y (yellow),
+ \a k (black), and \a a (alpha-channel, i.e. transparency).
+
+ All the values must be in the range 0-255.
+
+ \sa getCmyk(), setCmykF(), {QColor#The CMYK Color Model}{The
+ CMYK Color Model}
+*/
+void QColor::setCmyk(int c, int m, int y, int k, int a)
+{
+ if (c < 0 || c > 255
+ || m < 0 || m > 255
+ || y < 0 || y > 255
+ || k < 0 || k > 255
+ || a < 0 || a > 255) {
+ qWarning("QColor::setCmyk: CMYK parameters out of range");
+ return;
+ }
+
+ cspec = Cmyk;
+ ct.acmyk.alpha = a * 0x101;
+ ct.acmyk.cyan = c * 0x101;
+ ct.acmyk.magenta = m * 0x101;
+ ct.acmyk.yellow = y * 0x101;
+ ct.acmyk.black = k * 0x101;
+}
+
+/*!
+ \overload
+
+ Sets the color to CMYK values, \a c (cyan), \a m (magenta), \a y (yellow),
+ \a k (black), and \a a (alpha-channel, i.e. transparency).
+
+ All the values must be in the range 0.0-1.0.
+
+ \sa getCmykF() setCmyk(), {QColor#The CMYK Color Model}{The CMYK
+ Color Model}
+*/
+void QColor::setCmykF(qreal c, qreal m, qreal y, qreal k, qreal a)
+{
+ if (c < 0.0 || c > 1.0
+ || m < 0.0 || m > 1.0
+ || y < 0.0 || y > 1.0
+ || k < 0.0 || k > 1.0
+ || a < 0.0 || a > 1.0) {
+ qWarning("QColor::setCmykF: CMYK parameters out of range");
+ return;
+ }
+
+ cspec = Cmyk;
+ ct.acmyk.alpha = qRound(a * USHRT_MAX);
+ ct.acmyk.cyan = qRound(c * USHRT_MAX);
+ ct.acmyk.magenta = qRound(m * USHRT_MAX);
+ ct.acmyk.yellow = qRound(y * USHRT_MAX);
+ ct.acmyk.black = qRound(k * USHRT_MAX);
+}
+
+/*!
+ Static convenience function that returns a QColor constructed from the
+ given CMYK color values: \a c (cyan), \a m (magenta), \a y (yellow), \a k
+ (black), and \a a (alpha-channel, i.e. transparency).
+
+ All the values must be in the range 0-255.
+
+ \sa toCmyk(), fromCmykF(), isValid(), {QColor#The CMYK Color Model}{The CMYK
+ Color Model}
+*/
+QColor QColor::fromCmyk(int c, int m, int y, int k, int a)
+{
+ if (c < 0 || c > 255
+ || m < 0 || m > 255
+ || y < 0 || y > 255
+ || k < 0 || k > 255
+ || a < 0 || a > 255) {
+ qWarning("QColor::fromCmyk: CMYK parameters out of range");
+ return QColor();
+ }
+
+ QColor color;
+ color.cspec = Cmyk;
+ color.ct.acmyk.alpha = a * 0x101;
+ color.ct.acmyk.cyan = c * 0x101;
+ color.ct.acmyk.magenta = m * 0x101;
+ color.ct.acmyk.yellow = y * 0x101;
+ color.ct.acmyk.black = k * 0x101;
+ return color;
+}
+
+/*!
+ \overload
+
+ Static convenience function that returns a QColor constructed from the
+ given CMYK color values: \a c (cyan), \a m (magenta), \a y (yellow), \a k
+ (black), and \a a (alpha-channel, i.e. transparency).
+
+ All the values must be in the range 0.0-1.0.
+
+ \sa toCmyk(), fromCmyk(), isValid(), {QColor#The CMYK Color
+ Model}{The CMYK Color Model}
+*/
+QColor QColor::fromCmykF(qreal c, qreal m, qreal y, qreal k, qreal a)
+{
+ if (c < 0.0 || c > 1.0
+ || m < 0.0 || m > 1.0
+ || y < 0.0 || y > 1.0
+ || k < 0.0 || k > 1.0
+ || a < 0.0 || a > 1.0) {
+ qWarning("QColor::fromCmykF: CMYK parameters out of range");
+ return QColor();
+ }
+
+ QColor color;
+ color.cspec = Cmyk;
+ color.ct.acmyk.alpha = qRound(a * USHRT_MAX);
+ color.ct.acmyk.cyan = qRound(c * USHRT_MAX);
+ color.ct.acmyk.magenta = qRound(m * USHRT_MAX);
+ color.ct.acmyk.yellow = qRound(y * USHRT_MAX);
+ color.ct.acmyk.black = qRound(k * USHRT_MAX);
+ return color;
+}
+
+/*!
+ \fn QColor QColor::lighter(int factor) const
+ \since 4.3
+
+ Returns a lighter (or darker) color, but does not change this object.
+
+ If the \a factor is greater than 100, this functions returns a lighter
+ color. Setting \a factor to 150 returns a color that is 50% brighter. If
+ the \a factor is less than 100, the return color is darker, but we
+ recommend using the darker() function for this purpose. If the \a factor
+ is 0 or negative, the return value is unspecified.
+
+ The function converts the current RGB color to HSV, multiplies the value
+ (V) component by \a factor and converts the color back to RGB.
+
+ \sa darker(), isValid()
+*/
+
+/*!
+ \obsolete
+
+ Use lighter(\a factor) instead.
+*/
+QColor QColor::light(int factor) const
+{
+ if (factor <= 0) // invalid lightness factor
+ return *this;
+ else if (factor < 100) // makes color darker
+ return darker(10000 / factor);
+
+ QColor hsv = toHsv();
+ int s = hsv.ct.ahsv.saturation;
+ int v = hsv.ct.ahsv.value;
+
+ v = (factor*v)/100;
+ if (v > USHRT_MAX) {
+ // overflow... adjust saturation
+ s -= v - USHRT_MAX;
+ if (s < 0)
+ s = 0;
+ v = USHRT_MAX;
+ }
+
+ hsv.ct.ahsv.saturation = s;
+ hsv.ct.ahsv.value = v;
+
+ // convert back to same color spec as original color
+ return hsv.convertTo(cspec);
+}
+
+/*!
+ \fn QColor QColor::darker(int factor) const
+ \since 4.3
+
+ Returns a darker (or lighter) color, but does not change this object.
+
+ If the \a factor is greater than 100, this functions returns a darker
+ color. Setting \a factor to 300 returns a color that has one-third the
+ brightness. If the \a factor is less than 100, the return color is lighter,
+ but we recommend using the lighter() function for this purpose. If the
+ \a factor is 0 or negative, the return value is unspecified.
+
+ The function converts the current RGB color to HSV, divides the value (V)
+ component by \a factor and converts the color back to RGB.
+
+ \sa lighter(), isValid()
+*/
+
+/*!
+ \obsolete
+
+ Use darker(\a factor) instead.
+*/
+QColor QColor::dark(int factor) const
+{
+ if (factor <= 0) // invalid darkness factor
+ return *this;
+ else if (factor < 100) // makes color lighter
+ return lighter(10000 / factor);
+
+ QColor hsv = toHsv();
+ hsv.ct.ahsv.value = (hsv.ct.ahsv.value * 100) / factor;
+
+ // convert back to same color spec as original color
+ return hsv.convertTo(cspec);
+}
+
+/*!
+ Assigns a copy of \a color to this color, and returns a reference to it.
+*/
+QColor &QColor::operator=(const QColor &color)
+{
+ cspec = color.cspec;
+ ct.argb = color.ct.argb;
+ return *this;
+}
+
+/*! \overload
+ Assigns a copy of \a color and returns a reference to this color.
+ */
+QColor &QColor::operator=(Qt::GlobalColor color)
+{
+ return operator=(QColor(color));
+}
+
+/*!
+ Returns true if this color has the same RGB and alpha values as \a color;
+ otherwise returns false.
+*/
+bool QColor::operator==(const QColor &color) const
+{
+ return (cspec == color.cspec
+ && ct.argb.alpha == color.ct.argb.alpha
+ && ((cspec == QColor::Hsv
+ && ((ct.argb.red % 36000) == (color.ct.argb.red % 36000)))
+ || (ct.argb.red == color.ct.argb.red))
+ && ct.argb.green == color.ct.argb.green
+ && ct.argb.blue == color.ct.argb.blue
+ && ct.argb.pad == color.ct.argb.pad);
+}
+
+/*!
+ Returns true if this color has a different RGB and alpha values from
+ \a color; otherwise returns false.
+*/
+bool QColor::operator!=(const QColor &color) const
+{ return !operator==(color); }
+
+
+/*!
+ Returns the color as a QVariant
+*/
+QColor::operator QVariant() const
+{
+ return QVariant(QVariant::Color, this);
+}
+
+#ifdef Q_WS_X11
+/*!
+ Returns true if setNamedColor() is allowed to look up colors in the X11
+ color database. By default, this function returns false.
+
+ \note This function is only available on the X11 platform.
+
+ \sa setAllowX11ColorNames()
+*/
+bool QColor::allowX11ColorNames()
+{
+ return ::allowX11ColorNames;
+}
+
+/*!
+ Allow setNamedColor() to look up colors in the X11 color database if
+ \a enabled. By default, setNamedColor() does \e not look up colors in the
+ X11 color database.
+
+ \note This function is only available on the X11 platform.
+
+ \sa setNamedColor(), allowX11ColorNames()
+*/
+void QColor::setAllowX11ColorNames(bool enabled)
+{
+ ::allowX11ColorNames = enabled;
+}
+#endif
+
+/*! \internal
+
+ Marks the color as invalid and sets all components to zero (alpha is set
+ to fully opaque for compatibility with Qt 3).
+*/
+void QColor::invalidate()
+{
+ cspec = Invalid;
+ ct.argb.alpha = USHRT_MAX;
+ ct.argb.red = 0;
+ ct.argb.green = 0;
+ ct.argb.blue = 0;
+ ct.argb.pad = 0;
+}
+
+#ifdef QT3_SUPPORT
+
+/*!
+ Returns the pixel value used by the underlying window system to refer to a
+ color.
+
+ Use QColormap::pixel() instead.
+
+ \oldcode
+ QColor myColor;
+ uint pixel = myColor.pixel(screen);
+ \newcode
+ QColormap cmap = QColormap::instance(screen);
+ uint pixel = cmap.pixel(*this);
+ \endcode
+*/
+uint QColor::pixel(int screen) const
+{
+ QColormap cmap = QColormap::instance(screen);
+ return cmap.pixel(*this);
+}
+
+#endif // QT3_SUPPORT
+
+/*****************************************************************************
+ QColor stream functions
+ *****************************************************************************/
+
+#ifndef QT_NO_DEBUG_STREAM
+QDebug operator<<(QDebug dbg, const QColor &c)
+{
+#ifndef Q_BROKEN_DEBUG_STREAM
+ if (!c.isValid())
+ dbg.nospace() << "QColor(Invalid)";
+ else if (c.spec() == QColor::Rgb)
+ dbg.nospace() << "QColor(ARGB " << c.alphaF() << ", " << c.redF() << ", " << c.greenF() << ", " << c.blueF() << ")";
+ else if (c.spec() == QColor::Hsv)
+ dbg.nospace() << "QColor(AHSV " << c.alphaF() << ", " << c.hueF() << ", " << c.saturationF() << ", " << c.valueF() << ")";
+ else if (c.spec() == QColor::Cmyk)
+ dbg.nospace() << "QColor(ACMYK " << c.alphaF() << ", " << c.cyanF() << ", " << c.magentaF() << ", " << c.yellowF() << ", "
+ << c.blackF()<< ")";
+
+ return dbg.space();
+#else
+ qWarning("This compiler doesn't support streaming QColor to QDebug");
+ return dbg;
+ Q_UNUSED(c);
+#endif
+}
+#endif
+
+#ifndef QT_NO_DATASTREAM
+/*!
+ \fn QDataStream &operator<<(QDataStream &stream, const QColor &color)
+ \relates QColor
+
+ Writes the \a color to the \a stream.
+
+ \sa {Format of the QDataStream Operators}
+*/
+QDataStream &operator<<(QDataStream &stream, const QColor &color)
+{
+ if (stream.version() < 7) {
+ if (!color.isValid())
+ return stream << quint32(0x49000000);
+ quint32 p = (quint32)color.rgb();
+ if (stream.version() == 1) // Swap red and blue
+ p = ((p << 16) & 0xff0000) | ((p >> 16) & 0xff) | (p & 0xff00ff00);
+ return stream << p;
+ }
+
+ qint8 s = color.cspec;
+ quint16 a = color.ct.argb.alpha;
+ quint16 r = color.ct.argb.red;
+ quint16 g = color.ct.argb.green;
+ quint16 b = color.ct.argb.blue;
+ quint16 p = color.ct.argb.pad;
+
+ stream << s;
+ stream << a;
+ stream << r;
+ stream << g;
+ stream << b;
+ stream << p;
+
+ return stream;
+}
+
+/*!
+ \fn QDataStream &operator>>(QDataStream &stream, QColor &color)
+ \relates QColor
+
+ Reads the \a color from the \a stream.
+
+ \sa { Format of the QDataStream Operators}
+*/
+QDataStream &operator>>(QDataStream &stream, QColor &color)
+{
+ if (stream.version() < 7) {
+ quint32 p;
+ stream >> p;
+ if (p == 0x49000000) {
+ color.invalidate();
+ return stream;
+ }
+ if (stream.version() == 1) // Swap red and blue
+ p = ((p << 16) & 0xff0000) | ((p >> 16) & 0xff) | (p & 0xff00ff00);
+ color.setRgb(p);
+ return stream;
+ }
+
+ qint8 s;
+ quint16 a, r, g, b, p;
+ stream >> s;
+ stream >> a;
+ stream >> r;
+ stream >> g;
+ stream >> b;
+ stream >> p;
+
+ color.cspec = QColor::Spec(s);
+ color.ct.argb.alpha = a;
+ color.ct.argb.red = r;
+ color.ct.argb.green = g;
+ color.ct.argb.blue = b;
+ color.ct.argb.pad = p;
+
+ return stream;
+}
+#endif
+
+
+
+
+/*****************************************************************************
+ QColor global functions (documentation only)
+ *****************************************************************************/
+
+/*!
+ \fn int qRed(QRgb rgb)
+ \relates QColor
+
+ Returns the red component of the ARGB quadruplet \a rgb.
+
+ \sa qRgb(), QColor::red()
+*/
+
+/*!
+ \fn int qGreen(QRgb rgb)
+ \relates QColor
+
+ Returns the green component of the ARGB quadruplet \a rgb.
+
+ \sa qRgb(), QColor::green()
+*/
+
+/*!
+ \fn int qBlue(QRgb rgb)
+ \relates QColor
+
+ Returns the blue component of the ARGB quadruplet \a rgb.
+
+ \sa qRgb(), QColor::blue()
+*/
+
+/*!
+ \fn int qAlpha(QRgb rgba)
+ \relates QColor
+
+ Returns the alpha component of the ARGB quadruplet \a rgba.
+
+ \sa qRgb(), QColor::alpha()
+*/
+
+/*!
+ \fn QRgb qRgb(int r, int g, int b)
+ \relates QColor
+
+ Returns the ARGB quadruplet (255, \a{r}, \a{g}, \a{b}).
+
+ \sa qRgba(), qRed(), qGreen(), qBlue()
+*/
+
+/*!
+ \fn QRgb qRgba(int r, int g, int b, int a)
+ \relates QColor
+
+ Returns the ARGB quadruplet (\a{a}, \a{r}, \a{g}, \a{b}).
+
+ \sa qRgb(), qRed(), qGreen(), qBlue()
+*/
+
+/*!
+ \fn int qGray(int r, int g, int b)
+ \relates QColor
+
+ Returns a gray value (0 to 255) from the (\a r, \a g, \a b)
+ triplet.
+
+ The gray value is calculated using the formula (\a r * 11 + \a g * 16 +
+ \a b * 5)/32.
+*/
+
+/*!
+ \fn int qGray(QRgb rgb)
+ \overload
+ \relates QColor
+
+ Returns a gray value (0 to 255) from the given ARGB quadruplet \a rgb.
+
+ The gray value is calculated using the formula (R * 11 + G * 16 + B * 5)/32;
+ the alpha-channel is ignored.
+*/
+
+/*!
+ \fn QColor::QColor(int x, int y, int z, Spec colorSpec)
+
+ Use one of the other QColor constructors, or one of the static convenience
+ functions, instead.
+*/
+
+/*!
+ \fn QColor::rgb(int *r, int *g, int *b) const
+
+ Use getRgb() instead.
+*/
+
+/*!
+ \fn QColor::hsv(int *h, int *s, int *v) const
+
+ Use getHsv() instead.
+*/
+
+/*!
+ \fn QColor QColor::convertTo(Spec colorSpec) const
+
+ Creates a copy of \e this color in the format specified by \a colorSpec.
+
+ \sa spec(), toCmyk(), toHsv(), toRgb(), isValid()
+*/
+
+/*!
+ \typedef QRgb
+ \relates QColor
+
+ An ARGB quadruplet on the format #AARRGGBB, equivalent to an unsigned int.
+
+ The type also holds a value for the alpha-channel. The default alpha
+ channel is \c ff, i.e opaque. For more information, see the
+ \l{QColor#Alpha-Blended Drawing}{Alpha-Blended Drawing} section.
+
+ \sa QColor::rgb(), QColor::rgba()
+*/
+
+QT_END_NAMESPACE
diff --git a/src/gui/painting/qcolor.h b/src/gui/painting/qcolor.h
new file mode 100644
index 0000000000..f63f9c49c1
--- /dev/null
+++ b/src/gui/painting/qcolor.h
@@ -0,0 +1,275 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QCOLOR_H
+#define QCOLOR_H
+
+#include <QtGui/qrgb.h>
+#include <QtCore/qnamespace.h>
+#include <QtCore/qstringlist.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+class QColor;
+class QColormap;
+class QVariant;
+
+#ifndef QT_NO_DEBUG_STREAM
+Q_GUI_EXPORT QDebug operator<<(QDebug, const QColor &);
+#endif
+#ifndef QT_NO_DATASTREAM
+Q_GUI_EXPORT QDataStream &operator<<(QDataStream &, const QColor &);
+Q_GUI_EXPORT QDataStream &operator>>(QDataStream &, QColor &);
+#endif
+
+class Q_GUI_EXPORT QColor
+{
+public:
+ enum Spec { Invalid, Rgb, Hsv, Cmyk };
+
+ QColor();
+ QColor(Qt::GlobalColor color);
+ QColor(int r, int g, int b, int a = 255);
+ QColor(QRgb rgb);
+ QColor(const QString& name);
+ QColor(const char *name);
+ QColor(const QColor &color);
+ QColor(Spec spec);
+
+ bool isValid() const;
+
+ QString name() const;
+ void setNamedColor(const QString& name);
+
+ static QStringList colorNames();
+
+ inline Spec spec() const
+ { return cspec; }
+
+ int alpha() const;
+ void setAlpha(int alpha);
+
+ qreal alphaF() const;
+ void setAlphaF(qreal alpha);
+
+ int red() const;
+ int green() const;
+ int blue() const;
+ void setRed(int red);
+ void setGreen(int green);
+ void setBlue(int blue);
+
+ qreal redF() const;
+ qreal greenF() const;
+ qreal blueF() const;
+ void setRedF(qreal red);
+ void setGreenF(qreal green);
+ void setBlueF(qreal blue);
+
+ void getRgb(int *r, int *g, int *b, int *a = 0) const;
+ void setRgb(int r, int g, int b, int a = 255);
+
+ void getRgbF(qreal *r, qreal *g, qreal *b, qreal *a = 0) const;
+ void setRgbF(qreal r, qreal g, qreal b, qreal a = 1.0);
+
+ QRgb rgba() const;
+ void setRgba(QRgb rgba);
+
+ QRgb rgb() const;
+ void setRgb(QRgb rgb);
+
+ int hue() const; // 0 <= hue < 360
+ int saturation() const;
+ int value() const;
+
+ qreal hueF() const; // 0.0 <= hueF < 360.0
+ qreal saturationF() const;
+ qreal valueF() const;
+
+ void getHsv(int *h, int *s, int *v, int *a = 0) const;
+ void setHsv(int h, int s, int v, int a = 255);
+
+ void getHsvF(qreal *h, qreal *s, qreal *v, qreal *a = 0) const;
+ void setHsvF(qreal h, qreal s, qreal v, qreal a = 1.0);
+
+ int cyan() const;
+ int magenta() const;
+ int yellow() const;
+ int black() const;
+
+ qreal cyanF() const;
+ qreal magentaF() const;
+ qreal yellowF() const;
+ qreal blackF() const;
+
+ void getCmyk(int *c, int *m, int *y, int *k, int *a = 0);
+ void setCmyk(int c, int m, int y, int k, int a = 255);
+
+ void getCmykF(qreal *c, qreal *m, qreal *y, qreal *k, qreal *a = 0);
+ void setCmykF(qreal c, qreal m, qreal y, qreal k, qreal a = 1.0);
+
+ QColor toRgb() const;
+ QColor toHsv() const;
+ QColor toCmyk() const;
+
+ QColor convertTo(Spec colorSpec) const;
+
+ static QColor fromRgb(QRgb rgb);
+ static QColor fromRgba(QRgb rgba);
+
+ static QColor fromRgb(int r, int g, int b, int a = 255);
+ static QColor fromRgbF(qreal r, qreal g, qreal b, qreal a = 1.0);
+
+ static QColor fromHsv(int h, int s, int v, int a = 255);
+ static QColor fromHsvF(qreal h, qreal s, qreal v, qreal a = 1.0);
+
+ static QColor fromCmyk(int c, int m, int y, int k, int a = 255);
+ static QColor fromCmykF(qreal c, qreal m, qreal y, qreal k, qreal a = 1.0);
+
+ QColor light(int f = 150) const;
+ QColor lighter(int f = 150) const;
+ QColor dark(int f = 200) const;
+ QColor darker(int f = 200) const;
+
+ QColor &operator=(const QColor &);
+ QColor &operator=(Qt::GlobalColor color);
+
+ bool operator==(const QColor &c) const;
+ bool operator!=(const QColor &c) const;
+
+ operator QVariant() const;
+
+#ifdef Q_WS_X11
+ static bool allowX11ColorNames();
+ static void setAllowX11ColorNames(bool enabled);
+#endif
+
+#ifdef QT3_SUPPORT
+ inline QT3_SUPPORT_CONSTRUCTOR QColor(int x, int y, int z, Spec colorSpec)
+ { if (colorSpec == Hsv) setHsv(x, y, z); else setRgb(x, y, z); }
+
+ inline QT3_SUPPORT void rgb(int *r, int *g, int *b) const
+ { getRgb(r, g, b); }
+ inline QT3_SUPPORT void hsv(int *h, int *s, int *v) const
+ { getHsv(h, s, v); }
+
+ inline QT3_SUPPORT void setRgba(int r, int g, int b, int a)
+ { setRgb(r, g, b, a); }
+ inline QT3_SUPPORT void getRgba(int *r, int *g, int *b, int *a) const
+ { getRgb(r, g, b, a); }
+
+ QT3_SUPPORT uint pixel(int screen = -1) const;
+#endif
+
+private:
+#ifndef QT3_SUPPORT
+ // do not allow a spec to be used as an alpha value
+ QColor(int, int, int, Spec);
+#endif
+
+ void invalidate();
+
+ Spec cspec;
+ union {
+ struct {
+ ushort alpha;
+ ushort red;
+ ushort green;
+ ushort blue;
+ ushort pad;
+ } argb;
+ struct {
+ ushort alpha;
+ ushort hue;
+ ushort saturation;
+ ushort value;
+ ushort pad;
+ } ahsv;
+ struct {
+ ushort alpha;
+ ushort cyan;
+ ushort magenta;
+ ushort yellow;
+ ushort black;
+ } acmyk;
+ } ct;
+
+ friend class QColormap;
+#ifndef QT_NO_DATASTREAM
+ friend Q_GUI_EXPORT QDataStream &operator<<(QDataStream &, const QColor &);
+ friend Q_GUI_EXPORT QDataStream &operator>>(QDataStream &, QColor &);
+#endif
+};
+
+inline QColor::QColor()
+{ invalidate(); }
+
+inline QColor::QColor(int r, int g, int b, int a)
+{ setRgb(r, g, b, a); }
+
+inline QColor::QColor(const char *aname)
+{ setNamedColor(QLatin1String(aname)); }
+
+inline QColor::QColor(const QString& aname)
+{ setNamedColor(aname); }
+
+inline QColor::QColor(const QColor &acolor)
+ : cspec(acolor.cspec)
+{ ct.argb = acolor.ct.argb; }
+
+inline bool QColor::isValid() const
+{ return cspec != Invalid; }
+
+inline QColor QColor::lighter(int f) const
+{ return light(f); }
+
+inline QColor QColor::darker(int f) const
+{ return dark(f); }
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QCOLOR_H
diff --git a/src/gui/painting/qcolor_p.cpp b/src/gui/painting/qcolor_p.cpp
new file mode 100644
index 0000000000..5bdbee464c
--- /dev/null
+++ b/src/gui/painting/qcolor_p.cpp
@@ -0,0 +1,390 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@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 "qrgb.h"
+#include "qstringlist.h"
+
+#if defined(Q_OS_WINCE)
+#include "qguifunctions_wince.h"
+#endif
+QT_BEGIN_NAMESPACE
+
+static inline int h2i(char hex)
+{
+ if (hex >= '0' && hex <= '9')
+ return hex - '0';
+ if (hex >= 'a' && hex <= 'f')
+ return hex - 'a' + 10;
+ if (hex >= 'A' && hex <= 'F')
+ return hex - 'A' + 10;
+ return -1;
+}
+
+static inline int hex2int(const char *s)
+{
+ return (h2i(s[0]) << 4) | h2i(s[1]);
+}
+
+static inline int hex2int(char s)
+{
+ int h = h2i(s);
+ return (h << 4) | h;
+}
+
+bool qt_get_hex_rgb(const char *name, QRgb *rgb)
+{
+ if(name[0] != '#')
+ return false;
+ name++;
+ int len = qstrlen(name);
+ int r, g, b;
+ if (len == 12) {
+ r = hex2int(name);
+ g = hex2int(name + 4);
+ b = hex2int(name + 8);
+ } else if (len == 9) {
+ r = hex2int(name);
+ g = hex2int(name + 3);
+ b = hex2int(name + 6);
+ } else if (len == 6) {
+ r = hex2int(name);
+ g = hex2int(name + 2);
+ b = hex2int(name + 4);
+ } else if (len == 3) {
+ r = hex2int(name[0]);
+ g = hex2int(name[1]);
+ b = hex2int(name[2]);
+ } else {
+ r = g = b = -1;
+ }
+ if ((uint)r > 255 || (uint)g > 255 || (uint)b > 255) {
+ *rgb = 0;
+ return false;
+ }
+ *rgb = qRgb(r, g ,b);
+ return true;
+}
+
+bool qt_get_hex_rgb(const QChar *str, int len, QRgb *rgb)
+{
+ if (len > 13)
+ return false;
+ char tmp[16];
+ for(int i = 0; i < len; ++i)
+ tmp[i] = str[i].toLatin1();
+ tmp[len] = 0;
+ return qt_get_hex_rgb(tmp, rgb);
+}
+
+#ifndef QT_NO_COLORNAMES
+
+/*
+ CSS color names = SVG 1.0 color names + transparent (rgba(0,0,0,0))
+*/
+
+#ifdef rgb
+# undef rgb
+#endif
+#define rgb(r,g,b) (0xff000000 | (r << 16) | (g << 8) | b)
+
+static const struct RGBData {
+ const char *name;
+ uint value;
+} rgbTbl[] = {
+ { "aliceblue", rgb(240, 248, 255) },
+ { "antiquewhite", rgb(250, 235, 215) },
+ { "aqua", rgb( 0, 255, 255) },
+ { "aquamarine", rgb(127, 255, 212) },
+ { "azure", rgb(240, 255, 255) },
+ { "beige", rgb(245, 245, 220) },
+ { "bisque", rgb(255, 228, 196) },
+ { "black", rgb( 0, 0, 0) },
+ { "blanchedalmond", rgb(255, 235, 205) },
+ { "blue", rgb( 0, 0, 255) },
+ { "blueviolet", rgb(138, 43, 226) },
+ { "brown", rgb(165, 42, 42) },
+ { "burlywood", rgb(222, 184, 135) },
+ { "cadetblue", rgb( 95, 158, 160) },
+ { "chartreuse", rgb(127, 255, 0) },
+ { "chocolate", rgb(210, 105, 30) },
+ { "coral", rgb(255, 127, 80) },
+ { "cornflowerblue", rgb(100, 149, 237) },
+ { "cornsilk", rgb(255, 248, 220) },
+ { "crimson", rgb(220, 20, 60) },
+ { "cyan", rgb( 0, 255, 255) },
+ { "darkblue", rgb( 0, 0, 139) },
+ { "darkcyan", rgb( 0, 139, 139) },
+ { "darkgoldenrod", rgb(184, 134, 11) },
+ { "darkgray", rgb(169, 169, 169) },
+ { "darkgreen", rgb( 0, 100, 0) },
+ { "darkgrey", rgb(169, 169, 169) },
+ { "darkkhaki", rgb(189, 183, 107) },
+ { "darkmagenta", rgb(139, 0, 139) },
+ { "darkolivegreen", rgb( 85, 107, 47) },
+ { "darkorange", rgb(255, 140, 0) },
+ { "darkorchid", rgb(153, 50, 204) },
+ { "darkred", rgb(139, 0, 0) },
+ { "darksalmon", rgb(233, 150, 122) },
+ { "darkseagreen", rgb(143, 188, 143) },
+ { "darkslateblue", rgb( 72, 61, 139) },
+ { "darkslategray", rgb( 47, 79, 79) },
+ { "darkslategrey", rgb( 47, 79, 79) },
+ { "darkturquoise", rgb( 0, 206, 209) },
+ { "darkviolet", rgb(148, 0, 211) },
+ { "deeppink", rgb(255, 20, 147) },
+ { "deepskyblue", rgb( 0, 191, 255) },
+ { "dimgray", rgb(105, 105, 105) },
+ { "dimgrey", rgb(105, 105, 105) },
+ { "dodgerblue", rgb( 30, 144, 255) },
+ { "firebrick", rgb(178, 34, 34) },
+ { "floralwhite", rgb(255, 250, 240) },
+ { "forestgreen", rgb( 34, 139, 34) },
+ { "fuchsia", rgb(255, 0, 255) },
+ { "gainsboro", rgb(220, 220, 220) },
+ { "ghostwhite", rgb(248, 248, 255) },
+ { "gold", rgb(255, 215, 0) },
+ { "goldenrod", rgb(218, 165, 32) },
+ { "gray", rgb(128, 128, 128) },
+ { "green", rgb( 0, 128, 0) },
+ { "greenyellow", rgb(173, 255, 47) },
+ { "grey", rgb(128, 128, 128) },
+ { "honeydew", rgb(240, 255, 240) },
+ { "hotpink", rgb(255, 105, 180) },
+ { "indianred", rgb(205, 92, 92) },
+ { "indigo", rgb( 75, 0, 130) },
+ { "ivory", rgb(255, 255, 240) },
+ { "khaki", rgb(240, 230, 140) },
+ { "lavender", rgb(230, 230, 250) },
+ { "lavenderblush", rgb(255, 240, 245) },
+ { "lawngreen", rgb(124, 252, 0) },
+ { "lemonchiffon", rgb(255, 250, 205) },
+ { "lightblue", rgb(173, 216, 230) },
+ { "lightcoral", rgb(240, 128, 128) },
+ { "lightcyan", rgb(224, 255, 255) },
+ { "lightgoldenrodyellow", rgb(250, 250, 210) },
+ { "lightgray", rgb(211, 211, 211) },
+ { "lightgreen", rgb(144, 238, 144) },
+ { "lightgrey", rgb(211, 211, 211) },
+ { "lightpink", rgb(255, 182, 193) },
+ { "lightsalmon", rgb(255, 160, 122) },
+ { "lightseagreen", rgb( 32, 178, 170) },
+ { "lightskyblue", rgb(135, 206, 250) },
+ { "lightslategray", rgb(119, 136, 153) },
+ { "lightslategrey", rgb(119, 136, 153) },
+ { "lightsteelblue", rgb(176, 196, 222) },
+ { "lightyellow", rgb(255, 255, 224) },
+ { "lime", rgb( 0, 255, 0) },
+ { "limegreen", rgb( 50, 205, 50) },
+ { "linen", rgb(250, 240, 230) },
+ { "magenta", rgb(255, 0, 255) },
+ { "maroon", rgb(128, 0, 0) },
+ { "mediumaquamarine", rgb(102, 205, 170) },
+ { "mediumblue", rgb( 0, 0, 205) },
+ { "mediumorchid", rgb(186, 85, 211) },
+ { "mediumpurple", rgb(147, 112, 219) },
+ { "mediumseagreen", rgb( 60, 179, 113) },
+ { "mediumslateblue", rgb(123, 104, 238) },
+ { "mediumspringgreen", rgb( 0, 250, 154) },
+ { "mediumturquoise", rgb( 72, 209, 204) },
+ { "mediumvioletred", rgb(199, 21, 133) },
+ { "midnightblue", rgb( 25, 25, 112) },
+ { "mintcream", rgb(245, 255, 250) },
+ { "mistyrose", rgb(255, 228, 225) },
+ { "moccasin", rgb(255, 228, 181) },
+ { "navajowhite", rgb(255, 222, 173) },
+ { "navy", rgb( 0, 0, 128) },
+ { "oldlace", rgb(253, 245, 230) },
+ { "olive", rgb(128, 128, 0) },
+ { "olivedrab", rgb(107, 142, 35) },
+ { "orange", rgb(255, 165, 0) },
+ { "orangered", rgb(255, 69, 0) },
+ { "orchid", rgb(218, 112, 214) },
+ { "palegoldenrod", rgb(238, 232, 170) },
+ { "palegreen", rgb(152, 251, 152) },
+ { "paleturquoise", rgb(175, 238, 238) },
+ { "palevioletred", rgb(219, 112, 147) },
+ { "papayawhip", rgb(255, 239, 213) },
+ { "peachpuff", rgb(255, 218, 185) },
+ { "peru", rgb(205, 133, 63) },
+ { "pink", rgb(255, 192, 203) },
+ { "plum", rgb(221, 160, 221) },
+ { "powderblue", rgb(176, 224, 230) },
+ { "purple", rgb(128, 0, 128) },
+ { "red", rgb(255, 0, 0) },
+ { "rosybrown", rgb(188, 143, 143) },
+ { "royalblue", rgb( 65, 105, 225) },
+ { "saddlebrown", rgb(139, 69, 19) },
+ { "salmon", rgb(250, 128, 114) },
+ { "sandybrown", rgb(244, 164, 96) },
+ { "seagreen", rgb( 46, 139, 87) },
+ { "seashell", rgb(255, 245, 238) },
+ { "sienna", rgb(160, 82, 45) },
+ { "silver", rgb(192, 192, 192) },
+ { "skyblue", rgb(135, 206, 235) },
+ { "slateblue", rgb(106, 90, 205) },
+ { "slategray", rgb(112, 128, 144) },
+ { "slategrey", rgb(112, 128, 144) },
+ { "snow", rgb(255, 250, 250) },
+ { "springgreen", rgb( 0, 255, 127) },
+ { "steelblue", rgb( 70, 130, 180) },
+ { "tan", rgb(210, 180, 140) },
+ { "teal", rgb( 0, 128, 128) },
+ { "thistle", rgb(216, 191, 216) },
+ { "tomato", rgb(255, 99, 71) },
+ { "transparent", 0 },
+ { "turquoise", rgb( 64, 224, 208) },
+ { "violet", rgb(238, 130, 238) },
+ { "wheat", rgb(245, 222, 179) },
+ { "white", rgb(255, 255, 255) },
+ { "whitesmoke", rgb(245, 245, 245) },
+ { "yellow", rgb(255, 255, 0) },
+ { "yellowgreen", rgb(154, 205, 50) }
+};
+
+static const int rgbTblSize = sizeof(rgbTbl) / sizeof(RGBData);
+
+#undef rgb
+
+QT_BEGIN_INCLUDE_NAMESPACE
+#include <stdlib.h>
+QT_END_INCLUDE_NAMESPACE
+
+#if defined(Q_C_CALLBACKS)
+extern "C" {
+#endif
+
+#ifdef Q_OS_WINCE
+static int __cdecl rgb_cmp(const void *d1, const void *d2)
+#else
+static int rgb_cmp(const void *d1, const void *d2)
+#endif
+{
+ return qstricmp(((RGBData *)d1)->name, ((RGBData *)d2)->name);
+}
+
+#if defined(Q_C_CALLBACKS)
+}
+#endif
+
+static bool get_named_rgb(const char *name, QRgb *rgb)
+{
+ RGBData x;
+ x.name = name;
+ RGBData *r = (RGBData*)bsearch(&x, rgbTbl, rgbTblSize, sizeof(RGBData), rgb_cmp);
+ if (r) {
+ *rgb = r->value;
+ return true;
+ }
+ return false;
+}
+
+bool qt_get_named_rgb(const char *name, QRgb* rgb)
+{
+ int len = int(strlen(name));
+ if(len > 255)
+ return false;
+ char name_no_space[256];
+ int pos = 0;
+ for(int i = 0; i < len; i++) {
+ if(name[i] != '\t' && name[i] != ' ')
+ name_no_space[pos++] = name[i];
+ }
+ name_no_space[pos] = 0;
+
+ return get_named_rgb(name_no_space, rgb);
+}
+
+bool qt_get_named_rgb(const QChar *name, int len, QRgb *rgb)
+{
+ if(len > 255)
+ return false;
+ char name_no_space[256];
+ int pos = 0;
+ for(int i = 0; i < len; i++) {
+ if(name[i] != QLatin1Char('\t') && name[i] != QLatin1Char(' '))
+ name_no_space[pos++] = name[i].toLatin1();
+ }
+ name_no_space[pos] = 0;
+ return get_named_rgb(name_no_space, rgb);
+}
+
+
+uint qt_get_rgb_val(const char *name)
+{
+ QRgb r = 0;
+ qt_get_named_rgb(name,&r);
+ return r;
+}
+
+QStringList qt_get_colornames()
+{
+ int i = 0;
+ QStringList lst;
+ for (i = 0; i < rgbTblSize; i++)
+ lst << QLatin1String(rgbTbl[i].name);
+ return lst;
+}
+
+#else
+
+bool qt_get_named_rgb(const char *, QRgb*)
+{
+ return false;
+}
+
+uint qt_get_rgb_val(const char *)
+{
+ return 0;
+}
+QStringList qt_get_colornames()
+{
+ return QStringList();
+}
+#endif // QT_NO_COLORNAMES
+
+QT_END_NAMESPACE
diff --git a/src/gui/painting/qcolor_p.h b/src/gui/painting/qcolor_p.h
new file mode 100644
index 0000000000..5286701d48
--- /dev/null
+++ b/src/gui/painting/qcolor_p.h
@@ -0,0 +1,71 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QCOLOR_P_H
+#define QCOLOR_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 "QtCore/qglobal.h"
+#include "QtGui/qrgb.h"
+#include "QtCore/qstringlist.h"
+
+QT_BEGIN_NAMESPACE
+
+uint qt_get_rgb_val(const char *name);
+bool qt_get_named_rgb(const char *, QRgb*);
+bool qt_get_named_rgb(const QChar *, int len, QRgb*);
+bool qt_get_hex_rgb(const char *, QRgb *);
+bool qt_get_hex_rgb(const QChar *, int len, QRgb *);
+QStringList qt_get_colornames();
+
+QT_END_NAMESPACE
+
+#endif // QCOLOR_P_H
diff --git a/src/gui/painting/qcolormap.h b/src/gui/painting/qcolormap.h
new file mode 100644
index 0000000000..8d8f9642a5
--- /dev/null
+++ b/src/gui/painting/qcolormap.h
@@ -0,0 +1,97 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QCOLORMAP_H
+#define QCOLORMAP_H
+
+#include <QtCore/qatomic.h>
+#include <QtGui/qrgb.h>
+#include <QtCore/qvector.h>
+#include <QtGui/qwindowdefs.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+class QColor;
+class QColormapPrivate;
+
+class Q_GUI_EXPORT QColormap
+{
+public:
+ enum Mode { Direct, Indexed, Gray };
+
+ static void initialize();
+ static void cleanup();
+
+ static QColormap instance(int screen = -1);
+
+ QColormap(const QColormap &colormap);
+ ~QColormap();
+
+ QColormap &operator=(const QColormap &colormap);
+
+ Mode mode() const;
+
+ int depth() const;
+ int size() const;
+
+ uint pixel(const QColor &color) const;
+ const QColor colorAt(uint pixel) const;
+
+ const QVector<QColor> colormap() const;
+
+#ifdef Q_WS_WIN
+ static HPALETTE hPal();
+#endif
+
+private:
+ QColormap();
+ QColormapPrivate *d;
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QCOLORMAP_H
diff --git a/src/gui/painting/qcolormap_mac.cpp b/src/gui/painting/qcolormap_mac.cpp
new file mode 100644
index 0000000000..96da90f7ab
--- /dev/null
+++ b/src/gui/painting/qcolormap_mac.cpp
@@ -0,0 +1,111 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qcolormap.h"
+#include "qcolor.h"
+
+QT_BEGIN_NAMESPACE
+
+class QColormapPrivate
+{
+public:
+ inline QColormapPrivate()
+ : ref(1)
+ { }
+
+ QAtomicInt ref;
+};
+static QColormap *qt_mac_global_map = 0;
+
+void QColormap::initialize()
+{
+ qt_mac_global_map = new QColormap;
+}
+
+void QColormap::cleanup()
+{
+ delete qt_mac_global_map;
+ qt_mac_global_map = 0;
+}
+
+QColormap QColormap::instance(int)
+{
+ return *qt_mac_global_map;
+}
+
+QColormap::QColormap() : d(new QColormapPrivate)
+{}
+
+QColormap::QColormap(const QColormap &colormap) :d (colormap.d)
+{ d->ref.ref(); }
+
+QColormap::~QColormap()
+{
+ if (!d->ref.deref())
+ delete d;
+}
+
+QColormap::Mode QColormap::mode() const
+{ return QColormap::Direct; }
+
+int QColormap::depth() const
+{
+ return 32;
+}
+
+int QColormap::size() const
+{
+ return -1;
+}
+
+uint QColormap::pixel(const QColor &color) const
+{ return color.rgba(); }
+
+const QColor QColormap::colorAt(uint pixel) const
+{ return QColor(pixel); }
+
+const QVector<QColor> QColormap::colormap() const
+{ return QVector<QColor>(); }
+
+QColormap &QColormap::operator=(const QColormap &colormap)
+{ qAtomicAssign(d, colormap.d); return *this; }
+
+QT_END_NAMESPACE
diff --git a/src/gui/painting/qcolormap_qws.cpp b/src/gui/painting/qcolormap_qws.cpp
new file mode 100644
index 0000000000..a8bdebb428
--- /dev/null
+++ b/src/gui/painting/qcolormap_qws.cpp
@@ -0,0 +1,185 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qcolormap.h"
+#include "qcolor.h"
+#include "qpaintdevice.h"
+#include "qscreen_qws.h"
+#include "qwsdisplay_qws.h"
+
+QT_BEGIN_NAMESPACE
+
+class QColormapPrivate
+{
+public:
+ inline QColormapPrivate()
+ : ref(1), mode(QColormap::Direct), depth(0), numcolors(0)
+ { }
+
+ QAtomicInt ref;
+
+ QColormap::Mode mode;
+ int depth;
+ int numcolors;
+};
+
+static QColormapPrivate *screenMap = 0;
+
+void QColormap::initialize()
+{
+ screenMap = new QColormapPrivate;
+
+ screenMap->depth = QPaintDevice::qwsDisplay()->depth();
+ if (screenMap->depth < 8) {
+ screenMap->mode = QColormap::Indexed;
+ screenMap->numcolors = 256;
+ } else {
+ screenMap->mode = QColormap::Direct;
+ screenMap->numcolors = -1;
+ }
+}
+
+void QColormap::cleanup()
+{
+ delete screenMap;
+ screenMap = 0;
+}
+
+QColormap QColormap::instance(int /*screen*/)
+{
+ return QColormap();
+}
+
+QColormap::QColormap()
+ : d(screenMap)
+{ d->ref.ref(); }
+
+QColormap::QColormap(const QColormap &colormap)
+ :d (colormap.d)
+{ d->ref.ref(); }
+
+QColormap::~QColormap()
+{
+ if (!d->ref.deref())
+ delete d;
+}
+
+QColormap::Mode QColormap::mode() const
+{ return d->mode; }
+
+
+int QColormap::depth() const
+{ return d->depth; }
+
+
+int QColormap::size() const
+{
+ return d->numcolors;
+}
+
+uint QColormap::pixel(const QColor &color) const
+{
+ QRgb rgb = color.rgba();
+ if (d->mode == QColormap::Direct) {
+ switch(d->depth) {
+ case 16:
+ return qt_convRgbTo16(rgb);
+ case 24:
+ case 32:
+ {
+ const int r = qRed(rgb);
+ const int g = qGreen(rgb);
+ const int b = qBlue(rgb);
+ const int red_shift = 16;
+ const int green_shift = 8;
+ const int red_mask = 0xff0000;
+ const int green_mask = 0x00ff00;
+ const int blue_mask = 0x0000ff;
+ const int tg = g << green_shift;
+#ifdef QT_QWS_DEPTH_32_BGR
+ if (qt_screen->pixelType() == QScreen::BGRPixel) {
+ const int tb = b << red_shift;
+ return 0xff000000 | (r & blue_mask) | (tg & green_mask) | (tb & red_mask);
+ }
+#endif
+ const int tr = r << red_shift;
+ return 0xff000000 | (b & blue_mask) | (tg & green_mask) | (tr & red_mask);
+ }
+ }
+ }
+ return qt_screen->alloc(qRed(rgb), qGreen(rgb), qBlue(rgb));
+}
+
+const QColor QColormap::colorAt(uint pixel) const
+{
+ if (d->mode == Direct) {
+ if (d->depth == 16) {
+ pixel = qt_conv16ToRgb(pixel);
+ }
+ const int red_shift = 16;
+ const int green_shift = 8;
+ const int red_mask = 0xff0000;
+ const int green_mask = 0x00ff00;
+ const int blue_mask = 0x0000ff;
+#ifdef QT_QWS_DEPTH_32_BGR
+ if (qt_screen->pixelType() == QScreen::BGRPixel) {
+ return QColor((pixel & blue_mask),
+ (pixel & green_mask) >> green_shift,
+ (pixel & red_mask) >> red_shift);
+ }
+#endif
+ return QColor((pixel & red_mask) >> red_shift,
+ (pixel & green_mask) >> green_shift,
+ (pixel & blue_mask));
+ }
+ Q_ASSERT_X(int(pixel) < qt_screen->numCols(), "QColormap::colorAt", "pixel out of bounds of palette");
+ return QColor(qt_screen->clut()[pixel]);
+}
+
+const QVector<QColor> QColormap::colormap() const
+{
+ return QVector<QColor>();
+}
+
+QColormap &QColormap::operator=(const QColormap &colormap)
+{ qAtomicAssign(d, colormap.d); return *this; }
+
+QT_END_NAMESPACE
diff --git a/src/gui/painting/qcolormap_win.cpp b/src/gui/painting/qcolormap_win.cpp
new file mode 100644
index 0000000000..d61b933589
--- /dev/null
+++ b/src/gui/painting/qcolormap_win.cpp
@@ -0,0 +1,197 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qcolor.h"
+#include "qcolormap.h"
+#include "qvector.h"
+#include "qt_windows.h"
+
+#if defined(Q_OS_WINCE)
+#include "qguifunctions_wince.h"
+#endif
+
+QT_BEGIN_NAMESPACE
+
+class QColormapPrivate
+{
+public:
+ inline QColormapPrivate()
+ : ref(1), mode(QColormap::Direct), depth(0), hpal(0)
+ { }
+
+ QAtomicInt ref;
+
+ QColormap::Mode mode;
+ int depth;
+ int numcolors;
+
+ HPALETTE hpal;
+ QVector<QColor> palette;
+};
+
+static QColormapPrivate *screenMap = 0;
+
+void QColormap::initialize()
+{
+ HDC dc = qt_win_display_dc();
+
+ screenMap = new QColormapPrivate;
+ screenMap->depth = GetDeviceCaps(dc, BITSPIXEL);
+
+ screenMap->numcolors = -1;
+ if (GetDeviceCaps(dc, RASTERCAPS) & RC_PALETTE)
+ screenMap->numcolors = GetDeviceCaps(dc, SIZEPALETTE);
+
+ if (screenMap->numcolors <= 16 || screenMap->numcolors > 256) // no need to create palette
+ return;
+
+ LOGPALETTE* pal = 0;
+ int numPalEntries = 6*6*6; // color cube
+
+ pal = (LOGPALETTE*)malloc(sizeof(LOGPALETTE) + numPalEntries * sizeof(PALETTEENTRY));
+ // Make 6x6x6 color cube
+ int idx = 0;
+ for(int ir = 0x0; ir <= 0xff; ir+=0x33) {
+ for(int ig = 0x0; ig <= 0xff; ig+=0x33) {
+ for(int ib = 0x0; ib <= 0xff; ib+=0x33) {
+ pal->palPalEntry[idx].peRed = ir;
+ pal->palPalEntry[idx].peGreen = ig;
+ pal->palPalEntry[idx].peBlue = ib;
+ pal->palPalEntry[idx].peFlags = 0;
+ idx++;
+ }
+ }
+ }
+
+ pal->palVersion = 0x300;
+ pal->palNumEntries = numPalEntries;
+
+ screenMap->hpal = CreatePalette(pal);
+ if (!screenMap->hpal)
+ qErrnoWarning("QColor::initialize: Failed to create logical palette");
+ free (pal);
+
+ SelectPalette(dc, screenMap->hpal, FALSE);
+ RealizePalette(dc);
+
+ PALETTEENTRY paletteEntries[256];
+ screenMap->numcolors = GetPaletteEntries(screenMap->hpal, 0, 255, paletteEntries);
+
+ screenMap->palette.resize(screenMap->numcolors);
+ for (int i = 0; i < screenMap->numcolors; i++) {
+ screenMap->palette[i] = qRgb(paletteEntries[i].peRed,
+ paletteEntries[i].peGreen,
+ paletteEntries[i].peBlue);
+ }
+}
+
+void QColormap::cleanup()
+{
+ if (!screenMap)
+ return;
+
+ if (screenMap->hpal) { // delete application global
+ DeleteObject(screenMap->hpal); // palette
+ screenMap->hpal = 0;
+ }
+
+ delete screenMap;
+ screenMap = 0;
+}
+
+QColormap QColormap::instance(int)
+{ return QColormap(); }
+
+QColormap::QColormap()
+ : d(screenMap)
+{ d->ref.ref(); }
+
+QColormap::QColormap(const QColormap &colormap)
+ :d (colormap.d)
+{ d->ref.ref(); }
+
+QColormap::~QColormap()
+{
+ if (!d->ref.deref())
+ delete d;
+}
+
+QColormap::Mode QColormap::mode() const
+{ return d->mode; }
+
+int QColormap::depth() const
+{ return d->depth; }
+
+int QColormap::size() const
+{ return d->numcolors; }
+
+uint QColormap::pixel(const QColor &color) const
+{
+ const QColor c = color.toRgb();
+ COLORREF rgb = RGB(c.red(), c.green(), c.blue());
+ if (d->hpal)
+ return PALETTEINDEX(GetNearestPaletteIndex(d->hpal, rgb));
+ return rgb;
+}
+
+const QColor QColormap::colorAt(uint pixel) const
+{
+ if (d->hpal) {
+ if (pixel < uint(d->numcolors))
+ return d->palette.at(pixel);
+ return QColor();
+ }
+ return QColor(GetRValue(pixel), GetGValue(pixel), GetBValue(pixel));
+}
+
+
+HPALETTE QColormap::hPal()
+{ return screenMap ? screenMap->hpal : 0; }
+
+
+const QVector<QColor> QColormap::colormap() const
+{ return d->palette; }
+
+QColormap &QColormap::operator=(const QColormap &colormap)
+{ qAtomicAssign(d, colormap.d); return *this; }
+
+
+QT_END_NAMESPACE
diff --git a/src/gui/painting/qcolormap_x11.cpp b/src/gui/painting/qcolormap_x11.cpp
new file mode 100644
index 0000000000..ccf6955100
--- /dev/null
+++ b/src/gui/painting/qcolormap_x11.cpp
@@ -0,0 +1,674 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qcolormap.h"
+
+#include "qapplication.h"
+#include "qdebug.h"
+#include "qdesktopwidget.h"
+#include "qvarlengtharray.h"
+
+#include "qx11info_x11.h"
+#include <private/qt_x11_p.h>
+#include <limits.h>
+
+QT_BEGIN_NAMESPACE
+
+class QColormapPrivate
+{
+public:
+ QColormapPrivate()
+ : ref(1), mode(QColormap::Direct), depth(0),
+ colormap(0), defaultColormap(true),
+ visual(0), defaultVisual(true),
+ r_max(0), g_max(0), b_max(0),
+ r_shift(0), g_shift(0), b_shift(0)
+ {}
+
+ QAtomicInt ref;
+
+ QColormap::Mode mode;
+ int depth;
+
+ Colormap colormap;
+ bool defaultColormap;
+
+ Visual *visual;
+ bool defaultVisual;
+
+ int r_max;
+ int g_max;
+ int b_max;
+
+ uint r_shift;
+ uint g_shift;
+ uint b_shift;
+
+ QVector<QColor> colors;
+ QVector<int> pixels;
+};
+
+
+static uint right_align(uint v)
+{
+ while (!(v & 0x1))
+ v >>= 1;
+ return v;
+}
+
+static int lowest_bit(uint v)
+{
+ int i;
+ uint b = 1u;
+ for (i = 0; ((v & b) == 0u) && i < 32; ++i)
+ b <<= 1u;
+ return i == 32 ? -1 : i;
+}
+
+static int cube_root(int v)
+{
+ if (v == 1)
+ return 1;
+ // brute force algorithm
+ int i = 1;
+ for (;;) {
+ const int b = i * i * i;
+ if (b <= v) {
+ ++i;
+ } else {
+ --i;
+ break;
+ }
+ }
+ return i;
+}
+
+static Visual *find_visual(Display *display,
+ int screen,
+ int visual_class,
+ int visual_id,
+ int *depth,
+ bool *defaultVisual)
+{
+ XVisualInfo *vi, rvi;
+ int count;
+
+ uint mask = VisualScreenMask;
+ rvi.screen = screen;
+
+ if (visual_class != -1) {
+ rvi.c_class = visual_class;
+ mask |= VisualClassMask;
+ }
+ if (visual_id != -1) {
+ rvi.visualid = visual_id;
+ mask |= VisualIDMask;
+ }
+
+ Visual *visual = DefaultVisual(display, screen);
+ *defaultVisual = true;
+ *depth = DefaultDepth(display, screen);
+
+ vi = XGetVisualInfo(display, mask, &rvi, &count);
+ if (vi) {
+ int best = 0;
+ for (int x = 0; x < count; ++x) {
+ if (vi[x].depth > vi[best].depth)
+ best = x;
+ }
+ if (best >= 0 && best <= count && vi[best].visualid != XVisualIDFromVisual(visual)) {
+ visual = vi[best].visual;
+ *defaultVisual = (visual == DefaultVisual(display, screen));
+ *depth = vi[best].depth;
+ }
+ }
+ if (vi)
+ XFree((char *)vi);
+ return visual;
+}
+
+static void query_colormap(QColormapPrivate *d, int screen)
+{
+ Display *display = QX11Info::display();
+
+ // query existing colormap
+ int q_colors = (((1u << d->depth) > 256u) ? 256u : (1u << d->depth));
+ XColor queried[256];
+ memset(queried, 0, sizeof(queried));
+ for (int x = 0; x < q_colors; ++x)
+ queried[x].pixel = x;
+ XQueryColors(display, d->colormap, queried, q_colors);
+
+ d->colors.resize(q_colors);
+ for (int x = 0; x < q_colors; ++x) {
+ if (queried[x].red == 0
+ && queried[x].green == 0
+ && queried[x].blue == 0
+ && queried[x].pixel != BlackPixel(display, screen)) {
+ // unallocated color cell, skip it
+ continue;
+ }
+
+ d->colors[x] = QColor::fromRgbF(queried[x].red / float(USHRT_MAX),
+ queried[x].green / float(USHRT_MAX),
+ queried[x].blue / float(USHRT_MAX));
+ }
+
+ // for missing colors, find the closest color in the existing colormap
+ Q_ASSERT(d->pixels.size());
+ for (int x = 0; x < d->pixels.size(); ++x) {
+ if (d->pixels.at(x) != -1)
+ continue;
+
+ QRgb rgb;
+ if (d->mode == QColormap::Indexed) {
+ const int r = (x / (d->g_max * d->b_max)) % d->r_max;
+ const int g = (x / d->b_max) % d->g_max;
+ const int b = x % d->b_max;
+ rgb = qRgb((r * 0xff + (d->r_max - 1) / 2) / (d->r_max - 1),
+ (g * 0xff + (d->g_max - 1) / 2) / (d->g_max - 1),
+ (b * 0xff + (d->b_max - 1) / 2) / (d->b_max - 1));
+ } else {
+ rgb = qRgb(x, x, x);
+ }
+
+ // find closest color
+ int mindist = INT_MAX, best = -1;
+ for (int y = 0; y < q_colors; ++y) {
+ int r = qRed(rgb) - (queried[y].red >> 8);
+ int g = qGreen(rgb) - (queried[y].green >> 8);
+ int b = qBlue(rgb) - (queried[y].blue >> 8);
+ int dist = (r * r) + (g * g) + (b * b);
+ if (dist < mindist) {
+ mindist = dist;
+ best = y;
+ }
+ }
+
+ Q_ASSERT(best >= 0 && best < q_colors);
+ if (d->visual->c_class & 1) {
+ XColor xcolor;
+ xcolor.red = queried[best].red;
+ xcolor.green = queried[best].green;
+ xcolor.blue = queried[best].blue;
+ xcolor.pixel = queried[best].pixel;
+
+ if (XAllocColor(display, d->colormap, &xcolor)) {
+ d->pixels[x] = xcolor.pixel;
+ } else {
+ // some weird stuff is going on...
+ d->pixels[x] = (qGray(rgb) < 127
+ ? BlackPixel(display, screen)
+ : WhitePixel(display, screen));
+ }
+ } else {
+ d->pixels[x] = best;
+ }
+ }
+}
+
+static void init_gray(QColormapPrivate *d, int screen)
+{
+ d->pixels.resize(d->r_max);
+
+ for (int g = 0; g < d->g_max; ++g) {
+ const int gray = (g * 0xff + (d->r_max - 1) / 2) / (d->r_max - 1);
+ const QRgb rgb = qRgb(gray, gray, gray);
+
+ d->pixels[g] = -1;
+
+ if (d->visual->c_class & 1) {
+ XColor xcolor;
+ xcolor.red = qRed(rgb) * 0x101;
+ xcolor.green = qGreen(rgb) * 0x101;
+ xcolor.blue = qBlue(rgb) * 0x101;
+ xcolor.pixel = 0ul;
+
+ if (XAllocColor(QX11Info::display(), d->colormap, &xcolor))
+ d->pixels[g] = xcolor.pixel;
+ }
+ }
+
+ query_colormap(d, screen);
+}
+
+static void init_indexed(QColormapPrivate *d, int screen)
+{
+ d->pixels.resize(d->r_max * d->g_max * d->b_max);
+
+ // create color cube
+ for (int x = 0, r = 0; r < d->r_max; ++r) {
+ for (int g = 0; g < d->g_max; ++g) {
+ for (int b = 0; b < d->b_max; ++b, ++x) {
+ const QRgb rgb = qRgb((r * 0xff + (d->r_max - 1) / 2) / (d->r_max - 1),
+ (g * 0xff + (d->g_max - 1) / 2) / (d->g_max - 1),
+ (b * 0xff + (d->b_max - 1) / 2) / (d->b_max - 1));
+
+ d->pixels[x] = -1;
+
+ if (d->visual->c_class & 1) {
+ XColor xcolor;
+ xcolor.red = qRed(rgb) * 0x101;
+ xcolor.green = qGreen(rgb) * 0x101;
+ xcolor.blue = qBlue(rgb) * 0x101;
+ xcolor.pixel = 0ul;
+
+ if (XAllocColor(QX11Info::display(), d->colormap, &xcolor))
+ d->pixels[x] = xcolor.pixel;
+ }
+ }
+ }
+ }
+
+ query_colormap(d, screen);
+}
+
+static void init_direct(QColormapPrivate *d, bool ownColormap)
+{
+ if (d->visual->c_class != DirectColor || !ownColormap)
+ return;
+
+ // preallocate 768 on the stack, so that we don't have to malloc
+ // for the common case (<= 24 bpp)
+ QVarLengthArray<XColor, 768> colorTable(d->r_max + d->g_max + d->b_max);
+ int i = 0;
+
+ for (int r = 0; r < d->r_max; ++r) {
+ colorTable[i].red = r << 8 | r;
+ colorTable[i].pixel = r << d->r_shift;
+ colorTable[i].flags = DoRed;
+ ++i;
+ }
+
+ for (int g = 0; g < d->g_max; ++g) {
+ colorTable[i].green = g << 8 | g;
+ colorTable[i].pixel = g << d->g_shift;
+ colorTable[i].flags = DoGreen;
+ ++i;
+ }
+
+ for (int b = 0; b < d->b_max; ++b) {
+ colorTable[i].blue = (b << 8 | b);
+ colorTable[i].pixel = b << d->b_shift;
+ colorTable[i].flags = DoBlue;
+ ++i;
+ }
+
+ XStoreColors(X11->display, d->colormap, colorTable.data(), colorTable.count());
+}
+
+static QColormap **cmaps = 0;
+
+/*! \internal
+*/
+void QColormap::initialize()
+{
+ Display *display = QX11Info::display();
+ const int screens = ScreenCount(display);
+
+ cmaps = new QColormap*[screens];
+
+ for (int i = 0; i < screens; ++i) {
+ cmaps[i] = new QColormap;
+ QColormapPrivate * const d = cmaps[i]->d;
+
+ bool use_stdcmap = false;
+ int color_count = X11->color_count;
+
+ // defaults
+ d->depth = DefaultDepth(display, i);
+ d->colormap = DefaultColormap(display, i);
+ d->defaultColormap = true;
+ d->visual = DefaultVisual(display, i);
+ d->defaultVisual = true;
+
+ Visual *argbVisual = 0;
+
+ if (X11->visual && i == DefaultScreen(display)) {
+ // only use the outside colormap on the default screen
+ d->visual = find_visual(display, i, X11->visual->c_class,
+ XVisualIDFromVisual(X11->visual),
+ &d->depth, &d->defaultVisual);
+ } else if ((X11->visual_class != -1 && X11->visual_class >= 0 && X11->visual_class < 6)
+ || (X11->visual_id != -1)) {
+ // look for a specific visual or type of visual
+ d->visual = find_visual(display, i, X11->visual_class, X11->visual_id,
+ &d->depth, &d->defaultVisual);
+ } else if (QApplication::colorSpec() == QApplication::ManyColor) {
+ // look for a TrueColor w/ a depth higher than 8bpp
+ d->visual = find_visual(display, i, TrueColor, -1, &d->depth, &d->defaultVisual);
+ if (d->depth <= 8) {
+ d->visual = DefaultVisual(display, i);
+ d->defaultVisual = true;
+ color_count = 216;
+ }
+ } else if (!X11->custom_cmap) {
+ XStandardColormap *stdcmap = 0;
+ int ncmaps = 0;
+
+#ifndef QT_NO_XRENDER
+ if (X11->use_xrender) {
+ int nvi;
+ XVisualInfo templ;
+ templ.screen = i;
+ templ.depth = 32;
+ templ.c_class = TrueColor;
+ XVisualInfo *xvi = XGetVisualInfo(X11->display, VisualScreenMask |
+ VisualDepthMask |
+ VisualClassMask, &templ, &nvi);
+ for (int idx = 0; idx < nvi; ++idx) {
+ XRenderPictFormat *format = XRenderFindVisualFormat(X11->display,
+ xvi[idx].visual);
+ if (format->type == PictTypeDirect && format->direct.alphaMask) {
+ argbVisual = xvi[idx].visual;
+ break;
+ }
+ }
+ XFree(xvi);
+ }
+#endif
+ if (XGetRGBColormaps(display, RootWindow(display, i),
+ &stdcmap, &ncmaps, XA_RGB_DEFAULT_MAP)) {
+ if (stdcmap) {
+ for (int c = 0; c < ncmaps; ++c) {
+ if (!stdcmap[c].red_max ||
+ !stdcmap[c].green_max ||
+ !stdcmap[c].blue_max ||
+ !stdcmap[c].red_mult ||
+ !stdcmap[c].green_mult ||
+ !stdcmap[c].blue_mult)
+ continue; // invalid stdcmap
+
+ XVisualInfo proto;
+ proto.visualid = stdcmap[c].visualid;
+ proto.screen = i;
+
+ int nvisuals = 0;
+ XVisualInfo *vi = XGetVisualInfo(display, VisualIDMask | VisualScreenMask,
+ &proto, &nvisuals);
+ if (vi) {
+ if (nvisuals > 0) {
+ use_stdcmap = true;
+
+ d->mode = ((vi[0].visual->c_class < StaticColor)
+ ? Gray
+ : ((vi[0].visual->c_class < TrueColor)
+ ? Indexed
+ : Direct));
+
+ d->depth = vi[0].depth;
+ d->colormap = stdcmap[c].colormap;
+ d->defaultColormap = true;
+ d->visual = vi[0].visual;
+ d->defaultVisual = (d->visual == DefaultVisual(display, i));
+
+ d->r_max = stdcmap[c].red_max + 1;
+ d->g_max = stdcmap[c].green_max + 1;
+ d->b_max = stdcmap[c].blue_max + 1;
+
+ if (d->mode == Direct) {
+ // calculate offsets
+ d->r_shift = lowest_bit(d->visual->red_mask);
+ d->g_shift = lowest_bit(d->visual->green_mask);
+ d->b_shift = lowest_bit(d->visual->blue_mask);
+ } else {
+ d->r_shift = 0;
+ d->g_shift = 0;
+ d->b_shift = 0;
+ }
+ }
+ XFree(vi);
+ }
+ break;
+ }
+ XFree(stdcmap);
+ }
+ }
+ }
+ if (!use_stdcmap) {
+ switch (d->visual->c_class) {
+ case StaticGray:
+ d->mode = Gray;
+
+ d->r_max = d->g_max = d->b_max = d->visual->map_entries;
+ break;
+
+ case XGrayScale:
+ d->mode = Gray;
+
+ // follow precedent set in libXmu...
+ if (color_count != 0)
+ d->r_max = d->g_max = d->b_max = color_count;
+ else if (d->visual->map_entries > 65000)
+ d->r_max = d->g_max = d->b_max = 4096;
+ else if (d->visual->map_entries > 4000)
+ d->r_max = d->g_max = d->b_max = 512;
+ else if (d->visual->map_entries > 250)
+ d->r_max = d->g_max = d->b_max = 12;
+ else
+ d->r_max = d->g_max = d->b_max = 4;
+ break;
+
+ case StaticColor:
+ d->mode = Indexed;
+
+ d->r_max = right_align(d->visual->red_mask) + 1;
+ d->g_max = right_align(d->visual->green_mask) + 1;
+ d->b_max = right_align(d->visual->blue_mask) + 1;
+ break;
+
+ case PseudoColor:
+ d->mode = Indexed;
+
+ // follow precedent set in libXmu...
+ if (color_count != 0)
+ d->r_max = d->g_max = d->b_max = cube_root(color_count);
+ else if (d->visual->map_entries > 65000)
+ d->r_max = d->g_max = d->b_max = 27;
+ else if (d->visual->map_entries > 4000)
+ d->r_max = d->g_max = d->b_max = 12;
+ else if (d->visual->map_entries > 250)
+ d->r_max = d->g_max = d->b_max = cube_root(d->visual->map_entries - 125);
+ else
+ d->r_max = d->g_max = d->b_max = cube_root(d->visual->map_entries);
+ break;
+
+ case TrueColor:
+ case DirectColor:
+ d->mode = Direct;
+
+ d->r_max = right_align(d->visual->red_mask) + 1;
+ d->g_max = right_align(d->visual->green_mask) + 1;
+ d->b_max = right_align(d->visual->blue_mask) + 1;
+
+ d->r_shift = lowest_bit(d->visual->red_mask);
+ d->g_shift = lowest_bit(d->visual->green_mask);
+ d->b_shift = lowest_bit(d->visual->blue_mask);
+ break;
+ }
+ }
+
+ bool ownColormap = false;
+ if (X11->colormap && i == DefaultScreen(display)) {
+ // only use the outside colormap on the default screen
+ d->colormap = X11->colormap;
+ d->defaultColormap = (d->colormap == DefaultColormap(display, i));
+ } else if ((!use_stdcmap
+ && (((d->visual->c_class & 1) && X11->custom_cmap)
+ || d->visual != DefaultVisual(display, i)))
+ || d->visual->c_class == DirectColor) {
+ // allocate custom colormap (we always do this when using DirectColor visuals)
+ d->colormap =
+ XCreateColormap(display, RootWindow(display, i), d->visual,
+ d->visual->c_class == DirectColor ? AllocAll : AllocNone);
+ d->defaultColormap = false;
+ ownColormap = true;
+ }
+
+ switch (d->mode) {
+ case Gray:
+ init_gray(d, i);
+ break;
+ case Indexed:
+ init_indexed(d, i);
+ break;
+ case Direct:
+ init_direct(d, ownColormap);
+ break;
+ }
+
+ QX11InfoData *screen = X11->screens + i;
+ screen->depth = d->depth;
+ screen->visual = d->visual;
+ screen->defaultVisual = d->defaultVisual;
+ screen->colormap = d->colormap;
+ screen->defaultColormap = d->defaultColormap;
+ screen->cells = screen->visual->map_entries;
+
+ if (argbVisual) {
+ X11->argbVisuals[i] = argbVisual;
+ X11->argbColormaps[i] = XCreateColormap(display, RootWindow(display, i), argbVisual, AllocNone);
+ }
+
+ // ###
+ // We assume that 8bpp == pseudocolor, but this is not
+ // always the case (according to the X server), so we need
+ // to make sure that our internal data is setup in a way
+ // that is compatible with our assumptions
+ if (screen->visual->c_class == TrueColor && screen->depth == 8 && screen->cells == 8)
+ screen->cells = 256;
+ }
+}
+
+/*! \internal
+*/
+void QColormap::cleanup()
+{
+ Display *display = QX11Info::display();
+ const int screens = ScreenCount(display);
+
+ for (int i = 0; i < screens; ++i)
+ delete cmaps[i];
+
+ delete [] cmaps;
+ cmaps = 0;
+}
+
+
+QColormap QColormap::instance(int screen)
+{
+ if (screen == -1)
+ screen = QX11Info::appScreen();
+ return *cmaps[screen];
+}
+
+/*! \internal
+ Constructs a new colormap.
+*/
+QColormap::QColormap()
+ : d(new QColormapPrivate)
+{}
+
+QColormap::QColormap(const QColormap &colormap)
+ :d (colormap.d)
+{ d->ref.ref(); }
+
+QColormap::~QColormap()
+{
+ if (!d->ref.deref()) {
+ if (!d->defaultColormap)
+ XFreeColormap(QX11Info::display(), d->colormap);
+ delete d;
+ }
+}
+
+QColormap::Mode QColormap::mode() const
+{ return d->mode; }
+
+int QColormap::depth() const
+{ return d->depth; }
+
+int QColormap::size() const
+{
+ return (d->mode == Gray
+ ? d->r_max
+ : (d->mode == Indexed
+ ? d->r_max * d->g_max * d->b_max
+ : -1));
+}
+
+uint QColormap::pixel(const QColor &color) const
+{
+ const QColor c = color.toRgb();
+ const uint r = (c.ct.argb.red * d->r_max) >> 16;
+ const uint g = (c.ct.argb.green * d->g_max) >> 16;
+ const uint b = (c.ct.argb.blue * d->b_max) >> 16;
+ if (d->mode != Direct) {
+ if (d->mode == Gray)
+ return d->pixels.at((r * 30 + g * 59 + b * 11) / 100);
+ return d->pixels.at(r * d->g_max * d->b_max + g * d->b_max + b);
+ }
+ return (r << d->r_shift) + (g << d->g_shift) + (b << d->b_shift);
+}
+
+const QColor QColormap::colorAt(uint pixel) const
+{
+ if (d->mode != Direct) {
+ Q_ASSERT(pixel <= (uint)d->colors.size());
+ return d->colors.at(pixel);
+ }
+
+ const int r = (((pixel & d->visual->red_mask) >> d->r_shift) << 8) / d->r_max;
+ const int g = (((pixel & d->visual->green_mask) >> d->g_shift) << 8) / d->g_max;
+ const int b = (((pixel & d->visual->blue_mask) >> d->b_shift) << 8) / d->b_max;
+ return QColor(r, g, b);
+}
+
+const QVector<QColor> QColormap::colormap() const
+{ return d->colors; }
+
+QColormap &QColormap::operator=(const QColormap &colormap)
+{
+ qAtomicAssign(d, colormap.d);
+ return *this;
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/painting/qcssutil.cpp b/src/gui/painting/qcssutil.cpp
new file mode 100644
index 0000000000..29fe373481
--- /dev/null
+++ b/src/gui/painting/qcssutil.cpp
@@ -0,0 +1,408 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qcssutil_p.h"
+#include "private/qcssparser_p.h"
+#include "qpainter.h"
+#include <qmath.h>
+
+#ifndef QT_NO_CSSPARSER
+
+QT_BEGIN_NAMESPACE
+
+using namespace QCss;
+
+static QPen qPenFromStyle(const QBrush& b, qreal width, BorderStyle s)
+{
+ Qt::PenStyle ps = Qt::NoPen;
+
+ switch (s) {
+ case BorderStyle_Dotted:
+ ps = Qt::DotLine;
+ break;
+ case BorderStyle_Dashed:
+ ps = width == 1 ? Qt::DotLine : Qt::DashLine;
+ break;
+ case BorderStyle_DotDash:
+ ps = Qt::DashDotLine;
+ break;
+ case BorderStyle_DotDotDash:
+ ps = Qt::DashDotDotLine;
+ break;
+ case BorderStyle_Inset:
+ case BorderStyle_Outset:
+ case BorderStyle_Solid:
+ ps = Qt::SolidLine;
+ break;
+ default:
+ break;
+ }
+
+ return QPen(b, width, ps, Qt::FlatCap);
+}
+
+void qDrawRoundedCorners(QPainter *p, qreal x1, qreal y1, qreal x2, qreal y2,
+ const QSizeF& r1, const QSizeF& r2,
+ Edge edge, BorderStyle s, QBrush c)
+{
+ const qreal pw = (edge == TopEdge || edge == BottomEdge) ? y2-y1 : x2-x1;
+ if (s == BorderStyle_Double) {
+ qreal wby3 = pw/3;
+ switch (edge) {
+ case TopEdge:
+ case BottomEdge:
+ qDrawRoundedCorners(p, x1, y1, x2, y1+wby3, r1, r2, edge, BorderStyle_Solid, c);
+ qDrawRoundedCorners(p, x1, y2-wby3, x2, y2, r1, r2, edge, BorderStyle_Solid, c);
+ break;
+ case LeftEdge:
+ qDrawRoundedCorners(p, x1, y1+1, x1+wby3, y2, r1, r2, LeftEdge, BorderStyle_Solid, c);
+ qDrawRoundedCorners(p, x2-wby3, y1+1, x2, y2, r1, r2, LeftEdge, BorderStyle_Solid, c);
+ break;
+ case RightEdge:
+ qDrawRoundedCorners(p, x1, y1+1, x1+wby3, y2, r1, r2, RightEdge, BorderStyle_Solid, c);
+ qDrawRoundedCorners(p, x2-wby3, y1+1, x2, y2, r1, r2, RightEdge, BorderStyle_Solid, c);
+ break;
+ default:
+ break;
+ }
+ return;
+ } else if (s == BorderStyle_Ridge || s == BorderStyle_Groove) {
+ BorderStyle s1, s2;
+ if (s == BorderStyle_Groove) {
+ s1 = BorderStyle_Inset;
+ s2 = BorderStyle_Outset;
+ } else {
+ s1 = BorderStyle_Outset;
+ s2 = BorderStyle_Inset;
+ }
+ int pwby2 = qRound(pw/2);
+ switch (edge) {
+ case TopEdge:
+ qDrawRoundedCorners(p, x1, y1, x2, y1 + pwby2, r1, r2, TopEdge, s1, c);
+ qDrawRoundedCorners(p, x1, y1 + pwby2, x2, y2, r1, r2, TopEdge, s2, c);
+ break;
+ case BottomEdge:
+ qDrawRoundedCorners(p, x1, y1 + pwby2, x2, y2, r1, r2, BottomEdge, s1, c);
+ qDrawRoundedCorners(p, x1, y1, x2, y2-pwby2, r1, r2, BottomEdge, s2, c);
+ break;
+ case LeftEdge:
+ qDrawRoundedCorners(p, x1, y1, x1 + pwby2, y2, r1, r2, LeftEdge, s1, c);
+ qDrawRoundedCorners(p, x1 + pwby2, y1, x2, y2, r1, r2, LeftEdge, s2, c);
+ break;
+ case RightEdge:
+ qDrawRoundedCorners(p, x1 + pwby2, y1, x2, y2, r1, r2, RightEdge, s1, c);
+ qDrawRoundedCorners(p, x1, y1, x2 - pwby2, y2, r1, r2, RightEdge, s2, c);
+ break;
+ default:
+ break;
+ }
+ } else if ((s == BorderStyle_Outset && (edge == TopEdge || edge == LeftEdge))
+ || (s == BorderStyle_Inset && (edge == BottomEdge || edge == RightEdge)))
+ c = c.color().lighter();
+
+ p->save();
+ qreal pwby2 = pw/2;
+ p->setBrush(Qt::NoBrush);
+ QPen pen = qPenFromStyle(c, pw, s);
+ pen.setCapStyle(Qt::SquareCap); // this eliminates the offby1 errors that we might hit below
+ p->setPen(pen);
+ switch (edge) {
+ case TopEdge:
+ if (!r1.isEmpty())
+ p->drawArc(QRectF(x1 - r1.width() + pwby2, y1 + pwby2,
+ 2*r1.width() - pw, 2*r1.height() - pw), 135*16, -45*16);
+ if (!r2.isEmpty())
+ p->drawArc(QRectF(x2 - r2.width() + pwby2, y1 + pwby2,
+ 2*r2.width() - pw, 2*r2.height() - pw), 45*16, 45*16);
+ break;
+ case BottomEdge:
+ if (!r1.isEmpty())
+ p->drawArc(QRectF(x1 - r1.width() + pwby2, y2 - 2*r1.height() + pwby2,
+ 2*r1.width() - pw, 2*r1.height() - pw), -90 * 16, -45 * 16);
+ if (!r2.isEmpty())
+ p->drawArc(QRectF(x2 - r2.width() + pwby2, y2 - 2*r2.height() + pwby2,
+ 2*r2.width() - pw, 2*r2.height() - pw), -90 * 16, 45 * 16);
+ break;
+ case LeftEdge:
+ if (!r1.isEmpty())
+ p->drawArc(QRectF(x1 + pwby2, y1 - r1.height() + pwby2,
+ 2*r1.width() - pw, 2*r1.height() - pw), 135*16, 45*16);
+ if (!r2.isEmpty())
+ p->drawArc(QRectF(x1 + pwby2, y2 - r2.height() + pwby2,
+ 2*r2.width() - pw, 2*r2.height() - pw), 180*16, 45*16);
+ break;
+ case RightEdge:
+ if (!r1.isEmpty())
+ p->drawArc(QRectF(x2 - 2*r1.width() + pwby2, y1 - r1.height() + pwby2,
+ 2*r1.width() - pw, 2*r1.height() - pw), 45*16, -45*16);
+ if (!r2.isEmpty())
+ p->drawArc(QRectF(x2 - 2*r2.width() + pwby2, y2 - r2.height() + pwby2,
+ 2*r2.width() - pw, 2*r2.height() - pw), 315*16, 45*16);
+ break;
+ default:
+ break;
+ }
+ p->restore();
+}
+
+
+void qDrawEdge(QPainter *p, qreal x1, qreal y1, qreal x2, qreal y2, qreal dw1, qreal dw2,
+ QCss::Edge edge, QCss::BorderStyle style, QBrush c)
+{
+ p->save();
+ const qreal width = (edge == TopEdge || edge == BottomEdge) ? (y2-y1) : (x2-x1);
+
+ if (width <= 2 && style == BorderStyle_Double)
+ style = BorderStyle_Solid;
+
+ switch (style) {
+ case BorderStyle_Inset:
+ case BorderStyle_Outset:
+ if ((style == BorderStyle_Outset && (edge == TopEdge || edge == LeftEdge))
+ || (style == BorderStyle_Inset && (edge == BottomEdge || edge == RightEdge)))
+ c = c.color().lighter();
+ // fall through!
+ case BorderStyle_Solid: {
+ p->setPen(Qt::NoPen);
+ p->setBrush(c);
+ if (width == 1 || (dw1 == 0 && dw2 == 0)) {
+ p->drawRect(QRectF(x1, y1, x2-x1, y2-y1));
+ } else { // draw trapezoid
+ QPolygonF quad;
+ switch (edge) {
+ case TopEdge:
+ quad << QPointF(x1, y1) << QPointF(x1 + dw1, y2)
+ << QPointF(x2 - dw2, y2) << QPointF(x2, y1);
+ break;
+ case BottomEdge:
+ quad << QPointF(x1 + dw1, y1) << QPointF(x1, y2)
+ << QPointF(x2, y2) << QPointF(x2 - dw2, y1);
+ break;
+ case LeftEdge:
+ quad << QPointF(x1, y1) << QPointF(x1, y2)
+ << QPointF(x2, y2 - dw2) << QPointF(x2, y1 + dw1);
+ break;
+ case RightEdge:
+ quad << QPointF(x1, y1 + dw1) << QPointF(x1, y2 - dw2)
+ << QPointF(x2, y2) << QPointF(x2, y1);
+ break;
+ default:
+ break;
+ }
+ p->drawConvexPolygon(quad);
+ }
+ break;
+ }
+ case BorderStyle_Dotted:
+ case BorderStyle_Dashed:
+ case BorderStyle_DotDash:
+ case BorderStyle_DotDotDash:
+ p->setPen(qPenFromStyle(c, width, style));
+ if (width == 1)
+ p->drawLine(QLineF(x1, y1, x2 - 1, y2 - 1));
+ else if (edge == TopEdge || edge == BottomEdge)
+ p->drawLine(QLineF(x1 + width/2, (y1 + y2)/2, x2 - width/2, (y1 + y2)/2));
+ else
+ p->drawLine(QLineF((x1+x2)/2, y1 + width/2, (x1+x2)/2, y2 - width/2));
+ break;
+
+ case BorderStyle_Double: {
+ int wby3 = qRound(width/3);
+ int dw1by3 = qRound(dw1/3);
+ int dw2by3 = qRound(dw2/3);
+ switch (edge) {
+ case TopEdge:
+ qDrawEdge(p, x1, y1, x2, y1 + wby3, dw1by3, dw2by3, TopEdge, BorderStyle_Solid, c);
+ qDrawEdge(p, x1 + dw1 - dw1by3, y2 - wby3, x2 - dw2 + dw1by3, y2,
+ dw1by3, dw2by3, TopEdge, BorderStyle_Solid, c);
+ break;
+ case LeftEdge:
+ qDrawEdge(p, x1, y1, x1 + wby3, y2, dw1by3, dw2by3, LeftEdge, BorderStyle_Solid, c);
+ qDrawEdge(p, x2 - wby3, y1 + dw1 - dw1by3, x2, y2 - dw2 + dw2by3, dw1by3, dw2by3,
+ LeftEdge, BorderStyle_Solid, c);
+ break;
+ case BottomEdge:
+ qDrawEdge(p, x1 + dw1 - dw1by3, y1, x2 - dw2 + dw2by3, y1 + wby3, dw1by3, dw2by3,
+ BottomEdge, BorderStyle_Solid, c);
+ qDrawEdge(p, x1, y2 - wby3, x2, y2, dw1by3, dw2by3, BottomEdge, BorderStyle_Solid, c);
+ break;
+ case RightEdge:
+ qDrawEdge(p, x2 - wby3, y1, x2, y2, dw1by3, dw2by3, RightEdge, BorderStyle_Solid, c);
+ qDrawEdge(p, x1, y1 + dw1 - dw1by3, x1 + wby3, y2 - dw2 + dw2by3, dw1by3, dw2by3,
+ RightEdge, BorderStyle_Solid, c);
+ break;
+ default:
+ break;
+ }
+ break;
+ }
+ case BorderStyle_Ridge:
+ case BorderStyle_Groove: {
+ BorderStyle s1, s2;
+ if (style == BorderStyle_Groove) {
+ s1 = BorderStyle_Inset;
+ s2 = BorderStyle_Outset;
+ } else {
+ s1 = BorderStyle_Outset;
+ s2 = BorderStyle_Inset;
+ }
+ int dw1by2 = qFloor(dw1/2), dw2by2 = qFloor(dw2/2);
+ int wby2 = qRound(width/2);
+ switch (edge) {
+ case TopEdge:
+ qDrawEdge(p, x1, y1, x2, y1 + wby2, dw1by2, dw2by2, TopEdge, s1, c);
+ qDrawEdge(p, x1 + dw1by2, y1 + wby2, x2 - dw2by2, y2, dw1by2, dw2by2, TopEdge, s2, c);
+ break;
+ case BottomEdge:
+ qDrawEdge(p, x1, y1 + wby2, x2, y2, dw1by2, dw2by2, BottomEdge, s1, c);
+ qDrawEdge(p, x1 + dw1by2, y1, x2 - dw2by2, y1 + wby2, dw1by2, dw2by2, BottomEdge, s2, c);
+ break;
+ case LeftEdge:
+ qDrawEdge(p, x1, y1, x1 + wby2, y2, dw1by2, dw2by2, LeftEdge, s1, c);
+ qDrawEdge(p, x1 + wby2, y1 + dw1by2, x2, y2 - dw2by2, dw1by2, dw2by2, LeftEdge, s2, c);
+ break;
+ case RightEdge:
+ qDrawEdge(p, x1 + wby2, y1, x2, y2, dw1by2, dw2by2, RightEdge, s1, c);
+ qDrawEdge(p, x1, y1 + dw1by2, x1 + wby2, y2 - dw2by2, dw1by2, dw2by2, RightEdge, s2, c);
+ break;
+ default:
+ break;
+ }
+ }
+ default:
+ break;
+ }
+ p->restore();
+}
+
+void qNormalizeRadii(const QRect &br, const QSize *radii,
+ QSize *tlr, QSize *trr, QSize *blr, QSize *brr)
+{
+ *tlr = radii[0].expandedTo(QSize(0, 0));
+ *trr = radii[1].expandedTo(QSize(0, 0));
+ *blr = radii[2].expandedTo(QSize(0, 0));
+ *brr = radii[3].expandedTo(QSize(0, 0));
+ if (tlr->width() + trr->width() > br.width())
+ *tlr = *trr = QSize(0, 0);
+ if (blr->width() + brr->width() > br.width())
+ *blr = *brr = QSize(0, 0);
+ if (tlr->height() + blr->height() > br.height())
+ *tlr = *blr = QSize(0, 0);
+ if (trr->height() + brr->height() > br.height())
+ *trr = *brr = QSize(0, 0);
+}
+
+// Determines if Edge e1 draws over Edge e2. Depending on this trapezoids or rectanges are drawn
+static bool paintsOver(const QCss::BorderStyle *styles, const QBrush *colors, QCss::Edge e1, QCss::Edge e2)
+{
+ QCss::BorderStyle s1 = styles[e1];
+ QCss::BorderStyle s2 = styles[e2];
+
+ if (s2 == BorderStyle_None || colors[e2] == Qt::transparent)
+ return true;
+
+ if ((s1 == BorderStyle_Solid && s2 == BorderStyle_Solid) && (colors[e1] == colors[e2]))
+ return true;
+
+ return false;
+}
+
+void qDrawBorder(QPainter *p, const QRect &rect, const QCss::BorderStyle *styles,
+ const int *borders, const QBrush *colors, const QSize *radii)
+{
+ const QRectF br(rect);
+ QSize tlr, trr, blr, brr;
+ qNormalizeRadii(rect, radii, &tlr, &trr, &blr, &brr);
+
+ // Drawn in increasing order of precendence
+ if (styles[BottomEdge] != BorderStyle_None) {
+ qreal dw1 = (blr.width() || paintsOver(styles, colors, BottomEdge, LeftEdge)) ? 0 : borders[LeftEdge];
+ qreal dw2 = (brr.width() || paintsOver(styles, colors, BottomEdge, RightEdge)) ? 0 : borders[RightEdge];
+ qreal x1 = br.x() + blr.width();
+ qreal y1 = br.y() + br.height() - borders[BottomEdge];
+ qreal x2 = br.x() + br.width() - brr.width();
+ qreal y2 = br.y() + br.height() ;
+
+ qDrawEdge(p, x1, y1, x2, y2, dw1, dw2, BottomEdge, styles[BottomEdge], colors[BottomEdge]);
+ if (blr.width() || brr.width())
+ qDrawRoundedCorners(p, x1, y1, x2, y2, blr, brr, BottomEdge, styles[BottomEdge], colors[BottomEdge]);
+ }
+ if (styles[RightEdge] != BorderStyle_None) {
+ qreal dw1 = (trr.height() || paintsOver(styles, colors, RightEdge, TopEdge)) ? 0 : borders[TopEdge];
+ qreal dw2 = (brr.height() || paintsOver(styles, colors, RightEdge, BottomEdge)) ? 0 : borders[BottomEdge];
+ qreal x1 = br.x() + br.width() - borders[RightEdge];
+ qreal y1 = br.y() + trr.height();
+ qreal x2 = br.x() + br.width();
+ qreal y2 = br.y() + br.height() - brr.height();
+
+ qDrawEdge(p, x1, y1, x2, y2, dw1, dw2, RightEdge, styles[RightEdge], colors[RightEdge]);
+ if (trr.height() || brr.height())
+ qDrawRoundedCorners(p, x1, y1, x2, y2, trr, brr, RightEdge, styles[RightEdge], colors[RightEdge]);
+ }
+ if (styles[LeftEdge] != BorderStyle_None) {
+ qreal dw1 = (tlr.height() || paintsOver(styles, colors, LeftEdge, TopEdge)) ? 0 : borders[TopEdge];
+ qreal dw2 = (blr.height() || paintsOver(styles, colors, LeftEdge, BottomEdge)) ? 0 : borders[BottomEdge];
+ qreal x1 = br.x();
+ qreal y1 = br.y() + tlr.height();
+ qreal x2 = br.x() + borders[LeftEdge];
+ qreal y2 = br.y() + br.height() - blr.height();
+
+ qDrawEdge(p, x1, y1, x2, y2, dw1, dw2, LeftEdge, styles[LeftEdge], colors[LeftEdge]);
+ if (tlr.height() || blr.height())
+ qDrawRoundedCorners(p, x1, y1, x2, y2, tlr, blr, LeftEdge, styles[LeftEdge], colors[LeftEdge]);
+ }
+ if (styles[TopEdge] != BorderStyle_None) {
+ qreal dw1 = (tlr.width() || paintsOver(styles, colors, TopEdge, LeftEdge)) ? 0 : borders[LeftEdge];
+ qreal dw2 = (trr.width() || paintsOver(styles, colors, TopEdge, RightEdge)) ? 0 : borders[RightEdge];
+ qreal x1 = br.x() + tlr.width();
+ qreal y1 = br.y();
+ qreal x2 = br.left() + br.width() - trr.width();
+ qreal y2 = br.y() + borders[TopEdge];
+
+ qDrawEdge(p, x1, y1, x2, y2, dw1, dw2, TopEdge, styles[TopEdge], colors[TopEdge]);
+ if (tlr.width() || trr.width())
+ qDrawRoundedCorners(p, x1, y1, x2, y2, tlr, trr, TopEdge, styles[TopEdge], colors[TopEdge]);
+ }
+}
+
+#endif //QT_NO_CSSPARSER
+
+QT_END_NAMESPACE
diff --git a/src/gui/painting/qcssutil_p.h b/src/gui/painting/qcssutil_p.h
new file mode 100644
index 0000000000..1191ddb4e1
--- /dev/null
+++ b/src/gui/painting/qcssutil_p.h
@@ -0,0 +1,84 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QCSSUTIL_P_H
+#define QCSSUTIL_P_H
+
+#include "QtCore/qglobal.h"
+
+#ifndef QT_NO_CSSPARSER
+
+//
+// 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/qcssparser_p.h"
+#include "QtCore/qsize.h"
+
+QT_BEGIN_NAMESPACE
+
+class QPainter;
+
+extern void qDrawEdge(QPainter *p, qreal x1, qreal y1, qreal x2, qreal y2, qreal dw1, qreal dw2,
+ QCss::Edge edge, QCss::BorderStyle style, QBrush c);
+
+extern void qDrawRoundedCorners(QPainter *p, qreal x1, qreal y1, qreal x2, qreal y2,
+ const QSizeF& r1, const QSizeF& r2,
+ QCss::Edge edge, QCss::BorderStyle s, QBrush c);
+
+extern void qDrawBorder(QPainter *p, const QRect &rect, const QCss::BorderStyle *styles,
+ const int *borders, const QBrush *colors, const QSize *radii);
+
+extern void qNormalizeRadii(const QRect &br, const QSize *radii,
+ QSize *tlr, QSize *trr, QSize *blr, QSize *brr);
+
+QT_END_NAMESPACE
+
+#endif //QT_NO_CSSPARSER
+
+#endif // QCSSUTIL_P_H
diff --git a/src/gui/painting/qcups.cpp b/src/gui/painting/qcups.cpp
new file mode 100644
index 0000000000..e592d77e7d
--- /dev/null
+++ b/src/gui/painting/qcups.cpp
@@ -0,0 +1,398 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#include <qdebug.h>
+#include "qcups_p.h"
+
+#ifndef QT_NO_CUPS
+
+#ifndef QT_LINUXBASE // LSB merges everything into cups.h
+# include <cups/language.h>
+#endif
+#include <qtextcodec.h>
+
+QT_BEGIN_NAMESPACE
+
+typedef int (*CupsGetDests)(cups_dest_t **dests);
+typedef void (*CupsFreeDests)(int num_dests, cups_dest_t *dests);
+typedef const char* (*CupsGetPPD)(const char *printer);
+typedef int (*CupsMarkOptions)(ppd_file_t *ppd, int num_options, cups_option_t *options);
+typedef ppd_file_t* (*PPDOpenFile)(const char *filename);
+typedef void (*PPDMarkDefaults)(ppd_file_t *ppd);
+typedef int (*PPDMarkOption)(ppd_file_t *ppd, const char *keyword, const char *option);
+typedef void (*PPDClose)(ppd_file_t *ppd);
+typedef int (*PPDMarkOption)(ppd_file_t *ppd, const char *keyword, const char *option);
+typedef void (*CupsFreeOptions)(int num_options, cups_option_t *options);
+typedef void (*CupsSetDests)(int num_dests, cups_dest_t *dests);
+typedef cups_lang_t* (*CupsLangGet)(const char *language);
+typedef const char* (*CupsLangEncoding)(cups_lang_t *language);
+typedef int (*CupsAddOption)(const char *name, const char *value, int num_options, cups_option_t **options);
+typedef int (*CupsTempFd)(char *name, int len);
+typedef int (*CupsPrintFile)(const char * name, const char * filename, const char * title, int num_options, cups_option_t * options);
+
+static bool cupsLoaded = false;
+static int qt_cups_num_printers = 0;
+static CupsGetDests _cupsGetDests = 0;
+static CupsFreeDests _cupsFreeDests = 0;
+static CupsGetPPD _cupsGetPPD = 0;
+static PPDOpenFile _ppdOpenFile = 0;
+static PPDMarkDefaults _ppdMarkDefaults = 0;
+static PPDClose _ppdClose = 0;
+static CupsMarkOptions _cupsMarkOptions = 0;
+static PPDMarkOption _ppdMarkOption = 0;
+static CupsFreeOptions _cupsFreeOptions = 0;
+static CupsSetDests _cupsSetDests = 0;
+static CupsLangGet _cupsLangGet = 0;
+static CupsLangEncoding _cupsLangEncoding = 0;
+static CupsAddOption _cupsAddOption = 0;
+static CupsTempFd _cupsTempFd = 0;
+static CupsPrintFile _cupsPrintFile = 0;
+
+static void resolveCups()
+{
+ QLibrary cupsLib(QLatin1String("cups"), 2);
+ if(cupsLib.load()) {
+ _cupsGetDests = (CupsGetDests) cupsLib.resolve("cupsGetDests");
+ _cupsFreeDests = (CupsFreeDests) cupsLib.resolve("cupsFreeDests");
+ _cupsGetPPD = (CupsGetPPD) cupsLib.resolve("cupsGetPPD");
+ _cupsLangGet = (CupsLangGet) cupsLib.resolve("cupsLangGet");
+ _cupsLangEncoding = (CupsLangEncoding) cupsLib.resolve("cupsLangEncoding");
+ _ppdOpenFile = (PPDOpenFile) cupsLib.resolve("ppdOpenFile");
+ _ppdMarkDefaults = (PPDMarkDefaults) cupsLib.resolve("ppdMarkDefaults");
+ _ppdClose = (PPDClose) cupsLib.resolve("ppdClose");
+ _cupsMarkOptions = (CupsMarkOptions) cupsLib.resolve("cupsMarkOptions");
+ _ppdMarkOption = (PPDMarkOption) cupsLib.resolve("ppdMarkOption");
+ _cupsFreeOptions = (CupsFreeOptions) cupsLib.resolve("cupsFreeOptions");
+ _cupsSetDests = (CupsSetDests) cupsLib.resolve("cupsSetDests");
+ _cupsAddOption = (CupsAddOption) cupsLib.resolve("cupsAddOption");
+ _cupsTempFd = (CupsTempFd) cupsLib.resolve("cupsTempFd");
+ _cupsPrintFile = (CupsPrintFile) cupsLib.resolve("cupsPrintFile");
+
+ if (_cupsGetDests && _cupsFreeDests) {
+ cups_dest_t *printers;
+ int num_printers = _cupsGetDests(&printers);
+ if (num_printers)
+ _cupsFreeDests(num_printers, printers);
+ qt_cups_num_printers = num_printers;
+ }
+ }
+ cupsLoaded = true;
+}
+
+// ================ CUPS Support class ========================
+
+QCUPSSupport::QCUPSSupport()
+ :
+ prnCount(0),
+ printers(0),
+ page_sizes(0),
+ currPrinterIndex(0),
+ currPPD(0)
+{
+ if (!cupsLoaded)
+ resolveCups();
+
+ // getting all available printers
+ if (!isAvailable())
+ return;
+
+ prnCount = _cupsGetDests(&printers);
+
+ for (int i = 0; i < prnCount; ++i) {
+ if (printers[i].is_default) {
+ currPrinterIndex = i;
+ setCurrentPrinter(i);
+ break;
+ }
+ }
+
+#ifndef QT_NO_TEXTCODEC
+ cups_lang_t *cupsLang = _cupsLangGet(0);
+ codec = QTextCodec::codecForName(_cupsLangEncoding(cupsLang));
+ if (!codec)
+ codec = QTextCodec::codecForLocale();
+#endif
+}
+
+QCUPSSupport::~QCUPSSupport()
+{
+ if (currPPD)
+ _ppdClose(currPPD);
+ if (prnCount)
+ _cupsFreeDests(prnCount, printers);
+}
+
+int QCUPSSupport::availablePrintersCount() const
+{
+ return prnCount;
+}
+
+const cups_dest_t* QCUPSSupport::availablePrinters() const
+{
+ return printers;
+}
+
+const ppd_file_t* QCUPSSupport::currentPPD() const
+{
+ return currPPD;
+}
+
+const ppd_file_t* QCUPSSupport::setCurrentPrinter(int index)
+{
+ Q_ASSERT(index >= 0 && index <= prnCount);
+ if (index == prnCount)
+ return 0;
+
+ currPrinterIndex = index;
+
+ if (currPPD)
+ _ppdClose(currPPD);
+ currPPD = 0;
+ page_sizes = 0;
+
+ const char *ppdFile = _cupsGetPPD(printers[index].name);
+
+ if (!ppdFile)
+ return 0;
+
+ currPPD = _ppdOpenFile(ppdFile);
+ unlink(ppdFile);
+
+ // marking default options
+ _ppdMarkDefaults(currPPD);
+
+ // marking options explicitly set
+ _cupsMarkOptions(currPPD, printers[currPrinterIndex].num_options, printers[currPrinterIndex].options);
+
+ // getting pointer to page sizes
+ page_sizes = ppdOption("PageSize");
+
+ return currPPD;
+}
+
+int QCUPSSupport::currentPrinterIndex() const
+{
+ return currPrinterIndex;
+}
+
+bool QCUPSSupport::isAvailable()
+{
+ if(!cupsLoaded)
+ resolveCups();
+
+ return _cupsGetDests &&
+ _cupsFreeDests &&
+ _cupsGetPPD &&
+ _ppdOpenFile &&
+ _ppdMarkDefaults &&
+ _ppdClose &&
+ _cupsMarkOptions &&
+ _ppdMarkOption &&
+ _cupsFreeOptions &&
+ _cupsSetDests &&
+ _cupsLangGet &&
+ _cupsLangEncoding &&
+ _cupsAddOption &&
+ (qt_cups_num_printers > 0);
+}
+
+const ppd_option_t* QCUPSSupport::ppdOption(const char *key) const
+{
+ if (currPPD) {
+ for (int gr = 0; gr < currPPD->num_groups; ++gr) {
+ for (int opt = 0; opt < currPPD->groups[gr].num_options; ++opt) {
+ if (qstrcmp(currPPD->groups[gr].options[opt].keyword, key) == 0)
+ return &currPPD->groups[gr].options[opt];
+ }
+ }
+ }
+ return 0;
+}
+
+const cups_option_t* QCUPSSupport::printerOption(const QString &key) const
+{
+ for (int i = 0; i < printers[currPrinterIndex].num_options; ++i) {
+ if (QLatin1String(printers[currPrinterIndex].options[i].name) == key)
+ return &printers[currPrinterIndex].options[i];
+ }
+ return 0;
+}
+
+const ppd_option_t* QCUPSSupport::pageSizes() const
+{
+ return page_sizes;
+}
+
+int QCUPSSupport::markOption(const char* name, const char* value)
+{
+ return _ppdMarkOption(currPPD, name, value);
+}
+
+void QCUPSSupport::saveOptions(QList<const ppd_option_t*> options, QList<const char*> markedOptions)
+{
+ int oldOptionCount = printers[currPrinterIndex].num_options;
+ cups_option_t* oldOptions = printers[currPrinterIndex].options;
+
+ int newOptionCount = 0;
+ cups_option_t* newOptions = 0;
+
+ // copying old options that are not on the new list
+ for (int i = 0; i < oldOptionCount; ++i) {
+ bool contains = false;
+ for (int j = 0; j < options.count(); ++j) {
+ if (qstrcmp(options.at(j)->keyword, oldOptions[i].name) == 0) {
+ contains = true;
+ break;
+ }
+ }
+
+ if (!contains) {
+ newOptionCount = _cupsAddOption(oldOptions[i].name, oldOptions[i].value, newOptionCount, &newOptions);
+ }
+ }
+
+ // we can release old option list
+ _cupsFreeOptions(oldOptionCount, oldOptions);
+
+ // adding marked options
+ for (int i = 0; i < markedOptions.count(); ++i) {
+ const char* name = markedOptions.at(i);
+ ++i;
+ newOptionCount = _cupsAddOption(name, markedOptions.at(i), newOptionCount, &newOptions);
+ }
+
+ // placing the new option list
+ printers[currPrinterIndex].num_options = newOptionCount;
+ printers[currPrinterIndex].options = newOptions;
+
+ // saving new default values
+ _cupsSetDests(prnCount, printers);
+}
+
+QRect QCUPSSupport::paperRect(const char *choice) const
+{
+ if (!currPPD)
+ return QRect();
+ for (int i = 0; i < currPPD->num_sizes; ++i) {
+ if (qstrcmp(currPPD->sizes[i].name, choice) == 0)
+ return QRect(0, 0, qRound(currPPD->sizes[i].width), qRound(currPPD->sizes[i].length));
+ }
+ return QRect();
+}
+
+QRect QCUPSSupport::pageRect(const char *choice) const
+{
+ if (!currPPD)
+ return QRect();
+ for (int i = 0; i < currPPD->num_sizes; ++i) {
+ if (qstrcmp(currPPD->sizes[i].name, choice) == 0)
+ return QRect(qRound(currPPD->sizes[i].left),
+ qRound(currPPD->sizes[i].length - currPPD->sizes[i].top),
+ qRound(currPPD->sizes[i].right - currPPD->sizes[i].left),
+ qRound(currPPD->sizes[i].top - currPPD->sizes[i].bottom));
+ }
+ return QRect();
+}
+
+QStringList QCUPSSupport::options() const
+{
+ QStringList list;
+ collectMarkedOptions(list);
+ return list;
+}
+
+bool QCUPSSupport::printerHasPPD(const char *printerName)
+{
+ if (!isAvailable())
+ return false;
+ return _cupsGetPPD(printerName) != 0;
+}
+
+QString QCUPSSupport::unicodeString(const char *s)
+{
+#ifndef QT_NO_TEXTCODEC
+ return codec->toUnicode(s);
+#else
+ return QLatin1String(s);
+#endif
+}
+
+void QCUPSSupport::collectMarkedOptions(QStringList& list, const ppd_group_t* group) const
+{
+ if (group == 0) {
+ if (!currPPD)
+ return;
+ for (int i = 0; i < currPPD->num_groups; ++i) {
+ collectMarkedOptions(list, &currPPD->groups[i]);
+ collectMarkedOptionsHelper(list, &currPPD->groups[i]);
+ }
+ } else {
+ for (int i = 0; i < group->num_subgroups; ++i)
+ collectMarkedOptionsHelper(list, &group->subgroups[i]);
+ }
+}
+
+void QCUPSSupport::collectMarkedOptionsHelper(QStringList& list, const ppd_group_t* group) const
+{
+ for (int i = 0; i < group->num_options; ++i) {
+ for (int j = 0; j < group->options[i].num_choices; ++j) {
+ if (group->options[i].choices[j].marked == 1 && qstrcmp(group->options[i].choices[j].choice, group->options[i].defchoice) != 0)
+ list << QString::fromLocal8Bit(group->options[i].keyword) << QString::fromLocal8Bit(group->options[i].choices[j].choice);
+ }
+ }
+}
+
+QPair<int, QString> QCUPSSupport::tempFd()
+{
+ char filename[512];
+ int fd = _cupsTempFd(filename, 512);
+ return QPair<int, QString>(fd, QString::fromLocal8Bit(filename));
+}
+
+// Prints the given file and returns a job id.
+int QCUPSSupport::printFile(const char * printerName, const char * filename, const char * title,
+ int num_options, cups_option_t * options)
+{
+ return _cupsPrintFile(printerName, filename, title, num_options, options);
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_CUPS
diff --git a/src/gui/painting/qcups_p.h b/src/gui/painting/qcups_p.h
new file mode 100644
index 0000000000..6973ce0723
--- /dev/null
+++ b/src/gui/painting/qcups_p.h
@@ -0,0 +1,120 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QCUPS_P_H
+#define QCUPS_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 "QtCore/qstring.h"
+#include "QtCore/qstringlist.h"
+#include "QtGui/qprinter.h"
+
+#ifndef QT_NO_CUPS
+#include <QtCore/qlibrary.h>
+#include <cups/cups.h>
+
+QT_BEGIN_NAMESPACE
+
+Q_DECLARE_TYPEINFO(cups_option_t, Q_MOVABLE_TYPE | Q_PRIMITIVE_TYPE);
+
+class QCUPSSupport
+{
+public:
+ QCUPSSupport();
+ ~QCUPSSupport();
+
+ static bool isAvailable();
+ static int cupsVersion() { return isAvailable() ? CUPS_VERSION_MAJOR*10000+CUPS_VERSION_MINOR*100+CUPS_VERSION_PATCH : 0; }
+ int availablePrintersCount() const;
+ const cups_dest_t* availablePrinters() const;
+ int currentPrinterIndex() const;
+ const ppd_file_t* setCurrentPrinter(int index);
+
+ const ppd_file_t* currentPPD() const;
+ const ppd_option_t* ppdOption(const char *key) const;
+
+ const cups_option_t* printerOption(const QString &key) const;
+ const ppd_option_t* pageSizes() const;
+
+ int markOption(const char* name, const char* value);
+ void saveOptions(QList<const ppd_option_t*> options, QList<const char*> markedOptions);
+
+ QRect paperRect(const char *choice) const;
+ QRect pageRect(const char *choice) const;
+
+ QStringList options() const;
+
+ static bool printerHasPPD(const char *printerName);
+
+ QString unicodeString(const char *s);
+
+ QPair<int, QString> tempFd();
+ int printFile(const char * printerName, const char * filename, const char * title,
+ int num_options, cups_option_t * options);
+
+private:
+ void collectMarkedOptions(QStringList& list, const ppd_group_t* group = 0) const;
+ void collectMarkedOptionsHelper(QStringList& list, const ppd_group_t* group) const;
+
+ int prnCount;
+ cups_dest_t *printers;
+ const ppd_option_t* page_sizes;
+ int currPrinterIndex;
+ ppd_file_t *currPPD;
+#ifndef QT_NO_TEXTCODEC
+ QTextCodec *codec;
+#endif
+};
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_CUPS
+
+#endif
diff --git a/src/gui/painting/qdatabuffer_p.h b/src/gui/painting/qdatabuffer_p.h
new file mode 100644
index 0000000000..d6db2ac38f
--- /dev/null
+++ b/src/gui/painting/qdatabuffer_p.h
@@ -0,0 +1,127 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QDATABUFFER_P_H
+#define QDATABUFFER_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/qbytearray.h"
+
+QT_BEGIN_NAMESPACE
+
+template <typename Type> class QDataBuffer
+{
+public:
+ QDataBuffer(int res = 64)
+ {
+ capacity = res;
+ buffer = (Type*) qMalloc(capacity * sizeof(Type));
+ siz = 0;
+ }
+
+ ~QDataBuffer()
+ {
+ qFree(buffer);
+ }
+
+ inline void reset() { siz = 0; }
+
+ inline bool isEmpty() const { return siz==0; }
+
+ inline int size() const { return siz; }
+ inline Type *data() const { return buffer; }
+
+ inline Type &at(int i) { Q_ASSERT(i >= 0 && i < siz); return buffer[i]; }
+ inline const Type &at(int i) const { Q_ASSERT(i >= 0 && i < siz); return buffer[i]; }
+ inline const Type &last() const { Q_ASSERT(!isEmpty()); return buffer[siz-1]; }
+ inline const Type &first() const { Q_ASSERT(!isEmpty()); return buffer[0]; }
+
+ inline void add(const Type &t) {
+ reserve(siz + 1);
+ buffer[siz] = t;
+ ++siz;
+ }
+
+ inline void resize(int size) {
+ reserve(size);
+ siz = size;
+ }
+
+ inline void reserve(int size) {
+ if (size > capacity) {
+ while (capacity < size)
+ capacity *= 2;
+ buffer = (Type*) qRealloc(buffer, capacity * sizeof(Type));
+ }
+ }
+
+ inline void shrink(int size) {
+ capacity = size;
+ buffer = (Type*) qRealloc(buffer, capacity * sizeof(Type));
+ }
+
+ inline void swap(QDataBuffer<Type> &other) {
+ qSwap(capacity, other.capacity);
+ qSwap(siz, other.siz);
+ qSwap(buffer, other.buffer);
+ }
+
+ inline QDataBuffer &operator<<(const Type &t) { add(t); return *this; }
+
+private:
+ int capacity;
+ int siz;
+ Type *buffer;
+};
+
+QT_END_NAMESPACE
+
+#endif // QDATABUFFER_P_H
diff --git a/src/gui/painting/qdrawhelper.cpp b/src/gui/painting/qdrawhelper.cpp
new file mode 100644
index 0000000000..efdc7785e2
--- /dev/null
+++ b/src/gui/painting/qdrawhelper.cpp
@@ -0,0 +1,8248 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#include <private/qdrawhelper_p.h>
+#include <private/qpaintengine_raster_p.h>
+#include <private/qpainter_p.h>
+#include <private/qdrawhelper_x86_p.h>
+#include <private/qmath_p.h>
+#include <qmath.h>
+
+QT_BEGIN_NAMESPACE
+
+#define MASK(src, a) src = BYTE_MUL(src, a)
+
+#if defined(Q_OS_IRIX) && defined(Q_CC_GNU) && __GNUC__ == 3 && __GNUC__ < 4 && QT_POINTER_SIZE == 8
+#define Q_IRIX_GCC3_3_WORKAROUND
+//
+// work around http://gcc.gnu.org/bugzilla/show_bug.cgi?id=14484
+//
+static uint gccBug(uint value) __attribute__((noinline));
+static uint gccBug(uint value)
+{
+ return value;
+}
+#endif
+
+/*
+ constants and structures
+*/
+
+static const int fixed_scale = 1 << 16;
+static const int half_point = 1 << 15;
+static const int buffer_size = 2048;
+
+struct LinearGradientValues
+{
+ qreal dx;
+ qreal dy;
+ qreal l;
+ qreal off;
+};
+
+struct RadialGradientValues
+{
+ qreal dx;
+ qreal dy;
+ qreal a;
+};
+
+struct Operator;
+typedef uint* (QT_FASTCALL *DestFetchProc)(uint *buffer, QRasterBuffer *rasterBuffer, int x, int y, int length);
+typedef void (QT_FASTCALL *DestStoreProc)(QRasterBuffer *rasterBuffer, int x, int y, const uint *buffer, int length);
+typedef const uint* (QT_FASTCALL *SourceFetchProc)(uint *buffer, const Operator *o, const QSpanData *data, int y, int x, int length);
+
+
+struct Operator
+{
+ QPainter::CompositionMode mode;
+ DestFetchProc dest_fetch;
+ DestStoreProc dest_store;
+ SourceFetchProc src_fetch;
+ CompositionFunctionSolid funcSolid;
+ CompositionFunction func;
+ union {
+ LinearGradientValues linear;
+ RadialGradientValues radial;
+// TextureValues texture;
+ };
+};
+
+/*
+ Destination fetch. This is simple as we don't have to do bounds checks or
+ transformations
+*/
+
+static uint * QT_FASTCALL destFetchMono(uint *buffer, QRasterBuffer *rasterBuffer, int x, int y, int length)
+{
+ uchar *data = (uchar *)rasterBuffer->scanLine(y);
+ uint *start = buffer;
+ const uint *end = buffer + length;
+ while (buffer < end) {
+ *buffer = data[x>>3] & (0x80 >> (x & 7)) ? rasterBuffer->destColor1 : rasterBuffer->destColor0;
+ ++buffer;
+ ++x;
+ }
+ return start;
+}
+
+static uint * QT_FASTCALL destFetchMonoLsb(uint *buffer, QRasterBuffer *rasterBuffer, int x, int y, int length)
+{
+ uchar *data = (uchar *)rasterBuffer->scanLine(y);
+ uint *start = buffer;
+ const uint *end = buffer + length;
+ while (buffer < end) {
+ *buffer = data[x>>3] & (0x1 << (x & 7)) ? rasterBuffer->destColor1 : rasterBuffer->destColor0;
+ ++buffer;
+ ++x;
+ }
+ return start;
+}
+
+static uint * QT_FASTCALL destFetchARGB32(uint *buffer, QRasterBuffer *rasterBuffer, int x, int y, int length)
+{
+ const uint *data = (const uint *)rasterBuffer->scanLine(y) + x;
+ for (int i = 0; i < length; ++i)
+ buffer[i] = PREMUL(data[i]);
+ return buffer;
+}
+
+static uint * QT_FASTCALL destFetchARGB32P(uint *, QRasterBuffer *rasterBuffer, int x, int y, int)
+{
+ return (uint *)rasterBuffer->scanLine(y) + x;
+}
+
+static uint * QT_FASTCALL destFetchRGB16(uint *buffer, QRasterBuffer *rasterBuffer, int x, int y, int length)
+{
+ const ushort *data = (const ushort *)rasterBuffer->scanLine(y) + x;
+ for (int i = 0; i < length; ++i)
+ buffer[i] = qConvertRgb16To32(data[i]);
+ return buffer;
+}
+
+#if defined(Q_CC_MSVC) && _MSC_VER <= 1300 && !defined(Q_CC_INTEL)
+template <typename EnumType, int value>
+class QEnumToType
+{
+public:
+ inline EnumType operator()() const
+ {
+ return EnumType(value);
+ }
+};
+template <QImage::Format format>
+class QImageFormatToType
+{
+public:
+ inline QImage::Format operator()() const
+ {
+ return format;
+ }
+};
+// Would have used QEnumToType instead of creating a specialized version for QImageFormatToType,
+// but that causes internal compiler error on VC6
+#define Q_TEMPLATE_IMAGEFORMAT_FIX(format) , const QImageFormatToType<format> &imageFormatType
+#define Q_TEMPLATE_IMAGEFORMAT_CALL(format) , QImageFormatToType<format>()
+#define Q_TEMPLATE_ENUM_FIX(Type, Value) , const QEnumToType<Type, Value> &enumTemplateType
+#define Q_TEMPLATE_ENUM_CALL(Type, Value) , QEnumToType<Type, Value>()
+#define Q_TEMPLATE_FIX(Type) , const QTypeInfo<Type> &templateType
+#define Q_TEMPLATE_CALL(Type) , QTypeInfo<Type>()
+#else
+#define Q_TEMPLATE_IMAGEFORMAT_FIX(format)
+#define Q_TEMPLATE_IMAGEFORMAT_CALL(format)
+#define Q_TEMPLATE_ENUM_FIX(Type, Value)
+#define Q_TEMPLATE_ENUM_CALL(Type, Value)
+#define Q_TEMPLATE_FIX(Type)
+#define Q_TEMPLATE_CALL(Type)
+#endif
+
+template <class DST>
+Q_STATIC_TEMPLATE_FUNCTION uint * QT_FASTCALL destFetch(uint *buffer, QRasterBuffer *rasterBuffer,
+ int x, int y, int length
+ Q_TEMPLATE_FIX(DST))
+{
+ const DST *src = reinterpret_cast<DST*>(rasterBuffer->scanLine(y)) + x;
+ quint32 *dest = reinterpret_cast<quint32*>(buffer);
+ while (length--)
+ *dest++ = *src++;
+ return buffer;
+}
+
+#if defined(Q_CC_MSVC) && _MSC_VER <= 1300 && !defined(Q_CC_INTEL)
+#define DEST_FETCH_DECL(DST) \
+ static uint * QT_FASTCALL destFetch_##DST(uint *buffer, \
+ QRasterBuffer *rasterBuffer, \
+ int x, int y, int length) \
+ { \
+ return destFetch<DST>(buffer, rasterBuffer, x, y, length Q_TEMPLATE_CALL(DST)); \
+ }
+
+DEST_FETCH_DECL(qargb8565)
+DEST_FETCH_DECL(qrgb666)
+DEST_FETCH_DECL(qargb6666)
+DEST_FETCH_DECL(qrgb555)
+DEST_FETCH_DECL(qrgb888)
+DEST_FETCH_DECL(qargb8555)
+DEST_FETCH_DECL(qrgb444)
+DEST_FETCH_DECL(qargb4444)
+#undef DEST_FETCH_DECL
+# define SPANFUNC_POINTER_DESTFETCH(Arg) destFetch_##Arg
+#else // !VC6 && !VC2002
+# define SPANFUNC_POINTER_DESTFETCH(Arg) destFetch<Arg>
+#endif
+
+static const DestFetchProc destFetchProc[QImage::NImageFormats] =
+{
+ 0, // Format_Invalid
+ destFetchMono, // Format_Mono,
+ destFetchMonoLsb, // Format_MonoLSB
+ 0, // Format_Indexed8
+ destFetchARGB32P, // Format_RGB32
+ destFetchARGB32, // Format_ARGB32,
+ destFetchARGB32P, // Format_ARGB32_Premultiplied
+ destFetchRGB16, // Format_RGB16
+ SPANFUNC_POINTER_DESTFETCH(qargb8565), // Format_ARGB8565_Premultiplied
+ SPANFUNC_POINTER_DESTFETCH(qrgb666), // Format_RGB666
+ SPANFUNC_POINTER_DESTFETCH(qargb6666), // Format_ARGB6666_Premultiplied
+ SPANFUNC_POINTER_DESTFETCH(qrgb555), // Format_RGB555
+ SPANFUNC_POINTER_DESTFETCH(qargb8555), // Format_ARGB8555_Premultiplied
+ SPANFUNC_POINTER_DESTFETCH(qrgb888), // Format_RGB888
+ SPANFUNC_POINTER_DESTFETCH(qrgb444), // Format_RGB444
+ SPANFUNC_POINTER_DESTFETCH(qargb4444) // Format_ARGB4444_Premultiplied
+};
+
+/*
+ Returns the color in the mono destination color table
+ that is the "nearest" to /color/.
+*/
+static inline QRgb findNearestColor(QRgb color, QRasterBuffer *rbuf)
+{
+ QRgb color_0 = PREMUL(rbuf->destColor0);
+ QRgb color_1 = PREMUL(rbuf->destColor1);
+ color = PREMUL(color);
+
+ int r = qRed(color);
+ int g = qGreen(color);
+ int b = qBlue(color);
+ int rx, gx, bx;
+ int dist_0, dist_1;
+
+ rx = r - qRed(color_0);
+ gx = g - qGreen(color_0);
+ bx = b - qBlue(color_0);
+ dist_0 = rx*rx + gx*gx + bx*bx;
+
+ rx = r - qRed(color_1);
+ gx = g - qGreen(color_1);
+ bx = b - qBlue(color_1);
+ dist_1 = rx*rx + gx*gx + bx*bx;
+
+ if (dist_0 < dist_1)
+ return color_0;
+ return color_1;
+}
+
+/*
+ Destination store.
+*/
+
+static void QT_FASTCALL destStoreMono(QRasterBuffer *rasterBuffer, int x, int y, const uint *buffer, int length)
+{
+ uchar *data = (uchar *)rasterBuffer->scanLine(y);
+ if (rasterBuffer->monoDestinationWithClut) {
+ for (int i = 0; i < length; ++i) {
+ if (buffer[i] == rasterBuffer->destColor0) {
+ data[x >> 3] &= ~(0x80 >> (x & 7));
+ } else if (buffer[i] == rasterBuffer->destColor1) {
+ data[x >> 3] |= 0x80 >> (x & 7);
+ } else if (findNearestColor(buffer[i], rasterBuffer) == rasterBuffer->destColor0) {
+ data[x >> 3] &= ~(0x80 >> (x & 7));
+ } else {
+ data[x >> 3] |= 0x80 >> (x & 7);
+ }
+ ++x;
+ }
+ } else {
+ for (int i = 0; i < length; ++i) {
+ if (qGray(buffer[i]) < int(qt_bayer_matrix[y & 15][x & 15]))
+ data[x >> 3] |= 0x80 >> (x & 7);
+ else
+ data[x >> 3] &= ~(0x80 >> (x & 7));
+ ++x;
+ }
+ }
+}
+
+static void QT_FASTCALL destStoreMonoLsb(QRasterBuffer *rasterBuffer, int x, int y, const uint *buffer, int length)
+{
+ uchar *data = (uchar *)rasterBuffer->scanLine(y);
+ if (rasterBuffer->monoDestinationWithClut) {
+ for (int i = 0; i < length; ++i) {
+ if (buffer[i] == rasterBuffer->destColor0) {
+ data[x >> 3] &= ~(1 << (x & 7));
+ } else if (buffer[i] == rasterBuffer->destColor1) {
+ data[x >> 3] |= 1 << (x & 7);
+ } else if (findNearestColor(buffer[i], rasterBuffer) == rasterBuffer->destColor0) {
+ data[x >> 3] &= ~(1 << (x & 7));
+ } else {
+ data[x >> 3] |= 1 << (x & 7);
+ }
+ ++x;
+ }
+ } else {
+ for (int i = 0; i < length; ++i) {
+ if (qGray(buffer[i]) < int(qt_bayer_matrix[y & 15][x & 15]))
+ data[x >> 3] |= 1 << (x & 7);
+ else
+ data[x >> 3] &= ~(1 << (x & 7));
+ ++x;
+ }
+ }
+}
+
+static void QT_FASTCALL destStoreARGB32(QRasterBuffer *rasterBuffer, int x, int y, const uint *buffer, int length)
+{
+ uint *data = (uint *)rasterBuffer->scanLine(y) + x;
+ for (int i = 0; i < length; ++i) {
+ int p = buffer[i];
+ int alpha = qAlpha(p);
+ if (alpha == 255)
+ data[i] = p;
+ else if (alpha == 0)
+ data[i] = 0;
+ else {
+ int inv_alpha = 0xff0000/qAlpha(buffer[i]);
+ data[i] = (p & 0xff000000)
+ | ((qRed(p)*inv_alpha) & 0xff0000)
+ | (((qGreen(p)*inv_alpha) >> 8) & 0xff00)
+ | ((qBlue(p)*inv_alpha) >> 16);
+ }
+ }
+}
+
+static void QT_FASTCALL destStoreRGB16(QRasterBuffer *rasterBuffer, int x, int y, const uint *buffer, int length)
+{
+ quint16 *data = (quint16*)rasterBuffer->scanLine(y) + x;
+ qt_memconvert<quint16, quint32>(data, buffer, length);
+}
+
+template <class DST>
+Q_STATIC_TEMPLATE_FUNCTION void QT_FASTCALL destStore(QRasterBuffer *rasterBuffer,
+ int x, int y,
+ const uint *buffer, int length
+ Q_TEMPLATE_FIX(DST))
+{
+ DST *dest = reinterpret_cast<DST*>(rasterBuffer->scanLine(y)) + x;
+ const quint32 *src = reinterpret_cast<const quint32*>(buffer);
+ while (length--)
+ *dest++ = DST(*src++);
+}
+
+#if defined(Q_CC_MSVC) && _MSC_VER <= 1300 && !defined(Q_CC_INTEL)
+# define DEST_STORE_DECL(DST) \
+ static void QT_FASTCALL destStore_##DST(QRasterBuffer *rasterBuffer, \
+ int x, int y, \
+ const uint *buffer, int length) \
+ { \
+ destStore<DST>(rasterBuffer, x, y, buffer, length Q_TEMPLATE_CALL(DST)); \
+ }
+
+DEST_STORE_DECL(qargb8565)
+DEST_STORE_DECL(qrgb555)
+DEST_STORE_DECL(qrgb666)
+DEST_STORE_DECL(qargb6666)
+DEST_STORE_DECL(qargb8555)
+DEST_STORE_DECL(qrgb888)
+DEST_STORE_DECL(qrgb444)
+DEST_STORE_DECL(qargb4444)
+# undef DEST_FETCH_DECL
+# define SPANFUNC_POINTER_DESTSTORE(DEST) destStore_##DEST
+#else // !VC6 && !VC2002
+# define SPANFUNC_POINTER_DESTSTORE(DEST) destStore<DEST>
+#endif
+
+static const DestStoreProc destStoreProc[QImage::NImageFormats] =
+{
+ 0, // Format_Invalid
+ destStoreMono, // Format_Mono,
+ destStoreMonoLsb, // Format_MonoLSB
+ 0, // Format_Indexed8
+ 0, // Format_RGB32
+ destStoreARGB32, // Format_ARGB32,
+ 0, // Format_ARGB32_Premultiplied
+ destStoreRGB16, // Format_RGB16
+ SPANFUNC_POINTER_DESTSTORE(qargb8565), // Format_ARGB8565_Premultiplied
+ SPANFUNC_POINTER_DESTSTORE(qrgb666), // Format_RGB666
+ SPANFUNC_POINTER_DESTSTORE(qargb6666), // Format_ARGB6666_Premultiplied
+ SPANFUNC_POINTER_DESTSTORE(qrgb555), // Format_RGB555
+ SPANFUNC_POINTER_DESTSTORE(qargb8555), // Format_ARGB8555_Premultiplied
+ SPANFUNC_POINTER_DESTSTORE(qrgb888), // Format_RGB888
+ SPANFUNC_POINTER_DESTSTORE(qrgb444), // Format_RGB444
+ SPANFUNC_POINTER_DESTSTORE(qargb4444) // Format_ARGB4444_Premultiplied
+};
+
+/*
+ Source fetches
+
+ This is a bit more complicated, as we need several fetch routines for every surface type
+
+ We need 5 fetch methods per surface type:
+ untransformed
+ transformed
+ transformed tiled
+ transformed bilinear
+ transformed bilinear tiled
+
+ We don't need bounds checks for untransformed, but we need them for the other ones.
+
+ The generic implementation does pixel by pixel fetches
+*/
+
+template <QImage::Format format>
+Q_STATIC_TEMPLATE_FUNCTION uint QT_FASTCALL qt_fetchPixel(const uchar *scanLine, int x, const QVector<QRgb> *rgb
+ Q_TEMPLATE_IMAGEFORMAT_FIX(format));
+
+template<>
+Q_STATIC_TEMPLATE_SPECIALIZATION
+uint QT_FASTCALL qt_fetchPixel<QImage::Format_Mono>(const uchar *scanLine,
+ int x, const QVector<QRgb> *rgb
+ Q_TEMPLATE_IMAGEFORMAT_FIX(QImage::Format_Mono))
+{
+ bool pixel = scanLine[x>>3] & (0x80 >> (x & 7));
+ if (rgb) return PREMUL(rgb->at(pixel ? 1 : 0));
+ return pixel ? 0xff000000 : 0xffffffff;
+}
+
+template<>
+Q_STATIC_TEMPLATE_SPECIALIZATION
+uint QT_FASTCALL qt_fetchPixel<QImage::Format_MonoLSB>(const uchar *scanLine,
+ int x, const QVector<QRgb> *rgb
+ Q_TEMPLATE_IMAGEFORMAT_FIX(QImage::Format_MonoLSB))
+{
+ bool pixel = scanLine[x>>3] & (0x1 << (x & 7));
+ if (rgb) return PREMUL(rgb->at(pixel ? 1 : 0));
+ return pixel ? 0xff000000 : 0xffffffff;
+}
+
+template<>
+Q_STATIC_TEMPLATE_SPECIALIZATION
+uint QT_FASTCALL qt_fetchPixel<QImage::Format_Indexed8>(const uchar *scanLine,
+ int x, const QVector<QRgb> *rgb
+ Q_TEMPLATE_IMAGEFORMAT_FIX(QImage::Format_Indexed8))
+{
+ return PREMUL(rgb->at(scanLine[x]));
+}
+
+template<>
+Q_STATIC_TEMPLATE_SPECIALIZATION
+uint QT_FASTCALL qt_fetchPixel<QImage::Format_ARGB32>(const uchar *scanLine,
+ int x, const QVector<QRgb> *
+ Q_TEMPLATE_IMAGEFORMAT_FIX(QImage::Format_ARGB32))
+{
+ return PREMUL(((const uint *)scanLine)[x]);
+}
+
+template<>
+Q_STATIC_TEMPLATE_SPECIALIZATION
+uint QT_FASTCALL qt_fetchPixel<QImage::Format_ARGB32_Premultiplied>(const uchar *scanLine,
+ int x, const QVector<QRgb> *
+ Q_TEMPLATE_IMAGEFORMAT_FIX(QImage::Format_ARGB32_Premultiplied))
+{
+ return ((const uint *)scanLine)[x];
+}
+
+template<>
+Q_STATIC_TEMPLATE_SPECIALIZATION
+uint QT_FASTCALL qt_fetchPixel<QImage::Format_RGB16>(const uchar *scanLine,
+ int x, const QVector<QRgb> *
+ Q_TEMPLATE_IMAGEFORMAT_FIX(QImage::Format_RGB16))
+{
+ return qConvertRgb16To32(((const ushort *)scanLine)[x]);
+}
+
+template<>
+Q_STATIC_TEMPLATE_SPECIALIZATION
+uint QT_FASTCALL qt_fetchPixel<QImage::Format_ARGB8565_Premultiplied>(const uchar *scanLine,
+ int x,
+ const QVector<QRgb> *
+ Q_TEMPLATE_IMAGEFORMAT_FIX(QImage::Format_ARGB8565_Premultiplied))
+{
+ const qargb8565 color = reinterpret_cast<const qargb8565*>(scanLine)[x];
+ return qt_colorConvert<quint32, qargb8565>(color, 0);
+}
+
+template<>
+Q_STATIC_TEMPLATE_SPECIALIZATION
+uint QT_FASTCALL qt_fetchPixel<QImage::Format_RGB666>(const uchar *scanLine,
+ int x,
+ const QVector<QRgb> *
+ Q_TEMPLATE_IMAGEFORMAT_FIX(QImage::Format_RGB666))
+{
+ const qrgb666 color = reinterpret_cast<const qrgb666*>(scanLine)[x];
+ return qt_colorConvert<quint32, qrgb666>(color, 0);
+}
+
+template<>
+Q_STATIC_TEMPLATE_SPECIALIZATION
+uint QT_FASTCALL qt_fetchPixel<QImage::Format_ARGB6666_Premultiplied>(const uchar *scanLine,
+ int x,
+ const QVector<QRgb> *
+ Q_TEMPLATE_IMAGEFORMAT_FIX(QImage::Format_ARGB6666_Premultiplied))
+{
+ const qargb6666 color = reinterpret_cast<const qargb6666*>(scanLine)[x];
+ return qt_colorConvert<quint32, qargb6666>(color, 0);
+}
+
+template<>
+Q_STATIC_TEMPLATE_SPECIALIZATION
+uint QT_FASTCALL qt_fetchPixel<QImage::Format_RGB555>(const uchar *scanLine,
+ int x,
+ const QVector<QRgb> *
+ Q_TEMPLATE_IMAGEFORMAT_FIX(QImage::Format_RGB555))
+{
+ const qrgb555 color = reinterpret_cast<const qrgb555*>(scanLine)[x];
+ return qt_colorConvert<quint32, qrgb555>(color, 0);
+}
+
+template<>
+Q_STATIC_TEMPLATE_SPECIALIZATION
+uint QT_FASTCALL qt_fetchPixel<QImage::Format_ARGB8555_Premultiplied>(const uchar *scanLine,
+ int x,
+ const QVector<QRgb> *
+ Q_TEMPLATE_IMAGEFORMAT_FIX(QImage::Format_ARGB8555_Premultiplied))
+{
+ const qargb8555 color = reinterpret_cast<const qargb8555*>(scanLine)[x];
+ return qt_colorConvert<quint32, qargb8555>(color, 0);
+}
+
+template<>
+Q_STATIC_TEMPLATE_SPECIALIZATION
+uint QT_FASTCALL qt_fetchPixel<QImage::Format_RGB888>(const uchar *scanLine,
+ int x,
+ const QVector<QRgb> *
+ Q_TEMPLATE_IMAGEFORMAT_FIX(QImage::Format_RGB888))
+{
+ const qrgb888 color = reinterpret_cast<const qrgb888*>(scanLine)[x];
+ return qt_colorConvert<quint32, qrgb888>(color, 0);
+}
+
+template<>
+Q_STATIC_TEMPLATE_SPECIALIZATION
+uint QT_FASTCALL qt_fetchPixel<QImage::Format_RGB444>(const uchar *scanLine,
+ int x,
+ const QVector<QRgb> *
+ Q_TEMPLATE_IMAGEFORMAT_FIX(QImage::Format_RGB444))
+{
+ const qrgb444 color = reinterpret_cast<const qrgb444*>(scanLine)[x];
+ return qt_colorConvert<quint32, qrgb444>(color, 0);
+}
+
+template<>
+Q_STATIC_TEMPLATE_SPECIALIZATION
+uint QT_FASTCALL qt_fetchPixel<QImage::Format_ARGB4444_Premultiplied>(const uchar *scanLine,
+ int x,
+ const QVector<QRgb> *
+ Q_TEMPLATE_IMAGEFORMAT_FIX(QImage::Format_ARGB4444_Premultiplied))
+{
+ const qargb4444 color = reinterpret_cast<const qargb4444*>(scanLine)[x];
+ return qt_colorConvert<quint32, qargb4444>(color, 0);
+}
+
+typedef uint (QT_FASTCALL *FetchPixelProc)(const uchar *scanLine, int x, const QVector<QRgb> *);
+
+#if defined(Q_CC_MSVC) && _MSC_VER <= 1300 && !defined(Q_CC_INTEL)
+
+// explicit template instantiations needed to compile with VC6 and VC2002
+
+#define SPANFUNC_INSTANTIATION_FETCHPIXEL(Arg) \
+ static inline uint fetchPixel_##Arg(const uchar * scanLine, int x, const QVector<QRgb> * rgb) \
+{ \
+ return qt_fetchPixel<QImage::Arg>(scanLine, x, rgb Q_TEMPLATE_IMAGEFORMAT_CALL(QImage::Arg)); \
+}
+
+SPANFUNC_INSTANTIATION_FETCHPIXEL(Format_Mono);
+SPANFUNC_INSTANTIATION_FETCHPIXEL(Format_MonoLSB);
+SPANFUNC_INSTANTIATION_FETCHPIXEL(Format_Indexed8);
+SPANFUNC_INSTANTIATION_FETCHPIXEL(Format_ARGB32_Premultiplied);
+SPANFUNC_INSTANTIATION_FETCHPIXEL(Format_ARGB32);
+SPANFUNC_INSTANTIATION_FETCHPIXEL(Format_RGB16);
+SPANFUNC_INSTANTIATION_FETCHPIXEL(Format_ARGB8565_Premultiplied);
+SPANFUNC_INSTANTIATION_FETCHPIXEL(Format_RGB666);
+SPANFUNC_INSTANTIATION_FETCHPIXEL(Format_ARGB6666_Premultiplied);
+SPANFUNC_INSTANTIATION_FETCHPIXEL(Format_RGB555);
+SPANFUNC_INSTANTIATION_FETCHPIXEL(Format_ARGB8555_Premultiplied);
+SPANFUNC_INSTANTIATION_FETCHPIXEL(Format_RGB888);
+SPANFUNC_INSTANTIATION_FETCHPIXEL(Format_RGB444);
+SPANFUNC_INSTANTIATION_FETCHPIXEL(Format_ARGB4444_Premultiplied);
+
+#undef SPANFUNC_INSTANTIATION_FETCHPIXEL
+
+#define SPANFUNC_POINTER_FETCHPIXEL(Arg) fetchPixel_##Arg
+
+#else // !VC6 && !VC2002
+# define SPANFUNC_POINTER_FETCHPIXEL(Arg) qt_fetchPixel<QImage::Arg>
+#endif
+
+
+static const FetchPixelProc fetchPixelProc[QImage::NImageFormats] =
+{
+ 0,
+ SPANFUNC_POINTER_FETCHPIXEL(Format_Mono),
+ SPANFUNC_POINTER_FETCHPIXEL(Format_MonoLSB),
+ SPANFUNC_POINTER_FETCHPIXEL(Format_Indexed8),
+ SPANFUNC_POINTER_FETCHPIXEL(Format_ARGB32_Premultiplied),
+ SPANFUNC_POINTER_FETCHPIXEL(Format_ARGB32),
+ SPANFUNC_POINTER_FETCHPIXEL(Format_ARGB32_Premultiplied),
+ SPANFUNC_POINTER_FETCHPIXEL(Format_RGB16),
+ SPANFUNC_POINTER_FETCHPIXEL(Format_ARGB8565_Premultiplied),
+ SPANFUNC_POINTER_FETCHPIXEL(Format_RGB666),
+ SPANFUNC_POINTER_FETCHPIXEL(Format_ARGB6666_Premultiplied),
+ SPANFUNC_POINTER_FETCHPIXEL(Format_RGB555),
+ SPANFUNC_POINTER_FETCHPIXEL(Format_ARGB8555_Premultiplied),
+ SPANFUNC_POINTER_FETCHPIXEL(Format_RGB888),
+ SPANFUNC_POINTER_FETCHPIXEL(Format_RGB444),
+ SPANFUNC_POINTER_FETCHPIXEL(Format_ARGB4444_Premultiplied)
+};
+
+enum TextureBlendType {
+ BlendUntransformed,
+ BlendTiled,
+ BlendTransformed,
+ BlendTransformedTiled,
+ BlendTransformedBilinear,
+ BlendTransformedBilinearTiled,
+ NBlendTypes
+};
+
+template <QImage::Format format>
+Q_STATIC_TEMPLATE_FUNCTION const uint * QT_FASTCALL qt_fetchUntransformed(uint *buffer, const Operator *, const QSpanData *data,
+ int y, int x, int length Q_TEMPLATE_IMAGEFORMAT_FIX(format))
+{
+ const uchar *scanLine = data->texture.scanLine(y);
+ for (int i = 0; i < length; ++i)
+ buffer[i] = qt_fetchPixel<format>(scanLine, x + i, data->texture.colorTable Q_TEMPLATE_IMAGEFORMAT_CALL(format));
+ return buffer;
+}
+
+template <>
+Q_STATIC_TEMPLATE_SPECIALIZATION const uint * QT_FASTCALL
+qt_fetchUntransformed<QImage::Format_ARGB32_Premultiplied>(uint *, const Operator *,
+ const QSpanData *data,
+ int y, int x, int Q_TEMPLATE_IMAGEFORMAT_FIX(QImage::Format_ARGB32_Premultiplied))
+{
+ const uchar *scanLine = data->texture.scanLine(y);
+ return ((const uint *)scanLine) + x;
+}
+
+static const uint * QT_FASTCALL fetchTransformed(uint *buffer, const Operator *, const QSpanData *data,
+ int y, int x, int length)
+{
+ FetchPixelProc fetch = fetchPixelProc[data->texture.format];
+
+ int image_width = data->texture.width;
+ int image_height = data->texture.height;
+
+ const qreal cx = x + 0.5;
+ const qreal cy = y + 0.5;
+
+ const uint *end = buffer + length;
+ uint *b = buffer;
+ if (data->fast_matrix) {
+ // The increment pr x in the scanline
+ int fdx = (int)(data->m11 * fixed_scale);
+ int fdy = (int)(data->m12 * fixed_scale);
+
+ int fx = int((data->m21 * cy
+ + data->m11 * cx + data->dx) * fixed_scale);
+ int fy = int((data->m22 * cy
+ + data->m12 * cx + data->dy) * fixed_scale);
+
+ while (b < end) {
+ int px = fx >> 16;
+ int py = fy >> 16;
+
+ bool out = (px < 0) || (px >= image_width)
+ || (py < 0) || (py >= image_height);
+
+ const uchar *scanLine = data->texture.scanLine(py);
+ *b = out ? uint(0) : fetch(scanLine, px, data->texture.colorTable);
+ fx += fdx;
+ fy += fdy;
+ ++b;
+ }
+ } else {
+ const qreal fdx = data->m11;
+ const qreal fdy = data->m12;
+ const qreal fdw = data->m13;
+
+ qreal fx = data->m21 * cy + data->m11 * cx + data->dx;
+ qreal fy = data->m22 * cy + data->m12 * cx + data->dy;
+ qreal fw = data->m23 * cy + data->m13 * cx + data->m33;
+
+ while (b < end) {
+ const qreal iw = fw == 0 ? 1 : 1 / fw;
+ const qreal tx = fx * iw;
+ const qreal ty = fy * iw;
+ const int px = int(tx) - (tx < 0);
+ const int py = int(ty) - (ty < 0);
+
+ bool out = (px < 0) || (px >= image_width)
+ || (py < 0) || (py >= image_height);
+
+ const uchar *scanLine = data->texture.scanLine(py);
+ *b = out ? uint(0) : fetch(scanLine, px, data->texture.colorTable);
+ fx += fdx;
+ fy += fdy;
+ fw += fdw;
+ //force increment to avoid /0
+ if (!fw) {
+ fw += fdw;
+ }
+ ++b;
+ }
+ }
+
+ return buffer;
+}
+
+static const uint * QT_FASTCALL fetchTransformedTiled(uint *buffer, const Operator *, const QSpanData *data,
+ int y, int x, int length)
+{
+ FetchPixelProc fetch = fetchPixelProc[data->texture.format];
+
+ int image_width = data->texture.width;
+ int image_height = data->texture.height;
+
+ const qreal cx = x + 0.5;
+ const qreal cy = y + 0.5;
+
+ const uint *end = buffer + length;
+ uint *b = buffer;
+ if (data->fast_matrix) {
+ // The increment pr x in the scanline
+ int fdx = (int)(data->m11 * fixed_scale);
+ int fdy = (int)(data->m12 * fixed_scale);
+
+ int fx = int((data->m21 * cy
+ + data->m11 * cx + data->dx) * fixed_scale);
+ int fy = int((data->m22 * cy
+ + data->m12 * cx + data->dy) * fixed_scale);
+
+ while (b < end) {
+ int px = fx >> 16;
+ int py = fy >> 16;
+
+ px %= image_width;
+ py %= image_height;
+ if (px < 0) px += image_width;
+ if (py < 0) py += image_height;
+
+ const uchar *scanLine = data->texture.scanLine(py);
+ *b = fetch(scanLine, px, data->texture.colorTable);
+ fx += fdx;
+ fy += fdy;
+ ++b;
+ }
+ } else {
+ const qreal fdx = data->m11;
+ const qreal fdy = data->m12;
+ const qreal fdw = data->m13;
+
+ qreal fx = data->m21 * cy + data->m11 * cx + data->dx;
+ qreal fy = data->m22 * cy + data->m12 * cx + data->dy;
+ qreal fw = data->m23 * cy + data->m13 * cx + data->m33;
+
+ while (b < end) {
+ const qreal iw = fw == 0 ? 1 : 1 / fw;
+ const qreal tx = fx * iw;
+ const qreal ty = fy * iw;
+ int px = int(tx) - (tx < 0);
+ int py = int(ty) - (ty < 0);
+
+ px %= image_width;
+ py %= image_height;
+ if (px < 0) px += image_width;
+ if (py < 0) py += image_height;
+
+ const uchar *scanLine = data->texture.scanLine(py);
+ *b = fetch(scanLine, px, data->texture.colorTable);
+ fx += fdx;
+ fy += fdy;
+ fw += fdw;
+ //force increment to avoid /0
+ if (!fw) {
+ fw += fdw;
+ }
+ ++b;
+ }
+ }
+
+ return buffer;
+}
+
+static const uint * QT_FASTCALL fetchTransformedBilinear(uint *buffer, const Operator *, const QSpanData *data,
+ int y, int x, int length)
+{
+ FetchPixelProc fetch = fetchPixelProc[data->texture.format];
+
+ int image_width = data->texture.width;
+ int image_height = data->texture.height;
+
+ const qreal cx = x + 0.5;
+ const qreal cy = y + 0.5;
+
+ const uint *end = buffer + length;
+ uint *b = buffer;
+ if (data->fast_matrix) {
+ // The increment pr x in the scanline
+ int fdx = (int)(data->m11 * fixed_scale);
+ int fdy = (int)(data->m12 * fixed_scale);
+
+ int fx = int((data->m21 * cy
+ + data->m11 * cx + data->dx) * fixed_scale);
+ int fy = int((data->m22 * cy
+ + data->m12 * cx + data->dy) * fixed_scale);
+
+ fx -= half_point;
+ fy -= half_point;
+ while (b < end) {
+ int x1 = (fx >> 16);
+ int x2 = x1 + 1;
+ int y1 = (fy >> 16);
+ int y2 = y1 + 1;
+
+ int distx = ((fx - (x1 << 16)) >> 8);
+ int disty = ((fy - (y1 << 16)) >> 8);
+ int idistx = 256 - distx;
+ int idisty = 256 - disty;
+
+ x1 = qBound(0, x1, image_width - 1);
+ x2 = qBound(0, x2, image_width - 1);
+ y1 = qBound(0, y1, image_height - 1);
+ y2 = qBound(0, y2, image_height - 1);
+
+ const uchar *s1 = data->texture.scanLine(y1);
+ const uchar *s2 = data->texture.scanLine(y2);
+
+ uint tl = fetch(s1, x1, data->texture.colorTable);
+ uint tr = fetch(s1, x2, data->texture.colorTable);
+ uint bl = fetch(s2, x1, data->texture.colorTable);
+ uint br = fetch(s2, x2, data->texture.colorTable);
+
+ uint xtop = INTERPOLATE_PIXEL_256(tl, idistx, tr, distx);
+ uint xbot = INTERPOLATE_PIXEL_256(bl, idistx, br, distx);
+ *b = INTERPOLATE_PIXEL_256(xtop, idisty, xbot, disty);
+
+ fx += fdx;
+ fy += fdy;
+ ++b;
+ }
+ } else {
+ const qreal fdx = data->m11;
+ const qreal fdy = data->m12;
+ const qreal fdw = data->m13;
+
+ qreal fx = data->m21 * cy + data->m11 * cx + data->dx;
+ qreal fy = data->m22 * cy + data->m12 * cx + data->dy;
+ qreal fw = data->m23 * cy + data->m13 * cx + data->m33;
+
+ while (b < end) {
+ const qreal iw = fw == 0 ? 1 : 1 / fw;
+ const qreal px = fx * iw - 0.5;
+ const qreal py = fy * iw - 0.5;
+
+ int x1 = int(px) - (px < 0);
+ int x2 = x1 + 1;
+ int y1 = int(py) - (py < 0);
+ int y2 = y1 + 1;
+
+ int distx = int((px - x1) * 256);
+ int disty = int((py - y1) * 256);
+ int idistx = 256 - distx;
+ int idisty = 256 - disty;
+
+ x1 = qBound(0, x1, image_width - 1);
+ x2 = qBound(0, x2, image_width - 1);
+ y1 = qBound(0, y1, image_height - 1);
+ y2 = qBound(0, y2, image_height - 1);
+
+ const uchar *s1 = data->texture.scanLine(y1);
+ const uchar *s2 = data->texture.scanLine(y2);
+
+ uint tl = fetch(s1, x1, data->texture.colorTable);
+ uint tr = fetch(s1, x2, data->texture.colorTable);
+ uint bl = fetch(s2, x1, data->texture.colorTable);
+ uint br = fetch(s2, x2, data->texture.colorTable);
+
+ uint xtop = INTERPOLATE_PIXEL_256(tl, idistx, tr, distx);
+ uint xbot = INTERPOLATE_PIXEL_256(bl, idistx, br, distx);
+ *b = INTERPOLATE_PIXEL_256(xtop, idisty, xbot, disty);
+
+ fx += fdx;
+ fy += fdy;
+ fw += fdw;
+ //force increment to avoid /0
+ if (!fw) {
+ fw += fdw;
+ }
+ ++b;
+ }
+ }
+
+ return buffer;
+}
+
+static const uint * QT_FASTCALL fetchTransformedBilinearTiled(uint *buffer, const Operator *, const QSpanData *data,
+ int y, int x, int length)
+{
+ FetchPixelProc fetch = fetchPixelProc[data->texture.format];
+
+ int image_width = data->texture.width;
+ int image_height = data->texture.height;
+
+ const qreal cx = x + 0.5;
+ const qreal cy = y + 0.5;
+
+ const uint *end = buffer + length;
+ uint *b = buffer;
+ if (data->fast_matrix) {
+ // The increment pr x in the scanline
+ int fdx = (int)(data->m11 * fixed_scale);
+ int fdy = (int)(data->m12 * fixed_scale);
+
+ int fx = int((data->m21 * cy + data->m11 * cx + data->dx) * fixed_scale);
+ int fy = int((data->m22 * cy + data->m12 * cx + data->dy) * fixed_scale);
+
+ fx -= half_point;
+ fy -= half_point;
+ while (b < end) {
+ int x1 = (fx >> 16);
+ int x2 = x1 + 1;
+ int y1 = (fy >> 16);
+ int y2 = y1 + 1;
+
+ int distx = ((fx - (x1 << 16)) >> 8);
+ int disty = ((fy - (y1 << 16)) >> 8);
+ int idistx = 256 - distx;
+ int idisty = 256 - disty;
+
+ x1 %= image_width;
+ x2 %= image_width;
+ y1 %= image_height;
+ y2 %= image_height;
+
+ if (x1 < 0) x1 += image_width;
+ if (x2 < 0) x2 += image_width;
+ if (y1 < 0) y1 += image_height;
+ if (y2 < 0) y2 += image_height;
+
+ Q_ASSERT(x1 >= 0 && x1 < image_width);
+ Q_ASSERT(x2 >= 0 && x2 < image_width);
+ Q_ASSERT(y1 >= 0 && y1 < image_height);
+ Q_ASSERT(y2 >= 0 && y2 < image_height);
+
+ const uchar *s1 = data->texture.scanLine(y1);
+ const uchar *s2 = data->texture.scanLine(y2);
+
+ uint tl = fetch(s1, x1, data->texture.colorTable);
+ uint tr = fetch(s1, x2, data->texture.colorTable);
+ uint bl = fetch(s2, x1, data->texture.colorTable);
+ uint br = fetch(s2, x2, data->texture.colorTable);
+
+ uint xtop = INTERPOLATE_PIXEL_256(tl, idistx, tr, distx);
+ uint xbot = INTERPOLATE_PIXEL_256(bl, idistx, br, distx);
+ *b = INTERPOLATE_PIXEL_256(xtop, idisty, xbot, disty);
+
+ fx += fdx;
+ fy += fdy;
+ ++b;
+ }
+ } else {
+ const qreal fdx = data->m11;
+ const qreal fdy = data->m12;
+ const qreal fdw = data->m13;
+
+ qreal fx = data->m21 * cy + data->m11 * cx + data->dx;
+ qreal fy = data->m22 * cy + data->m12 * cx + data->dy;
+ qreal fw = data->m23 * cy + data->m13 * cx + data->m33;
+
+ while (b < end) {
+ const qreal iw = fw == 0 ? 1 : 1 / fw;
+ const qreal px = fx * iw - 0.5;
+ const qreal py = fy * iw - 0.5;
+
+ int x1 = int(px) - (px < 0);
+ int x2 = x1 + 1;
+ int y1 = int(py) - (py < 0);
+ int y2 = y1 + 1;
+
+ int distx = int((px - x1) * 256);
+ int disty = int((py - y1) * 256);
+ int idistx = 256 - distx;
+ int idisty = 256 - disty;
+
+ x1 %= image_width;
+ x2 %= image_width;
+ y1 %= image_height;
+ y2 %= image_height;
+
+ if (x1 < 0) x1 += image_width;
+ if (x2 < 0) x2 += image_width;
+ if (y1 < 0) y1 += image_height;
+ if (y2 < 0) y2 += image_height;
+
+ Q_ASSERT(x1 >= 0 && x1 < image_width);
+ Q_ASSERT(x2 >= 0 && x2 < image_width);
+ Q_ASSERT(y1 >= 0 && y1 < image_height);
+ Q_ASSERT(y2 >= 0 && y2 < image_height);
+
+ const uchar *s1 = data->texture.scanLine(y1);
+ const uchar *s2 = data->texture.scanLine(y2);
+
+ uint tl = fetch(s1, x1, data->texture.colorTable);
+ uint tr = fetch(s1, x2, data->texture.colorTable);
+ uint bl = fetch(s2, x1, data->texture.colorTable);
+ uint br = fetch(s2, x2, data->texture.colorTable);
+
+ uint xtop = INTERPOLATE_PIXEL_256(tl, idistx, tr, distx);
+ uint xbot = INTERPOLATE_PIXEL_256(bl, idistx, br, distx);
+ *b = INTERPOLATE_PIXEL_256(xtop, idisty, xbot, disty);
+
+ fx += fdx;
+ fy += fdy;
+ fw += fdw;
+ //force increment to avoid /0
+ if (!fw) {
+ fw += fdw;
+ }
+ ++b;
+ }
+ }
+
+ return buffer;
+}
+
+#if defined(Q_CC_MSVC) && _MSC_VER <= 1300 && !defined(Q_CC_INTEL)
+
+// explicit template instantiations needed to compile with VC6 and VC2002
+
+#define SPANFUNC_POINTER_FETCHUNTRANSFORMED(Arg) \
+ const uint *qt_fetchUntransformed_##Arg(uint *buffer, const Operator *op, const QSpanData *data, \
+ int y, int x, int length) \
+{ \
+ return qt_fetchUntransformed<QImage::Arg>(buffer, op, data, y, x, length Q_TEMPLATE_IMAGEFORMAT_CALL(QImage::Arg)); \
+}
+
+SPANFUNC_POINTER_FETCHUNTRANSFORMED(Format_Mono);
+SPANFUNC_POINTER_FETCHUNTRANSFORMED(Format_MonoLSB);
+SPANFUNC_POINTER_FETCHUNTRANSFORMED(Format_Indexed8);
+SPANFUNC_POINTER_FETCHUNTRANSFORMED(Format_ARGB32_Premultiplied);
+SPANFUNC_POINTER_FETCHUNTRANSFORMED(Format_ARGB32);
+SPANFUNC_POINTER_FETCHUNTRANSFORMED(Format_RGB16);
+SPANFUNC_POINTER_FETCHUNTRANSFORMED(Format_ARGB8565_Premultiplied);
+SPANFUNC_POINTER_FETCHUNTRANSFORMED(Format_RGB666);
+SPANFUNC_POINTER_FETCHUNTRANSFORMED(Format_ARGB6666_Premultiplied);
+SPANFUNC_POINTER_FETCHUNTRANSFORMED(Format_RGB555);
+SPANFUNC_POINTER_FETCHUNTRANSFORMED(Format_ARGB8555_Premultiplied);
+SPANFUNC_POINTER_FETCHUNTRANSFORMED(Format_RGB888);
+SPANFUNC_POINTER_FETCHUNTRANSFORMED(Format_RGB444);
+SPANFUNC_POINTER_FETCHUNTRANSFORMED(Format_ARGB4444_Premultiplied);
+
+#undef SPANFUNC_POINTER_FETCHUNTRANSFORMED
+
+#define SPANFUNC_POINTER_FETCHHUNTRANSFORMED(Arg) qt_fetchUntransformed_##Arg
+
+#else // !VC6 && !VC2002
+# define SPANFUNC_POINTER_FETCHHUNTRANSFORMED(Arg) qt_fetchUntransformed<QImage::Arg>
+#endif
+
+static const SourceFetchProc sourceFetch[NBlendTypes][QImage::NImageFormats] = {
+ // Untransformed
+ {
+ 0, // Invalid
+ SPANFUNC_POINTER_FETCHHUNTRANSFORMED(Format_Mono), // Mono
+ SPANFUNC_POINTER_FETCHHUNTRANSFORMED(Format_MonoLSB), // MonoLsb
+ SPANFUNC_POINTER_FETCHHUNTRANSFORMED(Format_Indexed8), // Indexed8
+ SPANFUNC_POINTER_FETCHHUNTRANSFORMED(Format_ARGB32_Premultiplied), // RGB32
+ SPANFUNC_POINTER_FETCHHUNTRANSFORMED(Format_ARGB32), // ARGB32
+ SPANFUNC_POINTER_FETCHHUNTRANSFORMED(Format_ARGB32_Premultiplied), // ARGB32_Premultiplied
+ SPANFUNC_POINTER_FETCHHUNTRANSFORMED(Format_RGB16), // RGB16
+ SPANFUNC_POINTER_FETCHHUNTRANSFORMED(Format_ARGB8565_Premultiplied),// ARGB8565_Premultiplied
+ SPANFUNC_POINTER_FETCHHUNTRANSFORMED(Format_RGB666), // RGB666
+ SPANFUNC_POINTER_FETCHHUNTRANSFORMED(Format_ARGB6666_Premultiplied),// ARGB6666_Premultiplied
+ SPANFUNC_POINTER_FETCHHUNTRANSFORMED(Format_RGB555), // RGB555
+ SPANFUNC_POINTER_FETCHHUNTRANSFORMED(Format_ARGB8555_Premultiplied),// ARGB8555_Premultiplied
+ SPANFUNC_POINTER_FETCHHUNTRANSFORMED(Format_RGB888), // RGB888
+ SPANFUNC_POINTER_FETCHHUNTRANSFORMED(Format_RGB444), // RGB444
+ SPANFUNC_POINTER_FETCHHUNTRANSFORMED(Format_ARGB4444_Premultiplied) // ARGB4444_Premultiplied
+ },
+ // Tiled
+ {
+ 0, // Invalid
+ SPANFUNC_POINTER_FETCHHUNTRANSFORMED(Format_Mono), // Mono
+ SPANFUNC_POINTER_FETCHHUNTRANSFORMED(Format_MonoLSB), // MonoLsb
+ SPANFUNC_POINTER_FETCHHUNTRANSFORMED(Format_Indexed8), // Indexed8
+ SPANFUNC_POINTER_FETCHHUNTRANSFORMED(Format_ARGB32_Premultiplied), // RGB32
+ SPANFUNC_POINTER_FETCHHUNTRANSFORMED(Format_ARGB32), // ARGB32
+ SPANFUNC_POINTER_FETCHHUNTRANSFORMED(Format_ARGB32_Premultiplied), // ARGB32_Premultiplied
+ SPANFUNC_POINTER_FETCHHUNTRANSFORMED(Format_RGB16), // RGB16
+ SPANFUNC_POINTER_FETCHHUNTRANSFORMED(Format_ARGB8565_Premultiplied),// ARGB8565_Premultiplied
+ SPANFUNC_POINTER_FETCHHUNTRANSFORMED(Format_RGB666), // RGB666
+ SPANFUNC_POINTER_FETCHHUNTRANSFORMED(Format_ARGB6666_Premultiplied),// ARGB6666_Premultiplied
+ SPANFUNC_POINTER_FETCHHUNTRANSFORMED(Format_RGB555), // RGB555
+ SPANFUNC_POINTER_FETCHHUNTRANSFORMED(Format_ARGB8555_Premultiplied),// ARGB8555_Premultiplied
+ SPANFUNC_POINTER_FETCHHUNTRANSFORMED(Format_RGB888), // RGB888
+ SPANFUNC_POINTER_FETCHHUNTRANSFORMED(Format_RGB444), // RGB444
+ SPANFUNC_POINTER_FETCHHUNTRANSFORMED(Format_ARGB4444_Premultiplied) // ARGB4444_Premultiplied
+ },
+ // Transformed
+ {
+ 0, // Invalid
+ fetchTransformed, // Mono
+ fetchTransformed, // MonoLsb
+ fetchTransformed, // Indexed8
+ fetchTransformed, // RGB32
+ fetchTransformed, // ARGB32
+ fetchTransformed, // ARGB32_Premultiplied
+ fetchTransformed, // RGB16
+ fetchTransformed, // ARGB8565_Premultiplied
+ fetchTransformed, // RGB666
+ fetchTransformed, // ARGB6666_Premultiplied
+ fetchTransformed, // RGB555
+ fetchTransformed, // ARGB8555_Premultiplied
+ fetchTransformed, // RGB888
+ fetchTransformed, // RGB444
+ fetchTransformed, // ARGB4444_Premultiplied
+ },
+ {
+ 0, // TransformedTiled
+ fetchTransformedTiled, // Mono
+ fetchTransformedTiled, // MonoLsb
+ fetchTransformedTiled, // Indexed8
+ fetchTransformedTiled, // RGB32
+ fetchTransformedTiled, // ARGB32
+ fetchTransformedTiled, // ARGB32_Premultiplied
+ fetchTransformedTiled, // RGB16
+ fetchTransformedTiled, // ARGB8565_Premultiplied
+ fetchTransformedTiled, // RGB666
+ fetchTransformedTiled, // ARGB6666_Premultiplied
+ fetchTransformedTiled, // RGB555
+ fetchTransformedTiled, // ARGB8555_Premultiplied
+ fetchTransformedTiled, // RGB888
+ fetchTransformedTiled, // RGB444
+ fetchTransformedTiled, // ARGB4444_Premultiplied
+ },
+ {
+ 0, // Bilinear
+ fetchTransformedBilinear, // Mono
+ fetchTransformedBilinear, // MonoLsb
+ fetchTransformedBilinear, // Indexed8
+ fetchTransformedBilinear, // RGB32
+ fetchTransformedBilinear, // ARGB32
+ fetchTransformedBilinear, // ARGB32_Premultiplied
+ fetchTransformedBilinear, // RGB16
+ fetchTransformedBilinear, // ARGB8565_Premultiplied
+ fetchTransformedBilinear, // RGB666
+ fetchTransformedBilinear, // ARGB6666_Premultiplied
+ fetchTransformedBilinear, // RGB555
+ fetchTransformedBilinear, // ARGB8555_Premultiplied
+ fetchTransformedBilinear, // RGB888
+ fetchTransformedBilinear, // RGB444
+ fetchTransformedBilinear // ARGB4444_Premultiplied
+ },
+ {
+ 0, // BilinearTiled
+ fetchTransformedBilinearTiled, // Mono
+ fetchTransformedBilinearTiled, // MonoLsb
+ fetchTransformedBilinearTiled, // Indexed8
+ fetchTransformedBilinearTiled, // RGB32
+ fetchTransformedBilinearTiled, // ARGB32
+ fetchTransformedBilinearTiled, // ARGB32_Premultiplied
+ fetchTransformedBilinearTiled, // RGB16
+ fetchTransformedBilinearTiled, // ARGB8565_Premultiplied
+ fetchTransformedBilinearTiled, // RGB666
+ fetchTransformedBilinearTiled, // ARGB6666_Premultiplied
+ fetchTransformedBilinearTiled, // RGB555
+ fetchTransformedBilinearTiled, // ARGB8555_Premultiplied
+ fetchTransformedBilinearTiled, // RGB888
+ fetchTransformedBilinearTiled, // RGB444
+ fetchTransformedBilinearTiled // ARGB4444_Premultiplied
+ },
+};
+
+
+static inline uint qt_gradient_pixel(const QGradientData *data, qreal pos)
+{
+ int ipos = int(pos * (GRADIENT_STOPTABLE_SIZE - 1) + 0.5);
+
+ // calculate the actual offset.
+ if (ipos < 0 || ipos >= GRADIENT_STOPTABLE_SIZE) {
+ if (data->spread == QGradient::RepeatSpread) {
+ ipos = ipos % GRADIENT_STOPTABLE_SIZE;
+ ipos = ipos < 0 ? GRADIENT_STOPTABLE_SIZE + ipos : ipos;
+
+ } else if (data->spread == QGradient::ReflectSpread) {
+ const int limit = GRADIENT_STOPTABLE_SIZE * 2 - 1;
+ ipos = ipos % limit;
+ ipos = ipos < 0 ? limit + ipos : ipos;
+ ipos = ipos >= GRADIENT_STOPTABLE_SIZE ? limit - ipos : ipos;
+
+ } else {
+ if (ipos < 0) ipos = 0;
+ else if (ipos >= GRADIENT_STOPTABLE_SIZE) ipos = GRADIENT_STOPTABLE_SIZE-1;
+ }
+ }
+
+ Q_ASSERT(ipos >= 0);
+ Q_ASSERT(ipos < GRADIENT_STOPTABLE_SIZE);
+
+ return data->colorTable[ipos];
+}
+
+#define FIXPT_BITS 8
+#define FIXPT_SIZE (1<<FIXPT_BITS)
+
+static uint qt_gradient_pixel_fixed(const QGradientData *data, int fixed_pos)
+{
+ int ipos = (fixed_pos + (FIXPT_SIZE / 2)) >> FIXPT_BITS;
+
+ // calculate the actual offset.
+ if (ipos < 0 || ipos >= GRADIENT_STOPTABLE_SIZE) {
+ if (data->spread == QGradient::RepeatSpread) {
+ ipos = ipos % GRADIENT_STOPTABLE_SIZE;
+ ipos = ipos < 0 ? GRADIENT_STOPTABLE_SIZE + ipos : ipos;
+
+ } else if (data->spread == QGradient::ReflectSpread) {
+ const int limit = GRADIENT_STOPTABLE_SIZE * 2 - 1;
+ ipos = ipos % limit;
+ ipos = ipos < 0 ? limit + ipos : ipos;
+ ipos = ipos >= GRADIENT_STOPTABLE_SIZE ? limit - ipos : ipos;
+
+ } else {
+ if (ipos < 0) ipos = 0;
+ else if (ipos >= GRADIENT_STOPTABLE_SIZE) ipos = GRADIENT_STOPTABLE_SIZE-1;
+ }
+ }
+
+ Q_ASSERT(ipos >= 0);
+ Q_ASSERT(ipos < GRADIENT_STOPTABLE_SIZE);
+
+ return data->colorTable[ipos];
+}
+
+static void QT_FASTCALL getLinearGradientValues(LinearGradientValues *v, const QSpanData *data)
+{
+ v->dx = data->gradient.linear.end.x - data->gradient.linear.origin.x;
+ v->dy = data->gradient.linear.end.y - data->gradient.linear.origin.y;
+ v->l = v->dx * v->dx + v->dy * v->dy;
+ v->off = 0;
+ if (v->l != 0) {
+ v->dx /= v->l;
+ v->dy /= v->l;
+ v->off = -v->dx * data->gradient.linear.origin.x - v->dy * data->gradient.linear.origin.y;
+ }
+}
+
+static const uint * QT_FASTCALL fetchLinearGradient(uint *buffer, const Operator *op, const QSpanData *data,
+ int y, int x, int length)
+{
+ const uint *b = buffer;
+ qreal t, inc;
+
+ bool affine = true;
+ qreal rx=0, ry=0;
+ if (op->linear.l == 0) {
+ t = inc = 0;
+ } else {
+ rx = data->m21 * (y + 0.5) + data->m11 * (x + 0.5) + data->dx;
+ ry = data->m22 * (y + 0.5) + data->m12 * (x + 0.5) + data->dy;
+ t = op->linear.dx*rx + op->linear.dy*ry + op->linear.off;
+ inc = op->linear.dx * data->m11 + op->linear.dy * data->m12;
+ affine = !data->m13 && !data->m23;
+
+ if (affine) {
+ t *= (GRADIENT_STOPTABLE_SIZE - 1);
+ inc *= (GRADIENT_STOPTABLE_SIZE - 1);
+ }
+ }
+
+ const uint *end = buffer + length;
+ if (affine) {
+ if (inc > -1e-5 && inc < 1e-5) {
+ QT_MEMFILL_UINT(buffer, length, qt_gradient_pixel_fixed(&data->gradient, int(t * FIXPT_SIZE)));
+ } else {
+ if (t+inc*length < qreal(INT_MAX >> (FIXPT_BITS + 1)) &&
+ t+inc*length > qreal(INT_MIN >> (FIXPT_BITS + 1))) {
+ // we can use fixed point math
+ int t_fixed = int(t * FIXPT_SIZE);
+ int inc_fixed = int(inc * FIXPT_SIZE);
+ while (buffer < end) {
+ *buffer = qt_gradient_pixel_fixed(&data->gradient, t_fixed);
+ t_fixed += inc_fixed;
+ ++buffer;
+ }
+ } else {
+ // we have to fall back to float math
+ while (buffer < end) {
+ *buffer = qt_gradient_pixel(&data->gradient, t/GRADIENT_STOPTABLE_SIZE);
+ t += inc;
+ ++buffer;
+ }
+ }
+ }
+ } else { // fall back to float math here as well
+ qreal rw = data->m23 * (y + 0.5) + data->m13 * (x + 0.5) + data->m33;
+ while (buffer < end) {
+ qreal x = rx/rw;
+ qreal y = ry/rw;
+ t = (op->linear.dx*x + op->linear.dy *y) + op->linear.off;
+
+ *buffer = qt_gradient_pixel(&data->gradient, t);
+ rx += data->m11;
+ ry += data->m12;
+ rw += data->m13;
+ if (!rw) {
+ rw += data->m13;
+ }
+ ++buffer;
+ }
+ }
+
+ return b;
+}
+
+static inline qreal determinant(qreal a, qreal b, qreal c)
+{
+ return (b * b) - (4 * a * c);
+}
+
+// function to evaluate real roots
+static inline qreal realRoots(qreal a, qreal b, qreal detSqrt)
+{
+ return (-b + detSqrt)/(2 * a);
+}
+
+static inline qreal qSafeSqrt(qreal x)
+{
+ return x > 0 ? qSqrt(x) : 0;
+}
+
+static void QT_FASTCALL getRadialGradientValues(RadialGradientValues *v, const QSpanData *data)
+{
+ v->dx = data->gradient.radial.center.x - data->gradient.radial.focal.x;
+ v->dy = data->gradient.radial.center.y - data->gradient.radial.focal.y;
+ v->a = data->gradient.radial.radius*data->gradient.radial.radius - v->dx*v->dx - v->dy*v->dy;
+}
+
+static const uint * QT_FASTCALL fetchRadialGradient(uint *buffer, const Operator *op, const QSpanData *data,
+ int y, int x, int length)
+{
+ const uint *b = buffer;
+ qreal rx = data->m21 * (y + 0.5)
+ + data->dx + data->m11 * (x + 0.5);
+ qreal ry = data->m22 * (y + 0.5)
+ + data->dy + data->m12 * (x + 0.5);
+ bool affine = !data->m13 && !data->m23;
+ //qreal r = data->gradient.radial.radius;
+
+ const uint *end = buffer + length;
+ if (affine) {
+ rx -= data->gradient.radial.focal.x;
+ ry -= data->gradient.radial.focal.y;
+
+ qreal inv_a = 1 / qreal(2 * op->radial.a);
+
+ const qreal delta_rx = data->m11;
+ const qreal delta_ry = data->m12;
+
+ qreal b = 2*(rx * op->radial.dx + ry * op->radial.dy);
+ qreal delta_b = 2*(delta_rx * op->radial.dx + delta_ry * op->radial.dy);
+ const qreal b_delta_b = 2 * b * delta_b;
+ const qreal delta_b_delta_b = 2 * delta_b * delta_b;
+
+ const qreal bb = b * b;
+ const qreal delta_bb = delta_b * delta_b;
+
+ b *= inv_a;
+ delta_b *= inv_a;
+
+ const qreal rxrxryry = rx * rx + ry * ry;
+ const qreal delta_rxrxryry = delta_rx * delta_rx + delta_ry * delta_ry;
+ const qreal rx_plus_ry = 2*(rx * delta_rx + ry * delta_ry);
+ const qreal delta_rx_plus_ry = 2 * delta_rxrxryry;
+
+ inv_a *= inv_a;
+
+ qreal det = (bb + 4 * op->radial.a * rxrxryry) * inv_a;
+ qreal delta_det = (b_delta_b + delta_bb + 4 * op->radial.a * (rx_plus_ry + delta_rxrxryry)) * inv_a;
+ const qreal delta_delta_det = (delta_b_delta_b + 4 * op->radial.a * delta_rx_plus_ry) * inv_a;
+
+ while (buffer < end) {
+ *buffer = qt_gradient_pixel(&data->gradient, qSafeSqrt(det) - b);
+
+ det += delta_det;
+ delta_det += delta_delta_det;
+ b += delta_b;
+
+ ++buffer;
+ }
+ } else {
+ qreal rw = data->m23 * (y + 0.5)
+ + data->m33 + data->m13 * (x + 0.5);
+ if (!rw)
+ rw = 1;
+ while (buffer < end) {
+ qreal gx = rx/rw - data->gradient.radial.focal.x;
+ qreal gy = ry/rw - data->gradient.radial.focal.y;
+ qreal b = 2*(gx*op->radial.dx + gy*op->radial.dy);
+ qreal det = determinant(op->radial.a, b , -(gx*gx + gy*gy));
+ qreal s = realRoots(op->radial.a, b, qSafeSqrt(det));
+
+ *buffer = qt_gradient_pixel(&data->gradient, s);
+
+ rx += data->m11;
+ ry += data->m12;
+ rw += data->m13;
+ if (!rw) {
+ rw += data->m13;
+ }
+ ++buffer;
+ }
+ }
+
+ return b;
+}
+
+static const uint * QT_FASTCALL fetchConicalGradient(uint *buffer, const Operator *, const QSpanData *data,
+ int y, int x, int length)
+{
+ const uint *b = buffer;
+ qreal rx = data->m21 * (y + 0.5)
+ + data->dx + data->m11 * (x + 0.5);
+ qreal ry = data->m22 * (y + 0.5)
+ + data->dy + data->m12 * (x + 0.5);
+ bool affine = !data->m13 && !data->m23;
+
+ const uint *end = buffer + length;
+ if (affine) {
+ rx -= data->gradient.conical.center.x;
+ ry -= data->gradient.conical.center.y;
+ while (buffer < end) {
+ qreal angle = atan2(ry, rx) + data->gradient.conical.angle;
+
+ *buffer = qt_gradient_pixel(&data->gradient, 1 - angle / (2*Q_PI));
+
+ rx += data->m11;
+ ry += data->m12;
+ ++buffer;
+ }
+ } else {
+ qreal rw = data->m23 * (y + 0.5)
+ + data->m33 + data->m13 * (x + 0.5);
+ if (!rw)
+ rw = 1;
+ while (buffer < end) {
+ qreal angle = atan2(ry/rw - data->gradient.conical.center.x,
+ rx/rw - data->gradient.conical.center.y)
+ + data->gradient.conical.angle;
+
+ *buffer = qt_gradient_pixel(&data->gradient, 1. - angle / (2*Q_PI));
+
+ rx += data->m11;
+ ry += data->m12;
+ rw += data->m13;
+ if (!rw) {
+ rw += data->m13;
+ }
+ ++buffer;
+ }
+ }
+ return b;
+}
+
+
+
+/* The constant alpha factor describes an alpha factor that gets applied
+ to the result of the composition operation combining it with the destination.
+
+ The intent is that if const_alpha == 0. we get back dest, and if const_alpha == 1.
+ we get the unmodified operation
+
+ result = src op dest
+ dest = result * const_alpha + dest * (1. - const_alpha)
+
+ This means that in the comments below, the first line is the const_alpha==255 case, the
+ second line the general one.
+
+ In the lines below:
+ s == src, sa == alpha(src), sia = 1 - alpha(src)
+ d == dest, da == alpha(dest), dia = 1 - alpha(dest)
+ ca = const_alpha, cia = 1 - const_alpha
+
+ The methods exist in two variants. One where we have a constant source, the other
+ where the source is an array of pixels.
+*/
+
+/*
+ result = 0
+ d = d * cia
+*/
+static void QT_FASTCALL comp_func_solid_Clear(uint *dest, int length, uint, uint const_alpha)
+{
+ if (const_alpha == 255) {
+ QT_MEMFILL_UINT(dest, length, 0);
+ } else {
+ int ialpha = 255 - const_alpha;
+ for (int i = 0; i < length; ++i)
+ dest[i] = BYTE_MUL(dest[i], ialpha);
+ }
+}
+
+static void QT_FASTCALL comp_func_Clear(uint *dest, const uint *, int length, uint const_alpha)
+{
+ if (const_alpha == 255) {
+ QT_MEMFILL_UINT(dest, length, 0);
+ } else {
+ int ialpha = 255 - const_alpha;
+ for (int i = 0; i < length; ++i)
+ dest[i] = BYTE_MUL(dest[i], ialpha);
+ }
+}
+
+/*
+ result = s
+ dest = s * ca + d * cia
+*/
+static void QT_FASTCALL comp_func_solid_Source(uint *dest, int length, uint color, uint const_alpha)
+{
+ if (const_alpha == 255) {
+ QT_MEMFILL_UINT(dest, length, color);
+ } else {
+ int ialpha = 255 - const_alpha;
+ color = BYTE_MUL(color, const_alpha);
+ for (int i = 0; i < length; ++i)
+ dest[i] = color + BYTE_MUL(dest[i], ialpha);
+ }
+}
+
+static void QT_FASTCALL comp_func_Source(uint *dest, const uint *src, int length, uint const_alpha)
+{
+ if (const_alpha == 255) {
+ ::memcpy(dest, src, length * sizeof(uint));
+ } else {
+ int ialpha = 255 - const_alpha;
+ for (int i = 0; i < length; ++i)
+ dest[i] = INTERPOLATE_PIXEL_255(src[i], const_alpha, dest[i], ialpha);
+ }
+}
+
+static void QT_FASTCALL comp_func_solid_Destination(uint *, int, uint, uint)
+{
+}
+
+static void QT_FASTCALL comp_func_Destination(uint *, const uint *, int, uint)
+{
+}
+
+/*
+ result = s + d * sia
+ dest = (s + d * sia) * ca + d * cia
+ = s * ca + d * (sia * ca + cia)
+ = s * ca + d * (1 - sa*ca)
+*/
+static void QT_FASTCALL comp_func_solid_SourceOver(uint *dest, int length, uint color, uint const_alpha)
+{
+ if ((const_alpha & qAlpha(color)) == 255) {
+ QT_MEMFILL_UINT(dest, length, color);
+ } else {
+ if (const_alpha != 255)
+ color = BYTE_MUL(color, const_alpha);
+ for (int i = 0; i < length; ++i)
+ dest[i] = color + BYTE_MUL(dest[i], qAlpha(~color));
+ }
+}
+
+static void QT_FASTCALL comp_func_SourceOver(uint *dest, const uint *src, int length, uint const_alpha)
+{
+ if (const_alpha == 255) {
+ for (int i = 0; i < length; ++i) {
+ uint s = src[i];
+ dest[i] = s + BYTE_MUL(dest[i], qAlpha(~s));
+ }
+ } else {
+ for (int i = 0; i < length; ++i) {
+ uint s = BYTE_MUL(src[i], const_alpha);
+ dest[i] = s + BYTE_MUL(dest[i], qAlpha(~s));
+ }
+ }
+}
+
+/*
+ result = d + s * dia
+ dest = (d + s * dia) * ca + d * cia
+ = d + s * dia * ca
+*/
+static void QT_FASTCALL comp_func_solid_DestinationOver(uint *dest, int length, uint color, uint const_alpha)
+{
+ if (const_alpha != 255)
+ color = BYTE_MUL(color, const_alpha);
+ for (int i = 0; i < length; ++i) {
+ uint d = dest[i];
+ dest[i] = d + BYTE_MUL(color, qAlpha(~d));
+ }
+}
+
+static void QT_FASTCALL comp_func_DestinationOver(uint *dest, const uint *src, int length, uint const_alpha)
+{
+ if (const_alpha == 255) {
+ for (int i = 0; i < length; ++i) {
+ uint d = dest[i];
+ dest[i] = d + BYTE_MUL(src[i], qAlpha(~d));
+ }
+ } else {
+ for (int i = 0; i < length; ++i) {
+ uint d = dest[i];
+ uint s = BYTE_MUL(src[i], const_alpha);
+ dest[i] = d + BYTE_MUL(s, qAlpha(~d));
+ }
+ }
+}
+
+/*
+ result = s * da
+ dest = s * da * ca + d * cia
+*/
+static void QT_FASTCALL comp_func_solid_SourceIn(uint *dest, int length, uint color, uint const_alpha)
+{
+ if (const_alpha == 255) {
+ for (int i = 0; i < length; ++i)
+ dest[i] = BYTE_MUL(color, qAlpha(dest[i]));
+ } else {
+ color = BYTE_MUL(color, const_alpha);
+ uint cia = 255 - const_alpha;
+ for (int i = 0; i < length; ++i) {
+ uint d = dest[i];
+ dest[i] = INTERPOLATE_PIXEL_255(color, qAlpha(d), d, cia);
+ }
+ }
+}
+
+static void QT_FASTCALL comp_func_SourceIn(uint *dest, const uint *src, int length, uint const_alpha)
+{
+ if (const_alpha == 255) {
+ for (int i = 0; i < length; ++i)
+ dest[i] = BYTE_MUL(src[i], qAlpha(dest[i]));
+ } else {
+ uint cia = 255 - const_alpha;
+ for (int i = 0; i < length; ++i) {
+ uint d = dest[i];
+ uint s = BYTE_MUL(src[i], const_alpha);
+ dest[i] = INTERPOLATE_PIXEL_255(s, qAlpha(d), d, cia);
+ }
+ }
+}
+
+/*
+ result = d * sa
+ dest = d * sa * ca + d * cia
+ = d * (sa * ca + cia)
+*/
+static void QT_FASTCALL comp_func_solid_DestinationIn(uint *dest, int length, uint color, uint const_alpha)
+{
+ uint a = qAlpha(color);
+ if (const_alpha != 255) {
+ a = BYTE_MUL(a, const_alpha) + 255 - const_alpha;
+ }
+ for (int i = 0; i < length; ++i) {
+ dest[i] = BYTE_MUL(dest[i], a);
+ }
+}
+
+static void QT_FASTCALL comp_func_DestinationIn(uint *dest, const uint *src, int length, uint const_alpha)
+{
+ if (const_alpha == 255) {
+ for (int i = 0; i < length; ++i)
+ dest[i] = BYTE_MUL(dest[i], qAlpha(src[i]));
+ } else {
+ int cia = 255 - const_alpha;
+ for (int i = 0; i < length; ++i) {
+ uint a = BYTE_MUL(qAlpha(src[i]), const_alpha) + cia;
+ dest[i] = BYTE_MUL(dest[i], a);
+ }
+ }
+}
+
+/*
+ result = s * dia
+ dest = s * dia * ca + d * cia
+*/
+
+static void QT_FASTCALL comp_func_solid_SourceOut(uint *dest, int length, uint color, uint const_alpha)
+{
+ if (const_alpha == 255) {
+ for (int i = 0; i < length; ++i)
+ dest[i] = BYTE_MUL(color, qAlpha(~dest[i]));
+ } else {
+ color = BYTE_MUL(color, const_alpha);
+ int cia = 255 - const_alpha;
+ for (int i = 0; i < length; ++i) {
+ uint d = dest[i];
+ dest[i] = INTERPOLATE_PIXEL_255(color, qAlpha(~d), d, cia);
+ }
+ }
+}
+
+static void QT_FASTCALL comp_func_SourceOut(uint *dest, const uint *src, int length, uint const_alpha)
+{
+ if (const_alpha == 255) {
+ for (int i = 0; i < length; ++i)
+ dest[i] = BYTE_MUL(src[i], qAlpha(~dest[i]));
+ } else {
+ int cia = 255 - const_alpha;
+ for (int i = 0; i < length; ++i) {
+ uint s = BYTE_MUL(src[i], const_alpha);
+ uint d = dest[i];
+ dest[i] = INTERPOLATE_PIXEL_255(s, qAlpha(~d), d, cia);
+ }
+ }
+}
+
+/*
+ result = d * sia
+ dest = d * sia * ca + d * cia
+ = d * (sia * ca + cia)
+*/
+static void QT_FASTCALL comp_func_solid_DestinationOut(uint *dest, int length, uint color, uint const_alpha)
+{
+ uint a = qAlpha(~color);
+ if (const_alpha != 255)
+ a = BYTE_MUL(a, const_alpha) + 255 - const_alpha;
+ for (int i = 0; i < length; ++i)
+ dest[i] = BYTE_MUL(dest[i], a);
+}
+
+static void QT_FASTCALL comp_func_DestinationOut(uint *dest, const uint *src, int length, uint const_alpha)
+{
+ if (const_alpha == 255) {
+ for (int i = 0; i < length; ++i)
+ dest[i] = BYTE_MUL(dest[i], qAlpha(~src[i]));
+ } else {
+ int cia = 255 - const_alpha;
+ for (int i = 0; i < length; ++i) {
+ uint sia = BYTE_MUL(qAlpha(~src[i]), const_alpha) + cia;
+ dest[i] = BYTE_MUL(dest[i], sia);
+ }
+ }
+}
+
+/*
+ result = s*da + d*sia
+ dest = s*da*ca + d*sia*ca + d *cia
+ = s*ca * da + d * (sia*ca + cia)
+ = s*ca * da + d * (1 - sa*ca)
+*/
+static void QT_FASTCALL comp_func_solid_SourceAtop(uint *dest, int length, uint color, uint const_alpha)
+{
+ if (const_alpha != 255) {
+ color = BYTE_MUL(color, const_alpha);
+ }
+ uint sia = qAlpha(~color);
+ for (int i = 0; i < length; ++i)
+ dest[i] = INTERPOLATE_PIXEL_255(color, qAlpha(dest[i]), dest[i], sia);
+}
+
+static void QT_FASTCALL comp_func_SourceAtop(uint *dest, const uint *src, int length, uint const_alpha)
+{
+ if (const_alpha == 255) {
+ for (int i = 0; i < length; ++i) {
+ uint s = src[i];
+ uint d = dest[i];
+ dest[i] = INTERPOLATE_PIXEL_255(s, qAlpha(d), d, qAlpha(~s));
+ }
+ } else {
+ for (int i = 0; i < length; ++i) {
+ uint s = BYTE_MUL(src[i], const_alpha);
+ uint d = dest[i];
+ dest[i] = INTERPOLATE_PIXEL_255(s, qAlpha(d), d, qAlpha(~s));
+ }
+ }
+}
+
+/*
+ result = d*sa + s*dia
+ dest = d*sa*ca + s*dia*ca + d *cia
+ = s*ca * dia + d * (sa*ca + cia)
+*/
+static void QT_FASTCALL comp_func_solid_DestinationAtop(uint *dest, int length, uint color, uint const_alpha)
+{
+ uint a = qAlpha(color);
+ if (const_alpha != 255) {
+ color = BYTE_MUL(color, const_alpha);
+ a = qAlpha(color) + 255 - const_alpha;
+ }
+ for (int i = 0; i < length; ++i) {
+ uint d = dest[i];
+ dest[i] = INTERPOLATE_PIXEL_255(d, a, color, qAlpha(~d));
+ }
+}
+
+static void QT_FASTCALL comp_func_DestinationAtop(uint *dest, const uint *src, int length, uint const_alpha)
+{
+ if (const_alpha == 255) {
+ for (int i = 0; i < length; ++i) {
+ uint s = src[i];
+ uint d = dest[i];
+ dest[i] = INTERPOLATE_PIXEL_255(d, qAlpha(s), s, qAlpha(~d));
+ }
+ } else {
+ int cia = 255 - const_alpha;
+ for (int i = 0; i < length; ++i) {
+ uint s = BYTE_MUL(src[i], const_alpha);
+ uint d = dest[i];
+ uint a = qAlpha(s) + cia;
+ dest[i] = INTERPOLATE_PIXEL_255(d, a, s, qAlpha(~d));
+ }
+ }
+}
+
+/*
+ result = d*sia + s*dia
+ dest = d*sia*ca + s*dia*ca + d *cia
+ = s*ca * dia + d * (sia*ca + cia)
+ = s*ca * dia + d * (1 - sa*ca)
+*/
+static void QT_FASTCALL comp_func_solid_XOR(uint *dest, int length, uint color, uint const_alpha)
+{
+ if (const_alpha != 255)
+ color = BYTE_MUL(color, const_alpha);
+ uint sia = qAlpha(~color);
+
+ for (int i = 0; i < length; ++i) {
+ uint d = dest[i];
+ dest[i] = INTERPOLATE_PIXEL_255(color, qAlpha(~d), d, sia);
+ }
+}
+
+static void QT_FASTCALL comp_func_XOR(uint *dest, const uint *src, int length, uint const_alpha)
+{
+ if (const_alpha == 255) {
+ for (int i = 0; i < length; ++i) {
+ uint d = dest[i];
+ uint s = src[i];
+ dest[i] = INTERPOLATE_PIXEL_255(s, qAlpha(~d), d, qAlpha(~s));
+ }
+ } else {
+ for (int i = 0; i < length; ++i) {
+ uint d = dest[i];
+ uint s = BYTE_MUL(src[i], const_alpha);
+ dest[i] = INTERPOLATE_PIXEL_255(s, qAlpha(~d), d, qAlpha(~s));
+ }
+ }
+}
+
+static const uint AMASK = 0xff000000;
+static const uint RMASK = 0x00ff0000;
+static const uint GMASK = 0x0000ff00;
+static const uint BMASK = 0x000000ff;
+
+struct QFullCoverage {
+ inline void store(uint *dest, const uint src) const
+ {
+ *dest = src;
+ }
+};
+
+struct QPartialCoverage {
+ inline QPartialCoverage(uint const_alpha)
+ : ca(const_alpha)
+ , ica(255 - const_alpha)
+ {
+ }
+
+ inline void store(uint *dest, const uint src) const
+ {
+ *dest = INTERPOLATE_PIXEL_255(src, ca, *dest, ica);
+ }
+
+private:
+ const uint ca;
+ const uint ica;
+};
+
+static inline int mix_alpha(int da, int sa)
+{
+ return 255 - ((255 - sa) * (255 - da) >> 8);
+}
+
+/*
+ Dca' = Sca.Da + Dca.Sa + Sca.(1 - Da) + Dca.(1 - Sa)
+ = Sca + Dca
+*/
+template <typename T>
+Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_solid_Plus_impl(uint *dest, int length, uint color, const T &coverage)
+{
+ uint s = color;
+
+ for (int i = 0; i < length; ++i) {
+ uint d = dest[i];
+#define MIX(mask) (qMin(((qint64(s)&mask) + (qint64(d)&mask)), qint64(mask)))
+ d = (MIX(AMASK) | MIX(RMASK) | MIX(GMASK) | MIX(BMASK));
+#undef MIX
+ coverage.store(&dest[i], d);
+ }
+}
+
+static void QT_FASTCALL comp_func_solid_Plus(uint *dest, int length, uint color, uint const_alpha)
+{
+ if (const_alpha == 255)
+ comp_func_solid_Plus_impl(dest, length, color, QFullCoverage());
+ else
+ comp_func_solid_Plus_impl(dest, length, color, QPartialCoverage(const_alpha));
+}
+
+template <typename T>
+Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_Plus_impl(uint *dest, const uint *src, int length, const T &coverage)
+{
+ for (int i = 0; i < length; ++i) {
+ uint d = dest[i];
+ uint s = src[i];
+
+#define MIX(mask) (qMin(((qint64(s)&mask) + (qint64(d)&mask)), qint64(mask)))
+ d = (MIX(AMASK) | MIX(RMASK) | MIX(GMASK) | MIX(BMASK));
+#undef MIX
+
+ coverage.store(&dest[i], d);
+ }
+}
+
+static void QT_FASTCALL comp_func_Plus(uint *dest, const uint *src, int length, uint const_alpha)
+{
+ if (const_alpha == 255)
+ comp_func_Plus_impl(dest, src, length, QFullCoverage());
+ else
+ comp_func_Plus_impl(dest, src, length, QPartialCoverage(const_alpha));
+}
+
+/*
+ Dca' = Sca.Dca + Sca.(1 - Da) + Dca.(1 - Sa)
+*/
+static inline int multiply_op(int dst, int src, int da, int sa)
+{
+ return qt_div_255(src * dst + src * (255 - da) + dst * (255 - sa));
+}
+
+template <typename T>
+Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_solid_Multiply_impl(uint *dest, int length, uint color, const T &coverage)
+{
+ int sa = qAlpha(color);
+ int sr = qRed(color);
+ int sg = qGreen(color);
+ int sb = qBlue(color);
+
+ for (int i = 0; i < length; ++i) {
+ uint d = dest[i];
+ int da = qAlpha(d);
+
+#define OP(a, b) multiply_op(a, b, da, sa)
+ int r = OP( qRed(d), sr);
+ int b = OP( qBlue(d), sb);
+ int g = OP(qGreen(d), sg);
+ int a = mix_alpha(da, sa);
+#undef OP
+
+ coverage.store(&dest[i], qRgba(r, g, b, a));
+ }
+}
+
+static void QT_FASTCALL comp_func_solid_Multiply(uint *dest, int length, uint color, uint const_alpha)
+{
+ if (const_alpha == 255)
+ comp_func_solid_Multiply_impl(dest, length, color, QFullCoverage());
+ else
+ comp_func_solid_Multiply_impl(dest, length, color, QPartialCoverage(const_alpha));
+}
+
+template <typename T>
+Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_Multiply_impl(uint *dest, const uint *src, int length, const T &coverage)
+{
+ for (int i = 0; i < length; ++i) {
+ uint d = dest[i];
+ uint s = src[i];
+
+ int da = qAlpha(d);
+ int sa = qAlpha(s);
+
+#define OP(a, b) multiply_op(a, b, da, sa)
+ int r = OP( qRed(d), qRed(s));
+ int b = OP( qBlue(d), qBlue(s));
+ int g = OP(qGreen(d), qGreen(s));
+ int a = mix_alpha(da, sa);
+#undef OP
+
+ coverage.store(&dest[i], qRgba(r, g, b, a));
+ }
+}
+
+static void QT_FASTCALL comp_func_Multiply(uint *dest, const uint *src, int length, uint const_alpha)
+{
+ if (const_alpha == 255)
+ comp_func_Multiply_impl(dest, src, length, QFullCoverage());
+ else
+ comp_func_Multiply_impl(dest, src, length, QPartialCoverage(const_alpha));
+}
+
+/*
+ Dca' = (Sca.Da + Dca.Sa - Sca.Dca) + Sca.(1 - Da) + Dca.(1 - Sa)
+ = Sca + Dca - Sca.Dca
+*/
+template <typename T>
+Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_solid_Screen_impl(uint *dest, int length, uint color, const T &coverage)
+{
+ int sa = qAlpha(color);
+ int sr = qRed(color);
+ int sg = qGreen(color);
+ int sb = qBlue(color);
+
+ for (int i = 0; i < length; ++i) {
+ uint d = dest[i];
+ int da = qAlpha(d);
+
+#define OP(a, b) 255 - qt_div_255((255-a) * (255-b))
+ int r = OP( qRed(d), sr);
+ int b = OP( qBlue(d), sb);
+ int g = OP(qGreen(d), sg);
+ int a = mix_alpha(da, sa);
+#undef OP
+
+ coverage.store(&dest[i], qRgba(r, g, b, a));
+ }
+}
+
+static void QT_FASTCALL comp_func_solid_Screen(uint *dest, int length, uint color, uint const_alpha)
+{
+ if (const_alpha == 255)
+ comp_func_solid_Screen_impl(dest, length, color, QFullCoverage());
+ else
+ comp_func_solid_Screen_impl(dest, length, color, QPartialCoverage(const_alpha));
+}
+
+template <typename T>
+Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_Screen_impl(uint *dest, const uint *src, int length, const T &coverage)
+{
+ for (int i = 0; i < length; ++i) {
+ uint d = dest[i];
+ uint s = src[i];
+
+ int da = qAlpha(d);
+ int sa = qAlpha(s);
+
+#define OP(a, b) 255 - (((255-a) * (255-b)) >> 8)
+ int r = OP( qRed(d), qRed(s));
+ int b = OP( qBlue(d), qBlue(s));
+ int g = OP(qGreen(d), qGreen(s));
+ int a = mix_alpha(da, sa);
+#undef OP
+
+ coverage.store(&dest[i], qRgba(r, g, b, a));
+ }
+}
+
+static void QT_FASTCALL comp_func_Screen(uint *dest, const uint *src, int length, uint const_alpha)
+{
+ if (const_alpha == 255)
+ comp_func_Screen_impl(dest, src, length, QFullCoverage());
+ else
+ comp_func_Screen_impl(dest, src, length, QPartialCoverage(const_alpha));
+}
+
+/*
+ if 2.Dca < Da
+ Dca' = 2.Sca.Dca + Sca.(1 - Da) + Dca.(1 - Sa)
+ otherwise
+ Dca' = Sa.Da - 2.(Da - Dca).(Sa - Sca) + Sca.(1 - Da) + Dca.(1 - Sa)
+*/
+static inline int overlay_op(int dst, int src, int da, int sa)
+{
+ const int temp = src * (255 - da) + dst * (255 - sa);
+ if (2 * dst < da)
+ return qt_div_255(2 * src * dst + temp);
+ else
+ return qt_div_255(sa * da - 2 * (da - dst) * (sa - src) + temp);
+}
+
+template <typename T>
+Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_solid_Overlay_impl(uint *dest, int length, uint color, const T &coverage)
+{
+ int sa = qAlpha(color);
+ int sr = qRed(color);
+ int sg = qGreen(color);
+ int sb = qBlue(color);
+
+ for (int i = 0; i < length; ++i) {
+ uint d = dest[i];
+ int da = qAlpha(d);
+
+#define OP(a, b) overlay_op(a, b, da, sa)
+ int r = OP( qRed(d), sr);
+ int b = OP( qBlue(d), sb);
+ int g = OP(qGreen(d), sg);
+ int a = mix_alpha(da, sa);
+#undef OP
+
+ coverage.store(&dest[i], qRgba(r, g, b, a));
+ }
+}
+
+static void QT_FASTCALL comp_func_solid_Overlay(uint *dest, int length, uint color, uint const_alpha)
+{
+ if (const_alpha == 255)
+ comp_func_solid_Overlay_impl(dest, length, color, QFullCoverage());
+ else
+ comp_func_solid_Overlay_impl(dest, length, color, QPartialCoverage(const_alpha));
+}
+
+template <typename T>
+Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_Overlay_impl(uint *dest, const uint *src, int length, const T &coverage)
+{
+ for (int i = 0; i < length; ++i) {
+ uint d = dest[i];
+ uint s = src[i];
+
+ int da = qAlpha(d);
+ int sa = qAlpha(s);
+
+#define OP(a, b) overlay_op(a, b, da, sa)
+ int r = OP( qRed(d), qRed(s));
+ int b = OP( qBlue(d), qBlue(s));
+ int g = OP(qGreen(d), qGreen(s));
+ int a = mix_alpha(da, sa);
+#undef OP
+
+ coverage.store(&dest[i], qRgba(r, g, b, a));
+ }
+}
+
+static void QT_FASTCALL comp_func_Overlay(uint *dest, const uint *src, int length, uint const_alpha)
+{
+ if (const_alpha == 255)
+ comp_func_Overlay_impl(dest, src, length, QFullCoverage());
+ else
+ comp_func_Overlay_impl(dest, src, length, QPartialCoverage(const_alpha));
+}
+
+/*
+ Dca' = min(Sca.Da, Dca.Sa) + Sca.(1 - Da) + Dca.(1 - Sa)
+ Da' = Sa + Da - Sa.Da
+*/
+static inline int darken_op(int dst, int src, int da, int sa)
+{
+ return qt_div_255(qMin(src * da, dst * sa) + src * (255 - da) + dst * (255 - sa));
+}
+
+template <typename T>
+Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_solid_Darken_impl(uint *dest, int length, uint color, const T &coverage)
+{
+ int sa = qAlpha(color);
+ int sr = qRed(color);
+ int sg = qGreen(color);
+ int sb = qBlue(color);
+
+ for (int i = 0; i < length; ++i) {
+ uint d = dest[i];
+ int da = qAlpha(d);
+
+#define OP(a, b) darken_op(a, b, da, sa)
+ int r = OP( qRed(d), sr);
+ int b = OP( qBlue(d), sb);
+ int g = OP(qGreen(d), sg);
+ int a = mix_alpha(da, sa);
+#undef OP
+
+ coverage.store(&dest[i], qRgba(r, g, b, a));
+ }
+}
+
+static void QT_FASTCALL comp_func_solid_Darken(uint *dest, int length, uint color, uint const_alpha)
+{
+ if (const_alpha == 255)
+ comp_func_solid_Darken_impl(dest, length, color, QFullCoverage());
+ else
+ comp_func_solid_Darken_impl(dest, length, color, QPartialCoverage(const_alpha));
+}
+
+template <typename T>
+Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_Darken_impl(uint *dest, const uint *src, int length, const T &coverage)
+{
+ for (int i = 0; i < length; ++i) {
+ uint d = dest[i];
+ uint s = src[i];
+
+ int da = qAlpha(d);
+ int sa = qAlpha(s);
+
+#define OP(a, b) darken_op(a, b, da, sa)
+ int r = OP( qRed(d), qRed(s));
+ int b = OP( qBlue(d), qBlue(s));
+ int g = OP(qGreen(d), qGreen(s));
+ int a = mix_alpha(da, sa);
+#undef OP
+
+ coverage.store(&dest[i], qRgba(r, g, b, a));
+ }
+}
+
+static void QT_FASTCALL comp_func_Darken(uint *dest, const uint *src, int length, uint const_alpha)
+{
+ if (const_alpha == 255)
+ comp_func_Darken_impl(dest, src, length, QFullCoverage());
+ else
+ comp_func_Darken_impl(dest, src, length, QPartialCoverage(const_alpha));
+}
+
+/*
+ Dca' = max(Sca.Da, Dca.Sa) + Sca.(1 - Da) + Dca.(1 - Sa)
+ Da' = Sa + Da - Sa.Da
+*/
+static inline int lighten_op(int dst, int src, int da, int sa)
+{
+ return qt_div_255(qMax(src * da, dst * sa) + src * (255 - da) + dst * (255 - sa));
+}
+
+template <typename T>
+Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_solid_Lighten_impl(uint *dest, int length, uint color, const T &coverage)
+{
+ int sa = qAlpha(color);
+ int sr = qRed(color);
+ int sg = qGreen(color);
+ int sb = qBlue(color);
+
+ for (int i = 0; i < length; ++i) {
+ uint d = dest[i];
+ int da = qAlpha(d);
+
+#define OP(a, b) lighten_op(a, b, da, sa)
+ int r = OP( qRed(d), sr);
+ int b = OP( qBlue(d), sb);
+ int g = OP(qGreen(d), sg);
+ int a = mix_alpha(da, sa);
+#undef OP
+
+ coverage.store(&dest[i], qRgba(r, g, b, a));
+ }
+}
+
+static void QT_FASTCALL comp_func_solid_Lighten(uint *dest, int length, uint color, uint const_alpha)
+{
+ if (const_alpha == 255)
+ comp_func_solid_Lighten_impl(dest, length, color, QFullCoverage());
+ else
+ comp_func_solid_Lighten_impl(dest, length, color, QPartialCoverage(const_alpha));
+}
+
+template <typename T>
+Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_Lighten_impl(uint *dest, const uint *src, int length, const T &coverage)
+{
+ for (int i = 0; i < length; ++i) {
+ uint d = dest[i];
+ uint s = src[i];
+
+ int da = qAlpha(d);
+ int sa = qAlpha(s);
+
+#define OP(a, b) lighten_op(a, b, da, sa)
+ int r = OP( qRed(d), qRed(s));
+ int b = OP( qBlue(d), qBlue(s));
+ int g = OP(qGreen(d), qGreen(s));
+ int a = mix_alpha(da, sa);
+#undef OP
+
+ coverage.store(&dest[i], qRgba(r, g, b, a));
+ }
+}
+
+static void QT_FASTCALL comp_func_Lighten(uint *dest, const uint *src, int length, uint const_alpha)
+{
+ if (const_alpha == 255)
+ comp_func_Lighten_impl(dest, src, length, QFullCoverage());
+ else
+ comp_func_Lighten_impl(dest, src, length, QPartialCoverage(const_alpha));
+}
+
+/*
+ if Sca.Da + Dca.Sa >= Sa.Da
+ Dca' = Sa.Da + Sca.(1 - Da) + Dca.(1 - Sa)
+ otherwise
+ Dca' = Dca.Sa/(1-Sca/Sa) + Sca.(1 - Da) + Dca.(1 - Sa)
+*/
+static inline int color_dodge_op(int dst, int src, int da, int sa)
+{
+ const int sa_da = sa * da;
+ const int dst_sa = dst * sa;
+ const int src_da = src * da;
+
+ const int temp = src * (255 - da) + dst * (255 - sa);
+ if (src_da + dst_sa >= sa_da)
+ return qt_div_255(sa_da + temp);
+ else
+ return qt_div_255(255 * dst_sa / (255 - 255 * src / sa) + temp);
+}
+
+template <typename T>
+Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_solid_ColorDodge_impl(uint *dest, int length, uint color, const T &coverage)
+{
+ int sa = qAlpha(color);
+ int sr = qRed(color);
+ int sg = qGreen(color);
+ int sb = qBlue(color);
+
+ for (int i = 0; i < length; ++i) {
+ uint d = dest[i];
+ int da = qAlpha(d);
+
+#define OP(a,b) color_dodge_op(a, b, da, sa)
+ int r = OP( qRed(d), sr);
+ int b = OP( qBlue(d), sb);
+ int g = OP(qGreen(d), sg);
+ int a = mix_alpha(da, sa);
+#undef OP
+
+ coverage.store(&dest[i], qRgba(r, g, b, a));
+ }
+}
+
+static void QT_FASTCALL comp_func_solid_ColorDodge(uint *dest, int length, uint color, uint const_alpha)
+{
+ if (const_alpha == 255)
+ comp_func_solid_ColorDodge_impl(dest, length, color, QFullCoverage());
+ else
+ comp_func_solid_ColorDodge_impl(dest, length, color, QPartialCoverage(const_alpha));
+}
+
+template <typename T>
+Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_ColorDodge_impl(uint *dest, const uint *src, int length, const T &coverage)
+{
+ for (int i = 0; i < length; ++i) {
+ uint d = dest[i];
+ uint s = src[i];
+
+ int da = qAlpha(d);
+ int sa = qAlpha(s);
+
+#define OP(a, b) color_dodge_op(a, b, da, sa)
+ int r = OP( qRed(d), qRed(s));
+ int b = OP( qBlue(d), qBlue(s));
+ int g = OP(qGreen(d), qGreen(s));
+ int a = mix_alpha(da, sa);
+#undef OP
+
+ coverage.store(&dest[i], qRgba(r, g, b, a));
+ }
+}
+
+static void QT_FASTCALL comp_func_ColorDodge(uint *dest, const uint *src, int length, uint const_alpha)
+{
+ if (const_alpha == 255)
+ comp_func_ColorDodge_impl(dest, src, length, QFullCoverage());
+ else
+ comp_func_ColorDodge_impl(dest, src, length, QPartialCoverage(const_alpha));
+}
+
+/*
+ if Sca.Da + Dca.Sa <= Sa.Da
+ Dca' = Sca.(1 - Da) + Dca.(1 - Sa)
+ otherwise
+ Dca' = Sa.(Sca.Da + Dca.Sa - Sa.Da)/Sca + Sca.(1 - Da) + Dca.(1 - Sa)
+*/
+static inline int color_burn_op(int dst, int src, int da, int sa)
+{
+ const int src_da = src * da;
+ const int dst_sa = dst * sa;
+ const int sa_da = sa * da;
+
+ const int temp = src * (255 - da) + dst * (255 - sa);
+
+ if (src == 0 || src_da + dst_sa <= sa_da)
+ return qt_div_255(temp);
+ else
+ return qt_div_255(sa * (src_da + dst_sa - sa_da) / src + temp);
+}
+
+template <typename T>
+Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_solid_ColorBurn_impl(uint *dest, int length, uint color, const T &coverage)
+{
+ int sa = qAlpha(color);
+ int sr = qRed(color);
+ int sg = qGreen(color);
+ int sb = qBlue(color);
+
+ for (int i = 0; i < length; ++i) {
+ uint d = dest[i];
+ int da = qAlpha(d);
+
+#define OP(a, b) color_burn_op(a, b, da, sa)
+ int r = OP( qRed(d), sr);
+ int b = OP( qBlue(d), sb);
+ int g = OP(qGreen(d), sg);
+ int a = mix_alpha(da, sa);
+#undef OP
+
+ coverage.store(&dest[i], qRgba(r, g, b, a));
+ }
+}
+
+static void QT_FASTCALL comp_func_solid_ColorBurn(uint *dest, int length, uint color, uint const_alpha)
+{
+ if (const_alpha == 255)
+ comp_func_solid_ColorBurn_impl(dest, length, color, QFullCoverage());
+ else
+ comp_func_solid_ColorBurn_impl(dest, length, color, QPartialCoverage(const_alpha));
+}
+
+template <typename T>
+Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_ColorBurn_impl(uint *dest, const uint *src, int length, const T &coverage)
+{
+ for (int i = 0; i < length; ++i) {
+ uint d = dest[i];
+ uint s = src[i];
+
+ int da = qAlpha(d);
+ int sa = qAlpha(s);
+
+#define OP(a, b) color_burn_op(a, b, da, sa)
+ int r = OP( qRed(d), qRed(s));
+ int b = OP( qBlue(d), qBlue(s));
+ int g = OP(qGreen(d), qGreen(s));
+ int a = mix_alpha(da, sa);
+#undef OP
+
+ coverage.store(&dest[i], qRgba(r, g, b, a));
+ }
+}
+
+static void QT_FASTCALL comp_func_ColorBurn(uint *dest, const uint *src, int length, uint const_alpha)
+{
+ if (const_alpha == 255)
+ comp_func_ColorBurn_impl(dest, src, length, QFullCoverage());
+ else
+ comp_func_ColorBurn_impl(dest, src, length, QPartialCoverage(const_alpha));
+}
+
+/*
+ if 2.Sca < Sa
+ Dca' = 2.Sca.Dca + Sca.(1 - Da) + Dca.(1 - Sa)
+ otherwise
+ Dca' = Sa.Da - 2.(Da - Dca).(Sa - Sca) + Sca.(1 - Da) + Dca.(1 - Sa)
+*/
+static inline uint hardlight_op(int dst, int src, int da, int sa)
+{
+ const uint temp = src * (255 - da) + dst * (255 - sa);
+
+ if (2 * src < sa)
+ return qt_div_255(2 * src * dst + temp);
+ else
+ return qt_div_255(sa * da - 2 * (da - dst) * (sa - src) + temp);
+}
+
+template <typename T>
+Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_solid_HardLight_impl(uint *dest, int length, uint color, const T &coverage)
+{
+ int sa = qAlpha(color);
+ int sr = qRed(color);
+ int sg = qGreen(color);
+ int sb = qBlue(color);
+
+ for (int i = 0; i < length; ++i) {
+ uint d = dest[i];
+ int da = qAlpha(d);
+
+#define OP(a, b) hardlight_op(a, b, da, sa)
+ int r = OP( qRed(d), sr);
+ int b = OP( qBlue(d), sb);
+ int g = OP(qGreen(d), sg);
+ int a = mix_alpha(da, sa);
+#undef OP
+
+ coverage.store(&dest[i], qRgba(r, g, b, a));
+ }
+}
+
+static void QT_FASTCALL comp_func_solid_HardLight(uint *dest, int length, uint color, uint const_alpha)
+{
+ if (const_alpha == 255)
+ comp_func_solid_HardLight_impl(dest, length, color, QFullCoverage());
+ else
+ comp_func_solid_HardLight_impl(dest, length, color, QPartialCoverage(const_alpha));
+}
+
+template <typename T>
+Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_HardLight_impl(uint *dest, const uint *src, int length, const T &coverage)
+{
+ for (int i = 0; i < length; ++i) {
+ uint d = dest[i];
+ uint s = src[i];
+
+ int da = qAlpha(d);
+ int sa = qAlpha(s);
+
+#define OP(a, b) hardlight_op(a, b, da, sa)
+ int r = OP( qRed(d), qRed(s));
+ int b = OP( qBlue(d), qBlue(s));
+ int g = OP(qGreen(d), qGreen(s));
+ int a = mix_alpha(da, sa);
+#undef OP
+
+ coverage.store(&dest[i], qRgba(r, g, b, a));
+ }
+}
+
+static void QT_FASTCALL comp_func_HardLight(uint *dest, const uint *src, int length, uint const_alpha)
+{
+ if (const_alpha == 255)
+ comp_func_HardLight_impl(dest, src, length, QFullCoverage());
+ else
+ comp_func_HardLight_impl(dest, src, length, QPartialCoverage(const_alpha));
+}
+
+/*
+ if 2.Sca < Sa
+ Dca' = Dca.(Sa - (1 - Dca/Da).(2.Sca - Sa)) + Sca.(1 - Da) + Dca.(1 - Sa)
+ otherwise if 8.Dca <= Da
+ Dca' = Dca.(Sa - (1 - Dca/Da).(2.Sca - Sa).(3 - 8.Dca/Da)) + Sca.(1 - Da) + Dca.(1 - Sa)
+ otherwise
+ Dca' = (Dca.Sa + ((Dca/Da)^(0.5).Da - Dca).(2.Sca - Sa)) + Sca.(1 - Da) + Dca.(1 - Sa)
+*/
+static inline int soft_light_op(int dst, int src, int da, int sa)
+{
+ const int src2 = src << 1;
+ const int dst_np = da != 0 ? (255 * dst) / da : 0;
+ const int temp = (src * (255 - da) + dst * (255 - sa)) * 255;
+
+ if (src2 < sa)
+ return (dst * ((sa * 255) - (255 - dst_np) * (src2 - sa)) + temp) / 65025;
+ else if (8 * dst <= da)
+ return (dst * ((sa * 255) - ((255 - dst_np) * (src2 - sa) * ((3 * 255) - 8 * dst_np)) / 255) + temp) / 65025;
+ else {
+ // sqrt is too expensive to do three times per pixel, so skipping it for now
+ // a future possibility is to use a LUT
+ return ((dst * sa * 255) + (int(dst_np) * da - (dst * 255)) * (src2 - sa) + temp) / 65025;
+ }
+}
+
+template <typename T>
+Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_solid_SoftLight_impl(uint *dest, int length, uint color, const T &coverage)
+{
+ int sa = qAlpha(color);
+ int sr = qRed(color);
+ int sg = qGreen(color);
+ int sb = qBlue(color);
+
+ for (int i = 0; i < length; ++i) {
+ uint d = dest[i];
+ int da = qAlpha(d);
+
+#define OP(a, b) soft_light_op(a, b, da, sa)
+ int r = OP( qRed(d), sr);
+ int b = OP( qBlue(d), sb);
+ int g = OP(qGreen(d), sg);
+ int a = mix_alpha(da, sa);
+#undef OP
+
+ coverage.store(&dest[i], qRgba(r, g, b, a));
+ }
+}
+
+static void QT_FASTCALL comp_func_solid_SoftLight(uint *dest, int length, uint color, uint const_alpha)
+{
+ if (const_alpha == 255)
+ comp_func_solid_SoftLight_impl(dest, length, color, QFullCoverage());
+ else
+ comp_func_solid_SoftLight_impl(dest, length, color, QPartialCoverage(const_alpha));
+}
+
+template <typename T>
+Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_SoftLight_impl(uint *dest, const uint *src, int length, const T &coverage)
+{
+ for (int i = 0; i < length; ++i) {
+ uint d = dest[i];
+ uint s = src[i];
+
+ int da = qAlpha(d);
+ int sa = qAlpha(s);
+
+#define OP(a, b) soft_light_op(a, b, da, sa)
+ int r = OP( qRed(d), qRed(s));
+ int b = OP( qBlue(d), qBlue(s));
+ int g = OP(qGreen(d), qGreen(s));
+ int a = mix_alpha(da, sa);
+#undef OP
+
+ coverage.store(&dest[i], qRgba(r, g, b, a));
+ }
+}
+
+static void QT_FASTCALL comp_func_SoftLight(uint *dest, const uint *src, int length, uint const_alpha)
+{
+ if (const_alpha == 255)
+ comp_func_SoftLight_impl(dest, src, length, QFullCoverage());
+ else
+ comp_func_SoftLight_impl(dest, src, length, QPartialCoverage(const_alpha));
+}
+
+/*
+ Dca' = abs(Dca.Sa - Sca.Da) + Sca.(1 - Da) + Dca.(1 - Sa)
+ = Sca + Dca - 2.min(Sca.Da, Dca.Sa)
+*/
+static inline int difference_op(int dst, int src, int da, int sa)
+{
+ return src + dst - qt_div_255(2 * qMin(src * da, dst * sa));
+}
+
+template <typename T>
+Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_solid_Difference_impl(uint *dest, int length, uint color, const T &coverage)
+{
+ int sa = qAlpha(color);
+ int sr = qRed(color);
+ int sg = qGreen(color);
+ int sb = qBlue(color);
+
+ for (int i = 0; i < length; ++i) {
+ uint d = dest[i];
+ int da = qAlpha(d);
+
+#define OP(a, b) difference_op(a, b, da, sa)
+ int r = OP( qRed(d), sr);
+ int b = OP( qBlue(d), sb);
+ int g = OP(qGreen(d), sg);
+ int a = mix_alpha(da, sa);
+#undef OP
+
+ coverage.store(&dest[i], qRgba(r, g, b, a));
+ }
+}
+
+static void QT_FASTCALL comp_func_solid_Difference(uint *dest, int length, uint color, uint const_alpha)
+{
+ if (const_alpha == 255)
+ comp_func_solid_Difference_impl(dest, length, color, QFullCoverage());
+ else
+ comp_func_solid_Difference_impl(dest, length, color, QPartialCoverage(const_alpha));
+}
+
+template <typename T>
+Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_Difference_impl(uint *dest, const uint *src, int length, const T &coverage)
+{
+ for (int i = 0; i < length; ++i) {
+ uint d = dest[i];
+ uint s = src[i];
+
+ int da = qAlpha(d);
+ int sa = qAlpha(s);
+
+#define OP(a, b) difference_op(a, b, da, sa)
+ int r = OP( qRed(d), qRed(s));
+ int b = OP( qBlue(d), qBlue(s));
+ int g = OP(qGreen(d), qGreen(s));
+ int a = mix_alpha(da, sa);
+#undef OP
+
+ coverage.store(&dest[i], qRgba(r, g, b, a));
+ }
+}
+
+static void QT_FASTCALL comp_func_Difference(uint *dest, const uint *src, int length, uint const_alpha)
+{
+ if (const_alpha == 255)
+ comp_func_Difference_impl(dest, src, length, QFullCoverage());
+ else
+ comp_func_Difference_impl(dest, src, length, QPartialCoverage(const_alpha));
+}
+
+/*
+ Dca' = (Sca.Da + Dca.Sa - 2.Sca.Dca) + Sca.(1 - Da) + Dca.(1 - Sa)
+*/
+template <typename T>
+Q_STATIC_TEMPLATE_FUNCTION inline void QT_FASTCALL comp_func_solid_Exclusion_impl(uint *dest, int length, uint color, const T &coverage)
+{
+ int sa = qAlpha(color);
+ int sr = qRed(color);
+ int sg = qGreen(color);
+ int sb = qBlue(color);
+
+ for (int i = 0; i < length; ++i) {
+ uint d = dest[i];
+ int da = qAlpha(d);
+
+#define OP(a, b) (a + b - qt_div_255(2*(a*b)))
+ int r = OP( qRed(d), sr);
+ int b = OP( qBlue(d), sb);
+ int g = OP(qGreen(d), sg);
+ int a = mix_alpha(da, sa);
+#undef OP
+
+ coverage.store(&dest[i], qRgba(r, g, b, a));
+ }
+}
+
+static void QT_FASTCALL comp_func_solid_Exclusion(uint *dest, int length, uint color, uint const_alpha)
+{
+ if (const_alpha == 255)
+ comp_func_solid_Exclusion_impl(dest, length, color, QFullCoverage());
+ else
+ comp_func_solid_Exclusion_impl(dest, length, color, QPartialCoverage(const_alpha));
+}
+
+template <typename T>
+Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_Exclusion_impl(uint *dest, const uint *src, int length, const T &coverage)
+{
+ for (int i = 0; i < length; ++i) {
+ uint d = dest[i];
+ uint s = src[i];
+
+ int da = qAlpha(d);
+ int sa = qAlpha(s);
+
+#define OP(a, b) (a + b - ((a*b) >> 7))
+ int r = OP( qRed(d), qRed(s));
+ int b = OP( qBlue(d), qBlue(s));
+ int g = OP(qGreen(d), qGreen(s));
+ int a = mix_alpha(da, sa);
+#undef OP
+
+ coverage.store(&dest[i], qRgba(r, g, b, a));
+ }
+}
+
+static void QT_FASTCALL comp_func_Exclusion(uint *dest, const uint *src, int length, uint const_alpha)
+{
+ if (const_alpha == 255)
+ comp_func_Exclusion_impl(dest, src, length, QFullCoverage());
+ else
+ comp_func_Exclusion_impl(dest, src, length, QPartialCoverage(const_alpha));
+}
+
+static void QT_FASTCALL rasterop_solid_SourceOrDestination(uint *dest,
+ int length,
+ uint color,
+ uint const_alpha)
+{
+ Q_UNUSED(const_alpha);
+ while (length--)
+ *dest++ |= color;
+}
+
+static void QT_FASTCALL rasterop_SourceOrDestination(uint *dest,
+ const uint *src,
+ int length,
+ uint const_alpha)
+{
+ Q_UNUSED(const_alpha);
+ while (length--)
+ *dest++ |= *src++;
+}
+
+static void QT_FASTCALL rasterop_solid_SourceAndDestination(uint *dest,
+ int length,
+ uint color,
+ uint const_alpha)
+{
+ Q_UNUSED(const_alpha);
+ color |= 0xff000000;
+ while (length--)
+ *dest++ &= color;
+}
+
+static void QT_FASTCALL rasterop_SourceAndDestination(uint *dest,
+ const uint *src,
+ int length,
+ uint const_alpha)
+{
+ Q_UNUSED(const_alpha);
+ while (length--) {
+ *dest = (*src & *dest) | 0xff000000;
+ ++dest; ++src;
+ }
+}
+
+static void QT_FASTCALL rasterop_solid_SourceXorDestination(uint *dest,
+ int length,
+ uint color,
+ uint const_alpha)
+{
+ Q_UNUSED(const_alpha);
+ color &= 0x00ffffff;
+ while (length--)
+ *dest++ ^= color;
+}
+
+static void QT_FASTCALL rasterop_SourceXorDestination(uint *dest,
+ const uint *src,
+ int length,
+ uint const_alpha)
+{
+ Q_UNUSED(const_alpha);
+ while (length--) {
+ *dest = (*src ^ *dest) | 0xff000000;
+ ++dest; ++src;
+ }
+}
+
+static void QT_FASTCALL rasterop_solid_NotSourceAndNotDestination(uint *dest,
+ int length,
+ uint color,
+ uint const_alpha)
+{
+ Q_UNUSED(const_alpha);
+ color = ~color;
+ while (length--) {
+ *dest = (color & ~(*dest)) | 0xff000000;
+ ++dest;
+ }
+}
+
+static void QT_FASTCALL rasterop_NotSourceAndNotDestination(uint *dest,
+ const uint *src,
+ int length,
+ uint const_alpha)
+{
+ Q_UNUSED(const_alpha);
+ while (length--) {
+ *dest = (~(*src) & ~(*dest)) | 0xff000000;
+ ++dest; ++src;
+ }
+}
+
+static void QT_FASTCALL rasterop_solid_NotSourceOrNotDestination(uint *dest,
+ int length,
+ uint color,
+ uint const_alpha)
+{
+ Q_UNUSED(const_alpha);
+ color = ~color | 0xff000000;
+ while (length--) {
+ *dest = color | ~(*dest);
+ ++dest;
+ }
+}
+
+static void QT_FASTCALL rasterop_NotSourceOrNotDestination(uint *dest,
+ const uint *src,
+ int length,
+ uint const_alpha)
+{
+ Q_UNUSED(const_alpha);
+ while (length--) {
+ *dest = ~(*src) | ~(*dest) | 0xff000000;
+ ++dest; ++src;
+ }
+}
+
+static void QT_FASTCALL rasterop_solid_NotSourceXorDestination(uint *dest,
+ int length,
+ uint color,
+ uint const_alpha)
+{
+ Q_UNUSED(const_alpha);
+ color = ~color & 0x00ffffff;
+ while (length--) {
+ *dest = color ^ (*dest);
+ ++dest;
+ }
+}
+
+static void QT_FASTCALL rasterop_NotSourceXorDestination(uint *dest,
+ const uint *src,
+ int length,
+ uint const_alpha)
+{
+ Q_UNUSED(const_alpha);
+ while (length--) {
+ *dest = ((~(*src)) ^ (*dest)) | 0xff000000;
+ ++dest; ++src;
+ }
+}
+
+static void QT_FASTCALL rasterop_solid_NotSource(uint *dest, int length,
+ uint color, uint const_alpha)
+{
+ Q_UNUSED(const_alpha);
+ qt_memfill(dest, ~color | 0xff000000, length);
+}
+
+static void QT_FASTCALL rasterop_NotSource(uint *dest, const uint *src,
+ int length, uint const_alpha)
+{
+ Q_UNUSED(const_alpha);
+ while (length--)
+ *dest++ = ~(*src++) | 0xff000000;
+}
+
+static void QT_FASTCALL rasterop_solid_NotSourceAndDestination(uint *dest,
+ int length,
+ uint color,
+ uint const_alpha)
+{
+ Q_UNUSED(const_alpha);
+ color = ~color | 0xff000000;
+ while (length--) {
+ *dest = color & *dest;
+ ++dest;
+ }
+}
+
+static void QT_FASTCALL rasterop_NotSourceAndDestination(uint *dest,
+ const uint *src,
+ int length,
+ uint const_alpha)
+{
+ Q_UNUSED(const_alpha);
+ while (length--) {
+ *dest = (~(*src) & *dest) | 0xff000000;
+ ++dest; ++src;
+ }
+}
+
+static void QT_FASTCALL rasterop_solid_SourceAndNotDestination(uint *dest,
+ int length,
+ uint color,
+ uint const_alpha)
+{
+ Q_UNUSED(const_alpha);
+ while (length--) {
+ *dest = (color & ~(*dest)) | 0xff000000;
+ ++dest;
+ }
+}
+
+static void QT_FASTCALL rasterop_SourceAndNotDestination(uint *dest,
+ const uint *src,
+ int length,
+ uint const_alpha)
+{
+ Q_UNUSED(const_alpha);
+ while (length--) {
+ *dest = (*src & ~(*dest)) | 0xff000000;
+ ++dest; ++src;
+ }
+}
+
+static const CompositionFunctionSolid functionForModeSolid_C[] = {
+ comp_func_solid_SourceOver,
+ comp_func_solid_DestinationOver,
+ comp_func_solid_Clear,
+ comp_func_solid_Source,
+ comp_func_solid_Destination,
+ comp_func_solid_SourceIn,
+ comp_func_solid_DestinationIn,
+ comp_func_solid_SourceOut,
+ comp_func_solid_DestinationOut,
+ comp_func_solid_SourceAtop,
+ comp_func_solid_DestinationAtop,
+ comp_func_solid_XOR,
+ comp_func_solid_Plus,
+ comp_func_solid_Multiply,
+ comp_func_solid_Screen,
+ comp_func_solid_Overlay,
+ comp_func_solid_Darken,
+ comp_func_solid_Lighten,
+ comp_func_solid_ColorDodge,
+ comp_func_solid_ColorBurn,
+ comp_func_solid_HardLight,
+ comp_func_solid_SoftLight,
+ comp_func_solid_Difference,
+ comp_func_solid_Exclusion,
+ rasterop_solid_SourceOrDestination,
+ rasterop_solid_SourceAndDestination,
+ rasterop_solid_SourceXorDestination,
+ rasterop_solid_NotSourceAndNotDestination,
+ rasterop_solid_NotSourceOrNotDestination,
+ rasterop_solid_NotSourceXorDestination,
+ rasterop_solid_NotSource,
+ rasterop_solid_NotSourceAndDestination,
+ rasterop_solid_SourceAndNotDestination
+};
+
+static const CompositionFunctionSolid *functionForModeSolid = functionForModeSolid_C;
+
+static const CompositionFunction functionForMode_C[] = {
+ comp_func_SourceOver,
+ comp_func_DestinationOver,
+ comp_func_Clear,
+ comp_func_Source,
+ comp_func_Destination,
+ comp_func_SourceIn,
+ comp_func_DestinationIn,
+ comp_func_SourceOut,
+ comp_func_DestinationOut,
+ comp_func_SourceAtop,
+ comp_func_DestinationAtop,
+ comp_func_XOR,
+ comp_func_Plus,
+ comp_func_Multiply,
+ comp_func_Screen,
+ comp_func_Overlay,
+ comp_func_Darken,
+ comp_func_Lighten,
+ comp_func_ColorDodge,
+ comp_func_ColorBurn,
+ comp_func_HardLight,
+ comp_func_SoftLight,
+ comp_func_Difference,
+ comp_func_Exclusion,
+ rasterop_SourceOrDestination,
+ rasterop_SourceAndDestination,
+ rasterop_SourceXorDestination,
+ rasterop_NotSourceAndNotDestination,
+ rasterop_NotSourceOrNotDestination,
+ rasterop_NotSourceXorDestination,
+ rasterop_NotSource,
+ rasterop_NotSourceAndDestination,
+ rasterop_SourceAndNotDestination
+};
+
+static const CompositionFunction *functionForMode = functionForMode_C;
+
+static TextureBlendType getBlendType(const QSpanData *data)
+{
+ TextureBlendType ft;
+ if (data->txop <= QTransform::TxTranslate)
+ if (data->texture.type == QTextureData::Tiled)
+ ft = BlendTiled;
+ else
+ ft = BlendUntransformed;
+ else if (data->bilinear)
+ if (data->texture.type == QTextureData::Tiled)
+ ft = BlendTransformedBilinearTiled;
+ else
+ ft = BlendTransformedBilinear;
+ else
+ if (data->texture.type == QTextureData::Tiled)
+ ft = BlendTransformedTiled;
+ else
+ ft = BlendTransformed;
+ return ft;
+}
+
+static inline Operator getOperator(const QSpanData *data, const QSpan *spans, int spanCount)
+{
+ Operator op;
+ bool solidSource = false;
+
+ switch(data->type) {
+ case QSpanData::Solid:
+ solidSource = (qAlpha(data->solid.color) == 255);
+ break;
+ case QSpanData::LinearGradient:
+ solidSource = !data->gradient.alphaColor;
+ getLinearGradientValues(&op.linear, data);
+ op.src_fetch = fetchLinearGradient;
+ break;
+ case QSpanData::RadialGradient:
+ solidSource = !data->gradient.alphaColor;
+ getRadialGradientValues(&op.radial, data);
+ op.src_fetch = fetchRadialGradient;
+ break;
+ case QSpanData::ConicalGradient:
+ solidSource = !data->gradient.alphaColor;
+ op.src_fetch = fetchConicalGradient;
+ break;
+ case QSpanData::Texture:
+ op.src_fetch = sourceFetch[getBlendType(data)][data->texture.format];
+ solidSource = !data->texture.hasAlpha;
+ default:
+ break;
+ }
+
+ op.mode = data->rasterBuffer->compositionMode;
+ if (op.mode == QPainter::CompositionMode_SourceOver && solidSource)
+ op.mode = QPainter::CompositionMode_Source;
+
+ op.dest_fetch = destFetchProc[data->rasterBuffer->format];
+ if (op.mode == QPainter::CompositionMode_Source) {
+ switch (data->rasterBuffer->format) {
+ case QImage::Format_RGB32:
+ case QImage::Format_ARGB32_Premultiplied:
+ // don't clear dest_fetch as it sets up the pointer correctly to save one copy
+ break;
+ default: {
+ const QSpan *lastSpan = spans + spanCount;
+ bool alphaSpans = false;
+ while (spans < lastSpan) {
+ if (spans->coverage != 255) {
+ alphaSpans = true;
+ break;
+ }
+ ++spans;
+ }
+ if (!alphaSpans)
+ op.dest_fetch = 0;
+ }
+ }
+ }
+
+ op.dest_store = destStoreProc[data->rasterBuffer->format];
+
+ op.funcSolid = functionForModeSolid[op.mode];
+ op.func = functionForMode[op.mode];
+
+ return op;
+}
+
+
+
+// -------------------- blend methods ---------------------
+
+enum SpanMethod {
+ RegularSpans,
+ CallbackSpans
+};
+
+#if !defined(Q_CC_SUN)
+static
+#endif
+void drawBufferSpan(QSpanData *data, const uint *buffer, int bufsize,
+ int x, int y, int length, uint const_alpha)
+{
+#if defined (Q_WS_QWS) && !defined(QT_NO_RASTERCALLBACKS)
+ data->rasterEngine->drawBufferSpan(buffer, bufsize, x, y, length, const_alpha);
+#else
+ Q_UNUSED(data);
+ Q_UNUSED(buffer);
+ Q_UNUSED(bufsize);
+ Q_UNUSED(x);
+ Q_UNUSED(y);
+ Q_UNUSED(length);
+ Q_UNUSED(const_alpha);
+#endif
+}
+
+#if !defined(Q_CC_SUN)
+static
+#endif
+void blend_color_generic(int count, const QSpan *spans, void *userData)
+{
+ QSpanData *data = reinterpret_cast<QSpanData *>(userData);
+ uint buffer[buffer_size];
+ Operator op = getOperator(data, spans, count);
+
+ while (count--) {
+ int x = spans->x;
+ int length = spans->len;
+ while (length) {
+ int l = qMin(buffer_size, length);
+ uint *dest = op.dest_fetch ? op.dest_fetch(buffer, data->rasterBuffer, x, spans->y, l) : buffer;
+ op.funcSolid(dest, l, data->solid.color, spans->coverage);
+ if (op.dest_store)
+ op.dest_store(data->rasterBuffer, x, spans->y, dest, l);
+ length -= l;
+ x += l;
+ }
+ ++spans;
+ }
+}
+
+#if defined (Q_WS_QWS) && !defined(QT_NO_RASTERCALLBACKS)
+static void blend_color_generic_callback(int count, const QSpan *spans, void *userData)
+{
+ // ### Falcon
+ Q_UNUSED(count);
+ Q_UNUSED(spans);
+ Q_UNUSED(userData);
+// QSpanData *data = reinterpret_cast<QSpanData*>(userData);
+// data->rasterEngine->drawColorSpans(spans, count, data->solid.color);
+}
+#endif // QT_NO_RASTERCALLBACKS
+
+static void blend_color_argb(int count, const QSpan *spans, void *userData)
+{
+ QSpanData *data = reinterpret_cast<QSpanData *>(userData);
+
+ Operator op = getOperator(data, spans, count);
+
+ if (op.mode == QPainter::CompositionMode_Source) {
+ // inline for performance
+ while (count--) {
+ uint *target = ((uint *)data->rasterBuffer->scanLine(spans->y)) + spans->x;
+ if (spans->coverage == 255) {
+ QT_MEMFILL_UINT(target, spans->len, data->solid.color);
+ } else {
+ uint c = BYTE_MUL(data->solid.color, spans->coverage);
+ int ialpha = 255 - spans->coverage;
+ for (int i = 0; i < spans->len; ++i)
+ target[i] = c + BYTE_MUL(target[i], ialpha);
+ }
+ ++spans;
+ }
+ return;
+ }
+
+ while (count--) {
+ uint *target = ((uint *)data->rasterBuffer->scanLine(spans->y)) + spans->x;
+ op.funcSolid(target, spans->len, data->solid.color, spans->coverage);
+ ++spans;
+ }
+}
+
+template <class T>
+Q_STATIC_TEMPLATE_FUNCTION void blendColor(int count, const QSpan *spans, void *userData
+ Q_TEMPLATE_FIX(T))
+{
+ QSpanData *data = reinterpret_cast<QSpanData *>(userData);
+ Operator op = getOperator(data, spans, count);
+
+ if (op.mode == QPainter::CompositionMode_Source) {
+ const T c = qt_colorConvert<T, quint32p>(quint32p::fromRawData(data->solid.color), 0);
+ while (count--) {
+ T *target = ((T*)data->rasterBuffer->scanLine(spans->y))
+ + spans->x;
+ if (spans->coverage == 255) {
+ qt_memfill(target, c, spans->len);
+ } else {
+ const quint8 alpha = T::alpha(spans->coverage);
+ const T color = c.byte_mul(alpha);
+ const int ialpha = T::ialpha(spans->coverage);
+ const T *end = target + spans->len;
+ while (target < end) {
+ *target = color + target->byte_mul(ialpha);
+ ++target;
+ }
+ }
+ ++spans;
+ }
+ return;
+ }
+
+ if (op.mode == QPainter::CompositionMode_SourceOver) {
+ while (count--) {
+ const quint32 color = BYTE_MUL(data->solid.color, spans->coverage);
+ const T c = qt_colorConvert<T, quint32p>(quint32p::fromRawData(color), 0);
+ const quint8 ialpha = T::alpha(qAlpha(~color));
+ T *target = ((T*)data->rasterBuffer->scanLine(spans->y)) + spans->x;
+ const T *end = target + spans->len;
+ while (target != end) {
+ *target = c + target->byte_mul(ialpha);
+ ++target;
+ }
+ ++spans;
+ }
+ return;
+ }
+
+ blend_color_generic(count, spans, userData);
+}
+
+#if defined(Q_CC_MSVC) && _MSC_VER <= 1300 && !defined(Q_CC_INTEL)
+#define BLEND_COLOR_DECL(DST) \
+ static void blendColor_##DST(int count, \
+ const QSpan *spans, \
+ void *userData) \
+ { \
+ blendColor<DST>(count, spans, userData Q_TEMPLATE_CALL(DST)); \
+ }
+
+BLEND_COLOR_DECL(qargb8565)
+BLEND_COLOR_DECL(qrgb666)
+BLEND_COLOR_DECL(qargb6666)
+BLEND_COLOR_DECL(qrgb555)
+BLEND_COLOR_DECL(qargb8555)
+BLEND_COLOR_DECL(qrgb888)
+BLEND_COLOR_DECL(qrgb444)
+BLEND_COLOR_DECL(qargb4444)
+#undef DEST_FETCH_DECL
+#define SPANFUNC_POINTER_BLENDCOLOR(DST) blendColor_##DST
+#else // !VC6 && !VC2002
+# define SPANFUNC_POINTER_BLENDCOLOR(DST) blendColor<DST>
+#endif
+
+static void blend_color_rgb16(int count, const QSpan *spans, void *userData)
+{
+ QSpanData *data = reinterpret_cast<QSpanData *>(userData);
+
+ /*
+ We duplicate a little logic from getOperator() and calculate the
+ composition mode directly. This allows blend_color_rgb16 to be used
+ from qt_gradient_quint16 with minimal overhead.
+ */
+ QPainter::CompositionMode mode = data->rasterBuffer->compositionMode;
+ if (mode == QPainter::CompositionMode_SourceOver &&
+ qAlpha(data->solid.color) == 255)
+ mode = QPainter::CompositionMode_Source;
+
+ if (mode == QPainter::CompositionMode_Source) {
+ // inline for performance
+ ushort c = qConvertRgb32To16(data->solid.color);
+ while (count--) {
+ ushort *target = ((ushort *)data->rasterBuffer->scanLine(spans->y)) + spans->x;
+ if (spans->coverage == 255) {
+ QT_MEMFILL_USHORT(target, spans->len, c);
+ } else {
+ ushort color = BYTE_MUL_RGB16(c, spans->coverage);
+ int ialpha = 255 - spans->coverage;
+ const ushort *end = target + spans->len;
+ while (target < end) {
+ *target = color + BYTE_MUL_RGB16(*target, ialpha);
+ ++target;
+ }
+ }
+ ++spans;
+ }
+ return;
+ }
+
+ if (mode == QPainter::CompositionMode_SourceOver) {
+ while (count--) {
+ uint color = BYTE_MUL(data->solid.color, spans->coverage);
+ int ialpha = qAlpha(~color);
+ ushort c = qConvertRgb32To16(color);
+ ushort *target = ((ushort *)data->rasterBuffer->scanLine(spans->y)) + spans->x;
+ int len = spans->len;
+ bool pre = (((quintptr)target) & 0x3) != 0;
+ bool post = false;
+ if (pre) {
+ // skip to word boundary
+ *target = c + BYTE_MUL_RGB16(*target, ialpha);
+ ++target;
+ --len;
+ }
+ if (len & 0x1) {
+ post = true;
+ --len;
+ }
+ uint *target32 = (uint*)target;
+ uint c32 = c | (c<<16);
+ len >>= 1;
+ uint salpha = (ialpha+1) >> 3; // calculate here rather than in loop
+ while (len--) {
+ // blend full words
+ *target32 = c32 + BYTE_MUL_RGB16_32(*target32, salpha);
+ ++target32;
+ target += 2;
+ }
+ if (post) {
+ // one last pixel beyond a full word
+ *target = c + BYTE_MUL_RGB16(*target, ialpha);
+ }
+ ++spans;
+ }
+ return;
+ }
+
+ blend_color_generic(count, spans, userData);
+}
+
+template <SpanMethod spanMethod>
+Q_STATIC_TEMPLATE_FUNCTION void blend_src_generic(int count, const QSpan *spans, void *userData
+ Q_TEMPLATE_ENUM_FIX(SpanMethod, spanMethod))
+{
+ QSpanData *data = reinterpret_cast<QSpanData *>(userData);
+
+ uint buffer[buffer_size];
+ uint src_buffer[buffer_size];
+ Operator op = getOperator(data, spans, count);
+
+ uint const_alpha = 256;
+ if (data->type == QSpanData::Texture)
+ const_alpha = data->texture.const_alpha;
+
+ while (count--) {
+ int x = spans->x;
+ int length = spans->len;
+ const int coverage = (spans->coverage * const_alpha) >> 8;
+ while (length) {
+ int l = qMin(buffer_size, length);
+ const uint *src = op.src_fetch(src_buffer, &op, data, spans->y, x, l);
+ if (spanMethod == RegularSpans) {
+ uint *dest = op.dest_fetch ? op.dest_fetch(buffer, data->rasterBuffer, x, spans->y, l) : buffer;
+ op.func(dest, src, l, coverage);
+ if (op.dest_store)
+ op.dest_store(data->rasterBuffer, x, spans->y, dest, l);
+ } else {
+ drawBufferSpan(data, src, l, x, spans->y, l, coverage);
+ }
+ x += l;
+ length -= l;
+ }
+ ++spans;
+ }
+}
+
+template <SpanMethod spanMethod>
+Q_STATIC_TEMPLATE_FUNCTION void blend_untransformed_generic(int count, const QSpan *spans, void *userData
+ Q_TEMPLATE_ENUM_FIX(SpanMethod, spanMethod))
+{
+ QSpanData *data = reinterpret_cast<QSpanData *>(userData);
+
+ uint buffer[buffer_size];
+ uint src_buffer[buffer_size];
+ Operator op = getOperator(data, spans, count);
+
+ const int image_width = data->texture.width;
+ const int image_height = data->texture.height;
+ int xoff = -qRound(-data->dx);
+ int yoff = -qRound(-data->dy);
+
+ while (count--) {
+ int x = spans->x;
+ int length = spans->len;
+ int sx = xoff + x;
+ int sy = yoff + spans->y;
+ if (sy >= 0 && sy < image_height && sx < image_width) {
+ if (sx < 0) {
+ x -= sx;
+ length += sx;
+ sx = 0;
+ }
+ if (sx + length > image_width)
+ length = image_width - sx;
+ if (length > 0) {
+ const int coverage = (spans->coverage * data->texture.const_alpha) >> 8;
+ while (length) {
+ int l = qMin(buffer_size, length);
+ const uint *src = op.src_fetch(src_buffer, &op, data, sy, sx, l);
+ if (spanMethod == RegularSpans) {
+ uint *dest = op.dest_fetch ? op.dest_fetch(buffer, data->rasterBuffer, x, spans->y, l) : buffer;
+ op.func(dest, src, l, coverage);
+ if (op.dest_store)
+ op.dest_store(data->rasterBuffer, x, spans->y, dest, l);
+ } else {
+ drawBufferSpan(data, src, l, x, spans->y,
+ l, coverage);
+ }
+ x += l;
+ sx += l;
+ length -= l;
+ }
+ }
+ }
+ ++spans;
+ }
+}
+
+template <SpanMethod spanMethod>
+Q_STATIC_TEMPLATE_FUNCTION void blend_untransformed_argb(int count, const QSpan *spans, void *userData
+ Q_TEMPLATE_ENUM_FIX(SpanMethod, spanMethod))
+{
+ QSpanData *data = reinterpret_cast<QSpanData *>(userData);
+ if (data->texture.format != QImage::Format_ARGB32_Premultiplied
+ && data->texture.format != QImage::Format_RGB32) {
+ blend_untransformed_generic<spanMethod>(count, spans, userData Q_TEMPLATE_ENUM_CALL(SpanMethod, spanMethod));
+ return;
+ }
+
+ Operator op = getOperator(data, spans, count);
+
+ const int image_width = data->texture.width;
+ const int image_height = data->texture.height;
+ int xoff = -qRound(-data->dx);
+ int yoff = -qRound(-data->dy);
+
+ while (count--) {
+ int x = spans->x;
+ int length = spans->len;
+ int sx = xoff + x;
+ int sy = yoff + spans->y;
+ if (sy >= 0 && sy < image_height && sx < image_width) {
+ if (sx < 0) {
+ x -= sx;
+ length += sx;
+ sx = 0;
+ }
+ if (sx + length > image_width)
+ length = image_width - sx;
+ if (length > 0) {
+ const int coverage = (spans->coverage * data->texture.const_alpha) >> 8;
+ const uint *src = (uint *)data->texture.scanLine(sy) + sx;
+ if (spanMethod == RegularSpans) {
+ uint *dest = ((uint *)data->rasterBuffer->scanLine(spans->y)) + x;
+ op.func(dest, src, length, coverage);
+ } else {
+ drawBufferSpan(data, src, length, x,
+ spans->y, length, coverage);
+ }
+ }
+ }
+ ++spans;
+ }
+}
+
+static inline quint16 interpolate_pixel_rgb16_255(quint16 x, quint8 a,
+ quint16 y, quint8 b)
+{
+ quint16 t = ((((x & 0x07e0) * a) + ((y & 0x07e0) * b)) >> 5) & 0x07e0;
+ t |= ((((x & 0xf81f) * a) + ((y & 0xf81f) * b)) >> 5) & 0xf81f;
+
+ return t;
+}
+
+static inline quint32 interpolate_pixel_rgb16x2_255(quint32 x, quint8 a,
+ quint32 y, quint8 b)
+{
+ uint t;
+ t = ((((x & 0xf81f07e0) >> 5) * a) + (((y & 0xf81f07e0) >> 5) * b)) & 0xf81f07e0;
+ t |= ((((x & 0x07e0f81f) * a) + ((y & 0x07e0f81f) * b)) >> 5) & 0x07e0f81f;
+ return t;
+}
+
+static inline void blend_sourceOver_rgb16_rgb16(quint16 *dest,
+ const quint16 *src,
+ int length,
+ const quint8 alpha,
+ const quint8 ialpha)
+{
+ const int dstAlign = ((quintptr)dest) & 0x3;
+ if (dstAlign) {
+ *dest = interpolate_pixel_rgb16_255(*src, alpha, *dest, ialpha);
+ ++dest;
+ ++src;
+ --length;
+ }
+ const int srcAlign = ((quintptr)src) & 0x3;
+ int length32 = length >> 1;
+ if (length32 && srcAlign == 0) {
+ while (length32--) {
+ const quint32 *src32 = reinterpret_cast<const quint32*>(src);
+ quint32 *dest32 = reinterpret_cast<quint32*>(dest);
+ *dest32 = interpolate_pixel_rgb16x2_255(*src32, alpha,
+ *dest32, ialpha);
+ dest += 2;
+ src += 2;
+ }
+ length &= 0x1;
+ }
+ while (length--) {
+ *dest = interpolate_pixel_rgb16_255(*src, alpha, *dest, ialpha);
+ ++dest;
+ ++src;
+ }
+}
+
+template <class DST, class SRC>
+Q_STATIC_TEMPLATE_SPECIALIZATION
+inline void madd_2(DST *dest, const quint16 alpha, const SRC *src)
+{
+ Q_ASSERT((quintptr(dest) & 0x3) == 0);
+ Q_ASSERT((quintptr(src) & 0x3) == 0);
+ dest[0] = dest[0].byte_mul(alpha >> 8) + DST(src[0]);
+ dest[1] = dest[1].byte_mul(alpha & 0xff) + DST(src[1]);
+}
+
+template <class DST, class SRC>
+Q_STATIC_TEMPLATE_SPECIALIZATION
+inline void madd_4(DST *dest, const quint32 alpha, const SRC *src)
+{
+ Q_ASSERT((quintptr(dest) & 0x3) == 0);
+ Q_ASSERT((quintptr(src) & 0x3) == 0);
+ dest[0] = dest[0].byte_mul(alpha >> 24) + DST(src[0]);
+ dest[1] = dest[1].byte_mul((alpha >> 16) & 0xff) + DST(src[1]);
+ dest[2] = dest[2].byte_mul((alpha >> 8) & 0xff) + DST(src[2]);
+ dest[3] = dest[3].byte_mul(alpha & 0xff) + DST(src[3]);
+}
+
+#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
+template <>
+Q_STATIC_TEMPLATE_SPECIALIZATION
+inline void madd_4(qargb8565 *dest, const quint32 a, const qargb8565 *src)
+{
+ Q_ASSERT((quintptr(dest) & 0x3) == 0);
+ Q_ASSERT((quintptr(src) & 0x3) == 0);
+
+ const quint32 *src32 = reinterpret_cast<const quint32*>(src);
+ quint32 *dest32 = reinterpret_cast<quint32*>(dest);
+ quint32 x, y, t;
+ quint8 a8;
+
+ {
+ x = dest32[0];
+ y = src32[0];
+
+ a8 = a >> 24;
+
+ // a0,g0
+ t = ((((x & 0x0007e0ff) * a8) >> 5) & 0x0007e0ff) + (y & 0x0007c0f8);
+
+ // r0,b0
+ t |= ((((x & 0x00f81f00) * a8) >> 5) & 0x00f81f00) + (y & 0x00f81f00);
+
+ a8 = (a >> 16) & 0xff;
+
+ // a1
+ t |= ((((x & 0xff000000) >> 5) * a8) & 0xff000000) + (y & 0xf8000000);
+
+ dest32[0] = t;
+ }
+ {
+ x = dest32[1];
+ y = src32[1];
+
+ // r1,b1
+ t = ((((x & 0x0000f81f) * a8) >> 5) & 0x0000f81f) + (y & 0x0000f81f);
+
+ // g1
+ t |= ((((x & 0x000007e0) * a8) >> 5) & 0x000007e0) + (y & 0x000007c0);
+
+ a8 = (a >> 8) & 0xff;
+
+ // a2
+ t |= ((((x & 0x00ff0000) * a8) >> 5) & 0x00ff0000) + (y & 0x00f80000);
+
+ {
+ // rgb2
+ quint16 x16 = (x >> 24) | ((dest32[2] & 0x000000ff) << 8);
+ quint16 y16 = (y >> 24) | ((src32[2] & 0x000000ff) << 8);
+ quint16 t16;
+
+ t16 = ((((x16 & 0xf81f) * a8) >> 5) & 0xf81f) + (y16 & 0xf81f);
+ t16 |= ((((x16 & 0x07e0) * a8) >> 5) & 0x07e0) + (y16 & 0x07c0);
+
+ // rg2
+ t |= ((t16 & 0x00ff) << 24);
+
+ dest32[1] = t;
+
+ x = dest32[2];
+ y = src32[2];
+
+ // gb2
+ t = (t16 >> 8);
+ }
+ }
+ {
+ a8 = a & 0xff;
+
+ // g3,a3
+ t |= ((((x & 0x07e0ff00) * a8) >> 5) & 0x07e0ff00) + (y & 0x07c0f800);
+
+ // r3,b3
+ t |= ((((x & 0xf81f0000) >> 5) * a8) & 0xf81f0000)+ (y & 0xf81f0000);
+
+ dest32[2] = t;
+ }
+}
+#endif
+
+#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
+template <>
+Q_STATIC_TEMPLATE_SPECIALIZATION
+inline void madd_4(qargb8555 *dest, const quint32 a, const qargb8555 *src)
+{
+ Q_ASSERT((quintptr(dest) & 0x3) == 0);
+ Q_ASSERT((quintptr(src) & 0x3) == 0);
+
+ const quint32 *src32 = reinterpret_cast<const quint32*>(src);
+ quint32 *dest32 = reinterpret_cast<quint32*>(dest);
+ quint32 x, y, t;
+ quint8 a8;
+
+ {
+ x = dest32[0];
+ y = src32[0];
+
+ a8 = a >> 24;
+
+ // a0,g0
+ t = ((((x & 0x0003e0ff) * a8) >> 5) & 0x0003e0ff) + (y & 0x0003e0f8);
+
+ // r0,b0
+ t |= ((((x & 0x007c1f00) * a8) >> 5) & 0x007c1f00) + (y & 0x007c1f00);
+
+ a8 = (a >> 16) & 0xff;
+
+ // a1
+ t |= ((((x & 0xff000000) >> 5) * a8) & 0xff000000) + (y & 0xf8000000);
+
+ dest32[0] = t;
+ }
+ {
+ x = dest32[1];
+ y = src32[1];
+
+ // r1,b1
+ t = ((((x & 0x00007c1f) * a8) >> 5) & 0x00007c1f) + (y & 0x00007c1f);
+
+ // g1
+ t |= ((((x & 0x000003e0) * a8) >> 5) & 0x000003e0) + (y & 0x000003e0);
+
+ a8 = (a >> 8) & 0xff;
+
+ // a2
+ t |= ((((x & 0x00ff0000) * a8) >> 5) & 0x00ff0000) + (y & 0x00f80000);
+
+ {
+ // rgb2
+ quint16 x16 = (x >> 24) | ((dest32[2] & 0x000000ff) << 8);
+ quint16 y16 = (y >> 24) | ((src32[2] & 0x000000ff) << 8);
+ quint16 t16;
+
+ t16 = ((((x16 & 0x7c1f) * a8) >> 5) & 0x7c1f) + (y16 & 0x7c1f);
+ t16 |= ((((x16 & 0x03e0) * a8) >> 5) & 0x03e0) + (y16 & 0x03e0);
+
+ // rg2
+ t |= ((t16 & 0x00ff) << 24);
+
+ dest32[1] = t;
+
+ x = dest32[2];
+ y = src32[2];
+
+ // gb2
+ t = (t16 >> 8);
+ }
+ }
+ {
+ a8 = a & 0xff;
+
+ // g3,a3
+ t |= ((((x & 0x03e0ff00) * a8) >> 5) & 0x03e0ff00) + (y & 0x03e0f800);
+
+ // r3,b3
+ t |= ((((x & 0x7c1f0000) >> 5) * a8) & 0x7c1f0000)+ (y & 0x7c1f0000);
+
+ dest32[2] = t;
+ }
+}
+#endif
+
+template <class T>
+Q_STATIC_TEMPLATE_SPECIALIZATION
+inline quint16 alpha_2(const T *src)
+{
+ Q_ASSERT((quintptr(src) & 0x3) == 0);
+
+ if (T::hasAlpha())
+ return (src[0].alpha() << 8) | src[1].alpha();
+ else
+ return 0xffff;
+}
+
+template <class T>
+Q_STATIC_TEMPLATE_SPECIALIZATION
+inline quint32 alpha_4(const T *src)
+{
+ Q_ASSERT((quintptr(src) & 0x3) == 0);
+
+ if (T::hasAlpha()) {
+ return (src[0].alpha() << 24) | (src[1].alpha() << 16)
+ | (src[2].alpha() << 8) | src[3].alpha();
+ } else {
+ return 0xffffffff;
+ }
+}
+
+template <>
+Q_STATIC_TEMPLATE_SPECIALIZATION
+inline quint32 alpha_4(const qargb8565 *src)
+{
+ const quint8 *src8 = reinterpret_cast<const quint8*>(src);
+ return src8[0] << 24 | src8[3] << 16 | src8[6] << 8 | src8[9];
+}
+
+template <>
+Q_STATIC_TEMPLATE_SPECIALIZATION
+inline quint32 alpha_4(const qargb6666 *src)
+{
+ const quint8 *src8 = reinterpret_cast<const quint8*>(src);
+ return ((src8[2] & 0xfc) | (src8[2] >> 6)) << 24
+ | ((src8[5] & 0xfc) | (src8[5] >> 6)) << 16
+ | ((src8[8] & 0xfc) | (src8[8] >> 6)) << 8
+ | ((src8[11] & 0xfc) | (src8[11] >> 6));
+}
+
+template <>
+Q_STATIC_TEMPLATE_SPECIALIZATION
+inline quint32 alpha_4(const qargb8555 *src)
+{
+ Q_ASSERT((long(src) & 0x3) == 0);
+ const quint8 *src8 = reinterpret_cast<const quint8*>(src);
+ return src8[0] << 24 | src8[3] << 16 | src8[6] << 8 | src8[9];
+}
+
+template <>
+Q_STATIC_TEMPLATE_SPECIALIZATION
+inline quint16 alpha_2(const qargb4444 *src)
+{
+ const quint32 *src32 = reinterpret_cast<const quint32*>(src);
+ const quint32 t = (*src32 & 0xf000f000) |
+ ((*src32 & 0xf000f000) >> 4);
+ return (t >> 24) | (t & 0xff00);
+}
+
+template <class T>
+Q_STATIC_TEMPLATE_SPECIALIZATION
+inline quint16 eff_alpha_2(quint16 alpha, const T*)
+{
+ return (T::alpha((alpha >> 8) & 0xff) << 8)
+ | T::alpha(alpha & 0xff);
+}
+
+template <>
+Q_STATIC_TEMPLATE_SPECIALIZATION
+inline quint16 eff_alpha_2(quint16 a, const qrgb565*)
+{
+ return ((((a & 0xff00) + 0x0100) >> 3) & 0xff00)
+ | ((((a & 0x00ff) + 0x0001) >> 3) & 0x00ff);
+}
+
+template <>
+Q_STATIC_TEMPLATE_SPECIALIZATION
+inline quint16 eff_alpha_2(quint16 a, const qrgb444*)
+{
+ return (((a & 0x00ff) + 0x0001) >> 4)
+ | ((((a & 0xff00) + 0x0100) >> 4) & 0xff00);
+}
+
+template <>
+Q_STATIC_TEMPLATE_SPECIALIZATION
+inline quint16 eff_alpha_2(quint16 a, const qargb4444*)
+{
+ return (((a & 0x00ff) + 0x0001) >> 4)
+ | ((((a & 0xff00) + 0x0100) >> 4) & 0xff00);
+}
+
+template <class T>
+Q_STATIC_TEMPLATE_SPECIALIZATION
+inline quint16 eff_ialpha_2(quint16 alpha, const T*)
+{
+ return (T::ialpha((alpha >> 8) & 0xff) << 8)
+ | T::ialpha(alpha & 0xff);
+}
+
+template <>
+Q_STATIC_TEMPLATE_SPECIALIZATION
+inline quint16 eff_ialpha_2(quint16 a, const qrgb565 *dummy)
+{
+ return 0x2020 - eff_alpha_2(a, dummy);
+}
+
+template <>
+Q_STATIC_TEMPLATE_SPECIALIZATION
+inline quint16 eff_ialpha_2(quint16 a, const qargb4444 *dummy)
+{
+ return 0x1010 - eff_alpha_2(a, dummy);
+}
+
+template <>
+Q_STATIC_TEMPLATE_SPECIALIZATION
+inline quint16 eff_ialpha_2(quint16 a, const qrgb444 *dummy)
+{
+ return 0x1010 - eff_alpha_2(a, dummy);
+}
+
+template <class T>
+Q_STATIC_TEMPLATE_SPECIALIZATION
+inline quint32 eff_alpha_4(quint32 alpha, const T*)
+{
+ return (T::alpha(alpha >> 24) << 24)
+ | (T::alpha((alpha >> 16) & 0xff) << 16)
+ | (T::alpha((alpha >> 8) & 0xff) << 8)
+ | T::alpha(alpha & 0xff);
+}
+
+template <>
+Q_STATIC_TEMPLATE_SPECIALIZATION
+inline quint32 eff_alpha_4(quint32 a, const qrgb888*)
+{
+ return a;
+}
+
+template <>
+Q_STATIC_TEMPLATE_SPECIALIZATION
+inline quint32 eff_alpha_4(quint32 a, const qargb8565*)
+{
+ return ((((a & 0xff00ff00) + 0x01000100) >> 3) & 0xff00ff00)
+ | ((((a & 0x00ff00ff) + 0x00010001) >> 3) & 0x00ff00ff);
+}
+
+template <>
+Q_STATIC_TEMPLATE_SPECIALIZATION
+inline quint32 eff_alpha_4(quint32 a, const qargb6666*)
+{
+ return ((((a & 0xff00ff00) >> 2) + 0x00400040) & 0xff00ff00)
+ | ((((a & 0x00ff00ff) + 0x00010001) >> 2) & 0x00ff00ff);
+}
+
+template <>
+Q_STATIC_TEMPLATE_SPECIALIZATION
+inline quint32 eff_alpha_4(quint32 a, const qrgb666*)
+{
+ return ((((a & 0xff00ff00) >> 2) + 0x00400040) & 0xff00ff00)
+ | ((((a & 0x00ff00ff) + 0x00010001) >> 2) & 0x00ff00ff);
+}
+
+template <>
+Q_STATIC_TEMPLATE_SPECIALIZATION
+inline quint32 eff_alpha_4(quint32 a, const qargb8555*)
+{
+ return ((((a & 0xff00ff00) + 0x01000100) >> 3) & 0xff00ff00)
+ | ((((a & 0x00ff00ff) + 0x00010001) >> 3) & 0x00ff00ff);
+}
+
+template <class T>
+Q_STATIC_TEMPLATE_SPECIALIZATION
+inline quint32 eff_ialpha_4(quint32 alpha, const T*)
+{
+ return (T::ialpha(alpha >> 24) << 24)
+ | (T::ialpha((alpha >> 16) & 0xff) << 16)
+ | (T::ialpha((alpha >> 8) & 0xff) << 8)
+ | T::ialpha(alpha & 0xff);
+}
+
+template <>
+Q_STATIC_TEMPLATE_SPECIALIZATION
+inline quint32 eff_ialpha_4(quint32 a, const qrgb888*)
+{
+ return ~a;
+}
+
+template <>
+Q_STATIC_TEMPLATE_SPECIALIZATION
+inline quint32 eff_ialpha_4(quint32 a, const qargb8565 *dummy)
+{
+ return 0x20202020 - eff_alpha_4(a, dummy);
+}
+
+template <>
+Q_STATIC_TEMPLATE_SPECIALIZATION
+inline quint32 eff_ialpha_4(quint32 a, const qargb6666 *dummy)
+{
+ return 0x40404040 - eff_alpha_4(a, dummy);
+}
+
+template <>
+Q_STATIC_TEMPLATE_SPECIALIZATION
+inline quint32 eff_ialpha_4(quint32 a, const qrgb666 *dummy)
+{
+ return 0x40404040 - eff_alpha_4(a, dummy);
+}
+
+template <>
+Q_STATIC_TEMPLATE_SPECIALIZATION
+inline quint32 eff_ialpha_4(quint32 a, const qargb8555 *dummy)
+{
+ return 0x20202020 - eff_alpha_4(a, dummy);
+}
+
+template <class DST, class SRC>
+inline void interpolate_pixel_unaligned_2(DST *dest, const SRC *src,
+ quint16 alpha)
+{
+ const quint16 a = eff_alpha_2(alpha, dest);
+ const quint16 ia = eff_ialpha_2(alpha, dest);
+ dest[0] = DST(src[0]).byte_mul(a >> 8) + dest[0].byte_mul(ia >> 8);
+ dest[1] = DST(src[1]).byte_mul(a & 0xff) + dest[1].byte_mul(ia & 0xff);
+}
+
+template <class DST, class SRC>
+inline void interpolate_pixel_2(DST *dest, const SRC *src, quint16 alpha)
+{
+ Q_ASSERT((long(dest) & 0x3) == 0);
+ Q_ASSERT((long(src) & 0x3) == 0);
+
+ const quint16 a = eff_alpha_2(alpha, dest);
+ const quint16 ia = eff_ialpha_2(alpha, dest);
+
+ dest[0] = DST(src[0]).byte_mul(a >> 8) + dest[0].byte_mul(ia >> 8);
+ dest[1] = DST(src[1]).byte_mul(a & 0xff) + dest[1].byte_mul(ia & 0xff);
+}
+
+template <class DST, class SRC>
+inline void interpolate_pixel(DST &dest, quint8 a, const SRC &src, quint8 b)
+{
+ if (SRC::hasAlpha() && !DST::hasAlpha())
+ interpolate_pixel(dest, a, DST(src), b);
+ else
+ dest = dest.byte_mul(a) + DST(src).byte_mul(b);
+}
+
+template <>
+inline void interpolate_pixel(qargb8565 &dest, quint8 a,
+ const qargb8565 &src, quint8 b)
+{
+ quint8 *d = reinterpret_cast<quint8*>(&dest);
+ const quint8 *s = reinterpret_cast<const quint8*>(&src);
+ d[0] = (d[0] * a + s[0] * b) >> 5;
+
+ const quint16 x = (d[2] << 8) | d[1];
+ const quint16 y = (s[2] << 8) | s[1];
+ quint16 t = (((x & 0x07e0) * a + (y & 0x07e0) * b) >> 5) & 0x07e0;
+ t |= (((x & 0xf81f) * a + (y & 0xf81f) * b) >> 5) & 0xf81f;
+
+ d[1] = t & 0xff;
+ d[2] = t >> 8;
+}
+
+template <>
+inline void interpolate_pixel(qrgb565 &dest, quint8 a,
+ const qrgb565 &src, quint8 b)
+{
+ const quint16 x = dest.rawValue();
+ const quint16 y = src.rawValue();
+ quint16 t = (((x & 0x07e0) * a + (y & 0x07e0) * b) >> 5) & 0x07e0;
+ t |= (((x & 0xf81f) * a + (y & 0xf81f) * b) >> 5) & 0xf81f;
+ dest = t;
+}
+
+template <>
+inline void interpolate_pixel(qrgb555 &dest, quint8 a,
+ const qrgb555 &src, quint8 b)
+{
+ const quint16 x = dest.rawValue();
+ const quint16 y = src.rawValue();
+ quint16 t = (((x & 0x03e0) * a + (y & 0x03e0) * b) >> 5) & 0x03e0;
+ t |= ((((x & 0x7c1f) * a) + ((y & 0x7c1f) * b)) >> 5) & 0x7c1f;
+ dest = t;
+}
+
+template <>
+inline void interpolate_pixel(qrgb444 &dest, quint8 a,
+ const qrgb444 &src, quint8 b)
+{
+ const quint16 x = dest.rawValue();
+ const quint16 y = src.rawValue();
+ quint16 t = ((x & 0x00f0) * a + (y & 0x00f0) * b) & 0x0f00;
+ t |= ((x & 0x0f0f) * a + (y & 0x0f0f) * b) & 0xf0f0;
+ quint16 *d = reinterpret_cast<quint16*>(&dest);
+ *d = (t >> 4);
+}
+
+template <class DST, class SRC>
+inline void interpolate_pixel_2(DST *dest, quint8 a,
+ const SRC *src, quint8 b)
+{
+ Q_ASSERT((long(dest) & 0x3) == 0);
+ Q_ASSERT((long(src) & 0x3) == 0);
+
+ Q_ASSERT(!SRC::hasAlpha());
+
+ dest[0] = dest[0].byte_mul(a) + DST(src[0]).byte_mul(b);
+ dest[1] = dest[1].byte_mul(a) + DST(src[1]).byte_mul(b);
+}
+
+template <>
+inline void interpolate_pixel_2(qrgb565 *dest, quint8 a,
+ const qrgb565 *src, quint8 b)
+{
+ quint32 *x = reinterpret_cast<quint32*>(dest);
+ const quint32 *y = reinterpret_cast<const quint32*>(src);
+ quint32 t = (((*x & 0xf81f07e0) >> 5) * a +
+ ((*y & 0xf81f07e0) >> 5) * b) & 0xf81f07e0;
+ t |= (((*x & 0x07e0f81f) * a
+ + (*y & 0x07e0f81f) * b) >> 5) & 0x07e0f81f;
+ *x = t;
+}
+
+template <>
+inline void interpolate_pixel_2(qrgb555 *dest, quint8 a,
+ const qrgb555 *src, quint8 b)
+{
+ quint32 *x = reinterpret_cast<quint32*>(dest);
+ const quint32 *y = reinterpret_cast<const quint32*>(src);
+ quint32 t = (((*x & 0x7c1f03e0) >> 5) * a +
+ ((*y & 0x7c1f03e0) >> 5) * b) & 0x7c1f03e0;
+ t |= (((*x & 0x03e07c1f) * a
+ + (*y & 0x03e07c1f) * b) >> 5) & 0x03e07c1f;
+ *x = t;
+}
+
+template <>
+inline void interpolate_pixel_2(qrgb444 *dest, quint8 a,
+ const qrgb444 *src, quint8 b)
+{
+ quint32 *x = reinterpret_cast<quint32*>(dest);
+ const quint32 *y = reinterpret_cast<const quint32*>(src);
+ quint32 t = ((*x & 0x0f0f0f0f) * a + (*y & 0x0f0f0f0f) * b) & 0xf0f0f0f0;
+ t |= ((*x & 0x00f000f0) * a + (*y & 0x00f000f0) * b) & 0x0f000f00;
+ *x = t >> 4;
+}
+
+template <class DST, class SRC>
+inline void interpolate_pixel_4(DST *dest, const SRC *src, quint32 alpha)
+{
+ Q_ASSERT((long(dest) & 0x3) == 0);
+ Q_ASSERT((long(src) & 0x3) == 0);
+
+ const quint32 a = eff_alpha_4(alpha, dest);
+ const quint32 ia = eff_ialpha_4(alpha, dest);
+ dest[0] = DST(src[0]).byte_mul(a >> 24)
+ + dest[0].byte_mul(ia >> 24);
+ dest[1] = DST(src[1]).byte_mul((a >> 16) & 0xff)
+ + dest[1].byte_mul((ia >> 16) & 0xff);
+ dest[2] = DST(src[2]).byte_mul((a >> 8) & 0xff)
+ + dest[2].byte_mul((ia >> 8) & 0xff);
+ dest[3] = DST(src[3]).byte_mul(a & 0xff)
+ + dest[3].byte_mul(ia & 0xff);
+}
+
+#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
+template <>
+inline void interpolate_pixel_4(qargb8565 *dest, const qargb8565 *src,
+ quint32 alpha)
+{
+ Q_ASSERT((long(dest) & 0x3) == 0);
+ Q_ASSERT((long(src) & 0x3) == 0);
+
+ const quint32 a = eff_alpha_4(alpha, dest);
+ const quint32 ia = eff_ialpha_4(alpha, dest);
+ const quint32 *src32 = reinterpret_cast<const quint32*>(src);
+ quint32 *dest32 = reinterpret_cast<quint32*>(dest);
+
+ quint32 x, y, t;
+ quint8 a8, ia8;
+ {
+ x = src32[0];
+ y = dest32[0];
+
+ a8 = a >> 24;
+ ia8 = ia >> 24;
+
+ // a0,g0
+ t = (((x & 0x0007e0ff) * a8 + (y & 0x0007e0ff) * ia8) >> 5)
+ & 0x0007e0ff;
+
+ // r0,b0
+ t |= (((x & 0x00f81f00) * a8 + (y & 0x00f81f00) * ia8) >> 5)
+ & 0x00f81f00;
+
+ a8 = (a >> 16) & 0xff;
+ ia8 = (ia >> 16) & 0xff;
+
+ // a1
+ t |= (((x & 0xff000000) >> 5) * a8 + ((y & 0xff000000) >> 5) * ia8)
+ & 0xff000000;
+
+ dest32[0] = t;
+ }
+ {
+ x = src32[1];
+ y = dest32[1];
+
+ // r1,b1
+ t = (((x & 0x0000f81f) * a8 + (y & 0x0000f81f) * ia8) >> 5)
+ & 0x0000f81f;
+
+ // g1
+ t |= (((x & 0x000007e0) * a8 + (y & 0x000007e0) * ia8) >> 5)
+ & 0x000007e0;
+
+ a8 = (a >> 8) & 0xff;
+ ia8 = (ia >> 8) & 0xff;
+
+ // a2
+ t |= (((x & 0x00ff0000) * a8 + (y & 0x00ff0000) * ia8) >> 5)
+ & 0x00ff0000;
+
+ {
+ // rgb2
+ quint16 x16 = (x >> 24) | ((src32[2] & 0x000000ff) << 8);
+ quint16 y16 = (y >> 24) | ((dest32[2] & 0x000000ff) << 8);
+ quint16 t16;
+
+ t16 = (((x16 & 0xf81f) * a8 + (y16 & 0xf81f) * ia8) >> 5) & 0xf81f;
+ t16 |= (((x16 & 0x07e0) * a8 + (y16 & 0x07e0) * ia8) >> 5) & 0x07e0;
+
+ // rg2
+ t |= ((t16 & 0x00ff) << 24);
+
+ dest32[1] = t;
+
+ x = src32[2];
+ y = dest32[2];
+
+ // gb2
+ t = (t16 >> 8);
+ }
+ }
+ {
+ a8 = a & 0xff;
+ ia8 = ia & 0xff;
+
+ // g3,a3
+ t |= (((x & 0x07e0ff00) * a8 + (y & 0x07e0ff00) * ia8) >> 5)
+ & 0x07e0ff00;
+
+ // r3,b3
+ t |= (((x & 0xf81f0000) >> 5) * a8 + ((y & 0xf81f0000) >> 5) * ia8)
+ & 0xf81f0000;
+
+ dest32[2] = t;
+ }
+}
+#endif
+
+#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
+template <>
+inline void interpolate_pixel_4(qargb8555 *dest, const qargb8555 *src,
+ quint32 alpha)
+{
+ Q_ASSERT((long(dest) & 0x3) == 0);
+ Q_ASSERT((long(src) & 0x3) == 0);
+
+
+ const quint32 a = eff_alpha_4(alpha, dest);
+ const quint32 ia = eff_ialpha_4(alpha, dest);
+ const quint32 *src32 = reinterpret_cast<const quint32*>(src);
+ quint32 *dest32 = reinterpret_cast<quint32*>(dest);
+
+ quint32 x, y, t;
+ quint8 a8, ia8;
+ {
+ x = src32[0];
+ y = dest32[0];
+
+ a8 = a >> 24;
+ ia8 = ia >> 24;
+
+ // a0,g0
+ t = (((x & 0x0003e0ff) * a8 + (y & 0x0003e0ff) * ia8) >> 5)
+ & 0x0003e0ff;
+
+ // r0,b0
+ t |= (((x & 0x007c1f00) * a8 + (y & 0x007c1f00) * ia8) >> 5)
+ & 0x007c1f00;
+
+ a8 = (a >> 16) & 0xff;
+ ia8 = (ia >> 16) & 0xff;
+
+ // a1
+ t |= (((x & 0xff000000) >> 5) * a8 + ((y & 0xff000000) >> 5) * ia8)
+ & 0xff000000;
+
+ dest32[0] = t;
+ }
+ {
+ x = src32[1];
+ y = dest32[1];
+
+ // r1,b1
+ t = (((x & 0x00007c1f) * a8 + (y & 0x00007c1f) * ia8) >> 5)
+ & 0x00007c1f;
+
+ // g1
+ t |= (((x & 0x000003e0) * a8 + (y & 0x000003e0) * ia8) >> 5)
+ & 0x000003e0;
+
+ a8 = (a >> 8) & 0xff;
+ ia8 = (ia >> 8) & 0xff;
+
+ // a2
+ t |= (((x & 0x00ff0000) * a8 + (y & 0x00ff0000) * ia8) >> 5)
+ & 0x00ff0000;
+
+ {
+ // rgb2
+ quint16 x16 = (x >> 24) | ((src32[2] & 0x000000ff) << 8);
+ quint16 y16 = (y >> 24) | ((dest32[2] & 0x000000ff) << 8);
+ quint16 t16;
+
+ t16 = (((x16 & 0x7c1f) * a8 + (y16 & 0x7c1f) * ia8) >> 5) & 0x7c1f;
+ t16 |= (((x16 & 0x03e0) * a8 + (y16 & 0x03e0) * ia8) >> 5) & 0x03e0;
+
+ // rg2
+ t |= ((t16 & 0x00ff) << 24);
+
+ dest32[1] = t;
+
+ x = src32[2];
+ y = dest32[2];
+
+ // gb2
+ t = (t16 >> 8);
+ }
+ }
+ {
+ a8 = a & 0xff;
+ ia8 = ia & 0xff;
+
+ // g3,a3
+ t |= (((x & 0x03e0ff00) * a8 + (y & 0x03e0ff00) * ia8) >> 5)
+ & 0x03e0ff00;
+
+ // r3,b3
+ t |= (((x & 0x7c1f0000) >> 5) * a8 + ((y & 0x7c1f0000) >> 5) * ia8)
+ & 0x7c1f0000;
+
+ dest32[2] = t;
+ }
+}
+#endif
+
+template <>
+inline void interpolate_pixel_4(qrgb888 *dest, const qrgb888 *src,
+ quint32 alpha)
+{
+ Q_ASSERT((long(dest) & 0x3) == 0);
+ Q_ASSERT((long(src) & 0x3) == 0);
+
+ const quint32 a = eff_alpha_4(alpha, dest);
+ const quint32 ia = eff_ialpha_4(alpha, dest);
+ const quint32 *src32 = reinterpret_cast<const quint32*>(src);
+ quint32 *dest32 = reinterpret_cast<quint32*>(dest);
+
+ {
+ quint32 x = src32[0];
+ quint32 y = dest32[0];
+
+ quint32 t;
+ t = ((x >> 8) & 0xff00ff) * (a >> 24)
+ + ((y >> 8) & 0xff00ff) * (ia >> 24);
+ t = (t + ((t >> 8) & 0xff00ff) + 0x800080);
+ t &= 0xff00ff00;
+
+ x = (x & 0xff0000) * (a >> 24)
+ + (x & 0x0000ff) * ((a >> 16) & 0xff)
+ + (y & 0xff0000) * (ia >> 24)
+ + (y & 0x0000ff) * ((ia >> 16) & 0xff);
+ x = (x + ((x >> 8) & 0xff00ff) + 0x800080) >> 8;
+ x &= 0x00ff00ff;
+
+ dest32[0] = x | t;
+ }
+ {
+ quint32 x = src32[1];
+ quint32 y = dest32[1];
+
+ quint32 t;
+ t = ((x >> 8) & 0xff0000) * ((a >> 16) & 0xff)
+ + ((x >> 8) & 0x0000ff) * ((a >> 8) & 0xff)
+ + ((y >> 8) & 0xff0000) * ((ia >> 16) & 0xff)
+ + ((y >> 8) & 0x0000ff) * ((ia >> 8) & 0xff);
+ t = (t + ((t >> 8) & 0xff00ff) + 0x800080);
+ t &= 0xff00ff00;
+
+ x = (x & 0xff0000) * ((a >> 16) & 0xff)
+ + (x & 0x0000ff) * ((a >> 8) & 0xff)
+ + (y & 0xff0000) * ((ia >> 16) & 0xff)
+ + (y & 0x0000ff) * ((ia >> 8) & 0xff);
+ x = (x + ((x >> 8) & 0xff00ff) + 0x800080) >> 8;
+ x &= 0x00ff00ff;
+
+ dest32[1] = x | t;
+ }
+ {
+ quint32 x = src32[2];
+ quint32 y = dest32[2];
+
+ quint32 t;
+ t = ((x >> 8) & 0xff0000) * ((a >> 8) & 0xff)
+ + ((x >> 8) & 0x0000ff) * (a & 0xff)
+ + ((y >> 8) & 0xff0000) * ((ia >> 8) & 0xff)
+ + ((y >> 8) & 0x0000ff) * (ia & 0xff);
+ t = (t + ((t >> 8) & 0xff00ff) + 0x800080);
+ t &= 0xff00ff00;
+
+ x = (x & 0xff00ff) * (a & 0xff)
+ + (y & 0xff00ff) * (ia & 0xff);
+ x = (x + ((x >> 8) & 0xff00ff) + 0x800080) >> 8;
+ x &= 0x00ff00ff;
+
+ dest32[2] = x | t;
+ }
+}
+
+template <class DST, class SRC>
+inline void interpolate_pixel_4(DST *dest, quint8 a,
+ const SRC *src, quint8 b)
+{
+ Q_ASSERT((long(dest) & 0x3) == 0);
+ Q_ASSERT((long(src) & 0x3) == 0);
+
+ dest[0] = dest[0].byte_mul(a) + DST(src[0]).byte_mul(b);
+ dest[1] = dest[1].byte_mul(a) + DST(src[1]).byte_mul(b);
+ dest[2] = dest[2].byte_mul(a) + DST(src[2]).byte_mul(b);
+ dest[3] = dest[3].byte_mul(a) + DST(src[3]).byte_mul(b);
+}
+
+template <class DST, class SRC>
+inline void blend_sourceOver_4(DST *dest, const SRC *src)
+{
+ Q_ASSERT((long(dest) & 0x3) == 0);
+ Q_ASSERT((long(src) & 0x3) == 0);
+
+ const quint32 a = alpha_4(src);
+ if (a == 0xffffffff) {
+ qt_memconvert(dest, src, 4);
+ } else if (a > 0) {
+ quint32 buf[3]; // array of quint32 to get correct alignment
+ qt_memconvert((DST*)(void*)buf, src, 4);
+ madd_4(dest, eff_ialpha_4(a, dest), (DST*)(void*)buf);
+ }
+}
+
+template <>
+inline void blend_sourceOver_4(qargb8565 *dest, const qargb8565 *src)
+{
+ Q_ASSERT((long(dest) & 0x3) == 0);
+ Q_ASSERT((long(src) & 0x3) == 0);
+
+ const quint32 a = alpha_4(src);
+ if (a == 0xffffffff) {
+ qt_memconvert(dest, src, 4);
+ } else if (a > 0) {
+ madd_4(dest, eff_ialpha_4(a, dest), src);
+ }
+}
+
+template <>
+inline void blend_sourceOver_4(qargb8555 *dest, const qargb8555 *src)
+{
+ Q_ASSERT((long(dest) & 0x3) == 0);
+ Q_ASSERT((long(src) & 0x3) == 0);
+
+ const quint32 a = alpha_4(src);
+ if (a == 0xffffffff) {
+ qt_memconvert(dest, src, 4);
+ } else if (a > 0) {
+ madd_4(dest, eff_ialpha_4(a, dest), src);
+ }
+}
+
+template <>
+inline void blend_sourceOver_4(qargb6666 *dest, const qargb6666 *src)
+{
+ Q_ASSERT((long(dest) & 0x3) == 0);
+ Q_ASSERT((long(src) & 0x3) == 0);
+
+ const quint32 a = alpha_4(src);
+ if (a == 0xffffffff) {
+ qt_memconvert(dest, src, 4);
+ } else if (a > 0) {
+ madd_4(dest, eff_ialpha_4(a, dest), src);
+ }
+}
+
+template <class DST, class SRC>
+void QT_FASTCALL blendUntransformed_unaligned(DST *dest, const SRC *src,
+ quint8 coverage, int length)
+{
+ Q_ASSERT(coverage > 0);
+
+ if (coverage < 255) {
+ if (SRC::hasAlpha()) {
+ for (int i = 0; i < length; ++i) {
+ if (src[i].alpha()) {
+ const quint8 alpha = qt_div_255(int(src[i].alpha()) * int(coverage));
+ interpolate_pixel(dest[i], DST::ialpha(alpha),
+ src[i], DST::alpha(alpha));
+ }
+ }
+ } else {
+ const quint8 alpha = DST::alpha(coverage);
+ const quint8 ialpha = DST::ialpha(coverage);
+ if (alpha) {
+ for (int i = 0; i < length; ++i)
+ interpolate_pixel(dest[i], ialpha, src[i], alpha);
+ }
+ }
+ return;
+ }
+
+ Q_ASSERT(coverage == 0xff);
+ Q_ASSERT(SRC::hasAlpha());
+
+ if (SRC::hasAlpha()) {
+ for (int i = 0; i < length; ++i) {
+ const quint8 a = src->alpha();
+ if (a == 0xff)
+ *dest = DST(*src);
+ else if (a > 0) {
+ if (DST::hasAlpha())
+ *dest = DST(*src).truncedAlpha() + dest->byte_mul(DST::ialpha(a));
+ else
+ *dest = DST(SRC(*src).truncedAlpha()) + dest->byte_mul(DST::ialpha(a));
+ }
+ ++src;
+ ++dest;
+ }
+ }
+}
+
+template <class DST, class SRC>
+void QT_FASTCALL blendUntransformed_dest16(DST *dest, const SRC *src,
+ quint8 coverage, int length)
+{
+ Q_ASSERT(sizeof(DST) == 2);
+ Q_ASSERT(sizeof(SRC) == 2);
+ Q_ASSERT((long(dest) & 0x3) == (long(src) & 0x3));
+ Q_ASSERT(coverage > 0);
+
+ const int align = quintptr(dest) & 0x3;
+
+ if (coverage < 255) {
+ // align
+ if (align) {
+ const quint8 alpha = SRC::hasAlpha()
+ ? qt_div_255(int(src->alpha()) * int(coverage))
+ : coverage;
+ if (alpha) {
+ interpolate_pixel(*dest, DST::ialpha(alpha),
+ *src, DST::alpha(alpha));
+ }
+ ++dest;
+ ++src;
+ --length;
+ }
+
+ if (SRC::hasAlpha()) {
+ while (length >= 2) {
+ const quint16 alpha16 = BYTE_MUL(uint(alpha_2(src)), uint(coverage));
+ interpolate_pixel_2(dest, src, alpha16);
+ length -= 2;
+ src += 2;
+ dest += 2;
+ }
+ } else {
+ const quint8 alpha = DST::alpha(coverage);
+ const quint8 ialpha = DST::ialpha(coverage);
+
+ while (length >= 2) {
+ interpolate_pixel_2(dest, ialpha, src, alpha);
+ length -= 2;
+ src += 2;
+ dest += 2;
+ }
+ }
+
+ // tail
+ if (length) {
+ const quint8 alpha = SRC::hasAlpha()
+ ? qt_div_255(int(src->alpha()) * int(coverage))
+ : coverage;
+ if (alpha) {
+ interpolate_pixel(*dest, DST::ialpha(alpha),
+ *src, DST::alpha(alpha));
+ }
+ }
+
+ return;
+ }
+
+ Q_ASSERT(SRC::hasAlpha());
+ if (SRC::hasAlpha()) {
+ if (align) {
+ const quint8 alpha = src->alpha();
+ if (alpha == 0xff)
+ *dest = DST(*src);
+ else if (alpha > 0)
+ *dest = DST(*src).truncedAlpha() + dest->byte_mul(DST::ialpha(alpha));
+ ++dest;
+ ++src;
+ --length;
+ }
+
+ while (length >= 2) {
+ Q_ASSERT((long(dest) & 3) == 0);
+ Q_ASSERT((long(src) & 3) == 0);
+
+ const quint16 a = alpha_2(src);
+ if (a == 0xffff) {
+ qt_memconvert(dest, src, 2);
+ } else if (a > 0) {
+ quint32 buf;
+ if (sizeof(DST) == 2)
+ qt_memconvert((DST*)(void*)&buf, src, 2);
+ madd_2(dest, eff_ialpha_2(a, dest), (DST*)(void*)&buf);
+ }
+
+ length -= 2;
+ src += 2;
+ dest += 2;
+ }
+
+ if (length) {
+ const quint8 alpha = src->alpha();
+ if (alpha == 0xff)
+ *dest = DST(*src);
+ else if (alpha > 0)
+ *dest = DST(*src).truncedAlpha() + dest->byte_mul(DST::ialpha(alpha));
+ }
+ }
+}
+
+template <class DST, class SRC>
+void QT_FASTCALL blendUntransformed_dest24(DST *dest, const SRC *src,
+ quint8 coverage, int length)
+{
+ Q_ASSERT((long(dest) & 0x3) == (long(src) & 0x3));
+ Q_ASSERT(sizeof(DST) == 3);
+ Q_ASSERT(coverage > 0);
+
+ const int align = quintptr(dest) & 0x3;
+
+ if (coverage < 255) {
+ // align
+ for (int i = 0; i < align; ++i) {
+ if (SRC::hasAlpha()) {
+ const quint8 alpha = qt_div_255(int(src->alpha()) * int(coverage));
+ if (alpha)
+ interpolate_pixel(*dest, DST::ialpha(alpha),
+ *src, DST::alpha(alpha));
+ } else {
+ interpolate_pixel(*dest, DST::ialpha(coverage),
+ *src, DST::alpha(coverage));
+ }
+ ++dest;
+ ++src;
+ --length;
+ }
+
+ if (SRC::hasAlpha()) {
+ while (length >= 4) {
+ const quint32 alpha = BYTE_MUL(uint(alpha_4(src)), uint(coverage));
+ if (alpha)
+ interpolate_pixel_4(dest, src, alpha);
+ length -= 4;
+ src += 4;
+ dest += 4;
+ }
+ } else {
+ const quint8 alpha = DST::alpha(coverage);
+ const quint8 ialpha = DST::ialpha(coverage);
+ while (length >= 4) {
+ interpolate_pixel_4(dest, ialpha, src, alpha);
+ length -= 4;
+ src += 4;
+ dest += 4;
+ }
+ }
+
+ // tail
+ while (length--) {
+ if (SRC::hasAlpha()) {
+ const quint8 alpha = qt_div_255(int(src->alpha()) * int(coverage));
+ if (alpha)
+ interpolate_pixel(*dest, DST::ialpha(alpha),
+ *src, DST::alpha(alpha));
+ } else {
+ interpolate_pixel(*dest, DST::ialpha(coverage),
+ *src, DST::alpha(coverage));
+ }
+ ++dest;
+ ++src;
+ }
+
+ return;
+ }
+
+
+ Q_ASSERT(coverage == 255);
+ Q_ASSERT(SRC::hasAlpha());
+
+ if (SRC::hasAlpha()) {
+ // align
+ for (int i = 0; i < align; ++i) {
+ const quint8 a = src->alpha();
+ if (a == 0xff) {
+ *dest = DST(*src);
+ } else if (a > 0) {
+ *dest = DST(*src).truncedAlpha() + dest->byte_mul(DST::ialpha(a));
+ }
+ ++dest;
+ ++src;
+ --length;
+ }
+
+ while (length >= 4) {
+ blend_sourceOver_4(dest, src);
+ length -= 4;
+ src += 4;
+ dest += 4;
+ }
+
+ // tail
+ while (length--) {
+ const quint8 a = src->alpha();
+ if (a == 0xff) {
+ *dest = DST(*src);
+ } else if (a > 0) {
+ *dest = DST(*src).truncedAlpha() + dest->byte_mul(DST::ialpha(a));
+ }
+ ++dest;
+ ++src;
+ }
+ }
+}
+
+template <class DST, class SRC>
+Q_STATIC_TEMPLATE_SPECIALIZATION
+void QT_FASTCALL blendUntransformed(int count, const QSpan *spans, void *userData)
+{
+ QSpanData *data = reinterpret_cast<QSpanData*>(userData);
+ QPainter::CompositionMode mode = data->rasterBuffer->compositionMode;
+
+ if (mode != QPainter::CompositionMode_SourceOver &&
+ mode != QPainter::CompositionMode_Source)
+ {
+ blend_src_generic<RegularSpans>(count, spans, userData
+ Q_TEMPLATE_ENUM_CALL(SpanMethod, RegularSpans));
+ return;
+ }
+
+ const bool modeSource = !SRC::hasAlpha() ||
+ mode == QPainter::CompositionMode_Source;
+ const int image_width = data->texture.width;
+ const int image_height = data->texture.height;
+ int xoff = -qRound(-data->dx);
+ int yoff = -qRound(-data->dy);
+
+ while (count--) {
+ const quint8 coverage = (data->texture.const_alpha * spans->coverage) >> 8;
+ if (coverage == 0) {
+ ++spans;
+ continue;
+ }
+
+ int x = spans->x;
+ int length = spans->len;
+ int sx = xoff + x;
+ int sy = yoff + spans->y;
+ if (sy >= 0 && sy < image_height && sx < image_width) {
+ if (sx < 0) {
+ x -= sx;
+ length += sx;
+ sx = 0;
+ }
+ if (sx + length > image_width)
+ length = image_width - sx;
+ if (length > 0) {
+ DST *dest = ((DST*)data->rasterBuffer->scanLine(spans->y)) + x;
+ const SRC *src = (SRC*)data->texture.scanLine(sy) + sx;
+ if (modeSource && coverage == 255) {
+ qt_memconvert<DST, SRC>(dest, src, length);
+ } else if (sizeof(DST) == 3 && sizeof(SRC) == 3 && length >= 3 &&
+ (quintptr(dest) & 3) == (quintptr(src) & 3))
+ {
+ blendUntransformed_dest24(dest, src, coverage, length);
+ } else if (sizeof(DST) == 2 && sizeof(SRC) == 2 && length >= 3 &&
+ (quintptr(dest) & 3) == (quintptr(src) & 3))
+ {
+ blendUntransformed_dest16(dest, src, coverage, length);
+ } else {
+ blendUntransformed_unaligned(dest, src, coverage, length);
+ }
+ }
+ }
+ ++spans;
+ }
+}
+
+static void blend_untransformed_rgb888(int count, const QSpan *spans,
+ void *userData)
+{
+#if !defined(Q_WS_QWS) || defined(QT_QWS_DEPTH_24)
+ QSpanData *data = reinterpret_cast<QSpanData *>(userData);
+
+ if (data->texture.format == QImage::Format_RGB888)
+ blendUntransformed<qrgb888, qrgb888>(count, spans, userData);
+ else
+#endif
+ blend_untransformed_generic<RegularSpans>(count, spans, userData
+ Q_TEMPLATE_ENUM_CALL(SpanMethod, RegularSpans));
+}
+
+static void blend_untransformed_argb6666(int count, const QSpan *spans,
+ void *userData)
+{
+#if !defined(Q_WS_QWS) || defined(QT_QWS_DEPTH_18)
+ QSpanData *data = reinterpret_cast<QSpanData *>(userData);
+
+ if (data->texture.format == QImage::Format_ARGB6666_Premultiplied)
+ blendUntransformed<qargb6666, qargb6666>(count, spans, userData);
+ else if (data->texture.format == QImage::Format_RGB666)
+ blendUntransformed<qargb6666, qrgb666>(count, spans, userData);
+ else
+#endif
+ blend_untransformed_generic<RegularSpans>(count, spans, userData
+ Q_TEMPLATE_ENUM_CALL(SpanMethod, RegularSpans));
+}
+
+static void blend_untransformed_rgb666(int count, const QSpan *spans,
+ void *userData)
+{
+#if !defined(Q_WS_QWS) || defined(QT_QWS_DEPTH_18)
+ QSpanData *data = reinterpret_cast<QSpanData *>(userData);
+
+ if (data->texture.format == QImage::Format_ARGB6666_Premultiplied)
+ blendUntransformed<qrgb666, qargb6666>(count, spans, userData);
+ else if (data->texture.format == QImage::Format_RGB666)
+ blendUntransformed<qrgb666, qrgb666>(count, spans, userData);
+ else
+#endif
+ blend_untransformed_generic<RegularSpans>(count, spans, userData
+ Q_TEMPLATE_ENUM_CALL(SpanMethod, RegularSpans));
+}
+
+static void blend_untransformed_argb8565(int count, const QSpan *spans,
+ void *userData)
+{
+#if !defined(Q_WS_QWS) || defined(QT_QWS_DEPTH_16)
+ QSpanData *data = reinterpret_cast<QSpanData *>(userData);
+
+ if (data->texture.format == QImage::Format_ARGB8565_Premultiplied)
+ blendUntransformed<qargb8565, qargb8565>(count, spans, userData);
+ else if (data->texture.format == QImage::Format_RGB16)
+ blendUntransformed<qargb8565, qrgb565>(count, spans, userData);
+ else
+#endif
+ blend_untransformed_generic<RegularSpans>(count, spans, userData
+ Q_TEMPLATE_ENUM_CALL(SpanMethod, RegularSpans));
+}
+
+static void blend_untransformed_rgb565(int count, const QSpan *spans,
+ void *userData)
+{
+#if !defined(Q_WS_QWS) || defined(QT_QWS_DEPTH_16)
+ QSpanData *data = reinterpret_cast<QSpanData *>(userData);
+
+ if (data->texture.format == QImage::Format_ARGB8565_Premultiplied)
+ blendUntransformed<qrgb565, qargb8565>(count, spans, userData);
+ else if (data->texture.format == QImage::Format_RGB16)
+ blendUntransformed<qrgb565, qrgb565>(count, spans, userData);
+ else
+#endif
+ blend_untransformed_generic<RegularSpans>(count, spans, userData
+ Q_TEMPLATE_ENUM_CALL(SpanMethod, RegularSpans));
+}
+
+static void blend_untransformed_argb8555(int count, const QSpan *spans,
+ void *userData)
+{
+#if !defined(Q_WS_QWS) || defined(QT_QWS_DEPTH_15)
+ QSpanData *data = reinterpret_cast<QSpanData *>(userData);
+
+ if (data->texture.format == QImage::Format_ARGB8555_Premultiplied)
+ blendUntransformed<qargb8555, qargb8555>(count, spans, userData);
+ else if (data->texture.format == QImage::Format_RGB555)
+ blendUntransformed<qargb8555, qrgb555>(count, spans, userData);
+ else
+#endif
+ blend_untransformed_generic<RegularSpans>(count, spans, userData
+ Q_TEMPLATE_ENUM_CALL(SpanMethod, RegularSpans));
+}
+
+static void blend_untransformed_rgb555(int count, const QSpan *spans,
+ void *userData)
+{
+#if !defined(Q_WS_QWS) || defined(QT_QWS_DEPTH_15)
+ QSpanData *data = reinterpret_cast<QSpanData *>(userData);
+
+ if (data->texture.format == QImage::Format_ARGB8555_Premultiplied)
+ blendUntransformed<qrgb555, qargb8555>(count, spans, userData);
+ else if (data->texture.format == QImage::Format_RGB555)
+ blendUntransformed<qrgb555, qrgb555>(count, spans, userData);
+ else
+#endif
+ blend_untransformed_generic<RegularSpans>(count, spans, userData
+ Q_TEMPLATE_ENUM_CALL(SpanMethod, RegularSpans));
+}
+
+static void blend_untransformed_argb4444(int count, const QSpan *spans,
+ void *userData)
+{
+#if !defined(Q_WS_QWS) || defined(QT_QWS_DEPTH_12)
+ QSpanData *data = reinterpret_cast<QSpanData *>(userData);
+
+ if (data->texture.format == QImage::Format_ARGB4444_Premultiplied)
+ blendUntransformed<qargb4444, qargb4444>(count, spans, userData);
+ else if (data->texture.format == QImage::Format_RGB444)
+ blendUntransformed<qargb4444, qrgb444>(count, spans, userData);
+ else
+#endif
+ blend_untransformed_generic<RegularSpans>(count, spans, userData
+ Q_TEMPLATE_ENUM_CALL(SpanMethod, RegularSpans));
+}
+
+static void blend_untransformed_rgb444(int count, const QSpan *spans,
+ void *userData)
+{
+#if !defined(Q_WS_QWS) || defined(QT_QWS_DEPTH_12)
+ QSpanData *data = reinterpret_cast<QSpanData *>(userData);
+
+ if (data->texture.format == QImage::Format_ARGB4444_Premultiplied)
+ blendUntransformed<qrgb444, qargb4444>(count, spans, userData);
+ else if (data->texture.format == QImage::Format_RGB444)
+ blendUntransformed<qrgb444, qrgb444>(count, spans, userData);
+ else
+#endif
+ blend_untransformed_generic<RegularSpans>(count, spans, userData
+ Q_TEMPLATE_ENUM_CALL(SpanMethod, RegularSpans));
+}
+
+template <SpanMethod spanMethod>
+Q_STATIC_TEMPLATE_FUNCTION void blend_tiled_generic(int count, const QSpan *spans, void *userData
+ Q_TEMPLATE_ENUM_FIX(SpanMethod, spanMethod))
+{
+ QSpanData *data = reinterpret_cast<QSpanData *>(userData);
+
+ uint buffer[buffer_size];
+ uint src_buffer[buffer_size];
+ Operator op = getOperator(data, spans, count);
+
+ const int image_width = data->texture.width;
+ const int image_height = data->texture.height;
+ int xoff = -qRound(-data->dx) % image_width;
+ int yoff = -qRound(-data->dy) % image_height;
+
+ if (xoff < 0)
+ xoff += image_width;
+ if (yoff < 0)
+ yoff += image_height;
+
+ while (count--) {
+ int x = spans->x;
+ int length = spans->len;
+ int sx = (xoff + spans->x) % image_width;
+ int sy = (spans->y + yoff) % image_height;
+ if (sx < 0)
+ sx += image_width;
+ if (sy < 0)
+ sy += image_height;
+
+ const int coverage = (spans->coverage * data->texture.const_alpha) >> 8;
+ while (length) {
+ int l = qMin(image_width - sx, length);
+ if (buffer_size < l)
+ l = buffer_size;
+ const uint *src = op.src_fetch(src_buffer, &op, data, sy, sx, l);
+ if (spanMethod == RegularSpans) {
+ uint *dest = op.dest_fetch ? op.dest_fetch(buffer, data->rasterBuffer, x, spans->y, l) : buffer;
+ op.func(dest, src, l, coverage);
+ if (op.dest_store)
+ op.dest_store(data->rasterBuffer, x, spans->y, dest, l);
+ } else {
+ drawBufferSpan(data, src, l, x, spans->y, l,
+ coverage);
+ }
+ x += l;
+ sx += l;
+ length -= l;
+ if (sx >= image_width)
+ sx = 0;
+ }
+ ++spans;
+ }
+}
+
+template <SpanMethod spanMethod>
+Q_STATIC_TEMPLATE_FUNCTION void blend_tiled_argb(int count, const QSpan *spans, void *userData
+ Q_TEMPLATE_ENUM_FIX(SpanMethod, spanMethod))
+{
+ QSpanData *data = reinterpret_cast<QSpanData *>(userData);
+ if (data->texture.format != QImage::Format_ARGB32_Premultiplied
+ && data->texture.format != QImage::Format_RGB32) {
+ blend_tiled_generic<spanMethod>(count, spans, userData Q_TEMPLATE_ENUM_CALL(SpanMethod, spanMethod));
+ return;
+ }
+
+ Operator op = getOperator(data, spans, count);
+
+ int image_width = data->texture.width;
+ int image_height = data->texture.height;
+ int xoff = -qRound(-data->dx) % image_width;
+ int yoff = -qRound(-data->dy) % image_height;
+
+ if (xoff < 0)
+ xoff += image_width;
+ if (yoff < 0)
+ yoff += image_height;
+
+ while (count--) {
+ int x = spans->x;
+ int length = spans->len;
+ int sx = (xoff + spans->x) % image_width;
+ int sy = (spans->y + yoff) % image_height;
+ if (sx < 0)
+ sx += image_width;
+ if (sy < 0)
+ sy += image_height;
+
+ const int coverage = (spans->coverage * data->texture.const_alpha) >> 8;
+ while (length) {
+ int l = qMin(image_width - sx, length);
+ if (buffer_size < l)
+ l = buffer_size;
+ const uint *src = (uint *)data->texture.scanLine(sy) + sx;
+ if (spanMethod == RegularSpans) {
+ uint *dest = ((uint *)data->rasterBuffer->scanLine(spans->y)) + x;
+ op.func(dest, src, l, coverage);
+ } else {
+ drawBufferSpan(data, src, buffer_size,
+ x, spans->y, l, coverage);
+ }
+ x += l;
+ length -= l;
+ sx = 0;
+ }
+ ++spans;
+ }
+}
+
+template <class DST, class SRC>
+Q_STATIC_TEMPLATE_FUNCTION void blendTiled(int count, const QSpan *spans, void *userData)
+{
+ QSpanData *data = reinterpret_cast<QSpanData*>(userData);
+ QPainter::CompositionMode mode = data->rasterBuffer->compositionMode;
+
+ if (mode != QPainter::CompositionMode_SourceOver &&
+ mode != QPainter::CompositionMode_Source)
+ {
+ blend_src_generic<RegularSpans>(count, spans, userData
+ Q_TEMPLATE_ENUM_CALL(SpanMethod, RegularSpans));
+ return;
+ }
+
+ const bool modeSource = !SRC::hasAlpha() ||
+ mode == QPainter::CompositionMode_Source;
+ const int image_width = data->texture.width;
+ const int image_height = data->texture.height;
+ int xoff = -qRound(-data->dx) % image_width;
+ int yoff = -qRound(-data->dy) % image_height;
+
+ if (xoff < 0)
+ xoff += image_width;
+ if (yoff < 0)
+ yoff += image_height;
+
+ while (count--) {
+ const quint8 coverage = (data->texture.const_alpha * spans->coverage) >> 8;
+ if (coverage == 0) {
+ ++spans;
+ continue;
+ }
+
+ int x = spans->x;
+ int length = spans->len;
+ int sx = (xoff + spans->x) % image_width;
+ int sy = (spans->y + yoff) % image_height;
+ if (sx < 0)
+ sx += image_width;
+ if (sy < 0)
+ sy += image_height;
+
+ while (length) {
+ int l = qMin(image_width - sx, length);
+ if (buffer_size < l)
+ l = buffer_size;
+
+ DST *dest = ((DST*)data->rasterBuffer->scanLine(spans->y)) + x;
+ const SRC *src = (SRC*)data->texture.scanLine(sy) + sx;
+ if (modeSource && coverage == 255) {
+ qt_memconvert<DST, SRC>(dest, src, l);
+ } else if (sizeof(DST) == 3 && sizeof(SRC) == 3 && l >= 4 &&
+ (quintptr(dest) & 3) == (quintptr(src) & 3))
+ {
+ blendUntransformed_dest24(dest, src, coverage, l);
+ } else if (sizeof(DST) == 2 && sizeof(SRC) == 2 && l >= 2 &&
+ (quintptr(dest) & 3) == (quintptr(src) & 3))
+ {
+ blendUntransformed_dest16(dest, src, coverage, l);
+ } else {
+ blendUntransformed_unaligned(dest, src, coverage, l);
+ }
+
+ x += l;
+ length -= l;
+ sx = 0;
+ }
+ ++spans;
+ }
+}
+
+static void blend_tiled_rgb888(int count, const QSpan *spans, void *userData)
+{
+#if !defined(Q_WS_QWS) || defined(QT_QWS_DEPTH_24)
+ QSpanData *data = reinterpret_cast<QSpanData *>(userData);
+
+ if (data->texture.format == QImage::Format_RGB888)
+ blendTiled<qrgb888, qrgb888>(count, spans, userData);
+ else
+#endif
+ blend_tiled_generic<RegularSpans>(count, spans, userData
+ Q_TEMPLATE_ENUM_CALL(SpanMethod, RegularSpans));
+}
+
+static void blend_tiled_argb6666(int count, const QSpan *spans, void *userData)
+{
+#if !defined(Q_WS_QWS) || defined(QT_QWS_DEPTH_18)
+ QSpanData *data = reinterpret_cast<QSpanData *>(userData);
+
+ if (data->texture.format == QImage::Format_ARGB6666_Premultiplied)
+ blendTiled<qargb6666, qargb6666>(count, spans, userData);
+ else if (data->texture.format == QImage::Format_RGB666)
+ blendTiled<qargb6666, qrgb666>(count, spans, userData);
+ else
+#endif
+ blend_tiled_generic<RegularSpans>(count, spans, userData
+ Q_TEMPLATE_ENUM_CALL(SpanMethod, RegularSpans));
+}
+
+static void blend_tiled_rgb666(int count, const QSpan *spans, void *userData)
+{
+#if !defined(Q_WS_QWS) || defined(QT_QWS_DEPTH_18)
+ QSpanData *data = reinterpret_cast<QSpanData *>(userData);
+
+ if (data->texture.format == QImage::Format_ARGB6666_Premultiplied)
+ blendTiled<qrgb666, qargb6666>(count, spans, userData);
+ else if (data->texture.format == QImage::Format_RGB666)
+ blendTiled<qrgb666, qrgb666>(count, spans, userData);
+ else
+#endif
+ blend_tiled_generic<RegularSpans>(count, spans, userData
+ Q_TEMPLATE_ENUM_CALL(SpanMethod, RegularSpans));
+}
+
+static void blend_tiled_argb8565(int count, const QSpan *spans, void *userData)
+{
+#if !defined(Q_WS_QWS) || defined(QT_QWS_DEPTH_16)
+ QSpanData *data = reinterpret_cast<QSpanData *>(userData);
+
+ if (data->texture.format == QImage::Format_ARGB8565_Premultiplied)
+ blendTiled<qargb8565, qargb8565>(count, spans, userData);
+ else if (data->texture.format == QImage::Format_RGB16)
+ blendTiled<qargb8565, qrgb565>(count, spans, userData);
+ else
+#endif
+ blend_tiled_generic<RegularSpans>(count, spans, userData
+ Q_TEMPLATE_ENUM_CALL(SpanMethod, RegularSpans));
+}
+
+static void blend_tiled_rgb565(int count, const QSpan *spans, void *userData)
+{
+#if !defined(Q_WS_QWS) || defined(QT_QWS_DEPTH_16)
+ QSpanData *data = reinterpret_cast<QSpanData *>(userData);
+
+ if (data->texture.format == QImage::Format_ARGB8565_Premultiplied)
+ blendTiled<qrgb565, qargb8565>(count, spans, userData);
+ else if (data->texture.format == QImage::Format_RGB16)
+ blendTiled<qrgb565, qrgb565>(count, spans, userData);
+ else
+#endif
+ blend_tiled_generic<RegularSpans>(count, spans, userData
+ Q_TEMPLATE_ENUM_CALL(SpanMethod, RegularSpans));
+}
+
+static void blend_tiled_argb8555(int count, const QSpan *spans, void *userData)
+{
+#if !defined(Q_WS_QWS) || defined(QT_QWS_DEPTH_15)
+ QSpanData *data = reinterpret_cast<QSpanData *>(userData);
+
+ if (data->texture.format == QImage::Format_ARGB8555_Premultiplied)
+ blendTiled<qargb8555, qargb8555>(count, spans, userData);
+ else if (data->texture.format == QImage::Format_RGB555)
+ blendTiled<qargb8555, qrgb555>(count, spans, userData);
+ else
+#endif
+ blend_tiled_generic<RegularSpans>(count, spans, userData
+ Q_TEMPLATE_ENUM_CALL(SpanMethod, RegularSpans));
+}
+
+static void blend_tiled_rgb555(int count, const QSpan *spans, void *userData)
+{
+#if !defined(Q_WS_QWS) || defined(QT_QWS_DEPTH_15)
+ QSpanData *data = reinterpret_cast<QSpanData *>(userData);
+
+ if (data->texture.format == QImage::Format_ARGB8555_Premultiplied)
+ blendTiled<qrgb555, qargb8555>(count, spans, userData);
+ else if (data->texture.format == QImage::Format_RGB555)
+ blendTiled<qrgb555, qrgb555>(count, spans, userData);
+ else
+#endif
+ blend_tiled_generic<RegularSpans>(count, spans, userData
+ Q_TEMPLATE_ENUM_CALL(SpanMethod, RegularSpans));
+}
+
+static void blend_tiled_argb4444(int count, const QSpan *spans, void *userData)
+{
+#if !defined(Q_WS_QWS) || defined(QT_QWS_DEPTH_12)
+ QSpanData *data = reinterpret_cast<QSpanData *>(userData);
+
+ if (data->texture.format == QImage::Format_ARGB4444_Premultiplied)
+ blendTiled<qargb4444, qargb4444>(count, spans, userData);
+ else if (data->texture.format == QImage::Format_RGB444)
+ blendTiled<qargb4444, qrgb444>(count, spans, userData);
+ else
+#endif
+ blend_tiled_generic<RegularSpans>(count, spans, userData
+ Q_TEMPLATE_ENUM_CALL(SpanMethod, RegularSpans));
+}
+
+static void blend_tiled_rgb444(int count, const QSpan *spans, void *userData)
+{
+#if !defined(Q_WS_QWS) || defined(QT_QWS_DEPTH_12)
+ QSpanData *data = reinterpret_cast<QSpanData *>(userData);
+
+ if (data->texture.format == QImage::Format_ARGB4444_Premultiplied)
+ blendTiled<qrgb444, qargb4444>(count, spans, userData);
+ else if (data->texture.format == QImage::Format_RGB444)
+ blendTiled<qrgb444, qrgb444>(count, spans, userData);
+ else
+#endif
+ blend_tiled_generic<RegularSpans>(count, spans, userData
+ Q_TEMPLATE_ENUM_CALL(SpanMethod, RegularSpans));
+}
+
+
+template <SpanMethod spanMethod>
+Q_STATIC_TEMPLATE_FUNCTION void blend_transformed_bilinear_argb(int count, const QSpan *spans, void *userData
+ Q_TEMPLATE_ENUM_FIX(SpanMethod, spanMethod))
+{
+ QSpanData *data = reinterpret_cast<QSpanData *>(userData);
+ if (data->texture.format != QImage::Format_ARGB32_Premultiplied
+ && data->texture.format != QImage::Format_RGB32) {
+ blend_src_generic<spanMethod>(count, spans, userData Q_TEMPLATE_ENUM_CALL(SpanMethod, spanMethod));
+ return;
+ }
+
+ CompositionFunction func = functionForMode[data->rasterBuffer->compositionMode];
+ uint buffer[buffer_size];
+
+ int image_x1 = data->texture.x1;
+ int image_y1 = data->texture.y1;
+ int image_x2 = data->texture.x2;
+ int image_y2 = data->texture.y2;
+ const int scanline_offset = data->texture.bytesPerLine / 4;
+
+ if (data->fast_matrix) {
+ // The increment pr x in the scanline
+ int fdx = (int)(data->m11 * fixed_scale);
+ int fdy = (int)(data->m12 * fixed_scale);
+
+ while (count--) {
+ void *t = data->rasterBuffer->scanLine(spans->y);
+
+ uint *target = ((uint *)t) + spans->x;
+ uint *image_bits = (uint *)data->texture.imageData;
+
+ const qreal cx = spans->x + 0.5;
+ const qreal cy = spans->y + 0.5;
+
+ int x = int((data->m21 * cy
+ + data->m11 * cx + data->dx) * fixed_scale) - half_point;
+ int y = int((data->m22 * cy
+ + data->m12 * cx + data->dy) * fixed_scale) - half_point;
+
+ int length = spans->len;
+ const int coverage = (data->texture.const_alpha * spans->coverage) >> 8;
+ while (length) {
+ int l = qMin(length, buffer_size);
+ const uint *end = buffer + l;
+ uint *b = buffer;
+ while (b < end) {
+ int x1 = (x >> 16);
+ int x2 = x1 + 1;
+ int y1 = (y >> 16);
+ int y2 = y1 + 1;
+
+ int distx = ((x - (x1 << 16)) >> 8);
+ int disty = ((y - (y1 << 16)) >> 8);
+ int idistx = 256 - distx;
+ int idisty = 256 - disty;
+
+ x1 = qBound(image_x1, x1, image_x2 - 1);
+ x2 = qBound(image_x1, x2, image_x2 - 1);
+ y1 = qBound(image_y1, y1, image_y2 - 1);
+ y2 = qBound(image_y1, y2, image_y2 - 1);
+
+ int y1_offset = y1 * scanline_offset;
+ int y2_offset = y2 * scanline_offset;
+
+#if defined(Q_IRIX_GCC3_3_WORKAROUND)
+ uint tl = gccBug(image_bits[y1_offset + x1]);
+ uint tr = gccBug(image_bits[y1_offset + x2]);
+ uint bl = gccBug(image_bits[y2_offset + x1]);
+ uint br = gccBug(image_bits[y2_offset + x2]);
+#else
+ uint tl = image_bits[y1_offset + x1];
+ uint tr = image_bits[y1_offset + x2];
+ uint bl = image_bits[y2_offset + x1];
+ uint br = image_bits[y2_offset + x2];
+#endif
+
+ uint xtop = INTERPOLATE_PIXEL_256(tl, idistx, tr, distx);
+ uint xbot = INTERPOLATE_PIXEL_256(bl, idistx, br, distx);
+ *b = INTERPOLATE_PIXEL_256(xtop, idisty, xbot, disty);
+ ++b;
+
+ x += fdx;
+ y += fdy;
+ }
+ if (spanMethod == RegularSpans)
+ func(target, buffer, l, coverage);
+ else
+ drawBufferSpan(data, buffer, buffer_size,
+ spans->x + spans->len - length,
+ spans->y, l, coverage);
+ target += l;
+ length -= l;
+ }
+ ++spans;
+ }
+ } else {
+ const qreal fdx = data->m11;
+ const qreal fdy = data->m12;
+ const qreal fdw = data->m13;
+
+ while (count--) {
+ void *t = data->rasterBuffer->scanLine(spans->y);
+
+ uint *target = ((uint *)t) + spans->x;
+ uint *image_bits = (uint *)data->texture.imageData;
+
+ const qreal cx = spans->x + 0.5;
+ const qreal cy = spans->y + 0.5;
+
+ qreal x = data->m21 * cy + data->m11 * cx + data->dx;
+ qreal y = data->m22 * cy + data->m12 * cx + data->dy;
+ qreal w = data->m23 * cy + data->m13 * cx + data->m33;
+
+ int length = spans->len;
+ const int coverage = (data->texture.const_alpha * spans->coverage) >> 8;
+ while (length) {
+ int l = qMin(length, buffer_size);
+ const uint *end = buffer + l;
+ uint *b = buffer;
+ while (b < end) {
+ const qreal iw = w == 0 ? 1 : 1 / w;
+ const qreal px = x * iw - 0.5;
+ const qreal py = y * iw - 0.5;
+
+ int x1 = int(px) - (px < 0);
+ int x2 = x1 + 1;
+ int y1 = int(py) - (py < 0);
+ int y2 = y1 + 1;
+
+ int distx = int((px - x1) * 256);
+ int disty = int((py - y1) * 256);
+ int idistx = 256 - distx;
+ int idisty = 256 - disty;
+
+ x1 = qBound(image_x1, x1, image_x2 - 1);
+ x2 = qBound(image_x1, x2, image_x2 - 1);
+ y1 = qBound(image_y1, y1, image_y2 - 1);
+ y2 = qBound(image_y1, y2, image_y2 - 1);
+
+ int y1_offset = y1 * scanline_offset;
+ int y2_offset = y2 * scanline_offset;
+
+#if defined(Q_IRIX_GCC3_3_WORKAROUND)
+ uint tl = gccBug(image_bits[y1_offset + x1]);
+ uint tr = gccBug(image_bits[y1_offset + x2]);
+ uint bl = gccBug(image_bits[y2_offset + x1]);
+ uint br = gccBug(image_bits[y2_offset + x2]);
+#else
+ uint tl = image_bits[y1_offset + x1];
+ uint tr = image_bits[y1_offset + x2];
+ uint bl = image_bits[y2_offset + x1];
+ uint br = image_bits[y2_offset + x2];
+#endif
+
+ uint xtop = INTERPOLATE_PIXEL_256(tl, idistx, tr, distx);
+ uint xbot = INTERPOLATE_PIXEL_256(bl, idistx, br, distx);
+ *b = INTERPOLATE_PIXEL_256(xtop, idisty, xbot, disty);
+ ++b;
+
+ x += fdx;
+ y += fdy;
+ w += fdw;
+ }
+ if (spanMethod == RegularSpans)
+ func(target, buffer, l, coverage);
+ else
+ drawBufferSpan(data, buffer, buffer_size,
+ spans->x + spans->len - length,
+ spans->y, l, coverage);
+ target += l;
+ length -= l;
+ }
+ ++spans;
+ }
+ }
+}
+
+template <class DST, class SRC>
+Q_STATIC_TEMPLATE_FUNCTION void blendTransformedBilinear(int count, const QSpan *spans,
+ void *userData)
+{
+ QSpanData *data = reinterpret_cast<QSpanData*>(userData);
+ QPainter::CompositionMode mode = data->rasterBuffer->compositionMode;
+
+
+ if (mode != QPainter::CompositionMode_SourceOver) {
+ blend_src_generic<RegularSpans>(count, spans, userData
+ Q_TEMPLATE_ENUM_CALL(SpanMethod, RegularSpans));
+ return;
+ }
+
+ SRC buffer[buffer_size];
+
+ const int src_minx = data->texture.x1;
+ const int src_miny = data->texture.y1;
+ const int src_maxx = data->texture.x2 - 1;
+ const int src_maxy = data->texture.y2 - 1;
+
+ if (data->fast_matrix) {
+ // The increment pr x in the scanline
+ const int fdx = (int)(data->m11 * fixed_scale);
+ const int fdy = (int)(data->m12 * fixed_scale);
+
+ while (count--) {
+ const quint8 coverage = (data->texture.const_alpha * spans->coverage) >> 8;
+ if (coverage == 0) {
+ ++spans;
+ continue;
+ }
+
+ DST *dest = (DST*)data->rasterBuffer->scanLine(spans->y)
+ + spans->x;
+ const qreal cx = spans->x + qreal(0.5);
+ const qreal cy = spans->y + qreal(0.5);
+ int x = int((data->m21 * cy
+ + data->m11 * cx + data->dx) * fixed_scale) - half_point;
+ int y = int((data->m22 * cy
+ + data->m12 * cx + data->dy) * fixed_scale) - half_point;
+ int length = spans->len;
+
+ while (length) {
+ const int l = qMin(length, buffer_size);
+
+ const SRC *end = buffer + l;
+ SRC *b = buffer;
+ while (b < end) {
+ int x1 = (x >> 16);
+ int x2 = x1 + 1;
+ int y1 = (y >> 16);
+ int y2 = y1 + 1;
+
+ const int distx = ((x - (x1 << 16)) >> 8);
+ const int disty = ((y - (y1 << 16)) >> 8);
+ x1 = qBound(src_minx, x1, src_maxx);
+ x2 = qBound(src_minx, x2, src_maxx);
+ y1 = qBound(src_miny, y1, src_maxy);
+ y2 = qBound(src_miny, y2, src_maxy);
+
+#if 0
+ if (x1 == x2) {
+ if (y1 == y2) {
+ *b = ((SRC*)data->texture.scanLine(y1))[x1];
+ } else {
+ *b = ((SRC*)data->texture.scanLine(y1))[x1];
+ const SRC t = data->texture.scanLine(y2)[x1];
+ interpolate_pixel(*b, SRC::ialpha(disty),
+ t, SRC::alpha(disty));
+ }
+ } else if (y1 == y2) {
+ *b = ((SRC*)data->texture.scanLine(y1))[x1];
+ const SRC t = ((SRC*)data->texture.scanLine(y1))[x2];
+ interpolate_pixel(*b, SRC::ialpha(distx),
+ t, SRC::alpha(distx));
+ } else
+#endif
+ {
+ const SRC *src1 = (SRC*)data->texture.scanLine(y1);
+ const SRC *src2 = (SRC*)data->texture.scanLine(y2);
+ SRC tl = src1[x1];
+ const SRC tr = src1[x2];
+ SRC bl = src2[x1];
+ const SRC br = src2[x2];
+ const quint8 ax = SRC::alpha(distx);
+ const quint8 iax = SRC::ialpha(distx);
+
+ interpolate_pixel(tl, iax, tr, ax);
+ interpolate_pixel(bl, iax, br, ax);
+ interpolate_pixel(tl, SRC::ialpha(disty),
+ bl, SRC::alpha(disty));
+ *b = tl;
+ }
+ ++b;
+
+ x += fdx;
+ y += fdy;
+ }
+
+ if (!SRC::hasAlpha() && coverage == 255) {
+ qt_memconvert(dest, buffer, l);
+ } else if (sizeof(DST) == 3 && l >= 4 &&
+ (quintptr(dest) & 3) == (quintptr(buffer) & 3))
+ {
+ blendUntransformed_dest24(dest, buffer, coverage, l);
+ } else if (sizeof(DST) == 2 && sizeof(SRC) == 2 && l >= 2 &&
+ (quintptr(dest) & 3) == (quintptr(buffer) & 3)) {
+ blendUntransformed_dest16(dest, buffer, coverage, l);
+ } else {
+ blendUntransformed_unaligned(dest, buffer, coverage, l);
+ }
+
+ dest += l;
+ length -= l;
+ }
+ ++spans;
+ }
+ } else {
+ const qreal fdx = data->m11;
+ const qreal fdy = data->m12;
+ const qreal fdw = data->m13;
+
+ while (count--) {
+ const quint8 coverage = (data->texture.const_alpha * spans->coverage) >> 8;
+ if (coverage == 0) {
+ ++spans;
+ continue;
+ }
+
+ DST *dest = (DST*)data->rasterBuffer->scanLine(spans->y)
+ + spans->x;
+
+ const qreal cx = spans->x + qreal(0.5);
+ const qreal cy = spans->y + qreal(0.5);
+
+ qreal x = data->m21 * cy + data->m11 * cx + data->dx;
+ qreal y = data->m22 * cy + data->m12 * cx + data->dy;
+ qreal w = data->m23 * cy + data->m13 * cx + data->m33;
+
+ int length = spans->len;
+ while (length) {
+ const int l = qMin(length, buffer_size);
+ const SRC *end = buffer + l;
+ SRC *b = buffer;
+ while (b < end) {
+ const qreal iw = w == 0 ? 1 : 1 / w;
+ const qreal px = x * iw - qreal(0.5);
+ const qreal py = y * iw - qreal(0.5);
+
+ int x1 = int(px) - (px < 0);
+ int x2 = x1 + 1;
+ int y1 = int(py) - (py < 0);
+ int y2 = y1 + 1;
+
+ const int distx = int((px - x1) * 256);
+ const int disty = int((py - y1) * 256);
+
+ x1 = qBound(src_minx, x1, src_maxx);
+ x2 = qBound(src_minx, x2, src_maxx);
+ y1 = qBound(src_miny, y1, src_maxy);
+ y2 = qBound(src_miny, y2, src_maxy);
+
+ const SRC *src1 = (SRC*)data->texture.scanLine(y1);
+ const SRC *src2 = (SRC*)data->texture.scanLine(y2);
+ SRC tl = src1[x1];
+ const SRC tr = src1[x2];
+ SRC bl = src2[x1];
+ const SRC br = src2[x2];
+ const quint8 ax = SRC::alpha(distx);
+ const quint8 iax = SRC::ialpha(distx);
+
+ interpolate_pixel(tl, iax, tr, ax);
+ interpolate_pixel(bl, iax, br, ax);
+ interpolate_pixel(tl, SRC::ialpha(disty),
+ bl, SRC::alpha(disty));
+ *b = tl;
+ ++b;
+
+ x += fdx;
+ y += fdy;
+ w += fdw;
+ }
+ if (!SRC::hasAlpha() && coverage == 255) {
+ qt_memconvert(dest, buffer, l);
+ } else if (sizeof(DST) == 3 && l >= 4 &&
+ (quintptr(dest) & 3) == (quintptr(buffer) & 3))
+ {
+ blendUntransformed_dest24(dest, buffer, coverage, l);
+ } else if (sizeof(DST) == 2 && sizeof(SRC) == 2 && l >= 2 &&
+ (quintptr(dest) & 3) == (quintptr(buffer) & 3)) {
+ blendUntransformed_dest16(dest, buffer, coverage, l);
+ } else {
+ blendUntransformed_unaligned(dest, buffer, coverage, l);
+ }
+
+ dest += l;
+ length -= l;
+ }
+ ++spans;
+ }
+ }
+}
+
+static void blend_transformed_bilinear_rgb888(int count, const QSpan *spans, void *userData)
+{
+#if !defined(Q_WS_QWS) || defined(QT_QWS_DEPTH_24)
+ QSpanData *data = reinterpret_cast<QSpanData *>(userData);
+
+ if (data->texture.format == QImage::Format_RGB888)
+ blendTransformedBilinear<qrgb888, qrgb888>(count, spans, userData);
+ else
+#endif
+ blend_src_generic<RegularSpans>(count, spans, userData
+ Q_TEMPLATE_ENUM_CALL(SpanMethod, RegularSpans));
+}
+
+static void blend_transformed_bilinear_argb6666(int count, const QSpan *spans, void *userData)
+{
+#if !defined(Q_WS_QWS) || defined(QT_QWS_DEPTH_18)
+ QSpanData *data = reinterpret_cast<QSpanData *>(userData);
+
+ if (data->texture.format == QImage::Format_ARGB6666_Premultiplied)
+ blendTransformedBilinear<qargb6666, qargb6666>(count, spans, userData);
+ else if (data->texture.format == QImage::Format_RGB666)
+ blendTransformedBilinear<qargb6666, qrgb666>(count, spans, userData);
+ else
+#endif
+ blend_src_generic<RegularSpans>(count, spans, userData
+ Q_TEMPLATE_ENUM_CALL(SpanMethod, RegularSpans));
+}
+
+static void blend_transformed_bilinear_rgb666(int count, const QSpan *spans, void *userData)
+{
+#if !defined(Q_WS_QWS) || defined(QT_QWS_DEPTH_18)
+ QSpanData *data = reinterpret_cast<QSpanData *>(userData);
+
+ if (data->texture.format == QImage::Format_ARGB6666_Premultiplied)
+ blendTransformedBilinear<qrgb666, qargb6666>(count, spans, userData);
+ else if (data->texture.format == QImage::Format_RGB666)
+ blendTransformedBilinear<qrgb666, qrgb666>(count, spans, userData);
+ else
+#endif
+ blend_src_generic<RegularSpans>(count, spans, userData
+ Q_TEMPLATE_ENUM_CALL(SpanMethod, RegularSpans));
+}
+
+static void blend_transformed_bilinear_argb8565(int count, const QSpan *spans, void *userData)
+{
+#if !defined(Q_WS_QWS) || defined(QT_QWS_DEPTH_16)
+ QSpanData *data = reinterpret_cast<QSpanData *>(userData);
+
+ if (data->texture.format == QImage::Format_ARGB8565_Premultiplied)
+ blendTransformedBilinear<qargb8565, qargb8565>(count, spans, userData);
+ else if (data->texture.format == QImage::Format_RGB16)
+ blendTransformedBilinear<qargb8565, qrgb565>(count, spans, userData);
+ else
+#endif
+ blend_src_generic<RegularSpans>(count, spans, userData
+ Q_TEMPLATE_ENUM_CALL(SpanMethod, RegularSpans));
+}
+
+static void blend_transformed_bilinear_rgb565(int count, const QSpan *spans,
+ void *userData)
+{
+#if !defined(Q_WS_QWS) || defined(QT_QWS_DEPTH_16)
+ QSpanData *data = reinterpret_cast<QSpanData *>(userData);
+
+ if (data->texture.format == QImage::Format_RGB16)
+ blendTransformedBilinear<qrgb565, qrgb565>(count, spans, userData);
+ else if (data->texture.format == QImage::Format_ARGB8565_Premultiplied)
+ blendTransformedBilinear<qrgb565, qargb8565>(count, spans, userData);
+ else
+#endif
+ blend_src_generic<RegularSpans>(count, spans, userData
+ Q_TEMPLATE_ENUM_CALL(SpanMethod, RegularSpans));
+}
+
+static void blend_transformed_bilinear_argb8555(int count, const QSpan *spans, void *userData)
+{
+#if !defined(Q_WS_QWS) || defined(QT_QWS_DEPTH_15)
+ QSpanData *data = reinterpret_cast<QSpanData *>(userData);
+
+ if (data->texture.format == QImage::Format_ARGB8555_Premultiplied)
+ blendTransformedBilinear<qargb8555, qargb8555>(count, spans, userData);
+ else if (data->texture.format == QImage::Format_RGB555)
+ blendTransformedBilinear<qargb8555, qrgb555>(count, spans, userData);
+ else
+#endif
+ blend_src_generic<RegularSpans>(count, spans, userData
+ Q_TEMPLATE_ENUM_CALL(SpanMethod, RegularSpans));
+}
+
+static void blend_transformed_bilinear_rgb555(int count, const QSpan *spans, void *userData)
+{
+#if !defined(Q_WS_QWS) || defined(QT_QWS_DEPTH_15)
+ QSpanData *data = reinterpret_cast<QSpanData *>(userData);
+
+ if (data->texture.format == QImage::Format_ARGB8555_Premultiplied)
+ blendTransformedBilinear<qrgb555, qargb8555>(count, spans, userData);
+ else if (data->texture.format == QImage::Format_RGB555)
+ blendTransformedBilinear<qrgb555, qrgb555>(count, spans, userData);
+ else
+#endif
+ blend_src_generic<RegularSpans>(count, spans, userData
+ Q_TEMPLATE_ENUM_CALL(SpanMethod, RegularSpans));
+}
+
+static void blend_transformed_bilinear_argb4444(int count, const QSpan *spans, void *userData)
+{
+#if !defined(Q_WS_QWS) || defined(QT_QWS_DEPTH_12)
+ QSpanData *data = reinterpret_cast<QSpanData *>(userData);
+
+ if (data->texture.format == QImage::Format_ARGB4444_Premultiplied)
+ blendTransformedBilinear<qargb4444, qargb4444>(count, spans, userData);
+ else if (data->texture.format == QImage::Format_RGB444)
+ blendTransformedBilinear<qargb4444, qrgb444>(count, spans, userData);
+ else
+#endif
+ blend_src_generic<RegularSpans>(count, spans, userData
+ Q_TEMPLATE_ENUM_CALL(SpanMethod, RegularSpans));
+}
+
+static void blend_transformed_bilinear_rgb444(int count, const QSpan *spans, void *userData)
+{
+#if !defined(Q_WS_QWS) || defined(QT_QWS_DEPTH_12)
+ QSpanData *data = reinterpret_cast<QSpanData *>(userData);
+
+ if (data->texture.format == QImage::Format_ARGB4444_Premultiplied)
+ blendTransformedBilinear<qrgb444, qargb4444>(count, spans, userData);
+ else if (data->texture.format == QImage::Format_RGB444)
+ blendTransformedBilinear<qrgb444, qrgb444>(count, spans, userData);
+ else
+#endif
+ blend_src_generic<RegularSpans>(count, spans, userData
+ Q_TEMPLATE_ENUM_CALL(SpanMethod, RegularSpans));
+}
+
+template <SpanMethod spanMethod>
+Q_STATIC_TEMPLATE_FUNCTION void blend_transformed_bilinear_tiled_argb(int count, const QSpan *spans, void *userData
+ Q_TEMPLATE_ENUM_FIX(SpanMethod, spanMethod))
+{
+ QSpanData *data = reinterpret_cast<QSpanData *>(userData);
+ if (data->texture.format != QImage::Format_ARGB32_Premultiplied
+ && data->texture.format != QImage::Format_RGB32) {
+ blend_src_generic<spanMethod>(count, spans, userData Q_TEMPLATE_ENUM_CALL(SpanMethod, spanMethod));
+ return;
+ }
+
+ CompositionFunction func = functionForMode[data->rasterBuffer->compositionMode];
+ uint buffer[buffer_size];
+
+ int image_width = data->texture.width;
+ int image_height = data->texture.height;
+ const int scanline_offset = data->texture.bytesPerLine / 4;
+
+ if (data->fast_matrix) {
+ // The increment pr x in the scanline
+ int fdx = (int)(data->m11 * fixed_scale);
+ int fdy = (int)(data->m12 * fixed_scale);
+
+ while (count--) {
+ void *t = data->rasterBuffer->scanLine(spans->y);
+
+ uint *target = ((uint *)t) + spans->x;
+ uint *image_bits = (uint *)data->texture.imageData;
+
+ const qreal cx = spans->x + 0.5;
+ const qreal cy = spans->y + 0.5;
+
+ int x = int((data->m21 * cy
+ + data->m11 * cx + data->dx) * fixed_scale) - half_point;
+ int y = int((data->m22 * cy
+ + data->m12 * cx + data->dy) * fixed_scale) - half_point;
+
+ int length = spans->len;
+ const int coverage = (spans->coverage * data->texture.const_alpha) >> 8;
+ while (length) {
+ int l = qMin(length, buffer_size);
+ const uint *end = buffer + l;
+ uint *b = buffer;
+ while (b < end) {
+ int x1 = (x >> 16);
+ int x2 = (x1 + 1);
+ int y1 = (y >> 16);
+ int y2 = (y1 + 1);
+
+ int distx = ((x - (x1 << 16)) >> 8);
+ int disty = ((y - (y1 << 16)) >> 8);
+ int idistx = 256 - distx;
+ int idisty = 256 - disty;
+
+ x1 %= image_width;
+ x2 %= image_width;
+ y1 %= image_height;
+ y2 %= image_height;
+
+ if (x1 < 0) x1 += image_width;
+ if (x2 < 0) x2 += image_width;
+ if (y1 < 0) y1 += image_height;
+ if (y2 < 0) y2 += image_height;
+
+ Q_ASSERT(x1 >= 0 && x1 < image_width);
+ Q_ASSERT(x2 >= 0 && x2 < image_width);
+ Q_ASSERT(y1 >= 0 && y1 < image_height);
+ Q_ASSERT(y2 >= 0 && y2 < image_height);
+
+ int y1_offset = y1 * scanline_offset;
+ int y2_offset = y2 * scanline_offset;
+
+#if defined(Q_IRIX_GCC3_3_WORKAROUND)
+ uint tl = gccBug(image_bits[y1_offset + x1]);
+ uint tr = gccBug(image_bits[y1_offset + x2]);
+ uint bl = gccBug(image_bits[y2_offset + x1]);
+ uint br = gccBug(image_bits[y2_offset + x2]);
+#else
+ uint tl = image_bits[y1_offset + x1];
+ uint tr = image_bits[y1_offset + x2];
+ uint bl = image_bits[y2_offset + x1];
+ uint br = image_bits[y2_offset + x2];
+#endif
+
+ uint xtop = INTERPOLATE_PIXEL_256(tl, idistx, tr, distx);
+ uint xbot = INTERPOLATE_PIXEL_256(bl, idistx, br, distx);
+ *b = INTERPOLATE_PIXEL_256(xtop, idisty, xbot, disty);
+ ++b;
+ x += fdx;
+ y += fdy;
+ }
+ if (spanMethod == RegularSpans)
+ func(target, buffer, l, coverage);
+ else
+ drawBufferSpan(data, buffer, buffer_size,
+ spans->x + spans->len - length,
+ spans->y, l, coverage);
+ target += l;
+ length -= l;
+ }
+ ++spans;
+ }
+ } else {
+ const qreal fdx = data->m11;
+ const qreal fdy = data->m12;
+ const qreal fdw = data->m13;
+ while (count--) {
+ void *t = data->rasterBuffer->scanLine(spans->y);
+
+ uint *target = ((uint *)t) + spans->x;
+ uint *image_bits = (uint *)data->texture.imageData;
+
+ const qreal cx = spans->x + 0.5;
+ const qreal cy = spans->y + 0.5;
+
+ qreal x = data->m21 * cy + data->m11 * cx + data->dx;
+ qreal y = data->m22 * cy + data->m12 * cx + data->dy;
+ qreal w = data->m23 * cy + data->m13 * cx + data->m33;
+
+ int length = spans->len;
+ const int coverage = (spans->coverage * data->texture.const_alpha) >> 8;
+ while (length) {
+ int l = qMin(length, buffer_size);
+ const uint *end = buffer + l;
+ uint *b = buffer;
+ while (b < end) {
+ const qreal iw = w == 0 ? 1 : 1 / w;
+ const qreal px = x * iw - 0.5;
+ const qreal py = y * iw - 0.5;
+
+ int x1 = int(px) - (px < 0);
+ int x2 = x1 + 1;
+ int y1 = int(py) - (py < 0);
+ int y2 = y1 + 1;
+
+ int distx = int((px - x1) * 256);
+ int disty = int((py - y1) * 256);
+ int idistx = 256 - distx;
+ int idisty = 256 - disty;
+
+ x1 %= image_width;
+ x2 %= image_width;
+ y1 %= image_height;
+ y2 %= image_height;
+
+ if (x1 < 0) x1 += image_width;
+ if (x2 < 0) x2 += image_width;
+ if (y1 < 0) y1 += image_height;
+ if (y2 < 0) y2 += image_height;
+
+ Q_ASSERT(x1 >= 0 && x1 < image_width);
+ Q_ASSERT(x2 >= 0 && x2 < image_width);
+ Q_ASSERT(y1 >= 0 && y1 < image_height);
+ Q_ASSERT(y2 >= 0 && y2 < image_height);
+
+ int y1_offset = y1 * scanline_offset;
+ int y2_offset = y2 * scanline_offset;
+
+#if defined(Q_IRIX_GCC3_3_WORKAROUND)
+ uint tl = gccBug(image_bits[y1_offset + x1]);
+ uint tr = gccBug(image_bits[y1_offset + x2]);
+ uint bl = gccBug(image_bits[y2_offset + x1]);
+ uint br = gccBug(image_bits[y2_offset + x2]);
+#else
+ uint tl = image_bits[y1_offset + x1];
+ uint tr = image_bits[y1_offset + x2];
+ uint bl = image_bits[y2_offset + x1];
+ uint br = image_bits[y2_offset + x2];
+#endif
+
+ uint xtop = INTERPOLATE_PIXEL_256(tl, idistx, tr, distx);
+ uint xbot = INTERPOLATE_PIXEL_256(bl, idistx, br, distx);
+ *b = INTERPOLATE_PIXEL_256(xtop, idisty, xbot, disty);
+ ++b;
+ x += fdx;
+ y += fdy;
+ w += fdw;
+ }
+ if (spanMethod == RegularSpans)
+ func(target, buffer, l, coverage);
+ else
+ drawBufferSpan(data, buffer, buffer_size,
+ spans->x + spans->len - length,
+ spans->y, l, coverage);
+ target += l;
+ length -= l;
+ }
+ ++spans;
+ }
+ }
+}
+
+template <SpanMethod spanMethod>
+Q_STATIC_TEMPLATE_FUNCTION void blend_transformed_argb(int count, const QSpan *spans, void *userData
+ Q_TEMPLATE_ENUM_FIX(SpanMethod, spanMethod))
+{
+ QSpanData *data = reinterpret_cast<QSpanData *>(userData);
+ if (data->texture.format != QImage::Format_ARGB32_Premultiplied
+ && data->texture.format != QImage::Format_RGB32) {
+ blend_src_generic<spanMethod>(count, spans, userData Q_TEMPLATE_ENUM_CALL(SpanMethod, spanMethod));
+ return;
+ }
+
+ CompositionFunction func = functionForMode[data->rasterBuffer->compositionMode];
+ uint buffer[buffer_size];
+
+ int image_width = data->texture.width;
+ int image_height = data->texture.height;
+ const int scanline_offset = data->texture.bytesPerLine / 4;
+
+ if (data->fast_matrix) {
+ // The increment pr x in the scanline
+ int fdx = (int)(data->m11 * fixed_scale);
+ int fdy = (int)(data->m12 * fixed_scale);
+
+ while (count--) {
+ void *t = data->rasterBuffer->scanLine(spans->y);
+
+ uint *target = ((uint *)t) + spans->x;
+ uint *image_bits = (uint *)data->texture.imageData;
+
+ const qreal cx = spans->x + 0.5;
+ const qreal cy = spans->y + 0.5;
+
+ int x = int((data->m21 * cy
+ + data->m11 * cx + data->dx) * fixed_scale);
+ int y = int((data->m22 * cy
+ + data->m12 * cx + data->dy) * fixed_scale);
+
+ int length = spans->len;
+ const int coverage = (spans->coverage * data->texture.const_alpha) >> 8;
+ while (length) {
+ int l = qMin(length, buffer_size);
+ const uint *end = buffer + l;
+ uint *b = buffer;
+ while (b < end) {
+ int px = x >> 16;
+ int py = y >> 16;
+
+ bool out = (px < 0) || (px >= image_width)
+ || (py < 0) || (py >= image_height);
+
+ int y_offset = py * scanline_offset;
+ *b = out ? uint(0) : image_bits[y_offset + px];
+ x += fdx;
+ y += fdy;
+ ++b;
+ }
+ if (spanMethod == RegularSpans)
+ func(target, buffer, l, coverage);
+ else
+ drawBufferSpan(data, buffer, buffer_size,
+ spans->x + spans->len - length,
+ spans->y, l, coverage);
+ target += l;
+ length -= l;
+ }
+ ++spans;
+ }
+ } else {
+ const qreal fdx = data->m11;
+ const qreal fdy = data->m12;
+ const qreal fdw = data->m13;
+ while (count--) {
+ void *t = data->rasterBuffer->scanLine(spans->y);
+
+ uint *target = ((uint *)t) + spans->x;
+ uint *image_bits = (uint *)data->texture.imageData;
+
+ const qreal cx = spans->x + 0.5;
+ const qreal cy = spans->y + 0.5;
+
+ qreal x = data->m21 * cy + data->m11 * cx + data->dx;
+ qreal y = data->m22 * cy + data->m12 * cx + data->dy;
+ qreal w = data->m23 * cy + data->m13 * cx + data->m33;
+
+ int length = spans->len;
+ const int coverage = (spans->coverage * data->texture.const_alpha) >> 8;
+ while (length) {
+ int l = qMin(length, buffer_size);
+ const uint *end = buffer + l;
+ uint *b = buffer;
+ while (b < end) {
+ const qreal iw = w == 0 ? 1 : 1 / w;
+ const qreal tx = x * iw;
+ const qreal ty = y * iw;
+ const int px = int(tx) - (tx < 0);
+ const int py = int(ty) - (ty < 0);
+
+ bool out = (px < 0) || (px >= image_width)
+ || (py < 0) || (py >= image_height);
+
+ int y_offset = py * scanline_offset;
+ *b = out ? uint(0) : image_bits[y_offset + px];
+ x += fdx;
+ y += fdy;
+ w += fdw;
+
+ ++b;
+ }
+ if (spanMethod == RegularSpans)
+ func(target, buffer, l, coverage);
+ else
+ drawBufferSpan(data, buffer, buffer_size,
+ spans->x + spans->len - length,
+ spans->y, l, coverage);
+ target += l;
+ length -= l;
+ }
+ ++spans;
+ }
+ }
+}
+
+template <class DST, class SRC>
+Q_STATIC_TEMPLATE_FUNCTION void blendTransformed(int count, const QSpan *spans, void *userData)
+{
+ QSpanData *data = reinterpret_cast<QSpanData*>(userData);
+ QPainter::CompositionMode mode = data->rasterBuffer->compositionMode;
+
+ if (mode != QPainter::CompositionMode_SourceOver) {
+ blend_src_generic<RegularSpans>(count, spans, userData
+ Q_TEMPLATE_ENUM_CALL(SpanMethod, RegularSpans));
+ return;
+ }
+
+ SRC buffer[buffer_size];
+ const int image_width = data->texture.width;
+ const int image_height = data->texture.height;
+
+ if (data->fast_matrix) {
+ // The increment pr x in the scanline
+ const int fdx = (int)(data->m11 * fixed_scale);
+ const int fdy = (int)(data->m12 * fixed_scale);
+
+ while (count--) {
+ const quint8 coverage = (data->texture.const_alpha * spans->coverage) >> 8;
+ if (coverage == 0) {
+ ++spans;
+ continue;
+ }
+
+ DST *dest = (DST*)data->rasterBuffer->scanLine(spans->y)
+ + spans->x;
+ const qreal cx = spans->x + qreal(0.5);
+ const qreal cy = spans->y + qreal(0.5);
+ int x = int((data->m21 * cy
+ + data->m11 * cx + data->dx) * fixed_scale);
+ int y = int((data->m22 * cy
+ + data->m12 * cx + data->dy) * fixed_scale);
+ int length = spans->len;
+
+ while (length) {
+ const int l = qMin(length, buffer_size);
+
+ const SRC *end = buffer + l;
+ SRC *b = buffer;
+ while (b < end) {
+ const int px = (x >> 16);
+ const int py = (y >> 16);
+
+ if ((px < 0) || (px >= image_width) ||
+ (py < 0) || (py >= image_height))
+ {
+ *b = 0;
+ } else {
+ *b = ((SRC*)data->texture.scanLine(py))[px];
+ }
+ ++b;
+
+ x += fdx;
+ y += fdy;
+ }
+
+ if (!SRC::hasAlpha() && coverage == 255) {
+ qt_memconvert(dest, buffer, l);
+ } else if (sizeof(DST) == 3 && sizeof(SRC) == 3 && l >= 4 &&
+ (quintptr(dest) & 3) == (quintptr(buffer) & 3))
+ {
+ blendUntransformed_dest24(dest, buffer, coverage, l);
+ } else if (sizeof(DST) == 2 && sizeof(SRC) == 2 && l >= 2 &&
+ (quintptr(dest) & 3) == (quintptr(buffer) & 3)) {
+ blendUntransformed_dest16(dest, buffer, coverage, l);
+ } else {
+ blendUntransformed_unaligned(dest, buffer, coverage, l);
+ }
+
+ dest += l;
+ length -= l;
+ }
+ ++spans;
+ }
+ } else {
+ const qreal fdx = data->m11;
+ const qreal fdy = data->m12;
+ const qreal fdw = data->m13;
+
+ while (count--) {
+ const quint8 coverage = (data->texture.const_alpha * spans->coverage) >> 8;
+ if (coverage == 0) {
+ ++spans;
+ continue;
+ }
+
+ DST *dest = (DST*)data->rasterBuffer->scanLine(spans->y)
+ + spans->x;
+
+ const qreal cx = spans->x + qreal(0.5);
+ const qreal cy = spans->y + qreal(0.5);
+
+ qreal x = data->m21 * cy + data->m11 * cx + data->dx;
+ qreal y = data->m22 * cy + data->m12 * cx + data->dy;
+ qreal w = data->m23 * cy + data->m13 * cx + data->m33;
+
+ int length = spans->len;
+ while (length) {
+ const int l = qMin(length, buffer_size);
+ const SRC *end = buffer + l;
+ SRC *b = buffer;
+ while (b < end) {
+ const qreal iw = w == 0 ? 1 : 1 / w;
+ const qreal tx = x * iw;
+ const qreal ty = y * iw;
+
+ const int px = int(tx) - (tx < 0);
+ const int py = int(ty) - (ty < 0);
+
+ if ((px < 0) || (px >= image_width) ||
+ (py < 0) || (py >= image_height))
+ {
+ *b = 0;
+ } else {
+ *b = ((SRC*)data->texture.scanLine(py))[px];
+ }
+ ++b;
+
+ x += fdx;
+ y += fdy;
+ w += fdw;
+ }
+ if (!SRC::hasAlpha() && coverage == 255) {
+ qt_memconvert(dest, buffer, l);
+ } else if (sizeof(DST) == 3 && sizeof(SRC) == 3 && l >= 4 &&
+ (quintptr(dest) & 3) == (quintptr(buffer) & 3))
+ {
+ blendUntransformed_dest24(dest, buffer, coverage, l);
+ } else if (sizeof(DST) == 2 && sizeof(SRC) == 2 && l >= 2 &&
+ (quintptr(dest) & 3) == (quintptr(buffer) & 3)) {
+ blendUntransformed_dest16(dest, buffer, coverage, l);
+ } else {
+ blendUntransformed_unaligned(dest, buffer, coverage, l);
+ }
+
+ dest += l;
+ length -= l;
+ }
+ ++spans;
+ }
+ }
+}
+
+static void blend_transformed_rgb888(int count, const QSpan *spans,
+ void *userData)
+{
+#if !defined(Q_WS_QWS) || defined(QT_QWS_DEPTH_24)
+ QSpanData *data = reinterpret_cast<QSpanData *>(userData);
+
+ if (data->texture.format == QImage::Format_RGB888)
+ blendTransformed<qrgb888, qrgb888>(count, spans, userData);
+ else
+#endif
+ blend_src_generic<RegularSpans>(count, spans, userData
+ Q_TEMPLATE_ENUM_CALL(SpanMethod, RegularSpans));
+}
+
+static void blend_transformed_argb6666(int count, const QSpan *spans,
+ void *userData)
+{
+#if !defined(Q_WS_QWS) || defined(QT_QWS_DEPTH_18)
+ QSpanData *data = reinterpret_cast<QSpanData *>(userData);
+
+ if (data->texture.format == QImage::Format_ARGB6666_Premultiplied)
+ blendTransformed<qargb6666, qargb6666>(count, spans, userData);
+ else if (data->texture.format == QImage::Format_RGB666)
+ blendTransformed<qargb6666, qrgb666>(count, spans, userData);
+ else
+#endif
+ blend_src_generic<RegularSpans>(count, spans, userData
+ Q_TEMPLATE_ENUM_CALL(SpanMethod, RegularSpans));
+}
+
+static void blend_transformed_rgb666(int count, const QSpan *spans,
+ void *userData)
+{
+#if !defined(Q_WS_QWS) || defined(QT_QWS_DEPTH_18)
+ QSpanData *data = reinterpret_cast<QSpanData *>(userData);
+
+ if (data->texture.format == QImage::Format_ARGB6666_Premultiplied)
+ blendTransformed<qrgb666, qargb6666>(count, spans, userData);
+ else if (data->texture.format == QImage::Format_RGB666)
+ blendTransformed<qrgb666, qrgb666>(count, spans, userData);
+ else
+#endif
+ blend_src_generic<RegularSpans>(count, spans, userData
+ Q_TEMPLATE_ENUM_CALL(SpanMethod, RegularSpans));
+}
+
+static void blend_transformed_argb8565(int count, const QSpan *spans,
+ void *userData)
+{
+#if !defined(Q_WS_QWS) || defined(QT_QWS_DEPTH_16)
+ QSpanData *data = reinterpret_cast<QSpanData *>(userData);
+
+ if (data->texture.format == QImage::Format_ARGB8565_Premultiplied)
+ blendTransformed<qargb8565, qargb8565>(count, spans, userData);
+ else if (data->texture.format == QImage::Format_RGB16)
+ blendTransformed<qargb8565, qrgb565>(count, spans, userData);
+ else
+#endif
+ blend_src_generic<RegularSpans>(count, spans, userData
+ Q_TEMPLATE_ENUM_CALL(SpanMethod, RegularSpans));
+}
+
+static void blend_transformed_rgb565(int count, const QSpan *spans,
+ void *userData)
+{
+#if !defined(Q_WS_QWS) || defined(QT_QWS_DEPTH_16)
+ QSpanData *data = reinterpret_cast<QSpanData *>(userData);
+
+ if (data->texture.format == QImage::Format_ARGB8565_Premultiplied)
+ blendTransformed<qrgb565, qargb8565>(count, spans, userData);
+ else if (data->texture.format == QImage::Format_RGB16)
+ blendTransformed<qrgb565, qrgb565>(count, spans, userData);
+ else
+#endif
+ blend_src_generic<RegularSpans>(count, spans, userData
+ Q_TEMPLATE_ENUM_CALL(SpanMethod, RegularSpans));
+}
+
+static void blend_transformed_argb8555(int count, const QSpan *spans,
+ void *userData)
+{
+#if !defined(Q_WS_QWS) || defined(QT_QWS_DEPTH_15)
+ QSpanData *data = reinterpret_cast<QSpanData *>(userData);
+
+ if (data->texture.format == QImage::Format_ARGB8555_Premultiplied)
+ blendTransformed<qargb8555, qargb8555>(count, spans, userData);
+ else if (data->texture.format == QImage::Format_RGB555)
+ blendTransformed<qargb8555, qrgb555>(count, spans, userData);
+ else
+#endif
+ blend_src_generic<RegularSpans>(count, spans, userData
+ Q_TEMPLATE_ENUM_CALL(SpanMethod, RegularSpans));
+}
+
+static void blend_transformed_rgb555(int count, const QSpan *spans,
+ void *userData)
+{
+#if !defined(Q_WS_QWS) || defined(QT_QWS_DEPTH_15)
+ QSpanData *data = reinterpret_cast<QSpanData *>(userData);
+
+ if (data->texture.format == QImage::Format_ARGB8555_Premultiplied)
+ blendTransformed<qrgb555, qargb8555>(count, spans, userData);
+ else if (data->texture.format == QImage::Format_RGB555)
+ blendTransformed<qrgb555, qrgb555>(count, spans, userData);
+ else
+#endif
+ blend_src_generic<RegularSpans>(count, spans, userData
+ Q_TEMPLATE_ENUM_CALL(SpanMethod, RegularSpans));
+}
+
+static void blend_transformed_argb4444(int count, const QSpan *spans,
+ void *userData)
+{
+#if !defined(Q_WS_QWS) || defined(QT_QWS_DEPTH_12)
+ QSpanData *data = reinterpret_cast<QSpanData *>(userData);
+
+ if (data->texture.format == QImage::Format_ARGB4444_Premultiplied)
+ blendTransformed<qargb4444, qargb4444>(count, spans, userData);
+ else if (data->texture.format == QImage::Format_RGB444)
+ blendTransformed<qargb4444, qrgb444>(count, spans, userData);
+ else
+#endif
+ blend_src_generic<RegularSpans>(count, spans, userData
+ Q_TEMPLATE_ENUM_CALL(SpanMethod, RegularSpans));
+}
+
+static void blend_transformed_rgb444(int count, const QSpan *spans,
+ void *userData)
+{
+#if !defined(Q_WS_QWS) || defined(QT_QWS_DEPTH_12)
+ QSpanData *data = reinterpret_cast<QSpanData *>(userData);
+
+ if (data->texture.format == QImage::Format_ARGB4444_Premultiplied)
+ blendTransformed<qrgb444, qargb4444>(count, spans, userData);
+ else if (data->texture.format == QImage::Format_RGB444)
+ blendTransformed<qrgb444, qrgb444>(count, spans, userData);
+ else
+#endif
+ blend_src_generic<RegularSpans>(count, spans, userData
+ Q_TEMPLATE_ENUM_CALL(SpanMethod, RegularSpans));
+}
+
+template <SpanMethod spanMethod>
+Q_STATIC_TEMPLATE_FUNCTION void blend_transformed_tiled_argb(int count, const QSpan *spans, void *userData
+ Q_TEMPLATE_ENUM_FIX(SpanMethod, spanMethod))
+{
+ QSpanData *data = reinterpret_cast<QSpanData *>(userData);
+ if (data->texture.format != QImage::Format_ARGB32_Premultiplied
+ && data->texture.format != QImage::Format_RGB32) {
+ blend_src_generic<spanMethod>(count, spans, userData Q_TEMPLATE_ENUM_CALL(SpanMethod, spanMethod));
+ return;
+ }
+
+ CompositionFunction func = functionForMode[data->rasterBuffer->compositionMode];
+ uint buffer[buffer_size];
+
+ int image_width = data->texture.width;
+ int image_height = data->texture.height;
+ const int scanline_offset = data->texture.bytesPerLine / 4;
+
+ if (data->fast_matrix) {
+ // The increment pr x in the scanline
+ int fdx = (int)(data->m11 * fixed_scale);
+ int fdy = (int)(data->m12 * fixed_scale);
+
+ while (count--) {
+ void *t = data->rasterBuffer->scanLine(spans->y);
+
+ uint *target = ((uint *)t) + spans->x;
+ uint *image_bits = (uint *)data->texture.imageData;
+
+ const qreal cx = spans->x + 0.5;
+ const qreal cy = spans->y + 0.5;
+
+ int x = int((data->m21 * cy
+ + data->m11 * cx + data->dx) * fixed_scale);
+ int y = int((data->m22 * cy
+ + data->m12 * cx + data->dy) * fixed_scale);
+
+ const int coverage = (spans->coverage * data->texture.const_alpha) >> 8;
+ int length = spans->len;
+ while (length) {
+ int l = qMin(length, buffer_size);
+ const uint *end = buffer + l;
+ uint *b = buffer;
+ while (b < end) {
+ int px = x >> 16;
+ int py = y >> 16;
+ px %= image_width;
+ py %= image_height;
+ if (px < 0) px += image_width;
+ if (py < 0) py += image_height;
+ int y_offset = py * scanline_offset;
+
+ Q_ASSERT(px >= 0 && px < image_width);
+ Q_ASSERT(py >= 0 && py < image_height);
+
+ *b = image_bits[y_offset + px];
+ x += fdx;
+ y += fdy;
+ ++b;
+ }
+ if (spanMethod == RegularSpans)
+ func(target, buffer, l, coverage);
+ else
+ drawBufferSpan(data, buffer, buffer_size,
+ spans->x + spans->len - length,
+ spans->y, l, coverage);
+ target += l;
+ length -= l;
+ }
+ ++spans;
+ }
+ } else {
+ const qreal fdx = data->m11;
+ const qreal fdy = data->m12;
+ const qreal fdw = data->m13;
+ while (count--) {
+ void *t = data->rasterBuffer->scanLine(spans->y);
+
+ uint *target = ((uint *)t) + spans->x;
+ uint *image_bits = (uint *)data->texture.imageData;
+
+ const qreal cx = spans->x + 0.5;
+ const qreal cy = spans->y + 0.5;
+
+ qreal x = data->m21 * cy + data->m11 * cx + data->dx;
+ qreal y = data->m22 * cy + data->m12 * cx + data->dy;
+ qreal w = data->m23 * cy + data->m13 * cx + data->m33;
+
+ const int coverage = (spans->coverage * data->texture.const_alpha) >> 8;
+ int length = spans->len;
+ while (length) {
+ int l = qMin(length, buffer_size);
+ const uint *end = buffer + l;
+ uint *b = buffer;
+ while (b < end) {
+ const qreal iw = w == 0 ? 1 : 1 / w;
+ const qreal tx = x * iw;
+ const qreal ty = y * iw;
+ int px = int(tx) - (tx < 0);
+ int py = int(ty) - (ty < 0);
+
+ px %= image_width;
+ py %= image_height;
+ if (px < 0) px += image_width;
+ if (py < 0) py += image_height;
+ int y_offset = py * scanline_offset;
+
+ Q_ASSERT(px >= 0 && px < image_width);
+ Q_ASSERT(py >= 0 && py < image_height);
+
+ *b = image_bits[y_offset + px];
+ x += fdx;
+ y += fdy;
+ w += fdw;
+ //force increment to avoid /0
+ if (!w) {
+ w += fdw;
+ }
+ ++b;
+ }
+ if (spanMethod == RegularSpans)
+ func(target, buffer, l, coverage);
+ else
+ drawBufferSpan(data, buffer, buffer_size,
+ spans->x + spans->len - length,
+ spans->y, l, coverage);
+ target += l;
+ length -= l;
+ }
+ ++spans;
+ }
+ }
+}
+
+template <class DST, class SRC>
+Q_STATIC_TEMPLATE_FUNCTION void blendTransformedTiled(int count, const QSpan *spans, void *userData)
+{
+ QSpanData *data = reinterpret_cast<QSpanData*>(userData);
+ QPainter::CompositionMode mode = data->rasterBuffer->compositionMode;
+
+ if (mode != QPainter::CompositionMode_SourceOver) {
+ blend_src_generic<RegularSpans>(count, spans, userData
+ Q_TEMPLATE_ENUM_CALL(SpanMethod, RegularSpans));
+ return;
+ }
+
+ SRC buffer[buffer_size];
+ const int image_width = data->texture.width;
+ const int image_height = data->texture.height;
+
+ if (data->fast_matrix) {
+ // The increment pr x in the scanline
+ const int fdx = (int)(data->m11 * fixed_scale);
+ const int fdy = (int)(data->m12 * fixed_scale);
+
+ while (count--) {
+ const quint8 coverage = (data->texture.const_alpha * spans->coverage) >> 8;
+ if (coverage == 0) {
+ ++spans;
+ continue;
+ }
+
+ DST *dest = (DST*)data->rasterBuffer->scanLine(spans->y)
+ + spans->x;
+ const qreal cx = spans->x + qreal(0.5);
+ const qreal cy = spans->y + qreal(0.5);
+ int x = int((data->m21 * cy
+ + data->m11 * cx + data->dx) * fixed_scale);
+ int y = int((data->m22 * cy
+ + data->m12 * cx + data->dy) * fixed_scale);
+ int length = spans->len;
+
+ while (length) {
+ const int l = qMin(length, buffer_size);
+
+ const SRC *end = buffer + l;
+ SRC *b = buffer;
+ while (b < end) {
+ int px = (x >> 16) % image_width;
+ int py = (y >> 16) % image_height;
+
+ if (px < 0)
+ px += image_width;
+ if (py < 0)
+ py += image_height;
+
+ *b = ((SRC*)data->texture.scanLine(py))[px];
+ ++b;
+
+ x += fdx;
+ y += fdy;
+ }
+
+ if (!SRC::hasAlpha() && coverage == 255) {
+ qt_memconvert(dest, buffer, l);
+ } else if (sizeof(DST) == 3 && sizeof(SRC) == 3 && l >= 4 &&
+ (quintptr(dest) & 3) == (quintptr(buffer) & 3))
+ {
+ blendUntransformed_dest24(dest, buffer, coverage, l);
+ } else if (sizeof(DST) == 2 && sizeof(SRC) == 2 && l >= 2 &&
+ (quintptr(dest) & 3) == (quintptr(buffer) & 3)) {
+ blendUntransformed_dest16(dest, buffer, coverage, l);
+ } else {
+ blendUntransformed_unaligned(dest, buffer, coverage, l);
+ }
+
+ dest += l;
+ length -= l;
+ }
+ ++spans;
+ }
+ } else {
+ const qreal fdx = data->m11;
+ const qreal fdy = data->m12;
+ const qreal fdw = data->m13;
+
+ while (count--) {
+ const quint8 coverage = (data->texture.const_alpha * spans->coverage) >> 8;
+ if (coverage == 0) {
+ ++spans;
+ continue;
+ }
+
+ DST *dest = (DST*)data->rasterBuffer->scanLine(spans->y)
+ + spans->x;
+
+ const qreal cx = spans->x + qreal(0.5);
+ const qreal cy = spans->y + qreal(0.5);
+
+ qreal x = data->m21 * cy + data->m11 * cx + data->dx;
+ qreal y = data->m22 * cy + data->m12 * cx + data->dy;
+ qreal w = data->m23 * cy + data->m13 * cx + data->m33;
+
+ int length = spans->len;
+ while (length) {
+ const int l = qMin(length, buffer_size);
+ const SRC *end = buffer + l;
+ SRC *b = buffer;
+ while (b < end) {
+ const qreal iw = w == 0 ? 1 : 1 / w;
+ const qreal tx = x * iw;
+ const qreal ty = y * iw;
+
+ int px = int(tx) - (tx < 0);
+ int py = int(ty) - (ty < 0);
+
+ if (px < 0)
+ px += image_width;
+ if (py < 0)
+ py += image_height;
+
+ *b = ((SRC*)data->texture.scanLine(py))[px];
+ ++b;
+
+ x += fdx;
+ y += fdy;
+ w += fdw;
+ // force increment to avoid /0
+ if (!w)
+ w += fdw;
+ }
+ if (!SRC::hasAlpha() && coverage == 255) {
+ qt_memconvert(dest, buffer, l);
+ } else if (sizeof(DST) == 3 && sizeof(SRC) == 3 && l >= 4 &&
+ (quintptr(dest) & 3) == (quintptr(buffer) & 3))
+ {
+ blendUntransformed_dest24(dest, buffer, coverage, l);
+ } else if (sizeof(DST) == 2 && sizeof(SRC) == 2 && l >= 2 &&
+ (quintptr(dest) & 3) == (quintptr(buffer) & 3)) {
+ blendUntransformed_dest16(dest, buffer, coverage, l);
+ } else {
+ blendUntransformed_unaligned(dest, buffer, coverage, l);
+ }
+
+ dest += l;
+ length -= l;
+ }
+ ++spans;
+ }
+ }
+}
+
+static void blend_transformed_tiled_rgb888(int count, const QSpan *spans,
+ void *userData)
+{
+#if !defined(Q_WS_QWS) || defined(QT_QWS_DEPTH_24)
+ QSpanData *data = reinterpret_cast<QSpanData *>(userData);
+
+ if (data->texture.format == QImage::Format_RGB888)
+ blendTransformedTiled<qrgb888, qrgb888>(count, spans, userData);
+ else
+#endif
+ blend_src_generic<RegularSpans>(count, spans, userData
+ Q_TEMPLATE_ENUM_CALL(SpanMethod, RegularSpans));
+}
+
+static void blend_transformed_tiled_argb6666(int count, const QSpan *spans,
+ void *userData)
+{
+#if !defined(Q_WS_QWS) || defined(QT_QWS_DEPTH_18)
+ QSpanData *data = reinterpret_cast<QSpanData *>(userData);
+
+ if (data->texture.format == QImage::Format_ARGB6666_Premultiplied)
+ blendTransformedTiled<qargb6666, qargb6666>(count, spans, userData);
+ else if (data->texture.format == QImage::Format_RGB666)
+ blendTransformedTiled<qargb6666, qrgb666>(count, spans, userData);
+ else
+#endif
+ blend_src_generic<RegularSpans>(count, spans, userData
+ Q_TEMPLATE_ENUM_CALL(SpanMethod, RegularSpans));
+}
+
+static void blend_transformed_tiled_rgb666(int count, const QSpan *spans,
+ void *userData)
+{
+#if !defined(Q_WS_QWS) || defined(QT_QWS_DEPTH_18)
+ QSpanData *data = reinterpret_cast<QSpanData *>(userData);
+
+ if (data->texture.format == QImage::Format_ARGB6666_Premultiplied)
+ blendTransformedTiled<qrgb666, qargb6666>(count, spans, userData);
+ else if (data->texture.format == QImage::Format_RGB666)
+ blendTransformedTiled<qrgb666, qrgb666>(count, spans, userData);
+ else
+#endif
+ blend_src_generic<RegularSpans>(count, spans, userData
+ Q_TEMPLATE_ENUM_CALL(SpanMethod, RegularSpans));
+}
+
+static void blend_transformed_tiled_argb8565(int count, const QSpan *spans,
+ void *userData)
+{
+#if !defined(Q_WS_QWS) || defined(QT_QWS_DEPTH_16)
+ QSpanData *data = reinterpret_cast<QSpanData *>(userData);
+
+ if (data->texture.format == QImage::Format_ARGB8565_Premultiplied)
+ blendTransformedTiled<qargb8565, qargb8565>(count, spans, userData);
+ else if (data->texture.format == QImage::Format_RGB16)
+ blendTransformedTiled<qargb8565, qrgb565>(count, spans, userData);
+ else
+#endif
+ blend_src_generic<RegularSpans>(count, spans, userData
+ Q_TEMPLATE_ENUM_CALL(SpanMethod, RegularSpans));
+}
+
+static void blend_transformed_tiled_rgb565(int count, const QSpan *spans,
+ void *userData)
+{
+#if !defined(Q_WS_QWS) || defined(QT_QWS_DEPTH_16)
+ QSpanData *data = reinterpret_cast<QSpanData *>(userData);
+
+ if (data->texture.format == QImage::Format_ARGB8565_Premultiplied)
+ blendTransformedTiled<qrgb565, qargb8565>(count, spans, userData);
+ else if (data->texture.format == QImage::Format_RGB16)
+ blendTransformedTiled<qrgb565, qrgb565>(count, spans, userData);
+ else
+#endif
+ blend_src_generic<RegularSpans>(count, spans, userData
+ Q_TEMPLATE_ENUM_CALL(SpanMethod, RegularSpans));
+}
+
+static void blend_transformed_tiled_argb8555(int count, const QSpan *spans,
+ void *userData)
+{
+#if !defined(Q_WS_QWS) || defined(QT_QWS_DEPTH_15)
+ QSpanData *data = reinterpret_cast<QSpanData *>(userData);
+
+ if (data->texture.format == QImage::Format_ARGB8555_Premultiplied)
+ blendTransformedTiled<qargb8555, qargb8555>(count, spans, userData);
+ else if (data->texture.format == QImage::Format_RGB555)
+ blendTransformedTiled<qargb8555, qrgb555>(count, spans, userData);
+ else
+#endif
+ blend_src_generic<RegularSpans>(count, spans, userData
+ Q_TEMPLATE_ENUM_CALL(SpanMethod, RegularSpans));
+}
+
+static void blend_transformed_tiled_rgb555(int count, const QSpan *spans,
+ void *userData)
+{
+#if !defined(Q_WS_QWS) || defined(QT_QWS_DEPTH_15)
+ QSpanData *data = reinterpret_cast<QSpanData *>(userData);
+
+ if (data->texture.format == QImage::Format_ARGB8555_Premultiplied)
+ blendTransformedTiled<qrgb555, qargb8555>(count, spans, userData);
+ else if (data->texture.format == QImage::Format_RGB555)
+ blendTransformedTiled<qrgb555, qrgb555>(count, spans, userData);
+ else
+#endif
+ blend_src_generic<RegularSpans>(count, spans, userData
+ Q_TEMPLATE_ENUM_CALL(SpanMethod, RegularSpans));
+}
+
+static void blend_transformed_tiled_argb4444(int count, const QSpan *spans,
+ void *userData)
+{
+#if !defined(Q_WS_QWS) || defined(QT_QWS_DEPTH_12)
+ QSpanData *data = reinterpret_cast<QSpanData *>(userData);
+
+ if (data->texture.format == QImage::Format_ARGB4444_Premultiplied)
+ blendTransformedTiled<qargb4444, qargb4444>(count, spans, userData);
+ else if (data->texture.format == QImage::Format_RGB444)
+ blendTransformedTiled<qargb4444, qrgb444>(count, spans, userData);
+ else
+#endif
+ blend_src_generic<RegularSpans>(count, spans, userData
+ Q_TEMPLATE_ENUM_CALL(SpanMethod, RegularSpans));
+}
+
+static void blend_transformed_tiled_rgb444(int count, const QSpan *spans,
+ void *userData)
+{
+#if !defined(Q_WS_QWS) || defined(QT_QWS_DEPTH_12)
+ QSpanData *data = reinterpret_cast<QSpanData *>(userData);
+
+ if (data->texture.format == QImage::Format_ARGB4444_Premultiplied)
+ blendTransformedTiled<qrgb444, qargb4444>(count, spans, userData);
+ else if (data->texture.format == QImage::Format_RGB444)
+ blendTransformedTiled<qrgb444, qrgb444>(count, spans, userData);
+ else
+#endif
+ blend_src_generic<RegularSpans>(count, spans, userData
+ Q_TEMPLATE_ENUM_CALL(SpanMethod, RegularSpans));
+}
+
+#if defined(Q_CC_MSVC) && _MSC_VER <= 1300 && !defined(Q_CC_INTEL)
+
+// explicit template instantiations needed to compile with VC6 and VC2002
+
+#define SPANFUNC_INSTANTIATION(Name, Arg) \
+static inline void Name##_##Arg(int count, const QSpan *spans, void *userData) \
+{ \
+ Name<Arg>(count, spans, userData Q_TEMPLATE_ENUM_CALL(SpanMethod, Arg)); \
+}
+
+SPANFUNC_INSTANTIATION(blend_untransformed_generic, RegularSpans);
+SPANFUNC_INSTANTIATION(blend_untransformed_argb, RegularSpans);
+SPANFUNC_INSTANTIATION(blend_tiled_generic, RegularSpans);
+SPANFUNC_INSTANTIATION(blend_tiled_argb, RegularSpans);
+SPANFUNC_INSTANTIATION(blend_src_generic, RegularSpans);
+SPANFUNC_INSTANTIATION(blend_transformed_argb, RegularSpans);
+SPANFUNC_INSTANTIATION(blend_transformed_tiled_argb, RegularSpans);
+SPANFUNC_INSTANTIATION(blend_transformed_bilinear_argb, RegularSpans);
+SPANFUNC_INSTANTIATION(blend_transformed_bilinear_tiled_argb, RegularSpans);
+#undef SPANFUNC_INSTANTIATION
+
+#define SPANFUNC_POINTER(Name, Arg) Name##_##Arg
+
+#else // !VC6 && !VC2002
+# define SPANFUNC_POINTER(Name, Arg) Name<Arg>
+#endif
+
+
+/* Image formats here are target formats */
+static const ProcessSpans processTextureSpans[NBlendTypes][QImage::NImageFormats] = {
+ // Untransformed
+ {
+ 0, // Invalid
+ SPANFUNC_POINTER(blend_untransformed_generic, RegularSpans), // Mono
+ SPANFUNC_POINTER(blend_untransformed_generic, RegularSpans), // MonoLsb
+ SPANFUNC_POINTER(blend_untransformed_generic, RegularSpans), // Indexed8
+ SPANFUNC_POINTER(blend_untransformed_generic, RegularSpans), // RGB32
+ SPANFUNC_POINTER(blend_untransformed_generic, RegularSpans), // ARGB32
+ SPANFUNC_POINTER(blend_untransformed_argb, RegularSpans), // ARGB32_Premultiplied
+ blend_untransformed_rgb565,
+ blend_untransformed_argb8565,
+ blend_untransformed_rgb666,
+ blend_untransformed_argb6666,
+ blend_untransformed_rgb555,
+ blend_untransformed_argb8555,
+ blend_untransformed_rgb888,
+ blend_untransformed_rgb444,
+ blend_untransformed_argb4444,
+ },
+ // Tiled
+ {
+ 0, // Invalid
+ SPANFUNC_POINTER(blend_tiled_generic, RegularSpans), // Mono
+ SPANFUNC_POINTER(blend_tiled_generic, RegularSpans), // MonoLsb
+ SPANFUNC_POINTER(blend_tiled_generic, RegularSpans), // Indexed8
+ SPANFUNC_POINTER(blend_tiled_generic, RegularSpans), // RGB32
+ SPANFUNC_POINTER(blend_tiled_generic, RegularSpans), // ARGB32
+ SPANFUNC_POINTER(blend_tiled_argb, RegularSpans), // ARGB32_Premultiplied
+ blend_tiled_rgb565,
+ blend_tiled_argb8565,
+ blend_tiled_rgb666,
+ blend_tiled_argb6666,
+ blend_tiled_rgb555,
+ blend_tiled_argb8555,
+ blend_tiled_rgb888,
+ blend_tiled_rgb444,
+ blend_tiled_argb4444,
+ },
+ // Transformed
+ {
+ 0, // Invalid
+ SPANFUNC_POINTER(blend_src_generic, RegularSpans), // Mono
+ SPANFUNC_POINTER(blend_src_generic, RegularSpans), // MonoLsb
+ SPANFUNC_POINTER(blend_src_generic, RegularSpans), // Indexed8
+ SPANFUNC_POINTER(blend_src_generic, RegularSpans), // RGB32
+ SPANFUNC_POINTER(blend_src_generic, RegularSpans), // ARGB32
+ SPANFUNC_POINTER(blend_transformed_argb, RegularSpans), // ARGB32_Premultiplied
+ blend_transformed_rgb565,
+ blend_transformed_argb8565,
+ blend_transformed_rgb666,
+ blend_transformed_argb6666,
+ blend_transformed_rgb555,
+ blend_transformed_argb8555,
+ blend_transformed_rgb888,
+ blend_transformed_rgb444,
+ blend_transformed_argb4444,
+ },
+ // TransformedTiled
+ {
+ 0,
+ SPANFUNC_POINTER(blend_src_generic, RegularSpans), // Mono
+ SPANFUNC_POINTER(blend_src_generic, RegularSpans), // MonoLsb
+ SPANFUNC_POINTER(blend_src_generic, RegularSpans), // Indexed8
+ SPANFUNC_POINTER(blend_src_generic, RegularSpans), // RGB32
+ SPANFUNC_POINTER(blend_src_generic, RegularSpans), // ARGB32
+ SPANFUNC_POINTER(blend_transformed_tiled_argb, RegularSpans), // ARGB32_Premultiplied
+ blend_transformed_tiled_rgb565,
+ blend_transformed_tiled_argb8565,
+ blend_transformed_tiled_rgb666,
+ blend_transformed_tiled_argb6666,
+ blend_transformed_tiled_rgb555,
+ blend_transformed_tiled_argb8555,
+ blend_transformed_tiled_rgb888,
+ blend_transformed_tiled_rgb444,
+ blend_transformed_tiled_argb4444
+ },
+ // Bilinear
+ {
+ 0,
+ SPANFUNC_POINTER(blend_src_generic, RegularSpans), // Mono
+ SPANFUNC_POINTER(blend_src_generic, RegularSpans), // MonoLsb
+ SPANFUNC_POINTER(blend_src_generic, RegularSpans), // Indexed8
+ SPANFUNC_POINTER(blend_src_generic, RegularSpans), // RGB32
+ SPANFUNC_POINTER(blend_src_generic, RegularSpans), // ARGB32
+ SPANFUNC_POINTER(blend_transformed_bilinear_argb, RegularSpans), // ARGB32_Premultiplied
+ blend_transformed_bilinear_rgb565,
+ blend_transformed_bilinear_argb8565,
+ blend_transformed_bilinear_rgb666,
+ blend_transformed_bilinear_argb6666,
+ blend_transformed_bilinear_rgb555,
+ blend_transformed_bilinear_argb8555,
+ blend_transformed_bilinear_rgb888,
+ blend_transformed_bilinear_rgb444,
+ blend_transformed_bilinear_argb4444,
+ },
+ // BilinearTiled
+ {
+ 0,
+ SPANFUNC_POINTER(blend_src_generic, RegularSpans), // Mono
+ SPANFUNC_POINTER(blend_src_generic, RegularSpans), // MonoLsb
+ SPANFUNC_POINTER(blend_src_generic, RegularSpans), // Indexed8
+ SPANFUNC_POINTER(blend_src_generic, RegularSpans), // RGB32
+ SPANFUNC_POINTER(blend_src_generic, RegularSpans), // ARGB32
+ SPANFUNC_POINTER(blend_transformed_bilinear_tiled_argb, RegularSpans), // ARGB32_Premultiplied
+ SPANFUNC_POINTER(blend_src_generic, RegularSpans), // RGB16
+ SPANFUNC_POINTER(blend_src_generic, RegularSpans), // ARGB8565_Premultiplied
+ SPANFUNC_POINTER(blend_src_generic, RegularSpans), // RGB666
+ SPANFUNC_POINTER(blend_src_generic, RegularSpans), // ARGB6666_Premultiplied
+ SPANFUNC_POINTER(blend_src_generic, RegularSpans), // RGB555
+ SPANFUNC_POINTER(blend_src_generic, RegularSpans), // ARGB8555_Premultiplied
+ SPANFUNC_POINTER(blend_src_generic, RegularSpans), // RGB888
+ SPANFUNC_POINTER(blend_src_generic, RegularSpans), // RGB444
+ SPANFUNC_POINTER(blend_src_generic, RegularSpans), // ARGB4444_Premultiplied
+ }
+};
+
+#if defined (Q_WS_QWS) && !defined(QT_NO_RASTERCALLBACKS)
+static const ProcessSpans processTextureSpansCallback[NBlendTypes][QImage::NImageFormats] = {
+ // Untransformed
+ {
+ 0, // Invalid
+ blend_untransformed_generic<CallbackSpans>, // Mono
+ blend_untransformed_generic<CallbackSpans>, // MonoLsb
+ blend_untransformed_generic<CallbackSpans>, // Indexed8
+ blend_untransformed_generic<CallbackSpans>, // RGB32
+ blend_untransformed_generic<CallbackSpans>, // ARGB32
+ blend_untransformed_argb<CallbackSpans>, // ARGB32_Premultiplied
+ blend_untransformed_generic<CallbackSpans>, // RGB16
+ blend_untransformed_generic<CallbackSpans>, // ARGB8565_Premultiplied
+ blend_untransformed_generic<CallbackSpans>, // RGB666
+ blend_untransformed_generic<CallbackSpans>, // ARGB6666_Premultiplied
+ blend_untransformed_generic<CallbackSpans>, // RGB555
+ blend_untransformed_generic<CallbackSpans>, // ARGB8555_Premultiplied
+ blend_untransformed_generic<CallbackSpans>, // RGB888
+ blend_untransformed_generic<CallbackSpans>, // RGB444
+ blend_untransformed_generic<CallbackSpans> // ARGB4444_Premultiplied
+ },
+ // Tiled
+ {
+ 0, // Invalid
+ blend_tiled_generic<CallbackSpans>, // Mono
+ blend_tiled_generic<CallbackSpans>, // MonoLsb
+ blend_tiled_generic<CallbackSpans>, // Indexed8
+ blend_tiled_generic<CallbackSpans>, // RGB32
+ blend_tiled_generic<CallbackSpans>, // ARGB32
+ blend_tiled_argb<CallbackSpans>, // ARGB32_Premultiplied
+ blend_tiled_generic<CallbackSpans>, // RGB16
+ blend_tiled_generic<CallbackSpans>, // ARGB8565_Premultiplied
+ blend_tiled_generic<CallbackSpans>, // RGB666
+ blend_tiled_generic<CallbackSpans>, // ARGB6666_Premultiplied
+ blend_tiled_generic<CallbackSpans>, // RGB555
+ blend_tiled_generic<CallbackSpans>, // ARGB8555_Premultiplied
+ blend_tiled_generic<CallbackSpans>, // RGB888
+ blend_tiled_generic<CallbackSpans>, // RGB444
+ blend_tiled_generic<CallbackSpans> // ARGB4444_Premultiplied
+ },
+ // Transformed
+ {
+ 0, // Invalid
+ blend_src_generic<CallbackSpans>, // Mono
+ blend_src_generic<CallbackSpans>, // MonoLsb
+ blend_src_generic<CallbackSpans>, // Indexed8
+ blend_src_generic<CallbackSpans>, // RGB32
+ blend_src_generic<CallbackSpans>, // ARGB32
+ blend_transformed_argb<CallbackSpans>, // ARGB32_Premultiplied
+ blend_src_generic<CallbackSpans>, // RGB16
+ blend_src_generic<CallbackSpans>, // ARGB8565_Premultiplied
+ blend_src_generic<CallbackSpans>, // RGB666
+ blend_src_generic<CallbackSpans>, // ARGB6666_Premultiplied
+ blend_src_generic<CallbackSpans>, // RGB555
+ blend_src_generic<CallbackSpans>, // ARGB8555_Premultiplied
+ blend_src_generic<CallbackSpans>, // RGB888
+ blend_src_generic<CallbackSpans>, // RGB444
+ blend_src_generic<CallbackSpans>, // ARGB4444_Premultiplied
+ },
+ // TransformedTiled
+ {
+ 0,
+ blend_src_generic<CallbackSpans>, // Mono
+ blend_src_generic<CallbackSpans>, // MonoLsb
+ blend_src_generic<CallbackSpans>, // Indexed8
+ blend_src_generic<CallbackSpans>, // RGB32
+ blend_src_generic<CallbackSpans>, // ARGB32
+ blend_transformed_tiled_argb<CallbackSpans>, // ARGB32_Premultiplied
+ blend_src_generic<CallbackSpans>, // RGB16
+ blend_src_generic<CallbackSpans>, // ARGB8565_Premultiplied
+ blend_src_generic<CallbackSpans>, // RGB666
+ blend_src_generic<CallbackSpans>, // ARGB6666_Premultiplied
+ blend_src_generic<CallbackSpans>, // RGB555
+ blend_src_generic<CallbackSpans>, // ARGB8555_Premultiplied
+ blend_src_generic<CallbackSpans>, // RGB888
+ blend_src_generic<CallbackSpans>, // RGB444
+ blend_src_generic<CallbackSpans> // ARGB4444_Premultiplied
+ },
+ // Bilinear
+ {
+ 0,
+ blend_src_generic<CallbackSpans>, // Mono
+ blend_src_generic<CallbackSpans>, // MonoLsb
+ blend_src_generic<CallbackSpans>, // Indexed8
+ blend_src_generic<CallbackSpans>, // RGB32
+ blend_src_generic<CallbackSpans>, // ARGB32
+ blend_transformed_bilinear_argb<CallbackSpans>, // ARGB32_Premultiplied
+ blend_src_generic<CallbackSpans>, // RGB16
+ blend_src_generic<CallbackSpans>, // ARGB8565_Premultiplied
+ blend_src_generic<CallbackSpans>, // RGB666
+ blend_src_generic<CallbackSpans>, // ARGB6666_Premultiplied
+ blend_src_generic<CallbackSpans>, // RGB555
+ blend_src_generic<CallbackSpans>, // ARGB8555_Premultiplied
+ blend_src_generic<CallbackSpans>, // RGB888
+ blend_src_generic<CallbackSpans>, // RGB444
+ blend_src_generic<CallbackSpans> // ARGB4444_Premultiplied
+ },
+ // BilinearTiled
+ {
+ 0,
+ blend_src_generic<CallbackSpans>, // Mono
+ blend_src_generic<CallbackSpans>, // MonoLsb
+ blend_src_generic<CallbackSpans>, // Indexed8
+ blend_src_generic<CallbackSpans>, // RGB32
+ blend_src_generic<CallbackSpans>, // ARGB32
+ blend_transformed_bilinear_tiled_argb<CallbackSpans>, // ARGB32_Premultiplied
+ blend_src_generic<CallbackSpans>, // RGB16
+ blend_src_generic<CallbackSpans>, // ARGB8565_Premultiplied
+ blend_src_generic<CallbackSpans>, // RGB666
+ blend_src_generic<CallbackSpans>, // ARGB6666_Premultiplied
+ blend_src_generic<CallbackSpans>, // RGB555
+ blend_src_generic<CallbackSpans>, // ARGB8555_Premultiplied
+ blend_src_generic<CallbackSpans>, // RGB888
+ blend_src_generic<CallbackSpans>, // RGB444
+ blend_src_generic<CallbackSpans> // ARGB4444_Premultiplied
+ }
+};
+#endif // QT_NO_RASTERCALLBACKS
+
+void qBlendTexture(int count, const QSpan *spans, void *userData)
+{
+ QSpanData *data = reinterpret_cast<QSpanData *>(userData);
+ ProcessSpans proc = processTextureSpans[getBlendType(data)][data->rasterBuffer->format];
+ proc(count, spans, userData);
+}
+
+#if defined (Q_WS_QWS) && !defined(QT_NO_RASTERCALLBACKS)
+void qBlendTextureCallback(int count, const QSpan *spans, void *userData)
+{
+ QSpanData *data = reinterpret_cast<QSpanData *>(userData);
+ ProcessSpans proc = processTextureSpansCallback[getBlendType(data)][data->rasterBuffer->format];
+ proc(count, spans, userData);
+}
+#endif // QT_NO_RASTERCALLBACKS
+
+template <class DST>
+inline void qt_bitmapblit_template(QRasterBuffer *rasterBuffer,
+ int x, int y, quint32 color,
+ const uchar *map,
+ int mapWidth, int mapHeight, int mapStride,
+ DST dummy = 0)
+{
+ Q_UNUSED(dummy);
+ const DST c = qt_colorConvert<DST, quint32>(color, 0);
+ DST *dest = reinterpret_cast<DST*>(rasterBuffer->scanLine(y)) + x;
+ const int destStride = rasterBuffer->bytesPerLine() / sizeof(DST);
+
+ if (mapWidth > 8) {
+ while (mapHeight--) {
+ int x0 = 0;
+ int n = 0;
+ for (int x = 0; x < mapWidth; x += 8) {
+ uchar s = map[x >> 3];
+ for (int i = 0; i < 8; ++i) {
+ if (s & 0x80) {
+ ++n;
+ } else {
+ if (n) {
+ qt_memfill(dest + x0, c, n);
+ x0 += n + 1;
+ n = 0;
+ } else {
+ ++x0;
+ }
+ if (!s) {
+ x0 += 8 - 1 - i;
+ break;
+ }
+ }
+ s <<= 1;
+ }
+ }
+ if (n)
+ qt_memfill(dest + x0, c, n);
+ dest += destStride;
+ map += mapStride;
+ }
+ } else {
+ while (mapHeight--) {
+ int x0 = 0;
+ int n = 0;
+ for (uchar s = *map; s; s <<= 1) {
+ if (s & 0x80) {
+ ++n;
+ } else if (n) {
+ qt_memfill(dest + x0, c, n);
+ x0 += n + 1;
+ n = 0;
+ } else {
+ ++x0;
+ }
+ }
+ if (n)
+ qt_memfill(dest + x0, c, n);
+ dest += destStride;
+ map += mapStride;
+ }
+ }
+}
+
+static void qt_gradient_quint32(int count, const QSpan *spans, void *userData)
+{
+ QSpanData *data = reinterpret_cast<QSpanData *>(userData);
+
+ bool isVerticalGradient =
+ data->txop <= QTransform::TxScale &&
+ data->type == QSpanData::LinearGradient &&
+ data->gradient.linear.end.x == data->gradient.linear.origin.x;
+
+ if (isVerticalGradient) {
+ LinearGradientValues linear;
+ getLinearGradientValues(&linear, data);
+
+ CompositionFunctionSolid funcSolid =
+ functionForModeSolid[data->rasterBuffer->compositionMode];
+
+ /*
+ The logic for vertical gradient calculations is a mathematically
+ reduced copy of that in fetchLinearGradient() - which is basically:
+
+ qreal ry = data->m22 * (y + 0.5) + data->dy;
+ qreal t = linear.dy*ry + linear.off;
+ t *= (GRADIENT_STOPTABLE_SIZE - 1);
+ quint32 color =
+ qt_gradient_pixel_fixed(&data->gradient,
+ int(t * FIXPT_SIZE));
+
+ This has then been converted to fixed point to improve performance.
+ */
+ const int gss = GRADIENT_STOPTABLE_SIZE - 1;
+ int yinc = int((linear.dy * data->m22 * gss) * FIXPT_SIZE);
+ int off = int((((linear.dy * (data->m22 * 0.5 + data->dy) + linear.off) * gss) * FIXPT_SIZE));
+
+ while (count--) {
+ int y = spans->y;
+ int x = spans->x;
+
+ quint32 *dst = (quint32 *)(data->rasterBuffer->scanLine(y)) + x;
+ quint32 color =
+ qt_gradient_pixel_fixed(&data->gradient, yinc * y + off);
+
+ funcSolid(dst, spans->len, color, spans->coverage);
+ ++spans;
+ }
+
+ } else {
+ blend_src_generic<RegularSpans>(count, spans, userData
+ Q_TEMPLATE_ENUM_CALL(SpanMethod, RegularSpans));
+ }
+}
+
+static void qt_gradient_quint16(int count, const QSpan *spans, void *userData)
+{
+ QSpanData *data = reinterpret_cast<QSpanData *>(userData);
+
+ bool isVerticalGradient =
+ data->txop <= QTransform::TxScale &&
+ data->type == QSpanData::LinearGradient &&
+ data->gradient.linear.end.x == data->gradient.linear.origin.x;
+
+ if (isVerticalGradient) {
+
+ LinearGradientValues linear;
+ getLinearGradientValues(&linear, data);
+
+ /*
+ The logic for vertical gradient calculations is a mathematically
+ reduced copy of that in fetchLinearGradient() - which is basically:
+
+ qreal ry = data->m22 * (y + 0.5) + data->dy;
+ qreal t = linear.dy*ry + linear.off;
+ t *= (GRADIENT_STOPTABLE_SIZE - 1);
+ quint32 color =
+ qt_gradient_pixel_fixed(&data->gradient,
+ int(t * FIXPT_SIZE));
+
+ This has then been converted to fixed point to improve performance.
+ */
+ const int gss = GRADIENT_STOPTABLE_SIZE - 1;
+ int yinc = int((linear.dy * data->m22 * gss) * FIXPT_SIZE);
+ int off = int((((linear.dy * (data->m22 * 0.5 + data->dy) + linear.off) * gss) * FIXPT_SIZE));
+
+ uint oldColor = data->solid.color;
+ while (count--) {
+ int y = spans->y;
+
+ quint32 color = qt_gradient_pixel_fixed(&data->gradient, yinc * y + off);
+
+ data->solid.color = color;
+ blend_color_rgb16(1, spans, userData);
+ ++spans;
+ }
+ data->solid.color = oldColor;
+
+ } else {
+ blend_src_generic<RegularSpans>(count, spans, userData
+ Q_TEMPLATE_ENUM_CALL(SpanMethod, RegularSpans));
+ }
+}
+
+inline static void qt_bitmapblit_quint32(QRasterBuffer *rasterBuffer,
+ int x, int y, quint32 color,
+ const uchar *map,
+ int mapWidth, int mapHeight, int mapStride)
+{
+ qt_bitmapblit_template<quint32>(rasterBuffer, x, y, color,
+ map, mapWidth, mapHeight, mapStride);
+}
+
+inline static void qt_bitmapblit_quint16(QRasterBuffer *rasterBuffer,
+ int x, int y, quint32 color,
+ const uchar *map,
+ int mapWidth, int mapHeight, int mapStride)
+{
+ qt_bitmapblit_template<quint16>(rasterBuffer, x, y, color,
+ map, mapWidth, mapHeight, mapStride);
+}
+
+
+uchar qt_pow_rgb_gamma[256];
+uchar qt_pow_rgb_invgamma[256];
+
+uint qt_pow_gamma[256];
+uchar qt_pow_invgamma[2048];
+
+static void qt_alphamapblit_quint16(QRasterBuffer *rasterBuffer,
+ int x, int y, quint32 color,
+ const uchar *map,
+ int mapWidth, int mapHeight, int mapStride,
+ const QClipData *)
+{
+ const quint16 c = qt_colorConvert<quint16, quint32>(color, 0);
+ quint16 *dest = reinterpret_cast<quint16*>(rasterBuffer->scanLine(y)) + x;
+ const int destStride = rasterBuffer->bytesPerLine() / sizeof(quint16);
+
+ while (mapHeight--) {
+ for (int i = 0; i < mapWidth; ++i) {
+ const int coverage = map[i];
+
+ if (coverage == 0) {
+ // nothing
+ } else if (coverage == 255) {
+ dest[i] = c;
+ } else {
+ int ialpha = 255 - coverage;
+ dest[i] = BYTE_MUL_RGB16(c, coverage)
+ + BYTE_MUL_RGB16(dest[i], ialpha);
+ }
+ }
+ dest += destStride;
+ map += mapStride;
+ }
+}
+
+void qt_build_pow_tables() {
+ qreal smoothing = 1.7;
+
+#ifdef Q_WS_WIN
+ int winSmooth;
+ if (SystemParametersInfo(0x200C /* SPI_GETFONTSMOOTHINGCONTRAST */, 0, &winSmooth, 0))
+ smoothing = winSmooth / 1000.0;
+#endif
+
+#ifdef Q_WS_X11
+ Q_UNUSED(smoothing);
+ for (int i=0; i<256; ++i) {
+ qt_pow_rgb_gamma[i] = uchar(i);
+ qt_pow_rgb_invgamma[i] = uchar(i);
+ }
+#else
+ for (int i=0; i<256; ++i) {
+ qt_pow_rgb_gamma[i] = uchar(qRound(pow(i / 255.0, smoothing) * 255));
+ qt_pow_rgb_invgamma[i] = uchar(qRound(pow(i / 255.0, 1 / smoothing) * 255));
+ }
+#endif
+
+#if defined(Q_OS_WIN) && !defined(Q_OS_WINCE)
+ const qreal gray_gamma = 2.31;
+ for (int i=0; i<256; ++i)
+ qt_pow_gamma[i] = uint(qRound(pow(i / 255.0, gray_gamma) * 2047));
+ for (int i=0; i<2048; ++i)
+ qt_pow_invgamma[i] = uchar(qRound(pow(i / 2047.0, 1 / gray_gamma) * 255));
+#endif
+}
+
+static inline void rgbBlendPixel(quint32 *dst, int coverage, int sr, int sg, int sb)
+{
+ // Do a gray alphablend...
+ int da = qAlpha(*dst);
+ int dr = qRed(*dst);
+ int dg = qGreen(*dst);
+ int db = qBlue(*dst);
+
+ if (da != 255
+#if defined (Q_WS_WIN)
+ // Work around GDI messing up alpha channel
+ && qRed(*dst) <= da && qBlue(*dst) <= da && qGreen(*dst) <= da
+#endif
+ ) {
+
+ int a = qGray(coverage);
+ sr = qt_div_255(sr * a);
+ sg = qt_div_255(sg * a);
+ sb = qt_div_255(sb * a);
+
+ int ia = 255 - a;
+ dr = qt_div_255(dr * ia);
+ dg = qt_div_255(dg * ia);
+ db = qt_div_255(db * ia);
+
+ *dst = ((a + qt_div_255((255 - a) * da)) << 24)
+ | ((sr + dr) << 16)
+ | ((sg + dg) << 8)
+ | ((sb + db));
+ return;
+ }
+
+ int mr = qRed(coverage);
+ int mg = qGreen(coverage);
+ int mb = qBlue(coverage);
+
+ dr = qt_pow_rgb_gamma[dr];
+ dg = qt_pow_rgb_gamma[dg];
+ db = qt_pow_rgb_gamma[db];
+
+ int nr = qt_div_255((sr - dr) * mr) + dr;
+ int ng = qt_div_255((sg - dg) * mg) + dg;
+ int nb = qt_div_255((sb - db) * mb) + db;
+
+ nr = qt_pow_rgb_invgamma[nr];
+ ng = qt_pow_rgb_invgamma[ng];
+ nb = qt_pow_rgb_invgamma[nb];
+
+ *dst = qRgb(nr, ng, nb);
+}
+
+#if defined(Q_OS_WIN) && !defined(Q_OS_WINCE)
+static inline void grayBlendPixel(quint32 *dst, int coverage, int sr, int sg, int sb)
+{
+ // Do a gammacorrected gray alphablend...
+ int dr = qRed(*dst);
+ int dg = qGreen(*dst);
+ int db = qBlue(*dst);
+
+ dr = qt_pow_gamma[dr];
+ dg = qt_pow_gamma[dg];
+ db = qt_pow_gamma[db];
+
+ int alpha = coverage;
+ int ialpha = 255 - alpha;
+ int nr = (sr * alpha + ialpha * dr) / 255;
+ int ng = (sg * alpha + ialpha * dg) / 255;
+ int nb = (sb * alpha + ialpha * db) / 255;
+
+ nr = qt_pow_invgamma[nr];
+ ng = qt_pow_invgamma[ng];
+ nb = qt_pow_invgamma[nb];
+
+ *dst = qRgb(nr, ng, nb);
+}
+#endif
+
+static void qt_alphamapblit_quint32(QRasterBuffer *rasterBuffer,
+ int x, int y, quint32 color,
+ const uchar *map,
+ int mapWidth, int mapHeight, int mapStride,
+ const QClipData *clip)
+{
+ const quint32 c = color;
+ const int destStride = rasterBuffer->bytesPerLine() / sizeof(quint32);
+
+#if defined(Q_OS_WIN) && !defined(Q_OS_WINCE)
+ int sr = qRed(color);
+ int sg = qGreen(color);
+ int sb = qBlue(color);
+
+ sr = qt_pow_gamma[sr];
+ sg = qt_pow_gamma[sg];
+ sb = qt_pow_gamma[sb];
+ bool opaque_src = (qAlpha(color) == 255);
+#endif
+
+ if (!clip) {
+ quint32 *dest = reinterpret_cast<quint32*>(rasterBuffer->scanLine(y)) + x;
+ while (mapHeight--) {
+ for (int i = 0; i < mapWidth; ++i) {
+ const int coverage = map[i];
+
+ if (coverage == 0) {
+ // nothing
+ } else if (coverage == 255) {
+ dest[i] = c;
+ } else {
+#if defined(Q_OS_WIN) && !defined(Q_OS_WINCE)
+ if (QSysInfo::WindowsVersion >= QSysInfo::WV_XP && opaque_src
+ && qAlpha(dest[i]) == 255) {
+ grayBlendPixel(dest+i, coverage, sr, sg, sb);
+ } else
+#endif
+ {
+ int ialpha = 255 - coverage;
+ dest[i] = BYTE_MUL(c, uint(coverage)) + BYTE_MUL(dest[i], ialpha);
+ }
+ }
+ }
+ dest += destStride;
+ map += mapStride;
+ }
+ } else {
+ int bottom = qMin(y + mapHeight, rasterBuffer->height());
+
+ int top = qMax(y, 0);
+ map += (top - y) * mapStride;
+
+ const_cast<QClipData *>(clip)->initialize();
+ for (int yp = top; yp<bottom; ++yp) {
+ const QClipData::ClipLine &line = clip->m_clipLines[yp];
+
+ quint32 *dest = reinterpret_cast<quint32 *>(rasterBuffer->scanLine(yp));
+
+ for (int i=0; i<line.count; ++i) {
+ const QSpan &clip = line.spans[i];
+
+ int start = qMax<int>(x, clip.x);
+ int end = qMin<int>(x + mapWidth, clip.x + clip.len);
+
+ for (int xp=start; xp<end; ++xp) {
+ const int coverage = map[xp - x];
+
+ if (coverage == 0) {
+ // nothing
+ } else if (coverage == 255) {
+ dest[xp] = c;
+ } else {
+#if defined(Q_OS_WIN) && !defined(Q_OS_WINCE)
+ if (QSysInfo::WindowsVersion >= QSysInfo::WV_XP && opaque_src
+ && qAlpha(dest[xp]) == 255) {
+ grayBlendPixel(dest+xp, coverage, sr, sg, sb);
+ } else
+#endif
+ {
+ int ialpha = 255 - coverage;
+ dest[xp] = BYTE_MUL(c, uint(coverage)) + BYTE_MUL(dest[xp], ialpha);
+ }
+ }
+
+ } // for (i -> line.count)
+ } // for (yp -> bottom)
+ map += mapStride;
+ }
+ }
+}
+
+static void qt_alphargbblit_quint32(QRasterBuffer *rasterBuffer,
+ int x, int y, quint32 color,
+ const uint *src, int mapWidth, int mapHeight, int srcStride,
+ const QClipData *clip)
+{
+ const quint32 c = color;
+
+ int sr = qRed(color);
+ int sg = qGreen(color);
+ int sb = qBlue(color);
+ int sa = qAlpha(color);
+
+ sr = qt_pow_rgb_gamma[sr];
+ sg = qt_pow_rgb_gamma[sg];
+ sb = qt_pow_rgb_gamma[sb];
+
+ if (sa == 0)
+ return;
+
+ if (!clip) {
+ quint32 *dst = reinterpret_cast<quint32*>(rasterBuffer->scanLine(y)) + x;
+ const int destStride = rasterBuffer->bytesPerLine() / sizeof(quint32);
+ while (mapHeight--) {
+ for (int i = 0; i < mapWidth; ++i) {
+ const uint coverage = src[i];
+ if (coverage == 0xffffffff) {
+ dst[i] = c;
+ } else if (coverage != 0xff000000) {
+ rgbBlendPixel(dst+i, coverage, sr, sg, sb);
+ }
+ }
+
+ dst += destStride;
+ src += srcStride;
+ }
+ } else {
+ int bottom = qMin(y + mapHeight, rasterBuffer->height());
+
+ int top = qMax(y, 0);
+ src += (top - y) * srcStride;
+
+ const_cast<QClipData *>(clip)->initialize();
+ for (int yp = top; yp<bottom; ++yp) {
+ const QClipData::ClipLine &line = clip->m_clipLines[yp];
+
+ quint32 *dst = reinterpret_cast<quint32 *>(rasterBuffer->scanLine(yp));
+
+ for (int i=0; i<line.count; ++i) {
+ const QSpan &clip = line.spans[i];
+
+ int start = qMax<int>(x, clip.x);
+ int end = qMin<int>(x + mapWidth, clip.x + clip.len);
+
+ for (int xp=start; xp<end; ++xp) {
+ const uint coverage = src[xp - x];
+ if (coverage == 0xffffffff) {
+ dst[xp] = c;
+ } else if (coverage != 0xff000000) {
+ rgbBlendPixel(dst+xp, coverage, sr, sg, sb);
+ }
+ }
+ } // for (i -> line.count)
+ src += srcStride;
+ } // for (yp -> bottom)
+
+ }
+}
+
+template <class T>
+inline void qt_rectfill_template(QRasterBuffer *rasterBuffer,
+ int x, int y, int width, int height,
+ quint32 color, T dummy = 0)
+{
+ Q_UNUSED(dummy);
+
+ qt_rectfill<T>(reinterpret_cast<T*>(rasterBuffer->buffer()),
+ qt_colorConvert<T, quint32p>(quint32p::fromRawData(color), 0),
+ x, y, width, height, rasterBuffer->bytesPerLine());
+}
+
+#define QT_RECTFILL(T) \
+ inline static void qt_rectfill_##T(QRasterBuffer *rasterBuffer, \
+ int x, int y, int width, int height, \
+ quint32 color) \
+ { \
+ qt_rectfill_template<T>(rasterBuffer, x, y, width, height, color); \
+ }
+
+QT_RECTFILL(quint32)
+QT_RECTFILL(quint16)
+QT_RECTFILL(qargb8565)
+QT_RECTFILL(qrgb666);
+QT_RECTFILL(qargb6666);
+QT_RECTFILL(qrgb555)
+QT_RECTFILL(qargb8555)
+QT_RECTFILL(qrgb888)
+QT_RECTFILL(qrgb444);
+QT_RECTFILL(qargb4444);
+#undef QT_RECTFILL
+
+
+// Map table for destination image format. Contains function pointers
+// for blends of various types unto the destination
+
+DrawHelper qDrawHelper[QImage::NImageFormats] =
+{
+ // Format_Invalid,
+ { 0, 0, 0, 0, 0, 0 },
+ // Format_Mono,
+ {
+ blend_color_generic,
+ SPANFUNC_POINTER(blend_src_generic, RegularSpans),
+ 0, 0, 0, 0
+ },
+ // Format_MonoLSB,
+ {
+ blend_color_generic,
+ SPANFUNC_POINTER(blend_src_generic, RegularSpans),
+ 0, 0, 0, 0
+ },
+ // Format_Indexed8,
+ {
+ blend_color_generic,
+ SPANFUNC_POINTER(blend_src_generic, RegularSpans),
+ 0, 0, 0, 0
+ },
+ // Format_RGB32,
+ {
+ blend_color_argb,
+ qt_gradient_quint32,
+ qt_bitmapblit_quint32,
+ qt_alphamapblit_quint32,
+ qt_alphargbblit_quint32,
+ qt_rectfill_quint32
+ },
+ // Format_ARGB32,
+ {
+ blend_color_generic,
+ qt_gradient_quint32,
+ qt_bitmapblit_quint32,
+ qt_alphamapblit_quint32,
+ qt_alphargbblit_quint32,
+ qt_rectfill_quint32
+ },
+ // Format_ARGB32_Premultiplied
+ {
+ blend_color_argb,
+ qt_gradient_quint32,
+ qt_bitmapblit_quint32,
+ qt_alphamapblit_quint32,
+ qt_alphargbblit_quint32,
+ qt_rectfill_quint32
+ },
+ // Format_RGB16
+ {
+ blend_color_rgb16,
+ qt_gradient_quint16,
+ qt_bitmapblit_quint16,
+ qt_alphamapblit_quint16,
+ 0,
+ qt_rectfill_quint16
+ },
+ // Format_ARGB8565_Premultiplied
+ {
+ SPANFUNC_POINTER_BLENDCOLOR(qargb8565),
+ SPANFUNC_POINTER(blend_src_generic, RegularSpans),
+ 0, 0, 0,
+ qt_rectfill_qargb8565
+ },
+ // Format_RGB666
+ {
+ SPANFUNC_POINTER_BLENDCOLOR(qrgb666),
+ SPANFUNC_POINTER(blend_src_generic, RegularSpans),
+ 0, 0, 0,
+ qt_rectfill_qrgb666
+ },
+ // Format_ARGB6666_Premultiplied
+ {
+ SPANFUNC_POINTER_BLENDCOLOR(qargb6666),
+ SPANFUNC_POINTER(blend_src_generic, RegularSpans),
+ 0, 0, 0,
+ qt_rectfill_qargb6666
+ },
+ // Format_RGB555
+ {
+ SPANFUNC_POINTER_BLENDCOLOR(qrgb555),
+ SPANFUNC_POINTER(blend_src_generic, RegularSpans),
+ 0, 0, 0,
+ qt_rectfill_qrgb555
+ },
+ // Format_ARGB8555_Premultiplied
+ {
+ SPANFUNC_POINTER_BLENDCOLOR(qargb8555),
+ SPANFUNC_POINTER(blend_src_generic, RegularSpans),
+ 0, 0, 0,
+ qt_rectfill_qargb8555
+ },
+ // Format_RGB888
+ {
+ SPANFUNC_POINTER_BLENDCOLOR(qrgb888),
+ SPANFUNC_POINTER(blend_src_generic, RegularSpans),
+ 0, 0, 0,
+ qt_rectfill_qrgb888
+ },
+ // Format_RGB444
+ {
+ SPANFUNC_POINTER_BLENDCOLOR(qrgb444),
+ SPANFUNC_POINTER(blend_src_generic, RegularSpans),
+ 0, 0, 0,
+ qt_rectfill_qrgb444
+ },
+ // Format_ARGB4444_Premultiplied
+ {
+ SPANFUNC_POINTER_BLENDCOLOR(qargb4444),
+ SPANFUNC_POINTER(blend_src_generic, RegularSpans),
+ 0, 0, 0,
+ qt_rectfill_qargb4444
+ }
+};
+
+#if defined (Q_WS_QWS) && !defined(QT_NO_RASTERCALLBACKS)
+DrawHelper qDrawHelperCallback[QImage::NImageFormats] =
+{
+ // Format_Invalid,
+ { 0, 0, 0, 0, 0, 0 },
+ // Format_Mono,
+ {
+ blend_color_generic_callback,
+ blend_src_generic<CallbackSpans>,
+ 0, 0, 0, 0
+ },
+ // Format_MonoLSB,
+ {
+ blend_color_generic_callback,
+ blend_src_generic<CallbackSpans>,
+ 0, 0, 0, 0
+ },
+ // Format_Indexed8,
+ {
+ blend_color_generic_callback,
+ blend_src_generic<CallbackSpans>,
+ 0, 0, 0, 0
+ },
+ // Format_RGB32,
+ {
+ blend_color_generic_callback,
+ blend_src_generic<CallbackSpans>,
+ 0, 0, 0, 0
+ },
+ // Format_ARGB32,
+ {
+ blend_color_generic_callback,
+ blend_src_generic<CallbackSpans>,
+ 0, 0, 0, 0
+ },
+ // Format_ARGB32_Premultiplied
+ {
+ blend_color_generic_callback,
+ blend_src_generic<CallbackSpans>,
+ 0, 0, 0, 0
+ },
+ // Format_RGB16
+ {
+ blend_color_generic_callback,
+ blend_src_generic<CallbackSpans>,
+ 0, 0, 0, 0
+ },
+ // Format_ARGB8565_Premultiplied
+ {
+ blend_color_generic_callback,
+ blend_src_generic<CallbackSpans>,
+ 0, 0, 0, 0
+ },
+ // Format_RGB666
+ {
+ blend_color_generic_callback,
+ blend_src_generic<CallbackSpans>,
+ 0, 0, 0, 0
+ },
+ // Format_ARGB6666_Premultiplied
+ {
+ blend_color_generic_callback,
+ blend_src_generic<CallbackSpans>,
+ 0, 0, 0, 0
+ },
+ // Format_RGB555
+ {
+ blend_color_generic_callback,
+ blend_src_generic<CallbackSpans>,
+ 0, 0, 0, 0
+ },
+ // Format_ARGB8555_Premultiplied
+ {
+ blend_color_generic_callback,
+ blend_src_generic<CallbackSpans>,
+ 0, 0, 0, 0
+ },
+ // Format_RGB888
+ {
+ blend_color_generic_callback,
+ blend_src_generic<CallbackSpans>,
+ 0, 0, 0, 0
+ },
+ // Format_RGB444
+ {
+ blend_color_generic_callback,
+ blend_src_generic<CallbackSpans>,
+ 0, 0, 0, 0
+ },
+ // Format_ARGB4444_Premultiplied
+ {
+ blend_color_generic_callback,
+ blend_src_generic<CallbackSpans>,
+ 0, 0, 0, 0
+ }
+};
+#endif
+
+
+
+#if defined(Q_CC_MSVC) && !defined(_MIPS_)
+template <class DST, class SRC>
+inline void qt_memfill_template(DST *dest, SRC color, int count)
+{
+ const DST c = qt_colorConvert<DST, SRC>(color, 0);
+ while (count--)
+ *dest++ = c;
+}
+
+#else
+
+template <class DST, class SRC>
+inline void qt_memfill_template(DST *dest, SRC color, int count)
+{
+ const DST c = qt_colorConvert<DST, SRC>(color, 0);
+ int n = (count + 7) / 8;
+ switch (count & 0x07)
+ {
+ case 0: do { *dest++ = c;
+ case 7: *dest++ = c;
+ case 6: *dest++ = c;
+ case 5: *dest++ = c;
+ case 4: *dest++ = c;
+ case 3: *dest++ = c;
+ case 2: *dest++ = c;
+ case 1: *dest++ = c;
+ } while (--n > 0);
+ }
+}
+
+template <>
+inline void qt_memfill_template(quint16 *dest, quint16 value, int count)
+{
+ if (count < 3) {
+ switch (count) {
+ case 2: *dest++ = value;
+ case 1: *dest = value;
+ }
+ return;
+ }
+
+ const int align = (quintptr)(dest) & 0x3;
+ switch (align) {
+ case 2: *dest++ = value; --count;
+ }
+
+ const quint32 value32 = (value << 16) | value;
+ qt_memfill(reinterpret_cast<quint32*>(dest), value32, count / 2);
+ if (count & 0x1)
+ dest[count - 1] = value;
+}
+#endif
+
+static void qt_memfill_quint16(quint16 *dest, quint16 color, int count)
+{
+ qt_memfill_template<quint16, quint16>(dest, color, count);
+}
+
+typedef void (*qt_memfill32_func)(quint32 *dest, quint32 value, int count);
+typedef void (*qt_memfill16_func)(quint16 *dest, quint16 value, int count);
+static void qt_memfill32_setup(quint32 *dest, quint32 value, int count);
+static void qt_memfill16_setup(quint16 *dest, quint16 value, int count);
+
+qt_memfill32_func qt_memfill32 = qt_memfill32_setup;
+qt_memfill16_func qt_memfill16 = qt_memfill16_setup;
+
+enum CPUFeatures {
+ None = 0,
+ MMX = 0x1,
+ MMXEXT = 0x2,
+ MMX3DNOW = 0x4,
+ MMX3DNOWEXT = 0x8,
+ SSE = 0x10,
+ SSE2 = 0x20,
+ CMOV = 0x40,
+ IWMMXT = 0x80
+};
+
+static uint detectCPUFeatures()
+{
+#if defined (Q_OS_WINCE)
+#if defined (ARM)
+ if (IsProcessorFeaturePresent(PF_ARM_INTEL_WMMX))
+ return IWMMXT;
+#elif defined(_X86_)
+ uint features = 0;
+#if defined QT_HAVE_MMX
+ if (IsProcessorFeaturePresent(PF_MMX_INSTRUCTIONS_AVAILABLE))
+ features |= MMX;
+#endif
+#if defined QT_HAVE_3DNOW
+ if (IsProcessorFeaturePresent(PF_3DNOW_INSTRUCTIONS_AVAILABLE))
+ features |= MMX3DNOW;
+#endif
+ return features;
+#endif
+ return 0;
+#elif defined(QT_HAVE_IWMMXT)
+ // runtime detection only available when running as a previlegied process
+ static const bool doIWMMXT = !qgetenv("QT_NO_IWMMXT").toInt();
+ return doIWMMXT ? IWMMXT : 0;
+#else
+ uint features = 0;
+#if defined(__x86_64__) || defined(Q_OS_WIN64)
+ features = MMX|SSE|SSE2|CMOV;
+#elif defined(__ia64__)
+ features = MMX|SSE|SSE2;
+#elif defined(__i386__) || defined(_M_IX86)
+ unsigned int extended_result = 0;
+ uint result = 0;
+ /* see p. 118 of amd64 instruction set manual Vol3 */
+#if defined(Q_CC_GNU)
+ asm ("push %%ebx\n"
+ "pushf\n"
+ "pop %%eax\n"
+ "mov %%eax, %%ebx\n"
+ "xor $0x00200000, %%eax\n"
+ "push %%eax\n"
+ "popf\n"
+ "pushf\n"
+ "pop %%eax\n"
+ "xor %%edx, %%edx\n"
+ "xor %%ebx, %%eax\n"
+ "jz 1f\n"
+
+ "mov $0x00000001, %%eax\n"
+ "cpuid\n"
+ "1:\n"
+ "pop %%ebx\n"
+ "mov %%edx, %0\n"
+ : "=r" (result)
+ :
+ : "%eax", "%ecx", "%edx"
+ );
+
+ asm ("push %%ebx\n"
+ "pushf\n"
+ "pop %%eax\n"
+ "mov %%eax, %%ebx\n"
+ "xor $0x00200000, %%eax\n"
+ "push %%eax\n"
+ "popf\n"
+ "pushf\n"
+ "pop %%eax\n"
+ "xor %%edx, %%edx\n"
+ "xor %%ebx, %%eax\n"
+ "jz 2f\n"
+
+ "mov $0x80000000, %%eax\n"
+ "cpuid\n"
+ "cmp $0x80000000, %%eax\n"
+ "jbe 2f\n"
+ "mov $0x80000001, %%eax\n"
+ "cpuid\n"
+ "2:\n"
+ "pop %%ebx\n"
+ "mov %%edx, %0\n"
+ : "=r" (extended_result)
+ :
+ : "%eax", "%ecx", "%edx"
+ );
+#elif defined (Q_OS_WIN)
+ _asm {
+ push eax
+ push ebx
+ push ecx
+ push edx
+ pushfd
+ pop eax
+ mov ebx, eax
+ xor eax, 00200000h
+ push eax
+ popfd
+ pushfd
+ pop eax
+ mov edx, 0
+ xor eax, ebx
+ jz skip
+
+ mov eax, 1
+ cpuid
+ mov result, edx
+ skip:
+ pop edx
+ pop ecx
+ pop ebx
+ pop eax
+ }
+
+ _asm {
+ push eax
+ push ebx
+ push ecx
+ push edx
+ pushfd
+ pop eax
+ mov ebx, eax
+ xor eax, 00200000h
+ push eax
+ popfd
+ pushfd
+ pop eax
+ mov edx, 0
+ xor eax, ebx
+ jz skip2
+
+ mov eax, 80000000h
+ cpuid
+ cmp eax, 80000000h
+ jbe skip2
+ mov eax, 80000001h
+ cpuid
+ mov extended_result, edx
+ skip2:
+ pop edx
+ pop ecx
+ pop ebx
+ pop eax
+ }
+#endif
+
+ // result now contains the standard feature bits
+ if (result & (1u << 15))
+ features |= CMOV;
+ if (result & (1u << 23))
+ features |= MMX;
+ if (extended_result & (1u << 22))
+ features |= MMXEXT;
+ if (extended_result & (1u << 31))
+ features |= MMX3DNOW;
+ if (extended_result & (1u << 30))
+ features |= MMX3DNOWEXT;
+ if (result & (1u << 25))
+ features |= SSE;
+ if (result & (1u << 26))
+ features |= SSE2;
+#endif // i386
+
+ if (qgetenv("QT_NO_MMX").toInt())
+ features ^= MMX;
+ if (qgetenv("QT_NO_MMXEXT").toInt())
+ features ^= MMXEXT;
+ if (qgetenv("QT_NO_3DNOW").toInt())
+ features ^= MMX3DNOW;
+ if (qgetenv("QT_NO_3DNOWEXT").toInt())
+ features ^= MMX3DNOWEXT;
+ if (qgetenv("QT_NO_SSE").toInt())
+ features ^= SSE;
+ if (qgetenv("QT_NO_SSE2").toInt())
+ features ^= SSE2;
+
+ return features;
+#endif
+}
+
+void qInitDrawhelperAsm()
+{
+ static uint features = 0xffffffff;
+ if (features != 0xffffffff)
+ return;
+ features = detectCPUFeatures();
+
+ qt_memfill32 = qt_memfill_template<quint32, quint32>;
+ qt_memfill16 = qt_memfill_quint16; //qt_memfill_template<quint16, quint16>;
+
+ CompositionFunction *functionForModeAsm = 0;
+ CompositionFunctionSolid *functionForModeSolidAsm = 0;
+
+#ifdef QT_NO_DEBUG
+ if (false) {
+#ifdef QT_HAVE_SSE2
+ } else if (features & SSE2) {
+ qt_memfill32 = qt_memfill32_sse2;
+ qt_memfill16 = qt_memfill16_sse2;
+ qDrawHelper[QImage::Format_RGB32].bitmapBlit = qt_bitmapblit32_sse2;
+ qDrawHelper[QImage::Format_ARGB32].bitmapBlit = qt_bitmapblit32_sse2;
+ qDrawHelper[QImage::Format_ARGB32_Premultiplied].bitmapBlit = qt_bitmapblit32_sse2;
+ qDrawHelper[QImage::Format_RGB16].bitmapBlit = qt_bitmapblit16_sse2;
+#endif
+#ifdef QT_HAVE_SSE
+ } else if (features & SSE) {
+// qt_memfill32 = qt_memfill32_sse;
+ qDrawHelper[QImage::Format_RGB16].bitmapBlit = qt_bitmapblit16_sse;
+#ifdef QT_HAVE_3DNOW
+ if (features & MMX3DNOW) {
+ qt_memfill32 = qt_memfill32_sse3dnow;
+ qDrawHelper[QImage::Format_RGB16].bitmapBlit = qt_bitmapblit16_sse3dnow;
+ }
+#endif
+#endif // SSE
+#if defined(QT_HAVE_MMXEXT) && defined(QT_HAVE_SSE)
+ } else if (features & MMXEXT) {
+ qt_memfill32 = qt_memfill32_sse;
+ qDrawHelper[QImage::Format_RGB16].bitmapBlit = qt_bitmapblit16_sse;
+# ifdef QT_HAVE_3DNOW
+ if (features & MMX3DNOW) {
+ qt_memfill32 = qt_memfill32_sse3dnow;
+ qDrawHelper[QImage::Format_RGB16].bitmapBlit = qt_bitmapblit16_sse3dnow;
+ }
+# endif // 3DNOW
+#endif // MMXEXT
+ }
+#ifdef QT_HAVE_MMX
+ if (features & MMX) {
+ functionForModeAsm = qt_functionForMode_MMX;
+ functionForModeSolidAsm = qt_functionForModeSolid_MMX;
+ qDrawHelper[QImage::Format_ARGB32_Premultiplied].blendColor = qt_blend_color_argb_mmx;
+#ifdef QT_HAVE_3DNOW
+ if (features & MMX3DNOW) {
+ functionForModeAsm = qt_functionForMode_MMX3DNOW;
+ functionForModeSolidAsm = qt_functionForModeSolid_MMX3DNOW;
+ qDrawHelper[QImage::Format_ARGB32_Premultiplied].blendColor = qt_blend_color_argb_mmx3dnow;
+ }
+#endif // 3DNOW
+
+ extern void qt_blend_rgb32_on_rgb32_mmx(uchar *destPixels, int dbpl,
+ const uchar *srcPixels, int sbpl,
+ int w, int h,
+ int const_alpha);
+ extern void qt_blend_argb32_on_argb32_mmx(uchar *destPixels, int dbpl,
+ const uchar *srcPixels, int sbpl,
+ int w, int h,
+ int const_alpha);
+
+ qBlendFunctions[QImage::Format_RGB32][QImage::Format_RGB32] = qt_blend_rgb32_on_rgb32_mmx;
+ qBlendFunctions[QImage::Format_ARGB32_Premultiplied][QImage::Format_RGB32] = qt_blend_rgb32_on_rgb32_mmx;
+ qBlendFunctions[QImage::Format_RGB32][QImage::Format_ARGB32_Premultiplied] = qt_blend_argb32_on_argb32_mmx;
+ qBlendFunctions[QImage::Format_ARGB32_Premultiplied][QImage::Format_ARGB32_Premultiplied] = qt_blend_argb32_on_argb32_mmx;
+
+ }
+#endif // MMX
+
+#ifdef QT_HAVE_SSE
+ if (features & SSE) {
+ functionForModeAsm = qt_functionForMode_SSE;
+ functionForModeSolidAsm = qt_functionForModeSolid_SSE;
+ qDrawHelper[QImage::Format_ARGB32_Premultiplied].blendColor = qt_blend_color_argb_sse;
+#ifdef QT_HAVE_3DNOW
+ if (features & MMX3DNOW) {
+ functionForModeAsm = qt_functionForMode_SSE3DNOW;
+ functionForModeSolidAsm = qt_functionForModeSolid_SSE3DNOW;
+ qDrawHelper[QImage::Format_ARGB32_Premultiplied].blendColor = qt_blend_color_argb_sse3dnow;
+ }
+#endif // 3DNOW
+ extern void qt_blend_rgb32_on_rgb32_sse(uchar *destPixels, int dbpl,
+ const uchar *srcPixels, int sbpl,
+ int w, int h,
+ int const_alpha);
+ extern void qt_blend_argb32_on_argb32_sse(uchar *destPixels, int dbpl,
+ const uchar *srcPixels, int sbpl,
+ int w, int h,
+ int const_alpha);
+
+ qBlendFunctions[QImage::Format_RGB32][QImage::Format_RGB32] = qt_blend_rgb32_on_rgb32_sse;
+ qBlendFunctions[QImage::Format_ARGB32_Premultiplied][QImage::Format_RGB32] = qt_blend_rgb32_on_rgb32_sse;
+ qBlendFunctions[QImage::Format_RGB32][QImage::Format_ARGB32_Premultiplied] = qt_blend_argb32_on_argb32_sse;
+ qBlendFunctions[QImage::Format_ARGB32_Premultiplied][QImage::Format_ARGB32_Premultiplied] = qt_blend_argb32_on_argb32_sse;
+ }
+#endif // SSE
+
+#ifdef QT_HAVE_IWMMXT
+ if (features & IWMMXT) {
+ functionForModeAsm = qt_functionForMode_IWMMXT;
+ functionForModeSolidAsm = qt_functionForModeSolid_IWMMXT;
+ qDrawHelper[QImage::Format_ARGB32_Premultiplied].blendColor = qt_blend_color_argb_iwmmxt;
+ }
+#endif // IWMMXT
+
+#endif // QT_NO_DEBUG
+
+ if (functionForModeSolidAsm) {
+ const int destinationMode = QPainter::CompositionMode_Destination;
+ functionForModeSolidAsm[destinationMode] = functionForModeSolid_C[destinationMode];
+
+ // use the default qdrawhelper implementation for the
+ // extended composition modes
+ for (int mode = 12; mode < 24; ++mode)
+ functionForModeSolidAsm[mode] = functionForModeSolid_C[mode];
+
+ functionForModeSolid = functionForModeSolidAsm;
+ }
+ if (functionForModeAsm) {
+ const int destinationMode = QPainter::CompositionMode_Destination;
+ functionForModeAsm[destinationMode] = functionForMode_C[destinationMode];
+
+ // use the default qdrawhelper implementation for the
+ // extended composition modes
+ for (int mode = 12; mode < numCompositionFunctions; ++mode)
+ functionForModeAsm[mode] = functionForMode_C[mode];
+
+ functionForMode = functionForModeAsm;
+ }
+
+ qt_build_pow_tables();
+}
+
+static void qt_memfill32_setup(quint32 *dest, quint32 value, int count)
+{
+ qInitDrawhelperAsm();
+ qt_memfill32(dest, value, count);
+}
+
+static void qt_memfill16_setup(quint16 *dest, quint16 value, int count)
+{
+ qInitDrawhelperAsm();
+ qt_memfill16(dest, value, count);
+}
+
+#ifdef QT_QWS_DEPTH_GENERIC
+
+int qrgb::bpp = 0;
+int qrgb::len_red = 0;
+int qrgb::len_green = 0;
+int qrgb::len_blue = 0;
+int qrgb::len_alpha = 0;
+int qrgb::off_red = 0;
+int qrgb::off_green = 0;
+int qrgb::off_blue = 0;
+int qrgb::off_alpha = 0;
+
+template <typename SRC>
+Q_STATIC_TEMPLATE_FUNCTION inline void qt_rectconvert_rgb(qrgb *dest, const SRC *src,
+ int x, int y, int width, int height,
+ int dstStride, int srcStride)
+{
+ quint8 *dest8 = reinterpret_cast<quint8*>(dest)
+ + y * dstStride + x * qrgb::bpp;
+
+ srcStride = srcStride / sizeof(SRC) - width;
+ dstStride -= (width * qrgb::bpp);
+
+ for (int j = 0; j < height; ++j) {
+ for (int i = 0; i < width; ++i) {
+ const quint32 v = qt_convertToRgb<SRC>(*src++);
+#if Q_BYTE_ORDER == Q_BIG_ENDIAN
+ for (int j = qrgb::bpp - 1; j >= 0; --j)
+ *dest8++ = (v >> (8 * j)) & 0xff;
+#else
+ for (int j = 0; j < qrgb::bpp; ++j)
+ *dest8++ = (v >> (8 * j)) & 0xff;
+#endif
+ }
+
+ dest8 += dstStride;
+ src += srcStride;
+ }
+}
+
+template <>
+void qt_rectconvert(qrgb *dest, const quint32 *src,
+ int x, int y, int width, int height,
+ int dstStride, int srcStride)
+{
+ qt_rectconvert_rgb<quint32>(dest, src, x, y, width, height,
+ dstStride, srcStride);
+}
+
+template <>
+void qt_rectconvert(qrgb *dest, const quint16 *src,
+ int x, int y, int width, int height,
+ int dstStride, int srcStride)
+{
+ qt_rectconvert_rgb<quint16>(dest, src, x, y, width, height,
+ dstStride, srcStride);
+}
+
+#endif // QT_QWS_DEPTH_GENERIC
+
+QT_END_NAMESPACE
diff --git a/src/gui/painting/qdrawhelper_iwmmxt.cpp b/src/gui/painting/qdrawhelper_iwmmxt.cpp
new file mode 100644
index 0000000000..cde6bf9aed
--- /dev/null
+++ b/src/gui/painting/qdrawhelper_iwmmxt.cpp
@@ -0,0 +1,127 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifdef QT_HAVE_IWMMXT
+
+#include <mmintrin.h>
+#if defined(Q_OS_WINCE)
+# include "qplatformdefs.h"
+#endif
+#if !defined(__IWMMXT__) && !defined(Q_OS_WINCE)
+# include <xmmintrin.h>
+#elif defined(Q_OS_WINCE_STD) && defined(_X86_)
+# pragma warning(disable: 4391)
+# include <xmmintrin.h>
+#endif
+
+#include <private/qdrawhelper_sse_p.h>
+
+QT_BEGIN_NAMESPACE
+
+#ifndef _MM_SHUFFLE
+#define _MM_SHUFFLE(fp3,fp2,fp1,fp0) \
+ (((fp3) << 6) | ((fp2) << 4) | ((fp1) << 2) | (fp0))
+#endif
+
+struct QIWMMXTIntrinsics : public QMMXCommonIntrinsics
+{
+ static inline m64 alpha(m64 x) {
+ return _mm_shuffle_pi16 (x, _MM_SHUFFLE(3, 3, 3, 3));
+ }
+
+ static inline m64 _load_alpha(uint x, const m64 &mmx_0x0000) {
+ m64 t = _mm_unpacklo_pi8(_mm_cvtsi32_si64(x), mmx_0x0000);
+ return _mm_shuffle_pi16(t, _MM_SHUFFLE(0, 0, 0, 0));
+ }
+
+ static inline void end() {
+ }
+};
+
+CompositionFunctionSolid qt_functionForModeSolid_IWMMXT[numCompositionFunctions] = {
+ comp_func_solid_SourceOver<QIWMMXTIntrinsics>,
+ comp_func_solid_DestinationOver<QIWMMXTIntrinsics>,
+ comp_func_solid_Clear<QIWMMXTIntrinsics>,
+ comp_func_solid_Source<QIWMMXTIntrinsics>,
+ 0,
+ comp_func_solid_SourceIn<QIWMMXTIntrinsics>,
+ comp_func_solid_DestinationIn<QIWMMXTIntrinsics>,
+ comp_func_solid_SourceOut<QIWMMXTIntrinsics>,
+ comp_func_solid_DestinationOut<QIWMMXTIntrinsics>,
+ comp_func_solid_SourceAtop<QIWMMXTIntrinsics>,
+ comp_func_solid_DestinationAtop<QIWMMXTIntrinsics>,
+ comp_func_solid_XOR<QIWMMXTIntrinsics>,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // svg 1.2 modes
+ rasterop_solid_SourceOrDestination<QIWMMXTIntrinsics>,
+ rasterop_solid_SourceAndDestination<QIWMMXTIntrinsics>,
+ rasterop_solid_SourceXorDestination<QIWMMXTIntrinsics>,
+ rasterop_solid_NotSourceAndNotDestination<QIWMMXTIntrinsics>,
+ rasterop_solid_NotSourceOrNotDestination<QIWMMXTIntrinsics>,
+ rasterop_solid_NotSourceXorDestination<QIWMMXTIntrinsics>,
+ rasterop_solid_NotSource<QIWMMXTIntrinsics>,
+ rasterop_solid_NotSourceAndDestination<QIWMMXTIntrinsics>,
+ rasterop_solid_SourceAndNotDestination<QIWMMXTIntrinsics>
+};
+
+CompositionFunction qt_functionForMode_IWMMXT[] = {
+ comp_func_SourceOver<QIWMMXTIntrinsics>,
+ comp_func_DestinationOver<QIWMMXTIntrinsics>,
+ comp_func_Clear<QIWMMXTIntrinsics>,
+ comp_func_Source<QIWMMXTIntrinsics>,
+ 0,
+ comp_func_SourceIn<QIWMMXTIntrinsics>,
+ comp_func_DestinationIn<QIWMMXTIntrinsics>,
+ comp_func_SourceOut<QIWMMXTIntrinsics>,
+ comp_func_DestinationOut<QIWMMXTIntrinsics>,
+ comp_func_SourceAtop<QIWMMXTIntrinsics>,
+ comp_func_DestinationAtop<QIWMMXTIntrinsics>,
+ comp_func_XOR<QIWMMXTIntrinsics>
+};
+
+void qt_blend_color_argb_iwmmxt(int count, const QSpan *spans, void *userData)
+{
+ qt_blend_color_argb_x86<QIWMMXTIntrinsics>(count, spans, userData,
+ (CompositionFunctionSolid*)qt_functionForModeSolid_IWMMXT);
+}
+
+#endif // QT_HAVE_IWMMXT
+
+QT_END_NAMESPACE
diff --git a/src/gui/painting/qdrawhelper_mmx.cpp b/src/gui/painting/qdrawhelper_mmx.cpp
new file mode 100644
index 0000000000..bb7f26d7e2
--- /dev/null
+++ b/src/gui/painting/qdrawhelper_mmx.cpp
@@ -0,0 +1,134 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <private/qdrawhelper_p.h>
+
+#if defined(QT_HAVE_MMX)
+
+#include <private/qdrawhelper_mmx_p.h>
+
+QT_BEGIN_NAMESPACE
+
+CompositionFunctionSolid qt_functionForModeSolid_MMX[numCompositionFunctions] = {
+ comp_func_solid_SourceOver<QMMXIntrinsics>,
+ comp_func_solid_DestinationOver<QMMXIntrinsics>,
+ comp_func_solid_Clear<QMMXIntrinsics>,
+ comp_func_solid_Source<QMMXIntrinsics>,
+ 0,
+ comp_func_solid_SourceIn<QMMXIntrinsics>,
+ comp_func_solid_DestinationIn<QMMXIntrinsics>,
+ comp_func_solid_SourceOut<QMMXIntrinsics>,
+ comp_func_solid_DestinationOut<QMMXIntrinsics>,
+ comp_func_solid_SourceAtop<QMMXIntrinsics>,
+ comp_func_solid_DestinationAtop<QMMXIntrinsics>,
+ comp_func_solid_XOR<QMMXIntrinsics>,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // svg 1.2 modes
+ rasterop_solid_SourceOrDestination<QMMXIntrinsics>,
+ rasterop_solid_SourceAndDestination<QMMXIntrinsics>,
+ rasterop_solid_SourceXorDestination<QMMXIntrinsics>,
+ rasterop_solid_NotSourceAndNotDestination<QMMXIntrinsics>,
+ rasterop_solid_NotSourceOrNotDestination<QMMXIntrinsics>,
+ rasterop_solid_NotSourceXorDestination<QMMXIntrinsics>,
+ rasterop_solid_NotSource<QMMXIntrinsics>,
+ rasterop_solid_NotSourceAndDestination<QMMXIntrinsics>,
+ rasterop_solid_SourceAndNotDestination<QMMXIntrinsics>
+};
+
+CompositionFunction qt_functionForMode_MMX[numCompositionFunctions] = {
+ comp_func_SourceOver<QMMXIntrinsics>,
+ comp_func_DestinationOver<QMMXIntrinsics>,
+ comp_func_Clear<QMMXIntrinsics>,
+ comp_func_Source<QMMXIntrinsics>,
+ 0,
+ comp_func_SourceIn<QMMXIntrinsics>,
+ comp_func_DestinationIn<QMMXIntrinsics>,
+ comp_func_SourceOut<QMMXIntrinsics>,
+ comp_func_DestinationOut<QMMXIntrinsics>,
+ comp_func_SourceAtop<QMMXIntrinsics>,
+ comp_func_DestinationAtop<QMMXIntrinsics>,
+ comp_func_XOR<QMMXIntrinsics>,
+};
+
+void qt_blend_color_argb_mmx(int count, const QSpan *spans, void *userData)
+{
+ qt_blend_color_argb_x86<QMMXIntrinsics>(count, spans, userData,
+ (CompositionFunctionSolid*)qt_functionForModeSolid_MMX);
+}
+
+
+void qt_blend_argb32_on_argb32_mmx(uchar *destPixels, int dbpl,
+ const uchar *srcPixels, int sbpl,
+ int w, int h,
+ int const_alpha)
+{
+ const uint *src = (const uint *) srcPixels;
+ uint *dst = (uint *) destPixels;
+
+ uint ca = const_alpha - 1;
+
+ for (int y=0; y<h; ++y) {
+ comp_func_SourceOver<QMMXIntrinsics>(dst, src, w, ca);
+ dst = (quint32 *)(((uchar *) dst) + dbpl);
+ src = (const quint32 *)(((const uchar *) src) + sbpl);
+ }
+}
+
+void qt_blend_rgb32_on_rgb32_mmx(uchar *destPixels, int dbpl,
+ const uchar *srcPixels, int sbpl,
+ int w, int h,
+ int const_alpha)
+{
+ const uint *src = (const uint *) srcPixels;
+ uint *dst = (uint *) destPixels;
+
+ uint ca = const_alpha - 1;
+
+ for (int y=0; y<h; ++y) {
+ comp_func_Source<QMMXIntrinsics>(dst, src, w, ca);
+ dst = (quint32 *)(((uchar *) dst) + dbpl);
+ src = (const quint32 *)(((const uchar *) src) + sbpl);
+ }
+}
+
+
+#endif // QT_HAVE_MMX
+
+QT_END_NAMESPACE
diff --git a/src/gui/painting/qdrawhelper_mmx3dnow.cpp b/src/gui/painting/qdrawhelper_mmx3dnow.cpp
new file mode 100644
index 0000000000..d2332952e9
--- /dev/null
+++ b/src/gui/painting/qdrawhelper_mmx3dnow.cpp
@@ -0,0 +1,106 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <private/qdrawhelper_x86_p.h>
+
+#ifdef QT_HAVE_3DNOW
+
+#include <private/qdrawhelper_mmx_p.h>
+#include <mm3dnow.h>
+
+QT_BEGIN_NAMESPACE
+
+struct QMMX3DNOWIntrinsics : public QMMXCommonIntrinsics
+{
+ static inline void end() {
+ _m_femms();
+ }
+};
+
+CompositionFunctionSolid qt_functionForModeSolid_MMX3DNOW[numCompositionFunctions] = {
+ comp_func_solid_SourceOver<QMMX3DNOWIntrinsics>,
+ comp_func_solid_DestinationOver<QMMX3DNOWIntrinsics>,
+ comp_func_solid_Clear<QMMX3DNOWIntrinsics>,
+ comp_func_solid_Source<QMMX3DNOWIntrinsics>,
+ 0,
+ comp_func_solid_SourceIn<QMMX3DNOWIntrinsics>,
+ comp_func_solid_DestinationIn<QMMX3DNOWIntrinsics>,
+ comp_func_solid_SourceOut<QMMX3DNOWIntrinsics>,
+ comp_func_solid_DestinationOut<QMMX3DNOWIntrinsics>,
+ comp_func_solid_SourceAtop<QMMX3DNOWIntrinsics>,
+ comp_func_solid_DestinationAtop<QMMX3DNOWIntrinsics>,
+ comp_func_solid_XOR<QMMX3DNOWIntrinsics>,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // svg 1.2 modes
+ rasterop_solid_SourceOrDestination<QMMX3DNOWIntrinsics>,
+ rasterop_solid_SourceAndDestination<QMMX3DNOWIntrinsics>,
+ rasterop_solid_SourceXorDestination<QMMX3DNOWIntrinsics>,
+ rasterop_solid_NotSourceAndNotDestination<QMMX3DNOWIntrinsics>,
+ rasterop_solid_NotSourceOrNotDestination<QMMX3DNOWIntrinsics>,
+ rasterop_solid_NotSourceXorDestination<QMMX3DNOWIntrinsics>,
+ rasterop_solid_NotSource<QMMX3DNOWIntrinsics>,
+ rasterop_solid_NotSourceAndDestination<QMMX3DNOWIntrinsics>,
+ rasterop_solid_SourceAndNotDestination<QMMX3DNOWIntrinsics>
+};
+
+CompositionFunction qt_functionForMode_MMX3DNOW[numCompositionFunctions] = {
+ comp_func_SourceOver<QMMX3DNOWIntrinsics>,
+ comp_func_DestinationOver<QMMX3DNOWIntrinsics>,
+ comp_func_Clear<QMMX3DNOWIntrinsics>,
+ comp_func_Source<QMMX3DNOWIntrinsics>,
+ 0,
+ comp_func_SourceIn<QMMX3DNOWIntrinsics>,
+ comp_func_DestinationIn<QMMX3DNOWIntrinsics>,
+ comp_func_SourceOut<QMMX3DNOWIntrinsics>,
+ comp_func_DestinationOut<QMMX3DNOWIntrinsics>,
+ comp_func_SourceAtop<QMMX3DNOWIntrinsics>,
+ comp_func_DestinationAtop<QMMX3DNOWIntrinsics>,
+ comp_func_XOR<QMMX3DNOWIntrinsics>
+};
+
+void qt_blend_color_argb_mmx3dnow(int count, const QSpan *spans, void *userData)
+{
+ qt_blend_color_argb_x86<QMMX3DNOWIntrinsics>(count, spans, userData,
+ (CompositionFunctionSolid*)qt_functionForModeSolid_MMX3DNOW);
+}
+
+#endif // QT_HAVE_3DNOW
+
+QT_END_NAMESPACE
diff --git a/src/gui/painting/qdrawhelper_mmx_p.h b/src/gui/painting/qdrawhelper_mmx_p.h
new file mode 100644
index 0000000000..3dea5dead7
--- /dev/null
+++ b/src/gui/painting/qdrawhelper_mmx_p.h
@@ -0,0 +1,893 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QDRAWHELPER_MMX_P_H
+#define QDRAWHELPER_MMX_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/qdrawhelper_p.h>
+#include <private/qdrawhelper_x86_p.h>
+#include <private/qpaintengine_raster_p.h>
+
+#ifdef QT_HAVE_MMX
+#include <mmintrin.h>
+#endif
+
+#define C_FF const m64 mmx_0x00ff = _mm_set1_pi16(0xff)
+#define C_80 const m64 mmx_0x0080 = _mm_set1_pi16(0x80)
+#define C_00 const m64 mmx_0x0000 = _mm_setzero_si64()
+
+#if defined(Q_OS_WIN)
+# pragma warning(disable: 4799) // No EMMS at end of function
+#endif
+
+typedef __m64 m64;
+
+QT_BEGIN_NAMESPACE
+
+struct QMMXCommonIntrinsics
+{
+ static inline m64 alpha(m64 x) {
+ x = _mm_unpackhi_pi16(x, x);
+ x = _mm_unpackhi_pi16(x, x);
+ return x;
+ }
+
+ static inline m64 _negate(const m64 &x, const m64 &mmx_0x00ff) {
+ return _mm_xor_si64(x, mmx_0x00ff);
+ }
+
+ static inline m64 add(const m64 &a, const m64 &b) {
+ return _mm_adds_pu16 (a, b);
+ }
+
+ static inline m64 _byte_mul(const m64 &a, const m64 &b,
+ const m64 &mmx_0x0080)
+ {
+ m64 res = _mm_mullo_pi16(a, b);
+ res = _mm_adds_pu16(res, mmx_0x0080);
+ res = _mm_adds_pu16(res, _mm_srli_pi16 (res, 8));
+ return _mm_srli_pi16(res, 8);
+ }
+
+ static inline m64 interpolate_pixel_256(const m64 &x, const m64 &a,
+ const m64 &y, const m64 &b)
+ {
+ m64 res = _mm_adds_pu16(_mm_mullo_pi16(x, a), _mm_mullo_pi16(y, b));
+ return _mm_srli_pi16(res, 8);
+ }
+
+ static inline m64 _interpolate_pixel_255(const m64 &x, const m64 &a,
+ const m64 &y, const m64 &b,
+ const m64 &mmx_0x0080)
+ {
+ m64 res = _mm_adds_pu16(_mm_mullo_pi16(x, a), _mm_mullo_pi16(y, b));
+ res = _mm_adds_pu16(res, mmx_0x0080);
+ res = _mm_adds_pu16(res, _mm_srli_pi16 (res, 8));
+ return _mm_srli_pi16(res, 8);
+ }
+
+ static inline m64 _premul(m64 x, const m64 &mmx_0x0080) {
+ m64 a = alpha(x);
+ return _byte_mul(x, a, mmx_0x0080);
+ }
+
+ static inline m64 _load(uint x, const m64 &mmx_0x0000) {
+ return _mm_unpacklo_pi8(_mm_cvtsi32_si64(x), mmx_0x0000);
+ }
+
+ static inline m64 _load_alpha(uint x, const m64 &) {
+ x |= (x << 16);
+ return _mm_set1_pi32(x);
+ }
+
+ static inline uint _store(const m64 &x, const m64 &mmx_0x0000) {
+ return _mm_cvtsi64_si32(_mm_packs_pu16(x, mmx_0x0000));
+ }
+};
+
+#define negate(x) _negate(x, mmx_0x00ff)
+#define byte_mul(a, b) _byte_mul(a, b, mmx_0x0080)
+#define interpolate_pixel_255(x, a, y, b) _interpolate_pixel_255(x, a, y, b, mmx_0x0080)
+#define premul(x) _premul(x, mmx_0x0080)
+#define load(x) _load(x, mmx_0x0000)
+#define load_alpha(x) _load_alpha(x, mmx_0x0000)
+#define store(x) _store(x, mmx_0x0000)
+
+/*
+ result = 0
+ d = d * cia
+*/
+template <class MM>
+static void QT_FASTCALL comp_func_solid_Clear(uint *dest, int length, uint, uint const_alpha)
+{
+ if (!length)
+ return;
+
+ if (const_alpha == 255) {
+ qt_memfill(static_cast<quint32*>(dest), quint32(0), length);
+ } else {
+ C_FF; C_80; C_00;
+ m64 ia = MM::negate(MM::load_alpha(const_alpha));
+ for (int i = 0; i < length; ++i) {
+ dest[i] = MM::store(MM::byte_mul(MM::load(dest[i]), ia));
+ }
+ }
+ MM::end();
+}
+
+template <class MM>
+static void QT_FASTCALL comp_func_Clear(uint *dest, const uint *, int length, uint const_alpha)
+{
+ if (const_alpha == 255) {
+ qt_memfill(static_cast<quint32*>(dest), quint32(0), length);
+ } else {
+ C_FF; C_80; C_00;
+ m64 ia = MM::negate(MM::load_alpha(const_alpha));
+ for (int i = 0; i < length; ++i)
+ dest[i] = MM::store(MM::byte_mul(MM::load(dest[i]), ia));
+ }
+ MM::end();
+}
+
+/*
+ result = s
+ dest = s * ca + d * cia
+*/
+template <class MM>
+static void QT_FASTCALL comp_func_solid_Source(uint *dest, int length, uint src, uint const_alpha)
+{
+ if (const_alpha == 255) {
+ qt_memfill(static_cast<quint32*>(dest), quint32(src), length);
+ } else {
+ C_FF; C_80; C_00;
+ const m64 a = MM::load_alpha(const_alpha);
+ const m64 ia = MM::negate(a);
+ const m64 s = MM::byte_mul(MM::load(src), a);
+ for (int i = 0; i < length; ++i) {
+ dest[i] = MM::store(MM::add(s, MM::byte_mul(MM::load(dest[i]), ia)));
+ }
+ MM::end();
+ }
+}
+
+template <class MM>
+static void QT_FASTCALL comp_func_Source(uint *dest, const uint *src, int length, uint const_alpha)
+{
+ if (const_alpha == 255) {
+ ::memcpy(dest, src, length * sizeof(uint));
+ } else {
+ C_FF; C_80; C_00;
+ const m64 a = MM::load_alpha(const_alpha);
+ const m64 ia = MM::negate(a);
+ for (int i = 0; i < length; ++i)
+ dest[i] = MM::store(MM::interpolate_pixel_255(MM::load(src[i]), a,
+ MM::load(dest[i]), ia));
+ }
+ MM::end();
+}
+
+/*
+ result = s + d * sia
+ dest = (s + d * sia) * ca + d * cia
+ = s * ca + d * (sia * ca + cia)
+ = s * ca + d * (1 - sa*ca)
+*/
+template <class MM>
+static void QT_FASTCALL comp_func_solid_SourceOver(uint *dest, int length, uint src, uint const_alpha)
+{
+ if ((const_alpha & qAlpha(src)) == 255) {
+ qt_memfill(static_cast<quint32*>(dest), quint32(src), length);
+ } else {
+ C_FF; C_80; C_00;
+ m64 s = MM::load(src);
+ if (const_alpha != 255) {
+ m64 ca = MM::load_alpha(const_alpha);
+ s = MM::byte_mul(s, ca);
+ }
+ m64 a = MM::negate(MM::alpha(s));
+ for (int i = 0; i < length; ++i)
+ dest[i] = MM::store(MM::add(s, MM::byte_mul(MM::load(dest[i]), a)));
+ MM::end();
+ }
+}
+
+template <class MM>
+static void QT_FASTCALL comp_func_SourceOver(uint *dest, const uint *src, int length, uint const_alpha)
+{
+ C_FF; C_80; C_00;
+ if (const_alpha == 255) {
+ for (int i = 0; i < length; ++i) {
+ if ((0xff000000 & src[i]) == 0xff000000) {
+ dest[i] = src[i];
+ } else {
+ m64 s = MM::load(src[i]);
+ m64 ia = MM::negate(MM::alpha(s));
+ dest[i] = MM::store(MM::add(s, MM::byte_mul(MM::load(dest[i]), ia)));
+ }
+ }
+ } else {
+ m64 ca = MM::load_alpha(const_alpha);
+ for (int i = 0; i < length; ++i) {
+ m64 s = MM::byte_mul(MM::load(src[i]), ca);
+ m64 ia = MM::negate(MM::alpha(s));
+ dest[i] = MM::store(MM::add(s, MM::byte_mul(MM::load(dest[i]), ia)));
+ }
+ }
+ MM::end();
+}
+
+/*
+ result = d + s * dia
+ dest = (d + s * dia) * ca + d * cia
+ = d + s * dia * ca
+*/
+template <class MM>
+static void QT_FASTCALL comp_func_solid_DestinationOver(uint *dest, int length, uint src, uint const_alpha)
+{
+ C_FF; C_80; C_00;
+ m64 s = MM::load(src);
+ if (const_alpha != 255)
+ s = MM::byte_mul(s, MM::load_alpha(const_alpha));
+
+ for (int i = 0; i < length; ++i) {
+ m64 d = MM::load(dest[i]);
+ m64 dia = MM::negate(MM::alpha(d));
+ dest[i] = MM::store(MM::add(d, MM::byte_mul(s, dia)));
+ }
+ MM::end();
+}
+
+template <class MM>
+static void QT_FASTCALL comp_func_DestinationOver(uint *dest, const uint *src, int length, uint const_alpha)
+{
+ C_FF; C_80; C_00;
+ if (const_alpha == 255) {
+ for (int i = 0; i < length; ++i) {
+ m64 d = MM::load(dest[i]);
+ m64 ia = MM::negate(MM::alpha(d));
+ dest[i] = MM::store(MM::add(d, MM::byte_mul(MM::load(src[i]), ia)));
+ }
+ } else {
+ m64 ca = MM::load_alpha(const_alpha);
+ for (int i = 0; i < length; ++i) {
+ m64 d = MM::load(dest[i]);
+ m64 dia = MM::negate(MM::alpha(d));
+ dia = MM::byte_mul(dia, ca);
+ dest[i] = MM::store(MM::add(d, MM::byte_mul(MM::load(src[i]), dia)));
+ }
+ }
+ MM::end();
+}
+
+/*
+ result = s * da
+ dest = s * da * ca + d * cia
+*/
+template <class MM>
+static void QT_FASTCALL comp_func_solid_SourceIn(uint *dest, int length, uint src, uint const_alpha)
+{
+ C_80; C_00;
+ if (const_alpha == 255) {
+ m64 s = MM::load(src);
+ for (int i = 0; i < length; ++i) {
+ m64 da = MM::alpha(MM::load(dest[i]));
+ dest[i] = MM::store(MM::byte_mul(s, da));
+ }
+ } else {
+ C_FF;
+ m64 s = MM::load(src);
+ m64 ca = MM::load_alpha(const_alpha);
+ s = MM::byte_mul(s, ca);
+ m64 cia = MM::negate(ca);
+ for (int i = 0; i < length; ++i) {
+ m64 d = MM::load(dest[i]);
+ dest[i] = MM::store(MM::interpolate_pixel_255(s, MM::alpha(d), d, cia));
+ }
+ }
+ MM::end();
+}
+
+template <class MM>
+static void QT_FASTCALL comp_func_SourceIn(uint *dest, const uint *src, int length, uint const_alpha)
+{
+ C_FF; C_80; C_00;
+ if (const_alpha == 255) {
+ for (int i = 0; i < length; ++i) {
+ m64 a = MM::alpha(MM::load(dest[i]));
+ dest[i] = MM::store(MM::byte_mul(MM::load(src[i]), a));
+ }
+ } else {
+ m64 ca = MM::load_alpha(const_alpha);
+ m64 cia = MM::negate(ca);
+ for (int i = 0; i < length; ++i) {
+ m64 d = MM::load(dest[i]);
+ m64 da = MM::byte_mul(MM::alpha(d), ca);
+ dest[i] = MM::store(MM::interpolate_pixel_255(
+ MM::load(src[i]), da, d, cia));
+ }
+ }
+ MM::end();
+}
+
+/*
+ result = d * sa
+ dest = d * sa * ca + d * cia
+ = d * (sa * ca + cia)
+*/
+template <class MM>
+static void QT_FASTCALL comp_func_solid_DestinationIn(uint *dest, int length, uint src, uint const_alpha)
+{
+ C_80; C_00;
+ m64 a = MM::alpha(MM::load(src));
+ if (const_alpha != 255) {
+ C_FF;
+ m64 ca = MM::load_alpha(const_alpha);
+ m64 cia = MM::negate(ca);
+ a = MM::byte_mul(a, ca);
+ a = MM::add(a, cia);
+ }
+ for (int i = 0; i < length; ++i)
+ dest[i] = MM::store(MM::byte_mul(MM::load(dest[i]), a));
+ MM::end();
+}
+
+template <class MM>
+static void QT_FASTCALL comp_func_DestinationIn(uint *dest, const uint *src, int length, uint const_alpha)
+{
+ C_FF; C_80; C_00;
+ if (const_alpha == 255) {
+ for (int i = 0; i < length; ++i) {
+ m64 a = MM::alpha(MM::load(src[i]));
+ dest[i] = MM::store(MM::byte_mul(MM::load(dest[i]), a));
+ }
+ } else {
+ m64 ca = MM::load_alpha(const_alpha);
+ m64 cia = MM::negate(ca);
+ for (int i = 0; i < length; ++i) {
+ m64 d = MM::load(dest[i]);
+ m64 a = MM::alpha(MM::load(src[i]));
+ a = MM::byte_mul(a, ca);
+ a = MM::add(a, cia);
+ dest[i] = MM::store(MM::byte_mul(d, a));
+ }
+ }
+ MM::end();
+}
+
+/*
+ result = s * dia
+ dest = s * dia * ca + d * cia
+*/
+template <class MM>
+static void QT_FASTCALL comp_func_solid_SourceOut(uint *dest, int length, uint src, uint const_alpha)
+{
+ C_FF; C_80; C_00;
+ m64 s = MM::load(src);
+ if (const_alpha == 255) {
+ for (int i = 0; i < length; ++i) {
+ m64 dia = MM::negate(MM::alpha(MM::load(dest[i])));
+ dest[i] = MM::store(MM::byte_mul(s, dia));
+ }
+ } else {
+ m64 ca = MM::load_alpha(const_alpha);
+ m64 cia = MM::negate(ca);
+ s = MM::byte_mul(s, ca);
+ for (int i = 0; i < length; ++i) {
+ m64 d = MM::load(dest[i]);
+ dest[i] = MM::store(MM::interpolate_pixel_255(s, MM::negate(MM::alpha(d)), d, cia));
+ }
+ }
+ MM::end();
+}
+
+template <class MM>
+static void QT_FASTCALL comp_func_SourceOut(uint *dest, const uint *src, int length, uint const_alpha)
+{
+ C_FF; C_80; C_00;
+ if (const_alpha == 255) {
+ for (int i = 0; i < length; ++i) {
+ m64 ia = MM::negate(MM::alpha(MM::load(dest[i])));
+ dest[i] = MM::store(MM::byte_mul(MM::load(src[i]), ia));
+ }
+ } else {
+ m64 ca = MM::load_alpha(const_alpha);
+ m64 cia = MM::negate(ca);
+ for (int i = 0; i < length; ++i) {
+ m64 d = MM::load(dest[i]);
+ m64 dia = MM::byte_mul(MM::negate(MM::alpha(d)), ca);
+ dest[i] = MM::store(MM::interpolate_pixel_255(MM::load(src[i]), dia, d, cia));
+ }
+ }
+ MM::end();
+}
+
+/*
+ result = d * sia
+ dest = d * sia * ca + d * cia
+ = d * (sia * ca + cia)
+*/
+template <class MM>
+static void QT_FASTCALL comp_func_solid_DestinationOut(uint *dest, int length, uint src, uint const_alpha)
+{
+ C_FF; C_80; C_00;
+ m64 a = MM::negate(MM::alpha(MM::load(src)));
+ if (const_alpha != 255) {
+ m64 ca = MM::load_alpha(const_alpha);
+ a = MM::byte_mul(a, ca);
+ a = MM::add(a, MM::negate(ca));
+ }
+ for (int i = 0; i < length; ++i)
+ dest[i] = MM::store(MM::byte_mul(MM::load(dest[i]), a));
+ MM::end();
+}
+
+template <class MM>
+static void QT_FASTCALL comp_func_DestinationOut(uint *dest, const uint *src, int length, uint const_alpha)
+{
+ C_FF; C_80; C_00;
+ if (const_alpha == 255) {
+ for (int i = 0; i < length; ++i) {
+ m64 a = MM::negate(MM::alpha(MM::load(src[i])));
+ dest[i] = MM::store(MM::byte_mul(MM::load(dest[i]), a));
+ }
+ } else {
+ m64 ca = MM::load_alpha(const_alpha);
+ m64 cia = MM::negate(ca);
+ for (int i = 0; i < length; ++i) {
+ m64 d = MM::load(dest[i]);
+ m64 a = MM::negate(MM::alpha(MM::load(src[i])));
+ a = MM::byte_mul(a, ca);
+ a = MM::add(a, cia);
+ dest[i] = MM::store(MM::byte_mul(d, a));
+ }
+ }
+ MM::end();
+}
+
+/*
+ result = s*da + d*sia
+ dest = s*da*ca + d*sia*ca + d *cia
+ = s*ca * da + d * (sia*ca + cia)
+ = s*ca * da + d * (1 - sa*ca)
+*/
+template <class MM>
+static void QT_FASTCALL comp_func_solid_SourceAtop(uint *dest, int length, uint src, uint const_alpha)
+{
+ C_FF; C_80; C_00;
+ m64 s = MM::load(src);
+ if (const_alpha != 255) {
+ m64 ca = MM::load_alpha(const_alpha);
+ s = MM::byte_mul(s, ca);
+ }
+ m64 a = MM::negate(MM::alpha(s));
+ for (int i = 0; i < length; ++i) {
+ m64 d = MM::load(dest[i]);
+ dest[i] = MM::store(MM::interpolate_pixel_255(s, MM::alpha(d), d, a));
+ }
+ MM::end();
+}
+
+template <class MM>
+static void QT_FASTCALL comp_func_SourceAtop(uint *dest, const uint *src, int length, uint const_alpha)
+{
+ C_FF; C_80; C_00;
+ if (const_alpha == 255) {
+ for (int i = 0; i < length; ++i) {
+ m64 s = MM::load(src[i]);
+ m64 d = MM::load(dest[i]);
+ dest[i] = MM::store(MM::interpolate_pixel_255(s, MM::alpha(d), d,
+ MM::negate(MM::alpha(s))));
+ }
+ } else {
+ m64 ca = MM::load_alpha(const_alpha);
+ for (int i = 0; i < length; ++i) {
+ m64 s = MM::load(src[i]);
+ s = MM::byte_mul(s, ca);
+ m64 d = MM::load(dest[i]);
+ dest[i] = MM::store(MM::interpolate_pixel_255(s, MM::alpha(d), d,
+ MM::negate(MM::alpha(s))));
+ }
+ }
+ MM::end();
+}
+
+/*
+ result = d*sa + s*dia
+ dest = d*sa*ca + s*dia*ca + d *cia
+ = s*ca * dia + d * (sa*ca + cia)
+*/
+template <class MM>
+static void QT_FASTCALL comp_func_solid_DestinationAtop(uint *dest, int length, uint src, uint const_alpha)
+{
+ C_FF; C_80; C_00;
+ m64 s = MM::load(src);
+ m64 a = MM::alpha(s);
+ if (const_alpha != 255) {
+ m64 ca = MM::load_alpha(const_alpha);
+ s = MM::byte_mul(s, ca);
+ a = MM::alpha(s);
+ a = MM::add(a, MM::negate(ca));
+ }
+ for (int i = 0; i < length; ++i) {
+ m64 d = MM::load(dest[i]);
+ dest[i] = MM::store(MM::interpolate_pixel_255(s, MM::negate(MM::alpha(d)), d, a));
+ }
+ MM::end();
+}
+
+template <class MM>
+static void QT_FASTCALL comp_func_DestinationAtop(uint *dest, const uint *src, int length, uint const_alpha)
+{
+ C_FF; C_80; C_00;
+ if (const_alpha == 255) {
+ for (int i = 0; i < length; ++i) {
+ m64 s = MM::load(src[i]);
+ m64 d = MM::load(dest[i]);
+ dest[i] = MM::store(MM::interpolate_pixel_255(d, MM::alpha(s), s,
+ MM::negate(MM::alpha(d))));
+ }
+ } else {
+ m64 ca = MM::load_alpha(const_alpha);
+ for (int i = 0; i < length; ++i) {
+ m64 s = MM::load(src[i]);
+ s = MM::byte_mul(s, ca);
+ m64 d = MM::load(dest[i]);
+ m64 a = MM::alpha(s);
+ a = MM::add(a, MM::negate(ca));
+ dest[i] = MM::store(MM::interpolate_pixel_255(s, MM::negate(MM::alpha(d)),
+ d, a));
+ }
+ }
+ MM::end();
+}
+
+/*
+ result = d*sia + s*dia
+ dest = d*sia*ca + s*dia*ca + d *cia
+ = s*ca * dia + d * (sia*ca + cia)
+ = s*ca * dia + d * (1 - sa*ca)
+*/
+template <class MM>
+static void QT_FASTCALL comp_func_solid_XOR(uint *dest, int length, uint src, uint const_alpha)
+{
+ C_FF; C_80; C_00;
+ m64 s = MM::load(src);
+ if (const_alpha != 255) {
+ m64 ca = MM::load_alpha(const_alpha);
+ s = MM::byte_mul(s, ca);
+ }
+ m64 a = MM::negate(MM::alpha(s));
+ for (int i = 0; i < length; ++i) {
+ m64 d = MM::load(dest[i]);
+ dest[i] = MM::store(MM::interpolate_pixel_255(s, MM::negate(MM::alpha(d)),
+ d, a));
+ }
+ MM::end();
+}
+
+template <class MM>
+static void QT_FASTCALL comp_func_XOR(uint *dest, const uint *src, int length, uint const_alpha)
+{
+ C_FF; C_80; C_00;
+ if (const_alpha == 255) {
+ for (int i = 0; i < length; ++i) {
+ m64 s = MM::load(src[i]);
+ m64 d = MM::load(dest[i]);
+ dest[i] = MM::store(MM::interpolate_pixel_255(s, MM::negate(MM::alpha(d)),
+ d, MM::negate(MM::alpha(s))));
+ }
+ } else {
+ m64 ca = MM::load_alpha(const_alpha);
+ for (int i = 0; i < length; ++i) {
+ m64 s = MM::load(src[i]);
+ s = MM::byte_mul(s, ca);
+ m64 d = MM::load(dest[i]);
+ dest[i] = MM::store(MM::interpolate_pixel_255(s, MM::negate(MM::alpha(d)),
+ d, MM::negate(MM::alpha(s))));
+ }
+ }
+ MM::end();
+}
+
+template <class MM>
+static void QT_FASTCALL rasterop_solid_SourceOrDestination(uint *dest,
+ int length,
+ uint color,
+ uint const_alpha)
+{
+ Q_UNUSED(const_alpha);
+
+ if ((quintptr)(dest) & 0x7) {
+ *dest++ |= color;
+ --length;
+ }
+
+ const int length64 = length / 2;
+ if (length64) {
+ __m64 *dst64 = reinterpret_cast<__m64*>(dest);
+ const __m64 color64 = _mm_set_pi32(color, color);
+
+ int n = (length64 + 3) / 4;
+ switch (length64 & 0x3) {
+ case 0: do { *dst64 = _mm_or_si64(*dst64, color64); ++dst64;
+ case 3: *dst64 = _mm_or_si64(*dst64, color64); ++dst64;
+ case 2: *dst64 = _mm_or_si64(*dst64, color64); ++dst64;
+ case 1: *dst64 = _mm_or_si64(*dst64, color64); ++dst64;
+ } while (--n > 0);
+ }
+ }
+
+ if (length & 0x1) {
+ dest[length - 1] |= color;
+ }
+
+ MM::end();
+}
+
+template <class MM>
+static void QT_FASTCALL rasterop_solid_SourceAndDestination(uint *dest,
+ int length,
+ uint color,
+ uint const_alpha)
+{
+ Q_UNUSED(const_alpha);
+
+ color |= 0xff000000;
+
+ if ((quintptr)(dest) & 0x7) { // align
+ *dest++ &= color;
+ --length;
+ }
+
+ const int length64 = length / 2;
+ if (length64) {
+ __m64 *dst64 = reinterpret_cast<__m64*>(dest);
+ const __m64 color64 = _mm_set_pi32(color, color);
+
+ int n = (length64 + 3) / 4;
+ switch (length64 & 0x3) {
+ case 0: do { *dst64 = _mm_and_si64(*dst64, color64); ++dst64;
+ case 3: *dst64 = _mm_and_si64(*dst64, color64); ++dst64;
+ case 2: *dst64 = _mm_and_si64(*dst64, color64); ++dst64;
+ case 1: *dst64 = _mm_and_si64(*dst64, color64); ++dst64;
+ } while (--n > 0);
+ }
+ }
+
+ if (length & 0x1) {
+ dest[length - 1] &= color;
+ }
+
+ MM::end();
+}
+
+template <class MM>
+static void QT_FASTCALL rasterop_solid_SourceXorDestination(uint *dest,
+ int length,
+ uint color,
+ uint const_alpha)
+{
+ Q_UNUSED(const_alpha);
+
+ color &= 0x00ffffff;
+
+ if ((quintptr)(dest) & 0x7) {
+ *dest++ ^= color;
+ --length;
+ }
+
+ const int length64 = length / 2;
+ if (length64) {
+ __m64 *dst64 = reinterpret_cast<__m64*>(dest);
+ const __m64 color64 = _mm_set_pi32(color, color);
+
+ int n = (length64 + 3) / 4;
+ switch (length64 & 0x3) {
+ case 0: do { *dst64 = _mm_xor_si64(*dst64, color64); ++dst64;
+ case 3: *dst64 = _mm_xor_si64(*dst64, color64); ++dst64;
+ case 2: *dst64 = _mm_xor_si64(*dst64, color64); ++dst64;
+ case 1: *dst64 = _mm_xor_si64(*dst64, color64); ++dst64;
+ } while (--n > 0);
+ }
+ }
+
+ if (length & 0x1) {
+ dest[length - 1] ^= color;
+ }
+
+ MM::end();
+}
+
+template <class MM>
+static void QT_FASTCALL rasterop_solid_SourceAndNotDestination(uint *dest,
+ int length,
+ uint color,
+ uint const_alpha)
+{
+
+ Q_UNUSED(const_alpha);
+
+ if ((quintptr)(dest) & 0x7) {
+ *dest = (color & ~(*dest)) | 0xff000000;
+ ++dest;
+ --length;
+ }
+
+ const int length64 = length / 2;
+ if (length64) {
+ __m64 *dst64 = reinterpret_cast<__m64*>(dest);
+ const __m64 color64 = _mm_set_pi32(color, color);
+ const m64 mmx_0xff000000 = _mm_set1_pi32(0xff000000);
+ __m64 tmp1, tmp2, tmp3, tmp4;
+
+ int n = (length64 + 3) / 4;
+ switch (length64 & 0x3) {
+ case 0: do { tmp1 = _mm_andnot_si64(*dst64, color64);
+ *dst64++ = _mm_or_si64(tmp1, mmx_0xff000000);
+ case 3: tmp2 = _mm_andnot_si64(*dst64, color64);
+ *dst64++ = _mm_or_si64(tmp2, mmx_0xff000000);
+ case 2: tmp3 = _mm_andnot_si64(*dst64, color64);
+ *dst64++ = _mm_or_si64(tmp3, mmx_0xff000000);
+ case 1: tmp4 = _mm_andnot_si64(*dst64, color64);
+ *dst64++ = _mm_or_si64(tmp4, mmx_0xff000000);
+ } while (--n > 0);
+ }
+ }
+
+ if (length & 0x1) {
+ dest[length - 1] = (color & ~(dest[length - 1])) | 0xff000000;
+ }
+
+ MM::end();
+}
+
+template <class MM>
+static void QT_FASTCALL rasterop_solid_NotSourceAndNotDestination(uint *dest,
+ int length,
+ uint color,
+ uint const_alpha)
+{
+ rasterop_solid_SourceAndNotDestination<MM>(dest, length,
+ ~color, const_alpha);
+}
+
+template <class MM>
+static void QT_FASTCALL rasterop_solid_NotSourceOrNotDestination(uint *dest,
+ int length,
+ uint color,
+ uint const_alpha)
+{
+ Q_UNUSED(const_alpha);
+ color = ~color | 0xff000000;
+ while (length--) {
+ *dest = color | ~(*dest);
+ ++dest;
+ }
+}
+
+template <class MM>
+static void QT_FASTCALL rasterop_solid_NotSourceXorDestination(uint *dest,
+ int length,
+ uint color,
+ uint const_alpha)
+{
+ rasterop_solid_SourceXorDestination<MM>(dest, length, ~color, const_alpha);
+}
+
+template <class MM>
+static void QT_FASTCALL rasterop_solid_NotSource(uint *dest, int length,
+ uint color, uint const_alpha)
+{
+ Q_UNUSED(const_alpha);
+ qt_memfill((quint32*)dest, ~color | 0xff000000, length);
+}
+
+template <class MM>
+static void QT_FASTCALL rasterop_solid_NotSourceAndDestination(uint *dest,
+ int length,
+ uint color,
+ uint const_alpha)
+{
+ rasterop_solid_SourceAndDestination<MM>(dest, length,
+ ~color, const_alpha);
+}
+
+template <class MM>
+static inline void qt_blend_color_argb_x86(int count, const QSpan *spans,
+ void *userData,
+ CompositionFunctionSolid *solidFunc)
+{
+ QSpanData *data = reinterpret_cast<QSpanData *>(userData);
+ if (data->rasterBuffer->compositionMode == QPainter::CompositionMode_Source
+ || (data->rasterBuffer->compositionMode == QPainter::CompositionMode_SourceOver
+ && qAlpha(data->solid.color) == 255)) {
+ // inline for performance
+ C_FF; C_80; C_00;
+ while (count--) {
+ uint *target = ((uint *)data->rasterBuffer->scanLine(spans->y)) + spans->x;
+ if (spans->coverage == 255) {
+ qt_memfill(static_cast<quint32*>(target), quint32(data->solid.color), spans->len);
+ } else {
+ // dest = s * ca + d * (1 - sa*ca) --> dest = s * ca + d * (1-ca)
+ m64 ca = MM::load_alpha(spans->coverage);
+ m64 s = MM::byte_mul(MM::load(data->solid.color), ca);
+ m64 ica = MM::negate(ca);
+ for (int i = 0; i < spans->len; ++i)
+ target[i] = MM::store(MM::add(s, MM::byte_mul(MM::load(target[i]), ica)));
+ }
+ ++spans;
+ }
+ MM::end();
+ return;
+ }
+ CompositionFunctionSolid func = solidFunc[data->rasterBuffer->compositionMode];
+ while (count--) {
+ uint *target = ((uint *)data->rasterBuffer->scanLine(spans->y)) + spans->x;
+ func(target, spans->len, data->solid.color, spans->coverage);
+ ++spans;
+ }
+}
+
+#ifdef QT_HAVE_MMX
+struct QMMXIntrinsics : public QMMXCommonIntrinsics
+{
+ static inline void end() {
+#if !defined(Q_OS_WINCE) || defined(_X86_)
+ _mm_empty();
+#endif
+ }
+};
+#endif // QT_HAVE_MMX
+
+QT_END_NAMESPACE
+
+#endif // QDRAWHELPER_MMX_P_H
diff --git a/src/gui/painting/qdrawhelper_p.h b/src/gui/painting/qdrawhelper_p.h
new file mode 100644
index 0000000000..de97683c29
--- /dev/null
+++ b/src/gui/painting/qdrawhelper_p.h
@@ -0,0 +1,1910 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QDRAWHELPER_P_H
+#define QDRAWHELPER_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 "QtCore/qglobal.h"
+#include "QtGui/qcolor.h"
+#include "QtGui/qpainter.h"
+#include "QtGui/qimage.h"
+#ifndef QT_FT_BEGIN_HEADER
+#define QT_FT_BEGIN_HEADER
+#define QT_FT_END_HEADER
+#endif
+#include "private/qrasterdefs_p.h"
+
+#ifdef Q_WS_QWS
+#include "QtGui/qscreen_qws.h"
+#endif
+
+// Disable MMX and SSE on Mac/PPC builds, or if the compiler
+// does not support -Xarch argument passing
+#if defined(QT_NO_MAC_XARCH) || (defined(Q_OS_DARWIN) && (defined(__ppc__) || defined(__ppc64__)))
+#undef QT_HAVE_SSE2
+#undef QT_HAVE_SSE
+#undef QT_HAVE_3DNOW
+#undef QT_HAVE_MMX
+#endif
+
+QT_BEGIN_NAMESPACE
+
+#if defined(Q_CC_MSVC) && _MSCVER <= 1300 && !defined(Q_CC_INTEL)
+#define Q_STATIC_TEMPLATE_SPECIALIZATION static
+#else
+#define Q_STATIC_TEMPLATE_SPECIALIZATION
+#endif
+
+#if defined(Q_CC_RVCT)
+// RVCT doesn't like static template functions
+# define Q_STATIC_TEMPLATE_FUNCTION
+# define Q_STATIC_INLINE_FUNCTION inline
+#else
+# define Q_STATIC_TEMPLATE_FUNCTION static
+# define Q_STATIC_INLINE_FUNCTION static inline
+#endif
+
+/*******************************************************************************
+ * QSpan
+ *
+ * duplicate definition of FT_Span
+ */
+typedef QT_FT_Span QSpan;
+
+struct QSolidData;
+struct QTextureData;
+struct QGradientData;
+struct QLinearGradientData;
+struct QRadialGradientData;
+struct QConicalGradientData;
+struct QSpanData;
+class QGradient;
+class QRasterBuffer;
+class QClipData;
+
+typedef QT_FT_SpanFunc ProcessSpans;
+typedef void (*BitmapBlitFunc)(QRasterBuffer *rasterBuffer,
+ int x, int y, quint32 color,
+ const uchar *bitmap,
+ int mapWidth, int mapHeight, int mapStride);
+
+typedef void (*AlphamapBlitFunc)(QRasterBuffer *rasterBuffer,
+ int x, int y, quint32 color,
+ const uchar *bitmap,
+ int mapWidth, int mapHeight, int mapStride,
+ const QClipData *clip);
+
+typedef void (*AlphaRGBBlitFunc)(QRasterBuffer *rasterBuffer,
+ int x, int y, quint32 color,
+ const uint *rgbmask,
+ int mapWidth, int mapHeight, int mapStride,
+ const QClipData *clip);
+
+typedef void (*RectFillFunc)(QRasterBuffer *rasterBuffer,
+ int x, int y, int width, int height,
+ quint32 color);
+
+typedef void (*SrcOverBlendFunc)(uchar *destPixels, int dbpl,
+ const uchar *src, int spbl,
+ int w, int h,
+ int const_alpha);
+
+typedef void (*SrcOverScaleFunc)(uchar *destPixels, int dbpl,
+ const uchar *src, int spbl,
+ const QRectF &targetRect,
+ const QRectF &sourceRect,
+ const QRect &clipRect,
+ int const_alpha);
+
+
+struct DrawHelper {
+ ProcessSpans blendColor;
+ ProcessSpans blendGradient;
+ BitmapBlitFunc bitmapBlit;
+ AlphamapBlitFunc alphamapBlit;
+ AlphaRGBBlitFunc alphaRGBBlit;
+ RectFillFunc fillRect;
+};
+
+extern SrcOverBlendFunc qBlendFunctions[QImage::NImageFormats][QImage::NImageFormats];
+extern SrcOverScaleFunc qScaleFunctions[QImage::NImageFormats][QImage::NImageFormats];
+
+extern DrawHelper qDrawHelper[QImage::NImageFormats];
+
+void qBlendTexture(int count, const QSpan *spans, void *userData);
+#if defined(Q_WS_QWS) && !defined(QT_NO_RASTERCALLBACKS)
+extern DrawHelper qDrawHelperCallback[QImage::NImageFormats];
+void qBlendTextureCallback(int count, const QSpan *spans, void *userData);
+#endif
+
+typedef void (QT_FASTCALL *CompositionFunction)(uint *dest, const uint *src, int length, uint const_alpha);
+typedef void (QT_FASTCALL *CompositionFunctionSolid)(uint *dest, int length, uint color, uint const_alpha);
+
+void qInitDrawhelperAsm();
+
+class QRasterPaintEngine;
+
+struct QSolidData
+{
+ uint color;
+};
+
+struct QLinearGradientData
+{
+ struct {
+ qreal x;
+ qreal y;
+ } origin;
+ struct {
+ qreal x;
+ qreal y;
+ } end;
+};
+
+struct QRadialGradientData
+{
+ struct {
+ qreal x;
+ qreal y;
+ } center;
+ struct {
+ qreal x;
+ qreal y;
+ } focal;
+ qreal radius;
+};
+
+struct QConicalGradientData
+{
+ struct {
+ qreal x;
+ qreal y;
+ } center;
+ qreal angle;
+};
+
+struct QGradientData
+{
+ QGradient::Spread spread;
+
+ union {
+ QLinearGradientData linear;
+ QRadialGradientData radial;
+ QConicalGradientData conical;
+ };
+
+#ifdef Q_WS_QWS
+#define GRADIENT_STOPTABLE_SIZE 256
+#else
+#define GRADIENT_STOPTABLE_SIZE 1024
+#endif
+
+ uint* colorTable; //[GRADIENT_STOPTABLE_SIZE];
+
+ uint alphaColor : 1;
+};
+
+struct QTextureData
+{
+ const uchar *imageData;
+ const uchar *scanLine(int y) const { return imageData + y*bytesPerLine; }
+
+ int width;
+ int height;
+ // clip rect
+ int x1;
+ int y1;
+ int x2;
+ int y2;
+ int bytesPerLine;
+ QImage::Format format;
+ const QVector<QRgb> *colorTable;
+ bool hasAlpha;
+ enum Type {
+ Plain,
+ Tiled
+ };
+ Type type;
+ int const_alpha;
+};
+
+struct QSpanData
+{
+ QSpanData() : tempImage(0) {}
+ ~QSpanData() { delete tempImage; }
+
+ QRasterBuffer *rasterBuffer;
+#ifdef Q_WS_QWS
+ QRasterPaintEngine *rasterEngine;
+#endif
+ ProcessSpans blend;
+ ProcessSpans unclipped_blend;
+ BitmapBlitFunc bitmapBlit;
+ AlphamapBlitFunc alphamapBlit;
+ AlphaRGBBlitFunc alphaRGBBlit;
+ RectFillFunc fillRect;
+ qreal m11, m12, m13, m21, m22, m23, m33, dx, dy; // inverse xform matrix
+ const QClipData *clip;
+ enum Type {
+ None,
+ Solid,
+ LinearGradient,
+ RadialGradient,
+ ConicalGradient,
+ Texture
+ } type : 8;
+ int txop : 8;
+ int fast_matrix : 1;
+ bool bilinear;
+ QImage *tempImage;
+ union {
+ QSolidData solid;
+ QGradientData gradient;
+ QTextureData texture;
+ };
+
+ void init(QRasterBuffer *rb, const QRasterPaintEngine *pe);
+ void setup(const QBrush &brush, int alpha);
+ void setupMatrix(const QTransform &matrix, int bilinear);
+ void initTexture(const QImage *image, int alpha, QTextureData::Type = QTextureData::Plain, const QRect &sourceRect = QRect());
+ void adjustSpanMethods();
+};
+
+
+static inline uint BYTE_MUL_RGB16(uint x, uint a) {
+ a += 1;
+ uint t = (((x & 0x07e0)*a) >> 8) & 0x07e0;
+ t |= (((x & 0xf81f)*(a>>2)) >> 6) & 0xf81f;
+ return t;
+}
+
+static inline uint BYTE_MUL_RGB16_32(uint x, uint a) {
+ uint t = (((x & 0xf81f07e0) >> 5)*a) & 0xf81f07e0;
+ t |= (((x & 0x07e0f81f)*a) >> 5) & 0x07e0f81f;
+ return t;
+}
+
+Q_STATIC_INLINE_FUNCTION uint BYTE_MUL(uint x, uint a) {
+ uint t = (x & 0xff00ff) * a;
+ t = (t + ((t >> 8) & 0xff00ff) + 0x800080) >> 8;
+ t &= 0xff00ff;
+
+ x = ((x >> 8) & 0xff00ff) * a;
+ x = (x + ((x >> 8) & 0xff00ff) + 0x800080);
+ x &= 0xff00ff00;
+ x |= t;
+ return x;
+}
+
+static inline uint PREMUL(uint x) {
+ uint a = x >> 24;
+ uint t = (x & 0xff00ff) * a;
+ t = (t + ((t >> 8) & 0xff00ff) + 0x800080) >> 8;
+ t &= 0xff00ff;
+
+ x = ((x >> 8) & 0xff) * a;
+ x = (x + ((x >> 8) & 0xff) + 0x80);
+ x &= 0xff00;
+ x |= t | (a << 24);
+ return x;
+}
+
+#define INV_PREMUL(p) \
+ (qAlpha(p) == 0 ? 0 : \
+ ((qAlpha(p) << 24) \
+ | (((255*qRed(p))/ qAlpha(p)) << 16) \
+ | (((255*qGreen(p)) / qAlpha(p)) << 8) \
+ | ((255*qBlue(p)) / qAlpha(p))))
+
+template <class DST, class SRC>
+inline DST qt_colorConvert(SRC color, DST dummy)
+{
+ Q_UNUSED(dummy);
+ return DST(color);
+}
+
+
+template <>
+inline quint32 qt_colorConvert(quint16 color, quint32 dummy)
+{
+ Q_UNUSED(dummy);
+ const int r = (color & 0xf800);
+ const int g = (color & 0x07e0);
+ const int b = (color & 0x001f);
+ const int tr = (r >> 8) | (r >> 13);
+ const int tg = (g >> 3) | (g >> 9);
+ const int tb = (b << 3) | (b >> 2);
+
+ return qRgb(tr, tg, tb);
+}
+
+template <>
+inline quint16 qt_colorConvert(quint32 color, quint16 dummy)
+{
+ Q_UNUSED(dummy);
+ const int r = qRed(color) << 8;
+ const int g = qGreen(color) << 3;
+ const int b = qBlue(color) >> 3;
+
+ return (r & 0xf800) | (g & 0x07e0)| (b & 0x001f);
+}
+
+class quint32p
+{
+public:
+ inline quint32p(quint32 v) : data(PREMUL(v)) {}
+
+ inline operator quint32() const { return data; }
+
+ inline operator quint16() const
+ {
+ return qt_colorConvert<quint16, quint32>(data, 0);
+ }
+
+ static inline quint32p fromRawData(quint32 v)
+ {
+ quint32p p;
+ p.data = v;
+ return p;
+ }
+
+private:
+ quint32p() {}
+ quint32 data;
+} Q_PACKED;
+
+class qabgr8888
+{
+public:
+ inline qabgr8888(quint32 v)
+ {
+ data = qRgba(qBlue(v), qGreen(v), qRed(v), qAlpha(v));
+ }
+
+ inline bool operator==(const qabgr8888 &v) const { return data == v.data; }
+
+private:
+ quint32 data;
+} Q_PACKED;
+
+class qrgb565;
+
+class qargb8565
+{
+public:
+ static inline bool hasAlpha() { return true; }
+
+ inline qargb8565() {}
+ inline qargb8565(quint32 v);
+ inline explicit qargb8565(quint32p v);
+ inline qargb8565(const qargb8565 &v);
+ inline qargb8565(const qrgb565 &v);
+
+ inline operator quint32() const;
+ inline operator quint16() const;
+
+ inline quint8 alpha() const { return data[0]; }
+ inline qargb8565 truncedAlpha() {
+ data[0] &= 0xf8;
+ data[1] &= 0xdf;
+ return *this;
+ }
+ static inline quint8 alpha(quint8 a) { return (a + 1) >> 3; }
+ static inline quint8 ialpha(quint8 a) { return 0x20 - alpha(a); }
+
+ inline qargb8565 byte_mul(quint8 a) const;
+ inline qargb8565 operator+(qargb8565 v) const;
+ inline bool operator==(const qargb8565 &v) const;
+
+ inline quint32 rawValue() const;
+
+private:
+ friend class qrgb565;
+
+ quint8 data[3];
+} Q_PACKED;
+
+class qrgb565
+{
+public:
+ static inline bool hasAlpha() { return false; }
+
+ qrgb565(int v = 0) : data(v) {}
+
+ inline explicit qrgb565(quint32p v);
+ inline explicit qrgb565(quint32 v);
+ inline explicit qrgb565(qargb8565 v);
+
+ inline operator quint32() const;
+ inline operator quint16() const;
+
+ inline qrgb565 operator+(qrgb565 v) const;
+
+ inline quint8 alpha() const { return 0xff; }
+ inline qrgb565 truncedAlpha() { return *this; }
+ static inline quint8 alpha(quint8 a) { return (a + 1) >> 3; }
+ static inline quint8 ialpha(quint8 a) { return 0x20 - alpha(a); }
+
+ inline qrgb565 byte_mul(quint8 a) const;
+
+ inline bool operator==(const qrgb565 &v) const;
+ inline quint16 rawValue() const { return data; }
+
+private:
+ friend class qargb8565;
+
+ quint16 data;
+} Q_PACKED;
+
+qargb8565::qargb8565(quint32 v)
+{
+ *this = qargb8565(quint32p(v));
+}
+
+qargb8565::qargb8565(quint32p v)
+{
+ data[0] = qAlpha(v);
+ const int r = qRed(v);
+ const int g = qGreen(v);
+ const int b = qBlue(v);
+ data[1] = ((g << 3) & 0xe0) | (b >> 3);
+ data[2] = (r & 0xf8) | (g >> 5);
+}
+
+qargb8565::qargb8565(const qargb8565 &v)
+{
+ data[0] = v.data[0];
+ data[1] = v.data[1];
+ data[2] = v.data[2];
+}
+
+qargb8565::qargb8565(const qrgb565 &v)
+{
+ data[0] = 0xff;
+ data[1] = v.data & 0xff;
+ data[2] = v.data >> 8;
+}
+
+qargb8565::operator quint32() const
+{
+ const quint16 rgb = (data[2] << 8) | data[1];
+ const int a = data[0];
+ const int r = (rgb & 0xf800);
+ const int g = (rgb & 0x07e0);
+ const int b = (rgb & 0x001f);
+ const int tr = qMin(a, (r >> 8) | (r >> 13));
+ const int tg = qMin(a, (g >> 3) | (g >> 9));
+ const int tb = qMin(a, (b << 3) | (b >> 2));
+ return qRgba(tr, tg, tb, data[0]);
+}
+
+qargb8565::operator quint16() const
+{
+ return (data[2] << 8) | data[1];
+}
+
+qargb8565 qargb8565::operator+(qargb8565 v) const
+{
+ qargb8565 t;
+ t.data[0] = data[0] + v.data[0];
+ const quint16 rgb = ((data[2] + v.data[2]) << 8)
+ + (data[1] + v.data[1]);
+ t.data[1] = rgb & 0xff;
+ t.data[2] = rgb >> 8;
+ return t;
+}
+
+qargb8565 qargb8565::byte_mul(quint8 a) const
+{
+ qargb8565 result;
+ result.data[0] = (data[0] * a) >> 5;
+
+ const quint16 x = (data[2] << 8) | data[1];
+ const quint16 t = ((((x & 0x07e0) >> 5) * a) & 0x07e0) |
+ ((((x & 0xf81f) * a) >> 5) & 0xf81f);
+ result.data[1] = t & 0xff;
+ result.data[2] = t >> 8;
+ return result;
+}
+
+bool qargb8565::operator==(const qargb8565 &v) const
+{
+ return data[0] == v.data[0]
+ && data[1] == v.data[1]
+ && data[2] == v.data[2];
+}
+
+quint32 qargb8565::rawValue() const
+{
+ return (data[2] << 16) | (data[1] << 8) | data[0];
+}
+
+qrgb565::qrgb565(quint32p v)
+{
+ *this = qrgb565(quint32(v));
+}
+
+qrgb565::qrgb565(quint32 v)
+{
+ const int r = qRed(v) << 8;
+ const int g = qGreen(v) << 3;
+ const int b = qBlue(v) >> 3;
+
+ data = (r & 0xf800) | (g & 0x07e0)| (b & 0x001f);
+}
+
+qrgb565::qrgb565(qargb8565 v)
+{
+ data = (v.data[2] << 8) | v.data[1];
+}
+
+qrgb565::operator quint32() const
+{
+ const int r = (data & 0xf800);
+ const int g = (data & 0x07e0);
+ const int b = (data & 0x001f);
+ const int tr = (r >> 8) | (r >> 13);
+ const int tg = (g >> 3) | (g >> 9);
+ const int tb = (b << 3) | (b >> 2);
+ return qRgb(tr, tg, tb);
+}
+
+qrgb565::operator quint16() const
+{
+ return data;
+}
+
+qrgb565 qrgb565::operator+(qrgb565 v) const
+{
+ qrgb565 t;
+ t.data = data + v.data;
+ return t;
+}
+
+qrgb565 qrgb565::byte_mul(quint8 a) const
+{
+ qrgb565 result;
+ result.data = ((((data & 0x07e0) >> 5) * a) & 0x07e0) |
+ ((((data & 0xf81f) * a) >> 5) & 0xf81f);
+ return result;
+}
+
+bool qrgb565::operator==(const qrgb565 &v) const
+{
+ return data == v.data;
+}
+
+class qbgr565
+{
+public:
+ inline qbgr565(quint16 v)
+ {
+ data = ((v & 0x001f) << 11) |
+ (v & 0x07e0) |
+ ((v & 0xf800) >> 11);
+ }
+
+ inline bool operator==(const qbgr565 &v) const
+ {
+ return data == v.data;
+ }
+
+private:
+ quint16 data;
+} Q_PACKED;
+
+class qrgb555;
+
+class qargb8555
+{
+public:
+ static inline bool hasAlpha() { return true; }
+
+ qargb8555() {}
+ inline qargb8555(quint32 v);
+ inline explicit qargb8555(quint32p v);
+ inline qargb8555(const qargb8555 &v);
+ inline qargb8555(const qrgb555 &v);
+
+ inline operator quint32() const;
+
+ inline quint8 alpha() const { return data[0]; }
+ inline qargb8555 truncedAlpha() { data[0] &= 0xf8; return *this; }
+ static inline quint8 alpha(quint8 a) { return (a + 1) >> 3; }
+ static inline quint8 ialpha(quint8 a) { return 0x20 - alpha(a); }
+
+ inline qargb8555 operator+(qargb8555 v) const;
+ inline qargb8555 byte_mul(quint8 a) const;
+
+ inline bool operator==(const qargb8555 &v) const;
+
+ inline quint32 rawValue() const;
+
+private:
+ friend class qrgb555;
+ quint8 data[3];
+} Q_PACKED;
+
+class qrgb555
+{
+public:
+ static inline bool hasAlpha() { return false; }
+
+ inline qrgb555(int v = 0) : data(v) {}
+
+ inline explicit qrgb555(quint32p v) { *this = qrgb555(quint32(v)); }
+
+ inline explicit qrgb555(quint32 v)
+ {
+ const int r = qRed(v) << 7;
+ const int g = qGreen(v) << 2;
+ const int b = qBlue(v) >> 3;
+
+ data = (r & 0x7c00) | (g & 0x03e0) | (b & 0x001f);
+ }
+
+ inline explicit qrgb555(quint16 v)
+ {
+ data = ((v >> 1) & (0x7c00 | 0x03e0)) |
+ (v & 0x001f);
+ }
+
+ inline explicit qrgb555(const qargb8555 &v);
+
+ inline operator quint32() const
+ {
+ const int r = (data & 0x7c00);
+ const int g = (data & 0x03e0);
+ const int b = (data & 0x001f);
+ const int tr = (r >> 7) | (r >> 12);
+ const int tg = (g >> 2) | (g >> 7);
+ const int tb = (b << 3) | (b >> 2);
+
+ return qRgb(tr, tg, tb);
+ }
+
+ inline operator quint16() const
+ {
+ const int r = ((data & 0x7c00) << 1) & 0xf800;
+ const int g = (((data & 0x03e0) << 1) | ((data >> 4) & 0x0020)) & 0x07e0;
+ const int b = (data & 0x001f);
+
+ return r | g | b;
+ }
+
+ inline qrgb555 operator+(qrgb555 v) const;
+ inline qrgb555 byte_mul(quint8 a) const;
+
+ inline quint8 alpha() const { return 0xff; }
+ inline qrgb555 truncedAlpha() { return *this; }
+ static inline quint8 alpha(quint8 a) { return (a + 1) >> 3; }
+ static inline quint8 ialpha(quint8 a) { return 0x20 - alpha(a); }
+
+ inline bool operator==(const qrgb555 &v) const { return v.data == data; }
+ inline bool operator!=(const qrgb555 &v) const { return v.data != data; }
+
+ inline quint16 rawValue() const { return data; }
+
+private:
+ friend class qargb8555;
+ friend class qbgr555;
+ quint16 data;
+
+} Q_PACKED;
+
+qrgb555::qrgb555(const qargb8555 &v)
+{
+ data = (v.data[2] << 8) | v.data[1];
+}
+
+qrgb555 qrgb555::operator+(qrgb555 v) const
+{
+ qrgb555 t;
+ t.data = data + v.data;
+ return t;
+}
+
+qrgb555 qrgb555::byte_mul(quint8 a) const
+{
+ quint16 t = (((data & 0x3e0) * a) >> 5) & 0x03e0;
+ t |= (((data & 0x7c1f) * a) >> 5) & 0x7c1f;
+
+ qrgb555 result;
+ result.data = t;
+ return result;
+}
+
+class qbgr555
+{
+public:
+ inline qbgr555(quint32 v) { *this = qbgr555(qrgb555(v)); }
+
+ inline qbgr555(qrgb555 v)
+ {
+ data = ((v.data & 0x001f) << 10) |
+ (v.data & 0x03e0) |
+ ((v.data & 0x7c00) >> 10);
+ }
+
+ inline bool operator==(const qbgr555 &v) const
+ {
+ return data == v.data;
+ }
+
+private:
+ quint16 data;
+} Q_PACKED;
+
+qargb8555::qargb8555(quint32 v)
+{
+ v = quint32p(v);
+ data[0] = qAlpha(v);
+ const int r = qRed(v);
+ const int g = qGreen(v);
+ const int b = qBlue(v);
+ data[1] = ((g << 2) & 0xe0) | (b >> 3);
+ data[2] = ((r >> 1) & 0x7c) | (g >> 6);
+
+}
+
+qargb8555::qargb8555(quint32p v)
+{
+ data[0] = qAlpha(v);
+ const int r = qRed(v);
+ const int g = qGreen(v);
+ const int b = qBlue(v);
+ data[1] = ((g << 2) & 0xe0) | (b >> 3);
+ data[2] = ((r >> 1) & 0x7c) | (g >> 6);
+}
+
+qargb8555::qargb8555(const qargb8555 &v)
+{
+ data[0] = v.data[0];
+ data[1] = v.data[1];
+ data[2] = v.data[2];
+}
+
+qargb8555::qargb8555(const qrgb555 &v)
+{
+ data[0] = 0xff;
+ data[1] = v.data & 0xff;
+ data[2] = v.data >> 8;
+}
+
+qargb8555::operator quint32() const
+{
+ const quint16 rgb = (data[2] << 8) | data[1];
+ const int r = (rgb & 0x7c00);
+ const int g = (rgb & 0x03e0);
+ const int b = (rgb & 0x001f);
+ const int tr = (r >> 7) | (r >> 12);
+ const int tg = (g >> 2) | (g >> 7);
+ const int tb = (b << 3) | (b >> 2);
+
+ return qRgba(tr, tg, tb, data[0]);
+}
+
+bool qargb8555::operator==(const qargb8555 &v) const
+{
+ return data[0] == v.data[0]
+ && data[1] == v.data[1]
+ && data[2] == v.data[2];
+}
+
+quint32 qargb8555::rawValue() const
+{
+ return (data[2] << 16) | (data[1] << 8) | data[0];
+}
+
+qargb8555 qargb8555::operator+(qargb8555 v) const
+{
+ qargb8555 t;
+ t.data[0] = data[0] + v.data[0];
+ const quint16 rgb = ((data[2] + v.data[2]) << 8)
+ + (data[1] + v.data[1]);
+ t.data[1] = rgb & 0xff;
+ t.data[2] = rgb >> 8;
+ return t;
+}
+
+qargb8555 qargb8555::byte_mul(quint8 a) const
+{
+ qargb8555 result;
+ result.data[0] = (data[0] * a) >> 5;
+
+ const quint16 x = (data[2] << 8) | data[1];
+ quint16 t = (((x & 0x3e0) * a) >> 5) & 0x03e0;
+ t |= (((x & 0x7c1f) * a) >> 5) & 0x7c1f;
+ result.data[1] = t & 0xff;
+ result.data[2] = t >> 8;
+ return result;
+
+}
+
+class qrgb666;
+
+class qargb6666
+{
+public:
+ static inline bool hasAlpha() { return true; }
+
+ inline qargb6666() {}
+ inline qargb6666(quint32 v) { *this = qargb6666(quint32p(v)); }
+ inline explicit qargb6666(quint32p v);
+ inline qargb6666(const qargb6666 &v);
+ inline qargb6666(const qrgb666 &v);
+
+ inline operator quint32 () const;
+
+ inline quint8 alpha() const;
+ inline qargb6666 truncedAlpha() { return *this; }
+ static inline quint8 alpha(quint8 a) { return (a + 1) >> 2; }
+ static inline quint8 ialpha(quint8 a) { return (255 - a + 1) >> 2; }
+
+ inline qargb6666 byte_mul(quint8 a) const;
+ inline qargb6666 operator+(qargb6666 v) const;
+ inline bool operator==(const qargb6666 &v) const;
+
+ inline quint32 rawValue() const;
+
+private:
+ friend class qrgb666;
+ quint8 data[3];
+
+} Q_PACKED;
+
+class qrgb666
+{
+public:
+ static inline bool hasAlpha() { return false; }
+
+ inline qrgb666() {}
+ inline qrgb666(quint32 v);
+ inline qrgb666(const qargb6666 &v);
+
+ inline operator quint32 () const;
+
+ inline quint8 alpha() const { return 0xff; }
+ inline qrgb666 truncedAlpha() { return *this; }
+ static inline quint8 alpha(quint8 a) { return (a + 1) >> 2; }
+ static inline quint8 ialpha(quint8 a) { return (255 - a + 1) >> 2; }
+
+ inline qrgb666 operator+(qrgb666 v) const;
+ inline qrgb666 byte_mul(quint8 a) const;
+
+ inline bool operator==(const qrgb666 &v) const;
+ inline bool operator!=(const qrgb666 &v) const { return !(*this == v); }
+
+ inline quint32 rawValue() const
+ {
+ return (data[2] << 16) | (data[1] << 8) | data[0];
+ }
+
+private:
+ friend class qargb6666;
+
+ quint8 data[3];
+} Q_PACKED;
+
+qrgb666::qrgb666(quint32 v)
+{
+ const uchar b = qBlue(v);
+ const uchar g = qGreen(v);
+ const uchar r = qRed(v);
+ const uint p = (b >> 2) | ((g >> 2) << 6) | ((r >> 2) << 12);
+ data[0] = qBlue(p);
+ data[1] = qGreen(p);
+ data[2] = qRed(p);
+}
+
+qrgb666::qrgb666(const qargb6666 &v)
+{
+ data[0] = v.data[0];
+ data[1] = v.data[1];
+ data[2] = v.data[2] & 0x03;
+}
+
+qrgb666::operator quint32 () const
+{
+ const uchar r = (data[2] << 6) | ((data[1] & 0xf0) >> 2) | (data[2] & 0x3);
+ const uchar g = (data[1] << 4) | ((data[0] & 0xc0) >> 4) | ((data[1] & 0x0f) >> 2);
+ const uchar b = (data[0] << 2) | ((data[0] & 0x3f) >> 4);
+ return qRgb(r, g, b);
+}
+
+qrgb666 qrgb666::operator+(qrgb666 v) const
+{
+ const quint32 x1 = (data[2] << 16) | (data[1] << 8) | data[0];
+ const quint32 x2 = (v.data[2] << 16) | (v.data[1] << 8) | v.data[0];
+ const quint32 t = x1 + x2;
+ qrgb666 r;
+ r.data[0] = t & 0xff;
+ r.data[1] = (t >> 8) & 0xff;
+ r.data[2] = (t >> 16) & 0xff;
+ return r;
+}
+
+qrgb666 qrgb666::byte_mul(quint8 a) const
+{
+ const quint32 x = (data[2] << 16) | (data[1] << 8) | data[0];
+ const quint32 t = ((((x & 0x03f03f) * a) >> 6) & 0x03f03f) |
+ ((((x & 0x000fc0) * a) >> 6) & 0x000fc0);
+
+ qrgb666 r;
+ r.data[0] = t & 0xff;
+ r.data[1] = (t >> 8) & 0xff;
+ r.data[2] = (t >> 16) & 0xff;
+ return r;
+}
+
+bool qrgb666::operator==(const qrgb666 &v) const
+{
+ return (data[0] == v.data[0] &&
+ data[1] == v.data[1] &&
+ data[2] == v.data[2]);
+}
+
+qargb6666::qargb6666(quint32p v)
+{
+ const quint8 b = qBlue(v) >> 2;
+ const quint8 g = qGreen(v) >> 2;
+ const quint8 r = qRed(v) >> 2;
+ const quint8 a = qAlpha(v) >> 2;
+ const uint p = (a << 18) | (r << 12) | (g << 6) | b;
+ data[0] = qBlue(p);
+ data[1] = qGreen(p);
+ data[2] = qRed(p);
+}
+
+qargb6666::qargb6666(const qargb6666 &v)
+{
+ data[0] = v.data[0];
+ data[1] = v.data[1];
+ data[2] = v.data[2];
+}
+
+qargb6666::qargb6666(const qrgb666 &v)
+{
+ data[0] = v.data[0];
+ data[1] = v.data[1];
+ data[2] = (v.data[2] | 0xfc);
+}
+
+qargb6666::operator quint32 () const
+{
+ const quint8 r = (data[2] << 6) | ((data[1] & 0xf0) >> 2) | (data[2] & 0x3);
+ const quint8 g = (data[1] << 4) | ((data[0] & 0xc0) >> 4) | ((data[1] & 0x0f) >> 2);
+ const quint8 b = (data[0] << 2) | ((data[0] & 0x3f) >> 4);
+ const quint8 a = (data[2] & 0xfc) | (data[2] >> 6);
+ return qRgba(r, g, b, a);
+}
+
+qargb6666 qargb6666::operator+(qargb6666 v) const
+{
+ const quint32 x1 = (data[2] << 16) | (data[1] << 8) | data[0];
+ const quint32 x2 = (v.data[2] << 16) | (v.data[1] << 8) | v.data[0];
+ const quint32 t = x1 + x2;
+ qargb6666 r;
+ r.data[0] = t & 0xff;
+ r.data[1] = (t >> 8) & 0xff;
+ r.data[2] = (t >> 16) & 0xff;
+ return r;
+}
+
+quint8 qargb6666::alpha() const
+{
+ return (data[2] & 0xfc) | (data[2] >> 6);
+}
+
+inline qargb6666 qargb6666::byte_mul(quint8 a) const
+{
+ const quint32 x = (data[2] << 16) | (data[1] << 8) | data[0];
+ const quint32 t = ((((x & 0x03f03f) * a) >> 6) & 0x03f03f) |
+ ((((x & 0xfc0fc0) * a) >> 6) & 0xfc0fc0);
+
+ qargb6666 r;
+ r.data[0] = t & 0xff;
+ r.data[1] = (t >> 8) & 0xff;
+ r.data[2] = (t >> 16) & 0xff;
+ return r;
+}
+
+bool qargb6666::operator==(const qargb6666 &v) const
+{
+ return data[0] == v.data[0]
+ && data[1] == v.data[1]
+ && data[2] == v.data[2];
+}
+
+quint32 qargb6666::rawValue() const
+{
+ return (data[2] << 16) | (data[1] << 8) | data[0];
+}
+
+class qrgb888
+{
+public:
+ static inline bool hasAlpha() { return false; }
+
+ inline qrgb888() {}
+ inline qrgb888(quint32 v);
+
+ inline operator quint32() const;
+
+ inline quint8 alpha() const { return 0xff; }
+ inline qrgb888 truncedAlpha() { return *this; }
+ static inline quint8 alpha(quint8 a) { return a; }
+ static inline quint8 ialpha(quint8 a) { return 255 - a; }
+
+ inline qrgb888 byte_mul(quint8 a) const;
+ inline qrgb888 operator+(qrgb888 v) const;
+ inline bool operator==(qrgb888 v) const;
+
+ inline quint32 rawValue() const;
+
+private:
+ uchar data[3];
+
+} Q_PACKED;
+
+qrgb888::qrgb888(quint32 v)
+{
+ data[0] = qRed(v);
+ data[1] = qGreen(v);
+ data[2] = qBlue(v);
+}
+
+qrgb888::operator quint32() const
+{
+ return qRgb(data[0], data[1], data[2]);
+}
+
+qrgb888 qrgb888::operator+(qrgb888 v) const
+{
+ qrgb888 t = *this;
+ t.data[0] += v.data[0];
+ t.data[1] += v.data[1];
+ t.data[2] += v.data[2];
+ return t;
+}
+
+qrgb888 qrgb888::byte_mul(quint8 a) const
+{
+ quint32 x(*this);
+
+ quint32 t = (x & 0xff00ff) * a;
+ t = (t + ((t >> 8) & 0xff00ff) + 0x800080) >> 8;
+ t &= 0xff00ff;
+
+ x = ((x >> 8) & 0xff00ff) * a;
+ x = (x + ((x >> 8) & 0xff00ff) + 0x800080);
+ x &= 0xff00ff00;
+ x |= t;
+ return qrgb888(x);
+}
+
+bool qrgb888::operator==(qrgb888 v) const
+{
+ return (data[0] == v.data[0] &&
+ data[1] == v.data[1] &&
+ data[2] == v.data[2]);
+}
+
+quint32 qrgb888::rawValue() const
+{
+ return (data[2] << 16) | (data[1] << 8) | data[0];
+}
+
+template <>
+inline qrgb888 qt_colorConvert(quint32 color, qrgb888 dummy)
+{
+ Q_UNUSED(dummy);
+ return qrgb888(color);
+}
+
+template <>
+inline quint32 qt_colorConvert(qrgb888 color, quint32 dummy)
+{
+ Q_UNUSED(dummy);
+ return quint32(color);
+}
+
+#ifdef QT_QWS_DEPTH_8
+template <>
+inline quint8 qt_colorConvert(quint32 color, quint8 dummy)
+{
+ Q_UNUSED(dummy);
+
+ uchar r = ((qRed(color) & 0xf8) + 0x19) / 0x33;
+ uchar g = ((qGreen(color) &0xf8) + 0x19) / 0x33;
+ uchar b = ((qBlue(color) &0xf8) + 0x19) / 0x33;
+
+ return r*6*6 + g*6 + b;
+}
+
+template <>
+inline quint8 qt_colorConvert(quint16 color, quint8 dummy)
+{
+ Q_UNUSED(dummy);
+
+ uchar r = (color & 0xf800) >> (11-3);
+ uchar g = (color & 0x07c0) >> (6-3);
+ uchar b = (color & 0x001f) << 3;
+
+ uchar tr = (r + 0x19) / 0x33;
+ uchar tg = (g + 0x19) / 0x33;
+ uchar tb = (b + 0x19) / 0x33;
+
+ return tr*6*6 + tg*6 + tb;
+}
+
+#endif // QT_QWS_DEPTH_8
+
+// hw: endianess??
+class quint24
+{
+public:
+ inline quint24(quint32 v)
+ {
+ data[0] = qBlue(v);
+ data[1] = qGreen(v);
+ data[2] = qRed(v);
+ }
+
+ inline operator quint32 ()
+ {
+ return qRgb(data[2], data[1], data[0]);
+ }
+
+ inline bool operator==(const quint24 &v) const
+ {
+ return data[0] == v.data[0]
+ && data[1] == v.data[1]
+ && data[2] == v.data[2];
+ }
+
+private:
+ uchar data[3];
+} Q_PACKED;
+
+template <>
+inline quint24 qt_colorConvert(quint32 color, quint24 dummy)
+{
+ Q_UNUSED(dummy);
+ return quint24(color);
+}
+
+// hw: endianess??
+class quint18
+{
+public:
+ inline quint18(quint32 v)
+ {
+ uchar b = qBlue(v);
+ uchar g = qGreen(v);
+ uchar r = qRed(v);
+ uint p = (b >> 2) | ((g >> 2) << 6) | ((r >> 2) << 12);
+ data[0] = qBlue(p);
+ data[1] = qGreen(p);
+ data[2] = qRed(p);
+ }
+
+ inline operator quint32 ()
+ {
+ const uchar r = (data[2] << 6) | ((data[1] & 0xf0) >> 2) | (data[2] & 0x3);
+ const uchar g = (data[1] << 4) | ((data[0] & 0xc0) >> 4) | ((data[1] & 0x0f) >> 2);
+ const uchar b = (data[0] << 2) | ((data[0] & 0x3f) >> 4);
+ return qRgb(r, g, b);
+ }
+
+private:
+ uchar data[3];
+} Q_PACKED;
+
+template <>
+inline quint18 qt_colorConvert(quint32 color, quint18 dummy)
+{
+ Q_UNUSED(dummy);
+ return quint18(color);
+}
+
+class qrgb444;
+
+class qargb4444
+{
+public:
+ static inline bool hasAlpha() { return true; }
+
+ inline qargb4444() {}
+ inline qargb4444(quint32 v) { *this = qargb4444(quint32p(v)); }
+ inline explicit qargb4444(quint32p v);
+ inline qargb4444(const qrgb444 &v);
+
+ inline operator quint32() const;
+ inline operator quint8() const;
+
+ inline qargb4444 operator+(qargb4444 v) const;
+
+ inline quint8 alpha() const { return ((data & 0xf000) >> 8) | ((data & 0xf000) >> 12); }
+ inline qargb4444 truncedAlpha() { return *this; }
+ static inline quint8 alpha(quint8 a) { return (a + 1) >> 4; }
+ static inline quint8 ialpha(quint8 a) { return 0x10 - alpha(a); }
+ inline qargb4444 byte_mul(quint8 a) const;
+
+ inline bool operator==(const qargb4444 &v) const { return data == v.data; }
+
+ inline quint16 rawValue() const { return data; }
+
+private:
+ friend class qrgb444;
+ quint16 data;
+
+} Q_PACKED;
+
+class qrgb444
+{
+public:
+ static inline bool hasAlpha() { return false; }
+
+ inline qrgb444() {}
+ inline qrgb444(quint32 v);
+ inline explicit qrgb444(qargb4444 v);
+
+ inline operator quint32() const;
+ inline operator quint8() const;
+
+ inline qrgb444 operator+(qrgb444 v) const;
+ inline quint8 alpha() const { return 0xff; }
+ inline qrgb444 truncedAlpha() { return *this; }
+ static inline quint8 alpha(quint8 a) { return (a + 1) >> 4; }
+ static inline quint8 ialpha(quint8 a) { return 0x10 - alpha(a); }
+ inline qrgb444 byte_mul(quint8 a) const;
+
+ inline bool operator==(const qrgb444 &v) const { return data == v.data; }
+ inline bool operator!=(const qrgb444 &v) const { return data != v.data; }
+
+ inline quint16 rawValue() const { return data; }
+
+private:
+ friend class qargb4444;
+ quint16 data;
+
+} Q_PACKED;
+
+
+qargb4444::qargb4444(quint32p color)
+{
+ quint32 v = color;
+ v &= 0xf0f0f0f0;
+ const int a = qAlpha(v) << 8;
+ const int r = qRed(v) << 4;
+ const int g = qGreen(v);
+ const int b = qBlue(v) >> 4;
+
+ data = a | r | g | b;
+}
+
+qargb4444::qargb4444(const qrgb444 &v)
+{
+ data = v.data | 0xf000;
+}
+
+qargb4444::operator quint32() const
+{
+ const int a = (data & 0xf000);
+ const int r = (data & 0x0f00);
+ const int g = (data & 0x00f0);
+ const int b = (data & 0x000f);
+ const int ta = (a >> 8) | (a >> 12);
+ const int tr = (r >> 4) | (r >> 8);
+ const int tg = g | (g >> 4);
+ const int tb = (b << 4) | b;
+
+ return qRgba(tr, tg, tb, ta);
+}
+
+qargb4444::operator quint8() const
+{
+ // hw: optimize!
+ return qt_colorConvert<quint8, quint32>(operator quint32(), 0);
+}
+
+qargb4444 qargb4444::operator+(qargb4444 v) const
+{
+ qargb4444 t;
+ t.data = data + v.data;
+ return t;
+}
+
+qargb4444 qargb4444::byte_mul(quint8 a) const
+{
+ quint16 t = (((data & 0xf0f0) * a) >> 4) & 0xf0f0;
+ t |= (((data & 0x0f0f) * a) >> 4) & 0x0f0f;
+
+ qargb4444 result;
+ result.data = t;
+ return result;
+}
+
+qrgb444::qrgb444(quint32 v)
+{
+ v &= 0xf0f0f0f0;
+ const int r = qRed(v) << 4;
+ const int g = qGreen(v);
+ const int b = qBlue(v) >> 4;
+
+ data = r | g | b;
+}
+
+qrgb444::qrgb444(qargb4444 v)
+{
+ data = v.data & 0x0fff;
+}
+
+qrgb444::operator quint32() const
+{
+ const int r = (data & 0x0f00);
+ const int g = (data & 0x00f0);
+ const int b = (data & 0x000f);
+ const int tr = (r >> 4) | (r >> 8);
+ const int tg = g | (g >> 4);
+ const int tb = (b << 4) | b;
+
+ return qRgb(tr, tg, tb);
+}
+
+qrgb444::operator quint8() const
+{
+ // hw: optimize!
+ return qt_colorConvert<quint8, quint32>(operator quint32(), 0);
+}
+
+qrgb444 qrgb444::operator+(qrgb444 v) const
+{
+ qrgb444 t;
+ t.data = data + v.data;
+ return t;
+}
+
+qrgb444 qrgb444::byte_mul(quint8 a) const
+{
+ quint16 t = (((data & 0xf0f0) * a) >> 4) & 0xf0f0;
+ t |= (((data & 0x0f0f) * a) >> 4) & 0x0f0f;
+
+ qrgb444 result;
+ result.data = t;
+ return result;
+}
+
+#ifdef QT_QWS_DEPTH_GENERIC
+
+struct qrgb
+{
+public:
+ static int bpp;
+ static int len_red;
+ static int len_green;
+ static int len_blue;
+ static int len_alpha;
+ static int off_red;
+ static int off_green;
+ static int off_blue;
+ static int off_alpha;
+} Q_PACKED;
+
+template <typename SRC>
+Q_STATIC_TEMPLATE_FUNCTION inline quint32 qt_convertToRgb(SRC color);
+
+template <>
+inline quint32 qt_convertToRgb(quint32 color)
+{
+ const int r = qRed(color) >> (8 - qrgb::len_red);
+ const int g = qGreen(color) >> (8 - qrgb::len_green);
+ const int b = qBlue(color) >> (8 - qrgb::len_blue);
+ const int a = qAlpha(color) >> (8 - qrgb::len_alpha);
+ const quint32 v = (r << qrgb::off_red)
+ | (g << qrgb::off_green)
+ | (b << qrgb::off_blue)
+ | (a << qrgb::off_alpha);
+
+ return v;
+}
+
+template <>
+inline quint32 qt_convertToRgb(quint16 color)
+{
+ return qt_convertToRgb(qt_colorConvert<quint32, quint16>(color, 0));
+}
+
+class qrgb_generic16
+{
+public:
+ inline qrgb_generic16(quint32 color)
+ {
+ const int r = qRed(color) >> (8 - qrgb::len_red);
+ const int g = qGreen(color) >> (8 - qrgb::len_green);
+ const int b = qBlue(color) >> (8 - qrgb::len_blue);
+ const int a = qAlpha(color) >> (8 - qrgb::len_alpha);
+ data = (r << qrgb::off_red)
+ | (g << qrgb::off_green)
+ | (b << qrgb::off_blue)
+ | (a << qrgb::off_alpha);
+ }
+
+ inline operator quint16 () { return data; }
+ inline quint32 operator<<(int shift) const { return data << shift; }
+
+private:
+ quint16 data;
+} Q_PACKED;
+
+template <>
+inline qrgb_generic16 qt_colorConvert(quint32 color, qrgb_generic16 dummy)
+{
+ Q_UNUSED(dummy);
+ return qrgb_generic16(color);
+}
+
+template <>
+inline qrgb_generic16 qt_colorConvert(quint16 color, qrgb_generic16 dummy)
+{
+ Q_UNUSED(dummy);
+ return qrgb_generic16(qt_colorConvert<quint32, quint16>(color, 0));
+}
+
+#endif // QT_QWS_DEPTH_GENERIC
+
+template <class T>
+void qt_memfill(T *dest, T value, int count);
+
+template<> inline void qt_memfill(quint32 *dest, quint32 color, int count)
+{
+ extern void (*qt_memfill32)(quint32 *dest, quint32 value, int count);
+ qt_memfill32(dest, color, count);
+}
+
+template<> inline void qt_memfill(quint16 *dest, quint16 color, int count)
+{
+ extern void (*qt_memfill16)(quint16 *dest, quint16 value, int count);
+ qt_memfill16(dest, color, count);
+}
+
+template<> inline void qt_memfill(quint8 *dest, quint8 color, int count)
+{
+ memset(dest, color, count);
+}
+
+template <class T>
+inline void qt_memfill(T *dest, T value, int count)
+{
+ int n = (count + 7) / 8;
+ switch (count & 0x07)
+ {
+ case 0: do { *dest++ = value;
+ case 7: *dest++ = value;
+ case 6: *dest++ = value;
+ case 5: *dest++ = value;
+ case 4: *dest++ = value;
+ case 3: *dest++ = value;
+ case 2: *dest++ = value;
+ case 1: *dest++ = value;
+ } while (--n > 0);
+ }
+}
+
+template <class T>
+inline void qt_rectfill(T *dest, T value,
+ int x, int y, int width, int height, int stride)
+{
+ char *d = reinterpret_cast<char*>(dest + x) + y * stride;
+ if (uint(stride) == (width * sizeof(T))) {
+ qt_memfill(reinterpret_cast<T*>(d), value, width * height);
+ } else {
+ for (int j = 0; j < height; ++j) {
+ dest = reinterpret_cast<T*>(d);
+ qt_memfill(dest, value, width);
+ d += stride;
+ }
+ }
+}
+
+template <class DST, class SRC>
+inline void qt_memconvert(DST *dest, const SRC *src, int count)
+{
+ if (sizeof(DST) == 1) {
+ while (count) {
+ int n = 1;
+ const SRC color = *src++;
+ const DST dstColor = qt_colorConvert<DST, SRC>(color, 0);
+ while (--count && (*src == color || dstColor == qt_colorConvert<DST, SRC>(*src, 0))) {
+ ++n;
+ ++src;
+ }
+ qt_memfill(dest, dstColor, n);
+ dest += n;
+ }
+ } else {
+ /* Duff's device */
+ int n = (count + 7) / 8;
+ switch (count & 0x07)
+ {
+ case 0: do { *dest++ = qt_colorConvert<DST, SRC>(*src++, 0);
+ case 7: *dest++ = qt_colorConvert<DST, SRC>(*src++, 0);
+ case 6: *dest++ = qt_colorConvert<DST, SRC>(*src++, 0);
+ case 5: *dest++ = qt_colorConvert<DST, SRC>(*src++, 0);
+ case 4: *dest++ = qt_colorConvert<DST, SRC>(*src++, 0);
+ case 3: *dest++ = qt_colorConvert<DST, SRC>(*src++, 0);
+ case 2: *dest++ = qt_colorConvert<DST, SRC>(*src++, 0);
+ case 1: *dest++ = qt_colorConvert<DST, SRC>(*src++, 0);
+ } while (--n > 0);
+ }
+ }
+}
+
+#define QT_TRIVIAL_MEMCONVERT_IMPL(T) \
+ template <> \
+ inline void qt_memconvert(T *dest, const T *src, int count) \
+ { \
+ memcpy(dest, src, count * sizeof(T)); \
+ }
+QT_TRIVIAL_MEMCONVERT_IMPL(quint32)
+QT_TRIVIAL_MEMCONVERT_IMPL(qrgb888)
+QT_TRIVIAL_MEMCONVERT_IMPL(qargb6666)
+QT_TRIVIAL_MEMCONVERT_IMPL(qrgb666)
+QT_TRIVIAL_MEMCONVERT_IMPL(quint16)
+#ifdef Q_WS_QWS
+QT_TRIVIAL_MEMCONVERT_IMPL(qrgb565)
+#endif
+QT_TRIVIAL_MEMCONVERT_IMPL(qargb8565)
+QT_TRIVIAL_MEMCONVERT_IMPL(qargb8555)
+QT_TRIVIAL_MEMCONVERT_IMPL(qrgb555)
+QT_TRIVIAL_MEMCONVERT_IMPL(qargb4444)
+QT_TRIVIAL_MEMCONVERT_IMPL(qrgb444)
+#undef QT_TRIVIAL_MEMCONVERT_IMPL
+
+#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
+template <>
+inline void qt_memconvert(qrgb666 *dest, const quint32 *src, int count)
+{
+ if (count < 3) {
+ switch (count) {
+ case 2: *dest++ = qrgb666(*src++);
+ case 1: *dest = qrgb666(*src);
+ }
+ return;
+ }
+
+ const int align = (long(dest) & 3);
+ switch (align) {
+ case 1: *dest++ = qrgb666(*src++); --count;
+ case 2: *dest++ = qrgb666(*src++); --count;
+ case 3: *dest++ = qrgb666(*src++); --count;
+ }
+
+ quint32 *dest32 = reinterpret_cast<quint32*>(dest);
+ int sourceCount = count >> 2;
+ while (sourceCount--) {
+ dest32[0] = ((src[1] & 0x00000c00) << 20)
+ | ((src[1] & 0x000000fc) << 22)
+ | ((src[0] & 0x00fc0000) >> 6)
+ | ((src[0] & 0x0000fc00) >> 4)
+ | ((src[0] & 0x000000fc) >> 2);
+ dest32[1] = ((src[2] & 0x003c0000) << 10)
+ | ((src[2] & 0x0000fc00) << 12)
+ | ((src[2] & 0x000000fc) << 14)
+ | ((src[1] & 0x00fc0000) >> 14)
+ | ((src[1] & 0x0000f000) >> 12);
+ dest32[2] = ((src[3] & 0x00fc0000) << 2)
+ | ((src[3] & 0x0000fc00) << 4)
+ | ((src[3] & 0x000000fc) << 6)
+ | ((src[2] & 0x00c00000) >> 22);
+ dest32 += 3;
+ src += 4;
+ }
+
+ dest = reinterpret_cast<qrgb666*>(dest32);
+ switch (count & 3) {
+ case 3: *dest++ = qrgb666(*src++);
+ case 2: *dest++ = qrgb666(*src++);
+ case 1: *dest = qrgb666(*src);
+ }
+}
+#endif // Q_BYTE_ORDER
+
+template <class T>
+inline void qt_rectcopy(T *dest, const T *src,
+ int x, int y, int width, int height,
+ int dstStride, int srcStride)
+{
+ char *d = (char*)(dest + x) + y * dstStride;
+ const char *s = (char*)(src);
+ for (int i = 0; i < height; ++i) {
+ ::memcpy(d, s, width * sizeof(T));
+ d += dstStride;
+ s += srcStride;
+ }
+}
+
+template <class DST, class SRC>
+inline void qt_rectconvert(DST *dest, const SRC *src,
+ int x, int y, int width, int height,
+ int dstStride, int srcStride)
+{
+ char *d = (char*)(dest + x) + y * dstStride;
+ const char *s = (char*)(src);
+ for (int i = 0; i < height; ++i) {
+ qt_memconvert<DST,SRC>((DST*)d, (const SRC*)s, width);
+ d += dstStride;
+ s += srcStride;
+ }
+}
+
+#define QT_RECTCONVERT_TRIVIAL_IMPL(T) \
+ template <> \
+ inline void qt_rectconvert(T *dest, const T *src, \
+ int x, int y, int width, int height, \
+ int dstStride, int srcStride) \
+ { \
+ qt_rectcopy(dest, src, x, y, width, height, dstStride, srcStride); \
+ }
+QT_RECTCONVERT_TRIVIAL_IMPL(quint32)
+QT_RECTCONVERT_TRIVIAL_IMPL(qrgb888)
+QT_RECTCONVERT_TRIVIAL_IMPL(qargb6666)
+QT_RECTCONVERT_TRIVIAL_IMPL(qrgb666)
+#ifdef Q_WS_QWS
+QT_RECTCONVERT_TRIVIAL_IMPL(qrgb565)
+#endif
+QT_RECTCONVERT_TRIVIAL_IMPL(qargb8565)
+QT_RECTCONVERT_TRIVIAL_IMPL(quint16)
+QT_RECTCONVERT_TRIVIAL_IMPL(qargb8555)
+QT_RECTCONVERT_TRIVIAL_IMPL(qrgb555)
+QT_RECTCONVERT_TRIVIAL_IMPL(qargb4444)
+QT_RECTCONVERT_TRIVIAL_IMPL(qrgb444)
+#undef QT_RECTCONVERT_TRIVIAL_IMPL
+
+#ifdef QT_QWS_DEPTH_GENERIC
+template <> void qt_rectconvert(qrgb *dest, const quint32 *src,
+ int x, int y, int width, int height,
+ int dstStride, int srcStride);
+
+template <> void qt_rectconvert(qrgb *dest, const quint16 *src,
+ int x, int y, int width, int height,
+ int dstStride, int srcStride);
+#endif // QT_QWS_DEPTH_GENERIC
+
+#define QT_MEMFILL_UINT(dest, length, color) \
+ qt_memfill<quint32>(dest, color, length);
+
+#define QT_MEMFILL_USHORT(dest, length, color) \
+ qt_memfill<quint16>(dest, color, length);
+
+#define QT_MEMCPY_REV_UINT(dest, src, length) \
+do { \
+ /* Duff's device */ \
+ uint *_d = (uint*)(dest) + length; \
+ const uint *_s = (uint*)(src) + length; \
+ register int n = ((length) + 7) / 8; \
+ switch ((length) & 0x07) \
+ { \
+ case 0: do { *--_d = *--_s; \
+ case 7: *--_d = *--_s; \
+ case 6: *--_d = *--_s; \
+ case 5: *--_d = *--_s; \
+ case 4: *--_d = *--_s; \
+ case 3: *--_d = *--_s; \
+ case 2: *--_d = *--_s; \
+ case 1: *--_d = *--_s; \
+ } while (--n > 0); \
+ } \
+} while (0)
+
+#define QT_MEMCPY_USHORT(dest, src, length) \
+do { \
+ /* Duff's device */ \
+ ushort *_d = (ushort*)(dest); \
+ const ushort *_s = (ushort*)(src); \
+ register int n = ((length) + 7) / 8; \
+ switch ((length) & 0x07) \
+ { \
+ case 0: do { *_d++ = *_s++; \
+ case 7: *_d++ = *_s++; \
+ case 6: *_d++ = *_s++; \
+ case 5: *_d++ = *_s++; \
+ case 4: *_d++ = *_s++; \
+ case 3: *_d++ = *_s++; \
+ case 2: *_d++ = *_s++; \
+ case 1: *_d++ = *_s++; \
+ } while (--n > 0); \
+ } \
+} while (0)
+
+Q_STATIC_INLINE_FUNCTION int qt_div_255(int x) { return (x + (x>>8) + 0x80) >> 8; }
+
+inline ushort qConvertRgb32To16(uint c)
+{
+ return (((c) >> 3) & 0x001f)
+ | (((c) >> 5) & 0x07e0)
+ | (((c) >> 8) & 0xf800);
+}
+
+#if defined(Q_WS_QWS) || (QT_VERSION >= 0x040400)
+inline quint32 qConvertRgb32To16x2(quint64 c)
+{
+ c = (((c) >> 3) & Q_UINT64_C(0x001f0000001f))
+ | (((c) >> 5) & Q_UINT64_C(0x07e0000007e0))
+ | (((c) >> 8) & Q_UINT64_C(0xf8000000f800));
+ return c | (c >> 16);
+}
+#endif
+
+inline QRgb qConvertRgb16To32(uint c)
+{
+ return 0xff000000
+ | ((((c) << 3) & 0xf8) | (((c) >> 2) & 0x7))
+ | ((((c) << 5) & 0xfc00) | (((c) >> 1) & 0x300))
+ | ((((c) << 8) & 0xf80000) | (((c) << 3) & 0x70000));
+}
+
+inline int qRed565(quint16 rgb) {
+ const int r = (rgb & 0xf800);
+ return (r >> 8) | (r >> 13);
+}
+
+inline int qGreen565(quint16 rgb) {
+ const int g = (rgb & 0x07e0);
+ return (g >> 3) | (g >> 9);
+}
+
+inline int qBlue565(quint16 rgb) {
+ const int b = (rgb & 0x001f);
+ return (b << 3) | (b >> 2);
+}
+
+#if 1
+static inline uint INTERPOLATE_PIXEL_256(uint x, uint a, uint y, uint b) {
+ uint t = (x & 0xff00ff) * a + (y & 0xff00ff) * b;
+ t >>= 8;
+ t &= 0xff00ff;
+
+ x = ((x >> 8) & 0xff00ff) * a + ((y >> 8) & 0xff00ff) * b;
+ x &= 0xff00ff00;
+ x |= t;
+ return x;
+}
+
+static inline uint INTERPOLATE_PIXEL_255(uint x, uint a, uint y, uint b) {
+ uint t = (x & 0xff00ff) * a + (y & 0xff00ff) * b;
+ t = (t + ((t >> 8) & 0xff00ff) + 0x800080) >> 8;
+ t &= 0xff00ff;
+
+ x = ((x >> 8) & 0xff00ff) * a + ((y >> 8) & 0xff00ff) * b;
+ x = (x + ((x >> 8) & 0xff00ff) + 0x800080);
+ x &= 0xff00ff00;
+ x |= t;
+ return x;
+}
+#else
+// possible implementation for 64 bit
+static inline uint INTERPOLATE_PIXEL_256(uint x, uint a, uint y, uint b) {
+ ulong t = (((ulong(x)) | ((ulong(x)) << 24)) & 0x00ff00ff00ff00ff) * a;
+ t += (((ulong(y)) | ((ulong(y)) << 24)) & 0x00ff00ff00ff00ff) * b;
+ t >>= 8;
+ t &= 0x00ff00ff00ff00ff;
+ return (uint(t)) | (uint(t >> 24));
+}
+
+static inline uint INTERPOLATE_PIXEL_255(uint x, uint a, uint y, uint b) {
+ ulong t = (((ulong(x)) | ((ulong(x)) << 24)) & 0x00ff00ff00ff00ff) * a;
+ t += (((ulong(y)) | ((ulong(y)) << 24)) & 0x00ff00ff00ff00ff) * b;
+ t = (t + ((t >> 8) & 0xff00ff00ff00ff) + 0x80008000800080);
+ t &= 0x00ff00ff00ff00ff;
+ return (uint(t)) | (uint(t >> 24));
+}
+
+Q_STATIC_INLINE_FUNCTION uint BYTE_MUL(uint x, uint a) {
+ ulong t = (((ulong(x)) | ((ulong(x)) << 24)) & 0x00ff00ff00ff00ff) * a;
+ t = (t + ((t >> 8) & 0xff00ff00ff00ff) + 0x80008000800080);
+ t &= 0x00ff00ff00ff00ff;
+ return (uint(t)) | (uint(t >> 24));
+}
+
+static inline uint PREMUL(uint x) {
+ uint a = x >> 24;
+ ulong t = (((ulong(x)) | ((ulong(x)) << 24)) & 0x00ff00ff00ff00ff) * a;
+ t = (t + ((t >> 8) & 0xff00ff00ff00ff) + 0x80008000800080);
+ t &= 0x00ff00ff00ff00ff;
+ return (uint(t)) | (uint(t >> 24)) | 0xff000000;
+}
+#endif
+
+const uint qt_bayer_matrix[16][16] = {
+ { 0x1, 0xc0, 0x30, 0xf0, 0xc, 0xcc, 0x3c, 0xfc,
+ 0x3, 0xc3, 0x33, 0xf3, 0xf, 0xcf, 0x3f, 0xff},
+ { 0x80, 0x40, 0xb0, 0x70, 0x8c, 0x4c, 0xbc, 0x7c,
+ 0x83, 0x43, 0xb3, 0x73, 0x8f, 0x4f, 0xbf, 0x7f},
+ { 0x20, 0xe0, 0x10, 0xd0, 0x2c, 0xec, 0x1c, 0xdc,
+ 0x23, 0xe3, 0x13, 0xd3, 0x2f, 0xef, 0x1f, 0xdf},
+ { 0xa0, 0x60, 0x90, 0x50, 0xac, 0x6c, 0x9c, 0x5c,
+ 0xa3, 0x63, 0x93, 0x53, 0xaf, 0x6f, 0x9f, 0x5f},
+ { 0x8, 0xc8, 0x38, 0xf8, 0x4, 0xc4, 0x34, 0xf4,
+ 0xb, 0xcb, 0x3b, 0xfb, 0x7, 0xc7, 0x37, 0xf7},
+ { 0x88, 0x48, 0xb8, 0x78, 0x84, 0x44, 0xb4, 0x74,
+ 0x8b, 0x4b, 0xbb, 0x7b, 0x87, 0x47, 0xb7, 0x77},
+ { 0x28, 0xe8, 0x18, 0xd8, 0x24, 0xe4, 0x14, 0xd4,
+ 0x2b, 0xeb, 0x1b, 0xdb, 0x27, 0xe7, 0x17, 0xd7},
+ { 0xa8, 0x68, 0x98, 0x58, 0xa4, 0x64, 0x94, 0x54,
+ 0xab, 0x6b, 0x9b, 0x5b, 0xa7, 0x67, 0x97, 0x57},
+ { 0x2, 0xc2, 0x32, 0xf2, 0xe, 0xce, 0x3e, 0xfe,
+ 0x1, 0xc1, 0x31, 0xf1, 0xd, 0xcd, 0x3d, 0xfd},
+ { 0x82, 0x42, 0xb2, 0x72, 0x8e, 0x4e, 0xbe, 0x7e,
+ 0x81, 0x41, 0xb1, 0x71, 0x8d, 0x4d, 0xbd, 0x7d},
+ { 0x22, 0xe2, 0x12, 0xd2, 0x2e, 0xee, 0x1e, 0xde,
+ 0x21, 0xe1, 0x11, 0xd1, 0x2d, 0xed, 0x1d, 0xdd},
+ { 0xa2, 0x62, 0x92, 0x52, 0xae, 0x6e, 0x9e, 0x5e,
+ 0xa1, 0x61, 0x91, 0x51, 0xad, 0x6d, 0x9d, 0x5d},
+ { 0xa, 0xca, 0x3a, 0xfa, 0x6, 0xc6, 0x36, 0xf6,
+ 0x9, 0xc9, 0x39, 0xf9, 0x5, 0xc5, 0x35, 0xf5},
+ { 0x8a, 0x4a, 0xba, 0x7a, 0x86, 0x46, 0xb6, 0x76,
+ 0x89, 0x49, 0xb9, 0x79, 0x85, 0x45, 0xb5, 0x75},
+ { 0x2a, 0xea, 0x1a, 0xda, 0x26, 0xe6, 0x16, 0xd6,
+ 0x29, 0xe9, 0x19, 0xd9, 0x25, 0xe5, 0x15, 0xd5},
+ { 0xaa, 0x6a, 0x9a, 0x5a, 0xa6, 0x66, 0x96, 0x56,
+ 0xa9, 0x69, 0x99, 0x59, 0xa5, 0x65, 0x95, 0x55}
+};
+
+#define ARGB_COMBINE_ALPHA(argb, alpha) \
+ ((((argb >> 24) * alpha) >> 8) << 24) | (argb & 0x00ffffff)
+
+
+QT_END_NAMESPACE
+
+#endif // QDRAWHELPER_P_H
diff --git a/src/gui/painting/qdrawhelper_sse.cpp b/src/gui/painting/qdrawhelper_sse.cpp
new file mode 100644
index 0000000000..2f50c4d199
--- /dev/null
+++ b/src/gui/painting/qdrawhelper_sse.cpp
@@ -0,0 +1,147 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <private/qdrawhelper_p.h>
+
+#ifdef QT_HAVE_SSE
+
+#include <private/qdrawhelper_sse_p.h>
+
+QT_BEGIN_NAMESPACE
+
+CompositionFunctionSolid qt_functionForModeSolid_SSE[numCompositionFunctions] = {
+ comp_func_solid_SourceOver<QSSEIntrinsics>,
+ comp_func_solid_DestinationOver<QSSEIntrinsics>,
+ comp_func_solid_Clear<QSSEIntrinsics>,
+ comp_func_solid_Source<QSSEIntrinsics>,
+ 0,
+ comp_func_solid_SourceIn<QSSEIntrinsics>,
+ comp_func_solid_DestinationIn<QSSEIntrinsics>,
+ comp_func_solid_SourceOut<QSSEIntrinsics>,
+ comp_func_solid_DestinationOut<QSSEIntrinsics>,
+ comp_func_solid_SourceAtop<QSSEIntrinsics>,
+ comp_func_solid_DestinationAtop<QSSEIntrinsics>,
+ comp_func_solid_XOR<QSSEIntrinsics>,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // svg 1.2 modes
+ rasterop_solid_SourceOrDestination<QMMXIntrinsics>,
+ rasterop_solid_SourceAndDestination<QMMXIntrinsics>,
+ rasterop_solid_SourceXorDestination<QMMXIntrinsics>,
+ rasterop_solid_NotSourceAndNotDestination<QMMXIntrinsics>,
+ rasterop_solid_NotSourceOrNotDestination<QMMXIntrinsics>,
+ rasterop_solid_NotSourceXorDestination<QMMXIntrinsics>,
+ rasterop_solid_NotSource<QMMXIntrinsics>,
+ rasterop_solid_NotSourceAndDestination<QMMXIntrinsics>,
+ rasterop_solid_SourceAndNotDestination<QMMXIntrinsics>
+};
+
+CompositionFunction qt_functionForMode_SSE[numCompositionFunctions] = {
+ comp_func_SourceOver<QSSEIntrinsics>,
+ comp_func_DestinationOver<QSSEIntrinsics>,
+ comp_func_Clear<QSSEIntrinsics>,
+ comp_func_Source<QSSEIntrinsics>,
+ 0,
+ comp_func_SourceIn<QSSEIntrinsics>,
+ comp_func_DestinationIn<QSSEIntrinsics>,
+ comp_func_SourceOut<QSSEIntrinsics>,
+ comp_func_DestinationOut<QSSEIntrinsics>,
+ comp_func_SourceAtop<QSSEIntrinsics>,
+ comp_func_DestinationAtop<QSSEIntrinsics>,
+ comp_func_XOR<QSSEIntrinsics>
+};
+
+void qt_blend_color_argb_sse(int count, const QSpan *spans, void *userData)
+{
+ qt_blend_color_argb_x86<QSSEIntrinsics>(count, spans, userData,
+ (CompositionFunctionSolid*)qt_functionForModeSolid_SSE);
+}
+
+void qt_memfill32_sse(quint32 *dest, quint32 value, int count)
+{
+ return qt_memfill32_sse_template<QSSEIntrinsics>(dest, value, count);
+}
+
+void qt_bitmapblit16_sse(QRasterBuffer *rasterBuffer, int x, int y,
+ quint32 color,
+ const uchar *src,
+ int width, int height, int stride)
+{
+ return qt_bitmapblit16_sse_template<QSSEIntrinsics>(rasterBuffer, x,y,
+ color, src, width,
+ height, stride);
+}
+
+void qt_blend_argb32_on_argb32_sse(uchar *destPixels, int dbpl,
+ const uchar *srcPixels, int sbpl,
+ int w, int h,
+ int const_alpha)
+{
+ const uint *src = (const uint *) srcPixels;
+ uint *dst = (uint *) destPixels;
+
+ uint ca = const_alpha - 1;
+
+ for (int y=0; y<h; ++y) {
+ comp_func_SourceOver<QSSEIntrinsics>(dst, src, w, ca);
+ dst = (quint32 *)(((uchar *) dst) + dbpl);
+ src = (const quint32 *)(((const uchar *) src) + sbpl);
+ }
+}
+
+void qt_blend_rgb32_on_rgb32_sse(uchar *destPixels, int dbpl,
+ const uchar *srcPixels, int sbpl,
+ int w, int h,
+ int const_alpha)
+{
+ const uint *src = (const uint *) srcPixels;
+ uint *dst = (uint *) destPixels;
+
+ uint ca = const_alpha - 1;
+
+ for (int y=0; y<h; ++y) {
+ comp_func_Source<QSSEIntrinsics>(dst, src, w, ca);
+ dst = (quint32 *)(((uchar *) dst) + dbpl);
+ src = (const quint32 *)(((const uchar *) src) + sbpl);
+ }
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_HAVE_SSE
diff --git a/src/gui/painting/qdrawhelper_sse2.cpp b/src/gui/painting/qdrawhelper_sse2.cpp
new file mode 100644
index 0000000000..a66284e9ae
--- /dev/null
+++ b/src/gui/painting/qdrawhelper_sse2.cpp
@@ -0,0 +1,211 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <private/qdrawhelper_x86_p.h>
+
+#ifdef QT_HAVE_SSE2
+
+#include <private/qpaintengine_raster_p.h>
+
+#ifdef QT_LINUXBASE
+// this is an evil hack - the posix_memalign declaration in LSB
+// is wrong - see http://bugs.linuxbase.org/show_bug.cgi?id=2431
+# define posix_memalign _lsb_hack_posix_memalign
+# include <emmintrin.h>
+# undef posix_memalign
+#else
+# include <emmintrin.h>
+#endif
+
+QT_BEGIN_NAMESPACE
+
+void qt_memfill32_sse2(quint32 *dest, quint32 value, int count)
+{
+ if (count < 7) {
+ switch (count) {
+ case 6: *dest++ = value;
+ case 5: *dest++ = value;
+ case 4: *dest++ = value;
+ case 3: *dest++ = value;
+ case 2: *dest++ = value;
+ case 1: *dest = value;
+ }
+ return;
+ };
+
+ const int align = (quintptr)(dest) & 0xf;
+ switch (align) {
+ case 4: *dest++ = value; --count;
+ case 8: *dest++ = value; --count;
+ case 12: *dest++ = value; --count;
+ }
+
+ int count128 = count / 4;
+ __m128i *dst128 = reinterpret_cast<__m128i*>(dest);
+ const __m128i value128 = _mm_set_epi32(value, value, value, value);
+
+ int n = (count128 + 3) / 4;
+ switch (count128 & 0x3) {
+ case 0: do { _mm_store_si128(dst128++, value128);
+ case 3: _mm_store_si128(dst128++, value128);
+ case 2: _mm_store_si128(dst128++, value128);
+ case 1: _mm_store_si128(dst128++, value128);
+ } while (--n > 0);
+ }
+
+ const int rest = count & 0x3;
+ if (rest) {
+ switch (rest) {
+ case 3: dest[count - 3] = value;
+ case 2: dest[count - 2] = value;
+ case 1: dest[count - 1] = value;
+ }
+ }
+}
+
+void qt_memfill16_sse2(quint16 *dest, quint16 value, int count)
+{
+ if (count < 3) {
+ switch (count) {
+ case 2: *dest++ = value;
+ case 1: *dest = value;
+ }
+ return;
+ }
+
+ const int align = (quintptr)(dest) & 0x3;
+ switch (align) {
+ case 2: *dest++ = value; --count;
+ }
+
+ const quint32 value32 = (value << 16) | value;
+ qt_memfill32_sse2(reinterpret_cast<quint32*>(dest), value32, count / 2);
+
+ if (count & 0x1)
+ dest[count - 1] = value;
+}
+
+void qt_bitmapblit32_sse2(QRasterBuffer *rasterBuffer, int x, int y,
+ quint32 color,
+ const uchar *src, int width, int height, int stride)
+{
+ quint32 *dest = reinterpret_cast<quint32*>(rasterBuffer->scanLine(y)) + x;
+ const int destStride = rasterBuffer->bytesPerLine() / sizeof(quint32);
+
+ const __m128i c128 = _mm_set1_epi32(color);
+ const __m128i maskmask1 = _mm_set_epi32(0x10101010, 0x20202020,
+ 0x40404040, 0x80808080);
+ const __m128i maskadd1 = _mm_set_epi32(0x70707070, 0x60606060,
+ 0x40404040, 0x00000000);
+
+ if (width > 4) {
+ const __m128i maskmask2 = _mm_set_epi32(0x01010101, 0x02020202,
+ 0x04040404, 0x08080808);
+ const __m128i maskadd2 = _mm_set_epi32(0x7f7f7f7f, 0x7e7e7e7e,
+ 0x7c7c7c7c, 0x78787878);
+ while (height--) {
+ for (int x = 0; x < width; x += 8) {
+ const quint8 s = src[x >> 3];
+ if (!s)
+ continue;
+ __m128i mask1 = _mm_set1_epi8(s);
+ __m128i mask2 = mask1;
+
+ mask1 = _mm_and_si128(mask1, maskmask1);
+ mask1 = _mm_add_epi8(mask1, maskadd1);
+ _mm_maskmoveu_si128(c128, mask1, (char*)(dest + x));
+ mask2 = _mm_and_si128(mask2, maskmask2);
+ mask2 = _mm_add_epi8(mask2, maskadd2);
+ _mm_maskmoveu_si128(c128, mask2, (char*)(dest + x + 4));
+ }
+ dest += destStride;
+ src += stride;
+ }
+ } else {
+ while (height--) {
+ const quint8 s = *src;
+ if (s) {
+ __m128i mask1 = _mm_set1_epi8(s);
+ mask1 = _mm_and_si128(mask1, maskmask1);
+ mask1 = _mm_add_epi8(mask1, maskadd1);
+ _mm_maskmoveu_si128(c128, mask1, (char*)(dest));
+ }
+ dest += destStride;
+ src += stride;
+ }
+ }
+}
+
+void qt_bitmapblit16_sse2(QRasterBuffer *rasterBuffer, int x, int y,
+ quint32 color,
+ const uchar *src, int width, int height, int stride)
+{
+ const quint16 c = qt_colorConvert<quint16, quint32>(color, 0);
+ quint16 *dest = reinterpret_cast<quint16*>(rasterBuffer->scanLine(y)) + x;
+ const int destStride = rasterBuffer->bytesPerLine() / sizeof(quint16);
+
+ const __m128i c128 = _mm_set1_epi16(c);
+#if defined(Q_OS_WIN)
+# pragma warning(disable: 4309) // truncation of constant value
+#endif
+ const __m128i maskmask = _mm_set_epi16(0x0101, 0x0202, 0x0404, 0x0808,
+ 0x1010, 0x2020, 0x4040, 0x8080);
+ const __m128i maskadd = _mm_set_epi16(0x7f7f, 0x7e7e, 0x7c7c, 0x7878,
+ 0x7070, 0x6060, 0x4040, 0x0000);
+
+ while (height--) {
+ for (int x = 0; x < width; x += 8) {
+ const quint8 s = src[x >> 3];
+ if (!s)
+ continue;
+ __m128i mask = _mm_set1_epi8(s);
+ mask = _mm_and_si128(mask, maskmask);
+ mask = _mm_add_epi8(mask, maskadd);
+ _mm_maskmoveu_si128(c128, mask, (char*)(dest + x));
+ }
+ dest += destStride;
+ src += stride;
+ }
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_HAVE_SSE2
diff --git a/src/gui/painting/qdrawhelper_sse3dnow.cpp b/src/gui/painting/qdrawhelper_sse3dnow.cpp
new file mode 100644
index 0000000000..74ad271ed3
--- /dev/null
+++ b/src/gui/painting/qdrawhelper_sse3dnow.cpp
@@ -0,0 +1,122 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <private/qdrawhelper_x86_p.h>
+
+#if defined(QT_HAVE_3DNOW) && defined(QT_HAVE_SSE)
+
+#include <private/qdrawhelper_sse_p.h>
+#include <mm3dnow.h>
+
+QT_BEGIN_NAMESPACE
+
+struct QSSE3DNOWIntrinsics : public QSSEIntrinsics
+{
+ static inline void end() {
+ _m_femms();
+ }
+};
+
+CompositionFunctionSolid qt_functionForModeSolid_SSE3DNOW[numCompositionFunctions] = {
+ comp_func_solid_SourceOver<QSSE3DNOWIntrinsics>,
+ comp_func_solid_DestinationOver<QSSE3DNOWIntrinsics>,
+ comp_func_solid_Clear<QSSE3DNOWIntrinsics>,
+ comp_func_solid_Source<QSSE3DNOWIntrinsics>,
+ 0,
+ comp_func_solid_SourceIn<QSSE3DNOWIntrinsics>,
+ comp_func_solid_DestinationIn<QSSE3DNOWIntrinsics>,
+ comp_func_solid_SourceOut<QSSE3DNOWIntrinsics>,
+ comp_func_solid_DestinationOut<QSSE3DNOWIntrinsics>,
+ comp_func_solid_SourceAtop<QSSE3DNOWIntrinsics>,
+ comp_func_solid_DestinationAtop<QSSE3DNOWIntrinsics>,
+ comp_func_solid_XOR<QSSE3DNOWIntrinsics>,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // svg 1.2 modes
+ rasterop_solid_SourceOrDestination<QSSE3DNOWIntrinsics>,
+ rasterop_solid_SourceAndDestination<QSSE3DNOWIntrinsics>,
+ rasterop_solid_SourceXorDestination<QSSE3DNOWIntrinsics>,
+ rasterop_solid_NotSourceAndNotDestination<QSSE3DNOWIntrinsics>,
+ rasterop_solid_NotSourceOrNotDestination<QSSE3DNOWIntrinsics>,
+ rasterop_solid_NotSourceXorDestination<QSSE3DNOWIntrinsics>,
+ rasterop_solid_NotSource<QSSE3DNOWIntrinsics>,
+ rasterop_solid_NotSourceAndDestination<QSSE3DNOWIntrinsics>,
+ rasterop_solid_SourceAndNotDestination<QSSE3DNOWIntrinsics>
+};
+
+CompositionFunction qt_functionForMode_SSE3DNOW[numCompositionFunctions] = {
+ comp_func_SourceOver<QSSE3DNOWIntrinsics>,
+ comp_func_DestinationOver<QSSE3DNOWIntrinsics>,
+ comp_func_Clear<QSSE3DNOWIntrinsics>,
+ comp_func_Source<QSSE3DNOWIntrinsics>,
+ 0,
+ comp_func_SourceIn<QSSE3DNOWIntrinsics>,
+ comp_func_DestinationIn<QSSE3DNOWIntrinsics>,
+ comp_func_SourceOut<QSSE3DNOWIntrinsics>,
+ comp_func_DestinationOut<QSSE3DNOWIntrinsics>,
+ comp_func_SourceAtop<QSSE3DNOWIntrinsics>,
+ comp_func_DestinationAtop<QSSE3DNOWIntrinsics>,
+ comp_func_XOR<QSSE3DNOWIntrinsics>
+};
+
+void qt_blend_color_argb_sse3dnow(int count, const QSpan *spans, void *userData)
+{
+ qt_blend_color_argb_x86<QSSE3DNOWIntrinsics>(count, spans, userData,
+ (CompositionFunctionSolid*)qt_functionForModeSolid_SSE3DNOW);
+}
+
+void qt_memfill32_sse3dnow(quint32 *dest, quint32 value, int count)
+{
+ return qt_memfill32_sse_template<QSSE3DNOWIntrinsics>(dest, value, count);
+}
+
+
+void qt_bitmapblit16_sse3dnow(QRasterBuffer *rasterBuffer, int x, int y,
+ quint32 color,
+ const uchar *src,
+ int width, int height, int stride)
+{
+ return qt_bitmapblit16_sse_template<QSSE3DNOWIntrinsics>(rasterBuffer, x,y,
+ color, src, width,
+ height, stride);
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_HAVE_3DNOW && QT_HAVE_SSE
diff --git a/src/gui/painting/qdrawhelper_sse_p.h b/src/gui/painting/qdrawhelper_sse_p.h
new file mode 100644
index 0000000000..1cb4cce6f4
--- /dev/null
+++ b/src/gui/painting/qdrawhelper_sse_p.h
@@ -0,0 +1,182 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QDRAWHELPER_SSE_P_H
+#define QDRAWHELPER_SSE_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/qdrawhelper_mmx_p.h>
+
+#ifdef QT_HAVE_SSE
+
+#ifdef QT_LINUXBASE
+// this is an evil hack - the posix_memalign declaration in LSB
+// is wrong - see http://bugs.linuxbase.org/show_bug.cgi?id=2431
+# define posix_memalign _lsb_hack_posix_memalign
+# include <xmmintrin.h>
+# undef posix_memalign
+#else
+# include <xmmintrin.h>
+#endif
+
+QT_BEGIN_NAMESPACE
+
+#ifndef _MM_SHUFFLE
+#define _MM_SHUFFLE(fp3,fp2,fp1,fp0) \
+ (((fp3) << 6) | ((fp2) << 4) | ((fp1) << 2) | (fp0))
+#endif
+
+struct QSSEIntrinsics : public QMMXIntrinsics
+{
+ static inline m64 alpha(m64 x) {
+ return _mm_shuffle_pi16 (x, _MM_SHUFFLE(3, 3, 3, 3));
+ }
+
+ static inline m64 _load_alpha(uint x, const m64 &mmx_0x0000) {
+ m64 t = _mm_unpacklo_pi8(_mm_cvtsi32_si64(x), mmx_0x0000);
+ return _mm_shuffle_pi16 (t, _MM_SHUFFLE(0, 0, 0, 0));
+ }
+};
+
+template <class MM>
+inline void qt_memfill32_sse_template(quint32 *dest, quint32 value, int count)
+{
+ if (count < 7) {
+ switch (count) {
+ case 6: *dest++ = value;
+ case 5: *dest++ = value;
+ case 4: *dest++ = value;
+ case 3: *dest++ = value;
+ case 2: *dest++ = value;
+ case 1: *dest = value;
+ }
+ return;
+ };
+
+ __m64 *dst64 = reinterpret_cast<__m64*>(dest);
+ const __m64 value64 = _mm_set_pi32(value, value);
+ int count64 = count / 2;
+
+ int n = (count64 + 3) / 4;
+ switch (count64 & 0x3) {
+ case 0: do { _mm_stream_pi(dst64++, value64);
+ case 3: _mm_stream_pi(dst64++, value64);
+ case 2: _mm_stream_pi(dst64++, value64);
+ case 1: _mm_stream_pi(dst64++, value64);
+ } while (--n > 0);
+ }
+
+ if (count & 0x1)
+ dest[count - 1] = value;
+
+ MM::end();
+}
+
+template <class MM>
+inline void qt_bitmapblit16_sse_template(QRasterBuffer *rasterBuffer,
+ int x, int y,
+ quint32 color,
+ const uchar *src,
+ int width, int height, int stride)
+{
+ const quint16 c = qt_colorConvert<quint16, quint32>(color, 0);
+ quint16 *dest = reinterpret_cast<quint16*>(rasterBuffer->scanLine(y)) + x;
+ const int destStride = rasterBuffer->bytesPerLine() / sizeof(quint16);
+
+ const __m64 c64 = _mm_set1_pi16(c);
+#if defined(Q_OS_WIN)
+# pragma warning(disable: 4309) // truncation of constant value
+#endif
+ const __m64 maskmask1 = _mm_set_pi16(0x1010, 0x2020, 0x4040, 0x8080);
+ const __m64 maskadd1 = _mm_set_pi16(0x7070, 0x6060, 0x4040, 0x0000);
+
+ if (width > 4) {
+ const __m64 maskmask2 = _mm_set_pi16(0x0101, 0x0202, 0x0404, 0x0808);
+ const __m64 maskadd2 = _mm_set_pi16(0x7f7f, 0x7e7e, 0x7c7c, 0x7878);
+
+ while (height--) {
+ for (int x = 0; x < width; x += 8) {
+ const quint8 s = src[x >> 3];
+ if (!s)
+ continue;
+ __m64 mask1 = _mm_set1_pi8(s);
+ __m64 mask2 = mask1;
+ mask1 = _m_pand(mask1, maskmask1);
+ mask1 = _mm_add_pi16(mask1, maskadd1);
+ _mm_maskmove_si64(c64, mask1, (char*)(dest + x));
+ mask2 = _m_pand(mask2, maskmask2);
+ mask2 = _mm_add_pi16(mask2, maskadd2);
+ _mm_maskmove_si64(c64, mask2, (char*)(dest + x + 4));
+ }
+ dest += destStride;
+ src += stride;
+ }
+ } else {
+ while (height--) {
+ const quint8 s = *src;
+ if (s) {
+ __m64 mask1 = _mm_set1_pi8(s);
+ mask1 = _m_pand(mask1, maskmask1);
+ mask1 = _mm_add_pi16(mask1, maskadd1);
+ _mm_maskmove_si64(c64, mask1, (char*)(dest));
+ }
+ dest += destStride;
+ src += stride;
+ }
+ }
+
+ MM::end();
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_HAVE_SSE
+#endif // QDRAWHELPER_SSE_P_H
diff --git a/src/gui/painting/qdrawhelper_x86_p.h b/src/gui/painting/qdrawhelper_x86_p.h
new file mode 100644
index 0000000000..ff43256d73
--- /dev/null
+++ b/src/gui/painting/qdrawhelper_x86_p.h
@@ -0,0 +1,130 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QDRAWHELPER_X86_P_H
+#define QDRAWHELPER_X86_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/qdrawhelper_p.h>
+
+QT_BEGIN_NAMESPACE
+
+#ifdef QT_HAVE_MMX
+extern CompositionFunction qt_functionForMode_MMX[];
+extern CompositionFunctionSolid qt_functionForModeSolid_MMX[];
+void qt_blend_color_argb_mmx(int count, const QSpan *spans, void *userData);
+#endif
+
+#ifdef QT_HAVE_MMXEXT
+void qt_memfill32_mmxext(quint32 *dest, quint32 value, int count);
+void qt_bitmapblit16_mmxext(QRasterBuffer *rasterBuffer, int x, int y,
+ quint32 color, const uchar *src,
+ int width, int height, int stride);
+#endif
+
+#ifdef QT_HAVE_3DNOW
+#if defined(QT_HAVE_MMX) || !defined(QT_HAVE_SSE)
+extern CompositionFunction qt_functionForMode_MMX3DNOW[];
+extern CompositionFunctionSolid qt_functionForModeSolid_MMX3DNOW[];
+
+void qt_blend_color_argb_mmx3dnow(int count, const QSpan *spans,
+ void *userData);
+#endif // MMX
+
+#ifdef QT_HAVE_SSE
+extern CompositionFunction qt_functionForMode_SSE3DNOW[];
+extern CompositionFunctionSolid qt_functionForModeSolid_SSE3DNOW[];
+
+void qt_memfill32_sse3dnow(quint32 *dest, quint32 value, int count);
+void qt_bitmapblit16_sse3dnow(QRasterBuffer *rasterBuffer, int x, int y,
+ quint32 color,
+ const uchar *src, int width, int height,
+ int stride);
+void qt_blend_color_argb_sse3dnow(int count, const QSpan *spans,
+ void *userData);
+#endif // SSE
+#endif // QT_HAVE_3DNOW
+
+#ifdef QT_HAVE_SSE
+void qt_memfill32_sse(quint32 *dest, quint32 value, int count);
+void qt_bitmapblit16_sse(QRasterBuffer *rasterBuffer, int x, int y,
+ quint32 color,
+ const uchar *src, int width, int height, int stride);
+
+void qt_blend_color_argb_sse(int count, const QSpan *spans, void *userData);
+
+extern CompositionFunction qt_functionForMode_SSE[];
+extern CompositionFunctionSolid qt_functionForModeSolid_SSE[];
+#endif // QT_HAVE_SSE
+
+#ifdef QT_HAVE_SSE2
+void qt_memfill32_sse2(quint32 *dest, quint32 value, int count);
+void qt_memfill16_sse2(quint16 *dest, quint16 value, int count);
+void qt_bitmapblit32_sse2(QRasterBuffer *rasterBuffer, int x, int y,
+ quint32 color,
+ const uchar *src, int width, int height, int stride);
+void qt_bitmapblit16_sse2(QRasterBuffer *rasterBuffer, int x, int y,
+ quint32 color,
+ const uchar *src, int width, int height, int stride);
+#endif // QT_HAVE_SSE2
+
+#ifdef QT_HAVE_IWMMXT
+void qt_blend_color_argb_iwmmxt(int count, const QSpan *spans, void *userData);
+
+extern CompositionFunction qt_functionForMode_IWMMXT[];
+extern CompositionFunctionSolid qt_functionForModeSolid_IWMMXT[];
+#endif
+
+static const int numCompositionFunctions = 33;
+
+QT_END_NAMESPACE
+
+#endif // QDRAWHELPER_X86_P_H
diff --git a/src/gui/painting/qdrawutil.cpp b/src/gui/painting/qdrawutil.cpp
new file mode 100644
index 0000000000..2beeb0ed88
--- /dev/null
+++ b/src/gui/painting/qdrawutil.cpp
@@ -0,0 +1,1041 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qdrawutil.h"
+#include "qbitmap.h"
+#include "qpixmapcache.h"
+#include "qapplication.h"
+#include "qpainter.h"
+#include "qpalette.h"
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \fn void qDrawShadeLine(QPainter *painter, int x1, int y1, int x2, int y2,
+ const QPalette &palette, bool sunken,
+ int lineWidth, int midLineWidth)
+ \relates QPainter
+
+ Draws a horizontal (\a y1 == \a y2) or vertical (\a x1 == \a x2)
+ shaded line using the given \a painter. Note that nothing is
+ drawn if \a y1 != \a y2 and \a x1 != \a x2 (i.e. the line is
+ neither horizontal nor vertical).
+
+ The provided \a palette specifies the shading colors (\l
+ {QPalette::light()}{light}, \l {QPalette::dark()}{dark} and \l
+ {QPalette::mid()}{middle} colors). The given \a lineWidth
+ specifies the line width for each of the lines; it is not the
+ total line width. The given \a midLineWidth specifies the width of
+ a middle line drawn in the QPalette::mid() color.
+
+ The line appears sunken if \a sunken is true, otherwise raised.
+
+ \warning This function does not look at QWidget::style() or
+ QApplication::style(). Use the drawing functions in QStyle to
+ make widgets that follow the current GUI style.
+
+
+ Alternatively you can use a QFrame widget and apply the
+ QFrame::setFrameStyle() function to display a shaded line:
+
+ \snippet doc/src/snippets/code/src_gui_painting_qdrawutil.cpp 0
+
+ \sa qDrawShadeRect(), qDrawShadePanel(), QStyle
+*/
+
+void qDrawShadeLine(QPainter *p, int x1, int y1, int x2, int y2,
+ const QPalette &pal, bool sunken,
+ int lineWidth, int midLineWidth)
+{
+ if (!(p && lineWidth >= 0 && midLineWidth >= 0)) {
+ qWarning("qDrawShadeLine: Invalid parameters");
+ return;
+ }
+ int tlw = lineWidth*2 + midLineWidth; // total line width
+ QPen oldPen = p->pen(); // save pen
+ if (sunken)
+ p->setPen(pal.color(QPalette::Dark));
+ else
+ p->setPen(pal.light().color());
+ QPolygon a;
+ int i;
+ if (y1 == y2) { // horizontal line
+ int y = y1 - tlw/2;
+ if (x1 > x2) { // swap x1 and x2
+ int t = x1;
+ x1 = x2;
+ x2 = t;
+ }
+ x2--;
+ for (i=0; i<lineWidth; i++) { // draw top shadow
+ a.setPoints(3, x1+i, y+tlw-1-i,
+ x1+i, y+i,
+ x2-i, y+i);
+ p->drawPolyline(a);
+ }
+ if (midLineWidth > 0) {
+ p->setPen(pal.mid().color());
+ for (i=0; i<midLineWidth; i++) // draw lines in the middle
+ p->drawLine(x1+lineWidth, y+lineWidth+i,
+ x2-lineWidth, y+lineWidth+i);
+ }
+ if (sunken)
+ p->setPen(pal.light().color());
+ else
+ p->setPen(pal.dark().color());
+ for (i=0; i<lineWidth; i++) { // draw bottom shadow
+ a.setPoints(3, x1+i, y+tlw-i-1,
+ x2-i, y+tlw-i-1,
+ x2-i, y+i+1);
+ p->drawPolyline(a);
+ }
+ }
+ else if (x1 == x2) { // vertical line
+ int x = x1 - tlw/2;
+ if (y1 > y2) { // swap y1 and y2
+ int t = y1;
+ y1 = y2;
+ y2 = t;
+ }
+ y2--;
+ for (i=0; i<lineWidth; i++) { // draw left shadow
+ a.setPoints(3, x+i, y2,
+ x+i, y1+i,
+ x+tlw-1, y1+i);
+ p->drawPolyline(a);
+ }
+ if (midLineWidth > 0) {
+ p->setPen(pal.mid().color());
+ for (i=0; i<midLineWidth; i++) // draw lines in the middle
+ p->drawLine(x+lineWidth+i, y1+lineWidth, x+lineWidth+i, y2);
+ }
+ if (sunken)
+ p->setPen(pal.light().color());
+ else
+ p->setPen(pal.dark().color());
+ for (i=0; i<lineWidth; i++) { // draw right shadow
+ a.setPoints(3, x+lineWidth, y2-i,
+ x+tlw-i-1, y2-i,
+ x+tlw-i-1, y1+lineWidth);
+ p->drawPolyline(a);
+ }
+ }
+ p->setPen(oldPen);
+}
+
+/*!
+ \fn void qDrawShadeRect(QPainter *painter, int x, int y, int width, int height,
+ const QPalette &palette, bool sunken,
+ int lineWidth, int midLineWidth,
+ const QBrush *fill)
+ \relates QPainter
+
+ Draws the shaded rectangle beginning at (\a x, \a y) with the
+ given \a width and \a height using the provided \a painter.
+
+ The provide \a palette specifies the shading colors (\l
+ {QPalette::light()}{light}, \l {QPalette::dark()}{dark} and \l
+ {QPalette::mid()}{middle} colors. The given \a lineWidth
+ specifies the line width for each of the lines; it is not the
+ total line width. The \a midLineWidth specifies the width of a
+ middle line drawn in the QPalette::mid() color. The rectangle's
+ interior is filled with the \a fill brush unless \a fill is 0.
+
+ The rectangle appears sunken if \a sunken is true, otherwise
+ raised.
+
+ \warning This function does not look at QWidget::style() or
+ QApplication::style(). Use the drawing functions in QStyle to make
+ widgets that follow the current GUI style.
+
+ Alternatively you can use a QFrame widget and apply the
+ QFrame::setFrameStyle() function to display a shaded rectangle:
+
+ \snippet doc/src/snippets/code/src_gui_painting_qdrawutil.cpp 1
+
+ \sa qDrawShadeLine(), qDrawShadePanel(), qDrawPlainRect(), QStyle
+*/
+
+void qDrawShadeRect(QPainter *p, int x, int y, int w, int h,
+ const QPalette &pal, bool sunken,
+ int lineWidth, int midLineWidth,
+ const QBrush *fill)
+{
+ if (w == 0 || h == 0)
+ return;
+ if (! (w > 0 && h > 0 && lineWidth >= 0 && midLineWidth >= 0)) {
+ qWarning("qDrawShadeRect: Invalid parameters");
+ return;
+ }
+ QPen oldPen = p->pen();
+ if (sunken)
+ p->setPen(pal.dark().color());
+ else
+ p->setPen(pal.light().color());
+ int x1=x, y1=y, x2=x+w-1, y2=y+h-1;
+
+ if (lineWidth == 1 && midLineWidth == 0) {// standard shade rectangle
+ p->drawRect(x1, y1, w-2, h-2);
+ if (sunken)
+ p->setPen(pal.light().color());
+ else
+ p->setPen(pal.dark().color());
+ QLineF lines[4] = { QLineF(x1+1, y1+1, x2-2, y1+1),
+ QLineF(x1+1, y1+2, x1+1, y2-2),
+ QLineF(x1, y2, x2, y2),
+ QLineF(x2,y1, x2,y2-1) };
+ p->drawLines(lines, 4); // draw bottom/right lines
+ } else { // more complicated
+ int m = lineWidth+midLineWidth;
+ int i, j=0, k=m;
+ for (i=0; i<lineWidth; i++) { // draw top shadow
+ QLineF lines[4] = { QLineF(x1+i, y2-i, x1+i, y1+i),
+ QLineF(x1+i, y1+i, x2-i, y1+i),
+ QLineF(x1+k, y2-k, x2-k, y2-k),
+ QLineF(x2-k, y2-k, x2-k, y1+k) };
+ p->drawLines(lines, 4);
+ k++;
+ }
+ p->setPen(pal.mid().color());
+ j = lineWidth*2;
+ for (i=0; i<midLineWidth; i++) { // draw lines in the middle
+ p->drawRect(x1+lineWidth+i, y1+lineWidth+i, w-j-1, h-j-1);
+ j += 2;
+ }
+ if (sunken)
+ p->setPen(pal.light().color());
+ else
+ p->setPen(pal.dark().color());
+ k = m;
+ for (i=0; i<lineWidth; i++) { // draw bottom shadow
+ QLineF lines[4] = { QLineF(x1+1+i, y2-i, x2-i, y2-i),
+ QLineF(x2-i, y2-i, x2-i, y1+i+1),
+ QLineF(x1+k, y2-k, x1+k, y1+k),
+ QLineF(x1+k, y1+k, x2-k, y1+k) };
+ p->drawLines(lines, 4);
+ k++;
+ }
+ }
+ if (fill) {
+ QBrush oldBrush = p->brush();
+ int tlw = lineWidth + midLineWidth;
+ p->setPen(Qt::NoPen);
+ p->setBrush(*fill);
+ p->drawRect(x+tlw, y+tlw, w-2*tlw, h-2*tlw);
+ p->setBrush(oldBrush);
+ }
+ p->setPen(oldPen); // restore pen
+}
+
+
+/*!
+ \fn void qDrawShadePanel(QPainter *painter, int x, int y, int width, int height,
+ const QPalette &palette, bool sunken,
+ int lineWidth, const QBrush *fill)
+ \relates QPainter
+
+ Draws the shaded panel beginning at (\a x, \a y) with the given \a
+ width and \a height using the provided \a painter and the given \a
+ lineWidth.
+
+ The given \a palette specifies the shading colors (\l
+ {QPalette::light()}{light}, \l {QPalette::dark()}{dark} and \l
+ {QPalette::mid()}{middle} colors). The panel's interior is filled
+ with the \a fill brush unless \a fill is 0.
+
+ The panel appears sunken if \a sunken is true, otherwise raised.
+
+ \warning This function does not look at QWidget::style() or
+ QApplication::style(). Use the drawing functions in QStyle to make
+ widgets that follow the current GUI style.
+
+ Alternatively you can use a QFrame widget and apply the
+ QFrame::setFrameStyle() function to display a shaded panel:
+
+ \snippet doc/src/snippets/code/src_gui_painting_qdrawutil.cpp 2
+
+ \sa qDrawWinPanel(), qDrawShadeLine(), qDrawShadeRect(), QStyle
+*/
+
+void qDrawShadePanel(QPainter *p, int x, int y, int w, int h,
+ const QPalette &pal, bool sunken,
+ int lineWidth, const QBrush *fill)
+{
+ if (w == 0 || h == 0)
+ return;
+ if (!(w > 0 && h > 0 && lineWidth >= 0)) {
+ qWarning("qDrawShadePanel: Invalid parameters");
+ }
+ QColor shade = pal.dark().color();
+ QColor light = pal.light().color();
+ if (fill) {
+ if (fill->color() == shade)
+ shade = pal.shadow().color();
+ if (fill->color() == light)
+ light = pal.midlight().color();
+ }
+ QPen oldPen = p->pen(); // save pen
+ QVector<QLineF> lines;
+ lines.reserve(2*lineWidth);
+
+ if (sunken)
+ p->setPen(shade);
+ else
+ p->setPen(light);
+ int x1, y1, x2, y2;
+ int i;
+ x1 = x;
+ y1 = y2 = y;
+ x2 = x+w-2;
+ for (i=0; i<lineWidth; i++) { // top shadow
+ lines << QLineF(x1, y1++, x2--, y2++);
+ }
+ x2 = x1;
+ y1 = y+h-2;
+ for (i=0; i<lineWidth; i++) { // left shado
+ lines << QLineF(x1++, y1, x2++, y2--);
+ }
+ p->drawLines(lines);
+ lines.clear();
+ if (sunken)
+ p->setPen(light);
+ else
+ p->setPen(shade);
+ x1 = x;
+ y1 = y2 = y+h-1;
+ x2 = x+w-1;
+ for (i=0; i<lineWidth; i++) { // bottom shadow
+ lines << QLineF(x1++, y1--, x2, y2--);
+ }
+ x1 = x2;
+ y1 = y;
+ y2 = y+h-lineWidth-1;
+ for (i=0; i<lineWidth; i++) { // right shadow
+ lines << QLineF(x1--, y1++, x2--, y2);
+ }
+ p->drawLines(lines);
+ if (fill) // fill with fill color
+ p->fillRect(x+lineWidth, y+lineWidth, w-lineWidth*2, h-lineWidth*2, *fill);
+ p->setPen(oldPen); // restore pen
+}
+
+
+/*!
+ \internal
+ This function draws a rectangle with two pixel line width.
+ It is called from qDrawWinButton() and qDrawWinPanel().
+
+ c1..c4 and fill are used:
+
+ 1 1 1 1 1 2
+ 1 3 3 3 4 2
+ 1 3 F F 4 2
+ 1 3 F F 4 2
+ 1 4 4 4 4 2
+ 2 2 2 2 2 2
+*/
+
+static void qDrawWinShades(QPainter *p,
+ int x, int y, int w, int h,
+ const QColor &c1, const QColor &c2,
+ const QColor &c3, const QColor &c4,
+ const QBrush *fill)
+{
+ if (w < 2 || h < 2) // can't do anything with that
+ return;
+ QPen oldPen = p->pen();
+ QPoint a[3] = { QPoint(x, y+h-2), QPoint(x, y), QPoint(x+w-2, y) };
+ p->setPen(c1);
+ p->drawPolyline(a, 3);
+ QPoint b[3] = { QPoint(x, y+h-1), QPoint(x+w-1, y+h-1), QPoint(x+w-1, y) };
+ p->setPen(c2);
+ p->drawPolyline(b, 3);
+ if (w > 4 && h > 4) {
+ QPoint c[3] = { QPoint(x+1, y+h-3), QPoint(x+1, y+1), QPoint(x+w-3, y+1) };
+ p->setPen(c3);
+ p->drawPolyline(c, 3);
+ QPoint d[3] = { QPoint(x+1, y+h-2), QPoint(x+w-2, y+h-2), QPoint(x+w-2, y+1) };
+ p->setPen(c4);
+ p->drawPolyline(d, 3);
+ if (fill)
+ p->fillRect(QRect(x+2, y+2, w-4, h-4), *fill);
+ }
+ p->setPen(oldPen);
+}
+
+
+/*!
+ \fn void qDrawWinButton(QPainter *painter, int x, int y, int width, int height,
+ const QPalette &palette, bool sunken,
+ const QBrush *fill)
+ \relates QPainter
+
+ Draws the Windows-style button specified by the given point (\a x,
+ \a y}, \a width and \a height using the provided \a painter with a
+ line width of 2 pixels. The button's interior is filled with the
+ \a{fill} brush unless \a fill is 0.
+
+ The given \a palette specifies the shading colors (\l
+ {QPalette::light()}{light}, \l {QPalette::dark()}{dark} and \l
+ {QPalette::mid()}{middle} colors).
+
+ The button appears sunken if \a sunken is true, otherwise raised.
+
+ \warning This function does not look at QWidget::style() or
+ QApplication::style()-> Use the drawing functions in QStyle to make
+ widgets that follow the current GUI style.
+
+ \sa qDrawWinPanel(), QStyle
+*/
+
+void qDrawWinButton(QPainter *p, int x, int y, int w, int h,
+ const QPalette &pal, bool sunken,
+ const QBrush *fill)
+{
+ if (sunken)
+ qDrawWinShades(p, x, y, w, h,
+ pal.shadow().color(), pal.light().color(), pal.dark().color(),
+ pal.button().color(), fill);
+ else
+ qDrawWinShades(p, x, y, w, h,
+ pal.light().color(), pal.shadow().color(), pal.button().color(),
+ pal.dark().color(), fill);
+}
+
+/*!
+ \fn void qDrawWinPanel(QPainter *painter, int x, int y, int width, int height,
+ const QPalette &palette, bool sunken,
+ const QBrush *fill)
+ \relates QPainter
+
+ Draws the Windows-style panel specified by the given point(\a x,
+ \a y), \a width and \a height using the provided \a painter with a
+ line width of 2 pixels. The button's interior is filled with the
+ \a fill brush unless \a fill is 0.
+
+ The given \a palette specifies the shading colors. The panel
+ appears sunken if \a sunken is true, otherwise raised.
+
+ \warning This function does not look at QWidget::style() or
+ QApplication::style(). Use the drawing functions in QStyle to make
+ widgets that follow the current GUI style.
+
+ Alternatively you can use a QFrame widget and apply the
+ QFrame::setFrameStyle() function to display a shaded panel:
+
+ \snippet doc/src/snippets/code/src_gui_painting_qdrawutil.cpp 3
+
+ \sa qDrawShadePanel(), qDrawWinButton(), QStyle
+*/
+
+void qDrawWinPanel(QPainter *p, int x, int y, int w, int h,
+ const QPalette &pal, bool sunken,
+ const QBrush *fill)
+{
+ if (sunken)
+ qDrawWinShades(p, x, y, w, h,
+ pal.dark().color(), pal.light().color(), pal.shadow().color(),
+ pal.midlight().color(), fill);
+ else
+ qDrawWinShades(p, x, y, w, h,
+ pal.light().color(), pal.shadow().color(), pal.midlight().color(),
+ pal.dark().color(), fill);
+}
+
+/*!
+ \fn void qDrawPlainRect(QPainter *painter, int x, int y, int width, int height, const QColor &lineColor,
+ int lineWidth, const QBrush *fill)
+ \relates QPainter
+
+ Draws the plain rectangle beginning at (\a x, \a y) with the given
+ \a width and \a height, using the specified \a painter, \a lineColor
+ and \a lineWidth. The rectangle's interior is filled with the \a
+ fill brush unless \a fill is 0.
+
+ \warning This function does not look at QWidget::style() or
+ QApplication::style(). Use the drawing functions in QStyle to make
+ widgets that follow the current GUI style.
+
+ Alternatively you can use a QFrame widget and apply the
+ QFrame::setFrameStyle() function to display a plain rectangle:
+
+ \snippet doc/src/snippets/code/src_gui_painting_qdrawutil.cpp 4
+
+ \sa qDrawShadeRect(), QStyle
+*/
+
+void qDrawPlainRect(QPainter *p, int x, int y, int w, int h, const QColor &c,
+ int lineWidth, const QBrush *fill)
+{
+ if (w == 0 || h == 0)
+ return;
+ if (!(w > 0 && h > 0 && lineWidth >= 0)) {
+ qWarning("qDrawPlainRect: Invalid parameters");
+ }
+ QPen oldPen = p->pen();
+ QBrush oldBrush = p->brush();
+ p->setPen(c);
+ p->setBrush(Qt::NoBrush);
+ for (int i=0; i<lineWidth; i++)
+ p->drawRect(x+i, y+i, w-i*2 - 1, h-i*2 - 1);
+ if (fill) { // fill with fill color
+ p->setPen(Qt::NoPen);
+ p->setBrush(*fill);
+ p->drawRect(x+lineWidth, y+lineWidth, w-lineWidth*2, h-lineWidth*2);
+ }
+ p->setPen(oldPen);
+ p->setBrush(oldBrush);
+}
+
+/*****************************************************************************
+ Overloaded functions.
+ *****************************************************************************/
+
+/*!
+ \fn void qDrawShadeLine(QPainter *painter, const QPoint &p1, const QPoint &p2,
+ const QPalette &palette, bool sunken, int lineWidth, int midLineWidth)
+ \relates QPainter
+ \overload
+
+ Draws a horizontal or vertical shaded line between \a p1 and \a p2
+ using the given \a painter. Note that nothing is drawn if the line
+ between the points would be neither horizontal nor vertical.
+
+ The provided \a palette specifies the shading colors (\l
+ {QPalette::light()}{light}, \l {QPalette::dark()}{dark} and \l
+ {QPalette::mid()}{middle} colors). The given \a lineWidth
+ specifies the line width for each of the lines; it is not the
+ total line width. The given \a midLineWidth specifies the width of
+ a middle line drawn in the QPalette::mid() color.
+
+ The line appears sunken if \a sunken is true, otherwise raised.
+
+ \warning This function does not look at QWidget::style() or
+ QApplication::style(). Use the drawing functions in QStyle to
+ make widgets that follow the current GUI style.
+
+
+ Alternatively you can use a QFrame widget and apply the
+ QFrame::setFrameStyle() function to display a shaded line:
+
+ \snippet doc/src/snippets/code/src_gui_painting_qdrawutil.cpp 5
+
+ \sa qDrawShadeRect(), qDrawShadePanel(), QStyle
+*/
+
+void qDrawShadeLine(QPainter *p, const QPoint &p1, const QPoint &p2,
+ const QPalette &pal, bool sunken,
+ int lineWidth, int midLineWidth)
+{
+ qDrawShadeLine(p, p1.x(), p1.y(), p2.x(), p2.y(), pal, sunken,
+ lineWidth, midLineWidth);
+}
+
+/*!
+ \fn void qDrawShadeRect(QPainter *painter, const QRect &rect, const QPalette &palette,
+ bool sunken, int lineWidth, int midLineWidth, const QBrush *fill)
+ \relates QPainter
+ \overload
+
+ Draws the shaded rectangle specified by \a rect using the given \a painter.
+
+ The provide \a palette specifies the shading colors (\l
+ {QPalette::light()}{light}, \l {QPalette::dark()}{dark} and \l
+ {QPalette::mid()}{middle} colors. The given \a lineWidth
+ specifies the line width for each of the lines; it is not the
+ total line width. The \a midLineWidth specifies the width of a
+ middle line drawn in the QPalette::mid() color. The rectangle's
+ interior is filled with the \a fill brush unless \a fill is 0.
+
+ The rectangle appears sunken if \a sunken is true, otherwise
+ raised.
+
+ \warning This function does not look at QWidget::style() or
+ QApplication::style(). Use the drawing functions in QStyle to make
+ widgets that follow the current GUI style.
+
+ Alternatively you can use a QFrame widget and apply the
+ QFrame::setFrameStyle() function to display a shaded rectangle:
+
+ \snippet doc/src/snippets/code/src_gui_painting_qdrawutil.cpp 6
+
+ \sa qDrawShadeLine(), qDrawShadePanel(), qDrawPlainRect(), QStyle
+*/
+
+void qDrawShadeRect(QPainter *p, const QRect &r,
+ const QPalette &pal, bool sunken,
+ int lineWidth, int midLineWidth,
+ const QBrush *fill)
+{
+ qDrawShadeRect(p, r.x(), r.y(), r.width(), r.height(), pal, sunken,
+ lineWidth, midLineWidth, fill);
+}
+
+/*!
+ \fn void qDrawShadePanel(QPainter *painter, const QRect &rect, const QPalette &palette,
+ bool sunken, int lineWidth, const QBrush *fill)
+ \relates QPainter
+ \overload
+
+ Draws the shaded panel at the rectangle specified by \a rect using the
+ given \a painter and the given \a lineWidth.
+
+ The given \a palette specifies the shading colors (\l
+ {QPalette::light()}{light}, \l {QPalette::dark()}{dark} and \l
+ {QPalette::mid()}{middle} colors). The panel's interior is filled
+ with the \a fill brush unless \a fill is 0.
+
+ The panel appears sunken if \a sunken is true, otherwise raised.
+
+ \warning This function does not look at QWidget::style() or
+ QApplication::style(). Use the drawing functions in QStyle to make
+ widgets that follow the current GUI style.
+
+ Alternatively you can use a QFrame widget and apply the
+ QFrame::setFrameStyle() function to display a shaded panel:
+
+ \snippet doc/src/snippets/code/src_gui_painting_qdrawutil.cpp 7
+
+ \sa qDrawWinPanel(), qDrawShadeLine(), qDrawShadeRect(), QStyle
+*/
+
+void qDrawShadePanel(QPainter *p, const QRect &r,
+ const QPalette &pal, bool sunken,
+ int lineWidth, const QBrush *fill)
+{
+ qDrawShadePanel(p, r.x(), r.y(), r.width(), r.height(), pal, sunken,
+ lineWidth, fill);
+}
+
+/*!
+ \fn void qDrawWinButton(QPainter *painter, const QRect &rect, const QPalette &palette,
+ bool sunken, const QBrush *fill)
+ \relates QPainter
+ \overload
+
+ Draws the Windows-style button at the rectangle specified by \a rect using
+ the given \a painter with a line width of 2 pixels. The button's interior
+ is filled with the \a{fill} brush unless \a fill is 0.
+
+ The given \a palette specifies the shading colors (\l
+ {QPalette::light()}{light}, \l {QPalette::dark()}{dark} and \l
+ {QPalette::mid()}{middle} colors).
+
+ The button appears sunken if \a sunken is true, otherwise raised.
+
+ \warning This function does not look at QWidget::style() or
+ QApplication::style()-> Use the drawing functions in QStyle to make
+ widgets that follow the current GUI style.
+
+ \sa qDrawWinPanel(), QStyle
+*/
+
+void qDrawWinButton(QPainter *p, const QRect &r,
+ const QPalette &pal, bool sunken, const QBrush *fill)
+{
+ qDrawWinButton(p, r.x(), r.y(), r.width(), r.height(), pal, sunken, fill);
+}
+
+/*!
+ \fn void qDrawWinPanel(QPainter *painter, const QRect &rect, const QPalette &palette,
+ bool sunken, const QBrush *fill)
+ \overload
+
+ Draws the Windows-style panel at the rectangle specified by \a rect using
+ the given \a painter with a line width of 2 pixels. The button's interior
+ is filled with the \a fill brush unless \a fill is 0.
+
+ The given \a palette specifies the shading colors. The panel
+ appears sunken if \a sunken is true, otherwise raised.
+
+ \warning This function does not look at QWidget::style() or
+ QApplication::style(). Use the drawing functions in QStyle to make
+ widgets that follow the current GUI style.
+
+ Alternatively you can use a QFrame widget and apply the
+ QFrame::setFrameStyle() function to display a shaded panel:
+
+ \snippet doc/src/snippets/code/src_gui_painting_qdrawutil.cpp 8
+
+ \sa qDrawShadePanel(), qDrawWinButton(), QStyle
+*/
+
+void qDrawWinPanel(QPainter *p, const QRect &r,
+ const QPalette &pal, bool sunken, const QBrush *fill)
+{
+ qDrawWinPanel(p, r.x(), r.y(), r.width(), r.height(), pal, sunken, fill);
+}
+
+/*!
+ \fn void qDrawPlainRect(QPainter *painter, const QRect &rect, const QColor &lineColor, int lineWidth, const QBrush *fill)
+ \relates QPainter
+ \overload
+
+ Draws the plain rectangle specified by \a rect using the given \a painter,
+ \a lineColor and \a lineWidth. The rectangle's interior is filled with the
+ \a fill brush unless \a fill is 0.
+
+ \warning This function does not look at QWidget::style() or
+ QApplication::style(). Use the drawing functions in QStyle to make
+ widgets that follow the current GUI style.
+
+ Alternatively you can use a QFrame widget and apply the
+ QFrame::setFrameStyle() function to display a plain rectangle:
+
+ \snippet doc/src/snippets/code/src_gui_painting_qdrawutil.cpp 9
+
+ \sa qDrawShadeRect(), QStyle
+*/
+
+void qDrawPlainRect(QPainter *p, const QRect &r, const QColor &c,
+ int lineWidth, const QBrush *fill)
+{
+ qDrawPlainRect(p, r.x(), r.y(), r.width(), r.height(), c,
+ lineWidth, fill);
+}
+
+#ifdef QT3_SUPPORT
+static void qDrawWinArrow(QPainter *p, Qt::ArrowType type, bool down,
+ int x, int y, int w, int h,
+ const QPalette &pal, bool enabled)
+{
+ QPolygon a; // arrow polygon
+ switch (type) {
+ case Qt::UpArrow:
+ a.setPoints(7, -3,1, 3,1, -2,0, 2,0, -1,-1, 1,-1, 0,-2);
+ break;
+ case Qt::DownArrow:
+ a.setPoints(7, -3,-1, 3,-1, -2,0, 2,0, -1,1, 1,1, 0,2);
+ break;
+ case Qt::LeftArrow:
+ a.setPoints(7, 1,-3, 1,3, 0,-2, 0,2, -1,-1, -1,1, -2,0);
+ break;
+ case Qt::RightArrow:
+ a.setPoints(7, -1,-3, -1,3, 0,-2, 0,2, 1,-1, 1,1, 2,0);
+ break;
+ default:
+ break;
+ }
+ if (a.isEmpty())
+ return;
+
+ if (down) {
+ x++;
+ y++;
+ }
+
+ QPen savePen = p->pen(); // save current pen
+ if (down)
+ p->setBrushOrigin(p->brushOrigin() + QPoint(1,1));
+ p->fillRect(x, y, w, h, pal.brush(QPalette::Button));
+ if (down)
+ p->setBrushOrigin(p->brushOrigin() - QPoint(1,1));
+ if (enabled) {
+ a.translate(x+w/2, y+h/2);
+ p->setPen(pal.foreground().color());
+ p->drawLine(a.at(0), a.at(1));
+ p->drawLine(a.at(2), a.at(2));
+ p->drawPoint(a[6]);
+ } else {
+ a.translate(x+w/2+1, y+h/2+1);
+ p->setPen(pal.light().color());
+ p->drawLine(a.at(0), a.at(1));
+ p->drawLine(a.at(2), a.at(2));
+ p->drawPoint(a[6]);
+ a.translate(-1, -1);
+ p->setPen(pal.mid().color());
+ p->drawLine(a.at(0), a.at(1));
+ p->drawLine(a.at(2), a.at(2));
+ p->drawPoint(a[6]);
+ }
+ p->setPen(savePen); // restore pen
+}
+#endif // QT3_SUPPORT
+
+#if defined(Q_CC_MSVC)
+#pragma warning(disable: 4244)
+#endif
+
+#ifdef QT3_SUPPORT
+#ifndef QT_NO_STYLE_MOTIF
+// motif arrows look the same whether they are used or not
+// is this correct?
+static void qDrawMotifArrow(QPainter *p, Qt::ArrowType type, bool down,
+ int x, int y, int w, int h,
+ const QPalette &pal, bool)
+{
+ QPolygon bFill; // fill polygon
+ QPolygon bTop; // top shadow.
+ QPolygon bBot; // bottom shadow.
+ QPolygon bLeft; // left shadow.
+ QTransform matrix; // xform matrix
+ bool vertical = type == Qt::UpArrow || type == Qt::DownArrow;
+ bool horizontal = !vertical;
+ int dim = w < h ? w : h;
+ int colspec = 0x0000; // color specification array
+
+ if (dim < 2) // too small arrow
+ return;
+
+ if (dim > 3) {
+ if (dim > 6)
+ bFill.resize(dim & 1 ? 3 : 4);
+ bTop.resize((dim/2)*2);
+ bBot.resize(dim & 1 ? dim + 1 : dim);
+ bLeft.resize(dim > 4 ? 4 : 2);
+ bLeft.putPoints(0, 2, 0,0, 0,dim-1);
+ if (dim > 4)
+ bLeft.putPoints(2, 2, 1,2, 1,dim-3);
+ bTop.putPoints(0, 4, 1,0, 1,1, 2,1, 3,1);
+ bBot.putPoints(0, 4, 1,dim-1, 1,dim-2, 2,dim-2, 3,dim-2);
+
+ for(int i=0; i<dim/2-2 ; i++) {
+ bTop.putPoints(i*2+4, 2, 2+i*2,2+i, 5+i*2, 2+i);
+ bBot.putPoints(i*2+4, 2, 2+i*2,dim-3-i, 5+i*2,dim-3-i);
+ }
+ if (dim & 1) // odd number size: extra line
+ bBot.putPoints(dim-1, 2, dim-3,dim/2, dim-1,dim/2);
+ if (dim > 6) { // dim>6: must fill interior
+ bFill.putPoints(0, 2, 1,dim-3, 1,2);
+ if (dim & 1) // if size is an odd number
+ bFill.setPoint(2, dim - 3, dim / 2);
+ else
+ bFill.putPoints(2, 2, dim-4,dim/2-1, dim-4,dim/2);
+ }
+ }
+ else {
+ if (dim == 3) { // 3x3 arrow pattern
+ bLeft.setPoints(4, 0,0, 0,2, 1,1, 1,1);
+ bTop .setPoints(2, 1,0, 1,0);
+ bBot .setPoints(2, 1,2, 2,1);
+ }
+ else { // 2x2 arrow pattern
+ bLeft.setPoints(2, 0,0, 0,1);
+ bTop .setPoints(2, 1,0, 1,0);
+ bBot .setPoints(2, 1,1, 1,1);
+ }
+ }
+
+ if (type == Qt::UpArrow || type == Qt::LeftArrow) {
+ matrix.translate(x, y);
+ if (vertical) {
+ matrix.translate(0, h - 1);
+ matrix.rotate(-90);
+ } else {
+ matrix.translate(w - 1, h - 1);
+ matrix.rotate(180);
+ }
+ if (down)
+ colspec = horizontal ? 0x2334 : 0x2343;
+ else
+ colspec = horizontal ? 0x1443 : 0x1434;
+ }
+ else if (type == Qt::DownArrow || type == Qt::RightArrow) {
+ matrix.translate(x, y);
+ if (vertical) {
+ matrix.translate(w-1, 0);
+ matrix.rotate(90);
+ }
+ if (down)
+ colspec = horizontal ? 0x2443 : 0x2434;
+ else
+ colspec = horizontal ? 0x1334 : 0x1343;
+ }
+
+ const QColor *cols[5];
+ cols[0] = 0;
+ cols[1] = &pal.button().color();
+ cols[2] = &pal.mid().color();
+ cols[3] = &pal.light().color();
+ cols[4] = &pal.dark().color();
+#define CMID *cols[(colspec>>12) & 0xf]
+#define CLEFT *cols[(colspec>>8) & 0xf]
+#define CTOP *cols[(colspec>>4) & 0xf]
+#define CBOT *cols[colspec & 0xf]
+
+ QPen savePen = p->pen(); // save current pen
+ QBrush saveBrush = p->brush(); // save current brush
+ QTransform wxm = p->transform();
+ QPen pen(Qt::NoPen);
+ const QBrush &brush = pal.brush(QPalette::Button);
+
+ p->setPen(pen);
+ p->setBrush(brush);
+ p->setTransform(matrix, true); // set transformation matrix
+ p->drawPolygon(bFill); // fill arrow
+ p->setBrush(Qt::NoBrush); // don't fill
+
+ p->setPen(CLEFT);
+ p->drawLines(bLeft);
+ p->setPen(CTOP);
+ p->drawLines(bTop);
+ p->setPen(CBOT);
+ p->drawLines(bBot);
+
+ p->setTransform(wxm);
+ p->setBrush(saveBrush); // restore brush
+ p->setPen(savePen); // restore pen
+
+#undef CMID
+#undef CLEFT
+#undef CTOP
+#undef CBOT
+}
+#endif // QT_NO_STYLE_MOTIF
+
+QRect qItemRect(QPainter *p, Qt::GUIStyle gs,
+ int x, int y, int w, int h,
+ int flags,
+ bool enabled,
+ const QPixmap *pixmap,
+ const QString& text, int len)
+{
+ QRect result;
+
+ if (pixmap) {
+ if ((flags & Qt::AlignVCenter) == Qt::AlignVCenter)
+ y += h/2 - pixmap->height()/2;
+ else if ((flags & Qt::AlignBottom) == Qt::AlignBottom)
+ y += h - pixmap->height();
+ if ((flags & Qt::AlignRight) == Qt::AlignRight)
+ x += w - pixmap->width();
+ else if ((flags & Qt::AlignHCenter) == Qt::AlignHCenter)
+ x += w/2 - pixmap->width()/2;
+ else if ((flags & Qt::AlignLeft) != Qt::AlignLeft && QApplication::isRightToLeft())
+ x += w - pixmap->width();
+ result = QRect(x, y, pixmap->width(), pixmap->height());
+ } else if (!text.isNull() && p) {
+ result = p->boundingRect(QRect(x, y, w, h), flags, text.left(len));
+ if (gs == Qt::WindowsStyle && !enabled) {
+ result.setWidth(result.width()+1);
+ result.setHeight(result.height()+1);
+ }
+ } else {
+ result = QRect(x, y, w, h);
+ }
+
+ return result;
+}
+
+void qDrawArrow(QPainter *p, Qt::ArrowType type, Qt::GUIStyle style, bool down,
+ int x, int y, int w, int h,
+ const QPalette &pal, bool enabled)
+{
+ switch (style) {
+ case Qt::WindowsStyle:
+ qDrawWinArrow(p, type, down, x, y, w, h, pal, enabled);
+ break;
+#ifndef QT_NO_STYLE_MOTIF
+ case Qt::MotifStyle:
+ qDrawMotifArrow(p, type, down, x, y, w, h, pal, enabled);
+ break;
+#endif
+ default:
+ qWarning("qDrawArrow: Requested unsupported GUI style");
+ }
+}
+
+void qDrawItem(QPainter *p, Qt::GUIStyle gs,
+ int x, int y, int w, int h,
+ int flags,
+ const QPalette &pal, bool enabled,
+ const QPixmap *pixmap,
+ const QString& text, int len , const QColor* penColor)
+{
+ p->setPen(penColor?*penColor:pal.foreground().color());
+ if (pixmap) {
+ QPixmap pm(*pixmap);
+ bool clip = (flags & Qt::TextDontClip) == 0;
+ if (clip) {
+ if (pm.width() < w && pm.height() < h)
+ clip = false;
+ else
+ p->setClipRect(x, y, w, h);
+ }
+ if ((flags & Qt::AlignVCenter) == Qt::AlignVCenter)
+ y += h/2 - pm.height()/2;
+ else if ((flags & Qt::AlignBottom) == Qt::AlignBottom)
+ y += h - pm.height();
+ if ((flags & Qt::AlignRight) == Qt::AlignRight)
+ x += w - pm.width();
+ else if ((flags & Qt::AlignHCenter) == Qt::AlignHCenter)
+ x += w/2 - pm.width()/2;
+ else if (((flags & Qt::AlignLeft) != Qt::AlignLeft) && QApplication::isRightToLeft()) // Qt::AlignAuto && rightToLeft
+ x += w - pm.width();
+
+ if (!enabled) {
+ if (pm.hasAlphaChannel()) { // pixmap with a mask
+ pm = pm.mask();
+ } else if (pm.depth() == 1) { // monochrome pixmap, no mask
+ ;
+#ifndef QT_NO_IMAGE_HEURISTIC_MASK
+ } else { // color pixmap, no mask
+ QString k;
+ k.sprintf("$qt-drawitem-%llx", pm.cacheKey());
+ if (!QPixmapCache::find(k, pm)) {
+ pm = pm.createHeuristicMask();
+ pm.setMask((QBitmap&)pm);
+ QPixmapCache::insert(k, pm);
+ }
+#endif
+ }
+ if (gs == Qt::WindowsStyle) {
+ p->setPen(pal.light().color());
+ p->drawPixmap(x+1, y+1, pm);
+ p->setPen(pal.text().color());
+ }
+ }
+ p->drawPixmap(x, y, pm);
+ if (clip)
+ p->setClipping(false);
+ } else if (!text.isNull()) {
+ if (gs == Qt::WindowsStyle && !enabled) {
+ p->setPen(pal.light().color());
+ p->drawText(x+1, y+1, w, h, flags, text.left(len));
+ p->setPen(pal.text().color());
+ }
+ p->drawText(x, y, w, h, flags, text.left(len));
+ }
+}
+
+#endif
+
+QT_END_NAMESPACE
diff --git a/src/gui/painting/qdrawutil.h b/src/gui/painting/qdrawutil.h
new file mode 100644
index 0000000000..14901f3c7a
--- /dev/null
+++ b/src/gui/painting/qdrawutil.h
@@ -0,0 +1,140 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QDRAWUTIL_H
+#define QDRAWUTIL_H
+
+#include <QtCore/qnamespace.h>
+#include <QtCore/qstring.h> // char*->QString conversion
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+class QPainter;
+#ifndef QT3_SUPPORT
+class QColorGroup;
+#endif
+class QPalette;
+class QPoint;
+class QColor;
+class QBrush;
+class QRect;
+class QPixmap;
+
+//
+// Standard shade drawing
+//
+
+Q_GUI_EXPORT void qDrawShadeLine(QPainter *p, int x1, int y1, int x2, int y2,
+ const QPalette &pal, bool sunken = true,
+ int lineWidth = 1, int midLineWidth = 0);
+
+Q_GUI_EXPORT void qDrawShadeLine(QPainter *p, const QPoint &p1, const QPoint &p2,
+ const QPalette &pal, bool sunken = true,
+ int lineWidth = 1, int midLineWidth = 0);
+
+Q_GUI_EXPORT void qDrawShadeRect(QPainter *p, int x, int y, int w, int h,
+ const QPalette &pal, bool sunken = false,
+ int lineWidth = 1, int midLineWidth = 0,
+ const QBrush *fill = 0);
+
+Q_GUI_EXPORT void qDrawShadeRect(QPainter *p, const QRect &r,
+ const QPalette &pal, bool sunken = false,
+ int lineWidth = 1, int midLineWidth = 0,
+ const QBrush *fill = 0);
+
+Q_GUI_EXPORT void qDrawShadePanel(QPainter *p, int x, int y, int w, int h,
+ const QPalette &pal, bool sunken = false,
+ int lineWidth = 1, const QBrush *fill = 0);
+
+Q_GUI_EXPORT void qDrawShadePanel(QPainter *p, const QRect &r,
+ const QPalette &pal, bool sunken = false,
+ int lineWidth = 1, const QBrush *fill = 0);
+
+Q_GUI_EXPORT void qDrawWinButton(QPainter *p, int x, int y, int w, int h,
+ const QPalette &pal, bool sunken = false,
+ const QBrush *fill = 0);
+
+Q_GUI_EXPORT void qDrawWinButton(QPainter *p, const QRect &r,
+ const QPalette &pal, bool sunken = false,
+ const QBrush *fill = 0);
+
+Q_GUI_EXPORT void qDrawWinPanel(QPainter *p, int x, int y, int w, int h,
+ const QPalette &pal, bool sunken = false,
+ const QBrush *fill = 0);
+
+Q_GUI_EXPORT void qDrawWinPanel(QPainter *p, const QRect &r,
+ const QPalette &pal, bool sunken = false,
+ const QBrush *fill = 0);
+
+Q_GUI_EXPORT void qDrawPlainRect(QPainter *p, int x, int y, int w, int h, const QColor &,
+ int lineWidth = 1, const QBrush *fill = 0);
+
+Q_GUI_EXPORT void qDrawPlainRect(QPainter *p, const QRect &r, const QColor &,
+ int lineWidth = 1, const QBrush *fill = 0);
+
+
+#ifdef QT3_SUPPORT
+//
+// Use QStyle::itemRect(), QStyle::drawItem() and QStyle::drawArrow() instead.
+//
+Q_GUI_EXPORT QT3_SUPPORT QRect qItemRect(QPainter *p, Qt::GUIStyle gs, int x, int y, int w, int h,
+ int flags, bool enabled,
+ const QPixmap *pixmap, const QString& text, int len=-1);
+
+Q_GUI_EXPORT QT3_SUPPORT void qDrawItem(QPainter *p, Qt::GUIStyle gs, int x, int y, int w, int h,
+ int flags, const QPalette &pal, bool enabled,
+ const QPixmap *pixmap, const QString& text,
+ int len=-1, const QColor* penColor = 0);
+
+Q_GUI_EXPORT QT3_SUPPORT void qDrawArrow(QPainter *p, Qt::ArrowType type, Qt::GUIStyle style, bool down,
+ int x, int y, int w, int h,
+ const QPalette &pal, bool enabled);
+#endif
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QDRAWUTIL_H
diff --git a/src/gui/painting/qemulationpaintengine.cpp b/src/gui/painting/qemulationpaintengine.cpp
new file mode 100644
index 0000000000..3397c45608
--- /dev/null
+++ b/src/gui/painting/qemulationpaintengine.cpp
@@ -0,0 +1,229 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <private/qemulationpaintengine_p.h>
+#include <private/qpainter_p.h>
+#include <private/qtextengine_p.h>
+#include <qdebug.h>
+
+QT_BEGIN_NAMESPACE
+
+QEmulationPaintEngine::QEmulationPaintEngine(QPaintEngineEx *engine)
+ : real_engine(engine)
+{
+ QPaintEngine::state = real_engine->state();
+}
+
+
+QPaintEngine::Type QEmulationPaintEngine::type() const
+{
+ return real_engine->type();
+}
+
+bool QEmulationPaintEngine::begin(QPaintDevice *)
+{
+ return true;
+}
+
+bool QEmulationPaintEngine::end()
+{
+ return true;
+}
+
+
+QPainterState *QEmulationPaintEngine::createState(QPainterState *orig) const
+{
+ return real_engine->createState(orig);
+}
+
+void QEmulationPaintEngine::fill(const QVectorPath &path, const QBrush &brush)
+{
+ QPainterState *s = state();
+
+ if (s->bgMode == Qt::OpaqueMode) {
+ Qt::BrushStyle style = brush.style();
+ if (style >= Qt::Dense1Pattern && style <= Qt::DiagCrossPattern)
+ real_engine->fill(path, s->bgBrush);
+ }
+
+ Qt::BrushStyle style = qbrush_style(brush);
+ if (style >= Qt::LinearGradientPattern && style <= Qt::ConicalGradientPattern) {
+ const QGradient *g = brush.gradient();
+
+ if (g->coordinateMode() > QGradient::LogicalMode) {
+ if (g->coordinateMode() == QGradient::StretchToDeviceMode) {
+ QBrush copy = brush;
+ QTransform mat = copy.transform();
+ mat.scale(real_engine->painter()->device()->width(), real_engine->painter()->device()->height());
+ copy.setTransform(mat);
+ real_engine->fill(path, copy);
+ return;
+ } else if (g->coordinateMode() == QGradient::ObjectBoundingMode) {
+ QBrush copy = brush;
+ QTransform mat = copy.transform();
+ QRealRect r = path.controlPointRect();
+ mat.translate(r.x1, r.y1);
+ mat.scale(r.x2 - r.x1, r.y2 - r.y1);
+ copy.setTransform(mat);
+ real_engine->fill(path, copy);
+ return;
+ }
+ }
+ }
+
+ real_engine->fill(path, brush);
+}
+
+void QEmulationPaintEngine::stroke(const QVectorPath &path, const QPen &pen)
+{
+ QPainterState *s = state();
+
+ if (s->bgMode == Qt::OpaqueMode && pen.style() > Qt::SolidLine) {
+ QPen bgPen = pen;
+ bgPen.setBrush(s->bgBrush);
+ bgPen.setStyle(Qt::SolidLine);
+ real_engine->stroke(path, bgPen);
+ }
+
+
+ QBrush brush = pen.brush();
+ Qt::BrushStyle style = qbrush_style(brush);
+ if (style >= Qt::LinearGradientPattern && style <= Qt::ConicalGradientPattern) {
+ const QGradient *g = brush.gradient();
+ if (g->coordinateMode() > QGradient::LogicalMode) {
+ QPaintEngineEx::stroke(path, pen);
+ return;
+ }
+ }
+
+ real_engine->stroke(path, pen);
+}
+
+void QEmulationPaintEngine::clip(const QVectorPath &path, Qt::ClipOperation op)
+{
+ real_engine->clip(path, op);
+}
+
+void QEmulationPaintEngine::drawPixmap(const QRectF &r, const QPixmap &pm, const QRectF &sr)
+{
+ if (state()->bgMode == Qt::OpaqueMode && pm.isQBitmap())
+ fillBGRect(r);
+ real_engine->drawPixmap(r, pm, sr);
+}
+
+void QEmulationPaintEngine::drawTextItem(const QPointF &p, const QTextItem &textItem)
+{
+ if (state()->bgMode == Qt::OpaqueMode) {
+ const QTextItemInt &ti = static_cast<const QTextItemInt &>(textItem);
+ QRectF rect(p.x(), p.y() - ti.ascent.toReal(), ti.width.toReal(), (ti.ascent + ti.descent + 1).toReal());
+ fillBGRect(rect);
+ }
+ real_engine->drawTextItem(p, textItem);
+}
+
+void QEmulationPaintEngine::drawTiledPixmap(const QRectF &r, const QPixmap &pixmap, const QPointF &s)
+{
+ if (state()->bgMode == Qt::OpaqueMode && pixmap.isQBitmap())
+ fillBGRect(r);
+ real_engine->drawTiledPixmap(r, pixmap, s);
+}
+
+void QEmulationPaintEngine::drawImage(const QRectF &r, const QImage &pm, const QRectF &sr, Qt::ImageConversionFlags flags)
+{
+ real_engine->drawImage(r, pm, sr, flags);
+}
+
+void QEmulationPaintEngine::clipEnabledChanged()
+{
+ real_engine->clipEnabledChanged();
+}
+
+void QEmulationPaintEngine::penChanged()
+{
+ real_engine->penChanged();
+}
+
+void QEmulationPaintEngine::brushChanged()
+{
+ real_engine->brushChanged();
+}
+
+void QEmulationPaintEngine::brushOriginChanged()
+{
+ real_engine->brushOriginChanged();
+}
+
+void QEmulationPaintEngine::opacityChanged()
+{
+ real_engine->opacityChanged();
+}
+
+void QEmulationPaintEngine::compositionModeChanged()
+{
+ real_engine->compositionModeChanged();
+}
+
+void QEmulationPaintEngine::renderHintsChanged()
+{
+ real_engine->renderHintsChanged();
+}
+
+void QEmulationPaintEngine::transformChanged()
+{
+ real_engine->transformChanged();
+}
+
+void QEmulationPaintEngine::setState(QPainterState *s)
+{
+ QPaintEngine::state = s;
+ real_engine->setState(s);
+}
+
+
+void QEmulationPaintEngine::fillBGRect(const QRectF &r)
+{
+ qreal pts[] = { r.x(), r.y(), r.x() + r.width(), r.y(),
+ r.x() + r.width(), r.y() + r.height(), r.x(), r.y() + r.height() };
+ QVectorPath vp(pts, 4, 0, QVectorPath::RectangleHint);
+ real_engine->fill(vp, state()->bgBrush);
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/painting/qemulationpaintengine_p.h b/src/gui/painting/qemulationpaintengine_p.h
new file mode 100644
index 0000000000..d4898c9450
--- /dev/null
+++ b/src/gui/painting/qemulationpaintengine_p.h
@@ -0,0 +1,107 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QEMULATIONPAINTENGINE_P_H
+#define QEMULATIONPAINTENGINE_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/qpaintengineex_p.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+class QEmulationPaintEngine : public QPaintEngineEx
+{
+public:
+ QEmulationPaintEngine(QPaintEngineEx *engine);
+
+ virtual bool begin(QPaintDevice *pdev);
+ virtual bool end();
+
+ virtual Type type() const;
+ virtual QPainterState *createState(QPainterState *orig) const;
+
+ virtual void fill(const QVectorPath &path, const QBrush &brush);
+ virtual void stroke(const QVectorPath &path, const QPen &pen);
+ virtual void clip(const QVectorPath &path, Qt::ClipOperation op);
+
+ virtual void drawPixmap(const QRectF &r, const QPixmap &pm, const QRectF &sr);
+ virtual void drawTextItem(const QPointF &p, const QTextItem &textItem);
+ virtual void drawTiledPixmap(const QRectF &r, const QPixmap &pixmap, const QPointF &s);
+ virtual void drawImage(const QRectF &r, const QImage &pm, const QRectF &sr, Qt::ImageConversionFlags flags);
+
+ virtual void clipEnabledChanged();
+ virtual void penChanged();
+ virtual void brushChanged();
+ virtual void brushOriginChanged();
+ virtual void opacityChanged();
+ virtual void compositionModeChanged();
+ virtual void renderHintsChanged();
+ virtual void transformChanged();
+
+ virtual void setState(QPainterState *s);
+
+ inline QPainterState *state() { return (QPainterState *)QPaintEngine::state; }
+ inline const QPainterState *state() const { return (const QPainterState *)QPaintEngine::state; }
+
+ QPaintEngineEx *real_engine;
+private:
+ void fillBGRect(const QRectF &r);
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif
diff --git a/src/gui/painting/qfixed_p.h b/src/gui/painting/qfixed_p.h
new file mode 100644
index 0000000000..2e49615634
--- /dev/null
+++ b/src/gui/painting/qfixed_p.h
@@ -0,0 +1,219 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QFIXED_P_H
+#define QFIXED_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/qdebug.h"
+#include "QtCore/qpoint.h"
+#include "QtCore/qsize.h"
+
+QT_BEGIN_NAMESPACE
+
+struct QFixed {
+public:
+ QFixed() : val(0) {}
+ QFixed(int i) : val(i<<6) {}
+ QFixed(long i) : val(i<<6) {}
+ QFixed &operator=(int i) { val = (i<<6); return *this; }
+ QFixed &operator=(long i) { val = (i<<6); return *this; }
+
+ static QFixed fromReal(qreal r) { QFixed f; f.val = (int)(r*qreal(64)); return f; }
+ static QFixed fromFixed(int fixed) { QFixed f; f.val = fixed; return f; }
+
+ inline int value() const { return val; }
+ inline void setValue(int value) { val = value; }
+
+ inline int toInt() const { return (((val)+32) & -64)>>6; }
+ inline qreal toReal() const { return ((qreal)val)/(qreal)64; }
+
+ inline int truncate() const { return val>>6; }
+ inline QFixed round() const { QFixed f; f.val = ((val)+32) & -64; return f; }
+ inline QFixed floor() const { QFixed f; f.val = (val) & -64; return f; }
+ inline QFixed ceil() const { QFixed f; f.val = (val+63) & -64; return f; }
+
+ inline QFixed operator+(int i) const { QFixed f; f.val = (val + (i<<6)); return f; }
+ inline QFixed operator+(uint i) const { QFixed f; f.val = (val + (i<<6)); return f; }
+ inline QFixed operator+(const QFixed &other) const { QFixed f; f.val = (val + other.val); return f; }
+ inline QFixed &operator+=(int i) { val += (i<<6); return *this; }
+ inline QFixed &operator+=(uint i) { val += (i<<6); return *this; }
+ inline QFixed &operator+=(const QFixed &other) { val += other.val; return *this; }
+ inline QFixed operator-(int i) const { QFixed f; f.val = (val - (i<<6)); return f; }
+ inline QFixed operator-(uint i) const { QFixed f; f.val = (val - (i<<6)); return f; }
+ inline QFixed operator-(const QFixed &other) const { QFixed f; f.val = (val - other.val); return f; }
+ inline QFixed &operator-=(int i) { val -= (i<<6); return *this; }
+ inline QFixed &operator-=(uint i) { val -= (i<<6); return *this; }
+ inline QFixed &operator-=(const QFixed &other) { val -= other.val; return *this; }
+ inline QFixed operator-() const { QFixed f; f.val = -val; return f; }
+
+ inline bool operator==(const QFixed &other) const { return val == other.val; }
+ inline bool operator!=(const QFixed &other) const { return val != other.val; }
+ inline bool operator<(const QFixed &other) const { return val < other.val; }
+ inline bool operator>(const QFixed &other) const { return val > other.val; }
+ inline bool operator<=(const QFixed &other) const { return val <= other.val; }
+ inline bool operator>=(const QFixed &other) const { return val >= other.val; }
+ inline bool operator!() const { return !val; }
+
+ inline QFixed &operator/=(int x) { val /= x; return *this; }
+ inline QFixed &operator/=(const QFixed &o) {
+ if (o.val == 0) {
+ val = 0x7FFFFFFFL;
+ } else {
+ bool neg = false;
+ qint64 a = val;
+ qint64 b = o.val;
+ if (a < 0) { a = -a; neg = true; }
+ if (b < 0) { b = -b; neg = !neg; }
+
+ int res = (int)(((a << 6) + (b >> 1)) / b);
+
+ val = (neg ? -res : res);
+ }
+ return *this;
+ }
+ inline QFixed operator/(int d) const { QFixed f; f.val = val/d; return f; }
+ inline QFixed operator/(QFixed b) const { QFixed f = *this; return (f /= b); }
+ inline QFixed operator>>(int d) const { QFixed f = *this; f.val >>= d; return f; }
+ inline QFixed &operator*=(int i) { val *= i; return *this; }
+ inline QFixed &operator*=(uint i) { val *= i; return *this; }
+ inline QFixed &operator*=(const QFixed &o) {
+ bool neg = false;
+ qint64 a = val;
+ qint64 b = o.val;
+ if (a < 0) { a = -a; neg = true; }
+ if (b < 0) { b = -b; neg = !neg; }
+
+ int res = (int)((a * b + 0x20L) >> 6);
+ val = neg ? -res : res;
+ return *this;
+ }
+ inline QFixed operator*(int i) const { QFixed f = *this; return (f *= i); }
+ inline QFixed operator*(uint i) const { QFixed f = *this; return (f *= i); }
+ inline QFixed operator*(const QFixed &o) const { QFixed f = *this; return (f *= o); }
+
+private:
+ QFixed(qreal i) : val((int)(i*qreal(64))) {}
+ QFixed &operator=(qreal i) { val = (int)(i*qreal(64)); return *this; }
+ inline QFixed operator+(qreal i) const { QFixed f; f.val = (val + (int)(i*qreal(64))); return f; }
+ inline QFixed &operator+=(qreal i) { val += (int)(i*64); return *this; }
+ inline QFixed operator-(qreal i) const { QFixed f; f.val = (val - (int)(i*qreal(64))); return f; }
+ inline QFixed &operator-=(qreal i) { val -= (int)(i*64); return *this; }
+ inline QFixed &operator/=(qreal r) { val = (int)(val/r); return *this; }
+ inline QFixed operator/(qreal d) const { QFixed f; f.val = (int)(val/d); return f; }
+ inline QFixed &operator*=(qreal d) { val = (int) (val*d); return *this; }
+ inline QFixed operator*(qreal d) const { QFixed f = *this; return (f *= d); }
+ int val;
+};
+Q_DECLARE_TYPEINFO(QFixed, Q_PRIMITIVE_TYPE);
+
+#define QFIXED_MAX (INT_MAX/256)
+
+inline int qRound(const QFixed &f) { return f.toInt(); }
+inline int qFloor(const QFixed &f) { return f.floor().truncate(); }
+
+inline QFixed operator*(int i, const QFixed &d) { return d*i; }
+inline QFixed operator+(int i, const QFixed &d) { return d+i; }
+inline QFixed operator-(int i, const QFixed &d) { return -(d-i); }
+inline QFixed operator*(uint i, const QFixed &d) { return d*i; }
+inline QFixed operator+(uint i, const QFixed &d) { return d+i; }
+inline QFixed operator-(uint i, const QFixed &d) { return -(d-i); }
+// inline QFixed operator*(qreal d, const QFixed &d2) { return d2*d; }
+
+inline bool operator==(const QFixed &f, int i) { return f.value() == (i<<6); }
+inline bool operator==(int i, const QFixed &f) { return f.value() == (i<<6); }
+inline bool operator!=(const QFixed &f, int i) { return f.value() != (i<<6); }
+inline bool operator!=(int i, const QFixed &f) { return f.value() != (i<<6); }
+inline bool operator<=(const QFixed &f, int i) { return f.value() <= (i<<6); }
+inline bool operator<=(int i, const QFixed &f) { return (i<<6) <= f.value(); }
+inline bool operator>=(const QFixed &f, int i) { return f.value() >= (i<<6); }
+inline bool operator>=(int i, const QFixed &f) { return (i<<6) >= f.value(); }
+inline bool operator<(const QFixed &f, int i) { return f.value() < (i<<6); }
+inline bool operator<(int i, const QFixed &f) { return (i<<6) < f.value(); }
+inline bool operator>(const QFixed &f, int i) { return f.value() > (i<<6); }
+inline bool operator>(int i, const QFixed &f) { return (i<<6) > f.value(); }
+
+inline QDebug &operator<<(QDebug &dbg, const QFixed &f)
+{ return dbg << f.toReal(); }
+
+struct QFixedPoint {
+ QFixed x;
+ QFixed y;
+ inline QFixedPoint() {}
+ inline QFixedPoint(const QFixed &_x, const QFixed &_y) : x(_x), y(_y) {}
+ QPointF toPointF() const { return QPointF(x.toReal(), y.toReal()); }
+ static QFixedPoint fromPointF(const QPointF &p) {
+ return QFixedPoint(QFixed::fromReal(p.x()), QFixed::fromReal(p.y()));
+ }
+};
+Q_DECLARE_TYPEINFO(QFixedPoint, Q_PRIMITIVE_TYPE);
+
+inline QFixedPoint operator-(const QFixedPoint &p1, const QFixedPoint &p2)
+{ return QFixedPoint(p1.x - p2.x, p1.y - p2.y); }
+inline QFixedPoint operator+(const QFixedPoint &p1, const QFixedPoint &p2)
+{ return QFixedPoint(p1.x + p2.x, p1.y + p2.y); }
+
+struct QFixedSize {
+ QFixed width;
+ QFixed height;
+ QSizeF toSizeF() const { return QSizeF(width.toReal(), height.toReal()); }
+ static QFixedSize fromSizeF(const QSizeF &s) {
+ QFixedSize size;
+ size.width = QFixed::fromReal(s.width());
+ size.height = QFixed::fromReal(s.height());
+ return size;
+ }
+};
+Q_DECLARE_TYPEINFO(QFixedSize, Q_PRIMITIVE_TYPE);
+
+QT_END_NAMESPACE
+
+#endif // QTEXTENGINE_P_H
diff --git a/src/gui/painting/qgraphicssystem.cpp b/src/gui/painting/qgraphicssystem.cpp
new file mode 100644
index 0000000000..7b1f637f8b
--- /dev/null
+++ b/src/gui/painting/qgraphicssystem.cpp
@@ -0,0 +1,78 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qgraphicssystem_p.h"
+
+#ifdef Q_WS_X11
+# include <private/qpixmap_x11_p.h>
+#endif
+#ifdef Q_WS_WIN
+# include <private/qpixmap_raster_p.h>
+#endif
+#ifdef Q_WS_MAC
+# include <private/qpixmap_mac_p.h>
+#endif
+
+QT_BEGIN_NAMESPACE
+
+QGraphicsSystem::~QGraphicsSystem()
+{
+}
+
+QPixmapData *QGraphicsSystem::createDefaultPixmapData(QPixmapData::PixelType type)
+{
+#ifdef Q_WS_QWS
+ Q_UNUSED(type);
+#endif
+#if defined(Q_WS_X11)
+ return new QX11PixmapData(type);
+#elif defined(Q_WS_WIN)
+ return new QRasterPixmapData(type);
+#elif defined(Q_WS_MAC)
+ return new QMacPixmapData(type);
+#elif !defined(Q_WS_QWS)
+#error QGraphicsSystem::createDefaultPixmapData() not implemented
+#endif
+ return 0;
+}
+
+
+QT_END_NAMESPACE
diff --git a/src/gui/painting/qgraphicssystem_mac.cpp b/src/gui/painting/qgraphicssystem_mac.cpp
new file mode 100644
index 0000000000..555de2f346
--- /dev/null
+++ b/src/gui/painting/qgraphicssystem_mac.cpp
@@ -0,0 +1,59 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qgraphicssystem_mac_p.h"
+
+#include <private/qpixmap_mac_p.h>
+#include <private/qwindowsurface_mac_p.h>
+
+QT_BEGIN_NAMESPACE
+
+QPixmapData *QCoreGraphicsSystem::createPixmapData(QPixmapData::PixelType type) const
+{
+ return new QMacPixmapData(type);
+}
+
+QWindowSurface *QCoreGraphicsSystem::createWindowSurface(QWidget *widget) const
+{
+ return new QMacWindowSurface(widget);
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/painting/qgraphicssystem_mac_p.h b/src/gui/painting/qgraphicssystem_mac_p.h
new file mode 100644
index 0000000000..5abfeeaab6
--- /dev/null
+++ b/src/gui/painting/qgraphicssystem_mac_p.h
@@ -0,0 +1,69 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QGRAPHICSSYSTEM_MAC_P_H
+#define QGRAPHICSSYSTEM_MAC_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 "private/qgraphicssystem_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class Q_GUI_EXPORT QCoreGraphicsSystem : public QGraphicsSystem
+{
+public:
+ QPixmapData *createPixmapData(QPixmapData::PixelType type) const;
+ QWindowSurface *createWindowSurface(QWidget *widget) const;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/gui/painting/qgraphicssystem_p.h b/src/gui/painting/qgraphicssystem_p.h
new file mode 100644
index 0000000000..31bd6362c9
--- /dev/null
+++ b/src/gui/painting/qgraphicssystem_p.h
@@ -0,0 +1,78 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QGRAPHICSSYSTEM_P_H
+#define QGRAPHICSSYSTEM_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/qpixmapdata_p.h"
+#include "private/qwindowsurface_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class QPixmapFilter;
+
+class Q_GUI_EXPORT QGraphicsSystem
+{
+public:
+ virtual QPixmapData *createPixmapData(QPixmapData::PixelType type) const = 0;
+ virtual QWindowSurface *createWindowSurface(QWidget *widget) const = 0;
+
+ virtual ~QGraphicsSystem() = 0;
+
+ //### Remove this & change qpixmap.cpp & qbitmap.cpp once every platform is gaurenteed
+ // to have a graphics system.
+ static QPixmapData *createDefaultPixmapData(QPixmapData::PixelType type);
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/gui/painting/qgraphicssystem_qws.cpp b/src/gui/painting/qgraphicssystem_qws.cpp
new file mode 100644
index 0000000000..2c7c6fd474
--- /dev/null
+++ b/src/gui/painting/qgraphicssystem_qws.cpp
@@ -0,0 +1,62 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <qscreen_qws.h>
+#include "qgraphicssystem_qws_p.h"
+#include <private/qpixmap_raster_p.h>
+#include <private/qwindowsurface_qws_p.h>
+
+QT_BEGIN_NAMESPACE
+
+QPixmapData *QWSGraphicsSystem::createPixmapData(QPixmapData::PixelType type) const
+{
+ if (screen->pixmapDataFactory())
+ return screen->pixmapDataFactory()->create(type); //### For 4.4 compatability
+ else
+ return new QRasterPixmapData(type);
+}
+
+QWindowSurface *QWSGraphicsSystem::createWindowSurface(QWidget *widget) const
+{
+ return screen->createSurface(widget);
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/painting/qgraphicssystem_qws_p.h b/src/gui/painting/qgraphicssystem_qws_p.h
new file mode 100644
index 0000000000..cdee3387b9
--- /dev/null
+++ b/src/gui/painting/qgraphicssystem_qws_p.h
@@ -0,0 +1,79 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QGRAPHICSSYSTEM_QWS_P_H
+#define QGRAPHICSSYSTEM_QWS_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 "qgraphicssystem_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class Q_GUI_EXPORT QWSGraphicsSystem : public QGraphicsSystem
+{
+public:
+
+ QWSGraphicsSystem()
+ : screen(QScreen::instance()) {}
+
+ QWSGraphicsSystem(QScreen* s)
+ : screen(s) {}
+
+ virtual QPixmapData *createPixmapData(QPixmapData::PixelType type) const;
+ QWindowSurface *createWindowSurface(QWidget *widget) const;
+
+private:
+ QScreen* screen;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/gui/painting/qgraphicssystem_raster.cpp b/src/gui/painting/qgraphicssystem_raster.cpp
new file mode 100644
index 0000000000..e4bfe395f4
--- /dev/null
+++ b/src/gui/painting/qgraphicssystem_raster.cpp
@@ -0,0 +1,59 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qgraphicssystem_raster_p.h"
+
+#include "private/qpixmap_raster_p.h"
+#include "private/qwindowsurface_raster_p.h"
+
+QT_BEGIN_NAMESPACE
+
+QPixmapData *QRasterGraphicsSystem::createPixmapData(QPixmapData::PixelType type) const
+{
+ return new QRasterPixmapData(type);
+}
+
+QWindowSurface *QRasterGraphicsSystem::createWindowSurface(QWidget *widget) const
+{
+ return new QRasterWindowSurface(widget);
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/painting/qgraphicssystem_raster_p.h b/src/gui/painting/qgraphicssystem_raster_p.h
new file mode 100644
index 0000000000..c3f78fa0cf
--- /dev/null
+++ b/src/gui/painting/qgraphicssystem_raster_p.h
@@ -0,0 +1,69 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QGRAPHICSSYSTEM_RASTER_P_H
+#define QGRAPHICSSYSTEM_RASTER_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 "qgraphicssystem_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class QRasterGraphicsSystem : public QGraphicsSystem
+{
+public:
+ QPixmapData *createPixmapData(QPixmapData::PixelType type) const;
+ QWindowSurface *createWindowSurface(QWidget *widget) const;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/gui/painting/qgraphicssystemfactory.cpp b/src/gui/painting/qgraphicssystemfactory.cpp
new file mode 100644
index 0000000000..d6afa53f46
--- /dev/null
+++ b/src/gui/painting/qgraphicssystemfactory.cpp
@@ -0,0 +1,110 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qgraphicssystemfactory_p.h"
+#include "qgraphicssystemplugin_p.h"
+#include "private/qfactoryloader_p.h"
+#include "qmutex.h"
+
+#include "qapplication.h"
+#include "qgraphicssystem_raster_p.h"
+#include "qdebug.h"
+
+QT_BEGIN_NAMESPACE
+
+#if !defined(QT_NO_LIBRARY) && !defined(QT_NO_SETTINGS)
+Q_GLOBAL_STATIC_WITH_ARGS(QFactoryLoader, loader,
+ (QGraphicsSystemFactoryInterface_iid, QLatin1String("/graphicssystems"), Qt::CaseInsensitive))
+#endif
+
+QGraphicsSystem *QGraphicsSystemFactory::create(const QString& key)
+{
+ QGraphicsSystem *ret = 0;
+ QString system = key.toLower();
+
+#if defined (QT_GRAPHICSSYSTEM_OPENGL)
+ if (system.isEmpty()) {
+ system = QLatin1String("opengl");
+ }
+#elif defined (QT_GRAPHICSSYSTEM_RASTER) && !defined(Q_WS_WIN)
+ if (system.isEmpty()) {
+ system = QLatin1String("raster");
+ }
+#endif
+
+ if (system == QLatin1String("raster"))
+ return new QRasterGraphicsSystem;
+ else if (system.isEmpty() || system == QLatin1String("native"))
+ return 0;
+
+#if !defined(QT_NO_LIBRARY) && !defined(QT_NO_SETTINGS)
+ if (!ret) {
+ if (QGraphicsSystemFactoryInterface *factory = qobject_cast<QGraphicsSystemFactoryInterface*>(loader()->instance(system)))
+ ret = factory->create(system);
+ }
+#endif
+
+ if (!ret)
+ qWarning() << "Unable to load graphicssystem" << system;
+
+ return ret;
+}
+
+/*!
+ Returns the list of valid keys, i.e. the keys this factory can
+ create styles for.
+
+ \sa create()
+*/
+QStringList QGraphicsSystemFactory::keys()
+{
+#if !defined(QT_NO_LIBRARY) && !defined(QT_NO_SETTINGS)
+ QStringList list = loader()->keys();
+#else
+ QStringList list;
+#endif
+ if (!list.contains(QLatin1String("Raster")))
+ list << QLatin1String("raster");
+ return list;
+}
+
+QT_END_NAMESPACE
+
diff --git a/src/gui/painting/qgraphicssystemfactory_p.h b/src/gui/painting/qgraphicssystemfactory_p.h
new file mode 100644
index 0000000000..9e95324472
--- /dev/null
+++ b/src/gui/painting/qgraphicssystemfactory_p.h
@@ -0,0 +1,78 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QGRAPHICSSYSTEMFACTORY_H
+#define QGRAPHICSSYSTEMFACTORY_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 <QtCore/qstringlist.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+class QGraphicsSystem;
+
+class Q_GUI_EXPORT QGraphicsSystemFactory
+{
+public:
+ static QStringList keys();
+ static QGraphicsSystem *create(const QString&);
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QGRAPHICSSYSTEMFACTORY_H
+
diff --git a/src/gui/painting/qgraphicssystemplugin.cpp b/src/gui/painting/qgraphicssystemplugin.cpp
new file mode 100644
index 0000000000..5f3fc8ef93
--- /dev/null
+++ b/src/gui/painting/qgraphicssystemplugin.cpp
@@ -0,0 +1,56 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qgraphicssystemplugin_p.h"
+#include "qgraphicssystem_p.h"
+
+QT_BEGIN_NAMESPACE
+
+QGraphicsSystemPlugin::QGraphicsSystemPlugin(QObject *parent)
+ : QObject(parent)
+{
+}
+
+QGraphicsSystemPlugin::~QGraphicsSystemPlugin()
+{
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/painting/qgraphicssystemplugin_p.h b/src/gui/painting/qgraphicssystemplugin_p.h
new file mode 100644
index 0000000000..2e70333720
--- /dev/null
+++ b/src/gui/painting/qgraphicssystemplugin_p.h
@@ -0,0 +1,92 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QGRAPHICSSYSTEMPLUGIN_H
+#define QGRAPHICSSYSTEMPLUGIN_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 <QtCore/qplugin.h>
+#include <QtCore/qfactoryinterface.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+class QGraphicsSystem;
+
+struct Q_GUI_EXPORT QGraphicsSystemFactoryInterface : public QFactoryInterface
+{
+ virtual QGraphicsSystem *create(const QString &key) = 0;
+};
+
+#define QGraphicsSystemFactoryInterface_iid "com.trolltech.Qt.QGraphicsSystemFactoryInterface"
+
+Q_DECLARE_INTERFACE(QGraphicsSystemFactoryInterface, QGraphicsSystemFactoryInterface_iid)
+
+class Q_GUI_EXPORT QGraphicsSystemPlugin : public QObject, public QGraphicsSystemFactoryInterface
+{
+ Q_OBJECT
+ Q_INTERFACES(QGraphicsSystemFactoryInterface:QFactoryInterface)
+public:
+ explicit QGraphicsSystemPlugin(QObject *parent = 0);
+ ~QGraphicsSystemPlugin();
+
+ virtual QStringList keys() const = 0;
+ virtual QGraphicsSystem *create(const QString &key) = 0;
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QGRAPHICSSYSTEMEPLUGIN_H
diff --git a/src/gui/painting/qgrayraster.c b/src/gui/painting/qgrayraster.c
new file mode 100644
index 0000000000..73e50dabb9
--- /dev/null
+++ b/src/gui/painting/qgrayraster.c
@@ -0,0 +1,1937 @@
+/****************************************************************************
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/***************************************************************************/
+/* */
+/* qgrayraster.c, derived from ftgrays.c */
+/* */
+/* A new `perfect' anti-aliasing renderer (body). */
+/* */
+/* Copyright 2000-2001, 2002, 2003 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, ../../3rdparty/freetype/docs/FTL.TXT. By continuing to use, */
+/* modify, or distribute this file you indicate that you have read */
+/* the license and understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+ /*************************************************************************/
+ /* */
+ /* This file can be compiled without the rest of the FreeType engine, by */
+ /* defining the _STANDALONE_ macro when compiling it. You also need to */
+ /* put the files `ftgrays.h' and `ftimage.h' into the current */
+ /* compilation directory. Typically, you could do something like */
+ /* */
+ /* - copy `src/smooth/ftgrays.c' (this file) to your current directory */
+ /* */
+ /* - copy `include/freetype/ftimage.h' and `src/smooth/ftgrays.h' to the */
+ /* same directory */
+ /* */
+ /* - compile `ftgrays' with the _STANDALONE_ macro defined, as in */
+ /* */
+ /* cc -c -D_STANDALONE_ ftgrays.c */
+ /* */
+ /* The renderer can be initialized with a call to */
+ /* `qt_ft_gray_raster.raster_new'; an anti-aliased bitmap can be generated */
+ /* with a call to `qt_ft_gray_raster.raster_render'. */
+ /* */
+ /* See the comments and documentation in the file `ftimage.h' for more */
+ /* details on how the raster works. */
+ /* */
+ /*************************************************************************/
+
+ /*************************************************************************/
+ /* */
+ /* This is a new anti-aliasing scan-converter for FreeType 2. The */
+ /* algorithm used here is _very_ different from the one in the standard */
+ /* `ftraster' module. Actually, `ftgrays' computes the _exact_ */
+ /* coverage of the outline on each pixel cell. */
+ /* */
+ /* It is based on ideas that I initially found in Raph Levien's */
+ /* excellent LibArt graphics library (see http://www.levien.com/libart */
+ /* for more information, though the web pages do not tell anything */
+ /* about the renderer; you'll have to dive into the source code to */
+ /* understand how it works). */
+ /* */
+ /* Note, however, that this is a _very_ different implementation */
+ /* compared to Raph's. Coverage information is stored in a very */
+ /* different way, and I don't use sorted vector paths. Also, it doesn't */
+ /* use floating point values. */
+ /* */
+ /* This renderer has the following advantages: */
+ /* */
+ /* - It doesn't need an intermediate bitmap. Instead, one can supply a */
+ /* callback function that will be called by the renderer to draw gray */
+ /* spans on any target surface. You can thus do direct composition on */
+ /* any kind of bitmap, provided that you give the renderer the right */
+ /* callback. */
+ /* */
+ /* - A perfect anti-aliaser, i.e., it computes the _exact_ coverage on */
+ /* each pixel cell. */
+ /* */
+ /* - It performs a single pass on the outline (the `standard' FT2 */
+ /* renderer makes two passes). */
+ /* */
+ /* - It can easily be modified to render to _any_ number of gray levels */
+ /* cheaply. */
+ /* */
+ /* - For small (< 20) pixel sizes, it is faster than the standard */
+ /* renderer. */
+ /* */
+ /*************************************************************************/
+
+/* experimental support for gamma correction within the rasterizer */
+#define xxxGRAYS_USE_GAMMA
+
+
+ /*************************************************************************/
+ /* */
+ /* The macro QT_FT_COMPONENT is used in trace mode. It is an implicit */
+ /* parameter of the QT_FT_TRACE() and QT_FT_ERROR() macros, used to print/log */
+ /* messages during execution. */
+ /* */
+#undef QT_FT_COMPONENT
+#define QT_FT_COMPONENT trace_smooth
+
+
+#define ErrRaster_MemoryOverflow -4
+
+
+#include <string.h> /* for qt_ft_memcpy() */
+#include <setjmp.h>
+#include <limits.h>
+
+#define QT_FT_UINT_MAX UINT_MAX
+
+#define qt_ft_memset memset
+
+#define qt_ft_setjmp setjmp
+#define qt_ft_longjmp longjmp
+#define qt_ft_jmp_buf jmp_buf
+
+#define ErrRaster_Invalid_Mode -2
+#define ErrRaster_Invalid_Outline -1
+#define ErrRaster_Invalid_Argument -3
+#define ErrRaster_Memory_Overflow -4
+
+#define QT_FT_BEGIN_HEADER
+#define QT_FT_END_HEADER
+
+#include <private/qrasterdefs_p.h>
+#include <private/qgrayraster_p.h>
+
+#include <stdlib.h>
+#include <stdio.h>
+
+ /* This macro is used to indicate that a function parameter is unused. */
+ /* Its purpose is simply to reduce compiler warnings. Note also that */
+ /* simply defining it as `(void)x' doesn't avoid warnings with certain */
+ /* ANSI compilers (e.g. LCC). */
+#define QT_FT_UNUSED( x ) (x) = (x)
+
+ /* Disable the tracing mechanism for simplicity -- developers can */
+ /* activate it easily by redefining these two macros. */
+#ifndef QT_FT_ERROR
+#define QT_FT_ERROR( x ) do ; while ( 0 ) /* nothing */
+#endif
+
+#ifndef QT_FT_TRACE
+#define QT_FT_TRACE( x ) do ; while ( 0 ) /* nothing */
+#endif
+
+#ifndef QT_FT_MEM_SET
+#define QT_FT_MEM_SET( d, s, c ) qt_ft_memset( d, s, c )
+#endif
+
+#ifndef QT_FT_MEM_ZERO
+#define QT_FT_MEM_ZERO( dest, count ) QT_FT_MEM_SET( dest, 0, count )
+#endif
+
+ /* define this to dump debugging information */
+#define xxxDEBUG_GRAYS
+
+
+#define RAS_ARG PWorker worker
+#define RAS_ARG_ PWorker worker,
+
+#define RAS_VAR worker
+#define RAS_VAR_ worker,
+
+#define ras (*worker)
+
+
+ /* must be at least 6 bits! */
+#define PIXEL_BITS 8
+
+#define ONE_PIXEL ( 1L << PIXEL_BITS )
+#define PIXEL_MASK ( -1L << PIXEL_BITS )
+#define TRUNC( x ) ( (TCoord)( (x) >> PIXEL_BITS ) )
+#define SUBPIXELS( x ) ( (TPos)(x) << PIXEL_BITS )
+#define FLOOR( x ) ( (x) & -ONE_PIXEL )
+#define CEILING( x ) ( ( (x) + ONE_PIXEL - 1 ) & -ONE_PIXEL )
+#define ROUND( x ) ( ( (x) + ONE_PIXEL / 2 ) & -ONE_PIXEL )
+
+#if PIXEL_BITS >= 6
+#define UPSCALE( x ) ( (x) << ( PIXEL_BITS - 6 ) )
+#define DOWNSCALE( x ) ( (x) >> ( PIXEL_BITS - 6 ) )
+#else
+#define UPSCALE( x ) ( (x) >> ( 6 - PIXEL_BITS ) )
+#define DOWNSCALE( x ) ( (x) << ( 6 - PIXEL_BITS ) )
+#endif
+
+
+ /*************************************************************************/
+ /* */
+ /* TYPE DEFINITIONS */
+ /* */
+
+ /* don't change the following types to QT_FT_Int or QT_FT_Pos, since we might */
+ /* need to define them to "float" or "double" when experimenting with */
+ /* new algorithms */
+
+ typedef int TCoord; /* integer scanline/pixel coordinate */
+ typedef long TPos; /* sub-pixel coordinate */
+
+ /* determine the type used to store cell areas. This normally takes at */
+ /* least PIXEL_BITS*2 + 1 bits. On 16-bit systems, we need to use */
+ /* `long' instead of `int', otherwise bad things happen */
+
+#if PIXEL_BITS <= 7
+
+ typedef int TArea;
+
+#else /* PIXEL_BITS >= 8 */
+
+ /* approximately determine the size of integers using an ANSI-C header */
+#if QT_FT_UINT_MAX == 0xFFFFU
+ typedef long TArea;
+#else
+ typedef int TArea;
+#endif
+
+#endif /* PIXEL_BITS >= 8 */
+
+
+ /* maximal number of gray spans in a call to the span callback */
+#define QT_FT_MAX_GRAY_SPANS 256
+
+
+ typedef struct TCell_* PCell;
+
+ typedef struct TCell_
+ {
+ int x;
+ int cover;
+ TArea area;
+ PCell next;
+
+ } TCell;
+
+
+ typedef struct TWorker_
+ {
+ TCoord ex, ey;
+ TPos min_ex, max_ex;
+ TPos min_ey, max_ey;
+ TPos count_ex, count_ey;
+
+ TArea area;
+ int cover;
+ int invalid;
+
+ PCell cells;
+ int max_cells;
+ int num_cells;
+
+ TCoord cx, cy;
+ TPos x, y;
+
+ TPos last_ey;
+
+ QT_FT_Vector bez_stack[32 * 3 + 1];
+ int lev_stack[32];
+
+ QT_FT_Outline outline;
+ QT_FT_Bitmap target;
+ QT_FT_BBox clip_box;
+
+ QT_FT_Span gray_spans[QT_FT_MAX_GRAY_SPANS];
+ int num_gray_spans;
+
+ QT_FT_Raster_Span_Func render_span;
+ void* render_span_data;
+
+ int band_size;
+ int band_shoot;
+ int conic_level;
+ int cubic_level;
+
+ qt_ft_jmp_buf jump_buffer;
+
+ void* buffer;
+ long buffer_size;
+
+ PCell* ycells;
+ int ycount;
+
+ } TWorker, *PWorker;
+
+
+ typedef struct TRaster_
+ {
+ void* buffer;
+ long buffer_size;
+ int band_size;
+ void* memory;
+ PWorker worker;
+
+ } TRaster, *PRaster;
+
+
+
+ /*************************************************************************/
+ /* */
+ /* Initialize the cells table. */
+ /* */
+ static void
+ gray_init_cells( RAS_ARG_ void* buffer,
+ long byte_size )
+ {
+ ras.buffer = buffer;
+ ras.buffer_size = byte_size;
+
+ ras.ycells = (PCell*) buffer;
+ ras.cells = NULL;
+ ras.max_cells = 0;
+ ras.num_cells = 0;
+ ras.area = 0;
+ ras.cover = 0;
+ ras.invalid = 1;
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* Compute the outline bounding box. */
+ /* */
+ static void
+ gray_compute_cbox( RAS_ARG )
+ {
+ QT_FT_Outline* outline = &ras.outline;
+ QT_FT_Vector* vec = outline->points;
+ QT_FT_Vector* limit = vec + outline->n_points;
+
+
+ if ( outline->n_points <= 0 )
+ {
+ ras.min_ex = ras.max_ex = 0;
+ ras.min_ey = ras.max_ey = 0;
+ return;
+ }
+
+ ras.min_ex = ras.max_ex = vec->x;
+ ras.min_ey = ras.max_ey = vec->y;
+
+ vec++;
+
+ for ( ; vec < limit; vec++ )
+ {
+ TPos x = vec->x;
+ TPos y = vec->y;
+
+
+ if ( x < ras.min_ex ) ras.min_ex = x;
+ if ( x > ras.max_ex ) ras.max_ex = x;
+ if ( y < ras.min_ey ) ras.min_ey = y;
+ if ( y > ras.max_ey ) ras.max_ey = y;
+ }
+
+ /* truncate the bounding box to integer pixels */
+ ras.min_ex = ras.min_ex >> 6;
+ ras.min_ey = ras.min_ey >> 6;
+ ras.max_ex = ( ras.max_ex + 63 ) >> 6;
+ ras.max_ey = ( ras.max_ey + 63 ) >> 6;
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* Record the current cell in the table. */
+ /* */
+ static PCell
+ gray_find_cell( RAS_ARG )
+ {
+ PCell *pcell, cell;
+ int x = ras.ex;
+
+
+ if ( x > ras.max_ex )
+ x = ras.max_ex;
+
+ pcell = &ras.ycells[ras.ey];
+ for (;;)
+ {
+ cell = *pcell;
+ if ( cell == NULL || cell->x > x )
+ break;
+
+ if ( cell->x == x )
+ goto Exit;
+
+ pcell = &cell->next;
+ }
+
+ if ( ras.num_cells >= ras.max_cells )
+ qt_ft_longjmp( ras.jump_buffer, 1 );
+
+ cell = ras.cells + ras.num_cells++;
+ cell->x = x;
+ cell->area = 0;
+ cell->cover = 0;
+
+ cell->next = *pcell;
+ *pcell = cell;
+
+ Exit:
+ return cell;
+ }
+
+
+ static void
+ gray_record_cell( RAS_ARG )
+ {
+ if ( !ras.invalid && ( ras.area | ras.cover ) )
+ {
+ PCell cell = gray_find_cell( RAS_VAR );
+
+
+ cell->area += ras.area;
+ cell->cover += ras.cover;
+ }
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* Set the current cell to a new position. */
+ /* */
+ static void
+ gray_set_cell( RAS_ARG_ TCoord ex,
+ TCoord ey )
+ {
+ /* Move the cell pointer to a new position. We set the `invalid' */
+ /* flag to indicate that the cell isn't part of those we're interested */
+ /* in during the render phase. This means that: */
+ /* */
+ /* . the new vertical position must be within min_ey..max_ey-1. */
+ /* . the new horizontal position must be strictly less than max_ex */
+ /* */
+ /* Note that if a cell is to the left of the clipping region, it is */
+ /* actually set to the (min_ex-1) horizontal position. */
+
+ /* All cells that are on the left of the clipping region go to the */
+ /* min_ex - 1 horizontal position. */
+ ey -= ras.min_ey;
+
+ if ( ex > ras.max_ex )
+ ex = ras.max_ex;
+
+ ex -= ras.min_ex;
+ if ( ex < 0 )
+ ex = -1;
+
+ /* are we moving to a different cell ? */
+ if ( ex != ras.ex || ey != ras.ey )
+ {
+ /* record the current one if it is valid */
+ if ( !ras.invalid )
+ gray_record_cell( RAS_VAR );
+
+ ras.area = 0;
+ ras.cover = 0;
+ }
+
+ ras.ex = ex;
+ ras.ey = ey;
+ ras.invalid = ( (unsigned)ey >= (unsigned)ras.count_ey ||
+ ex >= ras.count_ex );
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* Start a new contour at a given cell. */
+ /* */
+ static void
+ gray_start_cell( RAS_ARG_ TCoord ex,
+ TCoord ey )
+ {
+ if ( ex > ras.max_ex )
+ ex = (TCoord)( ras.max_ex );
+
+ if ( ex < ras.min_ex )
+ ex = (TCoord)( ras.min_ex - 1 );
+
+ ras.area = 0;
+ ras.cover = 0;
+ ras.ex = ex - ras.min_ex;
+ ras.ey = ey - ras.min_ey;
+ ras.last_ey = SUBPIXELS( ey );
+ ras.invalid = 0;
+
+ gray_set_cell( RAS_VAR_ ex, ey );
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* Render a scanline as one or more cells. */
+ /* */
+ static void
+ gray_render_scanline( RAS_ARG_ TCoord ey,
+ TPos x1,
+ TCoord y1,
+ TPos x2,
+ TCoord y2 )
+ {
+ TCoord ex1, ex2, fx1, fx2, delta;
+ long p, first, dx;
+ int incr, lift, mod, rem;
+
+
+ dx = x2 - x1;
+
+ ex1 = TRUNC( x1 );
+ ex2 = TRUNC( x2 );
+ fx1 = (TCoord)( x1 - SUBPIXELS( ex1 ) );
+ fx2 = (TCoord)( x2 - SUBPIXELS( ex2 ) );
+
+ /* trivial case. Happens often */
+ if ( y1 == y2 )
+ {
+ gray_set_cell( RAS_VAR_ ex2, ey );
+ return;
+ }
+
+ /* everything is located in a single cell. That is easy! */
+ /* */
+ if ( ex1 == ex2 )
+ {
+ delta = y2 - y1;
+ ras.area += (TArea)( fx1 + fx2 ) * delta;
+ ras.cover += delta;
+ return;
+ }
+
+ /* ok, we'll have to render a run of adjacent cells on the same */
+ /* scanline... */
+ /* */
+ p = ( ONE_PIXEL - fx1 ) * ( y2 - y1 );
+ first = ONE_PIXEL;
+ incr = 1;
+
+ if ( dx < 0 )
+ {
+ p = fx1 * ( y2 - y1 );
+ first = 0;
+ incr = -1;
+ dx = -dx;
+ }
+
+ delta = (TCoord)( p / dx );
+ mod = (TCoord)( p % dx );
+ if ( mod < 0 )
+ {
+ delta--;
+ mod += (TCoord)dx;
+ }
+
+ ras.area += (TArea)( fx1 + first ) * delta;
+ ras.cover += delta;
+
+ ex1 += incr;
+ gray_set_cell( RAS_VAR_ ex1, ey );
+ y1 += delta;
+
+ if ( ex1 != ex2 )
+ {
+ p = ONE_PIXEL * ( y2 - y1 + delta );
+ lift = (TCoord)( p / dx );
+ rem = (TCoord)( p % dx );
+ if ( rem < 0 )
+ {
+ lift--;
+ rem += (TCoord)dx;
+ }
+
+ mod -= (int)dx;
+
+ while ( ex1 != ex2 )
+ {
+ delta = lift;
+ mod += rem;
+ if ( mod >= 0 )
+ {
+ mod -= (TCoord)dx;
+ delta++;
+ }
+
+ ras.area += (TArea)ONE_PIXEL * delta;
+ ras.cover += delta;
+ y1 += delta;
+ ex1 += incr;
+ gray_set_cell( RAS_VAR_ ex1, ey );
+ }
+ }
+
+ delta = y2 - y1;
+ ras.area += (TArea)( fx2 + ONE_PIXEL - first ) * delta;
+ ras.cover += delta;
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* Render a given line as a series of scanlines. */
+ /* */
+ static void
+ gray_render_line( RAS_ARG_ TPos to_x,
+ TPos to_y )
+ {
+ TCoord ey1, ey2, fy1, fy2;
+ TPos dx, dy, x, x2;
+ long p, first;
+ int delta, rem, mod, lift, incr;
+
+
+ ey1 = TRUNC( ras.last_ey );
+ ey2 = TRUNC( to_y ); /* if (ey2 >= ras.max_ey) ey2 = ras.max_ey-1; */
+ fy1 = (TCoord)( ras.y - ras.last_ey );
+ fy2 = (TCoord)( to_y - SUBPIXELS( ey2 ) );
+
+ dx = to_x - ras.x;
+ dy = to_y - ras.y;
+
+ /* XXX: we should do something about the trivial case where dx == 0, */
+ /* as it happens very often! */
+
+ /* perform vertical clipping */
+ {
+ TCoord min, max;
+
+
+ min = ey1;
+ max = ey2;
+ if ( ey1 > ey2 )
+ {
+ min = ey2;
+ max = ey1;
+ }
+ if ( min >= ras.max_ey || max < ras.min_ey )
+ goto End;
+ }
+
+ /* everything is on a single scanline */
+ if ( ey1 == ey2 )
+ {
+ gray_render_scanline( RAS_VAR_ ey1, ras.x, fy1, to_x, fy2 );
+ goto End;
+ }
+
+ /* vertical line - avoid calling gray_render_scanline */
+ incr = 1;
+
+ if ( dx == 0 )
+ {
+ TCoord ex = TRUNC( ras.x );
+ TCoord two_fx = (TCoord)( ( ras.x - SUBPIXELS( ex ) ) << 1 );
+ TPos area;
+
+
+ first = ONE_PIXEL;
+ if ( dy < 0 )
+ {
+ first = 0;
+ incr = -1;
+ }
+
+ delta = (int)( first - fy1 );
+ ras.area += (TArea)two_fx * delta;
+ ras.cover += delta;
+ ey1 += incr;
+
+ gray_set_cell( &ras, ex, ey1 );
+
+ delta = (int)( first + first - ONE_PIXEL );
+ area = (TArea)two_fx * delta;
+ while ( ey1 != ey2 )
+ {
+ ras.area += area;
+ ras.cover += delta;
+ ey1 += incr;
+
+ gray_set_cell( &ras, ex, ey1 );
+ }
+
+ delta = (int)( fy2 - ONE_PIXEL + first );
+ ras.area += (TArea)two_fx * delta;
+ ras.cover += delta;
+
+ goto End;
+ }
+
+ /* ok, we have to render several scanlines */
+ p = ( ONE_PIXEL - fy1 ) * dx;
+ first = ONE_PIXEL;
+ incr = 1;
+
+ if ( dy < 0 )
+ {
+ p = fy1 * dx;
+ first = 0;
+ incr = -1;
+ dy = -dy;
+ }
+
+ delta = (int)( p / dy );
+ mod = (int)( p % dy );
+ if ( mod < 0 )
+ {
+ delta--;
+ mod += (TCoord)dy;
+ }
+
+ x = ras.x + delta;
+ gray_render_scanline( RAS_VAR_ ey1, ras.x, fy1, x, (TCoord)first );
+
+ ey1 += incr;
+ gray_set_cell( RAS_VAR_ TRUNC( x ), ey1 );
+
+ if ( ey1 != ey2 )
+ {
+ p = ONE_PIXEL * dx;
+ lift = (int)( p / dy );
+ rem = (int)( p % dy );
+ if ( rem < 0 )
+ {
+ lift--;
+ rem += (int)dy;
+ }
+ mod -= (int)dy;
+
+ while ( ey1 != ey2 )
+ {
+ delta = lift;
+ mod += rem;
+ if ( mod >= 0 )
+ {
+ mod -= (int)dy;
+ delta++;
+ }
+
+ x2 = x + delta;
+ gray_render_scanline( RAS_VAR_ ey1, x,
+ (TCoord)( ONE_PIXEL - first ), x2,
+ (TCoord)first );
+ x = x2;
+
+ ey1 += incr;
+ gray_set_cell( RAS_VAR_ TRUNC( x ), ey1 );
+ }
+ }
+
+ gray_render_scanline( RAS_VAR_ ey1, x,
+ (TCoord)( ONE_PIXEL - first ), to_x,
+ fy2 );
+
+ End:
+ ras.x = to_x;
+ ras.y = to_y;
+ ras.last_ey = SUBPIXELS( ey2 );
+ }
+
+
+ static void
+ gray_split_conic( QT_FT_Vector* base )
+ {
+ TPos a, b;
+
+
+ base[4].x = base[2].x;
+ b = base[1].x;
+ a = base[3].x = ( base[2].x + b ) / 2;
+ b = base[1].x = ( base[0].x + b ) / 2;
+ base[2].x = ( a + b ) / 2;
+
+ base[4].y = base[2].y;
+ b = base[1].y;
+ a = base[3].y = ( base[2].y + b ) / 2;
+ b = base[1].y = ( base[0].y + b ) / 2;
+ base[2].y = ( a + b ) / 2;
+ }
+
+
+ static void
+ gray_render_conic( RAS_ARG_ const QT_FT_Vector* control,
+ const QT_FT_Vector* to )
+ {
+ TPos dx, dy;
+ int top, level;
+ int* levels;
+ QT_FT_Vector* arc;
+
+
+ dx = DOWNSCALE( ras.x ) + to->x - ( control->x << 1 );
+ if ( dx < 0 )
+ dx = -dx;
+ dy = DOWNSCALE( ras.y ) + to->y - ( control->y << 1 );
+ if ( dy < 0 )
+ dy = -dy;
+ if ( dx < dy )
+ dx = dy;
+
+ level = 1;
+ dx = dx / ras.conic_level;
+ while ( dx > 0 )
+ {
+ dx >>= 2;
+ level++;
+ }
+
+ /* a shortcut to speed things up */
+ if ( level <= 1 )
+ {
+ /* we compute the mid-point directly in order to avoid */
+ /* calling gray_split_conic() */
+ TPos to_x, to_y, mid_x, mid_y;
+
+
+ to_x = UPSCALE( to->x );
+ to_y = UPSCALE( to->y );
+ mid_x = ( ras.x + to_x + 2 * UPSCALE( control->x ) ) / 4;
+ mid_y = ( ras.y + to_y + 2 * UPSCALE( control->y ) ) / 4;
+
+ gray_render_line( RAS_VAR_ mid_x, mid_y );
+ gray_render_line( RAS_VAR_ to_x, to_y );
+
+ return;
+ }
+
+ arc = ras.bez_stack;
+ levels = ras.lev_stack;
+ top = 0;
+ levels[0] = level;
+
+ arc[0].x = UPSCALE( to->x );
+ arc[0].y = UPSCALE( to->y );
+ arc[1].x = UPSCALE( control->x );
+ arc[1].y = UPSCALE( control->y );
+ arc[2].x = ras.x;
+ arc[2].y = ras.y;
+
+ while ( top >= 0 )
+ {
+ level = levels[top];
+ if ( level > 1 )
+ {
+ /* check that the arc crosses the current band */
+ TPos min, max, y;
+
+
+ min = max = arc[0].y;
+
+ y = arc[1].y;
+ if ( y < min ) min = y;
+ if ( y > max ) max = y;
+
+ y = arc[2].y;
+ if ( y < min ) min = y;
+ if ( y > max ) max = y;
+
+ if ( TRUNC( min ) >= ras.max_ey || TRUNC( max ) < ras.min_ey )
+ goto Draw;
+
+ gray_split_conic( arc );
+ arc += 2;
+ top++;
+ levels[top] = levels[top - 1] = level - 1;
+ continue;
+ }
+
+ Draw:
+ {
+ TPos to_x, to_y, mid_x, mid_y;
+
+
+ to_x = arc[0].x;
+ to_y = arc[0].y;
+ mid_x = ( ras.x + to_x + 2 * arc[1].x ) / 4;
+ mid_y = ( ras.y + to_y + 2 * arc[1].y ) / 4;
+
+ gray_render_line( RAS_VAR_ mid_x, mid_y );
+ gray_render_line( RAS_VAR_ to_x, to_y );
+
+ top--;
+ arc -= 2;
+ }
+ }
+
+ return;
+ }
+
+
+ static void
+ gray_split_cubic( QT_FT_Vector* base )
+ {
+ TPos a, b, c, d;
+
+
+ base[6].x = base[3].x;
+ c = base[1].x;
+ d = base[2].x;
+ base[1].x = a = ( base[0].x + c ) / 2;
+ base[5].x = b = ( base[3].x + d ) / 2;
+ c = ( c + d ) / 2;
+ base[2].x = a = ( a + c ) / 2;
+ base[4].x = b = ( b + c ) / 2;
+ base[3].x = ( a + b ) / 2;
+
+ base[6].y = base[3].y;
+ c = base[1].y;
+ d = base[2].y;
+ base[1].y = a = ( base[0].y + c ) / 2;
+ base[5].y = b = ( base[3].y + d ) / 2;
+ c = ( c + d ) / 2;
+ base[2].y = a = ( a + c ) / 2;
+ base[4].y = b = ( b + c ) / 2;
+ base[3].y = ( a + b ) / 2;
+ }
+
+
+ static void
+ gray_render_cubic( RAS_ARG_ const QT_FT_Vector* control1,
+ const QT_FT_Vector* control2,
+ const QT_FT_Vector* to )
+ {
+ TPos dx, dy, da, db;
+ int top, level;
+ int* levels;
+ QT_FT_Vector* arc;
+
+
+ dx = DOWNSCALE( ras.x ) + to->x - ( control1->x << 1 );
+ if ( dx < 0 )
+ dx = -dx;
+ dy = DOWNSCALE( ras.y ) + to->y - ( control1->y << 1 );
+ if ( dy < 0 )
+ dy = -dy;
+ if ( dx < dy )
+ dx = dy;
+ da = dx;
+
+ dx = DOWNSCALE( ras.x ) + to->x - 3 * ( control1->x + control2->x );
+ if ( dx < 0 )
+ dx = -dx;
+ dy = DOWNSCALE( ras.y ) + to->y - 3 * ( control1->x + control2->y );
+ if ( dy < 0 )
+ dy = -dy;
+ if ( dx < dy )
+ dx = dy;
+ db = dx;
+
+ level = 1;
+ da = da / ras.cubic_level;
+ db = db / ras.conic_level;
+ while ( da > 0 || db > 0 )
+ {
+ da >>= 2;
+ db >>= 3;
+ level++;
+ }
+
+ if ( level <= 1 )
+ {
+ TPos to_x, to_y, mid_x, mid_y;
+
+
+ to_x = UPSCALE( to->x );
+ to_y = UPSCALE( to->y );
+ mid_x = ( ras.x + to_x +
+ 3 * UPSCALE( control1->x + control2->x ) ) / 8;
+ mid_y = ( ras.y + to_y +
+ 3 * UPSCALE( control1->y + control2->y ) ) / 8;
+
+ gray_render_line( RAS_VAR_ mid_x, mid_y );
+ gray_render_line( RAS_VAR_ to_x, to_y );
+ return;
+ }
+
+ arc = ras.bez_stack;
+ arc[0].x = UPSCALE( to->x );
+ arc[0].y = UPSCALE( to->y );
+ arc[1].x = UPSCALE( control2->x );
+ arc[1].y = UPSCALE( control2->y );
+ arc[2].x = UPSCALE( control1->x );
+ arc[2].y = UPSCALE( control1->y );
+ arc[3].x = ras.x;
+ arc[3].y = ras.y;
+
+ levels = ras.lev_stack;
+ top = 0;
+ levels[0] = level;
+
+ while ( top >= 0 )
+ {
+ level = levels[top];
+ if ( level > 1 )
+ {
+ /* check that the arc crosses the current band */
+ TPos min, max, y;
+
+
+ min = max = arc[0].y;
+ y = arc[1].y;
+ if ( y < min ) min = y;
+ if ( y > max ) max = y;
+ y = arc[2].y;
+ if ( y < min ) min = y;
+ if ( y > max ) max = y;
+ y = arc[3].y;
+ if ( y < min ) min = y;
+ if ( y > max ) max = y;
+ if ( TRUNC( min ) >= ras.max_ey || TRUNC( max ) < 0 )
+ goto Draw;
+ gray_split_cubic( arc );
+ arc += 3;
+ top ++;
+ levels[top] = levels[top - 1] = level - 1;
+ continue;
+ }
+
+ Draw:
+ {
+ TPos to_x, to_y, mid_x, mid_y;
+
+
+ to_x = arc[0].x;
+ to_y = arc[0].y;
+ mid_x = ( ras.x + to_x + 3 * ( arc[1].x + arc[2].x ) ) / 8;
+ mid_y = ( ras.y + to_y + 3 * ( arc[1].y + arc[2].y ) ) / 8;
+
+ gray_render_line( RAS_VAR_ mid_x, mid_y );
+ gray_render_line( RAS_VAR_ to_x, to_y );
+ top --;
+ arc -= 3;
+ }
+ }
+
+ return;
+ }
+
+
+
+ static int
+ gray_move_to( const QT_FT_Vector* to,
+ PWorker worker )
+ {
+ TPos x, y;
+
+
+ /* record current cell, if any */
+ gray_record_cell( worker );
+
+ /* start to a new position */
+ x = UPSCALE( to->x );
+ y = UPSCALE( to->y );
+
+ gray_start_cell( worker, TRUNC( x ), TRUNC( y ) );
+
+ worker->x = x;
+ worker->y = y;
+ return 0;
+ }
+
+
+ static int
+ gray_line_to( const QT_FT_Vector* to,
+ PWorker worker )
+ {
+ gray_render_line( worker, UPSCALE( to->x ), UPSCALE( to->y ) );
+ return 0;
+ }
+
+
+ static int
+ gray_conic_to( const QT_FT_Vector* control,
+ const QT_FT_Vector* to,
+ PWorker worker )
+ {
+ gray_render_conic( worker, control, to );
+ return 0;
+ }
+
+
+ static int
+ gray_cubic_to( const QT_FT_Vector* control1,
+ const QT_FT_Vector* control2,
+ const QT_FT_Vector* to,
+ PWorker worker )
+ {
+ gray_render_cubic( worker, control1, control2, to );
+ return 0;
+ }
+
+
+ static void
+ gray_render_span( int count,
+ const QT_FT_Span* spans,
+ PWorker worker )
+ {
+ unsigned char* p;
+ QT_FT_Bitmap* map = &worker->target;
+
+ for ( ; count > 0; count--, spans++ )
+ {
+ unsigned char coverage = spans->coverage;
+
+ /* first of all, compute the scanline offset */
+ p = (unsigned char*)map->buffer - spans->y * map->pitch;
+ if ( map->pitch >= 0 )
+ p += ( map->rows - 1 ) * map->pitch;
+
+
+ if ( coverage )
+ {
+ /* For small-spans it is faster to do it by ourselves than
+ * calling `memset'. This is mainly due to the cost of the
+ * function call.
+ */
+ if ( spans->len >= 8 )
+ QT_FT_MEM_SET( p + spans->x, (unsigned char)coverage, spans->len );
+ else
+ {
+ unsigned char* q = p + spans->x;
+
+
+ switch ( spans->len )
+ {
+ case 7: *q++ = (unsigned char)coverage;
+ case 6: *q++ = (unsigned char)coverage;
+ case 5: *q++ = (unsigned char)coverage;
+ case 4: *q++ = (unsigned char)coverage;
+ case 3: *q++ = (unsigned char)coverage;
+ case 2: *q++ = (unsigned char)coverage;
+ case 1: *q = (unsigned char)coverage;
+ default:
+ ;
+ }
+ }
+ }
+ }
+ }
+
+
+ static void
+ gray_hline( RAS_ARG_ TCoord x,
+ TCoord y,
+ TPos area,
+ int acount )
+ {
+ QT_FT_Span* span;
+ int coverage;
+
+
+ /* compute the coverage line's coverage, depending on the */
+ /* outline fill rule */
+ /* */
+ /* the coverage percentage is area/(PIXEL_BITS*PIXEL_BITS*2) */
+ /* */
+ coverage = (int)( area >> ( PIXEL_BITS * 2 + 1 - 8 ) );
+ /* use range 0..256 */
+ if ( coverage < 0 )
+ coverage = -coverage;
+
+ if ( ras.outline.flags & QT_FT_OUTLINE_EVEN_ODD_FILL )
+ {
+ coverage &= 511;
+
+ if ( coverage > 256 )
+ coverage = 512 - coverage;
+ else if ( coverage == 256 )
+ coverage = 255;
+ }
+ else
+ {
+ /* normal non-zero winding rule */
+ if ( coverage >= 256 )
+ coverage = 255;
+ }
+
+ y += (TCoord)ras.min_ey;
+ x += (TCoord)ras.min_ex;
+
+ /* QT_FT_Span.x is a 16-bit short, so limit our coordinates appropriately */
+ if ( x >= 32768 )
+ x = 32767;
+
+ if ( coverage )
+ {
+ /* see whether we can add this span to the current list */
+ span = ras.gray_spans + ras.num_gray_spans - 1;
+ if ( ras.num_gray_spans > 0 &&
+ span->y == y &&
+ (int)span->x + span->len == (int)x &&
+ span->coverage == coverage )
+ {
+ span->len = (unsigned short)( span->len + acount );
+ return;
+ }
+
+ if ( ras.num_gray_spans >= QT_FT_MAX_GRAY_SPANS )
+ {
+ if ( ras.render_span )
+ ras.render_span( ras.num_gray_spans, ras.gray_spans,
+ ras.render_span_data );
+ /* ras.render_span( span->y, ras.gray_spans, count ); */
+
+#ifdef DEBUG_GRAYS
+
+ if ( 1 )
+ {
+ int n;
+
+
+ fprintf( stderr, "y=%3d ", y );
+ span = ras.gray_spans;
+ for ( n = 0; n < count; n++, span++ )
+ fprintf( stderr, "[%d..%d]:%02x ",
+ span->x, span->x + span->len - 1, span->coverage );
+ fprintf( stderr, "\n" );
+ }
+
+#endif /* DEBUG_GRAYS */
+
+ ras.num_gray_spans = 0;
+
+ span = ras.gray_spans;
+ }
+ else
+ span++;
+
+ /* add a gray span to the current list */
+ span->x = (short)x;
+ span->len = (unsigned short)acount;
+ span->y = (short)y;
+ span->coverage = (unsigned char)coverage;
+
+ ras.num_gray_spans++;
+ }
+ }
+
+
+#ifdef DEBUG_GRAYS
+
+ /* to be called while in the debugger */
+ gray_dump_cells( RAS_ARG )
+ {
+ int yindex;
+
+
+ for ( yindex = 0; yindex < ras.ycount; yindex++ )
+ {
+ PCell cell;
+
+
+ printf( "%3d:", yindex );
+
+ for ( cell = ras.ycells[yindex]; cell != NULL; cell = cell->next )
+ printf( " (%3d, c:%4d, a:%6d)", cell->x, cell->cover, cell->area );
+ printf( "\n" );
+ }
+ }
+
+#endif /* DEBUG_GRAYS */
+
+
+ static void
+ gray_sweep( RAS_ARG_ const QT_FT_Bitmap* target )
+ {
+ int yindex;
+
+ QT_FT_UNUSED( target );
+
+
+ if ( ras.num_cells == 0 )
+ return;
+
+ for ( yindex = 0; yindex < ras.ycount; yindex++ )
+ {
+ PCell cell = ras.ycells[yindex];
+ TCoord cover = 0;
+ TCoord x = 0;
+
+
+ for ( ; cell != NULL; cell = cell->next )
+ {
+ TArea area;
+
+
+ if ( cell->x > x && cover != 0 )
+ gray_hline( RAS_VAR_ x, yindex, cover * ( ONE_PIXEL * 2 ),
+ cell->x - x );
+
+ cover += cell->cover;
+ area = cover * ( ONE_PIXEL * 2 ) - cell->area;
+
+ if ( area != 0 && cell->x >= 0 )
+ gray_hline( RAS_VAR_ cell->x, yindex, area, 1 );
+
+ x = cell->x + 1;
+ }
+
+ if ( ras.count_ex > x && cover != 0 )
+ gray_hline( RAS_VAR_ x, yindex, cover * ( ONE_PIXEL * 2 ),
+ ras.count_ex - x );
+ }
+ }
+
+ /*************************************************************************/
+ /* */
+ /* The following function should only compile in stand_alone mode, */
+ /* i.e., when building this component without the rest of FreeType. */
+ /* */
+ /*************************************************************************/
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* QT_FT_Outline_Decompose */
+ /* */
+ /* <Description> */
+ /* Walks over an outline's structure to decompose it into individual */
+ /* segments and Bezier arcs. This function is also able to emit */
+ /* `move to' and `close to' operations to indicate the start and end */
+ /* of new contours in the outline. */
+ /* */
+ /* <Input> */
+ /* outline :: A pointer to the source target. */
+ /* */
+ /* func_interface :: A table of `emitters', i.e,. function pointers */
+ /* called during decomposition to indicate path */
+ /* operations. */
+ /* */
+ /* user :: A typeless pointer which is passed to each */
+ /* emitter during the decomposition. It can be */
+ /* used to store the state during the */
+ /* decomposition. */
+ /* */
+ /* <Return> */
+ /* Error code. 0 means success. */
+ /* */
+ static
+ int QT_FT_Outline_Decompose( const QT_FT_Outline* outline,
+ const QT_FT_Outline_Funcs* func_interface,
+ void* user )
+ {
+#undef SCALED
+#if 0
+#define SCALED( x ) ( ( (x) << shift ) - delta )
+#else
+#define SCALED( x ) (x)
+#endif
+
+ QT_FT_Vector v_last;
+ QT_FT_Vector v_control;
+ QT_FT_Vector v_start;
+
+ QT_FT_Vector* point;
+ QT_FT_Vector* limit;
+ char* tags;
+
+ int n; /* index of contour in outline */
+ int first; /* index of first point in contour */
+ int error;
+ char tag; /* current point's state */
+
+#if 0
+ int shift = func_interface->shift;
+ TPos delta = func_interface->delta;
+#endif
+
+
+ first = 0;
+
+ for ( n = 0; n < outline->n_contours; n++ )
+ {
+ int last; /* index of last point in contour */
+
+
+ last = outline->contours[n];
+ limit = outline->points + last;
+
+ v_start = outline->points[first];
+ v_last = outline->points[last];
+
+ v_start.x = SCALED( v_start.x );
+ v_start.y = SCALED( v_start.y );
+
+ v_last.x = SCALED( v_last.x );
+ v_last.y = SCALED( v_last.y );
+
+ v_control = v_start;
+
+ point = outline->points + first;
+ tags = outline->tags + first;
+ tag = QT_FT_CURVE_TAG( tags[0] );
+
+ /* A contour cannot start with a cubic control point! */
+ if ( tag == QT_FT_CURVE_TAG_CUBIC )
+ goto Invalid_Outline;
+
+ /* check first point to determine origin */
+ if ( tag == QT_FT_CURVE_TAG_CONIC )
+ {
+ /* first point is conic control. Yes, this happens. */
+ if ( QT_FT_CURVE_TAG( outline->tags[last] ) == QT_FT_CURVE_TAG_ON )
+ {
+ /* start at last point if it is on the curve */
+ v_start = v_last;
+ limit--;
+ }
+ else
+ {
+ /* if both first and last points are conic, */
+ /* start at their middle and record its position */
+ /* for closure */
+ v_start.x = ( v_start.x + v_last.x ) / 2;
+ v_start.y = ( v_start.y + v_last.y ) / 2;
+
+ v_last = v_start;
+ }
+ point--;
+ tags--;
+ }
+
+ error = func_interface->move_to( &v_start, user );
+ if ( error )
+ goto Exit;
+
+ while ( point < limit )
+ {
+ point++;
+ tags++;
+
+ tag = QT_FT_CURVE_TAG( tags[0] );
+ switch ( tag )
+ {
+ case QT_FT_CURVE_TAG_ON: /* emit a single line_to */
+ {
+ QT_FT_Vector vec;
+
+
+ vec.x = SCALED( point->x );
+ vec.y = SCALED( point->y );
+
+ error = func_interface->line_to( &vec, user );
+ if ( error )
+ goto Exit;
+ continue;
+ }
+
+ case QT_FT_CURVE_TAG_CONIC: /* consume conic arcs */
+ {
+ v_control.x = SCALED( point->x );
+ v_control.y = SCALED( point->y );
+
+ Do_Conic:
+ if ( point < limit )
+ {
+ QT_FT_Vector vec;
+ QT_FT_Vector v_middle;
+
+
+ point++;
+ tags++;
+ tag = QT_FT_CURVE_TAG( tags[0] );
+
+ vec.x = SCALED( point->x );
+ vec.y = SCALED( point->y );
+
+ if ( tag == QT_FT_CURVE_TAG_ON )
+ {
+ error = func_interface->conic_to( &v_control, &vec,
+ user );
+ if ( error )
+ goto Exit;
+ continue;
+ }
+
+ if ( tag != QT_FT_CURVE_TAG_CONIC )
+ goto Invalid_Outline;
+
+ v_middle.x = ( v_control.x + vec.x ) / 2;
+ v_middle.y = ( v_control.y + vec.y ) / 2;
+
+ error = func_interface->conic_to( &v_control, &v_middle,
+ user );
+ if ( error )
+ goto Exit;
+
+ v_control = vec;
+ goto Do_Conic;
+ }
+
+ error = func_interface->conic_to( &v_control, &v_start,
+ user );
+ goto Close;
+ }
+
+ default: /* QT_FT_CURVE_TAG_CUBIC */
+ {
+ QT_FT_Vector vec1, vec2;
+
+
+ if ( point + 1 > limit ||
+ QT_FT_CURVE_TAG( tags[1] ) != QT_FT_CURVE_TAG_CUBIC )
+ goto Invalid_Outline;
+
+ point += 2;
+ tags += 2;
+
+ vec1.x = SCALED( point[-2].x );
+ vec1.y = SCALED( point[-2].y );
+
+ vec2.x = SCALED( point[-1].x );
+ vec2.y = SCALED( point[-1].y );
+
+ if ( point <= limit )
+ {
+ QT_FT_Vector vec;
+
+
+ vec.x = SCALED( point->x );
+ vec.y = SCALED( point->y );
+
+ error = func_interface->cubic_to( &vec1, &vec2, &vec, user );
+ if ( error )
+ goto Exit;
+ continue;
+ }
+
+ error = func_interface->cubic_to( &vec1, &vec2, &v_start, user );
+ goto Close;
+ }
+ }
+ }
+
+ /* close the contour with a line segment */
+ error = func_interface->line_to( &v_start, user );
+
+ Close:
+ if ( error )
+ goto Exit;
+
+ first = last + 1;
+ }
+
+ return 0;
+
+ Exit:
+ return error;
+
+ Invalid_Outline:
+ return ErrRaster_Invalid_Outline;
+ }
+
+ typedef struct TBand_
+ {
+ TPos min, max;
+
+ } TBand;
+
+
+ static int
+ gray_convert_glyph_inner( RAS_ARG )
+ {
+ static
+ const QT_FT_Outline_Funcs func_interface =
+ {
+ (QT_FT_Outline_MoveTo_Func) gray_move_to,
+ (QT_FT_Outline_LineTo_Func) gray_line_to,
+ (QT_FT_Outline_ConicTo_Func)gray_conic_to,
+ (QT_FT_Outline_CubicTo_Func)gray_cubic_to,
+ 0,
+ 0
+ };
+
+ volatile int error = 0;
+
+ if ( qt_ft_setjmp( ras.jump_buffer ) == 0 )
+ {
+ error = QT_FT_Outline_Decompose( &ras.outline, &func_interface, &ras );
+ gray_record_cell( RAS_VAR );
+ }
+ else
+ {
+ error = ErrRaster_Memory_Overflow;
+ }
+
+ return error;
+ }
+
+
+ static int
+ gray_convert_glyph( RAS_ARG )
+ {
+ TBand bands[40];
+ TBand* volatile band;
+ int volatile n, num_bands;
+ TPos volatile min, max, max_y;
+ QT_FT_BBox* clip;
+
+ ras.num_gray_spans = 0;
+
+ /* Set up state in the raster object */
+ gray_compute_cbox( RAS_VAR );
+
+ /* clip to target bitmap, exit if nothing to do */
+ clip = &ras.clip_box;
+
+ if ( ras.max_ex <= clip->xMin || ras.min_ex >= clip->xMax ||
+ ras.max_ey <= clip->yMin || ras.min_ey >= clip->yMax )
+ return 0;
+
+ if ( ras.min_ex < clip->xMin ) ras.min_ex = clip->xMin;
+ if ( ras.min_ey < clip->yMin ) ras.min_ey = clip->yMin;
+
+ if ( ras.max_ex > clip->xMax ) ras.max_ex = clip->xMax;
+ if ( ras.max_ey > clip->yMax ) ras.max_ey = clip->yMax;
+
+ ras.count_ex = ras.max_ex - ras.min_ex;
+ ras.count_ey = ras.max_ey - ras.min_ey;
+
+ /* simple heuristic used to speed-up the bezier decomposition -- see */
+ /* the code in gray_render_conic() and gray_render_cubic() for more */
+ /* details */
+ ras.conic_level = 32;
+ ras.cubic_level = 16;
+
+ {
+ int level = 0;
+
+
+ if ( ras.count_ex > 24 || ras.count_ey > 24 )
+ level++;
+ if ( ras.count_ex > 120 || ras.count_ey > 120 )
+ level++;
+
+ ras.conic_level <<= level;
+ ras.cubic_level <<= level;
+ }
+
+ /* setup vertical bands */
+ num_bands = (int)( ( ras.max_ey - ras.min_ey ) / ras.band_size );
+ if ( num_bands == 0 ) num_bands = 1;
+ if ( num_bands >= 39 ) num_bands = 39;
+
+ ras.band_shoot = 0;
+
+ min = ras.min_ey;
+ max_y = ras.max_ey;
+
+ for ( n = 0; n < num_bands; n++, min = max )
+ {
+ max = min + ras.band_size;
+ if ( n == num_bands - 1 || max > max_y )
+ max = max_y;
+
+ bands[0].min = min;
+ bands[0].max = max;
+ band = bands;
+
+ while ( band >= bands )
+ {
+ TPos bottom, top, middle;
+ int error;
+
+ {
+ PCell cells_max;
+ int yindex;
+ long cell_start, cell_end, cell_mod;
+
+
+ ras.ycells = (PCell*)ras.buffer;
+ ras.ycount = band->max - band->min;
+
+ cell_start = sizeof ( PCell ) * ras.ycount;
+ cell_mod = cell_start % sizeof ( TCell );
+ if ( cell_mod > 0 )
+ cell_start += sizeof ( TCell ) - cell_mod;
+
+ cell_end = ras.buffer_size;
+ cell_end -= cell_end % sizeof( TCell );
+
+ cells_max = (PCell)( (char*)ras.buffer + cell_end );
+ ras.cells = (PCell)( (char*)ras.buffer + cell_start );
+ if ( ras.cells >= cells_max )
+ goto ReduceBands;
+
+ ras.max_cells = (int)(cells_max - ras.cells);
+ if ( ras.max_cells < 2 )
+ goto ReduceBands;
+
+ for ( yindex = 0; yindex < ras.ycount; yindex++ )
+ ras.ycells[yindex] = NULL;
+ }
+
+ ras.num_cells = 0;
+ ras.invalid = 1;
+ ras.min_ey = band->min;
+ ras.max_ey = band->max;
+ ras.count_ey = band->max - band->min;
+
+ error = gray_convert_glyph_inner( RAS_VAR );
+
+ if ( !error )
+ {
+ gray_sweep( RAS_VAR_ &ras.target );
+ band--;
+ continue;
+ }
+ else if ( error != ErrRaster_Memory_Overflow )
+ return 1;
+
+ ReduceBands:
+ /* render pool overflow; we will reduce the render band by half */
+ bottom = band->min;
+ top = band->max;
+ middle = bottom + ( ( top - bottom ) >> 1 );
+
+ /* This is too complex for a single scanline; there must */
+ /* be some problems. */
+ if ( middle == bottom )
+ {
+#ifdef DEBUG_GRAYS
+ fprintf( stderr, "Rotten glyph!\n" );
+#endif
+ /* == Raster_Err_OutOfMemory in qblackraster.c */
+ return -6;
+ }
+
+ if ( bottom-top >= ras.band_size )
+ ras.band_shoot++;
+
+ band[1].min = bottom;
+ band[1].max = middle;
+ band[0].min = middle;
+ band[0].max = top;
+ band++;
+ }
+ }
+
+ if ( ras.render_span && ras.num_gray_spans > 0 )
+ ras.render_span( ras.num_gray_spans,
+ ras.gray_spans, ras.render_span_data );
+
+ if ( ras.band_shoot > 8 && ras.band_size > 16 )
+ ras.band_size = ras.band_size / 2;
+
+ return 0;
+ }
+
+
+ static int
+ gray_raster_render( PRaster raster,
+ const QT_FT_Raster_Params* params )
+ {
+ const QT_FT_Outline* outline = (const QT_FT_Outline*)params->source;
+ const QT_FT_Bitmap* target_map = params->target;
+ PWorker worker;
+
+
+ if ( !raster || !raster->buffer || !raster->buffer_size )
+ return ErrRaster_Invalid_Argument;
+
+ /* return immediately if the outline is empty */
+ if ( outline->n_points == 0 || outline->n_contours <= 0 )
+ return 0;
+
+ if ( !outline || !outline->contours || !outline->points )
+ return ErrRaster_Invalid_Outline;
+
+ if ( outline->n_points !=
+ outline->contours[outline->n_contours - 1] + 1 )
+ return ErrRaster_Invalid_Outline;
+
+ worker = raster->worker;
+
+ /* if direct mode is not set, we must have a target bitmap */
+ if ( ( params->flags & QT_FT_RASTER_FLAG_DIRECT ) == 0 )
+ {
+ if ( !target_map )
+ return ErrRaster_Invalid_Argument;
+
+ /* nothing to do */
+ if ( !target_map->width || !target_map->rows )
+ return 0;
+
+ if ( !target_map->buffer )
+ return ErrRaster_Invalid_Argument;
+ }
+
+ /* this version does not support monochrome rendering */
+ if ( !( params->flags & QT_FT_RASTER_FLAG_AA ) )
+ return ErrRaster_Invalid_Mode;
+
+ /* compute clipping box */
+ if ( ( params->flags & QT_FT_RASTER_FLAG_DIRECT ) == 0 )
+ {
+ /* compute clip box from target pixmap */
+ ras.clip_box.xMin = 0;
+ ras.clip_box.yMin = 0;
+ ras.clip_box.xMax = target_map->width;
+ ras.clip_box.yMax = target_map->rows;
+ }
+ else if ( params->flags & QT_FT_RASTER_FLAG_CLIP )
+ {
+ ras.clip_box = params->clip_box;
+ }
+ else
+ {
+ ras.clip_box.xMin = -32768L;
+ ras.clip_box.yMin = -32768L;
+ ras.clip_box.xMax = 32767L;
+ ras.clip_box.yMax = 32767L;
+ }
+
+ gray_init_cells( worker, raster->buffer, raster->buffer_size );
+
+ ras.outline = *outline;
+ ras.num_cells = 0;
+ ras.invalid = 1;
+ ras.band_size = raster->band_size;
+
+ if ( target_map )
+ ras.target = *target_map;
+
+ ras.render_span = (QT_FT_Raster_Span_Func)gray_render_span;
+ ras.render_span_data = &ras;
+
+ if ( params->flags & QT_FT_RASTER_FLAG_DIRECT )
+ {
+ ras.render_span = (QT_FT_Raster_Span_Func)params->gray_spans;
+ ras.render_span_data = params->user;
+ }
+
+ return gray_convert_glyph( worker );
+ }
+
+
+ /**** RASTER OBJECT CREATION: In standalone mode, we simply use *****/
+ /**** a static object. *****/
+
+ static int
+ gray_raster_new( void * memory,
+ QT_FT_Raster* araster )
+ {
+ if (memory)
+ fprintf(stderr, "gray_raster_new(), memory ignored");
+ memory = malloc(sizeof(TRaster));
+ QT_FT_MEM_ZERO(memory, sizeof(TRaster));
+
+ *araster = (QT_FT_Raster) memory;
+ return 0;
+ }
+
+
+ static void
+ gray_raster_done( QT_FT_Raster raster )
+ {
+ free(raster);
+ }
+
+
+ static void
+ gray_raster_reset( QT_FT_Raster raster,
+ char* pool_base,
+ long pool_size )
+ {
+ PRaster rast = (PRaster)raster;
+
+
+ if ( raster )
+ {
+ if ( pool_base && pool_size >= (long)sizeof ( TWorker ) + 2048 )
+ {
+ PWorker worker = (PWorker)pool_base;
+
+
+ rast->worker = worker;
+ rast->buffer = pool_base +
+ ( ( sizeof ( TWorker ) + sizeof ( TCell ) - 1 ) &
+ ~( sizeof ( TCell ) - 1 ) );
+ rast->buffer_size = (long)( ( pool_base + pool_size ) -
+ (char*)rast->buffer ) &
+ ~( sizeof ( TCell ) - 1 );
+ rast->band_size = (int)( rast->buffer_size /
+ ( sizeof ( TCell ) * 8 ) );
+ }
+ else
+ {
+ rast->buffer = NULL;
+ rast->buffer_size = 0;
+ rast->worker = NULL;
+ }
+ }
+ }
+
+ const QT_FT_Raster_Funcs qt_ft_grays_raster =
+ {
+ QT_FT_GLYPH_FORMAT_OUTLINE,
+
+ (QT_FT_Raster_New_Func) gray_raster_new,
+ (QT_FT_Raster_Reset_Func) gray_raster_reset,
+ (QT_FT_Raster_Set_Mode_Func)0,
+ (QT_FT_Raster_Render_Func) gray_raster_render,
+ (QT_FT_Raster_Done_Func) gray_raster_done
+ };
+
+/* END */
diff --git a/src/gui/painting/qgrayraster_p.h b/src/gui/painting/qgrayraster_p.h
new file mode 100644
index 0000000000..06ba7e988f
--- /dev/null
+++ b/src/gui/painting/qgrayraster_p.h
@@ -0,0 +1,101 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/***************************************************************************/
+/* */
+/* qgrayraster_p.h, derived from ftgrays.h */
+/* */
+/* FreeType smooth renderer declaration */
+/* */
+/* Copyright 1996-2001 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, ../../3rdparty/freetype/docs/FTL.TXT. By continuing to use, */
+/* modify, or distribute this file you indicate that you have read */
+/* the license and understand and accept it fully. */
+/***************************************************************************/
+
+
+#ifndef __FTGRAYS_H__
+#define __FTGRAYS_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.
+*/
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+
+#include <private/qrasterdefs_p.h>
+
+ /*************************************************************************/
+ /* */
+ /* To make ftgrays.h independent from configuration files we check */
+ /* whether QT_FT_EXPORT_VAR has been defined already. */
+ /* */
+ /* On some systems and compilers (Win32 mostly), an extra keyword is */
+ /* necessary to compile the library as a DLL. */
+ /* */
+#ifndef QT_FT_EXPORT_VAR
+#define QT_FT_EXPORT_VAR( x ) extern x
+#endif
+
+ QT_FT_EXPORT_VAR( const QT_FT_Raster_Funcs ) qt_ft_grays_raster;
+
+
+#ifdef __cplusplus
+ }
+#endif
+
+#endif /* __FTGRAYS_H__ */
+
+/* END */
diff --git a/src/gui/painting/qimagescale.cpp b/src/gui/painting/qimagescale.cpp
new file mode 100644
index 0000000000..c68942c8fa
--- /dev/null
+++ b/src/gui/painting/qimagescale.cpp
@@ -0,0 +1,1031 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#include <private/qimagescale_p.h>
+#include <private/qdrawhelper_p.h>
+
+#include "qimage.h"
+#include "qcolor.h"
+
+QT_BEGIN_NAMESPACE
+
+namespace QImageScale {
+ struct QImageScaleInfo;
+}
+
+typedef void (*qt_qimageScaleFunc)(QImageScale::QImageScaleInfo *isi, unsigned int *dest,
+ int dxx, int dyy, int dx, int dy, int dw,
+ int dh, int dow, int sow);
+
+static void qt_qimageScaleAARGB(QImageScale::QImageScaleInfo *isi, unsigned int *dest,
+ int dxx, int dyy, int dx, int dy, int dw,
+ int dh, int dow, int sow);
+
+static void qt_qimageScaleAARGBA(QImageScale::QImageScaleInfo *isi, unsigned int *dest,
+ int dxx, int dyy, int dx, int dy, int dw,
+ int dh, int dow, int sow);
+
+qt_qimageScaleFunc qt_qimageScaleArgb = qt_qimageScaleAARGBA;
+qt_qimageScaleFunc qt_qimageScaleRgb = qt_qimageScaleAARGB;
+
+
+/*
+ * Copyright (C) 2004, 2005 Daniel M. Duley
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+/* OTHER CREDITS:
+ *
+ * This is the normal smoothscale method, based on Imlib2's smoothscale.
+ *
+ * Originally I took the algorithm used in NetPBM and Qt and added MMX/3dnow
+ * optimizations. It ran in about 1/2 the time as Qt. Then I ported Imlib's
+ * C algorithm and it ran at about the same speed as my MMX optimized one...
+ * Finally I ported Imlib's MMX version and it ran in less than half the
+ * time as my MMX algorithm, (taking only a quarter of the time Qt does).
+ * After further optimization it seems to run at around 1/6th.
+ *
+ * Changes include formatting, namespaces and other C++'ings, removal of old
+ * #ifdef'ed code, and removal of unneeded border calculation code.
+ *
+ * Imlib2 is (C) Carsten Haitzler and various contributors. The MMX code
+ * is by Willem Monsuwe <willem@stack.nl>. All other modifications are
+ * (C) Daniel M. Duley.
+ */
+
+
+namespace QImageScale {
+ struct QImageScaleInfo {
+ int *xpoints;
+ unsigned int **ypoints;
+ int *xapoints, *yapoints;
+ int xup_yup;
+ };
+
+ unsigned int** qimageCalcYPoints(unsigned int *src, int sw, int sh,
+ int dh);
+ int* qimageCalcXPoints(int sw, int dw);
+ int* qimageCalcApoints(int s, int d, int up);
+ QImageScaleInfo* qimageFreeScaleInfo(QImageScaleInfo *isi);
+ QImageScaleInfo *qimageCalcScaleInfo(const QImage &img, int sw, int sh,
+ int dw, int dh, char aa);
+}
+
+using namespace QImageScale;
+
+//
+// Code ported from Imlib...
+//
+
+// FIXME: replace with qRed, etc... These work on pointers to pixels, not
+// pixel values
+#define A_VAL(p) (qAlpha(*p))
+#define R_VAL(p) (qRed(*p))
+#define G_VAL(p) (qGreen(*p))
+#define B_VAL(p) (qBlue(*p))
+
+#define INV_XAP (256 - xapoints[x])
+#define XAP (xapoints[x])
+#define INV_YAP (256 - yapoints[dyy + y])
+#define YAP (yapoints[dyy + y])
+
+unsigned int** QImageScale::qimageCalcYPoints(unsigned int *src,
+ int sw, int sh, int dh)
+{
+ unsigned int **p;
+ int i, j = 0;
+ int val, inc, rv = 0;
+
+ if(dh < 0){
+ dh = -dh;
+ rv = 1;
+ }
+ p = new unsigned int* [dh+1];
+
+ int up = qAbs(dh) >= sh;
+ val = up ? 0x8000 * sh / dh - 0x8000 : 0;
+ inc = (sh << 16) / dh;
+ for(i = 0; i < dh; i++){
+ p[j++] = src + qMax(0, val >> 16) * sw;
+ val += inc;
+ }
+ if(rv){
+ for(i = dh / 2; --i >= 0; ){
+ unsigned int *tmp = p[i];
+ p[i] = p[dh - i - 1];
+ p[dh - i - 1] = tmp;
+ }
+ }
+ return(p);
+}
+
+int* QImageScale::qimageCalcXPoints(int sw, int dw)
+{
+ int *p, i, j = 0;
+ int val, inc, rv = 0;
+
+ if(dw < 0){
+ dw = -dw;
+ rv = 1;
+ }
+ p = new int[dw+1];
+
+ int up = qAbs(dw) >= sw;
+ val = up ? 0x8000 * sw / dw - 0x8000 : 0;
+ inc = (sw << 16) / dw;
+ for(i = 0; i < dw; i++){
+ p[j++] = qMax(0, val >> 16);
+ val += inc;
+ }
+
+ if(rv){
+ for(i = dw / 2; --i >= 0; ){
+ int tmp = p[i];
+ p[i] = p[dw - i - 1];
+ p[dw - i - 1] = tmp;
+ }
+ }
+ return(p);
+}
+
+int* QImageScale::qimageCalcApoints(int s, int d, int up)
+{
+ int *p, i, j = 0, rv = 0;
+
+ if(d < 0){
+ rv = 1;
+ d = -d;
+ }
+ p = new int[d];
+
+ /* scaling up */
+ if(up){
+ int val, inc;
+
+ val = 0x8000 * s / d - 0x8000;
+ inc = (s << 16) / d;
+ for(i = 0; i < d; i++){
+ int pos = val >> 16;
+ if (pos < 0)
+ p[j++] = 0;
+ else if (pos >= (s - 1))
+ p[j++] = 0;
+ else
+ p[j++] = (val >> 8) - ((val >> 8) & 0xffffff00);
+ val += inc;
+ }
+ }
+ /* scaling down */
+ else{
+ int val, inc, ap, Cp;
+ val = 0;
+ inc = (s << 16) / d;
+ Cp = ((d << 14) / s) + 1;
+ for(i = 0; i < d; i++){
+ ap = ((0x100 - ((val >> 8) & 0xff)) * Cp) >> 8;
+ p[j] = ap | (Cp << 16);
+ j++;
+ val += inc;
+ }
+ }
+ if(rv){
+ int tmp;
+ for(i = d / 2; --i >= 0; ){
+ tmp = p[i];
+ p[i] = p[d - i - 1];
+ p[d - i - 1] = tmp;
+ }
+ }
+ return(p);
+}
+
+QImageScaleInfo* QImageScale::qimageFreeScaleInfo(QImageScaleInfo *isi)
+{
+ if(isi){
+ delete[] isi->xpoints;
+ delete[] isi->ypoints;
+ delete[] isi->xapoints;
+ delete[] isi->yapoints;
+ delete isi;
+ }
+ return 0;
+}
+
+QImageScaleInfo* QImageScale::qimageCalcScaleInfo(const QImage &img,
+ int sw, int sh,
+ int dw, int dh, char aa)
+{
+ QImageScaleInfo *isi;
+ int scw, sch;
+
+ scw = dw * qlonglong(img.width()) / sw;
+ sch = dh * qlonglong(img.height()) / sh;
+
+ isi = new QImageScaleInfo;
+ if(!isi)
+ return 0;
+ memset(isi, 0, sizeof(QImageScaleInfo));
+
+ isi->xup_yup = (qAbs(dw) >= sw) + ((qAbs(dh) >= sh) << 1);
+
+ isi->xpoints = qimageCalcXPoints(img.width(), scw);
+ if(!isi->xpoints)
+ return(qimageFreeScaleInfo(isi));
+ isi->ypoints = qimageCalcYPoints((unsigned int *)img.scanLine(0),
+ img.bytesPerLine() / 4, img.height(), sch);
+ if (!isi->ypoints)
+ return(qimageFreeScaleInfo(isi));
+ if(aa) {
+ isi->xapoints = qimageCalcApoints(img.width(), scw, isi->xup_yup & 1);
+ if(!isi->xapoints)
+ return(qimageFreeScaleInfo(isi));
+ isi->yapoints = qimageCalcApoints(img.height(), sch, isi->xup_yup & 2);
+ if(!isi->yapoints)
+ return(qimageFreeScaleInfo(isi));
+ }
+ return(isi);
+}
+
+/* FIXME: NEED to optimise ScaleAARGBA - currently its "ok" but needs work*/
+
+/* scale by area sampling */
+static void qt_qimageScaleAARGBA(QImageScaleInfo *isi, unsigned int *dest,
+ int dxx, int dyy, int dx, int dy, int dw,
+ int dh, int dow, int sow)
+{
+ unsigned int *sptr, *dptr;
+ int x, y, end;
+ unsigned int **ypoints = isi->ypoints;
+ int *xpoints = isi->xpoints;
+ int *xapoints = isi->xapoints;
+ int *yapoints = isi->yapoints;
+
+ end = dxx + dw;
+ /* scaling up both ways */
+ if(isi->xup_yup == 3){
+ /* go through every scanline in the output buffer */
+ for(y = 0; y < dh; y++){
+ /* calculate the source line we'll scan from */
+ dptr = dest + dx + ((y + dy) * dow);
+ sptr = ypoints[dyy + y];
+ if(YAP > 0){
+ for(x = dxx; x < end; x++){
+ int r, g, b, a;
+ int rr, gg, bb, aa;
+ unsigned int *pix;
+
+ if(XAP > 0){
+ pix = ypoints[dyy + y] + xpoints[x];
+ r = R_VAL(pix) * INV_XAP;
+ g = G_VAL(pix) * INV_XAP;
+ b = B_VAL(pix) * INV_XAP;
+ a = A_VAL(pix) * INV_XAP;
+ pix++;
+ r += R_VAL(pix) * XAP;
+ g += G_VAL(pix) * XAP;
+ b += B_VAL(pix) * XAP;
+ a += A_VAL(pix) * XAP;
+ pix += sow;
+ rr = R_VAL(pix) * XAP;
+ gg = G_VAL(pix) * XAP;
+ bb = B_VAL(pix) * XAP;
+ aa = A_VAL(pix) * XAP;
+ pix--;
+ rr += R_VAL(pix) * INV_XAP;
+ gg += G_VAL(pix) * INV_XAP;
+ bb += B_VAL(pix) * INV_XAP;
+ aa += A_VAL(pix) * INV_XAP;
+ r = ((rr * YAP) + (r * INV_YAP)) >> 16;
+ g = ((gg * YAP) + (g * INV_YAP)) >> 16;
+ b = ((bb * YAP) + (b * INV_YAP)) >> 16;
+ a = ((aa * YAP) + (a * INV_YAP)) >> 16;
+ *dptr++ = qRgba(r, g, b, a);
+ }
+ else{
+ pix = ypoints[dyy + y] + xpoints[x];
+ r = R_VAL(pix) * INV_YAP;
+ g = G_VAL(pix) * INV_YAP;
+ b = B_VAL(pix) * INV_YAP;
+ a = A_VAL(pix) * INV_YAP;
+ pix += sow;
+ r += R_VAL(pix) * YAP;
+ g += G_VAL(pix) * YAP;
+ b += B_VAL(pix) * YAP;
+ a += A_VAL(pix) * YAP;
+ r >>= 8;
+ g >>= 8;
+ b >>= 8;
+ a >>= 8;
+ *dptr++ = qRgba(r, g, b, a);
+ }
+ }
+ }
+ else{
+ for(x = dxx; x < end; x++){
+ int r, g, b, a;
+ unsigned int *pix;
+
+ if(XAP > 0){
+ pix = ypoints[dyy + y] + xpoints[x];
+ r = R_VAL(pix) * INV_XAP;
+ g = G_VAL(pix) * INV_XAP;
+ b = B_VAL(pix) * INV_XAP;
+ a = A_VAL(pix) * INV_XAP;
+ pix++;
+ r += R_VAL(pix) * XAP;
+ g += G_VAL(pix) * XAP;
+ b += B_VAL(pix) * XAP;
+ a += A_VAL(pix) * XAP;
+ r >>= 8;
+ g >>= 8;
+ b >>= 8;
+ a >>= 8;
+ *dptr++ = qRgba(r, g, b, a);
+ }
+ else
+ *dptr++ = sptr[xpoints[x] ];
+ }
+ }
+ }
+ }
+ /* if we're scaling down vertically */
+ else if(isi->xup_yup == 1){
+ /*\ 'Correct' version, with math units prepared for MMXification \*/
+ int Cy, j;
+ unsigned int *pix;
+ int r, g, b, a, rr, gg, bb, aa;
+ int yap;
+
+ /* go through every scanline in the output buffer */
+ for(y = 0; y < dh; y++){
+ Cy = YAP >> 16;
+ yap = YAP & 0xffff;
+
+ dptr = dest + dx + ((y + dy) * dow);
+ for(x = dxx; x < end; x++){
+ pix = ypoints[dyy + y] + xpoints[x];
+ r = R_VAL(pix) * yap;
+ g = G_VAL(pix) * yap;
+ b = B_VAL(pix) * yap;
+ a = A_VAL(pix) * yap;
+ for(j = (1 << 14) - yap; j > Cy; j -= Cy){
+ pix += sow;
+ r += R_VAL(pix) * Cy;
+ g += G_VAL(pix) * Cy;
+ b += B_VAL(pix) * Cy;
+ a += A_VAL(pix) * Cy;
+ }
+ if(j > 0){
+ pix += sow;
+ r += R_VAL(pix) * j;
+ g += G_VAL(pix) * j;
+ b += B_VAL(pix) * j;
+ a += A_VAL(pix) * j;
+ }
+ if(XAP > 0){
+ pix = ypoints[dyy + y] + xpoints[x] + 1;
+ rr = R_VAL(pix) * yap;
+ gg = G_VAL(pix) * yap;
+ bb = B_VAL(pix) * yap;
+ aa = A_VAL(pix) * yap;
+ for(j = (1 << 14) - yap; j > Cy; j -= Cy){
+ pix += sow;
+ rr += R_VAL(pix) * Cy;
+ gg += G_VAL(pix) * Cy;
+ bb += B_VAL(pix) * Cy;
+ aa += A_VAL(pix) * Cy;
+ }
+ if(j > 0){
+ pix += sow;
+ rr += R_VAL(pix) * j;
+ gg += G_VAL(pix) * j;
+ bb += B_VAL(pix) * j;
+ aa += A_VAL(pix) * j;
+ }
+ r = r * INV_XAP;
+ g = g * INV_XAP;
+ b = b * INV_XAP;
+ a = a * INV_XAP;
+ r = (r + ((rr * XAP))) >> 12;
+ g = (g + ((gg * XAP))) >> 12;
+ b = (b + ((bb * XAP))) >> 12;
+ a = (a + ((aa * XAP))) >> 12;
+ }
+ else{
+ r >>= 4;
+ g >>= 4;
+ b >>= 4;
+ a >>= 4;
+ }
+ *dptr = qRgba(r >> 10, g >> 10, b >> 10, a >> 10);
+ dptr++;
+ }
+ }
+ }
+ /* if we're scaling down horizontally */
+ else if(isi->xup_yup == 2){
+ /*\ 'Correct' version, with math units prepared for MMXification \*/
+ int Cx, j;
+ unsigned int *pix;
+ int r, g, b, a, rr, gg, bb, aa;
+ int xap;
+
+ /* go through every scanline in the output buffer */
+ for(y = 0; y < dh; y++){
+ dptr = dest + dx + ((y + dy) * dow);
+ for(x = dxx; x < end; x++){
+ Cx = XAP >> 16;
+ xap = XAP & 0xffff;
+
+ pix = ypoints[dyy + y] + xpoints[x];
+ r = R_VAL(pix) * xap;
+ g = G_VAL(pix) * xap;
+ b = B_VAL(pix) * xap;
+ a = A_VAL(pix) * xap;
+ for(j = (1 << 14) - xap; j > Cx; j -= Cx){
+ pix++;
+ r += R_VAL(pix) * Cx;
+ g += G_VAL(pix) * Cx;
+ b += B_VAL(pix) * Cx;
+ a += A_VAL(pix) * Cx;
+ }
+ if(j > 0){
+ pix++;
+ r += R_VAL(pix) * j;
+ g += G_VAL(pix) * j;
+ b += B_VAL(pix) * j;
+ a += A_VAL(pix) * j;
+ }
+ if(YAP > 0){
+ pix = ypoints[dyy + y] + xpoints[x] + sow;
+ rr = R_VAL(pix) * xap;
+ gg = G_VAL(pix) * xap;
+ bb = B_VAL(pix) * xap;
+ aa = A_VAL(pix) * xap;
+ for(j = (1 << 14) - xap; j > Cx; j -= Cx){
+ pix++;
+ rr += R_VAL(pix) * Cx;
+ gg += G_VAL(pix) * Cx;
+ bb += B_VAL(pix) * Cx;
+ aa += A_VAL(pix) * Cx;
+ }
+ if(j > 0){
+ pix++;
+ rr += R_VAL(pix) * j;
+ gg += G_VAL(pix) * j;
+ bb += B_VAL(pix) * j;
+ aa += A_VAL(pix) * j;
+ }
+ r = r * INV_YAP;
+ g = g * INV_YAP;
+ b = b * INV_YAP;
+ a = a * INV_YAP;
+ r = (r + ((rr * YAP))) >> 12;
+ g = (g + ((gg * YAP))) >> 12;
+ b = (b + ((bb * YAP))) >> 12;
+ a = (a + ((aa * YAP))) >> 12;
+ }
+ else{
+ r >>= 4;
+ g >>= 4;
+ b >>= 4;
+ a >>= 4;
+ }
+ *dptr = qRgba(r >> 10, g >> 10, b >> 10, a >> 10);
+ dptr++;
+ }
+ }
+ }
+ /* if we're scaling down horizontally & vertically */
+ else{
+ /*\ 'Correct' version, with math units prepared for MMXification:
+ |*| The operation 'b = (b * c) >> 16' translates to pmulhw,
+ |*| so the operation 'b = (b * c) >> d' would translate to
+ |*| psllw (16 - d), %mmb; pmulh %mmc, %mmb
+ \*/
+ int Cx, Cy, i, j;
+ unsigned int *pix;
+ int a, r, g, b, ax, rx, gx, bx;
+ int xap, yap;
+
+ for(y = 0; y < dh; y++){
+ Cy = YAP >> 16;
+ yap = YAP & 0xffff;
+
+ dptr = dest + dx + ((y + dy) * dow);
+ for(x = dxx; x < end; x++){
+ Cx = XAP >> 16;
+ xap = XAP & 0xffff;
+
+ sptr = ypoints[dyy + y] + xpoints[x];
+ pix = sptr;
+ sptr += sow;
+ rx = R_VAL(pix) * xap;
+ gx = G_VAL(pix) * xap;
+ bx = B_VAL(pix) * xap;
+ ax = A_VAL(pix) * xap;
+
+ pix++;
+ for(i = (1 << 14) - xap; i > Cx; i -= Cx){
+ rx += R_VAL(pix) * Cx;
+ gx += G_VAL(pix) * Cx;
+ bx += B_VAL(pix) * Cx;
+ ax += A_VAL(pix) * Cx;
+ pix++;
+ }
+ if(i > 0){
+ rx += R_VAL(pix) * i;
+ gx += G_VAL(pix) * i;
+ bx += B_VAL(pix) * i;
+ ax += A_VAL(pix) * i;
+ }
+
+ r = (rx >> 5) * yap;
+ g = (gx >> 5) * yap;
+ b = (bx >> 5) * yap;
+ a = (ax >> 5) * yap;
+
+ for(j = (1 << 14) - yap; j > Cy; j -= Cy){
+ pix = sptr;
+ sptr += sow;
+ rx = R_VAL(pix) * xap;
+ gx = G_VAL(pix) * xap;
+ bx = B_VAL(pix) * xap;
+ ax = A_VAL(pix) * xap;
+ pix++;
+ for(i = (1 << 14) - xap; i > Cx; i -= Cx){
+ rx += R_VAL(pix) * Cx;
+ gx += G_VAL(pix) * Cx;
+ bx += B_VAL(pix) * Cx;
+ ax += A_VAL(pix) * Cx;
+ pix++;
+ }
+ if(i > 0){
+ rx += R_VAL(pix) * i;
+ gx += G_VAL(pix) * i;
+ bx += B_VAL(pix) * i;
+ ax += A_VAL(pix) * i;
+ }
+
+ r += (rx >> 5) * Cy;
+ g += (gx >> 5) * Cy;
+ b += (bx >> 5) * Cy;
+ a += (ax >> 5) * Cy;
+ }
+ if(j > 0){
+ pix = sptr;
+ sptr += sow;
+ rx = R_VAL(pix) * xap;
+ gx = G_VAL(pix) * xap;
+ bx = B_VAL(pix) * xap;
+ ax = A_VAL(pix) * xap;
+ pix++;
+ for(i = (1 << 14) - xap; i > Cx; i -= Cx){
+ rx += R_VAL(pix) * Cx;
+ gx += G_VAL(pix) * Cx;
+ bx += B_VAL(pix) * Cx;
+ ax += A_VAL(pix) * Cx;
+ pix++;
+ }
+ if(i > 0){
+ rx += R_VAL(pix) * i;
+ gx += G_VAL(pix) * i;
+ bx += B_VAL(pix) * i;
+ ax += A_VAL(pix) * i;
+ }
+
+ r += (rx >> 5) * j;
+ g += (gx >> 5) * j;
+ b += (bx >> 5) * j;
+ a += (ax >> 5) * j;
+ }
+
+ *dptr = qRgba(r >> 23, g >> 23, b >> 23, a >> 23);
+ dptr++;
+ }
+ }
+ }
+}
+
+/* scale by area sampling - IGNORE the ALPHA byte*/
+static void qt_qimageScaleAARGB(QImageScaleInfo *isi, unsigned int *dest,
+ int dxx, int dyy, int dx, int dy, int dw,
+ int dh, int dow, int sow)
+{
+ unsigned int *sptr, *dptr;
+ int x, y, end;
+ unsigned int **ypoints = isi->ypoints;
+ int *xpoints = isi->xpoints;
+ int *xapoints = isi->xapoints;
+ int *yapoints = isi->yapoints;
+
+ end = dxx + dw;
+ /* scaling up both ways */
+ if(isi->xup_yup == 3){
+ /* go through every scanline in the output buffer */
+ for(y = 0; y < dh; y++){
+ /* calculate the source line we'll scan from */
+ dptr = dest + dx + ((y + dy) * dow);
+ sptr = ypoints[dyy + y];
+ if(YAP > 0){
+ for(x = dxx; x < end; x++){
+ int r = 0, g = 0, b = 0;
+ int rr = 0, gg = 0, bb = 0;
+ unsigned int *pix;
+
+ if(XAP > 0){
+ pix = ypoints[dyy + y] + xpoints[x];
+ r = R_VAL(pix) * INV_XAP;
+ g = G_VAL(pix) * INV_XAP;
+ b = B_VAL(pix) * INV_XAP;
+ pix++;
+ r += R_VAL(pix) * XAP;
+ g += G_VAL(pix) * XAP;
+ b += B_VAL(pix) * XAP;
+ pix += sow;
+ rr = R_VAL(pix) * XAP;
+ gg = G_VAL(pix) * XAP;
+ bb = B_VAL(pix) * XAP;
+ pix --;
+ rr += R_VAL(pix) * INV_XAP;
+ gg += G_VAL(pix) * INV_XAP;
+ bb += B_VAL(pix) * INV_XAP;
+ r = ((rr * YAP) + (r * INV_YAP)) >> 16;
+ g = ((gg * YAP) + (g * INV_YAP)) >> 16;
+ b = ((bb * YAP) + (b * INV_YAP)) >> 16;
+ *dptr++ = qRgba(r, g, b, 0xff);
+ }
+ else{
+ pix = ypoints[dyy + y] + xpoints[x];
+ r = R_VAL(pix) * INV_YAP;
+ g = G_VAL(pix) * INV_YAP;
+ b = B_VAL(pix) * INV_YAP;
+ pix += sow;
+ r += R_VAL(pix) * YAP;
+ g += G_VAL(pix) * YAP;
+ b += B_VAL(pix) * YAP;
+ r >>= 8;
+ g >>= 8;
+ b >>= 8;
+ *dptr++ = qRgba(r, g, b, 0xff);
+ }
+ }
+ }
+ else{
+ for(x = dxx; x < end; x++){
+ int r = 0, g = 0, b = 0;
+ unsigned int *pix;
+
+ if(XAP > 0){
+ pix = ypoints[dyy + y] + xpoints[x];
+ r = R_VAL(pix) * INV_XAP;
+ g = G_VAL(pix) * INV_XAP;
+ b = B_VAL(pix) * INV_XAP;
+ pix++;
+ r += R_VAL(pix) * XAP;
+ g += G_VAL(pix) * XAP;
+ b += B_VAL(pix) * XAP;
+ r >>= 8;
+ g >>= 8;
+ b >>= 8;
+ *dptr++ = qRgba(r, g, b, 0xff);
+ }
+ else
+ *dptr++ = sptr[xpoints[x] ];
+ }
+ }
+ }
+ }
+ /* if we're scaling down vertically */
+ else if(isi->xup_yup == 1){
+ /*\ 'Correct' version, with math units prepared for MMXification \*/
+ int Cy, j;
+ unsigned int *pix;
+ int r, g, b, rr, gg, bb;
+ int yap;
+
+ /* go through every scanline in the output buffer */
+ for(y = 0; y < dh; y++){
+ Cy = YAP >> 16;
+ yap = YAP & 0xffff;
+
+ dptr = dest + dx + ((y + dy) * dow);
+ for(x = dxx; x < end; x++){
+ pix = ypoints[dyy + y] + xpoints[x];
+ r = R_VAL(pix) * yap;
+ g = G_VAL(pix) * yap;
+ b = B_VAL(pix) * yap;
+ pix += sow;
+ for(j = (1 << 14) - yap; j > Cy; j -= Cy){
+ r += R_VAL(pix) * Cy;
+ g += G_VAL(pix) * Cy;
+ b += B_VAL(pix) * Cy;
+ pix += sow;
+ }
+ if(j > 0){
+ r += R_VAL(pix) * j;
+ g += G_VAL(pix) * j;
+ b += B_VAL(pix) * j;
+ }
+ if(XAP > 0){
+ pix = ypoints[dyy + y] + xpoints[x] + 1;
+ rr = R_VAL(pix) * yap;
+ gg = G_VAL(pix) * yap;
+ bb = B_VAL(pix) * yap;
+ pix += sow;
+ for(j = (1 << 14) - yap; j > Cy; j -= Cy){
+ rr += R_VAL(pix) * Cy;
+ gg += G_VAL(pix) * Cy;
+ bb += B_VAL(pix) * Cy;
+ pix += sow;
+ }
+ if(j > 0){
+ rr += R_VAL(pix) * j;
+ gg += G_VAL(pix) * j;
+ bb += B_VAL(pix) * j;
+ }
+ r = r * INV_XAP;
+ g = g * INV_XAP;
+ b = b * INV_XAP;
+ r = (r + ((rr * XAP))) >> 12;
+ g = (g + ((gg * XAP))) >> 12;
+ b = (b + ((bb * XAP))) >> 12;
+ }
+ else{
+ r >>= 4;
+ g >>= 4;
+ b >>= 4;
+ }
+ *dptr = qRgba(r >> 10, g >> 10, b >> 10, 0xff);
+ dptr++;
+ }
+ }
+ }
+ /* if we're scaling down horizontally */
+ else if(isi->xup_yup == 2){
+ /*\ 'Correct' version, with math units prepared for MMXification \*/
+ int Cx, j;
+ unsigned int *pix;
+ int r, g, b, rr, gg, bb;
+ int xap;
+
+ /* go through every scanline in the output buffer */
+ for(y = 0; y < dh; y++){
+ dptr = dest + dx + ((y + dy) * dow);
+ for(x = dxx; x < end; x++){
+ Cx = XAP >> 16;
+ xap = XAP & 0xffff;
+
+ pix = ypoints[dyy + y] + xpoints[x];
+ r = R_VAL(pix) * xap;
+ g = G_VAL(pix) * xap;
+ b = B_VAL(pix) * xap;
+ pix++;
+ for(j = (1 << 14) - xap; j > Cx; j -= Cx){
+ r += R_VAL(pix) * Cx;
+ g += G_VAL(pix) * Cx;
+ b += B_VAL(pix) * Cx;
+ pix++;
+ }
+ if(j > 0){
+ r += R_VAL(pix) * j;
+ g += G_VAL(pix) * j;
+ b += B_VAL(pix) * j;
+ }
+ if(YAP > 0){
+ pix = ypoints[dyy + y] + xpoints[x] + sow;
+ rr = R_VAL(pix) * xap;
+ gg = G_VAL(pix) * xap;
+ bb = B_VAL(pix) * xap;
+ pix++;
+ for(j = (1 << 14) - xap; j > Cx; j -= Cx){
+ rr += R_VAL(pix) * Cx;
+ gg += G_VAL(pix) * Cx;
+ bb += B_VAL(pix) * Cx;
+ pix++;
+ }
+ if(j > 0){
+ rr += R_VAL(pix) * j;
+ gg += G_VAL(pix) * j;
+ bb += B_VAL(pix) * j;
+ }
+ r = r * INV_YAP;
+ g = g * INV_YAP;
+ b = b * INV_YAP;
+ r = (r + ((rr * YAP))) >> 12;
+ g = (g + ((gg * YAP))) >> 12;
+ b = (b + ((bb * YAP))) >> 12;
+ }
+ else{
+ r >>= 4;
+ g >>= 4;
+ b >>= 4;
+ }
+ *dptr = qRgba(r >> 10, g >> 10, b >> 10, 0xff);
+ dptr++;
+ }
+ }
+ }
+ /* fully optimized (i think) - onyl change of algorithm can help */
+ /* if we're scaling down horizontally & vertically */
+ else{
+ /*\ 'Correct' version, with math units prepared for MMXification \*/
+ int Cx, Cy, i, j;
+ unsigned int *pix;
+ int r, g, b, rx, gx, bx;
+ int xap, yap;
+
+ for(y = 0; y < dh; y++){
+ Cy = YAP >> 16;
+ yap = YAP & 0xffff;
+
+ dptr = dest + dx + ((y + dy) * dow);
+ for(x = dxx; x < end; x++){
+ Cx = XAP >> 16;
+ xap = XAP & 0xffff;
+
+ sptr = ypoints[dyy + y] + xpoints[x];
+ pix = sptr;
+ sptr += sow;
+ rx = R_VAL(pix) * xap;
+ gx = G_VAL(pix) * xap;
+ bx = B_VAL(pix) * xap;
+ pix++;
+ for(i = (1 << 14) - xap; i > Cx; i -= Cx){
+ rx += R_VAL(pix) * Cx;
+ gx += G_VAL(pix) * Cx;
+ bx += B_VAL(pix) * Cx;
+ pix++;
+ }
+ if(i > 0){
+ rx += R_VAL(pix) * i;
+ gx += G_VAL(pix) * i;
+ bx += B_VAL(pix) * i;
+ }
+
+ r = (rx >> 5) * yap;
+ g = (gx >> 5) * yap;
+ b = (bx >> 5) * yap;
+
+ for(j = (1 << 14) - yap; j > Cy; j -= Cy){
+ pix = sptr;
+ sptr += sow;
+ rx = R_VAL(pix) * xap;
+ gx = G_VAL(pix) * xap;
+ bx = B_VAL(pix) * xap;
+ pix++;
+ for(i = (1 << 14) - xap; i > Cx; i -= Cx){
+ rx += R_VAL(pix) * Cx;
+ gx += G_VAL(pix) * Cx;
+ bx += B_VAL(pix) * Cx;
+ pix++;
+ }
+ if(i > 0){
+ rx += R_VAL(pix) * i;
+ gx += G_VAL(pix) * i;
+ bx += B_VAL(pix) * i;
+ }
+
+ r += (rx >> 5) * Cy;
+ g += (gx >> 5) * Cy;
+ b += (bx >> 5) * Cy;
+ }
+ if(j > 0){
+ pix = sptr;
+ sptr += sow;
+ rx = R_VAL(pix) * xap;
+ gx = G_VAL(pix) * xap;
+ bx = B_VAL(pix) * xap;
+ pix++;
+ for(i = (1 << 14) - xap; i > Cx; i -= Cx){
+ rx += R_VAL(pix) * Cx;
+ gx += G_VAL(pix) * Cx;
+ bx += B_VAL(pix) * Cx;
+ pix++;
+ }
+ if(i > 0){
+ rx += R_VAL(pix) * i;
+ gx += G_VAL(pix) * i;
+ bx += B_VAL(pix) * i;
+ }
+
+ r += (rx >> 5) * j;
+ g += (gx >> 5) * j;
+ b += (bx >> 5) * j;
+ }
+
+ *dptr = qRgb(r >> 23, g >> 23, b >> 23);
+ dptr++;
+ }
+ }
+ }
+}
+
+#if 0
+static void qt_qimageScaleAARGBASetup(QImageScaleInfo *isi, unsigned int *dest,
+ int dxx, int dyy, int dx, int dy, int dw,
+ int dh, int dow, int sow)
+{
+ qInitDrawhelperAsm();
+ qt_qimageScaleAARGBA(isi, dest, dxx, dyy, dx, dy, dw, dh, dow, sow);
+}
+
+static void qt_qimageScaleAARGBSetup(QImageScaleInfo *isi, unsigned int *dest,
+ int dxx, int dyy, int dx, int dy, int dw,
+ int dh, int dow, int sow)
+{
+ qInitDrawhelperAsm();
+ qt_qimageScaleAARGB(isi, dest, dxx, dyy, dx, dy, dw, dh, dow, sow);
+}
+#endif
+
+QImage qSmoothScaleImage(const QImage &src, int dw, int dh)
+{
+ QImage buffer;
+ if (src.isNull() || dw <= 0 || dh <= 0)
+ return buffer;
+
+ int w = src.width();
+ int h = src.height();
+ QImageScaleInfo *scaleinfo =
+ qimageCalcScaleInfo(src, w, h, dw, dh, true);
+ if (!scaleinfo)
+ return buffer;
+
+ buffer = QImage(dw, dh, src.format());
+ if (buffer.isNull()) {
+ qWarning("QImage: out of memory, returning null");
+ qimageFreeScaleInfo(scaleinfo);
+ return QImage();
+ }
+
+ if (src.format() == QImage::Format_ARGB32_Premultiplied)
+ qt_qimageScaleArgb(scaleinfo, (unsigned int *)buffer.scanLine(0),
+ 0, 0, 0, 0, dw, dh, dw, src.bytesPerLine() / 4);
+ else
+ qt_qimageScaleRgb(scaleinfo, (unsigned int *)buffer.scanLine(0),
+ 0, 0, 0, 0, dw, dh, dw, src.bytesPerLine() / 4);
+
+ qimageFreeScaleInfo(scaleinfo);
+ return buffer;
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/painting/qimagescale_p.h b/src/gui/painting/qimagescale_p.h
new file mode 100644
index 0000000000..1f8fa492d0
--- /dev/null
+++ b/src/gui/painting/qimagescale_p.h
@@ -0,0 +1,66 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#ifndef QIMAGESCALE_P_H
+#define QIMAGESCALE_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 <qimage.h>
+
+QT_BEGIN_NAMESPACE
+
+/*
+ This version accepts only supported formats.
+*/
+QImage qSmoothScaleImage(const QImage &img, int w, int h);
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/gui/painting/qmath_p.h b/src/gui/painting/qmath_p.h
new file mode 100644
index 0000000000..dcdfda3618
--- /dev/null
+++ b/src/gui/painting/qmath_p.h
@@ -0,0 +1,66 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QMATH_P_H
+#define QMATH_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 <math.h>
+
+QT_BEGIN_NAMESPACE
+
+static const qreal Q_PI = qreal(3.14159265358979323846); // pi
+static const qreal Q_2PI = qreal(6.28318530717958647693); // 2*pi
+static const qreal Q_PI2 = qreal(1.57079632679489661923); // pi/2
+
+QT_END_NAMESPACE
+
+#endif // QMATH_P_H
diff --git a/src/gui/painting/qmatrix.cpp b/src/gui/painting/qmatrix.cpp
new file mode 100644
index 0000000000..4439d5223e
--- /dev/null
+++ b/src/gui/painting/qmatrix.cpp
@@ -0,0 +1,1180 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qdatastream.h"
+#include "qdebug.h"
+#include "qmatrix.h"
+#include "qregion.h"
+#include "qpainterpath.h"
+#include "qvariant.h"
+#include <qmath.h>
+
+#include <limits.h>
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QMatrix
+ \brief The QMatrix class specifies 2D transformations of a
+ coordinate system.
+
+ \ingroup multimedia
+
+ A matrix specifies how to translate, scale, shear or rotate the
+ coordinate system, and is typically used when rendering graphics.
+
+ A QMatrix object can be built using the setMatrix(), scale(),
+ rotate(), translate() and shear() functions. Alternatively, it
+ can be built by applying \l {QMatrix#Basic Matrix
+ Operations}{basic matrix operations}. The matrix can also be
+ defined when constructed, and it can be reset to the identity
+ matrix (the default) using the reset() function.
+
+ The QMatrix class supports mapping of graphic primitives: A given
+ point, line, polygon, region, or painter path can be mapped to the
+ coordinate system defined by \e this matrix using the map()
+ function. In case of a rectangle, its coordinates can be
+ transformed using the mapRect() function. A rectangle can also be
+ transformed into a \e polygon (mapped to the coordinate system
+ defined by \e this matrix), using the mapToPolygon() function.
+
+ QMatrix provides the isIdentity() function which returns true if
+ the matrix is the identity matrix, and the isInvertible() function
+ which returns true if the matrix is non-singular (i.e. AB = BA =
+ I). The inverted() function returns an inverted copy of \e this
+ matrix if it is invertible (otherwise it returns the identity
+ matrix). In addition, QMatrix provides the det() function
+ returning the matrix's determinant.
+
+ Finally, the QMatrix class supports matrix multiplication, and
+ objects of the class can be streamed as well as compared.
+
+ \tableofcontents
+
+ \section1 Rendering Graphics
+
+ When rendering graphics, the matrix defines the transformations
+ but the actual transformation is performed by the drawing routines
+ in QPainter.
+
+ By default, QPainter operates on the associated device's own
+ coordinate system. The standard coordinate system of a
+ QPaintDevice has its origin located at the top-left position. The
+ \e x values increase to the right; \e y values increase
+ downward. For a complete description, see the \l {The Coordinate
+ System}{coordinate system} documentation.
+
+ QPainter has functions to translate, scale, shear and rotate the
+ coordinate system without using a QMatrix. For example:
+
+ \table 100%
+ \row
+ \o \inlineimage qmatrix-simpletransformation.png
+ \o
+ \snippet doc/src/snippets/matrix/matrix.cpp 0
+ \endtable
+
+ Although these functions are very convenient, it can be more
+ efficient to build a QMatrix and call QPainter::setMatrix() if you
+ want to perform more than a single transform operation. For
+ example:
+
+ \table 100%
+ \row
+ \o \inlineimage qmatrix-combinedtransformation.png
+ \o
+ \snippet doc/src/snippets/matrix/matrix.cpp 1
+ \endtable
+
+ \section1 Basic Matrix Operations
+
+ \image qmatrix-representation.png
+
+ A QMatrix object contains a 3 x 3 matrix. The \c dx and \c dy
+ elements specify horizontal and vertical translation. The \c m11
+ and \c m22 elements specify horizontal and vertical scaling. And
+ finally, the \c m21 and \c m12 elements specify horizontal and
+ vertical \e shearing.
+
+ QMatrix transforms a point in the plane to another point using the
+ following formulas:
+
+ \snippet doc/src/snippets/code/src_gui_painting_qmatrix.cpp 0
+
+ The point \e (x, y) is the original point, and \e (x', y') is the
+ transformed point. \e (x', y') can be transformed back to \e (x,
+ y) by performing the same operation on the inverted() matrix.
+
+ The various matrix elements can be set when constructing the
+ matrix, or by using the setMatrix() function later on. They can also
+ be manipulated using the translate(), rotate(), scale() and
+ shear() convenience functions, The currently set values can be
+ retrieved using the m11(), m12(), m21(), m22(), dx() and dy()
+ functions.
+
+ Translation is the simplest transformation. Setting \c dx and \c
+ dy will move the coordinate system \c dx units along the X axis
+ and \c dy units along the Y axis. Scaling can be done by setting
+ \c m11 and \c m22. For example, setting \c m11 to 2 and \c m22 to
+ 1.5 will double the height and increase the width by 50%. The
+ identity matrix has \c m11 and \c m22 set to 1 (all others are set
+ to 0) mapping a point to itself. Shearing is controlled by \c m12
+ and \c m21. Setting these elements to values different from zero
+ will twist the coordinate system. Rotation is achieved by
+ carefully setting both the shearing factors and the scaling
+ factors.
+
+ Here's the combined transformations example using basic matrix
+ operations:
+
+ \table 100%
+ \row
+ \o \inlineimage qmatrix-combinedtransformation.png
+ \o
+ \snippet doc/src/snippets/matrix/matrix.cpp 2
+ \endtable
+
+ \sa QPainter, {The Coordinate System}, {demos/affine}{Affine
+ Transformations Demo}, {Transformations Example}
+*/
+
+
+// some defines to inline some code
+#define MAPDOUBLE(x, y, nx, ny) \
+{ \
+ qreal fx = x; \
+ qreal fy = y; \
+ nx = _m11*fx + _m21*fy + _dx; \
+ ny = _m12*fx + _m22*fy + _dy; \
+}
+
+#define MAPINT(x, y, nx, ny) \
+{ \
+ qreal fx = x; \
+ qreal fy = y; \
+ nx = qRound(_m11*fx + _m21*fy + _dx); \
+ ny = qRound(_m12*fx + _m22*fy + _dy); \
+}
+
+/*****************************************************************************
+ QMatrix member functions
+ *****************************************************************************/
+
+/*!
+ Constructs an identity matrix.
+
+ All elements are set to zero except \c m11 and \c m22 (specifying
+ the scale), which are set to 1.
+
+ \sa reset()
+*/
+
+QMatrix::QMatrix()
+{
+ _m11 = _m22 = 1.0;
+ _m12 = _m21 = _dx = _dy = 0.0;
+}
+
+/*!
+ Constructs a matrix with the elements, \a m11, \a m12, \a m21, \a
+ m22, \a dx and \a dy.
+
+ \sa setMatrix()
+*/
+
+QMatrix::QMatrix(qreal m11, qreal m12, qreal m21, qreal m22,
+ qreal dx, qreal dy)
+{
+ _m11 = m11; _m12 = m12;
+ _m21 = m21; _m22 = m22;
+ _dx = dx; _dy = dy;
+}
+
+
+/*!
+ Constructs a matrix that is a copy of the given \a matrix.
+ */
+QMatrix::QMatrix(const QMatrix &matrix)
+{
+ *this = matrix;
+}
+
+/*!
+ Sets the matrix elements to the specified values, \a m11, \a m12,
+ \a m21, \a m22, \a dx and \a dy.
+
+ Note that this function replaces the previous values. QMatrix
+ provide the translate(), rotate(), scale() and shear() convenience
+ functions to manipulate the various matrix elements based on the
+ currently defined coordinate system.
+
+ \sa QMatrix()
+*/
+
+void QMatrix::setMatrix(qreal m11, qreal m12, qreal m21, qreal m22,
+ qreal dx, qreal dy)
+{
+ _m11 = m11; _m12 = m12;
+ _m21 = m21; _m22 = m22;
+ _dx = dx; _dy = dy;
+}
+
+
+/*!
+ \fn qreal QMatrix::m11() const
+
+ Returns the horizontal scaling factor.
+
+ \sa scale(), {QMatrix#Basic Matrix Operations}{Basic Matrix
+ Operations}
+*/
+
+/*!
+ \fn qreal QMatrix::m12() const
+
+ Returns the vertical shearing factor.
+
+ \sa shear(), {QMatrix#Basic Matrix Operations}{Basic Matrix
+ Operations}
+*/
+
+/*!
+ \fn qreal QMatrix::m21() const
+
+ Returns the horizontal shearing factor.
+
+ \sa shear(), {QMatrix#Basic Matrix Operations}{Basic Matrix
+ Operations}
+*/
+
+/*!
+ \fn qreal QMatrix::m22() const
+
+ Returns the vertical scaling factor.
+
+ \sa scale(), {QMatrix#Basic Matrix Operations}{Basic Matrix
+ Operations}
+*/
+
+/*!
+ \fn qreal QMatrix::dx() const
+
+ Returns the horizontal translation factor.
+
+ \sa translate(), {QMatrix#Basic Matrix Operations}{Basic Matrix
+ Operations}
+*/
+
+/*!
+ \fn qreal QMatrix::dy() const
+
+ Returns the vertical translation factor.
+
+ \sa translate(), {QMatrix#Basic Matrix Operations}{Basic Matrix
+ Operations}
+*/
+
+
+/*!
+ Maps the given coordinates \a x and \a y into the coordinate
+ system defined by this matrix. The resulting values are put in *\a
+ tx and *\a ty, respectively.
+
+ The coordinates are transformed using the following formulas:
+
+ \snippet doc/src/snippets/code/src_gui_painting_qmatrix.cpp 1
+
+ The point (x, y) is the original point, and (x', y') is the
+ transformed point.
+
+ \sa {QMatrix#Basic Matrix Operations}{Basic Matrix Operations}
+*/
+
+void QMatrix::map(qreal x, qreal y, qreal *tx, qreal *ty) const
+{
+ MAPDOUBLE(x, y, *tx, *ty);
+}
+
+
+
+/*!
+ \overload
+
+ Maps the given coordinates \a x and \a y into the coordinate
+ system defined by this matrix. The resulting values are put in *\a
+ tx and *\a ty, respectively. Note that the transformed coordinates
+ are rounded to the nearest integer.
+*/
+
+void QMatrix::map(int x, int y, int *tx, int *ty) const
+{
+ MAPINT(x, y, *tx, *ty);
+}
+
+QRect QMatrix::mapRect(const QRect &rect) const
+{
+ QRect result;
+ if (_m12 == 0.0F && _m21 == 0.0F) {
+ int x = qRound(_m11*rect.x() + _dx);
+ int y = qRound(_m22*rect.y() + _dy);
+ int w = qRound(_m11*rect.width());
+ int h = qRound(_m22*rect.height());
+ if (w < 0) {
+ w = -w;
+ x -= w;
+ }
+ if (h < 0) {
+ h = -h;
+ y -= h;
+ }
+ result = QRect(x, y, w, h);
+ } else {
+ // see mapToPolygon for explanations of the algorithm.
+ qreal x0, y0;
+ qreal x, y;
+ MAPDOUBLE(rect.left(), rect.top(), x0, y0);
+ qreal xmin = x0;
+ qreal ymin = y0;
+ qreal xmax = x0;
+ qreal ymax = y0;
+ MAPDOUBLE(rect.right() + 1, rect.top(), x, y);
+ xmin = qMin(xmin, x);
+ ymin = qMin(ymin, y);
+ xmax = qMax(xmax, x);
+ ymax = qMax(ymax, y);
+ MAPDOUBLE(rect.right() + 1, rect.bottom() + 1, x, y);
+ xmin = qMin(xmin, x);
+ ymin = qMin(ymin, y);
+ xmax = qMax(xmax, x);
+ ymax = qMax(ymax, y);
+ MAPDOUBLE(rect.left(), rect.bottom() + 1, x, y);
+ xmin = qMin(xmin, x);
+ ymin = qMin(ymin, y);
+ xmax = qMax(xmax, x);
+ ymax = qMax(ymax, y);
+ result = QRect(qRound(xmin), qRound(ymin), qRound(xmax)-qRound(xmin), qRound(ymax)-qRound(ymin));
+ }
+ return result;
+}
+
+/*!
+ \fn QRectF QMatrix::mapRect(const QRectF &rectangle) const
+
+ Creates and returns a QRectF object that is a copy of the given \a
+ rectangle, mapped into the coordinate system defined by this
+ matrix.
+
+ The rectangle's coordinates are transformed using the following
+ formulas:
+
+ \snippet doc/src/snippets/code/src_gui_painting_qmatrix.cpp 2
+
+ If rotation or shearing has been specified, this function returns
+ the \e bounding rectangle. To retrieve the exact region the given
+ \a rectangle maps to, use the mapToPolygon() function instead.
+
+ \sa mapToPolygon(), {QMatrix#Basic Matrix Operations}{Basic Matrix
+ Operations}
+*/
+QRectF QMatrix::mapRect(const QRectF &rect) const
+{
+ QRectF result;
+ if (_m12 == 0.0F && _m21 == 0.0F) {
+ qreal x = _m11*rect.x() + _dx;
+ qreal y = _m22*rect.y() + _dy;
+ qreal w = _m11*rect.width();
+ qreal h = _m22*rect.height();
+ if (w < 0) {
+ w = -w;
+ x -= w;
+ }
+ if (h < 0) {
+ h = -h;
+ y -= h;
+ }
+ result = QRectF(x, y, w, h);
+ } else {
+ qreal x0, y0;
+ qreal x, y;
+ MAPDOUBLE(rect.x(), rect.y(), x0, y0);
+ qreal xmin = x0;
+ qreal ymin = y0;
+ qreal xmax = x0;
+ qreal ymax = y0;
+ MAPDOUBLE(rect.x() + rect.width(), rect.y(), x, y);
+ xmin = qMin(xmin, x);
+ ymin = qMin(ymin, y);
+ xmax = qMax(xmax, x);
+ ymax = qMax(ymax, y);
+ MAPDOUBLE(rect.x() + rect.width(), rect.y() + rect.height(), x, y);
+ xmin = qMin(xmin, x);
+ ymin = qMin(ymin, y);
+ xmax = qMax(xmax, x);
+ ymax = qMax(ymax, y);
+ MAPDOUBLE(rect.x(), rect.y() + rect.height(), x, y);
+ xmin = qMin(xmin, x);
+ ymin = qMin(ymin, y);
+ xmax = qMax(xmax, x);
+ ymax = qMax(ymax, y);
+ result = QRectF(xmin, ymin, xmax-xmin, ymax - ymin);
+ }
+ return result;
+}
+
+/*!
+ \fn QRect QMatrix::mapRect(const QRect &rectangle) const
+ \overload
+
+ Creates and returns a QRect object that is a copy of the given \a
+ rectangle, mapped into the coordinate system defined by this
+ matrix. Note that the transformed coordinates are rounded to the
+ nearest integer.
+*/
+
+
+/*!
+ \fn QPoint operator*(const QPoint &point, const QMatrix &matrix)
+ \relates QMatrix
+
+ This is the same as \a{matrix}.map(\a{point}).
+
+ \sa QMatrix::map()
+*/
+
+QPoint QMatrix::map(const QPoint &p) const
+{
+ qreal fx = p.x();
+ qreal fy = p.y();
+ return QPoint(qRound(_m11*fx + _m21*fy + _dx),
+ qRound(_m12*fx + _m22*fy + _dy));
+}
+
+/*!
+ \fn QPointF operator*(const QPointF &point, const QMatrix &matrix)
+ \relates QMatrix
+
+ Same as \a{matrix}.map(\a{point}).
+
+ \sa QMatrix::map()
+*/
+
+/*!
+ \overload
+
+ Creates and returns a QPointF object that is a copy of the given
+ \a point, mapped into the coordinate system defined by this
+ matrix.
+*/
+QPointF QMatrix::map(const QPointF &point) const
+{
+ qreal fx = point.x();
+ qreal fy = point.y();
+ return QPointF(_m11*fx + _m21*fy + _dx, _m12*fx + _m22*fy + _dy);
+}
+
+/*!
+ \fn QPoint QMatrix::map(const QPoint &point) const
+ \overload
+
+ Creates and returns a QPoint object that is a copy of the given \a
+ point, mapped into the coordinate system defined by this
+ matrix. Note that the transformed coordinates are rounded to the
+ nearest integer.
+*/
+
+/*!
+ \fn QLineF operator*(const QLineF &line, const QMatrix &matrix)
+ \relates QMatrix
+
+ This is the same as \a{matrix}.map(\a{line}).
+
+ \sa QMatrix::map()
+*/
+
+/*!
+ \fn QLine operator*(const QLine &line, const QMatrix &matrix)
+ \relates QMatrix
+
+ This is the same as \a{matrix}.map(\a{line}).
+
+ \sa QMatrix::map()
+*/
+
+/*!
+ \overload
+
+ Creates and returns a QLineF object that is a copy of the given \a
+ line, mapped into the coordinate system defined by this matrix.
+*/
+QLineF QMatrix::map(const QLineF &line) const
+{
+ return QLineF(map(line.p1()), map(line.p2()));
+}
+
+/*!
+ \overload
+
+ Creates and returns a QLine object that is a copy of the given \a
+ line, mapped into the coordinate system defined by this matrix.
+ Note that the transformed coordinates are rounded to the nearest
+ integer.
+*/
+QLine QMatrix::map(const QLine &line) const
+{
+ return QLine(map(line.p1()), map(line.p2()));
+}
+
+/*!
+ \fn QPolygonF operator *(const QPolygonF &polygon, const QMatrix &matrix)
+ \relates QMatrix
+
+ This is the same as \a{matrix}.map(\a{polygon}).
+
+ \sa QMatrix::map()
+*/
+
+/*!
+ \fn QPolygon operator*(const QPolygon &polygon, const QMatrix &matrix)
+ \relates QMatrix
+
+ This is the same as \a{matrix}.map(\a{polygon}).
+
+ \sa QMatrix::map()
+*/
+
+QPolygon QMatrix::map(const QPolygon &a) const
+{
+ int size = a.size();
+ int i;
+ QPolygon p(size);
+ const QPoint *da = a.constData();
+ QPoint *dp = p.data();
+ for(i = 0; i < size; i++) {
+ MAPINT(da[i].x(), da[i].y(), dp[i].rx(), dp[i].ry());
+ }
+ return p;
+}
+
+/*!
+ \fn QPolygonF QMatrix::map(const QPolygonF &polygon) const
+ \overload
+
+ Creates and returns a QPolygonF object that is a copy of the given
+ \a polygon, mapped into the coordinate system defined by this
+ matrix.
+*/
+QPolygonF QMatrix::map(const QPolygonF &a) const
+{
+ int size = a.size();
+ int i;
+ QPolygonF p(size);
+ const QPointF *da = a.constData();
+ QPointF *dp = p.data();
+ for(i = 0; i < size; i++) {
+ MAPDOUBLE(da[i].xp, da[i].yp, dp[i].xp, dp[i].yp);
+ }
+ return p;
+}
+
+/*!
+ \fn QPolygon QMatrix::map(const QPolygon &polygon) const
+ \overload
+
+ Creates and returns a QPolygon object that is a copy of the given
+ \a polygon, mapped into the coordinate system defined by this
+ matrix. Note that the transformed coordinates are rounded to the
+ nearest integer.
+*/
+
+/*!
+ \fn QRegion operator*(const QRegion &region, const QMatrix &matrix)
+ \relates QMatrix
+
+ This is the same as \a{matrix}.map(\a{region}).
+
+ \sa QMatrix::map()
+*/
+
+extern QPainterPath qt_regionToPath(const QRegion &region);
+
+/*!
+ \fn QRegion QMatrix::map(const QRegion &region) const
+ \overload
+
+ Creates and returns a QRegion object that is a copy of the given
+ \a region, mapped into the coordinate system defined by this matrix.
+
+ Calling this method can be rather expensive if rotations or
+ shearing are used.
+*/
+QRegion QMatrix::map(const QRegion &r) const
+{
+ if (_m11 == 1.0 && _m22 == 1.0 && _m12 == 0.0 && _m21 == 0.0) { // translate or identity
+ if (_dx == 0.0 && _dy == 0.0) // Identity
+ return r;
+ QRegion copy(r);
+ copy.translate(qRound(_dx), qRound(_dy));
+ return copy;
+ }
+
+ QPainterPath p = map(qt_regionToPath(r));
+ return p.toFillPolygon().toPolygon();
+}
+
+/*!
+ \fn QPainterPath operator *(const QPainterPath &path, const QMatrix &matrix)
+ \relates QMatrix
+
+ This is the same as \a{matrix}.map(\a{path}).
+
+ \sa QMatrix::map()
+*/
+
+/*!
+ \overload
+
+ Creates and returns a QPainterPath object that is a copy of the
+ given \a path, mapped into the coordinate system defined by this
+ matrix.
+*/
+QPainterPath QMatrix::map(const QPainterPath &path) const
+{
+ if (path.isEmpty())
+ return QPainterPath();
+
+ QPainterPath copy = path;
+
+ // Translate or identity
+ if (_m11 == 1.0 && _m22 == 1.0 && _m12 == 0.0 && _m21 == 0.0) {
+
+ // Translate
+ if (_dx != 0.0 || _dy != 0.0) {
+ copy.detach();
+ for (int i=0; i<path.elementCount(); ++i) {
+ QPainterPath::Element &e = copy.d_ptr->elements[i];
+ e.x += _dx;
+ e.y += _dy;
+ }
+ }
+
+ // Full xform
+ } else {
+ copy.detach();
+ for (int i=0; i<path.elementCount(); ++i) {
+ QPainterPath::Element &e = copy.d_ptr->elements[i];
+ qreal fx = e.x, fy = e.y;
+ e.x = _m11*fx + _m21*fy + _dx;
+ e.y = _m12*fx + _m22*fy + _dy;
+ }
+ }
+
+ return copy;
+}
+
+/*!
+ \fn QRegion QMatrix::mapToRegion(const QRect &rectangle) const
+
+ Returns the transformed rectangle \a rectangle as a QRegion
+ object. A rectangle which has been rotated or sheared may result
+ in a non-rectangular region being returned.
+
+ Use the mapToPolygon() or map() function instead.
+*/
+#ifdef QT3_SUPPORT
+QRegion QMatrix::mapToRegion(const QRect &rect) const
+{
+ QRegion result;
+ if (isIdentity()) {
+ result = rect;
+ } else if (m12() == 0.0F && m21() == 0.0F) {
+ int x = qRound(m11()*rect.x() + dx());
+ int y = qRound(m22()*rect.y() + dy());
+ int w = qRound(m11()*rect.width());
+ int h = qRound(m22()*rect.height());
+ if (w < 0) {
+ w = -w;
+ x -= w - 1;
+ }
+ if (h < 0) {
+ h = -h;
+ y -= h - 1;
+ }
+ result = QRect(x, y, w, h);
+ } else {
+ result = QRegion(mapToPolygon(rect));
+ }
+ return result;
+
+}
+#endif
+/*!
+ \fn QPolygon QMatrix::mapToPolygon(const QRect &rectangle) const
+
+ Creates and returns a QPolygon representation of the given \a
+ rectangle, mapped into the coordinate system defined by this
+ matrix.
+
+ The rectangle's coordinates are transformed using the following
+ formulas:
+
+ \snippet doc/src/snippets/code/src_gui_painting_qmatrix.cpp 3
+
+ Polygons and rectangles behave slightly differently when
+ transformed (due to integer rounding), so
+ \c{matrix.map(QPolygon(rectangle))} is not always the same as
+ \c{matrix.mapToPolygon(rectangle)}.
+
+ \sa mapRect(), {QMatrix#Basic Matrix Operations}{Basic Matrix
+ Operations}
+*/
+QPolygon QMatrix::mapToPolygon(const QRect &rect) const
+{
+ QPolygon a(4);
+ qreal x[4], y[4];
+ if (_m12 == 0.0F && _m21 == 0.0F) {
+ x[0] = _m11*rect.x() + _dx;
+ y[0] = _m22*rect.y() + _dy;
+ qreal w = _m11*rect.width();
+ qreal h = _m22*rect.height();
+ if (w < 0) {
+ w = -w;
+ x[0] -= w;
+ }
+ if (h < 0) {
+ h = -h;
+ y[0] -= h;
+ }
+ x[1] = x[0]+w;
+ x[2] = x[1];
+ x[3] = x[0];
+ y[1] = y[0];
+ y[2] = y[0]+h;
+ y[3] = y[2];
+ } else {
+ qreal right = rect.x() + rect.width();
+ qreal bottom = rect.y() + rect.height();
+ MAPDOUBLE(rect.x(), rect.y(), x[0], y[0]);
+ MAPDOUBLE(right, rect.y(), x[1], y[1]);
+ MAPDOUBLE(right, bottom, x[2], y[2]);
+ MAPDOUBLE(rect.x(), bottom, x[3], y[3]);
+ }
+#if 0
+ int i;
+ for(i = 0; i< 4; i++)
+ qDebug("coords(%d) = (%f/%f) (%d/%d)", i, x[i], y[i], qRound(x[i]), qRound(y[i]));
+ qDebug("width=%f, height=%f", qSqrt((x[1]-x[0])*(x[1]-x[0]) + (y[1]-y[0])*(y[1]-y[0])),
+ qSqrt((x[0]-x[3])*(x[0]-x[3]) + (y[0]-y[3])*(y[0]-y[3])));
+#endif
+ // all coordinates are correctly, tranform to a pointarray
+ // (rounding to the next integer)
+ a.setPoints(4, qRound(x[0]), qRound(y[0]),
+ qRound(x[1]), qRound(y[1]),
+ qRound(x[2]), qRound(y[2]),
+ qRound(x[3]), qRound(y[3]));
+ return a;
+}
+
+/*!
+ Resets the matrix to an identity matrix, i.e. all elements are set
+ to zero, except \c m11 and \c m22 (specifying the scale) which are
+ set to 1.
+
+ \sa QMatrix(), isIdentity(), {QMatrix#Basic Matrix
+ Operations}{Basic Matrix Operations}
+*/
+
+void QMatrix::reset()
+{
+ _m11 = _m22 = 1.0;
+ _m12 = _m21 = _dx = _dy = 0.0;
+}
+
+/*!
+ \fn bool QMatrix::isIdentity() const
+
+ Returns true if the matrix is the identity matrix, otherwise
+ returns false.
+
+ \sa reset()
+*/
+
+/*!
+ Moves the coordinate system \a dx along the x axis and \a dy along
+ the y axis, and returns a reference to the matrix.
+
+ \sa setMatrix()
+*/
+
+QMatrix &QMatrix::translate(qreal dx, qreal dy)
+{
+ _dx += dx*_m11 + dy*_m21;
+ _dy += dy*_m22 + dx*_m12;
+ return *this;
+}
+
+/*!
+ \fn QMatrix &QMatrix::scale(qreal sx, qreal sy)
+
+ Scales the coordinate system by \a sx horizontally and \a sy
+ vertically, and returns a reference to the matrix.
+
+ \sa setMatrix()
+*/
+
+QMatrix &QMatrix::scale(qreal sx, qreal sy)
+{
+ _m11 *= sx;
+ _m12 *= sx;
+ _m21 *= sy;
+ _m22 *= sy;
+ return *this;
+}
+
+/*!
+ Shears the coordinate system by \a sh horizontally and \a sv
+ vertically, and returns a reference to the matrix.
+
+ \sa setMatrix()
+*/
+
+QMatrix &QMatrix::shear(qreal sh, qreal sv)
+{
+ qreal tm11 = sv*_m21;
+ qreal tm12 = sv*_m22;
+ qreal tm21 = sh*_m11;
+ qreal tm22 = sh*_m12;
+ _m11 += tm11;
+ _m12 += tm12;
+ _m21 += tm21;
+ _m22 += tm22;
+ return *this;
+}
+
+const qreal deg2rad = qreal(0.017453292519943295769); // pi/180
+
+/*!
+ \fn QMatrix &QMatrix::rotate(qreal degrees)
+
+ Rotates the coordinate system the given \a degrees
+ counterclockwise.
+
+ Note that if you apply a QMatrix to a point defined in widget
+ coordinates, the direction of the rotation will be clockwise
+ because the y-axis points downwards.
+
+ Returns a reference to the matrix.
+
+ \sa setMatrix()
+*/
+
+QMatrix &QMatrix::rotate(qreal a)
+{
+ qreal sina = 0;
+ qreal cosa = 0;
+ if (a == 90. || a == -270.)
+ sina = 1.;
+ else if (a == 270. || a == -90.)
+ sina = -1.;
+ else if (a == 180.)
+ cosa = -1.;
+ else{
+ qreal b = deg2rad*a; // convert to radians
+ sina = qSin(b); // fast and convenient
+ cosa = qCos(b);
+ }
+ qreal tm11 = cosa*_m11 + sina*_m21;
+ qreal tm12 = cosa*_m12 + sina*_m22;
+ qreal tm21 = -sina*_m11 + cosa*_m21;
+ qreal tm22 = -sina*_m12 + cosa*_m22;
+ _m11 = tm11; _m12 = tm12;
+ _m21 = tm21; _m22 = tm22;
+ return *this;
+}
+
+/*!
+ \fn bool QMatrix::isInvertible() const
+
+ Returns true if the matrix is invertible, otherwise returns false.
+
+ \sa inverted()
+*/
+
+/*!
+ \fn qreal QMatrix::det() const
+
+ Returns the matrix's determinant.
+*/
+
+/*!
+ \fn QMatrix QMatrix::invert(bool *invertible) const
+
+ Returns an inverted copy of this matrix.
+
+ Use the inverted() function instead.
+*/
+
+/*!
+ Returns an inverted copy of this matrix.
+
+ If the matrix is singular (not invertible), the returned matrix is
+ the identity matrix. If \a invertible is valid (i.e. not 0), its
+ value is set to true if the matrix is invertible, otherwise it is
+ set to false.
+
+ \sa isInvertible()
+*/
+
+QMatrix QMatrix::inverted(bool *invertible) const
+{
+ qreal determinant = det();
+ if (determinant == 0.0) {
+ if (invertible)
+ *invertible = false; // singular matrix
+ QMatrix defaultMatrix;
+ return defaultMatrix;
+ }
+ else { // invertible matrix
+ if (invertible)
+ *invertible = true;
+ qreal dinv = 1.0/determinant;
+ QMatrix imatrix((_m22*dinv), (-_m12*dinv),
+ (-_m21*dinv), (_m11*dinv),
+ ((_m21*_dy - _m22*_dx)*dinv),
+ ((_m12*_dx - _m11*_dy)*dinv));
+ return imatrix;
+ }
+}
+
+
+/*!
+ \fn bool QMatrix::operator==(const QMatrix &matrix) const
+
+ Returns true if this matrix is equal to the given \a matrix,
+ otherwise returns false.
+*/
+
+bool QMatrix::operator==(const QMatrix &m) const
+{
+ return _m11 == m._m11 &&
+ _m12 == m._m12 &&
+ _m21 == m._m21 &&
+ _m22 == m._m22 &&
+ _dx == m._dx &&
+ _dy == m._dy;
+}
+
+/*!
+ \fn bool QMatrix::operator!=(const QMatrix &matrix) const
+
+ Returns true if this matrix is not equal to the given \a matrix,
+ otherwise returns false.
+*/
+
+bool QMatrix::operator!=(const QMatrix &m) const
+{
+ return _m11 != m._m11 ||
+ _m12 != m._m12 ||
+ _m21 != m._m21 ||
+ _m22 != m._m22 ||
+ _dx != m._dx ||
+ _dy != m._dy;
+}
+
+/*!
+ \fn QMatrix &QMatrix::operator *=(const QMatrix &matrix)
+ \overload
+
+ Returns the result of multiplying this matrix by the given \a
+ matrix.
+*/
+
+QMatrix &QMatrix::operator *=(const QMatrix &m)
+{
+ qreal tm11 = _m11*m._m11 + _m12*m._m21;
+ qreal tm12 = _m11*m._m12 + _m12*m._m22;
+ qreal tm21 = _m21*m._m11 + _m22*m._m21;
+ qreal tm22 = _m21*m._m12 + _m22*m._m22;
+
+ qreal tdx = _dx*m._m11 + _dy*m._m21 + m._dx;
+ qreal tdy = _dx*m._m12 + _dy*m._m22 + m._dy;
+
+ _m11 = tm11; _m12 = tm12;
+ _m21 = tm21; _m22 = tm22;
+ _dx = tdx; _dy = tdy;
+ return *this;
+}
+
+/*!
+ \fn QMatrix QMatrix::operator *(const QMatrix &matrix) const
+
+ Returns the result of multiplying this matrix by the given \a
+ matrix.
+
+ Note that matrix multiplication is not commutative, i.e. a*b !=
+ b*a.
+*/
+
+QMatrix QMatrix::operator *(const QMatrix &m) const
+{
+ QMatrix result = *this;
+ result *= m;
+ return result;
+}
+
+/*!
+ Assigns the given \a matrix's values to this matrix.
+*/
+QMatrix &QMatrix::operator=(const QMatrix &matrix)
+{
+ _m11 = matrix._m11;
+ _m12 = matrix._m12;
+ _m21 = matrix._m21;
+ _m22 = matrix._m22;
+ _dx = matrix._dx;
+ _dy = matrix._dy;
+ return *this;
+}
+
+/*!
+ \since 4.2
+
+ Returns the matrix as a QVariant.
+*/
+QMatrix::operator QVariant() const
+{
+ return QVariant(QVariant::Matrix, this);
+}
+
+Q_GUI_EXPORT QPainterPath operator *(const QPainterPath &p, const QMatrix &m)
+{
+ return m.map(p);
+}
+
+
+/*****************************************************************************
+ QMatrix stream functions
+ *****************************************************************************/
+#ifndef QT_NO_DATASTREAM
+/*!
+ \fn QDataStream &operator<<(QDataStream &stream, const QMatrix &matrix)
+ \relates QMatrix
+
+ Writes the given \a matrix to the given \a stream and returns a
+ reference to the stream.
+
+ \sa {Format of the QDataStream Operators}
+*/
+
+QDataStream &operator<<(QDataStream &s, const QMatrix &m)
+{
+ if (s.version() == 1) {
+ s << (float)m.m11() << (float)m.m12() << (float)m.m21()
+ << (float)m.m22() << (float)m.dx() << (float)m.dy();
+ } else {
+ s << double(m.m11())
+ << double(m.m12())
+ << double(m.m21())
+ << double(m.m22())
+ << double(m.dx())
+ << double(m.dy());
+ }
+ return s;
+}
+
+/*!
+ \fn QDataStream &operator>>(QDataStream &stream, QMatrix &matrix)
+ \relates QMatrix
+
+ Reads the given \a matrix from the given \a stream and returns a
+ reference to the stream.
+
+ \sa {Format of the QDataStream Operators}
+*/
+
+QDataStream &operator>>(QDataStream &s, QMatrix &m)
+{
+ if (s.version() == 1) {
+ float m11, m12, m21, m22, dx, dy;
+ s >> m11; s >> m12; s >> m21; s >> m22;
+ s >> dx; s >> dy;
+ m.setMatrix(m11, m12, m21, m22, dx, dy);
+ }
+ else {
+ double m11, m12, m21, m22, dx, dy;
+ s >> m11;
+ s >> m12;
+ s >> m21;
+ s >> m22;
+ s >> dx;
+ s >> dy;
+ m.setMatrix(m11, m12, m21, m22, dx, dy);
+ }
+ return s;
+}
+#endif // QT_NO_DATASTREAM
+
+#ifndef QT_NO_DEBUG_STREAM
+QDebug operator<<(QDebug dbg, const QMatrix &m)
+{
+ dbg.nospace() << "QMatrix("
+ << "11=" << m.m11()
+ << " 12=" << m.m12()
+ << " 21=" << m.m21()
+ << " 22=" << m.m22()
+ << " dx=" << m.dx()
+ << " dy=" << m.dy()
+ << ")";
+ return dbg.space();
+}
+#endif
+
+/*!
+ \fn QRect QMatrix::map(const QRect &rect) const
+ \compat
+
+ Creates and returns a QRect object that is a copy of the given
+ rectangle, mapped into the coordinate system defined by this
+ matrix.
+
+ Use the mapRect() function instead.
+*/
+
+QT_END_NAMESPACE
diff --git a/src/gui/painting/qmatrix.h b/src/gui/painting/qmatrix.h
new file mode 100644
index 0000000000..bf53c32ca1
--- /dev/null
+++ b/src/gui/painting/qmatrix.h
@@ -0,0 +1,175 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QMATRIX_H
+#define QMATRIX_H
+
+#include <QtGui/qpolygon.h>
+#include <QtGui/qregion.h>
+#include <QtGui/qwindowdefs.h>
+#include <QtCore/qline.h>
+#include <QtCore/qpoint.h>
+#include <QtCore/qrect.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+class QPainterPath;
+class QVariant;
+
+class Q_GUI_EXPORT QMatrix // 2D transform matrix
+{
+public:
+ QMatrix();
+ QMatrix(qreal m11, qreal m12, qreal m21, qreal m22,
+ qreal dx, qreal dy);
+ QMatrix(const QMatrix &matrix);
+
+ void setMatrix(qreal m11, qreal m12, qreal m21, qreal m22,
+ qreal dx, qreal dy);
+
+ qreal m11() const { return _m11; }
+ qreal m12() const { return _m12; }
+ qreal m21() const { return _m21; }
+ qreal m22() const { return _m22; }
+ qreal dx() const { return _dx; }
+ qreal dy() const { return _dy; }
+
+ void map(int x, int y, int *tx, int *ty) const;
+ void map(qreal x, qreal y, qreal *tx, qreal *ty) const;
+ QRect mapRect(const QRect &) const;
+ QRectF mapRect(const QRectF &) const;
+
+ QPoint map(const QPoint &p) const;
+ QPointF map(const QPointF&p) const;
+ QLine map(const QLine &l) const;
+ QLineF map(const QLineF &l) const;
+ QPolygonF map(const QPolygonF &a) const;
+ QPolygon map(const QPolygon &a) const;
+ QRegion map(const QRegion &r) const;
+ QPainterPath map(const QPainterPath &p) const;
+ QPolygon mapToPolygon(const QRect &r) const;
+
+ void reset();
+ inline bool isIdentity() const;
+
+ QMatrix &translate(qreal dx, qreal dy);
+ QMatrix &scale(qreal sx, qreal sy);
+ QMatrix &shear(qreal sh, qreal sv);
+ QMatrix &rotate(qreal a);
+
+ bool isInvertible() const { return !qFuzzyCompare(_m11*_m22 - _m12*_m21 + 1, 1); }
+ qreal det() const { return _m11*_m22 - _m12*_m21; }
+
+ QMatrix inverted(bool *invertible = 0) const;
+
+ bool operator==(const QMatrix &) const;
+ bool operator!=(const QMatrix &) const;
+
+ QMatrix &operator*=(const QMatrix &);
+ QMatrix operator*(const QMatrix &o) const;
+
+ QMatrix &operator=(const QMatrix &);
+
+ operator QVariant() const;
+
+#ifdef QT3_SUPPORT
+ inline QT3_SUPPORT QMatrix invert(bool *invertible=0) const { return inverted(invertible); }
+ inline QT3_SUPPORT QRect map(const QRect &r) const { return mapRect(r); }
+ QT3_SUPPORT QRegion mapToRegion(const QRect &r) const;
+#endif
+
+private:
+ friend class QTransform;
+ qreal _m11, _m12;
+ qreal _m21, _m22;
+ qreal _dx, _dy;
+};
+Q_DECLARE_TYPEINFO(QMatrix, Q_MOVABLE_TYPE);
+
+// mathematical semantics
+Q_GUI_EXPORT_INLINE QPoint operator*(const QPoint &p, const QMatrix &m)
+{ return m.map(p); }
+Q_GUI_EXPORT_INLINE QPointF operator*(const QPointF &p, const QMatrix &m)
+{ return m.map(p); }
+Q_GUI_EXPORT_INLINE QLineF operator*(const QLineF &l, const QMatrix &m)
+{ return m.map(l); }
+Q_GUI_EXPORT_INLINE QLine operator*(const QLine &l, const QMatrix &m)
+{ return m.map(l); }
+Q_GUI_EXPORT_INLINE QPolygon operator *(const QPolygon &a, const QMatrix &m)
+{ return m.map(a); }
+Q_GUI_EXPORT_INLINE QPolygonF operator *(const QPolygonF &a, const QMatrix &m)
+{ return m.map(a); }
+Q_GUI_EXPORT_INLINE QRegion operator *(const QRegion &r, const QMatrix &m)
+{ return m.map(r); }
+Q_GUI_EXPORT QPainterPath operator *(const QPainterPath &p, const QMatrix &m);
+
+inline bool QMatrix::isIdentity() const
+{
+ return qFuzzyCompare(_m11, 1) && qFuzzyCompare(_m22, 1) && qFuzzyCompare(_m12 + 1, 1)
+ && qFuzzyCompare(_m21 + 1, 1) && qFuzzyCompare(_dx + 1, 1) && qFuzzyCompare(_dy + 1, 1);
+}
+
+/*****************************************************************************
+ QMatrix stream functions
+ *****************************************************************************/
+
+Q_GUI_EXPORT QDataStream &operator<<(QDataStream &, const QMatrix &);
+Q_GUI_EXPORT QDataStream &operator>>(QDataStream &, QMatrix &);
+
+#ifndef QT_NO_DEBUG_STREAM
+Q_GUI_EXPORT QDebug operator<<(QDebug, const QMatrix &);
+#endif
+
+#ifdef QT3_SUPPORT
+QT_BEGIN_INCLUDE_NAMESPACE
+#include <QtGui/qwmatrix.h>
+QT_END_INCLUDE_NAMESPACE
+#endif
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QMATRIX_H
diff --git a/src/gui/painting/qmemrotate.cpp b/src/gui/painting/qmemrotate.cpp
new file mode 100644
index 0000000000..7ad0e42018
--- /dev/null
+++ b/src/gui/painting/qmemrotate.cpp
@@ -0,0 +1,547 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "private/qmemrotate_p.h"
+
+QT_BEGIN_NAMESPACE
+
+#if QT_ROTATION_ALGORITHM == QT_ROTATION_TILED
+static const int tileSize = 32;
+#endif
+
+#if Q_BYTE_ORDER == Q_BIG_ENDIAN
+#if QT_ROTATION_ALGORITHM == QT_ROTATION_PACKED || QT_ROTATION_ALGORITHM == QT_ROTATION_TILED
+#error Big endian version not implemented for the transformed driver!
+#endif
+#endif
+
+template <class DST, class SRC>
+Q_STATIC_TEMPLATE_FUNCTION inline void qt_memrotate90_cachedRead(const SRC *src, int w, int h,
+ int sstride,
+ DST *dest, int dstride)
+{
+ const char *s = reinterpret_cast<const char*>(src);
+ char *d = reinterpret_cast<char*>(dest);
+ for (int y = 0; y < h; ++y) {
+ for (int x = w - 1; x >= 0; --x) {
+ DST *destline = reinterpret_cast<DST*>(d + (w - x - 1) * dstride);
+ destline[y] = qt_colorConvert<DST,SRC>(src[x], 0);
+ }
+ s += sstride;
+ src = reinterpret_cast<const SRC*>(s);
+ }
+}
+
+template <class DST, class SRC>
+Q_STATIC_TEMPLATE_FUNCTION inline void qt_memrotate270_cachedRead(const SRC *src, int w, int h,
+ int sstride,
+ DST *dest, int dstride)
+{
+ const char *s = reinterpret_cast<const char*>(src);
+ char *d = reinterpret_cast<char*>(dest);
+ s += (h - 1) * sstride;
+ for (int y = h - 1; y >= 0; --y) {
+ src = reinterpret_cast<const SRC*>(s);
+ for (int x = 0; x < w; ++x) {
+ DST *destline = reinterpret_cast<DST*>(d + x * dstride);
+ destline[h - y - 1] = qt_colorConvert<DST,SRC>(src[x], 0);
+ }
+ s -= sstride;
+ }
+}
+
+#if QT_ROTATION_ALGORITHM == QT_ROTATION_CACHEDWRITE
+
+template <class DST, class SRC>
+Q_STATIC_TEMPLATE_FUNCTION inline void qt_memrotate90_cachedWrite(const SRC *src, int w, int h,
+ int sstride,
+ DST *dest, int dstride)
+{
+ for (int x = w - 1; x >= 0; --x) {
+ DST *d = dest + (w - x - 1) * dstride;
+ for (int y = 0; y < h; ++y) {
+ *d++ = qt_colorConvert<DST,SRC>(src[y * sstride + x], 0);
+ }
+ }
+
+}
+
+template <class DST, class SRC>
+Q_STATIC_TEMPLATE_FUNCTION inline void qt_memrotate270_cachedWrite(const SRC *src, int w, int h,
+ int sstride,
+ DST *dest, int dstride)
+{
+ for (int x = 0; x < w; ++x) {
+ DST *d = dest + x * dstride;
+ for (int y = h - 1; y >= 0; --y) {
+ *d++ = qt_colorConvert<DST,SRC>(src[y * sstride + x], 0);
+ }
+ }
+}
+
+#endif // QT_ROTATION_CACHEDWRITE
+
+#if QT_ROTATION_ALGORITHM == QT_ROTATION_PACKING
+
+// TODO: packing algorithms should probably be modified on 64-bit architectures
+
+template <class DST, class SRC>
+Q_STATIC_TEMPLATE_FUNCTION inline void qt_memrotate90_packing(const SRC *src, int w, int h,
+ int sstride,
+ DST *dest, int dstride)
+{
+ sstride /= sizeof(SRC);
+ dstride /= sizeof(DST);
+
+ const int pack = sizeof(quint32) / sizeof(DST);
+ const int unaligned = int((long(dest) & (sizeof(quint32)-1))) / sizeof(DST);
+
+ for (int x = w - 1; x >= 0; --x) {
+ int y = 0;
+
+ for (int i = 0; i < unaligned; ++i) {
+ dest[(w - x - 1) * dstride + y]
+ = qt_colorConvert<DST,SRC>(src[y * sstride + x], 0);
+ ++y;
+ }
+
+ quint32 *d = reinterpret_cast<quint32*>(dest + (w - x - 1) * dstride
+ + unaligned);
+ const int rest = (h - unaligned) % pack;
+ while (y < h - rest) {
+ quint32 c = qt_colorConvert<DST,SRC>(src[y * sstride + x], 0);
+ for (int i = 1; i < pack; ++i) {
+ c |= qt_colorConvert<DST,SRC>(src[(y + i) * sstride + x], 0)
+ << (sizeof(int) * 8 / pack * i);
+ }
+ *d++ = c;
+ y += pack;
+ }
+
+ while (y < h) {
+ dest[(w - x - 1) * dstride + y]
+ = qt_colorConvert<DST,SRC>(src[y * sstride + x], 0);
+ ++y;
+ }
+ }
+}
+
+template <class DST, class SRC>
+Q_STATIC_TEMPLATE_FUNCTION inline void qt_memrotate270_packing(const SRC *src, int w, int h,
+ int sstride,
+ DST *dest, int dstride)
+{
+ sstride /= sizeof(SRC);
+ dstride /= sizeof(DST);
+
+ const int pack = sizeof(quint32) / sizeof(DST);
+ const int unaligned = int((long(dest) & (sizeof(quint32)-1))) / sizeof(DST);
+
+ for (int x = 0; x < w; ++x) {
+ int y = h - 1;
+
+ for (int i = 0; i < unaligned; ++i) {
+ dest[x * dstride + h - y - 1]
+ = qt_colorConvert<DST,SRC>(src[y * sstride + x], 0);
+ --y;
+ }
+
+ quint32 *d = reinterpret_cast<quint32*>(dest + x * dstride
+ + unaligned);
+ const int rest = (h - unaligned) % pack;
+ while (y > rest) {
+ quint32 c = qt_colorConvert<DST,SRC>(src[y * sstride + x], 0);
+ for (int i = 1; i < pack; ++i) {
+ c |= qt_colorConvert<DST,SRC>(src[(y - i) * sstride + x], 0)
+ << (sizeof(int) * 8 / pack * i);
+ }
+ *d++ = c;
+ y -= pack;
+ }
+ while (y >= 0) {
+ dest[x * dstride + h - y - 1]
+ = qt_colorConvert<DST,SRC>(src[y * sstride + x], 0);
+ --y;
+ }
+ }
+}
+
+#endif // QT_ROTATION_PACKING
+
+#if QT_ROTATION_ALGORITHM == QT_ROTATION_TILED
+template <class DST, class SRC>
+Q_STATIC_TEMPLATE_FUNCTION inline void qt_memrotate90_tiled(const SRC *src, int w, int h,
+ int sstride,
+ DST *dest, int dstride)
+{
+ sstride /= sizeof(SRC);
+ dstride /= sizeof(DST);
+
+ const int pack = sizeof(quint32) / sizeof(DST);
+ const int unaligned =
+ qMin(uint((quintptr(dest) & (sizeof(quint32)-1)) / sizeof(DST)), uint(h));
+ const int restX = w % tileSize;
+ const int restY = (h - unaligned) % tileSize;
+ const int unoptimizedY = restY % pack;
+ const int numTilesX = w / tileSize + (restX > 0);
+ const int numTilesY = (h - unaligned) / tileSize + (restY >= pack);
+
+ for (int tx = 0; tx < numTilesX; ++tx) {
+ const int startx = w - tx * tileSize - 1;
+ const int stopx = qMax(startx - tileSize, 0);
+
+ if (unaligned) {
+ for (int x = startx; x >= stopx; --x) {
+ DST *d = dest + (w - x - 1) * dstride;
+ for (int y = 0; y < unaligned; ++y) {
+ *d++ = qt_colorConvert<DST,SRC>(src[y * sstride + x], 0);
+ }
+ }
+ }
+
+ for (int ty = 0; ty < numTilesY; ++ty) {
+ const int starty = ty * tileSize + unaligned;
+ const int stopy = qMin(starty + tileSize, h - unoptimizedY);
+
+ for (int x = startx; x >= stopx; --x) {
+ quint32 *d = reinterpret_cast<quint32*>(dest + (w - x - 1) * dstride + starty);
+ for (int y = starty; y < stopy; y += pack) {
+ quint32 c = qt_colorConvert<DST,SRC>(src[y * sstride + x], 0);
+ for (int i = 1; i < pack; ++i) {
+ const int shift = (sizeof(int) * 8 / pack * i);
+ const DST color = qt_colorConvert<DST,SRC>(src[(y + i) * sstride + x], 0);
+ c |= color << shift;
+ }
+ *d++ = c;
+ }
+ }
+ }
+
+ if (unoptimizedY) {
+ const int starty = h - unoptimizedY;
+ for (int x = startx; x >= stopx; --x) {
+ DST *d = dest + (w - x - 1) * dstride + starty;
+ for (int y = starty; y < h; ++y) {
+ *d++ = qt_colorConvert<DST,SRC>(src[y * sstride + x], 0);
+ }
+ }
+ }
+ }
+}
+
+template <class DST, class SRC>
+Q_STATIC_TEMPLATE_FUNCTION inline void qt_memrotate90_tiled_unpacked(const SRC *src, int w, int h,
+ int sstride,
+ DST *dest, int dstride)
+{
+ const int numTilesX = (w + tileSize - 1) / tileSize;
+ const int numTilesY = (h + tileSize - 1) / tileSize;
+
+ for (int tx = 0; tx < numTilesX; ++tx) {
+ const int startx = w - tx * tileSize - 1;
+ const int stopx = qMax(startx - tileSize, 0);
+
+ for (int ty = 0; ty < numTilesY; ++ty) {
+ const int starty = ty * tileSize;
+ const int stopy = qMin(starty + tileSize, h);
+
+ for (int x = startx; x >= stopx; --x) {
+ DST *d = (DST*)((char*)dest + (w - x - 1) * dstride) + starty;
+ const char *s = (const char*)(src + x) + starty * sstride;
+ for (int y = starty; y < stopy; ++y) {
+ *d++ = qt_colorConvert<DST,SRC>(*(const SRC*)(s), 0);
+ s += sstride;
+ }
+ }
+ }
+ }
+}
+
+template <class DST, class SRC>
+Q_STATIC_TEMPLATE_FUNCTION inline void qt_memrotate270_tiled(const SRC *src, int w, int h,
+ int sstride,
+ DST *dest, int dstride)
+{
+ sstride /= sizeof(SRC);
+ dstride /= sizeof(DST);
+
+ const int pack = sizeof(quint32) / sizeof(DST);
+ const int unaligned =
+ qMin(uint((long(dest) & (sizeof(quint32)-1)) / sizeof(DST)), uint(h));
+ const int restX = w % tileSize;
+ const int restY = (h - unaligned) % tileSize;
+ const int unoptimizedY = restY % pack;
+ const int numTilesX = w / tileSize + (restX > 0);
+ const int numTilesY = (h - unaligned) / tileSize + (restY >= pack);
+
+ for (int tx = 0; tx < numTilesX; ++tx) {
+ const int startx = tx * tileSize;
+ const int stopx = qMin(startx + tileSize, w);
+
+ if (unaligned) {
+ for (int x = startx; x < stopx; ++x) {
+ DST *d = dest + x * dstride;
+ for (int y = h - 1; y >= h - unaligned; --y) {
+ *d++ = qt_colorConvert<DST,SRC>(src[y * sstride + x], 0);
+ }
+ }
+ }
+
+ for (int ty = 0; ty < numTilesY; ++ty) {
+ const int starty = h - 1 - unaligned - ty * tileSize;
+ const int stopy = qMax(starty - tileSize, unoptimizedY);
+
+ for (int x = startx; x < stopx; ++x) {
+ quint32 *d = reinterpret_cast<quint32*>(dest + x * dstride
+ + h - 1 - starty);
+ for (int y = starty; y > stopy; y -= pack) {
+ quint32 c = qt_colorConvert<DST,SRC>(src[y * sstride + x], 0);
+ for (int i = 1; i < pack; ++i) {
+ const int shift = (sizeof(int) * 8 / pack * i);
+ const DST color = qt_colorConvert<DST,SRC>(src[(y - i) * sstride + x], 0);
+ c |= color << shift;
+ }
+ *d++ = c;
+ }
+ }
+ }
+ if (unoptimizedY) {
+ const int starty = unoptimizedY - 1;
+ for (int x = startx; x < stopx; ++x) {
+ DST *d = dest + x * dstride + h - 1 - starty;
+ for (int y = starty; y >= 0; --y) {
+ *d++ = qt_colorConvert<DST,SRC>(src[y * sstride + x], 0);
+ }
+ }
+ }
+ }
+}
+
+template <class DST, class SRC>
+Q_STATIC_TEMPLATE_FUNCTION inline void qt_memrotate270_tiled_unpacked(const SRC *src, int w, int h,
+ int sstride,
+ DST *dest, int dstride)
+{
+ const int numTilesX = (w + tileSize - 1) / tileSize;
+ const int numTilesY = (h + tileSize - 1) / tileSize;
+
+ for (int tx = 0; tx < numTilesX; ++tx) {
+ const int startx = tx * tileSize;
+ const int stopx = qMin(startx + tileSize, w);
+
+ for (int ty = 0; ty < numTilesY; ++ty) {
+ const int starty = h - 1 - ty * tileSize;
+ const int stopy = qMax(starty - tileSize, 0);
+
+ for (int x = startx; x < stopx; ++x) {
+ DST *d = (DST*)((char*)dest + x * dstride) + h - 1 - starty;
+ const char *s = (const char*)(src + x) + starty * sstride;
+ for (int y = starty; y >= stopy; --y) {
+ *d++ = qt_colorConvert<DST,SRC>(*(const SRC*)s, 0);
+ s -= sstride;
+ }
+ }
+ }
+ }
+}
+
+#endif // QT_ROTATION_ALGORITHM
+
+template <class DST, class SRC>
+Q_STATIC_TEMPLATE_FUNCTION inline void qt_memrotate90_template(const SRC *src,
+ int srcWidth, int srcHeight, int srcStride,
+ DST *dest, int dstStride)
+{
+#if QT_ROTATION_ALGORITHM == QT_ROTATION_CACHEDREAD
+ qt_memrotate90_cachedRead<DST,SRC>(src, srcWidth, srcHeight, srcStride,
+ dest, dstStride);
+#elif QT_ROTATION_ALGORITHM == QT_ROTATION_CACHEDWRITE
+ qt_memrotate90_cachedWrite<DST,SRC>(src, srcWidth, srcHeight, srcStride,
+ dest, dstStride);
+#elif QT_ROTATION_ALGORITHM == QT_ROTATION_PACKING
+ qt_memrotate90_packing<DST,SRC>(src, srcWidth, srcHeight, srcStride,
+ dest, dstStride);
+#elif QT_ROTATION_ALGORITHM == QT_ROTATION_TILED
+ qt_memrotate90_tiled<DST,SRC>(src, srcWidth, srcHeight, srcStride,
+ dest, dstStride);
+#endif
+}
+
+template <class DST, class SRC>
+Q_STATIC_TEMPLATE_FUNCTION inline void qt_memrotate180_template(const SRC *src,
+ int w, int h, int sstride,
+ DST *dest, int dstride)
+{
+ const char *s = (const char*)(src) + (h - 1) * sstride;
+ for (int y = h - 1; y >= 0; --y) {
+ DST *d = reinterpret_cast<DST*>((char *)(dest) + (h - y - 1) * dstride);
+ src = reinterpret_cast<const SRC*>(s);
+ for (int x = w - 1; x >= 0; --x) {
+ d[w - x - 1] = qt_colorConvert<DST,SRC>(src[x], 0);
+ }
+ s -= sstride;
+ }
+}
+
+template <class DST, class SRC>
+Q_STATIC_TEMPLATE_FUNCTION inline void qt_memrotate270_template(const SRC *src,
+ int srcWidth, int srcHeight, int srcStride,
+ DST *dest, int dstStride)
+{
+#if QT_ROTATION_ALGORITHM == QT_ROTATION_CACHEDREAD
+ qt_memrotate270_cachedRead<DST,SRC>(src, srcWidth, srcHeight, srcStride,
+ dest, dstStride);
+#elif QT_ROTATION_ALGORITHM == QT_ROTATION_CACHEDWRITE
+ qt_memrotate270_cachedWrite<DST,SRC>(src, srcWidth, srcHeight, srcStride,
+ dest, dstStride);
+#elif QT_ROTATION_ALGORITHM == QT_ROTATION_PACKING
+ qt_memrotate270_packing<DST,SRC>(src, srcWidth, srcHeight, srcStride,
+ dest, dstStride);
+#elif QT_ROTATION_ALGORITHM == QT_ROTATION_TILED
+ qt_memrotate270_tiled_unpacked<DST,SRC>(src, srcWidth, srcHeight,
+ srcStride,
+ dest, dstStride);
+#endif
+}
+
+template <>
+Q_STATIC_TEMPLATE_SPECIALIZATION
+inline void qt_memrotate90_template<quint24, quint24>(const quint24 *src,
+ int srcWidth, int srcHeight, int srcStride,
+ quint24 *dest, int dstStride)
+{
+#if QT_ROTATION_ALGORITHM == QT_ROTATION_CACHEDREAD
+ qt_memrotate90_cachedRead<quint24,quint24>(src, srcWidth, srcHeight,
+ srcStride, dest, dstStride);
+#elif QT_ROTATION_ALGORITHM == QT_ROTATION_CACHEDWRITE
+ qt_memrotate90_cachedWrite<quint24,quint24>(src, srcWidth, srcHeight,
+ srcStride, dest, dstStride);
+#elif QT_ROTATION_ALGORITHM == QT_ROTATION_PACKING
+ // packed algorithm not implemented
+ qt_memrotate90_cachedRead<quint24,quint24>(src, srcWidth, srcHeight,
+ srcStride, dest, dstStride);
+#elif QT_ROTATION_ALGORITHM == QT_ROTATION_TILED
+ // packed algorithm not implemented
+ qt_memrotate90_tiled_unpacked<quint24,quint24>(src, srcWidth, srcHeight,
+ srcStride, dest, dstStride);
+#endif
+}
+
+template <>
+Q_STATIC_TEMPLATE_SPECIALIZATION
+inline void qt_memrotate90_template<quint24, quint32>(const quint32 *src,
+ int srcWidth, int srcHeight, int srcStride,
+ quint24 *dest, int dstStride)
+{
+#if QT_ROTATION_ALGORITHM == QT_ROTATION_CACHEDREAD
+ qt_memrotate90_cachedRead<quint24,quint32>(src, srcWidth, srcHeight,
+ srcStride, dest, dstStride);
+#elif QT_ROTATION_ALGORITHM == QT_ROTATION_CACHEDWRITE
+ qt_memrotate90_cachedWrite<quint24,quint32>(src, srcWidth, srcHeight,
+ srcStride, dest, dstStride);
+#elif QT_ROTATION_ALGORITHM == QT_ROTATION_PACKING
+ // packed algorithm not implemented
+ qt_memrotate90_cachedRead<quint24,quint32>(src, srcWidth, srcHeight,
+ srcStride, dest, dstStride);
+#elif QT_ROTATION_ALGORITHM == QT_ROTATION_TILED
+ // packed algorithm not implemented
+ qt_memrotate90_tiled_unpacked<quint24,quint32>(src, srcWidth, srcHeight,
+ srcStride, dest, dstStride);
+#endif
+}
+
+template <>
+Q_STATIC_TEMPLATE_SPECIALIZATION
+inline void qt_memrotate90_template<quint18, quint32>(const quint32 *src,
+ int srcWidth, int srcHeight, int srcStride,
+ quint18 *dest, int dstStride)
+{
+#if QT_ROTATION_ALGORITHM == QT_ROTATION_CACHEDREAD
+ qt_memrotate90_cachedRead<quint18,quint32>(src, srcWidth, srcHeight,
+ srcStride, dest, dstStride);
+#elif QT_ROTATION_ALGORITHM == QT_ROTATION_CACHEDWRITE
+ qt_memrotate90_cachedWrite<quint18,quint32>(src, srcWidth, srcHeight,
+ srcStride, dest, dstStride);
+#elif QT_ROTATION_ALGORITHM == QT_ROTATION_PACKING
+ // packed algorithm not implemented
+ qt_memrotate90_cachedRead<quint18,quint32>(src, srcWidth, srcHeight,
+ srcStride, dest, dstStride);
+#elif QT_ROTATION_ALGORITHM == QT_ROTATION_TILED
+ // packed algorithm not implemented
+ qt_memrotate90_tiled_unpacked<quint18,quint32>(src, srcWidth, srcHeight,
+ srcStride, dest, dstStride);
+#endif
+}
+
+#define QT_IMPL_MEMROTATE(srctype, desttype) \
+void qt_memrotate90(const srctype *src, int w, int h, int sstride, \
+ desttype *dest, int dstride) \
+{ \
+ qt_memrotate90_template(src, w, h, sstride, dest, dstride); \
+} \
+void qt_memrotate180(const srctype *src, int w, int h, int sstride, \
+ desttype *dest, int dstride) \
+{ \
+ qt_memrotate180_template(src, w, h, sstride, dest, dstride); \
+} \
+void qt_memrotate270(const srctype *src, int w, int h, int sstride, \
+ desttype *dest, int dstride) \
+{ \
+ qt_memrotate270_template(src, w, h, sstride, dest, dstride); \
+}
+
+QT_IMPL_MEMROTATE(quint32, quint32)
+QT_IMPL_MEMROTATE(quint32, quint16)
+QT_IMPL_MEMROTATE(quint16, quint32)
+QT_IMPL_MEMROTATE(quint16, quint16)
+QT_IMPL_MEMROTATE(quint24, quint24)
+QT_IMPL_MEMROTATE(quint32, quint24)
+QT_IMPL_MEMROTATE(quint32, quint18)
+QT_IMPL_MEMROTATE(quint32, quint8)
+QT_IMPL_MEMROTATE(quint16, quint8)
+QT_IMPL_MEMROTATE(quint8, quint8)
+#ifdef QT_QWS_DEPTH_GENERIC
+QT_IMPL_MEMROTATE(quint32, qrgb_generic16)
+QT_IMPL_MEMROTATE(quint16, qrgb_generic16)
+#endif
+
+
+QT_END_NAMESPACE
diff --git a/src/gui/painting/qmemrotate_p.h b/src/gui/painting/qmemrotate_p.h
new file mode 100644
index 0000000000..bd6006bf30
--- /dev/null
+++ b/src/gui/painting/qmemrotate_p.h
@@ -0,0 +1,103 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QMEMROTATE_P_H
+#define QMEMROTATE_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/qdrawhelper_p.h"
+
+QT_BEGIN_NAMESPACE
+
+#define QT_ROTATION_CACHEDREAD 1
+#define QT_ROTATION_CACHEDWRITE 2
+#define QT_ROTATION_PACKING 3
+#define QT_ROTATION_TILED 4
+
+#ifndef QT_ROTATION_ALGORITHM
+#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
+#define QT_ROTATION_ALGORITHM QT_ROTATION_TILED
+#else
+#define QT_ROTATION_ALGORITHM QT_ROTATION_CACHEDREAD
+#endif
+#endif
+
+#ifdef Q_WS_QWS
+#define Q_GUI_QWS_EXPORT Q_GUI_EXPORT
+#else
+#define Q_GUI_QWS_EXPORT
+#endif
+
+#define QT_DECL_MEMROTATE(srctype, desttype) \
+ void Q_GUI_QWS_EXPORT qt_memrotate90(const srctype*, int, int, int, desttype*, int); \
+ void Q_GUI_QWS_EXPORT qt_memrotate180(const srctype*, int, int, int, desttype*, int); \
+ void Q_GUI_QWS_EXPORT qt_memrotate270(const srctype*, int, int, int, desttype*, int)
+
+QT_DECL_MEMROTATE(quint32, quint32);
+QT_DECL_MEMROTATE(quint32, quint16);
+QT_DECL_MEMROTATE(quint16, quint32);
+QT_DECL_MEMROTATE(quint16, quint16);
+QT_DECL_MEMROTATE(quint24, quint24);
+QT_DECL_MEMROTATE(quint32, quint24);
+QT_DECL_MEMROTATE(quint32, quint18);
+QT_DECL_MEMROTATE(quint32, quint8);
+QT_DECL_MEMROTATE(quint16, quint8);
+QT_DECL_MEMROTATE(quint8, quint8);
+#ifdef QT_QWS_DEPTH_GENERIC
+QT_DECL_MEMROTATE(quint32, qrgb_generic16);
+QT_DECL_MEMROTATE(quint16, qrgb_generic16);
+#endif
+
+#undef QT_DECL_MEMROTATE
+
+QT_END_NAMESPACE
+
+#endif // QMEMROTATE_P_H
diff --git a/src/gui/painting/qoutlinemapper.cpp b/src/gui/painting/qoutlinemapper.cpp
new file mode 100644
index 0000000000..4f90e71d2f
--- /dev/null
+++ b/src/gui/painting/qoutlinemapper.cpp
@@ -0,0 +1,412 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qoutlinemapper_p.h"
+
+#include "qmath.h"
+
+#include <stdlib.h>
+
+QT_BEGIN_NAMESPACE
+
+static const qreal aliasedCoordinateDelta = 0.5 - 0.015625;
+
+#define qreal_to_fixed_26_6(f) (int(f * 64))
+
+
+
+
+static const QRectF boundingRect(const QPointF *points, int pointCount)
+{
+ const QPointF *e = points;
+ const QPointF *last = points + pointCount;
+ qreal minx, maxx, miny, maxy;
+ minx = maxx = e->x();
+ miny = maxy = e->y();
+ while (++e < last) {
+ if (e->x() < minx)
+ minx = e->x();
+ else if (e->x() > maxx)
+ maxx = e->x();
+ if (e->y() < miny)
+ miny = e->y();
+ else if (e->y() > maxy)
+ maxy = e->y();
+ }
+ return QRectF(QPointF(minx, miny), QPointF(maxx, maxy));
+}
+
+
+QT_FT_Outline *QOutlineMapper::convertPath(const QPainterPath &path)
+{
+ Q_ASSERT(!path.isEmpty());
+ int elmCount = path.elementCount();
+#ifdef QT_DEBUG_CONVERT
+ printf("QOutlineMapper::convertPath(), size=%d\n", elmCount);
+#endif
+ beginOutline(path.fillRule());
+
+ for (int index=0; index<elmCount; ++index) {
+ const QPainterPath::Element &elm = path.elementAt(index);
+
+ switch (elm.type) {
+
+ case QPainterPath::MoveToElement:
+ if (index == elmCount - 1)
+ continue;
+ moveTo(elm);
+ break;
+
+ case QPainterPath::LineToElement:
+ lineTo(elm);
+ break;
+
+ case QPainterPath::CurveToElement:
+ curveTo(elm, path.elementAt(index + 1), path.elementAt(index + 2));
+ index += 2;
+ break;
+
+ default:
+ break; // This will never hit..
+ }
+ }
+
+ endOutline();
+ return outline();
+}
+
+QT_FT_Outline *QOutlineMapper::convertPath(const QVectorPath &path)
+{
+ int count = path.elementCount();
+
+#ifdef QT_DEBUG_CONVERT
+ printf("QOutlineMapper::convertPath(VP), size=%d\n", count);
+#endif
+ beginOutline(path.hasWindingFill() ? Qt::WindingFill : Qt::OddEvenFill);
+
+ if (path.elements()) {
+ // TODO: if we do closing of subpaths in convertElements instead we
+ // could avoid this loop
+ const QPainterPath::ElementType *elements = path.elements();
+ const QPointF *points = reinterpret_cast<const QPointF *>(path.points());
+
+ for (int index = 0; index < count; ++index) {
+ switch (elements[index]) {
+ case QPainterPath::MoveToElement:
+ if (index == count - 1)
+ continue;
+ moveTo(points[index]);
+ break;
+
+ case QPainterPath::LineToElement:
+ lineTo(points[index]);
+ break;
+
+ case QPainterPath::CurveToElement:
+ curveTo(points[index], points[index+1], points[index+2]);
+ index += 2;
+ break;
+
+ default:
+ break; // This will never hit..
+ }
+ }
+
+ } else {
+ // ### We can kill this copying and just use the buffer straight...
+
+ m_elements.resize(count);
+ memcpy(m_elements.data(), path.points(), count* sizeof(QPointF));
+
+ m_element_types.resize(0);
+ }
+
+ endOutline();
+ return outline();
+}
+
+
+void QOutlineMapper::endOutline()
+{
+ closeSubpath();
+
+ int element_count = m_elements.size();
+
+ if (element_count == 0) {
+ memset(&m_outline, 0, sizeof(m_outline));
+ return;
+ }
+
+ QPointF *elements;
+
+ // Transform the outline
+ if (m_txop == QTransform::TxNone) {
+ elements = m_elements.data();
+ } else {
+ if (m_txop == QTransform::TxTranslate) {
+ for (int i=0; i<m_elements.size(); ++i) {
+ const QPointF &e = m_elements.at(i);
+ m_elements_dev << QPointF(e.x() + m_dx, e.y() + m_dy);
+ }
+ } else if (m_txop == QTransform::TxScale) {
+ for (int i=0; i<m_elements.size(); ++i) {
+ const QPointF &e = m_elements.at(i);
+ m_elements_dev << QPointF(m_m11 * e.x() + m_dx, m_m22 * e.y() + m_dy);
+ }
+ } else if (m_txop < QTransform::TxProject) {
+ for (int i=0; i<m_elements.size(); ++i) {
+ const QPointF &e = m_elements.at(i);
+ m_elements_dev << QPointF(m_m11 * e.x() + m_m21 * e.y() + m_dx,
+ m_m22 * e.y() + m_m12 * e.x() + m_dy);
+ }
+ } else {
+ // ## TODO: this case needs to be plain code polygonal paths
+ QPainterPath path;
+ if (m_element_types.isEmpty()) {
+ if (!m_elements.isEmpty())
+ path.moveTo(m_elements.at(0));
+ for (int i=1; i<m_elements.size(); ++i)
+ path.lineTo(m_elements.at(i));
+ } else {
+ for (int i=0; i<m_elements.size(); ++i) {
+ switch (m_element_types.at(i)) {
+ case QPainterPath::MoveToElement:
+ path.moveTo(m_elements.at(i));
+ break;
+ case QPainterPath::LineToElement:
+ path.lineTo(m_elements.at(i));
+ break;
+ case QPainterPath::CurveToElement:
+ path.cubicTo(m_elements.at(i), m_elements.at(i+1), m_elements.at(i+2));
+ i += 2;
+ break;
+ default:
+ Q_ASSERT(false);
+ break;
+ }
+ }
+ }
+ path = QTransform(m_m11, m_m12, m_m13, m_m21, m_m22, m_m23, m_dx, m_dy, m_m33).map(path);
+ uint old_txop = m_txop;
+ m_txop = QTransform::TxNone;
+ if (path.isEmpty())
+ m_valid = false;
+ else
+ convertPath(path);
+ m_txop = old_txop;
+ return;
+ }
+ elements = m_elements_dev.data();
+ }
+
+ if (m_round_coords) {
+ // round coordinates to match outlines drawn with drawLine_midpoint_i
+ for (int i = 0; i < m_elements.size(); ++i)
+ elements[i] = QPointF(qFloor(elements[i].x() + aliasedCoordinateDelta),
+ qFloor(elements[i].y() + aliasedCoordinateDelta));
+ }
+
+ controlPointRect = boundingRect(elements, element_count);
+
+#ifdef QT_DEBUG_CONVERT
+ printf(" - control point rect (%.2f, %.2f) %.2f x %.2f\n",
+ controlPointRect.x(), controlPointRect.y(),
+ controlPointRect.width(), controlPointRect.height());
+#endif
+
+
+ // Check for out of dev bounds...
+ const bool do_clip = (controlPointRect.left() < -QT_RASTER_COORD_LIMIT
+ || controlPointRect.right() > QT_RASTER_COORD_LIMIT
+ || controlPointRect.top() < -QT_RASTER_COORD_LIMIT
+ || controlPointRect.bottom() > QT_RASTER_COORD_LIMIT);
+
+ if (do_clip) {
+ clipElements(elements, elementTypes(), element_count);
+ } else {
+ convertElements(elements, elementTypes(), element_count);
+ }
+}
+
+void QOutlineMapper::convertElements(const QPointF *elements,
+ const QPainterPath::ElementType *types,
+ int element_count)
+{
+
+ if (types) {
+ // Translate into FT coords
+ const QPointF *e = elements;
+ for (int i=0; i<element_count; ++i) {
+ switch (*types) {
+ case QPainterPath::MoveToElement:
+ {
+ QT_FT_Vector pt_fixed = { qreal_to_fixed_26_6(e->x()),
+ qreal_to_fixed_26_6(e->y()) };
+ if (i != 0)
+ m_contours << m_points.size() - 1;
+ m_points << pt_fixed;
+ m_tags << QT_FT_CURVE_TAG_ON;
+ }
+ break;
+
+ case QPainterPath::LineToElement:
+ {
+ QT_FT_Vector pt_fixed = { qreal_to_fixed_26_6(e->x()),
+ qreal_to_fixed_26_6(e->y()) };
+ m_points << pt_fixed;
+ m_tags << QT_FT_CURVE_TAG_ON;
+ }
+ break;
+
+ case QPainterPath::CurveToElement:
+ {
+ QT_FT_Vector cp1_fixed = { qreal_to_fixed_26_6(e->x()),
+ qreal_to_fixed_26_6(e->y()) };
+ ++e;
+ QT_FT_Vector cp2_fixed = { qreal_to_fixed_26_6((e)->x()),
+ qreal_to_fixed_26_6((e)->y()) };
+ ++e;
+ QT_FT_Vector ep_fixed = { qreal_to_fixed_26_6((e)->x()),
+ qreal_to_fixed_26_6((e)->y()) };
+
+ m_points << cp1_fixed << cp2_fixed << ep_fixed;
+ m_tags << QT_FT_CURVE_TAG_CUBIC
+ << QT_FT_CURVE_TAG_CUBIC
+ << QT_FT_CURVE_TAG_ON;
+
+ types += 2;
+ i += 2;
+ }
+ break;
+ default:
+ break;
+ }
+ ++types;
+ ++e;
+ }
+ } else {
+ // Plain polygon...
+ const QPointF *last = elements + element_count;
+ const QPointF *e = elements;
+ while (e < last) {
+ QT_FT_Vector pt_fixed = { qreal_to_fixed_26_6(e->x()),
+ qreal_to_fixed_26_6(e->y()) };
+ m_points << pt_fixed;
+ m_tags << QT_FT_CURVE_TAG_ON;
+ ++e;
+ }
+ }
+
+ // close the very last subpath
+ m_contours << m_points.size() - 1;
+
+ m_outline.n_contours = m_contours.size();
+ m_outline.n_points = m_points.size();
+
+ m_outline.points = m_points.data();
+ m_outline.tags = m_tags.data();
+ m_outline.contours = m_contours.data();
+
+#ifdef QT_DEBUG_CONVERT
+ printf("QOutlineMapper::endOutline\n");
+
+ printf(" - contours: %d\n", m_outline.n_contours);
+ for (int i=0; i<m_outline.n_contours; ++i) {
+ printf(" - %d\n", m_outline.contours[i]);
+ }
+
+ printf(" - points: %d\n", m_outline.n_points);
+ for (int i=0; i<m_outline.n_points; ++i) {
+ printf(" - %d -- %.2f, %.2f, (%d, %d)\n", i,
+ (double) (m_outline.points[i].x / 64.0),
+ (double) (m_outline.points[i].y / 64.0),
+ (int) m_outline.points[i].x, (int) m_outline.points[i].y);
+ }
+#endif
+}
+
+void QOutlineMapper::clipElements(const QPointF *elements,
+ const QPainterPath::ElementType *types,
+ int element_count)
+{
+ // We could save a bit of time by actually implementing them fully
+ // instead of going through convenience functionallity, but since
+ // this part of code hardly every used, it shouldn't matter.
+
+ QPainterPath path;
+ if (types) {
+ for (int i=0; i<element_count; ++i) {
+ switch (types[i]) {
+ case QPainterPath::MoveToElement:
+ path.moveTo(elements[i]);
+ break;
+
+ case QPainterPath::LineToElement:
+ path.lineTo(elements[i]);
+ break;
+
+ case QPainterPath::CurveToElement:
+ path.cubicTo(elements[i], elements[i+1], elements[i+2]);
+ i += 2;
+ break;
+ default:
+ break;
+ }
+ }
+ } else {
+ path.moveTo(elements[0]);
+ for (int i=1; i<element_count; ++i)
+ path.lineTo(elements[i]);
+ }
+
+ QPainterPath clipPath;
+ clipPath.addRect(m_clip_rect);
+ QPainterPath clippedPath = path.intersected(clipPath);
+ uint old_txop = m_txop;
+ m_txop = QTransform::TxNone;
+ if (clippedPath.isEmpty())
+ m_valid = false;
+ else
+ convertPath(clippedPath);
+ m_txop = old_txop;
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/painting/qoutlinemapper_p.h b/src/gui/painting/qoutlinemapper_p.h
new file mode 100644
index 0000000000..129169e33a
--- /dev/null
+++ b/src/gui/painting/qoutlinemapper_p.h
@@ -0,0 +1,238 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QOUTLINEMAPPER_P_H
+#define QOUTLINEMAPPER_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 <QtCore/qrect.h>
+
+#include <QtGui/qtransform.h>
+#include <QtGui/qpainterpath.h>
+
+#define QT_FT_BEGIN_HEADER
+#define QT_FT_END_HEADER
+
+#include <private/qrasterdefs_p.h>
+#include <private/qdatabuffer_p.h>
+#include "qpaintengineex_p.h"
+
+QT_BEGIN_NAMESPACE
+
+// This limitations comes from qgrayraster.c. Any higher and
+// rasterization of shapes will produce incorrect results.
+const int QT_RASTER_COORD_LIMIT = 32767;
+
+//#define QT_DEBUG_CONVERT
+
+/********************************************************************************
+ * class QOutlineMapper
+ *
+ * Used to map between QPainterPath and the QT_FT_Outline structure used by the
+ * freetype scanconvertor.
+ *
+ * The outline mapper uses a path iterator to get points from the path,
+ * so that it is possible to transform the points as they are converted. The
+ * callback can be a noop, translate or full-fledged xform. (Tests indicated
+ * that using a C callback was low cost).
+ */
+class QOutlineMapper
+{
+public:
+ QOutlineMapper()
+ : m_round_coords(false)
+ {
+ }
+
+ /*!
+ Sets up the matrix to be used for conversion. This also
+ sets up the qt_path_iterator function that is used as a callback
+ to get points.
+ */
+ void setMatrix(const QTransform &m)
+ {
+ m_m11 = m.m11();
+ m_m12 = m.m12();
+ m_m13 = m.m13();
+ m_m21 = m.m21();
+ m_m22 = m.m22();
+ m_m23 = m.m23();
+ m_m33 = m.m33();
+ m_dx = m.dx();
+ m_dy = m.dy();
+ m_txop = m.type();
+ }
+
+ void beginOutline(Qt::FillRule fillRule)
+ {
+#ifdef QT_DEBUG_CONVERT
+ printf("QOutlineMapper::beginOutline rule=%d\n", fillRule);
+#endif
+ m_valid = true;
+ m_elements.reset();
+ m_elements_dev.reset();
+ m_element_types.reset();
+ m_points.reset();
+ m_tags.reset();
+ m_contours.reset();
+ m_outline.flags = fillRule == Qt::WindingFill
+ ? QT_FT_OUTLINE_NONE
+ : QT_FT_OUTLINE_EVEN_ODD_FILL;
+ m_subpath_start = 0;
+ }
+
+ void endOutline();
+
+ void clipElements(const QPointF *points, const QPainterPath::ElementType *types, int count);
+
+ void convertElements(const QPointF *points, const QPainterPath::ElementType *types, int count);
+
+ inline void moveTo(const QPointF &pt) {
+#ifdef QT_DEBUG_CONVERT
+ printf("QOutlineMapper::moveTo() (%f, %f)\n", pt.x(), pt.y());
+#endif
+ closeSubpath();
+ m_subpath_start = m_elements.size();
+ m_elements << pt;
+ m_element_types << QPainterPath::MoveToElement;
+ }
+
+ inline void lineTo(const QPointF &pt) {
+#ifdef QT_DEBUG_CONVERT
+ printf("QOutlineMapper::lineTo() (%f, %f)\n", pt.x(), pt.y());
+#endif
+ m_elements.add(pt);
+ m_element_types << QPainterPath::LineToElement;
+ }
+
+ inline void curveTo(const QPointF &cp1, const QPointF &cp2, const QPointF &ep) {
+#ifdef QT_DEBUG_CONVERT
+ printf("QOutlineMapper::curveTo() (%f, %f)\n", ep.x(), ep.y());
+#endif
+ m_elements << cp1 << cp2 << ep;
+ m_element_types << QPainterPath::CurveToElement
+ << QPainterPath::CurveToDataElement
+ << QPainterPath::CurveToDataElement;
+ }
+
+ inline void closeSubpath() {
+ int element_count = m_elements.size();
+ if (element_count > 0) {
+ if (m_elements.at(element_count-1) != m_elements.at(m_subpath_start)) {
+#ifdef QT_DEBUG_CONVERT
+ printf(" - implicitly closing\n");
+#endif
+ // Put the object on the stack to avoid the odd case where
+ // lineTo reallocs the databuffer and the QPointF & will
+ // be invalidated.
+ QPointF pt = m_elements.at(m_subpath_start);
+
+ // only do lineTo if we have element_type array...
+ if (m_element_types.size())
+ lineTo(pt);
+ else
+ m_elements << pt;
+
+ }
+ }
+ }
+
+ QT_FT_Outline *outline() {
+ if (m_valid)
+ return &m_outline;
+ return 0;
+ }
+
+ QT_FT_Outline *convertPath(const QPainterPath &path);
+ QT_FT_Outline *convertPath(const QVectorPath &path);
+
+ void setCoordinateRounding(bool coordinateRounding) { m_round_coords = coordinateRounding; }
+
+ inline QPainterPath::ElementType *elementTypes() const { return m_element_types.size() == 0 ? 0 : m_element_types.data(); }
+
+public:
+ QDataBuffer<QPainterPath::ElementType> m_element_types;
+ QDataBuffer<QPointF> m_elements;
+ QDataBuffer<QPointF> m_elements_dev;
+ QDataBuffer<QT_FT_Vector> m_points;
+ QDataBuffer<char> m_tags;
+ QDataBuffer<int> m_contours;
+
+ QRect m_clip_rect;
+ QDataBuffer<QPointF> m_polygon_dev;
+
+ QRectF controlPointRect; // only valid after endOutline()
+
+ QT_FT_Outline m_outline;
+ uint m_txop;
+
+ int m_subpath_start;
+
+ // Matrix
+ qreal m_m11;
+ qreal m_m12;
+ qreal m_m13;
+ qreal m_m21;
+ qreal m_m22;
+ qreal m_m23;
+ qreal m_m33;
+ qreal m_dx;
+ qreal m_dy;
+
+ bool m_valid;
+
+private:
+ bool m_round_coords;
+};
+
+QT_END_NAMESPACE
+
+#endif // QOUTLINEMAPPER_P_H
diff --git a/src/gui/painting/qpaintdevice.h b/src/gui/painting/qpaintdevice.h
new file mode 100644
index 0000000000..e9d6d3f8a7
--- /dev/null
+++ b/src/gui/painting/qpaintdevice.h
@@ -0,0 +1,173 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QPAINTDEVICE_H
+#define QPAINTDEVICE_H
+
+#include <QtGui/qwindowdefs.h>
+#include <QtCore/qrect.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+#if defined(Q_WS_QWS)
+class QWSDisplay;
+#endif
+
+class QPaintEngine;
+
+class Q_GUI_EXPORT QPaintDevice // device for QPainter
+{
+public:
+ enum PaintDeviceMetric {
+ PdmWidth = 1,
+ PdmHeight,
+ PdmWidthMM,
+ PdmHeightMM,
+ PdmNumColors,
+ PdmDepth,
+ PdmDpiX,
+ PdmDpiY,
+ PdmPhysicalDpiX,
+ PdmPhysicalDpiY
+ };
+
+ virtual ~QPaintDevice();
+
+ virtual int devType() const;
+ bool paintingActive() const;
+ virtual QPaintEngine *paintEngine() const = 0;
+
+#if defined(Q_WS_QWS)
+ static QWSDisplay *qwsDisplay();
+#endif
+
+#ifdef Q_WS_WIN
+ virtual HDC getDC() const;
+ virtual void releaseDC(HDC hdc) const;
+#endif
+
+ int width() const { return metric(PdmWidth); }
+ int height() const { return metric(PdmHeight); }
+ int widthMM() const { return metric(PdmWidthMM); }
+ int heightMM() const { return metric(PdmHeightMM); }
+ int logicalDpiX() const { return metric(PdmDpiX); }
+ int logicalDpiY() const { return metric(PdmDpiY); }
+ int physicalDpiX() const { return metric(PdmPhysicalDpiX); }
+ int physicalDpiY() const { return metric(PdmPhysicalDpiY); }
+ int numColors() const { return metric(PdmNumColors); }
+ int depth() const { return metric(PdmDepth); }
+
+protected:
+ QPaintDevice();
+ virtual int metric(PaintDeviceMetric metric) const;
+
+ ushort painters; // refcount
+
+private:
+ Q_DISABLE_COPY(QPaintDevice)
+
+#if defined(Q_WS_X11) && defined(QT3_SUPPORT)
+public:
+ QT3_SUPPORT Display *x11Display() const;
+ QT3_SUPPORT int x11Screen() const;
+ QT3_SUPPORT int x11Depth() const;
+ QT3_SUPPORT int x11Cells() const;
+ QT3_SUPPORT Qt::HANDLE x11Colormap() const;
+ QT3_SUPPORT bool x11DefaultColormap() const;
+ QT3_SUPPORT void *x11Visual() const;
+ QT3_SUPPORT bool x11DefaultVisual() const;
+
+ static QT3_SUPPORT Display *x11AppDisplay();
+ static QT3_SUPPORT int x11AppScreen();
+ static QT3_SUPPORT int x11AppDepth(int screen = -1);
+ static QT3_SUPPORT int x11AppCells(int screen = -1);
+ static QT3_SUPPORT Qt::HANDLE x11AppRootWindow(int screen = -1);
+ static QT3_SUPPORT Qt::HANDLE x11AppColormap(int screen = -1);
+ static QT3_SUPPORT void *x11AppVisual(int screen = -1);
+ static QT3_SUPPORT bool x11AppDefaultColormap(int screen =-1);
+ static QT3_SUPPORT bool x11AppDefaultVisual(int screen =-1);
+ static QT3_SUPPORT int x11AppDpiX(int screen = -1);
+ static QT3_SUPPORT int x11AppDpiY(int screen = -1);
+ static QT3_SUPPORT void x11SetAppDpiX(int, int);
+ static QT3_SUPPORT void x11SetAppDpiY(int, int);
+#endif
+
+ friend class QPainter;
+ friend class QFontEngineMac;
+ friend class QX11PaintEngine;
+};
+
+#ifdef QT3_SUPPORT
+QT3_SUPPORT Q_GUI_EXPORT
+void bitBlt(QPaintDevice *dst, int dx, int dy,
+ const QPaintDevice *src, int sx=0, int sy=0, int sw=-1, int sh=-1,
+ bool ignoreMask=false);
+
+QT3_SUPPORT Q_GUI_EXPORT
+void bitBlt(QPaintDevice *dst, int dx, int dy,
+ const QImage *src, int sx=0, int sy=0, int sw=-1, int sh=-1,
+ int conversion_flags=0);
+
+QT3_SUPPORT Q_GUI_EXPORT
+void bitBlt(QPaintDevice *dst, const QPoint &dp,
+ const QPaintDevice *src, const QRect &sr=QRect(0,0,-1,-1),
+ bool ignoreMask=false);
+#endif
+
+/*****************************************************************************
+ Inline functions
+ *****************************************************************************/
+
+inline int QPaintDevice::devType() const
+{ return QInternal::UnknownDevice; }
+
+inline bool QPaintDevice::paintingActive() const
+{ return painters != 0; }
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QPAINTDEVICE_H
diff --git a/src/gui/painting/qpaintdevice_mac.cpp b/src/gui/painting/qpaintdevice_mac.cpp
new file mode 100644
index 0000000000..f0c5f583c8
--- /dev/null
+++ b/src/gui/painting/qpaintdevice_mac.cpp
@@ -0,0 +1,185 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qpaintdevice.h"
+#include "qpainter.h"
+#include "qwidget.h"
+#include "qbitmap.h"
+#include "qapplication.h"
+#include "qprinter.h"
+#include <qdebug.h>
+#include <private/qt_mac_p.h>
+#include <private/qprintengine_mac_p.h>
+#include <private/qpixmap_mac_p.h>
+#include <private/qpixmap_raster_p.h>
+
+QT_BEGIN_NAMESPACE
+
+/*****************************************************************************
+ Internal variables and functions
+ *****************************************************************************/
+
+
+/*****************************************************************************
+ External functions
+ *****************************************************************************/
+
+extern void qt_painter_removePaintDevice(QPaintDevice *); //qpainter.cpp
+
+/*****************************************************************************
+ QPaintDevice member functions
+ *****************************************************************************/
+QPaintDevice::QPaintDevice()
+{
+ painters = 0;
+}
+
+QPaintDevice::~QPaintDevice()
+{
+ if(paintingActive())
+ qWarning("QPaintDevice: Cannot destroy paint device that is being "
+ "painted, be sure to QPainter::end() painters");
+ qt_painter_removePaintDevice(this);
+}
+
+int QPaintDevice::metric(PaintDeviceMetric) const
+{
+ return 0;
+}
+
+/*! \internal */
+float qt_mac_defaultDpi_x()
+{
+ // Mac OS X currently assumes things to be 72 dpi.
+ // (see http://developer.apple.com/releasenotes/GraphicsImaging/RN-ResolutionIndependentUI/)
+ // This may need to be re-worked as we go further in the resolution-independence stuff.
+ return 72;
+}
+
+/*! \internal */
+float qt_mac_defaultDpi_y()
+{
+ // Mac OS X currently assumes things to be 72 dpi.
+ // (see http://developer.apple.com/releasenotes/GraphicsImaging/RN-ResolutionIndependentUI/)
+ // This may need to be re-worked as we go further in the resolution-independence stuff.
+ return 72;
+}
+
+
+/*! \internal
+
+ Returns the QuickDraw CGrafPtr of the paint device. 0 is returned
+ if it can't be obtained. Do not hold the pointer around for long
+ as it can be relocated.
+
+ \warning This function is only available on Mac OS X.
+*/
+
+Q_GUI_EXPORT GrafPtr qt_mac_qd_context(const QPaintDevice *device)
+{
+ if (device->devType() == QInternal::Pixmap) {
+ return static_cast<GrafPtr>(static_cast<const QPixmap *>(device)->macQDHandle());
+ } else if(device->devType() == QInternal::Widget) {
+ return static_cast<GrafPtr>(static_cast<const QWidget *>(device)->macQDHandle());
+ } else if(device->devType() == QInternal::Printer) {
+ QPaintEngine *engine = static_cast<const QPrinter *>(device)->paintEngine();
+ return static_cast<GrafPtr>(static_cast<const QMacPrintEngine *>(engine)->handle());
+ }
+ return 0;
+}
+
+extern CGColorSpaceRef qt_mac_colorSpaceForDeviceType(const QPaintDevice *pdev);
+
+/*! \internal
+
+ Returns the CoreGraphics CGContextRef of the paint device. 0 is
+ returned if it can't be obtained. It is the caller's responsiblity to
+ CGContextRelease the context when finished using it.
+
+ \warning This function is only available on Mac OS X.
+*/
+
+Q_GUI_EXPORT CGContextRef qt_mac_cg_context(const QPaintDevice *pdev)
+{
+ if (pdev->devType() == QInternal::Pixmap) {
+ const QPixmap *pm = static_cast<const QPixmap*>(pdev);
+ CGColorSpaceRef colorspace = qt_mac_colorSpaceForDeviceType(pdev);
+#if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4)
+ uint flags = kCGImageAlphaPremultipliedFirst;
+#ifdef kCGBitmapByteOrder32Host //only needed because CGImage.h added symbols in the minor version
+ if(QSysInfo::MacintoshVersion >= QSysInfo::MV_10_4)
+ flags |= kCGBitmapByteOrder32Host;
+#endif
+#else
+ CGImageAlphaInfo flags = kCGImageAlphaPremultipliedFirst;
+#endif
+ CGContextRef ret = 0;
+
+ // It would make sense to put this into a mac #ifdef'ed
+ // virtual function in the QPixmapData at some point
+ if (pm->data->classId() == QPixmapData::MacClass) {
+ const QMacPixmapData *pmData = static_cast<const QMacPixmapData*>(pm->data);
+ ret = CGBitmapContextCreate(pmData->pixels, pmData->w, pmData->h,
+ 8, pmData->bytesPerRow, colorspace,
+ flags);
+ if(!ret)
+ qWarning("QPaintDevice: Unable to create context for pixmap (%d/%d/%d)",
+ pmData->w, pmData->h, (pmData->bytesPerRow * pmData->h));
+ } else if (pm->data->classId() == QPixmapData::RasterClass) {
+ QImage *image = pm->data->buffer();
+ ret = CGBitmapContextCreate(image->bits(), image->width(), image->height(),
+ 8, image->bytesPerLine(), colorspace, flags);
+ }
+
+ CGContextTranslateCTM(ret, 0, pm->height());
+ CGContextScaleCTM(ret, 1, -1);
+ return ret;
+ } else if (pdev->devType() == QInternal::Widget) {
+ CGContextRef ret = static_cast<CGContextRef>(static_cast<const QWidget *>(pdev)->macCGHandle());
+ CGContextRetain(ret);
+ return ret;
+ } else if (pdev->devType() == QInternal::MacQuartz) {
+ return static_cast<const QMacQuartzPaintDevice *>(pdev)->cgContext();
+ }
+ return 0;
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/painting/qpaintdevice_qws.cpp b/src/gui/painting/qpaintdevice_qws.cpp
new file mode 100644
index 0000000000..6a68d28da0
--- /dev/null
+++ b/src/gui/painting/qpaintdevice_qws.cpp
@@ -0,0 +1,92 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qpaintdevice.h"
+#include "qpainter.h"
+#include "qwidget.h"
+#include "qbitmap.h"
+#include "qapplication.h"
+#include "qwsdisplay_qws.h"
+
+QT_BEGIN_NAMESPACE
+
+QPaintDevice::QPaintDevice()
+{
+ painters = 0;
+}
+
+extern void qt_painter_removePaintDevice(QPaintDevice *); //qpainter.cpp
+
+
+QPaintDevice::~QPaintDevice()
+{
+ if (paintingActive())
+ qWarning("QPaintDevice: Cannot destroy paint device that is being "
+ "painted");
+ qt_painter_removePaintDevice(this);
+}
+
+
+int QPaintDevice::metric(PaintDeviceMetric m) const
+{
+ qWarning("QPaintDevice::metrics: Device has no metric information");
+ if (m == PdmDpiX) {
+ return 72;
+ } else if (m == PdmDpiY) {
+ return 72;
+ } else if (m == PdmNumColors) {
+ // FIXME: does this need to be a real value?
+ return 256;
+ } else {
+ qDebug("Unrecognised metric %d!",m);
+ return 0;
+ }
+}
+
+/*!
+ \internal
+*/
+QWSDisplay *QPaintDevice::qwsDisplay()
+{
+ return qt_fbdpy;
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/painting/qpaintdevice_win.cpp b/src/gui/painting/qpaintdevice_win.cpp
new file mode 100644
index 0000000000..6cae744329
--- /dev/null
+++ b/src/gui/painting/qpaintdevice_win.cpp
@@ -0,0 +1,88 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qpaintdevice.h"
+#include "qpainter.h"
+#include "qwidget.h"
+#include "qbitmap.h"
+#include "qapplication.h"
+#include <private/qapplication_p.h>
+#include "qt_windows.h"
+#include "qprinter.h"
+
+QT_BEGIN_NAMESPACE
+
+QPaintDevice::QPaintDevice()
+{
+ painters = 0;
+}
+
+extern void qt_painter_removePaintDevice(QPaintDevice *); //qpainter.cpp
+
+QPaintDevice::~QPaintDevice()
+{
+ if (paintingActive())
+ qWarning("QPaintDevice: Cannot destroy paint device that is being "
+ "painted. Be sure to QPainter::end() painters!");
+ qt_painter_removePaintDevice(this);
+}
+
+int QPaintDevice::metric(PaintDeviceMetric) const
+{
+ qWarning("QPaintDevice::metrics: Device has no metric information");
+ return 0;
+}
+
+
+/*! \internal
+*/
+HDC QPaintDevice::getDC() const
+{
+ return 0;
+}
+
+/*! \internal
+*/
+void QPaintDevice::releaseDC(HDC) const
+{
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/painting/qpaintdevice_x11.cpp b/src/gui/painting/qpaintdevice_x11.cpp
new file mode 100644
index 0000000000..4ea9f579e1
--- /dev/null
+++ b/src/gui/painting/qpaintdevice_x11.cpp
@@ -0,0 +1,435 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qpaintdevice.h"
+#include "qpainter.h"
+#include "qwidget.h"
+#include "qbitmap.h"
+#include "qapplication.h"
+#include <private/qt_x11_p.h>
+#include "qx11info_x11.h"
+
+QT_BEGIN_NAMESPACE
+
+QPaintDevice::QPaintDevice()
+{
+ painters = 0;
+}
+
+extern void qt_painter_removePaintDevice(QPaintDevice *); //qpainter.cpp
+
+QPaintDevice::~QPaintDevice()
+{
+ if (paintingActive())
+ qWarning("QPaintDevice: Cannot destroy paint device that is being "
+ "painted");
+ qt_painter_removePaintDevice(this);
+}
+
+/*! \internal
+
+ Returns the X11 Drawable of the paint device. 0 is returned if it
+ can't be obtained.
+*/
+
+Drawable Q_GUI_EXPORT qt_x11Handle(const QPaintDevice *pd)
+{
+ if (!pd) return 0;
+ if (pd->devType() == QInternal::Widget)
+ return static_cast<const QWidget *>(pd)->handle();
+ else if (pd->devType() == QInternal::Pixmap)
+ return static_cast<const QPixmap *>(pd)->handle();
+ return 0;
+}
+
+/*!
+ \relates QPaintDevice
+
+ Returns the QX11Info structure for the \a pd paint device. 0 is
+ returned if it can't be obtained.
+*/
+const Q_GUI_EXPORT QX11Info *qt_x11Info(const QPaintDevice *pd)
+{
+ if (!pd) return 0;
+ if (pd->devType() == QInternal::Widget)
+ return &static_cast<const QWidget *>(pd)->x11Info();
+ else if (pd->devType() == QInternal::Pixmap)
+ return &static_cast<const QPixmap *>(pd)->x11Info();
+ return 0;
+}
+
+int QPaintDevice::metric(PaintDeviceMetric) const
+{
+ qWarning("QPaintDevice::metrics: Device has no metric information");
+ return 0;
+}
+
+
+
+#ifdef QT3_SUPPORT
+
+/*!
+ Use QX11Info::display() instead.
+
+ \oldcode
+ Display *display = widget->x11Display();
+ \newcode
+ Display *display = QX11Info::display();
+ \endcode
+
+ \sa QWidget::x11Info(), QX11Info::display()
+*/
+Display *QPaintDevice::x11Display() const
+{
+ return X11->display;
+}
+
+/*!
+ Use QX11Info::screen() instead.
+
+ \oldcode
+ int screen = widget->x11Screen();
+ \newcode
+ int screen = widget->x11Info().screen();
+ \endcode
+
+ \sa QWidget::x11Info(), QPixmap::x11Info()
+*/
+int QPaintDevice::x11Screen() const
+{
+ const QX11Info *info = qt_x11Info(this);
+ if (info)
+ return info->screen();
+ return QX11Info::appScreen();
+}
+
+/*!
+ Use QX11Info::visual() instead.
+
+ \oldcode
+ void *visual = widget->x11Visual();
+ \newcode
+ void *visual = widget->x11Info().visual();
+ \endcode
+
+ \sa QWidget::x11Info(), QPixmap::x11Info()
+*/
+void *QPaintDevice::x11Visual() const
+{
+ const QX11Info *info = qt_x11Info(this);
+ if (info)
+ return info->visual();
+ return QX11Info::appVisual();
+}
+
+/*!
+ Use QX11Info::depth() instead.
+
+ \oldcode
+ int depth = widget->x11Depth();
+ \newcode
+ int depth = widget->x11Info().depth();
+ \endcode
+
+ \sa QWidget::x11Info(), QPixmap::x11Info()
+*/
+int QPaintDevice::x11Depth() const
+{
+ const QX11Info *info = qt_x11Info(this);
+ if (info)
+ return info->depth();
+ return QX11Info::appDepth();
+}
+
+/*!
+ Use QX11Info::cells() instead.
+
+ \oldcode
+ int cells = widget->x11Cells();
+ \newcode
+ int cells = widget->x11Info().cells();
+ \endcode
+
+ \sa QWidget::x11Info(), QPixmap::x11Info()
+*/
+int QPaintDevice::x11Cells() const
+{
+ const QX11Info *info = qt_x11Info(this);
+ if (info)
+ return info->cells();
+ return QX11Info::appCells();
+}
+
+/*!
+ Use QX11Info::colormap() instead.
+
+ \oldcode
+ unsigned long screen = widget->x11Colormap();
+ \newcode
+ unsigned long screen = widget->x11Info().colormap();
+ \endcode
+
+ \sa QWidget::x11Info(), QPixmap::x11Info()
+*/
+Qt::HANDLE QPaintDevice::x11Colormap() const
+{
+ const QX11Info *info = qt_x11Info(this);
+ if (info)
+ return info->colormap();
+ return QX11Info::appColormap();
+}
+
+/*!
+ Use QX11Info::defaultColormap() instead.
+
+ \oldcode
+ bool isDefault = widget->x11DefaultColormap();
+ \newcode
+ bool isDefault = widget->x11Info().defaultColormap();
+ \endcode
+
+ \sa QWidget::x11Info(), QPixmap::x11Info()
+*/
+bool QPaintDevice::x11DefaultColormap() const
+{
+ const QX11Info *info = qt_x11Info(this);
+ if (info)
+ return info->defaultColormap();
+ return QX11Info::appDefaultColormap();
+}
+
+/*!
+ Use QX11Info::defaultVisual() instead.
+
+ \oldcode
+ bool isDefault = widget->x11DefaultVisual();
+ \newcode
+ bool isDefault = widget->x11Info().defaultVisual();
+ \endcode
+
+ \sa QWidget::x11Info(), QPixmap::x11Info()
+*/
+bool QPaintDevice::x11DefaultVisual() const
+{
+ const QX11Info *info = qt_x11Info(this);
+ if (info)
+ return info->defaultVisual();
+ return QX11Info::appDefaultVisual();
+}
+
+/*!
+ Use QX11Info::visual() instead.
+
+ \oldcode
+ void *visual = QPaintDevice::x11AppVisual(screen);
+ \newcode
+ void *visual = qApp->x11Info(screen).visual();
+ \endcode
+
+ \sa QWidget::x11Info(), QPixmap::x11Info()
+*/
+void *QPaintDevice::x11AppVisual(int screen)
+{ return QX11Info::appVisual(screen); }
+
+/*!
+ Use QX11Info::colormap() instead.
+
+ \oldcode
+ unsigned long colormap = QPaintDevice::x11AppColormap(screen);
+ \newcode
+ unsigned long colormap = qApp->x11Info(screen).colormap();
+ \endcode
+
+ \sa QWidget::x11Info(), QPixmap::x11Info()
+*/
+Qt::HANDLE QPaintDevice::x11AppColormap(int screen)
+{ return QX11Info::appColormap(screen); }
+
+/*!
+ Use QX11Info::display() instead.
+
+ \oldcode
+ Display *display = QPaintDevice::x11AppDisplay();
+ \newcode
+ Display *display = qApp->x11Info().display();
+ \endcode
+
+ \sa QWidget::x11Info(), QPixmap::x11Info()
+*/
+Display *QPaintDevice::x11AppDisplay()
+{ return QX11Info::display(); }
+
+/*!
+ Use QX11Info::screen() instead.
+
+ \oldcode
+ int screen = QPaintDevice::x11AppScreen();
+ \newcode
+ int screen = qApp->x11Info().screen();
+ \endcode
+
+ \sa QWidget::x11Info(), QPixmap::x11Info()
+*/
+int QPaintDevice::x11AppScreen()
+{ return QX11Info::appScreen(); }
+
+/*!
+ Use QX11Info::depth() instead.
+
+ \oldcode
+ int depth = QPaintDevice::x11AppDepth(screen);
+ \newcode
+ int depth = qApp->x11Info(screen).depth();
+ \endcode
+
+ \sa QWidget::x11Info(), QPixmap::x11Info()
+*/
+int QPaintDevice::x11AppDepth(int screen)
+{ return QX11Info::appDepth(screen); }
+
+/*!
+ Use QX11Info::cells() instead.
+
+ \oldcode
+ int cells = QPaintDevice::x11AppCells(screen);
+ \newcode
+ int cells = qApp->x11Info(screen).cells();
+ \endcode
+
+ \sa QWidget::x11Info(), QPixmap::x11Info()
+*/
+int QPaintDevice::x11AppCells(int screen)
+{ return QX11Info::appCells(screen); }
+
+/*!
+ Use QX11Info::appRootWindow() instead.
+
+ \oldcode
+ unsigned long window = QPaintDevice::x11AppRootWindow(screen);
+ \newcode
+ unsigned long window = qApp->x11Info(screen).appRootWindow();
+ \endcode
+
+ \sa QWidget::x11Info(), QPixmap::x11Info()
+*/
+Qt::HANDLE QPaintDevice::x11AppRootWindow(int screen)
+{ return QX11Info::appRootWindow(screen); }
+
+/*!
+ Use QX11Info::defaultColormap() instead.
+
+ \oldcode
+ bool isDefault = QPaintDevice::x11AppDefaultColormap(screen);
+ \newcode
+ bool isDefault = qApp->x11Info(screen).defaultColormap();
+ \endcode
+
+ \sa QWidget::x11Info(), QPixmap::x11Info()
+*/
+bool QPaintDevice::x11AppDefaultColormap(int screen)
+{ return QX11Info::appDefaultColormap(screen); }
+
+/*!
+ Use QX11Info::defaultVisual() instead.
+
+ \oldcode
+ bool isDefault = QPaintDevice::x11AppDefaultVisual(screen);
+ \newcode
+ bool isDefault = qApp->x11Info(screen).defaultVisual();
+ \endcode
+
+ \sa QWidget::x11Info(), QPixmap::x11Info()
+*/
+bool QPaintDevice::x11AppDefaultVisual(int screen)
+{ return QX11Info::appDefaultVisual(screen); }
+
+/*!
+ Use QX11Info::setAppDpiX() instead.
+*/
+void QPaintDevice::x11SetAppDpiX(int dpi, int screen)
+{
+ QX11Info::setAppDpiX(dpi, screen);
+}
+
+/*!
+ Use QX11Info::setAppDpiY() instead.
+*/
+void QPaintDevice::x11SetAppDpiY(int dpi, int screen)
+{
+ QX11Info::setAppDpiY(dpi, screen);
+}
+
+
+/*!
+ Use QX11Info::appDpiX() instead.
+
+ \oldcode
+ bool isDefault = QPaintDevice::x11AppDpiX(screen);
+ \newcode
+ bool isDefault = qApp->x11Info(screen).appDpiX();
+ \endcode
+
+ \sa QWidget::x11Info(), QPixmap::x11Info()
+*/
+int QPaintDevice::x11AppDpiX(int screen)
+{
+ return QX11Info::appDpiX(screen);
+}
+
+/*!
+ Use QX11Info::appDpiY() instead.
+
+ \oldcode
+ bool isDefault = QPaintDevice::x11AppDpiY(screen);
+ \newcode
+ bool isDefault = qApp->x11Info(screen).appDpiY();
+ \endcode
+
+ \sa QWidget::x11Info(), QPixmap::x11Info()
+*/
+int QPaintDevice::x11AppDpiY(int screen)
+{
+ return QX11Info::appDpiY(screen);
+}
+#endif
+
+
+QT_END_NAMESPACE
diff --git a/src/gui/painting/qpaintengine.cpp b/src/gui/painting/qpaintengine.cpp
new file mode 100644
index 0000000000..ad09060614
--- /dev/null
+++ b/src/gui/painting/qpaintengine.cpp
@@ -0,0 +1,1026 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#include "qpaintengine.h"
+#include "qpaintengine_p.h"
+#include "qpainter_p.h"
+#include "qpolygon.h"
+#include "qbitmap.h"
+#include "qapplication.h"
+#include <qdebug.h>
+#include <qmath.h>
+#include <private/qtextengine_p.h>
+#include <qvarlengtharray.h>
+#include <private/qfontengine_p.h>
+
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QTextItem
+
+ \brief The QTextItem class provides all the information required to draw
+ text in a custom paint engine.
+
+ When you reimplement your own paint engine, you must reimplement
+ QPaintEngine::drawTextItem(), a function that takes a QTextItem as
+ one of its arguments.
+*/
+
+/*!
+ \enum QTextItem::RenderFlag
+
+ \value RightToLeft Render the text from right to left.
+ \value Overline Paint a line above the text.
+ \value Underline Paint a line under the text.
+ \value StrikeOut Paint a line through the text.
+ \omitvalue Dummy
+*/
+
+
+/*!
+ \fn qreal QTextItem::descent() const
+
+ Corresponds to the \l{QFontMetrics::descent()}{descent} of the piece of text that is drawn.
+*/
+qreal QTextItem::descent() const
+{
+ const QTextItemInt *ti = static_cast<const QTextItemInt *>(this);
+ return ti->descent.toReal();
+}
+
+/*!
+ \fn qreal QTextItem::ascent() const
+
+ Corresponds to the \l{QFontMetrics::ascent()}{ascent} of the piece of text that is drawn.
+*/
+qreal QTextItem::ascent() const
+{
+ const QTextItemInt *ti = static_cast<const QTextItemInt *>(this);
+ return ti->ascent.toReal();
+}
+
+/*!
+ \fn qreal QTextItem::width() const
+
+ Specifies the total width of the text to be drawn.
+*/
+qreal QTextItem::width() const
+{
+ const QTextItemInt *ti = static_cast<const QTextItemInt *>(this);
+ return ti->width.toReal();
+}
+
+/*!
+ \fn QTextItem::RenderFlags QTextItem::renderFlags() const
+
+ Returns the render flags used.
+*/
+QTextItem::RenderFlags QTextItem::renderFlags() const
+{
+ const QTextItemInt *ti = static_cast<const QTextItemInt *>(this);
+ return ti->flags;
+}
+
+/*!
+ \fn QString QTextItem::text() const
+
+ Returns the text that should be drawn.
+*/
+QString QTextItem::text() const
+{
+ const QTextItemInt *ti = static_cast<const QTextItemInt *>(this);
+ return QString(ti->chars, ti->num_chars);
+}
+
+/*!
+ \fn QFont QTextItem::font() const
+
+ Returns the font that should be used to draw the text.
+*/
+QFont QTextItem::font() const
+{
+ const QTextItemInt *ti = static_cast<const QTextItemInt *>(this);
+ return ti->f ? *ti->f : QApplication::font();
+}
+
+
+/*!
+ \class QPaintEngine
+ \ingroup multimedia
+
+ \brief The QPaintEngine class provides an abstract definition of how
+ QPainter draws to a given device on a given platform.
+
+ Qt 4.0 provides several premade implementations of QPaintEngine for the
+ different painter backends we support. We provide one paint engine for each
+ window system and painting framework we support. This includes X11 on
+ Unix/Linux and CoreGraphics on Mac OS X. In addition we provide QPaintEngine
+ implementations for OpenGL (accessible through QGLWidget) and PostScript
+ (accessible through QPSPrinter on X11). Additionally there is a raster-based
+ paint engine that is a fallback for when an engine does not support a certain
+ capability.
+
+ If one wants to use QPainter to draw to a different backend,
+ one must subclass QPaintEngine and reimplement all its virtual
+ functions. The QPaintEngine implementation is then made available by
+ subclassing QPaintDevice and reimplementing the virtual function
+ QPaintDevice::paintEngine().
+
+ QPaintEngine is created and owned by the QPaintDevice that created it.
+
+ The big advantage of the QPaintEngine approach opposed to
+ Qt 3's QPainter/QPaintDevice::cmd() approach is that it is now
+ possible to adapt to multiple technologies on each platform and take
+ advantage of each to the fullest.
+
+ \sa QPainter, QPaintDevice::paintEngine(), {The Paint System}
+*/
+
+/*!
+ \enum QPaintEngine::PaintEngineFeature
+
+ This enum is used to describe the features or capabilities that the
+ paint engine has. If a feature is not supported by the engine,
+ QPainter will do a best effort to emulate that feature through other
+ means and pass on an alpha blended QImage to the engine with the
+ emulated results. Some features cannot be emulated: AlphaBlend and PorterDuff.
+
+ \value AlphaBlend The engine can alpha blend primitives.
+ \value Antialiasing The engine can use antialising to improve the appearance
+ of rendered primitives.
+ \value BlendModes The engine supports blending modes.
+ \value BrushStroke The engine supports drawing strokes that
+ contain brushes as fills, not just solid
+ colors (e.g. a dashed gradient line of
+ width 2).
+ \value ConicalGradientFill The engine supports conical gradient fills.
+ \value ConstantOpacity The engine supports the feature provided by
+ QPainter::setOpacity().
+ \value LinearGradientFill The engine supports linear gradient fills.
+ \value MaskedBrush The engine is capable of rendering brushes that has a
+ texture with an alpha channel or a mask.
+ \value ObjectBoundingModeGradients The engine has native support for gradients
+ with coordinate mode QGradient::ObjectBoundingMode.
+ Otherwise, if QPaintEngine::PatternTransform is
+ supported, object bounding mode gradients are
+ converted to gradients with coordinate mode
+ QGradient::LogicalMode and a brush transform for
+ the coordinate mapping.
+ \value PainterPaths The engine has path support.
+ \value PaintOutsidePaintEvent The engine is capable of painting outside of
+ paint events.
+ \value PatternBrush The engine is capable of rendering brushes with
+ the brush patterns specified in Qt::BrushStyle.
+ \value PatternTransform The engine has support for transforming brush
+ patterns.
+ \value PerspectiveTransform The engine has support for performing perspective
+ transformations on primitives.
+ \value PixmapTransform The engine can transform pixmaps, including
+ rotation and shearing.
+ \value PorterDuff The engine supports Porter-Duff operations
+ \value PrimitiveTransform The engine has support for transforming
+ drawing primitives.
+ \value RadialGradientFill The engine supports radial gradient fills.
+ \value RasterOpModes The engine supports bitwise raster operations.
+ \value AllFeatures All of the above features. This enum value is usually
+ used as a bit mask.
+*/
+
+/*!
+ \enum QPaintEngine::PolygonDrawMode
+
+ \value OddEvenMode The polygon should be drawn using OddEven fill
+ rule.
+
+ \value WindingMode The polygon should be drawn using Winding fill rule.
+
+ \value ConvexMode The polygon is a convex polygon and can be drawn
+ using specialized algorithms where available.
+
+ \value PolylineMode Only the outline of the polygon should be
+ drawn.
+
+*/
+
+/*!
+ \enum QPaintEngine::DirtyFlag
+
+ \value DirtyPen The pen is dirty and needs to be updated.
+
+ \value DirtyBrush The brush is dirty and needs to be updated.
+
+ \value DirtyBrushOrigin The brush origin is dirty and needs to
+ updated.
+
+ \value DirtyFont The font is dirty and needs to be updated.
+
+ \value DirtyBackground The background is dirty and needs to be
+ updated.
+
+ \value DirtyBackgroundMode The background mode is dirty and needs
+ to be updated.
+
+ \value DirtyTransform The transform is dirty and needs to be
+ updated.
+
+ \value DirtyClipRegion The clip region is dirty and needs to be
+ updated.
+
+ \value DirtyClipPath The clip path is dirty and needs to be
+ updated.
+
+ \value DirtyHints The render hints is dirty and needs to be
+ updated.
+
+ \value DirtyCompositionMode The composition mode is dirty and
+ needs to be updated.
+
+ \value DirtyClipEnabled Whether clipping is enabled or not is
+ dirty and needs to be updated.
+
+ \value DirtyOpacity The constant opacity has changed and needs to
+ be updated as part of the state change in
+ QPaintEngine::updateState().
+
+ \value AllDirty Convenience enum used internally.
+
+ These types are used by QPainter to trigger lazy updates of the
+ various states in the QPaintEngine using
+ QPaintEngine::updateState().
+
+ A paint engine must update every dirty state.
+*/
+
+/*!
+ \fn void QPaintEngine::syncState()
+
+ \internal
+
+ Updates all dirty states in this engine. This function should ONLY
+ be used when drawing with native handles directly and immediate sync
+ from QPainters state to the native state is required.
+*/
+void QPaintEngine::syncState()
+{
+ Q_ASSERT(state);
+ updateState(*state);
+}
+
+static QPaintEngine *qt_polygon_recursion = 0;
+struct QT_Point {
+ int x;
+ int y;
+};
+
+/*!
+ \fn void QPaintEngine::drawPolygon(const QPointF *points, int pointCount,
+ PolygonDrawMode mode)
+
+ Reimplement this virtual function to draw the polygon defined
+ by the \a pointCount first points in \a points, using mode \a
+ mode.
+
+ \note At least one of the drawPolygon() functions must be reimplemented.
+*/
+void QPaintEngine::drawPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode)
+{
+ Q_ASSERT_X(qt_polygon_recursion != this, "QPaintEngine::drawPolygon",
+ "At least one drawPolygon function must be implemented");
+ qt_polygon_recursion = this;
+ Q_ASSERT(sizeof(QT_Point) == sizeof(QPoint));
+ QVarLengthArray<QT_Point> p(pointCount);
+ for (int i = 0; i < pointCount; ++i) {
+ p[i].x = qRound(points[i].x());
+ p[i].y = qRound(points[i].y());
+ }
+ drawPolygon((QPoint *)p.data(), pointCount, mode);
+ qt_polygon_recursion = 0;
+}
+
+struct QT_PointF {
+ qreal x;
+ qreal y;
+};
+/*!
+ \overload
+
+ Reimplement this virtual function to draw the polygon defined by the
+ \a pointCount first points in \a points, using mode \a mode.
+
+ \note At least one of the drawPolygon() functions must be reimplemented.
+*/
+void QPaintEngine::drawPolygon(const QPoint *points, int pointCount, PolygonDrawMode mode)
+{
+ Q_ASSERT_X(qt_polygon_recursion != this, "QPaintEngine::drawPolygon",
+ "At least one drawPolygon function must be implemented");
+ qt_polygon_recursion = this;
+ Q_ASSERT(sizeof(QT_PointF) == sizeof(QPointF));
+ QVarLengthArray<QT_PointF> p(pointCount);
+ for (int i=0; i<pointCount; ++i) {
+ p[i].x = points[i].x();
+ p[i].y = points[i].y();
+ }
+ drawPolygon((QPointF *)p.data(), pointCount, mode);
+ qt_polygon_recursion = 0;
+}
+
+/*!
+ \enum QPaintEngine::Type
+
+ \value X11
+ \value Windows
+ \value MacPrinter
+ \value CoreGraphics Mac OS X's Quartz2D (CoreGraphics)
+ \value QuickDraw Mac OS X's QuickDraw
+ \value QWindowSystem Qt for Embedded Linux
+ \value PostScript
+ \value OpenGL
+ \value Picture QPicture format
+ \value SVG Scalable Vector Graphics XML format
+ \value Raster
+ \value Direct3D Windows only, Direct3D based engine
+ \value Pdf Portable Document Format
+ \value OpenVG
+ \value User First user type ID
+ \value MaxUser Last user type ID
+*/
+
+/*!
+ \fn bool QPaintEngine::isActive() const
+
+ Returns true if the paint engine is actively drawing; otherwise
+ returns false.
+
+ \sa setActive()
+*/
+
+/*!
+ \fn void QPaintEngine::setActive(bool state)
+
+ Sets the active state of the paint engine to \a state.
+
+ \sa isActive()
+*/
+
+/*!
+ \fn bool QPaintEngine::begin(QPaintDevice *pdev)
+
+ Reimplement this function to initialise your paint engine when
+ painting is to start on the paint device \a pdev. Return true if
+ the initialization was successful; otherwise return false.
+
+ \sa end() isActive()
+*/
+
+/*!
+ \fn bool QPaintEngine::end()
+
+ Reimplement this function to finish painting on the current paint
+ device. Return true if painting was finished successfully;
+ otherwise return false.
+
+ \sa begin() isActive()
+*/
+
+
+/*!
+ Draws the first \a pointCount points in the buffer \a points
+*/
+void QPaintEngine::drawPoints(const QPointF *points, int pointCount)
+{
+ QPainter *p = painter();
+ if (!p)
+ return;
+
+ qreal penWidth = p->pen().widthF();
+ if (penWidth == 0)
+ penWidth = 1;
+
+ bool ellipses = p->pen().capStyle() == Qt::RoundCap;
+
+ p->save();
+
+ QTransform transform;
+ if (p->pen().isCosmetic()) {
+ transform = p->transform();
+ p->setTransform(QTransform());
+ }
+
+ p->setBrush(p->pen().brush());
+ p->setPen(Qt::NoPen);
+
+ for (int i=0; i<pointCount; ++i) {
+ QPointF pos = transform.map(points[i]);
+ QRectF rect(pos.x() - penWidth / 2, pos.y() - penWidth / 2, penWidth, penWidth);
+
+ if (ellipses)
+ p->drawEllipse(rect);
+ else
+ p->drawRect(rect);
+ }
+
+ p->restore();
+}
+
+
+/*!
+ Draws the first \a pointCount points in the buffer \a points
+
+ The default implementation converts the first \a pointCount QPoints in \a points
+ to QPointFs and calls the floating point version of drawPoints.
+
+*/
+void QPaintEngine::drawPoints(const QPoint *points, int pointCount)
+{
+ Q_ASSERT(sizeof(QT_PointF) == sizeof(QPointF));
+ QT_PointF fp[256];
+ while (pointCount) {
+ int i = 0;
+ while (i < pointCount && i < 256) {
+ fp[i].x = points[i].x();
+ fp[i].y = points[i].y();
+ ++i;
+ }
+ drawPoints((QPointF *)(void *)fp, i);
+ points += i;
+ pointCount -= i;
+ }
+}
+
+/*!
+ \fn void QPaintEngine::drawEllipse(const QRectF &rect)
+
+ Reimplement this function to draw the largest ellipse that can be
+ contained within rectangle \a rect.
+
+ The default implementation calls drawPolygon().
+*/
+void QPaintEngine::drawEllipse(const QRectF &rect)
+{
+ QPainterPath path;
+ path.addEllipse(rect);
+ if (hasFeature(PainterPaths)) {
+ drawPath(path);
+ } else {
+ QPolygonF polygon = path.toFillPolygon();
+ drawPolygon(polygon.data(), polygon.size(), ConvexMode);
+ }
+}
+
+/*!
+ The default implementation of this function calls the floating
+ point version of this function
+*/
+void QPaintEngine::drawEllipse(const QRect &rect)
+{
+ drawEllipse(QRectF(rect));
+}
+
+/*!
+ \fn void QPaintEngine::drawPixmap(const QRectF &r, const QPixmap
+ &pm, const QRectF &sr)
+
+ Reimplement this function to draw the part of the \a pm
+ specified by the \a sr rectangle in the given \a r.
+*/
+
+
+void qt_fill_tile(QPixmap *tile, const QPixmap &pixmap)
+{
+ QPainter p(tile);
+ p.drawPixmap(0, 0, pixmap);
+ int x = pixmap.width();
+ while (x < tile->width()) {
+ p.drawPixmap(x, 0, *tile, 0, 0, x, pixmap.height());
+ x *= 2;
+ }
+ int y = pixmap.height();
+ while (y < tile->height()) {
+ p.drawPixmap(0, y, *tile, 0, 0, tile->width(), y);
+ y *= 2;
+ }
+}
+
+void qt_draw_tile(QPaintEngine *gc, qreal x, qreal y, qreal w, qreal h,
+ const QPixmap &pixmap, qreal xOffset, qreal yOffset)
+{
+ qreal yPos, xPos, drawH, drawW, yOff, xOff;
+ yPos = y;
+ yOff = yOffset;
+ while(yPos < y + h) {
+ drawH = pixmap.height() - yOff; // Cropping first row
+ if (yPos + drawH > y + h) // Cropping last row
+ drawH = y + h - yPos;
+ xPos = x;
+ xOff = xOffset;
+ while(xPos < x + w) {
+ drawW = pixmap.width() - xOff; // Cropping first column
+ if (xPos + drawW > x + w) // Cropping last column
+ drawW = x + w - xPos;
+ if (drawW > 0 && drawH > 0)
+ gc->drawPixmap(QRectF(xPos, yPos, drawW, drawH), pixmap, QRectF(xOff, yOff, drawW, drawH));
+ xPos += drawW;
+ xOff = 0;
+ }
+ yPos += drawH;
+ yOff = 0;
+ }
+}
+
+
+/*!
+ Reimplement this function to draw the \a pixmap in the given \a
+ rect, starting at the given \a p. The pixmap will be
+ drawn repeatedly until the \a rect is filled.
+*/
+void QPaintEngine::drawTiledPixmap(const QRectF &rect, const QPixmap &pixmap, const QPointF &p)
+{
+ int sw = pixmap.width();
+ int sh = pixmap.height();
+
+ if (sw*sh < 8192 && sw*sh < 16*rect.width()*rect.height()) {
+ int tw = sw, th = sh;
+ while (tw*th < 32678 && tw < rect.width()/2)
+ tw *= 2;
+ while (tw*th < 32678 && th < rect.height()/2)
+ th *= 2;
+ QPixmap tile;
+ if (pixmap.depth() == 1) {
+ tile = QBitmap(tw, th);
+ } else {
+ tile = QPixmap(tw, th);
+ if (pixmap.hasAlphaChannel())
+ tile.fill(Qt::transparent);
+ }
+ qt_fill_tile(&tile, pixmap);
+ qt_draw_tile(this, rect.x(), rect.y(), rect.width(), rect.height(), tile, p.x(), p.y());
+ } else {
+ qt_draw_tile(this, rect.x(), rect.y(), rect.width(), rect.height(), pixmap, p.x(), p.y());
+ }
+}
+
+/*!
+ \fn void QPaintEngine::drawImage(const QRectF &rectangle, const QImage
+ &image, const QRectF &sr, Qt::ImageConversionFlags flags)
+
+ Reimplement this function to draw the part of the \a image
+ specified by the \a sr rectangle in the given \a rectangle using
+ the given conversion flags \a flags, to convert it to a pixmap.
+*/
+
+void QPaintEngine::drawImage(const QRectF &r, const QImage &image, const QRectF &sr,
+ Qt::ImageConversionFlags flags)
+{
+ QRectF baseSize(0, 0, image.width(), image.height());
+ QImage im = image;
+ if (baseSize != sr)
+ im = im.copy(qFloor(sr.x()), qFloor(sr.y()),
+ qCeil(sr.width()), qCeil(sr.height()));
+ QPixmap pm = QPixmap::fromImage(im, flags);
+ drawPixmap(r, pm, QRectF(QPointF(0, 0), pm.size()));
+}
+
+/*!
+ \fn Type QPaintEngine::type() const
+
+ Reimplement this function to return the paint engine \l{Type}.
+*/
+
+/*!
+ \fn void QPaintEngine::fix_neg_rect(int *x, int *y, int *w, int *h);
+
+ \internal
+*/
+
+/*!
+ \fn bool QPaintEngine::testDirty(DirtyFlags df)
+
+ \internal
+*/
+
+/*!
+ \fn void QPaintEngine::clearDirty(DirtyFlags df)
+
+ \internal
+*/
+
+/*!
+ \fn void QPaintEngine::setDirty(DirtyFlags df)
+
+ \internal
+*/
+
+/*!
+ \fn bool QPaintEngine::hasFeature(PaintEngineFeatures feature) const
+
+ Returns true if the paint engine supports the specified \a
+ feature; otherwise returns false.
+*/
+
+/*!
+ \fn bool QPaintEngine::isExtended() const
+
+ \internal
+
+ Returns true if the paint engine is a QPaintEngineEx derivative.
+*/
+
+/*!
+ \fn void QPaintEngine::updateState(const QPaintEngineState &state)
+
+ Reimplement this function to update the state of a paint engine.
+
+ When implemented, this function is responsible for checking the
+ paint engine's current \a state and update the properties that are
+ changed. Use the QPaintEngineState::state() function to find out
+ which properties that must be updated, then use the corresponding
+ \l {GetFunction}{get function} to retrieve the current values for
+ the given properties.
+
+ \sa QPaintEngineState
+*/
+
+/*!
+ Creates a paint engine with the featureset specified by \a caps.
+*/
+
+QPaintEngine::QPaintEngine(PaintEngineFeatures caps)
+ : state(0),
+ gccaps(caps),
+ active(0),
+ selfDestruct(false),
+ extended(false),
+ d_ptr(new QPaintEnginePrivate)
+{
+ d_ptr->q_ptr = this;
+}
+
+/*!
+ \internal
+*/
+
+QPaintEngine::QPaintEngine(QPaintEnginePrivate &dptr, PaintEngineFeatures caps)
+ : state(0),
+ gccaps(caps),
+ active(0),
+ selfDestruct(false),
+ extended(false),
+ d_ptr(&dptr)
+{
+ d_ptr->q_ptr = this;
+}
+
+/*!
+ Destroys the paint engine.
+*/
+QPaintEngine::~QPaintEngine()
+{
+ delete d_ptr;
+}
+
+/*!
+ Returns the paint engine's painter.
+*/
+QPainter *QPaintEngine::painter() const
+{
+ return state ? state->painter() : 0;
+}
+
+/*!
+ The default implementation ignores the \a path and does nothing.
+*/
+
+void QPaintEngine::drawPath(const QPainterPath &)
+{
+ if (hasFeature(PainterPaths)) {
+ qWarning("QPaintEngine::drawPath: Must be implemented when feature PainterPaths is set");
+ }
+}
+
+/*!
+ This function draws the text item \a textItem at position \a p. The
+ default implementation of this function converts the text to a
+ QPainterPath and paints the resulting path.
+*/
+
+void QPaintEngine::drawTextItem(const QPointF &p, const QTextItem &textItem)
+{
+ const QTextItemInt &ti = static_cast<const QTextItemInt &>(textItem);
+
+ QPainterPath path;
+#ifndef Q_WS_MAC
+ path.setFillRule(Qt::WindingFill);
+#endif
+ if (ti.glyphs.numGlyphs)
+ ti.fontEngine->addOutlineToPath(p.x(), p.y(), ti.glyphs, &path, ti.flags);
+ if (!path.isEmpty()) {
+ bool oldAA = painter()->renderHints() & QPainter::Antialiasing;
+ painter()->setRenderHint(QPainter::Antialiasing,
+ bool((painter()->renderHints() & QPainter::TextAntialiasing)
+ && !(painter()->font().styleStrategy() & QFont::NoAntialias)));
+ painter()->fillPath(path, state->pen().brush());
+ painter()->setRenderHint(QPainter::Antialiasing, oldAA);
+ }
+}
+
+/*!
+ The default implementation splits the list of lines in \a lines
+ into \a lineCount separate calls to drawPath() or drawPolygon()
+ depending on the feature set of the paint engine.
+*/
+void QPaintEngine::drawLines(const QLineF *lines, int lineCount)
+{
+ for (int i=0; i<lineCount; ++i) {
+ QPointF pts[2] = { lines[i].p1(), lines[i].p2() };
+
+ if (pts[0] == pts[1]) {
+ if (state->pen().capStyle() != Qt::FlatCap)
+ drawPoints(pts, 1);
+ continue;
+ }
+
+ drawPolygon(pts, 2, PolylineMode);
+ }
+}
+
+/*!
+ \overload
+
+ The default implementation converts the first \a lineCount lines
+ in \a lines to a QLineF and calls the floating point version of
+ this function.
+*/
+void QPaintEngine::drawLines(const QLine *lines, int lineCount)
+{
+ struct PointF {
+ qreal x;
+ qreal y;
+ };
+ struct LineF {
+ PointF p1;
+ PointF p2;
+ };
+ Q_ASSERT(sizeof(PointF) == sizeof(QPointF));
+ Q_ASSERT(sizeof(LineF) == sizeof(QLineF));
+ LineF fl[256];
+ while (lineCount) {
+ int i = 0;
+ while (i < lineCount && i < 256) {
+ fl[i].p1.x = lines[i].x1();
+ fl[i].p1.y = lines[i].y1();
+ fl[i].p2.x = lines[i].x2();
+ fl[i].p2.y = lines[i].y2();
+ ++i;
+ }
+ drawLines((QLineF *)(void *)fl, i);
+ lines += i;
+ lineCount -= i;
+ }
+}
+
+
+/*!
+ \overload
+
+ The default implementation converts the first \a rectCount
+ rectangles in the buffer \a rects to a QRectF and calls the
+ floating point version of this function.
+*/
+void QPaintEngine::drawRects(const QRect *rects, int rectCount)
+{
+ struct RectF {
+ qreal x;
+ qreal y;
+ qreal w;
+ qreal h;
+ };
+ Q_ASSERT(sizeof(RectF) == sizeof(QRectF));
+ RectF fr[256];
+ while (rectCount) {
+ int i = 0;
+ while (i < rectCount && i < 256) {
+ fr[i].x = rects[i].x();
+ fr[i].y = rects[i].y();
+ fr[i].w = rects[i].width();
+ fr[i].h = rects[i].height();
+ ++i;
+ }
+ drawRects((QRectF *)(void *)fr, i);
+ rects += i;
+ rectCount -= i;
+ }
+}
+
+/*!
+ Draws the first \a rectCount rectangles in the buffer \a
+ rects. The default implementation of this function calls drawPath()
+ or drawPolygon() depending on the feature set of the paint engine.
+*/
+void QPaintEngine::drawRects(const QRectF *rects, int rectCount)
+{
+ if (hasFeature(PainterPaths) &&
+ !state->penNeedsResolving() &&
+ !state->brushNeedsResolving()) {
+ for (int i=0; i<rectCount; ++i) {
+ QPainterPath path;
+ path.addRect(rects[i]);
+ if (path.isEmpty())
+ continue;
+ drawPath(path);
+ }
+ } else {
+ for (int i=0; i<rectCount; ++i) {
+ QRectF rf = rects[i];
+ QPointF pts[4] = { QPointF(rf.x(), rf.y()),
+ QPointF(rf.x() + rf.width(), rf.y()),
+ QPointF(rf.x() + rf.width(), rf.y() + rf.height()),
+ QPointF(rf.x(), rf.y() + rf.height()) };
+ drawPolygon(pts, 4, ConvexMode);
+ }
+ }
+}
+
+/*!
+ \internal
+ Sets the paintdevice that this engine operates on to \a device
+*/
+void QPaintEngine::setPaintDevice(QPaintDevice *device)
+{
+ d_func()->pdev = device;
+}
+
+/*!
+ Returns the device that this engine is painting on, if painting is
+ active; otherwise returns 0.
+*/
+QPaintDevice *QPaintEngine::paintDevice() const
+{
+ return d_func()->pdev;
+}
+
+#ifdef Q_WS_WIN
+/*!
+ \internal
+
+ Empty default implementation.
+*/
+
+HDC QPaintEngine::getDC() const
+{
+ return 0;
+}
+
+
+/*!
+ \internal
+
+ Empty default implementation.
+*/
+
+void QPaintEngine::releaseDC(HDC) const
+{
+}
+
+#endif
+
+/*!
+ \internal
+
+ Returns the offset from the painters origo to the engines
+ origo. This value is used by QPainter for engines who have
+ internal double buffering.
+
+ This function only makes sense when the engine is active.
+*/
+QPoint QPaintEngine::coordinateOffset() const
+{
+ return QPoint();
+}
+
+/*!
+ \internal
+
+ Sets the system clip for this engine. The system clip defines the
+ basis area that the engine has to draw in. All clips that are
+ set will be be an intersection with the system clip.
+
+ Reset the systemclip to no clip by setting an empty region.
+*/
+void QPaintEngine::setSystemClip(const QRegion &region)
+{
+ Q_D(QPaintEngine);
+ d->systemClip = region;
+ // Be backward compatible and only call d->systemStateChanged()
+ // if we currently have a system transform set.
+ if (d->hasSystemTransform) {
+ d->transformSystemClip();
+ d->systemStateChanged();
+ }
+}
+
+/*!
+ \internal
+
+ Returns the system clip. The system clip is read only while the
+ painter is active. An empty region indicates that system clip
+ is not in use.
+*/
+
+QRegion QPaintEngine::systemClip() const
+{
+ return d_func()->systemClip;
+}
+
+/*!
+ \internal
+
+ Sets the target rect for drawing within the backing store. This
+ function should ONLY be used by the backing store.
+*/
+void QPaintEngine::setSystemRect(const QRect &rect)
+{
+ if (isActive()) {
+ qWarning("QPaintEngine::setSystemRect: Should not be changed while engine is active");
+ return;
+ }
+ d_func()->systemRect = rect;
+}
+
+/*!
+ \internal
+
+ Retreives the rect for drawing within the backing store. This
+ function should ONLY be used by the backing store.
+ */
+QRect QPaintEngine::systemRect() const
+{
+ return d_func()->systemRect;
+}
+
+void QPaintEnginePrivate::drawBoxTextItem(const QPointF &p, const QTextItemInt &ti)
+{
+ if (!ti.glyphs.numGlyphs)
+ return;
+
+ // any fixes here should probably also be done in QFontEngineBox::draw
+ const int size = qRound(ti.fontEngine->ascent());
+ QVarLengthArray<QFixedPoint> positions;
+ QVarLengthArray<glyph_t> glyphs;
+ QTransform matrix;
+ matrix.translate(p.x(), p.y() - size);
+ ti.fontEngine->getGlyphPositions(ti.glyphs, matrix, ti.flags, glyphs, positions);
+ if (glyphs.size() == 0)
+ return;
+
+ QSize s(size - 3, size - 3);
+
+ QPainter *painter = q_func()->state->painter();
+ painter->save();
+ painter->setBrush(Qt::NoBrush);
+ QPen pen = painter->pen();
+ pen.setWidthF(ti.fontEngine->lineThickness().toReal());
+ painter->setPen(pen);
+ for (int k = 0; k < positions.size(); k++)
+ painter->drawRect(QRectF(positions[k].toPointF(), s));
+ painter->restore();
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/painting/qpaintengine.h b/src/gui/painting/qpaintengine.h
new file mode 100644
index 0000000000..520f71f1bc
--- /dev/null
+++ b/src/gui/painting/qpaintengine.h
@@ -0,0 +1,359 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QPAINTENGINE_H
+#define QPAINTENGINE_H
+
+#include <QtCore/qnamespace.h>
+#include <QtCore/qobjectdefs.h>
+#include <QtGui/qpainter.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+class QFontEngine;
+class QLineF;
+class QPaintDevice;
+class QPaintEnginePrivate;
+class QPainterPath;
+class QPointF;
+class QPolygonF;
+class QRectF;
+struct QGlyphLayout;
+class QTextItemInt;
+class QPaintEngineState;
+
+class Q_GUI_EXPORT QTextItem {
+public:
+ enum RenderFlag {
+ RightToLeft = 0x1,
+ Overline = 0x10,
+ Underline = 0x20,
+ StrikeOut = 0x40,
+
+ Dummy = 0xffffffff
+ };
+ Q_DECLARE_FLAGS(RenderFlags, RenderFlag)
+ qreal descent() const;
+ qreal ascent() const;
+ qreal width() const;
+
+ RenderFlags renderFlags() const;
+ QString text() const;
+ QFont font() const;
+};
+Q_DECLARE_TYPEINFO(QTextItem, Q_PRIMITIVE_TYPE);
+
+
+class Q_GUI_EXPORT QPaintEngine
+{
+ Q_DECLARE_PRIVATE(QPaintEngine)
+public:
+ enum PaintEngineFeature {
+ PrimitiveTransform = 0x00000001, // Can transform primitives brushes
+ PatternTransform = 0x00000002, // Can transform pattern brushes
+ PixmapTransform = 0x00000004, // Can transform pixmaps
+ PatternBrush = 0x00000008, // Can fill with pixmaps and standard patterns
+ LinearGradientFill = 0x00000010, // Can fill gradient areas
+ RadialGradientFill = 0x00000020, // Can render radial gradients
+ ConicalGradientFill = 0x00000040, // Can render conical gradients
+ AlphaBlend = 0x00000080, // Can do source over alpha blend
+ PorterDuff = 0x00000100, // Can do general porter duff compositions
+ PainterPaths = 0x00000200, // Can fill, outline and clip paths
+ Antialiasing = 0x00000400, // Can antialias lines
+ BrushStroke = 0x00000800, // Can render brush based pens
+ ConstantOpacity = 0x00001000, // Can render at constant opacity
+ MaskedBrush = 0x00002000, // Can fill with textures that has an alpha channel or mask
+ PerspectiveTransform = 0x00004000, // Can do perspective transformations
+ BlendModes = 0x00008000, // Can do extended Porter&Duff composition
+ ObjectBoundingModeGradients = 0x00010000, // Can do object bounding mode gradients
+ RasterOpModes = 0x00020000, // Can do logical raster operations
+ PaintOutsidePaintEvent = 0x20000000, // Engine is capable of painting outside paint events
+ /* 0x10000000, // Used for emulating
+ QGradient::StretchToDevice,
+ defined in qpainter.cpp
+
+ 0x40000000, // Used internally for emulating opaque backgrounds
+ */
+
+ AllFeatures = 0xffffffff // For convenience
+ };
+ Q_DECLARE_FLAGS(PaintEngineFeatures, PaintEngineFeature)
+
+ enum DirtyFlag {
+ DirtyPen = 0x0001,
+ DirtyBrush = 0x0002,
+ DirtyBrushOrigin = 0x0004,
+ DirtyFont = 0x0008,
+ DirtyBackground = 0x0010,
+ DirtyBackgroundMode = 0x0020,
+ DirtyTransform = 0x0040,
+ DirtyClipRegion = 0x0080,
+ DirtyClipPath = 0x0100,
+ DirtyHints = 0x0200,
+ DirtyCompositionMode = 0x0400,
+ DirtyClipEnabled = 0x0800,
+ DirtyOpacity = 0x1000,
+
+ AllDirty = 0xffff
+ };
+ Q_DECLARE_FLAGS(DirtyFlags, DirtyFlag)
+
+ enum PolygonDrawMode {
+ OddEvenMode,
+ WindingMode,
+ ConvexMode,
+ PolylineMode
+ };
+
+ explicit QPaintEngine(PaintEngineFeatures features=0);
+ virtual ~QPaintEngine();
+
+ bool isActive() const { return active; }
+ void setActive(bool newState) { active = newState; }
+
+ virtual bool begin(QPaintDevice *pdev) = 0;
+ virtual bool end() = 0;
+
+ virtual void updateState(const QPaintEngineState &state) = 0;
+
+ virtual void drawRects(const QRect *rects, int rectCount);
+ virtual void drawRects(const QRectF *rects, int rectCount);
+
+ virtual void drawLines(const QLine *lines, int lineCount);
+ virtual void drawLines(const QLineF *lines, int lineCount);
+
+ virtual void drawEllipse(const QRectF &r);
+ virtual void drawEllipse(const QRect &r);
+
+ virtual void drawPath(const QPainterPath &path);
+
+ virtual void drawPoints(const QPointF *points, int pointCount);
+ virtual void drawPoints(const QPoint *points, int pointCount);
+
+ virtual void drawPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode);
+ virtual void drawPolygon(const QPoint *points, int pointCount, PolygonDrawMode mode);
+
+ virtual void drawPixmap(const QRectF &r, const QPixmap &pm, const QRectF &sr) = 0;
+ virtual void drawTextItem(const QPointF &p, const QTextItem &textItem);
+ virtual void drawTiledPixmap(const QRectF &r, const QPixmap &pixmap, const QPointF &s);
+ virtual void drawImage(const QRectF &r, const QImage &pm, const QRectF &sr,
+ Qt::ImageConversionFlags flags = Qt::AutoColor);
+
+ void setPaintDevice(QPaintDevice *device);
+ QPaintDevice *paintDevice() const;
+
+ void setSystemClip(const QRegion &baseClip);
+ QRegion systemClip() const;
+
+ void setSystemRect(const QRect &rect);
+ QRect systemRect() const;
+
+#ifdef Q_WS_WIN
+ virtual HDC getDC() const;
+ virtual void releaseDC(HDC hdc) const;
+#endif
+
+ virtual QPoint coordinateOffset() const;
+
+ enum Type {
+ X11,
+ Windows,
+ QuickDraw, CoreGraphics, MacPrinter,
+ QWindowSystem,
+ PostScript,
+ OpenGL,
+ Picture,
+ SVG,
+ Raster,
+ Direct3D,
+ Pdf,
+ OpenVG,
+
+ User = 50, // first user type id
+ MaxUser = 100 // last user type id
+ };
+ virtual Type type() const = 0;
+
+ inline void fix_neg_rect(int *x, int *y, int *w, int *h);
+
+ inline bool testDirty(DirtyFlags df);
+ inline void setDirty(DirtyFlags df);
+ inline void clearDirty(DirtyFlags df);
+
+ bool hasFeature(PaintEngineFeatures feature) const { return (gccaps & feature) != 0; }
+
+ QPainter *painter() const;
+
+ void syncState();
+ inline bool isExtended() const { return extended; }
+
+protected:
+ QPaintEngine(QPaintEnginePrivate &data, PaintEngineFeatures devcaps=0);
+
+ QPaintEngineState *state;
+ PaintEngineFeatures gccaps;
+
+ uint active : 1;
+ uint selfDestruct : 1;
+ uint extended : 1;
+
+ QPaintEnginePrivate *d_ptr;
+
+private:
+ void setAutoDestruct(bool autoDestr) { selfDestruct = autoDestr; }
+ bool autoDestruct() const { return selfDestruct; }
+ Q_DISABLE_COPY(QPaintEngine)
+
+ friend class QFontEngineBox;
+ friend class QFontEngineMac;
+ friend class QFontEngineWin;
+#ifndef QT_NO_FREETYPE
+ friend class QFontEngineFT;
+#endif
+#ifndef QT_NO_QWS_QPF
+ friend class QFontEngineQPF1;
+#endif
+#ifndef QT_NO_QWS_QPF2
+ friend class QFontEngineQPF;
+#endif
+ friend class QPSPrintEngine;
+ friend class QMacPrintEngine;
+ friend class QMacPrintEnginePrivate;
+#ifdef Q_WS_QWS
+ friend class QtopiaPrintEngine;
+ friend class QtopiaPrintEnginePrivate;
+ friend class QProxyFontEngine;
+#endif
+ friend class QPainter;
+ friend class QPainterPrivate;
+ friend class QWidget;
+ friend class QWidgetPrivate;
+ friend class QWin32PaintEngine;
+ friend class QWin32PaintEnginePrivate;
+ friend class QMacCGContext;
+ friend class QPreviewPaintEngine;
+};
+
+
+class Q_GUI_EXPORT QPaintEngineState
+{
+public:
+ QPaintEngine::DirtyFlags state() const { return dirtyFlags; }
+
+ QPen pen() const;
+ QBrush brush() const;
+ QPointF brushOrigin() const;
+ QBrush backgroundBrush() const;
+ Qt::BGMode backgroundMode() const;
+ QFont font() const;
+ QMatrix matrix() const;
+ QTransform transform() const;
+
+ Qt::ClipOperation clipOperation() const;
+ QRegion clipRegion() const;
+ QPainterPath clipPath() const;
+ bool isClipEnabled() const;
+
+ QPainter::RenderHints renderHints() const;
+ QPainter::CompositionMode compositionMode() const;
+ qreal opacity() const;
+
+ QPainter *painter() const;
+
+ bool brushNeedsResolving() const;
+ bool penNeedsResolving() const;
+
+protected:
+ friend class QPaintEngine;
+ friend class QRasterPaintEngine;
+ friend class QWidget;
+ friend class QPainter;
+ friend class QPainterPrivate;
+ friend class QMacPrintEnginePrivate;
+
+ QPaintEngine::DirtyFlags dirtyFlags;
+};
+
+//
+// inline functions
+//
+
+inline void QPaintEngine::fix_neg_rect(int *x, int *y, int *w, int *h)
+{
+ if (*w < 0) {
+ *w = -*w;
+ *x -= *w - 1;
+ }
+ if (*h < 0) {
+ *h = -*h;
+ *y -= *h - 1;
+ }
+}
+
+inline bool QPaintEngine::testDirty(DirtyFlags df) {
+ Q_ASSERT(state);
+ return ((state->dirtyFlags & df) != 0);
+}
+
+inline void QPaintEngine::setDirty(DirtyFlags df) {
+ Q_ASSERT(state);
+ state->dirtyFlags |= df;
+}
+
+inline void QPaintEngine::clearDirty(DirtyFlags df)
+{
+ Q_ASSERT(state);
+ state->dirtyFlags &= ~static_cast<uint>(df);
+}
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(QTextItem::RenderFlags)
+Q_DECLARE_OPERATORS_FOR_FLAGS(QPaintEngine::PaintEngineFeatures)
+Q_DECLARE_OPERATORS_FOR_FLAGS(QPaintEngine::DirtyFlags)
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QPAINTENGINE_H
diff --git a/src/gui/painting/qpaintengine_alpha.cpp b/src/gui/painting/qpaintengine_alpha.cpp
new file mode 100644
index 0000000000..e38f6a4168
--- /dev/null
+++ b/src/gui/painting/qpaintengine_alpha.cpp
@@ -0,0 +1,510 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <qglobal.h>
+
+#ifndef QT_NO_PRINTER
+#include <qdebug.h>
+#include "private/qpaintengine_alpha_p.h"
+
+#include "private/qpicture_p.h"
+#include "QtGui/qpicture.h"
+
+QT_BEGIN_NAMESPACE
+
+QAlphaPaintEngine::QAlphaPaintEngine(QAlphaPaintEnginePrivate &data, PaintEngineFeatures devcaps)
+ : QPaintEngine(data, devcaps)
+{
+
+}
+
+QAlphaPaintEngine::~QAlphaPaintEngine()
+{
+
+}
+
+bool QAlphaPaintEngine::begin(QPaintDevice *pdev)
+{
+ Q_D(QAlphaPaintEngine);
+
+ d->m_continueCall = true;
+ if (d->m_pass != 0) {
+ return true;
+ }
+
+ d->m_savedcaps = gccaps;
+ d->m_pdev = pdev;
+
+ d->m_alphaPen = false;
+ d->m_alphaBrush = false;
+ d->m_alphaOpacity = false;
+ d->m_hasalpha = false;
+ d->m_advancedPen = false;
+ d->m_advancedBrush = false;
+ d->m_complexTransform = false;
+
+ // clear alpha region
+ d->m_alphargn = QRegion();
+ d->m_cliprgn = QRegion();
+ d->m_pen = QPen();
+ d->m_transform = QTransform();
+
+ flushAndInit();
+
+ return true;
+}
+
+extern int qt_defaultDpiX();
+extern int qt_defaultDpiY();
+
+bool QAlphaPaintEngine::end()
+{
+ Q_D(QAlphaPaintEngine);
+
+ d->m_continueCall = true;
+ if (d->m_pass != 0) {
+ return true;
+ }
+
+ flushAndInit(false);
+ return true;
+}
+
+void QAlphaPaintEngine::updateState(const QPaintEngineState &state)
+{
+ Q_D(QAlphaPaintEngine);
+
+ DirtyFlags flags = state.state();
+ if (flags & QPaintEngine::DirtyTransform) {
+ d->m_transform = state.transform();
+ d->m_complexTransform = (d->m_transform.type() > QTransform::TxScale);
+ }
+ if (flags & QPaintEngine::DirtyPen) {
+ d->m_pen = state.pen();
+ if (d->m_pen.style() == Qt::NoPen) {
+ d->m_advancedPen = false;
+ d->m_alphaPen = false;
+ } else {
+ d->m_advancedPen = (d->m_pen.brush().style() != Qt::SolidPattern);
+ d->m_alphaPen = !d->m_pen.brush().isOpaque();
+ }
+ }
+
+ if (d->m_pass != 0) {
+ d->m_continueCall = true;
+ return;
+ }
+ d->m_continueCall = false;
+
+ if (flags & QPaintEngine::DirtyOpacity) {
+ d->m_alphaOpacity = (state.opacity() != 1.0f);
+ }
+
+ if (flags & QPaintEngine::DirtyBrush) {
+ if (state.brush().style() == Qt::NoBrush) {
+ d->m_advancedBrush = false;
+ d->m_alphaBrush = false;
+ } else {
+ d->m_advancedBrush = (state.brush().style() != Qt::SolidPattern);
+ d->m_alphaBrush = !state.brush().isOpaque();
+ }
+ }
+
+
+ d->m_hasalpha = d->m_alphaOpacity || d->m_alphaBrush || d->m_alphaPen;
+
+ if (d->m_picengine)
+ d->m_picengine->updateState(state);
+}
+
+void QAlphaPaintEngine::drawPath(const QPainterPath &path)
+{
+ Q_D(QAlphaPaintEngine);
+
+ QRectF tr = d->addPenWidth(path);
+
+ if (d->m_pass == 0) {
+ d->m_continueCall = false;
+ if (d->m_hasalpha || d->m_advancedPen || d->m_advancedBrush) {
+ d->addAlphaRect(tr);
+ }
+ if (d->m_picengine)
+ d->m_picengine->drawPath(path);
+ } else {
+ d->m_continueCall = !d->fullyContained(tr);
+ }
+}
+
+void QAlphaPaintEngine::drawPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode)
+{
+ Q_D(QAlphaPaintEngine);
+
+ QPolygonF poly;
+ for (int i=0; i<pointCount; ++i)
+ poly.append(points[i]);
+
+ QPainterPath path;
+ path.addPolygon(poly);
+ QRectF tr = d->addPenWidth(path);
+
+ if (d->m_pass == 0) {
+ d->m_continueCall = false;
+ if (d->m_hasalpha || d->m_advancedPen || d->m_advancedBrush) {
+ d->addAlphaRect(tr);
+ }
+
+ if (d->m_picengine)
+ d->m_picengine->drawPolygon(points, pointCount, mode);
+ } else {
+ d->m_continueCall = !d->fullyContained(tr);
+ }
+}
+
+void QAlphaPaintEngine::drawPixmap(const QRectF &r, const QPixmap &pm, const QRectF &sr)
+{
+ Q_D(QAlphaPaintEngine);
+
+ QRectF tr = d->m_transform.mapRect(r);
+ if (d->m_pass == 0) {
+ d->m_continueCall = false;
+ if (pm.hasAlpha() || d->m_alphaOpacity || d->m_complexTransform || pm.isQBitmap()) {
+ d->addAlphaRect(tr);
+ }
+
+ if (d->m_picengine)
+ d->m_picengine->drawPixmap(r, pm, sr);
+
+ } else {
+ d->m_continueCall = !d->fullyContained(tr);
+ }
+}
+
+void QAlphaPaintEngine::drawImage(const QRectF &r, const QImage &image, const QRectF &sr)
+{
+ Q_D(QAlphaPaintEngine);
+
+ QRectF tr = d->m_transform.mapRect(r);
+ if (d->m_pass == 0) {
+ d->m_continueCall = false;
+ if (image.hasAlphaChannel() || d->m_alphaOpacity || d->m_complexTransform) {
+ d->addAlphaRect(tr);
+ }
+
+ if (d->m_picengine)
+ d->m_picengine->drawImage(r, image, sr);
+
+ } else {
+ d->m_continueCall = !d->fullyContained(tr);
+ }
+}
+
+void QAlphaPaintEngine::drawTextItem(const QPointF &p, const QTextItem &textItem)
+{
+ Q_D(QAlphaPaintEngine);
+
+ QRectF tr(p.x(), p.y() - textItem.ascent(), textItem.width() + 5, textItem.ascent() + textItem.descent() + 5);
+ tr = d->m_transform.mapRect(tr);
+
+ if (d->m_pass == 0) {
+ d->m_continueCall = false;
+ if (d->m_alphaPen || d->m_alphaOpacity || d->m_advancedPen) {
+ d->addAlphaRect(tr);
+ }
+ if (d->m_picengine) {
+ d->m_picengine->drawTextItem(p, textItem);
+ }
+ } else {
+ d->m_continueCall = !d->fullyContained(tr);
+ }
+}
+
+void QAlphaPaintEngine::drawTiledPixmap(const QRectF &r, const QPixmap &pixmap, const QPointF &s)
+{
+ Q_D(QAlphaPaintEngine);
+
+ QRectF brect = d->m_transform.mapRect(r);
+
+ if (d->m_pass == 0) {
+ d->m_continueCall = false;
+ if (pixmap.hasAlpha() || d->m_alphaOpacity || d->m_complexTransform || pixmap.isQBitmap()) {
+ d->addAlphaRect(brect);
+ }
+ if (d->m_picengine)
+ d->m_picengine->drawTiledPixmap(r, pixmap, s);
+ } else {
+ d->m_continueCall = !d->fullyContained(brect);
+ }
+}
+
+QRegion QAlphaPaintEngine::alphaClipping() const
+{
+ Q_D(const QAlphaPaintEngine);
+ return d->m_cliprgn;
+}
+
+bool QAlphaPaintEngine::continueCall() const
+{
+ Q_D(const QAlphaPaintEngine);
+ return d->m_continueCall;
+}
+
+void QAlphaPaintEngine::flushAndInit(bool init)
+{
+ Q_D(QAlphaPaintEngine);
+ Q_ASSERT(d->m_pass == 0);
+
+ if (d->m_pic) {
+ d->m_picpainter->end();
+
+ // set clip region
+ d->m_alphargn = d->m_alphargn.intersected(QRect(0, 0, d->m_pdev->width(), d->m_pdev->height()));
+
+ // just use the bounding rect if it's a complex region..
+ QVector<QRect> rects = d->m_alphargn.rects();
+ if (rects.size() > 10) {
+ QRect br = d->m_alphargn.boundingRect();
+ d->m_alphargn = QRegion(br);
+ rects.clear();
+ rects.append(br);
+ }
+
+ d->m_cliprgn = d->m_alphargn;
+
+ // now replay the QPicture
+ ++d->m_pass; // we are now doing pass #2
+
+ // reset states
+ gccaps = d->m_savedcaps;
+
+ painter()->save();
+ d->resetState(painter());
+
+ // make sure the output from QPicture is unscaled
+ QTransform mtx;
+ mtx.scale(1.0f / (qreal(d->m_pdev->logicalDpiX()) / qreal(qt_defaultDpiX())),
+ 1.0f / (qreal(d->m_pdev->logicalDpiY()) / qreal(qt_defaultDpiY())));
+ painter()->setTransform(mtx);
+ painter()->drawPicture(0, 0, *d->m_pic);
+
+ d->m_cliprgn = QRegion();
+ d->resetState(painter());
+
+ // fill in the alpha images
+ for (int i=0; i<rects.size(); ++i)
+ d->drawAlphaImage(rects.at(i));
+
+ d->m_alphargn = QRegion();
+
+ painter()->restore();
+
+ --d->m_pass; // pass #2 finished
+
+ cleanUp();
+ }
+
+ if (init) {
+ gccaps = PaintEngineFeatures(AllFeatures & ~QPaintEngine::ObjectBoundingModeGradients);
+
+ d->m_pic = new QPicture();
+ d->m_pic->d_ptr->in_memory_only = true;
+ d->m_picpainter = new QPainter(d->m_pic);
+ d->m_picengine = d->m_picpainter->paintEngine();
+
+ // When newPage() is called and the m_picpainter is recreated
+ // we have to copy the current state of the original printer
+ // painter back to the m_picpainter
+ d->m_picpainter->setPen(painter()->pen());
+ d->m_picpainter->setBrush(painter()->brush());
+ d->m_picpainter->setBrushOrigin(painter()->brushOrigin());
+ d->m_picpainter->setFont(painter()->font());
+ d->m_picpainter->setOpacity(painter()->opacity());
+ d->m_picpainter->setTransform(painter()->combinedTransform());
+ d->m_picengine->syncState();
+ }
+}
+
+void QAlphaPaintEngine::cleanUp()
+{
+ Q_D(QAlphaPaintEngine);
+
+ delete d->m_picpainter;
+ delete d->m_pic;
+
+ d->m_picpainter = 0;
+ d->m_pic = 0;
+ d->m_picengine = 0;
+}
+
+QAlphaPaintEnginePrivate::QAlphaPaintEnginePrivate()
+ : m_pass(0),
+ m_pic(0),
+ m_picengine(0),
+ m_picpainter(0),
+ m_hasalpha(false),
+ m_alphaPen(false),
+ m_alphaBrush(false),
+ m_alphaOpacity(false),
+ m_advancedPen(false),
+ m_advancedBrush(false),
+ m_complexTransform(false)
+{
+
+}
+
+QAlphaPaintEnginePrivate::~QAlphaPaintEnginePrivate()
+{
+ delete m_picpainter;
+ delete m_pic;
+}
+
+QRectF QAlphaPaintEnginePrivate::addPenWidth(const QPainterPath &path)
+{
+ QPainterPath tmp = path;
+
+ if (m_pen.style() == Qt::NoPen)
+ return (path.controlPointRect() * m_transform).boundingRect();
+ if (m_pen.isCosmetic())
+ tmp = path * m_transform;
+
+ QPainterPathStroker stroker;
+ if (m_pen.widthF() == 0.0f)
+ stroker.setWidth(1.0);
+ else
+ stroker.setWidth(m_pen.widthF());
+ stroker.setJoinStyle(m_pen.joinStyle());
+ stroker.setCapStyle(m_pen.capStyle());
+ tmp = stroker.createStroke(tmp);
+ if (m_pen.isCosmetic())
+ return tmp.controlPointRect();
+
+ return (tmp.controlPointRect() * m_transform).boundingRect();
+}
+
+QRect QAlphaPaintEnginePrivate::toRect(const QRectF &rect) const
+{
+ QRect r;
+ r.setLeft(int(rect.left()));
+ r.setTop(int(rect.top()));
+ r.setRight(int(rect.right() + 1));
+ r.setBottom(int(rect.bottom() + 1));
+ return r;
+}
+
+void QAlphaPaintEnginePrivate::addAlphaRect(const QRectF &rect)
+{
+ m_alphargn |= toRect(rect);
+}
+
+void QAlphaPaintEnginePrivate::drawAlphaImage(const QRectF &rect)
+{
+ Q_Q(QAlphaPaintEngine);
+
+ qreal dpiX = qMax(m_pdev->logicalDpiX(), 300);
+ qreal dpiY = qMax(m_pdev->logicalDpiY(), 300);
+ qreal xscale = (dpiX / m_pdev->logicalDpiX());
+ qreal yscale = (dpiY / m_pdev->logicalDpiY());
+
+ QTransform picscale;
+ picscale.scale(xscale, yscale);
+
+ const int tileSize = 2048;
+ QSize size((int(rect.width() * xscale)), int(rect.height() * yscale));
+ int divw = (size.width() / tileSize);
+ int divh = (size.height() / tileSize);
+ divw += 1;
+ divh += 1;
+
+ int incx = int(rect.width() / divw);
+ int incy = int(rect.height() / divh);
+
+ for (int y=0; y<divh; ++y) {
+ int ypos = int((incy * y) + rect.y());
+ int height = int((y == (divh - 1)) ? (rect.height() - (incy * y)) : incy) + 1;
+
+ for (int x=0; x<divw; ++x) {
+ int xpos = int((incx * x) + rect.x());
+ int width = int((x == (divw - 1)) ? (rect.width() - (incx * x)) : incx) + 1;
+
+ QSize imgsize((int)(width * xscale), (int)(height * yscale));
+ QImage img(imgsize, QImage::Format_RGB32);
+ img.fill(0xffffffff);
+
+ QPainter imgpainter(&img);
+ imgpainter.setTransform(picscale);
+ QPointF picpos(qreal(-xpos), qreal(-ypos));
+ imgpainter.drawPicture(picpos, *m_pic);
+ imgpainter.end();
+
+ q->painter()->setTransform(QTransform());
+ QRect r(xpos, ypos, width, height);
+ q->painter()->drawImage(r, img);
+ }
+ }
+}
+
+bool QAlphaPaintEnginePrivate::fullyContained(const QRectF &rect) const
+{
+ QRegion r(toRect(rect));
+ return (m_cliprgn.intersected(r) == r);
+}
+
+void QAlphaPaintEnginePrivate::resetState(QPainter *p)
+{
+ p->setPen(QPen());
+ p->setBrush(QBrush());
+ p->setBrushOrigin(0,0);
+ p->setBackground(QBrush());
+ p->setFont(QFont());
+ p->setTransform(QTransform());
+ // The view transform is already recorded and included in the
+ // picture we're about to replay. If we don't turn if off,
+ // the view matrix will be applied twice.
+ p->setViewTransformEnabled(false);
+ p->setClipRegion(QRegion(), Qt::NoClip);
+ p->setClipPath(QPainterPath(), Qt::NoClip);
+ p->setClipping(false);
+ p->setOpacity(1.0f);
+}
+
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_PRINTER
diff --git a/src/gui/painting/qpaintengine_alpha_p.h b/src/gui/painting/qpaintengine_alpha_p.h
new file mode 100644
index 0000000000..f510c732b8
--- /dev/null
+++ b/src/gui/painting/qpaintengine_alpha_p.h
@@ -0,0 +1,134 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QPAINTENGINE_ALPHA_P_H
+#define QPAINTENGINE_ALPHA_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.
+//
+
+#ifndef QT_NO_PRINTER
+#include "private/qpaintengine_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class QAlphaPaintEnginePrivate;
+
+class QAlphaPaintEngine : public QPaintEngine
+{
+ Q_DECLARE_PRIVATE(QAlphaPaintEngine)
+public:
+ ~QAlphaPaintEngine();
+
+ virtual bool begin(QPaintDevice *pdev);
+ virtual bool end();
+
+ virtual void updateState(const QPaintEngineState &state);
+
+ virtual void drawPath(const QPainterPath &path);
+
+ virtual void drawPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode);
+
+ virtual void drawPixmap(const QRectF &r, const QPixmap &pm, const QRectF &sr);
+ virtual void drawImage(const QRectF &r, const QImage &image, const QRectF &sr);
+ virtual void drawTextItem(const QPointF &p, const QTextItem &textItem);
+ virtual void drawTiledPixmap(const QRectF &r, const QPixmap &pixmap, const QPointF &s);
+
+protected:
+ QAlphaPaintEngine(QAlphaPaintEnginePrivate &data, PaintEngineFeatures devcaps = 0);
+ QRegion alphaClipping() const;
+ bool continueCall() const;
+ void flushAndInit(bool init = true);
+ void cleanUp();
+};
+
+class QAlphaPaintEnginePrivate : public QPaintEnginePrivate
+{
+ Q_DECLARE_PUBLIC(QAlphaPaintEngine)
+public:
+ QAlphaPaintEnginePrivate();
+ ~QAlphaPaintEnginePrivate();
+
+ int m_pass;
+ QPicture *m_pic;
+ QPaintEngine *m_picengine;
+ QPainter *m_picpainter;
+
+ QPaintEngine::PaintEngineFeatures m_savedcaps;
+ QPaintDevice *m_pdev;
+
+ QRegion m_alphargn;
+ QRegion m_cliprgn;
+
+ bool m_hasalpha;
+ bool m_alphaPen;
+ bool m_alphaBrush;
+ bool m_alphaOpacity;
+ bool m_advancedPen;
+ bool m_advancedBrush;
+ bool m_complexTransform;
+ bool m_continueCall;
+
+ QTransform m_transform;
+ QPen m_pen;
+
+ void addAlphaRect(const QRectF &rect);
+ QRectF addPenWidth(const QPainterPath &path);
+ void drawAlphaImage(const QRectF &rect);
+ QRect toRect(const QRectF &rect) const;
+ bool fullyContained(const QRectF &rect) const;
+
+ void resetState(QPainter *p);
+};
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_PRINTER
+
+#endif // QPAINTENGINE_ALPHA_P_H
diff --git a/src/gui/painting/qpaintengine_d3d.cpp b/src/gui/painting/qpaintengine_d3d.cpp
new file mode 100644
index 0000000000..bb81623df6
--- /dev/null
+++ b/src/gui/painting/qpaintengine_d3d.cpp
@@ -0,0 +1,4576 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <qdebug.h>
+#include "qpaintengine_d3d_p.h"
+
+#include "private/qdrawhelper_p.h"
+#include "private/qfont_p.h"
+#include "private/qfontengine_p.h"
+#include "private/qpaintengine_p.h"
+#include "private/qtessellator_p.h"
+#include <private/qbezier_p.h>
+#include <private/qpainter_p.h>
+#include <private/qpixmap_raster_p.h>
+#include <private/qpolygonclipper_p.h>
+#include <qbuffer.h>
+#include <qcache.h>
+#include <qdir.h>
+#include <qfileinfo.h>
+#include <qlibrary.h>
+#include <qlibraryinfo.h>
+#include <qmath.h>
+#include <qpaintdevice.h>
+#include <qpixmapcache.h>
+
+#include <qwidget.h>
+#include <d3d9.h>
+#include <d3dx9.h>
+
+#include <mmintrin.h>
+#include <xmmintrin.h>
+
+QT_BEGIN_NAMESPACE
+
+#ifndef M_PI
+ #define M_PI 3.14159265358979323846
+#endif
+
+#define QD3D_MASK_MARGIN 1
+#define QD3D_BATCH_SIZE 256
+
+// for the ClearType detection stuff..
+#ifndef SPI_GETFONTSMOOTHINGTYPE
+#define SPI_GETFONTSMOOTHINGTYPE 0x200A
+#endif
+
+#ifndef FE_FONTSMOOTHINGCLEARTYPE
+#define FE_FONTSMOOTHINGCLEARTYPE 0x0002
+#endif
+
+//#include <performance.h>
+#define PM_INIT
+#define PM_MEASURE(A)
+#define PM_DISPLAY
+
+//debugging
+//#define QT_DEBUG_VERTEXBUFFER_ACCESS
+//#define QT_DEBUG_D3D
+//#define QT_DEBUG_D3D_CALLS
+
+#define QD3D_SET_MARK(output) \
+ D3DPERF_SetMarker(0, QString(output).utf16());
+
+#define QT_VERTEX_RESET_LIMIT 24576
+#define QT_VERTEX_BUF_SIZE 32768
+#define QD3DFVF_CSVERTEX (D3DFVF_XYZ | D3DFVF_DIFFUSE | D3DFVF_TEX2 | D3DFVF_TEXCOORDSIZE4(0) | D3DFVF_TEXCOORDSIZE4(1))
+
+// this is a different usage of the effect framework than intended,
+// but it's convenient for us to use (See effect file)
+#define PASS_STENCIL_ODDEVEN 0
+#define PASS_STENCIL_WINDING 1
+#define PASS_STENCIL_DRAW 2
+#define PASS_STENCIL_DRAW_DIRECT 3
+#define PASS_STENCIL_CLIP 4
+#define PASS_STENCIL_NOSTENCILCHECK 5
+#define PASS_STENCIL_NOSTENCILCHECK_DIRECT 6
+#define PASS_TEXT 7
+#define PASS_CLEARTYPE_TEXT 8
+#define PASS_ALIASED_LINES 9
+#define PASS_ALIASED_LINES_DIRECT 10
+
+#define PASS_AA_CREATEMASK 0
+#define PASS_AA_DRAW 1
+#define PASS_AA_DRAW_DIRECT 2
+
+#define D3D_STAGE_COUNT 2
+#define D3D_RENDER_STATES 210
+#define D3D_TEXTURE_STATES 33
+#define D3D_SAMPLE_STATES 14
+
+
+typedef HRESULT (APIENTRY *PFND3DXCREATEBUFFER)(DWORD, LPD3DXBUFFER *);
+typedef HRESULT (APIENTRY *PFND3DXCREATEEFFECT)(LPDIRECT3DDEVICE9, LPCVOID, UINT, CONST D3DXMACRO *,
+ LPD3DXINCLUDE, DWORD, LPD3DXEFFECTPOOL,
+ LPD3DXEFFECT *, LPD3DXBUFFER *);
+typedef D3DXMATRIX *(APIENTRY *PFND3DXMATRIXORTHOOFFCENTERLH)(D3DMATRIX *, FLOAT, FLOAT,
+ FLOAT, FLOAT, FLOAT, FLOAT);
+typedef IDirect3D9 *(APIENTRY *PFNDIRECT3DCREATE9)(uint);
+
+static PFNDIRECT3DCREATE9 pDirect3DCreate9 = 0;
+static PFND3DXCREATEBUFFER pD3DXCreateBuffer = 0;
+static PFND3DXCREATEEFFECT pD3DXCreateEffect = 0;
+static PFND3DXMATRIXORTHOOFFCENTERLH pD3DXMatrixOrthoOffCenterLH = 0;
+
+
+class QD3DSurfaceManager : public QObject {
+ Q_OBJECT
+
+public:
+ enum QD3DSurfaceManagerStatus {
+ NoStatus = 0,
+ NeedsResetting = 0x01,
+ MaxSizeChanged = 0x02
+ };
+
+ QD3DSurfaceManager();
+ ~QD3DSurfaceManager();
+
+ void init(LPDIRECT3D9 object);
+
+ void setPaintDevice(QPaintDevice *pd);
+
+ int status() const;
+ void reset();
+
+ LPDIRECT3DSURFACE9 renderTarget();
+
+ LPDIRECT3DSURFACE9 surface(QPaintDevice *pd);
+ LPDIRECT3DSWAPCHAIN9 swapChain(QPaintDevice *pd);
+ void releasePaintDevice(QPaintDevice *pd);
+
+ LPDIRECT3DDEVICE9 device();
+ void cleanup();
+
+ QSize maxSize() const;
+
+private:
+ struct D3DSwapChain {
+ QSize size;
+ LPDIRECT3DSWAPCHAIN9 swapchain;
+ LPDIRECT3DSURFACE9 surface;
+ };
+
+ void updateMaxSize();
+ void initPresentParameters(D3DPRESENT_PARAMETERS *params);
+ D3DSwapChain *createSwapChain(QWidget *w);
+
+ QSize m_max_size;
+ int m_status;
+ QMap<QPaintDevice *, D3DSwapChain *> m_swapchains;
+
+ LPDIRECT3DDEVICE9 m_device;
+ QPaintDevice *m_pd;
+ HWND m_dummy;
+ D3DSwapChain *m_current;
+
+private Q_SLOTS:
+ void cleanupPaintDevice(QObject *);
+};
+
+struct vertex {
+ D3DVECTOR pos;
+ DWORD color;
+ FLOAT s0, t0, r0, q0;
+ FLOAT s1, t1, r1, q1;
+};
+
+struct QD3DMaskPosition {
+ int x, y, channel;
+};
+
+
+struct QD3DBatchItem {
+ enum QD3DBatchInfo {
+ BI_WINDING = 0x0001,
+ BI_AA = 0x0002,
+ BI_BRECT = 0x0004,
+ BI_MASKFULL = 0x0008,
+ BI_TEXT = 0x0010,
+ BI_MASK = 0x0020,
+ BI_CLIP = 0x0040,
+ BI_SCISSOR = 0x0080,
+
+ BI_PIXMAP = 0x0100,
+ BI_IMAGE = 0x0200,
+ BI_COMPLEXBRUSH = 0x0400,
+
+ BI_CLEARCLIP = 0x0800, // clip nothing (filling the clip mask with 0)
+ BI_TRANSFORM = 0x1000,
+ BI_MASKSCISSOR = 0x2000,
+ BI_FASTLINE = 0x4000,
+ BI_COSMETICPEN = 0x8000
+ };
+
+ int m_info;
+
+ int m_count;
+ int m_offset;
+
+ QD3DMaskPosition m_maskpos;
+ qreal m_xoffset;
+ qreal m_yoffset;
+ qreal m_opacity;
+
+ QPixmap m_pixmap;
+ QRectF m_brect;
+ QBrush m_brush;
+
+ IDirect3DTexture9 *m_texture;
+
+ qreal m_width;
+ qreal m_distance;
+
+ QTransform m_matrix;
+ QPainter::CompositionMode m_cmode;
+
+ QVector<int> m_pointstops;
+};
+
+struct QD3DBatch {
+ int m_item_index;
+ QD3DBatchItem items[QD3D_BATCH_SIZE];
+};
+
+class QD3DStateManager;
+class QD3DFontCache;
+class QD3DDrawHelper;
+class QD3DGradientCache;
+
+class QDirect3DPaintEnginePrivate : public QPaintEnginePrivate
+{
+ Q_DECLARE_PUBLIC(QDirect3DPaintEngine)
+
+public:
+ enum RenderTechnique {
+ RT_NoTechnique,
+ RT_Antialiased,
+ RT_Aliased,
+ };
+
+ QDirect3DPaintEnginePrivate()
+ : m_d3d_object(0)
+ , m_d3d_device(0)
+ , m_txop(QTransform::TxNone)
+ , m_effect(0)
+ , m_flush_on_end(0)
+ { init(); }
+
+ ~QDirect3DPaintEnginePrivate();
+
+ bool init();
+ void initDevice();
+
+ inline QD3DBatchItem *nextBatchItem();
+
+ QPolygonF brushCoordinates(const QRectF &r, bool stroke, qreal *fp) const;
+ void fillAliasedPath(QPainterPath path, const QRectF &brect, const QTransform &txform);
+ void fillAntialiasedPath(const QPainterPath &path, const QRectF &brect,
+ const QTransform &txform, bool stroke);
+ void fillPath(const QPainterPath &path, QRectF brect);
+
+ void strokePath(const QPainterPath &path, QRectF brect, bool simple = false);
+ QPainterPath strokePathFastPen(const QPainterPath &path);
+ void strokeAliasedPath(QPainterPath path, const QRectF &brect, const QTransform &txform);
+
+ void flushBatch();
+ int flushAntialiased(int offset);
+ void flushAliased(QD3DBatchItem *item, int offset);
+ void flushText(QD3DBatchItem *item, int offset);
+ void flushLines(QD3DBatchItem *item, int offset);
+
+ void updateTransform(const QTransform &matrix);
+ void updatePen(const QPen &pen);
+ void updateBrush(const QBrush &pen);
+ void updateClipRegion(const QRegion &clipregion, Qt::ClipOperation op = Qt::ReplaceClip);
+ void updateClipPath(const QPainterPath &clipregion, Qt::ClipOperation op = Qt::ReplaceClip);
+ void updateFont(const QFont &font);
+
+ void setRenderTechnique(RenderTechnique technique);
+
+ QPointF transformPoint(const QPointF &p, qreal *w) const;
+
+ bool prepareBatch(QD3DBatchItem *item, int offset);
+ void prepareItem(QD3DBatchItem *item);
+ void cleanupItem(QD3DBatchItem *item);
+ void setCompositionMode(QPainter::CompositionMode mode);
+
+ void verifyTexture(const QPixmap &pixmap);
+
+ bool isFastRect(const QRectF &rect);
+
+ void releaseDC();
+
+ void cleanup();
+ bool testCaps();
+
+ QPixmap getPattern(Qt::BrushStyle style) const;
+
+ // clipping
+ QPainterPath m_sysclip_path;
+ QPainterPath m_clip_path;
+ QRegion m_sysclip_region;
+ QRegion m_clip_region;
+
+ qreal m_opacity;
+ D3DCOLOR m_opacity_color;
+
+ int m_current_state;
+
+ ID3DXEffect* m_effect;
+
+ RenderTechnique m_current_technique;
+
+ QTransform m_matrix;
+ qreal m_inv_scale;
+
+ QPen m_pen;
+ Qt::BrushStyle m_pen_brush_style;
+ QTransform m_inv_pen_matrix;
+ D3DCOLOR m_pen_color;
+ qreal m_pen_width;
+
+ QBrush m_brush;
+ Qt::BrushStyle m_brush_style;
+ QTransform m_inv_brush_matrix;
+ D3DCOLOR m_brush_color;
+ QTransform m_brush_origin;
+
+ uint m_clipping_enabled : 1;
+ uint m_has_complex_clipping : 1;
+ uint m_cleartype_text: 1;
+ uint m_has_pen : 1;
+ uint m_has_cosmetic_pen : 1;
+ uint m_has_brush : 1;
+ uint m_has_fast_pen : 1;
+ uint m_has_aa_fast_pen : 1;
+ uint m_flush_on_end : 1;
+ uint m_supports_d3d : 1;
+
+ QTransform::TransformationType m_txop;
+
+ QPainter::CompositionMode m_cmode;
+
+ QD3DSurfaceManager m_surface_manager;
+ QSize m_surface_size;
+
+ LPDIRECT3D9 m_d3d_object;
+ LPDIRECT3DDEVICE9 m_d3d_device;
+ IDirect3DSurface9 *m_current_surface;
+ bool m_in_scene;
+
+ QD3DGradientCache *m_gradient_cache;
+ QD3DDrawHelper *m_draw_helper;
+ QD3DBatch m_batch;
+ QD3DStateManager *m_statemanager;
+
+ HDC m_dc;
+ IDirect3DSurface9 *m_dcsurface;
+
+ QMap<Qt::BrushStyle, QPixmap> m_patterns;
+};
+
+
+class QD3DStateManager : public ID3DXEffectStateManager {
+public:
+ QD3DStateManager(LPDIRECT3DDEVICE9 pDevice, ID3DXEffect *effect);
+ void reset();
+
+ inline void startStateBlock();
+ inline void endStateBlock();
+
+ inline void setCosmeticPen(bool enabled);
+ inline void setBrushMode(int mode);
+ inline void setTexture(LPDIRECT3DBASETEXTURE9 pTexture);
+ inline void setTexture(LPDIRECT3DBASETEXTURE9 pTexture, QGradient::Spread spread);
+ inline void setTransformation(const QTransform *matrix = 0);
+ inline void setProjection(const D3DXMATRIX *pMatrix);
+ inline void setMaskChannel(int channel);
+ inline void setMaskOffset(qreal x, qreal y);
+ inline void setFocalDistance(const qreal &fd);
+
+ inline void beginPass(int pass);
+ inline void endPass();
+
+ STDMETHOD(QueryInterface)(REFIID iid, LPVOID *ppv);
+ STDMETHOD_(ULONG, AddRef)();
+ STDMETHOD_(ULONG, Release)();
+
+ STDMETHOD(SetTransform)(D3DTRANSFORMSTATETYPE State, CONST D3DMATRIX *pMatrix);
+ STDMETHOD(SetMaterial)(CONST D3DMATERIAL9 *pMaterial);
+ STDMETHOD(SetLight)(DWORD Index, CONST D3DLIGHT9 *pLight);
+ STDMETHOD(LightEnable)(DWORD Index, BOOL Enable);
+ STDMETHOD(SetRenderState)(D3DRENDERSTATETYPE State, DWORD Value);
+ STDMETHOD(SetTexture)(DWORD Stage, LPDIRECT3DBASETEXTURE9 pTexture);
+ STDMETHOD(SetTextureStageState)(DWORD Stage, D3DTEXTURESTAGESTATETYPE Type, DWORD Value);
+ STDMETHOD(SetSamplerState)(DWORD Sampler, D3DSAMPLERSTATETYPE Type, DWORD Value);
+ STDMETHOD(SetNPatchMode)(FLOAT NumSegments);
+ STDMETHOD(SetFVF)(DWORD FVF);
+ STDMETHOD(SetVertexShader)(LPDIRECT3DVERTEXSHADER9 pShader);
+ STDMETHOD(SetVertexShaderConstantF)(UINT RegisterIndex, CONST FLOAT *pConstantData, UINT RegisterCount);
+ STDMETHOD(SetVertexShaderConstantI)(UINT RegisterIndex, CONST INT *pConstantData, UINT RegisterCount);
+ STDMETHOD(SetVertexShaderConstantB)(UINT RegisterIndex, CONST BOOL *pConstantData, UINT RegisterCount);
+ STDMETHOD(SetPixelShader)(LPDIRECT3DPIXELSHADER9 pShader);
+ STDMETHOD(SetPixelShaderConstantF)(UINT RegisterIndex, CONST FLOAT *pConstantData, UINT RegisterCount);
+ STDMETHOD(SetPixelShaderConstantI)(UINT RegisterIndex, CONST INT *pConstantData, UINT RegisterCount);
+ STDMETHOD(SetPixelShaderConstantB)(UINT RegisterIndex, CONST BOOL *pConstantData, UINT RegisterCount);
+private:
+ LPDIRECT3DVERTEXSHADER9 m_vertexshader;
+ LPDIRECT3DPIXELSHADER9 m_pixelshader;
+
+ LPDIRECT3DBASETEXTURE9 m_textures[D3D_STAGE_COUNT];
+ DWORD m_texturestates[D3D_STAGE_COUNT][D3D_TEXTURE_STATES];
+ DWORD m_samplerstates[D3D_STAGE_COUNT][D3D_SAMPLE_STATES];
+ DWORD m_renderstate[D3D_RENDER_STATES];
+
+ qreal m_radgradfd;
+
+ bool m_cosmetic_pen;
+ int m_pass;
+ int m_maskchannel;
+ int m_brushmode;
+ LPDIRECT3DBASETEXTURE9 m_texture;
+ D3DXMATRIX m_projection;
+
+ D3DXMATRIX m_d3dIdentityMatrix;
+ bool m_isIdentity;
+ QTransform m_transformation;
+
+ LPDIRECT3DDEVICE9 m_pDevice;
+ ID3DXEffect *m_effect;
+
+ LONG m_refs;
+ bool m_changed;
+ qreal m_xoffset, m_yoffset;
+ static int m_mask_channels[4][4];
+};
+
+//
+// font cache stuff
+//
+
+struct QD3DGlyphCoord {
+ // stores the offset and size of a glyph texture
+ qreal x;
+ qreal y;
+ qreal width;
+ qreal height;
+ qreal log_width;
+ qreal log_height;
+ QFixed x_offset;
+ QFixed y_offset;
+};
+
+struct QD3DFontTexture {
+ int x_offset; // current glyph offset within the texture
+ int y_offset;
+ int width;
+ int height;
+ IDirect3DTexture9 *texture;
+};
+
+typedef QHash<glyph_t, QD3DGlyphCoord*> QD3DGlyphHash;
+typedef QHash<QFontEngine*, QD3DGlyphHash*> QD3DFontGlyphHash;
+typedef QHash<quint64, QD3DFontTexture*> QD3DFontTexHash;
+
+class QD3DGlyphCache : public QObject
+{
+ Q_OBJECT
+public:
+ QD3DGlyphCache()
+ : QObject(0)
+ , current_cache(0) {}
+ ~QD3DGlyphCache();
+ QD3DGlyphCoord *lookup(QFontEngine *, glyph_t);
+ void cacheGlyphs(QDirect3DPaintEngine *, const QTextItemInt &, const QVarLengthArray<glyph_t> &,
+ bool);
+ void cleanCache();
+ inline QD3DFontTexture *fontTexture(QFontEngine *engine) {
+ return font_textures.constFind(reinterpret_cast<quint64>(engine)).value();
+ }
+
+public slots:
+ void fontEngineDestroyed(QObject *);
+
+private:
+ QImage clearTypeGlyph(QFontEngine *, glyph_t glyph);
+ QD3DGlyphHash *current_cache;
+ QD3DFontTexHash font_textures;
+ QD3DFontGlyphHash font_cache;
+};
+
+QD3DGlyphCache::~QD3DGlyphCache()
+{
+}
+
+QD3DGlyphCoord *QD3DGlyphCache::lookup(QFontEngine *, glyph_t g)
+{
+ Q_ASSERT(current_cache != 0);
+ QD3DGlyphHash::const_iterator it = current_cache->constFind(g);
+ if (it == current_cache->constEnd())
+ return 0;
+ return it.value();
+}
+
+void QD3DGlyphCache::cleanCache()
+{
+ QList<quint64> keys = font_textures.keys();
+ for (int i=0; i<keys.size(); ++i)
+ font_textures.value(keys.at(i))->texture->Release();
+
+ qDeleteAll(font_textures);
+ qDeleteAll(font_cache);
+ font_textures.clear();
+ font_cache.clear();
+ current_cache = 0;
+}
+
+void QD3DGlyphCache::fontEngineDestroyed(QObject *object)
+{
+// qDebug() << "=> font engine destroyed: " << object;
+ QFontEngine *engine = static_cast<QFontEngine *>(object);
+
+ QD3DFontGlyphHash::iterator cache_it = font_cache.find(engine);
+ if (cache_it != font_cache.end()) {
+ QD3DGlyphHash *cache = font_cache.take(engine);
+ delete cache;
+ }
+
+ quint64 font_key = reinterpret_cast<quint64>(engine);
+ QD3DFontTexture *tex = font_textures.take(font_key);
+ if (tex) {
+ tex->texture->Release();
+ delete tex;
+ }
+}
+
+QImage QD3DGlyphCache::clearTypeGlyph(QFontEngine *engine, glyph_t glyph)
+{
+ glyph_metrics_t gm = engine->boundingBox(glyph);
+ int glyph_x = qFloor(gm.x.toReal());
+ int glyph_y = qFloor(gm.y.toReal());
+ int glyph_width = qCeil((gm.x + gm.width).toReal()) - glyph_x + 2;
+ int glyph_height = qCeil((gm.y + gm.height).toReal()) - glyph_y + 2;
+
+ if (glyph_width + glyph_x <= 0 || glyph_height <= 0)
+ return QImage();
+ QImage im(glyph_width + glyph_x, glyph_height, QImage::Format_ARGB32_Premultiplied);
+ im.fill(0xff000000); // solid black
+ QPainter p(&im);
+
+ p.setPen(Qt::white);
+ p.setBrush(Qt::NoBrush);
+
+ QTextItemInt ti;
+ ti.ascent = engine->ascent();
+ ti.descent = engine->descent();
+ ti.width = glyph_width;
+ ti.fontEngine = engine;
+
+ QGlyphLayoutArray<1> glyphLayout;
+ ti.glyphs = glyphLayout;
+ ti.glyphs.glyphs[0] = glyph;
+ ti.glyphs.advances_x[0] = glyph_width;
+ p.drawTextItem(QPointF(-glyph_x, -glyph_y), ti);
+ p.end();
+ return im;
+}
+
+#if 0
+static void dump_font_texture(QD3DFontTexture *tex)
+{
+ QColor color(Qt::red);
+ D3DLOCKED_RECT rect;
+ if (FAILED(tex->texture->LockRect(0, &rect, 0, 0))) {
+ qDebug() << "debug: unable to lock texture rect.";
+ return;
+ }
+
+// cleartype version
+// uint *tex_data = (uint *) rect.pBits;
+// QImage im(tex->width, tex->height, QImage::Format_ARGB32);
+// for (int y=0; y<tex->height; ++y) {
+// for (int x=0; x<tex->width; ++x) {
+// im.setPixel(x, y, ((*(tex_data+x+y*tex->width))));
+// }
+// }
+ uchar *tex_data = (uchar *) rect.pBits;
+ QImage im(rect.Pitch, tex->height, QImage::Format_ARGB32);
+ for (int y=0; y<tex->height; ++y) {
+ for (int x=0; x<rect.Pitch; ++x) {
+ uchar val = ((*(tex_data+x+y*rect.Pitch)));
+ im.setPixel(x, y, 0xff000000 | (val << 16) | (val << 8) | val);
+ }
+ }
+ tex->texture->UnlockRect(0);
+ static int i= 0;
+ im.save(QString("tx%1.png").arg(i++));
+}
+#endif
+
+void QD3DGlyphCache::cacheGlyphs(QDirect3DPaintEngine *engine, const QTextItemInt &ti,
+ const QVarLengthArray<glyph_t> &glyphs, bool clearType)
+{
+ IDirect3DDevice9 *device = engine->d_func()->m_d3d_device;
+ QD3DFontGlyphHash::const_iterator cache_it = font_cache.constFind(ti.fontEngine);
+ QD3DGlyphHash *cache = 0;
+ if (cache_it == font_cache.constEnd()) {
+ cache = new QD3DGlyphHash;
+ font_cache.insert(ti.fontEngine, cache);
+ connect(ti.fontEngine, SIGNAL(destroyed(QObject *)), SLOT(fontEngineDestroyed(QObject *)));
+ } else {
+ cache = cache_it.value();
+ }
+
+ current_cache = cache;
+
+ D3DFORMAT tex_format = clearType ? D3DFMT_A8R8G8B8 : D3DFMT_A8;
+ quint64 font_key = reinterpret_cast<quint64>(ti.fontEngine);
+ QD3DFontTexHash::const_iterator it = font_textures.constFind(font_key);
+ QD3DFontTexture *font_tex = 0;
+ if (it == font_textures.constEnd()) {
+ // alloc a new texture, put it into the cache
+ int tex_height = qCeil(ti.ascent.toReal() + ti.descent.toReal()) + 5;
+ int tex_width = tex_height * 30; // ###
+ IDirect3DTexture9 *tex;
+ if (FAILED(device->CreateTexture(tex_width, tex_height, 1, 0,
+ tex_format, D3DPOOL_MANAGED, &tex, NULL)))
+ {
+ qWarning("QD3DGlyphCache::cacheGlyphs(): can't allocate font texture (%dx%d).",
+ tex_width, tex_height);
+ return;
+ } else {
+// qDebug() << "=> new font texture: " << QSize(tex_width,tex_height);
+ font_tex = new QD3DFontTexture;
+ font_tex->texture = tex;
+ font_tex->x_offset = 0;
+ font_tex->y_offset = 0;
+ font_tex->width = tex_width;
+ font_tex->height = tex_height;
+ font_textures.insert(font_key, font_tex);
+ }
+ } else {
+ font_tex = it.value();
+ // make it current render target..
+ }
+
+ // cache each glyph
+ for (int i=0; i<glyphs.size(); ++i) {
+ QD3DGlyphHash::const_iterator it = cache->constFind(glyphs[i]);
+ if (it == cache->constEnd()) {
+ glyph_metrics_t metrics = ti.fontEngine->boundingBox(glyphs[i]);
+ int glyph_width = qCeil(metrics.width.toReal()) + 5;
+ int glyph_height = qCeil(ti.ascent.toReal() + ti.descent.toReal()) + 5;
+ if (font_tex->x_offset + glyph_width > font_tex->width) {
+ // no room on the current line, start new glyph strip
+ int strip_height = glyph_height;
+ font_tex->x_offset = 0;
+ font_tex->y_offset += strip_height;
+ if (font_tex->y_offset >= font_tex->height) {
+ // if no room in the current texture - realloc a larger texture
+ int old_tex_height = font_tex->height;
+ font_tex->height += strip_height;
+
+ IDirect3DTexture9 *new_tex;
+ if (FAILED(device->CreateTexture(font_tex->width, font_tex->height, 1, 0,
+ tex_format, D3DPOOL_MANAGED, &new_tex, NULL)))
+ {
+ qWarning("QD3DGlyphCache(): can't re-allocate font texture.");
+ return;
+ } else {
+// qDebug() << " -> new glyph strip added:" << QSize(font_tex->width,font_tex->height);
+
+ D3DLOCKED_RECT new_rect, old_rect;
+ if (FAILED(font_tex->texture->LockRect(0, &old_rect, 0, D3DLOCK_READONLY))) {
+ qDebug() << "QD3DGlyphCache: unable to lock texture rect.";
+ return;
+ }
+ if (FAILED(new_tex->LockRect(0, &new_rect, 0, 0))) {
+ qDebug() << "QD3DGlyphCache: unable to lock texture rect.";
+ return;
+ }
+ memcpy(new_rect.pBits, old_rect.pBits, new_rect.Pitch * old_tex_height);
+ font_tex->texture->UnlockRect(0);
+ new_tex->UnlockRect(0);
+ engine->d_func()->flushBatch();
+ font_tex->texture->Release();
+ font_tex->texture = new_tex;
+ }
+
+ // update the texture coords and the y offset for the existing glyphs in
+ // the cache, because of the texture size change
+ QD3DGlyphHash::iterator it = cache->begin();
+ while (it != cache->end()) {
+ it.value()->height = (it.value()->height * old_tex_height) / font_tex->height;
+ it.value()->y = (it.value()->y * old_tex_height) / font_tex->height;
+ ++it;
+ }
+ }
+ }
+ QD3DGlyphCoord *d3d_glyph = new QD3DGlyphCoord;
+ d3d_glyph->x = qreal(font_tex->x_offset) / font_tex->width;
+ d3d_glyph->y = qreal(font_tex->y_offset) / font_tex->height;
+ d3d_glyph->width = qreal(glyph_width) / font_tex->width;
+ d3d_glyph->height = qreal(glyph_height) / font_tex->height;
+ d3d_glyph->log_width = d3d_glyph->width * font_tex->width;
+ d3d_glyph->log_height = d3d_glyph->height * font_tex->height;
+ d3d_glyph->x_offset = -metrics.x;
+ d3d_glyph->y_offset = metrics.y;
+
+ QImage glyph_im;
+ if (clearType)
+ glyph_im = clearTypeGlyph(ti.fontEngine, glyphs[i]);
+ else
+ glyph_im = ti.fontEngine->alphaMapForGlyph(glyphs[i]).convertToFormat(QImage::Format_Indexed8);
+
+ // write glyph to texture
+ D3DLOCKED_RECT rect;
+ RECT glyph_rect = { font_tex->x_offset, font_tex->y_offset,
+ font_tex->x_offset + glyph_im.width(),
+ font_tex->y_offset + glyph_im.height() };
+
+// qDebug() << " > new glyph char added:" << QSize(glyph_im.width(), glyph_im.height());
+ if (FAILED(font_tex->texture->LockRect(0, &rect, &glyph_rect, 0))) {
+ qDebug() << "QD3DGlyphCache: unable to lock texture rect.";
+ return;
+ }
+
+ // ### unify these loops
+ if (clearType) {
+ int ppl = rect.Pitch / 4;
+ uint *tex_data = (uint *) rect.pBits;
+ for (int y=0; y<glyph_im.height(); ++y) {
+ uint *s = (uint *) glyph_im.scanLine(y);
+ for (int x=0; x<glyph_im.width(); ++x) {
+ tex_data[ppl*y + x] = *s;
+ ++s;
+ }
+ }
+ } else {
+ int ppl = rect.Pitch;
+ uchar *tex_data = (uchar *) rect.pBits;
+ for (int y=0; y<glyph_im.height(); ++y) {
+ uchar *s = (uchar *) glyph_im.scanLine(y);
+ for (int x=0; x<glyph_im.width(); ++x) {
+ tex_data[ppl*y + x] = *s;
+ ++s;
+ }
+ }
+ }
+ font_tex->texture->UnlockRect(0);
+
+ // debug
+// dump_font_texture(font_tex);
+
+ if (font_tex->x_offset + glyph_width > font_tex->width) {
+ font_tex->x_offset = 0;
+ font_tex->y_offset += glyph_height;
+ } else {
+ font_tex->x_offset += glyph_width;
+ }
+
+ cache->insert(glyphs[i], d3d_glyph);
+ }
+ }
+}
+
+Q_GLOBAL_STATIC(QD3DGlyphCache, qd3d_glyph_cache)
+
+//
+// end font caching stuff
+//
+
+
+//
+// D3D image cache stuff
+//
+
+// ### keep the GL stuff in mind..
+typedef void (*_qt_image_cleanup_hook_64)(qint64);
+extern Q_GUI_EXPORT _qt_image_cleanup_hook_64 qt_image_cleanup_hook_64;
+
+static void qd3d_image_cleanup(qint64 key);
+
+class QD3DImage
+{
+public:
+ QD3DImage(IDirect3DDevice9 *device, const QImage &image);
+ ~QD3DImage();
+
+ IDirect3DTexture9 *texture;
+};
+
+static QList<IDirect3DTexture9 *> qd3d_release_list;
+
+QD3DImage::QD3DImage(IDirect3DDevice9 *device, const QImage &image)
+{
+ texture = 0;
+ Q_ASSERT(device);
+ QImage im = image.convertToFormat(QImage::Format_ARGB32);
+ if (FAILED(device->CreateTexture(im.width(), im.height(), 1, 0,
+ D3DFMT_A8R8G8B8, D3DPOOL_MANAGED, &texture, 0))) {
+ qWarning("QD3DImage(): unable to create Direct3D texture.");
+ return;
+ }
+// qDebug(" -> created image texture: %p - 0x%08x%08x",texture,uint (image.cacheKey() >> 32),uint (image.cacheKey() & 0xffffffff));
+ D3DLOCKED_RECT rect;
+ if (FAILED(texture->LockRect(0, &rect, 0, 0))) {
+ qDebug() << "QD3DImage: unable to lock texture rect.";
+ return;
+ }
+ DWORD *dst = (DWORD *) rect.pBits;
+ DWORD *src = (DWORD *) im.scanLine(0);
+
+ Q_ASSERT((rect.Pitch/4) == (im.bytesPerLine()/4));
+ memcpy(dst, src, rect.Pitch*im.height());
+ texture->UnlockRect(0);
+}
+
+QD3DImage::~QD3DImage()
+{
+ if (texture)
+ qd3d_release_list.append(texture);
+}
+
+static int qd3d_cache_limit = 64*1024; // cache ~64 MB worth of textures
+typedef QCache<quint64, QD3DImage> QD3DImageCache;
+
+class QD3DImageManager
+{
+public:
+ QD3DImageManager() {
+ // ### GL does the same!
+ qt_image_cleanup_hook_64 = qd3d_image_cleanup;
+ cache.setMaxCost(qd3d_cache_limit);
+ }
+ ~QD3DImageManager() {
+// qDebug() << "unhooking d3d image cache";
+ qt_image_cleanup_hook_64 = 0;
+ cache.clear();
+ }
+
+ IDirect3DTexture9 *lookup(IDirect3DDevice9 *device, const QImage &image);
+ void remove(quint64 key);
+
+private:
+ QD3DImageCache cache;
+};
+
+IDirect3DTexture9 *QD3DImageManager::lookup(IDirect3DDevice9 *device, const QImage &image)
+{
+ QD3DImage *tex_image = 0;
+
+ tex_image = cache.object(image.cacheKey());
+ if (!tex_image) {
+ // to avoid cache thrashing we remove images from the cache
+ // that have the same serial no as the cached image, since
+ // that image is most likely destoyed already, and we got a
+ // stale cache entry
+ uint serial = (uint) (image.cacheKey() >> 32);
+ QList<quint64> keys = cache.keys();
+ for (int i=0; i<keys.size(); ++i) {
+ if ((uint)(keys.at(i) >> 32) == serial) {
+ cache.remove(keys.at(i));
+ break;
+ }
+ }
+// qDebug(" => cached: %d, adding cache image: 0x%08x%08x",cache.size(), uint (image.cacheKey() >> 32),uint (image.cacheKey() & 0xffffffff));
+ // add cache entry
+ int cost = image.width()*image.height()*4/1024;
+ if (cache.totalCost() + cost > cache.maxCost()) {
+ // no room for new entries? kick out half the cached images
+ int old_max_cost = cache.maxCost();
+ cache.setMaxCost(old_max_cost/2);
+ cache.setMaxCost(old_max_cost);
+ }
+ tex_image = new QD3DImage(device, image);
+ cache.insert(image.cacheKey(), tex_image, cost);
+// qDebug() << "==> total cache cost: " << cache.totalCost() << cost;
+ }
+
+ return tex_image->texture;
+}
+
+void QD3DImageManager::remove(quint64 key)
+{
+// QList<quint64> keys = cache.keys();
+// if (keys.contains(key))
+// qDebug() << "entery removed from cache";
+ cache.remove(key);
+}
+
+Q_GLOBAL_STATIC(QD3DImageManager, qd3d_image_cache)
+
+static void qd3d_image_cleanup(qint64 key)
+{
+// qDebug() << "qd3d_image_cleanup:";
+// qDebug(" => key: 0x%08x%08x", (uint) (key >> 32), (uint)(key & 0xffffffff));
+ qd3d_image_cache()->remove(key);
+}
+
+//
+// end D3D image cache stuff
+//
+
+class QD3DDrawHelper : public QTessellator
+{
+public:
+ QD3DDrawHelper(QDirect3DPaintEnginePrivate *pe);
+ ~QD3DDrawHelper();
+
+ bool needsFlushing() const;
+ QD3DMaskPosition allocateMaskPosition(const QRectF &brect, bool *breakbatch);
+
+ void setClipPath(const QPainterPath &path, QD3DBatchItem **item);
+
+ void queueAntialiasedMask(const QPolygonF &poly, QD3DBatchItem **item, const QRectF &brect);
+ QRectF queueAliasedMask(const QPainterPath &path, QD3DBatchItem **item, D3DCOLOR color);
+
+ void queueRect(const QRectF &rect, QD3DBatchItem *item, D3DCOLOR color, const QPolygonF &trect);
+ void queueRect(const QRectF &rect, QD3DBatchItem *item, D3DCOLOR color);
+
+ void queueTextGlyph(const QRectF &rect, const qreal *tex_coords, QD3DBatchItem *item,
+ D3DCOLOR color);
+
+ void queueAntialiasedLines(const QPainterPath &path, QD3DBatchItem **item, const QRectF &brect);
+ void queueAliasedLines(const QLineF *lines, int lineCount, QD3DBatchItem **item);
+
+ int drawAntialiasedMask(int offset, int maxoffset);
+ void drawAliasedMask(int offset);
+ void drawAntialiasedBoundingRect(QD3DBatchItem *item);
+ void drawAliasedBoundingRect(QD3DBatchItem *item);
+ void drawTextItem(QD3DBatchItem *item);
+ void drawAliasedLines(QD3DBatchItem *item);
+
+ void setMaskSize(QSize size);
+
+ void beforeReset();
+ void afterReset();
+
+ IDirect3DSurface9 *freeMaskSurface();
+
+ inline void lockVertexBuffer();
+ inline void unlockVertexBuffer();
+
+ inline int index() { return m_index; }
+
+#ifdef QT_DEBUG_VERTEXBUFFER_ACCESS
+ enum VertexBufferAccess {
+ CLEAR = 0x00,
+ READ = 0x01,
+ WRITE = 0x02
+ };
+ int accesscontrol[QT_VERTEX_BUF_SIZE];
+#endif
+
+private:
+ void addTrap(const Trapezoid &trap);
+ void tessellate(const QPolygonF &poly);
+ inline void lineToStencil(qreal x, qreal y);
+ inline void curveToStencil(const QPointF &cp1, const QPointF &cp2, const QPointF &ep);
+ QRectF pathToVertexArrays(const QPainterPath &path);
+ void resetMask();
+
+ QDirect3DPaintEnginePrivate *m_pe;
+
+ qreal m_xoffset, m_yoffset;
+ int m_startindex;
+ int m_index;
+ int m_height, m_width;
+ LPDIRECT3DVERTEXBUFFER9 m_d3dvbuff;
+ vertex *m_vbuff;
+ QD3DBatchItem *m_item;
+ QRectF m_boundingRect;
+
+ qreal max_x;
+ qreal max_y;
+ qreal min_x;
+ qreal min_y;
+ qreal firstx;
+ qreal firsty;
+
+ QPointF tess_lastpoint;
+ int tess_index;
+
+ bool m_locked;
+ IDirect3DTexture9 *m_mask;
+ IDirect3DSurface9 *m_maskSurface;
+ IDirect3DSurface9 *m_depthStencilSurface;
+
+ D3DCOLOR m_color;
+ bool m_clearmask;
+ bool m_isLine;
+ bool m_firstPoint;
+
+ QD3DMaskPosition m_mask_position;
+ int m_mask_offsetX2;
+ int m_mask_offsetY2;
+};
+
+QD3DStateManager::QD3DStateManager(LPDIRECT3DDEVICE9 pDevice, ID3DXEffect *effect)
+ : m_pDevice(pDevice), m_effect(effect), m_refs(0)
+{
+ if (FAILED(D3DXMatrixIdentity(&m_d3dIdentityMatrix))) {
+ qWarning("QDirect3DPaintEngine: D3DXMatrixIdentity failed");
+ }
+ reset();
+}
+
+void QD3DStateManager::reset()
+{
+ m_radgradfd = -1;
+
+ m_cosmetic_pen = false;
+ m_pass = -1;
+ m_maskchannel = -1;
+ m_brushmode = -1;
+ m_texture = 0;
+ m_xoffset = INT_MAX;
+ m_yoffset = INT_MAX;
+
+ m_vertexshader = 0;
+ m_pixelshader = 0;
+
+ m_isIdentity = true;
+ m_transformation = QTransform();
+ m_effect->SetMatrix("g_mTransformation", &m_d3dIdentityMatrix);
+
+ ZeroMemory(&m_projection, sizeof(D3DMATRIX));
+ ZeroMemory(m_textures, sizeof(LPDIRECT3DBASETEXTURE9) * D3D_STAGE_COUNT);
+ FillMemory(m_samplerstates, sizeof(DWORD) * D3D_SAMPLE_STATES * D3D_STAGE_COUNT, 0xFFFFFFFE);
+ FillMemory(m_texturestates, sizeof(DWORD) * D3D_TEXTURE_STATES * D3D_STAGE_COUNT, 0xFFFFFFFE);
+ FillMemory(m_renderstate, sizeof(DWORD) * D3D_RENDER_STATES, 0xFFFFFFFE);
+}
+
+inline void QD3DStateManager::beginPass(int pass)
+{
+ if (pass != m_pass) {
+ if (m_pass != -1)
+ m_effect->EndPass();
+ m_effect->BeginPass(pass);
+ m_pass = pass;
+ }
+}
+
+inline void QD3DStateManager::endPass()
+{
+ if (m_pass != -1) {
+ m_pass = -1;
+ m_effect->EndPass();
+ }
+}
+
+inline void QD3DStateManager::startStateBlock() {
+ m_changed = false;
+}
+
+inline void QD3DStateManager::setCosmeticPen(bool enabled)
+{
+ if (enabled != m_cosmetic_pen) {
+ m_effect->SetBool("g_mCosmeticPen", enabled);
+ m_cosmetic_pen = enabled;
+ m_changed = true;
+ }
+}
+
+inline void QD3DStateManager::setBrushMode(int mode)
+{
+ if (mode != m_brushmode) {
+ m_effect->SetInt("g_mBrushMode", mode);
+ m_brushmode = mode;
+ m_changed = true;
+ }
+}
+
+inline void QD3DStateManager::setTexture(LPDIRECT3DBASETEXTURE9 pTexture)
+{
+ SetSamplerState(0, D3DSAMP_ADDRESSU, D3DTADDRESS_BORDER);
+ SetSamplerState(0, D3DSAMP_ADDRESSV, D3DTADDRESS_BORDER);
+
+ if (pTexture != m_texture) {
+ m_texture = pTexture;
+ m_effect->SetTexture("g_mTexture", pTexture);
+ m_changed = true;
+ }
+}
+
+inline void QD3DStateManager::setTexture(LPDIRECT3DBASETEXTURE9 pTexture, QGradient::Spread spread)
+{
+ switch(spread) {
+ case QGradient::RepeatSpread:
+ SetSamplerState(0, D3DSAMP_ADDRESSU, D3DTADDRESS_WRAP);
+ SetSamplerState(0, D3DSAMP_ADDRESSV, D3DTADDRESS_WRAP);
+ break;
+ case QGradient::ReflectSpread:
+ SetSamplerState(0, D3DSAMP_ADDRESSU, D3DTADDRESS_MIRROR);
+ SetSamplerState(0, D3DSAMP_ADDRESSV, D3DTADDRESS_MIRROR);
+ break;
+ default:
+ SetSamplerState(0, D3DSAMP_ADDRESSU, D3DTADDRESS_CLAMP);
+ SetSamplerState(0, D3DSAMP_ADDRESSV, D3DTADDRESS_CLAMP);
+ break;
+ };
+
+ if (pTexture != m_texture) {
+ m_texture = pTexture;
+ m_effect->SetTexture("g_mTexture", pTexture);
+ m_changed = true;
+ }
+}
+
+inline void QD3DStateManager::setTransformation(const QTransform *matrix)
+{
+ if (matrix) {
+ if (*matrix != m_transformation) {
+ D3DXMATRIX dxmatrix(matrix->m11(), matrix->m12(), 0, matrix->m13(),
+ matrix->m21(), matrix->m22(), 0, matrix->m23(),
+ 0, 0, 1, 0,
+ matrix->dx(), matrix->dy(), 0, 1);
+ m_effect->SetMatrix("g_mTransformation", &dxmatrix);
+ m_transformation = *matrix;
+ m_changed = true;
+ m_isIdentity = false;
+ }
+ } else if (!m_isIdentity) {
+ m_effect->SetMatrix("g_mTransformation", &m_d3dIdentityMatrix);
+ m_transformation = QTransform();
+ m_changed = true;
+ m_isIdentity = true;
+ }
+}
+
+inline void QD3DStateManager::setProjection(const D3DXMATRIX *pMatrix)
+{
+ if (*pMatrix != m_projection) {
+ m_effect->SetMatrix("g_mViewProjection", pMatrix);
+ m_projection = *pMatrix;
+ m_changed = true;
+ }
+}
+
+inline void QD3DStateManager::setFocalDistance(const qreal &fd)
+{
+ if (fd != m_radgradfd) {
+ m_effect->SetFloat("g_mFocalDist", fd);
+ m_changed = true;
+ m_radgradfd = fd;
+ }
+}
+
+inline void QD3DStateManager::setMaskOffset(qreal x, qreal y)
+{
+ if (x != m_xoffset || y != m_yoffset) {
+ float offset[2] = {x, y};
+ m_effect->SetFloatArray("g_mMaskOffset", offset, 2);
+ m_xoffset = x;
+ m_yoffset = y;
+ m_changed = true;
+ }
+}
+
+inline void QD3DStateManager::setMaskChannel(int channel)
+{
+ if (m_maskchannel != channel) {
+ m_effect->SetIntArray("g_mChannel", m_mask_channels[channel], 4);
+ m_maskchannel = channel;
+ m_changed = true;
+ }
+}
+
+inline void QD3DStateManager::endStateBlock()
+{
+ if (m_changed) {
+ m_effect->CommitChanges();
+ m_changed = false;
+ }
+}
+
+STDMETHODIMP QD3DStateManager::QueryInterface(REFIID iid, LPVOID *ppv)
+{
+ if(iid == IID_IUnknown || iid == IID_ID3DXEffectStateManager)
+ {
+ *ppv = this;
+ ++m_refs;
+ return NOERROR;
+ }
+ *ppv = NULL;
+ return ResultFromScode(E_NOINTERFACE);
+}
+
+STDMETHODIMP_(ULONG) QD3DStateManager::AddRef(void)
+{
+ return (ULONG)InterlockedIncrement( &m_refs );
+}
+
+
+STDMETHODIMP_(ULONG) QD3DStateManager::Release(void)
+{
+ if( 0L == InterlockedDecrement( &m_refs ) ) {
+ delete this;
+ return 0L;
+ }
+
+ return m_refs;
+}
+STDMETHODIMP QD3DStateManager::SetTransform(D3DTRANSFORMSTATETYPE State, CONST D3DMATRIX *pMatrix)
+{
+ return m_pDevice->SetTransform(State, pMatrix);
+}
+
+STDMETHODIMP QD3DStateManager::SetMaterial(CONST D3DMATERIAL9 *pMaterial)
+{
+ return m_pDevice->SetMaterial(pMaterial);
+}
+
+STDMETHODIMP QD3DStateManager::SetLight(DWORD Index, CONST D3DLIGHT9 *pLight)
+{
+ return m_pDevice->SetLight(Index, pLight);
+}
+
+STDMETHODIMP QD3DStateManager::LightEnable(DWORD Index, BOOL Enable)
+{
+ return m_pDevice->LightEnable(Index, Enable);
+}
+
+STDMETHODIMP QD3DStateManager::SetRenderState(D3DRENDERSTATETYPE State, DWORD Value)
+{
+ if (State < D3D_RENDER_STATES) {
+ if (m_renderstate[State] == Value)
+ return S_OK;
+ m_renderstate[State] = Value;
+ }
+ return m_pDevice->SetRenderState(State, Value);
+}
+
+STDMETHODIMP QD3DStateManager::SetTexture(DWORD Stage, LPDIRECT3DBASETEXTURE9 pTexture)
+{
+ if (Stage < D3D_STAGE_COUNT) {
+ if (m_textures[Stage] == pTexture)
+ return S_OK;
+ m_textures[Stage] = pTexture;
+ }
+ return m_pDevice->SetTexture(Stage, pTexture);
+}
+
+STDMETHODIMP QD3DStateManager::SetTextureStageState(DWORD Stage, D3DTEXTURESTAGESTATETYPE Type, DWORD Value)
+{
+ if (Stage < D3D_STAGE_COUNT && Type < D3D_TEXTURE_STATES) {
+ if (m_texturestates[Stage][Type] == Value)
+ return S_OK;
+ m_texturestates[Stage][Type] = Value;
+ }
+ return m_pDevice->SetTextureStageState(Stage, Type, Value);
+}
+
+STDMETHODIMP QD3DStateManager::SetSamplerState(DWORD Sampler, D3DSAMPLERSTATETYPE Type, DWORD Value)
+{
+ if (Sampler < D3D_STAGE_COUNT && Type < D3D_SAMPLE_STATES) {
+ if (m_samplerstates[Sampler][Type] == Value)
+ return S_OK;
+ m_samplerstates[Sampler][Type] = Value;
+ }
+ return m_pDevice->SetSamplerState(Sampler, Type, Value);
+}
+
+STDMETHODIMP QD3DStateManager::SetNPatchMode(FLOAT NumSegments)
+{
+ return m_pDevice->SetNPatchMode(NumSegments);
+}
+
+STDMETHODIMP QD3DStateManager::SetFVF(DWORD FVF)
+{
+ return m_pDevice->SetFVF(FVF);
+}
+
+STDMETHODIMP QD3DStateManager::SetVertexShader(LPDIRECT3DVERTEXSHADER9 pShader)
+{
+ if (m_vertexshader == pShader)
+ return S_OK;
+ m_vertexshader = pShader;
+ return m_pDevice->SetVertexShader(pShader);
+}
+
+STDMETHODIMP QD3DStateManager::SetVertexShaderConstantF(UINT RegisterIndex, CONST FLOAT *pConstantData, UINT RegisterCount)
+{
+ return m_pDevice->SetVertexShaderConstantF(RegisterIndex, pConstantData, RegisterCount);
+}
+
+STDMETHODIMP QD3DStateManager::SetVertexShaderConstantI(UINT RegisterIndex, CONST INT *pConstantData, UINT RegisterCount)
+{
+ return m_pDevice->SetVertexShaderConstantI(RegisterIndex, pConstantData, RegisterCount);
+}
+
+STDMETHODIMP QD3DStateManager::SetVertexShaderConstantB(UINT RegisterIndex, CONST BOOL *pConstantData, UINT RegisterCount)
+{
+ return m_pDevice->SetVertexShaderConstantB(RegisterIndex, pConstantData, RegisterCount);
+}
+
+STDMETHODIMP QD3DStateManager::SetPixelShader(LPDIRECT3DPIXELSHADER9 pShader)
+{
+ if (m_pixelshader == pShader)
+ return S_OK;
+ m_pixelshader = pShader;
+ return m_pDevice->SetPixelShader(pShader);
+}
+
+STDMETHODIMP QD3DStateManager::SetPixelShaderConstantF(UINT RegisterIndex, CONST FLOAT *pConstantData, UINT RegisterCount)
+{
+ return m_pDevice->SetPixelShaderConstantF(RegisterIndex, pConstantData, RegisterCount);
+}
+
+STDMETHODIMP QD3DStateManager::SetPixelShaderConstantI(UINT RegisterIndex, CONST INT *pConstantData, UINT RegisterCount)
+{
+ return m_pDevice->SetPixelShaderConstantI(RegisterIndex, pConstantData, RegisterCount);
+}
+
+STDMETHODIMP QD3DStateManager::SetPixelShaderConstantB(UINT RegisterIndex, CONST BOOL *pConstantData, UINT RegisterCount)
+{
+ return m_pDevice->SetPixelShaderConstantB(RegisterIndex, pConstantData, RegisterCount);
+}
+
+#define QD3D_GRADIENT_CACHE_SIZE 60
+#define QD3D_GRADIENT_PALETTE_SIZE 1024
+
+class QD3DGradientCache
+{
+ struct CacheInfo
+ {
+ inline CacheInfo(QGradientStops s, qreal op) :
+ stops(s), opacity(op) {}
+
+ IDirect3DTexture9 *texture;
+ QGradientStops stops;
+ qreal opacity;
+ };
+
+ typedef QMultiHash<quint64, CacheInfo> QD3DGradientColorTableHash;
+
+public:
+ QD3DGradientCache(LPDIRECT3DDEVICE9 device);
+ ~QD3DGradientCache();
+
+ inline IDirect3DTexture9 *getBuffer(const QGradientStops &stops, qreal opacity);
+
+protected:
+ inline void generateGradientColorTable(const QGradientStops& s,
+ uint *colorTable,
+ int size, qreal opacity) const;
+ IDirect3DTexture9 *addCacheElement(quint64 hash_val, const QGradientStops &stops, qreal opacity);
+ void cleanCache();
+
+ QD3DGradientColorTableHash cache;
+ LPDIRECT3DDEVICE9 m_device;
+};
+
+QD3DGradientCache::QD3DGradientCache(LPDIRECT3DDEVICE9 device)
+ : m_device(device)
+{
+
+}
+
+QD3DGradientCache::~QD3DGradientCache()
+{
+ cleanCache();
+}
+
+inline IDirect3DTexture9 *QD3DGradientCache::getBuffer(const QGradientStops &stops, qreal opacity)
+{
+ quint64 hash_val = 0;
+
+ for (int i = 0; i < stops.size() && i <= 2; i++)
+ hash_val += stops[i].second.rgba();
+
+ QD3DGradientColorTableHash::const_iterator it = cache.constFind(hash_val);
+
+ if (it == cache.constEnd())
+ return addCacheElement(hash_val, stops, opacity);
+ else {
+ do {
+ const CacheInfo &cache_info = it.value();
+ if (cache_info.stops == stops && cache_info.opacity == opacity) {
+ return cache_info.texture;
+ }
+ ++it;
+ } while (it != cache.constEnd() && it.key() == hash_val);
+ // an exact match for these stops and opacity was not found, create new cache
+ return addCacheElement(hash_val, stops, opacity);
+ }
+}
+
+void QD3DGradientCache::generateGradientColorTable(const QGradientStops& s, uint *colorTable, int size, qreal opacity) const
+{
+ int pos = 0;
+ qreal fpos = 0.0;
+ qreal incr = 1.0 / qreal(size);
+ QVector<uint> colors(s.size());
+
+ for (int i = 0; i < s.size(); ++i)
+ colors[i] = s[i].second.rgba();
+
+ uint alpha = qRound(opacity * 255);
+ while (fpos < s.first().first) {
+ colorTable[pos] = ARGB_COMBINE_ALPHA(colors[0], alpha);
+ pos++;
+ fpos += incr;
+ }
+
+ for (int i = 0; i < s.size() - 1; ++i) {
+ qreal delta = 1/(s[i+1].first - s[i].first);
+ while (fpos < s[i+1].first && pos < size) {
+ int dist = int(256 * ((fpos - s[i].first) * delta));
+ int idist = 256 - dist;
+ uint current_color = ARGB_COMBINE_ALPHA(colors[i], alpha);
+ uint next_color = ARGB_COMBINE_ALPHA(colors[i+1], alpha);
+#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
+ colorTable[pos] = INTERPOLATE_PIXEL_256(current_color, idist, next_color, dist);
+#else
+ uint c = INTERPOLATE_PIXEL_256(current_color, idist, next_color, dist);
+ colorTable[pos] = ( (c << 24) & 0xff000000)
+ | ((c >> 24) & 0x000000ff)
+ | ((c << 8) & 0x00ff0000)
+ | ((c >> 8) & 0x0000ff00);
+#endif // Q_BYTE_ORDER
+ ++pos;
+ fpos += incr;
+ }
+ }
+ for (;pos < size; ++pos)
+ colorTable[pos] = colors[s.size() - 1];
+}
+
+IDirect3DTexture9 *QD3DGradientCache::addCacheElement(quint64 hash_val, const QGradientStops &stops, qreal opacity)
+{
+ if (cache.size() == QD3D_GRADIENT_CACHE_SIZE) {
+ int elem_to_remove = qrand() % QD3D_GRADIENT_CACHE_SIZE;
+ uint key = cache.keys()[elem_to_remove];
+
+ // need to call release on each removed cache entry:
+ QD3DGradientColorTableHash::const_iterator it = cache.constFind(key);
+ do {
+ it.value().texture->Release();
+ } while (++it != cache.constEnd() && it.key() == key);
+
+ cache.remove(key); // may remove more than 1, but OK
+ }
+
+ CacheInfo cache_entry(stops, opacity);
+ uint buffer[QD3D_GRADIENT_PALETTE_SIZE];
+ generateGradientColorTable(stops, buffer, QD3D_GRADIENT_PALETTE_SIZE, opacity);
+
+ if (FAILED(m_device->CreateTexture(QD3D_GRADIENT_PALETTE_SIZE, 1, 1, 0,
+ D3DFMT_A8R8G8B8, D3DPOOL_MANAGED, &cache_entry.texture, 0))) {
+ qWarning("QD3DGradientCache::addCacheElement(): unable to create Direct3D texture.");
+ return 0;
+ }
+
+ D3DLOCKED_RECT rect;
+ if (FAILED(cache_entry.texture->LockRect(0, &rect, 0, 0))) {
+ qDebug() << "QD3DGradientCache::addCacheElement(): unable to lock texture rect.";
+ return 0;
+ }
+ memcpy(rect.pBits, buffer, rect.Pitch);
+ cache_entry.texture->UnlockRect(0);
+
+ return cache.insert(hash_val, cache_entry).value().texture;
+}
+
+void QD3DGradientCache::cleanCache()
+{
+ QD3DGradientColorTableHash::const_iterator it = cache.constBegin();
+ for (; it != cache.constEnd(); ++it) {
+ const CacheInfo &cache_info = it.value();
+ cache_info.texture->Release();
+ }
+ cache.clear();
+}
+
+QD3DSurfaceManager::QD3DSurfaceManager() :
+ m_status(NoStatus), m_dummy(0), m_device(0), m_pd(0), m_current(0)
+{
+
+}
+
+QD3DSurfaceManager::~QD3DSurfaceManager()
+{
+}
+
+void QD3DSurfaceManager::setPaintDevice(QPaintDevice *pd)
+{
+ m_status = NoStatus;
+ m_pd = pd;
+ m_current = 0;
+
+ if (m_device->TestCooperativeLevel() != D3D_OK) {
+ m_status = NeedsResetting;
+ return;
+ }
+
+ m_current = m_swapchains.value(pd, 0);
+ QWidget *w = static_cast<QWidget*>(pd);
+
+ if (m_current) {
+ if (m_current->size != w->size()) {
+ m_swapchains.remove(pd);
+ m_current->surface->Release();
+ m_current->swapchain->Release();
+ delete m_current;
+ m_current = 0;
+ }
+ }
+
+ if (!m_current) {
+ m_current = createSwapChain(w);
+ updateMaxSize();
+ }
+}
+
+int QD3DSurfaceManager::status() const
+{
+ return m_status;
+}
+
+void QD3DSurfaceManager::reset()
+{
+ QList<QPaintDevice *> pds = m_swapchains.keys();
+
+ QMap<QPaintDevice *, D3DSwapChain *>::const_iterator i = m_swapchains.constBegin();
+ while (i != m_swapchains.constEnd()) {
+ i.value()->surface->Release();
+ i.value()->swapchain->Release();
+ ++i;
+ }
+ qDeleteAll(m_swapchains.values());
+ m_swapchains.clear();
+
+ D3DPRESENT_PARAMETERS params;
+ initPresentParameters(&params);
+ params.hDeviceWindow = m_dummy;
+
+ HRESULT res = m_device->Reset(&params);
+ if (FAILED(res)) {
+ switch (res) {
+ case D3DERR_DEVICELOST:
+ qWarning("QDirect3DPaintEngine: Reset failed (D3DERR_DEVICELOST)");
+ break;
+ case D3DERR_DRIVERINTERNALERROR:
+ qWarning("QDirect3DPaintEngine: Reset failed (D3DERR_DRIVERINTERNALERROR)");
+ break;
+ case D3DERR_OUTOFVIDEOMEMORY:
+ qWarning("QDirect3DPaintEngine: Reset failed (D3DERR_OUTOFVIDEOMEMORY)");
+ break;
+ default:
+ qWarning("QDirect3DPaintEngine: Reset failed");
+ };
+ }
+
+ for (int i=0; i<pds.count(); ++i) {
+ QWidget *w = static_cast<QWidget*>(pds.at(i));
+ createSwapChain(w);
+ }
+
+ // reset the mask as well
+ m_status = MaxSizeChanged;
+
+ setPaintDevice(m_pd);
+ updateMaxSize();
+}
+
+LPDIRECT3DSURFACE9 QD3DSurfaceManager::renderTarget()
+{
+ return m_current ? m_current->surface : 0;
+}
+
+LPDIRECT3DSURFACE9 QD3DSurfaceManager::surface(QPaintDevice *pd)
+{
+ D3DSwapChain *swapchain = m_swapchains.value(pd, 0);
+ return swapchain ? swapchain->surface : 0;
+}
+
+LPDIRECT3DSWAPCHAIN9 QD3DSurfaceManager::swapChain(QPaintDevice *pd)
+{
+ D3DSwapChain *swapchain = m_swapchains.value(pd, 0);
+ return swapchain ? swapchain->swapchain : 0;
+}
+
+void QD3DSurfaceManager::releasePaintDevice(QPaintDevice *pd)
+{
+ D3DSwapChain *swapchain = m_swapchains.take(pd);
+
+ if (swapchain) {
+ swapchain->surface->Release();
+ swapchain->swapchain->Release();
+ delete swapchain;
+ if (swapchain == m_current)
+ m_current = 0;
+ }
+}
+
+LPDIRECT3DDEVICE9 QD3DSurfaceManager::device()
+{
+ return m_device;
+}
+
+void QD3DSurfaceManager::cleanup()
+{
+ QPixmapCache::clear();
+ qd3d_glyph_cache()->cleanCache();
+
+ // release doomed textures
+ for (int k=0; k<qd3d_release_list.size(); ++k)
+ qd3d_release_list.at(k)->Release();
+ qd3d_release_list.clear();
+
+ QMap<QPaintDevice *, D3DSwapChain *>::const_iterator i = m_swapchains.constBegin();
+ while (i != m_swapchains.constEnd()) {
+ i.value()->surface->Release();
+ i.value()->swapchain->Release();
+ ++i;
+ }
+ qDeleteAll(m_swapchains.values());
+
+ if (m_device)
+ m_device->Release();
+
+ DestroyWindow(m_dummy);
+ QString cname(QLatin1String("qt_d3d_dummy"));
+ QT_WA({
+ UnregisterClass((TCHAR*)cname.utf16(), (HINSTANCE)qWinAppInst());
+ } , {
+ UnregisterClassA(cname.toLatin1(), (HINSTANCE)qWinAppInst());
+ });
+}
+
+QSize QD3DSurfaceManager::maxSize() const
+{
+ return m_max_size;
+}
+
+extern "C" {
+ LRESULT CALLBACK QtWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
+};
+
+void QD3DSurfaceManager::init(LPDIRECT3D9 object)
+{
+ QString cname(QLatin1String("qt_d3d_dummy"));
+ uint style = CS_DBLCLKS | CS_SAVEBITS;
+ ATOM atom;
+ QT_WA({
+ WNDCLASS wc;
+ wc.style = style;
+ wc.lpfnWndProc = (WNDPROC)QtWndProc;
+ wc.cbClsExtra = 0;
+ wc.cbWndExtra = 0;
+ wc.hInstance = (HINSTANCE)qWinAppInst();
+ wc.hIcon = 0;
+ wc.hCursor = 0;
+ wc.hbrBackground = 0;
+ wc.lpszMenuName = 0;
+ wc.lpszClassName = (TCHAR*)cname.utf16();
+ atom = RegisterClass(&wc);
+ } , {
+ WNDCLASSA wc;
+ wc.style = style;
+ wc.lpfnWndProc = (WNDPROC)QtWndProc;
+ wc.cbClsExtra = 0;
+ wc.cbWndExtra = 0;
+ wc.hInstance = (HINSTANCE)qWinAppInst();
+ wc.hIcon = 0;
+ wc.hCursor = 0;
+ wc.hbrBackground = 0;
+ wc.lpszMenuName = 0;
+ QByteArray tempArray = cname.toLatin1();
+ wc.lpszClassName = tempArray;
+ atom = RegisterClassA(&wc);
+ });
+
+ QT_WA({
+ const TCHAR *className = (TCHAR*)cname.utf16();
+ m_dummy = CreateWindow(className, className, 0,
+ 0, 0, 1, 1,
+ 0, 0, qWinAppInst(), 0);
+ } , {
+ m_dummy = CreateWindowA(cname.toLatin1(), cname.toLatin1(), 0,
+ 0, 0, 1, 1,
+ 0, 0, qWinAppInst(), 0);
+ });
+
+ D3DPRESENT_PARAMETERS params;
+ initPresentParameters(&params);
+ params.hDeviceWindow = m_dummy;
+
+ HRESULT res = object->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, 0,
+ D3DCREATE_PUREDEVICE|D3DCREATE_HARDWARE_VERTEXPROCESSING|D3DCREATE_NOWINDOWCHANGES|D3DCREATE_FPU_PRESERVE,
+ &params, &m_device);
+
+ if (FAILED(res) || m_device == 0)
+ qWarning("QDirect3DPaintEngine: failed to create Direct3D device (error=0x%x).", res);
+}
+
+void QD3DSurfaceManager::updateMaxSize()
+{
+ int w = 0, h = 0;
+ QMap<QPaintDevice *, D3DSwapChain *>::const_iterator i = m_swapchains.constBegin();
+ while (i != m_swapchains.constEnd()) {
+
+ int nw = i.key()->width();
+ if (nw > w)
+ w = nw;
+
+ int nh = i.key()->height();
+ if (nh > h)
+ h = nh;
+
+ ++i;
+ }
+
+ QSize newsize = QSize(w, h);
+ if (newsize != m_max_size) {
+ m_status |= MaxSizeChanged;
+ m_max_size = newsize;
+ }
+}
+
+void QD3DSurfaceManager::initPresentParameters(D3DPRESENT_PARAMETERS *params)
+{
+ ZeroMemory(params, sizeof(D3DPRESENT_PARAMETERS));
+ params->Windowed = true;
+ params->SwapEffect = D3DSWAPEFFECT_COPY;
+ params->BackBufferFormat = D3DFMT_UNKNOWN;
+ params->PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE;
+ params->Flags = D3DPRESENTFLAG_LOCKABLE_BACKBUFFER;
+}
+
+QD3DSurfaceManager::D3DSwapChain *QD3DSurfaceManager::createSwapChain(QWidget *w)
+{
+ D3DPRESENT_PARAMETERS params;
+ initPresentParameters(&params);
+ params.hDeviceWindow = w->winId();
+ D3DSwapChain *swapchain = new D3DSwapChain();
+ swapchain->size = w->size();
+ if (FAILED(m_device->CreateAdditionalSwapChain(&params, &swapchain->swapchain)))
+ qWarning("QDirect3DPaintEngine: CreateAdditionalSwapChain failed");
+ if (FAILED(swapchain->swapchain->GetBackBuffer(0, D3DBACKBUFFER_TYPE_MONO, &swapchain->surface)))
+ qWarning("QDirect3DPaintEngine: GetBackBuffer failed");
+ m_swapchains.insert(w, swapchain);
+ connect(w, SIGNAL(destroyed(QObject *)), SLOT(cleanupPaintDevice(QObject *)));
+
+ // init with background color
+ QColor bg = w->palette().color(QPalette::Background);
+ m_device->ColorFill(swapchain->surface, 0, D3DCOLOR_ARGB(bg.alpha(), bg.red(),bg.green(),bg.blue()));
+
+ return swapchain;
+}
+
+void QD3DSurfaceManager::cleanupPaintDevice(QObject *object)
+{
+ QWidget *w = static_cast<QWidget *>(object);
+ releasePaintDevice(w);
+}
+
+int QD3DStateManager::m_mask_channels[4][4] =
+ {{1,0,0,0},{0,1,0,0},{0,0,1,0},{0,0,0,1}};
+
+QD3DDrawHelper::QD3DDrawHelper(QDirect3DPaintEnginePrivate *pe)
+ : m_pe(pe), m_d3dvbuff(0), m_maskSurface(0), m_depthStencilSurface(0),
+ m_locked(false), m_mask(0), m_startindex(0), m_index(0), m_vbuff(0), m_clearmask(true),
+ m_isLine(false), m_firstPoint(true)
+{
+ resetMask();
+#ifdef QT_DEBUG_VERTEXBUFFER_ACCESS
+ memset(accesscontrol, 0, QT_VERTEX_BUF_SIZE * sizeof(VertexBufferAccess));
+#endif
+
+ // create vertex buffer
+ afterReset();
+}
+
+QD3DDrawHelper::~QD3DDrawHelper()
+{
+ if (m_maskSurface)
+ m_maskSurface->Release();
+
+ if (m_mask)
+ m_mask->Release();
+
+ if (m_depthStencilSurface)
+ m_depthStencilSurface->Release();
+
+ if (m_d3dvbuff)
+ m_d3dvbuff->Release();
+}
+
+inline void QD3DDrawHelper::lockVertexBuffer()
+{
+ if (!m_locked) {
+ DWORD lockflags = D3DLOCK_NOOVERWRITE;
+ if (m_startindex >= QT_VERTEX_RESET_LIMIT) {
+ m_startindex = 0;
+ m_index = 0;
+
+#ifdef QT_DEBUG_VERTEXBUFFER_ACCESS
+ for (int i=0; i<QT_VERTEX_BUF_SIZE; ++i) {
+ if (accesscontrol[i] != (WRITE|READ) && accesscontrol[i] != CLEAR)
+ qDebug() << "Vertex Buffer: Access Error";
+ }
+ memset(accesscontrol, 0, QT_VERTEX_BUF_SIZE * sizeof(VertexBufferAccess));
+#endif
+
+ lockflags = D3DLOCK_DISCARD;
+ }
+
+ if (FAILED(m_d3dvbuff->Lock(0, 0, (void**)&m_vbuff, lockflags))) {
+ qWarning() << "QDirect3DPaintEngine: unable to lock vertex buffer.";
+ }
+ m_locked = true;
+ }
+}
+
+inline void QD3DDrawHelper::unlockVertexBuffer()
+{
+ if (m_locked) {
+ if (FAILED(m_d3dvbuff->Unlock())) {
+ qWarning() << "QDirect3DPaintEngine: unable to unlock vertex buffer.";
+ }
+ m_locked = false;
+ }
+}
+
+void QD3DDrawHelper::setClipPath(const QPainterPath &path, QD3DBatchItem **item)
+{
+ lockVertexBuffer();
+
+ m_item = *item;
+ m_item->m_maskpos.x = m_item->m_maskpos.y = 0;
+ m_item->m_maskpos.channel = 3;
+ m_item->m_info |= QD3DBatchItem::BI_CLIP;
+
+ bool winding = (path.fillRule() == Qt::WindingFill);
+ if (winding)
+ m_item->m_info |= QD3DBatchItem::BI_WINDING;
+
+ if (!path.isEmpty()) {
+ m_item->m_info |= QD3DBatchItem::BI_MASK;
+ m_item->m_info &= ~QD3DBatchItem::BI_AA;
+ m_color = 0;
+ QRectF brect = pathToVertexArrays(path);
+ queueRect(brect, m_item, 0);
+ }
+
+ *item = m_item;
+}
+
+
+
+void QD3DDrawHelper::queueAntialiasedMask(const QPolygonF &poly, QD3DBatchItem **item, const QRectF &brect)
+{
+ lockVertexBuffer();
+
+ m_item = *item;
+ m_item->m_info |= QD3DBatchItem::BI_MASK;
+ setWinding(m_item->m_info & QD3DBatchItem::BI_WINDING);
+
+ int xoffset = m_item->m_maskpos.x;
+ int yoffset = m_item->m_maskpos.y;
+
+ int x = brect.left();
+ int y = brect.top();
+
+ m_item->m_xoffset = (xoffset - x) + 1;
+ m_item->m_yoffset = (yoffset - y) + 1;
+
+ m_boundingRect = brect;
+ tessellate(poly);
+
+ *item = m_item;
+}
+
+QRectF QD3DDrawHelper::queueAliasedMask(const QPainterPath &path, QD3DBatchItem **item, D3DCOLOR color)
+{
+ lockVertexBuffer();
+
+ m_color = color;
+ m_item = *item;
+ m_item->m_info |= QD3DBatchItem::BI_MASK;
+
+ bool winding = (path.fillRule() == Qt::WindingFill);
+ if (winding)
+ m_item->m_info |= QD3DBatchItem::BI_WINDING;
+
+ QRectF result = pathToVertexArrays(path);
+ *item = m_item;
+ return result;
+}
+
+// used for drawing aliased transformed rects directly
+// don't use for antialiased or masked drawing
+void QD3DDrawHelper::queueRect(const QRectF &rect, QD3DBatchItem *item, D3DCOLOR color, const QPolygonF &trect)
+{
+ lockVertexBuffer();
+
+ qreal zval = (item->m_info & QD3DBatchItem::BI_CLIP) ? 0.0f : 0.5f;
+ item->m_info |= QD3DBatchItem::BI_BRECT;
+
+ // if the item does not have a mask, the offset is different
+ if (!(item->m_info & QD3DBatchItem::BI_MASK)) {
+ item->m_offset = m_index;
+ item->m_count = (item->m_info & QD3DBatchItem::BI_AA) ? 0 : -2;
+ }
+
+ qreal x1 = rect.left();
+ qreal y1 = rect.top();
+ qreal x2 = rect.right();
+ qreal y2 = rect.bottom();
+
+ QPointF tc = trect.at(0);
+ vertex v1 = { {x1, y1, zval} , color,
+ tc.x(), tc.y(), 0.f, 0.f,
+ 0.f , 0.f , 0.f, 0.f };
+
+ tc = trect.at(1);
+ vertex v2 = { {x2, y1, zval} , color,
+ tc.x(), tc.y(), 0.f, 0.f,
+ 0.f , 0.f , 0.f, 0.f};
+
+ tc = trect.at(2);
+ vertex v3 = { {x2, y2, zval} , color,
+ tc.x(), tc.y(), 0.f, 0.f,
+ 0.f , 0.f , 0.f, 0.f};;
+
+ tc = trect.at(3);
+ vertex v4 = { {x1, y2, zval} , color,
+ tc.x(), tc.y(), 0.f, 0.f,
+ 0.f , 0.f , 0.f, 0.f};
+
+#ifdef QT_DEBUG_VERTEXBUFFER_ACCESS
+ for (int i=m_index; i<(m_index + 4); ++i) {
+ if ((m_index + 4) > QT_VERTEX_BUF_SIZE)
+ qDebug() << "Vertex Buffer: Buffer overflow";
+ if (accesscontrol[i] != CLEAR)
+ qDebug() << "Vertex Buffer: Access Error";
+ accesscontrol[i] |= WRITE;
+ }
+#endif
+
+ m_vbuff[m_index++] = v1;
+ m_vbuff[m_index++] = v2;
+ m_vbuff[m_index++] = v3;
+ m_vbuff[m_index++] = v4;
+
+ m_startindex = m_index;
+}
+
+
+QD3DMaskPosition QD3DDrawHelper::allocateMaskPosition(const QRectF &brect, bool *breakbatch)
+{
+ int w = brect.width();
+ int h = brect.height();
+
+ w += 3;
+ h += 3;
+
+ if (w > m_width)
+ w = m_width;
+ if (h > m_height)
+ h = m_height;
+
+ *breakbatch = false;
+
+ if ((m_height - m_mask_offsetY2) >= h && (m_width - m_mask_position.x) >= w) {
+ m_mask_position.y = m_mask_offsetY2;
+ } else if ((m_width - m_mask_offsetX2) >= w) {
+ m_mask_position.y = QD3D_MASK_MARGIN;
+ m_mask_position.x = m_mask_offsetX2;
+ } else if (m_mask_position.channel < 3) {
+ ++m_mask_position.channel;
+ m_mask_position.x = m_mask_position.y = QD3D_MASK_MARGIN;
+ m_mask_offsetX2 = m_mask_offsetY2 = QD3D_MASK_MARGIN;
+ } else {
+ resetMask();
+ *breakbatch = true;
+ }
+
+ int newoffset = m_mask_position.x + w;
+ if (m_mask_offsetX2 < newoffset)
+ m_mask_offsetX2 = newoffset;
+ m_mask_offsetY2 = (m_mask_position.y + h);
+
+ return m_mask_position;
+
+}
+
+void QD3DDrawHelper::queueRect(const QRectF &rect, QD3DBatchItem *item, D3DCOLOR color)
+{
+ lockVertexBuffer();
+
+ QRectF brect;
+ item->m_info |= QD3DBatchItem::BI_BRECT;
+ qreal zval = (item->m_info & QD3DBatchItem::BI_CLIP) ? 0.0f : 0.5f;
+
+ if (item->m_info & QD3DBatchItem::BI_AA) {
+ int xoffset = item->m_maskpos.x;
+ int yoffset = item->m_maskpos.y;
+
+ int x = rect.left();
+ int y = rect.top();
+
+ brect = QRectF(x, y, rect.width() + 1, rect.height() + 1);
+
+ item->m_xoffset = (xoffset - x) + 1;
+ item->m_yoffset = (yoffset - y) + 1;
+
+ // if the item does not have a mask, the offset is different
+ if (!(item->m_info & QD3DBatchItem::BI_MASK)) {
+ item->m_offset = m_index;
+ item->m_count = 0;
+ }
+ } else {
+ brect = rect;
+
+ if (!(item->m_info & QD3DBatchItem::BI_MASK)) {
+ item->m_offset = m_index;
+ item->m_count = -2;
+ }
+ }
+
+ qreal left = brect.left();
+ qreal right = brect.right();
+ qreal top = brect.top();
+ qreal bottom = brect.bottom();
+
+ vertex v1 = { {left, bottom, zval}, color,
+ 0.f, 0.f, 0.f, 0.f,
+ 0.f, 0.f, 0.f, 0.f};
+ vertex v2 = { {left, top, zval}, color,
+ 0.f, 0.f, 0.f, 0.f,
+ 0.f, 0.f, 0.f, 0.f};
+ vertex v3 = { {right, top, zval}, color,
+ 0.f, 0.f, 0.f, 0.f,
+ 0.f, 0.f, 0.f, 0.f};
+ vertex v4 = { {right, bottom, zval}, color,
+ 0.f, 0.f, 0.f, 0.f,
+ 0.f, 0.f, 0.f, 0.f};
+
+#ifdef QT_DEBUG_VERTEXBUFFER_ACCESS
+ for (int i=m_index; i<(m_index + 4); ++i) {
+ if ((m_index + 4) > QT_VERTEX_BUF_SIZE)
+ qDebug() << "Vertex Buffer: Buffer overflow";
+ if (accesscontrol[i] != CLEAR)
+ qDebug() << "Vertex Buffer: Access Error";
+ accesscontrol[i] |= WRITE;
+ }
+#endif
+
+ m_vbuff[m_index++] = v1;
+ m_vbuff[m_index++] = v2;
+ m_vbuff[m_index++] = v3;
+ m_vbuff[m_index++] = v4;
+
+ m_startindex = m_index;
+}
+
+void QD3DDrawHelper::queueAntialiasedLines(const QPainterPath &path, QD3DBatchItem **item, const QRectF &brect)
+{
+ lockVertexBuffer();
+
+ m_item = *item;
+ m_item->m_info |= QD3DBatchItem::BI_MASK;
+ setWinding(m_item->m_info & QD3DBatchItem::BI_WINDING);
+
+ int xoffset = m_item->m_maskpos.x;
+ int yoffset = m_item->m_maskpos.y;
+ int x = brect.left();
+ int y = brect.top();
+
+ m_item->m_xoffset = (xoffset - x) + 1;
+ m_item->m_yoffset = (yoffset - y) + 1;
+
+ m_boundingRect = brect;
+
+ m_xoffset = (x - xoffset) + 0.5f;
+ m_yoffset = (y - yoffset) + 0.5f;
+
+ QPointF last;
+ for (int i = 0; i < path.elementCount(); ++i) {
+ QPainterPath::Element element = path.elementAt(i);
+
+ //Q_ASSERT(!element.isCurveTo());
+
+ if (element.isLineTo())
+ QTessellator::tessellateRect(last, element, m_item->m_width);
+
+ last = element;
+ }
+
+ m_item->m_offset = m_startindex;
+ m_item->m_count = ( m_index - m_startindex ) / 3;
+ m_startindex = m_index;
+
+ *item = m_item;
+}
+
+void QD3DDrawHelper::queueAliasedLines(const QLineF *lines, int lineCount, QD3DBatchItem **item)
+{
+ lockVertexBuffer();
+
+ m_item = *item;
+ m_item->m_info |= QD3DBatchItem::BI_FASTLINE;
+
+ for (int i=0; i<lineCount; ++i) {
+ const QLineF line = lines[i];
+ qreal p1x = line.p1().x();
+ qreal p1y = line.p1().y();
+ qreal p2x = line.p2().x();
+ qreal p2y = line.p2().y();
+
+ vertex v1 = { {p1x, p1y, m_pe->m_pen_width} , m_pe->m_pen_color,
+ -1.f, -1.f, p2x, p2y,
+ 0.f, 0.f, 0.f, 0.f };
+ vertex v2 = { {p1x, p1y, m_pe->m_pen_width} , m_pe->m_pen_color,
+ 1.f, -1.f, p2x, p2y,
+ 0.f, 0.f, 0.f, 0.f };
+ vertex v3 = { {p1x, p1y, m_pe->m_pen_width} , m_pe->m_pen_color,
+ 1.f, 1.f, p2x, p2y,
+ 0.f, 0.f, 0.f, 0.f };
+ vertex v4 = { {p1x, p1y, m_pe->m_pen_width} , m_pe->m_pen_color,
+ -1.f, 1.f, p2x, p2y,
+ 0.f, 0.f, 0.f, 0.f };
+
+ m_vbuff[m_index++] = v1;
+ m_vbuff[m_index++] = v2;
+ m_vbuff[m_index++] = v4;
+ m_vbuff[m_index++] = v4;
+ m_vbuff[m_index++] = v2;
+ m_vbuff[m_index++] = v3;
+
+ if (m_index >= (QT_VERTEX_BUF_SIZE - 16)) {
+ m_item->m_offset = m_startindex;
+ m_item->m_count = ( m_index - m_startindex ) / 2;
+ m_startindex = m_index;
+
+ QD3DBatchItem itemcopy = *m_item;
+ m_item = m_pe->nextBatchItem();
+ *m_item = itemcopy;
+
+ lockVertexBuffer();
+ }
+ }
+
+ m_item->m_offset = m_startindex;
+ m_item->m_count = ( m_index - m_startindex ) - 2;
+ m_startindex = m_index;
+
+ *item = m_item;
+}
+
+void QD3DDrawHelper::queueTextGlyph(const QRectF &rect, const qreal *tex_coords,
+ QD3DBatchItem *item, D3DCOLOR color)
+{
+ lockVertexBuffer();
+
+ qreal x1 = rect.left();
+ qreal y1 = rect.top();
+ qreal x2 = rect.right();
+ qreal y2 = rect.bottom();
+
+ vertex v1 = { {x1, y1, 0.5f}, color,
+ tex_coords[0], tex_coords[1], 0.f, 0.f,
+ 0.f , 0.f , 0.f, 0.f};
+ vertex v2 = { {x2, y1, 0.5f}, color,
+ tex_coords[2], tex_coords[1], 0.f, 0.f,
+ 0.f , 0.f , 0.f, 0.f};
+ vertex v3 = { {x2, y2, 0.5f}, color,
+ tex_coords[2], tex_coords[3], 0.f, 0.f,
+ 0.f , 0.f , 0.f, 0.f};
+ vertex v4 = { {x1, y1, 0.5f}, color,
+ tex_coords[0], tex_coords[1], 0.f, 0.f,
+ 0.f , 0.f , 0.f, 0.f};
+ vertex v5 = { {x2, y2, 0.5f}, color,
+ tex_coords[2], tex_coords[3], 0.f, 0.f,
+ 0.f , 0.f , 0.f, 0.f};
+ vertex v6 = { {x1, y2, 0.5f}, color,
+ tex_coords[0], tex_coords[3], 0.f, 0.f,
+ 0.f , 0.f , 0.f, 0.f};
+
+#ifdef QT_DEBUG_VERTEXBUFFER_ACCESS
+ for (int i=m_index; i<(m_index + 6); ++i) {
+ if ((m_index + 6) > QT_VERTEX_BUF_SIZE)
+ qDebug() << "Vertex Buffer: Buffer overflow";
+ if (accesscontrol[i] != CLEAR)
+ qDebug() << "Vertex Buffer: Access Error";
+ accesscontrol[i] |= WRITE;
+ }
+#endif
+
+ m_vbuff[m_index++] = v1;
+ m_vbuff[m_index++] = v2;
+ m_vbuff[m_index++] = v3;
+ m_vbuff[m_index++] = v4;
+ m_vbuff[m_index++] = v5;
+ m_vbuff[m_index++] = v6;
+
+ m_startindex = m_index;
+ ++item->m_count;
+}
+
+bool QD3DDrawHelper::needsFlushing() const
+{
+ return (m_pe->m_batch.m_item_index >= QD3D_BATCH_SIZE || m_startindex >= QT_VERTEX_RESET_LIMIT);
+}
+
+void QD3DDrawHelper::setMaskSize(QSize size)
+{
+ m_width = size.width();
+ m_height = size.height();
+
+ if (m_maskSurface)
+ m_maskSurface->Release();
+
+ if (m_mask)
+ m_mask->Release();
+
+ if (FAILED(m_pe->m_d3d_device->CreateTexture(m_width, m_height, 1, D3DUSAGE_RENDERTARGET,
+ D3DFMT_A8R8G8B8, D3DPOOL_DEFAULT, &m_mask, NULL))) {
+ qWarning() << "QDirect3DPaintEngine: CreateTexture() failed.";
+ }
+
+ if (m_depthStencilSurface)
+ m_depthStencilSurface->Release();
+
+ if (FAILED(m_pe->m_d3d_device->CreateDepthStencilSurface(m_width, m_height, D3DFMT_D24S8, D3DMULTISAMPLE_NONE, 0,
+ TRUE, &m_depthStencilSurface, NULL))) {
+ qWarning() << "QDirect3DPaintEngine: CreateDepthStencilSurface() failed.";
+ }
+
+ m_pe->m_d3d_device->SetDepthStencilSurface(m_depthStencilSurface);
+ m_pe->m_d3d_device->Clear(0, 0, D3DCLEAR_ZBUFFER|D3DCLEAR_STENCIL, 0, 0.0f, 0);
+
+ if (FAILED(m_mask->GetSurfaceLevel(0, &m_maskSurface))) {
+ qWarning() << "QDirect3DPaintEngine: GetSurfaceLevel() failed.";
+ }
+
+ m_pe->m_d3d_device->ColorFill(m_maskSurface, 0, D3DCOLOR_ARGB(0,0,0,0));
+ D3DXMATRIX projMatrix;
+ pD3DXMatrixOrthoOffCenterLH(&projMatrix, 0, m_width, m_height, 0, 0, 1);
+ m_pe->m_effect->SetMatrix("g_mMaskProjection", &projMatrix);
+ m_pe->m_effect->SetTexture("g_mAAMask", m_mask);
+}
+
+void QD3DDrawHelper::beforeReset()
+{
+ resetMask();
+ m_clearmask = true;
+
+ if (m_maskSurface) {
+ m_maskSurface->Release();
+ m_maskSurface = 0;
+ }
+
+ if (m_mask) {
+ m_mask->Release();
+ m_mask = 0;
+ }
+
+ if (m_depthStencilSurface) {
+ m_depthStencilSurface->Release();
+ m_depthStencilSurface = 0;
+ }
+
+ if (m_d3dvbuff)
+ m_d3dvbuff->Release();
+}
+
+void QD3DDrawHelper::afterReset()
+{
+ if (FAILED(m_pe->m_d3d_device->CreateVertexBuffer(QT_VERTEX_BUF_SIZE*sizeof(vertex), D3DUSAGE_DYNAMIC|D3DUSAGE_WRITEONLY,
+ QD3DFVF_CSVERTEX,
+ D3DPOOL_DEFAULT, &m_d3dvbuff, NULL))) {
+ qWarning() << "QDirect3DPaintEngine: failed to create vertex buffer.";
+ }
+
+ m_pe->m_d3d_device->SetStreamSource(0, m_d3dvbuff, 0, sizeof(vertex));
+ m_pe->m_d3d_device->SetFVF(QD3DFVF_CSVERTEX);
+
+ m_startindex = 0;
+ m_index = 0;
+}
+
+IDirect3DSurface9 *QD3DDrawHelper::freeMaskSurface()
+{
+ // we need to make sure the mask is cleared when it's used for something else
+ resetMask();
+ m_clearmask = true;
+
+ return m_maskSurface;
+}
+
+int QD3DDrawHelper::drawAntialiasedMask(int offset, int maxoffset)
+{
+ int newoffset = offset;
+ QD3DBatchItem *item = &(m_pe->m_batch.items[offset]);
+
+ // set mask as render target
+ if (FAILED(m_pe->m_d3d_device->SetRenderTarget(0, m_maskSurface)))
+ qWarning() << "QDirect3DPaintEngine: SetRenderTarget failed!";
+
+ if (m_clearmask) {
+ m_pe->m_d3d_device->Clear(0, 0, D3DCLEAR_TARGET,D3DCOLOR_ARGB(0,0,0,0), 0, 0);
+ m_clearmask = false;
+ }
+
+ // fill the mask
+ m_pe->m_statemanager->beginPass(PASS_AA_CREATEMASK);
+ for (; newoffset<maxoffset; ++newoffset) {
+ item = &(m_pe->m_batch.items[newoffset]);
+ if (!(item->m_info & QD3DBatchItem::BI_AA) || !(item->m_info & QD3DBatchItem::BI_MASK)) {
+ break;
+ } else if (item->m_info & QD3DBatchItem::BI_MASKFULL) {
+ item->m_info &= ~QD3DBatchItem::BI_MASKFULL;
+ m_clearmask = true;
+ break;
+ }
+
+ m_pe->m_statemanager->startStateBlock();
+ if (item->m_info & QD3DBatchItem::BI_MASKSCISSOR) {
+ RECT rect;
+ QRectF srect = item->m_brect.adjusted(item->m_xoffset, item->m_yoffset,
+ item->m_xoffset, item->m_yoffset);
+ rect.left = qMax(qRound(srect.left()), 0);
+ rect.top = qMax(qRound(srect.top()), 0);
+ rect.bottom = qMin(m_height, qRound(srect.bottom()));
+ rect.right = qMin(m_width, qRound(srect.right()));
+ m_pe->m_d3d_device->SetScissorRect(&rect);
+ m_pe->m_statemanager->SetRenderState(D3DRS_SCISSORTESTENABLE, TRUE);
+ }
+ m_pe->m_statemanager->setMaskChannel(item->m_maskpos.channel);
+ m_pe->m_statemanager->endStateBlock();
+
+#ifdef QT_DEBUG_VERTEXBUFFER_ACCESS
+ int vbstart = item->m_offset;
+ for (int i=vbstart; i<(vbstart + (item->m_count * 3)); ++i) {
+ if (accesscontrol[i] != WRITE)
+ qDebug() << "Vertex Buffer: Access Error";
+ accesscontrol[i] |= READ;
+ }
+#endif
+
+ m_pe->m_d3d_device->DrawPrimitive(D3DPT_TRIANGLELIST, item->m_offset, item->m_count);
+
+ if (item->m_info & QD3DBatchItem::BI_MASKSCISSOR) {
+ m_pe->m_statemanager->SetRenderState(D3DRS_SCISSORTESTENABLE, FALSE);
+ }
+ }
+ m_pe->m_statemanager->endPass();
+
+ return newoffset;
+}
+
+void QD3DDrawHelper::drawAliasedMask(int offset)
+{
+ QD3DBatchItem *item = &(m_pe->m_batch.items[offset]);
+ if (item->m_info & QD3DBatchItem::BI_MASK) {
+ m_pe->m_statemanager->beginPass( (item->m_info & QD3DBatchItem::BI_WINDING) ? PASS_STENCIL_WINDING : PASS_STENCIL_ODDEVEN );
+ int prev_stop = 0;
+ for (int i=0; i<item->m_pointstops.count(); ++i) {
+ int stop = item->m_pointstops.at(i);
+
+#ifdef QT_DEBUG_VERTEXBUFFER_ACCESS
+ int vbstart = (item->m_offset + prev_stop);
+ for (int j=vbstart; j<(vbstart+(stop - prev_stop)); ++j) {
+ if (accesscontrol[j] != WRITE)
+ qDebug() << "Vertex Buffer: Access Error";
+ accesscontrol[j] |= READ;
+ }
+#endif
+ m_pe->m_d3d_device->DrawPrimitive(D3DPT_TRIANGLEFAN, item->m_offset + prev_stop, (stop - prev_stop) - 2);
+ prev_stop = stop;
+ }
+ m_pe->m_statemanager->endPass();
+ }
+}
+
+void QD3DDrawHelper::drawTextItem(QD3DBatchItem *item)
+{
+#ifdef QT_DEBUG_VERTEXBUFFER_ACCESS
+ int vbstart = item->m_offset;
+ for (int j=vbstart; j<(vbstart + ((item->m_count * 2) * 3)); ++j) {
+ if (accesscontrol[j] != WRITE)
+ qDebug() << "Vertex Buffer: Access Error";
+ accesscontrol[j] |= READ;
+ }
+#endif
+ m_pe->m_d3d_device->DrawPrimitive(D3DPT_TRIANGLELIST, item->m_offset, item->m_count*2);
+}
+
+void QD3DDrawHelper::drawAliasedLines(QD3DBatchItem *item)
+{
+ m_pe->m_statemanager->setCosmeticPen(item->m_info & QD3DBatchItem::BI_COSMETICPEN);
+ if (item->m_info & QD3DBatchItem::BI_TRANSFORM) {
+ m_pe->m_statemanager->setTransformation(&item->m_matrix);
+ } else {
+ m_pe->m_statemanager->setTransformation();
+ }
+ int pass = (item->m_info & QD3DBatchItem::BI_MASK)
+ ? PASS_ALIASED_LINES
+ : PASS_ALIASED_LINES_DIRECT;
+ m_pe->m_statemanager->beginPass(pass);
+ m_pe->m_d3d_device->DrawPrimitive(D3DPT_TRIANGLELIST, item->m_offset, (item->m_count + 2) / 3);
+ m_pe->m_statemanager->endPass();
+}
+
+void QD3DDrawHelper::drawAntialiasedBoundingRect(QD3DBatchItem *item)
+{
+ if (item->m_info & QD3DBatchItem::BI_SCISSOR) {
+ RECT rect;
+ rect.left = qMax(qRound(item->m_brect.left()), 0);
+ rect.top = qMax(qRound(item->m_brect.top()), 0);
+ rect.bottom = qMin(m_height, qRound(item->m_brect.bottom()));
+ rect.right = qMin(m_width, qRound(item->m_brect.right()));
+ m_pe->m_d3d_device->SetScissorRect(&rect);
+ m_pe->m_statemanager->SetRenderState(D3DRS_SCISSORTESTENABLE, TRUE);
+ }
+
+#ifdef QT_DEBUG_VERTEXBUFFER_ACCESS
+ int vbstart = item->m_offset + (item->m_count * 3);
+ for (int j=vbstart; j<(vbstart + 4); ++j) {
+ if (accesscontrol[j] != WRITE)
+ qDebug() << "Vertex Buffer: Access Error";
+ accesscontrol[j] |= READ;
+ }
+#endif
+
+ m_pe->m_d3d_device->DrawPrimitive(D3DPT_TRIANGLEFAN, item->m_offset + (item->m_count * 3), 2);
+
+ if (item->m_info & QD3DBatchItem::BI_SCISSOR) {
+ m_pe->m_statemanager->SetRenderState(D3DRS_SCISSORTESTENABLE, FALSE);
+ }
+}
+
+void QD3DDrawHelper::drawAliasedBoundingRect(QD3DBatchItem *item)
+{
+#ifdef QT_DEBUG_VERTEXBUFFER_ACCESS
+ int vbstart = (item->m_offset + item->m_count + 2);
+ for (int j=vbstart; j<(vbstart + 4); ++j) {
+ if (accesscontrol[j] != WRITE)
+ qDebug() << "Vertex Buffer: Access Error";
+ accesscontrol[j] |= READ;
+ }
+#endif
+
+ m_pe->m_d3d_device->DrawPrimitive(D3DPT_TRIANGLEFAN, item->m_offset + item->m_count + 2, 2);
+}
+
+void QD3DDrawHelper::addTrap(const Trapezoid &trap)
+{
+ qreal topLeftY = Q27Dot5ToDouble(trap.topLeft->y) - m_yoffset;
+ qreal topLeftX = Q27Dot5ToDouble(trap.topLeft->x) - m_xoffset;
+ qreal topRightY = Q27Dot5ToDouble(trap.topRight->y) - m_yoffset;
+ qreal topRightX = Q27Dot5ToDouble(trap.topRight->x) - m_xoffset;
+ qreal top = Q27Dot5ToDouble(trap.top) - m_yoffset;
+ qreal bottom = Q27Dot5ToDouble(trap.bottom) - m_yoffset;
+
+ Q27Dot5 _h = trap.topLeft->y - trap.bottomLeft->y;
+ Q27Dot5 _w = trap.topLeft->x - trap.bottomLeft->x;
+ qreal _leftA = (qreal)_w/_h;
+ qreal _leftB = topLeftX - _leftA * topLeftY;
+
+ _h = trap.topRight->y - trap.bottomRight->y;
+ _w = trap.topRight->x - trap.bottomRight->x;
+ qreal _rightA = (qreal)_w/_h;
+ qreal _rightB = topRightX - _rightA * topRightY;
+
+ qreal invLeftA = qFuzzyCompare(_leftA + 1, 1) ? 0.0 : 1.0 / _leftA;
+ qreal invRightA = qFuzzyCompare(_rightA + 1, 1) ? 0.0 : 1.0 / _rightA;
+
+ vertex v1 = { {1.f, top - 1.f, 0.5f}, 0.f,
+ top, bottom, invLeftA, -invRightA,
+ _leftA, _leftB, _rightA, _rightB};
+ vertex v2 = { {0.f, top - 1.f, 0.5f}, 0.f,
+ top, bottom, invLeftA, -invRightA,
+ _leftA, _leftB, _rightA, _rightB};
+ vertex v3 = { {0.f, bottom + 1.f, 0.5f}, 0.f,
+ top, bottom, invLeftA, -invRightA,
+ _leftA, _leftB, _rightA, _rightB};
+
+ vertex v4 = { {1.f, top - 1.f, 0.5f}, 0.f,
+ top, bottom, invLeftA, -invRightA,
+ _leftA, _leftB, _rightA, _rightB};
+ vertex v5 = { {0.f, bottom + 1.f, 0.5f}, 0.f,
+ top, bottom, invLeftA, -invRightA,
+ _leftA, _leftB, _rightA, _rightB};
+ vertex v6 = { {1.f, bottom + 1.f, 0.5f}, 0.f,
+ top, bottom, invLeftA, -invRightA,
+ _leftA, _leftB, _rightA, _rightB};
+
+#ifdef QT_DEBUG_VERTEXBUFFER_ACCESS
+ for (int i=m_index; i<(m_index + 6); ++i) {
+ if ((m_index + 6) > QT_VERTEX_BUF_SIZE)
+ qDebug() << "Vertex Buffer: Buffer overflow";
+ if (accesscontrol[i] != CLEAR)
+ qDebug() << "Vertex Buffer: Access Error";
+ accesscontrol[i] |= WRITE;
+ }
+#endif
+
+ m_vbuff[m_index++] = v1;
+ m_vbuff[m_index++] = v2;
+ m_vbuff[m_index++] = v3;
+ m_vbuff[m_index++] = v4;
+ m_vbuff[m_index++] = v5;
+ m_vbuff[m_index++] = v6;
+
+ // check if buffer is full
+ if (m_index >= (QT_VERTEX_BUF_SIZE - 16)) {
+ m_item->m_offset = m_startindex;
+ m_item->m_count = ( m_index - m_startindex ) / 3;
+ m_startindex = m_index;
+
+ QD3DBatchItem itemcopy = *m_item;
+ m_item = m_pe->nextBatchItem();
+ *m_item = itemcopy;
+ m_item->m_info &= ~QD3DBatchItem::BI_MASKFULL;
+
+ lockVertexBuffer();
+ }
+}
+
+void QD3DDrawHelper::tessellate(const QPolygonF &poly) {
+ int xoffset = m_item->m_maskpos.x;
+ int yoffset = m_item->m_maskpos.y;
+
+ int x = m_boundingRect.left();
+ int y = m_boundingRect.top();
+ m_xoffset = (x - xoffset) + 0.5f;
+ m_yoffset = (y - yoffset) + 0.5f;
+
+ QTessellator::tessellate(poly.data(), poly.count());
+
+ m_item->m_offset = m_startindex;
+ m_item->m_count = ( m_index - m_startindex ) / 3;
+ m_startindex = m_index;
+}
+
+inline void QD3DDrawHelper::lineToStencil(qreal x, qreal y)
+{
+ QPointF lastPt = tess_lastpoint;
+ tess_lastpoint = QPointF(x, y);
+
+ if (m_isLine && m_firstPoint)
+ return;
+
+
+#ifdef QT_DEBUG_VERTEXBUFFER_ACCESS
+ if (m_index > QT_VERTEX_BUF_SIZE)
+ qDebug() << "Vertex Buffer: Buffer overflow";
+ if (accesscontrol[m_index] != CLEAR)
+ qDebug() << "Vertex Buffer: Access Error";
+ accesscontrol[m_index] |= WRITE;
+#endif
+
+ vertex v;
+ if (m_isLine) {
+ vertex v1 = { {lastPt.x(), lastPt.y(), m_pe->m_pen_width }, m_color,
+ -1.f, -1.f, x, y,
+ 0.f, 0.f, 0.f, 0.f};
+ vertex v2 = { {lastPt.x(), lastPt.y(), m_pe->m_pen_width }, m_color,
+ 1.f, -1.f, x, y,
+ 0.f, 0.f, 0.f, 0.f};
+ vertex v3 = { {lastPt.x(), lastPt.y(), m_pe->m_pen_width }, m_color,
+ 1.f, 1.f, x, y,
+ 0.f, 0.f, 0.f, 0.f};
+ vertex v4 = { {lastPt.x(), lastPt.y(), m_pe->m_pen_width }, m_color,
+ -1.f, 1.f, x, y,
+ 0.f, 0.f, 0.f, 0.f};
+ m_vbuff[m_index++] = v1;
+ m_vbuff[m_index++] = v2;
+ m_vbuff[m_index++] = v4;
+ m_vbuff[m_index++] = v4;
+ m_vbuff[m_index++] = v2;
+ m_vbuff[m_index++] = v3;
+ } else {
+ vertex v1 = { {x, y, 0.5f}, m_color,
+ 0.f, 0.f, 0.f, 0.f,
+ 0.f, 0.f, 0.f, 0.f};
+ m_vbuff[m_index++] = v1;
+ v = v1;
+ }
+ ++tess_index;
+
+ // check if buffer is full
+ if (m_index >= (QT_VERTEX_BUF_SIZE - 16)) {
+ int firstindex = m_startindex;
+ if (!m_item->m_pointstops.isEmpty())
+ firstindex = m_item->m_pointstops.last();
+
+ vertex first = m_vbuff[firstindex];
+
+ // finish current polygon
+ m_item->m_pointstops.append(tess_index);
+ m_item->m_offset = m_startindex;
+ m_startindex = m_index;
+
+ // copy item
+ QD3DBatchItem itemcopy = *m_item;
+ m_item = m_pe->nextBatchItem();
+ *m_item = itemcopy;
+
+ // start new polygon
+ lockVertexBuffer();
+ m_item->m_pointstops.clear();
+ if (!m_isLine) {
+ tess_index = 2;
+
+#ifdef QT_DEBUG_VERTEXBUFFER_ACCESS
+ if (accesscontrol[m_index] != CLEAR)
+ qDebug() << "Vertex Buffer: Access Error";
+ accesscontrol[m_index] |= WRITE;
+#endif
+
+ m_vbuff[m_index++] = first;
+
+#ifdef QT_DEBUG_VERTEXBUFFER_ACCESS
+ if (accesscontrol[m_index] != CLEAR)
+ qDebug() << "Vertex Buffer: Access Error";
+ accesscontrol[m_index] |= WRITE;
+#endif
+
+ m_vbuff[m_index++] = v;
+ } else {
+ tess_index = 0;
+ }
+ }
+
+ if (x > max_x)
+ max_x = x;
+ else if (x < min_x)
+ min_x = x;
+ if (y > max_y)
+ max_y = y;
+ else if (y < min_y)
+ min_y = y;
+}
+
+inline void QD3DDrawHelper::curveToStencil(const QPointF &cp1, const QPointF &cp2,
+ const QPointF &ep)
+{
+ qreal inverseScale = 0.5f;
+ qreal inverseScaleHalf = inverseScale / 2;
+
+ QBezier beziers[32];
+ beziers[0] = QBezier::fromPoints(tess_lastpoint, cp1, cp2, ep);
+ QBezier *b = beziers;
+ while (b >= beziers) {
+ // check if we can pop the top bezier curve from the stack
+ qreal l = qAbs(b->x4 - b->x1) + qAbs(b->y4 - b->y1);
+ qreal d;
+ if (l > inverseScale) {
+ d = qAbs( (b->x4 - b->x1)*(b->y1 - b->y2) - (b->y4 - b->y1)*(b->x1 - b->x2) )
+ + qAbs( (b->x4 - b->x1)*(b->y1 - b->y3) - (b->y4 - b->y1)*(b->x1 - b->x3) );
+ d /= l;
+ } else {
+ d = qAbs(b->x1 - b->x2) + qAbs(b->y1 - b->y2) +
+ qAbs(b->x1 - b->x3) + qAbs(b->y1 - b->y3);
+ }
+ if (d < inverseScaleHalf || b == beziers + 31) {
+ // good enough, we pop it off and add the endpoint
+ lineToStencil(b->x4, b->y4);
+ --b;
+ } else {
+ // split, second half of the polygon goes lower into the stack
+ b->split(b+1, b);
+ ++b;
+ }
+ }
+}
+
+QRectF QD3DDrawHelper::pathToVertexArrays(const QPainterPath &path)
+{
+ m_isLine = (m_item->m_info & QD3DBatchItem::BI_FASTLINE);
+ const QPainterPath::Element &first = path.elementAt(0);
+ firstx = first.x;
+ firsty = first.y;
+ min_x = max_x = firstx;
+ min_y = max_y = firsty;
+
+ m_firstPoint = true;
+ tess_index = 0;
+ m_item->m_pointstops.clear();
+ lineToStencil(firstx, firsty);
+ m_firstPoint = false;
+
+ for (int i=1; i<path.elementCount(); ++i) {
+ const QPainterPath::Element &e = path.elementAt(i);
+ switch (e.type) {
+ case QPainterPath::MoveToElement:
+ m_item->m_pointstops.append(tess_index);
+ m_firstPoint = true;
+ lineToStencil(e.x, e.y);
+ m_firstPoint = false;
+ break;
+ case QPainterPath::LineToElement:
+ lineToStencil(e.x, e.y);
+ break;
+ case QPainterPath::CurveToElement:
+ curveToStencil(e, path.elementAt(i+1), path.elementAt(i+2));
+ i+=2;
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (!m_isLine)
+ lineToStencil(firstx, firsty);
+
+ m_item->m_pointstops.append(tess_index);
+
+ m_item->m_offset = m_startindex;
+ m_item->m_count = ( m_index - m_startindex ) - 2;
+ m_startindex = m_index;
+
+ QRectF result;
+ result.setLeft(min_x);
+ result.setRight(max_x);
+ result.setTop(min_y);
+ result.setBottom(max_y);
+
+ if (m_isLine)
+ result.adjust(0,0,1,1);
+
+ return result;
+}
+
+void QD3DDrawHelper::resetMask()
+{
+ m_mask_position.x = m_mask_position.y = QD3D_MASK_MARGIN;
+ m_mask_position.channel = 0;
+ m_mask_offsetX2 = m_mask_offsetY2 = QD3D_MASK_MARGIN;
+}
+
+
+static inline QPainterPath strokeForPath(const QPainterPath &path, const QPen &cpen) {
+ QPainterPathStroker stroker;
+ if (cpen.style() == Qt::CustomDashLine)
+ stroker.setDashPattern(cpen.dashPattern());
+ else
+ stroker.setDashPattern(cpen.style());
+
+ stroker.setCapStyle(cpen.capStyle());
+ stroker.setJoinStyle(cpen.joinStyle());
+ stroker.setMiterLimit(cpen.miterLimit());
+ stroker.setWidth(cpen.widthF());
+
+ QPainterPath stroke = stroker.createStroke(path);
+ stroke.setFillRule(Qt::WindingFill);
+ return stroke;
+}
+
+
+QDirect3DPaintEnginePrivate::~QDirect3DPaintEnginePrivate()
+{
+
+}
+
+void QDirect3DPaintEnginePrivate::updateClipPath(const QPainterPath &path, Qt::ClipOperation op)
+{
+ //#### remove me
+ QRegion r(path.toFillPolygon().toPolygon(), path.fillRule());
+ updateClipRegion(r, op);
+
+/* if (m_draw_helper->needsFlushing())
+ flushBatch();
+
+ if (op == Qt::IntersectClip && !has_clipping)
+ op = Qt::ReplaceClip;
+
+ // switch to paths
+ if (!m_has_complex_clipping) {
+ m_clip_path = QPainterPath();
+ m_clip_path.addRegion(m_clip_region);
+ m_clip_region = QRegion();
+ m_sysclip_path = QPainterPath();
+ m_sysclip_path.addRegion(m_sysclip_region);
+ m_sysclip_region = QRegion();
+ m_has_complex_clipping = true;
+ }
+
+ QPainterPath cpath = m_matrix.map(path);
+
+ QD3DBatchItem *item = &m_batch.items[m_batch.m_item_index++];
+ item->m_info = QD3DBatchItem::BI_COMPLEXCLIP;
+
+ switch (op) {
+ case Qt::UniteClip:
+ has_clipping = true;
+ m_clip_path = m_clip_path.united(cpath);
+ break;
+ case Qt::ReplaceClip:
+ has_clipping = true;
+ m_clip_path = cpath;
+ break;
+ case Qt::NoClip:
+ m_has_complex_clipping = false;
+ has_clipping = false;
+ item->m_info |= QD3DBatchItem::BI_CLEARCLIP;
+ break;
+ default: // intersect clip
+ has_clipping = true;
+ m_clip_path = m_clip_path.intersected(cpath);
+ break;
+ }
+
+ if (!m_sysclip_path.isEmpty()) {
+ item->m_info &= ~QD3DBatchItem::BI_CLEARCLIP;
+ if (has_clipping)
+ m_clip_path = m_clip_path.intersected(m_sysclip_path);
+ else
+ m_clip_path = m_sysclip_path;
+ }
+
+ // update the aliased clipping mask
+ m_draw_helper->setClipPath(m_clip_path, item);
+
+ // update the antialiased clipping mask
+ if (m_draw_helper->needsFlushing())
+ flushBatch();
+
+ QD3DBatchItem *aaitem = &m_batch.items[m_batch.m_item_index++];
+ aaitem->m_info = item->m_info|QD3DBatchItem::BI_AA;
+ m_draw_helper->setClipPath(m_clip_path, aaitem); */
+}
+
+extern QPainterPath qt_regionToPath(const QRegion &region);
+
+void QDirect3DPaintEnginePrivate::updateClipRegion(const QRegion &clipregion, Qt::ClipOperation op)
+{
+ if (m_draw_helper->needsFlushing())
+ flushBatch();
+ if (m_has_complex_clipping) {
+ QPainterPath path = qt_regionToPath(clipregion);
+ updateClipPath(path, op);
+ return;
+ }
+
+ if (op == Qt::IntersectClip && m_clip_region.isEmpty())
+ op = Qt::ReplaceClip;
+
+ QRegion cregion = m_matrix.map(clipregion);
+
+ QD3DBatchItem *item = nextBatchItem();
+ item->m_info &= ~QD3DBatchItem::BI_AA;
+
+ switch (op) {
+ case Qt::UniteClip:
+ m_clip_region = m_clip_region.united(cregion);
+ break;
+ case Qt::ReplaceClip:
+ m_clip_region = cregion;
+ break;
+ case Qt::NoClip:
+ m_clip_region = QRegion();
+ item->m_info |= QD3DBatchItem::BI_CLEARCLIP;
+ break;
+ default: // intersect clip
+ m_clip_region = m_clip_region.intersected(cregion);
+ break;
+ }
+
+ QRegion crgn = m_clip_region;
+ if (!m_sysclip_region.isEmpty()) {
+ item->m_info &= ~QD3DBatchItem::BI_CLEARCLIP;
+ if (!crgn.isEmpty())
+ crgn = crgn.intersected(m_sysclip_region);
+ else
+ crgn = m_sysclip_region;
+ }
+
+ QPainterPath path = qt_regionToPath(crgn);
+ m_draw_helper->setClipPath(path, &item);
+}
+
+void QDirect3DPaintEnginePrivate::updateFont(const QFont &)
+{
+}
+
+void QDirect3DPaintEnginePrivate::setRenderTechnique(RenderTechnique technique)
+{
+ if (m_current_technique != technique) {
+ if (m_current_technique != RT_NoTechnique)
+ m_effect->End();
+
+ if (technique == RT_Aliased) {
+ m_effect->SetTechnique("Aliased");
+ m_effect->Begin(0,D3DXFX_DONOTSAVESTATE);
+ } else if (technique == RT_Antialiased) {
+ m_effect->SetTechnique("Antialiased");
+ m_effect->Begin(0,D3DXFX_DONOTSAVESTATE);
+ }
+ }
+
+ m_current_technique = technique;
+}
+
+/*QPolygonF QDirect3DPaintEnginePrivate::transformedRect(const QRectF &brect) const
+{
+ QPolygonF poly(brect);
+ return m_matrix.map(poly);
+}
+
+QPolygonF QDirect3DPaintEnginePrivate::calcTextureCoords(const QPolygonF &trect) const
+{
+ QPolygonF result(4);
+ QRectF brect = trect.boundingRect();
+ qreal angle = atan(trect.at(0).x() -
+}
+
+QPolygonF QDirect3DPaintEnginePrivate::offsetTextureCoords(const QRectF &brect, const QPolygonF &trect) const
+{
+
+}*/
+
+inline QD3DBatchItem *QDirect3DPaintEnginePrivate::nextBatchItem()
+{
+ if (m_draw_helper->needsFlushing())
+ flushBatch();
+
+ QD3DBatchItem *item = &m_batch.items[m_batch.m_item_index++];
+ item->m_info = m_current_state;
+ item->m_cmode = m_cmode;
+ return item;
+}
+
+qreal calculateAngle(qreal dx, qreal dy)
+{
+ qreal angle;
+
+ if (qFuzzyCompare(dx + 1, 1)) {
+ angle = (dy < 0) ? -M_PI/2 : M_PI/2;
+ } else {
+ angle = atanf(dy/dx);
+ if (dx < 0)
+ angle += M_PI;
+ }
+
+ return angle;
+}
+
+QPolygonF QDirect3DPaintEnginePrivate::brushCoordinates(const QRectF &r, bool stroke, qreal *fd) const
+{
+ QBrush brush;
+ QTransform matrix;
+ Qt::BrushStyle style;
+
+ if (stroke) {
+ brush = m_pen.brush();
+ matrix = m_inv_pen_matrix;
+ style = m_pen_brush_style;
+ } else {
+ brush = m_brush;
+ matrix = m_inv_brush_matrix;
+ style = m_brush_style;
+ }
+
+ QPolygonF bpoly;
+ switch(style) {
+ case Qt::TexturePattern: {
+ QTransform totxcoords;
+ QRectF adj_brect = r.adjusted(-0.5f, -0.5f, -0.5f, -0.5f);
+ totxcoords.scale(1.0f/brush.texture().width(),
+ 1.0f/brush.texture().height());
+ bpoly = matrix.map(QPolygonF(adj_brect));
+ bpoly = totxcoords.map(bpoly);
+ break; }
+ case Qt::LinearGradientPattern: {
+ const QLinearGradient *g = static_cast<const QLinearGradient *>(brush.gradient());
+ QPointF start = g->start();
+ QPointF stop = g->finalStop();
+ qreal dx = stop.x() - start.x();
+ qreal dy = stop.y() - start.y();
+ qreal length = sqrt(dx * dx + dy * dy);
+ qreal angle = calculateAngle(dx, dy);
+ QTransform totxcoords;
+ QRectF adj_brect = r.adjusted(-0.5f, -0.5f, -0.5f, -0.5f);
+ totxcoords.scale(1.0f/length, 1.0f/length);
+ totxcoords.rotateRadians(-angle);
+ totxcoords.translate(-start.x(), -start.y());
+ bpoly = matrix.map(QPolygonF(adj_brect));
+ bpoly = totxcoords.map(bpoly);
+ break; }
+ case Qt::ConicalGradientPattern: {
+ const QConicalGradient *g = static_cast<const QConicalGradient *>(brush.gradient());
+ QPointF center = g->center();
+ qreal angle = g->angle();
+ QTransform totxcoords;
+ totxcoords.rotate(angle);
+ totxcoords.translate(-center.x(), -center.y());
+ bpoly = matrix.map(QPolygonF(r));
+ bpoly = totxcoords.map(bpoly);
+ break; }
+ case Qt::RadialGradientPattern: {
+ const QRadialGradient *g = static_cast<const QRadialGradient *>(brush.gradient());
+ QPointF center = g->center();
+ QPointF focalpoint = g->focalPoint();
+ qreal dx = focalpoint.x() - center.x();
+ qreal dy = focalpoint.y() - center.y();
+ qreal radius = g->radius();
+ *fd = sqrt(dx * dx + dy * dy) / radius;
+ qreal angle = calculateAngle(dx, dy);
+ QTransform totxcoords;
+ totxcoords.scale(1.0f/radius, 1.0f/radius);
+ totxcoords.rotateRadians(-angle);
+ totxcoords.translate(-center.x(), -center.y());
+ bpoly = matrix.map(QPolygonF(r));
+ bpoly = totxcoords.map(bpoly);
+ break; }
+ default: {
+ QTransform totxcoords;
+ QRectF adj_brect = r.adjusted(-0.5f, -0.5f, -0.5f, -0.5f);
+ QPixmap pat = getPattern(style);
+ totxcoords.scale(1.0f/pat.width(),
+ 1.0f/pat.height());
+ bpoly = matrix.map(QPolygonF(adj_brect));
+ bpoly = totxcoords.map(bpoly); }
+ };
+
+ return bpoly;
+}
+
+void QDirect3DPaintEnginePrivate::strokeAliasedPath(QPainterPath path, const QRectF &brect, const QTransform &txform)
+{
+ D3DCOLOR solid_color;
+ QD3DBatchItem *item = nextBatchItem();
+
+ if (!txform.isIdentity())
+ path = txform.map(path);
+
+ QRectF trect;
+ QPolygonF txcoord;
+
+ solid_color = m_pen_color;
+ bool has_complex_brush = false;
+ if (m_pen_brush_style != Qt::SolidPattern) {
+ has_complex_brush = true;
+ item->m_brush = m_pen.brush();
+ item->m_info |= QD3DBatchItem::BI_COMPLEXBRUSH;
+ item->m_opacity = m_opacity;
+ }
+
+ if (m_has_fast_pen) {
+ item->m_info |= QD3DBatchItem::BI_FASTLINE;
+ if (m_pen_brush_style == Qt::SolidPattern) {
+ m_draw_helper->queueAliasedMask(path, &item, solid_color);
+ item->m_info &= ~QD3DBatchItem::BI_MASK; // bypass stencil buffer
+ return;
+ }
+ }
+
+ QRectF txrect = m_draw_helper->queueAliasedMask(path, &item, 0);
+
+ if (has_complex_brush) {
+ trect = brect;
+ txcoord = brushCoordinates(brect, true, &item->m_distance);
+ item->m_info |= QD3DBatchItem::BI_TRANSFORM;
+ item->m_matrix = m_matrix;
+ } else {
+ trect = txrect;
+ static const QPolygonF empty_poly(4);
+ txcoord = empty_poly;
+ }
+
+ m_draw_helper->queueRect(trect, item, solid_color, txcoord);
+}
+
+void QDirect3DPaintEnginePrivate::fillAliasedPath(QPainterPath path, const QRectF &brect, const QTransform &txform)
+{
+ D3DCOLOR solid_color;
+ QD3DBatchItem *item = nextBatchItem();
+
+ if (!txform.isIdentity())
+ path = txform.map(path);
+
+ QRectF trect;
+ QPolygonF txcoord;
+
+ solid_color = m_brush_color;
+ bool has_complex_brush = false;
+ if (m_brush_style != Qt::SolidPattern) {
+ has_complex_brush = true;
+ item->m_brush = m_brush;
+ item->m_info |= QD3DBatchItem::BI_COMPLEXBRUSH;
+ item->m_opacity = m_opacity;
+ }
+
+ QRectF txrect = m_draw_helper->queueAliasedMask(path, &item, 0);
+
+ if (has_complex_brush) {
+ trect = brect;
+ txcoord = brushCoordinates(brect, false, &item->m_distance);
+ item->m_info |= QD3DBatchItem::BI_TRANSFORM;
+ item->m_matrix = m_matrix;
+ } else {
+ trect = txrect;
+ static const QPolygonF empty_poly(4);
+ txcoord = empty_poly;
+ }
+
+ m_draw_helper->queueRect(trect, item, solid_color, txcoord);
+}
+
+void QDirect3DPaintEnginePrivate::fillAntialiasedPath(const QPainterPath &path, const QRectF &brect,
+ const QTransform &txform, bool stroke)
+{
+ D3DCOLOR solid_color;
+ bool winding = (path.fillRule() == Qt::WindingFill);
+ QPolygonF poly;
+ QRectF txrect;
+ QPainterPath tpath;
+
+ if (m_has_aa_fast_pen && stroke) {
+ tpath = txform.map(path);
+ txrect = tpath.controlPointRect();
+ txrect.adjust(-(m_pen_width/2),-(m_pen_width/2), m_pen_width, m_pen_width);
+ } else {
+ poly = path.toFillPolygon(txform);
+ txrect = poly.boundingRect();
+ }
+
+ // brect = approx. bounding rect before transformation
+ // txrect = exact bounding rect after transformation
+ // trect = the rectangle to be drawn
+ // txcoord = the texture coordinates
+ // adj_txrect = adjusted rect to include aliased outline
+
+ bool use_scissor = false;
+ if (txrect.left() < 0) {
+ txrect.adjust(-txrect.left(),0,0,0);
+ use_scissor = true;
+ }
+ if (txrect.top() < 0) {
+ txrect.adjust(0,-txrect.top(),0,0);
+ use_scissor = true;
+ }
+
+ if (!txrect.isValid())
+ return;
+
+ QD3DBatchItem *item = nextBatchItem();
+
+ QRectF adj_txrect = txrect.adjusted(-1,-1,1,1);
+ QRectF trect;
+ QPolygonF txcoord;
+
+ bool has_complex_brush = false;
+ if (stroke) {
+ solid_color = m_pen_color;
+ if (m_pen_brush_style != Qt::SolidPattern) {
+ has_complex_brush = true;
+ item->m_brush = m_pen.brush();
+ }
+ item->m_width = m_pen_width;
+ } else {
+ solid_color = m_brush_color;
+ if (m_brush_style != Qt::SolidPattern) {
+ has_complex_brush = true;
+ item->m_brush = m_brush;
+ }
+ }
+
+ qreal focaldist = 0;
+ if (has_complex_brush) {
+ trect = brect;
+ txcoord = brushCoordinates(brect, stroke, &focaldist);
+ } else {
+ trect = adj_txrect;
+ static const QPolygonF empty_poly(4);
+ txcoord = empty_poly;
+ }
+
+ bool maskfull;
+ item->m_maskpos = m_draw_helper->allocateMaskPosition(txrect, &maskfull);
+ if (maskfull)
+ item->m_info |= QD3DBatchItem::BI_MASKFULL;
+ item->m_distance = focaldist;
+
+ if (winding)
+ item->m_info |= QD3DBatchItem::BI_WINDING;
+
+ if (has_complex_brush) {
+ item->m_info |= QD3DBatchItem::BI_SCISSOR|QD3DBatchItem::BI_COMPLEXBRUSH|
+ QD3DBatchItem::BI_TRANSFORM;
+ item->m_brect = adj_txrect;
+ item->m_matrix = m_matrix;
+ item->m_opacity = m_opacity;
+ }
+ if (use_scissor) {
+ item->m_info |= QD3DBatchItem::BI_MASKSCISSOR;
+ item->m_brect = adj_txrect;
+ }
+
+ if (m_has_aa_fast_pen && stroke) {
+ m_draw_helper->queueAntialiasedLines(tpath, &item, txrect);
+ } else {
+ m_draw_helper->queueAntialiasedMask(poly, &item, txrect);
+ }
+
+ m_draw_helper->queueRect(trect, item, solid_color, txcoord);
+}
+
+QPainterPath QDirect3DPaintEnginePrivate::strokePathFastPen(const QPainterPath &path)
+{
+ QPainterPath result;
+ QBezier beziers[32];
+ for (int i=0; i<path.elementCount(); ++i) {
+ const QPainterPath::Element &e = path.elementAt(i);
+ switch (e.type) {
+ case QPainterPath::MoveToElement:
+ result.moveTo(e.x, e.y);
+ break;
+ case QPainterPath::LineToElement:
+ result.lineTo(e.x, e.y);
+ break;
+
+ case QPainterPath::CurveToElement:
+ {
+ QPointF sp = path.elementAt(i-1);
+ QPointF cp2 = path.elementAt(i+1);
+ QPointF ep = path.elementAt(i+2);
+ i+=2;
+
+ qreal inverseScaleHalf = m_inv_scale / 2;
+ beziers[0] = QBezier::fromPoints(sp, e, cp2, ep);
+ QBezier *b = beziers;
+ while (b >= beziers) {
+ // check if we can pop the top bezier curve from the stack
+ qreal l = qAbs(b->x4 - b->x1) + qAbs(b->y4 - b->y1);
+ qreal d;
+ if (l > m_inv_scale) {
+ d = qAbs( (b->x4 - b->x1)*(b->y1 - b->y2)
+ - (b->y4 - b->y1)*(b->x1 - b->x2) )
+ + qAbs( (b->x4 - b->x1)*(b->y1 - b->y3)
+ - (b->y4 - b->y1)*(b->x1 - b->x3) );
+ d /= l;
+ } else {
+ d = qAbs(b->x1 - b->x2) + qAbs(b->y1 - b->y2) +
+ qAbs(b->x1 - b->x3) + qAbs(b->y1 - b->y3);
+ }
+ if (d < inverseScaleHalf || b == beziers + 31) {
+ // good enough, we pop it off and add the endpoint
+ result.lineTo(b->x4, b->y4);
+ --b;
+ } else {
+ // split, second half of the polygon goes lower into the stack
+ b->split(b+1, b);
+ ++b;
+ }
+ }
+ } // case CurveToElement
+ default:
+ break;
+ } // end of switch
+ }
+ return result;
+}
+
+void QDirect3DPaintEnginePrivate::strokePath(const QPainterPath &path, QRectF brect, bool simple)
+{
+ QTransform txform;
+ QPainterPath tpath;
+
+ if (m_has_fast_pen || m_has_aa_fast_pen) {
+ if (!simple)
+ tpath = strokePathFastPen(path);
+ else
+ tpath = path; //already only lines
+ } else {
+ tpath = strokeForPath(path, m_pen);
+ }
+
+ if (tpath.isEmpty())
+ return;
+
+ //brect is null if the path is not transformed
+ if (brect.isNull())
+ txform = m_matrix;
+
+ if (!brect.isNull()) {
+ // brect is set when the path is transformed already,
+ // this is the case when we have a cosmetic pen.
+ brect.adjust(-(m_pen_width/2),-(m_pen_width/2), m_pen_width, m_pen_width);
+ }
+
+ if (brect.isNull())
+ brect = tpath.controlPointRect();
+ brect.adjust(-m_inv_scale,-m_inv_scale,m_inv_scale,m_inv_scale); //adjust for antialiasing
+
+ if (m_current_state & QD3DBatchItem::BI_AA) {
+ fillAntialiasedPath(tpath, brect, txform, true);
+ } else {
+ strokeAliasedPath(tpath, brect, txform);
+ }
+}
+
+void QDirect3DPaintEnginePrivate::fillPath(const QPainterPath &path, QRectF brect)
+{
+ QTransform txform;
+
+ //brect is null if the path is not transformed
+ if (brect.isNull())
+ txform = m_matrix;
+
+ if (brect.isNull())
+ brect = path.controlPointRect();
+ brect.adjust(-m_inv_scale,-m_inv_scale,m_inv_scale,m_inv_scale); //adjust for antialiasing
+
+ if (m_current_state & QD3DBatchItem::BI_AA) {
+ fillAntialiasedPath(path, brect, txform, false);
+ } else {
+ fillAliasedPath(path, brect, txform);
+ }
+}
+
+
+bool QDirect3DPaintEnginePrivate::init()
+{
+#ifdef QT_DEBUG_D3D_CALLS
+ qDebug() << "QDirect3DPaintEnginePrivate::init()";
+#endif
+
+ m_draw_helper = 0;
+ m_gradient_cache = 0;
+ m_dc = 0;
+ m_dcsurface = 0;
+
+ m_supports_d3d = false;
+ m_current_state = 0;
+ m_in_scene = false;
+ m_has_fast_pen = false;
+ m_has_aa_fast_pen = false;
+ m_has_pen = false;
+ m_has_brush = false;
+ m_pen_color = 0;
+ m_brush_color = 0;
+ m_current_surface = 0;
+ m_batch.m_item_index = 0;
+ m_current_technique = RT_NoTechnique;
+
+ if (!pDirect3DCreate9) {
+ QLibrary d3d_lib(QLatin1String("d3d9.dll"));
+ pDirect3DCreate9 = (PFNDIRECT3DCREATE9) d3d_lib.resolve("Direct3DCreate9");
+ if (!pDirect3DCreate9) {
+ qWarning("QDirect3DPaintEngine: failed to resolve symbols from d3d9.dll.\n"
+ "Make sure you have the DirectX run-time installed.");
+ return false;
+ }
+ }
+
+ if (!pD3DXCreateBuffer || !pD3DXCreateEffect || !pD3DXMatrixOrthoOffCenterLH) {
+ QLibrary d3dx_lib(QLatin1String("d3dx9_32.dll"));
+ pD3DXCreateBuffer = (PFND3DXCREATEBUFFER) d3dx_lib.resolve("D3DXCreateBuffer");
+ pD3DXCreateEffect = (PFND3DXCREATEEFFECT) d3dx_lib.resolve("D3DXCreateEffect");
+ pD3DXMatrixOrthoOffCenterLH = (PFND3DXMATRIXORTHOOFFCENTERLH)
+ d3dx_lib.resolve("D3DXMatrixOrthoOffCenterLH");
+ if (!(pD3DXCreateBuffer && pD3DXCreateEffect && pD3DXMatrixOrthoOffCenterLH)) {
+ qWarning("QDirect3DPaintEngine: failed to resolve symbols from d3dx9_32.dll.\n"
+ "Make sure you have the DirectX run-time installed.");
+ return false;
+ }
+ }
+
+ if (!m_d3d_object) {
+ m_d3d_object = pDirect3DCreate9(D3D_SDK_VERSION);
+ if (!m_d3d_object) {
+ qWarning("QDirect3DPaintEngine: failed to create Direct3D object.\n"
+ "Direct3D support in Qt will be disabled.");
+ return false;
+ }
+ }
+
+ m_supports_d3d = testCaps();
+ if (!m_supports_d3d)
+ return false;
+
+ m_surface_manager.init(m_d3d_object);
+ m_d3d_device = m_surface_manager.device();
+
+ if (!m_d3d_device)
+ return false;
+
+ /* load shaders */
+ QFile file(QLatin1String(":/qpaintengine_d3d.fx"));
+ QByteArray fxFile;
+ if (file.open(QFile::ReadOnly))
+ fxFile = file.readAll();
+
+ if (fxFile.size() > 0) {
+ LPD3DXBUFFER compout;
+ pD3DXCreateBuffer(4096, &compout);
+ DWORD dwShaderFlags = D3DXFX_NOT_CLONEABLE|D3DXFX_DONOTSAVESTATE|D3DXSHADER_OPTIMIZATION_LEVEL3;
+ if(FAILED(pD3DXCreateEffect(m_d3d_device, fxFile.constData(), fxFile.size(),
+ NULL, NULL, dwShaderFlags, NULL, &m_effect, &compout))) {
+ qWarning("QDirect3DPaintEngine: failed to compile effect file");
+ if (compout)
+ qWarning((char *)compout->GetBufferPointer());
+ m_supports_d3d = false;
+ return false;
+ }
+ if (m_effect) {
+ m_statemanager = new QD3DStateManager(m_d3d_device, m_effect);
+ m_effect->SetStateManager(m_statemanager);
+ m_draw_helper = new QD3DDrawHelper(this);
+ initDevice();
+ m_gradient_cache = new QD3DGradientCache(m_d3d_device);
+ }
+ } else {
+ return false;
+ }
+
+ return true;
+}
+
+QPixmap QDirect3DPaintEnginePrivate::getPattern(Qt::BrushStyle style) const
+{
+ if (!m_patterns.contains(style)) {
+ QImage img(16,16,QImage::Format_ARGB32);
+ img.fill(0);
+ QPainter p(&img);
+ p.setBrush(QBrush(Qt::white, style));
+ p.setPen(Qt::NoPen);
+ p.drawRect(0,0,16,16);
+ p.end();
+ QPixmap pattern(QPixmap::fromImage(img));
+ QDirect3DPaintEnginePrivate *ct = const_cast<QDirect3DPaintEnginePrivate *>(this);
+ ct->verifyTexture(pattern);
+ ct->m_patterns.insert(style, pattern);
+ }
+
+ return m_patterns.value(style);
+}
+
+bool QDirect3DPaintEnginePrivate::testCaps()
+{
+ D3DCAPS9 caps;
+ if (FAILED(m_d3d_object->GetDeviceCaps(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, &caps)))
+ return false;
+
+ if ((caps.PresentationIntervals & D3DPRESENT_INTERVAL_IMMEDIATE)
+ && (caps.DevCaps & D3DDEVCAPS_PUREDEVICE)
+ && (caps.RasterCaps & D3DPRASTERCAPS_SCISSORTEST)
+ && (caps.StencilCaps & D3DSTENCILCAPS_TWOSIDED))
+ return true;
+#if 0
+ qDebug() << "Direct3D caps:";
+ qDebug() << "D3DPRESENT_INTERVAL_IMMEDIATE:" << ((caps.PresentationIntervals & D3DPRESENT_INTERVAL_IMMEDIATE) != 0);
+ qDebug() << "D3DDEVCAPS_PUREDEVICE:" << ((caps.DevCaps & D3DDEVCAPS_PUREDEVICE) != 0);
+ qDebug() << "D3DPRASTERCAPS_SCISSORTEST:" << ((caps.RasterCaps & D3DPRASTERCAPS_SCISSORTEST) != 0);
+ qDebug() << "D3DSTENCILCAPS_TWOSIDED:" << ((caps.StencilCaps & D3DSTENCILCAPS_TWOSIDED) != 0);
+#endif
+ return false;
+}
+
+void QDirect3DPaintEnginePrivate::initDevice()
+{
+ m_statemanager->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE);
+ m_statemanager->SetRenderState(D3DRS_ZENABLE, D3DZB_FALSE);
+ m_statemanager->SetRenderState(D3DRS_ZWRITEENABLE, FALSE);
+ m_statemanager->SetRenderState(D3DRS_LIGHTING, FALSE);
+ m_statemanager->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE);
+ m_statemanager->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
+ m_statemanager->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_DESTALPHA);
+}
+
+void QDirect3DPaintEnginePrivate::updatePen(const QPen &pen)
+{
+#ifdef QT_DEBUG_D3D_CALLS
+ qDebug() << "QDirect3DPaintEngine::updatePen";
+#endif
+ m_pen = pen;
+ m_has_cosmetic_pen = false;
+ m_has_pen = (m_pen.style() != Qt::NoPen);
+ if (m_has_pen) {
+ m_pen_brush_style = m_pen.brush().style();
+
+ if (m_pen_brush_style >= Qt::SolidPattern && m_pen_brush_style <= Qt::DiagCrossPattern) {
+ int a, r, g, b;
+ m_pen.color().getRgb(&r, &g, &b, &a);
+ m_pen_color = D3DCOLOR_ARGB((int)(a * m_opacity),r,g,b);
+ } else {
+ m_pen_color = m_opacity_color;
+ }
+
+ m_has_cosmetic_pen = m_pen.isCosmetic();
+
+ if (m_pen_brush_style != Qt::NoBrush &&
+ m_pen_brush_style != Qt::SolidPattern) {
+ bool ok;
+ m_inv_pen_matrix = m_pen.brush().transform().inverted(&ok);
+ if (!ok)
+ qWarning() << "QDirect3DPaintEngine: No inverse matix for pen brush matrix.";
+ }
+
+ m_pen_width = m_pen.widthF();
+ if (m_pen_width == 0.0f)
+ m_pen_width = 1.0f;
+ }
+}
+
+void QDirect3DPaintEnginePrivate::updateBrush(const QBrush &brush)
+{
+#ifdef QT_DEBUG_D3D_CALLS
+ qDebug() << "QDirect3DPaintEngine::updateBrush";
+#endif
+ m_brush = brush;
+ m_brush_style = m_brush.style();
+ m_has_brush = (m_brush_style != Qt::NoBrush);
+ if (m_has_brush) {
+ if (m_brush_style >= Qt::SolidPattern && m_brush_style <= Qt::DiagCrossPattern) {
+ int a, r, g, b;
+ m_brush.color().getRgb(&r, &g, &b, &a);
+ m_brush_color = D3DCOLOR_ARGB((int)(a * m_opacity),r,g,b);
+ } else {
+ m_brush_color = m_opacity_color;
+ }
+
+ if (m_brush_style != Qt::SolidPattern) {
+ bool ok;
+ m_inv_brush_matrix = (m_brush.transform() * m_brush_origin).inverted(&ok);
+ if (!ok)
+ qWarning() << "QDirect3DPaintEngine: No inverse matix for brush matrix.";
+
+ // make sure the texture is loaded as a texture
+ if (m_brush_style == Qt::TexturePattern)
+ verifyTexture(m_brush.texture());
+
+
+ }
+ }
+}
+
+void QDirect3DPaintEnginePrivate::updateTransform(const QTransform &matrix)
+{
+#ifdef QT_DEBUG_D3D_CALLS
+ qDebug() << "QDirect3DPaintEngine::updateTransform";
+#endif
+ m_matrix = matrix;
+ m_inv_scale = qMax(1 / qMax( qMax(qAbs(m_matrix.m11()), qAbs(m_matrix.m22())),
+ qMax(qAbs(m_matrix.m12()), qAbs(m_matrix.m21())) ), 0.0001);
+ m_txop = matrix.type();
+}
+
+int QDirect3DPaintEnginePrivate::flushAntialiased(int offset)
+{
+ // fills the mask (returns number of items added to the mask)
+ int newoffset = m_draw_helper->drawAntialiasedMask(offset, m_batch.m_item_index);
+
+ // set the render target to the current output surface
+ if (FAILED(m_d3d_device->SetRenderTarget(0, m_current_surface)))
+ qWarning() << "QDirect3DPaintEngine: SetRenderTarget failed!";
+
+ // draw the bounding boxes (using the mask generated by drawAntialiasedMask)
+ for (int i=offset; i<newoffset; ++i) {
+ QD3DBatchItem *item = &(m_batch.items[i]);
+ int pass = (item->m_info & QD3DBatchItem::BI_COMPLEXBRUSH) ? PASS_AA_DRAW : PASS_AA_DRAW_DIRECT;
+ m_statemanager->beginPass(pass);
+ prepareItem(item);
+ if (item->m_info & QD3DBatchItem::BI_BRECT)
+ m_draw_helper->drawAntialiasedBoundingRect(item);
+ cleanupItem(item);
+ }
+
+ m_statemanager->endPass();
+
+ return newoffset;
+}
+
+bool QDirect3DPaintEnginePrivate::prepareBatch(QD3DBatchItem *item, int offset)
+{
+ if (item->m_info & QD3DBatchItem::BI_CLIP) {
+ setRenderTechnique(RT_Aliased);
+ if (item->m_info & QD3DBatchItem::BI_CLEARCLIP) {
+ m_d3d_device->Clear(0, 0, D3DCLEAR_ZBUFFER, 0, 0.0f, 0);
+ return true;
+ }
+
+ m_draw_helper->drawAliasedMask(offset);
+ m_d3d_device->Clear(0, 0, D3DCLEAR_ZBUFFER, 0, 1.0f, 0);
+ if (item->m_info & QD3DBatchItem::BI_BRECT) {
+ m_statemanager->beginPass(PASS_STENCIL_CLIP);
+ m_draw_helper->drawAliasedBoundingRect(item);
+ m_statemanager->endPass();
+ }
+
+ return true;
+ }
+
+ if (item->m_info & QD3DBatchItem::BI_AA) {
+ setRenderTechnique(RT_Antialiased);
+ } else {
+ setRenderTechnique(RT_Aliased);
+ }
+
+ return false;
+}
+
+void QDirect3DPaintEnginePrivate::prepareItem(QD3DBatchItem *item) {
+ // pixmap
+ int brushmode = 0;
+ m_statemanager->startStateBlock();
+ if ((item->m_info & QD3DBatchItem::BI_PIXMAP) || (item->m_info & QD3DBatchItem::BI_IMAGE)) {
+ QRasterPixmapData *data = static_cast<QRasterPixmapData*>(item->m_pixmap.data);
+ IDirect3DTexture9 *tex = (item->m_info & QD3DBatchItem::BI_PIXMAP) ?
+ data->texture : item->m_texture;
+ m_statemanager->setTexture(tex);
+ brushmode = 5;
+ }
+
+ if (item->m_info & QD3DBatchItem::BI_AA) {
+ m_statemanager->setMaskChannel(item->m_maskpos.channel);
+ m_statemanager->setMaskOffset(item->m_xoffset, item->m_yoffset);
+ }
+
+ if (item->m_info & QD3DBatchItem::BI_COMPLEXBRUSH) {
+ const QBrush brush = item->m_brush;
+ switch (brush.style()) {
+ case Qt::TexturePattern: {
+ QRasterPixmapData *data = static_cast<QRasterPixmapData*>(brush.texture().data);
+ m_statemanager->setTexture(data->texture, QGradient::RepeatSpread);
+ brushmode = 1;
+ break;
+ }
+ case Qt::LinearGradientPattern:
+ m_statemanager->setTexture(m_gradient_cache->
+ getBuffer(brush.gradient()->stops(), item->m_opacity),
+ brush.gradient()->spread());
+ brushmode = 2;
+ break;
+ case Qt::ConicalGradientPattern:
+ m_statemanager->setTexture(m_gradient_cache->
+ getBuffer(brush.gradient()->stops(), item->m_opacity),
+ brush.gradient()->spread());
+ brushmode = 3;
+ break;
+ case Qt::RadialGradientPattern:
+ m_statemanager->setTexture(m_gradient_cache->
+ getBuffer(brush.gradient()->stops(), item->m_opacity),
+ brush.gradient()->spread());
+ m_statemanager->setFocalDistance(item->m_distance);
+ brushmode = 4;
+ break;
+ default: {
+ QRasterPixmapData *data = static_cast<QRasterPixmapData*>(getPattern(brush.style()).data);
+ m_statemanager->setTexture(data->texture, QGradient::RepeatSpread);
+ brushmode = 5;
+ }
+ };
+ }
+
+ if (item->m_info & QD3DBatchItem::BI_TRANSFORM) {
+ m_statemanager->setTransformation(&item->m_matrix);
+ } else {
+ m_statemanager->setTransformation();
+ }
+
+ m_statemanager->setBrushMode(brushmode);
+ setCompositionMode(item->m_cmode);
+ m_statemanager->endStateBlock();
+}
+
+
+void QDirect3DPaintEnginePrivate::releaseDC()
+{
+ if (m_dc) {
+ m_dcsurface->ReleaseDC(m_dc);
+ m_dcsurface = 0;
+ m_dc = 0;
+ }
+}
+
+
+void QDirect3DPaintEnginePrivate::cleanupItem(QD3DBatchItem *item)
+{
+ if (item->m_info & QD3DBatchItem::BI_PIXMAP)
+ item->m_pixmap = QPixmap();
+ item->m_brush = QBrush();
+}
+
+void QDirect3DPaintEnginePrivate::verifyTexture(const QPixmap &pm)
+{
+ QRasterPixmapData *pmData = static_cast<QRasterPixmapData*>(pm.data);
+ if (!pmData->texture) {
+ QImage im = pmData->image;
+ // bitmaps are drawn with the current pen color
+ if (im.depth() == 1) {
+ QVector<QRgb> colors(2);
+ colors[0] = 0;
+ colors[1] = m_pen.color().rgba();
+ im.setColorTable(colors);
+ }
+ im = im.convertToFormat(QImage::Format_ARGB32);
+ if (FAILED(m_d3d_device->CreateTexture(im.width(), im.height(), 1, 0,
+ D3DFMT_A8R8G8B8, D3DPOOL_MANAGED, &pmData->texture, 0)))
+ {
+ qWarning("QDirect3DPaintEngine: unable to create Direct3D texture from pixmap.");
+ return;
+ }
+ D3DLOCKED_RECT rect;
+ if (FAILED(pmData->texture->LockRect(0, &rect, 0, 0))) {
+ qDebug() << "QDirect3DPaintEngine: unable to lock texture rect.";
+ return;
+ }
+ DWORD *dst = (DWORD *) rect.pBits;
+ DWORD *src = (DWORD *) im.scanLine(0);
+
+ Q_ASSERT((rect.Pitch/4) == (im.bytesPerLine()/4));
+ memcpy(dst, src, rect.Pitch*im.height());
+ pmData->texture->UnlockRect(0);
+ }
+}
+
+bool QDirect3DPaintEnginePrivate::isFastRect(const QRectF &rect)
+{
+ if (m_matrix.type() < QTransform::TxRotate) {
+ QRectF r = m_matrix.mapRect(rect);
+ return r.topLeft().toPoint() == r.topLeft()
+ && r.bottomRight().toPoint() == r.bottomRight();
+ }
+
+ return false;
+}
+
+void QDirect3DPaintEnginePrivate::setCompositionMode(QPainter::CompositionMode mode)
+{
+ switch(mode) {
+ case QPainter::CompositionMode_SourceOver:
+ default:
+ m_statemanager->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
+ m_statemanager->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);
+ };
+}
+
+void QDirect3DPaintEnginePrivate::cleanup()
+{
+ // clean batch
+ for(int i=0; i<QD3D_BATCH_SIZE; ++i) {
+ m_batch.items[i].m_brush = QBrush();
+ m_batch.items[i].m_pixmap = QPixmap();
+ }
+
+ m_surface_manager.cleanup();
+ m_patterns.clear();
+
+ delete m_gradient_cache;
+ delete m_draw_helper;
+
+ if (m_effect)
+ m_effect->Release();
+
+ if (m_d3d_object)
+ m_d3d_object->Release();
+
+ m_effect = 0;
+ m_d3d_object = 0;
+ m_gradient_cache = 0;
+ m_draw_helper = 0;
+}
+
+void QDirect3DPaintEnginePrivate::flushAliased(QD3DBatchItem *item, int offset)
+{
+ m_draw_helper->drawAliasedMask(offset);
+
+ if (item->m_info & QD3DBatchItem::BI_BRECT) {
+ int pass = (item->m_info & QD3DBatchItem::BI_MASK) ? PASS_STENCIL_DRAW_DIRECT : PASS_STENCIL_NOSTENCILCHECK_DIRECT;
+ if (item->m_info & (QD3DBatchItem::BI_COMPLEXBRUSH|QD3DBatchItem::BI_IMAGE|QD3DBatchItem::BI_PIXMAP) )
+ pass = (item->m_info & QD3DBatchItem::BI_MASK) ? PASS_STENCIL_DRAW : PASS_STENCIL_NOSTENCILCHECK;
+ m_statemanager->beginPass(pass);
+ prepareItem(item);
+ m_draw_helper->drawAliasedBoundingRect(item);
+ cleanupItem(item);
+ m_statemanager->endPass();
+ }
+}
+
+void QDirect3DPaintEnginePrivate::flushText(QD3DBatchItem *item, int)
+{
+ prepareItem(item);
+ m_statemanager->setTexture(item->m_texture);
+ m_statemanager->setBrushMode(1);
+// m_statemanager->SetRenderState(D3DRS_BLENDFACTOR, item->m_brush.color().rgba());
+ m_statemanager->beginPass(m_cleartype_text ? PASS_CLEARTYPE_TEXT : PASS_TEXT);
+ m_draw_helper->drawTextItem(item);
+ m_statemanager->endPass();
+ cleanupItem(item);
+}
+
+void QDirect3DPaintEnginePrivate::flushLines(QD3DBatchItem *item, int)
+{
+ m_draw_helper->drawAliasedLines(item);
+
+ if (item->m_info & QD3DBatchItem::BI_BRECT) {
+ int pass = (item->m_info & QD3DBatchItem::BI_COMPLEXBRUSH) ? PASS_STENCIL_DRAW : PASS_STENCIL_DRAW_DIRECT;
+ m_statemanager->beginPass(pass);
+ prepareItem(item);
+ m_draw_helper->drawAliasedBoundingRect(item);
+ cleanupItem(item);
+ m_statemanager->endPass();
+ }
+}
+
+void QDirect3DPaintEnginePrivate::flushBatch()
+{
+// static int dbgcounter = 0;
+// ++dbgcounter;
+// qDebug() << " -> flush" << dbgcounter;
+
+ int offset = 0;
+ m_draw_helper->unlockVertexBuffer();
+ releaseDC();
+
+ // iterate over all items in the batch
+ while (offset != m_batch.m_item_index) {
+ QD3DBatchItem *item = &(m_batch.items[offset]);
+
+ if (prepareBatch(item, offset)) {
+ ++offset;
+ continue;
+ }
+
+ if (item->m_info & QD3DBatchItem::BI_FASTLINE) {
+ flushLines(item, offset++);
+ } else if (item->m_info & QD3DBatchItem::BI_AA) {
+ offset = flushAntialiased(offset);
+ } else if (item->m_info & QD3DBatchItem::BI_TEXT) {
+ flushText(item, offset++);
+ } else {
+ flushAliased(item, offset++);
+ }
+ }
+
+ // reset batch
+ m_batch.m_item_index = 0;
+
+ // release doomed textures
+ for (int i=0; i<qd3d_release_list.size(); ++i)
+ qd3d_release_list.at(i)->Release();
+ qd3d_release_list.clear();
+}
+
+QDirect3DPaintEngine::QDirect3DPaintEngine()
+ : QPaintEngine(*(new QDirect3DPaintEnginePrivate),
+ PaintEngineFeatures(AllFeatures & ~ObjectBoundingModeGradients))
+{ }
+
+QDirect3DPaintEngine::~QDirect3DPaintEngine()
+{
+}
+
+bool QDirect3DPaintEngine::begin(QPaintDevice *device)
+{
+#ifdef QT_DEBUG_D3D_CALLS
+ qDebug() << "QDirect3DPaintEngine::begin";
+#endif
+ Q_D(QDirect3DPaintEngine);
+ setActive(true);
+
+ QSize old_size = d->m_surface_size;
+ d->m_surface_size = QRect(0, 0, device->width(), device->height()).size();
+
+ d->m_current_state = 0;
+ d->m_inv_scale = 1;
+ d->m_opacity = 1.0f;
+ d->m_opacity_color = D3DCOLOR_ARGB(255,255,255,255);
+ d->m_matrix = QTransform();
+ d->m_brush_origin = QTransform();
+ d->m_txop = QTransform::TxNone;
+ d->m_cmode = QPainter::CompositionMode_SourceOver;
+
+ Q_ASSERT(device && device->devType() == QInternal::Widget);
+ if (d->m_d3d_device == 0) {
+ qWarning() << "QDirect3DPaintEngine: No Device!";
+ return false;
+ }
+
+ d->m_cleartype_text = false;
+// QT_WA({
+// UINT result;
+// BOOL ok;
+// ok = SystemParametersInfoW(SPI_GETFONTSMOOTHINGTYPE, 0, &result, 0);
+// if (ok)
+// d->m_cleartype_text = (result == FE_FONTSMOOTHINGCLEARTYPE);
+// }, {
+// UINT result;
+// BOOL ok;
+// ok = SystemParametersInfoA(SPI_GETFONTSMOOTHINGTYPE, 0, &result, 0);
+// if (ok)
+// d->m_cleartype_text = (result == FE_FONTSMOOTHINGCLEARTYPE);
+// });
+
+ d->m_surface_manager.setPaintDevice(device);
+ int status = d->m_surface_manager.status();
+ if (status & QD3DSurfaceManager::NeedsResetting) {
+ d->m_effect->OnLostDevice();
+ d->m_draw_helper->beforeReset();
+ d->m_statemanager->reset();
+ d->m_surface_manager.reset();
+ d->m_draw_helper->afterReset();
+ d->m_effect->OnResetDevice();
+ d->initDevice();
+ }
+
+ LPDIRECT3DSURFACE9 newsurface = d->m_surface_manager.renderTarget();
+ if (d->m_current_surface != newsurface) {
+ d->m_current_surface = newsurface;
+ if (FAILED(d->m_d3d_device->SetRenderTarget(0, newsurface)))
+ qWarning() << "QDirect3DPaintEngine: SetRenderTarget failed!";
+ }
+
+ status = d->m_surface_manager.status();
+ if (status & QD3DSurfaceManager::MaxSizeChanged) {
+ QSize maxsize = d->m_surface_manager.maxSize();
+ d->m_draw_helper->setMaskSize(maxsize);
+ int masksize[2] = {maxsize.width(), maxsize.height()};
+ d->m_effect->SetIntArray("g_mMaskSize", masksize, 2);
+ }
+
+ if (old_size != d->m_surface_size) {
+ D3DXMATRIX projMatrix;
+ pD3DXMatrixOrthoOffCenterLH(&projMatrix, 0, d->m_surface_size.width(), d->m_surface_size.height(), 0, 0.0f, 1.0f);
+ d->m_statemanager->setProjection(&projMatrix);
+ }
+
+ if (!d->m_in_scene) {
+ if (FAILED(d->m_d3d_device->BeginScene())) {
+ qWarning() << "QDirect3DPaintEngine: BeginScene() failed.";
+ return false;
+ }
+ QWidget *widget = static_cast<QWidget *>(device);
+ if (widget->autoFillBackground() == true) {
+ QColor color = widget->palette().brush(widget->backgroundRole()).color();
+ RECT rect = {0, 0, widget->width(), widget->height()};
+ d->m_d3d_device->ColorFill(d->m_current_surface, &rect,
+ D3DCOLOR_ARGB(color.alpha(), color.red(), color.green(), color.blue()));
+ }
+ d->m_in_scene = true;
+ }
+
+ // set system clip
+ d->m_clipping_enabled = false;
+ d->m_has_complex_clipping = false;
+
+ d->m_sysclip_region = systemClip();
+ QVector<QRect> rects = d->m_sysclip_region.rects();
+ if (rects.count() == 1 && rects.at(0).size() == d->m_surface_size)
+ d->m_sysclip_region = QRegion();
+
+ d->updateClipRegion(QRegion(), Qt::NoClip);
+
+ return true;
+}
+
+void QDirect3DPaintEngine::drawEllipse(const QRectF &rect)
+{
+#ifdef QT_DEBUG_D3D_CALLS
+ qDebug() << "QDirect3DPaintEngine::drawEllipse (float)";
+#endif
+ QPaintEngine::drawEllipse(rect);
+}
+
+void QDirect3DPaintEngine::drawEllipse(const QRect &rect)
+{
+#ifdef QT_DEBUG_D3D_CALLS
+ qDebug() << "QDirect3DPaintEngine::drawEllipse";
+#endif
+ QPaintEngine::drawEllipse(rect);
+}
+
+void QDirect3DPaintEngine::drawImage(const QRectF &r, const QImage &image, const QRectF &sr,
+ Qt::ImageConversionFlags)
+{
+#ifdef QT_DEBUG_D3D_CALLS
+ qDebug() << "QDirect3DPaintEngine::drawImage";
+#endif
+
+ Q_D(QDirect3DPaintEngine);
+ int width = image.width();
+ int height = image.height();
+
+ // transform rectangle
+ QPolygonF txrect(QRectF(sr.left() / width, sr.top() / height,
+ sr.width() / width, sr.height() / height));
+
+ QD3DBatchItem *item = d->nextBatchItem();
+ item->m_info = QD3DBatchItem::BI_IMAGE | QD3DBatchItem::BI_TRANSFORM;
+ item->m_texture = qd3d_image_cache()->lookup(d->m_d3d_device, image);
+ item->m_matrix = d->m_matrix;
+ d->m_draw_helper->queueRect(r.adjusted(-0.5f,-0.5f,-0.5f,-0.5f), item, d->m_opacity_color, txrect);
+}
+
+void QDirect3DPaintEngine::drawLines(const QLineF *lines, int lineCount)
+{
+#ifdef QT_DEBUG_D3D_CALLS
+ qDebug() << "QDirect3DPaintEngine::drawLines (float)";
+#endif
+ Q_D(QDirect3DPaintEngine);
+
+ if (!d->m_has_pen)
+ return;
+
+ if (d->m_has_fast_pen && (d->m_pen_brush_style == Qt::SolidPattern)) {
+ QD3DBatchItem *item = d->nextBatchItem();
+ if (d->m_pen.isCosmetic())
+ item->m_info |= QD3DBatchItem::BI_COSMETICPEN;
+ item->m_info |= QD3DBatchItem::BI_TRANSFORM;
+ item->m_matrix = d->m_matrix;
+ d->m_draw_helper->queueAliasedLines(lines, lineCount, &item);
+ } else {
+ QRectF brect;
+ QPainterPath path;
+
+ // creates a path with the lines
+ path.moveTo(lines[0].x1(), lines[0].y1());
+ qreal lastx = lines[0].x2();
+ qreal lasty = lines[0].y2();
+ path.lineTo(lastx, lasty);
+
+ for (int i=1; i<lineCount; ++i) {
+ qreal x = lines[i].x1();
+ qreal y = lines[i].y1();
+ if (lastx != x || lasty != y) {
+ path.moveTo(x, y);
+ }
+ path.lineTo(lines[i].x2(), lines[i].y2());
+ }
+
+ if (d->m_has_cosmetic_pen) {
+ brect = path.controlPointRect();
+ path = d->m_matrix.map(path);
+ }
+
+ d->strokePath(path, brect, true);
+ }
+}
+
+void QDirect3DPaintEngine::drawLines(const QLine *lines, int lineCount)
+{
+#ifdef QT_DEBUG_D3D_CALLS
+ qDebug() << "QDirect3DPaintEngine::drawLines";
+#endif
+ QPaintEngine::drawLines(lines, lineCount);
+}
+
+void QDirect3DPaintEngine::drawPath(const QPainterPath &path)
+{
+#ifdef QT_DEBUG_D3D_CALLS
+ qDebug() << "QDirect3DPaintEngine::drawPath";
+#endif
+ Q_D(QDirect3DPaintEngine);
+
+ if (path.isEmpty())
+ return;
+
+ QRectF brect;
+ QPainterPath tpath;
+
+ if (d->m_has_cosmetic_pen) {
+ brect = path.controlPointRect();
+ tpath = d->m_matrix.map(path);
+ } else {
+ tpath = path;
+ }
+
+ if (d->m_has_brush)
+ d->fillPath(tpath, brect);
+
+ if (d->m_has_pen)
+ d->strokePath(tpath, brect);
+}
+
+
+QPointF QDirect3DPaintEnginePrivate::transformPoint(const QPointF &p, qreal *w) const
+{
+ (*w) = 1.0f;
+ qreal fx = p.x();
+ qreal fy = p.y();
+ qreal nx = m_matrix.m11()*fx + m_matrix.m21()*fy + m_matrix.m31();
+ qreal ny = m_matrix.m12()*fx + m_matrix.m22()*fy + m_matrix.m32();
+ if (!m_matrix.isAffine()) {
+ *w = m_matrix.m13()*fx + m_matrix.m23()*fy + m_matrix.m33();
+ //*w = 1/(*w);
+ nx = nx/(*w);
+ ny = ny/(*w);
+ }
+ return QPointF(nx, ny);
+}
+
+void QDirect3DPaintEngine::drawPixmap(const QRectF &r, const QPixmap &pm, const QRectF &sr)
+{
+#ifdef QT_DEBUG_D3D_CALLS
+ qDebug() << "QDirect3DPaintEngine::drawPixmap";
+#endif
+ Q_D(QDirect3DPaintEngine);
+
+ if (d->m_draw_helper->needsFlushing())
+ d->flushBatch();
+
+ int width = pm.width();
+ int height = pm.height();
+
+ // transform rectangle
+ QPolygonF txrect(QRectF(sr.left() / width, sr.top() / height,
+ sr.width() / width, sr.height() / height));
+
+ QD3DBatchItem *item = d->nextBatchItem();
+ item->m_info = QD3DBatchItem::BI_PIXMAP|QD3DBatchItem::BI_TRANSFORM;
+
+ item->m_pixmap = pm;
+ d->verifyTexture(item->m_pixmap);
+
+ item->m_matrix = d->m_matrix;
+ d->m_draw_helper->queueRect(r.adjusted(-0.5f,-0.5f,-0.5f,-0.5f), item, d->m_opacity_color, txrect);
+}
+
+void QDirect3DPaintEngine::drawPoints(const QPointF *points, int pointCount)
+{
+#ifdef QT_DEBUG_D3D_CALLS
+ qDebug() << "QDirect3DPaintEngine::drawPoints (float)";
+#endif
+ QPaintEngine::drawPoints(points, pointCount);
+}
+
+void QDirect3DPaintEngine::drawPoints(const QPoint *points, int pointCount)
+{
+#ifdef QT_DEBUG_D3D_CALLS
+ qDebug() << "QDirect3DPaintEngine::drawPoints";
+#endif
+ QPaintEngine::drawPoints(points, pointCount);
+}
+
+void QDirect3DPaintEngine::drawPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode)
+{
+#ifdef QT_DEBUG_D3D_CALLS
+ qDebug() << "QDirect3DPaintEngine::drawPolygon";
+#endif
+ Q_D(QDirect3DPaintEngine);
+
+ if (d->m_has_brush && mode != PolylineMode) {
+ QPainterPath path;
+ path.setFillRule(mode == WindingMode ? Qt::WindingFill : Qt::OddEvenFill);
+ path.moveTo(points[0]);
+ for (int i=1; i<pointCount; ++i)
+ path.lineTo(points[i]);
+ if (path.isEmpty())
+ return;
+ d->fillPath(path, QRectF());
+ }
+
+ if (d->m_has_pen) {
+ QPainterPath path(points[0]);
+ for (int i = 1; i < pointCount; ++i)
+ path.lineTo(points[i]);
+ if (mode != PolylineMode)
+ path.lineTo(points[0]);
+
+ if (path.isEmpty())
+ return;
+ QRectF brect;
+ if (d->m_has_cosmetic_pen) {
+ brect = path.controlPointRect();
+ path = d->m_matrix.map(path);
+ }
+
+ d->strokePath(path, brect);
+ }
+}
+
+void QDirect3DPaintEngine::drawPolygon(const QPoint *points, int pointCount, PolygonDrawMode mode)
+{
+#ifdef QT_DEBUG_D3D_CALLS
+ qDebug() << "QDirect3DPaintEngine::drawPolygon";
+#endif
+ QPaintEngine::drawPolygon(points, pointCount, mode);
+}
+
+void QDirect3DPaintEngine::drawRects(const QRectF *rects, int rectCount)
+{
+ Q_D(QDirect3DPaintEngine);
+#ifdef QT_DEBUG_D3D_CALLS
+ qDebug() << "QDirect3DPaintEngine::drawRects (float)";
+#endif
+ for (int i=0; i<rectCount; ++i) {
+ if ((d->m_brush_style == Qt::SolidPattern) &&
+ (!(d->m_current_state & QD3DBatchItem::BI_AA) || d->isFastRect(rects[i]))) {
+ QD3DBatchItem *item = d->nextBatchItem();
+ item->m_info |= QD3DBatchItem::BI_TRANSFORM;
+ item->m_info &= ~QD3DBatchItem::BI_AA;
+ item->m_matrix = d->m_matrix;
+ const QRectF rect = rects[i];
+ d->m_draw_helper->queueRect(rect, item, d->m_brush_color);
+
+ if (d->m_has_pen) {
+ if (d->m_has_fast_pen && (d->m_pen_brush_style == Qt::SolidPattern)) {
+ QLineF lines[4];
+ qreal x1 = rect.x();
+ qreal y1 = rect.y();
+ qreal x2 = rect.width() + x1;
+ qreal y2 = rect.height() + y1;
+ lines[0] = QLineF(x1, y1, x2, y1);
+ lines[1] = QLineF(x2, y1, x2, y2);
+ lines[2] = QLineF(x2, y2, x1, y2);
+ lines[3] = QLineF(x1, y2, x1, y1);
+ QD3DBatchItem *item = d->nextBatchItem();
+ if (d->m_pen.isCosmetic())
+ item->m_info |= QD3DBatchItem::BI_COSMETICPEN;
+ item->m_info |= QD3DBatchItem::BI_TRANSFORM;
+ item->m_matrix = d->m_matrix;
+ d->m_draw_helper->queueAliasedLines(lines, 4, &item);
+ } else {
+ QPainterPath path;
+ QRectF brect;
+
+ path.addRect(rects[i]);
+ if (d->m_has_cosmetic_pen) {
+ brect = path.controlPointRect();
+ path = d->m_matrix.map(path);
+ }
+
+ d->strokePath(path, brect, true);
+ }
+ }
+ } else {
+ QPainterPath path;
+ QRectF brect;
+
+ path.addRect(rects[i]);
+ if (d->m_has_cosmetic_pen) {
+ brect = path.controlPointRect();
+ path = d->m_matrix.map(path);
+ }
+
+ if (d->m_has_brush)
+ d->fillPath(path, brect);
+
+ if (d->m_has_pen)
+ d->strokePath(path, brect, true);
+ }
+ }
+}
+
+void QDirect3DPaintEngine::drawRects(const QRect *rects, int rectCount)
+{
+#ifdef QT_DEBUG_D3D_CALLS
+ qDebug() << "QDirect3DPaintEngine::drawRects";
+#endif
+ QPaintEngine::drawRects(rects, rectCount);
+}
+
+
+void QDirect3DPaintEngine::drawTextItem(const QPointF &p, const QTextItem &textItem)
+{
+ Q_D(QDirect3DPaintEngine);
+
+#ifdef QT_DEBUG_D3D_CALLS
+ qDebug() << "QDirect3DPaintEngine::drawTextItem";
+#endif
+// if (d->m_matrix.isScaling() || (d->m_pen_brush_style >= Qt::LinearGradientPattern
+// && d->m_pen_brush_style <= Qt::ConicalGradientPattern)) {
+// QPaintEngine::drawTextItem(p, textItem);
+// return;
+// }
+
+ const QTextItemInt &ti = static_cast<const QTextItemInt &>(textItem);
+ QVarLengthArray<QFixedPoint> positions;
+ QVarLengthArray<glyph_t> glyphs;
+ QTransform matrix;
+ matrix.translate(p.x(), p.y());
+ ti.fontEngine->getGlyphPositions(ti.glyphs, matrix, ti.flags, glyphs, positions);
+
+ qd3d_glyph_cache()->cacheGlyphs(this, ti, glyphs, d->m_cleartype_text);
+ QD3DFontTexture *font_tex = qd3d_glyph_cache()->fontTexture(ti.fontEngine);
+
+ QD3DBatchItem *item = d->nextBatchItem();
+ d->m_draw_helper->lockVertexBuffer();
+
+ item->m_info = QD3DBatchItem::BI_TEXT
+ | (d->m_current_state & ~QD3DBatchItem::BI_AA) | QD3DBatchItem::BI_TRANSFORM;
+ item->m_texture = font_tex->texture;
+ item->m_offset = d->m_draw_helper->index();
+ item->m_matrix = d->m_matrix;
+ item->m_count = 0;
+ item->m_brush = d->m_pen.brush();
+
+ for (int i=0; i< glyphs.size(); ++i) {
+ QD3DGlyphCoord *g = qd3d_glyph_cache()->lookup(ti.fontEngine, glyphs[i]);
+
+ // we don't cache glyphs with no width/height
+ if (!g)
+ continue;
+
+ // texture coords
+ qreal tex_coords[] = { g->x, g->y, g->x + g->width, g->y + g->height };
+ QPointF logical_pos(qRound((positions[i].x - g->x_offset).toReal()) - 0.5f,
+ qRound((positions[i].y + g->y_offset).toReal()) - 0.5f);
+
+ QRectF glyph_rect(logical_pos, QSizeF(g->log_width, g->log_height));
+ d->m_draw_helper->queueTextGlyph(glyph_rect, tex_coords, item, d->m_pen_color);
+ }
+}
+
+void QDirect3DPaintEngine::drawTiledPixmap(const QRectF &rect, const QPixmap &pixmap, const QPointF &p)
+{
+#ifdef QT_DEBUG_D3D_CALLS
+ qDebug() << "QDirect3DPaintEngine::drawTiledPixmap";
+#endif
+ QPaintEngine::drawTiledPixmap(rect, pixmap, p);
+}
+
+bool QDirect3DPaintEngine::end()
+{
+ Q_D(QDirect3DPaintEngine);
+
+ d->flushBatch();
+
+ if (d->m_flush_on_end) {
+ QPaintDevice *pdev = paintDevice();
+ LPDIRECT3DSWAPCHAIN9 swapchain = swapChain(pdev);
+
+
+ QWidget *w = 0;
+ if (pdev->devType() == QInternal::Widget) {
+ w = static_cast<QWidget *>(pdev);
+ }
+
+ if (w && swapchain) {
+ QRect br = w->rect();
+ QRect wbr = br;//.translated(-w->pos());
+
+ RECT destrect;
+ destrect.left = wbr.x();
+ destrect.top = wbr.y();
+ destrect.right = destrect.left + wbr.width();
+ destrect.bottom = destrect.top + wbr.height();
+
+ RECT srcrect;
+ srcrect.left = br.x();// + w->x();
+ srcrect.top = br.y();// + w->y();
+ srcrect.right = wbr.width() + srcrect.left;
+ srcrect.bottom = wbr.height() + srcrect.top;
+ int devwidth = w->width();
+ int devheight = w->height();
+
+ if (devwidth <= srcrect.right) {
+ int diff = srcrect.right - devwidth;
+ srcrect.right -= diff;
+ destrect.right -= diff;
+ if (srcrect.right <= srcrect.left)
+ return false;
+ }
+ if (devheight <= srcrect.bottom) {
+ int diff = srcrect.bottom - devheight;
+ srcrect.bottom -= diff;
+ destrect.bottom -= diff;
+ if (srcrect.bottom <= srcrect.top)
+ return false;
+ }
+
+ if (FAILED(swapchain->Present(&srcrect, &destrect, w->winId(), 0, 0)))
+ qWarning("QDirect3DPaintEngine: failed to present back buffer.");
+ }
+ }
+
+
+ return true;
+}
+
+void QDirect3DPaintEngine::updateState(const QPaintEngineState &state)
+{
+#ifdef QT_DEBUG_D3D_CALLS
+ qDebug() << "QDirect3DPaintEngine::updateState";
+#endif
+ Q_D(QDirect3DPaintEngine);
+
+ bool update_fast_pen = false;
+ DirtyFlags flags = state.state();
+
+ if (flags & DirtyOpacity) {
+ d->m_opacity = state.opacity();
+ if (d->m_opacity > 1.0f)
+ d->m_opacity = 1.0f;
+ if (d->m_opacity < 0.f)
+ d->m_opacity = 0.f;
+ uint c = (d->m_opacity * 255);
+ d->m_opacity_color = D3DCOLOR_ARGB(c,c,c,c);
+ flags |= (DirtyPen | DirtyBrush);
+ }
+
+ if (flags & DirtyCompositionMode) {
+ d->m_cmode = state.compositionMode();
+ }
+
+ if (flags & DirtyTransform) {
+ d->updateTransform(state.transform());
+ update_fast_pen = true;
+ }
+
+ if (flags & DirtyHints) {
+ if (state.renderHints() & QPainter::Antialiasing)
+ d->m_current_state |= QD3DBatchItem::BI_AA;
+ else
+ d->m_current_state &= ~QD3DBatchItem::BI_AA;
+ update_fast_pen = true;
+ }
+
+ if (flags & DirtyFont) {
+ d->updateFont(state.font());
+ }
+
+ if (state.state() & DirtyClipEnabled) {
+ if (state.isClipEnabled() && !d->m_clipping_enabled) {
+ d->m_clipping_enabled = true;
+ if (d->m_has_complex_clipping)
+ d->updateClipPath(painter()->clipPath(), Qt::ReplaceClip);
+ else
+ d->updateClipRegion(painter()->clipRegion(), Qt::ReplaceClip);
+ } else if (!state.isClipEnabled() && d->m_clipping_enabled) {
+ d->m_clipping_enabled = false;
+ if (d->m_has_complex_clipping)
+ d->updateClipPath(QPainterPath(), Qt::NoClip);
+ else
+ d->updateClipRegion(QRegion(), Qt::NoClip);
+ }
+ }
+
+ if (flags & DirtyClipRegion) {
+ d->updateClipRegion(state.clipRegion(), state.clipOperation());
+ }
+
+ if (flags & DirtyClipPath) {
+ d->updateClipPath(state.clipPath(), state.clipOperation());
+ }
+
+ if (flags & DirtyBrushOrigin) {
+ d->m_brush_origin = QTransform();
+ d->m_brush_origin.translate(-state.brushOrigin().x(),
+ -state.brushOrigin().y());
+ flags |= DirtyBrush;
+ }
+
+ if (flags & DirtyPen) {
+ d->updatePen(state.pen());
+ update_fast_pen = true;
+ }
+
+ if (flags & DirtyBrush)
+ d->updateBrush(state.brush());
+
+ if (update_fast_pen && d->m_has_pen) {
+ if (d->m_current_state & QD3DBatchItem::BI_AA) {
+ d->m_has_fast_pen = false;
+ d->m_has_aa_fast_pen = ((d->m_txop <= QTransform::TxTranslate) || d->m_has_cosmetic_pen)
+ && (d->m_pen_width <= 1.0f)
+ && (d->m_pen.style() == Qt::SolidLine);
+ } else {
+ d->m_has_aa_fast_pen = false;
+ d->m_has_fast_pen = ((d->m_txop <= QTransform::TxTranslate) || d->m_has_cosmetic_pen)
+ && (d->m_pen.style() == Qt::SolidLine)
+ && (d->m_pen.capStyle() == Qt::SquareCap);
+ }
+ }
+}
+
+void QDirect3DPaintEngine::cleanup()
+{
+ Q_D(QDirect3DPaintEngine);
+ d->cleanup();
+}
+
+void QDirect3DPaintEngine::scroll(QPaintDevice *pd, const RECT &srcrect, const RECT &destrect)
+{
+ Q_D(QDirect3DPaintEngine);
+ LPDIRECT3DSURFACE9 srcsurf = d->m_surface_manager.surface(pd);
+ LPDIRECT3DSURFACE9 masksurf = d->m_draw_helper->freeMaskSurface();
+ if (FAILED(d->m_d3d_device->StretchRect(srcsurf, &srcrect, masksurf, &srcrect, D3DTEXF_NONE)))
+ qWarning("QDirect3DPaintEngine: StretchRect failed.");
+ if (FAILED(d->m_d3d_device->StretchRect(masksurf, &srcrect, srcsurf, &destrect, D3DTEXF_NONE)))
+ qWarning("QDirect3DPaintEngine: StretchRect failed.");
+}
+
+LPDIRECT3DSWAPCHAIN9 QDirect3DPaintEngine::swapChain(QPaintDevice *pd)
+{
+ Q_D(QDirect3DPaintEngine);
+
+ if (d->m_in_scene) {
+ if (d->m_d3d_device == 0) {
+ qWarning("QDirect3DPaintEngine: No device!");
+ return false;
+ }
+
+ d->setRenderTechnique(QDirect3DPaintEnginePrivate::RT_NoTechnique);
+ if (FAILED(d->m_d3d_device->EndScene()))
+ qWarning("QDirect3DPaintEngine: failed to end scene.");
+
+ d->m_in_scene = false;
+ }
+
+ return d->m_surface_manager.swapChain(pd);
+}
+
+void QDirect3DPaintEngine::releaseSwapChain(QPaintDevice *pd)
+{
+ Q_D(QDirect3DPaintEngine);
+ d->m_surface_manager.releasePaintDevice(pd);
+}
+
+HDC QDirect3DPaintEngine::getDC() const
+{
+ QDirect3DPaintEnginePrivate *d = const_cast<QDirect3DPaintEnginePrivate *>(d_func());
+
+ if (!d->m_dc && d->m_current_surface) {
+ d->m_dcsurface = d->m_current_surface;
+ if (FAILED(d->m_current_surface->GetDC(&d->m_dc)))
+ qWarning() << "QDirect3DPaintEngine::getDC() failed!";
+ }
+
+ return d->m_dc;
+}
+
+void QDirect3DPaintEngine::setFlushOnEnd(bool flushOnEnd)
+{
+ Q_D(QDirect3DPaintEngine);
+
+ d->m_flush_on_end = flushOnEnd;
+}
+
+bool QDirect3DPaintEngine::hasDirect3DSupport()
+{
+ Q_D(QDirect3DPaintEngine);
+ return d->m_supports_d3d;
+}
+
+QT_END_NAMESPACE
+
+#include "qpaintengine_d3d.moc"
diff --git a/src/gui/painting/qpaintengine_d3d.fx b/src/gui/painting/qpaintengine_d3d.fx
new file mode 100644
index 0000000000..1148b2a751
--- /dev/null
+++ b/src/gui/painting/qpaintengine_d3d.fx
@@ -0,0 +1,608 @@
+bool g_mCosmeticPen;
+int4 g_mChannel;
+float2 g_mMaskOffset;
+int2 g_mMaskSize;
+float4x4 g_mMaskProjection;
+float4x4 g_mViewProjection;
+float4x4 g_mTransformation;
+texture g_mAAMask;
+texture g_mTexture;
+int g_mBrushMode;
+float g_mFocalDist;
+
+#define M_PI 3.14159265358979323846
+
+sampler PixmapSampler = sampler_state
+{
+ texture = <g_mTexture>;
+ MIPFILTER = NONE;
+ MINFILTER = LINEAR;
+ MAGFILTER = LINEAR;
+};
+
+sampler TextSampler = sampler_state
+{
+ texture = <g_mTexture>;
+ MIPFILTER = NONE;
+ MINFILTER = POINT;
+ MAGFILTER = POINT;
+};
+
+sampler AAMaskSampler = sampler_state
+{
+ texture = <g_mAAMask>;
+ AddressU = WRAP;
+ AddressV = WRAP;
+ AddressW = WRAP;
+ MIPFILTER = NONE;
+ MINFILTER = POINT;
+ MAGFILTER = POINT;
+};
+
+struct VS_FULL
+{
+ float4 Position : POSITION;
+ float4 Diffuse : COLOR0;
+ float4 TexCoords0 : TEXCOORD0;
+ float4 TexCoords1 : TEXCOORD1;
+};
+
+VS_FULL TrapezoidVS( float4 Position : POSITION,
+ float4 Diffuse : COLOR0,
+ float4 TexCoords0 : TEXCOORD0,
+ float4 TexCoords1 : TEXCOORD1)
+{
+ VS_FULL Output;
+
+ float a = (TexCoords1.x * Position.x) + (TexCoords1.z * (1.0 - Position.x) ); // left or right a
+ float b = (TexCoords1.y * Position.x) + (TexCoords1.w * (1.0 - Position.x) ); // left or right b
+ float d = 1.0 - (Position.x * 2);
+
+ Position.x = (a * Position.y + b) + ( sqrt( abs(a * a) ) * d );
+ //Position.x += step(abs(a), 0) * d;
+ Position.x += (0.5 * d);
+
+ Output.Position = mul(Position, g_mMaskProjection);
+ Output.Diffuse = Diffuse;
+ Output.TexCoords0 = TexCoords0;
+ Output.TexCoords1 = TexCoords1;
+
+ return Output;
+}
+
+struct PS_OUTPUT
+{
+ float4 Color : COLOR0;
+};
+
+PS_OUTPUT TrapezoidPS(VS_FULL In, float2 pixelPos : VPOS)
+{
+ PS_OUTPUT Out;
+
+ float top = max(pixelPos.y - 0.5, In.TexCoords0.x);
+ float bottom = min(pixelPos.y + 0.5, In.TexCoords0.y);
+
+ float area = bottom - top;
+
+ float left = pixelPos.x - 0.5;
+ float right = pixelPos.x + 0.5;
+
+ // use line equations to compute intersections of left/right edges with top/bottom of truncated pixel
+ // vecX: x = (left, top), y = (left, bottom), z = (right, top), w = (right, bottom)
+ float4 vecX = In.TexCoords1.xxzz * float2(top, bottom).xyxy + In.TexCoords1.yyww;
+
+ float2 invA = In.TexCoords0.zw;
+
+ // transform right line to left to be able to use same calculations for both
+ vecX.zw = 2 * pixelPos.x - vecX.zw;
+
+ float2 topX = float2(vecX.x, vecX.z);
+ float2 bottomX = float2(vecX.y, vecX.w);
+
+ // transform lines such that top intersection is to the right of bottom intersection
+ float2 topXTemp = max(topX, bottomX);
+ float2 bottomXTemp = min(topX, bottomX);
+
+ // make sure line slope reflects mirrored lines
+ invA = lerp(invA, -invA, step(topX, bottomX));
+
+ float2 vecLeftRight = float2(left, right);
+
+ // compute the intersections of the lines with the left and right edges of the pixel
+ // intersectY: x = (left_line, left), y = (left_line, right), z = (right_line, left), w = (right_line, right)
+ float4 intersectY = top + (vecLeftRight.xyxy - topXTemp.xxyy) * invA.xxyy;
+
+ float2 temp = lerp(area - 0.5 * (right - bottomXTemp) * (bottom - intersectY.yw), // left < bottom < right < top
+ (0.5 * (topXTemp + bottomXTemp) - left) * area, // left < bottom < top < right
+ step(topXTemp, right));
+
+ float2 excluded = 0.5 * (intersectY.xz - top) * (topXTemp - left); // bottom < left < top < right
+
+ excluded = lerp(0.5 * (intersectY.yw + intersectY.xz) - top, // bottom < left < right < top
+ excluded, step(topXTemp, right));
+
+ excluded = lerp(temp, // left < bottom < right (see calculation of temp)
+ excluded, step(bottomXTemp, left));
+
+ excluded = lerp(float2(area, area), // right < bottom < top
+ excluded, step(bottomXTemp, right));
+
+ excluded *= step(left, topXTemp);
+
+ float result = (area - excluded.x - excluded.y) * step(top, bottom);
+ Out.Color.r = result * g_mChannel[0];
+ Out.Color.g = result * g_mChannel[1];
+ Out.Color.b = result * g_mChannel[2];
+ Out.Color.a = result * g_mChannel[3];
+
+ return Out;
+}
+
+VS_FULL ViewProjectionVS( float4 Position : POSITION,
+ float4 Diffuse : COLOR0,
+ float4 TexCoords0 : TEXCOORD0,
+ float4 TexCoords1 : TEXCOORD1)
+{
+ VS_FULL Output;
+
+ Output.Position = mul(Position, g_mTransformation);
+ Output.Position = mul(Output.Position, g_mViewProjection);
+ Output.Diffuse = Diffuse;
+ Output.TexCoords0 = TexCoords0;
+ Output.TexCoords1 = TexCoords1;
+
+ return Output;
+}
+
+PS_OUTPUT DirectMaskPS(VS_FULL In, float2 pixelPos : VPOS)
+{
+ PS_OUTPUT Out;
+ Out.Color = In.Diffuse;
+
+ float2 maskcoords = ( (pixelPos + g_mMaskOffset) - 0.5 ) / g_mMaskSize;
+ float2 clipcoords = (pixelPos - 0.5) / g_mMaskSize;
+
+ float4 c = tex2D(AAMaskSampler, maskcoords.xy) * Out.Color.a;
+ Out.Color.a = c.r * g_mChannel[0];
+ Out.Color.a += c.g * g_mChannel[1];
+ Out.Color.a += c.b * g_mChannel[2];
+ Out.Color.a += c.a * g_mChannel[3];
+
+ return Out;
+}
+
+PS_OUTPUT MaskPS(VS_FULL In, float2 pixelPos : VPOS)
+{
+ PS_OUTPUT Out;
+
+ if (g_mBrushMode == 1) {
+ float x = In.TexCoords0.x;
+ float y = In.TexCoords0.y;
+ x = x - int(x);
+ y = y - int(y);
+ Out.Color = tex2D(PixmapSampler, float2(x, y));
+ Out.Color.a = Out.Color.a * In.Diffuse.a;
+ } else if (g_mBrushMode == 2) {
+ Out.Color = tex1D(PixmapSampler, In.TexCoords0.x);
+ } else if (g_mBrushMode == 3) {
+ float t = atan2(In.TexCoords0.y, -In.TexCoords0.x) / (2 * M_PI);
+ Out.Color = tex1D(PixmapSampler, t + 0.5);
+ } else if (g_mBrushMode == 4) {
+ float2 tc = float2(In.TexCoords0.x, abs(In.TexCoords0.y));
+ float a = (tc.x - g_mFocalDist) / tc.y;
+ float b = g_mFocalDist;
+
+ float A = 1 + (a * a);
+ float B = 2.0 * a * b;
+ float C = (b * b) - 1;
+
+ float y = (-B + sqrt(B*B - 4.0*A*C)) / (2.0*A);
+ Out.Color = tex1D(PixmapSampler, (tc.y / y) );
+ } else if (g_mBrushMode == 5) {
+ Out.Color = tex2D(PixmapSampler, In.TexCoords0.xy);
+ Out.Color = Out.Color * In.Diffuse;
+ } else {
+ Out.Color = In.Diffuse;
+ }
+
+ float2 maskcoords = ( (pixelPos + g_mMaskOffset) - 0.5 ) / g_mMaskSize;
+
+ float4 c = tex2D(AAMaskSampler, maskcoords.xy) * Out.Color.a;
+ Out.Color.a = c.r * g_mChannel[0];
+ Out.Color.a += c.g * g_mChannel[1];
+ Out.Color.a += c.b * g_mChannel[2];
+ Out.Color.a += c.a * g_mChannel[3];
+
+ return Out;
+}
+
+struct VS_NORMAL
+{
+ float4 Position : POSITION;
+ float4 Diffuse : COLOR0;
+ float4 TexCoords : TEXCOORD0;
+};
+
+VS_NORMAL MaskProjectionVS(VS_NORMAL In)
+{
+ VS_NORMAL Output;
+
+ Output.Position = mul(In.Position, g_mMaskProjection);
+ Output.Diffuse = In.Diffuse;
+ Output.TexCoords = In.TexCoords;
+
+ return Output;
+}
+
+float4 DirectSimplePS(float4 Color : COLOR0) : COLOR0
+{
+ return Color;
+}
+
+float4 SimplePS(float4 Color : COLOR0, float4 TexCoords : TEXCOORD0) : COLOR0
+{
+ if (g_mBrushMode == 1) {
+ float opacity = Color.a;
+ float x = TexCoords.x;
+ float y = TexCoords.y;
+ x = x - int(x);
+ y = y - int(y);
+ Color = tex2D(PixmapSampler, float2(x, y));
+ Color.a = Color.a * opacity;
+ } else if (g_mBrushMode == 2) {
+ Color = tex1D(PixmapSampler, TexCoords.x);
+ } else if (g_mBrushMode == 3) {
+ float t = atan2(TexCoords.y, -TexCoords.x) / (2 * M_PI);
+ Color = tex1D(PixmapSampler, t + 0.5);
+ } else if (g_mBrushMode == 4) {
+ float2 tc = float2(TexCoords.x, abs(TexCoords.y));
+ float a = (tc.x - g_mFocalDist) / tc.y;
+ float b = g_mFocalDist;
+
+ float A = 1 + (a * a);
+ float B = 2.0 * a * b;
+ float C = (b * b) - 1;
+
+ float y = (-B + sqrt(B*B - 4.0*A*C)) / (2.0*A);
+ Color = tex1D(PixmapSampler, (tc.y / y) );
+ } else if (g_mBrushMode == 5) {
+ Color = tex2D(PixmapSampler, TexCoords.xy) * Color;
+ }
+
+ return Color;
+}
+
+float4 TextPS(float4 Color : COLOR0, float4 TexCoords : TEXCOORD0) : COLOR0
+{
+ Color.a *= tex2D(TextSampler, TexCoords.xy).a;
+ return Color;
+}
+
+float4 ClearTypePS(float4 Color : COLOR0, float4 TexCoords : TEXCOORD0) : COLOR0
+{
+// if (g_mUsePixmap) {
+// float4 MaskColor = tex2D(PixmapSampler, TexCoords.xy);
+// Color = float4(1.0, 0.0, 0.0, 1.0);
+// Color.a = (1 - MaskColor.a) + MaskColor.a * Color.a;
+// Color.r = (1.0 - MaskColor.r) + (MaskColor.r * Color.r);
+// Color.g = (1.0 - MaskColor.g) + (MaskColor.g * Color.g);
+// Color.b = (1.0 - MaskColor.b) + (MaskColor.b * Color.b);
+// Color = MaskColor;
+ return tex2D(PixmapSampler, TexCoords.xy);
+}
+
+VS_NORMAL NoTxAliasedVS(VS_NORMAL In)
+{
+ VS_NORMAL Output;
+
+ Output.Position = mul(In.Position, g_mViewProjection);
+ Output.Diffuse = In.Diffuse;
+ Output.TexCoords = In.TexCoords;
+
+ return Output;
+}
+
+VS_NORMAL AliasedVS(VS_NORMAL In)
+{
+ VS_NORMAL Output;
+
+ Output.Position = mul(In.Position, g_mTransformation);
+ Output.Position = mul(Output.Position, g_mViewProjection);
+ Output.Diffuse = In.Diffuse;
+ Output.TexCoords = In.TexCoords;
+
+ return Output;
+}
+
+VS_NORMAL AliasedLinesVS(VS_NORMAL In)
+{
+ VS_NORMAL Output;
+
+ float4 start = float4(In.Position.x, In.Position.y, 0.5, In.Position.w);
+ float4 end = float4(In.TexCoords.z, In.TexCoords.w, 0.5, In.Position.w);
+ if (g_mCosmeticPen) {
+ start = mul(start, g_mTransformation);
+ end = mul(end, g_mTransformation);
+ }
+
+ float2 line_vec = end - start;
+ float2 vec = normalize(line_vec);
+ float2 norm = float2(-vec.y, vec.x);
+
+ float pen_width = In.Position.z;
+ norm = norm * pen_width * 0.5;
+ vec = vec * pen_width * 0.5;
+
+ Output.Position.w = In.Position.w;
+ Output.Position.x = start.x + (vec.x * In.TexCoords.x);
+ Output.Position.x = Output.Position.x + (norm.x * In.TexCoords.y);
+ Output.Position.x = Output.Position.x + (line_vec.x * step(0, In.TexCoords.x));
+ Output.Position.y = start.y + (vec.y * In.TexCoords.x);
+ Output.Position.y = Output.Position.y + (norm.y * In.TexCoords.y);
+ Output.Position.y = Output.Position.y + (line_vec.y * step(0, In.TexCoords.x));
+ Output.Position.z = 0.5;
+
+ if (!g_mCosmeticPen) {
+ Output.Position = mul(Output.Position, g_mTransformation);
+ }
+ Output.Position = mul(Output.Position, g_mViewProjection);
+
+ Output.Diffuse = In.Diffuse;
+ Output.TexCoords = In.TexCoords;
+
+ return Output;
+}
+
+
+technique Antialiased
+{
+ pass PASS_AA_CREATEMASK
+ {
+ StencilEnable = False;
+ ZWriteEnable = False;
+ ColorWriteEnable = 0x0f;
+ ZEnable = False;
+
+ SrcBlend = One;
+ DestBlend = One;
+
+ VertexShader = compile vs_3_0 TrapezoidVS();
+ PixelShader = compile ps_3_0 TrapezoidPS();
+ }
+
+ pass PASS_AA_DRAW
+ {
+ StencilEnable = False;
+ ZFunc = Greater;
+ ZWriteEnable = False;
+ ZEnable = True;
+ ColorWriteEnable = 0x0f;
+
+ VertexShader = compile vs_3_0 ViewProjectionVS();
+ PixelShader = compile ps_3_0 MaskPS();
+ }
+
+ pass PASS_AA_DRAW_DIRECT
+ {
+ StencilEnable = False;
+ ZFunc = Greater;
+ ZEnable = True;
+ ZWriteEnable = False;
+ ColorWriteEnable = 0x0f;
+
+ VertexShader = compile vs_3_0 ViewProjectionVS();
+ PixelShader = compile ps_3_0 DirectMaskPS();
+ }
+}
+
+technique Aliased
+{
+ pass PASS_STENCIL_ODDEVEN
+ {
+ TwoSidedStencilMode = False;
+ StencilEnable = True;
+ StencilPass = Invert;
+ StencilFunc = Always;
+ ColorWriteEnable = 0;
+
+ ZEnable = False;
+ ZWriteEnable = False;
+
+ VertexShader = compile vs_1_1 NoTxAliasedVS();
+ PixelShader = compile ps_2_0 DirectSimplePS();
+ }
+
+ pass PASS_STENCIL_WINDING
+ {
+ TwoSidedStencilMode = True;
+ StencilEnable = True;
+ StencilRef = 0;
+ StencilMask = 0xFFFFFFFF;
+
+ CCW_StencilPass = Incr;
+ CCW_StencilFunc = Always;
+
+ StencilPass = Decr;
+ StencilFunc = Always;
+
+ ColorWriteEnable = 0;
+
+ ZEnable = False;
+ ZWriteEnable = False;
+
+ VertexShader = compile vs_1_1 NoTxAliasedVS();
+ PixelShader = compile ps_2_0 DirectSimplePS();
+ }
+
+ pass PASS_STENCIL_DRAW
+ {
+ TwoSidedStencilMode = False;
+ StencilEnable = True;
+ StencilFunc = NotEqual;
+ StencilMask = 0xFFFFFFFF;
+ StencilRef = 0;
+ StencilPass = Zero;
+ StencilFail = Zero;
+ StencilZFail = Zero;
+
+ ColorWriteEnable = 0x0f;
+ ZEnable = True;
+ ZWriteEnable = False;
+ ZFunc = Greater;
+
+ VertexShader = compile vs_1_1 AliasedVS();
+ PixelShader = compile ps_2_0 SimplePS();
+ }
+
+ pass PASS_STENCIL_DRAW_DIRECT
+ {
+ TwoSidedStencilMode = False;
+ StencilEnable = True;
+ StencilFunc = NotEqual;
+ StencilMask = 0xFFFFFFFF;
+ StencilRef = 0;
+ StencilPass = Zero;
+ StencilFail = Zero;
+ StencilZFail = Zero;
+
+ ColorWriteEnable = 0x0f;
+ ZEnable = True;
+ ZWriteEnable = False;
+ ZFunc = Greater;
+
+ VertexShader = compile vs_1_1 AliasedVS();
+ PixelShader = compile ps_2_0 DirectSimplePS();
+ }
+
+ pass PASS_STENCIL_CLIP
+ {
+ TwoSidedStencilMode = False;
+ StencilEnable = True;
+ StencilFunc = NotEqual;
+ StencilMask = 0xFFFFFFFF;
+ StencilRef = 0;
+ StencilPass = Zero;
+ StencilFail = Zero;
+ StencilZFail = Zero;
+
+ ColorWriteEnable = 0;
+ ZEnable = True;
+ ZWriteEnable = True;
+ ZFunc = Always;
+
+ VertexShader = compile vs_1_1 NoTxAliasedVS();
+ PixelShader = compile ps_2_0 DirectSimplePS();
+ }
+
+ pass PASS_STENCIL_NOSTENCILCHECK
+ {
+ StencilEnable = False;
+
+ ZEnable = True;
+ ZWriteEnable = False;
+ ZFunc = Greater;
+
+ ColorWriteEnable = 0x0f;
+
+ SrcBlend = SrcAlpha;
+ DestBlend = InvSrcAlpha;
+
+ VertexShader = compile vs_1_1 AliasedVS();
+ PixelShader = compile ps_2_0 SimplePS();
+ }
+
+ pass PASS_STENCIL_NOSTENCILCHECK_DIRECT
+ {
+ StencilEnable = False;
+
+ ZEnable = True;
+ ZWriteEnable = False;
+ ZFunc = Greater;
+
+ ColorWriteEnable = 0x0f;
+
+ SrcBlend = SrcAlpha;
+ DestBlend = InvSrcAlpha;
+
+ VertexShader = compile vs_1_1 AliasedVS();
+ PixelShader = compile ps_2_0 DirectSimplePS();
+ }
+
+ pass PASS_TEXT
+ {
+ StencilEnable = False;
+
+ ZEnable = True;
+ ZWriteEnable = False;
+ ZFunc = Greater;
+
+ ColorWriteEnable = 0x0f;
+
+ SrcBlend = SrcAlpha;
+ DestBlend = InvSrcAlpha;
+
+ VertexShader = compile vs_1_1 AliasedVS();
+ PixelShader = compile ps_2_0 TextPS();
+ }
+
+ pass PASS_CLEARTYPE_TEXT
+ {
+ StencilEnable = False;
+
+ ZEnable = True;
+ ZWriteEnable = False;
+ ZFunc = Greater;
+
+ ColorWriteEnable = 0x0f;
+
+// SrcBlend = SrcAlpha;
+// DestBlend = InvSrcAlpha;
+
+// SrcBlend = DestColor;
+// DestBlend = Zero;
+ SrcBlend = BlendFactor;
+ DestBlend = InvSrcColor;
+
+// SrcBlend = Zero;
+// DestBlend = SrcColor;
+
+// SrcBlend = One;
+// DestBlend = Zero;
+
+ VertexShader = compile vs_3_0 AliasedVS();
+ PixelShader = compile ps_3_0 ClearTypePS();
+ }
+
+ pass PASS_ALIASED_LINES
+ {
+ TwoSidedStencilMode = False;
+ StencilEnable = True;
+ StencilPass = Invert;
+ StencilFunc = Always;
+ ColorWriteEnable = 0;
+
+ ZEnable = False;
+ ZWriteEnable = False;
+
+ VertexShader = compile vs_1_1 AliasedLinesVS();
+ PixelShader = compile ps_2_0 DirectSimplePS();
+ }
+
+ pass PASS_ALIASED_LINES_DIRECT
+ {
+ StencilEnable = False;
+
+ ZEnable = True;
+ ZWriteEnable = False;
+ ZFunc = Greater;
+
+ ColorWriteEnable = 0x0f;
+
+ SrcBlend = SrcAlpha;
+ DestBlend = InvSrcAlpha;
+
+ VertexShader = compile vs_1_1 AliasedLinesVS();
+ PixelShader = compile ps_2_0 DirectSimplePS();
+ }
+}
+
diff --git a/src/gui/painting/qpaintengine_d3d.qrc b/src/gui/painting/qpaintengine_d3d.qrc
new file mode 100644
index 0000000000..c106f2bc68
--- /dev/null
+++ b/src/gui/painting/qpaintengine_d3d.qrc
@@ -0,0 +1,5 @@
+<!DOCTYPE RCC><RCC version="1.0">
+<qresource>
+ <file>qpaintengine_d3d.fx</file>
+</qresource>
+</RCC>
diff --git a/src/gui/painting/qpaintengine_d3d_p.h b/src/gui/painting/qpaintengine_d3d_p.h
new file mode 100644
index 0000000000..8fa5cf6df4
--- /dev/null
+++ b/src/gui/painting/qpaintengine_d3d_p.h
@@ -0,0 +1,120 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QPAINTENGINE_D3D_P_H
+#define QPAINTENGINE_D3D_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 "QtGui/qpaintengine.h"
+#include <d3d9.h>
+
+QT_BEGIN_NAMESPACE
+
+class QDirect3DPaintEnginePrivate;
+class QDirect3DPaintEngine : public QPaintEngine
+{
+ Q_DECLARE_PRIVATE(QDirect3DPaintEngine)
+public:
+ QDirect3DPaintEngine();
+ ~QDirect3DPaintEngine();
+ bool begin(QPaintDevice *device);
+
+ void drawEllipse(const QRectF &rect);
+ void drawEllipse(const QRect &rect);
+
+ void drawImage(const QRectF &rectangle, const QImage &image, const QRectF &sr,
+ Qt::ImageConversionFlags flags = Qt::AutoColor);
+
+ void drawLines(const QLineF *lines, int lineCount);
+ void drawLines(const QLine *lines, int lineCount);
+
+ void drawPath(const QPainterPath &path);
+
+ void drawPixmap(const QRectF &r, const QPixmap &pm, const QRectF &sr);
+
+ void drawPoints(const QPointF *points, int pointCount);
+ void drawPoints(const QPoint *points, int pointCount);
+
+ void drawPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode);
+ void drawPolygon(const QPoint *points, int pointCount, PolygonDrawMode mode);
+
+ void drawRects(const QRectF *rects, int rectCount);
+ void drawRects(const QRect * rects, int rectCount);
+
+ void drawTextItem(const QPointF &p, const QTextItem &textItem);
+
+ void drawTiledPixmap(const QRectF &r, const QPixmap &pm, const QPointF &sr);
+
+ bool end();
+
+ Type type() const { return Direct3D; }
+ void updateState(const QPaintEngineState &state);
+
+ void cleanup();
+
+ HDC getDC() const;
+ void setFlushOnEnd(bool flushOnEnd);
+ bool hasDirect3DSupport();
+
+public:
+ void scroll(QPaintDevice *pd, const RECT &srcrect, const RECT &destrect);
+ LPDIRECT3DSWAPCHAIN9 swapChain(QPaintDevice *pd);
+ void releaseSwapChain(QPaintDevice *pd);
+
+private:
+ Q_DISABLE_COPY(QDirect3DPaintEngine)
+ friend class QPixmap;
+ friend class QD3DGlyphCache;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/gui/painting/qpaintengine_mac.cpp b/src/gui/painting/qpaintengine_mac.cpp
new file mode 100644
index 0000000000..0644a02ac6
--- /dev/null
+++ b/src/gui/painting/qpaintengine_mac.cpp
@@ -0,0 +1,1789 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <qbitmap.h>
+#include <qpaintdevice.h>
+#include <private/qpaintengine_mac_p.h>
+#include <qpainterpath.h>
+#include <qpixmapcache.h>
+#include <private/qpaintengine_raster_p.h>
+#include <private/qprintengine_mac_p.h>
+#include <qprinter.h>
+#include <qstack.h>
+#include <qtextcodec.h>
+#include <qwidget.h>
+#include <qvarlengtharray.h>
+#include <qdebug.h>
+#include <qcoreapplication.h>
+#include <qmath.h>
+
+#include <private/qfont_p.h>
+#include <private/qfontengine_p.h>
+#include <private/qnumeric_p.h>
+#include <private/qpainter_p.h>
+#include <private/qpainterpath_p.h>
+#include <private/qpixmap_mac_p.h>
+#include <private/qt_mac_p.h>
+#include <private/qtextengine_p.h>
+#include <private/qwidget_p.h>
+#include <private/qt_cocoa_helpers_mac_p.h>
+
+#include <string.h>
+
+QT_BEGIN_NAMESPACE
+
+extern int qt_antialiasing_threshold; // QApplication.cpp
+
+/*****************************************************************************
+ External functions
+ *****************************************************************************/
+extern CGImageRef qt_mac_create_imagemask(const QPixmap &px, const QRectF &sr); //qpixmap_mac.cpp
+extern QPoint qt_mac_posInWindow(const QWidget *w); //qwidget_mac.cpp
+extern OSWindowRef qt_mac_window_for(const QWidget *); //qwidget_mac.cpp
+extern CGContextRef qt_mac_cg_context(const QPaintDevice *); //qpaintdevice_mac.cpp
+extern void qt_mac_dispose_rgn(RgnHandle r); //qregion_mac.cpp
+extern QPixmap qt_pixmapForBrush(int, bool); //qbrush.cpp
+
+void qt_mac_clip_cg(CGContextRef hd, const QRegion &rgn, CGAffineTransform *orig_xform);
+
+
+//Implemented for qt_mac_p.h
+QMacCGContext::QMacCGContext(QPainter *p)
+{
+ QPaintEngine *pe = p->paintEngine();
+ if (pe->type() == QPaintEngine::MacPrinter)
+ pe = static_cast<QMacPrintEngine*>(pe)->paintEngine();
+ pe->syncState();
+ context = 0;
+ if(pe->type() == QPaintEngine::CoreGraphics)
+ context = static_cast<QCoreGraphicsPaintEngine*>(pe)->handle();
+
+ int devType = p->device()->devType();
+ if (pe->type() == QPaintEngine::Raster
+ && (devType == QInternal::Widget || devType == QInternal::Pixmap)) {
+
+ extern CGColorSpaceRef qt_mac_colorSpaceForDeviceType(const QPaintDevice *paintDevice);
+ CGColorSpaceRef colorspace = qt_mac_colorSpaceForDeviceType(pe->paintDevice());
+#if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4)
+ uint flags = kCGImageAlphaPremultipliedFirst;
+#ifdef kCGBitmapByteOrder32Host //only needed because CGImage.h added symbols in the minor version
+ if(QSysInfo::MacintoshVersion >= QSysInfo::MV_10_4)
+ flags |= kCGBitmapByteOrder32Host;
+#endif
+#else
+ CGImageAlphaInfo flags = kCGImageAlphaPremultipliedFirst;
+#endif
+ const QImage *image = (const QImage *) pe->paintDevice();
+
+ context = CGBitmapContextCreate((void *) image->bits(), image->width(), image->height(),
+ 8, image->bytesPerLine(), colorspace, flags);
+
+ CGContextTranslateCTM(context, 0, image->height());
+ CGContextScaleCTM(context, 1, -1);
+
+ if (devType == QInternal::Widget) {
+ QRegion clip = p->paintEngine()->systemClip();
+ if (p->hasClipping()) {
+ if (clip.isEmpty())
+ clip = p->clipRegion();
+ else
+ clip &= p->clipRegion();
+ }
+ qt_mac_clip_cg(context, clip, 0);
+
+ QPainterState *state = static_cast<QPainterState *>(pe->state);
+ Q_ASSERT(state);
+ if (!state->redirection_offset.isNull())
+ CGContextTranslateCTM(context, -state->redirection_offset.x(), -state->redirection_offset.y());
+ }
+ }
+ CGContextRetain(context);
+}
+
+
+/*****************************************************************************
+ QCoreGraphicsPaintEngine utility functions
+ *****************************************************************************/
+
+//conversion
+inline static float qt_mac_convert_color_to_cg(int c) { return ((float)c * 1000 / 255) / 1000; }
+inline static int qt_mac_convert_color_from_cg(float c) { return qRound(c * 255); }
+CGAffineTransform qt_mac_convert_transform_to_cg(const QTransform &t) {
+ return CGAffineTransformMake(t.m11(), t.m12(), t.m21(), t.m22(), t.dx(), t.dy());
+}
+
+CGColorSpaceRef qt_mac_colorSpaceForDeviceType(const QPaintDevice *paintDevice)
+{
+ bool isWidget = (paintDevice->devType() == QInternal::Widget);
+ return QCoreGraphicsPaintEngine::macDisplayColorSpace(isWidget ? static_cast<const QWidget *>(paintDevice)
+ : 0);
+}
+
+inline static QCFType<CGColorRef> cgColorForQColor(const QColor &col, QPaintDevice *pdev)
+{
+ CGFloat components[] = {
+ qt_mac_convert_color_to_cg(col.red()),
+ qt_mac_convert_color_to_cg(col.green()),
+ qt_mac_convert_color_to_cg(col.blue()),
+ qt_mac_convert_color_to_cg(col.alpha())
+ };
+ return CGColorCreate(qt_mac_colorSpaceForDeviceType(pdev), components);
+}
+
+// There's architectural problems with using native gradients
+// on the Mac at the moment, so disable them.
+// #define QT_MAC_USE_NATIVE_GRADIENTS
+
+#ifdef QT_MAC_USE_NATIVE_GRADIENTS
+static bool drawGradientNatively(const QGradient *gradient)
+{
+ return gradient->spread() == QGradient::PadSpread;
+}
+
+// gradiant callback
+static void qt_mac_color_gradient_function(void *info, const CGFloat *in, CGFloat *out)
+{
+ QBrush *brush = static_cast<QBrush *>(info);
+ Q_ASSERT(brush && brush->gradient());
+
+ const QGradientStops stops = brush->gradient()->stops();
+ const int n = stops.count();
+ Q_ASSERT(n >= 1);
+ const QGradientStop *begin = stops.constBegin();
+ const QGradientStop *end = begin + n;
+
+ qreal p = in[0];
+ const QGradientStop *i = begin;
+ while (i != end && i->first < p)
+ ++i;
+
+ QRgb c;
+ if (i == begin) {
+ c = begin->second.rgba();
+ } else if (i == end) {
+ c = (end - 1)->second.rgba();
+ } else {
+ const QGradientStop &s1 = *(i - 1);
+ const QGradientStop &s2 = *i;
+ qreal p1 = s1.first;
+ qreal p2 = s2.first;
+ QRgb c1 = s1.second.rgba();
+ QRgb c2 = s2.second.rgba();
+ int idist = 256 * (p - p1) / (p2 - p1);
+ int dist = 256 - idist;
+ c = qRgba(INTERPOLATE_PIXEL_256(qRed(c1), dist, qRed(c2), idist),
+ INTERPOLATE_PIXEL_256(qGreen(c1), dist, qGreen(c2), idist),
+ INTERPOLATE_PIXEL_256(qBlue(c1), dist, qBlue(c2), idist),
+ INTERPOLATE_PIXEL_256(qAlpha(c1), dist, qAlpha(c2), idist));
+ }
+
+ out[0] = qt_mac_convert_color_to_cg(qRed(c));
+ out[1] = qt_mac_convert_color_to_cg(qGreen(c));
+ out[2] = qt_mac_convert_color_to_cg(qBlue(c));
+ out[3] = qt_mac_convert_color_to_cg(qAlpha(c));
+}
+#endif
+
+//clipping handling
+void QCoreGraphicsPaintEnginePrivate::resetClip()
+{
+ static bool inReset = false;
+ if (inReset)
+ return;
+ inReset = true;
+
+ CGAffineTransform old_xform = CGContextGetCTM(hd);
+
+ //setup xforms
+ CGContextConcatCTM(hd, CGAffineTransformInvert(old_xform));
+ while (stackCount > 0) {
+ restoreGraphicsState();
+ }
+ saveGraphicsState();
+ inReset = false;
+ //reset xforms
+ CGContextConcatCTM(hd, CGAffineTransformInvert(CGContextGetCTM(hd)));
+ CGContextConcatCTM(hd, old_xform);
+}
+
+static CGRect qt_mac_compose_rect(const QRectF &r, float off=0)
+{
+ return CGRectMake(r.x()+off, r.y()+off, r.width(), r.height());
+}
+
+static CGMutablePathRef qt_mac_compose_path(const QPainterPath &p, float off=0)
+{
+ CGMutablePathRef ret = CGPathCreateMutable();
+ QPointF startPt;
+ for (int i=0; i<p.elementCount(); ++i) {
+ const QPainterPath::Element &elm = p.elementAt(i);
+ switch (elm.type) {
+ case QPainterPath::MoveToElement:
+ if(i > 0
+ && p.elementAt(i - 1).x == startPt.x()
+ && p.elementAt(i - 1).y == startPt.y())
+ CGPathCloseSubpath(ret);
+ startPt = QPointF(elm.x, elm.y);
+ CGPathMoveToPoint(ret, 0, elm.x+off, elm.y+off);
+ break;
+ case QPainterPath::LineToElement:
+ CGPathAddLineToPoint(ret, 0, elm.x+off, elm.y+off);
+ break;
+ case QPainterPath::CurveToElement:
+ Q_ASSERT(p.elementAt(i+1).type == QPainterPath::CurveToDataElement);
+ Q_ASSERT(p.elementAt(i+2).type == QPainterPath::CurveToDataElement);
+ CGPathAddCurveToPoint(ret, 0,
+ elm.x+off, elm.y+off,
+ p.elementAt(i+1).x+off, p.elementAt(i+1).y+off,
+ p.elementAt(i+2).x+off, p.elementAt(i+2).y+off);
+ i+=2;
+ break;
+ default:
+ qFatal("QCoreGraphicsPaintEngine::drawPath(), unhandled type: %d", elm.type);
+ break;
+ }
+ }
+ if(!p.isEmpty()
+ && p.elementAt(p.elementCount() - 1).x == startPt.x()
+ && p.elementAt(p.elementCount() - 1).y == startPt.y())
+ CGPathCloseSubpath(ret);
+ return ret;
+}
+
+CGColorSpaceRef QCoreGraphicsPaintEngine::m_genericColorSpace = 0;
+QHash<CGDirectDisplayID, CGColorSpaceRef> QCoreGraphicsPaintEngine::m_displayColorSpaceHash;
+bool QCoreGraphicsPaintEngine::m_postRoutineRegistered = false;
+
+CGColorSpaceRef QCoreGraphicsPaintEngine::macGenericColorSpace()
+{
+#if 0
+ if (!m_genericColorSpace) {
+#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4
+ if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_4) {
+ m_genericColorSpace = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB);
+ } else
+#endif
+ {
+ m_genericColorSpace = CGColorSpaceCreateDeviceRGB();
+ }
+ if (!m_postRoutineRegistered) {
+ m_postRoutineRegistered = true;
+ qAddPostRoutine(QCoreGraphicsPaintEngine::cleanUpMacColorSpaces);
+ }
+ }
+ return m_genericColorSpace;
+#else
+ // Just return the main display colorspace for the moment.
+ return macDisplayColorSpace();
+#endif
+}
+
+/*
+ Ideally, we should pass the widget in here, and use CGGetDisplaysWithRect() etc.
+ to support multiple displays correctly.
+*/
+CGColorSpaceRef QCoreGraphicsPaintEngine::macDisplayColorSpace(const QWidget *widget)
+{
+ CGColorSpaceRef colorSpace;
+
+ CGDirectDisplayID displayID;
+ CMProfileRef displayProfile = 0;
+ if (widget == 0) {
+ displayID = CGMainDisplayID();
+ } else {
+ const QRect &qrect = widget->window()->geometry();
+ CGRect rect = CGRectMake(qrect.x(), qrect.y(), qrect.width(), qrect.height());
+ CGDisplayCount throwAway;
+ CGDisplayErr dErr = CGGetDisplaysWithRect(rect, 1, &displayID, &throwAway);
+ if (dErr != kCGErrorSuccess)
+ return macDisplayColorSpace(0); // fall back on main display
+ }
+ if ((colorSpace = m_displayColorSpaceHash.value(displayID)))
+ return colorSpace;
+
+ CMError err = CMGetProfileByAVID((CMDisplayIDType)displayID, &displayProfile);
+ if (err == noErr) {
+ colorSpace = CGColorSpaceCreateWithPlatformColorSpace(displayProfile);
+ } else if (widget) {
+ return macDisplayColorSpace(0); // fall back on main display
+ }
+
+ if (colorSpace == 0)
+ colorSpace = CGColorSpaceCreateDeviceRGB();
+
+ m_displayColorSpaceHash.insert(displayID, colorSpace);
+ CMCloseProfile(displayProfile);
+ if (!m_postRoutineRegistered) {
+ m_postRoutineRegistered = true;
+ qAddPostRoutine(QCoreGraphicsPaintEngine::cleanUpMacColorSpaces);
+ }
+ return colorSpace;
+}
+
+void QCoreGraphicsPaintEngine::cleanUpMacColorSpaces()
+{
+ if (m_genericColorSpace) {
+ CFRelease(m_genericColorSpace);
+ m_genericColorSpace = 0;
+ }
+ QHash<CGDirectDisplayID, CGColorSpaceRef>::const_iterator it = m_displayColorSpaceHash.constBegin();
+ while (it != m_displayColorSpaceHash.constEnd()) {
+ if (it.value())
+ CFRelease(it.value());
+ ++it;
+ }
+ m_displayColorSpaceHash.clear();
+}
+
+void qt_mac_clip_cg(CGContextRef hd, const QRegion &rgn, CGAffineTransform *orig_xform)
+{
+ CGAffineTransform old_xform = CGAffineTransformIdentity;
+ if(orig_xform) { //setup xforms
+ old_xform = CGContextGetCTM(hd);
+ CGContextConcatCTM(hd, CGAffineTransformInvert(old_xform));
+ CGContextConcatCTM(hd, *orig_xform);
+ }
+
+ //do the clipping
+ CGContextBeginPath(hd);
+ if(rgn.isEmpty()) {
+ CGContextAddRect(hd, CGRectMake(0, 0, 0, 0));
+ } else {
+#if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5)
+ if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_5) {
+ QCFType<HIMutableShapeRef> shape = rgn.toHIMutableShape();
+ Q_ASSERT(!HIShapeIsEmpty(shape));
+ HIShapeReplacePathInCGContext(shape, hd);
+ } else
+#endif
+ {
+ QVector<QRect> rects = rgn.rects();
+ const int count = rects.size();
+ for(int i = 0; i < count; i++) {
+ const QRect &r = rects[i];
+ CGRect mac_r = CGRectMake(r.x(), r.y(), r.width(), r.height());
+ CGContextAddRect(hd, mac_r);
+ }
+ }
+
+ }
+ CGContextClip(hd);
+
+ if(orig_xform) {//reset xforms
+ CGContextConcatCTM(hd, CGAffineTransformInvert(CGContextGetCTM(hd)));
+ CGContextConcatCTM(hd, old_xform);
+ }
+}
+
+
+//pattern handling (tiling)
+#if 1
+# define QMACPATTERN_MASK_MULTIPLIER 32
+#else
+# define QMACPATTERN_MASK_MULTIPLIER 1
+#endif
+class QMacPattern
+{
+public:
+ QMacPattern() : as_mask(false), pdev(0), image(0) { data.bytes = 0; }
+ ~QMacPattern() { CGImageRelease(image); }
+ int width() {
+ if(image)
+ return CGImageGetWidth(image);
+ if(data.bytes)
+ return 8*QMACPATTERN_MASK_MULTIPLIER;
+ return data.pixmap.width();
+ }
+ int height() {
+ if(image)
+ return CGImageGetHeight(image);
+ if(data.bytes)
+ return 8*QMACPATTERN_MASK_MULTIPLIER;
+ return data.pixmap.height();
+ }
+
+ //input
+ QColor foreground;
+ bool as_mask;
+ struct {
+ QPixmap pixmap;
+ const uchar *bytes;
+ } data;
+ QPaintDevice *pdev;
+ //output
+ CGImageRef image;
+};
+static void qt_mac_draw_pattern(void *info, CGContextRef c)
+{
+ QMacPattern *pat = (QMacPattern*)info;
+ int w = 0, h = 0;
+ bool isBitmap = (pat->data.pixmap.depth() == 1);
+ if(!pat->image) { //lazy cache
+ if(pat->as_mask) {
+ Q_ASSERT(pat->data.bytes);
+ w = h = 8;
+#if (QMACPATTERN_MASK_MULTIPLIER == 1)
+ CGDataProviderRef provider = CGDataProviderCreateWithData(0, pat->data.bytes, w*h, 0);
+ pat->image = CGImageMaskCreate(w, h, 1, 1, 1, provider, 0, false);
+ CGDataProviderRelease(provider);
+#else
+ const int numBytes = (w*h)/sizeof(uchar);
+ uchar xor_bytes[numBytes];
+ for(int i = 0; i < numBytes; ++i)
+ xor_bytes[i] = pat->data.bytes[i] ^ 0xFF;
+ CGDataProviderRef provider = CGDataProviderCreateWithData(0, xor_bytes, w*h, 0);
+ CGImageRef swatch = CGImageMaskCreate(w, h, 1, 1, 1, provider, 0, false);
+ CGDataProviderRelease(provider);
+
+ const QColor c0(0, 0, 0, 0), c1(255, 255, 255, 255);
+ QPixmap pm(w*QMACPATTERN_MASK_MULTIPLIER, h*QMACPATTERN_MASK_MULTIPLIER);
+ pm.fill(c0);
+ CGContextRef pm_ctx = qt_mac_cg_context(&pm);
+ CGContextSetFillColorWithColor(c, cgColorForQColor(c1, pat->pdev));
+ CGRect rect = CGRectMake(0, 0, w, h);
+ for(int x = 0; x < QMACPATTERN_MASK_MULTIPLIER; ++x) {
+ rect.origin.x = x * w;
+ for(int y = 0; y < QMACPATTERN_MASK_MULTIPLIER; ++y) {
+ rect.origin.y = y * h;
+ qt_mac_drawCGImage(pm_ctx, &rect, swatch);
+ }
+ }
+ pat->image = qt_mac_create_imagemask(pm, pm.rect());
+ CGImageRelease(swatch);
+ CGContextRelease(pm_ctx);
+ w *= QMACPATTERN_MASK_MULTIPLIER;
+ h *= QMACPATTERN_MASK_MULTIPLIER;
+#endif
+ } else {
+ w = pat->data.pixmap.width();
+ h = pat->data.pixmap.height();
+ if (isBitmap)
+ pat->image = qt_mac_create_imagemask(pat->data.pixmap, pat->data.pixmap.rect());
+ else
+ pat->image = (CGImageRef)pat->data.pixmap.macCGHandle();
+ }
+ } else {
+ w = CGImageGetWidth(pat->image);
+ h = CGImageGetHeight(pat->image);
+ }
+
+ //draw
+ bool needRestore = false;
+ if (CGImageIsMask(pat->image)) {
+ CGContextSaveGState(c);
+ CGContextSetFillColorWithColor(c, cgColorForQColor(pat->foreground, pat->pdev));
+ }
+ CGRect rect = CGRectMake(0, 0, w, h);
+ qt_mac_drawCGImage(c, &rect, pat->image);
+ if(needRestore)
+ CGContextRestoreGState(c);
+}
+static void qt_mac_dispose_pattern(void *info)
+{
+ QMacPattern *pat = (QMacPattern*)info;
+ delete pat;
+}
+
+/*****************************************************************************
+ QCoreGraphicsPaintEngine member functions
+ *****************************************************************************/
+
+inline static QPaintEngine::PaintEngineFeatures qt_mac_cg_features()
+{
+ QPaintEngine::PaintEngineFeatures ret(QPaintEngine::AllFeatures
+ & ~QPaintEngine::PaintOutsidePaintEvent
+ & ~QPaintEngine::PerspectiveTransform
+ & ~QPaintEngine::ConicalGradientFill
+ & ~QPaintEngine::LinearGradientFill
+ & ~QPaintEngine::RadialGradientFill
+ & ~QPaintEngine::BrushStroke);
+
+#if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4)
+ if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_4) {
+ ;
+ } else
+#endif
+ {
+ ret &= ~QPaintEngine::BlendModes;
+ }
+ return ret;
+}
+
+QCoreGraphicsPaintEngine::QCoreGraphicsPaintEngine()
+: QPaintEngine(*(new QCoreGraphicsPaintEnginePrivate), qt_mac_cg_features())
+{
+}
+
+QCoreGraphicsPaintEngine::QCoreGraphicsPaintEngine(QPaintEnginePrivate &dptr)
+: QPaintEngine(dptr, qt_mac_cg_features())
+{
+}
+
+QCoreGraphicsPaintEngine::~QCoreGraphicsPaintEngine()
+{
+}
+
+bool
+QCoreGraphicsPaintEngine::begin(QPaintDevice *pdev)
+{
+ Q_D(QCoreGraphicsPaintEngine);
+ if(isActive()) { // already active painting
+ qWarning("QCoreGraphicsPaintEngine::begin: Painter already active");
+ return false;
+ }
+
+ //initialization
+ d->pdev = pdev;
+ d->complexXForm = false;
+ d->cosmeticPen = QCoreGraphicsPaintEnginePrivate::CosmeticSetPenWidth;
+ d->cosmeticPenSize = 1;
+ d->current.clipEnabled = false;
+ d->pixelSize = QPoint(1,1);
+ d->hd = qt_mac_cg_context(pdev);
+ if(d->hd) {
+ d->saveGraphicsState();
+ d->orig_xform = CGContextGetCTM(d->hd);
+ if (d->shading) {
+ CGShadingRelease(d->shading);
+ d->shading = 0;
+ }
+ d->setClip(0); //clear the context's clipping
+ }
+
+ setActive(true);
+
+ if(d->pdev->devType() == QInternal::Widget) { // device is a widget
+ QWidget *w = (QWidget*)d->pdev;
+ bool unclipped = w->testAttribute(Qt::WA_PaintUnclipped);
+
+ if((w->windowType() == Qt::Desktop)) {
+ if(!unclipped)
+ qWarning("QCoreGraphicsPaintEngine::begin: Does not support clipped desktop on Mac OS X");
+ // ## need to do [qt_mac_window_for(w) makeKeyAndOrderFront]; (need to rename the file)
+ } else if(unclipped) {
+ qWarning("QCoreGraphicsPaintEngine::begin: Does not support unclipped painting");
+ }
+ } else if(d->pdev->devType() == QInternal::Pixmap) { // device is a pixmap
+ QPixmap *pm = (QPixmap*)d->pdev;
+ if(pm->isNull()) {
+ qWarning("QCoreGraphicsPaintEngine::begin: Cannot paint null pixmap");
+ end();
+ return false;
+ }
+ }
+
+ setDirty(QPaintEngine::DirtyPen);
+ setDirty(QPaintEngine::DirtyBrush);
+ setDirty(QPaintEngine::DirtyBackground);
+ setDirty(QPaintEngine::DirtyHints);
+ return true;
+}
+
+bool
+QCoreGraphicsPaintEngine::end()
+{
+ Q_D(QCoreGraphicsPaintEngine);
+ setActive(false);
+ if(d->pdev->devType() == QInternal::Widget && static_cast<QWidget*>(d->pdev)->windowType() == Qt::Desktop) {
+#ifndef QT_MAC_USE_COCOA
+ HideWindow(qt_mac_window_for(static_cast<QWidget*>(d->pdev)));
+#else
+// // ### need to do [qt_mac_window_for(static_cast<QWidget *>(d->pdev)) orderOut]; (need to rename)
+#endif
+
+ }
+ if(d->shading) {
+ CGShadingRelease(d->shading);
+ d->shading = 0;
+ }
+ d->pdev = 0;
+ if(d->hd) {
+ d->restoreGraphicsState();
+ CGContextSynchronize(d->hd);
+ CGContextRelease(d->hd);
+ d->hd = 0;
+ }
+ return true;
+}
+
+void
+QCoreGraphicsPaintEngine::updateState(const QPaintEngineState &state)
+{
+ Q_D(QCoreGraphicsPaintEngine);
+ QPaintEngine::DirtyFlags flags = state.state();
+
+ if (flags & DirtyTransform)
+ updateMatrix(state.transform());
+
+ if (flags & DirtyClipEnabled) {
+ if (state.isClipEnabled())
+ updateClipPath(painter()->clipPath(), Qt::ReplaceClip);
+ else
+ updateClipPath(QPainterPath(), Qt::NoClip);
+ }
+
+ if (flags & DirtyClipPath) {
+ updateClipPath(state.clipPath(), state.clipOperation());
+ } else if (flags & DirtyClipRegion) {
+ updateClipRegion(state.clipRegion(), state.clipOperation());
+ }
+
+ // If the clip has changed we need to update all other states
+ // too, since they are included in the system context on OSX,
+ // and changing the clip resets that context back to scratch.
+ if (flags & (DirtyClipPath | DirtyClipRegion | DirtyClipEnabled))
+ flags |= AllDirty;
+
+ if (flags & DirtyPen)
+ updatePen(state.pen());
+ if (flags & (DirtyBrush|DirtyBrushOrigin))
+ updateBrush(state.brush(), state.brushOrigin());
+ if (flags & DirtyFont)
+ updateFont(state.font());
+ if (flags & DirtyOpacity)
+ updateOpacity(state.opacity());
+ if (flags & DirtyHints)
+ updateRenderHints(state.renderHints());
+ if (flags & DirtyCompositionMode)
+ updateCompositionMode(state.compositionMode());
+
+ if (flags & (DirtyPen | DirtyTransform)) {
+ if (!d->current.pen.isCosmetic()) {
+ d->cosmeticPen = QCoreGraphicsPaintEnginePrivate::CosmeticNone;
+ } else if (d->current.transform.m11() < d->current.transform.m22()-1.0 ||
+ d->current.transform.m11() > d->current.transform.m22()+1.0) {
+ d->cosmeticPen = QCoreGraphicsPaintEnginePrivate::CosmeticTransformPath;
+ d->cosmeticPenSize = d->adjustPenWidth(d->current.pen.widthF());
+ if (!d->cosmeticPenSize)
+ d->cosmeticPenSize = 1.0;
+ } else {
+ d->cosmeticPen = QCoreGraphicsPaintEnginePrivate::CosmeticSetPenWidth;
+ static const float sqrt2 = sqrt(2);
+ qreal width = d->current.pen.widthF();
+ if (!width)
+ width = 1;
+ d->cosmeticPenSize = sqrt(pow(d->pixelSize.y(), 2) + pow(d->pixelSize.x(), 2)) / sqrt2 * width;
+ }
+ }
+}
+
+void
+QCoreGraphicsPaintEngine::updatePen(const QPen &pen)
+{
+ Q_D(QCoreGraphicsPaintEngine);
+ Q_ASSERT(isActive());
+ d->current.pen = pen;
+ d->setStrokePen(pen);
+}
+
+void
+QCoreGraphicsPaintEngine::updateBrush(const QBrush &brush, const QPointF &brushOrigin)
+{
+ Q_D(QCoreGraphicsPaintEngine);
+ Q_ASSERT(isActive());
+ d->current.brush = brush;
+
+#ifdef QT_MAC_USE_NATIVE_GRADIENTS
+ // Quartz supports only pad spread
+ if (const QGradient *gradient = brush.gradient()) {
+ if (drawGradientNatively(gradient)) {
+ gccaps |= QPaintEngine::LinearGradientFill | QPaintEngine::RadialGradientFill;
+ } else {
+ gccaps &= ~(QPaintEngine::LinearGradientFill | QPaintEngine::RadialGradientFill);
+ }
+ }
+#endif
+
+ if (d->shading) {
+ CGShadingRelease(d->shading);
+ d->shading = 0;
+ }
+ d->setFillBrush(brushOrigin);
+}
+
+void
+QCoreGraphicsPaintEngine::updateOpacity(qreal opacity)
+{
+ Q_D(QCoreGraphicsPaintEngine);
+ CGContextSetAlpha(d->hd, opacity);
+}
+
+void
+QCoreGraphicsPaintEngine::updateFont(const QFont &)
+{
+ Q_D(QCoreGraphicsPaintEngine);
+ Q_ASSERT(isActive());
+ updatePen(d->current.pen);
+}
+
+void
+QCoreGraphicsPaintEngine::updateMatrix(const QTransform &transform)
+{
+ Q_D(QCoreGraphicsPaintEngine);
+ Q_ASSERT(isActive());
+
+ if (qt_is_nan(transform.m11()) || qt_is_nan(transform.m12()) || qt_is_nan(transform.m13())
+ || qt_is_nan(transform.m21()) || qt_is_nan(transform.m22()) || qt_is_nan(transform.m23())
+ || qt_is_nan(transform.m31()) || qt_is_nan(transform.m32()) || qt_is_nan(transform.m33()))
+ return;
+
+ d->current.transform = transform;
+ d->setTransform(transform.isIdentity() ? 0 : &transform);
+ d->complexXForm = (transform.m11() != 1 || transform.m22() != 1
+ || transform.m12() != 0 || transform.m21() != 0);
+ d->pixelSize = d->devicePixelSize(d->hd);
+}
+
+void
+QCoreGraphicsPaintEngine::updateClipPath(const QPainterPath &p, Qt::ClipOperation op)
+{
+ Q_D(QCoreGraphicsPaintEngine);
+ Q_ASSERT(isActive());
+ if(op == Qt::NoClip) {
+ if(d->current.clipEnabled) {
+ d->current.clipEnabled = false;
+ d->current.clip = QRegion();
+ d->setClip(0);
+ }
+ } else {
+ if(!d->current.clipEnabled)
+ op = Qt::ReplaceClip;
+ d->current.clipEnabled = true;
+ QRegion clipRegion(p.toFillPolygon().toPolygon(), p.fillRule());
+ if(op == Qt::ReplaceClip) {
+ d->current.clip = clipRegion;
+ d->setClip(0);
+ if(p.isEmpty()) {
+ CGRect rect = CGRectMake(0, 0, 0, 0);
+ CGContextClipToRect(d->hd, rect);
+ } else {
+ CGMutablePathRef path = qt_mac_compose_path(p);
+ CGContextBeginPath(d->hd);
+ CGContextAddPath(d->hd, path);
+ if(p.fillRule() == Qt::WindingFill)
+ CGContextClip(d->hd);
+ else
+ CGContextEOClip(d->hd);
+ CGPathRelease(path);
+ }
+ } else if(op == Qt::IntersectClip) {
+ d->current.clip = d->current.clip.intersected(clipRegion);
+ d->setClip(&d->current.clip);
+ } else if(op == Qt::UniteClip) {
+ d->current.clip = d->current.clip.united(clipRegion);
+ d->setClip(&d->current.clip);
+ }
+ }
+}
+
+void
+QCoreGraphicsPaintEngine::updateClipRegion(const QRegion &clipRegion, Qt::ClipOperation op)
+{
+ Q_D(QCoreGraphicsPaintEngine);
+ Q_ASSERT(isActive());
+ if(op == Qt::NoClip) {
+ d->current.clipEnabled = false;
+ d->current.clip = QRegion();
+ d->setClip(0);
+ } else {
+ if(!d->current.clipEnabled)
+ op = Qt::ReplaceClip;
+ d->current.clipEnabled = true;
+ if(op == Qt::IntersectClip)
+ d->current.clip = d->current.clip.intersected(clipRegion);
+ else if(op == Qt::ReplaceClip)
+ d->current.clip = clipRegion;
+ else if(op == Qt::UniteClip)
+ d->current.clip = d->current.clip.united(clipRegion);
+ d->setClip(&d->current.clip);
+ }
+}
+
+void
+QCoreGraphicsPaintEngine::drawPath(const QPainterPath &p)
+{
+ Q_D(QCoreGraphicsPaintEngine);
+ Q_ASSERT(isActive());
+
+ if (state->compositionMode() == QPainter::CompositionMode_Destination)
+ return;
+
+ CGMutablePathRef path = qt_mac_compose_path(p);
+ uchar ops = QCoreGraphicsPaintEnginePrivate::CGStroke;
+ if(p.fillRule() == Qt::WindingFill)
+ ops |= QCoreGraphicsPaintEnginePrivate::CGFill;
+ else
+ ops |= QCoreGraphicsPaintEnginePrivate::CGEOFill;
+ CGContextBeginPath(d->hd);
+ d->drawPath(ops, path);
+ CGPathRelease(path);
+}
+
+void
+QCoreGraphicsPaintEngine::drawRects(const QRectF *rects, int rectCount)
+{
+ Q_D(QCoreGraphicsPaintEngine);
+ Q_ASSERT(isActive());
+
+ if (state->compositionMode() == QPainter::CompositionMode_Destination)
+ return;
+
+ for (int i=0; i<rectCount; ++i) {
+ QRectF r = rects[i];
+
+ CGMutablePathRef path = CGPathCreateMutable();
+ CGPathAddRect(path, 0, qt_mac_compose_rect(r));
+ d->drawPath(QCoreGraphicsPaintEnginePrivate::CGFill|QCoreGraphicsPaintEnginePrivate::CGStroke,
+ path);
+ CGPathRelease(path);
+ }
+}
+
+void
+QCoreGraphicsPaintEngine::drawPoints(const QPointF *points, int pointCount)
+{
+ Q_D(QCoreGraphicsPaintEngine);
+ Q_ASSERT(isActive());
+
+ if (state->compositionMode() == QPainter::CompositionMode_Destination)
+ return;
+
+ if (d->current.pen.capStyle() == Qt::FlatCap)
+ CGContextSetLineCap(d->hd, kCGLineCapSquare);
+
+ CGMutablePathRef path = CGPathCreateMutable();
+ for(int i=0; i < pointCount; i++) {
+ float x = points[i].x(), y = points[i].y();
+ CGPathMoveToPoint(path, 0, x, y);
+ CGPathAddLineToPoint(path, 0, x+0.001, y);
+ }
+
+ bool doRestore = false;
+ if(d->cosmeticPen == QCoreGraphicsPaintEnginePrivate::CosmeticNone && !(state->renderHints() & QPainter::Antialiasing)) {
+ //we don't want adjusted pens for point rendering
+ doRestore = true;
+ d->saveGraphicsState();
+ CGContextSetLineWidth(d->hd, d->current.pen.widthF());
+ }
+ d->drawPath(QCoreGraphicsPaintEnginePrivate::CGStroke, path);
+ if (doRestore)
+ d->restoreGraphicsState();
+ CGPathRelease(path);
+ if (d->current.pen.capStyle() == Qt::FlatCap)
+ CGContextSetLineCap(d->hd, kCGLineCapButt);
+}
+
+void
+QCoreGraphicsPaintEngine::drawEllipse(const QRectF &r)
+{
+ Q_D(QCoreGraphicsPaintEngine);
+ Q_ASSERT(isActive());
+
+ if (state->compositionMode() == QPainter::CompositionMode_Destination)
+ return;
+
+ CGMutablePathRef path = CGPathCreateMutable();
+ CGAffineTransform transform = CGAffineTransformMakeScale(r.width() / r.height(), 1);
+ CGPathAddArc(path, &transform,(r.x() + (r.width() / 2)) / (r.width() / r.height()),
+ r.y() + (r.height() / 2), r.height() / 2, 0, (2 * M_PI), false);
+ d->drawPath(QCoreGraphicsPaintEnginePrivate::CGFill | QCoreGraphicsPaintEnginePrivate::CGStroke,
+ path);
+ CGPathRelease(path);
+}
+
+void
+QCoreGraphicsPaintEngine::drawPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode)
+{
+ Q_D(QCoreGraphicsPaintEngine);
+ Q_ASSERT(isActive());
+
+ if (state->compositionMode() == QPainter::CompositionMode_Destination)
+ return;
+
+ CGMutablePathRef path = CGPathCreateMutable();
+ CGPathMoveToPoint(path, 0, points[0].x(), points[0].y());
+ for(int x = 1; x < pointCount; ++x)
+ CGPathAddLineToPoint(path, 0, points[x].x(), points[x].y());
+ if(mode != PolylineMode && points[0] != points[pointCount-1])
+ CGPathAddLineToPoint(path, 0, points[0].x(), points[0].y());
+ uint op = QCoreGraphicsPaintEnginePrivate::CGStroke;
+ if (mode != PolylineMode)
+ op |= mode == OddEvenMode ? QCoreGraphicsPaintEnginePrivate::CGEOFill
+ : QCoreGraphicsPaintEnginePrivate::CGFill;
+ d->drawPath(op, path);
+ CGPathRelease(path);
+}
+
+void
+QCoreGraphicsPaintEngine::drawLines(const QLineF *lines, int lineCount)
+{
+ Q_D(QCoreGraphicsPaintEngine);
+ Q_ASSERT(isActive());
+
+ if (state->compositionMode() == QPainter::CompositionMode_Destination)
+ return;
+
+ CGMutablePathRef path = CGPathCreateMutable();
+ for(int i = 0; i < lineCount; i++) {
+ const QPointF start = lines[i].p1(), end = lines[i].p2();
+ CGPathMoveToPoint(path, 0, start.x(), start.y());
+ CGPathAddLineToPoint(path, 0, end.x(), end.y());
+ }
+ d->drawPath(QCoreGraphicsPaintEnginePrivate::CGStroke, path);
+ CGPathRelease(path);
+}
+
+void QCoreGraphicsPaintEngine::drawPixmap(const QRectF &r, const QPixmap &pm, const QRectF &sr)
+{
+ Q_D(QCoreGraphicsPaintEngine);
+ Q_ASSERT(isActive());
+
+ if (state->compositionMode() == QPainter::CompositionMode_Destination)
+ return;
+
+ if(pm.isNull())
+ return;
+
+ bool differentSize = (QRectF(0, 0, pm.width(), pm.height()) != sr), doRestore = false;
+ CGRect rect = CGRectMake(qRound(r.x()), qRound(r.y()), qRound(r.width()), qRound(r.height()));
+ QCFType<CGImageRef> image;
+ bool isBitmap = (pm.depth() == 1);
+ if (isBitmap) {
+ doRestore = true;
+ d->saveGraphicsState();
+
+ const QColor &col = d->current.pen.color();
+ CGContextSetFillColorWithColor(d->hd, cgColorForQColor(col, d->pdev));
+ image = qt_mac_create_imagemask(pm, sr);
+ } else if (differentSize) {
+#if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4)
+ if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_4) {
+ CGImageRef img = (CGImageRef)pm.macCGHandle();
+ image = CGImageCreateWithImageInRect(img, CGRectMake(qRound(sr.x()), qRound(sr.y()), qRound(sr.width()), qRound(sr.height())));
+ CGImageRelease(img);
+ } else
+#endif
+ {
+ const int sx = qRound(sr.x()), sy = qRound(sr.y()), sw = qRound(sr.width()), sh = qRound(sr.height());
+ const QMacPixmapData *pmData = static_cast<const QMacPixmapData*>(pm.data);
+ quint32 *pantherData = pmData->pixels + (sy * pm.width() + sx);
+ QCFType<CGDataProviderRef> provider = CGDataProviderCreateWithData(0, pantherData, sw*sh*pmData->bytesPerRow, 0);
+ image = CGImageCreate(sw, sh, 8, 32, pmData->bytesPerRow,
+ macGenericColorSpace(),
+ kCGImageAlphaPremultipliedFirst, provider, 0, 0,
+ kCGRenderingIntentDefault);
+ }
+ } else {
+ image = (CGImageRef)pm.macCGHandle();
+ }
+ qt_mac_drawCGImage(d->hd, &rect, image);
+ if (doRestore)
+ d->restoreGraphicsState();
+}
+
+static void drawImageReleaseData (void *info, const void *, size_t)
+{
+ delete static_cast<QImage *>(info);
+}
+
+CGImageRef qt_mac_createCGImageFromQImage(const QImage &img, const QImage **imagePtr = 0)
+{
+ QImage *image;
+ if (img.depth() != 32)
+ image = new QImage(img.convertToFormat(QImage::Format_ARGB32_Premultiplied));
+ else
+ image = new QImage(img);
+
+#if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4)
+ uint cgflags = kCGImageAlphaNone;
+#else
+ CGImageAlphaInfo cgflags = kCGImageAlphaNone;
+#endif
+ switch (image->format()) {
+ case QImage::Format_ARGB32_Premultiplied:
+ cgflags = kCGImageAlphaPremultipliedFirst;
+ break;
+ case QImage::Format_ARGB32:
+ cgflags = kCGImageAlphaFirst;
+ break;
+ case QImage::Format_RGB32:
+ cgflags = kCGImageAlphaNoneSkipFirst;
+ default:
+ break;
+ }
+#if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4) && defined(kCGBitmapByteOrder32Host) //only needed because CGImage.h added symbols in the minor version
+ if(QSysInfo::MacintoshVersion >= QSysInfo::MV_10_4)
+ cgflags |= kCGBitmapByteOrder32Host;
+#endif
+ QCFType<CGDataProviderRef> dataProvider = CGDataProviderCreateWithData(image,
+ static_cast<const QImage *>(image)->bits(),
+ image->numBytes(),
+ drawImageReleaseData);
+ if (imagePtr)
+ *imagePtr = image;
+ return CGImageCreate(image->width(), image->height(), 8, 32,
+ image->bytesPerLine(),
+ QCoreGraphicsPaintEngine::macGenericColorSpace(),
+ cgflags, dataProvider, 0, false, kCGRenderingIntentDefault);
+
+}
+
+void QCoreGraphicsPaintEngine::drawImage(const QRectF &r, const QImage &img, const QRectF &sr,
+ Qt::ImageConversionFlags flags)
+{
+ Q_D(QCoreGraphicsPaintEngine);
+ Q_UNUSED(flags);
+ Q_ASSERT(isActive());
+
+ if (img.isNull() || state->compositionMode() == QPainter::CompositionMode_Destination)
+ return;
+
+ const QImage *image;
+ QCFType<CGImageRef> cgimage = qt_mac_createCGImageFromQImage(img, &image);
+ CGRect rect = CGRectMake(r.x(), r.y(), r.width(), r.height());
+ if (QRectF(0, 0, img.width(), img.height()) != sr) {
+#if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4)
+ if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_4) {
+ cgimage = CGImageCreateWithImageInRect(cgimage, CGRectMake(sr.x(), sr.y(),
+ sr.width(), sr.height()));
+ } else
+#endif
+ {
+ int sx = qRound(sr.x());
+ int sy = qRound(sr.y());
+ int sw = qRound(sr.width());
+ int sh = qRound(sr.height());
+ // Make another CGImage based on the part that we need.
+ const uchar *pantherData = image->scanLine(sy) + sx * sizeof(uint);
+ QCFType<CGDataProviderRef> dataProvider = CGDataProviderCreateWithData(0, pantherData,
+ sw * sh * image->bytesPerLine(), 0);
+ cgimage = CGImageCreate(sw, sh, 8, 32, image->bytesPerLine(),
+ macGenericColorSpace(),
+ CGImageGetAlphaInfo(cgimage), dataProvider, 0, false, kCGRenderingIntentDefault);
+ }
+ }
+ qt_mac_drawCGImage(d->hd, &rect, cgimage);
+}
+
+void QCoreGraphicsPaintEngine::initialize()
+{
+}
+
+void QCoreGraphicsPaintEngine::cleanup()
+{
+}
+
+CGContextRef
+QCoreGraphicsPaintEngine::handle() const
+{
+ return d_func()->hd;
+}
+
+void
+QCoreGraphicsPaintEngine::drawTiledPixmap(const QRectF &r, const QPixmap &pixmap,
+ const QPointF &p)
+{
+ Q_D(QCoreGraphicsPaintEngine);
+ Q_ASSERT(isActive());
+
+ if (state->compositionMode() == QPainter::CompositionMode_Destination)
+ return;
+
+ //save the old state
+ d->saveGraphicsState();
+
+ //setup the pattern
+ QMacPattern *qpattern = new QMacPattern;
+ qpattern->data.pixmap = pixmap;
+ qpattern->foreground = d->current.pen.color();
+ qpattern->pdev = d->pdev;
+ CGPatternCallbacks callbks;
+ callbks.version = 0;
+ callbks.drawPattern = qt_mac_draw_pattern;
+ callbks.releaseInfo = qt_mac_dispose_pattern;
+ const int width = qpattern->width(), height = qpattern->height();
+ CGAffineTransform trans = CGContextGetCTM(d->hd);
+ CGPatternRef pat = CGPatternCreate(qpattern, CGRectMake(0, 0, width, height),
+ trans, width, height,
+ kCGPatternTilingNoDistortion, true, &callbks);
+ CGColorSpaceRef cs = CGColorSpaceCreatePattern(0);
+ CGContextSetFillColorSpace(d->hd, cs);
+ CGFloat component = 1.0; //just one
+ CGContextSetFillPattern(d->hd, pat, &component);
+ CGSize phase = CGSizeApplyAffineTransform(CGSizeMake(-(p.x()-r.x()), -(p.y()-r.y())), trans);
+ CGContextSetPatternPhase(d->hd, phase);
+
+ //fill the rectangle
+ CGRect mac_rect = CGRectMake(r.x(), r.y(), r.width(), r.height());
+ CGContextFillRect(d->hd, mac_rect);
+
+ //restore the state
+ d->restoreGraphicsState();
+ //cleanup
+ CGColorSpaceRelease(cs);
+ CGPatternRelease(pat);
+}
+
+void QCoreGraphicsPaintEngine::drawTextItem(const QPointF &pos, const QTextItem &item)
+{
+ Q_D(QCoreGraphicsPaintEngine);
+ if (d->current.transform.type() == QTransform::TxProject
+#ifndef QMAC_NATIVE_GRADIENTS
+ || painter()->pen().brush().gradient() //Just let the base engine "emulate" the gradient
+#endif
+ ) {
+ QPaintEngine::drawTextItem(pos, item);
+ return;
+ }
+
+ if (state->compositionMode() == QPainter::CompositionMode_Destination)
+ return;
+
+ const QTextItemInt &ti = static_cast<const QTextItemInt &>(item);
+
+ QPen oldPen = painter()->pen();
+ QBrush oldBrush = painter()->brush();
+ QPointF oldBrushOrigin = painter()->brushOrigin();
+ updatePen(Qt::NoPen);
+ updateBrush(oldPen.brush(), QPointF(0, 0));
+
+ Q_ASSERT(type() == QPaintEngine::CoreGraphics);
+
+ QFontEngine *fe = ti.fontEngine;
+
+ const bool textAA = state->renderHints() & QPainter::TextAntialiasing && fe->fontDef.pointSize > qt_antialiasing_threshold && !(fe->fontDef.styleStrategy & QFont::NoAntialias);
+ const bool lineAA = state->renderHints() & QPainter::Antialiasing;
+ if(textAA != lineAA)
+ CGContextSetShouldAntialias(d->hd, textAA);
+
+ if (ti.glyphs.numGlyphs) {
+ switch (fe->type()) {
+ case QFontEngine::Mac:
+#ifdef QT_MAC_USE_COCOA
+ static_cast<QCoreTextFontEngine *>(fe)->draw(d->hd, pos.x(), pos.y(), ti, paintDevice()->height());
+#else
+ static_cast<QFontEngineMac *>(fe)->draw(d->hd, pos.x(), pos.y(), ti, paintDevice()->height());
+#endif
+ break;
+ case QFontEngine::Box:
+ d->drawBoxTextItem(pos, ti);
+ break;
+ default:
+ break;
+ }
+ }
+
+ if(textAA != lineAA)
+ CGContextSetShouldAntialias(d->hd, !textAA);
+
+ updatePen(oldPen);
+ updateBrush(oldBrush, oldBrushOrigin);
+}
+
+QPainter::RenderHints
+QCoreGraphicsPaintEngine::supportedRenderHints() const
+{
+ return QPainter::RenderHints(QPainter::Antialiasing | QPainter::TextAntialiasing | QPainter::SmoothPixmapTransform);
+}
+enum CGCompositeMode {
+ kCGCompositeModeClear = 0,
+ kCGCompositeModeCopy = 1,
+ kCGCompositeModeSourceOver = 2,
+ kCGCompositeModeSourceIn = 3,
+ kCGCompositeModeSourceOut = 4,
+ kCGCompositeModeSourceAtop = 5,
+ kCGCompositeModeDestinationOver = 6,
+ kCGCompositeModeDestinationIn = 7,
+ kCGCompositeModeDestinationOut = 8,
+ kCGCompositeModeDestinationAtop = 9,
+ kCGCompositeModeXOR = 10,
+ kCGCompositeModePlusDarker = 11, // (max (0, (1-d) + (1-s)))
+ kCGCompositeModePlusLighter = 12, // (min (1, s + d))
+ };
+extern "C" {
+ extern void CGContextSetCompositeOperation(CGContextRef, int);
+} // private function, but is in all versions of OS X.
+void
+QCoreGraphicsPaintEngine::updateCompositionMode(QPainter::CompositionMode mode)
+{
+#if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5)
+ if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_5) {
+ int cg_mode = kCGBlendModeNormal;
+ switch(mode) {
+ case QPainter::CompositionMode_Multiply:
+ cg_mode = kCGBlendModeMultiply;
+ break;
+ case QPainter::CompositionMode_Screen:
+ cg_mode = kCGBlendModeScreen;
+ break;
+ case QPainter::CompositionMode_Overlay:
+ cg_mode = kCGBlendModeOverlay;
+ break;
+ case QPainter::CompositionMode_Darken:
+ cg_mode = kCGBlendModeDarken;
+ break;
+ case QPainter::CompositionMode_Lighten:
+ cg_mode = kCGBlendModeLighten;
+ break;
+ case QPainter::CompositionMode_ColorDodge:
+ cg_mode = kCGBlendModeColorDodge;
+ break;
+ case QPainter::CompositionMode_ColorBurn:
+ cg_mode = kCGBlendModeColorBurn;
+ break;
+ case QPainter::CompositionMode_HardLight:
+ cg_mode = kCGBlendModeHardLight;
+ break;
+ case QPainter::CompositionMode_SoftLight:
+ cg_mode = kCGBlendModeSoftLight;
+ break;
+ case QPainter::CompositionMode_Difference:
+ cg_mode = kCGBlendModeDifference;
+ break;
+ case QPainter::CompositionMode_Exclusion:
+ cg_mode = kCGBlendModeExclusion;
+ break;
+ case QPainter::CompositionMode_Plus:
+ cg_mode = kCGBlendModePlusLighter;
+ break;
+ case QPainter::CompositionMode_SourceOver:
+ cg_mode = kCGBlendModeNormal;
+ break;
+ case QPainter::CompositionMode_DestinationOver:
+ cg_mode = kCGBlendModeDestinationOver;
+ break;
+ case QPainter::CompositionMode_Clear:
+ cg_mode = kCGBlendModeClear;
+ break;
+ case QPainter::CompositionMode_Source:
+ cg_mode = kCGBlendModeCopy;
+ break;
+ case QPainter::CompositionMode_Destination:
+ cg_mode = -1;
+ break;
+ case QPainter::CompositionMode_SourceIn:
+ cg_mode = kCGBlendModeSourceIn;
+ break;
+ case QPainter::CompositionMode_DestinationIn:
+ cg_mode = kCGCompositeModeDestinationIn;
+ break;
+ case QPainter::CompositionMode_SourceOut:
+ cg_mode = kCGBlendModeSourceOut;
+ break;
+ case QPainter::CompositionMode_DestinationOut:
+ cg_mode = kCGBlendModeDestinationOver;
+ break;
+ case QPainter::CompositionMode_SourceAtop:
+ cg_mode = kCGBlendModeSourceAtop;
+ break;
+ case QPainter::CompositionMode_DestinationAtop:
+ cg_mode = kCGBlendModeDestinationAtop;
+ break;
+ case QPainter::CompositionMode_Xor:
+ cg_mode = kCGBlendModeXOR;
+ break;
+ default:
+ break;
+ }
+ if (cg_mode > -1) {
+ CGContextSetBlendMode(d_func()->hd, CGBlendMode(cg_mode));
+ }
+ } else
+#endif
+ // The standard porter duff ops.
+ if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_3
+ && mode <= QPainter::CompositionMode_Xor) {
+ int cg_mode = kCGCompositeModeCopy;
+ switch (mode) {
+ case QPainter::CompositionMode_SourceOver:
+ cg_mode = kCGCompositeModeSourceOver;
+ break;
+ case QPainter::CompositionMode_DestinationOver:
+ cg_mode = kCGCompositeModeDestinationOver;
+ break;
+ case QPainter::CompositionMode_Clear:
+ cg_mode = kCGCompositeModeClear;
+ break;
+ default:
+ qWarning("QCoreGraphicsPaintEngine: Unhandled composition mode %d", (int)mode);
+ break;
+ case QPainter::CompositionMode_Source:
+ cg_mode = kCGCompositeModeCopy;
+ break;
+ case QPainter::CompositionMode_Destination:
+ cg_mode = CGCompositeMode(-1);
+ break;
+ case QPainter::CompositionMode_SourceIn:
+ cg_mode = kCGCompositeModeSourceIn;
+ break;
+ case QPainter::CompositionMode_DestinationIn:
+ cg_mode = kCGCompositeModeDestinationIn;
+ break;
+ case QPainter::CompositionMode_SourceOut:
+ cg_mode = kCGCompositeModeSourceOut;
+ break;
+ case QPainter::CompositionMode_DestinationOut:
+ cg_mode = kCGCompositeModeDestinationOut;
+ break;
+ case QPainter::CompositionMode_SourceAtop:
+ cg_mode = kCGCompositeModeSourceAtop;
+ break;
+ case QPainter::CompositionMode_DestinationAtop:
+ cg_mode = kCGCompositeModeDestinationAtop;
+ break;
+ case QPainter::CompositionMode_Xor:
+ cg_mode = kCGCompositeModeXOR;
+ break;
+ }
+ if (cg_mode > -1)
+ CGContextSetCompositeOperation(d_func()->hd, CGCompositeMode(cg_mode));
+ } else {
+#if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4)
+ bool needPrivateAPI = false;
+ if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_4) {
+ int cg_mode = kCGBlendModeNormal;
+ switch (mode) {
+ case QPainter::CompositionMode_Multiply:
+ cg_mode = kCGBlendModeMultiply;
+ break;
+ case QPainter::CompositionMode_Screen:
+ cg_mode = kCGBlendModeScreen;
+ break;
+ case QPainter::CompositionMode_Overlay:
+ cg_mode = kCGBlendModeOverlay;
+ break;
+ case QPainter::CompositionMode_Darken:
+ cg_mode = kCGBlendModeDarken;
+ break;
+ case QPainter::CompositionMode_Lighten:
+ cg_mode = kCGBlendModeLighten;
+ break;
+ case QPainter::CompositionMode_ColorDodge:
+ cg_mode = kCGBlendModeColorDodge;
+ break;
+ case QPainter::CompositionMode_ColorBurn:
+ cg_mode = kCGBlendModeColorBurn;
+ break;
+ case QPainter::CompositionMode_HardLight:
+ cg_mode = kCGBlendModeHardLight;
+ break;
+ case QPainter::CompositionMode_SoftLight:
+ cg_mode = kCGBlendModeSoftLight;
+ break;
+ case QPainter::CompositionMode_Difference:
+ cg_mode = kCGBlendModeDifference;
+ break;
+ case QPainter::CompositionMode_Exclusion:
+ cg_mode = kCGBlendModeExclusion;
+ break;
+ case QPainter::CompositionMode_Plus:
+ needPrivateAPI = true;
+ cg_mode = kCGCompositeModePlusLighter;
+ break;
+ default:
+ break;
+ }
+ if (!needPrivateAPI)
+ CGContextSetBlendMode(d_func()->hd, CGBlendMode(cg_mode));
+ else
+ CGContextSetCompositeOperation(d_func()->hd, CGCompositeMode(cg_mode));
+ }
+#endif
+ }
+}
+
+void
+QCoreGraphicsPaintEngine::updateRenderHints(QPainter::RenderHints hints)
+{
+ Q_D(QCoreGraphicsPaintEngine);
+ CGContextSetShouldAntialias(d->hd, hints & QPainter::Antialiasing);
+ CGContextSetInterpolationQuality(d->hd, (hints & QPainter::SmoothPixmapTransform) ?
+ kCGInterpolationHigh : kCGInterpolationNone);
+ CGContextSetShouldSmoothFonts(d->hd, hints & QPainter::TextAntialiasing);
+}
+
+/*
+ Returns the size of one device pixel in user-space coordinates.
+*/
+QPointF QCoreGraphicsPaintEnginePrivate::devicePixelSize(CGContextRef)
+{
+ QPointF p1 = current.transform.inverted().map(QPointF(0, 0));
+ QPointF p2 = current.transform.inverted().map(QPointF(1, 1));
+ return QPointF(qAbs(p2.x() - p1.x()), qAbs(p2.y() - p1.y()));
+}
+
+/*
+ Adjusts the pen width so we get correct line widths in the
+ non-transformed, aliased case.
+*/
+float QCoreGraphicsPaintEnginePrivate::adjustPenWidth(float penWidth)
+{
+ Q_Q(QCoreGraphicsPaintEngine);
+ float ret = penWidth;
+ if (!complexXForm && !(q->state->renderHints() & QPainter::Antialiasing)) {
+ if (penWidth < 2)
+ ret = 1;
+ else if (penWidth < 3)
+ ret = 1.5;
+ else
+ ret = penWidth -1;
+ }
+ return ret;
+}
+
+void
+QCoreGraphicsPaintEnginePrivate::setStrokePen(const QPen &pen)
+{
+ //pencap
+ CGLineCap cglinecap = kCGLineCapButt;
+ if(pen.capStyle() == Qt::SquareCap)
+ cglinecap = kCGLineCapSquare;
+ else if(pen.capStyle() == Qt::RoundCap)
+ cglinecap = kCGLineCapRound;
+ CGContextSetLineCap(hd, cglinecap);
+ CGContextSetLineWidth(hd, adjustPenWidth(pen.widthF()));
+
+ //join
+ CGLineJoin cglinejoin = kCGLineJoinMiter;
+ if(pen.joinStyle() == Qt::BevelJoin)
+ cglinejoin = kCGLineJoinBevel;
+ else if(pen.joinStyle() == Qt::RoundJoin)
+ cglinejoin = kCGLineJoinRound;
+ CGContextSetLineJoin(hd, cglinejoin);
+// CGContextSetMiterLimit(hd, pen.miterLimit());
+
+ //pen style
+ QVector<CGFloat> linedashes;
+ if(pen.style() == Qt::CustomDashLine) {
+ QVector<qreal> customs = pen.dashPattern();
+ for(int i = 0; i < customs.size(); ++i)
+ linedashes.append(customs.at(i));
+ } else if(pen.style() == Qt::DashLine) {
+ linedashes.append(3);
+ linedashes.append(1);
+ } else if(pen.style() == Qt::DotLine) {
+ linedashes.append(1);
+ linedashes.append(1);
+ } else if(pen.style() == Qt::DashDotLine) {
+ linedashes.append(3);
+ linedashes.append(1);
+ linedashes.append(1);
+ linedashes.append(1);
+ } else if(pen.style() == Qt::DashDotDotLine) {
+ linedashes.append(3);
+ linedashes.append(1);
+ linedashes.append(1);
+ linedashes.append(1);
+ linedashes.append(1);
+ linedashes.append(1);
+ }
+ const CGFloat cglinewidth = pen.widthF() <= 0.0f ? 1.0f : float(pen.widthF());
+ for(int i = 0; i < linedashes.size(); ++i) {
+ linedashes[i] *= cglinewidth;
+ if(cglinewidth < 3 && (cglinecap == kCGLineCapSquare || cglinecap == kCGLineCapRound)) {
+ if((i%2))
+ linedashes[i] += cglinewidth/2;
+ else
+ linedashes[i] -= cglinewidth/2;
+ }
+ }
+ CGContextSetLineDash(hd, pen.dashOffset() * cglinewidth, linedashes.data(), linedashes.size());
+
+ // color
+ CGContextSetStrokeColorWithColor(hd, cgColorForQColor(pen.color(), pdev));
+}
+
+// Add our own patterns here to deal with the fact that the coordinate system
+// is flipped vertically with Quartz2D.
+static const uchar *qt_mac_patternForBrush(int brushStyle)
+{
+ Q_ASSERT(brushStyle > Qt::SolidPattern && brushStyle < Qt::LinearGradientPattern);
+ static const uchar dense1_pat[] = { 0x00, 0x00, 0x44, 0x00, 0x00, 0x00, 0x44, 0x00 };
+ static const uchar dense2_pat[] = { 0x00, 0x22, 0x00, 0x88, 0x00, 0x22, 0x00, 0x88 };
+ static const uchar dense3_pat[] = { 0x11, 0xaa, 0x44, 0xaa, 0x11, 0xaa, 0x44, 0xaa };
+ static const uchar dense4_pat[] = { 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55 };
+ static const uchar dense5_pat[] = { 0xee, 0x55, 0xbb, 0x55, 0xee, 0x55, 0xbb, 0x55 };
+ static const uchar dense6_pat[] = { 0xff, 0xdd, 0xff, 0x77, 0xff, 0xdd, 0xff, 0x77 };
+ static const uchar dense7_pat[] = { 0xff, 0xff, 0xbb, 0xff, 0xff, 0xff, 0xbb, 0xff };
+ static const uchar hor_pat[] = { 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff };
+ static const uchar ver_pat[] = { 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef };
+ static const uchar cross_pat[] = { 0xef, 0xef, 0xef, 0xef, 0x00, 0xef, 0xef, 0xef };
+ static const uchar fdiag_pat[] = { 0x7f, 0xbf, 0xdf, 0xef, 0xf7, 0xfb, 0xfd, 0xfe };
+ static const uchar bdiag_pat[] = { 0xfe, 0xfd, 0xfb, 0xf7, 0xef, 0xdf, 0xbf, 0x7f };
+ static const uchar dcross_pat[] = { 0x7e, 0xbd, 0xdb, 0xe7, 0xe7, 0xdb, 0xbd, 0x7e };
+ static const uchar *const pat_tbl[] = {
+ dense1_pat, dense2_pat, dense3_pat, dense4_pat, dense5_pat,
+ dense6_pat, dense7_pat,
+ hor_pat, ver_pat, cross_pat, bdiag_pat, fdiag_pat, dcross_pat };
+ return pat_tbl[brushStyle - Qt::Dense1Pattern];
+}
+
+void QCoreGraphicsPaintEnginePrivate::setFillBrush(const QPointF &offset)
+{
+ // pattern
+ Qt::BrushStyle bs = current.brush.style();
+#ifdef QT_MAC_USE_NATIVE_GRADIENTS
+ if (bs == Qt::LinearGradientPattern || bs == Qt::RadialGradientPattern) {
+ const QGradient *grad = static_cast<const QGradient*>(current.brush.gradient());
+ if (drawGradientNatively(grad)) {
+ Q_ASSERT(grad->spread() == QGradient::PadSpread);
+
+ static const CGFloat domain[] = { 0.0f, +1.0f };
+ static const CGFunctionCallbacks callbacks = { 0, qt_mac_color_gradient_function, 0 };
+ CGFunctionRef fill_func = CGFunctionCreate(reinterpret_cast<void *>(&current.brush),
+ 1, domain, 4, 0, &callbacks);
+
+ CGColorSpaceRef colorspace = qt_mac_colorSpaceForDeviceType(pdev);
+ if (bs == Qt::LinearGradientPattern) {
+ const QLinearGradient *linearGrad = static_cast<const QLinearGradient *>(grad);
+ const QPointF start(linearGrad->start());
+ const QPointF stop(linearGrad->finalStop());
+ shading = CGShadingCreateAxial(colorspace, CGPointMake(start.x(), start.y()),
+ CGPointMake(stop.x(), stop.y()), fill_func, true, true);
+ } else {
+ Q_ASSERT(bs == Qt::RadialGradientPattern);
+ const QRadialGradient *radialGrad = static_cast<const QRadialGradient *>(grad);
+ QPointF center(radialGrad->center());
+ QPointF focal(radialGrad->focalPoint());
+ qreal radius = radialGrad->radius();
+ shading = CGShadingCreateRadial(colorspace, CGPointMake(focal.x(), focal.y()),
+ 0.0, CGPointMake(center.x(), center.y()), radius, fill_func, false, true);
+ }
+
+ CGFunctionRelease(fill_func);
+ }
+ } else
+#endif
+ if(bs != Qt::SolidPattern && bs != Qt::NoBrush
+#ifndef QT_MAC_USE_NATIVE_GRADIENTS
+ && (bs < Qt::LinearGradientPattern || bs > Qt::ConicalGradientPattern)
+#endif
+ )
+ {
+ QMacPattern *qpattern = new QMacPattern;
+ qpattern->pdev = pdev;
+ CGFloat components[4] = { 1.0, 1.0, 1.0, 1.0 };
+ CGColorSpaceRef base_colorspace = 0;
+ if(bs == Qt::TexturePattern) {
+ qpattern->data.pixmap = current.brush.texture();
+ if(qpattern->data.pixmap.isQBitmap()) {
+ const QColor &col = current.brush.color();
+ components[0] = qt_mac_convert_color_to_cg(col.red());
+ components[1] = qt_mac_convert_color_to_cg(col.green());
+ components[2] = qt_mac_convert_color_to_cg(col.blue());
+ base_colorspace = QCoreGraphicsPaintEngine::macGenericColorSpace();
+ }
+ } else {
+ qpattern->as_mask = true;
+
+ qpattern->data.bytes = qt_mac_patternForBrush(bs);
+ const QColor &col = current.brush.color();
+ components[0] = qt_mac_convert_color_to_cg(col.red());
+ components[1] = qt_mac_convert_color_to_cg(col.green());
+ components[2] = qt_mac_convert_color_to_cg(col.blue());
+ base_colorspace = QCoreGraphicsPaintEngine::macGenericColorSpace();
+ }
+ int width = qpattern->width(), height = qpattern->height();
+ qpattern->foreground = current.brush.color();
+
+ CGColorSpaceRef fill_colorspace = CGColorSpaceCreatePattern(base_colorspace);
+ CGContextSetFillColorSpace(hd, fill_colorspace);
+
+ CGAffineTransform xform = CGContextGetCTM(hd);
+ xform = CGAffineTransformConcat(qt_mac_convert_transform_to_cg(current.brush.transform()), xform);
+ xform = CGAffineTransformTranslate(xform, offset.x(), offset.y());
+
+ CGPatternCallbacks callbks;
+ callbks.version = 0;
+ callbks.drawPattern = qt_mac_draw_pattern;
+ callbks.releaseInfo = qt_mac_dispose_pattern;
+ CGPatternRef fill_pattern = CGPatternCreate(qpattern, CGRectMake(0, 0, width, height),
+ xform, width, height, kCGPatternTilingNoDistortion,
+ !base_colorspace, &callbks);
+ CGContextSetFillPattern(hd, fill_pattern, components);
+
+ CGPatternRelease(fill_pattern);
+ CGColorSpaceRelease(fill_colorspace);
+ } else if(bs != Qt::NoBrush) {
+ CGContextSetFillColorWithColor(hd, cgColorForQColor(current.brush.color(), pdev));
+ }
+}
+
+void
+QCoreGraphicsPaintEnginePrivate::setClip(const QRegion *rgn)
+{
+ Q_Q(QCoreGraphicsPaintEngine);
+ if(hd) {
+ resetClip();
+ QRegion sysClip = q->systemClip();
+ if(!sysClip.isEmpty())
+ qt_mac_clip_cg(hd, sysClip, &orig_xform);
+ if(rgn)
+ qt_mac_clip_cg(hd, *rgn, 0);
+ }
+}
+
+struct qt_mac_cg_transform_path {
+ CGMutablePathRef path;
+ CGAffineTransform transform;
+};
+
+void qt_mac_cg_transform_path_apply(void *info, const CGPathElement *element)
+{
+ Q_ASSERT(info && element);
+ qt_mac_cg_transform_path *t = (qt_mac_cg_transform_path*)info;
+ switch(element->type) {
+ case kCGPathElementMoveToPoint:
+ CGPathMoveToPoint(t->path, &t->transform, element->points[0].x, element->points[0].y);
+ break;
+ case kCGPathElementAddLineToPoint:
+ CGPathAddLineToPoint(t->path, &t->transform, element->points[0].x, element->points[0].y);
+ break;
+ case kCGPathElementAddQuadCurveToPoint:
+ CGPathAddQuadCurveToPoint(t->path, &t->transform, element->points[0].x, element->points[0].y,
+ element->points[1].x, element->points[1].y);
+ break;
+ case kCGPathElementAddCurveToPoint:
+ CGPathAddCurveToPoint(t->path, &t->transform, element->points[0].x, element->points[0].y,
+ element->points[1].x, element->points[1].y,
+ element->points[2].x, element->points[2].y);
+ break;
+ case kCGPathElementCloseSubpath:
+ CGPathCloseSubpath(t->path);
+ break;
+ default:
+ qDebug() << "Unhandled path transform type: " << element->type;
+ }
+}
+
+void QCoreGraphicsPaintEnginePrivate::drawPath(uchar ops, CGMutablePathRef path)
+{
+ Q_Q(QCoreGraphicsPaintEngine);
+ Q_ASSERT((ops & (CGFill | CGEOFill)) != (CGFill | CGEOFill)); //can't really happen
+ if((ops & (CGFill | CGEOFill))) {
+ if (shading) {
+ Q_ASSERT(path);
+ CGContextBeginPath(hd);
+ CGContextAddPath(hd, path);
+ saveGraphicsState();
+ if (ops & CGFill)
+ CGContextClip(hd);
+ else if (ops & CGEOFill)
+ CGContextEOClip(hd);
+ if (current.brush.gradient()->coordinateMode() == QGradient::ObjectBoundingMode) {
+ CGRect boundingBox = CGPathGetBoundingBox(path);
+ CGContextConcatCTM(hd,
+ CGAffineTransformMake(boundingBox.size.width, 0,
+ 0, boundingBox.size.height,
+ boundingBox.origin.x, boundingBox.origin.y));
+ }
+ CGContextDrawShading(hd, shading);
+ restoreGraphicsState();
+ ops &= ~CGFill;
+ ops &= ~CGEOFill;
+ } else if (current.brush.style() == Qt::NoBrush) {
+ ops &= ~CGFill;
+ ops &= ~CGEOFill;
+ }
+ }
+ if((ops & CGStroke) && current.pen.style() == Qt::NoPen)
+ ops &= ~CGStroke;
+
+ if(ops & (CGEOFill | CGFill)) {
+ CGContextBeginPath(hd);
+ CGContextAddPath(hd, path);
+ if (ops & CGEOFill) {
+ CGContextEOFillPath(hd);
+ } else {
+ CGContextFillPath(hd);
+ }
+ }
+
+ // Avoid saving and restoring the context if we can.
+ const bool needContextSave = (cosmeticPen != QCoreGraphicsPaintEnginePrivate::CosmeticNone ||
+ !(q->state->renderHints() & QPainter::Antialiasing));
+ if(ops & CGStroke) {
+ if (needContextSave)
+ saveGraphicsState();
+ CGContextBeginPath(hd);
+
+ // Translate a fraction of a pixel size in the y direction
+ // to make sure that primitives painted at pixel borders
+ // fills the right pixel. This is needed since the y xais
+ // in the Quartz coordinate system is inverted compared to Qt.
+ if (!(q->state->renderHints() & QPainter::Antialiasing)) {
+ if (current.pen.style() == Qt::SolidLine || current.pen.width() >= 3)
+ CGContextTranslateCTM(hd, double(pixelSize.x()) * 0.25, double(pixelSize.y()) * 0.25);
+ else if (current.pen.style() == Qt::DotLine && QSysInfo::MacintoshVersion == QSysInfo::MV_10_3)
+ ; // Do nothing.
+ else
+ CGContextTranslateCTM(hd, 0, double(pixelSize.y()) * 0.1);
+ }
+
+ if (cosmeticPen != QCoreGraphicsPaintEnginePrivate::CosmeticNone) {
+ // If antialiazing is enabled, use the cosmetic pen size directly.
+ if (q->state->renderHints() & QPainter::Antialiasing)
+ CGContextSetLineWidth(hd, cosmeticPenSize);
+ else if (current.pen.widthF() <= 1)
+ CGContextSetLineWidth(hd, cosmeticPenSize * 0.9f);
+ else
+ CGContextSetLineWidth(hd, cosmeticPenSize);
+ }
+ if(cosmeticPen == QCoreGraphicsPaintEnginePrivate::CosmeticTransformPath) {
+ qt_mac_cg_transform_path t;
+ t.transform = qt_mac_convert_transform_to_cg(current.transform);
+ t.path = CGPathCreateMutable();
+ CGPathApply(path, &t, qt_mac_cg_transform_path_apply); //transform the path
+ setTransform(0); //unset the context transform
+ CGContextSetLineWidth(hd, cosmeticPenSize);
+ CGContextAddPath(hd, t.path);
+ CGPathRelease(t.path);
+ } else {
+ CGContextAddPath(hd, path);
+ }
+
+ CGContextStrokePath(hd);
+ if (needContextSave)
+ restoreGraphicsState();
+ }
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/painting/qpaintengine_mac_p.h b/src/gui/painting/qpaintengine_mac_p.h
new file mode 100644
index 0000000000..298c145056
--- /dev/null
+++ b/src/gui/painting/qpaintengine_mac_p.h
@@ -0,0 +1,359 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QPAINTENGINE_MAC_P_H
+#define QPAINTENGINE_MAC_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 "QtGui/qpaintengine.h"
+#include "private/qt_mac_p.h"
+#include "private/qpaintengine_p.h"
+#include "private/qpolygonclipper_p.h"
+#include "QtCore/qhash.h"
+#ifndef QT_MAC_NO_QUICKDRAW
+#include <private/qwidget_p.h>
+#endif
+
+typedef struct CGColorSpace *CGColorSpaceRef;
+QT_BEGIN_NAMESPACE
+
+extern int qt_defaultDpi();
+extern int qt_defaultDpiX();
+extern int qt_defaultDpiY();
+
+#ifndef QT_MAC_NO_QUICKDRAW
+class QMacSavedPortInfo
+{
+ RgnHandle clip;
+ GWorldPtr world;
+ GDHandle handle;
+ PenState pen; //go pennstate
+ RGBColor back, fore;
+ bool valid_gworld;
+ void init();
+
+public:
+ inline QMacSavedPortInfo() { init(); }
+ inline QMacSavedPortInfo(QPaintDevice *pd) { init(); setPaintDevice(pd); }
+ inline QMacSavedPortInfo(QPaintDevice *pd, const QRect &r)
+ { init(); setPaintDevice(pd); setClipRegion(r); }
+ inline QMacSavedPortInfo(QPaintDevice *pd, const QRegion &r)
+ { init(); setPaintDevice(pd); setClipRegion(r); }
+ ~QMacSavedPortInfo();
+ static inline bool setClipRegion(const QRect &r);
+ static inline bool setClipRegion(const QRegion &r);
+ static inline bool setPaintDevice(QPaintDevice *);
+};
+
+inline bool
+QMacSavedPortInfo::setClipRegion(const QRect &rect)
+{
+ Rect r;
+ SetRect(&r, rect.x(), rect.y(), rect.right()+1, rect.bottom()+1);
+ ClipRect(&r);
+ return true;
+}
+
+inline bool
+QMacSavedPortInfo::setClipRegion(const QRegion &r)
+{
+ if(r.isEmpty())
+ return setClipRegion(QRect());
+ QMacSmartQuickDrawRegion rgn(r.toQDRgn());
+ SetClip(rgn);
+ return true;
+}
+
+inline bool
+QMacSavedPortInfo::setPaintDevice(QPaintDevice *pd)
+{
+ if(!pd)
+ return false;
+ bool ret = true;
+ extern GrafPtr qt_mac_qd_context(const QPaintDevice *); // qpaintdevice_mac.cpp
+ if(pd->devType() == QInternal::Widget)
+ SetPortWindowPort(qt_mac_window_for(static_cast<QWidget*>(pd)));
+ else if(pd->devType() == QInternal::Pixmap || pd->devType() == QInternal::Printer)
+ SetGWorld((GrafPtr)qt_mac_qd_context(pd), 0); //set the gworld
+ return ret;
+}
+
+inline void
+QMacSavedPortInfo::init()
+{
+ GetBackColor(&back);
+ GetForeColor(&fore);
+ GetGWorld(&world, &handle);
+ valid_gworld = true;
+ clip = NewRgn();
+ GetClip(clip);
+ GetPenState(&pen);
+}
+
+inline QMacSavedPortInfo::~QMacSavedPortInfo()
+{
+ bool set_state = false;
+ if(valid_gworld) {
+ set_state = IsValidPort(world);
+ if(set_state)
+ SetGWorld(world,handle); //always do this one first
+ } else {
+ setPaintDevice(qt_mac_safe_pdev);
+ }
+ if(set_state) {
+ SetClip(clip);
+ SetPenState(&pen);
+ RGBForeColor(&fore);
+ RGBBackColor(&back);
+ }
+ DisposeRgn(clip);
+}
+#else
+class QMacSavedPortInfo
+{
+public:
+ inline QMacSavedPortInfo() { }
+ inline QMacSavedPortInfo(QPaintDevice *) { }
+ inline QMacSavedPortInfo(QPaintDevice *, const QRect &) { }
+ inline QMacSavedPortInfo(QPaintDevice *, const QRegion &) { }
+ ~QMacSavedPortInfo() { }
+ static inline bool setClipRegion(const QRect &) { return false; }
+ static inline bool setClipRegion(const QRegion &) { return false; }
+ static inline bool setPaintDevice(QPaintDevice *) { return false; }
+};
+#endif
+
+class QCoreGraphicsPaintEnginePrivate;
+class QCoreGraphicsPaintEngine : public QPaintEngine
+{
+ Q_DECLARE_PRIVATE(QCoreGraphicsPaintEngine)
+
+public:
+ QCoreGraphicsPaintEngine();
+ ~QCoreGraphicsPaintEngine();
+
+ bool begin(QPaintDevice *pdev);
+ bool end();
+ static CGColorSpaceRef macGenericColorSpace();
+ static CGColorSpaceRef macDisplayColorSpace(const QWidget *widget = 0);
+
+ void updateState(const QPaintEngineState &state);
+
+ void updatePen(const QPen &pen);
+ void updateBrush(const QBrush &brush, const QPointF &pt);
+ void updateFont(const QFont &font);
+ void updateOpacity(qreal opacity);
+ void updateMatrix(const QTransform &matrix);
+ void updateTransform(const QTransform &matrix);
+ void updateClipRegion(const QRegion &region, Qt::ClipOperation op);
+ void updateClipPath(const QPainterPath &path, Qt::ClipOperation op);
+ void updateCompositionMode(QPainter::CompositionMode mode);
+ void updateRenderHints(QPainter::RenderHints hints);
+
+ void drawLines(const QLineF *lines, int lineCount);
+ void drawRects(const QRectF *rects, int rectCount);
+ void drawPoints(const QPointF *p, int pointCount);
+ void drawEllipse(const QRectF &r);
+ void drawPath(const QPainterPath &path);
+
+ void drawPolygon(const QPointF *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 &pos, const QTextItem &item);
+ void drawImage(const QRectF &r, const QImage &pm, const QRectF &sr,
+ Qt::ImageConversionFlags flags = Qt::AutoColor);
+
+ Type type() const { return QPaintEngine::CoreGraphics; }
+
+ CGContextRef handle() const;
+
+ static void initialize();
+ static void cleanup();
+
+ QPainter::RenderHints supportedRenderHints() const;
+
+ //avoid partial shadowed overload warnings...
+ void drawLines(const QLine *lines, int lineCount) { QPaintEngine::drawLines(lines, lineCount); }
+ void drawRects(const QRect *rects, int rectCount) { QPaintEngine::drawRects(rects, rectCount); }
+ void drawPoints(const QPoint *p, int pointCount) { QPaintEngine::drawPoints(p, pointCount); }
+ void drawEllipse(const QRect &r) { QPaintEngine::drawEllipse(r); }
+ void drawPolygon(const QPoint *points, int pointCount, PolygonDrawMode mode)
+ { QPaintEngine::drawPolygon(points, pointCount, mode); }
+
+protected:
+ friend class QMacPrintEngine;
+ friend class QMacPrintEnginePrivate;
+ friend void qt_mac_display_change_callbk(CGDirectDisplayID, CGDisplayChangeSummaryFlags, void *);
+ QCoreGraphicsPaintEngine(QPaintEnginePrivate &dptr);
+
+private:
+ static bool m_postRoutineRegistered;
+ static CGColorSpaceRef m_genericColorSpace;
+ static QHash<CGDirectDisplayID, CGColorSpaceRef> m_displayColorSpaceHash;
+ static void cleanUpMacColorSpaces();
+ Q_DISABLE_COPY(QCoreGraphicsPaintEngine)
+};
+
+/*****************************************************************************
+ Private data
+ *****************************************************************************/
+class QCoreGraphicsPaintEnginePrivate : public QPaintEnginePrivate
+{
+ Q_DECLARE_PUBLIC(QCoreGraphicsPaintEngine)
+public:
+ QCoreGraphicsPaintEnginePrivate()
+ : hd(0), shading(0), stackCount(0), complexXForm(false)
+ {
+ }
+
+ struct {
+ QPen pen;
+ QBrush brush;
+ uint clipEnabled : 1;
+ QRegion clip;
+ QTransform transform;
+ } current;
+
+ //state info (shared with QD)
+ CGAffineTransform orig_xform;
+
+ //cg structures
+ CGContextRef hd;
+ CGShadingRef shading;
+ int stackCount;
+ bool complexXForm;
+ enum { CosmeticNone, CosmeticTransformPath, CosmeticSetPenWidth } cosmeticPen;
+
+ // pixel and cosmetic pen size in user coordinates.
+ QPointF pixelSize;
+ float cosmeticPenSize;
+
+ //internal functions
+ enum { CGStroke=0x01, CGEOFill=0x02, CGFill=0x04 };
+ void drawPath(uchar ops, CGMutablePathRef path = 0);
+ void setClip(const QRegion *rgn=0);
+ void resetClip();
+ void setFillBrush(const QPointF &origin=QPoint());
+ void setStrokePen(const QPen &pen);
+ inline void saveGraphicsState();
+ inline void restoreGraphicsState();
+ float penOffset();
+ QPointF devicePixelSize(CGContextRef context);
+ float adjustPenWidth(float penWidth);
+ inline void setTransform(const QTransform *matrix=0)
+ {
+ CGContextConcatCTM(hd, CGAffineTransformInvert(CGContextGetCTM(hd)));
+ CGAffineTransform xform = orig_xform;
+ if(matrix) {
+ extern CGAffineTransform qt_mac_convert_transform_to_cg(const QTransform &);
+ xform = CGAffineTransformConcat(qt_mac_convert_transform_to_cg(*matrix), xform);
+ }
+ CGContextConcatCTM(hd, xform);
+ CGContextSetTextMatrix(hd, xform);
+ }
+};
+
+inline void QCoreGraphicsPaintEnginePrivate::saveGraphicsState()
+{
+ ++stackCount;
+ CGContextSaveGState(hd);
+}
+
+inline void QCoreGraphicsPaintEnginePrivate::restoreGraphicsState()
+{
+ --stackCount;
+ Q_ASSERT(stackCount >= 0);
+ CGContextRestoreGState(hd);
+}
+
+class QMacQuartzPaintDevice : public QPaintDevice
+{
+public:
+ QMacQuartzPaintDevice(CGContextRef cg, int width, int height, int bytesPerLine)
+ : mCG(cg), mWidth(width), mHeight(height), mBytesPerLine(bytesPerLine)
+ { }
+ int devType() const { return QInternal::MacQuartz; }
+ CGContextRef cgContext() const { return mCG; }
+ int metric(PaintDeviceMetric metric) const {
+ switch (metric) {
+ case PdmWidth:
+ return mWidth;
+ case PdmHeight:
+ return mHeight;
+ case PdmWidthMM:
+ return (qt_defaultDpiX() * mWidth) / 2.54;
+ case PdmHeightMM:
+ return (qt_defaultDpiY() * mHeight) / 2.54;
+ case PdmNumColors:
+ return 0;
+ case PdmDepth:
+ return 32;
+ case PdmDpiX:
+ case PdmPhysicalDpiX:
+ return qt_defaultDpiX();
+ case PdmDpiY:
+ case PdmPhysicalDpiY:
+ return qt_defaultDpiY();
+ }
+ return 0;
+ }
+ QPaintEngine *paintEngine() const { qWarning("This function should never be called."); return 0; }
+private:
+ CGContextRef mCG;
+ int mWidth;
+ int mHeight;
+ int mBytesPerLine;
+};
+
+QT_END_NAMESPACE
+
+#endif // QPAINTENGINE_MAC_P_H
diff --git a/src/gui/painting/qpaintengine_p.h b/src/gui/painting/qpaintengine_p.h
new file mode 100644
index 0000000000..eeba7ec74f
--- /dev/null
+++ b/src/gui/painting/qpaintengine_p.h
@@ -0,0 +1,125 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QPAINTENGINE_P_H
+#define QPAINTENGINE_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 "QtGui/qpainter.h"
+#include "QtGui/qpaintengine.h"
+#include "QtGui/qregion.h"
+#include "private/qobject_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class QPaintDevice;
+
+class QPaintEnginePrivate
+{
+ Q_DECLARE_PUBLIC(QPaintEngine)
+public:
+ QPaintEnginePrivate() : pdev(0), q_ptr(0), currentClipWidget(0), hasSystemTransform(0),
+ hasSystemViewport(0) {}
+ virtual ~QPaintEnginePrivate() { }
+ QPaintDevice *pdev;
+ QPaintEngine *q_ptr;
+ QRegion systemClip;
+ QRegion systemViewport;
+ QTransform systemTransform;
+ QWidget *currentClipWidget;
+ uint hasSystemTransform : 1;
+ uint hasSystemViewport : 1;
+
+ inline void transformSystemClip()
+ {
+ if (systemClip.isEmpty())
+ return;
+
+ if (systemTransform.type() <= QTransform::TxTranslate)
+ systemClip.translate(qRound(systemTransform.dx()), qRound(systemTransform.dy()));
+ else
+ systemClip = systemTransform.map(systemClip);
+
+ // Make sure we're inside the viewport.
+ if (hasSystemViewport) {
+ systemClip &= systemViewport;
+ if (systemClip.isEmpty()) {
+ // We don't want to paint without system clip, so set it to 1 pixel :)
+ systemClip = QRect(systemViewport.boundingRect().topLeft(), QSize(1, 1));
+ }
+ }
+ }
+
+ inline void setSystemTransform(const QTransform &xform)
+ {
+ systemTransform = xform;
+ if ((hasSystemTransform = !xform.isIdentity()))
+ transformSystemClip();
+ systemStateChanged();
+ }
+
+ inline void setSystemViewport(const QRegion &region)
+ {
+ systemViewport = region;
+ hasSystemViewport = !systemViewport.isEmpty();
+ }
+
+ virtual void systemStateChanged() { }
+
+ void drawBoxTextItem(const QPointF &p, const QTextItemInt &ti);
+
+private:
+ QRect systemRect;
+};
+
+QT_END_NAMESPACE
+
+#endif // QPAINTENGINE_P_H
diff --git a/src/gui/painting/qpaintengine_preview.cpp b/src/gui/painting/qpaintengine_preview.cpp
new file mode 100644
index 0000000000..2137e6d025
--- /dev/null
+++ b/src/gui/painting/qpaintengine_preview.cpp
@@ -0,0 +1,223 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <private/qpaintengine_preview_p.h>
+#include <private/qpainter_p.h>
+#include <private/qpaintengine_p.h>
+#include <private/qpicture_p.h>
+
+#include <QtGui/qprintengine.h>
+#include <QtGui/qpainter.h>
+#include <QtGui/qpicture.h>
+
+#ifndef QT_NO_PRINTPREVIEWWIDGET
+QT_BEGIN_NAMESPACE
+
+class QPreviewPaintEnginePrivate : public QPaintEnginePrivate
+{
+ Q_DECLARE_PUBLIC(QPreviewPaintEngine)
+public:
+ QPreviewPaintEnginePrivate() : state(QPrinter::Idle) {}
+ ~QPreviewPaintEnginePrivate() {}
+
+ QList<const QPicture *> pages;
+ QPaintEngine *engine;
+ QPainter *painter;
+ QPrinter::PrinterState state;
+
+ QPaintEngine *proxy_paint_engine;
+ QPrintEngine *proxy_print_engine;
+};
+
+
+QPreviewPaintEngine::QPreviewPaintEngine()
+ : QPaintEngine(*(new QPreviewPaintEnginePrivate), PaintEngineFeatures(AllFeatures & ~ObjectBoundingModeGradients))
+{
+ Q_D(QPreviewPaintEngine);
+ d->proxy_print_engine = 0;
+ d->proxy_paint_engine = 0;
+}
+
+QPreviewPaintEngine::~QPreviewPaintEngine()
+{
+ Q_D(QPreviewPaintEngine);
+
+ qDeleteAll(d->pages);
+}
+
+bool QPreviewPaintEngine::begin(QPaintDevice *)
+{
+ Q_D(QPreviewPaintEngine);
+
+ qDeleteAll(d->pages);
+ d->pages.clear();
+
+ QPicture *page = new QPicture;
+ page->d_func()->in_memory_only = true;
+ d->painter = new QPainter(page);
+ d->engine = d->painter->paintEngine();
+ d->pages.append(page);
+ d->state = QPrinter::Active;
+ return true;
+}
+
+bool QPreviewPaintEngine::end()
+{
+ Q_D(QPreviewPaintEngine);
+
+ delete d->painter;
+ d->painter = 0;
+ d->engine = 0;
+ d->state = QPrinter::Idle;
+ return true;
+}
+
+void QPreviewPaintEngine::updateState(const QPaintEngineState &state)
+{
+ Q_D(QPreviewPaintEngine);
+ d->engine->updateState(state);
+}
+
+void QPreviewPaintEngine::drawPath(const QPainterPath &path)
+{
+ Q_D(QPreviewPaintEngine);
+ d->engine->drawPath(path);
+}
+
+void QPreviewPaintEngine::drawPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode)
+{
+ Q_D(QPreviewPaintEngine);
+ d->engine->drawPolygon(points, pointCount, mode);
+}
+
+void QPreviewPaintEngine::drawTextItem(const QPointF &p, const QTextItem &textItem)
+{
+ Q_D(QPreviewPaintEngine);
+ d->engine->drawTextItem(p, textItem);
+}
+
+void QPreviewPaintEngine::drawPixmap(const QRectF &r, const QPixmap &pm, const QRectF &sr)
+{
+ Q_D(QPreviewPaintEngine);
+ d->engine->drawPixmap(r, pm, sr);
+}
+
+void QPreviewPaintEngine::drawTiledPixmap(const QRectF &r, const QPixmap &pm, const QPointF &p)
+{
+ Q_D(QPreviewPaintEngine);
+ d->engine->drawTiledPixmap(r, pm, p);
+}
+
+bool QPreviewPaintEngine::newPage()
+{
+ Q_D(QPreviewPaintEngine);
+
+ QPicture *page = new QPicture;
+ page->d_func()->in_memory_only = true;
+ QPainter *tmp_painter = new QPainter(page);
+ QPaintEngine *tmp_engine = tmp_painter->paintEngine();
+
+ // copy the painter state from the original painter
+ Q_ASSERT(painter()->d_func()->state && tmp_painter->d_func()->state);
+ *tmp_painter->d_func()->state = *painter()->d_func()->state;
+
+ // composition modes aren't supported on a QPrinter and yields a
+ // warning, so ignore it for now
+ tmp_engine->setDirty(DirtyFlags(AllDirty & ~DirtyCompositionMode));
+ tmp_engine->syncState();
+
+ delete d->painter;
+ d->painter = tmp_painter;
+ d->pages.append(page);
+ d->engine = tmp_engine;
+ return true;
+}
+
+bool QPreviewPaintEngine::abort()
+{
+ Q_D(QPreviewPaintEngine);
+ end();
+ qDeleteAll(d->pages);
+ d->state = QPrinter::Aborted;
+
+ return true;
+}
+
+QList<const QPicture *> QPreviewPaintEngine::pages()
+{
+ Q_D(QPreviewPaintEngine);
+ return d->pages;
+}
+
+void QPreviewPaintEngine::setProxyEngines(QPrintEngine *printEngine, QPaintEngine *paintEngine)
+{
+ Q_D(QPreviewPaintEngine);
+ d->proxy_print_engine = printEngine;
+ d->proxy_paint_engine = paintEngine;
+}
+
+void QPreviewPaintEngine::setProperty(PrintEnginePropertyKey key, const QVariant &value)
+{
+ Q_D(QPreviewPaintEngine);
+ d->proxy_print_engine->setProperty(key, value);
+}
+
+QVariant QPreviewPaintEngine::property(PrintEnginePropertyKey key) const
+{
+ Q_D(const QPreviewPaintEngine);
+ return d->proxy_print_engine->property(key);
+}
+
+int QPreviewPaintEngine::metric(QPaintDevice::PaintDeviceMetric id) const
+{
+ Q_D(const QPreviewPaintEngine);
+ return d->proxy_print_engine->metric(id);
+}
+
+QPrinter::PrinterState QPreviewPaintEngine::printerState() const
+{
+ Q_D(const QPreviewPaintEngine);
+ return d->state;
+}
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/gui/painting/qpaintengine_preview_p.h b/src/gui/painting/qpaintengine_preview_p.h
new file mode 100644
index 0000000000..6c884f9931
--- /dev/null
+++ b/src/gui/painting/qpaintengine_preview_p.h
@@ -0,0 +1,106 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QPREVIEWPAINTENGINE_P_H
+#define QPREVIEWPAINTENGINE_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of QPreviewPrinter and friends. This header file may change from
+// version to version without notice, or even be removed.
+//
+// We mean it.
+//
+//
+
+#include <QtGui/qpaintengine.h>
+#include <QtGui/qprintengine.h>
+
+#ifndef QT_NO_PRINTPREVIEWWIDGET
+
+QT_BEGIN_NAMESPACE
+
+class QPreviewPaintEnginePrivate;
+
+class QPreviewPaintEngine : public QPaintEngine, public QPrintEngine
+{
+ Q_DECLARE_PRIVATE(QPreviewPaintEngine)
+public:
+ QPreviewPaintEngine();
+ ~QPreviewPaintEngine();
+
+ bool begin(QPaintDevice *dev);
+ bool end();
+
+ void updateState(const QPaintEngineState &state);
+
+ void drawPath(const QPainterPath &path);
+ void drawPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode);
+ void drawTextItem(const QPointF &p, const QTextItem &textItem);
+
+ void drawPixmap(const QRectF &r, const QPixmap &pm, const QRectF &sr);
+ void drawTiledPixmap(const QRectF &r, const QPixmap &pm, const QPointF &p);
+
+ QList<const QPicture *> pages();
+
+ QPaintEngine::Type type() const { return Picture; }
+
+ void setProxyEngines(QPrintEngine *printEngine, QPaintEngine *paintEngine);
+
+ void setProperty(PrintEnginePropertyKey key, const QVariant &value);
+ QVariant property(PrintEnginePropertyKey key) const;
+
+ bool newPage();
+ bool abort();
+
+ int metric(QPaintDevice::PaintDeviceMetric) const;
+
+ QPrinter::PrinterState printerState() const;
+};
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_PRINTPREVIEWWIDGET
+
+#endif
diff --git a/src/gui/painting/qpaintengine_raster.cpp b/src/gui/painting/qpaintengine_raster.cpp
new file mode 100644
index 0000000000..ba79b5ba67
--- /dev/null
+++ b/src/gui/painting/qpaintengine_raster.cpp
@@ -0,0 +1,6058 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtCore/qglobal.h>
+
+#define QT_FT_BEGIN_HEADER
+#define QT_FT_END_HEADER
+
+#include <private/qrasterdefs_p.h>
+#include <private/qgrayraster_p.h>
+
+#include <qpainterpath.h>
+#include <qdebug.h>
+#include <qhash.h>
+#include <qlabel.h>
+#include <qbitmap.h>
+#include <qmath.h>
+
+#if defined (Q_WS_X11)
+# include <private/qfontengine_ft_p.h>
+#endif
+
+// #include <private/qdatabuffer_p.h>
+// #include <private/qpainter_p.h>
+#include <private/qmath_p.h>
+#include <private/qtextengine_p.h>
+#include <private/qfontengine_p.h>
+#include <private/qpixmap_raster_p.h>
+// #include <private/qpolygonclipper_p.h>
+// #include <private/qrasterizer_p.h>
+#include <private/qimage_p.h>
+
+#include "qpaintengine_raster_p.h"
+// #include "qbezier_p.h"
+#include "qoutlinemapper_p.h"
+
+#if defined(Q_WS_WIN)
+# include <qt_windows.h>
+# include <qvarlengtharray.h>
+# include <private/qfontengine_p.h>
+# if defined(Q_OS_WINCE)
+# include "qguifunctions_wince.h"
+# endif
+#elif defined(Q_WS_MAC)
+# include <private/qt_mac_p.h>
+# include <private/qpixmap_mac_p.h>
+# include <private/qpaintengine_mac_p.h>
+#elif defined(Q_WS_QWS)
+# if !defined(QT_NO_FREETYPE)
+# include <private/qfontengine_ft_p.h>
+# endif
+# if !defined(QT_NO_QWS_QPF2)
+# include <private/qfontengine_qpf_p.h>
+# endif
+# include <private/qabstractfontengine_p.h>
+#endif
+
+#if defined(Q_WS_WIN64)
+# include <malloc.h>
+#endif
+#include <limits.h>
+
+#if defined(QT_NO_FPU) || (_MSC_VER >= 1300 && _MSC_VER < 1400)
+# define FLOATING_POINT_BUGGY_OR_NO_FPU
+#endif
+
+QT_BEGIN_NAMESPACE
+
+extern bool qt_scaleForTransform(const QTransform &transform, qreal *scale); // qtransform.cpp
+
+#define qreal_to_fixed_26_6(f) (int(f * 64))
+#define qt_swap_int(x, y) { int tmp = (x); (x) = (y); (y) = tmp; }
+#define qt_swap_qreal(x, y) { qreal tmp = (x); (x) = (y); (y) = tmp; }
+
+#ifdef Q_WS_WIN
+static bool qt_enable_16bit_colors = false;
+#endif
+
+// #define QT_DEBUG_DRAW
+#ifdef QT_DEBUG_DRAW
+void dumpClip(int width, int height, QClipData *clip);
+#endif
+
+#define QT_FAST_SPANS
+
+
+// A little helper macro to get a better approximation of dimensions.
+// If we have a rect that starting at 0.5 of width 3.5 it should span
+// 4 pixels.
+#define int_dim(pos, dim) (int(pos+dim) - int(pos))
+
+// use the same rounding as in qrasterizer.cpp (6 bit fixed point)
+static const qreal aliasedCoordinateDelta = 0.5 - 0.015625;
+
+#ifdef Q_WS_WIN
+extern bool qt_cleartype_enabled;
+#endif
+
+
+/********************************************************************************
+ * Span functions
+ */
+static void qt_span_fill_clipRect(int count, const QSpan *spans, void *userData);
+static void qt_span_fill_clipRegion(int count, const QSpan *spans, void *userData);
+static void qt_span_fill_clipped(int count, const QSpan *spans, void *userData);
+static void qt_span_clip(int count, const QSpan *spans, void *userData);
+static void qt_merge_clip(const QClipData *c1, const QClipData *c2, QClipData *result);
+
+struct ClipData
+{
+ QClipData *oldClip;
+ QClipData *newClip;
+ Qt::ClipOperation operation;
+};
+
+enum LineDrawMode {
+ LineDrawClipped,
+ LineDrawNormal,
+ LineDrawIncludeLastPixel
+};
+
+static void drawLine_midpoint_i(int x1, int y1, int x2, int y2, ProcessSpans span_func, QSpanData *data,
+ LineDrawMode style, const QIntRect &rect);
+static void drawLine_midpoint_dashed_i(int x1, int y1, int x2, int y2,
+ QPen *pen, ProcessSpans span_func, QSpanData *data,
+ LineDrawMode style, const QIntRect &devRect,
+ int *patternOffset);
+// static void drawLine_midpoint_f(qreal x1, qreal y1, qreal x2, qreal y2,
+// ProcessSpans span_func, QSpanData *data,
+// LineDrawMode style, const QRect &devRect);
+
+static void drawEllipse_midpoint_i(const QRect &rect, const QRect &clip,
+ ProcessSpans pen_func, ProcessSpans brush_func,
+ QSpanData *pen_data, QSpanData *brush_data);
+
+struct QRasterFloatPoint {
+ qreal x;
+ qreal y;
+};
+
+#ifdef QT_DEBUG_DRAW
+static const QRectF boundingRect(const QPointF *points, int pointCount)
+{
+ const QPointF *e = points;
+ const QPointF *last = points + pointCount;
+ qreal minx, maxx, miny, maxy;
+ minx = maxx = e->x();
+ miny = maxy = e->y();
+ while (++e < last) {
+ if (e->x() < minx)
+ minx = e->x();
+ else if (e->x() > maxx)
+ maxx = e->x();
+ if (e->y() < miny)
+ miny = e->y();
+ else if (e->y() > maxy)
+ maxy = e->y();
+ }
+ return QRectF(QPointF(minx, miny), QPointF(maxx, maxy));
+}
+#endif
+
+template <typename T> static inline bool isRect(const T *pts, int elementCount) {
+ return (elementCount == 5 // 5-point polygon, check for closed rect
+ && pts[0] == pts[8] && pts[1] == pts[9] // last point == first point
+ && pts[0] == pts[6] && pts[2] == pts[4] // x values equal
+ && pts[1] == pts[3] && pts[5] == pts[7] // y values equal...
+ && pts[0] < pts[4] && pts[1] < pts[5]
+ ) ||
+ (elementCount == 4 // 4-point polygon, check for unclosed rect
+ && pts[0] == pts[6] && pts[2] == pts[4] // x values equal
+ && pts[1] == pts[3] && pts[5] == pts[7] // y values equal...
+ && pts[0] < pts[4] && pts[1] < pts[5]
+ );
+}
+
+
+static void qt_ft_outline_move_to(qfixed x, qfixed y, void *data)
+{
+ ((QOutlineMapper *) data)->moveTo(QPointF(qt_fixed_to_real(x), qt_fixed_to_real(y)));
+}
+
+static void qt_ft_outline_line_to(qfixed x, qfixed y, void *data)
+{
+ ((QOutlineMapper *) data)->lineTo(QPointF(qt_fixed_to_real(x), qt_fixed_to_real(y)));
+}
+
+static void qt_ft_outline_cubic_to(qfixed c1x, qfixed c1y,
+ qfixed c2x, qfixed c2y,
+ qfixed ex, qfixed ey,
+ void *data)
+{
+ ((QOutlineMapper *) data)->curveTo(QPointF(qt_fixed_to_real(c1x), qt_fixed_to_real(c1y)),
+ QPointF(qt_fixed_to_real(c2x), qt_fixed_to_real(c2y)),
+ QPointF(qt_fixed_to_real(ex), qt_fixed_to_real(ey)));
+}
+
+
+#if !defined(QT_NO_DEBUG) && 0
+static void qt_debug_path(const QPainterPath &path)
+{
+ const char *names[] = {
+ "MoveTo ",
+ "LineTo ",
+ "CurveTo ",
+ "CurveToData"
+ };
+
+ fprintf(stderr,"\nQPainterPath: elementCount=%d\n", path.elementCount());
+ for (int i=0; i<path.elementCount(); ++i) {
+ const QPainterPath::Element &e = path.elementAt(i);
+ Q_ASSERT(e.type >= 0 && e.type <= QPainterPath::CurveToDataElement);
+ fprintf(stderr," - %3d:: %s, (%.2f, %.2f)\n", i, names[e.type], e.x, e.y);
+ }
+}
+#endif
+
+
+
+/*!
+ \class QRasterPaintEngine
+ \preliminary
+ \ingroup qws
+ \since 4.2
+
+ \brief The QRasterPaintEngine class enables hardware acceleration
+ of painting operations in Qt for Embedded Linux.
+
+ Note that this functionality is only available in
+ \l{Qt for Embedded Linux}.
+
+ In \l{Qt for Embedded Linux}, painting is a pure software
+ implementation. But starting with Qt 4.2, it is
+ possible to add an accelerated graphics driver to take advantage
+ of available hardware resources.
+
+ Hardware acceleration is accomplished by creating a custom screen
+ driver, accelerating the copying from memory to the screen, and
+ implementing a custom paint engine accelerating the various
+ painting operations. Then a custom paint device (derived from the
+ QCustomRasterPaintDevice class) and a custom window surface
+ (derived from QWSWindowSurface) must be implemented to make
+ \l{Qt for Embedded Linux} aware of the accelerated driver.
+
+ \note The QRasterPaintEngine class does not support 8-bit images.
+ Instead, they need to be converted to a supported format, such as
+ QImage::Format_ARGB32_Premultiplied.
+
+ See the \l {Adding an Accelerated Graphics Driver to Qt for Embedded Linux}
+ documentation for details.
+
+ \sa QCustomRasterPaintDevice, QPaintEngine
+*/
+
+/*!
+ \fn Type QRasterPaintEngine::type() const
+ \reimp
+*/
+
+/*!
+ \typedef QSpan
+ \relates QRasterPaintEngine
+
+ A struct equivalent to QT_FT_Span, containing a position (x,
+ y), the span's length in pixels and its color/coverage (a value
+ ranging from 0 to 255).
+*/
+
+/*!
+ \since 4.5
+
+ Creates a raster based paint engine for operating on the given
+ \a device, with the complete set of \l
+ {QPaintEngine::PaintEngineFeature}{paint engine features and
+ capabilities}.
+*/
+QRasterPaintEngine::QRasterPaintEngine(QPaintDevice *device)
+ : QPaintEngineEx(*(new QRasterPaintEnginePrivate))
+{
+ d_func()->device = device;
+ init();
+}
+
+/*!
+ \internal
+*/
+QRasterPaintEngine::QRasterPaintEngine(QRasterPaintEnginePrivate &dd, QPaintDevice *device)
+ : QPaintEngineEx(dd)
+{
+ d_func()->device = device;
+ init();
+}
+
+void QRasterPaintEngine::init()
+{
+ Q_D(QRasterPaintEngine);
+
+
+#ifdef Q_WS_WIN
+ d->hdc = 0;
+#endif
+
+ d->rasterPoolSize = 8192;
+ d->rasterPoolBase =
+#if defined(Q_WS_WIN64)
+ // We make use of setjmp and longjmp in qgrayraster.c which requires
+ // 16-byte alignment, hence we hardcode this requirement here..
+ (unsigned char *) _aligned_malloc(d->rasterPoolSize, sizeof(void*) * 2);
+#else
+ (unsigned char *) malloc(d->rasterPoolSize);
+#endif
+
+ // The antialiasing raster.
+ d->grayRaster = new QT_FT_Raster;
+ qt_ft_grays_raster.raster_new(0, d->grayRaster);
+ qt_ft_grays_raster.raster_reset(*d->grayRaster, d->rasterPoolBase, d->rasterPoolSize);
+
+ d->rasterizer = new QRasterizer;
+ d->rasterBuffer = new QRasterBuffer();
+ d->outlineMapper = new QOutlineMapper;
+ d->outlinemapper_xform_dirty = true;
+
+ d->basicStroker.setMoveToHook(qt_ft_outline_move_to);
+ d->basicStroker.setLineToHook(qt_ft_outline_line_to);
+ d->basicStroker.setCubicToHook(qt_ft_outline_cubic_to);
+ d->dashStroker = 0;
+
+ d->baseClip = 0;
+
+ d->image_filler.init(d->rasterBuffer, this);
+ d->image_filler.type = QSpanData::Texture;
+
+ d->image_filler_xform.init(d->rasterBuffer, this);
+ d->image_filler_xform.type = QSpanData::Texture;
+
+ d->solid_color_filler.init(d->rasterBuffer, this);
+ d->solid_color_filler.type = QSpanData::Solid;
+
+ d->deviceDepth = d->device->depth();
+
+ d->mono_surface = false;
+ gccaps &= ~PorterDuff;
+
+ QImage::Format format = QImage::Format_Invalid;
+
+ switch (d->device->devType()) {
+ case QInternal::Pixmap:
+ qWarning("QRasterPaintEngine: unsupported for pixmaps...");
+ break;
+ case QInternal::Image:
+ format = d->rasterBuffer->prepare(static_cast<QImage *>(d->device));
+ break;
+#ifdef Q_WS_QWS
+ case QInternal::CustomRaster:
+ d->rasterBuffer->prepare(static_cast<QCustomRasterPaintDevice*>(d->device));
+ break;
+#endif
+ default:
+ qWarning("QRasterPaintEngine: unsupported target device %d\n", d->device->devType());
+ d->device = 0;
+ return;
+ }
+
+ switch (format) {
+ case QImage::Format_MonoLSB:
+ case QImage::Format_Mono:
+ d->mono_surface = true;
+ break;
+ case QImage::Format_ARGB8565_Premultiplied:
+ case QImage::Format_ARGB8555_Premultiplied:
+ case QImage::Format_ARGB6666_Premultiplied:
+ case QImage::Format_ARGB4444_Premultiplied:
+ case QImage::Format_ARGB32_Premultiplied:
+ case QImage::Format_ARGB32:
+ gccaps |= PorterDuff;
+ break;
+ case QImage::Format_RGB32:
+ case QImage::Format_RGB444:
+ case QImage::Format_RGB555:
+ case QImage::Format_RGB666:
+ case QImage::Format_RGB888:
+ case QImage::Format_RGB16:
+ break;
+ default:
+ break;
+ }
+}
+
+
+
+
+/*!
+ Destroys this paint engine.
+*/
+QRasterPaintEngine::~QRasterPaintEngine()
+{
+ Q_D(QRasterPaintEngine);
+
+#if defined(Q_WS_WIN64)
+ _aligned_free(d->rasterPoolBase);
+#else
+ free(d->rasterPoolBase);
+#endif
+
+ qt_ft_grays_raster.raster_done(*d->grayRaster);
+ delete d->grayRaster;
+
+ delete d->rasterBuffer;
+ delete d->outlineMapper;
+ delete d->rasterizer;
+ delete d->dashStroker;
+}
+
+/*!
+ \reimp
+*/
+bool QRasterPaintEngine::begin(QPaintDevice *device)
+{
+ Q_D(QRasterPaintEngine);
+
+ if (device->devType() == QInternal::Pixmap) {
+ QPixmap *pixmap = static_cast<QPixmap *>(device);
+ if (pixmap->data->classId() == QPixmapData::RasterClass)
+ d->device = pixmap->data->buffer();
+ } else {
+ d->device = device;
+ }
+
+ // Make sure QPaintEngine::paintDevice() returns the proper device.
+ d->pdev = d->device;
+
+ Q_ASSERT(d->device->devType() == QInternal::Image
+ || d->device->devType() == QInternal::CustomRaster);
+
+ d->systemStateChanged();
+
+ QRasterPaintEngineState *s = state();
+ ensureOutlineMapper();
+ d->outlineMapper->m_clip_rect = d->deviceRect.adjusted(-10, -10, 10, 10);
+ QRect bounds(-QT_RASTER_COORD_LIMIT, -QT_RASTER_COORD_LIMIT,
+ 2*QT_RASTER_COORD_LIMIT, 2*QT_RASTER_COORD_LIMIT);
+ d->outlineMapper->m_clip_rect = bounds.intersected(d->outlineMapper->m_clip_rect);
+
+
+ d->rasterizer->setClipRect(d->deviceRect);
+
+ s->penData.init(d->rasterBuffer, this);
+ s->penData.setup(s->pen.brush(), s->intOpacity);
+ s->stroker = &d->basicStroker;
+ d->basicStroker.setClipRect(d->deviceRect);
+
+ s->brushData.init(d->rasterBuffer, this);
+ s->brushData.setup(s->brush, s->intOpacity);
+
+ d->rasterBuffer->compositionMode = QPainter::CompositionMode_SourceOver;
+
+ setDirty(DirtyBrushOrigin);
+
+#ifdef QT_DEBUG_DRAW
+ qDebug() << "QRasterPaintEngine::begin(" << (void *) device
+ << ") devType:" << device->devType()
+ << "devRect:" << d->deviceRect;
+ if (d->baseClip) {
+ dumpClip(d->rasterBuffer->width(), d->rasterBuffer->height(), d->baseClip);
+ }
+#endif
+
+#if defined(Q_WS_WIN)
+ d->isPlain45DegreeRotation = true;
+#endif
+
+ if (d->mono_surface)
+ d->glyphCacheType = QFontEngineGlyphCache::Raster_Mono;
+#ifdef Q_WS_WIN
+ else if (qt_cleartype_enabled) {
+ QImage::Format format = static_cast<QImage *>(d->device)->format();
+ if (format == QImage::Format_ARGB32_Premultiplied || format == QImage::Format_RGB32)
+ d->glyphCacheType = QFontEngineGlyphCache::Raster_RGBMask;
+ else
+ d->glyphCacheType = QFontEngineGlyphCache::Raster_A8;
+ }
+#endif
+ else
+ d->glyphCacheType = QFontEngineGlyphCache::Raster_A8;
+
+ setActive(true);
+ return true;
+}
+
+/*!
+ \reimp
+*/
+bool QRasterPaintEngine::end()
+{
+ Q_D(QRasterPaintEngine);
+#ifdef QT_DEBUG_DRAW
+ qDebug() << "QRasterPaintEngine::end devRect:" << d->deviceRect;
+ if (d->baseClip) {
+ dumpClip(d->rasterBuffer->width(), d->rasterBuffer->height(), d->baseClip);
+ }
+#endif
+
+ if (d->baseClip) {
+ delete d->baseClip;
+ d->baseClip = 0;
+ }
+
+ return true;
+}
+
+/*!
+ \internal
+*/
+void QRasterPaintEngine::releaseBuffer()
+{
+ Q_D(QRasterPaintEngine);
+ delete d->rasterBuffer;
+ d->rasterBuffer = new QRasterBuffer;
+}
+
+/*!
+ \internal
+*/
+QSize QRasterPaintEngine::size() const
+{
+ Q_D(const QRasterPaintEngine);
+ return QSize(d->rasterBuffer->width(), d->rasterBuffer->height());
+}
+
+/*!
+ \internal
+*/
+#ifndef QT_NO_DEBUG
+void QRasterPaintEngine::saveBuffer(const QString &s) const
+{
+ Q_D(const QRasterPaintEngine);
+ d->rasterBuffer->bufferImage().save(s, "PNG");
+}
+#endif
+
+/*!
+ \internal
+*/
+void QRasterPaintEngine::updateMatrix(const QTransform &matrix)
+{
+ QRasterPaintEngineState *s = state();
+ // FALCON: get rid of this line, see drawImage call below.
+ s->matrix = matrix;
+ QTransform::TransformationType txop = s->matrix.type();
+
+ switch (txop) {
+
+ case QTransform::TxNone:
+ s->flags.int_xform = true;
+ break;
+
+ case QTransform::TxTranslate:
+ s->flags.int_xform = qreal(int(s->matrix.dx())) == s->matrix.dx()
+ && qreal(int(s->matrix.dy())) == s->matrix.dy();
+ break;
+
+ case QTransform::TxScale:
+ s->flags.int_xform = qreal(int(s->matrix.dx())) == s->matrix.dx()
+ && qreal(int(s->matrix.dy())) == s->matrix.dy()
+ && qreal(int(s->matrix.m11())) == s->matrix.m11()
+ && qreal(int(s->matrix.m22())) == s->matrix.m22();
+ break;
+
+ default: // shear / perspective...
+ s->flags.int_xform = false;
+ break;
+ }
+
+ s->flags.tx_noshear = qt_scaleForTransform(s->matrix, &s->txscale);
+
+ ensureOutlineMapper();
+
+#ifdef Q_WS_WIN
+ Q_D(QRasterPaintEngine);
+ d->isPlain45DegreeRotation = false;
+ if (txop >= QTransform::TxRotate) {
+ d->isPlain45DegreeRotation =
+ (qFuzzyCompare(matrix.m11() + 1, qreal(1))
+ && qFuzzyCompare(matrix.m12(), qreal(1))
+ && qFuzzyCompare(matrix.m21(), qreal(-1))
+ && qFuzzyCompare(matrix.m22() + 1, qreal(1))
+ )
+ ||
+ (qFuzzyCompare(matrix.m11(), qreal(-1))
+ && qFuzzyCompare(matrix.m12() + 1, qreal(1))
+ && qFuzzyCompare(matrix.m21() + 1, qreal(1))
+ && qFuzzyCompare(matrix.m22(), qreal(-1))
+ )
+ ||
+ (qFuzzyCompare(matrix.m11() + 1, qreal(1))
+ && qFuzzyCompare(matrix.m12(), qreal(-1))
+ && qFuzzyCompare(matrix.m21(), qreal(1))
+ && qFuzzyCompare(matrix.m22() + 1, qreal(1))
+ )
+ ;
+ }
+#endif
+
+}
+
+
+
+QRasterPaintEngineState::~QRasterPaintEngineState()
+{
+ if (flags.has_clip_ownership)
+ delete clip;
+}
+
+
+QRasterPaintEngineState::QRasterPaintEngineState()
+{
+ stroker = 0;
+
+ fillFlags = 0;
+ strokeFlags = 0;
+ pixmapFlags = 0;
+
+ intOpacity = 256;
+
+ txscale = 1.;
+
+ flags.fast_pen = true;
+ flags.antialiased = false;
+ flags.bilinear = false;
+ flags.fast_text = true;
+ flags.int_xform = true;
+ flags.tx_noshear = true;
+ flags.fast_images = true;
+
+ clip = 0;
+ flags.has_clip_ownership = false;
+
+ dirty = 0;
+}
+
+QRasterPaintEngineState::QRasterPaintEngineState(QRasterPaintEngineState &s)
+ : QPainterState(s)
+{
+ stroker = s.stroker;
+
+ lastBrush = s.lastBrush;
+ brushData = s.brushData;
+ brushData.tempImage = 0;
+
+ lastPen = s.lastPen;
+ penData = s.penData;
+ penData.tempImage = 0;
+
+ fillFlags = s.fillFlags;
+ strokeFlags = s.strokeFlags;
+ pixmapFlags = s.pixmapFlags;
+
+ intOpacity = s.intOpacity;
+
+ txscale = s.txscale;
+
+ flag_bits = s.flag_bits;
+
+ clip = s.clip;
+ flags.has_clip_ownership = false;
+
+ dirty = s.dirty;
+}
+
+/*!
+ \internal
+*/
+QPainterState *QRasterPaintEngine::createState(QPainterState *orig) const
+{
+ QRasterPaintEngineState *s;
+ if (!orig)
+ s = new QRasterPaintEngineState();
+ else
+ s = new QRasterPaintEngineState(*static_cast<QRasterPaintEngineState *>(orig));
+
+ return s;
+}
+
+/*!
+ \internal
+*/
+void QRasterPaintEngine::setState(QPainterState *s)
+{
+ Q_D(QRasterPaintEngine);
+ QPaintEngineEx::setState(s);
+ d->rasterBuffer->compositionMode = s->composition_mode;
+}
+
+/*!
+ \fn QRasterPaintEngineState *QRasterPaintEngine::state()
+ \internal
+*/
+
+/*!
+ \fn const QRasterPaintEngineState *QRasterPaintEngine::state() const
+ \internal
+*/
+
+/*!
+ \internal
+*/
+void QRasterPaintEngine::penChanged()
+{
+#ifdef QT_DEBUG_DRAW
+ qDebug() << "QRasterPaintEngine::penChanged():" << state()->pen;
+#endif
+ QRasterPaintEngineState *s = state();
+ s->strokeFlags |= DirtyPen;
+ s->dirty |= DirtyPen;
+}
+
+/*!
+ \internal
+*/
+void QRasterPaintEngine::updatePen(const QPen &pen)
+{
+ Q_D(QRasterPaintEngine);
+ QRasterPaintEngineState *s = state();
+#ifdef QT_DEBUG_DRAW
+ qDebug() << "QRasterPaintEngine::updatePen():" << s->pen;
+#endif
+
+ Qt::PenStyle pen_style = qpen_style(pen);
+
+ s->lastPen = pen;
+ s->strokeFlags = 0;
+
+ s->penData.clip = d->clip();
+ s->penData.setup(pen_style == Qt::NoPen ? QBrush() : pen.brush(), s->intOpacity);
+
+ if (s->strokeFlags & QRasterPaintEngine::DirtyTransform
+ || pen.brush().transform().type() >= QTransform::TxNone) {
+ d->updateMatrixData(&s->penData, pen.brush(), s->matrix);
+ }
+
+ // Slightly ugly handling of an uncommon case... We need to change
+ // the pen because it is reused in draw_midpoint to decide dashed
+ // or non-dashed.
+ if (pen_style == Qt::CustomDashLine && pen.dashPattern().size() == 0) {
+ pen_style = Qt::SolidLine;
+ s->lastPen.setStyle(Qt::SolidLine);
+ }
+
+ d->basicStroker.setJoinStyle(qpen_joinStyle(pen));
+ d->basicStroker.setCapStyle(qpen_capStyle(pen));
+ d->basicStroker.setMiterLimit(pen.miterLimit());
+
+ qreal penWidth = qpen_widthf(pen);
+ if (penWidth == 0)
+ d->basicStroker.setStrokeWidth(1);
+ else
+ d->basicStroker.setStrokeWidth(penWidth);
+
+ if(pen_style == Qt::SolidLine) {
+ s->stroker = &d->basicStroker;
+ } else if (pen_style != Qt::NoPen) {
+ if (!d->dashStroker)
+ d->dashStroker = new QDashStroker(&d->basicStroker);
+ if (pen.isCosmetic()) {
+ d->dashStroker->setClipRect(d->deviceRect);
+ } else {
+ // ### I've seen this inverted devrect multiple places now...
+ QRectF clipRect = s->matrix.inverted().mapRect(QRectF(d->deviceRect));
+ d->dashStroker->setClipRect(clipRect);
+ }
+ d->dashStroker->setDashPattern(pen.dashPattern());
+ d->dashStroker->setDashOffset(pen.dashOffset());
+ s->stroker = d->dashStroker;
+ } else {
+ s->stroker = 0;
+ }
+
+ s->flags.fast_pen = pen_style > Qt::NoPen
+ && s->penData.blend
+ && !s->flags.antialiased
+ && (penWidth == 0 || (penWidth <= 1
+ && (s->matrix.type() <= QTransform::TxTranslate
+ || pen.isCosmetic())));
+
+ ensureState(); // needed because of tx_noshear...
+ s->flags.non_complex_pen = qpen_capStyle(s->lastPen) <= Qt::SquareCap && s->flags.tx_noshear;
+
+ s->strokeFlags = 0;
+}
+
+
+
+/*!
+ \internal
+*/
+void QRasterPaintEngine::brushOriginChanged()
+{
+ QRasterPaintEngineState *s = state();
+#ifdef QT_DEBUG_DRAW
+ qDebug() << "QRasterPaintEngine::brushOriginChanged()" << s->brushOrigin;
+#endif
+
+ s->fillFlags |= DirtyBrushOrigin;
+}
+
+
+/*!
+ \internal
+*/
+void QRasterPaintEngine::brushChanged()
+{
+ QRasterPaintEngineState *s = state();
+#ifdef QT_DEBUG_DRAW
+ qDebug() << "QRasterPaintEngine::brushChanged():" << s->brush;
+#endif
+ s->fillFlags |= DirtyBrush;
+}
+
+
+
+
+/*!
+ \internal
+*/
+void QRasterPaintEngine::updateBrush(const QBrush &brush)
+{
+#ifdef QT_DEBUG_DRAW
+ qDebug() << "QRasterPaintEngine::updateBrush()" << brush;
+#endif
+ Q_D(QRasterPaintEngine);
+ QRasterPaintEngineState *s = state();
+ // must set clip prior to setup, as setup uses it...
+ s->brushData.clip = d->clip();
+ s->brushData.setup(brush, s->intOpacity);
+ if (s->fillFlags & DirtyTransform
+ || brush.transform().type() >= QTransform::TxNone)
+ d_func()->updateMatrixData(&s->brushData, brush, d->brushMatrix());
+ s->lastBrush = brush;
+ s->fillFlags = 0;
+}
+
+void QRasterPaintEngine::updateOutlineMapper()
+{
+ Q_D(QRasterPaintEngine);
+ d->outlineMapper->setMatrix(state()->matrix);
+}
+
+void QRasterPaintEngine::updateState()
+{
+ QRasterPaintEngineState *s = state();
+
+ if (s->dirty & DirtyTransform)
+ updateMatrix(s->matrix);
+
+ if (s->dirty & (DirtyPen|DirtyCompositionMode)) {
+ const QPainter::CompositionMode mode = s->composition_mode;
+ s->flags.fast_text = (s->penData.type == QSpanData::Solid)
+ && (mode == QPainter::CompositionMode_Source
+ || (mode == QPainter::CompositionMode_SourceOver
+ && qAlpha(s->penData.solid.color) == 255));
+ }
+
+ s->dirty = 0;
+}
+
+
+/*!
+ \internal
+*/
+void QRasterPaintEngine::opacityChanged()
+{
+ QRasterPaintEngineState *s = state();
+
+#ifdef QT_DEBUG_DRAW
+ qDebug() << "QRasterPaintEngine::opacityChanged()" << s->opacity;
+#endif
+
+ s->fillFlags |= DirtyOpacity;
+ s->strokeFlags |= DirtyOpacity;
+ s->pixmapFlags |= DirtyOpacity;
+ s->intOpacity = (int) (s->opacity * 256);
+}
+
+/*!
+ \internal
+*/
+void QRasterPaintEngine::compositionModeChanged()
+{
+ Q_D(QRasterPaintEngine);
+ QRasterPaintEngineState *s = state();
+
+#ifdef QT_DEBUG_DRAW
+ qDebug() << "QRasterPaintEngine::compositionModeChanged()" << s->composition_mode;
+#endif
+
+ s->fillFlags |= DirtyCompositionMode;
+ s->dirty |= DirtyCompositionMode;
+
+ s->strokeFlags |= DirtyCompositionMode;
+ d->rasterBuffer->compositionMode = s->composition_mode;
+
+ d->recalculateFastImages();
+}
+
+/*!
+ \internal
+*/
+void QRasterPaintEngine::renderHintsChanged()
+{
+ QRasterPaintEngineState *s = state();
+
+#ifdef QT_DEBUG_DRAW
+ qDebug() << "QRasterPaintEngine::renderHintsChanged()" << hex << s->renderHints;
+#endif
+
+ bool was_aa = s->flags.antialiased;
+ bool was_bilinear = s->flags.bilinear;
+
+ s->flags.antialiased = bool(s->renderHints & QPainter::Antialiasing);
+ s->flags.bilinear = bool(s->renderHints & QPainter::SmoothPixmapTransform);
+
+ if (was_aa != s->flags.antialiased)
+ s->strokeFlags |= DirtyHints;
+
+ if (was_bilinear != s->flags.bilinear) {
+ s->strokeFlags |= DirtyPen;
+ s->fillFlags |= DirtyBrush;
+ }
+
+ Q_D(QRasterPaintEngine);
+ d->recalculateFastImages();
+}
+
+/*!
+ \internal
+*/
+void QRasterPaintEngine::transformChanged()
+{
+ QRasterPaintEngineState *s = state();
+
+#ifdef QT_DEBUG_DRAW
+ qDebug() << "QRasterPaintEngine::transformChanged()" << s->matrix;
+#endif
+
+ s->fillFlags |= DirtyTransform;
+ s->strokeFlags |= DirtyTransform;
+
+ s->dirty |= DirtyTransform;
+
+ Q_D(QRasterPaintEngine);
+ d->recalculateFastImages();
+}
+
+/*!
+ \internal
+*/
+void QRasterPaintEngine::clipEnabledChanged()
+{
+ QRasterPaintEngineState *s = state();
+
+#ifdef QT_DEBUG_DRAW
+ qDebug() << "QRasterPaintEngine::clipEnabledChanged()" << s->clipEnabled;
+#endif
+
+ if (s->clip) {
+ s->clip->enabled = s->clipEnabled;
+ s->fillFlags |= DirtyClipEnabled;
+ s->strokeFlags |= DirtyClipEnabled;
+ s->pixmapFlags |= DirtyClipEnabled;
+ }
+}
+
+#ifdef Q_WS_QWS
+void QRasterPaintEnginePrivate::prepare(QCustomRasterPaintDevice *device)
+{
+ rasterBuffer->prepare(device);
+}
+#endif
+
+void QRasterPaintEnginePrivate::drawImage(const QPointF &pt,
+ const QImage &img,
+ SrcOverBlendFunc func,
+ const QRect &clip,
+ int alpha,
+ const QRect &sr)
+{
+ if (!clip.isValid())
+ return;
+ Q_ASSERT(img.depth() >= 8);
+
+ int srcBPL = img.bytesPerLine();
+ const uchar *srcBits = img.bits();
+ int srcSize = img.depth() >> 3; // This is the part that is incompatible with lower than 8-bit..
+ int iw = img.width();
+ int ih = img.height();
+
+ if (!sr.isEmpty()) {
+ iw = sr.width();
+ ih = sr.height();
+ // Adjust the image according to the source offset...
+ srcBits += ((sr.y() * srcBPL) + sr.x() * srcSize);
+ }
+
+ // adapt the x parameters
+ int x = qRound(pt.x());
+ int cx1 = clip.x();
+ int cx2 = clip.x() + clip.width();
+ if (x < cx1) {
+ int d = cx1 - x;
+ srcBits += srcSize * d;
+ iw -= d;
+ x = cx1;
+ }
+ if (x + iw > cx2) {
+ int d = x + iw - cx2;
+ iw -= d;
+ }
+ if (iw < 0)
+ return;
+
+ // adapt the y paremeters...
+ int cy1 = clip.y();
+ int cy2 = clip.y() + clip.height();
+ int y = qRound(pt.y());
+ if (y < cy1) {
+ int d = cy1 - y;
+ srcBits += srcBPL * d;
+ ih -= d;
+ y = cy1;
+ }
+ if (y + ih > cy2) {
+ int d = y + ih - cy2;
+ ih -= d;
+ }
+ if (ih < 0)
+ return;
+
+ // call the blend function...
+ int dstSize = rasterBuffer->bytesPerPixel();
+ int dstBPL = rasterBuffer->bytesPerLine();
+ func(rasterBuffer->buffer() + x * dstSize + y * dstBPL, dstBPL,
+ srcBits, srcBPL,
+ iw, ih,
+ alpha);
+}
+
+
+void QRasterPaintEnginePrivate::systemStateChanged()
+{
+ QRect clipRect(0, 0,
+ qMin(QT_RASTER_COORD_LIMIT, device->width()),
+ qMin(QT_RASTER_COORD_LIMIT, device->height()));
+
+ if (!systemClip.isEmpty()) {
+ QRegion clippedDeviceRgn = systemClip & clipRect;
+ deviceRect = clippedDeviceRgn.boundingRect();
+ delete baseClip;
+ baseClip = new QClipData(device->height());
+ baseClip->setClipRegion(clippedDeviceRgn);
+ } else {
+ deviceRect = clipRect;
+ }
+#ifdef QT_DEBUG_DRAW
+ qDebug() << "systemStateChanged" << this << "deviceRect" << deviceRect << clipRect << systemClip;
+#endif
+ Q_Q(QRasterPaintEngine);
+ q->state()->strokeFlags |= QPaintEngine::DirtyClipRegion;
+ q->state()->fillFlags |= QPaintEngine::DirtyClipRegion;
+ q->state()->pixmapFlags |= QPaintEngine::DirtyClipRegion;
+}
+
+void QRasterPaintEnginePrivate::updateMatrixData(QSpanData *spanData, const QBrush &b, const QTransform &m)
+{
+ if (b.d->style == Qt::NoBrush || b.d->style == Qt::SolidPattern)
+ return;
+
+ Q_Q(QRasterPaintEngine);
+ bool bilinear = q->state()->flags.bilinear;
+
+ if (b.d->transform.type() > QTransform::TxNone) { // FALCON: optimise
+ spanData->setupMatrix(b.transform() * m, bilinear);
+ } else {
+ if (m.type() <= QTransform::TxTranslate) {
+ // specialize setupMatrix for translation matrices
+ // to avoid needless matrix inversion
+ spanData->m11 = 1;
+ spanData->m12 = 0;
+ spanData->m13 = 0;
+ spanData->m21 = 0;
+ spanData->m22 = 1;
+ spanData->m23 = 0;
+ spanData->m33 = 1;
+ spanData->dx = -m.dx();
+ spanData->dy = -m.dy();
+ spanData->txop = m.type();
+ spanData->bilinear = bilinear;
+ spanData->fast_matrix = qAbs(m.dx()) < 1e4 && qAbs(m.dy()) < 1e4;
+ spanData->adjustSpanMethods();
+ } else {
+ spanData->setupMatrix(m, bilinear);
+ }
+ }
+}
+
+
+/*!
+ \internal
+*/
+void QRasterPaintEngine::clip(const QVectorPath &path, Qt::ClipOperation op)
+{
+#ifdef QT_DEBUG_DRAW
+ qDebug() << "QRasterPaintEngine::clip(): " << path << op;
+
+ if (path.elements()) {
+ for (int i=0; i<path.elementCount(); ++i) {
+ qDebug() << " - " << path.elements()[i]
+ << "(" << path.points()[i*2] << ", " << path.points()[i*2+1] << ")";
+ }
+ } else {
+ for (int i=0; i<path.elementCount(); ++i) {
+ qDebug() << " ---- "
+ << "(" << path.points()[i*2] << ", " << path.points()[i*2+1] << ")";
+ }
+ }
+#endif
+
+ Q_D(QRasterPaintEngine);
+ QRasterPaintEngineState *s = state();
+
+ const qreal *points = path.points();
+ const QPainterPath::ElementType *types = path.elements();
+
+ // There are some cases that are not supported by clip(QRect)
+ if (op != Qt::UniteClip && (op != Qt::IntersectClip || !s->clip
+ || s->clip->hasRectClip || s->clip->hasRegionClip)) {
+ if (s->matrix.type() <= QTransform::TxTranslate
+ && ((path.shape() == QVectorPath::RectangleHint)
+ || (isRect(points, path.elementCount())
+ && (!types || (types[0] == QPainterPath::MoveToElement
+ && types[1] == QPainterPath::LineToElement
+ && types[2] == QPainterPath::LineToElement
+ && types[3] == QPainterPath::LineToElement))))) {
+#ifdef QT_DEBUG_DRAW
+ qDebug() << " --- optimizing vector clip to rect clip...";
+#endif
+
+ QRectF r(points[0], points[1], points[4]-points[0], points[5]-points[1]);
+ clip(r.toRect(), op);
+ return;
+ }
+ }
+
+ if (op == Qt::NoClip) {
+ if (s->flags.has_clip_ownership)
+ delete s->clip;
+ s->clip = 0;
+ s->flags.has_clip_ownership = false;
+
+ } else {
+ QClipData *base = d->baseClip;
+
+ // Intersect with current clip when available...
+ if (op == Qt::IntersectClip && s->clip)
+ base = s->clip;
+
+ // We always intersect, except when there is nothing to
+ // intersect with, in which case we simplify the operation to
+ // a replace...
+ Qt::ClipOperation isectOp = Qt::IntersectClip;
+ if (base == 0)
+ isectOp = Qt::ReplaceClip;
+
+ QClipData *newClip = new QClipData(d->rasterBuffer->height());
+ newClip->initialize();
+ ClipData clipData = { base, newClip, isectOp };
+ ensureOutlineMapper();
+ d->rasterize(d->outlineMapper->convertPath(path), qt_span_clip, &clipData, 0);
+
+ newClip->fixup();
+
+ if (op == Qt::UniteClip) {
+ // merge clips
+ QClipData *result = new QClipData(d->rasterBuffer->height());
+ QClipData *current = s->clip ? s->clip : new QClipData(d->rasterBuffer->height());
+ qt_merge_clip(current, newClip, result);
+ result->fixup();
+ delete newClip;
+ if (!s->clip)
+ delete current;
+ newClip = result;
+ }
+
+ if (s->flags.has_clip_ownership)
+ delete s->clip;
+
+ s->clip = newClip;
+ s->flags.has_clip_ownership = true;
+ }
+
+ s->fillFlags |= DirtyClipPath;
+ s->strokeFlags |= DirtyClipPath;
+ s->pixmapFlags |= DirtyClipPath;
+
+ d->solid_color_filler.clip = d->clip();
+ d->solid_color_filler.adjustSpanMethods();
+}
+
+
+
+/*!
+ \internal
+*/
+void QRasterPaintEngine::clip(const QRect &rect, Qt::ClipOperation op)
+{
+#ifdef QT_DEBUG_DRAW
+ qDebug() << "QRasterPaintEngine::clip(): " << rect << op;
+#endif
+
+ Q_D(QRasterPaintEngine);
+ QRasterPaintEngineState *s = state();
+
+ if (op == Qt::NoClip) {
+ if (s->flags.has_clip_ownership)
+ delete s->clip;
+ s->clip = d->baseClip;
+ s->flags.has_clip_ownership = false;
+
+ } else if (op == Qt::UniteClip || s->matrix.type() > QTransform::TxScale) {
+ QPaintEngineEx::clip(rect, op);
+ return;
+
+ } else if (op == Qt::ReplaceClip || s->clip == 0) {
+
+ // No current clip, hence we intersect with sysclip and be
+ // done with it...
+ QRect clipRect = s->matrix.mapRect(rect) & d->deviceRect;
+ QRegion clipRegion = systemClip();
+ QClipData *clip = new QClipData(d->rasterBuffer->height());
+
+ if (clipRegion.isEmpty())
+ clip->setClipRect(clipRect);
+ else
+ clip->setClipRegion(clipRegion & clipRect);
+
+ if (s->flags.has_clip_ownership)
+ delete s->clip;
+
+ s->clip = clip;
+ s->flags.has_clip_ownership = true;
+
+ } else { // intersect clip with current clip
+ QClipData *base = s->clip;
+
+ Q_ASSERT(base);
+ if (base->hasRectClip || base->hasRegionClip) {
+ QRect clipRect = s->matrix.mapRect(rect) & d->deviceRect;
+ if (!s->flags.has_clip_ownership) {
+ s->clip = new QClipData(d->rasterBuffer->height());
+ s->flags.has_clip_ownership = true;
+ }
+ if (base->hasRectClip)
+ s->clip->setClipRect(base->clipRect & clipRect);
+ else
+ s->clip->setClipRegion(base->clipRegion & clipRect);
+ } else {
+ QPaintEngineEx::clip(rect, op);
+ return;
+ }
+ }
+
+ s->brushData.clip = d->clip();
+ s->penData.clip = d->clip();
+
+ s->fillFlags |= DirtyClipPath;
+ s->strokeFlags |= DirtyClipPath;
+ s->pixmapFlags |= DirtyClipPath;
+
+ d->solid_color_filler.clip = d->clip();
+ d->solid_color_filler.adjustSpanMethods();
+}
+
+/*!
+ \internal
+*/
+void QRasterPaintEngine::clip(const QRegion &region, Qt::ClipOperation op)
+{
+ QPaintEngineEx::clip(region, op);
+}
+
+/*!
+ \internal
+*/
+void QRasterPaintEngine::clip(const QPainterPath &path, Qt::ClipOperation op)
+{
+ QPaintEngineEx::clip(path, op);
+}
+
+/*!
+ \internal
+*/
+void QRasterPaintEngine::fillPath(const QPainterPath &path, QSpanData *fillData)
+{
+#ifdef QT_DEBUG_DRAW
+ qDebug() << " --- fillPath, bounds=" << path.boundingRect();
+#endif
+
+ if (!fillData->blend)
+ return;
+
+ Q_D(QRasterPaintEngine);
+
+ const QRectF controlPointRect = path.controlPointRect();
+
+ QRasterPaintEngineState *s = state();
+ const QRect deviceRect = s->matrix.mapRect(controlPointRect).toRect();
+ ProcessSpans blend = d->getBrushFunc(deviceRect, fillData);
+ const bool do_clip = (deviceRect.left() < -QT_RASTER_COORD_LIMIT
+ || deviceRect.right() > QT_RASTER_COORD_LIMIT
+ || deviceRect.top() < -QT_RASTER_COORD_LIMIT
+ || deviceRect.bottom() > QT_RASTER_COORD_LIMIT);
+
+ if (!s->flags.antialiased && !do_clip) {
+ d->initializeRasterizer(fillData);
+ d->rasterizer->rasterize(path * s->matrix, path.fillRule());
+ return;
+ }
+
+ ensureOutlineMapper();
+ d->rasterize(d->outlineMapper->convertPath(path), blend, fillData, d->rasterBuffer);
+}
+
+static void fillRect_normalized(const QRect &r, QSpanData *data,
+ QRasterPaintEnginePrivate *pe)
+{
+ int x1, x2, y1, y2;
+
+ bool rectClipped = false;
+
+ if (data->clip) {
+ x1 = qMax(r.x(), data->clip->xmin);
+ x2 = qMin(r.x() + r.width(), data->clip->xmax);
+ y1 = qMax(r.y(), data->clip->ymin);
+ y2 = qMin(r.y() + r.height(), data->clip->ymax);
+ rectClipped = data->clip->hasRectClip;
+
+ } else if (pe) {
+ x1 = qMax(r.x(), pe->deviceRect.x());
+ x2 = qMin(r.x() + r.width(), pe->deviceRect.x() + pe->deviceRect.width());
+ y1 = qMax(r.y(), pe->deviceRect.y());
+ y2 = qMin(r.y() + r.height(), pe->deviceRect.y() + pe->deviceRect.height());
+ } else {
+ x1 = qMax(r.x(), 0);
+ x2 = qMin(r.x() + r.width(), data->rasterBuffer->width());
+ y1 = qMax(r.y(), 0);
+ y2 = qMin(r.y() + r.height(), data->rasterBuffer->height());
+ }
+
+ if (x2 <= x1 || y2 <= y1)
+ return;
+
+ const int width = x2 - x1;
+ const int height = y2 - y1;
+
+ bool isUnclipped = rectClipped
+ || (pe && pe->isUnclipped_normalized(QRect(x1, y1, width, height)));
+
+ if (pe && isUnclipped) {
+ const QPainter::CompositionMode mode = pe->rasterBuffer->compositionMode;
+
+ if (data->fillRect && (mode == QPainter::CompositionMode_Source
+ || (mode == QPainter::CompositionMode_SourceOver
+ && qAlpha(data->solid.color) == 255)))
+ {
+ data->fillRect(data->rasterBuffer, x1, y1, width, height,
+ data->solid.color);
+ return;
+ }
+ }
+
+ ProcessSpans blend = isUnclipped ? data->unclipped_blend : data->blend;
+
+ const int nspans = 256;
+ QT_FT_Span spans[nspans];
+
+ Q_ASSERT(data->blend);
+ int y = y1;
+ while (y < y2) {
+ int n = qMin(nspans, y2 - y);
+ int i = 0;
+ while (i < n) {
+ spans[i].x = x1;
+ spans[i].len = width;
+ spans[i].y = y + i;
+ spans[i].coverage = 255;
+ ++i;
+ }
+
+ blend(n, spans, data);
+ y += n;
+ }
+}
+
+/*!
+ \reimp
+*/
+void QRasterPaintEngine::drawRects(const QRect *rects, int rectCount)
+{
+#ifdef QT_DEBUG_DRAW
+ qDebug(" - QRasterPaintEngine::drawRect(), rectCount=%d", rectCount);
+#endif
+ Q_D(QRasterPaintEngine);
+ QRasterPaintEngineState *s = state();
+
+ // Fill
+ ensureBrush();
+ if (s->brushData.blend) {
+ if (!s->flags.antialiased && s->matrix.type() <= QTransform::TxTranslate) {
+ const QRect *r = rects;
+ const QRect *lastRect = rects + rectCount;
+
+ int offset_x = int(s->matrix.dx());
+ int offset_y = int(s->matrix.dy());
+ while (r < lastRect) {
+ QRect rect = r->normalized();
+ QRect rr = rect.translated(offset_x, offset_y);
+ fillRect_normalized(rr, &s->brushData, d);
+ ++r;
+ }
+ } else {
+ QRectVectorPath path;
+ for (int i=0; i<rectCount; ++i) {
+ path.set(rects[i]);
+ fill(path, s->brush);
+ }
+ }
+ }
+
+ ensurePen();
+ if (s->penData.blend) {
+ if (s->flags.fast_pen && s->lastPen.brush().isOpaque()) {
+ const QRect *r = rects;
+ const QRect *lastRect = rects + rectCount;
+ while (r < lastRect) {
+ int left = r->x();
+ int right = r->x() + r->width();
+ int top = r->y();
+ int bottom = r->y() + r->height();
+
+#ifdef Q_WS_MAC
+ int pts[] = { top, left,
+ top, right,
+ bottom, right,
+ bottom, left };
+#else
+ int pts[] = { left, top,
+ right, top,
+ right, bottom,
+ left, bottom };
+#endif
+
+ strokePolygonCosmetic((QPoint *) pts, 4, WindingMode);
+ ++r;
+ }
+ } else {
+ QRectVectorPath path;
+ for (int i = 0; i < rectCount; ++i) {
+ path.set(rects[i]);
+ stroke(path, s->pen);
+ }
+ }
+ }
+}
+
+/*!
+ \reimp
+*/
+void QRasterPaintEngine::drawRects(const QRectF *rects, int rectCount)
+{
+#ifdef QT_DEBUG_DRAW
+ qDebug(" - QRasterPaintEngine::drawRect(), rectCount=%d", rectCount);
+#endif
+#ifdef QT_FAST_SPANS
+ Q_D(QRasterPaintEngine);
+ QRasterPaintEngineState *s = state();
+
+ ensureState();
+
+ if (s->flags.tx_noshear) {
+ ensureBrush();
+ if (s->brushData.blend) {
+ d->initializeRasterizer(&s->brushData);
+ for (int i = 0; i < rectCount; ++i) {
+ const QRectF &rect = rects[i].normalized();
+ if (rects[i].isEmpty())
+ continue;
+ const QPointF a = s->matrix.map((rect.topLeft() + rect.bottomLeft()) * 0.5f);
+ const QPointF b = s->matrix.map((rect.topRight() + rect.bottomRight()) * 0.5f);
+ d->rasterizer->rasterizeLine(a, b, rect.height() / rect.width());
+ }
+ }
+
+ ensurePen();
+ if (s->penData.blend) {
+ qreal width = s->pen.isCosmetic()
+ ? (s->lastPen.widthF() == 0 ? 1 : s->lastPen.widthF())
+ : s->lastPen.widthF() * s->txscale;
+
+ if (s->flags.fast_pen && s->lastPen.brush().isOpaque()) {
+ for (int i = 0; i < rectCount; ++i) {
+ const QRectF &r = rects[i];
+ qreal left = r.x();
+ qreal right = r.x() + r.width();
+ qreal top = r.y();
+ qreal bottom = r.y() + r.height();
+ qreal pts[] = { left, top,
+ right, top,
+ right, bottom,
+ left, bottom };
+ strokePolygonCosmetic((QPointF *) pts, 4, WindingMode);
+ }
+ } else if (width <= 1 && qpen_style(s->lastPen) == Qt::SolidLine) {
+ d->initializeRasterizer(&s->penData);
+
+ for (int i = 0; i < rectCount; ++i) {
+ const QRectF &rect = rects[i].normalized();
+ if (rect.isEmpty()) {
+ qreal pts[] = { rect.left(), rect.top(), rect.right(), rect.bottom() };
+ QVectorPath vp(pts, 2, 0, QVectorPath::LinesHint);
+ QPaintEngineEx::stroke(vp, s->lastPen);
+ } else {
+ const QPointF tl = s->matrix.map(rect.topLeft());
+ const QPointF tr = s->matrix.map(rect.topRight());
+ const QPointF bl = s->matrix.map(rect.bottomLeft());
+ const QPointF br = s->matrix.map(rect.bottomRight());
+ const qreal w = width / (rect.width() * s->txscale);
+ const qreal h = width / (rect.height() * s->txscale);
+ d->rasterizer->rasterizeLine(tl, tr, w); // top
+ d->rasterizer->rasterizeLine(bl, br, w); // bottom
+ d->rasterizer->rasterizeLine(bl, tl, h); // left
+ d->rasterizer->rasterizeLine(br, tr, h); // right
+ }
+ }
+ } else {
+ for (int i = 0; i < rectCount; ++i) {
+ const QRectF &r = rects[i];
+ qreal left = r.x();
+ qreal right = r.x() + r.width();
+ qreal top = r.y();
+ qreal bottom = r.y() + r.height();
+ qreal pts[] = { left, top,
+ right, top,
+ right, bottom,
+ left, bottom,
+ left, top };
+ QVectorPath vp(pts, 5, 0, QVectorPath::RectangleHint);
+ QPaintEngineEx::stroke(vp, s->lastPen);
+ }
+ }
+ }
+
+ return;
+ }
+#endif // QT_FAST_SPANS
+ QPaintEngineEx::drawRects(rects, rectCount);
+}
+
+void QRasterPaintEnginePrivate::strokeProjective(const QPainterPath &path)
+{
+ Q_Q(QRasterPaintEngine);
+ QRasterPaintEngineState *s = q->state();
+
+ const QPen &pen = s->lastPen;
+ QPainterPathStroker pathStroker;
+ pathStroker.setWidth(pen.width() == 0 ? qreal(1) : pen.width());
+ pathStroker.setCapStyle(pen.capStyle());
+ pathStroker.setJoinStyle(pen.joinStyle());
+ pathStroker.setMiterLimit(pen.miterLimit());
+ pathStroker.setDashOffset(pen.dashOffset());
+
+ if (qpen_style(pen) == Qt::CustomDashLine)
+ pathStroker.setDashPattern(pen.dashPattern());
+ else
+ pathStroker.setDashPattern(qpen_style(pen));
+
+ outlineMapper->setMatrix(QTransform());
+ const QPainterPath stroke = pen.isCosmetic()
+ ? pathStroker.createStroke(s->matrix.map(path))
+ : s->matrix.map(pathStroker.createStroke(path));
+
+ rasterize(outlineMapper->convertPath(stroke), s->penData.blend, &s->penData, rasterBuffer);
+ outlinemapper_xform_dirty = true;
+}
+
+
+
+/*!
+ \internal
+*/
+void QRasterPaintEngine::stroke(const QVectorPath &path, const QPen &pen)
+{
+ QRasterPaintEngineState *s = state();
+ ensurePen(pen);
+ if (!s->penData.blend)
+ return;
+
+ if (s->flags.fast_pen && path.shape() <= QVectorPath::NonCurvedShapeHint && s->lastPen.brush().isOpaque()) {
+ strokePolygonCosmetic((QPointF *) path.points(), path.elementCount(),
+ path.hasImplicitClose()
+ ? WindingMode
+ : PolylineMode);
+
+ } else if (s->flags.non_complex_pen && path.shape() == QVectorPath::LinesHint) {
+ qreal width = s->lastPen.isCosmetic()
+ ? (qpen_widthf(s->lastPen) == 0 ? 1 : qpen_widthf(s->lastPen))
+ : qpen_widthf(s->lastPen) * s->txscale;
+ int dashIndex = 0;
+ qreal dashOffset = s->lastPen.dashOffset();
+ bool inDash = true;
+ qreal patternLength = 0;
+ const QVector<qreal> pattern = s->lastPen.dashPattern();
+ for (int i = 0; i < pattern.size(); ++i)
+ patternLength += pattern.at(i);
+
+ if (patternLength > 0) {
+ int n = qFloor(dashOffset / patternLength);
+ dashOffset -= n * patternLength;
+ while (dashOffset > pattern.at(dashIndex)) {
+ dashOffset -= pattern.at(dashIndex);
+ dashIndex = (dashIndex + 1) % pattern.size();
+ inDash = !inDash;
+ }
+ }
+
+ Q_D(QRasterPaintEngine);
+ d->initializeRasterizer(&s->penData);
+ int lineCount = path.elementCount() / 2;
+ const QLineF *lines = reinterpret_cast<const QLineF *>(path.points());
+
+ for (int i = 0; i < lineCount; ++i) {
+ if (lines[i].p1() == lines[i].p2()) {
+ if (s->lastPen.capStyle() != Qt::FlatCap) {
+ QPointF p = lines[i].p1();
+ QLineF line = s->matrix.map(QLineF(QPointF(p.x() - width*0.5, p.y()),
+ QPointF(p.x() + width*0.5, p.y())));
+ d->rasterizer->rasterizeLine(line.p1(), line.p2(), 1);
+ }
+ continue;
+ }
+
+ const QLineF line = s->matrix.map(lines[i]);
+ if (qpen_style(s->lastPen) == Qt::SolidLine) {
+ d->rasterizer->rasterizeLine(line.p1(), line.p2(),
+ width / line.length(),
+ s->lastPen.capStyle() == Qt::SquareCap);
+ } else {
+ d->rasterizeLine_dashed(line, width,
+ &dashIndex, &dashOffset, &inDash);
+ }
+ }
+ }
+ else
+ QPaintEngineEx::stroke(path, pen);
+}
+
+static inline QRect toNormalizedFillRect(const QRectF &rect)
+{
+ const int x1 = qRound(rect.x() + aliasedCoordinateDelta);
+ const int y1 = qRound(rect.y() + aliasedCoordinateDelta);
+ const int x2 = qRound(rect.right() + aliasedCoordinateDelta);
+ const int y2 = qRound(rect.bottom() + aliasedCoordinateDelta);
+
+ return QRect(x1, y1, x2 - x1, y2 - y1).normalized();
+}
+
+/*!
+ \internal
+*/
+void QRasterPaintEngine::fill(const QVectorPath &path, const QBrush &brush)
+{
+ if (path.isEmpty())
+ return;
+#ifdef QT_DEBUG_DRAW
+ QRealRect vectorPathBounds = path.controlPointRect();
+ QRectF rf(vectorPathBounds.x1, vectorPathBounds.y1,
+ vectorPathBounds.x2 - vectorPathBounds.x1, vectorPathBounds.y2 - vectorPathBounds.y1);
+ qDebug() << "QRasterPaintEngine::fill(): "
+ << "size=" << path.elementCount()
+ << ", hints=" << hex << path.hints()
+ << rf << brush;
+#endif
+
+ Q_D(QRasterPaintEngine);
+ QRasterPaintEngineState *s = state();
+
+ ensureBrush(brush);
+ if (!s->brushData.blend)
+ return;
+
+ if (path.shape() == QVectorPath::RectangleHint) {
+ if (!s->flags.antialiased && s->matrix.type() <= QTransform::TxScale) {
+ const qreal *p = path.points();
+ QPointF tl = QPointF(p[0], p[1]) * s->matrix;
+ QPointF br = QPointF(p[4], p[5]) * s->matrix;
+ fillRect_normalized(toNormalizedFillRect(QRectF(tl, br)), &s->brushData, d);
+ return;
+ }
+ ensureState();
+ if (s->flags.tx_noshear) {
+ d->initializeRasterizer(&s->brushData);
+ // ### Is normalizing really nessesary here?
+ const qreal *p = path.points();
+ QRectF r = QRectF(p[0], p[1], p[2] - p[0], p[7] - p[1]).normalized();
+ if (!r.isEmpty()) {
+ const QPointF a = s->matrix.map((r.topLeft() + r.bottomLeft()) * 0.5f);
+ const QPointF b = s->matrix.map((r.topRight() + r.bottomRight()) * 0.5f);
+ d->rasterizer->rasterizeLine(a, b, r.height() / r.width());
+ }
+ return;
+ }
+ }
+
+ if (path.shape() == QVectorPath::EllipseHint) {
+ if (!s->flags.antialiased && s->matrix.type() <= QTransform::TxScale) {
+ const qreal *p = path.points();
+ QPointF tl = QPointF(p[0], p[1]) * s->matrix;
+ QPointF br = QPointF(p[4], p[5]) * s->matrix;
+ QRectF r = s->matrix.mapRect(QRectF(tl, br));
+
+ ProcessSpans penBlend = d->getPenFunc(r, &s->penData);
+ ProcessSpans brushBlend = d->getBrushFunc(r, &s->brushData);
+ const QRect brect = QRect(int(r.x()), int(r.y()),
+ int_dim(r.x(), r.width()),
+ int_dim(r.y(), r.height()));
+ if (brect == r) {
+ drawEllipse_midpoint_i(brect, d->deviceRect, penBlend, brushBlend,
+ &s->penData, &s->brushData);
+ return;
+ }
+ }
+ }
+
+ // ### Optimize for non transformed ellipses and rectangles...
+ QRealRect r = path.controlPointRect();
+ QRectF cpRect(r.x1, r.y1, r.x2 - r.x1, r.y2 - r.y1);
+ const QRect deviceRect = s->matrix.mapRect(cpRect).toRect();
+ ProcessSpans blend = d->getBrushFunc(deviceRect, &s->brushData);
+
+ // ### Falcon
+// const bool do_clip = (deviceRect.left() < -QT_RASTER_COORD_LIMIT
+// || deviceRect.right() > QT_RASTER_COORD_LIMIT
+// || deviceRect.top() < -QT_RASTER_COORD_LIMIT
+// || deviceRect.bottom() > QT_RASTER_COORD_LIMIT);
+
+ // ### Falonc: implement....
+// if (!s->flags.antialiased && !do_clip) {
+// d->initializeRasterizer(&s->brushData);
+// d->rasterizer->rasterize(path * d->matrix, path.fillRule());
+// return;
+// }
+
+ ensureOutlineMapper();
+ d->rasterize(d->outlineMapper->convertPath(path), blend, &s->brushData, d->rasterBuffer);
+}
+
+void QRasterPaintEngine::fillRect(const QRectF &r, QSpanData *data)
+{
+ Q_D(QRasterPaintEngine);
+ QRasterPaintEngineState *s = state();
+
+ if (!s->flags.antialiased) {
+ uint txop = s->matrix.type();
+ if (txop == QTransform::TxNone) {
+ fillRect_normalized(toNormalizedFillRect(r), data, d);
+ return;
+ } else if (txop == QTransform::TxTranslate) {
+ const QRect rr = toNormalizedFillRect(r.translated(s->matrix.dx(), s->matrix.dy()));
+ fillRect_normalized(rr, data, d);
+ return;
+ } else if (txop == QTransform::TxScale) {
+ const QRect rr = toNormalizedFillRect(s->matrix.mapRect(r));
+ fillRect_normalized(rr, data, d);
+ return;
+ }
+ }
+ ensureState();
+ if (s->flags.tx_noshear) {
+ d->initializeRasterizer(data);
+ QRectF nr = r.normalized();
+ if (!nr.isEmpty()) {
+ const QPointF a = s->matrix.map((nr.topLeft() + nr.bottomLeft()) * 0.5f);
+ const QPointF b = s->matrix.map((nr.topRight() + nr.bottomRight()) * 0.5f);
+ d->rasterizer->rasterizeLine(a, b, nr.height() / nr.width());
+ }
+ return;
+ }
+
+ QPainterPath path;
+ path.addRect(r);
+ ensureOutlineMapper();
+ fillPath(path, data);
+}
+
+/*!
+ \reimp
+*/
+void QRasterPaintEngine::fillRect(const QRectF &r, const QBrush &brush)
+{
+#ifdef QT_DEBUG_DRAW
+ qDebug() << "QRasterPaintEngine::fillRecct(): " << r << brush;
+#endif
+ QRasterPaintEngineState *s = state();
+
+ ensureBrush(brush);
+ if (!s->brushData.blend)
+ return;
+
+ fillRect(r, &s->brushData);
+}
+
+/*!
+ \reimp
+*/
+void QRasterPaintEngine::fillRect(const QRectF &r, const QColor &color)
+{
+#ifdef QT_DEBUG_DRAW
+ qDebug() << "QRasterPaintEngine::fillRect(): " << r << color;
+#endif
+ Q_D(QRasterPaintEngine);
+ QRasterPaintEngineState *s = state();
+
+ d->solid_color_filler.solid.color = PREMUL(ARGB_COMBINE_ALPHA(color.rgba(), s->intOpacity));
+ d->solid_color_filler.clip = d->clip();
+ d->solid_color_filler.adjustSpanMethods();
+
+ fillRect(r, &d->solid_color_filler);
+}
+
+/*!
+ \reimp
+*/
+void QRasterPaintEngine::drawPath(const QPainterPath &path)
+{
+#ifdef QT_DEBUG_DRAW
+ QRectF bounds = path.boundingRect();
+ qDebug(" - QRasterPaintEngine::drawPath(), [%.2f, %.2f, %.2f, %.2f]",
+ bounds.x(), bounds.y(), bounds.width(), bounds.height());
+#endif
+
+ if (path.isEmpty())
+ return;
+
+ // Filling..,
+ Q_D(QRasterPaintEngine);
+ QRasterPaintEngineState *s = state();
+
+ ensureBrush();
+ if (s->brushData.blend) {
+ ensureOutlineMapper();
+ fillPath(path, &s->brushData);
+ }
+
+ // Stroking...
+ ensurePen();
+ if (!s->penData.blend)
+ return;
+ {
+ if (s->matrix.type() >= QTransform::TxProject) {
+ d->strokeProjective(path);
+ } else {
+ Q_ASSERT(s->stroker);
+ d->outlineMapper->beginOutline(Qt::WindingFill);
+ qreal txscale = 1;
+ if (s->pen.isCosmetic() || (qt_scaleForTransform(s->matrix, &txscale) && txscale != 1)) {
+ const qreal strokeWidth = d->basicStroker.strokeWidth();
+ const QRectF clipRect = d->dashStroker ? d->dashStroker->clipRect() : QRectF();
+ if (d->dashStroker)
+ d->dashStroker->setClipRect(d->deviceRect);
+ d->basicStroker.setStrokeWidth(strokeWidth * txscale);
+ d->outlineMapper->setMatrix(QTransform());
+ s->stroker->strokePath(path, d->outlineMapper, s->matrix);
+ d->outlinemapper_xform_dirty = true;
+ d->basicStroker.setStrokeWidth(strokeWidth);
+ if (d->dashStroker)
+ d->dashStroker->setClipRect(clipRect);
+ } else {
+ ensureOutlineMapper();
+ s->stroker->strokePath(path, d->outlineMapper, QTransform());
+ }
+ d->outlineMapper->endOutline();
+
+ ProcessSpans blend = d->getPenFunc(d->outlineMapper->controlPointRect,
+ &s->penData);
+ d->rasterize(d->outlineMapper->outline(), blend, &s->penData, d->rasterBuffer);
+ }
+ }
+
+}
+
+static inline bool isAbove(const QPointF *a, const QPointF *b)
+{
+ return a->y() < b->y();
+}
+
+static bool splitPolygon(const QPointF *points, int pointCount, QVector<QPointF> *upper, QVector<QPointF> *lower)
+{
+ Q_ASSERT(upper);
+ Q_ASSERT(lower);
+
+ Q_ASSERT(pointCount >= 2);
+
+ QVector<const QPointF *> sorted;
+ sorted.reserve(pointCount);
+
+ upper->reserve(pointCount * 3 / 4);
+ lower->reserve(pointCount * 3 / 4);
+
+ for (int i = 0; i < pointCount; ++i)
+ sorted << points + i;
+
+ qSort(sorted.begin(), sorted.end(), isAbove);
+
+ qreal splitY = sorted.at(sorted.size() / 2)->y();
+
+ const QPointF *end = points + pointCount;
+ const QPointF *last = end - 1;
+
+ QVector<QPointF> *bin[2] = { upper, lower };
+
+ for (const QPointF *p = points; p < end; ++p) {
+ int side = p->y() < splitY;
+ int lastSide = last->y() < splitY;
+
+ if (side != lastSide) {
+ if (qFuzzyCompare(p->y(), splitY)) {
+ bin[!side]->append(*p);
+ } else if (qFuzzyCompare(last->y(), splitY)) {
+ bin[side]->append(*last);
+ } else {
+ QPointF delta = *p - *last;
+ QPointF intersection(p->x() + delta.x() * (splitY - p->y()) / delta.y(), splitY);
+
+ bin[0]->append(intersection);
+ bin[1]->append(intersection);
+ }
+ }
+
+ bin[side]->append(*p);
+
+ last = p;
+ }
+
+ // give up if we couldn't reduce the point count
+ return upper->size() < pointCount && lower->size() < pointCount;
+}
+
+/*!
+ \internal
+ */
+void QRasterPaintEngine::fillPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode)
+{
+ Q_D(QRasterPaintEngine);
+ QRasterPaintEngineState *s = state();
+
+ const int maxPoints = 0xffff;
+
+ // max amount of points that raster engine can reliably handle
+ if (pointCount > maxPoints) {
+ QVector<QPointF> upper, lower;
+
+ if (splitPolygon(points, pointCount, &upper, &lower)) {
+ fillPolygon(upper.constData(), upper.size(), mode);
+ fillPolygon(lower.constData(), lower.size(), mode);
+ } else
+ qWarning("Polygon too complex for filling.");
+
+ return;
+ }
+
+ // Compose polygon fill..,
+ QVectorPath vp((qreal *) points, pointCount, 0, QVectorPath::polygonFlags(mode));
+ ensureOutlineMapper();
+ QT_FT_Outline *outline = d->outlineMapper->convertPath(vp);
+
+ // scanconvert.
+ ProcessSpans brushBlend = d->getBrushFunc(d->outlineMapper->controlPointRect,
+ &s->brushData);
+ d->rasterize(outline, brushBlend, &s->brushData, d->rasterBuffer);
+}
+
+/*!
+ \reimp
+*/
+void QRasterPaintEngine::drawPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode)
+{
+ Q_D(QRasterPaintEngine);
+ QRasterPaintEngineState *s = state();
+
+#ifdef QT_DEBUG_DRAW
+ qDebug(" - QRasterPaintEngine::drawPolygon(F), pointCount=%d", pointCount);
+ for (int i=0; i<pointCount; ++i)
+ qDebug() << " - " << points[i];
+#endif
+ Q_ASSERT(pointCount >= 2);
+
+ if (mode != PolylineMode && isRect((qreal *) points, pointCount)) {
+ QRectF r(points[0], points[2]);
+ drawRects(&r, 1);
+ return;
+ }
+
+ ensurePen();
+ ensureBrush();
+ if (mode != PolylineMode) {
+ // Do the fill...
+ if (s->brushData.blend) {
+ d->outlineMapper->setCoordinateRounding(s->penData.blend && s->flags.fast_pen && s->lastPen.brush().isOpaque());
+ fillPolygon(points, pointCount, mode);
+ d->outlineMapper->setCoordinateRounding(false);
+ }
+ }
+
+ // Do the outline...
+ if (s->penData.blend) {
+ if (s->flags.fast_pen && s->lastPen.brush().isOpaque())
+ strokePolygonCosmetic(points, pointCount, mode);
+ else {
+ QVectorPath vp((qreal *) points, pointCount, 0, QVectorPath::polygonFlags(mode));
+ QPaintEngineEx::stroke(vp, s->lastPen);
+ }
+ }
+}
+
+/*!
+ \reimp
+*/
+void QRasterPaintEngine::drawPolygon(const QPoint *points, int pointCount, PolygonDrawMode mode)
+{
+ Q_D(QRasterPaintEngine);
+ QRasterPaintEngineState *s = state();
+
+#ifdef QT_DEBUG_DRAW
+ qDebug(" - QRasterPaintEngine::drawPolygon(I), pointCount=%d", pointCount);
+ for (int i=0; i<pointCount; ++i)
+ qDebug() << " - " << points[i];
+#endif
+ Q_ASSERT(pointCount >= 2);
+ if (mode != PolylineMode && isRect((int *) points, pointCount)) {
+ QRect r(points[0].x(),
+ points[0].y(),
+ points[2].x() - points[0].x(),
+ points[2].y() - points[0].y());
+ drawRects(&r, 1);
+ return;
+ }
+
+ ensureState();
+ ensurePen();
+ if (!(s->flags.int_xform && s->flags.fast_pen && (!s->penData.blend || s->pen.brush().isOpaque()))) {
+ // this calls the float version
+ QPaintEngineEx::drawPolygon(points, pointCount, mode);
+ return;
+ }
+
+ // Do the fill
+ if (mode != PolylineMode) {
+ ensureBrush();
+ if (s->brushData.blend) {
+ // Compose polygon fill..,
+ ensureOutlineMapper();
+ d->outlineMapper->setCoordinateRounding(s->penData.blend != 0);
+ d->outlineMapper->beginOutline(mode == WindingMode ? Qt::WindingFill : Qt::OddEvenFill);
+ d->outlineMapper->moveTo(*points);
+ const QPoint *p = points;
+ const QPoint *ep = points + pointCount - 1;
+ do {
+ d->outlineMapper->lineTo(*(++p));
+ } while (p < ep);
+ d->outlineMapper->endOutline();
+
+ // scanconvert.
+ ProcessSpans brushBlend = d->getBrushFunc(d->outlineMapper->controlPointRect,
+ &s->brushData);
+ d->rasterize(d->outlineMapper->outline(), brushBlend, &s->brushData, d->rasterBuffer);
+ d->outlineMapper->setCoordinateRounding(false);
+ }
+ }
+
+ // Do the outline...
+ if (s->penData.blend) {
+ if (s->flags.fast_pen && s->lastPen.brush().isOpaque())
+ strokePolygonCosmetic(points, pointCount, mode);
+ else {
+ int count = pointCount * 2;
+ QVarLengthArray<qreal> fpoints(count);
+#ifdef Q_WS_MAC
+ for (int i=0; i<count; i+=2) {
+ fpoints[i] = ((int *) points)[i+1];
+ fpoints[i+1] = ((int *) points)[i];
+ }
+#else
+ for (int i=0; i<count; ++i)
+ fpoints[i] = ((int *) points)[i];
+#endif
+ QVectorPath vp((qreal *) fpoints.data(), pointCount, 0, QVectorPath::polygonFlags(mode));
+ QPaintEngineEx::stroke(vp, s->lastPen);
+ }
+ }
+}
+
+/*!
+ \internal
+*/
+void QRasterPaintEngine::strokePolygonCosmetic(const QPointF *points, int pointCount, PolygonDrawMode mode)
+{
+ Q_D(QRasterPaintEngine);
+ QRasterPaintEngineState *s = state();
+
+ Q_ASSERT(s->penData.blend);
+ Q_ASSERT(s->flags.fast_pen);
+
+ bool needs_closing = mode != PolylineMode && points[0] != points[pointCount-1];
+
+ // Use fast path for 0 width / trivial pens.
+ QIntRect devRect;
+ devRect.set(d->deviceRect);
+
+ LineDrawMode mode_for_last = (s->lastPen.capStyle() != Qt::FlatCap
+ ? LineDrawIncludeLastPixel
+ : LineDrawNormal);
+ int dashOffset = int(s->lastPen.dashOffset());
+
+ const QPointF offs(aliasedCoordinateDelta, aliasedCoordinateDelta);
+
+ // Draw all the line segments.
+ for (int i=1; i<pointCount; ++i) {
+
+ QPointF lp1 = points[i-1] * s->matrix + offs;
+ QPointF lp2 = points[i] * s->matrix + offs;
+
+ const QRectF brect(lp1, lp2);
+ ProcessSpans penBlend = d->getPenFunc(brect, &s->penData);
+ if (qpen_style(s->lastPen) == Qt::SolidLine) {
+ drawLine_midpoint_i(qFloor(lp1.x()), qFloor(lp1.y()),
+ qFloor(lp2.x()), qFloor(lp2.y()),
+ penBlend, &s->penData,
+ i == pointCount - 1 ? mode_for_last : LineDrawIncludeLastPixel,
+ devRect);
+ } else {
+ drawLine_midpoint_dashed_i(qFloor(lp1.x()), qFloor(lp1.y()),
+ qFloor(lp2.x()), qFloor(lp2.y()),
+ &s->lastPen,
+ penBlend, &s->penData,
+ i == pointCount - 1 ? mode_for_last : LineDrawIncludeLastPixel,
+ devRect, &dashOffset);
+ }
+ }
+
+ // Polygons are implicitly closed.
+ if (needs_closing) {
+ QPointF lp1 = points[pointCount-1] * s->matrix + offs;
+ QPointF lp2 = points[0] * s->matrix + offs;
+
+ const QRectF brect(lp1, lp2);
+ ProcessSpans penBlend = d->getPenFunc(brect, &s->penData);
+ if (qpen_style(s->lastPen) == Qt::SolidLine) {
+ drawLine_midpoint_i(qFloor(lp1.x()), qFloor(lp1.y()),
+ qFloor(lp2.x()), qFloor(lp2.y()),
+ penBlend, &s->penData,
+ LineDrawIncludeLastPixel,
+ devRect);
+ } else {
+ drawLine_midpoint_dashed_i(qFloor(lp1.x()), qFloor(lp1.y()),
+ qFloor(lp2.x()), qFloor(lp2.y()),
+ &s->lastPen,
+ penBlend, &s->penData,
+ LineDrawIncludeLastPixel,
+ devRect, &dashOffset);
+ }
+ }
+
+}
+
+/*!
+ \internal
+*/
+void QRasterPaintEngine::strokePolygonCosmetic(const QPoint *points, int pointCount, PolygonDrawMode mode)
+{
+ Q_D(QRasterPaintEngine);
+ QRasterPaintEngineState *s = state();
+
+ // We assert here because this function is called from drawRects
+ // and drawPolygon and they already do ensurePen(), so we skip that
+ // here to avoid duplicate checks..
+ Q_ASSERT(s->penData.blend);
+
+ bool needs_closing = mode != PolylineMode && points[0] != points[pointCount-1];
+
+ QIntRect devRect;
+ devRect.set(d->deviceRect);
+
+ LineDrawMode mode_for_last = (s->lastPen.capStyle() != Qt::FlatCap
+ ? LineDrawIncludeLastPixel
+ : LineDrawNormal);
+
+ int m11 = int(s->matrix.m11());
+ int m22 = int(s->matrix.m22());
+ int dx = int(s->matrix.dx());
+ int dy = int(s->matrix.dy());
+ int m13 = int(s->matrix.m13());
+ int m23 = int(s->matrix.m23());
+ bool affine = !m13 && !m23;
+
+ int dashOffset = int(s->lastPen.dashOffset());
+
+ if (affine) {
+ // Draw all the line segments.
+ for (int i=1; i<pointCount; ++i) {
+ const QPoint lp1 = points[i-1] * s->matrix;
+ const QPoint lp2 = points[i] * s->matrix;
+ const QRect brect(lp1, lp2);
+ ProcessSpans penBlend = d->getPenFunc(brect, &s->penData);
+
+ if (qpen_style(s->lastPen) == Qt::SolidLine)
+ drawLine_midpoint_i(lp1.x(), lp1.y(),
+ lp2.x(), lp2.y(),
+ penBlend, &s->penData,
+ i == pointCount - 1 ? mode_for_last : LineDrawIncludeLastPixel,
+ devRect);
+ else
+ drawLine_midpoint_dashed_i(lp1.x(), lp1.y(),
+ lp2.x(), lp2.y(),
+ &s->lastPen,
+ penBlend, &s->penData,
+ i == pointCount - 1 ? mode_for_last : LineDrawIncludeLastPixel,
+ devRect, &dashOffset);
+
+ }
+
+ // Polygons are implicitly closed.
+ if (needs_closing) {
+ const QPoint lp1 = points[pointCount - 1] * s->matrix;
+ const QPoint lp2 = points[0] * s->matrix;
+ const QRect brect(lp1, lp2);
+ ProcessSpans penBlend = d->getPenFunc(brect, &s->penData);
+
+ if (qpen_style(s->lastPen) == Qt::SolidLine)
+ drawLine_midpoint_i(lp1.x(), lp1.y(),
+ lp2.x(), lp2.y(),
+ penBlend, &s->penData, LineDrawIncludeLastPixel,
+ devRect);
+ else
+ drawLine_midpoint_dashed_i(lp1.x(), lp1.y(),
+ lp2.x(), lp2.y(),
+ &s->lastPen,
+ penBlend, &s->penData, LineDrawIncludeLastPixel,
+ devRect, &dashOffset);
+ }
+ } else {
+ // Draw all the line segments.
+ for (int i=1; i<pointCount; ++i) {
+ int x1 = points[i-1].x() * m11 + dx;
+ int y1 = points[i-1].y() * m22 + dy;
+ qreal w = m13*points[i-1].x() + m23*points[i-1].y() + 1.;
+ w = 1/w;
+ x1 = int(x1*w);
+ y1 = int(y1*w);
+ int x2 = points[i].x() * m11 + dx;
+ int y2 = points[i].y() * m22 + dy;
+ w = m13*points[i].x() + m23*points[i].y() + 1.;
+ w = 1/w;
+ x2 = int(x2*w);
+ y2 = int(y2*w);
+
+ const QRect brect(x1, y1, x2 - x1 + 1, y2 - y1 + 1);
+ ProcessSpans penBlend = d->getPenFunc(brect, &s->penData);
+ if (qpen_style(s->lastPen) == Qt::SolidLine)
+ drawLine_midpoint_i(x1, y1, x2, y2,
+ penBlend, &s->penData,
+ i == pointCount - 1 ? mode_for_last : LineDrawIncludeLastPixel,
+ devRect);
+ else
+ drawLine_midpoint_dashed_i(x1, y1, x2, y2,
+ &s->lastPen,
+ penBlend, &s->penData,
+ i == pointCount - 1 ? mode_for_last : LineDrawIncludeLastPixel,
+ devRect, &dashOffset);
+
+ }
+
+ int x1 = points[pointCount-1].x() * m11 + dx;
+ int y1 = points[pointCount-1].y() * m22 + dy;
+ qreal w = m13*points[pointCount-1].x() + m23*points[pointCount-1].y() + 1.;
+ w = 1/w;
+ x1 = int(x1*w);
+ y1 = int(y1*w);
+ int x2 = points[0].x() * m11 + dx;
+ int y2 = points[0].y() * m22 + dy;
+ w = m13*points[0].x() + m23*points[0].y() + 1.;
+ w = 1/w;
+ x2 = int(x2 * w);
+ y2 = int(y2 * w);
+ // Polygons are implicitly closed.
+
+ if (needs_closing) {
+ const QRect brect(x1, y1, x2 - x1 + 1, y2 - y1 + 1);
+ ProcessSpans penBlend = d->getPenFunc(brect, &s->penData);
+ if (qpen_style(s->lastPen) == Qt::SolidLine)
+ drawLine_midpoint_i(x1, y1, x2, y2,
+ penBlend, &s->penData, LineDrawIncludeLastPixel,
+ devRect);
+ else
+ drawLine_midpoint_dashed_i(x1, y1, x2, y2,
+ &s->lastPen,
+ penBlend, &s->penData, LineDrawIncludeLastPixel,
+ devRect, &dashOffset);
+ }
+ }
+}
+
+#define IMAGE_FROM_PIXMAP(pixmap) \
+ pixmap.data->classId() == QPixmapData::RasterClass \
+ ? ((QRasterPixmapData *) pixmap.data)->image \
+ : pixmap.toImage()
+
+/*!
+ \internal
+*/
+void QRasterPaintEngine::drawPixmap(const QPointF &pos, const QPixmap &pixmap)
+{
+#ifdef QT_DEBUG_DRAW
+ qDebug() << " - QRasterPaintEngine::drawPixmap(), pos=" << pos << " pixmap=" << pixmap.size() << "depth=" << pixmap.depth();
+#endif
+ if (pixmap.depth() == 1) {
+ Q_D(QRasterPaintEngine);
+ QRasterPaintEngineState *s = state();
+ if (s->matrix.type() <= QTransform::TxTranslate) {
+ drawBitmap(pos + QPointF(s->matrix.dx(), s->matrix.dy()), pixmap, &s->penData);
+ } else {
+ drawImage(pos, d->rasterBuffer->colorizeBitmap(IMAGE_FROM_PIXMAP(pixmap), s->pen.color()));
+ }
+ } else {
+ QRasterPaintEngine::drawImage(pos, IMAGE_FROM_PIXMAP(pixmap));
+ }
+}
+
+/*!
+ \reimp
+*/
+void QRasterPaintEngine::drawPixmap(const QRectF &r, const QPixmap &pixmap, const QRectF &sr)
+{
+#ifdef QT_DEBUG_DRAW
+ qDebug() << " - QRasterPaintEngine::drawPixmap(), r=" << r << " sr=" << sr << " pixmap=" << pixmap.size() << "depth=" << pixmap.depth();
+#endif
+
+ Q_D(QRasterPaintEngine);
+ QRasterPaintEngineState *s = state();
+
+ if (pixmap.depth() == 1) {
+ if (s->matrix.type() <= QTransform::TxTranslate
+ && r.size() == sr.size()
+ && r.size() == pixmap.size()) {
+ ensurePen();
+ drawBitmap(r.topLeft() + QPointF(s->matrix.dx(), s->matrix.dy()), pixmap, &s->penData);
+ return;
+ } else {
+ drawImage(r, d->rasterBuffer->colorizeBitmap(IMAGE_FROM_PIXMAP(pixmap),
+ s->pen.color()), sr);
+ }
+ } else {
+ drawImage(r, IMAGE_FROM_PIXMAP(pixmap), sr);
+ }
+}
+
+// assumes that rect has positive width and height
+static inline const QRect toRect_normalized(const QRectF &rect)
+{
+ const int x = qRound(rect.x());
+ const int y = qRound(rect.y());
+ const int w = int(rect.width() + qreal(0.5));
+ const int h = int(rect.height() + qreal(0.5));
+
+ return QRect(x, y, w, h);
+}
+
+static inline int fast_ceil_positive(const qreal &v)
+{
+ const int iv = int(v);
+ if (v - iv == 0)
+ return iv;
+ else
+ return iv + 1;
+}
+
+static inline const QRect toAlignedRect_positive(const QRectF &rect)
+{
+ const int xmin = int(rect.x());
+ const int xmax = int(fast_ceil_positive(rect.right()));
+ const int ymin = int(rect.y());
+ const int ymax = int(fast_ceil_positive(rect.bottom()));
+ return QRect(xmin, ymin, xmax - xmin, ymax - ymin);
+}
+
+/*!
+ \internal
+*/
+void QRasterPaintEngine::drawImage(const QPointF &p, const QImage &img)
+{
+#ifdef QT_DEBUG_DRAW
+ qDebug() << " - QRasterPaintEngine::drawImage(), p=" << p << " image=" << img.size() << "depth=" << img.depth();
+#endif
+
+ Q_D(QRasterPaintEngine);
+ QRasterPaintEngineState *s = state();
+
+ if (s->matrix.type() > QTransform::TxTranslate) {
+ drawImage(QRectF(p.x(), p.y(), img.width(), img.height()),
+ img,
+ QRectF(0, 0, img.width(), img.height()));
+ } else {
+
+ const QClipData *clip = d->clip();
+ QPointF pt(p.x() + s->matrix.dx(), p.y() + s->matrix.dy());
+
+ // ### TODO: remove this eventually...
+ static bool NO_BLEND_FUNC = !qgetenv("QT_NO_BLEND_FUNCTIONS").isNull();
+
+ if (s->flags.fast_images && !NO_BLEND_FUNC) {
+ SrcOverBlendFunc func = qBlendFunctions[d->rasterBuffer->format][img.format()];
+ if (func) {
+ if (!clip) {
+ d->drawImage(pt, img, func, d->deviceRect, s->intOpacity);
+ return;
+ } else if (clip->hasRectClip) {
+ d->drawImage(pt, img, func, clip->clipRect, s->intOpacity);
+ return;
+ }
+ }
+ }
+
+ d->image_filler.clip = clip;
+ d->image_filler.initTexture(&img, s->intOpacity, QTextureData::Plain, img.rect());
+ if (!d->image_filler.blend)
+ return;
+ d->image_filler.dx = -pt.x();
+ d->image_filler.dy = -pt.y();
+ QRect rr = img.rect().translated(qRound(pt.x()), qRound(pt.y()));
+
+ fillRect_normalized(rr, &d->image_filler, d);
+ }
+
+}
+
+QRectF qt_mapRect_non_normalizing(const QRectF &r, const QTransform &t)
+{
+ return QRectF(r.topLeft() * t, r.bottomRight() * t);
+}
+
+/*!
+ \reimp
+*/
+void QRasterPaintEngine::drawImage(const QRectF &r, const QImage &img, const QRectF &sr,
+ Qt::ImageConversionFlags)
+{
+#ifdef QT_DEBUG_DRAW
+ qDebug() << " - QRasterPaintEngine::drawImage(), r=" << r << " sr=" << sr << " image=" << img.size() << "depth=" << img.depth();
+#endif
+
+ Q_D(QRasterPaintEngine);
+ QRasterPaintEngineState *s = state();
+ const bool aa = s->flags.antialiased || s->flags.bilinear;
+ if (!aa && sr.size() == QSize(1, 1)) {
+ fillRect(r, QColor::fromRgba(img.pixel(sr.x(), sr.y())));
+ return;
+ }
+
+ bool stretch_sr = r.width() != sr.width() || r.height() != sr.height();
+
+ const QClipData *clip = d->clip();
+
+ if (s->matrix.type() > QTransform::TxTranslate || stretch_sr) {
+
+ if (s->flags.fast_images) {
+ SrcOverScaleFunc func = qScaleFunctions[d->rasterBuffer->format][img.format()];
+ if (func && (!clip || clip->hasRectClip)) {
+ func(d->rasterBuffer->buffer(), d->rasterBuffer->bytesPerLine(),
+ img.bits(), img.bytesPerLine(),
+ qt_mapRect_non_normalizing(r, s->matrix), sr,
+ !clip ? d->deviceRect : clip->clipRect,
+ s->intOpacity);
+ return;
+ }
+ }
+
+ QTransform copy = s->matrix;
+ copy.translate(r.x(), r.y());
+ if (stretch_sr)
+ copy.scale(r.width() / sr.width(), r.height() / sr.height());
+ copy.translate(-sr.x(), -sr.y());
+
+ d->image_filler_xform.clip = clip;
+ d->image_filler_xform.initTexture(&img, s->intOpacity, QTextureData::Plain, toAlignedRect_positive(sr));
+ if (!d->image_filler_xform.blend)
+ return;
+ d->image_filler_xform.setupMatrix(copy, s->flags.bilinear);
+
+#ifdef QT_FAST_SPANS
+ ensureState();
+ if (s->flags.tx_noshear || s->matrix.type() == QTransform::TxScale) {
+ d->initializeRasterizer(&d->image_filler_xform);
+ d->rasterizer->setAntialiased(aa);
+
+ const QPointF offs = aa ? QPointF() : QPointF(aliasedCoordinateDelta, aliasedCoordinateDelta);
+
+ const QRectF &rect = r.normalized();
+ const QPointF a = s->matrix.map((rect.topLeft() + rect.bottomLeft()) * 0.5f) - offs;
+ const QPointF b = s->matrix.map((rect.topRight() + rect.bottomRight()) * 0.5f) - offs;
+
+ if (s->flags.tx_noshear)
+ d->rasterizer->rasterizeLine(a, b, rect.height() / rect.width());
+ else
+ d->rasterizer->rasterizeLine(a, b, qAbs((s->matrix.m22() * rect.height()) / (s->matrix.m11() * rect.width())));
+ return;
+ }
+#endif
+ bool wasAntialiased = s->flags.antialiased;
+ if (!s->flags.antialiased)
+ s->flags.antialiased = s->flags.bilinear;
+ const qreal offs = s->flags.antialiased ? qreal(0) : aliasedCoordinateDelta;
+ QPainterPath path;
+ path.addRect(r);
+ QTransform m = s->matrix;
+ s->matrix = QTransform(m.m11(), m.m12(), m.m13(),
+ m.m21(), m.m22(), m.m23(),
+ m.m31() - offs, m.m32() - offs, m.m33());
+ fillPath(path, &d->image_filler_xform);
+ s->matrix = m;
+ s->flags.antialiased = wasAntialiased;
+ } else {
+
+ if (s->flags.fast_images) {
+ SrcOverBlendFunc func = qBlendFunctions[d->rasterBuffer->format][img.format()];
+ if (func) {
+ QPointF pt(r.x() + s->matrix.dx(), r.y() + s->matrix.dy());
+ if (!clip) {
+ d->drawImage(pt, img, func, d->deviceRect, s->intOpacity, sr.toRect());
+ return;
+ } else if (clip->hasRectClip) {
+ d->drawImage(pt, img, func, clip->clipRect, s->intOpacity, sr.toRect());
+ return;
+ }
+ }
+ }
+
+ d->image_filler.clip = clip;
+ d->image_filler.initTexture(&img, s->intOpacity, QTextureData::Plain, toAlignedRect_positive(sr));
+ if (!d->image_filler.blend)
+ return;
+ d->image_filler.dx = -(r.x() + s->matrix.dx()) + sr.x();
+ d->image_filler.dy = -(r.y() + s->matrix.dy()) + sr.y();
+
+ QRectF rr = r;
+ rr.translate(s->matrix.dx(), s->matrix.dy());
+ fillRect_normalized(toRect_normalized(rr), &d->image_filler, d);
+ }
+}
+
+/*!
+ \reimp
+*/
+void QRasterPaintEngine::drawTiledPixmap(const QRectF &r, const QPixmap &pixmap, const QPointF &sr)
+{
+#ifdef QT_DEBUG_DRAW
+ qDebug() << " - QRasterPaintEngine::drawTiledPixmap(), r=" << r << "pixmap=" << pixmap.size();
+#endif
+ Q_D(QRasterPaintEngine);
+ QRasterPaintEngineState *s = state();
+
+ QImage image;
+ if (pixmap.depth() == 1)
+ image = d->rasterBuffer->colorizeBitmap(IMAGE_FROM_PIXMAP(pixmap), s->pen.color());
+ else
+ image = IMAGE_FROM_PIXMAP(pixmap);
+
+ if (s->matrix.type() > QTransform::TxTranslate) {
+ QTransform copy = s->matrix;
+ copy.translate(r.x(), r.y());
+ copy.translate(-sr.x(), -sr.y());
+ d->image_filler_xform.clip = d->clip();
+ d->image_filler_xform.initTexture(&image, s->intOpacity, QTextureData::Tiled);
+ if (!d->image_filler_xform.blend)
+ return;
+ d->image_filler_xform.setupMatrix(copy, s->flags.bilinear);
+
+#ifdef QT_FAST_SPANS
+ ensureState();
+ if (s->flags.tx_noshear || s->matrix.type() == QTransform::TxScale) {
+ d->initializeRasterizer(&d->image_filler_xform);
+ d->rasterizer->setAntialiased(s->flags.antialiased || s->flags.bilinear);
+
+ const QRectF &rect = r.normalized();
+ const QPointF a = s->matrix.map((rect.topLeft() + rect.bottomLeft()) * 0.5f);
+ const QPointF b = s->matrix.map((rect.topRight() + rect.bottomRight()) * 0.5f);
+ if (s->flags.tx_noshear)
+ d->rasterizer->rasterizeLine(a, b, rect.height() / rect.width());
+ else
+ d->rasterizer->rasterizeLine(a, b, qAbs((s->matrix.m22() * rect.height()) / (s->matrix.m11() * rect.width())));
+ return;
+ }
+#endif
+ bool wasAntialiased = s->flags.antialiased;
+ if (!s->flags.antialiased)
+ s->flags.antialiased = s->flags.bilinear;
+ QPainterPath path;
+ path.addRect(r);
+ fillPath(path, &d->image_filler_xform);
+ s->flags.antialiased = wasAntialiased;
+ } else {
+ d->image_filler.clip = d->clip();
+
+ d->image_filler.initTexture(&image, s->intOpacity, QTextureData::Tiled);
+ if (!d->image_filler.blend)
+ return;
+ d->image_filler.dx = -(r.x() + s->matrix.dx()) + sr.x();
+ d->image_filler.dy = -(r.y() + s->matrix.dy()) + sr.y();
+
+ QRectF rr = r;
+ rr.translate(s->matrix.dx(), s->matrix.dy());
+ fillRect_normalized(rr.toRect().normalized(), &d->image_filler, d);
+ }
+}
+
+
+//QWS hack
+static inline bool monoVal(const uchar* s, int x)
+{
+ return (s[x>>3] << (x&7)) & 0x80;
+}
+
+/*!
+ \internal
+*/
+void QRasterPaintEngine::alphaPenBlt(const void* src, int bpl, int depth, int rx,int ry,int w,int h)
+{
+ Q_D(QRasterPaintEngine);
+ QRasterPaintEngineState *s = state();
+
+ if (!s->penData.blend)
+ return;
+
+ QRasterBuffer *rb = d->rasterBuffer;
+
+ const QRect rect(rx, ry, w, h);
+ const QClipData *clip = d->clip();
+ bool unclipped = false;
+ if (clip) {
+ // inlined QRect::intersects
+ const bool intersects = qMax(clip->xmin, rect.left()) <= qMin(clip->xmax - 1, rect.right())
+ && qMax(clip->ymin, rect.top()) <= qMin(clip->ymax - 1, rect.bottom());
+
+ if (clip->hasRectClip) {
+ unclipped = rx > clip->xmin
+ && rx + w < clip->xmax
+ && ry > clip->ymin
+ && ry + h < clip->ymax;
+ }
+
+ if (!intersects)
+ return;
+ } else {
+ // inlined QRect::intersects
+ const bool intersects = qMax(0, rect.left()) <= qMin(rb->width() - 1, rect.right())
+ && qMax(0, rect.top()) <= qMin(rb->height() - 1, rect.bottom());
+ if (!intersects)
+ return;
+
+ // inlined QRect::contains
+ const bool contains = rect.left() >= 0 && rect.right() < rb->width()
+ && rect.top() >= 0 && rect.bottom() < rb->height();
+
+ unclipped = contains && d->isUnclipped_normalized(rect);
+ }
+
+ ProcessSpans blend = unclipped ? s->penData.unclipped_blend : s->penData.blend;
+ const uchar * scanline = static_cast<const uchar *>(src);
+
+ if (s->flags.fast_text) {
+ if (unclipped) {
+ if (depth == 1) {
+ if (s->penData.bitmapBlit) {
+ s->penData.bitmapBlit(rb, rx, ry, s->penData.solid.color,
+ scanline, w, h, bpl);
+ return;
+ }
+ } else if (depth == 8) {
+ if (s->penData.alphamapBlit) {
+ s->penData.alphamapBlit(rb, rx, ry, s->penData.solid.color,
+ scanline, w, h, bpl, 0);
+ return;
+ }
+ } else if (depth == 32) {
+ // (A)RGB Alpha mask where the alpha component is not used.
+ if (s->penData.alphaRGBBlit) {
+ s->penData.alphaRGBBlit(rb, rx, ry, s->penData.solid.color,
+ (const uint *) scanline, w, h, bpl / 4, 0);
+ return;
+ }
+ }
+ } else if (d->deviceDepth == 32 && (depth == 8 || depth == 32)) {
+ // (A)RGB Alpha mask where the alpha component is not used.
+ if (!clip) {
+ int nx = qMax(0, rx);
+ int ny = qMax(0, ry);
+
+ // Move scanline pointer to compensate for moved x and y
+ int xdiff = nx - rx;
+ int ydiff = ny - ry;
+ scanline += ydiff * bpl;
+ scanline += xdiff * (depth == 32 ? 4 : 1);
+
+ w -= xdiff;
+ h -= ydiff;
+
+ if (nx + w > d->rasterBuffer->width())
+ w = d->rasterBuffer->width() - nx;
+ if (ny + h > d->rasterBuffer->height())
+ h = d->rasterBuffer->height() - ny;
+
+ rx = nx;
+ ry = ny;
+ }
+ if (depth == 8 && s->penData.alphamapBlit) {
+ s->penData.alphamapBlit(rb, rx, ry, s->penData.solid.color,
+ scanline, w, h, bpl, clip);
+ } else if (depth == 32 && s->penData.alphaRGBBlit) {
+ s->penData.alphaRGBBlit(rb, rx, ry, s->penData.solid.color,
+ (const uint *) scanline, w, h, bpl / 4, clip);
+ }
+ return;
+ }
+ }
+
+ int x0 = 0;
+ if (rx < 0) {
+ x0 = -rx;
+ w -= x0;
+ }
+
+ int y0 = 0;
+ if (ry < 0) {
+ y0 = -ry;
+ scanline += bpl * y0;
+ h -= y0;
+ }
+
+ w = qMin(w, rb->width() - qMax(0, rx));
+ h = qMin(h, rb->height() - qMax(0, ry));
+
+ if (w <= 0 || h <= 0)
+ return;
+
+ const int NSPANS = 256;
+ QSpan spans[NSPANS];
+ int current = 0;
+
+ const int x1 = x0 + w;
+ const int y1 = y0 + h;
+
+ if (depth == 1) {
+ for (int y = y0; y < y1; ++y) {
+ for (int x = x0; x < x1; ) {
+ if (!monoVal(scanline, x)) {
+ ++x;
+ continue;
+ }
+
+ if (current == NSPANS) {
+ blend(current, spans, &s->penData);
+ current = 0;
+ }
+ spans[current].x = x + rx;
+ spans[current].y = y + ry;
+ spans[current].coverage = 255;
+ int len = 1;
+ ++x;
+ // extend span until we find a different one.
+ while (x < x1 && monoVal(scanline, x)) {
+ ++x;
+ ++len;
+ }
+ spans[current].len = len;
+ ++current;
+ }
+ scanline += bpl;
+ }
+ } else if (depth == 8) {
+ for (int y = y0; y < y1; ++y) {
+ for (int x = x0; x < x1; ) {
+ // Skip those with 0 coverage
+ if (scanline[x] == 0) {
+ ++x;
+ continue;
+ }
+
+ if (current == NSPANS) {
+ blend(current, spans, &s->penData);
+ current = 0;
+ }
+ int coverage = scanline[x];
+ spans[current].x = x + rx;
+ spans[current].y = y + ry;
+ spans[current].coverage = coverage;
+ int len = 1;
+ ++x;
+
+ // extend span until we find a different one.
+ while (x < x1 && scanline[x] == coverage) {
+ ++x;
+ ++len;
+ }
+ spans[current].len = len;
+ ++current;
+ }
+ scanline += bpl;
+ }
+ } else { // 32-bit alpha...
+ uint *sl = (uint *) src;
+ for (int y = y0; y < y1; ++y) {
+ for (int x = x0; x < x1; ) {
+ // Skip those with 0 coverage
+ if ((sl[x] & 0x00ffffff) == 0) {
+ ++x;
+ continue;
+ }
+
+ if (current == NSPANS) {
+ blend(current, spans, &s->penData);
+ current = 0;
+ }
+ uint rgbCoverage = sl[x];
+ int coverage = qGreen(rgbCoverage);
+ spans[current].x = x + rx;
+ spans[current].y = y + ry;
+ spans[current].coverage = coverage;
+ int len = 1;
+ ++x;
+
+ // extend span until we find a different one.
+ while (x < x1 && sl[x] == rgbCoverage) {
+ ++x;
+ ++len;
+ }
+ spans[current].len = len;
+ ++current;
+ }
+ sl += bpl / sizeof(uint);
+ }
+ }
+// qDebug() << "alphaPenBlt: num spans=" << current
+// << "span:" << spans->x << spans->y << spans->len << spans->coverage;
+ // Call span func for current set of spans.
+ if (current != 0)
+ blend(current, spans, &s->penData);
+}
+
+void QRasterPaintEngine::drawCachedGlyphs(const QPointF &p, const QTextItemInt &ti)
+{
+ Q_D(QRasterPaintEngine);
+ QRasterPaintEngineState *s = state();
+
+ QVarLengthArray<QFixedPoint> positions;
+ QVarLengthArray<glyph_t> glyphs;
+ QTransform matrix = s->matrix;
+ matrix.translate(p.x(), p.y());
+ ti.fontEngine->getGlyphPositions(ti.glyphs, matrix, ti.flags, glyphs, positions);
+
+ QFontEngineGlyphCache::Type glyphType = ti.fontEngine->glyphFormat >= 0 ? QFontEngineGlyphCache::Type(ti.fontEngine->glyphFormat) : d->glyphCacheType;
+
+ QImageTextureGlyphCache *cache =
+ (QImageTextureGlyphCache *) ti.fontEngine->glyphCache(glyphType, s->matrix);
+ if (!cache) {
+ cache = new QImageTextureGlyphCache(glyphType, s->matrix);
+ ti.fontEngine->setGlyphCache(glyphType, cache);
+ }
+
+ cache->populate(ti, glyphs, positions);
+
+ const QImage &image = cache->image();
+ int bpl = image.bytesPerLine();
+
+ int depth = image.depth();
+ int rightShift = 0;
+ int leftShift = 0;
+ if (depth == 32)
+ leftShift = 2; // multiply by 4
+ else if (depth == 1)
+ rightShift = 3; // divide by 8
+
+ int margin = cache->glyphMargin();
+
+ const QFixed offs = QFixed::fromReal(aliasedCoordinateDelta);
+
+ const uchar *bits = image.bits();
+ for (int i=0; i<glyphs.size(); ++i) {
+ const QTextureGlyphCache::Coord &c = cache->coords.value(glyphs[i]);
+ int x = qFloor(positions[i].x + offs) + c.baseLineX - margin;
+ int y = qFloor(positions[i].y + offs) - c.baseLineY - margin;
+
+// printf("drawing [%d %d %d %d] baseline [%d %d], glyph: %d, to: %d %d, pos: %d %d\n",
+// c.x, c.y,
+// c.w, c.h,
+// c.baseLineX, c.baseLineY,
+// glyphs[i],
+// x, y,
+// positions[i].x.toInt(), positions[i].y.toInt());
+
+ alphaPenBlt(bits + ((c.x << leftShift) >> rightShift) + c.y * bpl, bpl, depth, x, y, c.w, c.h);
+ }
+
+ return;
+}
+
+
+
+/*!
+ * Returns true if the rectangle is completly within the current clip
+ * state of the paint engine.
+ */
+bool QRasterPaintEnginePrivate::isUnclipped_normalized(const QRect &r) const
+{
+ const QClipData *cl = clip();
+ if (!cl) {
+ // inline contains() for performance (we know the rects are normalized)
+ const QRect &r1 = deviceRect;
+ return (r.left() >= r1.left() && r.right() <= r1.right()
+ && r.top() >= r1.top() && r.bottom() <= r1.bottom());
+ }
+
+
+ // currently all painting functions clips to deviceRect internally
+ if (cl->clipRect == deviceRect)
+ return true;
+
+ if (cl->hasRegionClip) {
+ // inline contains() for performance (we know the rects are normalized)
+ const QRect &r1 = cl->clipRect;
+ return (r.left() >= r1.left() && r.right() <= r1.right()
+ && r.top() >= r1.top() && r.bottom() <= r1.bottom());
+ } else {
+ return qt_region_strictContains(cl->clipRegion, r);
+ }
+}
+
+bool QRasterPaintEnginePrivate::isUnclipped(const QRect &rect,
+ int penWidth) const
+{
+ Q_Q(const QRasterPaintEngine);
+ const QRasterPaintEngineState *s = q->state();
+ const QClipData *cl = clip();
+ if (!cl) {
+ QRect r = rect.normalized();
+ // inline contains() for performance (we know the rects are normalized)
+ const QRect &r1 = deviceRect;
+ return (r.left() >= r1.left() && r.right() <= r1.right()
+ && r.top() >= r1.top() && r.bottom() <= r1.bottom());
+ }
+
+
+ // currently all painting functions that call this function clip to deviceRect internally
+ if (cl->clipRect == deviceRect)
+ return true;
+
+ if (s->flags.antialiased)
+ ++penWidth;
+
+ QRect r = rect.normalized();
+ if (penWidth > 0) {
+ r.setX(r.x() - penWidth);
+ r.setY(r.y() - penWidth);
+ r.setWidth(r.width() + 2 * penWidth);
+ r.setHeight(r.height() + 2 * penWidth);
+ }
+
+ if (!cl->clipRect.isEmpty()) {
+ // inline contains() for performance (we know the rects are normalized)
+ const QRect &r1 = cl->clipRect;
+ return (r.left() >= r1.left() && r.right() <= r1.right()
+ && r.top() >= r1.top() && r.bottom() <= r1.bottom());
+ } else {
+ return qt_region_strictContains(cl->clipRegion, r);
+ }
+}
+
+inline bool QRasterPaintEnginePrivate::isUnclipped(const QRectF &rect,
+ int penWidth) const
+{
+ return isUnclipped(rect.normalized().toAlignedRect(), penWidth);
+}
+
+inline ProcessSpans
+QRasterPaintEnginePrivate::getBrushFunc(const QRect &rect,
+ const QSpanData *data) const
+{
+ return isUnclipped(rect, 0) ? data->unclipped_blend : data->blend;
+}
+
+inline ProcessSpans
+QRasterPaintEnginePrivate::getBrushFunc(const QRectF &rect,
+ const QSpanData *data) const
+{
+ return isUnclipped(rect, 0) ? data->unclipped_blend : data->blend;
+}
+
+inline ProcessSpans
+QRasterPaintEnginePrivate::getPenFunc(const QRect &rect,
+ const QSpanData *data) const
+{
+ Q_Q(const QRasterPaintEngine);
+ const QRasterPaintEngineState *s = q->state();
+
+ if (!s->flags.fast_pen && s->matrix.type() > QTransform::TxTranslate)
+ return data->blend;
+ const int penWidth = s->flags.fast_pen ? 1 : qCeil(s->pen.widthF());
+ return isUnclipped(rect, penWidth) ? data->unclipped_blend : data->blend;
+}
+
+inline ProcessSpans
+QRasterPaintEnginePrivate::getPenFunc(const QRectF &rect,
+ const QSpanData *data) const
+{
+ Q_Q(const QRasterPaintEngine);
+ const QRasterPaintEngineState *s = q->state();
+
+ if (!s->flags.fast_pen && s->matrix.type() > QTransform::TxTranslate)
+ return data->blend;
+ const int penWidth = s->flags.fast_pen ? 1 : qCeil(s->lastPen.widthF());
+ return isUnclipped(rect, penWidth) ? data->unclipped_blend : data->blend;
+}
+
+/*!
+ \reimp
+*/
+void QRasterPaintEngine::drawTextItem(const QPointF &p, const QTextItem &textItem)
+{
+ const QTextItemInt &ti = static_cast<const QTextItemInt &>(textItem);
+ QRasterPaintEngineState *s = state();
+
+#ifdef QT_DEBUG_DRAW
+ Q_D(QRasterPaintEngine);
+ fprintf(stderr," - QRasterPaintEngine::drawTextItem(), (%.2f,%.2f), string=%s ct=%d\n",
+ p.x(), p.y(), QString::fromRawData(ti.chars, ti.num_chars).toLatin1().data(),
+ d->glyphCacheType);
+#endif
+
+ ensurePen();
+ ensureState();
+
+#if defined (Q_WS_WIN) || defined(Q_WS_MAC)
+
+ bool drawCached = true;
+
+ if (s->matrix.type() >= QTransform::TxProject)
+ drawCached = false;
+
+ // don't try to cache huge fonts
+ if (ti.fontEngine->fontDef.pixelSize * qSqrt(s->matrix.determinant()) >= 64)
+ drawCached = false;
+
+ // ### Remove the TestFontEngine and Box engine crap, in these
+ // ### cases we should delegate painting to the font engine
+ // ### directly...
+
+#if defined(Q_WS_WIN) && !defined(Q_OS_WINCE)
+ QFontEngine::Type fontEngineType = ti.fontEngine->type();
+ // qDebug() << "type" << fontEngineType << s->matrix.type();
+ if ((fontEngineType == QFontEngine::Win && !((QFontEngineWin *) ti.fontEngine)->ttf && s->matrix.type() > QTransform::TxTranslate)
+ || (s->matrix.type() <= QTransform::TxTranslate
+ && (fontEngineType == QFontEngine::TestFontEngine
+ || fontEngineType == QFontEngine::Box))) {
+ drawCached = false;
+ }
+#else
+ if (s->matrix.type() > QTransform::TxTranslate)
+ drawCached = false;
+#endif
+ if (drawCached) {
+ drawCachedGlyphs(p, ti);
+ return;
+ }
+
+#else // Q_WS_WIN || Q_WS_MAC
+
+ QFontEngine *fontEngine = ti.fontEngine;
+
+#if defined(Q_WS_QWS)
+ if (fontEngine->type() == QFontEngine::Box) {
+ fontEngine->draw(this, qFloor(p.x() + aliasedCoordinateDelta), qFloor(p.y() + aliasedCoordinateDelta), ti);
+ return;
+ }
+
+ if (s->matrix.type() < QTransform::TxScale
+ && (fontEngine->type() == QFontEngine::QPF1 || fontEngine->type() == QFontEngine::QPF2
+ || (fontEngine->type() == QFontEngine::Proxy
+ && !(static_cast<QProxyFontEngine *>(fontEngine)->drawAsOutline()))
+ )) {
+ fontEngine->draw(this, qFloor(p.x() + aliasedCoordinateDelta), qFloor(p.y() + aliasedCoordinateDelta), ti);
+ return;
+ }
+#endif // Q_WS_QWS
+
+#if (defined(Q_WS_X11) || defined(Q_WS_QWS)) && !defined(QT_NO_FREETYPE)
+
+#if defined(Q_WS_QWS) && !defined(QT_NO_QWS_QPF2)
+ if (fontEngine->type() == QFontEngine::QPF2) {
+ QFontEngine *renderingEngine = static_cast<QFontEngineQPF *>(fontEngine)->renderingEngine();
+ if (renderingEngine)
+ fontEngine = renderingEngine;
+ }
+#endif
+
+ if (fontEngine->type() != QFontEngine::Freetype) {
+ QPaintEngineEx::drawTextItem(p, ti);
+ return;
+ }
+
+ QFontEngineFT *fe = static_cast<QFontEngineFT *>(fontEngine);
+
+ QTransform matrix = s->matrix;
+ matrix.translate(p.x(), p.y());
+
+ QVarLengthArray<QFixedPoint> positions;
+ QVarLengthArray<glyph_t> glyphs;
+ fe->getGlyphPositions(ti.glyphs, matrix, ti.flags, glyphs, positions);
+ if (glyphs.size() == 0)
+ return;
+
+ // only use subpixel antialiasing when drawing to widgets
+ QFontEngineFT::GlyphFormat neededFormat =
+ painter()->device()->devType() == QInternal::Widget
+ ? fe->defaultGlyphFormat()
+ : QFontEngineFT::Format_A8;
+
+ if (d_func()->mono_surface
+ || fe->isBitmapFont() // alphaPenBlt can handle mono, too
+ )
+ neededFormat = QFontEngineFT::Format_Mono;
+
+ if (neededFormat == QFontEngineFT::Format_None)
+ neededFormat = QFontEngineFT::Format_A8;
+
+ QFontEngineFT::QGlyphSet *gset = fe->defaultGlyphs();
+ if (s->matrix.type() >= QTransform::TxScale) {
+ if (s->matrix.isAffine())
+ gset = fe->loadTransformedGlyphSet(s->matrix);
+ else
+ gset = 0;
+
+ }
+
+ if (!gset || gset->outline_drawing
+ || !fe->loadGlyphs(gset, glyphs.data(), glyphs.size(), neededFormat))
+ {
+ QPaintEngine::drawTextItem(p, ti);
+ return;
+ }
+
+ QFixed offs = QFixed::fromReal(aliasedCoordinateDelta);
+ FT_Face lockedFace = 0;
+
+ int depth;
+ switch (neededFormat) {
+ case QFontEngineFT::Format_Mono:
+ depth = 1;
+ break;
+ case QFontEngineFT::Format_A8:
+ depth = 8;
+ break;
+ case QFontEngineFT::Format_A32:
+ depth = 32;
+ break;
+ default:
+ Q_ASSERT(false);
+ };
+
+ for(int i = 0; i < glyphs.size(); i++) {
+ QFontEngineFT::Glyph *glyph = gset->glyph_data.value(glyphs[i]);
+
+ if (!glyph || glyph->format != neededFormat) {
+ if (!lockedFace)
+ lockedFace = fe->lockFace();
+ glyph = fe->loadGlyph(gset, glyphs[i], neededFormat);
+ }
+
+ if (!glyph || !glyph->data)
+ continue;
+
+ int pitch;
+ switch (neededFormat) {
+ case QFontEngineFT::Format_Mono:
+ pitch = ((glyph->width + 31) & ~31) >> 3;
+ break;
+ case QFontEngineFT::Format_A8:
+ pitch = (glyph->width + 3) & ~3;
+ break;
+ case QFontEngineFT::Format_A32:
+ pitch = glyph->width * 4;
+ break;
+ default:
+ Q_ASSERT(false);
+ };
+
+ alphaPenBlt(glyph->data, pitch, depth,
+ qFloor(positions[i].x + offs) + glyph->x,
+ qFloor(positions[i].y + offs) - glyph->y,
+ glyph->width, glyph->height);
+ }
+ if (lockedFace)
+ fe->unlockFace();
+ return;
+
+#endif
+#endif
+
+ QPaintEngineEx::drawTextItem(p, ti);
+}
+
+/*!
+ \reimp
+*/
+void QRasterPaintEngine::drawPoints(const QPointF *points, int pointCount)
+{
+ Q_D(QRasterPaintEngine);
+ QRasterPaintEngineState *s = state();
+
+ ensurePen();
+ qreal pw = s->lastPen.widthF();
+ if (!s->flags.fast_pen && (s->matrix.type() > QTransform::TxTranslate || pw > 1)) {
+ QPaintEngineEx::drawPoints(points, pointCount);
+
+ } else {
+ if (!s->penData.blend)
+ return;
+
+ QVarLengthArray<QT_FT_Span, 4096> array(pointCount);
+ QT_FT_Span span = { 0, 1, 0, 255 };
+ const QPointF *end = points + pointCount;
+ qreal trans_x, trans_y;
+ int x, y;
+ int left = d->deviceRect.x();
+ int right = left + d->deviceRect.width();
+ int top = d->deviceRect.y();
+ int bottom = top + d->deviceRect.height();
+ int count = 0;
+ while (points < end) {
+ s->matrix.map(points->x(), points->y(), &trans_x, &trans_y);
+ x = qFloor(trans_x);
+ y = qFloor(trans_y);
+ if (x >= left && x < right && y >= top && y < bottom) {
+ if (count > 0) {
+ const QT_FT_Span &last = array[count - 1];
+ // spans must be sorted on y (primary) and x (secondary)
+ if (y < last.y || (y == last.y && x < last.x)) {
+ s->penData.blend(count, array.constData(), &s->penData);
+ count = 0;
+ }
+ }
+
+ span.x = x;
+ span.y = y;
+ array[count++] = span;
+ }
+ ++points;
+ }
+
+ if (count > 0)
+ s->penData.blend(count, array.constData(), &s->penData);
+ }
+}
+
+
+void QRasterPaintEngine::drawPoints(const QPoint *points, int pointCount)
+{
+ Q_D(QRasterPaintEngine);
+ QRasterPaintEngineState *s = state();
+
+ ensurePen();
+ double pw = s->lastPen.widthF();
+ if (!s->flags.fast_pen && (s->matrix.type() > QTransform::TxTranslate || pw > 1)) {
+ QPaintEngineEx::drawPoints(points, pointCount);
+
+ } else {
+ if (!s->penData.blend)
+ return;
+
+ QVarLengthArray<QT_FT_Span, 4096> array(pointCount);
+ QT_FT_Span span = { 0, 1, 0, 255 };
+ const QPoint *end = points + pointCount;
+ qreal trans_x, trans_y;
+ int x, y;
+ int left = d->deviceRect.x();
+ int right = left + d->deviceRect.width();
+ int top = d->deviceRect.y();
+ int bottom = top + d->deviceRect.height();
+ int count = 0;
+ while (points < end) {
+ s->matrix.map(points->x(), points->y(), &trans_x, &trans_y);
+ x = qFloor(trans_x);
+ y = qFloor(trans_y);
+ if (x >= left && x < right && y >= top && y < bottom) {
+ if (count > 0) {
+ const QT_FT_Span &last = array[count - 1];
+ // spans must be sorted on y (primary) and x (secondary)
+ if (y < last.y || (y == last.y && x < last.x)) {
+ s->penData.blend(count, array.constData(), &s->penData);
+ count = 0;
+ }
+ }
+
+ span.x = x;
+ span.y = y;
+ array[count++] = span;
+ }
+ ++points;
+ }
+
+ if (count > 0)
+ s->penData.blend(count, array.constData(), &s->penData);
+ }
+}
+
+/*!
+ \reimp
+*/
+void QRasterPaintEngine::drawLines(const QLine *lines, int lineCount)
+{
+#ifdef QT_DEBUG_DRAW
+ qDebug() << " - QRasterPaintEngine::drawLine()";
+#endif
+ Q_D(QRasterPaintEngine);
+ QRasterPaintEngineState *s = state();
+
+ ensurePen();
+ if (s->flags.fast_pen) {
+ QIntRect bounds; bounds.set(d->deviceRect);
+ LineDrawMode mode = s->lastPen.capStyle() == Qt::FlatCap
+ ? LineDrawNormal
+ : LineDrawIncludeLastPixel;
+
+ int m11 = int(s->matrix.m11());
+ int m22 = int(s->matrix.m22());
+ int dx = qFloor(s->matrix.dx() + aliasedCoordinateDelta);
+ int dy = qFloor(s->matrix.dy() + aliasedCoordinateDelta);
+ int dashOffset = int(s->lastPen.dashOffset());
+ for (int i=0; i<lineCount; ++i) {
+ if (s->flags.int_xform) {
+ const QLine &l = lines[i];
+ int x1 = l.x1() * m11 + dx;
+ int y1 = l.y1() * m22 + dy;
+ int x2 = l.x2() * m11 + dx;
+ int y2 = l.y2() * m22 + dy;
+
+ const QRect brect(QPoint(x1, y1), QPoint(x2, y2));
+ ProcessSpans penBlend = d->getPenFunc(brect, &s->penData);
+ if (qpen_style(s->lastPen) == Qt::SolidLine)
+ drawLine_midpoint_i(x1, y1, x2, y2,
+ penBlend, &s->penData, mode, bounds);
+ else
+ drawLine_midpoint_dashed_i(x1, y1, x2, y2,
+ &s->lastPen, penBlend,
+ &s->penData, mode, bounds,
+ &dashOffset);
+ } else {
+ QLineF line = lines[i] * s->matrix;
+ const QRectF brect(QPointF(line.x1(), line.y1()),
+ QPointF(line.x2(), line.y2()));
+ ProcessSpans penBlend = d->getPenFunc(brect, &s->penData);
+ if (qpen_style(s->lastPen) == Qt::SolidLine)
+ drawLine_midpoint_i(int(line.x1()), int(line.y1()),
+ int(line.x2()), int(line.y2()),
+ penBlend, &s->penData, mode, bounds);
+ else
+ drawLine_midpoint_dashed_i(int(line.x1()), int(line.y1()),
+ int(line.x2()), int(line.y2()),
+ &s->lastPen, penBlend,
+ &s->penData, mode, bounds,
+ &dashOffset);
+ }
+ }
+ } else if (s->penData.blend) {
+ QPaintEngineEx::drawLines(lines, lineCount);
+ }
+}
+
+void QRasterPaintEnginePrivate::rasterizeLine_dashed(QLineF line,
+ qreal width,
+ int *dashIndex,
+ qreal *dashOffset,
+ bool *inDash)
+{
+ Q_Q(QRasterPaintEngine);
+ QRasterPaintEngineState *s = q->state();
+
+ const QPen &pen = s->lastPen;
+ const bool squareCap = (pen.capStyle() == Qt::SquareCap);
+ const QVector<qreal> pattern = pen.dashPattern();
+
+ qreal length = line.length();
+ Q_ASSERT(length > 0);
+ while (length > 0) {
+ const bool rasterize = *inDash;
+ qreal dash = (pattern.at(*dashIndex) - *dashOffset) * width;
+ QLineF l = line;
+
+ if (dash >= length) {
+ dash = length;
+ *dashOffset += dash;
+ length = 0;
+ } else {
+ *dashOffset = 0;
+ *inDash = !(*inDash);
+ *dashIndex = (*dashIndex + 1) % pattern.size();
+ length -= dash;
+ l.setLength(dash);
+ line.setP1(l.p2());
+ }
+
+ if (rasterize && dash != 0)
+ rasterizer->rasterizeLine(l.p1(), l.p2(), width / dash, squareCap);
+ }
+}
+
+/*!
+ \reimp
+*/
+void QRasterPaintEngine::drawLines(const QLineF *lines, int lineCount)
+{
+#ifdef QT_DEBUG_DRAW
+ qDebug() << " - QRasterPaintEngine::drawLine()";
+#endif
+ Q_D(QRasterPaintEngine);
+ QRasterPaintEngineState *s = state();
+
+ ensurePen();
+ if (!s->penData.blend)
+ return;
+ if (s->flags.fast_pen) {
+ QIntRect bounds;
+ bounds.set(d->deviceRect);
+ LineDrawMode mode = s->lastPen.capStyle() == Qt::FlatCap
+ ? LineDrawNormal
+ : LineDrawIncludeLastPixel;
+
+ int dashOffset = int(s->lastPen.dashOffset());
+ for (int i=0; i<lineCount; ++i) {
+ QLineF line = (lines[i] * s->matrix).translated(aliasedCoordinateDelta, aliasedCoordinateDelta);
+ const QRectF brect(QPointF(line.x1(), line.y1()),
+ QPointF(line.x2(), line.y2()));
+ ProcessSpans penBlend = d->getPenFunc(brect, &s->penData);
+ if (qpen_style(s->lastPen) == Qt::SolidLine)
+ drawLine_midpoint_i(int(line.x1()), int(line.y1()),
+ int(line.x2()), int(line.y2()),
+ penBlend, &s->penData, mode, bounds);
+ else
+ drawLine_midpoint_dashed_i(int(line.x1()), int(line.y1()),
+ int(line.x2()), int(line.y2()),
+ &s->lastPen,
+ penBlend, &s->penData, mode,
+ bounds, &dashOffset);
+ }
+ } else {
+ QPaintEngineEx::drawLines(lines, lineCount);
+ }
+}
+
+
+/*!
+ \reimp
+*/
+void QRasterPaintEngine::drawEllipse(const QRectF &rect)
+{
+ Q_D(QRasterPaintEngine);
+ QRasterPaintEngineState *s = state();
+
+ ensurePen();
+ if (((qpen_style(s->lastPen) == Qt::SolidLine && s->flags.fast_pen)
+ || (qpen_style(s->lastPen) == Qt::NoPen && !s->flags.antialiased))
+#ifdef FLOATING_POINT_BUGGY_OR_NO_FPU
+ && qMax(rect.width(), rect.height()) < 128 // integer math breakdown
+#endif
+ && s->matrix.type() <= QTransform::TxScale) // no shear
+ {
+ ensureBrush();
+ const QRectF r = s->matrix.mapRect(rect);
+ ProcessSpans penBlend = d->getPenFunc(r, &s->penData);
+ ProcessSpans brushBlend = d->getBrushFunc(r, &s->brushData);
+ const QRect brect = QRect(int(r.x()), int(r.y()),
+ int_dim(r.x(), r.width()),
+ int_dim(r.y(), r.height()));
+ if (brect == r) {
+ drawEllipse_midpoint_i(brect, d->deviceRect, penBlend, brushBlend,
+ &s->penData, &s->brushData);
+ return;
+ }
+ }
+ QPaintEngineEx::drawEllipse(rect);
+}
+
+/*!
+ \internal
+*/
+#ifdef Q_WS_MAC
+void QRasterPaintEngine::setCGContext(CGContextRef ctx)
+{
+ Q_D(QRasterPaintEngine);
+ d->cgContext = ctx;
+}
+
+/*!
+ \internal
+*/
+CGContextRef QRasterPaintEngine::getCGContext() const
+{
+ Q_D(const QRasterPaintEngine);
+ return d->cgContext;
+}
+#endif
+
+#ifdef Q_WS_WIN
+/*!
+ \internal
+*/
+void QRasterPaintEngine::setDC(HDC hdc) {
+ Q_D(QRasterPaintEngine);
+ d->hdc = hdc;
+}
+
+/*!
+ \internal
+*/
+HDC QRasterPaintEngine::getDC() const
+{
+ Q_D(const QRasterPaintEngine);
+ return d->hdc;
+}
+
+/*!
+ \internal
+*/
+void QRasterPaintEngine::releaseDC(HDC) const
+{
+}
+
+#endif
+
+/*!
+ \internal
+*/
+QPoint QRasterPaintEngine::coordinateOffset() const
+{
+ return QPoint(0, 0);
+}
+
+/*!
+ Draws the given color \a spans with the specified \a color. The \a
+ count parameter specifies the number of spans.
+
+ The default implementation does nothing; reimplement this function
+ to draw the given color \a spans with the specified \a color. Note
+ that this function \e must be reimplemented if the framebuffer is
+ not memory-mapped.
+
+ \sa drawBufferSpan()
+*/
+#if defined(Q_WS_QWS) && !defined(QT_NO_RASTERCALLBACKS)
+void QRasterPaintEngine::drawColorSpans(const QSpan *spans, int count, uint color)
+{
+ Q_UNUSED(spans);
+ Q_UNUSED(count);
+ Q_UNUSED(color);
+ qFatal("QRasterPaintEngine::drawColorSpans must be reimplemented on "
+ "a non memory-mapped device");
+}
+
+/*!
+ \fn void QRasterPaintEngine::drawBufferSpan(const uint *buffer, int size, int x, int y, int length, uint alpha)
+
+ Draws the given \a buffer.
+
+ The default implementation does nothing; reimplement this function
+ to draw a buffer that contains more than one color. Note that this
+ function \e must be reimplemented if the framebuffer is not
+ memory-mapped.
+
+ The \a size parameter specifies the total size of the given \a
+ buffer, while the \a length parameter specifies the number of
+ pixels to draw. The buffer's position is given by (\a x, \a
+ y). The provided \a alpha value is added to each pixel in the
+ buffer when drawing.
+
+ \sa drawColorSpans()
+*/
+void QRasterPaintEngine::drawBufferSpan(const uint *buffer, int bufsize,
+ int x, int y, int length, uint const_alpha)
+{
+ Q_UNUSED(buffer);
+ Q_UNUSED(bufsize);
+ Q_UNUSED(x);
+ Q_UNUSED(y);
+ Q_UNUSED(length);
+ Q_UNUSED(const_alpha);
+ qFatal("QRasterPaintEngine::drawBufferSpan must be reimplemented on "
+ "a non memory-mapped device");
+}
+#endif // Q_WS_QWS
+
+void QRasterPaintEngine::drawBitmap(const QPointF &pos, const QPixmap &pm, QSpanData *fg)
+{
+ Q_ASSERT(fg);
+ if (!fg->blend)
+ return;
+ Q_D(QRasterPaintEngine);
+
+ const QImage image = IMAGE_FROM_PIXMAP(pm);
+ Q_ASSERT(image.depth() == 1);
+
+ const int spanCount = 256;
+ QT_FT_Span spans[spanCount];
+ int n = 0;
+
+ // Boundaries
+ int w = pm.width();
+ int h = pm.height();
+ int ymax = qMin(qRound(pos.y() + h), d->rasterBuffer->height());
+ int ymin = qMax(qRound(pos.y()), 0);
+ int xmax = qMin(qRound(pos.x() + w), d->rasterBuffer->width());
+ int xmin = qMax(qRound(pos.x()), 0);
+
+ int x_offset = xmin - qRound(pos.x());
+
+ QImage::Format format = image.format();
+ for (int y = ymin; y < ymax; ++y) {
+ const uchar *src = image.scanLine(y - qRound(pos.y()));
+ if (format == QImage::Format_MonoLSB) {
+ for (int x = 0; x < xmax - xmin; ++x) {
+ int src_x = x + x_offset;
+ uchar pixel = src[src_x >> 3];
+ if (!pixel) {
+ x += 7 - (src_x%8);
+ continue;
+ }
+ if (pixel & (0x1 << (src_x & 7))) {
+ spans[n].x = xmin + x;
+ spans[n].y = y;
+ spans[n].coverage = 255;
+ int len = 1;
+ while (src_x < w-1 && src[(src_x+1) >> 3] & (0x1 << ((src_x+1) & 7))) {
+ ++src_x;
+ ++len;
+ }
+ spans[n].len = ((len + spans[n].x) > xmax) ? (xmax - spans[n].x) : len;
+ x += len;
+ ++n;
+ if (n == spanCount) {
+ fg->blend(n, spans, fg);
+ n = 0;
+ }
+ }
+ }
+ } else {
+ for (int x = 0; x < xmax - xmin; ++x) {
+ int src_x = x + x_offset;
+ uchar pixel = src[src_x >> 3];
+ if (!pixel) {
+ x += 7 - (src_x%8);
+ continue;
+ }
+ if (pixel & (0x80 >> (x & 7))) {
+ spans[n].x = xmin + x;
+ spans[n].y = y;
+ spans[n].coverage = 255;
+ int len = 1;
+ while (src_x < w-1 && src[(src_x+1) >> 3] & (0x80 >> ((src_x+1) & 7))) {
+ ++src_x;
+ ++len;
+ }
+ spans[n].len = ((len + spans[n].x) > xmax) ? (xmax - spans[n].x) : len;
+ x += len;
+ ++n;
+ if (n == spanCount) {
+ fg->blend(n, spans, fg);
+ n = 0;
+ }
+ }
+ }
+ }
+ }
+ if (n) {
+ fg->blend(n, spans, fg);
+ n = 0;
+ }
+}
+
+/*!
+ \enum QRasterPaintEngine::ClipType
+ \internal
+
+ \value RectClip Indicates that the currently set clip is a single rectangle.
+ \value ComplexClip Indicates that the currently set clip is a combination of several shapes.
+*/
+
+/*!
+ \internal
+ Returns the type of the clip currently set.
+*/
+QRasterPaintEngine::ClipType QRasterPaintEngine::clipType() const
+{
+ Q_D(const QRasterPaintEngine);
+
+ const QClipData *clip = d->clip();
+ if (!clip || clip->hasRectClip)
+ return RectClip;
+ else
+ return ComplexClip;
+}
+
+/*!
+ \internal
+ Returns the bounding rect of the currently set clip.
+*/
+QRect QRasterPaintEngine::clipBoundingRect() const
+{
+ Q_D(const QRasterPaintEngine);
+
+ const QClipData *clip = d->clip();
+
+ if (!clip)
+ return d->deviceRect;
+
+ if (clip->hasRectClip)
+ return clip->clipRect;
+
+ return QRect(clip->xmin, clip->ymin, clip->xmax - clip->xmin, clip->ymax - clip->ymin);
+}
+
+static void qt_merge_clip(const QClipData *c1, const QClipData *c2, QClipData *result)
+{
+ Q_ASSERT(c1->clipSpanHeight == c2->clipSpanHeight && c1->clipSpanHeight == result->clipSpanHeight);
+
+ // ### buffer overflow possible
+ const int BUFFER_SIZE = 4096;
+ int buffer[BUFFER_SIZE];
+ int *b = buffer;
+ int bsize = BUFFER_SIZE;
+
+ QClipData::ClipLine *c1ClipLines = const_cast<QClipData *>(c1)->clipLines();
+ QClipData::ClipLine *c2ClipLines = const_cast<QClipData *>(c2)->clipLines();
+ result->initialize();
+
+ for (int y = 0; y < c1->clipSpanHeight; ++y) {
+ const QSpan *c1_spans = c1ClipLines[y].spans;
+ int c1_count = c1ClipLines[y].count;
+ const QSpan *c2_spans = c2ClipLines[y].spans;
+ int c2_count = c2ClipLines[y].count;
+
+ if (c1_count == 0 && c2_count == 0)
+ continue;
+ if (c1_count == 0) {
+ result->appendSpans(c2_spans, c2_count);
+ continue;
+ } else if (c2_count == 0) {
+ result->appendSpans(c1_spans, c1_count);
+ continue;
+ }
+
+ // we need to merge the two
+
+ // find required length
+ int max = qMax(c1_spans[c1_count - 1].x + c1_spans[c1_count - 1].len,
+ c2_spans[c2_count - 1].x + c2_spans[c2_count - 1].len);
+ if (max > bsize) {
+ b = (int *)realloc(bsize == BUFFER_SIZE ? 0 : b, max*sizeof(int));
+ bsize = max;
+ }
+ memset(buffer, 0, BUFFER_SIZE * sizeof(int));
+
+ // Fill with old spans.
+ for (int i = 0; i < c1_count; ++i) {
+ const QSpan *cs = c1_spans + i;
+ for (int j=cs->x; j<cs->x + cs->len; ++j)
+ buffer[j] = cs->coverage;
+ }
+
+ // Fill with new spans
+ for (int i = 0; i < c2_count; ++i) {
+ const QSpan *cs = c2_spans + i;
+ for (int j = cs->x; j < cs->x + cs->len; ++j) {
+ buffer[j] += cs->coverage;
+ if (buffer[j] > 255)
+ buffer[j] = 255;
+ }
+ }
+
+ int x = 0;
+ while (x<max) {
+
+ // Skip to next span
+ while (x < max && buffer[x] == 0) ++x;
+ if (x >= max) break;
+
+ int sx = x;
+ int coverage = buffer[x];
+
+ // Find length of span
+ while (x < max && buffer[x] == coverage)
+ ++x;
+
+ result->appendSpan(sx, x - sx, y, coverage);
+ }
+ }
+ if (b != buffer)
+ free(b);
+}
+
+void QRasterPaintEnginePrivate::initializeRasterizer(QSpanData *data)
+{
+ Q_Q(QRasterPaintEngine);
+ QRasterPaintEngineState *s = q->state();
+
+ rasterizer->setAntialiased(s->flags.antialiased);
+
+ QRect clipRect(deviceRect);
+ ProcessSpans blend;
+ // ### get from optimized rectbased QClipData
+
+ const QClipData *c = clip();
+ if (c) {
+ const QRect r(QPoint(c->xmin, c->ymin),
+ QPoint(c->xmax, c->ymax));
+ clipRect = clipRect.intersected(r);
+ blend = data->blend;
+ } else {
+ blend = data->unclipped_blend;
+ }
+
+ rasterizer->setClipRect(clipRect);
+ rasterizer->initialize(blend, data);
+}
+
+void QRasterPaintEnginePrivate::rasterize(QT_FT_Outline *outline,
+ ProcessSpans callback,
+ QSpanData *spanData, QRasterBuffer *rasterBuffer)
+{
+ if (!callback || !outline)
+ return;
+
+ Q_Q(QRasterPaintEngine);
+ QRasterPaintEngineState *s = q->state();
+
+ if (!s->flags.antialiased) {
+ initializeRasterizer(spanData);
+
+ const Qt::FillRule fillRule = outline->flags == QT_FT_OUTLINE_NONE
+ ? Qt::WindingFill
+ : Qt::OddEvenFill;
+
+ rasterizer->rasterize(outline, fillRule);
+ return;
+ }
+
+ rasterize(outline, callback, (void *)spanData, rasterBuffer);
+}
+
+void QRasterPaintEnginePrivate::rasterize(QT_FT_Outline *outline,
+ ProcessSpans callback,
+ void *userData, QRasterBuffer *)
+{
+ if (!callback || !outline)
+ return;
+
+ Q_Q(QRasterPaintEngine);
+ QRasterPaintEngineState *s = q->state();
+
+ if (!s->flags.antialiased) {
+ rasterizer->setAntialiased(s->flags.antialiased);
+ rasterizer->setClipRect(deviceRect);
+ rasterizer->initialize(callback, userData);
+
+ const Qt::FillRule fillRule = outline->flags == QT_FT_OUTLINE_NONE
+ ? Qt::WindingFill
+ : Qt::OddEvenFill;
+
+ rasterizer->rasterize(outline, fillRule);
+ return;
+ }
+
+ void *data = userData;
+
+ QT_FT_BBox clip_box = { deviceRect.x(),
+ deviceRect.y(),
+ deviceRect.x() + deviceRect.width(),
+ deviceRect.y() + deviceRect.height() };
+
+ QT_FT_Raster_Params rasterParams;
+ rasterParams.target = 0;
+ rasterParams.source = outline;
+ rasterParams.flags = QT_FT_RASTER_FLAG_CLIP;
+ rasterParams.gray_spans = 0;
+ rasterParams.black_spans = 0;
+ rasterParams.bit_test = 0;
+ rasterParams.bit_set = 0;
+ rasterParams.user = data;
+ rasterParams.clip_box = clip_box;
+
+ bool done = false;
+ int error;
+
+ while (!done) {
+
+ rasterParams.flags |= (QT_FT_RASTER_FLAG_AA | QT_FT_RASTER_FLAG_DIRECT);
+ rasterParams.gray_spans = callback;
+ error = qt_ft_grays_raster.raster_render(*grayRaster, &rasterParams);
+
+ // Out of memory, reallocate some more and try again...
+ if (error == -6) { // -6 is Result_err_OutOfMemory
+ int new_size = rasterPoolSize * 2;
+ if (new_size > 1024 * 1024) {
+ qWarning("QPainter: Rasterization of primitive failed");
+ return;
+ }
+
+#if defined(Q_WS_WIN64)
+ _aligned_free(rasterPoolBase);
+#else
+ free(rasterPoolBase);
+#endif
+
+ rasterPoolSize = new_size;
+ rasterPoolBase =
+#if defined(Q_WS_WIN64)
+ // We make use of setjmp and longjmp in qgrayraster.c which requires
+ // 16-byte alignment, hence we hardcode this requirement here..
+ (unsigned char *) _aligned_malloc(rasterPoolSize, sizeof(void*) * 2);
+#else
+ (unsigned char *) malloc(rasterPoolSize);
+#endif
+
+ qt_ft_grays_raster.raster_done(*grayRaster);
+ qt_ft_grays_raster.raster_new(0, grayRaster);
+ qt_ft_grays_raster.raster_reset(*grayRaster, rasterPoolBase, rasterPoolSize);
+ } else {
+ done = true;
+ }
+ }
+}
+
+void QRasterPaintEnginePrivate::recalculateFastImages()
+{
+ Q_Q(QRasterPaintEngine);
+ QRasterPaintEngineState *s = q->state();
+
+ s->flags.fast_images = !(s->renderHints & QPainter::SmoothPixmapTransform)
+ && rasterBuffer->compositionMode == QPainter::CompositionMode_SourceOver
+ && s->matrix.type() <= QTransform::TxScale;
+}
+
+
+
+QImage QRasterBuffer::colorizeBitmap(const QImage &image, const QColor &color)
+{
+ Q_ASSERT(image.depth() == 1);
+
+ QImage sourceImage = image.convertToFormat(QImage::Format_MonoLSB);
+ QImage dest = QImage(sourceImage.size(), QImage::Format_ARGB32_Premultiplied);
+
+ QRgb fg = PREMUL(color.rgba());
+ QRgb bg = 0;
+
+ int height = sourceImage.height();
+ int width = sourceImage.width();
+ for (int y=0; y<height; ++y) {
+ uchar *source = sourceImage.scanLine(y);
+ QRgb *target = reinterpret_cast<QRgb *>(dest.scanLine(y));
+ for (int x=0; x < width; ++x)
+ target[x] = (source[x>>3] >> (x&7)) & 1 ? fg : bg;
+ }
+ return dest;
+}
+
+QRasterBuffer::~QRasterBuffer()
+{
+}
+
+void QRasterBuffer::init()
+{
+ compositionMode = QPainter::CompositionMode_SourceOver;
+ monoDestinationWithClut = false;
+ destColor0 = 0;
+ destColor1 = 0;
+}
+
+QImage::Format QRasterBuffer::prepare(QImage *image)
+{
+ m_buffer = (uchar *)image->bits();
+ m_width = qMin(QT_RASTER_COORD_LIMIT, image->width());
+ m_height = qMin(QT_RASTER_COORD_LIMIT, image->height());
+ bytes_per_pixel = image->depth()/8;
+ bytes_per_line = image->bytesPerLine();
+
+ format = image->format();
+ drawHelper = qDrawHelper + format;
+ if (image->depth() == 1 && image->colorTable().size() == 2) {
+ monoDestinationWithClut = true;
+ destColor0 = PREMUL(image->colorTable()[0]);
+ destColor1 = PREMUL(image->colorTable()[1]);
+ }
+
+ return format;
+}
+
+void QRasterBuffer::resetBuffer(int val)
+{
+ memset(m_buffer, val, m_height*bytes_per_line);
+}
+
+
+#if defined(Q_WS_QWS)
+void QRasterBuffer::prepare(QCustomRasterPaintDevice *device)
+{
+ m_buffer = reinterpret_cast<uchar*>(device->memory());
+ m_width = qMin(QT_RASTER_COORD_LIMIT, device->width());
+ m_height = qMin(QT_RASTER_COORD_LIMIT, device->height());
+ bytes_per_pixel = device->depth() / 8;
+ bytes_per_line = device->bytesPerLine();
+ format = device->format();
+#ifndef QT_NO_RASTERCALLBACKS
+ if (!m_buffer)
+ drawHelper = qDrawHelperCallback + format;
+ else
+#endif
+ drawHelper = qDrawHelper + format;
+}
+
+class MetricAccessor : public QWidget {
+public:
+ int metric(PaintDeviceMetric m) {
+ return QWidget::metric(m);
+ }
+};
+
+int QCustomRasterPaintDevice::metric(PaintDeviceMetric m) const
+{
+ switch (m) {
+ case PdmWidth:
+ return widget->frameGeometry().width();
+ case PdmHeight:
+ return widget->frameGeometry().height();
+ default:
+ break;
+ }
+
+ return (static_cast<MetricAccessor*>(widget)->metric(m));
+}
+
+int QCustomRasterPaintDevice::bytesPerLine() const
+{
+ return (width() * depth() + 7) / 8;
+}
+#endif // Q_WS_QWS
+
+
+/*!
+ \class QCustomRasterPaintDevice
+ \preliminary
+ \ingroup qws
+ \since 4.2
+
+ \brief The QCustomRasterPaintDevice class is provided to activate
+ hardware accelerated paint engines in Qt for Embedded Linux.
+
+ Note that this class is only available in \l{Qt for Embedded Linux}.
+
+ In \l{Qt for Embedded Linux}, painting is a pure software
+ implementation. But starting with Qt 4.2, it is
+ possible to add an accelerated graphics driver to take advantage
+ of available hardware resources.
+
+ Hardware acceleration is accomplished by creating a custom screen
+ driver, accelerating the copying from memory to the screen, and
+ implementing a custom paint engine accelerating the various
+ painting operations. Then a custom paint device (derived from the
+ QCustomRasterPaintDevice class) and a custom window surface
+ (derived from QWSWindowSurface) must be implemented to make
+ \l{Qt for Embedded Linux} aware of the accelerated driver.
+
+ See the \l {Adding an Accelerated Graphics Driver to Qt for Embedded Linux}
+ documentation for details.
+
+ \sa QRasterPaintEngine, QPaintDevice
+*/
+
+/*!
+ \fn QCustomRasterPaintDevice::QCustomRasterPaintDevice(QWidget *widget)
+
+ Constructs a custom raster based paint device for the given
+ top-level \a widget.
+*/
+
+/*!
+ \fn int QCustomRasterPaintDevice::bytesPerLine() const
+
+ Returns the number of bytes per line in the framebuffer. Note that
+ this number might be larger than the framebuffer width.
+*/
+
+/*!
+ \fn int QCustomRasterPaintDevice::devType() const
+ \internal
+*/
+
+/*!
+ \fn QImage::Format QCustomRasterPaintDevice::format() const
+
+ Returns the format of the device's memory buffet.
+
+ The default format is QImage::Format_ARGB32_Premultiplied. The
+ only other valid format is QImage::Format_RGB16.
+*/
+
+/*!
+ \fn void * QCustomRasterPaintDevice::memory () const
+
+ Returns a pointer to the paint device's memory buffer, or 0 if no
+ such buffer exists.
+*/
+
+/*!
+ \fn int QCustomRasterPaintDevice::metric ( PaintDeviceMetric m ) const
+ \reimp
+*/
+
+/*!
+ \fn QSize QCustomRasterPaintDevice::size () const
+ \internal
+*/
+
+
+QClipData::QClipData(int height)
+{
+ clipSpanHeight = height;
+ m_clipLines = 0;
+
+ allocated = height;
+ m_spans = 0;
+ xmin = xmax = ymin = ymax = 0;
+ count = 0;
+
+ enabled = true;
+ hasRectClip = hasRegionClip = false;
+}
+
+QClipData::~QClipData()
+{
+ if (m_clipLines)
+ free(m_clipLines);
+ if (m_spans)
+ free(m_spans);
+}
+
+void QClipData::initialize()
+{
+ if (m_spans)
+ return;
+
+ m_clipLines = (ClipLine *)calloc(sizeof(ClipLine), clipSpanHeight);
+ m_spans = (QSpan *)malloc(clipSpanHeight*sizeof(QSpan));
+
+ if (hasRectClip) {
+ int y = 0;
+ while (y < ymin) {
+ m_clipLines[y].spans = 0;
+ m_clipLines[y].count = 0;
+ ++y;
+ }
+
+ const int len = clipRect.width();
+ count = 0;
+ while (y < ymax) {
+ QSpan *span = m_spans + count;
+ span->x = xmin;
+ span->len = len;
+ span->y = y;
+ span->coverage = 255;
+ ++count;
+
+ m_clipLines[y].spans = span;
+ m_clipLines[y].count = 1;
+ ++y;
+ }
+
+ while (y < clipSpanHeight) {
+ m_clipLines[y].spans = 0;
+ m_clipLines[y].count = 0;
+ ++y;
+ }
+ } else if (hasRegionClip) {
+
+ const QVector<QRect> rects = clipRegion.rects();
+ const int numRects = rects.size();
+
+ { // resize
+ const int maxSpans = (ymax - ymin) * numRects;
+ if (maxSpans > allocated) {
+ m_spans = (QSpan *)realloc(m_spans, maxSpans * sizeof(QSpan));
+ allocated = maxSpans;
+ }
+ }
+
+ int y = 0;
+ int firstInBand = 0;
+ while (firstInBand < numRects) {
+ const int currMinY = rects.at(firstInBand).y();
+ const int currMaxY = currMinY + rects.at(firstInBand).height();
+
+ while (y < currMinY) {
+ m_clipLines[y].spans = 0;
+ m_clipLines[y].count = 0;
+ ++y;
+ }
+
+ int lastInBand = firstInBand;
+ while (lastInBand + 1 < numRects && rects.at(lastInBand+1).top() == y)
+ ++lastInBand;
+
+ while (y < currMaxY) {
+
+ m_clipLines[y].spans = m_spans + count;
+ m_clipLines[y].count = lastInBand - firstInBand + 1;
+
+ for (int r = firstInBand; r <= lastInBand; ++r) {
+ const QRect &currRect = rects.at(r);
+ QSpan *span = m_spans + count;
+ span->x = currRect.x();
+ span->len = currRect.width();
+ span->y = y;
+ span->coverage = 255;
+ ++count;
+ }
+ ++y;
+ }
+
+ firstInBand = lastInBand + 1;
+ }
+
+ Q_ASSERT(count <= allocated);
+
+ while (y < clipSpanHeight) {
+ m_clipLines[y].spans = 0;
+ m_clipLines[y].count = 0;
+ ++y;
+ }
+
+ }
+}
+
+void QClipData::fixup()
+{
+ Q_ASSERT(m_spans);
+
+ if (count == 0) {
+ ymin = ymax = xmin = xmax = 0;
+ return;
+ }
+
+// qDebug("QClipData::fixup: count=%d",count);
+ int y = -1;
+ ymin = m_spans[0].y;
+ ymax = m_spans[count-1].y + 1;
+ xmin = INT_MAX;
+ xmax = 0;
+ for (int i = 0; i < count; ++i) {
+// qDebug() << " " << spans[i].x << spans[i].y << spans[i].len << spans[i].coverage;
+ if (m_spans[i].y != y) {
+ y = m_spans[i].y;
+ m_clipLines[y].spans = m_spans+i;
+ m_clipLines[y].count = 0;
+// qDebug() << " new line: y=" << y;
+ }
+ ++m_clipLines[y].count;
+ xmin = qMin(xmin, (int)m_spans[i].x);
+ xmax = qMax(xmax, (int)m_spans[i].x + m_spans[i].len);
+ }
+ ++xmax;
+// qDebug("xmin=%d,xmax=%d,ymin=%d,ymax=%d", xmin, xmax, ymin, ymax);
+}
+
+/*
+ Convert \a rect to clip spans.
+ */
+void QClipData::setClipRect(const QRect &rect)
+{
+// qDebug() << "setClipRect" << clipSpanHeight << count << allocated << rect;
+ hasRectClip = true;
+ clipRect = rect;
+
+ xmin = rect.x();
+ xmax = rect.x() + rect.width();
+ ymin = qMin(rect.y(), clipSpanHeight);
+ ymax = qMin(rect.y() + rect.height(), clipSpanHeight);
+
+// qDebug() << xmin << xmax << ymin << ymax;
+}
+
+/*
+ Convert \a region to clip spans.
+ */
+void QClipData::setClipRegion(const QRegion &region)
+{
+ if (region.numRects() == 1) {
+ setClipRect(region.rects().at(0));
+ return;
+ }
+
+ hasRegionClip = true;
+ clipRegion = region;
+
+ { // set bounding rect
+ const QRect rect = region.boundingRect();
+ xmin = rect.x();
+ xmax = rect.x() + rect.width();
+ ymin = rect.y();
+ ymax = rect.y() + rect.height();
+ }
+}
+
+/*!
+ \internal
+ spans must be sorted on y
+*/
+static const QSpan *qt_intersect_spans(const QClipData *clip, int *currentClip,
+ const QSpan *spans, const QSpan *end,
+ QSpan **outSpans, int available)
+{
+ const_cast<QClipData *>(clip)->initialize();
+
+ QSpan *out = *outSpans;
+
+ const QSpan *clipSpans = clip->m_spans + *currentClip;
+ const QSpan *clipEnd = clip->m_spans + clip->count;
+
+ while (available && spans < end ) {
+ if (clipSpans >= clipEnd) {
+ spans = end;
+ break;
+ }
+ if (clipSpans->y > spans->y) {
+ ++spans;
+ continue;
+ }
+ if (spans->y != clipSpans->y) {
+ if (spans->y < clip->count && clip->m_clipLines[spans->y].spans)
+ clipSpans = clip->m_clipLines[spans->y].spans;
+ else
+ ++clipSpans;
+ continue;
+ }
+ Q_ASSERT(spans->y == clipSpans->y);
+
+ int sx1 = spans->x;
+ int sx2 = sx1 + spans->len;
+ int cx1 = clipSpans->x;
+ int cx2 = cx1 + clipSpans->len;
+
+ if (cx1 < sx1 && cx2 < sx1) {
+ ++clipSpans;
+ continue;
+ } else if (sx1 < cx1 && sx2 < cx1) {
+ ++spans;
+ continue;
+ }
+ int x = qMax(sx1, cx1);
+ int len = qMin(sx2, cx2) - x;
+ if (len) {
+ out->x = qMax(sx1, cx1);
+ out->len = qMin(sx2, cx2) - out->x;
+ out->y = spans->y;
+ out->coverage = qt_div_255(spans->coverage * clipSpans->coverage);
+ ++out;
+ --available;
+ }
+ if (sx2 < cx2) {
+ ++spans;
+ } else {
+ ++clipSpans;
+ }
+ }
+
+ *outSpans = out;
+ *currentClip = clipSpans - clip->m_spans;
+ return spans;
+}
+
+static void qt_span_fill_clipped(int spanCount, const QSpan *spans, void *userData)
+{
+// qDebug() << "qt_span_fill_clipped" << spanCount;
+ QSpanData *fillData = reinterpret_cast<QSpanData *>(userData);
+
+ Q_ASSERT(fillData->blend && fillData->unclipped_blend);
+
+ const int NSPANS = 256;
+ QSpan cspans[NSPANS];
+ int currentClip = 0;
+ const QSpan *end = spans + spanCount;
+ while (spans < end) {
+ QSpan *clipped = cspans;
+ spans = qt_intersect_spans(fillData->clip, &currentClip, spans, end, &clipped, NSPANS);
+// qDebug() << "processed " << processed << "clipped" << clipped-cspans
+// << "span:" << cspans->x << cspans->y << cspans->len << spans->coverage;
+
+ if (clipped - cspans)
+ fillData->unclipped_blend(clipped - cspans, cspans, fillData);
+ }
+}
+
+/*
+ \internal
+ Clip spans to \a{clip}-rectangle.
+ Returns number of unclipped spans
+*/
+static int qt_intersect_spans(QT_FT_Span *spans, int numSpans,
+ const QRect &clip)
+{
+ const short minx = clip.left();
+ const short miny = clip.top();
+ const short maxx = clip.right();
+ const short maxy = clip.bottom();
+
+ int n = 0;
+ for (int i = 0; i < numSpans; ++i) {
+ if (spans[i].y > maxy)
+ break;
+ if (spans[i].y < miny
+ || spans[i].x > maxx
+ || spans[i].x + spans[i].len <= minx) {
+ continue;
+ }
+ if (spans[i].x < minx) {
+ spans[n].len = qMin(spans[i].len - (minx - spans[i].x), maxx - minx + 1);
+ spans[n].x = minx;
+ } else {
+ spans[n].x = spans[i].x;
+ spans[n].len = qMin(spans[i].len, ushort(maxx - spans[n].x + 1));
+ }
+ if (spans[n].len == 0)
+ continue;
+ spans[n].y = spans[i].y;
+ spans[n].coverage = spans[i].coverage;
+ ++n;
+ }
+ return n;
+}
+
+/*
+ \internal
+ Clip spans to \a{clip}-region.
+ Returns number of unclipped spans
+*/
+static int qt_intersect_spans(QT_FT_Span *spans, int numSpans,
+ int *currSpan,
+ QT_FT_Span *outSpans, int maxOut,
+ const QRegion &clip)
+{
+ const QVector<QRect> rects = clip.rects();
+ const int numRects = rects.size();
+
+ int r = 0;
+ short miny, minx, maxx, maxy;
+ {
+ const QRect &rect = rects[0];
+ miny = rect.top();
+ minx = rect.left();
+ maxx = rect.right();
+ maxy = rect.bottom();
+ }
+
+ // TODO: better mapping of currY and startRect
+
+ int n = 0;
+ int i = *currSpan;
+ int currY = spans[i].y;
+ while (i < numSpans) {
+
+ if (spans[i].y != currY && r != 0) {
+ currY = spans[i].y;
+ r = 0;
+ const QRect &rect = rects[r];
+ miny = rect.top();
+ minx = rect.left();
+ maxx = rect.right();
+ maxy = rect.bottom();
+ }
+
+ if (spans[i].y < miny) {
+ ++i;
+ continue;
+ }
+
+ if (spans[i].y > maxy || spans[i].x > maxx) {
+ if (++r >= numRects) {
+ ++i;
+ continue;
+ }
+
+ const QRect &rect = rects[r];
+ miny = rect.top();
+ minx = rect.left();
+ maxx = rect.right();
+ maxy = rect.bottom();
+ continue;
+ }
+
+ if (spans[i].x + spans[i].len <= minx) {
+ ++i;
+ continue;
+ }
+
+ outSpans[n].y = spans[i].y;
+ outSpans[n].coverage = spans[i].coverage;
+
+ if (spans[i].x < minx) {
+ const ushort cutaway = minx - spans[i].x;
+ outSpans[n].len = qMin(spans[i].len - cutaway, maxx - minx + 1);
+ outSpans[n].x = minx;
+ if (outSpans[n].len == spans[i].len - cutaway) {
+ ++i;
+ } else {
+ // span wider than current rect
+ spans[i].len -= outSpans[n].len + cutaway;
+ spans[i].x = maxx + 1;
+ }
+ } else { // span starts inside current rect
+ outSpans[n].x = spans[i].x;
+ outSpans[n].len = qMin(spans[i].len,
+ ushort(maxx - spans[i].x + 1));
+ if (outSpans[n].len == spans[i].len) {
+ ++i;
+ } else {
+ // span wider than current rect
+ spans[i].len -= outSpans[n].len;
+ spans[i].x = maxx + 1;
+ }
+ }
+
+ if (++n >= maxOut)
+ break;
+ }
+
+ *currSpan = i;
+ return n;
+}
+
+static void qt_span_fill_clipRect(int count, const QSpan *spans,
+ void *userData)
+{
+ QSpanData *fillData = reinterpret_cast<QSpanData *>(userData);
+ Q_ASSERT(fillData->blend && fillData->unclipped_blend);
+
+ Q_ASSERT(fillData->clip);
+ Q_ASSERT(!fillData->clip->clipRect.isEmpty());
+
+ // hw: check if this const_cast<> is safe!!!
+ count = qt_intersect_spans(const_cast<QSpan*>(spans), count,
+ fillData->clip->clipRect);
+ if (count > 0)
+ fillData->unclipped_blend(count, spans, fillData);
+}
+
+static void qt_span_fill_clipRegion(int count, const QSpan *spans,
+ void *userData)
+{
+ QSpanData *fillData = reinterpret_cast<QSpanData *>(userData);
+ Q_ASSERT(fillData->blend && fillData->unclipped_blend);
+
+ Q_ASSERT(fillData->clip);
+ Q_ASSERT(!fillData->clip->clipRegion.isEmpty());
+
+ const int NSPANS = 256;
+ QSpan cspans[NSPANS];
+ int currentClip = 0;
+ while (currentClip < count) {
+ const int unclipped = qt_intersect_spans(const_cast<QSpan*>(spans),
+ count, &currentClip,
+ &cspans[0], NSPANS,
+ fillData->clip->clipRegion);
+ if (unclipped > 0)
+ fillData->unclipped_blend(unclipped, cspans, fillData);
+ }
+
+}
+
+static void qt_span_clip(int count, const QSpan *spans, void *userData)
+{
+ ClipData *clipData = reinterpret_cast<ClipData *>(userData);
+
+// qDebug() << " qt_span_clip: " << count << clipData->operation;
+// for (int i = 0; i < qMin(count, 10); ++i) {
+// qDebug() << " " << spans[i].x << spans[i].y << spans[i].len << spans[i].coverage;
+// }
+
+ switch (clipData->operation) {
+
+ case Qt::IntersectClip:
+ {
+ QClipData *newClip = clipData->newClip;
+ newClip->initialize();
+
+ int currentClip = 0;
+ const QSpan *end = spans + count;
+ while (spans < end) {
+ QSpan *newspans = newClip->m_spans + newClip->count;
+ spans = qt_intersect_spans(clipData->oldClip, &currentClip, spans, end,
+ &newspans, newClip->allocated - newClip->count);
+ newClip->count = newspans - newClip->m_spans;
+ if (spans < end) {
+ newClip->allocated *= 2;
+ newClip->m_spans = (QSpan *)realloc(newClip->m_spans, newClip->allocated*sizeof(QSpan));
+ }
+ }
+ }
+ break;
+
+ case Qt::UniteClip:
+ case Qt::ReplaceClip:
+ clipData->newClip->appendSpans(spans, count);
+ break;
+ case Qt::NoClip:
+ break;
+ }
+}
+
+#ifndef QT_NO_DEBUG
+QImage QRasterBuffer::bufferImage() const
+{
+ QImage image(m_width, m_height, QImage::Format_ARGB32_Premultiplied);
+
+ for (int y = 0; y < m_height; ++y) {
+ uint *span = (uint *)const_cast<QRasterBuffer *>(this)->scanLine(y);
+
+ for (int x=0; x<m_width; ++x) {
+ uint argb = span[x];
+ image.setPixel(x, y, argb);
+ }
+ }
+ return image;
+}
+#endif
+
+
+void QRasterBuffer::flushToARGBImage(QImage *target) const
+{
+ int w = qMin(m_width, target->width());
+ int h = qMin(m_height, target->height());
+
+ for (int y=0; y<h; ++y) {
+ uint *sourceLine = (uint *)const_cast<QRasterBuffer *>(this)->scanLine(y);
+ QRgb *dest = (QRgb *) target->scanLine(y);
+ for (int x=0; x<w; ++x) {
+ QRgb pixel = sourceLine[x];
+ int alpha = qAlpha(pixel);
+ if (!alpha) {
+ dest[x] = 0;
+ } else {
+ dest[x] = (alpha << 24)
+ | ((255*qRed(pixel)/alpha) << 16)
+ | ((255*qGreen(pixel)/alpha) << 8)
+ | ((255*qBlue(pixel)/alpha) << 0);
+ }
+ }
+ }
+}
+
+
+class QGradientCache
+{
+ struct CacheInfo
+ {
+ inline CacheInfo(QGradientStops s, int op, QGradient::InterpolationMode mode) :
+ stops(s), opacity(op), interpolationMode(mode) {}
+ uint buffer[GRADIENT_STOPTABLE_SIZE];
+ QGradientStops stops;
+ int opacity;
+ QGradient::InterpolationMode interpolationMode;
+ };
+
+ typedef QMultiHash<quint64, CacheInfo> QGradientColorTableHash;
+
+public:
+ inline const uint *getBuffer(const QGradient &gradient, int opacity) {
+ quint64 hash_val = 0;
+
+ QGradientStops stops = gradient.stops();
+ for (int i = 0; i < stops.size() && i <= 2; i++)
+ hash_val += stops[i].second.rgba();
+
+ QGradientColorTableHash::const_iterator it = cache.constFind(hash_val);
+
+ if (it == cache.constEnd())
+ return addCacheElement(hash_val, gradient, opacity);
+ else {
+ do {
+ const CacheInfo &cache_info = it.value();
+ if (cache_info.stops == stops && cache_info.opacity == opacity && cache_info.interpolationMode == gradient.interpolationMode())
+ return cache_info.buffer;
+ ++it;
+ } while (it != cache.constEnd() && it.key() == hash_val);
+ // an exact match for these stops and opacity was not found, create new cache
+ return addCacheElement(hash_val, gradient, opacity);
+ }
+ }
+
+ inline int paletteSize() const { return GRADIENT_STOPTABLE_SIZE; }
+protected:
+ inline int maxCacheSize() const { return 60; }
+ inline void generateGradientColorTable(const QGradient& g,
+ uint *colorTable,
+ int size, int opacity) const;
+ uint *addCacheElement(quint64 hash_val, const QGradient &gradient, int opacity) {
+ if (cache.size() == maxCacheSize()) {
+ int elem_to_remove = qrand() % maxCacheSize();
+ cache.remove(cache.keys()[elem_to_remove]); // may remove more than 1, but OK
+ }
+ CacheInfo cache_entry(gradient.stops(), opacity, gradient.interpolationMode());
+ generateGradientColorTable(gradient, cache_entry.buffer, paletteSize(), opacity);
+ return cache.insert(hash_val, cache_entry).value().buffer;
+ }
+
+ QGradientColorTableHash cache;
+};
+
+void QGradientCache::generateGradientColorTable(const QGradient& gradient, uint *colorTable, int size, int opacity) const
+{
+ QGradientStops stops = gradient.stops();
+ int stopCount = stops.count();
+ Q_ASSERT(stopCount > 0);
+
+ bool colorInterpolation = (gradient.interpolationMode() == QGradient::ColorInterpolation);
+
+ uint current_color = ARGB_COMBINE_ALPHA(stops[0].second.rgba(), opacity);
+ if (stopCount == 1) {
+ current_color = PREMUL(current_color);
+ for (int i = 0; i < size; ++i)
+ colorTable[i] = current_color;
+ return;
+ }
+
+ // The position where the gradient begins and ends
+ qreal begin_pos = stops[0].first;
+ qreal end_pos = stops[stopCount-1].first;
+
+ int pos = 0; // The position in the color table.
+ uint next_color;
+
+ qreal incr = 1 / qreal(size); // the double increment.
+ qreal dpos = 1.5 * incr; // current position in gradient stop list (0 to 1)
+
+ // Up to first point
+ colorTable[pos++] = PREMUL(current_color);
+ while (dpos <= begin_pos) {
+ colorTable[pos] = colorTable[pos - 1];
+ ++pos;
+ dpos += incr;
+ }
+
+ int current_stop = 0; // We always interpolate between current and current + 1.
+
+ qreal t; // position between current left and right stops
+ qreal t_delta; // the t increment per entry in the color table
+
+ if (dpos < end_pos) {
+ // Gradient area
+ while (dpos > stops[current_stop+1].first)
+ ++current_stop;
+
+ if (current_stop != 0)
+ current_color = ARGB_COMBINE_ALPHA(stops[current_stop].second.rgba(), opacity);
+ next_color = ARGB_COMBINE_ALPHA(stops[current_stop+1].second.rgba(), opacity);
+
+ if (colorInterpolation) {
+ current_color = PREMUL(current_color);
+ next_color = PREMUL(next_color);
+ }
+
+ qreal diff = stops[current_stop+1].first - stops[current_stop].first;
+ qreal c = (diff == 0) ? qreal(0) : 256 / diff;
+ t = (dpos - stops[current_stop].first) * c;
+ t_delta = incr * c;
+
+ while (true) {
+ Q_ASSERT(current_stop < stopCount);
+
+ int dist = qRound(t);
+ int idist = 256 - dist;
+
+ if (colorInterpolation)
+ colorTable[pos] = INTERPOLATE_PIXEL_256(current_color, idist, next_color, dist);
+ else
+ colorTable[pos] = PREMUL(INTERPOLATE_PIXEL_256(current_color, idist, next_color, dist));
+
+ ++pos;
+ dpos += incr;
+
+ if (dpos >= end_pos)
+ break;
+
+ t += t_delta;
+
+ int skip = 0;
+ while (dpos > stops[current_stop+skip+1].first)
+ ++skip;
+
+ if (skip != 0) {
+ current_stop += skip;
+ if (skip == 1)
+ current_color = next_color;
+ else
+ current_color = ARGB_COMBINE_ALPHA(stops[current_stop].second.rgba(), opacity);
+ next_color = ARGB_COMBINE_ALPHA(stops[current_stop+1].second.rgba(), opacity);
+
+ if (colorInterpolation) {
+ if (skip != 1)
+ current_color = PREMUL(current_color);
+ next_color = PREMUL(next_color);
+ }
+
+ qreal diff = stops[current_stop+1].first - stops[current_stop].first;
+ qreal c = (diff == 0) ? qreal(0) : 256 / diff;
+ t = (dpos - stops[current_stop].first) * c;
+ t_delta = incr * c;
+ }
+ }
+ }
+
+ // After last point
+ current_color = PREMUL(ARGB_COMBINE_ALPHA(stops[stopCount - 1].second.rgba(), opacity));
+ while (pos < size - 1) {
+ colorTable[pos] = current_color;
+ ++pos;
+ }
+
+ // Make sure the last color stop is represented at the end of the table
+ colorTable[size - 1] = current_color;
+}
+
+Q_GLOBAL_STATIC(QGradientCache, qt_gradient_cache)
+
+
+void QSpanData::init(QRasterBuffer *rb, const QRasterPaintEngine *pe)
+{
+ rasterBuffer = rb;
+#ifdef Q_WS_QWS
+ rasterEngine = const_cast<QRasterPaintEngine *>(pe);
+#endif
+ type = None;
+ txop = 0;
+ bilinear = false;
+ m11 = m22 = m33 = 1.;
+ m12 = m13 = m21 = m23 = dx = dy = 0.0;
+ clip = pe ? pe->d_func()->clip() : 0;
+}
+
+extern QImage qt_imageForBrush(int brushStyle, bool invert);
+
+void QSpanData::setup(const QBrush &brush, int alpha)
+{
+ Qt::BrushStyle brushStyle = qbrush_style(brush);
+ switch (brushStyle) {
+ case Qt::SolidPattern: {
+ type = Solid;
+ QColor c = qbrush_color(brush);
+ solid.color = PREMUL(ARGB_COMBINE_ALPHA(c.rgba(), alpha));
+ break;
+ }
+
+ case Qt::LinearGradientPattern:
+ {
+ type = LinearGradient;
+ const QLinearGradient *g = static_cast<const QLinearGradient *>(brush.gradient());
+ gradient.alphaColor = !brush.isOpaque() || alpha != 256;
+ gradient.colorTable = const_cast<uint*>(qt_gradient_cache()->getBuffer(*g, alpha));
+ gradient.spread = g->spread();
+
+ QLinearGradientData &linearData = gradient.linear;
+
+ linearData.origin.x = g->start().x();
+ linearData.origin.y = g->start().y();
+ linearData.end.x = g->finalStop().x();
+ linearData.end.y = g->finalStop().y();
+ break;
+ }
+
+ case Qt::RadialGradientPattern:
+ {
+ type = RadialGradient;
+ const QRadialGradient *g = static_cast<const QRadialGradient *>(brush.gradient());
+ gradient.alphaColor = !brush.isOpaque() || alpha != 256;
+ gradient.colorTable = const_cast<uint*>(qt_gradient_cache()->getBuffer(*g, alpha));
+ gradient.spread = g->spread();
+
+ QRadialGradientData &radialData = gradient.radial;
+
+ QPointF center = g->center();
+ radialData.center.x = center.x();
+ radialData.center.y = center.y();
+ QPointF focal = g->focalPoint();
+ radialData.focal.x = focal.x();
+ radialData.focal.y = focal.y();
+ radialData.radius = g->radius();
+ }
+ break;
+
+ case Qt::ConicalGradientPattern:
+ {
+ type = ConicalGradient;
+ const QConicalGradient *g = static_cast<const QConicalGradient *>(brush.gradient());
+ gradient.alphaColor = !brush.isOpaque() || alpha != 256;
+ gradient.colorTable = const_cast<uint*>(qt_gradient_cache()->getBuffer(*g, alpha));
+ gradient.spread = QGradient::RepeatSpread;
+
+ QConicalGradientData &conicalData = gradient.conical;
+
+ QPointF center = g->center();
+ conicalData.center.x = center.x();
+ conicalData.center.y = center.y();
+ conicalData.angle = g->angle() * 2 * Q_PI / 360.0;
+ }
+ break;
+
+ case Qt::Dense1Pattern:
+ case Qt::Dense2Pattern:
+ case Qt::Dense3Pattern:
+ case Qt::Dense4Pattern:
+ case Qt::Dense5Pattern:
+ case Qt::Dense6Pattern:
+ case Qt::Dense7Pattern:
+ case Qt::HorPattern:
+ case Qt::VerPattern:
+ case Qt::CrossPattern:
+ case Qt::BDiagPattern:
+ case Qt::FDiagPattern:
+ case Qt::DiagCrossPattern:
+ type = Texture;
+ if (!tempImage)
+ tempImage = new QImage();
+ *tempImage = rasterBuffer->colorizeBitmap(qt_imageForBrush(brushStyle, true), brush.color());
+ initTexture(tempImage, alpha, QTextureData::Tiled);
+ break;
+ case Qt::TexturePattern:
+ type = Texture;
+ if (!tempImage)
+ tempImage = new QImage();
+
+ if (qHasPixmapTexture(brush) && brush.texture().isQBitmap())
+ *tempImage = rasterBuffer->colorizeBitmap(brush.textureImage(), brush.color());
+ else
+ *tempImage = brush.textureImage();
+ initTexture(tempImage, alpha, QTextureData::Tiled, tempImage->rect());
+ break;
+
+ case Qt::NoBrush:
+ default:
+ type = None;
+ break;
+ }
+ adjustSpanMethods();
+}
+
+void QSpanData::adjustSpanMethods()
+{
+ bitmapBlit = 0;
+ alphamapBlit = 0;
+ alphaRGBBlit = 0;
+
+ fillRect = 0;
+
+ switch(type) {
+ case None:
+ unclipped_blend = 0;
+ break;
+ case Solid:
+ unclipped_blend = rasterBuffer->drawHelper->blendColor;
+ bitmapBlit = rasterBuffer->drawHelper->bitmapBlit;
+ alphamapBlit = rasterBuffer->drawHelper->alphamapBlit;
+ alphaRGBBlit = rasterBuffer->drawHelper->alphaRGBBlit;
+ fillRect = rasterBuffer->drawHelper->fillRect;
+ break;
+ case LinearGradient:
+ case RadialGradient:
+ case ConicalGradient:
+ unclipped_blend = rasterBuffer->drawHelper->blendGradient;
+ break;
+ case Texture:
+#ifdef Q_WS_QWS
+#ifndef QT_NO_RASTERCALLBACKS
+ if (!rasterBuffer->buffer())
+ unclipped_blend = qBlendTextureCallback;
+ else
+#endif
+ unclipped_blend = qBlendTexture;
+#else
+ unclipped_blend = qBlendTexture;
+#endif
+ break;
+ }
+ // setup clipping
+ if (!unclipped_blend) {
+ blend = 0;
+ } else if (!clip) {
+ blend = unclipped_blend;
+ } else if (clip->hasRectClip) {
+ blend = clip->clipRect.isEmpty() ? 0 : qt_span_fill_clipRect;
+ } else if (clip->hasRegionClip) {
+ blend = clip->clipRegion.isEmpty() ? 0 : qt_span_fill_clipRegion;
+ } else {
+ blend = qt_span_fill_clipped;
+ }
+}
+
+void QSpanData::setupMatrix(const QTransform &matrix, int bilin)
+{
+ QTransform inv = matrix.inverted();
+ m11 = inv.m11();
+ m12 = inv.m12();
+ m13 = inv.m13();
+ m21 = inv.m21();
+ m22 = inv.m22();
+ m23 = inv.m23();
+ m33 = inv.m33();
+ dx = inv.dx();
+ dy = inv.dy();
+ txop = inv.type();
+ bilinear = bilin;
+
+ const bool affine = !m13 && !m23;
+ fast_matrix = affine
+ && m11 * m11 + m21 * m21 < 1e4
+ && m12 * m12 + m22 * m22 < 1e4
+ && qAbs(dx) < 1e4
+ && qAbs(dy) < 1e4;
+
+ adjustSpanMethods();
+}
+
+extern const QVector<QRgb> *qt_image_colortable(const QImage &image);
+
+void QSpanData::initTexture(const QImage *image, int alpha, QTextureData::Type _type, const QRect &sourceRect)
+{
+ const QImageData *d = const_cast<QImage *>(image)->data_ptr();
+ if (!d || d->height == 0) {
+ texture.imageData = 0;
+ texture.width = 0;
+ texture.height = 0;
+ texture.x1 = 0;
+ texture.y1 = 0;
+ texture.x2 = 0;
+ texture.y2 = 0;
+ texture.bytesPerLine = 0;
+ texture.format = QImage::Format_Invalid;
+ texture.colorTable = 0;
+ texture.hasAlpha = alpha != 256;
+ } else {
+ texture.imageData = d->data;
+ texture.width = d->width;
+ texture.height = d->height;
+
+ if (sourceRect.isNull()) {
+ texture.x1 = 0;
+ texture.y1 = 0;
+ texture.x2 = texture.width;
+ texture.y2 = texture.height;
+ } else {
+ texture.x1 = sourceRect.x();
+ texture.y1 = sourceRect.y();
+ texture.x2 = qMin(texture.x1 + sourceRect.width(), d->width);
+ texture.y2 = qMin(texture.y1 + sourceRect.height(), d->height);
+ }
+
+ texture.bytesPerLine = d->bytes_per_line;
+
+ texture.format = d->format;
+ texture.colorTable = (d->format <= QImage::Format_Indexed8 && !d->colortable.isEmpty()) ? &d->colortable : 0;
+ texture.hasAlpha = image->hasAlphaChannel() || alpha != 256;
+ }
+ texture.const_alpha = alpha;
+ texture.type = _type;
+
+ adjustSpanMethods();
+}
+
+#ifdef Q_WS_WIN
+
+
+#endif
+
+
+/*!
+ \internal
+
+ Draws a line using the floating point midpoint algorithm. The line
+ \a line is already in device coords at this point.
+*/
+
+static void drawLine_midpoint_i(int x1, int y1, int x2, int y2, ProcessSpans span_func, QSpanData *data,
+ LineDrawMode style, const QIntRect &devRect)
+{
+#ifdef QT_DEBUG_DRAW
+ qDebug() << " - drawLine_midpoint_i" << QLine(QPoint(x1, y1), QPoint(x2, y2));
+#endif
+
+ int x, y;
+ int dx, dy, d, incrE, incrNE;
+
+ dx = x2 - x1;
+ dy = y2 - y1;
+
+ const int NSPANS = 256;
+ QT_FT_Span spans[NSPANS];
+ int current = 0;
+ bool ordered = true;
+
+ if (dy == 0) {
+ // specialcase horizontal lines
+ if (y1 >= devRect.y1 && y1 < devRect.y2) {
+ int start = qMax(devRect.x1, qMin(x1, x2));
+ int stop = qMax(x1, x2) + 1;
+ int stop_clipped = qMin(devRect.x2, stop);
+ int len = stop_clipped - start;
+ if (style == LineDrawNormal && stop == stop_clipped)
+ len--;
+ if (len > 0) {
+ spans[0].x = ushort(start);
+ spans[0].len = ushort(len);
+ spans[0].y = y1;
+ spans[0].coverage = 255;
+ span_func(1, spans, data);
+ }
+ }
+ return;
+ } else if (dx == 0) {
+ // specialcase vertical lines
+ if (x1 >= devRect.x1 && x1 < devRect.x2) {
+ int start = qMax(devRect.y1, qMin(y1, y2));
+ int stop = qMax(y1, y2) + 1;
+ int stop_clipped = qMin(devRect.y2, stop);
+ int len = stop_clipped - start;
+ if (style == LineDrawNormal && stop == stop_clipped)
+ len--;
+ // hw: create spans directly instead to possibly avoid clipping
+ if (len > 0)
+ fillRect_normalized(QRect(x1, start, 1, len).normalized(), data, 0);
+ }
+ return;
+ }
+
+
+ if (qAbs(dx) >= qAbs(dy)) { /* if x is the major axis: */
+
+ if (x2 < x1) { /* if coordinates are out of order */
+ qt_swap_int(x1, x2);
+ dx = -dx;
+
+ qt_swap_int(y1, y2);
+ dy = -dy;
+ }
+
+ if (style == LineDrawNormal)
+ --x2;
+
+ // In the loops below we increment before call the span function so
+ // we need to stop one pixel before
+ x2 = qMin(x2, devRect.x2 - 1);
+
+ // completely clipped, so abort
+ if (x2 <= x1) {
+ return;
+ }
+
+ int x = x1;
+ int y = y1;
+
+ if (y2 <= y1)
+ ordered = false;
+
+ {
+ const int index = (ordered ? current : NSPANS - 1 - current);
+ spans[index].coverage = 255;
+ spans[index].x = x;
+ spans[index].y = y;
+
+ if (x >= devRect.x1 && y >= devRect.y1 && y < devRect.y2)
+ spans[index].len = 1;
+ else
+ spans[index].len = 0;
+ }
+
+ if (y2 > y1) { // 315 -> 360 and 135 -> 180 (unit circle degrees)
+ y2 = qMin(y2, devRect.y2 - 1);
+
+ incrE = dy * 2;
+ d = incrE - dx;
+ incrNE = (dy - dx) * 2;
+
+ if (y > y2)
+ goto flush_and_return;
+
+ while (x < x2) {
+ ++x;
+ if (d > 0) {
+ if (spans[current].len > 0)
+ ++current;
+ if (current == NSPANS) {
+ span_func(NSPANS, spans, data);
+ current = 0;
+ }
+
+ ++y;
+ d += incrNE;
+ if (y > y2)
+ goto flush_and_return;
+
+ spans[current].len = 0;
+ spans[current].coverage = 255;
+ spans[current].x = x;
+ spans[current].y = y;
+ } else {
+ d += incrE;
+ if (x == devRect.x1)
+ spans[current].x = devRect.x1;
+ }
+
+ if (x < devRect.x1 || y < devRect.y1)
+ continue;
+
+ Q_ASSERT(x<devRect.x2);
+ Q_ASSERT(y<devRect.y2);
+ Q_ASSERT(spans[current].y == y);
+ spans[current].len++;
+ }
+ if (spans[current].len > 0) {
+ ++current;
+ }
+ } else { // 0-45 and 180->225 (unit circle degrees)
+
+ y1 = qMin(y1, devRect.y2 - 1);
+
+ incrE = dy * 2;
+ d = incrE + dx;
+ incrNE = (dy + dx) * 2;
+
+ if (y < devRect.y1)
+ goto flush_and_return;
+
+ while (x < x2) {
+ ++x;
+ if (d < 0) {
+ if (spans[NSPANS - 1 - current].len > 0)
+ ++current;
+ if (current == NSPANS) {
+ span_func(NSPANS, spans, data);
+ current = 0;
+ }
+
+ --y;
+ d += incrNE;
+ if (y < devRect.y1)
+ goto flush_and_return;
+
+ const int index = NSPANS - 1 - current;
+ spans[index].len = 0;
+ spans[index].coverage = 255;
+ spans[index].x = x;
+ spans[index].y = y;
+ } else {
+ d += incrE;
+ if (x == devRect.x1)
+ spans[NSPANS - 1 - current].x = devRect.x1;
+ }
+
+ if (x < devRect.x1 || y > y1)
+ continue;
+
+ Q_ASSERT(x<devRect.x2 && y<devRect.y2);
+ Q_ASSERT(spans[NSPANS - 1 - current].y == y);
+ spans[NSPANS - 1 - current].len++;
+ }
+ if (spans[NSPANS - 1 - current].len > 0) {
+ ++current;
+ }
+ }
+
+ } else {
+
+ // if y is the major axis:
+
+ if (y2 < y1) { /* if coordinates are out of order */
+ qt_swap_int(y1, y2);
+ dy = -dy;
+
+ qt_swap_int(x1, x2);
+ dx = -dx;
+ }
+
+ if (style == LineDrawNormal)
+ --y2;
+
+ // In the loops below we increment before call the span function so
+ // we need to stop one pixel before
+ y2 = qMin(y2, devRect.y2 - 1);
+
+ // completely clipped, so abort
+ if (y2 <= y1) {
+ return;
+ }
+
+ x = x1;
+ y = y1;
+
+ if (x>=devRect.x1 && y>=devRect.y1 && x < devRect.x2) {
+ Q_ASSERT(x >= devRect.x1 && y >= devRect.y1 && x < devRect.x2 && y < devRect.y2);
+ if (current == NSPANS) {
+ span_func(NSPANS, spans, data);
+ current = 0;
+ }
+ spans[current].len = 1;
+ spans[current].coverage = 255;
+ spans[current].x = x;
+ spans[current].y = y;
+ ++current;
+ }
+
+ if (x2 > x1) { // 90 -> 135 and 270 -> 315 (unit circle degrees)
+ x2 = qMin(x2, devRect.x2 - 1);
+ incrE = dx * 2;
+ d = incrE - dy;
+ incrNE = (dx - dy) * 2;
+
+ if (x > x2)
+ goto flush_and_return;
+
+ while (y < y2) {
+ if (d > 0) {
+ ++x;
+ d += incrNE;
+ if (x > x2)
+ goto flush_and_return;
+ } else {
+ d += incrE;
+ }
+ ++y;
+ if (x < devRect.x1 || y < devRect.y1)
+ continue;
+ Q_ASSERT(x<devRect.x2 && y<devRect.y2);
+ if (current == NSPANS) {
+ span_func(NSPANS, spans, data);
+ current = 0;
+ }
+ spans[current].len = 1;
+ spans[current].coverage = 255;
+ spans[current].x = x;
+ spans[current].y = y;
+ ++current;
+ }
+ } else { // 45 -> 90 and 225 -> 270 (unit circle degrees)
+ x1 = qMin(x1, devRect.x2 - 1);
+ incrE = dx * 2;
+ d = incrE + dy;
+ incrNE = (dx + dy) * 2;
+
+ if (x < devRect.x1)
+ goto flush_and_return;
+
+ while (y < y2) {
+ if (d < 0) {
+ --x;
+ d += incrNE;
+ if (x < devRect.x1)
+ goto flush_and_return;
+ } else {
+ d += incrE;
+ }
+ ++y;
+ if (y < devRect.y1 || x > x1)
+ continue;
+ Q_ASSERT(x>=devRect.x1 && x<devRect.x2 && y>=devRect.y1 && y<devRect.y2);
+ if (current == NSPANS) {
+ span_func(NSPANS, spans, data);
+ current = 0;
+ }
+ spans[current].len = 1;
+ spans[current].coverage = 255;
+ spans[current].x = x;
+ spans[current].y = y;
+ ++current;
+ }
+ }
+ }
+flush_and_return:
+ if (current > 0)
+ span_func(current, ordered ? spans : spans + (NSPANS - current), data);
+}
+
+static void offset_pattern(int offset, bool *inDash, int *dashIndex, int *currentOffset, const QVarLengthArray<qreal> &pattern)
+{
+ while (offset--) {
+ if (--*currentOffset == 0) {
+ *inDash = !*inDash;
+ *dashIndex = ((*dashIndex + 1) % pattern.size());
+ *currentOffset = int(pattern[*dashIndex]);
+ }
+ }
+}
+
+static void drawLine_midpoint_dashed_i(int x1, int y1, int x2, int y2,
+ QPen *pen,
+ ProcessSpans span_func, QSpanData *data,
+ LineDrawMode style, const QIntRect &devRect,
+ int *patternOffset)
+{
+#ifdef QT_DEBUG_DRAW
+ qDebug() << " - drawLine_midpoint_dashed_i" << x1 << y1 << x2 << y2 << *patternOffset;
+#endif
+
+ int x, y;
+ int dx, dy, d, incrE, incrNE;
+
+ dx = x2 - x1;
+ dy = y2 - y1;
+
+ Q_ASSERT(*patternOffset >= 0);
+
+ const QVector<qreal> penPattern = pen->dashPattern();
+ QVarLengthArray<qreal> pattern(penPattern.size());
+
+ int patternLength = 0;
+ for (int i = 0; i < penPattern.size(); ++i)
+ patternLength += qMax<qreal>(1.0, (penPattern.at(i)));
+
+ // pattern must be reversed if coordinates are out of order
+ int reverseLength = -1;
+ if (dy == 0 && x1 > x2)
+ reverseLength = x1 - x2;
+ else if (dx == 0 && y1 > y2)
+ reverseLength = y1 - y2;
+ else if (qAbs(dx) >= qAbs(dy) && x2 < x1) // x major axis
+ reverseLength = qAbs(dx);
+ else if (qAbs(dy) >= qAbs(dx) && y2 < y1) // y major axis
+ reverseLength = qAbs(dy);
+
+ const bool reversed = (reverseLength > -1);
+ if (reversed) { // reverse pattern
+ for (int i = 0; i < penPattern.size(); ++i)
+ pattern[penPattern.size() - 1 - i] = qMax<qreal>(1.0, penPattern.at(i));
+
+ *patternOffset = (patternLength - 1 - *patternOffset);
+ *patternOffset += patternLength - (reverseLength % patternLength);
+ *patternOffset = *patternOffset % patternLength;
+ } else {
+ for (int i = 0; i < penPattern.size(); ++i)
+ pattern[i] = qMax<qreal>(1.0, penPattern.at(i));
+ }
+
+ int dashIndex = 0;
+ bool inDash = !reversed;
+ int currPattern = int(pattern[dashIndex]);
+
+ // adjust pattern for offset
+ offset_pattern(*patternOffset, &inDash, &dashIndex, &currPattern, pattern);
+
+ const int NSPANS = 256;
+ QT_FT_Span spans[NSPANS];
+ int current = 0;
+ bool ordered = true;
+
+ if (dy == 0) {
+ // specialcase horizontal lines
+ if (y1 >= devRect.y1 && y1 < devRect.y2) {
+ int start_unclipped = qMin(x1, x2);
+ int start = qMax(devRect.x1, start_unclipped);
+ int stop = qMax(x1, x2) + 1;
+ int stop_clipped = qMin(devRect.x2, stop);
+ int len = stop_clipped - start;
+ if (style == LineDrawNormal && stop == stop_clipped)
+ len--;
+
+ // adjust pattern for starting offset
+ offset_pattern(start - start_unclipped, &inDash, &dashIndex, &currPattern, pattern);
+
+ if (len > 0) {
+ int x = start;
+ while (x < stop_clipped) {
+ if (current == NSPANS) {
+ span_func(NSPANS, spans, data);
+ current = 0;
+ }
+ const int dash = qMin(currPattern, stop_clipped - x);
+ if (inDash) {
+ spans[current].x = ushort(x);
+ spans[current].len = ushort(dash);
+ spans[current].y = y1;
+ spans[current].coverage = 255;
+ ++current;
+ }
+ if (dash < currPattern) {
+ currPattern -= dash;
+ } else {
+ dashIndex = (dashIndex + 1) % pattern.size();
+ currPattern = int(pattern[dashIndex]);
+ inDash = !inDash;
+ }
+ x += dash;
+ }
+ }
+ }
+ goto flush_and_return;
+ } else if (dx == 0) {
+ if (x1 >= devRect.x1 && x1 < devRect.x2) {
+ int start_unclipped = qMin(y1, y2);
+ int start = qMax(devRect.y1, start_unclipped);
+ int stop = qMax(y1, y2) + 1;
+ int stop_clipped = qMin(devRect.y2, stop);
+ if (style == LineDrawNormal && stop == stop_clipped)
+ --stop;
+ else
+ stop = stop_clipped;
+
+ // adjust pattern for starting offset
+ offset_pattern(start - start_unclipped, &inDash, &dashIndex, &currPattern, pattern);
+
+ // loop over dashes
+ int y = start;
+ while (y < stop) {
+ const int dash = qMin(currPattern, stop - y);
+ if (inDash) {
+ for (int i = 0; i < dash; ++i) {
+ if (current == NSPANS) {
+ span_func(NSPANS, spans, data);
+ current = 0;
+ }
+ spans[current].x = x1;
+ spans[current].len = 1;
+ spans[current].coverage = 255;
+ spans[current].y = ushort(y + i);
+ ++current;
+ }
+ }
+ if (dash < currPattern) {
+ currPattern -= dash;
+ } else {
+ dashIndex = (dashIndex + 1) % pattern.size();
+ currPattern = int(pattern[dashIndex]);
+ inDash = !inDash;
+ }
+ y += dash;
+ }
+ }
+ goto flush_and_return;
+ }
+
+ if (qAbs(dx) >= qAbs(dy)) { /* if x is the major axis: */
+
+ if (x2 < x1) { /* if coordinates are out of order */
+ qt_swap_int(x1, x2);
+ dx = -dx;
+
+ qt_swap_int(y1, y2);
+ dy = -dy;
+ }
+
+ if (style == LineDrawNormal)
+ --x2;
+
+ // In the loops below we increment before call the span function so
+ // we need to stop one pixel before
+ x2 = qMin(x2, devRect.x2 - 1);
+
+ // completely clipped, so abort
+ if (x2 <= x1)
+ goto flush_and_return;
+
+ int x = x1;
+ int y = y1;
+
+ if (x >= devRect.x1 && y >= devRect.y1 && y < devRect.y2) {
+ Q_ASSERT(x < devRect.x2);
+ if (inDash) {
+ if (current == NSPANS) {
+ span_func(NSPANS, spans, data);
+ current = 0;
+ }
+ spans[current].len = 1;
+ spans[current].coverage = 255;
+ spans[current].x = x;
+ spans[current].y = y;
+ ++current;
+ }
+ if (--currPattern <= 0) {
+ inDash = !inDash;
+ dashIndex = (dashIndex + 1) % pattern.size();
+ currPattern = int(pattern[dashIndex]);
+ }
+ }
+
+ if (y2 > y1) { // 315 -> 360 and 135 -> 180 (unit circle degrees)
+ y2 = qMin(y2, devRect.y2 - 1);
+
+ incrE = dy * 2;
+ d = incrE - dx;
+ incrNE = (dy - dx) * 2;
+
+ if (y > y2)
+ goto flush_and_return;
+
+ while (x < x2) {
+ if (d > 0) {
+ ++y;
+ d += incrNE;
+ if (y > y2)
+ goto flush_and_return;
+ } else {
+ d += incrE;
+ }
+ ++x;
+
+ const bool skip = x < devRect.x1 || y < devRect.y1;
+ Q_ASSERT(skip || (x < devRect.x2 && y < devRect.y2));
+ if (inDash && !skip) {
+ if (current == NSPANS) {
+ span_func(NSPANS, spans, data);
+ current = 0;
+ }
+ spans[current].len = 1;
+ spans[current].coverage = 255;
+ spans[current].x = x;
+ spans[current].y = y;
+ ++current;
+ }
+ if (--currPattern <= 0) {
+ inDash = !inDash;
+ dashIndex = (dashIndex + 1) % pattern.size();
+ currPattern = int(pattern[dashIndex]);
+ }
+ }
+ } else { // 0-45 and 180->225 (unit circle degrees)
+ y1 = qMin(y1, devRect.y2 - 1);
+
+ incrE = dy * 2;
+ d = incrE + dx;
+ incrNE = (dy + dx) * 2;
+
+ if (y < devRect.y1)
+ goto flush_and_return;
+
+ while (x < x2) {
+ if (d < 0) {
+ if (current > 0) {
+ span_func(current, spans, data);
+ current = 0;
+ }
+
+ --y;
+ d += incrNE;
+ if (y < devRect.y1)
+ goto flush_and_return;
+ } else {
+ d += incrE;
+ }
+ ++x;
+
+ const bool skip = x < devRect.x1 || y > y1;
+ Q_ASSERT(skip || (x < devRect.x2 && y < devRect.y2));
+ if (inDash && !skip) {
+ if (current == NSPANS) {
+ span_func(NSPANS, spans, data);
+ current = 0;
+ }
+ spans[current].len = 1;
+ spans[current].coverage = 255;
+ spans[current].x = x;
+ spans[current].y = y;
+ ++current;
+ }
+ if (--currPattern <= 0) {
+ inDash = !inDash;
+ dashIndex = (dashIndex + 1) % pattern.size();
+ currPattern = int(pattern[dashIndex]);
+ }
+ }
+ }
+ } else {
+
+ // if y is the major axis:
+
+ if (y2 < y1) { /* if coordinates are out of order */
+ qt_swap_int(y1, y2);
+ dy = -dy;
+
+ qt_swap_int(x1, x2);
+ dx = -dx;
+ }
+
+ if (style == LineDrawNormal)
+ --y2;
+
+ // In the loops below we increment before call the span function so
+ // we need to stop one pixel before
+ y2 = qMin(y2, devRect.y2 - 1);
+
+ // completely clipped, so abort
+ if (y2 <= y1)
+ goto flush_and_return;
+
+ x = x1;
+ y = y1;
+
+ if (x>=devRect.x1 && y>=devRect.y1 && x < devRect.x2) {
+ Q_ASSERT(x < devRect.x2);
+ if (inDash) {
+ if (current == NSPANS) {
+ span_func(NSPANS, spans, data);
+ current = 0;
+ }
+ spans[current].len = 1;
+ spans[current].coverage = 255;
+ spans[current].x = x;
+ spans[current].y = y;
+ ++current;
+ }
+ if (--currPattern <= 0) {
+ inDash = !inDash;
+ dashIndex = (dashIndex + 1) % pattern.size();
+ currPattern = int(pattern[dashIndex]);
+ }
+ }
+
+ if (x2 > x1) { // 90 -> 135 and 270 -> 315 (unit circle degrees)
+ x2 = qMin(x2, devRect.x2 - 1);
+ incrE = dx * 2;
+ d = incrE - dy;
+ incrNE = (dx - dy) * 2;
+
+ if (x > x2)
+ goto flush_and_return;
+
+ while (y < y2) {
+ if (d > 0) {
+ ++x;
+ d += incrNE;
+ if (x > x2)
+ goto flush_and_return;
+ } else {
+ d += incrE;
+ }
+ ++y;
+ const bool skip = x < devRect.x1 || y < devRect.y1;
+ Q_ASSERT(skip || (x < devRect.x2 && y < devRect.y2));
+ if (inDash && !skip) {
+ if (current == NSPANS) {
+ span_func(NSPANS, spans, data);
+ current = 0;
+ }
+ spans[current].len = 1;
+ spans[current].coverage = 255;
+ spans[current].x = x;
+ spans[current].y = y;
+ ++current;
+ }
+ if (--currPattern <= 0) {
+ inDash = !inDash;
+ dashIndex = (dashIndex + 1) % pattern.size();
+ currPattern = int(pattern[dashIndex]);
+ }
+ }
+ } else { // 45 -> 90 and 225 -> 270 (unit circle degrees)
+ x1 = qMin(x1, devRect.x2 - 1);
+ incrE = dx * 2;
+ d = incrE + dy;
+ incrNE = (dx + dy) * 2;
+
+ if (x < devRect.x1)
+ goto flush_and_return;
+
+ while (y < y2) {
+ if (d < 0) {
+ --x;
+ d += incrNE;
+ if (x < devRect.x1)
+ goto flush_and_return;
+ } else {
+ d += incrE;
+ }
+ ++y;
+ const bool skip = y < devRect.y1 || x > x1;
+ Q_ASSERT(skip || (x >= devRect.x1 && x < devRect.x2 && y < devRect.y2));
+ if (inDash && !skip) {
+ if (current == NSPANS) {
+ span_func(NSPANS, spans, data);
+ current = 0;
+ }
+ spans[current].len = 1;
+ spans[current].coverage = 255;
+ spans[current].x = x;
+ spans[current].y = y;
+ ++current;
+ }
+ if (--currPattern <= 0) {
+ inDash = !inDash;
+ dashIndex = (dashIndex + 1) % pattern.size();
+ currPattern = int(pattern[dashIndex]);
+ }
+ }
+ }
+ }
+flush_and_return:
+ if (current > 0)
+ span_func(current, ordered ? spans : spans + (NSPANS - current), data);
+
+ // adjust offset
+ if (reversed) {
+ *patternOffset = (patternLength - 1 - *patternOffset);
+ } else {
+ *patternOffset = 0;
+ for (int i = 0; i <= dashIndex; ++i)
+ *patternOffset += int(pattern[i]);
+ *patternOffset += patternLength - currPattern - 1;
+ *patternOffset = (*patternOffset % patternLength);
+ }
+}
+
+/*!
+ \internal
+ \a x and \a y is relative to the midpoint of \a rect.
+*/
+static inline void drawEllipsePoints(int x, int y, int length,
+ const QRect &rect,
+ const QRect &clip,
+ ProcessSpans pen_func, ProcessSpans brush_func,
+ QSpanData *pen_data, QSpanData *brush_data)
+{
+ if (length == 0)
+ return;
+
+ QT_FT_Span outline[4];
+ const int midx = rect.x() + (rect.width() + 1) / 2;
+ const int midy = rect.y() + (rect.height() + 1) / 2;
+
+ x = x + midx;
+ y = midy - y;
+
+ // topleft
+ outline[0].x = midx + (midx - x) - (length - 1) - (rect.width() & 0x1);
+ outline[0].len = qMin(length, x - outline[0].x);
+ outline[0].y = y;
+ outline[0].coverage = 255;
+
+ // topright
+ outline[1].x = x;
+ outline[1].len = length;
+ outline[1].y = y;
+ outline[1].coverage = 255;
+
+ // bottomleft
+ outline[2].x = outline[0].x;
+ outline[2].len = outline[0].len;
+ outline[2].y = midy + (midy - y) - (rect.height() & 0x1);
+ outline[2].coverage = 255;
+
+ // bottomright
+ outline[3].x = x;
+ outline[3].len = length;
+ outline[3].y = outline[2].y;
+ outline[3].coverage = 255;
+
+ if (brush_func && outline[0].x + outline[0].len < outline[1].x) {
+ QT_FT_Span fill[2];
+
+ // top fill
+ fill[0].x = outline[0].x + outline[0].len - 1;
+ fill[0].len = qMax(0, outline[1].x - fill[0].x);
+ fill[0].y = outline[1].y;
+ fill[0].coverage = 255;
+
+ // bottom fill
+ fill[1].x = outline[2].x + outline[2].len - 1;
+ fill[1].len = qMax(0, outline[3].x - fill[1].x);
+ fill[1].y = outline[3].y;
+ fill[1].coverage = 255;
+
+ int n = (fill[0].y >= fill[1].y ? 1 : 2);
+ n = qt_intersect_spans(fill, n, clip);
+ if (n > 0)
+ brush_func(n, fill, brush_data);
+ }
+ if (pen_func) {
+ int n = (outline[1].y >= outline[2].y ? 2 : 4);
+ n = qt_intersect_spans(outline, n, clip);
+ if (n > 0)
+ pen_func(n, outline, pen_data);
+ }
+}
+
+/*!
+ \internal
+ Draws an ellipse using the integer point midpoint algorithm.
+*/
+static void drawEllipse_midpoint_i(const QRect &rect, const QRect &clip,
+ ProcessSpans pen_func, ProcessSpans brush_func,
+ QSpanData *pen_data, QSpanData *brush_data)
+{
+#ifdef FLOATING_POINT_BUGGY_OR_NO_FPU // no fpu, so use fixed point
+ const QFixed a = QFixed(rect.width()) >> 1;
+ const QFixed b = QFixed(rect.height()) >> 1;
+ QFixed d = b*b - (a*a*b) + ((a*a) >> 2);
+#else
+ const qreal a = qreal(rect.width()) / 2;
+ const qreal b = qreal(rect.height()) / 2;
+ qreal d = b*b - (a*a*b) + 0.25*a*a;
+#endif
+
+ int x = 0;
+ int y = (rect.height() + 1) / 2;
+ int startx = x;
+
+ // region 1
+ while (a*a*(2*y - 1) > 2*b*b*(x + 1)) {
+ if (d < 0) { // select E
+ d += b*b*(2*x + 3);
+ ++x;
+ } else { // select SE
+ d += b*b*(2*x + 3) + a*a*(-2*y + 2);
+ drawEllipsePoints(startx, y, x - startx + 1, rect, clip,
+ pen_func, brush_func, pen_data, brush_data);
+ startx = ++x;
+ --y;
+ }
+ }
+ drawEllipsePoints(startx, y, x - startx + 1, rect, clip,
+ pen_func, brush_func, pen_data, brush_data);
+
+ // region 2
+#ifdef FLOATING_POINT_BUGGY_OR_NO_FPU
+ d = b*b*(x + (QFixed(1) >> 1))*(x + (QFixed(1) >> 1))
+ + a*a*((y - 1)*(y - 1) - b*b);
+#else
+ d = b*b*(x + 0.5)*(x + 0.5) + a*a*((y - 1)*(y - 1) - b*b);
+#endif
+ const int miny = rect.height() & 0x1;
+ while (y > miny) {
+ if (d < 0) { // select SE
+ d += b*b*(2*x + 2) + a*a*(-2*y + 3);
+ ++x;
+ } else { // select S
+ d += a*a*(-2*y + 3);
+ }
+ --y;
+ drawEllipsePoints(x, y, 1, rect, clip,
+ pen_func, brush_func, pen_data, brush_data);
+ }
+}
+
+/*!
+ \fn void QRasterPaintEngine::drawPoints(const QPoint *points, int pointCount)
+ \overload
+
+ Draws the first \a pointCount points in the buffer \a points
+
+ The default implementation converts the first \a pointCount QPoints in \a points
+ to QPointFs and calls the floating point version of drawPoints.
+*/
+
+/*!
+ \fn void QRasterPaintEngine::drawEllipse(const QRect &rect)
+ \overload
+
+ Reimplement this function to draw the largest ellipse that can be
+ contained within rectangle \a rect.
+*/
+
+#ifdef QT_DEBUG_DRAW
+void dumpClip(int width, int height, QClipData *clip)
+{
+ QImage clipImg(width, height, QImage::Format_ARGB32_Premultiplied);
+ clipImg.fill(0xffff0000);
+
+ int x0 = width;
+ int x1 = 0;
+ int y0 = height;
+ int y1 = 0;
+
+ for (int i = 0; i < clip->count; ++i) {
+ QSpan *span = clip->spans + i;
+ for (int j = 0; j < span->len; ++j)
+ clipImg.setPixel(span->x + j, span->y, 0xffffff00);
+ x0 = qMin(x0, int(span->x));
+ x1 = qMax(x1, int(span->x + span->len - 1));
+
+ y0 = qMin(y0, int(span->y));
+ y1 = qMax(y1, int(span->y));
+ }
+
+ static int counter = 0;
+
+ Q_ASSERT(y0 >= 0);
+ Q_ASSERT(x0 >= 0);
+ Q_ASSERT(y1 >= 0);
+ Q_ASSERT(x1 >= 0);
+
+ fprintf(stderr,"clip %d: %d %d - %d %d\n", counter, x0, y0, x1, y1);
+ clipImg.save(QString(QLatin1String("clip-%0.png")).arg(counter++));
+}
+#endif
+
+
+QT_END_NAMESPACE
diff --git a/src/gui/painting/qpaintengine_raster_p.h b/src/gui/painting/qpaintengine_raster_p.h
new file mode 100644
index 0000000000..0f8060ae7f
--- /dev/null
+++ b/src/gui/painting/qpaintengine_raster_p.h
@@ -0,0 +1,550 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QPAINTENGINE_RASTER_P_H
+#define QPAINTENGINE_RASTER_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 "private/qpaintengineex_p.h"
+#include "QtGui/qpainterpath.h"
+#include "private/qdatabuffer_p.h"
+#include "private/qdrawhelper_p.h"
+#include "private/qpaintengine_p.h"
+#include "private/qrasterizer_p.h"
+#include "private/qstroker_p.h"
+#include "private/qpainter_p.h"
+#include "private/qtextureglyphcache_p.h"
+
+#include <stdlib.h>
+
+QT_BEGIN_NAMESPACE
+
+class QOutlineMapper;
+class QRasterPaintEnginePrivate;
+class QRasterBuffer;
+class QClipData;
+class QCustomRasterPaintDevice;
+
+class QRasterPaintEngineState : public QPainterState
+{
+public:
+ QRasterPaintEngineState(QRasterPaintEngineState &other);
+ QRasterPaintEngineState();
+ ~QRasterPaintEngineState();
+
+
+ QPen lastPen;
+ QSpanData penData;
+ QStrokerOps *stroker;
+ uint strokeFlags;
+
+ QBrush lastBrush;
+ QSpanData brushData;
+ uint fillFlags;
+
+ uint pixmapFlags;
+ int intOpacity;
+
+ qreal txscale;
+
+ QClipData *clip;
+// QRect clipRect;
+// QRegion clipRegion;
+
+// QPainter::RenderHints hints;
+// QPainter::CompositionMode compositionMode;
+
+ uint dirty;
+
+ struct Flags {
+ uint has_clip_ownership : 1; // should delete the clip member..
+ uint fast_pen : 1; // cosmetic 1-width pens, using midpoint drawlines
+ uint non_complex_pen : 1; // can use rasterizer, rather than stroker
+ uint antialiased : 1;
+ uint bilinear : 1;
+ uint fast_text : 1;
+ uint int_xform : 1;
+ uint tx_noshear : 1;
+ uint fast_images : 1;
+ };
+
+ union {
+ Flags flags;
+ uint flag_bits;
+ };
+};
+
+
+
+
+/*******************************************************************************
+ * QRasterPaintEngine
+ */
+class
+#ifdef Q_WS_QWS
+Q_GUI_EXPORT
+#endif
+QRasterPaintEngine : public QPaintEngineEx
+{
+ Q_DECLARE_PRIVATE(QRasterPaintEngine)
+public:
+
+ QRasterPaintEngine(QPaintDevice *device);
+ ~QRasterPaintEngine();
+ bool begin(QPaintDevice *device);
+ bool end();
+
+ void penChanged();
+ void brushChanged();
+ void brushOriginChanged();
+ void opacityChanged();
+ void compositionModeChanged();
+ void renderHintsChanged();
+ void transformChanged();
+ void clipEnabledChanged();
+
+ void setState(QPainterState *s);
+ QPainterState *createState(QPainterState *orig) const;
+ inline QRasterPaintEngineState *state() {
+ return static_cast<QRasterPaintEngineState *>(QPaintEngineEx::state());
+ }
+ inline const QRasterPaintEngineState *state() const {
+ return static_cast<const QRasterPaintEngineState *>(QPaintEngineEx::state());
+ }
+
+ void updateBrush(const QBrush &brush);
+ void updatePen(const QPen &pen);
+
+ void updateMatrix(const QTransform &matrix);
+
+ void drawPath(const QPainterPath &path);
+ void drawPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode);
+ void drawPolygon(const QPoint *points, int pointCount, PolygonDrawMode mode);
+ void fillPath(const QPainterPath &path, QSpanData *fillData);
+ void fillPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode);
+
+ void drawEllipse(const QRectF &rect);
+
+ void fillRect(const QRectF &rect, const QBrush &brush);
+ void fillRect(const QRectF &rect, const QColor &color);
+
+ void drawRects(const QRect *rects, int rectCount);
+ void drawRects(const QRectF *rects, int rectCount);
+
+ void drawPixmap(const QPointF &p, const QPixmap &pm);
+ void drawPixmap(const QRectF &r, const QPixmap &pm, const QRectF &sr);
+ void drawImage(const QPointF &p, const QImage &img);
+ void drawImage(const QRectF &r, const QImage &pm, const QRectF &sr,
+ Qt::ImageConversionFlags falgs = Qt::AutoColor);
+ void drawTiledPixmap(const QRectF &r, const QPixmap &pm, const QPointF &sr);
+ void drawTextItem(const QPointF &p, const QTextItem &textItem);
+
+ void drawLines(const QLine *line, int lineCount);
+ void drawLines(const QLineF *line, int lineCount);
+
+ void drawPoints(const QPointF *points, int pointCount);
+ void drawPoints(const QPoint *points, int pointCount);
+
+ void stroke(const QVectorPath &path, const QPen &pen);
+ void fill(const QVectorPath &path, const QBrush &brush);
+
+ void strokePolygonCosmetic(const QPoint *pts, int pointCount, PolygonDrawMode mode);
+ void strokePolygonCosmetic(const QPointF *pt, int pointCount, PolygonDrawMode mode);
+
+ void clip(const QVectorPath &path, Qt::ClipOperation op);
+ void clip(const QRect &rect, Qt::ClipOperation op);
+ void clip(const QRegion &region, Qt::ClipOperation op);
+ void clip(const QPainterPath &path, Qt::ClipOperation op);
+
+ enum ClipType {
+ RectClip,
+ ComplexClip
+ };
+ ClipType clipType() const;
+ QRect clipBoundingRect() const;
+
+#ifdef Q_NO_USING_KEYWORD
+ inline void drawEllipse(const QRect &rect) { QPaintEngineEx::drawEllipse(rect); }
+#else
+ using QPaintEngineEx::drawPolygon;
+ using QPaintEngineEx::drawEllipse;
+#endif
+
+ void releaseBuffer();
+
+ QSize size() const;
+
+#ifndef QT_NO_DEBUG
+ void saveBuffer(const QString &s) const;
+#endif
+
+#ifdef Q_WS_MAC
+ void setCGContext(CGContextRef ref);
+ CGContextRef getCGContext() const;
+#endif
+
+#ifdef Q_WS_WIN
+ void setDC(HDC hdc);
+ HDC getDC() const;
+ void releaseDC(HDC hdc) const;
+#endif
+
+ void alphaPenBlt(const void* src, int bpl, int depth, int rx,int ry,int w,int h);
+
+ Type type() const { return Raster; }
+
+ QPoint coordinateOffset() const;
+
+#if defined(Q_WS_QWS) && !defined(QT_NO_RASTERCALLBACKS)
+ virtual void drawColorSpans(const QSpan *spans, int count, uint color);
+ virtual void drawBufferSpan(const uint *buffer, int bufsize,
+ int x, int y, int length, uint const_alpha);
+#endif
+
+protected:
+ QRasterPaintEngine(QRasterPaintEnginePrivate &d, QPaintDevice *);
+private:
+ friend struct QSpanData;
+ void init();
+
+ void fillRect(const QRectF &rect, QSpanData *data);
+ void drawBitmap(const QPointF &pos, const QPixmap &image, QSpanData *fill);
+
+ void drawCachedGlyphs(const QPointF &p, const QTextItemInt &ti);
+
+ inline void ensureBrush(const QBrush &brush) {
+ if (!qbrush_fast_equals(state()->lastBrush, brush) || state()->fillFlags)
+ updateBrush(brush);
+ }
+ inline void ensureBrush() { ensureBrush(state()->brush); }
+
+ inline void ensurePen(const QPen &pen) {
+ if (!qpen_fast_equals(state()->lastPen, pen) || state()->strokeFlags)
+ updatePen(pen);
+ }
+ inline void ensurePen() { ensurePen(state()->pen); }
+
+ void updateOutlineMapper();
+ inline void ensureOutlineMapper();
+
+ void updateState();
+ inline void ensureState() {
+ if (state()->dirty)
+ updateState();
+ }
+};
+
+
+/*******************************************************************************
+ * QRasterPaintEnginePrivate
+ */
+class
+#ifdef Q_WS_QWS
+Q_GUI_EXPORT
+#endif
+QRasterPaintEnginePrivate : public QPaintEngineExPrivate
+{
+ Q_DECLARE_PUBLIC(QRasterPaintEngine)
+public:
+
+ void rasterizeLine_dashed(QLineF line, qreal width,
+ int *dashIndex, qreal *dashOffset, bool *inDash);
+ void rasterize(QT_FT_Outline *outline, ProcessSpans callback, QSpanData *spanData, QRasterBuffer *rasterBuffer);
+ void rasterize(QT_FT_Outline *outline, ProcessSpans callback, void *userData, QRasterBuffer *rasterBuffer);
+ void updateMatrixData(QSpanData *spanData, const QBrush &brush, const QTransform &brushMatrix);
+
+ void systemStateChanged();
+
+ void drawImage(const QPointF &pt, const QImage &img, SrcOverBlendFunc func,
+ const QRect &clip, int alpha, const QRect &sr = QRect());
+
+ QTransform brushMatrix() const {
+ Q_Q(const QRasterPaintEngine);
+ const QRasterPaintEngineState *s = q->state();
+ QTransform m(s->matrix);
+ m.translate(s->brushOrigin.x(), s->brushOrigin.y());
+ return m;
+ }
+
+ bool isUnclipped_normalized(const QRect &rect) const;
+ bool isUnclipped(const QRect &rect, int penWidth) const;
+ bool isUnclipped(const QRectF &rect, int penWidth) const;
+ ProcessSpans getPenFunc(const QRect &rect, const QSpanData *data) const;
+ ProcessSpans getPenFunc(const QRectF &rect, const QSpanData *data) const;
+ ProcessSpans getBrushFunc(const QRect &rect, const QSpanData *data) const;
+ ProcessSpans getBrushFunc(const QRectF &rect, const QSpanData *data) const;
+
+#ifdef Q_WS_QWS
+ void prepare(QCustomRasterPaintDevice *);
+#endif
+
+ inline const QClipData *clip() const;
+
+ void strokeProjective(const QPainterPath &path);
+ void initializeRasterizer(QSpanData *data);
+
+ void recalculateFastImages();
+
+ QPaintDevice *device;
+ QOutlineMapper *outlineMapper;
+ QRasterBuffer *rasterBuffer;
+
+#if defined (Q_WS_WIN)
+ HDC hdc;
+#elif defined(Q_WS_MAC)
+ CGContextRef cgContext;
+#endif
+
+ QRect deviceRect;
+
+ QStroker basicStroker;
+ QDashStroker *dashStroker;
+
+ QT_FT_Raster *grayRaster;
+ unsigned long rasterPoolSize;
+ unsigned char *rasterPoolBase;
+
+ QDataBuffer<QLineF> cachedLines;
+ QSpanData image_filler;
+ QSpanData image_filler_xform;
+ QSpanData solid_color_filler;
+
+
+ QFontEngineGlyphCache::Type glyphCacheType;
+
+ QClipData *baseClip;
+
+ int deviceDepth;
+
+ uint mono_surface : 1;
+ uint outlinemapper_xform_dirty : 1;
+
+#ifdef Q_WS_WIN
+ uint isPlain45DegreeRotation : 1;
+#endif
+
+ QRasterizer *rasterizer;
+};
+
+
+class QClipData {
+public:
+ QClipData(int height);
+ ~QClipData();
+
+ int clipSpanHeight;
+ struct ClipLine {
+ int count;
+ QSpan *spans;
+ } *m_clipLines;
+
+ void initialize();
+
+ inline ClipLine *clipLines() {
+ if (!m_clipLines)
+ initialize();
+ return m_clipLines;
+ }
+
+ inline QSpan *spans() {
+ if (!m_spans)
+ initialize();
+ return m_spans;
+ }
+
+ int allocated;
+ int count;
+ QSpan *m_spans;
+ int xmin, xmax, ymin, ymax;
+
+ QRect clipRect;
+ QRegion clipRegion;
+
+ uint enabled : 1;
+ uint hasRectClip : 1;
+ uint hasRegionClip : 1;
+
+ void appendSpan(int x, int length, int y, int coverage);
+ void appendSpans(const QSpan *s, int num);
+
+ // ### Should optimize and actually kill the QSpans if the rect is
+ // ### a subset of The current region. Thus the "fast" clipspan
+ // ### callback can be used
+ void setClipRect(const QRect &rect);
+ void setClipRegion(const QRegion &region);
+ void fixup();
+};
+
+inline void QClipData::appendSpan(int x, int length, int y, int coverage)
+{
+ Q_ASSERT(m_spans); // initialize() has to be called prior to adding spans..
+
+ if (count == allocated) {
+ allocated *= 2;
+ m_spans = (QSpan *)realloc(m_spans, allocated*sizeof(QSpan));
+ }
+ m_spans[count].x = x;
+ m_spans[count].len = length;
+ m_spans[count].y = y;
+ m_spans[count].coverage = coverage;
+ ++count;
+}
+
+inline void QClipData::appendSpans(const QSpan *s, int num)
+{
+ Q_ASSERT(m_spans);
+
+ if (count + num > allocated) {
+ do {
+ allocated *= 2;
+ } while (count + num > allocated);
+ m_spans = (QSpan *)realloc(m_spans, allocated*sizeof(QSpan));
+ }
+ memcpy(m_spans+count, s, num*sizeof(QSpan));
+ count += num;
+}
+
+#ifdef Q_WS_QWS
+class Q_GUI_EXPORT QCustomRasterPaintDevice : public QPaintDevice
+{
+public:
+ QCustomRasterPaintDevice(QWidget *w) : widget(w) {}
+
+ int devType() const { return QInternal::CustomRaster; }
+
+ virtual int metric(PaintDeviceMetric m) const;
+
+ virtual void* memory() const { return 0; }
+
+ virtual QImage::Format format() const {
+ return QImage::Format_ARGB32_Premultiplied;
+ }
+
+ virtual int bytesPerLine() const;
+
+ virtual QSize size() const {
+ return static_cast<QRasterPaintEngine*>(paintEngine())->size();
+ }
+
+private:
+ QWidget *widget;
+};
+#endif // Q_WS_QWS
+
+/*******************************************************************************
+ * QRasterBuffer
+ */
+class QRasterBuffer
+{
+public:
+ QRasterBuffer() : m_width(0), m_height(0), m_buffer(0) { init(); }
+
+ ~QRasterBuffer();
+
+ void init();
+
+ QImage::Format prepare(QImage *image);
+ QImage::Format prepare(QPixmap *pix);
+#ifdef Q_WS_QWS
+ void prepare(QCustomRasterPaintDevice *device);
+#endif
+ void prepare(int w, int h);
+ void prepareBuffer(int w, int h);
+
+ void resetBuffer(int val=0);
+
+ uchar *scanLine(int y) { Q_ASSERT(y>=0); Q_ASSERT(y<m_height); return m_buffer + y * bytes_per_line; }
+
+#ifndef QT_NO_DEBUG
+ QImage bufferImage() const;
+#endif
+
+ void flushToARGBImage(QImage *image) const;
+
+ int width() const { return m_width; }
+ int height() const { return m_height; }
+ int bytesPerLine() const { return bytes_per_line; }
+ int bytesPerPixel() const { return bytes_per_pixel; }
+
+ uchar *buffer() const { return m_buffer; }
+
+ bool monoDestinationWithClut;
+ QRgb destColor0;
+ QRgb destColor1;
+
+ QPainter::CompositionMode compositionMode;
+ QImage::Format format;
+ DrawHelper *drawHelper;
+ QImage colorizeBitmap(const QImage &image, const QColor &color);
+
+private:
+ int m_width;
+ int m_height;
+ int bytes_per_line;
+ int bytes_per_pixel;
+ uchar *m_buffer;
+};
+
+inline void QRasterPaintEngine::ensureOutlineMapper() {
+ if (d_func()->outlinemapper_xform_dirty)
+ updateOutlineMapper();
+}
+
+inline const QClipData *QRasterPaintEnginePrivate::clip() const {
+ Q_Q(const QRasterPaintEngine);
+ if (q->state() && q->state()->clip && q->state()->clip->enabled)
+ return q->state()->clip;
+ return baseClip;
+}
+
+
+QT_END_NAMESPACE
+#endif // QPAINTENGINE_RASTER_P_H
diff --git a/src/gui/painting/qpaintengine_x11.cpp b/src/gui/painting/qpaintengine_x11.cpp
new file mode 100644
index 0000000000..e9f1bb362a
--- /dev/null
+++ b/src/gui/painting/qpaintengine_x11.cpp
@@ -0,0 +1,2451 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qplatformdefs.h"
+
+#include "private/qpixmap_x11_p.h"
+
+#include "qapplication.h"
+#include "qdebug.h"
+#include "qfont.h"
+#include "qwidget.h"
+#include "qbitmap.h"
+#include "qpixmapcache.h"
+#include "qtextcodec.h"
+#include "qcoreevent.h"
+#include "qiodevice.h"
+#include <qmath.h>
+
+#include "qpainter_p.h"
+#include <qtextlayout.h>
+#include <qvarlengtharray.h>
+#include <private/qfont_p.h>
+#include <private/qtextengine_p.h>
+#include <private/qpaintengine_x11_p.h>
+#include <private/qfontengine_x11_p.h>
+#include <private/qwidget_p.h>
+
+#include "qpen.h"
+#include "qcolor.h"
+#include "qcolormap.h"
+
+#include <private/qpaintengine_p.h>
+#include "qpaintengine_x11_p.h"
+
+#include <private/qt_x11_p.h>
+#include <private/qnumeric_p.h>
+#include <limits.h>
+
+#ifndef QT_NO_XRENDER
+#include <private/qtessellator_p.h>
+#endif
+
+QT_BEGIN_NAMESPACE
+
+extern Drawable qt_x11Handle(const QPaintDevice *pd);
+extern const QX11Info *qt_x11Info(const QPaintDevice *pd);
+extern QPixmap qt_pixmapForBrush(int brushStyle, bool invert); //in qbrush.cpp
+extern QPixmap qt_toX11Pixmap(const QPixmap &pixmap);
+
+// use the same rounding as in qrasterizer.cpp (6 bit fixed point)
+static const qreal aliasedCoordinateDelta = 0.5 - 0.015625;
+
+#undef X11 // defined in qt_x11_p.h
+/*!
+ Returns the X11 specific pen GC for the painter \a p. Note that
+ QPainter::begin() must be called before this function returns a
+ valid GC.
+*/
+Q_GUI_EXPORT GC qt_x11_get_pen_gc(QPainter *p)
+{
+ if (p && p->paintEngine()
+ && p->paintEngine()->isActive()
+ && p->paintEngine()->type() == QPaintEngine::X11) {
+ return static_cast<QX11PaintEngine *>(p->paintEngine())->d_func()->gc;
+ }
+ return 0;
+}
+
+/*!
+ Returns the X11 specific brush GC for the painter \a p. Note that
+ QPainter::begin() must be called before this function returns a
+ valid GC.
+*/
+Q_GUI_EXPORT GC qt_x11_get_brush_gc(QPainter *p)
+{
+ if (p && p->paintEngine()
+ && p->paintEngine()->isActive()
+ && p->paintEngine()->type() == QPaintEngine::X11) {
+ return static_cast<QX11PaintEngine *>(p->paintEngine())->d_func()->gc_brush;
+ }
+ return 0;
+}
+#define X11 qt_x11Data
+
+#ifndef QT_NO_XRENDER
+static const int compositionModeToRenderOp[QPainter::CompositionMode_Xor + 1] = {
+ PictOpOver, //CompositionMode_SourceOver,
+ PictOpOverReverse, //CompositionMode_DestinationOver,
+ PictOpClear, //CompositionMode_Clear,
+ PictOpSrc, //CompositionMode_Source,
+ PictOpDst, //CompositionMode_Destination,
+ PictOpIn, //CompositionMode_SourceIn,
+ PictOpInReverse, //CompositionMode_DestinationIn,
+ PictOpOut, //CompositionMode_SourceOut,
+ PictOpOutReverse, //CompositionMode_DestinationOut,
+ PictOpAtop, //CompositionMode_SourceAtop,
+ PictOpAtopReverse, //CompositionMode_DestinationAtop,
+ PictOpXor //CompositionMode_Xor
+};
+
+static inline int qpainterOpToXrender(QPainter::CompositionMode mode)
+{
+ Q_ASSERT(mode <= QPainter::CompositionMode_Xor);
+ return compositionModeToRenderOp[mode];
+}
+#endif
+
+// hack, so we don't have to make QRegion::clipRectangles() public or include
+// X11 headers in qregion.h
+Q_AUTOTEST_EXPORT void *qt_getClipRects(const QRegion &r, int &num)
+{
+ return r.clipRectangles(num);
+}
+
+static inline void x11SetClipRegion(Display *dpy, GC gc, GC gc2,
+#ifndef QT_NO_XRENDER
+ Picture picture,
+#else
+ Qt::HANDLE picture,
+#endif
+ const QRegion &r)
+{
+ int num;
+ XRectangle *rects = (XRectangle *)qt_getClipRects(r, num);
+
+ if (gc)
+ XSetClipRectangles( dpy, gc, 0, 0, rects, num, YXBanded );
+ if (gc2)
+ XSetClipRectangles( dpy, gc2, 0, 0, rects, num, YXBanded );
+
+#ifndef QT_NO_XRENDER
+ if (picture)
+ XRenderSetPictureClipRectangles(dpy, picture, 0, 0, rects, num);
+#else
+ Q_UNUSED(picture);
+#endif // QT_NO_XRENDER
+}
+
+
+static inline void x11ClearClipRegion(Display *dpy, GC gc, GC gc2,
+#ifndef QT_NO_XRENDER
+ Picture picture
+#else
+ Qt::HANDLE picture
+#endif
+ )
+{
+ if (gc)
+ XSetClipMask(dpy, gc, XNone);
+ if (gc2)
+ XSetClipMask(dpy, gc2, XNone);
+
+#ifndef QT_NO_XRENDER
+ if (picture) {
+ XRenderPictureAttributes attrs;
+ attrs.clip_mask = XNone;
+ XRenderChangePicture (dpy, picture, CPClipMask, &attrs);
+ }
+#else
+ Q_UNUSED(picture);
+#endif // QT_NO_XRENDER
+}
+
+
+#define DITHER_SIZE 16
+static const uchar base_dither_matrix[DITHER_SIZE][DITHER_SIZE] = {
+ { 0,192, 48,240, 12,204, 60,252, 3,195, 51,243, 15,207, 63,255 },
+ { 128, 64,176,112,140, 76,188,124,131, 67,179,115,143, 79,191,127 },
+ { 32,224, 16,208, 44,236, 28,220, 35,227, 19,211, 47,239, 31,223 },
+ { 160, 96,144, 80,172,108,156, 92,163, 99,147, 83,175,111,159, 95 },
+ { 8,200, 56,248, 4,196, 52,244, 11,203, 59,251, 7,199, 55,247 },
+ { 136, 72,184,120,132, 68,180,116,139, 75,187,123,135, 71,183,119 },
+ { 40,232, 24,216, 36,228, 20,212, 43,235, 27,219, 39,231, 23,215 },
+ { 168,104,152, 88,164,100,148, 84,171,107,155, 91,167,103,151, 87 },
+ { 2,194, 50,242, 14,206, 62,254, 1,193, 49,241, 13,205, 61,253 },
+ { 130, 66,178,114,142, 78,190,126,129, 65,177,113,141, 77,189,125 },
+ { 34,226, 18,210, 46,238, 30,222, 33,225, 17,209, 45,237, 29,221 },
+ { 162, 98,146, 82,174,110,158, 94,161, 97,145, 81,173,109,157, 93 },
+ { 10,202, 58,250, 6,198, 54,246, 9,201, 57,249, 5,197, 53,245 },
+ { 138, 74,186,122,134, 70,182,118,137, 73,185,121,133, 69,181,117 },
+ { 42,234, 26,218, 38,230, 22,214, 41,233, 25,217, 37,229, 21,213 },
+ { 170,106,154, 90,166,102,150, 86,169,105,153, 89,165,101,149, 85 }
+};
+
+static QPixmap qt_patternForAlpha(uchar alpha, int screen)
+{
+ QPixmap pm;
+ QString key = QLatin1String("$qt-alpha-brush$") + QString::number(alpha) + QString::number(screen);
+ if (!QPixmapCache::find(key, pm)) {
+ // #### why not use a mono image here????
+ QImage pattern(DITHER_SIZE, DITHER_SIZE, QImage::Format_ARGB32);
+ pattern.fill(0xffffffff);
+ for (int y = 0; y < DITHER_SIZE; ++y) {
+ for (int x = 0; x < DITHER_SIZE; ++x) {
+ if (base_dither_matrix[x][y] <= alpha)
+ pattern.setPixel(x, y, 0x00000000);
+ }
+ }
+ pm = QBitmap::fromImage(pattern);
+ pm.x11SetScreen(screen);
+ QPixmapCache::insert(key, pm);
+ }
+ return pm;
+}
+
+#if !defined(QT_NO_XRENDER)
+
+class QXRenderTessellator : public QTessellator
+{
+public:
+ QXRenderTessellator() : traps(0), allocated(0), size(0) {}
+ ~QXRenderTessellator() { free(traps); }
+ XTrapezoid *traps;
+ int allocated;
+ int size;
+ void addTrap(const Trapezoid &trap);
+ QRect tessellate(const QPointF *points, int nPoints, bool winding) {
+ size = 0;
+ setWinding(winding);
+ return QTessellator::tessellate(points, nPoints).toRect();
+ }
+ void done() {
+ if (allocated > 64) {
+ free(traps);
+ traps = 0;
+ allocated = 0;
+ }
+ }
+};
+
+void QXRenderTessellator::addTrap(const Trapezoid &trap)
+{
+ if (size == allocated) {
+ allocated = qMax(2*allocated, 64);
+ traps = (XTrapezoid *)realloc(traps, allocated * sizeof(XTrapezoid));
+ }
+ traps[size].top = Q27Dot5ToXFixed(trap.top);
+ traps[size].bottom = Q27Dot5ToXFixed(trap.bottom);
+ traps[size].left.p1.x = Q27Dot5ToXFixed(trap.topLeft->x);
+ traps[size].left.p1.y = Q27Dot5ToXFixed(trap.topLeft->y);
+ traps[size].left.p2.x = Q27Dot5ToXFixed(trap.bottomLeft->x);
+ traps[size].left.p2.y = Q27Dot5ToXFixed(trap.bottomLeft->y);
+ traps[size].right.p1.x = Q27Dot5ToXFixed(trap.topRight->x);
+ traps[size].right.p1.y = Q27Dot5ToXFixed(trap.topRight->y);
+ traps[size].right.p2.x = Q27Dot5ToXFixed(trap.bottomRight->x);
+ traps[size].right.p2.y = Q27Dot5ToXFixed(trap.bottomRight->y);
+ ++size;
+}
+
+#endif // !defined(QT_NO_XRENDER)
+
+
+#ifndef QT_NO_XRENDER
+static Picture getPatternFill(int screen, const QBrush &b)
+{
+ if (!X11->use_xrender)
+ return XNone;
+
+ XRenderColor color = X11->preMultiply(b.color());
+ XRenderColor bg_color;
+
+ bg_color = X11->preMultiply(QColor(0, 0, 0, 0));
+
+ for (int i = 0; i < X11->pattern_fill_count; ++i) {
+ if (X11->pattern_fills[i].screen == screen
+ && X11->pattern_fills[i].opaque == false
+ && X11->pattern_fills[i].style == b.style()
+ && X11->pattern_fills[i].color.alpha == color.alpha
+ && X11->pattern_fills[i].color.red == color.red
+ && X11->pattern_fills[i].color.green == color.green
+ && X11->pattern_fills[i].color.blue == color.blue
+ && X11->pattern_fills[i].bg_color.alpha == bg_color.alpha
+ && X11->pattern_fills[i].bg_color.red == bg_color.red
+ && X11->pattern_fills[i].bg_color.green == bg_color.green
+ && X11->pattern_fills[i].bg_color.blue == bg_color.blue)
+ return X11->pattern_fills[i].picture;
+ }
+ // none found, replace one
+ int i = rand() % 16;
+
+ if (X11->pattern_fills[i].screen != screen && X11->pattern_fills[i].picture) {
+ XRenderFreePicture (X11->display, X11->pattern_fills[i].picture);
+ X11->pattern_fills[i].picture = 0;
+ }
+
+ if (!X11->pattern_fills[i].picture) {
+ Pixmap pixmap = XCreatePixmap (X11->display, RootWindow (X11->display, screen), 8, 8, 32);
+ XRenderPictureAttributes attrs;
+ attrs.repeat = True;
+ X11->pattern_fills[i].picture = XRenderCreatePicture (X11->display, pixmap,
+ XRenderFindStandardFormat(X11->display, PictStandardARGB32),
+ CPRepeat, &attrs);
+ XFreePixmap (X11->display, pixmap);
+ }
+
+ X11->pattern_fills[i].screen = screen;
+ X11->pattern_fills[i].color = color;
+ X11->pattern_fills[i].bg_color = bg_color;
+ X11->pattern_fills[i].opaque = false;
+ X11->pattern_fills[i].style = b.style();
+
+ XRenderFillRectangle(X11->display, PictOpSrc, X11->pattern_fills[i].picture, &bg_color, 0, 0, 8, 8);
+
+ QPixmap pattern(qt_pixmapForBrush(b.style(), true));
+ XRenderPictureAttributes attrs;
+ attrs.repeat = true;
+ XRenderChangePicture(X11->display, pattern.x11PictureHandle(), CPRepeat, &attrs);
+
+ Picture fill_fg = X11->getSolidFill(screen, b.color());
+ XRenderComposite(X11->display, PictOpOver, fill_fg, pattern.x11PictureHandle(),
+ X11->pattern_fills[i].picture,
+ 0, 0, 0, 0, 0, 0, 8, 8);
+
+ return X11->pattern_fills[i].picture;
+}
+
+static void qt_render_bitmap(Display *dpy, int scrn, Picture src, Picture dst,
+ int sx, int sy, int x, int y, int sw, int sh,
+ const QPen &pen)
+{
+ Picture fill_fg = X11->getSolidFill(scrn, pen.color());
+ XRenderComposite(dpy, PictOpOver,
+ fill_fg, src, dst, sx, sy, sx, sy, x, y, sw, sh);
+}
+#endif
+
+void QX11PaintEnginePrivate::init()
+{
+ dpy = 0;
+ scrn = 0;
+ hd = 0;
+ picture = 0;
+ xinfo = 0;
+#ifndef QT_NO_XRENDER
+ current_brush = 0;
+ composition_mode = PictOpOver;
+ tessellator = new QXRenderTessellator;
+#endif
+}
+
+void QX11PaintEnginePrivate::setupAdaptedOrigin(const QPoint &p)
+{
+ if (adapted_pen_origin)
+ XSetTSOrigin(dpy, gc, p.x(), p.y());
+ if (adapted_brush_origin)
+ XSetTSOrigin(dpy, gc_brush, p.x(), p.y());
+}
+
+void QX11PaintEnginePrivate::resetAdaptedOrigin()
+{
+ if (adapted_pen_origin)
+ XSetTSOrigin(dpy, gc, 0, 0);
+ if (adapted_brush_origin)
+ XSetTSOrigin(dpy, gc_brush, 0, 0);
+}
+
+void QX11PaintEnginePrivate::clipPolygon_dev(const QPolygonF &poly, QPolygonF *clipped_poly)
+{
+ int clipped_count = 0;
+ qt_float_point *clipped_points = 0;
+ polygonClipper.clipPolygon((qt_float_point *) poly.data(), poly.size(),
+ &clipped_points, &clipped_count);
+ clipped_poly->resize(clipped_count);
+ for (int i=0; i<clipped_count; ++i)
+ (*clipped_poly)[i] = *((QPointF *)(&clipped_points[i]));
+}
+
+void QX11PaintEnginePrivate::systemStateChanged()
+{
+ Q_Q(QX11PaintEngine);
+ QPainter *painter = q->state ? static_cast<QPainterState *>(q->state)->painter : 0;
+ if (painter && painter->hasClipping()) {
+ if (q->testDirty(QPaintEngine::DirtyTransform))
+ q->updateMatrix(q->state->transform());
+ QPolygonF clip_poly_dev(matrix.map(painter->clipPath().toFillPolygon()));
+ QPolygonF clipped_poly_dev;
+ clipPolygon_dev(clip_poly_dev, &clipped_poly_dev);
+ q->updateClipRegion_dev(QRegion(clipped_poly_dev.toPolygon()), Qt::ReplaceClip);
+ } else {
+ q->updateClipRegion_dev(QRegion(), Qt::NoClip);
+ }
+}
+
+static QPaintEngine::PaintEngineFeatures qt_decide_features()
+{
+ QPaintEngine::PaintEngineFeatures features =
+ QPaintEngine::PrimitiveTransform
+ | QPaintEngine::PatternBrush
+ | QPaintEngine::AlphaBlend
+ | QPaintEngine::PainterPaths
+ | QPaintEngine::RasterOpModes;
+
+ if (X11->use_xrender) {
+ features |= QPaintEngine::Antialiasing;
+ features |= QPaintEngine::PorterDuff;
+ features |= QPaintEngine::MaskedBrush;
+#if 0
+ if (X11->xrender_version > 10) {
+ features |= QPaintEngine::LinearGradientFill;
+ // ###
+ }
+#endif
+ }
+
+ return features;
+}
+
+/*
+ * QX11PaintEngine members
+ */
+
+QX11PaintEngine::QX11PaintEngine()
+ : QPaintEngine(*(new QX11PaintEnginePrivate), qt_decide_features())
+{
+ d_func()->init();
+}
+
+QX11PaintEngine::QX11PaintEngine(QX11PaintEnginePrivate &dptr)
+ : QPaintEngine(dptr, qt_decide_features())
+{
+ d_func()->init();
+}
+
+QX11PaintEngine::~QX11PaintEngine()
+{
+#ifndef QT_NO_XRENDER
+ Q_D(QX11PaintEngine);
+ delete d->tessellator;
+#endif
+}
+
+bool QX11PaintEngine::begin(QPaintDevice *pdev)
+{
+ Q_D(QX11PaintEngine);
+ d->xinfo = qt_x11Info(pdev);
+ QWidget *w = d->pdev->devType() == QInternal::Widget ? static_cast<QWidget *>(d->pdev) : 0;
+ const bool isAlienWidget = w && !w->internalWinId() && w->testAttribute(Qt::WA_WState_Created);
+#ifndef QT_NO_XRENDER
+ if (w) {
+ if (isAlienWidget)
+ d->picture = (::Picture)w->nativeParentWidget()->x11PictureHandle();
+ else
+ d->picture = (::Picture)w->x11PictureHandle();
+ } else if (pdev->devType() == QInternal::Pixmap) {
+ const QPixmap *pm = static_cast<const QPixmap *>(pdev);
+ QX11PixmapData *data = static_cast<QX11PixmapData*>(pm->data);
+ if (X11->use_xrender && data->depth() != 32 && data->x11_mask)
+ data->convertToARGB32();
+ d->picture = (::Picture)static_cast<const QPixmap *>(pdev)->x11PictureHandle();
+ }
+#else
+ d->picture = 0;
+#endif
+ d->hd = !isAlienWidget ? qt_x11Handle(pdev) : qt_x11Handle(w->nativeParentWidget());
+
+ Q_ASSERT(d->xinfo != 0);
+ d->dpy = d->xinfo->display(); // get display variable
+ d->scrn = d->xinfo->screen(); // get screen variable
+
+ d->crgn = QRegion();
+ d->gc = XCreateGC(d->dpy, d->hd, 0, 0);
+ d->gc_brush = XCreateGC(d->dpy, d->hd, 0, 0);
+ d->has_alpha_brush = false;
+ d->has_alpha_pen = false;
+ d->has_clipping = false;
+ d->has_complex_xform = false;
+ d->has_scaling_xform = false;
+ d->has_non_scaling_xform = true;
+ d->xform_scale = 1;
+ d->has_custom_pen = false;
+ d->matrix = QTransform();
+ d->pdev_depth = d->pdev->depth();
+ d->render_hints = 0;
+ d->txop = QTransform::TxNone;
+ d->use_path_fallback = false;
+#if !defined(QT_NO_XRENDER)
+ d->composition_mode = PictOpOver;
+#endif
+ d->xlibMaxLinePoints = 32762; // a safe number used to avoid, call to XMaxRequestSize(d->dpy) - 3;
+ d->opacity = 1;
+
+ // Set up the polygon clipper. Note: This will only work in
+ // polyline mode as long as we have a buffer zone, since a
+ // polyline may be clipped into several non-connected polylines.
+ const int BUFFERZONE = 1000;
+ QRect devClipRect(-BUFFERZONE, -BUFFERZONE,
+ pdev->width() + 2*BUFFERZONE, pdev->height() + 2*BUFFERZONE);
+ d->polygonClipper.setBoundingRect(devClipRect);
+
+ if (isAlienWidget) {
+ // Set system clip for alien widgets painting outside the paint event.
+ // This is not a problem with native windows since the windowing system
+ // will handle the clip.
+ QWidgetPrivate *wd = w->d_func();
+ QRegion widgetClip(wd->clipRect());
+ wd->clipToEffectiveMask(widgetClip);
+ wd->subtractOpaqueSiblings(widgetClip);
+ widgetClip.translate(w->mapTo(w->nativeParentWidget(), QPoint()));
+ setSystemClip(widgetClip);
+ }
+
+ QPixmap::x11SetDefaultScreen(d->xinfo->screen());
+
+ if (w && w->testAttribute(Qt::WA_PaintUnclipped)) { // paint direct on device
+ updatePen(QPen(Qt::black));
+ updateBrush(QBrush(Qt::white), QPoint());
+ XSetSubwindowMode(d->dpy, d->gc, IncludeInferiors);
+ XSetSubwindowMode(d->dpy, d->gc_brush, IncludeInferiors);
+#ifndef QT_NO_XRENDER
+ XRenderPictureAttributes attrs;
+ attrs.subwindow_mode = IncludeInferiors;
+ XRenderChangePicture(d->dpy, d->picture, CPSubwindowMode, &attrs);
+#endif
+ }
+
+ setDirty(QPaintEngine::DirtyClipRegion);
+ setDirty(QPaintEngine::DirtyPen);
+ setDirty(QPaintEngine::DirtyBrush);
+ setDirty(QPaintEngine::DirtyBackground);
+
+ return true;
+}
+
+bool QX11PaintEngine::end()
+{
+ Q_D(QX11PaintEngine);
+
+#if !defined(QT_NO_XRENDER)
+ if (d->picture) {
+ // reset clipping/subwindow mode on our render picture
+ XRenderPictureAttributes attrs;
+ attrs.subwindow_mode = ClipByChildren;
+ attrs.clip_mask = XNone;
+ XRenderChangePicture(d->dpy, d->picture, CPClipMask|CPSubwindowMode, &attrs);
+ }
+#endif
+
+ if (d->gc_brush && d->pdev->painters < 2) {
+ XFreeGC(d->dpy, d->gc_brush);
+ d->gc_brush = 0;
+ }
+
+ if (d->gc && d->pdev->painters < 2) {
+ XFreeGC(d->dpy, d->gc);
+ d->gc = 0;
+ }
+
+ // Restore system clip for alien widgets painting outside the paint event.
+ if (d->pdev->devType() == QInternal::Widget && !static_cast<QWidget *>(d->pdev)->internalWinId())
+ setSystemClip(QRegion());
+
+ return true;
+}
+
+static bool clipLine(QLineF *line, const QRect &rect)
+{
+ qreal x1 = line->x1();
+ qreal x2 = line->x2();
+ qreal y1 = line->y1();
+ qreal y2 = line->y2();
+
+ qreal left = rect.x();
+ qreal right = rect.x() + rect.width() - 1;
+ qreal top = rect.y();
+ qreal bottom = rect.y() + rect.height() - 1;
+
+ enum { Left, Right, Top, Bottom };
+ // clip the lines, after cohen-sutherland, see e.g. http://www.nondot.org/~sabre/graphpro/line6.html
+ int p1 = ((x1 < left) << Left)
+ | ((x1 > right) << Right)
+ | ((y1 < top) << Top)
+ | ((y1 > bottom) << Bottom);
+ int p2 = ((x2 < left) << Left)
+ | ((x2 > right) << Right)
+ | ((y2 < top) << Top)
+ | ((y2 > bottom) << Bottom);
+
+ if (p1 & p2)
+ // completely outside
+ return false;
+
+ if (p1 | p2) {
+ qreal dx = x2 - x1;
+ qreal dy = y2 - y1;
+
+ // clip x coordinates
+ if (x1 < left) {
+ y1 += dy/dx * (left - x1);
+ x1 = left;
+ } else if (x1 > right) {
+ y1 -= dy/dx * (x1 - right);
+ x1 = right;
+ }
+ if (x2 < left) {
+ y2 += dy/dx * (left - x2);
+ x2 = left;
+ } else if (x2 > right) {
+ y2 -= dy/dx * (x2 - right);
+ x2 = right;
+ }
+ p1 = ((y1 < top) << Top)
+ | ((y1 > bottom) << Bottom);
+ p2 = ((y2 < top) << Top)
+ | ((y2 > bottom) << Bottom);
+ if (p1 & p2)
+ return false;
+ // clip y coordinates
+ if (y1 < top) {
+ x1 += dx/dy * (top - y1);
+ y1 = top;
+ } else if (y1 > bottom) {
+ x1 -= dx/dy * (y1 - bottom);
+ y1 = bottom;
+ }
+ if (y2 < top) {
+ x2 += dx/dy * (top - y2);
+ y2 = top;
+ } else if (y2 > bottom) {
+ x2 -= dx/dy * (y2 - bottom);
+ y2 = bottom;
+ }
+ *line = QLineF(QPointF(x1, y1), QPointF(x2, y2));
+ }
+ return true;
+}
+
+void QX11PaintEngine::drawLines(const QLine *lines, int lineCount)
+{
+ Q_ASSERT(lines);
+ Q_ASSERT(lineCount);
+ Q_D(QX11PaintEngine);
+ if (d->has_alpha_brush
+ || d->has_alpha_pen
+ || d->has_custom_pen
+ || (d->cpen.widthF() > 0 && d->has_complex_xform
+ && !d->has_non_scaling_xform)
+ || (d->render_hints & QPainter::Antialiasing)) {
+ for (int i = 0; i < lineCount; ++i) {
+ QPainterPath path(lines[i].p1());
+ path.lineTo(lines[i].p2());
+ drawPath(path);
+ }
+ return;
+ }
+
+ if (d->has_pen) {
+ for (int i = 0; i < lineCount; ++i) {
+ QLineF linef;
+ if (d->txop == QTransform::TxNone) {
+ linef = lines[i];
+ } else {
+ linef = d->matrix.map(QLineF(lines[i]));
+ }
+ if (clipLine(&linef, d->polygonClipper.boundingRect())) {
+ int x1 = qRound(linef.x1() + aliasedCoordinateDelta);
+ int y1 = qRound(linef.y1() + aliasedCoordinateDelta);
+ int x2 = qRound(linef.x2() + aliasedCoordinateDelta);
+ int y2 = qRound(linef.y2() + aliasedCoordinateDelta);
+
+ XDrawLine(d->dpy, d->hd, d->gc, x1, y1, x2, y2);
+ }
+ }
+ }
+}
+
+void QX11PaintEngine::drawLines(const QLineF *lines, int lineCount)
+{
+ Q_ASSERT(lines);
+ Q_ASSERT(lineCount);
+ Q_D(QX11PaintEngine);
+ if (d->has_alpha_brush
+ || d->has_alpha_pen
+ || d->has_custom_pen
+ || (d->cpen.widthF() > 0 && d->has_complex_xform
+ && !d->has_non_scaling_xform)
+ || (d->render_hints & QPainter::Antialiasing)) {
+ for (int i = 0; i < lineCount; ++i) {
+ QPainterPath path(lines[i].p1());
+ path.lineTo(lines[i].p2());
+ drawPath(path);
+ }
+ return;
+ }
+
+ if (d->has_pen) {
+ for (int i = 0; i < lineCount; ++i) {
+ QLineF linef = d->matrix.map(lines[i]);
+ if (clipLine(&linef, d->polygonClipper.boundingRect())) {
+ int x1 = qRound(linef.x1() + aliasedCoordinateDelta);
+ int y1 = qRound(linef.y1() + aliasedCoordinateDelta);
+ int x2 = qRound(linef.x2() + aliasedCoordinateDelta);
+ int y2 = qRound(linef.y2() + aliasedCoordinateDelta);
+
+ XDrawLine(d->dpy, d->hd, d->gc, x1, y1, x2, y2);
+ }
+ }
+ }
+}
+
+static inline QLine clipStraightLine(const QRect &clip, const QLine &l)
+{
+ if (l.p1().x() == l.p2().x()) {
+ int x = qBound(clip.left(), l.p1().x(), clip.right());
+ int y1 = qBound(clip.top(), l.p1().y(), clip.bottom());
+ int y2 = qBound(clip.top(), l.p2().y(), clip.bottom());
+
+ return QLine(x, y1, x, y2);
+ } else {
+ Q_ASSERT(l.p1().y() == l.p2().y());
+
+ int x1 = qBound(clip.left(), l.p1().x(), clip.right());
+ int x2 = qBound(clip.left(), l.p2().x(), clip.right());
+ int y = qBound(clip.top(), l.p1().y(), clip.bottom());
+
+ return QLine(x1, y, x2, y);
+ }
+}
+
+void QX11PaintEngine::drawRects(const QRectF *rects, int rectCount)
+{
+ Q_D(QX11PaintEngine);
+ Q_ASSERT(rects);
+ Q_ASSERT(rectCount);
+
+ if (rectCount != 1
+ || d->has_pen
+ || d->has_alpha_brush
+ || d->has_complex_xform
+ || d->has_custom_pen
+ || d->cbrush.style() != Qt::SolidPattern)
+ {
+ QPaintEngine::drawRects(rects, rectCount);
+ return;
+ }
+
+ QPoint alignedOffset;
+ if (d->txop == QTransform::TxTranslate) {
+ QPointF offset(d->matrix.dx(), d->matrix.dy());
+ alignedOffset = offset.toPoint();
+ if (offset != QPointF(alignedOffset)) {
+ QPaintEngine::drawRects(rects, rectCount);
+ return;
+ }
+ }
+
+ const QRectF& r = rects[0];
+ QRect alignedRect = r.toAlignedRect();
+ if (r != QRectF(alignedRect)) {
+ QPaintEngine::drawRects(rects, rectCount);
+ return;
+ }
+ alignedRect.translate(alignedOffset);
+
+ QRect clip(d->polygonClipper.boundingRect());
+ alignedRect = alignedRect.intersected(clip);
+ if (alignedRect.isEmpty())
+ return;
+
+ // simple-case:
+ // the rectangle is pixel-aligned
+ // the fill brush is just a solid non-alpha color
+ // the painter transform is only integer translation
+ // ignore: antialiasing and just XFillRectangles directly
+ XRectangle xrect;
+ xrect.x = short(alignedRect.x());
+ xrect.y = short(alignedRect.y());
+ xrect.width = ushort(alignedRect.width());
+ xrect.height = ushort(alignedRect.height());
+ XFillRectangles(d->dpy, d->hd, d->gc_brush, &xrect, 1);
+}
+
+void QX11PaintEngine::drawRects(const QRect *rects, int rectCount)
+{
+ Q_D(QX11PaintEngine);
+ Q_ASSERT(rects);
+ Q_ASSERT(rectCount);
+
+ if (d->has_alpha_pen
+ || d->has_complex_xform
+ || d->has_custom_pen
+ || (d->render_hints & QPainter::Antialiasing))
+ {
+ for (int i = 0; i < rectCount; ++i) {
+ QPainterPath path;
+ path.addRect(rects[i]);
+ drawPath(path);
+ }
+ return;
+ }
+
+ QRect clip(d->polygonClipper.boundingRect());
+ QPoint offset(qRound(d->matrix.dx()), qRound(d->matrix.dy()));
+#if !defined(QT_NO_XRENDER)
+ ::Picture pict = d->picture;
+
+ if (X11->use_xrender && pict && d->has_brush && d->pdev_depth != 1
+ && (d->has_texture || d->has_alpha_brush))
+ {
+ XRenderColor xc;
+ if (!d->has_texture && !d->has_pattern)
+ xc = X11->preMultiply(d->cbrush.color());
+
+ for (int i = 0; i < rectCount; ++i) {
+ QRect r(rects[i]);
+ if (d->txop == QTransform::TxTranslate)
+ r.translate(offset);
+
+ if (r.width() == 0 || r.height() == 0) {
+ if (d->has_pen) {
+ const QLine l = clipStraightLine(clip, QLine(r.left(), r.top(), r.left() + r.width(), r.top() + r.height()));
+ XDrawLine(d->dpy, d->hd, d->gc, l.p1().x(), l.p1().y(), l.p2().x(), l.p2().y());
+ }
+ continue;
+ }
+
+ r = r.intersected(clip);
+ if (r.isEmpty())
+ continue;
+ if (d->has_texture || d->has_pattern) {
+ XRenderComposite(d->dpy, d->composition_mode, d->current_brush, 0, pict,
+ qRound(r.x() - d->bg_origin.x()), qRound(r.y() - d->bg_origin.y()),
+ 0, 0, r.x(), r.y(), r.width(), r.height());
+ } else {
+ XRenderFillRectangle(d->dpy, d->composition_mode, pict, &xc, r.x(), r.y(), r.width(), r.height());
+ }
+ if (d->has_pen)
+ XDrawRectangle(d->dpy, d->hd, d->gc, r.x(), r.y(), r.width(), r.height());
+ }
+ } else
+#endif // !QT_NO_XRENDER
+ {
+ if (d->has_brush && d->has_pen) {
+ for (int i = 0; i < rectCount; ++i) {
+ QRect r(rects[i]);
+ if (d->txop == QTransform::TxTranslate)
+ r.translate(offset);
+
+ if (r.width() == 0 || r.height() == 0) {
+ const QLine l = clipStraightLine(clip, QLine(r.left(), r.top(), r.left() + r.width(), r.top() + r.height()));
+ XDrawLine(d->dpy, d->hd, d->gc, l.p1().x(), l.p1().y(), l.p2().x(), l.p2().y());
+ continue;
+ }
+
+ r = r.intersected(clip);
+ if (r.isEmpty())
+ continue;
+ d->setupAdaptedOrigin(r.topLeft());
+ XFillRectangle(d->dpy, d->hd, d->gc_brush, r.x(), r.y(), r.width(), r.height());
+ XDrawRectangle(d->dpy, d->hd, d->gc, r.x(), r.y(), r.width(), r.height());
+ }
+ d->resetAdaptedOrigin();
+ } else {
+ QVarLengthArray<XRectangle> xrects(rectCount);
+ int numClipped = rectCount;
+ for (int i = 0; i < rectCount; ++i) {
+ QRect r(rects[i]);
+ if (d->txop == QTransform::TxTranslate)
+ r.translate(offset);
+
+ if (r.width() == 0 || r.height() == 0) {
+ --numClipped;
+ if (d->has_pen) {
+ const QLine l = clipStraightLine(clip, QLine(r.left(), r.top(), r.left() + r.width(), r.top() + r.height()));
+ XDrawLine(d->dpy, d->hd, d->gc, l.p1().x(), l.p1().y(), l.p2().x(), l.p2().y());
+ }
+ continue;
+ }
+
+ r = r.intersected(clip);
+ if (r.isEmpty()) {
+ --numClipped;
+ continue;
+ }
+ xrects[i].x = short(r.x());
+ xrects[i].y = short(r.y());
+ xrects[i].width = ushort(r.width());
+ xrects[i].height = ushort(r.height());
+ }
+ if (numClipped) {
+ d->setupAdaptedOrigin(rects[0].topLeft());
+ if (d->has_brush)
+ XFillRectangles(d->dpy, d->hd, d->gc_brush, xrects.data(), numClipped);
+ else if (d->has_pen)
+ XDrawRectangles(d->dpy, d->hd, d->gc, xrects.data(), numClipped);
+ d->resetAdaptedOrigin();
+ }
+ }
+ }
+}
+
+static inline void setCapStyle(int cap_style, GC gc)
+{
+ ulong mask = GCCapStyle;
+ XGCValues vals;
+ vals.cap_style = cap_style;
+ XChangeGC(X11->display, gc, mask, &vals);
+}
+
+void QX11PaintEngine::drawPoints(const QPoint *points, int pointCount)
+{
+ Q_ASSERT(points);
+ Q_ASSERT(pointCount);
+ Q_D(QX11PaintEngine);
+
+ if (!d->has_pen)
+ return;
+
+ // use the same test here as in drawPath to ensure that we don't use the path fallback
+ // and end up in XDrawLines for pens with width <= 1
+ if (d->cpen.widthF() > 1.0f
+ || (X11->use_xrender && (d->has_alpha_pen || (d->render_hints & QPainter::Antialiasing)))
+ || (!d->cpen.isCosmetic() && d->txop > QTransform::TxTranslate))
+ {
+ Qt::PenCapStyle capStyle = d->cpen.capStyle();
+ if (capStyle == Qt::FlatCap) {
+ setCapStyle(CapProjecting, d->gc);
+ d->cpen.setCapStyle(Qt::SquareCap);
+ }
+ const QPoint *end = points + pointCount;
+ while (points < end) {
+ QPainterPath path;
+ path.moveTo(*points);
+ path.lineTo(points->x()+.005, points->y());
+ drawPath(path);
+ ++points;
+ }
+
+ if (capStyle == Qt::FlatCap) {
+ setCapStyle(CapButt, d->gc);
+ d->cpen.setCapStyle(capStyle);
+ }
+ return;
+ }
+
+ static const int BUF_SIZE = 1024;
+ XPoint xPoints[BUF_SIZE];
+ int i = 0, j = 0;
+ while (i < pointCount) {
+ while (i < pointCount && j < BUF_SIZE) {
+ const QPoint &xformed = d->matrix.map(points[i]);
+ int x = xformed.x();
+ int y = xformed.y();
+ if (x >= SHRT_MIN && y >= SHRT_MIN && x < SHRT_MAX && y < SHRT_MAX) {
+ xPoints[j].x = x;
+ xPoints[j].y = y;
+ ++j;
+ }
+ ++i;
+ }
+ if (j)
+ XDrawPoints(d->dpy, d->hd, d->gc, xPoints, j, CoordModeOrigin);
+
+ j = 0;
+ }
+}
+
+void QX11PaintEngine::drawPoints(const QPointF *points, int pointCount)
+{
+ Q_ASSERT(points);
+ Q_ASSERT(pointCount);
+ Q_D(QX11PaintEngine);
+
+ if (!d->has_pen)
+ return;
+
+ // use the same test here as in drawPath to ensure that we don't use the path fallback
+ // and end up in XDrawLines for pens with width <= 1
+ if (d->cpen.widthF() > 1.0f
+ || (X11->use_xrender && (d->has_alpha_pen || (d->render_hints & QPainter::Antialiasing)))
+ || (!d->cpen.isCosmetic() && d->txop > QTransform::TxTranslate))
+ {
+ Qt::PenCapStyle capStyle = d->cpen.capStyle();
+ if (capStyle == Qt::FlatCap) {
+ setCapStyle(CapProjecting, d->gc);
+ d->cpen.setCapStyle(Qt::SquareCap);
+ }
+
+ const QPointF *end = points + pointCount;
+ while (points < end) {
+ QPainterPath path;
+ path.moveTo(*points);
+ path.lineTo(points->x() + 0.005, points->y());
+ drawPath(path);
+ ++points;
+ }
+ if (capStyle == Qt::FlatCap) {
+ setCapStyle(CapButt, d->gc);
+ d->cpen.setCapStyle(capStyle);
+ }
+ return;
+ }
+
+ static const int BUF_SIZE = 1024;
+ XPoint xPoints[BUF_SIZE];
+ int i = 0, j = 0;
+ while (i < pointCount) {
+ while (i < pointCount && j < BUF_SIZE) {
+ const QPointF &xformed = d->matrix.map(points[i]);
+ int x = qFloor(xformed.x());
+ int y = qFloor(xformed.y());
+
+ if (x >= SHRT_MIN && y >= SHRT_MIN && x < SHRT_MAX && y < SHRT_MAX) {
+ xPoints[j].x = x;
+ xPoints[j].y = y;
+ ++j;
+ }
+ ++i;
+ }
+ if (j)
+ XDrawPoints(d->dpy, d->hd, d->gc, xPoints, j, CoordModeOrigin);
+
+ j = 0;
+ }
+}
+
+QPainter::RenderHints QX11PaintEngine::supportedRenderHints() const
+{
+#if !defined(QT_NO_XRENDER)
+ if (X11->use_xrender)
+ return QPainter::Antialiasing;
+#endif
+ return QFlag(0);
+}
+
+void QX11PaintEngine::updateState(const QPaintEngineState &state)
+{
+ Q_D(QX11PaintEngine);
+ QPaintEngine::DirtyFlags flags = state.state();
+
+
+ if (flags & DirtyOpacity) {
+ d->opacity = state.opacity();
+ // Force update pen/brush as to get proper alpha colors propagated
+ flags |= DirtyPen;
+ flags |= DirtyBrush;
+ }
+
+ if (flags & DirtyTransform) updateMatrix(state.transform());
+ if (flags & DirtyPen) updatePen(state.pen());
+ if (flags & (DirtyBrush | DirtyBrushOrigin)) updateBrush(state.brush(), state.brushOrigin());
+ if (flags & DirtyFont) updateFont(state.font());
+
+ if (state.state() & DirtyClipEnabled) {
+ if (state.isClipEnabled()) {
+ QPolygonF clip_poly_dev(d->matrix.map(painter()->clipPath().toFillPolygon()));
+ QPolygonF clipped_poly_dev;
+ d->clipPolygon_dev(clip_poly_dev, &clipped_poly_dev);
+ updateClipRegion_dev(QRegion(clipped_poly_dev.toPolygon()), Qt::ReplaceClip);
+ } else {
+ updateClipRegion_dev(QRegion(), Qt::NoClip);
+ }
+ }
+
+ if (flags & DirtyClipPath) {
+ QPolygonF clip_poly_dev(d->matrix.map(state.clipPath().toFillPolygon()));
+ QPolygonF clipped_poly_dev;
+ d->clipPolygon_dev(clip_poly_dev, &clipped_poly_dev);
+ updateClipRegion_dev(QRegion(clipped_poly_dev.toPolygon(), state.clipPath().fillRule()),
+ state.clipOperation());
+ } else if (flags & DirtyClipRegion) {
+ extern QPainterPath qt_regionToPath(const QRegion &region);
+ QPainterPath clip_path = qt_regionToPath(state.clipRegion());
+ QPolygonF clip_poly_dev(d->matrix.map(clip_path.toFillPolygon()));
+ QPolygonF clipped_poly_dev;
+ d->clipPolygon_dev(clip_poly_dev, &clipped_poly_dev);
+ updateClipRegion_dev(QRegion(clipped_poly_dev.toPolygon()), state.clipOperation());
+ }
+ if (flags & DirtyHints) updateRenderHints(state.renderHints());
+ if (flags & DirtyCompositionMode) {
+ int function = GXcopy;
+ if (state.compositionMode() >= QPainter::RasterOp_SourceOrDestination) {
+ switch (state.compositionMode()) {
+ case QPainter::RasterOp_SourceOrDestination:
+ function = GXor;
+ break;
+ case QPainter::RasterOp_SourceAndDestination:
+ function = GXand;
+ break;
+ case QPainter::RasterOp_SourceXorDestination:
+ function = GXxor;
+ break;
+ case QPainter::RasterOp_NotSourceAndNotDestination:
+ function = GXnor;
+ break;
+ case QPainter::RasterOp_NotSourceOrNotDestination:
+ function = GXnand;
+ break;
+ case QPainter::RasterOp_NotSourceXorDestination:
+ function = GXequiv;
+ break;
+ case QPainter::RasterOp_NotSource:
+ function = GXcopyInverted;
+ break;
+ case QPainter::RasterOp_SourceAndNotDestination:
+ function = GXandReverse;
+ break;
+ case QPainter::RasterOp_NotSourceAndDestination:
+ function = GXandInverted;
+ break;
+ default:
+ function = GXcopy;
+ }
+ }
+#if !defined(QT_NO_XRENDER)
+ else {
+ d->composition_mode =
+ qpainterOpToXrender(state.compositionMode());
+ }
+#endif
+ XSetFunction(X11->display, d->gc, function);
+ XSetFunction(X11->display, d->gc_brush, function);
+ }
+ d->decidePathFallback();
+ d->decideCoordAdjust();
+}
+
+void QX11PaintEngine::updateRenderHints(QPainter::RenderHints hints)
+{
+ Q_D(QX11PaintEngine);
+ d->render_hints = hints;
+
+#if !defined(QT_NO_XRENDER)
+ if (X11->use_xrender && d->picture) {
+ XRenderPictureAttributes attrs;
+ attrs.poly_edge = (hints & QPainter::Antialiasing) ? PolyEdgeSmooth : PolyEdgeSharp;
+ XRenderChangePicture(d->dpy, d->picture, CPPolyEdge, &attrs);
+ }
+#endif
+}
+
+void QX11PaintEngine::updatePen(const QPen &pen)
+{
+ Q_D(QX11PaintEngine);
+ d->cpen = pen;
+ int cp = CapButt;
+ int jn = JoinMiter;
+ int ps = pen.style();
+
+ if (d->opacity < 1.0) {
+ QColor c = d->cpen.color();
+ c.setAlpha(qRound(c.alpha()*d->opacity));
+ d->cpen.setColor(c);
+ }
+
+ d->has_pen = (ps != Qt::NoPen);
+ d->has_alpha_pen = (pen.color().alpha() != 255);
+
+ switch (pen.capStyle()) {
+ case Qt::SquareCap:
+ cp = CapProjecting;
+ break;
+ case Qt::RoundCap:
+ cp = CapRound;
+ break;
+ case Qt::FlatCap:
+ default:
+ cp = CapButt;
+ break;
+ }
+ switch (pen.joinStyle()) {
+ case Qt::BevelJoin:
+ jn = JoinBevel;
+ break;
+ case Qt::RoundJoin:
+ jn = JoinRound;
+ break;
+ case Qt::MiterJoin:
+ default:
+ jn = JoinMiter;
+ break;
+ }
+
+ d->adapted_pen_origin = false;
+
+ char dashes[10]; // custom pen dashes
+ int dash_len = 0; // length of dash list
+ int xStyle = LineSolid;
+
+ /*
+ We are emulating Windows here. Windows treats cpen.width() == 1
+ (or 0) as a very special case. The fudge variable unifies this
+ case with the general case.
+ */
+ qreal pen_width = pen.widthF();
+ int scale = qRound(pen_width < 1 ? 1 : pen_width);
+ int space = (pen_width < 1 && pen_width > 0 ? 1 : (2 * scale));
+ int dot = 1 * scale;
+ int dash = 4 * scale;
+
+ d->has_custom_pen = false;
+
+ switch (ps) {
+ case Qt::NoPen:
+ case Qt::SolidLine:
+ xStyle = LineSolid;
+ break;
+ case Qt::DashLine:
+ dashes[0] = dash;
+ dashes[1] = space;
+ dash_len = 2;
+ xStyle = LineOnOffDash;
+ break;
+ case Qt::DotLine:
+ dashes[0] = dot;
+ dashes[1] = space;
+ dash_len = 2;
+ xStyle = LineOnOffDash;
+ break;
+ case Qt::DashDotLine:
+ dashes[0] = dash;
+ dashes[1] = space;
+ dashes[2] = dot;
+ dashes[3] = space;
+ dash_len = 4;
+ xStyle = LineOnOffDash;
+ break;
+ case Qt::DashDotDotLine:
+ dashes[0] = dash;
+ dashes[1] = space;
+ dashes[2] = dot;
+ dashes[3] = space;
+ dashes[4] = dot;
+ dashes[5] = space;
+ dash_len = 6;
+ xStyle = LineOnOffDash;
+ break;
+ case Qt::CustomDashLine:
+ d->has_custom_pen = true;
+ break;
+ }
+
+ ulong mask = GCForeground | GCBackground | GCGraphicsExposures | GCLineWidth
+ | GCCapStyle | GCJoinStyle | GCLineStyle;
+ XGCValues vals;
+ vals.graphics_exposures = false;
+ if (d->pdev_depth == 1) {
+ vals.foreground = qGray(pen.color().rgb()) > 127 ? 0 : 1;
+ vals.background = qGray(QColor(Qt::transparent).rgb()) > 127 ? 0 : 1;
+ } else if (d->pdev->devType() == QInternal::Pixmap && d->pdev_depth == 32
+ && X11->use_xrender) {
+ vals.foreground = pen.color().rgba();
+ vals.background = QColor(Qt::transparent).rgba();
+ } else {
+ QColormap cmap = QColormap::instance(d->scrn);
+ vals.foreground = cmap.pixel(pen.color());
+ vals.background = cmap.pixel(QColor(Qt::transparent));
+ }
+
+
+ vals.line_width = qRound(pen.widthF());
+ vals.cap_style = cp;
+ vals.join_style = jn;
+ vals.line_style = xStyle;
+
+ XChangeGC(d->dpy, d->gc, mask, &vals);
+
+ if (dash_len) { // make dash list
+ XSetDashes(d->dpy, d->gc, 0, dashes, dash_len);
+ }
+
+ if (!d->has_clipping) { // if clipping is set the paintevent clip region is merged with the clip region
+ QRegion sysClip = systemClip();
+ if (!sysClip.isEmpty())
+ x11SetClipRegion(d->dpy, d->gc, 0, d->picture, sysClip);
+ else
+ x11ClearClipRegion(d->dpy, d->gc, 0, d->picture);
+ }
+}
+
+void QX11PaintEngine::updateBrush(const QBrush &brush, const QPointF &origin)
+{
+ Q_D(QX11PaintEngine);
+ d->cbrush = brush;
+ d->bg_origin = origin;
+ d->adapted_brush_origin = false;
+#if !defined(QT_NO_XRENDER)
+ d->current_brush = 0;
+#endif
+ if (d->opacity < 1.0) {
+ QColor c = d->cbrush.color();
+ c.setAlpha(qRound(c.alpha()*d->opacity));
+ d->cbrush.setColor(c);
+ }
+
+ int s = FillSolid;
+ int bs = d->cbrush.style();
+ d->has_brush = (bs != Qt::NoBrush);
+ d->has_pattern = bs >= Qt::Dense1Pattern && bs <= Qt::DiagCrossPattern;
+ d->has_texture = bs == Qt::TexturePattern;
+ d->has_alpha_brush = brush.color().alpha() != 255;
+ d->has_alpha_texture = d->has_texture && d->cbrush.texture().hasAlphaChannel();
+
+ ulong mask = GCForeground | GCBackground | GCGraphicsExposures
+ | GCLineStyle | GCCapStyle | GCJoinStyle | GCFillStyle;
+ XGCValues vals;
+ vals.graphics_exposures = false;
+ if (d->pdev_depth == 1) {
+ vals.foreground = qGray(d->cbrush.color().rgb()) > 127 ? 0 : 1;
+ vals.background = qGray(QColor(Qt::transparent).rgb()) > 127 ? 0 : 1;
+ } else if (X11->use_xrender && d->pdev->devType() == QInternal::Pixmap
+ && d->pdev_depth == 32) {
+ vals.foreground = d->cbrush.color().rgba();
+ vals.background = QColor(Qt::transparent).rgba();
+ } else {
+ QColormap cmap = QColormap::instance(d->scrn);
+ vals.foreground = cmap.pixel(d->cbrush.color());
+ vals.background = cmap.pixel(QColor(Qt::transparent));
+
+ if (!X11->use_xrender && d->has_brush && !d->has_pattern && !brush.isOpaque()) {
+ QPixmap pattern = qt_patternForAlpha(brush.color().alpha(), d->scrn);
+ mask |= GCStipple;
+ vals.stipple = pattern.handle();
+ s = FillStippled;
+ d->adapted_brush_origin = true;
+ }
+ }
+ vals.cap_style = CapButt;
+ vals.join_style = JoinMiter;
+ vals.line_style = LineSolid;
+
+ if (d->has_pattern || d->has_texture) {
+ if (bs == Qt::TexturePattern) {
+ d->brush_pm = qt_toX11Pixmap(d->cbrush.texture());
+#if !defined(QT_NO_XRENDER)
+ if (X11->use_xrender) {
+ XRenderPictureAttributes attrs;
+ attrs.repeat = true;
+ XRenderChangePicture(d->dpy, d->brush_pm.x11PictureHandle(), CPRepeat, &attrs);
+ QX11PixmapData *data = static_cast<QX11PixmapData*>(d->brush_pm.data);
+ if (data->mask_picture)
+ XRenderChangePicture(d->dpy, data->mask_picture, CPRepeat, &attrs);
+ }
+#endif
+ } else {
+ d->brush_pm = qt_toX11Pixmap(qt_pixmapForBrush(bs, true));
+ }
+ d->brush_pm.x11SetScreen(d->scrn);
+ if (d->brush_pm.depth() == 1) {
+ mask |= GCStipple;
+ vals.stipple = d->brush_pm.handle();
+ s = FillStippled;
+#if !defined(QT_NO_XRENDER)
+ if (X11->use_xrender) {
+ d->bitmap_texture = QPixmap(d->brush_pm.size());
+ d->bitmap_texture.fill(Qt::transparent);
+ d->bitmap_texture = qt_toX11Pixmap(d->bitmap_texture);
+ d->bitmap_texture.x11SetScreen(d->scrn);
+
+ ::Picture src = X11->getSolidFill(d->scrn, d->cbrush.color());
+ XRenderComposite(d->dpy, PictOpSrc, src, d->brush_pm.x11PictureHandle(),
+ d->bitmap_texture.x11PictureHandle(),
+ 0, 0, d->brush_pm.width(), d->brush_pm.height(),
+ 0, 0, d->brush_pm.width(), d->brush_pm.height());
+
+ XRenderPictureAttributes attrs;
+ attrs.repeat = true;
+ XRenderChangePicture(d->dpy, d->bitmap_texture.x11PictureHandle(), CPRepeat, &attrs);
+
+ d->current_brush = d->bitmap_texture.x11PictureHandle();
+ }
+#endif
+ } else {
+ mask |= GCTile;
+#ifndef QT_NO_XRENDER
+ if (d->pdev_depth == 32 && d->brush_pm.depth() != 32) {
+ QX11PixmapData *brushData = static_cast<QX11PixmapData*>(d->brush_pm.data);
+ brushData->convertToARGB32();
+ }
+#endif
+ vals.tile = (d->brush_pm.depth() == d->pdev_depth
+ ? d->brush_pm.handle()
+ : static_cast<QX11PixmapData*>(d->brush_pm.data)->x11ConvertToDefaultDepth());
+ s = FillTiled;
+#if !defined(QT_NO_XRENDER)
+ d->current_brush = d->cbrush.texture().x11PictureHandle();
+#endif
+ }
+
+ mask |= GCTileStipXOrigin | GCTileStipYOrigin;
+ vals.ts_x_origin = qRound(origin.x());
+ vals.ts_y_origin = qRound(origin.y());
+ }
+#if !defined(QT_NO_XRENDER)
+ else if (d->has_alpha_brush) {
+ d->current_brush = X11->getSolidFill(d->scrn, d->cbrush.color());
+ }
+#endif
+
+ vals.fill_style = s;
+ XChangeGC(d->dpy, d->gc_brush, mask, &vals);
+ if (!d->has_clipping) {
+ QRegion sysClip = systemClip();
+ if (!sysClip.isEmpty())
+ x11SetClipRegion(d->dpy, d->gc_brush, 0, d->picture, sysClip);
+ else
+ x11ClearClipRegion(d->dpy, d->gc_brush, 0, d->picture);
+ }
+}
+
+void QX11PaintEngine::drawEllipse(const QRectF &rect)
+{
+ QRect aligned = rect.toAlignedRect();
+ if (aligned == rect)
+ drawEllipse(aligned);
+ else
+ QPaintEngine::drawEllipse(rect);
+}
+
+void QX11PaintEngine::drawEllipse(const QRect &rect)
+{
+ Q_D(QX11PaintEngine);
+ QRect devclip(SHRT_MIN, SHRT_MIN, SHRT_MAX*2 - 1, SHRT_MAX*2 - 1);
+ QRect r(rect);
+ if (d->txop < QTransform::TxRotate) {
+ r = d->matrix.mapRect(rect);
+ } else if (d->txop == QTransform::TxRotate && rect.width() == rect.height()) {
+ QPainterPath path;
+ path.addEllipse(rect);
+ r = d->matrix.map(path).boundingRect().toRect();
+ }
+
+ if (d->has_alpha_brush || d->has_alpha_pen || d->has_custom_pen || (d->render_hints & QPainter::Antialiasing)
+ || d->has_alpha_texture || devclip.intersected(r) != r
+ || (d->has_complex_xform
+ && !(d->has_non_scaling_xform && rect.width() == rect.height())))
+ {
+ QPainterPath path;
+ path.addEllipse(rect);
+ drawPath(path);
+ return;
+ }
+
+ int x = r.x();
+ int y = r.y();
+ int w = r.width();
+ int h = r.height();
+ if (w < 1 || h < 1)
+ return;
+ if (w == 1 && h == 1) {
+ XDrawPoint(d->dpy, d->hd, d->has_pen ? d->gc : d->gc_brush, x, y);
+ return;
+ }
+ d->setupAdaptedOrigin(rect.topLeft());
+ if (d->has_brush) { // draw filled ellipse
+ if (!d->has_pen) {
+ XFillArc(d->dpy, d->hd, d->gc_brush, x, y, w-1, h-1, 0, 360*64);
+ XDrawArc(d->dpy, d->hd, d->gc_brush, x, y, w-1, h-1, 0, 360*64);
+ return;
+ } else{
+ XFillArc(d->dpy, d->hd, d->gc_brush, x, y, w, h, 0, 360*64);
+ }
+ }
+ if (d->has_pen) // draw outline
+ XDrawArc(d->dpy, d->hd, d->gc, x, y, w, h, 0, 360*64);
+ d->resetAdaptedOrigin();
+}
+
+
+
+void QX11PaintEnginePrivate::fillPolygon_translated(const QPointF *polygonPoints, int pointCount,
+ QX11PaintEnginePrivate::GCMode gcMode,
+ QPaintEngine::PolygonDrawMode mode)
+{
+
+ QVarLengthArray<QPointF> translated_points(pointCount);
+ QPointF offset(matrix.dx(), matrix.dy());
+
+ qreal offs = adjust_coords ? aliasedCoordinateDelta : 0.0;
+ if (!X11->use_xrender || !(render_hints & QPainter::Antialiasing))
+ offset += QPointF(aliasedCoordinateDelta, aliasedCoordinateDelta);
+
+ for (int i = 0; i < pointCount; ++i) {
+ translated_points[i] = polygonPoints[i] + offset;
+
+ translated_points[i].rx() = qRound(translated_points[i].x()) + offs;
+ translated_points[i].ry() = qRound(translated_points[i].y()) + offs;
+ }
+
+ fillPolygon_dev(translated_points.data(), pointCount, gcMode, mode);
+}
+
+#ifndef QT_NO_XRENDER
+static void qt_XRenderCompositeTrapezoids(Display *dpy,
+ int op,
+ Picture src,
+ Picture dst,
+ _Xconst XRenderPictFormat *maskFormat,
+ int xSrc,
+ int ySrc,
+ const XTrapezoid *traps, int size)
+{
+ const int MAX_TRAPS = 50000;
+ while (size) {
+ int to_draw = size;
+ if (to_draw > MAX_TRAPS)
+ to_draw = MAX_TRAPS;
+ XRenderCompositeTrapezoids(dpy, op, src, dst,
+ maskFormat,
+ xSrc, ySrc,
+ traps, to_draw);
+ size -= to_draw;
+ traps += to_draw;
+ }
+}
+#endif
+
+void QX11PaintEnginePrivate::fillPolygon_dev(const QPointF *polygonPoints, int pointCount,
+ QX11PaintEnginePrivate::GCMode gcMode,
+ QPaintEngine::PolygonDrawMode mode)
+{
+ int clippedCount = 0;
+ qt_float_point *clippedPoints = 0;
+
+#ifndef QT_NO_XRENDER
+ //can change if we switch to pen if gcMode != BrushGC
+ bool has_fill_texture = has_texture;
+ bool has_fill_pattern = has_pattern;
+ ::Picture src;
+#endif
+ QBrush fill;
+ GC fill_gc;
+ if (gcMode == BrushGC) {
+ fill = cbrush;
+ fill_gc = gc_brush;
+#ifndef QT_NO_XRENDER
+ if (current_brush)
+ src = current_brush;
+ else
+ src = X11->getSolidFill(scrn, fill.color());
+#endif
+ } else {
+ fill = QBrush(cpen.brush());
+ fill_gc = gc;
+#ifndef QT_NO_XRENDER
+ //we use the pens brush
+ has_fill_texture = (fill.style() == Qt::TexturePattern);
+ has_fill_pattern = (fill.style() >= Qt::Dense1Pattern && fill.style() <= Qt::DiagCrossPattern);
+ if (has_fill_texture)
+ src = fill.texture().x11PictureHandle();
+ else if (has_fill_pattern)
+ src = getPatternFill(scrn, fill);
+ else
+ src = X11->getSolidFill(scrn, fill.color());
+#endif
+ }
+
+ polygonClipper.clipPolygon((qt_float_point *) polygonPoints, pointCount,
+ &clippedPoints, &clippedCount);
+
+#ifndef QT_NO_XRENDER
+ bool solid_fill = fill.color().alpha() == 255;
+ if (has_fill_texture && fill.texture().depth() == 1 && solid_fill) {
+ has_fill_texture = false;
+ has_fill_pattern = true;
+ }
+
+ bool antialias = render_hints & QPainter::Antialiasing;
+
+ if (X11->use_xrender
+ && picture
+ && !has_fill_pattern
+ && (clippedCount > 0)
+ && (fill.style() != Qt::NoBrush)
+ && ((has_fill_texture && fill.texture().hasAlpha()) || antialias || !solid_fill || has_alpha_pen != has_alpha_brush))
+ {
+ QRect br = tessellator->tessellate((QPointF *)clippedPoints, clippedCount,
+ mode == QPaintEngine::WindingMode);
+ if (tessellator->size > 0) {
+ XRenderPictureAttributes attrs;
+ attrs.poly_edge = antialias ? PolyEdgeSmooth : PolyEdgeSharp;
+ XRenderChangePicture(dpy, picture, CPPolyEdge, &attrs);
+ int x_offset = int(XFixedToDouble(tessellator->traps[0].left.p1.x) - bg_origin.x());
+ int y_offset = int(XFixedToDouble(tessellator->traps[0].left.p1.y) - bg_origin.y());
+ qt_XRenderCompositeTrapezoids(dpy, composition_mode, src, picture,
+ antialias
+ ? XRenderFindStandardFormat(dpy, PictStandardA8)
+ : XRenderFindStandardFormat(dpy, PictStandardA1),
+ x_offset, y_offset,
+ tessellator->traps, tessellator->size);
+ tessellator->done();
+ }
+ } else
+#endif
+ if (fill.style() != Qt::NoBrush) {
+ if (clippedCount > 0) {
+ QVarLengthArray<XPoint> xpoints(clippedCount);
+ for (int i = 0; i < clippedCount; ++i) {
+ xpoints[i].x = qFloor(clippedPoints[i].x);
+ xpoints[i].y = qFloor(clippedPoints[i].y);
+ }
+ if (mode == QPaintEngine::WindingMode)
+ XSetFillRule(dpy, fill_gc, WindingRule);
+ setupAdaptedOrigin(QPoint(xpoints[0].x, xpoints[0].y));
+ XFillPolygon(dpy, hd, fill_gc,
+ xpoints.data(), clippedCount,
+ mode == QPaintEngine::ConvexMode ? Convex : Complex, CoordModeOrigin);
+ resetAdaptedOrigin();
+ if (mode == QPaintEngine::WindingMode)
+ XSetFillRule(dpy, fill_gc, EvenOddRule);
+ }
+ }
+}
+
+void QX11PaintEnginePrivate::strokePolygon_translated(const QPointF *polygonPoints, int pointCount, bool close)
+{
+ QVarLengthArray<QPointF> translated_points(pointCount);
+ QPointF offset(matrix.dx(), matrix.dy());
+ for (int i = 0; i < pointCount; ++i)
+ translated_points[i] = polygonPoints[i] + offset;
+ strokePolygon_dev(translated_points.data(), pointCount, close);
+}
+
+void QX11PaintEnginePrivate::strokePolygon_dev(const QPointF *polygonPoints, int pointCount, bool close)
+{
+ int clippedCount = 0;
+ qt_float_point *clippedPoints = 0;
+ polygonClipper.clipPolygon((qt_float_point *) polygonPoints, pointCount,
+ &clippedPoints, &clippedCount, close);
+
+ if (clippedCount > 0) {
+ QVarLengthArray<XPoint> xpoints(clippedCount);
+ for (int i = 0; i < clippedCount; ++i) {
+ xpoints[i].x = qRound(clippedPoints[i].x + aliasedCoordinateDelta);
+ xpoints[i].y = qRound(clippedPoints[i].y + aliasedCoordinateDelta);
+ }
+ uint numberPoints = qMin(clippedCount, xlibMaxLinePoints);
+ XPoint *pts = xpoints.data();
+ XDrawLines(dpy, hd, gc, pts, numberPoints, CoordModeOrigin);
+ pts += numberPoints;
+ clippedCount -= numberPoints;
+ numberPoints = qMin(clippedCount, xlibMaxLinePoints-1);
+ while (clippedCount) {
+ XDrawLines(dpy, hd, gc, pts-1, numberPoints+1, CoordModeOrigin);
+ pts += numberPoints;
+ clippedCount -= numberPoints;
+ numberPoints = qMin(clippedCount, xlibMaxLinePoints-1);
+ }
+ }
+}
+
+void QX11PaintEngine::drawPolygon(const QPointF *polygonPoints, int pointCount, PolygonDrawMode mode)
+{
+ Q_D(QX11PaintEngine);
+ if (d->use_path_fallback) {
+ QPainterPath path(polygonPoints[0]);
+ for (int i = 1; i < pointCount; ++i)
+ path.lineTo(polygonPoints[i]);
+ if (mode == PolylineMode) {
+ QBrush oldBrush = d->cbrush;
+ d->cbrush = QBrush(Qt::NoBrush);
+ path.setFillRule(Qt::WindingFill);
+ drawPath(path);
+ d->cbrush = oldBrush;
+ } else {
+ path.setFillRule(mode == OddEvenMode ? Qt::OddEvenFill : Qt::WindingFill);
+ path.closeSubpath();
+ drawPath(path);
+ }
+ return;
+ }
+ if (mode != PolylineMode && d->has_brush)
+ d->fillPolygon_translated(polygonPoints, pointCount, QX11PaintEnginePrivate::BrushGC, mode);
+
+ if (d->has_pen)
+ d->strokePolygon_translated(polygonPoints, pointCount, mode != PolylineMode);
+}
+
+
+void QX11PaintEnginePrivate::fillPath(const QPainterPath &path, QX11PaintEnginePrivate::GCMode gc_mode, bool transform)
+{
+ qreal offs = adjust_coords ? aliasedCoordinateDelta : 0.0;
+
+ QPainterPath clippedPath;
+ QPainterPath clipPath;
+ clipPath.addRect(polygonClipper.boundingRect());
+
+ if (transform)
+ clippedPath = (path*matrix).intersected(clipPath);
+ else
+ clippedPath = path.intersected(clipPath);
+
+ QList<QPolygonF> polys = clippedPath.toFillPolygons();
+ for (int i = 0; i < polys.size(); ++i) {
+ QVarLengthArray<QPointF> translated_points(polys.at(i).size());
+
+ for (int j = 0; j < polys.at(i).size(); ++j) {
+ translated_points[j] = polys.at(i).at(j);
+ if (!X11->use_xrender || !(render_hints & QPainter::Antialiasing)) {
+ translated_points[j].rx() = qRound(translated_points[j].rx() + aliasedCoordinateDelta) + offs;
+ translated_points[j].ry() = qRound(translated_points[j].ry() + aliasedCoordinateDelta) + offs;
+ }
+ }
+
+ fillPolygon_dev(translated_points.data(), polys.at(i).size(), gc_mode,
+ path.fillRule() == Qt::OddEvenFill ? QPaintEngine::OddEvenMode : QPaintEngine::WindingMode);
+ }
+}
+
+void QX11PaintEngine::drawPath(const QPainterPath &path)
+{
+ Q_D(QX11PaintEngine);
+ if (path.isEmpty())
+ return;
+ QTransform old_matrix = d->matrix;
+
+ if (d->has_brush)
+ d->fillPath(path, QX11PaintEnginePrivate::BrushGC, true);
+ if (d->has_pen
+ && ((X11->use_xrender && (d->has_alpha_pen || (d->render_hints & QPainter::Antialiasing)))
+ || (!d->cpen.isCosmetic() && d->txop > QTransform::TxTranslate
+ && !d->has_non_scaling_xform)
+ || (d->cpen.style() == Qt::CustomDashLine))) {
+ QPainterPathStroker stroker;
+ if (d->cpen.style() == Qt::CustomDashLine) {
+ stroker.setDashPattern(d->cpen.dashPattern());
+ stroker.setDashOffset(d->cpen.dashOffset());
+ } else {
+ stroker.setDashPattern(d->cpen.style());
+ }
+ stroker.setCapStyle(d->cpen.capStyle());
+ stroker.setJoinStyle(d->cpen.joinStyle());
+ QPainterPath stroke;
+ qreal width = d->cpen.widthF();
+ QPolygonF poly;
+ // necessary to get aliased alphablended primitives to be drawn correctly
+ if (d->cpen.isCosmetic() || d->has_scaling_xform) {
+ stroker.setWidth(width == 0 ? 1 : width * d->xform_scale);
+ stroke = stroker.createStroke(path * d->matrix);
+ if (stroke.isEmpty())
+ return;
+ stroke.setFillRule(Qt::WindingFill);
+ d->fillPath(stroke, QX11PaintEnginePrivate::PenGC, false);
+ } else {
+ stroker.setWidth(width);
+ stroke = stroker.createStroke(path);
+ if (stroke.isEmpty())
+ return;
+ stroke.setFillRule(Qt::WindingFill);
+ d->fillPath(stroke, QX11PaintEnginePrivate::PenGC, true);
+ }
+ } else if (d->has_pen) {
+ // if we have a cosmetic pen - use XDrawLine() for speed
+ QList<QPolygonF> polys = path.toSubpathPolygons(d->matrix);
+ for (int i = 0; i < polys.size(); ++i)
+ d->strokePolygon_dev(polys.at(i).data(), polys.at(i).size(), false);
+ }
+}
+
+Q_GUI_EXPORT void qt_x11_drawImage(const QRect &rect, const QPoint &pos, const QImage &image,
+ Drawable hd, GC gc, Display *dpy, Visual *visual, int depth)
+{
+ Q_ASSERT(image.format() == QImage::Format_RGB32);
+ Q_ASSERT(image.depth() == 32);
+
+ XImage *xi;
+ // Note: this code assumes either RGB or BGR, 8 bpc server layouts
+ const uint red_mask = (uint) visual->red_mask;
+ bool bgr_layout = (red_mask == 0xff);
+
+ const int w = rect.width();
+ const int h = rect.height();
+
+ QImage im;
+ if ((QSysInfo::ByteOrder == QSysInfo::BigEndian
+ && ((ImageByteOrder(X11->display) == LSBFirst) || bgr_layout))
+ || (ImageByteOrder(X11->display) == MSBFirst && QSysInfo::ByteOrder == QSysInfo::LittleEndian))
+ {
+ im = image.copy(rect);
+ const int iw = im.bytesPerLine() / 4;
+ uint *data = (uint *)im.bits();
+ for (int i=0; i < h; i++) {
+ uint *p = data;
+ uint *end = p + w;
+ if (bgr_layout && ImageByteOrder(X11->display) == MSBFirst && QSysInfo::ByteOrder == QSysInfo::LittleEndian) {
+ while (p < end) {
+ *p = ((*p << 8) & 0xffffff00) | ((*p >> 24) & 0x000000ff);
+ p++;
+ }
+ } else if ((ImageByteOrder(X11->display) == LSBFirst && QSysInfo::ByteOrder == QSysInfo::BigEndian)
+ || (ImageByteOrder(X11->display) == MSBFirst && QSysInfo::ByteOrder == QSysInfo::LittleEndian)) {
+ while (p < end) {
+ *p = ((*p << 24) & 0xff000000) | ((*p << 8) & 0x00ff0000)
+ | ((*p >> 8) & 0x0000ff00) | ((*p >> 24) & 0x000000ff);
+ p++;
+ }
+ } else if (ImageByteOrder(X11->display) == MSBFirst && QSysInfo::ByteOrder == QSysInfo::BigEndian) {
+ while (p < end) {
+ *p = ((*p << 16) & 0x00ff0000) | ((*p >> 16) & 0x000000ff)
+ | ((*p ) & 0xff00ff00);
+ p++;
+ }
+ }
+ data += iw;
+ }
+ xi = XCreateImage(dpy, visual, depth, ZPixmap,
+ 0, (char *) im.bits(), w, h, 32, im.bytesPerLine());
+ } else {
+ xi = XCreateImage(dpy, visual, depth, ZPixmap,
+ 0, (char *) image.scanLine(rect.y())+rect.x()*sizeof(uint), w, h, 32, image.bytesPerLine());
+ }
+ XPutImage(dpy, hd, gc, xi, 0, 0, pos.x(), pos.y(), w, h);
+ xi->data = 0; // QImage owns these bits
+ XDestroyImage(xi);
+}
+
+void QX11PaintEngine::drawImage(const QRectF &r, const QImage &image, const QRectF &sr, Qt::ImageConversionFlags flags)
+{
+ Q_D(QX11PaintEngine);
+
+ if (image.format() == QImage::Format_RGB32
+ && d->pdev_depth >= 24 && image.depth() == 32
+ && r.size() == sr.size())
+ {
+ int sx = qRound(sr.x());
+ int sy = qRound(sr.y());
+ int x = qRound(r.x());
+ int y = qRound(r.y());
+ int w = qRound(r.width());
+ int h = qRound(r.height());
+
+ qt_x11_drawImage(QRect(sx, sy, w, h), QPoint(x, y), image, d->hd, d->gc, d->dpy,
+ (Visual *)d->xinfo->visual(), d->pdev_depth);
+ } else {
+ QPaintEngine::drawImage(r, image, sr, flags);
+ }
+}
+
+void QX11PaintEngine::drawPixmap(const QRectF &r, const QPixmap &px, const QRectF &_sr)
+{
+ Q_D(QX11PaintEngine);
+ QRectF sr = _sr;
+ int x = qRound(r.x());
+ int y = qRound(r.y());
+ int sx = qRound(sr.x());
+ int sy = qRound(sr.y());
+ int sw = qRound(sr.width());
+ int sh = qRound(sr.height());
+
+ QPixmap pixmap = qt_toX11Pixmap(px);
+
+ if ((d->xinfo && d->xinfo->screen() != pixmap.x11Info().screen())
+ || (pixmap.x11Info().screen() != DefaultScreen(X11->display))) {
+ QPixmap* p = const_cast<QPixmap *>(&pixmap);
+ p->x11SetScreen(d->xinfo ? d->xinfo->screen() : DefaultScreen(X11->display));
+ }
+
+ QPixmap::x11SetDefaultScreen(pixmap.x11Info().screen());
+
+#ifndef QT_NO_XRENDER
+ ::Picture src_pict = static_cast<QX11PixmapData*>(pixmap.data)->picture;
+ if (src_pict && d->picture) {
+ const int pDepth = pixmap.depth();
+ if (pDepth == 1 && (d->has_alpha_pen)) {
+ qt_render_bitmap(d->dpy, d->scrn, src_pict, d->picture,
+ sx, sy, x, y, sw, sh, d->cpen);
+ return;
+ } else if (pDepth != 1 && (pDepth == 32 || pDepth != d->pdev_depth)) {
+ XRenderComposite(d->dpy, d->composition_mode,
+ src_pict, 0, d->picture, sx, sy, 0, 0, x, y, sw, sh);
+ return;
+ }
+ }
+#endif
+
+ bool mono_src = pixmap.depth() == 1;
+ bool mono_dst = d->pdev_depth == 1;
+ bool restore_clip = false;
+
+ if (static_cast<QX11PixmapData*>(pixmap.data)->x11_mask) { // pixmap has a mask
+ QBitmap comb(sw, sh);
+ GC cgc = XCreateGC(d->dpy, comb.handle(), 0, 0);
+ XSetForeground(d->dpy, cgc, 0);
+ XFillRectangle(d->dpy, comb.handle(), cgc, 0, 0, sw, sh);
+ XSetBackground(d->dpy, cgc, 0);
+ XSetForeground(d->dpy, cgc, 1);
+ if (!d->crgn.isEmpty()) {
+ int num;
+ XRectangle *rects = (XRectangle *)qt_getClipRects(d->crgn, num);
+ XSetClipRectangles(d->dpy, cgc, -x, -y, rects, num, Unsorted);
+ } else if (d->has_clipping) {
+ XSetClipRectangles(d->dpy, cgc, 0, 0, 0, 0, Unsorted);
+ }
+ XSetFillStyle(d->dpy, cgc, FillOpaqueStippled);
+ XSetTSOrigin(d->dpy, cgc, -sx, -sy);
+ XSetStipple(d->dpy, cgc,
+ static_cast<QX11PixmapData*>(pixmap.data)->x11_mask);
+ XFillRectangle(d->dpy, comb.handle(), cgc, 0, 0, sw, sh);
+ XFreeGC(d->dpy, cgc);
+
+ XSetClipOrigin(d->dpy, d->gc, x, y);
+ XSetClipMask(d->dpy, d->gc, comb.handle());
+ restore_clip = true;
+ }
+
+ if (mono_src) {
+ if (!d->crgn.isEmpty()) {
+ Pixmap comb = XCreatePixmap(d->dpy, d->hd, sw, sh, 1);
+ GC cgc = XCreateGC(d->dpy, comb, 0, 0);
+ XSetForeground(d->dpy, cgc, 0);
+ XFillRectangle(d->dpy, comb, cgc, 0, 0, sw, sh);
+ int num;
+ XRectangle *rects = (XRectangle *)qt_getClipRects(d->crgn, num);
+ XSetClipRectangles(d->dpy, cgc, -x, -y, rects, num, Unsorted);
+ XCopyArea(d->dpy, pixmap.handle(), comb, cgc, sx, sy, sw, sh, 0, 0);
+ XFreeGC(d->dpy, cgc);
+
+ XSetClipMask(d->dpy, d->gc, comb);
+ XSetClipOrigin(d->dpy, d->gc, x, y);
+ XFreePixmap(d->dpy, comb);
+ } else {
+ XSetClipMask(d->dpy, d->gc, pixmap.handle());
+ XSetClipOrigin(d->dpy, d->gc, x - sx, y - sy);
+ }
+
+ if (mono_dst) {
+ XSetForeground(d->dpy, d->gc, qGray(d->cpen.color().rgb()) > 127 ? 0 : 1);
+ } else {
+ QColormap cmap = QColormap::instance(d->scrn);
+ XSetForeground(d->dpy, d->gc, cmap.pixel(d->cpen.color()));
+ }
+ XFillRectangle(d->dpy, d->hd, d->gc, x, y, sw, sh);
+ restore_clip = true;
+ } else {
+ XCopyArea(d->dpy, pixmap.handle(), d->hd, d->gc, sx, sy, sw, sh, x, y);
+ }
+
+ if (d->pdev->devType() == QInternal::Pixmap) {
+ const QPixmap *px = static_cast<const QPixmap*>(d->pdev);
+ Pixmap src_mask = static_cast<QX11PixmapData*>(pixmap.data)->x11_mask;
+ Pixmap dst_mask = static_cast<QX11PixmapData*>(px->data)->x11_mask;
+ if (dst_mask) {
+ GC cgc = XCreateGC(d->dpy, dst_mask, 0, 0);
+ if (src_mask) { // copy src mask into dst mask
+ XCopyArea(d->dpy, src_mask, dst_mask, cgc, sx, sy, sw, sh, x, y);
+ } else { // no src mask, but make sure the area copied is opaque in dest
+ XSetBackground(d->dpy, cgc, 0);
+ XSetForeground(d->dpy, cgc, 1);
+ XFillRectangle(d->dpy, dst_mask, cgc, x, y, sw, sh);
+ }
+ XFreeGC(d->dpy, cgc);
+ }
+ }
+
+ if (restore_clip) {
+ XSetClipOrigin(d->dpy, d->gc, 0, 0);
+ int num;
+ XRectangle *rects = (XRectangle *)qt_getClipRects(d->crgn, num);
+ if (num == 0)
+ XSetClipMask(d->dpy, d->gc, XNone);
+ else
+ XSetClipRectangles(d->dpy, d->gc, 0, 0, rects, num, Unsorted);
+ }
+}
+
+void QX11PaintEngine::updateMatrix(const QTransform &mtx)
+{
+ Q_D(QX11PaintEngine);
+ d->txop = mtx.type();
+ d->matrix = mtx;
+
+ d->has_complex_xform = (d->txop > QTransform::TxTranslate);
+
+ extern bool qt_scaleForTransform(const QTransform &transform, qreal *scale);
+ bool scaling = qt_scaleForTransform(d->matrix, &d->xform_scale);
+ d->has_scaling_xform = scaling && d->xform_scale != 1.0;
+ d->has_non_scaling_xform = scaling && d->xform_scale == 1.0;
+}
+
+/*
+ NB! the clip region is expected to be in dev coordinates
+*/
+void QX11PaintEngine::updateClipRegion_dev(const QRegion &clipRegion, Qt::ClipOperation op)
+{
+ Q_D(QX11PaintEngine);
+ QRegion sysClip = systemClip();
+ if (op == Qt::NoClip) {
+ d->has_clipping = false;
+ d->crgn = sysClip;
+ if (!sysClip.isEmpty()) {
+ x11SetClipRegion(d->dpy, d->gc, d->gc_brush, d->picture, sysClip);
+ } else {
+ x11ClearClipRegion(d->dpy, d->gc, d->gc_brush, d->picture);
+ }
+ return;
+ }
+
+ switch (op) {
+ case Qt::IntersectClip:
+ if (d->has_clipping) {
+ d->crgn &= clipRegion;
+ break;
+ }
+ // fall through
+ case Qt::ReplaceClip:
+ if (!sysClip.isEmpty())
+ d->crgn = clipRegion.intersected(sysClip);
+ else
+ d->crgn = clipRegion;
+ break;
+ case Qt::UniteClip:
+ d->crgn |= clipRegion;
+ if (!sysClip.isEmpty())
+ d->crgn = d->crgn.intersected(sysClip);
+ break;
+ default:
+ break;
+ }
+ d->has_clipping = true;
+ x11SetClipRegion(d->dpy, d->gc, d->gc_brush, d->picture, d->crgn);
+}
+
+void QX11PaintEngine::updateFont(const QFont &)
+{
+}
+
+Qt::HANDLE QX11PaintEngine::handle() const
+{
+ Q_D(const QX11PaintEngine);
+ Q_ASSERT(isActive());
+ Q_ASSERT(d->hd);
+ return d->hd;
+}
+
+extern void qt_draw_tile(QPaintEngine *, qreal, qreal, qreal, qreal, const QPixmap &,
+ qreal, qreal);
+
+void QX11PaintEngine::drawTiledPixmap(const QRectF &r, const QPixmap &pixmap, const QPointF &p)
+{
+ int x = qRound(r.x());
+ int y = qRound(r.y());
+ int w = qRound(r.width());
+ int h = qRound(r.height());
+ int sx = qRound(p.x());
+ int sy = qRound(p.y());
+
+ bool mono_src = pixmap.depth() == 1;
+ Q_D(QX11PaintEngine);
+
+ if ((d->xinfo && d->xinfo->screen() != pixmap.x11Info().screen())
+ || (pixmap.x11Info().screen() != DefaultScreen(X11->display))) {
+ QPixmap* p = const_cast<QPixmap *>(&pixmap);
+ p->x11SetScreen(d->xinfo ? d->xinfo->screen() : DefaultScreen(X11->display));
+ }
+
+ QPixmap::x11SetDefaultScreen(pixmap.x11Info().screen());
+
+#ifndef QT_NO_XRENDER
+ if (X11->use_xrender && d->picture && pixmap.x11PictureHandle()) {
+#if 0
+ // ### Qt 5: enable this
+ XRenderPictureAttributes attrs;
+ attrs.repeat = true;
+ XRenderChangePicture(d->dpy, pixmap.x11PictureHandle(), CPRepeat, &attrs);
+
+ if (mono_src) {
+ qt_render_bitmap(d->dpy, d->scrn, pixmap.x11PictureHandle(), d->picture,
+ sx, sy, x, y, w, h, d->cpen);
+ } else {
+ XRenderComposite(d->dpy, d->composition_mode,
+ pixmap.x11PictureHandle(), XNone, d->picture,
+ sx, sy, 0, 0, x, y, w, h);
+ }
+#else
+ const int numTiles = (w / pixmap.width()) * (h / pixmap.height());
+ if (numTiles < 100) {
+ // this is essentially qt_draw_tile(), inlined for
+ // the XRenderComposite call
+ int yPos, xPos, drawH, drawW, yOff, xOff;
+ yPos = y;
+ yOff = sy;
+ while(yPos < y + h) {
+ drawH = pixmap.height() - yOff; // Cropping first row
+ if (yPos + drawH > y + h) // Cropping last row
+ drawH = y + h - yPos;
+ xPos = x;
+ xOff = sx;
+ while(xPos < x + w) {
+ drawW = pixmap.width() - xOff; // Cropping first column
+ if (xPos + drawW > x + w) // Cropping last column
+ drawW = x + w - xPos;
+ if (mono_src) {
+ qt_render_bitmap(d->dpy, d->scrn, pixmap.x11PictureHandle(), d->picture,
+ xOff, yOff, xPos, yPos, drawW, drawH, d->cpen);
+ } else {
+ XRenderComposite(d->dpy, d->composition_mode,
+ pixmap.x11PictureHandle(), XNone, d->picture,
+ xOff, yOff, 0, 0, xPos, yPos, drawW, drawH);
+ }
+ xPos += drawW;
+ xOff = 0;
+ }
+ yPos += drawH;
+ yOff = 0;
+ }
+ } else {
+ w = qMin(w, d->pdev->width() - x);
+ h = qMin(h, d->pdev->height() - y);
+ if (w <= 0 || h <= 0)
+ return;
+
+ const int pw = w + sx;
+ const int ph = h + sy;
+ QPixmap pm(pw, ph);
+ if (pixmap.hasAlpha() || mono_src)
+ pm.fill(Qt::transparent);
+
+ const int mode = pixmap.hasAlpha() ? PictOpOver : PictOpSrc;
+ const ::Picture pmPicture = pm.x11PictureHandle();
+
+ // first tile
+ XRenderComposite(d->dpy, mode,
+ pixmap.x11PictureHandle(), XNone, pmPicture,
+ 0, 0, 0, 0, 0, 0, qMin(pw, pixmap.width()), qMin(ph, pixmap.height()));
+
+ // first row of tiles
+ int xPos = pixmap.width();
+ const int sh = qMin(ph, pixmap.height());
+ while (xPos < pw) {
+ const int sw = qMin(xPos, pw - xPos);
+ XRenderComposite(d->dpy, mode,
+ pmPicture, XNone, pmPicture,
+ 0, 0, 0, 0, xPos, 0, sw, sh);
+ xPos *= 2;
+ }
+
+ // remaining rows
+ int yPos = pixmap.height();
+ const int sw = pw;
+ while (yPos < ph) {
+ const int sh = qMin(yPos, ph - yPos);
+ XRenderComposite(d->dpy, mode,
+ pmPicture, XNone, pmPicture,
+ 0, 0, 0, 0, 0, yPos, sw, sh);
+ yPos *= 2;
+ }
+
+ // composite
+ if (mono_src)
+ qt_render_bitmap(d->dpy, d->scrn, pmPicture, d->picture,
+ sx, sy, x, y, w, h, d->cpen);
+ else
+ XRenderComposite(d->dpy, d->composition_mode,
+ pmPicture, XNone, d->picture,
+ sx, sy, 0, 0, x, y, w, h);
+ }
+#endif
+ } else
+#endif // !QT_NO_XRENDER
+ if (pixmap.depth() > 1 && !static_cast<QX11PixmapData*>(pixmap.data)->x11_mask) {
+ XSetTile(d->dpy, d->gc, pixmap.handle());
+ XSetFillStyle(d->dpy, d->gc, FillTiled);
+ XSetTSOrigin(d->dpy, d->gc, x-sx, y-sy);
+ XFillRectangle(d->dpy, d->hd, d->gc, x, y, w, h);
+ XSetTSOrigin(d->dpy, d->gc, 0, 0);
+ XSetFillStyle(d->dpy, d->gc, FillSolid);
+ } else {
+ qt_draw_tile(this, x, y, w, h, pixmap, sx, sy);
+ }
+}
+
+void QX11PaintEngine::drawTextItem(const QPointF &p, const QTextItem &textItem)
+{
+ const QTextItemInt &ti = static_cast<const QTextItemInt &>(textItem);
+
+ switch(ti.fontEngine->type()) {
+ case QFontEngine::TestFontEngine:
+ case QFontEngine::Box:
+ d_func()->drawBoxTextItem(p, ti);
+ break;
+ case QFontEngine::XLFD:
+ drawXLFD(p, ti);
+ break;
+#ifndef QT_NO_FONTCONFIG
+ case QFontEngine::Freetype:
+ drawFreetype(p, ti);
+ break;
+#endif
+ default:
+ Q_ASSERT(false);
+ }
+}
+
+void QX11PaintEngine::drawXLFD(const QPointF &p, const QTextItemInt &ti)
+{
+ Q_D(QX11PaintEngine);
+
+ if (d->txop > QTransform::TxTranslate) {
+ // XServer or font don't support server side transformations, need to do it by hand
+ QPaintEngine::drawTextItem(p, ti);
+ return;
+ }
+
+ if (!ti.glyphs.numGlyphs)
+ return;
+
+ QVarLengthArray<QFixedPoint> positions;
+ QVarLengthArray<glyph_t> glyphs;
+ QTransform matrix = d->matrix;
+ matrix.translate(p.x(), p.y());
+ ti.fontEngine->getGlyphPositions(ti.glyphs, matrix, ti.flags, glyphs, positions);
+ if (glyphs.size() == 0)
+ return;
+
+ QFontEngineXLFD *xlfd = static_cast<QFontEngineXLFD *>(ti.fontEngine);
+ Qt::HANDLE font_id = xlfd->fontStruct()->fid;
+
+ XSetFont(d->dpy, d->gc, font_id);
+
+ const QFixed offs = QFixed::fromReal(aliasedCoordinateDelta);
+ for (int i = 0; i < glyphs.size(); i++) {
+ int xp = qRound(positions[i].x + offs);
+ int yp = qRound(positions[i].y + offs);
+ if (xp < SHRT_MAX && xp > SHRT_MIN && yp > SHRT_MIN && yp < SHRT_MAX) {
+ XChar2b ch;
+ ch.byte1 = glyphs[i] >> 8;
+ ch.byte2 = glyphs[i] & 0xff;
+ XDrawString16(d->dpy, d->hd, d->gc, xp, yp, &ch, 1);
+ }
+ }
+}
+
+#ifndef QT_NO_FONTCONFIG
+static QPainterPath path_for_glyphs(const QVarLengthArray<glyph_t> &glyphs,
+ const QVarLengthArray<QFixedPoint> &positions,
+ const QFontEngineFT *ft)
+{
+ QPainterPath path;
+ path.setFillRule(Qt::WindingFill);
+ ft->lockFace();
+ int i = 0;
+ while (i < glyphs.size()) {
+ QFontEngineFT::Glyph *glyph = ft->loadGlyph(glyphs[i], QFontEngineFT::Format_Mono);
+ // #### fix case where we don't get a glyph
+ if (!glyph)
+ break;
+
+ Q_ASSERT(glyph->format == QFontEngineFT::Format_Mono);
+ int n = 0;
+ int h = glyph->height;
+ int xp = qRound(positions[i].x);
+ int yp = qRound(positions[i].y);
+
+ xp += glyph->x;
+ yp += -glyph->y + glyph->height;
+ int pitch = ((glyph->width + 31) & ~31) >> 3;
+
+ uchar *src = glyph->data;
+ while (h--) {
+ for (int x = 0; x < glyph->width; ++x) {
+ bool set = src[x >> 3] & (0x80 >> (x & 7));
+ if (set) {
+ QRect r(xp + x, yp - h, 1, 1);
+ while (x < glyph->width-1 && src[(x+1) >> 3] & (0x80 >> ((x+1) & 7))) {
+ ++x;
+ r.setRight(r.right()+1);
+ }
+
+ path.addRect(r);
+ ++n;
+ }
+ }
+ src += pitch;
+ }
+ ++i;
+ }
+ ft->unlockFace();
+ return path;
+}
+
+void QX11PaintEngine::drawFreetype(const QPointF &p, const QTextItemInt &ti)
+{
+ Q_D(QX11PaintEngine);
+ if (!ti.glyphs.numGlyphs)
+ return;
+
+ QFontEngineX11FT *ft = static_cast<QFontEngineX11FT *>(ti.fontEngine);
+
+ if (!d->cpen.isSolid()) {
+ QPaintEngine::drawTextItem(p, ti);
+ return;
+ }
+
+ const bool xrenderPath = (X11->use_xrender
+ && !(d->pdev->devType() == QInternal::Pixmap
+ && static_cast<const QPixmap *>(d->pdev)->data->pixelType() == QPixmapData::BitmapType));
+
+ QVarLengthArray<QFixedPoint> positions;
+ QVarLengthArray<glyph_t> glyphs;
+ QTransform matrix;
+
+ if (xrenderPath)
+ matrix = d->matrix;
+ matrix.translate(p.x(), p.y());
+ ft->getGlyphPositions(ti.glyphs, matrix, ti.flags, glyphs, positions);
+ if (glyphs.count() == 0)
+ return;
+
+#ifndef QT_NO_XRENDER
+ QFontEngineFT::QGlyphSet *set = ft->defaultGlyphs();
+ if (d->txop >= QTransform::TxScale && xrenderPath)
+ set = ft->loadTransformedGlyphSet(d->matrix);
+
+ if (!set || set->outline_drawing
+ || !ft->loadGlyphs(set, glyphs.data(), glyphs.size(), QFontEngineFT::Format_Render))
+ {
+ QPaintEngine::drawTextItem(p, ti);
+ return;
+ }
+
+ if (xrenderPath) {
+ GlyphSet glyphSet = set->id;
+ const QColor &pen = d->cpen.color();
+ ::Picture src = X11->getSolidFill(d->scrn, pen);
+ XRenderPictFormat *maskFormat = XRenderFindStandardFormat(X11->display, ft->xglyph_format);
+
+ enum { t_min = SHRT_MIN, t_max = SHRT_MAX };
+
+ int i = 0;
+ for (; i < glyphs.size()
+ && (positions[i].x < t_min || positions[i].x > t_max
+ || positions[i].y < t_min || positions[i].y > t_max);
+ ++i)
+ ;
+
+ if (i >= glyphs.size())
+ return;
+ ++i;
+
+ QFixed xp = positions[i - 1].x;
+ QFixed yp = positions[i - 1].y;
+ QFixed offs = QFixed::fromReal(aliasedCoordinateDelta);
+
+ XGlyphElt32 elt;
+ elt.glyphset = glyphSet;
+ elt.chars = &glyphs[i - 1];
+ elt.nchars = 1;
+ elt.xOff = qRound(xp + offs);
+ elt.yOff = qRound(yp + offs);
+ for (; i < glyphs.size(); ++i) {
+ if (positions[i].x < t_min || positions[i].x > t_max
+ || positions[i].y < t_min || positions[i].y > t_max) {
+ break;
+ }
+ QFontEngineFT::Glyph *g = ft->cachedGlyph(glyphs[i - 1]);
+ if (g
+ && positions[i].x == xp + g->advance
+ && positions[i].y == yp
+ && elt.nchars < 253 // don't draw more than 253 characters as some X servers
+ // hang with it
+ ) {
+ elt.nchars++;
+ xp += g->advance;
+ } else {
+ xp = positions[i].x;
+ yp = positions[i].y;
+
+ XRenderCompositeText32(X11->display, PictOpOver, src, d->picture,
+ maskFormat, 0, 0, 0, 0,
+ &elt, 1);
+ elt.chars = &glyphs[i];
+ elt.nchars = 1;
+ elt.xOff = qRound(xp + offs);
+ elt.yOff = qRound(yp + offs);
+ }
+ }
+ XRenderCompositeText32(X11->display, PictOpOver, src, d->picture,
+ maskFormat, 0, 0, 0, 0,
+ &elt, 1);
+
+ return;
+
+ }
+#endif
+
+ QPainterPath path = path_for_glyphs(glyphs, positions, ft);
+ if (path.elementCount() <= 1)
+ return;
+ Q_ASSERT((path.elementCount() % 5) == 0);
+ if (d->txop >= QTransform::TxScale) {
+ painter()->save();
+ painter()->setBrush(d->cpen.brush());
+ painter()->setPen(Qt::NoPen);
+ painter()->drawPath(path);
+ painter()->restore();
+ return;
+ }
+
+ const int rectcount = 256;
+ XRectangle rects[rectcount];
+ int num_rects = 0;
+
+ for (int i=0; i < path.elementCount(); i+=5) {
+ int x = qRound(path.elementAt(i).x);
+ int y = qRound(path.elementAt(i).y);
+ int w = qRound(path.elementAt(i+1).x) - x;
+ int h = qRound(path.elementAt(i+2).y) - y;
+ rects[num_rects].x = x + qRound(d->matrix.dx());
+ rects[num_rects].y = y + qRound(d->matrix.dy());
+ rects[num_rects].width = w;
+ rects[num_rects].height = h;
+ ++num_rects;
+ if (num_rects == rectcount) {
+ XFillRectangles(d->dpy, d->hd, d->gc, rects, num_rects);
+ num_rects = 0;
+ }
+ }
+ if (num_rects > 0)
+ XFillRectangles(d->dpy, d->hd, d->gc, rects, num_rects);
+
+}
+#endif // !QT_NO_XRENDER
+
+QT_END_NAMESPACE
diff --git a/src/gui/painting/qpaintengine_x11_p.h b/src/gui/painting/qpaintengine_x11_p.h
new file mode 100644
index 0000000000..f277eeb5e9
--- /dev/null
+++ b/src/gui/painting/qpaintengine_x11_p.h
@@ -0,0 +1,245 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QPAINTENGINE_X11_P_H
+#define QPAINTENGINE_X11_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 "QtGui/qpaintengine.h"
+#include "QtGui/qregion.h"
+#include "QtGui/qpen.h"
+#include "QtCore/qpoint.h"
+#include "private/qpaintengine_p.h"
+#include "private/qpainter_p.h"
+#include "private/qpolygonclipper_p.h"
+
+typedef unsigned long Picture;
+
+QT_BEGIN_NAMESPACE
+
+class QX11PaintEnginePrivate;
+class QFontEngineFT;
+class QXRenderTessellator;
+
+struct qt_float_point
+{
+ qreal x, y;
+};
+
+class QX11PaintEngine : public QPaintEngine
+{
+ Q_DECLARE_PRIVATE(QX11PaintEngine)
+public:
+ QX11PaintEngine();
+ ~QX11PaintEngine();
+
+ bool begin(QPaintDevice *pdev);
+ bool end();
+
+ void updateState(const QPaintEngineState &state);
+
+ void updatePen(const QPen &pen);
+ void updateBrush(const QBrush &brush, const QPointF &pt);
+ void updateRenderHints(QPainter::RenderHints hints);
+ void updateFont(const QFont &font);
+ void updateMatrix(const QTransform &matrix);
+ void updateClipRegion_dev(const QRegion &region, Qt::ClipOperation op);
+
+ void drawLines(const QLine *lines, int lineCount);
+ void drawLines(const QLineF *lines, int lineCount);
+
+ void drawRects(const QRect *rects, int rectCount);
+ void drawRects(const QRectF *rects, int rectCount);
+
+ void drawPoints(const QPoint *points, int pointCount);
+ void drawPoints(const QPointF *points, int pointCount);
+
+ void drawEllipse(const QRect &r);
+ void drawEllipse(const QRectF &r);
+
+ virtual void drawPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode);
+ inline void drawPolygon(const QPoint *points, int pointCount, PolygonDrawMode mode)
+ { QPaintEngine::drawPolygon(points, pointCount, mode); }
+
+ void drawPixmap(const QRectF &r, const QPixmap &pm, const QRectF &sr);
+ void drawTiledPixmap(const QRectF &r, const QPixmap &pixmap, const QPointF &s);
+ void drawPath(const QPainterPath &path);
+ void drawTextItem(const QPointF &p, const QTextItem &textItem);
+ void drawImage(const QRectF &r, const QImage &img, const QRectF &sr,
+ Qt::ImageConversionFlags flags = Qt::AutoColor);
+
+ virtual Qt::HANDLE handle() const;
+ inline Type type() const { return QPaintEngine::X11; }
+
+ QPainter::RenderHints supportedRenderHints() const;
+
+protected:
+ QX11PaintEngine(QX11PaintEnginePrivate &dptr);
+
+ void drawXLFD(const QPointF &p, const QTextItemInt &si);
+#ifndef QT_NO_FONTCONFIG
+ void drawFreetype(const QPointF &p, const QTextItemInt &si);
+#endif
+
+ friend class QPixmap;
+ friend class QFontEngineBox;
+ friend Q_GUI_EXPORT GC qt_x11_get_pen_gc(QPainter *);
+ friend Q_GUI_EXPORT GC qt_x11_get_brush_gc(QPainter *);
+
+private:
+ Q_DISABLE_COPY(QX11PaintEngine)
+};
+
+class QX11PaintEnginePrivate : public QPaintEnginePrivate
+{
+ Q_DECLARE_PUBLIC(QX11PaintEngine)
+public:
+ QX11PaintEnginePrivate()
+ {
+ scrn = -1;
+ hd = 0;
+ picture = 0;
+ gc = gc_brush = 0;
+ dpy = 0;
+ xinfo = 0;
+ txop = QTransform::TxNone;
+ has_clipping = false;
+ render_hints = 0;
+#ifndef QT_NO_XRENDER
+ tessellator = 0;
+#endif
+ }
+ enum GCMode {
+ PenGC,
+ BrushGC
+ };
+
+ void init();
+ void fillPolygon_translated(const QPointF *points, int pointCount, GCMode gcMode,
+ QPaintEngine::PolygonDrawMode mode);
+ void fillPolygon_dev(const QPointF *points, int pointCount, GCMode gcMode,
+ QPaintEngine::PolygonDrawMode mode);
+ void fillPath(const QPainterPath &path, GCMode gcmode, bool transform);
+ void strokePolygon_dev(const QPointF *points, int pointCount, bool close);
+ void strokePolygon_translated(const QPointF *points, int pointCount, bool close);
+ void setupAdaptedOrigin(const QPoint &p);
+ void resetAdaptedOrigin();
+ void decidePathFallback() {
+ use_path_fallback = has_alpha_brush
+ || has_alpha_pen
+ || has_custom_pen
+ || has_complex_xform
+ || (render_hints & QPainter::Antialiasing);
+ }
+ void decideCoordAdjust() {
+ adjust_coords = !(render_hints & QPainter::Antialiasing)
+ && (has_alpha_pen
+ || (has_alpha_brush && has_pen && !has_alpha_pen)
+ || (cpen.style() > Qt::SolidLine));
+ }
+ void clipPolygon_dev(const QPolygonF &poly, QPolygonF *clipped_poly);
+ void systemStateChanged();
+
+ Display *dpy;
+ int scrn;
+ int pdev_depth;
+ Qt::HANDLE hd;
+ QPixmap brush_pm;
+#if !defined (QT_NO_XRENDER)
+ Qt::HANDLE picture;
+ Qt::HANDLE current_brush;
+ QPixmap bitmap_texture;
+ int composition_mode;
+#else
+ Qt::HANDLE picture;
+#endif
+ GC gc;
+ GC gc_brush;
+
+ QPen cpen;
+ QBrush cbrush;
+ QRegion crgn;
+ QTransform matrix;
+ qreal opacity;
+
+ uint has_complex_xform : 1;
+ uint has_scaling_xform : 1;
+ uint has_non_scaling_xform : 1;
+ uint has_custom_pen : 1;
+ uint use_path_fallback : 1;
+ uint adjust_coords : 1;
+ uint has_clipping : 1;
+ uint adapted_brush_origin : 1;
+ uint adapted_pen_origin : 1;
+ uint has_pen : 1;
+ uint has_brush : 1;
+ uint has_texture : 1;
+ uint has_alpha_texture : 1;
+ uint has_pattern : 1;
+ uint has_alpha_pen : 1;
+ uint has_alpha_brush : 1;
+ uint render_hints;
+
+ const QX11Info *xinfo;
+ QPointF bg_origin;
+ QTransform::TransformationType txop;
+ qreal xform_scale;
+ QPolygonClipper<qt_float_point, qt_float_point, float> polygonClipper;
+
+ int xlibMaxLinePoints;
+#ifndef QT_NO_XRENDER
+ QXRenderTessellator *tessellator;
+#endif
+};
+
+QT_END_NAMESPACE
+
+#endif // QPAINTENGINE_X11_P_H
diff --git a/src/gui/painting/qpaintengineex.cpp b/src/gui/painting/qpaintengineex.cpp
new file mode 100644
index 0000000000..8eaad60811
--- /dev/null
+++ b/src/gui/painting/qpaintengineex.cpp
@@ -0,0 +1,804 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qpaintengineex_p.h"
+#include "qpainter_p.h"
+#include "qstroker_p.h"
+#include <private/qpainterpath_p.h>
+
+#include <qvarlengtharray.h>
+#include <qdebug.h>
+
+
+QT_BEGIN_NAMESPACE
+
+/*******************************************************************************
+ *
+ * class QVectorPath
+ *
+ */
+
+const QRealRect &QVectorPath::controlPointRect() const
+{
+ if (m_hints & ControlPointRect)
+ return m_cp_rect;
+
+ if (m_count == 0) {
+ m_cp_rect.x1 = m_cp_rect.x2 = m_cp_rect.y1 = m_cp_rect.y2 = 0;
+ m_hints |= ControlPointRect;
+ return m_cp_rect;
+ }
+ Q_ASSERT(m_points && m_count > 0);
+
+ const qreal *pts = m_points;
+ m_cp_rect.x1 = m_cp_rect.x2 = *pts;
+ ++pts;
+ m_cp_rect.y1 = m_cp_rect.y2 = *pts;
+ ++pts;
+
+ const qreal *epts = m_points + (m_count << 1);
+ while (pts < epts) {
+ qreal x = *pts;
+ if (x < m_cp_rect.x1) m_cp_rect.x1 = x;
+ else if (x > m_cp_rect.x2) m_cp_rect.x2 = x;
+ ++pts;
+
+ qreal y = *pts;
+ if (y < m_cp_rect.y1) m_cp_rect.y1 = y;
+ else if (y > m_cp_rect.y2) m_cp_rect.y2 = y;
+ ++pts;
+ }
+
+ m_hints |= ControlPointRect;
+ return m_cp_rect;
+}
+
+const QVectorPath &qtVectorPathForPath(const QPainterPath &path)
+{
+ Q_ASSERT(path.d_func());
+ return path.d_func()->vectorPath();
+}
+
+#ifndef QT_NO_DEBUG_STREAM
+QDebug Q_GUI_EXPORT &operator<<(QDebug &s, const QVectorPath &path)
+{
+ QRealRect vectorPathBounds = path.controlPointRect();
+ QRectF rf(vectorPathBounds.x1, vectorPathBounds.y1,
+ vectorPathBounds.x2 - vectorPathBounds.x1, vectorPathBounds.y2 - vectorPathBounds.y1);
+ s << "QVectorPath(size:" << path.elementCount()
+ << " hints:" << hex << path.hints()
+ << rf << ")";
+ return s;
+}
+#endif
+
+/*******************************************************************************
+ *
+ * class QPaintEngineExPrivate:
+ *
+ */
+
+
+struct StrokeHandler {
+ QDataBuffer<qreal> pts;
+ QDataBuffer<QPainterPath::ElementType> types;
+};
+
+
+QPaintEngineExPrivate::QPaintEngineExPrivate()
+ : dasher(&stroker),
+ strokeHandler(0),
+ activeStroker(0),
+ strokerPen(Qt::NoPen)
+{
+}
+
+
+QPaintEngineExPrivate::~QPaintEngineExPrivate()
+{
+ delete strokeHandler;
+}
+
+
+
+/*******************************************************************************
+ *
+ * class QPaintEngineEx:
+ *
+ */
+
+static QPainterPath::ElementType qpaintengineex_ellipse_types[] = {
+ QPainterPath::MoveToElement,
+ QPainterPath::CurveToElement,
+ QPainterPath::CurveToDataElement,
+ QPainterPath::CurveToDataElement,
+
+ QPainterPath::CurveToElement,
+ QPainterPath::CurveToDataElement,
+ QPainterPath::CurveToDataElement,
+
+ QPainterPath::CurveToElement,
+ QPainterPath::CurveToDataElement,
+ QPainterPath::CurveToDataElement,
+
+ QPainterPath::CurveToElement,
+ QPainterPath::CurveToDataElement,
+ QPainterPath::CurveToDataElement
+};
+
+static QPainterPath::ElementType qpaintengineex_line_types_16[] = {
+ QPainterPath::MoveToElement, QPainterPath::LineToElement,
+ QPainterPath::MoveToElement, QPainterPath::LineToElement,
+ QPainterPath::MoveToElement, QPainterPath::LineToElement,
+ QPainterPath::MoveToElement, QPainterPath::LineToElement,
+ QPainterPath::MoveToElement, QPainterPath::LineToElement,
+ QPainterPath::MoveToElement, QPainterPath::LineToElement,
+ QPainterPath::MoveToElement, QPainterPath::LineToElement,
+ QPainterPath::MoveToElement, QPainterPath::LineToElement,
+ QPainterPath::MoveToElement, QPainterPath::LineToElement,
+ QPainterPath::MoveToElement, QPainterPath::LineToElement,
+ QPainterPath::MoveToElement, QPainterPath::LineToElement,
+ QPainterPath::MoveToElement, QPainterPath::LineToElement,
+ QPainterPath::MoveToElement, QPainterPath::LineToElement,
+ QPainterPath::MoveToElement, QPainterPath::LineToElement,
+ QPainterPath::MoveToElement, QPainterPath::LineToElement,
+ QPainterPath::MoveToElement, QPainterPath::LineToElement
+};
+
+static QPainterPath::ElementType qpaintengineex_rect4_types_32[] = {
+ QPainterPath::MoveToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, // 1
+ QPainterPath::MoveToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, // 2
+ QPainterPath::MoveToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, // 3
+ QPainterPath::MoveToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, // 4
+ QPainterPath::MoveToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, // 5
+ QPainterPath::MoveToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, // 6
+ QPainterPath::MoveToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, // 7
+ QPainterPath::MoveToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, // 8
+ QPainterPath::MoveToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, // 9
+ QPainterPath::MoveToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, // 10
+ QPainterPath::MoveToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, // 11
+ QPainterPath::MoveToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, // 12
+ QPainterPath::MoveToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, // 13
+ QPainterPath::MoveToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, // 14
+ QPainterPath::MoveToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, // 15
+ QPainterPath::MoveToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, // 16
+ QPainterPath::MoveToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, // 17
+ QPainterPath::MoveToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, // 18
+ QPainterPath::MoveToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, // 19
+ QPainterPath::MoveToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, // 20
+ QPainterPath::MoveToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, // 21
+ QPainterPath::MoveToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, // 22
+ QPainterPath::MoveToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, // 23
+ QPainterPath::MoveToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, // 24
+ QPainterPath::MoveToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, // 25
+ QPainterPath::MoveToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, // 26
+ QPainterPath::MoveToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, // 27
+ QPainterPath::MoveToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, // 28
+ QPainterPath::MoveToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, // 29
+ QPainterPath::MoveToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, // 30
+ QPainterPath::MoveToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, // 31
+ QPainterPath::MoveToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, // 32
+};
+
+static void qpaintengineex_moveTo(qreal x, qreal y, void *data) {
+ ((StrokeHandler *) data)->pts.add(x);
+ ((StrokeHandler *) data)->pts.add(y);
+ ((StrokeHandler *) data)->types.add(QPainterPath::MoveToElement);
+}
+
+static void qpaintengineex_lineTo(qreal x, qreal y, void *data) {
+ ((StrokeHandler *) data)->pts.add(x);
+ ((StrokeHandler *) data)->pts.add(y);
+ ((StrokeHandler *) data)->types.add(QPainterPath::LineToElement);
+}
+
+static void qpaintengineex_cubicTo(qreal c1x, qreal c1y, qreal c2x, qreal c2y, qreal ex, qreal ey, void *data) {
+ ((StrokeHandler *) data)->pts.add(c1x);
+ ((StrokeHandler *) data)->pts.add(c1y);
+ ((StrokeHandler *) data)->types.add(QPainterPath::CurveToElement);
+
+ ((StrokeHandler *) data)->pts.add(c2x);
+ ((StrokeHandler *) data)->pts.add(c2y);
+ ((StrokeHandler *) data)->types.add(QPainterPath::CurveToDataElement);
+
+ ((StrokeHandler *) data)->pts.add(ex);
+ ((StrokeHandler *) data)->pts.add(ey);
+ ((StrokeHandler *) data)->types.add(QPainterPath::CurveToDataElement);
+}
+
+QPaintEngineEx::QPaintEngineEx(QPaintEngineExPrivate &data)
+ : QPaintEngine(data, AllFeatures)
+{
+ extended = true;
+}
+
+
+QPainterState *QPaintEngineEx::createState(QPainterState *orig) const
+{
+ if (!orig)
+ return new QPainterState;
+ return new QPainterState(orig);
+}
+
+extern bool qt_scaleForTransform(const QTransform &transform, qreal *scale); // qtransform.cpp
+
+void QPaintEngineEx::stroke(const QVectorPath &path, const QPen &pen)
+{
+#ifdef QT_DEBUG_DRAW
+ qDebug() << "QPaintEngineEx::stroke()" << pen;
+#endif
+
+ Q_D(QPaintEngineEx);
+
+ if (path.isEmpty())
+ return;
+
+ if (!d->strokeHandler) {
+ d->strokeHandler = new StrokeHandler;
+ d->stroker.setMoveToHook(qpaintengineex_moveTo);
+ d->stroker.setLineToHook(qpaintengineex_lineTo);
+ d->stroker.setCubicToHook(qpaintengineex_cubicTo);
+ }
+
+ if (!qpen_fast_equals(pen, d->strokerPen)) {
+ d->strokerPen = pen;
+ d->stroker.setJoinStyle(pen.joinStyle());
+ d->stroker.setCapStyle(pen.capStyle());
+ d->stroker.setMiterLimit(pen.miterLimit());
+ qreal penWidth = pen.widthF();
+ if (penWidth == 0)
+ d->stroker.setStrokeWidth(1);
+ else
+ d->stroker.setStrokeWidth(penWidth);
+
+ Qt::PenStyle style = pen.style();
+ if (style == Qt::SolidLine) {
+ d->activeStroker = &d->stroker;
+ } else if (style == Qt::NoPen) {
+ d->activeStroker = 0;
+ } else {
+ // ### re-enable...
+// if (pen.isCosmetic()) {
+// d->dashStroker->setClipRect(d->deviceRect);
+// } else {
+// QRectF clipRect = s->matrix.inverted().mapRect(QRectF(d->deviceRect));
+// d->dashStroker->setClipRect(clipRect);
+// }
+ d->dasher.setDashPattern(pen.dashPattern());
+ d->dasher.setDashOffset(pen.dashOffset());
+ d->activeStroker = &d->dasher;
+ }
+ }
+
+ if (!d->activeStroker) {
+ return;
+ }
+
+ const QPainterPath::ElementType *types = path.elements();
+ const qreal *points = path.points();
+ int pointCount = path.elementCount();
+
+ const qreal *lastPoint = points + (pointCount<<1);
+
+ d->activeStroker->begin(d->strokeHandler);
+ d->strokeHandler->types.reset();
+ d->strokeHandler->pts.reset();
+
+ // Some engines might decide to optimize for the non-shape hint later on...
+ uint flags = QVectorPath::WindingFill;
+ if (d->stroker.capStyle() == Qt::RoundCap || d->stroker.joinStyle() == Qt::RoundJoin)
+ flags |= QVectorPath::CurvedShapeHint;
+
+ // ### Perspective Xforms are currently not supported...
+ qreal txscale = 1;
+ if (!(pen.isCosmetic() || (qt_scaleForTransform(state()->matrix, &txscale) && txscale != 1))) {
+ // We include cosmetic pens in this case to avoid having to
+ // change the current transform. Normal transformed,
+ // non-cosmetic pens will be transformed as part of fill
+ // later, so they are also covered here..
+ if (types) {
+ while (points < lastPoint) {
+ switch (*types) {
+ case QPainterPath::MoveToElement:
+ d->activeStroker->moveTo(points[0], points[1]);
+ points += 2;
+ ++types;
+ break;
+ case QPainterPath::LineToElement:
+ d->activeStroker->lineTo(points[0], points[1]);
+ points += 2;
+ ++types;
+ break;
+ case QPainterPath::CurveToElement:
+ d->activeStroker->cubicTo(points[0], points[1],
+ points[2], points[3],
+ points[4], points[5]);
+ points += 6;
+ types += 3;
+ flags |= QVectorPath::CurvedShapeHint;
+ break;
+ default:
+ break;
+ }
+ }
+ if (path.hasImplicitClose())
+ d->activeStroker->lineTo(path.points()[0], path.points()[1]);
+
+ } else {
+ d->activeStroker->moveTo(points[0], points[1]);
+ points += 2;
+ ++types;
+ while (points < lastPoint) {
+ d->activeStroker->lineTo(points[0], points[1]);
+ points += 2;
+ ++types;
+ }
+ if (path.hasImplicitClose())
+ d->activeStroker->lineTo(path.points()[0], path.points()[1]);
+ }
+ d->activeStroker->end();
+
+ if (!d->strokeHandler->types.size()) // an empty path...
+ return;
+
+ QVectorPath strokePath(d->strokeHandler->pts.data(),
+ d->strokeHandler->types.size(),
+ d->strokeHandler->types.data(),
+ QVectorPath::WindingFill);
+ fill(strokePath, pen.brush());
+ } else {
+ const qreal strokeWidth = d->stroker.strokeWidth();
+ d->stroker.setStrokeWidth(strokeWidth * txscale);
+ // For cosmetic pens we need a bit of trickery... We to process xform the input points
+ if (types) {
+ while (points < lastPoint) {
+ switch (*types) {
+ case QPainterPath::MoveToElement: {
+ QPointF pt = (*(QPointF *) points) * state()->matrix;
+ d->activeStroker->moveTo(pt.x(), pt.y());
+ points += 2;
+ ++types;
+ break;
+ }
+ case QPainterPath::LineToElement: {
+ QPointF pt = (*(QPointF *) points) * state()->matrix;
+ d->activeStroker->lineTo(pt.x(), pt.y());
+ points += 2;
+ ++types;
+ break;
+ }
+ case QPainterPath::CurveToElement: {
+ QPointF c1 = ((QPointF *) points)[0] * state()->matrix;
+ QPointF c2 = ((QPointF *) points)[1] * state()->matrix;
+ QPointF e = ((QPointF *) points)[2] * state()->matrix;
+ d->activeStroker->cubicTo(c1.x(), c1.y(), c2.x(), c2.y(), e.x(), e.y());
+ points += 6;
+ types += 3;
+ flags |= QVectorPath::CurvedShapeHint;
+ break;
+ }
+ default:
+ break;
+ }
+ }
+ if (path.hasImplicitClose()) {
+ QPointF pt = * ((QPointF *) path.points()) * state()->matrix;
+ d->activeStroker->lineTo(pt.x(), pt.y());
+ }
+
+ } else {
+ QPointF p = ((QPointF *)points)[0] * state()->matrix;
+ d->activeStroker->moveTo(p.x(), p.y());
+ points += 2;
+ ++types;
+ while (points < lastPoint) {
+ QPointF p = ((QPointF *)points)[0] * state()->matrix;
+ d->activeStroker->lineTo(p.x(), p.y());
+ points += 2;
+ ++types;
+ }
+ if (path.hasImplicitClose())
+ d->activeStroker->lineTo(p.x(), p.y());
+ }
+
+ d->activeStroker->end();
+ d->stroker.setStrokeWidth(strokeWidth);
+ QVectorPath strokePath(d->strokeHandler->pts.data(),
+ d->strokeHandler->types.size(),
+ d->strokeHandler->types.data(),
+ QVectorPath::WindingFill);
+
+ QTransform xform = state()->matrix;
+ state()->matrix = QTransform();
+ transformChanged();
+
+ QBrush brush = pen.brush();
+ if (qbrush_style(brush) != Qt::SolidPattern)
+ brush.setTransform(brush.transform() * xform);
+
+ fill(strokePath, brush);
+
+ state()->matrix = xform;
+ transformChanged();
+ }
+}
+
+void QPaintEngineEx::draw(const QVectorPath &path)
+{
+ fill(path, state()->brush);
+ stroke(path, state()->pen);
+}
+
+
+void QPaintEngineEx::clip(const QRect &r, Qt::ClipOperation op)
+{
+ qreal right = r.x() + r.width();
+ qreal bottom = r.y() + r.height();
+ qreal pts[] = { r.x(), r.y(),
+ right, r.y(),
+ right, bottom,
+ r.x(), bottom,
+ r.x(), r.y() };
+ QVectorPath vp(pts, 5, 0, QVectorPath::RectangleHint);
+ clip(vp, op);
+}
+
+void QPaintEngineEx::clip(const QRegion &region, Qt::ClipOperation op)
+{
+ QVector<QRect> rects = region.rects();
+ if (rects.size() <= 32) {
+ qreal pts[2*32*4];
+ int pos = 0;
+ for (QVector<QRect>::const_iterator i = rects.constBegin(); i != rects.constEnd(); ++i) {
+ qreal x1 = i->x();
+ qreal y1 = i->y();
+ qreal x2 = i->x() + i->width();
+ qreal y2 = i->y() + i->height();
+
+ pts[pos++] = x1;
+ pts[pos++] = y1;
+
+ pts[pos++] = x2;
+ pts[pos++] = y1;
+
+ pts[pos++] = x2;
+ pts[pos++] = y2;
+
+ pts[pos++] = x1;
+ pts[pos++] = y2;
+ }
+ QVectorPath vp(pts, rects.size() * 4, qpaintengineex_rect4_types_32);
+ clip(vp, op);
+ } else {
+ QVarLengthArray<qreal> pts(rects.size() * 2 * 4);
+ QVarLengthArray<QPainterPath::ElementType> types(rects.size() * 4);
+ int ppos = 0;
+ int tpos = 0;
+
+ for (QVector<QRect>::const_iterator i = rects.constBegin(); i != rects.constEnd(); ++i) {
+ qreal x1 = i->x();
+ qreal y1 = i->y();
+ qreal x2 = i->x() + i->width();
+ qreal y2 = i->y() + i->height();
+
+ pts[ppos++] = x1;
+ pts[ppos++] = y1;
+
+ pts[ppos++] = x2;
+ pts[ppos++] = y1;
+
+ pts[ppos++] = x2;
+ pts[ppos++] = y2;
+
+ pts[ppos++] = x1;
+ pts[ppos++] = y2;
+
+ types[tpos++] = QPainterPath::MoveToElement;
+ types[tpos++] = QPainterPath::LineToElement;
+ types[tpos++] = QPainterPath::LineToElement;
+ types[tpos++] = QPainterPath::LineToElement;
+ }
+
+ QVectorPath vp(pts.data(), rects.size() * 4, types.data());
+ clip(vp, op);
+ }
+
+}
+
+void QPaintEngineEx::clip(const QPainterPath &path, Qt::ClipOperation op)
+{
+ if (path.isEmpty()) {
+ QVectorPath vp(0, 0);
+ clip(vp, op);
+ } else {
+ clip(qtVectorPathForPath(path), op);
+ }
+}
+
+void QPaintEngineEx::fillRect(const QRectF &r, const QBrush &brush)
+{
+ qreal pts[] = { r.x(), r.y(), r.x() + r.width(), r.y(),
+ r.x() + r.width(), r.y() + r.height(), r.x(), r.y() + r.height() };
+ QVectorPath vp(pts, 4, 0, QVectorPath::RectangleHint);
+ fill(vp, brush);
+}
+
+void QPaintEngineEx::fillRect(const QRectF &r, const QColor &color)
+{
+ fillRect(r, QBrush(color));
+}
+
+void QPaintEngineEx::drawRects(const QRect *rects, int rectCount)
+{
+ for (int i=0; i<rectCount; ++i) {
+ const QRect &r = rects[i];
+ // ### Is there a one off here?
+ qreal right = r.x() + r.width();
+ qreal bottom = r.y() + r.height();
+ qreal pts[] = { r.x(), r.y(),
+ right, r.y(),
+ right, bottom,
+ r.x(), bottom,
+ r.x(), r.y() };
+ QVectorPath vp(pts, 5, 0, QVectorPath::RectangleHint);
+ draw(vp);
+ }
+}
+
+void QPaintEngineEx::drawRects(const QRectF *rects, int rectCount)
+{
+ for (int i=0; i<rectCount; ++i) {
+ const QRectF &r = rects[i];
+ qreal right = r.x() + r.width();
+ qreal bottom = r.y() + r.height();
+ qreal pts[] = { r.x(), r.y(),
+ right, r.y(),
+ right, bottom,
+ r.x(), bottom,
+ r.x(), r.y() };
+ QVectorPath vp(pts, 5, 0, QVectorPath::RectangleHint);
+ draw(vp);
+ }
+}
+
+void QPaintEngineEx::drawLines(const QLine *lines, int lineCount)
+{
+ int elementCount = lineCount << 1;
+ while (elementCount > 0) {
+ int count = qMin(elementCount, 32);
+
+ qreal pts[64];
+ int count2 = count<<1;
+#ifdef Q_WS_MAC
+ for (int i=0; i<count2; i+=2) {
+ pts[i] = ((int *) lines)[i+1];
+ pts[i+1] = ((int *) lines)[i];
+ }
+#else
+ for (int i=0; i<count2; ++i)
+ pts[i] = ((int *) lines)[i];
+#endif
+
+ QVectorPath path(pts, count, qpaintengineex_line_types_16, QVectorPath::LinesHint);
+ stroke(path, state()->pen);
+
+ elementCount -= 32;
+ lines += 16;
+ }
+}
+
+void QPaintEngineEx::drawLines(const QLineF *lines, int lineCount)
+{
+ int elementCount = lineCount << 1;
+ while (elementCount > 0) {
+ int count = qMin(elementCount, 32);
+
+ QVectorPath path((qreal *) lines, count, qpaintengineex_line_types_16,
+ QVectorPath::LinesHint);
+ stroke(path, state()->pen);
+
+ elementCount -= 32;
+ lines += 16;
+ }
+}
+
+void QPaintEngineEx::drawEllipse(const QRectF &r)
+{
+ qreal pts[26]; // QPointF[13] without constructors...
+ union {
+ qreal *ptr;
+ QPointF *points;
+ } x;
+ x.ptr = pts;
+
+ int point_count = 0;
+ x.points[0] = qt_curves_for_arc(r, 0, -360, x.points + 1, &point_count);
+ QVectorPath vp((qreal *) pts, 13, qpaintengineex_ellipse_types, QVectorPath::EllipseHint);
+ draw(vp);
+}
+
+void QPaintEngineEx::drawEllipse(const QRect &r)
+{
+ drawEllipse(QRectF(r));
+}
+
+void QPaintEngineEx::drawPath(const QPainterPath &path)
+{
+ if (!path.isEmpty())
+ draw(qtVectorPathForPath(path));
+}
+
+
+void QPaintEngineEx::drawPoints(const QPointF *points, int pointCount)
+{
+ QPen pen = state()->pen;
+ if (pen.capStyle() == Qt::FlatCap)
+ pen.setCapStyle(Qt::SquareCap);
+
+ if (pen.brush().isOpaque()) {
+ while (pointCount > 0) {
+ int count = qMin(pointCount, 16);
+ qreal pts[64];
+ int oset = -1;
+ for (int i=0; i<count; ++i) {
+ pts[++oset] = points[i].x();
+ pts[++oset] = points[i].y();
+ pts[++oset] = points[i].x() + 0.001;
+ pts[++oset] = points[i].y();
+ }
+ QVectorPath path(pts, count * 2, qpaintengineex_line_types_16, QVectorPath::NonCurvedShapeHint);
+ stroke(path, pen);
+ pointCount -= 16;
+ points += 16;
+ }
+ } else {
+ for (int i=0; i<pointCount; ++i) {
+ qreal pts[] = { points[i].x(), points[i].y(), points[i].x() + 0.001, points[i].y() };
+ QVectorPath path(pts, 2, 0);
+ stroke(path, pen);
+ }
+ }
+}
+
+void QPaintEngineEx::drawPoints(const QPoint *points, int pointCount)
+{
+ QPen pen = state()->pen;
+ if (pen.capStyle() == Qt::FlatCap)
+ pen.setCapStyle(Qt::SquareCap);
+
+ if (pen.brush().isOpaque()) {
+ while (pointCount > 0) {
+ int count = qMin(pointCount, 16);
+ qreal pts[64];
+ int oset = -1;
+ for (int i=0; i<count; ++i) {
+ pts[++oset] = points[i].x();
+ pts[++oset] = points[i].y();
+ pts[++oset] = points[i].x() + 0.001;
+ pts[++oset] = points[i].y();
+ }
+ QVectorPath path(pts, count * 2, qpaintengineex_line_types_16, QVectorPath::NonCurvedShapeHint);
+ stroke(path, pen);
+ pointCount -= 16;
+ points += 16;
+ }
+ } else {
+ for (int i=0; i<pointCount; ++i) {
+ qreal pts[] = { points[i].x(), points[i].y(), points[i].x() + 0.001, points[i].y() };
+ QVectorPath path(pts, 2, 0);
+ stroke(path, pen);
+ }
+ }
+}
+
+
+void QPaintEngineEx::drawPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode)
+{
+ QVectorPath path((qreal *) points, pointCount, 0, QVectorPath::polygonFlags(mode));
+
+ if (mode == PolylineMode)
+ stroke(path, state()->pen);
+ else
+ draw(path);
+}
+
+void QPaintEngineEx::drawPolygon(const QPoint *points, int pointCount, PolygonDrawMode mode)
+{
+ int count = pointCount<<1;
+ QVarLengthArray<qreal> pts(count);
+
+#ifdef Q_WS_MAC
+ for (int i=0; i<count; i+=2) {
+ pts[i] = ((int *) points)[i+1];
+ pts[i+1] = ((int *) points)[i];
+ }
+#else
+ for (int i=0; i<count; ++i)
+ pts[i] = ((int *) points)[i];
+#endif
+
+ QVectorPath path(pts.data(), pointCount, 0, QVectorPath::polygonFlags(mode));
+
+ if (mode == PolylineMode)
+ stroke(path, state()->pen);
+ else
+ draw(path);
+
+}
+
+void QPaintEngineEx::drawPixmap(const QPointF &pos, const QPixmap &pm)
+{
+ drawPixmap(QRectF(pos, pm.size()), pm, pm.rect());
+}
+
+void QPaintEngineEx::drawImage(const QPointF &pos, const QImage &image)
+{
+ drawImage(QRectF(pos, image.size()), image, image.rect());
+}
+
+void QPaintEngineEx::drawTiledPixmap(const QRectF &r, const QPixmap &pixmap, const QPointF &s)
+{
+ QBrush brush(state()->pen.color(), pixmap);
+ QTransform xform;
+ xform.translate(-s.x(), -s.y());
+ brush.setTransform(xform);
+
+ qreal pts[] = { r.x(), r.y(),
+ r.x() + r.width(), r.y(),
+ r.x() + r.width(), r.y() + r.height(),
+ r.x(), r.y() + r.height() };
+
+ QVectorPath path(pts, 4, 0, QVectorPath::RectangleHint);
+ fill(path, brush);
+}
+
+void QPaintEngineEx::setState(QPainterState *s)
+{
+ QPaintEngine::state = s;
+}
+
+
+void QPaintEngineEx::updateState(const QPaintEngineState &)
+{
+ // do nothing...
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/painting/qpaintengineex_p.h b/src/gui/painting/qpaintengineex_p.h
new file mode 100644
index 0000000000..593726c644
--- /dev/null
+++ b/src/gui/painting/qpaintengineex_p.h
@@ -0,0 +1,240 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QPAINTENGINEEX_P_H
+#define QPAINTENGINEEX_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 <QtGui/qpaintengine.h>
+
+#include "qpaintengine_p.h"
+#include "qstroker_p.h"
+#include "qpainter_p.h"
+#include "qvectorpath_p.h"
+
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+class QPainterState;
+class QPaintEngineExPrivate;
+struct StrokeHandler;
+
+
+struct QIntRect {
+ int x1, y1, x2, y2;
+ inline void set(const QRect &r) {
+ x1 = r.x();
+ y1 = r.y();
+ x2 = r.right() + 1;
+ y2 = r.bottom() + 1;
+ // We will assume normalized for later...
+ Q_ASSERT(x2 >= x1);
+ Q_ASSERT(y2 >= y1);
+ }
+};
+
+class QRectVectorPath : public QVectorPath {
+public:
+ inline void set(const QRect &r) {
+ qreal left = r.x();
+ qreal right = r.x() + r.width();
+ qreal top = r.y();
+ qreal bottom = r.y() + r.height();
+ pts[0] = left;
+ pts[1] = top;
+ pts[2] = right;
+ pts[3] = top;
+ pts[4] = right;
+ pts[5] = bottom;
+ pts[6] = left;
+ pts[7] = bottom;
+ }
+
+ inline void set(const QRectF &r) {
+ qreal left = r.x();
+ qreal right = r.x() + r.width();
+ qreal top = r.y();
+ qreal bottom = r.y() + r.height();
+ pts[0] = left;
+ pts[1] = top;
+ pts[2] = right;
+ pts[3] = top;
+ pts[4] = right;
+ pts[5] = bottom;
+ pts[6] = left;
+ pts[7] = bottom;
+ }
+ inline QRectVectorPath(const QRect &r)
+ : QVectorPath(pts, 4, 0, QVectorPath::RectangleHint | QVectorPath::ImplicitClose)
+ {
+ set(r);
+ }
+ inline QRectVectorPath(const QRectF &r)
+ : QVectorPath(pts, 4, 0, QVectorPath::RectangleHint | QVectorPath::ImplicitClose)
+ {
+ set(r);
+ }
+ inline QRectVectorPath()
+ : QVectorPath(pts, 4, 0, QVectorPath::RectangleHint | QVectorPath::ImplicitClose)
+ { }
+
+ qreal pts[8];
+};
+
+
+
+#ifndef QT_NO_DEBUG_STREAM
+QDebug Q_GUI_EXPORT &operator<<(QDebug &, const QVectorPath &path);
+#endif
+
+class Q_GUI_EXPORT QPaintEngineExPrivate : public QPaintEnginePrivate
+{
+public:
+ QPaintEngineExPrivate();
+ ~QPaintEngineExPrivate();
+
+ QStroker stroker;
+ QDashStroker dasher;
+ StrokeHandler *strokeHandler;
+ QStrokerOps *activeStroker;
+ QPen strokerPen;
+};
+
+class QPixmapFilter;
+
+class Q_GUI_EXPORT QPaintEngineEx : public QPaintEngine
+{
+ Q_DECLARE_PRIVATE(QPaintEngineEx)
+public:
+ inline QPaintEngineEx()
+ : QPaintEngine(*new QPaintEngineExPrivate, AllFeatures) { extended = true; }
+
+ virtual QPainterState *createState(QPainterState *orig) const;
+
+ virtual void draw(const QVectorPath &path);
+ virtual void fill(const QVectorPath &path, const QBrush &brush) = 0;
+ virtual void stroke(const QVectorPath &path, const QPen &pen);
+
+ virtual void clip(const QVectorPath &path, Qt::ClipOperation op) = 0;
+ virtual void clip(const QRect &rect, Qt::ClipOperation op);
+ virtual void clip(const QRegion &region, Qt::ClipOperation op);
+ virtual void clip(const QPainterPath &path, Qt::ClipOperation op);
+
+ virtual void clipEnabledChanged() = 0;
+ virtual void penChanged() = 0;
+ virtual void brushChanged() = 0;
+ virtual void brushOriginChanged() = 0;
+ virtual void opacityChanged() = 0;
+ virtual void compositionModeChanged() = 0;
+ virtual void renderHintsChanged() = 0;
+ virtual void transformChanged() = 0;
+
+ virtual void fillRect(const QRectF &rect, const QBrush &brush);
+ virtual void fillRect(const QRectF &rect, const QColor &color);
+
+ virtual void drawRects(const QRect *rects, int rectCount);
+ virtual void drawRects(const QRectF *rects, int rectCount);
+
+ virtual void drawLines(const QLine *lines, int lineCount);
+ virtual void drawLines(const QLineF *lines, int lineCount);
+
+ virtual void drawEllipse(const QRectF &r);
+ virtual void drawEllipse(const QRect &r);
+
+ virtual void drawPath(const QPainterPath &path);
+
+ virtual void drawPoints(const QPointF *points, int pointCount);
+ virtual void drawPoints(const QPoint *points, int pointCount);
+
+ virtual void drawPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode);
+ virtual void drawPolygon(const QPoint *points, int pointCount, PolygonDrawMode mode);
+
+ virtual void drawPixmap(const QRectF &r, const QPixmap &pm, const QRectF &sr) = 0;
+ virtual void drawPixmap(const QPointF &pos, const QPixmap &pm);
+
+ virtual void drawImage(const QRectF &r, const QImage &pm, const QRectF &sr,
+ Qt::ImageConversionFlags flags = Qt::AutoColor) = 0;
+ virtual void drawImage(const QPointF &pos, const QImage &image);
+
+ virtual void drawTiledPixmap(const QRectF &r, const QPixmap &pixmap, const QPointF &s);
+
+ virtual void updateState(const QPaintEngineState &state);
+
+ virtual void setState(QPainterState *s);
+ inline QPainterState *state() { return static_cast<QPainterState *>(QPaintEngine::state); }
+ inline const QPainterState *state() const { return static_cast<const QPainterState *>(QPaintEngine::state); }
+
+ virtual QPixmapFilter *createPixmapFilter(int /*type*/) const { return 0; }
+
+protected:
+ QPaintEngineEx(QPaintEngineExPrivate &data);
+};
+
+
+inline uint QVectorPath::polygonFlags(QPaintEngine::PolygonDrawMode mode) {
+ switch (mode) {
+ case QPaintEngine::ConvexMode: return ConvexPolygonHint | ImplicitClose;
+ case QPaintEngine::OddEvenMode: return NonCurvedShapeHint | OddEvenFill | ImplicitClose;
+ case QPaintEngine::WindingMode: return NonCurvedShapeHint | WindingFill | ImplicitClose;
+ case QPaintEngine::PolylineMode: return NonCurvedShapeHint;
+ default: return 0;
+ }
+}
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif
diff --git a/src/gui/painting/qpainter.cpp b/src/gui/painting/qpainter.cpp
new file mode 100644
index 0000000000..e2d9b32d36
--- /dev/null
+++ b/src/gui/painting/qpainter.cpp
@@ -0,0 +1,8533 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+// QtCore
+#include <qdebug.h>
+#include <qmath.h>
+#include <qmutex.h>
+
+// QtGui
+#include "qbitmap.h"
+#include "qimage.h"
+#include "qpaintdevice.h"
+#include "qpaintengine.h"
+#include "qpainter.h"
+#include "qpainter_p.h"
+#include "qpainterpath.h"
+#include "qpicture.h"
+#include "qpixmapcache.h"
+#include "qpolygon.h"
+#include "qtextlayout.h"
+#include "qwidget.h"
+#include "qapplication.h"
+#include "qstyle.h"
+#include "qthread.h"
+#include "qvarlengtharray.h"
+
+#include <private/qfontengine_p.h>
+#include <private/qpaintengine_p.h>
+#include <private/qemulationpaintengine_p.h>
+#include <private/qpainterpath_p.h>
+#include <private/qtextengine_p.h>
+#include <private/qwidget_p.h>
+#include <private/qpaintengine_raster_p.h>
+#include <private/qmath_p.h>
+
+QT_BEGIN_NAMESPACE
+
+#define QGradient_StretchToDevice 0x10000000
+#define QPaintEngine_OpaqueBackground 0x40000000
+
+// use the same rounding as in qrasterizer.cpp (6 bit fixed point)
+static const qreal aliasedCoordinateDelta = 0.5 - 0.015625;
+
+// #define QT_DEBUG_DRAW
+#ifdef QT_DEBUG_DRAW
+bool qt_show_painter_debug_output = true;
+#endif
+
+extern QPixmap qt_pixmapForBrush(int style, bool invert);
+
+void qt_format_text(const QFont &font,
+ const QRectF &_r, int tf, const QTextOption *option, const QString& str, QRectF *brect,
+ int tabstops, int* tabarray, int tabarraylen,
+ QPainter *painter);
+
+static inline QGradient::CoordinateMode coordinateMode(const QBrush &brush)
+{
+ switch (brush.style()) {
+ case Qt::LinearGradientPattern:
+ case Qt::RadialGradientPattern:
+ case Qt::ConicalGradientPattern:
+ return brush.gradient()->coordinateMode();
+ default:
+ ;
+ }
+ return QGradient::LogicalMode;
+}
+
+/* Returns true if the gradient requires stretch to device...*/
+static inline bool check_gradient(const QBrush &brush)
+{
+ return coordinateMode(brush) == QGradient::StretchToDeviceMode;
+}
+
+extern bool qHasPixmapTexture(const QBrush &);
+
+static inline bool is_brush_transparent(const QBrush &brush) {
+ Qt::BrushStyle s = brush.style();
+ bool brushBitmap = qHasPixmapTexture(brush)
+ ? brush.texture().isQBitmap()
+ : (brush.textureImage().depth() == 1);
+ return ((s >= Qt::Dense1Pattern && s <= Qt::DiagCrossPattern)
+ || (s == Qt::TexturePattern && brushBitmap));
+}
+
+static inline bool is_pen_transparent(const QPen &pen) {
+ return pen.style() > Qt::SolidLine || is_brush_transparent(pen.brush());
+}
+
+/* Discards the emulation flags that are not relevant for line drawing
+ and returns the result
+*/
+static inline uint line_emulation(uint emulation)
+{
+ return emulation & (QPaintEngine::PrimitiveTransform
+ | QPaintEngine::AlphaBlend
+ | QPaintEngine::Antialiasing
+ | QPaintEngine::BrushStroke
+ | QPaintEngine::ConstantOpacity
+ | QGradient_StretchToDevice
+ | QPaintEngine::ObjectBoundingModeGradients
+ | QPaintEngine_OpaqueBackground);
+}
+
+#ifndef QT_NO_DEBUG
+static bool qt_painter_thread_test(int devType, const char *what, bool extraCondition = false)
+{
+ switch (devType) {
+ case QInternal::Image:
+ case QInternal::Printer:
+ case QInternal::Picture:
+ // can be drawn onto these devices safely from any thread
+#ifndef Q_WS_WIN
+ if (extraCondition)
+#endif
+ break;
+ default:
+ if (!extraCondition && QThread::currentThread() != qApp->thread()) {
+ qWarning("QPainter: It is not safe to use %s outside the GUI thread", what);
+ return false;
+ }
+ break;
+ }
+ return true;
+}
+#endif
+
+void QPainterPrivate::checkEmulation()
+{
+ Q_ASSERT(extended);
+ bool doEmulation = false;
+ if (state->bgMode == Qt::OpaqueMode)
+ doEmulation = true;
+
+ const QGradient *bg = state->brush.gradient();
+ if (bg && bg->coordinateMode() > QGradient::LogicalMode)
+ doEmulation = true;
+
+ const QGradient *pg = qpen_brush(state->pen).gradient();
+ if (pg && pg->coordinateMode() > QGradient::LogicalMode)
+ doEmulation = true;
+
+ if (doEmulation) {
+ if (extended != emulationEngine) {
+ if (!emulationEngine)
+ emulationEngine = new QEmulationPaintEngine(extended);
+ extended = emulationEngine;
+ extended->setState(state);
+ }
+ } else if (emulationEngine && emulationEngine != extended) {
+ extended = emulationEngine->real_engine;
+ }
+}
+
+
+QPainterPrivate::~QPainterPrivate()
+{
+ delete emulationEngine;
+ for (int i=0; i<states.size(); ++i)
+ delete states.at(i);
+
+ if (dummyState)
+ delete dummyState;
+}
+
+
+QTransform QPainterPrivate::viewTransform() const
+{
+ if (state->VxF) {
+ qreal scaleW = qreal(state->vw)/qreal(state->ww);
+ qreal scaleH = qreal(state->vh)/qreal(state->wh);
+ return QTransform(scaleW, 0, 0, scaleH,
+ state->vx - state->wx*scaleW, state->vy - state->wy*scaleH);
+ }
+ return QTransform();
+}
+
+
+/*
+ \internal
+ Returns true if using a shared painter; otherwise false.
+*/
+bool QPainterPrivate::attachPainterPrivate(QPainter *q, QPaintDevice *pdev)
+{
+ Q_ASSERT(q);
+ Q_ASSERT(pdev);
+
+ if (pdev->devType() != QInternal::Widget)
+ return false;
+
+ QWidget *widget = static_cast<QWidget *>(pdev);
+ Q_ASSERT(widget);
+
+ // Someone either called QPainter::setRedirected in the widget's paint event
+ // right before this painter was created (or begin was called) or
+ // sent a paint event directly to the widget.
+ if (!widget->d_func()->redirectDev)
+ return false;
+
+ QPainter *sp = widget->d_func()->sharedPainter();
+ if (!sp || !sp->isActive())
+ return false;
+
+ if (sp->paintEngine()->paintDevice() != widget->d_func()->redirectDev)
+ return false;
+
+ // Check if we're attempting to paint outside a paint event.
+ if (!sp->d_ptr->engine->hasFeature(QPaintEngine::PaintOutsidePaintEvent)
+ && !widget->testAttribute(Qt::WA_PaintOutsidePaintEvent)
+ && !widget->testAttribute(Qt::WA_WState_InPaintEvent)) {
+
+ qWarning("QPainter::begin: Widget painting can only begin as a result of a paintEvent");
+ return false;
+ }
+
+ // Save the current state of the shared painter and assign
+ // the current d_ptr to the shared painter's d_ptr.
+ sp->save();
+ if (!sp->d_ptr->d_ptrs) {
+ // Allocate space for 4 d-pointers (enough for up to 4 sub-sequent
+ // redirections within the same paintEvent(), which should be enough
+ // in 99% of all cases). E.g: A renders B which renders C which renders D.
+ sp->d_ptr->d_ptrs_size = 4;
+ sp->d_ptr->d_ptrs = (QPainterPrivate **)malloc(4 * sizeof(QPainterPrivate *));
+ } else if (sp->d_ptr->refcount - 1 == sp->d_ptr->d_ptrs_size) {
+ // However, to support corner cases we grow the array dynamically if needed.
+ sp->d_ptr->d_ptrs_size <<= 1;
+ const int newSize = sp->d_ptr->d_ptrs_size * sizeof(QPainterPrivate *);
+ sp->d_ptr->d_ptrs = (QPainterPrivate **)realloc(sp->d_ptr->d_ptrs, newSize);
+ }
+ sp->d_ptr->d_ptrs[++sp->d_ptr->refcount - 2] = q->d_ptr;
+ q->d_ptr = sp->d_ptr;
+
+ Q_ASSERT(q->d_ptr->state);
+
+ // Now initialize the painter with correct widget properties.
+ q->initFrom(widget);
+ QPoint offset;
+ widget->d_func()->redirected(&offset);
+ offset += q->d_ptr->engine->coordinateOffset();
+
+ // Update system rect.
+ q->d_ptr->state->ww = q->d_ptr->state->vw = widget->width();
+ q->d_ptr->state->wh = q->d_ptr->state->vh = widget->height();
+
+ // Update matrix.
+ if (q->d_ptr->state->WxF)
+ q->d_ptr->state->worldMatrix.translate(-offset.x(), -offset.y());
+ else
+ q->d_ptr->state->redirection_offset = offset;
+ q->d_ptr->updateMatrix();
+
+ QPaintEnginePrivate *enginePrivate = q->d_ptr->engine->d_func();
+ if (enginePrivate->currentClipWidget == widget) {
+ enginePrivate->systemStateChanged();
+ return true;
+ }
+
+ // Update system transform and clip.
+ enginePrivate->currentClipWidget = widget;
+ enginePrivate->setSystemTransform(q->d_ptr->state->matrix);
+ return true;
+}
+
+void QPainterPrivate::detachPainterPrivate(QPainter *q)
+{
+ Q_ASSERT(refcount > 1);
+ Q_ASSERT(q);
+
+ QPainterPrivate *original = d_ptrs[--refcount - 1];
+ if (inDestructor) {
+ inDestructor = false;
+ if (original)
+ original->inDestructor = true;
+ } else if (!original) {
+ original = new QPainterPrivate(q);
+ }
+
+ d_ptrs[refcount - 1] = 0;
+ q->restore();
+ q->d_ptr = original;
+
+ if (emulationEngine) {
+ extended = emulationEngine->real_engine;
+ delete emulationEngine;
+ emulationEngine = 0;
+ }
+}
+
+
+void QPainterPrivate::draw_helper(const QPainterPath &originalPath, DrawOperation op)
+{
+#ifdef QT_DEBUG_DRAW
+ if (qt_show_painter_debug_output) {
+ printf("QPainter::drawHelper\n");
+ }
+#endif
+
+ if (originalPath.isEmpty())
+ return;
+
+ QPaintEngine::PaintEngineFeatures gradientStretch =
+ QPaintEngine::PaintEngineFeatures(QGradient_StretchToDevice
+ | QPaintEngine::ObjectBoundingModeGradients);
+
+ const bool mustEmulateObjectBoundingModeGradients = extended
+ || ((state->emulationSpecifier & QPaintEngine::ObjectBoundingModeGradients)
+ && !engine->hasFeature(QPaintEngine::PatternTransform));
+
+ if (!(state->emulationSpecifier & ~gradientStretch)
+ && !mustEmulateObjectBoundingModeGradients) {
+ drawStretchedGradient(originalPath, op);
+ return;
+ } else if (state->emulationSpecifier & QPaintEngine_OpaqueBackground) {
+ drawOpaqueBackground(originalPath, op);
+ return;
+ }
+
+ Q_Q(QPainter);
+
+ qreal strokeOffsetX = 0, strokeOffsetY = 0;
+
+ QPainterPath path = originalPath * state->matrix;
+ QRectF pathBounds = path.boundingRect();
+ QRectF strokeBounds;
+ bool doStroke = (op & StrokeDraw) && (state->pen.style() != Qt::NoPen);
+ if (doStroke) {
+ qreal penWidth = state->pen.widthF();
+ if (penWidth == 0) {
+ strokeOffsetX = 1;
+ strokeOffsetY = 1;
+ } else {
+ // In case of complex xform
+ if (state->matrix.type() > QTransform::TxScale) {
+ QPainterPathStroker stroker;
+ stroker.setWidth(penWidth);
+ stroker.setJoinStyle(state->pen.joinStyle());
+ stroker.setCapStyle(state->pen.capStyle());
+ QPainterPath stroke = stroker.createStroke(originalPath);
+ strokeBounds = (stroke * state->matrix).boundingRect();
+ } else {
+ strokeOffsetX = qAbs(penWidth * state->matrix.m11() / 2.0);
+ strokeOffsetY = qAbs(penWidth * state->matrix.m22() / 2.0);
+ }
+ }
+ }
+
+ QRect absPathRect;
+ if (!strokeBounds.isEmpty()) {
+ absPathRect = strokeBounds.intersected(QRectF(0, 0, device->width(), device->height())).toAlignedRect();
+ } else {
+ absPathRect = pathBounds.adjusted(-strokeOffsetX, -strokeOffsetY, strokeOffsetX, strokeOffsetY)
+ .intersected(QRectF(0, 0, device->width(), device->height())).toAlignedRect();
+ }
+
+ if (q->hasClipping()) {
+ bool hasPerspectiveTransform = false;
+ for (int i = 0; i < state->clipInfo.size(); ++i) {
+ const QPainterClipInfo &info = state->clipInfo.at(i);
+ if (info.matrix.type() == QTransform::TxProject) {
+ hasPerspectiveTransform = true;
+ break;
+ }
+ }
+ // avoid mapping QRegions with perspective transforms
+ if (!hasPerspectiveTransform) {
+ // The trick with txinv and invMatrix is done in order to
+ // avoid transforming the clip to logical coordinates, and
+ // then back to device coordinates. This is a problem with
+ // QRegion/QRect based clips, since they use integer
+ // coordinates and converting to/from logical coordinates will
+ // lose precision.
+ bool old_txinv = txinv;
+ QTransform old_invMatrix = invMatrix;
+ txinv = true;
+ invMatrix = QTransform().translate(-state->redirection_offset.x(), -state->redirection_offset.y());
+ QPainterPath clipPath = q->clipPath();
+ QRectF r = clipPath.boundingRect().intersected(absPathRect);
+ absPathRect = r.toAlignedRect();
+ txinv = old_txinv;
+ invMatrix = old_invMatrix;
+ }
+ }
+
+// qDebug("\nQPainterPrivate::draw_helper(), x=%d, y=%d, w=%d, h=%d",
+// devMinX, devMinY, device->width(), device->height());
+// qDebug() << " - matrix" << state->matrix;
+// qDebug() << " - originalPath.bounds" << originalPath.boundingRect();
+// qDebug() << " - path.bounds" << path.boundingRect();
+
+ if (absPathRect.width() <= 0 || absPathRect.height() <= 0)
+ return;
+
+ QImage image(absPathRect.width(), absPathRect.height(), QImage::Format_ARGB32_Premultiplied);
+ image.fill(0);
+
+ QPainter p(&image);
+
+ p.d_ptr->helper_device = helper_device;
+
+ p.setOpacity(state->opacity);
+ p.translate(-absPathRect.x(), -absPathRect.y());
+ p.setTransform(state->matrix, true);
+ p.setPen(doStroke ? state->pen : QPen(Qt::NoPen));
+ p.setBrush((op & FillDraw) ? state->brush : QBrush(Qt::NoBrush));
+ p.setBackground(state->bgBrush);
+ p.setBackgroundMode(state->bgMode);
+ p.setBrushOrigin(state->brushOrigin);
+
+ p.setRenderHint(QPainter::Antialiasing, state->renderHints & QPainter::Antialiasing);
+ p.setRenderHint(QPainter::SmoothPixmapTransform,
+ state->renderHints & QPainter::SmoothPixmapTransform);
+
+ p.drawPath(originalPath);
+
+#ifndef QT_NO_DEBUG
+ static bool do_fallback_overlay = qgetenv("QT_PAINT_FALLBACK_OVERLAY").size() > 0;
+ if (do_fallback_overlay) {
+ QImage block(8, 8, QImage::Format_ARGB32_Premultiplied);
+ QPainter pt(&block);
+ pt.fillRect(0, 0, 8, 8, QColor(196, 0, 196));
+ pt.drawLine(0, 0, 8, 8);
+ pt.end();
+ p.resetTransform();
+ p.setCompositionMode(QPainter::CompositionMode_SourceAtop);
+ p.setOpacity(0.5);
+ p.fillRect(0, 0, image.width(), image.height(), QBrush(block));
+ }
+#endif
+
+ p.end();
+
+ q->save();
+ q->resetMatrix();
+ updateState(state);
+ engine->drawImage(absPathRect,
+ image,
+ QRectF(0, 0, absPathRect.width(), absPathRect.height()),
+ Qt::OrderedDither | Qt::OrderedAlphaDither);
+ q->restore();
+}
+
+void QPainterPrivate::drawOpaqueBackground(const QPainterPath &path, DrawOperation op)
+{
+ Q_Q(QPainter);
+
+ q->setBackgroundMode(Qt::TransparentMode);
+
+ if (op & FillDraw && state->brush.style() != Qt::NoBrush) {
+ q->fillPath(path, state->bgBrush.color());
+ q->fillPath(path, state->brush);
+ }
+
+ if (op & StrokeDraw && state->pen.style() != Qt::NoPen) {
+ q->strokePath(path, QPen(state->bgBrush.color(), state->pen.width()));
+ q->strokePath(path, state->pen);
+ }
+
+ q->setBackgroundMode(Qt::OpaqueMode);
+}
+
+static inline QBrush stretchGradientToUserSpace(const QBrush &brush, const QRectF &boundingRect)
+{
+ Q_ASSERT(brush.style() >= Qt::LinearGradientPattern
+ && brush.style() <= Qt::ConicalGradientPattern);
+
+ QTransform gradientToUser(boundingRect.width(), 0, 0, boundingRect.height(),
+ boundingRect.x(), boundingRect.y());
+
+ QGradient g = *brush.gradient();
+ g.setCoordinateMode(QGradient::LogicalMode);
+
+ QBrush b(g);
+ b.setTransform(gradientToUser * b.transform());
+ return b;
+}
+
+void QPainterPrivate::drawStretchedGradient(const QPainterPath &path, DrawOperation op)
+{
+ Q_Q(QPainter);
+
+ const qreal sw = helper_device->width();
+ const qreal sh = helper_device->height();
+
+ bool changedPen = false;
+ bool changedBrush = false;
+ bool needsFill = false;
+
+ const QPen pen = state->pen;
+ const QBrush brush = state->brush;
+
+ const QGradient::CoordinateMode penMode = coordinateMode(pen.brush());
+ const QGradient::CoordinateMode brushMode = coordinateMode(brush);
+
+ QRectF boundingRect;
+
+ // Draw the xformed fill if the brush is a stretch gradient.
+ if ((op & FillDraw) && brush.style() != Qt::NoBrush) {
+ if (brushMode == QGradient::StretchToDeviceMode) {
+ q->setPen(Qt::NoPen);
+ changedPen = pen.style() != Qt::NoPen;
+ q->scale(sw, sh);
+ updateState(state);
+
+ const qreal isw = 1.0 / sw;
+ const qreal ish = 1.0 / sh;
+ QTransform inv(isw, 0, 0, ish, 0, 0);
+ engine->drawPath(path * inv);
+ q->scale(isw, ish);
+ } else {
+ needsFill = true;
+
+ if (brushMode == QGradient::ObjectBoundingMode) {
+ Q_ASSERT(engine->hasFeature(QPaintEngine::PatternTransform));
+ boundingRect = path.boundingRect();
+ q->setBrush(stretchGradientToUserSpace(brush, boundingRect));
+ changedBrush = true;
+ }
+ }
+ }
+
+ if ((op & StrokeDraw) && pen.style() != Qt::NoPen) {
+ // Draw the xformed outline if the pen is a stretch gradient.
+ if (penMode == QGradient::StretchToDeviceMode) {
+ q->setPen(Qt::NoPen);
+ changedPen = true;
+
+ if (needsFill) {
+ updateState(state);
+ engine->drawPath(path);
+ }
+
+ q->scale(sw, sh);
+ q->setBrush(pen.brush());
+ changedBrush = true;
+ updateState(state);
+
+ QPainterPathStroker stroker;
+ stroker.setDashPattern(pen.style());
+ stroker.setWidth(pen.widthF());
+ stroker.setJoinStyle(pen.joinStyle());
+ stroker.setCapStyle(pen.capStyle());
+ stroker.setMiterLimit(pen.miterLimit());
+ QPainterPath stroke = stroker.createStroke(path);
+
+ const qreal isw = 1.0 / sw;
+ const qreal ish = 1.0 / sh;
+ QTransform inv(isw, 0, 0, ish, 0, 0);
+ engine->drawPath(stroke * inv);
+ q->scale(isw, ish);
+ } else {
+ if (!needsFill && brush.style() != Qt::NoBrush) {
+ q->setBrush(Qt::NoBrush);
+ changedBrush = true;
+ }
+
+ if (penMode == QGradient::ObjectBoundingMode) {
+ Q_ASSERT(engine->hasFeature(QPaintEngine::PatternTransform));
+
+ // avoid computing the bounding rect twice
+ if (!needsFill || brushMode != QGradient::ObjectBoundingMode)
+ boundingRect = path.boundingRect();
+
+ QPen p = pen;
+ p.setBrush(stretchGradientToUserSpace(pen.brush(), boundingRect));
+ q->setPen(p);
+ changedPen = true;
+ } else if (changedPen) {
+ q->setPen(pen);
+ changedPen = false;
+ }
+
+ updateState(state);
+ engine->drawPath(path);
+ }
+ } else if (needsFill) {
+ if (pen.style() != Qt::NoPen) {
+ q->setPen(Qt::NoPen);
+ changedPen = true;
+ }
+
+ updateState(state);
+ engine->drawPath(path);
+ }
+
+ if (changedPen)
+ q->setPen(pen);
+ if (changedBrush)
+ q->setBrush(brush);
+}
+
+
+void QPainterPrivate::updateMatrix()
+{
+ state->matrix = state->WxF ? state->worldMatrix : QTransform();
+ if (state->VxF)
+ state->matrix *= viewTransform();
+
+ txinv = false; // no inverted matrix
+ if (!state->redirection_offset.isNull()) {
+ // We want to translate in dev space so we do the adding of the redirection
+ // offset manually.
+ if (state->matrix.isAffine()) {
+ state->matrix = QTransform(state->matrix.m11(), state->matrix.m12(),
+ state->matrix.m21(), state->matrix.m22(),
+ state->matrix.dx()-state->redirection_offset.x(),
+ state->matrix.dy()-state->redirection_offset.y());
+ } else {
+ QTransform temp;
+ temp.translate(-state->redirection_offset.x(), -state->redirection_offset.y());
+ state->matrix *= temp;
+ }
+ }
+ if (extended)
+ extended->transformChanged();
+ else
+ state->dirtyFlags |= QPaintEngine::DirtyTransform;
+
+// printf("VxF=%d, WxF=%d\n", state->VxF, state->WxF);
+// qDebug() << " --- using matrix" << state->matrix << redirection_offset;
+}
+
+/*! \internal */
+void QPainterPrivate::updateInvMatrix()
+{
+ Q_ASSERT(txinv == false);
+ txinv = true; // creating inverted matrix
+ QTransform m;
+
+ if (state->VxF)
+ m = viewTransform();
+
+ if (state->WxF) {
+ if (state->VxF)
+ m = state->worldMatrix * m;
+ else
+ m = state->worldMatrix;
+ }
+ invMatrix = m.inverted(); // invert matrix
+}
+
+void QPainterPrivate::updateEmulationSpecifier(QPainterState *s)
+{
+ bool alpha = false;
+ bool linearGradient = false;
+ bool radialGradient = false;
+ bool conicalGradient = false;
+ bool patternBrush = false;
+ bool xform = false;
+ bool complexXform = false;
+
+ bool skip = true;
+
+ // Pen and brush properties (we have to check both if one changes because the
+ // one that's unchanged can still be in a state which requires emulation)
+ if (s->state() & (QPaintEngine::DirtyPen | QPaintEngine::DirtyBrush | QPaintEngine::DirtyHints)) {
+ // Check Brush stroke emulation
+ if (!s->pen.isSolid() && !engine->hasFeature(QPaintEngine::BrushStroke))
+ s->emulationSpecifier |= QPaintEngine::BrushStroke;
+ else
+ s->emulationSpecifier &= ~QPaintEngine::BrushStroke;
+
+ skip = false;
+
+ QBrush penBrush = s->pen.brush();
+ Qt::BrushStyle brushStyle = s->brush.style();
+ Qt::BrushStyle penBrushStyle = penBrush.style();
+ alpha = (penBrushStyle != Qt::NoBrush
+ && (penBrushStyle < Qt::LinearGradientPattern && penBrush.color().alpha() != 255)
+ && !penBrush.isOpaque())
+ || (brushStyle != Qt::NoBrush
+ && (brushStyle < Qt::LinearGradientPattern && s->brush.color().alpha() != 255)
+ && !s->brush.isOpaque());
+ linearGradient = ((penBrushStyle == Qt::LinearGradientPattern) ||
+ (brushStyle == Qt::LinearGradientPattern));
+ radialGradient = ((penBrushStyle == Qt::RadialGradientPattern) ||
+ (brushStyle == Qt::RadialGradientPattern));
+ conicalGradient = ((penBrushStyle == Qt::ConicalGradientPattern) ||
+ (brushStyle == Qt::ConicalGradientPattern));
+ patternBrush = (((penBrushStyle > Qt::SolidPattern
+ && penBrushStyle < Qt::LinearGradientPattern)
+ || penBrushStyle == Qt::TexturePattern) ||
+ ((brushStyle > Qt::SolidPattern
+ && brushStyle < Qt::LinearGradientPattern)
+ || brushStyle == Qt::TexturePattern));
+
+ bool penTextureAlpha = false;
+ if (penBrush.style() == Qt::TexturePattern)
+ penTextureAlpha = qHasPixmapTexture(penBrush)
+ ? penBrush.texture().hasAlpha()
+ : penBrush.textureImage().hasAlphaChannel();
+ bool brushTextureAlpha = false;
+ if (s->brush.style() == Qt::TexturePattern)
+ brushTextureAlpha = qHasPixmapTexture(s->brush)
+ ? s->brush.texture().hasAlpha()
+ : s->brush.textureImage().hasAlphaChannel();
+ if (((penBrush.style() == Qt::TexturePattern && penTextureAlpha)
+ || (s->brush.style() == Qt::TexturePattern && brushTextureAlpha))
+ && !engine->hasFeature(QPaintEngine::MaskedBrush))
+ s->emulationSpecifier |= QPaintEngine::MaskedBrush;
+ else
+ s->emulationSpecifier &= ~QPaintEngine::MaskedBrush;
+ }
+
+ if (s->state() & (QPaintEngine::DirtyHints
+ | QPaintEngine::DirtyOpacity
+ | QPaintEngine::DirtyBackgroundMode)) {
+ skip = false;
+ }
+
+ if (skip)
+ return;
+
+#if 0
+ qDebug("QPainterPrivate::updateEmulationSpecifier, state=%p\n"
+ " - alpha: %d\n"
+ " - linearGradient: %d\n"
+ " - radialGradient: %d\n"
+ " - conicalGradient: %d\n"
+ " - patternBrush: %d\n"
+ " - hints: %x\n"
+ " - xform: %d\n",
+ s,
+ alpha,
+ linearGradient,
+ radialGradient,
+ conicalGradient,
+ patternBrush,
+ uint(s->renderHints),
+ xform);
+#endif
+
+ // XForm properties
+ if (s->state() & QPaintEngine::DirtyTransform) {
+ xform = !s->matrix.isIdentity();
+ complexXform = !s->matrix.isAffine();
+ } else if (s->matrix.type() >= QTransform::TxTranslate) {
+ xform = true;
+ complexXform = !s->matrix.isAffine();
+ }
+
+ const bool brushXform = (!s->brush.transform().type() == QTransform::TxNone);
+ const bool penXform = (!s->pen.brush().transform().type() == QTransform::TxNone);
+
+ const bool patternXform = patternBrush && (xform || brushXform || penXform);
+
+ // Check alphablending
+ if (alpha && !engine->hasFeature(QPaintEngine::AlphaBlend))
+ s->emulationSpecifier |= QPaintEngine::AlphaBlend;
+ else
+ s->emulationSpecifier &= ~QPaintEngine::AlphaBlend;
+
+ // Linear gradient emulation
+ if (linearGradient && !engine->hasFeature(QPaintEngine::LinearGradientFill))
+ s->emulationSpecifier |= QPaintEngine::LinearGradientFill;
+ else
+ s->emulationSpecifier &= ~QPaintEngine::LinearGradientFill;
+
+ // Radial gradient emulation
+ if (radialGradient && !engine->hasFeature(QPaintEngine::RadialGradientFill))
+ s->emulationSpecifier |= QPaintEngine::RadialGradientFill;
+ else
+ s->emulationSpecifier &= ~QPaintEngine::RadialGradientFill;
+
+ // Conical gradient emulation
+ if (conicalGradient && !engine->hasFeature(QPaintEngine::ConicalGradientFill))
+ s->emulationSpecifier |= QPaintEngine::ConicalGradientFill;
+ else
+ s->emulationSpecifier &= ~QPaintEngine::ConicalGradientFill;
+
+ // Pattern brushes
+ if (patternBrush && !engine->hasFeature(QPaintEngine::PatternBrush))
+ s->emulationSpecifier |= QPaintEngine::PatternBrush;
+ else
+ s->emulationSpecifier &= ~QPaintEngine::PatternBrush;
+
+ // Pattern XForms
+ if (patternXform && !engine->hasFeature(QPaintEngine::PatternTransform))
+ s->emulationSpecifier |= QPaintEngine::PatternTransform;
+ else
+ s->emulationSpecifier &= ~QPaintEngine::PatternTransform;
+
+ // Primitive XForms
+ if (xform && !engine->hasFeature(QPaintEngine::PrimitiveTransform))
+ s->emulationSpecifier |= QPaintEngine::PrimitiveTransform;
+ else
+ s->emulationSpecifier &= ~QPaintEngine::PrimitiveTransform;
+
+ // Perspective XForms
+ if (complexXform && !engine->hasFeature(QPaintEngine::PerspectiveTransform))
+ s->emulationSpecifier |= QPaintEngine::PerspectiveTransform;
+ else
+ s->emulationSpecifier &= ~QPaintEngine::PerspectiveTransform;
+
+ // Constant opacity
+ if (state->opacity != 1 && !engine->hasFeature(QPaintEngine::ConstantOpacity))
+ s->emulationSpecifier |= QPaintEngine::ConstantOpacity;
+ else
+ s->emulationSpecifier &= ~QPaintEngine::ConstantOpacity;
+
+ bool gradientStretch = false;
+ bool objectBoundingMode = false;
+ if (linearGradient || conicalGradient || radialGradient) {
+ QGradient::CoordinateMode brushMode = coordinateMode(s->brush);
+ QGradient::CoordinateMode penMode = coordinateMode(s->pen.brush());
+
+ gradientStretch |= (brushMode == QGradient::StretchToDeviceMode);
+ gradientStretch |= (penMode == QGradient::StretchToDeviceMode);
+
+ objectBoundingMode |= (brushMode == QGradient::ObjectBoundingMode);
+ objectBoundingMode |= (penMode == QGradient::ObjectBoundingMode);
+ }
+ if (gradientStretch)
+ s->emulationSpecifier |= QGradient_StretchToDevice;
+ else
+ s->emulationSpecifier &= ~QGradient_StretchToDevice;
+
+ if (objectBoundingMode && !engine->hasFeature(QPaintEngine::ObjectBoundingModeGradients))
+ s->emulationSpecifier |= QPaintEngine::ObjectBoundingModeGradients;
+ else
+ s->emulationSpecifier &= ~QPaintEngine::ObjectBoundingModeGradients;
+
+ // Opaque backgrounds...
+ if (s->bgMode == Qt::OpaqueMode &&
+ (is_pen_transparent(s->pen) || is_brush_transparent(s->brush)))
+ s->emulationSpecifier |= QPaintEngine_OpaqueBackground;
+ else
+ s->emulationSpecifier &= ~QPaintEngine_OpaqueBackground;
+
+#if 0
+ //won't be correct either way because the device can already have
+ // something rendered to it in which case subsequent emulation
+ // on a fully transparent qimage and then blitting the results
+ // won't produce correct results
+ // Blend modes
+ if (state->composition_mode > QPainter::CompositionMode_Xor &&
+ !engine->hasFeature(QPaintEngine::BlendModes))
+ s->emulationSpecifier |= QPaintEngine::BlendModes;
+ else
+ s->emulationSpecifier &= ~QPaintEngine::BlendModes;
+#endif
+}
+
+void QPainterPrivate::updateStateImpl(QPainterState *newState)
+{
+ // ### we might have to call QPainter::begin() here...
+ if (!engine->state) {
+ engine->state = newState;
+ engine->setDirty(QPaintEngine::AllDirty);
+ }
+
+ if (engine->state->painter() != newState->painter)
+ // ### this could break with clip regions vs paths.
+ engine->setDirty(QPaintEngine::AllDirty);
+
+ // Upon restore, revert all changes since last save
+ else if (engine->state != newState)
+ newState->dirtyFlags |= QPaintEngine::DirtyFlags(static_cast<QPainterState *>(engine->state)->changeFlags);
+
+ // We need to store all changes made so that restore can deal with them
+ else
+ newState->changeFlags |= newState->dirtyFlags;
+
+ updateEmulationSpecifier(newState);
+
+ // Unset potential dirty background mode
+ newState->dirtyFlags &= ~(QPaintEngine::DirtyBackgroundMode
+ | QPaintEngine::DirtyBackground);
+
+ engine->state = newState;
+ engine->updateState(*newState);
+ engine->clearDirty(QPaintEngine::AllDirty);
+
+}
+
+void QPainterPrivate::updateState(QPainterState *newState)
+{
+
+ if (!newState) {
+ engine->state = newState;
+
+ } else if (newState->state() || engine->state!=newState) {
+ bool setNonCosmeticPen = (newState->renderHints & QPainter::NonCosmeticDefaultPen)
+ && newState->pen.widthF() == 0;
+ if (setNonCosmeticPen) {
+ // Override the default pen's cosmetic state if the
+ // NonCosmeticDefaultPen render hint is used.
+ QPen oldPen = newState->pen;
+ newState->pen.setWidth(1);
+ newState->pen.setCosmetic(false);
+ newState->dirtyFlags |= QPaintEngine::DirtyPen;
+
+ updateStateImpl(newState);
+
+ // Restore the state pen back to its default to preserve visible
+ // state.
+ newState->pen = oldPen;
+ } else {
+ updateStateImpl(newState);
+ }
+ }
+}
+
+
+/*!
+ \class QPainter
+ \brief The QPainter class performs low-level painting on widgets and
+ other paint devices.
+
+ \ingroup multimedia
+ \mainclass
+ \reentrant
+
+ QPainter provides highly optimized functions to do most of the
+ drawing GUI programs require. It can draw everything from simple
+ lines to complex shapes like pies and chords. It can also draw
+ aligned text and pixmaps. Normally, it draws in a "natural"
+ coordinate system, but it can also do view and world
+ transformation. QPainter can operate on any object that inherits
+ the QPaintDevice class.
+
+ The common use of QPainter is inside a widget's paint event:
+ Construct and customize (e.g. set the pen or the brush) the
+ painter. Then draw. Remember to destroy the QPainter object after
+ drawing. For example:
+
+ \snippet doc/src/snippets/code/src_gui_painting_qpainter.cpp 0
+
+ The core functionality of QPainter is drawing, but the class also
+ provide several functions that allows you to customize QPainter's
+ settings and its rendering quality, and others that enable
+ clipping. In addition you can control how different shapes are
+ merged together by specifying the painter's composition mode.
+
+ The isActive() function indicates whether the painter is active. A
+ painter is activated by the begin() function and the constructor
+ that takes a QPaintDevice argument. The end() function, and the
+ destructor, deactivates it.
+
+ Together with the QPaintDevice and QPaintEngine classes, QPainter
+ form the basis for Qt's paint system. QPainter is the class used
+ to perform drawing operations. QPaintDevice represents a device
+ that can be painted on using a QPainter. QPaintEngine provides the
+ interface that the painter uses to draw onto different types of
+ devices. If the painter is active, device() returns the paint
+ device on which the painter paints, and paintEngine() returns the
+ paint engine that the painter is currently operating on. For more
+ information, see \l {The Paint System} documentation.
+
+ Sometimes it is desirable to make someone else paint on an unusual
+ QPaintDevice. QPainter supports a static function to do this,
+ setRedirected().
+
+ \warning When the paintdevice is a widget, QPainter can only be
+ used inside a paintEvent() function or in a function called by
+ paintEvent(); that is unless the Qt::WA_PaintOutsidePaintEvent
+ widget attribute is set. On Mac OS X and Windows, you can only
+ paint in a paintEvent() function regardless of this attribute's
+ setting.
+
+ \tableofcontents
+
+ \section1 Settings
+
+ There are several settings that you can customize to make QPainter
+ draw according to your preferences:
+
+ \list
+
+ \o font() is the font used for drawing text. If the painter
+ isActive(), you can retrieve information about the currently set
+ font, and its metrics, using the fontInfo() and fontMetrics()
+ functions respectively.
+
+ \o brush() defines the color or pattern that is used for filling
+ shapes.
+
+ \o pen() defines the color or stipple that is used for drawing
+ lines or boundaries.
+
+ \o backgroundMode() defines whether there is a background() or
+ not, i.e it is either Qt::OpaqueMode or Qt::TransparentMode.
+
+ \o background() only applies when backgroundMode() is \l
+ Qt::OpaqueMode and pen() is a stipple. In that case, it
+ describes the color of the background pixels in the stipple.
+
+ \o brushOrigin() defines the origin of the tiled brushes, normally
+ the origin of widget's background.
+
+ \o viewport(), window(), worldMatrix() make up the painter's coordinate
+ transformation system. For more information, see the \l
+ {Coordinate Transformations} section and the \l {The Coordinate
+ System} documentation.
+
+ \o hasClipping() tells whether the painter clips at all. (The paint
+ device clips, too.) If the painter clips, it clips to clipRegion().
+
+ \o layoutDirection() defines the layout direction used by the
+ painter when drawing text.
+
+ \o matrixEnabled() tells whether world transformation is enabled.
+
+ \o viewTransformEnabled() tells whether view transformation is
+ enabled.
+
+ \endlist
+
+ Note that some of these settings mirror settings in some paint
+ devices, e.g. QWidget::font(). The QPainter::begin() function (or
+ equivalently the QPainter constructor) copies these attributes
+ from the paint device.
+
+ You can at any time save the QPainter's state by calling the
+ save() function which saves all the available settings on an
+ internal stack. The restore() function pops them back.
+
+ \section1 Drawing
+
+ QPainter provides functions to draw most primitives: drawPoint(),
+ drawPoints(), drawLine(), drawRect(), drawRoundedRect(),
+ drawEllipse(), drawArc(), drawPie(), drawChord(), drawPolyline(),
+ drawPolygon(), drawConvexPolygon() and drawCubicBezier(). The two
+ convenience functions, drawRects() and drawLines(), draw the given
+ number of rectangles or lines in the given array of \l
+ {QRect}{QRects} or \l {QLine}{QLines} using the current pen and
+ brush.
+
+ The QPainter class also provides the fillRect() function which
+ fills the given QRect, with the given QBrush, and the eraseRect()
+ function that erases the area inside the given rectangle.
+
+ All of these functions have both integer and floating point
+ versions.
+
+ \table 100%
+ \row
+ \o \inlineimage qpainter-basicdrawing.png
+ \o
+ \bold {Basic Drawing Example}
+
+ The \l {painting/basicdrawing}{Basic Drawing} example shows how to
+ display basic graphics primitives in a variety of styles using the
+ QPainter class.
+
+ \endtable
+
+ If you need to draw a complex shape, especially if you need to do
+ so repeatedly, consider creating a QPainterPath and drawing it
+ using drawPath().
+
+ \table 100%
+ \row
+ \o
+ \bold {Painter Paths example}
+
+ The QPainterPath class provides a container for painting
+ operations, enabling graphical shapes to be constructed and
+ reused.
+
+ The \l {painting/painterpaths}{Painter Paths} example shows how
+ painter paths can be used to build complex shapes for rendering.
+
+ \o \inlineimage qpainter-painterpaths.png
+ \endtable
+
+ QPainter also provides the fillPath() function which fills the
+ given QPainterPath with the given QBrush, and the strokePath()
+ function that draws the outline of the given path (i.e. strokes
+ the path).
+
+ See also the \l {demos/deform}{Vector Deformation} demo which
+ shows how to use advanced vector techniques to draw text using a
+ QPainterPath, the \l {demos/gradients}{Gradients} demo which shows
+ the different types of gradients that are available in Qt, and the \l
+ {demos/pathstroke}{Path Stroking} demo which shows Qt's built-in
+ dash patterns and shows how custom patterns can be used to extend
+ the range of available patterns.
+
+ \table
+ \row
+ \o \inlineimage qpainter-vectordeformation.png
+ \o \inlineimage qpainter-gradients.png
+ \o \inlineimage qpainter-pathstroking.png
+ \header
+ \o \l {demos/deform}{Vector Deformation}
+ \o \l {demos/gradients}{Gradients}
+ \o \l {demos/pathstroke}{Path Stroking}
+ \endtable
+
+
+ There are functions to draw pixmaps/images, namely drawPixmap(),
+ drawImage() and drawTiledPixmap(). Both drawPixmap() and drawImage()
+ produce the same result, except that drawPixmap() is faster
+ on-screen while drawImage() may be faster on a QPrinter or other
+ devices.
+
+ Text drawing is done using drawText(). When you need
+ fine-grained positioning, boundingRect() tells you where a given
+ drawText() command will draw.
+
+ There is a drawPicture() function that draws the contents of an
+ entire QPicture. The drawPicture() function is the only function
+ that disregards all the painter's settings as QPicture has its own
+ settings.
+
+ \section1 Rendering Quality
+
+ To get the optimal rendering result using QPainter, you should use
+ the platform independent QImage as paint device; i.e. using QImage
+ will ensure that the result has an identical pixel representation
+ on any platform.
+
+ The QPainter class also provides a means of controlling the
+ rendering quality through its RenderHint enum and the support for
+ floating point precision: All the functions for drawing primitives
+ has a floating point version. These are often used in combination
+ with the \l {RenderHint}{QPainter::Antialiasing} render hint.
+
+ \table 100%
+ \row
+ \o \inlineimage qpainter-concentriccircles.png
+ \o
+ \bold {Concentric Circles Example}
+
+ The \l {painting/concentriccircles}{Concentric Circles} example
+ shows the improved rendering quality that can be obtained using
+ floating point precision and anti-aliasing when drawing custom
+ widgets.
+
+ The application's main window displays several widgets which are
+ drawn using the various combinations of precision and
+ anti-aliasing.
+
+ \endtable
+
+ The RenderHint enum specifies flags to QPainter that may or may
+ not be respected by any given engine. \l
+ {RenderHint}{QPainter::Antialiasing} indicates that the engine
+ should antialias edges of primitives if possible, \l
+ {RenderHint}{QPainter::TextAntialiasing} indicates that the engine
+ should antialias text if possible, and the \l
+ {RenderHint}{QPainter::SmoothPixmapTransform} indicates that the
+ engine should use a smooth pixmap transformation algorithm.
+ \l {RenderHint}{HighQualityAntialiasing} is an OpenGL-specific rendering hint
+ indicating that the engine should use fragment programs and offscreen
+ rendering for antialiasing.
+
+ The renderHints() function returns a flag that specifies the
+ rendering hints that are set for this painter. Use the
+ setRenderHint() function to set or clear the currently set
+ RenderHints.
+
+ \section1 Coordinate Transformations
+
+ Normally, the QPainter operates on the device's own coordinate
+ system (usually pixels), but QPainter has good support for
+ coordinate transformations.
+
+ \table
+ \row
+ \o \inlineimage qpainter-clock.png
+ \o \inlineimage qpainter-rotation.png
+ \o \inlineimage qpainter-scale.png
+ \o \inlineimage qpainter-translation.png
+ \header
+ \o nop \o rotate() \o scale() \o translate()
+ \endtable
+
+ The most commonly used transformations are scaling, rotation,
+ translation and shearing. Use the scale() function to scale the
+ coordinate system by a given offset, the rotate() function to
+ rotate it clockwise and translate() to translate it (i.e. adding a
+ given offset to the points). You can also twist the coordinate
+ system around the origin using the shear() function. See the \l
+ {demos/affine}{Affine Transformations} demo for a visualization of
+ a sheared coordinate system.
+
+ See also the \l {painting/transformations}{Transformations}
+ example which shows how transformations influence the way that
+ QPainter renders graphics primitives. In particular it shows how
+ the order of transformations affects the result.
+
+ \table 100%
+ \row
+ \o
+ \bold {Affine Transformations Demo}
+
+ The \l {demos/affine}{Affine Transformations} demo show Qt's
+ ability to perform affine transformations on painting
+ operations. The demo also allows the user to experiment with the
+ transformation operations and see the results immediately.
+
+ \o \inlineimage qpainter-affinetransformations.png
+ \endtable
+
+ All the tranformation operations operate on the transformation
+ worldMatrix(). A matrix transforms a point in the plane to another
+ point. For more information about the transformation matrix, see
+ the \l {The Coordinate System} and QMatrix documentation.
+
+ The setWorldMatrix() function can replace or add to the currently
+ set worldMatrix(). The resetMatrix() function resets any
+ transformations that were made using translate(), scale(),
+ shear(), rotate(), setWorldMatrix(), setViewport() and setWindow()
+ functions. The deviceMatrix() returns the matrix that transforms
+ from logical coordinates to device coordinates of the platform
+ dependent paint device. The latter function is only needed when
+ using platform painting commands on the platform dependent handle,
+ and the platform does not do transformations nativly.
+
+ When drawing with QPainter, we specify points using logical
+ coordinates which then are converted into the physical coordinates
+ of the paint device. The mapping of the logical coordinates to the
+ physical coordinates are handled by QPainter's combinedMatrix(), a
+ combination of viewport() and window() and worldMatrix(). The
+ viewport() represents the physical coordinates specifying an
+ arbitrary rectangle, the window() describes the same rectangle in
+ logical coordinates, and the worldMatrix() is identical with the
+ transformation matrix.
+
+ See also \l {The Coordinate System} documentation.
+
+ \section1 Clipping
+
+ QPainter can clip any drawing operation to a rectangle, a region,
+ or a vector path. The current clip is available using the
+ functions clipRegion() and clipPath(). Whether paths or regions are
+ preferred (faster) depends on the underlying paintEngine(). For
+ example, the QImage paint engine prefers paths while the X11 paint
+ engine prefers regions. Setting a clip is done in the painters
+ logical coordinates.
+
+ After QPainter's clipping, the paint device may also clip. For
+ example, most widgets clip away the pixels used by child widgets,
+ and most printers clip away an area near the edges of the paper.
+ This additional clipping is not reflected by the return value of
+ clipRegion() or hasClipping().
+
+ \section1 Composition Modes
+ \target Composition Modes
+
+ QPainter provides the CompositionMode enum which defines the
+ Porter-Duff rules for digital image compositing; it describes a
+ model for combining the pixels in one image, the source, with the
+ pixels in another image, the destination.
+
+ The two most common forms of composition are \l
+ {QPainter::CompositionMode}{Source} and \l
+ {QPainter::CompositionMode}{SourceOver}. \l
+ {QPainter::CompositionMode}{Source} is used to draw opaque objects
+ onto a paint device. In this mode, each pixel in the source
+ replaces the corresponding pixel in the destination. In \l
+ {QPainter::CompositionMode}{SourceOver} composition mode, the
+ source object is transparent and is drawn on top of the
+ destination.
+
+ Note that composition transformation operates pixelwise. For that
+ reason, there is a difference between using the grahic primitive
+ itself and its bounding rectangle: The bounding rect contains
+ pixels with alpha == 0 (i.e the pixels surrounding the
+ primitive). These pixels will overwrite the other image's pixels,
+ affectively clearing those, while the primitive only overwrites
+ its own area.
+
+ \table 100%
+ \row
+ \o \inlineimage qpainter-compositiondemo.png
+
+ \o
+ \bold {Composition Modes Demo}
+
+ The \l {demos/composition}{Composition Modes} demo, available in
+ Qt's demo directory, allows you to experiment with the various
+ composition modes and see the results immediately.
+
+ \endtable
+
+ \section1 Limitations
+ \target Limitations
+
+ If you are using coordinates with Qt's raster-based paint engine, it is
+ important to note that, while coordinates greater than +/- 2\sup 15 can
+ be used, any painting performed with coordinates outside this range is not
+ guaranteed to be shown; the drawing may be clipped. This is due to the
+ use of \c{short int} in the implementation.
+
+ The outlines generated by Qt's stroker are only an approximation when dealing
+ with curved shapes. It is in most cases impossible to represent the outline of
+ a bezier curve segment using another bezier curve segment, and so Qt approximates
+ the curve outlines by using several smaller curves. For performance reasons there
+ is a limit to how many curves Qt uses for these outlines, and thus when using
+ large pen widths or scales the outline error increases. To generate outlines with
+ smaller errors it is possible to use the QPainterPathStroker class, which has the
+ setCurveThreshold member function which let's the user specify the error tolerance.
+ Another workaround is to convert the paths to polygons first and then draw the
+ polygons instead.
+
+ \sa QPaintDevice, QPaintEngine, {QtSvg Module}, {Basic Drawing Example}
+*/
+
+/*!
+ \enum QPainter::RenderHint
+
+ Renderhints are used to specify flags to QPainter that may or
+ may not be respected by any given engine.
+
+ \value Antialiasing Indicates that the engine should antialias
+ edges of primitives if possible.
+
+ \value TextAntialiasing Indicates that the engine should antialias
+ text if possible. To forcibly disable antialiasing for text, do not
+ use this hint. Instead, set QFont::NoAntialias on your font's style
+ strategy.
+
+ \value SmoothPixmapTransform Indicates that the engine should use
+ a smooth pixmap transformation algorithm (such as bilinear) rather
+ than nearest neighbor.
+
+ \value HighQualityAntialiasing An OpenGL-specific rendering hint
+ indicating that the engine should use fragment programs and offscreen
+ rendering for antialiasing.
+
+ \value NonCosmeticDefaultPen The engine should interpret pens with a width
+ of 0 (which otherwise enables QPen::isCosmetic()) as being a non-cosmetic
+ pen with a width of 1.
+
+ \sa renderHints(), setRenderHint(), {QPainter#Rendering
+ Quality}{Rendering Quality}, {Concentric Circles Example}
+
+*/
+
+/*!
+ Constructs a painter.
+
+ \sa begin(), end()
+*/
+
+QPainter::QPainter()
+{
+ d_ptr = new QPainterPrivate(this);
+}
+
+/*!
+ \fn QPainter::QPainter(QPaintDevice *device)
+
+ Constructs a painter that begins painting the paint \a device
+ immediately.
+
+ This constructor is convenient for short-lived painters, e.g. in a
+ QWidget::paintEvent() and should be used only once. The
+ constructor calls begin() for you and the QPainter destructor
+ automatically calls end().
+
+ Here's an example using begin() and end():
+ \snippet doc/src/snippets/code/src_gui_painting_qpainter.cpp 1
+
+ The same example using this constructor:
+ \snippet doc/src/snippets/code/src_gui_painting_qpainter.cpp 2
+
+ Since the constructor cannot provide feedback when the initialization
+ of the painter failed you should rather use begin() and end() to paint
+ on external devices, e.g. printers.
+
+ \sa begin(), end()
+*/
+
+QPainter::QPainter(QPaintDevice *pd)
+ : d_ptr(0)
+{
+ Q_ASSERT(pd != 0);
+ if (!QPainterPrivate::attachPainterPrivate(this, pd)) {
+ d_ptr = new QPainterPrivate(this);
+ begin(pd);
+ }
+ Q_ASSERT(d_ptr);
+}
+
+/*!
+ Destroys the painter.
+*/
+QPainter::~QPainter()
+{
+ d_ptr->inDestructor = true;
+ if (isActive())
+ end();
+ else if (d_ptr->refcount > 1)
+ d_ptr->detachPainterPrivate(this);
+
+ if (d_ptr) {
+ // Make sure we haven't messed things up.
+ Q_ASSERT(d_ptr->inDestructor);
+ d_ptr->inDestructor = false;
+ Q_ASSERT(d_ptr->refcount == 1);
+ if (d_ptr->d_ptrs)
+ free(d_ptr->d_ptrs);
+ delete d_ptr;
+ }
+}
+
+/*!
+ Returns the paint device on which this painter is currently
+ painting, or 0 if the painter is not active.
+
+ \sa isActive()
+*/
+
+QPaintDevice *QPainter::device() const
+{
+ Q_D(const QPainter);
+ if (isActive() && d->engine->d_func()->currentClipWidget)
+ return d->engine->d_func()->currentClipWidget;
+ return d->original_device;
+}
+
+/*!
+ Returns true if begin() has been called and end() has not yet been
+ called; otherwise returns false.
+
+ \sa begin(), QPaintDevice::paintingActive()
+*/
+
+bool QPainter::isActive() const
+{
+ Q_D(const QPainter);
+ return d->engine;
+}
+
+/*!
+ Initializes the painters pen, background and font to the same as
+ the given \a widget. Call this function after begin() while the
+ painter is active.
+
+ \sa begin(), {QPainter#Settings}{Settings}
+*/
+void QPainter::initFrom(const QWidget *widget)
+{
+ Q_ASSERT_X(widget, "QPainter::initFrom(const QWidget *widget)", "Widget cannot be 0");
+ Q_D(QPainter);
+ if (!d->engine) {
+ qWarning("QPainter::initFrom: Painter not active, aborted");
+ return;
+ }
+
+ const QPalette &pal = widget->palette();
+ d->state->pen = QPen(pal.brush(widget->foregroundRole()), 0);
+ d->state->bgBrush = pal.brush(widget->backgroundRole());
+ d->state->deviceFont = QFont(widget->font(), const_cast<QWidget*> (widget));
+ d->state->font = d->state->deviceFont;
+ if (d->engine) {
+ d->engine->setDirty(QPaintEngine::DirtyPen);
+ d->engine->setDirty(QPaintEngine::DirtyBrush);
+ d->engine->setDirty(QPaintEngine::DirtyFont);
+ }
+ d->state->layoutDirection = widget->layoutDirection();
+}
+
+
+/*!
+ Saves the current painter state (pushes the state onto a stack). A
+ save() must be followed by a corresponding restore(); the end()
+ function unwinds the stack.
+
+ \sa restore()
+*/
+
+void QPainter::save()
+{
+#ifdef QT_DEBUG_DRAW
+ if (qt_show_painter_debug_output)
+ printf("QPainter::save()\n");
+#endif
+ Q_D(QPainter);
+ if (!d->engine) {
+ qWarning("QPainter::save: Painter not active");
+ return;
+ }
+
+ if (d->extended) {
+ d->state = d->extended->createState(d->states.back());
+ d->extended->setState(d->state);
+ } else {
+ d->updateState(d->state);
+ d->state = new QPainterState(d->states.back());
+ d->engine->state = d->state;
+ }
+ d->states.push_back(d->state);
+}
+
+/*!
+ Restores the current painter state (pops a saved state off the
+ stack).
+
+ \sa save()
+*/
+
+void QPainter::restore()
+{
+#ifdef QT_DEBUG_DRAW
+ if (qt_show_painter_debug_output)
+ printf("QPainter::restore()\n");
+#endif
+ Q_D(QPainter);
+ if (d->states.size()<=1) {
+ qWarning("QPainter::restore: Unbalanced save/restore");
+ return;
+ } else if (!d->engine) {
+ qWarning("QPainter::restore: Painter not active");
+ return;
+ }
+
+ QPainterState *tmp = d->state;
+ d->states.pop_back();
+ d->state = d->states.back();
+ d->txinv = false;
+
+ if (d->extended) {
+ d->checkEmulation();
+ d->extended->setState(d->state);
+ delete tmp;
+ return;
+ }
+
+ // trigger clip update if the clip path/region has changed since
+ // last save
+ if (!d->state->clipInfo.isEmpty()
+ && (tmp->changeFlags & (QPaintEngine::DirtyClipRegion | QPaintEngine::DirtyClipPath))) {
+ // reuse the tmp state to avoid any extra allocs...
+ tmp->dirtyFlags = QPaintEngine::DirtyClipPath;
+ tmp->clipOperation = Qt::NoClip;
+ tmp->clipPath = QPainterPath();
+ d->engine->updateState(*tmp);
+ // replay the list of clip states,
+ for (int i=0; i<d->state->clipInfo.size(); ++i) {
+ const QPainterClipInfo &info = d->state->clipInfo.at(i);
+ tmp->matrix.setMatrix(info.matrix.m11(), info.matrix.m12(), info.matrix.m13(),
+ info.matrix.m21(), info.matrix.m22(), info.matrix.m23(),
+ info.matrix.dx() - d->state->redirection_offset.x(),
+ info.matrix.dy() - d->state->redirection_offset.y(), info.matrix.m33());
+ tmp->clipOperation = info.operation;
+ if (info.clipType == QPainterClipInfo::RectClip) {
+ tmp->dirtyFlags = QPaintEngine::DirtyClipRegion | QPaintEngine::DirtyTransform;
+ tmp->clipRegion = info.rect;
+ } else if (info.clipType == QPainterClipInfo::RegionClip) {
+ tmp->dirtyFlags = QPaintEngine::DirtyClipRegion | QPaintEngine::DirtyTransform;
+ tmp->clipRegion = info.region;
+ } else { // clipType == QPainterClipInfo::PathClip
+ tmp->dirtyFlags = QPaintEngine::DirtyClipPath | QPaintEngine::DirtyTransform;
+ tmp->clipPath = info.path;
+ }
+ d->engine->updateState(*tmp);
+ }
+
+
+ //Since we've updated the clip region anyway, pretend that the clip path hasn't changed:
+ d->state->dirtyFlags &= ~(QPaintEngine::DirtyClipPath | QPaintEngine::DirtyClipRegion);
+ tmp->changeFlags &= ~(QPaintEngine::DirtyClipPath | QPaintEngine::DirtyClipRegion);
+ tmp->changeFlags |= QPaintEngine::DirtyTransform;
+ }
+
+ d->updateState(d->state);
+ delete tmp;
+}
+
+
+/*!
+
+ \fn bool QPainter::begin(QPaintDevice *device)
+
+ Begins painting the paint \a device and returns true if
+ successful; otherwise returns false.
+
+ Notice that all painter settings (setPen(), setBrush() etc.) are reset
+ to default values when begin() is called.
+
+ The errors that can occur are serious problems, such as these:
+
+ \snippet doc/src/snippets/code/src_gui_painting_qpainter.cpp 3
+
+ Note that most of the time, you can use one of the constructors
+ instead of begin(), and that end() is automatically done at
+ destruction.
+
+ \warning A paint device can only be painted by one painter at a
+ time.
+
+ \sa end(), QPainter()
+*/
+
+bool QPainter::begin(QPaintDevice *pd)
+{
+ Q_ASSERT(pd);
+
+ if (pd->painters > 0) {
+ qWarning("QPainter::begin: A paint device can only be painted by one painter at a time.");
+ return false;
+ }
+
+ if (d_ptr->engine) {
+ qWarning("QPainter::begin: Painter already active");
+ return false;
+ }
+
+ if (QPainterPrivate::attachPainterPrivate(this, pd))
+ return true;
+
+ Q_D(QPainter);
+
+ d->helper_device = pd;
+ d->original_device = pd;
+ QPaintDevice *rpd = 0;
+
+ QPoint redirectionOffset;
+ // We know for sure that redirection is broken when the widget is inside
+ // its paint event, so it's safe to use our hard-coded redirection. However,
+ // there IS one particular case we still need to support, and that's
+ // when people call QPainter::setRedirected in the widget's paint event right
+ // before any painter is created (or QPainter::begin is called). In that
+ // particular case our hard-coded redirection is restored and the redirection
+ // is retrieved from QPainter::redirected (as before).
+ if (pd->devType() == QInternal::Widget)
+ rpd = static_cast<QWidget *>(pd)->d_func()->redirected(&redirectionOffset);
+
+ if (!rpd)
+ rpd = redirected(pd, &redirectionOffset);
+
+ if (rpd)
+ pd = rpd;
+
+#ifdef QT_DEBUG_DRAW
+ if (qt_show_painter_debug_output)
+ printf("QPainter::begin(), device=%p, type=%d\n", pd, pd->devType());
+#endif
+
+
+ d->device = pd;
+ if (pd->devType() == QInternal::Pixmap)
+ static_cast<QPixmap *>(pd)->detach();
+ else if (pd->devType() == QInternal::Image)
+ static_cast<QImage *>(pd)->detach();
+
+ d->engine = pd->paintEngine();
+ d->extended = d->engine && d->engine->isExtended() ? static_cast<QPaintEngineEx *>(d->engine) : 0;
+ if (d->emulationEngine)
+ d->emulationEngine->real_engine = d->extended;
+
+ // Setup new state...
+ Q_ASSERT(!d->state);
+ d->state = d->extended ? d->extended->createState(0) : new QPainterState;
+ d->state->painter = this;
+ d->states.push_back(d->state);
+
+ d->state->redirection_offset = redirectionOffset;
+ d->state->brushOrigin = QPointF();
+
+ if (!d->engine) {
+ qWarning("QPainter::begin: Paint device returned engine == 0, type: %d", pd->devType());
+ return true;
+ }
+
+ // Slip a painter state into the engine before we do any other operations
+ if (d->extended)
+ d->extended->setState(d->state);
+ else
+ d->engine->state = d->state;
+
+ switch (pd->devType()) {
+ case QInternal::Widget:
+ {
+ const QWidget *widget = static_cast<const QWidget *>(pd);
+ Q_ASSERT(widget);
+
+ const bool paintOutsidePaintEvent = widget->testAttribute(Qt::WA_PaintOutsidePaintEvent);
+ const bool inPaintEvent = widget->testAttribute(Qt::WA_WState_InPaintEvent);
+ if(!d->engine->hasFeature(QPaintEngine::PaintOutsidePaintEvent)
+ && !paintOutsidePaintEvent && !inPaintEvent) {
+ qWarning("QPainter::begin: Widget painting can only begin as a "
+ "result of a paintEvent");
+ d->engine = 0;
+ d->device = 0;
+ return false;
+ }
+
+ // Adjust offset for alien widgets painting outside the paint event.
+ if (!inPaintEvent && paintOutsidePaintEvent && !widget->internalWinId()
+ && widget->testAttribute(Qt::WA_WState_Created)) {
+ d->state->redirection_offset -= widget->mapTo(widget->nativeParentWidget(), QPoint());
+ }
+ break;
+ }
+ case QInternal::Pixmap:
+ {
+ QPixmap *pm = static_cast<QPixmap *>(pd);
+ Q_ASSERT(pm);
+ if (pm->isNull()) {
+ qWarning("QPainter::begin: Cannot paint on a null pixmap");
+ d->engine = 0;
+ d->device = 0;
+ return false;
+ }
+
+ if (pm->depth() == 1) {
+ d->state->pen = QPen(Qt::color1);
+ d->state->brush = QBrush(Qt::color0);
+ }
+ break;
+ }
+ case QInternal::Image:
+ {
+ QImage *img = static_cast<QImage *>(pd);
+ Q_ASSERT(img);
+ if (img->isNull()) {
+ qWarning("QPainter::begin: Cannot paint on a null image");
+ d->engine = 0;
+ d->device = 0;
+ return false;
+ }
+ if (img->depth() == 1) {
+ d->state->pen = QPen(Qt::color1);
+ d->state->brush = QBrush(Qt::color0);
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ if (d->state->ww == 0) // For compat with 3.x painter defaults
+ d->state->ww = d->state->wh = d->state->vw = d->state->vh = 1024;
+
+ d->engine->setPaintDevice(pd);
+
+ bool begun = d->engine->begin(pd);
+ if (!begun) {
+ qWarning("QPainter::begin(): Returned false");
+ if (d->engine->isActive()) {
+ end();
+ } else {
+ d->states.clear();
+ delete d->state;
+ d->state = 0;
+ }
+ d->engine = 0;
+ d->device = 0;
+ return false;
+ } else {
+ d->engine->setActive(begun);
+ }
+
+ // Copy painter properties from original paint device,
+ // required for QPixmap::grabWidget()
+ if (d->original_device->devType() == QInternal::Widget) {
+ QWidget *widget = static_cast<QWidget *>(d->original_device);
+ initFrom(widget);
+ } else {
+ d->state->layoutDirection = QApplication::layoutDirection();
+ // make sure we have a font compatible with the paintdevice
+ d->state->deviceFont = d->state->font = QFont(d->state->deviceFont, device());
+ }
+
+ QRect systemRect = d->engine->systemRect();
+ if (!systemRect.isEmpty()) {
+ d->state->ww = d->state->vw = systemRect.width();
+ d->state->wh = d->state->vh = systemRect.height();
+ } else {
+ d->state->ww = d->state->vw = pd->metric(QPaintDevice::PdmWidth);
+ d->state->wh = d->state->vh = pd->metric(QPaintDevice::PdmHeight);
+ }
+
+ d->state->redirection_offset += d->engine->coordinateOffset();
+
+ Q_ASSERT(d->engine->isActive());
+
+ if (!d->state->redirection_offset.isNull())
+ d->updateMatrix();
+
+ Q_ASSERT(d->engine->isActive());
+ d->state->renderHints = QPainter::TextAntialiasing;
+ ++d->device->painters;
+
+ d->state->emulationSpecifier = 0;
+
+ return true;
+}
+
+/*!
+ Ends painting. Any resources used while painting are released. You
+ don't normally need to call this since it is called by the
+ destructor.
+
+ Returns true if the painter is no longer active; otherwise returns false.
+
+ \sa begin(), isActive()
+*/
+
+bool QPainter::end()
+{
+#ifdef QT_DEBUG_DRAW
+ if (qt_show_painter_debug_output)
+ printf("QPainter::end()\n");
+#endif
+ Q_D(QPainter);
+
+ if (!d->engine) {
+ qWarning("QPainter::end: Painter not active, aborted");
+ d->states.clear();
+ delete d->state;
+ d->state = 0;
+ d->device = 0;
+ return false;
+ }
+
+ if (d->refcount > 1) {
+ d->detachPainterPrivate(this);
+ return true;
+ }
+
+ if (d->states.size() > 1) {
+ qWarning("QPainter::end: Painter ended with %d saved states",
+ d->states.size());
+ }
+
+ bool ended = true;
+
+ if (d->engine->isActive()) {
+ ended = d->engine->end();
+ d->updateState(0);
+
+ --d->device->painters;
+ if (d->device->painters == 0) {
+ d->engine->setPaintDevice(0);
+ d->engine->setActive(false);
+ }
+ }
+
+ if (d->engine->autoDestruct()) {
+ delete d->engine;
+ }
+
+ d->engine = 0;
+
+ if (d->emulationEngine) {
+ delete d->emulationEngine;
+ d->emulationEngine = 0;
+ }
+
+ if (d->extended) {
+ d->extended = 0;
+ }
+
+ d->states.clear();
+ delete d->state;
+ d->state = 0;
+
+ d->device = 0;
+ return ended;
+}
+
+
+/*!
+ Returns the paint engine that the painter is currently operating
+ on if the painter is active; otherwise 0.
+
+ \sa isActive()
+*/
+QPaintEngine *QPainter::paintEngine() const
+{
+ Q_D(const QPainter);
+ return d->engine;
+}
+
+
+/*!
+ Returns the font metrics for the painter if the painter is
+ active. Otherwise, the return value is undefined.
+
+ \sa font(), isActive(), {QPainter#Settings}{Settings}
+*/
+
+QFontMetrics QPainter::fontMetrics() const
+{
+ Q_D(const QPainter);
+ if (!d->engine) {
+ qWarning("QPainter::fontMetrics: Painter not active");
+ return QFontMetrics(QFont());
+ }
+ return QFontMetrics(d->state->font);
+}
+
+
+/*!
+ Returns the font info for the painter if the painter is
+ active. Otherwise, the return value is undefined.
+
+ \sa font(), isActive(), {QPainter#Settings}{Settings}
+*/
+
+QFontInfo QPainter::fontInfo() const
+{
+ Q_D(const QPainter);
+ if (!d->engine) {
+ qWarning("QPainter::fontInfo: Painter not active");
+ return QFontInfo(QFont());
+ }
+ return QFontInfo(d->state->font);
+}
+
+/*!
+ \since 4.2
+
+ Returns the opacity of the painter. The default value is
+ 1.
+*/
+
+qreal QPainter::opacity() const
+{
+ Q_D(const QPainter);
+ if (!d->engine) {
+ qWarning("QPainter::opacity: Painter not active");
+ return 1.0;
+ }
+ return d->state->opacity;
+}
+
+/*!
+ \since 4.2
+
+ Sets the opacity of the painter to \a opacity. The value should
+ be in the range 0.0 to 1.0, where 0.0 is fully transparent and
+ 1.0 is fully opaque.
+
+ Opacity set on the painter will apply to all drawing operations
+ individually.
+*/
+
+void QPainter::setOpacity(qreal opacity)
+{
+ Q_D(QPainter);
+
+ if (!d->engine) {
+ qWarning("QPainter::setOpacity: Painter not active");
+ return;
+ }
+
+ opacity = qMin(qreal(1), qMax(qreal(0), opacity));
+
+ if (opacity == d->state->opacity)
+ return;
+
+ d->state->opacity = opacity;
+
+ if (d->extended)
+ d->extended->opacityChanged();
+ else
+ d->state->dirtyFlags |= QPaintEngine::DirtyOpacity;
+}
+
+
+/*!
+ Returns the currently set brush origin.
+
+ \sa setBrushOrigin(), {QPainter#Settings}{Settings}
+*/
+
+QPoint QPainter::brushOrigin() const
+{
+ Q_D(const QPainter);
+ if (!d->engine) {
+ qWarning("QPainter::brushOrigin: Painter not active");
+ return QPoint();
+ }
+ return QPointF(d->state->brushOrigin).toPoint();
+}
+
+/*!
+ \fn void QPainter::setBrushOrigin(const QPointF &position)
+
+ Sets the brush origin to \a position.
+
+ The brush origin specifies the (0, 0) coordinate of the painter's
+ brush. This setting only applies to pattern brushes and pixmap
+ brushes.
+
+ Note that while the brushOrigin() was necessary to adopt the
+ parent's background for a widget in Qt 3, this is no longer the
+ case since the Qt 4 painter doesn't paint the background unless
+ you explicitly tell it to do so by setting the widget's \l
+ {QWidget::autoFillBackground}{autoFillBackground} property to
+ true.
+
+ \sa brushOrigin(), {QPainter#Settings}{Settings}
+*/
+
+void QPainter::setBrushOrigin(const QPointF &p)
+{
+ Q_D(QPainter);
+#ifdef QT_DEBUG_DRAW
+ if (qt_show_painter_debug_output)
+ printf("QPainter::setBrushOrigin(), (%.2f,%.2f)\n", p.x(), p.y());
+#endif
+
+ if (!d->engine) {
+ qWarning("QPainter::setBrushOrigin: Painter not active");
+ return;
+ }
+
+ d->state->brushOrigin = p;
+
+ if (d->extended) {
+ d->extended->brushOriginChanged();
+ return;
+ }
+
+ d->state->dirtyFlags |= QPaintEngine::DirtyBrushOrigin;
+}
+
+/*!
+ \fn void QPainter::setBrushOrigin(const QPoint &position)
+ \overload
+
+ Sets the brush's origin to the given \a position.
+*/
+
+/*!
+ \fn void QPainter::setBrushOrigin(int x, int y)
+
+ \overload
+
+ Sets the brush's origin to point (\a x, \a y).
+*/
+
+/*!
+ \enum QPainter::CompositionMode
+
+ Defines the modes supported for digital image compositing.
+ Composition modes are used to specify how the pixels in one image,
+ the source, are merged with the pixel in another image, the
+ destination.
+
+ Please note that the bitwise raster operation modes, denoted with
+ a RasterOp prefix, are only natively supported in the X11 and
+ raster paint engines. This means that the only way to utilize
+ these modes on the Mac is via a QImage. The RasterOp denoted blend
+ modes are \e not supported for pens and brushes with alpha
+ components. Also, turning on the QPainter::Antialiasing render
+ hint will effectively disable the RasterOp modes.
+
+
+ \image qpainter-compositionmode1.png
+ \image qpainter-compositionmode2.png
+
+ The most common type is SourceOver (often referred to as just
+ alpha blending) where the source pixel is blended on top of the
+ destination pixel in such a way that the alpha component of the
+ source defines the translucency of the pixel.
+
+ When the paint device is a QImage, the image format must be set to
+ \l {QImage::Format}{Format_ARGB32Premultiplied} or
+ \l {QImage::Format}{Format_ARGB32} for the composition modes to have
+ any effect. For performance the premultiplied version is the preferred
+ format.
+
+ When a composition mode is set it applies to all painting
+ operator, pens, brushes, gradients and pixmap/image drawing.
+
+ \value CompositionMode_SourceOver This is the default mode. The
+ alpha of the source is used to blend the pixel on top of the
+ destination.
+
+ \value CompositionMode_DestinationOver The alpha of the
+ destination is used to blend it on top of the source pixels. This
+ mode is the inverse of CompositionMode_SourceOver.
+
+ \value CompositionMode_Clear The pixels in the destination are
+ cleared (set to fully transparent) independent of the source.
+
+ \value CompositionMode_Source The output is the source
+ pixel. (This means a basic copy operation and is identical to
+ SourceOver when the source pixel is opaque).
+
+ \value CompositionMode_Destination The output is the destination
+ pixel. This means that the blending has no effect. This mode is
+ the inverse of CompositionMode_Source.
+
+ \value CompositionMode_SourceIn The output is the source, where
+ the alpha is reduced by that of the destination.
+
+ \value CompositionMode_DestinationIn The output is the
+ destination, where the alpha is reduced by that of the
+ source. This mode is the inverse of CompositionMode_SourceIn.
+
+ \value CompositionMode_SourceOut The output is the source, where
+ the alpha is reduced by the inverse of destination.
+
+ \value CompositionMode_DestinationOut The output is the
+ destination, where the alpha is reduced by the inverse of the
+ source. This mode is the inverse of CompositionMode_SourceOut.
+
+ \value CompositionMode_SourceAtop The source pixel is blended on
+ top of the destination, with the alpha of the source pixel reduced
+ by the alpha of the destination pixel.
+
+ \value CompositionMode_DestinationAtop The destination pixel is
+ blended on top of the source, with the alpha of the destination
+ pixel is reduced by the alpha of the destination pixel. This mode
+ is the inverse of CompositionMode_SourceAtop.
+
+ \value CompositionMode_Xor The source, whose alpha is reduced with
+ the inverse of the destination alpha, is merged with the
+ destination, whose alpha is reduced by the inverse of the source
+ alpha. CompositionMode_Xor is not the same as the bitwise Xor.
+
+ \value CompositionMode_Plus Both the alpha and color of the source
+ and destination pixels are added together.
+
+ \value CompositionMode_Multiply The output is the source color
+ multiplied by the destination. Multiplying a color with white
+ leaves the color unchanged, while multiplying a color
+ with black produces black.
+
+ \value CompositionMode_Screen The source and destination colors
+ are inverted and then multiplied. Screening a color with white
+ produces white, whereas screening a color with black leaves the
+ color unchanged.
+
+ \value CompositionMode_Overlay Multiplies or screens the colors
+ depending on the destination color. The destination color is mixed
+ with the source color to reflect the lightness or darkness of the
+ destination.
+
+ \value CompositionMode_Darken The darker of the source and
+ destination colors is selected.
+
+ \value CompositionMode_Lighten The lighter of the source and
+ destination colors is selected.
+
+ \value CompositionMode_ColorDodge The destination color is
+ brightened to reflect the source color. A black source color
+ leaves the destination color unchanged.
+
+ \value CompositionMode_ColorBurn The destination color is darkened
+ to reflect the source color. A white source color leaves the
+ destination color unchanged.
+
+ \value CompositionMode_HardLight Multiplies or screens the colors
+ depending on the source color. A light source color will lighten
+ the destination color, whereas a dark source color will darken the
+ destination color.
+
+ \value CompositionMode_SoftLight Darkens or lightens the colors
+ depending on the source color. Similar to
+ CompositionMode_HardLight.
+
+ \value CompositionMode_Difference Subtracts the darker of the
+ colors from the lighter. Painting with white inverts the
+ destination color, whereas painting with black leaves the
+ destination color unchanged.
+
+ \value CompositionMode_Exclusion Similar to
+ CompositionMode_Difference, but with a lower contrast. Painting
+ with white inverts the destination color, whereas painting with
+ black leaves the destination color unchanged.
+
+ \value RasterOp_SourceOrDestination Does a bitwise OR operation on
+ the source and destination pixels (src OR dst).
+
+ \value RasterOp_SourceAndDestination Does a bitwise AND operation
+ on the source and destination pixels (src AND dst).
+
+ \value RasterOp_SourceXorDestination Does a bitwise XOR operation
+ on the source and destination pixels (src XOR dst).
+
+ \value RasterOp_NotSourceAndNotDestination Does a bitwise NOR
+ operation on the source and destination pixels ((NOT src) AND (NOT
+ dst)).
+
+ \value RasterOp_NotSourceOrNotDestination Does a bitwise NAND
+ operation on the source and destination pixels ((NOT src) OR (NOT
+ dst)).
+
+ \value RasterOp_NotSourceXorDestination Does a bitwise operation
+ where the source pixels are inverted and then XOR'ed with the
+ destination ((NOT src) XOR dst).
+
+ \value RasterOp_NotSource Does a bitwise operation where the
+ source pixels are inverted (NOT src).
+
+ \value RasterOp_NotSourceAndDestination Does a bitwise operation
+ where the source is inverted and then AND'ed with the destination
+ ((NOT src) AND dst).
+
+ \value RasterOp_SourceAndNotDestination Does a bitwise operation
+ where the source is AND'ed with the inverted destination pixels
+ (src AND (NOT dst)).
+
+ \sa compositionMode(), setCompositionMode(), {QPainter#Composition
+ Modes}{Composition Modes}, {Image Composition Example}
+*/
+
+/*!
+ Sets the composition mode to the given \a mode.
+
+ \warning You can only set the composition mode for QPainter
+ objects that operates on a QImage.
+
+ \sa compositionMode()
+*/
+void QPainter::setCompositionMode(CompositionMode mode)
+{
+ Q_D(QPainter);
+ if (!d->engine) {
+ qWarning("QPainter::setCompositionMode: Painter not active");
+ return;
+ }
+ if (d->extended) {
+ d->state->composition_mode = mode;
+ d->extended->compositionModeChanged();
+ return;
+ }
+
+ if (mode >= QPainter::RasterOp_SourceOrDestination) {
+ if (!d->engine->hasFeature(QPaintEngine::RasterOpModes)) {
+ qWarning("QPainter::setCompositionMode: "
+ "Raster operation modes not supported on device");
+ return;
+ }
+ } else if (mode >= QPainter::CompositionMode_Plus) {
+ if (!d->engine->hasFeature(QPaintEngine::BlendModes)) {
+ qWarning("QPainter::setCompositionMode: "
+ "Blend modes not supported on device");
+ return;
+ }
+ } else if (!d->engine->hasFeature(QPaintEngine::PorterDuff)) {
+ if (mode != CompositionMode_Source && mode != CompositionMode_SourceOver) {
+ qWarning("QPainter::setCompositionMode: "
+ "PorterDuff modes not supported on device");
+ return;
+ }
+ }
+
+ d->state->composition_mode = mode;
+ d->state->dirtyFlags |= QPaintEngine::DirtyCompositionMode;
+}
+
+/*!
+ Returns the current composition mode.
+
+ \sa CompositionMode, setCompositionMode()
+*/
+QPainter::CompositionMode QPainter::compositionMode() const
+{
+ Q_D(const QPainter);
+ if (!d->engine) {
+ qWarning("QPainter::compositionMode: Painter not active");
+ return QPainter::CompositionMode_SourceOver;
+ }
+ return d->state->composition_mode;
+}
+
+/*!
+ Returns the current background brush.
+
+ \sa setBackground(), {QPainter#Settings}{Settings}
+*/
+
+const QBrush &QPainter::background() const
+{
+ Q_D(const QPainter);
+ if (!d->engine) {
+ qWarning("QPainter::background: Painter not active");
+ return d->fakeState()->brush;
+ }
+ return d->state->bgBrush;
+}
+
+
+/*!
+ Returns true if clipping has been set; otherwise returns false.
+
+ \sa setClipping(), {QPainter#Clipping}{Clipping}
+*/
+
+bool QPainter::hasClipping() const
+{
+ Q_D(const QPainter);
+ if (!d->engine) {
+ qWarning("QPainter::hasClipping: Painter not active");
+ return false;
+ }
+ return d->state->clipEnabled && d->state->clipOperation != Qt::NoClip;
+}
+
+
+/*!
+ Enables clipping if \a enable is true, or disables clipping if \a
+ enable is false.
+
+ \sa hasClipping(), {QPainter#Clipping}{Clipping}
+*/
+
+void QPainter::setClipping(bool enable)
+{
+ Q_D(QPainter);
+#ifdef QT_DEBUG_DRAW
+ if (qt_show_painter_debug_output)
+ printf("QPainter::setClipping(), enable=%s, was=%s\n",
+ enable ? "on" : "off",
+ hasClipping() ? "on" : "off");
+#endif
+ if (!d->engine) {
+ qWarning("QPainter::setClipping: Painter not active, state will be reset by begin");
+ return;
+ }
+
+ if (hasClipping() == enable)
+ return;
+
+ // we can't enable clipping if we don't have a clip
+ if (enable
+ && (d->state->clipInfo.isEmpty() || d->state->clipInfo.last().operation == Qt::NoClip))
+ return;
+ d->state->clipEnabled = enable;
+
+ if (d->extended) {
+ d->extended->clipEnabledChanged();
+ return;
+ }
+
+ d->state->dirtyFlags |= QPaintEngine::DirtyClipEnabled;
+ d->updateState(d->state);
+}
+
+
+/*!
+ Returns the currently set clip region. Note that the clip region
+ is given in logical coordinates.
+
+ \sa setClipRegion(), clipPath(), setClipping()
+*/
+
+QRegion QPainter::clipRegion() const
+{
+ Q_D(const QPainter);
+ if (!d->engine) {
+ qWarning("QPainter::clipRegion: Painter not active");
+ return QRegion();
+ }
+
+ QRegion region;
+ bool lastWasNothing = true;
+
+ if (!d->txinv)
+ const_cast<QPainter *>(this)->d_ptr->updateInvMatrix();
+
+ // ### Falcon: Use QPainterPath
+ for (int i=0; i<d->state->clipInfo.size(); ++i) {
+ const QPainterClipInfo &info = d->state->clipInfo.at(i);
+ QRegion other;
+ switch (info.clipType) {
+
+ case QPainterClipInfo::RegionClip: {
+ QTransform matrix = (info.matrix * d->invMatrix);
+ if (lastWasNothing) {
+ region = info.region * matrix;
+ lastWasNothing = false;
+ continue;
+ }
+ if (info.operation == Qt::IntersectClip)
+ region &= info.region * matrix;
+ else if (info.operation == Qt::UniteClip)
+ region |= info.region * matrix;
+ else if (info.operation == Qt::NoClip) {
+ lastWasNothing = true;
+ region = QRegion();
+ } else
+ region = info.region * matrix;
+ break;
+ }
+
+ case QPainterClipInfo::PathClip: {
+ QTransform matrix = (info.matrix * d->invMatrix);
+ if (lastWasNothing) {
+ region = QRegion((info.path * matrix).toFillPolygon().toPolygon(),
+ info.path.fillRule());
+ lastWasNothing = false;
+ continue;
+ }
+ if (info.operation == Qt::IntersectClip) {
+ region &= QRegion((info.path * matrix).toFillPolygon().toPolygon(),
+ info.path.fillRule());
+ } else if (info.operation == Qt::UniteClip) {
+ region |= QRegion((info.path * matrix).toFillPolygon().toPolygon(),
+ info.path.fillRule());
+ } else if (info.operation == Qt::NoClip) {
+ lastWasNothing = true;
+ region = QRegion();
+ } else {
+ region = QRegion((info.path * matrix).toFillPolygon().toPolygon(),
+ info.path.fillRule());
+ }
+ break;
+ }
+
+ case QPainterClipInfo::RectClip: {
+ QTransform matrix = (info.matrix * d->invMatrix);
+ if (lastWasNothing) {
+ region = QRegion(info.rect) * matrix;
+ lastWasNothing = false;
+ continue;
+ }
+ if (info.operation == Qt::IntersectClip)
+ region &= QRegion(info.rect) * matrix;
+ else if (info.operation == Qt::UniteClip)
+ region |= QRegion(info.rect) * matrix;
+ else if (info.operation == Qt::NoClip) {
+ lastWasNothing = true;
+ region = QRegion();
+ } else
+ region = QRegion(info.rect) * matrix;
+ break;
+ }
+
+ case QPainterClipInfo::RectFClip: {
+ QTransform matrix = (info.matrix * d->invMatrix);
+ if (lastWasNothing) {
+ region = QRegion(info.rectf.toRect()) * matrix;
+ lastWasNothing = false;
+ continue;
+ }
+ if (info.operation == Qt::IntersectClip)
+ region &= QRegion(info.rectf.toRect()) * matrix;
+ else if (info.operation == Qt::UniteClip)
+ region |= QRegion(info.rectf.toRect()) * matrix;
+ else if (info.operation == Qt::NoClip) {
+ lastWasNothing = true;
+ region = QRegion();
+ } else
+ region = QRegion(info.rectf.toRect()) * matrix;
+ break;
+ }
+ }
+ }
+
+ return region;
+}
+
+extern QPainterPath qt_regionToPath(const QRegion &region);
+
+/*!
+ Returns the currently clip as a path. Note that the clip path is
+ given in logical coordinates.
+
+ \sa setClipPath(), clipRegion(), setClipping()
+*/
+QPainterPath QPainter::clipPath() const
+{
+ Q_D(const QPainter);
+
+ // ### Since we do not support path intersections and path unions yet,
+ // we just use clipRegion() here...
+ if (!d->engine) {
+ qWarning("QPainter::clipPath: Painter not active");
+ return QPainterPath();
+ }
+
+ // No clip, return empty
+ if (d->state->clipInfo.size() == 0) {
+ return QPainterPath();
+ } else {
+
+ // Update inverse matrix, used below.
+ if (!d->txinv)
+ const_cast<QPainter *>(this)->d_ptr->updateInvMatrix();
+
+ // For the simple case avoid conversion.
+ if (d->state->clipInfo.size() == 1
+ && d->state->clipInfo.at(0).clipType == QPainterClipInfo::PathClip) {
+ QTransform matrix = (d->state->clipInfo.at(0).matrix * d->invMatrix);
+ return d->state->clipInfo.at(0).path * matrix;
+
+ } else if (d->state->clipInfo.size() == 1
+ && d->state->clipInfo.at(0).clipType == QPainterClipInfo::RectClip) {
+ QTransform matrix = (d->state->clipInfo.at(0).matrix * d->invMatrix);
+ QPainterPath path;
+ path.addRect(d->state->clipInfo.at(0).rect);
+ return path * matrix;
+ } else {
+ // Fallback to clipRegion() for now, since we don't have isect/unite for paths
+ return qt_regionToPath(clipRegion());
+ }
+ }
+}
+
+/*!
+ \fn void QPainter::setClipRect(const QRectF &rectangle, Qt::ClipOperation operation)
+
+ Enables clipping, and sets the clip region to the given \a
+ rectangle using the given clip \a operation. The default operation
+ is to replace the current clip rectangle.
+
+ Note that the clip rectangle is specified in logical (painter)
+ coordinates.
+
+ \sa clipRegion(), setClipping(), {QPainter#Clipping}{Clipping}
+*/
+void QPainter::setClipRect(const QRectF &rect, Qt::ClipOperation op)
+{
+ Q_D(QPainter);
+
+ if (d->extended) {
+ if (!d->engine) {
+ qWarning("QPainter::setClipRect: Painter not active");
+ return;
+ }
+ qreal right = rect.x() + rect.width();
+ qreal bottom = rect.y() + rect.height();
+ qreal pts[] = { rect.x(), rect.y(),
+ right, rect.y(),
+ right, bottom,
+ rect.x(), bottom };
+ QVectorPath vp(pts, 4, 0, QVectorPath::RectangleHint);
+ d->state->clipEnabled = true;
+ d->extended->clip(vp, op);
+ d->state->clipInfo << QPainterClipInfo(rect, op, combinedTransform());
+ d->state->clipOperation = op;
+ return;
+ }
+
+ if (qreal(int(rect.top())) == rect.top()
+ && qreal(int(rect.bottom())) == rect.bottom()
+ && qreal(int(rect.left())) == rect.left()
+ && qreal(int(rect.right())) == rect.right())
+ {
+ setClipRect(rect.toRect(), op);
+ return;
+ }
+
+ if (rect.isEmpty()) {
+ setClipRegion(QRegion(), op);
+ return;
+ }
+
+ QPainterPath path;
+ path.addRect(rect);
+ setClipPath(path, op);
+}
+
+/*!
+ \fn void QPainter::setClipRect(const QRect &rectangle, Qt::ClipOperation operation)
+ \overload
+
+ Enables clipping, and sets the clip region to the given \a rectangle using the given
+ clip \a operation.
+*/
+void QPainter::setClipRect(const QRect &rect, Qt::ClipOperation op)
+{
+ Q_D(QPainter);
+
+ if (!d->engine) {
+ qWarning("QPainter::setClipRect: Painter not active");
+ return;
+ }
+
+ if (d->extended) {
+ d->state->clipEnabled = true;
+ d->extended->clip(rect, op);
+ d->state->clipInfo << QPainterClipInfo(rect, op, combinedTransform());
+ d->state->clipOperation = op;
+ return;
+ }
+
+ if (!hasClipping() && (op == Qt::IntersectClip || op == Qt::UniteClip))
+ op = Qt::ReplaceClip;
+
+ d->state->clipRegion = rect;
+ d->state->clipOperation = op;
+ if (op == Qt::NoClip || op == Qt::ReplaceClip)
+ d->state->clipInfo.clear();
+ d->state->clipInfo << QPainterClipInfo(rect, op, combinedTransform());
+ d->state->clipEnabled = true;
+ d->state->dirtyFlags |= QPaintEngine::DirtyClipRegion | QPaintEngine::DirtyClipEnabled;
+ d->updateState(d->state);
+}
+
+/*!
+ \fn void QPainter::setClipRect(int x, int y, int width, int height, Qt::ClipOperation operation)
+
+ Enables clipping, and sets the clip region to the rectangle beginning at (\a x, \a y)
+ with the given \a width and \a height.
+*/
+
+/*!
+ \fn void QPainter::setClipRegion(const QRegion &region, Qt::ClipOperation operation)
+
+ Sets the clip region to the given \a region using the specified clip
+ \a operation. The default clip operation is to replace the current
+ clip region.
+
+ Note that the clip region is given in logical coordinates.
+
+ \sa clipRegion(), setClipRect(), {QPainter#Clipping}{Clipping}
+*/
+void QPainter::setClipRegion(const QRegion &r, Qt::ClipOperation op)
+{
+ Q_D(QPainter);
+#ifdef QT_DEBUG_DRAW
+ QRect rect = r.boundingRect();
+ if (qt_show_painter_debug_output)
+ printf("QPainter::setClipRegion(), size=%d, [%d,%d,%d,%d]\n",
+ r.rects().size(), rect.x(), rect.y(), rect.width(), rect.height());
+#endif
+ if (!d->engine) {
+ qWarning("QPainter::setClipRegion: Painter not active");
+ return;
+ }
+
+ if (d->extended) {
+ d->state->clipEnabled = true;
+ d->extended->clip(r, op);
+ d->state->clipInfo << QPainterClipInfo(r, op, combinedTransform());
+ d->state->clipOperation = op;
+ return;
+ }
+
+ if (!hasClipping() && (op == Qt::IntersectClip || op == Qt::UniteClip))
+ op = Qt::ReplaceClip;
+
+ d->state->clipRegion = r;
+ d->state->clipOperation = op;
+ if (op == Qt::NoClip || op == Qt::ReplaceClip)
+ d->state->clipInfo.clear();
+ d->state->clipInfo << QPainterClipInfo(r, op, combinedTransform());
+ d->state->clipEnabled = true;
+ d->state->dirtyFlags |= QPaintEngine::DirtyClipRegion | QPaintEngine::DirtyClipEnabled;
+ d->updateState(d->state);
+}
+
+/*!
+ \since 4.2
+
+ Sets the transformation matrix to \a matrix and enables transformations.
+
+ \note It is advisable to use setWorldTransform() instead of this function to
+ preserve the properties of perspective transformations.
+
+ If \a combine is true, then \a matrix is combined with the current
+ transformation matrix; otherwise \a matrix replaces the current
+ transformation matrix.
+
+ If \a matrix is the identity matrix and \a combine is false, this
+ function calls setWorldMatrixEnabled(false). (The identity matrix is the
+ matrix where QMatrix::m11() and QMatrix::m22() are 1.0 and the
+ rest are 0.0.)
+
+ The following functions can transform the coordinate system without using
+ a QMatrix:
+ \list
+ \i translate()
+ \i scale()
+ \i shear()
+ \i rotate()
+ \endlist
+
+ They operate on the painter's worldMatrix() and are implemented like this:
+
+ \snippet doc/src/snippets/code/src_gui_painting_qpainter.cpp 4
+
+ Note that when using setWorldMatrix() function you should always have
+ \a combine be true when you are drawing into a QPicture. Otherwise
+ it may not be possible to replay the picture with additional
+ transformations; using the translate(), scale(), etc. convenience
+ functions is safe.
+
+ For more information about the coordinate system, transformations
+ and window-viewport conversion, see \l {The Coordinate System}
+ documentation.
+
+ \sa worldMatrixEnabled(), QMatrix
+*/
+
+void QPainter::setWorldMatrix(const QMatrix &matrix, bool combine)
+{
+ setWorldTransform(QTransform(matrix), combine);
+}
+
+/*!
+ \since 4.2
+
+ Returns the world transformation matrix.
+
+ It is advisable to use worldTransform() because worldMatrix() does not
+ preserve the properties of perspective transformations.
+
+ \sa {QPainter#Coordinate Transformations}{Coordinate Transformations},
+ {The Coordinate System}
+*/
+
+const QMatrix &QPainter::worldMatrix() const
+{
+ Q_D(const QPainter);
+ if (!d->engine) {
+ qWarning("QPainter::worldMatrix: Painter not active");
+ return d->fakeState()->transform.toAffine();
+ }
+ return d->state->worldMatrix.toAffine();
+}
+
+/*!
+ \obsolete
+
+ Use setWorldTransform() instead.
+
+ \sa setWorldTransform()
+*/
+
+void QPainter::setMatrix(const QMatrix &matrix, bool combine)
+{
+ setWorldTransform(QTransform(matrix), combine);
+}
+
+/*!
+ \obsolete
+
+ Use worldTransform() instead.
+
+ \sa worldTransform()
+*/
+
+const QMatrix &QPainter::matrix() const
+{
+ return worldMatrix();
+}
+
+
+/*!
+ \since 4.2
+
+ Returns the transformation matrix combining the current
+ window/viewport and world transformation.
+
+ It is advisable to use combinedTransform() instead of this
+ function to preserve the properties of perspective transformations.
+
+ \sa setWorldMatrix(), setWindow(), setViewport()
+*/
+QMatrix QPainter::combinedMatrix() const
+{
+ return combinedTransform().toAffine();
+}
+
+
+/*!
+ Returns the matrix that transforms from logical coordinates to
+ device coordinates of the platform dependent paint device.
+
+ \note It is advisable to use deviceTransform() instead of this
+ function to preserve the properties of perspective transformations.
+
+ This function is \e only needed when using platform painting
+ commands on the platform dependent handle (Qt::HANDLE), and the
+ platform does not do transformations nativly.
+
+ The QPaintEngine::PaintEngineFeature enum can be queried to
+ determine whether the platform performs the transformations or
+ not.
+
+ \sa worldMatrix(), QPaintEngine::hasFeature(),
+*/
+const QMatrix &QPainter::deviceMatrix() const
+{
+ Q_D(const QPainter);
+ if (!d->engine) {
+ qWarning("QPainter::deviceMatrix: Painter not active");
+ return d->fakeState()->transform.toAffine();
+ }
+ return d->state->matrix.toAffine();
+}
+
+/*!
+ Resets any transformations that were made using translate(), scale(),
+ shear(), rotate(), setWorldMatrix(), setViewport() and
+ setWindow().
+
+ It is advisable to use resetTransform() instead of this function
+ to preserve the properties of perspective transformations.
+
+ \sa {QPainter#Coordinate Transformations}{Coordinate
+ Transformations}
+*/
+
+void QPainter::resetMatrix()
+{
+ resetTransform();
+}
+
+
+/*!
+ \since 4.2
+
+ Enables transformations if \a enable is true, or disables
+ transformations if \a enable is false. The world transformation
+ matrix is not changed.
+
+ \sa worldMatrixEnabled(), worldMatrix(), {QPainter#Coordinate
+ Transformations}{Coordinate Transformations}
+*/
+
+void QPainter::setWorldMatrixEnabled(bool enable)
+{
+ Q_D(QPainter);
+#ifdef QT_DEBUG_DRAW
+ if (qt_show_painter_debug_output)
+ printf("QPainter::setMatrixEnabled(), enable=%d\n", enable);
+#endif
+
+ if (!d->engine) {
+ qWarning("QPainter::setMatrixEnabled: Painter not active");
+ return;
+ }
+ if (enable == d->state->WxF)
+ return;
+
+ d->state->WxF = enable;
+ d->updateMatrix();
+}
+
+/*!
+ \since 4.2
+
+ Returns true if world transformation is enabled; otherwise returns
+ false.
+
+ \sa setWorldMatrixEnabled(), worldMatrix(), {The Coordinate System}
+*/
+
+bool QPainter::worldMatrixEnabled() const
+{
+ Q_D(const QPainter);
+ if (!d->engine) {
+ qWarning("QPainter::worldMatrixEnabled: Painter not active");
+ return false;
+ }
+ return d->state->WxF;
+}
+
+/*!
+ \obsolete
+
+ Use setWorldMatrixEnabled() instead.
+
+ \sa setWorldMatrixEnabled()
+*/
+
+void QPainter::setMatrixEnabled(bool enable)
+{
+ setWorldMatrixEnabled(enable);
+}
+
+/*!
+ \obsolete
+
+ Use worldMatrixEnabled() instead
+
+ \sa worldMatrixEnabled()
+*/
+
+bool QPainter::matrixEnabled() const
+{
+ return worldMatrixEnabled();
+}
+
+/*!
+ Scales the coordinate system by (\a{sx}, \a{sy}).
+
+ \sa setWorldMatrix() {QPainter#Coordinate Transformations}{Coordinate
+ Transformations}
+*/
+
+void QPainter::scale(qreal sx, qreal sy)
+{
+#ifdef QT_DEBUG_DRAW
+ if (qt_show_painter_debug_output)
+ printf("QPainter::scale(), sx=%f, sy=%f\n", sx, sy);
+#endif
+ Q_D(QPainter);
+ if (!d->engine) {
+ qWarning("QPainter::scale: Painter not active");
+ return;
+ }
+
+ d->state->worldMatrix.scale(sx,sy);
+ d->state->WxF = true;
+ d->updateMatrix();
+}
+
+/*!
+ Shears the coordinate system by (\a{sh}, \a{sv}).
+
+ \sa setWorldMatrix(), {QPainter#Coordinate Transformations}{Coordinate
+ Transformations}
+*/
+
+void QPainter::shear(qreal sh, qreal sv)
+{
+#ifdef QT_DEBUG_DRAW
+ if (qt_show_painter_debug_output)
+ printf("QPainter::shear(), sh=%f, sv=%f\n", sh, sv);
+#endif
+ Q_D(QPainter);
+ if (!d->engine) {
+ qWarning("QPainter::shear: Painter not active");
+ return;
+ }
+
+ d->state->worldMatrix.shear(sh, sv);
+ d->state->WxF = true;
+ d->updateMatrix();
+}
+
+/*!
+ \fn void QPainter::rotate(qreal angle)
+
+ Rotates the coordinate system the given \a angle clockwise.
+
+ \sa setWorldMatrix(), {QPainter#Coordinate Transformations}{Coordinate
+ Transformations}
+*/
+
+void QPainter::rotate(qreal a)
+{
+#ifdef QT_DEBUG_DRAW
+ if (qt_show_painter_debug_output)
+ printf("QPainter::rotate(), angle=%f\n", a);
+#endif
+ Q_D(QPainter);
+ if (!d->engine) {
+ qWarning("QPainter::rotate: Painter not active");
+ return;
+ }
+
+ d->state->worldMatrix.rotate(a);
+ d->state->WxF = true;
+ d->updateMatrix();
+}
+
+/*!
+ Translates the coordinate system by the given \a offset; i.e. the
+ given \a offset is added to points.
+
+ \sa setWorldMatrix(), {QPainter#Coordinate Transformations}{Coordinate
+ Transformations}
+*/
+void QPainter::translate(const QPointF &offset)
+{
+ qreal dx = offset.x();
+ qreal dy = offset.y();
+#ifdef QT_DEBUG_DRAW
+ if (qt_show_painter_debug_output)
+ printf("QPainter::translate(), dx=%f, dy=%f\n", dx, dy);
+#endif
+ Q_D(QPainter);
+ if (!d->engine) {
+ qWarning("QPainter::translate: Painter not active");
+ return;
+ }
+
+ d->state->worldMatrix.translate(dx, dy);
+ d->state->WxF = true;
+ d->updateMatrix();
+}
+
+/*!
+ \fn void QPainter::translate(const QPoint &offset)
+ \overload
+
+ Translates the coordinate system by the given \a offset.
+*/
+
+/*!
+ \fn void QPainter::translate(qreal dx, qreal dy)
+ \overload
+
+ Translates the coordinate system by the vector (\a dx, \a dy).
+*/
+
+/*!
+ \fn void QPainter::setClipPath(const QPainterPath &path, Qt::ClipOperation operation)
+
+ Enables clipping, and sets the clip path for the painter to the
+ given \a path, with the clip \a operation.
+
+ Note that the clip path is specified in logical (painter)
+ coordinates.
+
+ \sa clipPath(), clipRegion(), {QPainter#Clipping}{Clipping}
+
+*/
+void QPainter::setClipPath(const QPainterPath &path, Qt::ClipOperation op)
+{
+#ifdef QT_DEBUG_DRAW
+ if (qt_show_painter_debug_output) {
+ QRectF b = path.boundingRect();
+ printf("QPainter::setClipPath(), size=%d, op=%d, bounds=[%.2f,%.2f,%.2f,%.2f]\n",
+ path.elementCount(), op, b.x(), b.y(), b.width(), b.height());
+ }
+#endif
+ Q_D(QPainter);
+
+ if (!d->engine) {
+ qWarning("QPainter::setClipPath: Painter not active");
+ return;
+ }
+
+ if (d->extended) {
+ d->state->clipEnabled = true;
+ d->extended->clip(path, op);
+ d->state->clipInfo << QPainterClipInfo(path, op, combinedTransform());
+ d->state->clipOperation = op;
+ return;
+ }
+
+
+
+ if (!hasClipping() && (op == Qt::IntersectClip || op == Qt::UniteClip))
+ op = Qt::ReplaceClip;
+
+ d->state->clipPath = path;
+ d->state->clipOperation = op;
+ if (op == Qt::NoClip || op == Qt::ReplaceClip)
+ d->state->clipInfo.clear();
+ d->state->clipInfo << QPainterClipInfo(path, op, combinedTransform());
+ d->state->clipEnabled = true;
+ d->state->dirtyFlags |= QPaintEngine::DirtyClipPath | QPaintEngine::DirtyClipEnabled;
+ d->updateState(d->state);
+}
+
+/*!
+ Draws the outline (strokes) the path \a path with the pen specified
+ by \a pen
+
+ \sa fillPath(), {QPainter#Drawing}{Drawing}
+*/
+void QPainter::strokePath(const QPainterPath &path, const QPen &pen)
+{
+ Q_D(QPainter);
+
+ if (!d->engine) {
+ qWarning("QPainter::strokePath: Painter not active");
+ return;
+ }
+
+ if (path.isEmpty())
+ return;
+
+ if (d->extended) {
+ const QGradient *g = qpen_brush(pen).gradient();
+ if (!g || g->coordinateMode() == QGradient::LogicalMode) {
+ d->extended->stroke(qtVectorPathForPath(path), pen);
+ return;
+ }
+ }
+
+ QBrush oldBrush = d->state->brush;
+ QPen oldPen = d->state->pen;
+
+ setPen(pen);
+ setBrush(Qt::NoBrush);
+
+ drawPath(path);
+
+ // Reset old state
+ setPen(oldPen);
+ setBrush(oldBrush);
+}
+
+/*!
+ Fills the given \a path using the given \a brush. The outline is
+ not drawn.
+
+ Alternatively, you can specify a QColor instead of a QBrush; the
+ QBrush constructor (taking a QColor argument) will automatically
+ create a solid pattern brush.
+
+ \sa drawPath()
+*/
+void QPainter::fillPath(const QPainterPath &path, const QBrush &brush)
+{
+ Q_D(QPainter);
+
+ if (!d->engine) {
+ qWarning("QPainter::fillPath: Painter not active");
+ return;
+ }
+
+ if (path.isEmpty())
+ return;
+
+ if (d->extended) {
+ const QGradient *g = brush.gradient();
+ if (!g || g->coordinateMode() == QGradient::LogicalMode) {
+ d->extended->fill(qtVectorPathForPath(path), brush);
+ return;
+ }
+ }
+
+ QBrush oldBrush = d->state->brush;
+ QPen oldPen = d->state->pen;
+
+ setPen(Qt::NoPen);
+ setBrush(brush);
+
+ drawPath(path);
+
+ // Reset old state
+ setPen(oldPen);
+ setBrush(oldBrush);
+}
+
+/*!
+ Draws the given painter \a path using the current pen for outline
+ and the current brush for filling.
+
+ \table 100%
+ \row
+ \o \inlineimage qpainter-path.png
+ \o
+ \snippet doc/src/snippets/code/src_gui_painting_qpainter.cpp 5
+ \endtable
+
+ \sa {painting/painterpaths}{the Painter Paths
+ example},{demos/deform}{the Vector Deformation demo}
+*/
+void QPainter::drawPath(const QPainterPath &path)
+{
+#ifdef QT_DEBUG_DRAW
+ QRectF pathBounds = path.boundingRect();
+ if (qt_show_painter_debug_output)
+ printf("QPainter::drawPath(), size=%d, [%.2f,%.2f,%.2f,%.2f]\n",
+ path.elementCount(),
+ pathBounds.x(), pathBounds.y(), pathBounds.width(), pathBounds.height());
+#endif
+
+ Q_D(QPainter);
+
+ if (!d->engine) {
+ qWarning("QPainter::drawPath: Painter not active");
+ return;
+ }
+
+ if (d->extended) {
+ d->extended->drawPath(path);
+ return;
+ }
+ d->updateState(d->state);
+
+ if (d->engine->hasFeature(QPaintEngine::PainterPaths) && d->state->emulationSpecifier == 0) {
+ d->engine->drawPath(path);
+ } else {
+ d->draw_helper(path);
+ }
+}
+
+/*!
+ \fn void QPainter::drawLine(const QLineF &line)
+
+ Draws a line defined by \a line.
+
+ \table 100%
+ \row
+ \o \inlineimage qpainter-line.png
+ \o
+ \snippet doc/src/snippets/code/src_gui_painting_qpainter.cpp 6
+ \endtable
+
+ \sa drawLines(), drawPolyline(), {The Coordinate System}
+*/
+
+/*!
+ \fn void QPainter::drawLine(const QLine &line)
+ \overload
+
+ Draws a line defined by \a line.
+*/
+
+/*!
+ \fn void QPainter::drawLine(const QPoint &p1, const QPoint &p2)
+ \overload
+
+ Draws a line from \a p1 to \a p2.
+*/
+
+/*!
+ \fn void QPainter::drawLine(const QPointF &p1, const QPointF &p2)
+ \overload
+
+ Draws a line from \a p1 to \a p2.
+*/
+
+/*!
+ \fn void QPainter::drawLine(int x1, int y1, int x2, int y2)
+ \overload
+
+ Draws a line from (\a x1, \a y1) to (\a x2, \a y2) and sets the
+ current pen position to (\a x2, \a y2).
+*/
+
+/*!
+ \fn void QPainter::drawRect(const QRectF &rectangle)
+
+ Draws the current \a rectangle with the current pen and brush.
+
+ A filled rectangle has a size of \a{rectangle}.size(). A stroked
+ rectangle has a size of \a{rectangle}.size() plus the pen width.
+
+ \table 100%
+ \row
+ \o \inlineimage qpainter-rectangle.png
+ \o
+ \snippet doc/src/snippets/code/src_gui_painting_qpainter.cpp 7
+ \endtable
+
+ \sa drawRects(), drawPolygon(), {The Coordinate System}
+*/
+
+/*!
+ \fn void QPainter::drawRect(const QRect &rectangle)
+
+ \overload
+
+ Draws the current \a rectangle with the current pen and brush.
+*/
+
+/*!
+ \fn void QPainter::drawRect(int x, int y, int width, int height)
+
+ \overload
+
+ Draws a rectangle with upper left corner at (\a{x}, \a{y}) and
+ with the given \a width and \a height.
+*/
+
+/*!
+ \fn void QPainter::drawRects(const QRectF *rectangles, int rectCount)
+
+ Draws the first \a rectCount of the given \a rectangles using the
+ current pen and brush.
+
+ \sa drawRect()
+*/
+void QPainter::drawRects(const QRectF *rects, int rectCount)
+{
+#ifdef QT_DEBUG_DRAW
+ if (qt_show_painter_debug_output)
+ printf("QPainter::drawRects(), count=%d\n", rectCount);
+#endif
+ Q_D(QPainter);
+
+ if (!d->engine) {
+ qWarning("QPainter::drawRects: Painter not active");
+ return;
+ }
+
+ if (rectCount <= 0)
+ return;
+
+ if (d->extended) {
+ d->extended->drawRects(rects, rectCount);
+ return;
+ }
+
+ d->updateState(d->state);
+
+ if (!d->state->emulationSpecifier) {
+ d->engine->drawRects(rects, rectCount);
+ return;
+ }
+
+ if (d->state->emulationSpecifier == QPaintEngine::PrimitiveTransform
+ && d->state->matrix.type() == QTransform::TxTranslate) {
+ for (int i=0; i<rectCount; ++i) {
+ QRectF r(rects[i].x() + d->state->matrix.dx(),
+ rects[i].y() + d->state->matrix.dy(),
+ rects[i].width(),
+ rects[i].height());
+ d->engine->drawRects(&r, 1);
+ }
+ } else {
+ if (d->state->brushNeedsResolving() || d->state->penNeedsResolving()) {
+ for (int i=0; i<rectCount; ++i) {
+ QPainterPath rectPath;
+ rectPath.addRect(rects[i]);
+ d->draw_helper(rectPath, QPainterPrivate::StrokeAndFillDraw);
+ }
+ } else {
+ QPainterPath rectPath;
+ for (int i=0; i<rectCount; ++i)
+ rectPath.addRect(rects[i]);
+ d->draw_helper(rectPath, QPainterPrivate::StrokeAndFillDraw);
+ }
+ }
+}
+
+/*!
+ \fn void QPainter::drawRects(const QRect *rectangles, int rectCount)
+ \overload
+
+ Draws the first \a rectCount of the given \a rectangles using the
+ current pen and brush.
+*/
+void QPainter::drawRects(const QRect *rects, int rectCount)
+{
+#ifdef QT_DEBUG_DRAW
+ if (qt_show_painter_debug_output)
+ printf("QPainter::drawRects(), count=%d\n", rectCount);
+#endif
+ Q_D(QPainter);
+
+ if (!d->engine) {
+ qWarning("QPainter::drawRects: Painter not active");
+ return;
+ }
+
+ if (rectCount <= 0)
+ return;
+
+ if (d->extended) {
+ d->extended->drawRects(rects, rectCount);
+ return;
+ }
+
+ d->updateState(d->state);
+
+ if (!d->state->emulationSpecifier) {
+ d->engine->drawRects(rects, rectCount);
+ return;
+ }
+
+ if (d->state->emulationSpecifier == QPaintEngine::PrimitiveTransform
+ && d->state->matrix.type() == QTransform::TxTranslate) {
+ for (int i=0; i<rectCount; ++i) {
+ QRectF r(rects[i].x() + d->state->matrix.dx(),
+ rects[i].y() + d->state->matrix.dy(),
+ rects[i].width(),
+ rects[i].height());
+
+ d->engine->drawRects(&r, 1);
+ }
+ } else {
+ if (d->state->brushNeedsResolving() || d->state->penNeedsResolving()) {
+ for (int i=0; i<rectCount; ++i) {
+ QPainterPath rectPath;
+ rectPath.addRect(rects[i]);
+ d->draw_helper(rectPath, QPainterPrivate::StrokeAndFillDraw);
+ }
+ } else {
+ QPainterPath rectPath;
+ for (int i=0; i<rectCount; ++i)
+ rectPath.addRect(rects[i]);
+
+ d->draw_helper(rectPath, QPainterPrivate::StrokeAndFillDraw);
+ }
+ }
+}
+
+/*!
+ \fn void QPainter::drawRects(const QVector<QRectF> &rectangles)
+ \overload
+
+ Draws the given \a rectangles using the current pen and brush.
+*/
+
+/*!
+ \fn void QPainter::drawRects(const QVector<QRect> &rectangles)
+
+ \overload
+
+ Draws the given \a rectangles using the current pen and brush.
+*/
+
+/*!
+ \fn void QPainter::drawPoint(const QPointF &position)
+
+ Draws a single point at the given \a position using the current
+ pen's color.
+
+ \sa {The Coordinate System}
+*/
+
+/*!
+ \fn void QPainter::drawPoint(const QPoint &position)
+ \overload
+
+ Draws a single point at the given \a position using the current
+ pen's color.
+*/
+
+/*! \fn void QPainter::drawPoint(int x, int y)
+
+ \overload
+
+ Draws a single point at position (\a x, \a y).
+*/
+
+/*!
+ Draws the first \a pointCount points in the array \a points using
+ the current pen's color.
+
+ \sa {The Coordinate System}
+*/
+void QPainter::drawPoints(const QPointF *points, int pointCount)
+{
+#ifdef QT_DEBUG_DRAW
+ if (qt_show_painter_debug_output)
+ printf("QPainter::drawPoints(), count=%d\n", pointCount);
+#endif
+ Q_D(QPainter);
+
+ if (!d->engine) {
+ qWarning("QPainter::drawPoints: Painter not active");
+ return;
+ }
+
+ if (pointCount <= 0)
+ return;
+
+ if (d->extended) {
+ d->extended->drawPoints(points, pointCount);
+ return;
+ }
+
+ d->updateState(d->state);
+
+ if (!d->state->emulationSpecifier) {
+ d->engine->drawPoints(points, pointCount);
+ return;
+ }
+
+ if (d->state->emulationSpecifier == QPaintEngine::PrimitiveTransform
+ && d->state->matrix.type() == QTransform::TxTranslate) {
+ // ### use drawPoints function
+ for (int i=0; i<pointCount; ++i) {
+ QPointF pt(points[i].x() + d->state->matrix.dx(),
+ points[i].y() + d->state->matrix.dy());
+ d->engine->drawPoints(&pt, 1);
+ }
+ } else {
+ QPen pen = d->state->pen;
+ bool flat_pen = pen.capStyle() == Qt::FlatCap;
+ if (flat_pen) {
+ save();
+ pen.setCapStyle(Qt::SquareCap);
+ setPen(pen);
+ }
+ QPainterPath path;
+ for (int i=0; i<pointCount; ++i) {
+ path.moveTo(points[i].x(), points[i].y());
+ path.lineTo(points[i].x() + 0.0001, points[i].y());
+ }
+ d->draw_helper(path, QPainterPrivate::StrokeDraw);
+ if (flat_pen)
+ restore();
+ }
+}
+
+/*!
+ \overload
+
+ Draws the first \a pointCount points in the array \a points using
+ the current pen's color.
+*/
+
+void QPainter::drawPoints(const QPoint *points, int pointCount)
+{
+#ifdef QT_DEBUG_DRAW
+ if (qt_show_painter_debug_output)
+ printf("QPainter::drawPoints(), count=%d\n", pointCount);
+#endif
+ Q_D(QPainter);
+
+ if (!d->engine) {
+ qWarning("QPainter::drawPoints: Painter not active");
+ return;
+ }
+
+ if (pointCount <= 0)
+ return;
+
+ if (d->extended) {
+ d->extended->drawPoints(points, pointCount);
+ return;
+ }
+
+ d->updateState(d->state);
+
+ if (!d->state->emulationSpecifier) {
+ d->engine->drawPoints(points, pointCount);
+ return;
+ }
+
+ if (d->state->emulationSpecifier == QPaintEngine::PrimitiveTransform
+ && d->state->matrix.type() == QTransform::TxTranslate) {
+ // ### use drawPoints function
+ for (int i=0; i<pointCount; ++i) {
+ QPointF pt(points[i].x() + d->state->matrix.dx(),
+ points[i].y() + d->state->matrix.dy());
+ d->engine->drawPoints(&pt, 1);
+ }
+ } else {
+ QPen pen = d->state->pen;
+ bool flat_pen = (pen.capStyle() == Qt::FlatCap);
+ if (flat_pen) {
+ save();
+ pen.setCapStyle(Qt::SquareCap);
+ setPen(pen);
+ }
+ QPainterPath path;
+ for (int i=0; i<pointCount; ++i) {
+ path.moveTo(points[i].x(), points[i].y());
+ path.lineTo(points[i].x() + 0.0001, points[i].y());
+ }
+ d->draw_helper(path, QPainterPrivate::StrokeDraw);
+ if (flat_pen)
+ restore();
+ }
+}
+
+/*!
+ \fn void QPainter::drawPoints(const QPolygonF &points)
+
+ \overload
+
+ Draws the points in the vector \a points.
+*/
+
+/*!
+ \fn void QPainter::drawPoints(const QPolygon &points)
+
+ \overload
+
+ Draws the points in the vector \a points.
+*/
+
+/*!
+ \fn void QPainter::drawPoints(const QPolygon &polygon, int index,
+ int count)
+
+ \overload
+ \compat
+
+ Draws \a count points in the vector \a polygon starting on \a index
+ using the current pen.
+
+ Use drawPoints() combined with QPolygon::constData() instead.
+
+ \oldcode
+ QPainter painter(this);
+ painter.drawPoints(polygon, index, count);
+ \newcode
+ int pointCount = (count == -1) ? polygon.size() - index : count;
+
+ QPainter painter(this);
+ painter.drawPoints(polygon.constData() + index, pointCount);
+ \endcode
+*/
+
+/*!
+ Sets the background mode of the painter to the given \a mode
+
+ Qt::TransparentMode (the default) draws stippled lines and text
+ without setting the background pixels. Qt::OpaqueMode fills these
+ space with the current background color.
+
+ Note that in order to draw a bitmap or pixmap transparently, you
+ must use QPixmap::setMask().
+
+ \sa backgroundMode(), setBackground(),
+ {QPainter#Settings}{Settings}
+*/
+
+void QPainter::setBackgroundMode(Qt::BGMode mode)
+{
+#ifdef QT_DEBUG_DRAW
+ if (qt_show_painter_debug_output)
+ printf("QPainter::setBackgroundMode(), mode=%d\n", mode);
+#endif
+
+ Q_D(QPainter);
+ if (!d->engine) {
+ qWarning("QPainter::setBackgroundMode: Painter not active");
+ return;
+ }
+ if (d->state->bgMode == mode)
+ return;
+
+ d->state->bgMode = mode;
+ if (d->extended) {
+ d->checkEmulation();
+ } else {
+ d->state->dirtyFlags |= QPaintEngine::DirtyBackgroundMode;
+ }
+}
+
+/*!
+ Returns the current background mode.
+
+ \sa setBackgroundMode(), {QPainter#Settings}{Settings}
+*/
+Qt::BGMode QPainter::backgroundMode() const
+{
+ Q_D(const QPainter);
+ if (!d->engine) {
+ qWarning("QPainter::backgroundMode: Painter not active");
+ return Qt::TransparentMode;
+ }
+ return d->state->bgMode;
+}
+
+
+/*!
+ \overload
+
+ Sets the painter's pen to have style Qt::SolidLine, width 0 and the
+ specified \a color.
+*/
+
+void QPainter::setPen(const QColor &color)
+{
+#ifdef QT_DEBUG_DRAW
+ if (qt_show_painter_debug_output)
+ printf("QPainter::setPen(), color=%04x\n", color.rgb());
+#endif
+ Q_D(QPainter);
+ if (!d->engine) {
+ qWarning("QPainter::setPen: Painter not active");
+ return;
+ }
+
+ if (d->state->pen.style() == Qt::SolidLine
+ && d->state->pen.widthF() == 0
+ && d->state->pen.isSolid()
+ && d->state->pen.color() == color)
+ return;
+
+ QPen pen(color.isValid() ? color : QColor(Qt::black), 0, Qt::SolidLine);
+
+ d->state->pen = pen;
+ if (d->extended)
+ d->extended->penChanged();
+ else
+ d->state->dirtyFlags |= QPaintEngine::DirtyPen;
+}
+
+/*!
+ Sets the painter's pen to be the given \a pen.
+
+ The \a pen defines how to draw lines and outlines, and it also
+ defines the text color.
+
+ \sa pen(), {QPainter#Settings}{Settings}
+*/
+
+void QPainter::setPen(const QPen &pen)
+{
+
+#ifdef QT_DEBUG_DRAW
+ if (qt_show_painter_debug_output)
+ printf("QPainter::setPen(), color=%04x, (brushStyle=%d) style=%d, cap=%d, join=%d\n",
+ pen.color().rgb(), pen.brush().style(), pen.style(), pen.capStyle(), pen.joinStyle());
+#endif
+ Q_D(QPainter);
+ if (!d->engine) {
+ qWarning("QPainter::setPen: Painter not active");
+ return;
+ }
+
+ if (d->state->pen == pen)
+ return;
+
+ if (d->extended) {
+ d->state->pen = pen;
+ d->checkEmulation();
+ d->extended->penChanged();
+ return;
+ }
+
+ // Do some checks to see if we are the same pen.
+ Qt::PenStyle currentStyle = d->state->pen.style();
+ if (currentStyle == pen.style() && currentStyle != Qt::CustomDashLine) {
+ if (currentStyle == Qt::NoPen ||
+ (d->state->pen.isSolid() && pen.isSolid()
+ && d->state->pen.color() == pen.color()
+ && d->state->pen.widthF() == pen.widthF()
+ && d->state->pen.capStyle() == pen.capStyle()
+ && d->state->pen.joinStyle() == pen.joinStyle()
+ && d->state->pen.isCosmetic() == pen.isCosmetic()))
+ return;
+ }
+
+ d->state->pen = pen;
+ d->state->dirtyFlags |= QPaintEngine::DirtyPen;
+}
+
+/*!
+ \overload
+
+ Sets the painter's pen to have the given \a style, width 0 and
+ black color.
+*/
+
+void QPainter::setPen(Qt::PenStyle style)
+{
+ Q_D(QPainter);
+ if (!d->engine) {
+ qWarning("QPainter::setPen: Painter not active");
+ return;
+ }
+
+ if (d->state->pen.style() == style
+ && (style == Qt::NoPen || (d->state->pen.widthF() == 0
+ && d->state->pen.isSolid()
+ && d->state->pen.color() == QColor(Qt::black))))
+ return;
+
+ // QPen(Qt::NoPen) is to avoid creating QPenData, including its brush (from the color)
+ // Note that this works well as long as QPen(Qt::NoPen) returns a black, zero-width pen
+ d->state->pen = (style == Qt::NoPen) ? QPen(Qt::NoPen) : QPen(Qt::black, 0, style);
+
+ if (d->extended)
+ d->extended->penChanged();
+ else
+ d->state->dirtyFlags |= QPaintEngine::DirtyPen;
+
+}
+
+/*!
+ Returns the painter's current pen.
+
+ \sa setPen(), {QPainter#Settings}{Settings}
+*/
+
+const QPen &QPainter::pen() const
+{
+ Q_D(const QPainter);
+ if (!d->engine) {
+ qWarning("QPainter::pen: Painter not active");
+ return d->fakeState()->pen;
+ }
+ return d->state->pen;
+}
+
+
+/*!
+ Sets the painter's brush to the given \a brush.
+
+ The painter's brush defines how shapes are filled.
+
+ \sa brush(), {QPainter#Settings}{Settings}
+*/
+
+void QPainter::setBrush(const QBrush &brush)
+{
+#ifdef QT_DEBUG_DRAW
+ if (qt_show_painter_debug_output)
+ printf("QPainter::setBrush(), color=%04x, style=%d\n", brush.color().rgb(), brush.style());
+#endif
+ Q_D(QPainter);
+ if (!d->engine) {
+ qWarning("QPainter::setBrush: Painter not active");
+ return;
+ }
+
+ if (d->state->brush.d == brush.d)
+ return;
+
+ if (d->extended) {
+ d->state->brush = brush;
+ d->checkEmulation();
+ d->extended->brushChanged();
+ return;
+ }
+
+ Qt::BrushStyle currentStyle = d->state->brush.style();
+ if (currentStyle == brush.style()) {
+ if (currentStyle == Qt::NoBrush
+ || (currentStyle == Qt::SolidPattern
+ && d->state->brush.color() == brush.color()))
+ return;
+ }
+
+ d->state->brush = brush;
+ d->state->dirtyFlags |= QPaintEngine::DirtyBrush;
+}
+
+
+/*!
+ \overload
+
+ Sets the painter's brush to black color and the specified \a
+ style.
+*/
+
+void QPainter::setBrush(Qt::BrushStyle style)
+{
+ Q_D(QPainter);
+ if (!d->engine) {
+ qWarning("QPainter::setBrush: Painter not active");
+ return;
+ }
+ if (d->state->brush.style() == style &&
+ (style == Qt::NoBrush
+ || (style == Qt::SolidPattern && d->state->brush.color() == QColor(0, 0, 0))))
+ return;
+ d->state->brush = QBrush(Qt::black, style);
+ if (d->extended)
+ d->extended->brushChanged();
+ else
+ d->state->dirtyFlags |= QPaintEngine::DirtyBrush;
+}
+
+/*!
+ Returns the painter's current brush.
+
+ \sa QPainter::setBrush(), {QPainter#Settings}{Settings}
+*/
+
+const QBrush &QPainter::brush() const
+{
+ Q_D(const QPainter);
+ if (!d->engine) {
+ qWarning("QPainter::brush: Painter not active");
+ return d->fakeState()->brush;
+ }
+ return d->state->brush;
+}
+
+/*!
+ \fn void QPainter::setBackground(const QBrush &brush)
+
+ Sets the background brush of the painter to the given \a brush.
+
+ The background brush is the brush that is filled in when drawing
+ opaque text, stippled lines and bitmaps. The background brush has
+ no effect in transparent background mode (which is the default).
+
+ \sa background(), setBackgroundMode(),
+ {QPainter#Settings}{Settings}
+*/
+
+void QPainter::setBackground(const QBrush &bg)
+{
+#ifdef QT_DEBUG_DRAW
+ if (qt_show_painter_debug_output)
+ printf("QPainter::setBackground(), color=%04x, style=%d\n", bg.color().rgb(), bg.style());
+#endif
+
+ Q_D(QPainter);
+ if (!d->engine) {
+ qWarning("QPainter::setBackground: Painter not active");
+ return;
+ }
+ d->state->bgBrush = bg;
+ if (!d->extended)
+ d->state->dirtyFlags |= QPaintEngine::DirtyBackground;
+}
+
+/*!
+ Sets the painter's font to the given \a font.
+
+ This font is used by subsequent drawText() functions. The text
+ color is the same as the pen color.
+
+ If you set a font that isn't available, Qt finds a close match.
+ font() will return what you set using setFont() and fontInfo() returns the
+ font actually being used (which may be the same).
+
+ \sa font(), drawText(), {QPainter#Settings}{Settings}
+*/
+
+void QPainter::setFont(const QFont &font)
+{
+ Q_D(QPainter);
+
+#ifdef QT_DEBUG_DRAW
+ if (qt_show_painter_debug_output)
+ printf("QPainter::setFont(), family=%s, pointSize=%d\n", font.family().toLatin1().constData(), font.pointSize());
+#endif
+
+ if (!d->engine) {
+ qWarning("QPainter::setFont: Painter not active");
+ return;
+ }
+
+ d->state->font = QFont(font.resolve(d->state->deviceFont), device());
+ if (!d->extended)
+ d->state->dirtyFlags |= QPaintEngine::DirtyFont;
+}
+
+/*!
+ Returns the currently set font used for drawing text.
+
+ \sa setFont(), drawText(), {QPainter#Settings}{Settings}
+*/
+const QFont &QPainter::font() const
+{
+ Q_D(const QPainter);
+ if (!d->engine) {
+ qWarning("QPainter::font: Painter not active");
+ return d->fakeState()->font;
+ }
+ return d->state->font;
+}
+
+/*!
+ \since 4.4
+
+ Draws the given rectangle \a rect with rounded corners.
+
+ The \a xRadius and \a yRadius arguments specify the radii
+ of the ellipses defining the corners of the rounded rectangle.
+ When \a mode is Qt::RelativeSize, \a xRadius and
+ \a yRadius are specified in percentage of half the rectangle's
+ width and height respectively, and should be in the range
+ 0.0 to 100.0.
+
+ A filled rectangle has a size of rect.size(). A stroked rectangle
+ has a size of rect.size() plus the pen width.
+
+ \table 100%
+ \row
+ \o \inlineimage qpainter-roundrect.png
+ \o
+ \snippet doc/src/snippets/code/src_gui_painting_qpainter.cpp 8
+ \endtable
+
+ \sa drawRect(), QPen
+*/
+// FALCON: Should we add a specialized method in QPaintEngineEx?
+void QPainter::drawRoundedRect(const QRectF &rect, qreal xRadius, qreal yRadius, Qt::SizeMode mode)
+{
+#ifdef QT_DEBUG_DRAW
+ if (qt_show_painter_debug_output)
+ printf("QPainter::drawRoundedRect(), [%.2f,%.2f,%.2f,%.2f]\n", rect.x(), rect.y(), rect.width(), rect.height());
+#endif
+ Q_D(QPainter);
+
+ if (!d->engine)
+ return;
+
+ if (xRadius <= 0 || yRadius <= 0) { // draw normal rectangle
+ drawRect(rect);
+ return;
+ }
+
+ if (d->extended) {
+ QPainterPath::ElementType types[] = {
+ QPainterPath::MoveToElement,
+ QPainterPath::LineToElement,
+ QPainterPath::CurveToElement,
+ QPainterPath::CurveToDataElement,
+ QPainterPath::CurveToDataElement,
+ QPainterPath::LineToElement,
+ QPainterPath::CurveToElement,
+ QPainterPath::CurveToDataElement,
+ QPainterPath::CurveToDataElement,
+ QPainterPath::LineToElement,
+ QPainterPath::CurveToElement,
+ QPainterPath::CurveToDataElement,
+ QPainterPath::CurveToDataElement,
+ QPainterPath::LineToElement,
+ QPainterPath::CurveToElement,
+ QPainterPath::CurveToDataElement,
+ QPainterPath::CurveToDataElement
+ };
+
+ qreal x1 = rect.left();
+ qreal x2 = rect.right();
+ qreal y1 = rect.top();
+ qreal y2 = rect.bottom();
+
+ if (mode == Qt::RelativeSize) {
+ xRadius = xRadius * rect.width() / 200.;
+ yRadius = yRadius * rect.height() / 200.;
+ }
+
+ xRadius = qMin(xRadius, rect.width() / 2);
+ yRadius = qMin(yRadius, rect.height() / 2);
+
+ qreal pts[] = {
+ x1 + xRadius, y1, // MoveTo
+ x2 - xRadius, y1, // LineTo
+ x2 - (1 - KAPPA) * xRadius, y1, // CurveTo
+ x2, y1 + (1 - KAPPA) * yRadius,
+ x2, y1 + yRadius,
+ x2, y2 - yRadius, // LineTo
+ x2, y2 - (1 - KAPPA) * yRadius, // CurveTo
+ x2 - (1 - KAPPA) * xRadius, y2,
+ x2 - xRadius, y2,
+ x1 + xRadius, y2, // LineTo
+ x1 + (1 - KAPPA) * xRadius, y2, // CurveTo
+ x1, y2 - (1 - KAPPA) * yRadius,
+ x1, y2 - yRadius,
+ x1, y1 + yRadius, // LineTo
+ x1, y1 + KAPPA * yRadius, // CurveTo
+ x1 + (1 - KAPPA) * xRadius, y1,
+ x1 + xRadius, y1
+ };
+
+ QVectorPath path(pts, 17, types);
+ d->extended->draw(path);
+ return;
+ }
+
+ QPainterPath path;
+ path.addRoundedRect(rect, xRadius, yRadius, mode);
+ drawPath(path);
+}
+
+/*!
+ \fn void QPainter::drawRoundedRect(const QRect &rect, qreal xRadius, qreal yRadius,
+ Qt::SizeMode mode = Qt::AbsoluteSize);
+ \since 4.4
+ \overload
+
+ Draws the given rectangle \a rect with rounded corners.
+*/
+
+/*!
+ \fn void QPainter::drawRoundedRect(int x, int y, int w, int h, qreal xRadius, qreal yRadius,
+ Qt::SizeMode mode = Qt::AbsoluteSize);
+ \since 4.4
+ \overload
+
+ Draws the given rectangle \a x, \a y, \a w, \a h with rounded corners.
+*/
+
+/*!
+ \obsolete
+
+ Draws a rectangle \a r with rounded corners.
+
+ The \a xRnd and \a yRnd arguments specify how rounded the corners
+ should be. 0 is angled corners, 99 is maximum roundedness.
+
+ A filled rectangle has a size of r.size(). A stroked rectangle
+ has a size of r.size() plus the pen width.
+
+ \sa drawRoundedRect()
+*/
+void QPainter::drawRoundRect(const QRectF &r, int xRnd, int yRnd)
+{
+#ifdef QT_DEBUG_DRAW
+ if (qt_show_painter_debug_output)
+ printf("QPainter::drawRoundRectangle(), [%.2f,%.2f,%.2f,%.2f]\n", r.x(), r.y(), r.width(), r.height());
+#endif
+ Q_D(QPainter);
+
+ if (!d->engine)
+ return;
+
+ if(xRnd <= 0 || yRnd <= 0) { // draw normal rectangle
+ drawRect(r);
+ return;
+ }
+
+ QPainterPath path;
+ path.addRoundRect(r, xRnd, yRnd);
+ drawPath(path);
+}
+
+
+/*!
+ \fn void QPainter::drawRoundRect(const QRect &r, int xRnd = 25, int yRnd = 25)
+
+ \overload
+ \obsolete
+
+ Draws the rectangle \a r with rounded corners.
+*/
+
+/*!
+ \obsolete
+
+ \fn QPainter::drawRoundRect(int x, int y, int w, int h, int xRnd, int yRnd)
+
+ \overload
+
+ Draws the rectangle \a x, \a y, \a w, \a h with rounded corners.
+*/
+
+/*!
+ \fn void QPainter::drawEllipse(const QRectF &rectangle)
+
+ Draws the ellipse defined by the given \a rectangle.
+
+ A filled ellipse has a size of \a{rectangle}.\l
+ {QRect::size()}{size()}. A stroked ellipse has a size of
+ \a{rectangle}.\l {QRect::size()}{size()} plus the pen width.
+
+ \table 100%
+ \row
+ \o \inlineimage qpainter-ellipse.png
+ \o
+ \snippet doc/src/snippets/code/src_gui_painting_qpainter.cpp 9
+ \endtable
+
+ \sa drawPie(), {The Coordinate System}
+*/
+void QPainter::drawEllipse(const QRectF &r)
+{
+#ifdef QT_DEBUG_DRAW
+ if (qt_show_painter_debug_output)
+ printf("QPainter::drawEllipse(), [%.2f,%.2f,%.2f,%.2f]\n", r.x(), r.y(), r.width(), r.height());
+#endif
+ Q_D(QPainter);
+
+ if (!d->engine)
+ return;
+
+ QRectF rect(r.normalized());
+ if (rect.isEmpty())
+ return;
+
+ if (d->extended) {
+ d->extended->drawEllipse(rect);
+ return;
+ }
+
+ d->updateState(d->state);
+ if (d->state->emulationSpecifier) {
+ if (d->state->emulationSpecifier == QPaintEngine::PrimitiveTransform
+ && d->state->matrix.type() == QTransform::TxTranslate) {
+ rect.translate(QPointF(d->state->matrix.dx(), d->state->matrix.dy()));
+ } else {
+ QPainterPath path;
+ path.addEllipse(rect);
+ d->draw_helper(path, QPainterPrivate::StrokeAndFillDraw);
+ return;
+ }
+ }
+
+ d->engine->drawEllipse(rect);
+}
+
+/*!
+ \fn QPainter::drawEllipse(const QRect &rectangle)
+
+ \overload
+
+ Draws the ellipse defined by the given \a rectangle.
+*/
+void QPainter::drawEllipse(const QRect &r)
+{
+#ifdef QT_DEBUG_DRAW
+ if (qt_show_painter_debug_output)
+ printf("QPainter::drawEllipse(), [%d,%d,%d,%d]\n", r.x(), r.y(), r.width(), r.height());
+#endif
+ Q_D(QPainter);
+
+ if (!d->engine)
+ return;
+
+ QRect rect(r.normalized());
+ if (rect.isEmpty())
+ return;
+
+ if (d->extended) {
+ d->extended->drawEllipse(rect);
+ return;
+ }
+
+ d->updateState(d->state);
+
+ if (d->state->emulationSpecifier) {
+ if (d->state->emulationSpecifier == QPaintEngine::PrimitiveTransform
+ && d->state->matrix.type() == QTransform::TxTranslate) {
+ rect.translate(QPoint(qRound(d->state->matrix.dx()), qRound(d->state->matrix.dy())));
+ } else {
+ QPainterPath path;
+ path.addEllipse(rect);
+ d->draw_helper(path, QPainterPrivate::StrokeAndFillDraw);
+ return;
+ }
+ }
+
+ d->engine->drawEllipse(rect);
+}
+
+/*!
+ \fn QPainter::drawEllipse(int x, int y, int width, int height)
+
+ \overload
+
+ Draws the ellipse defined by the rectangle beginning at (\a{x},
+ \a{y}) with the given \a width and \a height.
+*/
+
+/*!
+ \since 4.4
+
+ \fn QPainter::drawEllipse(const QPointF &center, qreal rx, qreal ry)
+
+ \overload
+
+ Draws the ellipse positioned at \a{center} with radii \a{rx} and \a{ry}.
+*/
+
+/*!
+ \since 4.4
+
+ \fn QPainter::drawEllipse(const QPoint &center, int rx, int ry)
+
+ \overload
+
+ Draws the ellipse positioned at \a{center} with radii \a{rx} and \a{ry}.
+*/
+
+/*!
+ \fn void QPainter::drawArc(const QRectF &rectangle, int startAngle, int spanAngle)
+
+ Draws the arc defined by the given \a rectangle, \a startAngle and
+ \a spanAngle.
+
+ The \a startAngle and \a spanAngle must be specified in 1/16th of
+ a degree, i.e. a full circle equals 5760 (16 * 360). Positive
+ values for the angles mean counter-clockwise while negative values
+ mean the clockwise direction. Zero degrees is at the 3 o'clock
+ position.
+
+ \table 100%
+ \row
+ \o \inlineimage qpainter-arc.png
+ \o
+ \snippet doc/src/snippets/code/src_gui_painting_qpainter.cpp 10
+ \endtable
+
+ \sa drawPie(), drawChord(), {The Coordinate System}
+*/
+
+void QPainter::drawArc(const QRectF &r, int a, int alen)
+{
+#ifdef QT_DEBUG_DRAW
+ if (qt_show_painter_debug_output)
+ printf("QPainter::drawArc(), [%.2f,%.2f,%.2f,%.2f], angle=%d, sweep=%d\n",
+ r.x(), r.y(), r.width(), r.height(), a/16, alen/16);
+#endif
+ Q_D(QPainter);
+
+ if (!d->engine)
+ return;
+
+ QRectF rect = r.normalized();
+
+ QPainterPath path;
+ path.arcMoveTo(rect, a/16.0);
+ path.arcTo(rect, a/16.0, alen/16.0);
+ strokePath(path, d->state->pen);
+}
+
+/*! \fn void QPainter::drawArc(const QRect &rectangle, int startAngle,
+ int spanAngle)
+
+ \overload
+
+ Draws the arc defined by the given \a rectangle, \a startAngle and
+ \a spanAngle.
+*/
+
+/*!
+ \fn void QPainter::drawArc(int x, int y, int width, int height,
+ int startAngle, int spanAngle)
+
+ \overload
+
+ Draws the arc defined by the rectangle beginning at (\a x, \a y)
+ with the specified \a width and \a height, and the given \a
+ startAngle and \a spanAngle.
+*/
+
+/*!
+ \fn void QPainter::drawPie(const QRectF &rectangle, int startAngle, int spanAngle)
+
+ Draws a pie defined by the given \a rectangle, \a startAngle and
+ and \a spanAngle.
+
+ The pie is filled with the current brush().
+
+ The startAngle and spanAngle must be specified in 1/16th of a
+ degree, i.e. a full circle equals 5760 (16 * 360). Positive values
+ for the angles mean counter-clockwise while negative values mean
+ the clockwise direction. Zero degrees is at the 3 o'clock
+ position.
+
+ \table 100%
+ \row
+ \o \inlineimage qpainter-pie.png
+ \o
+ \snippet doc/src/snippets/code/src_gui_painting_qpainter.cpp 11
+ \endtable
+
+ \sa drawEllipse(), drawChord(), {The Coordinate System}
+*/
+void QPainter::drawPie(const QRectF &r, int a, int alen)
+{
+#ifdef QT_DEBUG_DRAW
+ if (qt_show_painter_debug_output)
+ printf("QPainter::drawPie(), [%.2f,%.2f,%.2f,%.2f], angle=%d, sweep=%d\n",
+ r.x(), r.y(), r.width(), r.height(), a/16, alen/16);
+#endif
+ Q_D(QPainter);
+
+ if (!d->engine)
+ return;
+
+ if (a > (360*16)) {
+ a = a % (360*16);
+ } else if (a < 0) {
+ a = a % (360*16);
+ if (a < 0) a += (360*16);
+ }
+
+ QRectF rect = r.normalized();
+
+ QPainterPath path;
+ path.moveTo(rect.center());
+ path.arcTo(rect.x(), rect.y(), rect.width(), rect.height(), a/16.0, alen/16.0);
+ path.closeSubpath();
+ drawPath(path);
+
+}
+
+/*!
+ \fn void QPainter::drawPie(const QRect &rectangle, int startAngle, int spanAngle)
+ \overload
+
+ Draws a pie defined by the given \a rectangle, \a startAngle and
+ and \a spanAngle.
+*/
+
+/*!
+ \fn void QPainter::drawPie(int x, int y, int width, int height, int
+ startAngle, int spanAngle)
+
+ \overload
+
+ Draws the pie defined by the rectangle beginning at (\a x, \a y) with
+ the specified \a width and \a height, and the given \a startAngle and
+ \a spanAngle.
+*/
+
+/*!
+ \fn void QPainter::drawChord(const QRectF &rectangle, int startAngle, int spanAngle)
+
+ Draws the chord defined by the given \a rectangle, \a startAngle and
+ \a spanAngle. The chord is filled with the current brush().
+
+ The startAngle and spanAngle must be specified in 1/16th of a
+ degree, i.e. a full circle equals 5760 (16 * 360). Positive values
+ for the angles mean counter-clockwise while negative values mean
+ the clockwise direction. Zero degrees is at the 3 o'clock
+ position.
+
+ \table 100%
+ \row
+ \o \inlineimage qpainter-chord.png
+ \o
+ \snippet doc/src/snippets/code/src_gui_painting_qpainter.cpp 12
+ \endtable
+
+ \sa drawArc(), drawPie(), {The Coordinate System}
+*/
+void QPainter::drawChord(const QRectF &r, int a, int alen)
+{
+#ifdef QT_DEBUG_DRAW
+ if (qt_show_painter_debug_output)
+ printf("QPainter::drawChord(), [%.2f,%.2f,%.2f,%.2f], angle=%d, sweep=%d\n",
+ r.x(), r.y(), r.width(), r.height(), a/16, alen/16);
+#endif
+ Q_D(QPainter);
+
+ if (!d->engine)
+ return;
+
+ QRectF rect = r.normalized();
+
+ QPainterPath path;
+ path.arcMoveTo(rect, a/16.0);
+ path.arcTo(rect, a/16.0, alen/16.0);
+ path.closeSubpath();
+ drawPath(path);
+}
+/*!
+ \fn void QPainter::drawChord(const QRect &rectangle, int startAngle, int spanAngle)
+
+ \overload
+
+ Draws the chord defined by the given \a rectangle, \a startAngle and
+ \a spanAngle.
+*/
+
+/*!
+ \fn void QPainter::drawChord(int x, int y, int width, int height, int
+ startAngle, int spanAngle)
+
+ \overload
+
+ Draws the chord defined by the rectangle beginning at (\a x, \a y)
+ with the specified \a width and \a height, and the given \a
+ startAngle and \a spanAngle.
+*/
+
+#ifdef QT3_SUPPORT
+/*!
+ \fn void QPainter::drawLineSegments(const QPolygon &polygon, int
+ index, int count)
+
+ Draws \a count separate lines from points defined by the \a
+ polygon, starting at \a{polygon}\e{[index]} (\a index defaults to
+ 0). If \a count is -1 (the default) all points until the end of
+ the array are used.
+
+ Use drawLines() combined with QPolygon::constData() instead.
+
+ \oldcode
+ QPainter painter(this);
+ painter.drawLineSegments(polygon, index, count);
+ \newcode
+ int lineCount = (count == -1) ? (polygon.size() - index) / 2 : count;
+
+ QPainter painter(this);
+ painter.drawLines(polygon.constData() + index * 2, lineCount);
+ \endcode
+*/
+
+void QPainter::drawLineSegments(const QPolygon &a, int index, int nlines)
+{
+#ifdef QT_DEBUG_DRAW
+ if (qt_show_painter_debug_output)
+ printf("QPainter::drawLineSegments(), count=%d\n", a.size()/2);
+#endif
+ Q_D(QPainter);
+
+ if (!d->engine)
+ return;
+
+ if (nlines < 0)
+ nlines = a.size()/2 - index/2;
+ if (index + nlines*2 > (int)a.size())
+ nlines = (a.size() - index)/2;
+ if (nlines < 1 || index < 0)
+ return;
+
+ if (d->extended) {
+ // FALCON: Use QVectorPath
+ QVector<QLineF> lines;
+ for (int i=index; i<index + nlines*2; i+=2)
+ lines << QLineF(a.at(i), a.at(i+1));
+ d->extended->drawLines(lines.data(), lines.size());
+ return;
+ }
+
+ d->updateState(d->state);
+
+ QVector<QLineF> lines;
+ if (d->state->emulationSpecifier) {
+ if (d->state->emulationSpecifier == QPaintEngine::PrimitiveTransform
+ && d->state->matrix.type() == QTransform::TxTranslate) {
+ QPointF offset(d->state->matrix.dx(), d->state->matrix.dy());
+ for (int i=index; i<index + nlines*2; i+=2)
+ lines << QLineF(a.at(i) + offset, a.at(i+1) + offset);
+ } else {
+ QPainterPath linesPath;
+ for (int i=index; i<index + nlines*2; i+=2) {
+ linesPath.moveTo(a.at(i));
+ linesPath.lineTo(a.at(i+1));
+ }
+ d->draw_helper(linesPath, QPainterPrivate::StrokeDraw);
+ return;
+ }
+ } else {
+ for (int i=index; i<index + nlines*2; i+=2)
+ lines << QLineF(a.at(i), a.at(i+1));
+ }
+
+ d->engine->drawLines(lines.data(), lines.size());
+}
+#endif // QT3_SUPPORT
+
+/*!
+ Draws the first \a lineCount lines in the array \a lines
+ using the current pen.
+
+ \sa drawLine(), drawPolyline()
+*/
+void QPainter::drawLines(const QLineF *lines, int lineCount)
+{
+#ifdef QT_DEBUG_DRAW
+ if (qt_show_painter_debug_output)
+ printf("QPainter::drawLines(), line count=%d\n", lineCount);
+#endif
+
+ Q_D(QPainter);
+
+ if (!d->engine || lineCount < 1)
+ return;
+
+ if (d->extended) {
+ d->extended->drawLines(lines, lineCount);
+ return;
+ }
+
+ d->updateState(d->state);
+
+ uint lineEmulation = line_emulation(d->state->emulationSpecifier);
+
+ if (lineEmulation) {
+ if (lineEmulation == QPaintEngine::PrimitiveTransform
+ && d->state->matrix.type() == QTransform::TxTranslate) {
+ for (int i = 0; i < lineCount; ++i) {
+ QLineF line = lines[i];
+ line.translate(d->state->matrix.dx(), d->state->matrix.dy());
+ d->engine->drawLines(&line, 1);
+ }
+ } else {
+ QPainterPath linePath;
+ for (int i = 0; i < lineCount; ++i) {
+ linePath.moveTo(lines[i].p1());
+ linePath.lineTo(lines[i].p2());
+ }
+ d->draw_helper(linePath, QPainterPrivate::StrokeDraw);
+ }
+ return;
+ }
+ d->engine->drawLines(lines, lineCount);
+}
+
+/*!
+ \fn void QPainter::drawLines(const QLine *lines, int lineCount)
+ \overload
+
+ Draws the first \a lineCount lines in the array \a lines
+ using the current pen.
+*/
+void QPainter::drawLines(const QLine *lines, int lineCount)
+{
+#ifdef QT_DEBUG_DRAW
+ if (qt_show_painter_debug_output)
+ printf("QPainter::drawLine(), line count=%d\n", lineCount);
+#endif
+
+ Q_D(QPainter);
+
+ if (!d->engine || lineCount < 1)
+ return;
+
+ if (d->extended) {
+ d->extended->drawLines(lines, lineCount);
+ return;
+ }
+
+ d->updateState(d->state);
+
+ uint lineEmulation = line_emulation(d->state->emulationSpecifier);
+
+ if (lineEmulation) {
+ if (lineEmulation == QPaintEngine::PrimitiveTransform
+ && d->state->matrix.type() == QTransform::TxTranslate) {
+ for (int i = 0; i < lineCount; ++i) {
+ QLineF line = lines[i];
+ line.translate(d->state->matrix.dx(), d->state->matrix.dy());
+ d->engine->drawLines(&line, 1);
+ }
+ } else {
+ QPainterPath linePath;
+ for (int i = 0; i < lineCount; ++i) {
+ linePath.moveTo(lines[i].p1());
+ linePath.lineTo(lines[i].p2());
+ }
+ d->draw_helper(linePath, QPainterPrivate::StrokeDraw);
+ }
+ return;
+ }
+ d->engine->drawLines(lines, lineCount);
+}
+
+/*!
+ \overload
+
+ Draws the first \a lineCount lines in the array \a pointPairs
+ using the current pen. The lines are specified as pairs of points
+ so the number of entries in \a pointPairs must be at least \a
+ lineCount * 2.
+*/
+void QPainter::drawLines(const QPointF *pointPairs, int lineCount)
+{
+ Q_ASSERT(sizeof(QLineF) == 2*sizeof(QPointF));
+
+ drawLines((QLineF*)pointPairs, lineCount);
+}
+
+/*!
+ \overload
+
+ Draws the first \a lineCount lines in the array \a pointPairs
+ using the current pen.
+*/
+void QPainter::drawLines(const QPoint *pointPairs, int lineCount)
+{
+ Q_ASSERT(sizeof(QLine) == 2*sizeof(QPoint));
+
+ drawLines((QLine*)pointPairs, lineCount);
+}
+
+
+/*!
+ \fn void QPainter::drawLines(const QVector<QPointF> &pointPairs)
+ \overload
+
+ Draws a line for each pair of points in the vector \a pointPairs
+ using the current pen. If there is an odd number of points in the
+ array, the last point will be ignored.
+*/
+
+/*!
+ \fn void QPainter::drawLines(const QVector<QPoint> &pointPairs)
+ \overload
+
+ Draws a line for each pair of points in the vector \a pointPairs
+ using the current pen.
+*/
+
+/*!
+ \fn void QPainter::drawLines(const QVector<QLineF> &lines)
+ \overload
+
+ Draws the set of lines defined by the list \a lines using the
+ current pen and brush.
+*/
+
+/*!
+ \fn void QPainter::drawLines(const QVector<QLine> &lines)
+ \overload
+
+ Draws the set of lines defined by the list \a lines using the
+ current pen and brush.
+*/
+
+/*!
+ Draws the polyline defined by the first \a pointCount points in \a
+ points using the current pen.
+
+ Note that unlike the drawPolygon() function the last point is \e
+ not connected to the first, neither is the polyline filled.
+
+ \table 100%
+ \row
+ \o
+ \snippet doc/src/snippets/code/src_gui_painting_qpainter.cpp 13
+ \endtable
+
+ \sa drawLines(), drawPolygon(), {The Coordinate System}
+*/
+void QPainter::drawPolyline(const QPointF *points, int pointCount)
+{
+#ifdef QT_DEBUG_DRAW
+ if (qt_show_painter_debug_output)
+ printf("QPainter::drawPolyline(), count=%d\n", pointCount);
+#endif
+ Q_D(QPainter);
+
+ if (!d->engine || pointCount < 2)
+ return;
+
+ if (d->extended) {
+ d->extended->drawPolygon(points, pointCount, QPaintEngine::PolylineMode);
+ return;
+ }
+
+ d->updateState(d->state);
+
+ uint lineEmulation = line_emulation(d->state->emulationSpecifier);
+
+ if (lineEmulation) {
+ // ###
+// if (lineEmulation == QPaintEngine::PrimitiveTransform
+// && d->state->matrix.type() == QTransform::TxTranslate) {
+// } else {
+ QPainterPath polylinePath(points[0]);
+ for (int i=1; i<pointCount; ++i)
+ polylinePath.lineTo(points[i]);
+ d->draw_helper(polylinePath, QPainterPrivate::StrokeDraw);
+// }
+ } else {
+ d->engine->drawPolygon(points, pointCount, QPaintEngine::PolylineMode);
+ }
+}
+
+/*!
+ \overload
+
+ Draws the polyline defined by the first \a pointCount points in \a
+ points using the current pen.
+ */
+void QPainter::drawPolyline(const QPoint *points, int pointCount)
+{
+#ifdef QT_DEBUG_DRAW
+ if (qt_show_painter_debug_output)
+ printf("QPainter::drawPolyline(), count=%d\n", pointCount);
+#endif
+ Q_D(QPainter);
+
+ if (!d->engine || pointCount < 2)
+ return;
+
+ if (d->extended) {
+ d->extended->drawPolygon(points, pointCount, QPaintEngine::PolylineMode);
+ return;
+ }
+
+ d->updateState(d->state);
+
+ uint lineEmulation = line_emulation(d->state->emulationSpecifier);
+
+ if (lineEmulation) {
+ // ###
+// if (lineEmulation == QPaintEngine::PrimitiveTransform
+// && d->state->matrix.type() == QTransform::TxTranslate) {
+// } else {
+ QPainterPath polylinePath(points[0]);
+ for (int i=1; i<pointCount; ++i)
+ polylinePath.lineTo(points[i]);
+ d->draw_helper(polylinePath, QPainterPrivate::StrokeDraw);
+// }
+ } else {
+ d->engine->drawPolygon(points, pointCount, QPaintEngine::PolylineMode);
+ }
+}
+
+/*!
+ \fn void QPainter::drawPolyline(const QPolygon &polygon, int index, int
+ count)
+
+ \overload
+ \compat
+
+ Draws the polyline defined by the \a count lines of the given \a
+ polygon starting at \a index (\a index defaults to 0).
+
+ Use drawPolyline() combined with QPolygon::constData() instead.
+
+ \oldcode
+ QPainter painter(this);
+ painter.drawPolyline(polygon, index, count);
+ \newcode
+ int pointCount = (count == -1) ? polygon.size() - index : count;
+
+ QPainter painter(this);
+ painter.drawPolyline(polygon.constData() + index, pointCount);
+ \endcode
+*/
+
+/*!
+ \fn void QPainter::drawPolyline(const QPolygonF &points)
+
+ \overload
+
+ Draws the polyline defined by the given \a points using the
+ current pen.
+*/
+
+/*!
+ \fn void QPainter::drawPolyline(const QPolygon &points)
+
+ \overload
+
+ Draws the polyline defined by the given \a points using the
+ current pen.
+*/
+
+/*!
+ Draws the polygon defined by the first \a pointCount points in the
+ array \a points using the current pen and brush.
+
+ \table 100%
+ \row
+ \o \inlineimage qpainter-polygon.png
+ \o
+ \snippet doc/src/snippets/code/src_gui_painting_qpainter.cpp 14
+ \endtable
+
+ The first point is implicitly connected to the last point, and the
+ polygon is filled with the current brush().
+
+ If \a fillRule is Qt::WindingFill, the polygon is filled using the
+ winding fill algorithm. If \a fillRule is Qt::OddEvenFill, the
+ polygon is filled using the odd-even fill algorithm. See
+ \l{Qt::FillRule} for a more detailed description of these fill
+ rules.
+
+ \sa drawConvexPolygon(), drawPolyline(), {The Coordinate System}
+*/
+void QPainter::drawPolygon(const QPointF *points, int pointCount, Qt::FillRule fillRule)
+{
+#ifdef QT_DEBUG_DRAW
+ if (qt_show_painter_debug_output)
+ printf("QPainter::drawPolygon(), count=%d\n", pointCount);
+#endif
+
+ Q_D(QPainter);
+
+ if (!d->engine || pointCount < 2)
+ return;
+
+ if (d->extended) {
+ d->extended->drawPolygon(points, pointCount, QPaintEngine::PolygonDrawMode(fillRule));
+ return;
+ }
+
+ d->updateState(d->state);
+
+ uint emulationSpecifier = d->state->emulationSpecifier;
+
+ if (emulationSpecifier) {
+ QPainterPath polygonPath(points[0]);
+ for (int i=1; i<pointCount; ++i)
+ polygonPath.lineTo(points[i]);
+ polygonPath.closeSubpath();
+ polygonPath.setFillRule(fillRule);
+ d->draw_helper(polygonPath);
+ return;
+ }
+
+ d->engine->drawPolygon(points, pointCount, QPaintEngine::PolygonDrawMode(fillRule));
+}
+
+/*! \overload
+
+ Draws the polygon defined by the first \a pointCount points in the
+ array \a points.
+*/
+void QPainter::drawPolygon(const QPoint *points, int pointCount, Qt::FillRule fillRule)
+{
+#ifdef QT_DEBUG_DRAW
+ if (qt_show_painter_debug_output)
+ printf("QPainter::drawPolygon(), count=%d\n", pointCount);
+#endif
+
+ Q_D(QPainter);
+
+ if (!d->engine || pointCount < 2)
+ return;
+
+ if (d->extended) {
+ d->extended->drawPolygon(points, pointCount, QPaintEngine::PolygonDrawMode(fillRule));
+ return;
+ }
+
+ d->updateState(d->state);
+
+ uint emulationSpecifier = d->state->emulationSpecifier;
+
+ if (emulationSpecifier) {
+ QPainterPath polygonPath(points[0]);
+ for (int i=1; i<pointCount; ++i)
+ polygonPath.lineTo(points[i]);
+ polygonPath.closeSubpath();
+ polygonPath.setFillRule(fillRule);
+ d->draw_helper(polygonPath);
+ return;
+ }
+
+ d->engine->drawPolygon(points, pointCount, QPaintEngine::PolygonDrawMode(fillRule));
+}
+
+/*! \fn void QPainter::drawPolygon(const QPolygonF &polygon, bool winding, int index = 0,
+ int count = -1)
+ \compat
+ \overload
+
+ Use drawPolygon() combined with QPolygonF::constData() instead.
+
+ \oldcode
+ QPainter painter(this);
+ painter.drawPolygon(polygon, winding, index, count);
+ \newcode
+ int pointCount = (count == -1) ? polygon.size() - index : count;
+ int fillRule = winding ? Qt::WindingFill : Qt::OddEvenFill;
+
+ QPainter painter(this);
+ painter.drawPolygon( polygon.constData() + index, pointCount, fillRule);
+ \endcode
+*/
+
+/*! \fn void QPainter::drawPolygon(const QPolygon &polygon, bool winding,
+ int index = 0, int count = -1)
+
+ \compat
+ \overload
+
+ Use drawPolygon() combined with QPolygon::constData() instead.
+
+ \oldcode
+ QPainter painter(this);
+ painter.drawPolygon(polygon, winding, index, count);
+ \newcode
+ int pointCount = (count == -1) ? polygon.size() - index : count;
+ int fillRule = winding ? Qt::WindingFill : Qt::OddEvenFill;
+
+ QPainter painter(this);
+ painter.drawPolygon( polygon.constData() + index, pointCount, fillRule);
+ \endcode
+*/
+
+/*! \fn void QPainter::drawPolygon(const QPolygonF &points, Qt::FillRule fillRule)
+
+ \overload
+
+ Draws the polygon defined by the given \a points using the fill
+ rule \a fillRule.
+*/
+
+/*! \fn void QPainter::drawPolygon(const QPolygon &points, Qt::FillRule fillRule)
+
+ \overload
+
+ Draws the polygon defined by the given \a points using the fill
+ rule \a fillRule.
+*/
+
+/*!
+ \fn void QPainter::drawConvexPolygon(const QPointF *points, int pointCount)
+
+ Draws the convex polygon defined by the first \a pointCount points
+ in the array \a points using the current pen.
+
+ \table 100%
+ \row
+ \o \inlineimage qpainter-polygon.png
+ \o
+ \snippet doc/src/snippets/code/src_gui_painting_qpainter.cpp 15
+ \endtable
+
+ The first point is implicitly connected to the last point, and the
+ polygon is filled with the current brush(). If the supplied
+ polygon is not convex, i.e. it contains at least one angle larger
+ than 180 degrees, the results are undefined.
+
+ On some platforms (e.g. X11), the drawConvexPolygon() function can
+ be faster than the drawPolygon() function.
+
+ \sa drawPolygon(), drawPolyline(), {The Coordinate System}
+*/
+
+/*!
+ \fn void QPainter::drawConvexPolygon(const QPoint *points, int pointCount)
+ \overload
+
+ Draws the convex polygon defined by the first \a pointCount points
+ in the array \a points using the current pen.
+*/
+
+/*!
+ \fn void QPainter::drawConvexPolygon(const QPolygonF &polygon)
+
+ \overload
+
+ Draws the convex polygon defined by \a polygon using the current
+ pen and brush.
+*/
+
+/*!
+ \fn void QPainter::drawConvexPolygon(const QPolygon &polygon)
+ \overload
+
+ Draws the convex polygon defined by \a polygon using the current
+ pen and brush.
+*/
+
+/*!
+ \fn void QPainter::drawConvexPolygon(const QPolygonF &polygon, int
+ index, int count)
+
+ \compat
+ \overload
+
+ Use drawConvexPolygon() combined with QPolygonF::constData()
+ instead.
+
+ \oldcode
+ QPainter painter(this);
+ painter.drawConvexPolygon(polygon, index, count);
+ \newcode
+ int pointCount = (count == -1) ? polygon.size() - index : count;
+
+ QPainter painter(this);
+ painter.drawConvexPolygon(polygon.constData() + index, pointCount);
+ \endcode
+*/
+
+/*!
+ \fn void QPainter::drawConvexPolygon(const QPolygon &polygon, int
+ index, int count)
+
+ \compat
+ \overload
+
+ Use drawConvexPolygon() combined with QPolygon::constData()
+ instead.
+
+ \oldcode
+ QPainter painter(this);
+ painter.drawConvexPolygon(polygon, index, count);
+ \newcode
+ int pointCount = (count == -1) ? polygon.size() - index : count;
+
+ QPainter painter(this);
+ painter.drawConvexPolygon(polygon.constData() + index, pointCount);
+ \endcode
+*/
+
+void QPainter::drawConvexPolygon(const QPoint *points, int pointCount)
+{
+#ifdef QT_DEBUG_DRAW
+ if (qt_show_painter_debug_output)
+ printf("QPainter::drawConvexPolygon(), count=%d\n", pointCount);
+#endif
+
+ Q_D(QPainter);
+
+ if (!d->engine || pointCount < 2)
+ return;
+
+ if (d->extended) {
+ d->extended->drawPolygon(points, pointCount, QPaintEngine::ConvexMode);
+ return;
+ }
+
+ d->updateState(d->state);
+
+ uint emulationSpecifier = d->state->emulationSpecifier;
+
+ if (emulationSpecifier) {
+ QPainterPath polygonPath(points[0]);
+ for (int i=1; i<pointCount; ++i)
+ polygonPath.lineTo(points[i]);
+ polygonPath.closeSubpath();
+ polygonPath.setFillRule(Qt::WindingFill);
+ d->draw_helper(polygonPath);
+ return;
+ }
+
+ d->engine->drawPolygon(points, pointCount, QPaintEngine::ConvexMode);
+}
+
+void QPainter::drawConvexPolygon(const QPointF *points, int pointCount)
+{
+#ifdef QT_DEBUG_DRAW
+ if (qt_show_painter_debug_output)
+ printf("QPainter::drawConvexPolygon(), count=%d\n", pointCount);
+#endif
+
+ Q_D(QPainter);
+
+ if (!d->engine || pointCount < 2)
+ return;
+
+ if (d->extended) {
+ d->extended->drawPolygon(points, pointCount, QPaintEngine::ConvexMode);
+ return;
+ }
+
+ d->updateState(d->state);
+
+ uint emulationSpecifier = d->state->emulationSpecifier;
+
+ if (emulationSpecifier) {
+ QPainterPath polygonPath(points[0]);
+ for (int i=1; i<pointCount; ++i)
+ polygonPath.lineTo(points[i]);
+ polygonPath.closeSubpath();
+ polygonPath.setFillRule(Qt::WindingFill);
+ d->draw_helper(polygonPath);
+ return;
+ }
+
+ d->engine->drawPolygon(points, pointCount, QPaintEngine::ConvexMode);
+}
+
+/*!
+ \fn void QPainter::drawPixmap(const QRectF &target, const QPixmap &pixmap, const QRectF &source)
+
+ Draws the rectangular portion \a source of the given \a pixmap
+ into the given \a target in the paint device.
+
+ \note The pixmap is scaled to fit the rectangle, if both the pixmap and rectangle size disagree.
+
+ \table 100%
+ \row
+ \o
+ \snippet doc/src/snippets/code/src_gui_painting_qpainter.cpp 16
+ \endtable
+
+ If \a pixmap is a QBitmap it is drawn with the bits that are "set"
+ using the pens color. If backgroundMode is Qt::OpaqueMode, the
+ "unset" bits are drawn using the color of the background brush; if
+ backgroundMode is Qt::TransparentMode, the "unset" bits are
+ transparent. Drawing bitmaps with gradient or texture colors is
+ not supported.
+
+ \sa drawImage()
+*/
+void QPainter::drawPixmap(const QPointF &p, const QPixmap &pm)
+{
+#if defined QT_DEBUG_DRAW
+ if (qt_show_painter_debug_output)
+ printf("QPainter::drawPixmap(), p=[%.2f,%.2f], pix=[%d,%d]\n",
+ p.x(), p.y(),
+ pm.width(), pm.height());
+#endif
+
+ Q_D(QPainter);
+
+ if (!d->engine || pm.isNull())
+ return;
+
+#ifndef QT_NO_DEBUG
+ qt_painter_thread_test(d->device->devType(), "drawPixmap()");
+#endif
+
+ if (d->extended) {
+ d->extended->drawPixmap(p, pm);
+ return;
+ }
+
+ qreal x = p.x();
+ qreal y = p.y();
+
+ int w = pm.width();
+ int h = pm.height();
+
+ // Emulate opaque background for bitmaps
+ if (d->state->bgMode == Qt::OpaqueMode && pm.isQBitmap()) {
+ fillRect(QRectF(x, y, w, h), d->state->bgBrush.color());
+ }
+
+ d->updateState(d->state);
+
+ if ((d->state->matrix.type() > QTransform::TxTranslate
+ && !d->engine->hasFeature(QPaintEngine::PixmapTransform))
+ || (!d->state->matrix.isAffine() && !d->engine->hasFeature(QPaintEngine::PerspectiveTransform))
+ || (d->state->opacity != 1.0 && !d->engine->hasFeature(QPaintEngine::ConstantOpacity)))
+ {
+ save();
+ // If there is no scaling or transformation involved we have to make sure we use the
+ // antialiased and not the aliased coordinate system by rounding the coordinates.
+ if (d->state->matrix.type() <= QTransform::TxTranslate) {
+ x = qRound(x + d->state->matrix.dx()) - d->state->matrix.dx();
+ y = qRound(y + d->state->matrix.dy()) - d->state->matrix.dy();
+ }
+ translate(x, y);
+ setBackgroundMode(Qt::TransparentMode);
+ setRenderHint(Antialiasing, renderHints() & SmoothPixmapTransform);
+ QBrush brush(d->state->pen.color(), pm);
+ setBrush(brush);
+ setPen(Qt::NoPen);
+ setBrushOrigin(QPointF(0, 0));
+
+ drawRect(pm.rect());
+ restore();
+ } else {
+ if (!d->engine->hasFeature(QPaintEngine::PixmapTransform)) {
+ x += d->state->matrix.dx();
+ y += d->state->matrix.dy();
+ }
+ d->engine->drawPixmap(QRectF(x, y, w, h), pm, QRectF(0, 0, w, h));
+ }
+}
+
+void QPainter::drawPixmap(const QRectF &r, const QPixmap &pm, const QRectF &sr)
+{
+#if defined QT_DEBUG_DRAW
+ if (qt_show_painter_debug_output)
+ printf("QPainter::drawPixmap(), target=[%.2f,%.2f,%.2f,%.2f], pix=[%d,%d], source=[%.2f,%.2f,%.2f,%.2f]\n",
+ r.x(), r.y(), r.width(), r.height(),
+ pm.width(), pm.height(),
+ sr.x(), sr.y(), sr.width(), sr.height());
+#endif
+
+ Q_D(QPainter);
+ if (!d->engine || pm.isNull())
+ return;
+#ifndef QT_NO_DEBUG
+ qt_painter_thread_test(d->device->devType(), "drawPixmap()");
+#endif
+
+ qreal x = r.x();
+ qreal y = r.y();
+ qreal w = r.width();
+ qreal h = r.height();
+ qreal sx = sr.x();
+ qreal sy = sr.y();
+ qreal sw = sr.width();
+ qreal sh = sr.height();
+
+ // Sanity-check clipping
+ if (sw <= 0)
+ sw = pm.width() - sx;
+
+ if (sh <= 0)
+ sh = pm.height() - sy;
+
+ if (w < 0)
+ w = sw;
+ if (h < 0)
+ h = sh;
+
+ if (sx < 0) {
+ qreal w_ratio = sx * w/sw;
+ x -= w_ratio;
+ w += w_ratio;
+ sw += sx;
+ sx = 0;
+ }
+
+ if (sy < 0) {
+ qreal h_ratio = sy * h/sh;
+ y -= h_ratio;
+ h += h_ratio;
+ sh += sy;
+ sy = 0;
+ }
+
+ if (sw + sx > pm.width()) {
+ qreal delta = sw - (pm.width() - sx);
+ qreal w_ratio = delta * w/sw;
+ sw -= delta;
+ w -= w_ratio;
+ }
+
+ if (sh + sy > pm.height()) {
+ qreal delta = sh - (pm.height() - sy);
+ qreal h_ratio = delta * h/sh;
+ sh -= delta;
+ h -= h_ratio;
+ }
+
+ if (w == 0 || h == 0 || sw <= 0 || sh <= 0)
+ return;
+
+ if (d->extended) {
+ d->extended->drawPixmap(QRectF(x, y, w, h), pm, QRectF(sx, sy, sw, sh));
+ return;
+ }
+
+ // Emulate opaque background for bitmaps
+ if (d->state->bgMode == Qt::OpaqueMode && pm.isQBitmap())
+ fillRect(QRectF(x, y, w, h), d->state->bgBrush.color());
+
+ d->updateState(d->state);
+
+ if ((d->state->matrix.type() > QTransform::TxTranslate
+ && !d->engine->hasFeature(QPaintEngine::PixmapTransform))
+ || (!d->state->matrix.isAffine() && !d->engine->hasFeature(QPaintEngine::PerspectiveTransform))
+ || (d->state->opacity != 1.0 && !d->engine->hasFeature(QPaintEngine::ConstantOpacity))
+ || ((sw != w || sh != h) && !d->engine->hasFeature(QPaintEngine::PixmapTransform)))
+ {
+ save();
+ // If there is no scaling or transformation involved we have to make sure we use the
+ // antialiased and not the aliased coordinate system by rounding the coordinates.
+ if (d->state->matrix.type() <= QTransform::TxTranslate && sw == w && sh == h) {
+ x = qRound(x + d->state->matrix.dx()) - d->state->matrix.dx();
+ y = qRound(y + d->state->matrix.dy()) - d->state->matrix.dy();
+ sx = qRound(sx);
+ sy = qRound(sy);
+ sw = qRound(sw);
+ sh = qRound(sh);
+ }
+ translate(x, y);
+ scale(w / sw, h / sh);
+ setBackgroundMode(Qt::TransparentMode);
+ setRenderHint(Antialiasing, renderHints() & SmoothPixmapTransform);
+ QBrush brush(d->state->pen.color(), pm);
+ setBrush(brush);
+ setPen(Qt::NoPen);
+ setBrushOrigin(QPointF(-sx, -sy));
+
+ drawRect(QRectF(0, 0, sw, sh));
+ restore();
+ } else {
+ if (!d->engine->hasFeature(QPaintEngine::PixmapTransform)) {
+ x += d->state->matrix.dx();
+ y += d->state->matrix.dy();
+ }
+ d->engine->drawPixmap(QRectF(x, y, w, h), pm, QRectF(sx, sy, sw, sh));
+ }
+}
+
+
+/*!
+ \fn void QPainter::drawPixmap(const QRect &target, const QPixmap &pixmap,
+ const QRect &source)
+ \overload
+
+ Draws the rectangular portion \a source of the given \a pixmap
+ into the given \a target in the paint device.
+
+ \note The pixmap is scaled to fit the rectangle, if both the pixmap and rectangle size disagree.
+*/
+
+/*!
+ \fn void QPainter::drawPixmap(const QPointF &point, const QPixmap &pixmap,
+ const QRectF &source)
+ \overload
+
+ Draws the rectangular portion \a source of the given \a pixmap
+ with its origin at the given \a point.
+*/
+
+/*!
+ \fn void QPainter::drawPixmap(const QPoint &point, const QPixmap &pixmap,
+ const QRect &source)
+
+ \overload
+
+ Draws the rectangular portion \a source of the given \a pixmap
+ with its origin at the given \a point.
+*/
+
+/*!
+ \fn void QPainter::drawPixmap(const QPointF &point, const QPixmap &pixmap)
+ \overload
+
+ Draws the given \a pixmap with its origin at the given \a point.
+*/
+
+/*!
+ \fn void QPainter::drawPixmap(const QPoint &point, const QPixmap &pixmap)
+ \overload
+
+ Draws the given \a pixmap with its origin at the given \a point.
+*/
+
+/*!
+ \fn void QPainter::drawPixmap(int x, int y, const QPixmap &pixmap)
+
+ \overload
+
+ Draws the given \a pixmap at position (\a{x}, \a{y}).
+*/
+
+/*!
+ \fn void QPainter::drawPixmap(const QRect &rectangle, const QPixmap &pixmap)
+ \overload
+
+ Draws the given \a pixmap into the given \a rectangle.
+
+ \note The pixmap is scaled to fit the rectangle, if both the pixmap and rectangle size disagree.
+*/
+
+/*!
+ \fn void QPainter::drawPixmap(int x, int y, int width, int height,
+ const QPixmap &pixmap)
+
+ \overload
+
+ Draws the \a pixmap into the rectangle at position (\a{x}, \a{y})
+ with the given \a width and \a height.
+*/
+
+/*!
+ \fn void QPainter::drawPixmap(int x, int y, int w, int h, const QPixmap &pixmap,
+ int sx, int sy, int sw, int sh)
+
+ \overload
+
+ Draws the rectangular portion with the origin (\a{sx}, \a{sy}),
+ width \a sw and height \a sh, of the given \a pixmap , at the
+ point (\a{x}, \a{y}), with a width of \a w and a height of \a h.
+ If sw or sh are equal to zero the width/height of the pixmap
+ is used and adjusted by the offset sx/sy;
+*/
+
+/*!
+ \fn void QPainter::drawPixmap(int x, int y, const QPixmap &pixmap,
+ int sx, int sy, int sw, int sh)
+
+ \overload
+
+ Draws a pixmap at (\a{x}, \a{y}) by copying a part of the given \a
+ pixmap into the paint device.
+
+ (\a{x}, \a{y}) specifies the top-left point in the paint device that is
+ to be drawn onto. (\a{sx}, \a{sy}) specifies the top-left point in \a
+ pixmap that is to be drawn. The default is (0, 0).
+
+ (\a{sw}, \a{sh}) specifies the size of the pixmap that is to be drawn.
+ The default, (0, 0) (and negative) means all the way to the
+ bottom-right of the pixmap.
+*/
+
+void QPainter::drawImage(const QPointF &p, const QImage &image)
+{
+ Q_D(QPainter);
+
+ if (!d->engine || image.isNull())
+ return;
+
+ if (d->extended) {
+ d->extended->drawImage(p, image);
+ return;
+ }
+
+ qreal x = p.x();
+ qreal y = p.y();
+
+ int w = image.width();
+ int h = image.height();
+
+ d->updateState(d->state);
+
+ if (((d->state->matrix.type() > QTransform::TxTranslate)
+ && !d->engine->hasFeature(QPaintEngine::PixmapTransform))
+ || (!d->state->matrix.isAffine() && !d->engine->hasFeature(QPaintEngine::PerspectiveTransform))
+ || (d->state->opacity != 1.0 && !d->engine->hasFeature(QPaintEngine::ConstantOpacity)))
+ {
+ save();
+ // If there is no scaling or transformation involved we have to make sure we use the
+ // antialiased and not the aliased coordinate system by rounding the coordinates.
+ if (d->state->matrix.type() <= QTransform::TxTranslate) {
+ x = qRound(x + d->state->matrix.dx()) - d->state->matrix.dx();
+ y = qRound(y + d->state->matrix.dy()) - d->state->matrix.dy();
+ }
+ translate(x, y);
+ setBackgroundMode(Qt::TransparentMode);
+ setRenderHint(Antialiasing, renderHints() & SmoothPixmapTransform);
+ QBrush brush(image);
+ setBrush(brush);
+ setPen(Qt::NoPen);
+ setBrushOrigin(QPointF(0, 0));
+
+ drawRect(image.rect());
+ restore();
+ return;
+ }
+
+ if (d->state->matrix.type() == QTransform::TxTranslate
+ && !d->engine->hasFeature(QPaintEngine::PixmapTransform)) {
+ x += d->state->matrix.dx();
+ y += d->state->matrix.dy();
+ }
+
+ d->engine->drawImage(QRectF(x, y, w, h), image, QRectF(0, 0, w, h), Qt::AutoColor);
+}
+
+void QPainter::drawImage(const QRectF &targetRect, const QImage &image, const QRectF &sourceRect,
+ Qt::ImageConversionFlags flags)
+{
+ Q_D(QPainter);
+
+ if (!d->engine || image.isNull())
+ return;
+
+ qreal x = targetRect.x();
+ qreal y = targetRect.y();
+ qreal w = targetRect.width();
+ qreal h = targetRect.height();
+ qreal sx = sourceRect.x();
+ qreal sy = sourceRect.y();
+ qreal sw = sourceRect.width();
+ qreal sh = sourceRect.height();
+
+ // Sanity-check clipping
+ if (sw <= 0)
+ sw = image.width() - sx;
+
+ if (sh <= 0)
+ sh = image.height() - sy;
+
+ if (w < 0)
+ w = sw;
+ if (h < 0)
+ h = sh;
+
+ if (sx < 0) {
+ qreal w_ratio = sx * w/sw;
+ x -= w_ratio;
+ w += w_ratio;
+ sw += sx;
+ sx = 0;
+ }
+
+ if (sy < 0) {
+ qreal h_ratio = sy * h/sh;
+ y -= h_ratio;
+ h += h_ratio;
+ sh += sy;
+ sy = 0;
+ }
+
+ if (sw + sx > image.width()) {
+ qreal delta = sw - (image.width() - sx);
+ qreal w_ratio = delta * w/sw;
+ sw -= delta;
+ w -= w_ratio;
+ }
+
+ if (sh + sy > image.height()) {
+ qreal delta = sh - (image.height() - sy);
+ qreal h_ratio = delta * h/sh;
+ sh -= delta;
+ h -= h_ratio;
+ }
+
+ if (w == 0 || h == 0 || sw <= 0 || sh <= 0)
+ return;
+
+ if (d->extended) {
+ d->extended->drawImage(QRectF(x, y, w, h), image, QRectF(sx, sy, sw, sh), flags);
+ return;
+ }
+
+ d->updateState(d->state);
+
+ if (((d->state->matrix.type() > QTransform::TxTranslate || (sw != w || sh != h))
+ && !d->engine->hasFeature(QPaintEngine::PixmapTransform))
+ || (!d->state->matrix.isAffine() && !d->engine->hasFeature(QPaintEngine::PerspectiveTransform))
+ || (d->state->opacity != 1.0 && !d->engine->hasFeature(QPaintEngine::ConstantOpacity)))
+ {
+ save();
+ // If there is no scaling or transformation involved we have to make sure we use the
+ // antialiased and not the aliased coordinate system by rounding the coordinates.
+ if (d->state->matrix.type() <= QTransform::TxTranslate && sw == w && sh == h) {
+ x = qRound(x + d->state->matrix.dx()) - d->state->matrix.dx();
+ y = qRound(y + d->state->matrix.dy()) - d->state->matrix.dy();
+ sx = qRound(sx);
+ sy = qRound(sy);
+ sw = qRound(sw);
+ sh = qRound(sh);
+ }
+ translate(x, y);
+ scale(w / sw, h / sh);
+ setBackgroundMode(Qt::TransparentMode);
+ setRenderHint(Antialiasing, renderHints() & SmoothPixmapTransform);
+ QBrush brush(image);
+ setBrush(brush);
+ setPen(Qt::NoPen);
+ setBrushOrigin(QPointF(-sx, -sy));
+
+ drawRect(QRectF(0, 0, sw, sh));
+ restore();
+ return;
+ }
+
+ if (d->state->matrix.type() == QTransform::TxTranslate
+ && !d->engine->hasFeature(QPaintEngine::PixmapTransform)) {
+ x += d->state->matrix.dx();
+ y += d->state->matrix.dy();
+ }
+
+ d->engine->drawImage(QRectF(x, y, w, h), image, QRectF(sx, sy, sw, sh), flags);
+}
+
+/*!
+ \fn void QPainter::drawText(const QPointF &position, const QString &text)
+
+ Draws the given \a text with the currently defined text direction,
+ beginning at the given \a position.
+
+ This function does not handle the newline character (\n), as it cannot
+ break text into multiple lines, and it cannot display the newline character.
+ Use the QPainter::drawText() overload that takes a rectangle instead
+ if you want to draw multiple lines of text with the newline character, or
+ if you want the text to be wrapped.
+
+ By default, QPainter draws text anti-aliased.
+
+ \note The y-position is used as the baseline of the font.
+*/
+
+void QPainter::drawText(const QPointF &p, const QString &str)
+{
+ drawText(p, str, 0, 0);
+}
+
+/*!
+ \internal
+*/
+void QPainter::drawText(const QPointF &p, const QString &str, int tf, int justificationPadding)
+{
+#ifdef QT_DEBUG_DRAW
+ if (qt_show_painter_debug_output)
+ printf("QPainter::drawText(), pos=[%.2f,%.2f], str='%s'\n", p.x(), p.y(), str.toLatin1().constData());
+#endif
+
+ Q_D(QPainter);
+
+ if (!d->engine || str.isEmpty() || pen().style() == Qt::NoPen)
+ return;
+
+ QStackTextEngine engine(str, d->state->font);
+ engine.option.setTextDirection(d->state->layoutDirection);
+ if (tf & (Qt::TextForceLeftToRight|Qt::TextForceRightToLeft)) {
+ engine.ignoreBidi = true;
+ engine.option.setTextDirection((tf & Qt::TextForceLeftToRight) ? Qt::LeftToRight : Qt::RightToLeft);
+ }
+ engine.itemize();
+ QScriptLine line;
+ line.length = str.length();
+ engine.shapeLine(line);
+
+ int nItems = engine.layoutData->items.size();
+ QVarLengthArray<int> visualOrder(nItems);
+ QVarLengthArray<uchar> levels(nItems);
+ for (int i = 0; i < nItems; ++i)
+ levels[i] = engine.layoutData->items[i].analysis.bidiLevel;
+ QTextEngine::bidiReorder(nItems, levels.data(), visualOrder.data());
+
+ if (justificationPadding > 0) {
+ engine.option.setAlignment(Qt::AlignJustify);
+ engine.forceJustification = true;
+ // this works because justify() is only interested in the difference between width and textWidth
+ line.width = justificationPadding;
+ engine.justify(line);
+ }
+ QFixed x = QFixed::fromReal(p.x());
+ QFixed ox = x;
+
+ for (int i = 0; i < nItems; ++i) {
+ int item = visualOrder[i];
+ const QScriptItem &si = engine.layoutData->items.at(item);
+ if (si.analysis.flags >= QScriptAnalysis::TabOrObject) {
+ x += si.width;
+ continue;
+ }
+ QFont f = engine.font(si);
+ QTextItemInt gf(si, &f);
+ gf.glyphs = engine.shapedGlyphs(&si);
+ gf.chars = engine.layoutData->string.unicode() + si.position;
+ gf.num_chars = engine.length(item);
+ gf.width = si.width;
+ gf.logClusters = engine.logClusters(&si);
+
+ drawTextItem(QPointF(x.toReal(), p.y()), gf);
+
+ x += si.width;
+ }
+}
+
+void QPainter::drawText(const QRect &r, int flags, const QString &str, QRect *br)
+{
+#ifdef QT_DEBUG_DRAW
+ if (qt_show_painter_debug_output)
+ printf("QPainter::drawText(), r=[%d,%d,%d,%d], flags=%d, str='%s'\n",
+ r.x(), r.y(), r.width(), r.height(), flags, str.toLatin1().constData());
+#endif
+
+ Q_D(QPainter);
+
+ if (!d->engine || str.length() == 0 || pen().style() == Qt::NoPen)
+ return;
+
+ if (!d->extended)
+ d->updateState(d->state);
+
+ QRectF bounds;
+ qt_format_text(d->state->font, r, flags, 0, str, br ? &bounds : 0, 0, 0, 0, this);
+ if (br)
+ *br = bounds.toAlignedRect();
+}
+
+/*!
+ \fn void QPainter::drawText(const QPoint &position, const QString &text)
+
+ \overload
+
+ Draws the given \a text with the currently defined text direction,
+ beginning at the given \a position.
+
+ By default, QPainter draws text anti-aliased.
+
+ \note The y-position is used as the baseline of the font.
+
+*/
+
+/*!
+ \fn void QPainter::drawText(const QRectF &rectangle, int flags, const QString &text, QRectF *boundingRect)
+ \overload
+
+ Draws the given \a text within the provided \a rectangle.
+
+ \table 100%
+ \row
+ \o \inlineimage qpainter-text.png
+ \o
+ \snippet doc/src/snippets/code/src_gui_painting_qpainter.cpp 17
+ \endtable
+
+ The \a boundingRect (if not null) is set to the what the bounding rectangle
+ should be in order to enclose the whole text. The \a flags argument is a bitwise
+ OR of the following flags:
+
+ \list
+ \o Qt::AlignLeft
+ \o Qt::AlignRight
+ \o Qt::AlignHCenter
+ \o Qt::AlignJustify
+ \o Qt::AlignTop
+ \o Qt::AlignBottom
+ \o Qt::AlignVCenter
+ \o Qt::AlignCenter
+ \o Qt::TextDontClip
+ \o Qt::TextSingleLine
+ \o Qt::TextExpandTabs
+ \o Qt::TextShowMnemonic
+ \o Qt::TextWordWrap
+ \o Qt::TextIncludeTrailingSpaces
+ \endlist
+
+ \sa Qt::AlignmentFlag, Qt::TextFlag, boundingRect(), layoutDirection()
+
+ By default, QPainter draws text anti-aliased.
+
+ \note The y-coordinate of \a rectangle is used as the top of the font.
+*/
+void QPainter::drawText(const QRectF &r, int flags, const QString &str, QRectF *br)
+{
+#ifdef QT_DEBUG_DRAW
+ if (qt_show_painter_debug_output)
+ printf("QPainter::drawText(), r=[%.2f,%.2f,%.2f,%.2f], flags=%d, str='%s'\n",
+ r.x(), r.y(), r.width(), r.height(), flags, str.toLatin1().constData());
+#endif
+
+ Q_D(QPainter);
+
+ if (!d->engine || str.length() == 0 || pen().style() == Qt::NoPen)
+ return;
+
+ if (!d->extended)
+ d->updateState(d->state);
+
+ qt_format_text(d->state->font, r, flags, 0, str, br, 0, 0, 0, this);
+}
+
+/*!
+ \fn void QPainter::drawText(const QRect &rectangle, int flags, const QString &text, QRect *boundingRect)
+ \overload
+
+ Draws the given \a text within the provided \a rectangle according
+ to the specified \a flags. The \a boundingRect (if not null) is set to
+ the what the bounding rectangle should be in order to enclose the whole text.
+
+ By default, QPainter draws text anti-aliased.
+
+ \note The y-coordinate of \a rectangle is used as the top of the font.
+*/
+
+/*!
+ \fn void QPainter::drawText(int x, int y, const QString &text)
+
+ \overload
+
+ Draws the given \a text at position (\a{x}, \a{y}), using the painter's
+ currently defined text direction.
+
+ By default, QPainter draws text anti-aliased.
+
+ \note The y-position is used as the baseline of the font.
+
+*/
+
+/*!
+ \fn void QPainter::drawText(int x, int y, int width, int height, int flags,
+ const QString &text, QRect *boundingRect)
+
+ \overload
+
+ Draws the given \a text within the rectangle with origin (\a{x},
+ \a{y}), \a width and \a height.
+
+ The \a boundingRect (if not null) is set to the actual bounding
+ rectangle of the output. The \a flags argument is a bitwise OR of
+ the following flags:
+
+ \list
+ \o Qt::AlignLeft
+ \o Qt::AlignRight
+ \o Qt::AlignHCenter
+ \o Qt::AlignJustify
+ \o Qt::AlignTop
+ \o Qt::AlignBottom
+ \o Qt::AlignVCenter
+ \o Qt::AlignCenter
+ \o Qt::TextSingleLine
+ \o Qt::TextExpandTabs
+ \o Qt::TextShowMnemonic
+ \o Qt::TextWordWrap
+ \endlist
+
+ By default, QPainter draws text anti-aliased.
+
+ \note The y-position is used as the baseline of the font.
+
+ \sa Qt::AlignmentFlag, Qt::TextFlag
+*/
+
+/*!
+ \fn void QPainter::drawText(const QRectF &rectangle, const QString &text,
+ const QTextOption &option)
+ \overload
+
+ Draws the given \a text in the \a rectangle specified using the \a option
+ to control its positioning and orientation.
+
+ By default, QPainter draws text anti-aliased.
+
+ \note The y-coordinate of \a rectangle is used as the top of the font.
+*/
+void QPainter::drawText(const QRectF &r, const QString &text, const QTextOption &o)
+{
+#ifdef QT_DEBUG_DRAW
+ if (qt_show_painter_debug_output)
+ printf("QPainter::drawText(), r=[%.2f,%.2f,%.2f,%.2f], str='%s'\n",
+ r.x(), r.y(), r.width(), r.height(), text.toLatin1().constData());
+#endif
+
+ Q_D(QPainter);
+
+ if (!d->engine || text.length() == 0 || pen().style() == Qt::NoPen)
+ return;
+
+ if (!d->extended)
+ d->updateState(d->state);
+
+ qt_format_text(d->state->font, r, 0, &o, text, 0, 0, 0, 0, this);
+}
+
+/*!
+ \fn void QPainter::drawTextItem(int x, int y, const QTextItem &ti)
+
+ \internal
+ \overload
+*/
+
+/*!
+ \fn void QPainter::drawTextItem(const QPoint &p, const QTextItem &ti)
+
+ \internal
+ \overload
+
+ Draws the text item \a ti at position \a p.
+*/
+
+/*! \internal
+ Draws the text item \a ti at position \a p.
+
+ This method ignores the painters background mode and
+ color. drawText and qt_format_text have to do it themselves, as
+ only they know the extents of the complete string.
+
+ It ignores the font set on the painter as the text item has one of its own.
+
+ The underline and strikeout parameters of the text items font are
+ ignored aswell. You'll need to pass in the correct flags to get
+ underlining and strikeout.
+*/
+static QPainterPath generateWavyPath(qreal minWidth, qreal maxRadius, QPaintDevice *device)
+{
+ extern int qt_defaultDpi();
+ QPainterPath path;
+
+ bool up = true;
+ const qreal radius = qMax(qreal(.5), qMin(qreal(1.25 * device->logicalDpiY() / qt_defaultDpi()), maxRadius));
+ qreal xs, ys;
+ int i = 0;
+ path.moveTo(0, radius);
+ do {
+ xs = i*(2*radius);
+ ys = 0;
+
+ qreal remaining = minWidth - xs;
+ qreal angle = 180;
+
+ // cut-off at the last arc segment
+ if (remaining < 2 * radius)
+ angle = 180 * remaining / (2 * radius);
+
+ path.arcTo(xs, ys, 2*radius, 2*radius, 180, up ? angle : -angle);
+
+ up = !up;
+ ++i;
+ } while (xs + 2*radius < minWidth);
+
+ return path;
+}
+
+static void drawTextItemDecoration(QPainter *painter, const QPointF &pos, const QTextItemInt &ti)
+{
+ QTextCharFormat::UnderlineStyle underlineStyle = ti.underlineStyle;
+ if (underlineStyle == QTextCharFormat::NoUnderline
+ && !(ti.flags & (QTextItem::StrikeOut | QTextItem::Overline)))
+ return;
+
+ QFontEngine *fe = ti.fontEngine;
+
+ const QPen oldPen = painter->pen();
+ const QBrush oldBrush = painter->brush();
+ painter->setBrush(Qt::NoBrush);
+ QPen pen = oldPen;
+ pen.setStyle(Qt::SolidLine);
+ pen.setWidthF(fe->lineThickness().toReal());
+ pen.setCapStyle(Qt::FlatCap);
+
+ QLineF line(pos.x(), pos.y(), pos.x() + ti.width.toReal(), pos.y());
+ // deliberately ceil the offset to avoid the underline coming too close to
+ // the text above it.
+ const qreal underlinePos = pos.y() + qCeil(fe->underlinePosition().toReal());
+
+ if (underlineStyle == QTextCharFormat::SpellCheckUnderline) {
+ underlineStyle = QTextCharFormat::UnderlineStyle(QApplication::style()->styleHint(QStyle::SH_SpellCheckUnderlineStyle));
+ }
+
+ if (underlineStyle == QTextCharFormat::WaveUnderline) {
+ painter->save();
+ painter->setRenderHint(QPainter::Antialiasing);
+ painter->translate(pos.x(), underlinePos);
+
+ QColor uc = ti.charFormat.underlineColor();
+ if (uc.isValid())
+ painter->setPen(uc);
+
+ painter->drawPath(generateWavyPath(ti.width.toReal(),
+ fe->underlinePosition().toReal(),
+ painter->device()));
+ painter->restore();
+ } else if (underlineStyle != QTextCharFormat::NoUnderline) {
+ QLineF underLine(line.x1(), underlinePos, line.x2(), underlinePos);
+
+ QColor uc = ti.charFormat.underlineColor();
+ if (uc.isValid())
+ pen.setColor(uc);
+
+ pen.setStyle((Qt::PenStyle)(underlineStyle));
+ painter->setPen(pen);
+ painter->drawLine(underLine);
+ }
+
+ pen.setStyle(Qt::SolidLine);
+ pen.setColor(oldPen.color());
+
+ if (ti.flags & QTextItem::StrikeOut) {
+ QLineF strikeOutLine = line;
+ strikeOutLine.translate(0., - fe->ascent().toReal() / 3.);
+ painter->setPen(pen);
+ painter->drawLine(strikeOutLine);
+ }
+
+ if (ti.flags & QTextItem::Overline) {
+ QLineF overLine = line;
+ overLine.translate(0., - fe->ascent().toReal());
+ painter->setPen(pen);
+ painter->drawLine(overLine);
+ }
+
+ painter->setPen(oldPen);
+ painter->setBrush(oldBrush);
+}
+
+/*!
+ \internal
+ \since 4.1
+*/
+void QPainter::drawTextItem(const QPointF &p, const QTextItem &_ti)
+{
+#ifdef QT_DEBUG_DRAW
+ if (qt_show_painter_debug_output)
+ printf("QPainter::drawTextItem(), pos=[%.f,%.f], str='%s'\n",
+ p.x(), p.y(), qPrintable(_ti.text()));
+#endif
+
+ Q_D(QPainter);
+
+ if (!d->engine)
+ return;
+
+#ifndef QT_NO_DEBUG
+ qt_painter_thread_test(d->device->devType(),
+ "text and fonts",
+ QFontDatabase::supportsThreadedFontRendering());
+#endif
+
+ QTextItemInt &ti = const_cast<QTextItemInt &>(static_cast<const QTextItemInt &>(_ti));
+
+ if (!d->extended && d->state->bgMode == Qt::OpaqueMode) {
+ QRectF rect(p.x(), p.y() - ti.ascent.toReal(), ti.width.toReal(), (ti.ascent + ti.descent + 1).toReal());
+ fillRect(rect, d->state->bgBrush);
+ }
+
+ if (pen().style() == Qt::NoPen)
+ return;
+
+ const RenderHints oldRenderHints = d->state->renderHints;
+ if (!d->state->renderHints & QPainter::Antialiasing && d->state->matrix.type() >= QTransform::TxScale) {
+ // draw antialias decoration (underline/overline/strikeout) with
+ // transformed text
+
+ bool aa = true;
+ const QTransform &m = d->state->matrix;
+ if (d->state->matrix.type() < QTransform::TxShear) {
+ bool isPlain90DegreeRotation =
+ (qFuzzyCompare(m.m11() + 1, qreal(1))
+ && qFuzzyCompare(m.m12(), qreal(1))
+ && qFuzzyCompare(m.m21(), qreal(-1))
+ && qFuzzyCompare(m.m22() + 1, qreal(1))
+ )
+ ||
+ (qFuzzyCompare(m.m11(), qreal(-1))
+ && qFuzzyCompare(m.m12() + 1, qreal(1))
+ && qFuzzyCompare(m.m21() + 1, qreal(1))
+ && qFuzzyCompare(m.m22(), qreal(-1))
+ )
+ ||
+ (qFuzzyCompare(m.m11() + 1, qreal(1))
+ && qFuzzyCompare(m.m12(), qreal(-1))
+ && qFuzzyCompare(m.m21(), qreal(1))
+ && qFuzzyCompare(m.m22() + 1, qreal(1))
+ )
+ ;
+ aa = !isPlain90DegreeRotation;
+ }
+ if (aa)
+ setRenderHint(QPainter::Antialiasing, true);
+ }
+
+ if (!d->extended)
+ d->updateState(d->state);
+
+ if (!ti.glyphs.numGlyphs) {
+ // nothing to do
+ } else if (ti.fontEngine->type() == QFontEngine::Multi) {
+ QFontEngineMulti *multi = static_cast<QFontEngineMulti *>(ti.fontEngine);
+
+ const QGlyphLayout &glyphs = ti.glyphs;
+ int which = glyphs.glyphs[0] >> 24;
+
+ qreal x = p.x();
+ qreal y = p.y();
+
+ int start = 0;
+ int end, i;
+ for (end = 0; end < ti.glyphs.numGlyphs; ++end) {
+ const int e = glyphs.glyphs[end] >> 24;
+ if (e == which)
+ continue;
+
+
+ QTextItemInt ti2 = ti.midItem(multi->engine(which), start, end - start);
+ ti2.width = 0;
+ // set the high byte to zero and calc the width
+ for (i = start; i < end; ++i) {
+ glyphs.glyphs[i] = glyphs.glyphs[i] & 0xffffff;
+ ti2.width += ti.glyphs.effectiveAdvance(i);
+ }
+
+ d->engine->drawTextItem(QPointF(x, y), ti2);
+
+ // reset the high byte for all glyphs and advance to the next sub-string
+ const int hi = which << 24;
+ for (i = start; i < end; ++i) {
+ glyphs.glyphs[i] = hi | glyphs.glyphs[i];
+ }
+ x += ti2.width.toReal();
+
+ // change engine
+ start = end;
+ which = e;
+ }
+
+ QTextItemInt ti2 = ti.midItem(multi->engine(which), start, end - start);
+ ti2.width = 0;
+ // set the high byte to zero and calc the width
+ for (i = start; i < end; ++i) {
+ glyphs.glyphs[i] = glyphs.glyphs[i] & 0xffffff;
+ ti2.width += ti.glyphs.effectiveAdvance(i);
+ }
+
+ if (d->extended)
+ d->extended->drawTextItem(QPointF(x, y), ti2);
+ else
+ d->engine->drawTextItem(QPointF(x,y), ti2);
+
+ // reset the high byte for all glyphs
+ const int hi = which << 24;
+ for (i = start; i < end; ++i)
+ glyphs.glyphs[i] = hi | glyphs.glyphs[i];
+
+ } else {
+ if (d->extended)
+ d->extended->drawTextItem(p, ti);
+ else
+ d->engine->drawTextItem(p, ti);
+ }
+ drawTextItemDecoration(this, p, ti);
+
+ if (d->state->renderHints != oldRenderHints) {
+ d->state->renderHints = oldRenderHints;
+ if (d->extended)
+ d->extended->renderHintsChanged();
+ else
+ d->state->dirtyFlags |= QPaintEngine::DirtyHints;
+ }
+}
+
+/*!
+ \fn QRectF QPainter::boundingRect(const QRectF &rectangle, int flags, const QString &text)
+
+ Returns the bounding rectangle of the \a text as it will appear
+ when drawn inside the given \a rectangle with the specified \a
+ flags using the currently set font(); i.e the function tells you
+ where the drawText() function will draw when given the same
+ arguments.
+
+ If the \a text does not fit within the given \a rectangle using
+ the specified \a flags, the function returns the required
+ rectangle.
+
+ The \a flags argument is a bitwise OR of the following flags:
+ \list
+ \o Qt::AlignLeft
+ \o Qt::AlignRight
+ \o Qt::AlignHCenter
+ \o Qt::AlignTop
+ \o Qt::AlignBottom
+ \o Qt::AlignVCenter
+ \o Qt::AlignCenter
+ \o Qt::TextSingleLine
+ \o Qt::TextExpandTabs
+ \o Qt::TextShowMnemonic
+ \o Qt::TextWordWrap
+ \o Qt::TextIncludeTrailingSpaces
+ \endlist
+ If several of the horizontal or several of the vertical alignment
+ flags are set, the resulting alignment is undefined.
+
+ \sa drawText(), Qt::Alignment, Qt::TextFlag
+*/
+
+/*!
+ \fn QRect QPainter::boundingRect(const QRect &rectangle, int flags,
+ const QString &text)
+
+ \overload
+
+ Returns the bounding rectangle of the \a text as it will appear
+ when drawn inside the given \a rectangle with the specified \a
+ flags using the currently set font().
+*/
+
+/*!
+ \fn QRect QPainter::boundingRect(int x, int y, int w, int h, int flags,
+ const QString &text);
+
+ \overload
+
+ Returns the bounding rectangle of the given \a text as it will
+ appear when drawn inside the rectangle beginning at the point
+ (\a{x}, \a{y}) with width \a w and height \a h.
+*/
+QRect QPainter::boundingRect(const QRect &rect, int flags, const QString &str)
+{
+ if (str.isEmpty())
+ return QRect(rect.x(),rect.y(), 0,0);
+ QRect brect;
+ drawText(rect, flags | Qt::TextDontPrint, str, &brect);
+ return brect;
+}
+
+
+
+QRectF QPainter::boundingRect(const QRectF &rect, int flags, const QString &str)
+{
+ if (str.isEmpty())
+ return QRectF(rect.x(),rect.y(), 0,0);
+ QRectF brect;
+ drawText(rect, flags | Qt::TextDontPrint, str, &brect);
+ return brect;
+}
+
+/*!
+ \fn QRectF QPainter::boundingRect(const QRectF &rectangle,
+ const QString &text, const QTextOption &option)
+
+ \overload
+
+ Instead of specifying flags as a bitwise OR of the
+ Qt::AlignmentFlag and Qt::TextFlag, this overloaded function takes
+ an \a option argument. The QTextOption class provides a
+ description of general rich text properties.
+
+ \sa QTextOption
+*/
+QRectF QPainter::boundingRect(const QRectF &r, const QString &text, const QTextOption &o)
+{
+ Q_D(QPainter);
+
+ if (!d->engine || text.length() == 0)
+ return QRectF(r.x(),r.y(), 0,0);
+
+ QRectF br;
+ qt_format_text(d->state->font, r, Qt::TextDontPrint, &o, text, &br, 0, 0, 0, this);
+ return br;
+}
+
+/*!
+ \fn void QPainter::drawTiledPixmap(const QRectF &rectangle, const QPixmap &pixmap, const QPointF &position)
+
+ Draws a tiled \a pixmap, inside the given \a rectangle with its
+ origin at the given \a position.
+
+ Calling drawTiledPixmap() is similar to calling drawPixmap()
+ several times to fill (tile) an area with a pixmap, but is
+ potentially much more efficient depending on the underlying window
+ system.
+
+ \sa drawPixmap()
+*/
+void QPainter::drawTiledPixmap(const QRectF &r, const QPixmap &pixmap, const QPointF &sp)
+{
+#ifdef QT_DEBUG_DRAW
+ if (qt_show_painter_debug_output)
+ printf("QPainter::drawTiledPixmap(), target=[%.2f,%.2f,%.2f,%.2f], pix=[%d,%d], offset=[%.2f,%.2f]\n",
+ r.x(), r.y(), r.width(), r.height(),
+ pixmap.width(), pixmap.height(),
+ sp.x(), sp.y());
+#endif
+
+ Q_D(QPainter);
+ if (!d->engine || pixmap.isNull() || r.isEmpty())
+ return;
+
+#ifndef QT_NO_DEBUG
+ qt_painter_thread_test(d->device->devType(), "drawTiledPixmap()");
+#endif
+
+ qreal sw = pixmap.width();
+ qreal sh = pixmap.height();
+ qreal sx = sp.x();
+ qreal sy = sp.y();
+ if (sx < 0)
+ sx = qRound(sw) - qRound(-sx) % qRound(sw);
+ else
+ sx = qRound(sx) % qRound(sw);
+ if (sy < 0)
+ sy = qRound(sh) - -qRound(sy) % qRound(sh);
+ else
+ sy = qRound(sy) % qRound(sh);
+
+
+ if (d->extended) {
+ d->extended->drawTiledPixmap(r, pixmap, QPointF(sx, sy));
+ return;
+ }
+
+ if (d->state->bgMode == Qt::OpaqueMode && pixmap.isQBitmap())
+ fillRect(r, d->state->bgBrush);
+
+ d->updateState(d->state);
+ if ((d->state->matrix.type() > QTransform::TxTranslate
+ && !d->engine->hasFeature(QPaintEngine::PixmapTransform))
+ || (d->state->opacity != 1.0 && !d->engine->hasFeature(QPaintEngine::ConstantOpacity)))
+ {
+ save();
+ setBackgroundMode(Qt::TransparentMode);
+ setRenderHint(Antialiasing, renderHints() & SmoothPixmapTransform);
+ setBrush(QBrush(d->state->pen.color(), pixmap));
+ setPen(Qt::NoPen);
+
+ // If there is no scaling or transformation involved we have to make sure we use the
+ // antialiased and not the aliased coordinate system by rounding the coordinates.
+ if (d->state->matrix.type() <= QTransform::TxTranslate) {
+ qreal x = qRound(r.x() + d->state->matrix.dx()) - d->state->matrix.dx();
+ qreal y = qRound(r.y() + d->state->matrix.dy()) - d->state->matrix.dy();
+ qreal w = qRound(r.width());
+ qreal h = qRound(r.height());
+ sx = qRound(sx);
+ sy = qRound(sy);
+ setBrushOrigin(QPointF(r.x()-sx, r.y()-sy));
+ drawRect(QRectF(x, y, w, h));
+ } else {
+ setBrushOrigin(QPointF(r.x()-sx, r.y()-sy));
+ drawRect(r);
+ }
+ restore();
+ return;
+ }
+
+ qreal x = r.x();
+ qreal y = r.y();
+ if (d->state->matrix.type() == QTransform::TxTranslate
+ && !d->engine->hasFeature(QPaintEngine::PixmapTransform)) {
+ x += d->state->matrix.dx();
+ y += d->state->matrix.dy();
+ }
+
+ d->engine->drawTiledPixmap(QRectF(x, y, r.width(), r.height()), pixmap, QPointF(sx, sy));
+}
+
+/*!
+ \fn QPainter::drawTiledPixmap(const QRect &rectangle, const QPixmap &pixmap,
+ const QPoint &position = QPoint())
+ \overload
+
+ Draws a tiled \a pixmap, inside the given \a rectangle with its
+ origin at the given \a position.
+*/
+
+/*!
+ \fn void QPainter::drawTiledPixmap(int x, int y, int width, int height, const
+ QPixmap &pixmap, int sx, int sy);
+ \overload
+
+ Draws a tiled \a pixmap in the specified rectangle.
+
+ (\a{x}, \a{y}) specifies the top-left point in the paint device
+ that is to be drawn onto; with the given \a width and \a
+ height. (\a{sx}, \a{sy}) specifies the top-left point in the \a
+ pixmap that is to be drawn; this defaults to (0, 0).
+*/
+
+#ifndef QT_NO_PICTURE
+
+/*!
+ \fn void QPainter::drawPicture(const QPointF &point, const QPicture &picture)
+
+ Replays the given \a picture at the given \a point.
+
+ The QPicture class is a paint device that records and replays
+ QPainter commands. A picture serializes the painter commands to an
+ IO device in a platform-independent format. Everything that can be
+ painted on a widget or pixmap can also be stored in a picture.
+
+ This function does exactly the same as QPicture::play() when
+ called with \a point = QPoint(0, 0).
+
+ \table 100%
+ \row
+ \o
+ \snippet doc/src/snippets/code/src_gui_painting_qpainter.cpp 18
+ \endtable
+
+ \sa QPicture::play()
+*/
+
+void QPainter::drawPicture(const QPointF &p, const QPicture &picture)
+{
+ Q_D(QPainter);
+
+ if (!d->engine)
+ return;
+
+ if (!d->extended)
+ d->updateState(d->state);
+
+ save();
+ translate(p);
+ const_cast<QPicture *>(&picture)->play(this);
+ restore();
+}
+
+/*!
+ \fn void QPainter::drawPicture(const QPoint &point, const QPicture &picture)
+ \overload
+
+ Replays the given \a picture at the given \a point.
+*/
+
+/*!
+ \fn void QPainter::drawPicture(int x, int y, const QPicture &picture)
+ \overload
+
+ Draws the given \a picture at point (\a x, \a y).
+*/
+
+#endif // QT_NO_PICTURE
+
+/*!
+ \fn void QPainter::eraseRect(const QRectF &rectangle)
+
+ Erases the area inside the given \a rectangle. Equivalent to
+ calling
+ \snippet doc/src/snippets/code/src_gui_painting_qpainter.cpp 19
+
+ \sa fillRect()
+*/
+void QPainter::eraseRect(const QRectF &r)
+{
+ Q_D(QPainter);
+
+ fillRect(r, d->state->bgBrush);
+}
+
+static inline bool needsResolving(const QBrush &brush)
+{
+ Qt::BrushStyle s = brush.style();
+ return ((s == Qt::LinearGradientPattern || s == Qt::RadialGradientPattern ||
+ s == Qt::ConicalGradientPattern) &&
+ brush.gradient()->coordinateMode() == QGradient::ObjectBoundingMode);
+}
+
+/*!
+ \fn void QPainter::eraseRect(const QRect &rectangle)
+ \overload
+
+ Erases the area inside the given \a rectangle.
+*/
+
+/*!
+ \fn void QPainter::eraseRect(int x, int y, int width, int height)
+ \overload
+
+ Erases the area inside the rectangle beginning at (\a x, \a y)
+ with the given \a width and \a height.
+*/
+
+
+/*!
+ \fn void QPainter::fillRect(int x, int y, int width, int height, Qt::BrushStyle style)
+ \overload
+
+ Fills the rectangle beginning at (\a{x}, \a{y}) with the given \a
+ width and \a height, using the brush \a style specified.
+
+ \since 4.5
+*/
+
+/*!
+ \fn void QPainter::fillRect(const QRect &rectangle, Qt::BrushStyle style)
+ \overload
+
+ Fills the given \a rectangle with the brush \a style specified.
+
+ \since 4.5
+*/
+
+/*!
+ \fn void QPainter::fillRect(const QRectF &rectangle, Qt::BrushStyle style)
+ \overload
+
+ Fills the given \a rectangle with the brush \a style specified.
+
+ \since 4.5
+*/
+
+/*!
+ \fn void QPainter::fillRect(const QRectF &rectangle, const QBrush &brush)
+
+ Fills the given \a rectangle with the \a brush specified.
+
+ Alternatively, you can specify a QColor instead of a QBrush; the
+ QBrush constructor (taking a QColor argument) will automatically
+ create a solid pattern brush.
+
+ \sa drawRect()
+*/
+void QPainter::fillRect(const QRectF &r, const QBrush &brush)
+{
+ Q_D(QPainter);
+
+ if (!d->engine)
+ return;
+
+ if (d->extended) {
+ const QGradient *g = brush.gradient();
+ if (!g || g->coordinateMode() == QGradient::LogicalMode) {
+ d->extended->fillRect(r, brush);
+ return;
+ }
+ }
+
+ QPen oldPen = pen();
+ QBrush oldBrush = this->brush();
+ setPen(Qt::NoPen);
+ if (brush.style() == Qt::SolidPattern) {
+ d->colorBrush.setStyle(Qt::SolidPattern);
+ d->colorBrush.setColor(brush.color());
+ setBrush(d->colorBrush);
+ } else {
+ setBrush(brush);
+ }
+
+ drawRect(r);
+ setBrush(oldBrush);
+ setPen(oldPen);
+}
+
+/*!
+ \fn void QPainter::fillRect(const QRect &rectangle, const QBrush &brush)
+ \overload
+
+ Fills the given \a rectangle with the specified \a brush.
+*/
+
+void QPainter::fillRect(const QRect &r, const QBrush &brush)
+{
+ Q_D(QPainter);
+
+ if (!d->engine)
+ return;
+
+ if (d->extended) {
+ const QGradient *g = brush.gradient();
+ if (!g || g->coordinateMode() == QGradient::LogicalMode) {
+ d->extended->fillRect(r, brush);
+ return;
+ }
+ }
+
+ QPen oldPen = pen();
+ QBrush oldBrush = this->brush();
+ setPen(Qt::NoPen);
+ if (brush.style() == Qt::SolidPattern) {
+ d->colorBrush.setStyle(Qt::SolidPattern);
+ d->colorBrush.setColor(brush.color());
+ setBrush(d->colorBrush);
+ } else {
+ setBrush(brush);
+ }
+
+ drawRect(r);
+ setBrush(oldBrush);
+ setPen(oldPen);
+}
+
+
+
+/*!
+ \fn void QPainter::fillRect(const QRect &rectangle, const QColor &color)
+ \overload
+
+ Fills the given \a rectangle with the \a color specified.
+
+ \since 4.5
+*/
+void QPainter::fillRect(const QRect &r, const QColor &color)
+{
+ Q_D(QPainter);
+
+ if (!d->engine)
+ return;
+
+ if (d->extended) {
+ d->extended->fillRect(r, color);
+ return;
+ }
+
+ fillRect(r, QBrush(color));
+}
+
+
+/*!
+ \fn void QPainter::fillRect(const QRectF &rectangle, const QColor &color)
+ \overload
+
+ Fills the given \a rectangle with the \a color specified.
+
+ \since 4.5
+*/
+void QPainter::fillRect(const QRectF &r, const QColor &color)
+{
+ Q_D(QPainter);
+
+ if (!d->engine)
+ return;
+
+ if (d->extended) {
+ d->extended->fillRect(r, color);
+ return;
+ }
+
+ fillRect(r, QBrush(color));
+}
+
+/*!
+ \fn void QPainter::fillRect(int x, int y, int width, int height, const QBrush &brush)
+
+ \overload
+
+ Fills the rectangle beginning at (\a{x}, \a{y}) with the given \a
+ width and \a height, using the given \a brush.
+*/
+
+/*!
+ \fn void QPainter::fillRect(int x, int y, int width, int height, const QColor &color)
+
+ \overload
+
+ Fills the rectangle beginning at (\a{x}, \a{y}) with the given \a
+ width and \a height, using the given \a color.
+
+ \since 4.5
+*/
+
+/*!
+ \fn void QPainter::fillRect(int x, int y, int width, int height, Qt::GlobalColor color)
+
+ \overload
+
+ Fills the rectangle beginning at (\a{x}, \a{y}) with the given \a
+ width and \a height, using the given \a color.
+
+ \since 4.5
+*/
+
+/*!
+ \fn void QPainter::fillRect(const QRect &rectangle, Qt::GlobalColor color);
+
+ \overload
+
+ Fills the given \a rectangle with the specified \a color.
+
+ \since 4.5
+*/
+
+/*!
+ \fn void QPainter::fillRect(const QRectF &rectangle, Qt::GlobalColor color);
+
+ \overload
+
+ Fills the given \a rectangle with the specified \a color.
+
+ \since 4.5
+*/
+
+/*!
+ Sets the given render \a hint on the painter if \a on is true;
+ otherwise clears the render hint.
+
+ \sa setRenderHints(), renderHints(), {QPainter#Rendering
+ Quality}{Rendering Quality}
+*/
+void QPainter::setRenderHint(RenderHint hint, bool on)
+{
+#ifdef QT_DEBUG_DRAW
+ if (qt_show_painter_debug_output)
+ printf("QPainter::setRenderHint: hint=%x, %s\n", hint, on ? "on" : "off");
+#endif
+
+#ifndef QT_NO_DEBUG
+ static const bool antialiasingDisabled = qgetenv("QT_NO_ANTIALIASING").toInt();
+ if (hint == QPainter::Antialiasing && antialiasingDisabled)
+ return;
+#endif
+
+ setRenderHints(hint, on);
+}
+
+/*!
+ \since 4.2
+
+ Sets the given render \a hints on the painter if \a on is true;
+ otherwise clears the render hints.
+
+ \sa setRenderHint(), renderHints(), {QPainter#Rendering
+ Quality}{Rendering Quality}
+*/
+
+void QPainter::setRenderHints(RenderHints hints, bool on)
+{
+ Q_D(QPainter);
+
+ if (!d->engine) {
+ qWarning("QPainter::setRenderHint: Painter must be active to set rendering hints");
+ return;
+ }
+
+ if (on)
+ d->state->renderHints |= hints;
+ else
+ d->state->renderHints &= ~hints;
+
+ if (d->extended)
+ d->extended->renderHintsChanged();
+ else
+ d->state->dirtyFlags |= QPaintEngine::DirtyHints;
+}
+
+/*!
+ Returns a flag that specifies the rendering hints that are set for
+ this painter.
+
+ \sa testRenderHint(), {QPainter#Rendering Quality}{Rendering Quality}
+*/
+QPainter::RenderHints QPainter::renderHints() const
+{
+ Q_D(const QPainter);
+
+ if (!d->engine)
+ return 0;
+
+ return d->state->renderHints;
+}
+
+/*!
+ \fn bool QPainter::testRenderHint(RenderHint hint) const
+ \since 4.3
+
+ Returns true if \a hint is set; otherwise returns false.
+
+ \sa renderHints(), setRenderHint()
+*/
+
+/*!
+ Returns true if view transformation is enabled; otherwise returns
+ false.
+
+ \sa setViewTransformEnabled(), worldMatrix()
+*/
+
+bool QPainter::viewTransformEnabled() const
+{
+ Q_D(const QPainter);
+ if (!d->engine) {
+ qWarning("QPainter::viewTransformEnabled: Painter not active");
+ return false;
+ }
+ return d->state->VxF;
+}
+
+/*!
+ \fn void QPainter::setWindow(const QRect &rectangle)
+
+ Sets the painter's window to the given \a rectangle, and enables
+ view transformations.
+
+ The window rectangle is part of the view transformation. The
+ window specifies the logical coordinate system. Its sister, the
+ viewport(), specifies the device coordinate system.
+
+ The default window rectangle is the same as the device's
+ rectangle.
+
+ \sa window(), viewTransformEnabled(), {The Coordinate
+ System#Window-Viewport Conversion}{Window-Viewport Conversion}
+*/
+
+/*!
+ \fn void QPainter::setWindow(int x, int y, int width, int height)
+ \overload
+
+ Sets the painter's window to the rectangle beginning at (\a x, \a
+ y) and the given \a width and \a height.
+*/
+
+void QPainter::setWindow(const QRect &r)
+{
+#ifdef QT_DEBUG_DRAW
+ if (qt_show_painter_debug_output)
+ printf("QPainter::setWindow(), [%d,%d,%d,%d]\n", r.x(), r.y(), r.width(), r.height());
+#endif
+
+ Q_D(QPainter);
+
+ if (!d->engine) {
+ qWarning("QPainter::setWindow: Painter not active");
+ return;
+ }
+
+ d->state->wx = r.x();
+ d->state->wy = r.y();
+ d->state->ww = r.width();
+ d->state->wh = r.height();
+
+ d->state->VxF = true;
+ d->updateMatrix();
+}
+
+/*!
+ Returns the window rectangle.
+
+ \sa setWindow(), setViewTransformEnabled()
+*/
+
+QRect QPainter::window() const
+{
+ Q_D(const QPainter);
+ if (!d->engine) {
+ qWarning("QPainter::window: Painter not active");
+ return QRect();
+ }
+ return QRect(d->state->wx, d->state->wy, d->state->ww, d->state->wh);
+}
+
+/*!
+ \fn void QPainter::setViewport(const QRect &rectangle)
+
+ Sets the painter's viewport rectangle to the given \a rectangle,
+ and enables view transformations.
+
+ The viewport rectangle is part of the view transformation. The
+ viewport specifies the device coordinate system. Its sister, the
+ window(), specifies the logical coordinate system.
+
+ The default viewport rectangle is the same as the device's
+ rectangle.
+
+ \sa viewport(), viewTransformEnabled() {The Coordinate
+ System#Window-Viewport Conversion}{Window-Viewport Conversion}
+*/
+
+/*!
+ \fn void QPainter::setViewport(int x, int y, int width, int height)
+ \overload
+
+ Sets the painter's viewport rectangle to be the rectangle
+ beginning at (\a x, \a y) with the given \a width and \a height.
+*/
+
+void QPainter::setViewport(const QRect &r)
+{
+#ifdef QT_DEBUG_DRAW
+ if (qt_show_painter_debug_output)
+ printf("QPainter::setViewport(), [%d,%d,%d,%d]\n", r.x(), r.y(), r.width(), r.height());
+#endif
+
+ Q_D(QPainter);
+
+ if (!d->engine) {
+ qWarning("QPainter::setViewport: Painter not active");
+ return;
+ }
+
+ d->state->vx = r.x();
+ d->state->vy = r.y();
+ d->state->vw = r.width();
+ d->state->vh = r.height();
+
+ d->state->VxF = true;
+ d->updateMatrix();
+}
+
+/*!
+ Returns the viewport rectangle.
+
+ \sa setViewport(), setViewTransformEnabled()
+*/
+
+QRect QPainter::viewport() const
+{
+ Q_D(const QPainter);
+ if (!d->engine) {
+ qWarning("QPainter::viewport: Painter not active");
+ return QRect();
+ }
+ return QRect(d->state->vx, d->state->vy, d->state->vw, d->state->vh);
+}
+
+/*! \fn bool QPainter::hasViewXForm() const
+ \compat
+
+ Use viewTransformEnabled() instead.
+*/
+
+/*! \fn bool QPainter::hasWorldXForm() const
+ \compat
+
+ Use worldMatrixEnabled() instead.
+*/
+
+/*! \fn void QPainter::resetXForm()
+ \compat
+
+ Use resetMatrix() instead.
+*/
+
+/*! \fn void QPainter::setViewXForm(bool enabled)
+ \compat
+
+ Use setViewTransformEnabled() instead.
+*/
+
+/*! \fn void QPainter::setWorldXForm(bool enabled)
+ \compat
+
+ Use setWorldMatrixEnabled() instead.
+*/
+/*!
+ Enables view transformations if \a enable is true, or disables
+ view transformations if \a enable is false.
+
+ \sa viewTransformEnabled(), {The Coordinate System#Window-Viewport
+ Conversion}{Window-Viewport Conversion}
+*/
+
+void QPainter::setViewTransformEnabled(bool enable)
+{
+#ifdef QT_DEBUG_DRAW
+ if (qt_show_painter_debug_output)
+ printf("QPainter::setViewTransformEnabled(), enable=%d\n", enable);
+#endif
+
+ Q_D(QPainter);
+
+ if (!d->engine) {
+ qWarning("QPainter::setViewTransformEnabled: Painter not active");
+ return;
+ }
+
+ if (enable == d->state->VxF)
+ return;
+
+ d->state->VxF = enable;
+ d->updateMatrix();
+}
+
+#ifdef QT3_SUPPORT
+
+/*!
+ Use the worldMatrix() combined with QMatrix::dx() instead.
+
+ \oldcode
+ QPainter painter(this);
+ qreal x = painter.translationX();
+ \newcode
+ QPainter painter(this);
+ qreal x = painter.worldMatrix().dx();
+ \endcode
+*/
+qreal QPainter::translationX() const
+{
+ Q_D(const QPainter);
+ if (!d->engine) {
+ qWarning("QPainter::translationX: Painter not active");
+ return 0.0;
+ }
+ return d->state->worldMatrix.dx();
+}
+
+/*!
+ Use the worldMatrix() combined with QMatrix::dy() instead.
+
+ \oldcode
+ QPainter painter(this);
+ qreal y = painter.translationY();
+ \newcode
+ QPainter painter(this);
+ qreal y = painter.worldMatrix().dy();
+ \endcode
+*/
+qreal QPainter::translationY() const
+{
+ Q_D(const QPainter);
+ if (!d->engine) {
+ qWarning("QPainter::translationY: Painter not active");
+ return 0.0;
+ }
+ return d->state->worldMatrix.dy();
+}
+
+/*!
+ \fn void QPainter::map(int x, int y, int *rx, int *ry) const
+
+ \internal
+
+ Sets (\a{rx}, \a{ry}) to the point that results from applying the
+ painter's current transformation on the point (\a{x}, \a{y}).
+*/
+void QPainter::map(int x, int y, int *rx, int *ry) const
+{
+ QPoint p(x, y);
+ p = p * combinedMatrix();
+ *rx = p.x();
+ *ry = p.y();
+}
+
+/*!
+ \fn QPoint QPainter::xForm(const QPoint &point) const
+
+ Use combinedTransform() instead.
+*/
+
+QPoint QPainter::xForm(const QPoint &p) const
+{
+ Q_D(const QPainter);
+ if (!d->engine) {
+ qWarning("QPainter::xForm: Painter not active");
+ return QPoint();
+ }
+ if (d->state->matrix.type() == QTransform::TxNone)
+ return p;
+ return p * combinedMatrix();
+}
+
+
+/*!
+ \fn QRect QPainter::xForm(const QRect &rectangle) const
+ \overload
+
+ Use combinedTransform() instead of this function and call
+ mapRect() on the result to obtain a QRect.
+*/
+
+QRect QPainter::xForm(const QRect &r) const
+{
+ Q_D(const QPainter);
+ if (!d->engine) {
+ qWarning("QPainter::xForm: Painter not active");
+ return QRect();
+ }
+ if (d->state->matrix.type() == QTransform::TxNone)
+ return r;
+ return combinedMatrix().mapRect(r);
+}
+
+/*!
+ \fn QPolygon QPainter::xForm(const QPolygon &polygon) const
+ \overload
+
+ Use combinedTransform() instead.
+*/
+
+QPolygon QPainter::xForm(const QPolygon &a) const
+{
+ Q_D(const QPainter);
+ if (!d->engine) {
+ qWarning("QPainter::xForm: Painter not active");
+ return QPolygon();
+ }
+ if (d->state->matrix.type() == QTransform::TxNone)
+ return a;
+ return a * combinedMatrix();
+}
+
+/*!
+ \fn QPolygon QPainter::xForm(const QPolygon &polygon, int index, int count) const
+ \overload
+
+ Use combinedTransform() combined with QPolygon::mid() instead.
+
+ \oldcode
+ QPainter painter(this);
+ QPolygon transformed = painter.xForm(polygon, index, count)
+ \newcode
+ QPainter painter(this);
+ QPolygon transformed = polygon.mid(index, count) * painter.combinedMatrix();
+ \endcode
+*/
+
+QPolygon QPainter::xForm(const QPolygon &av, int index, int npoints) const
+{
+ int lastPoint = npoints < 0 ? av.size() : index+npoints;
+ QPolygon a(lastPoint-index);
+ memcpy(a.data(), av.data()+index, (lastPoint-index)*sizeof(QPoint));
+ return a * combinedMatrix();
+}
+
+/*!
+ \fn QPoint QPainter::xFormDev(const QPoint &point) const
+ \overload
+
+ Use combinedTransform() combined with QMatrix::inverted() instead.
+
+ \oldcode
+ QPainter painter(this);
+ QPoint transformed = painter.xFormDev(point);
+ \newcode
+ QPainter painter(this);
+ QPoint transformed = point * painter.combinedMatrix().inverted();
+ \endcode
+*/
+
+QPoint QPainter::xFormDev(const QPoint &p) const
+{
+ Q_D(const QPainter);
+ if (!d->engine) {
+ qWarning("QPainter::xFormDev: Painter not active");
+ return QPoint();
+ }
+ if(d->state->matrix.type() == QTransform::TxNone)
+ return p;
+ return p * combinedMatrix().inverted();
+}
+
+/*!
+ \fn QRect QPainter::xFormDev(const QRect &rectangle) const
+ \overload
+
+ Use combineMatrix() combined with QMatrix::inverted() instead.
+
+ \oldcode
+ QPainter painter(this);
+ QRect transformed = painter.xFormDev(rectangle);
+ \newcode
+ QPainter painter(this);
+ QRect transformed = rectangle * painter.combinedMatrix().inverted();
+ \endcode
+*/
+
+QRect QPainter::xFormDev(const QRect &r) const
+{
+ Q_D(const QPainter);
+ if (!d->engine) {
+ qWarning("QPainter::xFormDev: Painter not active");
+ return QRect();
+ }
+ if (d->state->matrix.type() == QTransform::TxNone)
+ return r;
+ return combinedMatrix().inverted().mapRect(r);
+}
+
+/*!
+ \overload
+
+ \fn QPoint QPainter::xFormDev(const QPolygon &polygon) const
+ \overload
+
+ Use combinedMatrix() combined with QMatrix::inverted() instead.
+
+ \oldcode
+ QPainter painter(this);
+ QPolygon transformed = painter.xFormDev(rectangle);
+ \newcode
+ QPainter painter(this);
+ QPolygon transformed = polygon * painter.combinedMatrix().inverted();
+ \endcode
+*/
+
+QPolygon QPainter::xFormDev(const QPolygon &a) const
+{
+ Q_D(const QPainter);
+ if (!d->engine) {
+ qWarning("QPainter::xFormDev: Painter not active");
+ return QPolygon();
+ }
+ if (d->state->matrix.type() == QTransform::TxNone)
+ return a;
+ return a * combinedMatrix().inverted();
+}
+
+/*!
+ \fn QPolygon QPainter::xFormDev(const QPolygon &polygon, int index, int count) const
+ \overload
+
+ Use combinedMatrix() combined with QPolygon::mid() and QMatrix::inverted() instead.
+
+ \oldcode
+ QPainter painter(this);
+ QPolygon transformed = painter.xFormDev(polygon, index, count);
+ \newcode
+ QPainter painter(this);
+ QPolygon transformed = polygon.mid(index, count) * painter.combinedMatrix().inverted();
+ \endcode
+*/
+
+QPolygon QPainter::xFormDev(const QPolygon &ad, int index, int npoints) const
+{
+ Q_D(const QPainter);
+ int lastPoint = npoints < 0 ? ad.size() : index+npoints;
+ QPolygon a(lastPoint-index);
+ memcpy(a.data(), ad.data()+index, (lastPoint-index)*sizeof(QPoint));
+ if (d->state->matrix.type() == QTransform::TxNone)
+ return a;
+ return a * combinedMatrix().inverted();
+}
+
+/*!
+ \fn void QPainter::drawCubicBezier(const QPolygon &controlPoints, int index)
+
+ Draws a cubic Bezier curve defined by the \a controlPoints,
+ starting at \a{controlPoints}\e{[index]} (\a index defaults to 0).
+ Points after \a{controlPoints}\e{[index + 3]} are ignored. Nothing
+ happens if there aren't enough control points.
+
+ Use strokePath() instead.
+
+ \oldcode
+ QPainter painter(this);
+ painter.drawCubicBezier(controlPoints, index)
+ \newcode
+ QPainterPath path;
+ path.moveTo(controlPoints.at(index));
+ path.cubicTo(controlPoints.at(index+1),
+ controlPoints.at(index+2),
+ controlPoints.at(index+3));
+
+ QPainter painter(this);
+ painter.strokePath(path, painter.pen());
+ \endcode
+*/
+void QPainter::drawCubicBezier(const QPolygon &a, int index)
+{
+ Q_D(QPainter);
+
+ if (!d->engine)
+ return;
+
+ if ((int)a.size() - index < 4) {
+ qWarning("QPainter::drawCubicBezier: Cubic Bezier needs 4 control "
+ "points");
+ return;
+ }
+
+ QPainterPath path;
+ path.moveTo(a.at(index));
+ path.cubicTo(a.at(index+1), a.at(index+2), a.at(index+3));
+ strokePath(path, d->state->pen);
+}
+#endif
+
+struct QPaintDeviceRedirection
+{
+ QPaintDeviceRedirection() : device(0), replacement(0), internalWidgetRedirectionIndex(-1) {}
+ QPaintDeviceRedirection(const QPaintDevice *device, QPaintDevice *replacement,
+ const QPoint& offset, int internalWidgetRedirectionIndex)
+ : device(device), replacement(replacement), offset(offset),
+ internalWidgetRedirectionIndex(internalWidgetRedirectionIndex) { }
+ const QPaintDevice *device;
+ QPaintDevice *replacement;
+ QPoint offset;
+ int internalWidgetRedirectionIndex;
+ bool operator==(const QPaintDevice *pdev) const { return device == pdev; }
+ Q_DUMMY_COMPARISON_OPERATOR(QPaintDeviceRedirection)
+};
+
+typedef QList<QPaintDeviceRedirection> QPaintDeviceRedirectionList;
+Q_GLOBAL_STATIC(QPaintDeviceRedirectionList, globalRedirections)
+Q_GLOBAL_STATIC(QMutex, globalRedirectionsMutex)
+
+/*!
+ \threadsafe
+
+ Redirects all paint commands for the given paint \a device, to the
+ \a replacement device. The optional point \a offset defines an
+ offset within the source device.
+
+ The redirection will not be effective until the begin() function
+ has been called; make sure to call end() for the given \a
+ device's painter (if any) before redirecting. Call
+ restoreRedirected() to restore the previous redirection.
+
+ In general, you'll probably find that calling
+ QPixmap::grabWidget() or QPixmap::grabWindow() is an easier
+ solution.
+
+ \sa redirected(), restoreRedirected()
+*/
+void QPainter::setRedirected(const QPaintDevice *device,
+ QPaintDevice *replacement,
+ const QPoint &offset)
+{
+ Q_ASSERT(device != 0);
+
+ bool hadInternalWidgetRedirection = false;
+ if (device->devType() == QInternal::Widget) {
+ const QWidgetPrivate *widgetPrivate = static_cast<const QWidget *>(device)->d_func();
+ // This is the case when the widget is in a paint event.
+ if (widgetPrivate->redirectDev) {
+ // Remove internal redirection and put it back into the global redirection list.
+ QPoint oldOffset;
+ QPaintDevice *oldReplacement = widgetPrivate->redirected(&oldOffset);
+ const_cast<QWidgetPrivate *>(widgetPrivate)->restoreRedirected();
+ setRedirected(device, oldReplacement, oldOffset);
+ hadInternalWidgetRedirection = true;
+ }
+ }
+
+ QPoint roffset;
+ QPaintDevice *rdev = redirected(replacement, &roffset);
+
+ QMutexLocker locker(globalRedirectionsMutex());
+ QPaintDeviceRedirectionList *redirections = globalRedirections();
+ Q_ASSERT(redirections != 0);
+ *redirections += QPaintDeviceRedirection(device, rdev ? rdev : replacement, offset + roffset,
+ hadInternalWidgetRedirection ? redirections->size() - 1 : -1);
+}
+
+/*!
+ \threadsafe
+
+ Restores the previous redirection for the given \a device after a
+ call to setRedirected().
+
+ \sa redirected()
+ */
+void QPainter::restoreRedirected(const QPaintDevice *device)
+{
+ Q_ASSERT(device != 0);
+ QMutexLocker locker(globalRedirectionsMutex());
+ QPaintDeviceRedirectionList *redirections = globalRedirections();
+ Q_ASSERT(redirections != 0);
+ for (int i = redirections->size()-1; i >= 0; --i) {
+ if (redirections->at(i) == device) {
+ const int internalWidgetRedirectionIndex = redirections->at(i).internalWidgetRedirectionIndex;
+ redirections->removeAt(i);
+ // Restore the internal widget redirection, i.e. remove it from the global
+ // redirection list and put it back into QWidgetPrivate. The index is only set when
+ // someone call QPainter::setRedirected in a widget's paint event and we internally
+ // have a redirection set (typically set in QWidgetPrivate::drawWidget).
+ if (internalWidgetRedirectionIndex >= 0) {
+ Q_ASSERT(internalWidgetRedirectionIndex < redirections->size());
+ const QPaintDeviceRedirection &redirectionDevice = redirections->at(internalWidgetRedirectionIndex);
+ QWidget *widget = static_cast<QWidget *>(const_cast<QPaintDevice *>(device));
+ widget->d_func()->setRedirected(redirectionDevice.replacement, redirectionDevice.offset);
+ redirections->removeAt(internalWidgetRedirectionIndex);
+ }
+ return;
+ }
+ }
+}
+
+/*!
+ \threadsafe
+
+ Returns the replacement for given \a device. The optional out
+ parameter \a offset returns the offset within the replaced device.
+
+ \sa setRedirected(), restoreRedirected()
+*/
+QPaintDevice *QPainter::redirected(const QPaintDevice *device, QPoint *offset)
+{
+ Q_ASSERT(device != 0);
+
+ if (device->devType() == QInternal::Widget) {
+ const QWidgetPrivate *widgetPrivate = static_cast<const QWidget *>(device)->d_func();
+ if (widgetPrivate->redirectDev)
+ return widgetPrivate->redirected(offset);
+ }
+
+ QMutexLocker locker(globalRedirectionsMutex());
+ QPaintDeviceRedirectionList *redirections = globalRedirections();
+ Q_ASSERT(redirections != 0);
+ for (int i = redirections->size()-1; i >= 0; --i)
+ if (redirections->at(i) == device) {
+ if (offset)
+ *offset = redirections->at(i).offset;
+ return redirections->at(i).replacement;
+ }
+ if (offset)
+ *offset = QPoint(0, 0);
+ return 0;
+}
+
+
+void qt_painter_removePaintDevice(QPaintDevice *dev)
+{
+ QMutexLocker locker(globalRedirectionsMutex());
+ if(QPaintDeviceRedirectionList *redirections = globalRedirections()) {
+ for (int i = 0; i < redirections->size(); ) {
+ if(redirections->at(i) == dev || redirections->at(i).replacement == dev)
+ redirections->removeAt(i);
+ else
+ ++i;
+ }
+ }
+}
+
+void qt_format_text(const QFont &fnt, const QRectF &_r,
+ int tf, const QString& str, QRectF *brect,
+ int tabstops, int *ta, int tabarraylen,
+ QPainter *painter)
+{
+ qt_format_text(fnt, _r,
+ tf, 0, str, brect,
+ tabstops, ta, tabarraylen,
+ painter);
+}
+void qt_format_text(const QFont &fnt, const QRectF &_r,
+ int tf, const QTextOption *option, const QString& str, QRectF *brect,
+ int tabstops, int *, int tabarraylen,
+ QPainter *painter)
+{
+
+ Q_ASSERT( !((tf & ~Qt::TextDontPrint)!=0 && option!=0) ); // we either have an option or flags
+
+ if (option) {
+ tf |= option->alignment();
+ if (option->wrapMode() != QTextOption::NoWrap)
+ tf |= Qt::TextWordWrap;
+
+ if (option->flags() & QTextOption::IncludeTrailingSpaces)
+ tf |= Qt::TextIncludeTrailingSpaces;
+
+ if (option->tabStop() >= 0 || !option->tabArray().isEmpty())
+ tf |= Qt::TextExpandTabs;
+ }
+
+ // we need to copy r here to protect against the case (&r == brect).
+ QRectF r(_r);
+
+ bool dontclip = (tf & Qt::TextDontClip);
+ bool wordwrap = (tf & Qt::TextWordWrap) || (tf & Qt::TextWrapAnywhere);
+ bool singleline = (tf & Qt::TextSingleLine);
+ bool showmnemonic = (tf & Qt::TextShowMnemonic);
+ bool hidemnmemonic = (tf & Qt::TextHideMnemonic);
+
+ Qt::LayoutDirection layout_direction;
+ if(option)
+ layout_direction = option->textDirection();
+ else if (painter)
+ layout_direction = painter->layoutDirection();
+ else
+ layout_direction = Qt::LeftToRight;
+
+ tf = QStyle::visualAlignment(layout_direction, QFlag(tf));
+
+ bool isRightToLeft = layout_direction == Qt::RightToLeft;
+ bool expandtabs = ((tf & Qt::TextExpandTabs) &&
+ (((tf & Qt::AlignLeft) && !isRightToLeft) ||
+ ((tf & Qt::AlignRight) && isRightToLeft)));
+
+ if (!painter)
+ tf |= Qt::TextDontPrint;
+
+ int maxUnderlines = 0;
+ int numUnderlines = 0;
+ int underlinePositionStack[32];
+ int *underlinePositions = underlinePositionStack;
+
+ QFontMetricsF fm(fnt);
+
+ QString text = str;
+ // compatible behaviour to the old implementation. Replace
+ // tabs by spaces
+ QChar *chr = text.data();
+ const QChar *end = chr + str.length();
+ bool has_tab = false;
+ while (chr != end) {
+ if (*chr == QLatin1Char('\r') || (singleline && *chr == QLatin1Char('\n'))) {
+ *chr = QLatin1Char(' ');
+ } else if (*chr == QLatin1Char('\n')) {
+ *chr = QChar::LineSeparator;
+ } else if (*chr == QLatin1Char('&')) {
+ ++maxUnderlines;
+ } else if (*chr == QLatin1Char('\t')) {
+ has_tab = true;
+ }
+ ++chr;
+ }
+ if (has_tab) {
+ if (!expandtabs) {
+ chr = text.data();
+ while (chr != end) {
+ if (*chr == QLatin1Char('\t'))
+ *chr = QLatin1Char(' ');
+ ++chr;
+ }
+ } else if (!tabarraylen && !tabstops) {
+ tabstops = qRound(fm.width(QLatin1Char('x'))*8);
+ }
+ }
+
+ if (hidemnmemonic || showmnemonic) {
+ if (maxUnderlines > 32)
+ underlinePositions = new int[maxUnderlines];
+ QChar *cout = text.data();
+ QChar *cin = cout;
+ int l = str.length();
+ while (l) {
+ if (*cin == QLatin1Char('&')) {
+ ++cin;
+ --l;
+ if (!l)
+ break;
+ if (*cin != QLatin1Char('&') && !hidemnmemonic)
+ underlinePositions[numUnderlines++] = cout - text.unicode();
+ }
+ *cout = *cin;
+ ++cout;
+ ++cin;
+ --l;
+ }
+ int newlen = cout - text.unicode();
+ if (newlen != text.length())
+ text.resize(newlen);
+ }
+
+ // no need to do extra work for underlines if we don't paint
+ if (tf & Qt::TextDontPrint)
+ numUnderlines = 0;
+
+ underlinePositions[numUnderlines] = -1;
+ qreal height = 0;
+ qreal width = 0;
+
+ QStackTextEngine engine(text, fnt);
+ if (option) {
+ engine.option = *option;
+ }
+
+
+
+ engine.option.setTextDirection(layout_direction);
+ if (tf & Qt::AlignJustify)
+ engine.option.setAlignment(Qt::AlignJustify);
+ else
+ engine.option.setAlignment(Qt::AlignLeft); // do not do alignment twice
+
+ if (!option && (tf & Qt::TextWrapAnywhere))
+ engine.option.setWrapMode(QTextOption::WrapAnywhere);
+
+ if (tf & Qt::TextJustificationForced)
+ engine.forceJustification = true;
+ QTextLayout textLayout(&engine);
+ textLayout.setCacheEnabled(true);
+ textLayout.engine()->underlinePositions = underlinePositions;
+
+ if (text.isEmpty()) {
+ height = fm.height();
+ width = 0;
+ tf |= Qt::TextDontPrint;
+ } else {
+ qreal lineWidth = 0x01000000;
+ if (wordwrap || (tf & Qt::TextJustificationForced))
+ lineWidth = qMax<qreal>(0, r.width());
+ if(!wordwrap)
+ tf |= Qt::TextIncludeTrailingSpaces;
+ textLayout.engine()->ignoreBidi = bool(tf & Qt::TextDontPrint);
+ textLayout.beginLayout();
+
+ qreal leading = fm.leading();
+ height = -leading;
+
+ while (1) {
+ QTextLine l = textLayout.createLine();
+ if (!l.isValid())
+ break;
+
+ l.setLineWidth(lineWidth);
+ height += leading;
+ l.setPosition(QPointF(0., height));
+ height += l.height();
+ width = qMax(width, l.naturalTextWidth());
+ if (!brect && height >= r.height())
+ break;
+ }
+ textLayout.endLayout();
+ }
+
+ qreal yoff = 0;
+ qreal xoff = 0;
+ if (tf & Qt::AlignBottom) {
+ yoff = r.height() - height;
+ } else if (tf & Qt::AlignVCenter) {
+ yoff = (r.height() - height)/2;
+ if (painter) {
+ QTransform::TransformationType type = painter->transform().type();
+ if (type <= QTransform::TxScale) {
+ // do the rounding manually to work around inconsistencies
+ // in the paint engines when drawing on floating point offsets
+ const qreal scale = painter->transform().m22();
+ if (scale != 0)
+ yoff = qRound(yoff * scale) / scale;
+ }
+ }
+ }
+ if (tf & Qt::AlignRight) {
+ xoff = r.width() - width;
+ } else if (tf & Qt::AlignHCenter) {
+ xoff = (r.width() - width)/2;
+ if (painter) {
+ QTransform::TransformationType type = painter->transform().type();
+ if (type <= QTransform::TxScale) {
+ // do the rounding manually to work around inconsistencies
+ // in the paint engines when drawing on floating point offsets
+ const qreal scale = painter->transform().m11();
+ if (scale != 0)
+ xoff = qRound(xoff * scale) / scale;
+ }
+ }
+ }
+ QRectF bounds = QRectF(r.x() + xoff, r.y() + yoff, width, height);
+ if (brect)
+ *brect = bounds;
+
+ if (!(tf & Qt::TextDontPrint)) {
+ bool restore = false;
+ if (!dontclip && !r.contains(bounds)) {
+ restore = true;
+ painter->save();
+ painter->setClipRect(r, Qt::IntersectClip);
+ }
+
+ for (int i = 0; i < textLayout.lineCount(); i++) {
+ QTextLine line = textLayout.lineAt(i);
+
+ if (tf & Qt::AlignRight)
+ xoff = r.width() - line.naturalTextWidth();
+ else if (tf & Qt::AlignHCenter)
+ xoff = (r.width() - line.naturalTextWidth())/2;
+
+ line.draw(painter, QPointF(r.x() + xoff + line.x(), r.y() + yoff));
+ }
+
+ if (restore) {
+ painter->restore();
+ }
+ }
+
+ if (underlinePositions != underlinePositionStack)
+ delete [] underlinePositions;
+}
+
+/*!
+ Sets the layout direction used by the painter when drawing text,
+ to the specified \a direction.
+
+ \sa layoutDirection(), drawText(), {QPainter#Settings}{Settings}
+*/
+void QPainter::setLayoutDirection(Qt::LayoutDirection direction)
+{
+ Q_D(QPainter);
+ if (d->state)
+ d->state->layoutDirection = direction;
+}
+
+/*!
+ Returns the layout direction used by the painter when drawing text.
+
+ \sa setLayoutDirection(), drawText(), {QPainter#Settings}{Settings}
+*/
+Qt::LayoutDirection QPainter::layoutDirection() const
+{
+ Q_D(const QPainter);
+ return d->state ? d->state->layoutDirection : Qt::LeftToRight;
+}
+
+QPainterState::QPainterState(const QPainterState *s)
+ : brushOrigin(s->brushOrigin), font(s->font), deviceFont(s->deviceFont),
+ pen(s->pen), brush(s->brush), bgBrush(s->bgBrush),
+ clipRegion(s->clipRegion), clipPath(s->clipPath),
+ clipOperation(s->clipOperation),
+ renderHints(s->renderHints), clipInfo(s->clipInfo),
+ worldMatrix(s->worldMatrix), matrix(s->matrix), redirection_offset(s->redirection_offset),
+ wx(s->wx), wy(s->wy), ww(s->ww), wh(s->wh),
+ vx(s->vx), vy(s->vy), vw(s->vw), vh(s->vh),
+ opacity(s->opacity), WxF(s->WxF), VxF(s->VxF),
+ clipEnabled(s->clipEnabled), bgMode(s->bgMode), painter(s->painter),
+ layoutDirection(s->layoutDirection),
+ composition_mode(s->composition_mode),
+ emulationSpecifier(s->emulationSpecifier), changeFlags(0)
+{
+ dirtyFlags = s->dirtyFlags;
+}
+
+QPainterState::QPainterState()
+ : brushOrigin(0, 0), bgBrush(Qt::white), clipOperation(Qt::NoClip),
+ renderHints(0),
+ wx(0), wy(0), ww(0), wh(0), vx(0), vy(0), vw(0), vh(0),
+ opacity(1), WxF(false), VxF(false), clipEnabled(true),
+ bgMode(Qt::TransparentMode), painter(0),
+ layoutDirection(QApplication::layoutDirection()),
+ composition_mode(QPainter::CompositionMode_SourceOver),
+ emulationSpecifier(0), changeFlags(0)
+{
+ dirtyFlags = 0;
+}
+
+QPainterState::~QPainterState()
+{
+}
+
+void QPainterState::init(QPainter *p) {
+ bgBrush = Qt::white;
+ bgMode = Qt::TransparentMode;
+ WxF = false;
+ VxF = false;
+ clipEnabled = true;
+ wx = wy = ww = wh = 0;
+ vx = vy = vw = vh = 0;
+ painter = p;
+ pen = QPen();
+ brushOrigin = QPointF(0, 0);
+ brush = QBrush();
+ font = deviceFont = QFont();
+ clipRegion = QRegion();
+ clipPath = QPainterPath();
+ clipOperation = Qt::NoClip;
+ clipInfo.clear();
+ worldMatrix.reset();
+ matrix.reset();
+ layoutDirection = QApplication::layoutDirection();
+ composition_mode = QPainter::CompositionMode_SourceOver;
+ emulationSpecifier = 0;
+ dirtyFlags = 0;
+ changeFlags = 0;
+ renderHints = 0;
+ opacity = 1;
+}
+
+#ifdef QT3_SUPPORT
+static void bitBlt_helper(QPaintDevice *dst, const QPoint &dp,
+ const QPaintDevice *src, const QRect &sr, bool)
+{
+ Q_ASSERT(dst);
+ Q_ASSERT(src);
+
+ if (src->devType() == QInternal::Pixmap) {
+ const QPixmap *pixmap = static_cast<const QPixmap *>(src);
+ QPainter pt(dst);
+ pt.drawPixmap(dp, *pixmap, sr);
+
+ } else {
+ qWarning("QPainter: bitBlt only works when source is of type pixmap");
+ }
+}
+
+void bitBlt(QPaintDevice *dst, int dx, int dy,
+ const QPaintDevice *src, int sx, int sy, int sw, int sh,
+ bool ignoreMask )
+{
+ bitBlt_helper(dst, QPoint(dx, dy), src, QRect(sx, sy, sw, sh), ignoreMask);
+}
+
+void bitBlt(QPaintDevice *dst, const QPoint &dp, const QPaintDevice *src, const QRect &sr, bool ignoreMask)
+{
+ bitBlt_helper(dst, dp, src, sr, ignoreMask);
+}
+
+void bitBlt(QPaintDevice *dst, int dx, int dy,
+ const QImage *src, int sx, int sy, int sw, int sh, int fl)
+{
+ Qt::ImageConversionFlags flags(fl);
+ QPixmap srcPixmap = QPixmap::fromImage(*src, flags);
+ bitBlt_helper(dst, QPoint(dx, dy), &srcPixmap, QRect(sx, sy, sw, sh), false);
+}
+
+#endif // QT3_SUPPORT
+
+/*!
+ \fn void QPainter::setBackgroundColor(const QColor &color)
+
+ Use setBackground() instead.
+*/
+
+/*!
+ \fn const QColor &QPainter::backgroundColor() const
+
+ Use background() and QBrush::color() instead.
+
+ \oldcode
+ QColor myColor = backgroundColor();
+ \newcode
+ QColor myColor = background().color();
+ \endcode
+
+ Note that the background can be a complex brush such as a texture
+ or a gradient.
+*/
+
+/*!
+ \fn void QPainter::drawText(int x, int y, const QString &text, int pos, int length)
+ \compat
+
+ Use drawText() combined with QString::mid() instead.
+
+ \oldcode
+ QPainter painter(this);
+ painter.drawText(x, y, text, pos, length);
+ \newcode
+ QPainter painter(this);
+ painter.drawText(x, y, text.mid(pos, length));
+ \endcode
+*/
+
+/*!
+ \fn void QPainter::drawText(const QPoint &point, const QString &text, int pos, int length)
+ \compat
+
+ Use drawText() combined with QString::mid() instead.
+
+ \oldcode
+ QPainter painter(this);
+ painter.drawText(point, text, pos, length);
+ \newcode
+ QPainter painter(this);
+ painter.drawText(point, text.mid(pos, length));
+ \endcode
+*/
+
+/*!
+ \fn void QPainter::drawText(int x, int y, const QString &text, int length)
+ \compat
+
+ Use drawText() combined with QString::left() instead.
+
+ \oldcode
+ QPainter painter(this);
+ painter.drawText(x, y, text, length);
+ \newcode
+ QPainter painter(this);
+ painter.drawText(x, y, text.left(length));
+ \endcode
+*/
+
+/*!
+ \fn void QPainter::drawText(const QPoint &point, const QString &text, int length)
+ \compat
+
+ Use drawText() combined with QString::left() instead.
+
+ \oldcode
+ QPainter painter(this);
+ painter.drawText(point, text, length);
+ \newcode
+ QPainter painter(this);
+ painter.drawText(point, text.left(length));
+ \endcode
+*/
+
+/*!
+ \fn bool QPainter::begin(QPaintDevice *device, const QWidget *init)
+ \compat
+
+ Use begin() instead.
+
+ If the paint \a device is a QWidget, QPainter is initialized after
+ the widget's settings automatically. Otherwise, you must call the
+ initFrom() function to initialize the painters pen, background and
+ font to the same as any given widget.
+
+ \oldcode
+ QPainter painter(this);
+ painter.begin(device, init);
+ \newcode
+ QPainter painter(this);
+ painter.begin(device);
+ painter.initFrom(init);
+ \endcode
+*/
+
+/*!
+ \fn void QPainter::drawImage(const QRectF &target, const QImage &image, const QRectF &source,
+ Qt::ImageConversionFlags flags)
+
+ Draws the rectangular portion \a source of the given \a image
+ into the \a target rectangle in the paint device.
+
+ \note The image is scaled to fit the rectangle, if both the image and rectangle size disagree.
+
+ If the image needs to be modified to fit in a lower-resolution
+ result (e.g. converting from 32-bit to 8-bit), use the \a flags to
+ specify how you would prefer this to happen.
+
+ \table 100%
+ \row
+ \o
+ \snippet doc/src/snippets/code/src_gui_painting_qpainter.cpp 20
+ \endtable
+
+ \sa drawPixmap()
+*/
+
+/*!
+ \fn void QPainter::drawImage(const QRect &target, const QImage &image, const QRect &source,
+ Qt::ImageConversionFlags flags)
+ \overload
+
+ Draws the rectangular portion \a source of the given \a image
+ into the \a target rectangle in the paint device.
+
+ \note The image is scaled to fit the rectangle, if both the image and rectangle size disagree.
+*/
+
+/*!
+ \fn void QPainter::drawImage(const QPointF &point, const QImage &image)
+
+ \overload
+
+ Draws the given \a image at the given \a point.
+*/
+
+/*!
+ \fn void QPainter::drawImage(const QPoint &point, const QImage &image)
+
+ \overload
+
+ Draws the given \a image at the given \a point.
+*/
+
+/*!
+ \fn void QPainter::drawImage(const QPointF &point, const QImage &image, const QRectF &source,
+ Qt::ImageConversionFlags flags = 0)
+
+ \overload
+
+ Draws the rectangular portion \a source of the given \a image with
+ its origin at the given \a point.
+*/
+
+/*!
+ \fn void QPainter::drawImage(const QPoint &point, const QImage &image, const QRect &source,
+ Qt::ImageConversionFlags flags = 0)
+ \overload
+
+ Draws the rectangular portion \a source of the given \a image with
+ its origin at the given \a point.
+*/
+
+/*!
+ \fn void QPainter::drawImage(const QRectF &rectangle, const QImage &image)
+
+ \overload
+
+ Draws the given \a image into the given \a rectangle.
+
+ \note The image is scaled to fit the rectangle, if both the image and rectangle size disagree.
+*/
+
+/*!
+ \fn void QPainter::drawImage(const QRect &rectangle, const QImage &image)
+
+ \overload
+
+ Draws the given \a image into the given \a rectangle.
+
+ \note The image is scaled to fit the rectangle, if both the image and rectangle size disagree.
+*/
+
+/*!
+ \fn void QPainter::drawImage(int x, int y, const QImage &image,
+ int sx, int sy, int sw, int sh,
+ Qt::ImageConversionFlags flags)
+ \overload
+
+ Draws an image at (\a{x}, \a{y}) by copying a part of \a image into
+ the paint device.
+
+ (\a{x}, \a{y}) specifies the top-left point in the paint device that is
+ to be drawn onto. (\a{sx}, \a{sy}) specifies the top-left point in \a
+ image that is to be drawn. The default is (0, 0).
+
+ (\a{sw}, \a{sh}) specifies the size of the image that is to be drawn.
+ The default, (0, 0) (and negative) means all the way to the
+ bottom-right of the image.
+*/
+
+/*!
+ \fn void QPainter::redirect(QPaintDevice *pdev, QPaintDevice *replacement)
+
+ Use setRedirected() instead.
+*/
+
+/*!
+ \fn QPaintDevice *QPainter::redirect(QPaintDevice *pdev)
+
+ Use redirected() instead.
+*/
+
+/*!
+ \fn QRect QPainter::boundingRect(const QRect &rectangle, int flags,
+ const QString &text, int length)
+ \compat
+
+ Returns the bounding rectangle for the given \a length of the \a
+ text constrained by the provided \a rectangle.
+
+ Use boundingRect() combined with QString::left() instead.
+
+ \oldcode
+ QRect rectangle = boundingRect(rect, flags, text, length);
+ \newcode
+ QRect rectangle = boundingRect(rect, flags, text.left(length));
+ \endcode
+*/
+
+/*!
+ \fn void QPainter::drawText(const QRect &rectangle, int flags, const QString &text,
+ int length, QRect *br)
+ \compat
+
+ Use drawText() combined with QString::left() instead.
+
+ \oldcode
+ QPainter painter(this);
+ painter.drawText(rectangle, flags, text, length, br );
+ \newcode
+ QPainter painter(this);
+ painter.drawText(rectangle, flags, text.left(length), br );
+ \endcode
+*/
+
+/*!
+ \fn QRect QPainter::boundingRect(int x, int y, int width, int height, int flags,
+ const QString &text, int length);
+
+ \compat
+
+ Returns the bounding rectangle for the given \a length of the \a
+ text constrained by the rectangle that begins at point (\a{x},
+ \a{y}) with the given \a width and \a height.
+
+ Use boundingRect() combined with QString::left() instead.
+
+ \oldcode
+ QRect rectangle = boundingRect(x, y, width, height, flags, text, length);
+ \newcode
+ QRect rectangle = boundingRect(x, y, width, height, flags, text.left(length));
+ \endcode
+*/
+
+/*!
+ \fn void QPainter::drawText(int x, int y, int width, int height, int flags,
+ const QString &text, int length, QRect *br)
+
+ \compat
+
+ Use drawText() combined with QString::left() instead.
+
+ \oldcode
+ QPainter painter(this);
+ painter.drawText(x, y, width, height, flags, text, length, br );
+ \newcode
+ QPainter painter(this);
+ painter.drawText(x, y, width, height, flags, text.left(length), br );
+ \endcode
+*/
+
+
+/*!
+ \class QPaintEngineState
+ \since 4.1
+
+ \brief The QPaintEngineState class provides information about the
+ active paint engine's current state.
+ \reentrant
+
+ QPaintEngineState records which properties that have changed since
+ the last time the paint engine was updated, as well as their
+ current value.
+
+ Which properties that have changed can at any time be retrieved
+ using the state() function. This function returns an instance of
+ the QPaintEngine::DirtyFlags type which stores an OR combination
+ of QPaintEngine::DirtyFlag values. The QPaintEngine::DirtyFlag
+ enum defines whether a property has changed since the last update
+ or not.
+
+ If a property is marked with a dirty flag, its current value can
+ be retrieved using the corresponding get function:
+
+ \target GetFunction
+
+ \table
+ \header \o Property Flag \o Current Property Value
+ \row \o QPaintEngine::DirtyBackground \o backgroundBrush()
+ \row \o QPaintEngine::DirtyBackgroundMode \o backgroundMode()
+ \row \o QPaintEngine::DirtyBrush \o brush()
+ \row \o QPaintEngine::DirtyBrushOrigin \o brushOrigin()
+ \row \o QPaintEngine::DirtyClipRegion \e or QPaintEngine::DirtyClipPath
+ \o clipOperation()
+ \row \o QPaintEngine::DirtyClipPath \o clipPath()
+ \row \o QPaintEngine::DirtyClipRegion \o clipRegion()
+ \row \o QPaintEngine::DirtyCompositionMode \o compositionMode()
+ \row \o QPaintEngine::DirtyFont \o font()
+ \row \o QPaintEngine::DirtyTransform \o matrix()
+ \row \o QPaintEngine::DirtyClipEnabled \o isClipEnabled()
+ \row \o QPaintEngine::DirtyPen \o pen()
+ \row \o QPaintEngine::DirtyHints \o renderHints()
+ \endtable
+
+ The QPaintEngineState class also provide the painter() function
+ which returns a pointer to the painter that is currently updating
+ the paint engine.
+
+ An instance of this class, representing the current state of the
+ active paint engine, is passed as argument to the
+ QPaintEngine::updateState() function. The only situation in which
+ you will have to use this class directly is when implementing your
+ own paint engine.
+
+ \sa QPaintEngine
+*/
+
+
+/*!
+ \fn QPaintEngine::DirtyFlags QPaintEngineState::state() const
+
+ Returns a combination of flags identifying the set of properties
+ that need to be updated when updating the paint engine's state
+ (i.e. during a call to the QPaintEngine::updateState() function).
+
+ \sa QPaintEngine::updateState()
+*/
+
+
+/*!
+ Returns the pen in the current paint engine state.
+
+ This variable should only be used when the state() returns a
+ combination which includes the QPaintEngine::DirtyPen flag.
+
+ \sa state(), QPaintEngine::updateState()
+*/
+
+QPen QPaintEngineState::pen() const
+{
+ return static_cast<const QPainterState *>(this)->pen;
+}
+
+/*!
+ Returns the brush in the current paint engine state.
+
+ This variable should only be used when the state() returns a
+ combination which includes the QPaintEngine::DirtyBrush flag.
+
+ \sa state(), QPaintEngine::updateState()
+*/
+
+QBrush QPaintEngineState::brush() const
+{
+ return static_cast<const QPainterState *>(this)->brush;
+}
+
+/*!
+ Returns the brush origin in the current paint engine state.
+
+ This variable should only be used when the state() returns a
+ combination which includes the QPaintEngine::DirtyBrushOrigin flag.
+
+ \sa state(), QPaintEngine::updateState()
+*/
+
+QPointF QPaintEngineState::brushOrigin() const
+{
+ return static_cast<const QPainterState *>(this)->brushOrigin;
+}
+
+/*!
+ Returns the background brush in the current paint engine state.
+
+ This variable should only be used when the state() returns a
+ combination which includes the QPaintEngine::DirtyBackground flag.
+
+ \sa state(), QPaintEngine::updateState()
+*/
+
+QBrush QPaintEngineState::backgroundBrush() const
+{
+ return static_cast<const QPainterState *>(this)->bgBrush;
+}
+
+/*!
+ Returns the background mode in the current paint engine
+ state.
+
+ This variable should only be used when the state() returns a
+ combination which includes the QPaintEngine::DirtyBackgroundMode flag.
+
+ \sa state(), QPaintEngine::updateState()
+*/
+
+Qt::BGMode QPaintEngineState::backgroundMode() const
+{
+ return static_cast<const QPainterState *>(this)->bgMode;
+}
+
+/*!
+ Returns the font in the current paint engine
+ state.
+
+ This variable should only be used when the state() returns a
+ combination which includes the QPaintEngine::DirtyFont flag.
+
+ \sa state(), QPaintEngine::updateState()
+*/
+
+QFont QPaintEngineState::font() const
+{
+ return static_cast<const QPainterState *>(this)->font;
+}
+
+/*!
+ \since 4.2
+
+ Returns the matrix in the current paint engine
+ state.
+
+ This variable should only be used when the state() returns a
+ combination which includes the QPaintEngine::DirtyTransform flag.
+
+ \sa state(), QPaintEngine::updateState()
+*/
+
+QMatrix QPaintEngineState::matrix() const
+{
+ const QPainterState *st = static_cast<const QPainterState *>(this);
+
+ return st->matrix.toAffine();
+}
+
+/*!
+ \since 4.3
+
+ Returns the matrix in the current paint engine state.
+
+ This variable should only be used when the state() returns a
+ combination which includes the QPaintEngine::DirtyTransform flag.
+
+ \sa state(), QPaintEngine::updateState()
+*/
+
+
+QTransform QPaintEngineState::transform() const
+{
+ const QPainterState *st = static_cast<const QPainterState *>(this);
+
+ return st->matrix;
+}
+
+
+/*!
+ Returns the clip operation in the current paint engine
+ state.
+
+ This variable should only be used when the state() returns a
+ combination which includes either the QPaintEngine::DirtyClipPath
+ or the QPaintEngine::DirtyClipRegion flag.
+
+ \sa state(), QPaintEngine::updateState()
+*/
+
+Qt::ClipOperation QPaintEngineState::clipOperation() const
+{
+ return static_cast<const QPainterState *>(this)->clipOperation;
+}
+
+/*!
+ \since 4.3
+
+ Returns whether the coordinate of the fill have been specified
+ as bounded by the current rendering operation and have to be
+ resolved (about the currently rendered primitive).
+*/
+bool QPaintEngineState::brushNeedsResolving() const
+{
+ const QBrush &brush = static_cast<const QPainterState *>(this)->brush;
+ return needsResolving(brush);
+}
+
+
+/*!
+ \since 4.3
+
+ Returns whether the coordinate of the stroke have been specified
+ as bounded by the current rendering operation and have to be
+ resolved (about the currently rendered primitive).
+*/
+bool QPaintEngineState::penNeedsResolving() const
+{
+ const QPen &pen = static_cast<const QPainterState *>(this)->pen;
+ return needsResolving(pen.brush());
+}
+
+/*!
+ Returns the clip region in the current paint engine state.
+
+ This variable should only be used when the state() returns a
+ combination which includes the QPaintEngine::DirtyClipRegion flag.
+
+ \sa state(), QPaintEngine::updateState()
+*/
+
+QRegion QPaintEngineState::clipRegion() const
+{
+ return static_cast<const QPainterState *>(this)->clipRegion;
+}
+
+/*!
+ Returns the clip path in the current paint engine state.
+
+ This variable should only be used when the state() returns a
+ combination which includes the QPaintEngine::DirtyClipPath flag.
+
+ \sa state(), QPaintEngine::updateState()
+*/
+
+QPainterPath QPaintEngineState::clipPath() const
+{
+ return static_cast<const QPainterState *>(this)->clipPath;
+}
+
+/*!
+ Returns wether clipping is enabled or not in the current paint
+ engine state.
+
+ This variable should only be used when the state() returns a
+ combination which includes the QPaintEngine::DirtyClipEnabled
+ flag.
+
+ \sa state(), QPaintEngine::updateState()
+*/
+
+bool QPaintEngineState::isClipEnabled() const
+{
+ return static_cast<const QPainterState *>(this)->clipEnabled;
+}
+
+/*!
+ Returns the render hints in the current paint engine state.
+
+ This variable should only be used when the state() returns a
+ combination which includes the QPaintEngine::DirtyHints
+ flag.
+
+ \sa state(), QPaintEngine::updateState()
+*/
+
+QPainter::RenderHints QPaintEngineState::renderHints() const
+{
+ return static_cast<const QPainterState *>(this)->renderHints;
+}
+
+/*!
+ Returns the composition mode in the current paint engine state.
+
+ This variable should only be used when the state() returns a
+ combination which includes the QPaintEngine::DirtyCompositionMode
+ flag.
+
+ \sa state(), QPaintEngine::updateState()
+*/
+
+QPainter::CompositionMode QPaintEngineState::compositionMode() const
+{
+ return static_cast<const QPainterState *>(this)->composition_mode;
+}
+
+
+/*!
+ Returns a pointer to the painter currently updating the paint
+ engine.
+*/
+
+QPainter *QPaintEngineState::painter() const
+{
+ return static_cast<const QPainterState *>(this)->painter;
+}
+
+
+/*!
+ \since 4.2
+
+ Returns the opacity in the current paint engine state.
+*/
+
+qreal QPaintEngineState::opacity() const
+{
+ return static_cast<const QPainterState *>(this)->opacity;
+}
+
+/*!
+ \since 4.3
+
+ Sets the world transformation matrix.
+ If \a combine is true, the specified \a transform is combined with
+ the current matrix; otherwise it replaces the current matrix.
+
+ This function has been added for compatibility with setMatrix(),
+ but as with setMatrix() the preferred method of setting a
+ transformation on the painter is through setWorldTransform().
+
+ \sa transform()
+*/
+
+void QPainter::setTransform(const QTransform &transform, bool combine )
+{
+ setWorldTransform(transform, combine);
+}
+
+/*!
+ Returns the world transformation matrix.
+*/
+
+const QTransform & QPainter::transform() const
+{
+ return worldTransform();
+}
+
+
+/*!
+ Returns the matrix that transforms from logical coordinates to
+ device coordinates of the platform dependent paint device.
+
+ This function is \e only needed when using platform painting
+ commands on the platform dependent handle (Qt::HANDLE), and the
+ platform does not do transformations nativly.
+
+ The QPaintEngine::PaintEngineFeature enum can be queried to
+ determine whether the platform performs the transformations or
+ not.
+
+ \sa worldTransform(), QPaintEngine::hasFeature(),
+*/
+
+const QTransform & QPainter::deviceTransform() const
+{
+ Q_D(const QPainter);
+ if (!d->engine) {
+ qWarning("QPainter::deviceTransform: Painter not active");
+ return d->fakeState()->transform;
+ }
+ return d->state->matrix;
+}
+
+
+/*!
+ Resets any transformations that were made using translate(),
+ scale(), shear(), rotate(), setWorldTransform(), setViewport()
+ and setWindow().
+
+ \sa {Coordinate Transformations}
+*/
+
+void QPainter::resetTransform()
+{
+ Q_D(QPainter);
+#ifdef QT_DEBUG_DRAW
+ if (qt_show_painter_debug_output)
+ printf("QPainter::resetMatrix()\n");
+#endif
+ if (!d->engine) {
+ qWarning("QPainter::resetMatrix: Painter not active");
+ return;
+ }
+
+ d->state->wx = d->state->wy = d->state->vx = d->state->vy = 0; // default view origins
+ d->state->ww = d->state->vw = d->device->metric(QPaintDevice::PdmWidth);
+ d->state->wh = d->state->vh = d->device->metric(QPaintDevice::PdmHeight);
+ d->state->worldMatrix = QTransform();
+ setMatrixEnabled(false);
+ setViewTransformEnabled(false);
+ if (d->extended)
+ d->extended->transformChanged();
+ else
+ d->state->dirtyFlags |= QPaintEngine::DirtyTransform;
+}
+
+/*!
+ Sets the world transformation matrix.
+ If \a combine is true, the specified \a matrix is combined with the current matrix;
+ otherwise it replaces the current matrix.
+
+ \sa transform(), setTransform()
+*/
+
+void QPainter::setWorldTransform(const QTransform &matrix, bool combine )
+{
+ Q_D(QPainter);
+
+ if (!d->engine) {
+ qWarning("QPainter::setWorldTransform: Painter not active");
+ return;
+ }
+
+ if (combine)
+ d->state->worldMatrix = matrix * d->state->worldMatrix; // combines
+ else
+ d->state->worldMatrix = matrix; // set new matrix
+
+ d->state->WxF = true;
+ d->updateMatrix();
+}
+
+/*!
+ Returns the world transformation matrix.
+*/
+
+const QTransform & QPainter::worldTransform() const
+{
+ Q_D(const QPainter);
+ if (!d->engine) {
+ qWarning("QPainter::worldTransform: Painter not active");
+ return d->fakeState()->transform;
+ }
+ return d->state->worldMatrix;
+}
+
+/*!
+ Returns the transformation matrix combining the current
+ window/viewport and world transformation.
+
+ \sa setWorldMatrix(), setWindow(), setViewport()
+*/
+
+QTransform QPainter::combinedTransform() const
+{
+ Q_D(const QPainter);
+ if (!d->engine) {
+ qWarning("QPainter::combinedTransform: Painter not active");
+ return QTransform();
+ }
+ return d->state->worldMatrix * d->viewTransform();
+}
+
+void qt_draw_helper(QPainterPrivate *p, const QPainterPath &path, QPainterPrivate::DrawOperation operation)
+{
+ p->draw_helper(path, operation);
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/painting/qpainter.h b/src/gui/painting/qpainter.h
new file mode 100644
index 0000000000..f3df7a31bc
--- /dev/null
+++ b/src/gui/painting/qpainter.h
@@ -0,0 +1,953 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QPAINTER_H
+#define QPAINTER_H
+
+#include <QtCore/qnamespace.h>
+#include <QtCore/qrect.h>
+#include <QtCore/qpoint.h>
+#include <QtGui/qpixmap.h>
+#include <QtGui/qimage.h>
+#include <QtGui/qtextoption.h>
+#include <QtGui/qdrawutil.h>
+
+#ifndef QT_INCLUDE_COMPAT
+#include <QtGui/qpolygon.h>
+#include <QtGui/qpen.h>
+#include <QtGui/qbrush.h>
+#include <QtGui/qmatrix.h>
+#include <QtGui/qtransform.h>
+#include <QtGui/qfontinfo.h>
+#include <QtGui/qfontmetrics.h>
+#endif
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+class QBrush;
+class QFontInfo;
+class QFontMetrics;
+class QPaintDevice;
+class QPainterPath;
+class QPainterPrivate;
+class QPen;
+class QPolygon;
+class QTextItem;
+class QMatrix;
+class QTransform;
+
+class Q_GUI_EXPORT QPainter
+{
+ Q_DECLARE_PRIVATE(QPainter)
+ Q_GADGET
+ Q_FLAGS(RenderHint RenderHints)
+
+public:
+ enum RenderHint {
+ Antialiasing = 0x01,
+ TextAntialiasing = 0x02,
+ SmoothPixmapTransform = 0x04,
+ HighQualityAntialiasing = 0x08,
+ NonCosmeticDefaultPen = 0x10
+ };
+
+ Q_DECLARE_FLAGS(RenderHints, RenderHint)
+
+ QPainter();
+ explicit QPainter(QPaintDevice *);
+ ~QPainter();
+
+ QPaintDevice *device() const;
+
+ bool begin(QPaintDevice *);
+ bool end();
+ bool isActive() const;
+
+ void initFrom(const QWidget *widget);
+
+ enum CompositionMode {
+ CompositionMode_SourceOver,
+ CompositionMode_DestinationOver,
+ CompositionMode_Clear,
+ CompositionMode_Source,
+ CompositionMode_Destination,
+ CompositionMode_SourceIn,
+ CompositionMode_DestinationIn,
+ CompositionMode_SourceOut,
+ CompositionMode_DestinationOut,
+ CompositionMode_SourceAtop,
+ CompositionMode_DestinationAtop,
+ CompositionMode_Xor,
+
+ //svg 1.2 blend modes
+ CompositionMode_Plus,
+ CompositionMode_Multiply,
+ CompositionMode_Screen,
+ CompositionMode_Overlay,
+ CompositionMode_Darken,
+ CompositionMode_Lighten,
+ CompositionMode_ColorDodge,
+ CompositionMode_ColorBurn,
+ CompositionMode_HardLight,
+ CompositionMode_SoftLight,
+ CompositionMode_Difference,
+ CompositionMode_Exclusion,
+
+ // ROPs
+ RasterOp_SourceOrDestination,
+ RasterOp_SourceAndDestination,
+ RasterOp_SourceXorDestination,
+ RasterOp_NotSourceAndNotDestination,
+ RasterOp_NotSourceOrNotDestination,
+ RasterOp_NotSourceXorDestination,
+ RasterOp_NotSource,
+ RasterOp_NotSourceAndDestination,
+ RasterOp_SourceAndNotDestination
+ };
+ void setCompositionMode(CompositionMode mode);
+ CompositionMode compositionMode() const;
+
+ const QFont &font() const;
+ void setFont(const QFont &f);
+
+ QFontMetrics fontMetrics() const;
+ QFontInfo fontInfo() const;
+
+ void setPen(const QColor &color);
+ void setPen(const QPen &pen);
+ void setPen(Qt::PenStyle style);
+ const QPen &pen() const;
+
+ void setBrush(const QBrush &brush);
+ void setBrush(Qt::BrushStyle style);
+ const QBrush &brush() const;
+
+ // attributes/modes
+ void setBackgroundMode(Qt::BGMode mode);
+ Qt::BGMode backgroundMode() const;
+
+ QPoint brushOrigin() const;
+ inline void setBrushOrigin(int x, int y);
+ inline void setBrushOrigin(const QPoint &);
+ void setBrushOrigin(const QPointF &);
+
+ void setBackground(const QBrush &bg);
+ const QBrush &background() const;
+
+ qreal opacity() const;
+ void setOpacity(qreal opacity);
+
+ // Clip functions
+ QRegion clipRegion() const;
+ QPainterPath clipPath() const;
+
+ void setClipRect(const QRectF &, Qt::ClipOperation op = Qt::ReplaceClip);
+ void setClipRect(const QRect &, Qt::ClipOperation op = Qt::ReplaceClip);
+ inline void setClipRect(int x, int y, int w, int h, Qt::ClipOperation op = Qt::ReplaceClip);
+
+ void setClipRegion(const QRegion &, Qt::ClipOperation op = Qt::ReplaceClip);
+
+ void setClipPath(const QPainterPath &path, Qt::ClipOperation op = Qt::ReplaceClip);
+
+ void setClipping(bool enable);
+ bool hasClipping() const;
+
+ void save();
+ void restore();
+
+ // XForm functions
+ void setMatrix(const QMatrix &matrix, bool combine = false);
+ const QMatrix &matrix() const;
+ const QMatrix &deviceMatrix() const;
+ void resetMatrix();
+
+ void setTransform(const QTransform &transform, bool combine = false);
+ const QTransform &transform() const;
+ const QTransform &deviceTransform() const;
+ void resetTransform();
+
+ void setWorldMatrix(const QMatrix &matrix, bool combine = false);
+ const QMatrix &worldMatrix() const;
+
+ void setWorldTransform(const QTransform &matrix, bool combine = false);
+ const QTransform &worldTransform() const;
+
+ QMatrix combinedMatrix() const;
+ QTransform combinedTransform() const;
+
+ void setMatrixEnabled(bool enabled);
+ bool matrixEnabled() const;
+
+ void setWorldMatrixEnabled(bool enabled);
+ bool worldMatrixEnabled() const;
+
+ void scale(qreal sx, qreal sy);
+ void shear(qreal sh, qreal sv);
+ void rotate(qreal a);
+
+ void translate(const QPointF &offset);
+ inline void translate(const QPoint &offset);
+ inline void translate(qreal dx, qreal dy);
+
+ QRect window() const;
+ void setWindow(const QRect &window);
+ inline void setWindow(int x, int y, int w, int h);
+
+ QRect viewport() const;
+ void setViewport(const QRect &viewport);
+ inline void setViewport(int x, int y, int w, int h);
+
+ void setViewTransformEnabled(bool enable);
+ bool viewTransformEnabled() const;
+
+ // drawing functions
+ void strokePath(const QPainterPath &path, const QPen &pen);
+ void fillPath(const QPainterPath &path, const QBrush &brush);
+ void drawPath(const QPainterPath &path);
+
+ inline void drawPoint(const QPointF &pt);
+ inline void drawPoint(const QPoint &p);
+ inline void drawPoint(int x, int y);
+
+ void drawPoints(const QPointF *points, int pointCount);
+ inline void drawPoints(const QPolygonF &points);
+ void drawPoints(const QPoint *points, int pointCount);
+ inline void drawPoints(const QPolygon &points);
+
+ inline void drawLine(const QLineF &line);
+ inline void drawLine(const QLine &line);
+ inline void drawLine(int x1, int y1, int x2, int y2);
+ inline void drawLine(const QPoint &p1, const QPoint &p2);
+ inline void drawLine(const QPointF &p1, const QPointF &p2);
+
+ void drawLines(const QLineF *lines, int lineCount);
+ inline void drawLines(const QVector<QLineF> &lines);
+ void drawLines(const QPointF *pointPairs, int lineCount);
+ inline void drawLines(const QVector<QPointF> &pointPairs);
+ void drawLines(const QLine *lines, int lineCount);
+ inline void drawLines(const QVector<QLine> &lines);
+ void drawLines(const QPoint *pointPairs, int lineCount);
+ inline void drawLines(const QVector<QPoint> &pointPairs);
+
+ inline void drawRect(const QRectF &rect);
+ inline void drawRect(int x1, int y1, int w, int h);
+ inline void drawRect(const QRect &rect);
+
+ void drawRects(const QRectF *rects, int rectCount);
+ inline void drawRects(const QVector<QRectF> &rectangles);
+ void drawRects(const QRect *rects, int rectCount);
+ inline void drawRects(const QVector<QRect> &rectangles);
+
+ void drawEllipse(const QRectF &r);
+ void drawEllipse(const QRect &r);
+ inline void drawEllipse(int x, int y, int w, int h);
+
+ inline void drawEllipse(const QPointF &center, qreal rx, qreal ry);
+ inline void drawEllipse(const QPoint &center, int rx, int ry);
+
+ void drawPolyline(const QPointF *points, int pointCount);
+ inline void drawPolyline(const QPolygonF &polyline);
+ void drawPolyline(const QPoint *points, int pointCount);
+ inline void drawPolyline(const QPolygon &polygon);
+
+ void drawPolygon(const QPointF *points, int pointCount, Qt::FillRule fillRule = Qt::OddEvenFill);
+ inline void drawPolygon(const QPolygonF &polygon, Qt::FillRule fillRule = Qt::OddEvenFill);
+ void drawPolygon(const QPoint *points, int pointCount, Qt::FillRule fillRule = Qt::OddEvenFill);
+ inline void drawPolygon(const QPolygon &polygon, Qt::FillRule fillRule = Qt::OddEvenFill);
+
+ void drawConvexPolygon(const QPointF *points, int pointCount);
+ inline void drawConvexPolygon(const QPolygonF &polygon);
+ void drawConvexPolygon(const QPoint *points, int pointCount);
+ inline void drawConvexPolygon(const QPolygon &polygon);
+
+ void drawArc(const QRectF &rect, int a, int alen);
+ inline void drawArc(const QRect &, int a, int alen);
+ inline void drawArc(int x, int y, int w, int h, int a, int alen);
+
+ void drawPie(const QRectF &rect, int a, int alen);
+ inline void drawPie(int x, int y, int w, int h, int a, int alen);
+ inline void drawPie(const QRect &, int a, int alen);
+
+ void drawChord(const QRectF &rect, int a, int alen);
+ inline void drawChord(int x, int y, int w, int h, int a, int alen);
+ inline void drawChord(const QRect &, int a, int alen);
+
+ void drawRoundedRect(const QRectF &rect, qreal xRadius, qreal yRadius,
+ Qt::SizeMode mode = Qt::AbsoluteSize);
+ inline void drawRoundedRect(int x, int y, int w, int h, qreal xRadius, qreal yRadius,
+ Qt::SizeMode mode = Qt::AbsoluteSize);
+ inline void drawRoundedRect(const QRect &rect, qreal xRadius, qreal yRadius,
+ Qt::SizeMode mode = Qt::AbsoluteSize);
+
+ void drawRoundRect(const QRectF &r, int xround = 25, int yround = 25);
+ inline void drawRoundRect(int x, int y, int w, int h, int = 25, int = 25);
+ inline void drawRoundRect(const QRect &r, int xround = 25, int yround = 25);
+
+ void drawTiledPixmap(const QRectF &rect, const QPixmap &pm, const QPointF &offset = QPointF());
+ inline void drawTiledPixmap(int x, int y, int w, int h, const QPixmap &, int sx=0, int sy=0);
+ inline void drawTiledPixmap(const QRect &, const QPixmap &, const QPoint & = QPoint());
+#ifndef QT_NO_PICTURE
+ void drawPicture(const QPointF &p, const QPicture &picture);
+ inline void drawPicture(int x, int y, const QPicture &picture);
+ inline void drawPicture(const QPoint &p, const QPicture &picture);
+#endif
+
+ void drawPixmap(const QRectF &targetRect, const QPixmap &pixmap, const QRectF &sourceRect);
+ inline void drawPixmap(const QRect &targetRect, const QPixmap &pixmap, const QRect &sourceRect);
+ inline void drawPixmap(int x, int y, int w, int h, const QPixmap &pm,
+ int sx, int sy, int sw, int sh);
+ inline void drawPixmap(int x, int y, const QPixmap &pm,
+ int sx, int sy, int sw, int sh);
+ inline void drawPixmap(const QPointF &p, const QPixmap &pm, const QRectF &sr);
+ inline void drawPixmap(const QPoint &p, const QPixmap &pm, const QRect &sr);
+ void drawPixmap(const QPointF &p, const QPixmap &pm);
+ inline void drawPixmap(const QPoint &p, const QPixmap &pm);
+ inline void drawPixmap(int x, int y, const QPixmap &pm);
+ inline void drawPixmap(const QRect &r, const QPixmap &pm);
+ inline void drawPixmap(int x, int y, int w, int h, const QPixmap &pm);
+
+ void drawImage(const QRectF &targetRect, const QImage &image, const QRectF &sourceRect,
+ Qt::ImageConversionFlags flags = Qt::AutoColor);
+ inline void drawImage(const QRect &targetRect, const QImage &image, const QRect &sourceRect,
+ Qt::ImageConversionFlags flags = Qt::AutoColor);
+ inline void drawImage(const QPointF &p, const QImage &image, const QRectF &sr,
+ Qt::ImageConversionFlags flags = Qt::AutoColor);
+ inline void drawImage(const QPoint &p, const QImage &image, const QRect &sr,
+ Qt::ImageConversionFlags flags = Qt::AutoColor);
+ inline void drawImage(const QRectF &r, const QImage &image);
+ inline void drawImage(const QRect &r, const QImage &image);
+ void drawImage(const QPointF &p, const QImage &image);
+ inline void drawImage(const QPoint &p, const QImage &image);
+ inline void drawImage(int x, int y, const QImage &image, int sx = 0, int sy = 0,
+ int sw = -1, int sh = -1, Qt::ImageConversionFlags flags = Qt::AutoColor);
+
+ void setLayoutDirection(Qt::LayoutDirection direction);
+ Qt::LayoutDirection layoutDirection() const;
+
+ void drawText(const QPointF &p, const QString &s);
+ inline void drawText(const QPoint &p, const QString &s);
+ inline void drawText(int x, int y, const QString &s);
+
+ void drawText(const QPointF &p, const QString &str, int tf, int justificationPadding);
+
+ void drawText(const QRectF &r, int flags, const QString &text, QRectF *br=0);
+ void drawText(const QRect &r, int flags, const QString &text, QRect *br=0);
+ inline void drawText(int x, int y, int w, int h, int flags, const QString &text, QRect *br=0);
+
+ void drawText(const QRectF &r, const QString &text, const QTextOption &o = QTextOption());
+
+ QRectF boundingRect(const QRectF &rect, int flags, const QString &text);
+ QRect boundingRect(const QRect &rect, int flags, const QString &text);
+ inline QRect boundingRect(int x, int y, int w, int h, int flags, const QString &text);
+
+ QRectF boundingRect(const QRectF &rect, const QString &text, const QTextOption &o = QTextOption());
+
+ void drawTextItem(const QPointF &p, const QTextItem &ti);
+ inline void drawTextItem(int x, int y, const QTextItem &ti);
+ inline void drawTextItem(const QPoint &p, const QTextItem &ti);
+
+ void fillRect(const QRectF &, const QBrush &);
+ inline void fillRect(int x, int y, int w, int h, const QBrush &);
+ void fillRect(const QRect &, const QBrush &);
+
+ void fillRect(const QRectF &, const QColor &color);
+ inline void fillRect(int x, int y, int w, int h, const QColor &color);
+ void fillRect(const QRect &, const QColor &color);
+
+ inline void fillRect(int x, int y, int w, int h, Qt::GlobalColor c);
+ inline void fillRect(const QRect &r, Qt::GlobalColor c);
+ inline void fillRect(const QRectF &r, Qt::GlobalColor c);
+
+ inline void fillRect(int x, int y, int w, int h, Qt::BrushStyle style);
+ inline void fillRect(const QRect &r, Qt::BrushStyle style);
+ inline void fillRect(const QRectF &r, Qt::BrushStyle style);
+
+ void eraseRect(const QRectF &);
+ inline void eraseRect(int x, int y, int w, int h);
+ inline void eraseRect(const QRect &);
+
+ void setRenderHint(RenderHint hint, bool on = true);
+ void setRenderHints(RenderHints hints, bool on = true);
+ RenderHints renderHints() const;
+ inline bool testRenderHint(RenderHint hint) const { return renderHints() & hint; }
+
+ QPaintEngine *paintEngine() const;
+
+ static void setRedirected(const QPaintDevice *device, QPaintDevice *replacement,
+ const QPoint& offset = QPoint());
+ static QPaintDevice *redirected(const QPaintDevice *device, QPoint *offset = 0);
+ static void restoreRedirected(const QPaintDevice *device);
+
+#ifdef QT3_SUPPORT
+
+ inline QT3_SUPPORT void setBackgroundColor(const QColor &color) { setBackground(color); }
+ inline QT3_SUPPORT const QColor &backgroundColor() const { return background().color(); }
+
+ inline QT3_SUPPORT void drawText(int x, int y, const QString &s, int pos, int len)
+ { drawText(x, y, s.mid(pos, len)); }
+ inline QT3_SUPPORT void drawText(const QPoint &p, const QString &s, int pos, int len)
+ { drawText(p, s.mid(pos, len)); }
+ inline QT3_SUPPORT void drawText(int x, int y, const QString &s, int len)
+ { drawText(x, y, s.left(len)); }
+ inline QT3_SUPPORT void drawText(const QPoint &p, const QString &s, int len)
+ { drawText(p, s.left(len)); }
+ inline QT3_SUPPORT void drawText(const QRect &r, int flags, const QString &str, int len, QRect *br=0)
+ { drawText(r, flags, str.left(len), br); }
+ inline QT3_SUPPORT void drawText(int x, int y, int w, int h, int flags, const QString &text, int len, QRect *br=0)
+ { drawText(QRect(x, y, w, h), flags, text.left(len), br); }
+ inline QT3_SUPPORT QRect boundingRect(const QRect &rect, int flags, const QString &text, int len)
+ { return boundingRect(rect, flags, text.left(len)); }
+ inline QT3_SUPPORT QRect boundingRect(int x, int y, int w, int h, int flags, const QString &text, int len)
+ { return boundingRect(QRect(x, y, w, h), flags, text.left(len)); }
+
+ inline QT3_SUPPORT bool begin(QPaintDevice *pdev, const QWidget *init)
+ { bool ret = begin(pdev); initFrom(init); return ret; }
+ QT3_SUPPORT void drawPoints(const QPolygon &pa, int index, int npoints = -1)
+ { drawPoints(pa.constData() + index, npoints == -1 ? pa.size() - index : npoints); }
+
+ QT3_SUPPORT void drawCubicBezier(const QPolygon &pa, int index = 0);
+
+ QT3_SUPPORT void drawLineSegments(const QPolygon &points, int index = 0, int nlines = -1);
+
+ inline QT3_SUPPORT void drawPolyline(const QPolygon &pa, int index, int npoints = -1)
+ { drawPolyline(pa.constData() + index, npoints == -1 ? pa.size() - index : npoints); }
+
+ inline QT3_SUPPORT void drawPolygon(const QPolygon &pa, bool winding, int index = 0, int npoints = -1)
+ { drawPolygon(pa.constData() + index, npoints == -1 ? pa.size() - index : npoints,
+ winding ? Qt::WindingFill : Qt::OddEvenFill); }
+
+ inline QT3_SUPPORT void drawPolygon(const QPolygonF &polygon, bool winding, int index = 0,
+ int npoints = -1)
+ { drawPolygon(polygon.constData() + index, npoints == -1 ? polygon.size() - index : npoints,
+ winding ? Qt::WindingFill : Qt::OddEvenFill); }
+
+ inline QT3_SUPPORT void drawConvexPolygon(const QPolygonF &polygon, int index, int npoints = -1)
+ { drawConvexPolygon(polygon.constData() + index, npoints == -1 ? polygon.size() - index : npoints); }
+ inline QT3_SUPPORT void drawConvexPolygon(const QPolygon &pa, int index, int npoints = -1)
+ { drawConvexPolygon(pa.constData() + index, npoints == -1 ? pa.size() - index : npoints); }
+
+ static inline QT3_SUPPORT void redirect(QPaintDevice *pdev, QPaintDevice *replacement)
+ { setRedirected(pdev, replacement); }
+ static inline QT3_SUPPORT QPaintDevice *redirect(QPaintDevice *pdev)
+ { return const_cast<QPaintDevice*>(redirected(pdev)); }
+
+ inline QT3_SUPPORT void setWorldXForm(bool enabled) { setMatrixEnabled(enabled); }
+ inline QT3_SUPPORT bool hasWorldXForm() const { return matrixEnabled(); }
+ inline QT3_SUPPORT void resetXForm() { resetTransform(); }
+
+ inline QT3_SUPPORT void setViewXForm(bool enabled) { setViewTransformEnabled(enabled); }
+ inline QT3_SUPPORT bool hasViewXForm() const { return viewTransformEnabled(); }
+
+ QT3_SUPPORT void map(int x, int y, int *rx, int *ry) const;
+ QT3_SUPPORT QPoint xForm(const QPoint &) const; // map virtual -> deviceb
+ QT3_SUPPORT QRect xForm(const QRect &) const;
+ QT3_SUPPORT QPolygon xForm(const QPolygon &) const;
+ QT3_SUPPORT QPolygon xForm(const QPolygon &, int index, int npoints) const;
+ QT3_SUPPORT QPoint xFormDev(const QPoint &) const; // map device -> virtual
+ QT3_SUPPORT QRect xFormDev(const QRect &) const;
+ QT3_SUPPORT QPolygon xFormDev(const QPolygon &) const;
+ QT3_SUPPORT QPolygon xFormDev(const QPolygon &, int index, int npoints) const;
+ QT3_SUPPORT qreal translationX() const;
+ QT3_SUPPORT qreal translationY() const;
+#endif
+
+private:
+ Q_DISABLE_COPY(QPainter)
+ friend class Q3Painter;
+
+ QPainterPrivate *d_ptr;
+
+ friend class QFontEngine;
+ friend class QFontEngineBox;
+ friend class QFontEngineFT;
+ friend class QFontEngineMac;
+ friend class QFontEngineWin;
+ friend class QFontEngineXLFD;
+ friend class QWSManager;
+ friend class QPaintEngine;
+ friend class QOpenGLPaintEngine;
+ friend class QX11PaintEngine;
+ friend class QX11PaintEnginePrivate;
+ friend class QWin32PaintEngine;
+ friend class QWin32PaintEnginePrivate;
+ friend class QRasterPaintEngine;
+ friend class QAlphaPaintEngine;
+ friend class QPreviewPaintEngine;
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(QPainter::RenderHints)
+
+//
+// functions
+//
+inline void QPainter::drawLine(const QLineF &l)
+{
+ drawLines(&l, 1);
+}
+
+inline void QPainter::drawLine(const QLine &line)
+{
+ drawLines(&line, 1);
+}
+
+inline void QPainter::drawLine(int x1, int y1, int x2, int y2)
+{
+ QLine l(x1, y1, x2, y2);
+ drawLines(&l, 1);
+}
+
+inline void QPainter::drawLine(const QPoint &p1, const QPoint &p2)
+{
+ QLine l(p1, p2);
+ drawLines(&l, 1);
+}
+
+inline void QPainter::drawLine(const QPointF &p1, const QPointF &p2)
+{
+ drawLine(QLineF(p1, p2));
+}
+
+inline void QPainter::drawLines(const QVector<QLineF> &lines)
+{
+ drawLines(lines.constData(), lines.size());
+}
+
+inline void QPainter::drawLines(const QVector<QLine> &lines)
+{
+ drawLines(lines.constData(), lines.size());
+}
+
+inline void QPainter::drawLines(const QVector<QPointF> &pointPairs)
+{
+ drawLines(pointPairs.constData(), pointPairs.size() / 2);
+}
+
+inline void QPainter::drawLines(const QVector<QPoint> &pointPairs)
+{
+ drawLines(pointPairs.constData(), pointPairs.size() / 2);
+}
+
+inline void QPainter::drawPolyline(const QPolygonF &polyline)
+{
+ drawPolyline(polyline.constData(), polyline.size());
+}
+
+inline void QPainter::drawPolyline(const QPolygon &polyline)
+{
+ drawPolyline(polyline.constData(), polyline.size());
+}
+
+inline void QPainter::drawPolygon(const QPolygonF &polygon, Qt::FillRule fillRule)
+{
+ drawPolygon(polygon.constData(), polygon.size(), fillRule);
+}
+
+inline void QPainter::drawPolygon(const QPolygon &polygon, Qt::FillRule fillRule)
+{
+ drawPolygon(polygon.constData(), polygon.size(), fillRule);
+}
+
+inline void QPainter::drawConvexPolygon(const QPolygonF &poly)
+{
+ drawConvexPolygon(poly.constData(), poly.size());
+}
+
+inline void QPainter::drawConvexPolygon(const QPolygon &poly)
+{
+ drawConvexPolygon(poly.constData(), poly.size());
+}
+
+inline void QPainter::drawRect(const QRectF &rect)
+{
+ drawRects(&rect, 1);
+}
+
+inline void QPainter::drawRect(int x, int y, int w, int h)
+{
+ QRect r(x, y, w, h);
+ drawRects(&r, 1);
+}
+
+inline void QPainter::drawRect(const QRect &r)
+{
+ drawRects(&r, 1);
+}
+
+inline void QPainter::drawRects(const QVector<QRectF> &rects)
+{
+ drawRects(rects.constData(), rects.size());
+}
+
+inline void QPainter::drawRects(const QVector<QRect> &rects)
+{
+ drawRects(rects.constData(), rects.size());
+}
+
+inline void QPainter::drawPoint(const QPointF &p)
+{
+ drawPoints(&p, 1);
+}
+
+inline void QPainter::drawPoint(int x, int y)
+{
+ QPoint p(x, y);
+ drawPoints(&p, 1);
+}
+
+inline void QPainter::drawPoint(const QPoint &p)
+{
+ drawPoints(&p, 1);
+}
+
+inline void QPainter::drawPoints(const QPolygonF &points)
+{
+ drawPoints(points.constData(), points.size());
+}
+
+inline void QPainter::drawPoints(const QPolygon &points)
+{
+ drawPoints(points.constData(), points.size());
+}
+
+inline void QPainter::drawRoundRect(int x, int y, int w, int h, int xRnd, int yRnd)
+{
+ drawRoundRect(QRectF(x, y, w, h), xRnd, yRnd);
+}
+
+inline void QPainter::drawRoundRect(const QRect &rect, int xRnd, int yRnd)
+{
+ drawRoundRect(QRectF(rect), xRnd, yRnd);
+}
+
+inline void QPainter::drawRoundedRect(int x, int y, int w, int h, qreal xRadius, qreal yRadius,
+ Qt::SizeMode mode)
+{
+ drawRoundedRect(QRectF(x, y, w, h), xRadius, yRadius, mode);
+}
+
+inline void QPainter::drawRoundedRect(const QRect &rect, qreal xRadius, qreal yRadius,
+ Qt::SizeMode mode)
+{
+ drawRoundedRect(QRectF(rect), xRadius, yRadius, mode);
+}
+
+inline void QPainter::drawEllipse(int x, int y, int w, int h)
+{
+ drawEllipse(QRect(x, y, w, h));
+}
+
+inline void QPainter::drawEllipse(const QPointF &center, qreal rx, qreal ry)
+{
+ drawEllipse(QRectF(center.x() - rx, center.y() - ry, 2 * rx, 2 * ry));
+}
+
+inline void QPainter::drawEllipse(const QPoint &center, int rx, int ry)
+{
+ drawEllipse(QRect(center.x() - rx, center.y() - ry, 2 * rx, 2 * ry));
+}
+
+inline void QPainter::drawArc(const QRect &r, int a, int alen)
+{
+ drawArc(QRectF(r), a, alen);
+}
+
+inline void QPainter::drawArc(int x, int y, int w, int h, int a, int alen)
+{
+ drawArc(QRectF(x, y, w, h), a, alen);
+}
+
+inline void QPainter::drawPie(const QRect &rect, int a, int alen)
+{
+ drawPie(QRectF(rect), a, alen);
+}
+
+inline void QPainter::drawPie(int x, int y, int w, int h, int a, int alen)
+{
+ drawPie(QRectF(x, y, w, h), a, alen);
+}
+
+inline void QPainter::drawChord(const QRect &rect, int a, int alen)
+{
+ drawChord(QRectF(rect), a, alen);
+}
+
+inline void QPainter::drawChord(int x, int y, int w, int h, int a, int alen)
+{
+ drawChord(QRectF(x, y, w, h), a, alen);
+}
+
+inline void QPainter::setClipRect(int x, int y, int w, int h, Qt::ClipOperation op)
+{
+ setClipRect(QRect(x, y, w, h), op);
+}
+
+inline void QPainter::eraseRect(const QRect &rect)
+{
+ eraseRect(QRectF(rect));
+}
+
+inline void QPainter::eraseRect(int x, int y, int w, int h)
+{
+ eraseRect(QRectF(x, y, w, h));
+}
+
+inline void QPainter::fillRect(int x, int y, int w, int h, const QBrush &b)
+{
+ fillRect(QRect(x, y, w, h), b);
+}
+
+inline void QPainter::fillRect(int x, int y, int w, int h, const QColor &b)
+{
+ fillRect(QRect(x, y, w, h), b);
+}
+
+inline void QPainter::fillRect(int x, int y, int w, int h, Qt::GlobalColor c)
+{
+ fillRect(QRect(x, y, w, h), QColor(c));
+}
+
+inline void QPainter::fillRect(const QRect &r, Qt::GlobalColor c)
+{
+ fillRect(r, QColor(c));
+}
+
+inline void QPainter::fillRect(const QRectF &r, Qt::GlobalColor c)
+{
+ fillRect(r, QColor(c));
+}
+
+inline void QPainter::fillRect(int x, int y, int w, int h, Qt::BrushStyle style)
+{
+ fillRect(QRectF(x, y, w, h), QBrush(style));
+}
+
+inline void QPainter::fillRect(const QRect &r, Qt::BrushStyle style)
+{
+ fillRect(QRectF(r), QBrush(style));
+}
+
+inline void QPainter::fillRect(const QRectF &r, Qt::BrushStyle style)
+{
+ fillRect(r, QBrush(style));
+}
+
+
+inline void QPainter::setBrushOrigin(int x, int y)
+{
+ setBrushOrigin(QPoint(x, y));
+}
+
+inline void QPainter::setBrushOrigin(const QPoint &p)
+{
+ setBrushOrigin(QPointF(p));
+}
+
+inline void QPainter::drawTiledPixmap(const QRect &rect, const QPixmap &pm, const QPoint &offset)
+{
+ drawTiledPixmap(QRectF(rect), pm, QPointF(offset));
+}
+
+inline void QPainter::drawTiledPixmap(int x, int y, int w, int h, const QPixmap &pm, int sx, int sy)
+{
+ drawTiledPixmap(QRectF(x, y, w, h), pm, QPointF(sx, sy));
+}
+
+inline void QPainter::drawPixmap(const QRect &targetRect, const QPixmap &pixmap, const QRect &sourceRect)
+{
+ drawPixmap(QRectF(targetRect), pixmap, QRectF(sourceRect));
+}
+
+inline void QPainter::drawPixmap(const QPoint &p, const QPixmap &pm)
+{
+ drawPixmap(QPointF(p), pm);
+}
+
+inline void QPainter::drawPixmap(const QRect &r, const QPixmap &pm)
+{
+ drawPixmap(QRectF(r), pm, QRectF());
+}
+
+inline void QPainter::drawPixmap(int x, int y, const QPixmap &pm)
+{
+ drawPixmap(QPointF(x, y), pm);
+}
+
+inline void QPainter::drawPixmap(int x, int y, int w, int h, const QPixmap &pm)
+{
+ drawPixmap(QRectF(x, y, w, h), pm, QRectF());
+}
+
+inline void QPainter::drawPixmap(int x, int y, int w, int h, const QPixmap &pm,
+ int sx, int sy, int sw, int sh)
+{
+ drawPixmap(QRectF(x, y, w, h), pm, QRectF(sx, sy, sw, sh));
+}
+
+inline void QPainter::drawPixmap(int x, int y, const QPixmap &pm,
+ int sx, int sy, int sw, int sh)
+{
+ drawPixmap(QRectF(x, y, -1, -1), pm, QRectF(sx, sy, sw, sh));
+}
+
+inline void QPainter::drawPixmap(const QPointF &p, const QPixmap &pm, const QRectF &sr)
+{
+ drawPixmap(QRectF(p.x(), p.y(), -1, -1), pm, sr);
+}
+
+inline void QPainter::drawPixmap(const QPoint &p, const QPixmap &pm, const QRect &sr)
+{
+ drawPixmap(QRectF(p.x(), p.y(), -1, -1), pm, sr);
+}
+
+inline void QPainter::drawTextItem(int x, int y, const QTextItem &ti)
+{
+ drawTextItem(QPointF(x, y), ti);
+}
+
+inline void QPainter::drawImage(const QRect &targetRect, const QImage &image, const QRect &sourceRect,
+ Qt::ImageConversionFlags flags)
+{
+ drawImage(QRectF(targetRect), image, QRectF(sourceRect), flags);
+}
+
+inline void QPainter::drawImage(const QPointF &p, const QImage &image, const QRectF &sr,
+ Qt::ImageConversionFlags flags)
+{
+ drawImage(QRectF(p.x(), p.y(), -1, -1), image, sr, flags);
+}
+
+inline void QPainter::drawImage(const QPoint &p, const QImage &image, const QRect &sr,
+ Qt::ImageConversionFlags flags)
+{
+ drawImage(QRect(p.x(), p.y(), -1, -1), image, sr, flags);
+}
+
+
+inline void QPainter::drawImage(const QRectF &r, const QImage &image)
+{
+ drawImage(r, image, QRect(0, 0, image.width(), image.height()));
+}
+
+inline void QPainter::drawImage(const QRect &r, const QImage &image)
+{
+ drawImage(r, image, QRectF(0, 0, image.width(), image.height()));
+}
+
+inline void QPainter::drawImage(const QPoint &p, const QImage &image)
+{
+ drawImage(QPointF(p), image);
+}
+
+inline void QPainter::drawImage(int x, int y, const QImage &image, int sx, int sy, int sw, int sh,
+ Qt::ImageConversionFlags flags)
+{
+ if (sx == 0 && sy == 0 && sw == -1 && sh == -1 && flags == Qt::AutoColor)
+ drawImage(QPointF(x, y), image);
+ else
+ drawImage(QRectF(x, y, -1, -1), image, QRectF(sx, sy, sw, sh), flags);
+}
+
+inline void QPainter::drawTextItem(const QPoint &p, const QTextItem &ti)
+{
+ drawTextItem(QPointF(p), ti);
+}
+
+inline void QPainter::drawText(const QPoint &p, const QString &s)
+{
+ drawText(QPointF(p), s);
+}
+
+inline void QPainter::drawText(int x, int y, int w, int h, int flags, const QString &str, QRect *br)
+{
+ drawText(QRect(x, y, w, h), flags, str, br);
+}
+
+inline void QPainter::drawText(int x, int y, const QString &s)
+{
+ drawText(QPointF(x, y), s);
+}
+
+inline QRect QPainter::boundingRect(int x, int y, int w, int h, int flags, const QString &text)
+{
+ return boundingRect(QRect(x, y, w, h), flags, text);
+}
+
+inline void QPainter::translate(qreal dx, qreal dy)
+{
+ translate(QPointF(dx, dy));
+}
+
+inline void QPainter::translate(const QPoint &offset)
+{
+ translate(offset.x(), offset.y());
+}
+
+inline void QPainter::setViewport(int x, int y, int w, int h)
+{
+ setViewport(QRect(x, y, w, h));
+}
+
+inline void QPainter::setWindow(int x, int y, int w, int h)
+{
+ setWindow(QRect(x, y, w, h));
+}
+
+#ifndef QT_NO_PICTURE
+inline void QPainter::drawPicture(int x, int y, const QPicture &p)
+{
+ drawPicture(QPoint(x, y), p);
+}
+
+inline void QPainter::drawPicture(const QPoint &pt, const QPicture &p)
+{
+ drawPicture(QPointF(pt), p);
+}
+#endif
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QPAINTER_H
diff --git a/src/gui/painting/qpainter_p.h b/src/gui/painting/qpainter_p.h
new file mode 100644
index 0000000000..258b25a09f
--- /dev/null
+++ b/src/gui/painting/qpainter_p.h
@@ -0,0 +1,260 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QPAINTER_P_H
+#define QPAINTER_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 "QtGui/qbrush.h"
+#include "QtGui/qfont.h"
+#include "QtGui/qpen.h"
+#include "QtGui/qregion.h"
+#include "QtGui/qmatrix.h"
+#include "QtGui/qpainter.h"
+#include "QtGui/qpainterpath.h"
+#include "QtGui/qpaintengine.h"
+#include <QtCore/qhash.h>
+
+#include "qpen_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class QPaintEngine;
+class QEmulationPaintEngine;
+class QPaintEngineEx;
+
+struct QTLWExtra;
+
+struct DataPtrContainer {
+ void *ptr;
+};
+
+inline void *data_ptr(const QTransform &t) { return (DataPtrContainer *) &t; }
+inline bool qtransform_fast_equals(const QTransform &a, const QTransform &b) { return data_ptr(a) == data_ptr(b); }
+
+// QPen inline functions...
+inline QPen::DataPtr &data_ptr(const QPen &p) { return const_cast<QPen &>(p).data_ptr(); }
+inline bool qpen_fast_equals(const QPen &a, const QPen &b) { return data_ptr(a) == data_ptr(b); }
+inline QBrush qpen_brush(const QPen &p) { return data_ptr(p)->brush; }
+inline qreal qpen_widthf(const QPen &p) { return data_ptr(p)->width; }
+inline Qt::PenStyle qpen_style(const QPen &p) { return data_ptr(p)->style; }
+inline Qt::PenCapStyle qpen_capStyle(const QPen &p) { return data_ptr(p)->capStyle; }
+inline Qt::PenJoinStyle qpen_joinStyle(const QPen &p) { return data_ptr(p)->joinStyle; }
+
+// QBrush inline functions...
+inline QBrush::DataPtr &data_ptr(const QBrush &p) { return const_cast<QBrush &>(p).data_ptr(); }
+inline bool qbrush_fast_equals(const QBrush &a, const QBrush &b) { return data_ptr(a) == data_ptr(b); }
+inline Qt::BrushStyle qbrush_style(const QBrush &b) { return data_ptr(b)->style; };
+inline const QColor &qbrush_color(const QBrush &b) { return data_ptr(b)->color; };
+inline bool qbrush_has_transform(const QBrush &b) { return data_ptr(b)->transform.type() > QTransform::TxNone; }
+
+class QPainterClipInfo
+{
+public:
+ enum ClipType { RegionClip, PathClip, RectClip, RectFClip };
+
+ QPainterClipInfo(const QPainterPath &p, Qt::ClipOperation op, const QTransform &m) :
+ clipType(PathClip), matrix(m), operation(op), path(p) { }
+
+ QPainterClipInfo(const QRegion &r, Qt::ClipOperation op, const QTransform &m) :
+ clipType(RegionClip), matrix(m), operation(op), region(r) { }
+
+ QPainterClipInfo(const QRect &r, Qt::ClipOperation op, const QTransform &m) :
+ clipType(RectClip), matrix(m), operation(op), rect(r) { }
+
+ QPainterClipInfo(const QRectF &r, Qt::ClipOperation op, const QTransform &m) :
+ clipType(RectFClip), matrix(m), operation(op), rectf(r) { }
+
+ ClipType clipType;
+ QTransform matrix;
+ Qt::ClipOperation operation;
+ QPainterPath path;
+ QRegion region;
+ QRect rect;
+ QRectF rectf;
+
+ // ###
+// union {
+// QRegionData *d;
+// QPainterPathPrivate *pathData;
+
+// struct {
+// int x, y, w, h;
+// } rectData;
+// struct {
+// qreal x, y, w, h;
+// } rectFData;
+// };
+
+};
+
+
+class Q_GUI_EXPORT QPainterState : public QPaintEngineState
+{
+public:
+ QPainterState();
+ QPainterState(const QPainterState *s);
+ virtual ~QPainterState();
+ void init(QPainter *p);
+
+ QPointF brushOrigin;
+ QFont font;
+ QFont deviceFont;
+ QPen pen;
+ QBrush brush;
+ QBrush bgBrush; // background brush
+ QRegion clipRegion;
+ QPainterPath clipPath;
+ Qt::ClipOperation clipOperation;
+ QPainter::RenderHints renderHints;
+ QList<QPainterClipInfo> clipInfo; // ### Make me smaller and faster to copy around...
+ QTransform worldMatrix; // World transformation matrix, not window and viewport
+ QTransform matrix; // Complete transformation matrix,
+ QPoint redirection_offset;
+ int wx, wy, ww, wh; // window rectangle
+ int vx, vy, vw, vh; // viewport rectangle
+ qreal opacity;
+
+ uint WxF:1; // World transformation
+ uint VxF:1; // View transformation
+ uint clipEnabled:1;
+
+ Qt::BGMode bgMode;
+ QPainter *painter;
+ Qt::LayoutDirection layoutDirection;
+ QPainter::CompositionMode composition_mode;
+ uint emulationSpecifier;
+ uint changeFlags;
+};
+
+struct QPainterDummyState
+{
+ QFont font;
+ QPen pen;
+ QBrush brush;
+ QTransform transform;
+};
+
+class QPainterPrivate
+{
+ Q_DECLARE_PUBLIC(QPainter)
+public:
+ QPainterPrivate(QPainter *painter)
+ : q_ptr(painter), d_ptrs(0), state(0), dummyState(0), txinv(0), inDestructor(false), d_ptrs_size(0),
+ refcount(1), device(0), original_device(0), helper_device(0), engine(0), emulationEngine(0),
+ extended(0)
+ {
+ }
+
+ ~QPainterPrivate();
+
+ QPainter *q_ptr;
+ QPainterPrivate **d_ptrs;
+
+ QPainterState *state;
+ QVector<QPainterState*> states;
+
+ mutable QPainterDummyState *dummyState;
+
+ QTransform invMatrix;
+ uint txinv:1;
+ uint inDestructor : 1;
+ uint d_ptrs_size;
+ uint refcount;
+
+ enum DrawOperation { StrokeDraw = 0x1,
+ FillDraw = 0x2,
+ StrokeAndFillDraw = 0x3
+ };
+
+ QPainterDummyState *fakeState() const {
+ if (!dummyState)
+ dummyState = new QPainterDummyState();
+ return dummyState;
+ }
+
+ void updateEmulationSpecifier(QPainterState *s);
+ void updateStateImpl(QPainterState *state);
+ void updateState(QPainterState *state);
+
+ void draw_helper(const QPainterPath &path, DrawOperation operation = StrokeAndFillDraw);
+ void drawStretchedGradient(const QPainterPath &path, DrawOperation operation);
+ void drawOpaqueBackground(const QPainterPath &path, DrawOperation operation);
+
+ void updateMatrix();
+ void updateInvMatrix();
+
+ int rectSubtraction() const {
+ return state->pen.style() != Qt::NoPen && state->pen.width() == 0 ? 1 : 0;
+ }
+
+ void checkEmulation();
+
+ QTransform viewTransform() const;
+ static bool attachPainterPrivate(QPainter *q, QPaintDevice *pdev);
+ void detachPainterPrivate(QPainter *q);
+
+ QPaintDevice *device;
+ QPaintDevice *original_device;
+ QPaintDevice *helper_device;
+ QPaintEngine *engine;
+ QEmulationPaintEngine *emulationEngine;
+ QPaintEngineEx *extended;
+ QBrush colorBrush; // for fill with solid color
+};
+
+Q_GUI_EXPORT void qt_draw_helper(QPainterPrivate *p, const QPainterPath &path, QPainterPrivate::DrawOperation operation);
+
+QString qt_generate_brush_key(const QBrush &brush);
+
+QT_END_NAMESPACE
+
+#endif // QPAINTER_P_H
diff --git a/src/gui/painting/qpainterpath.cpp b/src/gui/painting/qpainterpath.cpp
new file mode 100644
index 0000000000..bb07f813a7
--- /dev/null
+++ b/src/gui/painting/qpainterpath.cpp
@@ -0,0 +1,3309 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qpainterpath.h"
+#include "qpainterpath_p.h"
+
+#include <qbitmap.h>
+#include <qdebug.h>
+#include <qiodevice.h>
+#include <qlist.h>
+#include <qmatrix.h>
+#include <qpen.h>
+#include <qpolygon.h>
+#include <qtextlayout.h>
+#include <qvarlengtharray.h>
+#include <qmath.h>
+
+#include <private/qbezier_p.h>
+#include <private/qfontengine_p.h>
+#include <private/qnumeric_p.h>
+#include <private/qobject_p.h>
+#include <private/qpathclipper_p.h>
+#include <private/qstroker_p.h>
+#include <private/qtextengine_p.h>
+
+#include <limits.h>
+
+#if 0
+#include <performance.h>
+#else
+#define PM_INIT
+#define PM_MEASURE(x)
+#define PM_DISPLAY
+#endif
+
+QT_BEGIN_NAMESPACE
+
+// This value is used to determine the length of control point vectors
+// when approximating arc segments as curves. The factor is multiplied
+// with the radius of the circle.
+
+// #define QPP_DEBUG
+// #define QPP_STROKE_DEBUG
+//#define QPP_FILLPOLYGONS_DEBUG
+
+QPainterPath qt_stroke_dash(const QPainterPath &path, qreal *dashes, int dashCount);
+
+void qt_find_ellipse_coords(const QRectF &r, qreal angle, qreal length,
+ QPointF* startPoint, QPointF *endPoint)
+{
+ if (r.isNull()) {
+ if (startPoint)
+ *startPoint = QPointF();
+ if (endPoint)
+ *endPoint = QPointF();
+ return;
+ }
+
+ qreal w2 = r.width() / 2;
+ qreal h2 = r.height() / 2;
+
+ qreal angles[2] = { angle, angle + length };
+ QPointF *points[2] = { startPoint, endPoint };
+
+ for (int i = 0; i < 2; ++i) {
+ if (!points[i])
+ continue;
+
+ qreal theta = angles[i] - 360 * qFloor(angles[i] / 360);
+ qreal t = theta / 90;
+ // truncate
+ int quadrant = int(t);
+ t -= quadrant;
+
+ t = qt_t_for_arc_angle(90 * t);
+
+ // swap x and y?
+ if (quadrant & 1)
+ t = 1 - t;
+
+ qreal a, b, c, d;
+ QBezier::coefficients(t, a, b, c, d);
+ QPointF p(a + b + c*QT_PATH_KAPPA, d + c + b*QT_PATH_KAPPA);
+
+ // left quadrants
+ if (quadrant == 1 || quadrant == 2)
+ p.rx() = -p.x();
+
+ // top quadrants
+ if (quadrant == 0 || quadrant == 1)
+ p.ry() = -p.y();
+
+ *points[i] = r.center() + QPointF(w2 * p.x(), h2 * p.y());
+ }
+}
+
+#ifdef QPP_DEBUG
+static void qt_debug_path(const QPainterPath &path)
+{
+ const char *names[] = {
+ "MoveTo ",
+ "LineTo ",
+ "CurveTo ",
+ "CurveToData"
+ };
+
+ printf("\nQPainterPath: elementCount=%d\n", path.elementCount());
+ for (int i=0; i<path.elementCount(); ++i) {
+ const QPainterPath::Element &e = path.elementAt(i);
+ Q_ASSERT(e.type >= 0 && e.type <= QPainterPath::CurveToDataElement);
+ printf(" - %3d:: %s, (%.2f, %.2f)\n", i, names[e.type], e.x, e.y);
+ }
+}
+#endif
+
+/*!
+ \class QPainterPath
+ \ingroup multimedia
+ \ingroup shared
+
+ \brief The QPainterPath class provides a container for painting operations,
+ enabling graphical shapes to be constructed and reused.
+
+ A painter path is an object composed of a number of graphical
+ building blocks, such as rectangles, ellipses, lines, and curves.
+ Building blocks can be joined in closed subpaths, for example as a
+ rectangle or an ellipse. A closed path has coinciding start and
+ end points. Or they can exist independently as unclosed subpaths,
+ such as lines and curves.
+
+ A QPainterPath object can be used for filling, outlining, and
+ clipping. To generate fillable outlines for a given painter path,
+ use the QPainterPathStroker class. The main advantage of painter
+ paths over normal drawing operations is that complex shapes only
+ need to be created once; then they can be drawn many times using
+ only calls to the QPainter::drawPath() function.
+
+ QPainterPath provides a collection of functions that can be used
+ to obtain information about the path and its elements. In addition
+ it is possible to reverse the order of the elements using the
+ toReversed() function. There are also several functions to convert
+ this painter path object into a polygon representation.
+
+ \tableofcontents
+
+ \section1 Composing a QPainterPath
+
+ A QPainterPath object can be constructed as an empty path, with a
+ given start point, or as a copy of another QPainterPath object.
+ Once created, lines and curves can be added to the path using the
+ lineTo(), arcTo(), cubicTo() and quadTo() functions. The lines and
+ curves stretch from the currentPosition() to the position passed
+ as argument.
+
+ The currentPosition() of the QPainterPath object is always the end
+ position of the last subpath that was added (or the initial start
+ point). Use the moveTo() function to move the currentPosition()
+ without adding a component. The moveTo() function implicitly
+ starts a new subpath, and closes the previous one. Another way of
+ starting a new subpath is to call the closeSubpath() function
+ which closes the current path by adding a line from the
+ currentPosition() back to the path's start position. Note that the
+ new path will have (0, 0) as its initial currentPosition().
+
+ QPainterPath class also provides several convenience functions to
+ add closed subpaths to a painter path: addEllipse(), addPath(),
+ addRect(), addRegion() and addText(). The addPolygon() function
+ adds an \e unclosed subpath. In fact, these functions are all
+ collections of moveTo(), lineTo() and cubicTo() operations.
+
+ In addition, a path can be added to the current path using the
+ connectPath() function. But note that this function will connect
+ the last element of the current path to the first element of given
+ one by adding a line.
+
+ Below is a code snippet that shows how a QPainterPath object can
+ be used:
+
+ \table 100%
+ \row
+ \o \inlineimage qpainterpath-construction.png
+ \o
+ \snippet doc/src/snippets/code/src_gui_painting_qpainterpath.cpp 0
+ \endtable
+
+ The painter path is initially empty when constructed. We first add
+ a rectangle, which is a closed subpath. Then we add two bezier
+ curves which together form a closed subpath even though they are
+ not closed individually. Finally we draw the entire path. The path
+ is filled using the default fill rule, Qt::OddEvenFill. Qt
+ provides two methods for filling paths:
+
+ \table
+ \row
+ \o \inlineimage qt-fillrule-oddeven.png
+ \o \inlineimage qt-fillrule-winding.png
+ \header
+ \o Qt::OddEvenFill
+ \o Qt::WindingFill
+ \endtable
+
+ See the Qt::FillRule documentation for the definition of the
+ rules. A painter path's currently set fill rule can be retrieved
+ using the fillRule() function, and altered using the setFillRule()
+ function.
+
+ \section1 QPainterPath Information
+
+ The QPainterPath class provides a collection of functions that
+ returns information about the path and its elements.
+
+ The currentPosition() function returns the end point of the last
+ subpath that was added (or the initial start point). The
+ elementAt() function can be used to retrieve the various subpath
+ elements, the \e number of elements can be retrieved using the
+ elementCount() function, and the isEmpty() function tells whether
+ this QPainterPath object contains any elements at all.
+
+ The controlPointRect() function returns the rectangle containing
+ all the points and control points in this path. This function is
+ significantly faster to compute than the exact boundingRect()
+ which returns the bounding rectangle of this painter path with
+ floating point precision.
+
+ Finally, QPainterPath provides the contains() function which can
+ be used to determine whether a given point or rectangle is inside
+ the path, and the intersects() function which determines if any of
+ the points inside a given rectangle also are inside this path.
+
+ \section1 QPainterPath Conversion
+
+ For compatibility reasons, it might be required to simplify the
+ representation of a painter path: QPainterPath provides the
+ toFillPolygon(), toFillPolygons() and toSubpathPolygons()
+ functions which convert the painter path into a polygon. The
+ toFillPolygon() returns the painter path as one single polygon,
+ while the two latter functions return a list of polygons.
+
+ The toFillPolygons() and toSubpathPolygons() functions are
+ provided because it is usually faster to draw several small
+ polygons than to draw one large polygon, even though the total
+ number of points drawn is the same. The difference between the two
+ is the \e number of polygons they return: The toSubpathPolygons()
+ creates one polygon for each subpath regardless of intersecting
+ subpaths (i.e. overlapping bounding rectangles), while the
+ toFillPolygons() functions creates only one polygon for
+ overlapping subpaths.
+
+ The toFillPolygon() and toFillPolygons() functions first convert
+ all the subpaths to polygons, then uses a rewinding technique to
+ make sure that overlapping subpaths can be filled using the
+ correct fill rule. Note that rewinding inserts additional lines in
+ the polygon so the outline of the fill polygon does not match the
+ outline of the path.
+
+ \section1 Examples
+
+ Qt provides the \l {painting/painterpaths}{Painter Paths Example}
+ and the \l {demos/deform}{Vector Deformation Demo} which are
+ located in Qt's example and demo directories respectively.
+
+ The \l {painting/painterpaths}{Painter Paths Example} shows how
+ painter paths can be used to build complex shapes for rendering
+ and lets the user experiment with the filling and stroking. The
+ \l {demos/deform}{Vector Deformation Demo} shows how to use
+ QPainterPath to draw text.
+
+ \table
+ \row
+ \o \inlineimage qpainterpath-example.png
+ \o \inlineimage qpainterpath-demo.png
+ \header
+ \o \l {painting/painterpaths}{Painter Paths Example}
+ \o \l {demos/deform}{Vector Deformation Demo}
+ \endtable
+
+ \sa QPainterPathStroker, QPainter, QRegion, {Painter Paths Example}
+*/
+
+/*!
+ \enum QPainterPath::ElementType
+
+ This enum describes the types of elements used to connect vertices
+ in subpaths.
+
+ Note that elements added as closed subpaths using the
+ addEllipse(), addPath(), addPolygon(), addRect(), addRegion() and
+ addText() convenience functions, is actually added to the path as
+ a collection of separate elements using the moveTo(), lineTo() and
+ cubicTo() functions.
+
+ \value MoveToElement A new subpath. See also moveTo().
+ \value LineToElement A line. See also lineTo().
+ \value CurveToElement A curve. See also cubicTo() and quadTo().
+ \value CurveToDataElement The extra data required to describe a curve in
+ a CurveToElement element.
+
+ \sa elementAt(), elementCount()
+*/
+
+/*!
+ \class QPainterPath::Element
+
+ \brief The QPainterPath::Element class specifies the position and
+ type of a subpath.
+
+ Once a QPainterPath object is constructed, subpaths like lines and
+ curves can be added to the path (creating
+ QPainterPath::LineToElement and QPainterPath::CurveToElement
+ components).
+
+ The lines and curves stretch from the currentPosition() to the
+ position passed as argument. The currentPosition() of the
+ QPainterPath object is always the end position of the last subpath
+ that was added (or the initial start point). The moveTo() function
+ can be used to move the currentPosition() without adding a line or
+ curve, creating a QPainterPath::MoveToElement component.
+
+ \sa QPainterPath
+*/
+
+/*!
+ \variable QPainterPath::Element::x
+ \brief the x coordinate of the element's position.
+
+ \sa {operator QPointF()}
+*/
+
+/*!
+ \variable QPainterPath::Element::y
+ \brief the y coordinate of the element's position.
+
+ \sa {operator QPointF()}
+*/
+
+/*!
+ \variable QPainterPath::Element::type
+ \brief the type of element
+
+ \sa isCurveTo(), isLineTo(), isMoveTo()
+*/
+
+/*!
+ \fn bool QPainterPath::Element::operator==(const Element &other) const
+ \since 4.2
+
+ Returns true if this element is equal to \a other;
+ otherwise returns false.
+
+ \sa operator!=()
+*/
+
+/*!
+ \fn bool QPainterPath::Element::operator!=(const Element &other) const
+ \since 4.2
+
+ Returns true if this element is not equal to \a other;
+ otherwise returns false.
+
+ \sa operator==()
+*/
+
+/*!
+ \fn bool QPainterPath::Element::isCurveTo () const
+
+ Returns true if the element is a curve, otherwise returns false.
+
+ \sa type, QPainterPath::CurveToElement
+*/
+
+/*!
+ \fn bool QPainterPath::Element::isLineTo () const
+
+ Returns true if the element is a line, otherwise returns false.
+
+ \sa type, QPainterPath::LineToElement
+*/
+
+/*!
+ \fn bool QPainterPath::Element::isMoveTo () const
+
+ Returns true if the element is moving the current position,
+ otherwise returns false.
+
+ \sa type, QPainterPath::MoveToElement
+*/
+
+/*!
+ \fn QPainterPath::Element::operator QPointF () const
+
+ Returns the element's position.
+
+ \sa x, y
+*/
+
+/*!
+ \fn void QPainterPath::addEllipse(qreal x, qreal y, qreal width, qreal height)
+ \overload
+
+ Creates an ellipse within the bounding rectangle defined by its top-left
+ corner at (\a x, \a y), \a width and \a height, and adds it to the
+ painter path as a closed subpath.
+*/
+
+/*!
+ \since 4.4
+
+ \fn void QPainterPath::addEllipse(const QPointF &center, qreal rx, qreal ry)
+ \overload
+
+ Creates an ellipse positioned at \a{center} with radii \a{rx} and \a{ry},
+ and adds it to the painter path as a closed subpath.
+*/
+
+/*!
+ \fn void QPainterPath::addText(qreal x, qreal y, const QFont &font, const QString &text)
+ \overload
+
+ Adds the given \a text to this path as a set of closed subpaths created
+ from the \a font supplied. The subpaths are positioned so that the left
+ end of the text's baseline lies at the point specified by (\a x, \a y).
+*/
+
+/*!
+ \fn int QPainterPath::elementCount() const
+
+ Returns the number of path elements in the painter path.
+
+ \sa ElementType, elementAt(), isEmpty()
+*/
+
+/*!
+ \fn const QPainterPath::Element &QPainterPath::elementAt(int index) const
+
+ Returns the element at the given \a index in the painter path.
+
+ \sa ElementType, elementCount(), isEmpty()
+*/
+
+/*!
+ \fn void QPainterPath::setElementPositionAt(int index, qreal x, qreal y)
+ \since 4.2
+
+ Sets the x and y coordinate of the element at index \a index to \a
+ x and \a y.
+*/
+
+/*###
+ \fn QPainterPath &QPainterPath::operator +=(const QPainterPath &other)
+
+ Appends the \a other painter path to this painter path and returns a
+ reference to the result.
+*/
+
+/*!
+ Constructs an empty QPainterPath object.
+*/
+QPainterPath::QPainterPath()
+ : d_ptr(0)
+{
+}
+
+/*!
+ \fn QPainterPath::QPainterPath(const QPainterPath &path)
+
+ Creates a QPainterPath object that is a copy of the given \a path.
+
+ \sa operator=()
+*/
+QPainterPath::QPainterPath(const QPainterPath &other)
+ : d_ptr(other.d_ptr)
+{
+ if (d_func())
+ d_func()->ref.ref();
+}
+
+/*!
+ Creates a QPainterPath object with the given \a startPoint as its
+ current position.
+*/
+
+QPainterPath::QPainterPath(const QPointF &startPoint)
+ : d_ptr(new QPainterPathData)
+{
+ Element e = { startPoint.x(), startPoint.y(), MoveToElement };
+ d_func()->elements << e;
+}
+
+/*!
+ \internal
+*/
+void QPainterPath::detach_helper()
+{
+ QPainterPathPrivate *data = new QPainterPathData(*d_func());
+ if (d_ptr && !d_ptr->ref.deref())
+ delete d_ptr;
+ d_ptr = data;
+}
+
+/*!
+ \internal
+*/
+void QPainterPath::ensureData_helper()
+{
+ QPainterPathPrivate *data = new QPainterPathData;
+ data->elements.reserve(16);
+ QPainterPath::Element e = { 0, 0, QPainterPath::MoveToElement };
+ data->elements << e;
+ if (d_ptr && !d_ptr->ref.deref())
+ delete d_ptr;
+ d_ptr = data;
+ Q_ASSERT(d_ptr != 0);
+}
+
+/*!
+ \fn QPainterPath &QPainterPath::operator=(const QPainterPath &path)
+
+ Assigns the given \a path to this painter path.
+
+ \sa QPainterPath()
+*/
+QPainterPath &QPainterPath::operator=(const QPainterPath &other)
+{
+ if (other.d_func() != d_func()) {
+ QPainterPathPrivate *data = other.d_func();
+ if (data)
+ data->ref.ref();
+ if (d_ptr && !d_ptr->ref.deref())
+ delete d_ptr;
+ d_ptr = data;
+ }
+ return *this;
+}
+
+/*!
+ Destroys this QPainterPath object.
+*/
+QPainterPath::~QPainterPath()
+{
+ if (d_func() && !d_func()->ref.deref())
+ delete d_func();
+}
+
+/*!
+ Closes the current subpath by drawing a line to the beginning of
+ the subpath, automatically starting a new path. The current point
+ of the new path is (0, 0).
+
+ If the subpath does not contain any elements, this function does
+ nothing.
+
+ \sa moveTo(), {QPainterPath#Composing a QPainterPath}{Composing
+ a QPainterPath}
+ */
+void QPainterPath::closeSubpath()
+{
+#ifdef QPP_DEBUG
+ printf("QPainterPath::closeSubpath()\n");
+#endif
+ if (isEmpty())
+ return;
+ detach();
+
+ d_func()->close();
+}
+
+/*!
+ \fn void QPainterPath::moveTo(qreal x, qreal y)
+
+ \overload
+
+ Moves the current position to (\a{x}, \a{y}) and starts a new
+ subpath, implicitly closing the previous path.
+*/
+
+/*!
+ \fn void QPainterPath::moveTo(const QPointF &point)
+
+ Moves the current point to the given \a point, implicitly starting
+ a new subpath and closing the previous one.
+
+ \sa closeSubpath(), {QPainterPath#Composing a
+ QPainterPath}{Composing a QPainterPath}
+*/
+void QPainterPath::moveTo(const QPointF &p)
+{
+#ifdef QPP_DEBUG
+ printf("QPainterPath::moveTo() (%.2f,%.2f)\n", p.x(), p.y());
+#endif
+#ifndef QT_NO_DEBUG
+ if (qt_is_nan(p.x()) || qt_is_nan(p.y()))
+ qWarning("QPainterPath::moveTo: Adding point where x or y is NaN, results are undefined");
+#endif
+ ensureData();
+ detach();
+
+ QPainterPathData *d = d_func();
+ Q_ASSERT(!d->elements.isEmpty());
+
+ d->require_moveTo = false;
+
+ if (d->elements.last().type == MoveToElement) {
+ d->elements.last().x = p.x();
+ d->elements.last().y = p.y();
+ } else {
+ Element elm = { p.x(), p.y(), MoveToElement };
+ d->elements.append(elm);
+ }
+ d->cStart = d->elements.size() - 1;
+}
+
+/*!
+ \fn void QPainterPath::lineTo(qreal x, qreal y)
+
+ \overload
+
+ Draws a line from the current position to the point (\a{x},
+ \a{y}).
+*/
+
+/*!
+ \fn void QPainterPath::lineTo(const QPointF &endPoint)
+
+ Adds a straight line from the current position to the given \a
+ endPoint. After the line is drawn, the current position is updated
+ to be at the end point of the line.
+
+ \sa addPolygon(), addRect(), {QPainterPath#Composing a
+ QPainterPath}{Composing a QPainterPath}
+ */
+void QPainterPath::lineTo(const QPointF &p)
+{
+#ifdef QPP_DEBUG
+ printf("QPainterPath::lineTo() (%.2f,%.2f)\n", p.x(), p.y());
+#endif
+#ifndef QT_NO_DEBUG
+ if (qt_is_nan(p.x()) || qt_is_nan(p.y()))
+ qWarning("QPainterPath::lineTo: Adding point where x or y is NaN, results are undefined");
+#endif
+ ensureData();
+ detach();
+
+ QPainterPathData *d = d_func();
+ Q_ASSERT(!d->elements.isEmpty());
+ d->maybeMoveTo();
+ if (p == QPointF(d->elements.last()))
+ return;
+ Element elm = { p.x(), p.y(), LineToElement };
+ d->elements.append(elm);
+}
+
+/*!
+ \fn void QPainterPath::cubicTo(qreal c1X, qreal c1Y, qreal c2X,
+ qreal c2Y, qreal endPointX, qreal endPointY);
+
+ \overload
+
+ Adds a cubic Bezier curve between the current position and the end
+ point (\a{endPointX}, \a{endPointY}) with control points specified
+ by (\a{c1X}, \a{c1Y}) and (\a{c2X}, \a{c2Y}).
+*/
+
+/*!
+ \fn void QPainterPath::cubicTo(const QPointF &c1, const QPointF &c2, const QPointF &endPoint)
+
+ Adds a cubic Bezier curve between the current position and the
+ given \a endPoint using the control points specified by \a c1, and
+ \a c2.
+
+ After the curve is added, the current position is updated to be at
+ the end point of the curve.
+
+ \table 100%
+ \row
+ \o \inlineimage qpainterpath-cubicto.png
+ \o
+ \snippet doc/src/snippets/code/src_gui_painting_qpainterpath.cpp 1
+ \endtable
+
+ \sa quadTo(), {QPainterPath#Composing a QPainterPath}{Composing
+ a QPainterPath}
+*/
+void QPainterPath::cubicTo(const QPointF &c1, const QPointF &c2, const QPointF &e)
+{
+#ifdef QPP_DEBUG
+ printf("QPainterPath::cubicTo() (%.2f,%.2f), (%.2f,%.2f), (%.2f,%.2f)\n",
+ c1.x(), c1.y(), c2.x(), c2.y(), e.x(), e.y());
+#endif
+#ifndef QT_NO_DEBUG
+ if (qt_is_nan(c1.x()) || qt_is_nan(c1.y()) || qt_is_nan(c2.x()) || qt_is_nan(c2.y())
+ || qt_is_nan(e.x()) || qt_is_nan(e.y()))
+ qWarning("QPainterPath::cubicTo: Adding point where x or y is NaN, results are undefined");
+#endif
+ ensureData();
+ detach();
+
+ QPainterPathData *d = d_func();
+ Q_ASSERT(!d->elements.isEmpty());
+
+
+ // Abort on empty curve as a stroker cannot handle this and the
+ // curve is irrelevant anyway.
+ if (d->elements.last() == c1 && c1 == c2 && c2 == e)
+ return;
+
+ d->maybeMoveTo();
+
+ Element ce1 = { c1.x(), c1.y(), CurveToElement };
+ Element ce2 = { c2.x(), c2.y(), CurveToDataElement };
+ Element ee = { e.x(), e.y(), CurveToDataElement };
+ d->elements << ce1 << ce2 << ee;
+}
+
+/*!
+ \fn void QPainterPath::quadTo(qreal cx, qreal cy, qreal endPointX, qreal endPointY);
+
+ \overload
+
+ Adds a quadratic Bezier curve between the current point and the endpoint
+ (\a{endPointX}, \a{endPointY}) with the control point specified by
+ (\a{cx}, \a{cy}).
+*/
+
+/*!
+ \fn void QPainterPath::quadTo(const QPointF &c, const QPointF &endPoint)
+
+ Adds a quadratic Bezier curve between the current position and the
+ given \a endPoint with the control point specified by \a c.
+
+ After the curve is added, the current point is updated to be at
+ the end point of the curve.
+
+ \sa cubicTo(), {QPainterPath#Composing a QPainterPath}{Composing a
+ QPainterPath}
+*/
+void QPainterPath::quadTo(const QPointF &c, const QPointF &e)
+{
+#ifdef QPP_DEBUG
+ printf("QPainterPath::quadTo() (%.2f,%.2f), (%.2f,%.2f)\n",
+ c.x(), c.y(), e.x(), e.y());
+#endif
+#ifndef QT_NO_DEBUG
+ if (qt_is_nan(c.x()) || qt_is_nan(c.y()) || qt_is_nan(e.x()) || qt_is_nan(e.y()))
+ qWarning("QPainterPath::quadTo: Adding point where x or y is NaN, results are undefined");
+#endif
+ ensureData();
+ detach();
+
+ Q_D(QPainterPath);
+ Q_ASSERT(!d->elements.isEmpty());
+ const QPainterPath::Element &elm = d->elements.at(elementCount()-1);
+ QPointF prev(elm.x, elm.y);
+
+ // Abort on empty curve as a stroker cannot handle this and the
+ // curve is irrelevant anyway.
+ if (prev == c && c == e)
+ return;
+
+ QPointF c1((prev.x() + 2*c.x()) / 3, (prev.y() + 2*c.y()) / 3);
+ QPointF c2((e.x() + 2*c.x()) / 3, (e.y() + 2*c.y()) / 3);
+ cubicTo(c1, c2, e);
+}
+
+/*!
+ \fn void QPainterPath::arcTo(qreal x, qreal y, qreal width, qreal
+ height, qreal startAngle, qreal sweepLength)
+
+ \overload
+
+ Creates an arc that occupies the rectangle QRectF(\a x, \a y, \a
+ width, \a height), beginning at the specified \a startAngle and
+ extending \a sweepLength degrees counter-clockwise.
+
+*/
+
+/*!
+ \fn void QPainterPath::arcTo(const QRectF &rectangle, qreal startAngle, qreal sweepLength)
+
+ Creates an arc that occupies the given \a rectangle, beginning at
+ the specified \a startAngle and extending \a sweepLength degrees
+ counter-clockwise.
+
+ Angles are specified in degrees. Clockwise arcs can be specified
+ using negative angles.
+
+ Note that this function connects the starting point of the arc to
+ the current position if they are not already connected. After the
+ arc has been added, the current position is the last point in
+ arc. To draw a line back to the first point, use the
+ closeSubpath() function.
+
+ \table 100%
+ \row
+ \o \inlineimage qpainterpath-arcto.png
+ \o
+ \snippet doc/src/snippets/code/src_gui_painting_qpainterpath.cpp 2
+ \endtable
+
+ \sa arcMoveTo(), addEllipse(), QPainter::drawArc(), QPainter::drawPie(),
+ {QPainterPath#Composing a QPainterPath}{Composing a
+ QPainterPath}
+*/
+void QPainterPath::arcTo(const QRectF &rect, qreal startAngle, qreal sweepLength)
+{
+#ifdef QPP_DEBUG
+ printf("QPainterPath::arcTo() (%.2f, %.2f, %.2f, %.2f, angle=%.2f, sweep=%.2f\n",
+ rect.x(), rect.y(), rect.width(), rect.height(), startAngle, sweepLength);
+#endif
+#ifndef QT_NO_DEBUG
+ if (qt_is_nan(rect.x()) || qt_is_nan(rect.y()) || qt_is_nan(rect.width()) || qt_is_nan(rect.height())
+ || qt_is_nan(startAngle) || qt_is_nan(sweepLength))
+ qWarning("QPainterPath::arcTo: Adding arc where a parameter is NaN, results are undefined");
+#endif
+ if (rect.isNull())
+ return;
+
+ ensureData();
+ detach();
+
+ int point_count;
+ QPointF pts[15];
+ QPointF curve_start = qt_curves_for_arc(rect, startAngle, sweepLength, pts, &point_count);
+
+ lineTo(curve_start);
+ for (int i=0; i<point_count; i+=3) {
+ cubicTo(pts[i].x(), pts[i].y(),
+ pts[i+1].x(), pts[i+1].y(),
+ pts[i+2].x(), pts[i+2].y());
+ }
+
+}
+
+
+/*!
+ \fn void QPainterPath::arcMoveTo(qreal x, qreal y, qreal width, qreal height, qreal angle)
+ \overload
+ \since 4.2
+
+ Creates a move to that lies on the arc that occupies the
+ QRectF(\a x, \a y, \a width, \a height) at \a angle.
+*/
+
+
+/*!
+ \fn void QPainterPath::arcMoveTo(const QRectF &rectangle, qreal angle)
+ \since 4.2
+
+ Creates a move to that lies on the arc that occupies the given \a
+ rectangle at \a angle.
+
+ Angles are specified in degrees. Clockwise arcs can be specified
+ using negative angles.
+
+ \sa moveTo(), arcTo()
+*/
+
+void QPainterPath::arcMoveTo(const QRectF &rect, qreal angle)
+{
+ if (rect.isNull())
+ return;
+
+ QPointF pt;
+ qt_find_ellipse_coords(rect, angle, 0, &pt, 0);
+ moveTo(pt);
+}
+
+
+
+/*!
+ \fn QPointF QPainterPath::currentPosition() const
+
+ Returns the current position of the path.
+*/
+QPointF QPainterPath::currentPosition() const
+{
+ return !d_ptr || d_func()->elements.isEmpty()
+ ? QPointF()
+ : QPointF(d_func()->elements.last().x, d_func()->elements.last().y);
+}
+
+
+/*!
+ \fn void QPainterPath::addRect(qreal x, qreal y, qreal width, qreal height)
+
+ \overload
+
+ Adds a rectangle at position (\a{x}, \a{y}), with the given \a
+ width and \a height, as a closed subpath.
+*/
+
+/*!
+ \fn void QPainterPath::addRect(const QRectF &rectangle)
+
+ Adds the given \a rectangle to this path as a closed subpath.
+
+ The \a rectangle is added as a clockwise set of lines. The painter
+ path's current position after the \a rectangle has been added is
+ at the top-left corner of the rectangle.
+
+ \table 100%
+ \row
+ \o \inlineimage qpainterpath-addrectangle.png
+ \o
+ \snippet doc/src/snippets/code/src_gui_painting_qpainterpath.cpp 3
+ \endtable
+
+ \sa addRegion(), lineTo(), {QPainterPath#Composing a
+ QPainterPath}{Composing a QPainterPath}
+*/
+void QPainterPath::addRect(const QRectF &r)
+{
+#ifndef QT_NO_DEBUG
+ if (qt_is_nan(r.x()) || qt_is_nan(r.y()) || qt_is_nan(r.width()) || qt_is_nan(r.height()))
+ qWarning("QPainterPath::addRect: Adding rect where a parameter is NaN, results are undefined");
+#endif
+ if (r.isNull())
+ return;
+
+ ensureData();
+ detach();
+
+ d_func()->elements.reserve(d_func()->elements.size() + 5);
+ moveTo(r.x(), r.y());
+
+ Element l1 = { r.x() + r.width(), r.y(), LineToElement };
+ Element l2 = { r.x() + r.width(), r.y() + r.height(), LineToElement };
+ Element l3 = { r.x(), r.y() + r.height(), LineToElement };
+ Element l4 = { r.x(), r.y(), LineToElement };
+
+ d_func()->elements << l1 << l2 << l3 << l4;
+ d_func()->require_moveTo = true;
+}
+
+/*!
+ Adds the given \a polygon to the path as an (unclosed) subpath.
+
+ Note that the current position after the polygon has been added,
+ is the last point in \a polygon. To draw a line back to the first
+ point, use the closeSubpath() function.
+
+ \table 100%
+ \row
+ \o \inlineimage qpainterpath-addpolygon.png
+ \o
+ \snippet doc/src/snippets/code/src_gui_painting_qpainterpath.cpp 4
+ \endtable
+
+ \sa lineTo(), {QPainterPath#Composing a QPainterPath}{Composing
+ a QPainterPath}
+*/
+void QPainterPath::addPolygon(const QPolygonF &polygon)
+{
+ if (polygon.isEmpty())
+ return;
+
+ ensureData();
+ detach();
+
+ d_func()->elements.reserve(d_func()->elements.size() + polygon.size());
+
+ moveTo(polygon.first());
+ for (int i=1; i<polygon.size(); ++i) {
+ Element elm = { polygon.at(i).x(), polygon.at(i).y(), LineToElement };
+ d_func()->elements << elm;
+ }
+}
+
+/*!
+ \fn void QPainterPath::addEllipse(const QRectF &boundingRectangle)
+
+ Creates an ellipse within the the specified \a boundingRectangle
+ and adds it to the painter path as a closed subpath.
+
+ The ellipse is composed of a clockwise curve, starting and
+ finishing at zero degrees (the 3 o'clock position).
+
+ \table 100%
+ \row
+ \o \inlineimage qpainterpath-addellipse.png
+ \o
+ \snippet doc/src/snippets/code/src_gui_painting_qpainterpath.cpp 5
+ \endtable
+
+ \sa arcTo(), QPainter::drawEllipse(), {QPainterPath#Composing a
+ QPainterPath}{Composing a QPainterPath}
+*/
+void QPainterPath::addEllipse(const QRectF &boundingRect)
+{
+#ifndef QT_NO_DEBUG
+ if (qt_is_nan(boundingRect.x()) || qt_is_nan(boundingRect.y())
+ || qt_is_nan(boundingRect.width()) || qt_is_nan(boundingRect.height()))
+ qWarning("QPainterPath::addEllipse: Adding ellipse where a parameter is NaN, results are undefined");
+#endif
+ if (boundingRect.isNull())
+ return;
+
+ ensureData();
+ detach();
+
+ Q_D(QPainterPath);
+ d->elements.reserve(d->elements.size() + 13);
+
+ QPointF pts[12];
+ int point_count;
+ QPointF start = qt_curves_for_arc(boundingRect, 0, -360, pts, &point_count);
+
+ moveTo(start);
+ cubicTo(pts[0], pts[1], pts[2]); // 0 -> 270
+ cubicTo(pts[3], pts[4], pts[5]); // 270 -> 180
+ cubicTo(pts[6], pts[7], pts[8]); // 180 -> 90
+ cubicTo(pts[9], pts[10], pts[11]); // 90 - >0
+ d_func()->require_moveTo = true;
+}
+
+/*!
+ \fn void QPainterPath::addText(const QPointF &point, const QFont &font, const QString &text)
+
+ Adds the given \a text to this path as a set of closed subpaths
+ created from the \a font supplied. The subpaths are positioned so
+ that the left end of the text's baseline lies at the specified \a
+ point.
+
+ \table 100%
+ \row
+ \o \inlineimage qpainterpath-addtext.png
+ \o
+ \snippet doc/src/snippets/code/src_gui_painting_qpainterpath.cpp 6
+ \endtable
+
+ \sa QPainter::drawText(), {QPainterPath#Composing a
+ QPainterPath}{Composing a QPainterPath}
+*/
+void QPainterPath::addText(const QPointF &point, const QFont &f, const QString &text)
+{
+ if (text.isEmpty())
+ return;
+
+ ensureData();
+ detach();
+
+ QTextLayout layout(text, f);
+ layout.setCacheEnabled(true);
+ QTextEngine *eng = layout.engine();
+ layout.beginLayout();
+ QTextLine line = layout.createLine();
+ layout.endLayout();
+ const QScriptLine &sl = eng->lines[0];
+ if (!sl.length || !eng->layoutData)
+ return;
+
+ int nItems = eng->layoutData->items.size();
+
+ qreal x(point.x());
+ qreal y(point.y());
+
+ QVarLengthArray<int> visualOrder(nItems);
+ QVarLengthArray<uchar> levels(nItems);
+ for (int i = 0; i < nItems; ++i)
+ levels[i] = eng->layoutData->items[i].analysis.bidiLevel;
+ QTextEngine::bidiReorder(nItems, levels.data(), visualOrder.data());
+
+ for (int i = 0; i < nItems; ++i) {
+ int item = visualOrder[i];
+ QScriptItem &si = eng->layoutData->items[item];
+
+ if (si.analysis.flags < QScriptAnalysis::TabOrObject) {
+ QGlyphLayout glyphs = eng->shapedGlyphs(&si);
+ QFontEngine *fe = f.d->engineForScript(si.analysis.script);
+ Q_ASSERT(fe);
+ fe->addOutlineToPath(x, y, glyphs, this,
+ si.analysis.bidiLevel % 2
+ ? QTextItem::RenderFlags(QTextItem::RightToLeft)
+ : QTextItem::RenderFlags(0));
+
+ const qreal lw = fe->lineThickness().toReal();
+ if (f.d->underline) {
+ qreal pos = fe->underlinePosition().toReal();
+ addRect(x, y + pos, si.width.toReal(), lw);
+ }
+ if (f.d->overline) {
+ qreal pos = fe->ascent().toReal() + 1;
+ addRect(x, y - pos, si.width.toReal(), lw);
+ }
+ if (f.d->strikeOut) {
+ qreal pos = fe->ascent().toReal() / 3;
+ addRect(x, y - pos, si.width.toReal(), lw);
+ }
+ }
+ x += si.width.toReal();
+ }
+}
+
+/*!
+ \fn void QPainterPath::addPath(const QPainterPath &path)
+
+ Adds the given \a path to \e this path as a closed subpath.
+
+ \sa connectPath(), {QPainterPath#Composing a
+ QPainterPath}{Composing a QPainterPath}
+*/
+void QPainterPath::addPath(const QPainterPath &other)
+{
+ if (other.isEmpty())
+ return;
+
+ ensureData();
+ detach();
+
+ QPainterPathData *d = reinterpret_cast<QPainterPathData *>(d_func());
+ // Remove last moveto so we don't get multiple moveto's
+ if (d->elements.last().type == MoveToElement)
+ d->elements.remove(d->elements.size()-1);
+
+ // Locate where our own current subpath will start after the other path is added.
+ int cStart = d->elements.size() + other.d_func()->cStart;
+ d->elements += other.d_func()->elements;
+ d->cStart = cStart;
+
+ d->require_moveTo = other.d_func()->isClosed();
+}
+
+
+/*!
+ \fn void QPainterPath::connectPath(const QPainterPath &path)
+
+ Connects the given \a path to \e this path by adding a line from the
+ last element of this path to the first element of the given path.
+
+ \sa addPath(), {QPainterPath#Composing a QPainterPath}{Composing
+ a QPainterPath}
+*/
+void QPainterPath::connectPath(const QPainterPath &other)
+{
+ if (other.isEmpty())
+ return;
+
+ ensureData();
+ detach();
+
+ QPainterPathData *d = reinterpret_cast<QPainterPathData *>(d_func());
+ // Remove last moveto so we don't get multiple moveto's
+ if (d->elements.last().type == MoveToElement)
+ d->elements.remove(d->elements.size()-1);
+
+ // Locate where our own current subpath will start after the other path is added.
+ int cStart = d->elements.size() + other.d_func()->cStart;
+ int first = d->elements.size();
+ d->elements += other.d_func()->elements;
+
+ d->elements[first].type = LineToElement;
+
+ // avoid duplicate points
+ if (first > 0 && QPointF(d->elements[first]) == QPointF(d->elements[first - 1])) {
+ d->elements.remove(first--);
+ --cStart;
+ }
+
+ if (cStart != first)
+ d->cStart = cStart;
+}
+
+/*!
+ Adds the given \a region to the path by adding each rectangle in
+ the region as a separate closed subpath.
+
+ \sa addRect(), {QPainterPath#Composing a QPainterPath}{Composing
+ a QPainterPath}
+*/
+void QPainterPath::addRegion(const QRegion &region)
+{
+ ensureData();
+ detach();
+
+ QVector<QRect> rects = region.rects();
+ d_func()->elements.reserve(rects.size() * 5);
+ for (int i=0; i<rects.size(); ++i)
+ addRect(rects.at(i));
+}
+
+
+/*!
+ Returns the painter path's currently set fill rule.
+
+ \sa setFillRule()
+*/
+Qt::FillRule QPainterPath::fillRule() const
+{
+ return isEmpty() ? Qt::OddEvenFill : d_func()->fillRule;
+}
+
+/*!
+ \fn void QPainterPath::setFillRule(Qt::FillRule fillRule)
+
+ Sets the fill rule of the painter path to the given \a
+ fillRule. Qt provides two methods for filling paths:
+
+ \table
+ \row
+ \o \inlineimage qt-fillrule-oddeven.png
+ \o \inlineimage qt-fillrule-winding.png
+ \header
+ \o Qt::OddEvenFill (default)
+ \o Qt::WindingFill
+ \endtable
+
+ \sa fillRule()
+*/
+void QPainterPath::setFillRule(Qt::FillRule fillRule)
+{
+ ensureData();
+ detach();
+
+ d_func()->fillRule = fillRule;
+}
+
+#define QT_BEZIER_A(bezier, coord) 3 * (-bezier.coord##1 \
+ + 3*bezier.coord##2 \
+ - 3*bezier.coord##3 \
+ +bezier.coord##4)
+
+#define QT_BEZIER_B(bezier, coord) 6 * (bezier.coord##1 \
+ - 2*bezier.coord##2 \
+ + bezier.coord##3)
+
+#define QT_BEZIER_C(bezier, coord) 3 * (- bezier.coord##1 \
+ + bezier.coord##2)
+
+#define QT_BEZIER_CHECK_T(bezier, t) \
+ if (t >= 0 && t <= 1) { \
+ QPointF p(b.pointAt(t)); \
+ if (p.x() < minx) minx = p.x(); \
+ else if (p.x() > maxx) maxx = p.x(); \
+ if (p.y() < miny) miny = p.y(); \
+ else if (p.y() > maxy) maxy = p.y(); \
+ }
+
+
+static QRectF qt_painterpath_bezier_extrema(const QBezier &b)
+{
+ qreal minx, miny, maxx, maxy;
+
+ // initialize with end points
+ if (b.x1 < b.x4) {
+ minx = b.x1;
+ maxx = b.x4;
+ } else {
+ minx = b.x4;
+ maxx = b.x1;
+ }
+ if (b.y1 < b.y4) {
+ miny = b.y1;
+ maxy = b.y4;
+ } else {
+ miny = b.y4;
+ maxy = b.y1;
+ }
+
+ // Update for the X extrema
+ {
+ qreal ax = QT_BEZIER_A(b, x);
+ qreal bx = QT_BEZIER_B(b, x);
+ qreal cx = QT_BEZIER_C(b, x);
+ // specialcase quadratic curves to avoid div by zero
+ if (qFuzzyCompare(ax + 1, 1)) {
+
+ // linear curves are covered by initialization.
+ if (!qFuzzyCompare(bx + 1, 1)) {
+ qreal t = -cx / bx;
+ QT_BEZIER_CHECK_T(b, t);
+ }
+
+ } else {
+ const qreal tx = bx * bx - 4 * ax * cx;
+
+ if (tx >= 0) {
+ qreal temp = qSqrt(tx);
+ qreal rcp = 1 / (2 * ax);
+ qreal t1 = (-bx + temp) * rcp;
+ QT_BEZIER_CHECK_T(b, t1);
+
+ qreal t2 = (-bx - temp) * rcp;
+ QT_BEZIER_CHECK_T(b, t2);
+ }
+ }
+ }
+
+ // Update for the Y extrema
+ {
+ qreal ay = QT_BEZIER_A(b, y);
+ qreal by = QT_BEZIER_B(b, y);
+ qreal cy = QT_BEZIER_C(b, y);
+
+ // specialcase quadratic curves to avoid div by zero
+ if (qFuzzyCompare(ay + 1, 1)) {
+
+ // linear curves are covered by initialization.
+ if (!qFuzzyCompare(by + 1, 1)) {
+ qreal t = -cy / by;
+ QT_BEZIER_CHECK_T(b, t);
+ }
+
+ } else {
+ const qreal ty = by * by - 4 * ay * cy;
+
+ if (ty > 0) {
+ qreal temp = qSqrt(ty);
+ qreal rcp = 1 / (2 * ay);
+ qreal t1 = (-by + temp) * rcp;
+ QT_BEZIER_CHECK_T(b, t1);
+
+ qreal t2 = (-by - temp) * rcp;
+ QT_BEZIER_CHECK_T(b, t2);
+ }
+ }
+ }
+ return QRectF(minx, miny, maxx - minx, maxy - miny);
+}
+
+/*!
+ Returns the bounding rectangle of this painter path as a rectangle with
+ floating point precision.
+
+ \sa controlPointRect()
+*/
+QRectF QPainterPath::boundingRect() const
+{
+ if (!d_ptr)
+ return QRectF();
+ QPainterPathData *d = d_func();
+
+ if (d->dirtyBounds)
+ computeBoundingRect();
+ return d->bounds;
+}
+
+/*!
+ Returns the rectangle containing all the points and control points
+ in this path.
+
+ This function is significantly faster to compute than the exact
+ boundingRect(), and the returned rectangle is always a superset of
+ the rectangle returned by boundingRect().
+
+ \sa boundingRect()
+*/
+QRectF QPainterPath::controlPointRect() const
+{
+ if (!d_ptr)
+ return QRectF();
+ QPainterPathData *d = d_func();
+
+ if (d->dirtyControlBounds)
+ computeControlPointRect();
+ return d->controlBounds;
+}
+
+
+/*!
+ \fn bool QPainterPath::isEmpty() const
+
+ Returns true if either there are no elements in this path, or if the only
+ element is a MoveToElement; otherwise returns false.
+
+ \sa elementCount()
+*/
+
+/*!
+ Creates and returns a reversed copy of the path.
+
+ It is the order of the elements that is reversed: If a
+ QPainterPath is composed by calling the moveTo(), lineTo() and
+ cubicTo() functions in the specified order, the reversed copy is
+ composed by calling cubicTo(), lineTo() and moveTo().
+*/
+QPainterPath QPainterPath::toReversed() const
+{
+ Q_D(const QPainterPath);
+ QPainterPath rev;
+
+ if (isEmpty()) {
+ rev = *this;
+ return rev;
+ }
+
+ rev.moveTo(d->elements.at(d->elements.size()-1).x, d->elements.at(d->elements.size()-1).y);
+
+ for (int i=d->elements.size()-1; i>=1; --i) {
+ const QPainterPath::Element &elm = d->elements.at(i);
+ const QPainterPath::Element &prev = d->elements.at(i-1);
+ switch (elm.type) {
+ case LineToElement:
+ rev.lineTo(prev.x, prev.y);
+ break;
+ case MoveToElement:
+ rev.moveTo(prev.x, prev.y);
+ break;
+ case CurveToDataElement:
+ {
+ Q_ASSERT(i>=3);
+ const QPainterPath::Element &cp1 = d->elements.at(i-2);
+ const QPainterPath::Element &sp = d->elements.at(i-3);
+ Q_ASSERT(prev.type == CurveToDataElement);
+ Q_ASSERT(cp1.type == CurveToElement);
+ rev.cubicTo(prev.x, prev.y, cp1.x, cp1.y, sp.x, sp.y);
+ i -= 2;
+ break;
+ }
+ default:
+ Q_ASSERT(!"qt_reversed_path");
+ break;
+ }
+ }
+ //qt_debug_path(rev);
+ return rev;
+}
+
+/*!
+ Converts the path into a list of polygons using the QTransform
+ \a matrix, and returns the list.
+
+ This function creates one polygon for each subpath regardless of
+ intersecting subpaths (i.e. overlapping bounding rectangles). To
+ make sure that such overlapping subpaths are filled correctly, use
+ the toFillPolygons() function instead.
+
+ \sa toFillPolygons(), toFillPolygon(), {QPainterPath#QPainterPath
+ Conversion}{QPainterPath Conversion}
+*/
+QList<QPolygonF> QPainterPath::toSubpathPolygons(const QTransform &matrix) const
+{
+
+ Q_D(const QPainterPath);
+ QList<QPolygonF> flatCurves;
+ if (isEmpty())
+ return flatCurves;
+
+ QPolygonF current;
+ for (int i=0; i<elementCount(); ++i) {
+ const QPainterPath::Element &e = d->elements.at(i);
+ switch (e.type) {
+ case QPainterPath::MoveToElement:
+ if (current.size() > 1)
+ flatCurves += current;
+ current.clear();
+ current.reserve(16);
+ current += QPointF(e.x, e.y) * matrix;
+ break;
+ case QPainterPath::LineToElement:
+ current += QPointF(e.x, e.y) * matrix;
+ break;
+ case QPainterPath::CurveToElement: {
+ Q_ASSERT(d->elements.at(i+1).type == QPainterPath::CurveToDataElement);
+ Q_ASSERT(d->elements.at(i+2).type == QPainterPath::CurveToDataElement);
+ QBezier bezier = QBezier::fromPoints(QPointF(d->elements.at(i-1).x, d->elements.at(i-1).y) * matrix,
+ QPointF(e.x, e.y) * matrix,
+ QPointF(d->elements.at(i+1).x, d->elements.at(i+1).y) * matrix,
+ QPointF(d->elements.at(i+2).x, d->elements.at(i+2).y) * matrix);
+ bezier.addToPolygon(&current);
+ i+=2;
+ break;
+ }
+ case QPainterPath::CurveToDataElement:
+ Q_ASSERT(!"QPainterPath::toSubpathPolygons(), bad element type");
+ break;
+ }
+ }
+
+ if (current.size()>1)
+ flatCurves += current;
+
+ return flatCurves;
+}
+
+/*!
+ \overload
+ */
+QList<QPolygonF> QPainterPath::toSubpathPolygons(const QMatrix &matrix) const
+{
+ return toSubpathPolygons(QTransform(matrix));
+}
+
+static inline bool rect_intersects(const QRectF &r1, const QRectF &r2)
+{
+ return qMax(r1.left(), r2.left()) <= qMin(r1.right(), r2.right())
+ && qMax(r1.top(), r2.top()) <= qMin(r1.bottom(), r2.bottom());
+}
+
+/*!
+ Converts the path into a list of polygons using the
+ QTransform \a matrix, and returns the list.
+
+ The function differs from the toFillPolygon() function in that it
+ creates several polygons. It is provided because it is usually
+ faster to draw several small polygons than to draw one large
+ polygon, even though the total number of points drawn is the same.
+
+ The toFillPolygons() function differs from the toSubpathPolygons()
+ function in that it create only polygon for subpaths that have
+ overlapping bounding rectangles.
+
+ Like the toFillPolygon() function, this function uses a rewinding
+ technique to make sure that overlapping subpaths can be filled
+ using the correct fill rule. Note that rewinding inserts addition
+ lines in the polygons so the outline of the fill polygon does not
+ match the outline of the path.
+
+ \sa toSubpathPolygons(), toFillPolygon(),
+ {QPainterPath#QPainterPath Conversion}{QPainterPath Conversion}
+*/
+QList<QPolygonF> QPainterPath::toFillPolygons(const QTransform &matrix) const
+{
+
+ QList<QPolygonF> polys;
+
+ QList<QPolygonF> subpaths = toSubpathPolygons(matrix);
+ int count = subpaths.size();
+
+ if (count == 0)
+ return polys;
+
+ QList<QRectF> bounds;
+ for (int i=0; i<count; ++i)
+ bounds += subpaths.at(i).boundingRect();
+
+#ifdef QPP_FILLPOLYGONS_DEBUG
+ printf("QPainterPath::toFillPolygons, subpathCount=%d\n", count);
+ for (int i=0; i<bounds.size(); ++i)
+ qDebug() << " bounds" << i << bounds.at(i);
+#endif
+
+ QVector< QList<int> > isects;
+ isects.resize(count);
+
+ // find all intersections
+ for (int j=0; j<count; ++j) {
+ if (subpaths.at(j).size() <= 2)
+ continue;
+ QRectF cbounds = bounds.at(j);
+ for (int i=0; i<count; ++i) {
+ if (rect_intersects(cbounds, bounds.at(i))) {
+ isects[j] << i;
+ }
+ }
+ }
+
+#ifdef QPP_FILLPOLYGONS_DEBUG
+ printf("Intersections before flattening:\n");
+ for (int i = 0; i < count; ++i) {
+ printf("%d: ", i);
+ for (int j = 0; j < isects[i].size(); ++j) {
+ printf("%d ", isects[i][j]);
+ }
+ printf("\n");
+ }
+#endif
+
+ // flatten the sets of intersections
+ for (int i=0; i<count; ++i) {
+ const QList<int> &current_isects = isects.at(i);
+ for (int j=0; j<current_isects.size(); ++j) {
+ int isect_j = current_isects.at(j);
+ if (isect_j == i)
+ continue;
+ for (int k=0; k<isects[isect_j].size(); ++k) {
+ int isect_k = isects[isect_j][k];
+ if (isect_k != i && !isects.at(i).contains(isect_k)) {
+ isects[i] += isect_k;
+ }
+ }
+ isects[isect_j].clear();
+ }
+ }
+
+#ifdef QPP_FILLPOLYGONS_DEBUG
+ printf("Intersections after flattening:\n");
+ for (int i = 0; i < count; ++i) {
+ printf("%d: ", i);
+ for (int j = 0; j < isects[i].size(); ++j) {
+ printf("%d ", isects[i][j]);
+ }
+ printf("\n");
+ }
+#endif
+
+ // Join the intersected subpaths as rewinded polygons
+ for (int i=0; i<count; ++i) {
+ const QList<int> &subpath_list = isects[i];
+ if (!subpath_list.isEmpty()) {
+ QPolygonF buildUp;
+ for (int j=0; j<subpath_list.size(); ++j) {
+ const QPolygonF &subpath = subpaths.at(subpath_list.at(j));
+ buildUp += subpath;
+ if (!subpath.isClosed())
+ buildUp += subpath.first();
+ if (!buildUp.isClosed())
+ buildUp += buildUp.first();
+ }
+ polys += buildUp;
+ }
+ }
+
+ return polys;
+}
+
+/*!
+ \overload
+ */
+QList<QPolygonF> QPainterPath::toFillPolygons(const QMatrix &matrix) const
+{
+ return toFillPolygons(QTransform(matrix));
+}
+
+//same as qt_polygon_isect_line in qpolygon.cpp
+static void qt_painterpath_isect_line(const QPointF &p1,
+ const QPointF &p2,
+ const QPointF &pos,
+ int *winding)
+{
+ qreal x1 = p1.x();
+ qreal y1 = p1.y();
+ qreal x2 = p2.x();
+ qreal y2 = p2.y();
+ qreal y = pos.y();
+
+ int dir = 1;
+
+ if (qFuzzyCompare(y1, y2)) {
+ // ignore horizontal lines according to scan conversion rule
+ return;
+ } else if (y2 < y1) {
+ qreal x_tmp = x2; x2 = x1; x1 = x_tmp;
+ qreal y_tmp = y2; y2 = y1; y1 = y_tmp;
+ dir = -1;
+ }
+
+ if (y >= y1 && y < y2) {
+ qreal x = x1 + ((x2 - x1) / (y2 - y1)) * (y - y1);
+
+ // count up the winding number if we're
+ if (x<=pos.x()) {
+ (*winding) += dir;
+ }
+ }
+}
+
+static void qt_painterpath_isect_curve(const QBezier &bezier, const QPointF &pt,
+ int *winding)
+{
+ qreal y = pt.y();
+ qreal x = pt.x();
+ QRectF bounds = bezier.bounds();
+
+ // potential intersection, divide and try again...
+ // Please note that a sideeffect of the bottom exclusion is that
+ // horizontal lines are dropped, but this is correct according to
+ // scan conversion rules.
+ if (y >= bounds.y() && y < bounds.y() + bounds.height()) {
+
+ // hit lower limit... This is a rough threshold, but its a
+ // tradeoff between speed and precision.
+ const qreal lower_bound = qreal(.001);
+ if (bounds.width() < lower_bound && bounds.height() < lower_bound) {
+ // We make the assumption here that the curve starts to
+ // approximate a line after while (i.e. that it doesn't
+ // change direction drastically during its slope)
+ if (bezier.pt1().x() <= x) {
+ (*winding) += (bezier.pt4().y() > bezier.pt1().y() ? 1 : -1);
+ }
+ return;
+ }
+
+ // split curve and try again...
+ QBezier first_half, second_half;
+ bezier.split(&first_half, &second_half);
+ qt_painterpath_isect_curve(first_half, pt, winding);
+ qt_painterpath_isect_curve(second_half, pt, winding);
+ }
+}
+
+/*!
+ \fn bool QPainterPath::contains(const QPointF &point) const
+
+ Returns true if the given \a point is inside the path, otherwise
+ returns false.
+
+ \sa intersects()
+*/
+bool QPainterPath::contains(const QPointF &pt) const
+{
+ if (isEmpty())
+ return false;
+
+ QPainterPathData *d = d_func();
+
+ int winding_number = 0;
+
+ QPointF last_pt;
+ QPointF last_start;
+ for (int i=0; i<d->elements.size(); ++i) {
+ const Element &e = d->elements.at(i);
+
+ switch (e.type) {
+
+ case MoveToElement:
+ if (i > 0) // implicitly close all paths.
+ qt_painterpath_isect_line(last_pt, last_start, pt, &winding_number);
+ last_start = last_pt = e;
+ break;
+
+ case LineToElement:
+ qt_painterpath_isect_line(last_pt, e, pt, &winding_number);
+ last_pt = e;
+ break;
+
+ case CurveToElement:
+ {
+ const QPainterPath::Element &cp2 = d->elements.at(++i);
+ const QPainterPath::Element &ep = d->elements.at(++i);
+ qt_painterpath_isect_curve(QBezier::fromPoints(last_pt, e, cp2, ep),
+ pt, &winding_number);
+ last_pt = ep;
+
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ // implicitly close last subpath
+ if (last_pt != last_start)
+ qt_painterpath_isect_line(last_pt, last_start, pt, &winding_number);
+
+ return (d->fillRule == Qt::WindingFill
+ ? (winding_number != 0)
+ : ((winding_number % 2) != 0));
+}
+
+static bool qt_painterpath_isect_line_rect(qreal x1, qreal y1, qreal x2, qreal y2,
+ const QRectF &rect)
+{
+ qreal left = rect.left();
+ qreal right = rect.right();
+ qreal top = rect.top();
+ qreal bottom = rect.bottom();
+
+ enum { Left, Right, Top, Bottom };
+ // clip the lines, after cohen-sutherland, see e.g. http://www.nondot.org/~sabre/graphpro/line6.html
+ int p1 = ((x1 < left) << Left)
+ | ((x1 > right) << Right)
+ | ((y1 < top) << Top)
+ | ((y1 > bottom) << Bottom);
+ int p2 = ((x2 < left) << Left)
+ | ((x2 > right) << Right)
+ | ((y2 < top) << Top)
+ | ((y2 > bottom) << Bottom);
+
+ if (p1 & p2)
+ // completely inside
+ return false;
+
+ if (p1 | p2) {
+ qreal dx = x2 - x1;
+ qreal dy = y2 - y1;
+
+ // clip x coordinates
+ if (x1 < left) {
+ y1 += dy/dx * (left - x1);
+ x1 = left;
+ } else if (x1 > right) {
+ y1 -= dy/dx * (x1 - right);
+ x1 = right;
+ }
+ if (x2 < left) {
+ y2 += dy/dx * (left - x2);
+ x2 = left;
+ } else if (x2 > right) {
+ y2 -= dy/dx * (x2 - right);
+ x2 = right;
+ }
+
+ p1 = ((y1 < top) << Top)
+ | ((y1 > bottom) << Bottom);
+ p2 = ((y2 < top) << Top)
+ | ((y2 > bottom) << Bottom);
+
+ if (p1 & p2)
+ return false;
+
+ // clip y coordinates
+ if (y1 < top) {
+ x1 += dx/dy * (top - y1);
+ y1 = top;
+ } else if (y1 > bottom) {
+ x1 -= dx/dy * (y1 - bottom);
+ y1 = bottom;
+ }
+ if (y2 < top) {
+ x2 += dx/dy * (top - y2);
+ y2 = top;
+ } else if (y2 > bottom) {
+ x2 -= dx/dy * (y2 - bottom);
+ y2 = bottom;
+ }
+
+ p1 = ((x1 < left) << Left)
+ | ((x1 > right) << Right);
+ p2 = ((x2 < left) << Left)
+ | ((x2 > right) << Right);
+
+ if (p1 & p2)
+ return false;
+
+ return true;
+ }
+ return false;
+}
+
+static bool qt_isect_curve_horizontal(const QBezier &bezier, qreal y, qreal x1, qreal x2)
+{
+ QRectF bounds = bezier.bounds();
+
+ if (y >= bounds.top() && y < bounds.bottom()
+ && bounds.right() >= x1 && bounds.left() < x2) {
+ const qreal lower_bound = qreal(.01);
+ if (bounds.width() < lower_bound && bounds.height() < lower_bound)
+ return true;
+
+ QBezier first_half, second_half;
+ bezier.split(&first_half, &second_half);
+ if (qt_isect_curve_horizontal(first_half, y, x1, x2)
+ || qt_isect_curve_horizontal(second_half, y, x1, x2))
+ return true;
+ }
+ return false;
+}
+
+static bool qt_isect_curve_vertical(const QBezier &bezier, qreal x, qreal y1, qreal y2)
+{
+ QRectF bounds = bezier.bounds();
+
+ if (x >= bounds.left() && x < bounds.right()
+ && bounds.bottom() >= y1 && bounds.top() < y2) {
+ const qreal lower_bound = qreal(.01);
+ if (bounds.width() < lower_bound && bounds.height() < lower_bound)
+ return true;
+
+ QBezier first_half, second_half;
+ bezier.split(&first_half, &second_half);
+ if (qt_isect_curve_vertical(first_half, x, y1, y2)
+ || qt_isect_curve_vertical(second_half, x, y1, y2))
+ return true;
+ }
+ return false;
+}
+
+/*
+ Returns true if any lines or curves cross the four edges in of rect
+*/
+static bool qt_painterpath_check_crossing(const QPainterPath *path, const QRectF &rect)
+{
+ QPointF last_pt;
+ QPointF last_start;
+ for (int i=0; i<path->elementCount(); ++i) {
+ const QPainterPath::Element &e = path->elementAt(i);
+
+ switch (e.type) {
+
+ case QPainterPath::MoveToElement:
+ if (i > 0
+ && qFuzzyCompare(last_pt.x(), last_start.y())
+ && qFuzzyCompare(last_pt.y(), last_start.y())
+ && qt_painterpath_isect_line_rect(last_pt.x(), last_pt.y(),
+ last_start.x(), last_start.y(), rect))
+ return true;
+ last_start = last_pt = e;
+ break;
+
+ case QPainterPath::LineToElement:
+ if (qt_painterpath_isect_line_rect(last_pt.x(), last_pt.y(), e.x, e.y, rect))
+ return true;
+ last_pt = e;
+ break;
+
+ case QPainterPath::CurveToElement:
+ {
+ QPointF cp2 = path->elementAt(++i);
+ QPointF ep = path->elementAt(++i);
+ QBezier bezier = QBezier::fromPoints(last_pt, e, cp2, ep);
+ if (qt_isect_curve_horizontal(bezier, rect.top(), rect.left(), rect.right())
+ || qt_isect_curve_horizontal(bezier, rect.bottom(), rect.left(), rect.right())
+ || qt_isect_curve_vertical(bezier, rect.left(), rect.top(), rect.bottom())
+ || qt_isect_curve_vertical(bezier, rect.right(), rect.top(), rect.bottom()))
+ return true;
+ last_pt = ep;
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ // implicitly close last subpath
+ if (last_pt != last_start
+ && qt_painterpath_isect_line_rect(last_pt.x(), last_pt.y(),
+ last_start.x(), last_start.y(), rect))
+ return true;
+
+ return false;
+}
+
+/*!
+ \fn bool QPainterPath::intersects(const QRectF &rectangle) const
+
+ Returns true if any point in the given \a rectangle intersects the
+ path; otherwise returns false.
+
+ There is an intersection if any of the lines making up the
+ rectangle crosses a part of the path or if any part of the
+ rectangle overlaps with any area enclosed by the path. This
+ function respects the current fillRule to determine what is
+ considered inside the path.
+
+ \sa contains()
+*/
+bool QPainterPath::intersects(const QRectF &rect) const
+{
+ if (elementCount() == 1 && rect.contains(elementAt(0)))
+ return true;
+
+ if (isEmpty())
+ return false;
+
+ QRectF cp = controlPointRect();
+ QRectF rn = rect.normalized();
+
+ // QRectF::intersects returns false if one of the rects is a null rect
+ // which would happen for a painter path consisting of a vertical or
+ // horizontal line
+ if (qMax(rn.left(), cp.left()) > qMin(rn.right(), cp.right())
+ || qMax(rn.top(), cp.top()) > qMin(rn.bottom(), cp.bottom()))
+ return false;
+
+ // If any path element cross the rect its bound to be an intersection
+ if (qt_painterpath_check_crossing(this, rect))
+ return true;
+
+ if (contains(rect.center()))
+ return true;
+
+ Q_D(QPainterPath);
+
+ // Check if the rectangle surounds any subpath...
+ for (int i=0; i<d->elements.size(); ++i) {
+ const Element &e = d->elements.at(i);
+ if (e.type == QPainterPath::MoveToElement && rect.contains(e))
+ return true;
+ }
+
+ return false;
+}
+
+
+
+/*!
+ \fn bool QPainterPath::contains(const QRectF &rectangle) const
+
+ Returns true if the given \a rectangle is inside the path,
+ otherwise returns false.
+*/
+bool QPainterPath::contains(const QRectF &rect) const
+{
+ Q_D(QPainterPath);
+
+ // the path is empty or the control point rect doesn't completely
+ // cover the rectangle we abort stratight away.
+ if (isEmpty() || !controlPointRect().contains(rect))
+ return false;
+
+ // if there are intersections, chances are that the rect is not
+ // contained, except if we have winding rule, in which case it
+ // still might.
+ if (qt_painterpath_check_crossing(this, rect)) {
+ if (fillRule() == Qt::OddEvenFill) {
+ return false;
+ } else {
+ // Do some wague sampling in the winding case. This is not
+ // precise but it should mostly be good enough.
+ if (!contains(rect.topLeft()) ||
+ !contains(rect.topRight()) ||
+ !contains(rect.bottomRight()) ||
+ !contains(rect.bottomLeft()))
+ return false;
+ }
+ }
+
+ // If there exists a point inside that is not part of the path its
+ // because: rectangle lies completely outside path or a subpath
+ // excludes parts of the rectangle. Both cases mean that the rect
+ // is not contained
+ if (!contains(rect.center()))
+ return false;
+
+ // If there are any subpaths inside this rectangle we need to
+ // check if they are still contained as a result of the fill
+ // rule. This can only be the case for WindingFill though. For
+ // OddEvenFill the rect will never be contained if it surrounds a
+ // subpath. (the case where two subpaths are completely identical
+ // can be argued but we choose to neglect it).
+ for (int i=0; i<d->elements.size(); ++i) {
+ const Element &e = d->elements.at(i);
+ if (e.type == QPainterPath::MoveToElement && rect.contains(e)) {
+ if (fillRule() == Qt::OddEvenFill)
+ return false;
+
+ bool stop = false;
+ for (; !stop && i<d->elements.size(); ++i) {
+ const Element &el = d->elements.at(i);
+ switch (el.type) {
+ case MoveToElement:
+ stop = true;
+ break;
+ case LineToElement:
+ if (!contains(el))
+ return false;
+ break;
+ case CurveToElement:
+ if (!contains(d->elements.at(i+2)))
+ return false;
+ i += 2;
+ break;
+ default:
+ break;
+ }
+ }
+
+ // compensate for the last ++i in the inner for
+ --i;
+ }
+ }
+
+ return true;
+}
+
+static inline bool epsilonCompare(const QPointF &a, const QPointF &b, const QSizeF &epsilon)
+{
+ return qAbs(a.x() - b.x()) <= epsilon.width()
+ && qAbs(a.y() - b.y()) <= epsilon.height();
+}
+
+/*!
+ Returns true if this painterpath is equal to the given \a path.
+
+ Note that comparing paths may involve a per element comparison
+ which can be slow for complex paths.
+
+ \sa operator!=()
+*/
+
+bool QPainterPath::operator==(const QPainterPath &path) const
+{
+ QPainterPathData *d = reinterpret_cast<QPainterPathData *>(d_func());
+ if (path.d_func() == d)
+ return true;
+ else if (!d || !path.d_func())
+ return false;
+ else if (d->fillRule != path.d_func()->fillRule)
+ return false;
+ else if (d->elements.size() != path.d_func()->elements.size())
+ return false;
+
+ const qreal qt_epsilon = sizeof(qreal) == sizeof(double) ? 1e-12 : qreal(1e-5);
+
+ QSizeF epsilon = boundingRect().size();
+ epsilon.rwidth() *= qt_epsilon;
+ epsilon.rheight() *= qt_epsilon;
+
+ for (int i = 0; i < d->elements.size(); ++i)
+ if (d->elements.at(i).type != path.d_func()->elements.at(i).type
+ || !epsilonCompare(d->elements.at(i), path.d_func()->elements.at(i), epsilon))
+ return false;
+
+ return true;
+}
+
+/*!
+ Returns true if this painter path differs from the given \a path.
+
+ Note that comparing paths may involve a per element comparison
+ which can be slow for complex paths.
+
+ \sa operator==()
+*/
+
+bool QPainterPath::operator!=(const QPainterPath &path) const
+{
+ return !(*this==path);
+}
+
+/*!
+ \since 4.5
+
+ Returns the intersection of this path and the \a other path.
+
+ \sa intersected(), operator&=(), united(), operator|()
+*/
+QPainterPath QPainterPath::operator&(const QPainterPath &other) const
+{
+ return intersected(other);
+}
+
+/*!
+ \since 4.5
+
+ Returns the union of this path and the \a other path.
+
+ \sa united(), operator|=(), intersected(), operator&()
+*/
+QPainterPath QPainterPath::operator|(const QPainterPath &other) const
+{
+ return united(other);
+}
+
+/*!
+ \since 4.5
+
+ Returns the union of this path and the \a other path. This function is equivalent
+ to operator|().
+
+ \sa united(), operator+=(), operator-()
+*/
+QPainterPath QPainterPath::operator+(const QPainterPath &other) const
+{
+ return united(other);
+}
+
+/*!
+ \since 4.5
+
+ Subtracts the \a other path from a copy of this path, and returns the copy.
+
+ \sa subtracted(), operator-=(), operator+()
+*/
+QPainterPath QPainterPath::operator-(const QPainterPath &other) const
+{
+ return subtracted(other);
+}
+
+/*!
+ \since 4.5
+
+ Intersects this path with \a other and returns a reference to this path.
+
+ \sa intersected(), operator&(), operator|=()
+*/
+QPainterPath &QPainterPath::operator&=(const QPainterPath &other)
+{
+ return *this = (*this & other);
+}
+
+/*!
+ \since 4.5
+
+ Unites this path with \a other and returns a reference to this path.
+
+ \sa united(), operator|(), operator&=()
+*/
+QPainterPath &QPainterPath::operator|=(const QPainterPath &other)
+{
+ return *this = (*this | other);
+}
+
+/*!
+ \since 4.5
+
+ Unites this path with \a other, and returns a reference to this path. This
+ is equivalent to operator|=().
+
+ \sa united(), operator+(), operator-=()
+*/
+QPainterPath &QPainterPath::operator+=(const QPainterPath &other)
+{
+ return *this = (*this + other);
+}
+
+/*!
+ \since 4.5
+
+ Subtracts \a other from this path, and returns a reference to this
+ path.
+
+ \sa subtracted(), operator-(), operator+=()
+*/
+QPainterPath &QPainterPath::operator-=(const QPainterPath &other)
+{
+ return *this = (*this - other);
+}
+
+#ifndef QT_NO_DATASTREAM
+/*!
+ \fn QDataStream &operator<<(QDataStream &stream, const QPainterPath &path)
+ \relates QPainterPath
+
+ Writes the given painter \a path to the given \a stream, and
+ returns a reference to the \a stream.
+
+ \sa {Format of the QDataStream Operators}
+*/
+QDataStream &operator<<(QDataStream &s, const QPainterPath &p)
+{
+ if (p.isEmpty()) {
+ s << 0;
+ return s;
+ }
+
+ s << p.elementCount();
+ for (int i=0; i < p.d_func()->elements.size(); ++i) {
+ const QPainterPath::Element &e = p.d_func()->elements.at(i);
+ s << int(e.type);
+ s << double(e.x) << double(e.y);
+ }
+ s << p.d_func()->cStart;
+ s << int(p.d_func()->fillRule);
+ return s;
+}
+
+/*!
+ \fn QDataStream &operator>>(QDataStream &stream, QPainterPath &path)
+ \relates QPainterPath
+
+ Reads a painter path from the given \a stream into the specified \a path,
+ and returns a reference to the \a stream.
+
+ \sa {Format of the QDataStream Operators}
+*/
+QDataStream &operator>>(QDataStream &s, QPainterPath &p)
+{
+ int size;
+ s >> size;
+
+ if (size == 0)
+ return s;
+
+ p.ensureData(); // in case if p.d_func() == 0
+ if (p.d_func()->elements.size() == 1) {
+ Q_ASSERT(p.d_func()->elements.at(0).type == QPainterPath::MoveToElement);
+ p.d_func()->elements.clear();
+ }
+ p.d_func()->elements.reserve(p.d_func()->elements.size() + size);
+ for (int i=0; i<size; ++i) {
+ int type;
+ double x, y;
+ s >> type;
+ s >> x;
+ s >> y;
+ Q_ASSERT(type >= 0 && type <= 3);
+#ifndef QT_NO_DEBUG
+ if (qt_is_nan(x) || qt_is_nan(y))
+ qWarning("QDataStream::operator>>: Adding a NaN element to path, results are undefined");
+#endif
+ QPainterPath::Element elm = { x, y, QPainterPath::ElementType(type) };
+ p.d_func()->elements.append(elm);
+ }
+ s >> p.d_func()->cStart;
+ int fillRule;
+ s >> fillRule;
+ Q_ASSERT(fillRule == Qt::OddEvenFill || Qt::WindingFill);
+ p.d_func()->fillRule = Qt::FillRule(fillRule);
+ p.d_func()->dirtyBounds = true;
+ p.d_func()->dirtyControlBounds = true;
+ return s;
+}
+#endif
+
+
+/*******************************************************************************
+ * class QPainterPathStroker
+ */
+
+void qt_path_stroke_move_to(qfixed x, qfixed y, void *data)
+{
+ ((QPainterPath *) data)->moveTo(qt_fixed_to_real(x), qt_fixed_to_real(y));
+}
+
+void qt_path_stroke_line_to(qfixed x, qfixed y, void *data)
+{
+ ((QPainterPath *) data)->lineTo(qt_fixed_to_real(x), qt_fixed_to_real(y));
+}
+
+void qt_path_stroke_cubic_to(qfixed c1x, qfixed c1y,
+ qfixed c2x, qfixed c2y,
+ qfixed ex, qfixed ey,
+ void *data)
+{
+ ((QPainterPath *) data)->cubicTo(qt_fixed_to_real(c1x), qt_fixed_to_real(c1y),
+ qt_fixed_to_real(c2x), qt_fixed_to_real(c2y),
+ qt_fixed_to_real(ex), qt_fixed_to_real(ey));
+}
+
+/*!
+ \since 4.1
+ \class QPainterPathStroker
+ \ingroup multimedia
+
+ \brief The QPainterPathStroker class is used to generate fillable
+ outlines for a given painter path.
+
+ By calling the createStroke() function, passing a given
+ QPainterPath as argument, a new painter path representing the
+ outline of the given path is created. The newly created painter
+ path can then be filled to draw the original painter path's
+ outline.
+
+ You can control the various design aspects (width, cap styles,
+ join styles and dash pattern) of the outlining using the following
+ functions:
+
+ \list
+ \o setWidth()
+ \o setCapStyle()
+ \o setJoinStyle()
+ \o setDashPattern()
+ \endlist
+
+ The setDashPattern() function accepts both a Qt::PenStyle object
+ and a vector representation of the pattern as argument.
+
+ In addition you can specify a curve's threshold, controlling the
+ granularity with which a curve is drawn, using the
+ setCurveThreshold() function. The default threshold is a well
+ adjusted value (0.25), and normally you should not need to modify
+ it. However, you can make the curve's appearance smoother by
+ decreasing its value.
+
+ You can also control the miter limit for the generated outline
+ using the setMiterLimit() function. The miter limit describes how
+ far from each join the miter join can extend. The limit is
+ specified in the units of width so the pixelwise miter limit will
+ be \c {miterlimit * width}. This value is only used if the join
+ style is Qt::MiterJoin.
+
+ The painter path generated by the createStroke() function should
+ only be used for outlining the given painter path. Otherwise it
+ may cause unexpected behavior. Generated outlines also require the
+ Qt::WindingFill rule which is set by default.
+
+ \sa QPen, QBrush
+*/
+
+class QPainterPathStrokerPrivate
+{
+public:
+ QPainterPathStrokerPrivate()
+ : dashOffset(0)
+ {
+ stroker.setMoveToHook(qt_path_stroke_move_to);
+ stroker.setLineToHook(qt_path_stroke_line_to);
+ stroker.setCubicToHook(qt_path_stroke_cubic_to);
+ }
+
+ QStroker stroker;
+ QVector<qfixed> dashPattern;
+ qreal dashOffset;
+};
+
+/*!
+ Creates a new stroker.
+ */
+QPainterPathStroker::QPainterPathStroker()
+ : d_ptr(new QPainterPathStrokerPrivate)
+{
+}
+
+/*!
+ Destroys the stroker.
+*/
+QPainterPathStroker::~QPainterPathStroker()
+{
+ delete d_ptr;
+}
+
+
+/*!
+ Generates a new path that is a fillable area representing the
+ outline of the given \a path.
+
+ The various design aspects of the outline are based on the
+ stroker's properties: width(), capStyle(), joinStyle(),
+ dashPattern(), curveThreshold() and miterLimit().
+
+ The generated path should only be used for outlining the given
+ painter path. Otherwise it may cause unexpected
+ behavior. Generated outlines also require the Qt::WindingFill rule
+ which is set by default.
+*/
+QPainterPath QPainterPathStroker::createStroke(const QPainterPath &path) const
+{
+ QPainterPathStrokerPrivate *d = const_cast<QPainterPathStrokerPrivate *>(d_func());
+ QPainterPath stroke;
+ if (path.isEmpty())
+ return path;
+ if (d->dashPattern.isEmpty()) {
+ d->stroker.strokePath(path, &stroke, QTransform());
+ } else {
+ QDashStroker dashStroker(&d->stroker);
+ dashStroker.setDashPattern(d->dashPattern);
+ dashStroker.setDashOffset(d->dashOffset);
+ dashStroker.strokePath(path, &stroke, QTransform());
+ }
+ stroke.setFillRule(Qt::WindingFill);
+ return stroke;
+}
+
+/*!
+ Sets the width of the generated outline painter path to \a width.
+
+ The generated outlines will extend approximately 50% of \a width
+ to each side of the given input path's original outline.
+*/
+void QPainterPathStroker::setWidth(qreal width)
+{
+ Q_D(QPainterPathStroker);
+ if (width <= 0)
+ width = 1;
+ d->stroker.setStrokeWidth(qt_real_to_fixed(width));
+}
+
+/*!
+ Returns the width of the generated outlines.
+*/
+qreal QPainterPathStroker::width() const
+{
+ return qt_fixed_to_real(d_func()->stroker.strokeWidth());
+}
+
+
+/*!
+ Sets the cap style of the generated outlines to \a style. If a
+ dash pattern is set, each segment of the pattern is subject to the
+ cap \a style.
+*/
+void QPainterPathStroker::setCapStyle(Qt::PenCapStyle style)
+{
+ d_func()->stroker.setCapStyle(style);
+}
+
+
+/*!
+ Returns the cap style of the generated outlines.
+*/
+Qt::PenCapStyle QPainterPathStroker::capStyle() const
+{
+ return d_func()->stroker.capStyle();
+}
+
+/*!
+ Sets the join style of the generated outlines to \a style.
+*/
+void QPainterPathStroker::setJoinStyle(Qt::PenJoinStyle style)
+{
+ d_func()->stroker.setJoinStyle(style);
+}
+
+/*!
+ Returns the join style of the generated outlines.
+*/
+Qt::PenJoinStyle QPainterPathStroker::joinStyle() const
+{
+ return d_func()->stroker.joinStyle();
+}
+
+/*!
+ Sets the miter limit of the generated outlines to \a limit.
+
+ The miter limit describes how far from each join the miter join
+ can extend. The limit is specified in units of the currently set
+ width. So the pixelwise miter limit will be \c { miterlimit *
+ width}.
+
+ This value is only used if the join style is Qt::MiterJoin.
+*/
+void QPainterPathStroker::setMiterLimit(qreal limit)
+{
+ d_func()->stroker.setMiterLimit(qt_real_to_fixed(limit));
+}
+
+/*!
+ Returns the miter limit for the generated outlines.
+*/
+qreal QPainterPathStroker::miterLimit() const
+{
+ return qt_fixed_to_real(d_func()->stroker.miterLimit());
+}
+
+
+/*!
+ Specifies the curve flattening \a threshold, controlling the
+ granularity with which the generated outlines' curve is drawn.
+
+ The default threshold is a well adjusted value (0.25), and
+ normally you should not need to modify it. However, you can make
+ the curve's appearance smoother by decreasing its value.
+*/
+void QPainterPathStroker::setCurveThreshold(qreal threshold)
+{
+ d_func()->stroker.setCurveThreshold(qt_real_to_fixed(threshold));
+}
+
+/*!
+ Returns the curve flattening threshold for the generated
+ outlines.
+*/
+qreal QPainterPathStroker::curveThreshold() const
+{
+ return qt_fixed_to_real(d_func()->stroker.curveThreshold());
+}
+
+/*!
+ Sets the dash pattern for the generated outlines to \a style.
+*/
+void QPainterPathStroker::setDashPattern(Qt::PenStyle style)
+{
+ d_func()->dashPattern = QDashStroker::patternForStyle(style);
+}
+
+/*!
+ \overload
+
+ Sets the dash pattern for the generated outlines to \a
+ dashPattern. This function makes it possible to specify custom
+ dash patterns.
+
+ Each element in the vector contains the lengths of the dashes and spaces
+ in the stroke, beginning with the first dash in the first element, the
+ first space in the second element, and alternating between dashes and
+ spaces for each following pair of elements.
+
+ The vector can contain an odd number of elements, in which case the last
+ element will be extended by the length of the first element when the
+ pattern repeats.
+*/
+void QPainterPathStroker::setDashPattern(const QVector<qreal> &dashPattern)
+{
+ d_func()->dashPattern.clear();
+ for (int i=0; i<dashPattern.size(); ++i)
+ d_func()->dashPattern << qt_real_to_fixed(dashPattern.at(i));
+}
+
+/*!
+ Returns the dash pattern for the generated outlines.
+*/
+QVector<qreal> QPainterPathStroker::dashPattern() const
+{
+ return d_func()->dashPattern;
+}
+
+/*!
+ Returns the dash offset for the generated outlines.
+ */
+qreal QPainterPathStroker::dashOffset() const
+{
+ return d_func()->dashOffset;
+}
+
+/*!
+ Sets the dash offset for the generated outlines to \a offset.
+
+ See the documentation for QPen::setDashOffset() for a description of the
+ dash offset.
+ */
+void QPainterPathStroker::setDashOffset(qreal offset)
+{
+ d_func()->dashOffset = offset;
+}
+
+/*!
+ Converts the path into a polygon using the QTransform
+ \a matrix, and returns the polygon.
+
+ The polygon is created by first converting all subpaths to
+ polygons, then using a rewinding technique to make sure that
+ overlapping subpaths can be filled using the correct fill rule.
+
+ Note that rewinding inserts addition lines in the polygon so
+ the outline of the fill polygon does not match the outline of
+ the path.
+
+ \sa toSubpathPolygons(), toFillPolygons(),
+ {QPainterPath#QPainterPath Conversion}{QPainterPath Conversion}
+*/
+QPolygonF QPainterPath::toFillPolygon(const QTransform &matrix) const
+{
+
+ QList<QPolygonF> flats = toSubpathPolygons(matrix);
+ QPolygonF polygon;
+ if (flats.isEmpty())
+ return polygon;
+ QPointF first = flats.first().first();
+ for (int i=0; i<flats.size(); ++i) {
+ polygon += flats.at(i);
+ if (!flats.at(i).isClosed())
+ polygon += flats.at(i).first();
+ if (i > 0)
+ polygon += first;
+ }
+ return polygon;
+}
+
+/*!
+ \overload
+*/
+QPolygonF QPainterPath::toFillPolygon(const QMatrix &matrix) const
+{
+ return toFillPolygon(QTransform(matrix));
+}
+
+
+//derivative of the equation
+static inline qreal slopeAt(qreal t, qreal a, qreal b, qreal c, qreal d)
+{
+ return 3*t*t*(d - 3*c + 3*b - a) + 6*t*(c - 2*b + a) + 3*(b - a);
+}
+
+/*!
+ Returns the length of the current path.
+*/
+qreal QPainterPath::length() const
+{
+ Q_D(QPainterPath);
+ if (isEmpty())
+ return 0;
+
+ qreal len = 0;
+ for (int i=1; i<d->elements.size(); ++i) {
+ const Element &e = d->elements.at(i);
+
+ switch (e.type) {
+ case MoveToElement:
+ break;
+ case LineToElement:
+ {
+ len += QLineF(d->elements.at(i-1), e).length();
+ break;
+ }
+ case CurveToElement:
+ {
+ QBezier b = QBezier::fromPoints(d->elements.at(i-1),
+ e,
+ d->elements.at(i+1),
+ d->elements.at(i+2));
+ len += b.length();
+ i += 2;
+ break;
+ }
+ default:
+ break;
+ }
+ }
+ return len;
+}
+
+/*!
+ Returns percentage of the whole path at the specified length \a len.
+
+ Note that similarly to other percent methods, the percentage measurment
+ is not linear with regards to the length, if curves are present
+ in the path. When curves are present the percentage argument is mapped
+ to the t parameter of the Bezier equations.
+*/
+qreal QPainterPath::percentAtLength(qreal len) const
+{
+ Q_D(QPainterPath);
+ if (isEmpty() || len <= 0)
+ return 0;
+
+ qreal totalLength = length();
+ if (len > totalLength)
+ return 1;
+
+ qreal curLen = 0;
+ for (int i=1; i<d->elements.size(); ++i) {
+ const Element &e = d->elements.at(i);
+
+ switch (e.type) {
+ case MoveToElement:
+ break;
+ case LineToElement:
+ {
+ QLineF line(d->elements.at(i-1), e);
+ qreal llen = line.length();
+ curLen += llen;
+ if (curLen >= len) {
+ return len/totalLength ;
+ }
+
+ break;
+ }
+ case CurveToElement:
+ {
+ QBezier b = QBezier::fromPoints(d->elements.at(i-1),
+ e,
+ d->elements.at(i+1),
+ d->elements.at(i+2));
+ qreal blen = b.length();
+ qreal prevLen = curLen;
+ curLen += blen;
+
+ if (curLen >= len) {
+ qreal res = b.tAtLength(len - prevLen);
+ return (res * blen + prevLen)/totalLength;
+ }
+
+ i += 2;
+ break;
+ }
+ default:
+ break;
+ }
+ }
+
+ return 0;
+}
+
+static inline QBezier bezierAtT(const QPainterPath &path, qreal t, qreal *startingLength, qreal *bezierLength)
+{
+ *startingLength = 0;
+ if (t > 1)
+ return QBezier();
+
+ qreal curLen = 0;
+ qreal totalLength = path.length();
+
+ const int lastElement = path.elementCount() - 1;
+ for (int i=0; i <= lastElement; ++i) {
+ const QPainterPath::Element &e = path.elementAt(i);
+
+ switch (e.type) {
+ case QPainterPath::MoveToElement:
+ break;
+ case QPainterPath::LineToElement:
+ {
+ QLineF line(path.elementAt(i-1), e);
+ qreal llen = line.length();
+ curLen += llen;
+ if (i == lastElement || curLen/totalLength >= t) {
+ *bezierLength = llen;
+ QPointF a = path.elementAt(i-1);
+ QPointF delta = e - a;
+ return QBezier::fromPoints(a, a + delta / 3, a + 2 * delta / 3, e);
+ }
+ break;
+ }
+ case QPainterPath::CurveToElement:
+ {
+ QBezier b = QBezier::fromPoints(path.elementAt(i-1),
+ e,
+ path.elementAt(i+1),
+ path.elementAt(i+2));
+ qreal blen = b.length();
+ curLen += blen;
+
+ if (i + 2 == lastElement || curLen/totalLength >= t) {
+ *bezierLength = blen;
+ return b;
+ }
+
+ i += 2;
+ break;
+ }
+ default:
+ break;
+ }
+ *startingLength = curLen;
+ }
+ return QBezier();
+}
+
+/*!
+ Returns the point at at the percentage \a t of the current path.
+ The argument \a t has to be between 0 and 1.
+
+ Note that similarly to other percent methods, the percentage measurment
+ is not linear with regards to the length, if curves are present
+ in the path. When curves are present the percentage argument is mapped
+ to the t parameter of the Bezier equations.
+*/
+QPointF QPainterPath::pointAtPercent(qreal t) const
+{
+ if (t < 0 || t > 1) {
+ qWarning("QPainterPath::pointAtPercent accepts only values between 0 and 1");
+ return QPointF();
+ }
+
+ if (isEmpty())
+ return QPointF();
+
+ qreal totalLength = length();
+ qreal curLen = 0;
+ qreal bezierLen = 0;
+ QBezier b = bezierAtT(*this, t, &curLen, &bezierLen);
+ qreal realT = (totalLength * t - curLen) / bezierLen;
+
+ return b.pointAt(qBound(qreal(0), realT, qreal(1)));
+}
+
+/*!
+ Returns the angle of the path tangent at the percentage \a t.
+ The argument \a t has to be between 0 and 1.
+
+ Positive values for the angles mean counter-clockwise while negative values
+ mean the clockwise direction. Zero degrees is at the 3 o'clock position.
+
+ Note that similarly to the other percent methods, the percentage measurment
+ is not linear with regards to the length if curves are present
+ in the path. When curves are present the percentage argument is mapped
+ to the t parameter of the Bezier equations.
+*/
+qreal QPainterPath::angleAtPercent(qreal t) const
+{
+ if (t < 0 || t > 1) {
+ qWarning("QPainterPath::angleAtPercent accepts only values between 0 and 1");
+ return 0;
+ }
+
+ qreal totalLength = length();
+ qreal curLen = 0;
+ qreal bezierLen = 0;
+ QBezier bez = bezierAtT(*this, t, &curLen, &bezierLen);
+ qreal realT = (totalLength * t - curLen) / bezierLen;
+
+ qreal m1 = slopeAt(realT, bez.x1, bez.x2, bez.x3, bez.x4);
+ qreal m2 = slopeAt(realT, bez.y1, bez.y2, bez.y3, bez.y4);
+
+ return QLineF(0, 0, m1, m2).angle();
+}
+
+#if defined(Q_OS_WINCE)
+#pragma warning( disable : 4056 4756 )
+#endif
+
+/*!
+ Returns the slope of the path at the percentage \a t. The
+ argument \a t has to be between 0 and 1.
+
+ Note that similarly to other percent methods, the percentage measurment
+ is not linear with regards to the length, if curves are present
+ in the path. When curves are present the percentage argument is mapped
+ to the t parameter of the Bezier equations.
+*/
+qreal QPainterPath::slopeAtPercent(qreal t) const
+{
+ if (t < 0 || t > 1) {
+ qWarning("QPainterPath::slopeAtPercent accepts only values between 0 and 1");
+ return 0;
+ }
+
+ qreal totalLength = length();
+ qreal curLen = 0;
+ qreal bezierLen = 0;
+ QBezier bez = bezierAtT(*this, t, &curLen, &bezierLen);
+ qreal realT = (totalLength * t - curLen) / bezierLen;
+
+ qreal m1 = slopeAt(realT, bez.x1, bez.x2, bez.x3, bez.x4);
+ qreal m2 = slopeAt(realT, bez.y1, bez.y2, bez.y3, bez.y4);
+ //tangent line
+ qreal slope = 0;
+
+#define SIGN(x) ((x < 0)?-1:1)
+ if (m1)
+ slope = m2/m1;
+ else {
+ //windows doesn't define INFINITY :(
+#ifdef INFINITY
+ slope = INFINITY*SIGN(m2);
+#else
+ if (sizeof(qreal) == sizeof(double)) {
+ return 1.79769313486231570e+308;
+ } else {
+ return ((qreal)3.40282346638528860e+38);
+ }
+#endif
+ }
+
+ return slope;
+}
+
+/*!
+ \since 4.4
+
+ Adds the given rectangle \a rect with rounded corners to the path.
+
+ The \a xRadius and \a yRadius arguments specify the radii of
+ the ellipses defining the corners of the rounded rectangle.
+ When \a mode is Qt::RelativeSize, \a xRadius and
+ \a yRadius are specified in percentage of half the rectangle's
+ width and height respectively, and should be in the range 0.0 to 100.0.
+
+ \sa addRect()
+*/
+void QPainterPath::addRoundedRect(const QRectF &rect, qreal xRadius, qreal yRadius,
+ Qt::SizeMode mode)
+{
+ QRectF r = rect.normalized();
+
+ if (r.isNull())
+ return;
+
+ if (mode == Qt::AbsoluteSize) {
+ qreal w = r.width() / 2;
+ qreal h = r.height() / 2;
+
+ if (w == 0) {
+ xRadius = 0;
+ } else {
+ xRadius = 100 * qMin(xRadius, w) / w;
+ }
+ if (h == 0) {
+ yRadius = 0;
+ } else {
+ yRadius = 100 * qMin(yRadius, h) / h;
+ }
+ } else {
+ if (xRadius > 100) // fix ranges
+ xRadius = 100;
+
+ if (yRadius > 100)
+ yRadius = 100;
+ }
+
+ if (xRadius <= 0 || yRadius <= 0) { // add normal rectangle
+ addRect(r);
+ return;
+ }
+
+ qreal x = r.x();
+ qreal y = r.y();
+ qreal w = r.width();
+ qreal h = r.height();
+ qreal rxx2 = w*xRadius/100;
+ qreal ryy2 = h*yRadius/100;
+
+ ensureData();
+ detach();
+
+ arcMoveTo(x, y, rxx2, ryy2, 90);
+ arcTo(x, y, rxx2, ryy2, 90, 90);
+ arcTo(x, y+h-ryy2, rxx2, ryy2, 2*90, 90);
+ arcTo(x+w-rxx2, y+h-ryy2, rxx2, ryy2, 3*90, 90);
+ arcTo(x+w-rxx2, y, rxx2, ryy2, 0, 90);
+ closeSubpath();
+
+ d_func()->require_moveTo = true;
+}
+
+/*!
+ \fn void QPainterPath::addRoundedRect(qreal x, qreal y, qreal w, qreal h, qreal xRadius, qreal yRadius, Qt::SizeMode mode = Qt::AbsoluteSize);
+ \since 4.4
+ \overload
+
+ Adds the given rectangle \a x, \a y, \a w, \a h with rounded corners to the path.
+ */
+
+/*!
+ \obsolete
+
+ Adds a rectangle \a r with rounded corners to the path.
+
+ The \a xRnd and \a yRnd arguments specify how rounded the corners
+ should be. 0 is angled corners, 99 is maximum roundedness.
+
+ \sa addRoundedRect()
+*/
+void QPainterPath::addRoundRect(const QRectF &r, int xRnd, int yRnd)
+{
+ if(xRnd >= 100) // fix ranges
+ xRnd = 99;
+ if(yRnd >= 100)
+ yRnd = 99;
+ if(xRnd <= 0 || yRnd <= 0) { // add normal rectangle
+ addRect(r);
+ return;
+ }
+
+ QRectF rect = r.normalized();
+
+ if (rect.isNull())
+ return;
+
+ qreal x = rect.x();
+ qreal y = rect.y();
+ qreal w = rect.width();
+ qreal h = rect.height();
+ qreal rxx2 = w*xRnd/100;
+ qreal ryy2 = h*yRnd/100;
+
+ ensureData();
+ detach();
+
+ arcMoveTo(x, y, rxx2, ryy2, 90);
+ arcTo(x, y, rxx2, ryy2, 90, 90);
+ arcTo(x, y+h-ryy2, rxx2, ryy2, 2*90, 90);
+ arcTo(x+w-rxx2, y+h-ryy2, rxx2, ryy2, 3*90, 90);
+ arcTo(x+w-rxx2, y, rxx2, ryy2, 0, 90);
+ closeSubpath();
+
+ d_func()->require_moveTo = true;
+}
+
+/*!
+ \obsolete
+
+ \fn bool QPainterPath::addRoundRect(const QRectF &rect, int roundness);
+ \since 4.3
+ \overload
+
+ Adds a rounded rectangle, \a rect, to the path.
+
+ The \a roundness argument specifies uniform roundness for the
+ rectangle. Vertical and horizontal roundness factors will be
+ adjusted accordingly to act uniformly around both axes. Use this
+ method if you want a rectangle equally rounded across both the X and
+ Y axis.
+
+ \sa addRoundedRect()
+*/
+
+/*!
+ \obsolete
+
+ \fn void QPainterPath::addRoundRect(qreal x, qreal y, qreal w, qreal h, int xRnd, int yRnd);
+ \overload
+
+ Adds a rectangle with rounded corners to the path. The rectangle
+ is constructed from \a x, \a y, and the width and height \a w
+ and \a h.
+
+ The \a xRnd and \a yRnd arguments specify how rounded the corners
+ should be. 0 is angled corners, 99 is maximum roundedness.
+
+ \sa addRoundedRect()
+ */
+
+/*!
+ \obsolete
+
+ \fn bool QPainterPath::addRoundRect(qreal x, qreal y, qreal width, qreal height, int roundness);
+ \since 4.3
+ \overload
+
+ Adds a rounded rectangle to the path, defined by the coordinates \a
+ x and \a y with the specified \a width and \a height.
+
+ The \a roundness argument specifies uniform roundness for the
+ rectangle. Vertical and horizontal roundness factors will be
+ adjusted accordingly to act uniformly around both axes. Use this
+ method if you want a rectangle equally rounded across both the X and
+ Y axis.
+
+ \sa addRoundedRect()
+*/
+
+/*!
+ \since 4.3
+
+ Returns a path which is the union of this path's fill area and \a p's fill area.
+
+ Set operations on paths will treat the paths as areas. Non-closed
+ paths will be treated as implicitly closed.
+
+ \sa intersected(), subtracted(), subtractedInverted()
+*/
+QPainterPath QPainterPath::united(const QPainterPath &p) const
+{
+ if (isEmpty() || p.isEmpty())
+ return isEmpty() ? p : *this;
+ QPathClipper clipper(*this, p);
+ return clipper.clip(QPathClipper::BoolOr);
+}
+
+/*!
+ \since 4.3
+
+ Returns a path which is the intersection of this path's fill area and \a p's fill area.
+*/
+QPainterPath QPainterPath::intersected(const QPainterPath &p) const
+{
+ if (isEmpty() || p.isEmpty())
+ return QPainterPath();
+ QPathClipper clipper(*this, p);
+ return clipper.clip(QPathClipper::BoolAnd);
+}
+
+/*!
+ \since 4.3
+
+ Returns a path which is \a p's fill area subtracted from this path's fill area.
+
+ Set operations on paths will treat the paths as areas. Non-closed
+ paths will be treated as implicitly closed.
+
+*/
+QPainterPath QPainterPath::subtracted(const QPainterPath &p) const
+{
+ if (isEmpty() || p.isEmpty())
+ return *this;
+ QPathClipper clipper(*this, p);
+ return clipper.clip(QPathClipper::BoolSub);
+}
+
+/*!
+ \since 4.3
+ \obsolete
+
+ Use subtracted() instead.
+
+ \sa subtracted()
+*/
+QPainterPath QPainterPath::subtractedInverted(const QPainterPath &p) const
+{
+ return p.subtracted(*this);
+}
+
+/*!
+ \since 4.4
+
+ Returns a simplified version of this path. This implies merging all subpaths that intersect,
+ and returning a path containing no intersecting edges. Consecutive parallel lines will also
+ be merged. The simplified path will always use the default fill rule, Qt::OddEvenFill.
+*/
+QPainterPath QPainterPath::simplified() const
+{
+ if(isEmpty())
+ return *this;
+ QPathClipper clipper(*this, QPainterPath());
+ return clipper.clip(QPathClipper::Simplify);
+}
+
+/*!
+ \since 4.3
+
+ Returns true if the current path intersects at any point the given path \a p.
+ Also returns true if the current path contains or is contained by any part of \a p.
+
+ Set operations on paths will treat the paths as areas. Non-closed
+ paths will be treated as implicitly closed.
+
+ \sa contains()
+ */
+bool QPainterPath::intersects(const QPainterPath &p) const
+{
+ if (p.elementCount() == 1)
+ return contains(p.elementAt(0));
+ if (isEmpty() || p.isEmpty())
+ return false;
+ QPathClipper clipper(*this, p);
+ return clipper.intersect();
+}
+
+/*!
+ \since 4.3
+
+ Returns true if the given path \a p is contained within
+ the current path. Returns false if any edges of the current path and
+ \a p intersect.
+
+ Set operations on paths will treat the paths as areas. Non-closed
+ paths will be treated as implicitly closed.
+
+ \sa intersects()
+ */
+bool QPainterPath::contains(const QPainterPath &p) const
+{
+ if (p.elementCount() == 1)
+ return contains(p.elementAt(0));
+ if (isEmpty() || p.isEmpty())
+ return false;
+ QPathClipper clipper(*this, p);
+ return clipper.contains();
+}
+
+void QPainterPath::setDirty(bool dirty)
+{
+ d_func()->dirtyBounds = dirty;
+ d_func()->dirtyControlBounds = dirty;
+}
+
+void QPainterPath::computeBoundingRect() const
+{
+ QPainterPathData *d = d_func();
+ d->dirtyBounds = false;
+ if (!d_ptr) {
+ d->bounds = QRect();
+ return;
+ }
+
+ qreal minx, maxx, miny, maxy;
+ minx = maxx = d->elements.at(0).x;
+ miny = maxy = d->elements.at(0).y;
+ for (int i=1; i<d->elements.size(); ++i) {
+ const Element &e = d->elements.at(i);
+
+ switch (e.type) {
+ case MoveToElement:
+ case LineToElement:
+ if (e.x > maxx) maxx = e.x;
+ else if (e.x < minx) minx = e.x;
+ if (e.y > maxy) maxy = e.y;
+ else if (e.y < miny) miny = e.y;
+ break;
+ case CurveToElement:
+ {
+ QBezier b = QBezier::fromPoints(d->elements.at(i-1),
+ e,
+ d->elements.at(i+1),
+ d->elements.at(i+2));
+ QRectF r = qt_painterpath_bezier_extrema(b);
+ qreal right = r.right();
+ qreal bottom = r.bottom();
+ if (r.x() < minx) minx = r.x();
+ if (right > maxx) maxx = right;
+ if (r.y() < miny) miny = r.y();
+ if (bottom > maxy) maxy = bottom;
+ i += 2;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ d->bounds = QRectF(minx, miny, maxx - minx, maxy - miny);
+}
+
+
+void QPainterPath::computeControlPointRect() const
+{
+ QPainterPathData *d = d_func();
+ d->dirtyControlBounds = false;
+ if (!d_ptr) {
+ d->controlBounds = QRect();
+ return;
+ }
+
+ qreal minx, maxx, miny, maxy;
+ minx = maxx = d->elements.at(0).x;
+ miny = maxy = d->elements.at(0).y;
+ for (int i=1; i<d->elements.size(); ++i) {
+ const Element &e = d->elements.at(i);
+ if (e.x > maxx) maxx = e.x;
+ else if (e.x < minx) minx = e.x;
+ if (e.y > maxy) maxy = e.y;
+ else if (e.y < miny) miny = e.y;
+ }
+ d->controlBounds = QRectF(minx, miny, maxx - minx, maxy - miny);
+}
+
+#ifndef QT_NO_DEBUG_STREAM
+QDebug operator<<(QDebug s, const QPainterPath &p)
+{
+ s.nospace() << "QPainterPath: Element count=" << p.elementCount() << endl;
+ const char *types[] = {"MoveTo", "LineTo", "CurveTo", "CurveToData"};
+ for (int i=0; i<p.elementCount(); ++i) {
+ s.nospace() << " -> " << types[p.elementAt(i).type] << "(x=" << p.elementAt(i).x << ", y=" << p.elementAt(i).y << ")" << endl;
+
+ }
+ return s;
+}
+#endif
+
+QT_END_NAMESPACE
diff --git a/src/gui/painting/qpainterpath.h b/src/gui/painting/qpainterpath.h
new file mode 100644
index 0000000000..88fb19d2a9
--- /dev/null
+++ b/src/gui/painting/qpainterpath.h
@@ -0,0 +1,409 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QPAINTERPATH_H
+#define QPAINTERPATH_H
+
+#include <QtGui/qmatrix.h>
+#include <QtCore/qglobal.h>
+#include <QtCore/qrect.h>
+#include <QtCore/qline.h>
+#include <QtCore/qvector.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+class QFont;
+class QPainterPathPrivate;
+class QPainterPathData;
+class QPainterPathStrokerPrivate;
+class QPolygonF;
+class QRegion;
+class QVectorPath;
+
+class Q_GUI_EXPORT QPainterPath
+{
+public:
+ enum ElementType {
+ MoveToElement,
+ LineToElement,
+ CurveToElement,
+ CurveToDataElement
+ };
+
+ class Element {
+ public:
+ qreal x;
+ qreal y;
+ ElementType type;
+
+ bool isMoveTo() const { return type == MoveToElement; }
+ bool isLineTo() const { return type == LineToElement; }
+ bool isCurveTo() const { return type == CurveToElement; }
+
+ operator QPointF () const { return QPointF(x, y); }
+
+ bool operator==(const Element &e) const { return qFuzzyCompare(x, e.x)
+ && qFuzzyCompare(y, e.y) && type == e.type; }
+ inline bool operator!=(const Element &e) const { return !operator==(e); }
+ };
+
+ QPainterPath();
+ explicit QPainterPath(const QPointF &startPoint);
+ QPainterPath(const QPainterPath &other);
+ QPainterPath &operator=(const QPainterPath &other);
+ ~QPainterPath();
+
+ void closeSubpath();
+
+ void moveTo(const QPointF &p);
+ inline void moveTo(qreal x, qreal y);
+
+ void lineTo(const QPointF &p);
+ inline void lineTo(qreal x, qreal y);
+
+ void arcMoveTo(const QRectF &rect, qreal angle);
+ inline void arcMoveTo(qreal x, qreal y, qreal w, qreal h, qreal angle);
+
+ void arcTo(const QRectF &rect, qreal startAngle, qreal arcLength);
+ inline void arcTo(qreal x, qreal y, qreal w, qreal h, qreal startAngle, qreal arcLength);
+
+ void cubicTo(const QPointF &ctrlPt1, const QPointF &ctrlPt2, const QPointF &endPt);
+ inline void cubicTo(qreal ctrlPt1x, qreal ctrlPt1y, qreal ctrlPt2x, qreal ctrlPt2y,
+ qreal endPtx, qreal endPty);
+ void quadTo(const QPointF &ctrlPt, const QPointF &endPt);
+ inline void quadTo(qreal ctrlPtx, qreal ctrlPty, qreal endPtx, qreal endPty);
+
+ QPointF currentPosition() const;
+
+ void addRect(const QRectF &rect);
+ inline void addRect(qreal x, qreal y, qreal w, qreal h);
+ void addEllipse(const QRectF &rect);
+ inline void addEllipse(qreal x, qreal y, qreal w, qreal h);
+ inline void addEllipse(const QPointF &center, qreal rx, qreal ry);
+ void addPolygon(const QPolygonF &polygon);
+ void addText(const QPointF &point, const QFont &f, const QString &text);
+ inline void addText(qreal x, qreal y, const QFont &f, const QString &text);
+ void addPath(const QPainterPath &path);
+ void addRegion(const QRegion &region);
+
+ void addRoundedRect(const QRectF &rect, qreal xRadius, qreal yRadius,
+ Qt::SizeMode mode = Qt::AbsoluteSize);
+ inline void addRoundedRect(qreal x, qreal y, qreal w, qreal h,
+ qreal xRadius, qreal yRadius,
+ Qt::SizeMode mode = Qt::AbsoluteSize);
+
+ void addRoundRect(const QRectF &rect, int xRnd, int yRnd);
+ inline void addRoundRect(qreal x, qreal y, qreal w, qreal h,
+ int xRnd, int yRnd);
+ inline void addRoundRect(const QRectF &rect, int roundness);
+ inline void addRoundRect(qreal x, qreal y, qreal w, qreal h,
+ int roundness);
+
+ void connectPath(const QPainterPath &path);
+
+ bool contains(const QPointF &pt) const;
+ bool contains(const QRectF &rect) const;
+ bool intersects(const QRectF &rect) const;
+
+ QRectF boundingRect() const;
+ QRectF controlPointRect() const;
+
+ Qt::FillRule fillRule() const;
+ void setFillRule(Qt::FillRule fillRule);
+
+ inline bool isEmpty() const;
+
+ QPainterPath toReversed() const;
+ QList<QPolygonF> toSubpathPolygons(const QMatrix &matrix = QMatrix()) const;
+ QList<QPolygonF> toFillPolygons(const QMatrix &matrix = QMatrix()) const;
+ QPolygonF toFillPolygon(const QMatrix &matrix = QMatrix()) const;
+ QList<QPolygonF> toSubpathPolygons(const QTransform &matrix) const;
+ QList<QPolygonF> toFillPolygons(const QTransform &matrix) const;
+ QPolygonF toFillPolygon(const QTransform &matrix) const;
+
+ inline int elementCount() const;
+ inline const QPainterPath::Element &elementAt(int i) const;
+ inline void setElementPositionAt(int i, qreal x, qreal y);
+
+ qreal length() const;
+ qreal percentAtLength(qreal t) const;
+ QPointF pointAtPercent(qreal t) const;
+ qreal angleAtPercent(qreal t) const;
+ qreal slopeAtPercent(qreal t) const;
+
+ bool intersects(const QPainterPath &p) const;
+ bool contains(const QPainterPath &p) const;
+ QPainterPath united(const QPainterPath &r) const;
+ QPainterPath intersected(const QPainterPath &r) const;
+ QPainterPath subtracted(const QPainterPath &r) const;
+ QPainterPath subtractedInverted(const QPainterPath &r) const;
+
+ QPainterPath simplified() const;
+
+ bool operator==(const QPainterPath &other) const;
+ bool operator!=(const QPainterPath &other) const;
+
+ QPainterPath operator&(const QPainterPath &other) const;
+ QPainterPath operator|(const QPainterPath &other) const;
+ QPainterPath operator+(const QPainterPath &other) const;
+ QPainterPath operator-(const QPainterPath &other) const;
+ QPainterPath &operator&=(const QPainterPath &other);
+ QPainterPath &operator|=(const QPainterPath &other);
+ QPainterPath &operator+=(const QPainterPath &other);
+ QPainterPath &operator-=(const QPainterPath &other);
+
+private:
+ QPainterPathPrivate *d_ptr;
+
+ inline void ensureData() { if (!d_ptr) ensureData_helper(); }
+ void ensureData_helper();
+ inline void detach();
+ void detach_helper();
+ void setDirty(bool);
+ void computeBoundingRect() const;
+ void computeControlPointRect() const;
+
+ QPainterPathData *d_func() const { return reinterpret_cast<QPainterPathData *>(d_ptr); }
+
+ friend class QPainterPathData;
+ friend class QPainterPathStroker;
+ friend class QPainterPathStrokerPrivate;
+ friend class QMatrix;
+ friend class QTransform;
+ friend Q_GUI_EXPORT const QVectorPath &qtVectorPathForPath(const QPainterPath &);
+
+#ifndef QT_NO_DATASTREAM
+ friend Q_GUI_EXPORT QDataStream &operator<<(QDataStream &, const QPainterPath &);
+ friend Q_GUI_EXPORT QDataStream &operator>>(QDataStream &, QPainterPath &);
+#endif
+};
+
+class QPainterPathPrivate
+{
+public:
+ friend class QPainterPath;
+ friend class QPainterPathData;
+ friend class QPainterPathStroker;
+ friend class QPainterPathStrokerPrivate;
+ friend class QMatrix;
+ friend class QTransform;
+#ifndef QT_NO_DATASTREAM
+ friend Q_GUI_EXPORT QDataStream &operator<<(QDataStream &, const QPainterPath &);
+ friend Q_GUI_EXPORT QDataStream &operator>>(QDataStream &, QPainterPath &);
+#endif
+private:
+ QAtomicInt ref;
+ QVector<QPainterPath::Element> elements;
+};
+
+Q_DECLARE_TYPEINFO(QPainterPath::Element, Q_PRIMITIVE_TYPE);
+
+#ifndef QT_NO_DATASTREAM
+Q_GUI_EXPORT QDataStream &operator<<(QDataStream &, const QPainterPath &);
+Q_GUI_EXPORT QDataStream &operator>>(QDataStream &, QPainterPath &);
+#endif
+
+class Q_GUI_EXPORT QPainterPathStroker
+{
+ Q_DECLARE_PRIVATE(QPainterPathStroker)
+public:
+ QPainterPathStroker();
+ ~QPainterPathStroker();
+
+ void setWidth(qreal width);
+ qreal width() const;
+
+ void setCapStyle(Qt::PenCapStyle style);
+ Qt::PenCapStyle capStyle() const;
+
+ void setJoinStyle(Qt::PenJoinStyle style);
+ Qt::PenJoinStyle joinStyle() const;
+
+ void setMiterLimit(qreal length);
+ qreal miterLimit() const;
+
+ void setCurveThreshold(qreal threshold);
+ qreal curveThreshold() const;
+
+ void setDashPattern(Qt::PenStyle);
+ void setDashPattern(const QVector<qreal> &dashPattern);
+ QVector<qreal> dashPattern() const;
+
+ void setDashOffset(qreal offset);
+ qreal dashOffset() const;
+
+ QPainterPath createStroke(const QPainterPath &path) const;
+
+private:
+ QPainterPathStrokerPrivate *d_ptr;
+};
+
+inline void QPainterPath::moveTo(qreal x, qreal y)
+{
+ moveTo(QPointF(x, y));
+}
+
+inline void QPainterPath::lineTo(qreal x, qreal y)
+{
+ lineTo(QPointF(x, y));
+}
+
+inline void QPainterPath::arcTo(qreal x, qreal y, qreal w, qreal h, qreal startAngle, qreal arcLenght)
+{
+ arcTo(QRectF(x, y, w, h), startAngle, arcLenght);
+}
+
+inline void QPainterPath::arcMoveTo(qreal x, qreal y, qreal w, qreal h, qreal angle)
+{
+ arcMoveTo(QRectF(x, y, w, h), angle);
+}
+
+inline void QPainterPath::cubicTo(qreal ctrlPt1x, qreal ctrlPt1y, qreal ctrlPt2x, qreal ctrlPt2y,
+ qreal endPtx, qreal endPty)
+{
+ cubicTo(QPointF(ctrlPt1x, ctrlPt1y), QPointF(ctrlPt2x, ctrlPt2y),
+ QPointF(endPtx, endPty));
+}
+
+inline void QPainterPath::quadTo(qreal ctrlPtx, qreal ctrlPty, qreal endPtx, qreal endPty)
+{
+ quadTo(QPointF(ctrlPtx, ctrlPty), QPointF(endPtx, endPty));
+}
+
+inline void QPainterPath::addEllipse(qreal x, qreal y, qreal w, qreal h)
+{
+ addEllipse(QRectF(x, y, w, h));
+}
+
+inline void QPainterPath::addEllipse(const QPointF &center, qreal rx, qreal ry)
+{
+ addEllipse(QRectF(center.x() - rx, center.y() - ry, 2 * rx, 2 * ry));
+}
+
+inline void QPainterPath::addRect(qreal x, qreal y, qreal w, qreal h)
+{
+ addRect(QRectF(x, y, w, h));
+}
+
+inline void QPainterPath::addRoundedRect(qreal x, qreal y, qreal w, qreal h,
+ qreal xRadius, qreal yRadius,
+ Qt::SizeMode mode)
+{
+ addRoundedRect(QRectF(x, y, w, h), xRadius, yRadius, mode);
+}
+
+inline void QPainterPath::addRoundRect(qreal x, qreal y, qreal w, qreal h,
+ int xRnd, int yRnd)
+{
+ addRoundRect(QRectF(x, y, w, h), xRnd, yRnd);
+}
+
+inline void QPainterPath::addRoundRect(const QRectF &rect,
+ int roundness)
+{
+ int xRnd = roundness;
+ int yRnd = roundness;
+ if (rect.width() > rect.height())
+ xRnd = int(roundness * rect.height()/rect.width());
+ else
+ yRnd = int(roundness * rect.width()/rect.height());
+ addRoundRect(rect, xRnd, yRnd);
+}
+
+inline void QPainterPath::addRoundRect(qreal x, qreal y, qreal w, qreal h,
+ int roundness)
+{
+ addRoundRect(QRectF(x, y, w, h), roundness);
+}
+
+inline void QPainterPath::addText(qreal x, qreal y, const QFont &f, const QString &text)
+{
+ addText(QPointF(x, y), f, text);
+}
+
+inline bool QPainterPath::isEmpty() const
+{
+ return !d_ptr || (d_ptr->elements.size() == 1 && d_ptr->elements.first().type == MoveToElement);
+}
+
+inline int QPainterPath::elementCount() const
+{
+ return d_ptr ? d_ptr->elements.size() : 0;
+}
+
+inline const QPainterPath::Element &QPainterPath::elementAt(int i) const
+{
+ Q_ASSERT(d_ptr);
+ Q_ASSERT(i >= 0 && i < elementCount());
+ return d_ptr->elements.at(i);
+}
+
+inline void QPainterPath::setElementPositionAt(int i, qreal x, qreal y)
+{
+ Q_ASSERT(d_ptr);
+ Q_ASSERT(i >= 0 && i < elementCount());
+ detach();
+ QPainterPath::Element &e = d_ptr->elements[i];
+ e.x = x;
+ e.y = y;
+}
+
+
+inline void QPainterPath::detach()
+{
+ if (d_ptr->ref != 1)
+ detach_helper();
+ setDirty(true);
+}
+
+#ifndef QT_NO_DEBUG_STREAM
+Q_GUI_EXPORT QDebug operator<<(QDebug, const QPainterPath &);
+#endif
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QPAINTERPATH_H
diff --git a/src/gui/painting/qpainterpath_p.h b/src/gui/painting/qpainterpath_p.h
new file mode 100644
index 0000000000..93f9704a79
--- /dev/null
+++ b/src/gui/painting/qpainterpath_p.h
@@ -0,0 +1,211 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QPAINTERPATH_P_H
+#define QPAINTERPATH_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 "QtGui/qpainterpath.h"
+#include "QtGui/qregion.h"
+#include "QtCore/qlist.h"
+#include "QtCore/qvarlengtharray.h"
+
+#include <qdebug.h>
+
+#include <private/qvectorpath_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QPolygonF;
+class QVectorPathConverter;
+
+class QVectorPathConverter
+{
+public:
+ QVectorPathConverter(const QVector<QPainterPath::Element> &path, uint fillRule)
+ : pathData(path, fillRule),
+ path(pathData.points.data(), path.size(),
+ pathData.elements.data(), pathData.flags) {}
+
+ const QVectorPath &vectorPath() {
+ return path;
+ }
+
+ struct QVectorPathData {
+ QVectorPathData(const QVector<QPainterPath::Element> &path, uint fillRule)
+ : elements(path.size()),
+ points(path.size() * 2),
+ flags(0)
+ {
+ int ptsPos = 0;
+ for (int i=0; i<path.size(); ++i) {
+ const QPainterPath::Element &e = path.at(i);
+ elements[i] = e.type;
+ points[ptsPos++] = e.x;
+ points[ptsPos++] = e.y;
+ if (e.type == QPainterPath::CurveToElement)
+ flags |= QVectorPath::CurvedShapeHint;
+ }
+
+ if (fillRule == Qt::WindingFill)
+ flags |= QVectorPath::WindingFill;
+ else
+ flags |= QVectorPath::OddEvenFill;
+
+ }
+ QVarLengthArray<QPainterPath::ElementType> elements;
+ QVarLengthArray<qreal> points;
+ uint flags;
+ };
+
+ QVectorPathData pathData;
+ QVectorPath path;
+
+private:
+ Q_DISABLE_COPY(QVectorPathConverter)
+};
+
+class Q_GUI_EXPORT QPainterPathData : public QPainterPathPrivate
+{
+public:
+ QPainterPathData() :
+ cStart(0), fillRule(Qt::OddEvenFill),
+ dirtyBounds(false), dirtyControlBounds(false),
+ pathConverter(0)
+ {
+ ref = 1;
+ require_moveTo = false;
+ }
+
+ QPainterPathData(const QPainterPathData &other) :
+ QPainterPathPrivate(), cStart(other.cStart), fillRule(other.fillRule),
+ dirtyBounds(other.dirtyBounds), bounds(other.bounds),
+ dirtyControlBounds(other.dirtyControlBounds),
+ controlBounds(other.controlBounds),
+ pathConverter(0)
+ {
+ ref = 1;
+ require_moveTo = false;
+ elements = other.elements;
+ }
+
+ ~QPainterPathData() {
+ delete pathConverter;
+ }
+
+ inline bool isClosed() const;
+ inline void close();
+ inline void maybeMoveTo();
+
+ const QVectorPath &vectorPath() {
+ if (!pathConverter)
+ pathConverter = new QVectorPathConverter(elements, fillRule);
+ return pathConverter->path;
+ }
+
+ int cStart;
+ Qt::FillRule fillRule;
+
+ bool require_moveTo;
+
+ bool dirtyBounds;
+ QRectF bounds;
+ bool dirtyControlBounds;
+ QRectF controlBounds;
+
+ QVectorPathConverter *pathConverter;
+};
+
+
+
+void Q_GUI_EXPORT qt_find_ellipse_coords(const QRectF &r, qreal angle, qreal length,
+ QPointF* startPoint, QPointF *endPoint);
+
+inline bool QPainterPathData::isClosed() const
+{
+ const QPainterPath::Element &first = elements.at(cStart);
+ const QPainterPath::Element &last = elements.last();
+ return first.x == last.x && first.y == last.y;
+}
+
+inline void QPainterPathData::close()
+{
+ Q_ASSERT(ref == 1);
+ require_moveTo = true;
+ const QPainterPath::Element &first = elements.at(cStart);
+ QPainterPath::Element &last = elements.last();
+ if (first.x != last.x || first.y != last.y) {
+ if (qFuzzyCompare(first.x, last.x) && qFuzzyCompare(first.y, last.y)) {
+ last.x = first.x;
+ last.y = first.y;
+ } else {
+ QPainterPath::Element e = { first.x, first.y, QPainterPath::LineToElement };
+ elements << e;
+ }
+ }
+}
+
+inline void QPainterPathData::maybeMoveTo()
+{
+ if (require_moveTo) {
+ QPainterPath::Element e = elements.last();
+ e.type = QPainterPath::MoveToElement;
+ elements.append(e);
+ require_moveTo = false;
+ }
+}
+
+#define KAPPA 0.5522847498
+
+
+QT_END_NAMESPACE
+
+#endif // QPAINTERPATH_P_H
diff --git a/src/gui/painting/qpathclipper.cpp b/src/gui/painting/qpathclipper.cpp
new file mode 100644
index 0000000000..297cdd3e8f
--- /dev/null
+++ b/src/gui/painting/qpathclipper.cpp
@@ -0,0 +1,2042 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qpathclipper_p.h"
+
+#include <private/qbezier_p.h>
+#include <private/qdatabuffer_p.h>
+#include <qmath.h>
+
+#include <QImage>
+#include <QPainter>
+
+/**
+ The algorithm is as follows:
+
+ 1. Find all intersections between the two paths (including self-intersections),
+ and build a winged edge structure of non-intersecting parts.
+ 2. While there are more unhandled edges:
+ 3. Pick a y-coordinate from an unhandled edge.
+ 4. Intersect the horizontal line at y-coordinate with all edges.
+ 5. Traverse intersections left to right deciding whether each subpath should be added or not.
+ 6. If the subpath should be added, traverse the winged-edge structure and add the edges to
+ a separate winged edge structure.
+ 7. Mark all edges in subpaths crossing the horizontal line as handled.
+ 8. (Optional) Simplify the resulting winged edge structure by merging shared edges.
+ 9. Convert the resulting winged edge structure to a painter path.
+ */
+
+#include <qdebug.h>
+
+QT_BEGIN_NAMESPACE
+
+//#define QDEBUG_CLIPPER
+static qreal dot(const QPointF &a, const QPointF &b)
+{
+ return a.x() * b.x() + a.y() * b.y();
+}
+
+static QPointF normalize(const QPointF &p)
+{
+ return p / qSqrt(p.x() * p.x() + p.y() * p.y());
+}
+
+static bool pathToRect(const QPainterPath &path, QRectF *rect = 0);
+
+struct QIntersection
+{
+ qreal alphaA;
+ qreal alphaB;
+
+ QPointF pos;
+};
+
+class QIntersectionFinder
+{
+public:
+ void produceIntersections(QPathSegments &segments);
+ bool hasIntersections(const QPathSegments &a, const QPathSegments &b) const;
+
+private:
+ void intersectBeziers(const QBezier &one, const QBezier &two, QVector<QPair<qreal, qreal> > &t, QDataBuffer<QIntersection> &intersections);
+ void intersectLines(const QLineF &a, const QLineF &b, QDataBuffer<QIntersection> &intersections);
+
+ bool beziersIntersect(const QBezier &one, const QBezier &two) const;
+ bool linesIntersect(const QLineF &a, const QLineF &b) const;
+};
+
+bool QIntersectionFinder::beziersIntersect(const QBezier &one, const QBezier &two) const
+{
+ return (one.pt1() == two.pt1() && one.pt2() == two.pt2() && one.pt3() == two.pt3() && one.pt4() == two.pt4())
+ || (one.pt1() == two.pt4() && one.pt2() == two.pt3() && one.pt3() == two.pt2() && one.pt4() == two.pt1())
+ || QBezier::findIntersections(one, two, 0);
+}
+
+bool QIntersectionFinder::linesIntersect(const QLineF &a, const QLineF &b) const
+{
+ const QPointF p1 = a.p1();
+ const QPointF p2 = a.p2();
+
+ const QPointF q1 = b.p1();
+ const QPointF q2 = b.p2();
+
+ if (p1 == p2 || q1 == q2)
+ return false;
+
+ const bool p1_equals_q1 = (p1 == q1);
+ const bool p2_equals_q2 = (p2 == q2);
+
+ if (p1_equals_q1 && p2_equals_q2)
+ return true;
+
+ const bool p1_equals_q2 = (p1 == q2);
+ const bool p2_equals_q1 = (p2 == q1);
+
+ if (p1_equals_q2 && p2_equals_q1)
+ return true;
+
+ const QPointF pDelta = p2 - p1;
+ const QPointF qDelta = q2 - q1;
+
+ const qreal par = pDelta.x() * qDelta.y() - pDelta.y() * qDelta.x();
+
+ if (qFuzzyCompare(par + 1, 1)) {
+ const QPointF normal(-pDelta.y(), pDelta.x());
+
+ // coinciding?
+ if (qFuzzyCompare(dot(normal, q1 - p1) + 1, 1)) {
+ const qreal dp = dot(pDelta, pDelta);
+
+ const qreal tq1 = dot(pDelta, q1 - p1);
+ const qreal tq2 = dot(pDelta, q2 - p1);
+
+ if ((tq1 > 0 && tq1 < dp) || (tq2 > 0 && tq2 < dp))
+ return true;
+
+ const qreal dq = dot(qDelta, qDelta);
+
+ const qreal tp1 = dot(qDelta, p1 - q1);
+ const qreal tp2 = dot(qDelta, p2 - q1);
+
+ if ((tp1 > 0 && tp1 < dq) || (tp2 > 0 && tp2 < dq))
+ return true;
+ }
+
+ return false;
+ }
+
+ // if the lines are not parallel and share a common end point, then they
+ // don't intersect
+ if (p1_equals_q1 || p1_equals_q2 || p2_equals_q1 || p2_equals_q2)
+ return false;
+
+ const qreal invPar = 1 / par;
+
+ const qreal tp = (qDelta.y() * (q1.x() - p1.x()) -
+ qDelta.x() * (q1.y() - p1.y())) * invPar;
+
+ if (tp < 0 || tp > 1)
+ return false;
+
+ const qreal tq = (pDelta.y() * (q1.x() - p1.x()) -
+ pDelta.x() * (q1.y() - p1.y())) * invPar;
+
+ return tq >= 0 && tq <= 1;
+}
+
+void QIntersectionFinder::intersectBeziers(const QBezier &one, const QBezier &two, QVector<QPair<qreal, qreal> > &t, QDataBuffer<QIntersection> &intersections)
+{
+ if ((one.pt1() == two.pt1() && one.pt2() == two.pt2() && one.pt3() == two.pt3() && one.pt4() == two.pt4())
+ || (one.pt1() == two.pt4() && one.pt2() == two.pt3() && one.pt3() == two.pt2() && one.pt4() == two.pt1())) {
+
+ return;
+ }
+
+ t.clear();
+
+ if (!QBezier::findIntersections(one, two, &t))
+ return;
+
+ int count = t.size();
+
+ for (int i = 0; i < count; ++i) {
+ qreal alpha_p = t.at(i).first;
+ qreal alpha_q = t.at(i).second;
+
+ QPointF pt;
+ if (qFuzzyCompare(alpha_p + 1, 1)) {
+ pt = one.pt1();
+ } else if (qFuzzyCompare(alpha_p, 1)) {
+ pt = one.pt4();
+ } else if (qFuzzyCompare(alpha_q + 1, 1)) {
+ pt = two.pt1();
+ } else if (qFuzzyCompare(alpha_q, 1)) {
+ pt = two.pt4();
+ } else {
+ pt = one.pointAt(alpha_p);
+ }
+
+ QIntersection intersection;
+ intersection.alphaA = alpha_p;
+ intersection.alphaB = alpha_q;
+ intersection.pos = pt;
+ intersections.add(intersection);
+ }
+}
+
+void QIntersectionFinder::intersectLines(const QLineF &a, const QLineF &b, QDataBuffer<QIntersection> &intersections)
+{
+ const QPointF p1 = a.p1();
+ const QPointF p2 = a.p2();
+
+ const QPointF q1 = b.p1();
+ const QPointF q2 = b.p2();
+
+ if (p1 == p2 || q1 == q2)
+ return;
+
+ const bool p1_equals_q1 = (p1 == q1);
+ const bool p2_equals_q2 = (p2 == q2);
+
+ if (p1_equals_q1 && p2_equals_q2)
+ return;
+
+ const bool p1_equals_q2 = (p1 == q2);
+ const bool p2_equals_q1 = (p2 == q1);
+
+ if (p1_equals_q2 && p2_equals_q1)
+ return;
+
+ const QPointF pDelta = p2 - p1;
+ const QPointF qDelta = q2 - q1;
+
+ const qreal par = pDelta.x() * qDelta.y() - pDelta.y() * qDelta.x();
+
+ if (qFuzzyCompare(par + 1, 1)) {
+ const QPointF normal(-pDelta.y(), pDelta.x());
+
+ // coinciding?
+ if (qFuzzyCompare(dot(normal, q1 - p1) + 1, 1)) {
+ const qreal invDp = 1 / dot(pDelta, pDelta);
+
+ const qreal tq1 = dot(pDelta, q1 - p1) * invDp;
+ const qreal tq2 = dot(pDelta, q2 - p1) * invDp;
+
+ if (tq1 > 0 && tq1 < 1) {
+ QIntersection intersection;
+ intersection.alphaA = tq1;
+ intersection.alphaB = 0;
+ intersection.pos = q1;
+ intersections.add(intersection);
+ }
+
+ if (tq2 > 0 && tq2 < 1) {
+ QIntersection intersection;
+ intersection.alphaA = tq2;
+ intersection.alphaB = 1;
+ intersection.pos = q2;
+ intersections.add(intersection);
+ }
+
+ const qreal invDq = 1 / dot(qDelta, qDelta);
+
+ const qreal tp1 = dot(qDelta, p1 - q1) * invDq;
+ const qreal tp2 = dot(qDelta, p2 - q1) * invDq;
+
+ if (tp1 > 0 && tp1 < 1) {
+ QIntersection intersection;
+ intersection.alphaA = 0;
+ intersection.alphaB = tp1;
+ intersection.pos = p1;
+ intersections.add(intersection);
+ }
+
+ if (tp2 > 0 && tp2 < 1) {
+ QIntersection intersection;
+ intersection.alphaA = 1;
+ intersection.alphaB = tp2;
+ intersection.pos = p2;
+ intersections.add(intersection);
+ }
+ }
+
+ return;
+ }
+
+ // if the lines are not parallel and share a common end point, then they
+ // don't intersect
+ if (p1_equals_q1 || p1_equals_q2 || p2_equals_q1 || p2_equals_q2)
+ return;
+
+
+ const qreal tp = (qDelta.y() * (q1.x() - p1.x()) -
+ qDelta.x() * (q1.y() - p1.y())) / par;
+ const qreal tq = (pDelta.y() * (q1.x() - p1.x()) -
+ pDelta.x() * (q1.y() - p1.y())) / par;
+
+ if (tp<0 || tp>1 || tq<0 || tq>1)
+ return;
+
+ const bool p_zero = qFuzzyCompare(tp + 1, 1);
+ const bool p_one = qFuzzyCompare(tp, 1);
+
+ const bool q_zero = qFuzzyCompare(tq + 1, 1);
+ const bool q_one = qFuzzyCompare(tq, 1);
+
+ if ((q_zero || q_one) && (p_zero || p_one))
+ return;
+
+ QPointF pt;
+ if (p_zero) {
+ pt = p1;
+ } else if (p_one) {
+ pt = p2;
+ } else if (q_zero) {
+ pt = q1;
+ } else if (q_one) {
+ pt = q2;
+ } else {
+ pt = q1 + (q2 - q1) * tq;
+ }
+
+ QIntersection intersection;
+ intersection.alphaA = tp;
+ intersection.alphaB = tq;
+ intersection.pos = pt;
+ intersections.add(intersection);
+}
+
+static const QBezier bezierFromLine(const QLineF &line)
+{
+ const QPointF p1 = line.p1();
+ const QPointF p2 = line.p2();
+ const QPointF delta = (p2 - p1) / 3;
+ return QBezier::fromPoints(p1, p1 + delta, p1 + 2 * delta, p2);
+}
+
+bool QIntersectionFinder::hasIntersections(const QPathSegments &a, const QPathSegments &b) const
+{
+ QBezier tempA;
+ QBezier tempB;
+
+ if (a.segments() == 0 || b.segments() == 0)
+ return false;
+
+ const QRectF &rb0 = b.elementBounds(0);
+
+ qreal minX = rb0.left();
+ qreal minY = rb0.top();
+ qreal maxX = rb0.right();
+ qreal maxY = rb0.bottom();
+
+ for (int i = 1; i < b.segments(); ++i) {
+ const QRectF &r = b.elementBounds(i);
+ minX = qMin(minX, r.left());
+ minY = qMin(minY, r.top());
+ maxX = qMax(maxX, r.right());
+ maxY = qMax(maxY, r.bottom());
+ }
+
+ QRectF rb(minX, minY, maxX - minX, maxY - minY);
+
+ for (int i = 0; i < a.segments(); ++i) {
+ const QBezier *bezierA = a.bezierAt(i);
+ bool isBezierA = bezierA != 0;
+
+ const QRectF &r1 = a.elementBounds(i);
+
+ if (r1.left() > rb.right() || rb.left() > r1.right())
+ continue;
+ if (r1.top() > rb.bottom() || rb.top() > r1.bottom())
+ continue;
+
+ for (int j = 0; j < b.segments(); ++j) {
+ const QRectF &r2 = b.elementBounds(j);
+
+ if (r1.left() > r2.right() || r2.left() > r1.right())
+ continue;
+ if (r1.top() > r2.bottom() || r2.top() > r1.bottom())
+ continue;
+
+ bool isBezierB = b.bezierAt(j) != 0;
+
+ if (isBezierA || isBezierB) {
+ const QBezier *bezierB;
+ if (isBezierB) {
+ bezierB = b.bezierAt(j);
+ } else {
+ tempB = bezierFromLine(b.lineAt(j));
+ bezierB = &tempB;
+ }
+
+ if (!bezierA) {
+ tempA = bezierFromLine(a.lineAt(i));
+ bezierA = &tempA;
+ }
+
+ if (beziersIntersect(*bezierA, *bezierB))
+ return true;
+ } else {
+ if (linesIntersect(a.lineAt(i), b.lineAt(j)))
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+void QIntersectionFinder::produceIntersections(QPathSegments &segments)
+{
+ QBezier tempA;
+ QBezier tempB;
+
+ QVector<QPair<qreal, qreal> > t;
+ QDataBuffer<QIntersection> intersections;
+
+ for (int i = 0; i < segments.segments(); ++i) {
+ const QBezier *bezierA = segments.bezierAt(i);
+ bool isBezierA = bezierA != 0;
+
+ const QRectF &r1 = segments.elementBounds(i);
+
+ for (int j = 0; j < i; ++j) {
+ const QRectF &r2 = segments.elementBounds(j);
+
+ if (r1.left() > r2.right() || r2.left() > r1.right())
+ continue;
+ if (r1.top() > r2.bottom() || r2.top() > r1.bottom())
+ continue;
+
+ intersections.reset();
+
+ bool isBezierB = segments.bezierAt(j) != 0;
+
+ if (isBezierA || isBezierB) {
+ const QBezier *bezierB;
+ if (isBezierB) {
+ bezierB = segments.bezierAt(j);
+ } else {
+ tempB = bezierFromLine(segments.lineAt(j));
+ bezierB = &tempB;
+ }
+
+ if (!bezierA) {
+ tempA = bezierFromLine(segments.lineAt(i));
+ bezierA = &tempA;
+ }
+
+ intersectBeziers(*bezierA, *bezierB, t, intersections);
+ } else {
+ const QLineF lineA = segments.lineAt(i);
+ const QLineF lineB = segments.lineAt(j);
+
+ intersectLines(lineA, lineB, intersections);
+ }
+
+ for (int k = 0; k < intersections.size(); ++k) {
+ QPathSegments::Intersection i_isect, j_isect;
+ i_isect.vertex = j_isect.vertex = segments.addPoint(intersections.at(k).pos);
+
+ i_isect.t = intersections.at(k).alphaA;
+ j_isect.t = intersections.at(k).alphaB;
+
+ i_isect.next = 0;
+ j_isect.next = 0;
+
+ segments.addIntersection(i, i_isect);
+ segments.addIntersection(j, j_isect);
+ }
+ }
+ }
+}
+
+class QKdPointTree
+{
+public:
+ enum Traversal {
+ TraverseBoth,
+ TraverseLeft,
+ TraverseRight,
+ TraverseNone
+ };
+
+ struct Node {
+ int point;
+ int id;
+
+ Node *left;
+ Node *right;
+ };
+
+ QKdPointTree(const QPathSegments &segments)
+ : m_segments(&segments)
+ , m_nodes(m_segments->points())
+ , m_id(0)
+ {
+ m_nodes.resize(m_segments->points());
+
+ for (int i = 0; i < m_nodes.size(); ++i) {
+ m_nodes.at(i).point = i;
+ m_nodes.at(i).id = -1;
+ }
+
+ m_rootNode = build(0, m_nodes.size());
+ }
+
+ int build(int begin, int end, int depth = 0);
+
+ Node *rootNode()
+ {
+ return &m_nodes.at(m_rootNode);
+ }
+
+ inline int nextId()
+ {
+ return m_id++;
+ }
+
+private:
+ const QPathSegments *m_segments;
+ QDataBuffer<Node> m_nodes;
+
+ int m_rootNode;
+ int m_id;
+};
+
+template <typename T>
+void qTraverseKdPointTree(QKdPointTree::Node &node, T &t, int depth = 0)
+{
+ QKdPointTree::Traversal status = t(node, depth);
+
+ const bool traverseRight = (status == QKdPointTree::TraverseBoth || status == QKdPointTree::TraverseRight);
+ const bool traverseLeft = (status == QKdPointTree::TraverseBoth || status == QKdPointTree::TraverseLeft);
+
+ if (traverseLeft && node.left)
+ QT_PREPEND_NAMESPACE(qTraverseKdPointTree<T>)(*node.left, t, depth + 1);
+
+ if (traverseRight && node.right)
+ QT_PREPEND_NAMESPACE(qTraverseKdPointTree<T>)(*node.right, t, depth + 1);
+}
+
+static inline qreal component(const QPointF &point, unsigned int i)
+{
+ Q_ASSERT(i < 2);
+ const qreal components[] = { point.x(), point.y() };
+ return components[i];
+}
+
+int QKdPointTree::build(int begin, int end, int depth)
+{
+ Q_ASSERT(end > begin);
+
+ const qreal pivot = component(m_segments->pointAt(m_nodes.at(begin).point), depth & 1);
+
+ int first = begin + 1;
+ int last = end - 1;
+
+ while (first <= last) {
+ const qreal value = component(m_segments->pointAt(m_nodes.at(first).point), depth & 1);
+
+ if (value < pivot)
+ ++first;
+ else {
+ qSwap(m_nodes.at(first), m_nodes.at(last));
+ --last;
+ }
+ }
+
+ qSwap(m_nodes.at(last), m_nodes.at(begin));
+
+ if (last > begin)
+ m_nodes.at(last).left = &m_nodes.at(build(begin, last, depth + 1));
+ else
+ m_nodes.at(last).left = 0;
+
+ if (last + 1 < end)
+ m_nodes.at(last).right = &m_nodes.at(build(last + 1, end, depth + 1));
+ else
+ m_nodes.at(last).right = 0;
+
+ return last;
+}
+
+class QKdPointFinder
+{
+public:
+ QKdPointFinder(int point, const QPathSegments &segments, QKdPointTree &tree)
+ : m_point(point)
+ , m_result(-1)
+ , m_segments(&segments)
+ , m_tree(&tree)
+ {
+ pointComponents[0] = segments.pointAt(point).x();
+ pointComponents[1] = segments.pointAt(point).y();
+ }
+
+ inline QKdPointTree::Traversal operator()(QKdPointTree::Node &node, int depth)
+ {
+ if (m_result != -1)
+ return QKdPointTree::TraverseNone;
+
+ const QPointF &nodePoint = m_segments->pointAt(node.point);
+
+ const qreal pivotComponents[] = { nodePoint.x(), nodePoint.y() };
+
+ const qreal pivot = pivotComponents[depth & 1];
+ const qreal value = pointComponents[depth & 1];
+
+ if (qFuzzyCompare(pivot, value)) {
+ const qreal pivot2 = pivotComponents[(depth + 1) & 1];
+ const qreal value2 = pointComponents[(depth + 1) & 1];
+
+ if (qFuzzyCompare(pivot2, value2)) {
+ if (node.id < 0)
+ node.id = m_tree->nextId();
+
+ m_result = node.id;
+ return QKdPointTree::TraverseNone;
+ } else
+ return QKdPointTree::TraverseBoth;
+ } else if (value < pivot) {
+ return QKdPointTree::TraverseLeft;
+ } else {
+ return QKdPointTree::TraverseRight;
+ }
+ }
+
+ int result() const
+ {
+ return m_result;
+ }
+
+private:
+ int m_point;
+ qreal pointComponents[2];
+ int m_result;
+ const QPathSegments *m_segments;
+ QKdPointTree *m_tree;
+};
+
+// merge all points that are within qFuzzyCompare range of each other
+void QPathSegments::mergePoints()
+{
+ QKdPointTree tree(*this);
+
+ if (tree.rootNode()) {
+ QDataBuffer<QPointF> mergedPoints(points());
+ QDataBuffer<int> pointIndices(points());
+
+ for (int i = 0; i < points(); ++i) {
+ QKdPointFinder finder(i, *this, tree);
+ QT_PREPEND_NAMESPACE(qTraverseKdPointTree<QKdPointFinder>)(*tree.rootNode(), finder);
+
+ Q_ASSERT(finder.result() != -1);
+
+ if (finder.result() >= mergedPoints.size())
+ mergedPoints << m_points.at(i);
+
+ pointIndices << finder.result();
+ }
+
+ for (int i = 0; i < m_segments.size(); ++i) {
+ m_segments.at(i).va = pointIndices.at(m_segments.at(i).va);
+ m_segments.at(i).vb = pointIndices.at(m_segments.at(i).vb);
+ }
+
+ for (int i = 0; i < m_intersections.size(); ++i)
+ m_intersections.at(i).vertex = pointIndices.at(m_intersections.at(i).vertex);
+
+ m_points.swap(mergedPoints);
+ }
+}
+
+void QWingedEdge::intersectAndAdd()
+{
+ QIntersectionFinder finder;
+ finder.produceIntersections(m_segments);
+
+ m_segments.mergePoints();
+
+ for (int i = 0; i < m_segments.points(); ++i)
+ addVertex(m_segments.pointAt(i));
+
+ QDataBuffer<QPathSegments::Intersection> intersections;
+ for (int i = 0; i < m_segments.segments(); ++i) {
+ intersections.reset();
+
+ int pathId = m_segments.pathId(i);
+
+ const QPathSegments::Intersection *isect = m_segments.intersectionAt(i);
+ while (isect) {
+ intersections << *isect;
+
+ if (isect->next) {
+ isect += isect->next;
+ } else {
+ isect = 0;
+ }
+ }
+
+ qSort(intersections.data(), intersections.data() + intersections.size());
+
+ const QBezier *bezier = m_segments.bezierAt(i);
+ if (bezier) {
+ int first = m_segments.segmentAt(i).va;
+ int second = m_segments.segmentAt(i).vb;
+
+ qreal alpha = 0.0;
+ int last = first;
+ for (int j = 0; j < intersections.size(); ++j) {
+ const QPathSegments::Intersection &isect = intersections.at(j);
+
+ addBezierEdge(bezier, last, isect.vertex, alpha, isect.t, pathId);
+
+ alpha = isect.t;
+ last = isect.vertex;
+ }
+
+ addBezierEdge(bezier, last, second, alpha, 1.0, pathId);
+ } else {
+ int first = m_segments.segmentAt(i).va;
+ int second = m_segments.segmentAt(i).vb;
+
+ int last = first;
+ for (int j = 0; j < intersections.size(); ++j) {
+ const QPathSegments::Intersection &isect = intersections.at(j);
+
+ QPathEdge *ep = edge(addEdge(last, isect.vertex));
+
+ if (ep) {
+ const int dir = m_segments.pointAt(last).y() < m_segments.pointAt(isect.vertex).y() ? 1 : -1;
+ if (pathId == 0)
+ ep->windingA += dir;
+ else
+ ep->windingB += dir;
+ }
+
+ last = isect.vertex;
+ }
+
+ QPathEdge *ep = edge(addEdge(last, second));
+
+ if (ep) {
+ const int dir = m_segments.pointAt(last).y() < m_segments.pointAt(second).y() ? 1 : -1;
+ if (pathId == 0)
+ ep->windingA += dir;
+ else
+ ep->windingB += dir;
+ }
+ }
+ }
+}
+
+QWingedEdge::QWingedEdge()
+{
+}
+
+QWingedEdge::QWingedEdge(const QPainterPath &subject, const QPainterPath &clip)
+{
+ m_segments.setPath(subject);
+ m_segments.addPath(clip);
+
+ intersectAndAdd();
+}
+
+QWingedEdge::TraversalStatus QWingedEdge::next(const QWingedEdge::TraversalStatus &status) const
+{
+ const QPathEdge *sp = edge(status.edge);
+ Q_ASSERT(sp);
+
+ TraversalStatus result;
+ result.edge = sp->next(status.traversal, status.direction);
+ result.traversal = status.traversal;
+ result.direction = status.direction;
+
+ const QPathEdge *rp = edge(result.edge);
+ Q_ASSERT(rp);
+
+ if (sp->vertex(status.direction) == rp->vertex(status.direction))
+ result.flip();
+
+ return result;
+}
+
+static bool isLine(const QBezier &bezier)
+{
+ const bool equal_1_2 = bezier.pt1() == bezier.pt2();
+ const bool equal_2_3 = bezier.pt2() == bezier.pt3();
+ const bool equal_3_4 = bezier.pt3() == bezier.pt4();
+
+ // point?
+ if (equal_1_2 && equal_2_3 && equal_3_4)
+ return true;
+
+ if (bezier.pt1() == bezier.pt4())
+ return equal_1_2 || equal_3_4;
+
+ return (equal_1_2 && equal_3_4) || (equal_1_2 && equal_2_3) || (equal_2_3 && equal_3_4);
+}
+
+void QPathSegments::setPath(const QPainterPath &path)
+{
+ m_points.reset();
+ m_beziers.reset();
+ m_intersections.reset();
+ m_segments.reset();
+
+ m_pathId = 0;
+
+ addPath(path);
+}
+
+void QPathSegments::addPath(const QPainterPath &path)
+{
+ int firstSegment = m_segments.size();
+
+ bool hasMoveTo = false;
+ int lastMoveTo = 0;
+ int last = 0;
+ for (int i = 0; i < path.elementCount(); ++i) {
+ int current = m_points.size();
+
+ QPointF currentPoint;
+ if (path.elementAt(i).type == QPainterPath::CurveToElement)
+ currentPoint = path.elementAt(i+2);
+ else
+ currentPoint = path.elementAt(i);
+
+ if (i > 0 && m_points.at(lastMoveTo) == currentPoint)
+ current = lastMoveTo;
+ else
+ m_points << currentPoint;
+
+ switch (path.elementAt(i).type) {
+ case QPainterPath::MoveToElement:
+ if (hasMoveTo && last != lastMoveTo && m_points.at(last) != m_points.at(lastMoveTo))
+ m_segments << Segment(m_pathId, last, lastMoveTo);
+ hasMoveTo = true;
+ last = lastMoveTo = current;
+ break;
+ case QPainterPath::LineToElement:
+ m_segments << Segment(m_pathId, last, current);
+ last = current;
+ break;
+ case QPainterPath::CurveToElement:
+ {
+ QBezier bezier = QBezier::fromPoints(m_points.at(last), path.elementAt(i), path.elementAt(i+1), path.elementAt(i+2));
+ if (isLine(bezier)) {
+ m_segments << Segment(m_pathId, last, current);
+ } else {
+ m_segments << Segment(m_pathId, last, current, m_beziers.size());
+ m_beziers << bezier;
+ }
+ }
+ last = current;
+ i += 2;
+ break;
+ default:
+ Q_ASSERT(false);
+ break;
+ }
+ }
+
+ if (hasMoveTo && last != lastMoveTo && m_points.at(last) != m_points.at(lastMoveTo))
+ m_segments << Segment(m_pathId, last, lastMoveTo);
+
+ for (int i = firstSegment; i < m_segments.size(); ++i) {
+ const QBezier *bezier = bezierAt(i);
+ if (bezier) {
+ m_segments.at(i).bounds = bezier->bounds();
+ } else {
+ const QLineF line = lineAt(i);
+
+ qreal x1 = line.p1().x();
+ qreal y1 = line.p1().y();
+ qreal x2 = line.p2().x();
+ qreal y2 = line.p2().y();
+
+ if (x2 < x1)
+ qSwap(x1, x2);
+ if (y2 < y1)
+ qSwap(y1, y2);
+
+ m_segments.at(i).bounds = QRectF(x1, y1, x2 - x1, y2 - y1);
+ }
+ }
+
+ ++m_pathId;
+}
+
+qreal QWingedEdge::delta(int vertex, int a, int b) const
+{
+ const QPathEdge *ap = edge(a);
+ const QPathEdge *bp = edge(b);
+
+ qreal a_angle = ap->angle;
+ qreal b_angle = bp->angle;
+
+ if (vertex == ap->second)
+ a_angle = ap->invAngle;
+
+ if (vertex == bp->second)
+ b_angle = bp->invAngle;
+
+ qreal result = b_angle - a_angle;
+
+ if (qFuzzyCompare(result + 1, 1) || qFuzzyCompare(result, 128))
+ return 0;
+
+ if (result < 0)
+ return result + 128.;
+ else
+ return result;
+}
+
+static inline QPointF tangentAt(const QWingedEdge &list, int vi, int ei)
+{
+ const QPathEdge *ep = list.edge(ei);
+ Q_ASSERT(ep);
+
+ qreal t;
+ qreal sign;
+
+ if (ep->first == vi) {
+ t = ep->t0;
+ sign = 1;
+ } else {
+ t = ep->t1;
+ sign = -1;
+ }
+
+ QPointF normal;
+ if (ep->bezier) {
+ normal = ep->bezier->derivedAt(t);
+
+ if (qFuzzyCompare(normal.x() + 1, 1) && qFuzzyCompare(normal.y() + 1, 1))
+ normal = ep->bezier->secondDerivedAt(t);
+ } else {
+ const QPointF a = *list.vertex(ep->first);
+ const QPointF b = *list.vertex(ep->second);
+ normal = b - a;
+ }
+
+ return normalize(sign * normal);
+}
+
+static inline QPointF midPoint(const QWingedEdge &list, int ei)
+{
+ const QPathEdge *ep = list.edge(ei);
+ Q_ASSERT(ep);
+
+ if (ep->bezier) {
+ return ep->bezier->pointAt(0.5 * (ep->t0 + ep->t1));
+ } else {
+ const QPointF a = *list.vertex(ep->first);
+ const QPointF b = *list.vertex(ep->second);
+ return a + 0.5 * (b - a);
+ }
+}
+
+static QBezier transform(const QBezier &bezier, const QPointF &xAxis, const QPointF &yAxis, const QPointF &origin)
+{
+ QPointF points[4] = {
+ bezier.pt1(),
+ bezier.pt2(),
+ bezier.pt3(),
+ bezier.pt4()
+ };
+
+ for (int i = 0; i < 4; ++i) {
+ const QPointF p = points[i] - origin;
+
+ points[i].rx() = dot(xAxis, p);
+ points[i].ry() = dot(yAxis, p);
+ }
+
+ return QBezier::fromPoints(points[0], points[1], points[2], points[3]);
+}
+
+static bool isLeftOf(const QWingedEdge &list, int vi, int ai, int bi)
+{
+ const QPathEdge *ap = list.edge(ai);
+ const QPathEdge *bp = list.edge(bi);
+
+ Q_ASSERT(ap);
+ Q_ASSERT(bp);
+
+ if (!(ap->bezier || bp->bezier))
+ return false;
+
+ const QPointF tangent = tangentAt(list, vi, ai);
+ const QPointF normal(tangent.y(), -tangent.x());
+
+ const QPointF origin = *list.vertex(vi);
+
+ const QPointF dpA = midPoint(list, ai) - origin;
+ const QPointF dpB = midPoint(list, bi) - origin;
+
+ qreal xA = dot(normal, dpA);
+ qreal xB = dot(normal, dpB);
+
+ if (xA <= 0 && xB >= 0)
+ return true;
+
+ if (xA >= 0 && xB <= 0)
+ return false;
+
+ if (!ap->bezier)
+ return xB > 0;
+
+ if (!bp->bezier)
+ return xA < 0;
+
+ // both are beziers on the same side of the tangent
+
+ // transform the beziers into the local coordinate system
+ // such that positive y is along the tangent, and positive x is along the normal
+
+ QBezier bezierA = transform(*ap->bezier, normal, tangent, origin);
+ QBezier bezierB = transform(*bp->bezier, normal, tangent, origin);
+
+ qreal y = qMin(bezierA.pointAt(0.5 * (ap->t0 + ap->t1)).y(),
+ bezierB.pointAt(0.5 * (bp->t0 + bp->t1)).y());
+
+ xA = bezierA.pointAt(bezierA.tForY(ap->t0, ap->t1, y)).x();
+ xB = bezierB.pointAt(bezierB.tForY(bp->t0, bp->t1, y)).x();
+
+ return xA < xB;
+}
+
+QWingedEdge::TraversalStatus QWingedEdge::findInsertStatus(int vi, int ei) const
+{
+ const QPathVertex *vp = vertex(vi);
+
+ Q_ASSERT(vp);
+ Q_ASSERT(ei >= 0);
+ Q_ASSERT(vp->edge >= 0);
+
+ int position = vp->edge;
+ qreal d = 128.;
+
+ TraversalStatus status;
+ status.direction = edge(vp->edge)->directionTo(vi);
+ status.traversal = QPathEdge::RightTraversal;
+ status.edge = vp->edge;
+
+#ifdef QDEBUG_CLIPPER
+ const QPathEdge *ep = edge(ei);
+ qDebug() << "Finding insert status for edge" << ei << "at vertex" << QPointF(*vp) << ", angles: " << ep->angle << ep->invAngle;
+#endif
+
+ do {
+ status = next(status);
+ status.flip();
+
+ Q_ASSERT(edge(status.edge)->vertex(status.direction) == vi);
+
+ qreal d2 = delta(vi, ei, status.edge);
+
+#ifdef QDEBUG_CLIPPER
+ const QPathEdge *op = edge(status.edge);
+ qDebug() << "Delta to edge" << status.edge << d2 << ", angles: " << op->angle << op->invAngle;
+#endif
+
+ if (!(qFuzzyCompare(d2 + 1, 1) && isLeftOf(*this, vi, status.edge, ei))
+ && (d2 < d || (qFuzzyCompare(d2, d) && isLeftOf(*this, vi, status.edge, position)))) {
+ position = status.edge;
+ d = d2;
+ }
+ } while (status.edge != vp->edge);
+
+ status.traversal = QPathEdge::LeftTraversal;
+ status.direction = QPathEdge::Forward;
+ status.edge = position;
+
+ if (edge(status.edge)->vertex(status.direction) != vi)
+ status.flip();
+
+#ifdef QDEBUG_CLIPPER
+ qDebug() << "Inserting edge" << ei << "to" << (status.traversal == QPathEdge::LeftTraversal ? "left" : "right") << "of edge" << status.edge;
+#endif
+
+ Q_ASSERT(edge(status.edge)->vertex(status.direction) == vi);
+
+ return status;
+}
+
+void QWingedEdge::removeEdge(int ei)
+{
+ QPathEdge *ep = edge(ei);
+
+ TraversalStatus status;
+ status.direction = QPathEdge::Forward;
+ status.traversal = QPathEdge::RightTraversal;
+ status.edge = ei;
+
+ TraversalStatus forwardRight = next(status);
+ forwardRight.flipDirection();
+
+ status.traversal = QPathEdge::LeftTraversal;
+ TraversalStatus forwardLeft = next(status);
+ forwardLeft.flipDirection();
+
+ status.direction = QPathEdge::Backward;
+ TraversalStatus backwardLeft = next(status);
+ backwardLeft.flipDirection();
+
+ status.traversal = QPathEdge::RightTraversal;
+ TraversalStatus backwardRight = next(status);
+ backwardRight.flipDirection();
+
+ edge(forwardRight.edge)->setNext(forwardRight.traversal, forwardRight.direction, forwardLeft.edge);
+ edge(forwardLeft.edge)->setNext(forwardLeft.traversal, forwardLeft.direction, forwardRight.edge);
+
+ edge(backwardRight.edge)->setNext(backwardRight.traversal, backwardRight.direction, backwardLeft.edge);
+ edge(backwardLeft.edge)->setNext(backwardLeft.traversal, backwardLeft.direction, backwardRight.edge);
+
+ ep->setNext(QPathEdge::Forward, ei);
+ ep->setNext(QPathEdge::Backward, ei);
+
+ QPathVertex *a = vertex(ep->first);
+ QPathVertex *b = vertex(ep->second);
+
+ a->edge = backwardRight.edge;
+ b->edge = forwardRight.edge;
+}
+
+static int commonEdge(const QWingedEdge &list, int a, int b)
+{
+ const QPathVertex *ap = list.vertex(a);
+ Q_ASSERT(ap);
+
+ const QPathVertex *bp = list.vertex(b);
+ Q_ASSERT(bp);
+
+ if (ap->edge < 0 || bp->edge < 0)
+ return -1;
+
+ QWingedEdge::TraversalStatus status;
+ status.edge = ap->edge;
+ status.direction = list.edge(status.edge)->directionTo(a);
+ status.traversal = QPathEdge::RightTraversal;
+
+ do {
+ const QPathEdge *ep = list.edge(status.edge);
+
+ if ((ep->first == a && ep->second == b)
+ || (ep->first == b && ep->second == a))
+ return status.edge;
+
+ status = list.next(status);
+ status.flip();
+ } while (status.edge != ap->edge);
+
+ return -1;
+}
+
+static qreal computeAngle(const QPointF &v)
+{
+#if 1
+ if (v.x() == 0) {
+ return v.y() <= 0 ? 0 : 64.;
+ } else if (v.y() == 0) {
+ return v.x() <= 0 ? 32. : 96.;
+ }
+
+ QPointF nv = normalize(v);
+ if (nv.y() < 0) {
+ if (nv.x() < 0) { // 0 - 32
+ return -32. * nv.x();
+ } else { // 96 - 128
+ return 128. - 32. * nv.x();
+ }
+ } else { // 32 - 96
+ return 64. + 32 * nv.x();
+ }
+#else
+ // doesn't seem to be robust enough
+ return atan2(v.x(), v.y()) + Q_PI;
+#endif
+}
+
+int QWingedEdge::addEdge(const QPointF &a, const QPointF &b, const QBezier *bezier, qreal t0, qreal t1)
+{
+ int fi = insert(a);
+ int si = insert(b);
+
+ return addEdge(fi, si, bezier, t0, t1);
+}
+
+int QWingedEdge::addEdge(int fi, int si, const QBezier *bezier, qreal t0, qreal t1)
+{
+ if (fi == si)
+ return -1;
+
+ int common = commonEdge(*this, fi, si);
+ if (common >= 0)
+ return common;
+
+ m_edges << QPathEdge(fi, si);
+
+ int ei = m_edges.size() - 1;
+
+ QPathVertex *fp = vertex(fi);
+ QPathVertex *sp = vertex(si);
+
+ QPathEdge *ep = edge(ei);
+
+ ep->bezier = bezier;
+ ep->t0 = t0;
+ ep->t1 = t1;
+
+ if (bezier) {
+ QPointF aTangent = bezier->derivedAt(t0);
+ QPointF bTangent = -bezier->derivedAt(t1);
+
+ if (qFuzzyCompare(aTangent.x() + 1, 1) && qFuzzyCompare(aTangent.y() + 1, 1))
+ aTangent = bezier->secondDerivedAt(t0);
+
+ if (qFuzzyCompare(bTangent.x() + 1, 1) && qFuzzyCompare(bTangent.y() + 1, 1))
+ bTangent = bezier->secondDerivedAt(t1);
+
+ ep->angle = computeAngle(aTangent);
+ ep->invAngle = computeAngle(bTangent);
+ } else {
+ const QPointF tangent = QPointF(*sp) - QPointF(*fp);
+ ep->angle = computeAngle(tangent);
+ ep->invAngle = ep->angle + 64;
+ if (ep->invAngle >= 128)
+ ep->invAngle -= 128;
+ }
+
+ QPathVertex *vertices[2] = { fp, sp };
+ QPathEdge::Direction dirs[2] = { QPathEdge::Backward, QPathEdge::Forward };
+
+#ifdef QDEBUG_CLIPPER
+ printf("** Adding edge %d / vertices: %.07f %.07f, %.07f %.07f\n", ei, fp->x, fp->y, sp->x, sp->y);
+#endif
+
+ for (int i = 0; i < 2; ++i) {
+ QPathVertex *vp = vertices[i];
+ if (vp->edge < 0) {
+ vp->edge = ei;
+ ep->setNext(dirs[i], ei);
+ } else {
+ int vi = ep->vertex(dirs[i]);
+ Q_ASSERT(vertex(vi) == vertices[i]);
+
+ TraversalStatus os = findInsertStatus(vi, ei);
+ QPathEdge *op = edge(os.edge);
+
+ Q_ASSERT(vertex(op->vertex(os.direction)) == vertices[i]);
+
+ TraversalStatus ns = next(os);
+ ns.flipDirection();
+ QPathEdge *np = edge(ns.edge);
+
+ op->setNext(os.traversal, os.direction, ei);
+ np->setNext(ns.traversal, ns.direction, ei);
+
+ int oe = os.edge;
+ int ne = ns.edge;
+
+ os = next(os);
+ ns = next(ns);
+
+ os.flipDirection();
+ ns.flipDirection();
+
+ Q_ASSERT(os.edge == ei);
+ Q_ASSERT(ns.edge == ei);
+
+ ep->setNext(os.traversal, os.direction, oe);
+ ep->setNext(ns.traversal, ns.direction, ne);
+ }
+ }
+
+ Q_ASSERT(ep->next(QPathEdge::RightTraversal, QPathEdge::Forward) >= 0);
+ Q_ASSERT(ep->next(QPathEdge::RightTraversal, QPathEdge::Backward) >= 0);
+ Q_ASSERT(ep->next(QPathEdge::LeftTraversal, QPathEdge::Forward) >= 0);
+ Q_ASSERT(ep->next(QPathEdge::LeftTraversal, QPathEdge::Backward) >= 0);
+
+ return ei;
+}
+
+void QWingedEdge::addBezierEdge(const QBezier *bezier, int vertexA, int vertexB, qreal alphaA, qreal alphaB, int path)
+{
+ if (qFuzzyCompare(alphaA, alphaB))
+ return;
+
+ qreal alphaMid = (alphaA + alphaB) * 0.5;
+
+ qreal s0 = 0;
+ qreal s1 = 1;
+ int count = bezier->stationaryYPoints(s0, s1);
+
+ m_splitPoints.clear();
+ m_splitPoints << alphaA;
+ m_splitPoints << alphaMid;
+ m_splitPoints << alphaB;
+
+ if (count > 0 && !qFuzzyCompare(s0, alphaA) && !qFuzzyCompare(s0, alphaMid) && !qFuzzyCompare(s0, alphaB) && s0 > alphaA && s0 < alphaB)
+ m_splitPoints << s0;
+
+ if (count > 1 && !qFuzzyCompare(s1, alphaA) && !qFuzzyCompare(s1, alphaMid) && !qFuzzyCompare(s1, alphaB) && s1 > alphaA && s1 < alphaB)
+ m_splitPoints << s1;
+
+ if (count > 0)
+ qSort(m_splitPoints.begin(), m_splitPoints.end());
+
+ int last = vertexA;
+ for (int i = 0; i < m_splitPoints.size() - 1; ++i) {
+ const qreal t0 = m_splitPoints[i];
+ const qreal t1 = m_splitPoints[i+1];
+
+ int current;
+ if ((i + 1) == (m_splitPoints.size() - 1)) {
+ current = vertexB;
+ } else {
+ current = insert(bezier->pointAt(t1));
+ }
+
+ QPathEdge *ep = edge(addEdge(last, current, bezier, t0, t1));
+
+ if (ep) {
+ const int dir = m_vertices.at(last).y < m_vertices.at(current).y ? 1 : -1;
+ if (path == 0)
+ ep->windingA += dir;
+ else
+ ep->windingB += dir;
+ }
+
+ last = current;
+ }
+}
+
+void QWingedEdge::addBezierEdge(const QBezier *bezier, const QPointF &a, const QPointF &b, qreal alphaA, qreal alphaB, int path)
+{
+ if (qFuzzyCompare(alphaA, alphaB))
+ return;
+
+ if (a == b) {
+ int v = insert(a);
+
+ addBezierEdge(bezier, v, v, alphaA, alphaB, path);
+ } else {
+ int va = insert(a);
+ int vb = insert(b);
+
+ addBezierEdge(bezier, va, vb, alphaA, alphaB, path);
+ }
+}
+
+int QWingedEdge::insert(const QPathVertex &vertex)
+{
+ if (!m_vertices.isEmpty()) {
+ const QPathVertex &last = m_vertices.last();
+ if (vertex.x == last.x && vertex.y == last.y)
+ return m_vertices.size() - 1;
+
+ for (int i = 0; i < m_vertices.size(); ++i) {
+ const QPathVertex &v = m_vertices.at(i);
+ if (qFuzzyCompare(v.x, vertex.x) && qFuzzyCompare(v.y, vertex.y)) {
+ return i;
+ }
+ }
+ }
+
+ m_vertices << vertex;
+ return m_vertices.size() - 1;
+}
+
+static void addLineTo(QPainterPath &path, const QPointF &point)
+{
+ const int elementCount = path.elementCount();
+ if (elementCount >= 2) {
+ const QPainterPath::Element &middle = path.elementAt(elementCount - 1);
+ if (middle.type == QPainterPath::LineToElement) {
+ const QPointF first = path.elementAt(elementCount - 2);
+ const QPointF d1 = point - first;
+ const QPointF d2 = middle - first;
+
+ const QPointF p(-d1.y(), d1.x());
+
+ if (qFuzzyCompare(dot(p, d2) + 1, 1)) {
+ path.setElementPositionAt(elementCount - 1, point.x(), point.y());
+ return;
+ }
+ }
+ }
+
+ path.lineTo(point);
+}
+
+static void add(QPainterPath &path, const QWingedEdge &list, int edge, QPathEdge::Traversal traversal)
+{
+ QWingedEdge::TraversalStatus status;
+ status.edge = edge;
+ status.traversal = traversal;
+ status.direction = QPathEdge::Forward;
+
+ const QBezier *bezier = 0;
+ qreal t0 = 1;
+ qreal t1 = 0;
+ bool forward = true;
+
+ path.moveTo(*list.vertex(list.edge(edge)->first));
+
+ do {
+ const QPathEdge *ep = list.edge(status.edge);
+
+ if (ep->bezier != bezier || (bezier && t0 != ep->t1 && t1 != ep->t0)) {
+ if (bezier) {
+ QBezier sub = bezier->bezierOnInterval(t0, t1);
+
+ if (forward)
+ path.cubicTo(sub.pt2(), sub.pt3(), sub.pt4());
+ else
+ path.cubicTo(sub.pt3(), sub.pt2(), sub.pt1());
+ }
+
+ bezier = ep->bezier;
+ t0 = 1;
+ t1 = 0;
+ forward = status.direction == QPathEdge::Forward;
+ }
+
+ if (ep->bezier) {
+ t0 = qMin(t0, ep->t0);
+ t1 = qMax(t1, ep->t1);
+ } else
+ addLineTo(path, *list.vertex(ep->vertex(status.direction)));
+
+ if (status.traversal == QPathEdge::LeftTraversal)
+ ep->flag &= ~16;
+ else
+ ep->flag &= ~32;
+
+ status = list.next(status);
+ } while (status.edge != edge);
+
+ if (bezier) {
+ QBezier sub = bezier->bezierOnInterval(t0, t1);
+ if (forward)
+ path.cubicTo(sub.pt2(), sub.pt3(), sub.pt4());
+ else
+ path.cubicTo(sub.pt3(), sub.pt2(), sub.pt1());
+ }
+}
+
+void QWingedEdge::simplify()
+{
+ for (int i = 0; i < edgeCount(); ++i) {
+ const QPathEdge *ep = edge(i);
+
+ // if both sides are part of the inside then we can collapse the edge
+ int flag = 0x3 << 4;
+ if ((ep->flag & flag) == flag) {
+ removeEdge(i);
+
+ ep->flag &= ~flag;
+ }
+ }
+}
+
+QPainterPath QWingedEdge::toPath() const
+{
+ QPainterPath path;
+
+ for (int i = 0; i < edgeCount(); ++i) {
+ const QPathEdge *ep = edge(i);
+
+ if (ep->flag & 16) {
+ add(path, *this, i, QPathEdge::LeftTraversal);
+ }
+
+ if (ep->flag & 32)
+ add(path, *this, i, QPathEdge::RightTraversal);
+ }
+
+ return path;
+}
+
+bool QPathClipper::intersect()
+{
+ if (subjectPath == clipPath)
+ return true;
+
+ QRectF r1 = subjectPath.controlPointRect();
+ QRectF r2 = clipPath.controlPointRect();
+ if (qMax(r1.x(), r2.x()) > qMin(r1.x() + r1.width(), r2.x() + r2.width()) ||
+ qMax(r1.y(), r2.y()) > qMin(r1.y() + r1.height(), r2.y() + r2.height())) {
+ // no way we could intersect
+ return false;
+ }
+
+ bool subjectIsRect = pathToRect(subjectPath);
+ bool clipIsRect = pathToRect(clipPath);
+
+ if (subjectIsRect && clipIsRect)
+ return true;
+ else if (subjectIsRect)
+ return clipPath.intersects(r1);
+ else if (clipIsRect)
+ return subjectPath.intersects(r2);
+
+ QPathSegments a;
+ a.setPath(subjectPath);
+ QPathSegments b;
+ b.setPath(clipPath);
+
+ QIntersectionFinder finder;
+ if (finder.hasIntersections(a, b))
+ return true;
+
+ for (int i = 0; i < clipPath.elementCount(); ++i) {
+ if (clipPath.elementAt(i).type == QPainterPath::MoveToElement) {
+ const QPointF point = clipPath.elementAt(i);
+ if (r1.contains(point) && subjectPath.contains(point))
+ return true;
+ }
+ }
+
+ for (int i = 0; i < subjectPath.elementCount(); ++i) {
+ if (subjectPath.elementAt(i).type == QPainterPath::MoveToElement) {
+ const QPointF point = subjectPath.elementAt(i);
+ if (r2.contains(point) && clipPath.contains(point))
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool QPathClipper::contains()
+{
+ if (subjectPath == clipPath)
+ return false;
+
+ QRectF r1 = subjectPath.controlPointRect();
+ QRectF r2 = clipPath.controlPointRect();
+ if (qMax(r1.x(), r2.x()) > qMin(r1.x() + r1.width(), r2.x() + r2.width()) ||
+ qMax(r1.y(), r2.y()) > qMin(r1.y() + r1.height(), r2.y() + r2.height())) {
+ // no intersection -> not contained
+ return false;
+ }
+
+ bool clipIsRect = pathToRect(clipPath);
+ if (clipIsRect)
+ return subjectPath.contains(r2);
+
+ QPathSegments a;
+ a.setPath(subjectPath);
+ QPathSegments b;
+ b.setPath(clipPath);
+
+ QIntersectionFinder finder;
+ if (finder.hasIntersections(a, b))
+ return false;
+
+ for (int i = 0; i < clipPath.elementCount(); ++i) {
+ if (clipPath.elementAt(i).type == QPainterPath::MoveToElement) {
+ const QPointF point = clipPath.elementAt(i);
+ if (!r1.contains(point) || !subjectPath.contains(point))
+ return false;
+ }
+ }
+
+ return true;
+}
+
+QPathClipper::QPathClipper(const QPainterPath &subject,
+ const QPainterPath &clip)
+ : subjectPath(subject)
+ , clipPath(clip)
+{
+ aMask = subjectPath.fillRule() == Qt::WindingFill ? ~0x0 : 0x1;
+ bMask = clipPath.fillRule() == Qt::WindingFill ? ~0x0 : 0x1;
+}
+
+template <typename Iterator, typename Equality>
+Iterator qRemoveDuplicates(Iterator begin, Iterator end, Equality eq)
+{
+ if (begin == end)
+ return end;
+
+ Iterator last = begin;
+ ++begin;
+ Iterator insert = begin;
+ for (Iterator it = begin; it != end; ++it) {
+ if (!eq(*it, *last)) {
+ *insert++ = *it;
+ last = it;
+ }
+ }
+
+ return insert;
+}
+
+static void clear(QWingedEdge& list, int edge, QPathEdge::Traversal traversal)
+{
+ QWingedEdge::TraversalStatus status;
+ status.edge = edge;
+ status.traversal = traversal;
+ status.direction = QPathEdge::Forward;
+
+ do {
+ if (status.traversal == QPathEdge::LeftTraversal)
+ list.edge(status.edge)->flag |= 1;
+ else
+ list.edge(status.edge)->flag |= 2;
+
+ status = list.next(status);
+ } while (status.edge != edge);
+}
+
+template <typename InputIterator>
+InputIterator qFuzzyFind(InputIterator first, InputIterator last, qreal val)
+{
+ while (first != last && !qFuzzyCompare(qreal(*first), qreal(val)))
+ ++first;
+ return first;
+}
+
+static bool fuzzyCompare(qreal a, qreal b)
+{
+ return qFuzzyCompare(a, b);
+}
+
+static bool pathToRect(const QPainterPath &path, QRectF *rect)
+{
+ if (path.elementCount() != 5)
+ return false;
+
+ const bool mightBeRect = path.elementAt(0).isMoveTo()
+ && path.elementAt(1).isLineTo()
+ && path.elementAt(2).isLineTo()
+ && path.elementAt(3).isLineTo()
+ && path.elementAt(4).isLineTo();
+
+ if (!mightBeRect)
+ return false;
+
+ const qreal x1 = path.elementAt(0).x;
+ const qreal y1 = path.elementAt(0).y;
+
+ const qreal x2 = path.elementAt(1).x;
+ const qreal y2 = path.elementAt(2).y;
+
+ if (path.elementAt(1).y != y1)
+ return false;
+
+ if (path.elementAt(2).x != x2)
+ return false;
+
+ if (path.elementAt(3).x != x1 || path.elementAt(3).y != y2)
+ return false;
+
+ if (path.elementAt(4).x != x1 || path.elementAt(4).y != y1)
+ return false;
+
+ if (rect)
+ *rect = QRectF(QPointF(x1, y1), QPointF(x2, y2));
+
+ return true;
+}
+
+
+QPainterPath QPathClipper::clip(Operation operation)
+{
+ op = operation;
+
+ if (op != Simplify) {
+ if (subjectPath == clipPath)
+ return op == BoolSub ? QPainterPath() : subjectPath;
+
+ const QRectF clipBounds = clipPath.boundingRect();
+ const QRectF subjectBounds = subjectPath.boundingRect();
+
+ if (!clipBounds.intersects(subjectBounds)) {
+ switch (op) {
+ case BoolSub:
+ return subjectPath;
+ case BoolAnd:
+ return QPainterPath();
+ case BoolOr: {
+ QPainterPath result = subjectPath;
+ if (result.fillRule() == clipPath.fillRule()) {
+ result.addPath(clipPath);
+ } else if (result.fillRule() == Qt::WindingFill) {
+ result = result.simplified();
+ result.addPath(clipPath);
+ } else {
+ result.addPath(clipPath.simplified());
+ }
+ return result;
+ }
+ default:
+ break;
+ }
+ }
+
+ if (clipBounds.contains(subjectBounds)) {
+ QRectF clipRect;
+ if (pathToRect(clipPath, &clipRect) && clipRect.contains(subjectBounds)) {
+ switch (op) {
+ case BoolSub:
+ return QPainterPath();
+ case BoolAnd:
+ return subjectPath;
+ case BoolOr:
+ return clipPath;
+ default:
+ break;
+ }
+ }
+ } else if (subjectBounds.contains(clipBounds)) {
+ QRectF subjectRect;
+ if (pathToRect(subjectPath, &subjectRect) && subjectRect.contains(clipBounds)) {
+ switch (op) {
+ case BoolSub:
+ if (clipPath.fillRule() == Qt::OddEvenFill) {
+ QPainterPath result = clipPath;
+ result.addRect(subjectRect);
+ return result;
+ } else {
+ QPainterPath result = clipPath.simplified();
+ result.addRect(subjectRect);
+ return result;
+ }
+ break;
+ case BoolAnd:
+ return clipPath;
+ case BoolOr:
+ return subjectPath;
+ default:
+ break;
+ }
+ }
+ }
+ }
+
+ QWingedEdge list(subjectPath, clipPath);
+
+ doClip(list, ClipMode);
+
+ QPainterPath path = list.toPath();
+ return path;
+}
+
+bool QPathClipper::doClip(QWingedEdge &list, ClipperMode mode)
+{
+ QVector<qreal> y_coords;
+ y_coords.reserve(list.vertexCount());
+ for (int i = 0; i < list.vertexCount(); ++i)
+ y_coords << list.vertex(i)->y;
+
+ qSort(y_coords.begin(), y_coords.end());
+ y_coords.resize(qRemoveDuplicates(y_coords.begin(), y_coords.end(), fuzzyCompare) - y_coords.begin());
+
+#ifdef QDEBUG_CLIPPER
+ printf("sorted y coords:\n");
+ for (int i = 0; i < y_coords.size(); ++i) {
+ printf("%.9f\n", y_coords[i]);
+ }
+#endif
+
+ bool found;
+ do {
+ found = false;
+ int index = 0;
+ qreal maxHeight = 0;
+ for (int i = 0; i < list.edgeCount(); ++i) {
+ QPathEdge *edge = list.edge(i);
+
+ // have both sides of this edge already been handled?
+ if ((edge->flag & 0x3) == 0x3)
+ continue;
+
+ QPathVertex *a = list.vertex(edge->first);
+ QPathVertex *b = list.vertex(edge->second);
+
+ if (qFuzzyCompare(a->y, b->y))
+ continue;
+
+ found = true;
+
+ qreal height = qAbs(a->y - b->y);
+ if (height > maxHeight) {
+ index = i;
+ maxHeight = height;
+ }
+ }
+
+ if (found) {
+ QPathEdge *edge = list.edge(index);
+
+ QPathVertex *a = list.vertex(edge->first);
+ QPathVertex *b = list.vertex(edge->second);
+
+ // FIXME: this can be optimized by using binary search
+ const int first = qFuzzyFind(y_coords.begin(), y_coords.end(), qMin(a->y, b->y)) - y_coords.begin();
+ const int last = qFuzzyFind(y_coords.begin() + first, y_coords.end(), qMax(a->y, b->y)) - y_coords.begin();
+
+ Q_ASSERT(first < y_coords.size() - 1);
+ Q_ASSERT(last < y_coords.size());
+
+ qreal bestY = 0.5 * (y_coords[first] + y_coords[first+1]);
+ qreal biggestGap = y_coords[first+1] - y_coords[first];
+
+ for (int i = first + 1; i < last; ++i) {
+ qreal gap = y_coords[i+1] - y_coords[i];
+
+ if (gap > biggestGap) {
+ bestY = 0.5 * (y_coords[i] + y_coords[i+1]);
+ biggestGap = gap;
+ }
+ }
+
+#ifdef QDEBUG_CLIPPER
+ printf("y: %.9f, gap: %.9f\n", bestY, biggestGap);
+#endif
+
+ if (handleCrossingEdges(list, bestY, mode) && mode == CheckMode)
+ return true;
+
+ edge->flag |= 0x3;
+ }
+ } while (found);
+
+ if (mode == ClipMode)
+ list.simplify();
+
+ return false;
+}
+
+static void traverse(QWingedEdge &list, int edge, QPathEdge::Traversal traversal)
+{
+ QWingedEdge::TraversalStatus status;
+ status.edge = edge;
+ status.traversal = traversal;
+ status.direction = QPathEdge::Forward;
+
+ do {
+ int flag = status.traversal == QPathEdge::LeftTraversal ? 1 : 2;
+
+ QPathEdge *ep = list.edge(status.edge);
+
+ ep->flag |= (flag | (flag << 4));
+
+#ifdef QDEBUG_CLIPPER
+ qDebug() << "traverse: adding edge " << status.edge << ", mask:" << (flag << 4) <<ep->flag;
+#endif
+
+ status = list.next(status);
+ } while (status.edge != edge);
+}
+
+struct QCrossingEdge
+{
+ int edge;
+ qreal x;
+
+ bool operator<(const QCrossingEdge &edge) const
+ {
+ return x < edge.x;
+ }
+};
+
+static bool bool_op(bool a, bool b, QPathClipper::Operation op)
+{
+ switch (op) {
+ case QPathClipper::BoolAnd:
+ return a && b;
+ case QPathClipper::BoolOr: // fall-through
+ case QPathClipper::Simplify:
+ return a || b;
+ case QPathClipper::BoolSub:
+ return a && !b;
+ default:
+ Q_ASSERT(false);
+ return false;
+ }
+}
+
+bool QWingedEdge::isInside(qreal x, qreal y) const
+{
+ int winding = 0;
+ for (int i = 0; i < edgeCount(); ++i) {
+ const QPathEdge *ep = edge(i);
+
+ // left xor right
+ int w = ((ep->flag >> 4) ^ (ep->flag >> 5)) & 1;
+
+ if (!w)
+ continue;
+
+ QPointF a = *vertex(ep->first);
+ QPointF b = *vertex(ep->second);
+
+ if ((a.y() < y && b.y() > y) || (a.y() > y && b.y() < y)) {
+ if (ep->bezier) {
+ qreal maxX = qMax(a.x(), qMax(b.x(), qMax(ep->bezier->x2, ep->bezier->x3)));
+ qreal minX = qMin(a.x(), qMin(b.x(), qMin(ep->bezier->x2, ep->bezier->x3)));
+
+ if (minX > x) {
+ winding += w;
+ } else if (maxX > x) {
+ const qreal t = ep->bezier->tForY(ep->t0, ep->t1, y);
+ const qreal intersection = ep->bezier->pointAt(t).x();
+
+ if (intersection > x)
+ winding += w;
+ }
+ } else {
+ qreal intersectionX = a.x() + (b.x() - a.x()) * (y - a.y()) / (b.y() - a.y());
+
+ if (intersectionX > x)
+ winding += w;
+ }
+ }
+ }
+
+ return winding & 1;
+}
+
+static QVector<QCrossingEdge> findCrossings(const QWingedEdge &list, qreal y)
+{
+ QVector<QCrossingEdge> crossings;
+ for (int i = 0; i < list.edgeCount(); ++i) {
+ const QPathEdge *edge = list.edge(i);
+ QPointF a = *list.vertex(edge->first);
+ QPointF b = *list.vertex(edge->second);
+
+ if ((a.y() < y && b.y() > y) || (a.y() > y && b.y() < y)) {
+ if (edge->bezier) {
+ const qreal t = edge->bezier->tForY(edge->t0, edge->t1, y);
+ const qreal intersection = edge->bezier->pointAt(t).x();
+
+ const QCrossingEdge edge = { i, intersection };
+ crossings << edge;
+ } else {
+ const qreal intersection = a.x() + (b.x() - a.x()) * (y - a.y()) / (b.y() - a.y());
+ const QCrossingEdge edge = { i, intersection };
+ crossings << edge;
+ }
+ }
+ }
+ return crossings;
+}
+
+bool QPathClipper::handleCrossingEdges(QWingedEdge &list, qreal y, ClipperMode mode)
+{
+ QVector<QCrossingEdge> crossings = findCrossings(list, y);
+
+ Q_ASSERT(!crossings.isEmpty());
+ qSort(crossings.begin(), crossings.end());
+
+ int windingA = 0;
+ int windingB = 0;
+
+ int windingD = 0;
+
+#ifdef QDEBUG_CLIPPER
+ qDebug() << "crossings:" << crossings.size();
+#endif
+ for (int i = 0; i < crossings.size() - 1; ++i) {
+ int ei = crossings.at(i).edge;
+ const QPathEdge *edge = list.edge(ei);
+
+ windingA += edge->windingA;
+ windingB += edge->windingB;
+
+ const bool hasLeft = (edge->flag >> 4) & 1;
+ const bool hasRight = (edge->flag >> 4) & 2;
+
+ windingD += hasLeft ^ hasRight;
+
+ const bool inA = (windingA & aMask) != 0;
+ const bool inB = (windingB & bMask) != 0;
+ const bool inD = (windingD & 0x1) != 0;
+
+ const bool inside = bool_op(inA, inB, op);
+ const bool add = inD ^ inside;
+
+#ifdef QDEBUG_CLIPPER
+ printf("y %f, x %f, inA: %d, inB: %d, inD: %d, inside: %d, flag: %x, bezier: %p, edge: %d\n", y, crossings.at(i).x, inA, inB, inD, inside, edge->flag, edge->bezier, ei);
+#endif
+
+ if (add) {
+ if (mode == CheckMode)
+ return true;
+
+ qreal y0 = list.vertex(edge->first)->y;
+ qreal y1 = list.vertex(edge->second)->y;
+
+ if (y0 < y1) {
+ if (!(edge->flag & 1))
+ traverse(list, ei, QPathEdge::LeftTraversal);
+
+ if (!(edge->flag & 2))
+ clear(list, ei, QPathEdge::RightTraversal);
+ } else {
+ if (!(edge->flag & 1))
+ clear(list, ei, QPathEdge::LeftTraversal);
+
+ if (!(edge->flag & 2))
+ traverse(list, ei, QPathEdge::RightTraversal);
+ }
+
+ ++windingD;
+ } else {
+ if (!(edge->flag & 1))
+ clear(list, ei, QPathEdge::LeftTraversal);
+
+ if (!(edge->flag & 2))
+ clear(list, ei, QPathEdge::RightTraversal);
+ }
+ }
+
+ return false;
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/painting/qpathclipper_p.h b/src/gui/painting/qpathclipper_p.h
new file mode 100644
index 0000000000..981ca845de
--- /dev/null
+++ b/src/gui/painting/qpathclipper_p.h
@@ -0,0 +1,519 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QPATHCLIPPER_P_H
+#define QPATHCLIPPER_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 <QtGui/qpainterpath.h>
+#include <QtCore/qlist.h>
+
+#include <private/qbezier_p.h>
+#include <private/qdatabuffer_p.h>
+#include <stdio.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+class QWingedEdge;
+
+class Q_AUTOTEST_EXPORT QPathClipper
+{
+public:
+ enum Operation {
+ BoolAnd,
+ BoolOr,
+ BoolSub,
+ Simplify
+ };
+public:
+ QPathClipper(const QPainterPath &subject,
+ const QPainterPath &clip);
+
+ QPainterPath clip(Operation op = BoolAnd);
+
+ bool intersect();
+ bool contains();
+
+private:
+ Q_DISABLE_COPY(QPathClipper)
+
+ enum ClipperMode {
+ ClipMode, // do the full clip
+ CheckMode // for contains/intersects (only interested in whether the result path is non-empty)
+ };
+
+ bool handleCrossingEdges(QWingedEdge &list, qreal y, ClipperMode mode);
+ bool doClip(QWingedEdge &list, ClipperMode mode);
+
+ QPainterPath subjectPath;
+ QPainterPath clipPath;
+ Operation op;
+
+ int aMask;
+ int bMask;
+};
+
+struct QPathVertex
+{
+public:
+ QPathVertex(const QPointF &p = QPointF(), int e = -1);
+ operator QPointF() const;
+
+ int edge;
+
+ qreal x;
+ qreal y;
+};
+
+class QPathEdge
+{
+public:
+ enum Traversal {
+ RightTraversal,
+ LeftTraversal
+ };
+
+ enum Direction {
+ Forward,
+ Backward
+ };
+
+ enum Type {
+ Line,
+ Curve
+ };
+
+ QPathEdge(int a = -1, int b = -1);
+
+ mutable int flag;
+
+ int windingA;
+ int windingB;
+
+ int first;
+ int second;
+
+ qreal angle;
+ qreal invAngle;
+
+ const QBezier *bezier;
+ qreal t0;
+ qreal t1;
+
+ int next(Traversal traversal, Direction direction) const;
+
+ void setNext(Traversal traversal, Direction direction, int next);
+ void setNext(Direction direction, int next);
+
+ Direction directionTo(int vertex) const;
+ int vertex(Direction direction) const;
+
+ bool isBezier() const;
+
+private:
+ int m_next[2][2];
+};
+
+class QPathSegments
+{
+public:
+ struct Intersection {
+ int vertex;
+ qreal t;
+
+ int next;
+
+ bool operator<(const Intersection &o) const {
+ return t < o.t;
+ }
+ };
+
+ struct Segment {
+ Segment(int pathId, int vertexA, int vertexB, int bezierIndex = -1)
+ : path(pathId)
+ , bezier(bezierIndex)
+ , va(vertexA)
+ , vb(vertexB)
+ , intersection(-1)
+ {
+ }
+
+ int path;
+ int bezier;
+
+ // vertices
+ int va;
+ int vb;
+
+ // intersection index
+ int intersection;
+
+ QRectF bounds;
+ };
+
+
+ QPathSegments();
+
+ void setPath(const QPainterPath &path);
+ void addPath(const QPainterPath &path);
+
+ int intersections() const;
+ int segments() const;
+ int points() const;
+
+ const Segment &segmentAt(int index) const;
+ const QLineF lineAt(int index) const;
+ const QBezier *bezierAt(int index) const;
+ const QRectF &elementBounds(int index) const;
+ int pathId(int index) const;
+
+ const QPointF &pointAt(int vertex) const;
+ int addPoint(const QPointF &point);
+
+ const Intersection *intersectionAt(int index) const;
+ void addIntersection(int index, const Intersection &intersection);
+
+ void mergePoints();
+
+private:
+ QDataBuffer<QPointF> m_points;
+ QDataBuffer<Segment> m_segments;
+ QDataBuffer<QBezier> m_beziers;
+ QDataBuffer<Intersection> m_intersections;
+
+ int m_pathId;
+};
+
+class Q_AUTOTEST_EXPORT QWingedEdge
+{
+public:
+ struct TraversalStatus
+ {
+ int edge;
+ QPathEdge::Traversal traversal;
+ QPathEdge::Direction direction;
+
+ void flipDirection();
+ void flipTraversal();
+
+ void flip();
+ };
+
+ QWingedEdge();
+ QWingedEdge(const QPainterPath &subject, const QPainterPath &clip);
+
+ void simplify();
+ QPainterPath toPath() const;
+
+ int edgeCount() const;
+
+ QPathEdge *edge(int edge);
+ const QPathEdge *edge(int edge) const;
+
+ int vertexCount() const;
+
+ int addVertex(const QPointF &p);
+
+ QPathVertex *vertex(int vertex);
+ const QPathVertex *vertex(int vertex) const;
+
+ TraversalStatus next(const TraversalStatus &status) const;
+
+ int addEdge(const QPointF &a, const QPointF &b, const QBezier *bezier = 0, qreal t0 = 0, qreal t1 = 1);
+ int addEdge(int vertexA, int vertexB, const QBezier *bezier = 0, qreal t0 = 0, qreal t1 = 1);
+
+ bool isInside(qreal x, qreal y) const;
+
+ static QPathEdge::Traversal flip(QPathEdge::Traversal traversal);
+ static QPathEdge::Direction flip(QPathEdge::Direction direction);
+
+private:
+ void intersectAndAdd();
+
+ void printNode(int i, FILE *handle);
+
+ QBezier bezierFromIndex(int index) const;
+
+ void removeEdge(int ei);
+ void addBezierEdge(const QBezier *bezier, const QPointF &a, const QPointF &b, qreal alphaA, qreal alphaB, int path);
+ void addBezierEdge(const QBezier *bezier, int vertexA, int vertexB, qreal alphaA, qreal alphaB, int path);
+
+ int insert(const QPathVertex &vertex);
+ TraversalStatus findInsertStatus(int vertex, int edge) const;
+
+ qreal delta(int vertex, int a, int b) const;
+
+ QDataBuffer<QPathEdge> m_edges;
+ QDataBuffer<QPathVertex> m_vertices;
+
+ QVector<qreal> m_splitPoints;
+
+ QPathSegments m_segments;
+};
+
+inline QPathEdge::QPathEdge(int a, int b)
+ : flag(0)
+ , windingA(0)
+ , windingB(0)
+ , first(a)
+ , second(b)
+ , angle(0)
+ , invAngle(0)
+ , bezier(0)
+ , t0(0)
+ , t1(0)
+{
+ m_next[0][0] = -1;
+ m_next[1][0] = -1;
+ m_next[0][0] = -1;
+ m_next[1][0] = -1;
+}
+
+inline int QPathEdge::next(Traversal traversal, Direction direction) const
+{
+ return m_next[int(traversal)][int(direction)];
+}
+
+inline void QPathEdge::setNext(Traversal traversal, Direction direction, int next)
+{
+ m_next[int(traversal)][int(direction)] = next;
+}
+
+inline void QPathEdge::setNext(Direction direction, int next)
+{
+ m_next[0][int(direction)] = next;
+ m_next[1][int(direction)] = next;
+}
+
+inline QPathEdge::Direction QPathEdge::directionTo(int vertex) const
+{
+ return first == vertex ? Backward : Forward;
+}
+
+inline int QPathEdge::vertex(Direction direction) const
+{
+ return direction == Backward ? first : second;
+}
+
+inline bool QPathEdge::isBezier() const
+{
+ return bezier >= 0;
+}
+
+inline QPathVertex::QPathVertex(const QPointF &p, int e)
+ : edge(e)
+ , x(p.x())
+ , y(p.y())
+{
+}
+
+inline QPathVertex::operator QPointF() const
+{
+ return QPointF(x, y);
+}
+
+inline QPathSegments::QPathSegments()
+{
+}
+
+inline int QPathSegments::segments() const
+{
+ return m_segments.size();
+}
+
+inline int QPathSegments::points() const
+{
+ return m_points.size();
+}
+
+inline const QPointF &QPathSegments::pointAt(int i) const
+{
+ return m_points.at(i);
+}
+
+inline int QPathSegments::addPoint(const QPointF &point)
+{
+ m_points << point;
+ return m_points.size() - 1;
+}
+
+inline const QPathSegments::Segment &QPathSegments::segmentAt(int index) const
+{
+ return m_segments.at(index);
+}
+
+inline const QLineF QPathSegments::lineAt(int index) const
+{
+ const Segment &segment = m_segments.at(index);
+ return QLineF(m_points.at(segment.va), m_points.at(segment.vb));
+}
+
+inline const QBezier *QPathSegments::bezierAt(int index) const
+{
+ const Segment &segment = m_segments.at(index);
+ if (segment.bezier >= 0)
+ return &m_beziers.at(segment.bezier);
+ else
+ return 0;
+}
+
+inline const QRectF &QPathSegments::elementBounds(int index) const
+{
+ return m_segments.at(index).bounds;
+}
+
+inline int QPathSegments::pathId(int index) const
+{
+ return m_segments.at(index).path;
+}
+
+inline const QPathSegments::Intersection *QPathSegments::intersectionAt(int index) const
+{
+ const int intersection = m_segments.at(index).intersection;
+ if (intersection < 0)
+ return 0;
+ else
+ return &m_intersections.at(intersection);
+}
+
+inline int QPathSegments::intersections() const
+{
+ return m_intersections.size();
+}
+
+inline void QPathSegments::addIntersection(int index, const Intersection &intersection)
+{
+ m_intersections << intersection;
+
+ Segment &segment = m_segments.at(index);
+ if (segment.intersection < 0) {
+ segment.intersection = m_intersections.size() - 1;
+ } else {
+ Intersection *isect = &m_intersections.at(segment.intersection);
+
+ while (isect->next != 0)
+ isect += isect->next;
+
+ isect->next = (m_intersections.size() - 1) - (isect - m_intersections.data());
+ }
+}
+
+inline void QWingedEdge::TraversalStatus::flipDirection()
+{
+ direction = QWingedEdge::flip(direction);
+}
+
+inline void QWingedEdge::TraversalStatus::flipTraversal()
+{
+ traversal = QWingedEdge::flip(traversal);
+}
+
+inline void QWingedEdge::TraversalStatus::flip()
+{
+ flipDirection();
+ flipTraversal();
+}
+
+inline int QWingedEdge::edgeCount() const
+{
+ return m_edges.size();
+}
+
+inline QPathEdge *QWingedEdge::edge(int edge)
+{
+ return edge < 0 ? 0 : &m_edges.at(edge);
+}
+
+inline const QPathEdge *QWingedEdge::edge(int edge) const
+{
+ return edge < 0 ? 0 : &m_edges.at(edge);
+}
+
+inline int QWingedEdge::vertexCount() const
+{
+ return m_vertices.size();
+}
+
+inline int QWingedEdge::addVertex(const QPointF &p)
+{
+ m_vertices << p;
+ return m_vertices.size() - 1;
+}
+
+inline QPathVertex *QWingedEdge::vertex(int vertex)
+{
+ return vertex < 0 ? 0 : &m_vertices.at(vertex);
+}
+
+inline const QPathVertex *QWingedEdge::vertex(int vertex) const
+{
+ return vertex < 0 ? 0 : &m_vertices.at(vertex);
+}
+
+inline QPathEdge::Traversal QWingedEdge::flip(QPathEdge::Traversal traversal)
+{
+ return traversal == QPathEdge::RightTraversal ? QPathEdge::LeftTraversal : QPathEdge::RightTraversal;
+}
+
+inline QPathEdge::Direction QWingedEdge::flip(QPathEdge::Direction direction)
+{
+ return direction == QPathEdge::Forward ? QPathEdge::Backward : QPathEdge::Forward;
+}
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QPATHCLIPPER_P_H
diff --git a/src/gui/painting/qpdf.cpp b/src/gui/painting/qpdf.cpp
new file mode 100644
index 0000000000..b01020900c
--- /dev/null
+++ b/src/gui/painting/qpdf.cpp
@@ -0,0 +1,2087 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#include "qplatformdefs.h"
+#include <qdebug.h>
+#include "qpdf_p.h"
+#include <qfile.h>
+#include <qtemporaryfile.h>
+#include <private/qmath_p.h>
+#include "private/qcups_p.h"
+#include "qprinterinfo.h"
+#include <qnumeric.h>
+
+QT_BEGIN_NAMESPACE
+
+extern int qt_defaultDpi();
+
+#ifndef QT_NO_PRINTER
+
+extern QSizeF qt_paperSizeToQSizeF(QPrinter::PaperSize size);
+
+/* also adds a space at the end of the number */
+const char *qt_real_to_string(qreal val, char *buf) {
+ const char *ret = buf;
+
+ if (qIsNaN(val)) {
+ *(buf++) = '0';
+ *(buf++) = ' ';
+ *buf = 0;
+ return ret;
+ }
+
+ if (val < 0) {
+ *(buf++) = '-';
+ val = -val;
+ }
+ unsigned int ival = (unsigned int) val;
+ qreal frac = val - (qreal)ival;
+
+ int ifrac = (int)(frac * 1000000);
+ if (ifrac == 1000000) {
+ ++ival;
+ ifrac = 0;
+ }
+ char output[256];
+ int i = 0;
+ while (ival) {
+ output[i] = '0' + (ival % 10);
+ ++i;
+ ival /= 10;
+ }
+ int fact = 100000;
+ if (i == 0) {
+ *(buf++) = '0';
+ } else {
+ while (i) {
+ *(buf++) = output[--i];
+ fact /= 10;
+ ifrac /= 10;
+ }
+ }
+
+ if (ifrac) {
+ *(buf++) = '.';
+ while (fact) {
+ *(buf++) = '0' + ((ifrac/fact) % 10);
+ fact /= 10;
+ }
+ }
+ *(buf++) = ' ';
+ *buf = 0;
+ return ret;
+}
+
+const char *qt_int_to_string(int val, char *buf) {
+ const char *ret = buf;
+ if (val < 0) {
+ *(buf++) = '-';
+ val = -val;
+ }
+ char output[256];
+ int i = 0;
+ while (val) {
+ output[i] = '0' + (val % 10);
+ ++i;
+ val /= 10;
+ }
+ if (i == 0) {
+ *(buf++) = '0';
+ } else {
+ while (i)
+ *(buf++) = output[--i];
+ }
+ *(buf++) = ' ';
+ *buf = 0;
+ return ret;
+}
+
+
+namespace QPdf {
+ ByteStream::ByteStream(QByteArray *byteArray, bool fileBacking)
+ : dev(new QBuffer(byteArray)),
+ fileBackingEnabled(fileBacking),
+ fileBackingActive(false),
+ handleDirty(false)
+ {
+ dev->open(QIODevice::ReadWrite);
+ }
+
+ ByteStream::ByteStream(bool fileBacking)
+ : dev(new QBuffer(&ba)),
+ fileBackingEnabled(fileBacking),
+ fileBackingActive(false),
+ handleDirty(false)
+ {
+ dev->open(QIODevice::ReadWrite);
+ }
+
+ ByteStream::~ByteStream()
+ {
+ delete dev;
+ }
+
+ ByteStream &ByteStream::operator <<(char chr)
+ {
+ if (handleDirty) prepareBuffer();
+ dev->write(&chr, 1);
+ return *this;
+ }
+
+ ByteStream &ByteStream::operator <<(const char *str)
+ {
+ if (handleDirty) prepareBuffer();
+ dev->write(str, strlen(str));
+ return *this;
+ }
+
+ ByteStream &ByteStream::operator <<(const QByteArray &str)
+ {
+ if (handleDirty) prepareBuffer();
+ dev->write(str);
+ return *this;
+ }
+
+ ByteStream &ByteStream::operator <<(const ByteStream &src)
+ {
+ Q_ASSERT(!src.dev->isSequential());
+ if (handleDirty) prepareBuffer();
+ // We do play nice here, even though it looks ugly.
+ // We save the position and restore it afterwards.
+ ByteStream &s = const_cast<ByteStream&>(src);
+ qint64 pos = s.dev->pos();
+ s.dev->reset();
+ while (!s.dev->atEnd()) {
+ QByteArray buf = s.dev->read(chunkSize());
+ dev->write(buf);
+ }
+ s.dev->seek(pos);
+ return *this;
+ }
+
+ ByteStream &ByteStream::operator <<(qreal val) {
+ char buf[256];
+ qt_real_to_string(val, buf);
+ *this << buf;
+ return *this;
+ }
+
+ ByteStream &ByteStream::operator <<(int val) {
+ char buf[256];
+ qt_int_to_string(val, buf);
+ *this << buf;
+ return *this;
+ }
+
+ ByteStream &ByteStream::operator <<(const QPointF &p) {
+ char buf[256];
+ qt_real_to_string(p.x(), buf);
+ *this << buf;
+ qt_real_to_string(p.y(), buf);
+ *this << buf;
+ return *this;
+ }
+
+ QIODevice *ByteStream::stream()
+ {
+ dev->reset();
+ handleDirty = true;
+ return dev;
+ }
+
+ void ByteStream::clear()
+ {
+ dev->open(QIODevice::ReadWrite | QIODevice::Truncate);
+ }
+
+ void ByteStream::constructor_helper(QByteArray *ba)
+ {
+ delete dev;
+ dev = new QBuffer(ba);
+ dev->open(QIODevice::ReadWrite);
+ }
+
+ void ByteStream::prepareBuffer()
+ {
+ Q_ASSERT(!dev->isSequential());
+ qint64 size = dev->size();
+ if (fileBackingEnabled && !fileBackingActive
+ && size > maxMemorySize()) {
+ // Switch to file backing.
+ QTemporaryFile *newFile = new QTemporaryFile;
+ newFile->open();
+ dev->reset();
+ while (!dev->atEnd()) {
+ QByteArray buf = dev->read(chunkSize());
+ newFile->write(buf);
+ }
+ delete dev;
+ dev = newFile;
+ ba.clear();
+ fileBackingActive = true;
+ }
+ if (dev->pos() != size) {
+ dev->seek(size);
+ handleDirty = false;
+ }
+ }
+}
+
+#define QT_PATH_ELEMENT(elm)
+
+QByteArray QPdf::generatePath(const QPainterPath &path, const QTransform &matrix, PathFlags flags)
+{
+ QByteArray result;
+ if (!path.elementCount())
+ return result;
+
+ ByteStream s(&result);
+
+ int start = -1;
+ for (int i = 0; i < path.elementCount(); ++i) {
+ const QPainterPath::Element &elm = path.elementAt(i);
+ switch (elm.type) {
+ case QPainterPath::MoveToElement:
+ if (start >= 0
+ && path.elementAt(start).x == path.elementAt(i-1).x
+ && path.elementAt(start).y == path.elementAt(i-1).y)
+ s << "h\n";
+ s << matrix.map(QPointF(elm.x, elm.y)) << "m\n";
+ start = i;
+ break;
+ case QPainterPath::LineToElement:
+ s << matrix.map(QPointF(elm.x, elm.y)) << "l\n";
+ break;
+ case QPainterPath::CurveToElement:
+ Q_ASSERT(path.elementAt(i+1).type == QPainterPath::CurveToDataElement);
+ Q_ASSERT(path.elementAt(i+2).type == QPainterPath::CurveToDataElement);
+ s << matrix.map(QPointF(elm.x, elm.y))
+ << matrix.map(QPointF(path.elementAt(i+1).x, path.elementAt(i+1).y))
+ << matrix.map(QPointF(path.elementAt(i+2).x, path.elementAt(i+2).y))
+ << "c\n";
+ i += 2;
+ break;
+ default:
+ qFatal("QPdf::generatePath(), unhandled type: %d", elm.type);
+ }
+ }
+ if (start >= 0
+ && path.elementAt(start).x == path.elementAt(path.elementCount()-1).x
+ && path.elementAt(start).y == path.elementAt(path.elementCount()-1).y)
+ s << "h\n";
+
+ Qt::FillRule fillRule = path.fillRule();
+
+ const char *op = 0;
+ switch (flags) {
+ case ClipPath:
+ op = (fillRule == Qt::WindingFill) ? "W n\n" : "W* n\n";
+ break;
+ case FillPath:
+ op = (fillRule == Qt::WindingFill) ? "f\n" : "f*\n";
+ break;
+ case StrokePath:
+ op = "S\n";
+ break;
+ case FillAndStrokePath:
+ op = (fillRule == Qt::WindingFill) ? "B\n" : "B*\n";
+ break;
+ }
+ s << op;
+ return result;
+}
+
+QByteArray QPdf::generateMatrix(const QTransform &matrix)
+{
+ QByteArray result;
+ ByteStream s(&result);
+ s << matrix.m11()
+ << matrix.m12()
+ << matrix.m21()
+ << matrix.m22()
+ << matrix.dx()
+ << matrix.dy()
+ << "cm\n";
+ return result;
+}
+
+QByteArray QPdf::generateDashes(const QPen &pen)
+{
+ QByteArray result;
+ ByteStream s(&result);
+ s << "[";
+
+ QVector<qreal> dasharray = pen.dashPattern();
+ qreal w = pen.widthF();
+ if (w < 0.001)
+ w = 1;
+ for (int i = 0; i < dasharray.size(); ++i) {
+ qreal dw = dasharray.at(i)*w;
+ if (dw < 0.0001) dw = 0.0001;
+ s << dw;
+ }
+ s << "]";
+ //qDebug() << "dasharray: pen has" << dasharray;
+ //qDebug() << " => " << result;
+ return result;
+}
+
+
+
+static const char* pattern_for_brush[] = {
+ 0, // NoBrush
+ 0, // SolidPattern
+ "0 J\n"
+ "6 w\n"
+ "[] 0 d\n"
+ "4 0 m\n"
+ "4 8 l\n"
+ "0 4 m\n"
+ "8 4 l\n"
+ "S\n", // Dense1Pattern
+
+ "0 J\n"
+ "2 w\n"
+ "[6 2] 1 d\n"
+ "0 0 m\n"
+ "0 8 l\n"
+ "8 0 m\n"
+ "8 8 l\n"
+ "S\n"
+ "[] 0 d\n"
+ "2 0 m\n"
+ "2 8 l\n"
+ "6 0 m\n"
+ "6 8 l\n"
+ "S\n"
+ "[6 2] -3 d\n"
+ "4 0 m\n"
+ "4 8 l\n"
+ "S\n", // Dense2Pattern
+
+ "0 J\n"
+ "2 w\n"
+ "[6 2] 1 d\n"
+ "0 0 m\n"
+ "0 8 l\n"
+ "8 0 m\n"
+ "8 8 l\n"
+ "S\n"
+ "[2 2] -1 d\n"
+ "2 0 m\n"
+ "2 8 l\n"
+ "6 0 m\n"
+ "6 8 l\n"
+ "S\n"
+ "[6 2] -3 d\n"
+ "4 0 m\n"
+ "4 8 l\n"
+ "S\n", // Dense3Pattern
+
+ "0 J\n"
+ "2 w\n"
+ "[2 2] 1 d\n"
+ "0 0 m\n"
+ "0 8 l\n"
+ "8 0 m\n"
+ "8 8 l\n"
+ "S\n"
+ "[2 2] -1 d\n"
+ "2 0 m\n"
+ "2 8 l\n"
+ "6 0 m\n"
+ "6 8 l\n"
+ "S\n"
+ "[2 2] 1 d\n"
+ "4 0 m\n"
+ "4 8 l\n"
+ "S\n", // Dense4Pattern
+
+ "0 J\n"
+ "2 w\n"
+ "[2 6] -1 d\n"
+ "0 0 m\n"
+ "0 8 l\n"
+ "8 0 m\n"
+ "8 8 l\n"
+ "S\n"
+ "[2 2] 1 d\n"
+ "2 0 m\n"
+ "2 8 l\n"
+ "6 0 m\n"
+ "6 8 l\n"
+ "S\n"
+ "[2 6] 3 d\n"
+ "4 0 m\n"
+ "4 8 l\n"
+ "S\n", // Dense5Pattern
+
+ "0 J\n"
+ "2 w\n"
+ "[2 6] -1 d\n"
+ "0 0 m\n"
+ "0 8 l\n"
+ "8 0 m\n"
+ "8 8 l\n"
+ "S\n"
+ "[2 6] 3 d\n"
+ "4 0 m\n"
+ "4 8 l\n"
+ "S\n", // Dense6Pattern
+
+ "0 J\n"
+ "2 w\n"
+ "[2 6] -1 d\n"
+ "0 0 m\n"
+ "0 8 l\n"
+ "8 0 m\n"
+ "8 8 l\n"
+ "S\n", // Dense7Pattern
+
+ "1 w\n"
+ "0 4 m\n"
+ "8 4 l\n"
+ "S\n", // HorPattern
+
+ "1 w\n"
+ "4 0 m\n"
+ "4 8 l\n"
+ "S\n", // VerPattern
+
+ "1 w\n"
+ "4 0 m\n"
+ "4 8 l\n"
+ "0 4 m\n"
+ "8 4 l\n"
+ "S\n", // CrossPattern
+
+ "1 w\n"
+ "-1 5 m\n"
+ "5 -1 l\n"
+ "3 9 m\n"
+ "9 3 l\n"
+ "S\n", // BDiagPattern
+
+ "1 w\n"
+ "-1 3 m\n"
+ "5 9 l\n"
+ "3 -1 m\n"
+ "9 5 l\n"
+ "S\n", // FDiagPattern
+
+ "1 w\n"
+ "-1 3 m\n"
+ "5 9 l\n"
+ "3 -1 m\n"
+ "9 5 l\n"
+ "-1 5 m\n"
+ "5 -1 l\n"
+ "3 9 m\n"
+ "9 3 l\n"
+ "S\n", // DiagCrossPattern
+};
+
+QByteArray QPdf::patternForBrush(const QBrush &b)
+{
+ int style = b.style();
+ if (style > Qt::DiagCrossPattern)
+ return QByteArray();
+ return pattern_for_brush[style];
+}
+
+#ifdef USE_NATIVE_GRADIENTS
+static void writeTriangleLine(uchar *&data, int xpos, int ypos, int xoff, int yoff, uint rgb, uchar flag, bool alpha)
+{
+ data[0] = flag;
+ data[1] = (uchar)(xpos >> 16);
+ data[2] = (uchar)(xpos >> 8);
+ data[3] = (uchar)(xpos >> 0);
+ data[4] = (uchar)(ypos >> 16);
+ data[5] = (uchar)(ypos >> 8);
+ data[6] = (uchar)(ypos >> 0);
+ data += 7;
+ if (alpha) {
+ *data++ = (uchar)qAlpha(rgb);
+ } else {
+ *data++ = (uchar)qRed(rgb);
+ *data++ = (uchar)qGreen(rgb);
+ *data++ = (uchar)qBlue(rgb);
+ }
+ xpos += xoff;
+ ypos += yoff;
+ data[0] = flag;
+ data[1] = (uchar)(xpos >> 16);
+ data[2] = (uchar)(xpos >> 8);
+ data[3] = (uchar)(xpos >> 0);
+ data[4] = (uchar)(ypos >> 16);
+ data[5] = (uchar)(ypos >> 8);
+ data[6] = (uchar)(ypos >> 0);
+ data += 7;
+ if (alpha) {
+ *data++ = (uchar)qAlpha(rgb);
+ } else {
+ *data++ = (uchar)qRed(rgb);
+ *data++ = (uchar)qGreen(rgb);
+ *data++ = (uchar)qBlue(rgb);
+ }
+}
+
+
+QByteArray QPdf::generateLinearGradientShader(const QLinearGradient *gradient, const QPointF *page_rect, bool alpha)
+{
+ // generate list of triangles with colors
+ QPointF start = gradient->start();
+ QPointF stop = gradient->finalStop();
+ QGradientStops stops = gradient->stops();
+ QPointF offset = stop - start;
+ QGradient::Spread spread = gradient->spread();
+
+ if (gradient->spread() == QGradient::ReflectSpread) {
+ offset *= 2;
+ for (int i = stops.size() - 2; i >= 0; --i) {
+ QGradientStop stop = stops.at(i);
+ stop.first = 2. - stop.first;
+ stops.append(stop);
+ }
+ for (int i = 0 ; i < stops.size(); ++i)
+ stops[i].first /= 2.;
+ }
+
+ QPointF orthogonal(offset.y(), -offset.x());
+ qreal length = offset.x()*offset.x() + offset.y()*offset.y();
+
+ // find the max and min values in offset and orth direction that are needed to cover
+ // the whole page
+ int off_min = INT_MAX;
+ int off_max = INT_MIN;
+ qreal ort_min = INT_MAX;
+ qreal ort_max = INT_MIN;
+ for (int i = 0; i < 4; ++i) {
+ qreal off = ((page_rect[i].x() - start.x()) * offset.x() + (page_rect[i].y() - start.y()) * offset.y())/length;
+ qreal ort = ((page_rect[i].x() - start.x()) * orthogonal.x() + (page_rect[i].y() - start.y()) * orthogonal.y())/length;
+ off_min = qMin(off_min, qFloor(off));
+ off_max = qMax(off_max, qCeil(off));
+ ort_min = qMin(ort_min, ort);
+ ort_max = qMax(ort_max, ort);
+ }
+ ort_min -= 1;
+ ort_max += 1;
+
+ start += off_min * offset + ort_min * orthogonal;
+ orthogonal *= (ort_max - ort_min);
+ int num = off_max - off_min;
+
+ QPointF gradient_rect[4] = { start,
+ start + orthogonal,
+ start + num*offset,
+ start + num*offset + orthogonal };
+ qreal xmin = gradient_rect[0].x();
+ qreal xmax = gradient_rect[0].x();
+ qreal ymin = gradient_rect[0].y();
+ qreal ymax = gradient_rect[0].y();
+ for (int i = 1; i < 4; ++i) {
+ xmin = qMin(xmin, gradient_rect[i].x());
+ xmax = qMax(xmax, gradient_rect[i].x());
+ ymin = qMin(ymin, gradient_rect[i].y());
+ ymax = qMax(ymax, gradient_rect[i].y());
+ }
+ xmin -= 1000;
+ xmax += 1000;
+ ymin -= 1000;
+ ymax += 1000;
+ start -= QPointF(xmin, ymin);
+ qreal factor_x = qreal(1<<24)/(xmax - xmin);
+ qreal factor_y = qreal(1<<24)/(ymax - ymin);
+ int xoff = (int)(orthogonal.x()*factor_x);
+ int yoff = (int)(orthogonal.y()*factor_y);
+
+ QByteArray triangles;
+ triangles.resize(spread == QGradient::PadSpread ? 20*(stops.size()+2) : 20*num*stops.size());
+ uchar *data = (uchar *) triangles.data();
+ if (spread == QGradient::PadSpread) {
+ if (off_min > 0 || off_max < 1) {
+ // linear gradient outside of page
+ const QGradientStop &current_stop = off_min > 0 ? stops.at(stops.size()-1) : stops.at(0);
+ uint rgb = current_stop.second.rgba();
+ int xpos = (int)(start.x()*factor_x);
+ int ypos = (int)(start.y()*factor_y);
+ writeTriangleLine(data, xpos, ypos, xoff, yoff, rgb, 0, alpha);
+ start += num*offset;
+ xpos = (int)(start.x()*factor_x);
+ ypos = (int)(start.y()*factor_y);
+ writeTriangleLine(data, xpos, ypos, xoff, yoff, rgb, 1, alpha);
+ } else {
+ int flag = 0;
+ if (off_min < 0) {
+ uint rgb = stops.at(0).second.rgba();
+ int xpos = (int)(start.x()*factor_x);
+ int ypos = (int)(start.y()*factor_y);
+ writeTriangleLine(data, xpos, ypos, xoff, yoff, rgb, flag, alpha);
+ start -= off_min*offset;
+ flag = 1;
+ }
+ for (int s = 0; s < stops.size(); ++s) {
+ const QGradientStop &current_stop = stops.at(s);
+ uint rgb = current_stop.second.rgba();
+ int xpos = (int)(start.x()*factor_x);
+ int ypos = (int)(start.y()*factor_y);
+ writeTriangleLine(data, xpos, ypos, xoff, yoff, rgb, flag, alpha);
+ if (s < stops.size()-1)
+ start += offset*(stops.at(s+1).first - stops.at(s).first);
+ flag = 1;
+ }
+ if (off_max > 1) {
+ start += (off_max - 1)*offset;
+ uint rgb = stops.at(stops.size()-1).second.rgba();
+ int xpos = (int)(start.x()*factor_x);
+ int ypos = (int)(start.y()*factor_y);
+ writeTriangleLine(data, xpos, ypos, xoff, yoff, rgb, flag, alpha);
+ }
+ }
+ } else {
+ for (int i = 0; i < num; ++i) {
+ uchar flag = 0;
+ for (int s = 0; s < stops.size(); ++s) {
+ uint rgb = stops.at(s).second.rgba();
+ int xpos = (int)(start.x()*factor_x);
+ int ypos = (int)(start.y()*factor_y);
+ writeTriangleLine(data, xpos, ypos, xoff, yoff, rgb, flag, alpha);
+ if (s < stops.size()-1)
+ start += offset*(stops.at(s+1).first - stops.at(s).first);
+ flag = 1;
+ }
+ }
+ }
+ triangles.resize((char *)data - triangles.constData());
+
+ QByteArray shader;
+ QPdf::ByteStream s(&shader);
+ s << "<<\n"
+ "/ShadingType 4\n"
+ "/ColorSpace " << (alpha ? "/DeviceGray\n" : "/DeviceRGB\n") <<
+ "/AntiAlias true\n"
+ "/BitsPerCoordinate 24\n"
+ "/BitsPerComponent 8\n"
+ "/BitsPerFlag 8\n"
+ "/Decode [" << xmin << xmax << ymin << ymax << (alpha ? "0 1]\n" : "0 1 0 1 0 1]\n") <<
+ "/AntiAlias true\n"
+ "/Length " << triangles.length() << "\n"
+ ">>\n"
+ "stream\n" << triangles << "endstream\n"
+ "endobj\n";
+ return shader;
+}
+#endif
+
+static void moveToHook(qfixed x, qfixed y, void *data)
+{
+ QPdf::Stroker *t = (QPdf::Stroker *)data;
+ if (!t->first)
+ *t->stream << "h\n";
+ if (!t->cosmeticPen)
+ t->matrix.map(x, y, &x, &y);
+ *t->stream << x << y << "m\n";
+ t->first = false;
+}
+
+static void lineToHook(qfixed x, qfixed y, void *data)
+{
+ QPdf::Stroker *t = (QPdf::Stroker *)data;
+ if (!t->cosmeticPen)
+ t->matrix.map(x, y, &x, &y);
+ *t->stream << x << y << "l\n";
+}
+
+static void cubicToHook(qfixed c1x, qfixed c1y,
+ qfixed c2x, qfixed c2y,
+ qfixed ex, qfixed ey,
+ void *data)
+{
+ QPdf::Stroker *t = (QPdf::Stroker *)data;
+ if (!t->cosmeticPen) {
+ t->matrix.map(c1x, c1y, &c1x, &c1y);
+ t->matrix.map(c2x, c2y, &c2x, &c2y);
+ t->matrix.map(ex, ey, &ex, &ey);
+ }
+ *t->stream << c1x << c1y
+ << c2x << c2y
+ << ex << ey
+ << "c\n";
+}
+
+QPdf::Stroker::Stroker()
+ : stream(0),
+ first(true),
+ dashStroker(&basicStroker)
+{
+ stroker = &basicStroker;
+ basicStroker.setMoveToHook(moveToHook);
+ basicStroker.setLineToHook(lineToHook);
+ basicStroker.setCubicToHook(cubicToHook);
+ cosmeticPen = true;
+ basicStroker.setStrokeWidth(.1);
+}
+
+void QPdf::Stroker::setPen(const QPen &pen)
+{
+ if (pen.style() == Qt::NoPen) {
+ stroker = 0;
+ return;
+ }
+ qreal w = pen.widthF();
+ bool zeroWidth = w < 0.0001;
+ cosmeticPen = pen.isCosmetic();
+ if (zeroWidth)
+ w = .1;
+
+ basicStroker.setStrokeWidth(w);
+ basicStroker.setCapStyle(pen.capStyle());
+ basicStroker.setJoinStyle(pen.joinStyle());
+ basicStroker.setMiterLimit(pen.miterLimit());
+
+ QVector<qreal> dashpattern = pen.dashPattern();
+ if (zeroWidth) {
+ for (int i = 0; i < dashpattern.size(); ++i)
+ dashpattern[i] *= 10.;
+ }
+ if (!dashpattern.isEmpty()) {
+ dashStroker.setDashPattern(dashpattern);
+ dashStroker.setDashOffset(pen.dashOffset());
+ stroker = &dashStroker;
+ } else {
+ stroker = &basicStroker;
+ }
+}
+
+void QPdf::Stroker::strokePath(const QPainterPath &path)
+{
+ if (!stroker)
+ return;
+ first = true;
+
+ stroker->strokePath(path, this, cosmeticPen ? matrix : QTransform());
+ *stream << "h f\n";
+}
+
+QByteArray QPdf::ascii85Encode(const QByteArray &input)
+{
+ int isize = input.size()/4*4;
+ QByteArray output;
+ output.resize(input.size()*5/4+7);
+ char *out = output.data();
+ const uchar *in = (const uchar *)input.constData();
+ for (int i = 0; i < isize; i += 4) {
+ uint val = (((uint)in[i])<<24) + (((uint)in[i+1])<<16) + (((uint)in[i+2])<<8) + (uint)in[i+3];
+ if (val == 0) {
+ *out = 'z';
+ ++out;
+ } else {
+ char base[5];
+ base[4] = val % 85;
+ val /= 85;
+ base[3] = val % 85;
+ val /= 85;
+ base[2] = val % 85;
+ val /= 85;
+ base[1] = val % 85;
+ val /= 85;
+ base[0] = val % 85;
+ *(out++) = base[0] + '!';
+ *(out++) = base[1] + '!';
+ *(out++) = base[2] + '!';
+ *(out++) = base[3] + '!';
+ *(out++) = base[4] + '!';
+ }
+ }
+ //write the last few bytes
+ int remaining = input.size() - isize;
+ if (remaining) {
+ uint val = 0;
+ for (int i = isize; i < input.size(); ++i)
+ val = (val << 8) + in[i];
+ val <<= 8*(4-remaining);
+ char base[5];
+ base[4] = val % 85;
+ val /= 85;
+ base[3] = val % 85;
+ val /= 85;
+ base[2] = val % 85;
+ val /= 85;
+ base[1] = val % 85;
+ val /= 85;
+ base[0] = val % 85;
+ for (int i = 0; i < remaining+1; ++i)
+ *(out++) = base[i] + '!';
+ }
+ *(out++) = '~';
+ *(out++) = '>';
+ output.resize(out-output.data());
+ return output;
+}
+
+const char *QPdf::toHex(ushort u, char *buffer)
+{
+ int i = 3;
+ while (i >= 0) {
+ ushort hex = (u & 0x000f);
+ if (hex < 0x0a)
+ buffer[i] = '0'+hex;
+ else
+ buffer[i] = 'A'+(hex-0x0a);
+ u = u >> 4;
+ i--;
+ }
+ buffer[4] = '\0';
+ return buffer;
+}
+
+const char *QPdf::toHex(uchar u, char *buffer)
+{
+ int i = 1;
+ while (i >= 0) {
+ ushort hex = (u & 0x000f);
+ if (hex < 0x0a)
+ buffer[i] = '0'+hex;
+ else
+ buffer[i] = 'A'+(hex-0x0a);
+ u = u >> 4;
+ i--;
+ }
+ buffer[2] = '\0';
+ return buffer;
+}
+
+#define Q_MM(n) int((n * 720 + 127) / 254)
+#define Q_IN(n) int(n * 72)
+
+static const char * const psToStr[QPrinter::NPaperSize+1] =
+{
+ "A4", "B5", "Letter", "Legal", "Executive",
+ "A0", "A1", "A2", "A3", "A5", "A6", "A7", "A8", "A9", "B0", "B1",
+ "B10", "B2", "B3", "B4", "B6", "B7", "B8", "B9", "C5E", "Comm10E",
+ "DLE", "Folio", "Ledger", "Tabloid", 0
+};
+
+QPdf::PaperSize QPdf::paperSize(QPrinter::PaperSize paperSize)
+{
+ QSizeF s = qt_paperSizeToQSizeF(paperSize);
+ PaperSize p = { Q_MM(s.width()), Q_MM(s.height()) };
+ return p;
+}
+
+const char *QPdf::paperSizeToString(QPrinter::PaperSize paperSize)
+{
+ return psToStr[paperSize];
+}
+
+
+QByteArray QPdf::stripSpecialCharacters(const QByteArray &string)
+{
+ QByteArray s = string;
+ s.replace(" ", "");
+ s.replace("(", "");
+ s.replace(")", "");
+ s.replace("<", "");
+ s.replace(">", "");
+ s.replace("[", "");
+ s.replace("]", "");
+ s.replace("{", "");
+ s.replace("}", "");
+ s.replace("/", "");
+ s.replace("%", "");
+ return s;
+}
+
+
+// -------------------------- base engine, shared code between PS and PDF -----------------------
+
+QPdfBaseEngine::QPdfBaseEngine(QPdfBaseEnginePrivate &dd, PaintEngineFeatures f)
+ : QAlphaPaintEngine(dd, f)
+{
+ Q_D(QPdfBaseEngine);
+#if !defined(QT_NO_CUPS) && !defined(QT_NO_LIBRARY)
+ if (QCUPSSupport::isAvailable()) {
+ QCUPSSupport cups;
+ const cups_dest_t* printers = cups.availablePrinters();
+ int prnCount = cups.availablePrintersCount();
+
+ for (int i = 0; i < prnCount; ++i) {
+ if (printers[i].is_default) {
+ d->printerName = QString::fromLocal8Bit(printers[i].name);
+ break;
+ }
+ }
+
+ } else
+#endif
+ {
+ d->printerName = QString::fromLocal8Bit(qgetenv("PRINTER"));
+ if (d->printerName.isEmpty())
+ d->printerName = QString::fromLocal8Bit(qgetenv("LPDEST"));
+ if (d->printerName.isEmpty())
+ d->printerName = QString::fromLocal8Bit(qgetenv("NPRINTER"));
+ if (d->printerName.isEmpty())
+ d->printerName = QString::fromLocal8Bit(qgetenv("NGPRINTER"));
+ }
+}
+
+void QPdfBaseEngine::drawPoints (const QPointF *points, int pointCount)
+{
+ Q_D(QPdfBaseEngine);
+ if (!points || !d->hasPen)
+ return;
+
+ QPainterPath p;
+ for (int i=0; i!=pointCount;++i) {
+ p.moveTo(points[i]);
+ p.lineTo(points[i] + QPointF(0, 0.001));
+ }
+ drawPath(p);
+}
+
+void QPdfBaseEngine::drawLines (const QLineF *lines, int lineCount)
+{
+ if (!lines)
+ return;
+
+ QPainterPath p;
+ for (int i=0; i!=lineCount;++i) {
+ p.moveTo(lines[i].p1());
+ p.lineTo(lines[i].p2());
+ }
+ drawPath(p);
+}
+
+void QPdfBaseEngine::drawRects (const QRectF *rects, int rectCount)
+{
+ if (!rects)
+ return;
+
+ Q_D(QPdfBaseEngine);
+ if (d->clipEnabled && d->allClipped)
+ return;
+ if (!d->hasPen && !d->hasBrush)
+ return;
+
+ QBrush penBrush = d->pen.brush();
+ if (d->simplePen || !d->hasPen) {
+ // draw strokes natively in this case for better output
+ if(!d->simplePen && !d->stroker.matrix.isIdentity())
+ *d->currentPage << "q\n" << QPdf::generateMatrix(d->stroker.matrix);
+ for (int i = 0; i < rectCount; ++i)
+ *d->currentPage << rects[i].x() << rects[i].y() << rects[i].width() << rects[i].height() << "re\n";
+ *d->currentPage << (d->hasPen ? (d->hasBrush ? "B\n" : "S\n") : "f\n");
+ if(!d->simplePen && !d->stroker.matrix.isIdentity())
+ *d->currentPage << "Q\n";
+ } else {
+ QPainterPath p;
+ for (int i=0; i!=rectCount; ++i)
+ p.addRect(rects[i]);
+ drawPath(p);
+ }
+}
+
+void QPdfBaseEngine::drawPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode)
+{
+ Q_D(QPdfBaseEngine);
+
+ if (d->useAlphaEngine) {
+ QAlphaPaintEngine::drawPolygon(points, pointCount, mode);
+ if (!continueCall())
+ return;
+ }
+
+ if (!points || !pointCount)
+ return;
+
+ bool hb = d->hasBrush;
+ QPainterPath p;
+
+ switch(mode) {
+ case OddEvenMode:
+ p.setFillRule(Qt::OddEvenFill);
+ break;
+ case ConvexMode:
+ case WindingMode:
+ p.setFillRule(Qt::WindingFill);
+ break;
+ case PolylineMode:
+ d->hasBrush = false;
+ break;
+ default:
+ break;
+ }
+
+ p.moveTo(points[0]);
+ for (int i = 1; i < pointCount; ++i)
+ p.lineTo(points[i]);
+
+ if (mode != PolylineMode)
+ p.closeSubpath();
+ drawPath(p);
+
+ d->hasBrush = hb;
+}
+
+void QPdfBaseEngine::drawPath (const QPainterPath &p)
+{
+ Q_D(QPdfBaseEngine);
+
+ if (d->useAlphaEngine) {
+ QAlphaPaintEngine::drawPath(p);
+ if (!continueCall())
+ return;
+ }
+
+ if (d->clipEnabled && d->allClipped)
+ return;
+ if (!d->hasPen && !d->hasBrush)
+ return;
+
+ if (d->simplePen) {
+ // draw strokes natively in this case for better output
+ *d->currentPage << QPdf::generatePath(p, QTransform(), d->hasBrush ? QPdf::FillAndStrokePath : QPdf::StrokePath);
+ } else {
+ if (d->hasBrush)
+ *d->currentPage << QPdf::generatePath(p, d->stroker.matrix, QPdf::FillPath);
+ if (d->hasPen) {
+ *d->currentPage << "q\n";
+ QBrush b = d->brush;
+ d->brush = d->pen.brush();
+ setBrush();
+ d->stroker.strokePath(p);
+ *d->currentPage << "Q\n";
+ d->brush = b;
+ }
+ }
+}
+
+void QPdfBaseEngine::drawTextItem(const QPointF &p, const QTextItem &textItem)
+{
+ Q_D(QPdfBaseEngine);
+
+ if (d->useAlphaEngine) {
+ QAlphaPaintEngine::drawTextItem(p, textItem);
+ if (!continueCall())
+ return;
+ }
+
+ if (!d->hasPen || (d->clipEnabled && d->allClipped))
+ return;
+
+ *d->currentPage << "q\n";
+ if(!d->simplePen)
+ *d->currentPage << QPdf::generateMatrix(d->stroker.matrix);
+
+ bool hp = d->hasPen;
+ d->hasPen = false;
+ QBrush b = d->brush;
+ d->brush = d->pen.brush();
+ setBrush();
+
+ const QTextItemInt &ti = static_cast<const QTextItemInt &>(textItem);
+ Q_ASSERT(ti.fontEngine->type() != QFontEngine::Multi);
+ d->drawTextItem(p, ti);
+ d->hasPen = hp;
+ d->brush = b;
+ *d->currentPage << "Q\n";
+}
+
+
+void QPdfBaseEngine::updateState(const QPaintEngineState &state)
+{
+ Q_D(QPdfBaseEngine);
+
+ if (d->useAlphaEngine) {
+ QAlphaPaintEngine::updateState(state);
+ if (!continueCall())
+ return;
+ }
+
+ QPaintEngine::DirtyFlags flags = state.state();
+
+ if (flags & DirtyTransform)
+ d->stroker.matrix = state.transform();
+
+ if (flags & DirtyPen) {
+ d->pen = state.pen();
+ d->hasPen = d->pen.style() != Qt::NoPen;
+ d->stroker.setPen(d->pen);
+ QBrush penBrush = d->pen.brush();
+ bool oldSimple = d->simplePen;
+ d->simplePen = (d->hasPen && (penBrush.style() == Qt::SolidPattern) && penBrush.isOpaque());
+ if (oldSimple != d->simplePen)
+ flags |= DirtyTransform;
+ }
+ if (flags & DirtyBrush) {
+ d->brush = state.brush();
+ d->hasBrush = d->brush.style() != Qt::NoBrush;
+ }
+ if (flags & DirtyBrushOrigin) {
+ d->brushOrigin = state.brushOrigin();
+ flags |= DirtyBrush;
+ }
+ if (flags & DirtyOpacity)
+ d->opacity = state.opacity();
+
+ bool ce = d->clipEnabled;
+ if (flags & DirtyClipPath) {
+ d->clipEnabled = true;
+ updateClipPath(state.clipPath(), state.clipOperation());
+ } else if (flags & DirtyClipRegion) {
+ d->clipEnabled = true;
+ QPainterPath path;
+ QVector<QRect> rects = state.clipRegion().rects();
+ for (int i = 0; i < rects.size(); ++i)
+ path.addRect(rects.at(i));
+ updateClipPath(path, state.clipOperation());
+ flags |= DirtyClipPath;
+ } else if (flags & DirtyClipEnabled) {
+ d->clipEnabled = state.isClipEnabled();
+ }
+
+ if (ce != d->clipEnabled)
+ flags |= DirtyClipPath;
+ else if (!d->clipEnabled)
+ flags &= ~DirtyClipPath;
+
+ setupGraphicsState(flags);
+}
+
+void QPdfBaseEngine::setupGraphicsState(QPaintEngine::DirtyFlags flags)
+{
+ Q_D(QPdfBaseEngine);
+ if (flags & DirtyClipPath)
+ flags |= DirtyTransform|DirtyPen|DirtyBrush;
+
+ if (flags & DirtyTransform) {
+ *d->currentPage << "Q\n";
+ flags |= DirtyPen|DirtyBrush;
+ }
+
+ if (flags & DirtyClipPath) {
+ *d->currentPage << "Q q\n";
+
+ d->allClipped = false;
+ if (d->clipEnabled && !d->clips.isEmpty()) {
+ for (int i = 0; i < d->clips.size(); ++i) {
+ if (d->clips.at(i).isEmpty()) {
+ d->allClipped = true;
+ break;
+ }
+ }
+ if (!d->allClipped) {
+ for (int i = 0; i < d->clips.size(); ++i) {
+ *d->currentPage << QPdf::generatePath(d->clips.at(i), QTransform(), QPdf::ClipPath);
+ }
+ }
+ }
+ }
+
+ if (flags & DirtyTransform) {
+ *d->currentPage << "q\n";
+ if (d->simplePen && !d->stroker.matrix.isIdentity())
+ *d->currentPage << QPdf::generateMatrix(d->stroker.matrix);
+ }
+ if (flags & DirtyBrush)
+ setBrush();
+ if (d->simplePen && (flags & DirtyPen))
+ setPen();
+}
+
+extern QPainterPath qt_regionToPath(const QRegion &region);
+
+void QPdfBaseEngine::updateClipPath(const QPainterPath &p, Qt::ClipOperation op)
+{
+ Q_D(QPdfBaseEngine);
+ QPainterPath path = d->stroker.matrix.map(p);
+ //qDebug() << "updateClipPath: " << d->stroker.matrix << p.boundingRect() << path.boundingRect() << op;
+
+ if (op == Qt::NoClip) {
+ d->clipEnabled = false;
+ d->clips.clear();
+ } else if (op == Qt::ReplaceClip) {
+ d->clips.clear();
+ d->clips.append(path);
+ } else if (op == Qt::IntersectClip) {
+ d->clips.append(path);
+ } else { // UniteClip
+ // ask the painter for the current clipping path. that's the easiest solution
+ path = painter()->clipPath();
+ path = d->stroker.matrix.map(path);
+ d->clips.clear();
+ d->clips.append(path);
+ }
+
+ if (d->useAlphaEngine) {
+ // if we have an alpha region, we have to subtract that from the
+ // any existing clip region since that region will be filled in
+ // later with images
+ QPainterPath alphaClip = qt_regionToPath(alphaClipping());
+ if (!alphaClip.isEmpty()) {
+ if (!d->clipEnabled) {
+ QRect r = d->fullPage ? d->paperRect() : d->pageRect();
+ QPainterPath dev;
+ dev.addRect(QRect(0, 0, r.width(), r.height()));
+ if (path.isEmpty())
+ path = dev;
+ else
+ path = path.intersected(dev);
+ d->clipEnabled = true;
+ } else {
+ path = painter()->clipPath();
+ path = d->stroker.matrix.map(path);
+ }
+ path = path.subtracted(alphaClip);
+ d->clips.clear();
+ d->clips.append(path);
+ }
+ }
+}
+
+void QPdfBaseEngine::setPen()
+{
+ Q_D(QPdfBaseEngine);
+ if (d->pen.style() == Qt::NoPen)
+ return;
+ QBrush b = d->pen.brush();
+ Q_ASSERT(b.style() == Qt::SolidPattern && b.isOpaque());
+
+ QColor rgba = b.color();
+ if (d->colorMode == QPrinter::GrayScale) {
+ qreal gray = qGray(rgba.rgba())/255.;
+ *d->currentPage << gray << gray << gray;
+ } else {
+ *d->currentPage << rgba.redF()
+ << rgba.greenF()
+ << rgba.blueF();
+ }
+ *d->currentPage << "SCN\n";
+
+ *d->currentPage << d->pen.widthF() << "w ";
+
+ int pdfCapStyle = 0;
+ switch(d->pen.capStyle()) {
+ case Qt::FlatCap:
+ pdfCapStyle = 0;
+ break;
+ case Qt::SquareCap:
+ pdfCapStyle = 2;
+ break;
+ case Qt::RoundCap:
+ pdfCapStyle = 1;
+ break;
+ default:
+ break;
+ }
+ *d->currentPage << pdfCapStyle << "J ";
+
+ int pdfJoinStyle = 0;
+ switch(d->pen.joinStyle()) {
+ case Qt::MiterJoin:
+ pdfJoinStyle = 0;
+ break;
+ case Qt::BevelJoin:
+ pdfJoinStyle = 2;
+ break;
+ case Qt::RoundJoin:
+ pdfJoinStyle = 1;
+ break;
+ default:
+ break;
+ }
+ *d->currentPage << pdfJoinStyle << "j ";
+
+ *d->currentPage << QPdf::generateDashes(d->pen) << " 0 d\n";
+}
+
+bool QPdfBaseEngine::newPage()
+{
+ Q_D(QPdfBaseEngine);
+ setupGraphicsState(DirtyBrush|DirtyPen|DirtyClipPath);
+ QFile *outfile = qobject_cast<QFile*> (d->outDevice);
+ if (outfile && outfile->error() != QFile::NoError)
+ return false;
+ return true;
+}
+
+
+int QPdfBaseEngine::metric(QPaintDevice::PaintDeviceMetric metricType) const
+{
+ Q_D(const QPdfBaseEngine);
+ int val;
+ QRect r = d->fullPage ? d->paperRect() : d->pageRect();
+ switch (metricType) {
+ case QPaintDevice::PdmWidth:
+ val = r.width();
+ break;
+ case QPaintDevice::PdmHeight:
+ val = r.height();
+ break;
+ case QPaintDevice::PdmDpiX:
+ case QPaintDevice::PdmDpiY:
+ val = d->resolution;
+ break;
+ case QPaintDevice::PdmPhysicalDpiX:
+ case QPaintDevice::PdmPhysicalDpiY:
+ val = 1200;
+ break;
+ case QPaintDevice::PdmWidthMM:
+ val = qRound(r.width()*25.4/d->resolution);
+ break;
+ case QPaintDevice::PdmHeightMM:
+ val = qRound(r.height()*25.4/d->resolution);
+ break;
+ case QPaintDevice::PdmNumColors:
+ val = INT_MAX;
+ break;
+ case QPaintDevice::PdmDepth:
+ val = 32;
+ break;
+ default:
+ qWarning("QPrinter::metric: Invalid metric command");
+ return 0;
+ }
+ return val;
+}
+
+void QPdfBaseEngine::setProperty(PrintEnginePropertyKey key, const QVariant &value)
+{
+ Q_D(QPdfBaseEngine);
+ switch (key) {
+ case PPK_CollateCopies:
+ d->collate = value.toBool();
+ break;
+ case PPK_ColorMode:
+ d->colorMode = QPrinter::ColorMode(value.toInt());
+ break;
+ case PPK_Creator:
+ d->creator = value.toString();
+ break;
+ case PPK_DocumentName:
+ d->title = value.toString();
+ break;
+ case PPK_FullPage:
+ d->fullPage = value.toBool();
+ break;
+ case PPK_NumberOfCopies:
+ d->copies = value.toInt();
+ break;
+ case PPK_Orientation:
+ d->orientation = QPrinter::Orientation(value.toInt());
+ break;
+ case PPK_OutputFileName:
+ d->outputFileName = value.toString();
+ break;
+ case PPK_PageOrder:
+ d->pageOrder = QPrinter::PageOrder(value.toInt());
+ break;
+ case PPK_PaperSize:
+ d->paperSize = QPrinter::PaperSize(value.toInt());
+ break;
+ case PPK_PaperSource:
+ d->paperSource = QPrinter::PaperSource(value.toInt());
+ break;
+ case PPK_PrinterName:
+ d->printerName = value.toString();
+ break;
+ case PPK_PrinterProgram:
+ d->printProgram = value.toString();
+ break;
+ case PPK_Resolution:
+ d->resolution = value.toInt();
+ break;
+ case PPK_SelectionOption:
+ d->selectionOption = value.toString();
+ break;
+ case PPK_FontEmbedding:
+ d->embedFonts = value.toBool();
+ break;
+ case PPK_Duplex:
+ d->duplex = static_cast<QPrinter::DuplexMode> (value.toInt());
+ break;
+ case PPK_CupsPageRect:
+ d->cupsPageRect = value.toRect();
+ break;
+ case PPK_CupsPaperRect:
+ d->cupsPaperRect = value.toRect();
+ break;
+ case PPK_CupsOptions:
+ d->cupsOptions = value.toStringList();
+ break;
+ case PPK_CupsStringPageSize:
+ d->cupsStringPageSize = value.toString();
+ break;
+ case PPK_CustomPaperSize:
+ d->paperSize = QPrinter::Custom;
+ d->customPaperSize = value.toSizeF();
+ break;
+ case PPK_PageMargins:
+ {
+ QList<QVariant> margins(value.toList());
+ Q_ASSERT(margins.size() == 4);
+ d->leftMargin = margins.at(0).toDouble();
+ d->topMargin = margins.at(1).toDouble();
+ d->rightMargin = margins.at(2).toDouble();
+ d->bottomMargin = margins.at(3).toDouble();
+ d->hasCustomPageMargins = true;
+ break;
+ }
+ default:
+ break;
+ }
+}
+
+QVariant QPdfBaseEngine::property(PrintEnginePropertyKey key) const
+{
+ Q_D(const QPdfBaseEngine);
+
+ QVariant ret;
+ switch (key) {
+ case PPK_CollateCopies:
+ ret = d->collate;
+ break;
+ case PPK_ColorMode:
+ ret = d->colorMode;
+ break;
+ case PPK_Creator:
+ ret = d->creator;
+ break;
+ case PPK_DocumentName:
+ ret = d->title;
+ break;
+ case PPK_FullPage:
+ ret = d->fullPage;
+ break;
+ case PPK_NumberOfCopies:
+#if !defined(QT_NO_CUPS) && !defined(QT_NO_LIBRARY)
+ if (QCUPSSupport::isAvailable())
+ ret = 1;
+ else
+#endif
+ ret = d->copies;
+ break;
+ case PPK_Orientation:
+ ret = d->orientation;
+ break;
+ case PPK_OutputFileName:
+ ret = d->outputFileName;
+ break;
+ case PPK_PageOrder:
+ ret = d->pageOrder;
+ break;
+ case PPK_PaperSize:
+ ret = d->paperSize;
+ break;
+ case PPK_PaperSource:
+ ret = d->paperSource;
+ break;
+ case PPK_PrinterName:
+ ret = d->printerName;
+ break;
+ case PPK_PrinterProgram:
+ ret = d->printProgram;
+ break;
+ case PPK_Resolution:
+ ret = d->resolution;
+ break;
+ case PPK_SupportedResolutions:
+ ret = QList<QVariant>() << 72;
+ break;
+ case PPK_PaperRect:
+ ret = d->paperRect();
+ break;
+ case PPK_PageRect:
+ ret = d->pageRect();
+ break;
+ case PPK_SelectionOption:
+ ret = d->selectionOption;
+ break;
+ case PPK_FontEmbedding:
+ ret = d->embedFonts;
+ break;
+ case PPK_Duplex:
+ ret = d->duplex;
+ break;
+ case PPK_CupsPageRect:
+ ret = d->cupsPageRect;
+ break;
+ case PPK_CupsPaperRect:
+ ret = d->cupsPaperRect;
+ break;
+ case PPK_CupsOptions:
+ ret = d->cupsOptions;
+ break;
+ case PPK_CupsStringPageSize:
+ ret = d->cupsStringPageSize;
+ break;
+ case PPK_CustomPaperSize:
+ ret = d->customPaperSize;
+ break;
+ case PPK_PageMargins:
+ {
+ QList<QVariant> margins;
+ if (d->hasCustomPageMargins) {
+ margins << d->leftMargin << d->topMargin
+ << d->rightMargin << d->bottomMargin;
+ } else {
+ const int defaultMargin = 10; // ~3.5 mm
+ margins << defaultMargin << defaultMargin
+ << defaultMargin << defaultMargin;
+ }
+ ret = margins;
+ break;
+ }
+ default:
+ break;
+ }
+ return ret;
+}
+
+QPdfBaseEnginePrivate::QPdfBaseEnginePrivate(QPrinter::PrinterMode m)
+ : clipEnabled(false), allClipped(false), hasPen(true), hasBrush(false), simplePen(false),
+ useAlphaEngine(false),
+ outDevice(0), fd(-1),
+ duplex(QPrinter::DuplexNone), collate(false), fullPage(false), embedFonts(true), copies(1),
+ pageOrder(QPrinter::FirstPageFirst), orientation(QPrinter::Portrait),
+ paperSize(QPrinter::A4), colorMode(QPrinter::Color), paperSource(QPrinter::Auto),
+ hasCustomPageMargins(false),
+ leftMargin(0), topMargin(0), rightMargin(0), bottomMargin(0)
+{
+ resolution = 72;
+ if (m == QPrinter::HighResolution)
+ resolution = 1200;
+ else if (m == QPrinter::ScreenResolution)
+ resolution = qt_defaultDpi();
+
+ postscript = false;
+ currentObject = 1;
+ currentPage = 0;
+ stroker.stream = 0;
+}
+
+bool QPdfBaseEngine::begin(QPaintDevice *pdev)
+{
+ Q_D(QPdfBaseEngine);
+ d->pdev = pdev;
+
+ d->postscript = false;
+ d->currentObject = 1;
+
+ d->currentPage = new QPdfPage;
+ d->stroker.stream = d->currentPage;
+ d->opacity = 1.0;
+
+ return d->openPrintDevice();
+}
+
+bool QPdfBaseEngine::end()
+{
+ Q_D(QPdfBaseEngine);
+ qDeleteAll(d->fonts);
+ d->fonts.clear();
+ delete d->currentPage;
+ d->currentPage = 0;
+
+ d->closePrintDevice();
+ return true;
+}
+
+#ifndef QT_NO_LPR
+static void closeAllOpenFds()
+{
+ // hack time... getting the maximum number of open
+ // files, if possible. if not we assume it's the
+ // larger of 256 and the fd we got
+ int i;
+#if defined(_SC_OPEN_MAX)
+ i = (int)sysconf(_SC_OPEN_MAX);
+#elif defined(_POSIX_OPEN_MAX)
+ i = (int)_POSIX_OPEN_MAX;
+#elif defined(OPEN_MAX)
+ i = (int)OPEN_MAX;
+#else
+ i = 256;
+#endif
+ // leave stdin/out/err untouched
+ while(--i > 2)
+ ::close(i);
+}
+#endif
+
+bool QPdfBaseEnginePrivate::openPrintDevice()
+{
+ if(outDevice)
+ return false;
+
+ if (!outputFileName.isEmpty()) {
+ QFile *file = new QFile(outputFileName);
+ if (! file->open(QFile::WriteOnly|QFile::Truncate)) {
+ delete file;
+ return false;
+ }
+ outDevice = file;
+#if !defined(QT_NO_CUPS) && !defined(QT_NO_LIBRARY)
+ } else if (QCUPSSupport::isAvailable()) {
+ QCUPSSupport cups;
+ QPair<int, QString> ret = cups.tempFd();
+ if (ret.first < 0) {
+ qWarning("QPdfPrinter: Could not open temporary file to print");
+ return false;
+ }
+ cupsTempFile = ret.second;
+ outDevice = new QFile();
+ static_cast<QFile *>(outDevice)->open(ret.first, QIODevice::WriteOnly);
+#endif
+#ifndef QT_NO_LPR
+ } else {
+ QString pr;
+ if (!printerName.isEmpty())
+ pr = printerName;
+ int fds[2];
+ if (pipe(fds) != 0) {
+ qWarning("QPdfPrinter: Could not open pipe to print");
+ return false;
+ }
+
+ pid_t pid = fork();
+ if (pid == 0) { // child process
+ // if possible, exit quickly, so the actual lp/lpr
+ // becomes a child of init, and ::waitpid() is
+ // guaranteed not to wait.
+ if (fork() > 0) {
+ closeAllOpenFds();
+
+ // try to replace this process with "true" - this prevents
+ // global destructors from being called (that could possibly
+ // do wrong things to the parent process)
+ (void)execlp("true", "true", (char *)0);
+ (void)execl("/bin/true", "true", (char *)0);
+ (void)execl("/usr/bin/true", "true", (char *)0);
+ ::exit(0);
+ }
+ dup2(fds[0], 0);
+
+ closeAllOpenFds();
+
+ if (!printProgram.isEmpty()) {
+ if (!selectionOption.isEmpty())
+ pr.prepend(selectionOption);
+ else
+ pr.prepend(QLatin1String("-P"));
+ (void)execlp(printProgram.toLocal8Bit().data(), printProgram.toLocal8Bit().data(),
+ pr.toLocal8Bit().data(), (char *)0);
+ } else {
+ // if no print program has been specified, be smart
+ // about the option string too.
+ QList<QByteArray> lprhack;
+ QList<QByteArray> lphack;
+ QByteArray media;
+ if (!pr.isEmpty() || !selectionOption.isEmpty()) {
+ if (!selectionOption.isEmpty()) {
+ QStringList list = selectionOption.split(QLatin1Char(' '));
+ for (int i = 0; i < list.size(); ++i)
+ lprhack.append(list.at(i).toLocal8Bit());
+ lphack = lprhack;
+ } else {
+ lprhack.append("-P");
+ lphack.append("-d");
+ }
+ lprhack.append(pr.toLocal8Bit());
+ lphack.append(pr.toLocal8Bit());
+ }
+ lphack.append("-s");
+
+ char ** lpargs = new char *[lphack.size()+6];
+ char lp[] = "lp";
+ lpargs[0] = lp;
+ int i;
+ for (i = 0; i < lphack.size(); ++i)
+ lpargs[i+1] = (char *)lphack.at(i).constData();
+#ifndef Q_OS_OSF
+ if (QPdf::paperSizeToString(paperSize)) {
+ char dash_o[] = "-o";
+ lpargs[++i] = dash_o;
+ lpargs[++i] = const_cast<char *>(QPdf::paperSizeToString(paperSize));
+ lpargs[++i] = dash_o;
+ media = "media=";
+ media += QPdf::paperSizeToString(paperSize);
+ lpargs[++i] = media.data();
+ }
+#endif
+ lpargs[++i] = 0;
+ char **lprargs = new char *[lprhack.size()+2];
+ char lpr[] = "lpr";
+ lprargs[0] = lpr;
+ for (int i = 0; i < lprhack.size(); ++i)
+ lprargs[i+1] = (char *)lprhack[i].constData();
+ lprargs[lprhack.size() + 1] = 0;
+ (void)execvp("lp", lpargs);
+ (void)execvp("lpr", lprargs);
+ (void)execv("/bin/lp", lpargs);
+ (void)execv("/bin/lpr", lprargs);
+ (void)execv("/usr/bin/lp", lpargs);
+ (void)execv("/usr/bin/lpr", lprargs);
+ }
+ // if we couldn't exec anything, close the fd,
+ // wait for a second so the parent process (the
+ // child of the GUI process) has exited. then
+ // exit.
+ ::close(0);
+ (void)::sleep(1);
+ ::exit(0);
+ }
+ // parent process
+ ::close(fds[0]);
+ fd = fds[1];
+ (void)::waitpid(pid, 0, 0);
+
+ if (fd < 0)
+ return false;
+
+ outDevice = new QFile();
+ static_cast<QFile *>(outDevice)->open(fd, QIODevice::WriteOnly);
+#endif
+ }
+
+ return true;
+}
+
+void QPdfBaseEnginePrivate::closePrintDevice()
+{
+ if (!outDevice)
+ return;
+ outDevice->close();
+ if (fd >= 0)
+#if defined(Q_OS_WIN) && defined(_MSC_VER) && _MSC_VER >= 1400
+ ::_close(fd);
+#else
+ ::close(fd);
+#endif
+ fd = -1;
+ delete outDevice;
+ outDevice = 0;
+
+#if !defined(QT_NO_CUPS) && !defined(QT_NO_LIBRARY)
+ if (!cupsTempFile.isEmpty()) {
+ QString tempFile = cupsTempFile;
+ cupsTempFile.clear();
+ QCUPSSupport cups;
+
+ // Set up print options.
+ QByteArray prnName;
+ QList<QPair<QByteArray, QByteArray> > options;
+ QVector<cups_option_t> cupsOptStruct;
+
+ if (!printerName.isEmpty()) {
+ prnName = printerName.toLocal8Bit();
+ } else {
+ QPrinterInfo def = QPrinterInfo::defaultPrinter();
+ if (def.isNull()) {
+ qWarning("Could not determine printer to print to");
+ QFile::remove(tempFile);
+ return;
+ }
+ prnName = def.printerName().toLocal8Bit();
+ }
+
+ if (!cupsStringPageSize.isEmpty()) {
+ options.append(QPair<QByteArray, QByteArray>("media", cupsStringPageSize.toLocal8Bit()));
+ }
+
+ if (copies > 1) {
+ options.append(QPair<QByteArray, QByteArray>("copies", QString::number(copies).toLocal8Bit()));
+ }
+
+ if (collate) {
+ options.append(QPair<QByteArray, QByteArray>("Collate", "True"));
+ }
+
+ if (duplex != QPrinter::DuplexNone) {
+ switch(duplex) {
+ case QPrinter::DuplexNone: break;
+ case QPrinter::DuplexAuto:
+ if (orientation == QPrinter::Portrait)
+ options.append(QPair<QByteArray, QByteArray>("sides", "two-sided-long-edge"));
+ else
+ options.append(QPair<QByteArray, QByteArray>("sides", "two-sided-short-edge"));
+ break;
+ case QPrinter::DuplexLongSide:
+ options.append(QPair<QByteArray, QByteArray>("sides", "two-sided-long-edge"));
+ break;
+ case QPrinter::DuplexShortSide:
+ options.append(QPair<QByteArray, QByteArray>("sides", "two-sided-short-edge"));
+ break;
+ }
+ }
+
+ if (QCUPSSupport::cupsVersion() >= 10300 && orientation == QPrinter::Landscape) {
+ options.append(QPair<QByteArray, QByteArray>("landscape", ""));
+ }
+
+ QStringList::const_iterator it = cupsOptions.constBegin();
+ while (it != cupsOptions.constEnd()) {
+ options.append(QPair<QByteArray, QByteArray>((*it).toLocal8Bit(), (*(it+1)).toLocal8Bit()));
+ it += 2;
+ }
+
+ for (int c = 0; c < options.size(); ++c) {
+ cups_option_t opt;
+ opt.name = options[c].first.data();
+ opt.value = options[c].second.data();
+ cupsOptStruct.append(opt);
+ }
+
+ // Print the file.
+ cups_option_t* optPtr = cupsOptStruct.size() ? &cupsOptStruct.first() : 0;
+ cups.printFile(prnName.constData(), tempFile.toLocal8Bit().constData(),
+ title.toLocal8Bit().constData(), cupsOptStruct.size(), optPtr);
+
+ QFile::remove(tempFile);
+ }
+#endif
+}
+
+QPdfBaseEnginePrivate::~QPdfBaseEnginePrivate()
+{
+ qDeleteAll(fonts);
+ delete currentPage;
+}
+
+void QPdfBaseEnginePrivate::drawTextItem(const QPointF &p, const QTextItemInt &ti)
+{
+ Q_Q(QPdfBaseEngine);
+
+ QFontEngine *fe = ti.fontEngine;
+
+ QFontEngine::FaceId face_id = fe->faceId();
+ bool noEmbed = false;
+ if (face_id.filename.isEmpty()
+ || (!postscript && ((fe->fsType & 0x200) /* bitmap embedding only */
+ || (fe->fsType == 2) /* no embedding allowed */))) {
+ *currentPage << "Q\n";
+ q->QPaintEngine::drawTextItem(p, ti);
+ *currentPage << "q\n";
+ if (face_id.filename.isEmpty())
+ return;
+ noEmbed = true;
+ }
+
+ QFontSubset *font = fonts.value(face_id, 0);
+ if (!font) {
+ font = new QFontSubset(fe, requestObject());
+ font->noEmbed = noEmbed;
+ }
+ fonts.insert(face_id, font);
+
+ if (!currentPage->fonts.contains(font->object_id))
+ currentPage->fonts.append(font->object_id);
+
+ qreal size = ti.fontEngine->fontDef.pixelSize;
+#ifdef Q_WS_WIN
+ if (ti.fontEngine->type() == QFontEngine::Win) {
+ QFontEngineWin *fe = static_cast<QFontEngineWin *>(ti.fontEngine);
+ size = fe->tm.w.tmHeight;
+ }
+#endif
+
+ QVarLengthArray<glyph_t> glyphs;
+ QVarLengthArray<QFixedPoint> positions;
+ QTransform m;
+ m.translate(p.x(), p.y());
+ ti.fontEngine->getGlyphPositions(ti.glyphs, m, ti.flags,
+ glyphs, positions);
+ if (glyphs.size() == 0)
+ return;
+ int synthesized = ti.fontEngine->synthesized();
+ qreal stretch = synthesized & QFontEngine::SynthesizedStretch ? ti.fontEngine->fontDef.stretch/100. : 1.;
+
+ *currentPage << "BT\n"
+ << "/F" << font->object_id << size << "Tf "
+ << stretch << (synthesized & QFontEngine::SynthesizedItalic
+ ? "0 .3 -1 0 0 Tm\n"
+ : "0 0 -1 0 0 Tm\n");
+
+
+#if 0
+ // #### implement actual text for complex languages
+ const unsigned short *logClusters = ti.logClusters;
+ int pos = 0;
+ do {
+ int end = pos + 1;
+ while (end < ti.num_chars && logClusters[end] == logClusters[pos])
+ ++end;
+ *currentPage << "/Span << /ActualText <FEFF";
+ for (int i = pos; i < end; ++i) {
+ s << toHex((ushort)ti.chars[i].unicode(), buf);
+ }
+ *currentPage << "> >>\n"
+ "BDC\n"
+ "<";
+ int ge = end == ti.num_chars ? ti.num_glyphs : logClusters[end];
+ for (int gs = logClusters[pos]; gs < ge; ++gs)
+ *currentPage << toHex((ushort)ti.glyphs[gs].glyph, buf);
+ *currentPage << "> Tj\n"
+ "EMC\n";
+ pos = end;
+ } while (pos < ti.num_chars);
+#else
+ qreal last_x = 0.;
+ qreal last_y = 0.;
+ for (int i = 0; i < glyphs.size(); ++i) {
+ qreal x = positions[i].x.toReal();
+ qreal y = positions[i].y.toReal();
+ if (synthesized & QFontEngine::SynthesizedItalic)
+ x += .3*y;
+ x /= stretch;
+ char buf[5];
+ int g = font->addGlyph(glyphs[i]);
+ *currentPage << x - last_x << last_y - y << "Td <"
+ << QPdf::toHex((ushort)g, buf) << "> Tj\n";
+ last_x = x;
+ last_y = y;
+ }
+ if (synthesized & QFontEngine::SynthesizedBold) {
+ *currentPage << stretch << (synthesized & QFontEngine::SynthesizedItalic
+ ? "0 .3 -1 0 0 Tm\n"
+ : "0 0 -1 0 0 Tm\n");
+ *currentPage << "/Span << /ActualText <> >> BDC\n";
+ last_x = 0.5*fe->lineThickness().toReal();
+ last_y = 0.;
+ for (int i = 0; i < glyphs.size(); ++i) {
+ qreal x = positions[i].x.toReal();
+ qreal y = positions[i].y.toReal();
+ if (synthesized & QFontEngine::SynthesizedItalic)
+ x += .3*y;
+ x /= stretch;
+ char buf[5];
+ int g = font->addGlyph(glyphs[i]);
+ *currentPage << x - last_x << last_y - y << "Td <"
+ << QPdf::toHex((ushort)g, buf) << "> Tj\n";
+ last_x = x;
+ last_y = y;
+ }
+ *currentPage << "EMC\n";
+ }
+#endif
+
+ *currentPage << "ET\n";
+}
+
+QRect QPdfBaseEnginePrivate::paperRect() const
+{
+ int w;
+ int h;
+ if (paperSize == QPrinter::Custom) {
+ w = qRound(customPaperSize.width()*resolution/72.);
+ h = qRound(customPaperSize.height()*resolution/72.);
+ } else {
+#if !defined(QT_NO_CUPS) && !defined(QT_NO_LIBRARY)
+ if (QCUPSSupport::isAvailable() && !cupsPaperRect.isNull()) {
+ QRect r = cupsPaperRect;
+ w = r.width();
+ h = r.height();
+ } else
+#endif
+ {
+ QPdf::PaperSize s = QPdf::paperSize(paperSize);
+ w = s.width;
+ h = s.height;
+ }
+ w = qRound(w*resolution/72.);
+ h = qRound(h*resolution/72.);
+ }
+ if (orientation == QPrinter::Portrait)
+ return QRect(0, 0, w, h);
+ else
+ return QRect(0, 0, h, w);
+}
+
+QRect QPdfBaseEnginePrivate::pageRect() const
+{
+ if(fullPage)
+ return paperRect();
+
+ QRect r;
+
+#if !defined(QT_NO_CUPS) && !defined(QT_NO_LIBRARY)
+ if (!hasCustomPageMargins && QCUPSSupport::isAvailable() && !cupsPageRect.isNull()) {
+ r = cupsPageRect;
+ if (r == cupsPaperRect) {
+ // if cups doesn't define any margins, give it at least approx 3.5 mm
+ r = QRect(10, 10, r.width() - 20, r.height() - 20);
+ }
+ } else
+#endif
+ {
+ QPdf::PaperSize s;
+ if (paperSize == QPrinter::Custom) {
+ s.width = qRound(customPaperSize.width());
+ s.height = qRound(customPaperSize.height());
+ } else {
+ s = QPdf::paperSize(paperSize);
+ }
+ if (hasCustomPageMargins)
+ r = QRect(0, 0, s.width, s.height);
+ else
+ r = QRect(72/3, 72/3, s.width - 2*72/3, s.height - 2*72/3);
+ }
+
+ int x = qRound(r.left()*resolution/72.);
+ int y = qRound(r.top()*resolution/72.);
+ int w = qRound(r.width()*resolution/72.);
+ int h = qRound(r.height()*resolution/72.);
+ if (orientation == QPrinter::Portrait)
+ r = QRect(x, y, w, h);
+ else
+ r = QRect(y, x, h, w);
+
+ if (hasCustomPageMargins) {
+ r.adjust(qRound(leftMargin*(resolution/72.)),
+ qRound(topMargin*(resolution/72.)),
+ -qRound(rightMargin*(resolution/72.)),
+ -qRound(bottomMargin*(resolution/72.)));
+ }
+ return r;
+}
+
+#endif
+
+QT_END_NAMESPACE
diff --git a/src/gui/painting/qpdf_p.h b/src/gui/painting/qpdf_p.h
new file mode 100644
index 0000000000..1d45ca1957
--- /dev/null
+++ b/src/gui/painting/qpdf_p.h
@@ -0,0 +1,303 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QPDF_P_H
+#define QPDF_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 "QtGui/qmatrix.h"
+#include "QtCore/qstring.h"
+#include "QtCore/qvector.h"
+#include "private/qstroker_p.h"
+#include "private/qfontengine_p.h"
+#include "QtGui/qprinter.h"
+#include "private/qfontsubset_p.h"
+#include "private/qpaintengine_alpha_p.h"
+#include "qprintengine.h"
+#include "qbuffer.h"
+
+#ifndef QT_NO_PRINTER
+
+QT_BEGIN_NAMESPACE
+
+#define PPK_CupsOptions QPrintEngine::PrintEnginePropertyKey(0xfe00)
+#define PPK_CupsPageRect QPrintEngine::PrintEnginePropertyKey(0xfe01)
+#define PPK_CupsPaperRect QPrintEngine::PrintEnginePropertyKey(0xfe02)
+#define PPK_CupsStringPageSize QPrintEngine::PrintEnginePropertyKey(0xfe03)
+
+const char *qt_real_to_string(qreal val, char *buf);
+const char *qt_int_to_string(int val, char *buf);
+
+namespace QPdf {
+
+ class ByteStream
+ {
+ public:
+ // fileBacking means that ByteStream will buffer the contents on disk
+ // if the size exceeds a certain threshold. In this case, if a byte
+ // array was passed in, its contents may no longer correspond to the
+ // ByteStream contents.
+ explicit ByteStream(bool fileBacking = false);
+ explicit ByteStream(QByteArray *ba, bool fileBacking = false);
+ ~ByteStream();
+ ByteStream &operator <<(char chr);
+ ByteStream &operator <<(const char *str);
+ ByteStream &operator <<(const QByteArray &str);
+ ByteStream &operator <<(const ByteStream &src);
+ ByteStream &operator <<(qreal val);
+ ByteStream &operator <<(int val);
+ ByteStream &operator <<(const QPointF &p);
+ // Note that the stream may be invalidated by calls that insert data.
+ QIODevice *stream();
+ void clear();
+
+ static inline int maxMemorySize() { return 100000000; }
+ static inline int chunkSize() { return 10000000; }
+
+ protected:
+ void constructor_helper(QIODevice *dev);
+ void constructor_helper(QByteArray *ba);
+
+ private:
+ void prepareBuffer();
+
+ private:
+ QIODevice *dev;
+ QByteArray ba;
+ bool fileBackingEnabled;
+ bool fileBackingActive;
+ bool handleDirty;
+ };
+
+ enum PathFlags {
+ ClipPath,
+ FillPath,
+ StrokePath,
+ FillAndStrokePath
+ };
+ QByteArray generatePath(const QPainterPath &path, const QTransform &matrix, PathFlags flags);
+ QByteArray generateMatrix(const QTransform &matrix);
+ QByteArray generateDashes(const QPen &pen);
+ QByteArray patternForBrush(const QBrush &b);
+#ifdef USE_NATIVE_GRADIENTS
+ QByteArray generateLinearGradientShader(const QLinearGradient *lg, const QPointF *page_rect, bool alpha = false);
+#endif
+
+ struct Stroker {
+ Stroker();
+ void setPen(const QPen &pen);
+ void strokePath(const QPainterPath &path);
+ ByteStream *stream;
+ bool first;
+ QTransform matrix;
+ bool cosmeticPen;
+ private:
+ QStroker basicStroker;
+ QDashStroker dashStroker;
+ QStrokerOps *stroker;
+ };
+
+ QByteArray ascii85Encode(const QByteArray &input);
+
+ const char *toHex(ushort u, char *buffer);
+ const char *toHex(uchar u, char *buffer);
+
+
+ struct PaperSize {
+ int width, height; // in postscript points
+ };
+ PaperSize paperSize(QPrinter::PaperSize paperSize);
+ const char *paperSizeToString(QPrinter::PaperSize paperSize);
+
+
+ QByteArray stripSpecialCharacters(const QByteArray &string);
+};
+
+
+class QPdfPage : public QPdf::ByteStream
+{
+public:
+ QPdfPage();
+
+ QVector<uint> images;
+ QVector<uint> graphicStates;
+ QVector<uint> patterns;
+ QVector<uint> fonts;
+ QVector<uint> annotations;
+
+ void streamImage(int w, int h, int object);
+
+ QSize pageSize;
+private:
+};
+
+
+class QPdfBaseEnginePrivate;
+
+class QPdfBaseEngine : public QAlphaPaintEngine, public QPrintEngine
+{
+ Q_DECLARE_PRIVATE(QPdfBaseEngine)
+public:
+ QPdfBaseEngine(QPdfBaseEnginePrivate &d, PaintEngineFeatures f);
+ ~QPdfBaseEngine() {}
+
+ // reimplementations QPaintEngine
+ bool begin(QPaintDevice *pdev);
+ bool end();
+
+ void drawPoints(const QPointF *points, int pointCount);
+ void drawLines(const QLineF *lines, int lineCount);
+ void drawRects(const QRectF *rects, int rectCount);
+ void drawPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode);
+ void drawPath (const QPainterPath & path);
+
+ void drawTextItem(const QPointF &p, const QTextItem &textItem);
+
+ void updateState(const QPaintEngineState &state);
+
+ int metric(QPaintDevice::PaintDeviceMetric metricType) const;
+ // end reimplementations QPaintEngine
+
+ // Printer stuff...
+ bool newPage();
+ void setProperty(PrintEnginePropertyKey key, const QVariant &value);
+ QVariant property(PrintEnginePropertyKey key) const;
+
+ void setPen();
+ virtual void setBrush() = 0;
+ void setupGraphicsState(QPaintEngine::DirtyFlags flags);
+
+private:
+ void updateClipPath(const QPainterPath & path, Qt::ClipOperation op);
+
+ friend int qt_printerRealNumCopies(QPaintEngine *);
+};
+
+class QPdfBaseEnginePrivate : public QAlphaPaintEnginePrivate
+{
+ Q_DECLARE_PUBLIC(QPdfBaseEngine)
+public:
+ QPdfBaseEnginePrivate(QPrinter::PrinterMode m);
+ ~QPdfBaseEnginePrivate();
+
+ bool openPrintDevice();
+ void closePrintDevice();
+
+
+ virtual void drawTextItem(const QPointF &p, const QTextItemInt &ti);
+ inline uint requestObject() { return currentObject++; }
+
+ QRect paperRect() const;
+ QRect pageRect() const;
+
+ bool postscript;
+ int currentObject;
+
+ QPdfPage* currentPage;
+ QPdf::Stroker stroker;
+
+ QPointF brushOrigin;
+ QBrush brush;
+ QPen pen;
+ QList<QPainterPath> clips;
+ bool clipEnabled;
+ bool allClipped;
+ bool hasPen;
+ bool hasBrush;
+ bool simplePen;
+ qreal opacity;
+ bool useAlphaEngine;
+
+ QHash<QFontEngine::FaceId, QFontSubset *> fonts;
+
+ QPaintDevice *pdev;
+
+ // the device the output is in the end streamed to.
+ QIODevice *outDevice;
+ int fd;
+
+ // printer options
+ QString outputFileName;
+ QString printerName;
+ QString printProgram;
+ QString selectionOption;
+ QString title;
+ QString creator;
+ QPrinter::DuplexMode duplex;
+ bool collate;
+ bool fullPage;
+ bool embedFonts;
+ int copies;
+ int resolution;
+ QPrinter::PageOrder pageOrder;
+ QPrinter::Orientation orientation;
+ QPrinter::PaperSize paperSize;
+ QPrinter::ColorMode colorMode;
+ QPrinter::PaperSource paperSource;
+
+ QStringList cupsOptions;
+ QRect cupsPaperRect;
+ QRect cupsPageRect;
+ QString cupsStringPageSize;
+ QSizeF customPaperSize; // in postscript points
+ bool hasCustomPageMargins;
+ qreal leftMargin, topMargin, rightMargin, bottomMargin;
+
+#if !defined(QT_NO_CUPS) && !defined(QT_NO_LIBRARY)
+ QString cupsTempFile;
+#endif
+};
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_PRINTER
+
+#endif // QPDF_P_H
+
diff --git a/src/gui/painting/qpen.cpp b/src/gui/painting/qpen.cpp
new file mode 100644
index 0000000000..1d68520d47
--- /dev/null
+++ b/src/gui/painting/qpen.cpp
@@ -0,0 +1,1005 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#include "qpen.h"
+#include "qpen_p.h"
+#include "qdatastream.h"
+#include "qvariant.h"
+#include "qbrush.h"
+
+#include <qdebug.h>
+
+QT_BEGIN_NAMESPACE
+
+typedef QPenPrivate QPenData;
+
+/*!
+ \class QPen
+ \ingroup multimedia
+ \ingroup shared
+ \mainclass
+
+ \brief The QPen class defines how a QPainter should draw lines and outlines
+ of shapes.
+
+ A pen has a style(), width(), brush(), capStyle() and joinStyle().
+
+ The pen style defines the line type. The brush is used to fill
+ strokes generated with the pen. Use the QBrush class to specify
+ fill styles. The cap style determines the line end caps that can
+ be drawn using QPainter, while the join style describes how joins
+ between two lines are drawn. The pen width can be specified in
+ both integer (width()) and floating point (widthF()) precision. A
+ line width of zero indicates a cosmetic pen. This means that the
+ pen width is always drawn one pixel wide, independent of the \l
+ {QPainter#Coordinate Transformations}{transformation} set on the
+ painter.
+
+ The various settings can easily be modified using the
+ corresponding setStyle(), setWidth(), setBrush(), setCapStyle()
+ and setJoinStyle() functions (note that the painter's pen must be
+ reset when altering the pen's properties).
+
+ For example:
+
+ \snippet doc/src/snippets/code/src_gui_painting_qpen.cpp 0
+
+ which is equivalent to
+
+ \snippet doc/src/snippets/code/src_gui_painting_qpen.cpp 1
+
+ The default pen is a solid black brush with 0 width, square
+ cap style (Qt::SquareCap), and bevel join style (Qt::BevelJoin).
+
+ In addition QPen provides the color() and setColor()
+ convenience functions to extract and set the color of the pen's
+ brush, respectively. Pens may also be compared and streamed.
+
+ For more information about painting in general, see \l{The Paint
+ System} documentation.
+
+ \tableofcontents
+
+ \section1 Pen Style
+
+ Qt provides several built-in styles represented by the
+ Qt::PenStyle enum:
+
+ \table
+ \row
+ \o \inlineimage qpen-solid.png
+ \o \inlineimage qpen-dash.png
+ \o \inlineimage qpen-dot.png
+ \row
+ \o Qt::SolidLine
+ \o Qt::DashLine
+ \o Qt::DotLine
+ \row
+ \o \inlineimage qpen-dashdot.png
+ \o \inlineimage qpen-dashdotdot.png
+ \o \inlineimage qpen-custom.png
+ \row
+ \o Qt::DashDotLine
+ \o Qt::DashDotDotLine
+ \o Qt::CustomDashLine
+ \endtable
+
+ Simply use the setStyle() function to convert the pen style to
+ either of the built-in styles, except the Qt::CustomDashLine style
+ which we will come back to shortly. Setting the style to Qt::NoPen
+ tells the painter to not draw lines or outlines. The default pen
+ style is Qt::SolidLine.
+
+ Since Qt 4.1 it is also possible to specify a custom dash pattern
+ using the setDashPattern() function which implicitly converts the
+ style of the pen to Qt::CustomDashLine. The pattern argument, a
+ QVector, must be specified as an even number of \l qreal entries
+ where the entries 1, 3, 5... are the dashes and 2, 4, 6... are the
+ spaces. For example, the custom pattern shown above is created
+ using the following code:
+
+ \snippet doc/src/snippets/code/src_gui_painting_qpen.cpp 2
+
+ Note that the dash pattern is specified in units of the pens
+ width, e.g. a dash of length 5 in width 10 is 50 pixels long.
+
+ The currently set dash pattern can be retrieved using the
+ dashPattern() function. Use the isSolid() function to determine
+ whether the pen has a solid fill, or not.
+
+ \section1 Cap Style
+
+ The cap style defines how the end points of lines are drawn using
+ QPainter. The cap style only apply to wide lines, i.e. when the
+ width is 1 or greater. The Qt::PenCapStyle enum provides the
+ following styles:
+
+ \table
+ \row
+ \o \inlineimage qpen-square.png
+ \o \inlineimage qpen-flat.png
+ \o \inlineimage qpen-roundcap.png
+ \row
+ \o Qt::SquareCap
+ \o Qt::FlatCap
+ \o Qt::RoundCap
+ \endtable
+
+ The Qt::SquareCap style is a square line end that covers the end
+ point and extends beyond it by half the line width. The
+ Qt::FlatCap style is a square line end that does not cover the end
+ point of the line. And the Qt::RoundCap style is a rounded line
+ end covering the end point.
+
+ The default is Qt::SquareCap.
+
+ Whether or not end points are drawn when the pen width is 0 or 1
+ depends on the cap style. Using Qt::SquareCap or Qt::RoundCap they
+ are drawn, using Qt::FlatCap they are not drawn.
+
+ \section1 Join Style
+
+ The join style defines how joins between two connected lines can
+ be drawn using QPainter. The join style only apply to wide lines,
+ i.e. when the width is 1 or greater. The Qt::PenJoinStyle enum
+ provides the following styles:
+
+ \table
+ \row
+ \o \inlineimage qpen-bevel.png
+ \o \inlineimage qpen-miter.png
+ \o \inlineimage qpen-roundjoin.png
+ \row
+ \o Qt::BevelJoin
+ \o Qt::MiterJoin
+ \o Qt::RoundJoin
+ \endtable
+
+ The Qt::BevelJoin style fills the triangular notch between the two
+ lines. The Qt::MiterJoin style extends the lines to meet at an
+ angle. And the Qt::RoundJoin style fills a circular arc between
+ the two lines.
+
+ The default is Qt::BevelJoin.
+
+ \image qpen-miterlimit.png
+
+ When the Qt::MiterJoin style is applied, it is possible to use the
+ setMiterLimit() function to specify how far the miter join can
+ extend from the join point. The miterLimit() is used to reduce
+ artifacts between line joins where the lines are close to
+ parallel.
+
+ The miterLimit() must be specified in units of the pens width,
+ e.g. a miter limit of 5 in width 10 is 50 pixels long. The
+ default miter limit is 2, i.e. twice the pen width in pixels.
+
+ \table 100%
+ \row
+ \o \inlineimage qpen-demo.png
+ \o \bold {\l {demos/pathstroke}{The Path Stroking Demo}}
+
+ The Path Stroking demo shows Qt's built-in dash patterns and shows
+ how custom patterns can be used to extend the range of available
+ patterns.
+ \endtable
+
+ \sa QPainter, QBrush, {demos/pathstroke}{Path Stroking Demo},
+ {Scribble Example}
+*/
+
+/*!
+ \internal
+*/
+inline QPenPrivate::QPenPrivate(const QBrush &_brush, qreal _width, Qt::PenStyle penStyle,
+ Qt::PenCapStyle _capStyle, Qt::PenJoinStyle _joinStyle)
+ : dashOffset(0), miterLimit(2),
+ cosmetic(false)
+{
+ ref = 1;
+ width = _width;
+ brush = _brush;
+ style = penStyle;
+ capStyle = _capStyle;
+ joinStyle = _joinStyle;
+}
+
+static const Qt::PenCapStyle qpen_default_cap = Qt::SquareCap;
+static const Qt::PenJoinStyle qpen_default_join = Qt::BevelJoin;
+
+#ifndef QT_NO_THREAD
+// Special deleter that only deletes if the ref-count goes to zero
+template <>
+class QGlobalStaticDeleter<QPenPrivate>
+{
+public:
+ QGlobalStatic<QPenPrivate> &globalStatic;
+ QGlobalStaticDeleter(QGlobalStatic<QPenPrivate> &_globalStatic)
+ : globalStatic(_globalStatic)
+ { }
+
+ inline ~QGlobalStaticDeleter()
+ {
+ if (!globalStatic.pointer->ref.deref())
+ delete globalStatic.pointer;
+ globalStatic.pointer = 0;
+ globalStatic.destroyed = true;
+ }
+};
+#endif
+
+Q_GLOBAL_STATIC_WITH_ARGS(QPenData, defaultPenInstance,
+ (Qt::black, 0, Qt::SolidLine, qpen_default_cap, qpen_default_join))
+Q_GLOBAL_STATIC_WITH_ARGS(QPenData, nullPenInstance,
+ (Qt::black, 0, Qt::NoPen, qpen_default_cap, qpen_default_join))
+
+/*!
+ Constructs a default black solid line pen with 0 width.
+*/
+
+QPen::QPen()
+{
+ d = defaultPenInstance();
+ d->ref.ref();
+}
+
+/*!
+ Constructs a black pen with 0 width and the given \a style.
+
+ \sa setStyle()
+*/
+
+QPen::QPen(Qt::PenStyle style)
+{
+ if (style == Qt::NoPen) {
+ d = nullPenInstance();
+ d->ref.ref();
+ } else {
+ d = new QPenData(Qt::black, 0, style, qpen_default_cap, qpen_default_join);
+ }
+}
+
+
+/*!
+ Constructs a solid line pen with 0 width and the given \a color.
+
+ \sa setBrush(), setColor()
+*/
+
+QPen::QPen(const QColor &color)
+{
+ d = new QPenData(color, 0, Qt::SolidLine, qpen_default_cap, qpen_default_join);
+}
+
+
+/*!
+ \fn QPen::QPen(const QBrush &brush, qreal width, Qt::PenStyle style, Qt::PenCapStyle cap, Qt::PenJoinStyle join)
+
+ Constructs a pen with the specified \a brush, \a width, pen \a style,
+ \a cap style and \a join style.
+
+ \sa setBrush(), setWidth(), setStyle(), setCapStyle(), setJoinStyle()
+*/
+
+QPen::QPen(const QBrush &brush, qreal width, Qt::PenStyle s, Qt::PenCapStyle c, Qt::PenJoinStyle j)
+{
+ d = new QPenData(brush, width, s, c, j);
+}
+
+/*!
+ \fn QPen::QPen(const QPen &pen)
+
+ Constructs a pen that is a copy of the given \a pen.
+*/
+
+QPen::QPen(const QPen &p)
+{
+ d = p.d;
+ d->ref.ref();
+}
+
+
+/*!
+ Destroys the pen.
+*/
+
+QPen::~QPen()
+{
+ if (!d->ref.deref())
+ delete d;
+}
+
+/*!
+ \fn void QPen::detach()
+ Detaches from shared pen data to make sure that this pen is the
+ only one referring the data.
+
+ If multiple pens share common data, this pen dereferences the data
+ and gets a copy of the data. Nothing is done if there is just a
+ single reference.
+*/
+
+void QPen::detach()
+{
+ if (d->ref == 1)
+ return;
+
+ QPenData *x = new QPenData(*static_cast<QPenData *>(d));
+ if (!d->ref.deref())
+ delete d;
+ x->ref = 1;
+ d = x;
+}
+
+
+/*!
+ \fn QPen &QPen::operator=(const QPen &pen)
+
+ Assigns the given \a pen to this pen and returns a reference to
+ this pen.
+*/
+
+QPen &QPen::operator=(const QPen &p)
+{
+ qAtomicAssign(d, p.d);
+ return *this;
+}
+
+/*!
+ Returns the pen as a QVariant.
+*/
+QPen::operator QVariant() const
+{
+ return QVariant(QVariant::Pen, this);
+}
+
+/*!
+ \fn Qt::PenStyle QPen::style() const
+
+ Returns the pen style.
+
+ \sa setStyle(), {QPen#Pen Style}{Pen Style}
+*/
+Qt::PenStyle QPen::style() const
+{
+ return d->style;
+}
+/*!
+ \fn void QPen::setStyle(Qt::PenStyle style)
+
+ Sets the pen style to the given \a style.
+
+ See the \l Qt::PenStyle documentation for a list of the available
+ styles. Since Qt 4.1 it is also possible to specify a custom dash
+ pattern using the setDashPattern() function which implicitly
+ converts the style of the pen to Qt::CustomDashLine.
+
+ \sa style(), {QPen#Pen Style}{Pen Style}
+*/
+
+void QPen::setStyle(Qt::PenStyle s)
+{
+ if (d->style == s)
+ return;
+ detach();
+ d->style = s;
+ static_cast<QPenData *>(d)->dashPattern.clear();
+}
+
+/*!
+ Returns the dash pattern of this pen.
+
+ \sa style(), isSolid()
+ */
+QVector<qreal> QPen::dashPattern() const
+{
+ QPenData *dd = static_cast<QPenData *>(d);
+ if (d->style == Qt::SolidLine || d->style == Qt::NoPen) {
+ return QVector<qreal>();
+ } else if (dd->dashPattern.isEmpty()) {
+ const qreal space = 2;
+ const qreal dot = 1;
+ const qreal dash = 4;
+
+ switch (d->style) {
+ case Qt::DashLine:
+ dd->dashPattern << dash << space;
+ break;
+ case Qt::DotLine:
+ dd->dashPattern << dot << space;
+ break;
+ case Qt::DashDotLine:
+ dd->dashPattern << dash << space << dot << space;
+ break;
+ case Qt::DashDotDotLine:
+ dd->dashPattern << dash << space << dot << space << dot << space;
+ break;
+ default:
+ break;
+ }
+ }
+ return dd->dashPattern;
+}
+
+/*!
+ Sets the dash pattern for this pen to the given \a pattern. This
+ implicitly converts the style of the pen to Qt::CustomDashLine.
+
+ The pattern must be specified as an even number of entries where
+ the entries 1, 3, 5... are the dashes and 2, 4, 6... are the
+ spaces. For example:
+
+ \table 100%
+ \row
+ \o \inlineimage qpen-custom.png
+ \o
+ \snippet doc/src/snippets/code/src_gui_painting_qpen.cpp 3
+ \endtable
+
+ The dash pattern is specified in units of the pens width; e.g. a
+ dash of length 5 in width 10 is 50 pixels long. Note that a pen
+ with zero width is equivalent to a cosmetic pen with a width of 1
+ pixel.
+
+ Each dash is also subject to cap styles so a dash of 1 with square
+ cap set will extend 0.5 pixels out in each direction resulting in
+ a total width of 2.
+
+ Note that the default cap style is Qt::SquareCap, meaning that a
+ square line end covers the end point and extends beyond it by half
+ the line width.
+
+ \sa setStyle(), dashPattern(), setCapStyle(), setCosmetic()
+ */
+void QPen::setDashPattern(const QVector<qreal> &pattern)
+{
+ if (pattern.isEmpty())
+ return;
+ detach();
+
+ QPenData *dd = static_cast<QPenData *>(d);
+ dd->dashPattern = pattern;
+ d->style = Qt::CustomDashLine;
+
+ if ((dd->dashPattern.size() % 2) == 1) {
+ qWarning("QPen::setDashPattern: Pattern not of even length");
+ dd->dashPattern << 1;
+ }
+}
+
+
+/*!
+ Returns the dash offset for the pen.
+
+ \sa setDashOffset()
+*/
+qreal QPen::dashOffset() const
+{
+ QPenData *dd = static_cast<QPenData *>(d);
+ return dd->dashOffset;
+}
+/*!
+ Sets the dash offset (the starting point on the dash pattern) for this pen
+ to the \a offset specified. The offset is measured in terms of the units used
+ to specify the dash pattern.
+
+ \table
+ \row \o \inlineimage qpen-dashpattern.png
+ \o For example, a pattern where each stroke is four units long, followed by a gap
+ of two units, will begin with the stroke when drawn as a line.
+
+ However, if the dash offset is set to 4.0, any line drawn will begin with the gap.
+ Values of the offset up to 4.0 will cause part of the stroke to be drawn first,
+ and values of the offset between 4.0 and 6.0 will cause the line to begin with
+ part of the gap.
+ \endtable
+
+ \note This implicitly converts the style of the pen to Qt::CustomDashLine.
+*/
+void QPen::setDashOffset(qreal offset)
+{
+ if (qFuzzyCompare(offset, static_cast<QPenData *>(d)->dashOffset))
+ return;
+ detach();
+ static_cast<QPenData *>(d)->dashOffset = offset;
+ d->style = Qt::CustomDashLine;
+}
+
+/*!
+ Returns the miter limit of the pen. The miter limit is only
+ relevant when the join style is set to Qt::MiterJoin.
+
+ \sa setMiterLimit(), {QPen#Join Style}{Join Style}
+*/
+qreal QPen::miterLimit() const
+{
+ const QPenData *dd = static_cast<QPenData *>(d);
+ return dd->miterLimit;
+}
+
+/*!
+ Sets the miter limit of this pen to the given \a limit.
+
+ \image qpen-miterlimit.png
+
+ The miter limit describes how far a miter join can extend from the
+ join point. This is used to reduce artifacts between line joins
+ where the lines are close to parallel.
+
+ This value does only have effect when the pen style is set to
+ Qt::MiterJoin. The value is specified in units of the pen's width,
+ e.g. a miter limit of 5 in width 10 is 50 pixels long. The default
+ miter limit is 2, i.e. twice the pen width in pixels.
+
+ \sa miterLimit(), setJoinStyle(), {QPen#Join Style}{Join Style}
+*/
+void QPen::setMiterLimit(qreal limit)
+{
+ detach();
+ QPenData *dd = static_cast<QPenData *>(d);
+ dd->miterLimit = limit;
+}
+
+
+/*!
+ \fn qreal QPen::width() const
+
+ Returns the pen width with integer precision.
+
+ \sa setWidth(), widthF()
+*/
+
+int QPen::width() const
+{
+ return qRound(d->width);
+}
+
+/*!
+ \fn qreal QPen::widthF() const
+
+ Returns the pen width with floating point precision.
+
+ \sa setWidthF() width()
+*/
+qreal QPen::widthF() const
+{
+ return d->width;
+}
+
+/*!
+ \fn QPen::setWidth(int width)
+
+ Sets the pen width to the given \a width in pixels with integer
+ precision.
+
+ A line width of zero indicates a cosmetic pen. This means that the
+ pen width is always drawn one pixel wide, independent of the \l
+ {QPainter#Coordinate Transformations}{transformation} set on the
+ painter.
+
+ Setting a pen width with a negative value is not supported.
+
+ \sa setWidthF(), width()
+*/
+void QPen::setWidth(int width)
+{
+ if (width < 0)
+ qWarning("QPen::setWidth: Setting a pen width with a negative value is not defined");
+ if ((qreal)width == d->width)
+ return;
+ detach();
+ d->width = width;
+}
+
+/*!
+ Sets the pen width to the given \a width in pixels with floating point
+ precision.
+
+ A line width of zero indicates a cosmetic pen. This means that the
+ pen width is always drawn one pixel wide, independent of the \l
+ {QPainter#Coordinate Transformations}{transformation} on the
+ painter.
+
+ Setting a pen width with a negative value is not supported.
+
+ \sa setWidth() widthF()
+*/
+
+void QPen::setWidthF(qreal width)
+{
+ if (width < 0.f)
+ qWarning("QPen::setWidthF: Setting a pen width with a negative value is not defined");
+ if (qAbs(d->width - width) < 0.00000001f)
+ return;
+ detach();
+ d->width = width;
+}
+
+
+/*!
+ Returns the pen's cap style.
+
+ \sa setCapStyle(), {QPen#Cap Style}{Cap Style}
+*/
+Qt::PenCapStyle QPen::capStyle() const
+{
+ return d->capStyle;
+}
+
+/*!
+ \fn void QPen::setCapStyle(Qt::PenCapStyle style)
+
+ Sets the pen's cap style to the given \a style. The default value
+ is Qt::SquareCap.
+
+ \sa capStyle(), {QPen#Cap Style}{Cap Style}
+*/
+
+void QPen::setCapStyle(Qt::PenCapStyle c)
+{
+ if (d->capStyle == c)
+ return;
+ detach();
+ d->capStyle = c;
+}
+
+/*!
+ Returns the pen's join style.
+
+ \sa setJoinStyle(), {QPen#Join Style}{Join Style}
+*/
+Qt::PenJoinStyle QPen::joinStyle() const
+{
+ return d->joinStyle;
+}
+
+/*!
+ \fn void QPen::setJoinStyle(Qt::PenJoinStyle style)
+
+ Sets the pen's join style to the given \a style. The default value
+ is Qt::BevelJoin.
+
+ \sa joinStyle(), {QPen#Join Style}{Join Style}
+*/
+
+void QPen::setJoinStyle(Qt::PenJoinStyle j)
+{
+ if (d->joinStyle == j)
+ return;
+ detach();
+ d->joinStyle = j;
+}
+
+/*!
+ \fn const QColor &QPen::color() const
+
+ Returns the color of this pen's brush.
+
+ \sa brush(), setColor()
+*/
+QColor QPen::color() const
+{
+ return d->brush.color();
+}
+
+/*!
+ \fn void QPen::setColor(const QColor &color)
+
+ Sets the color of this pen's brush to the given \a color.
+
+ \sa setBrush(), color()
+*/
+
+void QPen::setColor(const QColor &c)
+{
+ detach();
+ d->brush = QBrush(c);
+}
+
+
+/*!
+ Returns the brush used to fill strokes generated with this pen.
+*/
+QBrush QPen::brush() const
+{
+ return d->brush;
+}
+
+/*!
+ Sets the brush used to fill strokes generated with this pen to the given
+ \a brush.
+
+ \sa brush(), setColor()
+*/
+void QPen::setBrush(const QBrush &brush)
+{
+ detach();
+ d->brush = brush;
+}
+
+
+/*!
+ Returns true if the pen has a solid fill, otherwise false.
+
+ \sa style(), dashPattern()
+*/
+bool QPen::isSolid() const
+{
+ return d->brush.style() == Qt::SolidPattern;
+}
+
+
+/*!
+ Returns true if the pen is cosmetic; otherwise returns false.
+
+ Cosmetic pens are used to draw strokes that have a constant width
+ regardless of any transformations applied to the QPainter they are
+ used with. Drawing a shape with a cosmetic pen ensures that its
+ outline will have the same thickness at different scale factors.
+
+ A zero width pen is cosmetic by default; pens with a non-zero width
+ are non-cosmetic.
+
+ \sa setCosmetic(), widthF()
+*/
+
+bool QPen::isCosmetic() const
+{
+ QPenData *dd = static_cast<QPenData *>(d);
+ return (dd->cosmetic == true) || d->width == 0;
+}
+
+
+/*!
+ Sets this pen to cosmetic or non-cosmetic, depending on the value of
+ \a cosmetic.
+
+ \sa isCosmetic()
+*/
+
+void QPen::setCosmetic(bool cosmetic)
+{
+ detach();
+ QPenData *dd = static_cast<QPenData *>(d);
+ dd->cosmetic = cosmetic;
+}
+
+
+
+/*!
+ \fn bool QPen::operator!=(const QPen &pen) const
+
+ Returns true if the pen is different from the given \a pen;
+ otherwise false. Two pens are different if they have different
+ styles, widths or colors.
+
+ \sa operator==()
+*/
+
+/*!
+ \fn bool QPen::operator==(const QPen &pen) const
+
+ Returns true if the pen is equal to the given \a pen; otherwise
+ false. Two pens are equal if they have equal styles, widths and
+ colors.
+
+ \sa operator!=()
+*/
+
+bool QPen::operator==(const QPen &p) const
+{
+ QPenData *dd = static_cast<QPenData *>(d);
+ QPenData *pdd = static_cast<QPenData *>(p.d);
+ return (p.d == d) || (p.d->style == d->style
+ && p.d->capStyle == d->capStyle
+ && p.d->joinStyle == d->joinStyle
+ && p.d->width == d->width
+ && pdd->miterLimit == dd->miterLimit
+ && (d->style != Qt::CustomDashLine
+ || (qFuzzyCompare(pdd->dashOffset, dd->dashOffset) &&
+ pdd->dashPattern == dd->dashPattern))
+ && p.d->brush == d->brush
+ && pdd->cosmetic == dd->cosmetic);
+}
+
+
+/*!
+ \fn bool QPen::isDetached()
+
+ \internal
+*/
+
+bool QPen::isDetached()
+{
+ return d->ref == 1;
+}
+
+
+/*****************************************************************************
+ QPen stream functions
+ *****************************************************************************/
+#ifndef QT_NO_DATASTREAM
+/*!
+ \fn QDataStream &operator<<(QDataStream &stream, const QPen &pen)
+ \relates QPen
+
+ Writes the given \a pen to the given \a stream and returns a reference to
+ the \a stream.
+
+ \sa {Format of the QDataStream Operators}
+*/
+
+QDataStream &operator<<(QDataStream &s, const QPen &p)
+{
+ QPenData *dd = static_cast<QPenData *>(p.d);
+ if (s.version() < 3) {
+ s << (quint8)p.style();
+ } else if (s.version() < QDataStream::Qt_4_3) {
+ s << (quint8)(p.style() | p.capStyle() | p.joinStyle());
+ } else {
+ s << (quint16)(p.style() | p.capStyle() | p.joinStyle());
+ s << (bool)(dd->cosmetic);
+ }
+
+ if (s.version() < 7) {
+ s << (quint8)p.width();
+ s << p.color();
+ } else {
+ s << double(p.widthF());
+ s << p.brush();
+ s << double(p.miterLimit());
+ if (sizeof(qreal) == sizeof(double)) {
+ s << p.dashPattern();
+ } else {
+ // ensure that we write doubles here instead of streaming the pattern
+ // directly; otherwise, platforms that redefine qreal might generate
+ // data that cannot be read on other platforms.
+ QVector<qreal> pattern = p.dashPattern();
+ s << quint32(pattern.size());
+ for (int i = 0; i < pattern.size(); ++i)
+ s << double(pattern.at(i));
+ }
+ if (s.version() >= 9)
+ s << double(p.dashOffset());
+ }
+ return s;
+}
+
+/*!
+ \fn QDataStream &operator>>(QDataStream &stream, QPen &pen)
+ \relates QPen
+
+ Reads a pen from the given \a stream into the given \a pen and
+ returns a reference to the \a stream.
+
+ \sa {Format of the QDataStream Operators}
+*/
+
+QDataStream &operator>>(QDataStream &s, QPen &p)
+{
+ quint16 style;
+ quint8 width8 = 0;
+ double width = 0;
+ QColor color;
+ QBrush brush;
+ double miterLimit = 2;
+ QVector<qreal> dashPattern;
+ double dashOffset = 0;
+ bool cosmetic = false;
+ if (s.version() < QDataStream::Qt_4_3) {
+ quint8 style8;
+ s >> style8;
+ style = style8;
+ } else {
+ s >> style;
+ s >> cosmetic;
+ }
+ if (s.version() < 7) {
+ s >> width8;
+ s >> color;
+ brush = color;
+ width = width8;
+ } else {
+ s >> width;
+ s >> brush;
+ s >> miterLimit;
+ if (sizeof(qreal) == sizeof(double)) {
+ s >> dashPattern;
+ } else {
+ quint32 numDashes;
+ s >> numDashes;
+ double dash;
+ for (quint32 i = 0; i < numDashes; ++i) {
+ s >> dash;
+ dashPattern << dash;
+ }
+ }
+ if (s.version() >= 9)
+ s >> dashOffset;
+ }
+
+ p.detach();
+ QPenData *dd = static_cast<QPenData *>(p.d);
+ dd->width = width;
+ dd->brush = brush;
+ dd->style = Qt::PenStyle(style & Qt::MPenStyle);
+ dd->capStyle = Qt::PenCapStyle(style & Qt::MPenCapStyle);
+ dd->joinStyle = Qt::PenJoinStyle(style & Qt::MPenJoinStyle);
+ dd->dashPattern = dashPattern;
+ dd->miterLimit = miterLimit;
+ dd->dashOffset = dashOffset;
+ dd->cosmetic = cosmetic;
+
+ return s;
+}
+#endif //QT_NO_DATASTREAM
+
+#ifndef QT_NO_DEBUG_STREAM
+QDebug operator<<(QDebug dbg, const QPen &p)
+{
+#ifndef Q_BROKEN_DEBUG_STREAM
+ dbg.nospace() << "QPen(" << p.width() << ',' << p.brush()
+ << ',' << int(p.style()) << ',' << int(p.capStyle())
+ << ',' << int(p.joinStyle()) << ',' << p.dashPattern()
+ << "," << p.dashOffset()
+ << ',' << p.miterLimit() << ')';
+ return dbg.space();
+#else
+ qWarning("This compiler doesn't support streaming QPen to QDebug");
+ return dbg;
+ Q_UNUSED(p);
+#endif
+}
+#endif
+
+/*!
+ \fn DataPtr &QPen::data_ptr()
+ \internal
+*/
+
+/*!
+ \typedef QPen::DataPtr
+
+ \internal
+*/
+
+QT_END_NAMESPACE
+
+#undef QT_COMPILING_QPEN
diff --git a/src/gui/painting/qpen.h b/src/gui/painting/qpen.h
new file mode 100644
index 0000000000..3d94b2ba51
--- /dev/null
+++ b/src/gui/painting/qpen.h
@@ -0,0 +1,140 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QPEN_H
+#define QPEN_H
+
+#include <QtGui/qcolor.h>
+#include <QtGui/qbrush.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+class QVariant;
+class QPenPrivate;
+class QBrush;
+class QPen;
+
+#ifndef QT_NO_DATASTREAM
+Q_GUI_EXPORT QDataStream &operator<<(QDataStream &, const QPen &);
+Q_GUI_EXPORT QDataStream &operator>>(QDataStream &, QPen &);
+#endif
+
+class Q_GUI_EXPORT QPen
+{
+public:
+ QPen();
+ QPen(Qt::PenStyle);
+ QPen(const QColor &color);
+ QPen(const QBrush &brush, qreal width, Qt::PenStyle s = Qt::SolidLine,
+ Qt::PenCapStyle c = Qt::SquareCap, Qt::PenJoinStyle j = Qt::BevelJoin);
+ QPen(const QPen &pen);
+
+ ~QPen();
+
+ QPen &operator=(const QPen &pen);
+
+ Qt::PenStyle style() const;
+ void setStyle(Qt::PenStyle);
+
+ QVector<qreal> dashPattern() const;
+ void setDashPattern(const QVector<qreal> &pattern);
+
+ qreal dashOffset() const;
+ void setDashOffset(qreal doffset);
+
+ qreal miterLimit() const;
+ void setMiterLimit(qreal limit);
+
+ qreal widthF() const;
+ void setWidthF(qreal width);
+
+ int width() const;
+ void setWidth(int width);
+
+ QColor color() const;
+ void setColor(const QColor &color);
+
+ QBrush brush() const;
+ void setBrush(const QBrush &brush);
+
+ bool isSolid() const;
+
+ Qt::PenCapStyle capStyle() const;
+ void setCapStyle(Qt::PenCapStyle pcs);
+
+ Qt::PenJoinStyle joinStyle() const;
+ void setJoinStyle(Qt::PenJoinStyle pcs);
+
+ bool isCosmetic() const;
+ void setCosmetic(bool cosmetic);
+
+ bool operator==(const QPen &p) const;
+ inline bool operator!=(const QPen &p) const { return !(operator==(p)); }
+ operator QVariant() const;
+
+ bool isDetached();
+private:
+ friend Q_GUI_EXPORT QDataStream &operator>>(QDataStream &, QPen &);
+ friend Q_GUI_EXPORT QDataStream &operator<<(QDataStream &, const QPen &);
+
+ void detach();
+ class QPenPrivate *d;
+
+public:
+ typedef QPenPrivate * DataPtr;
+ inline DataPtr &data_ptr() { return d; }
+};
+Q_DECLARE_TYPEINFO(QPen, Q_MOVABLE_TYPE);
+Q_DECLARE_SHARED(QPen)
+
+#ifndef QT_NO_DEBUG_STREAM
+Q_GUI_EXPORT QDebug operator<<(QDebug, const QPen &);
+#endif
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QPEN_H
diff --git a/src/gui/painting/qpen_p.h b/src/gui/painting/qpen_p.h
new file mode 100644
index 0000000000..ca010978d3
--- /dev/null
+++ b/src/gui/painting/qpen_p.h
@@ -0,0 +1,78 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QPEN_P_H
+#define QPEN_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 <QtCore/qglobal.h>
+
+QT_BEGIN_NAMESPACE
+
+class QPenPrivate {
+public:
+ QPenPrivate(const QBrush &brush, qreal width, Qt::PenStyle, Qt::PenCapStyle,
+ Qt::PenJoinStyle _joinStyle);
+ QAtomicInt ref;
+ qreal width;
+ QBrush brush;
+ Qt::PenStyle style;
+ Qt::PenCapStyle capStyle;
+ Qt::PenJoinStyle joinStyle;
+ mutable QVector<qreal> dashPattern;
+ qreal dashOffset;
+ qreal miterLimit;
+ uint cosmetic : 1;
+};
+
+QT_END_NAMESPACE
+
+#endif // QPEN_P_H
diff --git a/src/gui/painting/qpolygon.cpp b/src/gui/painting/qpolygon.cpp
new file mode 100644
index 0000000000..87dae0f578
--- /dev/null
+++ b/src/gui/painting/qpolygon.cpp
@@ -0,0 +1,928 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qpolygon.h"
+#include "qrect.h"
+#include "qdatastream.h"
+#include "qmatrix.h"
+#include "qdebug.h"
+#include "qpainterpath.h"
+#include "qvariant.h"
+#include "qpainterpath_p.h"
+#include "qbezier_p.h"
+
+#include <stdarg.h>
+
+QT_BEGIN_NAMESPACE
+
+//same as qt_painterpath_isect_line in qpainterpath.cpp
+static void qt_polygon_isect_line(const QPointF &p1, const QPointF &p2, const QPointF &pos,
+ int *winding)
+{
+ qreal x1 = p1.x();
+ qreal y1 = p1.y();
+ qreal x2 = p2.x();
+ qreal y2 = p2.y();
+ qreal y = pos.y();
+
+ int dir = 1;
+
+ if (qFuzzyCompare(y1, y2)) {
+ // ignore horizontal lines according to scan conversion rule
+ return;
+ } else if (y2 < y1) {
+ qreal x_tmp = x2; x2 = x1; x1 = x_tmp;
+ qreal y_tmp = y2; y2 = y1; y1 = y_tmp;
+ dir = -1;
+ }
+
+ if (y >= y1 && y < y2) {
+ qreal x = x1 + ((x2 - x1) / (y2 - y1)) * (y - y1);
+
+ // count up the winding number if we're
+ if (x<=pos.x()) {
+ (*winding) += dir;
+ }
+ }
+}
+
+/*!
+ \class QPolygon
+ \brief The QPolygon class provides a vector of points using
+ integer precision.
+
+ \reentrant
+
+ \ingroup multimedia
+ \ingroup shared
+
+ A QPolygon object is a QVector<QPoint>. The easiest way to add
+ points to a QPolygon is to use QVector's streaming operator, as
+ illustrated below:
+
+ \snippet doc/src/snippets/polygon/polygon.cpp 0
+
+ In addition to the functions provided by QVector, QPolygon
+ provides some point-specific functions.
+
+ Each point in a polygon can be retrieved by passing its index to
+ the point() function. To populate the polygon, QPolygon provides
+ the setPoint() function to set the point at a given index, the
+ setPoints() function to set all the points in the polygon
+ (resizing it to the given number of points), and the putPoints()
+ function which copies a number of given points into the polygon
+ from a specified index (resizing the polygon if necessary).
+
+ QPolygon provides the boundingRect() and translate() functions for
+ geometry functions. Use the QMatrix::map() function for more
+ general transformations of QPolygons.
+
+ The QPolygon class is \l {Implicit Data Sharing}{implicitly
+ shared}.
+
+ \sa QVector, QPolygonF, QLine
+*/
+
+
+/*****************************************************************************
+ QPolygon member functions
+ *****************************************************************************/
+
+/*!
+ \fn QPolygon::QPolygon()
+
+ Constructs a polygon with no points.
+
+ \sa QVector::isEmpty()
+*/
+
+/*!
+ \fn QPolygon::QPolygon(int size)
+
+ Constructs a polygon of the given \a size. Creates an empty
+ polygon if \a size == 0.
+
+ \sa QVector::isEmpty()
+*/
+
+/*!
+ \fn QPolygon::QPolygon(const QPolygon &polygon)
+
+ Constructs a copy of the given \a polygon.
+
+ \sa setPoints()
+*/
+
+/*!
+ \fn QPolygon::QPolygon(const QVector<QPoint> &points)
+
+ Constructs a polygon containing the specified \a points.
+
+ \sa setPoints()
+*/
+
+/*!
+ \fn QPolygon::QPolygon(const QRect &rectangle, bool closed)
+
+ Constructs a polygon from the given \a rectangle. If \a closed is
+ false, the polygon just contains the four points of the rectangle
+ ordered clockwise, otherwise the polygon's fifth point is set to
+ \a {rectangle}.topLeft().
+
+ Note that the bottom-right corner of the rectangle is located at
+ (rectangle.x() + rectangle.width(), rectangle.y() +
+ rectangle.height()).
+
+ \sa setPoints()
+*/
+
+QPolygon::QPolygon(const QRect &r, bool closed)
+{
+ reserve(closed ? 5 : 4);
+ *this << QPoint(r.x(), r.y())
+ << QPoint(r.x() + r.width(), r.y())
+ << QPoint(r.x() + r.width(), r.y() + r.height())
+ << QPoint(r.x(), r.y() + r.height());
+ if (closed)
+ *this << QPoint(r.left(), r.top());
+}
+
+/*!
+ \internal
+ Constructs a point array with \a nPoints points, taken from the
+ \a points array.
+
+ Equivalent to setPoints(nPoints, points).
+*/
+
+QPolygon::QPolygon(int nPoints, const int *points)
+{
+ setPoints(nPoints, points);
+}
+
+
+/*!
+ \fn QPolygon::~QPolygon()
+
+ Destroys the polygon.
+*/
+
+
+/*!
+ Translates all points in the polygon by (\a{dx}, \a{dy}).
+*/
+
+void QPolygon::translate(int dx, int dy)
+{
+ register QPoint *p = data();
+ register int i = size();
+ QPoint pt(dx, dy);
+ while (i--) {
+ *p += pt;
+ ++p;
+ }
+}
+
+/*!
+ \fn void QPolygon::translate(const QPoint &offset)
+ \overload
+
+ Translates all points in the polygon by the given \a offset.
+*/
+
+
+/*!
+ Extracts the coordinates of the point at the given \a index to
+ *\a{x} and *\a{y} (if they are valid pointers).
+
+ \sa setPoint()
+*/
+
+void QPolygon::point(int index, int *x, int *y) const
+{
+ QPoint p = at(index);
+ if (x)
+ *x = (int)p.x();
+ if (y)
+ *y = (int)p.y();
+}
+
+/*!
+ \fn QPoint QPolygon::point(int index) const
+ \overload
+
+ Returns the point at the given \a index.
+*/
+
+/*!
+ \fn void QPolygon::setPoint(int index, const QPoint &point)
+ \overload
+
+ Sets the point at the given \a index to the given \a point.
+*/
+
+/*!
+ \fn void QPolygon::setPoint(int index, int x, int y)
+
+ Sets the point at the given \a index to the point specified by
+ (\a{x}, \a{y}).
+
+ \sa point(), putPoints(), setPoints(),
+*/
+
+/*!
+ Resizes the polygon to \a nPoints and populates it with the given
+ \a points.
+
+ The example code creates a polygon with two points (10, 20) and
+ (30, 40):
+
+ \snippet doc/src/snippets/polygon/polygon.cpp 2
+
+ \sa setPoint() putPoints()
+*/
+
+void QPolygon::setPoints(int nPoints, const int *points)
+{
+ resize(nPoints);
+ int i = 0;
+ while (nPoints--) {
+ setPoint(i++, *points, *(points+1));
+ points += 2;
+ }
+}
+
+/*!
+ \overload
+
+ Resizes the polygon to \a nPoints and populates it with the points
+ specified by the variable argument list. The points are given as a
+ sequence of integers, starting with \a firstx then \a firsty, and
+ so on.
+
+ The example code creates a polygon with two points (10, 20) and
+ (30, 40):
+
+ \snippet doc/src/snippets/polygon/polygon.cpp 3
+*/
+
+void QPolygon::setPoints(int nPoints, int firstx, int firsty, ...)
+{
+ va_list ap;
+ resize(nPoints);
+ setPoint(0, firstx, firsty);
+ int i = 0, x, y;
+ va_start(ap, firsty);
+ while (--nPoints) {
+ x = va_arg(ap, int);
+ y = va_arg(ap, int);
+ setPoint(++i, x, y);
+ }
+ va_end(ap);
+}
+
+/*!
+ \overload
+ \internal
+
+ Copies \a nPoints points from the \a points coord array into this
+ point array, and resizes the point array if \c{index+nPoints}
+ exceeds the size of the array.
+
+ \sa setPoint()
+*/
+
+void QPolygon::putPoints(int index, int nPoints, const int *points)
+{
+ if (index + nPoints > size())
+ resize(index + nPoints);
+ int i = index;
+ while (nPoints--) {
+ setPoint(i++, *points, *(points+1));
+ points += 2;
+ }
+}
+
+/*!
+ Copies \a nPoints points from the variable argument list into this
+ polygon from the given \a index.
+
+ The points are given as a sequence of integers, starting with \a
+ firstx then \a firsty, and so on. The polygon is resized if
+ \c{index+nPoints} exceeds its current size.
+
+ The example code creates a polygon with three points (4,5), (6,7)
+ and (8,9), by expanding the polygon from 1 to 3 points:
+
+ \snippet doc/src/snippets/polygon/polygon.cpp 4
+
+ The following code has the same result, but here the putPoints()
+ function overwrites rather than extends:
+
+ \snippet doc/src/snippets/polygon/polygon.cpp 5
+
+ \sa setPoints()
+*/
+
+void QPolygon::putPoints(int index, int nPoints, int firstx, int firsty, ...)
+{
+ va_list ap;
+ if (index + nPoints > size())
+ resize(index + nPoints);
+ if (nPoints <= 0)
+ return;
+ setPoint(index, firstx, firsty);
+ int i = index, x, y;
+ va_start(ap, firsty);
+ while (--nPoints) {
+ x = va_arg(ap, int);
+ y = va_arg(ap, int);
+ setPoint(++i, x, y);
+ }
+ va_end(ap);
+}
+
+
+/*!
+ \fn void QPolygon::putPoints(int index, int nPoints, const QPolygon &fromPolygon, int fromIndex)
+ \overload
+
+ Copies \a nPoints points from the given \a fromIndex ( 0 by
+ default) in \a fromPolygon into this polygon, starting at the
+ specified \a index. For example:
+
+ \snippet doc/src/snippets/polygon/polygon.cpp 6
+*/
+
+void QPolygon::putPoints(int index, int nPoints, const QPolygon & from, int fromIndex)
+{
+ if (index + nPoints > size())
+ resize(index + nPoints);
+ if (nPoints <= 0)
+ return;
+ int n = 0;
+ while(n < nPoints) {
+ setPoint(index + n, from[fromIndex+n]);
+ ++n;
+ }
+}
+
+
+/*!
+ Returns the bounding rectangle of the polygon, or QRect(0, 0, 0,
+ 0) if the polygon is empty.
+
+ \sa QVector::isEmpty()
+*/
+
+QRect QPolygon::boundingRect() const
+{
+ if (isEmpty())
+ return QRect(0, 0, 0, 0);
+ register const QPoint *pd = constData();
+ int minx, maxx, miny, maxy;
+ minx = maxx = pd->x();
+ miny = maxy = pd->y();
+ ++pd;
+ for (int i = 1; i < size(); ++i) {
+ if (pd->x() < minx)
+ minx = pd->x();
+ else if (pd->x() > maxx)
+ maxx = pd->x();
+ if (pd->y() < miny)
+ miny = pd->y();
+ else if (pd->y() > maxy)
+ maxy = pd->y();
+ ++pd;
+ }
+ return QRect(QPoint(minx,miny), QPoint(maxx,maxy));
+}
+
+#ifndef QT_NO_DEBUG_STREAM
+QDebug operator<<(QDebug dbg, const QPolygon &a)
+{
+#ifndef Q_BROKEN_DEBUG_STREAM
+ dbg.nospace() << "QPolygon(";
+ for (int i = 0; i < a.count(); ++i)
+ dbg.nospace() << a.at(i);
+ dbg.nospace() << ')';
+ return dbg.space();
+#else
+ qWarning("This compiler doesn't support streaming QPolygon to QDebug");
+ return dbg;
+ Q_UNUSED(a);
+#endif
+}
+#endif
+
+
+/*!
+ \class QPolygonF
+ \brief The QPolygonF class provides a vector of points using
+ floating point precision.
+
+ \reentrant
+ \ingroup multimedia
+ \ingroup shared
+
+ A QPolygonF is a QVector<QPointF>. The easiest way to add points
+ to a QPolygonF is to use its streaming operator, as illustrated
+ below:
+
+ \snippet doc/src/snippets/polygon/polygon.cpp 1
+
+ In addition to the functions provided by QVector, QPolygonF
+ provides the boundingRect() and translate() functions for geometry
+ operations. Use the QMatrix::map() function for more general
+ transformations of QPolygonFs.
+
+ QPolygonF also provides the isClosed() function to determine
+ whether a polygon's start and end points are the same, and the
+ toPolygon() function returning an integer precision copy of this
+ polygon.
+
+ The QPolygonF class is \l {Implicit Data Sharing}{implicitly
+ shared}.
+
+ \sa QVector, QPolygon, QLineF
+*/
+
+
+/*****************************************************************************
+ QPolygonF member functions
+ *****************************************************************************/
+
+/*!
+ \fn QPolygonF::QPolygonF()
+
+ Constructs a polygon with no points.
+
+ \sa QVector::isEmpty()
+*/
+
+/*!
+ \fn QPolygonF::QPolygonF(int size)
+
+ Constructs a polygon of the given \a size. Creates an empty
+ polygon if \a size == 0.
+
+ \sa QVector::isEmpty()
+*/
+
+/*!
+ \fn QPolygonF::QPolygonF(const QPolygonF &polygon)
+
+ Constructs a copy of the given \a polygon.
+*/
+
+/*!
+ \fn QPolygonF::QPolygonF(const QVector<QPointF> &points)
+
+ Constructs a polygon containing the specified \a points.
+*/
+
+/*!
+ \fn QPolygonF::QPolygonF(const QRectF &rectangle)
+
+ Constructs a closed polygon from the specified \a rectangle.
+
+ The polygon contains the four vertices of the rectangle in
+ clockwise order starting and ending with the top-left vertex.
+
+ \sa isClosed()
+*/
+
+QPolygonF::QPolygonF(const QRectF &r)
+{
+ reserve(5);
+ append(QPointF(r.x(), r.y()));
+ append(QPointF(r.x() + r.width(), r.y()));
+ append(QPointF(r.x() + r.width(), r.y() + r.height()));
+ append(QPointF(r.x(), r.y() + r.height()));
+ append(QPointF(r.x(), r.y()));
+}
+
+/*!
+ \fn QPolygonF::QPolygonF(const QPolygon &polygon)
+
+ Constructs a float based polygon from the specified integer based
+ \a polygon.
+
+ \sa toPolygon()
+*/
+
+QPolygonF::QPolygonF(const QPolygon &a)
+{
+ reserve(a.size());
+ for (int i=0; i<a.size(); ++i)
+ append(a.at(i));
+}
+
+/*!
+ \fn QPolygonF::~QPolygonF()
+
+ Destroys the polygon.
+*/
+
+
+/*!
+ Translate all points in the polygon by the given \a offset.
+*/
+
+void QPolygonF::translate(const QPointF &offset)
+{
+ register QPointF *p = data();
+ register int i = size();
+ while (i--) {
+ *p += offset;
+ ++p;
+ }
+}
+
+/*!
+ \fn void QPolygonF::translate(qreal dx, qreal dy)
+ \overload
+
+ Translates all points in the polygon by (\a{dx}, \a{dy}).
+*/
+
+/*!
+ \fn bool QPolygonF::isClosed() const
+
+ Returns true if the polygon is closed; otherwise returns false.
+
+ A polygon is said to be closed if its start point and end point are equal.
+
+ \sa QVector::first(), QVector::last()
+*/
+
+/*!
+ Returns the bounding rectangle of the polygon, or QRectF(0,0,0,0)
+ if the polygon is empty.
+
+ \sa QVector::isEmpty()
+*/
+
+QRectF QPolygonF::boundingRect() const
+{
+ if (isEmpty())
+ return QRectF(0, 0, 0, 0);
+ register const QPointF *pd = constData();
+ qreal minx, maxx, miny, maxy;
+ minx = maxx = pd->x();
+ miny = maxy = pd->y();
+ ++pd;
+ for (int i = 1; i < size(); ++i) {
+ if (pd->x() < minx)
+ minx = pd->x();
+ else if (pd->x() > maxx)
+ maxx = pd->x();
+ if (pd->y() < miny)
+ miny = pd->y();
+ else if (pd->y() > maxy)
+ maxy = pd->y();
+ ++pd;
+ }
+ return QRectF(minx,miny, maxx - minx, maxy - miny);
+}
+
+/*!
+ Creates and returns a QPolygon by converting each QPointF to a
+ QPoint.
+
+ \sa QPointF::toPoint()
+*/
+
+QPolygon QPolygonF::toPolygon() const
+{
+ QPolygon a;
+ a.reserve(size());
+ for (int i=0; i<size(); ++i)
+ a.append(at(i).toPoint());
+ return a;
+}
+
+/*!
+ Returns the polygon as a QVariant
+*/
+QPolygon::operator QVariant() const
+{
+ return QVariant(QVariant::Polygon, this);
+}
+
+/*****************************************************************************
+ QPolygon stream functions
+ *****************************************************************************/
+#ifndef QT_NO_DATASTREAM
+/*!
+ \fn QDataStream &operator<<(QDataStream &stream, const QPolygon &polygon)
+ \since 4.4
+ \relates QPolygon
+
+ Writes the given \a polygon to the given \a stream, and returns a
+ reference to the stream.
+
+ \sa {Format of the QDataStream Operators}
+*/
+QDataStream &operator<<(QDataStream &s, const QPolygon &a)
+{
+ const QVector<QPoint> &v = a;
+ return s << v;
+}
+
+/*!
+ \fn QDataStream &operator>>(QDataStream &stream, QPolygon &polygon)
+ \since 4.4
+ \relates QPolygon
+
+ Reads a polygon from the given \a stream into the given \a
+ polygon, and returns a reference to the stream.
+
+ \sa {Format of the QDataStream Operators}
+*/
+QDataStream &operator>>(QDataStream &s, QPolygon &a)
+{
+ QVector<QPoint> &v = a;
+ return s >> v;
+}
+#endif
+
+/*****************************************************************************
+ QPolygonF stream functions
+ *****************************************************************************/
+#ifndef QT_NO_DATASTREAM
+/*!
+ \fn QDataStream &operator<<(QDataStream &stream, const QPolygonF &polygon)
+ \relates QPolygonF
+
+ Writes the given \a polygon to the given \a stream, and returns a
+ reference to the stream.
+
+ \sa {Format of the QDataStream Operators}
+*/
+
+QDataStream &operator<<(QDataStream &s, const QPolygonF &a)
+{
+ quint32 len = a.size();
+ uint i;
+
+ s << len;
+ for (i = 0; i < len; ++i)
+ s << a.at(i);
+ return s;
+}
+
+/*!
+ \fn QDataStream &operator>>(QDataStream &stream, QPolygonF &polygon)
+ \relates QPolygonF
+
+ Reads a polygon from the given \a stream into the given \a
+ polygon, and returns a reference to the stream.
+
+ \sa {Format of the QDataStream Operators}
+*/
+
+QDataStream &operator>>(QDataStream &s, QPolygonF &a)
+{
+ quint32 len;
+ uint i;
+
+ s >> len;
+ a.reserve(a.size() + (int)len);
+ QPointF p;
+ for (i = 0; i < len; ++i) {
+ s >> p;
+ a.insert(i, p);
+ }
+ return s;
+}
+#endif //QT_NO_DATASTREAM
+
+#ifndef QT_NO_DEBUG_STREAM
+QDebug operator<<(QDebug dbg, const QPolygonF &a)
+{
+#ifndef Q_BROKEN_DEBUG_STREAM
+ dbg.nospace() << "QPolygonF(";
+ for (int i = 0; i < a.count(); ++i)
+ dbg.nospace() << a.at(i);
+ dbg.nospace() << ')';
+ return dbg.space();
+#else
+ qWarning("This compiler doesn't support streaming QPolygonF to QDebug");
+ return dbg;
+ Q_UNUSED(a);
+#endif
+}
+#endif
+
+
+/*!
+ \since 4.3
+
+ \fn bool QPolygonF::containsPoint(const QPointF &point, Qt::FillRule fillRule) const
+
+ Returns true if the given \a point is inside the polygon according to
+ the specified \a fillRule; otherwise returns false.
+*/
+bool QPolygonF::containsPoint(const QPointF &pt, Qt::FillRule fillRule) const
+{
+ if (isEmpty())
+ return false;
+
+ int winding_number = 0;
+
+ QPointF last_pt = at(0);
+ QPointF last_start = at(0);
+ for (int i = 1; i < size(); ++i) {
+ const QPointF &e = at(i);
+ qt_polygon_isect_line(last_pt, e, pt, &winding_number);
+ last_pt = e;
+ }
+
+ // implicitly close last subpath
+ if (last_pt != last_start)
+ qt_polygon_isect_line(last_pt, last_start, pt, &winding_number);
+
+ return (fillRule == Qt::WindingFill
+ ? (winding_number != 0)
+ : ((winding_number % 2) != 0));
+}
+
+/*!
+ \since 4.3
+
+ \fn bool QPolygon::containsPoint(const QPoint &point, Qt::FillRule fillRule) const
+ Returns true if the given \a point is inside the polygon according to
+ the specified \a fillRule; otherwise returns false.
+*/
+bool QPolygon::containsPoint(const QPoint &pt, Qt::FillRule fillRule) const
+{
+ if (isEmpty())
+ return false;
+
+ int winding_number = 0;
+
+ QPoint last_pt = at(0);
+ QPoint last_start = at(0);
+ for (int i = 1; i < size(); ++i) {
+ const QPoint &e = at(i);
+ qt_polygon_isect_line(last_pt, e, pt, &winding_number);
+ last_pt = e;
+ }
+
+ // implicitly close last subpath
+ if (last_pt != last_start)
+ qt_polygon_isect_line(last_pt, last_start, pt, &winding_number);
+
+ return (fillRule == Qt::WindingFill
+ ? (winding_number != 0)
+ : ((winding_number % 2) != 0));
+}
+
+/*!
+ \since 4.3
+
+ Returns a polygon which is the union of this polygon and \a r.
+
+ Set operations on polygons, will treat the polygons as areas, and
+ implicitly close the polygon.
+
+ \sa intersected(), subtracted()
+*/
+
+QPolygon QPolygon::united(const QPolygon &r) const
+{
+ QPainterPath subject; subject.addPolygon(*this);
+ QPainterPath clip; clip.addPolygon(r);
+
+ return subject.united(clip).toFillPolygon().toPolygon();
+}
+
+/*!
+ \since 4.3
+
+ Returns a polygon which is the intersection of this polygon and \a r.
+
+ Set operations on polygons will treat the polygons as
+ areas. Non-closed polygons will be treated as implicitly closed.
+*/
+
+QPolygon QPolygon::intersected(const QPolygon &r) const
+{
+ QPainterPath subject; subject.addPolygon(*this);
+ QPainterPath clip; clip.addPolygon(r);
+
+ return subject.intersected(clip).toFillPolygon().toPolygon();
+}
+
+/*!
+ \since 4.3
+
+ Returns a polygon which is \a r subtracted from this polygon.
+
+ Set operations on polygons will treat the polygons as
+ areas. Non-closed polygons will be treated as implicitly closed.
+
+*/
+
+QPolygon QPolygon::subtracted(const QPolygon &r) const
+{
+ QPainterPath subject; subject.addPolygon(*this);
+ QPainterPath clip; clip.addPolygon(r);
+
+ return subject.subtracted(clip).toFillPolygon().toPolygon();
+}
+
+/*!
+ \since 4.3
+
+ Returns a polygon which is the union of this polygon and \a r.
+
+ Set operations on polygons will treat the polygons as
+ areas. Non-closed polygons will be treated as implicitly closed.
+
+ \sa intersected(), subtracted()
+*/
+
+QPolygonF QPolygonF::united(const QPolygonF &r) const
+{
+ QPainterPath subject; subject.addPolygon(*this);
+ QPainterPath clip; clip.addPolygon(r);
+
+ return subject.united(clip).toFillPolygon();
+}
+
+/*!
+ \since 4.3
+
+ Returns a polygon which is the intersection of this polygon and \a r.
+
+ Set operations on polygons will treat the polygons as
+ areas. Non-closed polygons will be treated as implicitly closed.
+
+*/
+
+QPolygonF QPolygonF::intersected(const QPolygonF &r) const
+{
+ QPainterPath subject; subject.addPolygon(*this);
+ QPainterPath clip; clip.addPolygon(r);
+
+ return subject.intersected(clip).toFillPolygon();
+}
+
+/*!
+ \since 4.3
+
+ Returns a polygon which is \a r subtracted from this polygon.
+
+ Set operations on polygons will treat the polygons as
+ areas. Non-closed polygons will be treated as implicitly closed.
+
+*/
+
+QPolygonF QPolygonF::subtracted(const QPolygonF &r) const
+{
+ QPainterPath subject; subject.addPolygon(*this);
+ QPainterPath clip; clip.addPolygon(r);
+ return subject.subtracted(clip).toFillPolygon();
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/painting/qpolygon.h b/src/gui/painting/qpolygon.h
new file mode 100644
index 0000000000..e5e0bd14d4
--- /dev/null
+++ b/src/gui/painting/qpolygon.h
@@ -0,0 +1,173 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QPOLYGON_H
+#define QPOLYGON_H
+
+#include <QtCore/qvector.h>
+#include <QtCore/qpoint.h>
+#include <QtCore/qrect.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+class QMatrix;
+class QTransform;
+class QRect;
+class QVariant;
+
+class Q_GUI_EXPORT QPolygon : public QVector<QPoint>
+{
+public:
+ inline QPolygon() {}
+ inline ~QPolygon() {}
+ inline QPolygon(int size);
+ inline QPolygon(const QPolygon &a) : QVector<QPoint>(a) {}
+ inline QPolygon(const QVector<QPoint> &v) : QVector<QPoint>(v) {}
+ QPolygon(const QRect &r, bool closed=false);
+ QPolygon(int nPoints, const int *points);
+ operator QVariant() const;
+
+ void translate(int dx, int dy);
+ void translate(const QPoint &offset);
+ QRect boundingRect() const;
+
+ void point(int i, int *x, int *y) const;
+ QPoint point(int i) const;
+ void setPoint(int index, int x, int y);
+ void setPoint(int index, const QPoint &p);
+ void setPoints(int nPoints, const int *points);
+ void setPoints(int nPoints, int firstx, int firsty, ...);
+ void putPoints(int index, int nPoints, const int *points);
+ void putPoints(int index, int nPoints, int firstx, int firsty, ...);
+ void putPoints(int index, int nPoints, const QPolygon & from, int fromIndex=0);
+
+ bool containsPoint(const QPoint &pt, Qt::FillRule fillRule) const;
+
+ QPolygon united(const QPolygon &r) const;
+ QPolygon intersected(const QPolygon &r) const;
+ QPolygon subtracted(const QPolygon &r) const;
+};
+
+inline QPolygon::QPolygon(int asize) : QVector<QPoint>(asize) {}
+
+#ifndef QT_NO_DEBUG_STREAM
+Q_GUI_EXPORT QDebug operator<<(QDebug, const QPolygon &);
+#endif
+
+/*****************************************************************************
+ QPolygon stream functions
+ *****************************************************************************/
+#ifndef QT_NO_DATASTREAM
+Q_GUI_EXPORT QDataStream &operator<<(QDataStream &stream, const QPolygon &polygon);
+Q_GUI_EXPORT QDataStream &operator>>(QDataStream &stream, QPolygon &polygon);
+#endif
+
+/*****************************************************************************
+ Misc. QPolygon functions
+ *****************************************************************************/
+
+inline void QPolygon::setPoint(int index, const QPoint &pt)
+{ (*this)[index] = pt; }
+
+inline void QPolygon::setPoint(int index, int x, int y)
+{ (*this)[index] = QPoint(x, y); }
+
+inline QPoint QPolygon::point(int index) const
+{ return at(index); }
+
+inline void QPolygon::translate(const QPoint &offset)
+{ translate(offset.x(), offset.y()); }
+
+class QRectF;
+
+class Q_GUI_EXPORT QPolygonF : public QVector<QPointF>
+{
+public:
+ inline QPolygonF() {}
+ inline ~QPolygonF() {}
+ inline QPolygonF(int size);
+ inline QPolygonF(const QPolygonF &a) : QVector<QPointF>(a) {}
+ inline QPolygonF(const QVector<QPointF> &v) : QVector<QPointF>(v) {}
+ QPolygonF(const QRectF &r);
+ QPolygonF(const QPolygon &a);
+
+ inline void translate(qreal dx, qreal dy);
+ void translate(const QPointF &offset);
+
+ QPolygon toPolygon() const;
+
+ bool isClosed() const { return !isEmpty() && first() == last(); }
+
+ QRectF boundingRect() const;
+
+ bool containsPoint(const QPointF &pt, Qt::FillRule fillRule) const;
+
+ QPolygonF united(const QPolygonF &r) const;
+ QPolygonF intersected(const QPolygonF &r) const;
+ QPolygonF subtracted(const QPolygonF &r) const;
+};
+
+inline QPolygonF::QPolygonF(int asize) : QVector<QPointF>(asize) {}
+
+#ifndef QT_NO_DEBUG_STREAM
+Q_GUI_EXPORT QDebug operator<<(QDebug, const QPolygonF &);
+#endif
+
+/*****************************************************************************
+ QPolygonF stream functions
+ *****************************************************************************/
+#ifndef QT_NO_DATASTREAM
+Q_GUI_EXPORT QDataStream &operator<<(QDataStream &stream, const QPolygonF &array);
+Q_GUI_EXPORT QDataStream &operator>>(QDataStream &stream, QPolygonF &array);
+#endif
+
+inline void QPolygonF::translate(qreal dx, qreal dy)
+{ translate(QPointF(dx, dy)); }
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QPOLYGON_H
diff --git a/src/gui/painting/qpolygonclipper_p.h b/src/gui/painting/qpolygonclipper_p.h
new file mode 100644
index 0000000000..b3083a47e8
--- /dev/null
+++ b/src/gui/painting/qpolygonclipper_p.h
@@ -0,0 +1,316 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QPOLYGONCLIPPER_P_H
+#define QPOLYGONCLIPPER_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 "private/qdatabuffer_p.h"
+
+QT_BEGIN_NAMESPACE
+
+/* based on sutherland-hodgman line-by-line clipping, as described in
+ Computer Graphics and Principles */
+template <typename InType, typename OutType, typename CastType> class QPolygonClipper
+{
+public:
+ QPolygonClipper()
+ {
+ x1 = y1 = x2 = y2 = 0;
+ }
+
+ ~QPolygonClipper()
+ {
+ }
+
+ void setBoundingRect(const QRect bounds)
+ {
+ x1 = bounds.x();
+ x2 = bounds.x() + bounds.width();
+ y1 = bounds.y();
+ y2 = bounds.y() + bounds.height();
+ }
+
+ QRect boundingRect()
+ {
+ return QRect(QPoint(x1, y1), QPoint(x2, y2));
+ }
+
+ inline OutType intersectLeft(const OutType &p1, const OutType &p2)
+ {
+ OutType t;
+ qreal dy = (p1.y - p2.y) / qreal(p1.x - p2.x);
+ t.x = x1;
+ t.y = static_cast<CastType>(p2.y + (x1 - p2.x) * dy);
+ return t;
+ }
+
+
+ inline OutType intersectRight(const OutType &p1, const OutType &p2)
+ {
+ OutType t;
+ qreal dy = (p1.y - p2.y) / qreal(p1.x - p2.x);
+ t.x = x2;
+ t.y = static_cast<CastType>(p2.y + (x2 - p2.x) * dy);
+ return t;
+ }
+
+
+ inline OutType intersectTop(const OutType &p1, const OutType &p2)
+ {
+ OutType t;
+ qreal dx = (p1.x - p2.x) / qreal(p1.y - p2.y);
+ t.x = static_cast<CastType>(p2.x + (y1 - p2.y) * dx);
+ t.y = y1;
+ return t;
+ }
+
+
+ inline OutType intersectBottom(const OutType &p1, const OutType &p2)
+ {
+ OutType t;
+ qreal dx = (p1.x - p2.x) / qreal(p1.y - p2.y);
+ t.x = static_cast<CastType>(p2.x + (y2 - p2.y) * dx);
+ t.y = y2;
+ return t;
+ }
+
+
+ void clipPolygon(const InType *inPoints, int inCount, OutType **outPoints, int *outCount,
+ bool closePolygon = true)
+ {
+ Q_ASSERT(outPoints);
+ Q_ASSERT(outCount);
+
+ if (inCount < 2) {
+ *outCount = 0;
+ return;
+ }
+
+ buffer1.reset();
+ buffer2.reset();
+
+ QDataBuffer<OutType> *source = &buffer1;
+ QDataBuffer<OutType> *clipped = &buffer2;
+
+ // Gather some info since we are iterating through the points anyway..
+ bool doLeft = false, doRight = false, doTop = false, doBottom = false;
+ OutType ot;
+ for (int i=0; i<inCount; ++i) {
+ ot = inPoints[i];
+ clipped->add(ot);
+
+ if (ot.x < x1)
+ doLeft = true;
+ else if (ot.x > x2)
+ doRight = true;
+ if (ot.y < y1)
+ doTop = true;
+ else if (ot.y > y2)
+ doBottom = true;
+ }
+
+ if (doLeft && clipped->size() > 1) {
+ QDataBuffer<OutType> *tmp = source;
+ source = clipped;
+ clipped = tmp;
+ clipped->reset();
+ int lastPos, start;
+ if (closePolygon) {
+ lastPos = source->size() - 1;
+ start = 0;
+ } else {
+ lastPos = 0;
+ start = 1;
+ if (source->at(0).x >= x1)
+ clipped->add(source->at(0));
+ }
+ for (int i=start; i<inCount; ++i) {
+ const OutType &cpt = source->at(i);
+ const OutType &ppt = source->at(lastPos);
+
+ if (cpt.x >= x1) {
+ if (ppt.x >= x1) {
+ clipped->add(cpt);
+ } else {
+ clipped->add(intersectLeft(cpt, ppt));
+ clipped->add(cpt);
+ }
+ } else if (ppt.x >= x1) {
+ clipped->add(intersectLeft(cpt, ppt));
+ }
+ lastPos = i;
+ }
+ }
+
+ if (doRight && clipped->size() > 1) {
+ QDataBuffer<OutType> *tmp = source;
+ source = clipped;
+ clipped = tmp;
+ clipped->reset();
+ int lastPos, start;
+ if (closePolygon) {
+ lastPos = source->size() - 1;
+ start = 0;
+ } else {
+ lastPos = 0;
+ start = 1;
+ if (source->at(0).x <= x2)
+ clipped->add(source->at(0));
+ }
+ for (int i=start; i<source->size(); ++i) {
+ const OutType &cpt = source->at(i);
+ const OutType &ppt = source->at(lastPos);
+
+ if (cpt.x <= x2) {
+ if (ppt.x <= x2) {
+ clipped->add(cpt);
+ } else {
+ clipped->add(intersectRight(cpt, ppt));
+ clipped->add(cpt);
+ }
+ } else if (ppt.x <= x2) {
+ clipped->add(intersectRight(cpt, ppt));
+ }
+
+ lastPos = i;
+ }
+
+ }
+
+ if (doTop && clipped->size() > 1) {
+ QDataBuffer<OutType> *tmp = source;
+ source = clipped;
+ clipped = tmp;
+ clipped->reset();
+ int lastPos, start;
+ if (closePolygon) {
+ lastPos = source->size() - 1;
+ start = 0;
+ } else {
+ lastPos = 0;
+ start = 1;
+ if (source->at(0).y >= y1)
+ clipped->add(source->at(0));
+ }
+ for (int i=start; i<source->size(); ++i) {
+ const OutType &cpt = source->at(i);
+ const OutType &ppt = source->at(lastPos);
+
+ if (cpt.y >= y1) {
+ if (ppt.y >= y1) {
+ clipped->add(cpt);
+ } else {
+ clipped->add(intersectTop(cpt, ppt));
+ clipped->add(cpt);
+ }
+ } else if (ppt.y >= y1) {
+ clipped->add(intersectTop(cpt, ppt));
+ }
+
+ lastPos = i;
+ }
+ }
+
+ if (doBottom && clipped->size() > 1) {
+ QDataBuffer<OutType> *tmp = source;
+ source = clipped;
+ clipped = tmp;
+ clipped->reset();
+ int lastPos, start;
+ if (closePolygon) {
+ lastPos = source->size() - 1;
+ start = 0;
+ } else {
+ lastPos = 0;
+ start = 1;
+ if (source->at(0).y <= y2)
+ clipped->add(source->at(0));
+ }
+ for (int i=start; i<source->size(); ++i) {
+ const OutType &cpt = source->at(i);
+ const OutType &ppt = source->at(lastPos);
+
+ if (cpt.y <= y2) {
+ if (ppt.y <= y2) {
+ clipped->add(cpt);
+ } else {
+ clipped->add(intersectBottom(cpt, ppt));
+ clipped->add(cpt);
+ }
+ } else if (ppt.y <= y2) {
+ clipped->add(intersectBottom(cpt, ppt));
+ }
+ lastPos = i;
+ }
+ }
+
+ if (closePolygon && clipped->size() > 0) {
+ // close clipped polygon
+ if (clipped->at(0).x != clipped->at(clipped->size()-1).x ||
+ clipped->at(0).y != clipped->at(clipped->size()-1).y) {
+ OutType ot = clipped->at(0);
+ clipped->add(ot);
+ }
+ }
+ *outCount = clipped->size();
+ *outPoints = clipped->data();
+ }
+
+private:
+ int x1, x2, y1, y2;
+ QDataBuffer<OutType> buffer1;
+ QDataBuffer<OutType> buffer2;
+};
+
+QT_END_NAMESPACE
+
+#endif // QPOLYGONCLIPPER_P_H
diff --git a/src/gui/painting/qprintengine.h b/src/gui/painting/qprintengine.h
new file mode 100644
index 0000000000..bf774125e5
--- /dev/null
+++ b/src/gui/painting/qprintengine.h
@@ -0,0 +1,117 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QPRINTENGINE_H
+#define QPRINTENGINE_H
+
+#include <QtCore/qvariant.h>
+#include <QtGui/qprinter.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+#ifndef QT_NO_PRINTER
+
+class Q_GUI_EXPORT QPrintEngine
+{
+public:
+ virtual ~QPrintEngine() {}
+ enum PrintEnginePropertyKey {
+ PPK_CollateCopies,
+ PPK_ColorMode,
+ PPK_Creator,
+ PPK_DocumentName,
+ PPK_FullPage,
+ PPK_NumberOfCopies,
+ PPK_Orientation,
+ PPK_OutputFileName,
+ PPK_PageOrder,
+ PPK_PageRect,
+ PPK_PageSize,
+ PPK_PaperRect,
+ PPK_PaperSource,
+ PPK_PrinterName,
+ PPK_PrinterProgram,
+ PPK_Resolution,
+ PPK_SelectionOption,
+ PPK_SupportedResolutions,
+
+ PPK_WindowsPageSize,
+ PPK_FontEmbedding,
+ PPK_SuppressSystemPrintStatus,
+
+ PPK_Duplex,
+
+ PPK_PaperSources,
+ PPK_CustomPaperSize,
+ PPK_PageMargins,
+ PPK_PaperSize = PPK_PageSize,
+
+ PPK_CustomBase = 0xff00
+ };
+
+ virtual void setProperty(PrintEnginePropertyKey key, const QVariant &value) = 0;
+ virtual QVariant property(PrintEnginePropertyKey key) const = 0;
+
+ virtual bool newPage() = 0;
+ virtual bool abort() = 0;
+
+ virtual int metric(QPaintDevice::PaintDeviceMetric) const = 0;
+
+ virtual QPrinter::PrinterState printerState() const = 0;
+
+#ifdef Q_WS_WIN
+ virtual HDC getPrinterDC() const { return 0; }
+ virtual void releasePrinterDC(HDC) const { }
+#endif
+
+};
+
+#endif // QT_NO_PRINTER
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QPRINTENGINE_H
diff --git a/src/gui/painting/qprintengine_mac.mm b/src/gui/painting/qprintengine_mac.mm
new file mode 100644
index 0000000000..7a77e47b91
--- /dev/null
+++ b/src/gui/painting/qprintengine_mac.mm
@@ -0,0 +1,926 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <private/qprintengine_mac_p.h>
+#include <qdebug.h>
+#include <qthread.h>
+#include <QtCore/qcoreapplication.h>
+
+#ifndef QT_NO_PRINTER
+
+QT_BEGIN_NAMESPACE
+
+extern QSizeF qt_paperSizeToQSizeF(QPrinter::PaperSize size);
+extern int qt_defaultDpi();
+
+QMacPrintEngine::QMacPrintEngine(QPrinter::PrinterMode mode) : QPaintEngine(*(new QMacPrintEnginePrivate))
+{
+ Q_D(QMacPrintEngine);
+ d->mode = mode;
+ d->initialize();
+}
+
+bool QMacPrintEngine::begin(QPaintDevice *dev)
+{
+ Q_D(QMacPrintEngine);
+
+ if (d->state == QPrinter::Idle && !d->isPrintSessionInitialized()) // Need to reinitialize
+ d->initialize();
+
+ d->paintEngine->state = state;
+ d->paintEngine->begin(dev);
+ Q_ASSERT_X(d->state == QPrinter::Idle, "QMacPrintEngine", "printer already active");
+
+ if (PMSessionValidatePrintSettings(d->session, d->settings, kPMDontWantBoolean) != noErr
+ || PMSessionValidatePageFormat(d->session, d->format, kPMDontWantBoolean) != noErr) {
+ d->state = QPrinter::Error;
+ return false;
+ }
+
+ if (!d->outputFilename.isEmpty()) {
+ QCFType<CFURLRef> outFile = CFURLCreateWithFileSystemPath(kCFAllocatorSystemDefault,
+ QCFString(d->outputFilename),
+ kCFURLPOSIXPathStyle,
+ false);
+ if (PMSessionSetDestination(d->session, d->settings, kPMDestinationFile,
+ kPMDocumentFormatPDF, outFile) != noErr) {
+ qWarning("QMacPrintEngine::begin: Problem setting file [%s]", d->outputFilename.toUtf8().constData());
+ return false;
+ }
+ }
+ OSStatus status = noErr;
+#ifndef QT_MAC_USE_COCOA
+# if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4)
+ if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_4) {
+ status = d->shouldSuppressStatus() ? PMSessionBeginCGDocumentNoDialog(d->session, d->settings, d->format)
+ : PMSessionBeginCGDocument(d->session, d->settings, d->format);
+ } else
+# endif
+ {
+ status = d->shouldSuppressStatus() ? PMSessionBeginDocumentNoDialog(d->session, d->settings, d->format)
+ : PMSessionBeginDocument(d->session, d->settings, d->format);
+ }
+#else
+ status = PMSessionBeginCGDocumentNoDialog(d->session, d->settings, d->format);
+#endif
+
+ if (status != noErr) {
+ d->state = QPrinter::Error;
+ return false;
+ }
+
+ d->state = QPrinter::Active;
+ setActive(true);
+ d->newPage_helper();
+ return true;
+}
+
+bool QMacPrintEngine::end()
+{
+ Q_D(QMacPrintEngine);
+ if (d->state == QPrinter::Aborted)
+ return true; // I was just here a function call ago :)
+ if(d->paintEngine->type() == QPaintEngine::CoreGraphics)
+ static_cast<QCoreGraphicsPaintEngine*>(d->paintEngine)->d_func()->hd = 0;
+ d->paintEngine->end();
+ if (d->state != QPrinter::Idle) {
+#ifndef QT_MAC_USE_COCOA
+ if (d->shouldSuppressStatus()) {
+ PMSessionEndPageNoDialog(d->session);
+ PMSessionEndDocumentNoDialog(d->session);
+ } else {
+ PMSessionEndPage(d->session);
+ PMSessionEndDocument(d->session);
+ }
+ PMRelease(d->session);
+#else
+ PMSessionEndPageNoDialog(d->session);
+ PMSessionEndDocumentNoDialog(d->session);
+ [d->printInfo release];
+#endif
+ d->printInfo = 0;
+ d->session = 0;
+ }
+ d->state = QPrinter::Idle;
+ return true;
+}
+
+QPaintEngine *
+QMacPrintEngine::paintEngine() const
+{
+ return d_func()->paintEngine;
+}
+
+Qt::HANDLE QMacPrintEngine::handle() const
+{
+ QCoreGraphicsPaintEngine *cgEngine = static_cast<QCoreGraphicsPaintEngine*>(paintEngine());
+ return cgEngine->d_func()->hd;
+}
+
+QMacPrintEnginePrivate::~QMacPrintEnginePrivate()
+{
+#ifdef QT_MAC_USE_COCOA
+ [printInfo release];
+#endif
+ delete paintEngine;
+}
+
+void QMacPrintEnginePrivate::setPaperSize(QPrinter::PaperSize ps)
+{
+ Q_Q(QMacPrintEngine);
+ QSizeF newSize = qt_paperSizeToQSizeF(ps);
+ QCFType<CFArrayRef> formats;
+ PMPrinter printer;
+
+ if (PMSessionGetCurrentPrinter(session, &printer) == noErr
+ && PMSessionCreatePageFormatList(session, printer, &formats) == noErr) {
+ CFIndex total = CFArrayGetCount(formats);
+ PMPageFormat tmp;
+ PMRect paper;
+ for (CFIndex idx = 0; idx < total; ++idx) {
+ tmp = static_cast<PMPageFormat>(
+ const_cast<void *>(CFArrayGetValueAtIndex(formats, idx)));
+ PMGetUnadjustedPaperRect(tmp, &paper);
+ int wMM = int((paper.right - paper.left) / 72 * 25.4 + 0.5);
+ int hMM = int((paper.bottom - paper.top) / 72 * 25.4 + 0.5);
+ if (newSize.width() == wMM && newSize.height() == hMM) {
+ PMCopyPageFormat(tmp, format);
+ // reset the orientation and resolution as they are lost in the copy.
+ q->setProperty(QPrintEngine::PPK_Orientation, orient);
+ if (PMSessionValidatePageFormat(session, format, kPMDontWantBoolean) != noErr) {
+ // Don't know, warn for the moment.
+ qWarning("QMacPrintEngine, problem setting format and resolution for this page size");
+ }
+ break;
+ }
+ }
+ }
+}
+
+QPrinter::PaperSize QMacPrintEnginePrivate::paperSize() const
+{
+ PMRect paper;
+ PMGetUnadjustedPaperRect(format, &paper);
+ int wMM = int((paper.right - paper.left) / 72 * 25.4 + 0.5);
+ int hMM = int((paper.bottom - paper.top) / 72 * 25.4 + 0.5);
+ for (int i = QPrinter::A4; i < QPrinter::NPaperSize; ++i) {
+ QSizeF s = qt_paperSizeToQSizeF(QPrinter::PaperSize(i));
+ if (s.width() == wMM && s.height() == hMM)
+ return (QPrinter::PaperSize)i;
+ }
+ return QPrinter::Custom;
+}
+
+QList<QVariant> QMacPrintEnginePrivate::supportedResolutions() const
+{
+ Q_ASSERT_X(session, "QMacPrinterEngine::supportedResolutions",
+ "must have a valid printer session");
+ UInt32 resCount;
+ QList<QVariant> resolutions;
+ PMPrinter printer;
+ if (PMSessionGetCurrentPrinter(session, &printer) == noErr) {
+ PMResolution res;
+ OSStatus status = PMPrinterGetPrinterResolutionCount(printer, &resCount);
+ if (status == kPMNotImplemented) {
+#if (MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_5)
+ // *Sigh* we have to use the non-indexed version.
+ if (PMPrinterGetPrinterResolution(printer, kPMMinSquareResolution, &res) == noErr)
+ resolutions.append(int(res.hRes));
+ if (PMPrinterGetPrinterResolution(printer, kPMMaxSquareResolution, &res) == noErr) {
+ QVariant var(int(res.hRes));
+ if (!resolutions.contains(var))
+ resolutions.append(var);
+ }
+ if (PMPrinterGetPrinterResolution(printer, kPMDefaultResolution, &res) == noErr) {
+ QVariant var(int(res.hRes));
+ if (!resolutions.contains(var))
+ resolutions.append(var);
+ }
+#endif
+ } else if (status == noErr) {
+ // According to the docs, index start at 1.
+ for (UInt32 i = 1; i <= resCount; ++i) {
+ if (PMPrinterGetIndexedPrinterResolution(printer, i, &res) == noErr)
+ resolutions.append(QVariant(int(res.hRes)));
+ }
+ } else {
+ qWarning("QMacPrintEngine::supportedResolutions: Unexpected error: %ld", long(status));
+ }
+ }
+ return resolutions;
+}
+
+bool QMacPrintEnginePrivate::shouldSuppressStatus() const
+{
+ if (suppressStatus == true)
+ return true;
+
+ // Supress displaying the automatic progress dialog if we are printing
+ // from a non-gui thread.
+ return (qApp->thread() != QThread::currentThread());
+}
+
+QPrinter::PrinterState QMacPrintEngine::printerState() const
+{
+ return d_func()->state;
+}
+
+bool QMacPrintEngine::newPage()
+{
+ Q_D(QMacPrintEngine);
+ Q_ASSERT(d->state == QPrinter::Active);
+ OSStatus err =
+#ifndef QT_MAC_USE_COCOA
+ d->shouldSuppressStatus() ? PMSessionEndPageNoDialog(d->session)
+ : PMSessionEndPage(d->session);
+#else
+ PMSessionEndPageNoDialog(d->session);
+#endif
+ if (err != noErr) {
+ if (err == kPMCancel) {
+ // User canceled, we need to abort!
+ abort();
+ } else {
+ // Not sure what the problem is...
+ qWarning("QMacPrintEngine::newPage: Cannot end current page. %ld", long(err));
+ d->state = QPrinter::Error;
+ }
+ return false;
+ }
+ return d->newPage_helper();
+}
+
+bool QMacPrintEngine::abort()
+{
+ Q_D(QMacPrintEngine);
+ if (d->state != QPrinter::Active)
+ return false;
+ bool ret = end();
+ d->state = QPrinter::Aborted;
+ return ret;
+}
+
+static inline int qt_get_PDMWidth(PMPageFormat pformat, bool fullPage,
+ const PMResolution &resolution)
+{
+ int val = 0;
+ PMRect r;
+ qreal hRatio = resolution.hRes / 72;
+ if (fullPage) {
+ if (PMGetAdjustedPaperRect(pformat, &r) == noErr)
+ val = qRound((r.right - r.left) * hRatio);
+ } else {
+ if (PMGetAdjustedPageRect(pformat, &r) == noErr)
+ val = qRound((r.right - r.left) * hRatio);
+ }
+ return val;
+}
+
+static inline int qt_get_PDMHeight(PMPageFormat pformat, bool fullPage,
+ const PMResolution &resolution)
+{
+ int val = 0;
+ PMRect r;
+ qreal vRatio = resolution.vRes / 72;
+ if (fullPage) {
+ if (PMGetAdjustedPaperRect(pformat, &r) == noErr)
+ val = qRound((r.bottom - r.top) * vRatio);
+ } else {
+ if (PMGetAdjustedPageRect(pformat, &r) == noErr)
+ val = qRound((r.bottom - r.top) * vRatio);
+ }
+ return val;
+}
+
+
+int QMacPrintEngine::metric(QPaintDevice::PaintDeviceMetric m) const
+{
+ Q_D(const QMacPrintEngine);
+ int val = 1;
+ switch (m) {
+ case QPaintDevice::PdmWidth:
+ if (d->hasCustomPaperSize) {
+ val = qRound(d->customSize.width());
+ if (d->hasCustomPageMargins) {
+ val -= qRound(d->leftMargin + d->rightMargin);
+ } else {
+ QList<QVariant> margins = property(QPrintEngine::PPK_PageMargins).toList();
+ val -= qRound(margins.at(0).toDouble() + margins.at(2).toDouble());
+ }
+ } else {
+ val = qt_get_PDMWidth(d->format, property(PPK_FullPage).toBool(), d->resolution);
+ }
+ break;
+ case QPaintDevice::PdmHeight:
+ if (d->hasCustomPaperSize) {
+ val = qRound(d->customSize.height());
+ if (d->hasCustomPageMargins) {
+ val -= qRound(d->topMargin + d->bottomMargin);
+ } else {
+ QList<QVariant> margins = property(QPrintEngine::PPK_PageMargins).toList();
+ val -= qRound(margins.at(1).toDouble() + margins.at(3).toDouble());
+ }
+ } else {
+ val = qt_get_PDMHeight(d->format, property(PPK_FullPage).toBool(), d->resolution);
+ }
+ break;
+ case QPaintDevice::PdmWidthMM:
+ val = metric(QPaintDevice::PdmWidth);
+ val = int((val * 254 + 5 * d->resolution.hRes) / (10 * d->resolution.hRes));
+ break;
+ case QPaintDevice::PdmHeightMM:
+ val = metric(QPaintDevice::PdmHeight);
+ val = int((val * 254 + 5 * d->resolution.vRes) / (10 * d->resolution.vRes));
+ break;
+ case QPaintDevice::PdmPhysicalDpiX:
+ case QPaintDevice::PdmPhysicalDpiY: {
+ PMPrinter printer;
+ if(PMSessionGetCurrentPrinter(d->session, &printer) == noErr) {
+ PMResolution resolution;
+#ifndef QT_MAC_USE_COCOA
+# if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5)
+ if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_5) {
+ PMPrinterGetOutputResolution(printer, d->settings, &resolution);
+ } else
+# endif
+ {
+ PMPrinterGetPrinterResolution(printer, kPMCurrentValue, &resolution);
+ }
+#else
+ PMPrinterGetOutputResolution(printer, d->settings, &resolution);
+#endif
+ val = (int)resolution.vRes;
+ break;
+ }
+ //otherwise fall through
+ }
+ case QPaintDevice::PdmDpiY:
+ val = (int)d->resolution.vRes;
+ break;
+ case QPaintDevice::PdmDpiX:
+ val = (int)d->resolution.hRes;
+ break;
+ case QPaintDevice::PdmNumColors:
+ val = (1 << metric(QPaintDevice::PdmDepth));
+ break;
+ case QPaintDevice::PdmDepth:
+ val = 24;
+ break;
+ default:
+ val = 0;
+ qWarning("QPrinter::metric: Invalid metric command");
+ }
+ return val;
+}
+
+void QMacPrintEnginePrivate::initialize()
+{
+ Q_Q(QMacPrintEngine);
+
+#ifndef QT_MAC_USE_COCOA
+ Q_ASSERT(!session);
+#else
+ Q_ASSERT(!printInfo);
+#endif
+
+ if (!paintEngine)
+ paintEngine = new QCoreGraphicsPaintEngine();
+
+ q->gccaps = paintEngine->gccaps;
+
+ fullPage = false;
+
+#ifndef QT_MAC_USE_COCOA
+ if (PMCreateSession(&session) != 0)
+ session = 0;
+#else
+ QMacCocoaAutoReleasePool pool;
+ printInfo = [[NSPrintInfo alloc] initWithDictionary:[NSDictionary dictionary]];
+ session = static_cast<PMPrintSession>([printInfo PMPrintSession]);
+#endif
+
+ PMPrinter printer;
+ if (session && PMSessionGetCurrentPrinter(session, &printer) == noErr) {
+ QList<QVariant> resolutions = supportedResolutions();
+ if (!resolutions.isEmpty() && mode != QPrinter::ScreenResolution) {
+ if (resolutions.count() > 1 && mode == QPrinter::HighResolution) {
+ int max = 0;
+ for (int i = 0; i < resolutions.count(); ++i) {
+ int value = resolutions.at(i).toInt();
+ if (value > max)
+ max = value;
+ }
+ resolution.hRes = resolution.vRes = max;
+ } else {
+ resolution.hRes = resolution.vRes = resolutions.at(0).toInt();
+ }
+ if(resolution.hRes == 0)
+ resolution.hRes = resolution.vRes = 600;
+ } else {
+ resolution.hRes = resolution.vRes = qt_defaultDpi();
+ }
+ }
+
+#ifndef QT_MAC_USE_COCOA
+ bool settingsInitialized = (settings != 0);
+ bool settingsOK = !settingsInitialized ? PMCreatePrintSettings(&settings) == noErr : true;
+ if (settingsOK && !settingsInitialized)
+ settingsOK = PMSessionDefaultPrintSettings(session, settings) == noErr;
+
+
+ bool formatInitialized = (format != 0);
+ bool formatOK = !formatInitialized ? PMCreatePageFormat(&format) == noErr : true;
+ if (formatOK) {
+ if (!formatInitialized) {
+ formatOK = PMSessionDefaultPageFormat(session, format) == noErr;
+ }
+ formatOK = PMSessionValidatePageFormat(session, format, kPMDontWantBoolean) == noErr;
+ }
+#else
+ settings = static_cast<PMPrintSettings>([printInfo PMPrintSettings]);
+ format = static_cast<PMPageFormat>([printInfo PMPageFormat]);
+#endif
+
+#ifndef QT_MAC_USE_COCOA
+# if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4)
+ if (QSysInfo::MacintoshVersion < QSysInfo::MV_10_4)
+# endif
+ {
+ if(paintEngine->type() == QPaintEngine::CoreGraphics) {
+ CFStringRef strings[1] = { kPMGraphicsContextCoreGraphics };
+ QCFType<CFArrayRef> contextArray = CFArrayCreate(kCFAllocatorDefault,
+ reinterpret_cast<const void **>(strings),
+ 1, &kCFTypeArrayCallBacks);
+ OSStatus err = PMSessionSetDocumentFormatGeneration(session, kPMDocumentFormatPDF,
+ contextArray, 0);
+ if(err != noErr) {
+ qWarning("QMacPrintEngine::initialize: Cannot set format generation to PDF: %ld", err);
+ state = QPrinter::Error;
+ }
+ }
+ }
+ if (!settingsOK || !formatOK) {
+ qWarning("QMacPrintEngine::initialize: Unable to initialize QPainter");
+ state = QPrinter::Error;
+ }
+#endif
+
+ QHash<QMacPrintEngine::PrintEnginePropertyKey, QVariant>::const_iterator propC;
+ for (propC = valueCache.constBegin(); propC != valueCache.constEnd(); propC++) {
+ q->setProperty(propC.key(), propC.value());
+ }
+}
+
+bool QMacPrintEnginePrivate::newPage_helper()
+{
+ Q_Q(QMacPrintEngine);
+ Q_ASSERT(state == QPrinter::Active);
+
+ if (PMSessionError(session) != noErr) {
+ q->abort();
+ return false;
+ }
+
+ // pop the stack of saved graphic states, in case we get the same
+ // context back - either way, the stack count should be 0 when we
+ // get the new one
+ QCoreGraphicsPaintEngine *cgEngine = static_cast<QCoreGraphicsPaintEngine*>(paintEngine);
+ while (cgEngine->d_func()->stackCount > 0)
+ cgEngine->d_func()->restoreGraphicsState();
+
+ OSStatus status =
+#ifndef QT_MAC_USE_COCOA
+ shouldSuppressStatus() ? PMSessionBeginPageNoDialog(session, format, 0)
+ : PMSessionBeginPage(session, format, 0);
+#else
+ PMSessionBeginPageNoDialog(session, format, 0);
+#endif
+ if(status != noErr) {
+ state = QPrinter::Error;
+ return false;
+ }
+
+ QRect page = q->property(QPrintEngine::PPK_PageRect).toRect();
+ QRect paper = q->property(QPrintEngine::PPK_PaperRect).toRect();
+
+ CGContextRef cgContext;
+ OSStatus err = noErr;
+#ifndef QT_MAC_USE_COCOA
+# if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4)
+ if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_4) {
+ err = PMSessionGetCGGraphicsContext(session, &cgContext);
+ } else
+# endif
+ {
+ err = PMSessionGetGraphicsContext(session, kPMGraphicsContextCoreGraphics,
+ reinterpret_cast<void **>(&cgContext));
+ }
+#else
+ err = PMSessionGetCGGraphicsContext(session, &cgContext);
+#endif
+ if(err != noErr) {
+ qWarning("QMacPrintEngine::newPage: Cannot retrieve CoreGraphics context: %ld", long(err));
+ state = QPrinter::Error;
+ return false;
+ }
+ cgEngine->d_func()->hd = cgContext;
+
+ // Set the resolution as a scaling ration of 72 (the default).
+ CGContextScaleCTM(cgContext, 72 / resolution.hRes, 72 / resolution.vRes);
+
+ CGContextScaleCTM(cgContext, 1, -1);
+ CGContextTranslateCTM(cgContext, 0, -paper.height());
+ if (!fullPage)
+ CGContextTranslateCTM(cgContext, page.x() - paper.x(), page.y() - paper.y());
+ cgEngine->d_func()->orig_xform = CGContextGetCTM(cgContext);
+ cgEngine->d_func()->setClip(0);
+ cgEngine->state->dirtyFlags = QPaintEngine::DirtyFlag(QPaintEngine::AllDirty
+ & ~(QPaintEngine::DirtyClipEnabled
+ | QPaintEngine::DirtyClipRegion
+ | QPaintEngine::DirtyClipPath));
+ if (cgEngine->painter()->hasClipping())
+ cgEngine->state->dirtyFlags |= QPaintEngine::DirtyClipEnabled;
+ cgEngine->syncState();
+ return true;
+}
+
+
+void QMacPrintEngine::updateState(const QPaintEngineState &state)
+{
+ d_func()->paintEngine->updateState(state);
+}
+
+void QMacPrintEngine::drawRects(const QRectF *r, int num)
+{
+ Q_D(QMacPrintEngine);
+ Q_ASSERT(d->state == QPrinter::Active);
+ d->paintEngine->drawRects(r, num);
+}
+
+void QMacPrintEngine::drawPoints(const QPointF *points, int pointCount)
+{
+ Q_D(QMacPrintEngine);
+ Q_ASSERT(d->state == QPrinter::Active);
+ d->paintEngine->drawPoints(points, pointCount);
+}
+
+void QMacPrintEngine::drawEllipse(const QRectF &r)
+{
+ Q_D(QMacPrintEngine);
+ Q_ASSERT(d->state == QPrinter::Active);
+ d->paintEngine->drawEllipse(r);
+}
+
+void QMacPrintEngine::drawLines(const QLineF *lines, int lineCount)
+{
+ Q_D(QMacPrintEngine);
+ Q_ASSERT(d->state == QPrinter::Active);
+ d->paintEngine->drawLines(lines, lineCount);
+}
+
+void QMacPrintEngine::drawPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode)
+{
+ Q_D(QMacPrintEngine);
+ Q_ASSERT(d->state == QPrinter::Active);
+ d->paintEngine->drawPolygon(points, pointCount, mode);
+}
+
+void QMacPrintEngine::drawPixmap(const QRectF &r, const QPixmap &pm, const QRectF &sr)
+{
+ Q_D(QMacPrintEngine);
+ Q_ASSERT(d->state == QPrinter::Active);
+ d->paintEngine->drawPixmap(r, pm, sr);
+}
+
+void QMacPrintEngine::drawImage(const QRectF &r, const QImage &pm, const QRectF &sr, Qt::ImageConversionFlags flags)
+{
+ Q_D(QMacPrintEngine);
+ Q_ASSERT(d->state == QPrinter::Active);
+ d->paintEngine->drawImage(r, pm, sr, flags);
+}
+
+void QMacPrintEngine::drawTextItem(const QPointF &p, const QTextItem &ti)
+{
+ Q_D(QMacPrintEngine);
+ Q_ASSERT(d->state == QPrinter::Active);
+ d->paintEngine->drawTextItem(p, ti);
+}
+
+void QMacPrintEngine::drawTiledPixmap(const QRectF &dr, const QPixmap &pixmap, const QPointF &sr)
+{
+ Q_D(QMacPrintEngine);
+ Q_ASSERT(d->state == QPrinter::Active);
+ d->paintEngine->drawTiledPixmap(dr, pixmap, sr);
+}
+
+void QMacPrintEngine::drawPath(const QPainterPath &path)
+{
+ Q_D(QMacPrintEngine);
+ Q_ASSERT(d->state == QPrinter::Active);
+ d->paintEngine->drawPath(path);
+}
+
+
+void QMacPrintEngine::setProperty(PrintEnginePropertyKey key, const QVariant &value)
+{
+ Q_D(QMacPrintEngine);
+
+ d->valueCache.insert(key, value);
+ if (!d->session)
+ return;
+
+ switch (key) {
+ case PPK_CollateCopies:
+ break;
+ case PPK_ColorMode:
+ break;
+ case PPK_Creator:
+ break;
+ case PPK_DocumentName:
+ break;
+ case PPK_PageOrder:
+ break;
+ case PPK_PaperSource:
+ break;
+ case PPK_SelectionOption:
+ break;
+ case PPK_Resolution: {
+ PMPrinter printer;
+ UInt32 count;
+ if (PMSessionGetCurrentPrinter(d->session, &printer) != noErr)
+ break;
+ if (PMPrinterGetPrinterResolutionCount(printer, &count) != noErr)
+ break;
+ PMResolution resolution = { 0.0, 0.0 };
+ PMResolution bestResolution = { 0.0, 0.0 };
+ int dpi = value.toInt();
+ int bestDistance = INT_MAX;
+ for (UInt32 i = 1; i <= count; ++i) { // Yes, it starts at 1
+ if (PMPrinterGetIndexedPrinterResolution(printer, i, &resolution) == noErr) {
+ if (dpi == int(resolution.hRes)) {
+ bestResolution = resolution;
+ break;
+ } else {
+ int distance = qAbs(dpi - int(resolution.hRes));
+ if (distance < bestDistance) {
+ bestDistance = distance;
+ bestResolution = resolution;
+ }
+ }
+ }
+ }
+ PMSessionValidatePageFormat(d->session, d->format, kPMDontWantBoolean);
+ break;
+ }
+
+ case PPK_FullPage:
+ d->fullPage = value.toBool();
+ break;
+ case PPK_NumberOfCopies:
+ PMSetCopies(d->settings, value.toInt(), false);
+ break;
+ case PPK_Orientation: {
+ if (d->state == QPrinter::Active) {
+ qWarning("QMacPrintEngine::setOrientation: Orientation cannot be changed during a print job, ignoring change");
+ } else {
+ QPrinter::Orientation newOrientation = QPrinter::Orientation(value.toInt());
+ if (d->hasCustomPaperSize && (d->orient != newOrientation))
+ d->customSize = QSizeF(d->customSize.height(), d->customSize.width());
+ d->orient = newOrientation;
+ PMOrientation o = d->orient == QPrinter::Portrait ? kPMPortrait : kPMLandscape;
+ PMSetOrientation(d->format, o, false);
+ PMSessionValidatePageFormat(d->session, d->format, kPMDontWantBoolean);
+ }
+ break; }
+ case PPK_OutputFileName:
+ d->outputFilename = value.toString();
+ break;
+ case PPK_PaperSize:
+ d->setPaperSize(QPrinter::PaperSize(value.toInt()));
+ break;
+ case PPK_PrinterName: {
+ OSStatus status = noErr;
+ QCFType<CFArrayRef> printerList;
+ status = PMServerCreatePrinterList(kPMServerLocal, &printerList);
+ if (status == noErr) {
+ CFIndex count = CFArrayGetCount(printerList);
+ for (CFIndex i=0; i<count; ++i) {
+ PMPrinter printer = static_cast<PMPrinter>(const_cast<void *>(CFArrayGetValueAtIndex(printerList, i)));
+ QString name = QCFString::toQString(PMPrinterGetName(printer));
+ if (name == value.toString()) {
+ status = PMSessionSetCurrentPMPrinter(d->session, printer);
+ break;
+ }
+ }
+ }
+ if (status != noErr)
+ qWarning("QMacPrintEngine::setPrinterName: Error setting printer: %ld", long(status));
+ break; }
+ case PPK_SuppressSystemPrintStatus:
+ d->suppressStatus = value.toBool();
+ break;
+ case PPK_CustomPaperSize:
+ {
+ PMOrientation orientation;
+ PMGetOrientation(d->format, &orientation);
+ d->hasCustomPaperSize = true;
+ d->customSize = value.toSizeF();
+ if (orientation != kPMPortrait)
+ d->customSize = QSizeF(d->customSize.height(), d->customSize.width());
+ break;
+ }
+ case PPK_PageMargins:
+ {
+ QList<QVariant> margins(value.toList());
+ Q_ASSERT(margins.size() == 4);
+ d->leftMargin = margins.at(0).toDouble();
+ d->topMargin = margins.at(1).toDouble();
+ d->rightMargin = margins.at(2).toDouble();
+ d->bottomMargin = margins.at(3).toDouble();
+ d->hasCustomPageMargins = true;
+ break;
+ }
+
+ default:
+ break;
+ }
+}
+
+QVariant QMacPrintEngine::property(PrintEnginePropertyKey key) const
+{
+ Q_D(const QMacPrintEngine);
+ QVariant ret;
+
+ if (!d->session && d->valueCache.contains(key))
+ return *d->valueCache.find(key);
+
+ switch (key) {
+ case PPK_CollateCopies:
+ ret = false;
+ break;
+ case PPK_ColorMode:
+ ret = QPrinter::Color;
+ break;
+ case PPK_Creator:
+ break;
+ case PPK_DocumentName:
+ break;
+ case PPK_FullPage:
+ ret = d->fullPage;
+ break;
+ case PPK_NumberOfCopies:
+ ret = 1;
+ break;
+ case PPK_Orientation:
+ PMOrientation orientation;
+ PMGetOrientation(d->format, &orientation);
+ ret = orientation == kPMPortrait ? QPrinter::Portrait : QPrinter::Landscape;
+ break;
+ case PPK_OutputFileName:
+ ret = d->outputFilename;
+ break;
+ case PPK_PageOrder:
+ break;
+ case PPK_PaperSource:
+ break;
+ case PPK_PageRect: {
+ // PageRect is returned in device pixels
+ QRect r;
+ PMRect macrect, macpaper;
+ qreal hRatio = d->resolution.hRes / 72;
+ qreal vRatio = d->resolution.vRes / 72;
+ if (d->hasCustomPaperSize) {
+ r = QRect(0, 0, qRound(d->customSize.width() * hRatio), qRound(d->customSize.height() * vRatio));
+ if (d->hasCustomPageMargins) {
+ r.adjust(qRound(d->leftMargin * hRatio), qRound(d->topMargin * vRatio),
+ -qRound(d->rightMargin * hRatio), -qRound(d->bottomMargin * vRatio));
+ } else {
+ QList<QVariant> margins = property(QPrintEngine::PPK_PageMargins).toList();
+ r.adjust(qRound(margins.at(0).toDouble() * hRatio),
+ qRound(margins.at(1).toDouble() * vRatio),
+ -qRound(margins.at(2).toDouble() * hRatio),
+ -qRound(margins.at(3).toDouble()) * vRatio);
+ }
+ } else if (PMGetAdjustedPageRect(d->format, &macrect) == noErr
+ && PMGetAdjustedPaperRect(d->format, &macpaper) == noErr)
+ {
+ if (d->fullPage || d->hasCustomPageMargins) {
+ r.setCoords(int(macpaper.left * hRatio), int(macpaper.top * vRatio),
+ int(macpaper.right * hRatio), int(macpaper.bottom * vRatio));
+ r.translate(-r.x(), -r.y());
+ if (d->hasCustomPageMargins) {
+ r.adjust(qRound(d->leftMargin * hRatio), qRound(d->topMargin * vRatio),
+ -qRound(d->rightMargin * hRatio), -qRound(d->bottomMargin * vRatio));
+ }
+ } else {
+ r.setCoords(int(macrect.left * hRatio), int(macrect.top * vRatio),
+ int(macrect.right * hRatio), int(macrect.bottom * vRatio));
+ r.translate(int(-macpaper.left * hRatio), int(-macpaper.top * vRatio));
+ }
+ }
+ ret = r;
+ break; }
+ case PPK_PaperSize:
+ ret = d->paperSize();
+ break;
+ case PPK_PaperRect: {
+ QRect r;
+ PMRect macrect;
+ if (d->hasCustomPaperSize) {
+ r = QRect(0, 0, qRound(d->customSize.width()), qRound(d->customSize.height()));
+ } else if (PMGetAdjustedPaperRect(d->format, &macrect) == noErr) {
+ qreal hRatio = d->resolution.hRes / 72;
+ qreal vRatio = d->resolution.vRes / 72;
+ r.setCoords(int(macrect.left * hRatio), int(macrect.top * vRatio),
+ int(macrect.right * hRatio), int(macrect.bottom * vRatio));
+ r.translate(-r.x(), -r.y());
+ }
+ ret = r;
+ break; }
+ case PPK_PrinterName: {
+ CFIndex currIndex;
+ PMPrinter unused;
+ QCFType<CFArrayRef> printerList;
+ OSStatus status = PMSessionCreatePrinterList(d->session, &printerList, &currIndex, &unused);
+ if (status != noErr)
+ qWarning("QMacPrintEngine::printerName: Problem getting list of printers: %ld", long(status));
+ if (currIndex != -1 && printerList && currIndex < CFArrayGetCount(printerList)) {
+ const CFStringRef name = static_cast<CFStringRef>(CFArrayGetValueAtIndex(printerList, currIndex));
+ if (name)
+ ret = QCFString::toQString(name);
+ }
+ break; }
+ case PPK_Resolution: {
+ ret = d->resolution.hRes;
+ break;
+ }
+ case PPK_SupportedResolutions:
+ ret = d->supportedResolutions();
+ break;
+ case PPK_CustomPaperSize:
+ ret = d->customSize;
+ break;
+ case PPK_PageMargins:
+ {
+ QList<QVariant> margins;
+ if (d->hasCustomPageMargins) {
+ margins << d->leftMargin << d->topMargin
+ << d->rightMargin << d->bottomMargin;
+ } else {
+ PMPaperMargins paperMargins;
+ PMPaper paper;
+ PMGetPageFormatPaper(d->format, &paper);
+ PMPaperGetMargins(paper, &paperMargins);
+ margins << paperMargins.left << paperMargins.top
+ << paperMargins.right << paperMargins.bottom;
+ }
+ ret = margins;
+ break;
+ }
+ default:
+ break;
+ }
+ return ret;
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_PRINTER
diff --git a/src/gui/painting/qprintengine_mac_p.h b/src/gui/painting/qprintengine_mac_p.h
new file mode 100644
index 0000000000..5e18845eb6
--- /dev/null
+++ b/src/gui/painting/qprintengine_mac_p.h
@@ -0,0 +1,165 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QPRINTENGINE_MAC_P_H
+#define QPRINTENGINE_MAC_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_PRINTER
+
+#include "QtGui/qprinter.h"
+#include "QtGui/qprintengine.h"
+#include "private/qpaintengine_mac_p.h"
+#include "private/qpainter_p.h"
+
+#ifdef __OBJC__
+@class NSPrintInfo;
+#else
+typedef void NSPrintInfo;
+#endif
+
+QT_BEGIN_NAMESPACE
+
+class QPrinterPrivate;
+class QMacPrintEnginePrivate;
+class QMacPrintEngine : public QPaintEngine, public QPrintEngine
+{
+ Q_DECLARE_PRIVATE(QMacPrintEngine)
+public:
+ QMacPrintEngine(QPrinter::PrinterMode mode);
+
+ Qt::HANDLE handle() const;
+
+ bool begin(QPaintDevice *dev);
+ bool end();
+ virtual QPaintEngine::Type type() const { return QPaintEngine::MacPrinter; }
+
+ QPaintEngine *paintEngine() const;
+
+ void setProperty(PrintEnginePropertyKey key, const QVariant &value);
+ QVariant property(PrintEnginePropertyKey key) const;
+
+ QPrinter::PrinterState printerState() const;
+
+ bool newPage();
+ bool abort();
+ int metric(QPaintDevice::PaintDeviceMetric) const;
+
+ //forwarded functions
+
+ void updateState(const QPaintEngineState &state);
+
+ virtual void drawLines(const QLineF *lines, int lineCount);
+ virtual void drawRects(const QRectF *r, int num);
+ virtual void drawPoints(const QPointF *p, int pointCount);
+ virtual void drawEllipse(const QRectF &r);
+ virtual void drawPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode);
+ virtual void drawPixmap(const QRectF &r, const QPixmap &pm, const QRectF &sr);
+ virtual void drawImage(const QRectF &r, const QImage &pm, const QRectF &sr, Qt::ImageConversionFlags flags);
+ virtual void drawTextItem(const QPointF &p, const QTextItem &ti);
+ virtual void drawTiledPixmap(const QRectF &r, const QPixmap &pixmap, const QPointF &s);
+ virtual void drawPath(const QPainterPath &);
+
+private:
+ friend class QPrintDialog;
+ friend class QPageSetupDialog;
+};
+
+class QMacPrintEnginePrivate : public QPaintEnginePrivate
+{
+ Q_DECLARE_PUBLIC(QMacPrintEngine)
+public:
+ QPrinter::PrinterMode mode;
+ QPrinter::PrinterState state;
+ QPrinter::Orientation orient;
+ NSPrintInfo *printInfo;
+ PMPageFormat format;
+ PMPrintSettings settings;
+ PMPrintSession session;
+ PMResolution resolution;
+ QString outputFilename;
+ bool fullPage;
+ QPaintEngine *paintEngine;
+ bool suppressStatus;
+ bool hasCustomPaperSize;
+ QSizeF customSize;
+ bool hasCustomPageMargins;
+ qreal leftMargin;
+ qreal topMargin;
+ qreal rightMargin;
+ qreal bottomMargin;
+ QHash<QMacPrintEngine::PrintEnginePropertyKey, QVariant> valueCache;
+ QMacPrintEnginePrivate() : mode(QPrinter::ScreenResolution), state(QPrinter::Idle),
+ orient(QPrinter::Portrait), printInfo(0), format(0), settings(0),
+ session(0), paintEngine(0), suppressStatus(false),
+ hasCustomPaperSize(false), hasCustomPageMargins(false) {}
+ ~QMacPrintEnginePrivate();
+ void initialize();
+ bool newPage_helper();
+ void setPaperSize(QPrinter::PaperSize ps);
+ QPrinter::PaperSize paperSize() const;
+ QList<QVariant> supportedResolutions() const;
+ inline bool isPrintSessionInitialized() const
+ {
+#ifndef QT_MAC_USE_COCOA
+ return session != 0;
+#else
+ return printInfo != 0;
+#endif
+ }
+ bool shouldSuppressStatus() const;
+};
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_PRINTER
+
+#endif // QPRINTENGINE_WIN_P_H
diff --git a/src/gui/painting/qprintengine_pdf.cpp b/src/gui/painting/qprintengine_pdf.cpp
new file mode 100644
index 0000000000..2e063b714f
--- /dev/null
+++ b/src/gui/painting/qprintengine_pdf.cpp
@@ -0,0 +1,1225 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtGui/qprintengine.h>
+
+#include <qiodevice.h>
+#include <qpainter.h>
+#include <qbitmap.h>
+#include <qpainterpath.h>
+#include <qpaintdevice.h>
+#include <qfile.h>
+#include <qdebug.h>
+#include <qimagewriter.h>
+#include <qbuffer.h>
+#include <qdatetime.h>
+
+#ifndef QT_NO_PRINTER
+#include <limits.h>
+#include <math.h>
+#ifndef QT_NO_COMPRESS
+#include <zlib.h>
+#endif
+
+#if defined(Q_OS_WINCE)
+#include "qwinfunctions_wince.h"
+#endif
+
+#include "qprintengine_pdf_p.h"
+#include "private/qdrawhelper_p.h"
+
+QT_BEGIN_NAMESPACE
+
+extern qint64 qt_pixmap_id(const QPixmap &pixmap);
+extern qint64 qt_image_id(const QImage &image);
+
+//#define FONT_DUMP
+
+// might be helpful for smooth transforms of images
+// Can't use it though, as gs generates completely wrong images if this is true.
+static const bool interpolateImages = false;
+
+#ifdef QT_NO_COMPRESS
+static const bool do_compress = false;
+#else
+static const bool do_compress = true;
+#endif
+
+QPdfPage::QPdfPage()
+ : QPdf::ByteStream(true) // Enable file backing
+{
+}
+
+void QPdfPage::streamImage(int w, int h, int object)
+{
+ *this << w << "0 0 " << -h << "0 " << h << "cm /Im" << object << " Do\n";
+ if (!images.contains(object))
+ images.append(object);
+}
+
+
+inline QPaintEngine::PaintEngineFeatures qt_pdf_decide_features()
+{
+ QPaintEngine::PaintEngineFeatures f = QPaintEngine::AllFeatures;
+ f &= ~(QPaintEngine::PorterDuff | QPaintEngine::PerspectiveTransform
+ | QPaintEngine::ObjectBoundingModeGradients
+#ifndef USE_NATIVE_GRADIENTS
+ | QPaintEngine::LinearGradientFill
+#endif
+ | QPaintEngine::RadialGradientFill
+ | QPaintEngine::ConicalGradientFill);
+ return f;
+}
+
+QPdfEngine::QPdfEngine(QPrinter::PrinterMode m)
+ : QPdfBaseEngine(*new QPdfEnginePrivate(m), qt_pdf_decide_features())
+{
+ state = QPrinter::Idle;
+}
+
+QPdfEngine::~QPdfEngine()
+{
+}
+
+bool QPdfEngine::begin(QPaintDevice *pdev)
+{
+ Q_D(QPdfEngine);
+
+ if(!QPdfBaseEngine::begin(pdev)) {
+ state = QPrinter::Error;
+ return false;
+ }
+ d->stream->setDevice(d->outDevice);
+
+ d->streampos = 0;
+ d->hasPen = true;
+ d->hasBrush = false;
+ d->clipEnabled = false;
+ d->allClipped = false;
+
+ d->xrefPositions.clear();
+ d->pageRoot = 0;
+ d->catalog = 0;
+ d->info = 0;
+ d->graphicsState = 0;
+ d->patternColorSpace = 0;
+
+ d->pages.clear();
+ d->imageCache.clear();
+
+ setActive(true);
+ state = QPrinter::Active;
+ d->writeHeader();
+ newPage();
+
+ return true;
+}
+
+bool QPdfEngine::end()
+{
+ Q_D(QPdfEngine);
+ d->writeTail();
+
+ d->stream->unsetDevice();
+ QPdfBaseEngine::end();
+ setActive(false);
+ state = QPrinter::Idle;
+ return true;
+}
+
+
+void QPdfEngine::drawPixmap (const QRectF &rectangle, const QPixmap &pixmap, const QRectF &sr)
+{
+ if (sr.isEmpty() || rectangle.isEmpty() || pixmap.isNull())
+ return;
+ Q_D(QPdfEngine);
+
+ QBrush b = d->brush;
+
+ QRect sourceRect = sr.toRect();
+ QPixmap pm = sourceRect != pixmap.rect() ? pixmap.copy(sourceRect) : pixmap;
+ QImage image = pm.toImage();
+ bool bitmap = true;
+ const int object = d->addImage(image, &bitmap, pm.cacheKey());
+ if (object < 0)
+ return;
+
+ *d->currentPage << "q\n/GSa gs\n";
+ *d->currentPage
+ << QPdf::generateMatrix(QTransform(rectangle.width() / sr.width(), 0, 0, rectangle.height() / sr.height(),
+ rectangle.x(), rectangle.y()) * (d->simplePen ? QTransform() : d->stroker.matrix));
+ if (bitmap) {
+ // set current pen as d->brush
+ d->brush = d->pen.brush();
+ }
+ setBrush();
+ d->currentPage->streamImage(image.width(), image.height(), object);
+ *d->currentPage << "Q\n";
+
+ d->brush = b;
+}
+
+void QPdfEngine::drawImage(const QRectF &rectangle, const QImage &image, const QRectF &sr, Qt::ImageConversionFlags)
+{
+ if (sr.isEmpty() || rectangle.isEmpty() || image.isNull())
+ return;
+ Q_D(QPdfEngine);
+
+ QRect sourceRect = sr.toRect();
+ QImage im = sourceRect != image.rect() ? image.copy(sourceRect) : image;
+ bool bitmap = true;
+ const int object = d->addImage(image, &bitmap, im.cacheKey());
+ if (object < 0)
+ return;
+
+ *d->currentPage << "q\n/GSa gs\n";
+ *d->currentPage
+ << QPdf::generateMatrix(QTransform(rectangle.width() / sr.width(), 0, 0, rectangle.height() / sr.height(),
+ rectangle.x(), rectangle.y()) * (d->simplePen ? QTransform() : d->stroker.matrix));
+ setBrush();
+ d->currentPage->streamImage(im.width(), im.height(), object);
+ *d->currentPage << "Q\n";
+}
+
+void QPdfEngine::drawTiledPixmap (const QRectF &rectangle, const QPixmap &pixmap, const QPointF &point)
+{
+ Q_D(QPdfEngine);
+
+ bool bitmap = (pixmap.depth() == 1);
+ QBrush b = d->brush;
+ QPointF bo = d->brushOrigin;
+ bool hp = d->hasPen;
+ d->hasPen = false;
+ bool hb = d->hasBrush;
+ d->hasBrush = true;
+
+ d->brush = QBrush(pixmap);
+ if (bitmap)
+ // #### fix bitmap case where we have a brush pen
+ d->brush.setColor(d->pen.color());
+
+ d->brushOrigin = -point;
+ *d->currentPage << "q\n";
+ setBrush();
+
+ drawRects(&rectangle, 1);
+ *d->currentPage << "Q\n";
+
+ d->hasPen = hp;
+ d->hasBrush = hb;
+ d->brush = b;
+ d->brushOrigin = bo;
+}
+
+
+void QPdfEngine::setBrush()
+{
+ Q_D(QPdfEngine);
+ Qt::BrushStyle style = d->brush.style();
+ if (style == Qt::NoBrush)
+ return;
+
+ bool specifyColor;
+ int gStateObject = 0;
+ int patternObject = d->addBrushPattern(d->stroker.matrix, &specifyColor, &gStateObject);
+
+ *d->currentPage << (patternObject ? "/PCSp cs " : "/CSp cs ");
+ if (specifyColor) {
+ QColor rgba = d->brush.color();
+ if (d->colorMode == QPrinter::GrayScale) {
+ qreal gray = qGray(rgba.rgba())/255.;
+ *d->currentPage << gray << gray << gray;
+ } else {
+ *d->currentPage << rgba.redF()
+ << rgba.greenF()
+ << rgba.blueF();
+ }
+ }
+ if (patternObject)
+ *d->currentPage << "/Pat" << patternObject;
+ *d->currentPage << "scn\n";
+
+ if (gStateObject)
+ *d->currentPage << "/GState" << gStateObject << "gs\n";
+ else
+ *d->currentPage << "/GSa gs\n";
+}
+
+QPaintEngine::Type QPdfEngine::type() const
+{
+ return QPaintEngine::Pdf;
+}
+
+bool QPdfEngine::newPage()
+{
+ Q_D(QPdfEngine);
+ if (!isActive())
+ return false;
+ d->newPage();
+ return QPdfBaseEngine::newPage();
+}
+
+QPdfEnginePrivate::QPdfEnginePrivate(QPrinter::PrinterMode m)
+ : QPdfBaseEnginePrivate(m)
+{
+ streampos = 0;
+
+ stream = new QDataStream;
+ pageOrder = QPrinter::FirstPageFirst;
+ orientation = QPrinter::Portrait;
+ fullPage = false;
+}
+
+QPdfEnginePrivate::~QPdfEnginePrivate()
+{
+ delete stream;
+}
+
+
+#ifdef USE_NATIVE_GRADIENTS
+int QPdfEnginePrivate::gradientBrush(const QBrush &b, const QMatrix &matrix, int *gStateObject)
+{
+ const QGradient *gradient = b.gradient();
+ if (!gradient)
+ return 0;
+
+ QTransform inv = matrix.inverted();
+ QPointF page_rect[4] = { inv.map(QPointF(0, 0)),
+ inv.map(QPointF(width_, 0)),
+ inv.map(QPointF(0, height_)),
+ inv.map(QPointF(width_, height_)) };
+
+ bool opaque = b.isOpaque();
+
+ QByteArray shader;
+ QByteArray alphaShader;
+ if (gradient->type() == QGradient::LinearGradient) {
+ const QLinearGradient *lg = static_cast<const QLinearGradient *>(gradient);
+ shader = QPdf::generateLinearGradientShader(lg, page_rect);
+ if (!opaque)
+ alphaShader = QPdf::generateLinearGradientShader(lg, page_rect, true);
+ } else {
+ // #############
+ return 0;
+ }
+ int shaderObject = addXrefEntry(-1);
+ write(shader);
+
+ QByteArray str;
+ QPdf::ByteStream s(&str);
+ s << "<<\n"
+ "/Type /Pattern\n"
+ "/PatternType 2\n"
+ "/Shading " << shaderObject << "0 R\n"
+ "/Matrix ["
+ << matrix.m11()
+ << matrix.m12()
+ << matrix.m21()
+ << matrix.m22()
+ << matrix.dx()
+ << matrix.dy() << "]\n";
+ s << ">>\n"
+ "endobj\n";
+
+ int patternObj = addXrefEntry(-1);
+ write(str);
+ currentPage->patterns.append(patternObj);
+
+ if (!opaque) {
+ bool ca = true;
+ QGradientStops stops = gradient->stops();
+ int a = stops.at(0).second.alpha();
+ for (int i = 1; i < stops.size(); ++i) {
+ if (stops.at(i).second.alpha() != a) {
+ ca = false;
+ break;
+ }
+ }
+ if (ca) {
+ *gStateObject = addConstantAlphaObject(stops.at(0).second.alpha());
+ } else {
+ int alphaShaderObject = addXrefEntry(-1);
+ write(alphaShader);
+
+ QByteArray content;
+ QPdf::ByteStream c(&content);
+ c << "/Shader" << alphaShaderObject << "sh\n";
+
+ QByteArray form;
+ QPdf::ByteStream f(&form);
+ f << "<<\n"
+ "/Type /XObject\n"
+ "/Subtype /Form\n"
+ "/BBox [0 0 " << width_ << height_ << "]\n"
+ "/Group <</S /Transparency >>\n"
+ "/Resources <<\n"
+ "/Shading << /Shader" << alphaShaderObject << alphaShaderObject << "0 R >>\n"
+ ">>\n";
+
+ f << "/Length " << content.length() << "\n"
+ ">>\n"
+ "stream\n"
+ << content
+ << "endstream\n"
+ "endobj\n";
+
+ int softMaskFormObject = addXrefEntry(-1);
+ write(form);
+ *gStateObject = addXrefEntry(-1);
+ xprintf("<< /SMask << /S /Alpha /G %d 0 R >> >>\n"
+ "endobj\n", softMaskFormObject);
+ currentPage->graphicStates.append(*gStateObject);
+ }
+ }
+
+ return patternObj;
+}
+#endif
+
+int QPdfEnginePrivate::addConstantAlphaObject(int brushAlpha, int penAlpha)
+{
+ if (brushAlpha == 255 && penAlpha == 255)
+ return 0;
+ int object = alphaCache.value(QPair<uint, uint>(brushAlpha, penAlpha), 0);
+ if (!object) {
+ object = addXrefEntry(-1);
+ QByteArray alphaDef;
+ QPdf::ByteStream s(&alphaDef);
+ s << "<<\n/ca " << (brushAlpha/qreal(255.)) << "\n";
+ s << "/CA " << (penAlpha/qreal(255.)) << "\n>>";
+ xprintf("%s\nendobj\n", alphaDef.constData());
+ alphaCache.insert(QPair<uint, uint>(brushAlpha, penAlpha), object);
+ }
+ if (currentPage->graphicStates.indexOf(object) < 0)
+ currentPage->graphicStates.append(object);
+
+ return object;
+}
+
+int QPdfEnginePrivate::addBrushPattern(const QTransform &m, bool *specifyColor, int *gStateObject)
+{
+ int paintType = 2; // Uncolored tiling
+ int w = 8;
+ int h = 8;
+
+ *specifyColor = true;
+ *gStateObject = 0;
+
+ QTransform matrix = m;
+ matrix.translate(brushOrigin.x(), brushOrigin.y());
+ matrix = matrix * pageMatrix();
+ //qDebug() << brushOrigin << matrix;
+
+ Qt::BrushStyle style = brush.style();
+ if (style == Qt::LinearGradientPattern) {// && style <= Qt::ConicalGradientPattern) {
+#ifdef USE_NATIVE_GRADIENTS
+ *specifyColor = false;
+ return gradientBrush(b, matrix, gStateObject);
+#else
+ return 0;
+#endif
+ }
+
+ if ((!brush.isOpaque() && brush.style() < Qt::LinearGradientPattern) || opacity != 1.0)
+ *gStateObject = addConstantAlphaObject(qRound(brush.color().alpha() * opacity),
+ qRound(pen.color().alpha() * opacity));
+
+ int imageObject = -1;
+ QByteArray pattern = QPdf::patternForBrush(brush);
+ if (pattern.isEmpty()) {
+ if (brush.style() != Qt::TexturePattern)
+ return 0;
+ QImage image = brush.texture().toImage();
+ bool bitmap = true;
+ imageObject = addImage(image, &bitmap, qt_pixmap_id(brush.texture()));
+ if (imageObject != -1) {
+ QImage::Format f = image.format();
+ if (f != QImage::Format_MonoLSB && f != QImage::Format_Mono) {
+ paintType = 1; // Colored tiling
+ *specifyColor = false;
+ }
+ w = image.width();
+ h = image.height();
+ QTransform m(w, 0, 0, -h, 0, h);
+ QPdf::ByteStream s(&pattern);
+ s << QPdf::generateMatrix(m);
+ s << "/Im" << imageObject << " Do\n";
+ }
+ }
+
+ QByteArray str;
+ QPdf::ByteStream s(&str);
+ s << "<<\n"
+ "/Type /Pattern\n"
+ "/PatternType 1\n"
+ "/PaintType " << paintType << "\n"
+ "/TilingType 1\n"
+ "/BBox [0 0 " << w << h << "]\n"
+ "/XStep " << w << "\n"
+ "/YStep " << h << "\n"
+ "/Matrix ["
+ << matrix.m11()
+ << matrix.m12()
+ << matrix.m21()
+ << matrix.m22()
+ << matrix.dx()
+ << matrix.dy() << "]\n"
+ "/Resources \n<< "; // open resource tree
+ if (imageObject > 0) {
+ s << "/XObject << /Im" << imageObject << ' ' << imageObject << "0 R >> ";
+ }
+ s << ">>\n"
+ "/Length " << pattern.length() << "\n"
+ ">>\n"
+ "stream\n"
+ << pattern
+ << "endstream\n"
+ "endobj\n";
+
+ int patternObj = addXrefEntry(-1);
+ write(str);
+ currentPage->patterns.append(patternObj);
+ return patternObj;
+}
+
+/*!
+ * Adds an image to the pdf and return the pdf-object id. Returns -1 if adding the image failed.
+ */
+int QPdfEnginePrivate::addImage(const QImage &img, bool *bitmap, qint64 serial_no)
+{
+ if (img.isNull())
+ return -1;
+
+ int object = imageCache.value(serial_no);
+ if(object)
+ return object;
+
+ QImage image = img;
+ QImage::Format format = image.format();
+ if (image.depth() == 1 && *bitmap) {
+ if (format == QImage::Format_MonoLSB)
+ image = image.convertToFormat(QImage::Format_Mono);
+ format = QImage::Format_Mono;
+ } else {
+ *bitmap = false;
+ if (format != QImage::Format_RGB32 && format != QImage::Format_ARGB32) {
+ image = image.convertToFormat(QImage::Format_ARGB32);
+ format = QImage::Format_ARGB32;
+ }
+ }
+
+ int w = image.width();
+ int h = image.height();
+ int d = image.depth();
+
+ if (format == QImage::Format_Mono) {
+ int bytesPerLine = (w + 7) >> 3;
+ QByteArray data;
+ data.resize(bytesPerLine * h);
+ char *rawdata = data.data();
+ for (int y = 0; y < h; ++y) {
+ memcpy(rawdata, image.scanLine(y), bytesPerLine);
+ rawdata += bytesPerLine;
+ }
+ object = writeImage(data, w, h, d, 0, 0);
+ } else {
+ QByteArray softMaskData;
+ bool dct = false;
+ QByteArray imageData;
+ bool hasAlpha = false;
+ bool hasMask = false;
+
+ if (QImageWriter::supportedImageFormats().contains("jpeg") && colorMode != QPrinter::GrayScale) {
+ QBuffer buffer(&imageData);
+ QImageWriter writer(&buffer, "jpeg");
+ writer.setQuality(94);
+ writer.write(image);
+ dct = true;
+
+ if (format != QImage::Format_RGB32) {
+ softMaskData.resize(w * h);
+ uchar *sdata = (uchar *)softMaskData.data();
+ for (int y = 0; y < h; ++y) {
+ const QRgb *rgb = (const QRgb *)image.scanLine(y);
+ for (int x = 0; x < w; ++x) {
+ uchar alpha = qAlpha(*rgb);
+ *sdata++ = alpha;
+ hasMask |= (alpha < 255);
+ hasAlpha |= (alpha != 0 && alpha != 255);
+ ++rgb;
+ }
+ }
+ }
+ } else {
+ imageData.resize(colorMode == QPrinter::GrayScale ? w * h : 3 * w * h);
+ uchar *data = (uchar *)imageData.data();
+ softMaskData.resize(w * h);
+ uchar *sdata = (uchar *)softMaskData.data();
+ for (int y = 0; y < h; ++y) {
+ const QRgb *rgb = (const QRgb *)image.scanLine(y);
+ if (colorMode == QPrinter::GrayScale) {
+ for (int x = 0; x < w; ++x) {
+ *(data++) = qGray(*rgb);
+ uchar alpha = qAlpha(*rgb);
+ *sdata++ = alpha;
+ hasMask |= (alpha < 255);
+ hasAlpha |= (alpha != 0 && alpha != 255);
+ ++rgb;
+ }
+ } else {
+ for (int x = 0; x < w; ++x) {
+ *(data++) = qRed(*rgb);
+ *(data++) = qGreen(*rgb);
+ *(data++) = qBlue(*rgb);
+ uchar alpha = qAlpha(*rgb);
+ *sdata++ = alpha;
+ hasMask |= (alpha < 255);
+ hasAlpha |= (alpha != 0 && alpha != 255);
+ ++rgb;
+ }
+ }
+ }
+ if (format == QImage::Format_RGB32)
+ hasAlpha = hasMask = false;
+ }
+ int maskObject = 0;
+ int softMaskObject = 0;
+ if (hasAlpha) {
+ softMaskObject = writeImage(softMaskData, w, h, 8, 0, 0);
+ } else if (hasMask) {
+ // dither the soft mask to 1bit and add it. This also helps PDF viewers
+ // without transparency support
+ int bytesPerLine = (w + 7) >> 3;
+ QByteArray mask(bytesPerLine * h, 0);
+ uchar *mdata = (uchar *)mask.data();
+ const uchar *sdata = (const uchar *)softMaskData.constData();
+ for (int y = 0; y < h; ++y) {
+ for (int x = 0; x < w; ++x) {
+ if (*sdata)
+ mdata[x>>3] |= (0x80 >> (x&7));
+ ++sdata;
+ }
+ mdata += bytesPerLine;
+ }
+ maskObject = writeImage(mask, w, h, 1, 0, 0);
+ }
+ object = writeImage(imageData, w, h, colorMode == QPrinter::GrayScale ? 8 : 32,
+ maskObject, softMaskObject, dct);
+ }
+ imageCache.insert(serial_no, object);
+ return object;
+}
+
+void QPdfEnginePrivate::drawTextItem(const QPointF &p, const QTextItemInt &ti)
+{
+ if (ti.charFormat.isAnchor()) {
+ qreal size = ti.fontEngine->fontDef.pixelSize;
+#ifdef Q_WS_WIN
+ if (ti.fontEngine->type() == QFontEngine::Win) {
+ QFontEngineWin *fe = static_cast<QFontEngineWin *>(ti.fontEngine);
+ size = fe->tm.w.tmHeight;
+ }
+#endif
+ int synthesized = ti.fontEngine->synthesized();
+ qreal stretch = synthesized & QFontEngine::SynthesizedStretch ? ti.fontEngine->fontDef.stretch/100. : 1.;
+
+ QTransform trans;
+ // Build text rendering matrix (Trm). We need it to map the text area to user
+ // space units on the PDF page.
+ trans = QTransform(size*stretch, 0, 0, size, 0, 0);
+ // Apply text matrix (Tm).
+ trans *= QTransform(1,0,0,-1,p.x(),p.y());
+ // Apply page displacement (Identity for first page).
+ trans *= stroker.matrix;
+ // Apply Current Transformation Matrix (CTM)
+ trans *= pageMatrix();
+ qreal x1, y1, x2, y2;
+ trans.map(0, 0, &x1, &y1);
+ trans.map(ti.width.toReal()/size, (ti.ascent.toReal()-ti.descent.toReal())/size, &x2, &y2);
+
+ uint annot = addXrefEntry(-1);
+#ifdef Q_DEBUG_PDF_LINKS
+ xprintf("<<\n/Type /Annot\n/Subtype /Link\n/Rect [%f %f %f %f]\n/Border [16 16 1]\n/A <<\n",
+#else
+ xprintf("<<\n/Type /Annot\n/Subtype /Link\n/Rect [%f %f %f %f]\n/Border [0 0 0]\n/A <<\n",
+#endif
+ static_cast<double>(x1),
+ static_cast<double>(y1),
+ static_cast<double>(x2),
+ static_cast<double>(y2));
+ xprintf("/Type /Action\n/S /URI\n/URI (%s)\n",
+ ti.charFormat.anchorHref().toLatin1().constData());
+ xprintf(">>\n>>\n");
+ xprintf("endobj\n");
+
+ if (!currentPage->annotations.contains(annot)) {
+ currentPage->annotations.append(annot);
+ }
+ }
+
+ QPdfBaseEnginePrivate::drawTextItem(p, ti);
+}
+
+QTransform QPdfEnginePrivate::pageMatrix() const
+{
+ qreal scale = 72./resolution;
+ QTransform tmp(scale, 0.0, 0.0, -scale, 0.0, height());
+ if (!fullPage) {
+ QRect r = pageRect();
+ tmp.translate(r.left(), r.top());
+ }
+ return tmp;
+}
+
+void QPdfEnginePrivate::newPage()
+{
+ if (currentPage && currentPage->pageSize.isEmpty())
+ currentPage->pageSize = QSize(width(), height());
+ writePage();
+
+ delete currentPage;
+ currentPage = new QPdfPage;
+ currentPage->pageSize = QSize(width(), height());
+ stroker.stream = currentPage;
+ pages.append(requestObject());
+
+ *currentPage << "/GSa gs /CSp cs /CSp CS\n"
+ << QPdf::generateMatrix(pageMatrix())
+ << "q q\n";
+}
+
+
+// For strings up to 10000 bytes only !
+void QPdfEnginePrivate::xprintf(const char* fmt, ...)
+{
+ if (!stream)
+ return;
+
+ const int msize = 10000;
+ char buf[msize];
+
+ va_list args;
+ va_start(args, fmt);
+ int bufsize = qvsnprintf(buf, msize, fmt, args);
+
+ Q_ASSERT(bufsize<msize);
+
+ va_end(args);
+
+ stream->writeRawData(buf, bufsize);
+ streampos += bufsize;
+}
+
+int QPdfEnginePrivate::writeCompressed(QIODevice *dev)
+{
+#ifndef QT_NO_COMPRESS
+ if (do_compress) {
+ int size = QPdfPage::chunkSize();
+ int sum = 0;
+ ::z_stream zStruct;
+ zStruct.zalloc = Z_NULL;
+ zStruct.zfree = Z_NULL;
+ zStruct.opaque = Z_NULL;
+ if (::deflateInit(&zStruct, Z_DEFAULT_COMPRESSION) != Z_OK) {
+ qWarning("QPdfStream::writeCompressed: Error in deflateInit()");
+ return sum;
+ }
+ zStruct.avail_in = 0;
+ QByteArray in, out;
+ out.resize(size);
+ while (!dev->atEnd() || zStruct.avail_in != 0) {
+ if (zStruct.avail_in == 0) {
+ in = dev->read(size);
+ zStruct.avail_in = in.size();
+ zStruct.next_in = reinterpret_cast<unsigned char*>(in.data());
+ if (in.size() <= 0) {
+ qWarning("QPdfStream::writeCompressed: Error in read()");
+ ::deflateEnd(&zStruct);
+ return sum;
+ }
+ }
+ zStruct.next_out = reinterpret_cast<unsigned char*>(out.data());
+ zStruct.avail_out = out.size();
+ if (::deflate(&zStruct, 0) != Z_OK) {
+ qWarning("QPdfStream::writeCompressed: Error in deflate()");
+ ::deflateEnd(&zStruct);
+ return sum;
+ }
+ int written = out.size() - zStruct.avail_out;
+ stream->writeRawData(out.constData(), written);
+ streampos += written;
+ sum += written;
+ }
+ int ret;
+ do {
+ zStruct.next_out = reinterpret_cast<unsigned char*>(out.data());
+ zStruct.avail_out = out.size();
+ ret = ::deflate(&zStruct, Z_FINISH);
+ if (ret != Z_OK && ret != Z_STREAM_END) {
+ qWarning("QPdfStream::writeCompressed: Error in deflate()");
+ ::deflateEnd(&zStruct);
+ return sum;
+ }
+ int written = out.size() - zStruct.avail_out;
+ stream->writeRawData(out.constData(), written);
+ streampos += written;
+ sum += written;
+ } while (ret == Z_OK);
+
+ ::deflateEnd(&zStruct);
+
+ return sum;
+ } else
+#endif
+ {
+ QByteArray arr;
+ int sum = 0;
+ while (!dev->atEnd()) {
+ arr = dev->read(QPdfPage::chunkSize());
+ stream->writeRawData(arr.constData(), arr.size());
+ streampos += arr.size();
+ sum += arr.size();
+ }
+ return sum;
+ }
+}
+
+int QPdfEnginePrivate::writeCompressed(const char *src, int len)
+{
+#ifndef QT_NO_COMPRESS
+ if(do_compress) {
+ uLongf destLen = len + len/100 + 13; // zlib requirement
+ Bytef* dest = new Bytef[destLen];
+ if (Z_OK == ::compress(dest, &destLen, (const Bytef*) src, (uLongf)len)) {
+ stream->writeRawData((const char*)dest, destLen);
+ } else {
+ qWarning("QPdfStream::writeCompressed: Error in compress()");
+ destLen = 0;
+ }
+ delete [] dest;
+ len = destLen;
+ } else
+#endif
+ {
+ stream->writeRawData(src,len);
+ }
+ streampos += len;
+ return len;
+}
+
+int QPdfEnginePrivate::writeImage(const QByteArray &data, int width, int height, int depth,
+ int maskObject, int softMaskObject, bool dct)
+{
+ int image = addXrefEntry(-1);
+ xprintf("<<\n"
+ "/Type /XObject\n"
+ "/Subtype /Image\n"
+ "/Width %d\n"
+ "/Height %d\n", width, height);
+
+ if (depth == 1) {
+ xprintf("/ImageMask true\n"
+ "/Decode [1 0]\n");
+ } else {
+ xprintf("/BitsPerComponent 8\n"
+ "/ColorSpace %s\n", (depth == 32) ? "/DeviceRGB" : "/DeviceGray");
+ }
+ if (maskObject > 0)
+ xprintf("/Mask %d 0 R\n", maskObject);
+ if (softMaskObject > 0)
+ xprintf("/SMask %d 0 R\n", softMaskObject);
+
+ int lenobj = requestObject();
+ xprintf("/Length %d 0 R\n", lenobj);
+ if (interpolateImages)
+ xprintf("/Interpolate true\n");
+ int len = 0;
+ if (dct) {
+ //qDebug() << "DCT";
+ xprintf("/Filter /DCTDecode\n>>\nstream\n");
+ write(data);
+ len = data.length();
+ } else {
+ if (do_compress)
+ xprintf("/Filter /FlateDecode\n>>\nstream\n");
+ else
+ xprintf(">>\nstream\n");
+ len = writeCompressed(data);
+ }
+ xprintf("endstream\n"
+ "endobj\n");
+ addXrefEntry(lenobj);
+ xprintf("%d\n"
+ "endobj\n", len);
+ return image;
+}
+
+
+void QPdfEnginePrivate::writeHeader()
+{
+ addXrefEntry(0,false);
+
+ xprintf("%%PDF-1.4\n");
+
+ writeInfo();
+
+ catalog = addXrefEntry(-1);
+ pageRoot = requestObject();
+ xprintf("<<\n"
+ "/Type /Catalog\n"
+ "/Pages %d 0 R\n"
+ ">>\n"
+ "endobj\n", pageRoot);
+
+ // graphics state
+ graphicsState = addXrefEntry(-1);
+ xprintf("<<\n"
+ "/Type /ExtGState\n"
+ "/SA true\n"
+ "/SM 0.02\n"
+ "/ca 1.0\n"
+ "/CA 1.0\n"
+ "/AIS false\n"
+ "/SMask /None"
+ ">>\n"
+ "endobj\n");
+
+ // color space for pattern
+ patternColorSpace = addXrefEntry(-1);
+ xprintf("[/Pattern /DeviceRGB]\n"
+ "endobj\n");
+}
+
+void QPdfEnginePrivate::writeInfo()
+{
+ info = addXrefEntry(-1);
+ xprintf("<<\n"
+ "/Title (%s)\n"
+// "/Author (%s)\n"
+ "/Creator (%s)\n"
+ "/Producer (Qt " QT_VERSION_STR " (C) 1992-$THISYEAR$ Nokia Corporation and/or its subsidiary(-ies))\n",
+ title.toUtf8().constData(),
+// author.toUtf8().constData(),
+ creator.toUtf8().constData());
+
+ QDateTime now = QDateTime::currentDateTime().toUTC();
+ QTime t = now.time();
+ QDate d = now.date();
+ xprintf("/CreationDate (D:%d%02d%02d%02d%02d%02d)\n",
+ d.year(),
+ d.month(),
+ d.day(),
+ t.hour(),
+ t.minute(),
+ t.second());
+ xprintf(">>\n"
+ "endobj\n");
+}
+
+void QPdfEnginePrivate::writePageRoot()
+{
+ addXrefEntry(pageRoot);
+
+ xprintf("<<\n"
+ "/Type /Pages\n"
+ "/Kids \n"
+ "[\n");
+ int size = pages.size();
+ for (int i = 0; i < size; ++i)
+ xprintf("%d 0 R\n", pages[i]);
+ xprintf("]\n");
+
+ //xprintf("/Group <</S /Transparency /I true /K false>>\n");
+ xprintf("/Count %d\n", pages.size());
+
+ xprintf("/ProcSet [/PDF /Text /ImageB /ImageC]\n"
+ ">>\n"
+ "endobj\n");
+}
+
+
+void QPdfEnginePrivate::embedFont(QFontSubset *font)
+{
+ //qDebug() << "embedFont" << font->object_id;
+ int fontObject = font->object_id;
+ QByteArray fontData = font->toTruetype();
+#ifdef FONT_DUMP
+ static int i = 0;
+ QString fileName("font%1.ttf");
+ fileName = fileName.arg(i++);
+ QFile ff(fileName);
+ ff.open(QFile::WriteOnly);
+ ff.write(fontData);
+ ff.close();
+#endif
+
+ int fontDescriptor = requestObject();
+ int fontstream = requestObject();
+ int cidfont = requestObject();
+ int toUnicode = requestObject();
+
+ QFontEngine::Properties properties = font->fontEngine->properties();
+
+ {
+ qreal scale = 1000/properties.emSquare.toReal();
+ addXrefEntry(fontDescriptor);
+ QByteArray descriptor;
+ QPdf::ByteStream s(&descriptor);
+ s << "<< /Type /FontDescriptor\n"
+ "/FontName /Q";
+ int tag = fontDescriptor;
+ for (int i = 0; i < 5; ++i) {
+ s << (char)('A' + (tag % 26));
+ tag /= 26;
+ }
+ s << "+" << properties.postscriptName << "\n"
+ "/Flags " << 4 << "\n"
+ "/FontBBox ["
+ << properties.boundingBox.x()*scale
+ << -(properties.boundingBox.y() + properties.boundingBox.height())*scale
+ << (properties.boundingBox.x() + properties.boundingBox.width())*scale
+ << -properties.boundingBox.y()*scale << "]\n"
+ "/ItalicAngle " << properties.italicAngle.toReal() << "\n"
+ "/Ascent " << properties.ascent.toReal()*scale << "\n"
+ "/Descent " << -properties.descent.toReal()*scale << "\n"
+ "/CapHeight " << properties.capHeight.toReal()*scale << "\n"
+ "/StemV " << properties.lineWidth.toReal()*scale << "\n"
+ "/FontFile2 " << fontstream << "0 R\n"
+ ">> endobj\n";
+ write(descriptor);
+ }
+ {
+ addXrefEntry(fontstream);
+ QByteArray header;
+ QPdf::ByteStream s(&header);
+
+ int length_object = requestObject();
+ s << "<<\n"
+ "/Length1 " << fontData.size() << "\n"
+ "/Length " << length_object << "0 R\n";
+ if (do_compress)
+ s << "/Filter /FlateDecode\n";
+ s << ">>\n"
+ "stream\n";
+ write(header);
+ int len = writeCompressed(fontData);
+ write("endstream\n"
+ "endobj\n");
+ addXrefEntry(length_object);
+ xprintf("%d\n"
+ "endobj\n", len);
+ }
+ {
+ addXrefEntry(cidfont);
+ QByteArray cid;
+ QPdf::ByteStream s(&cid);
+ s << "<< /Type /Font\n"
+ "/Subtype /CIDFontType2\n"
+ "/BaseFont /" << properties.postscriptName << "\n"
+ "/CIDSystemInfo << /Registry (Adobe) /Ordering (Identity) /Supplement 0 >>\n"
+ "/FontDescriptor " << fontDescriptor << "0 R\n"
+ "/CIDToGIDMap /Identity\n"
+ << font->widthArray() <<
+ ">>\n"
+ "endobj\n";
+ write(cid);
+ }
+ {
+ addXrefEntry(toUnicode);
+ QByteArray touc = font->createToUnicodeMap();
+ xprintf("<< /Length %d >>\n"
+ "stream\n", touc.length());
+ write(touc);
+ write("endstream\n"
+ "endobj\n");
+ }
+ {
+ addXrefEntry(fontObject);
+ QByteArray font;
+ QPdf::ByteStream s(&font);
+ s << "<< /Type /Font\n"
+ "/Subtype /Type0\n"
+ "/BaseFont /" << properties.postscriptName << "\n"
+ "/Encoding /Identity-H\n"
+ "/DescendantFonts [" << cidfont << "0 R]\n"
+ "/ToUnicode " << toUnicode << "0 R"
+ ">>\n"
+ "endobj\n";
+ write(font);
+ }
+}
+
+
+void QPdfEnginePrivate::writeFonts()
+{
+ for (QHash<QFontEngine::FaceId, QFontSubset *>::iterator it = fonts.begin(); it != fonts.end(); ++it) {
+ embedFont(*it);
+ delete *it;
+ }
+ fonts.clear();
+}
+
+void QPdfEnginePrivate::writePage()
+{
+ if (pages.empty())
+ return;
+
+ *currentPage << "Q Q\n";
+
+ uint pageStream = requestObject();
+ uint pageStreamLength = requestObject();
+ uint resources = requestObject();
+ uint annots = requestObject();
+
+ addXrefEntry(pages.last());
+ xprintf("<<\n"
+ "/Type /Page\n"
+ "/Parent %d 0 R\n"
+ "/Contents %d 0 R\n"
+ "/Resources %d 0 R\n"
+ "/Annots %d 0 R\n"
+ "/MediaBox [0 0 %d %d]\n"
+ ">>\n"
+ "endobj\n",
+ pageRoot, pageStream, resources, annots,
+ // make sure we use the pagesize from when we started the page, since the user may have changed it
+ currentPage->pageSize.width(), currentPage->pageSize.height());
+
+ addXrefEntry(resources);
+ xprintf("<<\n"
+ "/ColorSpace <<\n"
+ "/PCSp %d 0 R\n"
+ "/CSp /DeviceRGB\n"
+ "/CSpg /DeviceGray\n"
+ ">>\n"
+ "/ExtGState <<\n"
+ "/GSa %d 0 R\n",
+ patternColorSpace, graphicsState);
+
+ for (int i = 0; i < currentPage->graphicStates.size(); ++i)
+ xprintf("/GState%d %d 0 R\n", currentPage->graphicStates.at(i), currentPage->graphicStates.at(i));
+ xprintf(">>\n");
+
+ xprintf("/Pattern <<\n");
+ for (int i = 0; i < currentPage->patterns.size(); ++i)
+ xprintf("/Pat%d %d 0 R\n", currentPage->patterns.at(i), currentPage->patterns.at(i));
+ xprintf(">>\n");
+
+ xprintf("/Font <<\n");
+ for (int i = 0; i < currentPage->fonts.size();++i)
+ xprintf("/F%d %d 0 R\n", currentPage->fonts[i], currentPage->fonts[i]);
+ xprintf(">>\n");
+
+ xprintf("/XObject <<\n");
+ for (int i = 0; i<currentPage->images.size(); ++i) {
+ xprintf("/Im%d %d 0 R\n", currentPage->images.at(i), currentPage->images.at(i));
+ }
+ xprintf(">>\n");
+
+ xprintf(">>\n"
+ "endobj\n");
+
+ addXrefEntry(annots);
+ xprintf("[ ");
+ for (int i = 0; i<currentPage->annotations.size(); ++i) {
+ xprintf("%d 0 R ", currentPage->annotations.at(i));
+ }
+ xprintf("]\nendobj\n");
+
+ addXrefEntry(pageStream);
+ xprintf("<<\n"
+ "/Length %d 0 R\n", pageStreamLength); // object number for stream length object
+ if (do_compress)
+ xprintf("/Filter /FlateDecode\n");
+
+ xprintf(">>\n");
+ xprintf("stream\n");
+ QIODevice *content = currentPage->stream();
+ int len = writeCompressed(content);
+ xprintf("endstream\n"
+ "endobj\n");
+
+ addXrefEntry(pageStreamLength);
+ xprintf("%d\nendobj\n",len);
+}
+
+void QPdfEnginePrivate::writeTail()
+{
+ writePage();
+ writeFonts();
+ writePageRoot();
+ addXrefEntry(xrefPositions.size(),false);
+ xprintf("xref\n"
+ "0 %d\n"
+ "%010d 65535 f \n", xrefPositions.size()-1, xrefPositions[0]);
+
+ for (int i = 1; i < xrefPositions.size()-1; ++i)
+ xprintf("%010d 00000 n \n", xrefPositions[i]);
+
+ xprintf("trailer\n"
+ "<<\n"
+ "/Size %d\n"
+ "/Info %d 0 R\n"
+ "/Root %d 0 R\n"
+ ">>\n"
+ "startxref\n%d\n"
+ "%%%%EOF\n",
+ xrefPositions.size()-1, info, catalog, xrefPositions.last());
+}
+
+int QPdfEnginePrivate::addXrefEntry(int object, bool printostr)
+{
+ if (object < 0)
+ object = requestObject();
+
+ if (object>=xrefPositions.size())
+ xrefPositions.resize(object+1);
+
+ xrefPositions[object] = streampos;
+ if (printostr)
+ xprintf("%d 0 obj\n",object);
+
+ return object;
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_PRINTER
diff --git a/src/gui/painting/qprintengine_pdf_p.h b/src/gui/painting/qprintengine_pdf_p.h
new file mode 100644
index 0000000000..5f7181940f
--- /dev/null
+++ b/src/gui/painting/qprintengine_pdf_p.h
@@ -0,0 +1,194 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QPRINTENGINE_PDF_P_H
+#define QPRINTENGINE_PDF_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 "QtGui/qprintengine.h"
+
+#ifndef QT_NO_PRINTER
+#include "QtCore/qmap.h"
+#include "QtGui/qmatrix.h"
+#include "QtCore/qstring.h"
+#include "QtCore/qvector.h"
+#include "QtGui/qpaintengine.h"
+#include "QtGui/qpainterpath.h"
+#include "QtCore/qdatastream.h"
+
+#include "private/qfontengine_p.h"
+#include "private/qpdf_p.h"
+#include "private/qpaintengine_p.h"
+
+QT_BEGIN_NAMESPACE
+
+// #define USE_NATIVE_GRADIENTS
+
+class QImage;
+class QDataStream;
+class QPen;
+class QPointF;
+class QRegion;
+class QFile;
+class QPdfEngine;
+
+class QPdfEnginePrivate;
+
+class QPdfEngine : public QPdfBaseEngine
+{
+ Q_DECLARE_PRIVATE(QPdfEngine)
+public:
+ QPdfEngine(QPrinter::PrinterMode m);
+ virtual ~QPdfEngine();
+
+ // reimplementations QPaintEngine
+ bool begin(QPaintDevice *pdev);
+ bool end();
+ void drawPixmap (const QRectF & rectangle, const QPixmap & pixmap, const QRectF & sr);
+ void drawImage(const QRectF &r, const QImage &pm, const QRectF &sr,
+ Qt::ImageConversionFlags flags = Qt::AutoColor);
+ void drawTiledPixmap (const QRectF & rectangle, const QPixmap & pixmap, const QPointF & point);
+
+ Type type() const;
+ // end reimplementations QPaintEngine
+
+ // reimplementations QPrintEngine
+ bool abort() {return false;}
+ bool newPage();
+ QPrinter::PrinterState printerState() const {return state;}
+ // end reimplementations QPrintEngine
+
+ void setBrush();
+
+ // ### unused, should have something for this in QPrintEngine
+ void setAuthor(const QString &author);
+ QString author() const;
+
+ void setDevice(QIODevice* dev);
+
+private:
+ Q_DISABLE_COPY(QPdfEngine)
+
+ QPrinter::PrinterState state;
+};
+
+class QPdfEnginePrivate : public QPdfBaseEnginePrivate
+{
+ Q_DECLARE_PUBLIC(QPdfEngine)
+public:
+ QPdfEnginePrivate(QPrinter::PrinterMode m);
+ ~QPdfEnginePrivate();
+
+ void newPage();
+
+ int width() const {
+ QRect r = paperRect();
+ return qRound(r.width()*72./resolution);
+ }
+ int height() const {
+ QRect r = paperRect();
+ return qRound(r.height()*72./resolution);
+ }
+
+ void writeHeader();
+ void writeTail();
+
+ int addImage(const QImage &image, bool *bitmap, qint64 serial_no);
+ int addConstantAlphaObject(int brushAlpha, int penAlpha = 255);
+ int addBrushPattern(const QTransform &matrix, bool *specifyColor, int *gStateObject);
+
+ void drawTextItem(const QPointF &p, const QTextItemInt &ti);
+
+ QTransform pageMatrix() const;
+
+private:
+ Q_DISABLE_COPY(QPdfEnginePrivate)
+
+#ifdef USE_NATIVE_GRADIENTS
+ int gradientBrush(const QBrush &b, const QMatrix &matrix, int *gStateObject);
+#endif
+
+ void writeInfo();
+ void writePageRoot();
+ void writeFonts();
+ void embedFont(QFontSubset *font);
+
+ QVector<int> xrefPositions;
+ QDataStream* stream;
+ int streampos;
+
+ int writeImage(const QByteArray &data, int width, int height, int depth,
+ int maskObject, int softMaskObject, bool dct = false);
+ void writePage();
+
+ int addXrefEntry(int object, bool printostr = true);
+ void xprintf(const char* fmt, ...);
+ inline void write(const QByteArray &data) {
+ stream->writeRawData(data.constData(), data.size());
+ streampos += data.size();
+ }
+
+ int writeCompressed(const char *src, int len);
+ inline int writeCompressed(const QByteArray &data) { return writeCompressed(data.constData(), data.length()); }
+ int writeCompressed(QIODevice *dev);
+
+ // various PDF objects
+ int pageRoot, catalog, info, graphicsState, patternColorSpace;
+ QVector<uint> pages;
+ QHash<qint64, uint> imageCache;
+ QHash<QPair<uint, uint>, uint > alphaCache;
+};
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_PRINTER
+
+#endif // QPRINTENGINE_PDF_P_H
diff --git a/src/gui/painting/qprintengine_ps.cpp b/src/gui/painting/qprintengine_ps.cpp
new file mode 100644
index 0000000000..97ec640e81
--- /dev/null
+++ b/src/gui/painting/qprintengine_ps.cpp
@@ -0,0 +1,969 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qplatformdefs.h"
+
+#include <private/qprintengine_ps_p.h>
+#include <private/qpainter_p.h>
+#include <private/qfontengine_p.h>
+#include <private/qpaintengine_p.h>
+#include <private/qpdf_p.h>
+
+#ifndef QT_NO_PRINTER
+
+#include "qprinter.h"
+#include "qpainter.h"
+#include "qapplication.h"
+#include "qpixmap.h"
+#include "qimage.h"
+#include "qdatetime.h"
+#include "qstring.h"
+#include "qbytearray.h"
+#include "qhash.h"
+#include "qbuffer.h"
+#include "qsettings.h"
+#include "qmap.h"
+#include "qbitmap.h"
+#include "qregion.h"
+#include "qimagewriter.h"
+#include <private/qunicodetables_p.h>
+#include <private/qpainterpath_p.h>
+#include <qdebug.h>
+#include <private/qdrawhelper_p.h>
+#include <private/qmutexpool_p.h>
+
+#ifndef Q_OS_WIN
+#include <unistd.h>
+#endif
+#include <stdlib.h>
+#include <limits.h>
+
+QT_BEGIN_NAMESPACE
+
+static bool qt_gen_epsf = false;
+
+void qt_generate_epsf(bool b)
+{
+ qt_gen_epsf = b;
+}
+
+static const char *const ps_header =
+"/BD{bind def}bind def/d2{dup dup}BD/ED{exch def}BD/D0{0 ED}BD/F{setfont}BD\n"
+"/RL{rlineto}BD/CM{currentmatrix}BD/SM{setmatrix}BD/TR{translate}BD/SD\n"
+"{setdash}BD/SC{aload pop setrgbcolor}BD/CR{currentfile read pop}BD/i{index}\n"
+"BD/scs{setcolorspace}BD/DB{dict dup begin}BD/DE{end def}BD/ie{ifelse}BD/gs\n"
+"{gsave}BD/gr{grestore}BD/w{setlinewidth}BD/d{setdash}BD/J{setlinecap}BD/j\n"
+"{setlinejoin}BD/scn{3 array astore/BCol exch def}BD/SCN{3 array astore/PCol\n"
+"exch def}BD/cm{6 array astore concat}BD/m{moveto}BD/l{lineto}BD/c{curveto}BD\n"
+"/h{closepath}BD/W{clip}BD/W*{eoclip}BD/n{newpath}BD/q{gsave 10 dict begin}BD\n"
+"/Q{end grestore}BD/re{4 2 roll m dup 0 exch RL exch 0 RL 0 exch neg RL h}BD\n"
+"/S{gs PCol SC stroke gr n}BD/BT{gsave 10 dict begin/_m matrix CM def BCol\n"
+"SC}BD/ET{end grestore}BD/Tf{/_fs ED findfont[_fs 0 0 _fs 0 0]makefont F}BD\n"
+"/Tm{6 array astore concat}BD/Td{translate}BD/Tj{0 0 m show}BD/BDC{pop pop}BD\n"
+"/EMC{}BD/BSt 0 def/WFi false def/BCol[1 1 1]def/PCol[0 0 0]def/BDArr[0.94\n"
+"0.88 0.63 0.50 0.37 0.12 0.06]def/level3{/languagelevel where{pop\n"
+"languagelevel 3 ge}{false}ie}BD/QCIgray D0/QCIcolor D0/QCIindex D0/QCI{\n"
+"/colorimage where{pop false 3 colorimage}{exec/QCIcolor ED/QCIgray QCIcolor\n"
+"length 3 idiv string def 0 1 QCIcolor length 3 idiv 1 sub{/QCIindex ED/_x\n"
+"QCIindex 3 mul def QCIgray QCIindex QCIcolor _x get 0.30 mul QCIcolor _x 1\n"
+"add get 0.59 mul QCIcolor _x 2 add get 0.11 mul add add cvi put}for QCIgray\n"
+"image}ie}BD/di{gs TR 1 i 1 eq{pop pop false 3 1 roll BCol SC imagemask}{dup\n"
+"false ne{level3}{false}ie{/_ma ED 8 eq{/_dc[0 1]def/DeviceGray}{/_dc[0 1 0 1\n"
+"0 1]def/DeviceRGB}ie scs/_im ED/_mt ED/_h ED/_w ED <</ImageType 3/DataDict\n"
+"<</ImageType 1/Width _w/Height _h/ImageMatrix _mt/DataSource _im\n"
+"/BitsPerComponent 8/Decode _dc >>/MaskDict <</ImageType 1/Width _w/Height _h\n"
+"/ImageMatrix _mt/DataSource _ma/BitsPerComponent 1/Decode[0 1]>>\n"
+"/InterleaveType 3 >> image}{pop 8 4 1 roll 8 eq{image}{QCI}ie}ie}ie gr}BD/BF\n"
+"{gs BSt 1 eq{BCol SC WFi{fill}{eofill}ie}if BSt 2 ge BSt 8 le and{BDArr BSt\n"
+"2 sub get/_sc ED BCol{1. exch sub _sc mul 1. exch sub}forall 3 array astore\n"
+"SC WFi{fill}{eofill}ie}if BSt 9 ge BSt 14 le and{WFi{W}{W*}ie pathbbox 3 i 3\n"
+"i TR 4 2 roll 3 2 roll exch sub/_h ED sub/_w ED BCol SC 0.3 w n BSt 9 eq BSt\n"
+"11 eq or{0 4 _h{dup 0 exch m _w exch l}for}if BSt 10 eq BSt 11 eq or{0 4 _w{\n"
+"dup 0 m _h l}for}if BSt 12 eq BSt 14 eq or{_w _h gt{0 6 _w _h add{dup 0 m _h\n"
+"sub _h l}for}{0 6 _w _h add{dup 0 exch m _w sub _w exch l}for}ie}if BSt 13\n"
+"eq BSt 14 eq or{_w _h gt{0 6 _w _h add{dup _h m _h sub 0 l}for}{0 6 _w _h\n"
+"add{dup _w exch m _w sub 0 exch l}for}ie}if stroke}if BSt 15 eq{}if BSt 24\n"
+"eq{}if gr}BD/f{/WFi true def BF n}BD/f*{/WFi false def BF n}BD/B{/WFi true\n"
+"def BF S n}BD/B*{/WFi false def BF S n}BD/QI{/C save def pageinit q n}BD/QP{\n"
+"Q C restore showpage}BD/SPD{/setpagedevice where{<< 3 1 roll >>\n"
+"setpagedevice}{pop pop}ie}BD/T1AddMapping{10 dict begin/glyphs ED/fnt ED\n"
+"/current fnt/NumGlyphs get def/CMap fnt/CMap get def 0 1 glyphs length 1 sub\n"
+"{glyphs exch get/gn ED current dup 256 mod/min ED 256 idiv/maj ED CMap dup\n"
+"maj get dup null eq{pop 256 array 0 1 255{1 i exch/.notdef put}for}if dup\n"
+"min gn put maj exch put/current current 1 add def}for fnt/CMap CMap put fnt\n"
+"/NumGlyphs current put end}def/T1AddGlyphs{10 dict begin/glyphs ED/fnt ED\n"
+"/current fnt/NumGlyphs get def/CMap fnt/CMap get def/CharStrings fnt\n"
+"/CharStrings get def 0 1 glyphs length 2 idiv 1 sub{2 mul dup glyphs exch\n"
+"get/gn ED 1 add glyphs exch get/cs ED current dup 256 mod/min ED 256 idiv\n"
+"/maj ED CMap dup maj get dup null eq{pop 256 array 0 1 255{1 i exch/.notdef\n"
+"put}for}if dup min gn put maj exch put CharStrings gn cs put/current current\n"
+"1 add def}for fnt/CharStrings CharStrings put fnt/CMap CMap put fnt\n"
+"/NumGlyphs current put end}def/StringAdd{1 i length 1 i length add string 3\n"
+"1 roll 2 i 0 3 i putinterval 2 i 2 i length 2 i putinterval pop pop}def\n"
+"/T1Setup{10 dict begin dup/FontName ED (-Base) StringAdd cvx cvn/Font ED\n"
+"/MaxPage Font/NumGlyphs get 1 sub 256 idiv def/FDepVector MaxPage 1 add\n"
+"array def/Encoding MaxPage 1 add array def 0 1 MaxPage{dup Encoding exch dup\n"
+"put dup/Page ED FontName (-) StringAdd exch 20 string cvs StringAdd cvn Font\n"
+"0 dict copy d2/CMap get Page get/Encoding exch put definefont FDepVector\n"
+"exch Page exch put}for FontName cvn <</FontType 0/FMapType 2/FontMatrix[1 0\n"
+"0 1 0 0]/Encoding Encoding/FDepVector FDepVector >> definefont pop end}def\n";
+
+
+
+// ------------------------------End of static data ----------------------------------
+
+// make sure DSC comments are not longer than 255 chars per line.
+static QByteArray wrapDSC(const QByteArray &str)
+{
+ QByteArray dsc = str.simplified();
+ const int wrapAt = 254;
+ QByteArray wrapped;
+ if (dsc.length() < wrapAt)
+ wrapped = dsc;
+ else {
+ wrapped = dsc.left(wrapAt);
+ QByteArray tmp = dsc.mid(wrapAt);
+ while (tmp.length() > wrapAt-3) {
+ wrapped += "\n%%+" + tmp.left(wrapAt-3);
+ tmp = tmp.mid(wrapAt-3);
+ }
+ wrapped += "\n%%+" + tmp;
+ }
+ return wrapped + "\n";
+}
+
+// ----------------------------- Internal class declarations -----------------------------
+
+QPSPrintEnginePrivate::QPSPrintEnginePrivate(QPrinter::PrinterMode m)
+ : QPdfBaseEnginePrivate(m),
+ printerState(QPrinter::Idle), hugeDocument(false), headerDone(false)
+{
+ useAlphaEngine = true;
+ postscript = true;
+
+ firstPage = true;
+
+#ifndef QT_NO_SETTINGS
+ QSettings settings(QSettings::UserScope, QLatin1String("Trolltech"));
+ settings.beginGroup(QLatin1String("Qt"));
+ embedFonts = settings.value(QLatin1String("embedFonts"), true).toBool();
+#else
+ embedFonts = true;
+#endif
+}
+
+QPSPrintEnginePrivate::~QPSPrintEnginePrivate()
+{
+}
+
+QT_BEGIN_INCLUDE_NAMESPACE
+#include <qdebug.h>
+QT_END_INCLUDE_NAMESPACE
+
+static void ps_r7(QPdf::ByteStream& stream, const char * s, int l)
+{
+ int i = 0;
+ uchar line[84];
+ int col = 0;
+
+ while(i < l) {
+ line[col++] = s[i++];
+ if (i < l - 1 && col >= 76) {
+ line[col++] = '\n';
+ line[col++] = '\0';
+ stream << (const char *)line;
+ col = 0;
+ }
+ }
+ if (col > 0) {
+ while((col&3) != 0)
+ line[col++] = '%'; // use a comment as padding
+ line[col++] = '\n';
+ line[col++] = '\0';
+ stream << (const char *)line;
+ }
+}
+
+static QByteArray runlengthEncode(const QByteArray &input)
+{
+ if (!input.length())
+ return input;
+
+ const char *data = input.constData();
+
+ QByteArray out;
+ int start = 0;
+ char last = *data;
+
+ enum State {
+ Undef,
+ Equal,
+ Diff
+ };
+ State state = Undef;
+
+ int i = 1;
+ int written = 0;
+ while (1) {
+ bool flush = (i == input.size());
+ if (!flush) {
+ switch(state) {
+ case Undef:
+ state = (last == data[i]) ? Equal : Diff;
+ break;
+ case Equal:
+ if (data[i] != last)
+ flush = true;
+ break;
+ case Diff:
+ if (data[i] == last) {
+ --i;
+ flush = true;
+ }
+ }
+ }
+ if (flush || i - start == 128) {
+ int size = i - start;
+ if (state == Equal) {
+ out.append((char)(uchar)(257-size));
+ out.append(last);
+ written += size;
+ } else {
+ out.append((char)(uchar)size-1);
+ while (start < i)
+ out.append(data[start++]);
+ written += size;
+ }
+ state = Undef;
+ start = i;
+ if (i == input.size())
+ break;
+ }
+ last = data[i];
+ ++i;
+ };
+ out.append((char)(uchar)128);
+ return out;
+}
+
+enum format {
+ Raw,
+ Runlength,
+ DCT
+};
+static const char *const filters[3] = {
+ " ",
+ "/RunLengthDecode filter ",
+ "/DCTDecode filter "
+};
+
+static QByteArray compressHelper(const QImage &image, bool gray, int *format)
+{
+ // we can't use premultiplied here
+ QByteArray pixelData;
+ int depth = image.depth();
+
+ Q_ASSERT(image.format() != QImage::Format_ARGB32_Premultiplied);
+
+ if (depth != 1 && !gray && QImageWriter::supportedImageFormats().contains("jpeg")) {
+ QBuffer buffer(&pixelData);
+ QImageWriter writer(&buffer, "jpeg");
+ writer.setQuality(94);
+ writer.write(image);
+ *format = DCT;
+ } else {
+ int width = image.width();
+ int height = image.height();
+ int size = width*height;
+
+ if (depth == 1)
+ size = (width+7)/8*height;
+ else if (!gray)
+ size = size*3;
+
+ pixelData.resize(size);
+ uchar *pixel = (uchar *)pixelData.data();
+ int i = 0;
+ if (depth == 1) {
+ QImage::Format format = image.format();
+ memset(pixel, 0xff, size);
+ for(int y=0; y < height; y++) {
+ const uchar * s = image.scanLine(y);
+ for(int x=0; x < width; x++) {
+ // need to copy bit for bit...
+ bool b = (format == QImage::Format_MonoLSB) ?
+ (*(s + (x >> 3)) >> (x & 7)) & 1 :
+ (*(s + (x >> 3)) << (x & 7)) & 0x80 ;
+ if (b)
+ pixel[i >> 3] ^= (0x80 >> (i & 7));
+ i++;
+ }
+ // we need to align to 8 bit here
+ i = (i+7) & 0xffffff8;
+ }
+ } else if (depth == 8) {
+ for(int y=0; y < height; y++) {
+ const uchar * s = image.scanLine(y);
+ for(int x=0; x < width; x++) {
+ QRgb rgb = image.color(s[x]);
+ if (gray) {
+ pixel[i] = (unsigned char) qGray(rgb);
+ i++;
+ } else {
+ pixel[i] = (unsigned char) qRed(rgb);
+ pixel[i+1] = (unsigned char) qGreen(rgb);
+ pixel[i+2] = (unsigned char) qBlue(rgb);
+ i += 3;
+ }
+ }
+ }
+ } else {
+ for(int y=0; y < height; y++) {
+ QRgb * s = (QRgb*)(image.scanLine(y));
+ for(int x=0; x < width; x++) {
+ QRgb rgb = (*s++);
+ if (gray) {
+ pixel[i] = (unsigned char) qGray(rgb);
+ i++;
+ } else {
+ pixel[i] = (unsigned char) qRed(rgb);
+ pixel[i+1] = (unsigned char) qGreen(rgb);
+ pixel[i+2] = (unsigned char) qBlue(rgb);
+ i += 3;
+ }
+ }
+ }
+ }
+ *format = Raw;
+ if (depth == 1) {
+ pixelData = runlengthEncode(pixelData);
+ *format = Runlength;
+ }
+ }
+ QByteArray outarr = QPdf::ascii85Encode(pixelData);
+ return outarr;
+}
+
+void QPSPrintEnginePrivate::drawImageHelper(qreal x, qreal y, qreal w, qreal h, const QImage &img,
+ const QImage &mask, bool gray, qreal scaleX, qreal scaleY)
+{
+ Q_UNUSED(h);
+ Q_UNUSED(w);
+ int width = img.width();
+ int height = img.height();
+
+ QByteArray out;
+ int size = 0;
+ const char *bits;
+
+ if (!mask.isNull()) {
+ int format;
+ out = compressHelper(mask, true, &format);
+ size = (width+7)/8*height;
+ *currentPage << "/mask currentfile/ASCII85Decode filter"
+ << filters[format]
+ << size << " string readstring\n";
+ ps_r7(*currentPage, out, out.size());
+ *currentPage << " pop def\n";
+ }
+ if (img.depth() == 1) {
+ size = (width+7)/8*height;
+ bits = "1 ";
+ } else if (gray) {
+ size = width*height;
+ bits = "8 ";
+ } else {
+ size = width*height*3;
+ bits = "24 ";
+ }
+
+ int format;
+ out = compressHelper(img, gray, &format);
+ *currentPage << "/sl currentfile/ASCII85Decode filter"
+ << filters[format]
+ << size << " string readstring\n";
+ ps_r7(*currentPage, out, out.size());
+ *currentPage << " pop def\n";
+ *currentPage << width << ' ' << height << "[" << scaleX << " 0 0 " << scaleY << " 0 0]sl "
+ << bits << (!mask.isNull() ? "mask " : "false ")
+ << x << ' ' << y << " di\n";
+}
+
+
+void QPSPrintEnginePrivate::drawImage(qreal x, qreal y, qreal w, qreal h,
+ const QImage &image, const QImage &msk)
+{
+ if (!w || !h || image.isNull()) return;
+
+ QImage img(image);
+ QImage mask(msk);
+
+ if (image.format() == QImage::Format_ARGB32_Premultiplied)
+ img = image.convertToFormat(QImage::Format_ARGB32);
+
+ if (!msk.isNull() && msk.format() == QImage::Format_ARGB32_Premultiplied)
+ mask = msk.convertToFormat(QImage::Format_ARGB32);
+
+ int width = img.width();
+ int height = img.height();
+ qreal scaleX = width/w;
+ qreal scaleY = height/h;
+
+ bool gray = (colorMode == QPrinter::GrayScale) || img.allGray();
+ int splitSize = 21830 * (gray ? 3 : 1);
+ if (width * height > splitSize) { // 65535/3, tolerance for broken printers
+ int images, subheight;
+ images = (width * height + splitSize - 1) / splitSize;
+ subheight = (height + images-1) / images;
+ while (subheight * width > splitSize) {
+ images++;
+ subheight = (height + images-1) / images;
+ }
+ int suby = 0;
+ const QImage constImg(img);
+ const QImage constMask(mask);
+ while(suby < height) {
+ qreal subImageHeight = qMin(subheight, height-suby);
+ const QImage subImage(constImg.scanLine(suby), width, subImageHeight,
+ constImg.bytesPerLine(), constImg.format());
+ const QImage subMask = mask.isNull() ? mask : QImage(constMask.scanLine(suby), width, subImageHeight,
+ constMask.bytesPerLine(), constMask.format());
+ drawImageHelper(x, y + suby/scaleY, w, subImageHeight/scaleY,
+ subImage, subMask, gray, scaleX, scaleY);
+ suby += subheight;
+ }
+ } else {
+ drawImageHelper(x, y, width, height, img, mask, gray, scaleX, scaleY);
+ }
+}
+
+void QPSPrintEnginePrivate::emitHeader(bool finished)
+{
+ QPSPrintEngine *q = static_cast<QPSPrintEngine *>(q_ptr);
+ QPrinter *printer = static_cast<QPrinter*>(pdev);
+
+ if (creator.isEmpty())
+ creator = QLatin1String("Qt " QT_VERSION_STR);
+
+ QByteArray header;
+ QPdf::ByteStream s(&header);
+ s << "%!PS-Adobe-1.0";
+
+ qreal scale = 72. / ((qreal) q->metric(QPaintDevice::PdmDpiY));
+ QRect pageRect = this->pageRect();
+ QRect paperRect = this->paperRect();
+ int mtop = pageRect.top() - paperRect.top();
+ int mleft = pageRect.left() - paperRect.left();
+ int mbottom = paperRect.bottom() - pageRect.bottom();
+ int mright = paperRect.right() - pageRect.right();
+ int width = pageRect.width();
+ int height = pageRect.height();
+ if (finished && pageCount == 1 && copies == 1 &&
+ ((fullPage && qt_gen_epsf) || (outputFileName.endsWith(QLatin1String(".eps"))))
+ ) {
+ if (!boundingBox.isValid())
+ boundingBox.setRect(0, 0, width, height);
+ if (orientation == QPrinter::Landscape) {
+ if (!fullPage)
+ boundingBox.translate(-mleft, -mtop);
+ s << " EPSF-3.0\n%%BoundingBox: "
+ << (int)(printer->height() - boundingBox.bottom())*scale // llx
+ << (int)(printer->width() - boundingBox.right())*scale - 1 // lly
+ << (int)(printer->height() - boundingBox.top())*scale + 1 // urx
+ << (int)(printer->width() - boundingBox.left())*scale; // ury
+ } else {
+ if (!fullPage)
+ boundingBox.translate(mleft, -mtop);
+ s << " EPSF-3.0\n%%BoundingBox: "
+ << (int)(boundingBox.left())*scale
+ << (int)(printer->height() - boundingBox.bottom())*scale - 1
+ << (int)(boundingBox.right())*scale + 1
+ << (int)(printer->height() - boundingBox.top())*scale;
+ }
+ } else {
+ int w = width + (fullPage ? 0 : mleft + mright);
+ int h = height + (fullPage ? 0 : mtop + mbottom);
+ w = (int)(w*scale);
+ h = (int)(h*scale);
+ // set a bounding box according to the DSC
+ if (orientation == QPrinter::Landscape)
+ s << "\n%%BoundingBox: 0 0 " << h << w;
+ else
+ s << "\n%%BoundingBox: 0 0 " << w << h;
+ }
+ s << "\n" << wrapDSC("%%Creator: " + creator.toUtf8());
+ if (!title.isEmpty())
+ s << wrapDSC("%%Title: " + title.toUtf8());
+#ifndef QT_NO_DATESTRING
+ s << "%%CreationDate: " << QDateTime::currentDateTime().toString().toUtf8();
+#endif
+ s << "\n%%Orientation: ";
+ if (orientation == QPrinter::Landscape)
+ s << "Landscape";
+ else
+ s << "Portrait";
+
+ s << "\n%%Pages: (atend)"
+ "\n%%DocumentFonts: (atend)"
+ "\n%%EndComments\n"
+
+ "%%BeginProlog\n"
+ "% Prolog copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies).\n"
+ "% You may copy this prolog in any way that is directly related to this document.\n"
+ "% For other use of this prolog, see your licensing agreement for Qt.\n"
+ << ps_header << "\n";
+
+
+ s << "/pageinit {\n";
+ if (!fullPage) {
+ if (orientation == QPrinter::Portrait)
+ s << mleft*scale << mbottom*scale << "translate\n";
+ else
+ s << mtop*scale << mleft*scale << "translate\n";
+ }
+ if (orientation == QPrinter::Portrait) {
+ s << "% " << printer->widthMM() << "*" << printer->heightMM()
+ << "mm (portrait)\n0 " << height*scale
+ << "translate " << scale << "-" << scale << "scale } def\n";
+ } else {
+ s << "% " << printer->heightMM() << "*" << printer->widthMM()
+ << " mm (landscape)\n 90 rotate " << scale << "-" << scale << "scale } def\n";
+ }
+ s << "%%EndProlog\n";
+
+ outDevice->write(header);
+ headerDone = true;
+}
+
+
+void QPSPrintEnginePrivate::emitPages()
+{
+ if (!hugeDocument) {
+ for (QHash<QFontEngine::FaceId, QFontSubset *>::const_iterator it = fonts.constBegin();
+ it != fonts.constEnd(); ++it)
+ outDevice->write((*it)->toType1());
+ }
+
+ QIODevice *content = buffer.stream();
+ // Write the page contents in chunks.
+ while (!content->atEnd()) {
+ QByteArray buf = content->read(currentPage->chunkSize());
+ if (!buf.isEmpty())
+ outDevice->write(buf);
+ }
+ content = currentPage->stream();
+ // Write the page contents in chunks.
+ while (!content->atEnd()) {
+ QByteArray buf = content->read(currentPage->chunkSize());
+ if (!buf.isEmpty())
+ outDevice->write(buf);
+ }
+ outDevice->write(trailer);
+
+ buffer.clear();
+ currentPage->clear();
+ trailer = QByteArray();
+ hugeDocument = true;
+}
+
+
+#ifdef Q_WS_QWS
+static const int max_in_memory_size = 2000000;
+#else
+static const int max_in_memory_size = 32000000;
+#endif
+
+void QPSPrintEnginePrivate::flushPage(bool last)
+{
+ if (!last && currentPage->stream()->size() == 0)
+ return;
+
+ QPdf::ByteStream e(&trailer);
+ buffer << "%%Page: "
+ << pageCount << pageCount << "\n"
+ << "%%BeginPageSetup\n"
+ << "QI\n";
+ if (hugeDocument) {
+ for (QHash<QFontEngine::FaceId, QFontSubset *>::const_iterator it = fonts.constBegin();
+ it != fonts.constEnd(); ++it) {
+ if (currentPage->fonts.contains((*it)->object_id)) {
+ if ((*it)->downloaded_glyphs == 0) {
+ buffer << (*it)->toType1();
+ (*it)->downloaded_glyphs = 0;
+ } else {
+ buffer << (*it)->type1AddedGlyphs();
+ }
+ }
+ }
+ }
+ for (int i = 0; i < currentPage->fonts.size(); ++i)
+ buffer << "(F" << QByteArray::number(currentPage->fonts.at(i)) << ") T1Setup\n";
+
+ buffer << "%%EndPageSetup\nq\n";
+ e << "\nQ QP\n";
+ if (last || hugeDocument
+ || buffer.stream()->size() + currentPage->stream()->size() > max_in_memory_size) {
+// qDebug("emiting header at page %d", pageCount);
+ if (!headerDone)
+ emitHeader(last);
+ emitPages();
+ } else {
+ buffer << *currentPage << e;
+ currentPage->clear();
+ trailer.clear();
+ }
+ pageCount++;
+}
+
+// ================ PSPrinter class ========================
+
+QPSPrintEngine::QPSPrintEngine(QPrinter::PrinterMode m)
+ : QPdfBaseEngine(*(new QPSPrintEnginePrivate(m)),
+ PrimitiveTransform
+ | PatternTransform
+ | PixmapTransform
+ | PainterPaths
+ | PatternBrush
+ )
+{
+}
+
+static void ignoreSigPipe(bool b)
+{
+#ifndef QT_NO_LPR
+ static struct sigaction *users_sigpipe_handler = 0;
+ static int lockCount = 0;
+
+#ifndef QT_NO_THREAD
+ QMutexLocker locker(QMutexPool::globalInstanceGet(&users_sigpipe_handler));
+#endif
+
+ if (b) {
+ if (lockCount++ > 0)
+ return;
+
+ if (users_sigpipe_handler != 0)
+ return; // already ignoring sigpipe
+
+ users_sigpipe_handler = new struct sigaction;
+ struct sigaction tmp_sigpipe_handler;
+ tmp_sigpipe_handler.sa_handler = SIG_IGN;
+ sigemptyset(&tmp_sigpipe_handler.sa_mask);
+ tmp_sigpipe_handler.sa_flags = 0;
+
+ if (sigaction(SIGPIPE, &tmp_sigpipe_handler, users_sigpipe_handler) == -1) {
+ delete users_sigpipe_handler;
+ users_sigpipe_handler = 0;
+ }
+ }
+ else {
+ if (--lockCount > 0)
+ return;
+
+ if (users_sigpipe_handler == 0)
+ return; // not ignoring sigpipe
+
+ if (sigaction(SIGPIPE, users_sigpipe_handler, 0) == -1)
+ qWarning("QPSPrintEngine: Could not restore SIGPIPE handler");
+
+ delete users_sigpipe_handler;
+ users_sigpipe_handler = 0;
+ }
+#else
+ Q_UNUSED(b);
+#endif
+}
+QPSPrintEngine::~QPSPrintEngine()
+{
+ Q_D(QPSPrintEngine);
+ if (d->fd >= 0)
+#if defined(Q_OS_WIN) && defined(_MSC_VER) && _MSC_VER >= 1400
+ ::_close(d->fd);
+#else
+ ::close(d->fd);
+#endif
+}
+
+bool QPSPrintEngine::begin(QPaintDevice *pdev)
+{
+ Q_D(QPSPrintEngine);
+
+ if (d->fd >= 0)
+ return true;
+
+ if (d->useAlphaEngine) {
+ QAlphaPaintEngine::begin(pdev);
+ if (!continueCall())
+ return true;
+ }
+
+ if(!QPdfBaseEngine::begin(pdev)) {
+ d->printerState = QPrinter::Error;
+ return false;
+ }
+
+ d->pageCount = 1; // initialize state
+
+ d->pen = QPen(Qt::black);
+ d->brush = Qt::NoBrush;
+ d->hasPen = true;
+ d->hasBrush = false;
+ d->clipEnabled = false;
+ d->allClipped = false;
+ d->boundingBox = QRect();
+ d->fontsUsed = "";
+ d->hugeDocument = false;
+
+ setActive(true);
+ d->printerState = QPrinter::Active;
+
+ newPage();
+
+ return true;
+}
+
+bool QPSPrintEngine::end()
+{
+ Q_D(QPSPrintEngine);
+
+ if (d->useAlphaEngine) {
+ QAlphaPaintEngine::end();
+ if (!continueCall())
+ return true;
+ }
+
+ // we're writing to lp/lpr through a pipe, we don't want to crash with SIGPIPE
+ // if lp/lpr dies
+ ignoreSigPipe(true);
+ d->flushPage(true);
+ QByteArray trailer;
+ QPdf::ByteStream s(&trailer);
+ s << "%%Trailer\n";
+ s << "%%Pages: " << d->pageCount - 1 << "\n" <<
+ wrapDSC("%%DocumentFonts: " + d->fontsUsed);
+ s << "%%EOF\n";
+ d->outDevice->write(trailer);
+
+ QPdfBaseEngine::end();
+ ignoreSigPipe(false);
+
+ d->firstPage = true;
+ d->headerDone = false;
+
+ setActive(false);
+ d->printerState = QPrinter::Idle;
+ d->pdev = 0;
+
+ return true;
+}
+
+void QPSPrintEngine::setBrush()
+{
+ Q_D(QPSPrintEngine);
+#if 0
+ bool specifyColor;
+ int gStateObject = 0;
+ int patternObject = d->addBrushPattern(brush, d->stroker.matrix, brushOrigin, &specifyColor, &gStateObject);
+
+ *d->currentPage << (patternObject ? "/PCSp cs " : "/CSp cs ");
+ if (specifyColor) {
+ QColor rgba = brush.color();
+ *d->currentPage << rgba.redF()
+ << rgba.greenF()
+ << rgba.blueF();
+ }
+ if (patternObject)
+ *d->currentPage << "/Pat" << patternObject;
+ *d->currentPage << "scn\n";
+#endif
+ QColor rgba = d->brush.color();
+ if (d->colorMode == QPrinter::GrayScale) {
+ qreal gray = qGray(rgba.rgba())/255.;
+ *d->currentPage << gray << gray << gray;
+ } else {
+ *d->currentPage << rgba.redF()
+ << rgba.greenF()
+ << rgba.blueF();
+ }
+ *d->currentPage << "scn\n"
+ << "/BSt " << d->brush.style() << "def\n";
+}
+
+void QPSPrintEngine::drawImageInternal(const QRectF &r, QImage image, bool bitmap)
+{
+ Q_D(QPSPrintEngine);
+ if (d->clipEnabled && d->allClipped)
+ return;
+ if (bitmap && image.depth() != 1)
+ bitmap = false;
+ QImage mask;
+ // the below is not necessary since it's handled by the alpha
+ // engine
+ if (!d->useAlphaEngine && !bitmap) {
+ if (image.format() == QImage::Format_Mono || image.format() == QImage::Format_MonoLSB)
+ image = image.convertToFormat(QImage::Format_Indexed8);
+ if (image.hasAlphaChannel()) {
+ // get better alpha dithering
+ int xscale = image.width();
+ xscale *= xscale <= 800 ? 4 : (xscale <= 1600 ? 2 : 1);
+ int yscale = image.height();
+ yscale *= yscale <= 800 ? 4 : (yscale <= 1600 ? 2 : 1);
+ image = image.scaled(xscale, yscale);
+ mask = image.createAlphaMask(Qt::OrderedAlphaDither);
+ }
+ }
+ *d->currentPage << "q\n";
+ if(!d->simplePen)
+ *d->currentPage << QPdf::generateMatrix(d->stroker.matrix);
+ QBrush b = d->brush;
+ if (image.depth() == 1) {
+ // set current pen as brush
+ d->brush = d->pen.brush();
+ setBrush();
+ }
+ d->drawImage(r.x(), r.y(), r.width(), r.height(), image, mask);
+ *d->currentPage << "Q\n";
+ d->brush = b;
+}
+
+
+void QPSPrintEngine::drawImage(const QRectF &r, const QImage &img, const QRectF &sr,
+ Qt::ImageConversionFlags)
+{
+ Q_D(QPSPrintEngine);
+
+ if (d->useAlphaEngine) {
+ QAlphaPaintEngine::drawImage(r, img, sr);
+ if (!continueCall())
+ return;
+ }
+ QImage image = img.copy(sr.toRect());
+ drawImageInternal(r, image, false);
+}
+
+void QPSPrintEngine::drawPixmap(const QRectF &r, const QPixmap &pm, const QRectF &sr)
+{
+ Q_D(QPSPrintEngine);
+
+ if (d->useAlphaEngine) {
+ QAlphaPaintEngine::drawPixmap(r, pm, sr);
+ if (!continueCall())
+ return;
+ }
+
+ QImage img = pm.copy(sr.toRect()).toImage();
+ drawImageInternal(r, img, true);
+}
+
+void QPSPrintEngine::drawTiledPixmap(const QRectF &r, const QPixmap &pixmap, const QPointF &p)
+{
+ Q_D(QPSPrintEngine);
+
+ if (d->useAlphaEngine) {
+ QAlphaPaintEngine::drawTiledPixmap(r, pixmap, p);
+ if (!continueCall())
+ return;
+ }
+
+ if (d->clipEnabled && d->allClipped)
+ return;
+ // ### Optimise implementation!
+ qreal yPos = r.y();
+ qreal yOff = p.y();
+ while(yPos < r.y() + r.height()) {
+ qreal drawH = pixmap.height() - yOff; // Cropping first row
+ if (yPos + drawH > r.y() + r.height()) // Cropping last row
+ drawH = r.y() + r.height() - yPos;
+ qreal xPos = r.x();
+ qreal xOff = p.x();
+ while(xPos < r.x() + r.width()) {
+ qreal drawW = pixmap.width() - xOff; // Cropping first column
+ if (xPos + drawW > r.x() + r.width()) // Cropping last column
+ drawW = r.x() + r.width() - xPos;
+ // ########
+ painter()->drawPixmap(QPointF(xPos, yPos).toPoint(), pixmap,
+ QRectF(xOff, yOff, drawW, drawH).toRect());
+ xPos += drawW;
+ xOff = 0;
+ }
+ yPos += drawH;
+ yOff = 0;
+ }
+
+}
+
+bool QPSPrintEngine::newPage()
+{
+ Q_D(QPSPrintEngine);
+
+ if (!d->firstPage && d->useAlphaEngine)
+ flushAndInit();
+
+ // we're writing to lp/lpr through a pipe, we don't want to crash with SIGPIPE
+ // if lp/lpr dies
+ ignoreSigPipe(true);
+ if (!d->firstPage)
+ d->flushPage();
+ d->firstPage = false;
+ ignoreSigPipe(false);
+
+ delete d->currentPage;
+ d->currentPage = new QPdfPage;
+ d->stroker.stream = d->currentPage;
+
+ return QPdfBaseEngine::newPage();
+}
+
+bool QPSPrintEngine::abort()
+{
+ // ### abort!?!
+ return false;
+}
+
+QPrinter::PrinterState QPSPrintEngine::printerState() const
+{
+ Q_D(const QPSPrintEngine);
+ return d->printerState;
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_PRINTER
diff --git a/src/gui/painting/qprintengine_ps_p.h b/src/gui/painting/qprintengine_ps_p.h
new file mode 100644
index 0000000000..148c86f9e5
--- /dev/null
+++ b/src/gui/painting/qprintengine_ps_p.h
@@ -0,0 +1,137 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QPRINTENGINE_PS_P_H
+#define QPRINTENGINE_PS_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of qpsprinter.cpp and qprinter_x11.cpp.
+// This header file may change from version to version without notice,
+// or even be removed.
+//
+// We mean it.
+//
+
+#ifndef QT_NO_PRINTER
+
+#include "private/qpdf_p.h"
+#include "qplatformdefs.h"
+#include "QtCore/qlibrary.h"
+#include "QtCore/qstringlist.h"
+#include "QtCore/qhash.h"
+#include "QtCore/qabstractitemmodel.h"
+
+QT_BEGIN_NAMESPACE
+
+class QPrinter;
+class QPSPrintEnginePrivate;
+
+class QPSPrintEngine : public QPdfBaseEngine
+{
+ Q_DECLARE_PRIVATE(QPSPrintEngine)
+public:
+ // QPrinter uses these
+ explicit QPSPrintEngine(QPrinter::PrinterMode m);
+ ~QPSPrintEngine();
+
+
+ virtual bool begin(QPaintDevice *pdev);
+ virtual bool end();
+
+ void setBrush();
+
+ virtual void drawImage(const QRectF &r, const QImage &img, const QRectF &sr, Qt::ImageConversionFlags);
+ virtual void drawPixmap(const QRectF &r, const QPixmap &pm, const QRectF &sr);
+ virtual void drawTiledPixmap(const QRectF &r, const QPixmap &pixmap, const QPointF &s);
+
+ virtual void drawImageInternal(const QRectF &r, QImage img, bool bitmap);
+
+ virtual QPaintEngine::Type type() const { return QPaintEngine::PostScript; }
+
+ virtual bool newPage();
+ virtual bool abort();
+
+ virtual QPrinter::PrinterState printerState() const;
+
+ virtual Qt::HANDLE handle() const { return 0; };
+
+private:
+ Q_DISABLE_COPY(QPSPrintEngine)
+};
+
+class QPSPrintEnginePrivate : public QPdfBaseEnginePrivate {
+public:
+ QPSPrintEnginePrivate(QPrinter::PrinterMode m);
+ ~QPSPrintEnginePrivate();
+
+ void emitHeader(bool finished);
+ void emitPages();
+ void drawImage(qreal x, qreal y, qreal w, qreal h, const QImage &img, const QImage &mask);
+ void flushPage(bool last = false);
+ void drawImageHelper(qreal x, qreal y, qreal w, qreal h, const QImage &img, const QImage &mask,
+ bool gray, qreal scaleX, qreal scaleY);
+
+ int pageCount;
+ bool epsf;
+ QByteArray fontsUsed;
+
+ // stores the descriptions of the n first pages.
+ QPdf::ByteStream buffer;
+ QByteArray trailer;
+
+ bool firstPage;
+
+ QRect boundingBox;
+
+ QPrinter::PrinterState printerState;
+ bool hugeDocument;
+ bool headerDone;
+};
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_PRINTER
+
+#endif // QPRINTENGINE_PS_P_H
diff --git a/src/gui/painting/qprintengine_qws.cpp b/src/gui/painting/qprintengine_qws.cpp
new file mode 100644
index 0000000000..1a8ffbc976
--- /dev/null
+++ b/src/gui/painting/qprintengine_qws.cpp
@@ -0,0 +1,881 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <private/qprintengine_qws_p.h>
+
+#ifndef QT_NO_PRINTER
+
+#include <private/qpaintengine_raster_p.h>
+#include <qimage.h>
+#include <qfile.h>
+#include <qdebug.h>
+#include <QCopChannel>
+
+QT_BEGIN_NAMESPACE
+
+#define MM(n) int((n * 720 + 127) / 254)
+#define IN(n) int(n * 72)
+
+extern QSizeF qt_paperSizeToQSizeF(QPrinter::PaperSize size);
+
+QtopiaPrintEngine::QtopiaPrintEngine(QPrinter::PrinterMode mode)
+ : QPaintEngine(*(new QtopiaPrintEnginePrivate( mode )))
+{
+ d_func()->initialize();
+}
+
+bool QtopiaPrintEngine::begin(QPaintDevice *)
+{
+ Q_D(QtopiaPrintEngine);
+ Q_ASSERT_X(d->printerState == QPrinter::Idle, "QtopiaPrintEngine", "printer already active");
+
+ // Create a new off-screen monochrome image to handle the drawing process.
+ QSize size = paperRect().size();
+ if ( d->pageImage )
+ delete d->pageImage;
+ d->pageImage = new QImage( size, QImage::Format_RGB32 );
+ if ( !(d->pageImage) )
+ return false;
+
+ // Recreate the paint engine on the new image.
+ delete d->_paintEngine;
+ d->_paintEngine = 0;
+ d->paintEngine()->state = state;
+
+ // Begin the paint process on the image.
+ if (!d->paintEngine()->begin(d->pageImage))
+ return false;
+
+ // Clear the first page to all-white.
+ clearPage();
+
+ // Clear the print buffer and output the image header.
+ d->buffer.clear();
+ d->writeG3FaxHeader();
+
+ // The print engine is currently active.
+ d->printerState = QPrinter::Active;
+ return true;
+}
+
+bool QtopiaPrintEngine::end()
+{
+ Q_D(QtopiaPrintEngine);
+
+ d->paintEngine()->end();
+
+ // Flush the last page.
+ flushPage();
+
+ // Output the fax data to a file (TODO: send to the print queuing daemon).
+ QString filename;
+ if ( !d->outputFileName.isEmpty() )
+ filename = QString::fromLocal8Bit(qgetenv("HOME").constData()) + QLatin1String("/Documents/") + d->outputFileName;
+ else
+ filename = QString::fromLocal8Bit(qgetenv("HOME").constData()) + QLatin1String("/tmp/qwsfax.tiff");
+
+ setProperty(QPrintEngine::PPK_OutputFileName, filename);
+ QFile file( filename );
+ if ( !file.open( QIODevice::WriteOnly | QIODevice::Truncate ) ) {
+ qDebug( "Failed to open %s for printer output",
+ filename.toLatin1().constData() );
+ } else {
+ file.write( d->buffer.data() );
+ file.close();
+ }
+
+ // Free up the memory for the image buffer.
+ d->buffer.clear();
+
+ // Finalize the print job.
+ d->printerState = QPrinter::Idle;
+
+ // call qcop service
+ QMap<QString, QVariant> map;
+ for ( int x = 0; x <= QPrintEngine::PPK_Duplex; x++ )
+ map.insert( QString::number(x), property((QPrintEngine::PrintEnginePropertyKey)(x)));
+ QVariant variant(map);
+
+ QByteArray data;
+ QDataStream out(&data, QIODevice::WriteOnly);
+ out << variant;
+ QCopChannel::send(QLatin1String("QPE/Service/Print"), QLatin1String("print(QVariant)"), data);
+
+ return true;
+}
+
+QPaintEngine *QtopiaPrintEngine::paintEngine() const
+{
+ return const_cast<QtopiaPrintEnginePrivate *>(d_func())->paintEngine();
+}
+
+void QtopiaPrintEngine::drawPixmap(const QRectF &r, const QPixmap &pm, const QRectF &sr)
+{
+ Q_D(QtopiaPrintEngine);
+ Q_ASSERT(d->printerState == QPrinter::Active);
+ d->paintEngine()->drawPixmap(r, pm, sr);
+}
+
+void QtopiaPrintEngine::drawTextItem(const QPointF &p, const QTextItem &ti)
+{
+ Q_D(QtopiaPrintEngine);
+ Q_ASSERT(d->printerState == QPrinter::Active);
+ d->paintEngine()->drawTextItem(p, ti);
+}
+
+void QtopiaPrintEngine::updateState(const QPaintEngineState &state)
+{
+ Q_D(QtopiaPrintEngine);
+ d->paintEngine()->updateState(state);
+}
+
+QRect QtopiaPrintEngine::paperRect() const
+{
+ QSizeF s = qt_paperSizeToQSizeF(d_func()->paperSize);
+ s.rwidth() = MM(s.width());
+ s.rheight() = MM(s.height());
+ int w = qRound(s.width()*d_func()->resolution/72.);
+ int h = qRound(s.height()*d_func()->resolution/72.);
+ if (d_func()->orientation == QPrinter::Portrait)
+ return QRect(0, 0, w, h);
+ else
+ return QRect(0, 0, h, w);
+}
+
+QRect QtopiaPrintEngine::pageRect() const
+{
+ QRect r = paperRect();
+ if (d_func()->fullPage)
+ return r;
+ // would be nice to get better margins than this.
+ return QRect(d_func()->resolution/3, d_func()->resolution/3, r.width()-2*d_func()->resolution/3, r.height()-2*d_func()->resolution/3);
+}
+
+bool QtopiaPrintEngine::newPage()
+{
+ flushPage();
+ clearPage();
+ ++(d_func()->pageNumber);
+ return true;
+}
+
+bool QtopiaPrintEngine::abort()
+{
+ return false;
+}
+
+QPrinter::PrinterState QtopiaPrintEngine::printerState() const
+{
+ return d_func()->printerState;
+}
+
+int QtopiaPrintEngine::metric(QPaintDevice::PaintDeviceMetric metricType) const
+{
+ int val;
+ QRect r = d_func()->fullPage ? paperRect() : pageRect();
+ switch (metricType) {
+ case QPaintDevice::PdmWidth:
+ val = r.width();
+ break;
+ case QPaintDevice::PdmHeight:
+ val = r.height();
+ break;
+ case QPaintDevice::PdmDpiX:
+ val = d_func()->resolution;
+ break;
+ case QPaintDevice::PdmDpiY:
+ val = d_func()->resolution;
+ break;
+ case QPaintDevice::PdmPhysicalDpiX:
+ case QPaintDevice::PdmPhysicalDpiY:
+ val = QT_QWS_PRINTER_DEFAULT_DPI;
+ break;
+ case QPaintDevice::PdmWidthMM:
+ val = qRound(r.width()*25.4/d_func()->resolution);
+ break;
+ case QPaintDevice::PdmHeightMM:
+ val = qRound(r.height()*25.4/d_func()->resolution);
+ break;
+ case QPaintDevice::PdmNumColors:
+ val = 2;
+ break;
+ case QPaintDevice::PdmDepth:
+ val = 1;
+ break;
+ default:
+ qWarning("QtopiaPrintEngine::metric: Invalid metric command");
+ return 0;
+ }
+ return val;
+}
+
+QVariant QtopiaPrintEngine::property(PrintEnginePropertyKey key) const
+{
+ Q_D(const QtopiaPrintEngine);
+ QVariant ret;
+
+ switch (key) {
+ case PPK_CollateCopies:
+ ret = d->collateCopies;
+ break;
+ case PPK_ColorMode:
+ ret = d->colorMode;
+ break;
+ case PPK_Creator:
+ ret = d->creator;
+ break;
+ case PPK_DocumentName:
+ ret = d->docName;
+ break;
+ case PPK_FullPage:
+ ret = d->fullPage;
+ break;
+ case PPK_NumberOfCopies:
+ ret = d->numCopies;
+ break;
+ case PPK_Orientation:
+ ret = d->orientation;
+ break;
+ case PPK_OutputFileName:
+ ret = d->outputFileName;
+ break;
+ case PPK_PageOrder:
+ ret = d->pageOrder;
+ break;
+ case PPK_PageRect:
+ ret = pageRect();
+ break;
+ case PPK_PaperSize:
+ ret = d->paperSize;
+ break;
+ case PPK_PaperRect:
+ ret = paperRect();
+ break;
+ case PPK_PaperSource:
+ ret = d->paperSource;
+ break;
+ case PPK_PrinterName:
+ ret = d->printerName;
+ break;
+ case PPK_PrinterProgram:
+ ret = d->printProgram;
+ break;
+ case PPK_Resolution:
+ ret = d->resolution;
+ break;
+ case PPK_SupportedResolutions:
+ ret = QList<QVariant>() << QT_QWS_PRINTER_DEFAULT_DPI;
+ break;
+ default:
+ break;
+ }
+ return ret;
+}
+
+void QtopiaPrintEngine::setProperty(PrintEnginePropertyKey key, const QVariant &value)
+{
+ Q_D(QtopiaPrintEngine);
+ switch (key) {
+ case PPK_CollateCopies:
+ d->collateCopies = value.toBool();
+ break;
+ case PPK_ColorMode:
+ d->colorMode = QPrinter::ColorMode(value.toInt());
+ break;
+ case PPK_Creator:
+ d->creator = value.toString();
+ break;
+ case PPK_DocumentName:
+ d->docName = value.toString();
+ break;
+ case PPK_FullPage:
+ d->fullPage = value.toBool();
+ break;
+ case PPK_NumberOfCopies:
+ d->numCopies = value.toInt();
+ break;
+ case PPK_Orientation:
+ d->orientation = QPrinter::Orientation(value.toInt());
+ break;
+ case PPK_OutputFileName:
+ d->outputFileName = value.toString();
+ break;
+ case PPK_PageOrder:
+ d->pageOrder = QPrinter::PageOrder(value.toInt());
+ break;
+ case PPK_PaperSize:
+ d->paperSize = QPrinter::PaperSize(value.toInt());
+ break;
+ case PPK_PaperSource:
+ d->paperSource = QPrinter::PaperSource(value.toInt());
+ case PPK_PrinterName:
+ d->printerName = value.toString();
+ break;
+ case PPK_PrinterProgram:
+ d->printProgram = value.toString();
+ break;
+ case PPK_Resolution:
+ d->resolution = value.toInt();
+ break;
+ default:
+ break;
+ }
+}
+
+void QtopiaPrintEngine::clearPage()
+{
+ d_func()->pageImage->fill(QColor(255, 255, 255).rgb());
+}
+
+void QtopiaPrintEngine::flushPage()
+{
+ d_func()->writeG3FaxPage();
+}
+
+QtopiaPrintEnginePrivate::~QtopiaPrintEnginePrivate()
+{
+ if ( pageImage )
+ delete pageImage;
+}
+
+void QtopiaPrintEnginePrivate::initialize()
+{
+ _paintEngine = 0;
+}
+
+QPaintEngine *QtopiaPrintEnginePrivate::paintEngine()
+{
+ if (!_paintEngine)
+ _paintEngine = new QRasterPaintEngine(pageImage);
+ return _paintEngine;
+}
+
+void QtopiaPrintEnginePrivate::writeG3FaxHeader()
+{
+ // Write the TIFF file magic number (little-endian TIFF).
+ buffer.append( (char)'I' );
+ buffer.append( (char)'I' );
+ buffer.append( (char)42 );
+ buffer.append( (char)0 );
+
+ // Leave a place-holder for the IFD offset of the first page.
+ ifdPatch = buffer.size();
+ buffer.append( (int)0 );
+}
+
+// Tag values, from RFC 2301.
+#define TIFF_IFD_NEW_SUB_FILE_TYPE 254
+#define TIFF_IFD_IMAGE_WIDTH 256
+#define TIFF_IFD_IMAGE_LENGTH 257
+#define TIFF_IFD_BITS_PER_SAMPLE 258
+#define TIFF_IFD_COMPRESSION 259
+#define TIFF_IFD_PHOTOMETRIC_INTERP 262
+#define TIFF_IFD_FILL_ORDER 266
+#define TIFF_IFD_STRIP_OFFSETS 273
+#define TIFF_IFD_ORIENTATION 274
+#define TIFF_IFD_SAMPLES_PER_PIXEL 277
+#define TIFF_IFD_ROWS_PER_STRIP 278
+#define TIFF_IFD_STRIP_BYTE_COUNTS 279
+#define TIFF_IFD_X_RESOLUTION 282
+#define TIFF_IFD_Y_RESOLUTION 283
+#define TIFF_IFD_PLANAR_CONFIG 284
+#define TIFF_IFD_T4_OPTIONS 292
+#define TIFF_IFD_RESOLUTION_UNIT 296
+#define TIFF_IFD_PAGE_NUMBER 297
+#define TIFF_IFD_CLEAN_FAX_DATA 327
+
+// IFD type values.
+#define TIFF_TYPE_SHORT 3
+#define TIFF_TYPE_LONG 4
+#define TIFF_TYPE_RATIONAL 5
+
+// Construct a SHORT pair from two values.
+#define TIFF_SHORT_PAIR(a,b) (((a) & 0xFFFF) | ((b) << 16))
+
+// Width of a FAX page in pixels, in the baseline specification from RFC 2301.
+// This must be hard-wired, as per the RFC. We truncate any pixels that
+// are beyond this limit, or pad lines to reach this limit.
+#define TIFF_FAX_WIDTH 1728
+
+void QtopiaPrintEnginePrivate::writeG3FaxPage()
+{
+ // Pad the image file to a word boundary, just in case.
+ buffer.pad();
+
+ // Back-patch the IFD link for the previous page.
+ buffer.patch( ifdPatch, buffer.size() );
+
+ // Output the contents of the IFD for this page (these must be
+ // in ascending order of tag value).
+ buffer.append( (short)19 ); // Number of IFD entries.
+ writeG3IFDEntry( TIFF_IFD_NEW_SUB_FILE_TYPE, TIFF_TYPE_LONG, 1, 2 );
+ writeG3IFDEntry( TIFF_IFD_IMAGE_WIDTH, TIFF_TYPE_LONG, 1, TIFF_FAX_WIDTH );
+ writeG3IFDEntry
+ ( TIFF_IFD_IMAGE_LENGTH, TIFF_TYPE_LONG, 1, pageImage->height() );
+ writeG3IFDEntry( TIFF_IFD_BITS_PER_SAMPLE, TIFF_TYPE_SHORT, 1, 1 );
+ writeG3IFDEntry( TIFF_IFD_COMPRESSION, TIFF_TYPE_SHORT, 1, 3 );
+ writeG3IFDEntry( TIFF_IFD_PHOTOMETRIC_INTERP, TIFF_TYPE_SHORT, 1, 0 );
+ writeG3IFDEntry( TIFF_IFD_FILL_ORDER, TIFF_TYPE_SHORT, 1, 1 );
+ int stripOffsets =
+ writeG3IFDEntry( TIFF_IFD_STRIP_OFFSETS, TIFF_TYPE_LONG, 1, 0 );
+ writeG3IFDEntry( TIFF_IFD_ORIENTATION, TIFF_TYPE_SHORT, 1, 1 );
+ writeG3IFDEntry( TIFF_IFD_SAMPLES_PER_PIXEL, TIFF_TYPE_SHORT, 1, 1 );
+ writeG3IFDEntry
+ ( TIFF_IFD_ROWS_PER_STRIP, TIFF_TYPE_LONG, 1, pageImage->height() );
+ int stripBytes = writeG3IFDEntry
+ ( TIFF_IFD_STRIP_BYTE_COUNTS, TIFF_TYPE_LONG, 1, 0 );
+ int xres =
+ writeG3IFDEntry( TIFF_IFD_X_RESOLUTION, TIFF_TYPE_RATIONAL, 1, 0 );
+ int yres =
+ writeG3IFDEntry( TIFF_IFD_Y_RESOLUTION, TIFF_TYPE_RATIONAL, 1, 0 );
+ writeG3IFDEntry( TIFF_IFD_PLANAR_CONFIG, TIFF_TYPE_SHORT, 1, 1 );
+ writeG3IFDEntry( TIFF_IFD_T4_OPTIONS, TIFF_TYPE_LONG, 1, 2 );
+ writeG3IFDEntry( TIFF_IFD_RESOLUTION_UNIT, TIFF_TYPE_SHORT, 1, 2 );
+ writeG3IFDEntry( TIFF_IFD_PAGE_NUMBER, TIFF_TYPE_SHORT, 2,
+ TIFF_SHORT_PAIR( pageNumber, 0 ) );
+ writeG3IFDEntry( TIFF_IFD_CLEAN_FAX_DATA, TIFF_TYPE_SHORT, 1, 0 );
+
+ // Leave a place-holder for the IFD offset of the next page.
+ ifdPatch = buffer.size();
+ buffer.append( (int)0 );
+
+ // Output the X and Y resolutions, as rational values (usually 200/1).
+ buffer.patch( xres, buffer.size() );
+ buffer.append( (int)resolution );
+ buffer.append( (int)1 );
+ buffer.patch( yres, buffer.size() );
+ buffer.append( (int)resolution );
+ buffer.append( (int)1 );
+
+ // We are now at the start of the image data - set the strip offset.
+ int start = buffer.size();
+ buffer.patch( stripOffsets, start );
+
+ // Output the image data.
+ int width = pageImage->width();
+ QImage::Format imageFormat = pageImage->format();
+ for ( int y = 0; y < pageImage->height(); ++y ) {
+ unsigned char *scan = pageImage->scanLine(y);
+ int prev, pixel, len;
+ writeG3EOL();
+ prev = 0;
+ len = 0;
+
+ uint currentColor = qRgb(255, 255, 255); // start with white
+
+ for ( int x = 0; x < width && x < TIFF_FAX_WIDTH; ++x ) {
+ if ( imageFormat == QImage::Format_RGB32 ) {
+ // read color of the current pixel
+ uint *p = (uint *)scan + x;
+
+ if ( *p == currentColor ) { // if it is the same color
+ len++; // imcrement length
+ } else { // otherwise write color into the buffer
+ if ( len > 0 ) {
+ if ( currentColor == qRgb(0, 0, 0) )
+ writeG3BlackRun( len );
+ else
+ writeG3WhiteRun( len );
+ }
+ // initialise length and color;
+ len = 1;
+ currentColor = *p;
+ }
+ } else if ( imageFormat == QImage::Format_Mono ) {
+ pixel = ((scan[x >> 3] & (1 << (x & 7))) != 0);
+ if ( pixel != prev ) {
+ if ( prev ) {
+ writeG3BlackRun( len );
+ } else {
+ writeG3WhiteRun( len );
+ }
+ prev = pixel;
+ len = 1;
+ } else {
+ ++len;
+ }
+ }
+ }
+
+ if ( imageFormat == QImage::Format_RGB32 ) {
+ // Output the last run on the line, and pad to TIFF_FAX_WIDTH.
+ if ( len != 0 ) {
+ if ( currentColor == qRgb(0, 0, 0) )
+ writeG3BlackRun( len );
+ else
+ writeG3WhiteRun( len );
+ }
+ if ( width < TIFF_FAX_WIDTH )
+ writeG3WhiteRun( TIFF_FAX_WIDTH - width );
+ } else if ( imageFormat == QImage::Format_Mono ) {
+ if ( len != 0 ) {
+ if ( prev ) {
+ writeG3BlackRun( len );
+ if ( width < TIFF_FAX_WIDTH ) {
+ writeG3WhiteRun( TIFF_FAX_WIDTH - width );
+ }
+ } else {
+ if ( width < TIFF_FAX_WIDTH ) {
+ writeG3WhiteRun( len + ( TIFF_FAX_WIDTH - width ) );
+ } else {
+ writeG3WhiteRun( len );
+ }
+ }
+ }
+ }
+ }
+
+ // Flush the last partial byte, which is padded with zero fill bits.
+ if ( partialBits > 0 ) {
+ buffer.append( (char)( partialByte << ( 8 - partialBits ) ) );
+ partialByte = 0;
+ partialBits = 0;
+ }
+
+ // end of page add six EOLs
+ for ( int i = 0; i < 6; i++ )
+ writeG3EOL();
+
+ // Update the byte count for the image data strip.
+ buffer.patch( stripBytes, buffer.size() - start );
+}
+
+int QtopiaPrintEnginePrivate::writeG3IFDEntry
+ ( int tag, int type, int count, int value )
+{
+ buffer.append( (short)tag );
+ buffer.append( (short)type );
+ buffer.append( count );
+ buffer.append( value );
+ return buffer.size() - 4; // Offset of the value for back-patching.
+}
+
+void QtopiaPrintEnginePrivate::writeG3Code( int code, int bits )
+{
+ partialByte = ( ( partialByte << bits ) | code );
+ partialBits += bits;
+ while ( partialBits >= 8 ) {
+ partialBits -= 8;
+ buffer.append( (char)( partialByte >> partialBits ) );
+ }
+}
+
+void QtopiaPrintEnginePrivate::writeG3WhiteRun( int len )
+{
+ static struct {
+ unsigned short code;
+ unsigned short bits;
+ } whiteCodes[64 + 27] = {
+ {0x0035, 8}, // 0
+ {0x0007, 6},
+ {0x0007, 4},
+ {0x0008, 4},
+ {0x000B, 4},
+ {0x000C, 4},
+ {0x000E, 4},
+ {0x000F, 4},
+ {0x0013, 5}, // 8
+ {0x0014, 5},
+ {0x0007, 5},
+ {0x0008, 5},
+ {0x0008, 6},
+ {0x0003, 6},
+ {0x0034, 6},
+ {0x0035, 6},
+ {0x002A, 6}, // 16
+ {0x002B, 6},
+ {0x0027, 7},
+ {0x000C, 7},
+ {0x0008, 7},
+ {0x0017, 7},
+ {0x0003, 7},
+ {0x0004, 7},
+ {0x0028, 7}, // 24
+ {0x002B, 7},
+ {0x0013, 7},
+ {0x0024, 7},
+ {0x0018, 7},
+ {0x0002, 8},
+ {0x0003, 8},
+ {0x001A, 8},
+ {0x001B, 8}, // 32
+ {0x0012, 8},
+ {0x0013, 8},
+ {0x0014, 8},
+ {0x0015, 8},
+ {0x0016, 8},
+ {0x0017, 8},
+ {0x0028, 8},
+ {0x0029, 8}, // 40
+ {0x002A, 8},
+ {0x002B, 8},
+ {0x002C, 8},
+ {0x002D, 8},
+ {0x0004, 8},
+ {0x0005, 8},
+ {0x000A, 8},
+ {0x000B, 8}, // 48
+ {0x0052, 8},
+ {0x0053, 8},
+ {0x0054, 8},
+ {0x0055, 8},
+ {0x0024, 8},
+ {0x0025, 8},
+ {0x0058, 8},
+ {0x0059, 8}, // 56
+ {0x005A, 8},
+ {0x005B, 8},
+ {0x004A, 8},
+ {0x004B, 8},
+ {0x0032, 8},
+ {0x0033, 8},
+ {0x0034, 8},
+ {0x001B, 5}, // Make up codes: 64
+ {0x0012, 5}, // 128
+ {0x0017, 6}, // 192
+ {0x0037, 7}, // 256
+ {0x0036, 8}, // 320
+ {0x0037, 8}, // 384
+ {0x0064, 8}, // 448
+ {0x0065, 8}, // 512
+ {0x0068, 8}, // 576
+ {0x0067, 8}, // 640
+ {0x00CC, 9}, // 704
+ {0x00CD, 9}, // 768
+ {0x00D2, 9}, // 832
+ {0x00D3, 9}, // 896
+ {0x00D4, 9}, // 960
+ {0x00D5, 9}, // 1024
+ {0x00D6, 9}, // 1088
+ {0x00D7, 9}, // 1152
+ {0x00D8, 9}, // 1216
+ {0x00D9, 9}, // 1280
+ {0x00DA, 9}, // 1344
+ {0x00DB, 9}, // 1408
+ {0x0098, 9}, // 1472
+ {0x0099, 9}, // 1536
+ {0x009A, 9}, // 1600
+ {0x0018, 6}, // 1664
+ {0x009B, 9}, // 1728
+ };
+ if ( len >= 64 ) {
+ int index = 63 + (len >> 6);
+ writeG3Code( whiteCodes[index].code, whiteCodes[index].bits );
+ len &= 63;
+ }
+ writeG3Code( whiteCodes[len].code, whiteCodes[len].bits );
+}
+
+void QtopiaPrintEnginePrivate::writeG3BlackRun( int len )
+{
+ static struct {
+ unsigned short code;
+ unsigned short bits;
+ } blackCodes[64 + 27] = {
+ {0x0037, 10}, // 0
+ {0x0002, 3},
+ {0x0003, 2},
+ {0x0002, 2},
+ {0x0003, 3},
+ {0x0003, 4},
+ {0x0002, 4},
+ {0x0003, 5},
+ {0x0005, 6}, // 8
+ {0x0004, 6},
+ {0x0004, 7},
+ {0x0005, 7},
+ {0x0007, 7},
+ {0x0004, 8},
+ {0x0007, 8},
+ {0x0018, 9},
+ {0x0017, 10}, // 16
+ {0x0018, 10},
+ {0x0008, 10},
+ {0x0067, 11},
+ {0x0068, 11},
+ {0x006C, 11},
+ {0x0037, 11},
+ {0x0028, 11},
+ {0x0017, 11}, // 24
+ {0x0018, 11},
+ {0x00CA, 12},
+ {0x00CB, 12},
+ {0x00CC, 12},
+ {0x00CD, 12},
+ {0x0068, 12},
+ {0x0069, 12},
+ {0x006A, 12}, // 32
+ {0x006B, 12},
+ {0x00D2, 12},
+ {0x00D3, 12},
+ {0x00D4, 12},
+ {0x00D5, 12},
+ {0x00D6, 12},
+ {0x00D7, 12},
+ {0x006C, 12}, // 40
+ {0x006D, 12},
+ {0x00DA, 12},
+ {0x00DB, 12},
+ {0x0054, 12},
+ {0x0055, 12},
+ {0x0056, 12},
+ {0x0057, 12},
+ {0x0064, 12}, // 48
+ {0x0065, 12},
+ {0x0052, 12},
+ {0x0053, 12},
+ {0x0024, 12},
+ {0x0037, 12},
+ {0x0038, 12},
+ {0x0027, 12},
+ {0x0028, 12}, // 56
+ {0x0058, 12},
+ {0x0059, 12},
+ {0x002B, 12},
+ {0x002C, 12},
+ {0x005A, 12},
+ {0x0066, 12},
+ {0x0067, 12},
+ {0x000F, 10}, // Make up codes: 64
+ {0x00C8, 12}, // 128
+ {0x00C9, 12}, // 192
+ {0x005B, 12}, // 256
+ {0x0033, 12}, // 320
+ {0x0034, 12}, // 384
+ {0x0035, 12}, // 448
+ {0x006C, 13}, // 512
+ {0x006D, 13}, // 576
+ {0x004A, 13}, // 640
+ {0x004B, 13}, // 704
+ {0x004C, 13}, // 768
+ {0x004D, 13}, // 832
+ {0x0072, 13}, // 896
+ {0x0073, 13}, // 960
+ {0x0074, 13}, // 1024
+ {0x0075, 13}, // 1088
+ {0x0076, 13}, // 1152
+ {0x0077, 13}, // 1216
+ {0x0052, 13}, // 1280
+ {0x0053, 13}, // 1344
+ {0x0054, 13}, // 1408
+ {0x0055, 13}, // 1472
+ {0x005A, 13}, // 1536
+ {0x005B, 13}, // 1600
+ {0x0064, 13}, // 1664
+ {0x0065, 13}, // 1728
+ };
+ if ( len >= 64 ) {
+ int index = 63 + (len >> 6);
+ writeG3Code( blackCodes[index].code, blackCodes[index].bits );
+ len &= 63;
+ }
+ writeG3Code( blackCodes[len].code, blackCodes[len].bits );
+}
+
+void QtopiaPrintEnginePrivate::writeG3EOL()
+{
+ int bitToPad;
+ if ( partialBits <= 4 ) {
+ bitToPad = 4 - partialBits;
+ } else {
+ bitToPad = 8 - partialBits + 4;
+ }
+
+ partialByte = ((partialByte << (bitToPad + 12)) | 0x0001);
+ partialBits += bitToPad + 12;
+
+ while ( partialBits >= 8 ) {
+ partialBits -= 8;
+ buffer.append( (char)(partialByte >> partialBits ) );
+ }
+// writeG3Code( 0x0001, 12 );
+}
+
+void QtopiaPrintBuffer::append( short value )
+{
+ if ( _bigEndian ) {
+ _data.append( (char)(value >> 8) );
+ _data.append( (char)value );
+ } else {
+ _data.append( (char)value );
+ _data.append( (char)(value >> 8) );
+ }
+}
+
+void QtopiaPrintBuffer::append( int value )
+{
+ if ( _bigEndian ) {
+ _data.append( (char)(value >> 24) );
+ _data.append( (char)(value >> 16) );
+ _data.append( (char)(value >> 8) );
+ _data.append( (char)value );
+ } else {
+ _data.append( (char)value );
+ _data.append( (char)(value >> 8) );
+ _data.append( (char)(value >> 16) );
+ _data.append( (char)(value >> 24) );
+ }
+}
+
+void QtopiaPrintBuffer::patch( int posn, int value )
+{
+ if ( _bigEndian ) {
+ _data[posn] = (char)(value >> 24);
+ _data[posn + 1] = (char)(value >> 16);
+ _data[posn + 2] = (char)(value >> 8);
+ _data[posn + 3] = (char)value;
+ } else {
+ _data[posn] = (char)value;
+ _data[posn + 1] = (char)(value >> 8);
+ _data[posn + 2] = (char)(value >> 16);
+ _data[posn + 3] = (char)(value >> 24);
+ }
+}
+
+void QtopiaPrintBuffer::pad()
+{
+ while ( ( _data.size() % 4 ) != 0 )
+ _data.append( (char)0 );
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_PRINTER
diff --git a/src/gui/painting/qprintengine_qws_p.h b/src/gui/painting/qprintengine_qws_p.h
new file mode 100644
index 0000000000..c5472f33d8
--- /dev/null
+++ b/src/gui/painting/qprintengine_qws_p.h
@@ -0,0 +1,213 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QPRINTENGINE_QWS_P_H
+#define QPRINTENGINE_QWS_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 "QtGui/qprinter.h"
+
+#ifndef QT_NO_PRINTER
+
+#include "QtGui/qprintengine.h"
+#include "QtCore/qbytearray.h"
+#include "private/qpaintengine_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class QtopiaPrintEnginePrivate;
+class QRasterPaintEngine;
+class QPrinterPrivate;
+class QImage;
+
+class QtopiaPrintEngine : public QPaintEngine, public QPrintEngine
+{
+ Q_DECLARE_PRIVATE(QtopiaPrintEngine)
+public:
+ QtopiaPrintEngine(QPrinter::PrinterMode mode);
+
+ // override QWSPaintEngine
+ bool begin(QPaintDevice *dev);
+ bool end();
+ void drawPixmap(const QRectF &r, const QPixmap &pm, const QRectF &sr);
+ void drawTextItem(const QPointF &p, const QTextItem &ti);
+ QPaintEngine::Type type() const { return QPaintEngine::X11; }
+
+ QPaintEngine *paintEngine() const;
+
+ void updateState(const QPaintEngineState &state);
+
+ QRect paperRect() const;
+ QRect pageRect() const;
+
+ bool newPage();
+ bool abort();
+
+ QPrinter::PrinterState printerState() const;
+
+ int metric(QPaintDevice::PaintDeviceMetric metricType) const;
+
+ QVariant property(PrintEnginePropertyKey key) const;
+ void setProperty(PrintEnginePropertyKey key, const QVariant &value);
+
+private:
+ friend class QPrintDialog;
+ friend class QPageSetupDialog;
+
+ void clearPage();
+ void flushPage();
+};
+
+class QtopiaPrintBuffer
+{
+public:
+ QtopiaPrintBuffer( bool bigEndian=FALSE ) { _bigEndian = bigEndian; }
+ ~QtopiaPrintBuffer() {}
+
+ const QByteArray& data() const { return _data; }
+
+ int size() const { return _data.size(); }
+
+ void clear() { _data.clear(); }
+
+ void append( char value ) { _data.append( value ); }
+ void append( short value );
+ void append( int value );
+ void append( const QByteArray& array ) { _data.append( array ); }
+
+ void patch( int posn, int value );
+
+ void pad();
+
+private:
+ QByteArray _data;
+ bool _bigEndian;
+};
+
+#define QT_QWS_PRINTER_DEFAULT_DPI 200
+
+class QtopiaPrintEnginePrivate : public QPaintEnginePrivate
+{
+ Q_DECLARE_PUBLIC(QtopiaPrintEngine)
+public:
+ QtopiaPrintEnginePrivate(QPrinter::PrinterMode m) :
+ mode(m),
+ printerState(QPrinter::Idle),
+ orientation(QPrinter::Portrait),
+ paperSize(QPrinter::A4),
+ pageOrder(QPrinter::FirstPageFirst),
+ colorMode(QPrinter::GrayScale),
+ paperSource(QPrinter::OnlyOne),
+ resolution(QT_QWS_PRINTER_DEFAULT_DPI),
+ _paintEngine(0),
+ numCopies(1),
+ outputToFile(false),
+ fullPage(false),
+ collateCopies(false),
+ pageNumber(0),
+ pageImage(0),
+ partialByte(0),
+ partialBits(0)
+ {
+ }
+ ~QtopiaPrintEnginePrivate();
+
+ void initialize();
+ QPaintEngine *paintEngine();
+
+ QPrinter::PrinterMode mode;
+
+ QString printerName;
+ QString outputFileName;
+ QString printProgram;
+ QString docName;
+ QString creator;
+
+ QPrinter::PrinterState printerState;
+
+ QPrinter::Orientation orientation;
+ QPrinter::PaperSize paperSize;
+ QPrinter::PageOrder pageOrder;
+ QPrinter::ColorMode colorMode;
+ QPrinter::PaperSource paperSource;
+
+ int resolution;
+ QPaintEngine *_paintEngine;
+ int numCopies;
+
+ bool outputToFile;
+ bool fullPage;
+ bool collateCopies;
+
+ int pageNumber;
+
+ QImage *pageImage;
+
+ QtopiaPrintBuffer buffer;
+
+ // Definitions that are only relevant to G3FAX output.
+ int ifdPatch;
+ int partialByte;
+ int partialBits;
+ void writeG3FaxHeader();
+ void writeG3FaxPage();
+ int writeG3IFDEntry( int tag, int type, int count, int value );
+ void writeG3Code( int code, int bits );
+ void writeG3WhiteRun( int len );
+ void writeG3BlackRun( int len );
+ void writeG3EOL();
+};
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_PRINTER
+
+#endif // QPRINTENGINE_QWS_P_H
diff --git a/src/gui/painting/qprintengine_win.cpp b/src/gui/painting/qprintengine_win.cpp
new file mode 100644
index 0000000000..d48b5259a5
--- /dev/null
+++ b/src/gui/painting/qprintengine_win.cpp
@@ -0,0 +1,1968 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QT_NO_PRINTER
+
+#include "qprinter_p.h"
+#include "qprintengine_win_p.h"
+
+#include <limits.h>
+
+#include <private/qfont_p.h>
+#include <private/qfontengine_p.h>
+#include <private/qpainter_p.h>
+
+#include <qbitmap.h>
+#include <qdebug.h>
+#include <qvector.h>
+#include <qpicture.h>
+#include <private/qpicture_p.h>
+
+QT_BEGIN_NAMESPACE
+
+extern QPainterPath qt_regionToPath(const QRegion &region);
+
+// #define QT_DEBUG_DRAW
+
+static void draw_text_item_win(const QPointF &_pos, const QTextItemInt &ti, HDC hdc,
+ bool convertToText, const QTransform &xform, const QPointF &topLeft);
+
+static const struct {
+ int winSizeName;
+ QPrinter::PaperSize qtSizeName;
+} dmMapping[] = {
+ { DMPAPER_LETTER, QPrinter::Letter },
+ { DMPAPER_LETTERSMALL, QPrinter::Letter },
+ { DMPAPER_TABLOID, QPrinter::Tabloid },
+ { DMPAPER_LEDGER, QPrinter::Ledger },
+ { DMPAPER_LEGAL, QPrinter::Legal },
+ { DMPAPER_EXECUTIVE, QPrinter::Executive },
+ { DMPAPER_A3, QPrinter::A3 },
+ { DMPAPER_A4, QPrinter::A4 },
+ { DMPAPER_A4SMALL, QPrinter::A4 },
+ { DMPAPER_A5, QPrinter::A5 },
+ { DMPAPER_B4, QPrinter::B4 },
+ { DMPAPER_B5, QPrinter::B5 },
+ { DMPAPER_FOLIO, QPrinter::Folio },
+ { DMPAPER_ENV_10, QPrinter::Comm10E },
+ { DMPAPER_ENV_DL, QPrinter::DLE },
+ { DMPAPER_ENV_C3, QPrinter::C5E },
+ { DMPAPER_LETTER_EXTRA, QPrinter::Letter },
+ { DMPAPER_LEGAL_EXTRA, QPrinter::Legal },
+ { DMPAPER_TABLOID_EXTRA, QPrinter::Tabloid },
+ { DMPAPER_A4_EXTRA, QPrinter::A4},
+ { DMPAPER_LETTER_TRANSVERSE, QPrinter::Letter},
+ { DMPAPER_A4_TRANSVERSE, QPrinter::A4},
+ { DMPAPER_LETTER_EXTRA_TRANSVERSE, QPrinter::Letter },
+ { DMPAPER_A_PLUS, QPrinter::A4 },
+ { DMPAPER_B_PLUS, QPrinter::A3 },
+ { DMPAPER_LETTER_PLUS, QPrinter::Letter },
+ { DMPAPER_A4_PLUS, QPrinter::A4 },
+ { DMPAPER_A5_TRANSVERSE, QPrinter::A5 },
+ { DMPAPER_B5_TRANSVERSE, QPrinter::B5 },
+ { DMPAPER_A3_EXTRA, QPrinter::A3 },
+ { DMPAPER_A5_EXTRA, QPrinter::A5 },
+ { DMPAPER_B5_EXTRA, QPrinter::B5 },
+ { DMPAPER_A2, QPrinter::A2 },
+ { DMPAPER_A3_TRANSVERSE, QPrinter::A3 },
+ { DMPAPER_A3_EXTRA_TRANSVERSE,QPrinter::A3 },
+ { 0, QPrinter::Custom }
+};
+
+QPrinter::PaperSize mapDevmodePaperSize(int s)
+{
+ int i = 0;
+ while ((dmMapping[i].winSizeName > 0) && (dmMapping[i].winSizeName != s))
+ i++;
+ return dmMapping[i].qtSizeName;
+}
+
+static int mapPaperSizeDevmode(QPrinter::PaperSize s)
+{
+ int i = 0;
+ while ((dmMapping[i].winSizeName > 0) && (dmMapping[i].qtSizeName != s))
+ i++;
+ return dmMapping[i].winSizeName;
+}
+
+static const struct {
+ int winSourceName;
+ QPrinter::PaperSource qtSourceName;
+} sources[] = {
+ { DMBIN_ONLYONE, QPrinter::OnlyOne },
+ { DMBIN_LOWER, QPrinter::Lower },
+ { DMBIN_MIDDLE, QPrinter::Middle },
+ { DMBIN_MANUAL, QPrinter::Manual },
+ { DMBIN_ENVELOPE, QPrinter::Envelope },
+ { DMBIN_ENVMANUAL, QPrinter::EnvelopeManual },
+ { DMBIN_AUTO, QPrinter::Auto },
+ { DMBIN_TRACTOR, QPrinter::Tractor },
+ { DMBIN_SMALLFMT, QPrinter::SmallFormat },
+ { DMBIN_LARGEFMT, QPrinter::LargeFormat },
+ { DMBIN_LARGECAPACITY, QPrinter::LargeCapacity },
+ { DMBIN_CASSETTE, QPrinter::Cassette },
+ { DMBIN_FORMSOURCE, QPrinter::FormSource },
+ { 0, (QPrinter::PaperSource) -1 }
+};
+
+static QPrinter::PaperSource mapDevmodePaperSource(int s)
+{
+ int i = 0;
+ while ((sources[i].winSourceName > 0) && (sources[i].winSourceName != s))
+ i++;
+ return sources[i].winSourceName ? sources[i].qtSourceName : (QPrinter::PaperSource) s;
+}
+
+static int mapPaperSourceDevmode(QPrinter::PaperSource s)
+{
+ int i = 0;
+ while ((sources[i].qtSourceName >= 0) && (sources[i].qtSourceName != s))
+ i++;
+ return sources[i].winSourceName ? sources[i].winSourceName : s;
+}
+
+QWin32PrintEngine::QWin32PrintEngine(QPrinter::PrinterMode mode)
+ : QAlphaPaintEngine(*(new QWin32PrintEnginePrivate),
+ PaintEngineFeatures(PrimitiveTransform
+ | PixmapTransform
+ | PerspectiveTransform
+ | PainterPaths
+ | Antialiasing
+ | PaintOutsidePaintEvent))
+{
+ Q_D(QWin32PrintEngine);
+ d->docName = QLatin1String("document1");
+ d->mode = mode;
+ d->queryDefault();
+ d->initialize();
+}
+
+bool QWin32PrintEngine::begin(QPaintDevice *pdev)
+{
+ Q_D(QWin32PrintEngine);
+
+ QAlphaPaintEngine::begin(pdev);
+ if (!continueCall())
+ return true;
+
+ if (d->reinit) {
+ d->resetDC();
+ d->reinit = false;
+ }
+
+ // ### set default colors and stuff...
+
+ bool ok = d->state == QPrinter::Idle;
+
+ if (!d->hdc)
+ return false;
+
+ // Assign the FILE: to get the query...
+ if (d->printToFile && d->fileName.isEmpty())
+ d->fileName = d->port;
+
+ QT_WA({
+ d->devModeW()->dmCopies = d->num_copies;
+ DOCINFO di;
+ memset(&di, 0, sizeof(DOCINFO));
+ di.cbSize = sizeof(DOCINFO);
+ di.lpszDocName = reinterpret_cast<const wchar_t *>(d->docName.utf16());
+ if (d->printToFile && !d->fileName.isEmpty())
+ di.lpszOutput = reinterpret_cast<const wchar_t *>(d->fileName.utf16());
+ if (ok && StartDoc(d->hdc, &di) == SP_ERROR) {
+ qErrnoWarning("QWin32PrintEngine::begin: StartDoc failed");
+ ok = false;
+ }
+ } , {
+ d->devModeA()->dmCopies = d->num_copies;
+ DOCINFOA di;
+ memset(&di, 0, sizeof(DOCINFOA));
+ di.cbSize = sizeof(DOCINFOA);
+ QByteArray docNameA = d->docName.toLocal8Bit();
+ di.lpszDocName = docNameA.data();
+ QByteArray outfileA = d->fileName.toLocal8Bit();
+ if (d->printToFile && !d->fileName.isEmpty())
+ di.lpszOutput = outfileA;
+ if (ok && StartDocA(d->hdc, &di) == SP_ERROR) {
+ qErrnoWarning("QWin32PrintEngine::begin: StartDoc failed");
+ ok = false;
+ }
+ });
+
+ if (StartPage(d->hdc) <= 0) {
+ qErrnoWarning("QWin32PrintEngine::begin: StartPage failed");
+ ok = false;
+ }
+
+ if (!ok) {
+ d->state = QPrinter::Idle;
+ } else {
+ d->state = QPrinter::Active;
+ }
+
+ d->matrix = QTransform();
+ d->has_pen = true;
+ d->pen = QColor(Qt::black);
+ d->has_brush = false;
+
+ d->complex_xform = false;
+
+ updateMatrix(d->matrix);
+
+ if (!ok)
+ cleanUp();
+
+ return ok;
+}
+
+bool QWin32PrintEngine::end()
+{
+ Q_D(QWin32PrintEngine);
+
+ if (d->hdc) {
+ if (d->state == QPrinter::Aborted) {
+ cleanUp();
+ AbortDoc(d->hdc);
+ return true;
+ }
+ }
+
+ QAlphaPaintEngine::end();
+ if (!continueCall())
+ return true;
+
+ if (d->hdc) {
+ EndPage(d->hdc); // end; printing done
+ EndDoc(d->hdc);
+ }
+
+ d->state = QPrinter::Idle;
+ d->reinit = true;
+ return true;
+}
+
+bool QWin32PrintEngine::newPage()
+{
+ Q_D(QWin32PrintEngine);
+ Q_ASSERT(isActive());
+
+ Q_ASSERT(d->hdc);
+
+ flushAndInit();
+
+ bool transparent = GetBkMode(d->hdc) == TRANSPARENT;
+
+ if (!EndPage(d->hdc)) {
+ qErrnoWarning("QWin32PrintEngine::newPage: EndPage failed");
+ return false;
+ }
+
+ if (d->reinit) {
+ if (!d->resetDC()) {
+ qErrnoWarning("QWin32PrintEngine::newPage: ResetDC failed");
+ return false;
+ }
+ d->reinit = false;
+ }
+
+ if (!StartPage(d->hdc)) {
+ qErrnoWarning("Win32PrintEngine::newPage: StartPage failed");
+ return false;
+ }
+
+ SetTextAlign(d->hdc, TA_BASELINE);
+ if (transparent)
+ SetBkMode(d->hdc, TRANSPARENT);
+
+ // ###
+ return true;
+
+ bool success = false;
+ if (d->hdc && d->state == QPrinter::Active) {
+// bool restorePainter = false;
+// if ((qWinVersion()& Qt::WV_DOS_based) && painter && painter->isActive()) {
+// painter->save(); // EndPage/StartPage ruins the DC
+// restorePainter = true;
+// }
+ if (EndPage(d->hdc) != SP_ERROR) {
+ // reinitialize the DC before StartPage if needed,
+ // because resetdc is disabled between calls to the StartPage and EndPage functions
+ // (see StartPage documentation in the Platform SDK:Windows GDI)
+// state = PST_ACTIVEDOC;
+// reinit();
+// state = PST_ACTIVE;
+ // start the new page now
+ if (d->reinit) {
+ if (!d->resetDC())
+ qErrnoWarning("QWin32PrintEngine::newPage(), ResetDC failed (2)");
+ d->reinit = false;
+ }
+ success = (StartPage(d->hdc) != SP_ERROR);
+ }
+ if (!success)
+ d->state = QPrinter::Aborted;
+
+// if (qWinVersion() & Qt::WV_DOS_based)
+// if (restorePainter) {
+// painter->restore();
+// }
+
+ if (!success)
+ return false;
+ }
+ return true;
+}
+
+bool QWin32PrintEngine::abort()
+{
+ // do nothing loop.
+ return false;
+}
+
+void QWin32PrintEngine::drawTextItem(const QPointF &p, const QTextItem &textItem)
+{
+ Q_D(const QWin32PrintEngine);
+
+ QAlphaPaintEngine::drawTextItem(p, textItem);
+ if (!continueCall())
+ return;
+
+ const QTextItemInt &ti = static_cast<const QTextItemInt &>(textItem);
+ QRgb brushColor = state->pen().brush().color().rgb();
+ bool fallBack = state->pen().brush().style() != Qt::SolidPattern
+ || qAlpha(brushColor) != 0xff
+ || QT_WA_INLINE(false, d->txop >= QTransform::TxScale)
+ || ti.fontEngine->type() != QFontEngine::Win;
+
+
+ if (!fallBack) {
+ QFontEngineWin *fe = static_cast<QFontEngineWin *>(ti.fontEngine);
+
+ // Try selecting the font to see if we get a substitution font
+ SelectObject(d->hdc, fe->hfont);
+
+ if (GetDeviceCaps(d->hdc, TECHNOLOGY) != DT_CHARSTREAM) {
+ QT_WA({
+ TCHAR n[64];
+ GetTextFaceW(d->hdc, 64, n);
+ fallBack = QString::fromUtf16((ushort *)n)
+ != QString::fromUtf16((ushort *)fe->logfont.lfFaceName);
+ } , {
+ char an[64];
+ GetTextFaceA(d->hdc, 64, an);
+ fallBack = QString::fromLocal8Bit(an)
+ != QString::fromLocal8Bit(((LOGFONTA*)(&fe->logfont))->lfFaceName);
+ });
+ }
+ }
+
+
+ if (fallBack) {
+ QPaintEngine::drawTextItem(p, textItem);
+ return ;
+ }
+
+ // We only want to convert the glyphs to text if the entire string is latin1
+ bool latin1String = true;
+ for (int i=0; i < ti.num_chars; ++i) {
+ if (ti.chars[i].unicode() >= 0x100) {
+ latin1String = false;
+ break;
+ }
+ }
+
+ COLORREF cf = RGB(qRed(brushColor), qGreen(brushColor), qBlue(brushColor));
+ SelectObject(d->hdc, CreateSolidBrush(cf));
+ SelectObject(d->hdc, CreatePen(PS_SOLID, 1, cf));
+ SetTextColor(d->hdc, cf);
+
+ draw_text_item_win(p, ti, d->hdc, latin1String, d->matrix, d->devPaperRect.topLeft());
+ DeleteObject(SelectObject(d->hdc,GetStockObject(HOLLOW_BRUSH)));
+ DeleteObject(SelectObject(d->hdc,GetStockObject(BLACK_PEN)));
+}
+
+static inline qreal mmToInches(double mm)
+{
+ return mm*0.039370147;
+}
+
+static inline qreal inchesToMM(double in)
+{
+ return in/0.039370147;
+}
+
+int QWin32PrintEngine::metric(QPaintDevice::PaintDeviceMetric m) const
+{
+ Q_D(const QWin32PrintEngine);
+
+ if (!d->hdc)
+ return 0;
+
+ int val;
+ int res = d->resolution;
+
+ switch (m) {
+ case QPaintDevice::PdmWidth:
+ if (d->has_custom_paper_size) {
+ val = qRound(d->paper_size.width() * res / 72.0);
+ } else {
+ int logPixelsX = GetDeviceCaps(d->hdc, LOGPIXELSX);
+ if (logPixelsX == 0) {
+ qWarning("QWin32PrintEngine::metric: GetDeviceCaps() failed, "
+ "might be a driver problem");
+ logPixelsX = 600; // Reasonable default
+ }
+ val = res
+ * GetDeviceCaps(d->hdc, d->fullPage ? PHYSICALWIDTH : HORZRES)
+ / logPixelsX;
+ }
+ if (d->pageMarginsSet)
+ val -= int(mmToInches((d->previousDialogMargins.left() +
+ d->previousDialogMargins.width()) / 100.0) * res);
+ break;
+ case QPaintDevice::PdmHeight:
+ if (d->has_custom_paper_size) {
+ val = qRound(d->paper_size.height() * res / 72.0);
+ } else {
+ int logPixelsY = GetDeviceCaps(d->hdc, LOGPIXELSY);
+ if (logPixelsY == 0) {
+ qWarning("QWin32PrintEngine::metric: GetDeviceCaps() failed, "
+ "might be a driver problem");
+ logPixelsY = 600; // Reasonable default
+ }
+ val = res
+ * GetDeviceCaps(d->hdc, d->fullPage ? PHYSICALHEIGHT : VERTRES)
+ / logPixelsY;
+ }
+ if (d->pageMarginsSet)
+ val -= int(mmToInches((d->previousDialogMargins.top() +
+ d->previousDialogMargins.height()) / 100.0) * res);
+ break;
+ case QPaintDevice::PdmDpiX:
+ val = res;
+ break;
+ case QPaintDevice::PdmDpiY:
+ val = res;
+ break;
+ case QPaintDevice::PdmPhysicalDpiX:
+ val = GetDeviceCaps(d->hdc, LOGPIXELSX);
+ break;
+ case QPaintDevice::PdmPhysicalDpiY:
+ val = GetDeviceCaps(d->hdc, LOGPIXELSY);
+ break;
+ case QPaintDevice::PdmWidthMM:
+ if (d->has_custom_paper_size) {
+ val = qRound(d->paper_size.width()*25.4/72);
+ } else {
+ if (!d->fullPage) {
+ val = GetDeviceCaps(d->hdc, HORZSIZE);
+ } else {
+ float wi = 25.4 * GetDeviceCaps(d->hdc, PHYSICALWIDTH);
+ int logPixelsX = GetDeviceCaps(d->hdc, LOGPIXELSX);
+ if (logPixelsX == 0) {
+ qWarning("QWin32PrintEngine::metric: GetDeviceCaps() failed, "
+ "might be a driver problem");
+ logPixelsX = 600; // Reasonable default
+ }
+ val = qRound(wi / logPixelsX);
+ }
+ }
+ if (d->pageMarginsSet)
+ val -= (d->previousDialogMargins.left() +
+ d->previousDialogMargins.width()) / 100.0;
+ break;
+ case QPaintDevice::PdmHeightMM:
+ if (d->has_custom_paper_size) {
+ val = qRound(d->paper_size.height()*25.4/72);
+ } else {
+ if (!d->fullPage) {
+ val = GetDeviceCaps(d->hdc, VERTSIZE);
+ } else {
+ float hi = 25.4 * GetDeviceCaps(d->hdc, PHYSICALHEIGHT);
+ int logPixelsY = GetDeviceCaps(d->hdc, LOGPIXELSY);
+ if (logPixelsY == 0) {
+ qWarning("QWin32PrintEngine::metric: GetDeviceCaps() failed, "
+ "might be a driver problem");
+ logPixelsY = 600; // Reasonable default
+ }
+ val = qRound(hi / logPixelsY);
+ }
+ }
+ if (d->pageMarginsSet)
+ val -= (d->previousDialogMargins.top() +
+ d->previousDialogMargins.height()) / 100.0;
+ break;
+ case QPaintDevice::PdmNumColors:
+ {
+ int bpp = GetDeviceCaps(d->hdc, BITSPIXEL);
+ if(bpp==32)
+ val = INT_MAX;
+ else if(bpp<=8)
+ val = GetDeviceCaps(d->hdc, NUMCOLORS);
+ else
+ val = 1 << (bpp * GetDeviceCaps(d->hdc, PLANES));
+ }
+ break;
+ case QPaintDevice::PdmDepth:
+ val = GetDeviceCaps(d->hdc, PLANES);
+ break;
+ default:
+ qWarning("QPrinter::metric: Invalid metric command");
+ return 0;
+ }
+ return val;
+}
+
+void QWin32PrintEngine::updateState(const QPaintEngineState &state)
+{
+ Q_D(QWin32PrintEngine);
+
+ QAlphaPaintEngine::updateState(state);
+ if (!continueCall())
+ return;
+
+ if (state.state() & DirtyTransform) {
+ updateMatrix(state.transform());
+ }
+
+ if (state.state() & DirtyPen) {
+ d->pen = state.pen();
+ d->has_pen = d->pen.style() != Qt::NoPen && d->pen.isSolid();
+ }
+
+ if (state.state() & DirtyBrush) {
+ QBrush brush = state.brush();
+ d->has_brush = brush.style() == Qt::SolidPattern;
+ d->brush_color = brush.color();
+ }
+
+ if (state.state() & DirtyClipEnabled) {
+ if (state.isClipEnabled())
+ updateClipPath(painter()->clipPath(), Qt::ReplaceClip);
+ else
+ updateClipPath(QPainterPath(), Qt::NoClip);
+ }
+
+ if (state.state() & DirtyClipPath) {
+ updateClipPath(state.clipPath(), state.clipOperation());
+ }
+
+ if (state.state() & DirtyClipRegion) {
+ QRegion clipRegion = state.clipRegion();
+ QPainterPath clipPath = qt_regionToPath(clipRegion);
+ updateClipPath(clipPath, state.clipOperation());
+ }
+}
+
+void QWin32PrintEngine::updateClipPath(const QPainterPath &clipPath, Qt::ClipOperation op)
+{
+ Q_D(QWin32PrintEngine);
+
+ bool doclip = true;
+ if (op == Qt::NoClip) {
+ SelectClipRgn(d->hdc, 0);
+ doclip = false;
+ }
+
+ if (doclip) {
+ QPainterPath xformed = clipPath * d->matrix;
+
+ if (xformed.isEmpty()) {
+ QRegion empty(-0x1000000, -0x1000000, 1, 1);
+ SelectClipRgn(d->hdc, empty.handle());
+ } else {
+ d->composeGdiPath(xformed);
+ const int ops[] = {
+ -1, // Qt::NoClip, covered above
+ RGN_COPY, // Qt::ReplaceClip
+ RGN_AND, // Qt::IntersectClip
+ RGN_OR // Qt::UniteClip
+ };
+ Q_ASSERT(op > 0 && unsigned(op) <= sizeof(ops) / sizeof(int));
+ SelectClipPath(d->hdc, ops[op]);
+ }
+ }
+
+ QPainterPath aclip = qt_regionToPath(alphaClipping());
+ if (!aclip.isEmpty()) {
+ QTransform tx(d->stretch_x, 0, 0, d->stretch_y, d->origin_x, d->origin_y);
+ d->composeGdiPath(tx.map(aclip));
+ SelectClipPath(d->hdc, RGN_DIFF);
+ }
+}
+
+void QWin32PrintEngine::updateMatrix(const QTransform &m)
+{
+ Q_D(QWin32PrintEngine);
+
+ QTransform stretch(d->stretch_x, 0, 0, d->stretch_y, d->origin_x, d->origin_y);
+ d->painterMatrix = m;
+ d->matrix = d->painterMatrix * stretch;
+ d->txop = d->matrix.type();
+ d->complex_xform = (d->txop > QTransform::TxScale);
+}
+
+void QWin32PrintEngine::drawPixmap(const QRectF &targetRect,
+ const QPixmap &originalPixmap,
+ const QRectF &sourceRect)
+{
+ Q_D(QWin32PrintEngine);
+
+ QAlphaPaintEngine::drawPixmap(targetRect, originalPixmap, sourceRect);
+ if (!continueCall())
+ return;
+
+ const int tileSize = 2048;
+
+ QRectF r = targetRect;
+ QRectF sr = sourceRect;
+
+ QPixmap pixmap = originalPixmap;
+ if (sr.size() != pixmap.size()) {
+ pixmap = pixmap.copy(sr.toRect());
+ }
+
+ qreal scaleX = 1.0f;
+ qreal scaleY = 1.0f;
+
+ QTransform scaleMatrix;
+ scaleMatrix.scale(r.width() / pixmap.width(), r.height() / pixmap.height());
+ QTransform adapted = QPixmap::trueMatrix(d->painterMatrix * scaleMatrix,
+ pixmap.width(), pixmap.height());
+
+ qreal xform_offset_x = adapted.dx();
+ qreal xform_offset_y = adapted.dy();
+
+ if (d->complex_xform) {
+ pixmap = pixmap.transformed(adapted);
+ scaleX = d->stretch_x;
+ scaleY = d->stretch_y;
+ } else {
+ scaleX = d->stretch_x * (r.width() / pixmap.width()) * d->painterMatrix.m11();
+ scaleY = d->stretch_y * (r.height() / pixmap.height()) * d->painterMatrix.m22();
+ }
+
+ QPointF topLeft = r.topLeft() * d->painterMatrix;
+ int tx = int(topLeft.x() * d->stretch_x + d->origin_x);
+ int ty = int(topLeft.y() * d->stretch_y + d->origin_y);
+ int tw = qAbs(int(pixmap.width() * scaleX));
+ int th = qAbs(int(pixmap.height() * scaleY));
+
+ xform_offset_x *= d->stretch_x;
+ xform_offset_y *= d->stretch_y;
+
+ int dc_state = SaveDC(d->hdc);
+
+ int tilesw = pixmap.width() / tileSize;
+ int tilesh = pixmap.height() / tileSize;
+ ++tilesw;
+ ++tilesh;
+
+ int txinc = tileSize*scaleX;
+ int tyinc = tileSize*scaleY;
+
+ for (int y = 0; y < tilesh; ++y) {
+ int tposy = ty + (y * tyinc);
+ int imgh = tileSize;
+ int height = tyinc;
+ if (y == (tilesh - 1)) {
+ imgh = pixmap.height() - (y * tileSize);
+ height = (th - (y * tyinc));
+ }
+ for (int x = 0; x < tilesw; ++x) {
+ int tposx = tx + (x * txinc);
+ int imgw = tileSize;
+ int width = txinc;
+ if (x == (tilesw - 1)) {
+ imgw = pixmap.width() - (x * tileSize);
+ width = (tw - (x * txinc));
+ }
+
+ QPixmap p = pixmap.copy(tileSize * x, tileSize * y, imgw, imgh);
+ HBITMAP hbitmap = p.toWinHBITMAP(QPixmap::NoAlpha);
+ HDC display_dc = GetDC(0);
+ HDC hbitmap_hdc = CreateCompatibleDC(display_dc);
+ HGDIOBJ null_bitmap = SelectObject(hbitmap_hdc, hbitmap);
+
+ ReleaseDC(0, display_dc);
+
+ if (!StretchBlt(d->hdc, qRound(tposx - xform_offset_x), qRound(tposy - xform_offset_y), width, height,
+ hbitmap_hdc, 0, 0, p.width(), p.height(), SRCCOPY))
+ qErrnoWarning("QWin32PrintEngine::drawPixmap, StretchBlt failed");
+
+ SelectObject(hbitmap_hdc, null_bitmap);
+ DeleteObject(hbitmap);
+ DeleteDC(hbitmap_hdc);
+ }
+ }
+
+ RestoreDC(d->hdc, dc_state);
+}
+
+
+void QWin32PrintEngine::drawTiledPixmap(const QRectF &r, const QPixmap &pm, const QPointF &pos)
+{
+ Q_D(QWin32PrintEngine);
+
+ QAlphaPaintEngine::drawTiledPixmap(r, pm, pos);
+ if (!continueCall())
+ return;
+
+ if (d->complex_xform || !pos.isNull()) {
+ QPaintEngine::drawTiledPixmap(r, pm, pos);
+ } else {
+ int dc_state = SaveDC(d->hdc);
+
+ HDC display_dc = GetDC(0);
+ HBITMAP hbitmap = pm.toWinHBITMAP(QPixmap::NoAlpha);
+ HDC hbitmap_hdc = CreateCompatibleDC(display_dc);
+ HGDIOBJ null_bitmap = SelectObject(hbitmap_hdc, hbitmap);
+
+ ReleaseDC(0, display_dc);
+
+ QRectF trect = d->painterMatrix.mapRect(r);
+ int tx = int(trect.left() * d->stretch_x + d->origin_x);
+ int ty = int(trect.top() * d->stretch_y + d->origin_y);
+
+ int xtiles = int(trect.width() / pm.width()) + 1;
+ int ytiles = int(trect.height() / pm.height()) + 1;
+ int xinc = int(pm.width() * d->stretch_x);
+ int yinc = int(pm.height() * d->stretch_y);
+
+ for (int y = 0; y < ytiles; ++y) {
+ int ity = ty + (yinc * y);
+ int ith = pm.height();
+ if (y == (ytiles - 1)) {
+ ith = int(trect.height() - (pm.height() * y));
+ }
+
+ for (int x = 0; x < xtiles; ++x) {
+ int itx = tx + (xinc * x);
+ int itw = pm.width();
+ if (x == (xtiles - 1)) {
+ itw = int(trect.width() - (pm.width() * x));
+ }
+
+ if (!StretchBlt(d->hdc, itx, ity, int(itw * d->stretch_x), int(ith * d->stretch_y),
+ hbitmap_hdc, 0, 0, itw, ith, SRCCOPY))
+ qErrnoWarning("QWin32PrintEngine::drawPixmap, StretchBlt failed");
+
+ }
+ }
+
+ SelectObject(hbitmap_hdc, null_bitmap);
+ DeleteObject(hbitmap);
+ DeleteDC(hbitmap_hdc);
+
+ RestoreDC(d->hdc, dc_state);
+ }
+}
+
+
+void QWin32PrintEnginePrivate::composeGdiPath(const QPainterPath &path)
+{
+ if (!BeginPath(hdc))
+ qErrnoWarning("QWin32PrintEnginePrivate::drawPath: BeginPath failed");
+
+ // Drawing the subpaths
+ int start = -1;
+ for (int i=0; i<path.elementCount(); ++i) {
+ const QPainterPath::Element &elm = path.elementAt(i);
+ switch (elm.type) {
+ case QPainterPath::MoveToElement:
+ if (start >= 0
+ && path.elementAt(start).x == path.elementAt(i-1).x
+ && path.elementAt(start).y == path.elementAt(i-1).y)
+ CloseFigure(hdc);
+ start = i;
+ MoveToEx(hdc, qRound(elm.x), qRound(elm.y), 0);
+ break;
+ case QPainterPath::LineToElement:
+ LineTo(hdc, qRound(elm.x), qRound(elm.y));
+ break;
+ case QPainterPath::CurveToElement: {
+ POINT pts[3] = {
+ { qRound(elm.x), qRound(elm.y) },
+ { qRound(path.elementAt(i+1).x), qRound(path.elementAt(i+1).y) },
+ { qRound(path.elementAt(i+2).x), qRound(path.elementAt(i+2).y) }
+ };
+ i+=2;
+ PolyBezierTo(hdc, pts, 3);
+ break;
+ }
+ default:
+ qFatal("QWin32PaintEngine::drawPath: Unhandled type: %d", elm.type);
+ }
+ }
+
+ if (start >= 0
+ && path.elementAt(start).x == path.elementAt(path.elementCount()-1).x
+ && path.elementAt(start).y == path.elementAt(path.elementCount()-1).y)
+ CloseFigure(hdc);
+
+ if (!EndPath(hdc))
+ qErrnoWarning("QWin32PaintEngine::drawPath: EndPath failed");
+
+ SetPolyFillMode(hdc, path.fillRule() == Qt::WindingFill ? WINDING : ALTERNATE);
+}
+
+
+void QWin32PrintEnginePrivate::fillPath_dev(const QPainterPath &path, const QColor &color)
+{
+#ifdef QT_DEBUG_DRAW
+ qDebug() << " --- QWin32PrintEnginePrivate::fillPath() bound:" << path.boundingRect() << color;
+#endif
+
+ composeGdiPath(path);
+
+ HBRUSH brush = CreateSolidBrush(RGB(color.red(), color.green(), color.blue()));
+ HGDIOBJ old_brush = SelectObject(hdc, brush);
+ FillPath(hdc);
+ DeleteObject(SelectObject(hdc, old_brush));
+}
+
+void QWin32PrintEnginePrivate::strokePath_dev(const QPainterPath &path, const QColor &color, qreal penWidth)
+{
+ composeGdiPath(path);
+ HPEN pen = CreatePen(PS_SOLID, qRound(penWidth), RGB(color.red(), color.green(), color.blue()));
+ HGDIOBJ old_pen = SelectObject(hdc, pen);
+ StrokePath(hdc);
+ DeleteObject(SelectObject(hdc, old_pen));
+}
+
+
+void QWin32PrintEnginePrivate::fillPath(const QPainterPath &path, const QColor &color)
+{
+ fillPath_dev(path * matrix, color);
+}
+
+void QWin32PrintEnginePrivate::strokePath(const QPainterPath &path, const QColor &color)
+{
+ QPainterPathStroker stroker;
+ if (pen.style() == Qt::CustomDashLine) {
+ stroker.setDashPattern(pen.dashPattern());
+ stroker.setDashOffset(pen.dashOffset());
+ } else {
+ stroker.setDashPattern(pen.style());
+ }
+ stroker.setCapStyle(pen.capStyle());
+ stroker.setJoinStyle(pen.joinStyle());
+ stroker.setMiterLimit(pen.miterLimit());
+
+ QPainterPath stroke;
+ qreal width = pen.widthF();
+ if (pen.style() == Qt::SolidLine && (pen.isCosmetic() || matrix.type() < QTransform::TxScale)) {
+ strokePath_dev(path * matrix, color, width);
+ } else {
+ stroker.setWidth(width);
+ if (pen.isCosmetic()) {
+ stroke = stroker.createStroke(path * matrix);
+ } else {
+ stroke = stroker.createStroke(path) * painterMatrix;
+ QTransform stretch(stretch_x, 0, 0, stretch_y, origin_x, origin_y);
+ stroke = stroke * stretch;
+ }
+
+ if (stroke.isEmpty())
+ return;
+
+ fillPath_dev(stroke, color);
+ }
+}
+
+
+void QWin32PrintEngine::drawPath(const QPainterPath &path)
+{
+#ifdef QT_DEBUG_DRAW
+ qDebug() << " - QWin32PrintEngine::drawPath(), bounds: " << path.boundingRect();
+#endif
+
+ Q_D(QWin32PrintEngine);
+
+ QAlphaPaintEngine::drawPath(path);
+ if (!continueCall())
+ return;
+
+ if (d->has_brush)
+ d->fillPath(path, d->brush_color);
+
+ if (d->has_pen)
+ d->strokePath(path, d->pen.color());
+}
+
+
+void QWin32PrintEngine::drawPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode)
+{
+#ifdef QT_DEBUG_DRAW
+ qDebug() << " - QWin32PrintEngine::drawPolygon(), pointCount: " << pointCount;
+#endif
+
+ QAlphaPaintEngine::drawPolygon(points, pointCount, mode);
+ if (!continueCall())
+ return;
+
+ Q_ASSERT(pointCount > 1);
+
+ QPainterPath path(points[0]);
+
+ for (int i=1; i<pointCount; ++i) {
+ path.lineTo(points[i]);
+ }
+
+ Q_D(QWin32PrintEngine);
+
+ bool has_brush = d->has_brush;
+
+ if (mode == PolylineMode)
+ d->has_brush = false; // No brush for polylines
+ else
+ path.closeSubpath(); // polygons are should always be closed.
+
+ drawPath(path);
+ d->has_brush = has_brush;
+}
+
+void QWin32PrintEnginePrivate::queryDefault()
+{
+ /* Read the default printer name, driver and port with the intuitive function
+ * Strings "windows" and "device" are specified in the MSDN under EnumPrinters()
+ */
+ QString noPrinters(QLatin1String("qt_no_printers"));
+ QString output;
+ QT_WA({
+ ushort buffer[256];
+ GetProfileStringW(L"windows", L"device",
+ reinterpret_cast<const wchar_t *>(noPrinters.utf16()),
+ reinterpret_cast<wchar_t *>(buffer), 256);
+ output = QString::fromUtf16(buffer);
+ if (output.isEmpty() || output == noPrinters) // no printers
+ return;
+ }, {
+ char buffer[256];
+ GetProfileStringA("windows", "device", noPrinters.toLatin1(), buffer, 256);
+ output = QString::fromLocal8Bit(buffer);
+ if (output.isEmpty() || output == noPrinters) // no printers
+ return;
+ });
+ QStringList info = output.split(QLatin1Char(','));
+ if (info.size() > 0) {
+ if (name.isEmpty())
+ name = info.at(0);
+ if (program.isEmpty())
+ program = info.at(1);
+ if (port.isEmpty())
+ port = info.at(2);
+ }
+}
+
+QWin32PrintEnginePrivate::~QWin32PrintEnginePrivate()
+{
+ if (hdc)
+ release();
+}
+
+void QWin32PrintEnginePrivate::initialize()
+{
+ if (hdc)
+ release();
+ Q_ASSERT(!hPrinter);
+ Q_ASSERT(!hdc);
+ Q_ASSERT(!devMode);
+ Q_ASSERT(!pInfo);
+
+ if (name.isEmpty())
+ return;
+
+ txop = QTransform::TxNone;
+
+ bool ok;
+ QT_WA( {
+ ok = OpenPrinterW((LPWSTR)name.utf16(), (LPHANDLE)&hPrinter, 0);
+ }, {
+ ok = OpenPrinterA((LPSTR)name.toLatin1().data(), (LPHANDLE)&hPrinter, 0);
+ } );
+
+ if (!ok) {
+ qErrnoWarning("QWin32PrintEngine::initialize: OpenPrinter failed");
+ return;
+ }
+
+ // Fetch the PRINTER_INFO_2 with DEVMODE data containing the
+ // printer settings.
+ DWORD infoSize, numBytes;
+ ok = true;
+ QT_WA( {
+ GetPrinterW(hPrinter, 2, NULL, 0, &infoSize);
+ hMem = GlobalAlloc(GHND, infoSize);
+ pInfo = GlobalLock(hMem);
+ if (!GetPrinterW(hPrinter, 2, (LPBYTE)pInfo, infoSize, &numBytes)) {
+ ok = false;
+ }
+ }, {
+ GetPrinterA(hPrinter, 2, NULL, 0, &infoSize);
+ hMem = GlobalAlloc(GHND, infoSize);
+ pInfo = GlobalLock(hMem);
+ if (!GetPrinterA(hPrinter, 2, (LPBYTE)pInfo, infoSize, &numBytes)) {
+ ok = false;
+ }
+ });
+
+ if (!ok) {
+ qErrnoWarning("QWin32PrintEngine::initialize: GetPrinter failed");
+ GlobalUnlock(pInfo);
+ GlobalFree(hMem);
+ ClosePrinter(hPrinter);
+ pInfo = 0;
+ hMem = 0;
+ hPrinter = 0;
+ return;
+ }
+
+ QT_WA( {
+ devMode = pInfoW()->pDevMode;
+ }, {
+ devMode = pInfoA()->pDevMode;
+ } );
+
+ QT_WA( {
+ hdc = CreateDC(reinterpret_cast<const wchar_t *>(program.utf16()),
+ reinterpret_cast<const wchar_t *>(name.utf16()), 0, devModeW());
+ }, {
+ hdc = CreateDCA(program.toLatin1(), name.toLatin1(), 0, devModeA());
+ } );
+
+ Q_ASSERT(hPrinter);
+ Q_ASSERT(pInfo);
+
+ if (devMode) {
+ QT_WA( {
+ num_copies = devModeW()->dmCopies;
+ }, {
+ num_copies = devModeA()->dmCopies;
+ } );
+ }
+
+ initHDC();
+
+#ifdef QT_DEBUG_DRAW
+ qDebug() << "QWin32PrintEngine::initialize()" << endl
+ << " - paperRect" << devPaperRect << endl
+ << " - pageRect" << devPageRect << endl
+ << " - stretch_x" << stretch_x << endl
+ << " - stretch_y" << stretch_y << endl
+ << " - origin_x" << origin_x << endl
+ << " - origin_y" << origin_y << endl;
+#endif
+}
+
+void QWin32PrintEnginePrivate::initHDC()
+{
+ Q_ASSERT(hdc);
+
+ HDC display_dc = GetDC(0);
+ dpi_x = GetDeviceCaps(hdc, LOGPIXELSX);
+ dpi_y = GetDeviceCaps(hdc, LOGPIXELSY);
+ dpi_display = GetDeviceCaps(display_dc, LOGPIXELSY);
+ ReleaseDC(0, display_dc);
+ if (dpi_display == 0) {
+ qWarning("QWin32PrintEngine::metric: GetDeviceCaps() failed, "
+ "might be a driver problem");
+ dpi_display = 96; // Reasonable default
+ }
+
+ switch(mode) {
+ case QPrinter::ScreenResolution:
+ resolution = dpi_display;
+ stretch_x = dpi_x / double(dpi_display);
+ stretch_y = dpi_y / double(dpi_display);
+ break;
+ case QPrinter::PrinterResolution:
+ case QPrinter::HighResolution:
+ resolution = dpi_y;
+ stretch_x = 1;
+ stretch_y = 1;
+ break;
+ default:
+ break;
+ }
+
+ initDevRects();
+}
+
+void QWin32PrintEnginePrivate::initDevRects()
+{
+ devPaperRect = QRect(0, 0,
+ GetDeviceCaps(hdc, PHYSICALWIDTH),
+ GetDeviceCaps(hdc, PHYSICALHEIGHT));
+ devPhysicalPageRect = QRect(GetDeviceCaps(hdc, PHYSICALOFFSETX),
+ GetDeviceCaps(hdc, PHYSICALOFFSETY),
+ GetDeviceCaps(hdc, HORZRES),
+ GetDeviceCaps(hdc, VERTRES));
+ if (!pageMarginsSet)
+ devPageRect = devPhysicalPageRect;
+ else
+ devPageRect = devPaperRect.adjusted(qRound(mmToInches(previousDialogMargins.left() / 100.0) * dpi_x),
+ qRound(mmToInches(previousDialogMargins.top() / 100.0) * dpi_y),
+ -qRound(mmToInches(previousDialogMargins.width() / 100.0) * dpi_x),
+ -qRound(mmToInches(previousDialogMargins.height() / 100.0) * dpi_y));
+ updateOrigin();
+}
+
+void QWin32PrintEnginePrivate::setPageMargins(int marginLeft, int marginTop, int marginRight, int marginBottom)
+{
+ pageMarginsSet = true;
+ previousDialogMargins = QRect(marginLeft, marginTop, marginRight, marginBottom);
+
+ devPageRect = devPaperRect.adjusted(qRound(mmToInches(marginLeft / 100.0) * dpi_x),
+ qRound(mmToInches(marginTop / 100.0) * dpi_y),
+ - qRound(mmToInches(marginRight / 100.0) * dpi_x),
+ - qRound(mmToInches(marginBottom / 100.0) * dpi_y));
+ updateOrigin();
+}
+
+QRect QWin32PrintEnginePrivate::getPageMargins() const
+{
+ if (pageMarginsSet)
+ return previousDialogMargins;
+ else
+ return QRect(qRound(inchesToMM(devPhysicalPageRect.left()) * 100.0 / dpi_x),
+ qRound(inchesToMM(devPhysicalPageRect.top()) * 100.0 / dpi_y),
+ qRound(inchesToMM(devPaperRect.right() - devPhysicalPageRect.right()) * 100.0 / dpi_x),
+ qRound(inchesToMM(devPaperRect.bottom() - devPhysicalPageRect.bottom()) * 100.0 / dpi_y));
+}
+
+void QWin32PrintEnginePrivate::release()
+{
+ if (hdc == 0)
+ return;
+
+ if (globalDevMode) { // Devmode comes from print dialog
+ GlobalUnlock(globalDevMode);
+ } else { // Devmode comes from initialize...
+ // devMode is a part of the same memory block as pInfo so one free is enough...
+ GlobalUnlock(hMem);
+ GlobalFree(hMem);
+ }
+ if (hPrinter)
+ ClosePrinter(hPrinter);
+ DeleteDC(hdc);
+
+ hdc = 0;
+ hPrinter = 0;
+ pInfo = 0;
+ hMem = 0;
+ devMode = 0;
+}
+
+QList<QVariant> QWin32PrintEnginePrivate::queryResolutions() const
+{
+ // Read the supported resolutions of the printer.
+ DWORD numRes;
+ LONG *enumRes;
+ DWORD errRes;
+ QList<QVariant> list;
+
+ QT_WA({
+ numRes = DeviceCapabilities(reinterpret_cast<const wchar_t *>(name.utf16()),
+ reinterpret_cast<const wchar_t *>(port.utf16()),
+ DC_ENUMRESOLUTIONS, 0, 0);
+ if (numRes == (DWORD)-1)
+ return list;
+ enumRes = (LONG*)malloc(numRes * 2 * sizeof(LONG));
+ errRes = DeviceCapabilities(reinterpret_cast<const wchar_t *>(name.utf16()),
+ reinterpret_cast<const wchar_t *>(port.utf16()),
+ DC_ENUMRESOLUTIONS, (LPWSTR)enumRes, 0);
+ }, {
+ numRes = DeviceCapabilitiesA(name.toLocal8Bit(), port.toLocal8Bit(), DC_ENUMRESOLUTIONS, 0, 0);
+ if (numRes == (DWORD)-1)
+ return list;
+ enumRes = (LONG*)malloc(numRes * 2 * sizeof(LONG));
+ errRes = DeviceCapabilitiesA(name.toLocal8Bit(), port.toLocal8Bit(), DC_ENUMRESOLUTIONS, (LPSTR)enumRes, 0);
+ });
+
+ if (errRes == (DWORD)-1) {
+ qErrnoWarning("QWin32PrintEngine::queryResolutions: DeviceCapabilities failed");
+ return list;
+ }
+
+ for (uint i=0; i<numRes; ++i)
+ list.append(int(enumRes[i*2]));
+ return list;
+}
+
+void QWin32PrintEnginePrivate::doReinit()
+{
+ if (state == QPrinter::Active) {
+ reinit = true;
+ } else {
+ resetDC();
+ initDevRects();
+ reinit = false;
+ }
+}
+
+void QWin32PrintEnginePrivate::updateOrigin()
+{
+ if (fullPage) {
+ // subtract physical margins to make (0,0) absolute top corner of paper
+ // then add user defined margins
+ origin_x = -devPhysicalPageRect.x();
+ origin_y = -devPhysicalPageRect.y();
+ if (pageMarginsSet) {
+ origin_x += devPageRect.left();
+ origin_y += devPageRect.top();
+ }
+ } else {
+ origin_x = 0;
+ origin_y = 0;
+ if (pageMarginsSet) {
+ origin_x = devPageRect.left() - devPhysicalPageRect.x();
+ origin_y = devPageRect.top() - devPhysicalPageRect.y();
+ }
+ }
+}
+
+void QWin32PrintEngine::setProperty(PrintEnginePropertyKey key, const QVariant &value)
+{
+ Q_D(QWin32PrintEngine);
+ switch (key) {
+ case PPK_CollateCopies:
+ {
+ if (!d->devMode)
+ break;
+ short collate = value.toBool() ? DMCOLLATE_TRUE : DMCOLLATE_FALSE;
+ QT_WA( { d->devModeW()->dmCollate = collate; },
+ { d->devModeA()->dmCollate = collate; } );
+ d->doReinit();
+ }
+ break;
+
+ case PPK_ColorMode:
+ {
+ if (!d->devMode)
+ break;
+ int cm = value.toInt() == QPrinter::Color ? DMCOLOR_COLOR : DMCOLOR_MONOCHROME;
+ QT_WA( { d->devModeW()->dmColor = cm; }, { d->devModeA()->dmColor = cm; } );
+ d->doReinit();
+ }
+ break;
+
+ case PPK_Creator:
+
+ break;
+
+ case PPK_DocumentName:
+ if (isActive()) {
+ qWarning("QWin32PrintEngine: Cannot change document name while printing is active");
+ return;
+ }
+ d->docName = value.toString();
+ break;
+
+ case PPK_FullPage:
+ d->fullPage = value.toBool();
+ d->updateOrigin();
+ break;
+
+ case PPK_NumberOfCopies:
+ if (!d->devMode)
+ break;
+ d->num_copies = value.toInt();
+ QT_WA( { d->devModeW()->dmCopies = d->num_copies; },
+ { d->devModeA()->dmCopies = d->num_copies; });
+ d->doReinit();
+ break;
+
+ case PPK_Orientation:
+ {
+ if (!d->devMode)
+ break;
+ int orientation = value.toInt() == QPrinter::Landscape ? DMORIENT_LANDSCAPE : DMORIENT_PORTRAIT;
+ int old_orientation;
+ QT_WA( {
+ old_orientation = d->devModeW()->dmOrientation;
+ d->devModeW()->dmOrientation = orientation;
+ }, {
+ old_orientation = d->devModeA()->dmOrientation;
+ d->devModeA()->dmOrientation = orientation;
+ } );
+ if (d->has_custom_paper_size && old_orientation != orientation)
+ d->paper_size = QSizeF(d->paper_size.height(), d->paper_size.width());
+ d->doReinit();
+ }
+ break;
+
+ case PPK_OutputFileName:
+ if (isActive()) {
+ qWarning("QWin32PrintEngine: Cannot change filename while printing");
+ } else {
+ d->fileName = value.toString();
+ d->printToFile = !value.toString().isEmpty();
+ }
+ break;
+
+ case PPK_PaperSize:
+ if (!d->devMode)
+ break;
+ QT_WA( {
+ d->devModeW()->dmPaperSize = mapPaperSizeDevmode(QPrinter::PaperSize(value.toInt()));
+ }, {
+ d->devModeA()->dmPaperSize = mapPaperSizeDevmode(QPrinter::PaperSize(value.toInt()));
+ } );
+ d->has_custom_paper_size = (QPrinter::PaperSize(value.toInt()) == QPrinter::Custom);
+ d->doReinit();
+ break;
+
+ case PPK_PaperSource:
+ {
+ if (!d->devMode)
+ break;
+ int dmMapped = DMBIN_AUTO;
+
+ QList<QVariant> v = property(PPK_PaperSources).toList();
+ if (v.contains(value))
+ dmMapped = mapPaperSourceDevmode(QPrinter::PaperSource(value.toInt()));
+
+ QT_WA( {
+ d->devModeW()->dmDefaultSource = dmMapped;
+ }, {
+ d->devModeA()->dmDefaultSource = dmMapped;
+ } );
+ d->doReinit();
+ }
+ break;
+
+ case PPK_PrinterName:
+ d->name = value.toString();
+ if(d->name.isEmpty())
+ d->queryDefault();
+ d->initialize();
+ break;
+
+ case PPK_Resolution:
+ {
+ d->resolution = value.toInt();
+
+ d->stretch_x = d->dpi_x / double(d->resolution);
+ d->stretch_y = d->dpi_y / double(d->resolution);
+ }
+ break;
+
+ case PPK_SelectionOption:
+
+ break;
+
+ case PPK_SupportedResolutions:
+
+ break;
+
+
+ case PPK_WindowsPageSize:
+ if (!d->devMode)
+ break;
+ d->has_custom_paper_size = false;
+ QT_WA( {
+ d->devModeW()->dmPaperSize = value.toInt();
+ }, {
+ d->devModeA()->dmPaperSize = value.toInt();
+ } );
+ d->doReinit();
+ break;
+
+ case PPK_CustomPaperSize:
+ {
+ d->has_custom_paper_size = true;
+ d->paper_size = value.toSizeF();
+ if (!d->devMode)
+ break;
+ int orientation;
+ QT_WA( {
+ orientation = d->devModeW()->dmOrientation;
+ DWORD needed = 0;
+ DWORD returned = 0;
+ if (!EnumForms(d->hPrinter, 1, 0, 0, &needed, &returned)) {
+ BYTE *forms = (BYTE *) malloc(needed);
+ if (EnumForms(d->hPrinter, 1, forms, needed, &needed, &returned)) {
+ for (DWORD i=0; i< returned; ++i) {
+ FORM_INFO_1 *formArray = reinterpret_cast<FORM_INFO_1 *>(forms);
+ // the form sizes are specified in 1000th of a mm,
+ // convert the size to Points
+ QSizeF size((formArray[i].Size.cx * 72/25.4)/1000.0,
+ (formArray[i].Size.cy * 72/25.4)/1000.0);
+ if (qAbs(d->paper_size.width() - size.width()) <= 2
+ && qAbs(d->paper_size.height() - size.height()) <= 2)
+ {
+ d->devModeW()->dmPaperSize = i+1;
+ break;
+ }
+ }
+ }
+ free(forms);
+ }
+ }, {
+ orientation = d->devModeA()->dmOrientation;
+ } );
+ if (orientation != DMORIENT_PORTRAIT)
+ d->paper_size = QSizeF(d->paper_size.height(), d->paper_size.width());
+ break;
+ }
+
+ case PPK_PageMargins:
+ {
+ QList<QVariant> margins(value.toList());
+ Q_ASSERT(margins.size() == 4);
+ int left, top, right, bottom;
+ // specified in 1/100 mm
+ left = (margins.at(0).toDouble()*25.4/72.0) * 100;
+ top = (margins.at(1).toDouble()*25.4/72.0) * 100;
+ right = (margins.at(2).toDouble()*25.4/72.0) * 100;
+ bottom = (margins.at(3).toDouble()*25.4/72.0) * 100;
+ d->setPageMargins(left, top, right, bottom);
+ break;
+ }
+ default:
+ // Do nothing
+ break;
+ }
+}
+
+QVariant QWin32PrintEngine::property(PrintEnginePropertyKey key) const
+{
+ Q_D(const QWin32PrintEngine);
+ QVariant value;
+ switch (key) {
+
+ case PPK_CollateCopies:
+ value = false;
+ break;
+
+ case PPK_ColorMode:
+ {
+ if (!d->devMode) {
+ value = QPrinter::Color;
+ } else {
+ int mode;
+ QT_WA( {
+ mode = d->devModeW()->dmColor;
+ }, {
+ mode = d->devModeA()->dmColor;
+ } );
+ value = mode == DMCOLOR_COLOR ? QPrinter::Color : QPrinter::GrayScale;
+ }
+ }
+ break;
+
+ case PPK_DocumentName:
+ value = d->docName;
+ break;
+
+ case PPK_FullPage:
+ value = d->fullPage;
+ break;
+
+ case PPK_NumberOfCopies:
+ value = 1;
+ break;
+
+ case PPK_Orientation:
+ {
+ if (!d->devMode) {
+ value = QPrinter::Portrait;
+ } else {
+ int o;
+ QT_WA( { o = d->devModeW()->dmOrientation; }, { o = d->devModeA()->dmOrientation; } );
+ value = o == DMORIENT_LANDSCAPE ? QPrinter::Landscape : QPrinter::Portrait;
+ }
+ }
+ break;
+
+ case PPK_OutputFileName:
+ value = d->fileName;
+ break;
+
+ case PPK_PageRect:
+ if (d->has_custom_paper_size) {
+ QRect rect(0, 0,
+ qRound(d->paper_size.width() * d->resolution / 72.0),
+ qRound(d->paper_size.height() * d->resolution / 72.0));
+ if (d->pageMarginsSet) {
+ rect = rect.adjusted(qRound(mmToInches(d->previousDialogMargins.left()/100.0) * d->resolution),
+ qRound(mmToInches(d->previousDialogMargins.top()/100.0) * d->resolution),
+ -qRound(mmToInches(d->previousDialogMargins.width()/100.0) * d->resolution),
+ -qRound(mmToInches(d->previousDialogMargins.height()/100.0) * d->resolution));
+ }
+ value = rect;
+ } else {
+ value = QTransform(1/d->stretch_x, 0, 0, 1/d->stretch_y, 0, 0)
+ .mapRect(d->fullPage ? d->devPaperRect : d->devPageRect);
+ }
+ break;
+
+ case PPK_PaperSize:
+ if (d->has_custom_paper_size) {
+ value = QPrinter::Custom;
+ } else {
+ if (!d->devMode) {
+ value = QPrinter::A4;
+ } else {
+ QT_WA( {
+ value = mapDevmodePaperSize(d->devModeW()->dmPaperSize);
+ }, {
+ value = mapDevmodePaperSize(d->devModeA()->dmPaperSize);
+ } );
+ }
+ }
+ break;
+
+ case PPK_PaperRect:
+ if (d->has_custom_paper_size) {
+ value = QRect(0, 0,
+ qRound(d->paper_size.width() * d->resolution / 72.0),
+ qRound(d->paper_size.height() * d->resolution / 72.0));
+ } else {
+ value = QTransform(1/d->stretch_x, 0, 0, 1/d->stretch_y, 0, 0).mapRect(d->devPaperRect);
+ }
+ break;
+
+ case PPK_PaperSource:
+ if (!d->devMode) {
+ value = QPrinter::Auto;
+ } else {
+ QT_WA( {
+ value = mapDevmodePaperSource(d->devModeW()->dmDefaultSource);
+ }, {
+ value = mapDevmodePaperSource(d->devModeA()->dmDefaultSource);
+ } );
+ }
+ break;
+
+ case PPK_PrinterName:
+ value = d->name;
+ break;
+
+ case PPK_Resolution:
+ if (d->resolution || !d->name.isEmpty())
+ value = d->resolution;
+ break;
+
+ case PPK_SupportedResolutions:
+ value = d->queryResolutions();
+ break;
+
+ case PPK_WindowsPageSize:
+ if (!d->devMode) {
+ value = -1;
+ } else {
+ QT_WA( {
+ value = d->devModeW()->dmPaperSize;
+ }, {
+ value = d->devModeA()->dmPaperSize;
+ } );
+ }
+ break;
+
+ case PPK_PaperSources:
+ {
+ int available, count;
+ WORD *data;
+
+ QT_WA({
+ available = DeviceCapabilitiesW((const WCHAR *)d->name.utf16(), (const WCHAR *)d->port.utf16(), DC_BINS, 0,
+ d->devModeW());
+ }, {
+ available = DeviceCapabilitiesA(d->name.toLatin1(), d->port.toLatin1(), DC_BINS, 0,
+ d->devModeA());
+ });
+
+ if (available <= 0)
+ break;
+ data = (WORD *) malloc(available * sizeof(WORD));
+
+ QT_WA({
+ count = DeviceCapabilitiesW((const WCHAR *)d->name.utf16(), (const WCHAR *)d->port.utf16(), DC_BINS, (WCHAR *)data,
+ d->devModeW());
+ }, {
+ count = DeviceCapabilitiesA(d->name.toLatin1(), d->port.toLatin1(), DC_BINS,
+ (char *) data, d->devModeA());
+ });
+
+ QList<QVariant> out;
+ for (int i=0; i<count; ++i) {
+ QPrinter::PaperSource src = mapDevmodePaperSource(data[i]);
+ if (src != -1)
+ out << (int) src;
+ }
+ value = out;
+ free(data);
+ }
+ break;
+
+ case PPK_CustomPaperSize:
+ value = d->paper_size;
+ break;
+
+ case PPK_PageMargins:
+ {
+ QList<QVariant> margins;
+ QRect pageMargins(d->getPageMargins());
+
+ // specified in 1/100 mm
+ margins << (mmToInches(pageMargins.left()/100.0) * 72)
+ << (mmToInches(pageMargins.top()/100.0) * 72)
+ << (mmToInches(pageMargins.width()/100.0) * 72)
+ << (mmToInches(pageMargins.height()/100.0) * 72);
+ value = margins;
+ break;
+ }
+ default:
+ // Do nothing
+ break;
+ }
+ return value;
+}
+
+QPrinter::PrinterState QWin32PrintEngine::printerState() const
+{
+ return d_func()->state;
+}
+
+HDC QWin32PrintEngine::getDC() const
+{
+ return d_func()->hdc;
+}
+
+void QWin32PrintEngine::releaseDC(HDC) const
+{
+
+}
+
+HGLOBAL *QWin32PrintEnginePrivate::createDevNames()
+{
+ QT_WA( {
+ int size = sizeof(DEVNAMES)
+ + program.length() * 2 + 2
+ + name.length() * 2 + 2
+ + port.length() * 2 + 2;
+ HGLOBAL *hGlobal = (HGLOBAL *) GlobalAlloc(GMEM_MOVEABLE, size);
+ DEVNAMES *dn = (DEVNAMES*) GlobalLock(hGlobal);
+
+ dn->wDriverOffset = sizeof(DEVNAMES) / sizeof(TCHAR);
+ dn->wDeviceOffset = dn->wDriverOffset + program.length() + 1;
+ dn->wOutputOffset = dn->wDeviceOffset + name.length() + 1;
+
+ memcpy((ushort*)dn + dn->wDriverOffset, program.utf16(), program.length() * 2 + 2);
+ memcpy((ushort*)dn + dn->wDeviceOffset, name.utf16(), name.length() * 2 + 2);
+ memcpy((ushort*)dn + dn->wOutputOffset, port.utf16(), port.length() * 2 + 2);
+ dn->wDefault = 0;
+
+ GlobalUnlock(hGlobal);
+
+// printf("QPrintDialogWinPrivate::createDevNames()\n"
+// " -> wDriverOffset: %d\n"
+// " -> wDeviceOffset: %d\n"
+// " -> wOutputOffset: %d\n",
+// dn->wDriverOffset,
+// dn->wDeviceOffset,
+// dn->wOutputOffset);
+
+// printf("QPrintDialogWinPrivate::createDevNames(): %s, %s, %s\n",
+// QString::fromUtf16((ushort*)(dn) + dn->wDriverOffset).latin1(),
+// QString::fromUtf16((ushort*)(dn) + dn->wDeviceOffset).latin1(),
+// QString::fromUtf16((ushort*)(dn) + dn->wOutputOffset).latin1());
+
+ return hGlobal;
+ }, {
+ int size = sizeof(DEVNAMES)
+ + program.length() + 2
+ + name.length() + 2
+ + port.length() + 2;
+ HGLOBAL *hGlobal = (HGLOBAL *) GlobalAlloc(GMEM_MOVEABLE, size);
+ DEVNAMES *dn = (DEVNAMES*) GlobalLock(hGlobal);
+
+ dn->wDriverOffset = sizeof(DEVNAMES);
+ dn->wDeviceOffset = dn->wDriverOffset + program.length() + 1;
+ dn->wOutputOffset = dn->wDeviceOffset + name.length() + 1;
+
+ memcpy((char*)dn + dn->wDriverOffset, program.toLatin1(), program.length() + 2);
+ memcpy((char*)dn + dn->wDeviceOffset, name.toLatin1(), name.length() + 2);
+ memcpy((char*)dn + dn->wOutputOffset, port.toLatin1(), port.length() + 2);
+ dn->wDefault = 0;
+
+ GlobalUnlock(hGlobal);
+ return hGlobal;
+ } );
+}
+
+void QWin32PrintEnginePrivate::readDevnames(HGLOBAL globalDevnames)
+{
+ if (globalDevnames) {
+ QT_WA( {
+ DEVNAMES *dn = (DEVNAMES*) GlobalLock(globalDevnames);
+ name = QString::fromUtf16((ushort*)(dn) + dn->wDeviceOffset);
+ port = QString::fromUtf16((ushort*)(dn) + dn->wOutputOffset);
+ program = QString::fromUtf16((ushort*)(dn) + dn->wDriverOffset);
+ GlobalUnlock(globalDevnames);
+ }, {
+ DEVNAMES *dn = (DEVNAMES*) GlobalLock(globalDevnames);
+ name = QString::fromLatin1((char*)(dn) + dn->wDeviceOffset);
+ port = QString::fromLatin1((char*)(dn) + dn->wOutputOffset);
+ program = QString::fromLatin1((char*)(dn) + dn->wDriverOffset);
+ GlobalUnlock(globalDevnames);
+ } );
+ }
+}
+
+void QWin32PrintEnginePrivate::readDevmode(HGLOBAL globalDevmode)
+{
+ if (globalDevmode) {
+ QT_WA( {
+ DEVMODE *dm = (DEVMODE*) GlobalLock(globalDevmode);
+ release();
+ globalDevMode = globalDevmode;
+ devMode = dm;
+ hdc = CreateDC(reinterpret_cast<const wchar_t *>(program.utf16()),
+ reinterpret_cast<const wchar_t *>(name.utf16()), 0, dm);
+
+ num_copies = devModeW()->dmCopies;
+ if (!OpenPrinterW((LPWSTR)name.utf16(), (LPHANDLE)&hPrinter, 0))
+ qWarning("QPrinter: OpenPrinter() failed after reading DEVMODE.");
+ }, {
+ DEVMODEA *dm = (DEVMODEA*) GlobalLock(globalDevmode);
+ release();
+ globalDevMode = globalDevmode;
+ devMode = dm;
+ hdc = CreateDCA(program.toLatin1(), name.toLatin1(), 0, dm);
+
+ num_copies = devModeA()->dmCopies;
+ if (!OpenPrinterA((LPSTR)name.toLatin1().data(), (LPHANDLE)&hPrinter, 0))
+ qWarning("QPrinter: OpenPrinter() failed after reading DEVMODE.");
+ } );
+ }
+
+ if (hdc)
+ initHDC();
+}
+
+static void draw_text_item_win(const QPointF &_pos, const QTextItemInt &ti, HDC hdc,
+ bool convertToText, const QTransform &xform, const QPointF &topLeft)
+{
+
+ // Make sure we translate for systems that can't handle world transforms
+ QPointF pos(QT_WA_INLINE(_pos, _pos + QPointF(xform.dx(), xform.dy())));
+ QFontEngine *fe = ti.fontEngine;
+ QPointF baseline_pos = xform.inverted().map(xform.map(pos) - topLeft);
+
+ SetTextAlign(hdc, TA_BASELINE);
+ SetBkMode(hdc, TRANSPARENT);
+
+ bool has_kerning = ti.f && ti.f->kerning();
+ QFontEngineWin *winfe = (fe->type() == QFontEngine::Win) ? static_cast<QFontEngineWin *>(fe) : 0;
+
+ HFONT hfont;
+ bool ttf = false;
+ bool useTextOutA = false;
+
+ if (winfe) {
+ hfont = winfe->hfont;
+ ttf = winfe->ttf;
+ useTextOutA = winfe->useTextOutA;
+ } else {
+ hfont = (HFONT)GetStockObject(ANSI_VAR_FONT);
+ }
+
+ HGDIOBJ old_font = SelectObject(hdc, hfont);
+ unsigned int options = (ttf && !convertToText) ? ETO_GLYPH_INDEX : 0;
+ wchar_t *convertedGlyphs = (wchar_t *)ti.chars;
+ QGlyphLayout glyphs = ti.glyphs;
+
+ if (!(ti.flags & QTextItem::RightToLeft) && useTextOutA) {
+ qreal x = pos.x();
+ qreal y = pos.y();
+
+ // hack to get symbol fonts working on Win95. See also QFontEngine constructor
+ // can only happen if !ttf
+ for(int i = 0; i < glyphs.numGlyphs; i++) {
+ QString str(QChar(glyphs.glyphs[i]));
+ QT_WA({
+ TextOutW(hdc, qRound(x + glyphs.offsets[i].x.toReal()),
+ qRound(y + glyphs.offsets[i].y.toReal()),
+ (LPWSTR)str.utf16(), str.length());
+ } , {
+ QByteArray cstr = str.toLocal8Bit();
+ TextOutA(hdc, qRound(x + glyphs.offsets[i].x.toReal()),
+ qRound(y + glyphs.offsets[i].y.toReal()),
+ cstr.data(), cstr.length());
+ });
+ x += glyphs.effectiveAdvance(i).toReal();
+ }
+ } else {
+ bool fast = !has_kerning && !(ti.flags & QTextItem::RightToLeft);
+ for(int i = 0; fast && i < glyphs.numGlyphs; i++) {
+ if (glyphs.offsets[i].x != 0 || glyphs.offsets[i].y != 0 || glyphs.justifications[i].space_18d6 != 0
+ || glyphs.attributes[i].dontPrint) {
+ fast = false;
+ break;
+ }
+ }
+
+#if !defined(Q_OS_WINCE)
+ // Scale, rotate and translate here. This is only valid for systems > Windows Me.
+ // We should never get here on Windows Me or lower if the transformation specifies
+ // scaling or rotation.
+ QT_WA({
+ XFORM win_xform;
+ win_xform.eM11 = xform.m11();
+ win_xform.eM12 = xform.m12();
+ win_xform.eM21 = xform.m21();
+ win_xform.eM22 = xform.m22();
+ win_xform.eDx = xform.dx();
+ win_xform.eDy = xform.dy();
+ SetGraphicsMode(hdc, GM_ADVANCED);
+ SetWorldTransform(hdc, &win_xform);
+ }, {
+ // nothing
+ });
+#endif
+
+ if (fast) {
+ // fast path
+ QVarLengthArray<wchar_t> g(glyphs.numGlyphs);
+ for (int i = 0; i < glyphs.numGlyphs; ++i)
+ g[i] = glyphs.glyphs[i];
+ ExtTextOutW(hdc,
+ qRound(baseline_pos.x() + glyphs.offsets[0].x.toReal()),
+ qRound(baseline_pos.y() + glyphs.offsets[0].y.toReal()),
+ options, 0, convertToText ? convertedGlyphs : g.data(), glyphs.numGlyphs, 0);
+ } else {
+ QVarLengthArray<QFixedPoint> positions;
+ QVarLengthArray<glyph_t> _glyphs;
+
+ QTransform matrix;
+ matrix.translate(baseline_pos.x(), baseline_pos.y());
+ ti.fontEngine->getGlyphPositions(ti.glyphs, matrix, ti.flags,
+ _glyphs, positions);
+ if (_glyphs.size() == 0) {
+ SelectObject(hdc, old_font);
+ return;
+ }
+
+ convertToText = convertToText && glyphs.numGlyphs == _glyphs.size();
+
+ bool outputEntireItem = (QSysInfo::WindowsVersion & QSysInfo::WV_NT_based)
+ && QSysInfo::WindowsVersion != QSysInfo::WV_NT
+ && _glyphs.size() > 0;
+
+ if (outputEntireItem) {
+ options |= ETO_PDY;
+ QVarLengthArray<INT> glyphDistances(_glyphs.size() * 2);
+ QVarLengthArray<wchar_t> g(_glyphs.size());
+ for (int i=0; i<_glyphs.size() - 1; ++i) {
+ glyphDistances[i * 2] = qRound(positions[i + 1].x) - qRound(positions[i].x);
+ glyphDistances[i * 2 + 1] = qRound(positions[i + 1].y) - qRound(positions[i].y);
+ g[i] = _glyphs[i];
+ }
+ glyphDistances[(_glyphs.size() - 1) * 2] = 0;
+ glyphDistances[(_glyphs.size() - 1) * 2 + 1] = 0;
+ g[_glyphs.size() - 1] = _glyphs[_glyphs.size() - 1];
+ ExtTextOutW(hdc, qRound(positions[0].x), qRound(positions[0].y), options, 0,
+ convertToText ? convertedGlyphs : g.data(), _glyphs.size(),
+ glyphDistances.data());
+ } else {
+ int i = 0;
+ while(i < _glyphs.size()) {
+ wchar_t g = _glyphs[i];
+
+ ExtTextOutW(hdc, qRound(positions[i].x),
+ qRound(positions[i].y), options, 0,
+ convertToText ? convertedGlyphs + i : &g, 1, 0);
+ ++i;
+ }
+ }
+ }
+
+#if !defined(Q_OS_WINCE)
+ QT_WA({
+ XFORM win_xform;
+ win_xform.eM11 = win_xform.eM22 = 1.0;
+ win_xform.eM12 = win_xform.eM21 = win_xform.eDx = win_xform.eDy = 0.0;
+ SetWorldTransform(hdc, &win_xform);
+ }, {
+ // nothing
+ });
+#endif
+ }
+ SelectObject(hdc, old_font);
+}
+
+
+void QWin32PrintEnginePrivate::updateCustomPaperSize()
+{
+ QT_WA( {
+ uint paperSize = devModeW()->dmPaperSize;
+ if (paperSize > 0 && mapDevmodePaperSize(paperSize) == QPrinter::Custom) {
+ has_custom_paper_size = true;
+ DWORD needed = 0;
+ DWORD returned = 0;
+ if (!EnumForms(hPrinter, 1, 0, 0, &needed, &returned)) {
+ BYTE *forms = (BYTE *) malloc(needed);
+ if (EnumForms(hPrinter, 1, forms, needed, &needed, &returned)) {
+ if (paperSize <= returned) {
+ FORM_INFO_1 *formArray = (FORM_INFO_1 *) forms;
+ int width = formArray[paperSize-1].Size.cx; // 1/1000 of a mm
+ int height = formArray[paperSize-1].Size.cy; // 1/1000 of a mm
+ paper_size = QSizeF((width*72/25.4)/1000.0, (height*72/25.4)/1000.0);
+ } else {
+ has_custom_paper_size = false;
+ }
+ }
+ free(forms);
+ }
+ } else {
+ has_custom_paper_size = false;
+ }
+ }, {
+ // Not supported under Win98
+ } );
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_PRINTER
diff --git a/src/gui/painting/qprintengine_win_p.h b/src/gui/painting/qprintengine_win_p.h
new file mode 100644
index 0000000000..b3b3d4b32f
--- /dev/null
+++ b/src/gui/painting/qprintengine_win_p.h
@@ -0,0 +1,272 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QPRINTENGINE_WIN_P_H
+#define QPRINTENGINE_WIN_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.
+//
+
+#ifndef QT_NO_PRINTER
+
+#include "QtGui/qprinter.h"
+#include "QtGui/qprintengine.h"
+#include "QtGui/qpaintengine.h"
+#include "QtCore/qt_windows.h"
+#include "private/qpaintengine_alpha_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class QWin32PrintEnginePrivate;
+class QPrinterPrivate;
+class QPainterState;
+
+class QWin32PrintEngine : public QAlphaPaintEngine, public QPrintEngine
+{
+ Q_DECLARE_PRIVATE(QWin32PrintEngine)
+public:
+ QWin32PrintEngine(QPrinter::PrinterMode mode);
+
+ // override QWin32PaintEngine
+ bool begin(QPaintDevice *dev);
+ bool end();
+
+ void updateState(const QPaintEngineState &state);
+
+ void updateMatrix(const QTransform &matrix);
+ void updateClipPath(const QPainterPath &clip, Qt::ClipOperation op);
+
+ void drawPath(const QPainterPath &path);
+ void drawPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode);
+ void drawTextItem(const QPointF &p, const QTextItem &textItem);
+
+ void drawPixmap(const QRectF &r, const QPixmap &pm, const QRectF &sr);
+ void drawTiledPixmap(const QRectF &r, const QPixmap &pm, const QPointF &p);
+ void setProperty(PrintEnginePropertyKey key, const QVariant &value);
+ QVariant property(PrintEnginePropertyKey key) const;
+
+ bool newPage();
+ bool abort();
+ int metric(QPaintDevice::PaintDeviceMetric) const;
+
+ QPrinter::PrinterState printerState() const;
+
+ QPaintEngine::Type type() const { return Windows; }
+
+ HDC getDC() const;
+ void releaseDC(HDC) const;
+
+ HDC getPrinterDC() const { return getDC(); }
+ void releasePrinterDC(HDC dc) const { releaseDC(dc); }
+
+private:
+ friend class QPrintDialog;
+ friend class QPageSetupDialog;
+ friend int qt_printerRealNumCopies(QPaintEngine *);
+};
+
+class QWin32PrintEnginePrivate : public QAlphaPaintEnginePrivate
+{
+ Q_DECLARE_PUBLIC(QWin32PrintEngine)
+public:
+ QWin32PrintEnginePrivate() :
+ hPrinter(0),
+ globalDevMode(0),
+ devMode(0),
+ pInfo(0),
+ hdc(0),
+ mode(QPrinter::ScreenResolution),
+ state(QPrinter::Idle),
+ resolution(0),
+ pageMarginsSet(false),
+ num_copies(1),
+ printToFile(false),
+ fullPage(false),
+ reinit(false),
+ has_custom_paper_size(false)
+ {
+ }
+
+ ~QWin32PrintEnginePrivate();
+
+
+ /* Reads the default printer name and its driver (printerProgram) into
+ the engines private data. */
+ void queryDefault();
+
+ /* Initializes the printer data based on the current printer name. This
+ function creates a DEVMODE struct, HDC and a printer handle. If these
+ structures are already in use, they are freed using release
+ */
+ void initialize();
+
+ /* Initializes data in the print engine whenever the HDC has been renewed
+ */
+ void initHDC();
+
+ /* Releases all the handles the printer currently holds, HDC, DEVMODE,
+ etc and resets the corresponding members to 0. */
+ void release();
+
+ /* Queries the resolutions for the current printer, and returns them
+ in a list. */
+ QList<QVariant> queryResolutions() const;
+
+ /* Resets the DC with changes in devmode. If the printer is active
+ this function only sets the reinit variable to true so it
+ is handled in the next begin or newpage. */
+ void doReinit();
+
+ /* Used by print/page setup dialogs */
+ HGLOBAL *createDevNames();
+
+ void readDevmode(HGLOBAL globalDevmode);
+ void readDevnames(HGLOBAL globalDevnames);
+
+ inline DEVMODEW *devModeW() const { return (DEVMODEW*) devMode; }
+ inline DEVMODEA *devModeA() const { return (DEVMODEA*) devMode; }
+
+ inline PRINTER_INFO_2W *pInfoW() { return (PRINTER_INFO_2W*) pInfo; };
+ inline PRINTER_INFO_2A *pInfoA() { return (PRINTER_INFO_2A*) pInfo; };
+
+ inline bool resetDC() {
+ QT_WA( {
+ hdc = ResetDCW(hdc, devModeW());
+ }, {
+ hdc = ResetDCA(hdc, devModeA());
+ } );
+ return hdc != 0;
+ }
+
+ void strokePath(const QPainterPath &path, const QColor &color);
+ void fillPath(const QPainterPath &path, const QColor &color);
+
+ void composeGdiPath(const QPainterPath &path);
+ void fillPath_dev(const QPainterPath &path, const QColor &color);
+ void strokePath_dev(const QPainterPath &path, const QColor &color, qreal width);
+
+ void updateOrigin();
+
+ void initDevRects();
+ void setPageMargins(int margin_left, int margin_top, int margin_right, int margin_bottom);
+ QRect getPageMargins() const;
+ void updateCustomPaperSize();
+
+ // Windows GDI printer references.
+ HANDLE hPrinter;
+
+ HGLOBAL globalDevMode;
+ void *devMode;
+ void *pInfo;
+ HGLOBAL hMem;
+
+ HDC hdc;
+
+ QPrinter::PrinterMode mode;
+
+ // Printer info
+ QString name;
+ QString program;
+ QString port;
+
+ // Document info
+ QString docName;
+ QString fileName;
+
+ QPrinter::PrinterState state;
+ int resolution;
+
+ // This QRect is used to store the exact values
+ // entered into the PageSetup Dialog because those are
+ // entered in mm but are since converted to device coordinates.
+ // If they were to be converted back when displaying the dialog
+ // again, there would be inaccuracies so when the user entered 10
+ // it may show up as 9.99 the next time the dialog is opened.
+ // We don't want that confusion.
+ QRect previousDialogMargins;
+
+ bool pageMarginsSet;
+ QRect devPageRect;
+ QRect devPhysicalPageRect;
+ QRect devPaperRect;
+ qreal stretch_x;
+ qreal stretch_y;
+ int origin_x;
+ int origin_y;
+
+ int dpi_x;
+ int dpi_y;
+ int dpi_display;
+ int num_copies;
+
+ uint printToFile : 1;
+ uint fullPage : 1;
+ uint reinit : 1;
+
+ uint complex_xform : 1;
+ uint has_pen : 1;
+ uint has_brush : 1;
+ uint has_custom_paper_size : 1;
+
+ uint txop;
+
+ QColor brush_color;
+ QPen pen;
+ QColor pen_color;
+ QSizeF paper_size;
+
+ QTransform painterMatrix;
+ QTransform matrix;
+};
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_PRINTER
+
+#endif // QPRINTENGINE_WIN_P_H
diff --git a/src/gui/painting/qprinter.cpp b/src/gui/painting/qprinter.cpp
new file mode 100644
index 0000000000..413f4a12a3
--- /dev/null
+++ b/src/gui/painting/qprinter.cpp
@@ -0,0 +1,2377 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qprinter_p.h"
+#include "qprinter.h"
+#include "qprintengine.h"
+#include "qprinterinfo.h"
+#include "qlist.h"
+#include <qpagesetupdialog.h>
+#include <qapplication.h>
+#include <qfileinfo.h>
+#if !defined(QT_NO_CUPS) && !defined(QT_NO_LIBRARY)
+#include "private/qcups_p.h"
+#endif
+
+#ifndef QT_NO_PRINTER
+
+#if defined (Q_WS_WIN)
+#include <private/qprintengine_win_p.h>
+#elif defined (Q_WS_MAC)
+#include <private/qprintengine_mac_p.h>
+#elif defined (QTOPIA_PRINTENGINE)
+#include <private/qprintengine_qws_p.h>
+#endif
+#include <private/qprintengine_ps_p.h>
+
+#if defined(Q_WS_X11)
+#include <private/qt_x11_p.h>
+#endif
+
+#ifndef QT_NO_PDF
+#include "qprintengine_pdf_p.h"
+#endif
+
+#include <qpicture.h>
+#include <private/qpaintengine_preview_p.h>
+
+#if defined(QT3_SUPPORT)
+# include "qprintdialog.h"
+#endif // QT3_SUPPORT
+
+QT_BEGIN_NAMESPACE
+
+#define ABORT_IF_ACTIVE(location) \
+ if (d->printEngine->printerState() == QPrinter::Active) { \
+ qWarning("%s: Cannot be changed while printer is active", location); \
+ return; \
+ }
+
+// NB! This table needs to be in sync with QPrinter::PaperSize
+static const float qt_paperSizes[][2] = {
+ {210, 297}, // A4
+ {176, 250}, // B5
+ {215.9f, 279.4f}, // Letter
+ {215.9f, 355.6f}, // Legal
+ {190.5f, 254}, // Executive
+ {841, 1189}, // A0
+ {594, 841}, // A1
+ {420, 594}, // A2
+ {297, 420}, // A3
+ {148, 210}, // A5
+ {105, 148}, // A6
+ {74, 105}, // A7
+ {52, 74}, // A8
+ {37, 52}, // A8
+ {1000, 1414}, // B0
+ {707, 1000}, // B1
+ {31, 44}, // B10
+ {500, 707}, // B2
+ {353, 500}, // B3
+ {250, 353}, // B4
+ {125, 176}, // B6
+ {88, 125}, // B7
+ {62, 88}, // B8
+ {33, 62}, // B9
+ {163, 229}, // C5E
+ {105, 241}, // US Common
+ {110, 220}, // DLE
+ {210, 330}, // Folio
+ {431.8f, 279.4f}, // Ledger
+ {279.4f, 431.8f} // Tabloid
+};
+
+/// return the multiplier of converting from the unit value to postscript-points.
+double qt_multiplierForUnit(QPrinter::Unit unit, int resolution)
+{
+ switch(unit) {
+ case QPrinter::Millimeter:
+ return 2.83464566929;
+ case QPrinter::Point:
+ return 1.0;
+ case QPrinter::Inch:
+ return 72.0;
+ case QPrinter::Pica:
+ return 12;
+ case QPrinter::Didot:
+ return 1.065826771;
+ case QPrinter::Cicero:
+ return 12.789921252;
+ case QPrinter::DevicePixel:
+ return 72.0/resolution;
+ }
+ return 1.0;
+}
+
+// not static: it's needed in qpagesetupdialog_unix.cpp
+QSizeF qt_printerPaperSize(QPrinter::Orientation orientation,
+ QPrinter::PaperSize paperSize,
+ QPrinter::Unit unit,
+ int resolution)
+{
+ int width_index = 0;
+ int height_index = 1;
+ if (orientation == QPrinter::Landscape) {
+ width_index = 1;
+ height_index = 0;
+ }
+ const qreal multiplier = qt_multiplierForUnit(unit, resolution);
+ return QSizeF((qt_paperSizes[paperSize][width_index] * 72 / 25.4) / multiplier,
+ (qt_paperSizes[paperSize][height_index] * 72 / 25.4) / multiplier);
+}
+
+
+// returns the actual num copies set on a printer, not
+// the number that is documented in QPrinter::numCopies()
+int qt_printerRealNumCopies(QPaintEngine *engine)
+{
+ int numCopies = 1;
+ if (engine->type() == QPaintEngine::PostScript
+ || engine->type() == QPaintEngine::Pdf)
+ {
+ QPdfBaseEngine *base = static_cast<QPdfBaseEngine *>(engine);
+ numCopies = base->d_func()->copies;
+ }
+#ifdef Q_WS_WIN
+ else if (engine->type() == QPaintEngine::Windows) {
+ QWin32PrintEngine *base = static_cast<QWin32PrintEngine *>(engine);
+ numCopies = base->d_func()->num_copies;
+ }
+#endif
+ return numCopies;
+}
+
+void QPrinterPrivate::createDefaultEngines()
+{
+ QPrinter::OutputFormat realOutputFormat = outputFormat;
+#if !defined (QTOPIA_PRINTENGINE)
+#if defined (Q_OS_UNIX) && ! defined (Q_WS_MAC)
+ if(outputFormat == QPrinter::NativeFormat) {
+ realOutputFormat = QPrinter::PostScriptFormat;
+ }
+#endif
+#endif
+
+ switch (realOutputFormat) {
+ case QPrinter::NativeFormat: {
+#if defined (Q_WS_WIN)
+ QWin32PrintEngine *winEngine = new QWin32PrintEngine(printerMode);
+ paintEngine = winEngine;
+ printEngine = winEngine;
+#elif defined (Q_WS_MAC)
+ QMacPrintEngine *macEngine = new QMacPrintEngine(printerMode);
+ paintEngine = macEngine;
+ printEngine = macEngine;
+#elif defined (QTOPIA_PRINTENGINE)
+ QtopiaPrintEngine *qwsEngine = new QtopiaPrintEngine(printerMode);
+ paintEngine = qwsEngine;
+ printEngine = qwsEngine;
+#elif defined (Q_OS_UNIX)
+ Q_ASSERT(false);
+#endif
+ }
+ break;
+ case QPrinter::PdfFormat: {
+ QPdfEngine *pdfEngine = new QPdfEngine(printerMode);
+ paintEngine = pdfEngine;
+ printEngine = pdfEngine;
+ }
+ break;
+ case QPrinter::PostScriptFormat: {
+ QPSPrintEngine *psEngine = new QPSPrintEngine(printerMode);
+ paintEngine = psEngine;
+ printEngine = psEngine;
+ }
+ break;
+ }
+ use_default_engine = true;
+ had_default_engines = true;
+}
+
+#ifndef QT_NO_PRINTPREVIEWWIDGET
+QList<const QPicture *> QPrinterPrivate::previewPages() const
+{
+ if (previewEngine)
+ return previewEngine->pages();
+ return QList<const QPicture *>();
+}
+
+void QPrinterPrivate::setPreviewMode(bool enable)
+{
+ Q_Q(QPrinter);
+ if (enable) {
+ if (!previewEngine)
+ previewEngine = new QPreviewPaintEngine;
+ had_default_engines = use_default_engine;
+ use_default_engine = false;
+ realPrintEngine = printEngine;
+ realPaintEngine = paintEngine;
+ q->setEngines(previewEngine, previewEngine);
+ previewEngine->setProxyEngines(realPrintEngine, realPaintEngine);
+ } else {
+ q->setEngines(realPrintEngine, realPaintEngine);
+ use_default_engine = had_default_engines;
+ }
+}
+#endif // QT_NO_PRINTPREVIEWWIDGET
+
+void QPrinterPrivate::addToManualSetList(QPrintEngine::PrintEnginePropertyKey key)
+{
+ for (int c = 0; c < manualSetList.size(); ++c) {
+ if (manualSetList[c] == key) return;
+ }
+ manualSetList.append(key);
+}
+
+
+/*!
+ \class QPrinter
+ \reentrant
+
+ \brief The QPrinter class is a paint device that paints on a printer.
+
+ \ingroup multimedia
+ \mainclass
+
+ This device represents a series of pages of printed output, and is
+ used in almost exactly the same way as other paint devices such as
+ QWidget and QPixmap.
+ A set of additional functions are provided to manage device-specific
+ features, such as orientation and resolution, and to step through
+ the pages in a document as it is generated.
+
+ When printing directly to a printer on Windows or Mac OS X, QPrinter uses
+ the built-in printer drivers. On X11, QPrinter uses the
+ \l{Common Unix Printing System (CUPS)} or the standard Unix \l lpr utility
+ to send PostScript or PDF output to the printer. As an alternative,
+ the printProgram() function can be used to specify the command or utility
+ to use instead of the system default.
+
+ QPrinter supports a number of parameters, most of which can be
+ changed by the end user through a \l{QPrintDialog}{print dialog}. In
+ general, QPrinter passes these functions onto the underlying QPrintEngine.
+
+ The most important parameters are:
+ \list
+ \i setOrientation() tells QPrinter which page orientation to use.
+ \i setPaperSize() tells QPrinter what paper size to expect from the
+ printer.
+ \i setResolution() tells QPrinter what resolution you wish the
+ printer to provide, in dots per inch (DPI).
+ \i setFullPage() tells QPrinter whether you want to deal with the
+ full page or just with the part the printer can draw on.
+ \i setNumCopies() tells QPrinter how many copies of the document
+ it should print.
+ \endlist
+
+ Many of these functions can only be called before the actual printing
+ begins (i.e., before QPainter::begin() is called). This usually makes
+ sense because, for example, it's not possible to change the number of
+ copies when you are halfway through printing. There are also some
+ settings that the user sets (through the printer dialog) and that
+ applications are expected to obey. See QAbstractPrintDialog's
+ documentation for more details.
+
+ When QPainter::begin() is called, the QPrinter it operates on is prepared for
+ a new page, enabling the QPainter to be used immediately to paint the first
+ page in a document. Once the first page has been painted, newPage() can be
+ called to request a new blank page to paint on, or QPainter::end() can be
+ called to finish printing. The second page and all following pages are
+ prepared using a call to newPage() before they are painted.
+
+ The first page in a document does not need to be preceded by a call to
+ newPage(). You only need to calling newPage() after QPainter::begin() if you
+ need to insert a blank page at the beginning of a printed document.
+ Similarly, calling newPage() after the last page in a document is painted will
+ result in a trailing blank page appended to the end of the printed document.
+
+ If you want to abort the print job, abort() will try its best to
+ stop printing. It may cancel the entire job or just part of it.
+
+ Since QPrinter can print to any QPrintEngine subclass, it is possible to
+ extend printing support to cover new types of printing subsystem by
+ subclassing QPrintEngine and reimplementing its interface.
+
+ \sa QPrintDialog, {Printing with Qt}
+*/
+
+/*!
+ \enum QPrinter::PrinterState
+
+ \value Idle
+ \value Active
+ \value Aborted
+ \value Error
+*/
+
+/*!
+ \enum QPrinter::PrinterMode
+
+ This enum describes the mode the printer should work in. It
+ basically presets a certain resolution and working mode.
+
+ \value ScreenResolution Sets the resolution of the print device to
+ the screen resolution. This has the big advantage that the results
+ obtained when painting on the printer will match more or less
+ exactly the visible output on the screen. It is the easiest to
+ use, as font metrics on the screen and on the printer are the
+ same. This is the default value. ScreenResolution will produce a
+ lower quality output than HighResolution and should only be used
+ for drafts.
+
+ \value PrinterResolution This value is deprecated. Is is
+ equivalent to ScreenResolution on Unix and HighResolution on
+ Windows and Mac. Due do the difference between ScreenResolution
+ and HighResolution, use of this value may lead to non-portable
+ printer code.
+
+ \value HighResolution On Windows, sets the printer resolution to that
+ defined for the printer in use. For PostScript printing, sets the
+ resolution of the PostScript driver to 1200 dpi.
+
+ \note When rendering text on a QPrinter device, it is important
+ to realize that the size of text, when specified in points, is
+ independent of the resolution specified for the device itself.
+ Therefore, it may be useful to specify the font size in pixels
+ when combining text with graphics to ensure that their relative
+ sizes are what you expect.
+*/
+
+/*!
+ \enum QPrinter::Orientation
+
+ This enum type (not to be confused with \c Orientation) is used
+ to specify each page's orientation.
+
+ \value Portrait the page's height is greater than its width.
+
+ \value Landscape the page's width is greater than its height.
+
+ This type interacts with \l QPrinter::PaperSize and
+ QPrinter::setFullPage() to determine the final size of the page
+ available to the application.
+*/
+
+
+/*!
+ \enum QPrinter::PrintRange
+
+ Used to specify the print range selection option.
+
+ \value AllPages All pages should be printed.
+ \value Selection Only the selection should be printed.
+ \value PageRange The specified page range should be printed.
+
+ \sa QAbstractPrintDialog::PrintRange
+*/
+
+/*!
+ \enum QPrinter::PrinterOption
+ \compat
+
+ Use QAbstractPrintDialog::PrintDialogOption instead.
+
+ \value PrintToFile
+ \value PrintSelection
+ \value PrintPageRange
+*/
+
+/*!
+ \enum QPrinter::PageSize
+
+ \obsolete
+ Use QPrinter::PaperSize instead.
+
+ \value A0 841 x 1189 mm
+ \value A1 594 x 841 mm
+ \value A2 420 x 594 mm
+ \value A3 297 x 420 mm
+ \value A4 210 x 297 mm, 8.26 x 11.69 inches
+ \value A5 148 x 210 mm
+ \value A6 105 x 148 mm
+ \value A7 74 x 105 mm
+ \value A8 52 x 74 mm
+ \value A9 37 x 52 mm
+ \value B0 1030 x 1456 mm
+ \value B1 728 x 1030 mm
+ \value B10 32 x 45 mm
+ \value B2 515 x 728 mm
+ \value B3 364 x 515 mm
+ \value B4 257 x 364 mm
+ \value B5 182 x 257 mm, 7.17 x 10.13 inches
+ \value B6 128 x 182 mm
+ \value B7 91 x 128 mm
+ \value B8 64 x 91 mm
+ \value B9 45 x 64 mm
+ \value C5E 163 x 229 mm
+ \value Comm10E 105 x 241 mm, U.S. Common 10 Envelope
+ \value DLE 110 x 220 mm
+ \value Executive 7.5 x 10 inches, 191 x 254 mm
+ \value Folio 210 x 330 mm
+ \value Ledger 432 x 279 mm
+ \value Legal 8.5 x 14 inches, 216 x 356 mm
+ \value Letter 8.5 x 11 inches, 216 x 279 mm
+ \value Tabloid 279 x 432 mm
+ \value Custom Unknown, or a user defined size.
+
+ \omitvalue NPageSize
+ */
+
+/*!
+ \enum QPrinter::PaperSize
+ \since 4.4
+
+ This enum type specifies what paper size QPrinter should use.
+ QPrinter does not check that the paper size is available; it just
+ uses this information, together with QPrinter::Orientation and
+ QPrinter::setFullPage(), to determine the printable area.
+
+ The defined sizes (with setFullPage(true)) are:
+
+ \value A0 841 x 1189 mm
+ \value A1 594 x 841 mm
+ \value A2 420 x 594 mm
+ \value A3 297 x 420 mm
+ \value A4 210 x 297 mm, 8.26 x 11.69 inches
+ \value A5 148 x 210 mm
+ \value A6 105 x 148 mm
+ \value A7 74 x 105 mm
+ \value A8 52 x 74 mm
+ \value A9 37 x 52 mm
+ \value B0 1030 x 1456 mm
+ \value B1 728 x 1030 mm
+ \value B10 32 x 45 mm
+ \value B2 515 x 728 mm
+ \value B3 364 x 515 mm
+ \value B4 257 x 364 mm
+ \value B5 182 x 257 mm, 7.17 x 10.13 inches
+ \value B6 128 x 182 mm
+ \value B7 91 x 128 mm
+ \value B8 64 x 91 mm
+ \value B9 45 x 64 mm
+ \value C5E 163 x 229 mm
+ \value Comm10E 105 x 241 mm, U.S. Common 10 Envelope
+ \value DLE 110 x 220 mm
+ \value Executive 7.5 x 10 inches, 191 x 254 mm
+ \value Folio 210 x 330 mm
+ \value Ledger 432 x 279 mm
+ \value Legal 8.5 x 14 inches, 216 x 356 mm
+ \value Letter 8.5 x 11 inches, 216 x 279 mm
+ \value Tabloid 279 x 432 mm
+ \value Custom Unknown, or a user defined size.
+
+ With setFullPage(false) (the default), the metrics will be a bit
+ smaller; how much depends on the printer in use.
+
+ \omitvalue NPageSize
+ \omitvalue NPaperSize
+*/
+
+
+/*!
+ \enum QPrinter::PageOrder
+
+ This enum type is used by QPrinter to tell the application program
+ how to print.
+
+ \value FirstPageFirst the lowest-numbered page should be printed
+ first.
+
+ \value LastPageFirst the highest-numbered page should be printed
+ first.
+*/
+
+/*!
+ \enum QPrinter::ColorMode
+
+ This enum type is used to indicate whether QPrinter should print
+ in color or not.
+
+ \value Color print in color if available, otherwise in grayscale.
+
+ \value GrayScale print in grayscale, even on color printers.
+*/
+
+/*!
+ \enum QPrinter::PaperSource
+
+ This enum type specifies what paper source QPrinter is to use.
+ QPrinter does not check that the paper source is available; it
+ just uses this information to try and set the paper source.
+ Whether it will set the paper source depends on whether the
+ printer has that particular source.
+
+ \warning This is currently only implemented for Windows.
+
+ \value Auto
+ \value Cassette
+ \value Envelope
+ \value EnvelopeManual
+ \value FormSource
+ \value LargeCapacity
+ \value LargeFormat
+ \value Lower
+ \value MaxPageSource
+ \value Middle
+ \value Manual
+ \value OnlyOne
+ \value Tractor
+ \value SmallFormat
+*/
+
+/*!
+ \enum QPrinter::Unit
+ \since 4.4
+
+ This enum type is used to specify the measurement unit for page and
+ paper sizes.
+
+ \value Millimeter
+ \value Point
+ \value Inch
+ \value Pica
+ \value Didot
+ \value Cicero
+ \value DevicePixel
+
+ Note the difference between Point and DevicePixel. The Point unit is
+ defined to be 1/72th of an inch, while the DevicePixel unit is
+ resolution dependant and is based on the actual pixels, or dots, on
+ the printer.
+*/
+
+
+/*
+ \enum QPrinter::PrintRange
+
+ This enum is used to specify which print range the application
+ should use to print.
+
+ \value AllPages All the pages should be printed.
+ \value Selection Only the selection should be printed.
+ \value PageRange Print according to the from page and to page options.
+
+ \sa setPrintRange(), printRange()
+*/
+
+/*
+ \enum QPrinter::PrinterOption
+
+ This enum describes various printer options that appear in the
+ printer setup dialog. It is used to enable and disable these
+ options in the setup dialog.
+
+ \value PrintToFile Describes if print to file should be enabled.
+ \value PrintSelection Describes if printing selections should be enabled.
+ \value PrintPageRange Describes if printing page ranges (from, to) should
+ be enabled
+
+ \sa setOptionEnabled(), isOptionEnabled()
+*/
+
+/*!
+ Creates a new printer object with the given \a mode.
+*/
+QPrinter::QPrinter(PrinterMode mode)
+ : QPaintDevice(),
+ d_ptr(new QPrinterPrivate(this))
+{
+ init(mode);
+ QPrinterInfo defPrn(QPrinterInfo::defaultPrinter());
+ if (!defPrn.isNull()) {
+ setPrinterName(defPrn.printerName());
+ } else if (QPrinterInfo::availablePrinters().isEmpty()
+ && d_ptr->paintEngine->type() != QPaintEngine::Windows
+ && d_ptr->paintEngine->type() != QPaintEngine::MacPrinter) {
+ setOutputFormat(QPrinter::PdfFormat);
+ }
+}
+
+/*!
+ \since 4.4
+
+ Creates a new printer object with the given \a printer and \a mode.
+*/
+QPrinter::QPrinter(const QPrinterInfo& printer, PrinterMode mode)
+ : QPaintDevice(),
+ d_ptr(new QPrinterPrivate(this))
+{
+ init(mode);
+ setPrinterName(printer.printerName());
+}
+
+void QPrinter::init(PrinterMode mode)
+{
+#if !defined(Q_WS_X11)
+ if (!qApp) {
+#else
+ if (!qApp || !X11) {
+#endif
+ qFatal("QPrinter: Must construct a QApplication before a QPaintDevice");
+ return;
+ }
+ Q_D(QPrinter);
+
+ d->printerMode = mode;
+ d->outputFormat = QPrinter::NativeFormat;
+ d->createDefaultEngines();
+
+#ifndef QT_NO_PRINTPREVIEWWIDGET
+ d->previewEngine = 0;
+#endif
+ d->realPrintEngine = 0;
+ d->realPaintEngine = 0;
+
+#if !defined(QT_NO_CUPS) && !defined(QT_NO_LIBRARY)
+ if (QCUPSSupport::cupsVersion() >= 10200 && QCUPSSupport().currentPPD()) {
+ setOutputFormat(QPrinter::PdfFormat);
+ d->outputFormat = QPrinter::NativeFormat;
+ }
+#endif
+}
+
+/*!
+ This function is used by subclasses of QPrinter to specify custom
+ print and paint engines (\a printEngine and \a paintEngine,
+ respectively).
+
+ QPrinter does not take ownership of the engines, so you need to
+ manage these engine instances yourself.
+
+ Note that changing the engines will reset the printer state and
+ all its properties.
+
+ \sa printEngine() paintEngine() setOutputFormat()
+
+ \since 4.1
+*/
+void QPrinter::setEngines(QPrintEngine *printEngine, QPaintEngine *paintEngine)
+{
+ Q_D(QPrinter);
+
+ if (d->use_default_engine)
+ delete d->printEngine;
+
+ d->printEngine = printEngine;
+ d->paintEngine = paintEngine;
+ d->use_default_engine = false;
+}
+
+/*!
+ Destroys the printer object and frees any allocated resources. If
+ the printer is destroyed while a print job is in progress this may
+ or may not affect the print job.
+*/
+QPrinter::~QPrinter()
+{
+ Q_D(QPrinter);
+ if (d->use_default_engine)
+ delete d->printEngine;
+#ifndef QT_NO_PRINTPREVIEWWIDGET
+ delete d->previewEngine;
+#endif
+ delete d;
+}
+
+/*!
+ \enum QPrinter::OutputFormat
+
+ The OutputFormat enum is used to describe the format QPrinter should
+ use for printing.
+
+ \value NativeFormat QPrinter will print output using a method defined
+ by the platform it is running on. This mode is the default when printing
+ directly to a printer.
+
+ \value PdfFormat QPrinter will generate its output as a searchable PDF file.
+ This mode is the default when printing to a file.
+
+ \value PostScriptFormat QPrinter will generate its output as in the PostScript format.
+ (This feature was introduced in Qt 4.2.)
+
+ \sa outputFormat(), setOutputFormat(), setOutputFileName()
+*/
+
+/*!
+ \since 4.1
+
+ Sets the output format for this printer to \a format.
+*/
+void QPrinter::setOutputFormat(OutputFormat format)
+{
+
+#ifndef QT_NO_PDF
+ Q_D(QPrinter);
+ if (d->outputFormat == format)
+ return;
+ d->outputFormat = format;
+
+ QPrintEngine *oldPrintEngine = d->printEngine;
+ QPaintEngine *oldPaintEngine = d->paintEngine; // same as the above - shouldn't be deleted
+ const bool def_engine = d->use_default_engine;
+ d->printEngine = 0;
+
+ d->createDefaultEngines();
+
+ if (oldPrintEngine) {
+ for (int i = 0; i < d->manualSetList.size(); ++i) {
+ QPrintEngine::PrintEnginePropertyKey key = d->manualSetList[i];
+ QVariant prop;
+ // PPK_NumberOfCopies need special treatmeant since it in most cases
+ // will return 1, disregarding the actual value that was set
+ if (key == QPrintEngine::PPK_NumberOfCopies)
+ prop = QVariant(qt_printerRealNumCopies(oldPaintEngine));
+ else
+ prop = oldPrintEngine->property(key);
+ if (prop.isValid())
+ d->printEngine->setProperty(key, prop);
+ }
+ }
+
+ if (def_engine)
+ delete oldPrintEngine;
+
+ d->validPrinter = d->outputFormat == QPrinter::PdfFormat || d->outputFormat == QPrinter::PostScriptFormat;
+
+#else
+ Q_UNUSED(format);
+#endif
+}
+
+/*!
+ \since 4.1
+
+ Returns the output format for this printer.
+*/
+QPrinter::OutputFormat QPrinter::outputFormat() const
+{
+ Q_D(const QPrinter);
+ return d->outputFormat;
+}
+
+
+
+/*! \reimp */
+int QPrinter::devType() const
+{
+ return QInternal::Printer;
+}
+
+/*!
+ Returns the printer name. This value is initially set to the name
+ of the default printer.
+
+ \sa setPrinterName()
+*/
+QString QPrinter::printerName() const
+{
+ Q_D(const QPrinter);
+ return d->printEngine->property(QPrintEngine::PPK_PrinterName).toString();
+}
+
+/*!
+ Sets the printer name to \a name.
+
+ \sa printerName(), isValid()
+*/
+void QPrinter::setPrinterName(const QString &name)
+{
+ Q_D(QPrinter);
+ ABORT_IF_ACTIVE("QPrinter::setPrinterName");
+
+#if defined(Q_OS_UNIX) && !defined(QT_NO_CUPS)
+ if(d->use_default_engine
+ && d->outputFormat == QPrinter::NativeFormat) {
+ if (QCUPSSupport::cupsVersion() >= 10200
+ && QCUPSSupport::printerHasPPD(name.toLocal8Bit().constData()))
+ setOutputFormat(QPrinter::PdfFormat);
+ else
+ setOutputFormat(QPrinter::PostScriptFormat);
+ d->outputFormat = QPrinter::NativeFormat;
+ }
+#endif
+
+ QList<QPrinterInfo> prnList = QPrinterInfo::availablePrinters();
+ d->validPrinter = false;
+ for (int i = 0; i < prnList.size(); ++i) {
+ if (prnList[i].printerName() == name) {
+ d->validPrinter = true;
+ break;
+ }
+ }
+
+ d->printEngine->setProperty(QPrintEngine::PPK_PrinterName, name);
+ d->addToManualSetList(QPrintEngine::PPK_PrinterName);
+}
+
+
+/*!
+ \since 4.4
+
+ Returns true if the printer currently selected is a valid printer
+ in the system, or a pure PDF/PostScript printer; otherwise returns false.
+
+ To detect other failures check the output of QPainter::begin() or QPainter::nextPage().
+
+ \snippet doc/src/snippets/printing-qprinter/errors.cpp 0
+
+ \sa setPrinterName()
+*/
+bool QPrinter::isValid() const
+{
+ Q_D(const QPrinter);
+#if defined(Q_WS_X11)
+ if (!qApp || !X11) {
+ return false;
+ }
+#endif
+ return d->validPrinter;
+}
+
+
+/*!
+ \fn bool QPrinter::outputToFile() const
+
+ Returns true if the output should be written to a file, or false
+ if the output should be sent directly to the printer. The default
+ setting is false.
+
+ \sa setOutputToFile(), setOutputFileName()
+*/
+
+
+/*!
+ \fn void QPrinter::setOutputToFile(bool enable)
+
+ Specifies whether the output should be written to a file or sent
+ directly to the printer.
+
+ Will output to a file if \a enable is true, or will output
+ directly to the printer if \a enable is false.
+
+ \sa outputToFile(), setOutputFileName()
+*/
+
+
+/*!
+ \fn QString QPrinter::outputFileName() const
+
+ Returns the name of the output file. By default, this is an empty string
+ (indicating that the printer shouldn't print to file).
+
+ \sa QPrintEngine::PrintEnginePropertyKey
+
+*/
+
+QString QPrinter::outputFileName() const
+{
+ Q_D(const QPrinter);
+ return d->printEngine->property(QPrintEngine::PPK_OutputFileName).toString();
+}
+
+/*!
+ Sets the name of the output file to \a fileName.
+
+ Setting a null or empty name (0 or "") disables printing to a file.
+ Setting a non-empty name enables printing to a file.
+
+ This can change the value of outputFormat(). If the file name has the
+ suffix ".ps" then PostScript is automatically selected as output format.
+ If the file name has the ".pdf" suffix PDF is generated. If the file name
+ has a suffix other than ".ps" and ".pdf", the output format used is the
+ one set with setOutputFormat().
+
+ QPrinter uses Qt's cross-platform PostScript or PDF print engines
+ respectively. If you can produce this format natively, for example
+ Mac OS X can generate PDF's from its print engine, set the output format
+ back to NativeFormat.
+
+ \sa outputFileName() setOutputToFile() setOutputFormat()
+*/
+
+void QPrinter::setOutputFileName(const QString &fileName)
+{
+ Q_D(QPrinter);
+ ABORT_IF_ACTIVE("QPrinter::setOutputFileName");
+
+ QFileInfo fi(fileName);
+ if (!fi.suffix().compare(QLatin1String("ps"), Qt::CaseInsensitive))
+ setOutputFormat(QPrinter::PostScriptFormat);
+ else if (!fi.suffix().compare(QLatin1String("pdf"), Qt::CaseInsensitive))
+ setOutputFormat(QPrinter::PdfFormat);
+ else if (fileName.isEmpty())
+ setOutputFormat(QPrinter::NativeFormat);
+
+ d->printEngine->setProperty(QPrintEngine::PPK_OutputFileName, fileName);
+ d->addToManualSetList(QPrintEngine::PPK_OutputFileName);
+}
+
+
+/*!
+ Returns the name of the program that sends the print output to the
+ printer.
+
+ The default is to return an empty string; meaning that QPrinter will try to
+ be smart in a system-dependent way. On X11 only, you can set it to something
+ different to use a specific print program. On the other platforms, this
+ returns an empty string.
+
+ \sa setPrintProgram(), setPrinterSelectionOption()
+*/
+QString QPrinter::printProgram() const
+{
+ Q_D(const QPrinter);
+ return d->printEngine->property(QPrintEngine::PPK_PrinterProgram).toString();
+}
+
+
+/*!
+ Sets the name of the program that should do the print job to \a
+ printProg.
+
+ On X11, this function sets the program to call with the PostScript
+ output. On other platforms, it has no effect.
+
+ \sa printProgram()
+*/
+void QPrinter::setPrintProgram(const QString &printProg)
+{
+ Q_D(QPrinter);
+ ABORT_IF_ACTIVE("QPrinter::setPrintProgram");
+ d->printEngine->setProperty(QPrintEngine::PPK_PrinterProgram, printProg);
+ d->addToManualSetList(QPrintEngine::PPK_PrinterProgram);
+}
+
+
+/*!
+ Returns the document name.
+
+ \sa setDocName(), QPrintEngine::PrintEnginePropertyKey
+*/
+QString QPrinter::docName() const
+{
+ Q_D(const QPrinter);
+ return d->printEngine->property(QPrintEngine::PPK_DocumentName).toString();
+}
+
+
+/*!
+ Sets the document name to \a name.
+
+ On X11, the document name is for example used as the default
+ output filename in QPrintDialog. Note that the document name does
+ not affect the file name if the printer is printing to a file.
+ Use the setOutputFile() function for this.
+
+ \sa docName(), QPrintEngine::PrintEnginePropertyKey
+*/
+void QPrinter::setDocName(const QString &name)
+{
+ Q_D(QPrinter);
+ ABORT_IF_ACTIVE("QPrinter::setDocName");
+ d->printEngine->setProperty(QPrintEngine::PPK_DocumentName, name);
+ d->addToManualSetList(QPrintEngine::PPK_DocumentName);
+}
+
+
+/*!
+ Returns the name of the application that created the document.
+
+ \sa setCreator()
+*/
+QString QPrinter::creator() const
+{
+ Q_D(const QPrinter);
+ return d->printEngine->property(QPrintEngine::PPK_Creator).toString();
+}
+
+
+/*!
+ Sets the name of the application that created the document to \a
+ creator.
+
+ This function is only applicable to the X11 version of Qt. If no
+ creator name is specified, the creator will be set to "Qt"
+ followed by some version number.
+
+ \sa creator()
+*/
+void QPrinter::setCreator(const QString &creator)
+{
+ Q_D(QPrinter);
+ ABORT_IF_ACTIVE("QPrinter::setCreator");
+ d->printEngine->setProperty(QPrintEngine::PPK_Creator, creator);
+ d->addToManualSetList(QPrintEngine::PPK_Creator);
+}
+
+
+/*!
+ Returns the orientation setting. This is driver-dependent, but is usually
+ QPrinter::Portrait.
+
+ \sa setOrientation()
+*/
+QPrinter::Orientation QPrinter::orientation() const
+{
+ Q_D(const QPrinter);
+ return QPrinter::Orientation(d->printEngine->property(QPrintEngine::PPK_Orientation).toInt());
+}
+
+
+/*!
+ Sets the print orientation to \a orientation.
+
+ The orientation can be either QPrinter::Portrait or
+ QPrinter::Landscape.
+
+ The printer driver reads this setting and prints using the
+ specified orientation.
+
+ On Windows, this option can be changed while printing and will
+ take effect from the next call to newPage().
+
+ On Mac OS X, changing the orientation during a print job has no effect.
+
+ \sa orientation()
+*/
+
+void QPrinter::setOrientation(Orientation orientation)
+{
+ Q_D(QPrinter);
+ d->printEngine->setProperty(QPrintEngine::PPK_Orientation, orientation);
+ d->addToManualSetList(QPrintEngine::PPK_Orientation);
+}
+
+
+/*!
+ \since 4.4
+ Returns the printer paper size. The default value is driver-dependent.
+
+ \sa setPaperSize() pageRect() paperRect()
+*/
+
+QPrinter::PaperSize QPrinter::paperSize() const
+{
+ Q_D(const QPrinter);
+ return QPrinter::PaperSize(d->printEngine->property(QPrintEngine::PPK_PaperSize).toInt());
+}
+
+/*!
+ \since 4.4
+
+ Sets the printer paper size to \a newPaperSize if that size is
+ supported. The result is undefined if \a newPaperSize is not
+ supported.
+
+ The default paper size is driver-dependent.
+
+ This function is useful mostly for setting a default value that
+ the user can override in the print dialog.
+
+ \sa paperSize() PaperSize setFullPage() setResolution() pageRect() paperRect()
+*/
+void QPrinter::setPaperSize(PaperSize newPaperSize)
+{
+ Q_D(QPrinter);
+ if (d->paintEngine->type() != QPaintEngine::Pdf)
+ ABORT_IF_ACTIVE("QPrinter::setPaperSize");
+ if (newPaperSize < 0 || newPaperSize >= NPaperSize) {
+ qWarning("QPrinter::setPaperSize: Illegal paper size %d", newPaperSize);
+ return;
+ }
+ d->printEngine->setProperty(QPrintEngine::PPK_PaperSize, newPaperSize);
+ d->addToManualSetList(QPrintEngine::PPK_PaperSize);
+ d->hasUserSetPageSize = true;
+}
+
+/*!
+ \obsolete
+
+ Returns the printer page size. The default value is driver-dependent.
+
+ Use paperSize() instead.
+*/
+QPrinter::PageSize QPrinter::pageSize() const
+{
+ return paperSize();
+}
+
+
+/*!
+ \obsolete
+
+ Sets the printer page size based on \a newPageSize.
+
+ Use setPaperSize() instead.
+*/
+
+void QPrinter::setPageSize(PageSize newPageSize)
+{
+ setPaperSize(newPageSize);
+}
+
+/*!
+ \since 4.4
+
+ Sets the paper size based on \a paperSize in \a unit.
+
+ \sa paperSize()
+*/
+
+void QPrinter::setPaperSize(const QSizeF &paperSize, QPrinter::Unit unit)
+{
+ Q_D(QPrinter);
+ if (d->paintEngine->type() != QPaintEngine::Pdf)
+ ABORT_IF_ACTIVE("QPrinter::setPaperSize");
+ const qreal multiplier = qt_multiplierForUnit(unit, resolution());
+ QSizeF size(paperSize.width() * multiplier, paperSize.height() * multiplier);
+ d->printEngine->setProperty(QPrintEngine::PPK_CustomPaperSize, size);
+ d->addToManualSetList(QPrintEngine::PPK_CustomPaperSize);
+ d->hasUserSetPageSize = true;
+}
+
+/*!
+ \since 4.4
+
+ Returns the paper size in \a unit.
+
+ \sa setPaperSize()
+*/
+
+QSizeF QPrinter::paperSize(Unit unit) const
+{
+ Q_D(const QPrinter);
+ int res = resolution();
+ const qreal multiplier = qt_multiplierForUnit(unit, res);
+ PaperSize paperType = paperSize();
+ if (paperType == Custom) {
+ QSizeF size = d->printEngine->property(QPrintEngine::PPK_CustomPaperSize).toSizeF();
+ return QSizeF(size.width() / multiplier, size.height() / multiplier);
+ }
+ else {
+ return qt_printerPaperSize(orientation(), paperType, unit, res);
+ }
+}
+
+/*!
+ Sets the page order to \a pageOrder.
+
+ The page order can be QPrinter::FirstPageFirst or
+ QPrinter::LastPageFirst. The application is responsible for
+ reading the page order and printing accordingly.
+
+ This function is mostly useful for setting a default value that
+ the user can override in the print dialog.
+
+ This function is only supported under X11.
+*/
+
+void QPrinter::setPageOrder(PageOrder pageOrder)
+{
+ Q_D(QPrinter);
+ ABORT_IF_ACTIVE("QPrinter::setPageOrder");
+ d->printEngine->setProperty(QPrintEngine::PPK_PageOrder, pageOrder);
+ d->addToManualSetList(QPrintEngine::PPK_PageOrder);
+}
+
+
+/*!
+ Returns the current page order.
+
+ The default page order is \c FirstPageFirst.
+*/
+
+QPrinter::PageOrder QPrinter::pageOrder() const
+{
+ Q_D(const QPrinter);
+ return QPrinter::PageOrder(d->printEngine->property(QPrintEngine::PPK_PageOrder).toInt());
+}
+
+
+/*!
+ Sets the printer's color mode to \a newColorMode, which can be
+ either \c Color or \c GrayScale.
+
+ \sa colorMode()
+*/
+
+void QPrinter::setColorMode(ColorMode newColorMode)
+{
+ Q_D(QPrinter);
+ ABORT_IF_ACTIVE("QPrinter::setColorMode");
+ d->printEngine->setProperty(QPrintEngine::PPK_ColorMode, newColorMode);
+ d->addToManualSetList(QPrintEngine::PPK_ColorMode);
+}
+
+
+/*!
+ Returns the current color mode.
+
+ \sa setColorMode()
+*/
+QPrinter::ColorMode QPrinter::colorMode() const
+{
+ Q_D(const QPrinter);
+ return QPrinter::ColorMode(d->printEngine->property(QPrintEngine::PPK_ColorMode).toInt());
+}
+
+
+/*!
+ Returns the number of copies to be printed. The default value is 1.
+
+ On Windows, Mac OS X and X11 systems that support CUPS, this will always
+ return 1 as these operating systems can internally handle the number
+ of copies.
+
+ On X11, this value will return the number of times the application is
+ required to print in order to match the number specified in the printer setup
+ dialog. This has been done since some printer drivers are not capable of
+ buffering up the copies and in those cases the application must make an
+ explicit call to the print code for each copy.
+
+ \sa setNumCopies()
+*/
+
+int QPrinter::numCopies() const
+{
+ Q_D(const QPrinter);
+ return d->printEngine->property(QPrintEngine::PPK_NumberOfCopies).toInt();
+}
+
+
+/*!
+ Sets the number of copies to be printed to \a numCopies.
+
+ The printer driver reads this setting and prints the specified
+ number of copies.
+
+ \sa numCopies()
+*/
+
+void QPrinter::setNumCopies(int numCopies)
+{
+ Q_D(QPrinter);
+ ABORT_IF_ACTIVE("QPrinter::setNumCopies");
+ d->printEngine->setProperty(QPrintEngine::PPK_NumberOfCopies, numCopies);
+ d->addToManualSetList(QPrintEngine::PPK_NumberOfCopies);
+}
+
+
+/*!
+ \since 4.1
+
+ Returns true if collation is turned on when multiple copies is selected.
+ Returns false if it is turned off when multiple copies is selected.
+
+ \sa setCollateCopies()
+*/
+bool QPrinter::collateCopies() const
+{
+ Q_D(const QPrinter);
+ return d->printEngine->property(QPrintEngine::PPK_CollateCopies).toBool();
+}
+
+
+/*!
+ \since 4.1
+
+ Sets the default value for collation checkbox when the print
+ dialog appears. If \a collate is true, it will enable
+ setCollateCopiesEnabled(). The default value is false. This value
+ will be changed by what the user presses in the print dialog.
+
+ \sa collateCopies()
+*/
+void QPrinter::setCollateCopies(bool collate)
+{
+ Q_D(QPrinter);
+ ABORT_IF_ACTIVE("QPrinter::setCollateCopies");
+ d->printEngine->setProperty(QPrintEngine::PPK_CollateCopies, collate);
+ d->addToManualSetList(QPrintEngine::PPK_CollateCopies);
+}
+
+
+
+/*!
+ If \a fp is true, enables support for painting over the entire page;
+ otherwise restricts painting to the printable area reported by the
+ device.
+
+ By default, full page printing is disabled. In this case, the origin
+ of the QPrinter's coordinate system coincides with the top-left
+ corner of the printable area.
+
+ If full page printing is enabled, the origin of the QPrinter's
+ coordinate system coincides with the top-left corner of the paper
+ itself. In this case, the
+ \l{QPaintDevice::PaintDeviceMetric}{device metrics} will report
+ the exact same dimensions as indicated by \l{PaperSize}. It may not
+ be possible to print on the entire physical page because of the
+ printer's margins, so the application must account for the margins
+ itself.
+
+ \sa fullPage(), setPaperSize(), width(), height(), {Printing with Qt}
+*/
+
+void QPrinter::setFullPage(bool fp)
+{
+ Q_D(QPrinter);
+ d->printEngine->setProperty(QPrintEngine::PPK_FullPage, fp);
+ d->addToManualSetList(QPrintEngine::PPK_FullPage);
+}
+
+
+/*!
+ Returns true if the origin of the printer's coordinate system is
+ at the corner of the page and false if it is at the edge of the
+ printable area.
+
+ See setFullPage() for details and caveats.
+
+ \sa setFullPage() PaperSize
+*/
+
+bool QPrinter::fullPage() const
+{
+ Q_D(const QPrinter);
+ return d->printEngine->property(QPrintEngine::PPK_FullPage).toBool();
+}
+
+
+/*!
+ Requests that the printer prints at \a dpi or as near to \a dpi as
+ possible.
+
+ This setting affects the coordinate system as returned by, for
+ example QPainter::viewport().
+
+ This function must be called before QPainter::begin() to have an effect on
+ all platforms.
+
+ \sa resolution() setPaperSize()
+*/
+
+void QPrinter::setResolution(int dpi)
+{
+ Q_D(QPrinter);
+ ABORT_IF_ACTIVE("QPrinter::setResolution");
+ d->printEngine->setProperty(QPrintEngine::PPK_Resolution, dpi);
+ d->addToManualSetList(QPrintEngine::PPK_Resolution);
+}
+
+
+/*!
+ Returns the current assumed resolution of the printer, as set by
+ setResolution() or by the printer driver.
+
+ \sa setResolution()
+*/
+
+int QPrinter::resolution() const
+{
+ Q_D(const QPrinter);
+ return d->printEngine->property(QPrintEngine::PPK_Resolution).toInt();
+}
+
+/*!
+ Sets the paper source setting to \a source.
+
+ Windows only: This option can be changed while printing and will
+ take effect from the next call to newPage()
+
+ \sa paperSource()
+*/
+
+void QPrinter::setPaperSource(PaperSource source)
+{
+ Q_D(QPrinter);
+ d->printEngine->setProperty(QPrintEngine::PPK_PaperSource, source);
+ d->addToManualSetList(QPrintEngine::PPK_PaperSource);
+}
+
+/*!
+ Returns the printer's paper source. This is \c Manual or a printer
+ tray or paper cassette.
+*/
+QPrinter::PaperSource QPrinter::paperSource() const
+{
+ Q_D(const QPrinter);
+ return QPrinter::PaperSource(d->printEngine->property(QPrintEngine::PPK_PaperSource).toInt());
+}
+
+
+/*!
+ \since 4.1
+
+ Enabled or disables font embedding depending on \a enable.
+
+ Currently this option is only supported on X11.
+
+ \sa fontEmbeddingEnabled()
+*/
+void QPrinter::setFontEmbeddingEnabled(bool enable)
+{
+ Q_D(QPrinter);
+ d->printEngine->setProperty(QPrintEngine::PPK_FontEmbedding, enable);
+ d->addToManualSetList(QPrintEngine::PPK_FontEmbedding);
+}
+
+/*!
+ \since 4.1
+
+ Returns true if font embedding is enabled.
+
+ Currently this option is only supported on X11.
+
+ \sa setFontEmbeddingEnabled()
+*/
+bool QPrinter::fontEmbeddingEnabled() const
+{
+ Q_D(const QPrinter);
+ return d->printEngine->property(QPrintEngine::PPK_FontEmbedding).toBool();
+}
+
+/*!
+ \enum QPrinter::DuplexMode
+ \since 4.4
+
+ This enum is used to indicate whether printing will occur on one or both sides
+ of each sheet of paper (simplex or duplex printing).
+
+ \value DuplexNone Single sided (simplex) printing only.
+ \value DuplexAuto The printer's default setting is used to determine whether
+ duplex printing is used.
+ \value DuplexLongSide Both sides of each sheet of paper are used for printing.
+ The paper is turned over its longest edge before the second
+ side is printed
+ \value DuplexShortSide Both sides of each sheet of paper are used for printing.
+ The paper is turned over its shortest edge before the second
+ side is printed
+*/
+
+/*!
+ \since 4.2
+
+ Enables double sided printing if \a doubleSided is true; otherwise disables it.
+
+ Currently this option is only supported on X11.
+*/
+void QPrinter::setDoubleSidedPrinting(bool doubleSided)
+{
+ setDuplex(doubleSided ? DuplexAuto : DuplexNone);
+}
+
+
+/*!
+ \since 4.2
+
+ Returns true if double side printing is enabled.
+
+ Currently this option is only supported on X11.
+*/
+bool QPrinter::doubleSidedPrinting() const
+{
+ return duplex() != DuplexNone;
+}
+
+/*!
+ \since 4.4
+
+ Enables double sided printing based on the \a duplex mode.
+
+ Currently this option is only supported on X11.
+*/
+void QPrinter::setDuplex(DuplexMode duplex)
+{
+ Q_D(QPrinter);
+ d->printEngine->setProperty(QPrintEngine::PPK_Duplex, duplex);
+ d->addToManualSetList(QPrintEngine::PPK_Duplex);
+}
+
+/*!
+ \since 4.4
+
+ Returns the current duplex mode.
+
+ Currently this option is only supported on X11.
+*/
+QPrinter::DuplexMode QPrinter::duplex() const
+{
+ Q_D(const QPrinter);
+ return static_cast <DuplexMode> (d->printEngine->property(QPrintEngine::PPK_Duplex).toInt());
+}
+
+/*!
+ \since 4.4
+
+ Returns the page's rectangle in \a unit; this is usually smaller
+ than the paperRect() since the page normally has margins between
+ its borders and the paper.
+
+ \sa paperSize()
+*/
+QRectF QPrinter::pageRect(Unit unit) const
+{
+ Q_D(const QPrinter);
+ int res = resolution();
+ const qreal multiplier = qt_multiplierForUnit(unit, res);
+ // the page rect is in device pixels
+ QRect devRect(d->printEngine->property(QPrintEngine::PPK_PageRect).toRect());
+ if (unit == DevicePixel)
+ return devRect;
+ QRectF diRect(devRect.x()*72.0/res,
+ devRect.y()*72.0/res,
+ devRect.width()*72.0/res,
+ devRect.height()*72.0/res);
+ return QRectF(diRect.x()/multiplier, diRect.y()/multiplier,
+ diRect.width()/multiplier, diRect.height()/multiplier);
+}
+
+
+/*!
+ \since 4.4
+
+ Returns the paper's rectangle in \a unit; this is usually larger
+ than the pageRect().
+
+ \sa pageRect()
+*/
+QRectF QPrinter::paperRect(Unit unit) const
+{
+ Q_D(const QPrinter);
+ int res = resolution();
+ const qreal multiplier = qt_multiplierForUnit(unit, resolution());
+ // the page rect is in device pixels
+ QRect devRect(d->printEngine->property(QPrintEngine::PPK_PaperRect).toRect());
+ if (unit == DevicePixel)
+ return devRect;
+ QRectF diRect(devRect.x()*72.0/res,
+ devRect.y()*72.0/res,
+ devRect.width()*72.0/res,
+ devRect.height()*72.0/res);
+ return QRectF(diRect.x()/multiplier, diRect.y()/multiplier,
+ diRect.width()/multiplier, diRect.height()/multiplier);
+}
+
+/*!
+ Returns the page's rectangle; this is usually smaller than the
+ paperRect() since the page normally has margins between its
+ borders and the paper.
+
+ The unit of the returned rectangle is DevicePixel.
+
+ \sa paperSize()
+*/
+QRect QPrinter::pageRect() const
+{
+ Q_D(const QPrinter);
+ return d->printEngine->property(QPrintEngine::PPK_PageRect).toRect();
+}
+
+/*!
+ Returns the paper's rectangle; this is usually larger than the
+ pageRect().
+
+ The unit of the returned rectangle is DevicePixel.
+
+ \sa pageRect()
+*/
+QRect QPrinter::paperRect() const
+{
+ Q_D(const QPrinter);
+ return d->printEngine->property(QPrintEngine::PPK_PaperRect).toRect();
+}
+
+
+/*!
+ \since 4.4
+
+ This function sets the \a left, \a top, \a right and \a bottom
+ page margins for this printer. The unit of the margins are
+ specified with the \a unit parameter.
+*/
+void QPrinter::setPageMargins(qreal left, qreal top, qreal right, qreal bottom, QPrinter::Unit unit)
+{
+ Q_D(QPrinter);
+ const qreal multiplier = qt_multiplierForUnit(unit, resolution());
+ QList<QVariant> margins;
+ margins << (left * multiplier) << (top * multiplier)
+ << (right * multiplier) << (bottom * multiplier);
+ d->printEngine->setProperty(QPrintEngine::PPK_PageMargins, margins);
+ d->addToManualSetList(QPrintEngine::PPK_PageMargins);
+ d->hasCustomPageMargins = true;
+}
+
+/*!
+ \since 4.4
+
+ Returns the page margins for this printer in \a left, \a top, \a
+ right, \a bottom. The unit of the returned margins are specified
+ with the \a unit parameter.
+*/
+void QPrinter::getPageMargins(qreal *left, qreal *top, qreal *right, qreal *bottom, QPrinter::Unit unit) const
+{
+ Q_D(const QPrinter);
+ Q_ASSERT(left && top && right && bottom);
+ const qreal multiplier = qt_multiplierForUnit(unit, resolution());
+ QList<QVariant> margins(d->printEngine->property(QPrintEngine::PPK_PageMargins).toList());
+ *left = margins.at(0).toDouble() / multiplier;
+ *top = margins.at(1).toDouble() / multiplier;
+ *right = margins.at(2).toDouble() / multiplier;
+ *bottom = margins.at(3).toDouble() / multiplier;
+}
+
+/*!
+ \internal
+
+ Returns the metric for the given \a id.
+*/
+int QPrinter::metric(PaintDeviceMetric id) const
+{
+ Q_D(const QPrinter);
+ return d->printEngine->metric(id);
+}
+
+/*!
+ Returns the paint engine used by the printer.
+*/
+QPaintEngine *QPrinter::paintEngine() const
+{
+ Q_D(const QPrinter);
+ return d->paintEngine;
+}
+
+/*!
+ \since 4.1
+
+ Returns the print engine used by the printer.
+*/
+QPrintEngine *QPrinter::printEngine() const
+{
+ Q_D(const QPrinter);
+ return d->printEngine;
+}
+
+#if defined (Q_WS_WIN)
+/*!
+ Sets the page size to be used by the printer under Windows to \a
+ pageSize.
+
+ \warning This function is not portable so you may prefer to use
+ setPaperSize() instead.
+
+ \sa winPageSize()
+*/
+void QPrinter::setWinPageSize(int pageSize)
+{
+ Q_D(QPrinter);
+ ABORT_IF_ACTIVE("QPrinter::setWinPageSize");
+ d->printEngine->setProperty(QPrintEngine::PPK_WindowsPageSize, pageSize);
+ d->addToManualSetList(QPrintEngine::PPK_WindowsPageSize);
+}
+
+/*!
+ Returns the page size used by the printer under Windows.
+
+ \warning This function is not portable so you may prefer to use
+ paperSize() instead.
+
+ \sa setWinPageSize()
+*/
+int QPrinter::winPageSize() const
+{
+ Q_D(const QPrinter);
+ return d->printEngine->property(QPrintEngine::PPK_WindowsPageSize).toInt();
+}
+#endif // Q_WS_WIN
+
+/*!
+ Returns a list of the resolutions (a list of dots-per-inch
+ integers) that the printer says it supports.
+
+ For X11 where all printing is directly to postscript, this
+ function will always return a one item list containing only the
+ postscript resolution, i.e., 72 (72 dpi -- but see PrinterMode).
+*/
+QList<int> QPrinter::supportedResolutions() const
+{
+ Q_D(const QPrinter);
+ QList<QVariant> varlist
+ = d->printEngine->property(QPrintEngine::PPK_SupportedResolutions).toList();
+ QList<int> intlist;
+ for (int i=0; i<varlist.size(); ++i)
+ intlist << varlist.at(i).toInt();
+ return intlist;
+}
+
+/*!
+ Tells the printer to eject the current page and to continue
+ printing on a new page. Returns true if this was successful;
+ otherwise returns false.
+
+ Calling newPage() on an inactive QPrinter object will always
+ fail.
+*/
+bool QPrinter::newPage()
+{
+ Q_D(QPrinter);
+ if (d->printEngine->printerState() != QPrinter::Active)
+ return false;
+ return d->printEngine->newPage();
+}
+
+/*!
+ Aborts the current print run. Returns true if the print run was
+ successfully aborted and printerState() will return QPrinter::Aborted; otherwise
+ returns false.
+
+ It is not always possible to abort a print job. For example,
+ all the data has gone to the printer but the printer cannot or
+ will not cancel the job when asked to.
+*/
+bool QPrinter::abort()
+{
+ Q_D(QPrinter);
+ return d->printEngine->abort();
+}
+
+/*!
+ Returns the current state of the printer. This may not always be
+ accurate (for example if the printer doesn't have the capability
+ of reporting its state to the operating system).
+*/
+QPrinter::PrinterState QPrinter::printerState() const
+{
+ Q_D(const QPrinter);
+ return d->printEngine->printerState();
+}
+
+
+/*! \fn void QPrinter::margins(uint *top, uint *left, uint *bottom, uint *right) const
+
+ Sets *\a top, *\a left, *\a bottom, *\a right to be the top,
+ left, bottom, and right margins.
+
+ This function has been superseded by paperRect() and pageRect().
+ Use paperRect().top() - pageRect().top() for the top margin,
+ paperRect().left() - pageRect().left() for the left margin,
+ paperRect().bottom() - pageRect().bottom() for the bottom margin,
+ and papaerRect().right() - pageRect().right() for the right
+ margin.
+
+ \oldcode
+ uint rightMargin;
+ uint bottomMargin;
+ printer->margins(0, 0, &bottomMargin, &rightMargin);
+ \newcode
+ int rightMargin = printer->paperRect().right() - printer->pageRect().right();
+ int bottomMargin = printer->paperRect().bottom() - printer->pageRect().bottom();
+ \endcode
+*/
+
+/*! \fn QSize QPrinter::margins() const
+
+ \overload
+
+ Returns a QSize containing the left margin and the top margin.
+
+ This function has been superseded by paperRect() and pageRect().
+ Use paperRect().left() - pageRect().left() for the left margin,
+ and paperRect().top() - pageRect().top() for the top margin.
+
+ \oldcode
+ QSize margins = printer->margins();
+ int leftMargin = margins.width();
+ int topMargin = margins.height();
+ \newcode
+ int leftMargin = printer->paperRect().left() - printer->pageRect().left();
+ int topMargin = printer->paperRect().top() - printer->pageRect().top();
+ \endcode
+*/
+
+/*! \fn bool QPrinter::aborted()
+
+ Use printerState() == QPrinter::Aborted instead.
+*/
+
+#ifdef Q_WS_WIN
+/*!
+ \internal
+*/
+HDC QPrinter::getDC() const
+{
+ Q_D(const QPrinter);
+ return d->printEngine->getPrinterDC();
+}
+
+/*!
+ \internal
+*/
+void QPrinter::releaseDC(HDC hdc) const
+{
+ Q_D(const QPrinter);
+ d->printEngine->releasePrinterDC(hdc);
+}
+
+/*!
+ Returns the supported paper sizes for this printer.
+
+ The values will be either a value that matches an entry in the
+ QPrinter::PaperSource enum or a driver spesific value. The driver
+ spesific values are greater than the constant DMBIN_USER declared
+ in wingdi.h.
+
+ \warning This function is only available in windows.
+*/
+
+QList<QPrinter::PaperSource> QPrinter::supportedPaperSources() const
+{
+ Q_D(const QPrinter);
+ QVariant v = d->printEngine->property(QPrintEngine::PPK_PaperSources);
+
+ QList<QVariant> variant_list = v.toList();
+ QList<QPrinter::PaperSource> int_list;
+ for (int i=0; i<variant_list.size(); ++i)
+ int_list << (QPrinter::PaperSource) variant_list.at(i).toInt();
+
+ return int_list;
+}
+
+#endif
+
+/*!
+ \fn QString QPrinter::printerSelectionOption() const
+
+ Returns the printer options selection string. This is useful only
+ if the print command has been explicitly set.
+
+ The default value (an empty string) implies that the printer should
+ be selected in a system-dependent manner.
+
+ Any other value implies that the given value should be used.
+
+ \warning This function is not available on Windows.
+
+ \sa setPrinterSelectionOption()
+*/
+
+/*!
+ \fn void QPrinter::setPrinterSelectionOption(const QString &option)
+
+ Sets the printer to use \a option to select the printer. \a option
+ is null by default (which implies that Qt should be smart enough
+ to guess correctly), but it can be set to other values to use a
+ specific printer selection option.
+
+ If the printer selection option is changed while the printer is
+ active, the current print job may or may not be affected.
+
+ \warning This function is not available on Windows.
+
+ \sa printerSelectionOption()
+*/
+
+#ifndef Q_WS_WIN
+QString QPrinter::printerSelectionOption() const
+{
+ Q_D(const QPrinter);
+ return d->printEngine->property(QPrintEngine::PPK_SelectionOption).toString();
+}
+
+void QPrinter::setPrinterSelectionOption(const QString &option)
+{
+ Q_D(QPrinter);
+ d->printEngine->setProperty(QPrintEngine::PPK_SelectionOption, option);
+ d->addToManualSetList(QPrintEngine::PPK_SelectionOption);
+}
+#endif
+
+/*!
+ \since 4.1
+ \fn int QPrinter::fromPage() const
+
+ Returns the number of the first page in a range of pages to be printed
+ (the "from page" setting). Pages in a document are numbered according to
+ the convention that the first page is page 1.
+
+ By default, this function returns a special value of 0, meaning that
+ the "from page" setting is unset.
+
+ \note If fromPage() and toPage() both return 0, this indicates that
+ \e{the whole document will be printed}.
+
+ \sa setFromTo(), toPage()
+*/
+
+int QPrinter::fromPage() const
+{
+ Q_D(const QPrinter);
+ return d->fromPage;
+}
+
+/*!
+ \since 4.1
+
+ Returns the number of the last page in a range of pages to be printed
+ (the "to page" setting). Pages in a document are numbered according to
+ the convention that the first page is page 1.
+
+ By default, this function returns a special value of 0, meaning that
+ the "to page" setting is unset.
+
+ \note If fromPage() and toPage() both return 0, this indicates that
+ \e{the whole document will be printed}.
+
+ The programmer is responsible for reading this setting and
+ printing accordingly.
+
+ \sa setFromTo(), fromPage()
+*/
+
+int QPrinter::toPage() const
+{
+ Q_D(const QPrinter);
+ return d->toPage;
+}
+
+/*!
+ \since 4.1
+
+ Sets the range of pages to be printed to cover the pages with numbers
+ specified by \a from and \a to, where \a from corresponds to the first
+ page in the range and \a to corresponds to the last.
+
+ \note Pages in a document are numbered according to the convention that
+ the first page is page 1. However, if \a from and \a to are both set to 0,
+ the \e{whole document will be printed}.
+
+ This function is mostly used to set a default value that the user can
+ override in the print dialog when you call setup().
+
+ \sa fromPage(), toPage()
+*/
+
+void QPrinter::setFromTo(int from, int to)
+{
+ Q_D(QPrinter);
+ if (from > to) {
+ qWarning() << "QPrinter::setFromTo: 'from' must be less than or equal to 'to'";
+ from = to;
+ }
+ d->fromPage = from;
+ d->toPage = to;
+
+ if (d->minPage == 0 && d->maxPage == 0) {
+ d->minPage = 1;
+ d->maxPage = to;
+ d->options |= QAbstractPrintDialog::PrintPageRange;
+ }
+}
+
+/*!
+ \since 4.1
+
+ Sets the print range option in to be \a range.
+*/
+void QPrinter::setPrintRange( PrintRange range )
+{
+ Q_D(QPrinter);
+ d->printRange = QAbstractPrintDialog::PrintRange(range);
+}
+
+/*!
+ \since 4.1
+
+ Returns the page range of the QPrinter. After the print setup
+ dialog has been opened, this function returns the value selected
+ by the user.
+
+ \sa setPrintRange()
+*/
+QPrinter::PrintRange QPrinter::printRange() const
+{
+ Q_D(const QPrinter);
+ return PrintRange(d->printRange);
+}
+
+#if defined(QT3_SUPPORT)
+
+void QPrinter::setOutputToFile(bool f)
+{
+ if (f) {
+ if (outputFileName().isEmpty())
+ setOutputFileName(QLatin1String("untitled_printer_document"));
+ } else {
+ setOutputFileName(QString());
+ }
+}
+
+bool qt_compat_QPrinter_printSetup(QPrinter *printer, QPrinterPrivate *pd, QWidget *parent)
+{
+ Q_UNUSED(pd);
+ QPrintDialog dlg(printer, parent);
+ return dlg.exec() != 0;
+}
+
+
+#ifdef Q_WS_MAC
+bool qt_compat_QPrinter_pageSetup(QPrinter *p, QWidget *parent)
+{
+ QPageSetupDialog psd(p, parent);
+ return psd.exec() != 0;
+}
+
+/*!
+ Executes a page setup dialog so that the user can configure the type of
+ page used for printing. Returns true if the contents of the dialog are
+ accepted; returns false if the dialog is canceled.
+*/
+bool QPrinter::pageSetup(QWidget *parent)
+{
+ return qt_compat_QPrinter_pageSetup(this, parent);
+}
+
+/*!
+ Executes a print setup dialog so that the user can configure the printing
+ process. Returns true if the contents of the dialog are accepted; returns
+ false if the dialog is canceled.
+*/
+bool QPrinter::printSetup(QWidget *parent)
+{
+ Q_D(QPrinter);
+ return qt_compat_QPrinter_printSetup(this, d, parent);
+}
+#endif // Q_WS_MAC
+
+/*!
+ Use QPrintDialog instead.
+
+ \oldcode
+ if (printer->setup(parent))
+ ...
+ \newcode
+ QPrintDialog dialog(printer, parent);
+ if (dialog.exec())
+ ...
+ \endcode
+*/
+bool QPrinter::setup(QWidget *parent)
+{
+ Q_D(QPrinter);
+ return qt_compat_QPrinter_printSetup(this, d, parent)
+#ifdef Q_WS_MAC
+ && qt_compat_QPrinter_pageSetup(this, parent);
+#endif
+ ;
+}
+
+/*!
+ Use QPrintDialog::minPage() instead.
+*/
+int QPrinter::minPage() const
+{
+ Q_D(const QPrinter);
+ return d->minPage;
+}
+
+/*!
+ Use QPrintDialog::maxPage() instead.
+*/
+int QPrinter::maxPage() const
+{
+ Q_D(const QPrinter);
+ return d->maxPage;
+}
+
+/*!
+ Use QPrintDialog::setMinMax() instead.
+*/
+void QPrinter::setMinMax( int minPage, int maxPage )
+{
+ Q_D(QPrinter);
+ Q_ASSERT_X(minPage <= maxPage, "QPrinter::setMinMax",
+ "'min' must be less than or equal to 'max'");
+ d->minPage = minPage;
+ d->maxPage = maxPage;
+ d->options |= QPrintDialog::PrintPageRange;
+}
+
+/*!
+ Returns true if the printer is set up to collate copies of printed documents;
+ otherwise returns false.
+
+ Use QPrintDialog::isOptionEnabled(QPrintDialog::PrintCollateCopies)
+ instead.
+
+ \sa collateCopies()
+*/
+bool QPrinter::collateCopiesEnabled() const
+{
+ Q_D(const QPrinter);
+ return (d->options & QPrintDialog::PrintCollateCopies);
+}
+
+/*!
+ Use QPrintDialog::addEnabledOption(QPrintDialog::PrintCollateCopies)
+ or QPrintDialog::setEnabledOptions(QPrintDialog::enabledOptions()
+ & ~QPrintDialog::PrintCollateCopies) instead, depending on \a
+ enable.
+*/
+void QPrinter::setCollateCopiesEnabled(bool enable)
+{
+ Q_D(QPrinter);
+
+ if (enable)
+ d->options |= QPrintDialog::PrintCollateCopies;
+ else
+ d->options &= ~QPrintDialog::PrintCollateCopies;
+}
+
+/*!
+ Use QPrintDialog instead.
+*/
+void QPrinter::setOptionEnabled( PrinterOption option, bool enable )
+{
+ Q_D(QPrinter);
+ if (enable)
+ d->options |= QPrintDialog::PrintDialogOption(1 << option);
+ else
+ d->options &= ~QPrintDialog::PrintDialogOption(1 << option);
+}
+
+/*!
+ Use QPrintDialog instead.
+*/
+bool QPrinter::isOptionEnabled( PrinterOption option ) const
+{
+ Q_D(const QPrinter);
+ return (d->options & QPrintDialog::PrintDialogOption(option));
+}
+
+#endif // QT3_SUPPORT
+
+/*!
+ \class QPrintEngine
+ \reentrant
+
+ \ingroup multimedia
+
+ \brief The QPrintEngine class defines an interface for how QPrinter
+ interacts with a given printing subsystem.
+
+ The common case when creating your own print engine is to derive from both
+ QPaintEngine and QPrintEngine. Various properties of a print engine are
+ given with property() and set with setProperty().
+
+ \sa QPaintEngine
+*/
+
+/*!
+ \enum QPrintEngine::PrintEnginePropertyKey
+
+ This enum is used to communicate properties between the print
+ engine and QPrinter. A property may or may not be supported by a
+ given print engine.
+
+ \value PPK_CollateCopies A boolean value indicating whether the
+ printout should be collated or not.
+
+ \value PPK_ColorMode Refers to QPrinter::ColorMode, either color or
+ monochrome.
+
+ \value PPK_Creator A string describing the document's creator.
+
+ \value PPK_Duplex A boolean value indicating whether both sides of
+ the printer paper should be used for the printout.
+
+ \value PPK_DocumentName A string describing the document name in
+ the spooler.
+
+ \value PPK_FontEmbedding A boolean value indicating whether data for
+ the document's fonts should be embedded in the data sent to the
+ printer.
+
+ \value PPK_FullPage A boolean describing if the printer should be
+ full page or not.
+
+ \value PPK_NumberOfCopies An integer specifying the number of
+ copies
+
+ \value PPK_Orientation Specifies a QPrinter::Orientation value.
+
+ \value PPK_OutputFileName The output file name as a string. An
+ empty file name indicates that the printer should not print to a file.
+
+ \value PPK_PageOrder Specifies a QPrinter::PageOrder value.
+
+ \value PPK_PageRect A QRect specifying the page rectangle
+
+ \value PPK_PageSize Obsolete. Use PPK_PaperSize instead.
+
+ \value PPK_PaperRect A QRect specifying the paper rectangle.
+
+ \value PPK_PaperSource Specifies a QPrinter::PaperSource value.
+
+ \value PPK_PaperSources Specifies more than one QPrinter::PaperSource value.
+
+ \value PPK_PaperSize Specifies a QPrinter::PaperSize value.
+
+ \value PPK_PrinterName A string specifying the name of the printer.
+
+ \value PPK_PrinterProgram A string specifying the name of the
+ printer program used for printing,
+
+ \value PPK_Resolution An integer describing the dots per inch for
+ this printer.
+
+ \value PPK_SelectionOption
+
+ \value PPK_SupportedResolutions A list of integer QVariants
+ describing the set of supported resolutions that the printer has.
+
+ \value PPK_SuppressSystemPrintStatus Suppress the built-in dialog for showing
+ printing progress. As of 4.1 this only has effect on Mac OS X where, by default,
+ a status dialog is shown.
+
+ \value PPK_WindowsPageSize An integer specifying a DM_PAPER entry
+ on Windows.
+
+ \value PPK_CustomPaperSize A QSizeF specifying a custom paper size
+ in the QPrinter::Point unit.
+
+ \value PPK_PageMargins A QList<QVariant> containing the left, top,
+ right and bottom margin values.
+
+ \value PPK_CustomBase Basis for extension.
+*/
+
+/*!
+ \fn QPrintEngine::~QPrintEngine()
+
+ Destroys the print engine.
+*/
+
+/*!
+ \fn void QPrintEngine::setProperty(PrintEnginePropertyKey key, const QVariant &value)
+
+ Sets the print engine's property specified by \a key to the given \a value.
+
+ \sa property()
+*/
+
+/*!
+ \fn void QPrintEngine::property(PrintEnginePropertyKey key) const
+
+ Returns the print engine's property specified by \a key.
+
+ \sa setProperty()
+*/
+
+/*!
+ \fn bool QPrintEngine::newPage()
+
+ Instructs the print engine to start a new page. Returns true if
+ the printer was able to create the new page; otherwise returns false.
+*/
+
+/*!
+ \fn bool QPrintEngine::abort()
+
+ Instructs the print engine to abort the printing process. Returns
+ true if successful; otherwise returns false.
+*/
+
+/*!
+ \fn int QPrintEngine::metric(QPaintDevice::PaintDeviceMetric id) const
+
+ Returns the metric for the given \a id.
+*/
+
+/*!
+ \fn QPrinter::PrinterState QPrintEngine::printerState() const
+
+ Returns the current state of the printer being used by the print engine.
+*/
+
+/*!
+ \fn HDC QPrintEngine::getPrinterDC() const
+ \internal
+*/
+
+/*!
+ \fn void QPrintEngine::releasePrinterDC(HDC) const
+ \internal
+*/
+
+/*
+ Returns the dimensions for the given paper size, \a size, in millimeters.
+*/
+QSizeF qt_paperSizeToQSizeF(QPrinter::PaperSize size)
+{
+ if (size == QPrinter::Custom) return QSizeF(0, 0);
+ return QSizeF(qt_paperSizes[size][0], qt_paperSizes[size][1]);
+}
+
+/*
+ Returns the PaperSize type that matches \a size, where \a size
+ is in millimeters.
+
+ Because dimensions may not always be completely accurate (for
+ example when converting between units), a particular PaperSize
+ will be returned if it matches within -1/+1 millimeters.
+*/
+QPrinter::PaperSize qSizeFTopaperSize(const QSizeF& size)
+{
+ for (int i = 0; i < static_cast<int>(QPrinter::NPaperSize); ++i) {
+ if (qt_paperSizes[i][0] >= size.width() - 1 &&
+ qt_paperSizes[i][0] <= size.width() + 1 &&
+ qt_paperSizes[i][1] >= size.height() - 1 &&
+ qt_paperSizes[i][1] <= size.height() + 1) {
+ return QPrinter::PaperSize(i);
+ }
+ }
+
+ return QPrinter::Custom;
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_PRINTER
diff --git a/src/gui/painting/qprinter.h b/src/gui/painting/qprinter.h
new file mode 100644
index 0000000000..949c8f9b4f
--- /dev/null
+++ b/src/gui/painting/qprinter.h
@@ -0,0 +1,330 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QPRINTER_H
+#define QPRINTER_H
+
+#include <QtGui/qpaintdevice.h>
+#include <QtCore/qstring.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+#ifndef QT_NO_PRINTER
+
+#if defined(B0)
+#undef B0 // Terminal hang-up. We assume that you do not want that.
+#endif
+
+class QPrinterPrivate;
+class QPaintEngine;
+class QPrintEngine;
+class QPrinterInfo;
+
+class Q_GUI_EXPORT QPrinter : public QPaintDevice
+{
+ Q_DECLARE_PRIVATE(QPrinter)
+public:
+ enum PrinterMode { ScreenResolution, PrinterResolution, HighResolution };
+
+ explicit QPrinter(PrinterMode mode = ScreenResolution);
+ explicit QPrinter(const QPrinterInfo& printer, PrinterMode mode = ScreenResolution);
+ ~QPrinter();
+
+ int devType() const;
+
+ enum Orientation { Portrait, Landscape };
+
+#ifndef Q_QDOC
+ enum PageSize { A4, B5, Letter, Legal, Executive,
+ A0, A1, A2, A3, A5, A6, A7, A8, A9, B0, B1,
+ B10, B2, B3, B4, B6, B7, B8, B9, C5E, Comm10E,
+ DLE, Folio, Ledger, Tabloid, Custom, NPageSize = Custom, NPaperSize = Custom };
+ typedef PageSize PaperSize;
+#else
+ enum PageSize { A4, B5, Letter, Legal, Executive,
+ A0, A1, A2, A3, A5, A6, A7, A8, A9, B0, B1,
+ B10, B2, B3, B4, B6, B7, B8, B9, C5E, Comm10E,
+ DLE, Folio, Ledger, Tabloid, Custom, NPageSize = Custom };
+ enum PaperSize { A4, B5, Letter, Legal, Executive,
+ A0, A1, A2, A3, A5, A6, A7, A8, A9, B0, B1,
+ B10, B2, B3, B4, B6, B7, B8, B9, C5E, Comm10E,
+ DLE, Folio, Ledger, Tabloid, Custom, NPageSize = Custom, NPaperSize = Custom };
+#endif
+
+ enum PageOrder { FirstPageFirst,
+ LastPageFirst };
+
+ enum ColorMode { GrayScale,
+ Color };
+
+ enum PaperSource { OnlyOne,
+ Lower,
+ Middle,
+ Manual,
+ Envelope,
+ EnvelopeManual,
+ Auto,
+ Tractor,
+ SmallFormat,
+ LargeFormat,
+ LargeCapacity,
+ Cassette,
+ FormSource,
+ MaxPageSource
+ };
+
+ enum PrinterState { Idle,
+ Active,
+ Aborted,
+ Error };
+
+ enum OutputFormat { NativeFormat, PdfFormat, PostScriptFormat };
+
+ // ### Qt 5: Merge with QAbstractPrintDialog::PrintRange
+ enum PrintRange { AllPages, Selection, PageRange };
+
+ enum Unit {
+ Millimeter,
+ Point,
+ Inch,
+ Pica,
+ Didot,
+ Cicero,
+ DevicePixel
+ };
+
+ enum DuplexMode {
+ DuplexNone = 0,
+ DuplexAuto,
+ DuplexLongSide,
+ DuplexShortSide
+ };
+
+#ifdef QT3_SUPPORT
+ enum PrinterOption { PrintToFile, PrintSelection, PrintPageRange };
+#endif // QT3_SUPPORT
+
+ void setOutputFormat(OutputFormat format);
+ OutputFormat outputFormat() const;
+
+ void setPrinterName(const QString &);
+ QString printerName() const;
+
+ bool isValid() const;
+
+ void setOutputFileName(const QString &);
+ QString outputFileName()const;
+
+ void setPrintProgram(const QString &);
+ QString printProgram() const;
+
+ void setDocName(const QString &);
+ QString docName() const;
+
+ void setCreator(const QString &);
+ QString creator() const;
+
+ void setOrientation(Orientation);
+ Orientation orientation() const;
+
+ void setPageSize(PageSize);
+ PageSize pageSize() const;
+
+ void setPaperSize(PaperSize);
+ PaperSize paperSize() const;
+
+ void setPaperSize(const QSizeF &paperSize, Unit unit);
+ QSizeF paperSize(Unit unit) const;
+
+ void setPageOrder(PageOrder);
+ PageOrder pageOrder() const;
+
+ void setResolution(int);
+ int resolution() const;
+
+ void setColorMode(ColorMode);
+ ColorMode colorMode() const;
+
+ void setCollateCopies(bool collate);
+ bool collateCopies() const;
+
+ void setFullPage(bool);
+ bool fullPage() const;
+
+ void setNumCopies(int);
+ int numCopies() const;
+
+ void setPaperSource(PaperSource);
+ PaperSource paperSource() const;
+
+ void setDuplex(DuplexMode duplex);
+ DuplexMode duplex() const;
+
+ QList<int> supportedResolutions() const;
+
+#ifdef Q_WS_WIN
+ QList<PaperSource> supportedPaperSources() const;
+#endif
+
+ void setFontEmbeddingEnabled(bool enable);
+ bool fontEmbeddingEnabled() const;
+
+ void setDoubleSidedPrinting(bool enable);
+ bool doubleSidedPrinting() const;
+
+#ifdef Q_WS_WIN
+ void setWinPageSize(int winPageSize);
+ int winPageSize() const;
+#endif
+
+ QRect paperRect() const;
+ QRect pageRect() const;
+ QRectF paperRect(Unit) const;
+ QRectF pageRect(Unit) const;
+
+#if !defined(Q_WS_WIN) || defined(qdoc)
+ QString printerSelectionOption() const;
+ void setPrinterSelectionOption(const QString &);
+#endif
+
+ bool newPage();
+ bool abort();
+
+ PrinterState printerState() const;
+
+ QPaintEngine *paintEngine() const;
+ QPrintEngine *printEngine() const;
+
+#ifdef Q_WS_WIN
+ HDC getDC() const;
+ void releaseDC(HDC hdc) const;
+#endif
+
+ void setFromTo(int fromPage, int toPage);
+ int fromPage() const;
+ int toPage() const;
+
+ void setPrintRange(PrintRange range);
+ PrintRange printRange() const;
+
+ void setPageMargins(qreal left, qreal top, qreal right, qreal bottom, Unit unit);
+ void getPageMargins(qreal *left, qreal *top, qreal *right, qreal *bottom, Unit unit) const;
+
+#ifdef QT3_SUPPORT
+#ifdef Q_WS_MAC
+ QT3_SUPPORT bool pageSetup(QWidget *parent = 0);
+ QT3_SUPPORT bool printSetup(QWidget *parent = 0);
+#endif
+
+ QT3_SUPPORT bool setup(QWidget *parent = 0);
+
+ QT3_SUPPORT void setMinMax(int minPage, int maxPage);
+ QT3_SUPPORT int minPage() const;
+ QT3_SUPPORT int maxPage() const;
+
+ QT3_SUPPORT void setCollateCopiesEnabled(bool);
+ QT3_SUPPORT bool collateCopiesEnabled() const;
+
+ QT3_SUPPORT void setOptionEnabled(PrinterOption, bool enable);
+ QT3_SUPPORT bool isOptionEnabled(PrinterOption) const;
+
+ inline QT3_SUPPORT QSize margins() const;
+ inline QT3_SUPPORT void margins(uint *top, uint *left, uint *bottom, uint *right) const;
+
+ inline QT3_SUPPORT bool aborted() { return printerState() == Aborted; }
+
+ QT3_SUPPORT void setOutputToFile(bool);
+ inline QT3_SUPPORT bool outputToFile() const { return !outputFileName().isEmpty(); }
+#endif
+
+protected:
+ int metric(PaintDeviceMetric) const;
+ void setEngines(QPrintEngine *printEngine, QPaintEngine *paintEngine);
+
+private:
+ void init(PrinterMode mode);
+
+ Q_DISABLE_COPY(QPrinter)
+
+ QPrinterPrivate *d_ptr;
+
+ friend class QPrintDialogPrivate;
+ friend class QAbstractPrintDialog;
+ friend class QAbstractPrintDialogPrivate;
+ friend class QPrintPreviewWidgetPrivate;
+ friend class QTextDocument;
+ friend class QPageSetupWidget;
+};
+
+#ifdef QT3_SUPPORT
+inline QSize QPrinter::margins() const
+{
+ QRect page = pageRect();
+ QRect paper = paperRect();
+ return QSize(page.left() - paper.left(), page.top() - paper.top());
+}
+
+inline void QPrinter::margins(uint *top, uint *left, uint *bottom, uint *right) const
+{
+ QRect page = pageRect();
+ QRect paper = paperRect();
+ if (top)
+ *top = page.top() - paper.top();
+ if (left)
+ *left = page.left() - paper.left();
+ if (bottom)
+ *bottom = paper.bottom() - page.bottom();
+ if (right)
+ *right = paper.right() - page.right();
+}
+#endif
+
+#endif // QT_NO_PRINTER
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QPRINTER_H
diff --git a/src/gui/painting/qprinter_p.h b/src/gui/painting/qprinter_p.h
new file mode 100644
index 0000000000..adaeab469a
--- /dev/null
+++ b/src/gui/painting/qprinter_p.h
@@ -0,0 +1,140 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QPRINTER_P_H
+#define QPRINTER_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 "QtCore/qglobal.h"
+
+#ifndef QT_NO_PRINTER
+
+#include "QtGui/qprinter.h"
+#include "QtGui/qprintengine.h"
+#include "QtGui/qprintdialog.h"
+#include "QtCore/qpointer.h"
+
+#include <limits.h>
+
+QT_BEGIN_NAMESPACE
+
+class QPrintEngine;
+class QPreviewPaintEngine;
+class QPicture;
+
+class QPrinterPrivate
+{
+ Q_DECLARE_PUBLIC(QPrinter)
+public:
+ QPrinterPrivate(QPrinter *printer)
+ : printEngine(0)
+ , paintEngine(0)
+ , q_ptr(printer)
+ , options(QAbstractPrintDialog::PrintToFile | QAbstractPrintDialog::PrintPageRange |
+ QAbstractPrintDialog::PrintCollateCopies | QAbstractPrintDialog::PrintShowPageSize)
+ , printRange(QAbstractPrintDialog::AllPages)
+ , minPage(1)
+ , maxPage(INT_MAX)
+ , fromPage(0)
+ , toPage(0)
+ , use_default_engine(true)
+ , validPrinter(false)
+ , hasCustomPageMargins(false)
+ , hasUserSetPageSize(false)
+ {
+ }
+
+ ~QPrinterPrivate() {
+
+ }
+
+ void createDefaultEngines();
+#ifndef QT_NO_PRINTPREVIEWWIDGET
+ QList<const QPicture *> previewPages() const;
+ void setPreviewMode(bool);
+#endif
+
+ void addToManualSetList(QPrintEngine::PrintEnginePropertyKey key);
+
+ QPrinter::PrinterMode printerMode;
+ QPrinter::OutputFormat outputFormat;
+ QPrintEngine *printEngine;
+ QPaintEngine *paintEngine;
+
+ QPrintEngine *realPrintEngine;
+ QPaintEngine *realPaintEngine;
+#ifndef QT_NO_PRINTPREVIEWWIDGET
+ QPreviewPaintEngine *previewEngine;
+#endif
+
+ QPrinter *q_ptr;
+
+ QAbstractPrintDialog::PrintDialogOptions options;
+ QAbstractPrintDialog::PrintRange printRange;
+ int minPage, maxPage, fromPage, toPage;
+
+ uint use_default_engine : 1;
+ uint had_default_engines : 1;
+
+ uint validPrinter : 1;
+ uint hasCustomPageMargins : 1;
+ uint hasUserSetPageSize : 1;
+
+ // Used to remember which properties have been manually set by the user.
+ QList<QPrintEngine::PrintEnginePropertyKey> manualSetList;
+};
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_PRINTER
+
+#endif // QPRINTER_P_H
diff --git a/src/gui/painting/qprinterinfo.h b/src/gui/painting/qprinterinfo.h
new file mode 100644
index 0000000000..b8263063b4
--- /dev/null
+++ b/src/gui/painting/qprinterinfo.h
@@ -0,0 +1,88 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QPRINTERINFO_H
+#define QPRINTERINFO_H
+
+#include <QtGui/QPrinter>
+#include <QtCore/QList>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+#ifndef QT_NO_PRINTER
+class QPrinterInfoPrivate;
+class Q_GUI_EXPORT QPrinterInfo
+{
+Q_DECLARE_PRIVATE(QPrinterInfo)
+
+public:
+ QPrinterInfo();
+ QPrinterInfo(const QPrinterInfo& src);
+ QPrinterInfo(const QPrinter& printer);
+ ~QPrinterInfo();
+
+ QPrinterInfo& operator=(const QPrinterInfo& src);
+
+ QString printerName() const;
+ bool isNull() const;
+ bool isDefault() const;
+ QList<QPrinter::PaperSize> supportedPaperSizes() const;
+
+ static QList<QPrinterInfo> availablePrinters();
+ static QPrinterInfo defaultPrinter();
+
+private:
+ QPrinterInfo(const QString& name);
+
+ QPrinterInfoPrivate* d_ptr;
+};
+
+#endif // QT_NO_PRINTER
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QPRINTERINFO_H
diff --git a/src/gui/painting/qprinterinfo_mac.cpp b/src/gui/painting/qprinterinfo_mac.cpp
new file mode 100644
index 0000000000..ecd4b5b1a0
--- /dev/null
+++ b/src/gui/painting/qprinterinfo_mac.cpp
@@ -0,0 +1,241 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qprinterinfo.h"
+
+#include "private/qt_mac_p.h"
+
+QT_BEGIN_NAMESPACE
+
+#ifndef QT_NO_PRINTER
+
+class QPrinterInfoPrivate
+{
+Q_DECLARE_PUBLIC(QPrinterInfo)
+public:
+ ~QPrinterInfoPrivate();
+ QPrinterInfoPrivate();
+ QPrinterInfoPrivate(const QString& name);
+
+private:
+ QPrinterInfo* q_ptr;
+
+ QString m_name;
+ bool m_default;
+ bool m_isNull;
+};
+
+static QPrinterInfoPrivate nullQPrinterInfoPrivate;
+
+extern QPrinter::PaperSize qSizeFTopaperSize(const QSizeF& size);
+
+/////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////
+
+QList<QPrinterInfo> QPrinterInfo::availablePrinters()
+{
+ QList<QPrinterInfo> printers;
+
+ OSStatus status = noErr;
+ QCFType<CFArrayRef> printerList;
+ status = PMServerCreatePrinterList(kPMServerLocal, &printerList);
+ if (status == noErr) {
+ CFIndex count = CFArrayGetCount(printerList);
+ for (CFIndex i=0; i<count; ++i) {
+ PMPrinter printer = static_cast<PMPrinter>(const_cast<void *>(CFArrayGetValueAtIndex(printerList, i)));
+ QString name = QCFString::toQString(PMPrinterGetName(printer));
+ printers.append(QPrinterInfo(name));
+ if (PMPrinterIsDefault(printer)) {
+ printers[i].d_ptr->m_default = true;
+ }
+ }
+ }
+
+ return printers;
+}
+
+QPrinterInfo QPrinterInfo::defaultPrinter(){
+ QList<QPrinterInfo> printers = availablePrinters();
+ for (int c = 0; c < printers.size(); ++c) {
+ if (printers[c].isDefault()) {
+ return printers[c];
+ }
+ }
+ return QPrinterInfo();
+}
+
+/////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////
+
+QPrinterInfo::QPrinterInfo(const QPrinter& prn)
+{
+ d_ptr = &nullQPrinterInfoPrivate;
+ QList<QPrinterInfo> list = availablePrinters();
+ for (int c = 0; c < list.size(); ++c) {
+ if (prn.printerName() == list[c].printerName()) {
+ *this = list[c];
+ return;
+ }
+ }
+
+ *this = QPrinterInfo();
+}
+
+QPrinterInfo::~QPrinterInfo()
+{
+ if (d_ptr != &nullQPrinterInfoPrivate)
+ delete d_ptr;
+}
+
+QPrinterInfo::QPrinterInfo()
+{
+ d_ptr = &nullQPrinterInfoPrivate;
+}
+
+QPrinterInfo::QPrinterInfo(const QString& name)
+{
+ d_ptr = new QPrinterInfoPrivate(name);
+ d_ptr->q_ptr = this;
+}
+
+QPrinterInfo::QPrinterInfo(const QPrinterInfo& src)
+{
+ d_ptr = &nullQPrinterInfoPrivate;
+ *this = src;
+}
+
+QPrinterInfo& QPrinterInfo::operator=(const QPrinterInfo& src)
+{
+ Q_ASSERT(d_ptr);
+ if (d_ptr != &nullQPrinterInfoPrivate)
+ delete d_ptr;
+ d_ptr = new QPrinterInfoPrivate(*src.d_ptr);
+ d_ptr->q_ptr = this;
+ return *this;
+}
+
+QString QPrinterInfo::printerName() const
+{
+ const Q_D(QPrinterInfo);
+ return d->m_name;
+}
+
+bool QPrinterInfo::isNull() const
+{
+ const Q_D(QPrinterInfo);
+ return d->m_isNull;
+}
+
+bool QPrinterInfo::isDefault() const
+{
+ const Q_D(QPrinterInfo);
+ return d->m_default;
+}
+
+QList<QPrinter::PaperSize> QPrinterInfo::supportedPaperSizes() const
+{
+#if (MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_4)
+ return QList<QPrinter::PaperSize>();
+#else
+ if (QSysInfo::MacintoshVersion <= QSysInfo::MV_10_4)
+ return QList<QPrinter::PaperSize>();
+
+ const Q_D(QPrinterInfo);
+
+ PMPrinter cfPrn = PMPrinterCreateFromPrinterID(
+ QCFString::toCFStringRef(d->m_name));
+
+ if (!cfPrn) return QList<QPrinter::PaperSize>();
+
+ CFArrayRef array;
+ OSStatus status = PMPrinterGetPaperList(cfPrn, &array);
+
+ if (status != 0) {
+ PMRelease(cfPrn);
+ return QList<QPrinter::PaperSize>();
+ }
+
+ QList<QPrinter::PaperSize> paperList;
+ int count = CFArrayGetCount(array);
+ for (int c = 0; c < count; c++) {
+ PMPaper paper = static_cast<PMPaper>(
+ const_cast<void*>(
+ CFArrayGetValueAtIndex(array, c)));
+ double width, height;
+ status = PMPaperGetWidth(paper, &width);
+ status |= PMPaperGetHeight(paper, &height);
+ if (status != 0) continue;
+
+ QSizeF size(width * 0.3527, height * 0.3527);
+ paperList.append(qSizeFTopaperSize(size));
+ }
+
+ PMRelease(cfPrn);
+
+ return paperList;
+#endif // MAC_OS_X_VERSION_MAX_ALLOWED
+}
+
+/////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////
+
+QPrinterInfoPrivate::QPrinterInfoPrivate() :
+ q_ptr(NULL),
+ m_default(false),
+ m_isNull(true)
+{
+}
+
+QPrinterInfoPrivate::QPrinterInfoPrivate(const QString& name) :
+ q_ptr(NULL),
+ m_name(name),
+ m_default(false),
+ m_isNull(false)
+{
+}
+
+QPrinterInfoPrivate::~QPrinterInfoPrivate()
+{
+}
+
+#endif // QT_NO_PRINTER
+
+QT_END_NAMESPACE
diff --git a/src/gui/painting/qprinterinfo_unix.cpp b/src/gui/painting/qprinterinfo_unix.cpp
new file mode 100644
index 0000000000..0f33ea70e7
--- /dev/null
+++ b/src/gui/painting/qprinterinfo_unix.cpp
@@ -0,0 +1,1141 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qprinterinfo.h"
+
+#include <qfile.h>
+#include <qfileinfo.h>
+#include <qdir.h>
+#include <qprintdialog.h>
+#include <qlibrary.h>
+#include <qtextstream.h>
+
+#if !defined(QT_NO_CUPS) && !defined(QT_NO_LIBRARY)
+# include <private/qcups_p.h>
+# include <cups/cups.h>
+# include <private/qpdf_p.h>
+#endif
+
+#include <private/qprinterinfo_unix_p.h>
+
+QT_BEGIN_NAMESPACE
+
+#ifndef QT_NO_PRINTER
+
+class QPrinterInfoPrivate
+{
+Q_DECLARE_PUBLIC(QPrinterInfo)
+public:
+ QPrinterInfoPrivate();
+ QPrinterInfoPrivate(const QString& name);
+ ~QPrinterInfoPrivate();
+
+ static QPrinter::PaperSize string2PaperSize(const QString& str);
+ static QString pageSize2String(QPrinter::PaperSize size);
+
+private:
+ QString m_name;
+ bool m_isNull;
+ bool m_default;
+ QList<QPrinter::PaperSize> m_paperSizes;
+
+ QPrinterInfo* q_ptr;
+};
+
+static QPrinterInfoPrivate nullQPrinterInfoPrivate;
+
+/////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////
+
+void qt_perhapsAddPrinter(QList<QPrinterDescription> *printers, const QString &name,
+ QString host, QString comment,
+ QStringList aliases)
+{
+ for (int i = 0; i < printers->size(); ++i)
+ if (printers->at(i).samePrinter(name))
+ return;
+
+#ifndef QT_NO_PRINTDIALOG
+ if (host.isEmpty())
+ host = QPrintDialog::tr("locally connected");
+#endif
+ printers->append(QPrinterDescription(name.simplified(), host.simplified(), comment.simplified(), aliases));
+}
+
+void qt_parsePrinterDesc(QString printerDesc, QList<QPrinterDescription> *printers)
+{
+ if (printerDesc.length() < 1)
+ return;
+
+ printerDesc = printerDesc.simplified();
+ int i = printerDesc.indexOf(QLatin1Char(':'));
+ QString printerName, printerComment, printerHost;
+ QStringList aliases;
+
+ if (i >= 0) {
+ // have ':' want '|'
+ int j = printerDesc.indexOf(QLatin1Char('|'));
+ if (j > 0 && j < i) {
+ printerName = printerDesc.left(j);
+ aliases = printerDesc.mid(j + 1, i - j - 1).split(QLatin1Char('|'));
+#ifndef QT_NO_PRINTDIALOG
+ // try extracting a comment from the aliases
+ printerComment = QPrintDialog::tr("Aliases: %1")
+ .arg(aliases.join(QLatin1String(", ")));
+#endif
+ } else {
+ printerName = printerDesc.left(i);
+ }
+ // look for lprng pseudo all printers entry
+ i = printerDesc.indexOf(QRegExp(QLatin1String(": *all *=")));
+ if (i >= 0)
+ printerName = QString();
+ // look for signs of this being a remote printer
+ i = printerDesc.indexOf(QRegExp(QLatin1String(": *rm *=")));
+ if (i >= 0) {
+ // point k at the end of remote host name
+ while (printerDesc[i] != QLatin1Char('='))
+ i++;
+ while (printerDesc[i] == QLatin1Char('=') || printerDesc[i].isSpace())
+ i++;
+ j = i;
+ while (j < (int)printerDesc.length() && printerDesc[j] != QLatin1Char(':'))
+ j++;
+
+ // and stuff that into the string
+ printerHost = printerDesc.mid(i, j - i);
+ }
+ }
+ if (printerName.length())
+ qt_perhapsAddPrinter(printers, printerName, printerHost, printerComment,
+ aliases);
+}
+
+int qt_parsePrintcap(QList<QPrinterDescription> *printers, const QString& fileName)
+{
+ QFile printcap(fileName);
+ if (!printcap.open(QIODevice::ReadOnly))
+ return NotFound;
+
+ char *line_ascii = new char[1025];
+ line_ascii[1024] = '\0';
+
+ QString printerDesc;
+ bool atEnd = false;
+
+ while (!atEnd) {
+ if (printcap.atEnd() || printcap.readLine(line_ascii, 1024) <= 0)
+ atEnd = true;
+ QString line = QString::fromLocal8Bit(line_ascii);
+ line = line.trimmed();
+ if (line.length() >= 1 && line[int(line.length()) - 1] == QLatin1Char('\\'))
+ line.chop(1);
+ if (line[0] == QLatin1Char('#')) {
+ if (!atEnd)
+ continue;
+ } else if (line[0] == QLatin1Char('|') || line[0] == QLatin1Char(':')
+ || line.isEmpty()) {
+ printerDesc += line;
+ if (!atEnd)
+ continue;
+ }
+
+ qt_parsePrinterDesc(printerDesc, printers);
+
+ // add the first line of the new printer definition
+ printerDesc = line;
+ }
+ delete[] line_ascii;
+ return Success;
+}
+
+/*!
+ \internal
+
+ Checks $HOME/.printers for a line matching '_default <name>' (where
+ <name> does not contain any white space). The first such match
+ results in <name> being returned.
+ If no lines match then an empty string is returned.
+*/
+QString qt_getDefaultFromHomePrinters()
+{
+ QFile file(QDir::homePath() + QLatin1String("/.printers"));
+ if (!file.open(QIODevice::ReadOnly))
+ return QString();
+ QString all(QLatin1String(file.readAll()));
+ QStringList words = all.split(QRegExp(QLatin1String("\\W+")), QString::SkipEmptyParts);
+ const int i = words.indexOf(QLatin1String("_default"));
+ if (i != -1 && i < words.size() - 1)
+ return words.at(i + 1);
+ return QString();
+}
+
+// solaris, not 2.6
+void qt_parseEtcLpPrinters(QList<QPrinterDescription> *printers)
+{
+ QDir lp(QLatin1String("/etc/lp/printers"));
+ QFileInfoList dirs = lp.entryInfoList();
+ if (dirs.isEmpty())
+ return;
+
+ QString tmp;
+ for (int i = 0; i < dirs.size(); ++i) {
+ QFileInfo printer = dirs.at(i);
+ if (printer.isDir()) {
+ tmp.sprintf("/etc/lp/printers/%s/configuration",
+ printer.fileName().toAscii().data());
+ QFile configuration(tmp);
+ char *line = new char[1025];
+ QString remote(QLatin1String("Remote:"));
+ QString contentType(QLatin1String("Content types:"));
+ QString printerHost;
+ bool canPrintPostscript = false;
+ if (configuration.open(QIODevice::ReadOnly)) {
+ while (!configuration.atEnd() &&
+ configuration.readLine(line, 1024) > 0) {
+ if (QString::fromLatin1(line).startsWith(remote)) {
+ const char *p = line;
+ while (*p != ':')
+ p++;
+ p++;
+ while (isspace((uchar) *p))
+ p++;
+ printerHost = QString::fromLocal8Bit(p);
+ printerHost = printerHost.simplified();
+ } else if (QString::fromLatin1(line).startsWith(contentType)) {
+ char *p = line;
+ while (*p != ':')
+ p++;
+ p++;
+ char *e;
+ while (*p) {
+ while (isspace((uchar) *p))
+ p++;
+ if (*p) {
+ char s;
+ e = p;
+ while (isalnum((uchar) *e))
+ e++;
+ s = *e;
+ *e = '\0';
+ if (!qstrcmp(p, "postscript") ||
+ !qstrcmp(p, "any"))
+ canPrintPostscript = true;
+ *e = s;
+ if (s == ',')
+ e++;
+ p = e;
+ }
+ }
+ }
+ }
+ if (canPrintPostscript)
+ qt_perhapsAddPrinter(printers, printer.fileName(),
+ printerHost, QLatin1String(""));
+ }
+ delete[] line;
+ }
+ }
+}
+
+// solaris 2.6
+char *qt_parsePrintersConf(QList<QPrinterDescription> *printers, bool *found)
+{
+ QFile pc(QLatin1String("/etc/printers.conf"));
+ if (!pc.open(QIODevice::ReadOnly)) {
+ if (found)
+ *found = false;
+ return 0;
+ }
+ if (found)
+ *found = true;
+
+ char *line = new char[1025];
+ line[1024] = '\0';
+
+ QString printerDesc;
+ int lineLength = 0;
+
+ char *defaultPrinter = 0;
+
+ while (!pc.atEnd() &&
+ (lineLength=pc.readLine(line, 1024)) > 0) {
+ if (*line == '#') {
+ *line = '\0';
+ lineLength = 0;
+ }
+ if (lineLength >= 2 && line[lineLength-2] == '\\') {
+ line[lineLength-2] = '\0';
+ printerDesc += QString::fromLocal8Bit(line);
+ } else {
+ printerDesc += QString::fromLocal8Bit(line);
+ printerDesc = printerDesc.simplified();
+ int i = printerDesc.indexOf(QLatin1Char(':'));
+ QString printerName, printerHost, printerComment;
+ QStringList aliases;
+ if (i >= 0) {
+ // have : want |
+ int j = printerDesc.indexOf(QLatin1Char('|'));
+ if (j >= i)
+ j = -1;
+ printerName = printerDesc.mid(0, j < 0 ? i : j);
+ if (printerName == QLatin1String("_default")) {
+ i = printerDesc.indexOf(
+ QRegExp(QLatin1String(": *use *=")));
+ while (printerDesc[i] != QLatin1Char('='))
+ i++;
+ while (printerDesc[i] == QLatin1Char('=') || printerDesc[i].isSpace())
+ i++;
+ j = i;
+ while (j < (int)printerDesc.length() &&
+ printerDesc[j] != QLatin1Char(':') && printerDesc[j] != QLatin1Char(','))
+ j++;
+ // that's our default printer
+ defaultPrinter =
+ qstrdup(printerDesc.mid(i, j-i).toAscii().data());
+ printerName = QString();
+ printerDesc = QString();
+ } else if (printerName == QLatin1String("_all")) {
+ // skip it.. any other cases we want to skip?
+ printerName = QString();
+ printerDesc = QString();
+ }
+
+ if (j > 0) {
+ // try extracting a comment from the aliases
+ aliases = printerDesc.mid(j + 1, i - j - 1).split(QLatin1Char('|'));
+#ifndef QT_NO_PRINTDIALOG
+ printerComment = QPrintDialog::tr("Aliases: %1")
+ .arg(aliases.join(QLatin1String(", ")));
+#endif
+ }
+ // look for signs of this being a remote printer
+ i = printerDesc.indexOf(
+ QRegExp(QLatin1String(": *bsdaddr *=")));
+ if (i >= 0) {
+ // point k at the end of remote host name
+ while (printerDesc[i] != QLatin1Char('='))
+ i++;
+ while (printerDesc[i] == QLatin1Char('=') || printerDesc[i].isSpace())
+ i++;
+ j = i;
+ while (j < (int)printerDesc.length() &&
+ printerDesc[j] != QLatin1Char(':') && printerDesc[j] != QLatin1Char(','))
+ j++;
+ // and stuff that into the string
+ printerHost = printerDesc.mid(i, j-i);
+ // maybe stick the remote printer name into the comment
+ if (printerDesc[j] == QLatin1Char(',')) {
+ i = ++j;
+ while (printerDesc[i].isSpace())
+ i++;
+ j = i;
+ while (j < (int)printerDesc.length() &&
+ printerDesc[j] != QLatin1Char(':') && printerDesc[j] != QLatin1Char(','))
+ j++;
+ if (printerName != printerDesc.mid(i, j-i)) {
+ printerComment =
+ QLatin1String("Remote name: ");
+ printerComment += printerDesc.mid(i, j-i);
+ }
+ }
+ }
+ }
+ if (printerComment == QLatin1String(":"))
+ printerComment = QString(); // for cups
+ if (printerName.length())
+ qt_perhapsAddPrinter(printers, printerName, printerHost,
+ printerComment, aliases);
+ // chop away the line, for processing the next one
+ printerDesc = QString();
+ }
+ }
+ delete[] line;
+ return defaultPrinter;
+}
+
+#ifndef QT_NO_NIS
+
+#if defined(Q_C_CALLBACKS)
+extern "C" {
+#endif
+
+int qt_pd_foreach(int /*status */, char * /*key */, int /*keyLen */,
+ char *val, int valLen, char *data)
+{
+ qt_parsePrinterDesc(QString::fromLatin1(val, valLen), (QList<QPrinterDescription> *)data);
+ return 0;
+}
+
+#if defined(Q_C_CALLBACKS)
+}
+#endif
+
+int qt_retrieveNisPrinters(QList<QPrinterDescription> *printers)
+{
+ typedef int (*WildCast)(int, char *, int, char *, int, char *);
+ char printersConfByname[] = "printers.conf.byname";
+ char *domain;
+ int err;
+
+ QLibrary lib(QLatin1String("nsl"));
+ typedef int (*ypGetDefaultDomain)(char **);
+ ypGetDefaultDomain _ypGetDefaultDomain = (ypGetDefaultDomain)lib.resolve("yp_get_default_domain");
+ typedef int (*ypAll)(const char *, const char *, const struct ypall_callback *);
+ ypAll _ypAll = (ypAll)lib.resolve("yp_all");
+
+ if (_ypGetDefaultDomain && _ypAll) {
+ err = _ypGetDefaultDomain(&domain);
+ if (err == 0) {
+ ypall_callback cb;
+ // wild cast to support K&R-style system headers
+ (WildCast &) cb.foreach = (WildCast) qt_pd_foreach;
+ cb.data = (char *) printers;
+ err = _ypAll(domain, printersConfByname, &cb);
+ }
+ if (!err)
+ return Success;
+ }
+ return Unavail;
+}
+
+#endif // QT_NO_NIS
+
+char *qt_parseNsswitchPrintersEntry(QList<QPrinterDescription> *printers, char *line)
+{
+#define skipSpaces() \
+ while (line[k] != '\0' && isspace((uchar) line[k])) \
+ k++
+
+ char *defaultPrinter = 0;
+ bool stop = false;
+ int lastStatus = NotFound;
+
+ int k = 8;
+ skipSpaces();
+ if (line[k] != ':')
+ return 0;
+ k++;
+
+ char *cp = strchr(line, '#');
+ if (cp != 0)
+ *cp = '\0';
+
+ while (line[k] != '\0') {
+ if (isspace((uchar) line[k])) {
+ k++;
+ } else if (line[k] == '[') {
+ k++;
+ skipSpaces();
+ while (line[k] != '\0') {
+ char status = tolower(line[k]);
+ char action = '?';
+
+ while (line[k] != '=' && line[k] != ']' && line[k] != '\0')
+ k++;
+ if (line[k] == '=') {
+ k++;
+ skipSpaces();
+ action = tolower(line[k]);
+ while (line[k] != '\0' && !isspace((uchar) line[k]) && line[k] != ']')
+ k++;
+ } else if (line[k] == ']') {
+ k++;
+ break;
+ }
+ skipSpaces();
+
+ if (lastStatus == status)
+ stop = (action == (char) Return);
+ }
+ } else {
+ if (stop)
+ break;
+
+ QByteArray source;
+ while (line[k] != '\0' && !isspace((uchar) line[k]) && line[k] != '[') {
+ source += line[k];
+ k++;
+ }
+
+ if (source == "user") {
+ lastStatus = qt_parsePrintcap(printers,
+ QDir::homePath() + QLatin1String("/.printers"));
+ } else if (source == "files") {
+ bool found;
+ defaultPrinter = qt_parsePrintersConf(printers, &found);
+ if (found)
+ lastStatus = Success;
+#ifndef QT_NO_NIS
+ } else if (source == "nis") {
+ lastStatus = qt_retrieveNisPrinters(printers);
+#endif
+ } else {
+ // nisplus, dns, etc., are not implemented yet
+ lastStatus = NotFound;
+ }
+ stop = (lastStatus == Success);
+ }
+ }
+ return defaultPrinter;
+}
+
+char *qt_parseNsswitchConf(QList<QPrinterDescription> *printers)
+{
+ QFile nc(QLatin1String("/etc/nsswitch.conf"));
+ if (!nc.open(QIODevice::ReadOnly))
+ return 0;
+
+ char *defaultPrinter = 0;
+
+ char *line = new char[1025];
+ line[1024] = '\0';
+
+ while (!nc.atEnd() &&
+ nc.readLine(line, 1024) > 0) {
+ if (qstrncmp(line, "printers", 8) == 0) {
+ defaultPrinter = qt_parseNsswitchPrintersEntry(printers, line);
+ delete[] line;
+ return defaultPrinter;
+ }
+ }
+
+ strcpy(line, "printers: user files nis nisplus xfn");
+ defaultPrinter = qt_parseNsswitchPrintersEntry(printers, line);
+ delete[] line;
+ return defaultPrinter;
+}
+
+// HP-UX
+void qt_parseEtcLpMember(QList<QPrinterDescription> *printers)
+{
+ QDir lp(QLatin1String("/etc/lp/member"));
+ if (!lp.exists())
+ return;
+ QFileInfoList dirs = lp.entryInfoList();
+ if (dirs.isEmpty())
+ return;
+
+#ifdef QT_NO_PRINTDIALOG
+ Q_UNUSED(printers);
+#else
+ QString tmp;
+ for (int i = 0; i < dirs.size(); ++i) {
+ QFileInfo printer = dirs.at(i);
+ // I haven't found any real documentation, so I'm guessing that
+ // since lpstat uses /etc/lp/member rather than one of the
+ // other directories, it's the one to use. I did not find a
+ // decent way to locate aliases and remote printers.
+ if (printer.isFile())
+ qt_perhapsAddPrinter(printers, printer.fileName(),
+ QPrintDialog::tr("unknown"),
+ QLatin1String(""));
+ }
+#endif
+}
+
+// IRIX 6.x
+void qt_parseSpoolInterface(QList<QPrinterDescription> *printers)
+{
+ QDir lp(QLatin1String("/usr/spool/lp/interface"));
+ if (!lp.exists())
+ return;
+ QFileInfoList files = lp.entryInfoList();
+ if(files.isEmpty())
+ return;
+
+ for (int i = 0; i < files.size(); ++i) {
+ QFileInfo printer = files.at(i);
+
+ if (!printer.isFile())
+ continue;
+
+ // parse out some information
+ QFile configFile(printer.filePath());
+ if (!configFile.open(QIODevice::ReadOnly))
+ continue;
+
+ QByteArray line;
+ line.resize(1025);
+ QString namePrinter;
+ QString hostName;
+ QString hostPrinter;
+ QString printerType;
+
+ QString nameKey(QLatin1String("NAME="));
+ QString typeKey(QLatin1String("TYPE="));
+ QString hostKey(QLatin1String("HOSTNAME="));
+ QString hostPrinterKey(QLatin1String("HOSTPRINTER="));
+
+ while (!configFile.atEnd() &&
+ (configFile.readLine(line.data(), 1024)) > 0) {
+ QString uline = QString::fromLocal8Bit(line);
+ if (uline.startsWith(typeKey) ) {
+ printerType = uline.mid(nameKey.length());
+ printerType = printerType.simplified();
+ } else if (uline.startsWith(hostKey)) {
+ hostName = uline.mid(hostKey.length());
+ hostName = hostName.simplified();
+ } else if (uline.startsWith(hostPrinterKey)) {
+ hostPrinter = uline.mid(hostPrinterKey.length());
+ hostPrinter = hostPrinter.simplified();
+ } else if (uline.startsWith(nameKey)) {
+ namePrinter = uline.mid(nameKey.length());
+ namePrinter = namePrinter.simplified();
+ }
+ }
+ configFile.close();
+
+ printerType = printerType.trimmed();
+ if (printerType.indexOf(QLatin1String("postscript"), 0, Qt::CaseInsensitive) < 0)
+ continue;
+
+ int ii = 0;
+ while ((ii = namePrinter.indexOf(QLatin1Char('"'), ii)) >= 0)
+ namePrinter.remove(ii, 1);
+
+ if (hostName.isEmpty() || hostPrinter.isEmpty()) {
+ qt_perhapsAddPrinter(printers, printer.fileName(),
+ QLatin1String(""), namePrinter);
+ } else {
+ QString comment;
+ comment = namePrinter;
+ comment += QLatin1String(" (");
+ comment += hostPrinter;
+ comment += QLatin1Char(')');
+ qt_perhapsAddPrinter(printers, printer.fileName(),
+ hostName, comment);
+ }
+ }
+}
+
+
+// Every unix must have its own. It's a standard. Here is AIX.
+void qt_parseQconfig(QList<QPrinterDescription> *printers)
+{
+ QFile qconfig(QLatin1String("/etc/qconfig"));
+ if (!qconfig.open(QIODevice::ReadOnly))
+ return;
+
+ QTextStream ts(&qconfig);
+ QString line;
+
+ QString stanzaName; // either a queue or a device name
+ bool up = true; // queue up? default true, can be false
+ QString remoteHost; // null if local
+ QString deviceName; // null if remote
+
+ QRegExp newStanza(QLatin1String("^[0-z\\-]*:$"));
+
+ // our basic strategy here is to process each line, detecting new
+ // stanzas. each time we see a new stanza, we check if the
+ // previous stanza was a valid queue for a) a remote printer or b)
+ // a local printer. if it wasn't, we assume that what we see is
+ // the start of the first stanza, or that the previous stanza was
+ // a device stanza, or that there is some syntax error (we don't
+ // report those).
+
+ do {
+ line = ts.readLine();
+ bool indented = line[0].isSpace();
+ line = line.simplified();
+
+ int i = line.indexOf(QLatin1Char('='));
+ if (indented && i != -1) { // line in stanza
+ QString variable = line.left(i).simplified();
+ QString value=line.mid(i+1, line.length()).simplified();
+ if (variable == QLatin1String("device"))
+ deviceName = value;
+ else if (variable == QLatin1String("host"))
+ remoteHost = value;
+ else if (variable == QLatin1String("up"))
+ up = !(value.toLower() == QLatin1String("false"));
+ } else if (line[0] == QLatin1Char('*')) { // comment
+ // nothing to do
+ } else if (ts.atEnd() || // end of file, or beginning of new stanza
+ (!indented && line.contains(newStanza))) {
+ if (up && stanzaName.length() > 0 && stanzaName.length() < 21) {
+ if (remoteHost.length()) // remote printer
+ qt_perhapsAddPrinter(printers, stanzaName, remoteHost,
+ QString());
+ else if (deviceName.length()) // local printer
+ qt_perhapsAddPrinter(printers, stanzaName, QString(),
+ QString());
+ }
+ line.chop(1);
+ if (line.length() >= 1 && line.length() <= 20)
+ stanzaName = line;
+ up = true;
+ remoteHost.clear();
+ deviceName.clear();
+ } else {
+ // syntax error? ignore.
+ }
+ } while (!ts.atEnd());
+}
+
+int qt_getLprPrinters(QList<QPrinterDescription>& printers)
+{
+ QByteArray etcLpDefault;
+ qt_parsePrintcap(&printers, QLatin1String("/etc/printcap"));
+ qt_parseEtcLpMember(&printers);
+ qt_parseSpoolInterface(&printers);
+ qt_parseQconfig(&printers);
+
+ QFileInfo f;
+ f.setFile(QLatin1String("/etc/lp/printers"));
+ if (f.isDir()) {
+ qt_parseEtcLpPrinters(&printers);
+ QFile def(QLatin1String("/etc/lp/default"));
+ if (def.open(QIODevice::ReadOnly)) {
+ etcLpDefault.resize(1025);
+ if (def.readLine(etcLpDefault.data(), 1024) > 0) {
+ QRegExp rx(QLatin1String("^(\\S+)"));
+ if (rx.indexIn(QString::fromLatin1(etcLpDefault)) != -1)
+ etcLpDefault = rx.cap(1).toAscii();
+ }
+ }
+ }
+
+ char *def = 0;
+ f.setFile(QLatin1String("/etc/nsswitch.conf"));
+ if (f.isFile()) {
+ def = qt_parseNsswitchConf(&printers);
+ } else {
+ f.setFile(QLatin1String("/etc/printers.conf"));
+ if (f.isFile())
+ def = qt_parsePrintersConf(&printers);
+ }
+
+ if (def) {
+ etcLpDefault = def;
+ delete [] def;
+ }
+
+ QString homePrintersDefault = qt_getDefaultFromHomePrinters();
+
+ // all printers hopefully known. try to find a good default
+ QString dollarPrinter;
+ {
+ dollarPrinter = QString::fromLocal8Bit(qgetenv("PRINTER"));
+ if (dollarPrinter.isEmpty())
+ dollarPrinter = QString::fromLocal8Bit(qgetenv("LPDEST"));
+ if (dollarPrinter.isEmpty())
+ dollarPrinter = QString::fromLocal8Bit(qgetenv("NPRINTER"));
+ if (dollarPrinter.isEmpty())
+ dollarPrinter = QString::fromLocal8Bit(qgetenv("NGPRINTER"));
+#ifndef QT_NO_PRINTDIALOG
+ if (!dollarPrinter.isEmpty())
+ qt_perhapsAddPrinter(&printers, dollarPrinter,
+ QPrintDialog::tr("unknown"),
+ QLatin1String(""));
+#endif
+ }
+
+ int quality = 0;
+ int best = 0;
+ for (int i = 0; i < printers.size(); ++i) {
+ QRegExp ps(QLatin1String("[^a-z]ps(?:[^a-z]|$)"));
+ QRegExp lp(QLatin1String("[^a-z]lp(?:[^a-z]|$)"));
+
+ QString name = printers.at(i).name;
+ QString comment = printers.at(i).comment;
+ if (quality < 5 && name == dollarPrinter) {
+ best = i;
+ quality = 5;
+ } else if (quality < 4 && !homePrintersDefault.isEmpty() &&
+ name == homePrintersDefault) {
+ best = i;
+ quality = 4;
+ } else if (quality < 3 && !etcLpDefault.isEmpty() &&
+ name == QLatin1String(etcLpDefault)) {
+ best = i;
+ quality = 3;
+ } else if (quality < 2 &&
+ (name == QLatin1String("ps") ||
+ ps.indexIn(comment) != -1)) {
+ best = i;
+ quality = 2;
+ } else if (quality < 1 &&
+ (name == QLatin1String("lp") ||
+ lp.indexIn(comment) > -1)) {
+ best = i;
+ quality = 1;
+ }
+ }
+
+ return best;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////
+
+QList<QPrinterInfo> QPrinterInfo::availablePrinters()
+{
+ QList<QPrinterInfo> list;
+
+#if !defined(QT_NO_CUPS) && !defined(QT_NO_LIBRARY)
+ QCUPSSupport cups;
+ if (QCUPSSupport::isAvailable()) {
+ //const ppd_file_t* cupsPPD = cups.currentPPD();
+ int cupsPrinterCount = cups.availablePrintersCount();
+ const cups_dest_t* cupsPrinters = cups.availablePrinters();
+
+ for (int i = 0; i < cupsPrinterCount; ++i) {
+ QString printerName(QString::fromLocal8Bit(cupsPrinters[i].name));
+ if (cupsPrinters[i].instance)
+ printerName += QLatin1String("/") + QString::fromLocal8Bit(cupsPrinters[i].instance);
+ list.append(QPrinterInfo(printerName));
+ if (cupsPrinters[i].is_default)
+ list[i].d_ptr->m_default = true;
+ // Find paper sizes.
+ cups.setCurrentPrinter(i);
+ const ppd_option_t* sizes = cups.pageSizes();
+ if (sizes) {
+ for (int j = 0; j < sizes->num_choices; ++j) {
+ list[i].d_ptr->m_paperSizes.append(
+ QPrinterInfoPrivate::string2PaperSize(
+ QLatin1String(sizes->choices[j].choice)));
+ }
+ }
+ }
+ } else {
+#endif
+ QList<QPrinterDescription> lprPrinters;
+ int defprn = qt_getLprPrinters(lprPrinters);
+ // populating printer combo
+ QList<QPrinterDescription>::const_iterator i = lprPrinters.constBegin();
+ for(; i != lprPrinters.constEnd(); ++i) {
+ list.append(QPrinterInfo((*i).name));
+ }
+ if (defprn >= 0 && defprn < lprPrinters.size()) {
+ list[defprn].d_ptr->m_default = true;
+ }
+#if !defined(QT_NO_CUPS) && !defined(QT_NO_LIBRARY)
+ }
+#endif
+
+ return list;
+}
+
+QPrinterInfo QPrinterInfo::defaultPrinter()
+{
+ QList<QPrinterInfo> prnList = availablePrinters();
+ for (int i = 0; i < prnList.size(); ++i) {
+ if (prnList[i].isDefault())
+ return prnList[i];
+ }
+ return (prnList.size() > 0) ? prnList[0] : QPrinterInfo();
+}
+
+QPrinterInfo::QPrinterInfo()
+{
+ d_ptr = &nullQPrinterInfoPrivate;
+}
+
+QPrinterInfo::QPrinterInfo(const QPrinterInfo& src)
+{
+ d_ptr = &nullQPrinterInfoPrivate;
+ *this = src;
+}
+
+QPrinterInfo::QPrinterInfo(const QPrinter& printer)
+{
+ d_ptr = new QPrinterInfoPrivate(printer.printerName());
+
+ Q_D(QPrinterInfo);
+ d->q_ptr = this;
+
+#if !defined(QT_NO_CUPS) && !defined(QT_NO_LIBRARY)
+ QCUPSSupport cups;
+ if (QCUPSSupport::isAvailable()) {
+ int cupsPrinterCount = cups.availablePrintersCount();
+ const cups_dest_t* cupsPrinters = cups.availablePrinters();
+
+ for (int i = 0; i < cupsPrinterCount; ++i) {
+ QString printerName(QString::fromLocal8Bit(cupsPrinters[i].name));
+ if (cupsPrinters[i].instance)
+ printerName += QLatin1String("/") + QString::fromLocal8Bit(cupsPrinters[i].instance);
+ if (printerName == printer.printerName()) {
+ if (cupsPrinters[i].is_default)
+ d->m_default = true;
+ // Find paper sizes.
+ cups.setCurrentPrinter(i);
+ const ppd_option_t* sizes = cups.pageSizes();
+ if (sizes) {
+ for (int j = 0; j < sizes->num_choices; ++j) {
+ d->m_paperSizes.append(
+ QPrinterInfoPrivate::string2PaperSize(
+ QLatin1String(sizes->choices[j].choice)));
+ }
+ }
+ return;
+ }
+ }
+ } else {
+#endif
+ QList<QPrinterDescription> lprPrinters;
+ int defprn = qt_getLprPrinters(lprPrinters);
+ // populating printer combo
+ QList<QPrinterDescription>::const_iterator i = lprPrinters.constBegin();
+ int c;
+ for(c = 0; i != lprPrinters.constEnd(); ++i, ++c) {
+ if (i->name == printer.printerName()) {
+ if (defprn == c)
+ d->m_default = true;
+ return;
+ }
+ }
+#if !defined(QT_NO_CUPS) && !defined(QT_NO_LIBRARY)
+ }
+#endif
+
+ // Printer not found.
+ delete d;
+ d_ptr = &nullQPrinterInfoPrivate;
+}
+
+QPrinterInfo::QPrinterInfo(const QString& name)
+{
+ d_ptr = new QPrinterInfoPrivate(name);
+ d_ptr->q_ptr = this;
+}
+
+QPrinterInfo::~QPrinterInfo()
+{
+ if (d_ptr != &nullQPrinterInfoPrivate)
+ delete d_ptr;
+}
+
+QPrinterInfo& QPrinterInfo::operator=(const QPrinterInfo& src)
+{
+ Q_ASSERT(d_ptr);
+ if (d_ptr != &nullQPrinterInfoPrivate)
+ delete d_ptr;
+ d_ptr = new QPrinterInfoPrivate(*src.d_ptr);
+ d_ptr->q_ptr = this;
+ return *this;
+}
+
+QString QPrinterInfo::printerName() const
+{
+ const Q_D(QPrinterInfo);
+ return d->m_name;
+}
+
+bool QPrinterInfo::isNull() const
+{
+ const Q_D(QPrinterInfo);
+ return d->m_isNull;
+}
+
+bool QPrinterInfo::isDefault() const
+{
+ const Q_D(QPrinterInfo);
+ return d->m_default;
+}
+
+QList< QPrinter::PaperSize> QPrinterInfo::supportedPaperSizes() const
+{
+ const Q_D(QPrinterInfo);
+ return d->m_paperSizes;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////
+
+QPrinterInfoPrivate::QPrinterInfoPrivate()
+{
+ m_isNull = true;
+ m_default = false;
+ q_ptr = 0;
+}
+
+QPrinterInfoPrivate::QPrinterInfoPrivate(const QString& name)
+{
+ m_name = name;
+ m_isNull = false;
+ m_default = false;
+ q_ptr = 0;
+}
+
+QPrinterInfoPrivate::~QPrinterInfoPrivate()
+{
+}
+
+QPrinter::PaperSize QPrinterInfoPrivate::string2PaperSize(const QString& str)
+{
+ if (str == QLatin1String("A4")) {
+ return QPrinter::A4;
+ } else if (str == QLatin1String("B5")) {
+ return QPrinter::B5;
+ } else if (str == QLatin1String("Letter")) {
+ return QPrinter::Letter;
+ } else if (str == QLatin1String("Legal")) {
+ return QPrinter::Legal;
+ } else if (str == QLatin1String("Executive")) {
+ return QPrinter::Executive;
+ } else if (str == QLatin1String("A0")) {
+ return QPrinter::A0;
+ } else if (str == QLatin1String("A1")) {
+ return QPrinter::A1;
+ } else if (str == QLatin1String("A2")) {
+ return QPrinter::A2;
+ } else if (str == QLatin1String("A3")) {
+ return QPrinter::A3;
+ } else if (str == QLatin1String("A5")) {
+ return QPrinter::A5;
+ } else if (str == QLatin1String("A6")) {
+ return QPrinter::A6;
+ } else if (str == QLatin1String("A7")) {
+ return QPrinter::A7;
+ } else if (str == QLatin1String("A8")) {
+ return QPrinter::A8;
+ } else if (str == QLatin1String("A9")) {
+ return QPrinter::A9;
+ } else if (str == QLatin1String("B0")) {
+ return QPrinter::B0;
+ } else if (str == QLatin1String("B1")) {
+ return QPrinter::B1;
+ } else if (str == QLatin1String("B10")) {
+ return QPrinter::B10;
+ } else if (str == QLatin1String("B2")) {
+ return QPrinter::B2;
+ } else if (str == QLatin1String("B3")) {
+ return QPrinter::B3;
+ } else if (str == QLatin1String("B4")) {
+ return QPrinter::B4;
+ } else if (str == QLatin1String("B6")) {
+ return QPrinter::B6;
+ } else if (str == QLatin1String("B7")) {
+ return QPrinter::B7;
+ } else if (str == QLatin1String("B8")) {
+ return QPrinter::B8;
+ } else if (str == QLatin1String("B9")) {
+ return QPrinter::B9;
+ } else if (str == QLatin1String("C5E")) {
+ return QPrinter::C5E;
+ } else if (str == QLatin1String("Comm10E")) {
+ return QPrinter::Comm10E;
+ } else if (str == QLatin1String("DLE")) {
+ return QPrinter::DLE;
+ } else if (str == QLatin1String("Folio")) {
+ return QPrinter::Folio;
+ } else if (str == QLatin1String("Ledger")) {
+ return QPrinter::Ledger;
+ } else if (str == QLatin1String("Tabloid")) {
+ return QPrinter::Tabloid;
+ } else {
+ return QPrinter::Custom;
+ }
+}
+
+QString QPrinterInfoPrivate::pageSize2String(QPrinter::PaperSize size)
+{
+ switch (size) {
+ case QPrinter::A4:
+ return QLatin1String("A4");
+ case QPrinter::B5:
+ return QLatin1String("B5");
+ case QPrinter::Letter:
+ return QLatin1String("Letter");
+ case QPrinter::Legal:
+ return QLatin1String("Legal");
+ case QPrinter::Executive:
+ return QLatin1String("Executive");
+ case QPrinter::A0:
+ return QLatin1String("A0");
+ case QPrinter::A1:
+ return QLatin1String("A1");
+ case QPrinter::A2:
+ return QLatin1String("A2");
+ case QPrinter::A3:
+ return QLatin1String("A3");
+ case QPrinter::A5:
+ return QLatin1String("A5");
+ case QPrinter::A6:
+ return QLatin1String("A6");
+ case QPrinter::A7:
+ return QLatin1String("A7");
+ case QPrinter::A8:
+ return QLatin1String("A8");
+ case QPrinter::A9:
+ return QLatin1String("A9");
+ case QPrinter::B0:
+ return QLatin1String("B0");
+ case QPrinter::B1:
+ return QLatin1String("B1");
+ case QPrinter::B10:
+ return QLatin1String("B10");
+ case QPrinter::B2:
+ return QLatin1String("B2");
+ case QPrinter::B3:
+ return QLatin1String("B3");
+ case QPrinter::B4:
+ return QLatin1String("B4");
+ case QPrinter::B6:
+ return QLatin1String("B6");
+ case QPrinter::B7:
+ return QLatin1String("B7");
+ case QPrinter::B8:
+ return QLatin1String("B8");
+ case QPrinter::B9:
+ return QLatin1String("B9");
+ case QPrinter::C5E:
+ return QLatin1String("C5E");
+ case QPrinter::Comm10E:
+ return QLatin1String("Comm10E");
+ case QPrinter::DLE:
+ return QLatin1String("DLE");
+ case QPrinter::Folio:
+ return QLatin1String("Folio");
+ case QPrinter::Ledger:
+ return QLatin1String("Ledger");
+ case QPrinter::Tabloid:
+ return QLatin1String("Tabloid");
+ default:
+ return QLatin1String("Custom");
+ }
+}
+
+#endif // QT_NO_PRINTER
+
+QT_END_NAMESPACE
diff --git a/src/gui/painting/qprinterinfo_unix_p.h b/src/gui/painting/qprinterinfo_unix_p.h
new file mode 100644
index 0000000000..dc0181fbcd
--- /dev/null
+++ b/src/gui/painting/qprinterinfo_unix_p.h
@@ -0,0 +1,125 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QPRINTERINFO_UNIX_P_H
+#define QPRINTERINFO_UNIX_P_H
+
+#ifndef QT_NO_NIS
+# ifndef BOOL_DEFINED
+# define BOOL_DEFINED
+# endif
+
+# include <sys/types.h>
+# include <rpc/rpc.h>
+# include <rpcsvc/ypclnt.h>
+# include <rpcsvc/yp_prot.h>
+#endif // QT_NO_NIS
+
+#ifdef Success
+# undef Success
+#endif
+
+#include <ctype.h>
+
+QT_BEGIN_NAMESPACE
+
+//
+// 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_PRINTER
+
+struct QPrinterDescription {
+ QPrinterDescription(const QString &n, const QString &h, const QString &c, const QStringList &a)
+ : name(n), host(h), comment(c), aliases(a) {}
+ QString name;
+ QString host;
+ QString comment;
+ QStringList aliases;
+ bool samePrinter(const QString& printer) const {
+ return name == printer || aliases.contains(printer);
+ }
+};
+
+enum { Success = 's', Unavail = 'u', NotFound = 'n', TryAgain = 't' };
+enum { Continue = 'c', Return = 'r' };
+
+void qt_perhapsAddPrinter(QList<QPrinterDescription> *printers, const QString &name,
+ QString host, QString comment,
+ QStringList aliases = QStringList());
+void qt_parsePrinterDesc(QString printerDesc, QList<QPrinterDescription> *printers);
+
+int qt_parsePrintcap(QList<QPrinterDescription> *printers, const QString& fileName);
+QString qt_getDefaultFromHomePrinters();
+void qt_parseEtcLpPrinters(QList<QPrinterDescription> *printers);
+char *qt_parsePrintersConf(QList<QPrinterDescription> *printers, bool *found = 0);
+
+#ifndef QT_NO_NIS
+#if defined(Q_C_CALLBACKS)
+extern "C" {
+#endif
+int qt_pd_foreach(int /*status */, char * /*key */, int /*keyLen */,
+ char *val, int valLen, char *data);
+
+#if defined(Q_C_CALLBACKS)
+}
+#endif
+int qt_retrieveNisPrinters(QList<QPrinterDescription> *printers);
+#endif // QT_NO_NIS
+char *qt_parseNsswitchPrintersEntry(QList<QPrinterDescription> *printers, char *line);
+char *qt_parseNsswitchConf(QList<QPrinterDescription> *printers);
+void qt_parseEtcLpMember(QList<QPrinterDescription> *printers);
+void qt_parseSpoolInterface(QList<QPrinterDescription> *printers);
+void qt_parseQconfig(QList<QPrinterDescription> *printers);
+int qt_getLprPrinters(QList<QPrinterDescription>& printers);
+
+#endif // QT_NO_PRINTER
+
+QT_END_NAMESPACE
+
+#endif // QPRINTERINFO_UNIX_P_H
diff --git a/src/gui/painting/qprinterinfo_win.cpp b/src/gui/painting/qprinterinfo_win.cpp
new file mode 100644
index 0000000000..7cd3cf3aa4
--- /dev/null
+++ b/src/gui/painting/qprinterinfo_win.cpp
@@ -0,0 +1,279 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qprinterinfo.h"
+
+#include <qstringlist.h>
+
+#include <windows.h>
+
+QT_BEGIN_NAMESPACE
+
+#ifndef QT_NO_PRINTER
+
+extern QPrinter::PaperSize mapDevmodePaperSize(int s);
+
+class QPrinterInfoPrivate
+{
+Q_DECLARE_PUBLIC(QPrinterInfo)
+public:
+ ~QPrinterInfoPrivate();
+ QPrinterInfoPrivate();
+ QPrinterInfoPrivate(const QString& name);
+
+private:
+ QString m_name;
+ bool m_default;
+ bool m_isNull;
+
+ QPrinterInfo* q_ptr;
+};
+
+static QPrinterInfoPrivate nullQPrinterInfoPrivate;
+
+/////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////
+
+QList<QPrinterInfo> QPrinterInfo::availablePrinters()
+{
+ QList<QPrinterInfo> printers;
+ LPBYTE buffer;
+ DWORD needed = 0;
+ DWORD returned = 0;
+
+ QT_WA({
+ if (!EnumPrintersW(PRINTER_ENUM_LOCAL | PRINTER_ENUM_CONNECTIONS, NULL,
+ 4, 0, 0, &needed, &returned))
+ {
+ buffer = new BYTE[needed];
+ if (!EnumPrintersW(PRINTER_ENUM_LOCAL | PRINTER_ENUM_CONNECTIONS , NULL,
+ 4, buffer, needed, &needed, &returned))
+ {
+ delete [] buffer;
+ return printers;
+ }
+ PPRINTER_INFO_4 infoList = reinterpret_cast<PPRINTER_INFO_4>(buffer);
+ QPrinterInfo defPrn = defaultPrinter();
+ for (uint i = 0; i < returned; ++i) {
+ printers.append(QPrinterInfo(QString::fromUtf16(reinterpret_cast<const ushort*>(infoList[i].pPrinterName))));
+ if (printers.at(i).printerName() == defPrn.printerName())
+ printers[i].d_ptr->m_default = true;
+ }
+ delete [] buffer;
+ }
+ }, {
+ // In Windows 98 networked printers are served through the local connection
+ if (!EnumPrintersA(PRINTER_ENUM_LOCAL, NULL, 5, 0, 0, &needed, &returned)) {
+ buffer = new BYTE[needed];
+ if (!EnumPrintersA(PRINTER_ENUM_LOCAL, NULL, 5, buffer, needed, &needed, &returned)) {
+ delete [] buffer;
+ return printers;
+ }
+
+ PPRINTER_INFO_5 infoList = reinterpret_cast<PPRINTER_INFO_5>(buffer);
+ QPrinterInfo defPrn = defaultPrinter();
+ for (uint i = 0; i < returned; ++i) {
+ printers.append(QPrinterInfo(QString::fromLocal8Bit(reinterpret_cast<const char*>(infoList[i].pPrinterName))));
+ if (printers.at(i).printerName() == defPrn.printerName())
+ printers[i].d_ptr->m_default = true;
+ }
+ delete [] buffer;
+ }
+ });
+
+ return printers;
+}
+
+QPrinterInfo QPrinterInfo::defaultPrinter()
+{
+ QString noPrinters(QLatin1String("qt_no_printers"));
+ QString output;
+ QT_WA({
+ ushort buffer[256];
+ GetProfileStringW(L"windows", L"device",
+ reinterpret_cast<const wchar_t *>(noPrinters.utf16()),
+ reinterpret_cast<wchar_t *>(buffer), 256);
+ output = QString::fromUtf16(buffer);
+ }, {
+ char buffer[256];
+ GetProfileStringA("windows", "device", noPrinters.toLatin1(), buffer, 256);
+ output = QString::fromLocal8Bit(buffer);
+ });
+
+ // Filter out the name of the printer, which should be everything
+ // before a comma.
+ bool noConfiguredPrinters = (output == noPrinters);
+ QStringList info = output.split(QLatin1Char(','));
+ QString printerName = noConfiguredPrinters ? QString() : info.at(0);
+
+ QPrinterInfo prn(printerName);
+ prn.d_ptr->m_default = true;
+ if (noConfiguredPrinters)
+ prn.d_ptr->m_isNull = true;
+ return prn;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////
+
+QPrinterInfo::QPrinterInfo()
+{
+ d_ptr = &nullQPrinterInfoPrivate;
+}
+
+QPrinterInfo::QPrinterInfo(const QString& name)
+{
+ d_ptr = new QPrinterInfoPrivate(name);
+ d_ptr->q_ptr = this;
+}
+
+QPrinterInfo::QPrinterInfo(const QPrinterInfo& src)
+{
+ d_ptr = &nullQPrinterInfoPrivate;
+ *this = src;
+}
+
+QPrinterInfo::QPrinterInfo(const QPrinter& prn)
+{
+ d_ptr = &nullQPrinterInfoPrivate;
+ QList<QPrinterInfo> list = availablePrinters();
+ for (int c = 0; c < list.size(); ++c) {
+ if (prn.printerName() == list[c].printerName()) {
+ *this = list[c];
+ return;
+ }
+ }
+
+ *this = QPrinterInfo();
+}
+
+QPrinterInfo::~QPrinterInfo()
+{
+ if (d_ptr != &nullQPrinterInfoPrivate)
+ delete d_ptr;
+}
+
+QPrinterInfo& QPrinterInfo::operator=(const QPrinterInfo& src)
+{
+ Q_ASSERT(d_ptr);
+ if (d_ptr != &nullQPrinterInfoPrivate)
+ delete d_ptr;
+ d_ptr = new QPrinterInfoPrivate(*src.d_ptr);
+ d_ptr->q_ptr = this;
+ return *this;
+}
+
+QString QPrinterInfo::printerName() const
+{
+ const Q_D(QPrinterInfo);
+ return d->m_name;
+}
+
+bool QPrinterInfo::isNull() const
+{
+ const Q_D(QPrinterInfo);
+ return d->m_isNull;
+}
+
+bool QPrinterInfo::isDefault() const
+{
+ const Q_D(QPrinterInfo);
+ return d->m_default;
+}
+
+QList<QPrinter::PaperSize> QPrinterInfo::supportedPaperSizes() const
+{
+ const Q_D(QPrinterInfo);
+ DWORD size;
+ WORD* papers;
+ QList<QPrinter::PaperSize> paperList;
+
+ QT_WA({
+ size = DeviceCapabilitiesW(reinterpret_cast<const WCHAR*>(d->m_name.utf16()),
+ NULL, DC_PAPERS, NULL, NULL);
+ if ((int)size == -1)
+ return paperList;
+ papers = new WORD[size];
+ size = DeviceCapabilitiesW(reinterpret_cast<const WCHAR*>(d->m_name.utf16()),
+ NULL, DC_PAPERS, reinterpret_cast<WCHAR*>(papers), NULL);
+ }, {
+ size = DeviceCapabilitiesA(d->m_name.toLatin1().data(), NULL, DC_PAPERS, NULL, NULL);
+ if ((int)size == -1)
+ return paperList;
+ papers = new WORD[size];
+ size = DeviceCapabilitiesA(d->m_name.toLatin1().data(), NULL, DC_PAPERS,
+ reinterpret_cast<char*>(papers), NULL);
+ });
+
+ for (int c = 0; c < (int)size; ++c) {
+ paperList.append(mapDevmodePaperSize(papers[c]));
+ }
+
+ delete [] papers;
+
+ return paperList;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////
+
+QPrinterInfoPrivate::QPrinterInfoPrivate() :
+ m_default(false),
+ m_isNull(true),
+ q_ptr(NULL)
+{
+}
+
+QPrinterInfoPrivate::QPrinterInfoPrivate(const QString& name) :
+ m_name(name),
+ m_default(false),
+ m_isNull(false),
+ q_ptr(NULL)
+{
+}
+
+QPrinterInfoPrivate::~QPrinterInfoPrivate()
+{
+}
+
+#endif // QT_NO_PRINTER
+
+QT_END_NAMESPACE
diff --git a/src/gui/painting/qpsprinter.agl b/src/gui/painting/qpsprinter.agl
new file mode 100644
index 0000000000..137b64c53b
--- /dev/null
+++ b/src/gui/painting/qpsprinter.agl
@@ -0,0 +1,452 @@
+# the next table is derived from a list provided by Adobe on its web
+# server: http://partners.adobe.com/asn/developer/typeforum/glyphlist.txt
+
+# the start of the header comment:
+#
+# Name: Adobe Glyph List
+# Table version: 1.2
+# Date: 22 Oct 1998
+#
+# Description:
+#
+# The Adobe Glyph List (AGL) list relates Unicode values (UVs) to glyph
+# names, and should be used only as described in the document "Unicode and
+# Glyph Names," at
+# http://partners.adobe.com:80/asn/developer/type/unicodegn.html
+#
+# IMPORTANT NOTE:
+# the list contains glyphs in the private use area of unicode.
+# These should get removed when regenerating the glyphlist.
+#
+# also 0 should be mapped to .notdef
+#
+# grep '^[0-9A-F][0-9A-F][0-9A-F][0-9A-F];' < /tmp/glyphlist.txt | sed -e 's/;/, "/' -e 's-;-" }, // -' -e 's/^/ { 0x/' | sort
+#
+0x0000, ".notdef"
+0x0020, "space" # SPACE
+0x0021, "exclam" # EXCLAMATION MARK
+0x0022, "quotedbl" # QUOTATION MARK
+0x0023, "numbersign" # NUMBER SIGN
+0x0024, "dollar" # DOLLAR SIGN
+0x0025, "percent" # PERCENT SIGN
+0x0026, "ampersand" # AMPERSAND
+0x0027, "quotesingle" # APOSTROPHE
+0x0028, "parenleft" # LEFT PARENTHESIS
+0x0029, "parenright" # RIGHT PARENTHESIS
+0x002A, "asterisk" # ASTERISK
+0x002B, "plus" # PLUS SIGN
+0x002C, "comma" # COMMA
+0x002D, "hyphen" # HYPHEN-MINUS
+0x002E, "period" # FULL STOP
+0x002F, "slash" # SOLIDUS
+0x0030, "zero" # DIGIT ZERO
+0x0031, "one" # DIGIT ONE
+0x0032, "two" # DIGIT TWO
+0x0033, "three" # DIGIT THREE
+0x0034, "four" # DIGIT FOUR
+0x0035, "five" # DIGIT FIVE
+0x0036, "six" # DIGIT SIX
+0x0037, "seven" # DIGIT SEVEN
+0x0038, "eight" # DIGIT EIGHT
+0x0039, "nine" # DIGIT NINE
+0x003A, "colon" # COLON
+0x003B, "semicolon" # SEMICOLON
+0x003C, "less" # LESS-THAN SIGN
+0x003D, "equal" # EQUALS SIGN
+0x003E, "greater" # GREATER-THAN SIGN
+0x003F, "question" # QUESTION MARK
+0x0040, "at" # COMMERCIAL AT
+0x0041, "A" # LATIN CAPITAL LETTER A
+0x0042, "B" # LATIN CAPITAL LETTER B
+0x0043, "C" # LATIN CAPITAL LETTER C
+0x0044, "D" # LATIN CAPITAL LETTER D
+0x0045, "E" # LATIN CAPITAL LETTER E
+0x0046, "F" # LATIN CAPITAL LETTER F
+0x0047, "G" # LATIN CAPITAL LETTER G
+0x0048, "H" # LATIN CAPITAL LETTER H
+0x0049, "I" # LATIN CAPITAL LETTER I
+0x004A, "J" # LATIN CAPITAL LETTER J
+0x004B, "K" # LATIN CAPITAL LETTER K
+0x004C, "L" # LATIN CAPITAL LETTER L
+0x004D, "M" # LATIN CAPITAL LETTER M
+0x004E, "N" # LATIN CAPITAL LETTER N
+0x004F, "O" # LATIN CAPITAL LETTER O
+0x0050, "P" # LATIN CAPITAL LETTER P
+0x0051, "Q" # LATIN CAPITAL LETTER Q
+0x0052, "R" # LATIN CAPITAL LETTER R
+0x0053, "S" # LATIN CAPITAL LETTER S
+0x0054, "T" # LATIN CAPITAL LETTER T
+0x0055, "U" # LATIN CAPITAL LETTER U
+0x0056, "V" # LATIN CAPITAL LETTER V
+0x0057, "W" # LATIN CAPITAL LETTER W
+0x0058, "X" # LATIN CAPITAL LETTER X
+0x0059, "Y" # LATIN CAPITAL LETTER Y
+0x005A, "Z" # LATIN CAPITAL LETTER Z
+0x005B, "bracketleft" # LEFT SQUARE BRACKET
+0x005C, "backslash" # REVERSE SOLIDUS
+0x005D, "bracketright" # RIGHT SQUARE BRACKET
+0x005E, "asciicircum" # CIRCUMFLEX ACCENT
+0x005F, "underscore" # LOW LINE
+0x0060, "grave" # GRAVE ACCENT
+0x0061, "a" # LATIN SMALL LETTER A
+0x0062, "b" # LATIN SMALL LETTER B
+0x0063, "c" # LATIN SMALL LETTER C
+0x0064, "d" # LATIN SMALL LETTER D
+0x0065, "e" # LATIN SMALL LETTER E
+0x0066, "f" # LATIN SMALL LETTER F
+0x0067, "g" # LATIN SMALL LETTER G
+0x0068, "h" # LATIN SMALL LETTER H
+0x0069, "i" # LATIN SMALL LETTER I
+0x006A, "j" # LATIN SMALL LETTER J
+0x006B, "k" # LATIN SMALL LETTER K
+0x006C, "l" # LATIN SMALL LETTER L
+0x006D, "m" # LATIN SMALL LETTER M
+0x006E, "n" # LATIN SMALL LETTER N
+0x006F, "o" # LATIN SMALL LETTER O
+0x0070, "p" # LATIN SMALL LETTER P
+0x0071, "q" # LATIN SMALL LETTER Q
+0x0072, "r" # LATIN SMALL LETTER R
+0x0073, "s" # LATIN SMALL LETTER S
+0x0074, "t" # LATIN SMALL LETTER T
+0x0075, "u" # LATIN SMALL LETTER U
+0x0076, "v" # LATIN SMALL LETTER V
+0x0077, "w" # LATIN SMALL LETTER W
+0x0078, "x" # LATIN SMALL LETTER X
+0x0079, "y" # LATIN SMALL LETTER Y
+0x007A, "z" # LATIN SMALL LETTER Z
+0x007B, "braceleft" # LEFT CURLY BRACKET
+0x007C, "bar" # VERTICAL LINE
+0x007D, "braceright" # RIGHT CURLY BRACKET
+0x007E, "asciitilde" # TILDE
+0x00A0, "space" # NO-BREAK SPACE;Duplicate
+0x00A1, "exclamdown" # INVERTED EXCLAMATION MARK
+0x00A2, "cent" # CENT SIGN
+0x00A3, "sterling" # POUND SIGN
+0x00A4, "currency" # CURRENCY SIGN
+0x00A5, "yen" # YEN SIGN
+0x00A6, "brokenbar" # BROKEN BAR
+0x00A7, "section" # SECTION SIGN
+0x00A8, "dieresis" # DIAERESIS
+0x00A9, "copyright" # COPYRIGHT SIGN
+0x00AA, "ordfeminine" # FEMININE ORDINAL INDICATOR
+0x00AB, "guillemotleft" # LEFT-POINTING DOUBLE ANGLE QUOTATION MARK
+0x00AC, "logicalnot" # NOT SIGN
+0x00AD, "hyphen" # SOFT HYPHEN;Duplicate
+0x00AE, "registered" # REGISTERED SIGN
+0x00AF, "macron" # MACRON
+0x00B0, "degree" # DEGREE SIGN
+0x00B1, "plusminus" # PLUS-MINUS SIGN
+0x00B2, "twosuperior" # SUPERSCRIPT TWO
+0x00B3, "threesuperior" # SUPERSCRIPT THREE
+0x00B4, "acute" # ACUTE ACCENT
+0x00B5, "mu" # MICRO SIGN
+0x00B6, "paragraph" # PILCROW SIGN
+0x00B7, "periodcentered" # MIDDLE DOT
+0x00B8, "cedilla" # CEDILLA
+0x00B9, "onesuperior" # SUPERSCRIPT ONE
+0x00BA, "ordmasculine" # MASCULINE ORDINAL INDICATOR
+0x00BB, "guillemotright" # RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK
+0x00BC, "onequarter" # VULGAR FRACTION ONE QUARTER
+0x00BD, "onehalf" # VULGAR FRACTION ONE HALF
+0x00BE, "threequarters" # VULGAR FRACTION THREE QUARTERS
+0x00BF, "questiondown" # INVERTED QUESTION MARK
+0x00C0, "Agrave" # LATIN CAPITAL LETTER A WITH GRAVE
+0x00C1, "Aacute" # LATIN CAPITAL LETTER A WITH ACUTE
+0x00C2, "Acircumflex" # LATIN CAPITAL LETTER A WITH CIRCUMFLEX
+0x00C3, "Atilde" # LATIN CAPITAL LETTER A WITH TILDE
+0x00C4, "Adieresis" # LATIN CAPITAL LETTER A WITH DIAERESIS
+0x00C5, "Aring" # LATIN CAPITAL LETTER A WITH RING ABOVE
+0x00C6, "AE" # LATIN CAPITAL LETTER AE
+0x00C7, "Ccedilla" # LATIN CAPITAL LETTER C WITH CEDILLA
+0x00C8, "Egrave" # LATIN CAPITAL LETTER E WITH GRAVE
+0x00C9, "Eacute" # LATIN CAPITAL LETTER E WITH ACUTE
+0x00CA, "Ecircumflex" # LATIN CAPITAL LETTER E WITH CIRCUMFLEX
+0x00CB, "Edieresis" # LATIN CAPITAL LETTER E WITH DIAERESIS
+0x00CC, "Igrave" # LATIN CAPITAL LETTER I WITH GRAVE
+0x00CD, "Iacute" # LATIN CAPITAL LETTER I WITH ACUTE
+0x00CE, "Icircumflex" # LATIN CAPITAL LETTER I WITH CIRCUMFLEX
+0x00CF, "Idieresis" # LATIN CAPITAL LETTER I WITH DIAERESIS
+0x00D0, "Eth" # LATIN CAPITAL LETTER ETH
+0x00D1, "Ntilde" # LATIN CAPITAL LETTER N WITH TILDE
+0x00D2, "Ograve" # LATIN CAPITAL LETTER O WITH GRAVE
+0x00D3, "Oacute" # LATIN CAPITAL LETTER O WITH ACUTE
+0x00D4, "Ocircumflex" # LATIN CAPITAL LETTER O WITH CIRCUMFLEX
+0x00D5, "Otilde" # LATIN CAPITAL LETTER O WITH TILDE
+0x00D6, "Odieresis" # LATIN CAPITAL LETTER O WITH DIAERESIS
+0x00D7, "multiply" # MULTIPLICATION SIGN
+0x00D8, "Oslash" # LATIN CAPITAL LETTER O WITH STROKE
+0x00D9, "Ugrave" # LATIN CAPITAL LETTER U WITH GRAVE
+0x00DA, "Uacute" # LATIN CAPITAL LETTER U WITH ACUTE
+0x00DB, "Ucircumflex" # LATIN CAPITAL LETTER U WITH CIRCUMFLEX
+0x00DC, "Udieresis" # LATIN CAPITAL LETTER U WITH DIAERESIS
+0x00DD, "Yacute" # LATIN CAPITAL LETTER Y WITH ACUTE
+0x00DE, "Thorn" # LATIN CAPITAL LETTER THORN
+0x00DF, "germandbls" # LATIN SMALL LETTER SHARP S
+0x00E0, "agrave" # LATIN SMALL LETTER A WITH GRAVE
+0x00E1, "aacute" # LATIN SMALL LETTER A WITH ACUTE
+0x00E2, "acircumflex" # LATIN SMALL LETTER A WITH CIRCUMFLEX
+0x00E3, "atilde" # LATIN SMALL LETTER A WITH TILDE
+0x00E4, "adieresis" # LATIN SMALL LETTER A WITH DIAERESIS
+0x00E5, "aring" # LATIN SMALL LETTER A WITH RING ABOVE
+0x00E6, "ae" # LATIN SMALL LETTER AE
+0x00E7, "ccedilla" # LATIN SMALL LETTER C WITH CEDILLA
+0x00E8, "egrave" # LATIN SMALL LETTER E WITH GRAVE
+0x00E9, "eacute" # LATIN SMALL LETTER E WITH ACUTE
+0x00EA, "ecircumflex" # LATIN SMALL LETTER E WITH CIRCUMFLEX
+0x00EB, "edieresis" # LATIN SMALL LETTER E WITH DIAERESIS
+0x00EC, "igrave" # LATIN SMALL LETTER I WITH GRAVE
+0x00ED, "iacute" # LATIN SMALL LETTER I WITH ACUTE
+0x00EE, "icircumflex" # LATIN SMALL LETTER I WITH CIRCUMFLEX
+0x00EF, "idieresis" # LATIN SMALL LETTER I WITH DIAERESIS
+0x00F0, "eth" # LATIN SMALL LETTER ETH
+0x00F1, "ntilde" # LATIN SMALL LETTER N WITH TILDE
+0x00F2, "ograve" # LATIN SMALL LETTER O WITH GRAVE
+0x00F3, "oacute" # LATIN SMALL LETTER O WITH ACUTE
+0x00F4, "ocircumflex" # LATIN SMALL LETTER O WITH CIRCUMFLEX
+0x00F5, "otilde" # LATIN SMALL LETTER O WITH TILDE
+0x00F6, "odieresis" # LATIN SMALL LETTER O WITH DIAERESIS
+0x00F7, "divide" # DIVISION SIGN
+0x00F8, "oslash" # LATIN SMALL LETTER O WITH STROKE
+0x00F9, "ugrave" # LATIN SMALL LETTER U WITH GRAVE
+0x00FA, "uacute" # LATIN SMALL LETTER U WITH ACUTE
+0x00FB, "ucircumflex" # LATIN SMALL LETTER U WITH CIRCUMFLEX
+0x00FC, "udieresis" # LATIN SMALL LETTER U WITH DIAERESIS
+0x00FD, "yacute" # LATIN SMALL LETTER Y WITH ACUTE
+0x00FE, "thorn" # LATIN SMALL LETTER THORN
+0x00FF, "ydieresis" # LATIN SMALL LETTER Y WITH DIAERESIS
+0x0100, "Amacron" # LATIN CAPITAL LETTER A WITH MACRON
+0x0101, "amacron" # LATIN SMALL LETTER A WITH MACRON
+0x0102, "Abreve" # LATIN CAPITAL LETTER A WITH BREVE
+0x0103, "abreve" # LATIN SMALL LETTER A WITH BREVE
+0x0104, "Aogonek" # LATIN CAPITAL LETTER A WITH OGONEK
+0x0105, "aogonek" # LATIN SMALL LETTER A WITH OGONEK
+0x0106, "Cacute" # LATIN CAPITAL LETTER C WITH ACUTE
+0x0107, "cacute" # LATIN SMALL LETTER C WITH ACUTE
+0x0108, "Ccircumflex" # LATIN CAPITAL LETTER C WITH CIRCUMFLEX
+0x0109, "ccircumflex" # LATIN SMALL LETTER C WITH CIRCUMFLEX
+0x010A, "Cdotaccent" # LATIN CAPITAL LETTER C WITH DOT ABOVE
+0x010B, "cdotaccent" # LATIN SMALL LETTER C WITH DOT ABOVE
+0x010C, "Ccaron" # LATIN CAPITAL LETTER C WITH CARON
+0x010D, "ccaron" # LATIN SMALL LETTER C WITH CARON
+0x010E, "Dcaron" # LATIN CAPITAL LETTER D WITH CARON
+0x010F, "dcaron" # LATIN SMALL LETTER D WITH CARON
+0x0110, "Dcroat" # LATIN CAPITAL LETTER D WITH STROKE
+0x0111, "dcroat" # LATIN SMALL LETTER D WITH STROKE
+0x0112, "Emacron" # LATIN CAPITAL LETTER E WITH MACRON
+0x0113, "emacron" # LATIN SMALL LETTER E WITH MACRON
+0x0114, "Ebreve" # LATIN CAPITAL LETTER E WITH BREVE
+0x0115, "ebreve" # LATIN SMALL LETTER E WITH BREVE
+0x0116, "Edotaccent" # LATIN CAPITAL LETTER E WITH DOT ABOVE
+0x0117, "edotaccent" # LATIN SMALL LETTER E WITH DOT ABOVE
+0x0118, "Eogonek" # LATIN CAPITAL LETTER E WITH OGONEK
+0x0119, "eogonek" # LATIN SMALL LETTER E WITH OGONEK
+0x011A, "Ecaron" # LATIN CAPITAL LETTER E WITH CARON
+0x011B, "ecaron" # LATIN SMALL LETTER E WITH CARON
+0x011C, "Gcircumflex" # LATIN CAPITAL LETTER G WITH CIRCUMFLEX
+0x011D, "gcircumflex" # LATIN SMALL LETTER G WITH CIRCUMFLEX
+0x011E, "Gbreve" # LATIN CAPITAL LETTER G WITH BREVE
+0x011F, "gbreve" # LATIN SMALL LETTER G WITH BREVE
+0x0120, "Gdotaccent" # LATIN CAPITAL LETTER G WITH DOT ABOVE
+0x0121, "gdotaccent" # LATIN SMALL LETTER G WITH DOT ABOVE
+0x0122, "Gcommaaccent" # LATIN CAPITAL LETTER G WITH CEDILLA
+0x0123, "gcommaaccent" # LATIN SMALL LETTER G WITH CEDILLA
+0x0124, "Hcircumflex" # LATIN CAPITAL LETTER H WITH CIRCUMFLEX
+0x0125, "hcircumflex" # LATIN SMALL LETTER H WITH CIRCUMFLEX
+0x0126, "Hbar" # LATIN CAPITAL LETTER H WITH STROKE
+0x0127, "hbar" # LATIN SMALL LETTER H WITH STROKE
+0x0128, "Itilde" # LATIN CAPITAL LETTER I WITH TILDE
+0x0129, "itilde" # LATIN SMALL LETTER I WITH TILDE
+0x012A, "Imacron" # LATIN CAPITAL LETTER I WITH MACRON
+0x012B, "imacron" # LATIN SMALL LETTER I WITH MACRON
+0x012C, "Ibreve" # LATIN CAPITAL LETTER I WITH BREVE
+0x012D, "ibreve" # LATIN SMALL LETTER I WITH BREVE
+0x012E, "Iogonek" # LATIN CAPITAL LETTER I WITH OGONEK
+0x012F, "iogonek" # LATIN SMALL LETTER I WITH OGONEK
+0x0130, "Idotaccent" # LATIN CAPITAL LETTER I WITH DOT ABOVE
+0x0131, "dotlessi" # LATIN SMALL LETTER DOTLESS I
+0x0132, "IJ" # LATIN CAPITAL LIGATURE IJ
+0x0133, "ij" # LATIN SMALL LIGATURE IJ
+0x0134, "Jcircumflex" # LATIN CAPITAL LETTER J WITH CIRCUMFLEX
+0x0135, "jcircumflex" # LATIN SMALL LETTER J WITH CIRCUMFLEX
+0x0136, "Kcommaaccent" # LATIN CAPITAL LETTER K WITH CEDILLA
+0x0137, "kcommaaccent" # LATIN SMALL LETTER K WITH CEDILLA
+0x0138, "kgreenlandic" # LATIN SMALL LETTER KRA
+0x0139, "Lacute" # LATIN CAPITAL LETTER L WITH ACUTE
+0x013A, "lacute" # LATIN SMALL LETTER L WITH ACUTE
+0x013B, "Lcommaaccent" # LATIN CAPITAL LETTER L WITH CEDILLA
+0x013C, "lcommaaccent" # LATIN SMALL LETTER L WITH CEDILLA
+0x013D, "Lcaron" # LATIN CAPITAL LETTER L WITH CARON
+0x013E, "lcaron" # LATIN SMALL LETTER L WITH CARON
+0x013F, "Ldot" # LATIN CAPITAL LETTER L WITH MIDDLE DOT
+0x0140, "ldot" # LATIN SMALL LETTER L WITH MIDDLE DOT
+0x0141, "Lslash" # LATIN CAPITAL LETTER L WITH STROKE
+0x0142, "lslash" # LATIN SMALL LETTER L WITH STROKE
+0x0143, "Nacute" # LATIN CAPITAL LETTER N WITH ACUTE
+0x0144, "nacute" # LATIN SMALL LETTER N WITH ACUTE
+0x0145, "Ncommaaccent" # LATIN CAPITAL LETTER N WITH CEDILLA
+0x0146, "ncommaaccent" # LATIN SMALL LETTER N WITH CEDILLA
+0x0147, "Ncaron" # LATIN CAPITAL LETTER N WITH CARON
+0x0148, "ncaron" # LATIN SMALL LETTER N WITH CARON
+0x0149, "napostrophe" # LATIN SMALL LETTER N PRECEDED BY APOSTROPHE
+0x014A, "Eng" # LATIN CAPITAL LETTER ENG
+0x014B, "eng" # LATIN SMALL LETTER ENG
+0x014C, "Omacron" # LATIN CAPITAL LETTER O WITH MACRON
+0x014D, "omacron" # LATIN SMALL LETTER O WITH MACRON
+0x014E, "Obreve" # LATIN CAPITAL LETTER O WITH BREVE
+0x014F, "obreve" # LATIN SMALL LETTER O WITH BREVE
+0x0150, "Ohungarumlaut" # LATIN CAPITAL LETTER O WITH DOUBLE ACUTE
+0x0151, "ohungarumlaut" # LATIN SMALL LETTER O WITH DOUBLE ACUTE
+0x0152, "OE" # LATIN CAPITAL LIGATURE OE
+0x0153, "oe" # LATIN SMALL LIGATURE OE
+0x0154, "Racute" # LATIN CAPITAL LETTER R WITH ACUTE
+0x0155, "racute" # LATIN SMALL LETTER R WITH ACUTE
+0x0156, "Rcommaaccent" # LATIN CAPITAL LETTER R WITH CEDILLA
+0x0157, "rcommaaccent" # LATIN SMALL LETTER R WITH CEDILLA
+0x0158, "Rcaron" # LATIN CAPITAL LETTER R WITH CARON
+0x0159, "rcaron" # LATIN SMALL LETTER R WITH CARON
+0x015A, "Sacute" # LATIN CAPITAL LETTER S WITH ACUTE
+0x015B, "sacute" # LATIN SMALL LETTER S WITH ACUTE
+0x015C, "Scircumflex" # LATIN CAPITAL LETTER S WITH CIRCUMFLEX
+0x015D, "scircumflex" # LATIN SMALL LETTER S WITH CIRCUMFLEX
+0x015E, "Scedilla" # LATIN CAPITAL LETTER S WITH CEDILLA
+0x015F, "scedilla" # LATIN SMALL LETTER S WITH CEDILLA
+0x0160, "Scaron" # LATIN CAPITAL LETTER S WITH CARON
+0x0161, "scaron" # LATIN SMALL LETTER S WITH CARON
+0x0164, "Tcaron" # LATIN CAPITAL LETTER T WITH CARON
+0x0165, "tcaron" # LATIN SMALL LETTER T WITH CARON
+0x0166, "Tbar" # LATIN CAPITAL LETTER T WITH STROKE
+0x0167, "tbar" # LATIN SMALL LETTER T WITH STROKE
+0x0168, "Utilde" # LATIN CAPITAL LETTER U WITH TILDE
+0x0169, "utilde" # LATIN SMALL LETTER U WITH TILDE
+0x016A, "Umacron" # LATIN CAPITAL LETTER U WITH MACRON
+0x016B, "umacron" # LATIN SMALL LETTER U WITH MACRON
+0x016C, "Ubreve" # LATIN CAPITAL LETTER U WITH BREVE
+0x016D, "ubreve" # LATIN SMALL LETTER U WITH BREVE
+0x016E, "Uring" # LATIN CAPITAL LETTER U WITH RING ABOVE
+0x016F, "uring" # LATIN SMALL LETTER U WITH RING ABOVE
+0x0170, "Uhungarumlaut" # LATIN CAPITAL LETTER U WITH DOUBLE ACUTE
+0x0171, "uhungarumlaut" # LATIN SMALL LETTER U WITH DOUBLE ACUTE
+0x0172, "Uogonek" # LATIN CAPITAL LETTER U WITH OGONEK
+0x0173, "uogonek" # LATIN SMALL LETTER U WITH OGONEK
+0x0174, "Wcircumflex" # LATIN CAPITAL LETTER W WITH CIRCUMFLEX
+0x0175, "wcircumflex" # LATIN SMALL LETTER W WITH CIRCUMFLEX
+0x0176, "Ycircumflex" # LATIN CAPITAL LETTER Y WITH CIRCUMFLEX
+0x0177, "ycircumflex" # LATIN SMALL LETTER Y WITH CIRCUMFLEX
+0x0178, "Ydieresis" # LATIN CAPITAL LETTER Y WITH DIAERESIS
+0x0179, "Zacute" # LATIN CAPITAL LETTER Z WITH ACUTE
+0x017A, "zacute" # LATIN SMALL LETTER Z WITH ACUTE
+0x017B, "Zdotaccent" # LATIN CAPITAL LETTER Z WITH DOT ABOVE
+0x017C, "zdotaccent" # LATIN SMALL LETTER Z WITH DOT ABOVE
+0x017D, "Zcaron" # LATIN CAPITAL LETTER Z WITH CARON
+0x017E, "zcaron" # LATIN SMALL LETTER Z WITH CARON
+0x017F, "longs" # LATIN SMALL LETTER LONG S
+0x0192, "florin" # LATIN SMALL LETTER F WITH HOOK
+0x01A0, "Ohorn" # LATIN CAPITAL LETTER O WITH HORN
+0x01A1, "ohorn" # LATIN SMALL LETTER O WITH HORN
+0x01AF, "Uhorn" # LATIN CAPITAL LETTER U WITH HORN
+0x01B0, "uhorn" # LATIN SMALL LETTER U WITH HORN
+0x01E6, "Gcaron" # LATIN CAPITAL LETTER G WITH CARON
+0x01E7, "gcaron" # LATIN SMALL LETTER G WITH CARON
+0x01FA, "Aringacute" # LATIN CAPITAL LETTER A WITH RING ABOVE AND ACUTE
+0x01FB, "aringacute" # LATIN SMALL LETTER A WITH RING ABOVE AND ACUTE
+0x01FC, "AEacute" # LATIN CAPITAL LETTER AE WITH ACUTE
+0x01FD, "aeacute" # LATIN SMALL LETTER AE WITH ACUTE
+0x01FE, "Oslashacute" # LATIN CAPITAL LETTER O WITH STROKE AND ACUTE
+0x01FF, "oslashacute" # LATIN SMALL LETTER O WITH STROKE AND ACUTE
+0x0218, "Scommaaccent" # LATIN CAPITAL LETTER S WITH COMMA BELOW
+0x0219, "scommaaccent" # LATIN SMALL LETTER S WITH COMMA BELOW
+0x021A, "Tcommaaccent" # LATIN CAPITAL LETTER T WITH COMMA BELOW
+0x021B, "tcommaaccent" # LATIN SMALL LETTER T WITH COMMA BELOW
+0x02BC, "afii57929" # MODIFIER LETTER APOSTROPHE
+0x02BD, "afii64937" # MODIFIER LETTER REVERSED COMMA
+0x02C6, "circumflex" # MODIFIER LETTER CIRCUMFLEX ACCENT
+0x02C7, "caron" # CARON
+0x02D8, "breve" # BREVE
+0x02D9, "dotaccent" # DOT ABOVE
+0x02DA, "ring" # RING ABOVE
+0x02DB, "ogonek" # OGONEK
+0x02DC, "tilde" # SMALL TILDE
+0x02DD, "hungarumlaut" # DOUBLE ACUTE ACCENT
+0x0300, "gravecomb" # COMBINING GRAVE ACCENT
+0x0301, "acutecomb" # COMBINING ACUTE ACCENT
+0x0303, "tildecomb" # COMBINING TILDE
+0x0309, "hookabovecomb" # COMBINING HOOK ABOVE
+0x0323, "dotbelowcomb" # COMBINING DOT BELOW
+0x0384, "tonos" # GREEK TONOS
+0x0385, "dieresistonos" # GREEK DIALYTIKA TONOS
+0x0386, "Alphatonos" # GREEK CAPITAL LETTER ALPHA WITH TONOS
+0x0387, "anoteleia" # GREEK ANO TELEIA
+0x0388, "Epsilontonos" # GREEK CAPITAL LETTER EPSILON WITH TONOS
+0x0389, "Etatonos" # GREEK CAPITAL LETTER ETA WITH TONOS
+0x038A, "Iotatonos" # GREEK CAPITAL LETTER IOTA WITH TONOS
+0x038C, "Omicrontonos" # GREEK CAPITAL LETTER OMICRON WITH TONOS
+0x038E, "Upsilontonos" # GREEK CAPITAL LETTER UPSILON WITH TONOS
+0x038F, "Omegatonos" # GREEK CAPITAL LETTER OMEGA WITH TONOS
+0x0390, "iotadieresistonos" # GREEK SMALL LETTER IOTA WITH DIALYTIKA AND TONOS
+0x0391, "Alpha" # GREEK CAPITAL LETTER ALPHA
+0x0392, "Beta" # GREEK CAPITAL LETTER BETA
+0x0393, "Gamma" # GREEK CAPITAL LETTER GAMMA
+0x0394, "Delta" # GREEK CAPITAL LETTER DELTA
+0x0395, "Epsilon" # GREEK CAPITAL LETTER EPSILON
+0x0396, "Zeta" # GREEK CAPITAL LETTER ZETA
+0x0397, "Eta" # GREEK CAPITAL LETTER ETA
+0x0398, "Theta" # GREEK CAPITAL LETTER THETA
+0x0399, "Iota" # GREEK CAPITAL LETTER IOTA
+0x039A, "Kappa" # GREEK CAPITAL LETTER KAPPA
+0x039B, "Lambda" # GREEK CAPITAL LETTER LAMDA
+0x039C, "Mu" # GREEK CAPITAL LETTER MU
+0x039D, "Nu" # GREEK CAPITAL LETTER NU
+0x039E, "Xi" # GREEK CAPITAL LETTER XI
+0x039F, "Omicron" # GREEK CAPITAL LETTER OMICRON
+0x03A0, "Pi" # GREEK CAPITAL LETTER PI
+0x03A1, "Rho" # GREEK CAPITAL LETTER RHO
+0x03A3, "Sigma" # GREEK CAPITAL LETTER SIGMA
+0x03A4, "Tau" # GREEK CAPITAL LETTER TAU
+0x03A5, "Upsilon" # GREEK CAPITAL LETTER UPSILON
+0x03A6, "Phi" # GREEK CAPITAL LETTER PHI
+0x03A7, "Chi" # GREEK CAPITAL LETTER CHI
+0x03A8, "Psi" # GREEK CAPITAL LETTER PSI
+0x03A9, "Omega" # GREEK CAPITAL LETTER OMEGA
+0x03AA, "Iotadieresis" # GREEK CAPITAL LETTER IOTA WITH DIALYTIKA
+0x03AB, "Upsilondieresis" # GREEK CAPITAL LETTER UPSILON WITH DIALYTIKA
+0x03AC, "alphatonos" # GREEK SMALL LETTER ALPHA WITH TONOS
+0x03AD, "epsilontonos" # GREEK SMALL LETTER EPSILON WITH TONOS
+0x03AE, "etatonos" # GREEK SMALL LETTER ETA WITH TONOS
+0x03AF, "iotatonos" # GREEK SMALL LETTER IOTA WITH TONOS
+0x03B0, "upsilondieresistonos" # GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND TONOS
+0x03B1, "alpha" # GREEK SMALL LETTER ALPHA
+0x03B2, "beta" # GREEK SMALL LETTER BETA
+0x03B3, "gamma" # GREEK SMALL LETTER GAMMA
+0x03B4, "delta" # GREEK SMALL LETTER DELTA
+0x03B5, "epsilon" # GREEK SMALL LETTER EPSILON
+0x03B6, "zeta" # GREEK SMALL LETTER ZETA
+0x03B7, "eta" # GREEK SMALL LETTER ETA
+0x03B8, "theta" # GREEK SMALL LETTER THETA
+0x03B9, "iota" # GREEK SMALL LETTER IOTA
+0x03BA, "kappa" # GREEK SMALL LETTER KAPPA
+0x03BB, "lambda" # GREEK SMALL LETTER LAMDA
+0x03BC, "mu" # GREEK SMALL LETTER MU;Duplicate
+0x03BD, "nu" # GREEK SMALL LETTER NU
+0x03BE, "xi" # GREEK SMALL LETTER XI
+0x03BF, "omicron" # GREEK SMALL LETTER OMICRON
+0x03C0, "pi" # GREEK SMALL LETTER PI
+0x03C1, "rho" # GREEK SMALL LETTER RHO
+0x03C2, "sigma1" # GREEK SMALL LETTER FINAL SIGMA
+0x03C3, "sigma" # GREEK SMALL LETTER SIGMA
+0x03C4, "tau" # GREEK SMALL LETTER TAU
+0x03C5, "upsilon" # GREEK SMALL LETTER UPSILON
+0x03C6, "phi" # GREEK SMALL LETTER PHI
+0x03C7, "chi" # GREEK SMALL LETTER CHI
+0x03C8, "psi" # GREEK SMALL LETTER PSI
+0x03C9, "omega" # GREEK SMALL LETTER OMEGA
+0x03CA, "iotadieresis" # GREEK SMALL LETTER IOTA WITH DIALYTIKA
+0x03CB, "upsilondieresis" # GREEK SMALL LETTER UPSILON WITH DIALYTIKA
+0x03CC, "omicrontonos" # GREEK SMALL LETTER OMICRON WITH TONOS
+0x03CD, "upsilontonos" # GREEK SMALL LETTER UPSILON WITH TONOS
+0x03CE, "omegatonos" # GREEK SMALL LETTER OMEGA WITH TONOS
+0x03D1, "theta1" # GREEK THETA SYMBOL
+0x03D2, "Upsilon1" # GREEK UPSILON WITH HOOK SYMBOL
+0x03D5, "phi1" # GREEK PHI SYMBOL
+0x03D6, "omega1" # GREEK PI SYMBOL
+# end of stuff from glyphlist.txt
+0xFFFF, ""
diff --git a/src/gui/painting/qpsprinter.ps b/src/gui/painting/qpsprinter.ps
new file mode 100644
index 0000000000..ef3f42970b
--- /dev/null
+++ b/src/gui/painting/qpsprinter.ps
@@ -0,0 +1,449 @@
+% the postscript header we use for our qpsprinter in uncompressed and commented form.
+% use the makepsheader perl script to generate a compressed version of this header
+% you can then paste into qpsprinter.cpp
+%
+% some compression of the code is done by the makepsheader script, so we don't need to
+% write too criptically here.
+
+/BD {bind def} bind def
+/d2 {dup dup} BD
+/ED {exch def} BD
+/D0 {0 ED} BD
+
+/F {setfont} BD
+/RL {rlineto} BD
+/CM {currentmatrix} BD
+/SM {setmatrix} BD
+/TR {translate} BD
+/SD {setdash} BD
+/SC {aload pop setrgbcolor} BD
+/CR {currentfile read pop} BD
+/i {index} BD
+/scs {setcolorspace} BD
+/DB {dict dup begin} BD
+/DE {end def} BD
+/ie {ifelse} BD
+/gs {gsave} BD
+/gr {grestore} BD
+
+% these use PDF syntax
+/w {setlinewidth} BD
+/d {setdash} BD
+/J {setlinecap} BD
+/j {setlinejoin} BD
+/scn {3 array astore /BCol exch def} BD
+/SCN {3 array astore /PCol exch def} BD
+/cm {6 array astore concat} BD
+
+/m {moveto} BD
+/l {lineto} BD
+/c {curveto} BD
+/h {closepath} BD
+
+/W {clip} BD
+/W* {eoclip} BD
+/n {newpath} BD
+% ENDUNCOMPRESSED: Warning: leave this line in.
+% Everything before this line will be left untouched by the compression
+
+/q {gsave 10 dict begin} BD
+/Q {end grestore} BD
+
+% PDF operators
+/re { % PDF re operator
+ 4 2 roll % w h x y
+ moveto % w h
+ dup % w h h
+ 0 exch rlineto % w h
+ exch 0 rlineto % h
+ 0 exch neg rlineto
+ closepath
+} bind def
+
+/S {
+ gsave
+ PCol SC stroke
+ grestore
+ newpath
+} BD
+
+% PDF text operators
+/BT {gsave 10 dict begin /_m matrix currentmatrix def BCol SC} BD
+/ET {end grestore} BD
+/Tf {
+ /_fs exch def
+ findfont
+ [ _fs 0 0 _fs 0 0 ]
+ makefont
+ setfont
+} BD
+/Tm {6 array astore concat} BD
+/Td {translate} BD
+/Tj {0 0 moveto show} BD
+/BDC {pop pop} BD
+/EMC {} BD
+
+% old operators
+
+/BSt 0 def % brush style
+/WFi false def % winding fill
+
+/BCol [ 1 1 1 ] def % brush color
+/PCol [ 0 0 0 ] def % pen color
+/BDArr [ % Brush dense patterns
+ 0.94
+ 0.88
+ 0.63
+ 0.50
+ 0.37
+ 0.12
+ 0.06
+] def
+
+% -- level3 true/false
+/level3 {
+ /languagelevel where {
+ pop
+ languagelevel 3 ge
+ } { false } ifelse
+} bind def
+
+
+%% image drawing routines
+
+% defines for QCI
+/QCIgray D0 /QCIcolor D0 /QCIindex D0
+
+% this method prints color images if colorimage is available, otherwise
+% converts the string to a grayscale image and uses the reular postscript image
+% operator for printing.
+% Arguments are the same as for the image operator:
+%
+% width height bits/sample matrix datasrc QCI -
+/QCI {
+ /colorimage where {
+ pop
+ false 3 colorimage
+ }{ % the hard way, based on PD code by John Walker <kelvin@autodesk.com>
+ exec /QCIcolor exch def
+ /QCIgray QCIcolor length 3 idiv string def
+ 0 1 QCIcolor length 3 idiv 1 sub
+ { /QCIindex exch def
+ /_x QCIindex 3 mul def
+ QCIgray QCIindex
+ QCIcolor _x get 0.30 mul
+ QCIcolor _x 1 add get 0.59 mul
+ QCIcolor _x 2 add get 0.11 mul
+ add add cvi
+ put
+ } for
+ QCIgray image
+ } ifelse
+} bind def
+
+% general image drawing routine, used from the postscript driver
+%
+% Draws images with and without mask with 1, 8 and 24(rgb) bits depth.
+%
+% width height matrix image 1|8|24 mask|false x y di
+%
+% width and height specify the width/height of the image,
+% matrix a transformation matrix, image a procedure holding the image data
+% (same for mask) and x/y an additional translation.
+%
+% ### should move the translation into the matrix!!!
+/di
+{
+ gsave
+ translate
+ 1 index 1 eq { % bitmap
+ pop pop % get rid of mask and depth
+ false 3 1 roll % width height false matrix image
+ BCol SC
+ imagemask
+ } {
+ dup false ne {
+ % have a mask, see if we can use it
+ level3
+ } {
+ false
+ } ifelse
+
+ {
+ % languagelevel3, we can use image mask and dicts
+
+ % store the image mask
+ /_ma exch def
+ % select colorspace according to 8|24 bit depth and set the decode array /dc
+ 8 eq {
+ /_dc [0 1] def
+ /DeviceGray
+ } {
+ /_dc [0 1 0 1 0 1] def
+ /DeviceRGB
+ } ifelse
+ setcolorspace
+ % the image data
+ /_im exch def
+ % transformation matrix
+ /_mt exch def
+ % width and height
+ /_h exch def
+ /_w exch def
+ % and the combined image dict
+ <<
+ /ImageType 3
+ % the image dict
+ /DataDict <<
+ /ImageType 1
+ /Width _w
+ /Height _h
+ /ImageMatrix _mt
+ /DataSource _im
+ /BitsPerComponent 8
+ /Decode _dc
+ >>
+ % the mask dictionary
+ /MaskDict <<
+ /ImageType 1
+ /Width _w
+ /Height _h
+ /ImageMatrix _mt
+ /DataSource _ma
+ /BitsPerComponent 1
+ /Decode [0 1]
+ >>
+ /InterleaveType 3
+ >>
+ image
+ } {
+ pop % no mask or can't use it, get rid of it
+ 8 % width height image 8|24 8 matrix
+ 4 1 roll
+ 8 eq { % grayscale
+ image
+ } { %color
+ QCI
+ } ifelse
+ } ifelse
+ } ifelse
+ grestore
+} bind def
+
+
+/BF { % brush fill
+ gsave
+ BSt 1 eq % solid brush?
+ {
+ BCol SC
+ WFi { fill } { eofill } ifelse
+ } if
+ BSt 2 ge BSt 8 le and % dense pattern?
+ {
+ BDArr BSt 2 sub get /_sc exch def
+ % the following line scales the brush color according to the pattern. the higher the pattern the lighter the color.
+ BCol
+ {
+ 1. exch sub _sc mul 1. exch sub
+ } forall
+ 3 array astore
+ SC
+ WFi { fill } { eofill } ifelse
+ } if
+ BSt 9 ge BSt 14 le and % brush pattern?
+ {
+ WFi { clip } { eoclip } ifelse
+ pathbbox % left upper right lower
+ 3 index 3 index translate
+ 4 2 roll % right lower left upper
+ 3 2 roll % right left upper lower
+ exch % left right lower upper
+ sub /_h exch def
+ sub /_w exch def
+ BCol SC
+ 0.3 setlinewidth
+ newpath
+ BSt 9 eq BSt 11 eq or % horiz or cross pattern
+ { 0 4 _h
+ { dup 0 exch moveto _w exch lineto } for
+ } if
+ BSt 10 eq BSt 11 eq or % vert or cross pattern
+ { 0 4 _w
+ { dup 0 moveto _h lineto } for
+ } if
+ BSt 12 eq BSt 14 eq or % F-diag or diag cross
+ { _w _h gt
+ { 0 6 _w _h add
+ { dup 0 moveto _h sub _h lineto } for
+ } { 0 6 _w _h add
+ { dup 0 exch moveto _w sub _w exch lineto } for
+ } ifelse
+ } if
+ BSt 13 eq BSt 14 eq or % B-diag or diag cross
+ { _w _h gt
+ { 0 6 _w _h add
+ { dup _h moveto _h sub 0 lineto } for
+ } { 0 6 _w _h add
+ { dup _w exch moveto _w sub 0 exch lineto } for
+ } ifelse
+ } if
+ stroke
+ } if
+ BSt 15 eq
+ {
+ } if
+ BSt 24 eq % TexturePattern
+ {
+ } if
+ grestore
+} bind def
+
+% more PDF operators
+/f { /WFi true def BF newpath } bind def
+/f* { /WFi false def BF newpath } bind def
+/B { /WFi true def BF S newpath } bind def
+/B* { /WFi false def BF S newpath } bind def
+
+%% start of page
+/QI {
+ /C save def
+ pageinit
+ q
+ newpath
+} bind def
+
+%% end of page
+/QP {
+ Q % show page
+ C restore
+ showpage
+} bind def
+
+% merges one key value pair into the page device dict
+%
+% key value SPD -
+/SPD {
+ /setpagedevice where {
+ << 3 1 roll >>
+ setpagedevice
+ } { pop pop } ifelse
+} bind def
+
+
+% font handling
+
+/T1AddMapping { % basefont [glyphname ...] T1AddMapping -
+ 10 dict begin
+ /glyphs exch def
+ /fnt exch def
+ /current fnt /NumGlyphs get def
+ /CMap fnt /CMap get def
+
+ 0 1 glyphs length 1 sub % 0 1 (num glyphs - 1)
+ {
+ glyphs exch get /gn exch def
+
+ current dup % glyph_index glyph_index
+ 256 mod /min exch def % glyph_index
+ 256 idiv /maj exch def % -
+ CMap dup maj get dup % cmap cmap_maj cmap_maj
+ null eq {
+ pop 256 array
+ 0 1 255 {1 index exch /.notdef put} for
+ } if
+ dup % cmap cmap_maj cmap_maj
+ min gn put % cmap cmap_maj
+ maj exch put % -
+
+ /current current 1 add def
+ } for
+
+ fnt /CMap CMap put
+ fnt /NumGlyphs current put
+ end
+} def
+
+/T1AddGlyphs { % basefont [glyphname charstring ...] T1AddGlyphs -
+ 10 dict begin
+ /glyphs exch def
+ /fnt exch def
+ /current fnt /NumGlyphs get def
+ /CMap fnt /CMap get def
+ /CharStrings fnt /CharStrings get def
+
+ 0 1 glyphs length 2 idiv 1 sub % 0 1 (num glyphs - 1)
+ {
+ 2 mul dup
+ glyphs exch get /gn exch def
+ 1 add
+ glyphs exch get /cs exch def
+
+ current dup % glyph_index glyph_index
+ 256 mod /min exch def % glyph_index
+ 256 idiv /maj exch def % -
+ CMap dup maj get dup % cmap cmap_maj cmap_maj
+ null eq {
+ pop 256 array
+ 0 1 255 {1 index exch /.notdef put} for
+ } if
+ dup % cmap cmap_maj cmap_maj
+ min gn put % cmap cmap_maj
+ maj exch put % -
+
+ CharStrings gn cs put
+ /current current 1 add def
+ } for
+
+ fnt /CharStrings CharStrings put
+ fnt /CMap CMap put
+ fnt /NumGlyphs current put
+ end
+} def
+
+
+
+/StringAdd { % string1 string2 stringadd result
+ 1 index length 1 index length add
+ string
+ 3 1 roll
+ 2 index 0 3 index putinterval
+ 2 index 2 index length 2 index putinterval
+ pop pop
+} def
+
+
+/T1Setup { % fontname T1Setup -
+10 dict begin
+ dup /FontName exch def
+ (-Base) StringAdd cvx cvn /Font exch def
+ /MaxPage Font /NumGlyphs get 1 sub 256 idiv def
+
+ /FDepVector MaxPage 1 add array def
+ /Encoding MaxPage 1 add array def
+
+ 0 1 MaxPage {
+ dup Encoding exch dup put
+
+
+ dup /Page exch def
+ FontName (-) StringAdd
+ exch
+ 20 string cvs StringAdd % page fontname
+ cvn
+
+ Font 0 dict copy dup dup /CMap get
+ Page get
+ /Encoding exch put definefont
+ FDepVector exch Page exch put
+ } for
+
+ FontName cvn <<
+ /FontType 0
+ /FMapType 2
+ /FontMatrix[1 0 0 1 0 0]
+ /Encoding Encoding
+ /FDepVector FDepVector
+ >> definefont pop
+ end
+} def
+
diff --git a/src/gui/painting/qrasterdefs_p.h b/src/gui/painting/qrasterdefs_p.h
new file mode 100644
index 0000000000..cde98db820
--- /dev/null
+++ b/src/gui/painting/qrasterdefs_p.h
@@ -0,0 +1,1280 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/***************************************************************************/
+/* */
+/* ftimage.h */
+/* */
+/* FreeType glyph image formats and default raster interface */
+/* (specification). */
+/* */
+/* Copyright 1996-2001, 2002, 2003, 2004 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+ /*************************************************************************/
+ /* */
+ /* Note: A `raster' is simply a scan-line converter, used to render */
+ /* QT_FT_Outlines into QT_FT_Bitmaps. */
+ /* */
+ /*************************************************************************/
+
+
+#ifndef __QT_FTIMAGE_H__
+#define __QT_FTIMAGE_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.
+*/
+
+QT_FT_BEGIN_HEADER
+
+
+ /*************************************************************************/
+ /* */
+ /* <Section> */
+ /* basic_types */
+ /* */
+ /*************************************************************************/
+
+
+ /*************************************************************************/
+ /* */
+ /* <Type> */
+ /* QT_FT_Pos */
+ /* */
+ /* <Description> */
+ /* The type QT_FT_Pos is a 32-bit integer used to store vectorial */
+ /* coordinates. Depending on the context, these can represent */
+ /* distances in integer font units, or 16,16, or 26.6 fixed float */
+ /* pixel coordinates. */
+ /* */
+ typedef signed long QT_FT_Pos;
+
+
+ /*************************************************************************/
+ /* */
+ /* <Struct> */
+ /* QT_FT_Vector */
+ /* */
+ /* <Description> */
+ /* A simple structure used to store a 2D vector; coordinates are of */
+ /* the QT_FT_Pos type. */
+ /* */
+ /* <Fields> */
+ /* x :: The horizontal coordinate. */
+ /* y :: The vertical coordinate. */
+ /* */
+ typedef struct QT_FT_Vector_
+ {
+ QT_FT_Pos x;
+ QT_FT_Pos y;
+
+ } QT_FT_Vector;
+
+
+ /*************************************************************************/
+ /* */
+ /* <Struct> */
+ /* QT_FT_BBox */
+ /* */
+ /* <Description> */
+ /* A structure used to hold an outline's bounding box, i.e., the */
+ /* coordinates of its extrema in the horizontal and vertical */
+ /* directions. */
+ /* */
+ /* <Fields> */
+ /* xMin :: The horizontal minimum (left-most). */
+ /* */
+ /* yMin :: The vertical minimum (bottom-most). */
+ /* */
+ /* xMax :: The horizontal maximum (right-most). */
+ /* */
+ /* yMax :: The vertical maximum (top-most). */
+ /* */
+ typedef struct QT_FT_BBox_
+ {
+ QT_FT_Pos xMin, yMin;
+ QT_FT_Pos xMax, yMax;
+
+ } QT_FT_BBox;
+
+
+ /*************************************************************************/
+ /* */
+ /* <Enum> */
+ /* QT_FT_Pixel_Mode */
+ /* */
+ /* <Description> */
+ /* An enumeration type used to describe the format of pixels in a */
+ /* given bitmap. Note that additional formats may be added in the */
+ /* future. */
+ /* */
+ /* <Values> */
+ /* QT_FT_PIXEL_MODE_NONE :: */
+ /* Value 0 is reserved. */
+ /* */
+ /* QT_FT_PIXEL_MODE_MONO :: */
+ /* A monochrome bitmap, using 1 bit per pixel. Note that pixels */
+ /* are stored in most-significant order (MSB), which means that */
+ /* the left-most pixel in a byte has value 128. */
+ /* */
+ /* QT_FT_PIXEL_MODE_GRAY :: */
+ /* An 8-bit bitmap, generally used to represent anti-aliased glyph */
+ /* images. Each pixel is stored in one byte. Note that the number */
+ /* of value "gray" levels is stored in the `num_bytes' field of */
+ /* the @QT_FT_Bitmap structure (it generally is 256). */
+ /* */
+ /* QT_FT_PIXEL_MODE_GRAY2 :: */
+ /* A 2-bit/pixel bitmap, used to represent embedded anti-aliased */
+ /* bitmaps in font files according to the OpenType specification. */
+ /* We haven't found a single font using this format, however. */
+ /* */
+ /* QT_FT_PIXEL_MODE_GRAY4 :: */
+ /* A 4-bit/pixel bitmap, used to represent embedded anti-aliased */
+ /* bitmaps in font files according to the OpenType specification. */
+ /* We haven't found a single font using this format, however. */
+ /* */
+ /* QT_FT_PIXEL_MODE_LCD :: */
+ /* An 8-bit bitmap, used to represent RGB or BGR decimated glyph */
+ /* images used for display on LCD displays; the bitmap's width is */
+ /* three times wider than the original glyph image. See also */
+ /* @QT_FT_RENDER_MODE_LCD. */
+ /* */
+ /* QT_FT_PIXEL_MODE_LCD_V :: */
+ /* An 8-bit bitmap, used to represent RGB or BGR decimated glyph */
+ /* images used for display on rotated LCD displays; the bitmap's */
+ /* height is three times taller than the original glyph image. */
+ /* See also @QT_FT_RENDER_MODE_LCD_V. */
+ /* */
+ typedef enum QT_FT_Pixel_Mode_
+ {
+ QT_FT_PIXEL_MODE_NONE = 0,
+ QT_FT_PIXEL_MODE_MONO,
+ QT_FT_PIXEL_MODE_GRAY,
+ QT_FT_PIXEL_MODE_GRAY2,
+ QT_FT_PIXEL_MODE_GRAY4,
+ QT_FT_PIXEL_MODE_LCD,
+ QT_FT_PIXEL_MODE_LCD_V,
+
+ QT_FT_PIXEL_MODE_MAX /* do not remove */
+
+ } QT_FT_Pixel_Mode;
+
+
+ /*************************************************************************/
+ /* */
+ /* <Enum> */
+ /* qt_ft_pixel_mode_xxx */
+ /* */
+ /* <Description> */
+ /* A list of deprecated constants. Use the corresponding */
+ /* @QT_FT_Pixel_Mode values instead. */
+ /* */
+ /* <Values> */
+ /* qt_ft_pixel_mode_none :: see @QT_FT_PIXEL_MODE_NONE */
+ /* qt_ft_pixel_mode_mono :: see @QT_FT_PIXEL_MODE_MONO */
+ /* qt_ft_pixel_mode_grays :: see @QT_FT_PIXEL_MODE_GRAY */
+ /* qt_ft_pixel_mode_pal2 :: see @QT_FT_PIXEL_MODE_GRAY2 */
+ /* qt_ft_pixel_mode_pal4 :: see @QT_FT_PIXEL_MODE_GRAY4 */
+ /* */
+#define qt_ft_pixel_mode_none QT_FT_PIXEL_MODE_NONE
+#define qt_ft_pixel_mode_mono QT_FT_PIXEL_MODE_MONO
+#define qt_ft_pixel_mode_grays QT_FT_PIXEL_MODE_GRAY
+#define qt_ft_pixel_mode_pal2 QT_FT_PIXEL_MODE_GRAY2
+#define qt_ft_pixel_mode_pal4 QT_FT_PIXEL_MODE_GRAY4
+
+ /* */
+
+#if 0
+
+ /*************************************************************************/
+ /* */
+ /* <Enum> */
+ /* QT_FT_Palette_Mode */
+ /* */
+ /* <Description> */
+ /* THIS TYPE IS DEPRECATED. DO NOT USE IT! */
+ /* */
+ /* An enumeration type used to describe the format of a bitmap */
+ /* palette, used with qt_ft_pixel_mode_pal4 and qt_ft_pixel_mode_pal8. */
+ /* */
+ /* <Fields> */
+ /* qt_ft_palette_mode_rgb :: The palette is an array of 3-bytes RGB */
+ /* records. */
+ /* */
+ /* qt_ft_palette_mode_rgba :: The palette is an array of 4-bytes RGBA */
+ /* records. */
+ /* */
+ /* <Note> */
+ /* As qt_ft_pixel_mode_pal2, pal4 and pal8 are currently unused by */
+ /* FreeType, these types are not handled by the library itself. */
+ /* */
+ typedef enum QT_FT_Palette_Mode_
+ {
+ qt_ft_palette_mode_rgb = 0,
+ qt_ft_palette_mode_rgba,
+
+ qt_ft_palettte_mode_max /* do not remove */
+
+ } QT_FT_Palette_Mode;
+
+ /* */
+
+#endif
+
+
+ /*************************************************************************/
+ /* */
+ /* <Struct> */
+ /* QT_FT_Bitmap */
+ /* */
+ /* <Description> */
+ /* A structure used to describe a bitmap or pixmap to the raster. */
+ /* Note that we now manage pixmaps of various depths through the */
+ /* `pixel_mode' field. */
+ /* */
+ /* <Fields> */
+ /* rows :: The number of bitmap rows. */
+ /* */
+ /* width :: The number of pixels in bitmap row. */
+ /* */
+ /* pitch :: The pitch's absolute value is the number of bytes */
+ /* taken by one bitmap row, including padding. */
+ /* However, the pitch is positive when the bitmap has */
+ /* a `down' flow, and negative when it has an `up' */
+ /* flow. In all cases, the pitch is an offset to add */
+ /* to a bitmap pointer in order to go down one row. */
+ /* */
+ /* buffer :: A typeless pointer to the bitmap buffer. This */
+ /* value should be aligned on 32-bit boundaries in */
+ /* most cases. */
+ /* */
+ /* num_grays :: This field is only used with */
+ /* `QT_FT_PIXEL_MODE_GRAY'; it gives the number of gray */
+ /* levels used in the bitmap. */
+ /* */
+ /* pixel_mode :: The pixel mode, i.e., how pixel bits are stored. */
+ /* See @QT_FT_Pixel_Mode for possible values. */
+ /* */
+ /* palette_mode :: This field is only used with paletted pixel modes; */
+ /* it indicates how the palette is stored. */
+ /* */
+ /* palette :: A typeless pointer to the bitmap palette; only */
+ /* used for paletted pixel modes. */
+ /* */
+ /* <Note> */
+ /* For now, the only pixel mode supported by FreeType are mono and */
+ /* grays. However, drivers might be added in the future to support */
+ /* more `colorful' options. */
+ /* */
+ /* When using pixel modes pal2, pal4 and pal8 with a void `palette' */
+ /* field, a gray pixmap with respectively 4, 16, and 256 levels of */
+ /* gray is assumed. This, in order to be compatible with some */
+ /* embedded bitmap formats defined in the TrueType specification. */
+ /* */
+ /* Note that no font was found presenting such embedded bitmaps, so */
+ /* this is currently completely unhandled by the library. */
+ /* */
+ typedef struct QT_FT_Bitmap_
+ {
+ int rows;
+ int width;
+ int pitch;
+ unsigned char* buffer;
+ short num_grays;
+ char pixel_mode;
+ char palette_mode;
+ void* palette;
+
+ } QT_FT_Bitmap;
+
+
+ /*************************************************************************/
+ /* */
+ /* <Section> */
+ /* outline_processing */
+ /* */
+ /*************************************************************************/
+
+
+ /*************************************************************************/
+ /* */
+ /* <Struct> */
+ /* QT_FT_Outline */
+ /* */
+ /* <Description> */
+ /* This structure is used to describe an outline to the scan-line */
+ /* converter. */
+ /* */
+ /* <Fields> */
+ /* n_contours :: The number of contours in the outline. */
+ /* */
+ /* n_points :: The number of points in the outline. */
+ /* */
+ /* points :: A pointer to an array of `n_points' QT_FT_Vector */
+ /* elements, giving the outline's point coordinates. */
+ /* */
+ /* tags :: A pointer to an array of `n_points' chars, giving */
+ /* each outline point's type. If bit 0 is unset, the */
+ /* point is `off' the curve, i.e. a Bezier control */
+ /* point, while it is `on' when set. */
+ /* */
+ /* Bit 1 is meaningful for `off' points only. If set, */
+ /* it indicates a third-order Bezier arc control point; */
+ /* and a second-order control point if unset. */
+ /* */
+ /* contours :: An array of `n_contours' shorts, giving the end */
+ /* point of each contour within the outline. For */
+ /* example, the first contour is defined by the points */
+ /* `0' to `contours[0]', the second one is defined by */
+ /* the points `contours[0]+1' to `contours[1]', etc. */
+ /* */
+ /* flags :: A set of bit flags used to characterize the outline */
+ /* and give hints to the scan-converter and hinter on */
+ /* how to convert/grid-fit it. See QT_FT_Outline_Flags. */
+ /* */
+ typedef struct QT_FT_Outline_
+ {
+ int n_contours; /* number of contours in glyph */
+ int n_points; /* number of points in the glyph */
+
+ QT_FT_Vector* points; /* the outline's points */
+ char* tags; /* the points flags */
+ int* contours; /* the contour end points */
+
+ int flags; /* outline masks */
+
+ } QT_FT_Outline;
+
+
+ /*************************************************************************/
+ /* */
+ /* <Enum> */
+ /* QT_FT_OUTLINE_FLAGS */
+ /* */
+ /* <Description> */
+ /* A list of bit-field constants use for the flags in an outline's */
+ /* `flags' field. */
+ /* */
+ /* <Values> */
+ /* QT_FT_OUTLINE_NONE :: Value 0 is reserved. */
+ /* */
+ /* QT_FT_OUTLINE_OWNER :: If set, this flag indicates that the */
+ /* outline's field arrays (i.e. */
+ /* `points', `flags' & `contours') are */
+ /* `owned' by the outline object, and */
+ /* should thus be freed when it is */
+ /* destroyed. */
+ /* */
+ /* QT_FT_OUTLINE_EVEN_ODD_FILL :: By default, outlines are filled using */
+ /* the non-zero winding rule. If set to */
+ /* 1, the outline will be filled using */
+ /* the even-odd fill rule (only works */
+ /* with the smooth raster). */
+ /* */
+ /* QT_FT_OUTLINE_REVERSE_FILL :: By default, outside contours of an */
+ /* outline are oriented in clock-wise */
+ /* direction, as defined in the TrueType */
+ /* specification. This flag is set if */
+ /* the outline uses the opposite */
+ /* direction (typically for Type 1 */
+ /* fonts). This flag is ignored by the */
+ /* scan-converter. However, it is very */
+ /* important for the auto-hinter. */
+ /* */
+ /* QT_FT_OUTLINE_IGNORE_DROPOUTS :: By default, the scan converter will */
+ /* try to detect drop-outs in an outline */
+ /* and correct the glyph bitmap to */
+ /* ensure consistent shape continuity. */
+ /* If set, this flag hints the scan-line */
+ /* converter to ignore such cases. */
+ /* */
+ /* QT_FT_OUTLINE_HIGH_PRECISION :: This flag indicates that the */
+ /* scan-line converter should try to */
+ /* convert this outline to bitmaps with */
+ /* the highest possible quality. It is */
+ /* typically set for small character */
+ /* sizes. Note that this is only a */
+ /* hint, that might be completely */
+ /* ignored by a given scan-converter. */
+ /* */
+ /* QT_FT_OUTLINE_SINGLE_PASS :: This flag is set to force a given */
+ /* scan-converter to only use a single */
+ /* pass over the outline to render a */
+ /* bitmap glyph image. Normally, it is */
+ /* set for very large character sizes. */
+ /* It is only a hint, that might be */
+ /* completely ignored by a given */
+ /* scan-converter. */
+ /* */
+#define QT_FT_OUTLINE_NONE 0x0
+#define QT_FT_OUTLINE_OWNER 0x1
+#define QT_FT_OUTLINE_EVEN_ODD_FILL 0x2
+#define QT_FT_OUTLINE_REVERSE_FILL 0x4
+#define QT_FT_OUTLINE_IGNORE_DROPOUTS 0x8
+
+#define QT_FT_OUTLINE_HIGH_PRECISION 0x100
+#define QT_FT_OUTLINE_SINGLE_PASS 0x200
+
+
+ /*************************************************************************
+ *
+ * @enum:
+ * qt_ft_outline_flags
+ *
+ * @description:
+ * These constants are deprecated. Please use the corresponding
+ * @QT_FT_OUTLINE_FLAGS values.
+ *
+ * @values:
+ * qt_ft_outline_none :: See @QT_FT_OUTLINE_NONE.
+ * qt_ft_outline_owner :: See @QT_FT_OUTLINE_OWNER.
+ * qt_ft_outline_even_odd_fill :: See @QT_FT_OUTLINE_EVEN_ODD_FILL.
+ * qt_ft_outline_reverse_fill :: See @QT_FT_OUTLINE_REVERSE_FILL.
+ * qt_ft_outline_ignore_dropouts :: See @QT_FT_OUTLINE_IGNORE_DROPOUTS.
+ * qt_ft_outline_high_precision :: See @QT_FT_OUTLINE_HIGH_PRECISION.
+ * qt_ft_outline_single_pass :: See @QT_FT_OUTLINE_SINGLE_PASS.
+ */
+#define qt_ft_outline_none QT_FT_OUTLINE_NONE
+#define qt_ft_outline_owner QT_FT_OUTLINE_OWNER
+#define qt_ft_outline_even_odd_fill QT_FT_OUTLINE_EVEN_ODD_FILL
+#define qt_ft_outline_reverse_fill QT_FT_OUTLINE_REVERSE_FILL
+#define qt_ft_outline_ignore_dropouts QT_FT_OUTLINE_IGNORE_DROPOUTS
+#define qt_ft_outline_high_precision QT_FT_OUTLINE_HIGH_PRECISION
+#define qt_ft_outline_single_pass QT_FT_OUTLINE_SINGLE_PASS
+
+ /* */
+
+#define QT_FT_CURVE_TAG( flag ) ( flag & 3 )
+
+#define QT_FT_CURVE_TAG_ON 1
+#define QT_FT_CURVE_TAG_CONIC 0
+#define QT_FT_CURVE_TAG_CUBIC 2
+
+#define QT_FT_CURVE_TAG_TOUCH_X 8 /* reserved for the TrueType hinter */
+#define QT_FT_CURVE_TAG_TOUCH_Y 16 /* reserved for the TrueType hinter */
+
+#define QT_FT_CURVE_TAG_TOUCH_BOTH ( QT_FT_CURVE_TAG_TOUCH_X | \
+ QT_FT_CURVE_TAG_TOUCH_Y )
+
+#define QT_FT_Curve_Tag_On QT_FT_CURVE_TAG_ON
+#define QT_FT_Curve_Tag_Conic QT_FT_CURVE_TAG_CONIC
+#define QT_FT_Curve_Tag_Cubic QT_FT_CURVE_TAG_CUBIC
+#define QT_FT_Curve_Tag_Touch_X QT_FT_CURVE_TAG_TOUCH_X
+#define QT_FT_Curve_Tag_Touch_Y QT_FT_CURVE_TAG_TOUCH_Y
+
+ /*************************************************************************/
+ /* */
+ /* <FuncType> */
+ /* QT_FT_Outline_MoveToFunc */
+ /* */
+ /* <Description> */
+ /* A function pointer type used to describe the signature of a `move */
+ /* to' function during outline walking/decomposition. */
+ /* */
+ /* A `move to' is emitted to start a new contour in an outline. */
+ /* */
+ /* <Input> */
+ /* to :: A pointer to the target point of the `move to'. */
+ /* */
+ /* user :: A typeless pointer which is passed from the caller of the */
+ /* decomposition function. */
+ /* */
+ /* <Return> */
+ /* Error code. 0 means success. */
+ /* */
+ typedef int
+ (*QT_FT_Outline_MoveToFunc)( QT_FT_Vector* to,
+ void* user );
+
+#define QT_FT_Outline_MoveTo_Func QT_FT_Outline_MoveToFunc
+
+ /*************************************************************************/
+ /* */
+ /* <FuncType> */
+ /* QT_FT_Outline_LineToFunc */
+ /* */
+ /* <Description> */
+ /* A function pointer type used to describe the signature of a `line */
+ /* to' function during outline walking/decomposition. */
+ /* */
+ /* A `line to' is emitted to indicate a segment in the outline. */
+ /* */
+ /* <Input> */
+ /* to :: A pointer to the target point of the `line to'. */
+ /* */
+ /* user :: A typeless pointer which is passed from the caller of the */
+ /* decomposition function. */
+ /* */
+ /* <Return> */
+ /* Error code. 0 means success. */
+ /* */
+ typedef int
+ (*QT_FT_Outline_LineToFunc)( QT_FT_Vector* to,
+ void* user );
+
+#define QT_FT_Outline_LineTo_Func QT_FT_Outline_LineToFunc
+
+ /*************************************************************************/
+ /* */
+ /* <FuncType> */
+ /* QT_FT_Outline_ConicToFunc */
+ /* */
+ /* <Description> */
+ /* A function pointer type use to describe the signature of a `conic */
+ /* to' function during outline walking/decomposition. */
+ /* */
+ /* A `conic to' is emitted to indicate a second-order Bezier arc in */
+ /* the outline. */
+ /* */
+ /* <Input> */
+ /* control :: An intermediate control point between the last position */
+ /* and the new target in `to'. */
+ /* */
+ /* to :: A pointer to the target end point of the conic arc. */
+ /* */
+ /* user :: A typeless pointer which is passed from the caller of */
+ /* the decomposition function. */
+ /* */
+ /* <Return> */
+ /* Error code. 0 means success. */
+ /* */
+ typedef int
+ (*QT_FT_Outline_ConicToFunc)( QT_FT_Vector* control,
+ QT_FT_Vector* to,
+ void* user );
+
+#define QT_FT_Outline_ConicTo_Func QT_FT_Outline_ConicToFunc
+
+ /*************************************************************************/
+ /* */
+ /* <FuncType> */
+ /* QT_FT_Outline_CubicToFunc */
+ /* */
+ /* <Description> */
+ /* A function pointer type used to describe the signature of a `cubic */
+ /* to' function during outline walking/decomposition. */
+ /* */
+ /* A `cubic to' is emitted to indicate a third-order Bezier arc. */
+ /* */
+ /* <Input> */
+ /* control1 :: A pointer to the first Bezier control point. */
+ /* */
+ /* control2 :: A pointer to the second Bezier control point. */
+ /* */
+ /* to :: A pointer to the target end point. */
+ /* */
+ /* user :: A typeless pointer which is passed from the caller of */
+ /* the decomposition function. */
+ /* */
+ /* <Return> */
+ /* Error code. 0 means success. */
+ /* */
+ typedef int
+ (*QT_FT_Outline_CubicToFunc)( QT_FT_Vector* control1,
+ QT_FT_Vector* control2,
+ QT_FT_Vector* to,
+ void* user );
+
+#define QT_FT_Outline_CubicTo_Func QT_FT_Outline_CubicToFunc
+
+
+ /*************************************************************************/
+ /* */
+ /* <Struct> */
+ /* QT_FT_Outline_Funcs */
+ /* */
+ /* <Description> */
+ /* A structure to hold various function pointers used during outline */
+ /* decomposition in order to emit segments, conic, and cubic Beziers, */
+ /* as well as `move to' and `close to' operations. */
+ /* */
+ /* <Fields> */
+ /* move_to :: The `move to' emitter. */
+ /* */
+ /* line_to :: The segment emitter. */
+ /* */
+ /* conic_to :: The second-order Bezier arc emitter. */
+ /* */
+ /* cubic_to :: The third-order Bezier arc emitter. */
+ /* */
+ /* shift :: The shift that is applied to coordinates before they */
+ /* are sent to the emitter. */
+ /* */
+ /* delta :: The delta that is applied to coordinates before they */
+ /* are sent to the emitter, but after the shift. */
+ /* */
+ /* <Note> */
+ /* The point coordinates sent to the emitters are the transformed */
+ /* version of the original coordinates (this is important for high */
+ /* accuracy during scan-conversion). The transformation is simple: */
+ /* */
+ /* x' = (x << shift) - delta */
+ /* y' = (x << shift) - delta */
+ /* */
+ /* Set the value of `shift' and `delta' to 0 to get the original */
+ /* point coordinates. */
+ /* */
+ typedef struct QT_FT_Outline_Funcs_
+ {
+ QT_FT_Outline_MoveToFunc move_to;
+ QT_FT_Outline_LineToFunc line_to;
+ QT_FT_Outline_ConicToFunc conic_to;
+ QT_FT_Outline_CubicToFunc cubic_to;
+
+ int shift;
+ QT_FT_Pos delta;
+
+ } QT_FT_Outline_Funcs;
+
+
+ /*************************************************************************/
+ /* */
+ /* <Section> */
+ /* basic_types */
+ /* */
+ /*************************************************************************/
+
+
+ /*************************************************************************/
+ /* */
+ /* <Macro> */
+ /* QT_FT_IMAGE_TAG */
+ /* */
+ /* <Description> */
+ /* This macro converts four letter tags into an unsigned long. */
+ /* */
+ /* <Note> */
+ /* Since many 16bit compilers don't like 32bit enumerations, you */
+ /* should redefine this macro in case of problems to something like */
+ /* this: */
+ /* */
+ /* #define QT_FT_IMAGE_TAG( value, _x1, _x2, _x3, _x4 ) value */
+ /* */
+ /* to get a simple enumeration without assigning special numbers. */
+ /* */
+#ifndef QT_FT_IMAGE_TAG
+#define QT_FT_IMAGE_TAG( value, _x1, _x2, _x3, _x4 ) \
+ value = ( ( (unsigned long)_x1 << 24 ) | \
+ ( (unsigned long)_x2 << 16 ) | \
+ ( (unsigned long)_x3 << 8 ) | \
+ (unsigned long)_x4 )
+#endif /* QT_FT_IMAGE_TAG */
+
+
+ /*************************************************************************/
+ /* */
+ /* <Enum> */
+ /* QT_FT_Glyph_Format */
+ /* */
+ /* <Description> */
+ /* An enumeration type used to describe the format of a given glyph */
+ /* image. Note that this version of FreeType only supports two image */
+ /* formats, even though future font drivers will be able to register */
+ /* their own format. */
+ /* */
+ /* <Values> */
+ /* QT_FT_GLYPH_FORMAT_NONE :: */
+ /* The value 0 is reserved and does describe a glyph format. */
+ /* */
+ /* QT_FT_GLYPH_FORMAT_COMPOSITE :: */
+ /* The glyph image is a composite of several other images. This */
+ /* format is _only_ used with @QT_FT_LOAD_NO_RECURSE, and is used to */
+ /* report compound glyphs (like accented characters). */
+ /* */
+ /* QT_FT_GLYPH_FORMAT_BITMAP :: */
+ /* The glyph image is a bitmap, and can be described as an */
+ /* @QT_FT_Bitmap. You generally need to access the `bitmap' field of */
+ /* the @QT_FT_GlyphSlotRec structure to read it. */
+ /* */
+ /* QT_FT_GLYPH_FORMAT_OUTLINE :: */
+ /* The glyph image is a vertorial outline made of line segments */
+ /* and Bezier arcs; it can be described as an @QT_FT_Outline; you */
+ /* generally want to access the `outline' field of the */
+ /* @QT_FT_GlyphSlotRec structure to read it. */
+ /* */
+ /* QT_FT_GLYPH_FORMAT_PLOTTER :: */
+ /* The glyph image is a vectorial path with no inside/outside */
+ /* contours. Some Type 1 fonts, like those in the Hershey family, */
+ /* contain glyphs in this format. These are described as */
+ /* @QT_FT_Outline, but FreeType isn't currently capable of rendering */
+ /* them correctly. */
+ /* */
+ typedef enum QT_FT_Glyph_Format_
+ {
+ QT_FT_IMAGE_TAG( QT_FT_GLYPH_FORMAT_NONE, 0, 0, 0, 0 ),
+
+ QT_FT_IMAGE_TAG( QT_FT_GLYPH_FORMAT_COMPOSITE, 'c', 'o', 'm', 'p' ),
+ QT_FT_IMAGE_TAG( QT_FT_GLYPH_FORMAT_BITMAP, 'b', 'i', 't', 's' ),
+ QT_FT_IMAGE_TAG( QT_FT_GLYPH_FORMAT_OUTLINE, 'o', 'u', 't', 'l' ),
+ QT_FT_IMAGE_TAG( QT_FT_GLYPH_FORMAT_PLOTTER, 'p', 'l', 'o', 't' )
+
+ } QT_FT_Glyph_Format;
+
+
+ /*************************************************************************/
+ /* */
+ /* <Enum> */
+ /* qt_ft_glyph_format_xxx */
+ /* */
+ /* <Description> */
+ /* A list of decprecated constants. Use the corresponding */
+ /* @QT_FT_Glyph_Format values instead. */
+ /* */
+ /* <Values> */
+ /* qt_ft_glyph_format_none :: see @QT_FT_GLYPH_FORMAT_NONE */
+ /* qt_ft_glyph_format_composite :: see @QT_FT_GLYPH_FORMAT_COMPOSITE */
+ /* qt_ft_glyph_format_bitmap :: see @QT_FT_GLYPH_FORMAT_BITMAP */
+ /* qt_ft_glyph_format_outline :: see @QT_FT_GLYPH_FORMAT_OUTLINE */
+ /* qt_ft_glyph_format_plotter :: see @QT_FT_GLYPH_FORMAT_PLOTTER */
+ /* */
+#define qt_ft_glyph_format_none QT_FT_GLYPH_FORMAT_NONE
+#define qt_ft_glyph_format_composite QT_FT_GLYPH_FORMAT_COMPOSITE
+#define qt_ft_glyph_format_bitmap QT_FT_GLYPH_FORMAT_BITMAP
+#define qt_ft_glyph_format_outline QT_FT_GLYPH_FORMAT_OUTLINE
+#define qt_ft_glyph_format_plotter QT_FT_GLYPH_FORMAT_PLOTTER
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** R A S T E R D E F I N I T I O N S *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+ /*************************************************************************/
+
+
+ /*************************************************************************/
+ /* */
+ /* A raster is a scan converter, in charge of rendering an outline into */
+ /* a a bitmap. This section contains the public API for rasters. */
+ /* */
+ /* Note that in FreeType 2, all rasters are now encapsulated within */
+ /* specific modules called `renderers'. See `freetype/ftrender.h' for */
+ /* more details on renderers. */
+ /* */
+ /*************************************************************************/
+
+
+ /*************************************************************************/
+ /* */
+ /* <Section> */
+ /* raster */
+ /* */
+ /* <Title> */
+ /* Scanline converter */
+ /* */
+ /* <Abstract> */
+ /* How vectorial outlines are converted into bitmaps and pixmaps. */
+ /* */
+ /* <Description> */
+ /* This section contains technical definitions. */
+ /* */
+ /*************************************************************************/
+
+
+ /*************************************************************************/
+ /* */
+ /* <Type> */
+ /* QT_FT_Raster */
+ /* */
+ /* <Description> */
+ /* A handle (pointer) to a raster object. Each object can be used */
+ /* independently to convert an outline into a bitmap or pixmap. */
+ /* */
+ typedef struct QT_FT_RasterRec_* QT_FT_Raster;
+
+
+ /*************************************************************************/
+ /* */
+ /* <Struct> */
+ /* QT_FT_Span */
+ /* */
+ /* <Description> */
+ /* A structure used to model a single span of gray (or black) pixels */
+ /* when rendering a monochrome or anti-aliased bitmap. */
+ /* */
+ /* <Fields> */
+ /* x :: The span's horizontal start position. */
+ /* */
+ /* len :: The span's length in pixels. */
+ /* */
+ /* coverage :: The span color/coverage, ranging from 0 (background) */
+ /* to 255 (foreground). Only used for anti-aliased */
+ /* rendering. */
+ /* */
+ /* <Note> */
+ /* This structure is used by the span drawing callback type named */
+ /* QT_FT_SpanFunc which takes the y-coordinate of the span as a */
+ /* a parameter. */
+ /* */
+ /* The coverage value is always between 0 and 255, even if the number */
+ /* of gray levels have been set through QT_FT_Set_Gray_Levels(). */
+ /* */
+ typedef struct QT_FT_Span_
+ {
+ short x;
+ unsigned short len;
+ short y;
+ unsigned char coverage;
+ } QT_FT_Span;
+
+
+ /*************************************************************************/
+ /* */
+ /* <FuncType> */
+ /* QT_FT_SpanFunc */
+ /* */
+ /* <Description> */
+ /* A function used as a call-back by the anti-aliased renderer in */
+ /* order to let client applications draw themselves the gray pixel */
+ /* spans on each scan line. */
+ /* */
+ /* <Input> */
+ /* y :: The scanline's y-coordinate. */
+ /* */
+ /* count :: The number of spans to draw on this scanline. */
+ /* */
+ /* spans :: A table of `count' spans to draw on the scanline. */
+ /* */
+ /* user :: User-supplied data that is passed to the callback. */
+ /* */
+ /* <Note> */
+ /* This callback allows client applications to directly render the */
+ /* gray spans of the anti-aliased bitmap to any kind of surfaces. */
+ /* */
+ /* This can be used to write anti-aliased outlines directly to a */
+ /* given background bitmap, and even perform translucency. */
+ /* */
+ /* Note that the `count' field cannot be greater than a fixed value */
+ /* defined by the QT_FT_MAX_GRAY_SPANS configuration macro in */
+ /* ftoption.h. By default, this value is set to 32, which means that */
+ /* if there are more than 32 spans on a given scanline, the callback */
+ /* will be called several times with the same `y' parameter in order */
+ /* to draw all callbacks. */
+ /* */
+ /* Otherwise, the callback is only called once per scan-line, and */
+ /* only for those scanlines that do have `gray' pixels on them. */
+ /* */
+ typedef void
+ (*QT_FT_SpanFunc)(int count,
+ const QT_FT_Span* spans,
+ void* worker);
+
+#define QT_FT_Raster_Span_Func QT_FT_SpanFunc
+
+
+ /*************************************************************************/
+ /* */
+ /* <FuncType> */
+ /* QT_FT_Raster_BitTest_Func */
+ /* */
+ /* <Description> */
+ /* THIS TYPE IS DEPRECATED. DO NOT USE IT. */
+ /* */
+ /* A function used as a call-back by the monochrome scan-converter */
+ /* to test whether a given target pixel is already set to the drawing */
+ /* `color'. These tests are crucial to implement drop-out control */
+ /* per-se the TrueType spec. */
+ /* */
+ /* <Input> */
+ /* y :: The pixel's y-coordinate. */
+ /* */
+ /* x :: The pixel's x-coordinate. */
+ /* */
+ /* user :: User-supplied data that is passed to the callback. */
+ /* */
+ /* <Return> */
+ /* 1 if the pixel is `set', 0 otherwise. */
+ /* */
+ typedef int
+ (*QT_FT_Raster_BitTest_Func)( int y,
+ int x,
+ void* user );
+
+
+ /*************************************************************************/
+ /* */
+ /* <FuncType> */
+ /* QT_FT_Raster_BitSet_Func */
+ /* */
+ /* <Description> */
+ /* THIS TYPE IS DEPRECATED. DO NOT USE IT. */
+ /* */
+ /* A function used as a call-back by the monochrome scan-converter */
+ /* to set an individual target pixel. This is crucial to implement */
+ /* drop-out control according to the TrueType specification. */
+ /* */
+ /* <Input> */
+ /* y :: The pixel's y-coordinate. */
+ /* */
+ /* x :: The pixel's x-coordinate. */
+ /* */
+ /* user :: User-supplied data that is passed to the callback. */
+ /* */
+ /* <Return> */
+ /* 1 if the pixel is `set', 0 otherwise. */
+ /* */
+ typedef void
+ (*QT_FT_Raster_BitSet_Func)( int y,
+ int x,
+ void* user );
+
+
+ /*************************************************************************/
+ /* */
+ /* <Enum> */
+ /* QT_FT_RASTER_FLAG_XXX */
+ /* */
+ /* <Description> */
+ /* A list of bit flag constants as used in the `flags' field of a */
+ /* @QT_FT_Raster_Params structure. */
+ /* */
+ /* <Values> */
+ /* QT_FT_RASTER_FLAG_DEFAULT :: This value is 0. */
+ /* */
+ /* QT_FT_RASTER_FLAG_AA :: This flag is set to indicate that an */
+ /* anti-aliased glyph image should be */
+ /* generated. Otherwise, it will be */
+ /* monochrome (1-bit). */
+ /* */
+ /* QT_FT_RASTER_FLAG_DIRECT :: This flag is set to indicate direct */
+ /* rendering. In this mode, client */
+ /* applications must provide their own span */
+ /* callback. This lets them directly */
+ /* draw or compose over an existing bitmap. */
+ /* If this bit is not set, the target */
+ /* pixmap's buffer _must_ be zeroed before */
+ /* rendering. */
+ /* */
+ /* Note that for now, direct rendering is */
+ /* only possible with anti-aliased glyphs. */
+ /* */
+ /* QT_FT_RASTER_FLAG_CLIP :: This flag is only used in direct */
+ /* rendering mode. If set, the output will */
+ /* be clipped to a box specified in the */
+ /* "clip_box" field of the QT_FT_Raster_Params */
+ /* structure. */
+ /* */
+ /* Note that by default, the glyph bitmap */
+ /* is clipped to the target pixmap, except */
+ /* in direct rendering mode where all spans */
+ /* are generated if no clipping box is set. */
+ /* */
+#define QT_FT_RASTER_FLAG_DEFAULT 0x0
+#define QT_FT_RASTER_FLAG_AA 0x1
+#define QT_FT_RASTER_FLAG_DIRECT 0x2
+#define QT_FT_RASTER_FLAG_CLIP 0x4
+
+ /* deprecated */
+#define qt_ft_raster_flag_default QT_FT_RASTER_FLAG_DEFAULT
+#define qt_ft_raster_flag_aa QT_FT_RASTER_FLAG_AA
+#define qt_ft_raster_flag_direct QT_FT_RASTER_FLAG_DIRECT
+#define qt_ft_raster_flag_clip QT_FT_RASTER_FLAG_CLIP
+
+
+ /*************************************************************************/
+ /* */
+ /* <Struct> */
+ /* QT_FT_Raster_Params */
+ /* */
+ /* <Description> */
+ /* A structure to hold the arguments used by a raster's render */
+ /* function. */
+ /* */
+ /* <Fields> */
+ /* target :: The target bitmap. */
+ /* */
+ /* source :: A pointer to the source glyph image (e.g. an */
+ /* QT_FT_Outline). */
+ /* */
+ /* flags :: The rendering flags. */
+ /* */
+ /* gray_spans :: The gray span drawing callback. */
+ /* */
+ /* black_spans :: The black span drawing callback. */
+ /* */
+ /* bit_test :: The bit test callback. UNIMPLEMENTED! */
+ /* */
+ /* bit_set :: The bit set callback. UNIMPLEMENTED! */
+ /* */
+ /* user :: User-supplied data that is passed to each drawing */
+ /* callback. */
+ /* */
+ /* clip_box :: An optional clipping box. It is only used in */
+ /* direct rendering mode. Note that coordinates here */
+ /* should be expressed in _integer_ pixels (and not in */
+ /* 26.6 fixed-point units). */
+ /* */
+ /* <Note> */
+ /* An anti-aliased glyph bitmap is drawn if the QT_FT_RASTER_FLAG_AA bit */
+ /* flag is set in the `flags' field, otherwise a monochrome bitmap */
+ /* will be generated. */
+ /* */
+ /* If the QT_FT_RASTER_FLAG_DIRECT bit flag is set in `flags', the */
+ /* raster will call the `gray_spans' callback to draw gray pixel */
+ /* spans, in the case of an aa glyph bitmap, it will call */
+ /* `black_spans', and `bit_test' and `bit_set' in the case of a */
+ /* monochrome bitmap. This allows direct composition over a */
+ /* pre-existing bitmap through user-provided callbacks to perform the */
+ /* span drawing/composition. */
+ /* */
+ /* Note that the `bit_test' and `bit_set' callbacks are required when */
+ /* rendering a monochrome bitmap, as they are crucial to implement */
+ /* correct drop-out control as defined in the TrueType specification. */
+ /* */
+ typedef struct QT_FT_Raster_Params_
+ {
+ QT_FT_Bitmap* target;
+ void* source;
+ int flags;
+ QT_FT_SpanFunc gray_spans;
+ QT_FT_SpanFunc black_spans;
+ QT_FT_Raster_BitTest_Func bit_test; /* doesn't work! */
+ QT_FT_Raster_BitSet_Func bit_set; /* doesn't work! */
+ void* user;
+ QT_FT_BBox clip_box;
+
+ } QT_FT_Raster_Params;
+
+
+ /*************************************************************************/
+ /* */
+ /* <FuncType> */
+ /* QT_FT_Raster_NewFunc */
+ /* */
+ /* <Description> */
+ /* A function used to create a new raster object. */
+ /* */
+ /* <Input> */
+ /* memory :: A handle to the memory allocator. */
+ /* */
+ /* <Output> */
+ /* raster :: A handle to the new raster object. */
+ /* */
+ /* <Return> */
+ /* Error code. 0 means success. */
+ /* */
+ /* <Note> */
+ /* The `memory' parameter is a typeless pointer in order to avoid */
+ /* un-wanted dependencies on the rest of the FreeType code. In */
+ /* practice, it is a QT_FT_Memory, i.e., a handle to the standard */
+ /* FreeType memory allocator. However, this field can be completely */
+ /* ignored by a given raster implementation. */
+ /* */
+ typedef int
+ (*QT_FT_Raster_NewFunc)( void* memory,
+ QT_FT_Raster* raster );
+
+#define QT_FT_Raster_New_Func QT_FT_Raster_NewFunc
+
+ /*************************************************************************/
+ /* */
+ /* <FuncType> */
+ /* QT_FT_Raster_DoneFunc */
+ /* */
+ /* <Description> */
+ /* A function used to destroy a given raster object. */
+ /* */
+ /* <Input> */
+ /* raster :: A handle to the raster object. */
+ /* */
+ typedef void
+ (*QT_FT_Raster_DoneFunc)( QT_FT_Raster raster );
+
+#define QT_FT_Raster_Done_Func QT_FT_Raster_DoneFunc
+
+ /*************************************************************************/
+ /* */
+ /* <FuncType> */
+ /* QT_FT_Raster_ResetFunc */
+ /* */
+ /* <Description> */
+ /* FreeType provides an area of memory called the `render pool', */
+ /* available to all registered rasters. This pool can be freely used */
+ /* during a given scan-conversion but is shared by all rasters. Its */
+ /* content is thus transient. */
+ /* */
+ /* This function is called each time the render pool changes, or just */
+ /* after a new raster object is created. */
+ /* */
+ /* <Input> */
+ /* raster :: A handle to the new raster object. */
+ /* */
+ /* pool_base :: The address in memory of the render pool. */
+ /* */
+ /* pool_size :: The size in bytes of the render pool. */
+ /* */
+ /* <Note> */
+ /* Rasters can ignore the render pool and rely on dynamic memory */
+ /* allocation if they want to (a handle to the memory allocator is */
+ /* passed to the raster constructor). However, this is not */
+ /* recommended for efficiency purposes. */
+ /* */
+ typedef void
+ (*QT_FT_Raster_ResetFunc)( QT_FT_Raster raster,
+ unsigned char* pool_base,
+ unsigned long pool_size );
+
+#define QT_FT_Raster_Reset_Func QT_FT_Raster_ResetFunc
+
+ /*************************************************************************/
+ /* */
+ /* <FuncType> */
+ /* QT_FT_Raster_SetModeFunc */
+ /* */
+ /* <Description> */
+ /* This function is a generic facility to change modes or attributes */
+ /* in a given raster. This can be used for debugging purposes, or */
+ /* simply to allow implementation-specific `features' in a given */
+ /* raster module. */
+ /* */
+ /* <Input> */
+ /* raster :: A handle to the new raster object. */
+ /* */
+ /* mode :: A 4-byte tag used to name the mode or property. */
+ /* */
+ /* args :: A pointer to the new mode/property to use. */
+ /* */
+ typedef int
+ (*QT_FT_Raster_SetModeFunc)( QT_FT_Raster raster,
+ unsigned long mode,
+ void* args );
+
+#define QT_FT_Raster_Set_Mode_Func QT_FT_Raster_SetModeFunc
+
+ /*************************************************************************/
+ /* */
+ /* <FuncType> */
+ /* QT_FT_Raster_RenderFunc */
+ /* */
+ /* <Description> */
+ /* Invokes a given raster to scan-convert a given glyph image into a */
+ /* target bitmap. */
+ /* */
+ /* <Input> */
+ /* raster :: A handle to the raster object. */
+ /* */
+ /* params :: A pointer to a QT_FT_Raster_Params structure used to store */
+ /* the rendering parameters. */
+ /* */
+ /* <Return> */
+ /* Error code. 0 means success. */
+ /* */
+ /* <Note> */
+ /* The exact format of the source image depends on the raster's glyph */
+ /* format defined in its QT_FT_Raster_Funcs structure. It can be an */
+ /* QT_FT_Outline or anything else in order to support a large array of */
+ /* glyph formats. */
+ /* */
+ /* Note also that the render function can fail and return a */
+ /* QT_FT_Err_Unimplemented_Feature error code if the raster used does */
+ /* not support direct composition. */
+ /* */
+ /* XXX: For now, the standard raster doesn't support direct */
+ /* composition but this should change for the final release (see */
+ /* the files demos/src/ftgrays.c and demos/src/ftgrays2.c for */
+ /* examples of distinct implementations which support direct */
+ /* composition). */
+ /* */
+ typedef int
+ (*QT_FT_Raster_RenderFunc)( QT_FT_Raster raster,
+ QT_FT_Raster_Params* params );
+
+#define QT_FT_Raster_Render_Func QT_FT_Raster_RenderFunc
+
+ /*************************************************************************/
+ /* */
+ /* <Struct> */
+ /* QT_FT_Raster_Funcs */
+ /* */
+ /* <Description> */
+ /* A structure used to describe a given raster class to the library. */
+ /* */
+ /* <Fields> */
+ /* glyph_format :: The supported glyph format for this raster. */
+ /* */
+ /* raster_new :: The raster constructor. */
+ /* */
+ /* raster_reset :: Used to reset the render pool within the raster. */
+ /* */
+ /* raster_render :: A function to render a glyph into a given bitmap. */
+ /* */
+ /* raster_done :: The raster destructor. */
+ /* */
+ typedef struct QT_FT_Raster_Funcs_
+ {
+ QT_FT_Glyph_Format glyph_format;
+ QT_FT_Raster_NewFunc raster_new;
+ QT_FT_Raster_ResetFunc raster_reset;
+ QT_FT_Raster_SetModeFunc raster_set_mode;
+ QT_FT_Raster_RenderFunc raster_render;
+ QT_FT_Raster_DoneFunc raster_done;
+
+ } QT_FT_Raster_Funcs;
+
+
+ /* */
+
+
+QT_FT_END_HEADER
+
+#endif /* __FTIMAGE_H__ */
+
+
+/* END */
diff --git a/src/gui/painting/qrasterizer.cpp b/src/gui/painting/qrasterizer.cpp
new file mode 100644
index 0000000000..583885c3bb
--- /dev/null
+++ b/src/gui/painting/qrasterizer.cpp
@@ -0,0 +1,1249 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qrasterizer_p.h"
+
+#include <QPoint>
+#include <QRect>
+
+#include <private/qmath_p.h>
+#include <private/qdatabuffer_p.h>
+#include <private/qdrawhelper_p.h>
+
+QT_BEGIN_NAMESPACE
+
+typedef int Q16Dot16;
+#define Q16Dot16ToFloat(i) ((i)/65536.)
+#define FloatToQ16Dot16(i) (int)((i) * 65536.)
+#define IntToQ16Dot16(i) ((i) << 16)
+#define Q16Dot16ToInt(i) ((i) >> 16)
+#define Q16Dot16Factor 65536
+
+#define Q16Dot16Multiply(x, y) (int)((qlonglong(x) * qlonglong(y)) >> 16)
+#define Q16Dot16FastMultiply(x, y) (((x) * (y)) >> 16)
+
+#define SPAN_BUFFER_SIZE 256
+
+#define COORD_ROUNDING 1 // 0: round up, 1: round down
+#define COORD_OFFSET 32 // 26.6, 32 is half a pixel
+
+class QSpanBuffer {
+public:
+ QSpanBuffer(ProcessSpans blend, void *data, const QRect &clipRect)
+ : m_spanCount(0)
+ , m_blend(blend)
+ , m_data(data)
+ , m_clipRect(clipRect)
+ {
+ }
+
+ ~QSpanBuffer()
+ {
+ flushSpans();
+ }
+
+ void addSpan(int x, unsigned int len, int y, unsigned char coverage)
+ {
+ if (!coverage || !len)
+ return;
+
+ Q_ASSERT(y >= m_clipRect.top());
+ Q_ASSERT(y <= m_clipRect.bottom());
+ Q_ASSERT(x >= m_clipRect.left());
+ Q_ASSERT(x + int(len) - 1 <= m_clipRect.right());
+
+ m_spans[m_spanCount].x = x;
+ m_spans[m_spanCount].len = len;
+ m_spans[m_spanCount].y = y;
+ m_spans[m_spanCount].coverage = coverage;
+
+ if (++m_spanCount == SPAN_BUFFER_SIZE)
+ flushSpans();
+ }
+
+private:
+ void flushSpans()
+ {
+ m_blend(m_spanCount, m_spans, m_data);
+ m_spanCount = 0;
+ }
+
+ QT_FT_Span m_spans[SPAN_BUFFER_SIZE];
+ int m_spanCount;
+
+ ProcessSpans m_blend;
+ void *m_data;
+
+ QRect m_clipRect;
+};
+
+#define CHUNK_SIZE 64
+class QScanConverter
+{
+public:
+ QScanConverter();
+ ~QScanConverter();
+
+ void begin(int top, int bottom, int left, int right,
+ Qt::FillRule fillRule, QSpanBuffer *spanBuffer);
+ void end();
+
+ void mergeCurve(const QT_FT_Vector &a, const QT_FT_Vector &b,
+ const QT_FT_Vector &c, const QT_FT_Vector &d);
+ void mergeLine(QT_FT_Vector a, QT_FT_Vector b);
+
+ struct Line
+ {
+ Q16Dot16 x;
+ Q16Dot16 delta;
+
+ int top, bottom;
+
+ int winding;
+ };
+
+private:
+ struct Intersection
+ {
+ int x;
+ int winding;
+
+ int left, right;
+ };
+
+ inline bool clip(Q16Dot16 &xFP, int &iTop, int &iBottom, Q16Dot16 slopeFP, Q16Dot16 edgeFP, int winding);
+ inline void mergeIntersection(Intersection *head, const Intersection &isect);
+
+ void prepareChunk();
+
+ void emitNode(const Intersection *node);
+ void emitSpans(int chunk);
+
+ inline void allocate(int size);
+
+ QDataBuffer<Line> m_lines;
+
+ int m_alloc;
+ int m_size;
+
+ int m_top;
+ int m_bottom;
+
+ Q16Dot16 m_leftFP;
+ Q16Dot16 m_rightFP;
+
+ int m_fillRuleMask;
+
+ int m_x;
+ int m_y;
+ int m_winding;
+
+ Intersection *m_intersections;
+
+ QSpanBuffer *m_spanBuffer;
+
+ QDataBuffer<Line *> m_active;
+
+ template <typename T>
+ friend void qScanConvert(QScanConverter &d, T allVertical);
+};
+
+class QRasterizerPrivate
+{
+public:
+ bool antialiased;
+ ProcessSpans blend;
+ void *data;
+ QRect clipRect;
+
+ QScanConverter scanConverter;
+};
+
+QScanConverter::QScanConverter()
+ : m_alloc(0)
+ , m_size(0)
+ , m_intersections(0)
+{
+}
+
+QScanConverter::~QScanConverter()
+{
+ if (m_intersections)
+ free(m_intersections);
+}
+
+void QScanConverter::begin(int top, int bottom, int left, int right,
+ Qt::FillRule fillRule, QSpanBuffer *spanBuffer)
+{
+ m_top = top;
+ m_bottom = bottom;
+ m_leftFP = IntToQ16Dot16(left);
+ m_rightFP = IntToQ16Dot16(right + 1);
+
+ m_lines.reset();
+
+ m_fillRuleMask = fillRule == Qt::WindingFill ? ~0x0 : 0x1;
+ m_spanBuffer = spanBuffer;
+}
+
+void QScanConverter::prepareChunk()
+{
+ m_size = CHUNK_SIZE;
+
+ allocate(CHUNK_SIZE);
+ memset(m_intersections, 0, CHUNK_SIZE * sizeof(Intersection));
+}
+
+void QScanConverter::emitNode(const Intersection *node)
+{
+tail_call:
+ if (node->left)
+ emitNode(node + node->left);
+
+ if (m_winding & m_fillRuleMask)
+ m_spanBuffer->addSpan(m_x, node->x - m_x, m_y, 0xff);
+
+ m_x = node->x;
+ m_winding += node->winding;
+
+ if (node->right) {
+ node += node->right;
+ goto tail_call;
+ }
+}
+
+void QScanConverter::emitSpans(int chunk)
+{
+ for (int dy = 0; dy < CHUNK_SIZE; ++dy) {
+ m_x = 0;
+ m_y = chunk + dy;
+ m_winding = 0;
+
+ emitNode(&m_intersections[dy]);
+ }
+}
+
+// split control points b[0] ... b[3] into
+// left (b[0] ... b[3]) and right (b[3] ... b[6])
+static void split(QT_FT_Vector *b)
+{
+ b[6] = b[3];
+
+ {
+ const QT_FT_Pos temp = (b[1].x + b[2].x)/2;
+
+ b[1].x = (b[0].x + b[1].x)/2;
+ b[5].x = (b[2].x + b[3].x)/2;
+ b[2].x = (b[1].x + temp)/2;
+ b[4].x = (b[5].x + temp)/2;
+ b[3].x = (b[2].x + b[4].x)/2;
+ }
+ {
+ const QT_FT_Pos temp = (b[1].y + b[2].y)/2;
+
+ b[1].y = (b[0].y + b[1].y)/2;
+ b[5].y = (b[2].y + b[3].y)/2;
+ b[2].y = (b[1].y + temp)/2;
+ b[4].y = (b[5].y + temp)/2;
+ b[3].y = (b[2].y + b[4].y)/2;
+ }
+}
+
+static inline bool topOrder(const QScanConverter::Line &a, const QScanConverter::Line &b)
+{
+ return a.top < b.top;
+}
+
+static inline bool xOrder(const QScanConverter::Line *a, const QScanConverter::Line *b)
+{
+ return a->x < b->x;
+}
+
+template <bool B>
+struct QBoolToType
+{
+ inline bool operator()() const
+ {
+ return B;
+ }
+};
+
+// should be a member function but VC6 doesn't support member template functions
+template <typename T>
+void qScanConvert(QScanConverter &d, T allVertical)
+{
+ qSort(d.m_lines.data(), d.m_lines.data() + d.m_lines.size(), topOrder);
+ int line = 0;
+ for (int y = d.m_lines.first().top; y <= d.m_bottom; ++y) {
+ for (; line < d.m_lines.size() && d.m_lines.at(line).top == y; ++line) {
+ // add node to active list
+ if (allVertical()) {
+ QScanConverter::Line *l = &d.m_lines.at(line);
+ d.m_active.resize(d.m_active.size() + 1);
+ int j;
+ for (j = d.m_active.size() - 2; j >= 0 && xOrder(l, d.m_active.at(j)); --j)
+ d.m_active.at(j+1) = d.m_active.at(j);
+ d.m_active.at(j+1) = l;
+ } else {
+ d.m_active << &d.m_lines.at(line);
+ }
+ }
+
+ int numActive = d.m_active.size();
+ if (!allVertical()) {
+ // use insertion sort instead of qSort, as the active edge list is quite small
+ // and in the average case already sorted
+ for (int i = 1; i < numActive; ++i) {
+ QScanConverter::Line *l = d.m_active.at(i);
+ int j;
+ for (j = i-1; j >= 0 && xOrder(l, d.m_active.at(j)); --j)
+ d.m_active.at(j+1) = d.m_active.at(j);
+ d.m_active.at(j+1) = l;
+ }
+ }
+
+ int x = 0;
+ int winding = 0;
+ for (int i = 0; i < numActive; ++i) {
+ QScanConverter::Line *node = d.m_active.at(i);
+
+ const int current = Q16Dot16ToInt(node->x);
+ if (winding & d.m_fillRuleMask)
+ d.m_spanBuffer->addSpan(x, current - x, y, 0xff);
+
+ x = current;
+ winding += node->winding;
+
+ if (node->bottom == y) {
+ // remove node from active list
+ for (int j = i; j < numActive - 1; ++j)
+ d.m_active.at(j) = d.m_active.at(j+1);
+
+ d.m_active.resize(--numActive);
+ --i;
+ } else if (!allVertical())
+ node->x += node->delta;
+ }
+ }
+ d.m_active.reset();
+}
+
+void QScanConverter::end()
+{
+ if (m_lines.isEmpty())
+ return;
+
+ if (m_lines.size() <= 32) {
+ bool allVertical = true;
+ for (int i = 0; i < m_lines.size(); ++i) {
+ if (m_lines.at(i).delta) {
+ allVertical = false;
+ break;
+ }
+ }
+ if (allVertical)
+ qScanConvert(*this, QBoolToType<true>());
+ else
+ qScanConvert(*this, QBoolToType<false>());
+ } else {
+ for (int chunkTop = m_top; chunkTop <= m_bottom; chunkTop += CHUNK_SIZE) {
+ prepareChunk();
+
+ Intersection isect = { 0, 0, 0, 0 };
+
+ const int chunkBottom = chunkTop + CHUNK_SIZE;
+ for (int i = 0; i < m_lines.size(); ++i) {
+ Line &line = m_lines.at(i);
+
+ if ((line.bottom < chunkTop) || (line.top > chunkBottom))
+ continue;
+
+ const int top = qMax(0, line.top - chunkTop);
+ const int bottom = qMin(CHUNK_SIZE, line.bottom + 1 - chunkTop);
+ allocate(m_size + bottom - top);
+
+ isect.winding = line.winding;
+
+ Intersection *it = m_intersections + top;
+ Intersection *end = m_intersections + bottom;
+
+ if (line.delta) {
+ for (; it != end; ++it) {
+ isect.x = Q16Dot16ToInt(line.x);
+ line.x += line.delta;
+ mergeIntersection(it, isect);
+ }
+ } else {
+ isect.x = Q16Dot16ToInt(line.x);
+ for (; it != end; ++it)
+ mergeIntersection(it, isect);
+ }
+ }
+
+ emitSpans(chunkTop);
+ }
+ }
+
+ if (m_alloc > 1024) {
+ free(m_intersections);
+ m_alloc = 0;
+ m_size = 0;
+ m_intersections = 0;
+ }
+
+ if (m_lines.size() > 1024)
+ m_lines.shrink(1024);
+}
+
+inline void QScanConverter::allocate(int size)
+{
+ if (m_alloc < size) {
+ m_alloc = qMax(size, 2 * m_alloc);
+ m_intersections = (Intersection *)realloc(m_intersections, m_alloc * sizeof(Intersection));
+ }
+}
+
+inline void QScanConverter::mergeIntersection(Intersection *it, const Intersection &isect)
+{
+ Intersection *current = it;
+
+ while (isect.x != current->x) {
+ int &next = isect.x < current->x ? current->left : current->right;
+ if (next)
+ current += next;
+ else {
+ Intersection *last = m_intersections + m_size;
+ next = last - current;
+ *last = isect;
+ ++m_size;
+ return;
+ }
+ }
+
+ current->winding += isect.winding;
+}
+
+void QScanConverter::mergeCurve(const QT_FT_Vector &pa, const QT_FT_Vector &pb,
+ const QT_FT_Vector &pc, const QT_FT_Vector &pd)
+{
+ // make room for 32 splits
+ QT_FT_Vector beziers[4 + 3 * 32];
+
+ QT_FT_Vector *b = beziers;
+
+ b[0] = pa;
+ b[1] = pb;
+ b[2] = pc;
+ b[3] = pd;
+
+ const QT_FT_Pos flatness = 16;
+
+ while (b >= beziers) {
+ QT_FT_Vector delta = { b[3].x - b[0].x, b[3].y - b[0].y };
+ QT_FT_Pos l = qAbs(delta.x) + qAbs(delta.y);
+
+ bool belowThreshold;
+ if (l > 64) {
+ qlonglong d2 = qAbs(qlonglong(b[1].x-b[0].x) * qlonglong(delta.y) -
+ qlonglong(b[1].y-b[0].y) * qlonglong(delta.x));
+ qlonglong d3 = qAbs(qlonglong(b[2].x-b[0].x) * qlonglong(delta.y) -
+ qlonglong(b[2].y-b[0].y) * qlonglong(delta.x));
+
+ qlonglong d = d2 + d3;
+
+ belowThreshold = (d <= qlonglong(flatness) * qlonglong(l));
+ } else {
+ QT_FT_Pos d = qAbs(b[0].x-b[1].x) + qAbs(b[0].y-b[1].y) +
+ qAbs(b[0].x-b[2].x) + qAbs(b[0].y-b[2].y);
+
+ belowThreshold = (d <= flatness);
+ }
+
+ if (belowThreshold || b == beziers + 3 * 32) {
+ mergeLine(b[0], b[3]);
+ b -= 3;
+ continue;
+ }
+
+ split(b);
+ b += 3;
+ }
+}
+
+inline bool QScanConverter::clip(Q16Dot16 &xFP, int &iTop, int &iBottom, Q16Dot16 slopeFP, Q16Dot16 edgeFP, int winding)
+{
+ bool right = edgeFP == m_rightFP;
+
+ if (xFP == edgeFP) {
+ if ((slopeFP > 0) ^ right)
+ return false;
+ else {
+ Line line = { edgeFP, 0, iTop, iBottom, winding };
+ m_lines.add(line);
+ return true;
+ }
+ }
+
+ Q16Dot16 lastFP = xFP + slopeFP * (iBottom - iTop);
+
+ if (lastFP == edgeFP) {
+ if ((slopeFP < 0) ^ right)
+ return false;
+ else {
+ Line line = { edgeFP, 0, iTop, iBottom, winding };
+ m_lines.add(line);
+ return true;
+ }
+ }
+
+ // does line cross edge?
+ if ((lastFP < edgeFP) ^ (xFP < edgeFP)) {
+ Q16Dot16 deltaY = Q16Dot16((edgeFP - xFP) / Q16Dot16ToFloat(slopeFP));
+
+ if ((xFP < edgeFP) ^ right) {
+ // top segment needs to be clipped
+ int iHeight = Q16Dot16ToInt(deltaY + 1);
+ int iMiddle = iTop + iHeight;
+
+ Line line = { edgeFP, 0, iTop, iMiddle, winding };
+ m_lines.add(line);
+
+ if (iMiddle != iBottom) {
+ xFP += slopeFP * (iHeight + 1);
+ iTop = iMiddle + 1;
+ } else
+ return true;
+ } else {
+ // bottom segment needs to be clipped
+ int iHeight = Q16Dot16ToInt(deltaY);
+ int iMiddle = iTop + iHeight;
+
+ if (iMiddle != iBottom) {
+ Line line = { edgeFP, 0, iMiddle + 1, iBottom, winding };
+ m_lines.add(line);
+
+ iBottom = iMiddle;
+ }
+ }
+ return false;
+ } else if ((xFP < edgeFP) ^ right) {
+ Line line = { edgeFP, 0, iTop, iBottom, winding };
+ m_lines.add(line);
+ return true;
+ }
+
+ return false;
+}
+
+void QScanConverter::mergeLine(QT_FT_Vector a, QT_FT_Vector b)
+{
+ int winding = 1;
+
+ if (a.y > b.y) {
+ qSwap(a, b);
+ winding = -1;
+ }
+
+ a.x += COORD_OFFSET;
+ a.y += COORD_OFFSET;
+ b.x += COORD_OFFSET;
+ b.y += COORD_OFFSET;
+
+ int iTop = qMax(m_top, int((a.y + 32 - COORD_ROUNDING) >> 6));
+ int iBottom = qMin(m_bottom, int((b.y - 32 - COORD_ROUNDING) >> 6));
+
+ if (iTop <= iBottom) {
+ Q16Dot16 aFP = Q16Dot16Factor/2 + (a.x << 10) - COORD_ROUNDING;
+
+ if (b.x == a.x) {
+ Line line = { qBound(m_leftFP, aFP, m_rightFP), 0, iTop, iBottom, winding };
+ m_lines.add(line);
+ } else {
+ const qreal slope = (b.x - a.x) / qreal(b.y - a.y);
+
+ const Q16Dot16 slopeFP = FloatToQ16Dot16(slope);
+
+ Q16Dot16 xFP = aFP + Q16Dot16Multiply(slopeFP,
+ IntToQ16Dot16(iTop)
+ + Q16Dot16Factor/2 - (a.y << 10));
+
+ if (clip(xFP, iTop, iBottom, slopeFP, m_leftFP, winding))
+ return;
+
+ if (clip(xFP, iTop, iBottom, slopeFP, m_rightFP, winding))
+ return;
+
+ Q_ASSERT(xFP >= m_leftFP);
+
+ Line line = { xFP, slopeFP, iTop, iBottom, winding };
+ m_lines.add(line);
+ }
+ }
+}
+
+QRasterizer::QRasterizer()
+ : d(new QRasterizerPrivate)
+{
+}
+
+QRasterizer::~QRasterizer()
+{
+ delete d;
+}
+
+void QRasterizer::setAntialiased(bool antialiased)
+{
+ d->antialiased = antialiased;
+}
+
+void QRasterizer::initialize(ProcessSpans blend, void *data)
+{
+ d->blend = blend;
+ d->data = data;
+}
+
+void QRasterizer::setClipRect(const QRect &clipRect)
+{
+ d->clipRect = clipRect;
+}
+
+static Q16Dot16 intersectPixelFP(int x, Q16Dot16 top, Q16Dot16 bottom, Q16Dot16 leftIntersectX, Q16Dot16 rightIntersectX, Q16Dot16 slope, Q16Dot16 invSlope)
+{
+ Q16Dot16 leftX = IntToQ16Dot16(x);
+ Q16Dot16 rightX = IntToQ16Dot16(x) + Q16Dot16Factor;
+
+ Q16Dot16 leftIntersectY, rightIntersectY;
+ if (slope > 0) {
+ leftIntersectY = top + Q16Dot16Multiply(leftX - leftIntersectX, invSlope);
+ rightIntersectY = leftIntersectY + invSlope;
+ } else {
+ leftIntersectY = top + Q16Dot16Multiply(leftX - rightIntersectX, invSlope);
+ rightIntersectY = leftIntersectY + invSlope;
+ }
+
+ if (leftIntersectX >= leftX && rightIntersectX <= rightX) {
+ return Q16Dot16Multiply(bottom - top, leftIntersectX - leftX + ((rightIntersectX - leftIntersectX) >> 1));
+ } else if (leftIntersectX >= rightX) {
+ return bottom - top;
+ } else if (leftIntersectX >= leftX) {
+ if (slope > 0) {
+ return (bottom - top) - Q16Dot16FastMultiply((rightX - leftIntersectX) >> 1, rightIntersectY - top);
+ } else {
+ return (bottom - top) - Q16Dot16FastMultiply((rightX - leftIntersectX) >> 1, bottom - rightIntersectY);
+ }
+ } else if (rightIntersectX <= leftX) {
+ return 0;
+ } else if (rightIntersectX <= rightX) {
+ if (slope > 0) {
+ return Q16Dot16FastMultiply((rightIntersectX - leftX) >> 1, bottom - leftIntersectY);
+ } else {
+ return Q16Dot16FastMultiply((rightIntersectX - leftX) >> 1, leftIntersectY - top);
+ }
+ } else {
+ if (slope > 0) {
+ return (bottom - rightIntersectY) + ((rightIntersectY - leftIntersectY) >> 1);
+ } else {
+ return (rightIntersectY - top) + ((leftIntersectY - rightIntersectY) >> 1);
+ }
+ }
+}
+
+static inline bool q16Dot16Compare(qreal p1, qreal p2)
+{
+ return FloatToQ16Dot16(p2 - p1) == 0;
+}
+
+static inline qreal qRoundF(qreal v)
+{
+#ifdef QT_USE_MATH_H_FLOATS
+ if (sizeof(qreal) == sizeof(float))
+ return floorf(v + 0.5);
+ else
+#endif
+ return floor(v + 0.5);
+}
+
+void QRasterizer::rasterizeLine(const QPointF &a, const QPointF &b, qreal width, bool squareCap)
+{
+ if (a == b || width == 0)
+ return;
+
+ QPointF pa = a;
+ QPointF pb = b;
+
+ QPointF offs = QPointF(qAbs(b.y() - a.y()), qAbs(b.x() - a.x())) * width * 0.5;
+ if (squareCap)
+ offs += QPointF(offs.y(), offs.x());
+ const QRectF clip(d->clipRect.topLeft() - offs, d->clipRect.bottomRight() + QPoint(1, 1) + offs);
+
+ if (!clip.contains(a) || !clip.contains(b)) {
+ qreal t1 = 0;
+ qreal t2 = 1;
+
+ const qreal o[2] = { a.x(), a.y() };
+ const qreal d[2] = { b.x() - a.x(), b.y() - a.y() };
+
+ const qreal low[2] = { clip.left(), clip.top() };
+ const qreal high[2] = { clip.right(), clip.bottom() };
+
+ for (int i = 0; i < 2; ++i) {
+ if (d[i] == 0) {
+ if (o[i] <= low[i] || o[i] >= high[i])
+ return;
+ continue;
+ }
+ const qreal d_inv = 1 / d[i];
+ qreal t_low = (low[i] - o[i]) * d_inv;
+ qreal t_high = (high[i] - o[i]) * d_inv;
+ if (t_low > t_high)
+ qSwap(t_low, t_high);
+ if (t1 < t_low)
+ t1 = t_low;
+ if (t2 > t_high)
+ t2 = t_high;
+ if (t1 >= t2)
+ return;
+ }
+ pa = a + (b - a) * t1;
+ pb = a + (b - a) * t2;
+ }
+
+ if (!d->antialiased) {
+ pa.rx() += (COORD_OFFSET - COORD_ROUNDING)/64.;
+ pa.ry() += (COORD_OFFSET - COORD_ROUNDING)/64.;
+ pb.rx() += (COORD_OFFSET - COORD_ROUNDING)/64.;
+ pb.ry() += (COORD_OFFSET - COORD_ROUNDING)/64.;
+ }
+
+ {
+ const qreal gridResolution = 64;
+ const qreal reciprocal = 1 / gridResolution;
+
+ // snap to grid to prevent large slopes
+ pa.rx() = qRoundF(pa.rx() * gridResolution) * reciprocal;
+ pa.ry() = qRoundF(pa.ry() * gridResolution) * reciprocal;
+ pb.rx() = qRoundF(pb.rx() * gridResolution) * reciprocal;
+ pb.ry() = qRoundF(pb.ry() * gridResolution) * reciprocal;
+
+ // old delta
+ const QPointF d0 = a - b;
+ const qreal w0 = d0.x() * d0.x() + d0.y() * d0.y();
+
+ // new delta
+ const QPointF d = pa - pb;
+ const qreal w = d.x() * d.x() + d.y() * d.y();
+
+ if (w == 0)
+ return;
+
+ // adjust width which is given relative to |b - a|
+ width *= sqrt(w0 / w);
+ }
+
+ QSpanBuffer buffer(d->blend, d->data, d->clipRect);
+
+ if (q16Dot16Compare(pa.y(), pb.y())) {
+ const qreal x = (pa.x() + pb.x()) * 0.5f;
+ const qreal dx = qAbs(pb.x() - pa.x()) * 0.5f;
+
+ const qreal y = pa.y();
+ const qreal dy = width * dx;
+
+ pa = QPointF(x, y - dy);
+ pb = QPointF(x, y + dy);
+
+ if (squareCap)
+ width = 1 / width + 1.0f;
+ else
+ width = 1 / width;
+
+ squareCap = false;
+ }
+
+ if (q16Dot16Compare(pa.x(), pb.x())) {
+ if (pa.y() > pb.y())
+ qSwap(pa, pb);
+
+ const qreal dy = pb.y() - pa.y();
+ const qreal halfWidth = 0.5f * width * dy;
+
+ if (squareCap) {
+ pa.ry() -= halfWidth;
+ pb.ry() += halfWidth;
+ }
+
+ qreal left = pa.x() - halfWidth;
+ qreal right = pa.x() + halfWidth;
+
+ left = qBound(qreal(d->clipRect.left()), left, qreal(d->clipRect.right() + 1));
+ right = qBound(qreal(d->clipRect.left()), right, qreal(d->clipRect.right() + 1));
+
+ pa.ry() = qBound(qreal(d->clipRect.top()), pa.y(), qreal(d->clipRect.bottom() + 1));
+ pb.ry() = qBound(qreal(d->clipRect.top()), pb.y(), qreal(d->clipRect.bottom() + 1));
+
+ if (q16Dot16Compare(left, right) || q16Dot16Compare(pa.y(), pb.y()))
+ return;
+
+ if (d->antialiased) {
+ const Q16Dot16 iLeft = int(left);
+ const Q16Dot16 iRight = int(right);
+ const Q16Dot16 leftWidth = IntToQ16Dot16(iLeft + 1)
+ - FloatToQ16Dot16(left);
+ const Q16Dot16 rightWidth = FloatToQ16Dot16(right)
+ - IntToQ16Dot16(iRight);
+
+ Q16Dot16 coverage[3];
+ int x[3];
+ int len[3];
+
+ int n = 1;
+ if (iLeft == iRight) {
+ coverage[0] = (leftWidth + rightWidth) * 255;
+ x[0] = iLeft;
+ len[0] = 1;
+ } else {
+ coverage[0] = leftWidth * 255;
+ x[0] = iLeft;
+ len[0] = 1;
+ if (leftWidth == Q16Dot16Factor) {
+ len[0] = iRight - iLeft;
+ } else if (iRight - iLeft > 1) {
+ coverage[1] = IntToQ16Dot16(255);
+ x[1] = iLeft + 1;
+ len[1] = iRight - iLeft - 1;
+ ++n;
+ }
+ if (rightWidth) {
+ coverage[n] = rightWidth * 255;
+ x[n] = iRight;
+ len[n] = 1;
+ ++n;
+ }
+ }
+
+ const Q16Dot16 iTopFP = IntToQ16Dot16(int(pa.y()));
+ const Q16Dot16 iBottomFP = IntToQ16Dot16(int(pb.y()));
+ const Q16Dot16 yPa = FloatToQ16Dot16(pa.y());
+ const Q16Dot16 yPb = FloatToQ16Dot16(pb.y());
+ for (Q16Dot16 yFP = iTopFP; yFP <= iBottomFP; yFP += Q16Dot16Factor) {
+ const Q16Dot16 rowHeight = qMin(yFP + Q16Dot16Factor, yPb)
+ - qMax(yFP, yPa);
+ const int y = Q16Dot16ToInt(yFP);
+ for (int i = 0; i < n; ++i) {
+ buffer.addSpan(x[i], len[i], y,
+ Q16Dot16ToInt(Q16Dot16Multiply(rowHeight, coverage[i])));
+ }
+ }
+ } else { // aliased
+ int iTop = int(pa.y() + 0.5f);
+ int iBottom = pb.y() < 0.5f ? -1 : int(pb.y() - 0.5f);
+ int iLeft = int(left + 0.5f);
+ int iRight = right < 0.5f ? -1 : int(right - 0.5f);
+
+ int iWidth = iRight - iLeft + 1;
+ for (int y = iTop; y <= iBottom; ++y)
+ buffer.addSpan(iLeft, iWidth, y, 255);
+ }
+ } else {
+ if (pa.y() > pb.y())
+ qSwap(pa, pb);
+
+ QPointF delta = pb - pa;
+ delta *= 0.5f * width;
+ const QPointF perp(delta.y(), -delta.x());
+
+ if (squareCap) {
+ pa -= delta;
+ pb += delta;
+ }
+
+ QPointF top;
+ QPointF left;
+ QPointF right;
+ QPointF bottom;
+
+ if (pa.x() < pb.x()) {
+ top = pa + perp;
+ left = pa - perp;
+ right = pb + perp;
+ bottom = pb - perp;
+ } else {
+ top = pa - perp;
+ left = pb - perp;
+ right = pa + perp;
+ bottom = pb + perp;
+ }
+
+ const qreal topBound = qBound(qreal(d->clipRect.top()), top.y(), qreal(d->clipRect.bottom()));
+ const qreal bottomBound = qBound(qreal(d->clipRect.top()), bottom.y(), qreal(d->clipRect.bottom()));
+
+ const qreal leftSlope = (left.x() - top.x()) / (left.y() - top.y());
+ const qreal rightSlope = -1.0f / leftSlope;
+
+ const Q16Dot16 leftSlopeFP = FloatToQ16Dot16(leftSlope);
+ const Q16Dot16 rightSlopeFP = FloatToQ16Dot16(rightSlope);
+
+ if (d->antialiased) {
+ const Q16Dot16 iTopFP = IntToQ16Dot16(int(topBound));
+ const Q16Dot16 iLeftFP = IntToQ16Dot16(int(left.y()));
+ const Q16Dot16 iRightFP = IntToQ16Dot16(int(right.y()));
+ const Q16Dot16 iBottomFP = IntToQ16Dot16(int(bottomBound));
+
+ Q16Dot16 leftIntersectAf = FloatToQ16Dot16(top.x() + (int(topBound) - top.y()) * leftSlope);
+ Q16Dot16 rightIntersectAf = FloatToQ16Dot16(top.x() + (int(topBound) - top.y()) * rightSlope);
+ Q16Dot16 leftIntersectBf = 0;
+ Q16Dot16 rightIntersectBf = 0;
+
+ if (iLeftFP < iTopFP)
+ leftIntersectBf = FloatToQ16Dot16(left.x() + (int(topBound) - left.y()) * rightSlope);
+
+ if (iRightFP < iTopFP)
+ rightIntersectBf = FloatToQ16Dot16(right.x() + (int(topBound) - right.y()) * leftSlope);
+
+ Q16Dot16 rowTop, rowBottomLeft, rowBottomRight, rowTopLeft, rowTopRight, rowBottom;
+ Q16Dot16 topLeftIntersectAf, topLeftIntersectBf, topRightIntersectAf, topRightIntersectBf;
+ Q16Dot16 bottomLeftIntersectAf, bottomLeftIntersectBf, bottomRightIntersectAf, bottomRightIntersectBf;
+
+ int leftMin, leftMax, rightMin, rightMax;
+
+ const Q16Dot16 yTopFP = FloatToQ16Dot16(top.y());
+ const Q16Dot16 yLeftFP = FloatToQ16Dot16(left.y());
+ const Q16Dot16 yRightFP = FloatToQ16Dot16(right.y());
+ const Q16Dot16 yBottomFP = FloatToQ16Dot16(bottom.y());
+
+ rowTop = qMax(iTopFP, yTopFP);
+ topLeftIntersectAf = leftIntersectAf +
+ Q16Dot16Multiply(leftSlopeFP, rowTop - iTopFP);
+ topRightIntersectAf = rightIntersectAf +
+ Q16Dot16Multiply(rightSlopeFP, rowTop - iTopFP);
+
+ Q16Dot16 yFP = iTopFP;
+ while (yFP <= iBottomFP) {
+ rowBottomLeft = qMin(yFP + Q16Dot16Factor, yLeftFP);
+ rowBottomRight = qMin(yFP + Q16Dot16Factor, yRightFP);
+ rowTopLeft = qMax(yFP, yLeftFP);
+ rowTopRight = qMax(yFP, yRightFP);
+ rowBottom = qMin(yFP + Q16Dot16Factor, yBottomFP);
+
+ if (yFP == iLeftFP) {
+ const int y = Q16Dot16ToInt(yFP);
+ leftIntersectBf = FloatToQ16Dot16(left.x() + (y - left.y()) * rightSlope);
+ topLeftIntersectBf = leftIntersectBf + Q16Dot16Multiply(rightSlopeFP, rowTopLeft - yFP);
+ bottomLeftIntersectAf = leftIntersectAf + Q16Dot16Multiply(leftSlopeFP, rowBottomLeft - yFP);
+ } else {
+ topLeftIntersectBf = leftIntersectBf;
+ bottomLeftIntersectAf = leftIntersectAf + leftSlopeFP;
+ }
+
+ if (yFP == iRightFP) {
+ const int y = Q16Dot16ToInt(yFP);
+ rightIntersectBf = FloatToQ16Dot16(right.x() + (y - right.y()) * leftSlope);
+ topRightIntersectBf = rightIntersectBf + Q16Dot16Multiply(leftSlopeFP, rowTopRight - yFP);
+ bottomRightIntersectAf = rightIntersectAf + Q16Dot16Multiply(rightSlopeFP, rowBottomRight - yFP);
+ } else {
+ topRightIntersectBf = rightIntersectBf;
+ bottomRightIntersectAf = rightIntersectAf + rightSlopeFP;
+ }
+
+ if (yFP == iBottomFP) {
+ bottomLeftIntersectBf = leftIntersectBf + Q16Dot16Multiply(rightSlopeFP, rowBottom - yFP);
+ bottomRightIntersectBf = rightIntersectBf + Q16Dot16Multiply(leftSlopeFP, rowBottom - yFP);
+ } else {
+ bottomLeftIntersectBf = leftIntersectBf + rightSlopeFP;
+ bottomRightIntersectBf = rightIntersectBf + leftSlopeFP;
+ }
+
+ if (yFP < iLeftFP) {
+ leftMin = Q16Dot16ToInt(bottomLeftIntersectAf);
+ leftMax = Q16Dot16ToInt(topLeftIntersectAf);
+ } else if (yFP == iLeftFP) {
+ leftMin = Q16Dot16ToInt(qMax(bottomLeftIntersectAf, topLeftIntersectBf));
+ leftMax = Q16Dot16ToInt(qMax(topLeftIntersectAf, bottomLeftIntersectBf));
+ } else {
+ leftMin = Q16Dot16ToInt(topLeftIntersectBf);
+ leftMax = Q16Dot16ToInt(bottomLeftIntersectBf);
+ }
+
+ leftMin = qBound(d->clipRect.left(), leftMin, d->clipRect.right());
+ leftMax = qBound(d->clipRect.left(), leftMax, d->clipRect.right());
+
+ if (yFP < iRightFP) {
+ rightMin = Q16Dot16ToInt(topRightIntersectAf);
+ rightMax = Q16Dot16ToInt(bottomRightIntersectAf);
+ } else if (yFP == iRightFP) {
+ rightMin = Q16Dot16ToInt(qMin(topRightIntersectAf, bottomRightIntersectBf));
+ rightMax = Q16Dot16ToInt(qMin(bottomRightIntersectAf, topRightIntersectBf));
+ } else {
+ rightMin = Q16Dot16ToInt(bottomRightIntersectBf);
+ rightMax = Q16Dot16ToInt(topRightIntersectBf);
+ }
+
+ rightMin = qBound(d->clipRect.left(), rightMin, d->clipRect.right());
+ rightMax = qBound(d->clipRect.left(), rightMax, d->clipRect.right());
+
+ if (leftMax > rightMax)
+ leftMax = rightMax;
+ if (rightMin < leftMin)
+ rightMin = leftMin;
+
+ Q16Dot16 rowHeight = rowBottom - rowTop;
+
+ int x = leftMin;
+ while (x <= leftMax) {
+ Q16Dot16 excluded = 0;
+
+ if (yFP <= iLeftFP)
+ excluded += intersectPixelFP(x, rowTop, rowBottomLeft,
+ bottomLeftIntersectAf, topLeftIntersectAf,
+ leftSlopeFP, -rightSlopeFP);
+ if (yFP >= iLeftFP)
+ excluded += intersectPixelFP(x, rowTopLeft, rowBottom,
+ topLeftIntersectBf, bottomLeftIntersectBf,
+ rightSlopeFP, -leftSlopeFP);
+
+ if (x >= rightMin) {
+ if (yFP <= iRightFP)
+ excluded += (rowBottomRight - rowTop) - intersectPixelFP(x, rowTop, rowBottomRight,
+ topRightIntersectAf, bottomRightIntersectAf,
+ rightSlopeFP, -leftSlopeFP);
+ if (yFP >= iRightFP)
+ excluded += (rowBottom - rowTopRight) - intersectPixelFP(x, rowTopRight, rowBottom,
+ bottomRightIntersectBf, topRightIntersectBf,
+ leftSlopeFP, -rightSlopeFP);
+ }
+
+ Q16Dot16 coverage = rowHeight - excluded;
+ buffer.addSpan(x, 1, Q16Dot16ToInt(yFP),
+ Q16Dot16ToInt(255 * coverage));
+ ++x;
+ }
+ if (x < rightMin) {
+ buffer.addSpan(x, rightMin - x, Q16Dot16ToInt(yFP),
+ Q16Dot16ToInt(255 * rowHeight));
+ x = rightMin;
+ }
+ while (x <= rightMax) {
+ Q16Dot16 excluded = 0;
+ if (yFP <= iRightFP)
+ excluded += (rowBottomRight - rowTop) - intersectPixelFP(x, rowTop, rowBottomRight,
+ topRightIntersectAf, bottomRightIntersectAf,
+ rightSlopeFP, -leftSlopeFP);
+ if (yFP >= iRightFP)
+ excluded += (rowBottom - rowTopRight) - intersectPixelFP(x, rowTopRight, rowBottom,
+ bottomRightIntersectBf, topRightIntersectBf,
+ leftSlopeFP, -rightSlopeFP);
+
+ Q16Dot16 coverage = rowHeight - excluded;
+ buffer.addSpan(x, 1, Q16Dot16ToInt(yFP),
+ Q16Dot16ToInt(255 * coverage));
+ ++x;
+ }
+
+ leftIntersectAf += leftSlopeFP;
+ leftIntersectBf += rightSlopeFP;
+ rightIntersectAf += rightSlopeFP;
+ rightIntersectBf += leftSlopeFP;
+ topLeftIntersectAf = leftIntersectAf;
+ topRightIntersectAf = rightIntersectAf;
+
+ yFP += Q16Dot16Factor;
+ rowTop = yFP;
+ }
+ } else { // aliased
+ int iTop = int(top.y() + 0.5f);
+ int iLeft = left.y() < 0.5f ? -1 : int(left.y() - 0.5f);
+ int iRight = right.y() < 0.5f ? -1 : int(right.y() - 0.5f);
+ int iBottom = bottom.y() < 0.5f? -1 : int(bottom.y() - 0.5f);
+ int iMiddle = qMin(iLeft, iRight);
+
+ Q16Dot16 leftIntersectAf = FloatToQ16Dot16(top.x() + 0.5f + (iTop + 0.5f - top.y()) * leftSlope);
+ Q16Dot16 leftIntersectBf = FloatToQ16Dot16(left.x() + 0.5f + (iLeft + 1.5f - left.y()) * rightSlope);
+ Q16Dot16 rightIntersectAf = FloatToQ16Dot16(top.x() - 0.5f + (iTop + 0.5f - top.y()) * rightSlope);
+ Q16Dot16 rightIntersectBf = FloatToQ16Dot16(right.x() - 0.5f + (iRight + 1.5f - right.y()) * leftSlope);
+
+ int ny;
+ int y = iTop;
+#define DO_SEGMENT(next, li, ri, ls, rs) \
+ ny = qMin(next + 1, d->clipRect.top()); \
+ if (y < ny) { \
+ li += ls * (ny - y); \
+ ri += rs * (ny - y); \
+ y = ny; \
+ } \
+ if (next > d->clipRect.bottom()) \
+ next = d->clipRect.bottom(); \
+ for (; y <= next; ++y) { \
+ const int x1 = qMax(Q16Dot16ToInt(li), d->clipRect.left()); \
+ const int x2 = qMin(Q16Dot16ToInt(ri), d->clipRect.right()); \
+ if (x2 >= x1) \
+ buffer.addSpan(x1, x2 - x1 + 1, y, 255); \
+ li += ls; \
+ ri += rs; \
+ }
+
+ DO_SEGMENT(iMiddle, leftIntersectAf, rightIntersectAf, leftSlopeFP, rightSlopeFP)
+ DO_SEGMENT(iRight, leftIntersectBf, rightIntersectAf, rightSlopeFP, rightSlopeFP)
+ DO_SEGMENT(iLeft, leftIntersectAf, rightIntersectBf, leftSlopeFP, leftSlopeFP)
+ DO_SEGMENT(iBottom, leftIntersectBf, rightIntersectBf, rightSlopeFP, leftSlopeFP)
+#undef DO_SEGMENT
+ }
+ }
+}
+
+void QRasterizer::rasterize(const QT_FT_Outline *outline, Qt::FillRule fillRule)
+{
+ if (outline->n_points < 3 || outline->n_contours == 0)
+ return;
+
+ const QT_FT_Vector *points = outline->points;
+
+ QSpanBuffer buffer(d->blend, d->data, d->clipRect);
+
+ // ### QT_FT_Outline already has a bounding rect which is
+ // ### precomputed at this point, so we should probably just be
+ // ### using that instead...
+ QT_FT_Pos min_y = points[0].y, max_y = points[0].y;
+ for (int i = 1; i < outline->n_points; ++i) {
+ const QT_FT_Vector &p = points[i];
+ min_y = qMin(p.y, min_y);
+ max_y = qMax(p.y, max_y);
+ }
+
+ int iTopBound = qMax(d->clipRect.top(), int((min_y + 32 + COORD_OFFSET - COORD_ROUNDING) >> 6));
+ int iBottomBound = qMin(d->clipRect.bottom(), int((max_y - 32 + COORD_OFFSET - COORD_ROUNDING) >> 6));
+
+ if (iTopBound > iBottomBound)
+ return;
+
+ d->scanConverter.begin(iTopBound, iBottomBound, d->clipRect.left(), d->clipRect.right(), fillRule, &buffer);
+
+ int first = 0;
+ for (int i = 0; i < outline->n_contours; ++i) {
+ const int last = outline->contours[i];
+ for (int j = first; j < last; ++j) {
+ if (outline->tags[j+1] == QT_FT_CURVE_TAG_CUBIC) {
+ Q_ASSERT(outline->tags[j+2] == QT_FT_CURVE_TAG_CUBIC);
+ d->scanConverter.mergeCurve(points[j], points[j+1], points[j+2], points[j+3]);
+ j += 2;
+ } else {
+ d->scanConverter.mergeLine(points[j], points[j+1]);
+ }
+ }
+
+ first = last + 1;
+ }
+
+ d->scanConverter.end();
+}
+
+static inline QT_FT_Vector PointToVector(const QPointF &p)
+{
+ QT_FT_Vector result = { QT_FT_Pos(p.x() * 64), QT_FT_Pos(p.y() * 64) };
+ return result;
+}
+
+void QRasterizer::rasterize(const QPainterPath &path, Qt::FillRule fillRule)
+{
+ if (path.isEmpty())
+ return;
+
+ QSpanBuffer buffer(d->blend, d->data, d->clipRect);
+
+ QRectF bounds = path.controlPointRect();
+
+ int iTopBound = qMax(d->clipRect.top(), int(bounds.top() + 0.5 + (COORD_OFFSET - COORD_ROUNDING)/64.));
+ int iBottomBound = qMin(d->clipRect.bottom(), int(bounds.bottom() - 0.5 + (COORD_OFFSET - COORD_ROUNDING)/64.));
+
+ if (iTopBound > iBottomBound)
+ return;
+
+ d->scanConverter.begin(iTopBound, iBottomBound, d->clipRect.left(), d->clipRect.right(), fillRule, &buffer);
+
+ int subpathStart = 0;
+ QT_FT_Vector last = { 0, 0 };
+ for (int i = 0; i < path.elementCount(); ++i) {
+ switch (path.elementAt(i).type) {
+ case QPainterPath::LineToElement:
+ {
+ QT_FT_Vector p1 = last;
+ QT_FT_Vector p2 = PointToVector(path.elementAt(i));
+ d->scanConverter.mergeLine(p1, p2);
+ last = p2;
+ break;
+ }
+ case QPainterPath::MoveToElement:
+ {
+ if (i != 0) {
+ QT_FT_Vector first = PointToVector(path.elementAt(subpathStart));
+ // close previous subpath
+ if (first.x != last.x || first.y != last.y)
+ d->scanConverter.mergeLine(last, first);
+ }
+ subpathStart = i;
+ last = PointToVector(path.elementAt(i));
+ break;
+ }
+ case QPainterPath::CurveToElement:
+ {
+ QT_FT_Vector p1 = last;
+ QT_FT_Vector p2 = PointToVector(path.elementAt(i));
+ QT_FT_Vector p3 = PointToVector(path.elementAt(++i));
+ QT_FT_Vector p4 = PointToVector(path.elementAt(++i));
+ d->scanConverter.mergeCurve(p1, p2, p3, p4);
+ last = p4;
+ break;
+ }
+ default:
+ Q_ASSERT(false);
+ break;
+ }
+ }
+
+ QT_FT_Vector first = PointToVector(path.elementAt(subpathStart));
+
+ // close path
+ if (first.x != last.x || first.y != last.y)
+ d->scanConverter.mergeLine(last, first);
+
+ d->scanConverter.end();
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/painting/qrasterizer_p.h b/src/gui/painting/qrasterizer_p.h
new file mode 100644
index 0000000000..d7cd2047d3
--- /dev/null
+++ b/src/gui/painting/qrasterizer_p.h
@@ -0,0 +1,91 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QRASTERIZER_P_H
+#define QRASTERIZER_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 "QtCore/qglobal.h"
+#include "QtGui/qpainter.h"
+
+#include <private/qdrawhelper_p.h>
+#include <private/qrasterdefs_p.h>
+
+QT_BEGIN_NAMESPACE
+
+struct QSpanData;
+class QRasterBuffer;
+class QRasterizerPrivate;
+
+class QRasterizer
+{
+public:
+ QRasterizer();
+ ~QRasterizer();
+
+ void setAntialiased(bool antialiased);
+ void setClipRect(const QRect &clipRect);
+
+ void initialize(ProcessSpans blend, void *data);
+
+ void rasterize(const QT_FT_Outline *outline, Qt::FillRule fillRule);
+ void rasterize(const QPainterPath &path, Qt::FillRule fillRule);
+
+ // width should be in units of |a-b|
+ void rasterizeLine(const QPointF &a, const QPointF &b, qreal width, bool squareCap = false);
+
+private:
+ QRasterizerPrivate *d;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/gui/painting/qregion.cpp b/src/gui/painting/qregion.cpp
new file mode 100644
index 0000000000..f728f9de7f
--- /dev/null
+++ b/src/gui/painting/qregion.cpp
@@ -0,0 +1,4318 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qregion.h"
+#include "qpolygon.h"
+#include "qbuffer.h"
+#include "qdatastream.h"
+#include "qvariant.h"
+#include "qvarlengtharray.h"
+
+#include <qdebug.h>
+
+#if defined(Q_OS_UNIX) || defined(Q_OS_WINCE)
+#include "qpainterpath.h"
+#include "qimage.h"
+#include "qbitmap.h"
+#include <stdlib.h>
+#endif
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QRegion
+ \brief The QRegion class specifies a clip region for a painter.
+
+ \ingroup multimedia
+ \ingroup shared
+
+ QRegion is used with QPainter::setClipRegion() to limit the paint
+ area to what needs to be painted. There is also a
+ QWidget::repaint() function that takes a QRegion parameter.
+ QRegion is the best tool for reducing flicker.
+
+ A region can be created from a rectangle, an ellipse, a polygon or
+ a bitmap. Complex regions may be created by combining simple
+ regions using united(), intersected(), subtracted(), or xored() (exclusive
+ or). You can move a region using translate().
+
+ You can test whether a region isEmpty() or if it
+ contains() a QPoint or QRect. The bounding rectangle can be found
+ with boundingRect().
+
+ The function rects() gives a decomposition of the region into
+ rectangles.
+
+ Example of using complex regions:
+ \snippet doc/src/snippets/code/src_gui_painting_qregion.cpp 0
+
+ QRegion is an \l{implicitly shared} class.
+
+ \warning Due to window system limitations, the whole coordinate space for a
+ region is limited to the points between -32767 and 32767 on Windows
+ 95/98/ME. You can circumvent this limitation by using a QPainterPath.
+
+ \section1 Additional License Information
+
+ On Embedded Linux, Windows CE and X11 platforms, parts of this class rely on
+ code obtained under the following license:
+
+ \legalese
+ 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.
+ \endlegalese
+
+ \raw HTML
+ <hr />
+ \endraw
+
+ \legalese
+ 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.
+ \endlegalese
+
+ \sa QPainter::setClipRegion(), QPainter::setClipRect(), QPainterPath
+*/
+
+
+/*!
+ \enum QRegion::RegionType
+
+ Specifies the shape of the region to be created.
+
+ \value Rectangle the region covers the entire rectangle.
+ \value Ellipse the region is an ellipse inside the rectangle.
+*/
+
+/*!
+ \fn void QRegion::translate(const QPoint &point)
+
+ \overload
+
+ Translates the region \a{point}\e{.x()} along the x axis and
+ \a{point}\e{.y()} along the y axis, relative to the current
+ position. Positive values move the region to the right and down.
+
+ Translates to the given \a point.
+*/
+
+/*!
+ \fn Handle QRegion::handle() const
+
+ Returns a platform-specific region handle. The \c Handle type is
+ \c HRGN on Windows, \c Region on X11, and \c RgnHandle on Mac OS
+ X. On \l{Qt for Embedded Linux} it is \c {void *}.
+
+ \warning This function is not portable.
+*/
+
+/*****************************************************************************
+ QRegion member functions
+ *****************************************************************************/
+
+/*!
+ \fn QRegion::QRegion()
+
+ Constructs an empty region.
+
+ \sa isEmpty()
+*/
+
+/*!
+ \fn QRegion::QRegion(const QRect &r, RegionType t)
+ \overload
+
+ Create a region based on the rectange \a r with region type \a t.
+
+ If the rectangle is invalid a null region will be created.
+
+ \sa QRegion::RegionType
+*/
+
+/*!
+ \fn QRegion::QRegion(const QPolygon &a, Qt::FillRule fillRule)
+
+ Constructs a polygon region from the point array \a a with the fill rule
+ specified by \a fillRule.
+
+ If \a fillRule is \l{Qt::WindingFill}, the polygon region is defined
+ using the winding algorithm; if it is \l{Qt::OddEvenFill}, the odd-even fill
+ algorithm is used.
+
+ \warning This constructor can be used to create complex regions that will
+ slow down painting when used.
+*/
+
+/*!
+ \fn QRegion::QRegion(const QRegion &r)
+
+ Constructs a new region which is equal to region \a r.
+*/
+
+/*!
+ \fn QRegion::QRegion(const QBitmap &bm)
+
+ Constructs a region from the bitmap \a bm.
+
+ The resulting region consists of the pixels in bitmap \a bm that
+ are Qt::color1, as if each pixel was a 1 by 1 rectangle.
+
+ This constructor may create complex regions that will slow down
+ painting when used. Note that drawing masked pixmaps can be done
+ much faster using QPixmap::setMask().
+*/
+
+/*!
+ Constructs a rectangular or elliptic region.
+
+ If \a t is \c Rectangle, the region is the filled rectangle (\a x,
+ \a y, \a w, \a h). If \a t is \c Ellipse, the region is the filled
+ ellipse with center at (\a x + \a w / 2, \a y + \a h / 2) and size
+ (\a w ,\a h).
+*/
+QRegion::QRegion(int x, int y, int w, int h, RegionType t)
+{
+ QRegion tmp(QRect(x, y, w, h), t);
+ tmp.d->ref.ref();
+ d = tmp.d;
+}
+
+#ifdef QT3_SUPPORT
+/*!
+ Use the constructor tha takes a Qt::FillRule as the second
+ argument instead.
+*/
+QRegion::QRegion(const QPolygon &pa, bool winding)
+{
+ new (this) QRegion(pa, winding ? Qt::WindingFill : Qt::OddEvenFill);
+}
+#endif
+
+/*!
+ \fn QRegion::~QRegion()
+ \internal
+
+ Destroys the region.
+*/
+
+void QRegion::detach()
+{
+ if (d->ref != 1)
+ *this = copy();
+#if defined(Q_WS_X11)
+ else if (d->xrectangles) {
+ free(d->xrectangles);
+ d->xrectangles = 0;
+ }
+#endif
+}
+
+// duplicates in qregion_win.cpp and qregion_wce.cpp
+#define QRGN_SETRECT 1 // region stream commands
+#define QRGN_SETELLIPSE 2 // (these are internal)
+#define QRGN_SETPTARRAY_ALT 3
+#define QRGN_SETPTARRAY_WIND 4
+#define QRGN_TRANSLATE 5
+#define QRGN_OR 6
+#define QRGN_AND 7
+#define QRGN_SUB 8
+#define QRGN_XOR 9
+#define QRGN_RECTS 10
+
+
+#ifndef QT_NO_DATASTREAM
+
+/*
+ Executes region commands in the internal buffer and rebuilds the
+ original region.
+
+ We do this when we read a region from the data stream.
+
+ If \a ver is non-0, uses the format version \a ver on reading the
+ byte array.
+*/
+void QRegion::exec(const QByteArray &buffer, int ver, QDataStream::ByteOrder byteOrder)
+{
+ QByteArray copy = buffer;
+ QDataStream s(&copy, QIODevice::ReadOnly);
+ if (ver)
+ s.setVersion(ver);
+ s.setByteOrder(byteOrder);
+ QRegion rgn;
+#ifndef QT_NO_DEBUG
+ int test_cnt = 0;
+#endif
+ while (!s.atEnd()) {
+ qint32 id;
+ if (s.version() == 1) {
+ int id_int;
+ s >> id_int;
+ id = id_int;
+ } else {
+ s >> id;
+ }
+#ifndef QT_NO_DEBUG
+ if (test_cnt > 0 && id != QRGN_TRANSLATE)
+ qWarning("QRegion::exec: Internal error");
+ test_cnt++;
+#endif
+ if (id == QRGN_SETRECT || id == QRGN_SETELLIPSE) {
+ QRect r;
+ s >> r;
+ rgn = QRegion(r, id == QRGN_SETRECT ? Rectangle : Ellipse);
+ } else if (id == QRGN_SETPTARRAY_ALT || id == QRGN_SETPTARRAY_WIND) {
+ QPolygon a;
+ s >> a;
+ rgn = QRegion(a, id == QRGN_SETPTARRAY_WIND ? Qt::WindingFill : Qt::OddEvenFill);
+ } else if (id == QRGN_TRANSLATE) {
+ QPoint p;
+ s >> p;
+ rgn.translate(p.x(), p.y());
+ } else if (id >= QRGN_OR && id <= QRGN_XOR) {
+ QByteArray bop1, bop2;
+ QRegion r1, r2;
+ s >> bop1;
+ r1.exec(bop1);
+ s >> bop2;
+ r2.exec(bop2);
+
+ switch (id) {
+ case QRGN_OR:
+ rgn = r1.united(r2);
+ break;
+ case QRGN_AND:
+ rgn = r1.intersected(r2);
+ break;
+ case QRGN_SUB:
+ rgn = r1.subtracted(r2);
+ break;
+ case QRGN_XOR:
+ rgn = r1.xored(r2);
+ break;
+ }
+ } else if (id == QRGN_RECTS) {
+ // (This is the only form used in Qt 2.0)
+ quint32 n;
+ s >> n;
+ QRect r;
+ for (int i=0; i<(int)n; i++) {
+ s >> r;
+ rgn = rgn.united(QRegion(r));
+ }
+ }
+ }
+ *this = rgn;
+}
+
+
+/*****************************************************************************
+ QRegion stream functions
+ *****************************************************************************/
+
+/*!
+ \fn QRegion &QRegion::operator=(const QRegion &r)
+
+ Assigns \a r to this region and returns a reference to the region.
+*/
+
+/*!
+ \relates QRegion
+
+ Writes the region \a r to the stream \a s and returns a reference
+ to the stream.
+
+ \sa \link datastreamformat.html Format of the QDataStream operators \endlink
+*/
+
+QDataStream &operator<<(QDataStream &s, const QRegion &r)
+{
+ QVector<QRect> a = r.rects();
+ if (a.isEmpty()) {
+ s << (quint32)0;
+ } else {
+ if (s.version() == 1) {
+ int i;
+ for (i = a.size() - 1; i > 0; --i) {
+ s << (quint32)(12 + i * 24);
+ s << (int)QRGN_OR;
+ }
+ for (i = 0; i < a.size(); ++i) {
+ s << (quint32)(4+8) << (int)QRGN_SETRECT << a[i];
+ }
+ } else {
+ s << (quint32)(4 + 4 + 16 * a.size()); // 16: storage size of QRect
+ s << (qint32)QRGN_RECTS;
+ s << a;
+ }
+ }
+ return s;
+}
+
+/*!
+ \relates QRegion
+
+ Reads a region from the stream \a s into \a r and returns a
+ reference to the stream.
+
+ \sa \link datastreamformat.html Format of the QDataStream operators \endlink
+*/
+
+QDataStream &operator>>(QDataStream &s, QRegion &r)
+{
+ QByteArray b;
+ s >> b;
+ r.exec(b, s.version(), s.byteOrder());
+ return s;
+}
+#endif //QT_NO_DATASTREAM
+
+#ifndef QT_NO_DEBUG_STREAM
+QDebug operator<<(QDebug s, const QRegion &r)
+{
+ QVector<QRect> rects = r.rects();
+ s.nospace() << "QRegion(size=" << rects.size() << "), "
+ << "bounds = " << r.boundingRect() << "\n";
+ for (int i=0; i<rects.size(); ++i)
+ s << "- " << i << rects.at(i) << "\n";
+ return s;
+}
+#endif
+
+
+// These are not inline - they can be implemented better on some platforms
+// (eg. Windows at least provides 3-variable operations). For now, simple.
+
+
+/*!
+ Applies the united() function to this region and \a r. \c r1|r2 is
+ equivalent to \c r1.united(r2).
+
+ \sa united(), operator+()
+*/
+const QRegion QRegion::operator|(const QRegion &r) const
+ { return united(r); }
+
+/*!
+ Applies the united() function to this region and \a r. \c r1+r2 is
+ equivalent to \c r1.united(r2).
+
+ \sa united(), operator|()
+*/
+const QRegion QRegion::operator+(const QRegion &r) const
+ { return united(r); }
+
+/*!
+ \overload
+ \since 4.4
+ */
+const QRegion QRegion::operator+(const QRect &r) const
+ { return united(r); }
+
+/*!
+ Applies the intersected() function to this region and \a r. \c r1&r2
+ is equivalent to \c r1.intersected(r2).
+
+ \sa intersected()
+*/
+const QRegion QRegion::operator&(const QRegion &r) const
+ { return intersected(r); }
+
+/*!
+ \overload
+ \since 4.4
+ */
+const QRegion QRegion::operator&(const QRect &r) const
+{
+ return intersected(r);
+}
+
+/*!
+ Applies the subtracted() function to this region and \a r. \c r1-r2
+ is equivalent to \c r1.subtracted(r2).
+
+ \sa subtracted()
+*/
+const QRegion QRegion::operator-(const QRegion &r) const
+ { return subtracted(r); }
+
+/*!
+ Applies the xored() function to this region and \a r. \c r1^r2 is
+ equivalent to \c r1.xored(r2).
+
+ \sa xored()
+*/
+const QRegion QRegion::operator^(const QRegion &r) const
+ { return xored(r); }
+
+/*!
+ Applies the united() function to this region and \a r and assigns
+ the result to this region. \c r1|=r2 is equivalent to \c
+ {r1 = r1.united(r2)}.
+
+ \sa united()
+*/
+QRegion& QRegion::operator|=(const QRegion &r)
+ { return *this = *this | r; }
+
+/*!
+ \fn QRegion& QRegion::operator+=(const QRect &rect)
+
+ Returns a region that is the union of this region with the specified \a rect.
+
+ \sa united()
+*/
+/*!
+ \fn QRegion& QRegion::operator+=(const QRegion &r)
+
+ Applies the united() function to this region and \a r and assigns
+ the result to this region. \c r1+=r2 is equivalent to \c
+ {r1 = r1.united(r2)}.
+
+ \sa intersected()
+*/
+#if !defined (Q_OS_UNIX) && !defined (Q_OS_WINCE)
+QRegion& QRegion::operator+=(const QRect &r)
+{
+ return operator+=(QRegion(r));
+}
+#endif
+
+/*!
+ \fn QRegion& QRegion::operator&=(const QRegion &r)
+
+ Applies the intersected() function to this region and \a r and
+ assigns the result to this region. \c r1&=r2 is equivalent to \c
+ r1 = r1.intersected(r2).
+
+ \sa intersected()
+*/
+#if !defined(Q_WS_WIN) || defined(Q_OS_WINCE)
+QRegion& QRegion::operator&=(const QRegion &r)
+ { return *this = *this & r; }
+#endif
+
+/*!
+ \overload
+ \since 4.4
+ */
+#if defined (Q_OS_UNIX) || defined (Q_OS_WINCE)
+QRegion& QRegion::operator&=(const QRect &r)
+{
+ return *this = *this & r;
+}
+#else
+QRegion& QRegion::operator&=(const QRect &r)
+{
+ return *this &= (QRegion(r));
+}
+#endif
+
+/*!
+ \fn QRegion& QRegion::operator-=(const QRegion &r)
+
+ Applies the subtracted() function to this region and \a r and
+ assigns the result to this region. \c r1-=r2 is equivalent to \c
+ {r1 = r1.subtracted(r2)}.
+
+ \sa subtracted()
+*/
+#if !defined(Q_WS_WIN) || defined(Q_OS_WINCE)
+QRegion& QRegion::operator-=(const QRegion &r)
+ { return *this = *this - r; }
+#endif
+
+/*!
+ Applies the xored() function to this region and \a r and
+ assigns the result to this region. \c r1^=r2 is equivalent to \c
+ {r1 = r1.xored(r2)}.
+
+ \sa xored()
+*/
+QRegion& QRegion::operator^=(const QRegion &r)
+ { return *this = *this ^ r; }
+
+/*!
+ \fn bool QRegion::operator!=(const QRegion &other) const
+
+ Returns true if this region is different from the \a other region;
+ otherwise returns false.
+*/
+
+/*!
+ Returns the region as a QVariant
+*/
+QRegion::operator QVariant() const
+{
+ return QVariant(QVariant::Region, this);
+}
+
+/*!
+ \fn bool QRegion::operator==(const QRegion &r) const
+
+ Returns true if the region is equal to \a r; otherwise returns
+ false.
+*/
+
+/*!
+ \fn bool QRegion::isNull() const
+
+ Use isEmpty() instead.
+*/
+
+
+/*!
+ \fn void QRegion::translate(int dx, int dy)
+
+ Translates (moves) the region \a dx along the X axis and \a dy
+ along the Y axis.
+*/
+
+/*!
+ \fn QRegion QRegion::translated(const QPoint &p) const
+ \overload
+ \since 4.1
+
+ Returns a copy of the regtion that is translated \a{p}\e{.x()}
+ along the x axis and \a{p}\e{.y()} along the y axis, relative to
+ the current position. Positive values move the rectangle to the
+ right and down.
+
+ \sa translate()
+*/
+
+/*!
+ \since 4.1
+
+ Returns a copy of the region that is translated \a dx along the
+ x axis and \a dy along the y axis, relative to the current
+ position. Positive values move the region to the right and
+ down.
+
+ \sa translate()
+*/
+
+QRegion
+QRegion::translated(int dx, int dy) const
+{
+ QRegion ret(*this);
+ ret.translate(dx, dy);
+ return ret;
+}
+
+
+inline bool rect_intersects(const QRect &r1, const QRect &r2)
+{
+ return (r1.right() >= r2.left() && r1.left() <= r2.right() &&
+ r1.bottom() >= r2.top() && r1.top() <= r2.bottom());
+}
+
+/*!
+ \since 4.2
+
+ Returns true if this region intersects with \a region, otherwise
+ returns false.
+*/
+bool QRegion::intersects(const QRegion &region) const
+{
+ if (isEmpty() || region.isEmpty())
+ return false;
+
+ if (!rect_intersects(boundingRect(), region.boundingRect()))
+ return false;
+
+ const QVector<QRect> myRects = rects();
+ const QVector<QRect> otherRects = region.rects();
+
+ for (QVector<QRect>::const_iterator i1 = myRects.constBegin(); i1 < myRects.constEnd(); ++i1)
+ for (QVector<QRect>::const_iterator i2 = otherRects.constBegin(); i2 < otherRects.constEnd(); ++i2)
+ if (rect_intersects(*i1, *i2))
+ return true;
+ return false;
+}
+
+/*!
+ \since 4.2
+
+ Returns true if this region intersects with \a rect, otherwise
+ returns false.
+*/
+bool QRegion::intersects(const QRect &rect) const
+{
+ if (isEmpty() || rect.isNull())
+ return false;
+
+ const QRect r = rect.normalized();
+ if (!rect_intersects(boundingRect(), r))
+ return false;
+
+ const QVector<QRect> myRects = rects();
+ for (QVector<QRect>::const_iterator it = myRects.constBegin(); it < myRects.constEnd(); ++it)
+ if (rect_intersects(r, *it))
+ return true;
+ return false;
+}
+
+#if !defined (Q_OS_UNIX) && !defined (Q_OS_WINCE)
+/*!
+ \overload
+ \since 4.4
+*/
+QRegion QRegion::intersect(const QRect &r) const
+{
+ return intersect(QRegion(r));
+}
+#endif
+
+/*!
+ \fn int QRegion::numRects() const
+ \since 4.4
+
+ Returns the number of rectangles that will be returned in rects().
+*/
+
+/*!
+ \fn bool QRegion::isEmpty() const
+
+ Returns true if the region is empty; otherwise returns false. An
+ empty region is a region that contains no points.
+
+ Example:
+ \snippet doc/src/snippets/code/src_gui_painting_qregion_unix.cpp 0
+*/
+
+/*!
+ \fn bool QRegion::contains(const QPoint &p) const
+
+ Returns true if the region contains the point \a p; otherwise
+ returns false.
+*/
+
+/*!
+ \fn bool QRegion::contains(const QRect &r) const
+ \overload
+
+ Returns true if the region overlaps the rectangle \a r; otherwise
+ returns false.
+*/
+
+/*!
+ \fn QRegion QRegion::unite(const QRegion &r) const
+ \obsolete
+
+ Use united(\a r) instead.
+*/
+
+/*!
+ \fn QRegion QRegion::unite(const QRect &rect) const
+ \since 4.4
+ \obsolete
+
+ Use united(\a rect) instead.
+*/
+
+/*!
+ \fn QRegion QRegion::united(const QRect &rect) const
+ \since 4.4
+
+ Returns a region which is the union of this region and the given \a rect.
+
+ \sa intersected(), subtracted(), xored()
+*/
+
+/*!
+ \fn QRegion QRegion::united(const QRegion &r) const
+ \since 4.2
+
+ Returns a region which is the union of this region and \a r.
+
+ \img runion.png Region Union
+
+ The figure shows the union of two elliptical regions.
+
+ \sa intersected(), subtracted(), xored()
+*/
+
+/*!
+ \fn QRegion QRegion::intersect(const QRegion &r) const
+ \obsolete
+
+ Use intersected(\a r) instead.
+*/
+
+/*!
+ \fn QRegion QRegion::intersect(const QRect &rect) const
+ \since 4.4
+ \obsolete
+
+ Use intersected(\a rect) instead.
+*/
+
+/*!
+ \fn QRegion QRegion::intersected(const QRect &rect) const
+ \since 4.4
+
+ Returns a region which is the intersection of this region and the given \a rect.
+
+ \sa subtracted(), united(), xored()
+*/
+
+/*!
+ \fn QRegion QRegion::intersected(const QRegion &r) const
+ \since 4.2
+
+ Returns a region which is the intersection of this region and \a r.
+
+ \img rintersect.png Region Intersection
+
+ The figure shows the intersection of two elliptical regions.
+
+ \sa subtracted(), united(), xored()
+*/
+
+/*!
+ \fn QRegion QRegion::subtract(const QRegion &r) const
+ \obsolete
+
+ Use subtracted(\a r) instead.
+*/
+
+/*!
+ \fn QRegion QRegion::subtracted(const QRegion &r) const
+ \since 4.2
+
+ Returns a region which is \a r subtracted from this region.
+
+ \img rsubtract.png Region Subtraction
+
+ The figure shows the result when the ellipse on the right is
+ subtracted from the ellipse on the left (\c {left - right}).
+
+ \sa intersected(), united(), xored()
+*/
+
+/*!
+ \fn QRegion QRegion::eor(const QRegion &r) const
+ \obsolete
+
+ Use xored(\a r) instead.
+*/
+
+/*!
+ \fn QRegion QRegion::xored(const QRegion &r) const
+ \since 4.2
+
+ Returns a region which is the exclusive or (XOR) of this region
+ and \a r.
+
+ \img rxor.png Region XORed
+
+ The figure shows the exclusive or of two elliptical regions.
+
+ \sa intersected(), united(), subtracted()
+*/
+
+/*!
+ \fn QRect QRegion::boundingRect() const
+
+ Returns the bounding rectangle of this region. An empty region
+ gives a rectangle that is QRect::isNull().
+*/
+
+/*!
+ \fn QVector<QRect> QRegion::rects() const
+
+ Returns an array of non-overlapping rectangles that make up the
+ region.
+
+ The union of all the rectangles is equal to the original region.
+*/
+
+/*!
+ \fn void QRegion::setRects(const QRect *rects, int number)
+
+ Sets the region using the array of rectangles specified by \a rects and
+ \a number.
+ The rectangles \e must be optimally Y-X sorted and follow these restrictions:
+
+ \list
+ \o The rectangles must not intersect.
+ \o All rectangles with a given top coordinate must have the same height.
+ \o No two rectangles may abut horizontally (they should be combined
+ into a single wider rectangle in that case).
+ \o The rectangles must be sorted in ascending order, with Y as the major
+ sort key and X as the minor sort key.
+ \endlist
+ \omit
+ Only some platforms have these restrictions (Qt for Embedded Linux, X11 and Mac OS X).
+ \endomit
+*/
+
+namespace {
+
+struct Segment
+{
+ Segment() {}
+ Segment(const QPoint &p)
+ : added(false)
+ , point(p)
+ {
+ }
+
+ int left() const
+ {
+ return qMin(point.x(), next->point.x());
+ }
+
+ int right() const
+ {
+ return qMax(point.x(), next->point.x());
+ }
+
+ bool overlaps(const Segment &other) const
+ {
+ return left() < other.right() && other.left() < right();
+ }
+
+ void connect(Segment &other)
+ {
+ next = &other;
+ other.prev = this;
+
+ horizontal = (point.y() == other.point.y());
+ }
+
+ void merge(Segment &other)
+ {
+ if (right() <= other.right()) {
+ QPoint p = other.point;
+ Segment *oprev = other.prev;
+
+ other.point = point;
+ other.prev = prev;
+ prev->next = &other;
+
+ point = p;
+ prev = oprev;
+ oprev->next = this;
+ } else {
+ Segment *onext = other.next;
+ other.next = next;
+ next->prev = &other;
+
+ next = onext;
+ next->prev = this;
+ }
+ }
+
+ int horizontal : 1;
+ int added : 1;
+
+ QPoint point;
+ Segment *prev;
+ Segment *next;
+};
+
+void mergeSegments(Segment *a, int na, Segment *b, int nb)
+{
+ int i = 0;
+ int j = 0;
+
+ while (i != na && j != nb) {
+ Segment &sa = a[i];
+ Segment &sb = b[j];
+ const int ra = sa.right();
+ const int rb = sb.right();
+ if (sa.overlaps(sb))
+ sa.merge(sb);
+ i += (rb >= ra);
+ j += (ra >= rb);
+ }
+}
+
+void addSegmentsToPath(Segment *segment, QPainterPath &path)
+{
+ Segment *current = segment;
+ path.moveTo(current->point);
+
+ current->added = true;
+
+ Segment *last = current;
+ current = current->next;
+ while (current != segment) {
+ if (current->horizontal != last->horizontal)
+ path.lineTo(current->point);
+ current->added = true;
+ last = current;
+ current = current->next;
+ }
+}
+
+}
+
+Q_AUTOTEST_EXPORT QPainterPath qt_regionToPath(const QRegion &region)
+{
+ QPainterPath result;
+ if (region.numRects() == 1) {
+ result.addRect(region.boundingRect());
+ return result;
+ }
+
+ const QVector<QRect> rects = region.rects();
+
+ QVarLengthArray<Segment> segments;
+ segments.resize(4 * rects.size());
+
+ const QRect *rect = rects.constData();
+ const QRect *end = rect + rects.size();
+
+ int lastRowSegmentCount = 0;
+ Segment *lastRowSegments = 0;
+
+ int lastSegment = 0;
+ int lastY = 0;
+ while (rect != end) {
+ const int y = rect[0].y();
+ int count = 0;
+ while (&rect[count] != end && rect[count].y() == y)
+ ++count;
+
+ for (int i = 0; i < count; ++i) {
+ int offset = lastSegment + i;
+ segments[offset] = Segment(rect[i].topLeft());
+ segments[offset += count] = Segment(rect[i].topRight() + QPoint(1, 0));
+ segments[offset += count] = Segment(rect[i].bottomRight() + QPoint(1, 1));
+ segments[offset += count] = Segment(rect[i].bottomLeft() + QPoint(0, 1));
+
+ offset = lastSegment + i;
+ for (int j = 0; j < 4; ++j)
+ segments[offset + j * count].connect(segments[offset + ((j + 1) % 4) * count]);
+ }
+
+ if (lastRowSegments && lastY == y)
+ mergeSegments(lastRowSegments, lastRowSegmentCount, &segments[lastSegment], count);
+
+ lastRowSegments = &segments[lastSegment + 2 * count];
+ lastRowSegmentCount = count;
+ lastSegment += 4 * count;
+ lastY = y + rect[0].height();
+ rect += count;
+ }
+
+ for (int i = 0; i < lastSegment; ++i) {
+ Segment *segment = &segments[i];
+ if (!segment->added)
+ addSegmentsToPath(segment, result);
+ }
+
+ return result;
+}
+
+#if defined(Q_OS_UNIX) || defined(Q_OS_WINCE)
+
+//#define QT_REGION_DEBUG
+/*
+ * clip region
+ */
+
+struct QRegionPrivate {
+ int numRects;
+ QVector<QRect> rects;
+ QRect extents;
+ QRect innerRect;
+ int innerArea;
+
+ inline QRegionPrivate() : numRects(0), innerArea(-1) {}
+ inline QRegionPrivate(const QRect &r) {
+ numRects = 1;
+ extents = r;
+ innerRect = r;
+ innerArea = r.width() * r.height();
+ }
+
+ inline QRegionPrivate(const QRegionPrivate &r) {
+ rects = r.rects;
+ numRects = r.numRects;
+ extents = r.extents;
+ innerRect = r.innerRect;
+ innerArea = r.innerArea;
+ }
+
+ inline QRegionPrivate &operator=(const QRegionPrivate &r) {
+ rects = r.rects;
+ numRects = r.numRects;
+ extents = r.extents;
+ innerRect = r.innerRect;
+ innerArea = r.innerArea;
+ return *this;
+ }
+
+ void intersect(const QRect &r);
+
+ /*
+ * Returns true if r is guaranteed to be fully contained in this region.
+ * A false return value does not guarantee the opposite.
+ */
+ inline bool contains(const QRegionPrivate &r) const {
+ return contains(r.extents);
+ }
+
+ inline bool contains(const QRect &r2) const {
+ const QRect &r1 = innerRect;
+ return r2.left() >= r1.left() && r2.right() <= r1.right()
+ && r2.top() >= r1.top() && r2.bottom() <= r1.bottom();
+ }
+
+ /*
+ * Returns true if this region is guaranteed to be fully contained in r.
+ */
+ inline bool within(const QRect &r1) const {
+ const QRect &r2 = extents;
+ return r2.left() >= r1.left() && r2.right() <= r1.right()
+ && r2.top() >= r1.top() && r2.bottom() <= r1.bottom();
+ }
+
+ inline void updateInnerRect(const QRect &rect) {
+ const int area = rect.width() * rect.height();
+ if (area > innerArea) {
+ innerArea = area;
+ innerRect = rect;
+ }
+ }
+
+ inline void vectorize() {
+ if (numRects == 1) {
+ if (!rects.size())
+ rects.resize(1);
+ rects[0] = extents;
+ }
+ }
+
+ inline void append(const QRect *r);
+ void append(const QRegionPrivate *r);
+ void prepend(const QRect *r);
+ void prepend(const QRegionPrivate *r);
+ inline bool canAppend(const QRect *r) const;
+ inline bool canAppend(const QRegionPrivate *r) const;
+ inline bool canPrepend(const QRect *r) const;
+ inline bool canPrepend(const QRegionPrivate *r) const;
+
+ inline bool mergeFromRight(QRect *left, const QRect *right);
+ inline bool mergeFromLeft(QRect *left, const QRect *right);
+ inline bool mergeFromBelow(QRect *top, const QRect *bottom,
+ const QRect *nextToTop,
+ const QRect *nextToBottom);
+ inline bool mergeFromAbove(QRect *bottom, const QRect *top,
+ const QRect *nextToBottom,
+ const QRect *nextToTop);
+
+#ifdef QT_REGION_DEBUG
+ void selfTest() const;
+#endif
+};
+
+static inline bool isEmptyHelper(const QRegionPrivate *preg)
+{
+ return !preg || preg->numRects == 0;
+}
+
+static inline bool canMergeFromRight(const QRect *left, const QRect *right)
+{
+ return (right->top() == left->top()
+ && right->bottom() == left->bottom()
+ && right->left() <= (left->right() + 1));
+}
+
+static inline bool canMergeFromLeft(const QRect *right, const QRect *left)
+{
+ return canMergeFromRight(left, right);
+}
+
+bool QRegionPrivate::mergeFromRight(QRect *left, const QRect *right)
+{
+ if (canMergeFromRight(left, right)) {
+ left->setRight(right->right());
+ updateInnerRect(*left);
+ return true;
+ }
+ return false;
+}
+
+bool QRegionPrivate::mergeFromLeft(QRect *right, const QRect *left)
+{
+ if (canMergeFromLeft(right, left)) {
+ right->setLeft(left->left());
+ updateInnerRect(*right);
+ return true;
+ }
+ return false;
+}
+
+static inline bool canMergeFromBelow(const QRect *top, const QRect *bottom,
+ const QRect *nextToTop,
+ const QRect *nextToBottom)
+{
+ if (nextToTop && nextToTop->y() == top->y())
+ return false;
+ if (nextToBottom && nextToBottom->y() == bottom->y())
+ return false;
+
+ return ((top->bottom() >= (bottom->top() - 1))
+ && top->left() == bottom->left()
+ && top->right() == bottom->right());
+}
+
+bool QRegionPrivate::mergeFromBelow(QRect *top, const QRect *bottom,
+ const QRect *nextToTop,
+ const QRect *nextToBottom)
+{
+ if (canMergeFromBelow(top, bottom, nextToTop, nextToBottom)) {
+ top->setBottom(bottom->bottom());
+ updateInnerRect(*top);
+ return true;
+ }
+ return false;
+}
+
+bool QRegionPrivate::mergeFromAbove(QRect *bottom, const QRect *top,
+ const QRect *nextToBottom,
+ const QRect *nextToTop)
+{
+ if (canMergeFromBelow(top, bottom, nextToTop, nextToBottom)) {
+ bottom->setTop(top->top());
+ updateInnerRect(*bottom);
+ return true;
+ }
+ return false;
+}
+
+static inline QRect qt_rect_intersect_normalized(const QRect &r1,
+ const QRect &r2)
+{
+ QRect r;
+ r.setLeft(qMax(r1.left(), r2.left()));
+ r.setRight(qMin(r1.right(), r2.right()));
+ r.setTop(qMax(r1.top(), r2.top()));
+ r.setBottom(qMin(r1.bottom(), r2.bottom()));
+ return r;
+}
+
+void QRegionPrivate::intersect(const QRect &rect)
+{
+ Q_ASSERT(extents.intersects(rect));
+ Q_ASSERT(numRects > 1);
+
+#ifdef QT_REGION_DEBUG
+ selfTest();
+#endif
+
+ const QRect r = rect.normalized();
+ extents = QRect();
+ innerRect = QRect();
+ innerArea = -1;
+
+ QRect *dest = rects.data();
+ const QRect *src = dest;
+ int n = numRects;
+ numRects = 0;
+ while (n--) {
+ *dest = qt_rect_intersect_normalized(*src++, r);
+ if (dest->isEmpty())
+ continue;
+
+ if (numRects == 0) {
+ extents = *dest;
+ } else {
+ extents.setLeft(qMin(extents.left(), dest->left()));
+ // hw: extents.top() will never change after initialization
+ //extents.setTop(qMin(extents.top(), dest->top()));
+ extents.setRight(qMax(extents.right(), dest->right()));
+ extents.setBottom(qMax(extents.bottom(), dest->bottom()));
+
+ const QRect *nextToLast = (numRects > 1 ? dest - 2 : 0);
+
+ // mergeFromBelow inlined and optimized
+ if (canMergeFromBelow(dest - 1, dest, nextToLast, 0)) {
+ if (!n || src->y() != dest->y() || src->left() > r.right()) {
+ QRect *prev = dest - 1;
+ prev->setBottom(dest->bottom());
+ updateInnerRect(*prev);
+ continue;
+ }
+ }
+ }
+ updateInnerRect(*dest);
+ ++dest;
+ ++numRects;
+ }
+#ifdef QT_REGION_DEBUG
+ selfTest();
+#endif
+}
+
+void QRegionPrivate::append(const QRect *r)
+{
+ Q_ASSERT(!r->isEmpty());
+
+ QRect *myLast = (numRects == 1 ? &extents : rects.data() + (numRects - 1));
+ if (mergeFromRight(myLast, r)) {
+ if (numRects > 1) {
+ const QRect *nextToTop = (numRects > 2 ? myLast - 2 : 0);
+ if (mergeFromBelow(myLast - 1, myLast, nextToTop, 0))
+ --numRects;
+ }
+ } else if (mergeFromBelow(myLast, r, (numRects > 1 ? myLast - 1 : 0), 0)) {
+ // nothing
+ } else {
+ vectorize();
+ ++numRects;
+ updateInnerRect(*r);
+ if (rects.size() < numRects)
+ rects.resize(numRects);
+ rects[numRects - 1] = *r;
+ }
+ extents.setCoords(qMin(extents.left(), r->left()),
+ qMin(extents.top(), r->top()),
+ qMax(extents.right(), r->right()),
+ qMax(extents.bottom(), r->bottom()));
+
+#ifdef QT_REGION_DEBUG
+ selfTest();
+#endif
+}
+
+void QRegionPrivate::append(const QRegionPrivate *r)
+{
+ Q_ASSERT(!isEmptyHelper(r));
+
+ if (r->numRects == 1) {
+ append(&r->extents);
+ return;
+ }
+
+ vectorize();
+
+ QRect *destRect = rects.data() + numRects;
+ const QRect *srcRect = r->rects.constData();
+ int numAppend = r->numRects;
+
+ // try merging
+ {
+ const QRect *rFirst = srcRect;
+ QRect *myLast = destRect - 1;
+ const QRect *nextToLast = (numRects > 1 ? myLast - 1 : 0);
+ if (mergeFromRight(myLast, rFirst)) {
+ ++srcRect;
+ --numAppend;
+ const QRect *rNextToFirst = (numAppend > 1 ? rFirst + 2 : 0);
+ if (mergeFromBelow(myLast, rFirst + 1, nextToLast, rNextToFirst)) {
+ ++srcRect;
+ --numAppend;
+ }
+ if (numRects > 1) {
+ nextToLast = (numRects > 2 ? myLast - 2 : 0);
+ rNextToFirst = (numAppend > 0 ? srcRect : 0);
+ if (mergeFromBelow(myLast - 1, myLast, nextToLast, rNextToFirst)) {
+ --destRect;
+ --numRects;
+ }
+ }
+ } else if (mergeFromBelow(myLast, rFirst, nextToLast, rFirst + 1)) {
+ ++srcRect;
+ --numAppend;
+ }
+ }
+
+ // append rectangles
+ if (numAppend > 0) {
+ const int newNumRects = numRects + numAppend;
+ if (newNumRects > rects.size()) {
+ rects.resize(newNumRects);
+ destRect = rects.data() + numRects;
+ }
+ memcpy(destRect, srcRect, numAppend * sizeof(QRect));
+
+ numRects = newNumRects;
+ }
+
+ // update inner rectangle
+ if (innerArea < r->innerArea) {
+ innerArea = r->innerArea;
+ innerRect = r->innerRect;
+ }
+
+ // update extents
+ destRect = &extents;
+ srcRect = &r->extents;
+ extents.setCoords(qMin(destRect->left(), srcRect->left()),
+ qMin(destRect->top(), srcRect->top()),
+ qMax(destRect->right(), srcRect->right()),
+ qMax(destRect->bottom(), srcRect->bottom()));
+
+#ifdef QT_REGION_DEBUG
+ selfTest();
+#endif
+}
+
+void QRegionPrivate::prepend(const QRegionPrivate *r)
+{
+ Q_ASSERT(!isEmptyHelper(r));
+
+ if (r->numRects == 1) {
+ prepend(&r->extents);
+ return;
+ }
+
+ vectorize();
+
+ int numPrepend = r->numRects;
+ int numSkip = 0;
+
+ // try merging
+ {
+ QRect *myFirst = rects.data();
+ const QRect *nextToFirst = (numRects > 1 ? myFirst + 1 : 0);
+ const QRect *rLast = r->rects.constData() + r->numRects - 1;
+ const QRect *rNextToLast = (r->numRects > 1 ? rLast - 1 : 0);
+ if (mergeFromLeft(myFirst, rLast)) {
+ --numPrepend;
+ --rLast;
+ rNextToLast = (numPrepend > 1 ? rLast - 1 : 0);
+ if (mergeFromAbove(myFirst, rLast, nextToFirst, rNextToLast)) {
+ --numPrepend;
+ --rLast;
+ }
+ if (numRects > 1) {
+ nextToFirst = (numRects > 2? myFirst + 2 : 0);
+ rNextToLast = (numPrepend > 0 ? rLast : 0);
+ if (mergeFromAbove(myFirst + 1, myFirst, nextToFirst, rNextToLast)) {
+ --numRects;
+ ++numSkip;
+ }
+ }
+ } else if (mergeFromAbove(myFirst, rLast, nextToFirst, rNextToLast)) {
+ --numPrepend;
+ }
+ }
+
+ if (numPrepend > 0) {
+ const int newNumRects = numRects + numPrepend;
+ if (newNumRects > rects.size())
+ rects.resize(newNumRects);
+
+ // move existing rectangles
+ memmove(rects.data() + numPrepend, rects.constData() + numSkip,
+ numRects * sizeof(QRect));
+
+ // prepend new rectangles
+ memcpy(rects.data(), r->rects.constData(), numPrepend * sizeof(QRect));
+
+ numRects = newNumRects;
+ }
+
+ // update inner rectangle
+ if (innerArea < r->innerArea) {
+ innerArea = r->innerArea;
+ innerRect = r->innerRect;
+ }
+
+ // update extents
+ extents.setCoords(qMin(extents.left(), r->extents.left()),
+ qMin(extents.top(), r->extents.top()),
+ qMax(extents.right(), r->extents.right()),
+ qMax(extents.bottom(), r->extents.bottom()));
+
+#ifdef QT_REGION_DEBUG
+ selfTest();
+#endif
+}
+
+void QRegionPrivate::prepend(const QRect *r)
+{
+ Q_ASSERT(!r->isEmpty());
+
+ QRect *myFirst = (numRects == 1 ? &extents : rects.data());
+ if (mergeFromLeft(myFirst, r)) {
+ if (numRects > 1) {
+ const QRect *nextToFirst = (numRects > 2 ? myFirst + 2 : 0);
+ if (mergeFromAbove(myFirst + 1, myFirst, nextToFirst, 0)) {
+ --numRects;
+ memmove(rects.data(), rects.constData() + 1,
+ numRects * sizeof(QRect));
+ }
+ }
+ } else if (mergeFromAbove(myFirst, r, (numRects > 1 ? myFirst + 1 : 0), 0)) {
+ // nothing
+ } else {
+ vectorize();
+ ++numRects;
+ updateInnerRect(*r);
+ rects.prepend(*r);
+ }
+ extents.setCoords(qMin(extents.left(), r->left()),
+ qMin(extents.top(), r->top()),
+ qMax(extents.right(), r->right()),
+ qMax(extents.bottom(), r->bottom()));
+
+#ifdef QT_REGION_DEBUG
+ selfTest();
+#endif
+}
+
+bool QRegionPrivate::canAppend(const QRect *r) const
+{
+ Q_ASSERT(!r->isEmpty());
+
+ const QRect *myLast = (numRects == 1) ? &extents : (rects.constData() + (numRects - 1));
+ if (r->top() > myLast->bottom())
+ return true;
+ if (r->top() == myLast->top()
+ && r->height() == myLast->height()
+ && r->left() > myLast->right())
+ {
+ return true;
+ }
+
+ return false;
+}
+
+bool QRegionPrivate::canAppend(const QRegionPrivate *r) const
+{
+ return canAppend(r->numRects == 1 ? &r->extents : r->rects.constData());
+}
+
+bool QRegionPrivate::canPrepend(const QRect *r) const
+{
+ Q_ASSERT(!r->isEmpty());
+
+ const QRect *myFirst = (numRects == 1) ? &extents : rects.constData();
+ if (r->bottom() < myFirst->top()) // not overlapping
+ return true;
+ if (r->top() == myFirst->top()
+ && r->height() == myFirst->height()
+ && r->right() < myFirst->left())
+ {
+ return true;
+ }
+
+ return false;
+}
+
+bool QRegionPrivate::canPrepend(const QRegionPrivate *r) const
+{
+ return canPrepend(r->numRects == 1 ? &r->extents : r->rects.constData() + r->numRects - 1);
+}
+
+#ifdef QT_REGION_DEBUG
+void QRegionPrivate::selfTest() const
+{
+ if (numRects == 0) {
+ Q_ASSERT(extents.isEmpty());
+ Q_ASSERT(innerRect.isEmpty());
+ return;
+ }
+
+ Q_ASSERT(innerArea == (innerRect.width() * innerRect.height()));
+
+ if (numRects == 1) {
+ Q_ASSERT(innerRect == extents);
+ Q_ASSERT(!innerRect.isEmpty());
+ return;
+ }
+
+ for (int i = 0; i < numRects; ++i) {
+ const QRect r = rects.at(i);
+ if ((r.width() * r.height()) > innerArea)
+ qDebug() << "selfTest(): innerRect" << innerRect << "<" << r;
+ }
+
+ QRect r = rects.first();
+ for (int i = 1; i < numRects; ++i) {
+ const QRect r2 = rects.at(i);
+ Q_ASSERT(!r2.isEmpty());
+ if (r2.y() == r.y()) {
+ Q_ASSERT(r.bottom() == r2.bottom());
+ Q_ASSERT(r.right() < (r2.left() + 1));
+ } else {
+ Q_ASSERT(r2.y() >= r.bottom());
+ }
+ r = r2;
+ }
+}
+#endif // QT_REGION_DEBUG
+
+#if defined(Q_WS_X11)
+QT_BEGIN_INCLUDE_NAMESPACE
+# include "qregion_x11.cpp"
+QT_END_INCLUDE_NAMESPACE
+#elif defined(Q_WS_MAC)
+QT_BEGIN_INCLUDE_NAMESPACE
+# include "qregion_mac.cpp"
+QT_END_INCLUDE_NAMESPACE
+#elif defined(Q_OS_WINCE)
+QT_BEGIN_INCLUDE_NAMESPACE
+# include "qregion_wince.cpp"
+QT_END_INCLUDE_NAMESPACE
+#elif defined(Q_WS_QWS)
+static QRegionPrivate qrp;
+QRegion::QRegionData QRegion::shared_empty = {Q_BASIC_ATOMIC_INITIALIZER(1), &qrp};
+#endif
+
+typedef void (*OverlapFunc)(register QRegionPrivate &dest, register const QRect *r1, const QRect *r1End,
+ register const QRect *r2, const QRect *r2End, register int y1, register int y2);
+typedef void (*NonOverlapFunc)(register QRegionPrivate &dest, register const QRect *r, const QRect *rEnd,
+ register int y1, register int y2);
+
+static bool EqualRegion(const QRegionPrivate *r1, const QRegionPrivate *r2);
+static void UnionRegion(const QRegionPrivate *reg1, const QRegionPrivate *reg2, QRegionPrivate &dest);
+static void miRegionOp(register QRegionPrivate &dest, const QRegionPrivate *reg1, const QRegionPrivate *reg2,
+ OverlapFunc overlapFunc, NonOverlapFunc nonOverlap1Func,
+ NonOverlapFunc nonOverlap2Func);
+
+#define RectangleOut 0
+#define RectangleIn 1
+#define RectanglePart 2
+#define EvenOddRule 0
+#define WindingRule 1
+
+// START OF region.h extract
+/* $XConsortium: region.h,v 11.14 94/04/17 20:22:20 rws 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.
+
+
+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.
+
+************************************************************************/
+
+#ifndef _XREGION_H
+#define _XREGION_H
+
+QT_BEGIN_INCLUDE_NAMESPACE
+#include <limits.h>
+QT_END_INCLUDE_NAMESPACE
+
+/* 1 if two BOXs overlap.
+ * 0 if two BOXs do not overlap.
+ * Remember, x2 and y2 are not in the region
+ */
+#define EXTENTCHECK(r1, r2) \
+ ((r1)->right() >= (r2)->left() && \
+ (r1)->left() <= (r2)->right() && \
+ (r1)->bottom() >= (r2)->top() && \
+ (r1)->top() <= (r2)->bottom())
+
+/*
+ * update region extents
+ */
+#define EXTENTS(r,idRect){\
+ if((r)->left() < (idRect)->extents.left())\
+ (idRect)->extents.setLeft((r)->left());\
+ if((r)->top() < (idRect)->extents.top())\
+ (idRect)->extents.setTop((r)->top());\
+ if((r)->right() > (idRect)->extents.right())\
+ (idRect)->extents.setRight((r)->right());\
+ if((r)->bottom() > (idRect)->extents.bottom())\
+ (idRect)->extents.setBottom((r)->bottom());\
+ }
+
+/*
+ * Check to see if there is enough memory in the present region.
+ */
+#define MEMCHECK(dest, rect, firstrect){\
+ if ((dest).numRects >= ((dest).rects.size()-1)){\
+ firstrect.resize(firstrect.size() * 2); \
+ (rect) = (firstrect).data() + (dest).numRects;\
+ }\
+ }
+
+
+/*
+ * number of points to buffer before sending them off
+ * to scanlines(): Must be an even number
+ */
+#define NUMPTSTOBUFFER 200
+
+/*
+ * used to allocate buffers for points and link
+ * the buffers together
+ */
+typedef struct _POINTBLOCK {
+ int data[NUMPTSTOBUFFER * sizeof(QPoint)];
+ QPoint *pts;
+ struct _POINTBLOCK *next;
+} POINTBLOCK;
+
+#endif
+// END OF region.h extract
+
+// START OF Region.c extract
+/* $XConsortium: Region.c /main/30 1996/10/22 14:21:24 kaleb $ */
+/************************************************************************
+
+Copyright (c) 1987, 1988 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, 1988 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.
+
+************************************************************************/
+/*
+ * The functions in this file implement the Region abstraction, similar to one
+ * used in the X11 sample server. A Region is simply an area, as the name
+ * implies, and is implemented as a "y-x-banded" array of rectangles. To
+ * explain: Each Region is made up of a certain number of rectangles sorted
+ * by y coordinate first, and then by x coordinate.
+ *
+ * Furthermore, the rectangles are banded such that every rectangle with a
+ * given upper-left y coordinate (y1) will have the same lower-right y
+ * coordinate (y2) and vice versa. If a rectangle has scanlines in a band, it
+ * will span the entire vertical distance of the band. This means that some
+ * areas that could be merged into a taller rectangle will be represented as
+ * several shorter rectangles to account for shorter rectangles to its left
+ * or right but within its "vertical scope".
+ *
+ * An added constraint on the rectangles is that they must cover as much
+ * horizontal area as possible. E.g. no two rectangles in a band are allowed
+ * to touch.
+ *
+ * Whenever possible, bands will be merged together to cover a greater vertical
+ * distance (and thus reduce the number of rectangles). Two bands can be merged
+ * only if the bottom of one touches the top of the other and they have
+ * rectangles in the same places (of the same width, of course). This maintains
+ * the y-x-banding that's so nice to have...
+ */
+/* $XFree86: xc/lib/X11/Region.c,v 1.1.1.2.2.2 1998/10/04 15:22:50 hohndel Exp $ */
+
+static void UnionRectWithRegion(register const QRect *rect, const QRegionPrivate *source,
+ QRegionPrivate &dest)
+{
+ if (rect->isEmpty())
+ return;
+
+ Q_ASSERT(EqualRegion(source, &dest));
+
+ if (dest.numRects == 0) {
+ dest = QRegionPrivate(*rect);
+ } else if (dest.canAppend(rect)) {
+ dest.append(rect);
+ } else {
+ QRegionPrivate p(*rect);
+ UnionRegion(&p, source, dest);
+ }
+}
+
+/*-
+ *-----------------------------------------------------------------------
+ * miSetExtents --
+ * Reset the extents and innerRect of a region to what they should be.
+ * Called by miSubtract and miIntersect b/c they can't figure it out
+ * along the way or do so easily, as miUnion can.
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * The region's 'extents' and 'innerRect' structure is overwritten.
+ *
+ *-----------------------------------------------------------------------
+ */
+static void miSetExtents(QRegionPrivate &dest)
+{
+ register const QRect *pBox,
+ *pBoxEnd;
+ register QRect *pExtents;
+
+ dest.innerRect.setCoords(0, 0, -1, -1);
+ dest.innerArea = -1;
+ if (dest.numRects == 0) {
+ dest.extents.setCoords(0, 0, -1, -1);
+ return;
+ }
+
+ pExtents = &dest.extents;
+ if (dest.rects.isEmpty())
+ pBox = &dest.extents;
+ else
+ pBox = dest.rects.constData();
+ pBoxEnd = pBox + dest.numRects - 1;
+
+ /*
+ * Since pBox is the first rectangle in the region, it must have the
+ * smallest y1 and since pBoxEnd is the last rectangle in the region,
+ * it must have the largest y2, because of banding. Initialize x1 and
+ * x2 from pBox and pBoxEnd, resp., as good things to initialize them
+ * to...
+ */
+ pExtents->setLeft(pBox->left());
+ pExtents->setTop(pBox->top());
+ pExtents->setRight(pBoxEnd->right());
+ pExtents->setBottom(pBoxEnd->bottom());
+
+ Q_ASSERT(pExtents->top() <= pExtents->bottom());
+ while (pBox <= pBoxEnd) {
+ if (pBox->left() < pExtents->left())
+ pExtents->setLeft(pBox->left());
+ if (pBox->right() > pExtents->right())
+ pExtents->setRight(pBox->right());
+ dest.updateInnerRect(*pBox);
+ ++pBox;
+ }
+ Q_ASSERT(pExtents->left() <= pExtents->right());
+}
+
+/* TranslateRegion(pRegion, x, y)
+ translates in place
+ added by raymond
+*/
+
+static void OffsetRegion(register QRegionPrivate &region, register int x, register int y)
+{
+ if (region.rects.size()) {
+ register QRect *pbox = region.rects.data();
+ register int nbox = region.numRects;
+
+ while (nbox--) {
+ pbox->translate(x, y);
+ ++pbox;
+ }
+ }
+ region.extents.translate(x, y);
+ region.innerRect.translate(x, y);
+}
+
+/*======================================================================
+ * Region Intersection
+ *====================================================================*/
+/*-
+ *-----------------------------------------------------------------------
+ * miIntersectO --
+ * Handle an overlapping band for miIntersect.
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * Rectangles may be added to the region.
+ *
+ *-----------------------------------------------------------------------
+ */
+static void miIntersectO(register QRegionPrivate &dest, register const QRect *r1, const QRect *r1End,
+ register const QRect *r2, const QRect *r2End, int y1, int y2)
+{
+ register int x1;
+ register int x2;
+ register QRect *pNextRect;
+
+ pNextRect = dest.rects.data() + dest.numRects;
+
+ while (r1 != r1End && r2 != r2End) {
+ x1 = qMax(r1->left(), r2->left());
+ x2 = qMin(r1->right(), r2->right());
+
+ /*
+ * If there's any overlap between the two rectangles, add that
+ * overlap to the new region.
+ * There's no need to check for subsumption because the only way
+ * such a need could arise is if some region has two rectangles
+ * right next to each other. Since that should never happen...
+ */
+ if (x1 <= x2) {
+ Q_ASSERT(y1 <= y2);
+ MEMCHECK(dest, pNextRect, dest.rects)
+ pNextRect->setCoords(x1, y1, x2, y2);
+ ++dest.numRects;
+ ++pNextRect;
+ }
+
+ /*
+ * Need to advance the pointers. Shift the one that extends
+ * to the right the least, since the other still has a chance to
+ * overlap with that region's next rectangle, if you see what I mean.
+ */
+ if (r1->right() < r2->right()) {
+ ++r1;
+ } else if (r2->right() < r1->right()) {
+ ++r2;
+ } else {
+ ++r1;
+ ++r2;
+ }
+ }
+}
+
+/*======================================================================
+ * Generic Region Operator
+ *====================================================================*/
+
+/*-
+ *-----------------------------------------------------------------------
+ * miCoalesce --
+ * Attempt to merge the boxes in the current band with those in the
+ * previous one. Used only by miRegionOp.
+ *
+ * Results:
+ * The new index for the previous band.
+ *
+ * Side Effects:
+ * If coalescing takes place:
+ * - rectangles in the previous band will have their y2 fields
+ * altered.
+ * - dest.numRects will be decreased.
+ *
+ *-----------------------------------------------------------------------
+ */
+static int miCoalesce(register QRegionPrivate &dest, int prevStart, int curStart)
+{
+ register QRect *pPrevBox; /* Current box in previous band */
+ register QRect *pCurBox; /* Current box in current band */
+ register QRect *pRegEnd; /* End of region */
+ int curNumRects; /* Number of rectangles in current band */
+ int prevNumRects; /* Number of rectangles in previous band */
+ int bandY1; /* Y1 coordinate for current band */
+ QRect *rData = dest.rects.data();
+
+ pRegEnd = rData + dest.numRects;
+
+ pPrevBox = rData + prevStart;
+ prevNumRects = curStart - prevStart;
+
+ /*
+ * Figure out how many rectangles are in the current band. Have to do
+ * this because multiple bands could have been added in miRegionOp
+ * at the end when one region has been exhausted.
+ */
+ pCurBox = rData + curStart;
+ bandY1 = pCurBox->top();
+ for (curNumRects = 0; pCurBox != pRegEnd && pCurBox->top() == bandY1; ++curNumRects) {
+ ++pCurBox;
+ }
+
+ if (pCurBox != pRegEnd) {
+ /*
+ * If more than one band was added, we have to find the start
+ * of the last band added so the next coalescing job can start
+ * at the right place... (given when multiple bands are added,
+ * this may be pointless -- see above).
+ */
+ --pRegEnd;
+ while ((pRegEnd - 1)->top() == pRegEnd->top())
+ --pRegEnd;
+ curStart = pRegEnd - rData;
+ pRegEnd = rData + dest.numRects;
+ }
+
+ if (curNumRects == prevNumRects && curNumRects != 0) {
+ pCurBox -= curNumRects;
+ /*
+ * The bands may only be coalesced if the bottom of the previous
+ * matches the top scanline of the current.
+ */
+ if (pPrevBox->bottom() == pCurBox->top() - 1) {
+ /*
+ * Make sure the bands have boxes in the same places. This
+ * assumes that boxes have been added in such a way that they
+ * cover the most area possible. I.e. two boxes in a band must
+ * have some horizontal space between them.
+ */
+ do {
+ if (pPrevBox->left() != pCurBox->left() || pPrevBox->right() != pCurBox->right()) {
+ // The bands don't line up so they can't be coalesced.
+ return curStart;
+ }
+ ++pPrevBox;
+ ++pCurBox;
+ --prevNumRects;
+ } while (prevNumRects != 0);
+
+ dest.numRects -= curNumRects;
+ pCurBox -= curNumRects;
+ pPrevBox -= curNumRects;
+
+ /*
+ * The bands may be merged, so set the bottom y of each box
+ * in the previous band to that of the corresponding box in
+ * the current band.
+ */
+ do {
+ pPrevBox->setBottom(pCurBox->bottom());
+ dest.updateInnerRect(*pPrevBox);
+ ++pPrevBox;
+ ++pCurBox;
+ curNumRects -= 1;
+ } while (curNumRects != 0);
+
+ /*
+ * If only one band was added to the region, we have to backup
+ * curStart to the start of the previous band.
+ *
+ * If more than one band was added to the region, copy the
+ * other bands down. The assumption here is that the other bands
+ * came from the same region as the current one and no further
+ * coalescing can be done on them since it's all been done
+ * already... curStart is already in the right place.
+ */
+ if (pCurBox == pRegEnd) {
+ curStart = prevStart;
+ } else {
+ do {
+ *pPrevBox++ = *pCurBox++;
+ dest.updateInnerRect(*pPrevBox);
+ } while (pCurBox != pRegEnd);
+ }
+ }
+ }
+ return curStart;
+}
+
+/*-
+ *-----------------------------------------------------------------------
+ * miRegionOp --
+ * Apply an operation to two regions. Called by miUnion, miInverse,
+ * miSubtract, miIntersect...
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * The new region is overwritten.
+ *
+ * Notes:
+ * The idea behind this function is to view the two regions as sets.
+ * Together they cover a rectangle of area that this function divides
+ * into horizontal bands where points are covered only by one region
+ * or by both. For the first case, the nonOverlapFunc is called with
+ * each the band and the band's upper and lower extents. For the
+ * second, the overlapFunc is called to process the entire band. It
+ * is responsible for clipping the rectangles in the band, though
+ * this function provides the boundaries.
+ * At the end of each band, the new region is coalesced, if possible,
+ * to reduce the number of rectangles in the region.
+ *
+ *-----------------------------------------------------------------------
+ */
+static void miRegionOp(register QRegionPrivate &dest,
+ const QRegionPrivate *reg1, const QRegionPrivate *reg2,
+ OverlapFunc overlapFunc, NonOverlapFunc nonOverlap1Func,
+ NonOverlapFunc nonOverlap2Func)
+{
+ register const QRect *r1; // Pointer into first region
+ register const QRect *r2; // Pointer into 2d region
+ const QRect *r1End; // End of 1st region
+ const QRect *r2End; // End of 2d region
+ register int ybot; // Bottom of intersection
+ register int ytop; // Top of intersection
+ int prevBand; // Index of start of previous band in dest
+ int curBand; // Index of start of current band in dest
+ register const QRect *r1BandEnd; // End of current band in r1
+ register const QRect *r2BandEnd; // End of current band in r2
+ int top; // Top of non-overlapping band
+ int bot; // Bottom of non-overlapping band
+
+ /*
+ * Initialization:
+ * set r1, r2, r1End and r2End appropriately, preserve the important
+ * parts of the destination region until the end in case it's one of
+ * the two source regions, then mark the "new" region empty, allocating
+ * another array of rectangles for it to use.
+ */
+ if (reg1->numRects == 1)
+ r1 = &reg1->extents;
+ else
+ r1 = reg1->rects.constData();
+ if (reg2->numRects == 1)
+ r2 = &reg2->extents;
+ else
+ r2 = reg2->rects.constData();
+
+ r1End = r1 + reg1->numRects;
+ r2End = r2 + reg2->numRects;
+
+ dest.vectorize();
+
+ QVector<QRect> oldRects = dest.rects;
+
+ dest.numRects = 0;
+
+ /*
+ * Allocate a reasonable number of rectangles for the new region. The idea
+ * is to allocate enough so the individual functions don't need to
+ * reallocate and copy the array, which is time consuming, yet we don't
+ * have to worry about using too much memory. I hope to be able to
+ * nuke the realloc() at the end of this function eventually.
+ */
+ dest.rects.resize(qMax(reg1->numRects,reg2->numRects) * 2);
+
+ /*
+ * Initialize ybot and ytop.
+ * In the upcoming loop, ybot and ytop serve different functions depending
+ * on whether the band being handled is an overlapping or non-overlapping
+ * band.
+ * In the case of a non-overlapping band (only one of the regions
+ * has points in the band), ybot is the bottom of the most recent
+ * intersection and thus clips the top of the rectangles in that band.
+ * ytop is the top of the next intersection between the two regions and
+ * serves to clip the bottom of the rectangles in the current band.
+ * For an overlapping band (where the two regions intersect), ytop clips
+ * the top of the rectangles of both regions and ybot clips the bottoms.
+ */
+ if (reg1->extents.top() < reg2->extents.top())
+ ybot = reg1->extents.top() - 1;
+ else
+ ybot = reg2->extents.top() - 1;
+
+ /*
+ * prevBand serves to mark the start of the previous band so rectangles
+ * can be coalesced into larger rectangles. qv. miCoalesce, above.
+ * In the beginning, there is no previous band, so prevBand == curBand
+ * (curBand is set later on, of course, but the first band will always
+ * start at index 0). prevBand and curBand must be indices because of
+ * the possible expansion, and resultant moving, of the new region's
+ * array of rectangles.
+ */
+ prevBand = 0;
+
+ do {
+ curBand = dest.numRects;
+
+ /*
+ * This algorithm proceeds one source-band (as opposed to a
+ * destination band, which is determined by where the two regions
+ * intersect) at a time. r1BandEnd and r2BandEnd serve to mark the
+ * rectangle after the last one in the current band for their
+ * respective regions.
+ */
+ r1BandEnd = r1;
+ while (r1BandEnd != r1End && r1BandEnd->top() == r1->top())
+ ++r1BandEnd;
+
+ r2BandEnd = r2;
+ while (r2BandEnd != r2End && r2BandEnd->top() == r2->top())
+ ++r2BandEnd;
+
+ /*
+ * First handle the band that doesn't intersect, if any.
+ *
+ * Note that attention is restricted to one band in the
+ * non-intersecting region at once, so if a region has n
+ * bands between the current position and the next place it overlaps
+ * the other, this entire loop will be passed through n times.
+ */
+ if (r1->top() < r2->top()) {
+ top = qMax(r1->top(), ybot + 1);
+ bot = qMin(r1->bottom(), r2->top() - 1);
+
+ if (nonOverlap1Func != 0 && bot >= top)
+ (*nonOverlap1Func)(dest, r1, r1BandEnd, top, bot);
+ ytop = r2->top();
+ } else if (r2->top() < r1->top()) {
+ top = qMax(r2->top(), ybot + 1);
+ bot = qMin(r2->bottom(), r1->top() - 1);
+
+ if (nonOverlap2Func != 0 && bot >= top)
+ (*nonOverlap2Func)(dest, r2, r2BandEnd, top, bot);
+ ytop = r1->top();
+ } else {
+ ytop = r1->top();
+ }
+
+ /*
+ * If any rectangles got added to the region, try and coalesce them
+ * with rectangles from the previous band. Note we could just do
+ * this test in miCoalesce, but some machines incur a not
+ * inconsiderable cost for function calls, so...
+ */
+ if (dest.numRects != curBand)
+ prevBand = miCoalesce(dest, prevBand, curBand);
+
+ /*
+ * Now see if we've hit an intersecting band. The two bands only
+ * intersect if ybot >= ytop
+ */
+ ybot = qMin(r1->bottom(), r2->bottom());
+ curBand = dest.numRects;
+ if (ybot >= ytop)
+ (*overlapFunc)(dest, r1, r1BandEnd, r2, r2BandEnd, ytop, ybot);
+
+ if (dest.numRects != curBand)
+ prevBand = miCoalesce(dest, prevBand, curBand);
+
+ /*
+ * If we've finished with a band (y2 == ybot) we skip forward
+ * in the region to the next band.
+ */
+ if (r1->bottom() == ybot)
+ r1 = r1BandEnd;
+ if (r2->bottom() == ybot)
+ r2 = r2BandEnd;
+ } while (r1 != r1End && r2 != r2End);
+
+ /*
+ * Deal with whichever region still has rectangles left.
+ */
+ curBand = dest.numRects;
+ if (r1 != r1End) {
+ if (nonOverlap1Func != 0) {
+ do {
+ r1BandEnd = r1;
+ while (r1BandEnd < r1End && r1BandEnd->top() == r1->top())
+ ++r1BandEnd;
+ (*nonOverlap1Func)(dest, r1, r1BandEnd, qMax(r1->top(), ybot + 1), r1->bottom());
+ r1 = r1BandEnd;
+ } while (r1 != r1End);
+ }
+ } else if ((r2 != r2End) && (nonOverlap2Func != 0)) {
+ do {
+ r2BandEnd = r2;
+ while (r2BandEnd < r2End && r2BandEnd->top() == r2->top())
+ ++r2BandEnd;
+ (*nonOverlap2Func)(dest, r2, r2BandEnd, qMax(r2->top(), ybot + 1), r2->bottom());
+ r2 = r2BandEnd;
+ } while (r2 != r2End);
+ }
+
+ if (dest.numRects != curBand)
+ (void)miCoalesce(dest, prevBand, curBand);
+
+ /*
+ * A bit of cleanup. To keep regions from growing without bound,
+ * we shrink the array of rectangles to match the new number of
+ * rectangles in the region.
+ *
+ * Only do this stuff if the number of rectangles allocated is more than
+ * twice the number of rectangles in the region (a simple optimization).
+ */
+ if (qMax(4, dest.numRects) < (dest.rects.size() >> 1))
+ dest.rects.resize(dest.numRects);
+}
+
+/*======================================================================
+ * Region Union
+ *====================================================================*/
+
+/*-
+ *-----------------------------------------------------------------------
+ * miUnionNonO --
+ * Handle a non-overlapping band for the union operation. Just
+ * Adds the rectangles into the region. Doesn't have to check for
+ * subsumption or anything.
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * dest.numRects is incremented and the final rectangles overwritten
+ * with the rectangles we're passed.
+ *
+ *-----------------------------------------------------------------------
+ */
+
+static void miUnionNonO(register QRegionPrivate &dest, register const QRect *r, const QRect *rEnd,
+ register int y1, register int y2)
+{
+ register QRect *pNextRect;
+
+ pNextRect = dest.rects.data() + dest.numRects;
+
+ Q_ASSERT(y1 <= y2);
+
+ while (r != rEnd) {
+ Q_ASSERT(r->left() <= r->right());
+ MEMCHECK(dest, pNextRect, dest.rects)
+ pNextRect->setCoords(r->left(), y1, r->right(), y2);
+ dest.numRects++;
+ ++pNextRect;
+ ++r;
+ }
+}
+
+
+/*-
+ *-----------------------------------------------------------------------
+ * miUnionO --
+ * Handle an overlapping band for the union operation. Picks the
+ * left-most rectangle each time and merges it into the region.
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * Rectangles are overwritten in dest.rects and dest.numRects will
+ * be changed.
+ *
+ *-----------------------------------------------------------------------
+ */
+
+static void miUnionO(register QRegionPrivate &dest, register const QRect *r1, const QRect *r1End,
+ register const QRect *r2, const QRect *r2End, register int y1, register int y2)
+{
+ register QRect *pNextRect;
+
+ pNextRect = dest.rects.data() + dest.numRects;
+
+#define MERGERECT(r) \
+ if ((dest.numRects != 0) && \
+ (pNextRect[-1].top() == y1) && \
+ (pNextRect[-1].bottom() == y2) && \
+ (pNextRect[-1].right() >= r->left()-1)) { \
+ if (pNextRect[-1].right() < r->right()) { \
+ pNextRect[-1].setRight(r->right()); \
+ dest.updateInnerRect(pNextRect[-1]); \
+ Q_ASSERT(pNextRect[-1].left() <= pNextRect[-1].right()); \
+ } \
+ } else { \
+ MEMCHECK(dest, pNextRect, dest.rects) \
+ pNextRect->setCoords(r->left(), y1, r->right(), y2); \
+ dest.updateInnerRect(*pNextRect); \
+ dest.numRects++; \
+ pNextRect++; \
+ } \
+ r++;
+
+ Q_ASSERT(y1 <= y2);
+ while (r1 != r1End && r2 != r2End) {
+ if (r1->left() < r2->left()) {
+ MERGERECT(r1)
+ } else {
+ MERGERECT(r2)
+ }
+ }
+
+ if (r1 != r1End) {
+ do {
+ MERGERECT(r1)
+ } while (r1 != r1End);
+ } else {
+ while (r2 != r2End) {
+ MERGERECT(r2)
+ }
+ }
+}
+
+static void UnionRegion(const QRegionPrivate *reg1, const QRegionPrivate *reg2, QRegionPrivate &dest)
+{
+ Q_ASSERT(!isEmptyHelper(reg1) && !isEmptyHelper(reg2));
+ Q_ASSERT(!reg1->contains(*reg2));
+ Q_ASSERT(!reg2->contains(*reg1));
+ Q_ASSERT(!EqualRegion(reg1, reg2));
+ Q_ASSERT(!reg1->canAppend(reg2));
+ Q_ASSERT(!reg2->canAppend(reg1));
+
+ if (reg1->innerArea > reg2->innerArea) {
+ dest.innerArea = reg1->innerArea;
+ dest.innerRect = reg1->innerRect;
+ } else {
+ dest.innerArea = reg2->innerArea;
+ dest.innerRect = reg2->innerRect;
+ }
+ miRegionOp(dest, reg1, reg2, miUnionO, miUnionNonO, miUnionNonO);
+
+ dest.extents.setCoords(qMin(reg1->extents.left(), reg2->extents.left()),
+ qMin(reg1->extents.top(), reg2->extents.top()),
+ qMax(reg1->extents.right(), reg2->extents.right()),
+ qMax(reg1->extents.bottom(), reg2->extents.bottom()));
+}
+
+/*======================================================================
+ * Region Subtraction
+ *====================================================================*/
+
+/*-
+ *-----------------------------------------------------------------------
+ * miSubtractNonO --
+ * Deal with non-overlapping band for subtraction. Any parts from
+ * region 2 we discard. Anything from region 1 we add to the region.
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * dest may be affected.
+ *
+ *-----------------------------------------------------------------------
+ */
+
+static void miSubtractNonO1(register QRegionPrivate &dest, register const QRect *r,
+ const QRect *rEnd, register int y1, register int y2)
+{
+ register QRect *pNextRect;
+
+ pNextRect = dest.rects.data() + dest.numRects;
+
+ Q_ASSERT(y1<=y2);
+
+ while (r != rEnd) {
+ Q_ASSERT(r->left() <= r->right());
+ MEMCHECK(dest, pNextRect, dest.rects)
+ pNextRect->setCoords(r->left(), y1, r->right(), y2);
+ ++dest.numRects;
+ ++pNextRect;
+ ++r;
+ }
+}
+
+/*-
+ *-----------------------------------------------------------------------
+ * miSubtractO --
+ * Overlapping band subtraction. x1 is the left-most point not yet
+ * checked.
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * dest may have rectangles added to it.
+ *
+ *-----------------------------------------------------------------------
+ */
+
+static void miSubtractO(register QRegionPrivate &dest, register const QRect *r1, const QRect *r1End,
+ register const QRect *r2, const QRect *r2End, register int y1, register int y2)
+{
+ register QRect *pNextRect;
+ register int x1;
+
+ x1 = r1->left();
+
+ Q_ASSERT(y1 <= y2);
+ pNextRect = dest.rects.data() + dest.numRects;
+
+ while (r1 != r1End && r2 != r2End) {
+ if (r2->right() < x1) {
+ /*
+ * Subtrahend missed the boat: go to next subtrahend.
+ */
+ ++r2;
+ } else if (r2->left() <= x1) {
+ /*
+ * Subtrahend precedes minuend: nuke left edge of minuend.
+ */
+ x1 = r2->right() + 1;
+ if (x1 > r1->right()) {
+ /*
+ * Minuend completely covered: advance to next minuend and
+ * reset left fence to edge of new minuend.
+ */
+ ++r1;
+ if (r1 != r1End)
+ x1 = r1->left();
+ } else {
+ // Subtrahend now used up since it doesn't extend beyond minuend
+ ++r2;
+ }
+ } else if (r2->left() <= r1->right()) {
+ /*
+ * Left part of subtrahend covers part of minuend: add uncovered
+ * part of minuend to region and skip to next subtrahend.
+ */
+ Q_ASSERT(x1 < r2->left());
+ MEMCHECK(dest, pNextRect, dest.rects)
+ pNextRect->setCoords(x1, y1, r2->left() - 1, y2);
+ ++dest.numRects;
+ ++pNextRect;
+
+ x1 = r2->right() + 1;
+ if (x1 > r1->right()) {
+ /*
+ * Minuend used up: advance to new...
+ */
+ ++r1;
+ if (r1 != r1End)
+ x1 = r1->left();
+ } else {
+ // Subtrahend used up
+ ++r2;
+ }
+ } else {
+ /*
+ * Minuend used up: add any remaining piece before advancing.
+ */
+ if (r1->right() >= x1) {
+ MEMCHECK(dest, pNextRect, dest.rects)
+ pNextRect->setCoords(x1, y1, r1->right(), y2);
+ ++dest.numRects;
+ ++pNextRect;
+ }
+ ++r1;
+ if (r1 != r1End)
+ x1 = r1->left();
+ }
+ }
+
+ /*
+ * Add remaining minuend rectangles to region.
+ */
+ while (r1 != r1End) {
+ Q_ASSERT(x1 <= r1->right());
+ MEMCHECK(dest, pNextRect, dest.rects)
+ pNextRect->setCoords(x1, y1, r1->right(), y2);
+ ++dest.numRects;
+ ++pNextRect;
+
+ ++r1;
+ if (r1 != r1End)
+ x1 = r1->left();
+ }
+}
+
+/*-
+ *-----------------------------------------------------------------------
+ * miSubtract --
+ * Subtract regS from regM and leave the result in regD.
+ * S stands for subtrahend, M for minuend and D for difference.
+ *
+ * Side Effects:
+ * regD is overwritten.
+ *
+ *-----------------------------------------------------------------------
+ */
+
+static void SubtractRegion(QRegionPrivate *regM, QRegionPrivate *regS,
+ register QRegionPrivate &dest)
+{
+ Q_ASSERT(!isEmptyHelper(regM));
+ Q_ASSERT(!isEmptyHelper(regS));
+ Q_ASSERT(EXTENTCHECK(&regM->extents, &regS->extents));
+ Q_ASSERT(!regS->contains(*regM));
+ Q_ASSERT(!EqualRegion(regM, regS));
+
+ miRegionOp(dest, regM, regS, miSubtractO, miSubtractNonO1, 0);
+
+ /*
+ * Can't alter dest's extents before we call miRegionOp because
+ * it might be one of the source regions and miRegionOp depends
+ * on the extents of those regions being the unaltered. Besides, this
+ * way there's no checking against rectangles that will be nuked
+ * due to coalescing, so we have to examine fewer rectangles.
+ */
+ miSetExtents(dest);
+}
+
+static void XorRegion(QRegionPrivate *sra, QRegionPrivate *srb, QRegionPrivate &dest)
+{
+ Q_ASSERT(!isEmptyHelper(sra) && !isEmptyHelper(srb));
+ Q_ASSERT(EXTENTCHECK(&sra->extents, &srb->extents));
+ Q_ASSERT(!EqualRegion(sra, srb));
+
+ QRegionPrivate tra, trb;
+
+ if (!srb->contains(*sra))
+ SubtractRegion(sra, srb, tra);
+ if (!sra->contains(*srb))
+ SubtractRegion(srb, sra, trb);
+
+ Q_ASSERT(isEmptyHelper(&trb) || !tra.contains(trb));
+ Q_ASSERT(isEmptyHelper(&tra) || !trb.contains(tra));
+
+ if (isEmptyHelper(&tra)) {
+ dest = trb;
+ } else if (isEmptyHelper(&trb)) {
+ dest = tra;
+ } else if (tra.canAppend(&trb)) {
+ dest = tra;
+ dest.append(&trb);
+ } else if (trb.canAppend(&tra)) {
+ dest = trb;
+ dest.append(&tra);
+ } else {
+ UnionRegion(&tra, &trb, dest);
+ }
+}
+
+/*
+ * Check to see if two regions are equal
+ */
+static bool EqualRegion(const QRegionPrivate *r1, const QRegionPrivate *r2)
+{
+ if (r1->numRects != r2->numRects) {
+ return false;
+ } else if (r1->numRects == 0) {
+ return true;
+ } else if (r1->extents != r2->extents) {
+ return false;
+ } else if (r1->numRects == 1 && r2->numRects == 1) {
+ return true; // equality tested in previous if-statement
+ } else {
+ const QRect *rr1 = (r1->numRects == 1) ? &r1->extents : r1->rects.constData();
+ const QRect *rr2 = (r2->numRects == 1) ? &r2->extents : r2->rects.constData();
+ for (int i = 0; i < r1->numRects; ++i, ++rr1, ++rr2) {
+ if (*rr1 != *rr2)
+ return false;
+ }
+ }
+
+ return true;
+}
+
+static bool PointInRegion(QRegionPrivate *pRegion, int x, int y)
+{
+ int i;
+
+ if (isEmptyHelper(pRegion))
+ return false;
+ if (!pRegion->extents.contains(x, y))
+ return false;
+ if (pRegion->numRects == 1)
+ return pRegion->extents.contains(x, y);
+ if (pRegion->innerRect.contains(x, y))
+ return true;
+ for (i = 0; i < pRegion->numRects; ++i) {
+ if (pRegion->rects[i].contains(x, y))
+ return true;
+ }
+ return false;
+}
+
+static bool RectInRegion(register QRegionPrivate *region, int rx, int ry, uint rwidth, uint rheight)
+{
+ register const QRect *pbox;
+ register const QRect *pboxEnd;
+ QRect rect(rx, ry, rwidth, rheight);
+ register QRect *prect = &rect;
+ int partIn, partOut;
+
+ if (!region || region->numRects == 0 || !EXTENTCHECK(&region->extents, prect))
+ return RectangleOut;
+
+ partOut = false;
+ partIn = false;
+
+ /* can stop when both partOut and partIn are true, or we reach prect->y2 */
+ pbox = (region->numRects == 1) ? &region->extents : region->rects.constData();
+ pboxEnd = pbox + region->numRects;
+ for (; pbox < pboxEnd; ++pbox) {
+ if (pbox->bottom() < ry)
+ continue;
+
+ if (pbox->top() > ry) {
+ partOut = true;
+ if (partIn || pbox->top() > prect->bottom())
+ break;
+ ry = pbox->top();
+ }
+
+ if (pbox->right() < rx)
+ continue; /* not far enough over yet */
+
+ if (pbox->left() > rx) {
+ partOut = true; /* missed part of rectangle to left */
+ if (partIn)
+ break;
+ }
+
+ if (pbox->left() <= prect->right()) {
+ partIn = true; /* definitely overlap */
+ if (partOut)
+ break;
+ }
+
+ if (pbox->right() >= prect->right()) {
+ ry = pbox->bottom() + 1; /* finished with this band */
+ if (ry > prect->bottom())
+ break;
+ rx = prect->left(); /* reset x out to left again */
+ } else {
+ /*
+ * Because boxes in a band are maximal width, if the first box
+ * to overlap the rectangle doesn't completely cover it in that
+ * band, the rectangle must be partially out, since some of it
+ * will be uncovered in that band. partIn will have been set true
+ * by now...
+ */
+ break;
+ }
+ }
+ return partIn ? ((ry <= prect->bottom()) ? RectanglePart : RectangleIn) : RectangleOut;
+}
+// END OF Region.c extract
+// START OF poly.h extract
+/* $XConsortium: poly.h,v 1.4 94/04/17 20:22:19 rws 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.
+
+
+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.
+
+************************************************************************/
+
+/*
+ * 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_axis; /* 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_axis, bres.d, \
+ bres.m, bres.m1, bres.incr1, bres.incr2)
+
+#define BRESINCRPGONSTRUCT(bres) \
+ BRESINCRPGON(bres.d, bres.minor_axis, bres.m, bres.m1, bres.incr1, bres.incr2)
+
+
+
+/*
+ * 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 counter-clockwise 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).
+ */
+
+/*
+ * for the winding number rule
+ */
+#define CLOCKWISE 1
+#define COUNTERCLOCKWISE -1
+
+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;
+
+
+
+/*
+ *
+ * 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; \
+ } \
+}
+// END OF poly.h extract
+// START OF PolyReg.c extract
+/* $XConsortium: PolyReg.c,v 11.23 94/11/17 21:59:37 converse 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.
+
+
+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.
+
+************************************************************************/
+/* $XFree86: xc/lib/X11/PolyReg.c,v 1.1.1.2.8.2 1998/10/04 15:22:49 hohndel Exp $ */
+
+#define LARGE_COORDINATE 1000000
+#define SMALL_COORDINATE -LARGE_COORDINATE
+
+/*
+ * 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 void InsertEdgeInET(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));
+ (*SLLBlock)->next = tmpSLLBlock;
+ tmpSLLBlock->next = (ScanLineListBlock *)NULL;
+ *SLLBlock = tmpSLLBlock;
+ *iSLLBlock = 0;
+ }
+ pSLL = &((*SLLBlock)->SLLs[(*iSLLBlock)++]);
+
+ pSLL->next = pPrevSLL->next;
+ pSLL->edgelist = (EdgeTableEntry *)NULL;
+ pPrevSLL->next = pSLL;
+ }
+ pSLL->scanline = scanline;
+
+ /*
+ * now insert the edge in the right bucket
+ */
+ prev = 0;
+ start = pSLL->edgelist;
+ while (start && (start->bres.minor_axis < ETE->bres.minor_axis)) {
+ prev = start;
+ start = start->next;
+ }
+ ETE->next = start;
+
+ if (prev)
+ prev->next = ETE;
+ else
+ pSLL->edgelist = ETE;
+}
+
+/*
+ * 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.
+ *
+ */
+
+static void CreateETandAET(register int count, register const QPoint *pts,
+ EdgeTable *ET, EdgeTableEntry *AET, register EdgeTableEntry *pETEs,
+ ScanLineListBlock *pSLLBlock)
+{
+ register const QPoint *top,
+ *bottom,
+ *PrevPt,
+ *CurrPt;
+ int iSLLBlock = 0;
+ int dy;
+
+ if (count < 2)
+ return;
+
+ /*
+ * initialize the Active Edge Table
+ */
+ AET->next = 0;
+ AET->back = 0;
+ AET->nextWETE = 0;
+ AET->bres.minor_axis = SMALL_COORDINATE;
+
+ /*
+ * initialize the Edge Table.
+ */
+ ET->scanlines.next = 0;
+ ET->ymax = SMALL_COORDINATE;
+ ET->ymin = LARGE_COORDINATE;
+ 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)
+
+ InsertEdgeInET(ET, pETEs, top->y(), &pSLLBlock, &iSLLBlock);
+
+ if (PrevPt->y() > ET->ymax)
+ ET->ymax = PrevPt->y();
+ if (PrevPt->y() < ET->ymin)
+ ET->ymin = PrevPt->y();
+ ++pETEs;
+ }
+
+ PrevPt = CurrPt;
+ }
+}
+
+/*
+ * loadAET
+ *
+ * This routine moves EdgeTableEntries from the
+ * EdgeTable into the Active Edge Table,
+ * leaving them sorted by smaller x coordinate.
+ *
+ */
+
+static void loadAET(register EdgeTableEntry *AET, register EdgeTableEntry *ETEs)
+{
+ register EdgeTableEntry *pPrevAET;
+ register EdgeTableEntry *tmp;
+
+ pPrevAET = AET;
+ AET = AET->next;
+ while (ETEs) {
+ while (AET && AET->bres.minor_axis < ETEs->bres.minor_axis) {
+ 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 computeWAET(register 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 InsertionSort(register 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_axis > AET->bres.minor_axis)
+ 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;
+}
+
+/*
+ * Clean up our act.
+ */
+static void FreeStorage(register ScanLineListBlock *pSLLBlock)
+{
+ register ScanLineListBlock *tmpSLLBlock;
+
+ while (pSLLBlock) {
+ tmpSLLBlock = pSLLBlock->next;
+ free(pSLLBlock);
+ pSLLBlock = tmpSLLBlock;
+ }
+}
+
+struct QRegionSpan {
+ QRegionSpan() {}
+ QRegionSpan(int x1_, int x2_) : x1(x1_), x2(x2_) {}
+
+ int x1;
+ int x2;
+ int width() const { return x2 - x1; }
+};
+
+Q_DECLARE_TYPEINFO(QRegionSpan, Q_PRIMITIVE_TYPE);
+
+static inline void flushRow(const QRegionSpan *spans, int y, int numSpans, QRegionPrivate *reg, int *lastRow, int *extendTo, bool *needsExtend)
+{
+ QRect *regRects = reg->rects.data() + *lastRow;
+ bool canExtend = reg->rects.size() - *lastRow == numSpans
+ && !(*needsExtend && *extendTo + 1 != y)
+ && (*needsExtend || regRects[0].y() + regRects[0].height() == y);
+
+ for (int i = 0; i < numSpans && canExtend; ++i) {
+ if (regRects[i].x() != spans[i].x1 || regRects[i].right() != spans[i].x2 - 1)
+ canExtend = false;
+ }
+
+ if (canExtend) {
+ *extendTo = y;
+ *needsExtend = true;
+ } else {
+ if (*needsExtend) {
+ for (int i = 0; i < reg->rects.size() - *lastRow; ++i)
+ regRects[i].setBottom(*extendTo);
+ }
+
+ *lastRow = reg->rects.size();
+ reg->rects.reserve(*lastRow + numSpans);
+ for (int i = 0; i < numSpans; ++i)
+ reg->rects << QRect(spans[i].x1, y, spans[i].width(), 1);
+
+ if (spans[0].x1 < reg->extents.left())
+ reg->extents.setLeft(spans[0].x1);
+
+ if (spans[numSpans-1].x2 - 1 > reg->extents.right())
+ reg->extents.setRight(spans[numSpans-1].x2 - 1);
+
+ *needsExtend = false;
+ }
+}
+
+/*
+ * Create an array of rectangles from a list of points.
+ * If indeed these things (POINTS, RECTS) are the same,
+ * then this proc is still needed, because it allocates
+ * storage for the array, which was allocated on the
+ * stack by the calling procedure.
+ *
+ */
+static void PtsToRegion(register int numFullPtBlocks, register int iCurPtBlock,
+ POINTBLOCK *FirstPtBlock, QRegionPrivate *reg)
+{
+ int lastRow = 0;
+ int extendTo = 0;
+ bool needsExtend = false;
+ QVarLengthArray<QRegionSpan> row;
+ int rowSize = 0;
+
+ reg->extents.setLeft(INT_MAX);
+ reg->extents.setRight(INT_MIN);
+ reg->innerArea = -1;
+
+ POINTBLOCK *CurPtBlock = FirstPtBlock;
+ for (; numFullPtBlocks >= 0; --numFullPtBlocks) {
+ /* the loop uses 2 points per iteration */
+ int i = NUMPTSTOBUFFER >> 1;
+ if (!numFullPtBlocks)
+ i = iCurPtBlock >> 1;
+ if(i) {
+ row.resize(qMax(row.size(), rowSize + i));
+ for (QPoint *pts = CurPtBlock->pts; i--; pts += 2) {
+ const int width = pts[1].x() - pts[0].x();
+ if (width) {
+ if (rowSize && row[rowSize-1].x2 == pts[0].x())
+ row[rowSize-1].x2 = pts[1].x();
+ else
+ row[rowSize++] = QRegionSpan(pts[0].x(), pts[1].x());
+ }
+
+ if (rowSize) {
+ QPoint *next = i ? &pts[2] : (numFullPtBlocks ? CurPtBlock->next->pts : 0);
+
+ if (!next || next->y() != pts[0].y()) {
+ flushRow(row.data(), pts[0].y(), rowSize, reg, &lastRow, &extendTo, &needsExtend);
+ rowSize = 0;
+ }
+ }
+ }
+ }
+ CurPtBlock = CurPtBlock->next;
+ }
+
+ if (needsExtend) {
+ for (int i = lastRow; i < reg->rects.size(); ++i)
+ reg->rects[i].setBottom(extendTo);
+ }
+
+ reg->numRects = reg->rects.size();
+
+ if (reg->numRects) {
+ reg->extents.setTop(reg->rects[0].top());
+ reg->extents.setBottom(reg->rects[lastRow].bottom());
+
+ for (int i = 0; i < reg->rects.size(); ++i)
+ reg->updateInnerRect(reg->rects[i]);
+ } else {
+ reg->extents.setCoords(0, 0, 0, 0);
+ }
+}
+
+/*
+ * polytoregion
+ *
+ * Scan converts a polygon by returning a run-length
+ * encoding of the resultant bitmap -- the run-length
+ * encoding is in the form of an array of rectangles.
+ */
+static QRegionPrivate *PolygonRegion(const QPoint *Pts, int Count, int rule)
+ //Point *Pts; /* the pts */
+ //int Count; /* number of pts */
+ //int rule; /* winding rule */
+{
+ QRegionPrivate *region;
+ register EdgeTableEntry *pAET; /* Active Edge Table */
+ register int y; /* current scanline */
+ register int iPts = 0; /* number of pts in buffer */
+ register EdgeTableEntry *pWETE; /* Winding Edge Table Entry*/
+ register ScanLineList *pSLL; /* current scanLineList */
+ register QPoint *pts; /* output buffer */
+ EdgeTableEntry *pPrevAET; /* ptr to previous AET */
+ EdgeTable ET; /* header node for ET */
+ EdgeTableEntry AET; /* header node for AET */
+ EdgeTableEntry *pETEs; /* EdgeTableEntries pool */
+ ScanLineListBlock SLLBlock; /* header for scanlinelist */
+ int fixWAET = false;
+ POINTBLOCK FirstPtBlock, *curPtBlock; /* PtBlock buffers */
+ FirstPtBlock.pts = reinterpret_cast<QPoint *>(FirstPtBlock.data);
+ POINTBLOCK *tmpPtBlock;
+ int numFullPtBlocks = 0;
+
+ if (!(region = new QRegionPrivate))
+ return 0;
+
+ /* special case a rectangle */
+ if (((Count == 4) ||
+ ((Count == 5) && (Pts[4].x() == Pts[0].x()) && (Pts[4].y() == Pts[0].y())))
+ && (((Pts[0].y() == Pts[1].y()) && (Pts[1].x() == Pts[2].x()) && (Pts[2].y() == Pts[3].y())
+ && (Pts[3].x() == Pts[0].x())) || ((Pts[0].x() == Pts[1].x())
+ && (Pts[1].y() == Pts[2].y()) && (Pts[2].x() == Pts[3].x())
+ && (Pts[3].y() == Pts[0].y())))) {
+ int x = qMin(Pts[0].x(), Pts[2].x());
+ region->extents.setLeft(x);
+ int y = qMin(Pts[0].y(), Pts[2].y());
+ region->extents.setTop(y);
+ region->extents.setWidth(qMax(Pts[0].x(), Pts[2].x()) - x);
+ region->extents.setHeight(qMax(Pts[0].y(), Pts[2].y()) - y);
+ if ((region->extents.left() <= region->extents.right()) &&
+ (region->extents.top() <= region->extents.bottom())) {
+ region->numRects = 1;
+ region->innerRect = region->extents;
+ region->innerArea = region->innerRect.width() * region->innerRect.height();
+ }
+ return region;
+ }
+
+ if (!(pETEs = static_cast<EdgeTableEntry *>(malloc(sizeof(EdgeTableEntry) * Count))))
+ return 0;
+
+ region->vectorize();
+
+ pts = FirstPtBlock.pts;
+ CreateETandAET(Count, Pts, &ET, &AET, pETEs, &SLLBlock);
+
+ pSLL = ET.scanlines.next;
+ curPtBlock = &FirstPtBlock;
+
+ // sanity check that the region won't become too big...
+ if (ET.ymax - ET.ymin > 100000) {
+ // clean up region ptr
+#ifndef QT_NO_DEBUG
+ qWarning("QRegion: creating region from big polygon failed...!");
+#endif
+ delete region;
+ return 0;
+ }
+
+
+ if (rule == EvenOddRule) {
+ /*
+ * for each scanline
+ */
+ for (y = ET.ymin; y < ET.ymax; ++y) {
+
+ /*
+ * Add a new edge to the active edge table when we
+ * get to the next edge.
+ */
+ if (pSLL && y == pSLL->scanline) {
+ loadAET(&AET, pSLL->edgelist);
+ pSLL = pSLL->next;
+ }
+ pPrevAET = &AET;
+ pAET = AET.next;
+
+ /*
+ * for each active edge
+ */
+ while (pAET) {
+ pts->setX(pAET->bres.minor_axis);
+ pts->setY(y);
+ ++pts;
+ ++iPts;
+
+ /*
+ * send out the buffer
+ */
+ if (iPts == NUMPTSTOBUFFER) {
+ tmpPtBlock = (POINTBLOCK *)malloc(sizeof(POINTBLOCK));
+ tmpPtBlock->pts = reinterpret_cast<QPoint *>(tmpPtBlock->data);
+ curPtBlock->next = tmpPtBlock;
+ curPtBlock = tmpPtBlock;
+ pts = curPtBlock->pts;
+ ++numFullPtBlocks;
+ iPts = 0;
+ }
+ EVALUATEEDGEEVENODD(pAET, pPrevAET, y)
+ }
+ InsertionSort(&AET);
+ }
+ } else {
+ /*
+ * for each scanline
+ */
+ for (y = ET.ymin; y < ET.ymax; ++y) {
+ /*
+ * Add a new edge to the active edge table when we
+ * get to the next edge.
+ */
+ if (pSLL && y == pSLL->scanline) {
+ loadAET(&AET, pSLL->edgelist);
+ computeWAET(&AET);
+ pSLL = pSLL->next;
+ }
+ pPrevAET = &AET;
+ pAET = AET.next;
+ pWETE = pAET;
+
+ /*
+ * for each active edge
+ */
+ while (pAET) {
+ /*
+ * add to the buffer only those edges that
+ * are in the Winding active edge table.
+ */
+ if (pWETE == pAET) {
+ pts->setX(pAET->bres.minor_axis);
+ pts->setY(y);
+ ++pts;
+ ++iPts;
+
+ /*
+ * send out the buffer
+ */
+ if (iPts == NUMPTSTOBUFFER) {
+ tmpPtBlock = static_cast<POINTBLOCK *>(malloc(sizeof(POINTBLOCK)));
+ tmpPtBlock->pts = reinterpret_cast<QPoint *>(tmpPtBlock->data);
+ curPtBlock->next = tmpPtBlock;
+ curPtBlock = tmpPtBlock;
+ pts = curPtBlock->pts;
+ ++numFullPtBlocks;
+ iPts = 0;
+ }
+ pWETE = pWETE->nextWETE;
+ }
+ EVALUATEEDGEWINDING(pAET, pPrevAET, y, fixWAET)
+ }
+
+ /*
+ * recompute the winding active edge table if
+ * we just resorted or have exited an edge.
+ */
+ if (InsertionSort(&AET) || fixWAET) {
+ computeWAET(&AET);
+ fixWAET = false;
+ }
+ }
+ }
+ FreeStorage(SLLBlock.next);
+ PtsToRegion(numFullPtBlocks, iPts, &FirstPtBlock, region);
+ for (curPtBlock = FirstPtBlock.next; --numFullPtBlocks >= 0;) {
+ tmpPtBlock = curPtBlock->next;
+ free(curPtBlock);
+ curPtBlock = tmpPtBlock;
+ }
+ free(pETEs);
+ return region;
+}
+// END OF PolyReg.c extract
+
+QRegionPrivate *qt_bitmapToRegion(const QBitmap& bitmap)
+{
+ QImage image = bitmap.toImage();
+
+ QRegionPrivate *region = new QRegionPrivate;
+
+ QRect xr;
+
+#define AddSpan \
+ { \
+ xr.setCoords(prev1, y, x-1, y); \
+ UnionRectWithRegion(&xr, region, *region); \
+ }
+
+ const uchar zero = 0;
+ bool little = image.format() == QImage::Format_MonoLSB;
+
+ int x,
+ y;
+ for (y = 0; y < image.height(); ++y) {
+ uchar *line = image.scanLine(y);
+ int w = image.width();
+ uchar all = zero;
+ int prev1 = -1;
+ for (x = 0; x < w;) {
+ uchar byte = line[x / 8];
+ if (x > w - 8 || byte!=all) {
+ if (little) {
+ for (int b = 8; b > 0 && x < w; --b) {
+ if (!(byte & 0x01) == !all) {
+ // More of the same
+ } else {
+ // A change.
+ if (all!=zero) {
+ AddSpan
+ all = zero;
+ } else {
+ prev1 = x;
+ all = ~zero;
+ }
+ }
+ byte >>= 1;
+ ++x;
+ }
+ } else {
+ for (int b = 8; b > 0 && x < w; --b) {
+ if (!(byte & 0x80) == !all) {
+ // More of the same
+ } else {
+ // A change.
+ if (all != zero) {
+ AddSpan
+ all = zero;
+ } else {
+ prev1 = x;
+ all = ~zero;
+ }
+ }
+ byte <<= 1;
+ ++x;
+ }
+ }
+ } else {
+ x += 8;
+ }
+ }
+ if (all != zero) {
+ AddSpan
+ }
+ }
+#undef AddSpan
+
+ return region;
+}
+
+QRegion::QRegion()
+ : d(&shared_empty)
+{
+ d->ref.ref();
+}
+
+QRegion::QRegion(const QRect &r, RegionType t)
+{
+ if (r.isEmpty()) {
+ d = &shared_empty;
+ d->ref.ref();
+ } else {
+ d = new QRegionData;
+ d->ref = 1;
+#if defined(Q_WS_X11)
+ d->rgn = 0;
+ d->xrectangles = 0;
+#elif defined(Q_OS_WINCE)
+ d->rgn = 0;
+#endif
+ if (t == Rectangle) {
+ d->qt_rgn = new QRegionPrivate(r);
+ } else if (t == Ellipse) {
+ QPainterPath path;
+ path.addEllipse(r.x(), r.y(), r.width(), r.height());
+ QPolygon a = path.toSubpathPolygons().at(0).toPolygon();
+ d->qt_rgn = PolygonRegion(a.constData(), a.size(), EvenOddRule);
+ }
+ }
+}
+
+QRegion::QRegion(const QPolygon &a, Qt::FillRule fillRule)
+{
+ if (a.count() > 2) {
+ d = new QRegionData;
+ d->ref = 1;
+#if defined(Q_WS_X11)
+ d->rgn = 0;
+ d->xrectangles = 0;
+#elif defined(Q_OS_WINCE)
+ d->rgn = 0;
+#endif
+ d->qt_rgn = PolygonRegion(a.constData(), a.size(),
+ fillRule == Qt::WindingFill ? WindingRule : EvenOddRule);
+ } else {
+ d = &shared_empty;
+ d->ref.ref();
+ }
+}
+
+
+QRegion::QRegion(const QRegion &r)
+{
+ d = r.d;
+ d->ref.ref();
+}
+
+
+QRegion::QRegion(const QBitmap &bm)
+{
+ if (bm.isNull()) {
+ d = &shared_empty;
+ d->ref.ref();
+ } else {
+ d = new QRegionData;
+ d->ref = 1;
+#if defined(Q_WS_X11)
+ d->rgn = 0;
+ d->xrectangles = 0;
+#elif defined(Q_OS_WINCE)
+ d->rgn = 0;
+#endif
+ d->qt_rgn = qt_bitmapToRegion(bm);
+ }
+}
+
+void QRegion::cleanUp(QRegion::QRegionData *x)
+{
+ delete x->qt_rgn;
+#if defined(Q_WS_X11)
+ if (x->rgn)
+ XDestroyRegion(x->rgn);
+ if (x->xrectangles)
+ free(x->xrectangles);
+#elif defined(Q_OS_WINCE)
+ if (x->rgn)
+ qt_win_dispose_rgn(x->rgn);
+#endif
+ delete x;
+}
+
+QRegion::~QRegion()
+{
+ if (!d->ref.deref())
+ cleanUp(d);
+}
+
+
+QRegion &QRegion::operator=(const QRegion &r)
+{
+ r.d->ref.ref();
+ if (!d->ref.deref())
+ cleanUp(d);
+ d = r.d;
+ return *this;
+}
+
+
+/*!
+ \internal
+*/
+
+QRegion QRegion::copy() const
+{
+ QRegion r;
+ QRegionData *x = new QRegionData;
+ x->ref = 1;
+#if defined(Q_WS_X11)
+ x->rgn = 0;
+ x->xrectangles = 0;
+#elif defined(Q_OS_WINCE)
+ x->rgn = 0;
+#endif
+ if (d->qt_rgn)
+ x->qt_rgn = new QRegionPrivate(*d->qt_rgn);
+ else
+ x->qt_rgn = new QRegionPrivate;
+ if (!r.d->ref.deref())
+ cleanUp(r.d);
+ r.d = x;
+ return r;
+}
+
+bool QRegion::isEmpty() const
+{
+ return d == &shared_empty || d->qt_rgn->numRects == 0;
+}
+
+
+bool QRegion::contains(const QPoint &p) const
+{
+ return PointInRegion(d->qt_rgn, p.x(), p.y());
+}
+
+bool QRegion::contains(const QRect &r) const
+{
+ return RectInRegion(d->qt_rgn, r.left(), r.top(), r.width(), r.height()) != RectangleOut;
+}
+
+
+
+void QRegion::translate(int dx, int dy)
+{
+ if ((dx == 0 && dy == 0) || isEmptyHelper(d->qt_rgn))
+ return;
+
+ detach();
+ OffsetRegion(*d->qt_rgn, dx, dy);
+}
+
+QRegion QRegion::unite(const QRegion &r) const
+{
+ if (isEmptyHelper(d->qt_rgn))
+ return r;
+ if (isEmptyHelper(r.d->qt_rgn))
+ return *this;
+ if (d == r.d)
+ return *this;
+
+ if (d->qt_rgn->contains(*r.d->qt_rgn)) {
+ return *this;
+ } else if (r.d->qt_rgn->contains(*d->qt_rgn)) {
+ return r;
+ } else if (d->qt_rgn->canAppend(r.d->qt_rgn)) {
+ QRegion result(*this);
+ result.detach();
+ result.d->qt_rgn->append(r.d->qt_rgn);
+ return result;
+ } else if (d->qt_rgn->canPrepend(r.d->qt_rgn)) {
+ QRegion result(*this);
+ result.detach();
+ result.d->qt_rgn->prepend(r.d->qt_rgn);
+ return result;
+ } else if (EqualRegion(d->qt_rgn, r.d->qt_rgn)) {
+ return *this;
+ } else {
+ QRegion result;
+ result.detach();
+ UnionRegion(d->qt_rgn, r.d->qt_rgn, *result.d->qt_rgn);
+ return result;
+ }
+}
+
+QRegion& QRegion::operator+=(const QRegion &r)
+{
+ if (isEmptyHelper(d->qt_rgn))
+ return *this = r;
+ if (isEmptyHelper(r.d->qt_rgn))
+ return *this;
+ if (d == r.d)
+ return *this;
+
+ if (d->qt_rgn->contains(*r.d->qt_rgn)) {
+ return *this;
+ } else if (r.d->qt_rgn->contains(*d->qt_rgn)) {
+ return *this = r;
+ } else if (d->qt_rgn->canAppend(r.d->qt_rgn)) {
+ detach();
+ d->qt_rgn->append(r.d->qt_rgn);
+ return *this;
+ } else if (d->qt_rgn->canPrepend(r.d->qt_rgn)) {
+ detach();
+ d->qt_rgn->prepend(r.d->qt_rgn);
+ return *this;
+ } else if (EqualRegion(d->qt_rgn, r.d->qt_rgn)) {
+ return *this;
+ } else {
+ detach();
+ UnionRegion(d->qt_rgn, r.d->qt_rgn, *d->qt_rgn);
+ return *this;
+ }
+}
+
+QRegion QRegion::unite(const QRect &r) const
+{
+ if (isEmptyHelper(d->qt_rgn))
+ return r;
+ if (r.isEmpty())
+ return *this;
+
+ if (d->qt_rgn->contains(r)) {
+ return *this;
+ } else if (d->qt_rgn->within(r)) {
+ return r;
+ } else if (d->qt_rgn->numRects == 1 && d->qt_rgn->extents == r) {
+ return *this;
+ } else if (d->qt_rgn->canAppend(&r)) {
+ QRegion result(*this);
+ result.detach();
+ result.d->qt_rgn->append(&r);
+ return result;
+ } else if (d->qt_rgn->canPrepend(&r)) {
+ QRegion result(*this);
+ result.detach();
+ result.d->qt_rgn->prepend(&r);
+ return result;
+ } else {
+ QRegion result;
+ result.detach();
+ QRegionPrivate rp(r);
+ UnionRegion(d->qt_rgn, &rp, *result.d->qt_rgn);
+ return result;
+ }
+}
+
+QRegion& QRegion::operator+=(const QRect &r)
+{
+ if (isEmptyHelper(d->qt_rgn))
+ return *this = r;
+ if (r.isEmpty())
+ return *this;
+
+ if (d->qt_rgn->contains(r)) {
+ return *this;
+ } else if (d->qt_rgn->within(r)) {
+ return *this = r;
+ } else if (d->qt_rgn->canAppend(&r)) {
+ detach();
+ d->qt_rgn->append(&r);
+ return *this;
+ } else if (d->qt_rgn->canPrepend(&r)) {
+ detach();
+ d->qt_rgn->prepend(&r);
+ return *this;
+ } else if (d->qt_rgn->numRects == 1 && d->qt_rgn->extents == r) {
+ return *this;
+ } else {
+ detach();
+ QRegionPrivate p(r);
+ UnionRegion(d->qt_rgn, &p, *d->qt_rgn);
+ return *this;
+ }
+}
+
+QRegion QRegion::intersect(const QRegion &r) const
+{
+ if (isEmptyHelper(d->qt_rgn) || isEmptyHelper(r.d->qt_rgn)
+ || !EXTENTCHECK(&d->qt_rgn->extents, &r.d->qt_rgn->extents))
+ return QRegion();
+
+ /* this is fully contained in r */
+ if (r.d->qt_rgn->contains(*d->qt_rgn))
+ return *this;
+
+ /* r is fully contained in this */
+ if (d->qt_rgn->contains(*r.d->qt_rgn))
+ return r;
+
+ if (r.d->qt_rgn->numRects == 1 && d->qt_rgn->numRects == 1) {
+ const QRect rect = qt_rect_intersect_normalized(r.d->qt_rgn->extents,
+ d->qt_rgn->extents);
+ return QRegion(rect);
+ } else if (r.d->qt_rgn->numRects == 1) {
+ QRegion result(*this);
+ result.detach();
+ result.d->qt_rgn->intersect(r.d->qt_rgn->extents);
+ return result;
+ } else if (d->qt_rgn->numRects == 1) {
+ QRegion result(r);
+ result.detach();
+ result.d->qt_rgn->intersect(d->qt_rgn->extents);
+ return result;
+ }
+
+ QRegion result;
+ result.detach();
+ miRegionOp(*result.d->qt_rgn, d->qt_rgn, r.d->qt_rgn, miIntersectO, 0, 0);
+
+ /*
+ * Can't alter dest's extents before we call miRegionOp because
+ * it might be one of the source regions and miRegionOp depends
+ * on the extents of those regions being the same. Besides, this
+ * way there's no checking against rectangles that will be nuked
+ * due to coalescing, so we have to examine fewer rectangles.
+ */
+ miSetExtents(*result.d->qt_rgn);
+ return result;
+}
+
+QRegion QRegion::intersect(const QRect &r) const
+{
+ if (isEmptyHelper(d->qt_rgn) || r.isEmpty()
+ || !EXTENTCHECK(&d->qt_rgn->extents, &r))
+ return QRegion();
+
+ /* this is fully contained in r */
+ if (d->qt_rgn->within(r))
+ return *this;
+
+ /* r is fully contained in this */
+ if (d->qt_rgn->contains(r))
+ return r;
+
+ if (d->qt_rgn->numRects == 1) {
+ const QRect rect = qt_rect_intersect_normalized(d->qt_rgn->extents,
+ r.normalized());
+ return QRegion(rect);
+ }
+
+ QRegion result(*this);
+ result.detach();
+ result.d->qt_rgn->intersect(r);
+ return result;
+}
+
+QRegion QRegion::subtract(const QRegion &r) const
+{
+ if (isEmptyHelper(d->qt_rgn) || isEmptyHelper(r.d->qt_rgn))
+ return *this;
+ if (r.d->qt_rgn->contains(*d->qt_rgn))
+ return QRegion();
+ if (!EXTENTCHECK(&d->qt_rgn->extents, &r.d->qt_rgn->extents))
+ return *this;
+ if (d == r.d || EqualRegion(d->qt_rgn, r.d->qt_rgn))
+ return QRegion();
+
+#ifdef QT_REGION_DEBUG
+ d->qt_rgn->selfTest();
+ r.d->qt_rgn->selfTest();
+#endif
+
+ QRegion result;
+ result.detach();
+ SubtractRegion(d->qt_rgn, r.d->qt_rgn, *result.d->qt_rgn);
+#ifdef QT_REGION_DEBUG
+ result.d->qt_rgn->selfTest();
+#endif
+ return result;
+}
+
+QRegion QRegion::eor(const QRegion &r) const
+{
+ if (isEmptyHelper(d->qt_rgn)) {
+ return r;
+ } else if (isEmptyHelper(r.d->qt_rgn)) {
+ return *this;
+ } else if (!EXTENTCHECK(&d->qt_rgn->extents, &r.d->qt_rgn->extents)) {
+ return (*this + r);
+ } else if (d == r.d || EqualRegion(d->qt_rgn, r.d->qt_rgn)) {
+ return QRegion();
+ } else {
+ QRegion result;
+ result.detach();
+ XorRegion(d->qt_rgn, r.d->qt_rgn, *result.d->qt_rgn);
+ return result;
+ }
+}
+
+QRect QRegion::boundingRect() const
+{
+ if (isEmpty())
+ return QRect();
+ return d->qt_rgn->extents;
+}
+
+/*! \internal
+ Returns true if \a rect is guaranteed to be fully contained in \a region.
+ A false return value does not guarantee the opposite.
+*/
+#ifdef Q_WS_QWS
+Q_GUI_EXPORT
+#endif
+bool qt_region_strictContains(const QRegion &region, const QRect &rect)
+{
+ if (isEmptyHelper(region.d->qt_rgn) || !rect.isValid())
+ return false;
+
+#if 0 // TEST_INNERRECT
+ static bool guard = false;
+ if (guard)
+ return false;
+ guard = true;
+ QRegion inner = region.d->qt_rgn->innerRect;
+ Q_ASSERT((inner - region).isEmpty());
+ guard = false;
+
+ int maxArea = 0;
+ for (int i = 0; i < region.d->qt_rgn->numRects; ++i) {
+ const QRect r = region.d->qt_rgn->rects.at(i);
+ if (r.width() * r.height() > maxArea)
+ maxArea = r.width() * r.height();
+ }
+
+ if (maxArea > region.d->qt_rgn->innerArea) {
+ qDebug() << "not largest rectangle" << region << region.d->qt_rgn->innerRect;
+ }
+ Q_ASSERT(maxArea <= region.d->qt_rgn->innerArea);
+#endif
+
+ const QRect r1 = region.d->qt_rgn->innerRect;
+ return (rect.left() >= r1.left() && rect.right() <= r1.right()
+ && rect.top() >= r1.top() && rect.bottom() <= r1.bottom());
+}
+
+QVector<QRect> QRegion::rects() const
+{
+ if (d->qt_rgn) {
+ d->qt_rgn->vectorize();
+ // hw: modify the vector size directly to avoid reallocation
+ d->qt_rgn->rects.d->size = d->qt_rgn->numRects;
+ return d->qt_rgn->rects;
+ } else {
+ return QVector<QRect>();
+ }
+}
+
+void QRegion::setRects(const QRect *rects, int num)
+{
+ *this = QRegion();
+ if (!rects || num == 0 || (num == 1 && rects->isEmpty()))
+ return;
+
+ detach();
+
+ d->qt_rgn->numRects = num;
+ if (num == 1) {
+ d->qt_rgn->extents = *rects;
+ d->qt_rgn->innerRect = *rects;
+ } else {
+ d->qt_rgn->rects.resize(num);
+
+ int left = INT_MAX,
+ right = INT_MIN,
+ top = INT_MAX,
+ bottom = INT_MIN;
+ for (int i = 0; i < num; ++i) {
+ const QRect &rect = rects[i];
+ d->qt_rgn->rects[i] = rect;
+ left = qMin(rect.left(), left);
+ right = qMax(rect.right(), right);
+ top = qMin(rect.top(), top);
+ bottom = qMax(rect.bottom(), bottom);
+ d->qt_rgn->updateInnerRect(rect);
+ }
+ d->qt_rgn->extents = QRect(QPoint(left, top), QPoint(right, bottom));
+ }
+}
+
+int QRegion::numRects() const
+{
+ return (d->qt_rgn ? d->qt_rgn->numRects : 0);
+}
+
+bool QRegion::operator==(const QRegion &r) const
+{
+ if (!d->qt_rgn)
+ return r.isEmpty();
+ if (!r.d->qt_rgn)
+ return isEmpty();
+
+ if (d == r.d)
+ return true;
+ else
+ return EqualRegion(d->qt_rgn, r.d->qt_rgn);
+}
+
+#endif
+QT_END_NAMESPACE
diff --git a/src/gui/painting/qregion.h b/src/gui/painting/qregion.h
new file mode 100644
index 0000000000..c09087761e
--- /dev/null
+++ b/src/gui/painting/qregion.h
@@ -0,0 +1,231 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QREGION_H
+#define QREGION_H
+
+#include <QtCore/qatomic.h>
+#include <QtCore/qrect.h>
+#include <QtGui/qwindowdefs.h>
+
+#ifndef QT_NO_DATASTREAM
+#include <QtCore/qdatastream.h>
+#endif
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+template <class T> class QVector;
+class QVariant;
+
+#if defined(Q_WS_QWS) || defined(Q_WS_X11) || defined(Q_WS_MAC) || defined(Q_OS_WINCE)
+struct QRegionPrivate;
+#endif
+
+class QBitmap;
+
+class Q_GUI_EXPORT QRegion
+{
+public:
+ enum RegionType { Rectangle, Ellipse };
+
+ QRegion();
+ QRegion(int x, int y, int w, int h, RegionType t = Rectangle);
+ QRegion(const QRect &r, RegionType t = Rectangle);
+ QRegion(const QPolygon &pa, Qt::FillRule fillRule = Qt::OddEvenFill);
+#ifdef QT3_SUPPORT
+ QT3_SUPPORT_CONSTRUCTOR QRegion(const QPolygon &pa, bool winding);
+#endif
+ QRegion(const QRegion &region);
+ QRegion(const QBitmap &bitmap);
+ ~QRegion();
+ QRegion &operator=(const QRegion &);
+
+#ifdef QT3_SUPPORT
+ inline QT3_SUPPORT bool isNull() const { return isEmpty(); }
+#endif
+ bool isEmpty() const;
+
+ bool contains(const QPoint &p) const;
+ bool contains(const QRect &r) const;
+
+ void translate(int dx, int dy);
+ inline void translate(const QPoint &p) { translate(p.x(), p.y()); }
+ QRegion translated(int dx, int dy) const;
+ inline QRegion translated(const QPoint &p) const { return translated(p.x(), p.y()); }
+
+ // ### Qt 5: make these four functions QT4_SUPPORT
+ QRegion unite(const QRegion &r) const;
+ QRegion unite(const QRect &r) const;
+ QRegion intersect(const QRegion &r) const;
+ QRegion intersect(const QRect &r) const;
+ QRegion subtract(const QRegion &r) const;
+ QRegion eor(const QRegion &r) const;
+
+ inline QRegion united(const QRegion &r) const { return unite(r); }
+ inline QRegion united(const QRect &r) const { return unite(r); }
+ inline QRegion intersected(const QRegion &r) const { return intersect(r); }
+ inline QRegion intersected(const QRect &r) const { return intersect(r); }
+ inline QRegion subtracted(const QRegion &r) const { return subtract(r); }
+ inline QRegion xored(const QRegion &r) const { return eor(r); }
+
+ bool intersects(const QRegion &r) const;
+ bool intersects(const QRect &r) const;
+
+ QRect boundingRect() const;
+ QVector<QRect> rects() const;
+ void setRects(const QRect *rect, int num);
+ int numRects() const;
+
+ const QRegion operator|(const QRegion &r) const;
+ const QRegion operator+(const QRegion &r) const;
+ const QRegion operator+(const QRect &r) const;
+ const QRegion operator&(const QRegion &r) const;
+ const QRegion operator&(const QRect &r) const;
+ const QRegion operator-(const QRegion &r) const;
+ const QRegion operator^(const QRegion &r) const;
+ QRegion& operator|=(const QRegion &r);
+ QRegion& operator+=(const QRegion &r);
+ QRegion& operator+=(const QRect &r);
+ QRegion& operator&=(const QRegion &r);
+ QRegion& operator&=(const QRect &r);
+ QRegion& operator-=(const QRegion &r);
+ QRegion& operator^=(const QRegion &r);
+
+ bool operator==(const QRegion &r) const;
+ inline bool operator!=(const QRegion &r) const { return !(operator==(r)); }
+ operator QVariant() const;
+
+#ifdef qdoc
+ Handle handle() const;
+#endif
+#ifndef qdoc
+#if defined(Q_WS_WIN)
+ inline HRGN handle() const { ensureHandle(); return d->rgn; }
+#elif defined(Q_WS_X11)
+ inline Region handle() const { if(!d->rgn) updateX11Region(); return d->rgn; }
+#elif defined(Q_WS_MAC)
+#if defined Q_WS_MAC32
+ RgnHandle toQDRgn() const;
+ static QRegion fromQDRgn(RgnHandle shape);
+#endif
+#ifdef QT_MAC_USE_COCOA
+ inline HIMutableShapeRef handle(bool unused = false) const
+ { Q_UNUSED(unused); return toHIMutableShape(); }
+#else
+ inline RgnHandle handle() const { return handle(false); }
+ inline RgnHandle handle(bool) const { return toQDRgn(); }
+#endif
+ HIMutableShapeRef toHIMutableShape() const;
+ static QRegion fromHIShapeRef(HIShapeRef shape);
+#elif defined(Q_WS_QWS)
+ inline void *handle() const { return d->qt_rgn; }
+#endif
+#endif
+
+#ifndef QT_NO_DATASTREAM
+ friend Q_GUI_EXPORT QDataStream &operator<<(QDataStream &, const QRegion &);
+ friend Q_GUI_EXPORT QDataStream &operator>>(QDataStream &, QRegion &);
+#endif
+private:
+ QRegion copy() const; // helper of detach.
+ void detach();
+#if defined(Q_WS_WIN)
+ void ensureHandle() const;
+ QRegion winCombine(const QRegion &r, int num) const;
+#elif defined(Q_WS_X11)
+ void updateX11Region() const;
+ void *clipRectangles(int &num) const;
+ friend void *qt_getClipRects(const QRegion &r, int &num);
+#elif defined(Q_WS_MAC)
+ static OSStatus shape2QRegionHelper(int inMessage, HIShapeRef inShape,
+ const CGRect *inRect, void *inRefcon);
+#endif
+ friend bool qt_region_strictContains(const QRegion &region,
+ const QRect &rect);
+ friend struct QRegionPrivate;
+
+#ifndef QT_NO_DATASTREAM
+ void exec(const QByteArray &ba, int ver = 0, QDataStream::ByteOrder byteOrder = QDataStream::BigEndian);
+#endif
+ struct QRegionData {
+ QBasicAtomicInt ref;
+#if defined(Q_WS_WIN)
+ HRGN rgn;
+#elif defined(Q_WS_X11)
+ Region rgn;
+ void *xrectangles;
+#elif defined(Q_WS_MAC) && !defined(QT_MAC_USE_COCOA)
+ mutable RgnHandle unused; // Here for binary compatability reasons. ### Qt 5 remove.
+#endif
+#if defined(Q_WS_QWS) || defined(Q_WS_X11) || defined(Q_WS_MAC) || defined(Q_OS_WINCE)
+ QRegionPrivate *qt_rgn;
+#endif
+ };
+#if defined(Q_WS_WIN)
+ friend class QETWidget;
+#endif
+ struct QRegionData *d;
+ static struct QRegionData shared_empty;
+ static void cleanUp(QRegionData *x);
+};
+
+/*****************************************************************************
+ QRegion stream functions
+ *****************************************************************************/
+
+#ifndef QT_NO_DATASTREAM
+Q_GUI_EXPORT QDataStream &operator<<(QDataStream &, const QRegion &);
+Q_GUI_EXPORT QDataStream &operator>>(QDataStream &, QRegion &);
+#endif
+
+#ifndef QT_NO_DEBUG_STREAM
+Q_GUI_EXPORT QDebug operator<<(QDebug, const QRegion &);
+#endif
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QREGION_H
diff --git a/src/gui/painting/qregion_mac.cpp b/src/gui/painting/qregion_mac.cpp
new file mode 100644
index 0000000000..f5c37d1f7f
--- /dev/null
+++ b/src/gui/painting/qregion_mac.cpp
@@ -0,0 +1,249 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <private/qt_mac_p.h>
+#include "qcoreapplication.h"
+#include <qlibrary.h>
+
+QT_BEGIN_NAMESPACE
+
+QRegion::QRegionData QRegion::shared_empty = { Q_BASIC_ATOMIC_INITIALIZER(1), 0 };
+
+#if defined(Q_WS_MAC32) && !defined(QT_MAC_USE_COCOA)
+#define RGN_CACHE_SIZE 200
+#ifdef RGN_CACHE_SIZE
+static bool rgncache_init = false;
+static int rgncache_used;
+static RgnHandle rgncache[RGN_CACHE_SIZE];
+static void qt_mac_cleanup_rgncache()
+{
+ rgncache_init = false;
+ for(int i = 0; i < RGN_CACHE_SIZE; ++i) {
+ if(rgncache[i]) {
+ --rgncache_used;
+ DisposeRgn(rgncache[i]);
+ rgncache[i] = 0;
+ }
+ }
+}
+#endif
+
+Q_GUI_EXPORT RgnHandle qt_mac_get_rgn()
+{
+#ifdef RGN_CACHE_SIZE
+ if(!rgncache_init) {
+ rgncache_used = 0;
+ rgncache_init = true;
+ for(int i = 0; i < RGN_CACHE_SIZE; ++i)
+ rgncache[i] = 0;
+ qAddPostRoutine(qt_mac_cleanup_rgncache);
+ } else if(rgncache_used) {
+ for(int i = 0; i < RGN_CACHE_SIZE; ++i) {
+ if(rgncache[i]) {
+ RgnHandle ret = rgncache[i];
+ SetEmptyRgn(ret);
+ rgncache[i] = 0;
+ --rgncache_used;
+ return ret;
+ }
+ }
+ }
+#endif
+ return NewRgn();
+}
+
+Q_GUI_EXPORT void qt_mac_dispose_rgn(RgnHandle r)
+{
+#ifdef RGN_CACHE_SIZE
+ if(rgncache_init && rgncache_used < RGN_CACHE_SIZE) {
+ for(int i = 0; i < RGN_CACHE_SIZE; ++i) {
+ if(!rgncache[i]) {
+ ++rgncache_used;
+ rgncache[i] = r;
+ return;
+ }
+ }
+ }
+#endif
+ DisposeRgn(r);
+}
+
+static OSStatus qt_mac_get_rgn_rect(UInt16 msg, RgnHandle, const Rect *rect, void *reg)
+{
+ if(msg == kQDRegionToRectsMsgParse) {
+ QRect rct(rect->left, rect->top, (rect->right - rect->left), (rect->bottom - rect->top));
+ if(!rct.isEmpty())
+ *((QRegion *)reg) += rct;
+ }
+ return noErr;
+}
+
+Q_GUI_EXPORT QRegion qt_mac_convert_mac_region(RgnHandle rgn)
+{
+ return QRegion::fromQDRgn(rgn);
+}
+
+QRegion QRegion::fromQDRgn(RgnHandle rgn)
+{
+ QRegion ret;
+ ret.detach();
+ OSStatus oss = QDRegionToRects(rgn, kQDParseRegionFromTopLeft, qt_mac_get_rgn_rect, (void *)&ret);
+ if(oss != noErr)
+ return QRegion();
+ return ret;
+}
+
+/*!
+ \internal
+ Create's a RegionHandle, it's the caller's responsibility to release.
+*/
+RgnHandle QRegion::toQDRgn() const
+{
+ RgnHandle rgnHandle = qt_mac_get_rgn();
+ if(d->qt_rgn && d->qt_rgn->numRects) {
+ RgnHandle tmp_rgn = qt_mac_get_rgn();
+ int n = d->qt_rgn->numRects;
+ const QRect *qt_r = (n == 1) ? &d->qt_rgn->extents : d->qt_rgn->rects.constData();
+ while (n--) {
+ SetRectRgn(tmp_rgn,
+ qMax(SHRT_MIN, qt_r->x()),
+ qMax(SHRT_MIN, qt_r->y()),
+ qMin(SHRT_MAX, qt_r->right() + 1),
+ qMin(SHRT_MAX, qt_r->bottom() + 1));
+ UnionRgn(rgnHandle, tmp_rgn, rgnHandle);
+ ++qt_r;
+ }
+ qt_mac_dispose_rgn(tmp_rgn);
+ }
+ return rgnHandle;
+}
+#endif
+
+#if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5)
+OSStatus QRegion::shape2QRegionHelper(int inMessage, HIShapeRef,
+ const CGRect *inRect, void *inRefcon)
+{
+ QRegion *region = static_cast<QRegion *>(inRefcon);
+ if (!region)
+ return paramErr;
+
+ switch (inMessage) {
+ case kHIShapeEnumerateRect:
+ *region += QRect(inRect->origin.x, inRect->origin.y,
+ inRect->size.width, inRect->size.height);
+ break;
+ case kHIShapeEnumerateInit:
+ // Assume the region is already setup correctly
+ case kHIShapeEnumerateTerminate:
+ default:
+ break;
+ }
+ return noErr;
+}
+#endif
+
+/*!
+ \internal
+ Create's a mutable shape, it's the caller's responsibility to release.
+ WARNING: this function clamps the coordinates to SHRT_MIN/MAX on 10.4 and below.
+*/
+HIMutableShapeRef QRegion::toHIMutableShape() const
+{
+ HIMutableShapeRef shape = HIShapeCreateMutable();
+#if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5)
+ if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_5) {
+ if (d->qt_rgn && d->qt_rgn->numRects) {
+ int n = d->qt_rgn->numRects;
+ const QRect *qt_r = (n == 1) ? &d->qt_rgn->extents : d->qt_rgn->rects.constData();
+ while (n--) {
+ CGRect cgRect = CGRectMake(qt_r->x(), qt_r->y(), qt_r->width(), qt_r->height());
+ HIShapeUnionWithRect(shape, &cgRect);
+ ++qt_r;
+ }
+ }
+ } else
+#endif
+ {
+#ifndef QT_MAC_USE_COCOA
+ QCFType<HIShapeRef> qdShape = HIShapeCreateWithQDRgn(QMacSmartQuickDrawRegion(toQDRgn()));
+ HIShapeUnion(qdShape, shape, shape);
+#endif
+ }
+ return shape;
+}
+
+#if !defined(Q_WS_MAC64) && !defined(QT_MAC_USE_COCOA)
+typedef OSStatus (*PtrHIShapeGetAsQDRgn)(HIShapeRef, RgnHandle);
+static PtrHIShapeGetAsQDRgn ptrHIShapeGetAsQDRgn = 0;
+#endif
+
+
+QRegion QRegion::fromHIShapeRef(HIShapeRef shape)
+{
+ QRegion returnRegion;
+ returnRegion.detach();
+ // Begin gratuitous #if-defery
+#if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5)
+# ifndef Q_WS_MAC64
+ if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_5) {
+# endif
+ HIShapeEnumerate(shape, kHIShapeParseFromTopLeft, shape2QRegionHelper, &returnRegion);
+# ifndef Q_WS_MAC64
+ } else
+# endif
+#endif
+ {
+#if !defined(Q_WS_MAC64) && !defined(QT_MAC_USE_COCOA)
+ if (ptrHIShapeGetAsQDRgn == 0) {
+ QLibrary library(QLatin1String("/System/Library/Frameworks/Carbon.framework/Carbon"));
+ library.setLoadHints(QLibrary::ExportExternalSymbolsHint);
+ ptrHIShapeGetAsQDRgn = reinterpret_cast<PtrHIShapeGetAsQDRgn>(library.resolve("HIShapeGetAsQDRgn"));
+ }
+ RgnHandle rgn = qt_mac_get_rgn();
+ ptrHIShapeGetAsQDRgn(shape, rgn);
+ returnRegion = QRegion::fromQDRgn(rgn);
+ qt_mac_dispose_rgn(rgn);
+#endif
+ }
+ return returnRegion;
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/painting/qregion_qws.cpp b/src/gui/painting/qregion_qws.cpp
new file mode 100644
index 0000000000..3aea9a7a00
--- /dev/null
+++ b/src/gui/painting/qregion_qws.cpp
@@ -0,0 +1,3183 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+// XXX - add appropriate friendship relationships
+#define private public
+#include "qregion.h"
+#undef private
+#include "qpainterpath.h"
+#include "qpolygon.h"
+#include "qbuffer.h"
+#include "qimage.h"
+#include <qdebug.h>
+#include "qbitmap.h"
+#include <stdlib.h>
+#include <qatomic.h>
+#include <qsemaphore.h>
+
+QT_BEGIN_NAMESPACE
+
+class QFastMutex
+{
+ QAtomicInt contenders;
+ QSemaphore semaphore;
+public:
+ inline QFastMutex()
+ : contenders(0), semaphore(0)
+ { }
+ inline void lock()
+ {
+ if (contenders.fetchAndAddAcquire(1) != 0) {
+ semaphore.acquire();
+ contenders.deref();
+ }
+ }
+ inline bool tryLock()
+ {
+ return contenders.testAndSetAcquire(0, 1);
+ }
+ inline void unlock()
+ {
+ if (!contenders.testAndSetRelease(1, 0))
+ semaphore.release();
+ }
+};
+
+
+/*
+ * 1 if r1 contains r2
+ * 0 if r1 does not completely contain r2
+ */
+#define CONTAINSCHECK(r1, r2) \
+ ((r2).left() >= (r1).left() && (r2).right() <= (r1).right() && \
+ (r2).top() >= (r1).top() && (r2).bottom() <= (r1).bottom())
+
+/*
+ * clip region
+ */
+struct QRegionPrivate : public QRegion::QRegionData {
+ enum { Single, Vector } mode;
+ int numRects;
+ QVector<QRect> rects;
+ QRect single;
+ QRect extents;
+ QRect innerRect;
+ union {
+ int innerArea;
+ QRegionPrivate *next;
+ };
+
+ inline void vector()
+ {
+ if(mode != Vector && numRects) {
+ if(rects.size() < 1) rects.resize(1);
+ rects[0] = single;
+ }
+ mode = Vector;
+ }
+
+ inline QRegionPrivate() : mode(Single), numRects(0), innerArea(-1) {}
+ inline QRegionPrivate(const QRect &r) : mode(Single) {
+ numRects = 1;
+// rects[0] = r;
+ single = r;
+ extents = r;
+ innerRect = r;
+ innerArea = r.width() * r.height();
+ }
+
+ inline QRegionPrivate(const QRegionPrivate &r) {
+ mode = r.mode;
+ rects = r.rects;
+ single = r.single;
+ numRects = r.numRects;
+ extents = r.extents;
+ innerRect = r.innerRect;
+ innerArea = r.innerArea;
+ }
+
+ inline QRegionPrivate &operator=(const QRegionPrivate &r) {
+ mode = r.mode;
+ rects = r.rects;
+ single = r.single;
+ numRects = r.numRects;
+ extents = r.extents;
+ innerRect = r.innerRect;
+ innerArea = r.innerArea;
+ return *this;
+ }
+
+ /*
+ * Returns true if r is guaranteed to be fully contained in this region.
+ * A false return value does not guarantee the opposite.
+ */
+ inline bool contains(const QRegionPrivate &r) const {
+ const QRect &r1 = innerRect;
+ const QRect &r2 = r.extents;
+ return CONTAINSCHECK(r1, r2);
+ }
+
+ inline void updateInnerRect(const QRect &rect) {
+ const int area = rect.width() * rect.height();
+ if (area > innerArea) {
+ innerArea = area;
+ innerRect = rect;
+ }
+ }
+
+ void append(const QRegionPrivate *r);
+ void prepend(const QRegionPrivate *r);
+ inline bool canAppend(const QRegionPrivate *r) const;
+ inline bool canPrepend(const QRegionPrivate *r) const;
+};
+
+static QRegionPrivate *qt_nextRegionPtr = 0;
+static QFastMutex qt_nextRegionLock;
+
+static QRegionPrivate *qt_allocRegionMemory()
+{
+ QRegionPrivate *rv = 0;
+ qt_nextRegionLock.lock();
+
+ if(qt_nextRegionPtr) {
+ rv = qt_nextRegionPtr;
+ qt_nextRegionPtr = rv->next;
+ } else {
+ qt_nextRegionPtr =
+ (QRegionPrivate *)malloc(256 * sizeof(QRegionPrivate));
+ for(int ii = 0; ii < 256; ++ii) {
+ if(ii == 255) {
+ qt_nextRegionPtr[ii].next = 0;
+ } else {
+ qt_nextRegionPtr[ii].next = &qt_nextRegionPtr[ii + 1];
+ }
+ }
+
+ rv = qt_nextRegionPtr;
+ qt_nextRegionPtr = rv->next;
+ }
+
+ qt_nextRegionLock.unlock();
+ return rv;
+}
+
+static void qt_freeRegionMemory(QRegionPrivate *rp)
+{
+ qt_nextRegionLock.lock();
+ rp->next = qt_nextRegionPtr;
+ qt_nextRegionPtr = rp;
+ qt_nextRegionLock.unlock();
+}
+
+static QRegionPrivate *qt_allocRegion()
+{
+ QRegionPrivate *mem = qt_allocRegionMemory();
+ return new (mem) QRegionPrivate;
+}
+
+static QRegionPrivate *qt_allocRegion(const QRect &r)
+{
+ QRegionPrivate *mem = qt_allocRegionMemory();
+ return new (mem) QRegionPrivate(r);
+}
+
+static QRegionPrivate *qt_allocRegion(const QRegionPrivate &r)
+{
+ QRegionPrivate *mem = qt_allocRegionMemory();
+ return new (mem) QRegionPrivate(r);
+}
+
+void qt_freeRegion(QRegionPrivate *rp)
+{
+ rp->~QRegionPrivate();
+ qt_freeRegionMemory(rp);
+// delete rp;
+}
+
+static inline bool isEmptyHelper(const QRegionPrivate *preg)
+{
+ return !preg || preg->numRects == 0;
+}
+
+void QRegionPrivate::append(const QRegionPrivate *r)
+{
+ Q_ASSERT(!isEmptyHelper(r));
+
+ vector();
+ QRect *destRect = rects.data() + numRects;
+ const QRect *srcRect = (r->mode==Vector)?r->rects.constData():&r->single;
+ int numAppend = r->numRects;
+
+ // test for merge in x direction
+ {
+ const QRect *rFirst = srcRect;
+ QRect *myLast = rects.data() + (numRects - 1);
+ if (rFirst->top() == myLast->top()
+ && rFirst->height() == myLast->height()
+ && rFirst->left() == (myLast->right() + 1))
+ {
+ myLast->setWidth(myLast->width() + rFirst->width());
+ updateInnerRect(*myLast);
+ ++srcRect;
+ --numAppend;
+ }
+ }
+
+ // append rectangles
+ const int newNumRects = numRects + numAppend;
+ if (newNumRects > rects.size()) {
+ rects.resize(newNumRects);
+ destRect = rects.data() + numRects;
+ }
+ memcpy(destRect, srcRect, numAppend * sizeof(QRect));
+
+ // update inner rectangle
+ if (innerArea < r->innerArea) {
+ innerArea = r->innerArea;
+ innerRect = r->innerRect;
+ }
+
+ // update extents
+ destRect = &extents;
+ srcRect = &r->extents;
+ extents.setCoords(qMin(destRect->left(), srcRect->left()),
+ qMin(destRect->top(), srcRect->top()),
+ qMax(destRect->right(), srcRect->right()),
+ qMax(destRect->bottom(), srcRect->bottom()));
+
+ numRects = newNumRects;
+}
+
+void QRegionPrivate::prepend(const QRegionPrivate *r)
+{
+#if 1
+ Q_UNUSED(r);
+#else
+ // XXX ak: does not respect vectorization of region
+
+ Q_ASSERT(!isEmpty(r));
+
+ // move existing rectangles
+ memmove(rects.data() + r->numRects, rects.constData(),
+ numRects * sizeof(QRect));
+
+ // prepend new rectangles
+ memcpy(rects.data(), r->rects.constData(), r->numRects * sizeof(QRect));
+
+ // update inner rectangle
+ if (innerArea < r->innerArea) {
+ innerArea = r->innerArea;
+ innerRect = r->innerRect;
+ }
+
+ // update extents
+ destRect = &extents;
+ srcRect = &r->extents;
+ extents.setCoords(qMin(destRect->left(), srcRect->left()),
+ qMin(destRect->top(), srcRect->top()),
+ qMax(destRect->right(), srcRect->right()),
+ qMax(destRect->bottom(), srcRect->bottom()));
+
+ numRects = newNumRects;
+#endif
+}
+
+bool QRegionPrivate::canAppend(const QRegionPrivate *r) const
+{
+ Q_ASSERT(!isEmptyHelper(r));
+
+ const QRect *rFirst = (r->mode==Vector)?r->rects.constData():&r->single;
+ const QRect *myLast = (mode==Vector)?(rects.constData() + (numRects - 1)):&single;
+ // XXX: possible improvements:
+ // - nFirst->top() == myLast->bottom() + 1, must possibly merge bands
+ if (rFirst->top() > (myLast->bottom() + 1)
+ || (rFirst->top() == myLast->top()
+ && rFirst->height() == myLast->height()
+ && rFirst->left() > myLast->right()))
+ {
+ return true;
+ }
+
+ return false;
+}
+
+bool QRegionPrivate::canPrepend(const QRegionPrivate *r) const
+{
+#if 1
+ Q_UNUSED(r);
+ return false;
+#else
+ return r->canAppend(this);
+#endif
+}
+
+#if defined(Q_WS_X11)
+QT_BEGIN_INCLUDE_NAMESPACE
+# include "qregion_x11.cpp"
+QT_END_INCLUDE_NAMESPACE
+#elif defined(Q_WS_MAC)
+QT_BEGIN_INCLUDE_NAMESPACE
+# include "qregion_mac.cpp"
+QT_END_INCLUDE_NAMESPACE
+#elif defined(Q_WS_QWS)
+static QRegionPrivate qrp;
+QRegion::QRegionData QRegion::shared_empty = {Q_BASIC_ATOMIC_INITIALIZER(1), &qrp};
+#endif
+
+typedef void (*OverlapFunc)(register QRegionPrivate &dest, register const QRect *r1, const QRect *r1End,
+ register const QRect *r2, const QRect *r2End, register int y1, register int y2);
+typedef void (*NonOverlapFunc)(register QRegionPrivate &dest, register const QRect *r, const QRect *rEnd,
+ register int y1, register int y2);
+
+static bool EqualRegion(const QRegionPrivate *r1, const QRegionPrivate *r2);
+static void UnionRegion(const QRegionPrivate *reg1, const QRegionPrivate *reg2, QRegionPrivate &dest);
+static void miRegionOp(register QRegionPrivate &dest, const QRegionPrivate *reg1, const QRegionPrivate *reg2,
+ OverlapFunc overlapFunc, NonOverlapFunc nonOverlap1Func,
+ NonOverlapFunc nonOverlap2Func);
+
+#define RectangleOut 0
+#define RectangleIn 1
+#define RectanglePart 2
+#define EvenOddRule 0
+#define WindingRule 1
+
+// START OF region.h extract
+/* $XConsortium: region.h,v 11.14 94/04/17 20:22:20 rws 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.
+
+
+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.
+
+************************************************************************/
+
+#ifndef _XREGION_H
+#define _XREGION_H
+
+QT_BEGIN_INCLUDE_NAMESPACE
+#include <limits.h>
+QT_END_INCLUDE_NAMESPACE
+
+/* 1 if two BOXs overlap.
+ * 0 if two BOXs do not overlap.
+ * Remember, x2 and y2 are not in the region
+ */
+#define EXTENTCHECK(r1, r2) \
+ ((r1)->right() >= (r2)->left() && \
+ (r1)->left() <= (r2)->right() && \
+ (r1)->bottom() >= (r2)->top() && \
+ (r1)->top() <= (r2)->bottom())
+
+/*
+ * update region extents
+ */
+#define EXTENTS(r,idRect){\
+ if((r)->left() < (idRect)->extents.left())\
+ (idRect)->extents.setLeft((r)->left());\
+ if((r)->top() < (idRect)->extents.top())\
+ (idRect)->extents.setTop((r)->top());\
+ if((r)->right() > (idRect)->extents.right())\
+ (idRect)->extents.setRight((r)->right());\
+ if((r)->bottom() > (idRect)->extents.bottom())\
+ (idRect)->extents.setBottom((r)->bottom());\
+ }
+
+/*
+ * Check to see if there is enough memory in the present region.
+ */
+#define MEMCHECK(dest, rect, firstrect){\
+ if ((dest).numRects >= ((dest).rects.size()-1)){\
+ firstrect.resize(firstrect.size() * 2); \
+ (rect) = (firstrect).data() + (dest).numRects;\
+ }\
+ }
+
+
+/*
+ * number of points to buffer before sending them off
+ * to scanlines(): Must be an even number
+ */
+#define NUMPTSTOBUFFER 200
+
+/*
+ * used to allocate buffers for points and link
+ * the buffers together
+ */
+typedef struct _POINTBLOCK {
+ QPoint pts[NUMPTSTOBUFFER];
+ struct _POINTBLOCK *next;
+} POINTBLOCK;
+
+#endif
+// END OF region.h extract
+
+// START OF Region.c extract
+/* $XConsortium: Region.c /main/30 1996/10/22 14:21:24 kaleb $ */
+/************************************************************************
+
+Copyright (c) 1987, 1988 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, 1988 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.
+
+************************************************************************/
+/*
+ * The functions in this file implement the Region abstraction, similar to one
+ * used in the X11 sample server. A Region is simply an area, as the name
+ * implies, and is implemented as a "y-x-banded" array of rectangles. To
+ * explain: Each Region is made up of a certain number of rectangles sorted
+ * by y coordinate first, and then by x coordinate.
+ *
+ * Furthermore, the rectangles are banded such that every rectangle with a
+ * given upper-left y coordinate (y1) will have the same lower-right y
+ * coordinate (y2) and vice versa. If a rectangle has scanlines in a band, it
+ * will span the entire vertical distance of the band. This means that some
+ * areas that could be merged into a taller rectangle will be represented as
+ * several shorter rectangles to account for shorter rectangles to its left
+ * or right but within its "vertical scope".
+ *
+ * An added constraint on the rectangles is that they must cover as much
+ * horizontal area as possible. E.g. no two rectangles in a band are allowed
+ * to touch.
+ *
+ * Whenever possible, bands will be merged together to cover a greater vertical
+ * distance (and thus reduce the number of rectangles). Two bands can be merged
+ * only if the bottom of one touches the top of the other and they have
+ * rectangles in the same places (of the same width, of course). This maintains
+ * the y-x-banding that's so nice to have...
+ */
+/* $XFree86: xc/lib/X11/Region.c,v 1.1.1.2.2.2 1998/10/04 15:22:50 hohndel Exp $ */
+
+static void UnionRectWithRegion(register const QRect *rect, const QRegionPrivate *source,
+ QRegionPrivate &dest)
+{
+ if (!rect->width() || !rect->height())
+ return;
+
+ QRegionPrivate region(*rect);
+
+ Q_ASSERT(EqualRegion(source, &dest));
+ Q_ASSERT(!isEmptyHelper(&region));
+
+ if (dest.numRects == 0)
+ dest = region;
+ else if (dest.canAppend(&region))
+ dest.append(&region);
+ else
+ UnionRegion(&region, source, dest);
+}
+
+/*-
+ *-----------------------------------------------------------------------
+ * miSetExtents --
+ * Reset the extents and innerRect of a region to what they should be.
+ * Called by miSubtract and miIntersect b/c they can't figure it out
+ * along the way or do so easily, as miUnion can.
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * The region's 'extents' and 'innerRect' structure is overwritten.
+ *
+ *-----------------------------------------------------------------------
+ */
+static void miSetExtents(QRegionPrivate &dest)
+{
+ register const QRect *pBox,
+ *pBoxEnd;
+ register QRect *pExtents;
+
+ dest.innerRect.setCoords(0, 0, -1, -1);
+ dest.innerArea = -1;
+ if (dest.numRects == 0) {
+ dest.extents.setCoords(0, 0, 0, 0);
+ return;
+ }
+
+ pExtents = &dest.extents;
+ pBox = (dest.mode==QRegionPrivate::Vector)?(dest.rects.constData()):(&dest.single);
+ pBoxEnd = (dest.mode==QRegionPrivate::Vector)?(&pBox[dest.numRects - 1]):(&dest.single);
+
+ /*
+ * Since pBox is the first rectangle in the region, it must have the
+ * smallest y1 and since pBoxEnd is the last rectangle in the region,
+ * it must have the largest y2, because of banding. Initialize x1 and
+ * x2 from pBox and pBoxEnd, resp., as good things to initialize them
+ * to...
+ */
+ pExtents->setLeft(pBox->left());
+ pExtents->setTop(pBox->top());
+ pExtents->setRight(pBoxEnd->right());
+ pExtents->setBottom(pBoxEnd->bottom());
+
+ Q_ASSERT(pExtents->top() <= pExtents->bottom());
+ while (pBox <= pBoxEnd) {
+ if (pBox->left() < pExtents->left())
+ pExtents->setLeft(pBox->left());
+ if (pBox->right() > pExtents->right())
+ pExtents->setRight(pBox->right());
+ dest.updateInnerRect(*pBox);
+ ++pBox;
+ }
+ Q_ASSERT(pExtents->left() <= pExtents->right());
+}
+
+/* TranslateRegion(pRegion, x, y)
+ translates in place
+ added by raymond
+*/
+
+static void OffsetRegion(register QRegionPrivate &region, register int x, register int y)
+{
+ register int nbox;
+ register QRect *pbox;
+
+ if(region.mode == QRegionPrivate::Single) {
+ region.single.translate(x, y);
+ } else {
+ pbox = region.rects.data();
+ nbox = region.numRects;
+
+ while (nbox--) {
+ pbox->translate(x, y);
+ ++pbox;
+ }
+ }
+ region.extents.translate(x, y);
+ region.innerRect.translate(x, y);
+}
+
+/*======================================================================
+ * Region Intersection
+ *====================================================================*/
+/*-
+ *-----------------------------------------------------------------------
+ * miIntersectO --
+ * Handle an overlapping band for miIntersect.
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * Rectangles may be added to the region.
+ *
+ *-----------------------------------------------------------------------
+ */
+static void miIntersectO(register QRegionPrivate &dest, register const QRect *r1, const QRect *r1End,
+ register const QRect *r2, const QRect *r2End, int y1, int y2)
+{
+ register int x1;
+ register int x2;
+ register QRect *pNextRect;
+
+ pNextRect = dest.rects.data() + dest.numRects;
+
+ while (r1 != r1End && r2 != r2End) {
+ x1 = qMax(r1->left(), r2->left());
+ x2 = qMin(r1->right(), r2->right());
+
+ /*
+ * If there's any overlap between the two rectangles, add that
+ * overlap to the new region.
+ * There's no need to check for subsumption because the only way
+ * such a need could arise is if some region has two rectangles
+ * right next to each other. Since that should never happen...
+ */
+ if (x1 <= x2) {
+ Q_ASSERT(y1 <= y2);
+ MEMCHECK(dest, pNextRect, dest.rects)
+ pNextRect->setCoords(x1, y1, x2, y2);
+ ++dest.numRects;
+ ++pNextRect;
+ }
+
+ /*
+ * Need to advance the pointers. Shift the one that extends
+ * to the right the least, since the other still has a chance to
+ * overlap with that region's next rectangle, if you see what I mean.
+ */
+ if (r1->right() < r2->right()) {
+ ++r1;
+ } else if (r2->right() < r1->right()) {
+ ++r2;
+ } else {
+ ++r1;
+ ++r2;
+ }
+ }
+}
+
+/*======================================================================
+ * Generic Region Operator
+ *====================================================================*/
+
+/*-
+ *-----------------------------------------------------------------------
+ * miCoalesce --
+ * Attempt to merge the boxes in the current band with those in the
+ * previous one. Used only by miRegionOp.
+ *
+ * Results:
+ * The new index for the previous band.
+ *
+ * Side Effects:
+ * If coalescing takes place:
+ * - rectangles in the previous band will have their y2 fields
+ * altered.
+ * - dest.numRects will be decreased.
+ *
+ *-----------------------------------------------------------------------
+ */
+static int miCoalesce(register QRegionPrivate &dest, int prevStart, int curStart)
+{
+ register QRect *pPrevBox; /* Current box in previous band */
+ register QRect *pCurBox; /* Current box in current band */
+ register QRect *pRegEnd; /* End of region */
+ int curNumRects; /* Number of rectangles in current band */
+ int prevNumRects; /* Number of rectangles in previous band */
+ int bandY1; /* Y1 coordinate for current band */
+ QRect *rData = dest.rects.data();
+
+ pRegEnd = rData + dest.numRects;
+
+ pPrevBox = rData + prevStart;
+ prevNumRects = curStart - prevStart;
+
+ /*
+ * Figure out how many rectangles are in the current band. Have to do
+ * this because multiple bands could have been added in miRegionOp
+ * at the end when one region has been exhausted.
+ */
+ pCurBox = rData + curStart;
+ bandY1 = pCurBox->top();
+ for (curNumRects = 0; pCurBox != pRegEnd && pCurBox->top() == bandY1; ++curNumRects) {
+ ++pCurBox;
+ }
+
+ if (pCurBox != pRegEnd) {
+ /*
+ * If more than one band was added, we have to find the start
+ * of the last band added so the next coalescing job can start
+ * at the right place... (given when multiple bands are added,
+ * this may be pointless -- see above).
+ */
+ --pRegEnd;
+ while ((pRegEnd - 1)->top() == pRegEnd->top())
+ --pRegEnd;
+ curStart = pRegEnd - rData;
+ pRegEnd = rData + dest.numRects;
+ }
+
+ if (curNumRects == prevNumRects && curNumRects != 0) {
+ pCurBox -= curNumRects;
+ /*
+ * The bands may only be coalesced if the bottom of the previous
+ * matches the top scanline of the current.
+ */
+ if (pPrevBox->bottom() == pCurBox->top() - 1) {
+ /*
+ * Make sure the bands have boxes in the same places. This
+ * assumes that boxes have been added in such a way that they
+ * cover the most area possible. I.e. two boxes in a band must
+ * have some horizontal space between them.
+ */
+ do {
+ if (pPrevBox->left() != pCurBox->left() || pPrevBox->right() != pCurBox->right()) {
+ // The bands don't line up so they can't be coalesced.
+ return curStart;
+ }
+ ++pPrevBox;
+ ++pCurBox;
+ --prevNumRects;
+ } while (prevNumRects != 0);
+
+ dest.numRects -= curNumRects;
+ pCurBox -= curNumRects;
+ pPrevBox -= curNumRects;
+
+ /*
+ * The bands may be merged, so set the bottom y of each box
+ * in the previous band to that of the corresponding box in
+ * the current band.
+ */
+ do {
+ pPrevBox->setBottom(pCurBox->bottom());
+ dest.updateInnerRect(*pPrevBox);
+ ++pPrevBox;
+ ++pCurBox;
+ curNumRects -= 1;
+ } while (curNumRects != 0);
+
+ /*
+ * If only one band was added to the region, we have to backup
+ * curStart to the start of the previous band.
+ *
+ * If more than one band was added to the region, copy the
+ * other bands down. The assumption here is that the other bands
+ * came from the same region as the current one and no further
+ * coalescing can be done on them since it's all been done
+ * already... curStart is already in the right place.
+ */
+ if (pCurBox == pRegEnd) {
+ curStart = prevStart;
+ } else {
+ do {
+ *pPrevBox++ = *pCurBox++;
+ dest.updateInnerRect(*pPrevBox);
+ } while (pCurBox != pRegEnd);
+ }
+ }
+ }
+ return curStart;
+}
+
+/*-
+ *-----------------------------------------------------------------------
+ * miRegionOp --
+ * Apply an operation to two regions. Called by miUnion, miInverse,
+ * miSubtract, miIntersect...
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * The new region is overwritten.
+ *
+ * Notes:
+ * The idea behind this function is to view the two regions as sets.
+ * Together they cover a rectangle of area that this function divides
+ * into horizontal bands where points are covered only by one region
+ * or by both. For the first case, the nonOverlapFunc is called with
+ * each the band and the band's upper and lower extents. For the
+ * second, the overlapFunc is called to process the entire band. It
+ * is responsible for clipping the rectangles in the band, though
+ * this function provides the boundaries.
+ * At the end of each band, the new region is coalesced, if possible,
+ * to reduce the number of rectangles in the region.
+ *
+ *-----------------------------------------------------------------------
+ */
+static void miRegionOp(register QRegionPrivate &dest, const QRegionPrivate *reg1, const QRegionPrivate *reg2,
+ OverlapFunc overlapFunc, NonOverlapFunc nonOverlap1Func,
+ NonOverlapFunc nonOverlap2Func)
+{
+ register const QRect *r1; // Pointer into first region
+ register const QRect *r2; // Pointer into 2d region
+ const QRect *r1End; // End of 1st region
+ const QRect *r2End; // End of 2d region
+ register int ybot; // Bottom of intersection
+ register int ytop; // Top of intersection
+ int prevBand; // Index of start of previous band in dest
+ int curBand; // Index of start of current band in dest
+ register const QRect *r1BandEnd; // End of current band in r1
+ register const QRect *r2BandEnd; // End of current band in r2
+ int top; // Top of non-overlapping band
+ int bot; // Bottom of non-overlapping band
+
+ /*
+ * Initialization:
+ * set r1, r2, r1End and r2End appropriately, preserve the important
+ * parts of the destination region until the end in case it's one of
+ * the two source regions, then mark the "new" region empty, allocating
+ * another array of rectangles for it to use.
+ */
+ r1 = (reg1->mode==QRegionPrivate::Vector)?reg1->rects.data():&reg1->single;
+ r2 = (reg2->mode==QRegionPrivate::Vector)?reg2->rects.data():&reg2->single;
+ r1End = r1 + reg1->numRects;
+ r2End = r2 + reg2->numRects;
+
+ dest.vector();
+ QVector<QRect> oldRects = dest.rects;
+
+ dest.numRects = 0;
+
+ /*
+ * Allocate a reasonable number of rectangles for the new region. The idea
+ * is to allocate enough so the individual functions don't need to
+ * reallocate and copy the array, which is time consuming, yet we don't
+ * have to worry about using too much memory. I hope to be able to
+ * nuke the realloc() at the end of this function eventually.
+ */
+ dest.rects.resize(qMax(reg1->numRects,reg2->numRects) * 2);
+
+ /*
+ * Initialize ybot and ytop.
+ * In the upcoming loop, ybot and ytop serve different functions depending
+ * on whether the band being handled is an overlapping or non-overlapping
+ * band.
+ * In the case of a non-overlapping band (only one of the regions
+ * has points in the band), ybot is the bottom of the most recent
+ * intersection and thus clips the top of the rectangles in that band.
+ * ytop is the top of the next intersection between the two regions and
+ * serves to clip the bottom of the rectangles in the current band.
+ * For an overlapping band (where the two regions intersect), ytop clips
+ * the top of the rectangles of both regions and ybot clips the bottoms.
+ */
+ if (reg1->extents.top() < reg2->extents.top())
+ ybot = reg1->extents.top() - 1;
+ else
+ ybot = reg2->extents.top() - 1;
+
+ /*
+ * prevBand serves to mark the start of the previous band so rectangles
+ * can be coalesced into larger rectangles. qv. miCoalesce, above.
+ * In the beginning, there is no previous band, so prevBand == curBand
+ * (curBand is set later on, of course, but the first band will always
+ * start at index 0). prevBand and curBand must be indices because of
+ * the possible expansion, and resultant moving, of the new region's
+ * array of rectangles.
+ */
+ prevBand = 0;
+
+ do {
+ curBand = dest.numRects;
+
+ /*
+ * This algorithm proceeds one source-band (as opposed to a
+ * destination band, which is determined by where the two regions
+ * intersect) at a time. r1BandEnd and r2BandEnd serve to mark the
+ * rectangle after the last one in the current band for their
+ * respective regions.
+ */
+ r1BandEnd = r1;
+ while (r1BandEnd != r1End && r1BandEnd->top() == r1->top())
+ ++r1BandEnd;
+
+ r2BandEnd = r2;
+ while (r2BandEnd != r2End && r2BandEnd->top() == r2->top())
+ ++r2BandEnd;
+
+ /*
+ * First handle the band that doesn't intersect, if any.
+ *
+ * Note that attention is restricted to one band in the
+ * non-intersecting region at once, so if a region has n
+ * bands between the current position and the next place it overlaps
+ * the other, this entire loop will be passed through n times.
+ */
+ if (r1->top() < r2->top()) {
+ top = qMax(r1->top(), ybot + 1);
+ bot = qMin(r1->bottom(), r2->top() - 1);
+
+ if (nonOverlap1Func != 0 && bot >= top)
+ (*nonOverlap1Func)(dest, r1, r1BandEnd, top, bot);
+ ytop = r2->top();
+ } else if (r2->top() < r1->top()) {
+ top = qMax(r2->top(), ybot + 1);
+ bot = qMin(r2->bottom(), r1->top() - 1);
+
+ if (nonOverlap2Func != 0 && bot >= top)
+ (*nonOverlap2Func)(dest, r2, r2BandEnd, top, bot);
+ ytop = r1->top();
+ } else {
+ ytop = r1->top();
+ }
+
+ /*
+ * If any rectangles got added to the region, try and coalesce them
+ * with rectangles from the previous band. Note we could just do
+ * this test in miCoalesce, but some machines incur a not
+ * inconsiderable cost for function calls, so...
+ */
+ if (dest.numRects != curBand)
+ prevBand = miCoalesce(dest, prevBand, curBand);
+
+ /*
+ * Now see if we've hit an intersecting band. The two bands only
+ * intersect if ybot >= ytop
+ */
+ ybot = qMin(r1->bottom(), r2->bottom());
+ curBand = dest.numRects;
+ if (ybot >= ytop)
+ (*overlapFunc)(dest, r1, r1BandEnd, r2, r2BandEnd, ytop, ybot);
+
+ if (dest.numRects != curBand)
+ prevBand = miCoalesce(dest, prevBand, curBand);
+
+ /*
+ * If we've finished with a band (y2 == ybot) we skip forward
+ * in the region to the next band.
+ */
+ if (r1->bottom() == ybot)
+ r1 = r1BandEnd;
+ if (r2->bottom() == ybot)
+ r2 = r2BandEnd;
+ } while (r1 != r1End && r2 != r2End);
+
+ /*
+ * Deal with whichever region still has rectangles left.
+ */
+ curBand = dest.numRects;
+ if (r1 != r1End) {
+ if (nonOverlap1Func != 0) {
+ do {
+ r1BandEnd = r1;
+ while (r1BandEnd < r1End && r1BandEnd->top() == r1->top())
+ ++r1BandEnd;
+ (*nonOverlap1Func)(dest, r1, r1BandEnd, qMax(r1->top(), ybot + 1), r1->bottom());
+ r1 = r1BandEnd;
+ } while (r1 != r1End);
+ }
+ } else if ((r2 != r2End) && (nonOverlap2Func != 0)) {
+ do {
+ r2BandEnd = r2;
+ while (r2BandEnd < r2End && r2BandEnd->top() == r2->top())
+ ++r2BandEnd;
+ (*nonOverlap2Func)(dest, r2, r2BandEnd, qMax(r2->top(), ybot + 1), r2->bottom());
+ r2 = r2BandEnd;
+ } while (r2 != r2End);
+ }
+
+ if (dest.numRects != curBand)
+ (void)miCoalesce(dest, prevBand, curBand);
+
+ /*
+ * A bit of cleanup. To keep regions from growing without bound,
+ * we shrink the array of rectangles to match the new number of
+ * rectangles in the region.
+ *
+ * Only do this stuff if the number of rectangles allocated is more than
+ * twice the number of rectangles in the region (a simple optimization).
+ */
+ if (qMax(4, dest.numRects) < (dest.rects.size() >> 1))
+ dest.rects.resize(dest.numRects);
+}
+
+/*======================================================================
+ * Region Union
+ *====================================================================*/
+
+/*-
+ *-----------------------------------------------------------------------
+ * miUnionNonO --
+ * Handle a non-overlapping band for the union operation. Just
+ * Adds the rectangles into the region. Doesn't have to check for
+ * subsumption or anything.
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * dest.numRects is incremented and the final rectangles overwritten
+ * with the rectangles we're passed.
+ *
+ *-----------------------------------------------------------------------
+ */
+
+static void miUnionNonO(register QRegionPrivate &dest, register const QRect *r, const QRect *rEnd,
+ register int y1, register int y2)
+{
+ register QRect *pNextRect;
+
+ pNextRect = dest.rects.data() + dest.numRects;
+
+ Q_ASSERT(y1 <= y2);
+
+ while (r != rEnd) {
+ Q_ASSERT(r->left() <= r->right());
+ MEMCHECK(dest, pNextRect, dest.rects)
+ pNextRect->setCoords(r->left(), y1, r->right(), y2);
+ dest.numRects++;
+ ++pNextRect;
+ ++r;
+ }
+}
+
+
+/*-
+ *-----------------------------------------------------------------------
+ * miUnionO --
+ * Handle an overlapping band for the union operation. Picks the
+ * left-most rectangle each time and merges it into the region.
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * Rectangles are overwritten in dest.rects and dest.numRects will
+ * be changed.
+ *
+ *-----------------------------------------------------------------------
+ */
+
+static void miUnionO(register QRegionPrivate &dest, register const QRect *r1, const QRect *r1End,
+ register const QRect *r2, const QRect *r2End, register int y1, register int y2)
+{
+ register QRect *pNextRect;
+
+ pNextRect = dest.rects.data() + dest.numRects;
+
+#define MERGERECT(r) \
+ if ((dest.numRects != 0) && \
+ (pNextRect[-1].top() == y1) && \
+ (pNextRect[-1].bottom() == y2) && \
+ (pNextRect[-1].right() >= r->left()-1)) { \
+ if (pNextRect[-1].right() < r->right()) { \
+ pNextRect[-1].setRight(r->right()); \
+ dest.updateInnerRect(pNextRect[-1]); \
+ Q_ASSERT(pNextRect[-1].left() <= pNextRect[-1].right()); \
+ } \
+ } else { \
+ MEMCHECK(dest, pNextRect, dest.rects) \
+ pNextRect->setCoords(r->left(), y1, r->right(), y2); \
+ dest.updateInnerRect(*pNextRect); \
+ dest.numRects++; \
+ pNextRect++; \
+ } \
+ r++;
+
+ Q_ASSERT(y1 <= y2);
+ while (r1 != r1End && r2 != r2End) {
+ if (r1->left() < r2->left()) {
+ MERGERECT(r1)
+ } else {
+ MERGERECT(r2)
+ }
+ }
+
+ if (r1 != r1End) {
+ do {
+ MERGERECT(r1)
+ } while (r1 != r1End);
+ } else {
+ while (r2 != r2End) {
+ MERGERECT(r2)
+ }
+ }
+}
+
+static void UnionRegion(const QRegionPrivate *reg1, const QRegionPrivate *reg2, QRegionPrivate &dest)
+{
+ Q_ASSERT(!isEmptyHelper(reg1) && !isEmptyHelper(reg2));
+ Q_ASSERT(!reg1->contains(*reg2));
+ Q_ASSERT(!reg2->contains(*reg1));
+ Q_ASSERT(!EqualRegion(reg1, reg2));
+ Q_ASSERT(!reg1->canAppend(reg2));
+ Q_ASSERT(!reg2->canAppend(reg1));
+
+ if (reg1->innerArea > reg2->innerArea) {
+ dest.innerArea = reg1->innerArea;
+ dest.innerRect = reg1->innerRect;
+ } else {
+ dest.innerArea = reg2->innerArea;
+ dest.innerRect = reg2->innerRect;
+ }
+ miRegionOp(dest, reg1, reg2, miUnionO, miUnionNonO, miUnionNonO);
+
+ dest.extents.setCoords(qMin(reg1->extents.left(), reg2->extents.left()),
+ qMin(reg1->extents.top(), reg2->extents.top()),
+ qMax(reg1->extents.right(), reg2->extents.right()),
+ qMax(reg1->extents.bottom(), reg2->extents.bottom()));
+}
+
+/*======================================================================
+ * Region Subtraction
+ *====================================================================*/
+
+/*-
+ *-----------------------------------------------------------------------
+ * miSubtractNonO --
+ * Deal with non-overlapping band for subtraction. Any parts from
+ * region 2 we discard. Anything from region 1 we add to the region.
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * dest may be affected.
+ *
+ *-----------------------------------------------------------------------
+ */
+
+static void miSubtractNonO1(register QRegionPrivate &dest, register const QRect *r,
+ const QRect *rEnd, register int y1, register int y2)
+{
+ register QRect *pNextRect;
+
+ pNextRect = dest.rects.data() + dest.numRects;
+
+ Q_ASSERT(y1<=y2);
+
+ while (r != rEnd) {
+ Q_ASSERT(r->left() <= r->right());
+ MEMCHECK(dest, pNextRect, dest.rects)
+ pNextRect->setCoords(r->left(), y1, r->right(), y2);
+ ++dest.numRects;
+ ++pNextRect;
+ ++r;
+ }
+}
+
+/*-
+ *-----------------------------------------------------------------------
+ * miSubtractO --
+ * Overlapping band subtraction. x1 is the left-most point not yet
+ * checked.
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * dest may have rectangles added to it.
+ *
+ *-----------------------------------------------------------------------
+ */
+
+static void miSubtractO(register QRegionPrivate &dest, register const QRect *r1, const QRect *r1End,
+ register const QRect *r2, const QRect *r2End, register int y1, register int y2)
+{
+ register QRect *pNextRect;
+ register int x1;
+
+ x1 = r1->left();
+
+ Q_ASSERT(y1 <= y2);
+ pNextRect = dest.rects.data() + dest.numRects;
+
+ while (r1 != r1End && r2 != r2End) {
+ if (r2->right() < x1) {
+ /*
+ * Subtrahend missed the boat: go to next subtrahend.
+ */
+ ++r2;
+ } else if (r2->left() <= x1) {
+ /*
+ * Subtrahend precedes minuend: nuke left edge of minuend.
+ */
+ x1 = r2->right() + 1;
+ if (x1 > r1->right()) {
+ /*
+ * Minuend completely covered: advance to next minuend and
+ * reset left fence to edge of new minuend.
+ */
+ ++r1;
+ if (r1 != r1End)
+ x1 = r1->left();
+ } else {
+ // Subtrahend now used up since it doesn't extend beyond minuend
+ ++r2;
+ }
+ } else if (r2->left() <= r1->right()) {
+ /*
+ * Left part of subtrahend covers part of minuend: add uncovered
+ * part of minuend to region and skip to next subtrahend.
+ */
+ Q_ASSERT(x1 < r2->left());
+ MEMCHECK(dest, pNextRect, dest.rects)
+ pNextRect->setCoords(x1, y1, r2->left() - 1, y2);
+ ++dest.numRects;
+ ++pNextRect;
+
+ x1 = r2->right() + 1;
+ if (x1 > r1->right()) {
+ /*
+ * Minuend used up: advance to new...
+ */
+ ++r1;
+ if (r1 != r1End)
+ x1 = r1->left();
+ } else {
+ // Subtrahend used up
+ ++r2;
+ }
+ } else {
+ /*
+ * Minuend used up: add any remaining piece before advancing.
+ */
+ if (r1->right() >= x1) {
+ MEMCHECK(dest, pNextRect, dest.rects)
+ pNextRect->setCoords(x1, y1, r1->right(), y2);
+ ++dest.numRects;
+ ++pNextRect;
+ }
+ ++r1;
+ if (r1 != r1End)
+ x1 = r1->left();
+ }
+ }
+
+ /*
+ * Add remaining minuend rectangles to region.
+ */
+ while (r1 != r1End) {
+ Q_ASSERT(x1 <= r1->right());
+ MEMCHECK(dest, pNextRect, dest.rects)
+ pNextRect->setCoords(x1, y1, r1->right(), y2);
+ ++dest.numRects;
+ ++pNextRect;
+
+ ++r1;
+ if (r1 != r1End)
+ x1 = r1->left();
+ }
+}
+
+/*-
+ *-----------------------------------------------------------------------
+ * miSubtract --
+ * Subtract regS from regM and leave the result in regD.
+ * S stands for subtrahend, M for minuend and D for difference.
+ *
+ * Side Effects:
+ * regD is overwritten.
+ *
+ *-----------------------------------------------------------------------
+ */
+
+static void SubtractRegion(QRegionPrivate *regM, QRegionPrivate *regS,
+ register QRegionPrivate &dest)
+{
+ Q_ASSERT(!isEmptyHelper(regM));
+ Q_ASSERT(!isEmptyHelper(regS));
+ Q_ASSERT(EXTENTCHECK(&regM->extents, &regS->extents));
+ Q_ASSERT(!regS->contains(*regM));
+ Q_ASSERT(!EqualRegion(regM, regS));
+
+ miRegionOp(dest, regM, regS, miSubtractO, miSubtractNonO1, 0);
+
+ /*
+ * Can't alter dest's extents before we call miRegionOp because
+ * it might be one of the source regions and miRegionOp depends
+ * on the extents of those regions being the unaltered. Besides, this
+ * way there's no checking against rectangles that will be nuked
+ * due to coalescing, so we have to examine fewer rectangles.
+ */
+ miSetExtents(dest);
+}
+
+static void XorRegion(QRegionPrivate *sra, QRegionPrivate *srb, QRegionPrivate &dest)
+{
+ Q_ASSERT(!isEmptyHelper(sra) && !isEmptyHelper(srb));
+ Q_ASSERT(EXTENTCHECK(&sra->extents, &srb->extents));
+ Q_ASSERT(!EqualRegion(sra, srb));
+
+ QRegionPrivate tra, trb;
+
+ if (!srb->contains(*sra))
+ SubtractRegion(sra, srb, tra);
+ if (!sra->contains(*srb))
+ SubtractRegion(srb, sra, trb);
+
+ Q_ASSERT(isEmptyHelper(&trb) || !tra.contains(trb));
+ Q_ASSERT(isEmptyHelper(&tra) || !trb.contains(tra));
+
+ if (isEmptyHelper(&tra)) {
+ dest = trb;
+ } else if (isEmptyHelper(&trb)) {
+ dest = tra;
+ } else if (tra.canAppend(&trb)) {
+ dest = tra;
+ dest.append(&trb);
+ } else if (trb.canAppend(&tra)) {
+ dest = trb;
+ dest.append(&tra);
+ } else {
+ UnionRegion(&tra, &trb, dest);
+ }
+}
+
+/*
+ * Check to see if two regions are equal
+ */
+static bool EqualRegion(const QRegionPrivate *r1, const QRegionPrivate *r2)
+{
+ if (r1->numRects != r2->numRects) {
+ return false;
+ } else if (r1->numRects == 0) {
+ return true;
+ } else if (r1->extents != r2->extents) {
+ return false;
+ } else if (r1->mode == QRegionPrivate::Single && r2->mode == QRegionPrivate::Single) {
+ return r1->single == r2->single;
+ } else {
+ const QRect *rr1 = (r1->mode==QRegionPrivate::Vector)?r1->rects.constData():&r1->single;
+ const QRect *rr2 = (r2->mode==QRegionPrivate::Vector)?r2->rects.constData():&r2->single;
+ for (int i = 0; i < r1->numRects; ++i, ++rr1, ++rr2) {
+ if (*rr1 != *rr2)
+ return false;
+ }
+ }
+
+ return true;
+}
+
+static bool PointInRegion(QRegionPrivate *pRegion, int x, int y)
+{
+ int i;
+
+ if (pRegion->mode == QRegionPrivate::Single)
+ return pRegion->single.contains(x, y);
+ if (isEmptyHelper(pRegion))
+ return false;
+ if (!pRegion->extents.contains(x, y))
+ return false;
+ if (pRegion->innerRect.contains(x, y))
+ return true;
+ for (i = 0; i < pRegion->numRects; ++i) {
+ if (pRegion->rects[i].contains(x, y))
+ return true;
+ }
+ return false;
+}
+
+static bool RectInRegion(register QRegionPrivate *region, int rx, int ry, uint rwidth, uint rheight)
+{
+ register const QRect *pbox;
+ register const QRect *pboxEnd;
+ QRect rect(rx, ry, rwidth, rheight);
+ register QRect *prect = &rect;
+ int partIn, partOut;
+
+ if (!region || region->numRects == 0 || !EXTENTCHECK(&region->extents, prect))
+ return RectangleOut;
+
+ partOut = false;
+ partIn = false;
+
+ /* can stop when both partOut and partIn are true, or we reach prect->y2 */
+ for (pbox = (region->mode==QRegionPrivate::Vector)?region->rects.constData():&region->single, pboxEnd = pbox + region->numRects;
+ pbox < pboxEnd; ++pbox) {
+ if (pbox->bottom() < ry)
+ continue;
+
+ if (pbox->top() > ry) {
+ partOut = true;
+ if (partIn || pbox->top() > prect->bottom())
+ break;
+ ry = pbox->top();
+ }
+
+ if (pbox->right() < rx)
+ continue; /* not far enough over yet */
+
+ if (pbox->left() > rx) {
+ partOut = true; /* missed part of rectangle to left */
+ if (partIn)
+ break;
+ }
+
+ if (pbox->left() <= prect->right()) {
+ partIn = true; /* definitely overlap */
+ if (partOut)
+ break;
+ }
+
+ if (pbox->right() >= prect->right()) {
+ ry = pbox->bottom() + 1; /* finished with this band */
+ if (ry > prect->bottom())
+ break;
+ rx = prect->left(); /* reset x out to left again */
+ } else {
+ /*
+ * Because boxes in a band are maximal width, if the first box
+ * to overlap the rectangle doesn't completely cover it in that
+ * band, the rectangle must be partially out, since some of it
+ * will be uncovered in that band. partIn will have been set true
+ * by now...
+ */
+ break;
+ }
+ }
+ return partIn ? ((ry <= prect->bottom()) ? RectanglePart : RectangleIn) : RectangleOut;
+}
+// END OF Region.c extract
+// START OF poly.h extract
+/* $XConsortium: poly.h,v 1.4 94/04/17 20:22:19 rws 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.
+
+
+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.
+
+************************************************************************/
+
+/*
+ * 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_axis; /* 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_axis, bres.d, \
+ bres.m, bres.m1, bres.incr1, bres.incr2)
+
+#define BRESINCRPGONSTRUCT(bres) \
+ BRESINCRPGON(bres.d, bres.minor_axis, bres.m, bres.m1, bres.incr1, bres.incr2)
+
+
+
+/*
+ * 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 counter-clockwise 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).
+ */
+
+/*
+ * for the winding number rule
+ */
+#define CLOCKWISE 1
+#define COUNTERCLOCKWISE -1
+
+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;
+
+
+
+/*
+ *
+ * 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; \
+ } \
+}
+// END OF poly.h extract
+// START OF PolyReg.c extract
+/* $XConsortium: PolyReg.c,v 11.23 94/11/17 21:59:37 converse 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.
+
+
+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.
+
+************************************************************************/
+/* $XFree86: xc/lib/X11/PolyReg.c,v 1.1.1.2.8.2 1998/10/04 15:22:49 hohndel Exp $ */
+
+#define LARGE_COORDINATE 1000000
+#define SMALL_COORDINATE -LARGE_COORDINATE
+
+/*
+ * 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 void InsertEdgeInET(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));
+ (*SLLBlock)->next = tmpSLLBlock;
+ tmpSLLBlock->next = (ScanLineListBlock *)NULL;
+ *SLLBlock = tmpSLLBlock;
+ *iSLLBlock = 0;
+ }
+ pSLL = &((*SLLBlock)->SLLs[(*iSLLBlock)++]);
+
+ pSLL->next = pPrevSLL->next;
+ pSLL->edgelist = (EdgeTableEntry *)NULL;
+ pPrevSLL->next = pSLL;
+ }
+ pSLL->scanline = scanline;
+
+ /*
+ * now insert the edge in the right bucket
+ */
+ prev = 0;
+ start = pSLL->edgelist;
+ while (start && (start->bres.minor_axis < ETE->bres.minor_axis)) {
+ prev = start;
+ start = start->next;
+ }
+ ETE->next = start;
+
+ if (prev)
+ prev->next = ETE;
+ else
+ pSLL->edgelist = ETE;
+}
+
+/*
+ * 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.
+ *
+ */
+
+static void CreateETandAET(register int count, register const QPoint *pts,
+ EdgeTable *ET, EdgeTableEntry *AET, register EdgeTableEntry *pETEs,
+ ScanLineListBlock *pSLLBlock)
+{
+ register const QPoint *top,
+ *bottom,
+ *PrevPt,
+ *CurrPt;
+ int iSLLBlock = 0;
+ int dy;
+
+ if (count < 2)
+ return;
+
+ /*
+ * initialize the Active Edge Table
+ */
+ AET->next = 0;
+ AET->back = 0;
+ AET->nextWETE = 0;
+ AET->bres.minor_axis = SMALL_COORDINATE;
+
+ /*
+ * initialize the Edge Table.
+ */
+ ET->scanlines.next = 0;
+ ET->ymax = SMALL_COORDINATE;
+ ET->ymin = LARGE_COORDINATE;
+ 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)
+
+ InsertEdgeInET(ET, pETEs, top->y(), &pSLLBlock, &iSLLBlock);
+
+ if (PrevPt->y() > ET->ymax)
+ ET->ymax = PrevPt->y();
+ if (PrevPt->y() < ET->ymin)
+ ET->ymin = PrevPt->y();
+ ++pETEs;
+ }
+
+ PrevPt = CurrPt;
+ }
+}
+
+/*
+ * loadAET
+ *
+ * This routine moves EdgeTableEntries from the
+ * EdgeTable into the Active Edge Table,
+ * leaving them sorted by smaller x coordinate.
+ *
+ */
+
+static void loadAET(register EdgeTableEntry *AET, register EdgeTableEntry *ETEs)
+{
+ register EdgeTableEntry *pPrevAET;
+ register EdgeTableEntry *tmp;
+
+ pPrevAET = AET;
+ AET = AET->next;
+ while (ETEs) {
+ while (AET && AET->bres.minor_axis < ETEs->bres.minor_axis) {
+ 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 computeWAET(register 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 InsertionSort(register 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_axis > AET->bres.minor_axis)
+ 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;
+}
+
+/*
+ * Clean up our act.
+ */
+static void FreeStorage(register ScanLineListBlock *pSLLBlock)
+{
+ register ScanLineListBlock *tmpSLLBlock;
+
+ while (pSLLBlock) {
+ tmpSLLBlock = pSLLBlock->next;
+ free(pSLLBlock);
+ pSLLBlock = tmpSLLBlock;
+ }
+}
+
+/*
+ * Create an array of rectangles from a list of points.
+ * If indeed these things (POINTS, RECTS) are the same,
+ * then this proc is still needed, because it allocates
+ * storage for the array, which was allocated on the
+ * stack by the calling procedure.
+ *
+ */
+static void PtsToRegion(register int numFullPtBlocks, register int iCurPtBlock,
+ POINTBLOCK *FirstPtBlock, QRegionPrivate *reg)
+{
+ register QRect *rects;
+ register QPoint *pts;
+ register POINTBLOCK *CurPtBlock;
+ register int i;
+ register QRect *extents;
+ register int numRects;
+
+ extents = &reg->extents;
+ numRects = ((numFullPtBlocks * NUMPTSTOBUFFER) + iCurPtBlock) >> 1;
+
+ reg->rects.resize(numRects);
+
+ CurPtBlock = FirstPtBlock;
+ rects = reg->rects.data() - 1;
+ numRects = 0;
+ extents->setLeft(INT_MAX);
+ extents->setRight(INT_MIN);
+ reg->innerArea = -1;
+
+ for (; numFullPtBlocks >= 0; --numFullPtBlocks) {
+ /* the loop uses 2 points per iteration */
+ i = NUMPTSTOBUFFER >> 1;
+ if (!numFullPtBlocks)
+ i = iCurPtBlock >> 1;
+ if(i) {
+ for (pts = CurPtBlock->pts; i--; pts += 2) {
+ if (pts->x() == pts[1].x())
+ continue;
+ if (numRects && pts->x() == rects->left() && pts->y() == rects->bottom() + 1
+ && pts[1].x() == rects->right()+1 && (numRects == 1 || rects[-1].top() != rects->top())
+ && (i && pts[2].y() > pts[1].y())) {
+ rects->setBottom(pts[1].y());
+ reg->updateInnerRect(*rects);
+ continue;
+ }
+ ++numRects;
+ ++rects;
+ rects->setCoords(pts->x(), pts->y(), pts[1].x() - 1, pts[1].y());
+ if (rects->left() < extents->left())
+ extents->setLeft(rects->left());
+ if (rects->right() > extents->right())
+ extents->setRight(rects->right());
+ reg->updateInnerRect(*rects);
+ }
+ }
+ CurPtBlock = CurPtBlock->next;
+ }
+
+ if (numRects) {
+ extents->setTop(reg->rects[0].top());
+ extents->setBottom(rects->bottom());
+ } else {
+ extents->setCoords(0, 0, 0, 0);
+ }
+ reg->numRects = numRects;
+}
+
+/*
+ * polytoregion
+ *
+ * Scan converts a polygon by returning a run-length
+ * encoding of the resultant bitmap -- the run-length
+ * encoding is in the form of an array of rectangles.
+ */
+static QRegionPrivate *PolygonRegion(const QPoint *Pts, int Count, int rule,
+ QRegionPrivate *region)
+ //Point *Pts; /* the pts */
+ //int Count; /* number of pts */
+ //int rule; /* winding rule */
+{
+ register EdgeTableEntry *pAET; /* Active Edge Table */
+ register int y; /* current scanline */
+ register int iPts = 0; /* number of pts in buffer */
+ register EdgeTableEntry *pWETE; /* Winding Edge Table Entry*/
+ register ScanLineList *pSLL; /* current scanLineList */
+ register QPoint *pts; /* output buffer */
+ EdgeTableEntry *pPrevAET; /* ptr to previous AET */
+ EdgeTable ET; /* header node for ET */
+ EdgeTableEntry AET; /* header node for AET */
+ EdgeTableEntry *pETEs; /* EdgeTableEntries pool */
+ ScanLineListBlock SLLBlock; /* header for scanlinelist */
+ int fixWAET = false;
+ POINTBLOCK FirstPtBlock, *curPtBlock; /* PtBlock buffers */
+ POINTBLOCK *tmpPtBlock;
+ int numFullPtBlocks = 0;
+
+ region->vector();
+
+ /* special case a rectangle */
+ if (((Count == 4) ||
+ ((Count == 5) && (Pts[4].x() == Pts[0].x()) && (Pts[4].y() == Pts[0].y())))
+ && (((Pts[0].y() == Pts[1].y()) && (Pts[1].x() == Pts[2].x()) && (Pts[2].y() == Pts[3].y())
+ && (Pts[3].x() == Pts[0].x())) || ((Pts[0].x() == Pts[1].x())
+ && (Pts[1].y() == Pts[2].y()) && (Pts[2].x() == Pts[3].x())
+ && (Pts[3].y() == Pts[0].y())))) {
+ int x = qMin(Pts[0].x(), Pts[2].x());
+ region->extents.setLeft(x);
+ int y = qMin(Pts[0].y(), Pts[2].y());
+ region->extents.setTop(y);
+ region->extents.setWidth(qMax(Pts[0].x(), Pts[2].x()) - x);
+ region->extents.setHeight(qMax(Pts[0].y(), Pts[2].y()) - y);
+ if ((region->extents.left() <= region->extents.right()) &&
+ (region->extents.top() <= region->extents.bottom())) {
+ region->numRects = 1;
+ region->rects.resize(1);
+ region->rects[0] = region->extents;
+ region->innerRect = region->extents;
+ region->innerArea = region->innerRect.width() * region->innerRect.height();
+ }
+ return region;
+ }
+
+ if (!(pETEs = static_cast<EdgeTableEntry *>(malloc(sizeof(EdgeTableEntry) * Count))))
+ return 0;
+
+ pts = FirstPtBlock.pts;
+ CreateETandAET(Count, Pts, &ET, &AET, pETEs, &SLLBlock);
+ pSLL = ET.scanlines.next;
+ curPtBlock = &FirstPtBlock;
+
+ if (rule == EvenOddRule) {
+ /*
+ * for each scanline
+ */
+ for (y = ET.ymin; y < ET.ymax; ++y) {
+ /*
+ * Add a new edge to the active edge table when we
+ * get to the next edge.
+ */
+ if (pSLL && y == pSLL->scanline) {
+ loadAET(&AET, pSLL->edgelist);
+ pSLL = pSLL->next;
+ }
+ pPrevAET = &AET;
+ pAET = AET.next;
+
+ /*
+ * for each active edge
+ */
+ while (pAET) {
+ pts->setX(pAET->bres.minor_axis);
+ pts->setY(y);
+ ++pts;
+ ++iPts;
+
+ /*
+ * send out the buffer
+ */
+ if (iPts == NUMPTSTOBUFFER) {
+ tmpPtBlock = (POINTBLOCK *)malloc(sizeof(POINTBLOCK));
+ curPtBlock->next = tmpPtBlock;
+ curPtBlock = tmpPtBlock;
+ pts = curPtBlock->pts;
+ ++numFullPtBlocks;
+ iPts = 0;
+ }
+ EVALUATEEDGEEVENODD(pAET, pPrevAET, y)
+ }
+ InsertionSort(&AET);
+ }
+ } else {
+ /*
+ * for each scanline
+ */
+ for (y = ET.ymin; y < ET.ymax; ++y) {
+ /*
+ * Add a new edge to the active edge table when we
+ * get to the next edge.
+ */
+ if (pSLL && y == pSLL->scanline) {
+ loadAET(&AET, pSLL->edgelist);
+ computeWAET(&AET);
+ pSLL = pSLL->next;
+ }
+ pPrevAET = &AET;
+ pAET = AET.next;
+ pWETE = pAET;
+
+ /*
+ * for each active edge
+ */
+ while (pAET) {
+ /*
+ * add to the buffer only those edges that
+ * are in the Winding active edge table.
+ */
+ if (pWETE == pAET) {
+ pts->setX(pAET->bres.minor_axis);
+ pts->setY(y);
+ ++pts;
+ ++iPts;
+
+ /*
+ * send out the buffer
+ */
+ if (iPts == NUMPTSTOBUFFER) {
+ tmpPtBlock = static_cast<POINTBLOCK *>(malloc(sizeof(POINTBLOCK)));
+ curPtBlock->next = tmpPtBlock;
+ curPtBlock = tmpPtBlock;
+ pts = curPtBlock->pts;
+ ++numFullPtBlocks;
+ iPts = 0;
+ }
+ pWETE = pWETE->nextWETE;
+ }
+ EVALUATEEDGEWINDING(pAET, pPrevAET, y, fixWAET)
+ }
+
+ /*
+ * recompute the winding active edge table if
+ * we just resorted or have exited an edge.
+ */
+ if (InsertionSort(&AET) || fixWAET) {
+ computeWAET(&AET);
+ fixWAET = false;
+ }
+ }
+ }
+ FreeStorage(SLLBlock.next);
+ PtsToRegion(numFullPtBlocks, iPts, &FirstPtBlock, region);
+ for (curPtBlock = FirstPtBlock.next; --numFullPtBlocks >= 0;) {
+ tmpPtBlock = curPtBlock->next;
+ free(curPtBlock);
+ curPtBlock = tmpPtBlock;
+ }
+ free(pETEs);
+ return region;
+}
+// END OF PolyReg.c extract
+
+QRegionPrivate *qt_bitmapToRegion(const QBitmap& bitmap, QRegionPrivate *region)
+{
+ region->vector();
+
+ QImage image = bitmap.toImage();
+
+ QRect xr;
+
+#define AddSpan \
+ { \
+ xr.setCoords(prev1, y, x-1, y); \
+ UnionRectWithRegion(&xr, region, *region); \
+ }
+
+ const uchar zero = 0;
+ bool little = image.format() == QImage::Format_MonoLSB;
+
+ int x,
+ y;
+ for (y = 0; y < image.height(); ++y) {
+ uchar *line = image.scanLine(y);
+ int w = image.width();
+ uchar all = zero;
+ int prev1 = -1;
+ for (x = 0; x < w;) {
+ uchar byte = line[x / 8];
+ if (x > w - 8 || byte!=all) {
+ if (little) {
+ for (int b = 8; b > 0 && x < w; --b) {
+ if (!(byte & 0x01) == !all) {
+ // More of the same
+ } else {
+ // A change.
+ if (all!=zero) {
+ AddSpan
+ all = zero;
+ } else {
+ prev1 = x;
+ all = ~zero;
+ }
+ }
+ byte >>= 1;
+ ++x;
+ }
+ } else {
+ for (int b = 8; b > 0 && x < w; --b) {
+ if (!(byte & 0x80) == !all) {
+ // More of the same
+ } else {
+ // A change.
+ if (all != zero) {
+ AddSpan
+ all = zero;
+ } else {
+ prev1 = x;
+ all = ~zero;
+ }
+ }
+ byte <<= 1;
+ ++x;
+ }
+ }
+ } else {
+ x += 8;
+ }
+ }
+ if (all != zero) {
+ AddSpan
+ }
+ }
+#undef AddSpan
+
+ return region;
+}
+
+/*
+ Constructs an empty region.
+
+ \sa isEmpty()
+*/
+
+QRegion::QRegion()
+ : d(&shared_empty)
+{
+ d->ref.ref();
+}
+
+/*
+ \overload
+
+ Create a region based on the rectange \a r with region type \a t.
+
+ If the rectangle is invalid a null region will be created.
+
+ \sa QRegion::RegionType
+*/
+
+QRegion::QRegion(const QRect &r, RegionType t)
+{
+ if (r.isEmpty()) {
+ d = &shared_empty;
+ d->ref.ref();
+ } else {
+// d = new QRegionData;
+ QRegionPrivate *rp = 0;
+ if (t == Rectangle) {
+// rp = new QRegionPrivate(r);
+ rp = qt_allocRegion(r);
+ } else if (t == Ellipse) {
+ QPainterPath path;
+ path.addEllipse(r.x(), r.y(), r.width(), r.height());
+ QPolygon a = path.toSubpathPolygons().at(0).toPolygon();
+ rp = qt_allocRegion();
+// rp = new QRegionPrivate;
+ PolygonRegion(a.constData(), a.size(), EvenOddRule, rp);
+ }
+ d = rp;
+ d->ref = 1;
+#if defined(Q_WS_X11)
+ d->rgn = 0;
+ d->xrectangles = 0;
+#elif defined(Q_WS_MAC)
+ d->rgn = 0;
+#endif
+ d->qt_rgn = rp;
+ }
+}
+
+/*
+ Constructs a polygon region from the point array \a a with the fill rule
+ specified by \a fillRule.
+
+ If \a fillRule is \l{Qt::WindingFill}, the polygon region is defined
+ using the winding algorithm; if it is \l{Qt::OddEvenFill}, the odd-even fill
+ algorithm is used.
+
+ \warning This constructor can be used to create complex regions that will
+ slow down painting when used.
+*/
+
+QRegion::QRegion(const QPolygon &a, Qt::FillRule fillRule)
+{
+ if (a.count() > 2) {
+ //d = new QRegionData;
+ // QRegionPrivate *rp = new QRegionPrivate;
+ QRegionPrivate *rp = qt_allocRegion();
+ PolygonRegion(a.constData(), a.size(),
+ fillRule == Qt::WindingFill ? WindingRule : EvenOddRule, rp);
+ d = rp;
+ d->ref = 1;
+#if defined(Q_WS_X11)
+ d->rgn = 0;
+ d->xrectangles = 0;
+#elif defined(Q_WS_MAC)
+ d->rgn = 0;
+#endif
+ d->qt_rgn = rp;
+ } else {
+ d = &shared_empty;
+ d->ref.ref();
+ }
+}
+
+
+/*
+ Constructs a new region which is equal to region \a r.
+*/
+
+QRegion::QRegion(const QRegion &r)
+{
+ d = r.d;
+ d->ref.ref();
+}
+
+
+/*
+ Constructs a region from the bitmap \a bm.
+
+ The resulting region consists of the pixels in bitmap \a bm that
+ are Qt::color1, as if each pixel was a 1 by 1 rectangle.
+
+ This constructor may create complex regions that will slow down
+ painting when used. Note that drawing masked pixmaps can be done
+ much faster using QPixmap::setMask().
+*/
+QRegion::QRegion(const QBitmap &bm)
+{
+ if (bm.isNull()) {
+ d = &shared_empty;
+ d->ref.ref();
+ } else {
+ // d = new QRegionData;
+// QRegionPrivate *rp = new QRegionPrivate;
+ QRegionPrivate *rp = qt_allocRegion();
+
+ qt_bitmapToRegion(bm, rp);
+ d = rp;
+ d->ref = 1;
+#if defined(Q_WS_X11)
+ d->rgn = 0;
+ d->xrectangles = 0;
+#elif defined(Q_WS_MAC)
+ d->rgn = 0;
+#endif
+ d->qt_rgn = rp;
+ }
+}
+
+void QRegion::cleanUp(QRegion::QRegionData *x)
+{
+ // delete x->qt_rgn;
+#if defined(Q_WS_X11)
+ if (x->rgn)
+ XDestroyRegion(x->rgn);
+ if (x->xrectangles)
+ free(x->xrectangles);
+#elif defined(Q_WS_MAC)
+ if (x->rgn)
+ qt_mac_dispose_rgn(x->rgn);
+#endif
+ if(x->qt_rgn) {
+// delete x->qt_rgn;
+ qt_freeRegion(x->qt_rgn);
+ } else {
+ delete x;
+ }
+}
+
+/*
+ Destroys the region.
+*/
+
+QRegion::~QRegion()
+{
+ if (!d->ref.deref())
+ cleanUp(d);
+}
+
+
+/*
+ Assigns \a r to this region and returns a reference to the region.
+*/
+
+QRegion &QRegion::operator=(const QRegion &r)
+{
+ r.d->ref.ref();
+ if (!d->ref.deref())
+ cleanUp(d);
+ d = r.d;
+ return *this;
+}
+
+
+/*
+ \internal
+*/
+
+QRegion QRegion::copy() const
+{
+ QRegion r;
+ QRegionData *x = 0; // new QRegionData;
+ QRegionPrivate *rp = 0;
+ if (d->qt_rgn)
+// rp = new QRegionPrivate(*d->qt_rgn);
+ rp = qt_allocRegion(*d->qt_rgn);
+ else
+ rp = qt_allocRegion();
+ x = rp;
+ x->qt_rgn = rp;
+ x->ref = 1;
+#if defined(Q_WS_X11)
+ x->rgn = 0;
+ x->xrectangles = 0;
+#elif defined(Q_WS_MAC)
+ x->rgn = 0;
+#endif
+
+ if (!r.d->ref.deref())
+ cleanUp(r.d);
+ r.d = x;
+ return r;
+}
+
+/*
+ Returns true if the region is empty; otherwise returns false. An
+ empty region is a region that contains no points.
+
+ Example:
+ \snippet doc/src/snippets/code/src.gui.painting.qregion_qws.cpp 0
+*/
+
+bool QRegion::isEmpty() const
+{
+ return d == &shared_empty || d->qt_rgn->numRects == 0;
+}
+
+
+/*
+ Returns true if the region contains the point \a p; otherwise
+ returns false.
+*/
+
+bool QRegion::contains(const QPoint &p) const
+{
+ return PointInRegion(d->qt_rgn, p.x(), p.y());
+}
+
+/*
+ \overload
+
+ Returns true if the region overlaps the rectangle \a r; otherwise
+ returns false.
+*/
+
+bool QRegion::contains(const QRect &r) const
+{
+ if(!d->qt_rgn)
+ return false;
+ if(d->qt_rgn->mode == QRegionPrivate::Single)
+ return d->qt_rgn->single.contains(r);
+
+ return RectInRegion(d->qt_rgn, r.left(), r.top(), r.width(), r.height()) != RectangleOut;
+}
+
+
+
+/*
+ Translates (moves) the region \a dx along the X axis and \a dy
+ along the Y axis.
+*/
+
+void QRegion::translate(int dx, int dy)
+{
+ if ((dx == 0 && dy == 0) || isEmptyHelper(d->qt_rgn))
+ return;
+
+ detach();
+ OffsetRegion(*d->qt_rgn, dx, dy);
+#if defined(Q_WS_X11)
+ if (d->xrectangles) {
+ free(d->xrectangles);
+ d->xrectangles = 0;
+ }
+#elif defined(Q_WS_MAC)
+ if(d->rgn) {
+ qt_mac_dispose_rgn(d->rgn);
+ d->rgn = 0;
+ }
+#endif
+}
+
+/*
+ \fn QRegion QRegion::unite(const QRegion &r) const
+ \obsolete
+
+ Use united(\a r) instead.
+*/
+
+/*
+ \fn QRegion QRegion::united(const QRegion &r) const
+ \since 4.2
+
+ Returns a region which is the union of this region and \a r.
+
+ \img runion.png Region Union
+
+ The figure shows the union of two elliptical regions.
+
+ \sa intersected(), subtracted(), xored()
+*/
+
+QRegion QRegion::unite(const QRegion &r) const
+{
+ if (isEmptyHelper(d->qt_rgn))
+ return r;
+ if (isEmptyHelper(r.d->qt_rgn))
+ return *this;
+
+ if (d->qt_rgn->contains(*r.d->qt_rgn)) {
+ return *this;
+ } else if (r.d->qt_rgn->contains(*d->qt_rgn)) {
+ return r;
+ } else if (d->qt_rgn->canAppend(r.d->qt_rgn)) {
+ QRegion result(*this);
+ result.detach();
+ result.d->qt_rgn->append(r.d->qt_rgn);
+ return result;
+ } else if (r.d->qt_rgn->canAppend(d->qt_rgn)) {
+ QRegion result(r);
+ result.detach();
+ result.d->qt_rgn->append(d->qt_rgn);
+ return result;
+ } else if (EqualRegion(d->qt_rgn, r.d->qt_rgn)) {
+ return *this;
+ } else {
+ QRegion result;
+ result.detach();
+ UnionRegion(d->qt_rgn, r.d->qt_rgn, *result.d->qt_rgn);
+ return result;
+ }
+}
+
+QRegion& QRegion::operator+=(const QRegion &r)
+{
+ if (isEmptyHelper(d->qt_rgn))
+ return *this = r;
+ if (isEmptyHelper(r.d->qt_rgn))
+ return *this;
+
+ if (d->qt_rgn->contains(*r.d->qt_rgn)) {
+ return *this;
+ } else if (r.d->qt_rgn->contains(*d->qt_rgn)) {
+ return *this = r;
+ } else if (d->qt_rgn->canAppend(r.d->qt_rgn)) {
+ detach();
+ d->qt_rgn->append(r.d->qt_rgn);
+ return *this;
+ } else if (d->qt_rgn->canPrepend(r.d->qt_rgn)) {
+ detach();
+ d->qt_rgn->prepend(r.d->qt_rgn);
+ return *this;
+ } else if (EqualRegion(d->qt_rgn, r.d->qt_rgn)) {
+ return *this;
+ }
+
+ return *this = unite(r);
+}
+
+/*
+ \fn QRegion QRegion::intersect(const QRegion &r) const
+ \obsolete
+
+ Use intersected(\a r) instead.
+*/
+
+/*
+ \fn QRegion QRegion::intersected(const QRegion &r) const
+ \since 4.2
+
+ Returns a region which is the intersection of this region and \a r.
+
+ \img rintersect.png Region Intersection
+
+ The figure shows the intersection of two elliptical regions.
+*/
+
+QRegion QRegion::intersect(const QRegion &r) const
+{
+ if (isEmptyHelper(d->qt_rgn) || isEmptyHelper(r.d->qt_rgn)
+ || !EXTENTCHECK(&d->qt_rgn->extents, &r.d->qt_rgn->extents))
+ return QRegion();
+
+ /* this is fully contained in r */
+ if (r.d->qt_rgn->contains(*d->qt_rgn))
+ return *this;
+
+ /* r is fully contained in this */
+ if (d->qt_rgn->contains(*r.d->qt_rgn))
+ return r;
+
+ if(r.d->qt_rgn->mode == QRegionPrivate::Single &&
+ d->qt_rgn->mode == QRegionPrivate::Single)
+ return QRegion(r.d->qt_rgn->single.intersected(d->qt_rgn->single));
+#ifdef QT_GREENPHONE_OPT
+ else if(r.d->qt_rgn->mode == QRegionPrivate::Single)
+ return intersect(r.d->qt_rgn->single);
+ else if(d->qt_rgn->mode == QRegionPrivate::Single)
+ return r.intersect(d->qt_rgn->single);
+#endif
+
+ QRegion result;
+ result.detach();
+ miRegionOp(*result.d->qt_rgn, d->qt_rgn, r.d->qt_rgn, miIntersectO, 0, 0);
+
+ /*
+ * Can't alter dest's extents before we call miRegionOp because
+ * it might be one of the source regions and miRegionOp depends
+ * on the extents of those regions being the same. Besides, this
+ * way there's no checking against rectangles that will be nuked
+ * due to coalescing, so we have to examine fewer rectangles.
+ */
+ miSetExtents(*result.d->qt_rgn);
+ return result;
+}
+
+#ifdef QT_GREENPHONE_OPT
+/*
+ \overload
+ */
+QRegion QRegion::intersect(const QRect &r) const
+{
+ // No intersection
+ if(r.isEmpty() || isEmpty() || !EXTENTCHECK(&r, &d->qt_rgn->extents))
+ return QRegion();
+
+ // This is fully contained in r
+ if(CONTAINSCHECK(r, d->qt_rgn->extents))
+ return *this;
+
+ // r is fully contained in this
+ if(CONTAINSCHECK(d->qt_rgn->innerRect, r))
+ return QRegion(r);
+
+ if(d->qt_rgn->mode == QRegionPrivate::Single) {
+ return QRegion(d->qt_rgn->single & r);
+ } else {
+ QRegion rv(*this);
+ rv.detach();
+
+ rv.d->qt_rgn->extents &= r;
+ rv.d->qt_rgn->innerRect &= r;
+ rv.d->qt_rgn->innerArea = rv.d->qt_rgn->innerRect.height() *
+ rv.d->qt_rgn->innerRect.width();
+
+ int numRects = 0;
+ for(int ii = 0; ii < rv.d->qt_rgn->numRects; ++ii) {
+ QRect result = rv.d->qt_rgn->rects[ii] & r;
+ if(!result.isEmpty())
+ rv.d->qt_rgn->rects[numRects++] = result;
+ }
+ rv.d->qt_rgn->numRects = numRects;
+ return rv;
+ }
+}
+
+/*
+ \overload
+ */
+const QRegion QRegion::operator&(const QRect &r) const
+{
+ return intersect(r);
+}
+
+/*
+ \overload
+ */
+QRegion& QRegion::operator&=(const QRect &r)
+{
+ if(isEmpty() || CONTAINSCHECK(r, d->qt_rgn->extents)) {
+ // Do nothing
+ } else if(r.isEmpty() || !EXTENTCHECK(&r, &d->qt_rgn->extents)) {
+ *this = QRegion();
+ } else if(CONTAINSCHECK(d->qt_rgn->innerRect, r)) {
+ *this = QRegion(r);
+ } else {
+ detach();
+ if(d->qt_rgn->mode == QRegionPrivate::Single) {
+ QRect result = d->qt_rgn->single & r;
+ d->qt_rgn->single = result;
+ d->qt_rgn->extents = result;
+ d->qt_rgn->innerRect = result;
+ d->qt_rgn->innerArea = result.height() * result.width();
+ } else {
+ d->qt_rgn->extents &= r;
+ d->qt_rgn->innerRect &= r;
+ d->qt_rgn->innerArea = d->qt_rgn->innerRect.height() *
+ d->qt_rgn->innerRect.width();
+
+ int numRects = 0;
+ for(int ii = 0; ii < d->qt_rgn->numRects; ++ii) {
+ QRect result = d->qt_rgn->rects[ii] & r;
+ if(!result.isEmpty())
+ d->qt_rgn->rects[numRects++] = result;
+ }
+ d->qt_rgn->numRects = numRects;
+ }
+ }
+ return *this;
+}
+#endif
+
+/*
+ \fn QRegion QRegion::subtract(const QRegion &r) const
+ \obsolete
+
+ Use subtracted(\a r) instead.
+*/
+
+/*
+ \fn QRegion QRegion::subtracted(const QRegion &r) const
+ \since 4.2
+
+ Returns a region which is \a r subtracted from this region.
+
+ \img rsubtract.png Region Subtraction
+
+ The figure shows the result when the ellipse on the right is
+ subtracted from the ellipse on the left (\c {left - right}).
+
+ \sa intersected(), united(), xored()
+*/
+
+QRegion QRegion::subtract(const QRegion &r) const
+{
+ if (isEmptyHelper(d->qt_rgn) || isEmptyHelper(r.d->qt_rgn))
+ return *this;
+ if (r.d->qt_rgn->contains(*d->qt_rgn))
+ return QRegion();
+ if (!EXTENTCHECK(&d->qt_rgn->extents, &r.d->qt_rgn->extents))
+ return *this;
+ if (EqualRegion(d->qt_rgn, r.d->qt_rgn))
+ return QRegion();
+
+ QRegion result;
+ result.detach();
+ SubtractRegion(d->qt_rgn, r.d->qt_rgn, *result.d->qt_rgn);
+ return result;
+}
+
+/*
+ \fn QRegion QRegion::eor(const QRegion &r) const
+ \obsolete
+
+ Use xored(\a r) instead.
+*/
+
+/*
+ \fn QRegion QRegion::xored(const QRegion &r) const
+ \since 4.2
+
+ Returns a region which is the exclusive or (XOR) of this region
+ and \a r.
+
+ \img rxor.png Region XORed
+
+ The figure shows the exclusive or of two elliptical regions.
+
+ \sa intersected(), united(), subtracted()
+*/
+
+QRegion QRegion::eor(const QRegion &r) const
+{
+ if (isEmptyHelper(d->qt_rgn)) {
+ return r;
+ } else if (isEmptyHelper(r.d->qt_rgn)) {
+ return *this;
+ } else if (!EXTENTCHECK(&d->qt_rgn->extents, &r.d->qt_rgn->extents)) {
+ return (*this + r);
+ } else if (EqualRegion(d->qt_rgn, r.d->qt_rgn)) {
+ return QRegion();
+ } else {
+ QRegion result;
+ result.detach();
+ XorRegion(d->qt_rgn, r.d->qt_rgn, *result.d->qt_rgn);
+ return result;
+ }
+}
+
+/*
+ Returns the bounding rectangle of this region. An empty region
+ gives a rectangle that is QRect::isNull().
+*/
+
+QRect QRegion::boundingRect() const
+{
+ if (isEmpty())
+ return QRect();
+ return d->qt_rgn->extents;
+}
+
+/* \internal
+ Returns true if \a rect is guaranteed to be fully contained in \a region.
+ A false return value does not guarantee the opposite.
+*/
+bool qt_region_strictContains(const QRegion &region, const QRect &rect)
+{
+ if (isEmptyHelper(region.d->qt_rgn) || !rect.isValid())
+ return false;
+
+#if 0 // TEST_INNERRECT
+ static bool guard = false;
+ if (guard)
+ return QRect();
+ guard = true;
+ QRegion inner = region.d->qt_rgn->innerRect;
+ Q_ASSERT((inner - region).isEmpty());
+ guard = false;
+
+ int maxArea = 0;
+ for (int i = 0; i < region.d->qt_rgn->numRects; ++i) {
+ const QRect r = region.d->qt_rgn->rects.at(i);
+ if (r.width() * r.height() > maxArea)
+ maxArea = r.width() * r.height();
+ }
+
+ if (maxArea > region.d->qt_rgn->innerArea) {
+ qDebug() << "not largest rectangle" << region << region.d->qt_rgn->innerRect;
+ }
+ Q_ASSERT(maxArea <= region.d->qt_rgn->innerArea);
+#endif
+
+ const QRect r1 = region.d->qt_rgn->innerRect;
+ return (rect.left() >= r1.left() && rect.right() <= r1.right()
+ && rect.top() >= r1.top() && rect.bottom() <= r1.bottom());
+}
+
+/*
+ Returns an array of non-overlapping rectangles that make up the
+ region.
+
+ The union of all the rectangles is equal to the original region.
+*/
+QVector<QRect> QRegion::rects() const
+{
+ if (d->qt_rgn) {
+ d->qt_rgn->vector();
+ d->qt_rgn->rects.resize(d->qt_rgn->numRects);
+ return d->qt_rgn->rects;
+ } else {
+ return QVector<QRect>();
+ }
+}
+
+/*
+ \fn void QRegion::setRects(const QRect *rects, int number)
+
+ Sets the region using the array of rectangles specified by \a rects and
+ \a number.
+ The rectangles \e must be optimally Y-X sorted and follow these restrictions:
+
+ \list
+ \o The rectangles must not intersect.
+ \o All rectangles with a given top coordinate must have the same height.
+ \o No two rectangles may abut horizontally (they should be combined
+ into a single wider rectangle in that case).
+ \o The rectangles must be sorted in ascending order, with Y as the major
+ sort key and X as the minor sort key.
+ \endlist
+ \omit
+ Only some platforms have these restrictions (Qt for Embedded Linux, X11 and Mac OS X).
+ \endomit
+*/
+void QRegion::setRects(const QRect *rects, int num)
+{
+ *this = QRegion();
+ if (!rects || num == 0 || (num == 1 && rects->isEmpty()))
+ return;
+
+ detach();
+
+ if(num == 1) {
+ d->qt_rgn->single = *rects;
+ d->qt_rgn->mode = QRegionPrivate::Single;
+ d->qt_rgn->numRects = num;
+ d->qt_rgn->extents = *rects;
+ d->qt_rgn->innerRect = *rects;
+ } else {
+ d->qt_rgn->mode = QRegionPrivate::Vector;
+ d->qt_rgn->rects.resize(num);
+ d->qt_rgn->numRects = num;
+ int left = INT_MAX,
+ right = INT_MIN,
+ top = INT_MAX,
+ bottom = INT_MIN;
+ for (int i = 0; i < num; ++i) {
+ const QRect &rect = rects[i];
+ d->qt_rgn->rects[i] = rect;
+ left = qMin(rect.left(), left);
+ right = qMax(rect.right(), right);
+ top = qMin(rect.top(), top);
+ bottom = qMax(rect.bottom(), bottom);
+ d->qt_rgn->updateInnerRect(rect);
+ }
+ d->qt_rgn->extents = QRect(QPoint(left, top), QPoint(right, bottom));
+ }
+}
+
+/*
+ Returns true if the region is equal to \a r; otherwise returns
+ false.
+*/
+
+bool QRegion::operator==(const QRegion &r) const
+{
+ if (!d->qt_rgn || !r.d->qt_rgn)
+ return r.d->qt_rgn == d->qt_rgn;
+
+ if (d == r.d)
+ return true;
+ else
+ return EqualRegion(d->qt_rgn, r.d->qt_rgn);
+}
+
+#ifdef QT_GREENPHONE_OPT
+bool QRegion::isRect() const
+{
+ return d->qt_rgn && d->qt_rgn->mode == QRegionPrivate::Single;
+}
+#endif
+
+QT_END_NAMESPACE
diff --git a/src/gui/painting/qregion_win.cpp b/src/gui/painting/qregion_win.cpp
new file mode 100644
index 0000000000..1fab97b185
--- /dev/null
+++ b/src/gui/painting/qregion_win.cpp
@@ -0,0 +1,576 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qbitmap.h"
+#include "qbuffer.h"
+#include "qimage.h"
+#include "qpolygon.h"
+#include "qregion.h"
+#include "qt_windows.h"
+
+QT_BEGIN_NAMESPACE
+
+
+/*
+ In Windows versions before Windows Vista CreateRectRgn - when called in a multi-threaded
+ environment - might return an invalid handle. This function works around this limitation
+ by verifying the handle with a quick GetRegionData() call and re-creates the region
+ if necessary.
+*/
+HRGN qt_tryCreateRegion(QRegion::RegionType type, int left, int top, int right, int bottom)
+{
+ const int tries = 10;
+ for (int i = 0; i < tries; ++i) {
+ HRGN region;
+ switch (type) {
+ case QRegion::Rectangle:
+ region = CreateRectRgn(left, top, right, bottom);
+ break;
+ case QRegion::Ellipse:
+#ifndef Q_OS_WINCE
+ region = CreateEllipticRgn(left, top, right, bottom);
+#endif
+ break;
+ }
+ if (region) {
+ if (GetRegionData(region, 0, 0))
+ return region;
+ else
+ DeleteObject(region);
+ }
+ }
+ return 0;
+}
+
+#ifndef Q_OS_WINCE
+HRGN qt_tryCreatePolygonRegion(const QPolygon &a, Qt::FillRule fillRule)
+{
+ const int tries = 10;
+ for (int i = 0; i < tries; ++i) {
+ HRGN region = CreatePolygonRgn(reinterpret_cast<const POINT*>(a.data()), a.size(),
+ fillRule == Qt::OddEvenFill ? ALTERNATE : WINDING);
+ if (region) {
+ if (GetRegionData(region, 0, 0))
+ return region;
+ else
+ DeleteObject(region);
+ }
+ }
+ return 0;
+}
+#endif
+
+QRegion::QRegionData QRegion::shared_empty = { Q_BASIC_ATOMIC_INITIALIZER(1), 0 };
+
+QRegion::QRegion()
+ : d(&shared_empty)
+{
+ d->ref.ref();
+}
+
+#ifndef Q_OS_WINCE //implementation for WindowsCE in qregion_wce.cpp
+QRegion::QRegion(const QRect &r, RegionType t)
+{
+ if (r.isEmpty()) {
+ d = &shared_empty;
+ d->ref.ref();
+ } else {
+ d = new QRegionData;
+ d->ref = 1;
+ if (t == Rectangle)
+ d->rgn = qt_tryCreateRegion(t, r.left(), r.top(), r.x() + r.width(), r.y() + r.height());
+ else if (t == Ellipse) {
+ // need to add 1 to width/height for the ellipse to have correct boundingrect.
+ d->rgn = qt_tryCreateRegion(t, r.x(), r.y(), r.x() + r.width() + 1, r.y() + r.height() + 1);
+ }
+ }
+}
+#endif
+
+#ifndef Q_OS_WINCE //implementation for WindowsCE in qregion_wce.cpp
+QRegion::QRegion(const QPolygon &a, Qt::FillRule fillRule)
+{
+ if (a.size() < 3) {
+ d = &shared_empty;
+ d->ref.ref();
+ } else {
+ d = new QRegionData;
+ d->ref = 1;
+ d->rgn = qt_tryCreatePolygonRegion(a, fillRule);
+ }
+}
+#endif
+
+QRegion::QRegion(const QRegion &r)
+{
+ d = r.d;
+ d->ref.ref();
+}
+
+HRGN qt_win_bitmapToRegion(const QBitmap& bitmap)
+{
+ HRGN region=0;
+ QImage image = bitmap.toImage();
+ const int MAXRECT = 256;
+ struct RData {
+ RGNDATAHEADER header;
+ RECT rect[MAXRECT];
+ };
+ RData data;
+
+#define FlushSpans \
+ { \
+ data.header.dwSize = sizeof(RGNDATAHEADER); \
+ data.header.iType = RDH_RECTANGLES; \
+ data.header.nCount = n; \
+ data.header.nRgnSize = 0; \
+ data.header.rcBound.bottom = y; \
+ HRGN r = ExtCreateRegion(0, \
+ sizeof(RGNDATAHEADER)+n*sizeof(RECT),(RGNDATA*)&data); \
+ if (region) { \
+ CombineRgn(region, region, r, RGN_OR); \
+ DeleteObject(r); \
+ } else { \
+ region = r; \
+ } \
+ data.header.rcBound.top = y; \
+ }
+
+#define AddSpan \
+ { \
+ data.rect[n].left=prev1; \
+ data.rect[n].top=y; \
+ data.rect[n].right=x-1+1; \
+ data.rect[n].bottom=y+1; \
+ n++; \
+ if (n == MAXRECT) { \
+ FlushSpans \
+ n=0; \
+ } \
+ }
+
+ data.header.rcBound.top = 0;
+ data.header.rcBound.left = 0;
+ data.header.rcBound.right = image.width()-1;
+ int n = 0;
+
+ int zero = 0x00;
+
+ int x, y;
+ for (y = 0; y < image.height(); ++y) {
+ uchar *line = image.scanLine(y);
+ int w = image.width();
+ uchar all=zero;
+ int prev1 = -1;
+ for (x = 0; x < w;) {
+ uchar byte = line[x/8];
+ if (x > w - 8 || byte != all) {
+ for (int b = 8; b > 0 && x < w; --b) {
+ if (!(byte & 0x01) == !all) {
+ // More of the same
+ } else {
+ // A change.
+ if (all != zero) {
+ AddSpan;
+ all = zero;
+ } else {
+ prev1 = x;
+ all = ~zero;
+ }
+ }
+ byte >>= 1;
+ ++x;
+ }
+ } else {
+ x += 8;
+ }
+ }
+ if (all != zero) {
+ AddSpan;
+ }
+ }
+ if (n) {
+ FlushSpans;
+ }
+
+ if (!region) {
+ // Surely there is some better way.
+ region = qt_tryCreateRegion(QRegion::Rectangle, 0,0,1,1);
+ CombineRgn(region, region, region, RGN_XOR);
+ }
+ return region;
+}
+
+#ifndef Q_OS_WINCE //implementation for WindowsCE in qregion_wce.cpp
+QRegion::QRegion(const QBitmap &bm)
+{
+ if (bm.isNull()) {
+ d = &shared_empty;
+ d->ref.ref();
+ } else {
+ d = new QRegionData;
+ d->ref = 1;
+ d->rgn = qt_win_bitmapToRegion(bm);
+ }
+}
+#endif
+
+void QRegion::cleanUp(QRegion::QRegionData *x)
+{
+ if (x->rgn)
+ DeleteObject(x->rgn);
+ delete x;
+}
+
+QRegion::~QRegion()
+{
+ if (!d->ref.deref())
+ cleanUp(d);
+}
+
+QRegion &QRegion::operator=(const QRegion &r)
+{
+ r.d->ref.ref();
+ if (!d->ref.deref())
+ cleanUp(d);
+ d = r.d;
+ return *this;
+}
+
+
+QRegion QRegion::copy() const
+{
+ QRegion r;
+ QRegionData *x = new QRegionData;
+ x->ref = 1;
+ if (d->rgn) {
+ x->rgn = qt_tryCreateRegion(QRegion::Rectangle, 0, 0, 2, 2);
+ CombineRgn(x->rgn, d->rgn, 0, RGN_COPY);
+ } else {
+ x->rgn = 0;
+ }
+ if (!r.d->ref.deref())
+ cleanUp(r.d);
+ r.d = x;
+ return r;
+}
+
+bool QRegion::isEmpty() const
+{
+ return (d == &shared_empty || boundingRect().isEmpty());
+}
+
+
+bool QRegion::contains(const QPoint &p) const
+{
+ return d->rgn ? PtInRegion(d->rgn, p.x(), p.y()) : false;
+}
+
+bool QRegion::contains(const QRect &r) const
+{
+ if (!d->rgn)
+ return false;
+ RECT rect;
+ SetRect(&rect, r.left(), r.top(), r.right(), r.bottom());
+ return RectInRegion(d->rgn, &rect);
+}
+
+
+void QRegion::translate(int dx, int dy)
+{
+ if (!d->rgn || (dx == 0 && dy == 0))
+ return;
+ detach();
+ OffsetRgn(d->rgn, dx, dy);
+}
+
+
+#define RGN_NOP -1
+
+// Duplicates of those in qregion.cpp
+#define QRGN_OR 6
+#define QRGN_AND 7
+#define QRGN_SUB 8
+#define QRGN_XOR 9
+
+/*
+ Performs the actual OR, AND, SUB and XOR operation between regions.
+ Sets the resulting region handle to 0 to indicate an empty region.
+*/
+
+QRegion QRegion::winCombine(const QRegion &r, int op) const
+{
+ int both=RGN_NOP,
+ left=RGN_NOP,
+ right=RGN_NOP;
+ switch (op) {
+ case QRGN_OR:
+ both = RGN_OR;
+ left = right = RGN_COPY;
+ break;
+ case QRGN_AND:
+ both = RGN_AND;
+ break;
+ case QRGN_SUB:
+ both = RGN_DIFF;
+ left = RGN_COPY;
+ break;
+ case QRGN_XOR:
+ both = RGN_XOR;
+ left = right = RGN_COPY;
+ break;
+ default:
+ qWarning("QRegion: Internal error in winCombine");
+ }
+
+ int allCombineRgnResults = NULLREGION;
+ QRegion result;
+ result.detach();
+ result.d->rgn = qt_tryCreateRegion(QRegion::Rectangle, 0, 0, 0, 0);
+ if (d->rgn && r.d->rgn)
+ allCombineRgnResults = CombineRgn(result.d->rgn, d->rgn, r.d->rgn, both);
+ else if (d->rgn && left != RGN_NOP)
+ allCombineRgnResults = CombineRgn(result.d->rgn, d->rgn, d->rgn, left);
+ else if (r.d->rgn && right != RGN_NOP)
+ allCombineRgnResults = CombineRgn(result.d->rgn, r.d->rgn, r.d->rgn, right);
+
+ if (allCombineRgnResults == NULLREGION || allCombineRgnResults == ERROR)
+ result = QRegion();
+
+ //##### do not delete this. A null pointer is different from an empty region in SelectClipRgn in qpainter_win! (M)
+// if (allCombineRgnResults == NULLREGION) {
+// if (result.data->rgn)
+// DeleteObject(result.data->rgn);
+// result.data->rgn = 0; // empty region
+// }
+ return result;
+}
+
+QRegion QRegion::unite(const QRegion &r) const
+{
+ if (!d->rgn)
+ return r;
+ if (!r.d->rgn)
+ return *this;
+ return winCombine(r, QRGN_OR);
+}
+
+QRegion QRegion::unite(const QRect &r) const
+{
+ return unite(QRegion(r));
+}
+
+QRegion QRegion::intersect(const QRegion &r) const
+{
+ if (!r.d->rgn || !d->rgn)
+ return QRegion();
+ return winCombine(r, QRGN_AND);
+}
+
+QRegion QRegion::subtract(const QRegion &r) const
+{
+ if (!r.d->rgn || !d->rgn)
+ return *this;
+ return winCombine(r, QRGN_SUB);
+}
+
+QRegion QRegion::eor(const QRegion &r) const
+{
+ if (!d->rgn)
+ return r;
+ if (!r.d->rgn)
+ return *this;
+ return winCombine(r, QRGN_XOR);
+}
+
+
+QRect QRegion::boundingRect() const
+{
+ if (!d->rgn)
+ return QRect();
+ RECT r;
+ if (GetRgnBox(d->rgn, &r) == NULLREGION)
+ return QRect();
+ else
+ return QRect(r.left, r.top, r.right - r.left, r.bottom - r.top);
+}
+
+QVector<QRect> QRegion::rects() const
+{
+ if (d->rgn == 0)
+ return QVector<QRect>();
+
+ int numBytes = GetRegionData(d->rgn, 0, 0);
+ if (numBytes == 0)
+ return QVector<QRect>();
+
+ char *buf = new char[numBytes];
+ if (buf == 0)
+ return QVector<QRect>();
+
+ RGNDATA *rd = reinterpret_cast<RGNDATA*>(buf);
+ if (GetRegionData(d->rgn, numBytes, rd) == 0) {
+ delete [] buf;
+ return QVector<QRect>();
+ }
+
+ QVector<QRect> a(rd->rdh.nCount);
+ RECT *r = reinterpret_cast<RECT*>(rd->Buffer);
+ for (int i = 0; i < a.size(); ++i) {
+ a[i].setCoords(r->left, r->top, r->right - 1, r->bottom - 1);
+ ++r;
+ }
+
+ delete [] buf;
+ return a;
+}
+
+void QRegion::setRects(const QRect *rects, int num)
+{
+ *this = QRegion();
+ if (!rects || num == 0 || (num == 1 && rects->isEmpty()))
+ return;
+ for (int i = 0; i < num; ++i)
+ *this |= rects[i];
+}
+
+int QRegion::numRects() const
+{
+ if (d->rgn == 0)
+ return 0;
+
+ const int numBytes = GetRegionData(d->rgn, 0, 0);
+ if (numBytes == 0)
+ return 0;
+
+ char *buf = new char[numBytes];
+ if (buf == 0)
+ return 0;
+
+ RGNDATA *rd = reinterpret_cast<RGNDATA*>(buf);
+ if (GetRegionData(d->rgn, numBytes, rd) == 0) {
+ delete[] buf;
+ return 0;
+ }
+
+ const int n = rd->rdh.nCount;
+ delete[] buf;
+
+ return n;
+}
+
+bool QRegion::operator==(const QRegion &r) const
+{
+ if (d == r.d)
+ return true;
+ if ((d->rgn == 0) ^ (r.d->rgn == 0)) // one is empty, not both
+ return false;
+ return d->rgn == 0 ? true // both empty
+ : EqualRgn(d->rgn, r.d->rgn); // both non-empty
+}
+
+QRegion& QRegion::operator+=(const QRegion &r)
+{
+ if (!r.d->rgn)
+ return *this;
+
+ if (!d->rgn) {
+ *this = r;
+ return *this;
+ }
+
+ detach();
+
+ int result;
+ result = CombineRgn(d->rgn, d->rgn, r.d->rgn, RGN_OR);
+ if (result == NULLREGION || result == ERROR)
+ *this = QRegion();
+
+ return *this;
+}
+
+QRegion& QRegion::operator-=(const QRegion &r)
+{
+ if (!r.d->rgn || !d->rgn)
+ return *this;
+
+ detach();
+
+ int result;
+ result = CombineRgn(d->rgn, d->rgn, r.d->rgn, RGN_DIFF);
+ if (result == NULLREGION || result == ERROR)
+ *this = QRegion();
+
+ return *this;
+}
+
+QRegion& QRegion::operator&=(const QRegion &r)
+{
+ if (!d->rgn)
+ return *this;
+
+ if (!r.d->rgn) {
+ *this = QRegion();
+ return *this;
+ }
+
+ detach();
+
+ int result;
+ result = CombineRgn(d->rgn, d->rgn, r.d->rgn, RGN_AND);
+ if (result == NULLREGION || result == ERROR)
+ *this = QRegion();
+
+ return *this;
+}
+
+bool qt_region_strictContains(const QRegion &region, const QRect &rect)
+{
+ Q_UNUSED(region);
+ Q_UNUSED(rect);
+ return false;
+}
+
+void QRegion::ensureHandle() const
+{
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/painting/qregion_wince.cpp b/src/gui/painting/qregion_wince.cpp
new file mode 100644
index 0000000000..5adc9439b5
--- /dev/null
+++ b/src/gui/painting/qregion_wince.cpp
@@ -0,0 +1,119 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qatomic.h"
+#include "qbitmap.h"
+#include "qbuffer.h"
+#include "qimage.h"
+#include "qpolygon.h"
+#include "qregion.h"
+#include "qt_windows.h"
+#include "qpainterpath.h"
+#include "qguifunctions_wince.h"
+
+QT_BEGIN_NAMESPACE
+
+QRegion::QRegionData QRegion::shared_empty = { Q_BASIC_ATOMIC_INITIALIZER(1), 0, 0 };
+
+HRGN qt_tryCreateRegion(QRegion::RegionType type, int left, int top, int right, int bottom)
+{
+ const int tries = 10;
+ for (int i = 0; i < tries; ++i) {
+ HRGN region;
+ switch (type) {
+ case QRegion::Rectangle:
+ region = CreateRectRgn(left, top, right, bottom);
+ break;
+ case QRegion::Ellipse:
+#ifndef Q_OS_WINCE
+ region = CreateEllipticRgn(left, top, right, bottom);
+#endif
+ break;
+ }
+ if (region) {
+ if (GetRegionData(region, 0, 0))
+ return region;
+ else
+ DeleteObject(region);
+ }
+ }
+ return 0;
+}
+
+void qt_win_dispose_rgn(HRGN r)
+{
+ if (r)
+ DeleteObject(r);
+}
+
+static void qt_add_rect(HRGN &winRegion, QRect r)
+{
+ HRGN rgn = CreateRectRgn(r.left(), r.top(), r.x() + r.width(), r.y() + r.height());
+ if (rgn) {
+ HRGN dest = CreateRectRgn(0,0,0,0);
+ int result = CombineRgn(dest, winRegion, rgn, RGN_OR);
+ if (result) {
+ DeleteObject(winRegion);
+ winRegion = dest;
+ }
+ }
+}
+
+void QRegion::ensureHandle() const
+{
+ if (d->rgn)
+ DeleteObject(d->rgn);
+ d->rgn = CreateRectRgn(0,0,0,0);
+ if (d->qt_rgn) {
+ if (d->qt_rgn->numRects == 1) {
+ QRect r = d->qt_rgn->extents;
+ qt_add_rect(d->rgn, r);
+ return;
+ }
+ for (int i = 0;i < d->qt_rgn->numRects;i++) {
+ QRect r = d->qt_rgn->rects.at(i);
+ qt_add_rect(d->rgn, r);
+ }
+ }
+}
+
+
+QT_END_NAMESPACE
diff --git a/src/gui/painting/qregion_x11.cpp b/src/gui/painting/qregion_x11.cpp
new file mode 100644
index 0000000000..77718b7a10
--- /dev/null
+++ b/src/gui/painting/qregion_x11.cpp
@@ -0,0 +1,92 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <private/qt_x11_p.h>
+
+#include <limits.h>
+
+QT_BEGIN_NAMESPACE
+
+QRegion::QRegionData QRegion::shared_empty = {Q_BASIC_ATOMIC_INITIALIZER(1), 0, 0, 0};
+
+void QRegion::updateX11Region() const
+{
+ d->rgn = XCreateRegion();
+ if (!d->qt_rgn)
+ return;
+
+ int n = d->qt_rgn->numRects;
+ const QRect *rect = (n == 1 ? &d->qt_rgn->extents : d->qt_rgn->rects.constData());
+ while (n--) {
+ XRectangle r;
+ r.x = qMax(SHRT_MIN, rect->x());
+ r.y = qMax(SHRT_MIN, rect->y());
+ r.width = qMin((int)USHRT_MAX, rect->width());
+ r.height = qMin((int)USHRT_MAX, rect->height());
+ XUnionRectWithRegion(&r, d->rgn, d->rgn);
+ ++rect;
+ }
+}
+
+void *QRegion::clipRectangles(int &num) const
+{
+ if (!d->xrectangles && !(d == &shared_empty || d->qt_rgn->numRects == 0)) {
+ XRectangle *r = static_cast<XRectangle*>(malloc(d->qt_rgn->numRects * sizeof(XRectangle)));
+ d->xrectangles = r;
+ int n = d->qt_rgn->numRects;
+ const QRect *rect = (n == 1 ? &d->qt_rgn->extents : d->qt_rgn->rects.constData());
+ while (n--) {
+ r->x = qMax(SHRT_MIN, rect->x());
+ r->y = qMax(SHRT_MIN, rect->y());
+ r->width = qMin((int)USHRT_MAX, rect->width());
+ r->height = qMin((int)USHRT_MAX, rect->height());
+ ++r;
+ ++rect;
+ }
+ }
+ if (d == &shared_empty || d->qt_rgn->numRects == 0)
+ num = 0;
+ else
+ num = d->qt_rgn->numRects;
+ return d->xrectangles;
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/painting/qrgb.h b/src/gui/painting/qrgb.h
new file mode 100644
index 0000000000..25218c682a
--- /dev/null
+++ b/src/gui/painting/qrgb.h
@@ -0,0 +1,88 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QRGB_H
+#define QRGB_H
+
+#include <QtCore/qglobal.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+typedef unsigned int QRgb; // RGB triplet
+
+const QRgb RGB_MASK = 0x00ffffff; // masks RGB values
+
+Q_GUI_EXPORT_INLINE int qRed(QRgb rgb) // get red part of RGB
+{ return ((rgb >> 16) & 0xff); }
+
+Q_GUI_EXPORT_INLINE int qGreen(QRgb rgb) // get green part of RGB
+{ return ((rgb >> 8) & 0xff); }
+
+Q_GUI_EXPORT_INLINE int qBlue(QRgb rgb) // get blue part of RGB
+{ return (rgb & 0xff); }
+
+Q_GUI_EXPORT_INLINE int qAlpha(QRgb rgb) // get alpha part of RGBA
+{ return ((rgb >> 24) & 0xff); }
+
+Q_GUI_EXPORT_INLINE QRgb qRgb(int r, int g, int b)// set RGB value
+{ return (0xffu << 24) | ((r & 0xff) << 16) | ((g & 0xff) << 8) | (b & 0xff); }
+
+Q_GUI_EXPORT_INLINE QRgb qRgba(int r, int g, int b, int a)// set RGBA value
+{ return ((a & 0xff) << 24) | ((r & 0xff) << 16) | ((g & 0xff) << 8) | (b & 0xff); }
+
+Q_GUI_EXPORT_INLINE int qGray(int r, int g, int b)// convert R,G,B to gray 0..255
+{ return (r*11+g*16+b*5)/32; }
+
+Q_GUI_EXPORT_INLINE int qGray(QRgb rgb) // convert RGB to gray 0..255
+{ return qGray(qRed(rgb), qGreen(rgb), qBlue(rgb)); }
+
+Q_GUI_EXPORT_INLINE bool qIsGray(QRgb rgb)
+{ return qRed(rgb) == qGreen(rgb) && qRed(rgb) == qBlue(rgb); }
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QRGB_H
diff --git a/src/gui/painting/qstroker.cpp b/src/gui/painting/qstroker.cpp
new file mode 100644
index 0000000000..b894c62341
--- /dev/null
+++ b/src/gui/painting/qstroker.cpp
@@ -0,0 +1,1136 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "private/qstroker_p.h"
+#include "private/qbezier_p.h"
+#include "private/qmath_p.h"
+#include "qline.h"
+#include "qtransform.h"
+#include <qmath.h>
+
+QT_BEGIN_NAMESPACE
+
+// #define QPP_STROKE_DEBUG
+
+class QSubpathForwardIterator
+{
+public:
+ QSubpathForwardIterator(const QDataBuffer<QStrokerOps::Element> *path)
+ : m_path(path), m_pos(0) { }
+ inline int position() const { return m_pos; }
+ inline bool hasNext() const { return m_pos < m_path->size(); }
+ inline QStrokerOps::Element next() { Q_ASSERT(hasNext()); return m_path->at(m_pos++); }
+
+private:
+ const QDataBuffer<QStrokerOps::Element> *m_path;
+ int m_pos;
+};
+
+class QSubpathBackwardIterator
+{
+public:
+ QSubpathBackwardIterator(const QDataBuffer<QStrokerOps::Element> *path)
+ : m_path(path), m_pos(path->size() - 1) { }
+
+ inline int position() const { return m_pos; }
+
+ inline bool hasNext() const { return m_pos >= 0; }
+
+ inline QStrokerOps::Element next()
+ {
+ Q_ASSERT(hasNext());
+
+ QStrokerOps::Element ce = m_path->at(m_pos); // current element
+
+ if (m_pos == m_path->size() - 1) {
+ --m_pos;
+ ce.type = QPainterPath::MoveToElement;
+ return ce;
+ }
+
+ const QStrokerOps::Element &pe = m_path->at(m_pos + 1); // previous element
+
+ switch (pe.type) {
+ case QPainterPath::LineToElement:
+ ce.type = QPainterPath::LineToElement;
+ break;
+ case QPainterPath::CurveToDataElement:
+ // First control point?
+ if (ce.type == QPainterPath::CurveToElement) {
+ ce.type = QPainterPath::CurveToDataElement;
+ } else { // Second control point then
+ ce.type = QPainterPath::CurveToElement;
+ }
+ break;
+ case QPainterPath::CurveToElement:
+ ce.type = QPainterPath::CurveToDataElement;
+ break;
+ default:
+ qWarning("QSubpathReverseIterator::next: Case %d unhandled", ce.type);
+ break;
+ }
+ --m_pos;
+
+ return ce;
+ }
+
+private:
+ const QDataBuffer<QStrokerOps::Element> *m_path;
+ int m_pos;
+};
+
+class QSubpathFlatIterator
+{
+public:
+ QSubpathFlatIterator(const QDataBuffer<QStrokerOps::Element> *path)
+ : m_path(path), m_pos(0), m_curve_index(-1) { }
+
+ inline bool hasNext() const { return m_curve_index >= 0 || m_pos < m_path->size(); }
+
+ QStrokerOps::Element next()
+ {
+ Q_ASSERT(hasNext());
+
+ if (m_curve_index >= 0) {
+ QStrokerOps::Element e = { QPainterPath::LineToElement,
+ qt_real_to_fixed(m_curve.at(m_curve_index).x()),
+ qt_real_to_fixed(m_curve.at(m_curve_index).y())
+ };
+ ++m_curve_index;
+ if (m_curve_index >= m_curve.size())
+ m_curve_index = -1;
+ return e;
+ }
+
+ QStrokerOps::Element e = m_path->at(m_pos);
+ if (e.isCurveTo()) {
+ Q_ASSERT(m_pos > 0);
+ Q_ASSERT(m_pos < m_path->size());
+
+ m_curve = QBezier::fromPoints(QPointF(qt_fixed_to_real(m_path->at(m_pos-1).x),
+ qt_fixed_to_real(m_path->at(m_pos-1).y)),
+ QPointF(qt_fixed_to_real(e.x),
+ qt_fixed_to_real(e.y)),
+ QPointF(qt_fixed_to_real(m_path->at(m_pos+1).x),
+ qt_fixed_to_real(m_path->at(m_pos+1).y)),
+ QPointF(qt_fixed_to_real(m_path->at(m_pos+2).x),
+ qt_fixed_to_real(m_path->at(m_pos+2).y))).toPolygon();
+ m_curve_index = 1;
+ e.type = QPainterPath::LineToElement;
+ e.x = m_curve.at(0).x();
+ e.y = m_curve.at(0).y();
+ m_pos += 2;
+ }
+ Q_ASSERT(e.isLineTo() || e.isMoveTo());
+ ++m_pos;
+ return e;
+ }
+
+private:
+ const QDataBuffer<QStrokerOps::Element> *m_path;
+ int m_pos;
+ QPolygonF m_curve;
+ int m_curve_index;
+};
+
+template <class Iterator> bool qt_stroke_side(Iterator *it, QStroker *stroker,
+ bool capFirst, QLineF *startTangent);
+
+/*******************************************************************************
+ * QLineF::angle gives us the smalles angle between two lines. Here we
+ * want to identify the line's angle direction on the unit circle.
+ */
+static inline qreal adapted_angle_on_x(const QLineF &line)
+{
+ qreal angle = line.angle(QLineF(0, 0, 1, 0));
+ if (line.dy() > 0)
+ angle = 360 - angle;
+ return angle;
+}
+
+QStrokerOps::QStrokerOps()
+ : m_customData(0), m_moveTo(0), m_lineTo(0), m_cubicTo(0)
+{
+}
+
+QStrokerOps::~QStrokerOps()
+{
+}
+
+
+/*!
+ Prepares the stroker. Call this function once before starting a
+ stroke by calling moveTo, lineTo or cubicTo.
+
+ The \a customData is passed back through that callback functions
+ and can be used by the user to for instance maintain state
+ information.
+*/
+void QStrokerOps::begin(void *customData)
+{
+ m_customData = customData;
+ m_elements.reset();
+}
+
+
+/*!
+ Finishes the stroke. Call this function once when an entire
+ primitive has been stroked.
+*/
+void QStrokerOps::end()
+{
+ if (m_elements.size() > 1)
+ processCurrentSubpath();
+ m_customData = 0;
+}
+
+/*!
+ Convenience function that decomposes \a path into begin(),
+ moveTo(), lineTo(), curevTo() and end() calls.
+
+ The \a customData parameter is used in the callback functions
+
+ The \a matrix is used to transform the points before input to the
+ stroker.
+
+ \sa begin()
+*/
+void QStrokerOps::strokePath(const QPainterPath &path, void *customData, const QTransform &matrix)
+{
+ if (path.isEmpty())
+ return;
+
+ begin(customData);
+ int count = path.elementCount();
+ if (matrix.isIdentity()) {
+ for (int i=0; i<count; ++i) {
+ const QPainterPath::Element &e = path.elementAt(i);
+ switch (e.type) {
+ case QPainterPath::MoveToElement:
+ moveTo(qt_real_to_fixed(e.x), qt_real_to_fixed(e.y));
+ break;
+ case QPainterPath::LineToElement:
+ lineTo(qt_real_to_fixed(e.x), qt_real_to_fixed(e.y));
+ break;
+ case QPainterPath::CurveToElement:
+ {
+ const QPainterPath::Element &cp2 = path.elementAt(++i);
+ const QPainterPath::Element &ep = path.elementAt(++i);
+ cubicTo(qt_real_to_fixed(e.x), qt_real_to_fixed(e.y),
+ qt_real_to_fixed(cp2.x), qt_real_to_fixed(cp2.y),
+ qt_real_to_fixed(ep.x), qt_real_to_fixed(ep.y));
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ } else {
+ for (int i=0; i<count; ++i) {
+ const QPainterPath::Element &e = path.elementAt(i);
+ QPointF pt = QPointF(e.x, e.y) * matrix;
+ switch (e.type) {
+ case QPainterPath::MoveToElement:
+ moveTo(qt_real_to_fixed(pt.x()), qt_real_to_fixed(pt.y()));
+ break;
+ case QPainterPath::LineToElement:
+ lineTo(qt_real_to_fixed(pt.x()), qt_real_to_fixed(pt.y()));
+ break;
+ case QPainterPath::CurveToElement:
+ {
+ QPointF cp2 = ((QPointF) path.elementAt(++i)) * matrix;
+ QPointF ep = ((QPointF) path.elementAt(++i)) * matrix;
+ cubicTo(qt_real_to_fixed(pt.x()), qt_real_to_fixed(pt.y()),
+ qt_real_to_fixed(cp2.x()), qt_real_to_fixed(cp2.y()),
+ qt_real_to_fixed(ep.x()), qt_real_to_fixed(ep.y()));
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ end();
+}
+
+/*!
+ Convenience function for stroking a polygon of the \a pointCount
+ first points in \a points. If \a implicit_close is set to true a
+ line is implictly drawn between the first and last point in the
+ polygon. Typically true for polygons and false for polylines.
+
+ The \a matrix is used to transform the points before they enter the
+ stroker.
+
+ \sa begin()
+*/
+
+void QStrokerOps::strokePolygon(const QPointF *points, int pointCount, bool implicit_close,
+ void *data, const QTransform &matrix)
+{
+ if (!pointCount)
+ return;
+ begin(data);
+ if (matrix.isIdentity()) {
+ moveTo(qt_real_to_fixed(points[0].x()), qt_real_to_fixed(points[0].y()));
+ for (int i=1; i<pointCount; ++i)
+ lineTo(qt_real_to_fixed(points[i].x()),
+ qt_real_to_fixed(points[i].y()));
+ if (implicit_close)
+ lineTo(qt_real_to_fixed(points[0].x()), qt_real_to_fixed(points[0].y()));
+ } else {
+ QPointF start = points[0] * matrix;
+ moveTo(qt_real_to_fixed(start.x()), qt_real_to_fixed(start.y()));
+ for (int i=1; i<pointCount; ++i) {
+ QPointF pt = points[i] * matrix;
+ lineTo(qt_real_to_fixed(pt.x()), qt_real_to_fixed(pt.y()));
+ }
+ if (implicit_close)
+ lineTo(qt_real_to_fixed(start.x()), qt_real_to_fixed(start.y()));
+ }
+ end();
+}
+
+/*!
+ Convenience function for stroking an ellipse with bounding rect \a
+ rect. The \a matrix is used to transform the coordinates before
+ they enter the stroker.
+*/
+void QStrokerOps::strokeEllipse(const QRectF &rect, void *data, const QTransform &matrix)
+{
+ int count = 0;
+ QPointF pts[12];
+ QPointF start = qt_curves_for_arc(rect, 0, -360, pts, &count);
+ Q_ASSERT(count == 12); // a perfect circle..
+
+ if (!matrix.isIdentity()) {
+ start = start * matrix;
+ for (int i=0; i<12; ++i) {
+ pts[i] = pts[i] * matrix;
+ }
+ }
+
+ begin(data);
+ moveTo(qt_real_to_fixed(start.x()), qt_real_to_fixed(start.y()));
+ for (int i=0; i<12; i+=3) {
+ cubicTo(qt_real_to_fixed(pts[i].x()), qt_real_to_fixed(pts[i].y()),
+ qt_real_to_fixed(pts[i+1].x()), qt_real_to_fixed(pts[i+1].y()),
+ qt_real_to_fixed(pts[i+2].x()), qt_real_to_fixed(pts[i+2].y()));
+ }
+ end();
+}
+
+
+QStroker::QStroker()
+ : m_capStyle(SquareJoin), m_joinStyle(FlatJoin),
+ m_back1X(0), m_back1Y(0),
+ m_back2X(0), m_back2Y(0)
+{
+ m_strokeWidth = qt_real_to_fixed(1);
+ m_miterLimit = qt_real_to_fixed(2);
+ m_curveThreshold = qt_real_to_fixed(0.25);
+}
+
+QStroker::~QStroker()
+{
+
+}
+
+Qt::PenCapStyle QStroker::capForJoinMode(LineJoinMode mode)
+{
+ if (mode == FlatJoin) return Qt::FlatCap;
+ else if (mode == SquareJoin) return Qt::SquareCap;
+ else return Qt::RoundCap;
+}
+
+QStroker::LineJoinMode QStroker::joinModeForCap(Qt::PenCapStyle style)
+{
+ if (style == Qt::FlatCap) return FlatJoin;
+ else if (style == Qt::SquareCap) return SquareJoin;
+ else return RoundCap;
+}
+
+Qt::PenJoinStyle QStroker::joinForJoinMode(LineJoinMode mode)
+{
+ if (mode == FlatJoin) return Qt::BevelJoin;
+ else if (mode == MiterJoin) return Qt::MiterJoin;
+ else if (mode == SvgMiterJoin) return Qt::SvgMiterJoin;
+ else return Qt::RoundJoin;
+}
+
+QStroker::LineJoinMode QStroker::joinModeForJoin(Qt::PenJoinStyle joinStyle)
+{
+ if (joinStyle == Qt::BevelJoin) return FlatJoin;
+ else if (joinStyle == Qt::MiterJoin) return MiterJoin;
+ else if (joinStyle == Qt::SvgMiterJoin) return SvgMiterJoin;
+ else return RoundJoin;
+}
+
+
+/*!
+ This function is called to stroke the currently built up
+ subpath. The subpath is cleared when the function completes.
+*/
+void QStroker::processCurrentSubpath()
+{
+ Q_ASSERT(!m_elements.isEmpty());
+ Q_ASSERT(m_elements.first().type == QPainterPath::MoveToElement);
+ Q_ASSERT(m_elements.size() > 1);
+
+ QSubpathForwardIterator fwit(&m_elements);
+ QSubpathBackwardIterator bwit(&m_elements);
+
+ QLineF fwStartTangent, bwStartTangent;
+
+ bool fwclosed = qt_stroke_side(&fwit, this, false, &fwStartTangent);
+ bool bwclosed = qt_stroke_side(&bwit, this, !fwclosed, &bwStartTangent);
+
+ if (!bwclosed)
+ joinPoints(m_elements.at(0).x, m_elements.at(0).y, fwStartTangent, m_capStyle);
+}
+
+
+/*!
+ \internal
+*/
+void QStroker::joinPoints(qfixed focal_x, qfixed focal_y, const QLineF &nextLine, LineJoinMode join)
+{
+#ifdef QPP_STROKE_DEBUG
+ printf(" -----> joinPoints: around=(%.0f, %.0f), next_p1=(%.0f, %.f) next_p2=(%.0f, %.f)\n",
+ qt_fixed_to_real(focal_x),
+ qt_fixed_to_real(focal_y),
+ nextLine.x1(), nextLine.y1(), nextLine.x2(), nextLine.y2());
+#endif
+ // points connected already, don't join
+
+#if !defined (QFIXED_26_6) && !defined (Q_FIXED_32_32)
+ if (qFuzzyCompare(m_back1X, nextLine.x1()) && qFuzzyCompare(m_back1Y, nextLine.y1()))
+ return;
+#else
+ if (m_back1X == qt_real_to_fixed(nextLine.x1())
+ && m_back1Y == qt_real_to_fixed(nextLine.y1())) {
+ return;
+ }
+#endif
+
+ if (join == FlatJoin) {
+ emitLineTo(qt_real_to_fixed(nextLine.x1()),
+ qt_real_to_fixed(nextLine.y1()));
+
+ } else {
+ QLineF prevLine(qt_fixed_to_real(m_back2X), qt_fixed_to_real(m_back2Y),
+ qt_fixed_to_real(m_back1X), qt_fixed_to_real(m_back1Y));
+
+ QPointF isect;
+ QLineF::IntersectType type = prevLine.intersect(nextLine, &isect);
+
+ if (join == MiterJoin) {
+ qreal appliedMiterLimit = qt_fixed_to_real(m_strokeWidth * m_miterLimit);
+
+ // If we are on the inside, do the short cut...
+ QLineF shortCut(prevLine.p2(), nextLine.p1());
+ qreal angle = shortCut.angleTo(prevLine);
+
+ if (type == QLineF::BoundedIntersection || (angle > 90 && !qFuzzyCompare(angle, (qreal)90))) {
+ emitLineTo(qt_real_to_fixed(nextLine.x1()), qt_real_to_fixed(nextLine.y1()));
+ return;
+ }
+ QLineF miterLine(QPointF(qt_fixed_to_real(m_back1X),
+ qt_fixed_to_real(m_back1Y)), isect);
+ if (type == QLineF::NoIntersection || miterLine.length() > appliedMiterLimit) {
+ QLineF l1(prevLine);
+ l1.setLength(appliedMiterLimit);
+ l1.translate(prevLine.dx(), prevLine.dy());
+
+ QLineF l2(nextLine);
+ l2.setLength(appliedMiterLimit);
+ l2.translate(-l2.dx(), -l2.dy());
+
+ emitLineTo(qt_real_to_fixed(l1.x2()), qt_real_to_fixed(l1.y2()));
+ emitLineTo(qt_real_to_fixed(l2.x1()), qt_real_to_fixed(l2.y1()));
+ emitLineTo(qt_real_to_fixed(nextLine.x1()), qt_real_to_fixed(nextLine.y1()));
+ } else {
+ emitLineTo(qt_real_to_fixed(isect.x()), qt_real_to_fixed(isect.y()));
+ emitLineTo(qt_real_to_fixed(nextLine.x1()), qt_real_to_fixed(nextLine.y1()));
+ }
+
+ } else if (join == SquareJoin) {
+ qfixed offset = m_strokeWidth / 2;
+
+ QLineF l1(prevLine);
+ l1.translate(l1.dx(), l1.dy());
+ l1.setLength(qt_fixed_to_real(offset));
+ QLineF l2(nextLine.p2(), nextLine.p1());
+ l2.translate(l2.dx(), l2.dy());
+ l2.setLength(qt_fixed_to_real(offset));
+ emitLineTo(qt_real_to_fixed(l1.x2()), qt_real_to_fixed(l1.y2()));
+ emitLineTo(qt_real_to_fixed(l2.x2()), qt_real_to_fixed(l2.y2()));
+ emitLineTo(qt_real_to_fixed(l2.x1()), qt_real_to_fixed(l2.y1()));
+
+ } else if (join == RoundJoin) {
+ qfixed offset = m_strokeWidth / 2;
+
+ QLineF shortCut(prevLine.p2(), nextLine.p1());
+ qreal angle = prevLine.angle(shortCut);
+ if (type == QLineF::BoundedIntersection || (angle > 90 && !qFuzzyCompare(angle, (qreal)90))) {
+ emitLineTo(qt_real_to_fixed(nextLine.x1()), qt_real_to_fixed(nextLine.y1()));
+ return;
+ }
+ qreal l1_on_x = adapted_angle_on_x(prevLine);
+ qreal l2_on_x = adapted_angle_on_x(nextLine);
+
+ qreal sweepLength = qAbs(l2_on_x - l1_on_x);
+
+ int point_count;
+ QPointF curves[15];
+
+ QPointF curve_start =
+ qt_curves_for_arc(QRectF(qt_fixed_to_real(focal_x - offset),
+ qt_fixed_to_real(focal_y - offset),
+ qt_fixed_to_real(offset * 2),
+ qt_fixed_to_real(offset * 2)),
+ l1_on_x + 90, -sweepLength,
+ curves, &point_count);
+
+// // line to the beginning of the arc segment, (should not be needed).
+// emitLineTo(qt_real_to_fixed(curve_start.x()), qt_real_to_fixed(curve_start.y()));
+
+ for (int i=0; i<point_count; i+=3) {
+ emitCubicTo(qt_real_to_fixed(curves[i].x()),
+ qt_real_to_fixed(curves[i].y()),
+ qt_real_to_fixed(curves[i+1].x()),
+ qt_real_to_fixed(curves[i+1].y()),
+ qt_real_to_fixed(curves[i+2].x()),
+ qt_real_to_fixed(curves[i+2].y()));
+ }
+
+ // line to the end of the arc segment, (should also not be needed).
+ emitLineTo(qt_real_to_fixed(nextLine.x1()), qt_real_to_fixed(nextLine.y1()));
+
+ // Same as round join except we know its 180 degrees. Can also optimize this
+ // later based on the addEllipse logic
+ } else if (join == RoundCap) {
+ qfixed offset = m_strokeWidth / 2;
+
+ // first control line
+ QLineF l1 = prevLine;
+ l1.translate(l1.dx(), l1.dy());
+ l1.setLength(QT_PATH_KAPPA * offset);
+
+ // second control line, find through normal between prevLine and focal.
+ QLineF l2(qt_fixed_to_real(focal_x), qt_fixed_to_real(focal_y),
+ prevLine.x2(), prevLine.y2());
+ l2.translate(-l2.dy(), l2.dx());
+ l2.setLength(QT_PATH_KAPPA * offset);
+
+ emitCubicTo(qt_real_to_fixed(l1.x2()),
+ qt_real_to_fixed(l1.y2()),
+ qt_real_to_fixed(l2.x2()),
+ qt_real_to_fixed(l2.y2()),
+ qt_real_to_fixed(l2.x1()),
+ qt_real_to_fixed(l2.y1()));
+
+ // move so that it matches
+ l2 = QLineF(l2.x1(), l2.y1(), l2.x1()-l2.dx(), l2.y1()-l2.dy());
+
+ // last line is parallel to l1 so just shift it down.
+ l1.translate(nextLine.x1() - l1.x1(), nextLine.y1() - l1.y1());
+
+ emitCubicTo(qt_real_to_fixed(l2.x2()),
+ qt_real_to_fixed(l2.y2()),
+ qt_real_to_fixed(l1.x2()),
+ qt_real_to_fixed(l1.y2()),
+ qt_real_to_fixed(l1.x1()),
+ qt_real_to_fixed(l1.y1()));
+ } else if (join == SvgMiterJoin) {
+ QLineF miterLine(QPointF(qt_fixed_to_real(focal_x),
+ qt_fixed_to_real(focal_y)), isect);
+ if (miterLine.length() > qt_fixed_to_real(m_strokeWidth * m_miterLimit) / 2) {
+ emitLineTo(qt_real_to_fixed(nextLine.x1()),
+ qt_real_to_fixed(nextLine.y1()));
+ } else {
+ emitLineTo(qt_real_to_fixed(isect.x()), qt_real_to_fixed(isect.y()));
+ emitLineTo(qt_real_to_fixed(nextLine.x1()), qt_real_to_fixed(nextLine.y1()));
+ }
+ } else {
+ Q_ASSERT(!"QStroker::joinPoints(), bad join style...");
+ }
+ }
+}
+
+
+/*
+ Strokes a subpath side using the \a it as source. Results are put into
+ \a stroke. The function returns true if the subpath side was closed.
+ If \a capFirst is true, we will use capPoints instead of joinPoints to
+ connect the first segment, other segments will be joined using joinPoints.
+ This is to put capping in order...
+*/
+template <class Iterator> bool qt_stroke_side(Iterator *it,
+ QStroker *stroker,
+ bool capFirst,
+ QLineF *startTangent)
+{
+ // Used in CurveToElement section below.
+ const int MAX_OFFSET = 16;
+ QBezier offsetCurves[MAX_OFFSET];
+
+ Q_ASSERT(it->hasNext()); // The initaial move to
+ QStrokerOps::Element first_element = it->next();
+ Q_ASSERT(first_element.isMoveTo());
+
+ qfixed2d start = first_element;
+
+#ifdef QPP_STROKE_DEBUG
+ qDebug(" -> (side) [%.2f, %.2f], startPos=%d",
+ qt_fixed_to_real(start.x),
+ qt_fixed_to_real(start.y));
+#endif
+
+ qfixed2d prev = start;
+
+ bool first = true;
+
+ qfixed offset = stroker->strokeWidth() / 2;
+
+ while (it->hasNext()) {
+ QStrokerOps::Element e = it->next();
+
+ // LineToElement
+ if (e.isLineTo()) {
+#ifdef QPP_STROKE_DEBUG
+ qDebug("\n ---> (side) lineto [%.2f, %.2f]", e.x, e.y);
+#endif
+ QLineF line(qt_fixed_to_real(prev.x), qt_fixed_to_real(prev.y),
+ qt_fixed_to_real(e.x), qt_fixed_to_real(e.y));
+ QLineF normal = line.normalVector();
+ normal.setLength(offset);
+ line.translate(normal.dx(), normal.dy());
+
+ // If we are starting a new subpath, move to correct starting point.
+ if (first) {
+ if (capFirst)
+ stroker->joinPoints(prev.x, prev.y, line, stroker->capStyleMode());
+ else
+ stroker->emitMoveTo(qt_real_to_fixed(line.x1()), qt_real_to_fixed(line.y1()));
+ *startTangent = line;
+ first = false;
+ } else {
+ stroker->joinPoints(prev.x, prev.y, line, stroker->joinStyleMode());
+ }
+
+ // Add the stroke for this line.
+ stroker->emitLineTo(qt_real_to_fixed(line.x2()),
+ qt_real_to_fixed(line.y2()));
+ prev = e;
+
+ // CurveToElement
+ } else if (e.isCurveTo()) {
+ QStrokerOps::Element cp2 = it->next(); // control point 2
+ QStrokerOps::Element ep = it->next(); // end point
+
+#ifdef QPP_STROKE_DEBUG
+ qDebug("\n ---> (side) cubicTo [%.2f, %.2f]",
+ qt_fixed_to_real(ep.x),
+ qt_fixed_to_real(ep.y));
+#endif
+
+ QBezier bezier =
+ QBezier::fromPoints(QPointF(qt_fixed_to_real(prev.x), qt_fixed_to_real(prev.y)),
+ QPointF(qt_fixed_to_real(e.x), qt_fixed_to_real(e.y)),
+ QPointF(qt_fixed_to_real(cp2.x), qt_fixed_to_real(cp2.y)),
+ QPointF(qt_fixed_to_real(ep.x), qt_fixed_to_real(ep.y)));
+
+ int count = bezier.shifted(offsetCurves,
+ MAX_OFFSET,
+ offset,
+ stroker->curveThreshold());
+
+ if (count) {
+ // If we are starting a new subpath, move to correct starting point
+ QLineF tangent = bezier.startTangent();
+ tangent.translate(offsetCurves[0].pt1() - bezier.pt1());
+ if (first) {
+ QPointF pt = offsetCurves[0].pt1();
+ if (capFirst) {
+ stroker->joinPoints(prev.x, prev.y,
+ tangent,
+ stroker->capStyleMode());
+ } else {
+ stroker->emitMoveTo(qt_real_to_fixed(pt.x()),
+ qt_real_to_fixed(pt.y()));
+ }
+ *startTangent = tangent;
+ first = false;
+ } else {
+ stroker->joinPoints(prev.x, prev.y,
+ tangent,
+ stroker->joinStyleMode());
+ }
+
+ // Add these beziers
+ for (int i=0; i<count; ++i) {
+ QPointF cp1 = offsetCurves[i].pt2();
+ QPointF cp2 = offsetCurves[i].pt3();
+ QPointF ep = offsetCurves[i].pt4();
+ stroker->emitCubicTo(qt_real_to_fixed(cp1.x()), qt_real_to_fixed(cp1.y()),
+ qt_real_to_fixed(cp2.x()), qt_real_to_fixed(cp2.y()),
+ qt_real_to_fixed(ep.x()), qt_real_to_fixed(ep.y()));
+ }
+ }
+
+ prev = ep;
+ }
+ }
+
+ if (start == prev) {
+ // closed subpath, join first and last point
+#ifdef QPP_STROKE_DEBUG
+ qDebug("\n ---> (side) closed subpath");
+#endif
+ stroker->joinPoints(prev.x, prev.y, *startTangent, stroker->joinStyleMode());
+ return true;
+ } else {
+#ifdef QPP_STROKE_DEBUG
+ qDebug("\n ---> (side) open subpath");
+#endif
+ return false;
+ }
+}
+
+/*!
+ \internal
+
+ For a given angle in the range [0 .. 90], finds the corresponding parameter t
+ of the prototype cubic bezier arc segment
+ b = fromPoints(QPointF(1, 0), QPointF(1, KAPPA), QPointF(KAPPA, 1), QPointF(0, 1));
+
+ From the bezier equation:
+ b.pointAt(t).x() = (1-t)^3 + t*(1-t)^2 + t^2*(1-t)*KAPPA
+ b.pointAt(t).y() = t*(1-t)^2 * KAPPA + t^2*(1-t) + t^3
+
+ Third degree coefficients:
+ b.pointAt(t).x() = at^3 + bt^2 + ct + d
+ where a = 2-3*KAPPA, b = 3*(KAPPA-1), c = 0, d = 1
+
+ b.pointAt(t).y() = at^3 + bt^2 + ct + d
+ where a = 3*KAPPA-2, b = 6*KAPPA+3, c = 3*KAPPA, d = 0
+
+ Newton's method to find the zero of a function:
+ given a function f(x) and initial guess x_0
+ x_1 = f(x_0) / f'(x_0)
+ x_2 = f(x_1) / f'(x_1)
+ etc...
+*/
+
+qreal qt_t_for_arc_angle(qreal angle)
+{
+ if (qFuzzyCompare(angle + 1, qreal(1)))
+ return 0;
+
+ if (qFuzzyCompare(angle, qreal(90)))
+ return 1;
+
+ qreal radians = Q_PI * angle / 180;
+ qreal cosAngle = qCos(radians);
+ qreal sinAngle = qSin(radians);
+
+ // initial guess
+ qreal tc = angle / 90;
+ // do some iterations of newton's method to approximate cosAngle
+ // finds the zero of the function b.pointAt(tc).x() - cosAngle
+ tc -= ((((2-3*QT_PATH_KAPPA) * tc + 3*(QT_PATH_KAPPA-1)) * tc) * tc + 1 - cosAngle) // value
+ / (((6-9*QT_PATH_KAPPA) * tc + 6*(QT_PATH_KAPPA-1)) * tc); // derivative
+ tc -= ((((2-3*QT_PATH_KAPPA) * tc + 3*(QT_PATH_KAPPA-1)) * tc) * tc + 1 - cosAngle) // value
+ / (((6-9*QT_PATH_KAPPA) * tc + 6*(QT_PATH_KAPPA-1)) * tc); // derivative
+
+ // initial guess
+ qreal ts = tc;
+ // do some iterations of newton's method to approximate sinAngle
+ // finds the zero of the function b.pointAt(tc).y() - sinAngle
+ ts -= ((((3*QT_PATH_KAPPA-2) * ts - 6*QT_PATH_KAPPA + 3) * ts + 3*QT_PATH_KAPPA) * ts - sinAngle)
+ / (((9*QT_PATH_KAPPA-6) * ts + 12*QT_PATH_KAPPA - 6) * ts + 3*QT_PATH_KAPPA);
+ ts -= ((((3*QT_PATH_KAPPA-2) * ts - 6*QT_PATH_KAPPA + 3) * ts + 3*QT_PATH_KAPPA) * ts - sinAngle)
+ / (((9*QT_PATH_KAPPA-6) * ts + 12*QT_PATH_KAPPA - 6) * ts + 3*QT_PATH_KAPPA);
+
+ // use the average of the t that best approximates cosAngle
+ // and the t that best approximates sinAngle
+ qreal t = 0.5 * (tc + ts);
+
+#if 0
+ printf("angle: %f, t: %f\n", angle, t);
+ qreal a, b, c, d;
+ bezierCoefficients(t, a, b, c, d);
+ printf("cosAngle: %.10f, value: %.10f\n", cosAngle, a + b + c * QT_PATH_KAPPA);
+ printf("sinAngle: %.10f, value: %.10f\n", sinAngle, b * QT_PATH_KAPPA + c + d);
+#endif
+
+ return t;
+}
+
+void qt_find_ellipse_coords(const QRectF &r, qreal angle, qreal length,
+ QPointF* startPoint, QPointF *endPoint);
+
+/*!
+ \internal
+
+ Creates a number of curves for a given arc definition. The arc is
+ defined an arc along the ellipses that fits into \a rect starting
+ at \a startAngle and an arc length of \a sweepLength.
+
+ The function has three out parameters. The return value is the
+ starting point of the arc. The \a curves array represents the list
+ of cubicTo elements up to a maximum of \a point_count. There are of course
+ 3 points pr curve.
+*/
+QPointF qt_curves_for_arc(const QRectF &rect, qreal startAngle, qreal sweepLength,
+ QPointF *curves, int *point_count)
+{
+ Q_ASSERT(point_count);
+ Q_ASSERT(curves);
+
+ *point_count = 0;
+ if (qt_is_nan(rect.x()) || qt_is_nan(rect.y()) || qt_is_nan(rect.width()) || qt_is_nan(rect.height())
+ || qt_is_nan(startAngle) || qt_is_nan(sweepLength)) {
+ qWarning("QPainterPath::arcTo: Adding arc where a parameter is NaN, results are undefined");
+ return QPointF();
+ }
+
+ if (rect.isNull()) {
+ return QPointF();
+ }
+
+ qreal x = rect.x();
+ qreal y = rect.y();
+
+ qreal w = rect.width();
+ qreal w2 = rect.width() / 2;
+ qreal w2k = w2 * QT_PATH_KAPPA;
+
+ qreal h = rect.height();
+ qreal h2 = rect.height() / 2;
+ qreal h2k = h2 * QT_PATH_KAPPA;
+
+ QPointF points[16] =
+ {
+ // start point
+ QPointF(x + w, y + h2),
+
+ // 0 -> 270 degrees
+ QPointF(x + w, y + h2 + h2k),
+ QPointF(x + w2 + w2k, y + h),
+ QPointF(x + w2, y + h),
+
+ // 270 -> 180 degrees
+ QPointF(x + w2 - w2k, y + h),
+ QPointF(x, y + h2 + h2k),
+ QPointF(x, y + h2),
+
+ // 180 -> 90 degrees
+ QPointF(x, y + h2 - h2k),
+ QPointF(x + w2 - w2k, y),
+ QPointF(x + w2, y),
+
+ // 90 -> 0 degrees
+ QPointF(x + w2 + w2k, y),
+ QPointF(x + w, y + h2 - h2k),
+ QPointF(x + w, y + h2)
+ };
+
+ if (sweepLength > 360) sweepLength = 360;
+ else if (sweepLength < -360) sweepLength = -360;
+
+ // Special case fast paths
+ if (startAngle == 0.0) {
+ if (sweepLength == 360.0) {
+ for (int i = 11; i >= 0; --i)
+ curves[(*point_count)++] = points[i];
+ return points[12];
+ } else if (sweepLength == -360.0) {
+ for (int i = 1; i <= 12; ++i)
+ curves[(*point_count)++] = points[i];
+ return points[0];
+ }
+ }
+
+ int startSegment = int(floor(startAngle / 90));
+ int endSegment = int(floor((startAngle + sweepLength) / 90));
+
+ qreal startT = (startAngle - startSegment * 90) / 90;
+ qreal endT = (startAngle + sweepLength - endSegment * 90) / 90;
+
+ int delta = sweepLength > 0 ? 1 : -1;
+ if (delta < 0) {
+ startT = 1 - startT;
+ endT = 1 - endT;
+ }
+
+ // avoid empty start segment
+ if (qFuzzyCompare(startT, qreal(1))) {
+ startT = 0;
+ startSegment += delta;
+ }
+
+ // avoid empty end segment
+ if (qFuzzyCompare(endT + 1, qreal(1))) {
+ endT = 1;
+ endSegment -= delta;
+ }
+
+ startT = qt_t_for_arc_angle(startT * 90);
+ endT = qt_t_for_arc_angle(endT * 90);
+
+ const bool splitAtStart = !qFuzzyCompare(startT + 1, qreal(1));
+ const bool splitAtEnd = !qFuzzyCompare(endT, qreal(1));
+
+ const int end = endSegment + delta;
+
+ // empty arc?
+ if (startSegment == end) {
+ const int quadrant = 3 - ((startSegment % 4) + 4) % 4;
+ const int j = 3 * quadrant;
+ return delta > 0 ? points[j + 3] : points[j];
+ }
+
+ QPointF startPoint, endPoint;
+ qt_find_ellipse_coords(rect, startAngle, sweepLength, &startPoint, &endPoint);
+
+ for (int i = startSegment; i != end; i += delta) {
+ const int quadrant = 3 - ((i % 4) + 4) % 4;
+ const int j = 3 * quadrant;
+
+ QBezier b;
+ if (delta > 0)
+ b = QBezier::fromPoints(points[j + 3], points[j + 2], points[j + 1], points[j]);
+ else
+ b = QBezier::fromPoints(points[j], points[j + 1], points[j + 2], points[j + 3]);
+
+ // empty arc?
+ if (startSegment == endSegment && qFuzzyCompare(startT, endT))
+ return startPoint;
+
+ if (i == startSegment) {
+ if (i == endSegment && splitAtEnd)
+ b = b.bezierOnInterval(startT, endT);
+ else if (splitAtStart)
+ b = b.bezierOnInterval(startT, 1);
+ } else if (i == endSegment && splitAtEnd) {
+ b = b.bezierOnInterval(0, endT);
+ }
+
+ // push control points
+ curves[(*point_count)++] = b.pt2();
+ curves[(*point_count)++] = b.pt3();
+ curves[(*point_count)++] = b.pt4();
+ }
+
+ Q_ASSERT(*point_count > 0);
+ curves[*(point_count)-1] = endPoint;
+
+ return startPoint;
+}
+
+
+/*******************************************************************************
+ * QDashStroker members
+ */
+QDashStroker::QDashStroker(QStroker *stroker)
+ : m_stroker(stroker), m_dashOffset(0)
+{
+
+}
+
+QVector<qfixed> QDashStroker::patternForStyle(Qt::PenStyle style)
+{
+ const qfixed space = 2;
+ const qfixed dot = 1;
+ const qfixed dash = 4;
+
+ QVector<qfixed> pattern;
+
+ switch (style) {
+ case Qt::DashLine:
+ pattern << dash << space;
+ break;
+ case Qt::DotLine:
+ pattern << dot << space;
+ break;
+ case Qt::DashDotLine:
+ pattern << dash << space << dot << space;
+ break;
+ case Qt::DashDotDotLine:
+ pattern << dash << space << dot << space << dot << space;
+ break;
+ default:
+ break;
+ }
+
+ return pattern;
+}
+
+
+void QDashStroker::processCurrentSubpath()
+{
+ int dashCount = qMin(m_dashPattern.size(), 32);
+ qfixed dashes[32];
+
+ qreal sumLength = 0;
+ for (int i=0; i<dashCount; ++i) {
+ dashes[i] = qMax(m_dashPattern.at(i), qreal(0)) * m_stroker->strokeWidth();
+ sumLength += dashes[i];
+ }
+
+ if (qFuzzyCompare(sumLength + 1, qreal(1)))
+ return;
+
+ Q_ASSERT(dashCount > 0);
+
+ dashCount = (dashCount / 2) * 2; // Round down to even number
+
+ int idash = 0; // Index to current dash
+ qreal pos = 0; // The position on the curve, 0 <= pos <= path.length
+ qreal elen = 0; // element length
+ qreal doffset = m_dashOffset * m_stroker->strokeWidth();
+
+ // make sure doffset is in range [0..sumLength)
+ doffset -= qFloor(doffset / sumLength) * sumLength;
+
+ while (doffset >= dashes[idash]) {
+ doffset -= dashes[idash];
+ idash = (idash + 1) % dashCount;
+ }
+
+ qreal estart = 0; // The elements starting position
+ qreal estop = 0; // The element stop position
+
+ QLineF cline;
+
+ QPainterPath dashPath;
+
+ QSubpathFlatIterator it(&m_elements);
+ qfixed2d prev = it.next();
+
+ bool clipping = !m_clip_rect.isEmpty();
+ qfixed2d move_to_pos = prev;
+ qfixed2d line_to_pos;
+
+ // Pad to avoid clipping the borders of thick pens.
+ qfixed padding = qMax(m_stroker->strokeWidth(), m_stroker->miterLimit());
+ qfixed2d clip_tl = { qt_real_to_fixed(m_clip_rect.left()) - padding,
+ qt_real_to_fixed(m_clip_rect.top()) - padding };
+ qfixed2d clip_br = { qt_real_to_fixed(m_clip_rect.right()) + padding ,
+ qt_real_to_fixed(m_clip_rect.bottom()) + padding };
+
+ bool hasMoveTo = false;
+ while (it.hasNext()) {
+ QStrokerOps::Element e = it.next();
+
+ Q_ASSERT(e.isLineTo());
+ cline = QLineF(qt_fixed_to_real(prev.x),
+ qt_fixed_to_real(prev.y),
+ qt_fixed_to_real(e.x),
+ qt_fixed_to_real(e.y));
+ elen = cline.length();
+
+ estop = estart + elen;
+
+ bool done = pos >= estop;
+ // Dash away...
+ while (!done) {
+ QPointF p2;
+
+ int idash_incr = 0;
+ bool has_offset = doffset > 0;
+ qreal dpos = pos + dashes[idash] - doffset - estart;
+
+ Q_ASSERT(dpos >= 0);
+
+ if (dpos > elen) { // dash extends this line
+ doffset = dashes[idash] - (dpos - elen); // subtract the part already used
+ pos = estop; // move pos to next path element
+ done = true;
+ p2 = cline.p2();
+ } else { // Dash is on this line
+ p2 = cline.pointAt(dpos/elen);
+ pos = dpos + estart;
+ done = pos >= estop;
+ idash_incr = 1;
+ doffset = 0; // full segment so no offset on next.
+ }
+
+ if (idash % 2 == 0) {
+ line_to_pos.x = qt_real_to_fixed(p2.x());
+ line_to_pos.y = qt_real_to_fixed(p2.y());
+
+ // If we have an offset, we're continuing a dash
+ // from a previous element and should only
+ // continue the current dash, without starting a
+ // new subpath.
+ if (!has_offset || !hasMoveTo) {
+ m_stroker->moveTo(move_to_pos.x, move_to_pos.y);
+ hasMoveTo = true;
+ }
+
+ if (!clipping
+ // if move_to is inside...
+ || (move_to_pos.x > clip_tl.x && move_to_pos.x < clip_br.x
+ && move_to_pos.y > clip_tl.y && move_to_pos.y < clip_br.y)
+ // Or if line_to is inside...
+ || (line_to_pos.x > clip_tl.x && line_to_pos.x < clip_br.x
+ && line_to_pos.y > clip_tl.y && line_to_pos.y < clip_br.y))
+ {
+ m_stroker->lineTo(line_to_pos.x, line_to_pos.y);
+ }
+ } else {
+ move_to_pos.x = qt_real_to_fixed(p2.x());
+ move_to_pos.y = qt_real_to_fixed(p2.y());
+ }
+
+ idash = (idash + idash_incr) % dashCount;
+ }
+
+ // Shuffle to the next cycle...
+ estart = estop;
+ prev = e;
+ }
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/painting/qstroker_p.h b/src/gui/painting/qstroker_p.h
new file mode 100644
index 0000000000..72141aade9
--- /dev/null
+++ b/src/gui/painting/qstroker_p.h
@@ -0,0 +1,378 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSTROKER_P_H
+#define QSTROKER_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 "QtGui/qpainterpath.h"
+#include "private/qdatabuffer_p.h"
+#include "private/qnumeric_p.h"
+
+QT_BEGIN_NAMESPACE
+
+// #define QFIXED_IS_26_6
+
+#if defined QFIXED_IS_26_6
+typedef int qfixed;
+#define qt_real_to_fixed(real) qfixed(real * 64)
+#define qt_int_to_fixed(real) qfixed(int(real) << 6)
+#define qt_fixed_to_real(fixed) qreal(fixed / qreal(64))
+#define qt_fixed_to_int(fixed) int(fixed >> 6)
+struct qfixed2d
+{
+ qfixed x;
+ qfixed y;
+
+ bool operator==(const qfixed2d &other) const { return x == other.x && y == other.y; }
+};
+#elif defined QFIXED_IS_32_32
+typedef qint64 qfixed;
+#define qt_real_to_fixed(real) qfixed(real * double(qint64(1) << 32))
+#define qt_fixed_to_real(fixed) qreal(fixed / double(qint64(1) << 32))
+struct qfixed2d
+{
+ qfixed x;
+ qfixed y;
+
+ bool operator==(const qfixed2d &other) const { return x == other.x && y == other.y; }
+};
+#elif defined QFIXED_IS_16_16
+typedef int qfixed;
+#define qt_real_to_fixed(real) qfixed(real * qreal(1 << 16))
+#define qt_fixed_to_real(fixed) qreal(fixed / qreal(1 << 16))
+struct qfixed2d
+{
+ qfixed x;
+ qfixed y;
+
+ bool operator==(const qfixed2d &other) const { return x == other.x && y == other.y; }
+};
+#else
+typedef qreal qfixed;
+#define qt_real_to_fixed(real) qfixed(real)
+#define qt_fixed_to_real(fixed) fixed
+struct qfixed2d
+{
+ qfixed x;
+ qfixed y;
+
+ bool operator==(const qfixed2d &other) const { return qFuzzyCompare(x, other.x)
+ && qFuzzyCompare(y, other.y); }
+};
+#endif
+
+#define QT_PATH_KAPPA 0.5522847498
+
+QPointF qt_curves_for_arc(const QRectF &rect, qreal startAngle, qreal sweepLength,
+ QPointF *controlPoints, int *point_count);
+
+qreal qt_t_for_arc_angle(qreal angle);
+
+typedef void (*qStrokerMoveToHook)(qfixed x, qfixed y, void *data);
+typedef void (*qStrokerLineToHook)(qfixed x, qfixed y, void *data);
+typedef void (*qStrokerCubicToHook)(qfixed c1x, qfixed c1y,
+ qfixed c2x, qfixed c2y,
+ qfixed ex, qfixed ey,
+ void *data);
+
+class Q_GUI_EXPORT QStrokerOps
+{
+public:
+ struct Element {
+ QPainterPath::ElementType type;
+ qfixed x;
+ qfixed y;
+
+ inline bool isMoveTo() const { return type == QPainterPath::MoveToElement; }
+ inline bool isLineTo() const { return type == QPainterPath::LineToElement; }
+ inline bool isCurveTo() const { return type == QPainterPath::CurveToElement; }
+
+ operator qfixed2d () { qfixed2d pt = { x, y }; return pt; }
+ };
+
+ QStrokerOps();
+ virtual ~QStrokerOps();
+
+ void setMoveToHook(qStrokerMoveToHook moveToHook) { m_moveTo = moveToHook; }
+ void setLineToHook(qStrokerLineToHook lineToHook) { m_lineTo = lineToHook; }
+ void setCubicToHook(qStrokerCubicToHook cubicToHook) { m_cubicTo = cubicToHook; }
+
+ virtual void begin(void *customData);
+ virtual void end();
+
+ inline void moveTo(qfixed x, qfixed y);
+ inline void lineTo(qfixed x, qfixed y);
+ inline void cubicTo(qfixed x1, qfixed y1, qfixed x2, qfixed y2, qfixed ex, qfixed ey);
+
+ void strokePath(const QPainterPath &path, void *data, const QTransform &matrix);
+ void strokePolygon(const QPointF *points, int pointCount, bool implicit_close,
+ void *data, const QTransform &matrix);
+ void strokeEllipse(const QRectF &ellipse, void *data, const QTransform &matrix);
+
+ QRectF clipRect() const { return m_clip_rect; }
+ void setClipRect(const QRectF &clip) { m_clip_rect = clip; }
+
+protected:
+ inline void emitMoveTo(qfixed x, qfixed y);
+ inline void emitLineTo(qfixed x, qfixed y);
+ inline void emitCubicTo(qfixed c1x, qfixed c1y, qfixed c2x, qfixed c2y, qfixed ex, qfixed ey);
+
+ virtual void processCurrentSubpath() = 0;
+ QDataBuffer<Element> m_elements;
+
+ QRectF m_clip_rect;
+
+private:
+ void *m_customData;
+ qStrokerMoveToHook m_moveTo;
+ qStrokerLineToHook m_lineTo;
+ qStrokerCubicToHook m_cubicTo;
+
+};
+
+class Q_GUI_EXPORT QStroker : public QStrokerOps
+{
+public:
+
+ enum LineJoinMode {
+ FlatJoin,
+ SquareJoin,
+ MiterJoin,
+ RoundJoin,
+ RoundCap,
+ SvgMiterJoin
+ };
+
+ QStroker();
+ ~QStroker();
+
+ void setStrokeWidth(qfixed width) { m_strokeWidth = width; }
+ qfixed strokeWidth() const { return m_strokeWidth; }
+
+ void setCapStyle(Qt::PenCapStyle capStyle) { m_capStyle = joinModeForCap(capStyle); }
+ Qt::PenCapStyle capStyle() const { return capForJoinMode(m_capStyle); }
+ LineJoinMode capStyleMode() const { return m_capStyle; }
+
+ void setJoinStyle(Qt::PenJoinStyle style) { m_joinStyle = joinModeForJoin(style); }
+ Qt::PenJoinStyle joinStyle() const { return joinForJoinMode(m_joinStyle); }
+ LineJoinMode joinStyleMode() const { return m_joinStyle; }
+
+ void setMiterLimit(qfixed length) { m_miterLimit = length; }
+ qfixed miterLimit() const { return m_miterLimit; }
+
+ void setCurveThreshold(qfixed threshold) { m_curveThreshold = threshold; }
+ qfixed curveThreshold() const { return m_curveThreshold; }
+
+ void joinPoints(qfixed x, qfixed y, const QLineF &nextLine, LineJoinMode join);
+ inline void emitMoveTo(qfixed x, qfixed y);
+ inline void emitLineTo(qfixed x, qfixed y);
+ inline void emitCubicTo(qfixed c1x, qfixed c1y, qfixed c2x, qfixed c2y, qfixed ex, qfixed ey);
+
+protected:
+ static Qt::PenCapStyle capForJoinMode(LineJoinMode mode);
+ static LineJoinMode joinModeForCap(Qt::PenCapStyle);
+
+ static Qt::PenJoinStyle joinForJoinMode(LineJoinMode mode);
+ static LineJoinMode joinModeForJoin(Qt::PenJoinStyle joinStyle);
+
+ virtual void processCurrentSubpath();
+
+ qfixed m_strokeWidth;
+ qfixed m_miterLimit;
+ qfixed m_curveThreshold;
+
+ LineJoinMode m_capStyle;
+ LineJoinMode m_joinStyle;
+
+ qfixed m_back1X;
+ qfixed m_back1Y;
+
+ qfixed m_back2X;
+ qfixed m_back2Y;
+};
+
+class Q_GUI_EXPORT QDashStroker : public QStrokerOps
+{
+public:
+ QDashStroker(QStroker *stroker);
+
+ QStroker *stroker() const { return m_stroker; }
+
+ static QVector<qfixed> patternForStyle(Qt::PenStyle style);
+
+ void setDashPattern(const QVector<qfixed> &dashPattern) { m_dashPattern = dashPattern; }
+ QVector<qfixed> dashPattern() const { return m_dashPattern; }
+
+ void setDashOffset(qreal offset) { m_dashOffset = offset; }
+ qreal dashOffset() const { return m_dashOffset; }
+
+ virtual void begin(void *data);
+ virtual void end();
+
+protected:
+ virtual void processCurrentSubpath();
+
+ QStroker *m_stroker;
+ QVector<qfixed> m_dashPattern;
+ qreal m_dashOffset;
+};
+
+
+/*******************************************************************************
+ * QStrokerOps inline membmers
+ */
+
+inline void QStrokerOps::emitMoveTo(qfixed x, qfixed y)
+{
+ Q_ASSERT(m_moveTo);
+ m_moveTo(x, y, m_customData);
+}
+
+inline void QStrokerOps::emitLineTo(qfixed x, qfixed y)
+{
+ Q_ASSERT(m_lineTo);
+ m_lineTo(x, y, m_customData);
+}
+
+inline void QStrokerOps::emitCubicTo(qfixed c1x, qfixed c1y, qfixed c2x, qfixed c2y, qfixed ex, qfixed ey)
+{
+ Q_ASSERT(m_cubicTo);
+ m_cubicTo(c1x, c1y, c2x, c2y, ex, ey, m_customData);
+}
+
+inline void QStrokerOps::moveTo(qfixed x, qfixed y)
+{
+ if (m_elements.size()>1)
+ processCurrentSubpath();
+ m_elements.reset();
+ Element e = { QPainterPath::MoveToElement, x, y };
+ m_elements.add(e);
+}
+
+inline void QStrokerOps::lineTo(qfixed x, qfixed y)
+{
+ Element e = { QPainterPath::LineToElement, x, y };
+ m_elements.add(e);
+}
+
+inline void QStrokerOps::cubicTo(qfixed x1, qfixed y1, qfixed x2, qfixed y2, qfixed ex, qfixed ey)
+{
+ Element c1 = { QPainterPath::CurveToElement, x1, y1 };
+ Element c2 = { QPainterPath::CurveToDataElement, x2, y2 };
+ Element e = { QPainterPath::CurveToDataElement, ex, ey };
+ m_elements.add(c1);
+ m_elements.add(c2);
+ m_elements.add(e);
+}
+
+/*******************************************************************************
+ * QStroker inline members
+ */
+inline void QStroker::emitMoveTo(qfixed x, qfixed y)
+{
+ m_back2X = m_back1X;
+ m_back2Y = m_back1Y;
+ m_back1X = x;
+ m_back1Y = y;
+ QStrokerOps::emitMoveTo(x, y);
+}
+
+inline void QStroker::emitLineTo(qfixed x, qfixed y)
+{
+ m_back2X = m_back1X;
+ m_back2Y = m_back1Y;
+ m_back1X = x;
+ m_back1Y = y;
+ QStrokerOps::emitLineTo(x, y);
+}
+
+inline void QStroker::emitCubicTo(qfixed c1x, qfixed c1y,
+ qfixed c2x, qfixed c2y,
+ qfixed ex, qfixed ey)
+{
+ if (c2x == ex && c2y == ey) {
+ if (c1x == ex && c1y == ey) {
+ m_back2X = m_back1X;
+ m_back2Y = m_back1Y;
+ } else {
+ m_back2X = c1x;
+ m_back2Y = c1y;
+ }
+ } else {
+ m_back2X = c2x;
+ m_back2Y = c2y;
+ }
+ m_back1X = ex;
+ m_back1Y = ey;
+ QStrokerOps::emitCubicTo(c1x, c1y, c2x, c2y, ex, ey);
+}
+
+/*******************************************************************************
+ * QDashStroker inline members
+ */
+inline void QDashStroker::begin(void *data)
+{
+ Q_ASSERT(m_stroker);
+ m_stroker->begin(data);
+ QStrokerOps::begin(data);
+}
+
+inline void QDashStroker::end()
+{
+ Q_ASSERT(m_stroker);
+ QStrokerOps::end();
+ m_stroker->end();
+}
+
+QT_END_NAMESPACE
+
+#endif // QSTROKER_P_H
diff --git a/src/gui/painting/qstylepainter.cpp b/src/gui/painting/qstylepainter.cpp
new file mode 100644
index 0000000000..1427a7ad74
--- /dev/null
+++ b/src/gui/painting/qstylepainter.cpp
@@ -0,0 +1,176 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qstylepainter.h"
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QStylePainter
+
+ \brief The QStylePainter class is a convenience class for drawing QStyle
+ elements inside a widget.
+
+ \ingroup appearance
+ \ingroup multimedia
+
+ QStylePainter extends QPainter with a set of high-level \c
+ draw...() functions implemented on top of QStyle's API. The
+ advantage of using QStylePainter is that the parameter lists get
+ considerably shorter. Whereas a QStyle object must be able to
+ draw on any widget using any painter (because the application
+ normally has one QStyle object shared by all widget), a
+ QStylePainter is initialized with a widget, eliminating the need
+ to specify the QWidget, the QPainter, and the QStyle for every
+ function call.
+
+ Example using QStyle directly:
+
+ \snippet doc/src/snippets/styles/styles.cpp 1
+
+ Example using QStylePainter:
+
+ \snippet doc/src/snippets/styles/styles.cpp 0
+ \snippet doc/src/snippets/styles/styles.cpp 4
+ \snippet doc/src/snippets/styles/styles.cpp 6
+
+ \sa QStyle, QStyleOption
+*/
+
+/*!
+ \fn QStylePainter::QStylePainter()
+
+ Constructs a QStylePainter.
+*/
+
+/*!
+ \fn QStylePainter::QStylePainter(QWidget *widget)
+
+ Construct a QStylePainter using widget \a widget for its paint device.
+*/
+
+/*!
+ \fn QStylePainter::QStylePainter(QPaintDevice *pd, QWidget *widget)
+
+ Construct a QStylePainter using \a pd for its paint device, and
+ attributes from \a widget.
+*/
+
+
+/*!
+ \fn bool QStylePainter::begin(QWidget *widget)
+
+ Begin painting operations on the specified \a widget.
+ Returns true if the painter is ready to use; otherwise returns false.
+
+ This is automatically called by the constructor that takes a QWidget.
+*/
+
+/*!
+ \fn bool QStylePainter::begin(QPaintDevice *pd, QWidget *widget)
+ \overload
+
+ Begin painting operations on paint device \a pd as if it was \a
+ widget.
+
+ This is automatically called by the constructor that
+ takes a QPaintDevice and a QWidget.
+*/
+
+
+/*!
+ \fn void QStylePainter::drawPrimitive(QStyle::PrimitiveElement pe, const QStyleOption &option)
+
+ Use the widget's style to draw a primitive element \a pe specified by QStyleOption \a option.
+
+ \sa QStyle::drawPrimitive()
+*/
+
+/*!
+ \fn void QStylePainter::drawControl(QStyle::ControlElement ce, const QStyleOption &option)
+
+ Use the widget's style to draw a control element \a ce specified by QStyleOption \a option.
+
+ \sa QStyle::drawControl()
+*/
+
+/*!
+ \fn void QStylePainter::drawComplexControl(QStyle::ComplexControl cc,
+ const QStyleOptionComplex &option)
+
+ Use the widget's style to draw a complex control \a cc specified by the
+ QStyleOptionComplex \a option.
+
+ \sa QStyle::drawComplexControl()
+*/
+
+/*!
+ \fn void QStylePainter::drawItemText(const QRect &rect, int flags, const QPalette &pal,
+ bool enabled, const QString &text,
+ QPalette::ColorRole textRole = QPalette::NoRole)
+
+ Draws the \a text in rectangle \a rect and palette \a pal.
+ The text is aligned and wrapped according to \a
+ flags.
+
+ The pen color is specified with \a textRole. The \a enabled bool
+ indicates whether or not the item is enabled; when reimplementing
+ this bool should influence how the item is drawn.
+
+ \sa QStyle::drawItemText(), Qt::Alignment
+*/
+
+/*!
+ \fn void QStylePainter::drawItemPixmap(const QRect &rect, int flags, const QPixmap &pixmap)
+
+ Draws the \a pixmap in rectangle \a rect.
+ The pixmap is aligned according to \a flags.
+
+ \sa QStyle::drawItemPixmap(), Qt::Alignment
+*/
+
+/*!
+ \fn QStyle *QStylePainter::style() const
+
+ Return the current style used by the QStylePainter.
+*/
+
+QT_END_NAMESPACE
diff --git a/src/gui/painting/qstylepainter.h b/src/gui/painting/qstylepainter.h
new file mode 100644
index 0000000000..5fefdfeb46
--- /dev/null
+++ b/src/gui/painting/qstylepainter.h
@@ -0,0 +1,112 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSTYLEPAINTER_H
+#define QSTYLEPAINTER_H
+
+#include <QtGui/qpainter.h>
+#include <QtGui/qstyle.h>
+#include <QtGui/qwidget.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+class QStylePainter : public QPainter
+{
+public:
+ inline QStylePainter() : QPainter(), widget(0), wstyle(0) {}
+ inline explicit QStylePainter(QWidget *w) { begin(w, w); }
+ inline QStylePainter(QPaintDevice *pd, QWidget *w) { begin(pd, w); }
+ inline bool begin(QWidget *w) { return begin(w, w); }
+ inline bool begin(QPaintDevice *pd, QWidget *w) {
+ Q_ASSERT_X(w, "QStylePainter::QStylePainter", "Widget must be non-zero");
+ widget = w;
+ wstyle = w->style();
+ return QPainter::begin(pd);
+ };
+ inline void drawPrimitive(QStyle::PrimitiveElement pe, const QStyleOption &opt);
+ inline void drawControl(QStyle::ControlElement ce, const QStyleOption &opt);
+ inline void drawComplexControl(QStyle::ComplexControl cc, const QStyleOptionComplex &opt);
+ inline void drawItemText(const QRect &r, int flags, const QPalette &pal, bool enabled,
+ const QString &text, QPalette::ColorRole textRole = QPalette::NoRole);
+ inline void drawItemPixmap(const QRect &r, int flags, const QPixmap &pixmap);
+ inline QStyle *style() const { return wstyle; }
+
+private:
+ QWidget *widget;
+ QStyle *wstyle;
+ Q_DISABLE_COPY(QStylePainter)
+};
+
+void QStylePainter::drawPrimitive(QStyle::PrimitiveElement pe, const QStyleOption &opt)
+{
+ wstyle->drawPrimitive(pe, &opt, this, widget);
+}
+
+void QStylePainter::drawControl(QStyle::ControlElement ce, const QStyleOption &opt)
+{
+ wstyle->drawControl(ce, &opt, this, widget);
+}
+
+void QStylePainter::drawComplexControl(QStyle::ComplexControl cc, const QStyleOptionComplex &opt)
+{
+ wstyle->drawComplexControl(cc, &opt, this, widget);
+}
+
+void QStylePainter::drawItemText(const QRect &r, int flags, const QPalette &pal, bool enabled,
+ const QString &text, QPalette::ColorRole textRole)
+{
+ wstyle->drawItemText(this, r, flags, pal, enabled, text, textRole);
+}
+
+void QStylePainter::drawItemPixmap(const QRect &r, int flags, const QPixmap &pixmap)
+{
+ wstyle->drawItemPixmap(this, r, flags, pixmap);
+}
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QSTYLEPAINTER_H
diff --git a/src/gui/painting/qtessellator.cpp b/src/gui/painting/qtessellator.cpp
new file mode 100644
index 0000000000..e02f02d2d4
--- /dev/null
+++ b/src/gui/painting/qtessellator.cpp
@@ -0,0 +1,1499 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qtessellator_p.h"
+
+#include <QRect>
+#include <QList>
+#include <QDebug>
+
+#include <qmath.h>
+#include <limits.h>
+
+QT_BEGIN_NAMESPACE
+
+//#define DEBUG
+#ifdef DEBUG
+#define QDEBUG qDebug
+#else
+#define QDEBUG if (1); else qDebug
+#endif
+
+static const bool emit_clever = true;
+static const bool mark_clever = false;
+
+enum VertexFlags {
+ LineBeforeStarts = 0x1,
+ LineBeforeEnds = 0x2,
+ LineBeforeHorizontal = 0x4,
+ LineAfterStarts = 0x8,
+ LineAfterEnds = 0x10,
+ LineAfterHorizontal = 0x20
+};
+
+
+
+class QTessellatorPrivate {
+public:
+ struct Vertices;
+
+ QTessellatorPrivate() {}
+
+ QRectF collectAndSortVertices(const QPointF *points, int *maxActiveEdges);
+ void cancelCoincidingEdges();
+
+ void emitEdges(QTessellator *tessellator);
+ void processIntersections();
+ void removeEdges();
+ void addEdges();
+ void addIntersections();
+
+ struct Vertex : public QTessellator::Vertex
+ {
+ int flags;
+ };
+
+ struct Intersection
+ {
+ Q27Dot5 y;
+ int edge;
+ bool operator <(const Intersection &other) const {
+ if (y != other.y)
+ return y < other.y;
+ return edge < other.edge;
+ }
+ };
+ struct IntersectionLink
+ {
+ int next;
+ int prev;
+ };
+ typedef QMap<Intersection, IntersectionLink> Intersections;
+
+ struct Edge {
+ Edge(const Vertices &v, int _edge);
+ int edge;
+ const Vertex *v0;
+ const Vertex *v1;
+ Q27Dot5 y_left;
+ Q27Dot5 y_right;
+ signed int winding : 8;
+ bool mark;
+ bool free;
+ bool intersect_left;
+ bool intersect_right;
+ bool isLeftOf(const Edge &other, Q27Dot5 y) const;
+ Q27Dot5 positionAt(Q27Dot5 y) const;
+ bool intersect(const Edge &other, Q27Dot5 *y, bool *det_positive) const;
+
+ };
+
+ class EdgeSorter
+ {
+ public:
+ EdgeSorter(int _y) : y(_y) {}
+ bool operator() (const Edge *e1, const Edge *e2);
+ int y;
+ };
+
+ class Scanline {
+ public:
+ Scanline();
+ ~Scanline();
+
+ void init(int maxActiveEdges);
+ void done();
+
+ int findEdgePosition(Q27Dot5 x, Q27Dot5 y) const;
+ int findEdgePosition(const Edge &e) const;
+ int findEdge(int edge) const;
+ void clearMarks();
+
+ void swap(int p1, int p2) {
+ Edge *tmp = edges[p1];
+ edges[p1] = edges[p2];
+ edges[p2] = tmp;
+ }
+ void insert(int pos, const Edge &e);
+ void removeAt(int pos);
+ void markEdges(int pos1, int pos2);
+
+ void prepareLine();
+ void lineDone();
+
+ Edge **old;
+ int old_size;
+
+ Edge **edges;
+ int size;
+
+ private:
+ Edge *edge_table;
+ int first_unused;
+ int max_edges;
+ enum { default_alloc = 32 };
+ };
+
+ struct Vertices {
+ enum { default_alloc = 128 };
+ Vertices();
+ ~Vertices();
+ void init(int maxVertices);
+ void done();
+ Vertex *storage;
+ Vertex **sorted;
+
+ Vertex *operator[] (int i) { return storage + i; }
+ const Vertex *operator[] (int i) const { return storage + i; }
+ int position(const Vertex *v) const {
+ return v - storage;
+ }
+ Vertex *next(Vertex *v) {
+ ++v;
+ if (v == storage + nPoints)
+ v = storage;
+ return v;
+ }
+ const Vertex *next(const Vertex *v) const {
+ ++v;
+ if (v == storage + nPoints)
+ v = storage;
+ return v;
+ }
+ int nextPos(const Vertex *v) const {
+ ++v;
+ if (v == storage + nPoints)
+ return 0;
+ return v - storage;
+ }
+ Vertex *prev(Vertex *v) {
+ if (v == storage)
+ v = storage + nPoints;
+ --v;
+ return v;
+ }
+ const Vertex *prev(const Vertex *v) const {
+ if (v == storage)
+ v = storage + nPoints;
+ --v;
+ return v;
+ }
+ int prevPos(const Vertex *v) const {
+ if (v == storage)
+ v = storage + nPoints;
+ --v;
+ return v - storage;
+ }
+ int nPoints;
+ int allocated;
+ };
+ Vertices vertices;
+ Intersections intersections;
+ Scanline scanline;
+ bool winding;
+ Q27Dot5 y;
+ int currentVertex;
+
+private:
+ void addIntersection(const Edge *e1, const Edge *e2);
+ bool edgeInChain(Intersection i, int edge);
+};
+
+
+QTessellatorPrivate::Edge::Edge(const QTessellatorPrivate::Vertices &vertices, int edge)
+{
+ this->edge = edge;
+ intersect_left = intersect_right = true;
+ mark = false;
+ free = false;
+
+ v0 = vertices[edge];
+ v1 = vertices.next(v0);
+
+ Q_ASSERT(v0->y != v1->y);
+
+ if (v0->y > v1->y) {
+ qSwap(v0, v1);
+ winding = -1;
+ } else {
+ winding = 1;
+ }
+ y_left = y_right = v0->y;
+}
+
+// This is basically the algorithm from graphics gems. The algorithm
+// is cubic in the coordinates at one place. Since we use 64bit
+// integers, this implies, that the allowed range for our coordinates
+// is limited to 21 bits. With 5 bits behind the decimal, this
+// implies that differences in coordaintes can range from 2*SHORT_MIN
+// to 2*SHORT_MAX, giving us efficiently a coordinate system from
+// SHORT_MIN to SHORT_MAX.
+//
+
+// WARNING: It's absolutely critical that the intersect() and isLeftOf() methods use
+// exactly the same algorithm to calulate yi. It's also important to be sure the algorithms
+// are transitive (ie. the conditions below are true for all input data):
+//
+// a.intersect(b) == b.intersect(a)
+// a.isLeftOf(b) != b.isLeftOf(a)
+//
+// This is tricky to get right, so be very careful when changing anything in here!
+
+static inline bool sameSign(qint64 a, qint64 b) {
+ return (((qint64) ((quint64) a ^ (quint64) b)) >= 0 );
+}
+
+bool QTessellatorPrivate::Edge::intersect(const Edge &other, Q27Dot5 *y, bool *det_positive) const
+{
+ qint64 a1 = v1->y - v0->y;
+ qint64 b1 = v0->x - v1->x;
+
+ qint64 a2 = other.v1->y - other.v0->y;
+ qint64 b2 = other.v0->x - other.v1->x;
+
+ qint64 det = a1 * b2 - a2 * b1;
+ if (det == 0)
+ return false;
+
+ qint64 c1 = qint64(v1->x) * v0->y - qint64(v0->x) * v1->y;
+
+ qint64 r3 = a1 * other.v0->x + b1 * other.v0->y + c1;
+ qint64 r4 = a1 * other.v1->x + b1 * other.v1->y + c1;
+
+ // Check signs of r3 and r4. If both point 3 and point 4 lie on
+ // same side of line 1, the line segments do not intersect.
+ QDEBUG() << " " << r3 << r4;
+ if (r3 != 0 && r4 != 0 && sameSign( r3, r4 ))
+ return false;
+
+ qint64 c2 = qint64(other.v1->x) * other.v0->y - qint64(other.v0->x) * other.v1->y;
+
+ qint64 r1 = a2 * v0->x + b2 * v0->y + c2;
+ qint64 r2 = a2 * v1->x + b2 * v1->y + c2;
+
+ // Check signs of r1 and r2. If both point 1 and point 2 lie
+ // on same side of second line segment, the line segments do not intersect.
+ QDEBUG() << " " << r1 << r2;
+ if (r1 != 0 && r2 != 0 && sameSign( r1, r2 ))
+ return false;
+
+ // The det/2 is to get rounding instead of truncating. It
+ // is added or subtracted to the numerator, depending upon the
+ // sign of the numerator.
+ qint64 offset = det < 0 ? -det : det;
+ offset >>= 1;
+
+ qint64 num = a2 * c1 - a1 * c2;
+ *y = ( num < 0 ? num - offset : num + offset ) / det;
+
+ *det_positive = (det > 0);
+
+ return true;
+}
+
+#undef SAME_SIGNS
+
+bool QTessellatorPrivate::Edge::isLeftOf(const Edge &other, Q27Dot5 y) const
+{
+// QDEBUG() << "isLeftOf" << edge << other.edge << y;
+ qint64 a1 = v1->y - v0->y;
+ qint64 b1 = v0->x - v1->x;
+ qint64 a2 = other.v1->y - other.v0->y;
+ qint64 b2 = other.v0->x - other.v1->x;
+
+ qint64 c2 = qint64(other.v1->x) * other.v0->y - qint64(other.v0->x) * other.v1->y;
+
+ qint64 det = a1 * b2 - a2 * b1;
+ if (det == 0) {
+ // lines are parallel. Only need to check side of one point
+ // fixed ordering for coincident edges
+ qint64 r1 = a2 * v0->x + b2 * v0->y + c2;
+// QDEBUG() << "det = 0" << r1;
+ if (r1 == 0)
+ return edge < other.edge;
+ return (r1 < 0);
+ }
+
+ // not parallel, need to find the y coordinate of the intersection point
+ qint64 c1 = qint64(v1->x) * v0->y - qint64(v0->x) * v1->y;
+
+ qint64 offset = det < 0 ? -det : det;
+ offset >>= 1;
+
+ qint64 num = a2 * c1 - a1 * c2;
+ qint64 yi = ( num < 0 ? num - offset : num + offset ) / det;
+// QDEBUG() << " num=" << num << "offset=" << offset << "det=" << det;
+
+ return ((yi > y) ^ (det < 0));
+}
+
+static inline bool compareVertex(const QTessellatorPrivate::Vertex *p1,
+ const QTessellatorPrivate::Vertex *p2)
+{
+ if (p1->y == p2->y) {
+ if (p1->x == p2->x)
+ return p1 < p2;
+ return p1->x < p2->x;
+ }
+ return p1->y < p2->y;
+}
+
+Q27Dot5 QTessellatorPrivate::Edge::positionAt(Q27Dot5 y) const
+{
+ if (y == v0->y)
+ return v0->x;
+ else if (y == v1->y)
+ return v1->x;
+
+ qint64 d = v1->x - v0->x;
+ return (v0->x + d*(y - v0->y)/(v1->y-v0->y));
+}
+
+bool QTessellatorPrivate::EdgeSorter::operator() (const Edge *e1, const Edge *e2)
+{
+ return e1->isLeftOf(*e2, y);
+}
+
+
+QTessellatorPrivate::Scanline::Scanline()
+{
+ edges = 0;
+ edge_table = 0;
+ old = 0;
+}
+
+void QTessellatorPrivate::Scanline::init(int maxActiveEdges)
+{
+ maxActiveEdges *= 2;
+ if (!edges || maxActiveEdges > default_alloc) {
+ max_edges = maxActiveEdges;
+ int s = qMax(maxActiveEdges + 1, default_alloc + 1);
+ edges = (Edge **)realloc(edges, s*sizeof(Edge *));
+ edge_table = (Edge *)realloc(edge_table, s*sizeof(Edge));
+ old = (Edge **)realloc(old, s*sizeof(Edge *));
+ }
+ size = 0;
+ old_size = 0;
+ first_unused = 0;
+ for (int i = 0; i < maxActiveEdges; ++i)
+ edge_table[i].edge = i+1;
+ edge_table[maxActiveEdges].edge = -1;
+}
+
+void QTessellatorPrivate::Scanline::done()
+{
+ if (max_edges > default_alloc) {
+ free(edges);
+ free(old);
+ free(edge_table);
+ edges = 0;
+ old = 0;
+ edge_table = 0;
+ }
+}
+
+QTessellatorPrivate::Scanline::~Scanline()
+{
+ free(edges);
+ free(old);
+ free(edge_table);
+}
+
+int QTessellatorPrivate::Scanline::findEdgePosition(Q27Dot5 x, Q27Dot5 y) const
+{
+ int min = 0;
+ int max = size - 1;
+ while (min < max) {
+ int pos = min + ((max - min + 1) >> 1);
+ Q27Dot5 ax = edges[pos]->positionAt(y);
+ if (ax > x) {
+ max = pos - 1;
+ } else {
+ min = pos;
+ }
+ }
+ return min;
+}
+
+int QTessellatorPrivate::Scanline::findEdgePosition(const Edge &e) const
+{
+// qDebug() << ">> findEdgePosition";
+ int min = 0;
+ int max = size;
+ while (min < max) {
+ int pos = min + ((max - min) >> 1);
+// qDebug() << " " << min << max << pos << edges[pos]->isLeftOf(e, e.y0);
+ if (edges[pos]->isLeftOf(e, e.v0->y)) {
+ min = pos + 1;
+ } else {
+ max = pos;
+ }
+ }
+// qDebug() << "<< findEdgePosition got" << min;
+ return min;
+}
+
+int QTessellatorPrivate::Scanline::findEdge(int edge) const
+{
+ for (int i = 0; i < size; ++i) {
+ int item_edge = edges[i]->edge;
+ if (item_edge == edge)
+ return i;
+ }
+ //Q_ASSERT(false);
+ return -1;
+}
+
+void QTessellatorPrivate::Scanline::clearMarks()
+{
+ for (int i = 0; i < size; ++i) {
+ edges[i]->mark = false;
+ edges[i]->intersect_left = false;
+ edges[i]->intersect_right = false;
+ }
+}
+
+void QTessellatorPrivate::Scanline::prepareLine()
+{
+ Edge **end = edges + size;
+ Edge **e = edges;
+ Edge **o = old;
+ while (e < end) {
+ *o = *e;
+ ++o;
+ ++e;
+ }
+ old_size = size;
+}
+
+void QTessellatorPrivate::Scanline::lineDone()
+{
+ Edge **end = old + old_size;
+ Edge **e = old;
+ while (e < end) {
+ if ((*e)->free) {
+ (*e)->edge = first_unused;
+ first_unused = (*e - edge_table);
+ }
+ ++e;
+ }
+}
+
+void QTessellatorPrivate::Scanline::insert(int pos, const Edge &e)
+{
+ Edge *edge = edge_table + first_unused;
+ first_unused = edge->edge;
+ Q_ASSERT(first_unused != -1);
+ *edge = e;
+ memmove(edges + pos + 1, edges + pos, (size - pos)*sizeof(Edge *));
+ edges[pos] = edge;
+ ++size;
+}
+
+void QTessellatorPrivate::Scanline::removeAt(int pos)
+{
+ Edge *e = edges[pos];
+ e->free = true;
+ --size;
+ memmove(edges + pos, edges + pos + 1, (size - pos)*sizeof(Edge *));
+}
+
+void QTessellatorPrivate::Scanline::markEdges(int pos1, int pos2)
+{
+ if (pos2 < pos1)
+ return;
+
+ for (int i = pos1; i <= pos2; ++i)
+ edges[i]->mark = true;
+}
+
+
+QTessellatorPrivate::Vertices::Vertices()
+{
+ storage = 0;
+ sorted = 0;
+ allocated = 0;
+ nPoints = 0;
+}
+
+QTessellatorPrivate::Vertices::~Vertices()
+{
+ if (storage) {
+ free(storage);
+ free(sorted);
+ }
+}
+
+void QTessellatorPrivate::Vertices::init(int maxVertices)
+{
+ if (!storage || maxVertices > allocated) {
+ int size = qMax((int)default_alloc, maxVertices);
+ storage = (Vertex *)realloc(storage, size*sizeof(Vertex));
+ sorted = (Vertex **)realloc(sorted, size*sizeof(Vertex *));
+ allocated = maxVertices;
+ }
+}
+
+void QTessellatorPrivate::Vertices::done()
+{
+ if (allocated > default_alloc) {
+ free(storage);
+ free(sorted);
+ storage = 0;
+ sorted = 0;
+ allocated = 0;
+ }
+}
+
+
+
+static inline void fillTrapezoid(Q27Dot5 y1, Q27Dot5 y2, int left, int right,
+ const QTessellatorPrivate::Vertices &vertices,
+ QTessellator::Trapezoid *trap)
+{
+ trap->top = y1;
+ trap->bottom = y2;
+ const QTessellatorPrivate::Vertex *v = vertices[left];
+ trap->topLeft = v;
+ trap->bottomLeft = vertices.next(v);
+ if (trap->topLeft->y > trap->bottomLeft->y)
+ qSwap(trap->topLeft,trap->bottomLeft);
+ v = vertices[right];
+ trap->topRight = v;
+ trap->bottomRight = vertices.next(v);
+ if (trap->topRight->y > trap->bottomRight->y)
+ qSwap(trap->topRight, trap->bottomRight);
+}
+
+QRectF QTessellatorPrivate::collectAndSortVertices(const QPointF *points, int *maxActiveEdges)
+{
+ *maxActiveEdges = 0;
+ Vertex *v = vertices.storage;
+ Vertex **vv = vertices.sorted;
+
+ qreal xmin(points[0].x());
+ qreal xmax(points[0].x());
+ qreal ymin(points[0].y());
+ qreal ymax(points[0].y());
+
+ // collect vertex data
+ Q27Dot5 y_prev = FloatToQ27Dot5(points[vertices.nPoints-1].y());
+ Q27Dot5 x_next = FloatToQ27Dot5(points[0].x());
+ Q27Dot5 y_next = FloatToQ27Dot5(points[0].y());
+ int j = 0;
+ int i = 0;
+ while (i < vertices.nPoints) {
+ Q27Dot5 y_curr = y_next;
+
+ *vv = v;
+
+ v->x = x_next;
+ v->y = y_next;
+ v->flags = 0;
+
+ next_point:
+
+ xmin = qMin(xmin, points[i+1].x());
+ xmax = qMax(xmax, points[i+1].x());
+ ymin = qMin(ymin, points[i+1].y());
+ ymax = qMax(ymax, points[i+1].y());
+
+ y_next = FloatToQ27Dot5(points[i+1].y());
+ x_next = FloatToQ27Dot5(points[i+1].x());
+
+ // skip vertices on top of each other
+ if (v->x == x_next && v->y == y_next) {
+ ++i;
+ if (i < vertices.nPoints)
+ goto next_point;
+ Vertex *v0 = vertices.storage;
+ v0->flags &= ~(LineBeforeStarts|LineBeforeEnds|LineBeforeHorizontal);
+ if (y_prev < y_curr)
+ v0->flags |= LineBeforeEnds;
+ else if (y_prev > y_curr)
+ v0->flags |= LineBeforeStarts;
+ else
+ v0->flags |= LineBeforeHorizontal;
+ if ((v0->flags & (LineBeforeStarts|LineAfterStarts))
+ && !(v0->flags & (LineAfterEnds|LineBeforeEnds)))
+ *maxActiveEdges += 2;
+ break;
+ }
+
+ if (y_prev < y_curr)
+ v->flags |= LineBeforeEnds;
+ else if (y_prev > y_curr)
+ v->flags |= LineBeforeStarts;
+ else
+ v->flags |= LineBeforeHorizontal;
+
+
+ if (y_curr < y_next)
+ v->flags |= LineAfterStarts;
+ else if (y_curr > y_next)
+ v->flags |= LineAfterEnds;
+ else
+ v->flags |= LineAfterHorizontal;
+ // ### could probably get better limit by looping over sorted list and counting down on ending edges
+ if ((v->flags & (LineBeforeStarts|LineAfterStarts))
+ && !(v->flags & (LineAfterEnds|LineBeforeEnds)))
+ *maxActiveEdges += 2;
+ y_prev = y_curr;
+ ++v;
+ ++vv;
+ ++j;
+ ++i;
+ }
+ vertices.nPoints = j;
+
+ QDEBUG() << "maxActiveEdges=" << *maxActiveEdges;
+ vv = vertices.sorted;
+ qSort(vv, vv + vertices.nPoints, compareVertex);
+
+ return QRectF(xmin, ymin, xmax-xmin, ymax-ymin);
+}
+
+struct QCoincidingEdge {
+ QTessellatorPrivate::Vertex *start;
+ QTessellatorPrivate::Vertex *end;
+ bool used;
+ bool before;
+
+ inline bool operator<(const QCoincidingEdge &e2) const
+ {
+ return end->y == e2.end->y ? end->x < e2.end->x : end->y < e2.end->y;
+ }
+};
+
+
+static void cancelEdges(QCoincidingEdge &e1, QCoincidingEdge &e2)
+{
+ if (e1.before) {
+ e1.start->flags &= ~(LineBeforeStarts|LineBeforeHorizontal);
+ e1.end->flags &= ~(LineAfterEnds|LineAfterHorizontal);
+ } else {
+ e1.start->flags &= ~(LineAfterStarts|LineAfterHorizontal);
+ e1.end->flags &= ~(LineBeforeEnds|LineBeforeHorizontal);
+ }
+ if (e2.before) {
+ e2.start->flags &= ~(LineBeforeStarts|LineBeforeHorizontal);
+ e2.end->flags &= ~(LineAfterEnds|LineAfterHorizontal);
+ } else {
+ e2.start->flags &= ~(LineAfterStarts|LineAfterHorizontal);
+ e2.end->flags &= ~(LineBeforeEnds|LineBeforeHorizontal);
+ }
+ e1.used = e2.used = true;
+}
+
+void QTessellatorPrivate::cancelCoincidingEdges()
+{
+ Vertex **vv = vertices.sorted;
+
+ QCoincidingEdge *tl = 0;
+ int tlSize = 0;
+
+ for (int i = 0; i < vertices.nPoints - 1; ++i) {
+ Vertex *v = vv[i];
+ int testListSize = 0;
+ while (i < vertices.nPoints - 1) {
+ Vertex *n = vv[i];
+ if (v->x != n->x || v->y != n->y)
+ break;
+
+ if (testListSize > tlSize - 2) {
+ tlSize = qMax(tlSize*2, 16);
+ tl = (QCoincidingEdge *)realloc(tl, tlSize*sizeof(QCoincidingEdge));
+ }
+ if (n->flags & (LineBeforeStarts|LineBeforeHorizontal)) {
+ tl[testListSize].start = n;
+ tl[testListSize].end = vertices.prev(n);
+ tl[testListSize].used = false;
+ tl[testListSize].before = true;
+ ++testListSize;
+ }
+ if (n->flags & (LineAfterStarts|LineAfterHorizontal)) {
+ tl[testListSize].start = n;
+ tl[testListSize].end = vertices.next(n);
+ tl[testListSize].used = false;
+ tl[testListSize].before = false;
+ ++testListSize;
+ }
+ ++i;
+ }
+ if (!testListSize)
+ continue;
+
+ qSort(tl, tl + testListSize);
+
+ for (int j = 0; j < testListSize; ++j) {
+ if (tl[j].used)
+ continue;
+
+ for (int k = j + 1; k < testListSize; ++k) {
+ if (tl[j].end->x != tl[k].end->x
+ || tl[j].end->y != tl[k].end->y
+ || tl[k].used)
+ break;
+
+ if (!winding || tl[j].before != tl[k].before) {
+ cancelEdges(tl[j], tl[k]);
+ break;
+ }
+ ++k;
+ }
+ ++j;
+ }
+ }
+ free(tl);
+}
+
+
+void QTessellatorPrivate::emitEdges(QTessellator *tessellator)
+{
+ //QDEBUG() << "TRAPS:";
+ if (!scanline.old_size)
+ return;
+
+ // emit edges
+ if (winding) {
+ // winding fill rule
+ int w = 0;
+
+ scanline.old[0]->y_left = y;
+
+ for (int i = 0; i < scanline.old_size - 1; ++i) {
+ Edge *left = scanline.old[i];
+ Edge *right = scanline.old[i+1];
+ w += left->winding;
+// qDebug() << "i=" << i << "edge->winding=" << left->winding << "winding=" << winding;
+ if (w == 0) {
+ left->y_right = y;
+ right->y_left = y;
+ } else if (!emit_clever || left->mark || right->mark) {
+ Q27Dot5 top = qMax(left->y_right, right->y_left);
+ if (top != y) {
+ QTessellator::Trapezoid trap;
+ fillTrapezoid(top, y, left->edge, right->edge, vertices, &trap);
+ tessellator->addTrap(trap);
+// QDEBUG() << " top=" << Q27Dot5ToDouble(top) << "left=" << left->edge << "right=" << right->edge;
+ }
+ right->y_left = y;
+ left->y_right = y;
+ }
+ left->mark = false;
+ }
+ if (scanline.old[scanline.old_size - 1]->mark) {
+ scanline.old[scanline.old_size - 1]->y_right = y;
+ scanline.old[scanline.old_size - 1]->mark = false;
+ }
+ } else {
+ // odd-even fill rule
+ for (int i = 0; i < scanline.old_size; i += 2) {
+ Edge *left = scanline.old[i];
+ Edge *right = scanline.old[i+1];
+ if (!emit_clever || left->mark || right->mark) {
+ Q27Dot5 top = qMax(left->y_right, right->y_left);
+ if (top != y) {
+ QTessellator::Trapezoid trap;
+ fillTrapezoid(top, y, left->edge, right->edge, vertices, &trap);
+ tessellator->addTrap(trap);
+ }
+// QDEBUG() << " top=" << Q27Dot5ToDouble(top) << "left=" << left->edge << "right=" << right->edge;
+ left->y_left = y;
+ left->y_right = y;
+ right->y_left = y;
+ right->y_right = y;
+ left->mark = right->mark = false;
+ }
+ }
+ }
+}
+
+
+void QTessellatorPrivate::processIntersections()
+{
+ QDEBUG() << "PROCESS INTERSECTIONS";
+ // process intersections
+ while (!intersections.isEmpty()) {
+ Intersections::iterator it = intersections.begin();
+ if (it.key().y != y)
+ break;
+
+ // swap edges
+ QDEBUG() << " swapping intersecting edges ";
+ int min = scanline.size;
+ int max = 0;
+ Q27Dot5 xmin = INT_MAX;
+ Q27Dot5 xmax = INT_MIN;
+ int num = 0;
+ while (1) {
+ const Intersection &i = it.key();
+ int next = it->next;
+
+ int edgePos = scanline.findEdge(i.edge);
+ if (edgePos >= 0) {
+ ++num;
+ min = qMin(edgePos, min);
+ max = qMax(edgePos, max);
+ Edge *edge = scanline.edges[edgePos];
+ xmin = qMin(xmin, edge->positionAt(y));
+ xmax = qMax(xmax, edge->positionAt(y));
+ }
+ Intersection key;
+ key.y = y;
+ key.edge = next;
+ it = intersections.find(key);
+ intersections.remove(i);
+ if (it == intersections.end())
+ break;
+ }
+ if (num < 2)
+ continue;
+
+ Q_ASSERT(min != max);
+ QDEBUG() << "sorting between" << min << "and" << max << "xpos=" << xmin << xmax;
+ while (min > 0 && scanline.edges[min - 1]->positionAt(y) >= xmin) {
+ QDEBUG() << " adding edge on left";
+ --min;
+ }
+ while (max < scanline.size - 1 && scanline.edges[max + 1]->positionAt(y) <= xmax) {
+ QDEBUG() << " adding edge on right";
+ ++max;
+ }
+
+ qSort(scanline.edges + min, scanline.edges + max + 1, EdgeSorter(y));
+#ifdef DEBUG
+ for (int i = min; i <= max; ++i)
+ QDEBUG() << " " << scanline.edges[i]->edge << "at pos" << i;
+#endif
+ for (int i = min; i <= max; ++i) {
+ Edge *edge = scanline.edges[i];
+ edge->intersect_left = true;
+ edge->intersect_right = true;
+ edge->mark = true;
+ }
+ }
+}
+
+void QTessellatorPrivate::removeEdges()
+{
+ int cv = currentVertex;
+ while (cv < vertices.nPoints) {
+ const Vertex *v = vertices.sorted[cv];
+ if (v->y > y)
+ break;
+ if (v->flags & LineBeforeEnds) {
+ QDEBUG() << " removing edge" << vertices.prevPos(v);
+ int pos = scanline.findEdge(vertices.prevPos(v));
+ if (pos == -1)
+ continue;
+ scanline.edges[pos]->mark = true;
+ if (pos > 0)
+ scanline.edges[pos - 1]->intersect_right = true;
+ if (pos < scanline.size - 1)
+ scanline.edges[pos + 1]->intersect_left = true;
+ scanline.removeAt(pos);
+ }
+ if (v->flags & LineAfterEnds) {
+ QDEBUG() << " removing edge" << vertices.position(v);
+ int pos = scanline.findEdge(vertices.position(v));
+ if (pos == -1)
+ continue;
+ scanline.edges[pos]->mark = true;
+ if (pos > 0)
+ scanline.edges[pos - 1]->intersect_right = true;
+ if (pos < scanline.size - 1)
+ scanline.edges[pos + 1]->intersect_left = true;
+ scanline.removeAt(pos);
+ }
+ ++cv;
+ }
+}
+
+void QTessellatorPrivate::addEdges()
+{
+ while (currentVertex < vertices.nPoints) {
+ const Vertex *v = vertices.sorted[currentVertex];
+ if (v->y > y)
+ break;
+ if (v->flags & LineBeforeStarts) {
+ // add new edge
+ int start = vertices.prevPos(v);
+ Edge e(vertices, start);
+ int pos = scanline.findEdgePosition(e);
+ QDEBUG() << " adding edge" << start << "at position" << pos;
+ scanline.insert(pos, e);
+ if (!mark_clever || !(v->flags & LineAfterEnds)) {
+ if (pos > 0)
+ scanline.edges[pos - 1]->mark = true;
+ if (pos < scanline.size - 1)
+ scanline.edges[pos + 1]->mark = true;
+ }
+ }
+ if (v->flags & LineAfterStarts) {
+ Edge e(vertices, vertices.position(v));
+ int pos = scanline.findEdgePosition(e);
+ QDEBUG() << " adding edge" << vertices.position(v) << "at position" << pos;
+ scanline.insert(pos, e);
+ if (!mark_clever || !(v->flags & LineBeforeEnds)) {
+ if (pos > 0)
+ scanline.edges[pos - 1]->mark = true;
+ if (pos < scanline.size - 1)
+ scanline.edges[pos + 1]->mark = true;
+ }
+ }
+ if (v->flags & LineAfterHorizontal) {
+ int pos1 = scanline.findEdgePosition(v->x, v->y);
+ const Vertex *next = vertices.next(v);
+ Q_ASSERT(v->y == next->y);
+ int pos2 = scanline.findEdgePosition(next->x, next->y);
+ if (pos2 < pos1)
+ qSwap(pos1, pos2);
+ if (pos1 > 0)
+ --pos1;
+ if (pos2 == scanline.size)
+ --pos2;
+ //QDEBUG() << "marking horizontal edge from " << pos1 << "to" << pos2;
+ scanline.markEdges(pos1, pos2);
+ }
+ ++currentVertex;
+ }
+}
+
+#ifdef DEBUG
+static void checkLinkChain(const QTessellatorPrivate::Intersections &intersections,
+ QTessellatorPrivate::Intersection i)
+{
+// qDebug() << " Link chain: ";
+ int end = i.edge;
+ while (1) {
+ QTessellatorPrivate::IntersectionLink l = intersections.value(i);
+// qDebug() << " " << i.edge << "next=" << l.next << "prev=" << l.prev;
+ if (l.next == end)
+ break;
+ Q_ASSERT(l.next != -1);
+ Q_ASSERT(l.prev != -1);
+
+ QTessellatorPrivate::Intersection i2 = i;
+ i2.edge = l.next;
+ QTessellatorPrivate::IntersectionLink l2 = intersections.value(i2);
+
+ Q_ASSERT(l2.next != -1);
+ Q_ASSERT(l2.prev != -1);
+ Q_ASSERT(l.next == i2.edge);
+ Q_ASSERT(l2.prev == i.edge);
+ i = i2;
+ }
+}
+#endif
+
+bool QTessellatorPrivate::edgeInChain(Intersection i, int edge)
+{
+ int end = i.edge;
+ while (1) {
+ if (i.edge == edge)
+ return true;
+ IntersectionLink l = intersections.value(i);
+ if (l.next == end)
+ break;
+ Q_ASSERT(l.next != -1);
+ Q_ASSERT(l.prev != -1);
+
+ Intersection i2 = i;
+ i2.edge = l.next;
+
+#ifndef QT_NO_DEBUG
+ IntersectionLink l2 = intersections.value(i2);
+ Q_ASSERT(l2.next != -1);
+ Q_ASSERT(l2.prev != -1);
+ Q_ASSERT(l.next == i2.edge);
+ Q_ASSERT(l2.prev == i.edge);
+#endif
+ i = i2;
+ }
+ return false;
+}
+
+
+void QTessellatorPrivate::addIntersection(const Edge *e1, const Edge *e2)
+{
+ const IntersectionLink emptyLink = {-1, -1};
+
+ int next = vertices.nextPos(vertices[e1->edge]);
+ if (e2->edge == next)
+ return;
+ int prev = vertices.prevPos(vertices[e1->edge]);
+ if (e2->edge == prev)
+ return;
+
+ Q27Dot5 yi;
+ bool det_positive;
+ bool isect = e1->intersect(*e2, &yi, &det_positive);
+ QDEBUG("checking edges %d and %d", e1->edge, e2->edge);
+ if (!isect) {
+ QDEBUG() << " no intersection";
+ return;
+ }
+
+ // don't emit an intersection if it's at the start of a line segment or above us
+ if (yi <= y) {
+ if (!det_positive)
+ return;
+ QDEBUG() << " ----->>>>>> WRONG ORDER!";
+ yi = y;
+ }
+ QDEBUG() << " between edges " << e1->edge << "and" << e2->edge << "at point ("
+ << Q27Dot5ToDouble(yi) << ")";
+
+ Intersection i1;
+ i1.y = yi;
+ i1.edge = e1->edge;
+ IntersectionLink link1 = intersections.value(i1, emptyLink);
+ Intersection i2;
+ i2.y = yi;
+ i2.edge = e2->edge;
+ IntersectionLink link2 = intersections.value(i2, emptyLink);
+
+ // new pair of edges
+ if (link1.next == -1 && link2.next == -1) {
+ link1.next = link1.prev = i2.edge;
+ link2.next = link2.prev = i1.edge;
+ } else if (link1.next == i2.edge || link1.prev == i2.edge
+ || link2.next == i1.edge || link2.prev == i1.edge) {
+#ifdef DEBUG
+ checkLinkChain(intersections, i1);
+ checkLinkChain(intersections, i2);
+ Q_ASSERT(edgeInChain(i1, i2.edge));
+#endif
+ return;
+ } else if (link1.next == -1 || link2.next == -1) {
+ if (link2.next == -1) {
+ qSwap(i1, i2);
+ qSwap(link1, link2);
+ }
+ Q_ASSERT(link1.next == -1);
+#ifdef DEBUG
+ checkLinkChain(intersections, i2);
+#endif
+ // only i2 in list
+ link1.next = i2.edge;
+ link1.prev = link2.prev;
+ link2.prev = i1.edge;
+ Intersection other;
+ other.y = yi;
+ other.edge = link1.prev;
+ IntersectionLink link = intersections.value(other, emptyLink);
+ Q_ASSERT(link.next == i2.edge);
+ Q_ASSERT(link.prev != -1);
+ link.next = i1.edge;
+ intersections.insert(other, link);
+ } else {
+ bool connected = edgeInChain(i1, i2.edge);
+ if (connected)
+ return;
+#ifdef DEBUG
+ checkLinkChain(intersections, i1);
+ checkLinkChain(intersections, i2);
+#endif
+ // both already in some list. Have to make sure they are connected
+ // this can be done by cutting open the ring(s) after the two eges and
+ // connecting them again
+ Intersection other1;
+ other1.y = yi;
+ other1.edge = link1.next;
+ IntersectionLink linko1 = intersections.value(other1, emptyLink);
+ Intersection other2;
+ other2.y = yi;
+ other2.edge = link2.next;
+ IntersectionLink linko2 = intersections.value(other2, emptyLink);
+
+ linko1.prev = i2.edge;
+ link2.next = other1.edge;
+
+ linko2.prev = i1.edge;
+ link1.next = other2.edge;
+ intersections.insert(other1, linko1);
+ intersections.insert(other2, linko2);
+ }
+ intersections.insert(i1, link1);
+ intersections.insert(i2, link2);
+#ifdef DEBUG
+ checkLinkChain(intersections, i1);
+ checkLinkChain(intersections, i2);
+ Q_ASSERT(edgeInChain(i1, i2.edge));
+#endif
+ return;
+
+}
+
+
+void QTessellatorPrivate::addIntersections()
+{
+ if (scanline.size) {
+ QDEBUG() << "INTERSECTIONS";
+ // check marked edges for intersections
+#ifdef DEBUG
+ for (int i = 0; i < scanline.size; ++i) {
+ Edge *e = scanline.edges[i];
+ QDEBUG() << " " << i << e->edge << "isect=(" << e->intersect_left << e->intersect_right
+ << ")";
+ }
+#endif
+
+ for (int i = 0; i < scanline.size - 1; ++i) {
+ Edge *e1 = scanline.edges[i];
+ Edge *e2 = scanline.edges[i + 1];
+ // check for intersection
+ if (e1->intersect_right || e2->intersect_left)
+ addIntersection(e1, e2);
+ }
+ }
+#if 0
+ if (intersections.constBegin().key().y == y) {
+ QDEBUG() << "----------------> intersection on same line";
+ scanline.clearMarks();
+ scanline.processIntersections(y, &intersections);
+ goto redo;
+ }
+#endif
+}
+
+
+QTessellator::QTessellator()
+{
+ d = new QTessellatorPrivate;
+}
+
+QTessellator::~QTessellator()
+{
+ delete d;
+}
+
+void QTessellator::setWinding(bool w)
+{
+ d->winding = w;
+}
+
+
+QRectF QTessellator::tessellate(const QPointF *points, int nPoints)
+{
+ Q_ASSERT(points[0] == points[nPoints-1]);
+ --nPoints;
+
+#ifdef DEBUG
+ QDEBUG()<< "POINTS:";
+ for (int i = 0; i < nPoints; ++i) {
+ QDEBUG() << points[i];
+ }
+#endif
+
+ // collect edges and calculate bounds
+ d->vertices.nPoints = nPoints;
+ d->vertices.init(nPoints);
+
+ int maxActiveEdges = 0;
+ QRectF br = d->collectAndSortVertices(points, &maxActiveEdges);
+ d->cancelCoincidingEdges();
+
+#ifdef DEBUG
+ QDEBUG() << "nPoints = " << nPoints << "using " << d->vertices.nPoints;
+ QDEBUG()<< "VERTICES:";
+ for (int i = 0; i < d->vertices.nPoints; ++i) {
+ QDEBUG() << " " << i << ": "
+ << "point=" << d->vertices.position(d->vertices.sorted[i])
+ << "flags=" << d->vertices.sorted[i]->flags
+ << "pos=(" << Q27Dot5ToDouble(d->vertices.sorted[i]->x) << "/"
+ << Q27Dot5ToDouble(d->vertices.sorted[i]->y) << ")";
+ }
+#endif
+
+ d->scanline.init(maxActiveEdges);
+ d->y = INT_MIN/256;
+ d->currentVertex = 0;
+
+ while (d->currentVertex < d->vertices.nPoints) {
+ d->scanline.clearMarks();
+
+ d->y = d->vertices.sorted[d->currentVertex]->y;
+ if (!d->intersections.isEmpty())
+ d->y = qMin(d->y, d->intersections.constBegin().key().y);
+
+ QDEBUG()<< "===== SCANLINE: y =" << Q27Dot5ToDouble(d->y) << " =====";
+
+ d->scanline.prepareLine();
+ d->processIntersections();
+ d->removeEdges();
+ d->addEdges();
+ d->addIntersections();
+ d->emitEdges(this);
+ d->scanline.lineDone();
+
+#ifdef DEBUG
+ QDEBUG()<< "===== edges:";
+ for (int i = 0; i < d->scanline.size; ++i) {
+ QDEBUG() << " " << d->scanline.edges[i]->edge
+ << "p0= (" << Q27Dot5ToDouble(d->scanline.edges[i]->v0->x)
+ << "/" << Q27Dot5ToDouble(d->scanline.edges[i]->v0->y)
+ << ") p1= (" << Q27Dot5ToDouble(d->scanline.edges[i]->v1->x)
+ << "/" << Q27Dot5ToDouble(d->scanline.edges[i]->v1->y) << ")"
+ << "x=" << Q27Dot5ToDouble(d->scanline.edges[i]->positionAt(d->y))
+ << "isLeftOfNext="
+ << ((i < d->scanline.size - 1)
+ ? d->scanline.edges[i]->isLeftOf(*d->scanline.edges[i+1], d->y)
+ : true);
+ }
+#endif
+}
+
+ d->scanline.done();
+ d->intersections.clear();
+ return br;
+}
+
+// tessellates the given convex polygon
+void QTessellator::tessellateConvex(const QPointF *points, int nPoints)
+{
+ Q_ASSERT(points[0] == points[nPoints-1]);
+ --nPoints;
+
+ d->vertices.nPoints = nPoints;
+ d->vertices.init(nPoints);
+
+ for (int i = 0; i < nPoints; ++i) {
+ d->vertices[i]->x = FloatToQ27Dot5(points[i].x());
+ d->vertices[i]->y = FloatToQ27Dot5(points[i].y());
+ }
+
+ int left = 0, right = 0;
+
+ int top = 0;
+ for (int i = 1; i < nPoints; ++i) {
+ if (d->vertices[i]->y < d->vertices[top]->y)
+ top = i;
+ }
+
+ left = (top + nPoints - 1) % nPoints;
+ right = (top + 1) % nPoints;
+
+ while (d->vertices[left]->x == d->vertices[top]->x && d->vertices[left]->y == d->vertices[top]->y && left != right)
+ left = (left + nPoints - 1) % nPoints;
+
+ while (d->vertices[right]->x == d->vertices[top]->x && d->vertices[right]->y == d->vertices[top]->y && left != right)
+ right = (right + 1) % nPoints;
+
+ if (left == right)
+ return;
+
+ int dir = 1;
+
+ Vertex dLeft = { d->vertices[top]->x - d->vertices[left]->x,
+ d->vertices[top]->y - d->vertices[left]->y };
+
+ Vertex dRight = { d->vertices[right]->x - d->vertices[top]->x,
+ d->vertices[right]->y - d->vertices[top]->y };
+
+ Q27Dot5 cross = dLeft.x * dRight.y - dLeft.y * dRight.x;
+
+ // flip direction if polygon is clockwise
+ if (cross < 0 || (cross == 0 && dLeft.x > 0)) {
+ qSwap(left, right);
+ dir = -1;
+ }
+
+ Vertex *lastLeft = d->vertices[top];
+ Vertex *lastRight = d->vertices[top];
+
+ QTessellator::Trapezoid trap;
+
+ while (lastLeft->y == d->vertices[left]->y && left != right) {
+ lastLeft = d->vertices[left];
+ left = (left + nPoints - dir) % nPoints;
+ }
+
+ while (lastRight->y == d->vertices[right]->y && left != right) {
+ lastRight = d->vertices[right];
+ right = (right + nPoints + dir) % nPoints;
+ }
+
+ while (true) {
+ trap.top = qMax(lastRight->y, lastLeft->y);
+ trap.bottom = qMin(d->vertices[left]->y, d->vertices[right]->y);
+ trap.topLeft = lastLeft;
+ trap.topRight = lastRight;
+ trap.bottomLeft = d->vertices[left];
+ trap.bottomRight = d->vertices[right];
+
+ if (trap.bottom > trap.top)
+ addTrap(trap);
+
+ if (left == right)
+ break;
+
+ if (d->vertices[right]->y < d->vertices[left]->y) {
+ do {
+ lastRight = d->vertices[right];
+ right = (right + nPoints + dir) % nPoints;
+ }
+ while (lastRight->y == d->vertices[right]->y && left != right);
+ } else {
+ do {
+ lastLeft = d->vertices[left];
+ left = (left + nPoints - dir) % nPoints;
+ }
+ while (lastLeft->y == d->vertices[left]->y && left != right);
+ }
+ }
+}
+
+// tessellates the stroke of the line from a_ to b_ with the given width and a flat cap
+void QTessellator::tessellateRect(const QPointF &a_, const QPointF &b_, qreal width)
+{
+ Vertex a = { FloatToQ27Dot5(a_.x()), FloatToQ27Dot5(a_.y()) };
+ Vertex b = { FloatToQ27Dot5(b_.x()), FloatToQ27Dot5(b_.y()) };
+
+ QPointF pa = a_, pb = b_;
+
+ if (a.y > b.y) {
+ qSwap(a, b);
+ qSwap(pa, pb);
+ }
+
+ Vertex delta = { b.x - a.x, b.y - a.y };
+
+ if (delta.x == 0 && delta.y == 0)
+ return;
+
+ qreal hw = 0.5 * width;
+
+ if (delta.x == 0) {
+ Q27Dot5 halfWidth = FloatToQ27Dot5(hw);
+
+ if (halfWidth == 0)
+ return;
+
+ Vertex topLeft = { a.x - halfWidth, a.y };
+ Vertex topRight = { a.x + halfWidth, a.y };
+ Vertex bottomLeft = { a.x - halfWidth, b.y };
+ Vertex bottomRight = { a.x + halfWidth, b.y };
+
+ QTessellator::Trapezoid trap = { topLeft.y, bottomLeft.y, &topLeft, &bottomLeft, &topRight, &bottomRight };
+ addTrap(trap);
+ } else if (delta.y == 0) {
+ Q27Dot5 halfWidth = FloatToQ27Dot5(hw);
+
+ if (halfWidth == 0)
+ return;
+
+ if (a.x > b.x)
+ qSwap(a.x, b.x);
+
+ Vertex topLeft = { a.x, a.y - halfWidth };
+ Vertex topRight = { b.x, a.y - halfWidth };
+ Vertex bottomLeft = { a.x, a.y + halfWidth };
+ Vertex bottomRight = { b.x, a.y + halfWidth };
+
+ QTessellator::Trapezoid trap = { topLeft.y, bottomLeft.y, &topLeft, &bottomLeft, &topRight, &bottomRight };
+ addTrap(trap);
+ } else {
+ QPointF perp(pb.y() - pa.y(), pa.x() - pb.x());
+ qreal length = qSqrt(perp.x() * perp.x() + perp.y() * perp.y());
+
+ if (qFuzzyCompare(length + 1, static_cast<qreal>(1)))
+ return;
+
+ // need the half of the width
+ perp *= hw / length;
+
+ QPointF pta = pa + perp;
+ QPointF ptb = pa - perp;
+ QPointF ptc = pb - perp;
+ QPointF ptd = pb + perp;
+
+ Vertex ta = { FloatToQ27Dot5(pta.x()), FloatToQ27Dot5(pta.y()) };
+ Vertex tb = { FloatToQ27Dot5(ptb.x()), FloatToQ27Dot5(ptb.y()) };
+ Vertex tc = { FloatToQ27Dot5(ptc.x()), FloatToQ27Dot5(ptc.y()) };
+ Vertex td = { FloatToQ27Dot5(ptd.x()), FloatToQ27Dot5(ptd.y()) };
+
+ if (ta.y < tb.y) {
+ if (tb.y < td.y) {
+ QTessellator::Trapezoid top = { ta.y, tb.y, &ta, &tb, &ta, &td };
+ QTessellator::Trapezoid bottom = { td.y, tc.y, &tb, &tc, &td, &tc };
+ addTrap(top);
+ addTrap(bottom);
+
+ QTessellator::Trapezoid middle = { tb.y, td.y, &tb, &tc, &ta, &td };
+ addTrap(middle);
+ } else {
+ QTessellator::Trapezoid top = { ta.y, td.y, &ta, &tb, &ta, &td };
+ QTessellator::Trapezoid bottom = { tb.y, tc.y, &tb, &tc, &td, &tc };
+ addTrap(top);
+ addTrap(bottom);
+
+ if (tb.y != td.y) {
+ QTessellator::Trapezoid middle = { td.y, tb.y, &ta, &tb, &td, &tc };
+ addTrap(middle);
+ }
+ }
+ } else {
+ if (ta.y < tc.y) {
+ QTessellator::Trapezoid top = { tb.y, ta.y, &tb, &tc, &tb, &ta };
+ QTessellator::Trapezoid bottom = { tc.y, td.y, &tc, &td, &ta, &td };
+ addTrap(top);
+ addTrap(bottom);
+
+ QTessellator::Trapezoid middle = { ta.y, tc.y, &tb, &tc, &ta, &td };
+ addTrap(middle);
+ } else {
+ QTessellator::Trapezoid top = { tb.y, tc.y, &tb, &tc, &tb, &ta };
+ QTessellator::Trapezoid bottom = { ta.y, td.y, &tc, &td, &ta, &td };
+ addTrap(top);
+ addTrap(bottom);
+
+ if (ta.y != tc.y) {
+ QTessellator::Trapezoid middle = { tc.y, ta.y, &tc, &td, &tb, &ta };
+ addTrap(middle);
+ }
+ }
+ }
+ }
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/painting/qtessellator_p.h b/src/gui/painting/qtessellator_p.h
new file mode 100644
index 0000000000..4f476cb825
--- /dev/null
+++ b/src/gui/painting/qtessellator_p.h
@@ -0,0 +1,102 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QTESSELATOR_P_H
+#define QTESSELATOR_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 <qpoint.h>
+#include <qrect.h>
+
+QT_BEGIN_NAMESPACE
+
+class QTessellatorPrivate;
+
+typedef int Q27Dot5;
+#define Q27Dot5ToDouble(i) ((i)/32.)
+#define FloatToQ27Dot5(i) (int)((i) * 32)
+#define IntToQ27Dot5(i) ((i) << 5)
+#define Q27Dot5ToXFixed(i) ((i) << 11)
+#define Q27Dot5Factor 32
+
+class Q_GUI_EXPORT QTessellator {
+public:
+ QTessellator();
+ virtual ~QTessellator();
+
+ QRectF tessellate(const QPointF *points, int nPoints);
+ void tessellateConvex(const QPointF *points, int nPoints);
+ void tessellateRect(const QPointF &a, const QPointF &b, qreal width);
+
+ void setWinding(bool w);
+
+ struct Vertex {
+ Q27Dot5 x;
+ Q27Dot5 y;
+ };
+ struct Trapezoid {
+ Q27Dot5 top;
+ Q27Dot5 bottom;
+ const Vertex *topLeft;
+ const Vertex *bottomLeft;
+ const Vertex *topRight;
+ const Vertex *bottomRight;
+ };
+ virtual void addTrap(const Trapezoid &trap) = 0;
+
+private:
+ friend class QTessellatorPrivate;
+ QTessellatorPrivate *d;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/gui/painting/qtextureglyphcache.cpp b/src/gui/painting/qtextureglyphcache.cpp
new file mode 100644
index 0000000000..1ea40bad89
--- /dev/null
+++ b/src/gui/painting/qtextureglyphcache.cpp
@@ -0,0 +1,316 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <qmath.h>
+
+#include "qtextureglyphcache_p.h"
+
+#include "private/qnumeric_p.h"
+#include "private/qnativeimage_p.h"
+#include "private/qfontengine_ft_p.h"
+
+QT_BEGIN_NAMESPACE
+
+// #define CACHE_DEBUG
+
+void QTextureGlyphCache::populate(const QTextItemInt &ti,
+ const QVarLengthArray<glyph_t> &glyphs,
+ const QVarLengthArray<QFixedPoint> &)
+{
+#ifdef CACHE_DEBUG
+ printf("Populating with '%s'\n", QString::fromRawData(ti.chars, ti.num_chars).toLatin1().data());
+ qDebug() << " -> current transformation: " << m_transform;
+#endif
+
+ m_current_textitem = &ti;
+ const int margin = glyphMargin();
+
+ QHash<glyph_t, Coord> listItemCoordinates;
+ int rowHeight = 0;
+
+ // check each glyph for its metrics and get the required rowHeight.
+ for (int i=0; i < glyphs.size(); ++i) {
+ const glyph_t glyph = glyphs[i];
+ if (coords.contains(glyph))
+ continue;
+ if (listItemCoordinates.contains(glyph))
+ continue;
+ glyph_metrics_t metrics = ti.fontEngine->boundingBox(glyph, m_transform);
+
+#ifdef CACHE_DEBUG
+ printf("'%c' (%4x): w=%.2f, h=%.2f, xoff=%.2f, yoff=%.2f, x=%.2f, y=%.2f, ti.ascent=%.2f, ti.descent=%.2f\n",
+ ti.chars[i].toLatin1(),
+ glyph,
+ metrics.width.toReal(),
+ metrics.height.toReal(),
+ metrics.xoff.toReal(),
+ metrics.yoff.toReal(),
+ metrics.x.toReal(),
+ metrics.y.toReal(),
+ ti.ascent.toReal(),
+ ti.descent.toReal());
+#endif
+ int glyph_width = metrics.width.ceil().toInt() + margin * 2;
+ int glyph_height = metrics.height.ceil().toInt() + margin * 2;
+ if (glyph_height == 0 || glyph_width == 0)
+ continue;
+
+ // align to 8-bit boundary
+ if (m_type == QFontEngineGlyphCache::Raster_Mono)
+ glyph_width = (glyph_width+7)&~7;
+
+ Coord c = { 0, 0, // will be filled in later
+ glyph_width,
+ glyph_height, // texture coords
+ metrics.x.truncate(),
+ -metrics.y.truncate() }; // baseline for horizontal scripts
+
+ listItemCoordinates.insert(glyph, c);
+ rowHeight = qMax(rowHeight, glyph_height);
+ }
+ if (listItemCoordinates.isEmpty())
+ return;
+
+ rowHeight += margin * 2;
+ if (isNull())
+ createCache(256, rowHeight);
+
+ // now actually use the coords and paint the wanted glyps into cache.
+ QHash<glyph_t, Coord>::iterator iter = listItemCoordinates.begin();
+ while (iter != listItemCoordinates.end()) {
+ Coord c = iter.value();
+
+ if (m_cx + c.w > m_w) {
+ // no room on the current line, start new glyph strip
+ m_cx = 0;
+ m_cy = m_h;
+ }
+ if (m_cy + c.h > m_h) {
+ int new_height;
+ if (m_cx == 0) { // add a whole row
+ new_height = m_h + rowHeight;
+ m_cy = m_h;
+ } else { // just extend row
+ new_height = m_cy + rowHeight;
+ }
+ // if no room in the current texture - realloc a larger texture
+ resizeTextureData(m_w, new_height);
+ m_h = new_height;
+ }
+
+ c.x = m_cx;
+ c.y = m_cy;
+
+ fillTexture(c, iter.key());
+ coords.insert(iter.key(), c);
+
+ if (m_cx + c.w > m_w) {
+ m_cx = 0;
+ m_cy += rowHeight;
+ } else {
+ // for the Mono case, glyph_width is 8-bit aligned,
+ // and therefore so will m_cx
+ m_cx += c.w;
+ }
+ ++iter;
+ }
+
+
+}
+
+/************************************************************************
+ * QImageTextureGlyphCache
+ */
+
+void QImageTextureGlyphCache::resizeTextureData(int width, int height)
+{
+ m_image = m_image.copy(0, 0, width, height);
+}
+
+void QImageTextureGlyphCache::createTextureData(int width, int height)
+{
+ switch (m_type) {
+ case QFontEngineGlyphCache::Raster_Mono:
+ m_image = QImage(width, height, QImage::Format_Mono);
+ break;
+ case QFontEngineGlyphCache::Raster_A8: {
+ m_image = QImage(width, height, QImage::Format_Indexed8);
+ m_image.fill(0);
+ QVector<QRgb> colors(256);
+ QRgb *it = colors.data();
+ for (int i=0; i<256; ++i, ++it)
+ *it = 0xff000000 | i | (i<<8) | (i<<16);
+ m_image.setColorTable(colors);
+ break; }
+ case QFontEngineGlyphCache::Raster_RGBMask:
+ m_image = QImage(width, height, QImage::Format_RGB32);
+ break;
+ }
+}
+
+int QImageTextureGlyphCache::glyphMargin() const
+{
+#ifdef Q_WS_MAC
+ return 2;
+#else
+ return m_type == QFontEngineGlyphCache::Raster_RGBMask ? 2 : 0;
+#endif
+}
+
+void QImageTextureGlyphCache::fillTexture(const Coord &c, glyph_t g)
+{
+ QImage mask;
+#if defined(Q_WS_X11)
+ if (m_transform.type() > QTransform::TxTranslate) {
+ QFontEngineFT::GlyphFormat format = QFontEngineFT::Format_None;
+ switch (m_type) {
+ case Raster_RGBMask:
+ format = QFontEngineFT::Format_A32; break;
+ case Raster_A8:
+ format = QFontEngineFT::Format_A8; break;
+ case Raster_Mono:
+ format = QFontEngineFT::Format_Mono; break;
+ };
+
+ QFontEngineFT *ft = static_cast<QFontEngineFT*> (m_current_textitem->fontEngine);
+ QFontEngineFT::QGlyphSet *gset = ft->loadTransformedGlyphSet(m_transform);
+
+ if (gset && ft->loadGlyphs(gset, &g, 1, format)) {
+ QFontEngineFT::Glyph *glyph = gset->glyph_data.value(g);
+ const int bytesPerLine = (format == QFontEngineFT::Format_Mono ? ((glyph->width + 31) & ~31) >> 3
+ : (glyph->width + 3) & ~3);
+ mask = QImage(glyph->data, glyph->width, glyph->height, bytesPerLine, m_image.format());
+ }
+ } else
+#endif
+ if (m_type == QFontEngineGlyphCache::Raster_RGBMask)
+ mask = m_current_textitem->fontEngine->alphaRGBMapForGlyph(g, glyphMargin(), m_transform);
+ else
+ mask = m_current_textitem->fontEngine->alphaMapForGlyph(g, m_transform);
+
+#ifdef CACHE_DEBUG
+ printf("fillTexture of %dx%d at %d,%d in the cache of %dx%d\n", c.w, c.h, c.x, c.y, m_image.width(), m_image.height());
+ if (mask.width() > c.w || mask.height() > c.h) {
+ printf(" ERROR; mask is bigger than reserved space! %dx%d instead of %dx%d\n", mask.width(), mask.height(), c.w,c.h);
+ return;
+ }
+#endif
+
+ if (m_type == QFontEngineGlyphCache::Raster_RGBMask) {
+ QPainter p(&m_image);
+ p.setCompositionMode(QPainter::CompositionMode_Source);
+ p.fillRect(c.x, c.y, c.w, c.h, QColor(0,0,0,0)); // TODO optimize this
+ p.drawImage(c.x, c.y, mask);
+ p.end();
+ } else if (m_type == QFontEngineGlyphCache::Raster_Mono) {
+ if (mask.depth() > 1) {
+ // TODO optimize this
+ mask = mask.alphaChannel();
+ mask.invertPixels();
+ mask = mask.convertToFormat(QImage::Format_Mono);
+ }
+
+ int mw = qMin(mask.width(), c.w);
+ int mh = qMin(mask.height(), c.h);
+ uchar *d = m_image.bits();
+ int dbpl = m_image.bytesPerLine();
+
+ for (int y = 0; y < c.h; ++y) {
+ uchar *dest = d + (c.y + y) *dbpl + c.x/8;
+
+ if (y < mh) {
+ uchar *src = mask.scanLine(y);
+ for (int x = 0; x < c.w/8; ++x) {
+ if (x < (mw+7)/8)
+ dest[x] = src[x];
+ else
+ dest[x] = 0;
+ }
+ } else {
+ for (int x = 0; x < c.w/8; ++x)
+ dest[x] = 0;
+ }
+ }
+ } else { // A8
+ int mw = qMin(mask.width(), c.w);
+ int mh = qMin(mask.height(), c.h);
+ uchar *d = m_image.bits();
+ int dbpl = m_image.bytesPerLine();
+
+ if (mask.depth() == 1) {
+ for (int y = 0; y < c.h; ++y) {
+ uchar *dest = d + (c.y + y) *dbpl + c.x;
+ if (y < mh) {
+ uchar *src = (uchar *) mask.scanLine(y);
+ for (int x = 0; x < c.w; ++x) {
+ if (x < mw)
+ dest[x] = (src[x >> 3] & (1 << (7 - (x & 7)))) > 0 ? 255 : 0;
+ }
+ }
+ }
+ } else if (mask.depth() == 8) {
+ for (int y = 0; y < c.h; ++y) {
+ uchar *dest = d + (c.y + y) *dbpl + c.x;
+ if (y < mh) {
+ uchar *src = (uchar *) mask.scanLine(y);
+ for (int x = 0; x < c.w; ++x) {
+ if (x < mw)
+ dest[x] = src[x];
+ }
+ }
+ }
+ }
+ }
+
+#ifdef CACHE_DEBUG
+// QPainter p(&m_image);
+// p.drawLine(
+ QPoint base(c.x + glyphMargin(), c.y + glyphMargin() + c.baseLineY-1);
+ if (m_image.rect().contains(base))
+ m_image.setPixel(base, 255);
+ m_image.save(QString::fromLatin1("cache-%1-%2-%3.png")
+ .arg(m_current_textitem->font().family())
+ .arg(m_current_textitem->font().pointSize())
+ .arg(m_transform.type()));
+#endif
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/painting/qtextureglyphcache_p.h b/src/gui/painting/qtextureglyphcache_p.h
new file mode 100644
index 0000000000..7f2c478865
--- /dev/null
+++ b/src/gui/painting/qtextureglyphcache_p.h
@@ -0,0 +1,141 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QTEXTUREGLYPHCACHE_P_H
+#define QTEXTUREGLYPHCACHE_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 <qhash.h>
+#include <qimage.h>
+#include <qobject.h>
+#include <qtransform.h>
+
+#include <private/qfontengineglyphcache_p.h>
+
+struct glyph_metrics_t;
+typedef unsigned int glyph_t;
+
+
+QT_BEGIN_NAMESPACE
+
+class QTextItemInt;
+
+class Q_GUI_EXPORT QTextureGlyphCache : public QFontEngineGlyphCache
+{
+public:
+ QTextureGlyphCache(QFontEngineGlyphCache::Type type, const QTransform &matrix)
+ : QFontEngineGlyphCache(matrix), m_w(0), m_h(0), m_cx(0), m_cy(0), m_type(type) { }
+
+ virtual ~QTextureGlyphCache() { }
+
+ struct Coord {
+ int x;
+ int y;
+ int w;
+ int h;
+
+ int baseLineX;
+ int baseLineY;
+ };
+
+ void populate(const QTextItemInt &ti,
+ const QVarLengthArray<glyph_t> &glyphs,
+ const QVarLengthArray<QFixedPoint> &positions);
+
+ virtual void createTextureData(int width, int height) = 0;
+ virtual void resizeTextureData(int width, int height) = 0;
+ virtual int glyphMargin() const { return 0; }
+
+ QFontEngineGlyphCache::Type cacheType() const { return m_type; }
+
+ virtual void fillTexture(const Coord &coord, glyph_t glyph) = 0;
+
+ inline void createCache(int width, int height) {
+ m_w = width;
+ m_h = height;
+ createTextureData(width, height);
+ }
+
+ inline bool isNull() const { return m_w <= 0 || m_h <= 0; }
+
+ QHash<glyph_t, Coord> coords;
+
+protected:
+ const QTextItemInt *m_current_textitem;
+
+ int m_w; // image width
+ int m_h; // image height
+ int m_cx; // current x
+ int m_cy; // current y
+ QFontEngineGlyphCache::Type m_type;
+
+};
+
+
+class Q_GUI_EXPORT QImageTextureGlyphCache : public QTextureGlyphCache
+{
+public:
+ QImageTextureGlyphCache(QFontEngineGlyphCache::Type type, const QTransform &matrix)
+ : QTextureGlyphCache(type, matrix) { }
+ virtual int glyphMargin() const;
+ virtual void createTextureData(int width, int height);
+ virtual void resizeTextureData(int width, int height);
+ virtual void fillTexture(const Coord &c, glyph_t glyph);
+
+ inline const QImage &image() const { return m_image; }
+
+private:
+ QImage m_image;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/gui/painting/qtransform.cpp b/src/gui/painting/qtransform.cpp
new file mode 100644
index 0000000000..da8c4281bd
--- /dev/null
+++ b/src/gui/painting/qtransform.cpp
@@ -0,0 +1,2079 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#include "qtransform.h"
+
+#include "qdatastream.h"
+#include "qdebug.h"
+#include "qmatrix.h"
+#include "qregion.h"
+#include "qpainterpath.h"
+#include "qvariant.h"
+#include <qmath.h>
+
+QT_BEGIN_NAMESPACE
+
+#define Q_NEAR_CLIP 0.000001
+
+
+#define MAP(x, y, nx, ny) \
+ do { \
+ qreal FX_ = x; \
+ qreal FY_ = y; \
+ switch(t) { \
+ case TxNone: \
+ nx = FX_; \
+ ny = FY_; \
+ break; \
+ case TxTranslate: \
+ nx = FX_ + affine._dx; \
+ ny = FY_ + affine._dy; \
+ break; \
+ case TxScale: \
+ nx = affine._m11 * FX_ + affine._dx; \
+ ny = affine._m22 * FY_ + affine._dy; \
+ break; \
+ case TxRotate: \
+ case TxShear: \
+ case TxProject: \
+ nx = affine._m11 * FX_ + affine._m21 * FY_ + affine._dx; \
+ ny = affine._m12 * FX_ + affine._m22 * FY_ + affine._dy; \
+ if (t == TxProject) { \
+ qreal w = 1./(m_13 * FX_ + m_23 * FY_ + m_33); \
+ nx *= w; \
+ ny *= w; \
+ } \
+ } \
+ } while (0)
+
+/*!
+ \class QTransform
+ \brief The QTransform class specifies 2D transformations of a coordinate system.
+ \since 4.3
+ \ingroup multimedia
+
+ A transformation specifies how to translate, scale, shear, rotate
+ or project the coordinate system, and is typically used when
+ rendering graphics.
+
+ QTransform differs from QMatrix in that it is a true 3x3 matrix,
+ allowing perspective transformations. QTransform's toAffine()
+ method allows casting QTransform to QMatrix. If a perspective
+ transformation has been specified on the matrix, then the
+ conversion to an affine QMatrix will cause loss of data.
+
+ QTransform is the recommended transformation class in Qt.
+
+ A QTransform object can be built using the setMatrix(), scale(),
+ rotate(), translate() and shear() functions. Alternatively, it
+ can be built by applying \l {QTransform#Basic Matrix
+ Operations}{basic matrix operations}. The matrix can also be
+ defined when constructed, and it can be reset to the identity
+ matrix (the default) using the reset() function.
+
+ The QTransform class supports mapping of graphic primitives: A given
+ point, line, polygon, region, or painter path can be mapped to the
+ coordinate system defined by \e this matrix using the map()
+ function. In case of a rectangle, its coordinates can be
+ transformed using the mapRect() function. A rectangle can also be
+ transformed into a \e polygon (mapped to the coordinate system
+ defined by \e this matrix), using the mapToPolygon() function.
+
+ QTransform provides the isIdentity() function which returns true if
+ the matrix is the identity matrix, and the isInvertible() function
+ which returns true if the matrix is non-singular (i.e. AB = BA =
+ I). The inverted() function returns an inverted copy of \e this
+ matrix if it is invertible (otherwise it returns the identity
+ matrix). In addition, QTransform provides the det() function
+ returning the matrix's determinant.
+
+ Finally, the QTransform class supports matrix multiplication, and
+ objects of the class can be streamed as well as compared.
+
+ \tableofcontents
+
+ \section1 Rendering Graphics
+
+ When rendering graphics, the matrix defines the transformations
+ but the actual transformation is performed by the drawing routines
+ in QPainter.
+
+ By default, QPainter operates on the associated device's own
+ coordinate system. The standard coordinate system of a
+ QPaintDevice has its origin located at the top-left position. The
+ \e x values increase to the right; \e y values increase
+ downward. For a complete description, see the \l {The Coordinate
+ System}{coordinate system} documentation.
+
+ QPainter has functions to translate, scale, shear and rotate the
+ coordinate system without using a QTransform. For example:
+
+ \table 100%
+ \row
+ \o \inlineimage qtransform-simpletransformation.png
+ \o
+ \snippet doc/src/snippets/transform/main.cpp 0
+ \endtable
+
+ Although these functions are very convenient, it can be more
+ efficient to build a QTransform and call QPainter::setTransform() if you
+ want to perform more than a single transform operation. For
+ example:
+
+ \table 100%
+ \row
+ \o \inlineimage qtransform-combinedtransformation.png
+ \o
+ \snippet doc/src/snippets/transform/main.cpp 1
+ \endtable
+
+ \section1 Basic Matrix Operations
+
+ \image qtransform-representation.png
+
+ A QTransform object contains a 3 x 3 matrix. The \c dx and \c dy
+ elements specify horizontal and vertical translation. The \c m11
+ and \c m22 elements specify horizontal and vertical scaling. The
+ \c m21 and \c m12 elements specify horizontal and vertical \e shearing.
+ And finally, the \c m13 and \c m23 elements specify horizontal and vertical
+ projection, with \c m33 as an additional projection factor.
+
+ QTransform transforms a point in the plane to another point using the
+ following formulas:
+
+ \snippet doc/src/snippets/code/src_gui_painting_qtransform.cpp 0
+
+ The point \e (x, y) is the original point, and \e (x', y') is the
+ transformed point. \e (x', y') can be transformed back to \e (x,
+ y) by performing the same operation on the inverted() matrix.
+
+ The various matrix elements can be set when constructing the
+ matrix, or by using the setMatrix() function later on. They can also
+ be manipulated using the translate(), rotate(), scale() and
+ shear() convenience functions, The currently set values can be
+ retrieved using the m11(), m12(), m13(), m21(), m22(), m23(),
+ m31(), m32(), m33(), dx() and dy() functions.
+
+ Translation is the simplest transformation. Setting \c dx and \c
+ dy will move the coordinate system \c dx units along the X axis
+ and \c dy units along the Y axis. Scaling can be done by setting
+ \c m11 and \c m22. For example, setting \c m11 to 2 and \c m22 to
+ 1.5 will double the height and increase the width by 50%. The
+ identity matrix has \c m11, \c m22, and \c m33 set to 1 (all others are set
+ to 0) mapping a point to itself. Shearing is controlled by \c m12
+ and \c m21. Setting these elements to values different from zero
+ will twist the coordinate system. Rotation is achieved by
+ carefully setting both the shearing factors and the scaling
+ factors. Perspective transformation is achieved by carefully setting
+ both the projection factors and the scaling factors.
+
+ Here's the combined transformations example using basic matrix
+ operations:
+
+ \table 100%
+ \row
+ \o \inlineimage qtransform-combinedtransformation2.png
+ \o
+ \snippet doc/src/snippets/transform/main.cpp 2
+ \endtable
+
+ \sa QPainter, {The Coordinate System}, {demos/affine}{Affine
+ Transformations Demo}, {Transformations Example}
+*/
+
+/*!
+ \enum QTransform::TransformationType
+
+ \value TxNone
+ \value TxTranslate
+ \value TxScale
+ \value TxRotate
+ \value TxShear
+ \value TxProject
+*/
+
+/*!
+ Constructs an identity matrix.
+
+ All elements are set to zero except \c m11 and \c m22 (specifying
+ the scale) and \c m13 which are set to 1.
+
+ \sa reset()
+*/
+QTransform::QTransform()
+ : m_13(0), m_23(0), m_33(1)
+ , m_type(TxNone)
+ , m_dirty(TxNone)
+{
+
+}
+
+/*!
+ Constructs a matrix with the elements, \a h11, \a h12, \a h13,
+ \a h21, \a h22, \a h23, \a h31, \a h32, \a h33.
+
+ \sa setMatrix()
+*/
+QTransform::QTransform(qreal h11, qreal h12, qreal h13,
+ qreal h21, qreal h22, qreal h23,
+ qreal h31, qreal h32, qreal h33)
+ : affine(h11, h12, h21, h22, h31, h32),
+ m_13(h13), m_23(h23), m_33(h33)
+ , m_type(TxNone)
+ , m_dirty(TxProject)
+{
+
+}
+
+/*!
+ Constructs a matrix with the elements, \a h11, \a h12, \a h21, \a
+ h22, \a dx and \a dy.
+
+ \sa setMatrix()
+*/
+QTransform::QTransform(qreal h11, qreal h12, qreal h21,
+ qreal h22, qreal dx, qreal dy)
+ : affine(h11, h12, h21, h22, dx, dy),
+ m_13(0), m_23(0), m_33(1)
+ , m_type(TxNone)
+ , m_dirty(TxShear)
+{
+
+}
+
+/*!
+ \fn QTransform::QTransform(const QMatrix &matrix)
+
+ Constructs a matrix that is a copy of the given \a matrix.
+ Note that the \c m13, \c m23, and \c m33 elements are set to 0, 0,
+ and 1 respectively.
+ */
+QTransform::QTransform(const QMatrix &mtx)
+ : affine(mtx),
+ m_13(0), m_23(0), m_33(1)
+ , m_type(TxNone)
+ , m_dirty(TxShear)
+{
+
+}
+
+/*!
+ Returns the adjoint of this matrix.
+*/
+QTransform QTransform::adjoint() const
+{
+ qreal h11, h12, h13,
+ h21, h22, h23,
+ h31, h32, h33;
+ h11 = affine._m22*m_33 - m_23*affine._dy;
+ h21 = m_23*affine._dx - affine._m21*m_33;
+ h31 = affine._m21*affine._dy - affine._m22*affine._dx;
+ h12 = m_13*affine._dy - affine._m12*m_33;
+ h22 = affine._m11*m_33 - m_13*affine._dx;
+ h32 = affine._m12*affine._dx - affine._m11*affine._dy;
+ h13 = affine._m12*m_23 - m_13*affine._m22;
+ h23 = m_13*affine._m21 - affine._m11*m_23;
+ h33 = affine._m11*affine._m22 - affine._m12*affine._m21;
+
+ return QTransform(h11, h12, h13,
+ h21, h22, h23,
+ h31, h32, h33);
+}
+
+/*!
+ Returns the transpose of this matrix.
+*/
+QTransform QTransform::transposed() const
+{
+ QTransform t(affine._m11, affine._m21, affine._dx,
+ affine._m12, affine._m22, affine._dy,
+ m_13, m_23, m_33);
+ t.m_type = m_type;
+ t.m_dirty = m_dirty;
+ return t;
+}
+
+/*!
+ Returns an inverted copy of this matrix.
+
+ If the matrix is singular (not invertible), the returned matrix is
+ the identity matrix. If \a invertible is valid (i.e. not 0), its
+ value is set to true if the matrix is invertible, otherwise it is
+ set to false.
+
+ \sa isInvertible()
+*/
+QTransform QTransform::inverted(bool *invertible) const
+{
+ QTransform invert;
+ bool inv = true;
+ qreal det;
+
+ switch(type()) {
+ case TxNone:
+ break;
+ case TxTranslate:
+ invert.affine._dx = -affine._dx;
+ invert.affine._dy = -affine._dy;
+ break;
+ case TxScale:
+ inv = !qFuzzyCompare(affine._m11 + 1, 1);
+ inv &= !qFuzzyCompare(affine._m22 + 1, 1);
+ if (inv) {
+ invert.affine._m11 = 1 / affine._m11;
+ invert.affine._m22 = 1 / affine._m22;
+ invert.affine._dx = -affine._dx * invert.affine._m11;
+ invert.affine._dy = -affine._dy * invert.affine._m22;
+ }
+ break;
+ case TxRotate:
+ case TxShear:
+ invert.affine = affine.inverted(&inv);
+ break;
+ default:
+ // general case
+ det = determinant();
+ inv = !qFuzzyCompare(det + 1, 1);
+ if (inv)
+ invert = adjoint() / det;
+ break;
+ }
+
+ if (invertible)
+ *invertible = inv;
+
+ if (inv) {
+ // inverting doesn't change the type
+ invert.m_type = m_type;
+ invert.m_dirty = m_dirty;
+ }
+
+ return invert;
+}
+
+/*!
+ Moves the coordinate system \a dx along the x axis and \a dy along
+ the y axis, and returns a reference to the matrix.
+
+ \sa setMatrix()
+*/
+QTransform & QTransform::translate(qreal dx, qreal dy)
+{
+ switch(type()) {
+ case TxNone:
+ affine._dx = dx;
+ affine._dy = dy;
+ break;
+ case TxTranslate:
+ affine._dx += dx;
+ affine._dy += dy;
+ break;
+ case TxScale:
+ affine._dx += dx*affine._m11;
+ affine._dy += dy*affine._m22;
+ break;
+ case TxProject:
+ m_33 += dx*m_13 + dy*m_23;
+ // Fall through
+ case TxShear:
+ case TxRotate:
+ affine._dx += dx*affine._m11 + dy*affine._m21;
+ affine._dy += dy*affine._m22 + dx*affine._m12;
+ break;
+ }
+ m_dirty |= TxTranslate;
+ return *this;
+}
+
+/*!
+ Creates a matrix which corresponds to a translation of \a dx along
+ the x axis and \a dy along the y axis. This is the same as
+ QTransform().translate(dx, dy) but slightly faster.
+
+ \since 4.5
+*/
+QTransform QTransform::fromTranslate(qreal dx, qreal dy)
+{
+ QTransform transform(1, 0, 0, 1, dx, dy);
+ transform.m_dirty = TxTranslate;
+ return transform;
+}
+
+/*!
+ Scales the coordinate system by \a sx horizontally and \a sy
+ vertically, and returns a reference to the matrix.
+
+ \sa setMatrix()
+*/
+QTransform & QTransform::scale(qreal sx, qreal sy)
+{
+ switch(type()) {
+ case TxNone:
+ case TxTranslate:
+ affine._m11 = sx;
+ affine._m22 = sy;
+ break;
+ case TxProject:
+ m_13 *= sx;
+ m_23 *= sy;
+ // fall through
+ case TxRotate:
+ case TxShear:
+ affine._m12 *= sx;
+ affine._m21 *= sy;
+ // fall through
+ case TxScale:
+ affine._m11 *= sx;
+ affine._m22 *= sy;
+ break;
+ }
+ m_dirty |= TxScale;
+ return *this;
+}
+
+/*!
+ Creates a matrix which corresponds to a scaling of
+ \a sx horizontally and \a sy vertically.
+ This is the same as QTransform().scale(sx, sy) but slightly faster.
+
+ \since 4.5
+*/
+QTransform QTransform::fromScale(qreal sx, qreal sy)
+{
+ QTransform transform(sx, 0, 0, sy, 0, 0);
+ transform.m_dirty = TxScale;
+ return transform;
+}
+
+/*!
+ Shears the coordinate system by \a sh horizontally and \a sv
+ vertically, and returns a reference to the matrix.
+
+ \sa setMatrix()
+*/
+QTransform & QTransform::shear(qreal sh, qreal sv)
+{
+ switch(type()) {
+ case TxNone:
+ case TxTranslate:
+ affine._m12 = sv;
+ affine._m21 = sh;
+ break;
+ case TxScale:
+ affine._m12 = sv*affine._m22;
+ affine._m21 = sh*affine._m11;
+ break;
+ case TxProject: {
+ qreal tm13 = sv*m_23;
+ qreal tm23 = sh*m_13;
+ m_13 += tm13;
+ m_23 += tm23;
+ }
+ // fall through
+ case TxRotate:
+ case TxShear: {
+ qreal tm11 = sv*affine._m21;
+ qreal tm22 = sh*affine._m12;
+ qreal tm12 = sv*affine._m22;
+ qreal tm21 = sh*affine._m11;
+ affine._m11 += tm11; affine._m12 += tm12;
+ affine._m21 += tm21; affine._m22 += tm22;
+ break;
+ }
+ }
+ m_dirty |= TxShear;
+ return *this;
+}
+
+const qreal deg2rad = qreal(0.017453292519943295769); // pi/180
+const qreal inv_dist_to_plane = 1. / 1024.;
+
+/*!
+ \fn QTransform &QTransform::rotate(qreal angle, Qt::Axis axis)
+
+ Rotates the coordinate system counterclockwise by the given \a angle
+ about the specified \a axis and returns a reference to the matrix.
+
+ Note that if you apply a QTransform to a point defined in widget
+ coordinates, the direction of the rotation will be clockwise
+ because the y-axis points downwards.
+
+ The angle is specified in degrees.
+
+ \sa setMatrix()
+*/
+QTransform & QTransform::rotate(qreal a, Qt::Axis axis)
+{
+ qreal sina = 0;
+ qreal cosa = 0;
+ if (a == 90. || a == -270.)
+ sina = 1.;
+ else if (a == 270. || a == -90.)
+ sina = -1.;
+ else if (a == 180.)
+ cosa = -1.;
+ else{
+ qreal b = deg2rad*a; // convert to radians
+ sina = qSin(b); // fast and convenient
+ cosa = qCos(b);
+ }
+
+ if (axis == Qt::ZAxis) {
+ switch(type()) {
+ case TxNone:
+ case TxTranslate:
+ affine._m11 = cosa;
+ affine._m12 = sina;
+ affine._m21 = -sina;
+ affine._m22 = cosa;
+ break;
+ case TxScale: {
+ qreal tm11 = cosa*affine._m11;
+ qreal tm12 = sina*affine._m22;
+ qreal tm21 = -sina*affine._m11;
+ qreal tm22 = cosa*affine._m22;
+ affine._m11 = tm11; affine._m12 = tm12;
+ affine._m21 = tm21; affine._m22 = tm22;
+ break;
+ }
+ case TxProject: {
+ qreal tm13 = cosa*m_13 + sina*m_23;
+ qreal tm23 = -sina*m_13 + cosa*m_23;
+ m_13 = tm13;
+ m_23 = tm23;
+ // fall through
+ }
+ case TxRotate:
+ case TxShear: {
+ qreal tm11 = cosa*affine._m11 + sina*affine._m21;
+ qreal tm12 = cosa*affine._m12 + sina*affine._m22;
+ qreal tm21 = -sina*affine._m11 + cosa*affine._m21;
+ qreal tm22 = -sina*affine._m12 + cosa*affine._m22;
+ affine._m11 = tm11; affine._m12 = tm12;
+ affine._m21 = tm21; affine._m22 = tm22;
+ break;
+ }
+ }
+ m_dirty |= TxRotate;
+ } else {
+ QTransform result;
+ if (axis == Qt::YAxis) {
+ result.affine._m11 = cosa;
+ result.m_13 = -sina * inv_dist_to_plane;
+ } else {
+ result.affine._m22 = cosa;
+ result.m_23 = -sina * inv_dist_to_plane;
+ }
+ result.m_type = TxProject;
+ *this = result * *this;
+ }
+
+ return *this;
+}
+
+/*!
+ \fn QTransform & QTransform::rotateRadians(qreal angle, Qt::Axis axis)
+
+ Rotates the coordinate system counterclockwise by the given \a angle
+ about the specified \a axis and returns a reference to the matrix.
+
+ Note that if you apply a QTransform to a point defined in widget
+ coordinates, the direction of the rotation will be clockwise
+ because the y-axis points downwards.
+
+ The angle is specified in radians.
+
+ \sa setMatrix()
+*/
+QTransform & QTransform::rotateRadians(qreal a, Qt::Axis axis)
+{
+ qreal sina = qSin(a);
+ qreal cosa = qCos(a);
+
+ if (axis == Qt::ZAxis) {
+ switch(type()) {
+ case TxNone:
+ case TxTranslate:
+ affine._m11 = cosa;
+ affine._m12 = sina;
+ affine._m21 = -sina;
+ affine._m22 = cosa;
+ break;
+ case TxScale: {
+ qreal tm11 = cosa*affine._m11;
+ qreal tm12 = sina*affine._m22;
+ qreal tm21 = -sina*affine._m11;
+ qreal tm22 = cosa*affine._m22;
+ affine._m11 = tm11; affine._m12 = tm12;
+ affine._m21 = tm21; affine._m22 = tm22;
+ break;
+ }
+ case TxProject: {
+ qreal tm13 = cosa*m_13 + sina*m_23;
+ qreal tm23 = -sina*m_13 + cosa*m_23;
+ m_13 = tm13;
+ m_23 = tm23;
+ // fall through
+ }
+ case TxRotate:
+ case TxShear: {
+ qreal tm11 = cosa*affine._m11 + sina*affine._m21;
+ qreal tm12 = cosa*affine._m12 + sina*affine._m22;
+ qreal tm21 = -sina*affine._m11 + cosa*affine._m21;
+ qreal tm22 = -sina*affine._m12 + cosa*affine._m22;
+ affine._m11 = tm11; affine._m12 = tm12;
+ affine._m21 = tm21; affine._m22 = tm22;
+ break;
+ }
+ }
+ m_dirty |= TxRotate;
+ } else {
+ QTransform result;
+ if (axis == Qt::YAxis) {
+ result.affine._m11 = cosa;
+ result.m_13 = -sina * inv_dist_to_plane;
+ } else {
+ result.affine._m22 = cosa;
+ result.m_23 = -sina * inv_dist_to_plane;
+ }
+ result.m_type = TxProject;
+ *this = result * *this;
+ }
+ return *this;
+}
+
+/*!
+ \fn bool QTransform::operator==(const QTransform &matrix) const
+ Returns true if this matrix is equal to the given \a matrix,
+ otherwise returns false.
+*/
+bool QTransform::operator==(const QTransform &o) const
+{
+#define qFZ qFuzzyCompare
+ return qFZ(affine._m11, o.affine._m11) && qFZ(affine._m12, o.affine._m12) && qFZ(m_13, o.m_13)
+ && qFZ(affine._m21, o.affine._m21) && qFZ(affine._m22, o.affine._m22) && qFZ(m_23, o.m_23)
+ && qFZ(affine._dx, o.affine._dx) && qFZ(affine._dy, o.affine._dy) && qFZ(m_33, o.m_33);
+#undef qFZ
+}
+
+/*!
+ \fn bool QTransform::operator!=(const QTransform &matrix) const
+ Returns true if this matrix is not equal to the given \a matrix,
+ otherwise returns false.
+*/
+bool QTransform::operator!=(const QTransform &o) const
+{
+ return !operator==(o);
+}
+
+/*!
+ \fn QTransform & QTransform::operator*=(const QTransform &matrix)
+ \overload
+
+ Returns the result of multiplying this matrix by the given \a
+ matrix.
+*/
+QTransform & QTransform::operator*=(const QTransform &o)
+{
+ TransformationType t = qMax(type(), o.type());
+ switch(t) {
+ case TxNone:
+ break;
+ case TxTranslate:
+ affine._dx += o.affine._dx;
+ affine._dy += o.affine._dy;
+ break;
+ case TxScale:
+ {
+ qreal m11 = affine._m11*o.affine._m11;
+ qreal m22 = affine._m22*o.affine._m22;
+
+ qreal m31 = affine._dx*o.affine._m11 + o.affine._dx;
+ qreal m32 = affine._dy*o.affine._m22 + o.affine._dy;
+
+ affine._m11 = m11;
+ affine._m22 = m22;
+ affine._dx = m31; affine._dy = m32;
+ break;
+ }
+ case TxRotate:
+ case TxShear:
+ {
+ qreal m11 = affine._m11*o.affine._m11 + affine._m12*o.affine._m21;
+ qreal m12 = affine._m11*o.affine._m12 + affine._m12*o.affine._m22;
+
+ qreal m21 = affine._m21*o.affine._m11 + affine._m22*o.affine._m21;
+ qreal m22 = affine._m21*o.affine._m12 + affine._m22*o.affine._m22;
+
+ qreal m31 = affine._dx*o.affine._m11 + affine._dy*o.affine._m21 + o.affine._dx;
+ qreal m32 = affine._dx*o.affine._m12 + affine._dy*o.affine._m22 + o.affine._dy;
+
+ affine._m11 = m11; affine._m12 = m12;
+ affine._m21 = m21; affine._m22 = m22;
+ affine._dx = m31; affine._dy = m32;
+ break;
+ }
+ case TxProject:
+ {
+ qreal m11 = affine._m11*o.affine._m11 + affine._m12*o.affine._m21 + m_13*o.affine._dx;
+ qreal m12 = affine._m11*o.affine._m12 + affine._m12*o.affine._m22 + m_13*o.affine._dy;
+ qreal m13 = affine._m11*o.m_13 + affine._m12*o.m_23 + m_13*o.m_33;
+
+ qreal m21 = affine._m21*o.affine._m11 + affine._m22*o.affine._m21 + m_23*o.affine._dx;
+ qreal m22 = affine._m21*o.affine._m12 + affine._m22*o.affine._m22 + m_23*o.affine._dy;
+ qreal m23 = affine._m21*o.m_13 + affine._m22*o.m_23 + m_23*o.m_33;
+
+ qreal m31 = affine._dx*o.affine._m11 + affine._dy*o.affine._m21 + m_33*o.affine._dx;
+ qreal m32 = affine._dx*o.affine._m12 + affine._dy*o.affine._m22 + m_33*o.affine._dy;
+ qreal m33 = affine._dx*o.m_13 + affine._dy*o.m_23 + m_33*o.m_33;
+
+ affine._m11 = m11; affine._m12 = m12; m_13 = m13;
+ affine._m21 = m21; affine._m22 = m22; m_23 = m23;
+ affine._dx = m31; affine._dy = m32; m_33 = m33;
+ }
+ }
+
+ m_dirty = t;
+ m_type = t;
+
+ return *this;
+}
+
+/*!
+ \fn QTransform QTransform::operator*(const QTransform &matrix) const
+ Returns the result of multiplying this matrix by the given \a
+ matrix.
+
+ Note that matrix multiplication is not commutative, i.e. a*b !=
+ b*a.
+*/
+QTransform QTransform::operator*(const QTransform &m) const
+{
+ QTransform result = *this;
+ result *= m;
+ return result;
+}
+
+/*!
+ \fn QTransform & QTransform::operator*=(qreal scalar)
+ \overload
+
+ Returns the result of performing an element-wise multiplication of this
+ matrix with the given \a scalar.
+*/
+
+/*!
+ \fn QTransform & QTransform::operator/=(qreal scalar)
+ \overload
+
+ Returns the result of performing an element-wise division of this
+ matrix by the given \a scalar.
+*/
+
+/*!
+ \fn QTransform & QTransform::operator+=(qreal scalar)
+ \overload
+
+ Returns the matrix obtained by adding the given \a scalar to each
+ element of this matrix.
+*/
+
+/*!
+ \fn QTransform & QTransform::operator-=(qreal scalar)
+ \overload
+
+ Returns the matrix obtained by subtracting the given \a scalar from each
+ element of this matrix.
+*/
+
+/*!
+ Assigns the given \a matrix's values to this matrix.
+*/
+QTransform & QTransform::operator=(const QTransform &matrix)
+{
+ affine._m11 = matrix.affine._m11;
+ affine._m12 = matrix.affine._m12;
+ affine._m21 = matrix.affine._m21;
+ affine._m22 = matrix.affine._m22;
+ affine._dx = matrix.affine._dx;
+ affine._dy = matrix.affine._dy;
+ m_13 = matrix.m_13;
+ m_23 = matrix.m_23;
+ m_33 = matrix.m_33;
+ m_type = matrix.m_type;
+ m_dirty = matrix.m_dirty;
+
+ return *this;
+}
+
+/*!
+ Resets the matrix to an identity matrix, i.e. all elements are set
+ to zero, except \c m11 and \c m22 (specifying the scale) which are
+ set to 1.
+
+ \sa QTransform(), isIdentity(), {QTransform#Basic Matrix
+ Operations}{Basic Matrix Operations}
+*/
+void QTransform::reset()
+{
+ affine._m11 = affine._m22 = m_33 = 1.0;
+ affine._m12 = m_13 = affine._m21 = m_23 = affine._dx = affine._dy = 0;
+ m_type = TxNone;
+ m_dirty = TxNone;
+}
+
+#ifndef QT_NO_DATASTREAM
+/*!
+ \fn QDataStream &operator<<(QDataStream &stream, const QTransform &matrix)
+ \since 4.3
+ \relates QTransform
+
+ Writes the given \a matrix to the given \a stream and returns a
+ reference to the stream.
+
+ \sa {Format of the QDataStream Operators}
+*/
+QDataStream & operator<<(QDataStream &s, const QTransform &m)
+{
+ s << double(m.m11())
+ << double(m.m12())
+ << double(m.m13())
+ << double(m.m21())
+ << double(m.m22())
+ << double(m.m23())
+ << double(m.m31())
+ << double(m.m32())
+ << double(m.m33());
+ return s;
+}
+
+/*!
+ \fn QDataStream &operator>>(QDataStream &stream, QTransform &matrix)
+ \since 4.3
+ \relates QTransform
+
+ Reads the given \a matrix from the given \a stream and returns a
+ reference to the stream.
+
+ \sa {Format of the QDataStream Operators}
+*/
+QDataStream & operator>>(QDataStream &s, QTransform &t)
+{
+ double m11, m12, m13,
+ m21, m22, m23,
+ m31, m32, m33;
+
+ s >> m11;
+ s >> m12;
+ s >> m13;
+ s >> m21;
+ s >> m22;
+ s >> m23;
+ s >> m31;
+ s >> m32;
+ s >> m33;
+ t.setMatrix(m11, m12, m13,
+ m21, m22, m23,
+ m31, m32, m33);
+ return s;
+}
+
+#endif // QT_NO_DATASTREAM
+
+#ifndef QT_NO_DEBUG_STREAM
+QDebug operator<<(QDebug dbg, const QTransform &m)
+{
+ dbg.nospace() << "QTransform("
+ << "11=" << m.m11()
+ << " 12=" << m.m12()
+ << " 13=" << m.m13()
+ << " 21=" << m.m21()
+ << " 22=" << m.m22()
+ << " 23=" << m.m23()
+ << " 31=" << m.m31()
+ << " 32=" << m.m32()
+ << " 33=" << m.m33()
+ << ")";
+ return dbg.space();
+}
+#endif
+
+/*!
+ \fn QPoint operator*(const QPoint &point, const QTransform &matrix)
+ \relates QTransform
+
+ This is the same as \a{matrix}.map(\a{point}).
+
+ \sa QTransform::map()
+*/
+QPoint QTransform::map(const QPoint &p) const
+{
+ qreal fx = p.x();
+ qreal fy = p.y();
+
+ qreal x = 0, y = 0;
+
+ TransformationType t = type();
+ switch(t) {
+ case TxNone:
+ x = fx;
+ y = fy;
+ break;
+ case TxTranslate:
+ x = fx + affine._dx;
+ y = fy + affine._dy;
+ break;
+ case TxScale:
+ x = affine._m11 * fx + affine._dx;
+ y = affine._m22 * fy + affine._dy;
+ break;
+ case TxRotate:
+ case TxShear:
+ case TxProject:
+ x = affine._m11 * fx + affine._m21 * fy + affine._dx;
+ y = affine._m12 * fx + affine._m22 * fy + affine._dy;
+ if (t == TxProject) {
+ qreal w = 1./(m_13 * fx + m_23 * fy + m_33);
+ x *= w;
+ y *= w;
+ }
+ }
+ return QPoint(qRound(x), qRound(y));
+}
+
+
+/*!
+ \fn QPointF operator*(const QPointF &point, const QTransform &matrix)
+ \relates QTransform
+
+ Same as \a{matrix}.map(\a{point}).
+
+ \sa QTransform::map()
+*/
+
+/*!
+ \overload
+
+ Creates and returns a QPointF object that is a copy of the given point,
+ \a p, mapped into the coordinate system defined by this matrix.
+*/
+QPointF QTransform::map(const QPointF &p) const
+{
+ qreal fx = p.x();
+ qreal fy = p.y();
+
+ qreal x = 0, y = 0;
+
+ TransformationType t = type();
+ switch(t) {
+ case TxNone:
+ x = fx;
+ y = fy;
+ break;
+ case TxTranslate:
+ x = fx + affine._dx;
+ y = fy + affine._dy;
+ break;
+ case TxScale:
+ x = affine._m11 * fx + affine._dx;
+ y = affine._m22 * fy + affine._dy;
+ break;
+ case TxRotate:
+ case TxShear:
+ case TxProject:
+ x = affine._m11 * fx + affine._m21 * fy + affine._dx;
+ y = affine._m12 * fx + affine._m22 * fy + affine._dy;
+ if (t == TxProject) {
+ qreal w = 1./(m_13 * fx + m_23 * fy + m_33);
+ x *= w;
+ y *= w;
+ }
+ }
+ return QPointF(x, y);
+}
+
+/*!
+ \fn QPoint QTransform::map(const QPoint &point) const
+ \overload
+
+ Creates and returns a QPoint object that is a copy of the given \a
+ point, mapped into the coordinate system defined by this
+ matrix. Note that the transformed coordinates are rounded to the
+ nearest integer.
+*/
+
+/*!
+ \fn QLineF operator*(const QLineF &line, const QTransform &matrix)
+ \relates QTransform
+
+ This is the same as \a{matrix}.map(\a{line}).
+
+ \sa QTransform::map()
+*/
+
+/*!
+ \fn QLine operator*(const QLine &line, const QTransform &matrix)
+ \relates QTransform
+
+ This is the same as \a{matrix}.map(\a{line}).
+
+ \sa QTransform::map()
+*/
+
+/*!
+ \overload
+
+ Creates and returns a QLineF object that is a copy of the given line,
+ \a l, mapped into the coordinate system defined by this matrix.
+*/
+QLine QTransform::map(const QLine &l) const
+{
+ qreal fx1 = l.x1();
+ qreal fy1 = l.y1();
+ qreal fx2 = l.x2();
+ qreal fy2 = l.y2();
+
+ qreal x1 = 0, y1 = 0, x2 = 0, y2 = 0;
+
+ TransformationType t = type();
+ switch(t) {
+ case TxNone:
+ x1 = fx1;
+ y1 = fy1;
+ x2 = fx2;
+ y2 = fy2;
+ break;
+ case TxTranslate:
+ x1 = fx1 + affine._dx;
+ y1 = fy1 + affine._dy;
+ x2 = fx2 + affine._dx;
+ y2 = fy2 + affine._dy;
+ break;
+ case TxScale:
+ x1 = affine._m11 * fx1 + affine._dx;
+ y1 = affine._m22 * fy1 + affine._dy;
+ x2 = affine._m11 * fx2 + affine._dx;
+ y2 = affine._m22 * fy2 + affine._dy;
+ break;
+ case TxRotate:
+ case TxShear:
+ case TxProject:
+ x1 = affine._m11 * fx1 + affine._m21 * fy1 + affine._dx;
+ y1 = affine._m12 * fx1 + affine._m22 * fy1 + affine._dy;
+ x2 = affine._m11 * fx2 + affine._m21 * fy2 + affine._dx;
+ y2 = affine._m12 * fx2 + affine._m22 * fy2 + affine._dy;
+ if (t == TxProject) {
+ qreal w = 1./(m_13 * fx1 + m_23 * fy1 + m_33);
+ x1 *= w;
+ y1 *= w;
+ w = 1./(m_13 * fx2 + m_23 * fy2 + m_33);
+ x2 *= w;
+ y2 *= w;
+ }
+ }
+ return QLine(qRound(x1), qRound(y1), qRound(x2), qRound(y2));
+}
+
+/*!
+ \overload
+
+ \fn QLineF QTransform::map(const QLineF &line) const
+
+ Creates and returns a QLine object that is a copy of the given \a
+ line, mapped into the coordinate system defined by this matrix.
+ Note that the transformed coordinates are rounded to the nearest
+ integer.
+*/
+
+QLineF QTransform::map(const QLineF &l) const
+{
+ qreal fx1 = l.x1();
+ qreal fy1 = l.y1();
+ qreal fx2 = l.x2();
+ qreal fy2 = l.y2();
+
+ qreal x1 = 0, y1 = 0, x2 = 0, y2 = 0;
+
+ TransformationType t = type();
+ switch(t) {
+ case TxNone:
+ x1 = fx1;
+ y1 = fy1;
+ x2 = fx2;
+ y2 = fy2;
+ break;
+ case TxTranslate:
+ x1 = fx1 + affine._dx;
+ y1 = fy1 + affine._dy;
+ x2 = fx2 + affine._dx;
+ y2 = fy2 + affine._dy;
+ break;
+ case TxScale:
+ x1 = affine._m11 * fx1 + affine._dx;
+ y1 = affine._m22 * fy1 + affine._dy;
+ x2 = affine._m11 * fx2 + affine._dx;
+ y2 = affine._m22 * fy2 + affine._dy;
+ break;
+ case TxRotate:
+ case TxShear:
+ case TxProject:
+ x1 = affine._m11 * fx1 + affine._m21 * fy1 + affine._dx;
+ y1 = affine._m12 * fx1 + affine._m22 * fy1 + affine._dy;
+ x2 = affine._m11 * fx2 + affine._m21 * fy2 + affine._dx;
+ y2 = affine._m12 * fx2 + affine._m22 * fy2 + affine._dy;
+ if (t == TxProject) {
+ qreal w = 1./(m_13 * fx1 + m_23 * fy1 + m_33);
+ x1 *= w;
+ y1 *= w;
+ w = 1./(m_13 * fx2 + m_23 * fy2 + m_33);
+ x2 *= w;
+ y2 *= w;
+ }
+ }
+ return QLineF(x1, y1, x2, y2);
+}
+
+static QPolygonF mapProjective(const QTransform &transform, const QPolygonF &poly)
+{
+ if (poly.size() == 0)
+ return poly;
+
+ if (poly.size() == 1)
+ return QPolygonF() << transform.map(poly.at(0));
+
+ QPainterPath path;
+ path.addPolygon(poly);
+
+ path = transform.map(path);
+
+ QPolygonF result;
+ for (int i = 0; i < path.elementCount(); ++i)
+ result << path.elementAt(i);
+ return result;
+}
+
+
+/*!
+ \fn QPolygonF operator *(const QPolygonF &polygon, const QTransform &matrix)
+ \since 4.3
+ \relates QTransform
+
+ This is the same as \a{matrix}.map(\a{polygon}).
+
+ \sa QTransform::map()
+*/
+
+/*!
+ \fn QPolygon operator*(const QPolygon &polygon, const QTransform &matrix)
+ \relates QTransform
+
+ This is the same as \a{matrix}.map(\a{polygon}).
+
+ \sa QTransform::map()
+*/
+
+/*!
+ \fn QPolygonF QTransform::map(const QPolygonF &polygon) const
+ \overload
+
+ Creates and returns a QPolygonF object that is a copy of the given
+ \a polygon, mapped into the coordinate system defined by this
+ matrix.
+*/
+QPolygonF QTransform::map(const QPolygonF &a) const
+{
+ if (type() >= QTransform::TxProject)
+ return mapProjective(*this, a);
+
+ int size = a.size();
+ int i;
+ QPolygonF p(size);
+ const QPointF *da = a.constData();
+ QPointF *dp = p.data();
+
+ TransformationType t = type();
+ for(i = 0; i < size; ++i) {
+ MAP(da[i].xp, da[i].yp, dp[i].xp, dp[i].yp);
+ }
+ return p;
+}
+
+/*!
+ \fn QPolygon QTransform::map(const QPolygon &polygon) const
+ \overload
+
+ Creates and returns a QPolygon object that is a copy of the given
+ \a polygon, mapped into the coordinate system defined by this
+ matrix. Note that the transformed coordinates are rounded to the
+ nearest integer.
+*/
+QPolygon QTransform::map(const QPolygon &a) const
+{
+ if (type() >= QTransform::TxProject)
+ return mapProjective(*this, QPolygonF(a)).toPolygon();
+
+ int size = a.size();
+ int i;
+ QPolygon p(size);
+ const QPoint *da = a.constData();
+ QPoint *dp = p.data();
+
+ TransformationType t = type();
+ for(i = 0; i < size; ++i) {
+ qreal nx = 0, ny = 0;
+ MAP(da[i].xp, da[i].yp, nx, ny);
+ dp[i].xp = qRound(nx);
+ dp[i].yp = qRound(ny);
+ }
+ return p;
+}
+
+/*!
+ \fn QRegion operator*(const QRegion &region, const QTransform &matrix)
+ \relates QTransform
+
+ This is the same as \a{matrix}.map(\a{region}).
+
+ \sa QTransform::map()
+*/
+
+extern QPainterPath qt_regionToPath(const QRegion &region);
+
+/*!
+ \fn QRegion QTransform::map(const QRegion &region) const
+ \overload
+
+ Creates and returns a QRegion object that is a copy of the given
+ \a region, mapped into the coordinate system defined by this matrix.
+
+ Calling this method can be rather expensive if rotations or
+ shearing are used.
+*/
+QRegion QTransform::map(const QRegion &r) const
+{
+ TransformationType t = type();
+ if (t == TxNone)
+ return r;
+ if (t == TxTranslate) {
+ QRegion copy(r);
+ copy.translate(qRound(affine._dx), qRound(affine._dy));
+ return copy;
+ }
+
+ QPainterPath p = map(qt_regionToPath(r));
+ return p.toFillPolygon(QTransform()).toPolygon();
+}
+
+struct QHomogeneousCoordinate
+{
+ qreal x;
+ qreal y;
+ qreal w;
+
+ QHomogeneousCoordinate() {}
+ QHomogeneousCoordinate(qreal x_, qreal y_, qreal w_) : x(x_), y(y_), w(w_) {}
+
+ const QPointF toPoint() const {
+ qreal iw = 1 / w;
+ return QPointF(x * iw, y * iw);
+ }
+};
+
+static inline QHomogeneousCoordinate mapHomogeneous(const QTransform &transform, const QPointF &p)
+{
+ QHomogeneousCoordinate c;
+ c.x = transform.m11() * p.x() + transform.m21() * p.y() + transform.m31();
+ c.y = transform.m12() * p.x() + transform.m22() * p.y() + transform.m32();
+ c.w = transform.m13() * p.x() + transform.m23() * p.y() + transform.m33();
+ return c;
+}
+
+static inline bool lineTo_clipped(QPainterPath &path, const QTransform &transform, const QPointF &a, const QPointF &b, bool needsMoveTo)
+{
+ QHomogeneousCoordinate ha = mapHomogeneous(transform, a);
+ QHomogeneousCoordinate hb = mapHomogeneous(transform, b);
+
+ if (ha.w < Q_NEAR_CLIP && hb.w < Q_NEAR_CLIP)
+ return false;
+
+ if (hb.w < Q_NEAR_CLIP) {
+ const qreal t = (Q_NEAR_CLIP - hb.w) / (ha.w - hb.w);
+
+ hb.x += (ha.x - hb.x) * t;
+ hb.y += (ha.y - hb.y) * t;
+ hb.w = qreal(Q_NEAR_CLIP);
+ } else if (ha.w < Q_NEAR_CLIP) {
+ const qreal t = (Q_NEAR_CLIP - ha.w) / (hb.w - ha.w);
+
+ ha.x += (hb.x - ha.x) * t;
+ ha.y += (hb.y - ha.y) * t;
+ ha.w = qreal(Q_NEAR_CLIP);
+
+ const QPointF p = ha.toPoint();
+ if (needsMoveTo) {
+ path.moveTo(p);
+ needsMoveTo = false;
+ } else {
+ path.lineTo(p);
+ }
+ }
+
+ if (needsMoveTo)
+ path.moveTo(ha.toPoint());
+
+ path.lineTo(hb.toPoint());
+
+ return true;
+}
+
+static inline bool cubicTo_clipped(QPainterPath &path, const QTransform &transform, const QPointF &a, const QPointF &b, const QPointF &c, const QPointF &d, bool needsMoveTo)
+{
+ const QHomogeneousCoordinate ha = mapHomogeneous(transform, a);
+ const QHomogeneousCoordinate hb = mapHomogeneous(transform, b);
+ const QHomogeneousCoordinate hc = mapHomogeneous(transform, c);
+ const QHomogeneousCoordinate hd = mapHomogeneous(transform, d);
+
+ if (ha.w < Q_NEAR_CLIP && hb.w < Q_NEAR_CLIP && hc.w < Q_NEAR_CLIP && hd.w < Q_NEAR_CLIP)
+ return false;
+
+ if (ha.w >= Q_NEAR_CLIP && hb.w >= Q_NEAR_CLIP && hc.w >= Q_NEAR_CLIP && hd.w >= Q_NEAR_CLIP) {
+ if (needsMoveTo)
+ path.moveTo(ha.toPoint());
+
+ path.cubicTo(hb.toPoint(), hc.toPoint(), hd.toPoint());
+ return true;
+ }
+
+ if (lineTo_clipped(path, transform, a, b, needsMoveTo))
+ needsMoveTo = false;
+ if (lineTo_clipped(path, transform, b, c, needsMoveTo))
+ needsMoveTo = false;
+ if (lineTo_clipped(path, transform, c, d, needsMoveTo))
+ needsMoveTo = false;
+
+ return !needsMoveTo;
+}
+
+static QPainterPath mapProjective(const QTransform &transform, const QPainterPath &path)
+{
+ QPainterPath result;
+
+ QPointF last;
+ QPointF lastMoveTo;
+ bool needsMoveTo = true;
+ for (int i = 0; i < path.elementCount(); ++i) {
+ switch (path.elementAt(i).type) {
+ case QPainterPath::MoveToElement:
+ if (i > 0 && lastMoveTo != last)
+ lineTo_clipped(result, transform, last, lastMoveTo, needsMoveTo);
+
+ lastMoveTo = path.elementAt(i);
+ last = path.elementAt(i);
+ needsMoveTo = true;
+ break;
+ case QPainterPath::LineToElement:
+ if (lineTo_clipped(result, transform, last, path.elementAt(i), needsMoveTo))
+ needsMoveTo = false;
+ last = path.elementAt(i);
+ break;
+ case QPainterPath::CurveToElement:
+ if (cubicTo_clipped(result, transform, last, path.elementAt(i), path.elementAt(i+1), path.elementAt(i+2), needsMoveTo))
+ needsMoveTo = false;
+ i += 2;
+ last = path.elementAt(i);
+ break;
+ default:
+ Q_ASSERT(false);
+ }
+ }
+
+ if (path.elementCount() > 0 && lastMoveTo != last)
+ lineTo_clipped(result, transform, last, lastMoveTo, needsMoveTo);
+
+ return result;
+}
+
+/*!
+ \fn QPainterPath operator *(const QPainterPath &path, const QTransform &matrix)
+ \since 4.3
+ \relates QTransform
+
+ This is the same as \a{matrix}.map(\a{path}).
+
+ \sa QTransform::map()
+*/
+
+/*!
+ \overload
+
+ Creates and returns a QPainterPath object that is a copy of the
+ given \a path, mapped into the coordinate system defined by this
+ matrix.
+*/
+QPainterPath QTransform::map(const QPainterPath &path) const
+{
+ TransformationType t = type();
+ if (t == TxNone || path.isEmpty())
+ return path;
+
+ if (t >= TxProject)
+ return mapProjective(*this, path);
+
+ QPainterPath copy = path;
+ copy.detach();
+
+ if (t == TxTranslate) {
+ for (int i=0; i<path.elementCount(); ++i) {
+ QPainterPath::Element &e = copy.d_ptr->elements[i];
+ e.x += affine._dx;
+ e.y += affine._dy;
+ }
+ } else {
+ // Full xform
+ for (int i=0; i<path.elementCount(); ++i) {
+ QPainterPath::Element &e = copy.d_ptr->elements[i];
+ MAP(e.x, e.y, e.x, e.y);
+ }
+ }
+
+ return copy;
+}
+
+/*!
+ \fn QPolygon QTransform::mapToPolygon(const QRect &rectangle) const
+
+ Creates and returns a QPolygon representation of the given \a
+ rectangle, mapped into the coordinate system defined by this
+ matrix.
+
+ The rectangle's coordinates are transformed using the following
+ formulas:
+
+ \snippet doc/src/snippets/code/src_gui_painting_qtransform.cpp 1
+
+ Polygons and rectangles behave slightly differently when
+ transformed (due to integer rounding), so
+ \c{matrix.map(QPolygon(rectangle))} is not always the same as
+ \c{matrix.mapToPolygon(rectangle)}.
+
+ \sa mapRect(), {QTransform#Basic Matrix Operations}{Basic Matrix
+ Operations}
+*/
+QPolygon QTransform::mapToPolygon(const QRect &rect) const
+{
+ TransformationType t = type();
+
+ QPolygon a(4);
+ qreal x[4] = { 0, 0, 0, 0 }, y[4] = { 0, 0, 0, 0 };
+ if (t <= TxScale) {
+ x[0] = affine._m11*rect.x() + affine._dx;
+ y[0] = affine._m22*rect.y() + affine._dy;
+ qreal w = affine._m11*rect.width();
+ qreal h = affine._m22*rect.height();
+ if (w < 0) {
+ w = -w;
+ x[0] -= w;
+ }
+ if (h < 0) {
+ h = -h;
+ y[0] -= h;
+ }
+ x[1] = x[0]+w;
+ x[2] = x[1];
+ x[3] = x[0];
+ y[1] = y[0];
+ y[2] = y[0]+h;
+ y[3] = y[2];
+ } else {
+ qreal right = rect.x() + rect.width();
+ qreal bottom = rect.y() + rect.height();
+ MAP(rect.x(), rect.y(), x[0], y[0]);
+ MAP(right, rect.y(), x[1], y[1]);
+ MAP(right, bottom, x[2], y[2]);
+ MAP(rect.x(), bottom, x[3], y[3]);
+ }
+
+ // all coordinates are correctly, tranform to a pointarray
+ // (rounding to the next integer)
+ a.setPoints(4, qRound(x[0]), qRound(y[0]),
+ qRound(x[1]), qRound(y[1]),
+ qRound(x[2]), qRound(y[2]),
+ qRound(x[3]), qRound(y[3]));
+ return a;
+}
+
+/*!
+ Creates a transformation matrix, \a trans, that maps a unit square
+ to a four-sided polygon, \a quad. Returns true if the transformation
+ is constructed or false if such a transformation does not exist.
+
+ \sa quadToSquare(), quadToQuad()
+*/
+bool QTransform::squareToQuad(const QPolygonF &quad, QTransform &trans)
+{
+ if (quad.count() != 4)
+ return false;
+
+ qreal dx0 = quad[0].x();
+ qreal dx1 = quad[1].x();
+ qreal dx2 = quad[2].x();
+ qreal dx3 = quad[3].x();
+
+ qreal dy0 = quad[0].y();
+ qreal dy1 = quad[1].y();
+ qreal dy2 = quad[2].y();
+ qreal dy3 = quad[3].y();
+
+ double ax = dx0 - dx1 + dx2 - dx3;
+ double ay = dy0 - dy1 + dy2 - dy3;
+
+ if (!ax && !ay) { //afine transform
+ trans.setMatrix(dx1 - dx0, dy1 - dy0, 0,
+ dx2 - dx1, dy2 - dy1, 0,
+ dx0, dy0, 1);
+ } else {
+ double ax1 = dx1 - dx2;
+ double ax2 = dx3 - dx2;
+ double ay1 = dy1 - dy2;
+ double ay2 = dy3 - dy2;
+
+ /*determinants */
+ double gtop = ax * ay2 - ax2 * ay;
+ double htop = ax1 * ay - ax * ay1;
+ double bottom = ax1 * ay2 - ax2 * ay1;
+
+ double a, b, c, d, e, f, g, h; /*i is always 1*/
+
+ if (!bottom)
+ return false;
+
+ g = gtop/bottom;
+ h = htop/bottom;
+
+ a = dx1 - dx0 + g * dx1;
+ b = dx3 - dx0 + h * dx3;
+ c = dx0;
+ d = dy1 - dy0 + g * dy1;
+ e = dy3 - dy0 + h * dy3;
+ f = dy0;
+
+ trans.setMatrix(a, d, g,
+ b, e, h,
+ c, f, 1.0);
+ }
+
+ return true;
+}
+
+/*!
+ \fn bool QTransform::quadToSquare(const QPolygonF &quad, QTransform &trans)
+
+ Creates a transformation matrix, \a trans, that maps a four-sided polygon,
+ \a quad, to a unit square. Returns true if the transformation is constructed
+ or false if such a transformation does not exist.
+
+ \sa squareToQuad(), quadToQuad()
+*/
+bool QTransform::quadToSquare(const QPolygonF &quad, QTransform &trans)
+{
+ if (!squareToQuad(quad, trans))
+ return false;
+
+ bool invertible = false;
+ trans = trans.inverted(&invertible);
+
+ return invertible;
+}
+
+/*!
+ Creates a transformation matrix, \a trans, that maps a four-sided
+ polygon, \a one, to another four-sided polygon, \a two.
+ Returns true if the transformation is possible; otherwise returns
+ false.
+
+ This is a convenience method combining quadToSquare() and
+ squareToQuad() methods. It allows the input quad to be
+ transformed into any other quad.
+
+ \sa squareToQuad(), quadToSquare()
+*/
+bool QTransform::quadToQuad(const QPolygonF &one,
+ const QPolygonF &two,
+ QTransform &trans)
+{
+ QTransform stq;
+ if (!quadToSquare(one, trans))
+ return false;
+ if (!squareToQuad(two, stq))
+ return false;
+ trans *= stq;
+ //qDebug()<<"Final = "<<trans;
+ return true;
+}
+
+/*!
+ Sets the matrix elements to the specified values, \a m11,
+ \a m12, \a m13 \a m21, \a m22, \a m23 \a m31, \a m32 and
+ \a m33. Note that this function replaces the previous values.
+ QMatrix provides the translate(), rotate(), scale() and shear()
+ convenience functions to manipulate the various matrix elements
+ based on the currently defined coordinate system.
+
+ \sa QTransform()
+*/
+
+void QTransform::setMatrix(qreal m11, qreal m12, qreal m13,
+ qreal m21, qreal m22, qreal m23,
+ qreal m31, qreal m32, qreal m33)
+{
+ affine._m11 = m11; affine._m12 = m12; m_13 = m13;
+ affine._m21 = m21; affine._m22 = m22; m_23 = m23;
+ affine._dx = m31; affine._dy = m32; m_33 = m33;
+ m_type = TxNone;
+ m_dirty = TxProject;
+}
+
+QRect QTransform::mapRect(const QRect &rect) const
+{
+ TransformationType t = type();
+ if (t <= TxScale) {
+ int x = qRound(affine._m11*rect.x() + affine._dx);
+ int y = qRound(affine._m22*rect.y() + affine._dy);
+ int w = qRound(affine._m11*rect.width());
+ int h = qRound(affine._m22*rect.height());
+ if (w < 0) {
+ w = -w;
+ x -= w;
+ }
+ if (h < 0) {
+ h = -h;
+ y -= h;
+ }
+ return QRect(x, y, w, h);
+ } else if (t < TxProject) {
+ // see mapToPolygon for explanations of the algorithm.
+ qreal x0 = 0, y0 = 0;
+ qreal x, y;
+ MAP(rect.left(), rect.top(), x0, y0);
+ qreal xmin = x0;
+ qreal ymin = y0;
+ qreal xmax = x0;
+ qreal ymax = y0;
+ MAP(rect.right() + 1, rect.top(), x, y);
+ xmin = qMin(xmin, x);
+ ymin = qMin(ymin, y);
+ xmax = qMax(xmax, x);
+ ymax = qMax(ymax, y);
+ MAP(rect.right() + 1, rect.bottom() + 1, x, y);
+ xmin = qMin(xmin, x);
+ ymin = qMin(ymin, y);
+ xmax = qMax(xmax, x);
+ ymax = qMax(ymax, y);
+ MAP(rect.left(), rect.bottom() + 1, x, y);
+ xmin = qMin(xmin, x);
+ ymin = qMin(ymin, y);
+ xmax = qMax(xmax, x);
+ ymax = qMax(ymax, y);
+ return QRect(qRound(xmin), qRound(ymin), qRound(xmax)-qRound(xmin), qRound(ymax)-qRound(ymin));
+ } else {
+ QPainterPath path;
+ path.addRect(rect);
+ return map(path).boundingRect().toRect();
+ }
+}
+
+/*!
+ \fn QRectF QTransform::mapRect(const QRectF &rectangle) const
+
+ Creates and returns a QRectF object that is a copy of the given \a
+ rectangle, mapped into the coordinate system defined by this
+ matrix.
+
+ The rectangle's coordinates are transformed using the following
+ formulas:
+
+ \snippet doc/src/snippets/code/src_gui_painting_qtransform.cpp 2
+
+ If rotation or shearing has been specified, this function returns
+ the \e bounding rectangle. To retrieve the exact region the given
+ \a rectangle maps to, use the mapToPolygon() function instead.
+
+ \sa mapToPolygon(), {QTransform#Basic Matrix Operations}{Basic Matrix
+ Operations}
+*/
+QRectF QTransform::mapRect(const QRectF &rect) const
+{
+ TransformationType t = type();
+ if (t <= TxScale) {
+ qreal x = affine._m11*rect.x() + affine._dx;
+ qreal y = affine._m22*rect.y() + affine._dy;
+ qreal w = affine._m11*rect.width();
+ qreal h = affine._m22*rect.height();
+ if (w < 0) {
+ w = -w;
+ x -= w;
+ }
+ if (h < 0) {
+ h = -h;
+ y -= h;
+ }
+ return QRectF(x, y, w, h);
+ } else if (t < TxProject) {
+ qreal x0 = 0, y0 = 0;
+ qreal x, y;
+ MAP(rect.x(), rect.y(), x0, y0);
+ qreal xmin = x0;
+ qreal ymin = y0;
+ qreal xmax = x0;
+ qreal ymax = y0;
+ MAP(rect.x() + rect.width(), rect.y(), x, y);
+ xmin = qMin(xmin, x);
+ ymin = qMin(ymin, y);
+ xmax = qMax(xmax, x);
+ ymax = qMax(ymax, y);
+ MAP(rect.x() + rect.width(), rect.y() + rect.height(), x, y);
+ xmin = qMin(xmin, x);
+ ymin = qMin(ymin, y);
+ xmax = qMax(xmax, x);
+ ymax = qMax(ymax, y);
+ MAP(rect.x(), rect.y() + rect.height(), x, y);
+ xmin = qMin(xmin, x);
+ ymin = qMin(ymin, y);
+ xmax = qMax(xmax, x);
+ ymax = qMax(ymax, y);
+ return QRectF(xmin, ymin, xmax-xmin, ymax - ymin);
+ } else {
+ QPainterPath path;
+ path.addRect(rect);
+ return map(path).boundingRect();
+ }
+}
+
+/*!
+ \fn QRect QTransform::mapRect(const QRect &rectangle) const
+ \overload
+
+ Creates and returns a QRect object that is a copy of the given \a
+ rectangle, mapped into the coordinate system defined by this
+ matrix. Note that the transformed coordinates are rounded to the
+ nearest integer.
+*/
+
+/*!
+ Maps the given coordinates \a x and \a y into the coordinate
+ system defined by this matrix. The resulting values are put in *\a
+ tx and *\a ty, respectively.
+
+ The coordinates are transformed using the following formulas:
+
+ \snippet doc/src/snippets/code/src_gui_painting_qtransform.cpp 3
+
+ The point (x, y) is the original point, and (x', y') is the
+ transformed point.
+
+ \sa {QTransform#Basic Matrix Operations}{Basic Matrix Operations}
+*/
+void QTransform::map(qreal x, qreal y, qreal *tx, qreal *ty) const
+{
+ TransformationType t = type();
+ MAP(x, y, *tx, *ty);
+}
+
+/*!
+ \overload
+
+ Maps the given coordinates \a x and \a y into the coordinate
+ system defined by this matrix. The resulting values are put in *\a
+ tx and *\a ty, respectively. Note that the transformed coordinates
+ are rounded to the nearest integer.
+*/
+void QTransform::map(int x, int y, int *tx, int *ty) const
+{
+ TransformationType t = type();
+ qreal fx = 0, fy = 0;
+ MAP(x, y, fx, fy);
+ *tx = qRound(fx);
+ *ty = qRound(fy);
+}
+
+/*!
+ Returns the QTransform cast to a QMatrix.
+ */
+const QMatrix &QTransform::toAffine() const
+{
+ return affine;
+}
+
+/*!
+ Returns the transformation type of this matrix.
+
+ The transformation type is the highest enumeration value
+ capturing all of the matrix's transformations. For example,
+ if the matrix both scales and shears, the type would be \c TxShear,
+ because \c TxShear has a higher enumeration value than \c TxScale.
+
+ Knowing the transformation type of a matrix is useful for optimization:
+ you can often handle specific types more optimally than handling
+ the generic case.
+ */
+QTransform::TransformationType QTransform::type() const
+{
+ if (m_dirty >= m_type) {
+ if (m_dirty > TxShear && (!qFuzzyCompare(m_13 + 1, 1) || !qFuzzyCompare(m_23 + 1, 1)))
+ m_type = TxProject;
+ else if (m_dirty > TxScale && (!qFuzzyCompare(affine._m12 + 1, 1) || !qFuzzyCompare(affine._m21 + 1, 1))) {
+ const qreal dot = affine._m11 * affine._m12 + affine._m21 * affine._m22;
+ if (qFuzzyCompare(dot + 1, 1))
+ m_type = TxRotate;
+ else
+ m_type = TxShear;
+ } else if (m_dirty > TxTranslate && (!qFuzzyCompare(affine._m11, 1) || !qFuzzyCompare(affine._m22, 1) || !qFuzzyCompare(m_33, 1)))
+ m_type = TxScale;
+ else if (m_dirty > TxNone && (!qFuzzyCompare(affine._dx + 1, 1) || !qFuzzyCompare(affine._dy + 1, 1)))
+ m_type = TxTranslate;
+ else
+ m_type = TxNone;
+
+ m_dirty = TxNone;
+ }
+
+ return static_cast<TransformationType>(m_type);
+}
+
+/*!
+
+ Returns the transform as a QVariant.
+*/
+QTransform::operator QVariant() const
+{
+ return QVariant(QVariant::Transform, this);
+}
+
+
+/*!
+ \fn bool QTransform::isInvertible() const
+
+ Returns true if the matrix is invertible, otherwise returns false.
+
+ \sa inverted()
+*/
+
+/*!
+ \fn qreal QTransform::det() const
+
+ Returns the matrix's determinant.
+*/
+
+
+/*!
+ \fn qreal QTransform::m11() const
+
+ Returns the horizontal scaling factor.
+
+ \sa scale(), {QTransform#Basic Matrix Operations}{Basic Matrix
+ Operations}
+*/
+
+/*!
+ \fn qreal QTransform::m12() const
+
+ Returns the vertical shearing factor.
+
+ \sa shear(), {QTransform#Basic Matrix Operations}{Basic Matrix
+ Operations}
+*/
+
+/*!
+ \fn qreal QTransform::m21() const
+
+ Returns the horizontal shearing factor.
+
+ \sa shear(), {QTransform#Basic Matrix Operations}{Basic Matrix
+ Operations}
+*/
+
+/*!
+ \fn qreal QTransform::m22() const
+
+ Returns the vertical scaling factor.
+
+ \sa scale(), {QTransform#Basic Matrix Operations}{Basic Matrix
+ Operations}
+*/
+
+/*!
+ \fn qreal QTransform::dx() const
+
+ Returns the horizontal translation factor.
+
+ \sa m31(), translate(), {QTransform#Basic Matrix Operations}{Basic Matrix
+ Operations}
+*/
+
+/*!
+ \fn qreal QTransform::dy() const
+
+ Returns the vertical translation factor.
+
+ \sa translate(), {QTransform#Basic Matrix Operations}{Basic Matrix
+ Operations}
+*/
+
+
+/*!
+ \fn qreal QTransform::m13() const
+
+ Returns the horizontal projection factor.
+
+ \sa translate(), {QTransform#Basic Matrix Operations}{Basic Matrix
+ Operations}
+*/
+
+
+/*!
+ \fn qreal QTransform::m23() const
+
+ Returns the vertical projection factor.
+
+ \sa translate(), {QTransform#Basic Matrix Operations}{Basic Matrix
+ Operations}
+*/
+
+/*!
+ \fn qreal QTransform::m31() const
+
+ Returns the horizontal translation factor.
+
+ \sa dx(), translate(), {QTransform#Basic Matrix Operations}{Basic Matrix
+ Operations}
+*/
+
+/*!
+ \fn qreal QTransform::m32() const
+
+ Returns the vertical translation factor.
+
+ \sa dy(), translate(), {QTransform#Basic Matrix Operations}{Basic Matrix
+ Operations}
+*/
+
+/*!
+ \fn qreal QTransform::m33() const
+
+ Returns the division factor.
+
+ \sa translate(), {QTransform#Basic Matrix Operations}{Basic Matrix
+ Operations}
+*/
+
+/*!
+ \fn qreal QTransform::determinant() const
+
+ Returns the matrix's determinant.
+*/
+
+/*!
+ \fn bool QTransform::isIdentity() const
+
+ Returns true if the matrix is the identity matrix, otherwise
+ returns false.
+
+ \sa reset()
+*/
+
+/*!
+ \fn bool QTransform::isAffine() const
+
+ Returns true if the matrix represent an affine transformation,
+ otherwise returns false.
+*/
+
+/*!
+ \fn bool QTransform::isScaling() const
+
+ Returns true if the matrix represents a scaling
+ transformation, otherwise returns false.
+
+ \sa reset()
+*/
+
+/*!
+ \fn bool QTransform::isRotating() const
+
+ Returns true if the matrix represents some kind of a
+ rotating transformation, otherwise returns false.
+
+ \sa reset()
+*/
+
+/*!
+ \fn bool QTransform::isTranslating() const
+
+ Returns true if the matrix represents a translating
+ transformation, otherwise returns false.
+
+ \sa reset()
+*/
+
+// returns true if the transform is uniformly scaling
+// (same scale in x and y direction)
+// scale is set to the max of x and y scaling factors
+Q_GUI_EXPORT
+bool qt_scaleForTransform(const QTransform &transform, qreal *scale)
+{
+ if (transform.type() <= QTransform::TxTranslate) {
+ *scale = 1;
+ return true;
+ } else if (transform.type() == QTransform::TxScale) {
+ const qreal xScale = qAbs(transform.m11());
+ const qreal yScale = qAbs(transform.m22());
+ *scale = qMax(xScale, yScale);
+ return qFuzzyCompare(xScale, yScale);
+ }
+
+ const qreal xScale = transform.m11() * transform.m11()
+ + transform.m21() * transform.m21();
+ const qreal yScale = transform.m12() * transform.m12()
+ + transform.m22() * transform.m22();
+ *scale = qSqrt(qMax(xScale, yScale));
+ return transform.type() == QTransform::TxRotate && qFuzzyCompare(xScale, yScale);
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/painting/qtransform.h b/src/gui/painting/qtransform.h
new file mode 100644
index 0000000000..de7ebcd01f
--- /dev/null
+++ b/src/gui/painting/qtransform.h
@@ -0,0 +1,346 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#ifndef QTRANSFORM_H
+#define QTRANSFORM_H
+
+#include <QtGui/qmatrix.h>
+#include <QtGui/qpainterpath.h>
+#include <QtGui/qpolygon.h>
+#include <QtGui/qregion.h>
+#include <QtGui/qwindowdefs.h>
+#include <QtCore/qline.h>
+#include <QtCore/qpoint.h>
+#include <QtCore/qrect.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+class QVariant;
+
+class Q_GUI_EXPORT QTransform
+{
+ Q_ENUMS(TransformationType)
+public:
+ enum TransformationType {
+ TxNone = 0x00,
+ TxTranslate = 0x01,
+ TxScale = 0x02,
+ TxRotate = 0x04,
+ TxShear = 0x08,
+ TxProject = 0x10
+ };
+
+ QTransform();
+ QTransform(qreal h11, qreal h12, qreal h13,
+ qreal h21, qreal h22, qreal h23,
+ qreal h31, qreal h32, qreal h33 = 1.0);
+ QTransform(qreal h11, qreal h12, qreal h21,
+ qreal h22, qreal dx, qreal dy);
+ explicit QTransform(const QMatrix &mtx);
+
+ bool isAffine() const;
+ bool isIdentity() const;
+ bool isInvertible() const;
+ bool isScaling() const;
+ bool isRotating() const;
+ bool isTranslating() const;
+
+ TransformationType type() const;
+
+ inline qreal determinant() const;
+ qreal det() const;
+
+ qreal m11() const;
+ qreal m12() const;
+ qreal m13() const;
+ qreal m21() const;
+ qreal m22() const;
+ qreal m23() const;
+ qreal m31() const;
+ qreal m32() const;
+ qreal m33() const;
+ qreal dx() const;
+ qreal dy() const;
+
+ void setMatrix(qreal m11, qreal m12, qreal m13,
+ qreal m21, qreal m22, qreal m23,
+ qreal m31, qreal m32, qreal m33);
+
+ QTransform inverted(bool *invertible = 0) const;
+ QTransform adjoint() const;
+ QTransform transposed() const;
+
+ QTransform &translate(qreal dx, qreal dy);
+ QTransform &scale(qreal sx, qreal sy);
+ QTransform &shear(qreal sh, qreal sv);
+ QTransform &rotate(qreal a, Qt::Axis axis = Qt::ZAxis);
+ QTransform &rotateRadians(qreal a, Qt::Axis axis = Qt::ZAxis);
+
+ static bool squareToQuad(const QPolygonF &square, QTransform &result);
+ static bool quadToSquare(const QPolygonF &quad, QTransform &result);
+ static bool quadToQuad(const QPolygonF &one,
+ const QPolygonF &two,
+ QTransform &result);
+
+ bool operator==(const QTransform &) const;
+ bool operator!=(const QTransform &) const;
+
+ QTransform &operator*=(const QTransform &);
+ QTransform operator*(const QTransform &o) const;
+
+ QTransform &operator=(const QTransform &);
+
+ operator QVariant() const;
+
+ void reset();
+ QPoint map(const QPoint &p) const;
+ QPointF map(const QPointF &p) const;
+ QLine map(const QLine &l) const;
+ QLineF map(const QLineF &l) const;
+ QPolygonF map(const QPolygonF &a) const;
+ QPolygon map(const QPolygon &a) const;
+ QRegion map(const QRegion &r) const;
+ QPainterPath map(const QPainterPath &p) const;
+ QPolygon mapToPolygon(const QRect &r) const;
+ QRect mapRect(const QRect &) const;
+ QRectF mapRect(const QRectF &) const;
+ void map(int x, int y, int *tx, int *ty) const;
+ void map(qreal x, qreal y, qreal *tx, qreal *ty) const;
+
+ const QMatrix &toAffine() const;
+
+ QTransform &operator*=(qreal div);
+ QTransform &operator/=(qreal div);
+ QTransform &operator+=(qreal div);
+ QTransform &operator-=(qreal div);
+
+ static QTransform fromTranslate(qreal dx, qreal dy);
+ static QTransform fromScale(qreal dx, qreal dy);
+
+private:
+ QMatrix affine;
+ qreal m_13;
+ qreal m_23;
+ qreal m_33;
+
+ mutable uint m_type : 5;
+ mutable uint m_dirty : 5;
+
+ class Private;
+ Private *d;
+};
+Q_DECLARE_TYPEINFO(QTransform, Q_MOVABLE_TYPE);
+
+/******* inlines *****/
+inline bool QTransform::isAffine() const
+{
+ return type() < TxProject;
+}
+inline bool QTransform::isIdentity() const
+{
+ return type() == TxNone;
+}
+
+inline bool QTransform::isInvertible() const
+{
+ return !qFuzzyCompare(determinant() + 1, 1);
+}
+
+inline bool QTransform::isScaling() const
+{
+ return type() >= TxScale;
+}
+inline bool QTransform::isRotating() const
+{
+ return type() >= TxRotate;
+}
+
+inline bool QTransform::isTranslating() const
+{
+ return type() >= TxTranslate;
+}
+
+inline qreal QTransform::determinant() const
+{
+ return affine._m11*(m_33*affine._m22-affine._dy*m_23) -
+ affine._m21*(m_33*affine._m12-affine._dy*m_13)+affine._dx*(m_23*affine._m12-affine._m22*m_13);
+}
+inline qreal QTransform::det() const
+{
+ return determinant();
+}
+inline qreal QTransform::m11() const
+{
+ return affine._m11;
+}
+inline qreal QTransform::m12() const
+{
+ return affine._m12;
+}
+inline qreal QTransform::m13() const
+{
+ return m_13;
+}
+inline qreal QTransform::m21() const
+{
+ return affine._m21;
+}
+inline qreal QTransform::m22() const
+{
+ return affine._m22;
+}
+inline qreal QTransform::m23() const
+{
+ return m_23;
+}
+inline qreal QTransform::m31() const
+{
+ return affine._dx;
+}
+inline qreal QTransform::m32() const
+{
+ return affine._dy;
+}
+inline qreal QTransform::m33() const
+{
+ return m_33;
+}
+inline qreal QTransform::dx() const
+{
+ return affine._dx;
+}
+inline qreal QTransform::dy() const
+{
+ return affine._dy;
+}
+
+inline QTransform &QTransform::operator*=(qreal num)
+{
+ affine._m11 *= num;
+ affine._m12 *= num;
+ m_13 *= num;
+ affine._m21 *= num;
+ affine._m22 *= num;
+ m_23 *= num;
+ affine._dx *= num;
+ affine._dy *= num;
+ m_33 *= num;
+ m_dirty |= TxScale;
+ return *this;
+}
+inline QTransform &QTransform::operator/=(qreal div)
+{
+ div = 1/div;
+ return operator*=(div);
+}
+inline QTransform &QTransform::operator+=(qreal num)
+{
+ affine._m11 += num;
+ affine._m12 += num;
+ m_13 += num;
+ affine._m21 += num;
+ affine._m22 += num;
+ m_23 += num;
+ affine._dx += num;
+ affine._dy += num;
+ m_33 += num;
+ m_dirty |= TxProject;
+ return *this;
+}
+inline QTransform &QTransform::operator-=(qreal num)
+{
+ affine._m11 -= num;
+ affine._m12 -= num;
+ m_13 -= num;
+ affine._m21 -= num;
+ affine._m22 -= num;
+ m_23 -= num;
+ affine._dx -= num;
+ affine._dy -= num;
+ m_33 -= num;
+ m_dirty |= TxProject;
+ return *this;
+}
+
+/****** stream functions *******************/
+Q_GUI_EXPORT QDataStream &operator<<(QDataStream &, const QTransform &);
+Q_GUI_EXPORT QDataStream &operator>>(QDataStream &, QTransform &);
+
+#ifndef QT_NO_DEBUG_STREAM
+Q_GUI_EXPORT QDebug operator<<(QDebug, const QTransform &);
+#endif
+/****** end stream functions *******************/
+
+// mathematical semantics
+Q_GUI_EXPORT_INLINE QPoint operator*(const QPoint &p, const QTransform &m)
+{ return m.map(p); }
+Q_GUI_EXPORT_INLINE QPointF operator*(const QPointF &p, const QTransform &m)
+{ return m.map(p); }
+Q_GUI_EXPORT_INLINE QLineF operator*(const QLineF &l, const QTransform &m)
+{ return m.map(l); }
+Q_GUI_EXPORT_INLINE QLine operator*(const QLine &l, const QTransform &m)
+{ return m.map(l); }
+Q_GUI_EXPORT_INLINE QPolygon operator *(const QPolygon &a, const QTransform &m)
+{ return m.map(a); }
+Q_GUI_EXPORT_INLINE QPolygonF operator *(const QPolygonF &a, const QTransform &m)
+{ return m.map(a); }
+Q_GUI_EXPORT_INLINE QRegion operator *(const QRegion &r, const QTransform &m)
+{ return m.map(r); }
+Q_GUI_EXPORT_INLINE QPainterPath operator *(const QPainterPath &p, const QTransform &m)
+{ return m.map(p); }
+
+Q_GUI_EXPORT_INLINE QTransform operator *(const QTransform &a, qreal n)
+{ QTransform t(a); t *= n; return t; }
+Q_GUI_EXPORT_INLINE QTransform operator /(const QTransform &a, qreal n)
+{ QTransform t(a); t /= n; return t; }
+Q_GUI_EXPORT_INLINE QTransform operator +(const QTransform &a, qreal n)
+{ QTransform t(a); t += n; return t; }
+Q_GUI_EXPORT_INLINE QTransform operator -(const QTransform &a, qreal n)
+{ QTransform t(a); t -= n; return t; }
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif
diff --git a/src/gui/painting/qvectorpath_p.h b/src/gui/painting/qvectorpath_p.h
new file mode 100644
index 0000000000..2713cdaedc
--- /dev/null
+++ b/src/gui/painting/qvectorpath_p.h
@@ -0,0 +1,166 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QVECTORPATH_P_H
+#define QVECTORPATH_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 <QtGui/qpaintengine.h>
+
+#include "qpaintengine_p.h"
+#include "qstroker_p.h"
+#include "qpainter_p.h"
+
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+
+#define QVECTORPATH_NO_CACHE
+
+struct QRealRect {
+ qreal x1, y1, x2, y2;
+};
+
+class Q_GUI_EXPORT QVectorPath
+{
+public:
+ enum Hint {
+ // Basic shapes...
+ LinesHint = 0x0001, // Just plain lines...
+ RectangleHint = 0x0002,
+ ConvexPolygonHint = 0x0003, // Convex polygon...
+ NonISectPolygonHint = 0x0004, // concave polygon, but not intersecting..
+ NonCurvedShapeHint = 0x0005, // Generic polygon, possibly self-intersecting...
+ CurvedShapeHint = 0x0006, // Generic vector path..
+ EllipseHint = 0x0007,
+ ShapeHintMask = 0x000f,
+
+ // Other hints
+ CacheHint = 0x0100,
+ ControlPointRect = 0x0200, // Set if the control point rect has been calculated...
+
+ // Shape rendering specifiers...
+ OddEvenFill = 0x1000,
+ WindingFill = 0x2000,
+ ImplicitClose = 0x4000,
+ };
+
+ // ### Falcon: introduca a struct XY for points so lars is not so confused...
+ QVectorPath(const qreal *points,
+ int count,
+ const QPainterPath::ElementType *elements = 0,
+ uint hints = CurvedShapeHint)
+ : m_elements(elements),
+ m_points(points),
+ m_count(count),
+ m_hints(hints)
+#ifndef QVECTORPATH_NO_CACHE
+ , m_cache(0)
+#endif
+ {
+ }
+
+ const QRealRect &controlPointRect() const;
+
+ inline Hint shape() const { return (Hint) (m_hints & ShapeHintMask); }
+
+ inline bool hasCacheHint() const { return m_hints & CacheHint; }
+ inline bool hasImplicitClose() const { return m_hints & ImplicitClose; }
+ inline bool hasWindingFill() const { return m_hints & WindingFill; }
+
+ inline uint hints() const { return m_hints; }
+
+ inline const QPainterPath::ElementType *elements() const { return m_elements; }
+ inline const qreal *points() const { return m_points; }
+ inline bool isEmpty() const { return m_points == 0; }
+
+ inline int elementCount() const { return m_count; }
+
+ static inline uint polygonFlags(QPaintEngine::PolygonDrawMode mode);
+
+private:
+ Q_DISABLE_COPY(QVectorPath)
+
+#ifndef QVECTORPATH_NO_CACHE
+ struct CacheEntry {
+ void *engine;
+ int id;
+ void *extra;
+ CacheEntry *next;
+ };
+
+ void addCacheData(CacheEntry *d) {
+ d->next = m_cache;
+ m_cache = d;
+ }
+
+ CacheEntry *m_cache;
+#endif
+
+ const QPainterPath::ElementType *m_elements;
+ const qreal *m_points;
+ const int m_count;
+
+ mutable uint m_hints;
+ mutable QRealRect m_cp_rect;
+};
+
+Q_GUI_EXPORT const QVectorPath &qtVectorPathForPath(const QPainterPath &path);
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif
diff --git a/src/gui/painting/qwindowsurface.cpp b/src/gui/painting/qwindowsurface.cpp
new file mode 100644
index 0000000000..bcb038094f
--- /dev/null
+++ b/src/gui/painting/qwindowsurface.cpp
@@ -0,0 +1,349 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <private/qwindowsurface_p.h>
+#include <qwidget.h>
+#include <private/qwidget_p.h>
+#include <private/qbackingstore_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QWindowSurfacePrivate
+{
+public:
+ QWindowSurfacePrivate(QWidget *w) : window(w), staticContentsSupport(false) {}
+
+ QWidget *window;
+ QRect geometry;
+ QRegion staticContents;
+ QList<QImage*> bufferImages;
+ bool staticContentsSupport;
+};
+
+/*!
+ \class QWindowSurface
+ \since 4.3
+ \internal
+ \preliminary
+ \ingroup qws
+
+ \brief The QWindowSurface class provides the drawing area for top-level
+ windows.
+*/
+
+
+/*!
+ \fn void QWindowSurface::beginPaint(const QRegion &region)
+
+ This function is called before painting onto the surface begins,
+ with the \a region in which the painting will occur.
+
+ \sa endPaint(), paintDevice()
+*/
+
+/*!
+ \fn void QWindowSurface::endPaint(const QRegion &region)
+
+ This function is called after painting onto the surface has ended,
+ with the \a region in which the painting was performed.
+
+ \sa beginPaint(), paintDevice()
+*/
+
+/*!
+ \fn void QWindowSurface::flush(QWidget *widget, const QRegion &region,
+ const QPoint &offset)
+
+ Flushes the given \a region from the specified \a widget onto the
+ screen.
+
+ Note that the \a offset parameter is currently unused.
+*/
+
+/*!
+ \fn QPaintDevice* QWindowSurface::paintDevice()
+
+ Implement this function to return the appropriate paint device.
+*/
+
+/*!
+ Constructs an empty surface for the given top-level \a window.
+*/
+QWindowSurface::QWindowSurface(QWidget *window)
+ : d_ptr(new QWindowSurfacePrivate(window))
+{
+ if (window)
+ window->setWindowSurface(this);
+}
+
+/*!
+ Destroys this surface.
+*/
+QWindowSurface::~QWindowSurface()
+{
+ if (d_ptr->window)
+ d_ptr->window->d_func()->extra->topextra->windowSurface = 0;
+ delete d_ptr;
+}
+
+/*!
+ Returns a pointer to the top-level window associated with this
+ surface.
+*/
+QWidget* QWindowSurface::window() const
+{
+ return d_ptr->window;
+}
+
+void QWindowSurface::beginPaint(const QRegion &)
+{
+}
+
+void QWindowSurface::endPaint(const QRegion &)
+{
+// QApplication::syncX();
+ qDeleteAll(d_ptr->bufferImages);
+ d_ptr->bufferImages.clear();
+}
+
+/*!
+ Sets the currently allocated area to be the given \a rect.
+
+ This function is called whenever area covered by the top-level
+ window changes.
+
+ \sa geometry()
+*/
+void QWindowSurface::setGeometry(const QRect &rect)
+{
+ d_ptr->geometry = rect;
+}
+
+/*!
+ Returns the currently allocated area on the screen.
+*/
+QRect QWindowSurface::geometry() const
+{
+ return d_ptr->geometry;
+}
+
+/*!
+ Scrolls the given \a area \a dx pixels to the right and \a dy
+ downward; both \a dx and \a dy may be negative.
+
+ Returns true if the area was scrolled successfully; false otherwise.
+*/
+bool QWindowSurface::scroll(const QRegion &area, int dx, int dy)
+{
+ Q_UNUSED(area);
+ Q_UNUSED(dx);
+ Q_UNUSED(dy);
+
+ return false;
+}
+
+/*!
+ Returns a QImage pointer which represents the actual buffer the \a widget
+ is drawn into or 0 if this is unavailable.
+
+ You must call beginPaint() before you call this function and the returned
+ pointer is only valid until endPaint() is called.
+*/
+QImage* QWindowSurface::buffer(const QWidget *widget)
+{
+ if (widget->window() != window())
+ return 0;
+
+ QPaintDevice *pdev = paintDevice();
+ if (!pdev || pdev->devType() != QInternal::Image)
+ return 0;
+
+ const QPoint off = offset(widget);
+ QImage *img = static_cast<QImage*>(pdev);
+
+ QRect rect(off, widget->size());
+ rect &= QRect(QPoint(), img->size());
+
+ if (rect.isEmpty())
+ return 0;
+
+ img = new QImage(img->scanLine(rect.y()) + rect.x() * img->depth() / 8,
+ rect.width(), rect.height(),
+ img->bytesPerLine(), img->format());
+ d_ptr->bufferImages.append(img);
+
+ return img;
+}
+
+/*!
+ Returns a QPixmap generated from the part of the backing store
+ corresponding to \a widget. Returns a null QPixmap if an error
+ occurs. The contents of the pixmap are only defined for the regions
+ of \a widget that have received paint events since the last resize
+ of the backing store.
+
+ If \a rectangle is a null rectangle (the default), the entire widget
+ is grabbed. Otherwise, the grabbed area is limited to \a rectangle.
+
+ The default implementation uses QWindowSurface::buffer().
+
+ \sa QPixmap::grabWidget()
+*/
+QPixmap QWindowSurface::grabWidget(const QWidget *widget, const QRect &rectangle) const
+{
+ QPixmap result;
+
+ if (widget->window() != window())
+ return result;
+
+ const QImage *img = const_cast<QWindowSurface *>(this)->buffer(widget->window());
+
+ if (!img || img->isNull())
+ return result;
+
+ QRect rect = rectangle.isEmpty() ? widget->rect() : (widget->rect() & rectangle);
+
+ rect.translate(offset(widget) - offset(widget->window()));
+ rect &= QRect(QPoint(), img->size());
+
+ if (rect.isEmpty())
+ return result;
+
+ QImage subimg(img->scanLine(rect.y()) + rect.x() * img->depth() / 8,
+ rect.width(), rect.height(),
+ img->bytesPerLine(), img->format());
+ subimg.detach(); //### expensive -- maybe we should have a real SubImage that shares reference count
+
+ result = QPixmap::fromImage(subimg);
+ return result;
+}
+
+/*!
+ Returns the offset of \a widget in the coordinates of this
+ window surface.
+ */
+QPoint QWindowSurface::offset(const QWidget *widget) const
+{
+ QWidget *window = d_ptr->window;
+ QPoint offset = widget->mapTo(window, QPoint());
+#ifdef Q_WS_QWS
+ offset += window->geometry().topLeft() - window->frameGeometry().topLeft();
+#endif
+ return offset;
+}
+
+/*!
+ \fn QRect QWindowSurface::rect(const QWidget *widget) const
+
+ Returns the rectangle for \a widget in the coordinates of this
+ window surface.
+*/
+
+bool QWindowSurface::hasStaticContentsSupport() const
+{
+ return d_ptr->staticContentsSupport;
+}
+
+void QWindowSurface::setStaticContentsSupport(bool enable)
+{
+ d_ptr->staticContentsSupport = enable;
+}
+
+void QWindowSurface::setStaticContents(const QRegion &region)
+{
+ d_ptr->staticContents = region;
+}
+
+QRegion QWindowSurface::staticContents() const
+{
+ return d_ptr->staticContents;
+}
+
+bool QWindowSurface::hasStaticContents() const
+{
+ return d_ptr->staticContentsSupport && !d_ptr->staticContents.isEmpty();
+}
+
+void qt_scrollRectInImage(QImage &img, const QRect &rect, const QPoint &offset)
+{
+ // make sure we don't detach
+ uchar *mem = const_cast<uchar*>(const_cast<const QImage &>(img).bits());
+
+ int lineskip = img.bytesPerLine();
+ int depth = img.depth() >> 3;
+
+
+ const QRect r = rect & QRect(0, 0, img.width(), img.height());
+ const QPoint p = rect.topLeft() + offset;
+
+ const uchar *src;
+ uchar *dest;
+
+ if (r.top() < p.y()) {
+ src = mem + r.bottom() * lineskip + r.left() * depth;
+ dest = mem + (p.y() + r.height() - 1) * lineskip + p.x() * depth;
+ lineskip = -lineskip;
+ } else {
+ src = mem + r.top() * lineskip + r.left() * depth;
+ dest = mem + p.y() * lineskip + p.x() * depth;
+ }
+
+ const int w = r.width();
+ int h = r.height();
+ const int bytes = w * depth;
+
+ // overlapping segments?
+ if (offset.y() == 0 && qAbs(offset.x()) < w) {
+ do {
+ ::memmove(dest, src, bytes);
+ dest += lineskip;
+ src += lineskip;
+ } while (--h);
+ } else {
+ do {
+ ::memcpy(dest, src, bytes);
+ dest += lineskip;
+ src += lineskip;
+ } while (--h);
+ }
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/painting/qwindowsurface_d3d.cpp b/src/gui/painting/qwindowsurface_d3d.cpp
new file mode 100644
index 0000000000..2b7f633157
--- /dev/null
+++ b/src/gui/painting/qwindowsurface_d3d.cpp
@@ -0,0 +1,169 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//#define D3D_DEBUG_BACKBUFFER
+
+#include <QtGui/QPaintDevice>
+#include <QtGui/QWidget>
+#include "qdebug.h"
+
+#include "qpaintengine_d3d_p.h"
+#include "qwindowsurface_d3d_p.h"
+#include "private/qwidget_p.h"
+#include "private/qbackingstore_p.h"
+
+#include <d3d9.h>
+
+QT_BEGIN_NAMESPACE
+
+extern QDirect3DPaintEngine *qt_d3dEngine();
+
+struct QD3DWindowSurfacePrivate
+{
+ QSize m_lastSize;
+ QWidget *m_widget;
+};
+
+QD3DWindowSurface::QD3DWindowSurface(QWidget *window)
+ : QWindowSurface(window), d_ptr(new QD3DWindowSurfacePrivate)
+{
+ Q_ASSERT(window->isTopLevel());
+ d_ptr->m_widget = window;
+}
+
+
+QD3DWindowSurface::~QD3DWindowSurface()
+{
+ delete d_ptr;
+}
+
+QPaintDevice *QD3DWindowSurface::paintDevice()
+{
+ return d_ptr->m_widget;
+}
+
+
+void QD3DWindowSurface::flush(QWidget *widget, const QRegion &rgn, const QPoint &offset)
+{
+ QPoint wOffset = qt_qwidget_data(widget)->wrect.topLeft();
+
+ QDirect3DPaintEngine *engine = qt_d3dEngine();
+ LPDIRECT3DSWAPCHAIN9 swapchain = engine->swapChain(d_ptr->m_widget);
+
+ if (swapchain) {
+ QRect br = rgn.boundingRect();
+ QRect wbr = br.translated(-wOffset);
+
+ RECT destrect;
+ destrect.left = wbr.x();
+ destrect.top = wbr.y();
+ destrect.right = destrect.left + wbr.width();
+ destrect.bottom = destrect.top + wbr.height();
+
+ RECT srcrect;
+ srcrect.left = br.x() + offset.x();
+ srcrect.top = br.y() + offset.y();
+ srcrect.right = wbr.width() + srcrect.left;
+ srcrect.bottom = wbr.height() + srcrect.top;
+ int devwidth = d_ptr->m_lastSize.width();
+ int devheight = d_ptr->m_lastSize.height();
+
+ if (devwidth <= srcrect.right) {
+ int diff = srcrect.right - devwidth;
+ srcrect.right -= diff;
+ destrect.right -= diff;
+ if (srcrect.right <= srcrect.left)
+ return;
+ }
+ if (devheight <= srcrect.bottom) {
+ int diff = srcrect.bottom - devheight;
+ srcrect.bottom -= diff;
+ destrect.bottom -= diff;
+ if (srcrect.bottom <= srcrect.top)
+ return;
+ }
+
+ if (FAILED(swapchain->Present(&srcrect, &destrect, widget->winId(), 0, 0)))
+ qWarning("QDirect3DPaintEngine: failed to present back buffer.");
+
+#ifdef D3D_DEBUG_BACKBUFFER
+ qDebug() << widget << srcrect.left << srcrect.top << wbr.width() << wbr.height() << "Dest: " << destrect.left << destrect.top;
+ IDirect3DSurface9 *surface;
+ swapchain->GetBackBuffer(0, D3DBACKBUFFER_TYPE_MONO, &surface);
+ QString filename("C:\\test.bmp");
+ D3DXSaveSurfaceToFile(filename.utf16(), D3DXIFF_BMP, surface, 0, 0);
+ surface->Release();
+#endif
+ }
+}
+
+void QD3DWindowSurface::setGeometry(const QRect &rect)
+{
+ if (rect.isEmpty())
+ qt_d3dEngine()->releaseSwapChain(d_ptr->m_widget);
+
+ d_ptr->m_lastSize = rect.size();
+ QWindowSurface::setGeometry(rect);
+}
+
+
+bool QD3DWindowSurface::scroll(const QRegion &area, int dx, int dy)
+{
+ QDirect3DPaintEngine *engine = qt_d3dEngine();
+ QRect rect = area.boundingRect();
+
+ RECT destrect;
+ destrect.left = rect.x()+dx;
+ destrect.top = rect.y()+dy;
+ destrect.right = rect.width() + destrect.left;
+ destrect.bottom = rect.height() + destrect.top;
+
+ RECT srcrect;
+ srcrect.left = rect.x();
+ srcrect.top = rect.y();
+ srcrect.right = rect.width() + srcrect.left;
+ srcrect.bottom = rect.height() + srcrect.top;
+
+ engine->scroll(d_ptr->m_widget, srcrect, destrect);
+ return true;
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/painting/qwindowsurface_d3d_p.h b/src/gui/painting/qwindowsurface_d3d_p.h
new file mode 100644
index 0000000000..9cdfe29a1e
--- /dev/null
+++ b/src/gui/painting/qwindowsurface_d3d_p.h
@@ -0,0 +1,84 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QWINDOWSURFACE_D3D_P_H
+#define QWINDOWSURFACE_D3D_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of the QLibrary class. This header file may change from
+// version to version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <qglobal.h>
+#include "private/qwindowsurface_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class QPaintDevice;
+class QPoint;
+class QRegion;
+class QWidget;
+struct QD3DWindowSurfacePrivate;
+
+class QD3DWindowSurface : public QWindowSurface
+{
+public:
+ QD3DWindowSurface(QWidget *widget);
+ ~QD3DWindowSurface();
+
+ QPaintDevice *paintDevice();
+ void flush(QWidget *widget, const QRegion &region, const QPoint &offset);
+ void setGeometry(const QRect &rect);
+ bool scroll(const QRegion &area, int dx, int dy);
+
+private:
+ QD3DWindowSurfacePrivate *d_ptr;
+};
+
+QT_END_NAMESPACE
+
+#endif // QWINDOWSURFACE_D3D_P_H
diff --git a/src/gui/painting/qwindowsurface_mac.cpp b/src/gui/painting/qwindowsurface_mac.cpp
new file mode 100644
index 0000000000..d0ad029100
--- /dev/null
+++ b/src/gui/painting/qwindowsurface_mac.cpp
@@ -0,0 +1,137 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qwindowsurface_mac_p.h"
+
+#include <private/qt_mac_p.h>
+#include <private/qt_cocoa_helpers_mac_p.h>
+#include <QtGui/qwidget.h>
+
+QT_BEGIN_NAMESPACE
+
+struct QMacWindowSurfacePrivate
+{
+ QWidget *widget;
+ QPixmap device;
+};
+
+QMacWindowSurface::QMacWindowSurface(QWidget *widget)
+ : QWindowSurface(widget), d_ptr(new QMacWindowSurfacePrivate)
+{
+ d_ptr->widget = widget;
+}
+
+QMacWindowSurface::~QMacWindowSurface()
+{
+ delete d_ptr;
+}
+
+QPaintDevice *QMacWindowSurface::paintDevice()
+{
+ return &d_ptr->device;
+}
+
+void QMacWindowSurface::flush(QWidget *widget, const QRegion &rgn, const QPoint &offset)
+{
+ Q_UNUSED(offset);
+
+ // Get a context for the widget.
+#ifndef QT_MAC_USE_COCOA
+ CGContextRef context;
+ CGrafPtr port = GetWindowPort(qt_mac_window_for(widget));
+ QDBeginCGContext(port, &context);
+#else
+ extern CGContextRef qt_mac_graphicsContextFor(QWidget *);
+ CGContextRef context = qt_mac_graphicsContextFor(widget);
+#endif
+ CGContextSaveGState(context);
+
+ // Flip context.
+ CGContextTranslateCTM(context, 0, widget->height());
+ CGContextScaleCTM(context, 1, -1);
+
+ // Clip to region.
+ const QVector<QRect> &rects = rgn.rects();
+ for (int i = 0; i < rects.size(); ++i) {
+ const QRect &rect = rects.at(i);
+ CGContextAddRect(context, CGRectMake(rect.x(), rect.y(), rect.width(), rect.height()));
+ }
+ CGContextClip(context);
+
+ // Draw the image onto the window.
+ const CGRect dest = CGRectMake(0, 0, widget->width(), widget->height());
+ const CGImageRef image = d_ptr->device.toMacCGImageRef();
+ qt_mac_drawCGImage(context, &dest, image);
+ CFRelease(image);
+
+ // Restore context.
+ CGContextRestoreGState(context);
+#ifndef QT_MAC_USE_COCOA
+ QDEndCGContext(port, &context);
+#else
+ CGContextFlush(context);
+#endif
+}
+
+void QMacWindowSurface::setGeometry(const QRect &rect)
+{
+ QWindowSurface::setGeometry(rect);
+ const QSize size = rect.size();
+ if (d_ptr->device.size() != size)
+ d_ptr->device = QPixmap(size);
+}
+
+bool QMacWindowSurface::scroll(const QRegion &area, int dx, int dy)
+{
+ if (d_ptr->device.size().isNull())
+ return false;
+
+ QCFType<CGImageRef> image = d_ptr->device.toMacCGImageRef();
+ const QRect rect(area.boundingRect());
+ const CGRect dest = CGRectMake(rect.x(), rect.y(), rect.width(), rect.height());
+ QCFType<CGImageRef> subimage = CGImageCreateWithImageInRect(image, dest);
+ QCFType<CGContextRef> context = qt_mac_cg_context(&d_ptr->device);
+ CGContextTranslateCTM(context, dx, dy);
+ qt_mac_drawCGImage(context, &dest, subimage);
+ return true;
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/painting/qwindowsurface_mac_p.h b/src/gui/painting/qwindowsurface_mac_p.h
new file mode 100644
index 0000000000..914b2df8de
--- /dev/null
+++ b/src/gui/painting/qwindowsurface_mac_p.h
@@ -0,0 +1,84 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QWINDOWSURFACE_MAC_P_H
+#define QWINDOWSURFACE_MAC_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 <qglobal.h>
+#include "private/qwindowsurface_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class QPaintDevice;
+class QPoint;
+class QRegion;
+class QWidget;
+struct QMacWindowSurfacePrivate;
+
+class QMacWindowSurface : public QWindowSurface
+{
+public:
+ QMacWindowSurface(QWidget *widget);
+ ~QMacWindowSurface();
+
+ QPaintDevice *paintDevice();
+ void flush(QWidget *widget, const QRegion &region, const QPoint &offset);
+ void setGeometry(const QRect &rect);
+ bool scroll(const QRegion &area, int dx, int dy);
+
+private:
+ QMacWindowSurfacePrivate *d_ptr;
+};
+
+QT_END_NAMESPACE
+
+#endif // QWINDOWSURFACE_MAC_P_H
diff --git a/src/gui/painting/qwindowsurface_p.h b/src/gui/painting/qwindowsurface_p.h
new file mode 100644
index 0000000000..76e68e4f91
--- /dev/null
+++ b/src/gui/painting/qwindowsurface_p.h
@@ -0,0 +1,112 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QWINDOWSURFACE_P_H
+#define QWINDOWSURFACE_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 <QtGui/qwidget.h>
+
+QT_BEGIN_NAMESPACE
+
+class QPaintDevice;
+class QRegion;
+class QRect;
+class QPoint;
+class QImage;
+class QWindowSurfacePrivate;
+
+class Q_GUI_EXPORT QWindowSurface
+{
+public:
+ QWindowSurface(QWidget *window);
+ virtual ~QWindowSurface();
+
+ QWidget *window() const;
+
+ virtual QPaintDevice *paintDevice() = 0;
+ virtual void flush(QWidget *widget, const QRegion &region,
+ const QPoint &offset) = 0;
+ virtual void setGeometry(const QRect &rect);
+ QRect geometry() const;
+
+ virtual bool scroll(const QRegion &area, int dx, int dy);
+
+ virtual void beginPaint(const QRegion &);
+ virtual void endPaint(const QRegion &);
+
+ virtual QImage* buffer(const QWidget *widget);
+ virtual QPixmap grabWidget(const QWidget *widget, const QRect& rectangle = QRect()) const;
+
+ virtual QPoint offset(const QWidget *widget) const;
+ inline QRect rect(const QWidget *widget) const;
+
+ bool hasStaticContentsSupport() const;
+
+ void setStaticContents(const QRegion &region);
+ QRegion staticContents() const;
+
+protected:
+ bool hasStaticContents() const;
+ void setStaticContentsSupport(bool enable);
+
+private:
+ QWindowSurfacePrivate *d_ptr;
+};
+
+inline QRect QWindowSurface::rect(const QWidget *widget) const
+{
+ return widget->rect().translated(offset(widget));
+}
+
+QT_END_NAMESPACE
+
+#endif // QWINDOWSURFACE_P_H
diff --git a/src/gui/painting/qwindowsurface_qws.cpp b/src/gui/painting/qwindowsurface_qws.cpp
new file mode 100644
index 0000000000..1b7fd42001
--- /dev/null
+++ b/src/gui/painting/qwindowsurface_qws.cpp
@@ -0,0 +1,1411 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qwindowsurface_qws_p.h"
+#include <qwidget.h>
+#include <qscreen_qws.h>
+#include <qwsmanager_qws.h>
+#include <qapplication.h>
+#include <qwsdisplay_qws.h>
+#include <qrgb.h>
+#include <qpaintengine.h>
+#include <qdesktopwidget.h>
+#include <private/qapplication_p.h>
+#include <private/qwsdisplay_qws_p.h>
+#include <private/qwidget_p.h>
+#include <private/qwsmanager_p.h>
+#include <private/qwslock_p.h>
+#include <private/qbackingstore_p.h>
+#include <stdio.h>
+
+QT_BEGIN_NAMESPACE
+
+#ifdef Q_BACKINGSTORE_SUBSURFACES
+
+typedef QMap<int, QWSWindowSurface*> SurfaceMap;
+Q_GLOBAL_STATIC(SurfaceMap, winIdToSurfaceMap);
+
+QWSWindowSurface* qt_findWindowSurface(int winId)
+{
+ return winIdToSurfaceMap()->value(winId);
+}
+
+static void qt_insertWindowSurface(int winId, QWSWindowSurface *surface)
+{
+ if (!surface)
+ winIdToSurfaceMap()->remove(winId);
+ else
+ winIdToSurfaceMap()->insert(winId, surface);
+}
+
+#endif // Q_BACKINGSTORE_SUBSURFACES
+
+inline bool isWidgetOpaque(const QWidget *w)
+{
+ return w->d_func()->isOpaque;
+}
+
+static inline QScreen *getScreen(const QWidget *w)
+{
+ const QList<QScreen*> subScreens = qt_screen->subScreens();
+ if (subScreens.isEmpty())
+ return qt_screen;
+
+ const int screen = QApplication::desktop()->screenNumber(w);
+
+ return qt_screen->subScreens().at(screen < 0 ? 0 : screen);
+}
+
+static int bytesPerPixel(QImage::Format format)
+{
+ switch (format) {
+ case QImage::Format_Invalid:
+ return 0;
+#ifndef QT_NO_DEBUG
+ case QImage::Format_Mono:
+ case QImage::Format_MonoLSB:
+ qFatal("QWSWindowSurface: Invalid backingstore format: %i",
+ int(format));
+#endif
+ case QImage::Format_Indexed8:
+ return 1;
+ case QImage::Format_RGB32:
+ case QImage::Format_ARGB32:
+ case QImage::Format_ARGB32_Premultiplied:
+ return 4;
+ case QImage::Format_RGB16:
+ case QImage::Format_RGB555:
+ case QImage::Format_RGB444:
+ case QImage::Format_ARGB4444_Premultiplied:
+ return 2;
+ case QImage::Format_ARGB8565_Premultiplied:
+ case QImage::Format_ARGB8555_Premultiplied:
+ case QImage::Format_ARGB6666_Premultiplied:
+ case QImage::Format_RGB666:
+ case QImage::Format_RGB888:
+ return 3;
+ default:
+#ifndef QT_NO_DEBUG
+ qFatal("QWSWindowSurface: Invalid backingstore format: %i",
+ int(format));
+#endif
+ return 0;
+ }
+}
+
+static inline int nextMulOf4(int n)
+{
+ return ((n + 3) & 0xfffffffc);
+}
+
+static inline void setImageMetrics(QImage &img, QWidget *window) {
+ QScreen *myScreen = getScreen(window);
+ if (myScreen) {
+ int dpmx = myScreen->width()*1000 / myScreen->physicalWidth();
+ int dpmy = myScreen->height()*1000 / myScreen->physicalHeight();
+ img.setDotsPerMeterX(dpmx);
+ img.setDotsPerMeterY(dpmy);
+ }
+}
+
+void QWSWindowSurface::invalidateBuffer()
+{
+
+ QWidget *win = window();
+ if (win) {
+ win->d_func()->invalidateBuffer(win->rect());
+#ifndef QT_NO_QWS_MANAGER
+ QTLWExtra *topextra = win->d_func()->extra->topextra;
+ QWSManager *manager = topextra->qwsManager;
+ if (manager)
+ manager->d_func()->dirtyRegion(QDecoration::All,
+ QDecoration::Normal);
+#endif
+ }
+}
+
+QWSWindowSurfacePrivate::QWSWindowSurfacePrivate()
+ : flags(0),
+#ifdef QT_QWS_CLIENTBLIT
+ directId(-1),
+#endif
+ winId(0)
+{
+}
+
+void QWSWindowSurfacePrivate::setWinId(int id)
+{
+ winId = id;
+}
+
+int QWSWindowSurface::winId() const
+{
+ // XXX: the widget winId may change during the lifetime of the widget!!!
+
+ const QWidget *win = window();
+ if (win && win->isWindow())
+ return win->internalWinId();
+
+#ifdef Q_BACKINGSTORE_SUBSURFACES
+ if (!d_ptr->winId) {
+ QWSWindowSurface *that = const_cast<QWSWindowSurface*>(this);
+ QWSDisplay *display = QWSDisplay::instance();
+ const int id = display->takeId();
+ qt_insertWindowSurface(id, that);
+ that->d_ptr->winId = id;
+
+ if (win)
+ display->nameRegion(id, win->objectName(), win->windowTitle());
+ else
+ display->nameRegion(id, QString(), QString());
+
+ display->setAltitude(id, 1, true); // XXX
+ }
+#endif
+
+ return d_ptr->winId;
+}
+
+void QWSWindowSurface::setWinId(int id)
+{
+ d_ptr->winId = id;
+}
+
+/*!
+ \class QWSWindowSurface
+ \since 4.2
+ \ingroup qws
+ \preliminary
+ \internal
+
+ \brief The QWSWindowSurface class provides the drawing area for top-level
+ windows in Qt for Embedded Linux.
+
+ Note that this class is only available in Qt for Embedded Linux.
+
+ In \l{Qt for Embedded Linux}, the default behavior is for each client to
+ render its widgets into memory while the server is responsible for
+ putting the contents of the memory onto the
+ screen. QWSWindowSurface is used by the window system to implement
+ the associated memory allocation.
+
+ When a screen update is required, the server runs through all the
+ top-level windows that intersect with the region that is about to
+ be updated, and ensures that the associated clients have updated
+ their memory buffer. Then the server uses the screen driver to
+ copy the content of the memory to the screen. To locate the
+ relevant parts of memory, the driver is provided with the list of
+ top-level windows that intersect with the given region. Associated
+ with each of the top-level windows there is a window surface
+ representing the drawing area of the window.
+
+ When deriving from the QWSWindowSurface class, e.g., when adding
+ an \l {Adding an Accelerated Graphics Driver to Qt for Embedded Linux}
+ {accelerated graphics driver}, there are several pure virtual
+ functions that must be implemented. In addition, QWSWindowSurface
+ provides several virtual functions that can be reimplemented to
+ customize the drawing process.
+
+ \tableofcontents
+
+ \section1 Pure Virtual Functions
+
+ There are in fact two window surface instances for each top-level
+ window; one used by the application when drawing a window, and
+ another used by the server application to perform window
+ compositioning. Implement the attach() to create the server-side
+ representation of the surface. The data() function must be
+ implemented to provide the required data.
+
+ Implement the key() function to uniquely identify the surface
+ class, and the isValid() function to determine is a surface
+ corresponds to a given widget.
+
+ The geometry() function must be implemented to let the window
+ system determine the area required by the window surface
+ (QWSWindowSurface also provides a corresponding virtual
+ setGeometry() function that is called whenever the area necessary
+ for the top-level window to be drawn, changes). The image()
+ function is called by the window system during window
+ compositioning, and must be implemented to return an image of the
+ top-level window.
+
+ Finally, the paintDevice() function must be implemented to return
+ the appropriate paint device, and the scroll() function must be
+ implemented to scroll the given region of the surface the given
+ number of pixels.
+
+ \section1 Virtual Functions
+
+ When painting onto the surface, the window system will always call
+ the beginPaint() function before any painting operations are
+ performed. Likewise the endPaint() function is automatically
+ called when the painting is done. Reimplement the painterOffset()
+ function to alter the offset that is applied when drawing.
+
+ The window system uses the flush() function to put a given region
+ of the widget onto the screen, and the release() function to
+ deallocate the screen region corresponding to this window surface.
+
+ \section1 Other Members
+
+ QWSWindowSurface provides the window() function returning a
+ pointer to the top-level window the surface is representing. The
+ currently visible region of the associated widget can be retrieved
+ and set using the clipRegion() and setClipRegion() functions,
+ respectively.
+
+ When the window system performs the window compositioning, it uses
+ the SurfaceFlag enum describing the surface content. The currently
+ set surface flags can be retrieved and altered using the
+ surfaceFlags() and setSurfaceFlags() functions. In addition,
+ QWSWindowSurface provides the isBuffered(), isOpaque() and
+ isRegionReserved() convenience functions.
+
+ \sa {Qt for Embedded Linux Architecture#Drawing on Screen}{Qt for
+ Embedded Linux Architecture}
+*/
+
+/*!
+ \enum QWSWindowSurface::SurfaceFlag
+
+ This enum is used to describe the window surface's contents. It
+ is used by the screen driver to handle region allocation and
+ composition.
+
+ \value RegionReserved The surface contains a reserved area. Once
+ allocated, a reserved area can not not be changed by the window
+ system, i.e., no other widgets can be drawn on top of this.
+
+ \value Buffered
+ The surface is in a memory area which is not part of a framebuffer.
+ (A top-level window with QWidget::windowOpacity() other than 1.0 must use
+ a buffered surface in order to making blending with the background work.)
+
+ \value Opaque
+ The surface contains only opaque pixels.
+
+ \sa surfaceFlags(), setSurfaceFlags()
+*/
+
+/*!
+ \fn bool QWSWindowSurface::isValid() const
+ \since 4.3
+
+ Implement this function to return true if the surface is a valid
+ surface for the given top-level \a window; otherwise return
+ false.
+
+ \sa window(), key()
+*/
+
+/*!
+ \fn QString QWSWindowSurface::key() const
+
+ Implement this function to return a string that uniquely
+ identifies the class of this surface.
+
+ \sa window(), isValid()
+*/
+
+/*!
+ \fn QByteArray QWSWindowSurface::permanentState() const
+ \since 4.3
+
+ Implement this function to return the data required for creating a
+ server-side representation of the surface.
+
+ \sa attach()
+*/
+
+/*!
+ \fn void QWSWindowSurface::setPermanentState(const QByteArray &data)
+ \since 4.3
+
+ Implement this function to attach a server-side surface instance
+ to the corresponding client side instance using the given \a
+ data. Return true if successful; otherwise return false.
+
+ \sa data()
+*/
+
+/*!
+ \fn const QImage QWSWindowSurface::image() const
+
+ Implement this function to return an image of the top-level window.
+
+ \sa geometry()
+*/
+
+/*!
+ \fn bool QWSWindowSurface::isRegionReserved() const
+
+ Returns true if the QWSWindowSurface::RegionReserved is set; otherwise
+ returns false.
+
+ \sa surfaceFlags()
+*/
+
+/*!
+ \fn bool QWSWindowSurface::isBuffered() const
+
+ Returns true if the QWSWindowSurface::Buffered is set; otherwise returns false.
+
+ \sa surfaceFlags()
+*/
+
+/*!
+ \fn bool QWSWindowSurface::isOpaque() const
+
+ Returns true if the QWSWindowSurface::Opaque is set; otherwise
+ returns false.
+
+ \sa surfaceFlags()
+*/
+
+
+/*!
+ Constructs an empty surface.
+*/
+QWSWindowSurface::QWSWindowSurface()
+ : QWindowSurface(0), d_ptr(new QWSWindowSurfacePrivate)
+{
+}
+
+/*!
+ Constructs an empty surface for the given top-level \a widget.
+*/
+QWSWindowSurface::QWSWindowSurface(QWidget *widget)
+ : QWindowSurface(widget), d_ptr(new QWSWindowSurfacePrivate)
+{
+}
+
+QWSWindowSurface::~QWSWindowSurface()
+{
+#ifdef Q_BACKINGSTORE_SUBSURFACES
+ winIdToSurfaceMap()->remove(winId());
+#endif
+
+ delete d_ptr;
+}
+
+/*!
+ Returns the offset to be used when painting.
+
+ \sa paintDevice()
+*/
+QPoint QWSWindowSurface::painterOffset() const
+{
+ const QWidget *w = window();
+ if (!w)
+ return QPoint();
+ return w->geometry().topLeft() - w->frameGeometry().topLeft();
+}
+
+void QWSWindowSurface::beginPaint(const QRegion &)
+{
+ lock();
+}
+
+void QWSWindowSurface::endPaint(const QRegion &)
+{
+ unlock();
+}
+
+// XXX: documentation!!!
+QByteArray QWSWindowSurface::transientState() const
+{
+ return QByteArray();
+}
+
+QByteArray QWSWindowSurface::permanentState() const
+{
+ return QByteArray();
+}
+
+void QWSWindowSurface::setTransientState(const QByteArray &state)
+{
+ Q_UNUSED(state);
+}
+
+void QWSWindowSurface::setPermanentState(const QByteArray &state)
+{
+ Q_UNUSED(state);
+}
+
+bool QWSWindowSurface::lock(int timeout)
+{
+ Q_UNUSED(timeout);
+ return true;
+}
+
+void QWSWindowSurface::unlock()
+{
+}
+
+#ifdef QT_QWS_CLIENTBLIT
+/*! \internal */
+const QRegion QWSWindowSurface::directRegion() const
+{
+ return d_ptr->direct;
+}
+
+/*! \internal */
+int QWSWindowSurface::directRegionId() const
+{
+ return d_ptr->directId;
+}
+
+/*! \internal */
+void QWSWindowSurface::setDirectRegion(const QRegion &r, int id)
+{
+ d_ptr->direct = r;
+ d_ptr->directId = id;
+}
+#endif
+
+/*!
+ Returns the region currently visible on the screen.
+
+ \sa setClipRegion()
+*/
+const QRegion QWSWindowSurface::clipRegion() const
+{
+ return d_ptr->clip;
+}
+
+/*!
+ Sets the region currently visible on the screen to be the given \a
+ clip region.
+
+ \sa clipRegion()
+*/
+void QWSWindowSurface::setClipRegion(const QRegion &clip)
+{
+ if (clip == d_ptr->clip)
+ return;
+
+ QRegion expose = (clip - d_ptr->clip);
+ d_ptr->clip = clip;
+
+ if (expose.isEmpty() || clip.isEmpty())
+ return; // No repaint or flush required.
+
+ QWidget *win = window();
+ if (!win)
+ return;
+
+ if (isBuffered()) {
+ // No repaint required. Flush exposed area via the backing store.
+ win->d_func()->syncBackingStore(expose);
+ return;
+ }
+
+#ifndef QT_NO_QWS_MANAGER
+ // Invalidate exposed decoration area.
+ if (win && win->isWindow()) {
+ QTLWExtra *topextra = win->d_func()->extra->topextra;
+ if (QWSManager *manager = topextra->qwsManager) {
+ QRegion decorationExpose(manager->region());
+ decorationExpose.translate(-win->geometry().topLeft());
+ decorationExpose &= expose;
+ if (!decorationExpose.isEmpty()) {
+ expose -= decorationExpose;
+ manager->d_func()->dirtyRegion(QDecoration::All, QDecoration::Normal, decorationExpose);
+ }
+ }
+ }
+#endif
+
+ // Invalidate exposed widget area.
+ win->d_func()->invalidateBuffer(expose);
+}
+
+/*!
+ Returns the surface flags describing the contents of this surface.
+
+ \sa isBuffered(), isOpaque(), isRegionReserved()
+*/
+QWSWindowSurface::SurfaceFlags QWSWindowSurface::surfaceFlags() const
+{
+ return d_ptr->flags;
+}
+
+/*!
+ Sets the surface flags describing the contents of this surface, to
+ be the given \a flags.
+
+ \sa surfaceFlags()
+*/
+void QWSWindowSurface::setSurfaceFlags(SurfaceFlags flags)
+{
+ d_ptr->flags = flags;
+}
+
+void QWSWindowSurface::setGeometry(const QRect &rect)
+{
+ QRegion mask = rect;
+
+ const QWidget *win = window();
+ if (win) {
+#ifndef QT_NO_QWS_MANAGER
+ if (win->isWindow()) {
+ QTLWExtra *topextra = win->d_func()->extra->topextra;
+ QWSManager *manager = topextra->qwsManager;
+
+ if (manager) {
+ // The frame geometry is the bounding rect of manager->region,
+ // which could be too much, so we need to clip.
+ mask &= (manager->region() + win->geometry());
+ }
+ }
+#endif
+
+ const QRegion winMask = win->mask();
+ if (!winMask.isEmpty())
+ mask &= winMask.translated(win->geometry().topLeft());
+ }
+
+ setGeometry(rect, mask);
+}
+
+void QWSWindowSurface::setGeometry(const QRect &rect, const QRegion &mask)
+{
+ if (rect == geometry()) // XXX: && mask == prevMask
+ return;
+
+ const bool isResize = rect.size() != geometry().size();
+ const bool needsRepaint = isResize || !isBuffered();
+
+ QWindowSurface::setGeometry(rect);
+
+ const QRegion region = mask & rect;
+ QWidget *win = window();
+ // Only request regions for widgets visible on the screen.
+ // (Added the !win check for compatibility reasons, because
+ // there was no "if (win)" check before).
+ const bool requestQWSRegion = !win || !win->testAttribute(Qt::WA_DontShowOnScreen);
+ if (requestQWSRegion)
+ QWidget::qwsDisplay()->requestRegion(winId(), key(), permanentState(), region);
+
+ if (needsRepaint)
+ invalidateBuffer();
+
+ if (!requestQWSRegion) {
+ // We didn't request a region, hence we won't get a QWSRegionEvent::Allocation
+ // event back from the server so we set the clip directly. We have to
+ // do this after the invalidateBuffer() call above, as it might trigger a
+ // backing store sync, resulting in too many update requests.
+ setClipRegion(region);
+ }
+}
+
+static inline void flushUpdate(QWidget *widget, const QRegion &region,
+ const QPoint &offset)
+{
+#ifdef QT_NO_PAINT_DEBUG
+ Q_UNUSED(widget);
+ Q_UNUSED(region);
+ Q_UNUSED(offset);
+#else
+ static int delay = -1;
+
+ if (delay == -1)
+ delay = qgetenv("QT_FLUSH_UPDATE").toInt() * 10;
+
+ if (delay == 0)
+ return;
+
+ static QWSYellowSurface surface(true);
+ surface.setDelay(delay);
+ surface.flush(widget, region, offset);
+#endif // QT_NO_PAINT_DEBUG
+}
+
+void QWSWindowSurface::flush(QWidget *widget, const QRegion &region,
+ const QPoint &offset)
+{
+ const QWidget *win = window();
+ if (!win)
+ return;
+
+ QWExtra *extra = win->d_func()->extra;
+ if (extra && extra->proxyWidget)
+ return;
+
+ Q_UNUSED(offset);
+
+ const bool opaque = isOpaque();
+#ifdef QT_QWS_DISABLE_FLUSHCLIPPING
+ QRegion toFlush = region;
+#else
+ QRegion toFlush = region & d_ptr->clip;
+#endif
+
+ if (!toFlush.isEmpty()) {
+ flushUpdate(widget, toFlush, QPoint(0, 0));
+ QPoint globalZero = win->mapToGlobal(QPoint(0, 0));
+ toFlush.translate(globalZero);
+
+#ifdef QT_QWS_CLIENTBLIT
+ bool needRepaint = true;
+ if (opaque) {
+ QScreen* widgetScreen = getScreen(widget);
+ if (widgetScreen->supportsBlitInClients()) {
+
+ QWSDisplay::grab();
+ if(directRegion().intersected(toFlush) == toFlush) {
+ QPoint translate = -globalZero + painterOffset() + geometry().topLeft();
+ QRegion flushRegion = toFlush.translated(translate);
+ widgetScreen->blit(image(), geometry().topLeft(), flushRegion);
+ widgetScreen->setDirty(toFlush.boundingRect());
+ needRepaint = false;
+ }
+ QWSDisplay::ungrab();
+ }
+ }
+
+ if(needRepaint)
+#endif
+ win->qwsDisplay()->repaintRegion(winId(), win->windowFlags(), opaque, toFlush);
+ }
+}
+
+/*!
+ Move the surface with the given \a offset.
+
+ A subclass may reimplement this function to enable accelerated window move.
+ It must return true if the move was successful and no repaint is necessary,
+ false otherwise.
+
+ The default implementation updates the QWindowSurface geometry and
+ returns true if the surface is buffered; false otherwise.
+
+ This function is called by the window system on the client instance.
+
+ \sa isBuffered()
+*/
+bool QWSWindowSurface::move(const QPoint &offset)
+{
+ QWindowSurface::setGeometry(geometry().translated(offset));
+ return isBuffered();
+}
+
+/*!
+ Move the surface with the given \a offset.
+
+ The new visible region after the window move is given by \a newClip
+ in screen coordinates.
+
+ A subclass may reimplement this function to enable accelerated window move.
+ The returned region indicates the area that still needs to be composed
+ on the screen.
+
+ The default implementation updates the QWindowSurface geometry and
+ returns the union of the old and new geometry.
+
+ This function is called by the window system on the server instance.
+*/
+QRegion QWSWindowSurface::move(const QPoint &offset, const QRegion &newClip)
+{
+ const QRegion oldGeometry = geometry();
+ QWindowSurface::setGeometry(geometry().translated(offset));
+ return oldGeometry + newClip;
+}
+
+void QWSWindowSurface::releaseSurface()
+{
+}
+
+bool QWSMemorySurface::lock(int timeout)
+{
+ Q_UNUSED(timeout);
+#ifndef QT_NO_QWS_MULTIPROCESS
+ if (memlock && !memlock->lock(QWSLock::BackingStore))
+ return false;
+#endif
+#ifndef QT_NO_THREAD
+ threadLock.lock();
+#endif
+ return true;
+}
+
+void QWSMemorySurface::unlock()
+{
+#ifndef QT_NO_THREAD
+ threadLock.unlock();
+#endif
+#ifndef QT_NO_QWS_MULTIPROCESS
+ if (memlock)
+ memlock->unlock(QWSLock::BackingStore);
+#endif
+}
+
+QWSMemorySurface::QWSMemorySurface()
+ : QWSWindowSurface()
+#ifndef QT_NO_QWS_MULTIPROCESS
+ , memlock(0)
+#endif
+{
+ setSurfaceFlags(Buffered);
+}
+
+QWSMemorySurface::QWSMemorySurface(QWidget *w)
+ : QWSWindowSurface(w)
+{
+ SurfaceFlags flags = Buffered;
+ if (isWidgetOpaque(w))
+ flags |= Opaque;
+ setSurfaceFlags(flags);
+
+#ifndef QT_NO_QWS_MULTIPROCESS
+ memlock = QWSDisplay::Data::getClientLock();
+#endif
+}
+
+QWSMemorySurface::~QWSMemorySurface()
+{
+}
+
+
+QImage::Format
+QWSMemorySurface::preferredImageFormat(const QWidget *widget) const
+{
+ QScreen *screen = getScreen(widget);
+ const int depth = screen->depth();
+ const bool opaque = isWidgetOpaque(widget);
+
+ if (!opaque) {
+ if (depth <= 12)
+ return QImage::Format_ARGB4444_Premultiplied;
+ else if (depth <= 15)
+ return QImage::Format_ARGB8555_Premultiplied;
+ else if (depth <= 16)
+ return QImage::Format_ARGB8565_Premultiplied;
+ else if (depth <= 18)
+ return QImage::Format_ARGB6666_Premultiplied;
+ else
+ return QImage::Format_ARGB32_Premultiplied;
+ }
+
+ QImage::Format format = screen->pixelFormat();
+ if (format > QImage::Format_Indexed8) // ### assumes all new image formats supports a QPainter
+ return format;
+
+ if (depth <= 12)
+ return QImage::Format_RGB444;
+ else if (depth <= 15)
+ return QImage::Format_RGB555;
+ else if (depth <= 16)
+ return QImage::Format_RGB16;
+ else if (depth <= 18)
+ return QImage::Format_RGB666;
+ else if (depth <= 24)
+ return QImage::Format_RGB888;
+ else
+ return QImage::Format_ARGB32_Premultiplied;
+}
+
+#ifndef QT_NO_QWS_MULTIPROCESS
+void QWSMemorySurface::setLock(int lockId)
+{
+ if (memlock && memlock->id() == lockId)
+ return;
+ delete memlock;
+ memlock = (lockId == -1 ? 0 : new QWSLock(lockId));
+ return;
+}
+#endif // QT_NO_QWS_MULTIPROCESS
+
+bool QWSMemorySurface::isValid() const
+{
+ if (img.isNull())
+ return true;
+
+ const QWidget *win = window();
+ if (preferredImageFormat(win) != img.format())
+ return false;
+
+ if (isOpaque() != isWidgetOpaque(win)) // XXX: use QWidgetPrivate::isOpaque()
+ return false;
+
+ return true;
+}
+
+// from qwindowsurface.cpp
+extern void qt_scrollRectInImage(QImage &img, const QRect &rect, const QPoint &offset);
+
+bool QWSMemorySurface::scroll(const QRegion &area, int dx, int dy)
+{
+ const QVector<QRect> rects = area.rects();
+ for (int i = 0; i < rects.size(); ++i)
+ qt_scrollRectInImage(img, rects.at(i), QPoint(dx, dy));
+
+ return true;
+}
+
+QPoint QWSMemorySurface::painterOffset() const
+{
+ const QWidget *w = window();
+ if (!w)
+ return QPoint();
+
+ if (w->mask().isEmpty())
+ return QWSWindowSurface::painterOffset();
+
+ const QRegion region = w->mask()
+ & w->frameGeometry().translated(-w->geometry().topLeft());
+ return -region.boundingRect().topLeft();
+}
+
+QWSLocalMemSurface::QWSLocalMemSurface()
+ : QWSMemorySurface(), mem(0), memsize(0)
+{
+}
+
+QWSLocalMemSurface::QWSLocalMemSurface(QWidget *w)
+ : QWSMemorySurface(w), mem(0), memsize(0)
+{
+}
+
+QWSLocalMemSurface::~QWSLocalMemSurface()
+{
+ if (memsize)
+ delete[] mem;
+}
+
+void QWSLocalMemSurface::setGeometry(const QRect &rect)
+{
+ QSize size = rect.size();
+
+ QWidget *win = window();
+ if (win && !win->mask().isEmpty()) {
+ const QRegion region = win->mask()
+ & rect.translated(-win->geometry().topLeft());
+ size = region.boundingRect().size();
+ }
+
+ uchar *deleteLater = 0;
+ // In case of a Hide event we need to delete the memory after sending the
+ // event to the server in order to let the server animate the event.
+ if (size.isEmpty()) {
+ deleteLater = mem;
+ mem = 0;
+ }
+
+ if (img.size() != size) {
+ delete[] mem;
+ if (size.isEmpty()) {
+ mem = 0;
+ img = QImage();
+ } else {
+ const QImage::Format format = preferredImageFormat(win);
+ const int bpl = nextMulOf4(bytesPerPixel(format) * size.width());
+ const int memsize = bpl * size.height();
+ mem = new uchar[memsize];
+ img = QImage(mem, size.width(), size.height(), bpl, format);
+ setImageMetrics(img, win);
+ }
+ }
+
+ QWSWindowSurface::setGeometry(rect);
+ delete[] deleteLater;
+}
+
+QByteArray QWSLocalMemSurface::permanentState() const
+{
+ QByteArray array;
+ array.resize(sizeof(uchar*) + 3 * sizeof(int) +
+ sizeof(SurfaceFlags));
+
+ char *ptr = array.data();
+
+ *reinterpret_cast<uchar**>(ptr) = mem;
+ ptr += sizeof(uchar*);
+
+ reinterpret_cast<int*>(ptr)[0] = img.width();
+ reinterpret_cast<int*>(ptr)[1] = img.height();
+ ptr += 2 * sizeof(int);
+
+ *reinterpret_cast<int *>(ptr) = img.format();
+ ptr += sizeof(int);
+
+ *reinterpret_cast<SurfaceFlags*>(ptr) = surfaceFlags();
+
+ return array;
+}
+
+void QWSLocalMemSurface::setPermanentState(const QByteArray &data)
+{
+ int width;
+ int height;
+ QImage::Format format;
+ SurfaceFlags flags;
+
+ const char *ptr = data.constData();
+
+ mem = *reinterpret_cast<uchar* const*>(ptr);
+ ptr += sizeof(uchar*);
+
+ width = reinterpret_cast<const int*>(ptr)[0];
+ height = reinterpret_cast<const int*>(ptr)[1];
+ ptr += 2 * sizeof(int);
+
+ format = QImage::Format(*reinterpret_cast<const int*>(ptr));
+ ptr += sizeof(int);
+
+ flags = *reinterpret_cast<const SurfaceFlags*>(ptr);
+
+ const int bpl = nextMulOf4(bytesPerPixel(format) * width);
+ QWSMemorySurface::img = QImage(mem, width, height, bpl, format);
+ setSurfaceFlags(flags);
+}
+
+void QWSLocalMemSurface::releaseSurface()
+{
+ mem = 0;
+ img = QImage();
+}
+
+#ifndef QT_NO_QWS_MULTIPROCESS
+
+QWSSharedMemSurface::QWSSharedMemSurface()
+ : QWSMemorySurface()
+{
+}
+
+QWSSharedMemSurface::QWSSharedMemSurface(QWidget *widget)
+ : QWSMemorySurface(widget)
+{
+}
+
+QWSSharedMemSurface::~QWSSharedMemSurface()
+{
+ // mem.detach() is done automatically by ~QSharedMemory
+}
+
+bool QWSSharedMemSurface::setMemory(int memId)
+{
+ if (mem.id() == memId)
+ return true;
+
+ mem.detach();
+ if (!mem.attach(memId)) {
+ perror("QWSSharedMemSurface: attaching to shared memory");
+ qCritical("QWSSharedMemSurface: Error attaching to"
+ " shared memory 0x%x", memId);
+ return false;
+ }
+
+ return true;
+}
+
+#ifdef QT_QWS_CLIENTBLIT
+void QWSSharedMemSurface::setDirectRegion(const QRegion &r, int id)
+{
+ QWSMemorySurface::setDirectRegion(r, id);
+ if(mem.address())
+ *(uint *)mem.address() = id;
+}
+
+const QRegion QWSSharedMemSurface::directRegion() const
+{
+ QWSSharedMemory *cmem = const_cast<QWSSharedMemory *>(&mem);
+ if (cmem->address() && ((int*)cmem->address())[0] == directRegionId())
+ return QWSMemorySurface::directRegion();
+ else
+ return QRegion();
+}
+#endif
+
+void QWSSharedMemSurface::setPermanentState(const QByteArray &data)
+{
+ int memId;
+ int width;
+ int height;
+ int lockId;
+ QImage::Format format;
+ SurfaceFlags flags;
+
+ const int *ptr = reinterpret_cast<const int*>(data.constData());
+
+ memId = ptr[0];
+ width = ptr[1];
+ height = ptr[2];
+ lockId = ptr[3];
+ format = QImage::Format(ptr[4]);
+ flags = SurfaceFlags(ptr[5]);
+
+ setSurfaceFlags(flags);
+ setMemory(memId);
+ setLock(lockId);
+
+#ifdef QT_QWS_CLIENTBLIT
+ uchar *base = static_cast<uchar*>(mem.address()) + sizeof(uint);
+#else
+ uchar *base = static_cast<uchar*>(mem.address());
+#endif
+ const int bpl = nextMulOf4(bytesPerPixel(format) * width);
+ QWSMemorySurface::img = QImage(base, width, height, bpl, format);
+}
+
+void QWSSharedMemSurface::setGeometry(const QRect &rect)
+{
+ const QSize size = rect.size();
+ if (img.size() != size) {
+ if (size.isEmpty()) {
+ mem.detach();
+ img = QImage();
+ } else {
+ mem.detach();
+
+ QWidget *win = window();
+ const QImage::Format format = preferredImageFormat(win);
+ const int bpl = nextMulOf4(bytesPerPixel(format) * size.width());
+#ifdef QT_QWS_CLIENTBLIT
+ const int imagesize = bpl * size.height() + sizeof(uint);
+#else
+ const int imagesize = bpl * size.height();
+#endif
+ if (!mem.create(imagesize)) {
+ perror("QWSSharedMemSurface::setGeometry allocating shared memory");
+ qFatal("Error creating shared memory of size %d", imagesize);
+ }
+#ifdef QT_QWS_CLIENTBLIT
+ *((uint *)mem.address()) = 0;
+ uchar *base = static_cast<uchar*>(mem.address()) + sizeof(uint);
+#else
+ uchar *base = static_cast<uchar*>(mem.address());
+#endif
+ img = QImage(base, size.width(), size.height(), bpl, format);
+ setImageMetrics(img, win);
+ }
+ }
+
+ QWSWindowSurface::setGeometry(rect);
+}
+
+QByteArray QWSSharedMemSurface::permanentState() const
+{
+ QByteArray array;
+ array.resize(6 * sizeof(int));
+
+ int *ptr = reinterpret_cast<int*>(array.data());
+
+ ptr[0] = mem.id();
+ ptr[1] = img.width();
+ ptr[2] = img.height();
+ ptr[3] = (memlock ? memlock->id() : -1);
+ ptr[4] = int(img.format());
+ ptr[5] = int(surfaceFlags());
+
+ return array;
+}
+
+void QWSSharedMemSurface::releaseSurface()
+{
+ mem.detach();
+ img = QImage();
+}
+
+#endif // QT_NO_QWS_MULTIPROCESS
+
+#ifndef QT_NO_PAINTONSCREEN
+
+QWSOnScreenSurface::QWSOnScreenSurface(QWidget *w)
+ : QWSMemorySurface(w)
+{
+ attachToScreen(getScreen(w));
+ setSurfaceFlags(Opaque);
+}
+
+QWSOnScreenSurface::QWSOnScreenSurface()
+ : QWSMemorySurface()
+{
+ setSurfaceFlags(Opaque);
+}
+
+void QWSOnScreenSurface::attachToScreen(const QScreen *s)
+{
+ screen = s;
+ uchar *base = screen->base();
+ QImage::Format format = screen->pixelFormat();
+
+ if (format == QImage::Format_Invalid || format == QImage::Format_Indexed8) {
+ //### currently we have no paint engine for indexed image formats
+ qFatal("QWSOnScreenSurface::attachToScreen(): screen depth %d "
+ "not implemented", screen->depth());
+ return;
+ }
+ QWSMemorySurface::img = QImage(base, screen->width(), screen->height(),
+ screen->linestep(), format );
+}
+
+QWSOnScreenSurface::~QWSOnScreenSurface()
+{
+}
+
+QPoint QWSOnScreenSurface::painterOffset() const
+{
+ return geometry().topLeft() + QWSWindowSurface::painterOffset();
+}
+
+bool QWSOnScreenSurface::isValid() const
+{
+ const QWidget *win = window();
+ if (screen != getScreen(win))
+ return false;
+ if (img.isNull())
+ return false;
+ return QScreen::isWidgetPaintOnScreen(win);
+}
+
+QByteArray QWSOnScreenSurface::permanentState() const
+{
+ QByteArray array;
+ array.resize(sizeof(int));
+ int *ptr = reinterpret_cast<int*>(array.data());
+ ptr[0] = QApplication::desktop()->screenNumber(window());
+ return array;
+}
+
+void QWSOnScreenSurface::setPermanentState(const QByteArray &data)
+{
+ const int *ptr = reinterpret_cast<const int*>(data.constData());
+ const int screenNo = ptr[0];
+
+ QScreen *screen = qt_screen;
+ if (screenNo > 0)
+ screen = qt_screen->subScreens().at(screenNo);
+ attachToScreen(screen);
+}
+
+#endif // QT_NO_PAINTONSCREEN
+
+#ifndef QT_NO_PAINT_DEBUG
+
+QWSYellowSurface::QWSYellowSurface(bool isClient)
+ : QWSWindowSurface(), delay(10)
+{
+ if (isClient) {
+ setWinId(QWidget::qwsDisplay()->takeId());
+ QWidget::qwsDisplay()->nameRegion(winId(),
+ QLatin1String("Debug flush paint"),
+ QLatin1String("Silly yellow thing"));
+ QWidget::qwsDisplay()->setAltitude(winId(), 1, true);
+ }
+ setSurfaceFlags(Buffered);
+}
+
+QWSYellowSurface::~QWSYellowSurface()
+{
+}
+
+QByteArray QWSYellowSurface::permanentState() const
+{
+ QByteArray array;
+ array.resize(2 * sizeof(int));
+
+ int *ptr = reinterpret_cast<int*>(array.data());
+ ptr[0] = surfaceSize.width();
+ ptr[1] = surfaceSize.height();
+
+ return array;
+}
+
+void QWSYellowSurface::setPermanentState(const QByteArray &data)
+{
+ const int *ptr = reinterpret_cast<const int*>(data.constData());
+
+ const int width = ptr[0];
+ const int height = ptr[1];
+
+ img = QImage(width, height, QImage::Format_ARGB32);
+ img.fill(qRgba(255,255,31,127));
+}
+
+void QWSYellowSurface::flush(QWidget *widget, const QRegion &region,
+ const QPoint &offset)
+{
+ Q_UNUSED(offset);
+
+ QWSDisplay *display = QWidget::qwsDisplay();
+ QRegion rgn = region;
+
+ if (widget)
+ rgn.translate(widget->mapToGlobal(QPoint(0, 0)));
+
+ surfaceSize = region.boundingRect().size();
+
+ const int id = winId();
+ display->requestRegion(id, key(), permanentState(), rgn);
+ display->setAltitude(id, 1, true);
+ display->repaintRegion(id, 0, false, rgn);
+
+ ::usleep(500 * delay);
+ display->requestRegion(id, key(), permanentState(), QRegion());
+ ::usleep(500 * delay);
+}
+
+#endif // QT_NO_PAINT_DEBUG
+
+#ifndef QT_NO_DIRECTPAINTER
+
+static inline QScreen *getPrimaryScreen()
+{
+ QScreen *screen = QScreen::instance();
+ if (!screen->base()) {
+ QList<QScreen*> subScreens = screen->subScreens();
+ if (subScreens.size() < 1)
+ return 0;
+ screen = subScreens.at(0);
+ }
+ return screen;
+}
+
+QWSDirectPainterSurface::QWSDirectPainterSurface(bool isClient,
+ QDirectPainter::SurfaceFlag flags)
+ : QWSWindowSurface(), flushingRegionEvents(false), doLocking(false)
+{
+ setSurfaceFlags(Opaque);
+ synchronous = (flags == QDirectPainter::ReservedSynchronous);
+
+ if (isClient) {
+ setWinId(QWidget::qwsDisplay()->takeId());
+ QWidget::qwsDisplay()->nameRegion(winId(),
+ QLatin1String("QDirectPainter reserved space"),
+ QLatin1String("reserved"));
+ } else {
+ setWinId(0);
+ }
+ _screen = QScreen::instance();
+ if (!_screen->base()) {
+ QList<QScreen*> subScreens = _screen->subScreens();
+ if (subScreens.size() < 1)
+ _screen = 0;
+ else
+ _screen = subScreens.at(0);
+ }
+}
+
+QWSDirectPainterSurface::~QWSDirectPainterSurface()
+{
+ if (winId() && QWSDisplay::instance()) // make sure not in QApplication destructor
+ QWidget::qwsDisplay()->destroyRegion(winId());
+}
+
+void QWSDirectPainterSurface::setRegion(const QRegion &region)
+{
+ const int id = winId();
+ QWidget::qwsDisplay()->requestRegion(id, key(), permanentState(), region);
+#ifndef QT_NO_QWS_MULTIPROCESS
+ if (synchronous)
+ QWSDisplay::instance()->d->waitForRegionAck(id);
+#endif
+}
+
+void QWSDirectPainterSurface::flush(QWidget *, const QRegion &r, const QPoint &)
+{
+ QWSDisplay::instance()->repaintRegion(winId(), 0, true, r);
+}
+
+QByteArray QWSDirectPainterSurface::permanentState() const
+{
+ QByteArray res;
+ if (isRegionReserved())
+ res.append( 'r');
+ return res;
+}
+
+void QWSDirectPainterSurface::setPermanentState(const QByteArray &ba)
+{
+ if (ba.size() > 0 && ba.at(0) == 'r')
+ setReserved();
+ setSurfaceFlags(surfaceFlags() | Opaque);
+}
+
+void QWSDirectPainterSurface::beginPaint(const QRegion &region)
+{
+ QWSWindowSurface::beginPaint(region);
+#ifndef QT_NO_QWS_MULTIPROCESS
+ if (!synchronous) {
+ flushingRegionEvents = true;
+ QWSDisplay::instance()->d->waitForRegionEvents(winId(), doLocking);
+ flushingRegionEvents = false;
+ }
+#endif
+}
+
+bool QWSDirectPainterSurface::hasPendingRegionEvents() const
+{
+#ifndef QT_NO_QWS_MULTIPROCESS
+ if (synchronous)
+ return false;
+
+ return QWSDisplay::instance()->d->hasPendingRegionEvents();
+#else
+ return false;
+#endif
+}
+
+bool QWSDirectPainterSurface::lock(int timeout)
+{
+#ifndef QT_NO_THREAD
+ threadLock.lock();
+#endif
+ Q_UNUSED(timeout);
+ if (doLocking)
+ QWSDisplay::grab(true);
+ return true;
+}
+
+void QWSDirectPainterSurface::unlock()
+{
+ if (doLocking)
+ QWSDisplay::ungrab();
+#ifndef QT_NO_THREAD
+ threadLock.unlock();
+#endif
+}
+
+#endif // QT_NO_DIRECTPAINTER
+
+QT_END_NAMESPACE
diff --git a/src/gui/painting/qwindowsurface_qws_p.h b/src/gui/painting/qwindowsurface_qws_p.h
new file mode 100644
index 0000000000..5eb5c8a339
--- /dev/null
+++ b/src/gui/painting/qwindowsurface_qws_p.h
@@ -0,0 +1,353 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QWINDOWSURFACE_QWS_P_H
+#define QWINDOWSURFACE_QWS_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 "qwindowsurface_p.h"
+#include <qregion.h>
+#include <qimage.h>
+#include <qdirectpainter_qws.h>
+#include <qmutex.h>
+#include <private/qwssharedmemory_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QScreen;
+class QWSWindowSurfacePrivate;
+
+class Q_GUI_EXPORT QWSWindowSurface : public QWindowSurface
+{
+public:
+ QWSWindowSurface();
+ QWSWindowSurface(QWidget *widget);
+ ~QWSWindowSurface();
+
+ virtual bool isValid() const = 0;
+
+ virtual void setGeometry(const QRect &rect);
+ virtual void setGeometry(const QRect &rect, const QRegion &mask);
+ virtual void flush(QWidget *widget, const QRegion &region,
+ const QPoint &offset);
+
+ virtual bool move(const QPoint &offset);
+ virtual QRegion move(const QPoint &offset, const QRegion &newClip);
+
+ virtual QPoint painterOffset() const; // remove!!!
+
+ virtual void beginPaint(const QRegion &);
+ virtual void endPaint(const QRegion &);
+
+ virtual bool lock(int timeout = -1);
+ virtual void unlock();
+
+ virtual QString key() const = 0;
+
+ // XXX: not good enough
+ virtual QByteArray transientState() const;
+ virtual QByteArray permanentState() const;
+ virtual void setTransientState(const QByteArray &state);
+ virtual void setPermanentState(const QByteArray &state);
+
+ virtual QImage image() const = 0;
+ virtual QPaintDevice *paintDevice() = 0;
+
+ const QRegion clipRegion() const;
+ void setClipRegion(const QRegion &);
+
+#ifdef QT_QWS_CLIENTBLIT
+ virtual const QRegion directRegion() const;
+ virtual int directRegionId() const;
+ virtual void setDirectRegion(const QRegion &, int);
+#endif
+
+ enum SurfaceFlag {
+ RegionReserved = 0x1,
+ Buffered = 0x2,
+ Opaque = 0x4
+ };
+ Q_DECLARE_FLAGS(SurfaceFlags, SurfaceFlag)
+
+ SurfaceFlags surfaceFlags() const;
+
+ inline bool isRegionReserved() const {
+ return surfaceFlags() & RegionReserved;
+ }
+ inline bool isBuffered() const { return surfaceFlags() & Buffered; }
+ inline bool isOpaque() const { return surfaceFlags() & Opaque; }
+
+ int winId() const;
+ virtual void releaseSurface();
+
+protected:
+ void setSurfaceFlags(SurfaceFlags type);
+ void setWinId(int id);
+
+private:
+ friend class QWidgetPrivate;
+
+ void invalidateBuffer();
+
+ QWSWindowSurfacePrivate *d_ptr;
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(QWSWindowSurface::SurfaceFlags)
+
+class QWSWindowSurfacePrivate
+{
+public:
+ QWSWindowSurfacePrivate();
+
+ void setWinId(int id);
+
+ QWSWindowSurface::SurfaceFlags flags;
+ QRegion clip;
+#ifdef QT_QWS_CLIENTBLIT
+ QRegion direct;
+ int directId;
+#endif
+
+ int winId;
+};
+
+class QWSLock;
+
+class Q_GUI_EXPORT QWSMemorySurface : public QWSWindowSurface
+{
+public:
+ QWSMemorySurface();
+ QWSMemorySurface(QWidget *widget);
+ ~QWSMemorySurface();
+
+ bool isValid() const;
+
+ QPaintDevice *paintDevice() { return &img; }
+ bool scroll(const QRegion &area, int dx, int dy);
+
+ QImage image() const { return img; };
+ QPoint painterOffset() const;
+
+ bool lock(int timeout = -1);
+ void unlock();
+
+protected:
+ QImage::Format preferredImageFormat(const QWidget *widget) const;
+
+#ifndef QT_NO_QWS_MULTIPROCESS
+ void setLock(int lockId);
+ QWSLock *memlock;
+#endif
+#ifndef QT_NO_THREAD
+ QMutex threadLock;
+#endif
+
+ QImage img;
+};
+
+class Q_GUI_EXPORT QWSLocalMemSurface : public QWSMemorySurface
+{
+public:
+ QWSLocalMemSurface();
+ QWSLocalMemSurface(QWidget *widget);
+ ~QWSLocalMemSurface();
+
+ void setGeometry(const QRect &rect);
+
+ QString key() const { return QLatin1String("mem"); }
+ QByteArray permanentState() const;
+
+ void setPermanentState(const QByteArray &data);
+ virtual void releaseSurface();
+protected:
+ uchar *mem;
+ int memsize;
+};
+
+#ifndef QT_NO_QWS_MULTIPROCESS
+class Q_GUI_EXPORT QWSSharedMemSurface : public QWSMemorySurface
+{
+public:
+ QWSSharedMemSurface();
+ QWSSharedMemSurface(QWidget *widget);
+ ~QWSSharedMemSurface();
+
+ void setGeometry(const QRect &rect);
+
+ QString key() const { return QLatin1String("shm"); }
+ QByteArray permanentState() const;
+
+ void setPermanentState(const QByteArray &data);
+
+#ifdef QT_QWS_CLIENTBLIT
+ virtual void setDirectRegion(const QRegion &, int);
+ virtual const QRegion directRegion() const;
+#endif
+ virtual void releaseSurface();
+
+private:
+ bool setMemory(int memId);
+
+ QWSSharedMemory mem;
+};
+#endif // QT_NO_QWS_MULTIPROCESS
+
+#ifndef QT_NO_PAINTONSCREEN
+class Q_GUI_EXPORT QWSOnScreenSurface : public QWSMemorySurface
+{
+public:
+ QWSOnScreenSurface();
+ QWSOnScreenSurface(QWidget *widget);
+ ~QWSOnScreenSurface();
+
+ bool isValid() const;
+ QPoint painterOffset() const;
+
+ QString key() const { return QLatin1String("OnScreen"); }
+ QByteArray permanentState() const;
+
+ void setPermanentState(const QByteArray &data);
+
+private:
+ void attachToScreen(const QScreen *screen);
+
+ const QScreen *screen;
+};
+#endif // QT_NO_PAINTONSCREEN
+
+#ifndef QT_NO_PAINT_DEBUG
+class Q_GUI_EXPORT QWSYellowSurface : public QWSWindowSurface
+{
+public:
+ QWSYellowSurface(bool isClient = false);
+ ~QWSYellowSurface();
+
+ void setDelay(int msec) { delay = msec; }
+
+ bool isValid() const { return true; }
+
+ void flush(QWidget *widget, const QRegion &region, const QPoint &offset);
+
+ QString key() const { return QLatin1String("Yellow"); }
+ QByteArray permanentState() const;
+
+ void setPermanentState(const QByteArray &data);
+
+ QPaintDevice *paintDevice() { return &img; }
+ QImage image() const { return img; }
+
+private:
+ int delay;
+ QSize surfaceSize; // client side
+ QImage img; // server side
+};
+#endif // QT_NO_PAINT_DEBUG
+
+#ifndef QT_NO_DIRECTPAINTER
+
+class QScreen;
+
+class Q_GUI_EXPORT QWSDirectPainterSurface : public QWSWindowSurface
+{
+public:
+ QWSDirectPainterSurface(bool isClient = false,
+ QDirectPainter::SurfaceFlag flags = QDirectPainter::NonReserved);
+ ~QWSDirectPainterSurface();
+
+ void setReserved() { setSurfaceFlags(RegionReserved); }
+
+ void setGeometry(const QRect &rect) { setRegion(rect); }
+
+ void setRegion(const QRegion &region);
+ QRegion region() const { return clipRegion(); }
+
+ void flush(QWidget*, const QRegion &, const QPoint &);
+
+ bool isValid() const { return false; }
+
+ QString key() const { return QLatin1String("DirectPainter"); }
+ QByteArray permanentState() const;
+
+ void setPermanentState(const QByteArray &);
+
+ QImage image() const { return QImage(); }
+ QPaintDevice *paintDevice() { return 0; }
+
+ // hw: get rid of this
+ WId windowId() const { return static_cast<WId>(winId()); }
+
+ QScreen *screen() const { return _screen; }
+
+ void beginPaint(const QRegion &);
+ bool lock(int timeout = -1);
+ void unlock();
+
+ void setLocking(bool b) { doLocking = b; }
+
+ bool hasPendingRegionEvents() const;
+
+private:
+ QScreen *_screen;
+#ifndef QT_NO_THREAD
+ QMutex threadLock;
+#endif
+
+ friend void qt_directpainter_region(QDirectPainter*, const QRegion&, int);
+ bool flushingRegionEvents;
+ bool synchronous;
+ bool doLocking;
+};
+
+#endif // QT_NO_DIRECTPAINTER
+
+QT_END_NAMESPACE
+
+#endif // QWINDOWSURFACE_QWS_P_H
diff --git a/src/gui/painting/qwindowsurface_raster.cpp b/src/gui/painting/qwindowsurface_raster.cpp
new file mode 100644
index 0000000000..7a74fe0dad
--- /dev/null
+++ b/src/gui/painting/qwindowsurface_raster.cpp
@@ -0,0 +1,413 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <qdebug.h>
+
+#include <qglobal.h> // for Q_WS_WIN define (non-PCH)
+#ifdef Q_WS_WIN
+#include <qt_windows.h>
+#endif
+
+#include <QtGui/qpaintdevice.h>
+#include <QtGui/qwidget.h>
+
+#include "private/qwindowsurface_raster_p.h"
+#include "private/qnativeimage_p.h"
+#include "private/qwidget_p.h"
+
+#ifdef Q_WS_X11
+#include "private/qpixmap_x11_p.h"
+#include "private/qt_x11_p.h"
+#include "private/qwidget_p.h"
+#include "qx11info_x11.h"
+#endif
+#include "private/qdrawhelper_p.h"
+
+#ifdef Q_WS_MAC
+#include <private/qt_cocoa_helpers_mac_p.h>
+#endif
+
+QT_BEGIN_NAMESPACE
+
+#ifdef Q_WS_WIN
+PtrUpdateLayeredWindowIndirect ptrUpdateLayeredWindowIndirect;
+#endif
+
+class QRasterWindowSurfacePrivate
+{
+public:
+ QNativeImage *image;
+
+#ifdef Q_WS_X11
+ GC gc;
+#ifndef QT_NO_XRENDER
+ uint translucentBackground : 1;
+#endif
+#endif
+#if defined(Q_WS_WIN) && !defined(Q_OS_WINCE)
+ uint canUseLayeredWindow : 1;
+#endif
+ uint inSetGeometry : 1;
+};
+
+QRasterWindowSurface::QRasterWindowSurface(QWidget *window)
+ : QWindowSurface(window), d_ptr(new QRasterWindowSurfacePrivate)
+{
+#ifdef Q_WS_X11
+ d_ptr->gc = XCreateGC(X11->display, window->handle(), 0, 0);
+#ifndef QT_NO_XRENDER
+ d_ptr->translucentBackground = X11->use_xrender
+ && window->x11Info().depth() == 32;
+#endif
+#endif
+#if defined(Q_WS_WIN) && !defined(Q_OS_WINCE)
+ d_ptr->canUseLayeredWindow = ptrUpdateLayeredWindowIndirect
+ && (qt_widget_private(window)->data.window_flags & Qt::FramelessWindowHint);
+#endif
+ d_ptr->image = 0;
+ d_ptr->inSetGeometry = false;
+ setStaticContentsSupport(true);
+}
+
+
+QRasterWindowSurface::~QRasterWindowSurface()
+{
+#ifdef Q_WS_X11
+ XFreeGC(X11->display, d_ptr->gc);
+#endif
+ if (d_ptr->image)
+ delete d_ptr->image;
+
+ delete d_ptr;
+}
+
+
+QPaintDevice *QRasterWindowSurface::paintDevice()
+{
+ return &d_ptr->image->image;
+}
+
+void QRasterWindowSurface::beginPaint(const QRegion &rgn)
+{
+#if (defined(Q_WS_X11) && !defined(QT_NO_XRENDER)) || (defined(Q_WS_WIN) && !defined(Q_OS_WINCE))
+ if (!qt_widget_private(window())->isOpaque) {
+#if defined(Q_WS_WIN) && !defined(Q_OS_WINCE)
+ if (d_ptr->image->image.format() != QImage::Format_ARGB32_Premultiplied
+ && d_ptr->canUseLayeredWindow)
+ prepareBuffer(QImage::Format_ARGB32_Premultiplied, window());
+#endif
+ QPainter p(&d_ptr->image->image);
+ p.setCompositionMode(QPainter::CompositionMode_Source);
+ const QVector<QRect> rects = rgn.rects();
+ const QColor blank = Qt::transparent;
+ for (QVector<QRect>::const_iterator it = rects.begin(); it != rects.end(); ++it) {
+ p.fillRect(*it, blank);
+ }
+ }
+#endif
+#if defined(Q_OS_WINCE)
+ Q_UNUSED(rgn);
+#endif
+}
+
+void QRasterWindowSurface::flush(QWidget *widget, const QRegion &rgn, const QPoint &offset)
+{
+ Q_D(QRasterWindowSurface);
+
+ // Not ready for painting yet, bail out. This can happen in
+ // QWidget::create_sys()
+ if (!d->image)
+ return;
+
+#ifdef Q_WS_WIN
+ QRect br = rgn.boundingRect();
+
+#ifndef Q_OS_WINCE
+ if (!qt_widget_private(window())->isOpaque && d->canUseLayeredWindow) {
+ QRect r = window()->frameGeometry();
+ QPoint frameOffset = qt_widget_private(window())->frameStrut().topLeft();
+ QRect dirtyRect = br.translated(offset + frameOffset);
+
+ SIZE size = {r.width(), r.height()};
+ POINT ptDst = {r.x(), r.y()};
+ POINT ptSrc = {0, 0};
+ Q_BLENDFUNCTION blend = {AC_SRC_OVER, 0, (int)(255.0 * window()->windowOpacity()), Q_AC_SRC_ALPHA};
+ RECT dirty = {dirtyRect.x(), dirtyRect.y(),
+ dirtyRect.x() + dirtyRect.width(), dirtyRect.y() + dirtyRect.height()};
+ Q_UPDATELAYEREDWINDOWINFO info = {sizeof(info), NULL, &ptDst, &size, d->image->hdc, &ptSrc, 0, &blend, Q_ULW_ALPHA, &dirty};
+
+ (*ptrUpdateLayeredWindowIndirect)(window()->internalWinId(), &info);
+ } else
+#endif
+ {
+ QPoint wOffset = qt_qwidget_data(widget)->wrect.topLeft();
+
+ HDC widget_dc = widget->getDC();
+
+ QRect wbr = br.translated(-wOffset);
+ BitBlt(widget_dc, wbr.x(), wbr.y(), wbr.width(), wbr.height(),
+ d->image->hdc, br.x() + offset.x(), br.y() + offset.y(), SRCCOPY);
+ widget->releaseDC(widget_dc);
+ }
+
+#ifndef QT_NO_DEBUG
+ static bool flush = !qgetenv("QT_FLUSH_WINDOWSURFACE").isEmpty();
+ if (flush) {
+ SelectObject(qt_win_display_dc(), GetStockObject(BLACK_BRUSH));
+ Rectangle(qt_win_display_dc(), 0, 0, d->image->width() + 2, d->image->height() + 2);
+ BitBlt(qt_win_display_dc(), 1, 1, d->image->width(), d->image->height(),
+ d->image->hdc, 0, 0, SRCCOPY);
+ }
+#endif
+
+#endif
+
+#ifdef Q_WS_X11
+ extern void *qt_getClipRects(const QRegion &r, int &num); // in qpaintengine_x11.cpp
+ extern QWidgetData* qt_widget_data(QWidget *);
+ QPoint wOffset = qt_qwidget_data(widget)->wrect.topLeft();
+
+ if (widget->window() != window()) {
+ XFreeGC(X11->display, d_ptr->gc);
+ d_ptr->gc = XCreateGC(X11->display, widget->handle(), 0, 0);
+ }
+
+ QRegion wrgn(rgn);
+ if (!wOffset.isNull())
+ wrgn.translate(-wOffset);
+ QRect wbr = wrgn.boundingRect();
+
+ int num;
+ XRectangle *rects = (XRectangle *)qt_getClipRects(wrgn, num);
+ XSetClipRectangles(X11->display, d_ptr->gc, 0, 0, rects, num, YXBanded);
+
+ QRect br = rgn.boundingRect().translated(offset);
+#ifndef QT_NO_MITSHM
+ if (d_ptr->image->xshmpm) {
+ XCopyArea(X11->display, d_ptr->image->xshmpm, widget->handle(), d_ptr->gc,
+ br.x(), br.y(), br.width(), br.height(), wbr.x(), wbr.y());
+ XSync(X11->display, False);
+ } else
+#endif
+ {
+ const QImage &src = d->image->image;
+ br = br.intersected(src.rect());
+ if (src.format() != QImage::Format_RGB32) {
+ QX11PixmapData *data = new QX11PixmapData(QPixmapData::PixmapType);
+ data->xinfo = widget->x11Info();
+ data->fromImage(src, Qt::AutoColor);
+ QPixmap pm = QPixmap(data);
+ XCopyArea(X11->display, pm.handle(), widget->handle(), d_ptr->gc, br.x() , br.y() , br.width(), br.height(), wbr.x(), wbr.y());
+ } else {
+ // qpaintengine_x11.cpp
+ extern void qt_x11_drawImage(const QRect &rect, const QPoint &pos, const QImage &image, Drawable hd, GC gc, Display *dpy, Visual *visual, int depth);
+ qt_x11_drawImage(br, wbr.topLeft(), src, widget->handle(), d_ptr->gc, X11->display, (Visual *)widget->x11Info().visual(), widget->x11Info().depth());
+ }
+ }
+#endif // FALCON
+
+#ifdef Q_WS_MAC
+
+// qDebug() << "Flushing" << widget << rgn << offset;
+
+// d->image->image.save("flush.png");
+
+ // Get a context for the widget.
+#ifndef QT_MAC_USE_COCOA
+ CGContextRef context;
+ CGrafPtr port = GetWindowPort(qt_mac_window_for(widget));
+ QDBeginCGContext(port, &context);
+#else
+ extern CGContextRef qt_mac_graphicsContextFor(QWidget *);
+ CGContextRef context = qt_mac_graphicsContextFor(widget);
+#endif
+ CGContextSaveGState(context);
+
+ // Flip context.
+ CGContextTranslateCTM(context, 0, widget->height());
+ CGContextScaleCTM(context, 1, -1);
+
+ // Clip to region.
+ const QVector<QRect> &rects = rgn.rects();
+ for (int i = 0; i < rects.size(); ++i) {
+ const QRect &rect = rects.at(i);
+ CGContextAddRect(context, CGRectMake(rect.x(), rect.y(), rect.width(), rect.height()));
+ }
+ CGContextClip(context);
+
+ QRect r = rgn.boundingRect();
+ const CGRect area = CGRectMake(r.x(), r.y(), r.width(), r.height());
+ CGImageRef image = CGBitmapContextCreateImage(d->image->cg);
+ CGImageRef subImage = CGImageCreateWithImageInRect(image, area);
+
+ qt_mac_drawCGImage(context, &area, subImage);
+ CGImageRelease(subImage);
+ CGImageRelease(image);
+
+// CGSize size = { d->image->image.width(), d->image->image.height() };
+// CGLayerRef layer = CGLayerCreateWithContext(d->image->cg, size, 0);
+// CGPoint pt = { 0, 0 };
+// CGContextDrawLayerAtPoint(context, pt, layer);
+// CGLayerRelease(layer);
+
+ // Restore context.
+ CGContextRestoreGState(context);
+#ifndef QT_MAC_USE_COCOA
+ QDEndCGContext(port, &context);
+#else
+ CGContextFlush(context);
+#endif
+#endif
+}
+
+void QRasterWindowSurface::setGeometry(const QRect &rect)
+{
+ QWindowSurface::setGeometry(rect);
+ Q_D(QRasterWindowSurface);
+ d->inSetGeometry = true;
+ if (d->image == 0 || d->image->width() < rect.width() || d->image->height() < rect.height()) {
+#if (defined(Q_WS_X11) && !defined(QT_NO_XRENDER)) || (defined(Q_WS_WIN) && !defined(Q_OS_WINCE))
+#ifndef Q_WS_WIN
+ if (d_ptr->translucentBackground)
+#else
+ if (!qt_widget_private(window())->isOpaque && d->canUseLayeredWindow)
+#endif
+ prepareBuffer(QImage::Format_ARGB32_Premultiplied, window());
+ else
+#endif
+ prepareBuffer(QNativeImage::systemFormat(), window());
+ }
+ d->inSetGeometry = false;
+}
+
+// from qwindowsurface.cpp
+extern void qt_scrollRectInImage(QImage &img, const QRect &rect, const QPoint &offset);
+
+bool QRasterWindowSurface::scroll(const QRegion &area, int dx, int dy)
+{
+#ifdef Q_WS_WIN
+ Q_D(QRasterWindowSurface);
+
+ if (!d->image || !d->image->hdc)
+ return false;
+
+ QRect rect = area.boundingRect();
+ BitBlt(d->image->hdc, rect.x()+dx, rect.y()+dy, rect.width(), rect.height(),
+ d->image->hdc, rect.x(), rect.y(), SRCCOPY);
+
+ return true;
+#else
+ Q_D(QRasterWindowSurface);
+
+ if (!d->image || d->image->image.isNull())
+ return false;
+
+ const QVector<QRect> rects = area.rects();
+ for (int i = 0; i < rects.size(); ++i)
+ qt_scrollRectInImage(d->image->image, rects.at(i), QPoint(dx, dy));
+
+ return true;
+#endif
+}
+
+
+void QRasterWindowSurface::prepareBuffer(QImage::Format format, QWidget *widget)
+{
+ Q_D(QRasterWindowSurface);
+
+ int width = window()->width();
+ int height = window()->height();
+ if (d->image) {
+ width = qMax(d->image->width(), width);
+ height = qMax(d->image->height(), height);
+ }
+
+ if (width == 0 || height == 0) {
+ delete d->image;
+ d->image = 0;
+ return;
+ }
+
+ QNativeImage *oldImage = d->image;
+
+ d->image = new QNativeImage(width, height, format, false, widget);
+
+ if (oldImage && d->inSetGeometry && hasStaticContents()) {
+ // Make sure we use the const version of bits() (no detach).
+ const uchar *src = const_cast<const QImage &>(oldImage->image).bits();
+ uchar *dst = d->image->image.bits();
+
+ const int srcBytesPerLine = oldImage->image.bytesPerLine();
+ const int dstBytesPerLine = d->image->image.bytesPerLine();
+ const int bytesPerPixel = oldImage->image.depth() >> 3;
+
+ QRegion staticRegion(staticContents());
+ // Make sure we're inside the boundaries of the old image.
+ staticRegion &= QRect(0, 0, oldImage->image.width(), oldImage->image.height());
+ const QVector<QRect> &rects = staticRegion.rects();
+ const QRect *srcRect = rects.constData();
+
+ // Copy the static content of the old image into the new one.
+ int numRectsLeft = rects.size();
+ do {
+ const int bytesOffset = srcRect->x() * bytesPerPixel;
+ const int dy = srcRect->y();
+
+ // Adjust src and dst to point to the right offset.
+ const uchar *s = src + dy * srcBytesPerLine + bytesOffset;
+ uchar *d = dst + dy * dstBytesPerLine + bytesOffset;
+ const int numBytes = srcRect->width() * bytesPerPixel;
+
+ int numScanLinesLeft = srcRect->height();
+ do {
+ ::memcpy(d, s, numBytes);
+ d += dstBytesPerLine;
+ s += srcBytesPerLine;
+ } while (--numScanLinesLeft);
+
+ ++srcRect;
+ } while (--numRectsLeft);
+ }
+
+ delete oldImage;
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/painting/qwindowsurface_raster_p.h b/src/gui/painting/qwindowsurface_raster_p.h
new file mode 100644
index 0000000000..2a3535fc3f
--- /dev/null
+++ b/src/gui/painting/qwindowsurface_raster_p.h
@@ -0,0 +1,120 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QWINDOWSURFACE_RASTER_P_H
+#define QWINDOWSURFACE_RASTER_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of the QLibrary class. This header file may change from
+// version to version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <qglobal.h>
+#include "private/qwindowsurface_p.h"
+
+QT_BEGIN_NAMESPACE
+
+#ifdef Q_WS_WIN
+#define Q_WS_EX_LAYERED 0x00080000 // copied from WS_EX_LAYERED in winuser.h
+#define Q_LWA_ALPHA 0x00000002 // copied from LWA_ALPHA in winuser.h
+#define Q_ULW_ALPHA 0x00000002 // copied from ULW_ALPHA in winuser.h
+#define Q_AC_SRC_ALPHA 0x00000001 // copied from AC_SRC_ALPHA in winuser.h
+
+struct Q_BLENDFUNCTION {
+ BYTE BlendOp;
+ BYTE BlendFlags;
+ BYTE SourceConstantAlpha;
+ BYTE AlphaFormat;
+};
+
+struct Q_UPDATELAYEREDWINDOWINFO {
+ DWORD cbSize;
+ HDC hdcDst;
+ const POINT *pptDst;
+ const SIZE *psize;
+ HDC hdcSrc;
+ const POINT *pptSrc;
+ COLORREF crKey;
+ const Q_BLENDFUNCTION *pblend;
+ DWORD dwFlags;
+ const RECT *prcDirty;
+};
+
+typedef BOOL (WINAPI *PtrUpdateLayeredWindowIndirect)(HWND hwnd, const Q_UPDATELAYEREDWINDOWINFO *pULWInfo);
+extern PtrUpdateLayeredWindowIndirect ptrUpdateLayeredWindowIndirect;
+#endif
+
+class QPaintDevice;
+class QPoint;
+class QRegion;
+class QRegion;
+class QSize;
+class QWidget;
+class QRasterWindowSurfacePrivate;
+class QNativeImage;
+
+class Q_GUI_EXPORT QRasterWindowSurface : public QWindowSurface
+{
+public:
+ QRasterWindowSurface(QWidget *widget);
+ ~QRasterWindowSurface();
+
+ QPaintDevice *paintDevice();
+ void flush(QWidget *widget, const QRegion &region, const QPoint &offset);
+ void beginPaint(const QRegion &rgn);
+ void setGeometry(const QRect &rect);
+ bool scroll(const QRegion &area, int dx, int dy);
+
+private:
+ void prepareBuffer(QImage::Format format, QWidget *widget);
+ Q_DECLARE_PRIVATE(QRasterWindowSurface)
+ QRasterWindowSurfacePrivate *d_ptr;
+};
+
+QT_END_NAMESPACE
+
+#endif // QWINDOWSURFACE_RASTER_P_H
diff --git a/src/gui/painting/qwindowsurface_x11.cpp b/src/gui/painting/qwindowsurface_x11.cpp
new file mode 100644
index 0000000000..9e8b498bb3
--- /dev/null
+++ b/src/gui/painting/qwindowsurface_x11.cpp
@@ -0,0 +1,244 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtGui/QPaintDevice>
+#include <QtGui/QPainter>
+#include <QtGui/QPixmap>
+#include <QtGui/QWidget>
+
+#include "private/qt_x11_p.h"
+#include "private/qpixmap_x11_p.h"
+#include "private/qwidget_p.h"
+#include "qx11info_x11.h"
+#include "qwindowsurface_x11_p.h"
+
+QT_BEGIN_NAMESPACE
+
+extern void *qt_getClipRects(const QRegion &r, int &num); // in qpaintengine_x11.cpp
+
+struct QX11WindowSurfacePrivate
+{
+ QWidget *widget;
+ QPixmap device;
+#ifndef QT_NO_XRENDER
+ bool translucentBackground;
+#endif
+};
+
+QX11WindowSurface::QX11WindowSurface(QWidget *widget)
+ : QWindowSurface(widget), d_ptr(new QX11WindowSurfacePrivate), gc(0)
+{
+ d_ptr->widget = widget;
+#ifndef QT_NO_XRENDER
+ d_ptr->translucentBackground = X11->use_xrender
+ && widget->x11Info().depth() == 32;
+ setStaticContentsSupport(!d_ptr->translucentBackground);
+#else
+ setStaticContentsSupport(true);
+#endif
+}
+
+
+QX11WindowSurface::~QX11WindowSurface()
+{
+ delete d_ptr;
+ if (gc) {
+ XFreeGC(X11->display, gc);
+ gc = 0;
+ }
+}
+
+QPaintDevice *QX11WindowSurface::paintDevice()
+{
+ return &d_ptr->device;
+}
+
+void QX11WindowSurface::beginPaint(const QRegion &rgn)
+{
+#ifndef QT_NO_XRENDER
+ if (d_ptr->translucentBackground) {
+ if (d_ptr->device.depth() != 32)
+ static_cast<QX11PixmapData *>(d_ptr->device.data_ptr())->convertToARGB32();
+ ::Picture src = X11->getSolidFill(d_ptr->device.x11Info().screen(), Qt::transparent);
+ ::Picture dst = d_ptr->device.x11PictureHandle();
+ const QVector<QRect> rects = rgn.rects();
+ const int w = d_ptr->device.width();
+ const int h = d_ptr->device.height();
+ for (QVector<QRect>::const_iterator it = rects.begin(); it != rects.end(); ++it)
+ XRenderComposite(X11->display, PictOpSrc, src, 0, dst,
+ 0, 0, w, h, it->x(), it->y(),
+ it->width(), it->height());
+ }
+#endif
+}
+
+void QX11WindowSurface::flush(QWidget *widget, const QRegion &rgn, const QPoint &offset)
+{
+ if (d_ptr->device.isNull())
+ return;
+
+ QPoint wOffset = qt_qwidget_data(widget)->wrect.topLeft();
+ QRegion wrgn(rgn);
+ QRect br = rgn.boundingRect();
+ if (!wOffset.isNull())
+ wrgn.translate(-wOffset);
+ QRect wbr = wrgn.boundingRect();
+
+ int num;
+ XRectangle *rects = (XRectangle *)qt_getClipRects(wrgn, num);
+ if (num <= 0)
+ return;
+// qDebug() << "XSetClipRectangles";
+// for (int i = 0; i < num; ++i)
+// qDebug() << " " << i << rects[i].x << rects[i].x << rects[i].y << rects[i].width << rects[i].height;
+ XSetClipRectangles(X11->display, gc, 0, 0, rects, num, YXBanded);
+ XCopyArea(X11->display, d_ptr->device.handle(), widget->handle(), gc,
+ br.x() + offset.x(), br.y() + offset.y(), br.width(), br.height(), wbr.x(), wbr.y());
+}
+
+void QX11WindowSurface::setGeometry(const QRect &rect)
+{
+ QWindowSurface::setGeometry(rect);
+
+ const QSize size = rect.size();
+ if (d_ptr->device.size() == size)
+ return;
+#ifndef QT_NO_XRENDER
+ if (d_ptr->translucentBackground) {
+ QX11PixmapData *data = new QX11PixmapData(QPixmapData::PixmapType);
+ data->xinfo = d_ptr->widget->x11Info();
+ data->resize(size.width(), size.height());
+ d_ptr->device = QPixmap(data);
+ } else
+#endif
+ {
+ QPixmap::x11SetDefaultScreen(d_ptr->widget->x11Info().screen());
+
+ QX11PixmapData *oldData = static_cast<QX11PixmapData *>(d_ptr->device.pixmapData());
+ Q_ASSERT(oldData);
+ if (!oldData->uninit && hasStaticContents()) {
+ // Copy the content of the old pixmap into the new one.
+ QX11PixmapData *newData = new QX11PixmapData(QPixmapData::PixmapType);
+ newData->resize(size.width(), size.height());
+ Q_ASSERT(oldData->d == newData->d);
+
+ QRegion staticRegion(staticContents());
+ // Make sure we're inside the boundaries of the old pixmap.
+ staticRegion &= QRect(0, 0, oldData->w, oldData->h);
+ const QRect boundingRect(staticRegion.boundingRect());
+ const int dx = boundingRect.x();
+ const int dy = boundingRect.y();
+
+ int num;
+ XRectangle *rects = (XRectangle *)qt_getClipRects(staticRegion, num);
+ GC tmpGc = XCreateGC(X11->display, oldData->hd, 0, 0);
+ XSetClipRectangles(X11->display, tmpGc, 0, 0, rects, num, YXBanded);
+ XCopyArea(X11->display, oldData->hd, newData->hd, tmpGc,
+ dx, dy, qMin(boundingRect.width(), size.width()),
+ qMin(boundingRect.height(), size.height()), dx, dy);
+ XFreeGC(X11->display, tmpGc);
+ newData->uninit = false;
+
+ d_ptr->device = QPixmap(newData);
+ } else {
+ d_ptr->device = QPixmap(size);
+ }
+ }
+
+ if (gc)
+ XFreeGC(X11->display, gc);
+ gc = XCreateGC(X11->display, d_ptr->device.handle(), 0, 0);
+ XSetGraphicsExposures(X11->display, gc, False);
+}
+
+bool QX11WindowSurface::scroll(const QRegion &area, int dx, int dy)
+{
+ QRect rect = area.boundingRect();
+
+ if (d_ptr->device.isNull())
+ return false;
+
+ GC gc = XCreateGC(X11->display, d_ptr->device.handle(), 0, 0);
+ XCopyArea(X11->display, d_ptr->device.handle(), d_ptr->device.handle(), gc,
+ rect.x(), rect.y(), rect.width(), rect.height(),
+ rect.x()+dx, rect.y()+dy);
+ XFreeGC(X11->display, gc);
+
+ return true;
+}
+
+QPixmap QX11WindowSurface::grabWidget(const QWidget *widget,
+ const QRect& rect) const
+{
+ if (!widget || d_ptr->device.isNull())
+ return QPixmap();
+
+ QRect srcRect;
+
+ // make sure the rect is inside the widget & clip to widget's rect
+ if (!rect.isEmpty())
+ srcRect = rect & widget->rect();
+ else
+ srcRect = widget->rect();
+
+ if (srcRect.isEmpty())
+ return QPixmap();
+
+ // If it's a child widget we have to translate the coordinates
+ if (widget != window())
+ srcRect.translate(widget->mapTo(window(), QPoint(0, 0)));
+
+ QPixmap::x11SetDefaultScreen(widget->x11Info().screen());
+ QPixmap px(srcRect.width(), srcRect.height());
+
+ GC tmpGc = XCreateGC(X11->display, d_ptr->device.handle(), 0, 0);
+
+ // Copy srcRect from the backing store to the new pixmap
+ XSetGraphicsExposures(X11->display, tmpGc, False);
+ XCopyArea(X11->display, d_ptr->device.handle(), px.handle(), tmpGc,
+ srcRect.x(), srcRect.y(), srcRect.width(), srcRect.height(), 0, 0);
+
+ XFreeGC(X11->display, tmpGc);
+
+ return px;
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/painting/qwindowsurface_x11_p.h b/src/gui/painting/qwindowsurface_x11_p.h
new file mode 100644
index 0000000000..4017c5b7d0
--- /dev/null
+++ b/src/gui/painting/qwindowsurface_x11_p.h
@@ -0,0 +1,90 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QWINDOWSURFACE_X11_P_H
+#define QWINDOWSURFACE_X11_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 <qglobal.h>
+#include "private/qwindowsurface_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class QPaintDevice;
+class QPoint;
+class QRegion;
+class QRegion;
+class QSize;
+class QWidget;
+struct QX11WindowSurfacePrivate;
+
+class QX11WindowSurface : public QWindowSurface
+{
+public:
+ QX11WindowSurface(QWidget *widget);
+ ~QX11WindowSurface();
+
+ QPaintDevice *paintDevice();
+ void flush(QWidget *widget, const QRegion &region, const QPoint &offset);
+
+ void beginPaint(const QRegion &rgn);
+ void setGeometry(const QRect &rect);
+ bool scroll(const QRegion &area, int dx, int dy);
+ QPixmap grabWidget(const QWidget *widget,
+ const QRect& rectangle = QRect()) const;
+private:
+ QX11WindowSurfacePrivate *d_ptr;
+ GC gc;
+};
+
+QT_END_NAMESPACE
+
+#endif // QWINDOWSURFACE_X11_P_H
diff --git a/src/gui/painting/qwmatrix.h b/src/gui/painting/qwmatrix.h
new file mode 100644
index 0000000000..ad8ec5d4b1
--- /dev/null
+++ b/src/gui/painting/qwmatrix.h
@@ -0,0 +1,61 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QWMATRIX_H
+#define QWMATRIX_H
+
+#include <QtGui/qmatrix.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+#if defined(QT3_SUPPORT)
+typedef QMatrix QWMatrix;
+#endif
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QWMATRIX_H
diff --git a/src/gui/styles/gtksymbols.cpp b/src/gui/styles/gtksymbols.cpp
new file mode 100644
index 0000000000..3fbf233384
--- /dev/null
+++ b/src/gui/styles/gtksymbols.cpp
@@ -0,0 +1,904 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "gtksymbols_p.h"
+
+// This file is responsible for resolving all GTK functions we use
+// dynamically. This is done to avoid link-time dependancy on GTK
+// as well as crashes occurring due to usage of the GTK_QT engines
+//
+// Additionally we create a map of common GTK widgets that we can pass
+// to the GTK theme engine as many engines resort to querying the
+// actual widget pointers for details that are not covered by the
+// state flags
+
+#include <QtCore/qglobal.h>
+#if !defined(QT_NO_STYLE_GTK)
+
+#include <QtCore/QEvent>
+#include <QtCore/QFile>
+#include <QtCore/QStringList>
+#include <QtCore/QTextStream>
+#include <QtCore/QHash>
+
+#include <private/qapplication_p.h>
+
+#include <QtGui/QMenu>
+#include <QtGui/QStyle>
+#include <QtGui/QApplication>
+#include <QtGui/QPixmapCache>
+#include <QtGui/QStatusBar>
+#include <QtGui/QMenuBar>
+#include <QtGui/QToolBar>
+#include <QtGui/QX11Info>
+
+#include <X11/Xlib.h>
+
+QT_BEGIN_NAMESPACE
+
+static bool displayDepth = -1;
+
+typedef QHash<QString, GtkWidget*> WidgetMap;
+Q_GLOBAL_STATIC(WidgetMap, gtkWidgetMap)
+
+Ptr_gtk_container_forall QGtk::gtk_container_forall = 0;
+Ptr_gtk_init QGtk::gtk_init = 0;
+Ptr_gtk_style_attach QGtk::gtk_style_attach = 0;
+Ptr_gtk_window_new QGtk::gtk_window_new = 0;
+Ptr_gtk_widget_destroy QGtk::gtk_widget_destroy = 0;
+Ptr_gtk_widget_realize QGtk::gtk_widget_realize = 0;
+Ptr_gtk_widget_set_default_direction QGtk::gtk_widget_set_default_direction = 0;
+Ptr_gtk_widget_modify_color QGtk::gtk_widget_modify_fg = 0;
+Ptr_gtk_widget_modify_color QGtk::gtk_widget_modify_bg = 0;
+Ptr_gtk_arrow_new QGtk::gtk_arrow_new = 0;
+Ptr_gtk_menu_item_new QGtk::gtk_menu_item_new = 0;
+Ptr_gtk_check_menu_item_new QGtk::gtk_check_menu_item_new = 0;
+Ptr_gtk_menu_bar_new QGtk::gtk_menu_bar_new = 0;
+Ptr_gtk_menu_new QGtk::gtk_menu_new = 0;
+Ptr_gtk_button_new QGtk::gtk_button_new = 0;
+Ptr_gtk_hbutton_box_new QGtk::gtk_hbutton_box_new = 0;
+Ptr_gtk_check_button_new QGtk::gtk_check_button_new = 0;
+Ptr_gtk_radio_button_new QGtk::gtk_radio_button_new = 0;
+Ptr_gtk_spin_button_new QGtk::gtk_spin_button_new = 0;
+Ptr_gtk_frame_new QGtk::gtk_frame_new = 0;
+Ptr_gtk_expander_new QGtk::gtk_expander_new = 0;
+Ptr_gtk_statusbar_new QGtk::gtk_statusbar_new = 0;
+Ptr_gtk_entry_new QGtk::gtk_entry_new = 0;
+Ptr_gtk_hscale_new QGtk::gtk_hscale_new = 0;
+Ptr_gtk_vscale_new QGtk::gtk_vscale_new = 0;
+Ptr_gtk_hscrollbar_new QGtk::gtk_hscrollbar_new = 0;
+Ptr_gtk_vscrollbar_new QGtk::gtk_vscrollbar_new = 0;
+Ptr_gtk_scrolled_window_new QGtk::gtk_scrolled_window_new = 0;
+Ptr_gtk_notebook_new QGtk::gtk_notebook_new = 0;
+Ptr_gtk_toolbar_new QGtk::gtk_toolbar_new = 0;
+Ptr_gtk_toolbar_insert QGtk::gtk_toolbar_insert = 0;
+Ptr_gtk_separator_tool_item_new QGtk::gtk_separator_tool_item_new = 0;
+Ptr_gtk_tree_view_new QGtk::gtk_tree_view_new = 0;
+Ptr_gtk_combo_box_new QGtk::gtk_combo_box_new = 0;
+Ptr_gtk_combo_box_entry_new QGtk::gtk_combo_box_entry_new = 0;
+Ptr_gtk_progress_bar_new QGtk::gtk_progress_bar_new = 0;
+Ptr_gtk_container_add QGtk::gtk_container_add = 0;
+Ptr_gtk_menu_shell_append QGtk::gtk_menu_shell_append = 0;
+Ptr_gtk_progress_set_adjustment QGtk::gtk_progress_set_adjustment = 0;
+Ptr_gtk_range_set_adjustment QGtk::gtk_range_set_adjustment = 0;
+Ptr_gtk_range_set_inverted QGtk::gtk_range_set_inverted = 0;
+Ptr_gtk_icon_factory_lookup_default QGtk::gtk_icon_factory_lookup_default = 0;
+Ptr_gtk_widget_style_get QGtk::gtk_widget_style_get = 0;
+Ptr_gtk_icon_set_render_icon QGtk::gtk_icon_set_render_icon = 0;
+Ptr_gtk_fixed_new QGtk::gtk_fixed_new = 0;
+Ptr_gtk_tree_view_column_new QGtk::gtk_tree_view_column_new = 0;
+Ptr_gtk_tree_view_get_column QGtk::gtk_tree_view_get_column = 0;
+Ptr_gtk_tree_view_append_column QGtk::gtk_tree_view_append_column = 0;
+Ptr_gtk_paint_check QGtk::gtk_paint_check = 0;
+Ptr_gtk_paint_box QGtk::gtk_paint_box = 0;
+Ptr_gtk_paint_box_gap QGtk::gtk_paint_box_gap = 0;
+Ptr_gtk_paint_flat_box QGtk::gtk_paint_flat_box = 0;
+Ptr_gtk_paint_option QGtk::gtk_paint_option = 0;
+Ptr_gtk_paint_extension QGtk::gtk_paint_extension = 0;
+Ptr_gtk_paint_slider QGtk::gtk_paint_slider = 0;
+Ptr_gtk_paint_shadow QGtk::gtk_paint_shadow = 0;
+Ptr_gtk_paint_resize_grip QGtk::gtk_paint_resize_grip = 0;
+Ptr_gtk_paint_focus QGtk::gtk_paint_focus = 0;
+Ptr_gtk_paint_arrow QGtk::gtk_paint_arrow = 0;
+Ptr_gtk_paint_handle QGtk::gtk_paint_handle = 0;
+Ptr_gtk_paint_expander QGtk::gtk_paint_expander = 0;
+Ptr_gtk_adjustment_new QGtk::gtk_adjustment_new = 0;
+Ptr_gtk_paint_hline QGtk::gtk_paint_hline = 0;
+Ptr_gtk_paint_vline QGtk::gtk_paint_vline = 0;
+Ptr_gtk_menu_item_set_submenu QGtk::gtk_menu_item_set_submenu = 0;
+Ptr_gtk_settings_get_default QGtk::gtk_settings_get_default = 0;
+Ptr_gtk_separator_menu_item_new QGtk::gtk_separator_menu_item_new = 0;
+Ptr_gtk_widget_size_allocate QGtk::gtk_widget_size_allocate = 0;
+Ptr_gtk_widget_set_direction QGtk::gtk_widget_set_direction = 0;
+Ptr_gtk_widget_path QGtk::gtk_widget_path = 0;
+Ptr_gtk_container_get_type QGtk::gtk_container_get_type = 0;
+Ptr_gtk_window_get_type QGtk::gtk_window_get_type = 0;
+Ptr_gtk_widget_get_type QGtk::gtk_widget_get_type = 0;
+Ptr_gtk_rc_get_style_by_paths QGtk::gtk_rc_get_style_by_paths = 0;
+Ptr_gtk_check_version QGtk::gtk_check_version = 0;
+
+Ptr_pango_font_description_get_size QGtk::pango_font_description_get_size = 0;
+Ptr_pango_font_description_get_weight QGtk::pango_font_description_get_weight = 0;
+Ptr_pango_font_description_get_family QGtk::pango_font_description_get_family = 0;
+Ptr_pango_font_description_get_style QGtk::pango_font_description_get_style = 0;
+
+Ptr_gtk_file_filter_new QGtk::gtk_file_filter_new = 0;
+Ptr_gtk_file_filter_set_name QGtk::gtk_file_filter_set_name = 0;
+Ptr_gtk_file_filter_add_pattern QGtk::gtk_file_filter_add_pattern = 0;
+Ptr_gtk_file_chooser_add_filter QGtk::gtk_file_chooser_add_filter = 0;
+Ptr_gtk_file_chooser_set_filter QGtk::gtk_file_chooser_set_filter = 0;
+Ptr_gtk_file_chooser_dialog_new QGtk::gtk_file_chooser_dialog_new = 0;
+Ptr_gtk_file_chooser_set_current_folder QGtk::gtk_file_chooser_set_current_folder = 0;
+Ptr_gtk_file_chooser_get_filename QGtk::gtk_file_chooser_get_filename = 0;
+Ptr_gtk_file_chooser_get_filenames QGtk::gtk_file_chooser_get_filenames = 0;
+Ptr_gtk_file_chooser_set_current_name QGtk::gtk_file_chooser_set_current_name = 0;
+Ptr_gtk_dialog_run QGtk::gtk_dialog_run = 0;
+Ptr_gtk_file_chooser_set_filename QGtk::gtk_file_chooser_set_filename = 0;
+
+Ptr_gdk_pixbuf_get_pixels QGtk::gdk_pixbuf_get_pixels = 0;
+Ptr_gdk_pixbuf_get_width QGtk::gdk_pixbuf_get_width = 0;
+Ptr_gdk_pixbuf_get_height QGtk::gdk_pixbuf_get_height = 0;
+Ptr_gdk_pixmap_new QGtk::gdk_pixmap_new = 0;
+Ptr_gdk_pixbuf_new QGtk::gdk_pixbuf_new = 0;
+Ptr_gdk_pixbuf_get_from_drawable QGtk::gdk_pixbuf_get_from_drawable = 0;
+Ptr_gdk_draw_rectangle QGtk::gdk_draw_rectangle = 0;
+Ptr_gdk_pixbuf_unref QGtk::gdk_pixbuf_unref = 0;
+Ptr_gdk_drawable_unref QGtk::gdk_drawable_unref = 0;
+Ptr_gdk_drawable_get_depth QGtk::gdk_drawable_get_depth = 0;
+Ptr_gdk_color_free QGtk::gdk_color_free = 0;
+Ptr_gdk_x11_window_set_user_time QGtk::gdk_x11_window_set_user_time = 0;
+Ptr_gdk_x11_drawable_get_xid QGtk::gdk_x11_drawable_get_xid = 0;
+Ptr_gdk_x11_drawable_get_xdisplay QGtk::gdk_x11_drawable_get_xdisplay = 0;
+
+Ptr_gconf_client_get_default QGtk::gconf_client_get_default = 0;
+Ptr_gconf_client_get_string QGtk::gconf_client_get_string = 0;
+
+static QString classPath(GtkWidget *widget)
+{
+ char* class_path;
+ QGtk::gtk_widget_path (widget, NULL, &class_path, NULL);
+ QString path = QLS(class_path);
+ g_free(class_path);
+
+ // Remove the prefixes
+ path.remove(QLS("GtkWindow."));
+ path.remove(QLS("GtkFixed."));
+ return path;
+}
+
+static void resolveGtk()
+{
+ const QString GTK_PATH(QLS("gtk-x11-2.0"));
+ QGtk::gtk_init = (Ptr_gtk_init)QLibrary::resolve(GTK_PATH, 0, "gtk_init");
+ QGtk::gtk_window_new = (Ptr_gtk_window_new)QLibrary::resolve(GTK_PATH, 0, "gtk_window_new");
+ QGtk::gtk_style_attach = (Ptr_gtk_style_attach)QLibrary::resolve(GTK_PATH, 0, "gtk_style_attach");
+ QGtk::gtk_widget_destroy = (Ptr_gtk_widget_destroy)QLibrary::resolve(GTK_PATH, 0, "gtk_widget_destroy");
+ QGtk::gtk_widget_realize = (Ptr_gtk_widget_realize)QLibrary::resolve(GTK_PATH, 0, "gtk_widget_realize");
+
+ QGtk::gtk_file_chooser_set_current_folder = (Ptr_gtk_file_chooser_set_current_folder)QLibrary::resolve(GTK_PATH, 0, "gtk_file_chooser_set_current_folder");
+ QGtk::gtk_file_filter_new = (Ptr_gtk_file_filter_new)QLibrary::resolve(GTK_PATH, 0, "gtk_file_filter_new");
+ QGtk::gtk_file_filter_set_name = (Ptr_gtk_file_filter_set_name)QLibrary::resolve(GTK_PATH, 0, "gtk_file_filter_set_name");
+ QGtk::gtk_file_filter_add_pattern = (Ptr_gtk_file_filter_add_pattern)QLibrary::resolve(GTK_PATH, 0, "gtk_file_filter_add_pattern");
+ QGtk::gtk_file_chooser_add_filter = (Ptr_gtk_file_chooser_add_filter)QLibrary::resolve(GTK_PATH, 0, "gtk_file_chooser_add_filter");
+ QGtk::gtk_file_chooser_set_filter = (Ptr_gtk_file_chooser_set_filter)QLibrary::resolve(GTK_PATH, 0, "gtk_file_chooser_set_filter");
+ QGtk::gtk_file_chooser_dialog_new = (Ptr_gtk_file_chooser_dialog_new)QLibrary::resolve(GTK_PATH, 0, "gtk_file_chooser_dialog_new");
+ QGtk::gtk_file_chooser_set_current_folder = (Ptr_gtk_file_chooser_set_current_folder)QLibrary::resolve(GTK_PATH, 0, "gtk_file_chooser_set_current_folder");
+ QGtk::gtk_file_chooser_get_filename = (Ptr_gtk_file_chooser_get_filename)QLibrary::resolve(GTK_PATH, 0, "gtk_file_chooser_get_filename");
+ QGtk::gtk_file_chooser_get_filenames = (Ptr_gtk_file_chooser_get_filenames)QLibrary::resolve(GTK_PATH, 0, "gtk_file_chooser_get_filenames");
+ QGtk::gtk_file_chooser_set_current_name = (Ptr_gtk_file_chooser_set_current_name)QLibrary::resolve(GTK_PATH, 0, "gtk_file_chooser_set_current_name");
+ QGtk::gtk_dialog_run = (Ptr_gtk_dialog_run)QLibrary::resolve(GTK_PATH, 0, "gtk_dialog_run");
+ QGtk::gtk_file_chooser_set_filename = (Ptr_gtk_file_chooser_set_filename)QLibrary::resolve(GTK_PATH, 0, "gtk_file_chooser_set_filename");
+
+ QGtk::gdk_pixbuf_get_pixels = (Ptr_gdk_pixbuf_get_pixels)QLibrary::resolve(GTK_PATH, 0, "gdk_pixbuf_get_pixels");
+ QGtk::gdk_pixbuf_get_width = (Ptr_gdk_pixbuf_get_width)QLibrary::resolve(GTK_PATH, 0, "gdk_pixbuf_get_width");
+ QGtk::gdk_pixbuf_get_height = (Ptr_gdk_pixbuf_get_height)QLibrary::resolve(GTK_PATH, 0, "gdk_pixbuf_get_height");
+ QGtk::gdk_pixmap_new = (Ptr_gdk_pixmap_new)QLibrary::resolve(GTK_PATH, 0, "gdk_pixmap_new");
+ QGtk::gdk_pixbuf_new = (Ptr_gdk_pixbuf_new)QLibrary::resolve(GTK_PATH, 0, "gdk_pixbuf_new");
+ QGtk::gdk_pixbuf_get_from_drawable = (Ptr_gdk_pixbuf_get_from_drawable)QLibrary::resolve(GTK_PATH, 0, "gdk_pixbuf_get_from_drawable");
+ QGtk::gdk_draw_rectangle = (Ptr_gdk_draw_rectangle)QLibrary::resolve(GTK_PATH, 0, "gdk_draw_rectangle");
+ QGtk::gdk_pixbuf_unref = (Ptr_gdk_pixbuf_unref)QLibrary::resolve(GTK_PATH, 0, "gdk_pixbuf_unref");
+ QGtk::gdk_drawable_unref = (Ptr_gdk_drawable_unref)QLibrary::resolve(GTK_PATH, 0, "gdk_drawable_unref");
+ QGtk::gdk_drawable_get_depth = (Ptr_gdk_drawable_get_depth)QLibrary::resolve(GTK_PATH, 0, "gdk_drawable_get_depth");
+ QGtk::gdk_color_free = (Ptr_gdk_color_free)QLibrary::resolve(GTK_PATH, 0, "gdk_color_free");
+ QGtk::gdk_x11_window_set_user_time = (Ptr_gdk_x11_window_set_user_time)QLibrary::resolve(GTK_PATH, 0, "gdk_x11_window_set_user_time");
+ QGtk::gdk_x11_drawable_get_xid = (Ptr_gdk_x11_drawable_get_xid)QLibrary::resolve(GTK_PATH, 0, "gdk_x11_drawable_get_xid");
+ QGtk::gdk_x11_drawable_get_xdisplay = (Ptr_gdk_x11_drawable_get_xdisplay)QLibrary::resolve(GTK_PATH, 0, "gdk_x11_drawable_get_xdisplay");
+
+ QGtk::gtk_widget_set_default_direction = (Ptr_gtk_widget_set_default_direction)QLibrary::resolve(GTK_PATH, 0, "gtk_widget_set_default_direction");
+ QGtk::gtk_widget_modify_fg = (Ptr_gtk_widget_modify_color)QLibrary::resolve(GTK_PATH, 0, "gtk_widget_modify_fg");
+ QGtk::gtk_widget_modify_bg = (Ptr_gtk_widget_modify_color)QLibrary::resolve(GTK_PATH, 0, "gtk_widget_modify_bg");
+ QGtk::gtk_arrow_new = (Ptr_gtk_arrow_new)QLibrary::resolve(GTK_PATH, 0, "gtk_arrow_new");
+ QGtk::gtk_menu_item_new = (Ptr_gtk_menu_item_new)QLibrary::resolve(GTK_PATH, 0, "gtk_menu_item_new");
+ QGtk::gtk_check_menu_item_new = (Ptr_gtk_check_menu_item_new)QLibrary::resolve(GTK_PATH, 0, "gtk_check_menu_item_new");
+ QGtk::gtk_menu_bar_new = (Ptr_gtk_menu_bar_new)QLibrary::resolve(GTK_PATH, 0, "gtk_menu_bar_new");
+ QGtk::gtk_menu_new = (Ptr_gtk_menu_new)QLibrary::resolve(GTK_PATH, 0, "gtk_menu_new");
+ QGtk::gtk_toolbar_new = (Ptr_gtk_toolbar_new)QLibrary::resolve(GTK_PATH, 0, "gtk_toolbar_new");
+ QGtk::gtk_separator_tool_item_new = (Ptr_gtk_separator_tool_item_new)QLibrary::resolve(GTK_PATH, 0, "gtk_separator_tool_item_new");
+ QGtk::gtk_toolbar_insert = (Ptr_gtk_toolbar_insert)QLibrary::resolve(GTK_PATH, 0, "gtk_toolbar_insert");
+ QGtk::gtk_button_new = (Ptr_gtk_button_new)QLibrary::resolve(GTK_PATH, 0, "gtk_button_new");
+ QGtk::gtk_hbutton_box_new = (Ptr_gtk_hbutton_box_new)QLibrary::resolve(GTK_PATH, 0, "gtk_hbutton_box_new");
+ QGtk::gtk_check_button_new = (Ptr_gtk_check_button_new)QLibrary::resolve(GTK_PATH, 0, "gtk_check_button_new");
+ QGtk::gtk_radio_button_new = (Ptr_gtk_radio_button_new)QLibrary::resolve(GTK_PATH, 0, "gtk_radio_button_new");
+ QGtk::gtk_notebook_new = (Ptr_gtk_notebook_new)QLibrary::resolve(GTK_PATH, 0, "gtk_notebook_new");
+ QGtk::gtk_progress_bar_new = (Ptr_gtk_progress_bar_new)QLibrary::resolve(GTK_PATH, 0, "gtk_progress_bar_new");
+ QGtk::gtk_spin_button_new = (Ptr_gtk_spin_button_new)QLibrary::resolve(GTK_PATH, 0, "gtk_spin_button_new");
+ QGtk::gtk_hscale_new = (Ptr_gtk_hscale_new)QLibrary::resolve(GTK_PATH, 0, "gtk_hscale_new");
+ QGtk::gtk_vscale_new = (Ptr_gtk_vscale_new)QLibrary::resolve(GTK_PATH, 0, "gtk_vscale_new");
+ QGtk::gtk_hscrollbar_new = (Ptr_gtk_hscrollbar_new)QLibrary::resolve(GTK_PATH, 0, "gtk_hscrollbar_new");
+ QGtk::gtk_vscrollbar_new = (Ptr_gtk_vscrollbar_new)QLibrary::resolve(GTK_PATH, 0, "gtk_vscrollbar_new");
+ QGtk::gtk_scrolled_window_new = (Ptr_gtk_scrolled_window_new)QLibrary::resolve(GTK_PATH, 0, "gtk_scrolled_window_new");
+ QGtk::gtk_menu_shell_append = (Ptr_gtk_menu_shell_append)QLibrary::resolve(GTK_PATH, 0, "gtk_menu_shell_append");
+ QGtk::gtk_entry_new = (Ptr_gtk_entry_new)QLibrary::resolve(GTK_PATH, 0, "gtk_entry_new");
+ QGtk::gtk_tree_view_new = (Ptr_gtk_tree_view_new)QLibrary::resolve(GTK_PATH, 0, "gtk_tree_view_new");
+ QGtk::gtk_combo_box_new = (Ptr_gtk_combo_box_new)QLibrary::resolve(GTK_PATH, 0, "gtk_combo_box_new");
+ QGtk::gtk_progress_set_adjustment = (Ptr_gtk_progress_set_adjustment)QLibrary::resolve(GTK_PATH, 0, "gtk_progress_set_adjustment");
+ QGtk::gtk_range_set_adjustment = (Ptr_gtk_range_set_adjustment)QLibrary::resolve(GTK_PATH, 0, "gtk_range_set_adjustment");
+ QGtk::gtk_range_set_inverted = (Ptr_gtk_range_set_inverted)QLibrary::resolve(GTK_PATH, 0, "gtk_range_set_inverted");
+ QGtk::gtk_container_add = (Ptr_gtk_container_add)QLibrary::resolve(GTK_PATH, 0, "gtk_container_add");
+ QGtk::gtk_icon_factory_lookup_default = (Ptr_gtk_icon_factory_lookup_default)QLibrary::resolve(GTK_PATH, 0, "gtk_icon_factory_lookup_default");
+ QGtk::gtk_widget_style_get = (Ptr_gtk_widget_style_get)QLibrary::resolve(GTK_PATH, 0, "gtk_widget_style_get");
+ QGtk::gtk_icon_set_render_icon = (Ptr_gtk_icon_set_render_icon)QLibrary::resolve(GTK_PATH, 0, "gtk_icon_set_render_icon");
+ QGtk::gtk_fixed_new = (Ptr_gtk_fixed_new)QLibrary::resolve(GTK_PATH, 0, "gtk_fixed_new");
+ QGtk::gtk_tree_view_column_new = (Ptr_gtk_tree_view_column_new)QLibrary::resolve(GTK_PATH, 0, "gtk_tree_view_column_new");
+ QGtk::gtk_tree_view_append_column= (Ptr_gtk_tree_view_append_column )QLibrary::resolve(GTK_PATH, 0, "gtk_tree_view_append_column");
+ QGtk::gtk_tree_view_get_column = (Ptr_gtk_tree_view_get_column )QLibrary::resolve(GTK_PATH, 0, "gtk_tree_view_get_column");
+ QGtk::gtk_paint_check = (Ptr_gtk_paint_check)QLibrary::resolve(GTK_PATH, 0, "gtk_paint_check");
+ QGtk::gtk_paint_box = (Ptr_gtk_paint_box)QLibrary::resolve(GTK_PATH, 0, "gtk_paint_box");
+ QGtk::gtk_paint_flat_box = (Ptr_gtk_paint_flat_box)QLibrary::resolve(GTK_PATH, 0, "gtk_paint_flat_box");
+ QGtk::gtk_paint_check = (Ptr_gtk_paint_check)QLibrary::resolve(GTK_PATH, 0, "gtk_paint_check");
+ QGtk::gtk_paint_box = (Ptr_gtk_paint_box)QLibrary::resolve(GTK_PATH, 0, "gtk_paint_box");
+ QGtk::gtk_paint_resize_grip = (Ptr_gtk_paint_resize_grip)QLibrary::resolve(GTK_PATH, 0, "gtk_paint_resize_grip");
+ QGtk::gtk_paint_focus = (Ptr_gtk_paint_focus)QLibrary::resolve(GTK_PATH, 0, "gtk_paint_focus");
+ QGtk::gtk_paint_shadow = (Ptr_gtk_paint_shadow)QLibrary::resolve(GTK_PATH, 0, "gtk_paint_shadow");
+ QGtk::gtk_paint_slider = (Ptr_gtk_paint_slider)QLibrary::resolve(GTK_PATH, 0, "gtk_paint_slider");
+ QGtk::gtk_paint_expander = (Ptr_gtk_paint_expander)QLibrary::resolve(GTK_PATH, 0, "gtk_paint_expander");
+ QGtk::gtk_paint_handle = (Ptr_gtk_paint_handle)QLibrary::resolve(GTK_PATH, 0, "gtk_paint_handle");
+ QGtk::gtk_paint_option = (Ptr_gtk_paint_option)QLibrary::resolve(GTK_PATH, 0, "gtk_paint_option");
+ QGtk::gtk_paint_arrow = (Ptr_gtk_paint_arrow)QLibrary::resolve(GTK_PATH, 0, "gtk_paint_arrow");
+ QGtk::gtk_paint_box_gap = (Ptr_gtk_paint_box_gap)QLibrary::resolve(GTK_PATH, 0, "gtk_paint_box_gap");
+ QGtk::gtk_paint_extension = (Ptr_gtk_paint_extension)QLibrary::resolve(GTK_PATH, 0, "gtk_paint_extension");
+ QGtk::gtk_paint_hline = (Ptr_gtk_paint_hline)QLibrary::resolve(GTK_PATH, 0, "gtk_paint_hline");
+ QGtk::gtk_paint_vline = (Ptr_gtk_paint_vline)QLibrary::resolve(GTK_PATH, 0, "gtk_paint_vline");
+ QGtk::gtk_adjustment_new = (Ptr_gtk_adjustment_new)QLibrary::resolve(GTK_PATH, 0, "gtk_adjustment_new");
+ QGtk::gtk_menu_item_set_submenu = (Ptr_gtk_menu_item_set_submenu)QLibrary::resolve(GTK_PATH, 0, "gtk_menu_item_set_submenu");
+ QGtk::gtk_settings_get_default = (Ptr_gtk_settings_get_default)QLibrary::resolve(GTK_PATH, 0, "gtk_settings_get_default");
+ QGtk::gtk_separator_menu_item_new = (Ptr_gtk_separator_menu_item_new)QLibrary::resolve(GTK_PATH, 0, "gtk_separator_menu_item_new");
+ QGtk::gtk_frame_new = (Ptr_gtk_frame_new)QLibrary::resolve(GTK_PATH, 0, "gtk_frame_new");
+ QGtk::gtk_expander_new = (Ptr_gtk_expander_new)QLibrary::resolve(GTK_PATH, 0, "gtk_expander_new");
+ QGtk::gtk_statusbar_new = (Ptr_gtk_statusbar_new)QLibrary::resolve(GTK_PATH, 0, "gtk_statusbar_new");
+ QGtk::gtk_combo_box_entry_new = (Ptr_gtk_combo_box_entry_new)QLibrary::resolve(GTK_PATH, 0, "gtk_combo_box_entry_new");
+ QGtk::gtk_container_forall = (Ptr_gtk_container_forall)QLibrary::resolve(GTK_PATH, 0, "gtk_container_forall");
+ QGtk::gtk_widget_size_allocate =(Ptr_gtk_widget_size_allocate)QLibrary::resolve(GTK_PATH, 0, "gtk_widget_size_allocate");
+ QGtk::gtk_widget_set_direction =(Ptr_gtk_widget_set_direction)QLibrary::resolve(GTK_PATH, 0, "gtk_widget_set_direction");
+ QGtk::gtk_widget_path =(Ptr_gtk_widget_path)QLibrary::resolve(GTK_PATH, 0, "gtk_widget_path");
+ QGtk::gtk_container_get_type =(Ptr_gtk_container_get_type)QLibrary::resolve(GTK_PATH, 0, "gtk_container_get_type");
+ QGtk::gtk_window_get_type =(Ptr_gtk_window_get_type)QLibrary::resolve(GTK_PATH, 0, "gtk_window_get_type");
+ QGtk::gtk_widget_get_type =(Ptr_gtk_widget_get_type)QLibrary::resolve(GTK_PATH, 0, "gtk_widget_get_type");
+ QGtk::gtk_rc_get_style_by_paths =(Ptr_gtk_rc_get_style_by_paths)QLibrary::resolve(GTK_PATH, 0, "gtk_rc_get_style_by_paths");
+ QGtk::gtk_check_version =(Ptr_gtk_check_version)QLibrary::resolve(GTK_PATH, 0, "gtk_check_version");
+ QGtk::pango_font_description_get_size = (Ptr_pango_font_description_get_size)QLibrary::resolve(GTK_PATH, 0, "pango_font_description_get_size");
+ QGtk::pango_font_description_get_weight = (Ptr_pango_font_description_get_weight)QLibrary::resolve(GTK_PATH, 0, "pango_font_description_get_weight");
+ QGtk::pango_font_description_get_family = (Ptr_pango_font_description_get_family)QLibrary::resolve(GTK_PATH, 0, "pango_font_description_get_family");
+ QGtk::pango_font_description_get_style = (Ptr_pango_font_description_get_style)QLibrary::resolve(GTK_PATH, 0, "pango_font_description_get_style");
+}
+
+void QGtk::cleanup_gtk_widgets()
+{
+ if (gtkWidgetMap()->contains(QLS("GtkWindow"))) // Gtk will destroy all children
+ QGtk::gtk_widget_destroy(gtkWidgetMap()->value(QLS("GtkWindow")));
+}
+
+static bool resolveGConf()
+{
+ if (!QGtk::gconf_client_get_default) {
+ QGtk::gconf_client_get_default = (Ptr_gconf_client_get_default)QLibrary::resolve(QLS("gconf-2"), 4, "gconf_client_get_default");
+ QGtk::gconf_client_get_string = (Ptr_gconf_client_get_string)QLibrary::resolve(QLS("gconf-2"), 4, "gconf_client_get_string");
+ }
+ return (QGtk::gconf_client_get_default !=0);
+}
+
+typedef int (*x11ErrorHandler)(Display*, XErrorEvent*);
+
+static QString getGConfString(const QString &value)
+{
+ QString retVal;
+ if (resolveGConf()) {
+ g_type_init();
+ GConfClient* client = QGtk::gconf_client_get_default();
+ GError *err = 0;
+ char *str = QGtk::gconf_client_get_string(client, qPrintable(value), &err);
+ if (!err) {
+ retVal = QString::fromUtf8(str);
+ g_free(str);
+ }
+ g_object_unref(client);
+ if (err)
+ g_error_free (err);
+ }
+ return retVal;
+}
+
+static QString getThemeName()
+{
+ QString themeName;
+ // We try to parse the gtkrc file first
+ // primarily to avoid resolving Gtk functions if
+ // the KDE 3 "Qt" style is currently in use
+ QString rcPaths = QString::fromLocal8Bit(qgetenv("GTK2_RC_FILES"));
+ if (!rcPaths.isEmpty()) {
+ QStringList paths = rcPaths.split(QLS(":"));
+ foreach (const QString &rcPath, paths) {
+ if (!rcPath.isEmpty()) {
+ QFile rcFile(rcPath);
+ if (rcFile.exists() && rcFile.open(QIODevice::ReadOnly | QIODevice::Text)) {
+ QTextStream in(&rcFile);
+ while(!in.atEnd()) {
+ QString line = in.readLine();
+ if (line.contains(QLS("gtk-theme-name"))) {
+ line = line.right(line.length() - line.indexOf(QLS("=")) - 1);
+ line.remove(QLS("\""));
+ line = line.trimmed();
+ themeName = line;
+ break;
+ }
+ }
+ }
+ }
+ if (!themeName.isEmpty())
+ break;
+ }
+ }
+
+ // Fall back to gconf
+ if (themeName.isEmpty() && resolveGConf())
+ themeName = getGConfString(QLS("/desktop/gnome/interface/gtk_theme"));
+
+ return themeName;
+}
+
+static void init_gtk_window()
+{
+ static QString themeName;
+ if (!gtkWidgetMap()->contains(QLS("GtkWindow")) && themeName.isEmpty()) {
+ themeName = getThemeName();
+ // Due to namespace conflicts with Qt3 and obvious recursion with Qt4,
+ // we cannot support the GTK_Qt Gtk engine
+ if (!(themeName.isEmpty() || themeName == QLS("Qt") || themeName == QLS("Qt4"))) {
+ resolveGtk();
+ if (QGtk::gtk_init) {
+ // Gtk will set the Qt error handler so we have to reset it afterwards
+ x11ErrorHandler qt_x_errhandler = XSetErrorHandler(0);
+ QGtk::gtk_init (NULL, NULL);
+ XSetErrorHandler(qt_x_errhandler);
+
+ GtkWidget* gtkWindow = QGtk::gtk_window_new(GTK_WINDOW_POPUP);
+ QGtk::gtk_widget_realize(gtkWindow);
+ if (displayDepth == -1)
+ displayDepth = QGtk::gdk_drawable_get_depth(gtkWindow->window);
+ gtkWidgetMap()->insert(QLS("GtkWindow"), gtkWindow);
+ }
+ else {
+ qWarning("QGtkStyle could not resolve GTK. Make sure you have installed the proper libraries.");
+ }
+ } else {
+ qWarning("QGtkStyle cannot be used together with the GTK_Qt engine.");
+ }
+ }
+}
+
+static void setup_gtk_widget(GtkWidget* widget)
+{
+ if (Q_GTK_IS_WIDGET(widget)) {
+ static GtkWidget* protoLayout = 0;
+ if (!protoLayout) {
+ protoLayout = QGtk::gtk_fixed_new();
+ QGtk::gtk_container_add((GtkContainer*)(gtkWidgetMap()->value(QLS("GtkWindow"))), protoLayout);
+ }
+ Q_ASSERT(protoLayout);
+
+ QGtk::gtk_container_add((GtkContainer*)(protoLayout), widget);
+ QGtk::gtk_widget_realize(widget);
+ }
+}
+
+static void add_widget_to_map(GtkWidget *widget)
+{
+ if (Q_GTK_IS_WIDGET(widget)) {
+ QGtk::gtk_widget_realize(widget);
+ gtkWidgetMap()->insert(classPath(widget), widget);
+ }
+ }
+
+static void add_all_sub_widgets(GtkWidget *widget, gpointer v = 0)
+{
+ Q_UNUSED(v);
+ add_widget_to_map(widget);
+ if (GTK_CHECK_TYPE ((widget), Q_GTK_TYPE_CONTAINER))
+ QGtk::gtk_container_forall((GtkContainer*)widget, add_all_sub_widgets, NULL);
+}
+
+static void init_gtk_menu()
+{
+ // Create menubar
+ GtkWidget *gtkMenuBar = QGtk::gtk_menu_bar_new();
+ setup_gtk_widget(gtkMenuBar);
+
+ GtkWidget *gtkMenuBarItem = QGtk::gtk_menu_item_new();
+ QGtk::gtk_menu_shell_append((GtkMenuShell*)(gtkMenuBar), gtkMenuBarItem);
+ QGtk::gtk_widget_realize(gtkMenuBarItem);
+
+ // Create menu
+ GtkWidget *gtkMenu = QGtk::gtk_menu_new();
+ QGtk::gtk_menu_item_set_submenu((GtkMenuItem*)(gtkMenuBarItem), gtkMenu);
+ QGtk::gtk_widget_realize(gtkMenu);
+
+ GtkWidget *gtkMenuItem = QGtk::gtk_menu_item_new();
+ QGtk::gtk_menu_shell_append((GtkMenuShell*)gtkMenu, gtkMenuItem);
+ QGtk::gtk_widget_realize(gtkMenuItem);
+
+ GtkWidget *gtkCheckMenuItem = QGtk::gtk_check_menu_item_new();
+ QGtk::gtk_menu_shell_append((GtkMenuShell*)gtkMenu, gtkCheckMenuItem);
+ QGtk::gtk_widget_realize(gtkCheckMenuItem);
+
+ GtkWidget *gtkMenuSeparator = QGtk::gtk_separator_menu_item_new();
+ QGtk::gtk_menu_shell_append((GtkMenuShell*)gtkMenu, gtkMenuSeparator);
+
+ add_all_sub_widgets(gtkMenuBar);
+ add_all_sub_widgets(gtkMenu);
+}
+
+// Updates window/windowtext palette based on the indicated gtk widget
+static void ensureWidgetPalette(QWidget* widget, const QString &gtkWidgetName)
+{
+ GtkWidget *gtkWidget = QGtk::gtkWidget(gtkWidgetName);
+ Q_ASSERT(gtkWidget);
+ QPalette pal = widget->palette();
+ GdkColor gdkBg = gtkWidget->style->bg[GTK_STATE_NORMAL];
+ GdkColor gdkText = gtkWidget->style->fg[GTK_STATE_NORMAL];
+ GdkColor gdkDisabledText = gtkWidget->style->fg[GTK_STATE_INSENSITIVE];
+ QColor bgColor(gdkBg.red>>8, gdkBg.green>>8, gdkBg.blue>>8);
+ QColor textColor(gdkText.red>>8, gdkText.green>>8, gdkText.blue>>8);
+ QColor disabledTextColor(gdkDisabledText.red>>8, gdkDisabledText.green>>8, gdkDisabledText.blue>>8);
+ pal.setBrush(QPalette::Window, bgColor);
+ pal.setBrush(QPalette::Button, bgColor);
+ pal.setBrush(QPalette::All, QPalette::WindowText, textColor);
+ pal.setBrush(QPalette::Disabled, QPalette::WindowText, disabledTextColor);
+ pal.setBrush(QPalette::All, QPalette::ButtonText, textColor);
+ pal.setBrush(QPalette::Disabled, QPalette::ButtonText, disabledTextColor);
+ widget->setPalette(pal);
+ widget->setAttribute(Qt::WA_SetPalette, false);
+}
+
+bool QGtk::isKDE4Session()
+{
+ static int version = -1;
+ if (version == -1)
+ version = qgetenv("KDE_SESSION_VERSION").toInt();
+ return (version == 4);
+}
+
+// Maps a Gtk widget palettes to a Qt widget
+void QGtk::applyGtkSystemPalette(QWidget *widget)
+{
+ // Do not apply if the widget has a custom palette;
+ if (widget->testAttribute(Qt::WA_SetPalette))
+ return;
+
+ QPalette pal;
+ if (QStatusBar *statusbar = qobject_cast<QStatusBar*> (widget))
+ ensureWidgetPalette(statusbar, QLS("GtkStatusbar"));
+ else if (QMenuBar *menubar = qobject_cast<QMenuBar*> (widget))
+ ensureWidgetPalette(menubar, QLS("GtkMenuBar"));
+ else if (QToolBar *toolbar = qobject_cast<QToolBar*> (widget))
+ ensureWidgetPalette(toolbar, QLS("GtkToolbar"));
+ else if (QMenu *menubar = qobject_cast<QMenu*> (widget)) {
+ // This really applies to the combo box rendering since
+ // QComboBox copies the palette from a QMenu
+ QPalette pal = widget->palette();
+ GdkColor gdkBg = QGtk::gtkWidget(QLS("GtkMenu"))->style->bg[GTK_STATE_NORMAL];
+ QColor bgColor(gdkBg.red>>8, gdkBg.green>>8, gdkBg.blue>>8);
+ pal.setBrush(QPalette::Base, bgColor);
+ menubar->setPalette(pal);
+ }
+ widget->setAttribute(Qt::WA_SetPalette, false);
+}
+
+static void gtkStyleSetCallback(GtkWidget*, GtkStyle*, void*)
+{
+ static QString oldTheme(QLS("qt_not_set"));
+ QPixmapCache::clear();
+ qApp->setFont(QGtk::getThemeFont());
+ QGtk::initGtkWidgets();
+ if (oldTheme != getThemeName()) {
+ oldTheme = getThemeName();
+ QApplicationPrivate::setSystemPalette(qApp->style()->standardPalette());
+ QList<QWidget*> widgets = QApplication::allWidgets();
+ foreach (QWidget *widget, widgets) {
+ QGtk::applyGtkSystemPalette(widget);
+ }
+ }
+}
+
+static void add_widget(GtkWidget *widget)
+{
+ if (widget) {
+ setup_gtk_widget(widget);
+ add_all_sub_widgets(widget);
+ }
+}
+
+static void init_gtk_treeview()
+{
+ GtkWidget *gtkTreeView = QGtk::gtk_tree_view_new();
+ QGtk::gtk_tree_view_append_column((GtkTreeView*)gtkTreeView, QGtk::gtk_tree_view_column_new());
+ QGtk::gtk_tree_view_append_column((GtkTreeView*)gtkTreeView, QGtk::gtk_tree_view_column_new());
+ QGtk::gtk_tree_view_append_column((GtkTreeView*)gtkTreeView, QGtk::gtk_tree_view_column_new());
+ add_widget(gtkTreeView);
+}
+
+
+// Fetch the application font from the pango font description
+// contained in the theme.
+QFont QGtk::getThemeFont()
+{
+ QFont font;
+ GtkStyle *style = gtkStyle();
+ if (style && qApp->desktopSettingsAware())
+ {
+ PangoFontDescription *gtk_font = style->font_desc;
+ font.setPointSizeF((float)(pango_font_description_get_size(gtk_font))/PANGO_SCALE);
+
+ QString family = QString::fromLatin1(pango_font_description_get_family(gtk_font));
+ if (!family.isEmpty())
+ font.setFamily(family);
+
+ int weight = pango_font_description_get_weight(gtk_font);
+ if (weight >= PANGO_WEIGHT_HEAVY)
+ font.setWeight(QFont::Black);
+ else if (weight >= PANGO_WEIGHT_BOLD)
+ font.setWeight(QFont::Bold);
+ else if (weight >= PANGO_WEIGHT_SEMIBOLD)
+ font.setWeight(QFont::DemiBold);
+ else if (weight >= PANGO_WEIGHT_NORMAL)
+ font.setWeight(QFont::Normal);
+ else
+ font.setWeight(QFont::Light);
+
+ PangoStyle fontstyle = pango_font_description_get_style(gtk_font);
+ if (fontstyle == PANGO_STYLE_ITALIC)
+ font.setStyle(QFont::StyleItalic);
+ else if (fontstyle == PANGO_STYLE_OBLIQUE)
+ font.setStyle(QFont::StyleOblique);
+ else
+ font.setStyle(QFont::StyleNormal);
+ }
+ return font;
+}
+
+GtkWidget* QGtk::gtkWidget(const QString &path)
+{
+ GtkWidget *widget = gtkWidgetMap()->value(path);
+ if (!widget) {
+ // Theme might have rearranged widget internals
+ widget = gtkWidgetMap()->value(path);
+ }
+ return widget;
+}
+
+GtkStyle* QGtk::gtkStyle(const QString &path)
+{
+ if (gtkWidgetMap()->contains(path))
+ return gtkWidgetMap()->value(path)->style;
+ return 0;
+}
+
+#ifdef Q_OS_LINUX
+QT_END_NAMESPACE
+
+int getresuid(uid_t *ruid, uid_t *euid, uid_t *suid);
+int getresgid(gid_t *rgid, gid_t *egid, gid_t *sgid);
+
+QT_BEGIN_NAMESPACE
+#endif
+
+void QGtk::initGtkWidgets()
+{
+ // From gtkmain.c
+
+ uid_t ruid, rgid, euid, egid, suid, sgid;
+
+#ifdef Q_OS_LINUX
+ if (getresuid (&ruid, &euid, &suid) != 0 || getresgid (&rgid, &egid, &sgid) != 0)
+#endif
+ {
+ suid = ruid = getuid ();
+ sgid = rgid = getgid ();
+ euid = geteuid ();
+ egid = getegid ();
+ }
+ if (ruid != euid || ruid != suid || rgid != egid || rgid != sgid) {
+ qWarning("\nThis process is currently running setuid or setgid.\nGTK+ does not allow this "
+ "therefore Qt cannot use the GTK+ integration.\nTry launching your app using \'gksudo\', "
+ "\'kdesudo\' or a similar tool.\n\n"
+ "See http://www.gtk.org/setuid.html for more information.\n");
+ return;
+ }
+
+ init_gtk_window();
+
+ if (QGtk::gtk_init) {
+
+ // Make all widgets respect the text direction
+ if (qApp->layoutDirection() == Qt::RightToLeft)
+ QGtk::gtk_widget_set_default_direction(GTK_TEXT_DIR_RTL);
+
+ if (!gtkWidgetMap()->contains(QLS("GtkButton"))) {
+ GtkWidget *gtkButton = QGtk::gtk_button_new();
+ add_widget(gtkButton);
+ g_signal_connect(gtkButton, "style-set", G_CALLBACK(gtkStyleSetCallback), NULL);
+ add_widget(QGtk::gtk_arrow_new(GTK_ARROW_DOWN, GTK_SHADOW_NONE));
+ add_widget(QGtk::gtk_hbutton_box_new());
+ add_widget(QGtk::gtk_check_button_new());
+ add_widget(QGtk::gtk_radio_button_new(NULL));
+ add_widget(QGtk::gtk_combo_box_new());
+ add_widget(QGtk::gtk_combo_box_entry_new());
+ add_widget(QGtk::gtk_entry_new());
+ add_widget(QGtk::gtk_frame_new(NULL));
+ add_widget(QGtk::gtk_expander_new(""));
+ add_widget(QGtk::gtk_statusbar_new());
+ add_widget(QGtk::gtk_hscale_new((GtkAdjustment*)(QGtk::gtk_adjustment_new(1, 0, 1, 0, 0, 0))));
+ add_widget(QGtk::gtk_hscrollbar_new(NULL));
+ add_widget(QGtk::gtk_scrolled_window_new(NULL, NULL));
+ init_gtk_menu();
+ add_widget(QGtk::gtk_notebook_new());
+ add_widget(QGtk::gtk_progress_bar_new());
+ add_widget(QGtk::gtk_spin_button_new((GtkAdjustment*)
+ (QGtk::gtk_adjustment_new(1, 0, 1, 0, 0, 0)), 0.1, 3));
+ GtkWidget *toolbar = QGtk::gtk_toolbar_new();
+ QGtk::gtk_toolbar_insert((GtkToolbar*)toolbar, QGtk::gtk_separator_tool_item_new(), -1);
+ add_widget(toolbar);
+ init_gtk_treeview();
+ add_widget(QGtk::gtk_vscale_new((GtkAdjustment*)(QGtk::gtk_adjustment_new(1, 0, 1, 0, 0, 0))));
+ add_widget(QGtk::gtk_vscrollbar_new(NULL));
+ }
+ else // Rebuild map
+ {
+ // When styles change subwidgets can get rearranged
+ // as with the combo box. We need to update the widget map
+ // to reflect this;
+ QHash<QString, GtkWidget*> oldMap = *gtkWidgetMap();
+ gtkWidgetMap()->clear();
+ QHashIterator<QString, GtkWidget*> it(oldMap);
+ while (it.hasNext()) {
+ it.next();
+ if (!it.key().contains(QLS("."))) {
+ add_all_sub_widgets(it.value());
+ }
+ }
+ }
+ }
+}
+
+// ----------- Native file dialogs -----------
+
+// Extract filter list from expressions of type: foo (*.a *.b *.c)"
+static QStringList extract_filter(const QString &rawFilter)
+{
+ QString result = rawFilter;
+ QRegExp r(QString::fromLatin1("^([^()]*)\\(([a-zA-Z0-9_.*? +;#\\-\\[\\]@\\{\\}/!<>\\$%&=^~:\\|]*)\\)$"));
+ int index = r.indexIn(result);
+ if (index >= 0)
+ result = r.cap(2);
+ return result.split(QLatin1Char(' '));
+}
+
+extern QStringList qt_make_filter_list(const QString &filter);
+
+static void setupGtkFileChooser(GtkWidget* gtkFileChooser, QWidget *parent,
+ const QString &dir, const QString &filter, QString *selectedFilter,
+ QFileDialog::Options options, bool isSaveDialog = false)
+{
+ g_object_set(gtkFileChooser, "do-overwrite-confirmation", gboolean(!(options & QFileDialog::DontConfirmOverwrite)), NULL);
+ g_object_set(gtkFileChooser, "local_only", gboolean(true), NULL);
+ if (!filter.isEmpty()) {
+ QStringList filters = qt_make_filter_list(filter);
+ foreach (const QString &rawfilter, filters) {
+ GtkFileFilter *gtkFilter = QGtk::gtk_file_filter_new ();
+ QString name = rawfilter.left(rawfilter.indexOf(QLatin1Char('(')));
+ QGtk::gtk_file_filter_set_name(gtkFilter, qPrintable(name));
+
+ QStringList extensions = extract_filter(rawfilter);
+ foreach (const QString &fileExtension, extensions) {
+ QGtk::gtk_file_filter_add_pattern (gtkFilter, qPrintable(fileExtension));
+ }
+ QGtk::gtk_file_chooser_add_filter((GtkFileChooser*)gtkFileChooser, gtkFilter);
+ if (selectedFilter && (rawfilter == *selectedFilter))
+ QGtk::gtk_file_chooser_set_filter((GtkFileChooser*)gtkFileChooser, gtkFilter);
+ }
+ }
+
+ // Using the currently active window is not entirely correct, however
+ // it gives more sensible behavior for applications that do not provide a
+ // parent
+ QWidget *modalFor = parent ? parent->window() : qApp->activeWindow();
+ if (modalFor) {
+ QGtk::gtk_widget_realize(gtkFileChooser); // Creates X window
+ XSetTransientForHint(QGtk::gdk_x11_drawable_get_xdisplay(gtkFileChooser->window),
+ QGtk::gdk_x11_drawable_get_xid(gtkFileChooser->window),
+ modalFor->winId());
+ QGtk::gdk_x11_window_set_user_time (gtkFileChooser->window, QX11Info::appUserTime());
+
+ }
+
+ QFileInfo fileinfo(dir);
+ if (dir.isEmpty())
+ fileinfo.setFile(QDir::currentPath());
+ fileinfo.makeAbsolute();
+ if (fileinfo.isDir()) {
+ QGtk::gtk_file_chooser_set_current_folder((GtkFileChooser*)gtkFileChooser, qPrintable(dir));
+ } else if (isSaveDialog) {
+ QGtk::gtk_file_chooser_set_current_folder((GtkFileChooser*)gtkFileChooser, qPrintable(fileinfo.absolutePath()));
+ QGtk::gtk_file_chooser_set_current_name((GtkFileChooser*)gtkFileChooser, qPrintable(fileinfo.fileName()));
+ } else {
+ QGtk::gtk_file_chooser_set_filename((GtkFileChooser*)gtkFileChooser, qPrintable(dir));
+ }
+}
+
+QString QGtk::openFilename(QWidget *parent, const QString &caption, const QString &dir, const QString &filter,
+ QString *selectedFilter, QFileDialog::Options options)
+{
+
+ GtkWidget *gtkFileChooser = QGtk::gtk_file_chooser_dialog_new (qPrintable(caption),
+ NULL,
+ GTK_FILE_CHOOSER_ACTION_OPEN,
+ GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+ GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
+ NULL);
+
+ setupGtkFileChooser(gtkFileChooser, parent, dir, filter, selectedFilter, options);
+
+ QWidget modal_widget;
+ modal_widget.setAttribute(Qt::WA_NoChildEventsForParent, true);
+ modal_widget.setParent(parent, Qt::Window);
+ QApplicationPrivate::enterModal(&modal_widget);
+
+ QString filename;
+ if (QGtk::gtk_dialog_run ((GtkDialog*)gtkFileChooser) == GTK_RESPONSE_ACCEPT) {
+ char *gtk_filename = QGtk::gtk_file_chooser_get_filename ((GtkFileChooser*)gtkFileChooser);
+ filename = QString::fromUtf8(gtk_filename);
+ g_free (gtk_filename);
+ }
+
+ QApplicationPrivate::leaveModal(&modal_widget);
+ gtk_widget_destroy (gtkFileChooser);
+ return filename;
+}
+
+
+QString QGtk::openDirectory(QWidget *parent, const QString &caption, const QString &dir, QFileDialog::Options options)
+{
+ GtkWidget *gtkFileChooser = QGtk::gtk_file_chooser_dialog_new (qPrintable(caption),
+ NULL,
+ GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER,
+ GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+ GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
+ NULL);
+
+ setupGtkFileChooser(gtkFileChooser, parent, dir, QString(), 0, options);
+ QWidget modal_widget;
+ modal_widget.setAttribute(Qt::WA_NoChildEventsForParent, true);
+ modal_widget.setParent(parent, Qt::Window);
+ QApplicationPrivate::enterModal(&modal_widget);
+
+ QString filename;
+ if (QGtk::gtk_dialog_run ((GtkDialog*)gtkFileChooser) == GTK_RESPONSE_ACCEPT) {
+ char *gtk_filename = QGtk::gtk_file_chooser_get_filename ((GtkFileChooser*)gtkFileChooser);
+ filename = QString::fromUtf8(gtk_filename);
+ g_free (gtk_filename);
+ }
+
+ QApplicationPrivate::leaveModal(&modal_widget);
+ gtk_widget_destroy (gtkFileChooser);
+ return filename;
+}
+
+QStringList QGtk::openFilenames(QWidget *parent, const QString &caption, const QString &dir, const QString &filter,
+ QString *selectedFilter, QFileDialog::Options options)
+{
+ QStringList filenames;
+ GtkWidget *gtkFileChooser = QGtk::gtk_file_chooser_dialog_new (qPrintable(caption),
+ NULL,
+ GTK_FILE_CHOOSER_ACTION_OPEN,
+ GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+ GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
+ NULL);
+
+ setupGtkFileChooser(gtkFileChooser, parent, dir, filter, selectedFilter, options);
+ g_object_set(gtkFileChooser, "select-multiple", gboolean(true), NULL);
+
+ QWidget modal_widget;
+ modal_widget.setAttribute(Qt::WA_NoChildEventsForParent, true);
+ modal_widget.setParent(parent, Qt::Window);
+ QApplicationPrivate::enterModal(&modal_widget);
+
+ if (gtk_dialog_run ((GtkDialog*)gtkFileChooser) == GTK_RESPONSE_ACCEPT) {
+ GSList *gtk_file_names = QGtk::gtk_file_chooser_get_filenames((GtkFileChooser*)gtkFileChooser);
+ for (GSList *iterator = gtk_file_names ; iterator; iterator = iterator->next)
+ filenames << QString::fromUtf8((const char*)iterator->data);
+ g_slist_free(gtk_file_names);
+ }
+
+ QApplicationPrivate::leaveModal(&modal_widget);
+ gtk_widget_destroy (gtkFileChooser);
+ return filenames;
+}
+
+QString QGtk::saveFilename(QWidget *parent, const QString &caption, const QString &dir, const QString &filter,
+ QString *selectedFilter, QFileDialog::Options options)
+{
+ GtkWidget *gtkFileChooser = QGtk::gtk_file_chooser_dialog_new (qPrintable(caption),
+ NULL,
+ GTK_FILE_CHOOSER_ACTION_SAVE,
+ GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+ GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
+ NULL);
+ setupGtkFileChooser(gtkFileChooser, parent, dir, filter, selectedFilter, options, true);
+
+ QWidget modal_widget;
+ modal_widget.setAttribute(Qt::WA_NoChildEventsForParent, true);
+ modal_widget.setParent(parent, Qt::Window);
+ QApplicationPrivate::enterModal(&modal_widget);
+
+ QString filename;
+ if (QGtk::gtk_dialog_run ((GtkDialog*)gtkFileChooser) == GTK_RESPONSE_ACCEPT) {
+ char *gtk_filename = QGtk::gtk_file_chooser_get_filename ((GtkFileChooser*)gtkFileChooser);
+ filename = QString::fromUtf8(gtk_filename);
+ g_free (gtk_filename);
+ }
+
+ QApplicationPrivate::leaveModal(&modal_widget);
+ gtk_widget_destroy (gtkFileChooser);
+ return filename;
+}
+
+QT_END_NAMESPACE
+
+#endif // !defined(QT_NO_STYLE_GTK)
diff --git a/src/gui/styles/gtksymbols_p.h b/src/gui/styles/gtksymbols_p.h
new file mode 100644
index 0000000000..0cea5428dd
--- /dev/null
+++ b/src/gui/styles/gtksymbols_p.h
@@ -0,0 +1,335 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef GTKSYMBOLS_H
+#define GTKSYMBOLS_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 <QtCore/qglobal.h>
+#if !defined(QT_NO_STYLE_GTK)
+
+#undef signals // Collides with GTK stymbols
+#include <gtk/gtk.h>
+#include <QtCore/QLibrary>
+#include <QtGui/QFont>
+#include <QtGui/QFileDialog>
+typedef unsigned long XID;
+
+#undef GTK_OBJECT_FLAGS
+#define GTK_OBJECT_FLAGS(obj)(((GtkObject*)(obj))->flags)
+#define Q_GTK_TYPE_WIDGET QGtk::gtk_widget_get_type()
+#define Q_GTK_IS_WIDGET(widget) widget && GTK_CHECK_TYPE ((widget), Q_GTK_TYPE_WIDGET)
+#define Q_GTK_TYPE_WINDOW QGtk::gtk_window_get_type()
+#define Q_GTK_TYPE_CONTAINER QGtk::gtk_container_get_type()
+
+#define QLS(x) QLatin1String(x)
+
+class GConf;
+class GConfClient;
+
+typedef GConfClient* (*Ptr_gconf_client_get_default)();
+typedef char* (*Ptr_gconf_client_get_string)(GConfClient*, const char*, GError **);
+
+typedef void (*Ptr_gtk_init)(int *, char ***);
+typedef GtkWidget* (*Ptr_gtk_window_new) (GtkWindowType);
+typedef GtkStyle* (*Ptr_gtk_style_attach)(GtkStyle *, GdkWindow *);
+typedef void (*Ptr_gtk_widget_destroy) (GtkWidget *);
+typedef void (*Ptr_gtk_widget_realize) (GtkWidget *);
+typedef void (*Ptr_gtk_widget_set_default_direction) (GtkTextDirection);
+typedef void (*Ptr_gtk_widget_modify_color)(GtkWidget *widget, GtkStateType state, const GdkColor *color);
+typedef GtkWidget* (*Ptr_gtk_arrow_new)(GtkArrowType, GtkShadowType);
+typedef GtkWidget* (*Ptr_gtk_menu_item_new)(void);
+typedef GtkWidget* (*Ptr_gtk_separator_menu_item_new)(void);
+typedef GtkWidget* (*Ptr_gtk_check_menu_item_new)(void);
+typedef GtkWidget* (*Ptr_gtk_menu_bar_new)(void);
+typedef GtkWidget* (*Ptr_gtk_menu_new)(void);
+typedef GtkWidget* (*Ptr_gtk_combo_box_entry_new)(void);
+typedef GtkWidget* (*Ptr_gtk_toolbar_new)(void);
+typedef GtkWidget* (*Ptr_gtk_spin_button_new)(GtkAdjustment*, double, int);
+typedef GtkWidget* (*Ptr_gtk_button_new)(void);
+typedef GtkWidget* (*Ptr_gtk_hbutton_box_new)(void);
+typedef GtkWidget* (*Ptr_gtk_check_button_new)(void);
+typedef GtkWidget* (*Ptr_gtk_radio_button_new)(GSList *);
+typedef GtkWidget* (*Ptr_gtk_notebook_new)(void);
+typedef GtkWidget* (*Ptr_gtk_progress_bar_new)(void);
+typedef GtkWidget* (*Ptr_gtk_hscale_new)(GtkAdjustment*);
+typedef GtkWidget* (*Ptr_gtk_vscale_new)(GtkAdjustment*);
+typedef GtkWidget* (*Ptr_gtk_hscrollbar_new)(GtkAdjustment*);
+typedef GtkWidget* (*Ptr_gtk_vscrollbar_new)(GtkAdjustment*);
+typedef GtkWidget* (*Ptr_gtk_scrolled_window_new)(GtkAdjustment*, GtkAdjustment*);
+typedef gchar* (*Ptr_gtk_check_version)(guint, guint, guint);
+typedef GtkToolItem* (*Ptr_gtk_separator_tool_item_new) (void);
+typedef GtkWidget* (*Ptr_gtk_entry_new)(void);
+typedef GtkWidget* (*Ptr_gtk_tree_view_new)(void);
+typedef GtkTreeViewColumn* (*Ptr_gtk_tree_view_get_column)(GtkTreeView *, gint);
+typedef GtkWidget* (*Ptr_gtk_combo_box_new)(void);
+typedef GtkWidget* (*Ptr_gtk_frame_new)(const gchar *);
+typedef GtkWidget* (*Ptr_gtk_expander_new)(const gchar*);
+typedef GtkWidget* (*Ptr_gtk_statusbar_new)(void);
+typedef GtkSettings* (*Ptr_gtk_settings_get_default)(void);
+typedef void (*Ptr_gtk_range_set_adjustment)(GtkRange *, GtkAdjustment *);
+typedef void (*Ptr_gtk_progress_set_adjustment)(GtkProgress *, GtkAdjustment *);
+typedef void (*Ptr_gtk_range_set_inverted)(GtkRange*, bool);
+typedef void (*Ptr_gtk_container_add)(GtkContainer *container, GtkWidget *widget);
+typedef GtkIconSet* (*Ptr_gtk_icon_factory_lookup_default) (const gchar*);
+typedef void (*Ptr_gtk_widget_style_get)(GtkWidget *, const gchar *first_property_name, ...);
+typedef GtkTreeViewColumn* (*Ptr_gtk_tree_view_column_new)(void);
+typedef GtkWidget* (*Ptr_gtk_fixed_new)(void);
+typedef GdkPixbuf* (*Ptr_gtk_icon_set_render_icon)(GtkIconSet *, GtkStyle *, GtkTextDirection, GtkStateType, GtkIconSize, GtkWidget *,const char *);
+typedef void (*Ptr_gtk_tree_view_append_column) (GtkTreeView*, GtkTreeViewColumn*);
+typedef void (*Ptr_gtk_paint_check) (GtkStyle*,GdkWindow*, GtkStateType, GtkShadowType, const GdkRectangle *, GtkWidget *, const gchar *, gint , gint , gint , gint);
+typedef void (*Ptr_gtk_paint_box) (GtkStyle*,GdkWindow*, GtkStateType, GtkShadowType, const GdkRectangle *, GtkWidget *, const gchar *, gint , gint , gint , gint);
+typedef void (*Ptr_gtk_paint_box_gap) (GtkStyle*,GdkWindow*, GtkStateType, GtkShadowType, const GdkRectangle *, GtkWidget *, const gchar *, gint, gint, gint , gint, GtkPositionType, gint gap_x, gint gap_width);
+typedef void (*Ptr_gtk_paint_resize_grip) (GtkStyle*,GdkWindow*, GtkStateType, const GdkRectangle *, GtkWidget *, const gchar *, GdkWindowEdge, gint , gint , gint , gint);
+typedef void (*Ptr_gtk_paint_focus) (GtkStyle*,GdkWindow*, GtkStateType, const GdkRectangle *, GtkWidget *, const gchar *, gint , gint , gint , gint);
+typedef void (*Ptr_gtk_paint_shadow) (GtkStyle*,GdkWindow*, GtkStateType, GtkShadowType, const GdkRectangle *, GtkWidget *, const gchar *, gint , gint , gint , gint);
+typedef void (*Ptr_gtk_paint_slider) (GtkStyle*,GdkWindow*, GtkStateType, GtkShadowType, const GdkRectangle *, GtkWidget *, const gchar *, gint , gint , gint , gint, GtkOrientation);
+typedef void (*Ptr_gtk_paint_expander) (GtkStyle*,GdkWindow*, GtkStateType, const GdkRectangle *, GtkWidget *, const gchar *, gint , gint , GtkExpanderStyle );
+typedef void (*Ptr_gtk_paint_handle) (GtkStyle*,GdkWindow*, GtkStateType, GtkShadowType, const GdkRectangle *, GtkWidget *, const gchar *, gint , gint , gint , gint, GtkOrientation);
+typedef void (*Ptr_gtk_paint_arrow) (GtkStyle*,GdkWindow*, GtkStateType, GtkShadowType, const GdkRectangle *, GtkWidget *, const gchar *, GtkArrowType, gboolean, gint , gint , gint , gint);
+typedef void (*Ptr_gtk_paint_option) (GtkStyle*,GdkWindow*, GtkStateType, GtkShadowType, const GdkRectangle *, GtkWidget *, const gchar *, gint , gint , gint , gint);
+typedef void (*Ptr_gtk_paint_flat_box) (GtkStyle*,GdkWindow*, GtkStateType, GtkShadowType, const GdkRectangle *, GtkWidget *, const gchar *, gint , gint , gint , gint);
+typedef void (*Ptr_gtk_paint_extension) (GtkStyle *, GdkWindow *, GtkStateType, GtkShadowType, const GdkRectangle *, GtkWidget *, const gchar *, gint, gint, gint, gint, GtkPositionType);
+typedef GtkObject* (*Ptr_gtk_adjustment_new) (double, double, double, double, double, double);
+typedef void (*Ptr_gtk_paint_hline) (GtkStyle *, GdkWindow *, GtkStateType, const GdkRectangle *, GtkWidget *, const gchar *, gint, gint, gint y);
+typedef void (*Ptr_gtk_paint_vline) (GtkStyle *, GdkWindow *, GtkStateType, const GdkRectangle *, GtkWidget *, const gchar *, gint, gint, gint);
+typedef void (*Ptr_gtk_menu_item_set_submenu) (GtkMenuItem *, GtkWidget *);
+typedef void (*Ptr_gtk_container_forall) (GtkContainer *, GtkCallback, gpointer);
+typedef void (*Ptr_gtk_widget_size_allocate) (GtkWidget *, GtkAllocation*);
+typedef void (*Ptr_gtk_widget_set_direction) (GtkWidget *, GtkTextDirection);
+typedef void (*Ptr_gtk_widget_path) (GtkWidget *, guint *, gchar **, gchar**);
+typedef void (*Ptr_gtk_toolbar_insert) (GtkToolbar *toolbar, GtkToolItem *item, int pos);
+typedef void (*Ptr_gtk_menu_shell_append)(GtkMenuShell *, GtkWidget *);
+typedef GtkType (*Ptr_gtk_container_get_type) (void);
+typedef GtkType (*Ptr_gtk_window_get_type) (void);
+typedef GtkType (*Ptr_gtk_widget_get_type) (void);
+typedef GtkStyle* (*Ptr_gtk_rc_get_style_by_paths) (GtkSettings *, const char *, const char *, GType);
+typedef gint (*Ptr_pango_font_description_get_size) (const PangoFontDescription *);
+typedef PangoWeight (*Ptr_pango_font_description_get_weight) (const PangoFontDescription *);
+typedef const char* (*Ptr_pango_font_description_get_family) (const PangoFontDescription *);
+typedef PangoStyle (*Ptr_pango_font_description_get_style) (const PangoFontDescription *desc);
+typedef gboolean (*Ptr_gtk_file_chooser_set_current_folder)(GtkFileChooser *, const gchar *);
+typedef GtkFileFilter* (*Ptr_gtk_file_filter_new)(void);
+typedef void (*Ptr_gtk_file_filter_set_name)(GtkFileFilter *, const gchar *);
+typedef void (*Ptr_gtk_file_filter_add_pattern)(GtkFileFilter *filter, const gchar *pattern);
+typedef void (*Ptr_gtk_file_chooser_add_filter)(GtkFileChooser *chooser, GtkFileFilter *filter);
+typedef void (*Ptr_gtk_file_chooser_set_filter)(GtkFileChooser *chooser, GtkFileFilter *filter);
+typedef gchar* (*Ptr_gtk_file_chooser_get_filename)(GtkFileChooser *chooser);
+typedef GSList* (*Ptr_gtk_file_chooser_get_filenames)(GtkFileChooser *chooser);
+typedef GtkWidget* (*Ptr_gtk_file_chooser_dialog_new)(const gchar *title,
+ GtkWindow *parent,
+ GtkFileChooserAction action,
+ const gchar *first_button_text,
+ ...);
+typedef void (*Ptr_gtk_file_chooser_set_current_name) (GtkFileChooser *, const gchar *);
+typedef gboolean (*Ptr_gtk_file_chooser_set_filename) (GtkFileChooser *chooser, const gchar *name);
+typedef gint (*Ptr_gtk_dialog_run) (GtkDialog*);
+
+typedef guchar* (*Ptr_gdk_pixbuf_get_pixels) (const GdkPixbuf *pixbuf);
+typedef int (*Ptr_gdk_pixbuf_get_width) (const GdkPixbuf *pixbuf);
+typedef void (*Ptr_gdk_color_free) (const GdkColor *);
+typedef int (*Ptr_gdk_pixbuf_get_height) (const GdkPixbuf *pixbuf);
+typedef GdkPixbuf* (*Ptr_gdk_pixbuf_get_from_drawable) (GdkPixbuf *dest, GdkDrawable *src,
+ GdkColormap *cmap, int src_x,
+ int src_y, int dest_x, int dest_y,
+ int width, int height);
+typedef GdkPixmap* (*Ptr_gdk_pixmap_new) (GdkDrawable *drawable, gint width, gint height, gint depth);
+typedef GdkPixbuf* (*Ptr_gdk_pixbuf_new) (GdkColorspace colorspace, gboolean has_alpha,
+ int bits_per_sample, int width, int height);
+typedef void (*Ptr_gdk_draw_rectangle) (GdkDrawable *drawable, GdkGC *gc,
+ gboolean filled, gint x, gint y, gint width, gint height);
+typedef void (*Ptr_gdk_pixbuf_unref)(GdkPixbuf *);
+typedef void (*Ptr_gdk_drawable_unref)(GdkDrawable *);
+typedef gint (*Ptr_gdk_drawable_get_depth)(GdkDrawable *);
+typedef void (*Ptr_gdk_x11_window_set_user_time) (GdkWindow *window, guint32);
+typedef XID (*Ptr_gdk_x11_drawable_get_xid) (GdkDrawable *);
+typedef Display* (*Ptr_gdk_x11_drawable_get_xdisplay) ( GdkDrawable *);
+
+QT_BEGIN_NAMESPACE
+
+class QGtk
+{
+public:
+ static GtkWidget* gtkWidget(const QString &path);
+ static GtkStyle* gtkStyle(const QString &path = QLatin1String("GtkWindow"));
+
+ static void cleanup_gtk_widgets();
+ static void initGtkWidgets();
+ static bool isKDE4Session();
+ static void applyGtkSystemPalette(QWidget* widget);
+ static QFont getThemeFont();
+ static bool isThemeAvailable() { return gtkStyle() != 0; }
+
+ static QString openFilename(QWidget *parent, const QString &caption, const QString &dir, const QString &filter,
+ QString *selectedFilter, QFileDialog::Options options);
+ static QString saveFilename(QWidget *parent, const QString &caption, const QString &dir, const QString &filter,
+ QString *selectedFilter, QFileDialog::Options options);
+ static QString openDirectory(QWidget *parent, const QString &caption, const QString &dir, QFileDialog::Options options);
+ static QStringList openFilenames(QWidget *parent, const QString &caption, const QString &dir, const QString &filter,
+ QString *selectedFilter, QFileDialog::Options options);
+
+ static Ptr_gtk_container_forall gtk_container_forall;
+ static Ptr_gtk_init gtk_init;
+ static Ptr_gtk_style_attach gtk_style_attach;
+ static Ptr_gtk_window_new gtk_window_new;
+ static Ptr_gtk_widget_destroy gtk_widget_destroy;
+ static Ptr_gtk_widget_realize gtk_widget_realize;
+ static Ptr_gtk_widget_set_default_direction gtk_widget_set_default_direction;
+ static Ptr_gtk_widget_modify_color gtk_widget_modify_fg;
+ static Ptr_gtk_widget_modify_color gtk_widget_modify_bg;
+ static Ptr_gtk_menu_item_new gtk_menu_item_new;
+ static Ptr_gtk_arrow_new gtk_arrow_new;
+ static Ptr_gtk_check_menu_item_new gtk_check_menu_item_new;
+ static Ptr_gtk_menu_bar_new gtk_menu_bar_new;
+ static Ptr_gtk_menu_new gtk_menu_new;
+ static Ptr_gtk_expander_new gtk_expander_new;
+ static Ptr_gtk_button_new gtk_button_new;
+ static Ptr_gtk_hbutton_box_new gtk_hbutton_box_new;
+ static Ptr_gtk_check_button_new gtk_check_button_new;
+ static Ptr_gtk_radio_button_new gtk_radio_button_new;
+ static Ptr_gtk_spin_button_new gtk_spin_button_new;
+ static Ptr_gtk_separator_tool_item_new gtk_separator_tool_item_new;
+ static Ptr_gtk_toolbar_insert gtk_toolbar_insert;
+ static Ptr_gtk_frame_new gtk_frame_new;
+ static Ptr_gtk_statusbar_new gtk_statusbar_new;
+ static Ptr_gtk_entry_new gtk_entry_new;
+ static Ptr_gtk_hscale_new gtk_hscale_new;
+ static Ptr_gtk_vscale_new gtk_vscale_new;
+ static Ptr_gtk_hscrollbar_new gtk_hscrollbar_new;
+ static Ptr_gtk_vscrollbar_new gtk_vscrollbar_new;
+ static Ptr_gtk_scrolled_window_new gtk_scrolled_window_new;
+ static Ptr_gtk_notebook_new gtk_notebook_new;
+ static Ptr_gtk_toolbar_new gtk_toolbar_new;
+ static Ptr_gtk_tree_view_new gtk_tree_view_new;
+ static Ptr_gtk_tree_view_get_column gtk_tree_view_get_column;
+ static Ptr_gtk_combo_box_new gtk_combo_box_new;
+ static Ptr_gtk_combo_box_entry_new gtk_combo_box_entry_new;
+ static Ptr_gtk_progress_bar_new gtk_progress_bar_new;
+ static Ptr_gtk_container_add gtk_container_add;
+ static Ptr_gtk_menu_shell_append gtk_menu_shell_append;
+ static Ptr_gtk_progress_set_adjustment gtk_progress_set_adjustment;
+ static Ptr_gtk_range_set_adjustment gtk_range_set_adjustment;
+ static Ptr_gtk_range_set_inverted gtk_range_set_inverted;
+ static Ptr_gtk_icon_factory_lookup_default gtk_icon_factory_lookup_default;
+ static Ptr_gtk_widget_style_get gtk_widget_style_get;
+ static Ptr_gtk_icon_set_render_icon gtk_icon_set_render_icon;
+ static Ptr_gtk_fixed_new gtk_fixed_new;
+ static Ptr_gtk_tree_view_column_new gtk_tree_view_column_new;
+ static Ptr_gtk_tree_view_append_column gtk_tree_view_append_column;
+ static Ptr_gtk_paint_check gtk_paint_check;
+ static Ptr_gtk_paint_box gtk_paint_box;
+ static Ptr_gtk_paint_box_gap gtk_paint_box_gap;
+ static Ptr_gtk_paint_flat_box gtk_paint_flat_box;
+ static Ptr_gtk_paint_option gtk_paint_option;
+ static Ptr_gtk_paint_extension gtk_paint_extension;
+ static Ptr_gtk_paint_slider gtk_paint_slider;
+ static Ptr_gtk_paint_shadow gtk_paint_shadow;
+ static Ptr_gtk_paint_resize_grip gtk_paint_resize_grip;
+ static Ptr_gtk_paint_focus gtk_paint_focus;
+ static Ptr_gtk_paint_arrow gtk_paint_arrow;
+ static Ptr_gtk_paint_handle gtk_paint_handle;
+ static Ptr_gtk_paint_expander gtk_paint_expander;
+ static Ptr_gtk_adjustment_new gtk_adjustment_new;
+ static Ptr_gtk_paint_vline gtk_paint_vline;
+ static Ptr_gtk_paint_hline gtk_paint_hline;
+ static Ptr_gtk_menu_item_set_submenu gtk_menu_item_set_submenu;
+ static Ptr_gtk_settings_get_default gtk_settings_get_default;
+ static Ptr_gtk_separator_menu_item_new gtk_separator_menu_item_new;
+ static Ptr_gtk_widget_size_allocate gtk_widget_size_allocate;
+ static Ptr_gtk_widget_set_direction gtk_widget_set_direction;
+ static Ptr_gtk_widget_path gtk_widget_path;
+ static Ptr_gtk_container_get_type gtk_container_get_type;
+ static Ptr_gtk_window_get_type gtk_window_get_type;
+ static Ptr_gtk_widget_get_type gtk_widget_get_type;
+ static Ptr_gtk_rc_get_style_by_paths gtk_rc_get_style_by_paths;
+ static Ptr_gtk_check_version gtk_check_version;
+
+ static Ptr_pango_font_description_get_size pango_font_description_get_size;
+ static Ptr_pango_font_description_get_weight pango_font_description_get_weight;
+ static Ptr_pango_font_description_get_family pango_font_description_get_family;
+ static Ptr_pango_font_description_get_style pango_font_description_get_style;
+
+ static Ptr_gtk_file_filter_new gtk_file_filter_new;
+ static Ptr_gtk_file_filter_set_name gtk_file_filter_set_name;
+ static Ptr_gtk_file_filter_add_pattern gtk_file_filter_add_pattern;
+ static Ptr_gtk_file_chooser_add_filter gtk_file_chooser_add_filter;
+ static Ptr_gtk_file_chooser_set_filter gtk_file_chooser_set_filter;
+ static Ptr_gtk_file_chooser_dialog_new gtk_file_chooser_dialog_new;
+ static Ptr_gtk_file_chooser_set_current_folder gtk_file_chooser_set_current_folder;
+ static Ptr_gtk_file_chooser_get_filename gtk_file_chooser_get_filename;
+ static Ptr_gtk_file_chooser_get_filenames gtk_file_chooser_get_filenames;
+ static Ptr_gtk_file_chooser_set_current_name gtk_file_chooser_set_current_name;
+ static Ptr_gtk_dialog_run gtk_dialog_run;
+ static Ptr_gtk_file_chooser_set_filename gtk_file_chooser_set_filename;
+
+ static Ptr_gdk_pixbuf_get_pixels gdk_pixbuf_get_pixels;
+ static Ptr_gdk_pixbuf_get_width gdk_pixbuf_get_width;
+ static Ptr_gdk_pixbuf_get_height gdk_pixbuf_get_height;
+ static Ptr_gdk_pixmap_new gdk_pixmap_new;
+ static Ptr_gdk_pixbuf_new gdk_pixbuf_new;
+ static Ptr_gdk_pixbuf_get_from_drawable gdk_pixbuf_get_from_drawable;
+ static Ptr_gdk_draw_rectangle gdk_draw_rectangle;
+ static Ptr_gdk_pixbuf_unref gdk_pixbuf_unref;
+ static Ptr_gdk_drawable_unref gdk_drawable_unref;
+ static Ptr_gdk_drawable_get_depth gdk_drawable_get_depth;
+ static Ptr_gdk_color_free gdk_color_free;
+ static Ptr_gdk_x11_window_set_user_time gdk_x11_window_set_user_time;
+ static Ptr_gdk_x11_drawable_get_xid gdk_x11_drawable_get_xid;
+ static Ptr_gdk_x11_drawable_get_xdisplay gdk_x11_drawable_get_xdisplay;
+
+ static Ptr_gconf_client_get_default gconf_client_get_default;
+ static Ptr_gconf_client_get_string gconf_client_get_string;
+};
+
+QT_END_NAMESPACE
+
+#endif // !QT_NO_STYLE_GTK
+#endif // GTKSYMBOLS_H
diff --git a/src/gui/styles/images/cdr-128.png b/src/gui/styles/images/cdr-128.png
new file mode 100644
index 0000000000..c5daa15fc8
--- /dev/null
+++ b/src/gui/styles/images/cdr-128.png
Binary files differ
diff --git a/src/gui/styles/images/cdr-16.png b/src/gui/styles/images/cdr-16.png
new file mode 100644
index 0000000000..82d7533bd1
--- /dev/null
+++ b/src/gui/styles/images/cdr-16.png
Binary files differ
diff --git a/src/gui/styles/images/cdr-32.png b/src/gui/styles/images/cdr-32.png
new file mode 100644
index 0000000000..dcfb085da5
--- /dev/null
+++ b/src/gui/styles/images/cdr-32.png
Binary files differ
diff --git a/src/gui/styles/images/closedock-16.png b/src/gui/styles/images/closedock-16.png
new file mode 100644
index 0000000000..ab9d669eee
--- /dev/null
+++ b/src/gui/styles/images/closedock-16.png
Binary files differ
diff --git a/src/gui/styles/images/closedock-down-16.png b/src/gui/styles/images/closedock-down-16.png
new file mode 100644
index 0000000000..c1791dd2cc
--- /dev/null
+++ b/src/gui/styles/images/closedock-down-16.png
Binary files differ
diff --git a/src/gui/styles/images/computer-16.png b/src/gui/styles/images/computer-16.png
new file mode 100644
index 0000000000..43fb0bb581
--- /dev/null
+++ b/src/gui/styles/images/computer-16.png
Binary files differ
diff --git a/src/gui/styles/images/computer-32.png b/src/gui/styles/images/computer-32.png
new file mode 100644
index 0000000000..6d750ce89b
--- /dev/null
+++ b/src/gui/styles/images/computer-32.png
Binary files differ
diff --git a/src/gui/styles/images/desktop-16.png b/src/gui/styles/images/desktop-16.png
new file mode 100644
index 0000000000..d612dfb0fc
--- /dev/null
+++ b/src/gui/styles/images/desktop-16.png
Binary files differ
diff --git a/src/gui/styles/images/desktop-32.png b/src/gui/styles/images/desktop-32.png
new file mode 100644
index 0000000000..ad85b48d3a
--- /dev/null
+++ b/src/gui/styles/images/desktop-32.png
Binary files differ
diff --git a/src/gui/styles/images/dirclosed-128.png b/src/gui/styles/images/dirclosed-128.png
new file mode 100644
index 0000000000..e4fa843162
--- /dev/null
+++ b/src/gui/styles/images/dirclosed-128.png
Binary files differ
diff --git a/src/gui/styles/images/dirclosed-16.png b/src/gui/styles/images/dirclosed-16.png
new file mode 100644
index 0000000000..333fe8eaac
--- /dev/null
+++ b/src/gui/styles/images/dirclosed-16.png
Binary files differ
diff --git a/src/gui/styles/images/dirclosed-32.png b/src/gui/styles/images/dirclosed-32.png
new file mode 100644
index 0000000000..3add907ed5
--- /dev/null
+++ b/src/gui/styles/images/dirclosed-32.png
Binary files differ
diff --git a/src/gui/styles/images/dirlink-128.png b/src/gui/styles/images/dirlink-128.png
new file mode 100644
index 0000000000..ec299f8e52
--- /dev/null
+++ b/src/gui/styles/images/dirlink-128.png
Binary files differ
diff --git a/src/gui/styles/images/dirlink-16.png b/src/gui/styles/images/dirlink-16.png
new file mode 100644
index 0000000000..9f16cd3520
--- /dev/null
+++ b/src/gui/styles/images/dirlink-16.png
Binary files differ
diff --git a/src/gui/styles/images/dirlink-32.png b/src/gui/styles/images/dirlink-32.png
new file mode 100644
index 0000000000..776536d131
--- /dev/null
+++ b/src/gui/styles/images/dirlink-32.png
Binary files differ
diff --git a/src/gui/styles/images/diropen-128.png b/src/gui/styles/images/diropen-128.png
new file mode 100644
index 0000000000..b91c4af72a
--- /dev/null
+++ b/src/gui/styles/images/diropen-128.png
Binary files differ
diff --git a/src/gui/styles/images/diropen-16.png b/src/gui/styles/images/diropen-16.png
new file mode 100644
index 0000000000..95f0771d06
--- /dev/null
+++ b/src/gui/styles/images/diropen-16.png
Binary files differ
diff --git a/src/gui/styles/images/diropen-32.png b/src/gui/styles/images/diropen-32.png
new file mode 100644
index 0000000000..af5f7e7e81
--- /dev/null
+++ b/src/gui/styles/images/diropen-32.png
Binary files differ
diff --git a/src/gui/styles/images/dockdock-16.png b/src/gui/styles/images/dockdock-16.png
new file mode 100644
index 0000000000..4ac9483176
--- /dev/null
+++ b/src/gui/styles/images/dockdock-16.png
Binary files differ
diff --git a/src/gui/styles/images/dockdock-down-16.png b/src/gui/styles/images/dockdock-down-16.png
new file mode 100644
index 0000000000..2e85a679be
--- /dev/null
+++ b/src/gui/styles/images/dockdock-down-16.png
Binary files differ
diff --git a/src/gui/styles/images/down-128.png b/src/gui/styles/images/down-128.png
new file mode 100644
index 0000000000..09dfe43a93
--- /dev/null
+++ b/src/gui/styles/images/down-128.png
Binary files differ
diff --git a/src/gui/styles/images/down-16.png b/src/gui/styles/images/down-16.png
new file mode 100644
index 0000000000..c60a174e25
--- /dev/null
+++ b/src/gui/styles/images/down-16.png
Binary files differ
diff --git a/src/gui/styles/images/down-32.png b/src/gui/styles/images/down-32.png
new file mode 100644
index 0000000000..46eadb8e12
--- /dev/null
+++ b/src/gui/styles/images/down-32.png
Binary files differ
diff --git a/src/gui/styles/images/dvd-128.png b/src/gui/styles/images/dvd-128.png
new file mode 100644
index 0000000000..9ed9dc1e55
--- /dev/null
+++ b/src/gui/styles/images/dvd-128.png
Binary files differ
diff --git a/src/gui/styles/images/dvd-16.png b/src/gui/styles/images/dvd-16.png
new file mode 100644
index 0000000000..623386d4ca
--- /dev/null
+++ b/src/gui/styles/images/dvd-16.png
Binary files differ
diff --git a/src/gui/styles/images/dvd-32.png b/src/gui/styles/images/dvd-32.png
new file mode 100644
index 0000000000..089b72accb
--- /dev/null
+++ b/src/gui/styles/images/dvd-32.png
Binary files differ
diff --git a/src/gui/styles/images/file-128.png b/src/gui/styles/images/file-128.png
new file mode 100644
index 0000000000..46e6ceb49a
--- /dev/null
+++ b/src/gui/styles/images/file-128.png
Binary files differ
diff --git a/src/gui/styles/images/file-16.png b/src/gui/styles/images/file-16.png
new file mode 100644
index 0000000000..664b56356b
--- /dev/null
+++ b/src/gui/styles/images/file-16.png
Binary files differ
diff --git a/src/gui/styles/images/file-32.png b/src/gui/styles/images/file-32.png
new file mode 100644
index 0000000000..83e5c3d311
--- /dev/null
+++ b/src/gui/styles/images/file-32.png
Binary files differ
diff --git a/src/gui/styles/images/filecontents-128.png b/src/gui/styles/images/filecontents-128.png
new file mode 100644
index 0000000000..50e0a838e2
--- /dev/null
+++ b/src/gui/styles/images/filecontents-128.png
Binary files differ
diff --git a/src/gui/styles/images/filecontents-16.png b/src/gui/styles/images/filecontents-16.png
new file mode 100644
index 0000000000..b9399ccf2e
--- /dev/null
+++ b/src/gui/styles/images/filecontents-16.png
Binary files differ
diff --git a/src/gui/styles/images/filecontents-32.png b/src/gui/styles/images/filecontents-32.png
new file mode 100644
index 0000000000..3761f70690
--- /dev/null
+++ b/src/gui/styles/images/filecontents-32.png
Binary files differ
diff --git a/src/gui/styles/images/fileinfo-128.png b/src/gui/styles/images/fileinfo-128.png
new file mode 100644
index 0000000000..8c5b331876
--- /dev/null
+++ b/src/gui/styles/images/fileinfo-128.png
Binary files differ
diff --git a/src/gui/styles/images/fileinfo-16.png b/src/gui/styles/images/fileinfo-16.png
new file mode 100644
index 0000000000..729be4d5f2
--- /dev/null
+++ b/src/gui/styles/images/fileinfo-16.png
Binary files differ
diff --git a/src/gui/styles/images/fileinfo-32.png b/src/gui/styles/images/fileinfo-32.png
new file mode 100644
index 0000000000..ca795aa49b
--- /dev/null
+++ b/src/gui/styles/images/fileinfo-32.png
Binary files differ
diff --git a/src/gui/styles/images/filelink-128.png b/src/gui/styles/images/filelink-128.png
new file mode 100644
index 0000000000..be86a82901
--- /dev/null
+++ b/src/gui/styles/images/filelink-128.png
Binary files differ
diff --git a/src/gui/styles/images/filelink-16.png b/src/gui/styles/images/filelink-16.png
new file mode 100644
index 0000000000..6643f2c428
--- /dev/null
+++ b/src/gui/styles/images/filelink-16.png
Binary files differ
diff --git a/src/gui/styles/images/filelink-32.png b/src/gui/styles/images/filelink-32.png
new file mode 100644
index 0000000000..1e46fdc13c
--- /dev/null
+++ b/src/gui/styles/images/filelink-32.png
Binary files differ
diff --git a/src/gui/styles/images/floppy-128.png b/src/gui/styles/images/floppy-128.png
new file mode 100644
index 0000000000..fa7a3e1334
--- /dev/null
+++ b/src/gui/styles/images/floppy-128.png
Binary files differ
diff --git a/src/gui/styles/images/floppy-16.png b/src/gui/styles/images/floppy-16.png
new file mode 100644
index 0000000000..91c59c567d
--- /dev/null
+++ b/src/gui/styles/images/floppy-16.png
Binary files differ
diff --git a/src/gui/styles/images/floppy-32.png b/src/gui/styles/images/floppy-32.png
new file mode 100644
index 0000000000..e63b3213bf
--- /dev/null
+++ b/src/gui/styles/images/floppy-32.png
Binary files differ
diff --git a/src/gui/styles/images/fontbitmap-16.png b/src/gui/styles/images/fontbitmap-16.png
new file mode 100644
index 0000000000..03efc9cbab
--- /dev/null
+++ b/src/gui/styles/images/fontbitmap-16.png
Binary files differ
diff --git a/src/gui/styles/images/fonttruetype-16.png b/src/gui/styles/images/fonttruetype-16.png
new file mode 100644
index 0000000000..25205021e9
--- /dev/null
+++ b/src/gui/styles/images/fonttruetype-16.png
Binary files differ
diff --git a/src/gui/styles/images/harddrive-128.png b/src/gui/styles/images/harddrive-128.png
new file mode 100644
index 0000000000..0b73d9de1e
--- /dev/null
+++ b/src/gui/styles/images/harddrive-128.png
Binary files differ
diff --git a/src/gui/styles/images/harddrive-16.png b/src/gui/styles/images/harddrive-16.png
new file mode 100644
index 0000000000..45d592baa3
--- /dev/null
+++ b/src/gui/styles/images/harddrive-16.png
Binary files differ
diff --git a/src/gui/styles/images/harddrive-32.png b/src/gui/styles/images/harddrive-32.png
new file mode 100644
index 0000000000..7041452b68
--- /dev/null
+++ b/src/gui/styles/images/harddrive-32.png
Binary files differ
diff --git a/src/gui/styles/images/left-128.png b/src/gui/styles/images/left-128.png
new file mode 100644
index 0000000000..a26a5195f8
--- /dev/null
+++ b/src/gui/styles/images/left-128.png
Binary files differ
diff --git a/src/gui/styles/images/left-16.png b/src/gui/styles/images/left-16.png
new file mode 100644
index 0000000000..110dd90f2d
--- /dev/null
+++ b/src/gui/styles/images/left-16.png
Binary files differ
diff --git a/src/gui/styles/images/left-32.png b/src/gui/styles/images/left-32.png
new file mode 100644
index 0000000000..ec4107b372
--- /dev/null
+++ b/src/gui/styles/images/left-32.png
Binary files differ
diff --git a/src/gui/styles/images/media-pause-16.png b/src/gui/styles/images/media-pause-16.png
new file mode 100644
index 0000000000..6cb1fd7f63
--- /dev/null
+++ b/src/gui/styles/images/media-pause-16.png
Binary files differ
diff --git a/src/gui/styles/images/media-pause-32.png b/src/gui/styles/images/media-pause-32.png
new file mode 100644
index 0000000000..3f172a04d6
--- /dev/null
+++ b/src/gui/styles/images/media-pause-32.png
Binary files differ
diff --git a/src/gui/styles/images/media-play-16.png b/src/gui/styles/images/media-play-16.png
new file mode 100644
index 0000000000..d7ee3ccbe3
--- /dev/null
+++ b/src/gui/styles/images/media-play-16.png
Binary files differ
diff --git a/src/gui/styles/images/media-play-32.png b/src/gui/styles/images/media-play-32.png
new file mode 100644
index 0000000000..af8d2f7ba5
--- /dev/null
+++ b/src/gui/styles/images/media-play-32.png
Binary files differ
diff --git a/src/gui/styles/images/media-seek-backward-16.png b/src/gui/styles/images/media-seek-backward-16.png
new file mode 100644
index 0000000000..b8a8ea42d1
--- /dev/null
+++ b/src/gui/styles/images/media-seek-backward-16.png
Binary files differ
diff --git a/src/gui/styles/images/media-seek-backward-32.png b/src/gui/styles/images/media-seek-backward-32.png
new file mode 100644
index 0000000000..a21d1372fe
--- /dev/null
+++ b/src/gui/styles/images/media-seek-backward-32.png
Binary files differ
diff --git a/src/gui/styles/images/media-seek-forward-16.png b/src/gui/styles/images/media-seek-forward-16.png
new file mode 100644
index 0000000000..3c900dcb62
--- /dev/null
+++ b/src/gui/styles/images/media-seek-forward-16.png
Binary files differ
diff --git a/src/gui/styles/images/media-seek-forward-32.png b/src/gui/styles/images/media-seek-forward-32.png
new file mode 100644
index 0000000000..4f8d370fa1
--- /dev/null
+++ b/src/gui/styles/images/media-seek-forward-32.png
Binary files differ
diff --git a/src/gui/styles/images/media-skip-backward-16.png b/src/gui/styles/images/media-skip-backward-16.png
new file mode 100644
index 0000000000..f5b3f4f56d
--- /dev/null
+++ b/src/gui/styles/images/media-skip-backward-16.png
Binary files differ
diff --git a/src/gui/styles/images/media-skip-backward-32.png b/src/gui/styles/images/media-skip-backward-32.png
new file mode 100644
index 0000000000..1d338035ef
--- /dev/null
+++ b/src/gui/styles/images/media-skip-backward-32.png
Binary files differ
diff --git a/src/gui/styles/images/media-skip-forward-16.png b/src/gui/styles/images/media-skip-forward-16.png
new file mode 100644
index 0000000000..27e205b02f
--- /dev/null
+++ b/src/gui/styles/images/media-skip-forward-16.png
Binary files differ
diff --git a/src/gui/styles/images/media-skip-forward-32.png b/src/gui/styles/images/media-skip-forward-32.png
new file mode 100644
index 0000000000..a583fa1b11
--- /dev/null
+++ b/src/gui/styles/images/media-skip-forward-32.png
Binary files differ
diff --git a/src/gui/styles/images/media-stop-16.png b/src/gui/styles/images/media-stop-16.png
new file mode 100644
index 0000000000..9ce035d696
--- /dev/null
+++ b/src/gui/styles/images/media-stop-16.png
Binary files differ
diff --git a/src/gui/styles/images/media-stop-32.png b/src/gui/styles/images/media-stop-32.png
new file mode 100644
index 0000000000..aae24ba925
--- /dev/null
+++ b/src/gui/styles/images/media-stop-32.png
Binary files differ
diff --git a/src/gui/styles/images/media-volume-16.png b/src/gui/styles/images/media-volume-16.png
new file mode 100644
index 0000000000..ad258340f2
--- /dev/null
+++ b/src/gui/styles/images/media-volume-16.png
Binary files differ
diff --git a/src/gui/styles/images/media-volume-muted-16.png b/src/gui/styles/images/media-volume-muted-16.png
new file mode 100644
index 0000000000..06bded21e7
--- /dev/null
+++ b/src/gui/styles/images/media-volume-muted-16.png
Binary files differ
diff --git a/src/gui/styles/images/networkdrive-128.png b/src/gui/styles/images/networkdrive-128.png
new file mode 100644
index 0000000000..fd4a59c6bd
--- /dev/null
+++ b/src/gui/styles/images/networkdrive-128.png
Binary files differ
diff --git a/src/gui/styles/images/networkdrive-16.png b/src/gui/styles/images/networkdrive-16.png
new file mode 100644
index 0000000000..1bc62f766a
--- /dev/null
+++ b/src/gui/styles/images/networkdrive-16.png
Binary files differ
diff --git a/src/gui/styles/images/networkdrive-32.png b/src/gui/styles/images/networkdrive-32.png
new file mode 100644
index 0000000000..6a389dcae4
--- /dev/null
+++ b/src/gui/styles/images/networkdrive-32.png
Binary files differ
diff --git a/src/gui/styles/images/newdirectory-128.png b/src/gui/styles/images/newdirectory-128.png
new file mode 100644
index 0000000000..fdbee27688
--- /dev/null
+++ b/src/gui/styles/images/newdirectory-128.png
Binary files differ
diff --git a/src/gui/styles/images/newdirectory-16.png b/src/gui/styles/images/newdirectory-16.png
new file mode 100644
index 0000000000..6c9f80318b
--- /dev/null
+++ b/src/gui/styles/images/newdirectory-16.png
Binary files differ
diff --git a/src/gui/styles/images/newdirectory-32.png b/src/gui/styles/images/newdirectory-32.png
new file mode 100644
index 0000000000..4fd0329216
--- /dev/null
+++ b/src/gui/styles/images/newdirectory-32.png
Binary files differ
diff --git a/src/gui/styles/images/parentdir-128.png b/src/gui/styles/images/parentdir-128.png
new file mode 100644
index 0000000000..84d14ab079
--- /dev/null
+++ b/src/gui/styles/images/parentdir-128.png
Binary files differ
diff --git a/src/gui/styles/images/parentdir-16.png b/src/gui/styles/images/parentdir-16.png
new file mode 100644
index 0000000000..665f8280f2
--- /dev/null
+++ b/src/gui/styles/images/parentdir-16.png
Binary files differ
diff --git a/src/gui/styles/images/parentdir-32.png b/src/gui/styles/images/parentdir-32.png
new file mode 100644
index 0000000000..44f3c4876d
--- /dev/null
+++ b/src/gui/styles/images/parentdir-32.png
Binary files differ
diff --git a/src/gui/styles/images/refresh-24.png b/src/gui/styles/images/refresh-24.png
new file mode 100644
index 0000000000..4c9b72c489
--- /dev/null
+++ b/src/gui/styles/images/refresh-24.png
Binary files differ
diff --git a/src/gui/styles/images/refresh-32.png b/src/gui/styles/images/refresh-32.png
new file mode 100644
index 0000000000..eecde4b8f9
--- /dev/null
+++ b/src/gui/styles/images/refresh-32.png
Binary files differ
diff --git a/src/gui/styles/images/right-128.png b/src/gui/styles/images/right-128.png
new file mode 100644
index 0000000000..14b1cfd8eb
--- /dev/null
+++ b/src/gui/styles/images/right-128.png
Binary files differ
diff --git a/src/gui/styles/images/right-16.png b/src/gui/styles/images/right-16.png
new file mode 100644
index 0000000000..81ca628ff6
--- /dev/null
+++ b/src/gui/styles/images/right-16.png
Binary files differ
diff --git a/src/gui/styles/images/right-32.png b/src/gui/styles/images/right-32.png
new file mode 100644
index 0000000000..0f6ba8608b
--- /dev/null
+++ b/src/gui/styles/images/right-32.png
Binary files differ
diff --git a/src/gui/styles/images/standardbutton-apply-128.png b/src/gui/styles/images/standardbutton-apply-128.png
new file mode 100644
index 0000000000..85f07a57ef
--- /dev/null
+++ b/src/gui/styles/images/standardbutton-apply-128.png
Binary files differ
diff --git a/src/gui/styles/images/standardbutton-apply-16.png b/src/gui/styles/images/standardbutton-apply-16.png
new file mode 100644
index 0000000000..8f11ce6504
--- /dev/null
+++ b/src/gui/styles/images/standardbutton-apply-16.png
Binary files differ
diff --git a/src/gui/styles/images/standardbutton-apply-32.png b/src/gui/styles/images/standardbutton-apply-32.png
new file mode 100644
index 0000000000..e8f7853a1e
--- /dev/null
+++ b/src/gui/styles/images/standardbutton-apply-32.png
Binary files differ
diff --git a/src/gui/styles/images/standardbutton-cancel-128.png b/src/gui/styles/images/standardbutton-cancel-128.png
new file mode 100644
index 0000000000..16d857030f
--- /dev/null
+++ b/src/gui/styles/images/standardbutton-cancel-128.png
Binary files differ
diff --git a/src/gui/styles/images/standardbutton-cancel-16.png b/src/gui/styles/images/standardbutton-cancel-16.png
new file mode 100644
index 0000000000..7bd25bd7c7
--- /dev/null
+++ b/src/gui/styles/images/standardbutton-cancel-16.png
Binary files differ
diff --git a/src/gui/styles/images/standardbutton-cancel-32.png b/src/gui/styles/images/standardbutton-cancel-32.png
new file mode 100644
index 0000000000..64a78727a1
--- /dev/null
+++ b/src/gui/styles/images/standardbutton-cancel-32.png
Binary files differ
diff --git a/src/gui/styles/images/standardbutton-clear-128.png b/src/gui/styles/images/standardbutton-clear-128.png
new file mode 100644
index 0000000000..107aea2234
--- /dev/null
+++ b/src/gui/styles/images/standardbutton-clear-128.png
Binary files differ
diff --git a/src/gui/styles/images/standardbutton-clear-16.png b/src/gui/styles/images/standardbutton-clear-16.png
new file mode 100644
index 0000000000..5359134c72
--- /dev/null
+++ b/src/gui/styles/images/standardbutton-clear-16.png
Binary files differ
diff --git a/src/gui/styles/images/standardbutton-clear-32.png b/src/gui/styles/images/standardbutton-clear-32.png
new file mode 100644
index 0000000000..8b85d6b7b3
--- /dev/null
+++ b/src/gui/styles/images/standardbutton-clear-32.png
Binary files differ
diff --git a/src/gui/styles/images/standardbutton-close-128.png b/src/gui/styles/images/standardbutton-close-128.png
new file mode 100644
index 0000000000..571aeae2bd
--- /dev/null
+++ b/src/gui/styles/images/standardbutton-close-128.png
Binary files differ
diff --git a/src/gui/styles/images/standardbutton-close-16.png b/src/gui/styles/images/standardbutton-close-16.png
new file mode 100644
index 0000000000..e9e481987a
--- /dev/null
+++ b/src/gui/styles/images/standardbutton-close-16.png
Binary files differ
diff --git a/src/gui/styles/images/standardbutton-close-32.png b/src/gui/styles/images/standardbutton-close-32.png
new file mode 100644
index 0000000000..47e5733062
--- /dev/null
+++ b/src/gui/styles/images/standardbutton-close-32.png
Binary files differ
diff --git a/src/gui/styles/images/standardbutton-closetab-16.png b/src/gui/styles/images/standardbutton-closetab-16.png
new file mode 100644
index 0000000000..540694eae3
--- /dev/null
+++ b/src/gui/styles/images/standardbutton-closetab-16.png
Binary files differ
diff --git a/src/gui/styles/images/standardbutton-closetab-down-16.png b/src/gui/styles/images/standardbutton-closetab-down-16.png
new file mode 100644
index 0000000000..ccec241652
--- /dev/null
+++ b/src/gui/styles/images/standardbutton-closetab-down-16.png
Binary files differ
diff --git a/src/gui/styles/images/standardbutton-closetab-hover-16.png b/src/gui/styles/images/standardbutton-closetab-hover-16.png
new file mode 100644
index 0000000000..b22a0ffaf0
--- /dev/null
+++ b/src/gui/styles/images/standardbutton-closetab-hover-16.png
Binary files differ
diff --git a/src/gui/styles/images/standardbutton-delete-128.png b/src/gui/styles/images/standardbutton-delete-128.png
new file mode 100644
index 0000000000..11947ba681
--- /dev/null
+++ b/src/gui/styles/images/standardbutton-delete-128.png
Binary files differ
diff --git a/src/gui/styles/images/standardbutton-delete-16.png b/src/gui/styles/images/standardbutton-delete-16.png
new file mode 100644
index 0000000000..63fe93fe98
--- /dev/null
+++ b/src/gui/styles/images/standardbutton-delete-16.png
Binary files differ
diff --git a/src/gui/styles/images/standardbutton-delete-32.png b/src/gui/styles/images/standardbutton-delete-32.png
new file mode 100644
index 0000000000..336d965d1c
--- /dev/null
+++ b/src/gui/styles/images/standardbutton-delete-32.png
Binary files differ
diff --git a/src/gui/styles/images/standardbutton-help-128.png b/src/gui/styles/images/standardbutton-help-128.png
new file mode 100644
index 0000000000..aa38e6fdfb
--- /dev/null
+++ b/src/gui/styles/images/standardbutton-help-128.png
Binary files differ
diff --git a/src/gui/styles/images/standardbutton-help-16.png b/src/gui/styles/images/standardbutton-help-16.png
new file mode 100644
index 0000000000..e8299418da
--- /dev/null
+++ b/src/gui/styles/images/standardbutton-help-16.png
Binary files differ
diff --git a/src/gui/styles/images/standardbutton-help-32.png b/src/gui/styles/images/standardbutton-help-32.png
new file mode 100644
index 0000000000..310056a632
--- /dev/null
+++ b/src/gui/styles/images/standardbutton-help-32.png
Binary files differ
diff --git a/src/gui/styles/images/standardbutton-no-128.png b/src/gui/styles/images/standardbutton-no-128.png
new file mode 100644
index 0000000000..491c048ebd
--- /dev/null
+++ b/src/gui/styles/images/standardbutton-no-128.png
Binary files differ
diff --git a/src/gui/styles/images/standardbutton-no-16.png b/src/gui/styles/images/standardbutton-no-16.png
new file mode 100644
index 0000000000..812d3f57dd
--- /dev/null
+++ b/src/gui/styles/images/standardbutton-no-16.png
Binary files differ
diff --git a/src/gui/styles/images/standardbutton-no-32.png b/src/gui/styles/images/standardbutton-no-32.png
new file mode 100644
index 0000000000..9548d59196
--- /dev/null
+++ b/src/gui/styles/images/standardbutton-no-32.png
Binary files differ
diff --git a/src/gui/styles/images/standardbutton-ok-128.png b/src/gui/styles/images/standardbutton-ok-128.png
new file mode 100644
index 0000000000..63cc5279ae
--- /dev/null
+++ b/src/gui/styles/images/standardbutton-ok-128.png
Binary files differ
diff --git a/src/gui/styles/images/standardbutton-ok-16.png b/src/gui/styles/images/standardbutton-ok-16.png
new file mode 100644
index 0000000000..fb4b4dbf96
--- /dev/null
+++ b/src/gui/styles/images/standardbutton-ok-16.png
Binary files differ
diff --git a/src/gui/styles/images/standardbutton-ok-32.png b/src/gui/styles/images/standardbutton-ok-32.png
new file mode 100644
index 0000000000..2dadd7a690
--- /dev/null
+++ b/src/gui/styles/images/standardbutton-ok-32.png
Binary files differ
diff --git a/src/gui/styles/images/standardbutton-open-128.png b/src/gui/styles/images/standardbutton-open-128.png
new file mode 100644
index 0000000000..8a052e829d
--- /dev/null
+++ b/src/gui/styles/images/standardbutton-open-128.png
Binary files differ
diff --git a/src/gui/styles/images/standardbutton-open-16.png b/src/gui/styles/images/standardbutton-open-16.png
new file mode 100644
index 0000000000..08cdc2b91f
--- /dev/null
+++ b/src/gui/styles/images/standardbutton-open-16.png
Binary files differ
diff --git a/src/gui/styles/images/standardbutton-open-32.png b/src/gui/styles/images/standardbutton-open-32.png
new file mode 100644
index 0000000000..db33c79852
--- /dev/null
+++ b/src/gui/styles/images/standardbutton-open-32.png
Binary files differ
diff --git a/src/gui/styles/images/standardbutton-save-128.png b/src/gui/styles/images/standardbutton-save-128.png
new file mode 100644
index 0000000000..fc6fd7ce1d
--- /dev/null
+++ b/src/gui/styles/images/standardbutton-save-128.png
Binary files differ
diff --git a/src/gui/styles/images/standardbutton-save-16.png b/src/gui/styles/images/standardbutton-save-16.png
new file mode 100644
index 0000000000..dd4e228280
--- /dev/null
+++ b/src/gui/styles/images/standardbutton-save-16.png
Binary files differ
diff --git a/src/gui/styles/images/standardbutton-save-32.png b/src/gui/styles/images/standardbutton-save-32.png
new file mode 100644
index 0000000000..177678c963
--- /dev/null
+++ b/src/gui/styles/images/standardbutton-save-32.png
Binary files differ
diff --git a/src/gui/styles/images/standardbutton-yes-128.png b/src/gui/styles/images/standardbutton-yes-128.png
new file mode 100644
index 0000000000..79c8296016
--- /dev/null
+++ b/src/gui/styles/images/standardbutton-yes-128.png
Binary files differ
diff --git a/src/gui/styles/images/standardbutton-yes-16.png b/src/gui/styles/images/standardbutton-yes-16.png
new file mode 100644
index 0000000000..cc16dbbec3
--- /dev/null
+++ b/src/gui/styles/images/standardbutton-yes-16.png
Binary files differ
diff --git a/src/gui/styles/images/standardbutton-yes-32.png b/src/gui/styles/images/standardbutton-yes-32.png
new file mode 100644
index 0000000000..e3340c6453
--- /dev/null
+++ b/src/gui/styles/images/standardbutton-yes-32.png
Binary files differ
diff --git a/src/gui/styles/images/stop-24.png b/src/gui/styles/images/stop-24.png
new file mode 100644
index 0000000000..99856e9640
--- /dev/null
+++ b/src/gui/styles/images/stop-24.png
Binary files differ
diff --git a/src/gui/styles/images/stop-32.png b/src/gui/styles/images/stop-32.png
new file mode 100644
index 0000000000..4f4952bb2a
--- /dev/null
+++ b/src/gui/styles/images/stop-32.png
Binary files differ
diff --git a/src/gui/styles/images/trash-128.png b/src/gui/styles/images/trash-128.png
new file mode 100644
index 0000000000..334fe5b6f3
--- /dev/null
+++ b/src/gui/styles/images/trash-128.png
Binary files differ
diff --git a/src/gui/styles/images/trash-16.png b/src/gui/styles/images/trash-16.png
new file mode 100644
index 0000000000..c471791ee8
--- /dev/null
+++ b/src/gui/styles/images/trash-16.png
Binary files differ
diff --git a/src/gui/styles/images/trash-32.png b/src/gui/styles/images/trash-32.png
new file mode 100644
index 0000000000..68625cf698
--- /dev/null
+++ b/src/gui/styles/images/trash-32.png
Binary files differ
diff --git a/src/gui/styles/images/up-128.png b/src/gui/styles/images/up-128.png
new file mode 100644
index 0000000000..c10df10677
--- /dev/null
+++ b/src/gui/styles/images/up-128.png
Binary files differ
diff --git a/src/gui/styles/images/up-16.png b/src/gui/styles/images/up-16.png
new file mode 100644
index 0000000000..33e939db8f
--- /dev/null
+++ b/src/gui/styles/images/up-16.png
Binary files differ
diff --git a/src/gui/styles/images/up-32.png b/src/gui/styles/images/up-32.png
new file mode 100644
index 0000000000..d7157c9476
--- /dev/null
+++ b/src/gui/styles/images/up-32.png
Binary files differ
diff --git a/src/gui/styles/images/viewdetailed-128.png b/src/gui/styles/images/viewdetailed-128.png
new file mode 100644
index 0000000000..363937a857
--- /dev/null
+++ b/src/gui/styles/images/viewdetailed-128.png
Binary files differ
diff --git a/src/gui/styles/images/viewdetailed-16.png b/src/gui/styles/images/viewdetailed-16.png
new file mode 100644
index 0000000000..44a14b923a
--- /dev/null
+++ b/src/gui/styles/images/viewdetailed-16.png
Binary files differ
diff --git a/src/gui/styles/images/viewdetailed-32.png b/src/gui/styles/images/viewdetailed-32.png
new file mode 100644
index 0000000000..fac1a3e683
--- /dev/null
+++ b/src/gui/styles/images/viewdetailed-32.png
Binary files differ
diff --git a/src/gui/styles/images/viewlist-128.png b/src/gui/styles/images/viewlist-128.png
new file mode 100644
index 0000000000..cc301059c1
--- /dev/null
+++ b/src/gui/styles/images/viewlist-128.png
Binary files differ
diff --git a/src/gui/styles/images/viewlist-16.png b/src/gui/styles/images/viewlist-16.png
new file mode 100644
index 0000000000..9132877ff6
--- /dev/null
+++ b/src/gui/styles/images/viewlist-16.png
Binary files differ
diff --git a/src/gui/styles/images/viewlist-32.png b/src/gui/styles/images/viewlist-32.png
new file mode 100644
index 0000000000..fae3c24536
--- /dev/null
+++ b/src/gui/styles/images/viewlist-32.png
Binary files differ
diff --git a/src/gui/styles/qcdestyle.cpp b/src/gui/styles/qcdestyle.cpp
new file mode 100644
index 0000000000..b58c41c1ae
--- /dev/null
+++ b/src/gui/styles/qcdestyle.cpp
@@ -0,0 +1,305 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qcdestyle.h"
+
+#if !defined(QT_NO_STYLE_CDE) || defined(QT_PLUGIN)
+
+#include "qmenu.h"
+#include "qapplication.h"
+#include "qpainter.h"
+#include "qdrawutil.h"
+#include "qpixmap.h"
+#include "qpalette.h"
+#include "qwidget.h"
+#include "qpushbutton.h"
+#include "qscrollbar.h"
+#include "qtabbar.h"
+#include "qtabwidget.h"
+#include "qlistview.h"
+#include "qsplitter.h"
+#include "qslider.h"
+#include "qcombobox.h"
+#include "qlineedit.h"
+#include "qprogressbar.h"
+#include "qimage.h"
+#include "qfocusframe.h"
+#include "qpainterpath.h"
+#include "qdebug.h"
+#include <limits.h>
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QCDEStyle
+ \brief The QCDEStyle class provides a CDE look and feel.
+
+ \ingroup appearance
+
+ This style provides a slightly improved Motif look similar to some
+ versions of the Common Desktop Environment (CDE). The main
+ differences are thinner frames and more modern radio buttons and
+ checkboxes. Together with a dark background and a bright
+ text/foreground color, the style looks quite attractive (at least
+ for Motif fans).
+
+ Note that most of the functions provided by QCDEStyle are
+ reimplementations of QStyle functions; see QStyle for their
+ documentation. QCDEStyle provides overloads for drawControl() and
+ drawPrimitive() which are documented here.
+
+ \img qcdestyle.png
+ \sa QWindowsXPStyle, QMacStyle, QWindowsStyle, QPlastiqueStyle, QMotifStyle
+*/
+
+/*!
+ Constructs a QCDEStyle.
+
+ If \a useHighlightCols is false (the default), then the style will
+ polish the application's color palette to emulate the Motif way of
+ highlighting, which is a simple inversion between the base and the
+ text color.
+*/
+QCDEStyle::QCDEStyle(bool useHighlightCols)
+ : QMotifStyle(useHighlightCols)
+{
+}
+
+/*!
+ Destroys the style.
+*/
+QCDEStyle::~QCDEStyle()
+{
+}
+
+
+/*!\reimp
+*/
+int QCDEStyle::pixelMetric(PixelMetric metric, const QStyleOption *option,
+ const QWidget *widget) const
+/*
+int QCDEStyle::pixelMetric(PixelMetric metric, const QStyleOption *option,
+ const QWidget *widget) const
+ */
+{
+ int ret = 0;
+
+ switch(metric) {
+ case PM_MenuBarPanelWidth:
+ case PM_DefaultFrameWidth:
+ case PM_FocusFrameVMargin:
+ case PM_FocusFrameHMargin:
+ case PM_MenuPanelWidth:
+ case PM_SpinBoxFrameWidth:
+ case PM_MenuBarVMargin:
+ case PM_MenuBarHMargin:
+ case PM_DockWidgetFrameWidth:
+ ret = 1;
+ break;
+ case PM_ScrollBarExtent:
+ ret = 13;
+ break;
+ default:
+ ret = QMotifStyle::pixelMetric(metric, option, widget);
+ break;
+ }
+ return ret;
+}
+
+/*!
+ \reimp
+*/
+void QCDEStyle::drawControl(ControlElement element, const QStyleOption *opt, QPainter *p,
+ const QWidget *widget) const
+{
+
+ switch(element) {
+ case CE_MenuBarItem: {
+ if (opt->state & State_Selected) // active item
+ qDrawShadePanel(p, opt->rect, opt->palette, true, 1,
+ &opt->palette.brush(QPalette::Button));
+ else // other item
+ p->fillRect(opt->rect, opt->palette.brush(QPalette::Button));
+ QCommonStyle::drawControl(element, opt, p, widget);
+ break; }
+ case CE_RubberBand: {
+ p->save();
+ p->setClipping(false);
+ QPainterPath path;
+ path.addRect(opt->rect);
+ path.addRect(opt->rect.adjusted(2, 2, -2, -2));
+ p->fillPath(path, opt->palette.color(QPalette::Active, QPalette::Text));
+ p->restore();
+ break; }
+ default:
+ QMotifStyle::drawControl(element, opt, p, widget);
+ break;
+ }
+}
+
+/*!
+ \reimp
+*/
+void QCDEStyle::drawPrimitive(PrimitiveElement pe, const QStyleOption *opt, QPainter *p,
+ const QWidget *widget) const
+{
+ switch(pe) {
+ case PE_IndicatorCheckBox: {
+ bool down = opt->state & State_Sunken;
+ bool on = opt->state & State_On;
+ bool showUp = !(down ^ on);
+ QBrush fill = (showUp || (opt->state & State_NoChange)) ? opt->palette.brush(QPalette::Button) : opt->palette.brush(QPalette::Mid);
+ qDrawShadePanel(p, opt->rect, opt->palette, !showUp, pixelMetric(PM_DefaultFrameWidth), &opt->palette.brush(QPalette::Button));
+
+ if (on || (opt->state & State_NoChange)) {
+ QRect r = opt->rect;
+ QPolygon a(7 * 2);
+ int i, xx, yy;
+ xx = r.x() + 3;
+ yy = r.y() + 5;
+ if (opt->rect.width() <= 9) {
+ // When called from CE_MenuItem in QMotifStyle
+ xx -= 2;
+ yy -= 2;
+ }
+
+ for (i = 0; i < 3; i++) {
+ a.setPoint(2 * i, xx, yy);
+ a.setPoint(2 * i + 1, xx, yy + 2);
+ xx++; yy++;
+ }
+ yy -= 2;
+ for (i = 3; i < 7; i++) {
+ a.setPoint(2 * i, xx, yy);
+ a.setPoint(2 * i + 1, xx, yy + 2);
+ xx++; yy--;
+ }
+ if (opt->state & State_NoChange)
+ p->setPen(opt->palette.dark().color());
+ else
+ p->setPen(opt->palette.foreground().color());
+ p->drawPolyline(a);
+ }
+ if (!(opt->state & State_Enabled) && styleHint(SH_DitherDisabledText))
+ p->fillRect(opt->rect, QBrush(p->background().color(), Qt::Dense5Pattern));
+ } break;
+ case PE_IndicatorRadioButton:
+ {
+ QRect r = opt->rect;
+#define INTARRLEN(x) sizeof(x)/(sizeof(int)*2)
+ static const int pts1[] = { // up left lines
+ 1,9, 1,8, 0,7, 0,4, 1,3, 1,2, 2,1, 3,1, 4,0, 7,0, 8,1, 9,1 };
+ static const int pts4[] = { // bottom right lines
+ 2,10, 3,10, 4,11, 7,11, 8,10, 9,10, 10,9, 10,8, 11,7,
+ 11,4, 10,3, 10,2 };
+ static const int pts5[] = { // inner fill
+ 4,2, 7,2, 9,4, 9,7, 7,9, 4,9, 2,7, 2,4 };
+ bool down = opt->state & State_Sunken;
+ bool on = opt->state & State_On;
+ QPolygon a(INTARRLEN(pts1), pts1);
+
+ //center when rect is larger than indicator size
+ int xOffset = 0;
+ int yOffset = 0;
+ int indicatorWidth = pixelMetric(PM_ExclusiveIndicatorWidth);
+ int indicatorHeight = pixelMetric(PM_ExclusiveIndicatorWidth);
+ if (r.width() > indicatorWidth)
+ xOffset += (r.width() - indicatorWidth)/2;
+ if (r.height() > indicatorHeight)
+ yOffset += (r.height() - indicatorHeight)/2;
+ p->translate(xOffset, yOffset);
+
+ a.translate(r.x(), r.y());
+ QPen oldPen = p->pen();
+ QBrush oldBrush = p->brush();
+ p->setPen((down || on) ? opt->palette.dark().color() : opt->palette.light().color());
+ p->drawPolyline(a);
+ a.setPoints(INTARRLEN(pts4), pts4);
+ a.translate(r.x(), r.y());
+ p->setPen((down || on) ? opt->palette.light().color() : opt->palette.dark().color());
+ p->drawPolyline(a);
+ a.setPoints(INTARRLEN(pts5), pts5);
+ a.translate(r.x(), r.y());
+ QColor fillColor = on ? opt->palette.dark().color() : opt->palette.background().color();
+ p->setPen(fillColor);
+ p->setBrush(on ? opt->palette.brush(QPalette::Dark) :
+ opt->palette.brush(QPalette::Window));
+ p->drawPolygon(a);
+ if (!(opt->state & State_Enabled) && styleHint(SH_DitherDisabledText))
+ p->fillRect(opt->rect, QBrush(p->background().color(), Qt::Dense5Pattern));
+ p->setPen(oldPen);
+ p->setBrush(oldBrush);
+
+ p->translate(-xOffset, -yOffset);
+
+ } break;
+ default:
+ QMotifStyle::drawPrimitive(pe, opt, p, widget);
+ }
+}
+
+/*!\reimp*/
+QPalette QCDEStyle::standardPalette() const
+{
+ QColor background(0xb6, 0xb6, 0xcf);
+ QColor light = background.lighter();
+ QColor mid = background.darker(150);
+ QColor dark = background.darker();
+ QPalette palette(Qt::black, background, light, dark, mid, Qt::black, Qt::white);
+ palette.setBrush(QPalette::Disabled, QPalette::WindowText, dark);
+ palette.setBrush(QPalette::Disabled, QPalette::Text, dark);
+ palette.setBrush(QPalette::Disabled, QPalette::ButtonText, dark);
+ palette.setBrush(QPalette::Disabled, QPalette::Base, background);
+ return palette;
+}
+
+/*!
+ \internal
+*/
+QIcon QCDEStyle::standardIconImplementation(StandardPixmap standardIcon, const QStyleOption *opt,
+ const QWidget *widget) const
+{
+ return QMotifStyle::standardIconImplementation(standardIcon, opt, widget);
+}
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/gui/styles/qcdestyle.h b/src/gui/styles/qcdestyle.h
new file mode 100644
index 0000000000..bb752d3064
--- /dev/null
+++ b/src/gui/styles/qcdestyle.h
@@ -0,0 +1,82 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+
+#ifndef QCDESTYLE_H
+#define QCDESTYLE_H
+
+#include <QtGui/qmotifstyle.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+#if !defined(QT_NO_STYLE_CDE)
+
+class Q_GUI_EXPORT QCDEStyle : public QMotifStyle
+{
+ Q_OBJECT
+public:
+ explicit QCDEStyle(bool useHighlightCols = false);
+ virtual ~QCDEStyle();
+
+ int pixelMetric(PixelMetric metric, const QStyleOption *option = 0,
+ const QWidget *widget = 0) const;
+ void drawControl(ControlElement element, const QStyleOption *opt, QPainter *p,
+ const QWidget *w = 0) const;
+ void drawPrimitive(PrimitiveElement pe, const QStyleOption *opt, QPainter *p,
+ const QWidget *w = 0) const;
+ QPalette standardPalette() const;
+
+protected Q_SLOTS:
+ QIcon standardIconImplementation(StandardPixmap standardIcon, const QStyleOption *opt = 0,
+ const QWidget *widget = 0) const;
+};
+
+#endif // QT_NO_STYLE_CDE
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QCDESTYLE_H
diff --git a/src/gui/styles/qcleanlooksstyle.cpp b/src/gui/styles/qcleanlooksstyle.cpp
new file mode 100644
index 0000000000..468ada9484
--- /dev/null
+++ b/src/gui/styles/qcleanlooksstyle.cpp
@@ -0,0 +1,4945 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qcleanlooksstyle.h"
+#include "qcleanlooksstyle_p.h"
+
+#if !defined(QT_NO_STYLE_CLEANLOOKS) || defined(QT_PLUGIN)
+
+#include "qwindowsstyle_p.h"
+#include <qcombobox.h>
+#include <qpushbutton.h>
+#include <qpainter.h>
+#include <qdir.h>
+#include <qhash.h>
+#include <qstyleoption.h>
+#include <qapplication.h>
+#include <qmainwindow.h>
+#include <qfont.h>
+#include <qgroupbox.h>
+#include <qprocess.h>
+#include <qpixmapcache.h>
+#include <qdialogbuttonbox.h>
+#include <qscrollbar.h>
+#include <qspinbox.h>
+#include <qslider.h>
+#include <qsplitter.h>
+#include <qprogressbar.h>
+#include <qtoolbar.h>
+#include <qwizard.h>
+#include <qlibrary.h>
+
+#define CL_MAX(a,b) (a)>(b) ? (a):(b) // ### qMin/qMax does not work for vc6
+#define CL_MIN(a,b) (a)<(b) ? (a):(b) // remove this when it is working
+
+QT_BEGIN_NAMESPACE
+
+static const bool UsePixmapCache = true;
+
+enum Direction {
+ TopDown,
+ FromLeft,
+ BottomUp,
+ FromRight
+};
+
+// from windows style
+static const int windowsItemFrame = 2; // menu item frame width
+static const int windowsSepHeight = 6; // separator item height
+static const int windowsItemHMargin = 3; // menu item hor text margin
+static const int windowsItemVMargin = 8; // menu item ver text margin
+static const int windowsArrowHMargin = 6; // arrow horizontal margin
+static const int windowsTabSpacing = 12; // space between text and tab
+static const int windowsCheckMarkHMargin = 2; // horiz. margins of check mark
+static const int windowsRightBorder = 15; // right border on windows
+static const int windowsCheckMarkWidth = 12; // checkmarks width on windows
+
+/* XPM */
+static const char * const dock_widget_close_xpm[] = {
+ "11 13 7 1",
+ " c None",
+ ". c #D5CFCB",
+ "+ c #8F8B88",
+ "@ c #6C6A67",
+ "# c #ABA6A3",
+ "$ c #B5B0AC",
+ "% c #A4A09D",
+ " ",
+ " +@@@@@@@+ ",
+ "+# #+",
+ "@ $@ @$ @",
+ "@ @@@ @@@ @",
+ "@ @@@@@ @",
+ "@ @@@ @",
+ "@ @@@@@ @",
+ "@ @@@ @@@ @",
+ "@ $@ @$ @",
+ "+% #+",
+ " +@@@@@@@+ ",
+ " "};
+
+static const char * const qt_cleanlooks_arrow_down_xpm[] = {
+ "11 7 2 1",
+ " c None",
+ "x c #000000",
+ " ",
+ " x x ",
+ " xxx xxx ",
+ " xxxxxxx ",
+ " xxxxx ",
+ " xxx ",
+ " x "};
+
+static const char * const qt_cleanlooks_arrow_up_xpm[] = {
+ "11 7 2 1",
+ " c None",
+ "x c #000000",
+ " x ",
+ " xxx ",
+ " xxxxx ",
+ " xxxxxxx ",
+ " xxx xxx ",
+ " x x ",
+ " "};
+
+static const char * const dock_widget_restore_xpm[] = {
+ "11 13 7 1",
+ " c None",
+ ". c #D5CFCB",
+ "+ c #8F8B88",
+ "@ c #6C6A67",
+ "# c #ABA6A3",
+ "$ c #B5B0AC",
+ "% c #A4A09D",
+ " ",
+ " +@@@@@@@+ ",
+ "+# #+",
+ "@ #@@@# @",
+ "@ @ @ @",
+ "@ #@@@# @ @",
+ "@ @ @ @ @",
+ "@ @ @@@ @",
+ "@ @ @ @",
+ "@ #@@@# @",
+ "+% #+",
+ " +@@@@@@@+ ",
+ " "};
+
+static const char * const workspace_minimize[] = {
+ "11 13 7 1",
+ " c None",
+ ". c #D5CFCB",
+ "+ c #8F8B88",
+ "@ c #6C6A67",
+ "# c #ABA6A3",
+ "$ c #B5B0AC",
+ "% c #A4A09D",
+ " ",
+ " +@@@@@@@+ ",
+ "+# #+",
+ "@ @",
+ "@ @",
+ "@ @",
+ "@ @@@@@@@ @",
+ "@ @@@@@@@ @",
+ "@ @",
+ "@ @",
+ "+% #+",
+ " +@@@@@@@+ ",
+ " "};
+
+
+static const char * const qt_titlebar_context_help[] = {
+ "10 10 3 1",
+ " c None",
+ "# c #000000",
+ "+ c #444444",
+ " +####+ ",
+ " ### ### ",
+ " ## ## ",
+ " +##+ ",
+ " +## ",
+ " ## ",
+ " ## ",
+ " ",
+ " ## ",
+ " ## "};
+
+static const char * const qt_cleanlooks_radiobutton[] = {
+ "13 13 9 1",
+ " c None",
+ ". c #ABA094",
+ "+ c #B7ADA0",
+ "@ c #C4BBB2",
+ "# c #DDD4CD",
+ "$ c #E7E1E0",
+ "% c #F4EFED",
+ "& c #FFFAF9",
+ "* c #FCFEFB",
+ " #@...@# ",
+ " @+@#$$#+@ ",
+ " @+$%%***&@@ ",
+ "#+$%**&&**&+#",
+ "@@$&&******#@",
+ ".#**********.",
+ ".$&******&*&.",
+ ".$*&******&*.",
+ "+#********&#@",
+ "#+*********+#",
+ " @@*******@@ ",
+ " @+#%*%#+@ ",
+ " #@...+# "};
+
+static const char * const qt_cleanlooks_radiobutton_checked[] = {
+ "13 13 20 1",
+ " c None",
+ ". c #A8ABAE",
+ "+ c #596066",
+ "@ c #283138",
+ "# c #A9ACAF",
+ "$ c #A6A9AB",
+ "% c #6B7378",
+ "& c #8C9296",
+ "* c #A2A6AA",
+ "= c #61696F",
+ "- c #596065",
+ "; c #93989C",
+ "> c #777E83",
+ ", c #60686E",
+ "' c #252D33",
+ ") c #535B62",
+ "! c #21292E",
+ "~ c #242B31",
+ "{ c #1F262B",
+ "] c #41484E",
+ " ",
+ " ",
+ " ",
+ " .+@+# ",
+ " $%&*&=# ",
+ " -&;>,'+ ",
+ " @*>,)!@ ",
+ " +&,)~{+ ",
+ " #='!{]# ",
+ " #+@+# ",
+ " ",
+ " ",
+ " "};
+
+
+static const char * const qt_scrollbar_button_arrow_left[] = {
+ "4 7 2 1",
+ " c None",
+ "* c #BFBFBF",
+ " *",
+ " **",
+ " ***",
+ "****",
+ " ***",
+ " **",
+ " *"};
+
+static const char * const qt_scrollbar_button_arrow_right[] = {
+ "4 7 2 1",
+ " c None",
+ "* c #BFBFBF",
+ "* ",
+ "** ",
+ "*** ",
+ "****",
+ "*** ",
+ "** ",
+ "* "};
+
+static const char * const qt_scrollbar_button_arrow_up[] = {
+ "7 4 2 1",
+ " c None",
+ "* c #BFBFBF",
+ " * ",
+ " *** ",
+ " ***** ",
+ "*******"};
+
+static const char * const qt_scrollbar_button_arrow_down[] = {
+ "7 4 2 1",
+ " c None",
+ "* c #BFBFBF",
+ "*******",
+ " ***** ",
+ " *** ",
+ " * "};
+
+static const char * const qt_spinbox_button_arrow_down[] = {
+ "7 4 2 1",
+ " c None",
+ "* c #BFBFBF",
+ "*******",
+ " ***** ",
+ " *** ",
+ " * "};
+
+static const char * const qt_spinbox_button_arrow_up[] = {
+ "7 4 2 1",
+ " c None",
+ "* c #BFBFBF",
+ " * ",
+ " *** ",
+ " ***** ",
+ "*******"};
+
+static const char * const qt_scrollbar_button_left[] = {
+ "16 16 6 1",
+ " c None",
+ ". c #BFBFBF",
+ "+ c #979797",
+ "# c #FAFAFA",
+ "< c #FAFAFA",
+ "* c #FAFAFA",
+ " .++++++++++++++",
+ ".+#############+",
+ "+# <+",
+ "+# <+",
+ "+# <+",
+ "+# <+",
+ "+# <+",
+ "+# <+",
+ "+# <+",
+ "+# <+",
+ "+# <+",
+ "+# <+",
+ "+# <+",
+ "+# <+",
+ ".+<<<<<<<<<<<<<+",
+ " .++++++++++++++"};
+
+static const char * const qt_scrollbar_button_right[] = {
+ "16 16 6 1",
+ " c None",
+ ". c #BFBFBF",
+ "+ c #979797",
+ "# c #FAFAFA",
+ "< c #FAFAFA",
+ "* c #FAFAFA",
+ "++++++++++++++. ",
+ "+#############+.",
+ "+# <+",
+ "+# <+",
+ "+# <+",
+ "+# <+",
+ "+# <+",
+ "+# <+",
+ "+# <+",
+ "+# <+",
+ "+# <+",
+ "+# <+",
+ "+# <+",
+ "+# <+",
+ "+<<<<<<<<<<<<<+.",
+ "++++++++++++++. "};
+
+static const char * const qt_scrollbar_button_up[] = {
+ "16 16 6 1",
+ " c None",
+ ". c #BFBFBF",
+ "+ c #979797",
+ "# c #FAFAFA",
+ "< c #FAFAFA",
+ "* c #FAFAFA",
+ " .++++++++++++. ",
+ ".+############+.",
+ "+# <+",
+ "+# <+",
+ "+# <+",
+ "+# <+",
+ "+# <+",
+ "+# <+",
+ "+# <+",
+ "+# <+",
+ "+# <+",
+ "+# <+",
+ "+# <+",
+ "+# <+",
+ "+<<<<<<<<<<<<<<+",
+ "++++++++++++++++"};
+
+static const char * const qt_scrollbar_button_down[] = {
+ "16 16 6 1",
+ " c None",
+ ". c #BFBFBF",
+ "+ c #979797",
+ "# c #FAFAFA",
+ "< c #FAFAFA",
+ "* c #FAFAFA",
+ "++++++++++++++++",
+ "+##############+",
+ "+# <+",
+ "+# <+",
+ "+# <+",
+ "+# <+",
+ "+# <+",
+ "+# <+",
+ "+# <+",
+ "+# <+",
+ "+# <+",
+ "+# <+",
+ "+# <+",
+ "+# <+",
+ ".+<<<<<<<<<<<<+.",
+ " .++++++++++++. "};
+
+static const char * const qt_cleanlooks_menuitem_checkbox_checked[] = {
+ "8 7 6 1",
+ " g None",
+ ". g #959595",
+ "+ g #676767",
+ "@ g #454545",
+ "# g #1D1D1D",
+ "0 g #101010",
+ " ..",
+ " .+ ",
+ " .+ ",
+ "0 .@ ",
+ "@#++. ",
+ " @# ",
+ " . "};
+
+static const char * const qt_cleanlooks_checkbox_checked[] = {
+ "13 13 3 1",
+ " c None",
+ ". c #272D33",
+ "% c #666666",
+
+ " ",
+ " % ",
+ " %. ",
+ " %.% ",
+ " %.. ",
+ " %.% %.. ",
+ " %..%..% ",
+ " %...% ",
+ " %..% ",
+ " %.% ",
+ " % ",
+ " ",
+ " "};
+
+#ifdef Q_WS_X11
+extern "C" {
+ struct GConfClient;
+ struct GError;
+ typedef void (*Ptr_g_type_init)();
+ typedef GConfClient* (*Ptr_gconf_client_get_default)();
+ typedef char* (*Ptr_gconf_client_get_string)(GConfClient*, const char*, GError **);
+ typedef void (*Ptr_g_object_unref)(void *);
+ typedef void (*Ptr_g_error_free)(GError *);
+ typedef void (*Ptr_g_free)(void*);
+}
+
+static Ptr_g_type_init p_g_type_init = 0;
+static Ptr_gconf_client_get_default p_gconf_client_get_default = 0;
+static Ptr_gconf_client_get_string p_gconf_client_get_string = 0;
+static Ptr_g_object_unref p_g_object_unref = 0;
+static Ptr_g_error_free p_g_error_free = 0;
+static Ptr_g_free p_g_free = 0;
+#endif
+
+static void qt_cleanlooks_draw_gradient(QPainter *painter, const QRect &rect, const QColor &gradientStart,
+ const QColor &gradientStop, Direction direction = TopDown, QBrush bgBrush = QBrush())
+{
+ int x = rect.center().x();
+ int y = rect.center().y();
+ QLinearGradient *gradient;
+ switch(direction) {
+ case FromLeft:
+ gradient = new QLinearGradient(rect.left(), y, rect.right(), y);
+ break;
+ case FromRight:
+ gradient = new QLinearGradient(rect.right(), y, rect.left(), y);
+ break;
+ case BottomUp:
+ gradient = new QLinearGradient(x, rect.bottom(), x, rect.top());
+ break;
+ case TopDown:
+ default:
+ gradient = new QLinearGradient(x, rect.top(), x, rect.bottom());
+ break;
+ }
+ if (bgBrush.gradient())
+ gradient->setStops(bgBrush.gradient()->stops());
+ else {
+ gradient->setColorAt(0, gradientStart);
+ gradient->setColorAt(1, gradientStop);
+ }
+ painter->fillRect(rect, *gradient);
+ delete gradient;
+}
+
+static void qt_cleanlooks_draw_buttongradient(QPainter *painter, const QRect &rect, const QColor &gradientStart,
+ const QColor &gradientMid, const QColor &gradientStop, Direction direction = TopDown,
+ QBrush bgBrush = QBrush())
+{
+ int x = rect.center().x();
+ int y = rect.center().y();
+ QLinearGradient *gradient;
+ bool horizontal = false;
+ switch(direction) {
+ case FromLeft:
+ horizontal = true;
+ gradient = new QLinearGradient(rect.left(), y, rect.right(), y);
+ break;
+ case FromRight:
+ horizontal = true;
+ gradient = new QLinearGradient(rect.right(), y, rect.left(), y);
+ break;
+ case BottomUp:
+ gradient = new QLinearGradient(x, rect.bottom(), x, rect.top());
+ break;
+ case TopDown:
+ default:
+ gradient = new QLinearGradient(x, rect.top(), x, rect.bottom());
+ break;
+ }
+ if (bgBrush.gradient())
+ gradient->setStops(bgBrush.gradient()->stops());
+ else {
+ int size = horizontal ? rect.width() : rect.height() ;
+ if (size < 1)
+ size = 1;
+ float edge = 4.0/(float)size;
+ gradient->setColorAt(0, gradientStart);
+ gradient->setColorAt(edge, gradientMid.lighter(104));
+ gradient->setColorAt(1.0 - edge, gradientMid.darker(100));
+ gradient->setColorAt(1.0, gradientStop);
+ }
+ painter->fillRect(rect, *gradient);
+ delete gradient;
+}
+
+static QString uniqueName(const QString &key, const QStyleOption *option, const QSize &size)
+{
+ QString tmp;
+ const QStyleOptionComplex *complexOption = qstyleoption_cast<const QStyleOptionComplex *>(option);
+ tmp.sprintf("%s-%d-%d-%lld-%dx%d-%d", key.toLatin1().constData(), uint(option->state),
+ complexOption ? uint(complexOption->activeSubControls) : uint(0),
+ option->palette.cacheKey(), size.width(), size.height(), option->direction);
+#ifndef QT_NO_SPINBOX
+ if (const QStyleOptionSpinBox *spinBox = qstyleoption_cast<const QStyleOptionSpinBox *>(option)) {
+ tmp.append(QLatin1Char('-'));
+ tmp.append(QString::number(spinBox->buttonSymbols));
+ tmp.append(QLatin1Char('-'));
+ tmp.append(QString::number(spinBox->stepEnabled));
+ tmp.append(QLatin1Char('-'));
+ tmp.append(QLatin1Char(spinBox->frame ? '1' : '0'));
+ }
+#endif // QT_NO_SPINBOX
+ return tmp;
+}
+
+static void qt_cleanlooks_draw_mdibutton(QPainter *painter, const QStyleOptionTitleBar *option, const QRect &tmp, bool hover, bool sunken)
+{
+ QColor dark;
+ dark.setHsv(option->palette.button().color().hue(),
+ CL_MIN(255, (int)(option->palette.button().color().saturation()*1.9)),
+ CL_MIN(255, (int)(option->palette.button().color().value()*0.7)));
+
+ QColor highlight = option->palette.highlight().color();
+
+ bool active = (option->titleBarState & QStyle::State_Active);
+ QColor titleBarHighlight(255, 255, 255, 60);
+
+ if (sunken)
+ painter->fillRect(tmp.adjusted(1, 1, -1, -1), option->palette.highlight().color().darker(120));
+ else if (hover)
+ painter->fillRect(tmp.adjusted(1, 1, -1, -1), QColor(255, 255, 255, 20));
+
+ QColor mdiButtonGradientStartColor;
+ QColor mdiButtonGradientStopColor;
+
+ mdiButtonGradientStartColor = QColor(0, 0, 0, 40);
+ mdiButtonGradientStopColor = QColor(255, 255, 255, 60);
+
+ if (sunken)
+ titleBarHighlight = highlight.darker(130);
+
+ QLinearGradient gradient(tmp.center().x(), tmp.top(), tmp.center().x(), tmp.bottom());
+ gradient.setColorAt(0, mdiButtonGradientStartColor);
+ gradient.setColorAt(1, mdiButtonGradientStopColor);
+ QColor mdiButtonBorderColor(active ? option->palette.highlight().color().darker(180): dark.darker(110));
+
+ painter->setPen(QPen(mdiButtonBorderColor, 1));
+ painter->drawLine(tmp.left() + 2, tmp.top(), tmp.right() - 2, tmp.top());
+ painter->drawLine(tmp.left() + 2, tmp.bottom(), tmp.right() - 2, tmp.bottom());
+ painter->drawLine(tmp.left(), tmp.top() + 2, tmp.left(), tmp.bottom() - 2);
+ painter->drawLine(tmp.right(), tmp.top() + 2, tmp.right(), tmp.bottom() - 2);
+ painter->drawPoint(tmp.left() + 1, tmp.top() + 1);
+ painter->drawPoint(tmp.right() - 1, tmp.top() + 1);
+ painter->drawPoint(tmp.left() + 1, tmp.bottom() - 1);
+ painter->drawPoint(tmp.right() - 1, tmp.bottom() - 1);
+
+ painter->setPen(titleBarHighlight);
+ painter->drawLine(tmp.left() + 2, tmp.top() + 1, tmp.right() - 2, tmp.top() + 1);
+ painter->drawLine(tmp.left() + 1, tmp.top() + 2, tmp.left() + 1, tmp.bottom() - 2);
+
+ painter->setPen(QPen(gradient, 1));
+ painter->drawLine(tmp.right() + 1, tmp.top() + 2, tmp.right() + 1, tmp.bottom() - 2);
+ painter->drawPoint(tmp.right() , tmp.top() + 1);
+
+ painter->drawLine(tmp.left() + 2, tmp.bottom() + 1, tmp.right() - 2, tmp.bottom() + 1);
+ painter->drawPoint(tmp.left() + 1, tmp.bottom());
+ painter->drawPoint(tmp.right() - 1, tmp.bottom());
+ painter->drawPoint(tmp.right() , tmp.bottom() - 1);
+}
+
+/*!
+ \class QCleanlooksStyle
+ \brief The QCleanlooksStyle class provides a widget style similar to the
+ Clearlooks style available in GNOME.
+ \since 4.2
+
+ The Cleanlooks style provides a look and feel for widgets
+ that closely resembles the Clearlooks style, introduced by Richard
+ Stellingwerff and Daniel Borgmann.
+
+ \sa {Cleanlooks Style Widget Gallery}, QWindowsXPStyle, QMacStyle, QWindowsStyle,
+ QCDEStyle, QMotifStyle, QPlastiqueStyle
+*/
+
+/*!
+ Constructs a QCleanlooksStyle object.
+*/
+QCleanlooksStyle::QCleanlooksStyle() : QWindowsStyle(*new QCleanlooksStylePrivate)
+{
+ setObjectName(QLatin1String("CleanLooks"));
+}
+
+/*!
+ \internal
+
+ Constructs a QCleanlooksStyle object.
+*/
+QCleanlooksStyle::QCleanlooksStyle(QCleanlooksStylePrivate &dd) : QWindowsStyle(dd)
+{
+}
+
+/*!
+ Destroys the QCleanlooksStyle object.
+*/
+QCleanlooksStyle::~QCleanlooksStyle()
+{
+}
+
+/*!
+ \fn void QCleanlooksStyle::drawItemText(QPainter *painter, const QRect &rectangle, int alignment, const QPalette &palette,
+ bool enabled, const QString& text, QPalette::ColorRole textRole) const
+
+ Draws the given \a text in the specified \a rectangle using the
+ provided \a painter and \a palette.
+
+ Text is drawn using the painter's pen. If an explicit \a textRole
+ is specified, then the text is drawn using the \a palette's color
+ for the specified role. The \a enabled value indicates whether or
+ not the item is enabled; when reimplementing, this value should
+ influence how the item is drawn.
+
+ The text is aligned and wrapped according to the specified \a
+ alignment.
+
+ \sa Qt::Alignment
+*/
+void QCleanlooksStyle::drawItemText(QPainter *painter, const QRect &rect, int alignment, const QPalette &pal,
+ bool enabled, const QString& text, QPalette::ColorRole textRole) const
+{
+ if (text.isEmpty())
+ return;
+
+ QPen savedPen = painter->pen();
+ if (textRole != QPalette::NoRole) {
+ painter->setPen(QPen(pal.brush(textRole), savedPen.widthF()));
+ }
+ if (!enabled) {
+ QPen pen = painter->pen();
+ painter->setPen(pen);
+ }
+ painter->drawText(rect, alignment, text);
+ painter->setPen(savedPen);
+}
+
+static QColor mergedColors(const QColor &colorA, const QColor &colorB, int factor = 50)
+{
+ const int maxFactor = 100;
+ QColor tmp = colorA;
+ tmp.setRed((tmp.red() * factor) / maxFactor + (colorB.red() * (maxFactor - factor)) / maxFactor);
+ tmp.setGreen((tmp.green() * factor) / maxFactor + (colorB.green() * (maxFactor - factor)) / maxFactor);
+ tmp.setBlue((tmp.blue() * factor) / maxFactor + (colorB.blue() * (maxFactor - factor)) / maxFactor);
+ return tmp;
+}
+
+/*!
+ \reimp
+*/
+void QCleanlooksStyle::drawPrimitive(PrimitiveElement elem,
+ const QStyleOption *option,
+ QPainter *painter, const QWidget *widget) const
+{
+ Q_ASSERT(option);
+ QRect rect = option->rect;
+ int state = option->state;
+ QColor button = option->palette.button().color();
+ QColor buttonShadow = option->palette.button().color().darker(110);
+ QColor buttonShadowAlpha = buttonShadow;
+ buttonShadowAlpha.setAlpha(128);
+ QColor darkOutline;
+ QColor dark;
+ darkOutline.setHsv(button.hue(),
+ CL_MIN(255, (int)(button.saturation()*3.0)),
+ CL_MIN(255, (int)(button.value()*0.6)));
+ dark.setHsv(button.hue(),
+ CL_MIN(255, (int)(button.saturation()*1.9)),
+ CL_MIN(255, (int)(button.value()*0.7)));
+ QColor tabFrameColor = mergedColors(option->palette.background().color(),
+ dark.lighter(135), 60);
+
+ switch(elem) {
+#ifndef QT_NO_TABBAR
+ case PE_FrameTabBarBase:
+ if (const QStyleOptionTabBarBase *tbb
+ = qstyleoption_cast<const QStyleOptionTabBarBase *>(option)) {
+ painter->save();
+ painter->setPen(QPen(darkOutline.lighter(110), 0));
+ switch (tbb->shape) {
+ case QTabBar::RoundedNorth: {
+ QRegion region(tbb->rect);
+ region -= tbb->selectedTabRect;
+ painter->drawLine(tbb->rect.topLeft(), tbb->rect.topRight());
+ painter->setClipRegion(region);
+ painter->setPen(option->palette.light().color());
+ painter->drawLine(tbb->rect.topLeft() + QPoint(0, 1),
+ tbb->rect.topRight() + QPoint(0, 1));
+ }
+ break;
+ case QTabBar::RoundedWest:
+ painter->drawLine(tbb->rect.left(), tbb->rect.top(), tbb->rect.left(), tbb->rect.bottom());
+ break;
+ case QTabBar::RoundedSouth:
+ painter->drawLine(tbb->rect.left(), tbb->rect.bottom(),
+ tbb->rect.right(), tbb->rect.bottom());
+ break;
+ case QTabBar::RoundedEast:
+ painter->drawLine(tbb->rect.topRight(), tbb->rect.bottomRight());
+ break;
+ case QTabBar::TriangularNorth:
+ case QTabBar::TriangularEast:
+ case QTabBar::TriangularWest:
+ case QTabBar::TriangularSouth:
+ painter->restore();
+ QWindowsStyle::drawPrimitive(elem, option, painter, widget);
+ return;
+ }
+ painter->restore();
+ }
+ return;
+#endif // QT_NO_TABBAR
+ case PE_IndicatorViewItemCheck:
+ {
+ QStyleOptionButton button;
+ button.QStyleOption::operator=(*option);
+ button.state &= ~State_MouseOver;
+ drawPrimitive(PE_IndicatorCheckBox, &button, painter, widget);
+ }
+ return;
+ case PE_IndicatorHeaderArrow:
+ if (const QStyleOptionHeader *header = qstyleoption_cast<const QStyleOptionHeader *>(option)) {
+ QRect r = header->rect;
+ QImage arrow;
+ if (header->sortIndicator & QStyleOptionHeader::SortUp)
+ arrow = QImage(qt_cleanlooks_arrow_up_xpm);
+ else if (header->sortIndicator & QStyleOptionHeader::SortDown)
+ arrow = QImage(qt_cleanlooks_arrow_down_xpm);
+ if (!arrow.isNull()) {
+ r.setSize(arrow.size());
+ r.moveCenter(header->rect.center());
+ arrow.setColor(1, header->palette.foreground().color().rgba());
+ painter->drawImage(r, arrow);
+ }
+ }
+ break;
+ case PE_IndicatorButtonDropDown:
+ drawPrimitive(PE_PanelButtonCommand, option, painter, widget);
+ break;
+ case PE_IndicatorToolBarSeparator:
+ {
+ QRect rect = option->rect;
+ const int margin = 6;
+ if (option->state & State_Horizontal) {
+ const int offset = rect.width()/2;
+ painter->setPen(QPen(option->palette.background().color().darker(110)));
+ painter->drawLine(rect.bottomLeft().x() + offset,
+ rect.bottomLeft().y() - margin,
+ rect.topLeft().x() + offset,
+ rect.topLeft().y() + margin);
+ painter->setPen(QPen(option->palette.background().color().lighter(110)));
+ painter->drawLine(rect.bottomLeft().x() + offset + 1,
+ rect.bottomLeft().y() - margin,
+ rect.topLeft().x() + offset + 1,
+ rect.topLeft().y() + margin);
+ } else { //Draw vertical separator
+ const int offset = rect.height()/2;
+ painter->setPen(QPen(option->palette.background().color().darker(110)));
+ painter->drawLine(rect.topLeft().x() + margin ,
+ rect.topLeft().y() + offset,
+ rect.topRight().x() - margin,
+ rect.topRight().y() + offset);
+ painter->setPen(QPen(option->palette.background().color().lighter(110)));
+ painter->drawLine(rect.topLeft().x() + margin ,
+ rect.topLeft().y() + offset + 1,
+ rect.topRight().x() - margin,
+ rect.topRight().y() + offset + 1);
+ }
+ }
+ break;
+ case PE_Frame:
+ painter->save();
+ painter->setPen(dark.lighter(108));
+ painter->drawRect(option->rect.adjusted(0, 0, -1, -1));
+ painter->restore();
+ break;
+ case PE_FrameMenu:
+ painter->save();
+ {
+ painter->setPen(QPen(darkOutline, 1));
+ painter->drawRect(option->rect.adjusted(0, 0, -1, -1));
+ QColor frameLight = option->palette.background().color().lighter(160);
+ QColor frameShadow = option->palette.background().color().darker(110);
+
+ //paint beveleffect
+ QRect frame = option->rect.adjusted(1, 1, -1, -1);
+ painter->setPen(frameLight);
+ painter->drawLine(frame.topLeft(), frame.bottomLeft());
+ painter->drawLine(frame.topLeft(), frame.topRight());
+
+ painter->setPen(frameShadow);
+ painter->drawLine(frame.topRight(), frame.bottomRight());
+ painter->drawLine(frame.bottomLeft(), frame.bottomRight());
+ }
+ painter->restore();
+ break;
+ case PE_FrameDockWidget:
+
+ painter->save();
+ {
+ QColor softshadow = option->palette.background().color().darker(120);
+
+ QRect rect= option->rect;
+ painter->setPen(softshadow);
+ painter->drawRect(option->rect.adjusted(0, 0, -1, -1));
+ painter->setPen(QPen(option->palette.light(), 0));
+ painter->drawLine(QPoint(rect.left() + 1, rect.top() + 1), QPoint(rect.left() + 1, rect.bottom() - 1));
+ painter->setPen(QPen(option->palette.background().color().darker(120), 0));
+ painter->drawLine(QPoint(rect.left() + 1, rect.bottom() - 1), QPoint(rect.right() - 2, rect.bottom() - 1));
+ painter->drawLine(QPoint(rect.right() - 1, rect.top() + 1), QPoint(rect.right() - 1, rect.bottom() - 1));
+
+ }
+ painter->restore();
+ break;
+ case PE_PanelButtonTool:
+ painter->save();
+ if ((option->state & State_Enabled || option->state & State_On) || !(option->state & State_AutoRaise)) {
+ QRect rect = option->rect;
+ QPen oldPen = painter->pen();
+
+ if (widget && widget->inherits("QDockWidgetTitleButton")) {
+ if (option->state & State_MouseOver)
+ drawPrimitive(PE_PanelButtonCommand, option, painter, widget);
+ } else {
+ drawPrimitive(PE_PanelButtonCommand, option, painter, widget);
+ }
+ }
+ painter->restore();
+ break;
+ case PE_IndicatorDockWidgetResizeHandle:
+ {
+ QStyleOption dockWidgetHandle = *option;
+ bool horizontal = option->state & State_Horizontal;
+ if (horizontal)
+ dockWidgetHandle.state &= ~State_Horizontal;
+ else
+ dockWidgetHandle.state |= State_Horizontal;
+ drawControl(CE_Splitter, &dockWidgetHandle, painter, widget);
+ }
+ break;
+ case PE_FrameWindow:
+ painter->save();
+ {
+ QRect rect= option->rect;
+ painter->setPen(QPen(dark.darker(150), 0));
+ painter->drawRect(option->rect.adjusted(0, 0, -1, -1));
+ painter->setPen(QPen(option->palette.light(), 0));
+ painter->drawLine(QPoint(rect.left() + 1, rect.top() + 1),
+ QPoint(rect.left() + 1, rect.bottom() - 1));
+ painter->setPen(QPen(option->palette.background().color().darker(120), 0));
+ painter->drawLine(QPoint(rect.left() + 1, rect.bottom() - 1),
+ QPoint(rect.right() - 2, rect.bottom() - 1));
+ painter->drawLine(QPoint(rect.right() - 1, rect.top() + 1),
+ QPoint(rect.right() - 1, rect.bottom() - 1));
+ }
+ painter->restore();
+ break;
+#ifndef QT_NO_LINEDIT
+ case PE_FrameLineEdit:
+ // fall through
+#endif // QT_NO_LINEEDIT
+#ifdef QT3_SUPPORT
+ if (widget && widget->inherits("Q3ToolBar")) {
+ drawPrimitive(PE_Q3Separator, option, painter, widget);
+ break;
+ }
+#endif
+ {
+ QPen oldPen = painter->pen();
+ if (option->state & State_Enabled) {
+ painter->setPen(QPen(option->palette.background(), 0));
+ painter->drawRect(rect.adjusted(0, 0, 0, 0));
+ painter->drawRect(rect.adjusted(1, 1, -1, -1));
+ } else {
+ painter->fillRect(rect, option->palette.background());
+ }
+ QRect r = rect.adjusted(0, 1, 0, -1);
+ painter->setPen(buttonShadowAlpha);
+ painter->drawLine(QPoint(r.left() + 2, r.top() - 1), QPoint(r.right() - 2, r.top() - 1));
+ painter->drawPoint(r.right() - 1, r.top());
+ painter->drawPoint(r.right(), r.top() + 1);
+ painter->drawPoint(r.right() - 1, r.bottom());
+ painter->drawPoint(r.right(), r.bottom() - 1);
+ painter->drawPoint(r.left() + 1, r.top() );
+ painter->drawPoint(r.left(), r.top() + 1);
+ painter->drawPoint(r.left() + 1, r.bottom() );
+ painter->drawPoint(r.left(), r.bottom() - 1);
+ painter->setPen(QPen(option->palette.background().color(), 1));
+ painter->drawLine(QPoint(r.left() + 2, r.top() + 1), QPoint(r.right() - 2, r.top() + 1));
+
+ if (option->state & State_HasFocus) {
+ QColor darkoutline = option->palette.highlight().color().darker(150);
+ QColor innerline = mergedColors(option->palette.highlight().color(), Qt::white);
+ painter->setPen(QPen(innerline, 0));
+ painter->drawRect(rect.adjusted(1, 2, -2, -3));
+ painter->setPen(QPen(darkoutline, 0));
+ }
+ else {
+ QColor highlight = Qt::white;
+ highlight.setAlpha(130);
+ painter->setPen(option->palette.base().color().darker(120));
+ painter->drawLine(QPoint(r.left() + 1, r.top() + 1),
+ QPoint(r.right() - 1, r.top() + 1));
+ painter->drawLine(QPoint(r.left() + 1, r.top() + 1),
+ QPoint(r.left() + 1, r.bottom() - 1));
+ painter->setPen(option->palette.base().color());
+ painter->drawLine(QPoint(r.right() - 1, r.top() + 1),
+ QPoint(r.right() - 1, r.bottom() - 1));
+ painter->setPen(highlight);
+ painter->drawLine(QPoint(r.left() + 1, r.bottom() + 1),
+ QPoint(r.right() - 1, r.bottom() + 1));
+ painter->drawPoint(QPoint(r.left(), r.bottom()));
+ painter->drawPoint(QPoint(r.right(), r.bottom() ));
+ painter->setPen(QPen(darkOutline.lighter(115), 1));
+ }
+ painter->drawLine(QPoint(r.left(), r.top() + 2), QPoint(r.left(), r.bottom() - 2));
+ painter->drawLine(QPoint(r.right(), r.top() + 2), QPoint(r.right(), r.bottom() - 2));
+ painter->drawLine(QPoint(r.left() + 2, r.bottom()), QPoint(r.right() - 2, r.bottom()));
+ painter->drawPoint(QPoint(r.right() - 1, r.bottom() - 1));
+ painter->drawPoint(QPoint(r.right() - 1, r.top() + 1));
+ painter->drawPoint(QPoint(r.left() + 1, r.bottom() - 1));
+ painter->drawPoint(QPoint(r.left() + 1, r.top() + 1));
+ painter->drawLine(QPoint(r.left() + 2, r.top()), QPoint(r.right() - 2, r.top()));
+ painter->setPen(oldPen);
+ }
+ break;
+ case PE_IndicatorCheckBox:
+ painter->save();
+ if (const QStyleOptionButton *checkbox = qstyleoption_cast<const QStyleOptionButton*>(option)) {
+ QRect checkRect;
+ checkRect.setX(rect.left() );
+ checkRect.setY(rect.top() );
+ checkRect.setWidth(rect.width() - 1);
+ checkRect.setHeight(rect.height() - 1);
+ if (state & State_Sunken)
+ painter->setBrush(dark.lighter(130));
+ else
+ painter->setBrush(option->palette.base());
+ painter->setPen(QPen(dark.lighter(110), 0));
+ painter->drawRect(checkRect);
+ if (checkbox->state & (State_On | State_Sunken | State_NoChange)) {
+ QImage image(qt_cleanlooks_checkbox_checked);
+ QColor fillColor = option->palette.text().color();
+ image.setColor(1, fillColor.rgba());
+ fillColor.setAlpha(100);
+ image.setColor(2, fillColor.rgba());
+ painter->drawImage(rect, image);
+ if (checkbox->state & State_NoChange) {
+ QColor bgc = option->palette.background().color();
+ bgc.setAlpha(127);
+ painter->fillRect(checkRect.adjusted(1, 1, -1, -1), bgc);
+ }
+ }
+ }
+ painter->restore();
+ break;
+ case PE_IndicatorRadioButton:
+ painter->save();
+ {
+ painter->setRenderHint(QPainter::SmoothPixmapTransform);
+ QRect checkRect = rect.adjusted(0, 0, 0, 0);
+ if (state & (State_On )) {
+ painter->drawImage(rect, QImage(qt_cleanlooks_radiobutton));
+ painter->drawImage(checkRect, QImage(qt_cleanlooks_radiobutton_checked));
+ }
+ else if (state & State_Sunken) {
+ painter->drawImage(rect, QImage(qt_cleanlooks_radiobutton));
+ QColor bgc = buttonShadow;
+ painter->setRenderHint(QPainter::Antialiasing);
+ painter->setBrush(bgc);
+ painter->setPen(Qt::NoPen);
+ painter->drawEllipse(rect.adjusted(1, 1, -1, -1)); }
+ else {
+ painter->drawImage(rect, QImage(qt_cleanlooks_radiobutton));
+ }
+ }
+ painter->restore();
+ break;
+ case PE_IndicatorToolBarHandle:
+ painter->save();
+ if (option->state & State_Horizontal) {
+ for (int i = rect.height()/5; i <= 4*(rect.height()/5) ; ++i) {
+ int y = rect.topLeft().y() + i + 1;
+ int x1 = rect.topLeft().x() + 3;
+ int x2 = rect.topRight().x() - 2;
+
+ if (i % 2 == 0)
+ painter->setPen(QPen(option->palette.light(), 0));
+ else
+ painter->setPen(QPen(dark.lighter(110), 0));
+ painter->drawLine(x1, y, x2, y);
+ }
+ }
+ else { //vertical toolbar
+ for (int i = rect.width()/5; i <= 4*(rect.width()/5) ; ++i) {
+ int x = rect.topLeft().x() + i + 1;
+ int y1 = rect.topLeft().y() + 3;
+ int y2 = rect.topLeft().y() + 5;
+
+ if (i % 2 == 0)
+ painter->setPen(QPen(option->palette.light(), 0));
+ else
+ painter->setPen(QPen(dark.lighter(110), 0));
+ painter->drawLine(x, y1, x, y2);
+ }
+ }
+ painter->restore();
+ break;
+ case PE_FrameDefaultButton:
+ case PE_FrameFocusRect:
+ if (const QStyleOptionFocusRect *focusFrame = qstyleoption_cast<const QStyleOptionFocusRect *>(option)) {
+ if (!(focusFrame->state & State_KeyboardFocusChange))
+ return;
+ QRect rect = focusFrame->rect;
+ painter->save();
+ painter->setBackgroundMode(Qt::TransparentMode);
+ painter->setBrush(QBrush(dark.darker(120), Qt::Dense4Pattern));
+ painter->setBrushOrigin(rect.topLeft());
+ painter->setPen(Qt::NoPen);
+ painter->drawRect(rect.left(), rect.top(), rect.width(), 1); // Top
+ painter->drawRect(rect.left(), rect.bottom(), rect.width(), 1); // Bottom
+ painter->drawRect(rect.left(), rect.top(), 1, rect.height()); // Left
+ painter->drawRect(rect.right(), rect.top(), 1, rect.height()); // Right
+ painter->restore();
+ }
+ break;
+ case PE_PanelButtonCommand:
+ {
+ bool isDefault = false;
+ bool isFlat = false;
+ bool isDown = (option->state & State_Sunken) || (option->state & State_On);
+ QPen oldPen = painter->pen();
+ QBrush oldBrush = painter->brush();
+ QRect r;
+
+ if (const QStyleOptionButton *button = qstyleoption_cast<const QStyleOptionButton*>(option)) {
+ isDefault = (button->features & QStyleOptionButton::DefaultButton) && (button->state & State_Enabled);
+ isFlat = (button->features & QStyleOptionButton::Flat);
+ }
+
+ if (isFlat && !isDown) {
+ if (isDefault) {
+ r = option->rect.adjusted(0, 1, 0, -1);
+ painter->setPen(QPen(Qt::black, 0));
+ painter->drawLine(QPoint(r.left() + 2, r.top()),
+ QPoint(r.right() - 2, r.top()));
+ painter->drawLine(QPoint(r.left(), r.top() + 2),
+ QPoint(r.left(), r.bottom() - 2));
+ painter->drawLine(QPoint(r.right(), r.top() + 2),
+ QPoint(r.right(), r.bottom() - 2));
+ painter->drawLine(QPoint(r.left() + 2, r.bottom()),
+ QPoint(r.right() - 2, r.bottom()));
+ painter->drawPoint(QPoint(r.right() - 1, r.bottom() - 1));
+ painter->drawPoint(QPoint(r.right() - 1, r.top() + 1));
+ painter->drawPoint(QPoint(r.left() + 1, r.bottom() - 1));
+ painter->drawPoint(QPoint(r.left() + 1, r.top() + 1));
+ painter->setPen(oldPen);
+ }
+ return;
+ }
+
+ BEGIN_STYLE_PIXMAPCACHE(QString::fromLatin1("pushbutton-%1").arg(isDefault))
+ r = rect.adjusted(0, 1, 0, -1);
+
+ bool isEnabled = (option->state & State_Enabled);
+
+ QColor highlightedGradientStartColor = option->palette.button().color().lighter(107);
+ QColor highlightedGradientMidColor = option->palette.button().color().lighter(105);
+ QColor highlightedGradientStopColor = buttonShadow.lighter(107);
+ QColor gradientStartColor = option->palette.button().color().lighter(108);
+
+ QColor buttonColor = option->palette.button().color();
+ QColor gradientMidColor = option->palette.button().color();
+ QColor gradientStopColor;
+ gradientStopColor.setHsv(buttonColor.hue(),
+ CL_MIN(255, (int)(buttonColor.saturation()*1.9)),
+ CL_MIN(255, (int)(buttonColor.value()*0.96)));
+
+ QRect gradRect = rect.adjusted(1, 2, -1, -2);
+ // gradient fill
+ QRect innerBorder = r.adjusted(1, 1, -1, 0);
+
+ if (isDown) {
+ QBrush fillColor = gradientStopColor.darker(110);
+ if (option->palette.button().gradient())
+ fillColor = option->palette.button();
+ p->fillRect(gradRect, fillColor);
+ p->setPen(gradientStopColor.darker(125));
+ p->drawLine(innerBorder.topLeft(), innerBorder.topRight());
+ p->drawLine(innerBorder.topLeft(), innerBorder.bottomLeft());
+ } else {
+ if (isEnabled && option->state & State_MouseOver ) {
+ qt_cleanlooks_draw_buttongradient(p, gradRect,
+ highlightedGradientStartColor,
+ highlightedGradientMidColor,
+ highlightedGradientStopColor, TopDown, option->palette.button());
+ } else {
+ qt_cleanlooks_draw_buttongradient(p, gradRect,
+ gradientStartColor,
+ gradientMidColor,
+ gradientStopColor, TopDown, option->palette.button());
+ }
+ }
+
+ bool hasFocus = option->state & State_HasFocus;
+
+ if (!isEnabled)
+ p->setPen(QPen(dark.lighter(115)));
+ else if (isDefault)
+ p->setPen(QPen(Qt::black, 1));
+ else
+ p->setPen(QPen(darkOutline, 1));
+
+ p->drawLine(QPoint(r.left(), r.top() + 2),
+ QPoint(r.left(), r.bottom() - 2));
+ p->drawLine(QPoint(r.right(), r.top() + 2),
+ QPoint(r.right(), r.bottom() - 2));
+ p->drawLine(QPoint(r.left() + 2, r.bottom()),
+ QPoint(r.right() - 2, r.bottom()));
+ p->drawPoint(QPoint(r.right() - 1, r.bottom() - 1));
+ p->drawPoint(QPoint(r.right() - 1, r.top() + 1));
+ p->drawPoint(QPoint(r.left() + 1, r.bottom() - 1));
+ p->drawPoint(QPoint(r.left() + 1, r.top() + 1));
+
+ if (!isDefault && !hasFocus && isEnabled)
+ p->setPen(QPen(darkOutline.darker(110), 0));
+
+ p->drawLine(QPoint(r.left() + 2, r.top()),
+ QPoint(r.right() - 2, r.top()));
+
+ QColor highlight = Qt::white;
+ highlight.setAlpha(110);
+ p->setPen(highlight);
+ p->drawLine(QPoint(r.left() + 1, r.top() + 2),
+ QPoint(r.left() + 1, r.bottom() - 2));
+ p->drawLine(QPoint(r.left() + 3, r.bottom() + 1),
+ QPoint(r.right() - 3, r.bottom() + 1));
+
+ QColor topShadow = darkOutline;
+ topShadow.setAlpha(60);
+
+ p->setPen(topShadow);
+ p->drawPoint(QPoint(r.right(), r.top() + 1));
+ p->drawPoint(QPoint(r.right() - 1, r.top() ));
+ p->drawPoint(QPoint(r.right(), r.bottom() - 1));
+ p->drawPoint(QPoint(r.right() - 1, r.bottom() ));
+ p->drawPoint(QPoint(r.left() + 1, r.bottom()));
+ p->drawPoint(QPoint(r.left(), r.bottom() - 1));
+ p->drawPoint(QPoint(r.left() + 1, r.top()));
+ p->drawPoint(QPoint(r.left(), r.top() + 1));
+
+ topShadow.setAlpha(30);
+ p->setPen(topShadow);
+
+ p->drawLine(QPoint(r.right() - 1, r.top() + 2),
+ QPoint(r.right() - 1, r.bottom() - 2));
+ p->drawLine(QPoint(r.left() + 2, r.top() - 1),
+ QPoint(r.right() - 2, r.top() - 1));
+
+ if (isDefault) {
+ r.adjust(-1, -1, 1, 1);
+ p->setPen(buttonShadowAlpha.darker(120));
+ p->drawLine(r.topLeft() + QPoint(3, 0), r.topRight() - QPoint(3, 0));
+ p->drawLine(r.bottomLeft() + QPoint(3, 0), r.bottomRight() - QPoint(3, 0));
+ p->drawLine(r.topLeft() + QPoint(0, 3), r.bottomLeft() - QPoint(0, 3));
+ p->drawLine(r.topRight() + QPoint(0, 3), r.bottomRight() - QPoint(0, 3));
+ p->drawPoint(r.topRight() + QPoint(-2, 1));
+ p->drawPoint(r.topRight() + QPoint(-1, 2));
+ p->drawPoint(r.bottomRight() + QPoint(-1, -2));
+ p->drawPoint(r.bottomRight() + QPoint(-2, -1));
+ p->drawPoint(r.topLeft() + QPoint(1, 2));
+ p->drawPoint(r.topLeft() + QPoint(2, 1));
+ p->drawPoint(r.bottomLeft() + QPoint(1, -2));
+ p->drawPoint(r.bottomLeft() + QPoint(2, -1));
+ }
+ painter->setPen(oldPen);
+ painter->setBrush(oldBrush);
+ END_STYLE_PIXMAPCACHE
+ }
+ break;
+#ifndef QT_NO_TABBAR
+ case PE_FrameTabWidget:
+ painter->save();
+ {
+ painter->fillRect(option->rect, tabFrameColor);
+ }
+#ifndef QT_NO_TABWIDGET
+ if (const QStyleOptionTabWidgetFrame *twf = qstyleoption_cast<const QStyleOptionTabWidgetFrame *>(option)) {
+ QColor borderColor = darkOutline.lighter(110);
+ QColor alphaCornerColor = mergedColors(borderColor, option->palette.background().color());
+ QColor innerShadow = mergedColors(borderColor, option->palette.base().color());
+
+ int borderThickness = pixelMetric(PM_TabBarBaseOverlap, twf, widget);
+ bool reverse = (twf->direction == Qt::RightToLeft);
+ QRect tabBarRect;
+
+ switch (twf->shape) {
+ case QTabBar::RoundedNorth:
+ if (reverse) {
+ tabBarRect = QRect(twf->rect.right() - twf->leftCornerWidgetSize.width()
+ - twf->tabBarSize.width() + 1,
+ twf->rect.top(),
+ twf->tabBarSize.width(), borderThickness);
+ } else {
+ tabBarRect = QRect(twf->rect.left() + twf->leftCornerWidgetSize.width(),
+ twf->rect.top(),
+ twf->tabBarSize.width(), borderThickness);
+ }
+ break ;
+ case QTabBar::RoundedWest:
+ tabBarRect = QRect(twf->rect.left(),
+ twf->rect.top() + twf->leftCornerWidgetSize.height(),
+ borderThickness,
+ twf->tabBarSize.height());
+ tabBarRect = tabBarRect; //adjust
+ break ;
+ case QTabBar::RoundedEast:
+ tabBarRect = QRect(twf->rect.right() - borderThickness + 1,
+ twf->rect.top() + twf->leftCornerWidgetSize.height(),
+ 0,
+ twf->tabBarSize.height());
+ break ;
+ case QTabBar::RoundedSouth:
+ if (reverse) {
+ tabBarRect = QRect(twf->rect.right() - twf->leftCornerWidgetSize.width() - twf->tabBarSize.width() + 1,
+ twf->rect.bottom() + 1,
+ twf->tabBarSize.width(),
+ borderThickness);
+ } else {
+ tabBarRect = QRect(twf->rect.left() + twf->leftCornerWidgetSize.width(),
+ twf->rect.bottom() + 1,
+ twf->tabBarSize.width(),
+ borderThickness);
+ }
+ break;
+ default:
+ break;
+ }
+
+ QRegion region(twf->rect);
+ region -= tabBarRect;
+ painter->setClipRegion(region);
+
+ // Outer border
+ QLine leftLine = QLine(twf->rect.topLeft() + QPoint(0, 2), twf->rect.bottomLeft() - QPoint(0, 2));
+ QLine rightLine = QLine(twf->rect.topRight(), twf->rect.bottomRight() - QPoint(0, 2));
+ QLine bottomLine = QLine(twf->rect.bottomLeft() + QPoint(2, 0), twf->rect.bottomRight() - QPoint(2, 0));
+ QLine topLine = QLine(twf->rect.topLeft(), twf->rect.topRight());
+
+ painter->setPen(borderColor);
+ painter->drawLine(topLine);
+
+ // Inner border
+ QLine innerLeftLine = QLine(leftLine.p1() + QPoint(1, 0), leftLine.p2() + QPoint(1, 0));
+ QLine innerRightLine = QLine(rightLine.p1() - QPoint(1, -1), rightLine.p2() - QPoint(1, 0));
+ QLine innerBottomLine = QLine(bottomLine.p1() - QPoint(0, 1), bottomLine.p2() - QPoint(0, 1));
+ QLine innerTopLine = QLine(topLine.p1() + QPoint(0, 1), topLine.p2() + QPoint(-1, 1));
+
+ // Rounded Corner
+ QPoint leftBottomOuterCorner = QPoint(innerLeftLine.p2() + QPoint(0, 1));
+ QPoint leftBottomInnerCorner1 = QPoint(leftLine.p2() + QPoint(0, 1));
+ QPoint leftBottomInnerCorner2 = QPoint(bottomLine.p1() - QPoint(1, 0));
+ QPoint rightBottomOuterCorner = QPoint(innerRightLine.p2() + QPoint(0, 1));
+ QPoint rightBottomInnerCorner1 = QPoint(rightLine.p2() + QPoint(0, 1));
+ QPoint rightBottomInnerCorner2 = QPoint(bottomLine.p2() + QPoint(1, 0));
+ QPoint leftTopOuterCorner = QPoint(innerLeftLine.p1() - QPoint(0, 1));
+ QPoint leftTopInnerCorner1 = QPoint(leftLine.p1() - QPoint(0, 1));
+ QPoint leftTopInnerCorner2 = QPoint(topLine.p1() - QPoint(1, 0));
+
+ painter->setPen(borderColor);
+ painter->drawLine(leftLine);
+ painter->drawLine(rightLine);
+ painter->drawLine(bottomLine);
+ painter->drawPoint(leftBottomOuterCorner);
+ painter->drawPoint(rightBottomOuterCorner);
+ painter->drawPoint(leftTopOuterCorner);
+
+ painter->setPen(option->palette.light().color());
+ painter->drawLine(innerLeftLine);
+ painter->drawLine(innerTopLine);
+
+ painter->setPen(buttonShadowAlpha);
+ painter->drawLine(innerRightLine);
+ painter->drawLine(innerBottomLine);
+
+ painter->setPen(alphaCornerColor);
+ painter->drawPoint(leftBottomInnerCorner1);
+ painter->drawPoint(leftBottomInnerCorner2);
+ painter->drawPoint(rightBottomInnerCorner1);
+ painter->drawPoint(rightBottomInnerCorner2);
+ painter->drawPoint(leftTopInnerCorner1);
+ painter->drawPoint(leftTopInnerCorner2);
+ }
+#endif // QT_NO_TABWIDGET
+ painter->restore();
+ break ;
+
+ case PE_FrameStatusBarItem:
+ break;
+ case PE_IndicatorTabClose:
+ {
+ Q_D(const QCleanlooksStyle);
+ if (d->tabBarcloseButtonIcon.isNull())
+ d->tabBarcloseButtonIcon = standardIcon(SP_DialogCloseButton, option, widget);
+ if ((option->state & State_Enabled) && (option->state & State_MouseOver))
+ drawPrimitive(PE_PanelButtonCommand, option, painter, widget);
+ QPixmap pixmap = d->tabBarcloseButtonIcon.pixmap(QSize(16, 16), QIcon::Normal, QIcon::On);
+ drawItemPixmap(painter, option->rect, Qt::AlignCenter, pixmap);
+ }
+ break;
+
+#endif // QT_NO_TABBAR
+ default:
+ QWindowsStyle::drawPrimitive(elem, option, painter, widget);
+ break;
+ }
+}
+
+/*!
+ \reimp
+*/
+void QCleanlooksStyle::drawControl(ControlElement element, const QStyleOption *option, QPainter *painter,
+ const QWidget *widget) const
+{
+ QColor button = option->palette.button().color();
+ QColor dark;
+ dark.setHsv(button.hue(),
+ CL_MIN(255, (int)(button.saturation()*1.9)),
+ CL_MIN(255, (int)(button.value()*0.7)));
+ QColor darkOutline;
+ darkOutline.setHsv(button.hue(),
+ CL_MIN(255, (int)(button.saturation()*2.0)),
+ CL_MIN(255, (int)(button.value()*0.6)));
+ QRect rect = option->rect;
+ QColor shadow = mergedColors(option->palette.background().color().darker(120),
+ dark.lighter(130), 60);
+ QColor tabFrameColor = mergedColors(option->palette.background().color(),
+ dark.lighter(135), 60);
+
+ QColor highlight = option->palette.highlight().color();
+ QColor highlightText = option->palette.highlightedText().color();
+
+ switch(element) {
+ case CE_RadioButton: //fall through
+ case CE_CheckBox:
+ if (const QStyleOptionButton *btn = qstyleoption_cast<const QStyleOptionButton *>(option)) {
+ bool hover = (btn->state & State_MouseOver && btn->state & State_Enabled);
+ if (hover)
+ painter->fillRect(rect, btn->palette.background().color().lighter(104));
+ QStyleOptionButton copy = *btn;
+ copy.rect.adjust(2, 0, -2, 0);
+ QWindowsStyle::drawControl(element, &copy, painter, widget);
+ }
+ break;
+ case CE_Splitter:
+ painter->save();
+ {
+ // hover appearance
+ QBrush fillColor = option->palette.background().color();
+ if (option->state & State_MouseOver && option->state & State_Enabled)
+ fillColor = fillColor.color().lighter(106);
+
+ painter->fillRect(option->rect, fillColor);
+
+ QColor grooveColor = mergedColors(dark.lighter(110), option->palette.button().color(),40);
+ QColor gripShadow = grooveColor.darker(110);
+ QPalette palette = option->palette;
+ bool vertical = !(option->state & State_Horizontal);
+ QRect scrollBarSlider = option->rect;
+ int gripMargin = 4;
+ //draw grips
+ if (vertical) {
+ for( int i = -20; i< 20 ; i += 2) {
+ painter->setPen(QPen(gripShadow, 1));
+ painter->drawLine(
+ QPoint(scrollBarSlider.center().x() + i ,
+ scrollBarSlider.top() + gripMargin),
+ QPoint(scrollBarSlider.center().x() + i,
+ scrollBarSlider.bottom() - gripMargin));
+ painter->setPen(QPen(palette.light(), 1));
+ painter->drawLine(
+ QPoint(scrollBarSlider.center().x() + i + 1,
+ scrollBarSlider.top() + gripMargin ),
+ QPoint(scrollBarSlider.center().x() + i + 1,
+ scrollBarSlider.bottom() - gripMargin));
+ }
+ } else {
+ for (int i = -20; i < 20 ; i += 2) {
+ painter->setPen(QPen(gripShadow, 1));
+ painter->drawLine(
+ QPoint(scrollBarSlider.left() + gripMargin ,
+ scrollBarSlider.center().y()+ i),
+ QPoint(scrollBarSlider.right() - gripMargin,
+ scrollBarSlider.center().y()+ i));
+ painter->setPen(QPen(palette.light(), 1));
+ painter->drawLine(
+ QPoint(scrollBarSlider.left() + gripMargin,
+ scrollBarSlider.center().y() + 1 + i),
+ QPoint(scrollBarSlider.right() - gripMargin,
+ scrollBarSlider.center().y() + 1 + i));
+
+ }
+ }
+ }
+ painter->restore();
+ break;
+#ifndef QT_NO_SIZEGRIP
+ case CE_SizeGrip:
+ painter->save();
+ {
+ int x, y, w, h;
+ option->rect.getRect(&x, &y, &w, &h);
+ int sw = qMin(h, w);
+ if (h > w)
+ painter->translate(0, h - w);
+ else
+ painter->translate(w - h, 0);
+
+ int sx = x;
+ int sy = y;
+ int s = 4;
+ if (option->direction == Qt::RightToLeft) {
+ sx = x + sw;
+ for (int i = 0; i < 4; ++i) {
+ painter->setPen(QPen(option->palette.light().color(), 1));
+ painter->drawLine(x, sy - 1 , sx + 1, sw);
+ painter->setPen(QPen(dark.lighter(120), 1));
+ painter->drawLine(x, sy, sx, sw);
+ sx -= s;
+ sy += s;
+ }
+ } else {
+ for (int i = 0; i < 4; ++i) {
+ painter->setPen(QPen(option->palette.light().color(), 1));
+ painter->drawLine(sx - 1, sw, sw, sy - 1);
+ painter->setPen(QPen(dark.lighter(120), 1));
+ painter->drawLine(sx, sw, sw, sy);
+ sx += s;
+ sy += s;
+ }
+ }
+ }
+ painter->restore();
+ break;
+#endif // QT_NO_SIZEGRIP
+#ifndef QT_NO_TOOLBAR
+ case CE_ToolBar:
+ painter->save();
+ if (const QStyleOptionToolBar *toolbar = qstyleoption_cast<const QStyleOptionToolBar *>(option)) {
+ QRect rect = option->rect;
+
+ bool paintLeftBorder = true;
+ bool paintRightBorder = true;
+ bool paintBottomBorder = true;
+
+ switch (toolbar->toolBarArea) {
+ case Qt::BottomToolBarArea:
+ switch(toolbar->positionOfLine) {
+ case QStyleOptionToolBar::Beginning:
+ case QStyleOptionToolBar::OnlyOne:
+ paintBottomBorder = false;
+ default:
+ break;
+ }
+ case Qt::TopToolBarArea:
+ switch (toolbar->positionWithinLine) {
+ case QStyleOptionToolBar::Beginning:
+ paintLeftBorder = false;
+ break;
+ case QStyleOptionToolBar::End:
+ paintRightBorder = false;
+ break;
+ case QStyleOptionToolBar::OnlyOne:
+ paintRightBorder = false;
+ paintLeftBorder = false;
+ default:
+ break;
+ }
+ if (QApplication::layoutDirection() == Qt::RightToLeft) { //reverse layout changes the order of Beginning/end
+ bool tmp = paintLeftBorder;
+ paintRightBorder=paintLeftBorder;
+ paintLeftBorder=tmp;
+ }
+ break;
+ case Qt::RightToolBarArea:
+ switch (toolbar->positionOfLine) {
+ case QStyleOptionToolBar::Beginning:
+ case QStyleOptionToolBar::OnlyOne:
+ paintRightBorder = false;
+ break;
+ default:
+ break;
+ }
+ break;
+ case Qt::LeftToolBarArea:
+ switch (toolbar->positionOfLine) {
+ case QStyleOptionToolBar::Beginning:
+ case QStyleOptionToolBar::OnlyOne:
+ paintLeftBorder = false;
+ break;
+ default:
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+
+ QColor light = option->palette.background().color().lighter(110);
+
+ //draw top border
+ painter->setPen(QPen(light));
+ painter->drawLine(rect.topLeft().x(),
+ rect.topLeft().y(),
+ rect.topRight().x(),
+ rect.topRight().y());
+
+ if (paintLeftBorder) {
+ painter->setPen(QPen(light));
+ painter->drawLine(rect.topLeft().x(),
+ rect.topLeft().y(),
+ rect.bottomLeft().x(),
+ rect.bottomLeft().y());
+ }
+
+ if (paintRightBorder) {
+ painter->setPen(QPen(shadow));
+ painter->drawLine(rect.topRight().x(),
+ rect.topRight().y(),
+ rect.bottomRight().x(),
+ rect.bottomRight().y());
+ }
+
+ if (paintBottomBorder) {
+ painter->setPen(QPen(shadow));
+ painter->drawLine(rect.bottomLeft().x(),
+ rect.bottomLeft().y(),
+ rect.bottomRight().x(),
+ rect.bottomRight().y());
+ }
+ }
+ painter->restore();
+ break;
+#endif // QT_NO_TOOLBAR
+#ifndef QT_NO_DOCKWIDGET
+ case CE_DockWidgetTitle:
+ painter->save();
+ if (const QStyleOptionDockWidget *dwOpt = qstyleoption_cast<const QStyleOptionDockWidget *>(option)) {
+ const QStyleOptionDockWidgetV2 *v2
+ = qstyleoption_cast<const QStyleOptionDockWidgetV2*>(dwOpt);
+ bool verticalTitleBar = v2 == 0 ? false : v2->verticalTitleBar;
+
+ QRect rect = dwOpt->rect;
+ QRect titleRect = subElementRect(SE_DockWidgetTitleBarText, option, widget);
+ QRect r = rect.adjusted(0, 0, -1, 0);
+ if (verticalTitleBar)
+ r.adjust(0, 0, 0, -1);
+ painter->setPen(option->palette.light().color());
+ painter->drawRect(r.adjusted(1, 1, 1, 1));
+ painter->setPen(shadow);
+ painter->drawRect(r);
+
+ if (verticalTitleBar) {
+ QRect r = rect;
+ QSize s = r.size();
+ s.transpose();
+ r.setSize(s);
+
+ titleRect = QRect(r.left() + rect.bottom()
+ - titleRect.bottom(),
+ r.top() + titleRect.left() - rect.left(),
+ titleRect.height(), titleRect.width());
+
+ painter->translate(r.left(), r.top() + r.width());
+ painter->rotate(-90);
+ painter->translate(-r.left(), -r.top());
+
+ rect = r;
+ }
+
+ if (!dwOpt->title.isEmpty()) {
+ QString titleText
+ = painter->fontMetrics().elidedText(dwOpt->title,
+ Qt::ElideRight, titleRect.width());
+ drawItemText(painter,
+ titleRect,
+ Qt::AlignLeft | Qt::AlignVCenter | Qt::TextShowMnemonic, dwOpt->palette,
+ dwOpt->state & State_Enabled, titleText,
+ QPalette::WindowText);
+ }
+ }
+ painter->restore();
+ break;
+#endif // QT_NO_DOCKWIDGET
+ case CE_HeaderSection:
+ painter->save();
+ // Draws the header in tables.
+ if (const QStyleOptionHeader *header = qstyleoption_cast<const QStyleOptionHeader *>(option)) {
+ QPixmap cache;
+ QString pixmapName = uniqueName(QLatin1String("headersection"), option, option->rect.size());
+ pixmapName += QLatin1String("-") + QString::number(int(header->position));
+ pixmapName += QLatin1String("-") + QString::number(int(header->orientation));
+ QRect r = option->rect;
+ QColor gradientStopColor;
+ QColor gradientStartColor = option->palette.button().color();
+ gradientStopColor.setHsv(gradientStartColor.hue(),
+ CL_MIN(255, (int)(gradientStartColor.saturation()*2)),
+ CL_MIN(255, (int)(gradientStartColor.value()*0.96)));
+ QLinearGradient gradient(rect.topLeft(), rect.bottomLeft());
+ if (option->palette.background().gradient()) {
+ gradient.setStops(option->palette.background().gradient()->stops());
+ } else {
+ gradient.setColorAt(0, gradientStartColor);
+ gradient.setColorAt(0.8, gradientStartColor);
+ gradient.setColorAt(1, gradientStopColor);
+ }
+ painter->fillRect(r, gradient);
+
+ if (!UsePixmapCache || !QPixmapCache::find(pixmapName, cache)) {
+ cache = QPixmap(r.size());
+ cache.fill(Qt::transparent);
+ QRect pixmapRect(0, 0, r.width(), r.height());
+ QPainter cachePainter(&cache);
+ if (header->orientation == Qt::Vertical) {
+ cachePainter.setPen(QPen(dark));
+ cachePainter.drawLine(pixmapRect.topRight(), pixmapRect.bottomRight());
+ if (header->position != QStyleOptionHeader::End) {
+ cachePainter.setPen(QPen(shadow));
+ cachePainter.drawLine(pixmapRect.bottomLeft() + QPoint(3, -1), pixmapRect.bottomRight() + QPoint(-3, -1)); cachePainter.setPen(QPen(option->palette.light().color()));
+ cachePainter.drawLine(pixmapRect.bottomLeft() + QPoint(3, 0), pixmapRect.bottomRight() + QPoint(-3, 0)); }
+ } else {
+ cachePainter.setPen(QPen(dark));
+ cachePainter.drawLine(pixmapRect.bottomLeft(), pixmapRect.bottomRight());
+ cachePainter.setPen(QPen(shadow));
+ cachePainter.drawLine(pixmapRect.topRight() + QPoint(-1, 3), pixmapRect.bottomRight() + QPoint(-1, -3)); cachePainter.setPen(QPen(option->palette.light().color()));
+ cachePainter.drawLine(pixmapRect.topRight() + QPoint(0, 3), pixmapRect.bottomRight() + QPoint(0, -3)); }
+ cachePainter.end();
+ if (UsePixmapCache)
+ QPixmapCache::insert(pixmapName, cache);
+ }
+ painter->drawPixmap(r.topLeft(), cache);
+ }
+ painter->restore();
+ break;
+ case CE_ProgressBarGroove:
+ painter->save();
+ {
+ painter->fillRect(rect, option->palette.base());
+ QColor borderColor = dark.lighter(110);
+ painter->setPen(QPen(borderColor, 0));
+ painter->drawLine(QPoint(rect.left() + 1, rect.top()), QPoint(rect.right() - 1, rect.top()));
+ painter->drawLine(QPoint(rect.left() + 1, rect.bottom()), QPoint(rect.right() - 1, rect.bottom()));
+ painter->drawLine(QPoint(rect.left(), rect.top() + 1), QPoint(rect.left(), rect.bottom() - 1));
+ painter->drawLine(QPoint(rect.right(), rect.top() + 1), QPoint(rect.right(), rect.bottom() - 1));
+ QColor alphaCorner = mergedColors(borderColor, option->palette.background().color());
+ QColor innerShadow = mergedColors(borderColor, option->palette.base().color());
+
+ //corner smoothing
+ painter->setPen(alphaCorner);
+ painter->drawPoint(rect.topRight());
+ painter->drawPoint(rect.topLeft());
+ painter->drawPoint(rect.bottomRight());
+ painter->drawPoint(rect.bottomLeft());
+
+ //inner shadow
+ painter->setPen(innerShadow);
+ painter->drawLine(QPoint(rect.left() + 1, rect.top() + 1),
+ QPoint(rect.right() - 1, rect.top() + 1));
+ painter->drawLine(QPoint(rect.left() + 1, rect.top() + 1),
+ QPoint(rect.left() + 1, rect.bottom() + 1));
+
+ }
+ painter->restore();
+ break;
+ case CE_ProgressBarContents:
+ painter->save();
+ if (const QStyleOptionProgressBar *bar = qstyleoption_cast<const QStyleOptionProgressBar *>(option)) {
+ QRect rect = bar->rect;
+ bool vertical = false;
+ bool inverted = false;
+ bool indeterminate = (bar->minimum == 0 && bar->maximum == 0);
+
+ // Get extra style options if version 2
+ if (const QStyleOptionProgressBarV2 *bar2 = qstyleoption_cast<const QStyleOptionProgressBarV2 *>(option)) {
+ vertical = (bar2->orientation == Qt::Vertical);
+ inverted = bar2->invertedAppearance;
+ }
+
+ // If the orientation is vertical, we use a transform to rotate
+ // the progress bar 90 degrees clockwise. This way we can use the
+ // same rendering code for both orientations.
+ if (vertical) {
+ rect = QRect(rect.left(), rect.top(), rect.height(), rect.width()); // flip width and height
+ QTransform m;
+ m.translate(rect.height()-1, -1.0);
+ m.rotate(90.0);
+ painter->setTransform(m, true);
+ }
+
+ int maxWidth = rect.width() - 4;
+ int minWidth = 4;
+ qint64 progress = (qint64)qMax(bar->progress, bar->minimum); // workaround for bug in QProgressBar
+ double vc6_workaround = ((progress - qint64(bar->minimum)) / qMax(double(1.0), double(qint64(bar->maximum) - qint64(bar->minimum))) * maxWidth);
+ int progressBarWidth = (int(vc6_workaround) > minWidth ) ? int(vc6_workaround) : minWidth;
+ int width = indeterminate ? maxWidth : progressBarWidth;
+
+ bool reverse = (!vertical && (bar->direction == Qt::RightToLeft)) || vertical;
+ if (inverted)
+ reverse = !reverse;
+
+ QRect progressBar;
+ if (!indeterminate) {
+ if (!reverse) {
+ progressBar.setRect(rect.left() + 1, rect.top() + 1, width + 1, rect.height() - 3);
+ } else {
+ progressBar.setRect(rect.right() - 1 - width, rect.top() + 1, width + 1, rect.height() - 3);
+ }
+ } else {
+ Q_D(const QCleanlooksStyle);
+ int slideWidth = ((rect.width() - 4) * 2) / 3;
+ int step = ((d->animateStep * slideWidth) / d->animationFps) % slideWidth;
+ if ((((d->animateStep * slideWidth) / d->animationFps) % (2 * slideWidth)) >= slideWidth)
+ step = slideWidth - step;
+ progressBar.setRect(rect.left() + 1 + step, rect.top() + 1,
+ slideWidth / 2, rect.height() - 3);
+ }
+ QColor highlight = option->palette.color(QPalette::Normal, QPalette::Highlight);
+ painter->setPen(QPen(highlight.darker(140), 0));
+
+ QColor highlightedGradientStartColor = highlight.lighter(100);
+ QColor highlightedGradientStopColor = highlight.lighter(130);
+
+ QLinearGradient gradient(rect.topLeft(), QPoint(rect.bottomLeft().x(),
+ rect.bottomLeft().y()*2));
+
+ gradient.setColorAt(0, highlightedGradientStartColor);
+ gradient.setColorAt(1, highlightedGradientStopColor);
+
+ painter->setBrush(gradient);
+ painter->drawRect(progressBar);
+
+ painter->setPen(QPen(highlight.lighter(120), 0));
+ painter->drawLine(QPoint(progressBar.left() + 1, progressBar.top() + 1),
+ QPoint(progressBar.right(), progressBar.top() + 1));
+ painter->drawLine(QPoint(progressBar.left() + 1, progressBar.top() + 1),
+ QPoint(progressBar.left() + 1, progressBar.bottom() - 1));
+
+ painter->setPen(QPen(highlightedGradientStartColor, 7.0));//QPen(option->palette.highlight(), 3));
+
+ painter->save();
+ painter->setClipRect(progressBar.adjusted(2, 2, -1, -1));
+ for (int x = progressBar.left() - 32; x < rect.right() ; x+=18) {
+ painter->drawLine(x, progressBar.bottom() + 1, x + 23, progressBar.top() - 2);
+ }
+ painter->restore();
+
+ }
+ painter->restore();
+ break;
+ case CE_MenuBarItem:
+ painter->save();
+ if (const QStyleOptionMenuItem *mbi = qstyleoption_cast<const QStyleOptionMenuItem *>(option))
+ {
+ QStyleOptionMenuItem item = *mbi;
+ item.rect = mbi->rect.adjusted(0, 3, 0, -1);
+ QColor highlightOutline = highlight.darker(125);
+ QLinearGradient gradient(rect.topLeft(), QPoint(rect.bottomLeft().x(), rect.bottomLeft().y()*2));
+
+ if (option->palette.button().gradient()) {
+ gradient.setStops(option->palette.button().gradient()->stops());
+ } else {
+ gradient.setColorAt(0, option->palette.button().color());
+ gradient.setColorAt(1, option->palette.button().color().darker(110));
+ }
+ painter->fillRect(rect, gradient);
+
+ QCommonStyle::drawControl(element, &item, painter, widget);
+
+ bool act = mbi->state & State_Selected && mbi->state & State_Sunken;
+ bool dis = !(mbi->state & State_Enabled);
+
+ QRect r = option->rect;
+ if (act) {
+ qt_cleanlooks_draw_gradient(painter, r.adjusted(1, 1, -1, -1),
+ highlight,
+ highlightOutline, TopDown,
+ option->palette.highlight());
+
+ painter->setPen(QPen(highlightOutline, 0));
+ painter->drawLine(QPoint(r.left(), r.top() + 1), QPoint(r.left(), r.bottom()));
+ painter->drawLine(QPoint(r.right(), r.top() + 1), QPoint(r.right(), r.bottom()));
+ painter->drawLine(QPoint(r.left() + 1, r.bottom()), QPoint(r.right() - 1, r.bottom()));
+ painter->drawLine(QPoint(r.left() + 1, r.top()), QPoint(r.right() - 1, r.top()));
+
+ //draw text
+ QPalette::ColorRole textRole = dis ? QPalette::Text : QPalette::HighlightedText;
+ uint alignment = Qt::AlignCenter | Qt::TextShowMnemonic | Qt::TextDontClip | Qt::TextSingleLine;
+ if (!styleHint(SH_UnderlineShortcut, mbi, widget))
+ alignment |= Qt::TextHideMnemonic;
+ drawItemText(painter, item.rect, alignment, mbi->palette, mbi->state & State_Enabled, mbi->text, textRole);
+ }
+
+ }
+ painter->restore();
+ break;
+ case CE_MenuItem:
+ painter->save();
+ // Draws one item in a popup menu.
+ if (const QStyleOptionMenuItem *menuItem = qstyleoption_cast<const QStyleOptionMenuItem *>(option)) {
+ QColor highlightOutline = highlight.darker(125);
+ QColor menuBackground = option->palette.background().color().lighter(104);
+ QColor borderColor = option->palette.background().color().darker(160);
+ QColor alphaCornerColor;
+
+ if (widget) {
+ // ### backgroundrole/foregroundrole should be part of the style option
+ alphaCornerColor = mergedColors(option->palette.color(widget->backgroundRole()), borderColor);
+ } else {
+ alphaCornerColor = mergedColors(option->palette.background().color(), borderColor);
+ }
+ QColor alphaTextColor = mergedColors(option->palette.background().color(), option->palette.text().color());
+ if (menuItem->menuItemType == QStyleOptionMenuItem::Separator) {
+ painter->fillRect(menuItem->rect, menuBackground);
+ int w = 0;
+ if (!menuItem->text.isEmpty()) {
+ painter->setFont(menuItem->font);
+ drawItemText(painter, menuItem->rect.adjusted(5, 0, -5, 0), Qt::AlignLeft | Qt::AlignVCenter,
+ menuItem->palette, menuItem->state & State_Enabled, menuItem->text,
+ QPalette::Text);
+ w = menuItem->fontMetrics.width(menuItem->text) + 5;
+ }
+ painter->setPen(shadow.lighter(106));
+ bool reverse = menuItem->direction == Qt::RightToLeft;
+ painter->drawLine(menuItem->rect.left() + 5 + (reverse ? 0 : w), menuItem->rect.center().y(),
+ menuItem->rect.right() - 5 - (reverse ? w : 0), menuItem->rect.center().y());
+ painter->restore();
+ break;
+ }
+ bool selected = menuItem->state & State_Selected && menuItem->state & State_Enabled;
+ if (selected) {
+ QRect r = option->rect.adjusted(1, 0, -2, -1);
+ qt_cleanlooks_draw_gradient(painter, r, highlight,
+ highlightOutline, TopDown,
+ highlight);
+ r = r.adjusted(-1, 0, 1, 0);
+ painter->setPen(QPen(highlightOutline, 0));
+ painter->drawLine(QPoint(r.left(), r.top() + 1), QPoint(r.left(), r.bottom() - 1));
+ painter->drawLine(QPoint(r.right(), r.top() + 1), QPoint(r.right(), r.bottom() - 1));
+ painter->drawLine(QPoint(r.left() + 1, r.bottom()), QPoint(r.right() - 1, r.bottom()));
+ painter->drawLine(QPoint(r.left() + 1, r.top()), QPoint(r.right() - 1, r.top()));
+ } else {
+ painter->fillRect(option->rect, menuBackground);
+ }
+
+ bool checkable = menuItem->checkType != QStyleOptionMenuItem::NotCheckable;
+ bool checked = menuItem->checked;
+ bool sunken = menuItem->state & State_Sunken;
+ bool enabled = menuItem->state & State_Enabled;
+
+ bool ignoreCheckMark = false;
+ int checkcol = qMax(menuItem->maxIconWidth, 20);
+
+#ifndef QT_NO_COMBOBOX
+ if (qobject_cast<const QComboBox*>(widget))
+ ignoreCheckMark = true; //ignore the checkmarks provided by the QComboMenuDelegate
+#endif
+
+ if (!ignoreCheckMark) {
+ // Check
+ QRect checkRect(option->rect.left() + 7, option->rect.center().y() - 6, 13, 13);
+ checkRect = visualRect(menuItem->direction, menuItem->rect, checkRect);
+ if (checkable) {
+ if (menuItem->checkType & QStyleOptionMenuItem::Exclusive) {
+ // Radio button
+ if (checked || sunken) {
+ painter->setRenderHint(QPainter::Antialiasing);
+ painter->setPen(Qt::NoPen);
+
+ QPalette::ColorRole textRole = !enabled ? QPalette::Text:
+ selected ? QPalette::HighlightedText : QPalette::ButtonText;
+ painter->setBrush(option->palette.brush( option->palette.currentColorGroup(), textRole));
+ painter->drawEllipse(checkRect.adjusted(4, 4, -4, -4));
+ }
+ } else {
+ // Check box
+ if (menuItem->icon.isNull()) {
+ if (checked || sunken) {
+ QImage image(qt_cleanlooks_menuitem_checkbox_checked);
+ if (enabled && (menuItem->state & State_Selected)) {
+ image.setColor(1, 0x55ffffff);
+ image.setColor(2, 0xAAffffff);
+ image.setColor(3, 0xBBffffff);
+ image.setColor(4, 0xFFffffff);
+ image.setColor(5, 0x33ffffff);
+ } else {
+ image.setColor(1, 0x55000000);
+ image.setColor(2, 0xAA000000);
+ image.setColor(3, 0xBB000000);
+ image.setColor(4, 0xFF000000);
+ image.setColor(5, 0x33000000);
+ }
+ painter->drawImage(QPoint(checkRect.center().x() - image.width() / 2,
+ checkRect.center().y() - image.height() / 2), image);
+ }
+ }
+ }
+ }
+ } else { //ignore checkmark
+ if (menuItem->icon.isNull())
+ checkcol = 0;
+ else
+ checkcol = menuItem->maxIconWidth;
+ }
+
+ // Text and icon, ripped from windows style
+ bool dis = !(menuItem->state & State_Enabled);
+ bool act = menuItem->state & State_Selected;
+ const QStyleOption *opt = option;
+ const QStyleOptionMenuItem *menuitem = menuItem;
+
+ QPainter *p = painter;
+ QRect vCheckRect = visualRect(opt->direction, menuitem->rect,
+ QRect(menuitem->rect.x(), menuitem->rect.y(),
+ checkcol, menuitem->rect.height()));
+ if (!menuItem->icon.isNull()) {
+ QIcon::Mode mode = dis ? QIcon::Disabled : QIcon::Normal;
+ if (act && !dis)
+ mode = QIcon::Active;
+ QPixmap pixmap;
+
+ int smallIconSize = pixelMetric(PM_SmallIconSize, option, widget);
+ QSize iconSize(smallIconSize, smallIconSize);
+#ifndef QT_NO_COMBOBOX
+ if (const QComboBox *combo = qobject_cast<const QComboBox*>(widget))
+ iconSize = combo->iconSize();
+#endif // QT_NO_COMBOBOX
+ if (checked)
+ pixmap = menuItem->icon.pixmap(iconSize, mode, QIcon::On);
+ else
+ pixmap = menuItem->icon.pixmap(iconSize, mode);
+
+ int pixw = pixmap.width();
+ int pixh = pixmap.height();
+
+ QRect pmr(0, 0, pixw, pixh);
+ pmr.moveCenter(vCheckRect.center());
+ painter->setPen(menuItem->palette.text().color());
+ if (checkable && checked) {
+ QStyleOption opt = *option;
+ if (act) {
+ QColor activeColor = mergedColors(option->palette.background().color(),
+ option->palette.highlight().color());
+ opt.palette.setBrush(QPalette::Button, activeColor);
+ }
+ opt.state |= State_Sunken;
+ opt.rect = vCheckRect;
+ drawPrimitive(PE_PanelButtonCommand, &opt, painter, widget);
+ }
+ painter->drawPixmap(pmr.topLeft(), pixmap);
+ }
+ if (selected) {
+ painter->setPen(menuItem->palette.highlightedText().color());
+ } else {
+ painter->setPen(menuItem->palette.text().color());
+ }
+ int x, y, w, h;
+ menuitem->rect.getRect(&x, &y, &w, &h);
+ int tab = menuitem->tabWidth;
+ QColor discol;
+ if (dis) {
+ discol = menuitem->palette.text().color();
+ p->setPen(discol);
+ }
+ int xm = windowsItemFrame + checkcol + windowsItemHMargin;
+ int xpos = menuitem->rect.x() + xm;
+
+ QRect textRect(xpos, y + windowsItemVMargin, w - xm - windowsRightBorder - tab + 1, h - 2 * windowsItemVMargin);
+ QRect vTextRect = visualRect(opt->direction, menuitem->rect, textRect);
+ QString s = menuitem->text;
+ if (!s.isEmpty()) { // draw text
+ p->save();
+ int t = s.indexOf(QLatin1Char('\t'));
+ int text_flags = Qt::AlignVCenter | Qt::TextShowMnemonic | Qt::TextDontClip | Qt::TextSingleLine;
+ if (!styleHint(SH_UnderlineShortcut, menuitem, widget))
+ text_flags |= Qt::TextHideMnemonic;
+ text_flags |= Qt::AlignLeft;
+ if (t >= 0) {
+ QRect vShortcutRect = visualRect(opt->direction, menuitem->rect,
+ QRect(textRect.topRight(), QPoint(menuitem->rect.right(), textRect.bottom())));
+ if (dis && !act && styleHint(SH_EtchDisabledText, option, widget)) {
+ p->setPen(menuitem->palette.light().color());
+ p->drawText(vShortcutRect.adjusted(1, 1, 1, 1), text_flags, s.mid(t + 1));
+ p->setPen(discol);
+ }
+ p->drawText(vShortcutRect, text_flags, s.mid(t + 1));
+ s = s.left(t);
+ }
+ QFont font = menuitem->font;
+
+ if (menuitem->menuItemType == QStyleOptionMenuItem::DefaultItem)
+ font.setBold(true);
+
+ p->setFont(font);
+ if (dis && !act && styleHint(SH_EtchDisabledText, option, widget)) {
+ p->setPen(menuitem->palette.light().color());
+ p->drawText(vTextRect.adjusted(1, 1, 1, 1), text_flags, s.left(t));
+ p->setPen(discol);
+ }
+ p->drawText(vTextRect, text_flags, s.left(t));
+ p->restore();
+ }
+
+ // Arrow
+ if (menuItem->menuItemType == QStyleOptionMenuItem::SubMenu) {// draw sub menu arrow
+ int dim = (menuItem->rect.height() - 4) / 2;
+ PrimitiveElement arrow;
+ arrow = QApplication::isRightToLeft() ? PE_IndicatorArrowLeft : PE_IndicatorArrowRight;
+ int xpos = menuItem->rect.left() + menuItem->rect.width() - 3 - dim;
+ QRect vSubMenuRect = visualRect(option->direction, menuItem->rect,
+ QRect(xpos, menuItem->rect.top() + menuItem->rect.height() / 2 - dim / 2, dim, dim));
+ QStyleOptionMenuItem newMI = *menuItem;
+ newMI.rect = vSubMenuRect;
+ newMI.state = !enabled ? State_None : State_Enabled;
+ if (selected)
+ newMI.palette.setColor(QPalette::ButtonText,
+ newMI.palette.highlightedText().color());
+ drawPrimitive(arrow, &newMI, painter, widget);
+ }
+ }
+ painter->restore();
+ break;
+ case CE_MenuHMargin:
+ case CE_MenuVMargin:
+ break;
+ case CE_MenuEmptyArea:
+ break;
+ case CE_PushButtonLabel:
+ if (const QStyleOptionButton *button = qstyleoption_cast<const QStyleOptionButton *>(option)) {
+ QRect ir = button->rect;
+ uint tf = Qt::AlignVCenter;
+ if (styleHint(SH_UnderlineShortcut, button, widget))
+ tf |= Qt::TextShowMnemonic;
+ else
+ tf |= Qt::TextHideMnemonic;
+
+ if (!button->icon.isNull()) {
+ //Center both icon and text
+ QPoint point;
+
+ QIcon::Mode mode = button->state & State_Enabled ? QIcon::Normal
+ : QIcon::Disabled;
+ if (mode == QIcon::Normal && button->state & State_HasFocus)
+ mode = QIcon::Active;
+ QIcon::State state = QIcon::Off;
+ if (button->state & State_On)
+ state = QIcon::On;
+
+ QPixmap pixmap = button->icon.pixmap(button->iconSize, mode, state);
+ int w = pixmap.width();
+ int h = pixmap.height();
+
+ if (!button->text.isEmpty())
+ w += button->fontMetrics.boundingRect(option->rect, tf, button->text).width() + 2;
+
+ point = QPoint(ir.x() + ir.width() / 2 - w / 2,
+ ir.y() + ir.height() / 2 - h / 2);
+
+ if (button->direction == Qt::RightToLeft)
+ point.rx() += pixmap.width();
+
+ painter->drawPixmap(visualPos(button->direction, button->rect, point), pixmap);
+
+ if (button->direction == Qt::RightToLeft)
+ ir.translate(-point.x() - 2, 0);
+ else
+ ir.translate(point.x() + pixmap.width(), 0);
+
+ // left-align text if there is
+ if (!button->text.isEmpty())
+ tf |= Qt::AlignLeft;
+
+ } else {
+ tf |= Qt::AlignHCenter;
+ }
+
+ if (button->features & QStyleOptionButton::HasMenu)
+ ir = ir.adjusted(0, 0, -pixelMetric(PM_MenuButtonIndicator, button, widget), 0);
+ drawItemText(painter, ir, tf, button->palette, (button->state & State_Enabled),
+ button->text, QPalette::ButtonText);
+ }
+ break;
+ case CE_MenuBarEmptyArea:
+ painter->save();
+ {
+ QColor shadow = mergedColors(option->palette.background().color().darker(120),
+ dark.lighter(140), 60);
+
+ QLinearGradient gradient(rect.topLeft(), QPoint(rect.bottomLeft().x(), rect.bottomLeft().y()*2));
+ gradient.setColorAt(0, option->palette.button().color());
+ gradient.setColorAt(1, option->palette.button().color().darker(110));
+ painter->fillRect(rect, gradient);
+
+#ifndef QT_NO_MAINWINDOW
+ if (widget && qobject_cast<const QMainWindow *>(widget->parentWidget())) {
+ QPen oldPen = painter->pen();
+ painter->setPen(QPen(shadow));
+ painter->drawLine(option->rect.bottomLeft(), option->rect.bottomRight());
+ }
+#endif // QT_NO_MAINWINDOW
+ }
+ painter->restore();
+ break;
+#ifndef QT_NO_TABBAR
+ case CE_TabBarTabShape:
+ painter->save();
+ if (const QStyleOptionTab *tab = qstyleoption_cast<const QStyleOptionTab *>(option)) {
+
+ bool rtlHorTabs = (tab->direction == Qt::RightToLeft
+ && (tab->shape == QTabBar::RoundedNorth
+ || tab->shape == QTabBar::RoundedSouth));
+ bool selected = tab->state & State_Selected;
+ bool lastTab = ((!rtlHorTabs && tab->position == QStyleOptionTab::End)
+ || (rtlHorTabs
+ && tab->position == QStyleOptionTab::Beginning));
+ bool onlyTab = tab->position == QStyleOptionTab::OnlyOneTab;
+ bool leftCornerWidget = (tab->cornerWidgets & QStyleOptionTab::LeftCornerWidget);
+
+ bool atBeginning = ((tab->position == (tab->direction == Qt::LeftToRight ?
+ QStyleOptionTab::Beginning : QStyleOptionTab::End)) || onlyTab);
+
+ bool onlyOne = tab->position == QStyleOptionTab::OnlyOneTab;
+ bool previousSelected =
+ ((!rtlHorTabs
+ && tab->selectedPosition == QStyleOptionTab::PreviousIsSelected)
+ || (rtlHorTabs
+ && tab->selectedPosition == QStyleOptionTab::NextIsSelected));
+ bool nextSelected =
+ ((!rtlHorTabs
+ && tab->selectedPosition == QStyleOptionTab::NextIsSelected)
+ || (rtlHorTabs
+ && tab->selectedPosition
+ == QStyleOptionTab::PreviousIsSelected));
+ int tabBarAlignment = styleHint(SH_TabBar_Alignment, tab, widget);
+ bool leftAligned = (!rtlHorTabs && tabBarAlignment == Qt::AlignLeft)
+ || (rtlHorTabs
+ && tabBarAlignment == Qt::AlignRight);
+
+ bool rightAligned = (!rtlHorTabs && tabBarAlignment == Qt::AlignRight)
+ || (rtlHorTabs
+ && tabBarAlignment == Qt::AlignLeft);
+
+ QColor light = tab->palette.light().color();
+ QColor midlight = tab->palette.midlight().color();
+
+ QColor background = tab->palette.background().color();
+ int borderThinkness = pixelMetric(PM_TabBarBaseOverlap, tab, widget);
+ if (selected)
+ borderThinkness /= 2;
+ QRect r2(option->rect);
+ int x1 = r2.left();
+ int x2 = r2.right();
+ int y1 = r2.top();
+ int y2 = r2.bottom();
+
+ QTransform rotMatrix;
+ bool flip = false;
+ painter->setPen(shadow);
+ QColor activeHighlight = option->palette.color(QPalette::Normal, QPalette::Highlight);
+ switch (tab->shape) {
+ case QTabBar::RoundedNorth:
+ break;
+ case QTabBar::RoundedSouth:
+ rotMatrix.rotate(180);
+ rotMatrix.translate(0, -rect.height() + 1);
+ rotMatrix.scale(-1, 1);
+ painter->setTransform(rotMatrix, true);
+ break;
+ case QTabBar::RoundedWest:
+ rotMatrix.rotate(180 + 90);
+ rotMatrix.scale(-1, 1);
+ flip = true;
+ painter->setTransform(rotMatrix, true);
+ break;
+ case QTabBar::RoundedEast:
+ rotMatrix.rotate(90);
+ rotMatrix.translate(0, - rect.width() + 1);
+ flip = true;
+ painter->setTransform(rotMatrix, true);
+ break;
+ default:
+ painter->restore();
+ QWindowsStyle::drawControl(element, tab, painter, widget);
+ return;
+ }
+
+ if (flip) {
+ QRect tmp = rect;
+ rect = QRect(tmp.y(), tmp.x(), tmp.height(), tmp.width());
+ int temp = x1;
+ x1 = y1;
+ y1 = temp;
+ temp = x2;
+ x2 = y2;
+ y2 = temp;
+ }
+
+ QLinearGradient gradient(rect.topLeft(), rect.bottomLeft());
+ if (option->palette.button().gradient()) {
+ if (selected)
+ gradient.setStops(option->palette.background().gradient()->stops());
+ else
+ gradient.setStops(option->palette.background().gradient()->stops());
+ }
+ else if (selected) {
+ gradient.setColorAt(0, option->palette.background().color().lighter(104));
+ gradient.setColorAt(1, tabFrameColor);
+ painter->fillRect(rect.adjusted(0, 2, 0, -1), gradient);
+ } else {
+ y1 += 2;
+ gradient.setColorAt(0, option->palette.background().color());
+ gradient.setColorAt(1, dark.lighter(120));
+ painter->fillRect(rect.adjusted(0, 2, 0, -2), gradient);
+ }
+
+ // Delete border
+ if (selected) {
+ painter->setPen(QPen(activeHighlight, 0));
+ painter->drawLine(x1 + 1, y1 + 1, x2 - 1, y1 + 1);
+ painter->drawLine(x1 , y1 + 2, x2 , y1 + 2);
+ } else {
+ painter->setPen(dark);
+ painter->drawLine(x1, y2 - 1, x2 + 2, y2 - 1 );
+ if (tab->shape == QTabBar::RoundedNorth || tab->shape == QTabBar::RoundedWest) {
+ painter->setPen(light);
+ painter->drawLine(x1, y2 , x2, y2 );
+ }
+ }
+ // Left
+ if (atBeginning || selected ) {
+ painter->setPen(light);
+ painter->drawLine(x1 + 1, y1 + 2 + 1, x1 + 1, y2 - ((onlyOne || atBeginning) && selected && leftAligned ? 0 : borderThinkness) - (atBeginning && leftCornerWidget ? 1 : 0));
+ painter->drawPoint(x1 + 1, y1 + 1);
+ painter->setPen(dark);
+ painter->drawLine(x1, y1 + 2, x1, y2 - ((onlyOne || atBeginning) && leftAligned ? 0 : borderThinkness) - (atBeginning && leftCornerWidget ? 1 : 0));
+ }
+ // Top
+ {
+ int beg = x1 + (previousSelected ? 0 : 2);
+ int end = x2 - (nextSelected ? 0 : 2);
+ painter->setPen(light);
+
+ if (!selected)painter->drawLine(beg - 2, y1 + 1, end, y1 + 1);
+
+ if (selected)
+ painter->setPen(QPen(activeHighlight.darker(150), 0));
+ else
+ painter->setPen(darkOutline);
+ painter->drawLine(beg, y1 , end, y1);
+
+ if (atBeginning|| selected) {
+ painter->drawPoint(beg - 1, y1 + 1);
+ } else if (!atBeginning) {
+ painter->drawPoint(beg - 1, y1);
+ painter->drawPoint(beg - 2, y1);
+ if (!lastTab) {
+ painter->setPen(dark.lighter(130));
+ painter->drawPoint(end + 1, y1);
+ painter->drawPoint(end + 2 , y1);
+ painter->drawPoint(end + 2, y1 + 1);
+ }
+ }
+ }
+ // Right
+ if (lastTab || selected || onlyOne || !nextSelected) {
+ painter->setPen(darkOutline);
+ painter->drawLine(x2, y1 + 2, x2, y2 - ((onlyOne || lastTab) && selected && rightAligned ? 0 : borderThinkness));
+ if (selected)
+ painter->setPen(QPen(activeHighlight.darker(150), 0));
+ else
+ painter->setPen(darkOutline);
+ painter->drawPoint(x2 - 1, y1 + 1);
+
+ if (selected) {
+ painter->setPen(background.darker(110));
+ painter->drawLine(x2 - 1, y1 + 3, x2 - 1, y2 - ((onlyOne || lastTab) && selected && rightAligned ? 0 : borderThinkness));
+ }
+ }
+ }
+ painter->restore();
+ break;
+
+#endif // QT_NO_TABBAR
+ default:
+ QWindowsStyle::drawControl(element,option,painter,widget);
+ break;
+ }
+}
+
+/*!
+ \reimp
+*/
+QPalette QCleanlooksStyle::standardPalette () const
+{
+ QPalette palette = QWindowsStyle::standardPalette();
+ palette.setBrush(QPalette::Active, QPalette::Highlight, QColor(98, 140, 178));
+ palette.setBrush(QPalette::Inactive, QPalette::Highlight, QColor(145, 141, 126));
+ palette.setBrush(QPalette::Disabled, QPalette::Highlight, QColor(145, 141, 126));
+
+ QColor backGround(239, 235, 231);
+
+ QColor light = backGround.lighter(150);
+ QColor base = Qt::white;
+ QColor dark = QColor(170, 156, 143).darker(110);
+ dark = backGround.darker(150);
+ QColor darkDisabled = QColor(209, 200, 191).darker(110);
+
+ //### Find the correct disabled text color
+ palette.setBrush(QPalette::Disabled, QPalette::Text, QColor(190, 190, 190));
+
+ palette.setBrush(QPalette::Window, backGround);
+ palette.setBrush(QPalette::Mid, backGround.darker(130));
+ palette.setBrush(QPalette::Light, light);
+
+ palette.setBrush(QPalette::Active, QPalette::Base, base);
+ palette.setBrush(QPalette::Inactive, QPalette::Base, base);
+ palette.setBrush(QPalette::Disabled, QPalette::Base, backGround);
+
+ palette.setBrush(QPalette::Midlight, palette.mid().color().lighter(110));
+
+ palette.setBrush(QPalette::All, QPalette::Dark, dark);
+ palette.setBrush(QPalette::Disabled, QPalette::Dark, darkDisabled);
+
+ QColor button = backGround;
+
+ palette.setBrush(QPalette::Button, button);
+
+ QColor shadow = dark.darker(135);
+ palette.setBrush(QPalette::Shadow, shadow);
+ palette.setBrush(QPalette::Disabled, QPalette::Shadow, shadow.lighter(150));
+ palette.setBrush(QPalette::HighlightedText, QColor(QRgb(0xffffffff)));
+ return palette;
+}
+
+/*!
+ \reimp
+*/
+void QCleanlooksStyle::drawComplexControl(ComplexControl control, const QStyleOptionComplex *option,
+ QPainter *painter, const QWidget *widget) const
+{
+ QColor button = option->palette.button().color();
+ QColor dark;
+ QColor grooveColor;
+ QColor darkOutline;
+ dark.setHsv(button.hue(),
+ CL_MIN(255, (int)(button.saturation()*1.9)),
+ CL_MIN(255, (int)(button.value()*0.7)));
+ grooveColor.setHsv(button.hue(),
+ CL_MIN(255, (int)(button.saturation()*2.6)),
+ CL_MIN(255, (int)(button.value()*0.9)));
+ darkOutline.setHsv(button.hue(),
+ CL_MIN(255, (int)(button.saturation()*3.0)),
+ CL_MIN(255, (int)(button.value()*0.6)));
+
+ QColor alphaCornerColor;
+ if (widget) {
+ // ### backgroundrole/foregroundrole should be part of the style option
+ alphaCornerColor = mergedColors(option->palette.color(widget->backgroundRole()), darkOutline);
+ } else {
+ alphaCornerColor = mergedColors(option->palette.background().color(), darkOutline);
+ }
+ QColor gripShadow = grooveColor.darker(110);
+ QColor buttonShadow = option->palette.button().color().darker(110);
+
+ QColor gradientStartColor = option->palette.button().color().lighter(108);
+ QColor gradientStopColor = mergedColors(option->palette.button().color().darker(108), dark.lighter(150), 70);
+
+ QColor highlightedGradientStartColor = option->palette.button().color();
+ QColor highlightedGradientStopColor = mergedColors(option->palette.button().color(), option->palette.highlight().color(), 85);
+
+ QColor highlightedDarkInnerBorderColor = mergedColors(option->palette.button().color(), option->palette.highlight().color(), 35);
+ QColor highlightedLightInnerBorderColor = mergedColors(option->palette.button().color(), option->palette.highlight().color(), 58);
+
+ QColor buttonShadowAlpha = option->palette.background().color().darker(105);
+
+ QPalette palette = option->palette;
+
+ switch (control) {
+#ifndef QT_NO_SPINBOX
+ case CC_SpinBox:
+ if (const QStyleOptionSpinBox *spinBox = qstyleoption_cast<const QStyleOptionSpinBox *>(option)) {
+ QPixmap cache;
+ QString pixmapName = uniqueName(QLatin1String("spinbox"), spinBox, spinBox->rect.size());
+ if (!UsePixmapCache || !QPixmapCache::find(pixmapName, cache)) {
+ cache = QPixmap(spinBox->rect.size());
+ cache.fill(Qt::transparent);
+ QRect pixmapRect(0, 0, spinBox->rect.width(), spinBox->rect.height());
+ QPainter cachePainter(&cache);
+
+ bool isEnabled = (spinBox->state & State_Enabled);
+ //bool focus = isEnabled && (spinBox->state & State_HasFocus);
+ bool hover = isEnabled && (spinBox->state & State_MouseOver);
+ bool sunken = (spinBox->state & State_Sunken);
+ bool upIsActive = (spinBox->activeSubControls == SC_SpinBoxUp);
+ bool downIsActive = (spinBox->activeSubControls == SC_SpinBoxDown);
+
+ QRect rect = pixmapRect;
+ QStyleOptionSpinBox spinBoxCopy = *spinBox;
+ spinBoxCopy.rect = pixmapRect;
+ QRect upRect = subControlRect(CC_SpinBox, &spinBoxCopy, SC_SpinBoxUp, widget);
+ QRect downRect = subControlRect(CC_SpinBox, &spinBoxCopy, SC_SpinBoxDown, widget);
+
+ int fw = spinBoxCopy.frame ? pixelMetric(PM_SpinBoxFrameWidth, &spinBoxCopy, widget) : 0;
+ cachePainter.fillRect(rect.adjusted(1, qMax(fw - 1, 0), -1, -fw),
+ option->palette.base());
+
+ QRect r = rect.adjusted(0, 1, 0, -1);
+ if (spinBox->frame) {
+
+ QColor topShadow = darkOutline;
+ topShadow.setAlpha(60);
+ cachePainter.setPen(topShadow);
+
+ // antialias corners
+ cachePainter.drawPoint(QPoint(r.right(), r.top() + 1));
+ cachePainter.drawPoint(QPoint(r.right() - 1, r.top() ));
+ cachePainter.drawPoint(QPoint(r.right(), r.bottom() - 1));
+ cachePainter.drawPoint(QPoint(r.right() - 1, r.bottom() ));
+ cachePainter.drawPoint(QPoint(r.left() + 1, r.bottom()));
+ cachePainter.drawPoint(QPoint(r.left(), r.bottom() - 1));
+ cachePainter.drawPoint(QPoint(r.left() + 1, r.top()));
+ cachePainter.drawPoint(QPoint(r.left(), r.top() + 1));
+
+ // draw frame
+ topShadow.setAlpha(30);
+ cachePainter.setPen(topShadow);
+ cachePainter.drawLine(QPoint(r.left() + 2, r.top() - 1), QPoint(r.right() - 2, r.top() - 1));
+
+ cachePainter.setPen(QPen(option->palette.background().color(), 1));
+ cachePainter.drawLine(QPoint(r.left() + 2, r.top() + 1), QPoint(r.right() - 2, r.top() + 1));
+ QColor highlight = Qt::white;
+ highlight.setAlpha(130);
+ cachePainter.setPen(option->palette.base().color().darker(120));
+ cachePainter.drawLine(QPoint(r.left() + 1, r.top() + 1),
+ QPoint(r.right() - 1, r.top() + 1));
+ cachePainter.drawLine(QPoint(r.left() + 1, r.top() + 1),
+ QPoint(r.left() + 1, r.bottom() - 1));
+ cachePainter.setPen(option->palette.base().color());
+ cachePainter.drawLine(QPoint(r.right() - 1, r.top() + 1),
+ QPoint(r.right() - 1, r.bottom() - 1));
+ cachePainter.drawLine(QPoint(r.left() + 1, r.bottom() - 1),
+ QPoint(r.right() - 1, r.bottom() - 1));
+ cachePainter.setPen(highlight);
+ cachePainter.drawLine(QPoint(r.left() + 3, r.bottom() + 1),
+ QPoint(r.right() - 3, r.bottom() + 1));
+
+ cachePainter.setPen(QPen(darkOutline, 1));
+
+ // top and bottom lines
+ cachePainter.drawLine(QPoint(r.left() + 2, r.bottom()), QPoint(r.right()- 2, r.bottom()));
+ cachePainter.drawLine(QPoint(r.left() + 2, r.top()), QPoint(r.right() - 2, r.top()));
+ cachePainter.drawLine(QPoint(r.right(), r.top() + 2), QPoint(r.right(), r.bottom() - 2));
+ cachePainter.drawLine(QPoint(r.left(), r.top() + 2), QPoint(r.left(), r.bottom() - 2));
+ }
+
+ // gradients
+ qt_cleanlooks_draw_gradient(&cachePainter, upRect,
+ gradientStartColor.darker(106),
+ gradientStopColor, TopDown, option->palette.button());
+ qt_cleanlooks_draw_gradient(&cachePainter, downRect.adjusted(0, 0, 0, 1),
+ gradientStartColor.darker(106),
+ gradientStopColor, TopDown, option->palette.button());
+ if (isEnabled) {
+ if(upIsActive) {
+ if (sunken) {
+ cachePainter.fillRect(upRect.adjusted(1, 0, 0, 0), gradientStopColor.darker(110));
+ } else if (hover) {
+ qt_cleanlooks_draw_gradient(&cachePainter, upRect.adjusted(1, 0, 0, 0),
+ gradientStartColor.lighter(110),
+ gradientStopColor.lighter(110), TopDown, option->palette.button());
+ }
+ }
+ if(downIsActive) {
+ if (sunken) {
+ cachePainter.fillRect(downRect.adjusted(1, 0, 0, 1), gradientStopColor.darker(110));
+
+ } else if (hover) {
+ qt_cleanlooks_draw_gradient(&cachePainter, downRect.adjusted(1, 0, 0, 1),
+ gradientStartColor.lighter(110),
+ gradientStopColor.lighter(110), TopDown, option->palette.button());
+ }
+ }
+ }
+
+ if (spinBox->frame) {
+ // rounded corners
+ cachePainter.drawPoint(QPoint(r.left() + 1, r.bottom() - 1));
+ cachePainter.drawPoint(QPoint(r.left() + 1, r.top() + 1));
+ cachePainter.drawPoint(QPoint(r.right() - 1, r.bottom() - 1));
+ cachePainter.drawPoint(QPoint(r.right() - 1, r.top() + 1));
+
+ if (option->state & State_HasFocus) {
+ QColor darkoutline = option->palette.highlight().color().darker(150);
+ QColor innerline = mergedColors(option->palette.highlight().color(), Qt::white);
+ cachePainter.setPen(QPen(innerline, 0));
+ if (spinBox->direction == Qt::LeftToRight) {
+ cachePainter.drawRect(rect.adjusted(1, 2, -3 -downRect.width(), -3));
+ cachePainter.setPen(QPen(darkoutline, 0));
+ cachePainter.drawLine(QPoint(r.left() + 2, r.bottom()), QPoint(r.right()- downRect.width() - 1, r.bottom()));
+ cachePainter.drawLine(QPoint(r.left() + 2, r.top()), QPoint(r.right() - downRect.width() - 1, r.top()));
+ cachePainter.drawLine(QPoint(r.right() - downRect.width() - 1, r.top() + 1), QPoint(r.right()- downRect.width() - 1, r.bottom() - 1));
+ cachePainter.drawLine(QPoint(r.left(), r.top() + 2), QPoint(r.left(), r.bottom() - 2));
+ cachePainter.drawPoint(QPoint(r.left() + 1, r.bottom() - 1));
+ cachePainter.drawPoint(QPoint(r.left() + 1, r.top() + 1));
+ cachePainter.drawLine(QPoint(r.left(), r.top() + 2), QPoint(r.left(), r.bottom() - 2));
+ } else {
+ cachePainter.drawRect(rect.adjusted(downRect.width() + 2, 2, -2, -3));
+ cachePainter.setPen(QPen(darkoutline, 0));
+ cachePainter.drawLine(QPoint(r.left() + downRect.width(), r.bottom()), QPoint(r.right()- 2 - 1, r.bottom()));
+ cachePainter.drawLine(QPoint(r.left() + downRect.width(), r.top()), QPoint(r.right() - 2 - 1, r.top()));
+
+ cachePainter.drawLine(QPoint(r.right(), r.top() + 2), QPoint(r.right(), r.bottom() - 2));
+ cachePainter.drawPoint(QPoint(r.right() - 1, r.bottom() - 1));
+ cachePainter.drawPoint(QPoint(r.right() - 1, r.top() + 1));
+ cachePainter.drawLine(QPoint(r.left() + downRect.width() + 1, r.top()),
+ QPoint(r.left() + downRect.width() + 1, r.bottom()));
+ }
+ }
+ }
+
+ // outline the up/down buttons
+ cachePainter.setPen(darkOutline);
+ QColor light = option->palette.light().color().lighter();
+
+ if (spinBox->direction == Qt::RightToLeft) {
+ cachePainter.drawLine(upRect.right(), upRect.top() - 1, upRect.right(), downRect.bottom() + 1);
+ cachePainter.setPen(light);
+ cachePainter.drawLine(upRect.right() - 1, upRect.top() + 3, upRect.right() - 1, downRect.bottom() );
+ } else {
+ cachePainter.drawLine(upRect.left(), upRect.top() - 1, upRect.left(), downRect.bottom() + 1);
+ cachePainter.setPen(light);
+ cachePainter.drawLine(upRect.left() + 1, upRect.top() , upRect.left() + 1, downRect.bottom() );
+ }
+ if (upIsActive && sunken) {
+ cachePainter.setPen(gradientStopColor.darker(130));
+ cachePainter.drawLine(upRect.left() + 1, upRect.top(), upRect.left() + 1, upRect.bottom());
+ cachePainter.drawLine(upRect.left(), upRect.top() - 1, upRect.right(), upRect.top() - 1);
+ } else {
+ cachePainter.setPen(light);
+ cachePainter.drawLine(upRect.topLeft() + QPoint(1, -1), upRect.topRight() + QPoint(-1, -1));
+ cachePainter.setPen(darkOutline);
+ cachePainter.drawLine(upRect.bottomLeft(), upRect.bottomRight());
+ }
+ if (downIsActive && sunken) {
+ cachePainter.setPen(gradientStopColor.darker(130));
+ cachePainter.drawLine(downRect.left() + 1, downRect.top(), downRect.left() + 1, downRect.bottom() + 1);
+ cachePainter.drawLine(downRect.left(), downRect.top(), downRect.right(), downRect.top());
+ cachePainter.setPen(gradientStopColor.darker(110));
+ cachePainter.drawLine(downRect.left(), downRect.bottom() + 1, downRect.right(), downRect.bottom() + 1);
+ } else {
+ cachePainter.setPen(light);
+ cachePainter.drawLine(downRect.topLeft() + QPoint(2,0), downRect.topRight());
+ }
+
+ if (spinBox->buttonSymbols == QAbstractSpinBox::PlusMinus) {
+ int centerX = upRect.center().x();
+ int centerY = upRect.center().y();
+ cachePainter.setPen(spinBox->palette.foreground().color());
+
+ // plus/minus
+ if (spinBox->activeSubControls == SC_SpinBoxUp && sunken) {
+ cachePainter.drawLine(1 + centerX - 2, 1 + centerY, 1 + centerX + 2, 1 + centerY);
+ cachePainter.drawLine(1 + centerX, 1 + centerY - 2, 1 + centerX, 1 + centerY + 2);
+ } else {
+ cachePainter.drawLine(centerX - 2, centerY, centerX + 2, centerY);
+ cachePainter.drawLine(centerX, centerY - 2, centerX, centerY + 2);
+ }
+
+ centerX = downRect.center().x();
+ centerY = downRect.center().y();
+ if (spinBox->activeSubControls == SC_SpinBoxDown && sunken) {
+ cachePainter.drawLine(1 + centerX - 2, 1 + centerY, 1 + centerX + 2, 1 + centerY);
+ } else {
+ cachePainter.drawLine(centerX - 2, centerY, centerX + 2, centerY);
+ }
+ } else if (spinBox->buttonSymbols == QAbstractSpinBox::UpDownArrows){
+ // arrows
+ QImage upArrow(qt_spinbox_button_arrow_up);
+ upArrow.setColor(1, spinBox->palette.foreground().color().rgba());
+
+ cachePainter.drawImage(upRect.center().x() - upArrow.width() / 2,
+ upRect.center().y() - upArrow.height() / 2,
+ upArrow);
+
+ QImage downArrow(qt_spinbox_button_arrow_down);
+ downArrow.setColor(1, spinBox->palette.foreground().color().rgba());
+
+ cachePainter.drawImage(downRect.center().x() - downArrow.width() / 2,
+ downRect.center().y() - downArrow.height() / 2 + 1,
+ downArrow);
+ }
+
+ QColor disabledColor = option->palette.background().color();
+ disabledColor.setAlpha(150);
+ if (!(spinBox->stepEnabled & QAbstractSpinBox::StepUpEnabled))
+ cachePainter.fillRect(upRect.adjusted(1, 0, 0, 0), disabledColor);
+ if (!(spinBox->stepEnabled & QAbstractSpinBox::StepDownEnabled)) {
+ cachePainter.fillRect(downRect.adjusted(1, 0, 0, 0), disabledColor);
+ }
+ cachePainter.end();
+ if (UsePixmapCache)
+ QPixmapCache::insert(pixmapName, cache);
+ }
+ painter->drawPixmap(spinBox->rect.topLeft(), cache);
+ }
+ break;
+#endif // QT_NO_SPINBOX
+ case CC_TitleBar:
+ painter->save();
+ if (const QStyleOptionTitleBar *titleBar = qstyleoption_cast<const QStyleOptionTitleBar *>(option)) {
+ const int buttonMargin = 5;
+ bool active = (titleBar->titleBarState & State_Active);
+ QRect fullRect = titleBar->rect;
+ QPalette palette = option->palette;
+ QColor highlight = option->palette.highlight().color();
+
+ QColor titleBarFrameBorder(active ? highlight.darker(180): dark.darker(110));
+ QColor titleBarHighlight(active ? highlight.lighter(120): palette.background().color().lighter(120));
+ QColor textColor(active ? 0xffffff : 0xff000000);
+ QColor textAlphaColor(active ? 0xffffff : 0xff000000 );
+
+#ifdef QT3_SUPPORT
+ if (widget && widget->inherits("Q3DockWindowTitleBar")) {
+ QStyleOptionDockWidgetV2 dockwidget;
+ dockwidget.QStyleOption::operator=(*option);
+ drawControl(CE_DockWidgetTitle, &dockwidget, painter, widget);
+ } else
+#endif // QT3_SUPPORT
+ {
+ // Fill title bar gradient
+ QColor titlebarColor = QColor(active ? highlight: palette.background().color());
+ QColor titleBarGradientStop(active ? highlight.darker(150): palette.background().color().darker(120));
+ QLinearGradient gradient(option->rect.center().x(), option->rect.top(),
+ option->rect.center().x(), option->rect.bottom());
+
+ gradient.setColorAt(0, titlebarColor.lighter(114));
+ gradient.setColorAt(0.5, titlebarColor.lighter(102));
+ gradient.setColorAt(0.51, titlebarColor.darker(104));
+ gradient.setColorAt(1, titlebarColor);
+ painter->fillRect(option->rect.adjusted(1, 1, -1, 0), gradient);
+
+ // Frame and rounded corners
+ painter->setPen(titleBarFrameBorder);
+
+ // top outline
+ painter->drawLine(fullRect.left() + 5, fullRect.top(), fullRect.right() - 5, fullRect.top());
+ painter->drawLine(fullRect.left(), fullRect.top() + 4, fullRect.left(), fullRect.bottom());
+ painter->drawPoint(fullRect.left() + 4, fullRect.top() + 1);
+ painter->drawPoint(fullRect.left() + 3, fullRect.top() + 1);
+ painter->drawPoint(fullRect.left() + 2, fullRect.top() + 2);
+ painter->drawPoint(fullRect.left() + 1, fullRect.top() + 3);
+ painter->drawPoint(fullRect.left() + 1, fullRect.top() + 4);
+
+ painter->drawLine(fullRect.right(), fullRect.top() + 4, fullRect.right(), fullRect.bottom());
+ painter->drawPoint(fullRect.right() - 3, fullRect.top() + 1);
+ painter->drawPoint(fullRect.right() - 4, fullRect.top() + 1);
+ painter->drawPoint(fullRect.right() - 2, fullRect.top() + 2);
+ painter->drawPoint(fullRect.right() - 1, fullRect.top() + 3);
+ painter->drawPoint(fullRect.right() - 1, fullRect.top() + 4);
+
+ // draw bottomline
+ painter->drawLine(fullRect.right(), fullRect.bottom(), fullRect.left(), fullRect.bottom());
+
+ // top highlight
+ painter->setPen(titleBarHighlight);
+ painter->drawLine(fullRect.left() + 6, fullRect.top() + 1, fullRect.right() - 6, fullRect.top() + 1);
+ }
+ // draw title
+ QRect textRect = subControlRect(CC_TitleBar, titleBar, SC_TitleBarLabel, widget);
+ QFont font = painter->font();
+ font.setBold(true);
+ painter->setFont(font);
+ painter->setPen(active? (titleBar->palette.text().color().lighter(120)) :
+ titleBar->palette.text().color() );
+ // Note workspace also does elliding but it does not use the correct font
+ QString title = QFontMetrics(font).elidedText(titleBar->text, Qt::ElideRight, textRect.width() - 14);
+ painter->drawText(textRect.adjusted(1, 1, 1, 1), title, QTextOption(Qt::AlignHCenter | Qt::AlignVCenter));
+ painter->setPen(Qt::white);
+ if (active)
+ painter->drawText(textRect, title, QTextOption(Qt::AlignHCenter | Qt::AlignVCenter));
+ // min button
+ if ((titleBar->subControls & SC_TitleBarMinButton) && (titleBar->titleBarFlags & Qt::WindowMinimizeButtonHint) &&
+ !(titleBar->titleBarState& Qt::WindowMinimized)) {
+ QRect minButtonRect = subControlRect(CC_TitleBar, titleBar, SC_TitleBarMinButton, widget);
+ if (minButtonRect.isValid()) {
+ bool hover = (titleBar->activeSubControls & SC_TitleBarMinButton) && (titleBar->state & State_MouseOver);
+ bool sunken = (titleBar->activeSubControls & SC_TitleBarMinButton) && (titleBar->state & State_Sunken);
+ qt_cleanlooks_draw_mdibutton(painter, titleBar, minButtonRect, hover, sunken);
+ QRect minButtonIconRect = minButtonRect.adjusted(buttonMargin ,buttonMargin , -buttonMargin, -buttonMargin);
+ painter->setPen(textColor);
+ painter->drawLine(minButtonIconRect.center().x() - 2, minButtonIconRect.center().y() + 3,
+ minButtonIconRect.center().x() + 3, minButtonIconRect.center().y() + 3);
+ painter->drawLine(minButtonIconRect.center().x() - 2, minButtonIconRect.center().y() + 4,
+ minButtonIconRect.center().x() + 3, minButtonIconRect.center().y() + 4);
+ painter->setPen(textAlphaColor);
+ painter->drawLine(minButtonIconRect.center().x() - 3, minButtonIconRect.center().y() + 3,
+ minButtonIconRect.center().x() - 3, minButtonIconRect.center().y() + 4);
+ painter->drawLine(minButtonIconRect.center().x() + 4, minButtonIconRect.center().y() + 3,
+ minButtonIconRect.center().x() + 4, minButtonIconRect.center().y() + 4);
+ }
+ }
+ // max button
+ if ((titleBar->subControls & SC_TitleBarMaxButton) && (titleBar->titleBarFlags & Qt::WindowMaximizeButtonHint) &&
+ !(titleBar->titleBarState & Qt::WindowMaximized)) {
+ QRect maxButtonRect = subControlRect(CC_TitleBar, titleBar, SC_TitleBarMaxButton, widget);
+ if (maxButtonRect.isValid()) {
+ bool hover = (titleBar->activeSubControls & SC_TitleBarMaxButton) && (titleBar->state & State_MouseOver);
+ bool sunken = (titleBar->activeSubControls & SC_TitleBarMaxButton) && (titleBar->state & State_Sunken);
+ qt_cleanlooks_draw_mdibutton(painter, titleBar, maxButtonRect, hover, sunken);
+
+ QRect maxButtonIconRect = maxButtonRect.adjusted(buttonMargin, buttonMargin, -buttonMargin, -buttonMargin);
+
+ painter->setPen(textColor);
+ painter->drawRect(maxButtonIconRect.adjusted(0, 0, -1, -1));
+ painter->drawLine(maxButtonIconRect.left() + 1, maxButtonIconRect.top() + 1,
+ maxButtonIconRect.right() - 1, maxButtonIconRect.top() + 1);
+ painter->setPen(textAlphaColor);
+ painter->drawPoint(maxButtonIconRect.topLeft());
+ painter->drawPoint(maxButtonIconRect.topRight());
+ painter->drawPoint(maxButtonIconRect.bottomLeft());
+ painter->drawPoint(maxButtonIconRect.bottomRight());
+ }
+ }
+
+ // close button
+ if ((titleBar->subControls & SC_TitleBarCloseButton) && (titleBar->titleBarFlags & Qt::WindowSystemMenuHint)) {
+ QRect closeButtonRect = subControlRect(CC_TitleBar, titleBar, SC_TitleBarCloseButton, widget);
+ if (closeButtonRect.isValid()) {
+ bool hover = (titleBar->activeSubControls & SC_TitleBarCloseButton) && (titleBar->state & State_MouseOver);
+ bool sunken = (titleBar->activeSubControls & SC_TitleBarCloseButton) && (titleBar->state & State_Sunken);
+ qt_cleanlooks_draw_mdibutton(painter, titleBar, closeButtonRect, hover, sunken);
+ QRect closeIconRect = closeButtonRect.adjusted(buttonMargin, buttonMargin, -buttonMargin, -buttonMargin);
+ painter->setPen(textAlphaColor);
+ painter->drawLine(closeIconRect.left() + 1, closeIconRect.top(),
+ closeIconRect.right(), closeIconRect.bottom() - 1);
+ painter->drawLine(closeIconRect.left(), closeIconRect.top() + 1,
+ closeIconRect.right() - 1, closeIconRect.bottom());
+ painter->drawLine(closeIconRect.right() - 1, closeIconRect.top(),
+ closeIconRect.left(), closeIconRect.bottom() - 1);
+ painter->drawLine(closeIconRect.right(), closeIconRect.top() + 1,
+ closeIconRect.left() + 1, closeIconRect.bottom());
+ painter->drawPoint(closeIconRect.topLeft());
+ painter->drawPoint(closeIconRect.topRight());
+ painter->drawPoint(closeIconRect.bottomLeft());
+ painter->drawPoint(closeIconRect.bottomRight());
+
+ painter->setPen(textColor);
+ painter->drawLine(closeIconRect.left() + 1, closeIconRect.top() + 1,
+ closeIconRect.right() - 1, closeIconRect.bottom() - 1);
+ painter->drawLine(closeIconRect.left() + 1, closeIconRect.bottom() - 1,
+ closeIconRect.right() - 1, closeIconRect.top() + 1);
+ }
+ }
+
+ // normalize button
+ if ((titleBar->subControls & SC_TitleBarNormalButton) &&
+ (((titleBar->titleBarFlags & Qt::WindowMinimizeButtonHint) &&
+ (titleBar->titleBarState & Qt::WindowMinimized)) ||
+ ((titleBar->titleBarFlags & Qt::WindowMaximizeButtonHint) &&
+ (titleBar->titleBarState & Qt::WindowMaximized)))) {
+ QRect normalButtonRect = subControlRect(CC_TitleBar, titleBar, SC_TitleBarNormalButton, widget);
+ if (normalButtonRect.isValid()) {
+
+ bool hover = (titleBar->activeSubControls & SC_TitleBarNormalButton) && (titleBar->state & State_MouseOver);
+ bool sunken = (titleBar->activeSubControls & SC_TitleBarNormalButton) && (titleBar->state & State_Sunken);
+ QRect normalButtonIconRect = normalButtonRect.adjusted(buttonMargin, buttonMargin, -buttonMargin, -buttonMargin);
+ qt_cleanlooks_draw_mdibutton(painter, titleBar, normalButtonRect, hover, sunken);
+
+ QRect frontWindowRect = normalButtonIconRect.adjusted(0, 3, -3, 0);
+ painter->setPen(textColor);
+ painter->drawRect(frontWindowRect.adjusted(0, 0, -1, -1));
+ painter->drawLine(frontWindowRect.left() + 1, frontWindowRect.top() + 1,
+ frontWindowRect.right() - 1, frontWindowRect.top() + 1);
+ painter->setPen(textAlphaColor);
+ painter->drawPoint(frontWindowRect.topLeft());
+ painter->drawPoint(frontWindowRect.topRight());
+ painter->drawPoint(frontWindowRect.bottomLeft());
+ painter->drawPoint(frontWindowRect.bottomRight());
+
+ QRect backWindowRect = normalButtonIconRect.adjusted(3, 0, 0, -3);
+ QRegion clipRegion = backWindowRect;
+ clipRegion -= frontWindowRect;
+ painter->save();
+ painter->setClipRegion(clipRegion);
+ painter->setPen(textColor);
+ painter->drawRect(backWindowRect.adjusted(0, 0, -1, -1));
+ painter->drawLine(backWindowRect.left() + 1, backWindowRect.top() + 1,
+ backWindowRect.right() - 1, backWindowRect.top() + 1);
+ painter->setPen(textAlphaColor);
+ painter->drawPoint(backWindowRect.topLeft());
+ painter->drawPoint(backWindowRect.topRight());
+ painter->drawPoint(backWindowRect.bottomLeft());
+ painter->drawPoint(backWindowRect.bottomRight());
+ painter->restore();
+ }
+ }
+
+ // context help button
+ if (titleBar->subControls & SC_TitleBarContextHelpButton
+ && (titleBar->titleBarFlags & Qt::WindowContextHelpButtonHint)) {
+ QRect contextHelpButtonRect = subControlRect(CC_TitleBar, titleBar, SC_TitleBarContextHelpButton, widget);
+ if (contextHelpButtonRect.isValid()) {
+ bool hover = (titleBar->activeSubControls & SC_TitleBarContextHelpButton) && (titleBar->state & State_MouseOver);
+ bool sunken = (titleBar->activeSubControls & SC_TitleBarContextHelpButton) && (titleBar->state & State_Sunken);
+ qt_cleanlooks_draw_mdibutton(painter, titleBar, contextHelpButtonRect, hover, sunken);
+
+ QColor blend;
+ QImage image(qt_titlebar_context_help);
+ QColor alpha = textColor;
+ alpha.setAlpha(128);
+ image.setColor(1, textColor.rgba());
+ image.setColor(2, alpha.rgba());
+ painter->setRenderHint(QPainter::SmoothPixmapTransform);
+ painter->drawImage(contextHelpButtonRect.adjusted(4, 4, -4, -4), image);
+ }
+ }
+
+ // shade button
+ if (titleBar->subControls & SC_TitleBarShadeButton && (titleBar->titleBarFlags & Qt::WindowShadeButtonHint)) {
+ QRect shadeButtonRect = subControlRect(CC_TitleBar, titleBar, SC_TitleBarShadeButton, widget);
+ if (shadeButtonRect.isValid()) {
+ bool hover = (titleBar->activeSubControls & SC_TitleBarShadeButton) && (titleBar->state & State_MouseOver);
+ bool sunken = (titleBar->activeSubControls & SC_TitleBarShadeButton) && (titleBar->state & State_Sunken);
+ qt_cleanlooks_draw_mdibutton(painter, titleBar, shadeButtonRect, hover, sunken);
+ QImage image(qt_scrollbar_button_arrow_up);
+ image.setColor(1, textColor.rgba());
+ painter->drawImage(shadeButtonRect.adjusted(5, 7, -5, -7), image);
+ }
+ }
+
+ // unshade button
+ if (titleBar->subControls & SC_TitleBarUnshadeButton && (titleBar->titleBarFlags & Qt::WindowShadeButtonHint)) {
+ QRect unshadeButtonRect = subControlRect(CC_TitleBar, titleBar, SC_TitleBarUnshadeButton, widget);
+ if (unshadeButtonRect.isValid()) {
+ bool hover = (titleBar->activeSubControls & SC_TitleBarUnshadeButton) && (titleBar->state & State_MouseOver);
+ bool sunken = (titleBar->activeSubControls & SC_TitleBarUnshadeButton) && (titleBar->state & State_Sunken);
+ qt_cleanlooks_draw_mdibutton(painter, titleBar, unshadeButtonRect, hover, sunken);
+ QImage image(qt_scrollbar_button_arrow_down);
+ image.setColor(1, textColor.rgba());
+ painter->drawImage(unshadeButtonRect.adjusted(5, 7, -5, -7), image);
+ }
+ }
+
+ if ((titleBar->subControls & SC_TitleBarSysMenu) && (titleBar->titleBarFlags & Qt::WindowSystemMenuHint)) {
+ QRect iconRect = subControlRect(CC_TitleBar, titleBar, SC_TitleBarSysMenu, widget);
+ if (iconRect.isValid()) {
+ if (!titleBar->icon.isNull()) {
+ titleBar->icon.paint(painter, iconRect);
+ } else {
+ QStyleOption tool(0);
+ tool.palette = titleBar->palette;
+ QPixmap pm = standardIcon(SP_TitleBarMenuButton, &tool, widget).pixmap(16, 16);
+ tool.rect = iconRect;
+ painter->save();
+ drawItemPixmap(painter, iconRect, Qt::AlignCenter, pm);
+ painter->restore();
+ }
+ }
+ }
+ }
+ painter->restore();
+ break;
+#ifndef QT_NO_SCROLLBAR
+ case CC_ScrollBar:
+ painter->save();
+ if (const QStyleOptionSlider *scrollBar = qstyleoption_cast<const QStyleOptionSlider *>(option)) {
+ bool isEnabled = scrollBar->state & State_Enabled;
+ bool reverse = scrollBar->direction == Qt::RightToLeft;
+ bool horizontal = scrollBar->orientation == Qt::Horizontal;
+ bool sunken = scrollBar->state & State_Sunken;
+
+ painter->fillRect(option->rect, option->palette.background());
+
+ QRect rect = scrollBar->rect;
+ QRect scrollBarSubLine = subControlRect(control, scrollBar, SC_ScrollBarSubLine, widget);
+ QRect scrollBarAddLine = subControlRect(control, scrollBar, SC_ScrollBarAddLine, widget);
+ QRect scrollBarSlider = subControlRect(control, scrollBar, SC_ScrollBarSlider, widget);
+ QRect grooveRect = subControlRect(control, scrollBar, SC_ScrollBarGroove, widget);
+
+ // paint groove
+ if (scrollBar->subControls & SC_ScrollBarGroove) {
+ painter->setBrush(grooveColor);
+ painter->setPen(Qt::NoPen);
+ if (horizontal) {
+ painter->drawRect(grooveRect);
+ painter->setPen(darkOutline);
+ painter->drawLine(grooveRect.topLeft(), grooveRect.topRight());
+ painter->drawLine(grooveRect.bottomLeft(), grooveRect.bottomRight());
+ } else {
+ painter->drawRect(grooveRect);
+ painter->setPen(darkOutline);
+ painter->drawLine(grooveRect.topLeft(), grooveRect.bottomLeft());
+ painter->drawLine(grooveRect.topRight(), grooveRect.bottomRight());
+ }
+ }
+ //paint slider
+ if (scrollBar->subControls & SC_ScrollBarSlider) {
+ QRect pixmapRect = scrollBarSlider;
+ if (horizontal)
+ pixmapRect.adjust(-1, 0, 0, -1);
+ else
+ pixmapRect.adjust(0, -1, -1, 0);
+
+ if (isEnabled) {
+ QLinearGradient gradient(pixmapRect.center().x(), pixmapRect.top(),
+ pixmapRect.center().x(), pixmapRect.bottom());
+ if (!horizontal)
+ gradient = QLinearGradient(pixmapRect.left(), pixmapRect.center().y(),
+ pixmapRect.right(), pixmapRect.center().y());
+
+ if (option->palette.button().gradient()) {
+ gradient.setStops(option->palette.button().gradient()->stops());
+ } else {
+ if (sunken || (option->state & State_MouseOver &&
+ (scrollBar->activeSubControls & SC_ScrollBarSlider))) {
+ gradient.setColorAt(0, gradientStartColor.lighter(110));
+ gradient.setColorAt(1, gradientStopColor.lighter(110));
+ } else {
+ gradient.setColorAt(0, gradientStartColor);
+ gradient.setColorAt(1, gradientStopColor);
+ }
+ }
+ painter->setPen(QPen(darkOutline, 0));
+ painter->setBrush(gradient);
+ painter->drawRect(pixmapRect);
+
+
+ //calculate offsets used by highlight and shadow
+ int yoffset, xoffset;
+ if (option->state & State_Horizontal) {
+ xoffset = 0;
+ yoffset = 1;
+ } else {
+ xoffset = 1;
+ yoffset = 0;
+ }
+ //draw slider highlights
+ painter->setPen(QPen(gradientStopColor, 0));
+ painter->drawLine(scrollBarSlider.left() + xoffset,
+ scrollBarSlider.bottom() - yoffset,
+ scrollBarSlider.right() - xoffset,
+ scrollBarSlider.bottom() - yoffset);
+ painter->drawLine(scrollBarSlider.right() - xoffset,
+ scrollBarSlider.top() + yoffset,
+ scrollBarSlider.right() - xoffset,
+ scrollBarSlider.bottom() - yoffset);
+
+ //draw slider shadow
+ painter->setPen(QPen(gradientStartColor, 0));
+ painter->drawLine(scrollBarSlider.left() + xoffset,
+ scrollBarSlider.top() + yoffset,
+ scrollBarSlider.right() - xoffset,
+ scrollBarSlider.top() + yoffset);
+ painter->drawLine(scrollBarSlider.left() + xoffset,
+ scrollBarSlider.top() + yoffset,
+ scrollBarSlider.left() + xoffset,
+ scrollBarSlider.bottom() - yoffset);
+ } else {
+ QLinearGradient gradient(pixmapRect.center().x(), pixmapRect.top(),
+ pixmapRect.center().x(), pixmapRect.bottom());
+ if (!horizontal) {
+ gradient = QLinearGradient(pixmapRect.left(), pixmapRect.center().y(),
+ pixmapRect.right(), pixmapRect.center().y());
+ }
+ if (sunken) {
+ gradient.setColorAt(0, gradientStartColor.lighter(110));
+ gradient.setColorAt(1, gradientStopColor.lighter(110));
+ } else {
+ gradient.setColorAt(0, gradientStartColor);
+ gradient.setColorAt(1, gradientStopColor);
+ }
+ painter->setPen(darkOutline);
+ painter->setBrush(gradient);
+ painter->drawRect(pixmapRect);
+ }
+ int gripMargin = 4;
+ //draw grips
+ if (horizontal) {
+ for (int i = -3; i< 6 ; i += 3) {
+ painter->setPen(QPen(gripShadow, 1));
+ painter->drawLine(
+ QPoint(scrollBarSlider.center().x() + i ,
+ scrollBarSlider.top() + gripMargin),
+ QPoint(scrollBarSlider.center().x() + i,
+ scrollBarSlider.bottom() - gripMargin));
+ painter->setPen(QPen(palette.light(), 1));
+ painter->drawLine(
+ QPoint(scrollBarSlider.center().x() + i + 1,
+ scrollBarSlider.top() + gripMargin ),
+ QPoint(scrollBarSlider.center().x() + i + 1,
+ scrollBarSlider.bottom() - gripMargin));
+ }
+ } else {
+ for (int i = -3; i < 6 ; i += 3) {
+ painter->setPen(QPen(gripShadow, 1));
+ painter->drawLine(
+ QPoint(scrollBarSlider.left() + gripMargin ,
+ scrollBarSlider.center().y()+ i),
+ QPoint(scrollBarSlider.right() - gripMargin,
+ scrollBarSlider.center().y()+ i));
+ painter->setPen(QPen(palette.light(), 1));
+ painter->drawLine(
+ QPoint(scrollBarSlider.left() + gripMargin,
+ scrollBarSlider.center().y() + 1 + i),
+ QPoint(scrollBarSlider.right() - gripMargin,
+ scrollBarSlider.center().y() + 1 + i));
+ }
+ }
+ }
+
+ // The SubLine (up/left) buttons
+ if (scrollBar->subControls & SC_ScrollBarSubLine) {
+ //int scrollBarExtent = pixelMetric(PM_ScrollBarExtent, option, widget);
+ QRect pixmapRect = scrollBarSubLine;
+ if (isEnabled ) {
+ QRect fillRect = pixmapRect.adjusted(1, 1, -1, -1);
+ // Gradients
+ if ((scrollBar->activeSubControls & SC_ScrollBarSubLine) && sunken) {
+ qt_cleanlooks_draw_gradient(painter,
+ QRect(fillRect),
+ gradientStopColor.darker(120),
+ gradientStopColor.darker(120),
+ horizontal ? TopDown : FromLeft, option->palette.button());
+ } else {
+ qt_cleanlooks_draw_gradient(painter,
+ QRect(fillRect),
+ gradientStartColor.lighter(105),
+ gradientStopColor,
+ horizontal ? TopDown : FromLeft, option->palette.button());
+ }
+ }
+ // Details
+ QImage subButton;
+ if (horizontal) {
+ subButton = QImage(reverse ? qt_scrollbar_button_right : qt_scrollbar_button_left);
+ } else {
+ subButton = QImage(qt_scrollbar_button_up);
+ }
+ subButton.setColor(1, alphaCornerColor.rgba());
+ subButton.setColor(2, darkOutline.rgba());
+ if ((scrollBar->activeSubControls & SC_ScrollBarSubLine) && sunken) {
+ subButton.setColor(3, gradientStopColor.darker(140).rgba());
+ subButton.setColor(4, gradientStopColor.darker(120).rgba());
+ } else {
+ subButton.setColor(3, gradientStartColor.lighter(105).rgba());
+ subButton.setColor(4, gradientStopColor.rgba());
+ }
+ subButton.setColor(5, scrollBar->palette.text().color().rgba());
+ painter->drawImage(pixmapRect, subButton);
+
+ // Arrows
+ PrimitiveElement arrow;
+ if (option->state & State_Horizontal)
+ arrow = option->direction == Qt::LeftToRight ? PE_IndicatorArrowLeft: PE_IndicatorArrowRight;
+ else
+ arrow = PE_IndicatorArrowUp;
+ QStyleOption arrowOpt = *option;
+ arrowOpt.rect = scrollBarSubLine.adjusted(3, 3, -2, -2);
+ drawPrimitive(arrow, &arrowOpt, painter, widget);
+
+
+ // The AddLine (down/right) button
+ if (scrollBar->subControls & SC_ScrollBarAddLine) {
+ QString addLinePixmapName = uniqueName(QLatin1String("scrollbar_addline"), option, QSize(16, 16));
+ QRect pixmapRect = scrollBarAddLine;
+ if (isEnabled) {
+ QRect fillRect = pixmapRect.adjusted(1, 1, -1, -1);
+ // Gradients
+ if ((scrollBar->activeSubControls & SC_ScrollBarAddLine) && sunken) {
+ qt_cleanlooks_draw_gradient(painter,
+ fillRect,
+ gradientStopColor.darker(120),
+ gradientStopColor.darker(120),
+ horizontal ? TopDown: FromLeft, option->palette.button());
+ } else {
+ qt_cleanlooks_draw_gradient(painter,
+ fillRect,
+ gradientStartColor.lighter(105),
+ gradientStopColor,
+ horizontal ? TopDown : FromLeft, option->palette.button());
+ }
+ }
+ // Details
+ QImage addButton;
+ if (horizontal) {
+ addButton = QImage(reverse ? qt_scrollbar_button_left : qt_scrollbar_button_right);
+ } else {
+ addButton = QImage(qt_scrollbar_button_down);
+ }
+ addButton.setColor(1, alphaCornerColor.rgba());
+ addButton.setColor(2, darkOutline.rgba());
+ if ((scrollBar->activeSubControls & SC_ScrollBarAddLine) && sunken) {
+ addButton.setColor(3, gradientStopColor.darker(140).rgba());
+ addButton.setColor(4, gradientStopColor.darker(120).rgba());
+ } else {
+ addButton.setColor(3, gradientStartColor.lighter(105).rgba());
+ addButton.setColor(4, gradientStopColor.rgba());
+ }
+ addButton.setColor(5, scrollBar->palette.text().color().rgba());
+ painter->drawImage(pixmapRect, addButton);
+
+ PrimitiveElement arrow;
+ if (option->state & State_Horizontal)
+ arrow = option->direction == Qt::LeftToRight ? PE_IndicatorArrowRight : PE_IndicatorArrowLeft;
+ else
+ arrow = PE_IndicatorArrowDown;
+
+ QStyleOption arrowOpt = *option;
+ arrowOpt.rect = scrollBarAddLine.adjusted(3, 3, -2, -2);
+ drawPrimitive(arrow, &arrowOpt, painter, widget);
+ }
+ }
+ }
+ painter->restore();
+ break;;
+#endif // QT_NO_SCROLLBAR
+#ifndef QT_NO_COMBOBOX
+ case CC_ComboBox:
+ painter->save();
+ if (const QStyleOptionComboBox *comboBox = qstyleoption_cast<const QStyleOptionComboBox *>(option)) {
+ bool sunken = comboBox->state & State_On; // play dead, if combobox has no items
+ bool isEnabled = (comboBox->state & State_Enabled);
+ bool focus = isEnabled && (comboBox->state & State_HasFocus);
+ QPixmap cache;
+ QString pixmapName = uniqueName(QLatin1String("combobox"), option, comboBox->rect.size());
+ if (sunken)
+ pixmapName += QLatin1String("-sunken");
+ if (comboBox->editable)
+ pixmapName += QLatin1String("-editable");
+ if (isEnabled)
+ pixmapName += QLatin1String("-enabled");
+
+ if (!UsePixmapCache || !QPixmapCache::find(pixmapName, cache)) {
+ cache = QPixmap(comboBox->rect.size());
+ cache.fill(Qt::transparent);
+ QPainter cachePainter(&cache);
+ QRect pixmapRect(0, 0, comboBox->rect.width(), comboBox->rect.height());
+ QStyleOptionComboBox comboBoxCopy = *comboBox;
+ comboBoxCopy.rect = pixmapRect;
+
+ QRect rect = pixmapRect;
+ QRect downArrowRect = subControlRect(CC_ComboBox, &comboBoxCopy,
+ SC_ComboBoxArrow, widget);
+ QRect editRect = subControlRect(CC_ComboBox, &comboBoxCopy,
+ SC_ComboBoxEditField, widget);
+ // Draw a push button
+ if (comboBox->editable) {
+ QStyleOptionFrame buttonOption;
+ buttonOption.QStyleOption::operator=(*comboBox);
+ buttonOption.rect = rect;
+ buttonOption.state = comboBox->state & (State_Enabled | State_MouseOver);
+
+ if (sunken) {
+ buttonOption.state |= State_Sunken;
+ buttonOption.state &= ~State_MouseOver;
+ }
+
+ drawPrimitive(PE_PanelButtonCommand, &buttonOption, &cachePainter, widget);
+
+ //remove shadow from left side of edit field when pressed:
+ if (comboBox->direction != Qt::RightToLeft)
+ cachePainter.fillRect(editRect.left() - 1, editRect.top() + 1, editRect.left(),
+ editRect.bottom() - 3, option->palette.base());
+
+ cachePainter.setPen(dark.lighter(110));
+ if (!sunken) {
+ int borderSize = 2;
+ if (comboBox->direction == Qt::RightToLeft) {
+ cachePainter.drawLine(QPoint(downArrowRect.right() - 1, downArrowRect.top() + borderSize ),
+ QPoint(downArrowRect.right() - 1, downArrowRect.bottom() - borderSize));
+ cachePainter.setPen(option->palette.light().color());
+ cachePainter.drawLine(QPoint(downArrowRect.right(), downArrowRect.top() + borderSize),
+ QPoint(downArrowRect.right(), downArrowRect.bottom() - borderSize));
+ } else {
+ cachePainter.drawLine(QPoint(downArrowRect.left() , downArrowRect.top() + borderSize),
+ QPoint(downArrowRect.left() , downArrowRect.bottom() - borderSize));
+ cachePainter.setPen(option->palette.light().color());
+ cachePainter.drawLine(QPoint(downArrowRect.left() + 1, downArrowRect.top() + borderSize),
+ QPoint(downArrowRect.left() + 1, downArrowRect.bottom() - borderSize));
+ }
+ } else {
+ if (comboBox->direction == Qt::RightToLeft) {
+ cachePainter.drawLine(QPoint(downArrowRect.right(), downArrowRect.top() + 2),
+ QPoint(downArrowRect.right(), downArrowRect.bottom() - 2));
+
+ } else {
+ cachePainter.drawLine(QPoint(downArrowRect.left(), downArrowRect.top() + 2),
+ QPoint(downArrowRect.left(), downArrowRect.bottom() - 2));
+ }
+ }
+ } else {
+ QStyleOptionButton buttonOption;
+ buttonOption.QStyleOption::operator=(*comboBox);
+ buttonOption.rect = rect;
+ buttonOption.state = comboBox->state & (State_Enabled | State_MouseOver);
+ if (sunken) {
+ buttonOption.state |= State_Sunken;
+ buttonOption.state &= ~State_MouseOver;
+ }
+ drawPrimitive(PE_PanelButtonCommand, &buttonOption, &cachePainter, widget);
+
+ cachePainter.setPen(buttonShadow.darker(102));
+ int borderSize = 4;
+
+ if (!sunken) {
+ if (comboBox->direction == Qt::RightToLeft) {
+ cachePainter.drawLine(QPoint(downArrowRect.right() + 1, downArrowRect.top() + borderSize),
+ QPoint(downArrowRect.right() + 1, downArrowRect.bottom() - borderSize));
+ cachePainter.setPen(option->palette.light().color());
+ cachePainter.drawLine(QPoint(downArrowRect.right(), downArrowRect.top() + borderSize),
+ QPoint(downArrowRect.right(), downArrowRect.bottom() - borderSize));
+ } else {
+ cachePainter.drawLine(QPoint(downArrowRect.left() - 1, downArrowRect.top() + borderSize),
+ QPoint(downArrowRect.left() - 1, downArrowRect.bottom() - borderSize));
+ cachePainter.setPen(option->palette.light().color());
+ cachePainter.drawLine(QPoint(downArrowRect.left() , downArrowRect.top() + borderSize),
+ QPoint(downArrowRect.left() , downArrowRect.bottom() - borderSize));
+ }
+ } else {
+ cachePainter.setPen(dark.lighter(110));
+ if (comboBox->direction == Qt::RightToLeft) {
+ cachePainter.drawLine(QPoint(downArrowRect.right() + 1, downArrowRect.top() + borderSize),
+ QPoint(downArrowRect.right() + 1, downArrowRect.bottom() - borderSize));
+
+ } else {
+ cachePainter.drawLine(QPoint(downArrowRect.left() - 1, downArrowRect.top() + borderSize),
+ QPoint(downArrowRect.left() - 1, downArrowRect.bottom() - borderSize));
+ }
+ }
+ }
+
+
+ if (comboBox->subControls & SC_ComboBoxArrow) {
+ if (comboBox->editable) {
+ // Draw the down arrow
+ QImage downArrow(qt_cleanlooks_arrow_down_xpm);
+ downArrow.setColor(1, comboBox->palette.foreground().color().rgba());
+ cachePainter.drawImage(downArrowRect.center().x() - downArrow.width() / 2,
+ downArrowRect.center().y() - downArrow.height() / 2 + 1, downArrow);
+ } else {
+ // Draw the up/down arrow
+ QImage upArrow(qt_scrollbar_button_arrow_up);
+ upArrow.setColor(1, comboBox->palette.foreground().color().rgba());
+ QImage downArrow(qt_scrollbar_button_arrow_down);
+ downArrow.setColor(1, comboBox->palette.foreground().color().rgba());
+ cachePainter.drawImage(downArrowRect.center().x() - downArrow.width() / 2,
+ downArrowRect.center().y() - upArrow.height() - 1 , upArrow);
+ cachePainter.drawImage(downArrowRect.center().x() - downArrow.width() / 2,
+ downArrowRect.center().y() + 2, downArrow);
+ }
+ }
+ // Draw the focus rect
+ if ((focus && (option->state & State_KeyboardFocusChange)) && !comboBox->editable) {
+ QStyleOptionFocusRect focus;
+ focus.rect = subControlRect(CC_ComboBox, &comboBoxCopy, SC_ComboBoxEditField, widget)
+ .adjusted(0, 2, option->direction == Qt::RightToLeft ? 1 : -1, -2);
+ drawPrimitive(PE_FrameFocusRect, &focus, &cachePainter, widget);
+ }
+ cachePainter.end();
+ if (UsePixmapCache)
+ QPixmapCache::insert(pixmapName, cache);
+ }
+ painter->drawPixmap(comboBox->rect.topLeft(), cache);
+ }
+ painter->restore();
+ break;
+#endif // QT_NO_COMBOBOX
+#ifndef QT_NO_GROUPBOX
+ case CC_GroupBox:
+ painter->save();
+ if (const QStyleOptionGroupBox *groupBox = qstyleoption_cast<const QStyleOptionGroupBox *>(option)) {
+ QRect textRect = subControlRect(CC_GroupBox, groupBox, SC_GroupBoxLabel, widget);
+ QRect checkBoxRect = subControlRect(CC_GroupBox, groupBox, SC_GroupBoxCheckBox, widget);
+ bool flat = groupBox->features & QStyleOptionFrameV2::Flat;
+
+ if(!flat) {
+ if (groupBox->subControls & QStyle::SC_GroupBoxFrame) {
+ QStyleOptionFrameV2 frame;
+ frame.QStyleOption::operator=(*groupBox);
+ frame.features = groupBox->features;
+ frame.lineWidth = groupBox->lineWidth;
+ frame.midLineWidth = groupBox->midLineWidth;
+ frame.rect = subControlRect(CC_GroupBox, option, SC_GroupBoxFrame, widget);
+
+ painter->save();
+ QRegion region(groupBox->rect);
+ bool ltr = groupBox->direction == Qt::LeftToRight;
+ region -= checkBoxRect.united(textRect).adjusted(ltr ? -4 : 0, 0, ltr ? 0 : 4, 0);
+ if (!groupBox->text.isEmpty() || groupBox->subControls & SC_GroupBoxCheckBox)
+ painter->setClipRegion(region);
+ frame.palette.setBrush(QPalette::Dark, option->palette.mid().color().lighter(110));
+ drawPrimitive(PE_FrameGroupBox, &frame, painter);
+ painter->restore();
+ }
+ }
+ // Draw title
+ if ((groupBox->subControls & QStyle::SC_GroupBoxLabel) && !groupBox->text.isEmpty()) {
+ if (!groupBox->text.isEmpty()) {
+ QColor textColor = groupBox->textColor;
+ if (textColor.isValid())
+ painter->setPen(textColor);
+ int alignment = int(groupBox->textAlignment);
+ if (!styleHint(QStyle::SH_UnderlineShortcut, option, widget))
+ alignment |= Qt::TextHideMnemonic;
+ if (flat) {
+ QFont font = painter->font();
+ font.setBold(true);
+ painter->setFont(font);
+ if (groupBox->subControls & SC_GroupBoxCheckBox) {
+ textRect.adjust(checkBoxRect.right() + 4, 0, checkBoxRect.right() + 4, 0);
+ }
+ }
+ painter->drawText(textRect, Qt::TextShowMnemonic | Qt::AlignLeft| alignment, groupBox->text);
+ }
+ }
+ if (groupBox->subControls & SC_GroupBoxCheckBox) {
+ QStyleOptionButton box;
+ box.QStyleOption::operator=(*groupBox);
+ box.rect = checkBoxRect;
+ drawPrimitive(PE_IndicatorCheckBox, &box, painter, widget);
+ }
+ }
+ painter->restore();
+ break;
+#endif // QT_NO_GROUPBOX
+#ifndef QT_NO_SLIDER
+ case CC_Slider:
+ if (const QStyleOptionSlider *slider = qstyleoption_cast<const QStyleOptionSlider *>(option)) {
+ QRect groove = subControlRect(CC_Slider, option, SC_SliderGroove, widget);
+ QRect handle = subControlRect(CC_Slider, option, SC_SliderHandle, widget);
+ QRect ticks = subControlRect(CC_Slider, option, SC_SliderTickmarks, widget);
+
+ bool horizontal = slider->orientation == Qt::Horizontal;
+ bool ticksAbove = slider->tickPosition & QSlider::TicksAbove;
+ bool ticksBelow = slider->tickPosition & QSlider::TicksBelow;
+ QColor activeHighlight = option->palette.color(QPalette::Normal, QPalette::Highlight);
+ QPixmap cache;
+
+ QBrush oldBrush = painter->brush();
+ QPen oldPen = painter->pen();
+
+ QColor shadowAlpha(Qt::black);
+ shadowAlpha.setAlpha(10);
+ QColor highlightAlpha(Qt::white);
+ highlightAlpha.setAlpha(80);
+
+ if ((option->subControls & SC_SliderGroove) && groove.isValid()) {
+ QString groovePixmapName = uniqueName(QLatin1String("slider_groove"), option, groove.size());
+ QRect pixmapRect(0, 0, groove.width(), groove.height());
+
+ // draw background groove
+ if (!UsePixmapCache || !QPixmapCache::find(groovePixmapName, cache)) {
+ cache = QPixmap(pixmapRect.size());
+ cache.fill(Qt::transparent);
+ QPainter groovePainter(&cache);
+
+ groovePainter.setPen(shadowAlpha);
+ groovePainter.drawLine(1, 0, groove.width(), 0);
+ groovePainter.drawLine(0, 0, 0, groove.height() - 1);
+
+ groovePainter.setPen(highlightAlpha);
+ groovePainter.drawLine(1, groove.height() - 1, groove.width() - 1, groove.height() - 1);
+ groovePainter.drawLine(groove.width() - 1, 1, groove.width() - 1, groove.height() - 1);
+ QLinearGradient gradient;
+ if (horizontal) {
+ gradient.setStart(pixmapRect.center().x(), pixmapRect.top());
+ gradient.setFinalStop(pixmapRect.center().x(), pixmapRect.bottom());
+ }
+ else {
+ gradient.setStart(pixmapRect.left(), pixmapRect.center().y());
+ gradient.setFinalStop(pixmapRect.right(), pixmapRect.center().y());
+ }
+ groovePainter.setPen(QPen(darkOutline.darker(110), 0));
+ gradient.setColorAt(0, grooveColor.darker(110));//dark.lighter(120));
+ gradient.setColorAt(1, grooveColor.lighter(110));//palette.button().color().darker(115));
+ groovePainter.setBrush(gradient);
+ groovePainter.drawRect(pixmapRect.adjusted(1, 1, -2, -2));
+ groovePainter.end();
+ if (UsePixmapCache)
+ QPixmapCache::insert(groovePixmapName, cache);
+ }
+ painter->drawPixmap(groove.topLeft(), cache);
+
+ // draw blue groove highlight
+ QRect clipRect;
+ groovePixmapName += QLatin1String("_blue");
+ if (!UsePixmapCache || !QPixmapCache::find(groovePixmapName, cache)) {
+ cache = QPixmap(pixmapRect.size());
+ cache.fill(Qt::transparent);
+ QPainter groovePainter(&cache);
+ QLinearGradient gradient;
+ if (horizontal) {
+ gradient.setStart(pixmapRect.center().x(), pixmapRect.top());
+ gradient.setFinalStop(pixmapRect.center().x(), pixmapRect.bottom());
+ }
+ else {
+ gradient.setStart(pixmapRect.left(), pixmapRect.center().y());
+ gradient.setFinalStop(pixmapRect.right(), pixmapRect.center().y());
+ }
+ groovePainter.setPen(QPen(activeHighlight.darker(150), 0));
+ gradient.setColorAt(0, activeHighlight.darker(120));
+ gradient.setColorAt(1, activeHighlight.lighter(160));
+ groovePainter.setBrush(gradient);
+ groovePainter.drawRect(pixmapRect.adjusted(1, 1, -2, -2));
+ groovePainter.end();
+ if (UsePixmapCache)
+ QPixmapCache::insert(groovePixmapName, cache);
+ }
+ if (horizontal) {
+ if (slider->upsideDown)
+ clipRect = QRect(handle.right(), groove.top(), groove.right() - handle.right(), groove.height());
+ else
+ clipRect = QRect(groove.left(), groove.top(), handle.left(), groove.height());
+ } else {
+ if (slider->upsideDown)
+ clipRect = QRect(groove.left(), handle.bottom(), groove.width(), groove.height() - handle.bottom());
+ else
+ clipRect = QRect(groove.left(), groove.top(), groove.width(), handle.top() - groove.top());
+ }
+ painter->save();
+ painter->setClipRect(clipRect.adjusted(0, 0, 1, 1));
+ painter->drawPixmap(groove.topLeft(), cache);
+ painter->restore();
+ }
+
+ // draw handle
+ if ((option->subControls & SC_SliderHandle) ) {
+ QString handlePixmapName = uniqueName(QLatin1String("slider_handle"), option, handle.size());
+ if (!UsePixmapCache || !QPixmapCache::find(handlePixmapName, cache)) {
+ cache = QPixmap(handle.size());
+ cache.fill(Qt::transparent);
+ QRect pixmapRect(0, 0, handle.width(), handle.height());
+ QPainter handlePainter(&cache);
+
+ QColor highlightedGradientStartColor = option->palette.button().color();
+ QColor highlightedGradientStopColor = option->palette.light().color();
+ QColor gradientStartColor = mergedColors(option->palette.button().color().lighter(155),
+ dark.lighter(155), 50);
+ QColor gradientStopColor = gradientStartColor.darker(108);
+ QRect gradRect = pixmapRect.adjusted(2, 2, -2, -2);
+
+ QColor gradientBgStartColor = gradientStartColor;
+ QColor gradientBgStopColor = gradientStopColor;
+
+ QColor outline = option->state & State_Enabled ? dark : dark.lighter(130);
+ if (option->state & State_Enabled && option->activeSubControls & SC_SliderHandle) {
+ gradientBgStartColor = option->palette.highlight().color().lighter(180);
+ gradientBgStopColor = option->palette.highlight().color().lighter(110);
+ outline = option->palette.highlight().color().darker(130);
+ }
+
+ // gradient fill
+ QRect innerBorder = gradRect;
+ QRect r = pixmapRect.adjusted(1, 1, -1, -1);
+
+ qt_cleanlooks_draw_gradient(&handlePainter, gradRect,
+ gradientBgStartColor,
+ gradientBgStopColor,
+ horizontal ? TopDown : FromLeft, option->palette.button());
+
+ handlePainter.setPen(QPen(outline.darker(110), 1));
+ handlePainter.drawLine(QPoint(r.left(), r.top() + 3), QPoint(r.left(), r.bottom() - 3));
+ handlePainter.drawLine(QPoint(r.right(), r.top() + 3), QPoint(r.right(), r.bottom() - 3));
+ handlePainter.drawLine(QPoint(r.left() + 3, r.bottom()), QPoint(r.right() - 3, r.bottom()));
+
+ handlePainter.save();
+ handlePainter.setRenderHint(QPainter::Antialiasing);
+ handlePainter.translate(0.5, 0.5);
+ handlePainter.drawLine(QPoint(r.left(), r.bottom() - 2), QPoint(r.left() + 2, r.bottom()));
+ handlePainter.drawLine(QPoint(r.left(), r.top() + 2), QPoint(r.left() + 2, r.top()));
+ handlePainter.drawLine(QPoint(r.right(), r.bottom() - 2), QPoint(r.right() - 2, r.bottom()));
+ handlePainter.drawLine(QPoint(r.right(), r.top() + 2), QPoint(r.right() - 2, r.top()));
+ handlePainter.restore();;
+ handlePainter.setPen(QPen(outline.darker(130), 1));
+ handlePainter.drawLine(QPoint(r.left() + 3, r.top()), QPoint(r.right() - 3, r.top()));
+ QColor cornerAlpha = outline.darker(120);
+ cornerAlpha.setAlpha(80);
+
+ handlePainter.setPen(cornerAlpha);
+ if (horizontal) {
+ handlePainter.drawLine(QPoint(r.left() + 6, r.top()), QPoint(r.left() + 6, r.bottom()));
+ handlePainter.drawLine(QPoint(r.right() - 6, r.top()), QPoint(r.right() - 6, r.bottom()));
+ } else {
+ handlePainter.drawLine(QPoint(r.left(), r.top() + 6), QPoint(r.right(), r.top() + 6));
+ handlePainter.drawLine(QPoint(r.left(), r.bottom() - 6), QPoint(r.right(), r.bottom() - 6));
+ }
+
+ //handle shadow
+ handlePainter.setPen(shadowAlpha);
+ handlePainter.drawLine(QPoint(r.left() + 2, r.bottom() + 1), QPoint(r.right() - 2, r.bottom() + 1));
+ handlePainter.drawLine(QPoint(r.right() + 1, r.bottom() - 3), QPoint(r.right() + 1, r.top() + 4));
+ handlePainter.drawLine(QPoint(r.right() - 1, r.bottom()), QPoint(r.right() + 1, r.bottom() - 2));
+
+ qt_cleanlooks_draw_gradient(&handlePainter, horizontal ?
+ gradRect.adjusted(6, 0, -6, 0) : gradRect.adjusted(0, 6, 0, -6),
+ gradientStartColor,
+ gradientStopColor.darker(106),
+ horizontal ? TopDown : FromLeft,
+ option->palette.button());
+
+ //draw grips
+ for (int i = -3; i< 6 ; i += 3) {
+ for (int j = -3; j< 6 ; j += 3) {
+ handlePainter.fillRect(r.center().x() + i, r.center().y() + j, 2, 2, highlightAlpha);
+ handlePainter.setPen(gripShadow);
+ handlePainter.drawPoint(r.center().x() + i, r.center().y() + j );
+ }
+ }
+ handlePainter.end();
+ if (UsePixmapCache)
+ QPixmapCache::insert(handlePixmapName, cache);
+ }
+
+ painter->drawPixmap(handle.topLeft(), cache);
+
+ if (slider->state & State_HasFocus) {
+ QStyleOptionFocusRect fropt;
+ fropt.QStyleOption::operator=(*slider);
+ fropt.rect = slider->rect;
+ drawPrimitive(PE_FrameFocusRect, &fropt, painter, widget);
+ }
+ }
+ if (option->subControls & SC_SliderTickmarks) {
+ painter->setPen(darkOutline);
+ int tickSize = pixelMetric(PM_SliderTickmarkOffset, option, widget);
+ int available = pixelMetric(PM_SliderSpaceAvailable, slider, widget);
+ int interval = slider->tickInterval;
+ if (interval <= 0) {
+ interval = slider->singleStep;
+ if (QStyle::sliderPositionFromValue(slider->minimum, slider->maximum, interval,
+ available)
+ - QStyle::sliderPositionFromValue(slider->minimum, slider->maximum,
+ 0, available) < 3)
+ interval = slider->pageStep;
+ }
+ if (interval <= 0)
+ interval = 1;
+
+ int v = slider->minimum;
+ int len = pixelMetric(PM_SliderLength, slider, widget);
+ while (v <= slider->maximum + 1) {
+ if (v == slider->maximum + 1 && interval == 1)
+ break;
+ const int v_ = qMin(v, slider->maximum);
+ int pos = sliderPositionFromValue(slider->minimum, slider->maximum,
+ v_, (horizontal
+ ? slider->rect.width()
+ : slider->rect.height()) - len,
+ slider->upsideDown) + len / 2;
+ int extra = 2 - ((v_ == slider->minimum || v_ == slider->maximum) ? 1 : 0);
+
+ if (horizontal) {
+ if (ticksAbove) {
+ painter->drawLine(pos, slider->rect.top() + extra,
+ pos, slider->rect.top() + tickSize);
+ }
+ if (ticksBelow) {
+ painter->drawLine(pos, slider->rect.bottom() - extra,
+ pos, slider->rect.bottom() - tickSize);
+ }
+ } else {
+ if (ticksAbove) {
+ painter->drawLine(slider->rect.left() + extra, pos,
+ slider->rect.left() + tickSize, pos);
+ }
+ if (ticksBelow) {
+ painter->drawLine(slider->rect.right() - extra, pos,
+ slider->rect.right() - tickSize, pos);
+ }
+ }
+ // in the case where maximum is max int
+ int nextInterval = v + interval;
+ if (nextInterval < v)
+ break;
+ v = nextInterval;
+ }
+ }
+ painter->setBrush(oldBrush);
+ painter->setPen(oldPen);
+ }
+ break;
+#endif // QT_NO_SLIDER
+ default:
+ QWindowsStyle::drawComplexControl(control, option, painter, widget);
+ break;
+ }
+}
+
+/*!
+ \reimp
+*/
+int QCleanlooksStyle::pixelMetric(PixelMetric metric, const QStyleOption *option, const QWidget *widget) const
+{
+ int ret = -1;
+ switch (metric) {
+ case PM_ButtonDefaultIndicator:
+ ret = 0;
+ break;
+ case PM_ButtonShiftHorizontal:
+ case PM_ButtonShiftVertical:
+ ret = 0;
+ break;
+ case PM_MessageBoxIconSize:
+ ret = 48;
+ break;
+ case PM_ListViewIconSize:
+ ret = 24;
+ break;
+ case PM_DialogButtonsSeparator:
+ case PM_SplitterWidth:
+ ret = 6;
+ break;
+ case PM_ScrollBarSliderMin:
+ ret = 26;
+ break;
+ case PM_MenuPanelWidth: //menu framewidth
+ ret = 2;
+ break;
+ case PM_TitleBarHeight:
+ ret = 24;
+ break;
+ case PM_ScrollBarExtent:
+ ret = 15;
+ break;
+ case PM_SliderThickness:
+ ret = 15;
+ break;
+ case PM_SliderLength:
+ ret = 27;
+ break;
+ case PM_DockWidgetTitleMargin:
+ ret = 1;
+ break;
+ case PM_MenuBarVMargin:
+ ret = 1;
+ break;
+ case PM_DefaultFrameWidth:
+ ret = 2;
+ break;
+ case PM_SpinBoxFrameWidth:
+ ret = 3;
+ break;
+ case PM_MenuBarItemSpacing:
+ ret = 6;
+ case PM_MenuBarHMargin:
+ ret = 0;
+ break;
+ case PM_ToolBarHandleExtent:
+ ret = 9;
+ break;
+ case PM_ToolBarItemSpacing:
+ ret = 2;
+ break;
+ case PM_ToolBarFrameWidth:
+ ret = 0;
+ break;
+ case PM_ToolBarItemMargin:
+ ret = 1;
+ break;
+ case PM_SmallIconSize:
+ ret = 16;
+ break;
+ case PM_ButtonIconSize:
+ ret = 24;
+ break;
+ case PM_MenuVMargin:
+ case PM_MenuHMargin:
+ ret = 0;
+ break;
+ case PM_DockWidgetTitleBarButtonMargin:
+ ret = 4;
+ break;
+ case PM_MaximumDragDistance:
+ return -1;
+ case PM_TabCloseIndicatorWidth:
+ case PM_TabCloseIndicatorHeight:
+ return 20;
+ default:
+ break;
+ }
+
+ return ret != -1 ? ret : QWindowsStyle::pixelMetric(metric, option, widget);
+}
+
+/*!
+ \reimp
+*/
+QSize QCleanlooksStyle::sizeFromContents(ContentsType type, const QStyleOption *option,
+ const QSize &size, const QWidget *widget) const
+{
+ QSize newSize = QWindowsStyle::sizeFromContents(type, option, size, widget);
+ switch (type) {
+ case CT_PushButton:
+ if (const QStyleOptionButton *btn = qstyleoption_cast<const QStyleOptionButton *>(option)) {
+ if (!btn->text.isEmpty() && newSize.width() < 80)
+ newSize.setWidth(80);
+ if (!btn->icon.isNull() && btn->iconSize.height() > 16)
+ newSize -= QSize(0, 2);
+ }
+ if (const QPushButton *button = qobject_cast<const QPushButton *>(widget)) {
+ if (qobject_cast<const QDialogButtonBox *>(button->parentWidget())) {
+ if (newSize.height() < 32)
+ newSize.setHeight(32);
+ }
+ }
+ break;
+ case CT_GroupBox:
+ case CT_RadioButton:
+ case CT_CheckBox:
+ newSize += QSize(0, 1);
+ break;
+ case CT_ToolButton:
+#ifndef QT_NO_TOOLBAR
+ if (widget && qobject_cast<QToolBar *>(widget->parentWidget()))
+ newSize += QSize(4, 6);
+#endif // QT_NO_TOOLBAR
+ break;
+ case CT_SpinBox:
+ newSize += QSize(0, -2);
+ break;
+ case CT_ComboBox:
+ newSize += QSize(2, 4);
+ break;
+ case CT_LineEdit:
+ newSize += QSize(0, 4);
+ break;
+ case CT_MenuBarItem:
+ newSize += QSize(0, 2);
+ break;
+ case CT_MenuItem:
+ if (const QStyleOptionMenuItem *menuItem = qstyleoption_cast<const QStyleOptionMenuItem *>(option)) {
+ if (menuItem->menuItemType == QStyleOptionMenuItem::Separator) {
+ if (!menuItem->text.isEmpty()) {
+ newSize.setHeight(menuItem->fontMetrics.lineSpacing());
+ }
+ }
+#ifndef QT_NO_COMBOBOX
+ else if (!menuItem->icon.isNull()) {
+ if (const QComboBox *combo = qobject_cast<const QComboBox*>(widget)) {
+ newSize.setHeight(qMax(combo->iconSize().height() + 2, newSize.height()));
+ }
+ }
+#endif // QT_NO_COMBOBOX
+ }
+ break;
+ case CT_SizeGrip:
+ newSize += QSize(4, 4);
+ break;
+ case CT_MdiControls:
+ if (const QStyleOptionComplex *styleOpt = qstyleoption_cast<const QStyleOptionComplex *>(option)) {
+ int width = 0;
+ if (styleOpt->subControls & SC_MdiMinButton)
+ width += 19 + 1;
+ if (styleOpt->subControls & SC_MdiNormalButton)
+ width += 19 + 1;
+ if (styleOpt->subControls & SC_MdiCloseButton)
+ width += 19 + 1;
+ newSize = QSize(width, 19);
+ } else {
+ newSize = QSize(60, 19);
+ }
+ break;
+ default:
+ break;
+ }
+ return newSize;
+}
+
+/*!
+ \reimp
+*/
+void QCleanlooksStyle::polish(QApplication *app)
+{
+ QWindowsStyle::polish(app);
+#ifdef Q_WS_X11
+ Q_D(QCleanlooksStyle);
+
+ QString dataDirs = QLatin1String(getenv("XDG_DATA_DIRS"));
+
+ if (dataDirs.isEmpty())
+ dataDirs = QLatin1String("/usr/local/share/:/usr/share/");
+
+ dataDirs.prepend(QDir::homePath() + QLatin1String("/:"));
+ d->iconDirs = dataDirs.split(QLatin1String(":"));
+#endif
+}
+
+/*!
+ \reimp
+*/
+void QCleanlooksStyle::polish(QWidget *widget)
+{
+ QWindowsStyle::polish(widget);
+ if (qobject_cast<QAbstractButton*>(widget)
+#ifndef QT_NO_COMBOBOX
+ || qobject_cast<QComboBox *>(widget)
+#endif
+#ifndef QT_NO_PROGRESSBAR
+ || qobject_cast<QProgressBar *>(widget)
+#endif
+#ifndef QT_NO_SCROLLBAR
+ || qobject_cast<QScrollBar *>(widget)
+#endif
+#ifndef QT_NO_SPLITTER
+ || qobject_cast<QSplitterHandle *>(widget)
+#endif
+ || qobject_cast<QAbstractSlider *>(widget)
+#ifndef QT_NO_SPINBOX
+ || qobject_cast<QAbstractSpinBox *>(widget)
+#endif
+ || (widget->inherits("QWorkspaceChild"))
+ || (widget->inherits("QDockSeparator"))
+ || (widget->inherits("QDockWidgetSeparator"))
+ ) {
+ widget->setAttribute(Qt::WA_Hover, true);
+ }
+}
+
+/*!
+ \reimp
+*/
+void QCleanlooksStyle::polish(QPalette &pal)
+{
+ QWindowsStyle::polish(pal);
+ //this is a workaround for some themes such as Human, where the contrast
+ //between text and background is too low.
+ QColor highlight = pal.highlight().color();
+ QColor highlightText = pal.highlightedText().color();
+ if (qAbs(qGray(highlight.rgb()) - qGray(highlightText.rgb())) < 150) {
+ if (qGray(highlightText.rgb()) < 128)
+ pal.setBrush(QPalette::Highlight, highlight.lighter(145));
+ }
+}
+
+/*!
+ \reimp
+*/
+void QCleanlooksStyle::unpolish(QWidget *widget)
+{
+ QWindowsStyle::unpolish(widget);
+ if (qobject_cast<QAbstractButton*>(widget)
+#ifndef QT_NO_COMBOBOX
+ || qobject_cast<QComboBox *>(widget)
+#endif
+#ifndef QT_NO_PROGRESSBAR
+ || qobject_cast<QProgressBar *>(widget)
+#endif
+#ifndef QT_NO_SCROLLBAR
+ || qobject_cast<QScrollBar *>(widget)
+#endif
+#ifndef QT_NO_SPLITTER
+ || qobject_cast<QSplitterHandle *>(widget)
+#endif
+ || qobject_cast<QAbstractSlider *>(widget)
+#ifndef QT_NO_SPINBOX
+ || qobject_cast<QAbstractSpinBox *>(widget)
+#endif
+ || (widget->inherits("QWorkspaceChild"))
+ || (widget->inherits("QDockSeparator"))
+ || (widget->inherits("QDockWidgetSeparator"))
+ ) {
+ widget->setAttribute(Qt::WA_Hover, false);
+ }
+}
+
+/*!
+ \reimp
+*/
+void QCleanlooksStyle::unpolish(QApplication *app)
+{
+ QWindowsStyle::unpolish(app);
+}
+
+/*!
+ \reimp
+*/
+QRect QCleanlooksStyle::subControlRect(ComplexControl control, const QStyleOptionComplex *option,
+ SubControl subControl, const QWidget *widget) const
+{
+ QRect rect = QWindowsStyle::subControlRect(control, option, subControl, widget);
+
+ switch (control) {
+#ifndef QT_NO_SLIDER
+ case CC_Slider:
+ if (const QStyleOptionSlider *slider = qstyleoption_cast<const QStyleOptionSlider *>(option)) {
+ int tickSize = pixelMetric(PM_SliderTickmarkOffset, option, widget);
+ switch (subControl) {
+ case SC_SliderHandle: {
+ if (slider->orientation == Qt::Horizontal) {
+ rect.setHeight(pixelMetric(PM_SliderThickness));
+ rect.setWidth(pixelMetric(PM_SliderLength));
+ int centerY = slider->rect.center().y() - rect.height() / 2;
+ if (slider->tickPosition & QSlider::TicksAbove)
+ centerY += tickSize;
+ if (slider->tickPosition & QSlider::TicksBelow)
+ centerY -= tickSize;
+ rect.moveTop(centerY);
+ } else {
+ rect.setWidth(pixelMetric(PM_SliderThickness));
+ rect.setHeight(pixelMetric(PM_SliderLength));
+ int centerX = slider->rect.center().x() - rect.width() / 2;
+ if (slider->tickPosition & QSlider::TicksAbove)
+ centerX += tickSize;
+ if (slider->tickPosition & QSlider::TicksBelow)
+ centerX -= tickSize;
+ rect.moveLeft(centerX);
+ }
+ }
+ break;
+ case SC_SliderGroove: {
+ QPoint grooveCenter = slider->rect.center();
+ if (slider->orientation == Qt::Horizontal) {
+ rect.setHeight(7);
+ if (slider->tickPosition & QSlider::TicksAbove)
+ grooveCenter.ry() += tickSize;
+ if (slider->tickPosition & QSlider::TicksBelow)
+ grooveCenter.ry() -= tickSize;
+ } else {
+ rect.setWidth(7);
+ if (slider->tickPosition & QSlider::TicksAbove)
+ grooveCenter.rx() += tickSize;
+ if (slider->tickPosition & QSlider::TicksBelow)
+ grooveCenter.rx() -= tickSize;
+ }
+ rect.moveCenter(grooveCenter);
+ break;
+ }
+ default:
+ break;
+ }
+ }
+ break;
+#endif // QT_NO_SLIDER
+ case CC_ScrollBar:
+ break;
+#ifndef QT_NO_SPINBOX
+ case CC_SpinBox:
+ if (const QStyleOptionSpinBox *spinbox = qstyleoption_cast<const QStyleOptionSpinBox *>(option)) {
+ QSize bs;
+ int center = spinbox->rect.height() / 2;
+ int fw = spinbox->frame ? pixelMetric(PM_SpinBoxFrameWidth, spinbox, widget) : 0;
+ int y = fw;
+ bs.setHeight(qMax(8, spinbox->rect.height()/2 - y));
+ bs.setWidth(15);
+ int x, lx, rx;
+ x = spinbox->rect.width() - y - bs.width() + 2;
+ lx = fw;
+ rx = x - fw;
+ switch (subControl) {
+ case SC_SpinBoxUp:
+ if (spinbox->buttonSymbols == QAbstractSpinBox::NoButtons)
+ return QRect();
+ rect = QRect(x, fw, bs.width(), center - fw);
+ break;
+ case SC_SpinBoxDown:
+ if (spinbox->buttonSymbols == QAbstractSpinBox::NoButtons)
+ return QRect();
+
+ rect = QRect(x, center, bs.width(), spinbox->rect.bottom() - center - fw + 1);
+ break;
+ case SC_SpinBoxEditField:
+ if (spinbox->buttonSymbols == QAbstractSpinBox::NoButtons) {
+ rect = QRect(lx, fw, spinbox->rect.width() - 2*fw, spinbox->rect.height() - 2*fw);
+ } else {
+ rect = QRect(lx, fw, rx - qMax(fw - 1, 0), spinbox->rect.height() - 2*fw);
+ }
+ break;
+ case SC_SpinBoxFrame:
+ rect = spinbox->rect;
+ default:
+ break;
+ }
+ rect = visualRect(spinbox->direction, spinbox->rect, rect);
+ }
+ break;
+#endif // Qt_NO_SPINBOX
+#ifndef QT_NO_GROUPBOX
+ case CC_GroupBox:
+ if (const QStyleOptionGroupBox *groupBox = qstyleoption_cast<const QStyleOptionGroupBox *>(option)) {
+ int topMargin = 0;
+ int topHeight = 0;
+ int verticalAlignment = styleHint(SH_GroupBox_TextLabelVerticalAlignment, groupBox, widget);
+ bool flat = groupBox->features & QStyleOptionFrameV2::Flat;
+ if (!groupBox->text.isEmpty()) {
+ topHeight = groupBox->fontMetrics.height();
+ if (verticalAlignment & Qt::AlignVCenter)
+ topMargin = topHeight / 2;
+ else if (verticalAlignment & Qt::AlignTop)
+ topMargin = topHeight;
+ }
+ QRect frameRect = groupBox->rect;
+ frameRect.setTop(topMargin);
+ if (subControl == SC_GroupBoxFrame) {
+ return rect;
+ }
+ else if (subControl == SC_GroupBoxContents) {
+ if( flat ) {
+ int margin = 0;
+ int leftMarginExtension = 16;
+ rect = frameRect.adjusted(leftMarginExtension + margin, margin + topHeight, -margin, -margin);
+ }
+ break;
+ }
+ if(flat) {
+ if (const QGroupBox *groupBoxWidget = qobject_cast<const QGroupBox *>(widget)) {
+ //Prepare metrics for a bold font
+ QFont font = widget->font();
+ font.setBold(true);
+ QFontMetrics fontMetrics(font);
+
+ QSize textRect = fontMetrics.boundingRect(groupBoxWidget->title()).size() + QSize(2, 2);
+ if (subControl == SC_GroupBoxCheckBox) {
+ int indicatorWidth = pixelMetric(PM_IndicatorWidth, option, widget);
+ int indicatorHeight = pixelMetric(PM_IndicatorHeight, option, widget);
+ rect.setWidth(indicatorWidth);
+ rect.setHeight(indicatorHeight);
+ rect.moveTop((fontMetrics.height() - indicatorHeight) / 2 + 2);
+ } else if (subControl == SC_GroupBoxLabel) {
+ rect.setSize(textRect);
+ }
+ }
+ }
+ }
+ return rect;
+#ifndef QT_NO_COMBOBOX
+ case CC_ComboBox:
+ switch (subControl) {
+ case SC_ComboBoxArrow:
+ rect = visualRect(option->direction, option->rect, rect);
+ rect.setRect(rect.right() - 18, rect.top() - 2,
+ 19, rect.height() + 4);
+ rect = visualRect(option->direction, option->rect, rect);
+ break;
+ case SC_ComboBoxEditField: {
+ int frameWidth = pixelMetric(PM_DefaultFrameWidth);
+ rect = visualRect(option->direction, option->rect, rect);
+ rect.setRect(option->rect.left() + frameWidth, option->rect.top() + frameWidth,
+ option->rect.width() - 19 - 2 * frameWidth,
+ option->rect.height() - 2 * frameWidth);
+ if (const QStyleOptionComboBox *box = qstyleoption_cast<const QStyleOptionComboBox *>(option)) {
+ if (!box->editable) {
+ rect.adjust(2, 0, 0, 0);
+ if (box->state & (State_Sunken | State_On))
+ rect.translate(1, 1);
+ }
+ }
+ rect = visualRect(option->direction, option->rect, rect);
+ break;
+ }
+ default:
+ break;
+ }
+ break;
+#endif // QT_NO_COMBOBOX
+#endif //QT_NO_GROUPBOX
+ case CC_TitleBar:
+ if (const QStyleOptionTitleBar *tb = qstyleoption_cast<const QStyleOptionTitleBar *>(option)) {
+ SubControl sc = subControl;
+ QRect &ret = rect;
+ const int indent = 3;
+ const int controlTopMargin = 3;
+ const int controlBottomMargin = 3;
+ const int controlWidthMargin = 2;
+ const int controlHeight = tb->rect.height() - controlTopMargin - controlBottomMargin ;
+ const int delta = controlHeight + controlWidthMargin;
+ int offset = 0;
+
+ bool isMinimized = tb->titleBarState & Qt::WindowMinimized;
+ bool isMaximized = tb->titleBarState & Qt::WindowMaximized;
+
+ switch (sc) {
+ case SC_TitleBarLabel:
+ if (tb->titleBarFlags & (Qt::WindowTitleHint | Qt::WindowSystemMenuHint)) {
+ ret = tb->rect;
+ if (tb->titleBarFlags & Qt::WindowSystemMenuHint)
+ ret.adjust(delta, 0, -delta, 0);
+ if (tb->titleBarFlags & Qt::WindowMinimizeButtonHint)
+ ret.adjust(0, 0, -delta, 0);
+ if (tb->titleBarFlags & Qt::WindowMaximizeButtonHint)
+ ret.adjust(0, 0, -delta, 0);
+ if (tb->titleBarFlags & Qt::WindowShadeButtonHint)
+ ret.adjust(0, 0, -delta, 0);
+ if (tb->titleBarFlags & Qt::WindowContextHelpButtonHint)
+ ret.adjust(0, 0, -delta, 0);
+ }
+ break;
+ case SC_TitleBarContextHelpButton:
+ if (tb->titleBarFlags & Qt::WindowContextHelpButtonHint)
+ offset += delta;
+ case SC_TitleBarMinButton:
+ if (!isMinimized && (tb->titleBarFlags & Qt::WindowMinimizeButtonHint))
+ offset += delta;
+ else if (sc == SC_TitleBarMinButton)
+ break;
+ case SC_TitleBarNormalButton:
+ if (isMinimized && (tb->titleBarFlags & Qt::WindowMinimizeButtonHint))
+ offset += delta;
+ else if (isMaximized && (tb->titleBarFlags & Qt::WindowMaximizeButtonHint))
+ offset += delta;
+ else if (sc == SC_TitleBarNormalButton)
+ break;
+ case SC_TitleBarMaxButton:
+ if (!isMaximized && (tb->titleBarFlags & Qt::WindowMaximizeButtonHint))
+ offset += delta;
+ else if (sc == SC_TitleBarMaxButton)
+ break;
+ case SC_TitleBarShadeButton:
+ if (!isMinimized && (tb->titleBarFlags & Qt::WindowShadeButtonHint))
+ offset += delta;
+ else if (sc == SC_TitleBarShadeButton)
+ break;
+ case SC_TitleBarUnshadeButton:
+ if (isMinimized && (tb->titleBarFlags & Qt::WindowShadeButtonHint))
+ offset += delta;
+ else if (sc == SC_TitleBarUnshadeButton)
+ break;
+ case SC_TitleBarCloseButton:
+ if (tb->titleBarFlags & Qt::WindowSystemMenuHint)
+ offset += delta;
+ else if (sc == SC_TitleBarCloseButton)
+ break;
+ ret.setRect(tb->rect.right() - indent - offset, tb->rect.top() + controlTopMargin,
+ controlHeight, controlHeight);
+ break;
+ case SC_TitleBarSysMenu:
+ if (tb->titleBarFlags & Qt::WindowSystemMenuHint) {
+ ret.setRect(tb->rect.left() + controlWidthMargin + indent, tb->rect.top() + controlTopMargin,
+ controlHeight, controlHeight);
+ }
+ break;
+ default:
+ break;
+ }
+ ret = visualRect(tb->direction, tb->rect, ret);
+ }
+ break;
+ default:
+ break;
+ }
+
+ return rect;
+}
+
+
+/*!
+ \reimp
+*/
+QRect QCleanlooksStyle::itemPixmapRect(const QRect &r, int flags, const QPixmap &pixmap) const
+{
+ return QWindowsStyle::itemPixmapRect(r, flags, pixmap);
+}
+
+/*!
+ \reimp
+*/
+void QCleanlooksStyle::drawItemPixmap(QPainter *painter, const QRect &rect,
+ int alignment, const QPixmap &pixmap) const
+{
+ QWindowsStyle::drawItemPixmap(painter, rect, alignment, pixmap);
+}
+
+/*!
+ \reimp
+*/
+QStyle::SubControl QCleanlooksStyle::hitTestComplexControl(ComplexControl cc, const QStyleOptionComplex *opt,
+ const QPoint &pt, const QWidget *w) const
+{
+ return QWindowsStyle::hitTestComplexControl(cc, opt, pt, w);
+}
+
+/*!
+ \reimp
+*/
+QPixmap QCleanlooksStyle::generatedIconPixmap(QIcon::Mode iconMode, const QPixmap &pixmap,
+ const QStyleOption *opt) const
+{
+ return QWindowsStyle::generatedIconPixmap(iconMode, pixmap, opt);
+}
+
+/*!
+ \reimp
+*/
+int QCleanlooksStyle::styleHint(StyleHint hint, const QStyleOption *option, const QWidget *widget,
+ QStyleHintReturn *returnData) const
+{
+ int ret = 0;
+ switch (hint) {
+ case SH_ScrollBar_MiddleClickAbsolutePosition:
+ ret = true;
+ break;
+ case SH_EtchDisabledText:
+ ret = 1;
+ break;
+ case SH_Menu_AllowActiveAndDisabled:
+ ret = false;
+ break;
+ case SH_MainWindow_SpaceBelowMenuBar:
+ ret = 0;
+ break;
+ case SH_MenuBar_MouseTracking:
+ ret = 1;
+ break;
+ case SH_TitleBar_AutoRaise:
+ ret = 1;
+ break;
+ case SH_TitleBar_NoBorder:
+ ret = 1;
+ break;
+ case SH_ItemView_ShowDecorationSelected:
+ ret = true;
+ break;
+ case SH_Table_GridLineColor:
+ if (option) {
+ ret = option->palette.background().color().darker(120).rgb();
+ break;
+ }
+ case SH_ComboBox_Popup:
+#ifdef QT3_SUPPORT
+ if (widget && widget->inherits("Q3ComboBox"))
+ return 0;
+#endif
+ if (const QStyleOptionComboBox *cmb = qstyleoption_cast<const QStyleOptionComboBox *>(option))
+ ret = !cmb->editable;
+ else
+ ret = 0;
+ break;
+ case SH_WindowFrame_Mask:
+ ret = 1;
+ if (QStyleHintReturnMask *mask = qstyleoption_cast<QStyleHintReturnMask *>(returnData)) {
+ //left rounded corner
+ mask->region = option->rect;
+ mask->region -= QRect(option->rect.left(), option->rect.top(), 5, 1);
+ mask->region -= QRect(option->rect.left(), option->rect.top() + 1, 3, 1);
+ mask->region -= QRect(option->rect.left(), option->rect.top() + 2, 2, 1);
+ mask->region -= QRect(option->rect.left(), option->rect.top() + 3, 1, 2);
+
+ //right rounded corner
+ mask->region -= QRect(option->rect.right() - 4, option->rect.top(), 5, 1);
+ mask->region -= QRect(option->rect.right() - 2, option->rect.top() + 1, 3, 1);
+ mask->region -= QRect(option->rect.right() - 1, option->rect.top() + 2, 2, 1);
+ mask->region -= QRect(option->rect.right() , option->rect.top() + 3, 1, 2);
+ }
+ break;
+ case SH_MessageBox_TextInteractionFlags:
+ ret = Qt::TextSelectableByMouse | Qt::LinksAccessibleByMouse;
+ break;
+ case SH_DialogButtonBox_ButtonsHaveIcons:
+ ret = true;
+ break;
+ case SH_MessageBox_CenterButtons:
+ ret = false;
+ break;
+#ifndef QT_NO_WIZARD
+ case SH_WizardStyle:
+ ret = QWizard::ClassicStyle;
+ break;
+#endif
+ case SH_ItemView_ArrowKeysNavigateIntoChildren:
+ ret = false;
+ break;
+ case SH_Menu_SubMenuPopupDelay:
+ ret = 225; // default from GtkMenu
+ break;
+ default:
+ ret = QWindowsStyle::styleHint(hint, option, widget, returnData);
+ break;
+ }
+ return ret;
+}
+
+/*! \reimp */
+QRect QCleanlooksStyle::subElementRect(SubElement sr, const QStyleOption *opt, const QWidget *w) const
+{
+ QRect r = QWindowsStyle::subElementRect(sr, opt, w);
+ switch (sr) {
+ case SE_PushButtonFocusRect:
+ r.adjust(0, 1, 0, -1);
+ break;
+ case SE_DockWidgetTitleBarText: {
+ const QStyleOptionDockWidgetV2 *v2
+ = qstyleoption_cast<const QStyleOptionDockWidgetV2*>(opt);
+ bool verticalTitleBar = v2 == 0 ? false : v2->verticalTitleBar;
+ if (verticalTitleBar) {
+ r.adjust(0, 0, 0, -4);
+ } else {
+ if (QApplication::layoutDirection() == Qt::LeftToRight)
+ r.adjust(4, 0, 0, 0);
+ else
+ r.adjust(0, 0, -4, 0);
+ }
+
+ break;
+ }
+ case SE_ProgressBarContents:
+ r = subElementRect(SE_ProgressBarGroove, opt, w);
+ break;
+ default:
+ break;
+ }
+ return r;
+}
+
+void QCleanlooksStylePrivate::lookupIconTheme() const
+{
+#ifdef Q_WS_X11
+
+ if (themeName.isEmpty()) {
+ //resolve glib and gconf functions
+ p_g_type_init = (Ptr_g_type_init)QLibrary::resolve(QLatin1String("gobject-2.0"), 0, "g_type_init");
+ p_gconf_client_get_default = (Ptr_gconf_client_get_default)QLibrary::resolve(QLatin1String("gconf-2"), 4, "gconf_client_get_default");
+ p_gconf_client_get_string = (Ptr_gconf_client_get_string)QLibrary::resolve(QLatin1String("gconf-2"), 4, "gconf_client_get_string");
+ p_g_object_unref = (Ptr_g_object_unref)QLibrary::resolve(QLatin1String("gobject-2.0"), 0, "g_object_unref");
+ p_g_error_free = (Ptr_g_error_free)QLibrary::resolve(QLatin1String("glib-2.0"), 0, "g_error_free");
+ p_g_free = (Ptr_g_free)QLibrary::resolve(QLatin1String("glib-2.0"), 0, "g_free");
+
+ if (p_g_type_init &&
+ p_gconf_client_get_default &&
+ p_gconf_client_get_string &&
+ p_g_object_unref &&
+ p_g_error_free &&
+ p_g_free) {
+
+ p_g_type_init();
+ GConfClient* client = p_gconf_client_get_default();
+ GError *err = 0;
+ char *str = p_gconf_client_get_string(client, "/desktop/gnome/interface/icon_theme", &err);
+ if (!err) {
+ themeName = QString::fromUtf8(str);
+ p_g_free(str);
+ }
+ p_g_object_unref(client);
+ if (err)
+ p_g_error_free (err);
+ }
+ if (themeName.isEmpty())
+ themeName = QLatin1String("gnome");
+ }
+#endif
+}
+
+/*!
+ \internal
+*/
+QIcon QCleanlooksStyle::standardIconImplementation(StandardPixmap standardIcon,
+ const QStyleOption *option,
+ const QWidget *widget) const
+{
+#ifdef Q_WS_X11
+ Q_D(const QCleanlooksStyle);
+ if (!qApp->desktopSettingsAware())
+ return QWindowsStyle::standardIconImplementation(standardIcon, option, widget);
+ QIcon icon;
+ QPixmap pixmap;
+ QPixmap link;
+ d->lookupIconTheme();
+ switch (standardIcon) {
+ case SP_DirIcon: {
+ icon = QIcon(standardPixmap(standardIcon, option, widget));
+ icon.addPixmap(standardPixmap(SP_DirClosedIcon, option, widget),
+ QIcon::Normal, QIcon::Off);
+ pixmap = d->findIcon(16, QLatin1String("gnome-fs-directory.png"));
+ if (!pixmap.isNull())
+ icon.addPixmap(pixmap, QIcon::Normal, QIcon::Off);
+ pixmap = d->findIcon(48, QLatin1String("gnome-fs-directory.png"));
+ if (!pixmap.isNull())
+ icon.addPixmap(pixmap, QIcon::Normal, QIcon::Off);
+ pixmap = d->findIcon(16, QLatin1String("gnome-fs-directory-accept.png"));
+ if (!pixmap.isNull())
+ icon.addPixmap(pixmap, QIcon::Normal, QIcon::On);
+ pixmap = d->findIcon(16, QLatin1String("gnome-fs-directory-accept.png"));
+ if (!pixmap.isNull())
+ icon.addPixmap(pixmap, QIcon::Normal, QIcon::On);
+ }
+ break;
+ case SP_DirLinkIcon:
+ {
+ icon = QIcon(standardPixmap(standardIcon, option, widget));
+ QPixmap link = d->findIcon(12, QLatin1String("emblem-symbolic-link.png"));
+ if (!link.isNull()) {
+ icon.addPixmap(standardPixmap(SP_DirLinkIcon, option, widget));
+ pixmap = d->findIcon(16, QLatin1String("gnome-fs-directory.png"));
+ if (!pixmap.isNull()) {
+ QPainter painter(&pixmap);
+ painter.drawPixmap(8, 8, 8, 8, link);
+ painter.end();
+ icon.addPixmap(pixmap);
+ }
+ }
+ break;
+ }
+ case SP_FileIcon:
+ {
+ icon = d->createIcon(QLatin1String("unknown.png"));
+ if (icon.isNull())
+ icon = d->createIcon(QLatin1String("gnome-fs-regular.png"));
+ if (icon.isNull())
+ icon = d->createIcon(QLatin1String("stock_new.png"));
+ break;
+ }
+ case SP_DialogCloseButton:
+ {
+ icon = d->createIcon(QLatin1String("gtk-close.png"));
+ if (icon.isNull())
+ icon = d->createIcon(QLatin1String("stock-close.png"));
+ break;
+ }
+ case SP_DirHomeIcon:
+ {
+ icon = d->createIcon(QLatin1String("folder_home.png"));
+ if (icon.isNull())
+ icon = d->createIcon(QLatin1String("gnome_home.png"));
+ break;
+ }
+ case SP_DriveFDIcon:
+ {
+ icon = d->createIcon(QLatin1String("gnome-dev-floppy.png"));
+ break;
+ }
+ case SP_ComputerIcon:
+ {
+ icon = d->createIcon(QLatin1String("gnome-fs-client.png"));
+ break;
+ }
+ case SP_DesktopIcon:
+ {
+ icon = d->createIcon(QLatin1String("gnome-fs-desktop.png"));
+ break;
+ }
+ case SP_TrashIcon:
+ {
+ icon = d->createIcon(QLatin1String("gnome-fs-trash-empty.png"));
+ break;
+ }
+ case SP_DriveCDIcon:
+ case SP_DriveDVDIcon:
+ {
+ icon = d->createIcon(QLatin1String("gnome-dev-cdrom.png"));
+ break;
+ }
+ case SP_DriveHDIcon:
+ {
+ icon = d->createIcon(QLatin1String("gnome-dev-harddisk.png"));
+ break;
+ }
+ case SP_ArrowUp:
+ {
+ icon = d->createIcon(QLatin1String("stock_up.png"));
+ break;
+ }
+ case SP_ArrowDown:
+ {
+ icon = d->createIcon(QLatin1String("stock_down.png"));
+ break;
+ }
+ case SP_ArrowRight:
+ {
+ icon = d->createIcon(QLatin1String("stock_right.png"));
+ break;
+ }
+ case SP_ArrowLeft:
+ {
+ icon = d->createIcon(QLatin1String("stock_left.png"));
+ break;
+ }
+ case SP_BrowserReload:
+ {
+ icon = d->createIcon(QLatin1String("view-refresh.png"));
+ break;
+ }
+ case SP_BrowserStop:
+ {
+ pixmap = d->findIcon(24, QLatin1String("stop.png"));
+ break;
+ }
+ case SP_FileLinkIcon:
+ {
+ icon = QIcon(standardPixmap(standardIcon, option, widget));
+ QPixmap link = d->findIcon(12, QLatin1String("emblem-symbolic-link.png"));
+ if (!link.isNull()) {
+ icon.addPixmap(standardPixmap(SP_FileLinkIcon,option, widget));
+ pixmap = d->findIcon(16, QLatin1String("unknown.png"));
+ if (pixmap.isNull())
+ pixmap = d->findIcon(16, QLatin1String("stock_new.png"));
+ if (!pixmap.isNull()) {
+ QPainter painter(&pixmap);
+ painter.drawPixmap(8, 8, 8, 8, link);
+ painter.end();
+ icon.addPixmap(pixmap);
+ }
+ }
+ break;
+ }
+ case SP_ArrowForward:
+ if (QApplication::layoutDirection() == Qt::RightToLeft)
+ return standardIconImplementation(SP_ArrowLeft, option, widget);
+ return standardIconImplementation(SP_ArrowRight, option, widget);
+ case SP_ArrowBack:
+ if (QApplication::layoutDirection() == Qt::RightToLeft)
+ return standardIconImplementation(SP_ArrowRight, option, widget);
+ return standardIconImplementation(SP_ArrowLeft, option, widget);
+ default:
+ icon = QIcon(standardPixmap(standardIcon, option, widget));
+ }
+ if (!icon.isNull())
+ return icon;
+#endif // Q_WS_X11
+ return QWindowsStyle::standardIconImplementation(standardIcon, option, widget);
+}
+
+/*!
+ \reimp
+ */
+QPixmap QCleanlooksStyle::standardPixmap(StandardPixmap standardPixmap, const QStyleOption *opt,
+ const QWidget *widget) const
+{
+#ifdef Q_WS_X11
+ Q_D(const QCleanlooksStyle);
+ QPixmap pixmap;
+ if (!qApp->desktopSettingsAware())
+ return QWindowsStyle::standardPixmap(standardPixmap, opt, widget);
+ d->lookupIconTheme();
+#ifndef QT_NO_IMAGEFORMAT_XPM
+ switch (standardPixmap) {
+ case SP_MessageBoxInformation:
+ {
+ pixmap = d->findIcon(48, QLatin1String("dialog-info.png"));
+ if (pixmap.isNull())
+ pixmap = d->findIcon(48, QLatin1String("stock_dialog-info.png"));
+ if (!pixmap.isNull())
+ return pixmap;
+ break;
+ }
+ case SP_MessageBoxWarning:
+ {
+ pixmap = d->findIcon(48, QLatin1String("dialog-warning.png"));
+ if (pixmap.isNull())
+ pixmap = d->findIcon(48, QLatin1String("stock_dialog-warning.png"));
+ if (!pixmap.isNull())
+ return pixmap;
+ break;
+ }
+ case SP_MessageBoxCritical:
+ {
+ pixmap = d->findIcon(48, QLatin1String("dialog-error.png"));
+ if (pixmap.isNull())
+ pixmap = d->findIcon(48, QLatin1String("stock_dialog-error.png"));
+ if (!pixmap.isNull())
+ return pixmap;
+ break;
+ }
+ case SP_MessageBoxQuestion:
+ {
+ pixmap = d->findIcon(48, QLatin1String("dialog-question.png"));
+ if (!pixmap.isNull())
+ return pixmap;
+ break;
+ }
+ case SP_DirHomeIcon:
+ {
+ pixmap = d->findIcon(16, QLatin1String("folder_home.png"));
+ if (pixmap.isNull())
+ pixmap = d->findIcon(16, QLatin1String("gnome_home.png"));
+ if (!pixmap.isNull())
+ return pixmap;
+ break;
+ }
+ case SP_DialogOpenButton:
+ case SP_DirOpenIcon:
+ {
+ pixmap = d->findIcon(24, QLatin1String("stock_open.png"));
+ if (!pixmap.isNull())
+ return pixmap;
+ break;
+ }
+ case SP_FileIcon:
+ {
+ pixmap = d->findIcon(24, QLatin1String("unknown.png"));
+ if (pixmap.isNull())
+ pixmap = d->findIcon(24, QLatin1String("gnome-fs-regular.png"));
+ if (pixmap.isNull())
+ pixmap = d->findIcon(24, QLatin1String("stock_new.png"));
+ if (!pixmap.isNull())
+ return pixmap;
+ break;
+ }
+ case SP_FileLinkIcon:
+ {
+ pixmap = d->findIcon(24, QLatin1String("emblem-symbolic-link.png"));
+ if (!pixmap.isNull()) {
+ QPixmap fileIcon = d->findIcon(24, QLatin1String("unknown.png"));
+ if (fileIcon.isNull())
+ fileIcon = d->findIcon(24, QLatin1String("stock_new.png"));
+ if (!fileIcon.isNull()) {
+ QPainter painter(&fileIcon);
+ painter.setRenderHint(QPainter::SmoothPixmapTransform);
+ painter.drawPixmap(12, 12, 12, 12, pixmap);
+ return fileIcon;
+ }
+ }
+ break;
+ }
+ case SP_DirClosedIcon:
+ case SP_DirIcon:
+ {
+ pixmap = d->findIcon(24, QLatin1String("gnome-fs-directory.png"));
+ if (!pixmap.isNull())
+ return pixmap;
+ break;
+ }
+ case SP_DirLinkIcon:
+ {
+ pixmap = d->findIcon(24, QLatin1String("emblem-symbolic-link.png"));
+ if (!pixmap.isNull()) {
+ QPixmap dirIcon = d->findIcon(24, QLatin1String("gnome-fs-directory.png"));
+ if (!dirIcon.isNull()) {
+ QPainter painter(&dirIcon);
+ painter.setRenderHint(QPainter::SmoothPixmapTransform);
+ painter.drawPixmap(12, 12, 12, 12, pixmap);
+ return dirIcon;
+ }
+ }
+ break;
+ }
+ case SP_DriveFDIcon:
+ {
+ pixmap = d->findIcon(24, QLatin1String("gnome-dev-floppy.png"));
+ if (!pixmap.isNull())
+ return pixmap;
+ break;
+ }
+ case SP_ComputerIcon:
+ {
+ pixmap = d->findIcon(24, QLatin1String("gnome-fs-client.png"));
+ if (!pixmap.isNull())
+ return pixmap;
+ break;
+ }
+ case SP_DesktopIcon:
+ {
+ pixmap = d->findIcon(24, QLatin1String("gnome-fs-desktop.png"));
+ if (!pixmap.isNull())
+ return pixmap;
+ break;
+ }
+ case SP_TrashIcon:
+ {
+ pixmap = d->findIcon(24, QLatin1String("gnome-fs-trash-empty.png"));
+ if (!pixmap.isNull())
+ return pixmap;
+ break;
+ }
+ case SP_DriveCDIcon:
+ case SP_DriveDVDIcon:
+ {
+ pixmap = d->findIcon(24, QLatin1String("gnome-dev-cdrom.png"));
+ if (!pixmap.isNull())
+ return pixmap;
+ break;
+ }
+ case SP_DriveHDIcon:
+ {
+ pixmap = d->findIcon(24, QLatin1String("gnome-dev-harddisk.png"));
+ if (!pixmap.isNull())
+ return pixmap;
+ break;
+ }
+ case SP_FileDialogToParent:
+ {
+ pixmap = d->findIcon(16, QLatin1String("stock_up.png"));
+ if (!pixmap.isNull())
+ return pixmap;
+ break;
+ }
+ case SP_FileDialogNewFolder:
+ {
+ pixmap = d->findIcon(16, QLatin1String("stock_new-dir.png"));
+ if (!pixmap.isNull())
+ return pixmap;
+ break;
+ }
+ case SP_ArrowUp:
+ {
+ pixmap = d->findIcon(16, QLatin1String("stock_up.png"));
+ if (!pixmap.isNull())
+ return pixmap;
+ break;
+ }
+ case SP_ArrowDown:
+ {
+ pixmap = d->findIcon(16, QLatin1String("stock_down.png"));
+ if (!pixmap.isNull())
+ return pixmap;
+ break;
+ }
+ case SP_ArrowRight:
+ {
+ pixmap = d->findIcon(16, QLatin1String("stock_right.png"));
+ if (!pixmap.isNull())
+ return pixmap;
+ break;
+ }
+ case SP_ArrowLeft:
+ {
+ pixmap = d->findIcon(16, QLatin1String("stock_left.png"));
+ if (!pixmap.isNull())
+ return pixmap;
+ break;
+ }
+ case SP_DialogCloseButton:
+ {
+ pixmap = d->findIcon(24, QLatin1String("gtk-close.png"));
+ if (pixmap.isNull())
+ pixmap = d->findIcon(24, QLatin1String("stock-close.png"));
+ if (!pixmap.isNull())
+ return pixmap;
+ break;
+ }
+ case SP_DialogApplyButton:
+ {
+ pixmap = d->findIcon(24, QLatin1String("dialog-apply.png"));
+ if (pixmap.isNull())
+ pixmap = d->findIcon(24, QLatin1String("stock-apply.png"));
+ if (!pixmap.isNull())
+ return pixmap;
+ break;
+ }
+ case SP_DialogResetButton:
+ {
+ pixmap = d->findIcon(24, QLatin1String("gtk-clear.png"));
+ if (!pixmap.isNull())
+ return pixmap;
+ break;
+ }
+ case SP_DialogHelpButton:
+ {
+ pixmap = d->findIcon(24, QLatin1String("gtk-help.png"));
+ if (!pixmap.isNull())
+ return pixmap;
+ break;
+ }
+ case SP_DialogOkButton:
+ {
+ pixmap = d->findIcon(24, QLatin1String("dialog-ok.png"));
+ if (pixmap.isNull())
+ pixmap = d->findIcon(24, QLatin1String("stock-ok.png"));
+ if (!pixmap.isNull())
+ return pixmap;
+ break;
+ }
+ case SP_DialogCancelButton:
+ {
+ pixmap = d->findIcon(24, QLatin1String("dialog-cancel.png"));
+ if (pixmap.isNull())
+ pixmap = d->findIcon(24, QLatin1String("stock-cancel.png"));
+ if (pixmap.isNull())
+ pixmap = d->findIcon(24, QLatin1String("process-stop.png"));
+ if (!pixmap.isNull())
+ return pixmap;
+ break;
+ }
+ case SP_DialogSaveButton:
+ {
+ pixmap = d->findIcon(24, QLatin1String("stock_save.png"));
+ if (!pixmap.isNull())
+ return pixmap;
+ break;
+ }
+ case SP_BrowserStop:
+ {
+ pixmap = d->findIcon(16, QLatin1String("process-stop.png"));
+ if (!pixmap.isNull())
+ return pixmap;
+ break;
+ }
+ case SP_BrowserReload:
+ {
+ pixmap = d->findIcon(16, QLatin1String("view-refresh.png"));
+ if (!pixmap.isNull())
+ return pixmap;
+ break;
+ }
+ case SP_MediaPlay:
+ {
+ pixmap = d->findIcon(24, QLatin1String("media-playback-start.png"));
+ if (!pixmap.isNull())
+ return pixmap;
+ break;
+ }
+ case SP_MediaPause:
+ {
+ pixmap = d->findIcon(24, QLatin1String("media-playback-pause.png"));
+ if (!pixmap.isNull())
+ return pixmap;
+ break;
+ }
+ case SP_MediaStop:
+ {
+ pixmap = d->findIcon(24, QLatin1String("media-playback-stop.png"));
+ if (!pixmap.isNull())
+ return pixmap;
+ break;
+ }
+ case SP_MediaVolume:
+ {
+ pixmap = d->findIcon(16, QLatin1String("audio-volume-medium.png"));
+ if (!pixmap.isNull())
+ return pixmap;
+ break;
+ }
+ case SP_MediaVolumeMuted:
+ {
+ pixmap = d->findIcon(16, QLatin1String("audio-volume-muted.png"));
+ if (!pixmap.isNull())
+ return pixmap;
+ break;
+ }
+ case SP_MediaSeekForward:
+ {
+ pixmap = d->findIcon(24, QLatin1String("media-seek-forward.png"));
+ if (!pixmap.isNull())
+ return pixmap;
+ break;
+ }
+ case SP_MediaSeekBackward:
+ {
+ pixmap = d->findIcon(24, QLatin1String("media-seek-backward.png"));
+ if (!pixmap.isNull())
+ return pixmap;
+ break;
+ }
+ case SP_MediaSkipForward:
+ {
+ pixmap = d->findIcon(24, QLatin1String("media-skip-forward.png"));
+ if (!pixmap.isNull())
+ return pixmap;
+ break;
+ }
+ case SP_MediaSkipBackward:
+ {
+ pixmap = d->findIcon(24, QLatin1String("media-skip-backward.png"));
+ if (!pixmap.isNull())
+ return pixmap;
+ break;
+ }
+ case SP_TitleBarMenuButton:
+ case SP_TitleBarShadeButton:
+ case SP_TitleBarUnshadeButton:
+ case SP_TitleBarMaxButton:
+ case SP_TitleBarContextHelpButton:
+ return QWindowsStyle::standardPixmap(standardPixmap, opt, widget);
+ case SP_TitleBarNormalButton:
+ return QPixmap((const char **)dock_widget_restore_xpm);
+ case SP_TitleBarMinButton:
+ return QPixmap((const char **)workspace_minimize);
+ case SP_TitleBarCloseButton:
+ case SP_DockWidgetCloseButton:
+ return QPixmap((const char **)dock_widget_close_xpm);
+
+ default:
+ break;
+ }
+#endif //QT_NO_IMAGEFORMAT_XPM
+#endif //Q_WS_X11
+ return QWindowsStyle::standardPixmap(standardPixmap, opt, widget);
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_STYLE_CLEANLOOKS || QT_PLUGIN
diff --git a/src/gui/styles/qcleanlooksstyle.h b/src/gui/styles/qcleanlooksstyle.h
new file mode 100644
index 0000000000..1da457e3f0
--- /dev/null
+++ b/src/gui/styles/qcleanlooksstyle.h
@@ -0,0 +1,114 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QCLEANLOOKSSTYLE_H
+#define QCLEANLOOKSSTYLE_H
+
+#include <QtGui/qwindowsstyle.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+#if !defined(QT_NO_STYLE_CLEANLOOKS)
+
+class QCleanlooksStylePrivate;
+class Q_GUI_EXPORT QCleanlooksStyle : public QWindowsStyle
+{
+ Q_OBJECT
+ Q_DECLARE_PRIVATE(QCleanlooksStyle)
+
+public:
+ QCleanlooksStyle();
+ ~QCleanlooksStyle();
+
+ QPalette standardPalette () const;
+ void drawPrimitive(PrimitiveElement elem,
+ const QStyleOption *option,
+ QPainter *painter, const QWidget *widget = 0) const;
+ void drawControl(ControlElement ce, const QStyleOption *option, QPainter *painter,
+ const QWidget *widget) const;
+ int pixelMetric(PixelMetric metric, const QStyleOption *option = 0, const QWidget *widget = 0) const;
+ void drawComplexControl(ComplexControl control, const QStyleOptionComplex *option,
+ QPainter *painter, const QWidget *widget) const;
+ QRect subElementRect(SubElement r, const QStyleOption *opt, const QWidget *widget = 0) const;
+ QSize sizeFromContents(ContentsType type, const QStyleOption *option,
+ const QSize &size, const QWidget *widget) const;
+ SubControl hitTestComplexControl(ComplexControl cc, const QStyleOptionComplex *opt,
+ const QPoint &pt, const QWidget *w = 0) const;
+ QRect subControlRect(ComplexControl cc, const QStyleOptionComplex *opt,
+ SubControl sc, const QWidget *widget) const;
+ QPixmap generatedIconPixmap(QIcon::Mode iconMode, const QPixmap &pixmap,
+ const QStyleOption *opt) const;
+ int styleHint(StyleHint hint, const QStyleOption *option = 0, const QWidget *widget = 0,
+ QStyleHintReturn *returnData = 0) const;
+ QRect itemPixmapRect(const QRect &r, int flags, const QPixmap &pixmap) const;
+ QPixmap standardPixmap(StandardPixmap standardPixmap, const QStyleOption *opt,
+ const QWidget *widget = 0) const;
+ void drawItemPixmap(QPainter *painter, const QRect &rect,
+ int alignment, const QPixmap &pixmap) const;
+ void drawItemText(QPainter *painter, const QRect &rect,
+ int flags, const QPalette &pal, bool enabled,
+ const QString &text, QPalette::ColorRole textRole = QPalette::NoRole) const;
+ void polish(QWidget *widget);
+ void polish(QApplication *app);
+ void polish(QPalette &pal);
+ void unpolish(QWidget *widget);
+ void unpolish(QApplication *app);
+
+protected Q_SLOTS:
+ QIcon standardIconImplementation(StandardPixmap standardIcon, const QStyleOption *option,
+ const QWidget *widget = 0) const;
+
+protected:
+ QCleanlooksStyle(QCleanlooksStylePrivate &dd);
+
+};
+
+#endif // QT_NO_STYLE_CLEANLOOKS
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QCLEANLOOKSSTYLE_H
diff --git a/src/gui/styles/qcleanlooksstyle_p.h b/src/gui/styles/qcleanlooksstyle_p.h
new file mode 100644
index 0000000000..dfc4441d3e
--- /dev/null
+++ b/src/gui/styles/qcleanlooksstyle_p.h
@@ -0,0 +1,82 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QCLEANLOOKSSTYLE_P_H
+#define QCLEANLOOKSSTYLE_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of qapplication_*.cpp, qwidget*.cpp and qfiledialog.cpp. This header
+// file may change from version to version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qwindowsstyle.h"
+#include "qwindowsstyle_p.h"
+
+#ifndef QT_NO_STYLE_CLEANLOOKS
+
+QT_BEGIN_NAMESPACE
+
+class QCleanlooksStylePrivate : public QWindowsStylePrivate
+{
+ Q_DECLARE_PUBLIC(QCleanlooksStyle)
+public:
+ QCleanlooksStylePrivate()
+ : QWindowsStylePrivate() {
+ animationFps = 24;
+ }
+
+ ~QCleanlooksStylePrivate() {
+ }
+
+ void lookupIconTheme() const;
+};
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_STYLE_CLEANLOOKS
+
+#endif //QCLEANLOOKSSTYLE_P_H
diff --git a/src/gui/styles/qcommonstyle.cpp b/src/gui/styles/qcommonstyle.cpp
new file mode 100644
index 0000000000..3cae08ab8c
--- /dev/null
+++ b/src/gui/styles/qcommonstyle.cpp
@@ -0,0 +1,6357 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qcommonstyle.h"
+#include "qcommonstyle_p.h"
+
+#include <qfile.h>
+#include <qapplication.h>
+#include <qbitmap.h>
+#include <qcache.h>
+#include <qdockwidget.h>
+#include <qdrawutil.h>
+#include <qdialogbuttonbox.h>
+#include <qformlayout.h>
+#include <qgroupbox.h>
+#include <qmath.h>
+#include <qmenu.h>
+#include <qpainter.h>
+#include <qpaintengine.h>
+#include <qpainterpath.h>
+#include <qslider.h>
+#include <qstyleoption.h>
+#include <qtabbar.h>
+#include <qtabwidget.h>
+#include <qtoolbar.h>
+#include <qtoolbutton.h>
+#include <qrubberband.h>
+#include <private/qapplication_p.h>
+#include <private/qcommonstylepixmaps_p.h>
+#include <private/qmath_p.h>
+#include <qdebug.h>
+#include <qtextformat.h>
+#include <qwizard.h>
+#include <qtabbar.h>
+#include <qfileinfo.h>
+#include <qdir.h>
+#include <qsettings.h>
+#include <qpixmapcache.h>
+
+#include <limits.h>
+
+#ifndef QT_NO_ITEMVIEWS
+# include "private/qtextengine_p.h"
+#endif
+
+#ifdef Q_WS_X11
+# include <private/qt_x11_p.h>
+#endif
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QCommonStyle
+ \brief The QCommonStyle class encapsulates the common Look and Feel of a GUI.
+
+ \ingroup appearance
+
+ This abstract class implements some of the widget's look and feel
+ that is common to all GUI styles provided and shipped as part of
+ Qt.
+
+ Since QCommonStyle inherits QStyle, all of its functions are fully documented
+ in the QStyle documentation.
+ \omit
+ , although the
+ extra functions that QCommonStyle provides, e.g.
+ drawComplexControl(), drawControl(), drawPrimitive(),
+ hitTestComplexControl(), subControlRect(), sizeFromContents(), and
+ subElementRect() are documented here.
+ \endomit
+
+ \sa QStyle, QMotifStyle, QWindowsStyle
+*/
+
+/*!
+ Constructs a QCommonStyle.
+*/
+QCommonStyle::QCommonStyle()
+ : QStyle(*new QCommonStylePrivate)
+{ }
+
+/*! \internal
+*/
+QCommonStyle::QCommonStyle(QCommonStylePrivate &dd)
+ : QStyle(dd)
+{ }
+
+/*!
+ \overload
+
+ Destroys the style
+*/
+QCommonStyle::~QCommonStyle()
+{ }
+
+
+/*!
+ \reimp
+*/
+void QCommonStyle::drawPrimitive(PrimitiveElement pe, const QStyleOption *opt, QPainter *p,
+ const QWidget *widget) const
+{
+ Q_D(const QCommonStyle);
+ switch (pe) {
+ case PE_FrameButtonBevel:
+ case PE_FrameButtonTool:
+ qDrawShadeRect(p, opt->rect, opt->palette,
+ opt->state & (State_Sunken | State_On), 1, 0);
+ break;
+ case PE_PanelButtonCommand:
+ case PE_PanelButtonBevel:
+ case PE_PanelButtonTool:
+ case PE_IndicatorButtonDropDown:
+ qDrawShadePanel(p, opt->rect, opt->palette,
+ opt->state & (State_Sunken | State_On), 1,
+ &opt->palette.brush(QPalette::Button));
+ break;
+ case PE_IndicatorViewItemCheck:
+ drawPrimitive(PE_IndicatorCheckBox, opt, p, widget);
+ break;
+ case PE_IndicatorCheckBox:
+ if (opt->state & State_NoChange) {
+ p->setPen(opt->palette.foreground().color());
+ p->fillRect(opt->rect, opt->palette.brush(QPalette::Button));
+ p->drawRect(opt->rect);
+ p->drawLine(opt->rect.topLeft(), opt->rect.bottomRight());
+ } else {
+ qDrawShadePanel(p, opt->rect.x(), opt->rect.y(), opt->rect.width(), opt->rect.height(),
+ opt->palette, opt->state & (State_Sunken | State_On), 1,
+ &opt->palette.brush(QPalette::Button));
+ }
+ break;
+ case PE_IndicatorRadioButton: {
+ QRect ir = opt->rect;
+ p->setPen(opt->palette.dark().color());
+ p->drawArc(opt->rect, 0, 5760);
+ if (opt->state & (State_Sunken | State_On)) {
+ ir.adjust(2, 2, -2, -2);
+ p->setBrush(opt->palette.foreground());
+ p->drawEllipse(ir);
+ }
+ break; }
+ case PE_FrameFocusRect:
+ if (const QStyleOptionFocusRect *fropt = qstyleoption_cast<const QStyleOptionFocusRect *>(opt)) {
+ QColor bg = fropt->backgroundColor;
+ QPen oldPen = p->pen();
+ if (bg.isValid()) {
+ int h, s, v;
+ bg.getHsv(&h, &s, &v);
+ if (v >= 128)
+ p->setPen(Qt::black);
+ else
+ p->setPen(Qt::white);
+ } else {
+ p->setPen(opt->palette.foreground().color());
+ }
+ QRect focusRect = opt->rect.adjusted(1, 1, -1, -1);
+ p->drawRect(focusRect.adjusted(0, 0, -1, -1)); //draw pen inclusive
+ p->setPen(oldPen);
+ }
+ break;
+ case PE_IndicatorMenuCheckMark: {
+ const int markW = opt->rect.width() > 7 ? 7 : opt->rect.width();
+ const int markH = markW;
+ int posX = opt->rect.x() + (opt->rect.width() - markW)/2 + 1;
+ int posY = opt->rect.y() + (opt->rect.height() - markH)/2;
+
+ QVector<QLineF> a;
+ a.reserve(markH);
+
+ int i, xx, yy;
+ xx = posX;
+ yy = 3 + posY;
+ for (i = 0; i < markW/2; ++i) {
+ a << QLineF(xx, yy, xx, yy + 2);
+ ++xx;
+ ++yy;
+ }
+ yy -= 2;
+ for (; i < markH; ++i) {
+ a << QLineF(xx, yy, xx, yy + 2);
+ ++xx;
+ --yy;
+ }
+ if (!(opt->state & State_Enabled) && !(opt->state & State_On)) {
+ int pnt;
+ p->setPen(opt->palette.highlightedText().color());
+ QPoint offset(1, 1);
+ for (pnt = 0; pnt < a.size(); ++pnt)
+ a[pnt].translate(offset.x(), offset.y());
+ p->drawLines(a);
+ for (pnt = 0; pnt < a.size(); ++pnt)
+ a[pnt].translate(offset.x(), offset.y());
+ }
+ p->setPen(opt->palette.text().color());
+ p->drawLines(a);
+ break; }
+ case PE_Frame:
+ case PE_FrameMenu:
+ if (const QStyleOptionFrame *frame = qstyleoption_cast<const QStyleOptionFrame *>(opt)) {
+ if (pe == PE_FrameMenu || (frame->state & State_Sunken) || (frame->state & State_Raised)) {
+ qDrawShadePanel(p, frame->rect, frame->palette, frame->state & State_Sunken,
+ frame->lineWidth);
+ } else {
+ qDrawPlainRect(p, frame->rect, frame->palette.foreground().color(), frame->lineWidth);
+ }
+ }
+ break;
+#ifndef QT_NO_TOOLBAR
+ case PE_PanelMenuBar:
+ if (widget && qobject_cast<QToolBar *>(widget->parentWidget()))
+ break;
+ if (const QStyleOptionFrame *frame = qstyleoption_cast<const QStyleOptionFrame *>(opt)){
+ qDrawShadePanel(p, frame->rect, frame->palette, false, frame->lineWidth,
+ &frame->palette.brush(QPalette::Button));
+
+ }
+ else if (const QStyleOptionToolBar *frame = qstyleoption_cast<const QStyleOptionToolBar *>(opt)){
+ qDrawShadePanel(p, frame->rect, frame->palette, false, frame->lineWidth,
+ &frame->palette.brush(QPalette::Button));
+ }
+
+ break;
+ case PE_PanelMenu:
+ break;
+ case PE_PanelToolBar:
+ break;
+#endif // QT_NO_TOOLBAR
+#ifndef QT_NO_PROGRESSBAR
+ case PE_IndicatorProgressChunk:
+ {
+ bool vertical = false;
+ if (const QStyleOptionProgressBarV2 *pb2 = qstyleoption_cast<const QStyleOptionProgressBarV2 *>(opt))
+ vertical = (pb2->orientation == Qt::Vertical);
+ if (!vertical) {
+ p->fillRect(opt->rect.x(), opt->rect.y() + 3, opt->rect.width() -2, opt->rect.height() - 6,
+ opt->palette.brush(QPalette::Highlight));
+ } else {
+ p->fillRect(opt->rect.x() + 2, opt->rect.y(), opt->rect.width() -6, opt->rect.height() - 2,
+ opt->palette.brush(QPalette::Highlight));
+ }
+ }
+ break;
+#endif // QT_NO_PROGRESSBAR
+#ifdef QT3_SUPPORT
+ case PE_Q3CheckListController:
+#ifndef QT_NO_IMAGEFORMAT_XPM
+ p->drawPixmap(opt->rect.topLeft(), QPixmap(check_list_controller_xpm));
+#endif
+ break;
+ case PE_Q3CheckListExclusiveIndicator:
+ if (const QStyleOptionQ3ListView *lv = qstyleoption_cast<const QStyleOptionQ3ListView *>(opt)) {
+ if (lv->items.isEmpty())
+ return;
+ int x = lv->rect.x(),
+ y = lv->rect.y();
+#define INTARRLEN(x) sizeof(x)/(sizeof(int)*2)
+ static const int pts1[] = { // dark lines
+ 1,9, 1,8, 0,7, 0,4, 1,3, 1,2, 2,1, 3,1, 4,0, 7,0, 8,1, 9,1 };
+ static const int pts2[] = { // black lines
+ 2,8, 1,7, 1,4, 2,3, 2,2, 3,2, 4,1, 7,1, 8,2, 9,2 };
+ static const int pts3[] = { // background lines
+ 2,9, 3,9, 4,10, 7,10, 8,9, 9,9, 9,8, 10,7, 10,4, 9,3 };
+ static const int pts4[] = { // white lines
+ 2,10, 3,10, 4,11, 7,11, 8,10, 9,10, 10,9, 10,8, 11,7,
+ 11,4, 10,3, 10,2 };
+ // static const int pts5[] = { // inner fill
+ // 4,2, 7,2, 9,4, 9,7, 7,9, 4,9, 2,7, 2,4 };
+ //QPolygon a;
+
+ if (lv->state & State_Enabled)
+ p->setPen(lv->palette.text().color());
+ else
+ p->setPen(QPen(lv->viewportPalette.color(QPalette::Disabled, QPalette::Text)));
+ QPolygon a(INTARRLEN(pts1), pts1);
+ a.translate(x, y);
+ //p->setPen(pal.dark());
+ p->drawPolyline(a);
+ a.setPoints(INTARRLEN(pts2), pts2);
+ a.translate(x, y);
+ p->drawPolyline(a);
+ a.setPoints(INTARRLEN(pts3), pts3);
+ a.translate(x, y);
+ // p->setPen(black);
+ p->drawPolyline(a);
+ a.setPoints(INTARRLEN(pts4), pts4);
+ a.translate(x, y);
+ // p->setPen(blue);
+ p->drawPolyline(a);
+ // a.setPoints(INTARRLEN(pts5), pts5);
+ // a.translate(x, y);
+ // QColor fillColor = isDown() ? g.background() : g.base();
+ // p->setPen(fillColor);
+ // p->setBrush(fillColor);
+ // p->drawPolygon(a);
+ if (opt->state & State_On) {
+ p->setPen(Qt::NoPen);
+ p->setBrush(opt->palette.text());
+ p->drawRect(x + 5, y + 4, 2, 4);
+ p->drawRect(x + 4, y + 5, 4, 2);
+ }
+#undef INTARRLEN
+ }
+ break;
+ case PE_Q3CheckListIndicator:
+ if (const QStyleOptionQ3ListView *lv = qstyleoption_cast<const QStyleOptionQ3ListView *>(opt)) {
+ if(lv->items.isEmpty())
+ break;
+ QStyleOptionQ3ListViewItem item = lv->items.at(0);
+ int x = lv->rect.x(),
+ y = lv->rect.y(),
+ w = lv->rect.width(),
+ h = lv->rect.width(),
+ marg = lv->itemMargin;
+
+ if (lv->state & State_Enabled)
+ p->setPen(QPen(lv->palette.text().color(), 2));
+ else
+ p->setPen(QPen(lv->viewportPalette.color(QPalette::Disabled, QPalette::Text), 2));
+ if (opt->state & State_Selected && !lv->rootIsDecorated
+ && !(item.features & QStyleOptionQ3ListViewItem::ParentControl)) {
+ p->fillRect(0, 0, x + marg + w + 4, item.height,
+ lv->palette.brush(QPalette::Highlight));
+ if (item.state & State_Enabled)
+ p->setPen(QPen(lv->palette.highlightedText().color(), 2));
+ }
+
+ if (lv->state & State_NoChange)
+ p->setBrush(lv->palette.brush(QPalette::Button));
+ p->drawRect(x + marg, y + 2, w - 4, h - 4);
+ /////////////////////
+ ++x;
+ ++y;
+ if (lv->state & State_On || lv->state & State_NoChange) {
+ QLineF lines[7];
+ int i,
+ xx = x + 1 + marg,
+ yy = y + 5;
+ for (i = 0; i < 3; ++i) {
+ lines[i] = QLineF(xx, yy, xx, yy + 2);
+ ++xx;
+ ++yy;
+ }
+ yy -= 2;
+ for (i = 3; i < 7; ++i) {
+ lines[i] = QLineF(xx, yy, xx, yy + 2);
+ ++xx;
+ --yy;
+ }
+ p->drawLines(lines, 7);
+ }
+ }
+ break;
+#endif // QT3_SUPPORT
+ case PE_IndicatorBranch: {
+ int mid_h = opt->rect.x() + opt->rect.width() / 2;
+ int mid_v = opt->rect.y() + opt->rect.height() / 2;
+ int bef_h = mid_h;
+ int bef_v = mid_v;
+ int aft_h = mid_h;
+ int aft_v = mid_v;
+#ifndef QT_NO_IMAGEFORMAT_XPM
+ static const int decoration_size = 9;
+ static QPixmap open(tree_branch_open_xpm);
+ static QPixmap closed(tree_branch_closed_xpm);
+ if (opt->state & State_Children) {
+ int delta = decoration_size / 2;
+ bef_h -= delta;
+ bef_v -= delta;
+ aft_h += delta;
+ aft_v += delta;
+ p->drawPixmap(bef_h, bef_v, opt->state & State_Open ? open : closed);
+ }
+#endif // QT_NO_IMAGEFORMAT_XPM
+ if (opt->state & State_Item) {
+ if (opt->direction == Qt::RightToLeft)
+ p->drawLine(opt->rect.left(), mid_v, bef_h, mid_v);
+ else
+ p->drawLine(aft_h, mid_v, opt->rect.right(), mid_v);
+ }
+ if (opt->state & State_Sibling)
+ p->drawLine(mid_h, aft_v, mid_h, opt->rect.bottom());
+ if (opt->state & (State_Open | State_Children | State_Item | State_Sibling))
+ p->drawLine(mid_h, opt->rect.y(), mid_h, bef_v);
+ break; }
+#ifdef QT3_SUPPORT
+ case PE_Q3Separator:
+ qDrawShadeLine(p, opt->rect.left(), opt->rect.top(), opt->rect.right(), opt->rect.bottom(),
+ opt->palette, opt->state & State_Sunken, 1, 0);
+ break;
+#endif // QT3_SUPPORT
+ case PE_FrameStatusBarItem:
+ qDrawShadeRect(p, opt->rect, opt->palette, true, 1, 0, 0);
+ break;
+ case PE_IndicatorHeaderArrow:
+ if (const QStyleOptionHeader *header = qstyleoption_cast<const QStyleOptionHeader *>(opt)) {
+ QPen oldPen = p->pen();
+ if (header->sortIndicator & QStyleOptionHeader::SortUp) {
+ QPolygon pa(3);
+ p->setPen(QPen(opt->palette.light(), 0));
+ p->drawLine(opt->rect.x() + opt->rect.width(), opt->rect.y(),
+ opt->rect.x() + opt->rect.width() / 2, opt->rect.y() + opt->rect.height());
+ p->setPen(QPen(opt->palette.dark(), 0));
+ pa.setPoint(0, opt->rect.x() + opt->rect.width() / 2, opt->rect.y() + opt->rect.height());
+ pa.setPoint(1, opt->rect.x(), opt->rect.y());
+ pa.setPoint(2, opt->rect.x() + opt->rect.width(), opt->rect.y());
+ p->drawPolyline(pa);
+ } else if (header->sortIndicator & QStyleOptionHeader::SortDown) {
+ QPolygon pa(3);
+ p->setPen(QPen(opt->palette.light(), 0));
+ pa.setPoint(0, opt->rect.x(), opt->rect.y() + opt->rect.height());
+ pa.setPoint(1, opt->rect.x() + opt->rect.width(), opt->rect.y() + opt->rect.height());
+ pa.setPoint(2, opt->rect.x() + opt->rect.width() / 2, opt->rect.y());
+ p->drawPolyline(pa);
+ p->setPen(QPen(opt->palette.dark(), 0));
+ p->drawLine(opt->rect.x(), opt->rect.y() + opt->rect.height(),
+ opt->rect.x() + opt->rect.width() / 2, opt->rect.y());
+ }
+ p->setPen(oldPen);
+ }
+ break;
+#ifndef QT_NO_TABBAR
+ case PE_FrameTabBarBase:
+ if (const QStyleOptionTabBarBase *tbb
+ = qstyleoption_cast<const QStyleOptionTabBarBase *>(opt)) {
+ p->save();
+ switch (tbb->shape) {
+ case QTabBar::RoundedNorth:
+ case QTabBar::TriangularNorth:
+ p->setPen(QPen(tbb->palette.light(), 0));
+ p->drawLine(tbb->rect.topLeft(), tbb->rect.topRight());
+ break;
+ case QTabBar::RoundedWest:
+ case QTabBar::TriangularWest:
+ p->setPen(QPen(tbb->palette.light(), 0));
+ p->drawLine(tbb->rect.topLeft(), tbb->rect.bottomLeft());
+ break;
+ case QTabBar::RoundedSouth:
+ case QTabBar::TriangularSouth:
+ p->setPen(QPen(tbb->palette.shadow(), 0));
+ p->drawLine(tbb->rect.left(), tbb->rect.bottom(),
+ tbb->rect.right(), tbb->rect.bottom());
+ p->setPen(QPen(tbb->palette.dark(), 0));
+ p->drawLine(tbb->rect.left(), tbb->rect.bottom() - 1,
+ tbb->rect.right() - 1, tbb->rect.bottom() - 1);
+ break;
+ case QTabBar::RoundedEast:
+ case QTabBar::TriangularEast:
+ p->setPen(QPen(tbb->palette.dark(), 0));
+ p->drawLine(tbb->rect.topRight(), tbb->rect.bottomRight());
+ break;
+ }
+ p->restore();
+ }
+ break;
+ case PE_IndicatorTabClose: {
+ if (d->tabBarcloseButtonIcon.isNull()) {
+ d->tabBarcloseButtonIcon.addPixmap(QPixmap(
+ QLatin1String(":/trolltech/styles/commonstyle/images/standardbutton-closetab-16.png")),
+ QIcon::Normal, QIcon::Off);
+ d->tabBarcloseButtonIcon.addPixmap(QPixmap(
+ QLatin1String(":/trolltech/styles/commonstyle/images/standardbutton-closetab-down-16.png")),
+ QIcon::Normal, QIcon::On);
+ d->tabBarcloseButtonIcon.addPixmap(QPixmap(
+ QLatin1String(":/trolltech/styles/commonstyle/images/standardbutton-closetab-hover-16.png")),
+ QIcon::Active, QIcon::Off);
+ }
+
+ int size = pixelMetric(QStyle::PM_SmallIconSize);
+ QIcon::Mode mode = opt->state & State_Enabled ?
+ (opt->state & State_Raised ? QIcon::Active : QIcon::Normal)
+ : QIcon::Disabled;
+ if (!(opt->state & State_Raised)
+ && !(opt->state & State_Sunken)
+ && !(opt->state & QStyle::State_Selected))
+ mode = QIcon::Disabled;
+
+ QIcon::State state = opt->state & State_Sunken ? QIcon::On : QIcon::Off;
+ QPixmap pixmap = d->tabBarcloseButtonIcon.pixmap(size, mode, state);
+ drawItemPixmap(p, opt->rect, Qt::AlignCenter, pixmap);
+ break;
+ }
+#endif // QT_NO_TABBAR
+ case PE_FrameTabWidget:
+ case PE_FrameWindow:
+ qDrawWinPanel(p, opt->rect, opt->palette, false, 0);
+ break;
+ case PE_FrameLineEdit:
+ drawPrimitive(PE_Frame, opt, p, widget);
+ break;
+#ifndef QT_NO_GROUPBOX
+ case PE_FrameGroupBox:
+ if (const QStyleOptionFrame *frame = qstyleoption_cast<const QStyleOptionFrame *>(opt)) {
+ const QStyleOptionFrameV2 *frame2 = qstyleoption_cast<const QStyleOptionFrameV2 *>(opt);
+ if (frame2 && (frame2->features & QStyleOptionFrameV2::Flat)) {
+ QRect fr = frame->rect;
+ QPoint p1(fr.x(), fr.y() + 1);
+ QPoint p2(fr.x() + fr.width(), p1.y());
+ qDrawShadeLine(p, p1, p2, frame->palette, true,
+ frame->lineWidth, frame->midLineWidth);
+ } else {
+ qDrawShadeRect(p, frame->rect.x(), frame->rect.y(), frame->rect.width(),
+ frame->rect.height(), frame->palette, true,
+ frame->lineWidth, frame->midLineWidth);
+ }
+ }
+ break;
+#endif // QT_NO_GROUPBOX
+#ifndef QT_NO_DOCKWIDGET
+ case PE_FrameDockWidget:
+ if (const QStyleOptionFrame *frame = qstyleoption_cast<const QStyleOptionFrame *>(opt)) {
+ int lw = frame->lineWidth;
+ if (lw <= 0)
+ lw = pixelMetric(PM_DockWidgetFrameWidth);
+
+ qDrawShadePanel(p, frame->rect, frame->palette, false, lw);
+ }
+ break;
+#endif // QT_NO_DOCKWIDGET
+#ifndef QT_NO_TOOLBAR
+ case PE_IndicatorToolBarHandle:
+ p->save();
+ p->translate(opt->rect.x(), opt->rect.y());
+ if (opt->state & State_Horizontal) {
+ int x = opt->rect.width() / 3;
+ if (QApplication::layoutDirection() == Qt::RightToLeft)
+ x -= 2;
+ if (opt->rect.height() > 4) {
+ qDrawShadePanel(p, x, 2, 3, opt->rect.height() - 4,
+ opt->palette, false, 1, 0);
+ qDrawShadePanel(p, x+3, 2, 3, opt->rect.height() - 4,
+ opt->palette, false, 1, 0);
+ }
+ } else {
+ if (opt->rect.width() > 4) {
+ int y = opt->rect.height() / 3;
+ qDrawShadePanel(p, 2, y, opt->rect.width() - 4, 3,
+ opt->palette, false, 1, 0);
+ qDrawShadePanel(p, 2, y+3, opt->rect.width() - 4, 3,
+ opt->palette, false, 1, 0);
+ }
+ }
+ p->restore();
+ break;
+ case PE_Q3DockWindowSeparator:
+ drawPrimitive(PE_IndicatorToolBarSeparator, opt, p, widget);
+ break;
+ case PE_IndicatorToolBarSeparator:
+ {
+ QPoint p1, p2;
+ if (opt->state & State_Horizontal) {
+ p1 = QPoint(opt->rect.width()/2, 0);
+ p2 = QPoint(p1.x(), opt->rect.height());
+ } else {
+ p1 = QPoint(0, opt->rect.height()/2);
+ p2 = QPoint(opt->rect.width(), p1.y());
+ }
+ qDrawShadeLine(p, p1, p2, opt->palette, 1, 1, 0);
+ break;
+ }
+#endif // QT_NO_TOOLBAR
+#ifndef QT_NO_SPINBOX
+ case PE_IndicatorSpinPlus:
+ case PE_IndicatorSpinMinus: {
+ QRect r = opt->rect;
+ int fw = pixelMetric(PM_DefaultFrameWidth, opt, widget);
+ QRect br = r.adjusted(fw, fw, -fw, -fw);
+
+ int offset = (opt->state & State_Sunken) ? 1 : 0;
+ int step = (br.width() + 4) / 5;
+ p->fillRect(br.x() + offset, br.y() + offset +br.height() / 2 - step / 2,
+ br.width(), step,
+ opt->palette.buttonText());
+ if (pe == PE_IndicatorSpinPlus)
+ p->fillRect(br.x() + br.width() / 2 - step / 2 + offset, br.y() + offset,
+ step, br.height(),
+ opt->palette.buttonText());
+
+ break; }
+ case PE_IndicatorSpinUp:
+ case PE_IndicatorSpinDown: {
+ QRect r = opt->rect;
+ int fw = pixelMetric(PM_DefaultFrameWidth, opt, widget);
+ // QRect br = r.adjusted(fw, fw, -fw, -fw);
+ int x = r.x(), y = r.y(), w = r.width(), h = r.height();
+ int sw = w-4;
+ if (sw < 3)
+ break;
+ else if (!(sw & 1))
+ sw--;
+ sw -= (sw / 7) * 2; // Empty border
+ int sh = sw/2 + 2; // Must have empty row at foot of arrow
+
+ int sx = x + w / 2 - sw / 2;
+ int sy = y + h / 2 - sh / 2;
+
+ if (pe == PE_IndicatorSpinUp && fw)
+ --sy;
+
+ QPolygon a;
+ if (pe == PE_IndicatorSpinDown)
+ a.setPoints(3, 0, 1, sw-1, 1, sh-2, sh-1);
+ else
+ a.setPoints(3, 0, sh-1, sw-1, sh-1, sh-2, 1);
+ int bsx = 0;
+ int bsy = 0;
+ if (opt->state & State_Sunken) {
+ bsx = pixelMetric(PM_ButtonShiftHorizontal);
+ bsy = pixelMetric(PM_ButtonShiftVertical);
+ }
+ p->save();
+ p->translate(sx + bsx, sy + bsy);
+ p->setPen(opt->palette.buttonText().color());
+ p->setBrush(opt->palette.buttonText());
+ p->drawPolygon(a);
+ p->restore();
+ break; }
+#endif // QT_NO_SPINBOX
+ case PE_PanelTipLabel: {
+ QBrush oldBrush = p->brush();
+ QPen oldPen = p->pen();
+ p->setPen(QPen(opt->palette.toolTipText(), 0));
+ p->setBrush(opt->palette.toolTipBase());
+ p->drawRect(opt->rect.adjusted(0, 0, -1, -1));
+ p->setPen(oldPen);
+ p->setBrush(oldBrush);
+ break;
+ }
+#ifndef QT_NO_TABBAR
+ case PE_IndicatorTabTear:
+ if (const QStyleOptionTab *tab = qstyleoption_cast<const QStyleOptionTab *>(opt)) {
+ bool rtl = tab->direction == Qt::RightToLeft;
+ QRect rect = tab->rect;
+ QPainterPath path;
+
+ rect.setTop(rect.top() + ((tab->state & State_Selected) ? 1 : 3));
+ rect.setBottom(rect.bottom() - ((tab->state & State_Selected) ? 0 : 2));
+
+ path.moveTo(QPoint(rtl ? rect.right() : rect.left(), rect.top()));
+ int count = 4;
+ for(int jags = 1; jags <= count; ++jags, rtl = !rtl)
+ path.lineTo(QPoint(rtl ? rect.left() : rect.right(), rect.top() + jags * rect.height()/count));
+
+ p->setPen(QPen(tab->palette.light(), qreal(.8)));
+ p->setBrush(tab->palette.background());
+ p->setRenderHint(QPainter::Antialiasing);
+ p->drawPath(path);
+ }
+ break;
+#endif // QT_NO_TABBAR
+#ifndef QT_NO_LINEEDIT
+ case PE_PanelLineEdit:
+ if (const QStyleOptionFrame *panel = qstyleoption_cast<const QStyleOptionFrame *>(opt)) {
+ p->fillRect(panel->rect.adjusted(panel->lineWidth, panel->lineWidth, -panel->lineWidth, -panel->lineWidth),
+ panel->palette.brush(QPalette::Base));
+
+ if (panel->lineWidth > 0)
+ drawPrimitive(PE_FrameLineEdit, panel, p, widget);
+ }
+ break;
+#endif // QT_NO_LINEEDIT
+#ifndef QT_NO_COLUMNVIEW
+ case PE_IndicatorColumnViewArrow: {
+ if (const QStyleOptionViewItem *viewOpt = qstyleoption_cast<const QStyleOptionViewItem *>(opt)) {
+ bool reverse = (viewOpt->direction == Qt::RightToLeft);
+ p->save();
+ QPainterPath path;
+ int x = viewOpt->rect.x() + 1;
+ int offset = (viewOpt->rect.height() / 3);
+ int height = (viewOpt->rect.height()) - offset * 2;
+ if (height % 2 == 1)
+ --height;
+ int x2 = x + height - 1;
+ if (reverse) {
+ x = viewOpt->rect.x() + viewOpt->rect.width() - 1;
+ x2 = x - height + 1;
+ }
+ path.moveTo(x, viewOpt->rect.y() + offset);
+ path.lineTo(x, viewOpt->rect.y() + offset + height);
+ path.lineTo(x2, viewOpt->rect.y() + offset+height/2);
+ path.closeSubpath();
+ if (viewOpt->state & QStyle::State_Selected ) {
+ if (viewOpt->showDecorationSelected) {
+ QColor color = viewOpt->palette.color(QPalette::Active, QPalette::HighlightedText);
+ p->setPen(color);
+ p->setBrush(color);
+ } else {
+ QColor color = viewOpt->palette.color(QPalette::Active, QPalette::WindowText);
+ p->setPen(color);
+ p->setBrush(color);
+ }
+
+ } else {
+ QColor color = viewOpt->palette.color(QPalette::Active, QPalette::Mid);
+ p->setPen(color);
+ p->setBrush(color);
+ }
+ p->drawPath(path);
+
+ // draw the vertical and top triangle line
+ if (!(viewOpt->state & QStyle::State_Selected)) {
+ QPainterPath lines;
+ lines.moveTo(x, viewOpt->rect.y() + offset);
+ lines.lineTo(x, viewOpt->rect.y() + offset + height);
+ lines.moveTo(x, viewOpt->rect.y() + offset);
+ lines.lineTo(x2, viewOpt->rect.y() + offset+height/2);
+ QColor color = viewOpt->palette.color(QPalette::Active, QPalette::Dark);
+ p->setPen(color);
+ p->drawPath(lines);
+ }
+ p->restore();
+ }
+ break; }
+#endif //QT_NO_COLUMNVIEW
+ case PE_IndicatorItemViewItemDrop: {
+ QRect rect = opt->rect;
+ if (opt->rect.height() == 0)
+ p->drawLine(rect.topLeft(), rect.topRight());
+ else
+ p->drawRect(rect);
+ break; }
+#ifndef QT_NO_ITEMVIEWS
+ case PE_PanelItemViewRow:
+ if (const QStyleOptionViewItemV4 *vopt = qstyleoption_cast<const QStyleOptionViewItemV4 *>(opt)) {
+ QPalette::ColorGroup cg = vopt->state & QStyle::State_Enabled
+ ? QPalette::Normal : QPalette::Disabled;
+ if (cg == QPalette::Normal && !(vopt->state & QStyle::State_Active))
+ cg = QPalette::Inactive;
+
+ if ((vopt->state & QStyle::State_Selected) && styleHint(QStyle::SH_ItemView_ShowDecorationSelected, opt, widget))
+ p->fillRect(vopt->rect, vopt->palette.brush(cg, QPalette::Highlight));
+ else if (vopt->features & QStyleOptionViewItemV2::Alternate)
+ p->fillRect(vopt->rect, vopt->palette.brush(cg, QPalette::AlternateBase));
+ }
+ break;
+ case PE_PanelItemViewItem:
+ if (const QStyleOptionViewItemV4 *vopt = qstyleoption_cast<const QStyleOptionViewItemV4 *>(opt)) {
+ QPalette::ColorGroup cg = vopt->state & QStyle::State_Enabled
+ ? QPalette::Normal : QPalette::Disabled;
+ if (cg == QPalette::Normal && !(vopt->state & QStyle::State_Active))
+ cg = QPalette::Inactive;
+
+ if (vopt->showDecorationSelected && (vopt->state & QStyle::State_Selected)) {
+ p->fillRect(vopt->rect, vopt->palette.brush(cg, QPalette::Highlight));
+ } else {
+ if (vopt->backgroundBrush.style() != Qt::NoBrush) {
+ QPointF oldBO = p->brushOrigin();
+ p->setBrushOrigin(vopt->rect.topLeft());
+ p->fillRect(vopt->rect, vopt->backgroundBrush);
+ p->setBrushOrigin(oldBO);
+ }
+
+ if (vopt->state & QStyle::State_Selected) {
+ QRect textRect = subElementRect(QStyle::SE_ItemViewItemText, opt, widget);
+ p->fillRect(textRect, vopt->palette.brush(cg, QPalette::Highlight));
+ }
+ }
+ }
+ break;
+#endif //QT_NO_ITEMVIEWS
+ case PE_PanelScrollAreaCorner: {
+ const QBrush brush(opt->palette.brush(QPalette::Window));
+ p->fillRect(opt->rect, brush);
+ } break;
+ default:
+ break;
+ }
+}
+
+#ifndef QT_NO_TOOLBUTTON
+static void drawArrow(const QStyle *style, const QStyleOptionToolButton *toolbutton,
+ const QRect &rect, QPainter *painter, const QWidget *widget = 0)
+{
+ QStyle::PrimitiveElement pe;
+ switch (toolbutton->arrowType) {
+ case Qt::LeftArrow:
+ pe = QStyle::PE_IndicatorArrowLeft;
+ break;
+ case Qt::RightArrow:
+ pe = QStyle::PE_IndicatorArrowRight;
+ break;
+ case Qt::UpArrow:
+ pe = QStyle::PE_IndicatorArrowUp;
+ break;
+ case Qt::DownArrow:
+ pe = QStyle::PE_IndicatorArrowDown;
+ break;
+ default:
+ return;
+ }
+ QStyleOption arrowOpt;
+ arrowOpt.rect = rect;
+ arrowOpt.palette = toolbutton->palette;
+ arrowOpt.state = toolbutton->state;
+ style->drawPrimitive(pe, &arrowOpt, painter, widget);
+}
+#endif // QT_NO_TOOLBUTTON
+
+
+
+#ifdef Q_WS_X11 // These functions are used to parse the X11 freedesktop icon spec
+
+void QCommonStylePrivate::lookupIconTheme() const
+{
+ if (!themeName.isEmpty())
+ return;
+
+ QString dataDirs = QString::fromLocal8Bit(getenv("XDG_DATA_DIRS"));
+ if (dataDirs.isEmpty())
+ dataDirs = QLatin1String("/usr/local/share/:/usr/share/");
+ dataDirs += QLatin1Char(':') + QApplicationPrivate::kdeHome() + QLatin1String("/share");
+ dataDirs.prepend(QDir::homePath() + QLatin1String("/:"));
+ QStringList kdeDirs = QString::fromLocal8Bit(getenv("KDEDIRS")).split(QLatin1Char(':'));
+ foreach (const QString &dirName, kdeDirs)
+ dataDirs.append(QLatin1String(":") + dirName + QLatin1String("/share"));
+ iconDirs = dataDirs.split(QLatin1Char(':'));
+
+ QFileInfo fileInfo(QLatin1String("/usr/share/icons/default.kde"));
+ QDir dir(fileInfo.canonicalFilePath());
+ int kdeVersion = qgetenv("KDE_SESSION_VERSION").toInt();
+ QString kdeDefault = kdeVersion >= 4 ? QString::fromLatin1("oxygen") : QString::fromLatin1("crystalsvg");
+ QString defaultTheme = fileInfo.exists() ? dir.dirName() : kdeDefault;
+ QSettings settings(QApplicationPrivate::kdeHome() +
+ QLatin1String("/share/config/kdeglobals"), QSettings::IniFormat);
+ settings.beginGroup(QLatin1String("Icons"));
+ themeName = settings.value(QLatin1String("Theme"), defaultTheme).toString();
+}
+
+QIconTheme QCommonStylePrivate::parseIndexFile(const QString &themeName) const
+{
+ Q_Q(const QCommonStyle);
+ QIconTheme theme;
+ QFile themeIndex;
+ QStringList parents;
+ QHash <int, QString> dirList;
+
+ for ( int i = 0 ; i < iconDirs.size() && !themeIndex.exists() ; ++i) {
+ QString contentDir = QLatin1String(iconDirs[i].startsWith(QDir::homePath()) ?
+ "/.icons/" : "/icons/");
+ themeIndex.setFileName(iconDirs[i] + contentDir +
+ themeName + QLatin1String("/index.theme"));
+ }
+
+ if (themeIndex.open(QIODevice::ReadOnly | QIODevice::Text)) {
+
+ QTextStream in(&themeIndex);
+
+ while (!in.atEnd()) {
+
+ QString line = in.readLine();
+
+ if (line.startsWith(QLatin1String("Inherits="))) {
+ line = line.right(line.length() - 9);
+ parents = line.split(QLatin1Char(','));
+ }
+
+ if (line.startsWith(QLatin1String("["))) {
+ line = line.trimmed();
+ line.chop(1);
+ QString dirName = line.right(line.length() - 1);
+ if (!in.atEnd()) {
+ line = in.readLine();
+ int size;
+ if (line.startsWith(QLatin1String("Size="))) {
+ size = line.right(line.length() - 5).toInt();
+ if (size)
+ dirList.insertMulti(size, dirName);
+ }
+ }
+ }
+ }
+ }
+
+ if (q->inherits("QPlastiqueStyle")) {
+ QFileInfo fileInfo(QLatin1String("/usr/share/icons/default.kde"));
+ QDir dir(fileInfo.canonicalFilePath());
+ QString defaultKDETheme = dir.exists() ? dir.dirName() : QString::fromLatin1("crystalsvg");
+ if (!parents.contains(defaultKDETheme) && themeName != defaultKDETheme)
+ parents.append(defaultKDETheme);
+ } else if (parents.isEmpty() && themeName != QLatin1String("hicolor")) {
+ parents.append(QLatin1String("hicolor"));
+ }
+ theme = QIconTheme(dirList, parents);
+ return theme;
+}
+
+QPixmap QCommonStylePrivate::findIconHelper(int size,
+ const QString &themeName,
+ const QString &iconName,
+ QStringList &visited) const
+{
+ QPixmap pixmap;
+
+ if (!themeName.isEmpty()) {
+
+ visited << themeName;
+ QIconTheme theme = themeList.value(themeName);
+
+ if (!theme.isValid()) {
+ theme = parseIndexFile(themeName);
+ themeList.insert(themeName, theme);
+ }
+
+ if (!theme.isValid())
+ return QPixmap();
+
+ QList <QString> subDirs = theme.dirList().values(size);
+
+ for ( int i = 0 ; i < iconDirs.size() ; ++i) {
+ for ( int j = 0 ; j < subDirs.size() ; ++j) {
+ QString contentDir = (iconDirs[i].startsWith(QDir::homePath())) ?
+ QLatin1String("/.icons/") : QLatin1String("/icons/");
+ QString fileName = iconDirs[i] + contentDir + themeName + QLatin1Char('/') + subDirs[j] + QLatin1Char('/') + iconName;
+ QFile file(fileName);
+ if (file.exists())
+ pixmap.load(fileName);
+ if (!pixmap.isNull())
+ break;
+ }
+ }
+
+ if (pixmap.isNull()) {
+ QStringList parents = theme.parents();
+ //search recursively through inherited themes
+ for (int i = 0 ; pixmap.isNull() && i < parents.size() ; ++i) {
+ QString parentTheme = parents[i].trimmed();
+ if (!visited.contains(parentTheme)) //guard against endless recursion
+ pixmap = findIconHelper(size, parentTheme, iconName, visited);
+ }
+ }
+ }
+ return pixmap;
+}
+
+QPixmap QCommonStylePrivate::findIcon(int size, const QString &name) const
+{
+ QPixmap pixmap;
+ QString pixmapName = QLatin1String("$qt") + name + QString::number(size);
+
+ if (QPixmapCache::find(pixmapName, pixmap))
+ return pixmap;
+
+ if (!themeName.isEmpty()) {
+ QStringList visited;
+ pixmap = findIconHelper(size, themeName, name, visited);
+ }
+ QPixmapCache::insert(pixmapName, pixmap);
+ return pixmap;
+}
+
+QIcon QCommonStylePrivate::createIcon(const QString &name) const
+{
+ QIcon icon;
+ icon.addPixmap(findIcon(16, name));
+ icon.addPixmap(findIcon(24, name));
+ icon.addPixmap(findIcon(32, name));
+ return icon;
+}
+
+#endif //Q_WS_X11
+
+#ifndef QT_NO_ITEMVIEWS
+
+
+QSize QCommonStylePrivate::viewItemSize(const QStyleOptionViewItemV4 *option, int role) const
+{
+ Q_Q(const QCommonStyle);
+
+ const QWidget *widget = option->widget;
+ switch (role) {
+ case Qt::CheckStateRole:
+ if (option->features & QStyleOptionViewItemV2::HasCheckIndicator)
+ return QSize(q->pixelMetric(QStyle::PM_IndicatorWidth, option, widget),
+ q->pixelMetric(QStyle::PM_IndicatorHeight, option, widget));
+ break;
+ case Qt::DisplayRole:
+ if (option->features & QStyleOptionViewItemV2::HasDisplay) {
+ QTextOption textOption;
+ textOption.setWrapMode(QTextOption::WordWrap);
+ QTextLayout textLayout;
+ textLayout.setTextOption(textOption);
+ textLayout.setFont(option->font);
+ textLayout.setText(option->text);
+ const bool wrapText = option->features & QStyleOptionViewItemV2::WrapText;
+ const int textMargin = q->pixelMetric(QStyle::PM_FocusFrameHMargin, option, widget) + 1;
+ QRect bounds = option->rect;
+ switch (option->decorationPosition) {
+ case QStyleOptionViewItem::Left:
+ case QStyleOptionViewItem::Right:
+ bounds.setWidth(wrapText && bounds.isValid() ? bounds.width() - 2 * textMargin : QFIXED_MAX);
+ break;
+ case QStyleOptionViewItem::Top:
+ case QStyleOptionViewItem::Bottom:
+ bounds.setWidth(wrapText ? option->decorationSize.width() : QFIXED_MAX);
+ break;
+ default:
+ break;
+ }
+
+ qreal height = 0, widthUsed = 0;
+ textLayout.beginLayout();
+ while (true) {
+ QTextLine line = textLayout.createLine();
+ if (!line.isValid())
+ break;
+ line.setLineWidth(bounds.width());
+ line.setPosition(QPointF(0, height));
+ height += line.height();
+ widthUsed = qMax(widthUsed, line.naturalTextWidth());
+ }
+ textLayout.endLayout();
+ const QSize size = QSizeF(widthUsed, height).toSize();
+ return QSize(size.width() + 2 * textMargin, size.height());
+ }
+ break;
+ case Qt::DecorationRole:
+ if (option->features & QStyleOptionViewItemV2::HasDecoration) {
+ return option->decorationSize;
+ }
+ break;
+ default:
+ break;
+ }
+
+ return QSize(0, 0);
+}
+
+static QSizeF viewItemTextLayout(QTextLayout &textLayout, int lineWidth)
+{
+ qreal height = 0;
+ qreal widthUsed = 0;
+ textLayout.beginLayout();
+ while (true) {
+ QTextLine line = textLayout.createLine();
+ if (!line.isValid())
+ break;
+ line.setLineWidth(lineWidth);
+ line.setPosition(QPointF(0, height));
+ height += line.height();
+ widthUsed = qMax(widthUsed, line.naturalTextWidth());
+ }
+ textLayout.endLayout();
+ return QSizeF(widthUsed, height);
+}
+
+void QCommonStylePrivate::viewItemDrawText(QPainter *p, const QStyleOptionViewItemV4 *option, const QRect &rect) const
+{
+ Q_Q(const QCommonStyle);
+ const QWidget *widget = option->widget;
+ const int textMargin = q->pixelMetric(QStyle::PM_FocusFrameHMargin, 0, widget) + 1;
+
+ QRect textRect = rect.adjusted(textMargin, 0, -textMargin, 0); // remove width padding
+ const bool wrapText = option->features & QStyleOptionViewItemV2::WrapText;
+ QTextOption textOption;
+ textOption.setWrapMode(wrapText ? QTextOption::WordWrap : QTextOption::ManualWrap);
+ textOption.setTextDirection(option->direction);
+ textOption.setAlignment(QStyle::visualAlignment(option->direction, option->displayAlignment));
+ QTextLayout textLayout;
+ textLayout.setTextOption(textOption);
+ textLayout.setFont(option->font);
+ textLayout.setText(option->text);
+
+ QSizeF textLayoutSize = viewItemTextLayout(textLayout, textRect.width());
+
+ QString elidedText;
+ qreal height = 0;
+ qreal width = 0;
+ int elidedIndex = -1;
+ const int lineCount = textLayout.lineCount();
+ for (int j = 0; j < lineCount; ++j) {
+ const QTextLine line = textLayout.lineAt(j);
+ if (j + 1 <= lineCount - 1) {
+ const QTextLine nextLine = textLayout.lineAt(j + 1);
+ if ((nextLine.y() + nextLine.height()) > textRect.height()) {
+ int start = line.textStart();
+ int length = line.textLength() + nextLine.textLength();
+ const QStackTextEngine engine(textLayout.text().mid(start, length), option->font);
+ elidedText = engine.elidedText(option->textElideMode, textRect.width());
+ height += line.height();
+ width = textRect.width();
+ elidedIndex = j;
+ break;
+ }
+ }
+ if (line.naturalTextWidth() > textRect.width()) {
+ int start = line.textStart();
+ int length = line.textLength();
+ const QStackTextEngine engine(textLayout.text().mid(start, length), option->font);
+ elidedText = engine.elidedText(option->textElideMode, textRect.width());
+ height += line.height();
+ width = textRect.width();
+ elidedIndex = j;
+ break;
+ }
+ width = qMax<qreal>(width, line.width());
+ height += line.height();
+ }
+
+ const QRect layoutRect = QStyle::alignedRect(option->direction, option->displayAlignment,
+ QSize(int(width), int(height)), textRect);
+ const QPointF position = layoutRect.topLeft();
+ for (int i = 0; i < lineCount; ++i) {
+ const QTextLine line = textLayout.lineAt(i);
+ if (i == elidedIndex) {
+ qreal x = position.x() + line.x();
+ qreal y = position.y() + line.y() + line.ascent();
+ p->save();
+ p->setFont(option->font);
+ p->drawText(int(x), int(y), elidedText);
+ p->restore();
+ break;
+ }
+ line.draw(p, position);
+ }
+}
+
+/* Set sizehint to false to layout the elements inside opt->rect. Set sizehint to true to ignore
+ opt->rect and return rectangles in infinite space */
+void QCommonStylePrivate::viewItemLayout(const QStyleOptionViewItemV4 *opt, QRect *checkRect,
+ QRect *pixmapRect, QRect *textRect, bool sizehint) const
+{
+ Q_Q(const QCommonStyle);
+ Q_ASSERT(checkRect && pixmapRect && textRect);
+ *pixmapRect = QRect(QPoint(0, 0), viewItemSize(opt, Qt::DecorationRole));
+ *textRect = QRect(QPoint(0, 0), viewItemSize(opt, Qt::DisplayRole));
+ *checkRect = QRect(QPoint(0, 0), viewItemSize(opt, Qt::CheckStateRole));
+
+ const QWidget *widget = opt->widget;
+ const bool hasCheck = checkRect->isValid();
+ const bool hasPixmap = pixmapRect->isValid();
+ const bool hasText = textRect->isValid();
+ const int textMargin = hasText ? q->pixelMetric(QStyle::PM_FocusFrameHMargin, opt, widget) + 1 : 0;
+ const int pixmapMargin = hasPixmap ? q->pixelMetric(QStyle::PM_FocusFrameHMargin, opt, widget) + 1 : 0;
+ const int checkMargin = hasCheck ? q->pixelMetric(QStyle::PM_FocusFrameHMargin, opt, widget) + 1 : 0;
+ int x = opt->rect.left();
+ int y = opt->rect.top();
+ int w, h;
+
+ if (textRect->height() == 0 && !hasPixmap)
+ textRect->setHeight(opt->fontMetrics.height());
+
+ QSize pm(0, 0);
+ if (hasPixmap) {
+ pm = pixmapRect->size();
+ pm.rwidth() += 2 * pixmapMargin;
+ }
+ if (sizehint) {
+ h = qMax(checkRect->height(), qMax(textRect->height(), pm.height()));
+ if (opt->decorationPosition == QStyleOptionViewItem::Left
+ || opt->decorationPosition == QStyleOptionViewItem::Right) {
+ w = textRect->width() + pm.width();
+ } else {
+ w = qMax(textRect->width(), pm.width());
+ }
+ } else {
+ w = opt->rect.width();
+ h = opt->rect.height();
+ }
+
+ int cw = 0;
+ QRect check;
+ if (hasCheck) {
+ cw = checkRect->width() + 2 * checkMargin;
+ if (sizehint) w += cw;
+ if (opt->direction == Qt::RightToLeft) {
+ check.setRect(x + w - cw, y, cw, h);
+ } else {
+ check.setRect(x, y, cw, h);
+ }
+ }
+
+ QRect display;
+ QRect decoration;
+ switch (opt->decorationPosition) {
+ case QStyleOptionViewItem::Top: {
+ if (hasPixmap)
+ pm.setHeight(pm.height() + pixmapMargin); // add space
+ h = sizehint ? textRect->height() : h - pm.height();
+
+ if (opt->direction == Qt::RightToLeft) {
+ decoration.setRect(x, y, w - cw, pm.height());
+ display.setRect(x, y + pm.height(), w - cw, h);
+ } else {
+ decoration.setRect(x + cw, y, w - cw, pm.height());
+ display.setRect(x + cw, y + pm.height(), w - cw, h);
+ }
+ break; }
+ case QStyleOptionViewItem::Bottom: {
+ if (hasText)
+ textRect->setHeight(textRect->height() + textMargin); // add space
+ h = sizehint ? textRect->height() + pm.height() : h;
+
+ if (opt->direction == Qt::RightToLeft) {
+ display.setRect(x, y, w - cw, textRect->height());
+ decoration.setRect(x, y + textRect->height(), w - cw, h - textRect->height());
+ } else {
+ display.setRect(x + cw, y, w - cw, textRect->height());
+ decoration.setRect(x + cw, y + textRect->height(), w - cw, h - textRect->height());
+ }
+ break; }
+ case QStyleOptionViewItem::Left: {
+ if (opt->direction == Qt::LeftToRight) {
+ decoration.setRect(x + cw, y, pm.width(), h);
+ display.setRect(decoration.right() + 1, y, w - pm.width() - cw, h);
+ } else {
+ display.setRect(x, y, w - pm.width() - cw, h);
+ decoration.setRect(display.right() + 1, y, pm.width(), h);
+ }
+ break; }
+ case QStyleOptionViewItem::Right: {
+ if (opt->direction == Qt::LeftToRight) {
+ display.setRect(x + cw, y, w - pm.width() - cw, h);
+ decoration.setRect(display.right() + 1, y, pm.width(), h);
+ } else {
+ decoration.setRect(x, y, pm.width(), h);
+ display.setRect(decoration.right() + 1, y, w - pm.width() - cw, h);
+ }
+ break; }
+ default:
+ qWarning("doLayout: decoration position is invalid");
+ decoration = *pixmapRect;
+ break;
+ }
+
+ if (!sizehint) { // we only need to do the internal layout if we are going to paint
+ *checkRect = QStyle::alignedRect(opt->direction, Qt::AlignCenter,
+ checkRect->size(), check);
+ *pixmapRect = QStyle::alignedRect(opt->direction, opt->decorationAlignment,
+ pixmapRect->size(), decoration);
+ // the text takes up all available space, unless the decoration is not shown as selected
+ if (opt->showDecorationSelected)
+ *textRect = display;
+ else
+ *textRect = QStyle::alignedRect(opt->direction, opt->displayAlignment,
+ textRect->size().boundedTo(display.size()), display);
+ } else {
+ *checkRect = check;
+ *pixmapRect = decoration;
+ *textRect = display;
+ }
+}
+#endif // QT_NO_ITEMVIEWS
+
+/*!
+ \reimp
+*/
+void QCommonStyle::drawControl(ControlElement element, const QStyleOption *opt,
+ QPainter *p, const QWidget *widget) const
+{
+ Q_D(const QCommonStyle);
+ switch (element) {
+
+ case CE_PushButton:
+ if (const QStyleOptionButton *btn = qstyleoption_cast<const QStyleOptionButton *>(opt)) {
+ drawControl(CE_PushButtonBevel, btn, p, widget);
+ QStyleOptionButton subopt = *btn;
+ subopt.rect = subElementRect(SE_PushButtonContents, btn, widget);
+ drawControl(CE_PushButtonLabel, &subopt, p, widget);
+ if (btn->state & State_HasFocus) {
+ QStyleOptionFocusRect fropt;
+ fropt.QStyleOption::operator=(*btn);
+ fropt.rect = subElementRect(SE_PushButtonFocusRect, btn, widget);
+ drawPrimitive(PE_FrameFocusRect, &fropt, p, widget);
+ }
+ }
+ break;
+ case CE_PushButtonBevel:
+ if (const QStyleOptionButton *btn = qstyleoption_cast<const QStyleOptionButton *>(opt)) {
+ QRect br = btn->rect;
+ int dbi = pixelMetric(PM_ButtonDefaultIndicator, btn, widget);
+ if (btn->features & QStyleOptionButton::DefaultButton)
+ drawPrimitive(PE_FrameDefaultButton, opt, p, widget);
+ if (btn->features & QStyleOptionButton::AutoDefaultButton)
+ br.setCoords(br.left() + dbi, br.top() + dbi, br.right() - dbi, br.bottom() - dbi);
+ if (!(btn->features & (QStyleOptionButton::Flat | QStyleOptionButton::CommandLinkButton))
+ || btn->state & (State_Sunken | State_On)
+ || (btn->features & QStyleOptionButton::CommandLinkButton && btn->state & State_MouseOver)) {
+ QStyleOptionButton tmpBtn = *btn;
+ tmpBtn.rect = br;
+ drawPrimitive(PE_PanelButtonCommand, &tmpBtn, p, widget);
+ }
+ if (btn->features & QStyleOptionButton::HasMenu) {
+ int mbi = pixelMetric(PM_MenuButtonIndicator, btn, widget);
+ QRect ir = btn->rect;
+ QStyleOptionButton newBtn = *btn;
+ newBtn.rect = QRect(ir.right() - mbi + 2, ir.height()/2 - mbi/2 + 3, mbi - 6, mbi - 6);
+ drawPrimitive(PE_IndicatorArrowDown, &newBtn, p, widget);
+ }
+ }
+ break;
+ case CE_PushButtonLabel:
+ if (const QStyleOptionButton *button = qstyleoption_cast<const QStyleOptionButton *>(opt)) {
+ QRect textRect = button->rect;
+ uint tf = Qt::AlignVCenter | Qt::TextShowMnemonic;
+ if (!styleHint(SH_UnderlineShortcut, button, widget))
+ tf |= Qt::TextHideMnemonic;
+
+ if (!button->icon.isNull()) {
+ //Center both icon and text
+ QRect iconRect;
+ QIcon::Mode mode = button->state & State_Enabled ? QIcon::Normal : QIcon::Disabled;
+ if (mode == QIcon::Normal && button->state & State_HasFocus)
+ mode = QIcon::Active;
+ QIcon::State state = QIcon::Off;
+ if (button->state & State_On)
+ state = QIcon::On;
+
+ QPixmap pixmap = button->icon.pixmap(button->iconSize, mode, state);
+ int labelWidth = pixmap.width();
+ int labelHeight = pixmap.height();
+ int iconSpacing = 4;//### 4 is currently hardcoded in QPushButton::sizeHint()
+ int textWidth = button->fontMetrics.boundingRect(opt->rect, tf, button->text).width();
+ if (!button->text.isEmpty())
+ labelWidth += (textWidth + iconSpacing);
+
+ iconRect = QRect(textRect.x() + (textRect.width() - labelWidth) / 2,
+ textRect.y() + (textRect.height() - labelHeight) / 2,
+ pixmap.width(), pixmap.height());
+
+ iconRect = visualRect(button->direction, textRect, iconRect);
+
+ tf |= Qt::AlignLeft; //left align, we adjust the text-rect instead
+
+ if (button->direction == Qt::RightToLeft)
+ textRect.setRight(iconRect.left() - iconSpacing);
+ else
+ textRect.setLeft(iconRect.left() + iconRect.width() + iconSpacing);
+
+ if (button->state & (State_On | State_Sunken))
+ iconRect.translate(pixelMetric(PM_ButtonShiftHorizontal, opt, widget),
+ pixelMetric(PM_ButtonShiftVertical, opt, widget));
+ p->drawPixmap(iconRect, pixmap);
+ } else {
+ tf |= Qt::AlignHCenter;
+ }
+ if (button->state & (State_On | State_Sunken))
+ textRect.translate(pixelMetric(PM_ButtonShiftHorizontal, opt, widget),
+ pixelMetric(PM_ButtonShiftVertical, opt, widget));
+
+ if (button->features & QStyleOptionButton::HasMenu) {
+ int indicatorSize = pixelMetric(PM_MenuButtonIndicator, button, widget);
+ if (button->direction == Qt::LeftToRight)
+ textRect = textRect.adjusted(0, 0, -indicatorSize, 0);
+ else
+ textRect = textRect.adjusted(indicatorSize, 0, 0, 0);
+ }
+ drawItemText(p, textRect, tf, button->palette, (button->state & State_Enabled),
+ button->text, QPalette::ButtonText);
+ }
+ break;
+ case CE_RadioButton:
+ case CE_CheckBox:
+ if (const QStyleOptionButton *btn = qstyleoption_cast<const QStyleOptionButton *>(opt)) {
+ bool isRadio = (element == CE_RadioButton);
+ QStyleOptionButton subopt = *btn;
+ subopt.rect = subElementRect(isRadio ? SE_RadioButtonIndicator
+ : SE_CheckBoxIndicator, btn, widget);
+ drawPrimitive(isRadio ? PE_IndicatorRadioButton : PE_IndicatorCheckBox,
+ &subopt, p, widget);
+ subopt.rect = subElementRect(isRadio ? SE_RadioButtonContents
+ : SE_CheckBoxContents, btn, widget);
+ drawControl(isRadio ? CE_RadioButtonLabel : CE_CheckBoxLabel, &subopt, p, widget);
+ if (btn->state & State_HasFocus) {
+ QStyleOptionFocusRect fropt;
+ fropt.QStyleOption::operator=(*btn);
+ fropt.rect = subElementRect(isRadio ? SE_RadioButtonFocusRect
+ : SE_CheckBoxFocusRect, btn, widget);
+ drawPrimitive(PE_FrameFocusRect, &fropt, p, widget);
+ }
+ }
+ break;
+ case CE_RadioButtonLabel:
+ case CE_CheckBoxLabel:
+ if (const QStyleOptionButton *btn = qstyleoption_cast<const QStyleOptionButton *>(opt)) {
+ uint alignment = visualAlignment(btn->direction, Qt::AlignLeft | Qt::AlignVCenter);
+
+ if (!styleHint(SH_UnderlineShortcut, btn, widget))
+ alignment |= Qt::TextHideMnemonic;
+ QPixmap pix;
+ QRect textRect = btn->rect;
+ if (!btn->icon.isNull()) {
+ pix = btn->icon.pixmap(btn->iconSize, btn->state & State_Enabled ? QIcon::Normal : QIcon::Disabled);
+ drawItemPixmap(p, btn->rect, alignment, pix);
+ if (btn->direction == Qt::RightToLeft)
+ textRect.setRight(textRect.right() - btn->iconSize.width() - 4);
+ else
+ textRect.setLeft(textRect.left() + btn->iconSize.width() + 4);
+ }
+ if (!btn->text.isEmpty()){
+ drawItemText(p, textRect, alignment | Qt::TextShowMnemonic,
+ btn->palette, btn->state & State_Enabled, btn->text, QPalette::WindowText);
+ }
+ }
+ break;
+#ifndef QT_NO_MENU
+ case CE_MenuScroller: {
+ p->fillRect(opt->rect, opt->palette.background());
+ QStyleOption arrowOpt = *opt;
+ arrowOpt.state |= State_Enabled;
+ drawPrimitive(((opt->state & State_DownArrow) ? PE_IndicatorArrowDown : PE_IndicatorArrowUp),
+ &arrowOpt, p, widget);
+ break; }
+ case CE_MenuTearoff:
+ if (opt->state & State_Selected)
+ p->fillRect(opt->rect, opt->palette.brush(QPalette::Highlight));
+ else
+ p->fillRect(opt->rect, opt->palette.brush(QPalette::Button));
+ p->setPen(QPen(opt->palette.dark().color(), 1, Qt::DashLine));
+ p->drawLine(opt->rect.x() + 2, opt->rect.y() + opt->rect.height() / 2 - 1,
+ opt->rect.x() + opt->rect.width() - 4,
+ opt->rect.y() + opt->rect.height() / 2 - 1);
+ p->setPen(QPen(opt->palette.light().color(), 1, Qt::DashLine));
+ p->drawLine(opt->rect.x() + 2, opt->rect.y() + opt->rect.height() / 2,
+ opt->rect.x() + opt->rect.width() - 4, opt->rect.y() + opt->rect.height() / 2);
+ break;
+#endif // QT_NO_MENU
+#ifndef QT_NO_MENUBAR
+ case CE_MenuBarItem:
+ if (const QStyleOptionMenuItem *mbi = qstyleoption_cast<const QStyleOptionMenuItem *>(opt)) {
+ uint alignment = Qt::AlignCenter | Qt::TextShowMnemonic | Qt::TextDontClip
+ | Qt::TextSingleLine;
+ if (!styleHint(SH_UnderlineShortcut, mbi, widget))
+ alignment |= Qt::TextHideMnemonic;
+ QPixmap pix = mbi->icon.pixmap(pixelMetric(PM_SmallIconSize), (mbi->state & State_Enabled) ? QIcon::Normal : QIcon::Disabled);
+ if (!pix.isNull())
+ drawItemPixmap(p,mbi->rect, alignment, pix);
+ else
+ drawItemText(p, mbi->rect, alignment, mbi->palette, mbi->state & State_Enabled,
+ mbi->text, QPalette::ButtonText);
+ }
+ break;
+ case CE_MenuBarEmptyArea:
+ if (widget && !widget->testAttribute(Qt::WA_NoSystemBackground))
+ p->eraseRect(opt->rect);
+ break;
+#endif // QT_NO_MENUBAR
+#ifndef QT_NO_PROGRESSBAR
+ case CE_ProgressBar:
+ if (const QStyleOptionProgressBar *pb
+ = qstyleoption_cast<const QStyleOptionProgressBar *>(opt)) {
+ QStyleOptionProgressBarV2 subopt = *pb;
+ subopt.rect = subElementRect(SE_ProgressBarGroove, pb, widget);
+ drawControl(CE_ProgressBarGroove, &subopt, p, widget);
+ subopt.rect = subElementRect(SE_ProgressBarContents, pb, widget);
+ drawControl(CE_ProgressBarContents, &subopt, p, widget);
+ if (pb->textVisible) {
+ subopt.rect = subElementRect(SE_ProgressBarLabel, pb, widget);
+ drawControl(CE_ProgressBarLabel, &subopt, p, widget);
+ }
+ }
+ break;
+ case CE_ProgressBarGroove:
+ qDrawShadePanel(p, opt->rect, opt->palette, true, 1,
+ &opt->palette.brush(QPalette::Window));
+ break;
+ case CE_ProgressBarLabel:
+ if (const QStyleOptionProgressBar *pb = qstyleoption_cast<const QStyleOptionProgressBar *>(opt)) {
+ bool vertical = false;
+ if (const QStyleOptionProgressBarV2 *pb2 = qstyleoption_cast<const QStyleOptionProgressBarV2 *>(opt)) {
+ vertical = (pb2->orientation == Qt::Vertical);
+ }
+ if (!vertical) {
+ QPalette::ColorRole textRole = QPalette::NoRole;
+ if ((pb->textAlignment & Qt::AlignCenter) && pb->textVisible
+ && ((qint64(pb->progress) - qint64(pb->minimum)) * 2 >= (qint64(pb->maximum) - qint64(pb->minimum)))) {
+ textRole = QPalette::HighlightedText;
+ //Draw text shadow, This will increase readability when the background of same color
+ QRect shadowRect(pb->rect);
+ shadowRect.translate(1,1);
+ QColor shadowColor = (pb->palette.color(textRole).value() <= 128)
+ ? QColor(255,255,255,160) : QColor(0,0,0,160);
+ QPalette shadowPalette = pb->palette;
+ shadowPalette.setColor(textRole, shadowColor);
+ drawItemText(p, shadowRect, Qt::AlignCenter | Qt::TextSingleLine, shadowPalette,
+ pb->state & State_Enabled, pb->text, textRole);
+ }
+ drawItemText(p, pb->rect, Qt::AlignCenter | Qt::TextSingleLine, pb->palette,
+ pb->state & State_Enabled, pb->text, textRole);
+ }
+ }
+ break;
+ case CE_ProgressBarContents:
+ if (const QStyleOptionProgressBar *pb = qstyleoption_cast<const QStyleOptionProgressBar *>(opt)) {
+
+ QRect rect = pb->rect;
+ bool vertical = false;
+ bool inverted = false;
+ qint64 minimum = qint64(pb->minimum);
+ qint64 maximum = qint64(pb->maximum);
+ qint64 progress = qint64(pb->progress);
+
+ // Get extra style options if version 2
+ const QStyleOptionProgressBarV2 *pb2 = qstyleoption_cast<const QStyleOptionProgressBarV2 *>(opt);
+ if (pb2) {
+ vertical = (pb2->orientation == Qt::Vertical);
+ inverted = pb2->invertedAppearance;
+ }
+ QMatrix m;
+
+ if (vertical) {
+ rect = QRect(rect.y(), rect.x(), rect.height(), rect.width()); // flip width and height
+ m.rotate(90);
+ m.translate(0, -(rect.height() + rect.y()*2));
+ }
+
+ QPalette pal2 = pb->palette;
+ // Correct the highlight color if it is the same as the background
+ if (pal2.highlight() == pal2.background())
+ pal2.setColor(QPalette::Highlight, pb->palette.color(QPalette::Active,
+ QPalette::Highlight));
+ bool reverse = ((!vertical && (pb->direction == Qt::RightToLeft)) || vertical);
+ if (inverted)
+ reverse = !reverse;
+ int w = rect.width();
+ if (pb->minimum == 0 && pb->maximum == 0) {
+ // draw busy indicator
+ int x = (progress - minimum) % (w * 2);
+ if (x > w)
+ x = 2 * w - x;
+ x = reverse ? rect.right() - x : x + rect.x();
+ p->setPen(QPen(pal2.highlight().color(), 4));
+ p->drawLine(x, rect.y(), x, rect.height());
+ } else {
+ const int unit_width = pixelMetric(PM_ProgressBarChunkWidth, pb, widget);
+ if (!unit_width)
+ return;
+
+ int u;
+ if (unit_width > 1)
+ u = ((rect.width() + unit_width) / unit_width);
+ else
+ u = w / unit_width;
+ qint64 p_v = progress - minimum;
+ qint64 t_s = (maximum - minimum) ? (maximum - minimum) : qint64(1);
+
+ if (u > 0 && p_v >= INT_MAX / u && t_s >= u) {
+ // scale down to something usable.
+ p_v /= u;
+ t_s /= u;
+ }
+
+ // nu < tnu, if last chunk is only a partial chunk
+ int tnu, nu;
+ tnu = nu = p_v * u / t_s;
+
+ if (nu * unit_width > w)
+ --nu;
+
+ // Draw nu units out of a possible u of unit_width
+ // width, each a rectangle bordered by background
+ // color, all in a sunken panel with a percentage text
+ // display at the end.
+ int x = 0;
+ int x0 = reverse ? rect.right() - ((unit_width > 1) ? unit_width : 0)
+ : rect.x();
+
+ QStyleOptionProgressBarV2 pbBits = *pb;
+ pbBits.rect = rect;
+ pbBits.palette = pal2;
+ int myY = pbBits.rect.y();
+ int myHeight = pbBits.rect.height();
+ pbBits.state = State_None;
+ for (int i = 0; i < nu; ++i) {
+ pbBits.rect.setRect(x0 + x, myY, unit_width, myHeight);
+ pbBits.rect = m.mapRect(QRectF(pbBits.rect)).toRect();
+ drawPrimitive(PE_IndicatorProgressChunk, &pbBits, p, widget);
+ x += reverse ? -unit_width : unit_width;
+ }
+
+ // Draw the last partial chunk to fill up the
+ // progress bar entirely
+ if (nu < tnu) {
+ int pixels_left = w - (nu * unit_width);
+ int offset = reverse ? x0 + x + unit_width-pixels_left : x0 + x;
+ pbBits.rect.setRect(offset, myY, pixels_left, myHeight);
+ pbBits.rect = m.mapRect(QRectF(pbBits.rect)).toRect();
+ drawPrimitive(PE_IndicatorProgressChunk, &pbBits, p, widget);
+ }
+ }
+ }
+ break;
+#endif // QT_NO_PROGRESSBAR
+ case CE_HeaderLabel:
+ if (const QStyleOptionHeader *header = qstyleoption_cast<const QStyleOptionHeader *>(opt)) {
+ QRect rect = header->rect;
+ if (!header->icon.isNull()) {
+ QPixmap pixmap
+ = header->icon.pixmap(pixelMetric(PM_SmallIconSize), (header->state & State_Enabled) ? QIcon::Normal : QIcon::Disabled);
+ int pixw = pixmap.width();
+
+ QRect aligned = alignedRect(header->direction, QFlag(header->iconAlignment), pixmap.size(), rect);
+ QRect inter = aligned.intersected(rect);
+ p->drawPixmap(inter.x(), inter.y(), pixmap, inter.x() - aligned.x(), inter.y() - aligned.y(), inter.width(), inter.height());
+
+ if (header->direction == Qt::LeftToRight)
+ rect.setLeft(rect.left() + pixw + 2);
+ else
+ rect.setRight(rect.right() - pixw - 2);
+ }
+ if (header->state & QStyle::State_On) {
+ QFont fnt = p->font();
+ fnt.setBold(true);
+ p->setFont(fnt);
+ }
+ drawItemText(p, rect, header->textAlignment, header->palette,
+ (header->state & State_Enabled), header->text, QPalette::ButtonText);
+ }
+ break;
+#ifndef QT_NO_TOOLBUTTON
+ case CE_ToolButtonLabel:
+ if (const QStyleOptionToolButton *toolbutton
+ = qstyleoption_cast<const QStyleOptionToolButton *>(opt)) {
+ QRect rect = toolbutton->rect;
+ int shiftX = 0;
+ int shiftY = 0;
+ if (toolbutton->state & (State_Sunken | State_On)) {
+ shiftX = pixelMetric(PM_ButtonShiftHorizontal, toolbutton, widget);
+ shiftY = pixelMetric(PM_ButtonShiftVertical, toolbutton, widget);
+ }
+ // Arrow type always overrules and is always shown
+ bool hasArrow = toolbutton->features & QStyleOptionToolButton::Arrow;
+ if (((!hasArrow && toolbutton->icon.isNull()) && !toolbutton->text.isEmpty())
+ || toolbutton->toolButtonStyle == Qt::ToolButtonTextOnly) {
+ int alignment = Qt::AlignCenter | Qt::TextShowMnemonic;
+ if (!styleHint(SH_UnderlineShortcut, opt, widget))
+ alignment |= Qt::TextHideMnemonic;
+ rect.translate(shiftX, shiftY);
+ drawItemText(p, rect, alignment, toolbutton->palette,
+ opt->state & State_Enabled, toolbutton->text,
+ QPalette::ButtonText);
+ } else {
+ QPixmap pm;
+ QSize pmSize = toolbutton->iconSize;
+ if (!toolbutton->icon.isNull()) {
+ QIcon::State state = toolbutton->state & State_On ? QIcon::On : QIcon::Off;
+ QIcon::Mode mode;
+ if (!(toolbutton->state & State_Enabled))
+ mode = QIcon::Disabled;
+ else if ((opt->state & State_MouseOver) && (opt->state & State_AutoRaise))
+ mode = QIcon::Active;
+ else
+ mode = QIcon::Normal;
+ pm = toolbutton->icon.pixmap(toolbutton->rect.size().boundedTo(toolbutton->iconSize),
+ mode, state);
+ pmSize = pm.size();
+ }
+
+ if (toolbutton->toolButtonStyle != Qt::ToolButtonIconOnly) {
+ p->setFont(toolbutton->font);
+ QRect pr = rect,
+ tr = rect;
+ int alignment = Qt::TextShowMnemonic;
+ if (!styleHint(SH_UnderlineShortcut, opt, widget))
+ alignment |= Qt::TextHideMnemonic;
+
+ if (toolbutton->toolButtonStyle == Qt::ToolButtonTextUnderIcon) {
+ pr.setHeight(pmSize.height() + 6);
+ tr.adjust(0, pr.height(), 0, -3);
+ pr.translate(shiftX, shiftY);
+ if (!hasArrow) {
+ drawItemPixmap(p, pr, Qt::AlignCenter, pm);
+ } else {
+ drawArrow(this, toolbutton, pr, p, widget);
+ }
+ alignment |= Qt::AlignCenter;
+ } else {
+ pr.setWidth(pmSize.width() + 8);
+ tr.adjust(pr.width(), 0, 0, 0);
+ pr.translate(shiftX, shiftY);
+ if (!hasArrow) {
+ drawItemPixmap(p, QStyle::visualRect(opt->direction, rect, pr), Qt::AlignCenter, pm);
+ } else {
+ drawArrow(this, toolbutton, pr, p, widget);
+ }
+ alignment |= Qt::AlignLeft | Qt::AlignVCenter;
+ }
+ tr.translate(shiftX, shiftY);
+ drawItemText(p, QStyle::visualRect(opt->direction, rect, tr), alignment, toolbutton->palette,
+ toolbutton->state & State_Enabled, toolbutton->text,
+ QPalette::ButtonText);
+ } else {
+ rect.translate(shiftX, shiftY);
+ if (hasArrow) {
+ drawArrow(this, toolbutton, rect, p, widget);
+ } else {
+ drawItemPixmap(p, rect, Qt::AlignCenter, pm);
+ }
+ }
+ }
+ }
+ break;
+#endif // QT_NO_TOOLBUTTON
+#ifndef QT_NO_TOOLBOX
+ case CE_ToolBoxTab:
+ if (const QStyleOptionToolBox *tb = qstyleoption_cast<const QStyleOptionToolBox *>(opt)) {
+ drawControl(CE_ToolBoxTabShape, tb, p, widget);
+ drawControl(CE_ToolBoxTabLabel, tb, p, widget);
+ }
+ break;
+ case CE_ToolBoxTabShape:
+ if (const QStyleOptionToolBox *tb = qstyleoption_cast<const QStyleOptionToolBox *>(opt)) {
+ int d = 20 + tb->rect.height() - 3;
+ QPolygon a(7);
+ if (tb->direction != Qt::RightToLeft) {
+ a.setPoint(0, -1, tb->rect.height() + 1);
+ a.setPoint(1, -1, 1);
+ a.setPoint(2, tb->rect.width() - d, 1);
+ a.setPoint(3, tb->rect.width() - 20, tb->rect.height() - 2);
+ a.setPoint(4, tb->rect.width() - 1, tb->rect.height() - 2);
+ a.setPoint(5, tb->rect.width() - 1, tb->rect.height() + 1);
+ a.setPoint(6, -1, tb->rect.height() + 1);
+ } else {
+ a.setPoint(0, tb->rect.width(), tb->rect.height() + 1);
+ a.setPoint(1, tb->rect.width(), 1);
+ a.setPoint(2, d - 1, 1);
+ a.setPoint(3, 20 - 1, tb->rect.height() - 2);
+ a.setPoint(4, 0, tb->rect.height() - 2);
+ a.setPoint(5, 0, tb->rect.height() + 1);
+ a.setPoint(6, tb->rect.width(), tb->rect.height() + 1);
+ }
+
+ p->setPen(tb->palette.mid().color().darker(150));
+ p->drawPolygon(a);
+ p->setPen(tb->palette.light().color());
+ if (tb->direction != Qt::RightToLeft) {
+ p->drawLine(0, 2, tb->rect.width() - d, 2);
+ p->drawLine(tb->rect.width() - d - 1, 2, tb->rect.width() - 21, tb->rect.height() - 1);
+ p->drawLine(tb->rect.width() - 20, tb->rect.height() - 1,
+ tb->rect.width(), tb->rect.height() - 1);
+ } else {
+ p->drawLine(tb->rect.width() - 1, 2, d - 1, 2);
+ p->drawLine(d, 2, 20, tb->rect.height() - 1);
+ p->drawLine(19, tb->rect.height() - 1,
+ -1, tb->rect.height() - 1);
+ }
+ p->setBrush(Qt::NoBrush);
+ }
+ break;
+#endif // QT_NO_TOOLBOX
+#ifndef QT_NO_TABBAR
+ case CE_TabBarTab:
+ if (const QStyleOptionTab *tab = qstyleoption_cast<const QStyleOptionTab *>(opt)) {
+ drawControl(CE_TabBarTabShape, tab, p, widget);
+ drawControl(CE_TabBarTabLabel, tab, p, widget);
+ }
+ break;
+ case CE_TabBarTabShape:
+ if (const QStyleOptionTab *tab = qstyleoption_cast<const QStyleOptionTab *>(opt)) {
+ p->save();
+
+ QRect rect(tab->rect);
+ bool selected = tab->state & State_Selected;
+ bool onlyOne = tab->position == QStyleOptionTab::OnlyOneTab;
+ int tabOverlap = onlyOne ? 0 : pixelMetric(PM_TabBarTabOverlap, opt, widget);
+
+ if (!selected) {
+ switch (tab->shape) {
+ case QTabBar::TriangularNorth:
+ rect.adjust(0, 0, 0, -tabOverlap);
+ if(!selected)
+ rect.adjust(1, 1, -1, 0);
+ break;
+ case QTabBar::TriangularSouth:
+ rect.adjust(0, tabOverlap, 0, 0);
+ if(!selected)
+ rect.adjust(1, 0, -1, -1);
+ break;
+ case QTabBar::TriangularEast:
+ rect.adjust(tabOverlap, 0, 0, 0);
+ if(!selected)
+ rect.adjust(0, 1, -1, -1);
+ break;
+ case QTabBar::TriangularWest:
+ rect.adjust(0, 0, -tabOverlap, 0);
+ if(!selected)
+ rect.adjust(1, 1, 0, -1);
+ break;
+ default:
+ break;
+ }
+ }
+
+ p->setPen(QPen(tab->palette.foreground(), 0));
+ if (selected) {
+ p->setBrush(tab->palette.base());
+ } else {
+ if (widget && widget->parentWidget())
+ p->setBrush(widget->parentWidget()->palette().background());
+ else
+ p->setBrush(tab->palette.background());
+ }
+
+ int y;
+ int x;
+ QPolygon a(10);
+ switch (tab->shape) {
+ case QTabBar::TriangularNorth:
+ case QTabBar::TriangularSouth: {
+ a.setPoint(0, 0, -1);
+ a.setPoint(1, 0, 0);
+ y = rect.height() - 2;
+ x = y / 3;
+ a.setPoint(2, x++, y - 1);
+ ++x;
+ a.setPoint(3, x++, y++);
+ a.setPoint(4, x, y);
+
+ int i;
+ int right = rect.width() - 1;
+ for (i = 0; i < 5; ++i)
+ a.setPoint(9 - i, right - a.point(i).x(), a.point(i).y());
+ if (tab->shape == QTabBar::TriangularNorth)
+ for (i = 0; i < 10; ++i)
+ a.setPoint(i, a.point(i).x(), rect.height() - 1 - a.point(i).y());
+
+ a.translate(rect.left(), rect.top());
+ p->setRenderHint(QPainter::Antialiasing);
+ p->translate(0, 0.5);
+
+ QPainterPath path;
+ path.addPolygon(a);
+ p->drawPath(path);
+ break; }
+ case QTabBar::TriangularEast:
+ case QTabBar::TriangularWest: {
+ a.setPoint(0, -1, 0);
+ a.setPoint(1, 0, 0);
+ x = rect.width() - 2;
+ y = x / 3;
+ a.setPoint(2, x - 1, y++);
+ ++y;
+ a.setPoint(3, x++, y++);
+ a.setPoint(4, x, y);
+ int i;
+ int bottom = rect.height() - 1;
+ for (i = 0; i < 5; ++i)
+ a.setPoint(9 - i, a.point(i).x(), bottom - a.point(i).y());
+ if (tab->shape == QTabBar::TriangularWest)
+ for (i = 0; i < 10; ++i)
+ a.setPoint(i, rect.width() - 1 - a.point(i).x(), a.point(i).y());
+ a.translate(rect.left(), rect.top());
+ p->setRenderHint(QPainter::Antialiasing);
+ p->translate(0.5, 0);
+ QPainterPath path;
+ path.addPolygon(a);
+ p->drawPath(path);
+ break; }
+ default:
+ break;
+ }
+ p->restore();
+ }
+ break;
+ case CE_ToolBoxTabLabel:
+ if (const QStyleOptionToolBox *tb = qstyleoption_cast<const QStyleOptionToolBox *>(opt)) {
+ bool enabled = tb->state & State_Enabled;
+ bool selected = tb->state & State_Selected;
+ QPixmap pm = tb->icon.pixmap(pixelMetric(QStyle::PM_SmallIconSize, tb, widget),
+ enabled ? QIcon::Normal : QIcon::Disabled);
+
+ QRect cr = subElementRect(QStyle::SE_ToolBoxTabContents, tb, widget);
+ QRect tr, ir;
+ int ih = 0;
+ if (pm.isNull()) {
+ tr = cr;
+ tr.adjust(4, 0, -8, 0);
+ } else {
+ int iw = pm.width() + 4;
+ ih = pm.height();
+ ir = QRect(cr.left() + 4, cr.top(), iw + 2, ih);
+ tr = QRect(ir.right(), cr.top(), cr.width() - ir.right() - 4, cr.height());
+ }
+
+ if (selected && styleHint(QStyle::SH_ToolBox_SelectedPageTitleBold, tb, widget)) {
+ QFont f(p->font());
+ f.setBold(true);
+ p->setFont(f);
+ }
+
+ QString txt = tb->fontMetrics.elidedText(tb->text, Qt::ElideRight, tr.width());
+
+ if (ih)
+ p->drawPixmap(ir.left(), (tb->rect.height() - ih) / 2, pm);
+
+ int alignment = Qt::AlignLeft | Qt::AlignVCenter | Qt::TextShowMnemonic;
+ if (!styleHint(QStyle::SH_UnderlineShortcut, tb, widget))
+ alignment |= Qt::TextHideMnemonic;
+ drawItemText(p, tr, alignment, tb->palette, enabled, txt, QPalette::ButtonText);
+
+ if (!txt.isEmpty() && opt->state & State_HasFocus) {
+ QStyleOptionFocusRect opt;
+ opt.rect = tr;
+ opt.palette = tb->palette;
+ opt.state = QStyle::State_None;
+ drawPrimitive(QStyle::PE_FrameFocusRect, &opt, p, widget);
+ }
+ }
+ break;
+ case CE_TabBarTabLabel:
+ if (const QStyleOptionTab *tab = qstyleoption_cast<const QStyleOptionTab *>(opt)) {
+ // ### consider merging this with SE_TabBarTabText
+ QStyleOptionTabV3 tabV2(*tab);
+ QRect tr = tabV2.rect;
+ bool verticalTabs = tabV2.shape == QTabBar::RoundedEast
+ || tabV2.shape == QTabBar::RoundedWest
+ || tabV2.shape == QTabBar::TriangularEast
+ || tabV2.shape == QTabBar::TriangularWest;
+
+ int alignment = Qt::AlignCenter | Qt::TextShowMnemonic;
+ if (!styleHint(SH_UnderlineShortcut, opt, widget))
+ alignment |= Qt::TextHideMnemonic;
+
+ if (verticalTabs) {
+ p->save();
+ int newX, newY, newRot;
+ if (tabV2.shape == QTabBar::RoundedEast || tabV2.shape == QTabBar::TriangularEast) {
+ newX = tr.width() + tr.x();
+ newY = tr.y();
+ newRot = 90;
+ } else {
+ newX = tr.x();
+ newY = tr.y() + tr.height();
+ newRot = -90;
+ }
+ tr.setRect(0, 0, tr.height(), tr.width());
+ QTransform m;
+ m.translate(newX, newY);
+ m.rotate(newRot);
+ p->setTransform(m, true);
+ }
+ tr = subElementRect(SE_TabBarTabText, opt, widget);
+
+ if (!tabV2.icon.isNull()) {
+ QSize iconSize = tabV2.iconSize;
+ if (!iconSize.isValid()) {
+ int iconExtent = pixelMetric(PM_SmallIconSize);
+ iconSize = QSize(iconExtent, iconExtent);
+ }
+ QSize tabIconSize = tabV2.icon.actualSize(iconSize,
+ (tabV2.state & State_Enabled) ? QIcon::Normal
+ : QIcon::Disabled);
+ QPixmap tabIcon = tabV2.icon.pixmap(iconSize,
+ (tabV2.state & State_Enabled) ? QIcon::Normal
+ : QIcon::Disabled);
+
+ int offset = 4;
+ int left = opt->rect.left();
+ if (tabV2.leftButtonSize.isEmpty())
+ offset += 2;
+ else
+ left += tabV2.leftButtonSize.width() + (6 + 2) + 2;
+ QRect iconRect = QRect(left + offset, tr.center().y() - tabIcon.height() / 2,
+ tabIconSize.width(), tabIconSize.height());
+ if (!verticalTabs)
+ iconRect = visualRect(opt->direction, opt->rect, iconRect);
+ p->drawPixmap(iconRect.x(), iconRect.y(), tabIcon);
+ }
+
+ drawItemText(p, tr, alignment, tab->palette, tab->state & State_Enabled, tab->text, QPalette::WindowText);
+ if (verticalTabs)
+ p->restore();
+
+ if (tabV2.state & State_HasFocus) {
+ const int OFFSET = 1 + pixelMetric(PM_DefaultFrameWidth);
+
+ int x1, x2;
+ x1 = tabV2.rect.left();
+ x2 = tabV2.rect.right() - 1;
+
+ QStyleOptionFocusRect fropt;
+ fropt.QStyleOption::operator=(*tab);
+ fropt.rect.setRect(x1 + 1 + OFFSET, tabV2.rect.y() + OFFSET,
+ x2 - x1 - 2*OFFSET, tabV2.rect.height() - 2*OFFSET);
+ drawPrimitive(PE_FrameFocusRect, &fropt, p, widget);
+ }
+ }
+ break;
+#endif // QT_NO_TABBAR
+#ifndef QT_NO_SIZEGRIP
+ case CE_SizeGrip: {
+ p->save();
+ int x, y, w, h;
+ opt->rect.getRect(&x, &y, &w, &h);
+
+ int sw = qMin(h, w);
+ if (h > w)
+ p->translate(0, h - w);
+ else
+ p->translate(w - h, 0);
+
+ int sx = x;
+ int sy = y;
+ int s = sw / 3;
+
+ Qt::Corner corner;
+ if (const QStyleOptionSizeGrip *sgOpt = qstyleoption_cast<const QStyleOptionSizeGrip *>(opt))
+ corner = sgOpt->corner;
+ else if (opt->direction == Qt::RightToLeft)
+ corner = Qt::BottomLeftCorner;
+ else
+ corner = Qt::BottomRightCorner;
+
+ if (corner == Qt::BottomLeftCorner) {
+ sx = x + sw;
+ for (int i = 0; i < 4; ++i) {
+ p->setPen(QPen(opt->palette.light().color(), 1));
+ p->drawLine(x, sy - 1 , sx + 1, sw);
+ p->setPen(QPen(opt->palette.dark().color(), 1));
+ p->drawLine(x, sy, sx, sw);
+ p->setPen(QPen(opt->palette.dark().color(), 1));
+ p->drawLine(x, sy + 1, sx - 1, sw);
+ sx -= s;
+ sy += s;
+ }
+ } else if (corner == Qt::BottomRightCorner) {
+ for (int i = 0; i < 4; ++i) {
+ p->setPen(QPen(opt->palette.light().color(), 1));
+ p->drawLine(sx - 1, sw, sw, sy - 1);
+ p->setPen(QPen(opt->palette.dark().color(), 1));
+ p->drawLine(sx, sw, sw, sy);
+ p->setPen(QPen(opt->palette.dark().color(), 1));
+ p->drawLine(sx + 1, sw, sw, sy + 1);
+ sx += s;
+ sy += s;
+ }
+ } else if (corner == Qt::TopRightCorner) {
+ sy = y + sw;
+ for (int i = 0; i < 4; ++i) {
+ p->setPen(QPen(opt->palette.light().color(), 1));
+ p->drawLine(sx - 1, y, sw, sy + 1);
+ p->setPen(QPen(opt->palette.dark().color(), 1));
+ p->drawLine(sx, y, sw, sy);
+ p->setPen(QPen(opt->palette.dark().color(), 1));
+ p->drawLine(sx + 1, y, sw, sy - 1);
+ sx += s;
+ sy -= s;
+ }
+ } else if (corner == Qt::TopLeftCorner) {
+ for (int i = 0; i < 4; ++i) {
+ p->setPen(QPen(opt->palette.light().color(), 1));
+ p->drawLine(x, sy - 1, sx - 1, y);
+ p->setPen(QPen(opt->palette.dark().color(), 1));
+ p->drawLine(x, sy, sx, y);
+ p->setPen(QPen(opt->palette.dark().color(), 1));
+ p->drawLine(x, sy + 1, sx + 1, y);
+ sx += s;
+ sy += s;
+ }
+ }
+ p->restore();
+ break; }
+#endif // QT_NO_SIZEGRIP
+#ifndef QT_NO_RUBBERBAND
+ case CE_RubberBand: {
+ if (const QStyleOptionRubberBand *rbOpt = qstyleoption_cast<const QStyleOptionRubberBand *>(opt)) {
+ QPixmap tiledPixmap(16, 16);
+ QPainter pixmapPainter(&tiledPixmap);
+ pixmapPainter.setPen(Qt::NoPen);
+ pixmapPainter.setBrush(Qt::Dense4Pattern);
+ pixmapPainter.setBackground(QBrush(opt->palette.base()));
+ pixmapPainter.setBackgroundMode(Qt::OpaqueMode);
+ pixmapPainter.drawRect(0, 0, tiledPixmap.width(), tiledPixmap.height());
+ pixmapPainter.end();
+ // ### workaround for borked XRENDER
+ tiledPixmap = QPixmap::fromImage(tiledPixmap.toImage());
+
+ p->save();
+ QRect r = opt->rect;
+ QStyleHintReturnMask mask;
+ if (styleHint(QStyle::SH_RubberBand_Mask, opt, widget, &mask))
+ p->setClipRegion(mask.region);
+ p->drawTiledPixmap(r.x(), r.y(), r.width(), r.height(), tiledPixmap);
+ p->setPen(opt->palette.color(QPalette::Active, QPalette::WindowText));
+ p->setBrush(Qt::NoBrush);
+ p->drawRect(r.adjusted(0, 0, -1, -1));
+ if (rbOpt->shape == QRubberBand::Rectangle)
+ p->drawRect(r.adjusted(3, 3, -4, -4));
+ p->restore();
+ }
+ break; }
+#endif // QT_NO_RUBBERBAND
+#ifndef QT_NO_DOCKWIDGET
+ case CE_DockWidgetTitle:
+ if (const QStyleOptionDockWidget *dwOpt = qstyleoption_cast<const QStyleOptionDockWidget *>(opt)) {
+ QRect r = dwOpt->rect.adjusted(0, 0, -1, -1);
+ if (dwOpt->movable) {
+ p->setPen(dwOpt->palette.color(QPalette::Dark));
+ p->drawRect(r);
+ }
+
+ if (!dwOpt->title.isEmpty()) {
+ const QStyleOptionDockWidgetV2 *v2
+ = qstyleoption_cast<const QStyleOptionDockWidgetV2*>(opt);
+ bool verticalTitleBar = v2 == 0 ? false : v2->verticalTitleBar;
+
+ if (verticalTitleBar) {
+ QSize s = r.size();
+ s.transpose();
+ r.setSize(s);
+
+ p->save();
+ p->translate(r.left(), r.top() + r.width());
+ p->rotate(-90);
+ p->translate(-r.left(), -r.top());
+ }
+
+ const int indent = p->fontMetrics().descent();
+ drawItemText(p, r.adjusted(indent + 1, 1, -indent - 1, -1),
+ Qt::AlignLeft | Qt::AlignVCenter | Qt::TextShowMnemonic, dwOpt->palette,
+ dwOpt->state & State_Enabled, dwOpt->title,
+ QPalette::WindowText);
+
+ if (verticalTitleBar)
+ p->restore();
+ }
+ }
+ break;
+#endif // QT_NO_DOCKWIDGET
+ case CE_Header:
+ if (const QStyleOptionHeader *header = qstyleoption_cast<const QStyleOptionHeader *>(opt)) {
+ QRegion clipRegion = p->clipRegion();
+ p->setClipRect(opt->rect);
+ drawControl(CE_HeaderSection, header, p, widget);
+ QStyleOptionHeader subopt = *header;
+ subopt.rect = subElementRect(SE_HeaderLabel, header, widget);
+ if (subopt.rect.isValid())
+ drawControl(CE_HeaderLabel, &subopt, p, widget);
+ if (header->sortIndicator != QStyleOptionHeader::None) {
+ subopt.rect = subElementRect(SE_HeaderArrow, opt, widget);
+ drawPrimitive(PE_IndicatorHeaderArrow, &subopt, p, widget);
+ }
+ p->setClipRegion(clipRegion);
+ }
+ break;
+ case CE_FocusFrame:
+ p->fillRect(opt->rect, opt->palette.foreground());
+ break;
+ case CE_HeaderSection:
+ qDrawShadePanel(p, opt->rect, opt->palette,
+ opt->state & State_Sunken, 1,
+ &opt->palette.brush(QPalette::Button));
+ break;
+ case CE_HeaderEmptyArea:
+ p->fillRect(opt->rect, opt->palette.background());
+ break;
+#ifndef QT_NO_COMBOBOX
+ case CE_ComboBoxLabel:
+ if (const QStyleOptionComboBox *cb = qstyleoption_cast<const QStyleOptionComboBox *>(opt)) {
+ QRect editRect = subControlRect(CC_ComboBox, cb, SC_ComboBoxEditField, widget);
+ p->save();
+ p->setClipRect(editRect);
+ if (!cb->currentIcon.isNull()) {
+ QIcon::Mode mode = cb->state & State_Enabled ? QIcon::Normal
+ : QIcon::Disabled;
+ QPixmap pixmap = cb->currentIcon.pixmap(cb->iconSize, mode);
+ QRect iconRect(editRect);
+ iconRect.setWidth(cb->iconSize.width() + 4);
+ iconRect = alignedRect(cb->direction,
+ Qt::AlignLeft | Qt::AlignVCenter,
+ iconRect.size(), editRect);
+ if (cb->editable)
+ p->fillRect(iconRect, opt->palette.brush(QPalette::Base));
+ drawItemPixmap(p, iconRect, Qt::AlignCenter, pixmap);
+
+ if (cb->direction == Qt::RightToLeft)
+ editRect.translate(-4 - cb->iconSize.width(), 0);
+ else
+ editRect.translate(cb->iconSize.width() + 4, 0);
+ }
+ if (!cb->currentText.isEmpty() && !cb->editable) {
+ drawItemText(p, editRect.adjusted(1, 0, -1, 0),
+ visualAlignment(cb->direction, Qt::AlignLeft | Qt::AlignVCenter),
+ cb->palette, cb->state & State_Enabled, cb->currentText);
+ }
+ p->restore();
+ }
+ break;
+#endif // QT_NO_COMBOBOX
+#ifndef QT_NO_TOOLBAR
+ case CE_ToolBar:
+ if (const QStyleOptionToolBar *toolBar = qstyleoption_cast<const QStyleOptionToolBar *>(opt)) {
+ // Compatibility with styles that use PE_PanelToolBar
+ QStyleOptionFrame frame;
+ frame.QStyleOption::operator=(*toolBar);
+ frame.lineWidth = toolBar->lineWidth;
+ frame.midLineWidth = toolBar->midLineWidth;
+ drawPrimitive(PE_PanelToolBar, opt, p, widget);
+
+ if (widget && qobject_cast<QToolBar *>(widget->parentWidget()))
+ break;
+ qDrawShadePanel(p, toolBar->rect, toolBar->palette, false, toolBar->lineWidth,
+ &toolBar->palette.brush(QPalette::Button));
+ }
+ break;
+#endif // QT_NO_TOOLBAR
+ case CE_ColumnViewGrip: {
+ // draw background gradients
+ QLinearGradient g(0, 0, opt->rect.width(), 0);
+ g.setColorAt(0, opt->palette.color(QPalette::Active, QPalette::Mid));
+ g.setColorAt(0.5, Qt::white);
+ p->fillRect(QRect(0, 0, opt->rect.width(), opt->rect.height()), g);
+
+ // draw the two lines
+ QPen pen(p->pen());
+ pen.setWidth(opt->rect.width()/20);
+ pen.setColor(opt->palette.color(QPalette::Active, QPalette::Dark));
+ p->setPen(pen);
+
+ int line1starting = opt->rect.width()*8 / 20;
+ int line2starting = opt->rect.width()*13 / 20;
+ int top = opt->rect.height()*20/75;
+ int bottom = opt->rect.height() - 1 - top;
+ p->drawLine(line1starting, top, line1starting, bottom);
+ p->drawLine(line2starting, top, line2starting, bottom);
+ }
+ break;
+
+#ifndef QT_NO_ITEMVIEWS
+ case CE_ItemViewItem:
+ if (const QStyleOptionViewItemV4 *vopt = qstyleoption_cast<const QStyleOptionViewItemV4 *>(opt)) {
+ p->save();
+ p->setClipRect(opt->rect);
+
+ QRect checkRect = subElementRect(SE_ItemViewItemCheckIndicator, vopt, widget);
+ QRect iconRect = subElementRect(SE_ItemViewItemDecoration, vopt, widget);
+ QRect textRect = subElementRect(SE_ItemViewItemText, vopt, widget);
+
+ // draw the background
+ drawPrimitive(PE_PanelItemViewItem, opt, p, widget);
+
+ // draw the check mark
+ if (checkRect.isValid()) {
+ QStyleOptionViewItemV4 option(*vopt);
+ option.rect = checkRect;
+ option.state = option.state & ~QStyle::State_HasFocus;
+
+ switch (vopt->checkState) {
+ case Qt::Unchecked:
+ option.state |= QStyle::State_Off;
+ break;
+ case Qt::PartiallyChecked:
+ option.state |= QStyle::State_NoChange;
+ break;
+ case Qt::Checked:
+ option.state |= QStyle::State_On;
+ break;
+ }
+ drawPrimitive(QStyle::PE_IndicatorViewItemCheck, &option, p, widget);
+ }
+
+ // draw the icon
+ QIcon::Mode mode = QIcon::Normal;
+ if (!(vopt->state & QStyle::State_Enabled))
+ mode = QIcon::Disabled;
+ else if (vopt->state & QStyle::State_Selected)
+ mode = QIcon::Selected;
+ QIcon::State state = vopt->state & QStyle::State_Open ? QIcon::On : QIcon::Off;
+ vopt->icon.paint(p, iconRect, vopt->decorationAlignment, mode, state);
+
+ // draw the text
+ if (!vopt->text.isEmpty()) {
+ QPalette::ColorGroup cg = vopt->state & QStyle::State_Enabled
+ ? QPalette::Normal : QPalette::Disabled;
+ if (cg == QPalette::Normal && !(vopt->state & QStyle::State_Active))
+ cg = QPalette::Inactive;
+
+ if (vopt->state & QStyle::State_Selected) {
+ p->setPen(vopt->palette.color(cg, QPalette::HighlightedText));
+ } else {
+ p->setPen(vopt->palette.color(cg, QPalette::Text));
+ }
+ if (vopt->state & QStyle::State_Editing) {
+ p->setPen(vopt->palette.color(cg, QPalette::Text));
+ p->drawRect(textRect.adjusted(0, 0, -1, -1));
+ }
+
+ d->viewItemDrawText(p, vopt, textRect);
+ }
+
+ // draw the focus rect
+ if (vopt->state & QStyle::State_HasFocus) {
+ QStyleOptionFocusRect o;
+ o.QStyleOption::operator=(*vopt);
+ o.rect = subElementRect(SE_ItemViewItemFocusRect, vopt, widget);
+ o.state |= QStyle::State_KeyboardFocusChange;
+ o.state |= QStyle::State_Item;
+ QPalette::ColorGroup cg = (vopt->state & QStyle::State_Enabled)
+ ? QPalette::Normal : QPalette::Disabled;
+ o.backgroundColor = vopt->palette.color(cg, (vopt->state & QStyle::State_Selected)
+ ? QPalette::Highlight : QPalette::Window);
+ drawPrimitive(QStyle::PE_FrameFocusRect, &o, p, widget);
+ }
+
+ p->restore();
+ }
+ break;
+
+#endif // QT_NO_ITEMVIEWS
+#ifndef QT_NO_FRAME
+ case CE_ShapedFrame:
+ if (const QStyleOptionFrameV3 *f = qstyleoption_cast<const QStyleOptionFrameV3 *>(opt)) {
+ int frameShape = f->frameShape;
+ int frameShadow = QFrame::Plain;
+ if (f->state & QStyle::State_Sunken) {
+ frameShadow = QFrame::Sunken;
+ } else if (f->state & QStyle::State_Raised) {
+ frameShadow = QFrame::Raised;
+ }
+
+ int lw = f->lineWidth;
+ int mlw = f->midLineWidth;
+ QPalette::ColorRole foregroundRole = QPalette::WindowText;
+ if (widget)
+ foregroundRole = widget->foregroundRole();
+
+ switch (frameShape) {
+ case QFrame::Box:
+ if (frameShadow == QFrame::Plain) {
+ qDrawPlainRect(p, f->rect, f->palette.color(foregroundRole), lw);
+ } else {
+ qDrawShadeRect(p, f->rect, f->palette, frameShadow == QFrame::Sunken, lw, mlw);
+ }
+ break;
+ case QFrame::StyledPanel:
+ //keep the compatibility with Qt 4.4 if there is a proxy style.
+ //be sure to call drawPrimitive(QStyle::PE_Frame) on the proxy style
+ if (widget) {
+ widget->style()->drawPrimitive(QStyle::PE_Frame, opt, p, widget);
+ } else {
+ drawPrimitive(QStyle::PE_Frame, opt, p, widget);
+ }
+ break;
+ case QFrame::Panel:
+ if (frameShadow == QFrame::Plain) {
+ qDrawPlainRect(p, f->rect, f->palette.color(foregroundRole), lw);
+ } else {
+ qDrawShadePanel(p, f->rect, f->palette, frameShadow == QFrame::Sunken, lw);
+ }
+ break;
+ case QFrame::WinPanel:
+ if (frameShadow == QFrame::Plain) {
+ qDrawPlainRect(p, f->rect, f->palette.color(foregroundRole), lw);
+ } else {
+ qDrawWinPanel(p, f->rect, f->palette, frameShadow == QFrame::Sunken);
+ }
+ break;
+ case QFrame::HLine:
+ case QFrame::VLine: {
+ QPoint p1, p2;
+ if (frameShape == QFrame::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 == QFrame::Plain) {
+ QPen oldPen = p->pen();
+ p->setPen(QPen(opt->palette.brush(foregroundRole), lw));
+ p->drawLine(p1, p2);
+ p->setPen(oldPen);
+ } else {
+ qDrawShadeLine(p, p1, p2, f->palette, frameShadow == QFrame::Sunken, lw, mlw);
+ }
+ break;
+ }
+ }
+ }
+ break;
+#endif
+ default:
+ break;
+ }
+}
+
+/*!
+ \reimp
+*/
+QRect QCommonStyle::subElementRect(SubElement sr, const QStyleOption *opt,
+ const QWidget *widget) const
+{
+ Q_D(const QCommonStyle);
+ QRect r;
+ switch (sr) {
+ case SE_PushButtonContents:
+ if (const QStyleOptionButton *btn = qstyleoption_cast<const QStyleOptionButton *>(opt)) {
+ int dx1, dx2;
+ dx1 = pixelMetric(PM_DefaultFrameWidth, btn, widget);
+ if (btn->features & QStyleOptionButton::AutoDefaultButton)
+ dx1 += pixelMetric(PM_ButtonDefaultIndicator, btn, widget);
+ dx2 = dx1 * 2;
+ r.setRect(opt->rect.x() + dx1, opt->rect.y() + dx1, opt->rect.width() - dx2,
+ opt->rect.height() - dx2);
+ r = visualRect(opt->direction, opt->rect, r);
+ }
+ break;
+ case SE_PushButtonFocusRect:
+ if (const QStyleOptionButton *btn = qstyleoption_cast<const QStyleOptionButton *>(opt)) {
+ int dbw1 = 0, dbw2 = 0;
+ if (btn->features & QStyleOptionButton::AutoDefaultButton){
+ dbw1 = pixelMetric(PM_ButtonDefaultIndicator, btn, widget);
+ dbw2 = dbw1 * 2;
+ }
+
+ int dfw1 = pixelMetric(PM_DefaultFrameWidth, btn, widget) + 1,
+ dfw2 = dfw1 * 2;
+
+ r.setRect(btn->rect.x() + dfw1 + dbw1, btn->rect.y() + dfw1 + dbw1,
+ btn->rect.width() - dfw2 - dbw2, btn->rect.height()- dfw2 - dbw2);
+ r = visualRect(opt->direction, opt->rect, r);
+ }
+ break;
+ case SE_CheckBoxIndicator:
+ {
+ int h = pixelMetric(PM_IndicatorHeight, opt, widget);
+ r.setRect(opt->rect.x(), opt->rect.y() + ((opt->rect.height() - h) / 2),
+ pixelMetric(PM_IndicatorWidth, opt, widget), h);
+ r = visualRect(opt->direction, opt->rect, r);
+ }
+ break;
+
+ case SE_CheckBoxContents:
+ {
+ // Deal with the logical first, then convert it back to screen coords.
+ QRect ir = visualRect(opt->direction, opt->rect,
+ subElementRect(SE_CheckBoxIndicator, opt, widget));
+ int spacing = pixelMetric(PM_CheckBoxLabelSpacing, opt, widget);
+ r.setRect(ir.right() + spacing, opt->rect.y(), opt->rect.width() - ir.width() - spacing,
+ opt->rect.height());
+ r = visualRect(opt->direction, opt->rect, r);
+ }
+ break;
+
+ case SE_CheckBoxFocusRect:
+ if (const QStyleOptionButton *btn = qstyleoption_cast<const QStyleOptionButton *>(opt)) {
+ if (btn->icon.isNull() && btn->text.isEmpty()) {
+ r = subElementRect(SE_CheckBoxIndicator, opt, widget);
+ r.adjust(1, 1, -1, -1);
+ break;
+ }
+ // As above, deal with the logical first, then convert it back to screen coords.
+ QRect cr = visualRect(btn->direction, btn->rect,
+ subElementRect(SE_CheckBoxContents, btn, widget));
+
+ QRect iconRect, textRect;
+ if (!btn->text.isEmpty()) {
+ textRect = itemTextRect(opt->fontMetrics, cr, Qt::AlignAbsolute | Qt::AlignLeft
+ | Qt::AlignVCenter | Qt::TextShowMnemonic,
+ btn->state & State_Enabled, btn->text);
+ }
+ if (!btn->icon.isNull()) {
+ iconRect = itemPixmapRect(cr, Qt::AlignAbsolute | Qt::AlignLeft | Qt::AlignVCenter
+ | Qt::TextShowMnemonic,
+ btn->icon.pixmap(btn->iconSize, QIcon::Normal));
+ if (!textRect.isEmpty())
+ textRect.translate(iconRect.right() + 4, 0);
+ }
+ r = iconRect | textRect;
+ r.adjust(-3, -2, 3, 2);
+ r = r.intersected(btn->rect);
+ r = visualRect(btn->direction, btn->rect, r);
+ }
+ break;
+
+ case SE_RadioButtonIndicator:
+ {
+ int h = pixelMetric(PM_ExclusiveIndicatorHeight, opt, widget);
+ r.setRect(opt->rect.x(), opt->rect.y() + ((opt->rect.height() - h) / 2),
+ pixelMetric(PM_ExclusiveIndicatorWidth, opt, widget), h);
+ r = visualRect(opt->direction, opt->rect, r);
+ }
+ break;
+
+ case SE_RadioButtonContents:
+ {
+ QRect ir = visualRect(opt->direction, opt->rect,
+ subElementRect(SE_RadioButtonIndicator, opt, widget));
+ int spacing = pixelMetric(PM_RadioButtonLabelSpacing, opt, widget);
+ r.setRect(ir.left() + ir.width() + spacing, opt->rect.y(), opt->rect.width() - ir.width() - spacing,
+ opt->rect.height());
+ r = visualRect(opt->direction, opt->rect, r);
+ break;
+ }
+
+ case SE_RadioButtonFocusRect:
+ if (const QStyleOptionButton *btn = qstyleoption_cast<const QStyleOptionButton *>(opt)) {
+ if (btn->icon.isNull() && btn->text.isEmpty()) {
+ r = subElementRect(SE_RadioButtonIndicator, opt, widget);
+ r.adjust(1, 1, -1, -1);
+ break;
+ }
+ QRect cr = visualRect(btn->direction, btn->rect,
+ subElementRect(SE_RadioButtonContents, opt, widget));
+
+ QRect iconRect, textRect;
+ if (!btn->text.isEmpty()){
+ textRect = itemTextRect(opt->fontMetrics, cr, Qt::AlignAbsolute | Qt::AlignLeft | Qt::AlignVCenter
+ | Qt::TextShowMnemonic, btn->state & State_Enabled, btn->text);
+ }
+ if (!btn->icon.isNull()) {
+ iconRect = itemPixmapRect(cr, Qt::AlignAbsolute | Qt::AlignLeft | Qt::AlignVCenter | Qt::TextShowMnemonic,
+ btn->icon.pixmap(btn->iconSize, QIcon::Normal));
+ if (!textRect.isEmpty())
+ textRect.translate(iconRect.right() + 4, 0);
+ }
+ r = iconRect | textRect;
+ r.adjust(-3, -2, 3, 2);
+ r = r.intersected(btn->rect);
+ r = visualRect(btn->direction, btn->rect, r);
+ }
+ break;
+#ifndef QT_NO_SLIDER
+ case SE_SliderFocusRect:
+ if (const QStyleOptionSlider *slider = qstyleoption_cast<const QStyleOptionSlider *>(opt)) {
+ int tickOffset = pixelMetric(PM_SliderTickmarkOffset, slider, widget);
+ int thickness = pixelMetric(PM_SliderControlThickness, slider, widget);
+ if (slider->orientation == Qt::Horizontal)
+ r.setRect(0, tickOffset - 1, slider->rect.width(), thickness + 2);
+ else
+ r.setRect(tickOffset - 1, 0, thickness + 2, slider->rect.height());
+ r = r.intersected(slider->rect);
+ r = visualRect(opt->direction, opt->rect, r);
+ }
+ break;
+#endif // QT_NO_SLIDER
+#ifndef QT_NO_PROGRESSBAR
+ case SE_ProgressBarGroove:
+ case SE_ProgressBarContents:
+ case SE_ProgressBarLabel:
+ if (const QStyleOptionProgressBar *pb = qstyleoption_cast<const QStyleOptionProgressBar *>(opt)) {
+ int textw = 0;
+ bool vertical = false;
+ if (const QStyleOptionProgressBarV2 *pb2 = qstyleoption_cast<const QStyleOptionProgressBarV2 *>(opt)) {
+ vertical = (pb2->orientation == Qt::Vertical);
+ }
+ if (!vertical) {
+ if (pb->textVisible)
+ textw = qMax(pb->fontMetrics.width(pb->text), pb->fontMetrics.width(QLatin1String("100%"))) + 6;
+ }
+
+ if ((pb->textAlignment & Qt::AlignCenter) == 0) {
+ if (sr != SE_ProgressBarLabel)
+ r.setCoords(pb->rect.left(), pb->rect.top(),
+ pb->rect.right() - textw, pb->rect.bottom());
+ else
+ r.setCoords(pb->rect.right() - textw, pb->rect.top(),
+ pb->rect.right(), pb->rect.bottom());
+ } else {
+ r = pb->rect;
+ }
+ r = visualRect(pb->direction, pb->rect, r);
+ }
+ break;
+#endif // QT_NO_PROGRESSBAR
+#ifdef QT3_SUPPORT
+ case SE_Q3DockWindowHandleRect:
+ if (const QStyleOptionQ3DockWindow *dw = qstyleoption_cast<const QStyleOptionQ3DockWindow *>(opt)) {
+ if (!dw->docked || !dw->closeEnabled)
+ r.setRect(0, 0, dw->rect.width(), dw->rect.height());
+ else {
+ if (dw->state & State_Horizontal)
+ r.setRect(0, 15, dw->rect.width(), dw->rect.height() - 15);
+ else
+ r.setRect(0, 1, dw->rect.width() - 15, dw->rect.height() - 1);
+ }
+ r = visualRect(opt->direction, opt->rect, r);
+ }
+ break;
+#endif // QT3_SUPPORT
+#ifndef QT_NO_COMBOBOX
+ case SE_ComboBoxFocusRect:
+ if (const QStyleOptionComboBox *cb = qstyleoption_cast<const QStyleOptionComboBox *>(opt)) {
+ int margin = cb->frame ? 3 : 0;
+ r.setRect(opt->rect.left() + margin, opt->rect.top() + margin,
+ opt->rect.width() - 2*margin - 16, opt->rect.height() - 2*margin);
+ r = visualRect(opt->direction, opt->rect, r);
+ }
+ break;
+#endif // QT_NO_COMBOBOX
+#ifndef QT_NO_TOOLBOX
+ case SE_ToolBoxTabContents:
+ r = opt->rect;
+ r.adjust(0, 0, -30, 0);
+ break;
+#endif // QT_NO_TOOLBOX
+ case SE_HeaderLabel: {
+ int margin = pixelMetric(QStyle::PM_HeaderMargin, opt, widget);
+ r.setRect(opt->rect.x() + margin, opt->rect.y() + margin,
+ opt->rect.width() - margin * 2, opt->rect.height() - margin * 2);
+
+ if (const QStyleOptionHeader *header = qstyleoption_cast<const QStyleOptionHeader *>(opt)) {
+ // Subtract width needed for arrow, if there is one
+ if (header->sortIndicator != QStyleOptionHeader::None) {
+ if (opt->state & State_Horizontal)
+ r.setWidth(r.width() - (opt->rect.height() / 2) - (margin * 2));
+ else
+ r.setHeight(r.height() - (opt->rect.width() / 2) - (margin * 2));
+ }
+ }
+ r = visualRect(opt->direction, opt->rect, r);
+ break; }
+ case SE_HeaderArrow: {
+ int h = opt->rect.height();
+ int w = opt->rect.width();
+ int x = opt->rect.x();
+ int y = opt->rect.y();
+ int margin = pixelMetric(QStyle::PM_HeaderMargin, opt, widget);
+
+ if (opt->state & State_Horizontal) {
+ int horiz_size = h / 2;
+ r.setRect(x + w - margin * 2 - horiz_size, y + 5,
+ horiz_size, h - margin * 2 - 5);
+ } else {
+ int vert_size = w / 2;
+ r.setRect(x + 5, y + h - margin * 2 - vert_size,
+ w - margin * 2 - 5, vert_size);
+ }
+ r = visualRect(opt->direction, opt->rect, r);
+ break; }
+
+ case SE_RadioButtonClickRect:
+ r = subElementRect(SE_RadioButtonFocusRect, opt, widget);
+ r |= subElementRect(SE_RadioButtonIndicator, opt, widget);
+ break;
+ case SE_CheckBoxClickRect:
+ r = subElementRect(SE_CheckBoxFocusRect, opt, widget);
+ r |= subElementRect(SE_CheckBoxIndicator, opt, widget);
+ break;
+#ifndef QT_NO_TABWIDGET
+ case SE_TabWidgetTabBar:
+ if (const QStyleOptionTabWidgetFrame *twf
+ = qstyleoption_cast<const QStyleOptionTabWidgetFrame *>(opt)) {
+ r.setSize(twf->tabBarSize);
+ const uint alingMask = Qt::AlignLeft | Qt::AlignRight | Qt::AlignHCenter;
+ switch (twf->shape) {
+ case QTabBar::RoundedNorth:
+ case QTabBar::TriangularNorth:
+ // Constrain the size now, otherwise, center could get off the page
+ // This of course repeated for all the other directions
+ r.setWidth(qMin(r.width(), twf->rect.width()
+ - twf->leftCornerWidgetSize.width()
+ - twf->rightCornerWidgetSize.width()));
+ switch (styleHint(SH_TabBar_Alignment, twf, widget) & alingMask) {
+ default:
+ case Qt::AlignLeft:
+ r.moveTopLeft(QPoint(twf->leftCornerWidgetSize.width(), 0));
+ break;
+ case Qt::AlignHCenter:
+ r.moveTopLeft(QPoint(twf->rect.center().x() - qRound(r.width() / 2.0f)
+ + (twf->leftCornerWidgetSize.width() / 2)
+ - (twf->rightCornerWidgetSize.width() / 2), 0));
+ break;
+ case Qt::AlignRight:
+ r.moveTopLeft(QPoint(twf->rect.width() - twf->tabBarSize.width()
+ - twf->rightCornerWidgetSize.width(), 0));
+ break;
+ }
+ r = visualRect(twf->direction, twf->rect, r);
+ break;
+ case QTabBar::RoundedSouth:
+ case QTabBar::TriangularSouth:
+ r.setWidth(qMin(r.width(), twf->rect.width()
+ - twf->leftCornerWidgetSize.width()
+ - twf->rightCornerWidgetSize.width()));
+ switch (styleHint(SH_TabBar_Alignment, twf, widget) & alingMask) {
+ default:
+ case Qt::AlignLeft:
+ r.moveTopLeft(QPoint(twf->leftCornerWidgetSize.width(),
+ twf->rect.height() - twf->tabBarSize.height()));
+ break;
+ case Qt::AlignHCenter:
+ r.moveTopLeft(QPoint(twf->rect.center().x() - qRound(r.width() / 2.0f)
+ + (twf->leftCornerWidgetSize.width() / 2)
+ - (twf->rightCornerWidgetSize.width() / 2),
+ twf->rect.height() - twf->tabBarSize.height()));
+ break;
+ case Qt::AlignRight:
+ r.moveTopLeft(QPoint(twf->rect.width() - twf->tabBarSize.width()
+ - twf->rightCornerWidgetSize.width(),
+ twf->rect.height() - twf->tabBarSize.height()));
+ break;
+ }
+ r = visualRect(twf->direction, twf->rect, r);
+ break;
+ case QTabBar::RoundedEast:
+ case QTabBar::TriangularEast:
+ r.setHeight(qMin(r.height(), twf->rect.height()
+ - twf->leftCornerWidgetSize.height()
+ - twf->rightCornerWidgetSize.height()));
+ switch (styleHint(SH_TabBar_Alignment, twf, widget) & alingMask) {
+ default:
+ case Qt::AlignLeft:
+ r.moveTopLeft(QPoint(twf->rect.width() - twf->tabBarSize.width(),
+ twf->leftCornerWidgetSize.height()));
+ break;
+ case Qt::AlignHCenter:
+ r.moveTopLeft(QPoint(twf->rect.width() - twf->tabBarSize.width(),
+ twf->rect.center().y() - r.height() / 2));
+ break;
+ case Qt::AlignRight:
+ r.moveTopLeft(QPoint(twf->rect.width() - twf->tabBarSize.width(),
+ twf->rect.height() - twf->tabBarSize.height()
+ - twf->rightCornerWidgetSize.height()));
+ break;
+ }
+ break;
+ case QTabBar::RoundedWest:
+ case QTabBar::TriangularWest:
+ r.setHeight(qMin(r.height(), twf->rect.height()
+ - twf->leftCornerWidgetSize.height()
+ - twf->rightCornerWidgetSize.height()));
+ switch (styleHint(SH_TabBar_Alignment, twf, widget) & alingMask) {
+ default:
+ case Qt::AlignLeft:
+ r.moveTopLeft(QPoint(0, twf->leftCornerWidgetSize.height()));
+ break;
+ case Qt::AlignHCenter:
+ r.moveTopLeft(QPoint(0, twf->rect.center().y() - r.height() / 2));
+ break;
+ case Qt::AlignRight:
+ r.moveTopLeft(QPoint(0, twf->rect.height() - twf->tabBarSize.height()
+ - twf->rightCornerWidgetSize.height()));
+ break;
+ }
+ break;
+ }
+ }
+ break;
+ case SE_TabWidgetTabPane:
+ case SE_TabWidgetTabContents:
+ if (const QStyleOptionTabWidgetFrame *twf = qstyleoption_cast<const QStyleOptionTabWidgetFrame *>(opt)) {
+ QStyleOptionTab tabopt;
+ tabopt.shape = twf->shape;
+ int overlap = pixelMetric(PM_TabBarBaseOverlap, &tabopt, widget);
+ if (twf->lineWidth == 0)
+ overlap = 0;
+ switch (twf->shape) {
+ case QTabBar::RoundedNorth:
+ case QTabBar::TriangularNorth:
+ r = QRect(QPoint(0,qMax(twf->tabBarSize.height() - overlap, 0)),
+ QSize(twf->rect.width(), qMin(twf->rect.height() - twf->tabBarSize.height() + overlap, twf->rect.height())));
+ break;
+ case QTabBar::RoundedSouth:
+ case QTabBar::TriangularSouth:
+ r = QRect(QPoint(0,0), QSize(twf->rect.width(), qMin(twf->rect.height() - twf->tabBarSize.height() + overlap, twf->rect.height())));
+ break;
+ case QTabBar::RoundedEast:
+ case QTabBar::TriangularEast:
+ r = QRect(QPoint(0, 0), QSize(qMin(twf->rect.width() - twf->tabBarSize.width() + overlap, twf->rect.width()), twf->rect.height()));
+ break;
+ case QTabBar::RoundedWest:
+ case QTabBar::TriangularWest:
+ r = QRect(QPoint(qMax(twf->tabBarSize.width() - overlap, 0), 0),
+ QSize(qMin(twf->rect.width() - twf->tabBarSize.width() + overlap, twf->rect.width()), twf->rect.height()));
+ break;
+ }
+ if (sr == SE_TabWidgetTabContents && twf->lineWidth > 0)
+ r.adjust(2, 2, -2, -2);
+ }
+ break;
+ case SE_TabWidgetLeftCorner:
+ if (const QStyleOptionTabWidgetFrame *twf = qstyleoption_cast<const QStyleOptionTabWidgetFrame *>(opt)) {
+ QRect paneRect = subElementRect(SE_TabWidgetTabPane, twf, widget);
+ switch (twf->shape) {
+ case QTabBar::RoundedNorth:
+ case QTabBar::TriangularNorth:
+ r = QRect(QPoint(paneRect.x(), paneRect.y() - twf->leftCornerWidgetSize.height()),
+ twf->leftCornerWidgetSize);
+ break;
+ case QTabBar::RoundedSouth:
+ case QTabBar::TriangularSouth:
+ r = QRect(QPoint(paneRect.x(), paneRect.height()), twf->leftCornerWidgetSize);
+ break;
+ default:
+ break;
+ }
+ r = visualRect(twf->direction, twf->rect, r);
+ }
+ break;
+ case SE_TabWidgetRightCorner:
+ if (const QStyleOptionTabWidgetFrame *twf = qstyleoption_cast<const QStyleOptionTabWidgetFrame *>(opt)) {
+ QRect paneRect = subElementRect(SE_TabWidgetTabPane, twf, widget);
+ switch (twf->shape) {
+ case QTabBar::RoundedNorth:
+ case QTabBar::TriangularNorth:
+ r = QRect(QPoint(paneRect.width() - twf->rightCornerWidgetSize.width(),
+ paneRect.y() - twf->rightCornerWidgetSize.height()),
+ twf->rightCornerWidgetSize);
+ break;
+ case QTabBar::RoundedSouth:
+ case QTabBar::TriangularSouth:
+ r = QRect(QPoint(paneRect.width() - twf->rightCornerWidgetSize.width(),
+ paneRect.height()), twf->rightCornerWidgetSize);
+ break;
+ default:
+ break;
+ }
+ r = visualRect(twf->direction, twf->rect, r);
+ }
+ break;
+ case SE_TabBarTabText:
+ // ### consider merging this with CE_TabBarTabLabel
+ if (const QStyleOptionTab *tab = qstyleoption_cast<const QStyleOptionTab *>(opt)) {
+ QStyleOptionTabV3 tabV2(*tab);
+ QRect tr = tabV2.rect;
+ bool verticalTabs = tabV2.shape == QTabBar::RoundedEast
+ || tabV2.shape == QTabBar::RoundedWest
+ || tabV2.shape == QTabBar::TriangularEast
+ || tabV2.shape == QTabBar::TriangularWest;
+ if (verticalTabs)
+ tr.setRect(0, 0, tr.height(), tr.width());
+ int verticalShift = pixelMetric(QStyle::PM_TabBarTabShiftVertical, tab, widget);
+ int horizontalShift = pixelMetric(QStyle::PM_TabBarTabShiftHorizontal, tab, widget);
+ if (tabV2.shape == QTabBar::RoundedSouth || tabV2.shape == QTabBar::TriangularSouth)
+ verticalShift = -verticalShift;
+ tr.adjust(0, 0, horizontalShift, verticalShift);
+ bool selected = tabV2.state & State_Selected;
+ if (selected) {
+ tr.setBottom(tr.bottom() - verticalShift);
+ tr.setRight(tr.right() - horizontalShift);
+ }
+
+ // left widget
+ if (!tabV2.leftButtonSize.isEmpty()) {
+ tr.setLeft(tr.left() + 6 + 2 +
+ (verticalTabs ? tabV2.leftButtonSize.height() : tabV2.leftButtonSize.width()));
+ }
+
+ // icon
+ if (!tabV2.icon.isNull()) {
+ QSize iconSize = tabV2.iconSize;
+ if (!iconSize.isValid()) {
+ int iconExtent = pixelMetric(PM_SmallIconSize);
+ iconSize = QSize(iconExtent, iconExtent);
+ }
+ QSize tabIconSize = tabV2.icon.actualSize(iconSize,
+ (tabV2.state & State_Enabled) ? QIcon::Normal
+ : QIcon::Disabled);
+ int offset = 4;
+ if (tabV2.leftButtonSize.isEmpty())
+ offset += 2;
+
+ QRect iconRect = QRect(tr.left() + offset, tr.center().y() - tabIconSize.height() / 2,
+ tabIconSize.width(), tabIconSize .height());
+ if (!verticalTabs)
+ iconRect = visualRect(opt->direction, opt->rect, iconRect);
+ tr.setLeft(tr.left() + tabIconSize.width() + offset + 2);
+ }
+
+ // right widget
+ if (!tabV2.rightButtonSize.isEmpty()) {
+ tr.setRight(tr.right() - 6 - 2 -
+ (verticalTabs ? tabV2.rightButtonSize.height() : tabV2.rightButtonSize.width()));
+ }
+
+ if (!verticalTabs)
+ tr = visualRect(opt->direction, opt->rect, tr);
+ r = tr;
+ }
+ break;
+ case SE_TabBarTabLeftButton:
+ case SE_TabBarTabRightButton:
+ if (const QStyleOptionTabV3 *tab = qstyleoption_cast<const QStyleOptionTabV3 *>(opt)) {
+ bool selected = tab->state & State_Selected;
+ int verticalShift = pixelMetric(QStyle::PM_TabBarTabShiftVertical, tab, widget);
+ int horizontalShift = pixelMetric(QStyle::PM_TabBarTabShiftHorizontal, tab, widget);
+
+ bool verticalTabs = tab->shape == QTabBar::RoundedEast
+ || tab->shape == QTabBar::RoundedWest
+ || tab->shape == QTabBar::TriangularEast
+ || tab->shape == QTabBar::TriangularWest;
+
+ QRect tr = tab->rect;
+ if (tab->shape == QTabBar::RoundedSouth || tab->shape == QTabBar::TriangularSouth)
+ verticalShift = -verticalShift;
+ if (verticalTabs) {
+ qSwap(horizontalShift, verticalShift);
+ horizontalShift *= -1;
+ verticalShift *= -1;
+ }
+ tr.adjust(0, 0, horizontalShift, verticalShift);
+ if (selected)
+ {
+ tr.setBottom(tr.bottom() - verticalShift);
+ tr.setRight(tr.right() - horizontalShift);
+ }
+
+ QSize size = (sr == SE_TabBarTabLeftButton) ? tab->leftButtonSize : tab->rightButtonSize;
+ int w = size.width();
+ int h = size.height();
+ int midHeight = static_cast<int>(ceil(float(tr.height() - h) / 2));
+ int midWidth = ((tr.width() - w) / 2);
+
+ bool atTheTop = true;
+ switch (tab->shape) {
+ case QTabBar::RoundedWest:
+ case QTabBar::TriangularWest:
+ atTheTop = (sr == SE_TabBarTabLeftButton);
+ break;
+ case QTabBar::RoundedEast:
+ case QTabBar::TriangularEast:
+ atTheTop = (sr == SE_TabBarTabRightButton);
+ break;
+ default:
+ if (sr == SE_TabBarTabLeftButton)
+ r = QRect(6 + tab->rect.x(), midHeight, w, h);
+ else
+ r = QRect(tab->rect.right() - 6 - w, midHeight, w, h);
+ r = visualRect(tab->direction, tab->rect, r);
+ }
+ if (verticalTabs) {
+ if (atTheTop)
+ r = QRect(midWidth, tr.y() + tab->rect.height() - 6 - h, w, h);
+ else
+ r = QRect(midWidth, tr.y() + 6, w, h);
+ }
+ }
+
+ break;
+#endif // QT_NO_TABWIDGET
+#ifndef QT_NO_TABBAR
+ case SE_TabBarTearIndicator:
+ if (const QStyleOptionTab *tab = qstyleoption_cast<const QStyleOptionTab *>(opt)) {
+ switch (tab->shape) {
+ case QTabBar::RoundedNorth:
+ case QTabBar::TriangularNorth:
+ case QTabBar::RoundedSouth:
+ case QTabBar::TriangularSouth:
+ r.setRect(tab->rect.left(), tab->rect.top(), 4, opt->rect.height());
+ break;
+ case QTabBar::RoundedWest:
+ case QTabBar::TriangularWest:
+ case QTabBar::RoundedEast:
+ case QTabBar::TriangularEast:
+ r.setRect(tab->rect.left(), tab->rect.top(), opt->rect.width(), 4);
+ break;
+ default:
+ break;
+ }
+ r = visualRect(opt->direction, opt->rect, r);
+ }
+ break;
+#endif
+ case SE_TreeViewDisclosureItem:
+ r = opt->rect;
+ break;
+ case SE_LineEditContents:
+ if (const QStyleOptionFrame *f = qstyleoption_cast<const QStyleOptionFrame *>(opt)) {
+ r = f->rect.adjusted(f->lineWidth, f->lineWidth, -f->lineWidth, -f->lineWidth);
+ r = visualRect(opt->direction, opt->rect, r);
+ }
+ break;
+ case SE_FrameContents:
+ if (const QStyleOptionFrameV2 *f = qstyleoption_cast<const QStyleOptionFrameV2 *>(opt)) {
+ int fw = pixelMetric(PM_DefaultFrameWidth, f, widget);
+ r = opt->rect.adjusted(fw, fw, -fw, -fw);
+ r = visualRect(opt->direction, opt->rect, r);
+ }
+ break;
+ case SE_ShapedFrameContents:
+ if (const QStyleOptionFrameV3 *f = qstyleoption_cast<const QStyleOptionFrameV3 *>(opt)) {
+ int frameShape = f->frameShape;
+ int frameShadow = QFrame::Plain;
+ if (f->state & QStyle::State_Sunken) {
+ frameShadow = QFrame::Sunken;
+ } else if (f->state & QStyle::State_Raised) {
+ frameShadow = QFrame::Raised;
+ }
+
+ int frameWidth = 0;
+
+ switch (frameShape) {
+ case QFrame::NoFrame:
+ frameWidth = 0;
+ break;
+
+ case QFrame::Box:
+ case QFrame::HLine:
+ case QFrame::VLine:
+ switch (frameShadow) {
+ case QFrame::Plain:
+ frameWidth = f->lineWidth;
+ break;
+ case QFrame::Raised:
+ case QFrame::Sunken:
+ frameWidth = (short)(f->lineWidth*2 + f->midLineWidth);
+ break;
+ }
+ break;
+
+ case QFrame::StyledPanel:
+ //keep the compatibility with Qt 4.4 if there is a proxy style.
+ //be sure to call drawPrimitive(QStyle::SE_FrameContents) on the proxy style
+ if (widget)
+ return widget->style()->subElementRect(QStyle::SE_FrameContents, opt, widget);
+ else
+ return subElementRect(QStyle::SE_FrameContents, opt, widget);
+ break;
+
+ case QFrame::WinPanel:
+ frameWidth = 2;
+ break;
+
+ case QFrame::Panel:
+ switch (frameShadow) {
+ case QFrame::Plain:
+ case QFrame::Raised:
+ case QFrame::Sunken:
+ frameWidth = f->lineWidth;
+ break;
+ }
+ break;
+ }
+ r = f->rect.adjusted(frameWidth, frameWidth, -frameWidth, -frameWidth);
+ }
+ break;
+#ifndef QT_NO_DOCKWIDGET
+ case SE_DockWidgetCloseButton:
+ case SE_DockWidgetFloatButton:
+ case SE_DockWidgetTitleBarText:
+ case SE_DockWidgetIcon: {
+ int iconSize = pixelMetric(PM_SmallIconSize, opt, widget);
+ int buttonMargin = pixelMetric(PM_DockWidgetTitleBarButtonMargin, opt, widget);
+ int margin = pixelMetric(QStyle::PM_DockWidgetTitleMargin, opt, widget);
+ QRect rect = opt->rect;
+
+ const QStyleOptionDockWidget *dwOpt
+ = qstyleoption_cast<const QStyleOptionDockWidget*>(opt);
+ bool canClose = dwOpt == 0 ? true : dwOpt->closable;
+ bool canFloat = dwOpt == 0 ? false : dwOpt->floatable;
+ const QStyleOptionDockWidgetV2 *v2
+ = qstyleoption_cast<const QStyleOptionDockWidgetV2*>(opt);
+ bool verticalTitleBar = v2 == 0 ? false : v2->verticalTitleBar;
+
+ // If this is a vertical titlebar, we transpose and work as if it was
+ // horizontal, then transpose again.
+
+ if (verticalTitleBar) {
+ QSize size = rect.size();
+ size.transpose();
+ rect.setSize(size);
+ }
+
+ do {
+
+ int right = rect.right();
+ int left = rect.left();
+
+ QRect closeRect;
+ if (canClose) {
+ QSize sz = standardIcon(QStyle::SP_TitleBarCloseButton,
+ opt, widget).actualSize(QSize(iconSize, iconSize));
+ sz += QSize(buttonMargin, buttonMargin);
+ if (verticalTitleBar)
+ sz.transpose();
+ closeRect = QRect(right - sz.width(),
+ rect.center().y() - sz.height()/2,
+ sz.width(), sz.height());
+ right = closeRect.left() - 1;
+ }
+ if (sr == SE_DockWidgetCloseButton) {
+ r = closeRect;
+ break;
+ }
+
+ QRect floatRect;
+ if (canFloat) {
+ QSize sz = standardIcon(QStyle::SP_TitleBarNormalButton,
+ opt, widget).actualSize(QSize(iconSize, iconSize));
+ sz += QSize(buttonMargin, buttonMargin);
+ if (verticalTitleBar)
+ sz.transpose();
+ floatRect = QRect(right - sz.width(),
+ rect.center().y() - sz.height()/2,
+ sz.width(), sz.height());
+ right = floatRect.left() - 1;
+ }
+ if (sr == SE_DockWidgetFloatButton) {
+ r = floatRect;
+ break;
+ }
+
+ QRect iconRect;
+ if (const QDockWidget *dw = qobject_cast<const QDockWidget*>(widget)) {
+ QIcon icon;
+ if (dw->isFloating())
+ icon = dw->windowIcon();
+ if (!icon.isNull()
+ && icon.cacheKey() != qApp->windowIcon().cacheKey()) {
+ QSize sz = icon.actualSize(QSize(r.height(), r.height()));
+ if (verticalTitleBar)
+ sz.transpose();
+ iconRect = QRect(left, rect.center().y() - sz.height()/2,
+ sz.width(), sz.height());
+ left = iconRect.right() + margin;
+ }
+ }
+ if (sr == SE_DockWidgetIcon) {
+ r = iconRect;
+ break;
+ }
+
+ QRect textRect = QRect(left, rect.top(),
+ right - left, rect.height());
+ if (sr == SE_DockWidgetTitleBarText) {
+ r = textRect;
+ break;
+ }
+
+ } while (false);
+
+ if (verticalTitleBar) {
+ r = QRect(rect.left() + r.top() - rect.top(),
+ rect.top() + rect.right() - r.right(),
+ r.height(), r.width());
+ } else {
+ r = visualRect(opt->direction, rect, r);
+ }
+ break;
+ }
+#endif
+#ifndef QT_NO_ITEMVIEWS
+ case SE_ItemViewItemCheckIndicator:
+ if (!qstyleoption_cast<const QStyleOptionViewItemV4 *>(opt)) {
+ r = subElementRect(SE_CheckBoxIndicator, opt, widget);
+ break;
+ }
+ case SE_ItemViewItemDecoration:
+ case SE_ItemViewItemText:
+ case SE_ItemViewItemFocusRect:
+ if (const QStyleOptionViewItemV4 *vopt = qstyleoption_cast<const QStyleOptionViewItemV4 *>(opt)) {
+ if (!d->isViewItemCached(*vopt)) {
+ d->viewItemLayout(vopt, &d->checkRect, &d->decorationRect, &d->displayRect, false);
+ if (d->cachedOption)
+ delete d->cachedOption;
+ d->cachedOption = new QStyleOptionViewItemV4(*vopt);
+ }
+ if (sr == SE_ViewItemCheckIndicator)
+ r = d->checkRect;
+ else if (sr == SE_ItemViewItemDecoration)
+ r = d->decorationRect;
+ else if (sr == SE_ItemViewItemText || sr == SE_ItemViewItemFocusRect)
+ r = d->displayRect;
+ }
+ break;
+#endif //QT_NO_ITEMVIEWS
+ default:
+ break;
+ }
+ return r;
+}
+
+#ifndef QT_NO_DIAL
+static qreal angle(const QPointF &p1, const QPointF &p2)
+{
+ static const qreal rad_factor = 180 / Q_PI;
+ qreal _angle = 0;
+
+ if (p1.x() == p2.x()) {
+ if (p1.y() < p2.y())
+ _angle = 270;
+ else
+ _angle = 90;
+ } else {
+ qreal x1, x2, y1, y2;
+
+ if (p1.x() <= p2.x()) {
+ x1 = p1.x(); y1 = p1.y();
+ x2 = p2.x(); y2 = p2.y();
+ } else {
+ x2 = p1.x(); y2 = p1.y();
+ x1 = p2.x(); y1 = p2.y();
+ }
+
+ qreal m = -(y2 - y1) / (x2 - x1);
+ _angle = atan(m) * rad_factor;
+
+ if (p1.x() < p2.x())
+ _angle = 180 - _angle;
+ else
+ _angle = -_angle;
+ }
+ return _angle;
+}
+
+static int calcBigLineSize(int radius)
+{
+ int bigLineSize = radius / 6;
+ if (bigLineSize < 4)
+ bigLineSize = 4;
+ if (bigLineSize > radius / 2)
+ bigLineSize = radius / 2;
+ return bigLineSize;
+}
+
+static QPolygonF calcArrow(const QStyleOptionSlider *dial, qreal &a)
+{
+ int width = dial->rect.width();
+ int height = dial->rect.height();
+ int r = qMin(width, height) / 2;
+ int currentSliderPosition = dial->upsideDown ? dial->sliderPosition : (dial->maximum - dial->sliderPosition);
+
+ if (dial->maximum == dial->minimum)
+ a = Q_PI / 2;
+ else if (dial->dialWrapping)
+ a = Q_PI * 3 / 2 - (currentSliderPosition - dial->minimum) * 2 * Q_PI
+ / (dial->maximum - dial->minimum);
+ else
+ a = (Q_PI * 8 - (currentSliderPosition - dial->minimum) * 10 * Q_PI
+ / (dial->maximum - dial->minimum)) / 6;
+
+ int xc = width / 2;
+ int yc = height / 2;
+
+ int len = r - calcBigLineSize(r) - 5;
+ if (len < 5)
+ len = 5;
+ int back = len / 2;
+
+ QPolygonF arrow(3);
+ arrow[0] = QPointF(0.5 + xc + len * qCos(a),
+ 0.5 + yc - len * qSin(a));
+ arrow[1] = QPointF(0.5 + xc + back * qCos(a + Q_PI * 5 / 6),
+ 0.5 + yc - back * qSin(a + Q_PI * 5 / 6));
+ arrow[2] = QPointF(0.5 + xc + back * qCos(a - Q_PI * 5 / 6),
+ 0.5 + yc - back * qSin(a - Q_PI * 5 / 6));
+ return arrow;
+}
+
+static QPolygonF calcLines(const QStyleOptionSlider *dial, const QWidget *)
+{
+ QPolygonF poly;
+ int width = dial->rect.width();
+ int height = dial->rect.height();
+ qreal r = qMin(width, height) / 2;
+ int bigLineSize = calcBigLineSize(int(r));
+
+ qreal xc = width / 2;
+ qreal yc = height / 2;
+ int ns = dial->tickInterval;
+ int notches = (dial->maximum + ns - 1 - dial->minimum) / ns;
+ if (notches <= 0)
+ return poly;
+ if (dial->maximum < dial->minimum
+ || dial->maximum - dial->minimum > 1000) {
+ int maximum = dial->minimum + 1000;
+ notches = (maximum + ns - 1 - dial->minimum) / ns;
+ }
+
+ poly.resize(2 + 2 * notches);
+ int smallLineSize = bigLineSize / 2;
+ for (int i = 0; i <= notches; ++i) {
+ qreal angle = dial->dialWrapping ? Q_PI * 3 / 2 - i * 2 * Q_PI / notches
+ : (Q_PI * 8 - i * 10 * Q_PI / notches) / 6;
+ qreal s = qSin(angle);
+ qreal c = qCos(angle);
+ if (i == 0 || (((ns * i) % (dial->pageStep ? dial->pageStep : 1)) == 0)) {
+ poly[2 * i] = QPointF(xc + (r - bigLineSize) * c,
+ yc - (r - bigLineSize) * s);
+ poly[2 * i + 1] = QPointF(xc + r * c, yc - r * s);
+ } else {
+ poly[2 * i] = QPointF(xc + (r - 1 - smallLineSize) * c,
+ yc - (r - 1 - smallLineSize) * s);
+ poly[2 * i + 1] = QPointF(xc + (r - 1) * c, yc -(r - 1) * s);
+ }
+ }
+ return poly;
+}
+#endif // QT_NO_DIAL
+
+/*!
+ \reimp
+*/
+void QCommonStyle::drawComplexControl(ComplexControl cc, const QStyleOptionComplex *opt,
+ QPainter *p, const QWidget *widget) const
+{
+ switch (cc) {
+#ifndef QT_NO_SLIDER
+ case CC_Slider:
+ if (const QStyleOptionSlider *slider = qstyleoption_cast<const QStyleOptionSlider *>(opt)) {
+ if (slider->subControls == SC_SliderTickmarks) {
+ int tickOffset = pixelMetric(PM_SliderTickmarkOffset, slider, widget);
+ int ticks = slider->tickPosition;
+ int thickness = pixelMetric(PM_SliderControlThickness, slider, widget);
+ int len = pixelMetric(PM_SliderLength, slider, widget);
+ int available = pixelMetric(PM_SliderSpaceAvailable, slider, widget);
+ int interval = slider->tickInterval;
+ if (interval <= 0) {
+ interval = slider->singleStep;
+ if (QStyle::sliderPositionFromValue(slider->minimum, slider->maximum, interval,
+ available)
+ - QStyle::sliderPositionFromValue(slider->minimum, slider->maximum,
+ 0, available) < 3)
+ interval = slider->pageStep;
+ }
+ if (!interval)
+ interval = 1;
+ int fudge = len / 2;
+ int pos;
+ // Since there is no subrect for tickmarks do a translation here.
+ p->save();
+ p->translate(slider->rect.x(), slider->rect.y());
+ p->setPen(slider->palette.foreground().color());
+ int v = slider->minimum;
+ while (v <= slider->maximum + 1) {
+ if (v == slider->maximum + 1 && interval == 1)
+ break;
+ const int v_ = qMin(v, slider->maximum);
+ pos = QStyle::sliderPositionFromValue(slider->minimum, slider->maximum,
+ v_, available) + fudge;
+ if (slider->orientation == Qt::Horizontal) {
+ if (ticks & QSlider::TicksAbove)
+ p->drawLine(pos, 0, pos, tickOffset - 2);
+ if (ticks & QSlider::TicksBelow)
+ p->drawLine(pos, tickOffset + thickness + 1, pos,
+ slider->rect.height()-1);
+ } else {
+ if (ticks & QSlider::TicksAbove)
+ p->drawLine(0, pos, tickOffset - 2, pos);
+ if (ticks & QSlider::TicksBelow)
+ p->drawLine(tickOffset + thickness + 1, pos,
+ slider->rect.width()-1, pos);
+ }
+ // in the case where maximum is max int
+ int nextInterval = v + interval;
+ if (nextInterval < v)
+ break;
+ v = nextInterval;
+ }
+ p->restore();
+ }
+ }
+ break;
+#endif // QT_NO_SLIDER
+#ifndef QT_NO_SCROLLBAR
+ case CC_ScrollBar:
+ if (const QStyleOptionSlider *scrollbar = qstyleoption_cast<const QStyleOptionSlider *>(opt)) {
+ // Make a copy here and reset it for each primitive.
+ QStyleOptionSlider newScrollbar = *scrollbar;
+ State saveFlags = scrollbar->state;
+
+ if (scrollbar->subControls & SC_ScrollBarSubLine) {
+ newScrollbar.state = saveFlags;
+ newScrollbar.rect = subControlRect(cc, &newScrollbar, SC_ScrollBarSubLine, widget);
+ if (newScrollbar.rect.isValid()) {
+ if (!(scrollbar->activeSubControls & SC_ScrollBarSubLine))
+ newScrollbar.state &= ~(State_Sunken | State_MouseOver);
+ drawControl(CE_ScrollBarSubLine, &newScrollbar, p, widget);
+ }
+ }
+ if (scrollbar->subControls & SC_ScrollBarAddLine) {
+ newScrollbar.rect = scrollbar->rect;
+ newScrollbar.state = saveFlags;
+ newScrollbar.rect = subControlRect(cc, &newScrollbar, SC_ScrollBarAddLine, widget);
+ if (newScrollbar.rect.isValid()) {
+ if (!(scrollbar->activeSubControls & SC_ScrollBarAddLine))
+ newScrollbar.state &= ~(State_Sunken | State_MouseOver);
+ drawControl(CE_ScrollBarAddLine, &newScrollbar, p, widget);
+ }
+ }
+ if (scrollbar->subControls & SC_ScrollBarSubPage) {
+ newScrollbar.rect = scrollbar->rect;
+ newScrollbar.state = saveFlags;
+ newScrollbar.rect = subControlRect(cc, &newScrollbar, SC_ScrollBarSubPage, widget);
+ if (newScrollbar.rect.isValid()) {
+ if (!(scrollbar->activeSubControls & SC_ScrollBarSubPage))
+ newScrollbar.state &= ~(State_Sunken | State_MouseOver);
+ drawControl(CE_ScrollBarSubPage, &newScrollbar, p, widget);
+ }
+ }
+ if (scrollbar->subControls & SC_ScrollBarAddPage) {
+ newScrollbar.rect = scrollbar->rect;
+ newScrollbar.state = saveFlags;
+ newScrollbar.rect = subControlRect(cc, &newScrollbar, SC_ScrollBarAddPage, widget);
+ if (newScrollbar.rect.isValid()) {
+ if (!(scrollbar->activeSubControls & SC_ScrollBarAddPage))
+ newScrollbar.state &= ~(State_Sunken | State_MouseOver);
+ drawControl(CE_ScrollBarAddPage, &newScrollbar, p, widget);
+ }
+ }
+ if (scrollbar->subControls & SC_ScrollBarFirst) {
+ newScrollbar.rect = scrollbar->rect;
+ newScrollbar.state = saveFlags;
+ newScrollbar.rect = subControlRect(cc, &newScrollbar, SC_ScrollBarFirst, widget);
+ if (newScrollbar.rect.isValid()) {
+ if (!(scrollbar->activeSubControls & SC_ScrollBarFirst))
+ newScrollbar.state &= ~(State_Sunken | State_MouseOver);
+ drawControl(CE_ScrollBarFirst, &newScrollbar, p, widget);
+ }
+ }
+ if (scrollbar->subControls & SC_ScrollBarLast) {
+ newScrollbar.rect = scrollbar->rect;
+ newScrollbar.state = saveFlags;
+ newScrollbar.rect = subControlRect(cc, &newScrollbar, SC_ScrollBarLast, widget);
+ if (newScrollbar.rect.isValid()) {
+ if (!(scrollbar->activeSubControls & SC_ScrollBarLast))
+ newScrollbar.state &= ~(State_Sunken | State_MouseOver);
+ drawControl(CE_ScrollBarLast, &newScrollbar, p, widget);
+ }
+ }
+ if (scrollbar->subControls & SC_ScrollBarSlider) {
+ newScrollbar.rect = scrollbar->rect;
+ newScrollbar.state = saveFlags;
+ newScrollbar.rect = subControlRect(cc, &newScrollbar, SC_ScrollBarSlider, widget);
+ if (newScrollbar.rect.isValid()) {
+ if (!(scrollbar->activeSubControls & SC_ScrollBarSlider))
+ newScrollbar.state &= ~(State_Sunken | State_MouseOver);
+ drawControl(CE_ScrollBarSlider, &newScrollbar, p, widget);
+
+ if (scrollbar->state & State_HasFocus) {
+ QStyleOptionFocusRect fropt;
+ fropt.QStyleOption::operator=(newScrollbar);
+ fropt.rect.setRect(newScrollbar.rect.x() + 2, newScrollbar.rect.y() + 2,
+ newScrollbar.rect.width() - 5,
+ newScrollbar.rect.height() - 5);
+ drawPrimitive(PE_FrameFocusRect, &fropt, p, widget);
+ }
+ }
+ }
+ }
+ break;
+#endif // QT_NO_SCROLLBAR
+#ifdef QT3_SUPPORT
+ case CC_Q3ListView:
+ if (const QStyleOptionQ3ListView *lv = qstyleoption_cast<const QStyleOptionQ3ListView *>(opt)) {
+ if (lv->subControls & SC_Q3ListView)
+ p->fillRect(lv->rect, lv->viewportPalette.brush(lv->viewportBGRole));
+ }
+ break;
+#endif // QT3_SUPPORT
+#ifndef QT_NO_SPINBOX
+ case CC_SpinBox:
+ if (const QStyleOptionSpinBox *sb = qstyleoption_cast<const QStyleOptionSpinBox *>(opt)) {
+ QStyleOptionSpinBox copy = *sb;
+ PrimitiveElement pe;
+
+ if (sb->frame && (sb->subControls & SC_SpinBoxFrame)) {
+ QRect r = subControlRect(CC_SpinBox, sb, SC_SpinBoxFrame, widget);
+ qDrawWinPanel(p, r, sb->palette, true);
+ }
+
+ if (sb->subControls & SC_SpinBoxUp) {
+ copy.subControls = SC_SpinBoxUp;
+ QPalette pal2 = sb->palette;
+ if (!(sb->stepEnabled & QAbstractSpinBox::StepUpEnabled)) {
+ pal2.setCurrentColorGroup(QPalette::Disabled);
+ copy.state &= ~State_Enabled;
+ }
+
+ copy.palette = pal2;
+
+ if (sb->activeSubControls == SC_SpinBoxUp && (sb->state & State_Sunken)) {
+ copy.state |= State_On;
+ copy.state |= State_Sunken;
+ } else {
+ copy.state |= State_Raised;
+ copy.state &= ~State_Sunken;
+ }
+ pe = (sb->buttonSymbols == QAbstractSpinBox::PlusMinus ? PE_IndicatorSpinPlus
+ : PE_IndicatorSpinUp);
+
+ copy.rect = subControlRect(CC_SpinBox, sb, SC_SpinBoxUp, widget);
+ drawPrimitive(PE_PanelButtonBevel, &copy, p, widget);
+ copy.rect.adjust(3, 0, -4, 0);
+ drawPrimitive(pe, &copy, p, widget);
+ }
+
+ if (sb->subControls & SC_SpinBoxDown) {
+ copy.subControls = SC_SpinBoxDown;
+ copy.state = sb->state;
+ QPalette pal2 = sb->palette;
+ if (!(sb->stepEnabled & QAbstractSpinBox::StepDownEnabled)) {
+ pal2.setCurrentColorGroup(QPalette::Disabled);
+ copy.state &= ~State_Enabled;
+ }
+ copy.palette = pal2;
+
+ if (sb->activeSubControls == SC_SpinBoxDown && (sb->state & State_Sunken)) {
+ copy.state |= State_On;
+ copy.state |= State_Sunken;
+ } else {
+ copy.state |= State_Raised;
+ copy.state &= ~State_Sunken;
+ }
+ pe = (sb->buttonSymbols == QAbstractSpinBox::PlusMinus ? PE_IndicatorSpinMinus
+ : PE_IndicatorSpinDown);
+
+ copy.rect = subControlRect(CC_SpinBox, sb, SC_SpinBoxDown, widget);
+ drawPrimitive(PE_PanelButtonBevel, &copy, p, widget);
+ copy.rect.adjust(3, 0, -4, 0);
+ drawPrimitive(pe, &copy, p, widget);
+ }
+ }
+ break;
+#endif // QT_NO_SPINBOX
+#ifndef QT_NO_TOOLBUTTON
+ case CC_ToolButton:
+ if (const QStyleOptionToolButton *toolbutton
+ = qstyleoption_cast<const QStyleOptionToolButton *>(opt)) {
+ QRect button, menuarea;
+ button = subControlRect(cc, toolbutton, SC_ToolButton, widget);
+ menuarea = subControlRect(cc, toolbutton, SC_ToolButtonMenu, widget);
+
+ State bflags = toolbutton->state & ~State_Sunken;
+
+ if (bflags & State_AutoRaise) {
+ if (!(bflags & State_MouseOver) || !(bflags & State_Enabled)) {
+ bflags &= ~State_Raised;
+ }
+ }
+ State mflags = bflags;
+ if (toolbutton->state & State_Sunken) {
+ if (toolbutton->activeSubControls & SC_ToolButton)
+ bflags |= State_Sunken;
+ mflags |= State_Sunken;
+ }
+
+ QStyleOption tool(0);
+ tool.palette = toolbutton->palette;
+ if (toolbutton->subControls & SC_ToolButton) {
+ if (bflags & (State_Sunken | State_On | State_Raised)) {
+ tool.rect = button;
+ tool.state = bflags;
+ drawPrimitive(PE_PanelButtonTool, &tool, p, widget);
+ }
+ }
+
+ if (toolbutton->state & State_HasFocus) {
+ QStyleOptionFocusRect fr;
+ fr.QStyleOption::operator=(*toolbutton);
+ fr.rect.adjust(3, 3, -3, -3);
+ if (toolbutton->features & QStyleOptionToolButton::MenuButtonPopup)
+ fr.rect.adjust(0, 0, -pixelMetric(QStyle::PM_MenuButtonIndicator,
+ toolbutton, widget), 0);
+ drawPrimitive(PE_FrameFocusRect, &fr, p, widget);
+ }
+ QStyleOptionToolButton label = *toolbutton;
+ label.state = bflags;
+ int fw = pixelMetric(PM_DefaultFrameWidth, opt, widget);
+ label.rect = button.adjusted(fw, fw, -fw, -fw);
+ drawControl(CE_ToolButtonLabel, &label, p, widget);
+
+ if (toolbutton->subControls & SC_ToolButtonMenu) {
+ tool.rect = menuarea;
+ tool.state = mflags;
+ if (mflags & (State_Sunken | State_On | State_Raised))
+ drawPrimitive(PE_IndicatorButtonDropDown, &tool, p, widget);
+ drawPrimitive(PE_IndicatorArrowDown, &tool, p, widget);
+ } else if (toolbutton->features & QStyleOptionToolButton::HasMenu) {
+ int mbi = pixelMetric(PM_MenuButtonIndicator, toolbutton, widget);
+ QRect ir = toolbutton->rect;
+ QStyleOptionToolButton newBtn = *toolbutton;
+ newBtn.rect = QRect(ir.right() + 5 - mbi, ir.y() + ir.height() - mbi + 4, mbi - 6, mbi - 6);
+ drawPrimitive(PE_IndicatorArrowDown, &newBtn, p, widget);
+ }
+ }
+ break;
+#endif // QT_NO_TOOLBUTTON
+ case CC_TitleBar:
+ if (const QStyleOptionTitleBar *tb = qstyleoption_cast<const QStyleOptionTitleBar *>(opt)) {
+ QRect ir;
+ if (opt->subControls & SC_TitleBarLabel) {
+ QColor left = tb->palette.highlight().color();
+ QColor right = tb->palette.base().color();
+
+ QBrush fillBrush(left);
+ if (left != right) {
+ QPoint p1(tb->rect.x(), tb->rect.top() + tb->rect.height()/2);
+ QPoint p2(tb->rect.right(), tb->rect.top() + tb->rect.height()/2);
+ QLinearGradient lg(p1, p2);
+ lg.setColorAt(0, left);
+ lg.setColorAt(1, right);
+ fillBrush = lg;
+ }
+
+ p->fillRect(opt->rect, fillBrush);
+
+ ir = subControlRect(CC_TitleBar, tb, SC_TitleBarLabel, widget);
+
+ p->setPen(tb->palette.highlightedText().color());
+ p->drawText(ir.x() + 2, ir.y(), ir.width() - 2, ir.height(),
+ Qt::AlignLeft | Qt::AlignVCenter | Qt::TextSingleLine, tb->text);
+ }
+
+ bool down = false;
+ QPixmap pm;
+
+ QStyleOption tool(0);
+ tool.palette = tb->palette;
+ if (tb->subControls & SC_TitleBarCloseButton && tb->titleBarFlags & Qt::WindowSystemMenuHint) {
+ ir = subControlRect(CC_TitleBar, tb, SC_TitleBarCloseButton, widget);
+ down = tb->activeSubControls & SC_TitleBarCloseButton && (opt->state & State_Sunken);
+ if ((tb->titleBarFlags & Qt::WindowType_Mask) == Qt::Tool
+#ifndef QT_NO_DOCKWIDGET
+ || qobject_cast<const QDockWidget *>(widget)
+#endif
+ )
+ pm = standardIcon(SP_DockWidgetCloseButton, &tool, widget).pixmap(10, 10);
+ else
+ pm = standardIcon(SP_TitleBarCloseButton, &tool, widget).pixmap(10, 10);
+ tool.rect = ir;
+ tool.state = down ? State_Sunken : State_Raised;
+ drawPrimitive(PE_PanelButtonTool, &tool, p, widget);
+
+ p->save();
+ if (down)
+ p->translate(pixelMetric(PM_ButtonShiftHorizontal, tb, widget),
+ pixelMetric(PM_ButtonShiftVertical, tb, widget));
+ drawItemPixmap(p, ir, Qt::AlignCenter, pm);
+ p->restore();
+ }
+
+ if (tb->subControls & SC_TitleBarMaxButton
+ && tb->titleBarFlags & Qt::WindowMaximizeButtonHint
+ && !(tb->titleBarState & Qt::WindowMaximized)) {
+ ir = subControlRect(CC_TitleBar, tb, SC_TitleBarMaxButton, widget);
+
+ down = tb->activeSubControls & SC_TitleBarMaxButton && (opt->state & State_Sunken);
+ pm = standardIcon(SP_TitleBarMaxButton, &tool, widget).pixmap(10, 10);
+ tool.rect = ir;
+ tool.state = down ? State_Sunken : State_Raised;
+ drawPrimitive(PE_PanelButtonTool, &tool, p, widget);
+
+ p->save();
+ if (down)
+ p->translate(pixelMetric(PM_ButtonShiftHorizontal, tb, widget),
+ pixelMetric(PM_ButtonShiftVertical, tb, widget));
+ drawItemPixmap(p, ir, Qt::AlignCenter, pm);
+ p->restore();
+ }
+
+ if (tb->subControls & SC_TitleBarMinButton
+ && tb->titleBarFlags & Qt::WindowMinimizeButtonHint
+ && !(tb->titleBarState & Qt::WindowMinimized)) {
+ ir = subControlRect(CC_TitleBar, tb, SC_TitleBarMinButton, widget);
+ down = tb->activeSubControls & SC_TitleBarMinButton && (opt->state & State_Sunken);
+ pm = standardIcon(SP_TitleBarMinButton, &tool, widget).pixmap(10, 10);
+ tool.rect = ir;
+ tool.state = down ? State_Sunken : State_Raised;
+ drawPrimitive(PE_PanelButtonTool, &tool, p, widget);
+
+ p->save();
+ if (down)
+ p->translate(pixelMetric(PM_ButtonShiftHorizontal, tb, widget),
+ pixelMetric(PM_ButtonShiftVertical, tb, widget));
+ drawItemPixmap(p, ir, Qt::AlignCenter, pm);
+ p->restore();
+ }
+
+ bool drawNormalButton = (tb->subControls & SC_TitleBarNormalButton)
+ && (((tb->titleBarFlags & Qt::WindowMinimizeButtonHint)
+ && (tb->titleBarState & Qt::WindowMinimized))
+ || ((tb->titleBarFlags & Qt::WindowMaximizeButtonHint)
+ && (tb->titleBarState & Qt::WindowMaximized)));
+
+ if (drawNormalButton) {
+ ir = subControlRect(CC_TitleBar, tb, SC_TitleBarNormalButton, widget);
+ down = tb->activeSubControls & SC_TitleBarNormalButton && (opt->state & State_Sunken);
+ pm = standardIcon(SP_TitleBarNormalButton, &tool, widget).pixmap(10, 10);
+ tool.rect = ir;
+ tool.state = down ? State_Sunken : State_Raised;
+ drawPrimitive(PE_PanelButtonTool, &tool, p, widget);
+
+ p->save();
+ if (down)
+ p->translate(pixelMetric(PM_ButtonShiftHorizontal, tb, widget),
+ pixelMetric(PM_ButtonShiftVertical, tb, widget));
+ drawItemPixmap(p, ir, Qt::AlignCenter, pm);
+ p->restore();
+ }
+
+ if (tb->subControls & SC_TitleBarShadeButton
+ && tb->titleBarFlags & Qt::WindowShadeButtonHint
+ && !(tb->titleBarState & Qt::WindowMinimized)) {
+ ir = subControlRect(CC_TitleBar, tb, SC_TitleBarShadeButton, widget);
+ down = (tb->activeSubControls & SC_TitleBarShadeButton && (opt->state & State_Sunken));
+ pm = standardIcon(SP_TitleBarShadeButton, &tool, widget).pixmap(10, 10);
+ tool.rect = ir;
+ tool.state = down ? State_Sunken : State_Raised;
+ drawPrimitive(PE_PanelButtonTool, &tool, p, widget);
+ p->save();
+ if (down)
+ p->translate(pixelMetric(PM_ButtonShiftHorizontal, tb, widget),
+ pixelMetric(PM_ButtonShiftVertical, tb, widget));
+ drawItemPixmap(p, ir, Qt::AlignCenter, pm);
+ p->restore();
+ }
+
+ if (tb->subControls & SC_TitleBarUnshadeButton
+ && tb->titleBarFlags & Qt::WindowShadeButtonHint
+ && tb->titleBarState & Qt::WindowMinimized) {
+ ir = subControlRect(CC_TitleBar, tb, SC_TitleBarUnshadeButton, widget);
+
+ down = tb->activeSubControls & SC_TitleBarUnshadeButton && (opt->state & State_Sunken);
+ pm = standardIcon(SP_TitleBarUnshadeButton, &tool, widget).pixmap(10, 10);
+ tool.rect = ir;
+ tool.state = down ? State_Sunken : State_Raised;
+ drawPrimitive(PE_PanelButtonTool, &tool, p, widget);
+ p->save();
+ if (down)
+ p->translate(pixelMetric(PM_ButtonShiftHorizontal, tb, widget),
+ pixelMetric(PM_ButtonShiftVertical, tb, widget));
+ drawItemPixmap(p, ir, Qt::AlignCenter, pm);
+ p->restore();
+ }
+ if (tb->subControls & SC_TitleBarContextHelpButton
+ && tb->titleBarFlags & Qt::WindowContextHelpButtonHint) {
+ ir = subControlRect(CC_TitleBar, tb, SC_TitleBarContextHelpButton, widget);
+
+ down = tb->activeSubControls & SC_TitleBarContextHelpButton && (opt->state & State_Sunken);
+ pm = standardIcon(SP_TitleBarContextHelpButton, &tool, widget).pixmap(10, 10);
+ tool.rect = ir;
+ tool.state = down ? State_Sunken : State_Raised;
+ drawPrimitive(PE_PanelButtonTool, &tool, p, widget);
+ p->save();
+ if (down)
+ p->translate(pixelMetric(PM_ButtonShiftHorizontal, tb, widget),
+ pixelMetric(PM_ButtonShiftVertical, tb, widget));
+ drawItemPixmap(p, ir, Qt::AlignCenter, pm);
+ p->restore();
+ }
+ if (tb->subControls & SC_TitleBarSysMenu && tb->titleBarFlags & Qt::WindowSystemMenuHint) {
+ ir = subControlRect(CC_TitleBar, tb, SC_TitleBarSysMenu, widget);
+ if (!tb->icon.isNull()) {
+ tb->icon.paint(p, ir);
+ } else {
+ int iconSize = pixelMetric(PM_SmallIconSize, tb, widget);
+ pm = standardIcon(SP_TitleBarMenuButton, &tool, widget).pixmap(iconSize, iconSize);
+ tool.rect = ir;
+ p->save();
+ drawItemPixmap(p, ir, Qt::AlignCenter, pm);
+ p->restore();
+ }
+ }
+ }
+ break;
+#ifndef QT_NO_DIAL
+ case CC_Dial:
+ if (const QStyleOptionSlider *dial = qstyleoption_cast<const QStyleOptionSlider *>(opt)) {
+ // OK, this is more a port of things over
+ p->save();
+
+ // avoid dithering
+ if (p->paintEngine()->hasFeature(QPaintEngine::Antialiasing))
+ p->setRenderHint(QPainter::Antialiasing);
+
+ int width = dial->rect.width();
+ int height = dial->rect.height();
+ qreal r = qMin(width, height) / 2;
+ qreal d_ = r / 6;
+ qreal dx = dial->rect.x() + d_ + (width - 2 * r) / 2 + 1;
+ qreal dy = dial->rect.y() + d_ + (height - 2 * r) / 2 + 1;
+ QRect br = QRect(int(dx), int(dy), int(r * 2 - 2 * d_ - 2), int(r * 2 - 2 * d_ - 2));
+
+ QPalette pal = opt->palette;
+ // draw notches
+ if (dial->subControls & QStyle::SC_DialTickmarks) {
+ p->setPen(pal.foreground().color());
+ p->drawLines(calcLines(dial, widget)); // ### calcLines could be cached...
+ }
+
+ if (dial->state & State_Enabled) {
+ p->setBrush(pal.brush(QPalette::ColorRole(styleHint(SH_Dial_BackgroundRole,
+ dial, widget))));
+ p->setPen(Qt::NoPen);
+ p->drawEllipse(br);
+ p->setBrush(Qt::NoBrush);
+ }
+ p->setPen(QPen(pal.dark().color()));
+ p->drawArc(br, 60 * 16, 180 * 16);
+ p->setPen(QPen(pal.light().color()));
+ p->drawArc(br, 240 * 16, 180 * 16);
+
+ qreal a;
+ QPolygonF arrow(calcArrow(dial, a));
+
+ p->setPen(Qt::NoPen);
+ p->setBrush(pal.button());
+ p->drawPolygon(arrow);
+
+ a = angle(QPointF(width / 2, height / 2), arrow[0]);
+ p->setBrush(Qt::NoBrush);
+
+ if (a <= 0 || a > 200) {
+ p->setPen(pal.light().color());
+ p->drawLine(arrow[2], arrow[0]);
+ p->drawLine(arrow[1], arrow[2]);
+ p->setPen(pal.dark().color());
+ p->drawLine(arrow[0], arrow[1]);
+ } else if (a > 0 && a < 45) {
+ p->setPen(pal.light().color());
+ p->drawLine(arrow[2], arrow[0]);
+ p->setPen(pal.dark().color());
+ p->drawLine(arrow[1], arrow[2]);
+ p->drawLine(arrow[0], arrow[1]);
+ } else if (a >= 45 && a < 135) {
+ p->setPen(pal.dark().color());
+ p->drawLine(arrow[2], arrow[0]);
+ p->drawLine(arrow[1], arrow[2]);
+ p->setPen(pal.light().color());
+ p->drawLine(arrow[0], arrow[1]);
+ } else if (a >= 135 && a < 200) {
+ p->setPen(pal.dark().color());
+ p->drawLine(arrow[2], arrow[0]);
+ p->setPen(pal.light().color());
+ p->drawLine(arrow[0], arrow[1]);
+ p->drawLine(arrow[1], arrow[2]);
+ }
+
+ // draw focus rect around the dial
+ QStyleOptionFocusRect fropt;
+ fropt.rect = dial->rect;
+ fropt.state = dial->state;
+ fropt.palette = dial->palette;
+ if (fropt.state & QStyle::State_HasFocus) {
+ br.adjust(0, 0, 2, 2);
+ if (dial->subControls & SC_DialTickmarks) {
+ int r = qMin(width, height) / 2;
+ br.translate(-r / 6, - r / 6);
+ br.setWidth(br.width() + r / 3);
+ br.setHeight(br.height() + r / 3);
+ }
+ fropt.rect = br.adjusted(-2, -2, 2, 2);
+ drawPrimitive(QStyle::PE_FrameFocusRect, &fropt, p, widget);
+ }
+ p->restore();
+ }
+ break;
+#endif // QT_NO_DIAL
+#ifndef QT_NO_GROUPBOX
+ case CC_GroupBox:
+ if (const QStyleOptionGroupBox *groupBox = qstyleoption_cast<const QStyleOptionGroupBox *>(opt)) {
+ // Draw frame
+ QRect textRect = subControlRect(CC_GroupBox, opt, SC_GroupBoxLabel, widget);
+ QRect checkBoxRect = subControlRect(CC_GroupBox, opt, SC_GroupBoxCheckBox, widget);
+ if (groupBox->subControls & QStyle::SC_GroupBoxFrame) {
+ QStyleOptionFrameV2 frame;
+ frame.QStyleOption::operator=(*groupBox);
+ frame.features = groupBox->features;
+ frame.lineWidth = groupBox->lineWidth;
+ frame.midLineWidth = groupBox->midLineWidth;
+ frame.rect = subControlRect(CC_GroupBox, opt, SC_GroupBoxFrame, widget);
+ p->save();
+ QRegion region(groupBox->rect);
+ if (!groupBox->text.isEmpty()) {
+ bool ltr = groupBox->direction == Qt::LeftToRight;
+ QRect finalRect;
+ if (groupBox->subControls & QStyle::SC_GroupBoxCheckBox) {
+ finalRect = checkBoxRect.united(textRect);
+ finalRect.adjust(ltr ? -4 : 0, 0, ltr ? 0 : 4, 0);
+ } else {
+ finalRect = textRect;
+ }
+ region -= finalRect;
+ }
+ p->setClipRegion(region);
+ drawPrimitive(PE_FrameGroupBox, &frame, p, widget);
+ p->restore();
+ }
+
+ // Draw title
+ if ((groupBox->subControls & QStyle::SC_GroupBoxLabel) && !groupBox->text.isEmpty()) {
+ QColor textColor = groupBox->textColor;
+ if (textColor.isValid())
+ p->setPen(textColor);
+ int alignment = int(groupBox->textAlignment);
+ if (!styleHint(QStyle::SH_UnderlineShortcut, opt, widget))
+ alignment |= Qt::TextHideMnemonic;
+
+ drawItemText(p, textRect, Qt::TextShowMnemonic | Qt::AlignHCenter | alignment,
+ groupBox->palette, groupBox->state & State_Enabled, groupBox->text,
+ textColor.isValid() ? QPalette::NoRole : QPalette::WindowText);
+
+ if (groupBox->state & State_HasFocus) {
+ QStyleOptionFocusRect fropt;
+ fropt.QStyleOption::operator=(*groupBox);
+ fropt.rect = textRect;
+ drawPrimitive(PE_FrameFocusRect, &fropt, p, widget);
+ }
+ }
+
+ // Draw checkbox
+ if (groupBox->subControls & SC_GroupBoxCheckBox) {
+ QStyleOptionButton box;
+ box.QStyleOption::operator=(*groupBox);
+ box.rect = checkBoxRect;
+ drawPrimitive(PE_IndicatorCheckBox, &box, p, widget);
+ }
+ }
+ break;
+#endif // QT_NO_GROUPBOX
+#ifndef QT_NO_WORKSPACE
+ case CC_MdiControls:
+ {
+ QStyleOptionButton btnOpt;
+ btnOpt.QStyleOption::operator=(*opt);
+ btnOpt.state &= ~State_MouseOver;
+ int bsx = 0;
+ int bsy = 0;
+ if (opt->subControls & QStyle::SC_MdiCloseButton) {
+ if (opt->activeSubControls & QStyle::SC_MdiCloseButton && (opt->state & State_Sunken)) {
+ btnOpt.state |= State_Sunken;
+ btnOpt.state &= ~State_Raised;
+ bsx = pixelMetric(PM_ButtonShiftHorizontal);
+ bsy = pixelMetric(PM_ButtonShiftVertical);
+ } else {
+ btnOpt.state |= State_Raised;
+ btnOpt.state &= ~State_Sunken;
+ bsx = 0;
+ bsy = 0;
+ }
+ btnOpt.rect = subControlRect(CC_MdiControls, opt, SC_MdiCloseButton, widget);
+ drawPrimitive(PE_PanelButtonCommand, &btnOpt, p, widget);
+ QPixmap pm = standardIcon(SP_TitleBarCloseButton).pixmap(16, 16);
+ drawItemPixmap(p, btnOpt.rect.translated(bsx, bsy), Qt::AlignCenter, pm);
+ }
+ if (opt->subControls & QStyle::SC_MdiNormalButton) {
+ if (opt->activeSubControls & QStyle::SC_MdiNormalButton && (opt->state & State_Sunken)) {
+ btnOpt.state |= State_Sunken;
+ btnOpt.state &= ~State_Raised;
+ bsx = pixelMetric(PM_ButtonShiftHorizontal);
+ bsy = pixelMetric(PM_ButtonShiftVertical);
+ } else {
+ btnOpt.state |= State_Raised;
+ btnOpt.state &= ~State_Sunken;
+ bsx = 0;
+ bsy = 0;
+ }
+ btnOpt.rect = subControlRect(CC_MdiControls, opt, SC_MdiNormalButton, widget);
+ drawPrimitive(PE_PanelButtonCommand, &btnOpt, p, widget);
+ QPixmap pm = standardIcon(SP_TitleBarNormalButton).pixmap(16, 16);
+ drawItemPixmap(p, btnOpt.rect.translated(bsx, bsy), Qt::AlignCenter, pm);
+ }
+ if (opt->subControls & QStyle::SC_MdiMinButton) {
+ if (opt->activeSubControls & QStyle::SC_MdiMinButton && (opt->state & State_Sunken)) {
+ btnOpt.state |= State_Sunken;
+ btnOpt.state &= ~State_Raised;
+ bsx = pixelMetric(PM_ButtonShiftHorizontal);
+ bsy = pixelMetric(PM_ButtonShiftVertical);
+ } else {
+ btnOpt.state |= State_Raised;
+ btnOpt.state &= ~State_Sunken;
+ bsx = 0;
+ bsy = 0;
+ }
+ btnOpt.rect = subControlRect(CC_MdiControls, opt, SC_MdiMinButton, widget);
+ drawPrimitive(PE_PanelButtonCommand, &btnOpt, p, widget);
+ QPixmap pm = standardIcon(SP_TitleBarMinButton).pixmap(16, 16);
+ drawItemPixmap(p, btnOpt.rect.translated(bsx, bsy), Qt::AlignCenter, pm);
+ }
+ }
+ break;
+#endif // QT_NO_WORKSPACE
+
+ default:
+ qWarning("QCommonStyle::drawComplexControl: Control %d not handled", cc);
+ }
+}
+
+/*!
+ \reimp
+*/
+QStyle::SubControl QCommonStyle::hitTestComplexControl(ComplexControl cc, const QStyleOptionComplex *opt,
+ const QPoint &pt, const QWidget *widget) const
+{
+ SubControl sc = SC_None;
+ switch (cc) {
+#ifndef QT_NO_SLIDER
+ case CC_Slider:
+ if (const QStyleOptionSlider *slider = qstyleoption_cast<const QStyleOptionSlider *>(opt)) {
+ QRect r = subControlRect(cc, slider, SC_SliderHandle, widget);
+ if (r.isValid() && r.contains(pt)) {
+ sc = SC_SliderHandle;
+ } else {
+ r = subControlRect(cc, slider, SC_SliderGroove ,widget);
+ if (r.isValid() && r.contains(pt))
+ sc = SC_SliderGroove;
+ }
+ }
+ break;
+#endif // QT_NO_SLIDER
+#ifndef QT_NO_SCROLLBAR
+ case CC_ScrollBar:
+ if (const QStyleOptionSlider *scrollbar = qstyleoption_cast<const QStyleOptionSlider *>(opt)) {
+ QRect r;
+ uint ctrl = SC_ScrollBarAddLine;
+ while (ctrl <= SC_ScrollBarGroove) {
+ r = subControlRect(cc, scrollbar, QStyle::SubControl(ctrl), widget);
+ if (r.isValid() && r.contains(pt)) {
+ sc = QStyle::SubControl(ctrl);
+ break;
+ }
+ ctrl <<= 1;
+ }
+ }
+ break;
+#endif // QT_NO_SCROLLBAR
+#ifndef QT_NO_TOOLBUTTON
+ case CC_ToolButton:
+ if (const QStyleOptionToolButton *toolbutton = qstyleoption_cast<const QStyleOptionToolButton *>(opt)) {
+ QRect r;
+ uint ctrl = SC_ToolButton;
+ while (ctrl <= SC_ToolButtonMenu) {
+ r = subControlRect(cc, toolbutton, QStyle::SubControl(ctrl), widget);
+ if (r.isValid() && r.contains(pt)) {
+ sc = QStyle::SubControl(ctrl);
+ break;
+ }
+ ctrl <<= 1;
+ }
+ }
+ break;
+#endif // QT_NO_TOOLBUTTON
+#ifdef QT3_SUPPORT
+ case CC_Q3ListView:
+ if (const QStyleOptionQ3ListView *lv = qstyleoption_cast<const QStyleOptionQ3ListView *>(opt)) {
+ if (pt.x() >= 0 && pt.x() < lv->treeStepSize)
+ sc = SC_Q3ListViewExpand;
+ }
+ break;
+#endif // QT3_SUPPORT
+#ifndef QT_NO_SPINBOX
+ case CC_SpinBox:
+ if (const QStyleOptionSpinBox *spinbox = qstyleoption_cast<const QStyleOptionSpinBox *>(opt)) {
+ QRect r;
+ uint ctrl = SC_SpinBoxUp;
+ while (ctrl <= SC_SpinBoxEditField) {
+ r = subControlRect(cc, spinbox, QStyle::SubControl(ctrl), widget);
+ if (r.isValid() && r.contains(pt)) {
+ sc = QStyle::SubControl(ctrl);
+ break;
+ }
+ ctrl <<= 1;
+ }
+ }
+ break;
+#endif // QT_NO_SPINBOX
+ case CC_TitleBar:
+ if (const QStyleOptionTitleBar *tb = qstyleoption_cast<const QStyleOptionTitleBar *>(opt)) {
+ QRect r;
+ uint ctrl = SC_TitleBarSysMenu;
+
+ while (ctrl <= SC_TitleBarLabel) {
+ r = subControlRect(cc, tb, QStyle::SubControl(ctrl), widget);
+ if (r.isValid() && r.contains(pt)) {
+ sc = QStyle::SubControl(ctrl);
+ break;
+ }
+ ctrl <<= 1;
+ }
+ }
+ break;
+#ifndef QT_NO_COMBOBOX
+ case CC_ComboBox:
+ if (const QStyleOptionComboBox *cb = qstyleoption_cast<const QStyleOptionComboBox *>(opt)) {
+ QRect r;
+ uint ctrl = SC_ComboBoxArrow; // Start here and go down.
+ while (ctrl > 0) {
+ r = subControlRect(cc, cb, QStyle::SubControl(ctrl), widget);
+ if (r.isValid() && r.contains(pt)) {
+ sc = QStyle::SubControl(ctrl);
+ break;
+ }
+ ctrl >>= 1;
+ }
+ }
+ break;
+#endif // QT_NO_COMBOBOX
+#ifndef QT_NO_GROUPBOX
+ case CC_GroupBox:
+ if (const QStyleOptionGroupBox *groupBox = qstyleoption_cast<const QStyleOptionGroupBox *>(opt)) {
+ QRect r;
+ uint ctrl = SC_GroupBoxCheckBox;
+ while (ctrl <= SC_GroupBoxFrame) {
+ r = subControlRect(cc, groupBox, QStyle::SubControl(ctrl), widget);
+ if (r.isValid() && r.contains(pt)) {
+ sc = QStyle::SubControl(ctrl);
+ break;
+ }
+ ctrl <<= 1;
+ }
+ }
+ break;
+#endif // QT_NO_GROUPBOX
+ case CC_MdiControls:
+ {
+ QRect r;
+ uint ctrl = SC_MdiMinButton;
+ while (ctrl <= SC_MdiCloseButton) {
+ r = subControlRect(CC_MdiControls, opt, QStyle::SubControl(ctrl), widget);
+ if (r.isValid() && r.contains(pt) && (opt->subControls & ctrl)) {
+ sc = QStyle::SubControl(ctrl);
+ return sc;
+ }
+ ctrl <<= 1;
+ }
+ }
+ break;
+ default:
+ qWarning("QCommonStyle::hitTestComplexControl: Case %d not handled", cc);
+ }
+ return sc;
+}
+
+/*!
+ \reimp
+*/
+QRect QCommonStyle::subControlRect(ComplexControl cc, const QStyleOptionComplex *opt,
+ SubControl sc, const QWidget *widget) const
+{
+ QRect ret;
+ switch (cc) {
+#ifndef QT_NO_SLIDER
+ case CC_Slider:
+ if (const QStyleOptionSlider *slider = qstyleoption_cast<const QStyleOptionSlider *>(opt)) {
+ int tickOffset = pixelMetric(PM_SliderTickmarkOffset, slider, widget);
+ int thickness = pixelMetric(PM_SliderControlThickness, slider, widget);
+
+ switch (sc) {
+ case SC_SliderHandle: {
+ int sliderPos = 0;
+ int len = pixelMetric(PM_SliderLength, slider, widget);
+ bool horizontal = slider->orientation == Qt::Horizontal;
+ sliderPos = sliderPositionFromValue(slider->minimum, slider->maximum,
+ slider->sliderPosition,
+ (horizontal ? slider->rect.width()
+ : slider->rect.height()) - len,
+ slider->upsideDown);
+ if (horizontal)
+ ret.setRect(slider->rect.x() + sliderPos, slider->rect.y() + tickOffset, len, thickness);
+ else
+ ret.setRect(slider->rect.x() + tickOffset, slider->rect.y() + sliderPos, thickness, len);
+ break; }
+ case SC_SliderGroove:
+ if (slider->orientation == Qt::Horizontal)
+ ret.setRect(slider->rect.x(), slider->rect.y() + tickOffset,
+ slider->rect.width(), thickness);
+ else
+ ret.setRect(slider->rect.x() + tickOffset, slider->rect.y(),
+ thickness, slider->rect.height());
+ break;
+ default:
+ break;
+ }
+ ret = visualRect(slider->direction, slider->rect, ret);
+ }
+ break;
+#endif // QT_NO_SLIDER
+#ifndef QT_NO_SCROLLBAR
+ case CC_ScrollBar:
+ if (const QStyleOptionSlider *scrollbar = qstyleoption_cast<const QStyleOptionSlider *>(opt)) {
+ const QRect scrollBarRect = scrollbar->rect;
+ int sbextent = pixelMetric(PM_ScrollBarExtent, scrollbar, widget);
+ int maxlen = ((scrollbar->orientation == Qt::Horizontal) ?
+ scrollBarRect.width() : scrollBarRect.height()) - (sbextent * 2);
+ int sliderlen;
+
+ // calculate slider length
+ if (scrollbar->maximum != scrollbar->minimum) {
+ uint range = scrollbar->maximum - scrollbar->minimum;
+ sliderlen = (qint64(scrollbar->pageStep) * maxlen) / (range + scrollbar->pageStep);
+
+ int slidermin = pixelMetric(PM_ScrollBarSliderMin, scrollbar, widget);
+ if (sliderlen < slidermin || range > INT_MAX / 2)
+ sliderlen = slidermin;
+ if (sliderlen > maxlen)
+ sliderlen = maxlen;
+ } else {
+ sliderlen = maxlen;
+ }
+
+ int sliderstart = sbextent + sliderPositionFromValue(scrollbar->minimum,
+ scrollbar->maximum,
+ scrollbar->sliderPosition,
+ maxlen - sliderlen,
+ scrollbar->upsideDown);
+
+ switch (sc) {
+ case SC_ScrollBarSubLine: // top/left button
+ if (scrollbar->orientation == Qt::Horizontal) {
+ int buttonWidth = qMin(scrollBarRect.width() / 2, sbextent);
+ ret.setRect(0, 0, buttonWidth, scrollBarRect.height());
+ } else {
+ int buttonHeight = qMin(scrollBarRect.height() / 2, sbextent);
+ ret.setRect(0, 0, scrollBarRect.width(), buttonHeight);
+ }
+ break;
+ case SC_ScrollBarAddLine: // bottom/right button
+ if (scrollbar->orientation == Qt::Horizontal) {
+ int buttonWidth = qMin(scrollBarRect.width()/2, sbextent);
+ ret.setRect(scrollBarRect.width() - buttonWidth, 0, buttonWidth, scrollBarRect.height());
+ } else {
+ int buttonHeight = qMin(scrollBarRect.height()/2, sbextent);
+ ret.setRect(0, scrollBarRect.height() - buttonHeight, scrollBarRect.width(), buttonHeight);
+ }
+ break;
+ case SC_ScrollBarSubPage: // between top/left button and slider
+ if (scrollbar->orientation == Qt::Horizontal)
+ ret.setRect(sbextent, 0, sliderstart - sbextent, scrollBarRect.height());
+ else
+ ret.setRect(0, sbextent, scrollBarRect.width(), sliderstart - sbextent);
+ break;
+ case SC_ScrollBarAddPage: // between bottom/right button and slider
+ if (scrollbar->orientation == Qt::Horizontal)
+ ret.setRect(sliderstart + sliderlen, 0,
+ maxlen - sliderstart - sliderlen + sbextent, scrollBarRect.height());
+ else
+ ret.setRect(0, sliderstart + sliderlen, scrollBarRect.width(),
+ maxlen - sliderstart - sliderlen + sbextent);
+ break;
+ case SC_ScrollBarGroove:
+ if (scrollbar->orientation == Qt::Horizontal)
+ ret.setRect(sbextent, 0, scrollBarRect.width() - sbextent * 2,
+ scrollBarRect.height());
+ else
+ ret.setRect(0, sbextent, scrollBarRect.width(),
+ scrollBarRect.height() - sbextent * 2);
+ break;
+ case SC_ScrollBarSlider:
+ if (scrollbar->orientation == Qt::Horizontal)
+ ret.setRect(sliderstart, 0, sliderlen, scrollBarRect.height());
+ else
+ ret.setRect(0, sliderstart, scrollBarRect.width(), sliderlen);
+ break;
+ default:
+ break;
+ }
+ ret = visualRect(scrollbar->direction, scrollBarRect, ret);
+ }
+ break;
+#endif // QT_NO_SCROLLBAR
+#ifndef QT_NO_SPINBOX
+ case CC_SpinBox:
+ if (const QStyleOptionSpinBox *spinbox = qstyleoption_cast<const QStyleOptionSpinBox *>(opt)) {
+ QSize bs;
+ int fw = spinbox->frame ? pixelMetric(PM_SpinBoxFrameWidth, spinbox, widget) : 0;
+ bs.setHeight(qMax(8, spinbox->rect.height()/2 - fw));
+ // 1.6 -approximate golden mean
+ bs.setWidth(qMax(16, qMin(bs.height() * 8 / 5, spinbox->rect.width() / 4)));
+ bs = bs.expandedTo(QApplication::globalStrut());
+ int y = fw + spinbox->rect.y();
+ int x, lx, rx;
+ x = spinbox->rect.x() + spinbox->rect.width() - fw - bs.width();
+ lx = fw;
+ rx = x - fw;
+ switch (sc) {
+ case SC_SpinBoxUp:
+ if (spinbox->buttonSymbols == QAbstractSpinBox::NoButtons)
+ return QRect();
+ ret = QRect(x, y, bs.width(), bs.height());
+ break;
+ case SC_SpinBoxDown:
+ if (spinbox->buttonSymbols == QAbstractSpinBox::NoButtons)
+ return QRect();
+
+ ret = QRect(x, y + bs.height(), bs.width(), bs.height());
+ break;
+ case SC_SpinBoxEditField:
+ if (spinbox->buttonSymbols == QAbstractSpinBox::NoButtons) {
+ ret = QRect(lx, fw, spinbox->rect.width() - 2*fw, spinbox->rect.height() - 2*fw);
+ } else {
+ ret = QRect(lx, fw, rx, spinbox->rect.height() - 2*fw);
+ }
+ break;
+ case SC_SpinBoxFrame:
+ ret = spinbox->rect;
+ default:
+ break;
+ }
+ ret = visualRect(spinbox->direction, spinbox->rect, ret);
+ }
+ break;
+#endif // Qt_NO_SPINBOX
+#ifndef QT_NO_TOOLBUTTON
+ case CC_ToolButton:
+ if (const QStyleOptionToolButton *tb = qstyleoption_cast<const QStyleOptionToolButton *>(opt)) {
+ int mbi = pixelMetric(PM_MenuButtonIndicator, tb, widget);
+ ret = tb->rect;
+ switch (sc) {
+ case SC_ToolButton:
+ if ((tb->features
+ & (QStyleOptionToolButton::MenuButtonPopup | QStyleOptionToolButton::PopupDelay))
+ == QStyleOptionToolButton::MenuButtonPopup)
+ ret.adjust(0, 0, -mbi, 0);
+ break;
+ case SC_ToolButtonMenu:
+ if ((tb->features
+ & (QStyleOptionToolButton::MenuButtonPopup | QStyleOptionToolButton::PopupDelay))
+ == QStyleOptionToolButton::MenuButtonPopup)
+ ret.adjust(ret.width() - mbi, 0, 0, 0);
+ break;
+ default:
+ break;
+ }
+ ret = visualRect(tb->direction, tb->rect, ret);
+ }
+ break;
+#endif // QT_NO_TOOLBUTTON
+#ifndef QT_NO_COMBOBOX
+ case CC_ComboBox:
+ if (const QStyleOptionComboBox *cb = qstyleoption_cast<const QStyleOptionComboBox *>(opt)) {
+ int x = cb->rect.x(),
+ y = cb->rect.y(),
+ wi = cb->rect.width(),
+ he = cb->rect.height();
+ int xpos = x;
+ int margin = cb->frame ? 3 : 0;
+ int bmarg = cb->frame ? 2 : 0;
+ xpos += wi - bmarg - 16;
+
+
+ switch (sc) {
+ case SC_ComboBoxFrame:
+ ret = cb->rect;
+ break;
+ case SC_ComboBoxArrow:
+ ret.setRect(xpos, y + bmarg, 16, he - 2*bmarg);
+ break;
+ case SC_ComboBoxEditField:
+ ret.setRect(x + margin, y + margin, wi - 2 * margin - 16, he - 2 * margin);
+ break;
+ case SC_ComboBoxListBoxPopup:
+ ret = cb->rect;
+ break;
+ default:
+ break;
+ }
+ ret = visualRect(cb->direction, cb->rect, ret);
+ }
+ break;
+#endif // QT_NO_COMBOBOX
+ case CC_TitleBar:
+ if (const QStyleOptionTitleBar *tb = qstyleoption_cast<const QStyleOptionTitleBar *>(opt)) {
+ const int controlMargin = 2;
+ const int controlHeight = tb->rect.height() - controlMargin *2;
+ const int delta = controlHeight + controlMargin;
+ int offset = 0;
+
+ bool isMinimized = tb->titleBarState & Qt::WindowMinimized;
+ bool isMaximized = tb->titleBarState & Qt::WindowMaximized;
+
+ switch (sc) {
+ case SC_TitleBarLabel:
+ if (tb->titleBarFlags & (Qt::WindowTitleHint | Qt::WindowSystemMenuHint)) {
+ ret = tb->rect;
+ if (tb->titleBarFlags & Qt::WindowSystemMenuHint)
+ ret.adjust(delta, 0, -delta, 0);
+ if (tb->titleBarFlags & Qt::WindowMinimizeButtonHint)
+ ret.adjust(0, 0, -delta, 0);
+ if (tb->titleBarFlags & Qt::WindowMaximizeButtonHint)
+ ret.adjust(0, 0, -delta, 0);
+ if (tb->titleBarFlags & Qt::WindowShadeButtonHint)
+ ret.adjust(0, 0, -delta, 0);
+ if (tb->titleBarFlags & Qt::WindowContextHelpButtonHint)
+ ret.adjust(0, 0, -delta, 0);
+ }
+ break;
+ case SC_TitleBarContextHelpButton:
+ if (tb->titleBarFlags & Qt::WindowContextHelpButtonHint)
+ offset += delta;
+ case SC_TitleBarMinButton:
+ if (!isMinimized && (tb->titleBarFlags & Qt::WindowMinimizeButtonHint))
+ offset += delta;
+ else if (sc == SC_TitleBarMinButton)
+ break;
+ case SC_TitleBarNormalButton:
+ if (isMinimized && (tb->titleBarFlags & Qt::WindowMinimizeButtonHint))
+ offset += delta;
+ else if (isMaximized && (tb->titleBarFlags & Qt::WindowMaximizeButtonHint))
+ offset += delta;
+ else if (sc == SC_TitleBarNormalButton)
+ break;
+ case SC_TitleBarMaxButton:
+ if (!isMaximized && (tb->titleBarFlags & Qt::WindowMaximizeButtonHint))
+ offset += delta;
+ else if (sc == SC_TitleBarMaxButton)
+ break;
+ case SC_TitleBarShadeButton:
+ if (!isMinimized && (tb->titleBarFlags & Qt::WindowShadeButtonHint))
+ offset += delta;
+ else if (sc == SC_TitleBarShadeButton)
+ break;
+ case SC_TitleBarUnshadeButton:
+ if (isMinimized && (tb->titleBarFlags & Qt::WindowShadeButtonHint))
+ offset += delta;
+ else if (sc == SC_TitleBarUnshadeButton)
+ break;
+ case SC_TitleBarCloseButton:
+ if (tb->titleBarFlags & Qt::WindowSystemMenuHint)
+ offset += delta;
+ else if (sc == SC_TitleBarCloseButton)
+ break;
+ ret.setRect(tb->rect.right() - offset, tb->rect.top() + controlMargin,
+ controlHeight, controlHeight);
+ break;
+ case SC_TitleBarSysMenu:
+ if (tb->titleBarFlags & Qt::WindowSystemMenuHint) {
+ ret.setRect(tb->rect.left() + controlMargin, tb->rect.top() + controlMargin,
+ controlHeight, controlHeight);
+ }
+ break;
+ default:
+ break;
+ }
+ ret = visualRect(tb->direction, tb->rect, ret);
+ }
+ break;
+#ifndef QT_NO_GROUPBOX
+ case CC_GroupBox: {
+ if (const QStyleOptionGroupBox *groupBox = qstyleoption_cast<const QStyleOptionGroupBox *>(opt)) {
+ switch (sc) {
+ case SC_GroupBoxFrame:
+ // FALL THROUGH
+ case SC_GroupBoxContents: {
+ int topMargin = 0;
+ int topHeight = 0;
+ int verticalAlignment = styleHint(SH_GroupBox_TextLabelVerticalAlignment, groupBox, widget);
+ if (groupBox->text.size() || (groupBox->subControls & QStyle::SC_GroupBoxCheckBox)) {
+ topHeight = groupBox->fontMetrics.height();
+ if (verticalAlignment & Qt::AlignVCenter)
+ topMargin = topHeight / 2;
+ else if (verticalAlignment & Qt::AlignTop)
+ topMargin = topHeight;
+ }
+
+ QRect frameRect = groupBox->rect;
+ frameRect.setTop(topMargin);
+
+ if (sc == SC_GroupBoxFrame) {
+ ret = frameRect;
+ break;
+ }
+
+ int frameWidth = 0;
+ if (!(widget && widget->inherits("Q3GroupBox"))
+ && ((groupBox->features & QStyleOptionFrameV2::Flat) == 0)) {
+ frameWidth = pixelMetric(PM_DefaultFrameWidth, groupBox, widget);
+ }
+ ret = frameRect.adjusted(frameWidth, frameWidth + topHeight - topMargin,
+ -frameWidth, -frameWidth);
+ break;
+ }
+ case SC_GroupBoxCheckBox:
+ // FALL THROUGH
+ case SC_GroupBoxLabel: {
+ QFontMetrics fontMetrics = groupBox->fontMetrics;
+ int h = fontMetrics.height();
+ int tw = fontMetrics.size(Qt::TextShowMnemonic, groupBox->text + QLatin1Char(' ')).width();
+ int marg = (groupBox->features & QStyleOptionFrameV2::Flat) ? 0 : 8;
+ ret = groupBox->rect.adjusted(marg, 0, -marg, 0);
+ ret.setHeight(h);
+
+ int indicatorWidth = pixelMetric(PM_IndicatorWidth, opt, widget);
+ int indicatorSpace = pixelMetric(PM_CheckBoxLabelSpacing, opt, widget) - 1;
+ bool hasCheckBox = groupBox->subControls & QStyle::SC_GroupBoxCheckBox;
+ int checkBoxSize = hasCheckBox ? (indicatorWidth + indicatorSpace) : 0;
+
+ // Adjusted rect for label + indicatorWidth + indicatorSpace
+ QRect totalRect = alignedRect(groupBox->direction, groupBox->textAlignment,
+ QSize(tw + checkBoxSize, h), ret);
+
+ // Adjust totalRect if checkbox is set
+ if (hasCheckBox) {
+ bool ltr = groupBox->direction == Qt::LeftToRight;
+ int left = 0;
+ // Adjust for check box
+ if (sc == SC_GroupBoxCheckBox) {
+ int indicatorHeight = pixelMetric(PM_IndicatorHeight, opt, widget);
+ left = ltr ? totalRect.left() : (totalRect.right() - indicatorWidth);
+ int top = totalRect.top() + (fontMetrics.height() - indicatorHeight) / 2;
+ totalRect.setRect(left, top, indicatorWidth, indicatorHeight);
+ // Adjust for label
+ } else {
+ left = ltr ? (totalRect.left() + checkBoxSize - 2) : totalRect.left();
+ totalRect.setRect(left, totalRect.top(),
+ totalRect.width() - checkBoxSize, totalRect.height());
+ }
+ }
+ ret = totalRect;
+ break;
+ }
+ default:
+ break;
+ }
+ }
+ break;
+ }
+#endif // QT_NO_GROUPBOX
+#ifndef QT_NO_WORKSPACE
+ case CC_MdiControls:
+ {
+ int numSubControls = 0;
+ if (opt->subControls & SC_MdiCloseButton)
+ ++numSubControls;
+ if (opt->subControls & SC_MdiMinButton)
+ ++numSubControls;
+ if (opt->subControls & SC_MdiNormalButton)
+ ++numSubControls;
+ if (numSubControls == 0)
+ break;
+
+ int buttonWidth = opt->rect.width()/ numSubControls - 1;
+ int offset = 0;
+ switch (sc) {
+ case SC_MdiCloseButton:
+ // Only one sub control, no offset needed.
+ if (numSubControls == 1)
+ break;
+ offset += buttonWidth + 2;
+ //FALL THROUGH
+ case SC_MdiNormalButton:
+ // No offset needed if
+ // 1) There's only one sub control
+ // 2) We have a close button and a normal button (offset already added in SC_MdiClose)
+ if (numSubControls == 1 || (numSubControls == 2 && !(opt->subControls & SC_MdiMinButton)))
+ break;
+ if (opt->subControls & SC_MdiNormalButton)
+ offset += buttonWidth;
+ break;
+ default:
+ break;
+ }
+
+ // Subtract one pixel if we only have one sub control. At this point
+ // buttonWidth is the actual width + 1 pixel margin, but we don't want the
+ // margin when there are no other controllers.
+ if (numSubControls == 1)
+ --buttonWidth;
+ ret = QRect(offset, 0, buttonWidth, opt->rect.height());
+ break;
+ }
+#endif // QT_NO_WORKSPACE
+ default:
+ qWarning("QCommonStyle::subControlRect: Case %d not handled", cc);
+ }
+ return ret;
+}
+
+/*! \reimp */
+int QCommonStyle::pixelMetric(PixelMetric m, const QStyleOption *opt, const QWidget *widget) const
+{
+ int ret;
+
+ switch (m) {
+ case PM_FocusFrameVMargin:
+ case PM_FocusFrameHMargin:
+ ret = 2;
+ break;
+ case PM_MenuBarVMargin:
+ case PM_MenuBarHMargin:
+ ret = 0;
+ break;
+ case PM_DialogButtonsSeparator:
+ ret = 5;
+ break;
+ case PM_DialogButtonsButtonWidth:
+ ret = 70;
+ break;
+ case PM_DialogButtonsButtonHeight:
+ ret = 30;
+ break;
+ case PM_CheckListControllerSize:
+ case PM_CheckListButtonSize:
+ ret = 16;
+ break;
+ case PM_TitleBarHeight: {
+ if (const QStyleOptionTitleBar *tb = qstyleoption_cast<const QStyleOptionTitleBar *>(opt)) {
+ if ((tb->titleBarFlags & Qt::WindowType_Mask) == Qt::Tool) {
+ ret = qMax(widget ? widget->fontMetrics().lineSpacing() : opt->fontMetrics.lineSpacing(), 16);
+#ifndef QT_NO_DOCKWIDGET
+ } else if (qobject_cast<const QDockWidget*>(widget)) {
+ ret = qMax(widget->fontMetrics().lineSpacing(), 13);
+#endif
+ } else {
+ ret = qMax(widget ? widget->fontMetrics().lineSpacing() : opt->fontMetrics.lineSpacing(), 18);
+ }
+ } else {
+ ret = 18;
+ }
+
+ break; }
+ case PM_ScrollBarSliderMin:
+ ret = 9;
+ break;
+
+ case PM_ButtonMargin:
+ ret = 6;
+ break;
+
+ case PM_DockWidgetTitleBarButtonMargin:
+ ret = 2;
+ break;
+
+ case PM_ButtonDefaultIndicator:
+ ret = 0;
+ break;
+
+ case PM_MenuButtonIndicator:
+ ret = 12;
+ break;
+
+ case PM_ButtonShiftHorizontal:
+ case PM_ButtonShiftVertical:
+
+ case PM_DefaultFrameWidth:
+ ret = 2;
+ break;
+
+ case PM_ComboBoxFrameWidth:
+ case PM_SpinBoxFrameWidth:
+ case PM_MenuPanelWidth:
+ case PM_TabBarBaseOverlap:
+ case PM_TabBarBaseHeight:
+ ret = pixelMetric(PM_DefaultFrameWidth, opt, widget);
+ break;
+
+ case PM_MdiSubWindowFrameWidth:
+ ret = 4;
+ break;
+
+ case PM_MdiSubWindowMinimizedWidth:
+ ret = 196;
+ break;
+
+#ifndef QT_NO_SCROLLBAR
+ case PM_ScrollBarExtent:
+ if (const QStyleOptionSlider *sb = qstyleoption_cast<const QStyleOptionSlider *>(opt)) {
+ int s = sb->orientation == Qt::Horizontal ?
+ QApplication::globalStrut().height()
+ : QApplication::globalStrut().width();
+ ret = qMax(16, s);
+ } else {
+ ret = 16;
+ }
+ break;
+#endif
+ case PM_MaximumDragDistance:
+ ret = -1;
+ break;
+
+#ifndef QT_NO_SLIDER
+ case PM_SliderThickness:
+ ret = 16;
+ break;
+
+ case PM_SliderTickmarkOffset:
+ if (const QStyleOptionSlider *sl = qstyleoption_cast<const QStyleOptionSlider *>(opt)) {
+ int space = (sl->orientation == Qt::Horizontal) ? sl->rect.height()
+ : sl->rect.width();
+ int thickness = pixelMetric(PM_SliderControlThickness, sl, widget);
+ int ticks = sl->tickPosition;
+
+ if (ticks == QSlider::TicksBothSides)
+ ret = (space - thickness) / 2;
+ else if (ticks == QSlider::TicksAbove)
+ ret = space - thickness;
+ else
+ ret = 0;
+ } else {
+ ret = 0;
+ }
+ break;
+
+ case PM_SliderSpaceAvailable:
+ if (const QStyleOptionSlider *sl = qstyleoption_cast<const QStyleOptionSlider *>(opt)) {
+ if (sl->orientation == Qt::Horizontal)
+ ret = sl->rect.width() - pixelMetric(PM_SliderLength, sl, widget);
+ else
+ ret = sl->rect.height() - pixelMetric(PM_SliderLength, sl, widget);
+ } else {
+ ret = 0;
+ }
+ break;
+#endif // QT_NO_SLIDER
+#ifndef QT_NO_DOCKWIDGET
+ case PM_DockWidgetSeparatorExtent:
+ ret = 6;
+ break;
+
+ case PM_DockWidgetHandleExtent:
+ ret = 8;
+ break;
+ case PM_DockWidgetTitleMargin:
+ ret = 0;
+ break;
+ case PM_DockWidgetFrameWidth:
+ ret = 1;
+ break;
+#endif // QT_NO_DOCKWIDGET
+
+ case PM_SpinBoxSliderHeight:
+ case PM_MenuBarPanelWidth:
+ ret = 2;
+ break;
+
+ case PM_MenuBarItemSpacing:
+ ret = 0;
+ break;
+
+#ifndef QT_NO_TOOLBAR
+ case PM_ToolBarFrameWidth:
+ ret = 1;
+ break;
+
+ case PM_ToolBarItemMargin:
+ ret = 0;
+ break;
+
+ case PM_ToolBarItemSpacing:
+ ret = 4;
+ break;
+
+ case PM_ToolBarHandleExtent:
+ ret = 8;
+ break;
+
+ case PM_ToolBarSeparatorExtent:
+ ret = 6;
+ break;
+
+ case PM_ToolBarExtensionExtent:
+ ret = 12;
+ break;
+#endif // QT_NO_TOOLBAR
+
+#ifndef QT_NO_TABBAR
+ case PM_TabBarTabOverlap:
+ ret = 3;
+ break;
+
+ case PM_TabBarTabHSpace:
+ ret = 24;
+ break;
+
+ case PM_TabBarTabShiftHorizontal:
+ ret = 0;
+ break;
+
+ case PM_TabBarTabShiftVertical:
+ ret = 2;
+ break;
+
+ case PM_TabBarTabVSpace: {
+ const QStyleOptionTab *tb = qstyleoption_cast<const QStyleOptionTab *>(opt);
+ if (tb && (tb->shape == QTabBar::RoundedNorth || tb->shape == QTabBar::RoundedSouth
+ || tb->shape == QTabBar::RoundedWest || tb->shape == QTabBar::RoundedEast))
+ ret = 8;
+ else
+ if(tb && (tb->shape == QTabBar::TriangularWest || tb->shape == QTabBar::TriangularEast))
+ ret = 3;
+ else
+ ret = 2;
+ break; }
+#endif
+
+ case PM_ProgressBarChunkWidth:
+ ret = 9;
+ break;
+
+ case PM_IndicatorWidth:
+ ret = 13;
+ break;
+
+ case PM_IndicatorHeight:
+ ret = 13;
+ break;
+
+ case PM_ExclusiveIndicatorWidth:
+ ret = 12;
+ break;
+
+ case PM_ExclusiveIndicatorHeight:
+ ret = 12;
+ break;
+
+ case PM_MenuTearoffHeight:
+ ret = 10;
+ break;
+
+ case PM_MenuScrollerHeight:
+ ret = 10;
+ break;
+
+ case PM_MenuDesktopFrameWidth:
+ case PM_MenuHMargin:
+ case PM_MenuVMargin:
+ ret = 0;
+ break;
+
+ case PM_HeaderMargin:
+ ret = 4;
+ break;
+ case PM_HeaderMarkSize:
+ ret = 32;
+ break;
+ case PM_HeaderGripMargin:
+ ret = 4;
+ break;
+ case PM_TabBarScrollButtonWidth:
+ ret = 16;
+ break;
+ case PM_LayoutLeftMargin:
+ case PM_LayoutTopMargin:
+ case PM_LayoutRightMargin:
+ case PM_LayoutBottomMargin:
+ {
+ bool isWindow = false;
+ if (opt) {
+ isWindow = (opt->state & State_Window);
+ } else if (widget) {
+ isWindow = widget->isWindow();
+ }
+ ret = pixelMetric(isWindow ? PM_DefaultTopLevelMargin : PM_DefaultChildMargin);
+ }
+ break;
+ case PM_LayoutHorizontalSpacing:
+ case PM_LayoutVerticalSpacing:
+ ret = pixelMetric(PM_DefaultLayoutSpacing);
+ break;
+
+ case PM_DefaultTopLevelMargin:
+ ret = 11;
+ break;
+ case PM_DefaultChildMargin:
+ ret = 9;
+ break;
+ case PM_DefaultLayoutSpacing:
+ ret = 6;
+ break;
+
+ case PM_TabBarIconSize:
+ case PM_ToolBarIconSize:
+ case PM_ListViewIconSize:
+ ret = pixelMetric(PM_SmallIconSize, opt, widget);
+ break;
+
+ case PM_ButtonIconSize:
+ case PM_SmallIconSize:
+ ret = 16;
+ break;
+ case PM_IconViewIconSize:
+ ret = pixelMetric(PM_LargeIconSize, opt, widget);
+ break;
+
+ case PM_LargeIconSize:
+ ret = 32;
+ break;
+
+ case PM_ToolTipLabelFrameWidth:
+ ret = 1;
+ break;
+ case PM_CheckBoxLabelSpacing:
+ case PM_RadioButtonLabelSpacing:
+ ret = 6;
+ break;
+ case PM_SizeGripSize:
+ ret = 13;
+ break;
+ case PM_MessageBoxIconSize:
+ ret = 32;
+ break;
+ case PM_TextCursorWidth:
+ ret = 1;
+ break;
+ case PM_TabBar_ScrollButtonOverlap:
+ ret = 1;
+ break;
+ case PM_TabCloseIndicatorWidth:
+ case PM_TabCloseIndicatorHeight:
+ ret = 16;
+ break;
+ case PM_ScrollView_ScrollBarSpacing:
+ ret = 2 * pixelMetric(PM_DefaultFrameWidth, opt, widget);
+ break;
+ case PM_SubMenuOverlap:
+ ret = -pixelMetric(QStyle::PM_MenuPanelWidth, opt, widget);
+ break;
+ default:
+ ret = 0;
+ break;
+ }
+
+ return ret;
+}
+
+/*!
+ \reimp
+*/
+QSize QCommonStyle::sizeFromContents(ContentsType ct, const QStyleOption *opt,
+ const QSize &csz, const QWidget *widget) const
+{
+ Q_D(const QCommonStyle);
+ QSize sz(csz);
+ switch (ct) {
+ case CT_PushButton:
+ if (const QStyleOptionButton *btn = qstyleoption_cast<const QStyleOptionButton *>(opt)) {
+ int w = csz.width(),
+ h = csz.height(),
+ bm = pixelMetric(PM_ButtonMargin, btn, widget),
+ fw = pixelMetric(PM_DefaultFrameWidth, btn, widget) * 2;
+ w += bm + fw;
+ h += bm + fw;
+ if (btn->features & QStyleOptionButton::AutoDefaultButton){
+ int dbw = pixelMetric(PM_ButtonDefaultIndicator, btn, widget) * 2;
+ w += dbw;
+ h += dbw;
+ }
+ sz = QSize(w, h);
+ }
+ break;
+ case CT_RadioButton:
+ case CT_CheckBox:
+ if (const QStyleOptionButton *btn = qstyleoption_cast<const QStyleOptionButton *>(opt)) {
+ bool isRadio = (ct == CT_RadioButton);
+
+ int w = pixelMetric(isRadio ? PM_ExclusiveIndicatorWidth
+ : PM_IndicatorWidth, btn, widget);
+ int h = pixelMetric(isRadio ? PM_ExclusiveIndicatorHeight
+ : PM_IndicatorHeight, btn, widget);
+
+ int margins = 0;
+ // we add 4 pixels for label margins
+ if (btn->icon.isNull() || !btn->text.isEmpty())
+ margins = 4 + pixelMetric(isRadio ? PM_RadioButtonLabelSpacing
+ : PM_CheckBoxLabelSpacing, opt, widget);
+ sz += QSize(w + margins, 4);
+ sz.setHeight(qMax(sz.height(), h));
+ }
+ break;
+#ifndef QT_NO_MENU
+ case CT_MenuItem:
+ if (const QStyleOptionMenuItem *mi = qstyleoption_cast<const QStyleOptionMenuItem *>(opt)) {
+ bool checkable = mi->menuHasCheckableItems;
+ int maxpmw = mi->maxIconWidth;
+ int w = sz.width(), h = sz.height();
+ if (mi->menuItemType == QStyleOptionMenuItem::Separator) {
+ w = 10;
+ h = 2;
+ } else {
+ h = mi->fontMetrics.height() + 8;
+ if (!mi->icon.isNull()) {
+ int iconExtent = pixelMetric(PM_SmallIconSize);
+ h = qMax(h, mi->icon.actualSize(QSize(iconExtent, iconExtent)).height() + 4);
+ }
+ }
+ if (mi->text.contains(QLatin1Char('\t')))
+ w += 12;
+ if (maxpmw > 0)
+ w += maxpmw + 6;
+ if (checkable && maxpmw < 20)
+ w += 20 - maxpmw;
+ if (checkable || maxpmw > 0)
+ w += 2;
+ w += 12;
+ sz = QSize(w, h);
+ }
+ break;
+#endif // QT_NO_MENU
+#ifndef QT_NO_TOOLBUTTON
+ case CT_ToolButton:
+ sz = QSize(sz.width() + 6, sz.height() + 5);
+ break;
+#endif // QT_NO_TOOLBUTTON
+#ifndef QT_NO_COMBOBOX
+ case CT_ComboBox:
+ if (const QStyleOptionComboBox *cmb = qstyleoption_cast<const QStyleOptionComboBox *>(opt)) {
+ int fw = cmb->frame ? pixelMetric(PM_ComboBoxFrameWidth, opt, widget) * 2 : 0;
+ const int textMargins = 2*(pixelMetric(PM_FocusFrameHMargin) + 1);
+ // QItemDelegate::sizeHint expands the textMargins two times, thus the 2*textMargins...
+ int other = qMax(23, 2*textMargins + pixelMetric(QStyle::PM_ScrollBarExtent, opt, widget));
+ sz = QSize(sz.width() + fw + other, sz.height() + fw);
+ }
+ break;
+#endif // QT_NO_COMBOBOX
+ case CT_HeaderSection:
+ if (const QStyleOptionHeader *hdr = qstyleoption_cast<const QStyleOptionHeader *>(opt)) {
+ bool nullIcon = hdr->icon.isNull();
+ int margin = pixelMetric(QStyle::PM_HeaderMargin, hdr, widget);
+ int iconSize = nullIcon ? 0 : pixelMetric(QStyle::PM_SmallIconSize, hdr, widget);
+ QSize txt = hdr->fontMetrics.size(0, hdr->text);
+ sz.setHeight(margin + qMax(iconSize, txt.height()) + margin);
+ sz.setWidth((nullIcon ? 0 : margin) + iconSize
+ + (hdr->text.isNull() ? 0 : margin) + txt.width() + margin);
+ }
+ break;
+ case CT_TabWidget:
+ sz += QSize(4, 4);
+ break;
+ case CT_LineEdit:
+ if (const QStyleOptionFrame *f = qstyleoption_cast<const QStyleOptionFrame *>(opt))
+ sz += QSize(2*f->lineWidth, 2*f->lineWidth);
+ break;
+#ifndef QT_NO_GROUPBOX
+ case CT_GroupBox:
+ if (const QGroupBox *grb = static_cast<const QGroupBox *>(widget))
+ sz += QSize(!grb->isFlat() ? 16 : 0, 0);
+ break;
+#endif // QT_NO_GROUPBOX
+ case CT_MdiControls:
+ if (const QStyleOptionComplex *styleOpt = qstyleoption_cast<const QStyleOptionComplex *>(opt)) {
+ int width = 1;
+ if (styleOpt->subControls & SC_MdiMinButton)
+ width += 16 + 1;
+ if (styleOpt->subControls & SC_MdiNormalButton)
+ width += 16 + 1;
+ if (styleOpt->subControls & SC_MdiCloseButton)
+ width += 16 + 1;
+ sz = QSize(width, 16);
+ } else {
+ sz = QSize(52, 16);
+ }
+ break;
+#ifndef QT_NO_ITEMVIEWS
+ case CT_ItemViewItem:
+ if (const QStyleOptionViewItemV4 *vopt = qstyleoption_cast<const QStyleOptionViewItemV4 *>(opt)) {
+ QRect decorationRect, displayRect, checkRect;
+ d->viewItemLayout(vopt, &checkRect, &decorationRect, &displayRect, true);
+ sz = (decorationRect|displayRect|checkRect).size();
+ }
+ break;
+#endif // QT_NO_ITEMVIEWS
+ case CT_ScrollBar:
+ case CT_MenuBar:
+ case CT_Menu:
+ case CT_MenuBarItem:
+ case CT_Q3Header:
+ case CT_Slider:
+ case CT_ProgressBar:
+ case CT_TabBarTab:
+ // just return the contentsSize for now
+ // fall through intended
+ default:
+ break;
+ }
+ return sz;
+}
+
+
+/*! \reimp */
+int QCommonStyle::styleHint(StyleHint sh, const QStyleOption *opt, const QWidget *widget,
+ QStyleHintReturn *hret) const
+{
+ int ret = 0;
+
+ switch (sh) {
+ case SH_Menu_KeyboardSearch:
+ ret = false;
+ break;
+ case SH_Slider_AbsoluteSetButtons:
+ ret = Qt::MidButton;
+ break;
+ case SH_Slider_PageSetButtons:
+ ret = Qt::LeftButton;
+ break;
+ case SH_ScrollBar_ContextMenu:
+ ret = true;
+ break;
+ case SH_DialogButtons_DefaultButton: // This value not used anywhere.
+ ret = QDialogButtonBox::AcceptRole;
+ break;
+#ifndef QT_NO_GROUPBOX
+ case SH_GroupBox_TextLabelVerticalAlignment:
+ ret = Qt::AlignVCenter;
+ break;
+
+ case SH_GroupBox_TextLabelColor:
+ ret = opt ? int(opt->palette.color(QPalette::Text).rgba()) : 0;
+ break;
+#endif // QT_NO_GROUPBOX
+
+ case SH_Q3ListViewExpand_SelectMouseType:
+ case SH_TabBar_SelectMouseType:
+ ret = QEvent::MouseButtonPress;
+ break;
+
+#ifdef QT3_SUPPORT
+ case SH_GUIStyle:
+ ret = Qt::WindowsStyle;
+ break;
+#endif
+
+ case SH_TabBar_Alignment:
+ case SH_Header_ArrowAlignment:
+ ret = Qt::AlignLeft;
+ break;
+
+ case SH_TitleBar_AutoRaise:
+ ret = false;
+ break;
+
+ case SH_Menu_SubMenuPopupDelay:
+ ret = 256;
+ break;
+
+ case SH_ProgressDialog_TextLabelAlignment:
+ ret = Qt::AlignCenter;
+ break;
+
+ case SH_BlinkCursorWhenTextSelected:
+ ret = 1;
+ break;
+
+ case SH_Table_GridLineColor:
+ if (opt)
+ ret = opt->palette.color(QPalette::Mid).rgb();
+ else
+ ret = -1;
+ break;
+ case SH_LineEdit_PasswordCharacter: {
+ const QFontMetrics &fm = opt ? opt->fontMetrics
+ : (widget ? widget->fontMetrics() : QFontMetrics(QFont()));
+ ret = 0;
+ if (fm.inFont(QChar(0x25CF))) {
+ ret = 0x25CF;
+ } else if (fm.inFont(QChar(0x2022))) {
+ ret = 0x2022;
+ } else {
+ ret = '*';
+ }
+ break;
+ }
+
+
+ case SH_ToolBox_SelectedPageTitleBold:
+ ret = 1;
+ break;
+
+ case SH_UnderlineShortcut:
+ ret = 1;
+ break;
+
+ case SH_SpinBox_ClickAutoRepeatRate:
+ ret = 150;
+ break;
+
+ case SH_SpinBox_ClickAutoRepeatThreshold:
+ ret = 500;
+ break;
+
+ case SH_SpinBox_KeyPressAutoRepeatRate:
+ ret = 75;
+ break;
+
+ case SH_Menu_SelectionWrap:
+ ret = true;
+ break;
+
+ case SH_Menu_FillScreenWithScroll:
+ ret = true;
+ break;
+
+ case SH_ToolTipLabel_Opacity:
+ ret = 255;
+ break;
+
+ case SH_Button_FocusPolicy:
+ ret = Qt::StrongFocus;
+ break;
+
+ case SH_MenuBar_DismissOnSecondClick:
+ ret = 1;
+ break;
+
+ case SH_MessageBox_UseBorderForButtonSpacing:
+ ret = 0;
+ break;
+
+ case SH_ToolButton_PopupDelay:
+ ret = 600;
+ break;
+
+ case SH_FocusFrame_Mask:
+ ret = 1;
+ if (widget) {
+ if(QStyleHintReturnMask *mask = qstyleoption_cast<QStyleHintReturnMask*>(hret)) {
+ mask->region = widget->rect();
+ int vmargin = pixelMetric(QStyle::PM_FocusFrameVMargin),
+ hmargin = pixelMetric(QStyle::PM_FocusFrameHMargin);
+ mask->region -= QRect(widget->rect().adjusted(hmargin, vmargin, -hmargin, -vmargin));
+ }
+ }
+ break;
+#ifndef QT_NO_RUBBERBAND
+ case SH_RubberBand_Mask:
+ if (const QStyleOptionRubberBand *rbOpt = qstyleoption_cast<const QStyleOptionRubberBand *>(opt)) {
+ ret = 0;
+ if (rbOpt->shape == QRubberBand::Rectangle) {
+ ret = true;
+ if(QStyleHintReturnMask *mask = qstyleoption_cast<QStyleHintReturnMask*>(hret)) {
+ mask->region = opt->rect;
+ int margin = pixelMetric(PM_DefaultFrameWidth) * 2;
+ mask->region -= opt->rect.adjusted(margin, margin, -margin, -margin);
+ }
+ }
+ }
+ break;
+#endif // QT_NO_RUBBERBAND
+ case SH_SpinControls_DisableOnBounds:
+ ret = 1;
+ break;
+
+ case SH_Dial_BackgroundRole:
+ ret = QPalette::Window;
+ break;
+
+ case SH_ComboBox_LayoutDirection:
+ ret = opt ? opt->direction : Qt::LeftToRight;
+ break;
+
+ case SH_ItemView_EllipsisLocation:
+ ret = Qt::AlignTrailing;
+ break;
+
+ case SH_ItemView_ShowDecorationSelected:
+ ret = false;
+ break;
+
+ case SH_ItemView_ActivateItemOnSingleClick:
+ ret = false;
+ break;
+
+ case SH_TitleBar_ModifyNotification:
+ ret = true;
+ break;
+ case SH_ScrollBar_RollBetweenButtons:
+ ret = false;
+ break;
+ case SH_TabBar_ElideMode:
+ ret = Qt::ElideNone;
+ break;
+ case SH_DialogButtonLayout:
+ ret = QDialogButtonBox::WinLayout;
+#ifdef Q_WS_X11
+ if (X11->desktopEnvironment == DE_KDE)
+ ret = QDialogButtonBox::KdeLayout;
+ else if (X11->desktopEnvironment == DE_GNOME)
+ ret = QDialogButtonBox::GnomeLayout;
+#endif
+ break;
+ case SH_ComboBox_PopupFrameStyle:
+ ret = QFrame::StyledPanel | QFrame::Plain;
+ break;
+ case SH_MessageBox_TextInteractionFlags:
+ ret = Qt::LinksAccessibleByMouse;
+ break;
+ case SH_DialogButtonBox_ButtonsHaveIcons:
+ ret = 0;
+ break;
+ case SH_SpellCheckUnderlineStyle:
+ ret = QTextCharFormat::WaveUnderline;
+ break;
+ case SH_MessageBox_CenterButtons:
+ ret = true;
+ break;
+ case SH_ItemView_MovementWithoutUpdatingSelection:
+ ret = true;
+ break;
+ case SH_FocusFrame_AboveWidget:
+ ret = false;
+ break;
+#ifndef QT_NO_TABWIDGET
+ case SH_TabWidget_DefaultTabPosition:
+ ret = QTabWidget::North;
+ break;
+#endif
+ case SH_ToolBar_Movable:
+ ret = true;
+ break;
+ case SH_TextControl_FocusIndicatorTextCharFormat:
+ ret = true;
+ if (QStyleHintReturnVariant *vret = qstyleoption_cast<QStyleHintReturnVariant*>(hret)) {
+ QPen outline(opt->palette.color(QPalette::Text), 1, Qt::DotLine);
+ QTextCharFormat fmt;
+ fmt.setProperty(QTextFormat::OutlinePen, outline);
+ vret->variant = fmt;
+ }
+ break;
+#ifndef QT_NO_WIZARD
+ case SH_WizardStyle:
+ ret = QWizard::ClassicStyle;
+ break;
+#endif
+ case SH_FormLayoutWrapPolicy:
+ ret = QFormLayout::DontWrapRows;
+ break;
+ case SH_FormLayoutFieldGrowthPolicy:
+ ret = QFormLayout::AllNonFixedFieldsGrow;
+ break;
+ case SH_FormLayoutFormAlignment:
+ ret = Qt::AlignLeft | Qt::AlignTop;
+ break;
+ case SH_FormLayoutLabelAlignment:
+ ret = Qt::AlignLeft;
+ break;
+ case SH_ItemView_ArrowKeysNavigateIntoChildren:
+ ret = false;
+ break;
+ case SH_ItemView_DrawDelegateFrame:
+ ret = 0;
+ break;
+#ifndef QT_NO_TABBAR
+ case SH_TabBar_CloseButtonPosition:
+ ret = QTabBar::RightSide;
+ break;
+#endif
+ case SH_DockWidget_ButtonsHaveFrame:
+ ret = true;
+ break;
+ default:
+ ret = 0;
+ break;
+ }
+
+ return ret;
+}
+
+/*! \reimp */
+QPixmap QCommonStyle::standardPixmap(StandardPixmap sp, const QStyleOption *option,
+ const QWidget *widget) const
+{
+#ifdef QT_NO_IMAGEFORMAT_PNG
+ Q_UNUSED(option);
+ Q_UNUSED(widget);
+ Q_UNUSED(sp);
+#else
+#ifdef Q_WS_X11
+ Q_D(const QCommonStyle);
+ QPixmap pixmap;
+ if (qApp->desktopSettingsAware()) {
+ d->lookupIconTheme();
+ switch (sp) {
+ case SP_DirHomeIcon:
+ {
+ pixmap = d->findIcon(16, QLatin1String("folder_home.png"));
+ if (!pixmap.isNull())
+ return pixmap;
+ break;
+ }
+ case SP_MessageBoxInformation:
+ {
+ pixmap = d->findIcon(32, QLatin1String("messagebox_info.png"));
+ if (!pixmap.isNull())
+ return pixmap;
+ break;
+ }
+ case SP_MessageBoxWarning:
+ {
+ pixmap = d->findIcon(32, QLatin1String("messagebox_warning.png"));
+ if (!pixmap.isNull())
+ return pixmap;
+ break;
+ }
+ case SP_MessageBoxCritical:
+ {
+ pixmap = d->findIcon(32, QLatin1String("messagebox_critical.png"));
+ if (!pixmap.isNull())
+ return pixmap;
+ break;
+ }
+ case SP_MessageBoxQuestion:
+ {
+ pixmap = d->findIcon(32, QLatin1String("help.png"));
+ if (!pixmap.isNull())
+ return pixmap;
+ break;
+ }
+ case SP_DialogOpenButton:
+ case SP_DirOpenIcon:
+ {
+ pixmap = d->findIcon(16, QLatin1String("folder-open.png"));
+ if (pixmap.isNull())
+ pixmap = d->findIcon(16, QLatin1String("folder_open.png"));
+ if (!pixmap.isNull())
+ return pixmap;
+ break;
+ }
+ case SP_FileIcon:
+ {
+ pixmap = d->findIcon(16, QLatin1String("text-x-generic.png"));
+ if (pixmap.isNull())
+ pixmap = d->findIcon(16, QLatin1String("empty.png"));
+ if (!pixmap.isNull())
+ return pixmap;
+ break;
+ }
+ case SP_FileLinkIcon:
+ {
+ pixmap = d->findIcon(16, QLatin1String("link_overlay.png"));
+ if (!pixmap.isNull()) {
+ QPixmap fileIcon = d->findIcon(16, QLatin1String("text-x-generic.png"));
+ if (fileIcon.isNull())
+ fileIcon = d->findIcon(16, QLatin1String("empty.png"));
+ if (!fileIcon.isNull()) {
+ QPainter painter(&fileIcon);
+ painter.drawPixmap(0, 0, 16, 16, pixmap);
+ return fileIcon;
+ }
+ }
+ break;
+ }
+ case SP_DirClosedIcon:
+ case SP_DirIcon:
+ {
+ pixmap = d->findIcon(16, QLatin1String("folder.png"));
+ if (!pixmap.isNull())
+ return pixmap;
+ break;
+ }
+ case SP_DirLinkIcon:
+ {
+ pixmap = d->findIcon(16, QLatin1String("link_overlay.png"));
+ if (!pixmap.isNull()) {
+ QPixmap dirIcon = d->findIcon(16, QLatin1String("folder.png"));
+ if (!dirIcon.isNull()) {
+ QPainter painter(&dirIcon);
+ painter.drawPixmap(0, 0, 16, 16, pixmap);
+ return dirIcon;
+ }
+ }
+ break;
+ }
+ case SP_DriveFDIcon:
+ {
+ pixmap = d->findIcon(16, QLatin1String("media-floppy.png"));
+ if (pixmap.isNull())
+ pixmap = d->findIcon(16, QLatin1String("3floppy_unmount.png"));
+ if (!pixmap.isNull())
+ return pixmap;
+ break;
+ }
+ case SP_ComputerIcon:
+ {
+ pixmap = d->findIcon(16, QLatin1String("computer.png"));
+ if (pixmap.isNull())
+ pixmap = d->findIcon(16, QLatin1String("system.png"));
+ if (!pixmap.isNull())
+ return pixmap;
+ break;
+ }
+ case SP_DesktopIcon:
+ {
+ pixmap = d->findIcon(16, QLatin1String("user-desktop.png"));
+ if (pixmap.isNull())
+ pixmap = d->findIcon(16, QLatin1String("desktop.png"));
+ if (!pixmap.isNull())
+ return pixmap;
+ break;
+ }
+ case SP_TrashIcon:
+ {
+ pixmap = d->findIcon(16, QLatin1String("user-trash.png"));
+ if (pixmap.isNull())
+ pixmap = d->findIcon(16, QLatin1String("trashcan_empty.png"));
+ if (!pixmap.isNull())
+ return pixmap;
+ break;
+ }
+ case SP_DriveCDIcon:
+ case SP_DriveDVDIcon:
+ {
+ pixmap = d->findIcon(16, QLatin1String("media-optical.png"));
+ if (pixmap.isNull())
+ pixmap = d->findIcon(16, QLatin1String("cdrom_unmount.png"));
+ if (!pixmap.isNull())
+ return pixmap;
+ break;
+ }
+ case SP_DriveHDIcon:
+ {
+ pixmap = d->findIcon(16, QLatin1String("drive-harddisk.png"));
+ if (pixmap.isNull())
+ pixmap = d->findIcon(16, QLatin1String("hdd_unmount.png"));
+ if (!pixmap.isNull())
+ return pixmap;
+ break;
+ }
+ case SP_FileDialogToParent:
+ {
+ pixmap = d->findIcon(32, QLatin1String("go-up.png"));
+ if (pixmap.isNull())
+ pixmap = d->findIcon(32, QLatin1String("up.png"));
+ if (!pixmap.isNull())
+ return pixmap;
+ break;
+ }
+ case SP_FileDialogNewFolder:
+ {
+ pixmap = d->findIcon(16, QLatin1String("folder_new.png"));
+ if (!pixmap.isNull())
+ return pixmap;
+ break;
+ }
+ case SP_ArrowUp:
+ {
+ pixmap = d->findIcon(32, QLatin1String("go-up.png"));
+ if (pixmap.isNull())
+ pixmap = d->findIcon(32, QLatin1String("up.png"));
+ if (!pixmap.isNull())
+ return pixmap;
+ break;
+ }
+ case SP_ArrowDown:
+ {
+ pixmap = d->findIcon(32, QLatin1String("go-down.png"));
+ if (pixmap.isNull())
+ pixmap = d->findIcon(32, QLatin1String("down.png"));
+ if (!pixmap.isNull())
+ return pixmap;
+ break;
+ }
+ case SP_ArrowRight:
+ {
+ pixmap = d->findIcon(32, QLatin1String("go-next.png"));
+ if (pixmap.isNull())
+ pixmap = d->findIcon(32, QLatin1String("forward.png"));
+ if (!pixmap.isNull())
+ return pixmap;
+ break;
+ }
+ case SP_ArrowLeft:
+ {
+ pixmap = d->findIcon(32, QLatin1String("go-previous.png"));
+ if (pixmap.isNull())
+ pixmap = d->findIcon(32, QLatin1String("back.png"));
+ if (!pixmap.isNull())
+ return pixmap;
+ break;
+ }
+ case SP_FileDialogDetailedView:
+ {
+ pixmap = d->findIcon(16, QLatin1String("view_detailed.png"));
+ if (!pixmap.isNull())
+ return pixmap;
+ break;
+ }
+
+ case SP_FileDialogListView:
+ {
+ pixmap = d->findIcon(16, QLatin1String("view_icon.png"));
+ if (!pixmap.isNull())
+ return pixmap;
+ break;
+ }
+ case SP_BrowserReload:
+ {
+ pixmap = d->findIcon(32, QLatin1String("reload.png"));
+ if (!pixmap.isNull())
+ return pixmap;
+ break;
+ }
+ case SP_BrowserStop:
+ {
+ pixmap = d->findIcon(32, QLatin1String("stop.png"));
+ if (!pixmap.isNull())
+ return pixmap;
+ break;
+ }
+ case SP_MediaPlay:
+ {
+ pixmap = d->findIcon(16, QLatin1String("player_play.png"));
+ if (!pixmap.isNull())
+ return pixmap;
+ break;
+ }
+ case SP_MediaPause:
+ {
+ pixmap = d->findIcon(16, QLatin1String("player_pause.png"));
+ if (!pixmap.isNull())
+ return pixmap;
+ break;
+ }
+ case SP_MediaStop:
+ {
+ pixmap = d->findIcon(16, QLatin1String("player_stop.png"));
+ if (!pixmap.isNull())
+ return pixmap;
+ break;
+ }
+ case SP_MediaSeekForward:
+ {
+ pixmap = d->findIcon(16, QLatin1String("player_fwd.png"));
+ if (!pixmap.isNull())
+ return pixmap;
+ break;
+ }
+ case SP_MediaSeekBackward:
+ {
+ pixmap = d->findIcon(16, QLatin1String("player_rew.png"));
+ if (!pixmap.isNull())
+ return pixmap;
+ break;
+ }
+ case SP_MediaSkipForward:
+ {
+ pixmap = d->findIcon(16, QLatin1String("player_end.png"));
+ if (!pixmap.isNull())
+ return pixmap;
+ break;
+ }
+ case SP_MediaSkipBackward:
+ {
+ pixmap = d->findIcon(16, QLatin1String("player_start.png"));
+ if (!pixmap.isNull())
+ return pixmap;
+ break;
+ }
+
+ default:
+ break;
+ }
+ }
+#endif //Q_WS_X11
+#endif //QT_NO_IMAGEFORMAT_PNG
+ switch (sp) {
+#ifndef QT_NO_IMAGEFORMAT_XPM
+ case SP_ToolBarHorizontalExtensionButton:
+ if (QApplication::layoutDirection() == Qt::RightToLeft) {
+ QImage im(tb_extension_arrow_h_xpm);
+ im = im.convertToFormat(QImage::Format_ARGB32).mirrored(true, false);
+ return QPixmap::fromImage(im);
+ }
+ return QPixmap(tb_extension_arrow_h_xpm);
+ case SP_ToolBarVerticalExtensionButton:
+ return QPixmap(tb_extension_arrow_v_xpm);
+ case SP_FileDialogStart:
+ return QPixmap(filedialog_start_xpm);
+ case SP_FileDialogEnd:
+ return QPixmap(filedialog_end_xpm);
+#endif
+#ifndef QT_NO_IMAGEFORMAT_PNG
+ case SP_CommandLink:
+ case SP_ArrowForward:
+ if (QApplication::layoutDirection() == Qt::RightToLeft)
+ return standardPixmap(SP_ArrowLeft, option, widget);
+ return standardPixmap(SP_ArrowRight, option, widget);
+ case SP_ArrowBack:
+ if (QApplication::layoutDirection() == Qt::RightToLeft)
+ return standardPixmap(SP_ArrowRight, option, widget);
+ return standardPixmap(SP_ArrowLeft, option, widget);
+ case SP_ArrowLeft:
+ return QPixmap(QLatin1String(":/trolltech/styles/commonstyle/images/left-16.png"));
+ case SP_ArrowRight:
+ return QPixmap(QLatin1String(":/trolltech/styles/commonstyle/images/right-16.png"));
+ case SP_ArrowUp:
+ return QPixmap(QLatin1String(":/trolltech/styles/commonstyle/images/up-16.png"));
+ case SP_ArrowDown:
+ return QPixmap(QLatin1String(":/trolltech/styles/commonstyle/images/down-16.png"));
+ case SP_FileDialogToParent:
+ return standardPixmap(SP_ArrowUp, option, widget);
+ case SP_FileDialogNewFolder:
+ return QPixmap(QLatin1String(":/trolltech/styles/commonstyle/images/newdirectory-16.png"));
+ case SP_FileDialogDetailedView:
+ return QPixmap(QLatin1String(":/trolltech/styles/commonstyle/images/viewdetailed-16.png"));
+ case SP_FileDialogInfoView:
+ return QPixmap(QLatin1String(":/trolltech/styles/commonstyle/images/fileinfo-16.png"));
+ case SP_FileDialogContentsView:
+ return QPixmap(QLatin1String(":/trolltech/styles/commonstyle/images/filecontents-16.png"));
+ case SP_FileDialogListView:
+ return QPixmap(QLatin1String(":/trolltech/styles/commonstyle/images/viewlist-16.png"));
+ case SP_FileDialogBack:
+ return standardPixmap(SP_ArrowBack, option, widget);
+ case SP_DriveHDIcon:
+ return QPixmap(QLatin1String(":/trolltech/styles/commonstyle/images/harddrive-16.png"));
+ case SP_TrashIcon:
+ return QPixmap(QLatin1String(":/trolltech/styles/commonstyle/images/trash-16.png"));
+ case SP_DriveFDIcon:
+ return QPixmap(QLatin1String(":/trolltech/styles/commonstyle/images/floppy-16.png"));
+ case SP_DriveNetIcon:
+ return QPixmap(QLatin1String(":/trolltech/styles/commonstyle/images/networkdrive-16.png"));
+ case SP_DesktopIcon:
+ return QPixmap(QLatin1String(":/trolltech/styles/commonstyle/images/desktop-16.png"));
+ case SP_ComputerIcon:
+ return QPixmap(QLatin1String(":/trolltech/styles/commonstyle/images/computer-16.png"));
+ case SP_DriveCDIcon:
+ return QPixmap(QLatin1String(":/trolltech/styles/commonstyle/images/cdr-16.png"));
+ case SP_DriveDVDIcon:
+ return QPixmap(QLatin1String(":/trolltech/styles/commonstyle/images/dvd-16.png"));
+ case SP_DirHomeIcon:
+ case SP_DirOpenIcon:
+ return QPixmap(QLatin1String(":/trolltech/styles/commonstyle/images/diropen-16.png"));
+ case SP_DirIcon:
+ case SP_DirClosedIcon:
+ return QPixmap(QLatin1String(":/trolltech/styles/commonstyle/images/dirclosed-16.png"));
+ case SP_DirLinkIcon:
+ return QPixmap(QLatin1String(":/trolltech/styles/commonstyle/images/dirlink-16.png"));
+ case SP_FileIcon:
+ return QPixmap(QLatin1String(":/trolltech/styles/commonstyle/images/file-16.png"));
+ case SP_FileLinkIcon:
+ return QPixmap(QLatin1String(":/trolltech/styles/commonstyle/images/filelink-16.png"));
+ case SP_DialogOkButton:
+ return QPixmap(QLatin1String(":/trolltech/styles/commonstyle/images/standardbutton-ok-16.png"));
+ case SP_DialogCancelButton:
+ return QPixmap(QLatin1String(":/trolltech/styles/commonstyle/images/standardbutton-cancel-16.png"));
+ case SP_DialogHelpButton:
+ return QPixmap(QLatin1String(":/trolltech/styles/commonstyle/images/standardbutton-help-16.png"));
+ case SP_DialogOpenButton:
+ return QPixmap(QLatin1String(":/trolltech/styles/commonstyle/images/standardbutton-open-16.png"));
+ case SP_DialogSaveButton:
+ return QPixmap(QLatin1String(":/trolltech/styles/commonstyle/images/standardbutton-save-16.png"));
+ case SP_DialogCloseButton:
+ return QPixmap(QLatin1String(":/trolltech/styles/commonstyle/images/standardbutton-close-16.png"));
+ case SP_DialogApplyButton:
+ return QPixmap(QLatin1String(":/trolltech/styles/commonstyle/images/standardbutton-apply-16.png"));
+ case SP_DialogResetButton:
+ return QPixmap(QLatin1String(":/trolltech/styles/commonstyle/images/standardbutton-clear-16.png"));
+ case SP_DialogDiscardButton:
+ return QPixmap(QLatin1String(":/trolltech/styles/commonstyle/images/standardbutton-delete-16.png"));
+ case SP_DialogYesButton:
+ return QPixmap(QLatin1String(":/trolltech/styles/commonstyle/images/standardbutton-yes-16.png"));
+ case SP_DialogNoButton:
+ return QPixmap(QLatin1String(":/trolltech/styles/commonstyle/images/standardbutton-no-16.png"));
+ case SP_BrowserReload:
+ return QPixmap(QLatin1String(":/trolltech/styles/commonstyle/images/refresh-24.png"));
+ case SP_BrowserStop:
+ return QPixmap(QLatin1String(":/trolltech/styles/commonstyle/images/stop-24.png"));
+ case SP_MediaPlay:
+ return QPixmap(QLatin1String(":/trolltech/styles/commonstyle/images/media-play-32.png"));
+ case SP_MediaPause:
+ return QPixmap(QLatin1String(":/trolltech/styles/commonstyle/images/media-pause-32.png"));
+ case SP_MediaStop:
+ return QPixmap(QLatin1String(":/trolltech/styles/commonstyle/images/media-stop-32.png"));
+ case SP_MediaSeekForward:
+ return QPixmap(QLatin1String(":/trolltech/styles/commonstyle/images/media-seek-forward-32.png"));
+ case SP_MediaSeekBackward:
+ return QPixmap(QLatin1String(":/trolltech/styles/commonstyle/images/media-seek-backward-32.png"));
+ case SP_MediaSkipForward:
+ return QPixmap(QLatin1String(":/trolltech/styles/commonstyle/images/media-skip-forward-32.png"));
+ case SP_MediaSkipBackward:
+ return QPixmap(QLatin1String(":/trolltech/styles/commonstyle/images/media-skip-backward-32.png"));
+ case SP_MediaVolume:
+ return QPixmap(QLatin1String(":/trolltech/styles/commonstyle/images/media-volume-16.png"));
+ case SP_MediaVolumeMuted:
+ return QPixmap(QLatin1String(":/trolltech/styles/commonstyle/images/media-volume-muted-16.png"));
+#endif // QT_NO_IMAGEFORMAT_PNG
+ default:
+ break;
+ }
+ return QPixmap();
+}
+
+/*!
+ \internal
+*/
+QIcon QCommonStyle::standardIconImplementation(StandardPixmap standardIcon, const QStyleOption *option,
+ const QWidget *widget) const
+{
+ QIcon icon;
+#ifdef Q_WS_X11
+ Q_D(const QCommonStyle);
+ if (qApp->desktopSettingsAware()) {
+ d->lookupIconTheme();
+ QPixmap pixmap;
+ switch (standardIcon) {
+ case SP_DirHomeIcon:
+ {
+ icon = d->createIcon(QLatin1String("folder_home.png"));
+ break;
+ }
+ case SP_MessageBoxInformation:
+ {
+ icon = d->createIcon(QLatin1String("dialog-information.png"));
+ if (icon.isNull())
+ icon = d->createIcon(QLatin1String("messagebox_info.png"));
+ break;
+ }
+ case SP_MessageBoxWarning:
+ {
+ icon = d->createIcon(QLatin1String("dialog-warning.png"));
+ if (icon.isNull())
+ icon = d->createIcon(QLatin1String("messagebox_warning.png"));
+ break;
+ }
+ case SP_MessageBoxCritical:
+ {
+ icon = d->createIcon(QLatin1String("dialog-error.png"));
+ if (icon.isNull())
+ icon = d->createIcon(QLatin1String("messagebox_critical.png"));
+ break;
+ }
+ case SP_MessageBoxQuestion:
+ {
+ icon = d->createIcon(QLatin1String("help.png"));
+ break;
+ }
+ case SP_DialogOpenButton:
+ case SP_DirOpenIcon:
+ {
+ icon = d->createIcon(QLatin1String("folder-open.png"));
+ if (icon.isNull())
+ icon = d->createIcon(QLatin1String("folder_open.png"));
+ break;
+ }
+ case SP_FileIcon:
+ {
+ icon = d->createIcon(QLatin1String("text-x-generic.png"));
+ if (icon.isNull())
+ icon = d->createIcon(QLatin1String("empty.png"));
+ break;
+ }
+ case SP_DirClosedIcon:
+ case SP_DirIcon:
+ {
+ icon = d->createIcon(QLatin1String("folder.png"));
+ break;
+ }
+ case SP_DriveFDIcon:
+ {
+ icon = d->createIcon(QLatin1String("floppy_unmount.png"));
+ break;
+ }
+ case SP_ComputerIcon:
+ {
+ icon = d->createIcon(QLatin1String("computer.png"));
+ if (icon.isNull())
+ icon = d->createIcon(QLatin1String("system.png"));
+ break;
+ }
+ case SP_DesktopIcon:
+ {
+ icon = d->createIcon(QLatin1String("user-desktop.png"));
+ break;
+ }
+ case SP_TrashIcon:
+ {
+ icon = d->createIcon(QLatin1String("user-trash.png"));
+ break;
+ }
+ case SP_DriveCDIcon:
+ case SP_DriveDVDIcon:
+ {
+ icon = d->createIcon(QLatin1String("media-optical.png"));
+ if (icon.isNull())
+ icon = d->createIcon(QLatin1String("cdrom_unmount.png"));
+ break;
+ }
+ case SP_DriveHDIcon:
+ {
+ icon = d->createIcon(QLatin1String("drive-harddisk.png"));
+ if (icon.isNull())
+ icon = d->createIcon(QLatin1String("hdd_unmount.png"));
+ break;
+ }
+ case SP_FileDialogToParent:
+ {
+ icon = d->createIcon(QLatin1String("go-up.png"));
+ if (icon.isNull())
+ icon = d->createIcon(QLatin1String("up.png"));
+ break;
+ }
+ case SP_FileDialogNewFolder:
+ {
+ icon = d->createIcon(QLatin1String("folder_new.png"));
+ break;
+ }
+ case SP_ArrowUp:
+ {
+ icon = d->createIcon(QLatin1String("go-up.png"));
+ if (icon.isNull())
+ icon = d->createIcon(QLatin1String("up.png"));
+ break;
+ }
+ case SP_ArrowDown:
+ {
+ icon = d->createIcon(QLatin1String("go-down.png"));
+ if (icon.isNull())
+ icon = d->createIcon(QLatin1String("down.png"));
+ break;
+ }
+ case SP_ArrowRight:
+ {
+ icon = d->createIcon(QLatin1String("go-next.png"));
+ if (icon.isNull())
+ icon = d->createIcon(QLatin1String("forward.png"));
+ break;
+ }
+ case SP_ArrowLeft:
+ {
+ icon = d->createIcon(QLatin1String("go-previous.png"));
+ if (icon.isNull())
+ icon = d->createIcon(QLatin1String("back.png"));
+ break;
+ }
+ case SP_FileDialogDetailedView:
+ {
+ icon = d->createIcon(QLatin1String("view-list-details.png"));
+ if (icon.isNull())
+ icon = d->createIcon(QLatin1String("view_detailed.png"));
+ break;
+ }
+ case SP_FileDialogListView:
+ {
+ icon = d->createIcon(QLatin1String("view-list-icons.png"));
+ if (icon.isNull())
+ icon = d->createIcon(QLatin1String("view_icon.png"));
+ break;
+ }
+ case SP_BrowserReload:
+ {
+ icon = d->createIcon(QLatin1String("view-refresh.png"));
+ if (icon.isNull())
+ icon = d->createIcon(QLatin1String("reload.png"));
+ break;
+ }
+ case SP_BrowserStop:
+ {
+ icon = d->createIcon(QLatin1String("process-stop.png"));
+ if (icon.isNull())
+ icon = d->createIcon(QLatin1String("stop.png"));
+ break;
+ }
+ case SP_MediaPlay:
+ {
+ icon = d->createIcon(QLatin1String("media-playback-start.png"));
+ if (icon.isNull())
+ icon = d->createIcon(QLatin1String("player_play.png"));
+ break;
+ }
+ case SP_MediaPause:
+ {
+ icon = d->createIcon(QLatin1String("media-playback-pause.png"));
+ if (icon.isNull())
+ icon = d->createIcon(QLatin1String("player_pause.png"));
+ break;
+ }
+ case SP_MediaStop:
+ {
+ icon = d->createIcon(QLatin1String("media-playback-stop.png"));
+ if (icon.isNull())
+ icon = d->createIcon(QLatin1String("player_stop.png"));
+ break;
+ }
+ case SP_MediaSeekForward:
+ {
+ icon = d->createIcon(QLatin1String("media-skip-forward.png"));
+ if (icon.isNull())
+ icon = d->createIcon(QLatin1String("player_fwd.png"));
+ break;
+ }
+ case SP_MediaSeekBackward:
+ {
+ icon = d->createIcon(QLatin1String("media-skip-backward.png"));
+ if (icon.isNull())
+ icon = d->createIcon(QLatin1String("player_rew.png"));
+ break;
+ }
+ case SP_MediaSkipForward:
+ {
+ icon = d->createIcon(QLatin1String("media-skip-forward.png"));
+ if (icon.isNull())
+ icon = d->createIcon(QLatin1String("player_end.png"));
+ break;
+ }
+ case SP_MediaSkipBackward:
+ {
+ icon = d->createIcon(QLatin1String("media-skip-backward.png"));
+ if (icon.isNull())
+ icon = d->createIcon(QLatin1String("player_start.png"));
+ break;
+ }
+
+ case SP_FileLinkIcon: {
+ icon = QIcon(standardPixmap(standardIcon, option, widget));
+ QPixmap pixmap = d->findIcon(32, QLatin1String("link_overlay.png"));
+ if (!pixmap.isNull()) {
+ QPixmap fileIcon = d->findIcon(32, QLatin1String("text-x-generic.png"));
+ if (fileIcon.isNull())
+ fileIcon = d->findIcon(32, QLatin1String("empty.png"));
+ if (!fileIcon.isNull()) {
+ QPainter painter(&fileIcon);
+ painter.drawPixmap(0, 0, 32, 32, pixmap);
+ icon.addPixmap(fileIcon);
+ }
+ }
+ }
+ break;
+ case SP_DirLinkIcon: {
+ icon = QIcon(standardPixmap(standardIcon, option, widget));
+ QPixmap pixmap = d->findIcon(32, QLatin1String("link_overlay.png"));
+ if (!pixmap.isNull()) {
+ QPixmap fileIcon = d->findIcon(32, QLatin1String("folder.png"));
+ if (!fileIcon.isNull()) {
+ QPainter painter(&fileIcon);
+ painter.drawPixmap(0, 0, 32, 32, pixmap);
+ icon.addPixmap(fileIcon);
+ }
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ if (!icon.isNull())
+ return icon;
+ }
+#endif//Q_WS_X11
+
+ switch (standardIcon) {
+#ifndef QT_NO_IMAGEFORMAT_PNG
+ case SP_FileDialogNewFolder:
+ icon.addFile(QLatin1String(":/trolltech/styles/commonstyle/images/newdirectory-16.png"));
+ icon.addFile(QLatin1String(":/trolltech/styles/commonstyle/images/newdirectory-32.png"));
+ icon.addFile(QLatin1String(":/trolltech/styles/commonstyle/images/newdirectory-128.png"));
+ break;
+ case SP_FileDialogBack:
+ return standardIconImplementation(SP_ArrowBack, option, widget);
+ case SP_FileDialogToParent:
+ return standardIconImplementation(SP_ArrowUp, option, widget);
+ case SP_FileDialogDetailedView:
+ icon.addFile(QLatin1String(":/trolltech/styles/commonstyle/images/viewdetailed-16.png"));
+ icon.addFile(QLatin1String(":/trolltech/styles/commonstyle/images/viewdetailed-32.png"));
+ icon.addFile(QLatin1String(":/trolltech/styles/commonstyle/images/viewdetailed-128.png"));
+ break;
+ case SP_FileDialogInfoView:
+ icon.addFile(QLatin1String(":/trolltech/styles/commonstyle/images/fileinfo-16.png"));
+ icon.addFile(QLatin1String(":/trolltech/styles/commonstyle/images/fileinfo-32.png"));
+ icon.addFile(QLatin1String(":/trolltech/styles/commonstyle/images/fileinfo-128.png"));
+ break;
+ case SP_FileDialogContentsView:
+ icon.addFile(QLatin1String(":/trolltech/styles/commonstyle/images/filecontents-16.png"));
+ icon.addFile(QLatin1String(":/trolltech/styles/commonstyle/images/filecontents-32.png"));
+ icon.addFile(QLatin1String(":/trolltech/styles/commonstyle/images/filecontents-128.png"));
+ break;
+ case SP_FileDialogListView:
+ icon.addFile(QLatin1String(":/trolltech/styles/commonstyle/images/viewlist-16.png"));
+ icon.addFile(QLatin1String(":/trolltech/styles/commonstyle/images/viewlist-32.png"));
+ icon.addFile(QLatin1String(":/trolltech/styles/commonstyle/images/viewlist-128.png"));
+ break;
+ case SP_DialogOkButton:
+ icon.addFile(QLatin1String(":/trolltech/styles/commonstyle/images/standardbutton-ok-16.png"));
+ icon.addFile(QLatin1String(":/trolltech/styles/commonstyle/images/standardbutton-ok-32.png"));
+ icon.addFile(QLatin1String(":/trolltech/styles/commonstyle/images/standardbutton-ok-128.png"));
+ break;
+ case SP_DialogCancelButton:
+ icon.addFile(QLatin1String(":/trolltech/styles/commonstyle/images/standardbutton-cancel-16.png"));
+ icon.addFile(QLatin1String(":/trolltech/styles/commonstyle/images/standardbutton-cancel-32.png"));
+ icon.addFile(QLatin1String(":/trolltech/styles/commonstyle/images/standardbutton-cancel-128.png"));
+ break;
+ case SP_DialogHelpButton:
+ icon.addFile(QLatin1String(":/trolltech/styles/commonstyle/images/standardbutton-help-16.png"));
+ icon.addFile(QLatin1String(":/trolltech/styles/commonstyle/images/standardbutton-help-32.png"));
+ icon.addFile(QLatin1String(":/trolltech/styles/commonstyle/images/standardbutton-help-128.png"));
+ break;
+ case SP_DialogOpenButton:
+ icon.addFile(QLatin1String(":/trolltech/styles/commonstyle/images/standardbutton-open-16.png"));
+ icon.addFile(QLatin1String(":/trolltech/styles/commonstyle/images/standardbutton-open-32.png"));
+ icon.addFile(QLatin1String(":/trolltech/styles/commonstyle/images/standardbutton-open-128.png"));
+ break;
+ case SP_DialogSaveButton:
+ icon.addFile(QLatin1String(":/trolltech/styles/commonstyle/images/standardbutton-save-16.png"));
+ icon.addFile(QLatin1String(":/trolltech/styles/commonstyle/images/standardbutton-save-32.png"));
+ icon.addFile(QLatin1String(":/trolltech/styles/commonstyle/images/standardbutton-save-128.png"));
+ break;
+ case SP_DialogCloseButton:
+ icon.addFile(QLatin1String(":/trolltech/styles/commonstyle/images/standardbutton-close-16.png"));
+ icon.addFile(QLatin1String(":/trolltech/styles/commonstyle/images/standardbutton-close-32.png"));
+ icon.addFile(QLatin1String(":/trolltech/styles/commonstyle/images/standardbutton-close-128.png"));
+ break;
+ case SP_DialogApplyButton:
+ icon.addFile(QLatin1String(":/trolltech/styles/commonstyle/images/standardbutton-apply-16.png"));
+ icon.addFile(QLatin1String(":/trolltech/styles/commonstyle/images/standardbutton-apply-32.png"));
+ icon.addFile(QLatin1String(":/trolltech/styles/commonstyle/images/standardbutton-apply-128.png"));
+ break;
+ case SP_DialogResetButton:
+ icon.addFile(QLatin1String(":/trolltech/styles/commonstyle/images/standardbutton-clear-16.png"));
+ icon.addFile(QLatin1String(":/trolltech/styles/commonstyle/images/standardbutton-clear-32.png"));
+ icon.addFile(QLatin1String(":/trolltech/styles/commonstyle/images/standardbutton-clear-128.png"));
+ break;
+ case SP_DialogDiscardButton:
+ icon.addFile(QLatin1String(":/trolltech/styles/commonstyle/images/standardbutton-delete-16.png"));
+ icon.addFile(QLatin1String(":/trolltech/styles/commonstyle/images/standardbutton-delete-32.png"));
+ icon.addFile(QLatin1String(":/trolltech/styles/commonstyle/images/standardbutton-delete-128.png"));
+ break;
+ case SP_DialogYesButton:
+ icon.addFile(QLatin1String(":/trolltech/styles/commonstyle/images/standardbutton-yes-16.png"));
+ icon.addFile(QLatin1String(":/trolltech/styles/commonstyle/images/standardbutton-yes-32.png"));
+ icon.addFile(QLatin1String(":/trolltech/styles/commonstyle/images/standardbutton-yes-128.png"));
+ break;
+ case SP_DialogNoButton:
+ icon.addFile(QLatin1String(":/trolltech/styles/commonstyle/images/standardbutton-no-16.png"));
+ icon.addFile(QLatin1String(":/trolltech/styles/commonstyle/images/standardbutton-no-32.png"));
+ icon.addFile(QLatin1String(":/trolltech/styles/commonstyle/images/standardbutton-no-128.png"));
+ break;
+ case SP_ArrowForward:
+ if (QApplication::layoutDirection() == Qt::RightToLeft)
+ return standardIconImplementation(SP_ArrowLeft, option, widget);
+ return standardIconImplementation(SP_ArrowRight, option, widget);
+ case SP_ArrowBack:
+ if (QApplication::layoutDirection() == Qt::RightToLeft)
+ return standardIconImplementation(SP_ArrowRight, option, widget);
+ return standardIconImplementation(SP_ArrowLeft, option, widget);
+ case SP_ArrowLeft:
+ icon.addFile(QLatin1String(":/trolltech/styles/commonstyle/images/left-16.png"));
+ icon.addFile(QLatin1String(":/trolltech/styles/commonstyle/images/left-32.png"));
+ icon.addFile(QLatin1String(":/trolltech/styles/commonstyle/images/left-128.png"));
+ break;
+ case SP_ArrowRight:
+ icon.addFile(QLatin1String(":/trolltech/styles/commonstyle/images/right-16.png"));
+ icon.addFile(QLatin1String(":/trolltech/styles/commonstyle/images/right-32.png"));
+ icon.addFile(QLatin1String(":/trolltech/styles/commonstyle/images/right-128.png"));
+ break;
+ case SP_ArrowUp:
+ icon.addFile(QLatin1String(":/trolltech/styles/commonstyle/images/up-16.png"));
+ icon.addFile(QLatin1String(":/trolltech/styles/commonstyle/images/up-32.png"));
+ icon.addFile(QLatin1String(":/trolltech/styles/commonstyle/images/up-128.png"));
+ break;
+ case SP_ArrowDown:
+ icon.addFile(QLatin1String(":/trolltech/styles/commonstyle/images/down-16.png"));
+ icon.addFile(QLatin1String(":/trolltech/styles/commonstyle/images/down-32.png"));
+ icon.addFile(QLatin1String(":/trolltech/styles/commonstyle/images/down-128.png"));
+ break;
+ case SP_DirHomeIcon:
+ case SP_DirIcon:
+ icon.addFile(QLatin1String(":/trolltech/styles/commonstyle/images/dirclosed-16.png"),
+ QSize(), QIcon::Normal, QIcon::Off);
+ icon.addFile(QLatin1String(":/trolltech/styles/commonstyle/images/diropen-16.png"),
+ QSize(), QIcon::Normal, QIcon::On);
+ icon.addFile(QLatin1String(":/trolltech/styles/commonstyle/images/dirclosed-32.png"),
+ QSize(32, 32), QIcon::Normal, QIcon::Off);
+ icon.addFile(QLatin1String(":/trolltech/styles/commonstyle/images/diropen-32.png"),
+ QSize(32, 32), QIcon::Normal, QIcon::On);
+ icon.addFile(QLatin1String(":/trolltech/styles/commonstyle/images/dirclosed-128.png"),
+ QSize(128, 128), QIcon::Normal, QIcon::Off);
+ icon.addFile(QLatin1String(":/trolltech/styles/commonstyle/images/diropen-128.png"),
+ QSize(128, 128), QIcon::Normal, QIcon::On);
+ break;
+ case SP_DriveCDIcon:
+ icon.addFile(QLatin1String(":/trolltech/styles/commonstyle/images/cdr-16.png"));
+ icon.addFile(QLatin1String(":/trolltech/styles/commonstyle/images/cdr-32.png"));
+ icon.addFile(QLatin1String(":/trolltech/styles/commonstyle/images/cdr-128.png"));
+ break;
+ case SP_DriveDVDIcon:
+ icon.addFile(QLatin1String(":/trolltech/styles/commonstyle/images/dvd-16.png"));
+ icon.addFile(QLatin1String(":/trolltech/styles/commonstyle/images/dvd-32.png"));
+ icon.addFile(QLatin1String(":/trolltech/styles/commonstyle/images/dvd-128.png"));
+ break;
+ case SP_FileIcon:
+ icon.addFile(QLatin1String(":/trolltech/styles/commonstyle/images/file-16.png"));
+ icon.addFile(QLatin1String(":/trolltech/styles/commonstyle/images/file-32.png"));
+ icon.addFile(QLatin1String(":/trolltech/styles/commonstyle/images/file-128.png"));
+ break;
+ case SP_FileLinkIcon:
+ icon.addFile(QLatin1String(":/trolltech/styles/commonstyle/images/filelink-16.png"));
+ icon.addFile(QLatin1String(":/trolltech/styles/commonstyle/images/filelink-32.png"));
+ icon.addFile(QLatin1String(":/trolltech/styles/commonstyle/images/filelink-128.png"));
+ break;
+ case SP_TrashIcon:
+ icon.addFile(QLatin1String(":/trolltech/styles/commonstyle/images/trash-16.png"));
+ icon.addFile(QLatin1String(":/trolltech/styles/commonstyle/images/trash-32.png"));
+ icon.addFile(QLatin1String(":/trolltech/styles/commonstyle/images/trash-128.png"));
+ break;
+ case SP_BrowserReload:
+ icon.addFile(QLatin1String(":/trolltech/styles/commonstyle/images/refresh-24.png"));
+ icon.addFile(QLatin1String(":/trolltech/styles/commonstyle/images/refresh-32.png"));
+ break;
+ case SP_BrowserStop:
+ icon.addFile(QLatin1String(":/trolltech/styles/commonstyle/images/stop-24.png"));
+ icon.addFile(QLatin1String(":/trolltech/styles/commonstyle/images/stop-32.png"));
+ break;
+ case SP_MediaPlay:
+ icon.addFile(QLatin1String(":/trolltech/styles/commonstyle/images/media-play-16.png"));
+ icon.addFile(QLatin1String(":/trolltech/styles/commonstyle/images/media-play-32.png"));
+ break;
+ case SP_MediaPause:
+ icon.addFile(QLatin1String(":/trolltech/styles/commonstyle/images/media-pause-16.png"));
+ icon.addFile(QLatin1String(":/trolltech/styles/commonstyle/images/media-pause-32.png"));
+ break;
+ case SP_MediaStop:
+ icon.addFile(QLatin1String(":/trolltech/styles/commonstyle/images/media-stop-16.png"));
+ icon.addFile(QLatin1String(":/trolltech/styles/commonstyle/images/media-stop-32.png"));
+ break;
+ case SP_MediaSeekForward:
+ icon.addFile(QLatin1String(":/trolltech/styles/commonstyle/images/media-seek-forward-16.png"));
+ icon.addFile(QLatin1String(":/trolltech/styles/commonstyle/images/media-seek-forward-32.png"));
+ break;
+ case SP_MediaSeekBackward:
+ icon.addFile(QLatin1String(":/trolltech/styles/commonstyle/images/media-seek-backward-16.png"));
+ icon.addFile(QLatin1String(":/trolltech/styles/commonstyle/images/media-seek-backward-32.png"));
+ break;
+ case SP_MediaSkipForward:
+ icon.addFile(QLatin1String(":/trolltech/styles/commonstyle/images/media-skip-forward-16.png"));
+ icon.addFile(QLatin1String(":/trolltech/styles/commonstyle/images/media-skip-forward-32.png"));
+ break;
+ case SP_MediaSkipBackward:
+ icon.addFile(QLatin1String(":/trolltech/styles/commonstyle/images/media-skip-backward-16.png"));
+ icon.addFile(QLatin1String(":/trolltech/styles/commonstyle/images/media-skip-backward-32.png"));
+ break;
+ case SP_MediaVolume:
+ icon.addFile(QLatin1String(":/trolltech/styles/commonstyle/images/media-volume-16.png"));
+ break;
+ case SP_MediaVolumeMuted:
+ icon.addFile(QLatin1String(":/trolltech/styles/commonstyle/images/media-volume-muted-16.png"));
+ break;
+#endif // QT_NO_IMAGEFORMAT_PNG
+ default:
+ icon.addPixmap(standardPixmap(standardIcon, option, widget));
+ break;
+ }
+ return icon;
+}
+
+static inline uint qt_intensity(uint r, uint g, uint b)
+{
+ // 30% red, 59% green, 11% blue
+ return (77 * r + 150 * g + 28 * b) / 255;
+}
+
+/*! \reimp */
+QPixmap QCommonStyle::generatedIconPixmap(QIcon::Mode iconMode, const QPixmap &pixmap,
+ const QStyleOption *opt) const
+{
+ switch (iconMode) {
+ case QIcon::Disabled: {
+ QImage im = pixmap.toImage().convertToFormat(QImage::Format_ARGB32);
+
+ // Create a colortable based on the background (black -> bg -> white)
+ QColor bg = opt->palette.color(QPalette::Disabled, QPalette::Window);
+ int red = bg.red();
+ int green = bg.green();
+ int blue = bg.blue();
+ uchar reds[256], greens[256], blues[256];
+ for (int i=0; i<128; ++i) {
+ reds[i] = uchar((red * (i<<1)) >> 8);
+ greens[i] = uchar((green * (i<<1)) >> 8);
+ blues[i] = uchar((blue * (i<<1)) >> 8);
+ }
+ for (int i=0; i<128; ++i) {
+ reds[i+128] = uchar(qMin(red + (i << 1), 255));
+ greens[i+128] = uchar(qMin(green + (i << 1), 255));
+ blues[i+128] = uchar(qMin(blue + (i << 1), 255));
+ }
+
+ int intensity = qt_intensity(red, green, blue);
+ const int factor = 191;
+
+ // High intensity colors needs dark shifting in the color table, while
+ // low intensity colors needs light shifting. This is to increase the
+ // percieved contrast.
+ if ((red - factor > green && red - factor > blue)
+ || (green - factor > red && green - factor > blue)
+ || (blue - factor > red && blue - factor > green))
+ intensity = qMin(255, intensity + 91);
+ else if (intensity <= 128)
+ intensity -= 51;
+
+ for (int y=0; y<im.height(); ++y) {
+ QRgb *scanLine = (QRgb*)im.scanLine(y);
+ for (int x=0; x<im.width(); ++x) {
+ QRgb pixel = *scanLine;
+ // Calculate color table index, taking intensity adjustment
+ // and a magic offset into account.
+ uint ci = uint(qGray(pixel)/3 + (130 - intensity / 3));
+ *scanLine = qRgba(reds[ci], greens[ci], blues[ci], qAlpha(pixel));
+ ++scanLine;
+ }
+ }
+
+ return QPixmap::fromImage(im);
+ }
+ case QIcon::Selected: {
+ QImage img = pixmap.toImage().convertToFormat(QImage::Format_ARGB32_Premultiplied);
+ QColor color = opt->palette.color(QPalette::Normal, QPalette::Highlight);
+ color.setAlphaF(qreal(0.3));
+ QPainter painter(&img);
+ painter.setCompositionMode(QPainter::CompositionMode_SourceAtop);
+ painter.fillRect(0, 0, img.width(), img.height(), color);
+ painter.end();
+ return QPixmap::fromImage(img); }
+ case QIcon::Active:
+ return pixmap;
+ default:
+ break;
+ }
+ return pixmap;
+}
+
+/*!
+ \reimp
+*/
+void QCommonStyle::polish(QPalette &pal)
+{
+ QStyle::polish(pal);
+}
+
+/*!
+ \reimp
+ */
+void QCommonStyle::polish(QWidget *widget)
+{
+ QStyle::polish(widget);
+}
+
+/*!
+ \reimp
+ */
+void QCommonStyle::unpolish(QWidget *widget)
+{
+ QStyle::unpolish(widget);
+}
+
+/*!
+ \reimp
+*/
+void QCommonStyle::polish(QApplication *app)
+{
+ QStyle::polish(app);
+}
+
+/*!
+ \reimp
+ */
+void QCommonStyle::unpolish(QApplication *application)
+{
+ Q_D(const QCommonStyle);
+ d->tabBarcloseButtonIcon = QIcon();
+ QStyle::unpolish(application);
+}
+
+
+QT_END_NAMESPACE
diff --git a/src/gui/styles/qcommonstyle.h b/src/gui/styles/qcommonstyle.h
new file mode 100644
index 0000000000..367afc080e
--- /dev/null
+++ b/src/gui/styles/qcommonstyle.h
@@ -0,0 +1,109 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QCOMMONSTYLE_H
+#define QCOMMONSTYLE_H
+
+#include <QtGui/qstyle.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+QT_MODULE(Gui)
+
+class QCommonStylePrivate;
+
+class Q_GUI_EXPORT QCommonStyle: public QStyle
+{
+ Q_OBJECT
+
+public:
+ QCommonStyle();
+ ~QCommonStyle();
+
+ void drawPrimitive(PrimitiveElement pe, const QStyleOption *opt, QPainter *p,
+ const QWidget *w = 0) const;
+ void drawControl(ControlElement element, const QStyleOption *opt, QPainter *p,
+ const QWidget *w = 0) const;
+ QRect subElementRect(SubElement r, const QStyleOption *opt, const QWidget *widget = 0) const;
+ void drawComplexControl(ComplexControl cc, const QStyleOptionComplex *opt, QPainter *p,
+ const QWidget *w = 0) const;
+ SubControl hitTestComplexControl(ComplexControl cc, const QStyleOptionComplex *opt,
+ const QPoint &pt, const QWidget *w = 0) const;
+ QRect subControlRect(ComplexControl cc, const QStyleOptionComplex *opt, SubControl sc,
+ const QWidget *w = 0) const;
+ QSize sizeFromContents(ContentsType ct, const QStyleOption *opt,
+ const QSize &contentsSize, const QWidget *widget = 0) const;
+
+ int pixelMetric(PixelMetric m, const QStyleOption *opt = 0, const QWidget *widget = 0) const;
+
+ int styleHint(StyleHint sh, const QStyleOption *opt = 0, const QWidget *w = 0,
+ QStyleHintReturn *shret = 0) const;
+
+ QPixmap standardPixmap(StandardPixmap sp, const QStyleOption *opt = 0,
+ const QWidget *widget = 0) const;
+
+ QPixmap generatedIconPixmap(QIcon::Mode iconMode, const QPixmap &pixmap,
+ const QStyleOption *opt) const;
+
+ void polish(QPalette &);
+ void polish(QApplication *app);
+ void polish(QWidget *widget);
+ void unpolish(QWidget *widget);
+ void unpolish(QApplication *application);
+
+protected Q_SLOTS:
+ QIcon standardIconImplementation(StandardPixmap standardIcon, const QStyleOption *opt = 0,
+ const QWidget *widget = 0) const;
+
+protected:
+ QCommonStyle(QCommonStylePrivate &dd);
+
+private:
+ Q_DECLARE_PRIVATE(QCommonStyle)
+ Q_DISABLE_COPY(QCommonStyle)
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QCOMMONSTYLE_H
diff --git a/src/gui/styles/qcommonstyle_p.h b/src/gui/styles/qcommonstyle_p.h
new file mode 100644
index 0000000000..a941706792
--- /dev/null
+++ b/src/gui/styles/qcommonstyle_p.h
@@ -0,0 +1,142 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QCOMMONSTYLE_P_H
+#define QCOMMONSTYLE_P_H
+
+#include "qcommonstyle.h"
+#include "qstyle_p.h"
+
+#include "qstyleoption.h"
+
+QT_BEGIN_NAMESPACE
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of qapplication_*.cpp, qwidget*.cpp and qfiledialog.cpp. This header
+// file may change from version to version without notice, or even be removed.
+//
+// We mean it.
+//
+
+class QStringList;
+
+#ifdef Q_WS_X11
+class QIconTheme
+{
+public:
+ QIconTheme(QHash <int, QString> dirList, QStringList parents) :
+ _dirList(dirList), _parents(parents), _valid(true){ }
+ QIconTheme() : _valid(false){ }
+
+ QHash <int, QString> dirList() {return _dirList;}
+ QStringList parents() {return _parents;}
+ bool isValid() {return _valid;}
+
+private:
+ QHash <int, QString> _dirList;
+ QStringList _parents;
+ bool _valid;
+};
+#endif
+
+// Private class
+class QCommonStylePrivate : public QStylePrivate
+{
+ Q_DECLARE_PUBLIC(QCommonStyle)
+public:
+ inline QCommonStylePrivate()
+#ifndef QT_NO_ITEMVIEWS
+ : cachedOption(0)
+#endif
+ { }
+
+#ifndef QT_NO_ITEMVIEWS
+ ~QCommonStylePrivate()
+ {
+ delete cachedOption;
+ }
+ void viewItemDrawText(QPainter *p, const QStyleOptionViewItemV4 *option, const QRect &rect) const;
+ void viewItemLayout(const QStyleOptionViewItemV4 *opt, QRect *checkRect,
+ QRect *pixmapRect, QRect *textRect, bool sizehint) const;
+ QSize viewItemSize(const QStyleOptionViewItemV4 *option, int role) const;
+
+ mutable QRect decorationRect, displayRect, checkRect;
+ mutable QStyleOptionViewItemV4 *cachedOption;
+ bool isViewItemCached(const QStyleOptionViewItemV4 &option) const {
+ return cachedOption && (option.rect == cachedOption->rect
+ && option.direction == cachedOption->direction
+ && option.state == cachedOption->state
+ && option.displayAlignment == cachedOption->displayAlignment
+ && option.decorationAlignment == cachedOption->decorationAlignment
+ && option.decorationPosition == cachedOption->decorationPosition
+ && option.decorationSize == cachedOption->decorationSize
+ && option.font == cachedOption->font
+ && option.features == cachedOption->features
+ && option.widget == cachedOption->widget
+ && option.index == cachedOption->index
+ && option.icon.isNull() == cachedOption->icon.isNull()
+ && option.text == cachedOption->text
+ && option.viewItemPosition == cachedOption->viewItemPosition);
+ }
+#endif
+ mutable QIcon tabBarcloseButtonIcon;
+
+//icon detection on X11
+#ifdef Q_WS_X11
+ void lookupIconTheme() const;
+ QIcon createIcon(const QString &) const;
+ QPixmap findIcon(int size, const QString &) const;
+ QPixmap findIconHelper(int size, const QString &, const QString &, QStringList &visited) const;
+ QIconTheme parseIndexFile(const QString &themeName) const;
+ mutable QString themeName;
+ mutable QStringList iconDirs;
+ mutable QHash <QString, QIconTheme> themeList;
+#endif
+
+};
+
+QT_END_NAMESPACE
+
+#endif //QCOMMONSTYLE_P_H
diff --git a/src/gui/styles/qcommonstylepixmaps_p.h b/src/gui/styles/qcommonstylepixmaps_p.h
new file mode 100644
index 0000000000..c4adb7686a
--- /dev/null
+++ b/src/gui/styles/qcommonstylepixmaps_p.h
@@ -0,0 +1,186 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QT_NO_IMAGEFORMAT_XPM
+
+//
+// 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.
+//
+
+static const char * const check_list_controller_xpm[] = {
+"16 16 4 1",
+" c None",
+". c #000000000000",
+"X c #FFFFFFFF0000",
+"o c #C71BC30BC71B",
+" ",
+" ",
+" .......... ",
+" .XXXXXXXX. ",
+" .XXXXXXXX.oo ",
+" .XXXXXXXX.oo ",
+" .XXXXXXXX.oo ",
+" .XXXXXXXX.oo ",
+" .XXXXXXXX.oo ",
+" .XXXXXXXX.oo ",
+" .XXXXXXXX.oo ",
+" ..........oo ",
+" oooooooooo ",
+" oooooooooo ",
+" ",
+" "};
+
+static const char * const tree_branch_open_xpm[] = {
+"9 9 2 1",
+" c None",
+"# c #000000",
+"#########",
+"# #",
+"# ##### #",
+"# ### #",
+"# ### #",
+"# # #",
+"# # #",
+"# #",
+"#########"};
+
+static const char * const tree_branch_closed_xpm[] = {
+"9 9 2 1",
+" c None",
+"# c #000000",
+"#########",
+"# #",
+"# # #",
+"# ### #",
+"# ##### #",
+"# ### #",
+"# # #",
+"# #",
+"#########"};
+
+static const char * const tb_extension_arrow_v_xpm[] = {
+ "5 8 3 1",
+ " c None",
+ ". c #000000",
+ "+ c none",
+ ".+++.",
+ "..+..",
+ "+...+",
+ "++.++",
+ ".+++.",
+ "..+..",
+ "+...+",
+ "++.++"
+};
+
+static const char * const tb_extension_arrow_h_xpm[] = {
+ "8 5 3 1",
+ " c None",
+ ". c #000000",
+ "+ c none",
+ "..++..++",
+ "+..++..+",
+ "++..++..",
+ "+..++..+",
+ "..++..++",
+};
+
+static const char * const filedialog_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#",
+ "...#############",
+ "................"};
+
+static const char * const filedialog_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"};
+
+#endif // QT_NO_IMAGEFORMAT_XPM
diff --git a/src/gui/styles/qgtkpainter.cpp b/src/gui/styles/qgtkpainter.cpp
new file mode 100644
index 0000000000..3d47b80b9b
--- /dev/null
+++ b/src/gui/styles/qgtkpainter.cpp
@@ -0,0 +1,705 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qgtkpainter_p.h"
+
+#include <QtCore/qglobal.h>
+#if !defined(QT_NO_STYLE_GTK)
+
+// This class is primarily a wrapper around the gtk painter functions
+// and takes care of converting all such calls into cached Qt pixmaps.
+
+#include <QtGui/QWidget>
+#include <QtGui/QStyleOption>
+#include <QtGui/QPixmapCache>
+
+QT_BEGIN_NAMESPACE
+
+#undef GTK_OBJECT_FLAGS
+#define GTK_OBJECT_FLAGS(obj)(((GtkObject*)(obj))->flags)
+
+#if Q_BYTE_ORDER == Q_BIG_ENDIAN
+# define QT_RED 3
+# define QT_GREEN 2
+# define QT_BLUE 1
+# define QT_ALPHA 0
+#else
+# define QT_RED 0
+# define QT_GREEN 1
+# define QT_BLUE 2
+# define QT_ALPHA 3
+#endif
+# define GTK_RED 2
+# define GTK_GREEN 1
+# define GTK_BLUE 0
+# define GTK_ALPHA 3
+
+// To recover alpha we apply the gtk painting function two times to
+// white, and black window backgrounds. This can be used to
+// recover the premultiplied alpha channel
+QPixmap QGtkPainter::renderTheme(uchar *bdata, uchar *wdata, const QRect &rect)
+{
+ const int bytecount = rect.width() * rect.height() * 4;
+ for (int index = 0; index < bytecount ; index += 4) {
+ uchar val = bdata[index + GTK_BLUE];
+ if (m_alpha) {
+ int alphaval = qMax(bdata[index + GTK_BLUE] - wdata[index + GTK_BLUE],
+ bdata[index + GTK_GREEN] - wdata[index + GTK_GREEN]);
+ alphaval = qMax(alphaval, bdata[index + GTK_RED] - wdata[index + GTK_RED]) + 255;
+ bdata[index + QT_ALPHA] = alphaval;
+ }
+ bdata[index + QT_RED] = bdata[index + GTK_RED];
+ bdata[index + QT_GREEN] = bdata[index + GTK_GREEN];
+ bdata[index + QT_BLUE] = val;
+ }
+ QImage converted((const uchar*)bdata, rect.width(), rect.height(), m_alpha ?
+ QImage::Format_ARGB32_Premultiplied : QImage::Format_RGB32);
+
+ if (m_hflipped || m_vflipped) {
+ return QPixmap::fromImage(converted.mirrored(m_hflipped, m_vflipped));
+ } else {
+ // on raster graphicssystem we need to do a copy here, because
+ // we intend to deallocate the qimage bits shortly after...
+ return QPixmap::fromImage(converted.copy());
+ }
+}
+
+// This macro is responsible for painting any GtkStyle painting function onto a QPixmap
+#define DRAW_TO_CACHE(draw_func) \
+ if (rect.width() > QWIDGETSIZE_MAX || rect.height() > QWIDGETSIZE_MAX) \
+ return; \
+ QRect pixmapRect(0, 0, rect.width(), rect.height()); \
+ { \
+ GdkPixmap *pixmap = QGtk::gdk_pixmap_new((GdkDrawable*)(m_window->window), \
+ rect.width(), rect.height(), -1); \
+ if (!pixmap) \
+ return; \
+ style = QGtk::gtk_style_attach (style, m_window->window); \
+ QGtk::gdk_draw_rectangle(pixmap, m_alpha ? style->black_gc : *style->bg_gc, true, \
+ 0, 0, rect.width(), rect.height()); \
+ draw_func; \
+ GdkPixbuf *imgb = QGtk::gdk_pixbuf_new(GDK_COLORSPACE_RGB, true, 8, rect.width(), rect.height());\
+ if (!imgb) \
+ return; \
+ imgb = QGtk::gdk_pixbuf_get_from_drawable(imgb, pixmap, NULL, 0, 0, 0, 0, \
+ rect.width(), rect.height()); \
+ uchar* bdata = (uchar*)QGtk::gdk_pixbuf_get_pixels(imgb); \
+ if (m_alpha) { \
+ QGtk::gdk_draw_rectangle(pixmap, style->white_gc, true, 0, 0, rect.width(), rect.height()); \
+ draw_func; \
+ GdkPixbuf *imgw = QGtk::gdk_pixbuf_new(GDK_COLORSPACE_RGB, true, 8, rect. \
+ width(), rect.height()); \
+ if (!imgw) \
+ return; \
+ imgw = QGtk::gdk_pixbuf_get_from_drawable(imgw, pixmap, NULL, 0, 0, 0, 0, \
+ rect.width(), rect.height()); \
+ uchar* wdata = (uchar*)QGtk::gdk_pixbuf_get_pixels(imgw); \
+ cache = renderTheme(bdata, wdata, rect); \
+ QGtk::gdk_pixbuf_unref(imgw); \
+ } else { \
+ cache = renderTheme(bdata, 0, rect); \
+ } \
+ QGtk::gdk_drawable_unref(pixmap); \
+ QGtk::gdk_pixbuf_unref(imgb); \
+ }
+
+QGtkPainter::QGtkPainter(QPainter *_painter)
+ : m_window(QGtk::gtkWidget(QLatin1String("GtkWindow")))
+ , m_painter(_painter)
+ , m_alpha(true)
+ , m_hflipped(false)
+ , m_vflipped(false)
+ , m_usePixmapCache(true)
+{}
+
+
+static QString uniqueName(const QString &key, GtkStateType state, GtkShadowType shadow,
+ const QSize &size, GtkWidget *widget = 0)
+{
+ // Note the widget arg should ideally use the widget path, though would compromise performance
+ QString tmp = QString(QLS("%0-%1-%2-%3x%4-%5")).arg(key).arg(uint(state)).arg(shadow)
+ .arg(size.width()).arg(size.height()).arg(quintptr(widget));
+ return tmp;
+}
+
+
+GtkStateType QGtkPainter::gtkState(const QStyleOption *option)
+
+{
+ GtkStateType state = GTK_STATE_NORMAL;
+ if (!(option->state & QStyle::State_Enabled))
+ state = GTK_STATE_INSENSITIVE;
+ else if (option->state & QStyle::State_MouseOver)
+ state = GTK_STATE_PRELIGHT;
+
+ return state;
+}
+
+
+GtkStyle* QGtkPainter::getStyle(GtkWidget *gtkWidget)
+
+{
+ Q_ASSERT(gtkWidget);
+ GtkStyle* style = gtkWidget->style;
+ Q_ASSERT(style);
+ return style;
+}
+
+QPixmap QGtkPainter::getIcon(const char* iconName, GtkIconSize size)
+{
+ GtkStyle *style = QGtk::gtkStyle();
+ GtkIconSet* iconSet = QGtk::gtk_icon_factory_lookup_default (iconName);
+ GdkPixbuf* icon = QGtk::gtk_icon_set_render_icon(iconSet,
+ style,
+ GTK_TEXT_DIR_LTR,
+ GTK_STATE_NORMAL,
+ size,
+ NULL,
+ "button");
+ uchar* data = (uchar*)QGtk::gdk_pixbuf_get_pixels(icon);
+ int width = QGtk::gdk_pixbuf_get_width(icon);
+ int height = QGtk::gdk_pixbuf_get_height(icon);
+ QImage converted(width, height, QImage::Format_ARGB32);
+ uchar* tdata = (uchar*)converted.bits();
+
+ for ( int index = 0 ; index < height * width*4 ; index +=4 ) {
+ //int index = y * rowstride + x;
+ tdata[index + QT_RED] = data[index + GTK_RED];
+ tdata[index + QT_GREEN] = data[index + GTK_GREEN];
+ tdata[index + QT_BLUE] = data[index + GTK_BLUE];
+ tdata[index + QT_ALPHA] = data[index + GTK_ALPHA];
+ }
+
+ QGtk::gdk_pixbuf_unref(icon);
+
+ // should we free iconset?
+ return QPixmap::fromImage(converted);
+
+}
+
+// Note currently painted without alpha for performance reasons
+void QGtkPainter::paintBoxGap(GtkWidget *gtkWidget, const gchar* part,
+ const QRect &paintRect, GtkStateType state,
+ GtkShadowType shadow, GtkPositionType gap_side,
+ gint x, gint width,
+ GtkStyle *style)
+{
+ if (!paintRect.isValid())
+ return;
+
+ QPixmap cache;
+ QRect rect = paintRect;
+
+ // To avoid exhausting cache on large tabframes we cheat a bit by
+ // tiling the center part.
+
+ const int maxHeight = 256;
+ const int border = 16;
+ if (rect.height() > maxHeight && (gap_side == GTK_POS_TOP || gap_side == GTK_POS_BOTTOM))
+ rect.setHeight(2 * border + 1);
+
+ QString gapExtras = QString(QLS("s %0 w %1 g %2")).arg(gap_side).arg(width).arg(x);
+ QString pixmapName = uniqueName(QLS(part), state, shadow, rect.size(), gtkWidget) + gapExtras;
+
+ if (!m_usePixmapCache || !QPixmapCache::find(pixmapName, cache)) {
+ DRAW_TO_CACHE(QGtk::gtk_paint_box_gap (style,
+ pixmap,
+ state,
+ shadow,
+ NULL,
+ gtkWidget,
+ (gchar*)part,
+ 0, 0,
+ rect.width(),
+ rect.height(),
+ gap_side,
+ x,
+ width));
+ if (m_usePixmapCache)
+ QPixmapCache::insert(pixmapName, cache);
+ }
+ if (rect.size() != paintRect.size()) {
+ // We assume we can stretch the middle tab part
+ // Note: the side effect of this is that pinstripe patterns will get fuzzy
+ const QSize size = cache.size();
+ // top part
+ m_painter->drawPixmap(QRect(paintRect.left(), paintRect.top(),
+ paintRect.width(), border), cache,
+ QRect(0, 0, size.width(), border));
+
+ // tiled center part
+ QPixmap tilePart(cache.width(), 1);
+ QPainter scanLinePainter(&tilePart);
+ scanLinePainter.drawPixmap(QRect(0, 0, tilePart.width(), tilePart.height()), cache, QRect(0, border, size.width(), 1));
+ scanLinePainter.end();
+ m_painter->drawTiledPixmap(QRect(paintRect.left(), paintRect.top() + border,
+ paintRect.width(), paintRect.height() - 2*border), tilePart);
+
+ // bottom part
+ m_painter->drawPixmap(QRect(paintRect.left(), paintRect.top() + paintRect.height() - border,
+ paintRect.width(), border), cache,
+ QRect(0, size.height() - border, size.width(), border));
+ } else
+ m_painter->drawPixmap(paintRect.topLeft(), cache);
+}
+
+void QGtkPainter::paintBox(GtkWidget *gtkWidget, const gchar* part,
+ const QRect &paintRect, GtkStateType state,
+ GtkShadowType shadow, GtkStyle *style,
+ const QString &pmKey)
+{
+ if (!paintRect.isValid())
+ return;
+
+ QPixmap cache;
+ QRect rect = paintRect;
+
+ // To avoid exhausting cache on large tabframes we cheat a bit by
+ // tiling the center part.
+
+ const int maxHeight = 256;
+ const int maxArea = 256*512;
+ const int border = 32;
+ if (rect.height() > maxHeight && (rect.width()*rect.height() > maxArea))
+ rect.setHeight(2 * border + 1);
+
+ QString pixmapName = uniqueName(QLS(part), state, shadow,
+ rect.size(), gtkWidget) + pmKey;
+
+ if (!m_usePixmapCache || !QPixmapCache::find(pixmapName, cache)) {
+ DRAW_TO_CACHE(QGtk::gtk_paint_box (style,
+ pixmap,
+ state,
+ shadow,
+ NULL,
+ gtkWidget,
+ part,
+ 0, 0,
+ rect.width(),
+ rect.height()));
+ if (m_usePixmapCache)
+ QPixmapCache::insert(pixmapName, cache);
+ }
+ if (rect.size() != paintRect.size()) {
+ // We assume we can stretch the middle tab part
+ // Note: the side effect of this is that pinstripe patterns will get fuzzy
+ const QSize size = cache.size();
+ // top part
+ m_painter->drawPixmap(QRect(paintRect.left(), paintRect.top(),
+ paintRect.width(), border), cache,
+ QRect(0, 0, size.width(), border));
+
+ // tiled center part
+ QPixmap tilePart(cache.width(), 1);
+ QPainter scanLinePainter(&tilePart);
+ scanLinePainter.drawPixmap(QRect(0, 0, tilePart.width(), tilePart.height()), cache, QRect(0, border, size.width(), 1));
+ scanLinePainter.end();
+ m_painter->drawTiledPixmap(QRect(paintRect.left(), paintRect.top() + border,
+ paintRect.width(), paintRect.height() - 2*border), tilePart);
+
+ // bottom part
+ m_painter->drawPixmap(QRect(paintRect.left(), paintRect.top() + paintRect.height() - border,
+ paintRect.width(), border), cache,
+ QRect(0, size.height() - border, size.width(), border));
+ } else
+ m_painter->drawPixmap(paintRect.topLeft(), cache);
+}
+
+void QGtkPainter::paintHline(GtkWidget *gtkWidget, const gchar* part,
+ const QRect &rect, GtkStateType state,
+ GtkStyle *style, int x1, int x2, int y,
+ const QString &pmKey)
+{
+ if (!rect.isValid())
+ return;
+
+ QPixmap cache;
+ QString hLineExtras = QString(QLS("%0 %1 %2")).arg(x1).arg(x2).arg(y);
+ QString pixmapName = uniqueName(QLS(part), state, GTK_SHADOW_NONE, rect.size(), gtkWidget)
+ + hLineExtras + pmKey;
+ if (!m_usePixmapCache || !QPixmapCache::find(pixmapName, cache)) {
+ DRAW_TO_CACHE(QGtk::gtk_paint_hline (style,
+ pixmap,
+ state,
+ NULL,
+ gtkWidget,
+ part,
+ x1, x2, y));
+ if (m_usePixmapCache)
+ QPixmapCache::insert(pixmapName, cache);
+ }
+
+ m_painter->drawPixmap(rect.topLeft(), cache);
+}
+
+void QGtkPainter::paintVline(GtkWidget *gtkWidget, const gchar* part,
+ const QRect &rect, GtkStateType state,
+ GtkStyle *style, int y1, int y2, int x,
+ const QString &pmKey)
+{
+ if (!rect.isValid())
+ return;
+
+ QPixmap cache;
+ QString vLineExtras = QString(QLS("%0 %1 %2")).arg(y1).arg(y2).arg(x);
+ QString pixmapName = uniqueName(QLS(part), state, GTK_SHADOW_NONE, rect.size(),
+ gtkWidget) + vLineExtras +pmKey;
+ if (!m_usePixmapCache || !QPixmapCache::find(pixmapName, cache)) {
+ DRAW_TO_CACHE(QGtk::gtk_paint_vline (style,
+ pixmap,
+ state,
+ NULL,
+ gtkWidget,
+ part,
+ y1, y2,
+ x));
+ if (m_usePixmapCache)
+ QPixmapCache::insert(pixmapName, cache);
+ }
+ m_painter->drawPixmap(rect.topLeft(), cache);
+}
+
+
+void QGtkPainter::paintExpander(GtkWidget *gtkWidget,
+ const gchar* part, const QRect &rect,
+ GtkStateType state, GtkExpanderStyle expander_state,
+ GtkStyle *style, const QString &pmKey)
+{
+ if (!rect.isValid())
+ return;
+
+ QPixmap cache;
+ QString pixmapName = uniqueName(QLS(part), state, GTK_SHADOW_NONE, rect.size(),
+ gtkWidget) + QString::number(expander_state) + pmKey;
+ if (!m_usePixmapCache || !QPixmapCache::find(pixmapName, cache)) {
+ DRAW_TO_CACHE(QGtk::gtk_paint_expander (style, pixmap,
+ state, NULL,
+ gtkWidget, part,
+ rect.width()/2,
+ rect.height()/2,
+ expander_state));
+ if (m_usePixmapCache)
+ QPixmapCache::insert(pixmapName, cache);
+ }
+
+ m_painter->drawPixmap(rect.topLeft(), cache);
+}
+
+void QGtkPainter::paintFocus(GtkWidget *gtkWidget, const gchar* part,
+ const QRect &rect, GtkStateType state,
+ GtkStyle *style, const QString &pmKey)
+{
+ if (!rect.isValid())
+ return;
+
+ QPixmap cache;
+ QString pixmapName = uniqueName(QLS(part), state, GTK_SHADOW_NONE, rect.size(), gtkWidget) + pmKey;
+ if (!m_usePixmapCache || !QPixmapCache::find(pixmapName, cache)) {
+ DRAW_TO_CACHE(QGtk::gtk_paint_focus (style, pixmap, state, NULL,
+ gtkWidget,
+ part,
+ 0, 0,
+ rect.width(),
+ rect.height()));
+ if (m_usePixmapCache)
+ QPixmapCache::insert(pixmapName, cache);
+ }
+
+ m_painter->drawPixmap(rect.topLeft(), cache);
+}
+
+
+void QGtkPainter::paintResizeGrip(GtkWidget *gtkWidget, const gchar* part,
+ const QRect &rect, GtkStateType state,
+ GtkShadowType shadow, GdkWindowEdge edge,
+ GtkStyle *style, const QString &pmKey)
+{
+ if (!rect.isValid())
+ return;
+
+ QPixmap cache;
+ QString pixmapName = uniqueName(QLS(part), state, shadow, rect.size(), gtkWidget) + pmKey;
+ if (!m_usePixmapCache || !QPixmapCache::find(pixmapName, cache)) {
+ DRAW_TO_CACHE(QGtk::gtk_paint_resize_grip (style, pixmap, state,
+ NULL, gtkWidget,
+ part, edge, 0, 0,
+ rect.width(),
+ rect.height()));
+ if (m_usePixmapCache)
+ QPixmapCache::insert(pixmapName, cache);
+ }
+
+ m_painter->drawPixmap(rect.topLeft(), cache);
+}
+
+
+void QGtkPainter::paintArrow(GtkWidget *gtkWidget, const gchar* part,
+ const QRect &arrowrect, GtkArrowType arrow_type,
+ GtkStateType state, GtkShadowType shadow,
+ gboolean fill, GtkStyle *style, const QString &pmKey)
+{
+ QRect rect = m_cliprect.isValid() ? m_cliprect : arrowrect;
+ if (!rect.isValid())
+ return;
+
+ QPixmap cache;
+ QString pixmapName = uniqueName(QLS(part), state, shadow, rect.size()) +
+ QString::number((int)arrow_type) + pmKey;
+
+ GdkRectangle gtkCliprect = {0, 0, rect.width(), rect.height()};
+ int xOffset = m_cliprect.isValid() ? arrowrect.x() - m_cliprect.x() : 0;
+ int yOffset = m_cliprect.isValid() ? arrowrect.y() - m_cliprect.y() : 0;
+ if (!m_usePixmapCache || !QPixmapCache::find(pixmapName, cache)) {
+ DRAW_TO_CACHE(QGtk::gtk_paint_arrow (style, pixmap, state, shadow,
+ &gtkCliprect,
+ gtkWidget,
+ part,
+ arrow_type, fill,
+ xOffset, yOffset,
+ arrowrect.width(),
+ arrowrect.height()))
+ if (m_usePixmapCache)
+ QPixmapCache::insert(pixmapName, cache);
+ }
+
+ m_painter->drawPixmap(rect.topLeft(), cache);
+}
+
+
+void QGtkPainter::paintHandle(GtkWidget *gtkWidget, const gchar* part, const QRect &rect,
+ GtkStateType state, GtkShadowType shadow,
+ GtkOrientation orientation, GtkStyle *style)
+{
+ if (!rect.isValid())
+ return;
+
+ QPixmap cache;
+ QString pixmapName = uniqueName(QLS(part), state, shadow, rect.size())
+ + QString::number(orientation);
+ if (!m_usePixmapCache || !QPixmapCache::find(pixmapName, cache)) {
+ DRAW_TO_CACHE(QGtk::gtk_paint_handle (style,
+ pixmap,
+ state,
+ shadow,
+ NULL,
+ gtkWidget,
+ part, 0, 0,
+ rect.width(),
+ rect.height(),
+ orientation));
+ if (m_usePixmapCache)
+ QPixmapCache::insert(pixmapName, cache);
+ }
+ m_painter->drawPixmap(rect.topLeft(), cache);
+}
+
+
+void QGtkPainter::paintSlider(GtkWidget *gtkWidget, const gchar* part, const QRect &rect,
+ GtkStateType state, GtkShadowType shadow,
+ GtkStyle *style, GtkOrientation orientation,
+ const QString &pmKey)
+{
+ if (!rect.isValid())
+ return;
+
+ QPixmap cache;
+ QString pixmapName = uniqueName(QLS(part), state, shadow, rect.size(), gtkWidget) + pmKey;
+ if (!m_usePixmapCache || !QPixmapCache::find(pixmapName, cache)) {
+ DRAW_TO_CACHE(QGtk::gtk_paint_slider (style,
+ pixmap,
+ state,
+ shadow,
+ NULL,
+ gtkWidget,
+ part,
+ 0, 0,
+ rect.width(),
+ rect.height(),
+ orientation));
+ if (m_usePixmapCache)
+ QPixmapCache::insert(pixmapName, cache);
+ }
+ m_painter->drawPixmap(rect.topLeft(), cache);
+}
+
+
+void QGtkPainter::paintShadow(GtkWidget *gtkWidget, const gchar* part,
+ const QRect &rect, GtkStateType state,
+ GtkShadowType shadow, GtkStyle *style,
+ const QString &pmKey)
+
+{
+ if (!rect.isValid())
+ return;
+
+ QRect r = rect;
+ QPixmap cache;
+ QString pixmapName = uniqueName(QLS(part), state, shadow, rect.size()) + pmKey;
+ if (!m_usePixmapCache || !QPixmapCache::find(pixmapName, cache)) {
+ DRAW_TO_CACHE(QGtk::gtk_paint_shadow(style, pixmap, state, shadow, NULL,
+ gtkWidget, part, 0, 0, rect.width(), rect.height()));
+ if (m_usePixmapCache)
+ QPixmapCache::insert(pixmapName, cache);
+ }
+ m_painter->drawPixmap(rect.topLeft(), cache);
+}
+
+void QGtkPainter::paintFlatBox(GtkWidget *gtkWidget, const gchar* part,
+ const QRect &rect, GtkStateType state,
+ GtkShadowType shadow, GtkStyle *style,
+ const QString &pmKey)
+{
+ if (!rect.isValid())
+ return;
+ QRect r = rect;
+ QPixmap cache;
+ QString pixmapName = uniqueName(QLS(part), state, shadow, rect.size()) + pmKey;
+ if (!m_usePixmapCache || !QPixmapCache::find(pixmapName, cache)) {
+ DRAW_TO_CACHE(QGtk::gtk_paint_flat_box (style,
+ pixmap,
+ state,
+ shadow,
+ NULL,
+ gtkWidget,
+ part, 0, 0,
+ rect.width(),
+ rect.height()));
+ if (m_usePixmapCache)
+ QPixmapCache::insert(pixmapName, cache);
+ }
+ m_painter->drawPixmap(rect.topLeft(), cache);
+}
+
+void QGtkPainter::paintExtention(GtkWidget *gtkWidget,
+ const gchar *part, const QRect &rect,
+ GtkStateType state, GtkShadowType shadow,
+ GtkPositionType gap_pos, GtkStyle *style)
+{
+ if (!rect.isValid())
+ return;
+
+ QRect r = rect;
+ QPixmap cache;
+ QString pixmapName = uniqueName(QLS(part), state, shadow, rect.size(), gtkWidget);
+ pixmapName += QString::number(gap_pos);
+
+ if (!m_usePixmapCache || !QPixmapCache::find(pixmapName, cache)) {
+ DRAW_TO_CACHE(QGtk::gtk_paint_extension (style, pixmap, state, shadow,
+ NULL, gtkWidget,
+ (gchar*)part, 0, 0,
+ rect.width(),
+ rect.height(),
+ gap_pos));
+ if (m_usePixmapCache)
+ QPixmapCache::insert(pixmapName, cache);
+ }
+
+ m_painter->drawPixmap(rect.topLeft(), cache);
+}
+
+void QGtkPainter::paintOption(GtkWidget *gtkWidget, const QRect &radiorect,
+ GtkStateType state, GtkShadowType shadow,
+ GtkStyle *style, const QString &detail)
+
+{
+ QRect rect = m_cliprect.isValid() ? m_cliprect : radiorect;
+ if (!rect.isValid())
+ return;
+
+ QRect r = rect;
+ QPixmap cache;
+ QString pixmapName = uniqueName(detail, state, shadow, rect.size());
+ GdkRectangle gtkCliprect = {0, 0, rect.width(), rect.height()};
+ int xOffset = m_cliprect.isValid() ? radiorect.x() - m_cliprect.x() : 0;
+ int yOffset = m_cliprect.isValid() ? radiorect.y() - m_cliprect.y() : 0;
+ if (!m_usePixmapCache || !QPixmapCache::find(pixmapName, cache)) {
+ DRAW_TO_CACHE(QGtk::gtk_paint_option(style, pixmap,
+ state, shadow,
+ &gtkCliprect,
+ gtkWidget,
+ detail.toLatin1(),
+ xOffset, yOffset,
+ radiorect.width(),
+ radiorect.height()));
+
+ if (m_usePixmapCache)
+ QPixmapCache::insert(pixmapName, cache);
+ }
+
+ m_painter->drawPixmap(rect.topLeft(), cache);
+}
+
+void QGtkPainter::paintCheckbox(GtkWidget *gtkWidget, const QRect &checkrect,
+ GtkStateType state, GtkShadowType shadow,
+ GtkStyle *style, const QString &detail)
+
+{
+ QRect rect = m_cliprect.isValid() ? m_cliprect : checkrect;
+ if (!rect.isValid())
+ return;
+
+ QRect r = rect;
+ QPixmap cache;
+ QString pixmapName = uniqueName(detail, state, shadow, rect.size());
+ GdkRectangle gtkCliprect = {0, 0, rect.width(), rect.height()};
+ int xOffset = m_cliprect.isValid() ? checkrect.x() - m_cliprect.x() : 0;
+ int yOffset = m_cliprect.isValid() ? checkrect.y() - m_cliprect.y() : 0;
+ if (!m_usePixmapCache || !QPixmapCache::find(pixmapName, cache)) {
+ DRAW_TO_CACHE(QGtk::gtk_paint_check (style,
+ pixmap,
+ state,
+ shadow,
+ &gtkCliprect,
+ gtkWidget,
+ detail.toLatin1(),
+ xOffset, yOffset,
+ checkrect.width(),
+ checkrect.height()));
+ if (m_usePixmapCache)
+ QPixmapCache::insert(pixmapName, cache);
+ }
+
+ m_painter->drawPixmap(rect.topLeft(), cache);
+}
+
+QT_END_NAMESPACE
+
+#endif //!defined(QT_NO_STYLE_GTK)
diff --git a/src/gui/styles/qgtkpainter_p.h b/src/gui/styles/qgtkpainter_p.h
new file mode 100644
index 0000000000..dc0bc98d1c
--- /dev/null
+++ b/src/gui/styles/qgtkpainter_p.h
@@ -0,0 +1,129 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QGTKPAINTER_H
+#define QGTKPAINTER_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 <QtCore/qglobal.h>
+#if !defined(QT_NO_STYLE_GTK)
+
+#include "gtksymbols_p.h"
+#include <QtGui/QCleanlooksStyle>
+#include <QtGui/QPainter>
+#include <QtGui/QPalette>
+#include <QtGui/QFont>
+
+QT_BEGIN_NAMESPACE
+
+class QGtkPainter
+{
+
+public:
+ QGtkPainter(QPainter *painter);
+ GtkStyle *getStyle(GtkWidget *gtkWidget);
+ GtkStateType gtkState(const QStyleOption *option);
+
+ void setAlphaSupport(bool value) { m_alpha = value; }
+ void setClipRect(const QRect &rect) { m_cliprect = rect; }
+ void setFlipHorizontal(bool value) { m_hflipped = value; }
+ void setFlipVertical(bool value) { m_vflipped = value; }
+ void setUsePixmapCache(bool value) { m_usePixmapCache = value; }
+
+ void paintBoxGap(GtkWidget *gtkWidget, const gchar* part, const QRect &rect,
+ GtkStateType state, GtkShadowType shadow, GtkPositionType gap_side, gint x,
+ gint width, GtkStyle *style);
+ void paintBox(GtkWidget *gtkWidget, const gchar* part,
+ const QRect &rect, GtkStateType state, GtkShadowType shadow, GtkStyle *style,
+ const QString &pmKey = QString());
+ void paintHline(GtkWidget *gtkWidget, const gchar* part, const QRect &rect, GtkStateType state, GtkStyle *style,
+ int x1, int x2, int y, const QString &pmKey = QString());
+ void paintVline(GtkWidget *gtkWidget, const gchar* part, const QRect &rect, GtkStateType state, GtkStyle *style,
+ int y1, int y2, int x, const QString &pmKey = QString());
+ void paintExpander(GtkWidget *gtkWidget, const gchar* part, const QRect &rect, GtkStateType state,
+ GtkExpanderStyle expander_state, GtkStyle *style, const QString &pmKey = QString());
+ void paintFocus(GtkWidget *gtkWidget, const gchar* part, const QRect &rect, GtkStateType state, GtkStyle *style,
+ const QString &pmKey = QString());
+ void paintResizeGrip(GtkWidget *gtkWidget, const gchar* part, const QRect &rect, GtkStateType state, GtkShadowType shadow,
+ GdkWindowEdge edge, GtkStyle *style, const QString &pmKey = QString());
+ void paintArrow(GtkWidget *gtkWidget, const gchar* part, const QRect &arrowrect, GtkArrowType arrow_type, GtkStateType state, GtkShadowType shadow,
+ gboolean fill, GtkStyle *style, const QString &pmKey = QString());
+ void paintHandle(GtkWidget *gtkWidget, const gchar* part, const QRect &rect,
+ GtkStateType state, GtkShadowType shadow, GtkOrientation orientation, GtkStyle *style);
+ void paintSlider(GtkWidget *gtkWidget, const gchar* part, const QRect &rect, GtkStateType state, GtkShadowType shadow,
+ GtkStyle *style, GtkOrientation orientation, const QString &pmKey = QString());
+ void paintShadow(GtkWidget *gtkWidget, const gchar* part, const QRect &rect, GtkStateType state, GtkShadowType shadow,
+ GtkStyle *style, const QString &pmKey = QString());
+ void paintFlatBox(GtkWidget *gtkWidget, const gchar* part, const QRect &rect, GtkStateType state, GtkShadowType shadow, GtkStyle *style, const QString & = QString());
+ void paintExtention(GtkWidget *gtkWidget, const gchar *part, const QRect &rect, GtkStateType state, GtkShadowType shadow,
+ GtkPositionType gap_pos, GtkStyle *style);
+ void paintOption(GtkWidget *gtkWidget, const QRect &rect, GtkStateType state, GtkShadowType shadow, GtkStyle *style, const QString &detail);
+ void paintCheckbox(GtkWidget *gtkWidget, const QRect &rect, GtkStateType state, GtkShadowType shadow, GtkStyle *style, const QString &detail);
+
+ static QPixmap getIcon(const char* iconName, GtkIconSize size = GTK_ICON_SIZE_BUTTON);
+private:
+ QPixmap renderTheme(uchar *bdata, uchar *wdata, const QRect&);
+
+ GtkWidget *m_window;
+ QPainter *m_painter;
+ bool m_alpha;
+ bool m_hflipped;
+ bool m_vflipped;
+ bool m_usePixmapCache;
+ QRect m_cliprect;
+
+};
+
+QT_END_NAMESPACE
+
+#endif //!defined(QT_NO_STYLE_QGTK)
+
+#endif // QGTKPAINTER_H
diff --git a/src/gui/styles/qgtkstyle.cpp b/src/gui/styles/qgtkstyle.cpp
new file mode 100644
index 0000000000..103e97f8ea
--- /dev/null
+++ b/src/gui/styles/qgtkstyle.cpp
@@ -0,0 +1,3280 @@
+/******* *********************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qgtkstyle.h"
+
+#if !defined(QT_NO_STYLE_GTK)
+
+#include <private/qapplication_p.h>
+#include <QtCore/QLibrary>
+#include <QtCore/QSettings>
+#include <QtGui/QDialogButtonBox>
+#include <QtGui/QStatusBar>
+#include <QtGui/QLineEdit>
+#include <QtGui/QWidget>
+#include <QtGui/QListView>
+#include <QtGui/QApplication>
+#include <QtGui/QStyleOption>
+#include <QtGui/QPushButton>
+#include <QtGui/QPainter>
+#include <QtGui/QMainWindow>
+#include <QtGui/QToolBar>
+#include <QtGui/QHeaderView>
+#include <QtGui/QMenuBar>
+#include <QtGui/QComboBox>
+#include <QtGui/QSpinBox>
+#include <QtGui/QScrollBar>
+#include <QtGui/QAbstractButton>
+#include <QtGui/QToolButton>
+#include <QtGui/QGroupBox>
+#include <QtGui/QRadioButton>
+#include <QtGui/QCheckBox>
+#include <QtGui/QTreeView>
+#include <qpixmapcache.h>
+#undef signals // Collides with GTK stymbols
+#include "qgtkpainter_p.h"
+
+#include <private/qcleanlooksstyle_p.h>
+
+
+QT_BEGIN_NAMESPACE
+
+typedef QStringList (*_qt_filedialog_open_filenames_hook)(QWidget * parent, const QString &caption, const QString &dir,
+ const QString &filter, QString *selectedFilter, QFileDialog::Options options);
+typedef QString (*_qt_filedialog_open_filename_hook) (QWidget * parent, const QString &caption, const QString &dir,
+ const QString &filter, QString *selectedFilter, QFileDialog::Options options);
+typedef QString (*_qt_filedialog_save_filename_hook) (QWidget * parent, const QString &caption, const QString &dir,
+ const QString &filter, QString *selectedFilter, QFileDialog::Options options);
+typedef QString (*_qt_filedialog_existing_directory_hook)(QWidget *parent, const QString &caption, const QString &dir,
+ QFileDialog::Options options);
+
+extern Q_GUI_EXPORT _qt_filedialog_open_filename_hook qt_filedialog_open_filename_hook;
+extern Q_GUI_EXPORT _qt_filedialog_open_filenames_hook qt_filedialog_open_filenames_hook;
+extern Q_GUI_EXPORT _qt_filedialog_save_filename_hook qt_filedialog_save_filename_hook;
+extern Q_GUI_EXPORT _qt_filedialog_existing_directory_hook qt_filedialog_existing_directory_hook;
+
+static const char * const dock_widget_close_xpm[] =
+ {
+ "11 13 5 1",
+ " c None",
+ ". c #D5CFCB",
+ "+ c #6C6A67",
+ "@ c #6C6A67",
+ "$ c #B5B0AC",
+ " ",
+ " @@@@@@@@@ ",
+ "@+ +@",
+ "@ +@ @+ @",
+ "@ @@@ @@@ @",
+ "@ @@@@@ @",
+ "@ @@@ @",
+ "@ @@@@@ @",
+ "@ @@@ @@@ @",
+ "@ +@ @+ @",
+ "@+ +@",
+ " @@@@@@@@@ ",
+ " "
+ };
+
+static const char * const dock_widget_restore_xpm[] =
+ {
+ "11 13 5 1",
+ " c None",
+ ". c #D5CFCB",
+ "+ c #6C6A67",
+ "@ c #6C6A67",
+ "# c #6C6A67",
+ " ",
+ " @@@@@@@@@ ",
+ "@+ +@",
+ "@ #@@@# @",
+ "@ @ @ @",
+ "@ #@@@# @ @",
+ "@ @ @ @ @",
+ "@ @ @@@ @",
+ "@ @ @ @",
+ "@ #@@@@ @",
+ "@+ +@",
+ " @@@@@@@@@ ",
+ " "
+ };
+
+
+class QGtkStylePrivate : public QCleanlooksStylePrivate
+{
+ Q_DECLARE_PUBLIC(QGtkStyle)
+public:
+ QGtkStylePrivate()
+ : QCleanlooksStylePrivate()
+ {}
+};
+
+static const int groupBoxBottomMargin = 2; // space below the groupbox
+static const int groupBoxTitleMargin = 6; // space between contents and title
+static const int groupBoxTopMargin = 2;
+static bool UsePixmapCache = true;
+
+// Get size of the arrow controls in a GtkSpinButton
+static int spinboxArrowSize()
+{
+ const int MIN_ARROW_WIDTH = 6;
+ GtkWidget *spinButton = QGtk::gtkWidget(QLS("GtkSpinButton"));
+ GtkStyle *style = spinButton->style;
+ gint size = QGtk::pango_font_description_get_size (style->font_desc);
+ gint arrow_size;
+ arrow_size = qMax(PANGO_PIXELS (size), MIN_ARROW_WIDTH) + style->xthickness;
+ arrow_size += arrow_size%2 + 1;
+ return arrow_size;
+}
+
+static QColor mergedColors(const QColor &colorA, const QColor &colorB, int factor = 50)
+{
+ const int maxFactor = 100;
+ QColor tmp = colorA;
+ tmp.setRed((tmp.red() * factor) / maxFactor + (colorB.red() * (maxFactor - factor)) / maxFactor);
+ tmp.setGreen((tmp.green() * factor) / maxFactor + (colorB.green() * (maxFactor - factor)) / maxFactor);
+ tmp.setBlue((tmp.blue() * factor) / maxFactor + (colorB.blue() * (maxFactor - factor)) / maxFactor);
+ return tmp;
+}
+
+static GdkColor fromQColor(const QColor &color)
+{
+ GdkColor retval;
+ retval.red = color.red() * 255;
+ retval.green = color.green() * 255;
+ retval.blue = color.blue() * 255;
+ return retval;
+}
+
+// Note this is different from uniqueName as used in QGtkPainter
+static QString uniqueName(const QString &key, const QStyleOption *option, const QSize &size)
+{
+ QString tmp;
+ const QStyleOptionComplex *complexOption = qstyleoption_cast<const QStyleOptionComplex *>(option);
+ tmp.sprintf("%s-%d-%d-%d-%lld-%dx%d", key.toLatin1().constData(), uint(option->state),
+ option->direction, complexOption ? uint(complexOption->activeSubControls) : uint(0),
+ option->palette.cacheKey(), size.width(), size.height());
+ return tmp;
+}
+
+/*!
+ \class QGtkStyle
+ \brief The QGtkStyle class provides a widget style rendered by GTK+
+ \since 4.5
+
+ The QGtkStyle style provides a look and feel that integrates well
+ into GTK-based desktop environments such as the XFCe and GNOME.
+
+ It does this by making use of the GTK+ theme engine, ensuring
+ that Qt applications look and feel native on these platforms.
+
+ Note: The style requires GTK+ version 2.10 or later.
+ The Qt3-based "Qt" GTK+ theme engine will not work with QGtkStyle.
+
+ \sa {Cleanlooks Style Widget Gallery}, QWindowsXPStyle, QMacStyle, QWindowsStyle,
+ QCDEStyle, QMotifStyle, QPlastiqueStyle, QCleanlooksStyle
+*/
+
+/*!
+ Constructs a QGtkStyle object.
+*/
+QGtkStyle::QGtkStyle()
+{
+ QGtk::initGtkWidgets();
+}
+
+/*!
+ Destroys the QGtkStyle object.
+*/
+QGtkStyle::~QGtkStyle()
+{
+}
+
+/*!
+ \reimp
+*/
+QPalette QGtkStyle::standardPalette() const
+{
+ QPalette palette = QCleanlooksStyle::standardPalette();
+ if (QGtk::isThemeAvailable()) {
+ GtkStyle *style = QGtk::gtkStyle();
+ GtkWidget *gtkButton = QGtk::gtkWidget(QLS("GtkButton"));
+ GtkWidget *gtkEntry = QGtk::gtkWidget(QLS("GtkEntry"));
+
+ GdkColor gdkBg, gdkBase, gdkText, gdkForeground, gdkSbg, gdkSfg;
+ QColor bg, base, text, fg, highlight, highlightText;
+ gdkBg = style->bg[GTK_STATE_NORMAL];
+ gdkForeground = gtkButton->style->fg[GTK_STATE_NORMAL];
+
+ // Our base and selected color is primarily used for text
+ // so we assume a gtkEntry will have the most correct value
+ gdkBase = gtkEntry->style->base[GTK_STATE_NORMAL];
+ gdkText = gtkEntry->style->text[GTK_STATE_NORMAL];
+ gdkSbg = gtkEntry->style->base[GTK_STATE_SELECTED];
+ gdkSfg = gtkEntry->style->text[GTK_STATE_SELECTED];
+ bg = QColor(gdkBg.red>>8, gdkBg.green>>8, gdkBg.blue>>8);
+ text = QColor(gdkText.red>>8, gdkText.green>>8, gdkText.blue>>8);
+ fg = QColor(gdkForeground.red>>8, gdkForeground.green>>8, gdkForeground.blue>>8);
+ base = QColor(gdkBase.red>>8, gdkBase.green>>8, gdkBase.blue>>8);
+ highlight = QColor(gdkSbg.red>>8, gdkSbg.green>>8, gdkSbg.blue>>8);
+ highlightText = QColor(gdkSfg.red>>8, gdkSfg.green>>8, gdkSfg.blue>>8);
+
+ palette.setColor(QPalette::HighlightedText, highlightText);
+ palette.setColor(QPalette::Light, bg.lighter(125));
+ palette.setColor(QPalette::Shadow, bg.darker(130));
+ palette.setColor(QPalette::Dark, bg.darker(120));
+ palette.setColor(QPalette::Text, text);
+ palette.setColor(QPalette::WindowText, fg);
+ palette.setColor(QPalette::ButtonText, fg);
+ palette.setColor(QPalette::Base, base);
+
+ QColor alternateRowColor = palette.base().color().lighter(93); // ref gtkstyle.c draw_flat_box
+ GtkWidget *gtkTreeView = QGtk::gtkWidget(QLS("GtkTreeView"));
+ GdkColor *gtkAltBase = NULL;
+ QGtk::gtk_widget_style_get(gtkTreeView, "odd-row-color", &gtkAltBase, NULL);
+ if (gtkAltBase) {
+ alternateRowColor = QColor(gtkAltBase->red>>8, gtkAltBase->green>>8, gtkAltBase->blue>>8);
+ QGtk::gdk_color_free(gtkAltBase);
+ }
+ palette.setColor(QPalette::AlternateBase, alternateRowColor);
+
+ palette.setColor(QPalette::Window, bg);
+ palette.setColor(QPalette::Button, bg);
+ palette.setColor(QPalette::Background, bg);
+ QColor disabled((fg.red() + bg.red()) / 2,
+ (fg.green() + bg.green())/ 2,
+ (fg.blue() + bg.blue()) / 2);
+ palette.setColor(QPalette::Disabled, QPalette::Text, disabled);
+ palette.setColor(QPalette::Disabled, QPalette::WindowText, disabled);
+ palette.setColor(QPalette::Disabled, QPalette::Foreground, disabled);
+ palette.setColor(QPalette::Disabled, QPalette::ButtonText, disabled);
+ palette.setColor(QPalette::Highlight, highlight);
+ // calculate disabled colors by removing saturation
+ highlight.setHsv(highlight.hue(), 0, highlight.value(), highlight.alpha());
+ highlightText.setHsv(highlightText.hue(), 0, highlightText.value(), highlightText.alpha());
+ palette.setColor(QPalette::Disabled, QPalette::Highlight, highlight);
+ palette.setColor(QPalette::Disabled, QPalette::HighlightedText, highlightText);
+ style = QGtk::gtk_rc_get_style_by_paths(QGtk::gtk_settings_get_default(), "gtk-tooltips", "GtkWindow", Q_GTK_TYPE_WINDOW);
+ if (style) {
+ gdkText = style->fg[GTK_STATE_NORMAL];
+ text = QColor(gdkText.red>>8, gdkText.green>>8, gdkText.blue>>8);
+ palette.setColor(QPalette::ToolTipText, text);
+ }
+ }
+ return palette;
+}
+
+/*!
+ \reimp
+*/
+void QGtkStyle::polish(QPalette &palette)
+{
+ // QCleanlooksStyle will alter the palette, hence we do
+ // not want to polish the palette unless we are using it as
+ // the fallback
+ if (!QGtk::isThemeAvailable())
+ QCleanlooksStyle::polish(palette);
+ else
+ palette = standardPalette();
+}
+
+/*!
+ \reimp
+*/
+void QGtkStyle::polish(QApplication *app)
+{
+ QCleanlooksStyle::polish(app);
+ // Custom fonts and palettes with QtConfig are intentionally
+ // not supported as these should be entirely determined by
+ // current Gtk settings
+ if (app->desktopSettingsAware() && QGtk::isThemeAvailable()) {
+ QApplicationPrivate::setSystemPalette(standardPalette());
+ QApplicationPrivate::setSystemFont(QGtk::getThemeFont());
+ if (!QGtk::isKDE4Session()) {
+ qt_filedialog_open_filename_hook = &QGtk::openFilename;
+ qt_filedialog_save_filename_hook = &QGtk::saveFilename;
+ qt_filedialog_open_filenames_hook = &QGtk::openFilenames;
+ qt_filedialog_existing_directory_hook = &QGtk::openDirectory;
+ }
+ }
+}
+
+/*!
+ \reimp
+*/
+void QGtkStyle::unpolish(QApplication *app)
+{
+ QCleanlooksStyle::unpolish(app);
+ QPixmapCache::clear();
+
+ if (app->desktopSettingsAware() && QGtk::isThemeAvailable()
+ && !QGtk::isKDE4Session()) {
+ qt_filedialog_open_filename_hook = 0;
+ qt_filedialog_save_filename_hook = 0;
+ qt_filedialog_open_filenames_hook = 0;
+ qt_filedialog_existing_directory_hook = 0;
+ }
+}
+
+/*!
+ \reimp
+*/
+void QGtkStyle::polish(QWidget *widget)
+{
+ QCleanlooksStyle::polish(widget);
+ if (!QGtk::isThemeAvailable())
+ return;
+
+ if (qobject_cast<QAbstractButton*>(widget)
+ || qobject_cast<QToolButton*>(widget)
+ || qobject_cast<QComboBox*>(widget)
+ || qobject_cast<QGroupBox*>(widget)
+ || qobject_cast<QScrollBar*>(widget)
+ || qobject_cast<QSlider*>(widget)
+ || qobject_cast<QAbstractSpinBox*>(widget)
+ || qobject_cast<QSpinBox*>(widget)
+ || qobject_cast<QHeaderView*>(widget))
+ widget->setAttribute(Qt::WA_Hover);
+ else if (QTreeView *tree = qobject_cast<QTreeView *> (widget))
+ tree->viewport()->setAttribute(Qt::WA_Hover);
+
+ QGtk::applyGtkSystemPalette(widget);
+}
+
+/*!
+ \reimp
+*/
+void QGtkStyle::unpolish(QWidget *widget)
+{
+ QCleanlooksStyle::unpolish(widget);
+}
+
+/*!
+ \reimp
+*/
+int QGtkStyle::pixelMetric(PixelMetric metric,
+
+ const QStyleOption *option,
+ const QWidget *widget) const
+{
+ if (!QGtk::isThemeAvailable())
+ return QCleanlooksStyle::pixelMetric(metric, option, widget);
+
+ switch (metric) {
+ case PM_MenuButtonIndicator:
+ return 20;
+
+ case PM_TabBarBaseOverlap:
+ return 1;
+
+ case PM_ToolBarSeparatorExtent:
+ return 11;
+
+ case PM_ToolBarFrameWidth:
+ return 1;
+
+ case PM_ToolBarItemSpacing:
+ return 0;
+
+ case PM_ButtonShiftHorizontal: {
+ GtkWidget *gtkButton = QGtk::gtkWidget(QLS("GtkButton"));
+ guint horizontal_shift;
+ QGtk::gtk_widget_style_get(gtkButton, "child-displacement-x", &horizontal_shift, NULL);
+ return horizontal_shift;
+ }
+
+ case PM_ButtonShiftVertical: {
+ GtkWidget *gtkButton = QGtk::gtkWidget(QLS("GtkButton"));
+ guint vertical_shift;
+ QGtk::gtk_widget_style_get(gtkButton, "child-displacement-y", &vertical_shift, NULL);
+ return vertical_shift;
+ }
+
+ case PM_MenuBarPanelWidth:
+ return 0;
+
+ case PM_MenuPanelWidth: {
+ GtkWidget *gtkMenu = QGtk::gtkWidget(QLS("GtkMenu"));
+ guint horizontal_padding = 0;
+ // horizontal-padding is used by Maemo to get thicker borders
+ if (!QGtk::gtk_check_version(2, 10, 0))
+ QGtk::gtk_widget_style_get(gtkMenu, "horizontal-padding", &horizontal_padding, NULL);
+ int padding = qMax<int>(gtkMenu->style->xthickness, horizontal_padding);
+ return padding;
+ }
+
+ case PM_ButtonIconSize: {
+ int retVal = 24;
+ GtkSettings *settings = QGtk::gtk_settings_get_default();
+ gchararray icon_sizes;
+ g_object_get(settings, "gtk-icon-sizes", &icon_sizes, NULL);
+ QStringList values = QString(QLS(icon_sizes)).split(QLatin1Char(':'));
+ g_free(icon_sizes);
+ QChar splitChar(QLatin1Char(','));
+ foreach (const QString &value, values) {
+ if (value.startsWith(QLS("gtk-button="))) {
+ QString iconSize = value.right(value.size() - 11);
+
+ if (iconSize.contains(splitChar))
+ retVal = iconSize.split(splitChar)[0].toInt();
+ break;
+ }
+ }
+ return retVal;
+ }
+
+ case PM_MenuVMargin:
+
+ case PM_MenuHMargin:
+ return 0;
+
+ case PM_DockWidgetTitleMargin:
+ return 0;
+
+ case PM_DockWidgetTitleBarButtonMargin:
+ return 5;
+
+ case PM_TabBarTabVSpace:
+ return 12;
+
+ case PM_TabBarTabHSpace:
+ return 14;
+
+ case PM_TabBarTabShiftVertical:
+ return 2;
+
+ case PM_ToolBarHandleExtent:
+ return 9;
+
+ case PM_SplitterWidth:
+ return 6;
+
+ case PM_SliderThickness:
+ case PM_SliderControlThickness: {
+ GtkWidget *gtkScale = QGtk::gtkWidget(QLS("GtkHScale"));
+ gint val;
+ QGtk::gtk_widget_style_get(gtkScale, "slider-width", &val, NULL);
+ if (metric == PM_SliderControlThickness)
+ return val + 2*gtkScale->style->ythickness;
+ return val;
+ }
+
+ case PM_ScrollBarExtent: {
+ gint sliderLength;
+ gint trough_border;
+ GtkWidget *hScrollbar = QGtk::gtkWidget(QLS("GtkHScrollbar"));
+ QGtk::gtk_widget_style_get(hScrollbar,
+ "trough-border", &trough_border,
+ "slider-width", &sliderLength,
+ NULL);
+ return sliderLength + trough_border*2;
+ }
+
+ case PM_ScrollBarSliderMin:
+ return 34;
+
+ case PM_SliderLength:
+ gint val;
+ QGtk::gtk_widget_style_get(QGtk::gtkWidget(QLS("GtkHScale")), "slider-length", &val, NULL);
+ return val;
+
+ case PM_ExclusiveIndicatorWidth:
+ case PM_ExclusiveIndicatorHeight:
+ case PM_IndicatorWidth:
+ case PM_IndicatorHeight: {
+ GtkWidget *gtkCheckButton = QGtk::gtkWidget(QLS("GtkCheckButton"));
+ gint size, spacing;
+ QGtk::gtk_widget_style_get(gtkCheckButton, "indicator-spacing", &spacing, "indicator-size", &size, NULL);
+ return size + 2 * spacing;
+ }
+
+ case PM_MenuBarVMargin: {
+ GtkWidget *gtkMenubar = QGtk::gtkWidget(QLS("GtkMenuBar"));
+ return qMax(0, gtkMenubar->style->ythickness);
+ }
+ case PM_ScrollView_ScrollBarSpacing:
+ {
+ gint spacing = 3;
+ GtkWidget *gtkScrollWindow = QGtk::gtkWidget(QLS("GtkScrolledWindow"));
+ Q_ASSERT(gtkScrollWindow);
+ QGtk::gtk_widget_style_get(gtkScrollWindow, "scrollbar-spacing", &spacing, NULL);
+ return spacing;
+ }
+ case PM_SubMenuOverlap: {
+ gint offset = 0;
+ GtkWidget *gtkMenu = QGtk::gtkWidget(QLS("GtkMenu"));
+ QGtk::gtk_widget_style_get(gtkMenu, "horizontal-offset", &offset, NULL);
+ return offset;
+ }
+ default:
+ return QCleanlooksStyle::pixelMetric(metric, option, widget);
+ }
+}
+
+/*!
+ \reimp
+*/
+int QGtkStyle::styleHint(StyleHint hint, const QStyleOption *option, const QWidget *widget,
+
+ QStyleHintReturn *returnData = 0) const
+{
+ if (!QGtk::isThemeAvailable())
+ return QCleanlooksStyle::styleHint(hint, option, widget, returnData);
+
+ switch (hint) {
+
+ case SH_DialogButtonLayout: {
+ int ret = QDialogButtonBox::GnomeLayout;
+ gboolean alternateOrder = 0;
+ GtkSettings *settings = QGtk::gtk_settings_get_default();
+ g_object_get(settings, "gtk-alternative-button-order", &alternateOrder, NULL);
+
+ if (alternateOrder)
+ ret = QDialogButtonBox::WinLayout;
+
+ return ret;
+ }
+
+ break;
+
+ case SH_SpinControls_DisableOnBounds:
+ return int(true);
+
+ case SH_DitherDisabledText:
+ return int(false);
+
+ case SH_ComboBox_Popup: {
+ GtkWidget *gtkComboBox = QGtk::gtkWidget(QLS("GtkComboBox"));
+ gboolean appears_as_list;
+ QGtk::gtk_widget_style_get((GtkWidget*)gtkComboBox, "appears-as-list", &appears_as_list, NULL);
+ return appears_as_list ? 0 : 1;
+ }
+
+ case SH_MenuBar_AltKeyNavigation:
+ return int(false);
+
+ case SH_EtchDisabledText:
+ return int(false);
+
+ case SH_Menu_SubMenuPopupDelay: {
+ gint delay = 225;
+ GtkSettings *settings = QGtk::gtk_settings_get_default();
+ g_object_get(settings, "gtk-menu-popup-delay", &delay, NULL);
+ return delay;
+ }
+
+ case SH_ScrollView_FrameOnlyAroundContents: {
+ gboolean scrollbars_within_bevel = false;
+ if (widget && widget->isWindow())
+ scrollbars_within_bevel = true;
+ else if (!QGtk::gtk_check_version(2, 12, 0)) {
+ GtkWidget *gtkScrollWindow = QGtk::gtkWidget(QLS("GtkScrolledWindow"));
+ QGtk::gtk_widget_style_get(gtkScrollWindow, "scrollbars-within-bevel", &scrollbars_within_bevel, NULL);
+ }
+ return !scrollbars_within_bevel;
+ }
+
+ default:
+ return QCleanlooksStyle::styleHint(hint, option, widget, returnData);
+ }
+}
+
+/*!
+ \reimp
+*/
+void QGtkStyle::drawPrimitive(PrimitiveElement element,
+
+ const QStyleOption *option,
+ QPainter *painter,
+ const QWidget *widget) const
+{
+ if (!QGtk::isThemeAvailable()) {
+ QCleanlooksStyle::drawPrimitive(element, option, painter, widget);
+ return;
+ }
+
+ GtkStyle* style = QGtk::gtkStyle();
+ QGtkPainter gtkPainter(painter);
+
+ switch (element) {
+
+ case PE_PanelTipLabel: {
+ GtkWidget *gtkWindow = QGtk::gtkWidget(QLS("GtkWindow")); // The Murrine Engine currently assumes a widget is passed
+ style = QGtk::gtk_rc_get_style_by_paths(QGtk::gtk_settings_get_default(), "gtk-tooltips", "GtkWindow", Q_GTK_TYPE_WINDOW);
+ gtkPainter.paintFlatBox(gtkWindow, "tooltip", option->rect, GTK_STATE_NORMAL, GTK_SHADOW_NONE, style);
+ }
+ break;
+
+ case PE_PanelStatusBar: {
+ if (widget && widget->testAttribute(Qt::WA_SetPalette) &&
+ option->palette.resolve() & (1 << QPalette::Window)) {
+ // Respect custom palette
+ painter->fillRect(option->rect, option->palette.window());
+ break;
+ }
+ GtkShadowType shadow_type;
+ GtkWidget *gtkStatusbarFrame = QGtk::gtkWidget(QLS("GtkStatusbar.GtkFrame"));
+ QGtk::gtk_widget_style_get(gtkStatusbarFrame->parent, "shadow-type", &shadow_type, NULL);
+ gtkPainter.paintShadow(gtkStatusbarFrame, "frame", option->rect, GTK_STATE_NORMAL,
+ shadow_type, gtkStatusbarFrame->style);
+ }
+ break;
+
+ case PE_IndicatorHeaderArrow:
+ if (const QStyleOptionHeader *header = qstyleoption_cast<const QStyleOptionHeader *>(option)) {
+ GtkWidget *gtkTreeHeader = QGtk::gtkWidget(QLS("GtkTreeView.GtkButton"));
+ GtkStateType state = gtkPainter.gtkState(option);
+ style = gtkTreeHeader->style;
+ GtkArrowType type = GTK_ARROW_UP;
+ QRect r = header->rect;
+ QImage arrow;
+ if (header->sortIndicator & QStyleOptionHeader::SortUp)
+ type = GTK_ARROW_UP;
+ else if (header->sortIndicator & QStyleOptionHeader::SortDown)
+ type = GTK_ARROW_DOWN;
+
+ gtkPainter.paintArrow(gtkTreeHeader, "button", option->rect.adjusted(1, 1, -1, -1), type, state,
+ GTK_SHADOW_NONE, FALSE, style);
+ }
+ break;
+
+ case PE_FrameFocusRect:
+ if (!widget || qobject_cast<const QAbstractItemView*>(widget))
+ QCleanlooksStyle::drawPrimitive(element, option, painter, widget);
+ else {
+ // ### this mess should move to subcontrolrect
+ QRect frameRect = option->rect.adjusted(1, 1, -1, -2);
+
+ if (qobject_cast<const QTabBar*>(widget))
+ frameRect.adjust(-1, 1, 1, 1);
+
+ gtkPainter.paintFocus(NULL, "tab", frameRect, GTK_STATE_ACTIVE, style);
+ }
+ break;
+
+ case PE_IndicatorBranch:
+ if (option->state & State_Children) {
+ QRect rect = option->rect;
+ rect = QRect(0, 0, 10, 10);
+ rect.moveCenter(option->rect.center());
+ rect.translate(2, 0);
+ GtkExpanderStyle openState = GTK_EXPANDER_EXPANDED;
+ GtkExpanderStyle closedState = GTK_EXPANDER_COLLAPSED;
+ GtkWidget *gtkExpander = QGtk::gtkWidget(QLS("GtkExpander"));
+ guint expanderSize;
+ QGtk::gtk_widget_style_get(gtkExpander, "expander-size", &expanderSize, NULL);
+ // Note CleanIce will crash unless a GtkExpander is provided
+ // but providing the expander will enforce the expander-size, which we
+ // don't neccessarily have room for
+ GtkStateType state = GTK_STATE_NORMAL;
+ if (!(option->state & State_Enabled))
+ state = GTK_STATE_INSENSITIVE;
+ else if (option->state & State_MouseOver)
+ state = GTK_STATE_PRELIGHT;
+ gtkPainter.paintExpander(expanderSize <= 10 ? gtkExpander : NULL, "expander", rect, state,
+ option->state & State_Open ? openState : closedState , gtkExpander->style);
+ }
+ break;
+ case PE_PanelItemViewItem:
+ if (const QStyleOptionViewItemV4 *vopt = qstyleoption_cast<const QStyleOptionViewItemV4 *>(option)) {
+ if (vopt->state & State_Selected) {
+ QLinearGradient gradient;
+ gradient.setStart(option->rect.left(), option->rect.top());
+ gradient.setFinalStop(option->rect.left(), option->rect.bottom());
+ gradient.setColorAt(0, option->palette.highlight().color().lighter(105));
+ gradient.setColorAt(0.5, option->palette.highlight().color().lighter(101));
+ gradient.setColorAt(0.51, option->palette.highlight().color().darker(101));
+ gradient.setColorAt(1, option->palette.highlight().color().darker(105));
+ painter->fillRect(option->rect, gradient);
+ } else {
+ if (vopt->backgroundBrush.style() != Qt::NoBrush) {
+ QPointF oldBO = painter->brushOrigin();
+ painter->setBrushOrigin(vopt->rect.topLeft());
+ painter->fillRect(vopt->rect, vopt->backgroundBrush);
+ painter->setBrushOrigin(oldBO);
+ }
+ }
+ }
+ break;
+ case PE_IndicatorToolBarSeparator:
+ {
+ const int margin = 6;
+ GtkWidget *gtkSeparator = QGtk::gtkWidget(QLS("GtkToolbar.GtkSeparatorToolItem"));
+ if (option->state & State_Horizontal) {
+ const int offset = option->rect.width()/2;
+ QRect rect = option->rect.adjusted(offset, margin, 0, -margin);
+ painter->setPen(QPen(option->palette.background().color().darker(110)));
+ gtkPainter.paintVline( gtkSeparator, "vseparator",
+ rect, GTK_STATE_NORMAL, gtkSeparator->style,
+ 0, rect.height(), 0);
+ } else { //Draw vertical separator
+ const int offset = option->rect.height()/2;
+ QRect rect = option->rect.adjusted(margin, offset, -margin, 0);
+ painter->setPen(QPen(option->palette.background().color().darker(110)));
+ gtkPainter.paintHline( gtkSeparator, "hseparator",
+ rect, GTK_STATE_NORMAL, gtkSeparator->style,
+ 0, rect.width(), 0);
+ }
+ }
+ break;
+
+ case PE_IndicatorToolBarHandle: {
+ GtkWidget *gtkToolbar = QGtk::gtkWidget(QLS("GtkToolbar"));
+ GtkShadowType shadow_type;
+ QGtk::gtk_widget_style_get(gtkToolbar, "shadow-type", &shadow_type, NULL);
+ //Note when the toolbar is horizontal, the handle is vertical
+ painter->setClipRect(option->rect);
+ gtkPainter.paintHandle(gtkToolbar, "toolbar", option->rect.adjusted(-1, -1 ,0 ,1),
+ GTK_STATE_NORMAL, shadow_type, !(option->state & State_Horizontal) ?
+ GTK_ORIENTATION_HORIZONTAL : GTK_ORIENTATION_VERTICAL, gtkToolbar->style);
+ }
+ break;
+
+ case PE_IndicatorArrowUp:
+ case PE_IndicatorArrowDown:
+ case PE_IndicatorArrowLeft:
+ case PE_IndicatorArrowRight: {
+
+
+ GtkArrowType type = GTK_ARROW_UP;
+
+ switch (element) {
+
+ case PE_IndicatorArrowDown:
+ type = GTK_ARROW_DOWN;
+ break;
+
+ case PE_IndicatorArrowLeft:
+ type = GTK_ARROW_LEFT;
+ break;
+
+ case PE_IndicatorArrowRight:
+ type = GTK_ARROW_RIGHT;
+ break;
+
+ default:
+ break;
+ }
+ int size = qMin(option->rect.height(), option->rect.width());
+ int border = (size > 9) ? (size/4) : 0; //Allow small arrows to have exact dimensions
+ int bsx = 0, bsy = 0;
+ if (option->state & State_Sunken) {
+ bsx = pixelMetric(PM_ButtonShiftHorizontal);
+ bsy = pixelMetric(PM_ButtonShiftVertical);
+ }
+ QRect arrowRect = option->rect.adjusted(border + bsx, border + bsy, -border + bsx, -border + bsy);
+ GtkShadowType shadow = option->state & State_Sunken ? GTK_SHADOW_IN : GTK_SHADOW_OUT;
+ GtkStateType state = gtkPainter.gtkState(option);
+
+ QColor arrowColor = option->palette.buttonText().color();
+ GtkWidget *gtkArrow = QGtk::gtkWidget(QLS("GtkArrow"));
+ GdkColor color = fromQColor(arrowColor);
+ QGtk::gtk_widget_modify_fg (gtkArrow, state, &color);
+ gtkPainter.paintArrow(gtkArrow, "button", arrowRect,
+ type, state, shadow, FALSE, gtkArrow->style,
+ QString::number(arrowColor.rgba(), 16));
+ // Passing NULL will revert the color change
+ QGtk::gtk_widget_modify_fg (gtkArrow, state, NULL);
+ }
+ break;
+
+ case PE_FrameGroupBox:
+ // Do nothing here, the GNOME groupboxes are flat
+ break;
+
+ case PE_PanelMenu: {
+ GtkWidget *gtkMenu = QGtk::gtkWidget(QLS("GtkMenu"));
+ gtkPainter.setAlphaSupport(false); // Note, alpha disabled for performance reasons
+ gtkPainter.paintBox(gtkMenu, "menu", option->rect, GTK_STATE_NORMAL, GTK_SHADOW_OUT, gtkMenu->style, QString());
+ }
+ break;
+
+ case PE_FrameMenu:
+ //This is actually done by PE_Widget due to a clipping issue
+ //Otherwise Menu items will not be able to span the entire menu width
+
+ // This is only used by floating tool bars
+ if (qobject_cast<const QToolBar *>(widget)) {
+ GtkWidget *gtkMenubar = QGtk::gtkWidget(QLS("GtkMenuBar"));
+ gtkPainter.paintBox( gtkMenubar, "toolbar", option->rect,
+ GTK_STATE_NORMAL, GTK_SHADOW_OUT, style);
+ gtkPainter.paintBox( gtkMenubar, "menu", option->rect,
+ GTK_STATE_NORMAL, GTK_SHADOW_OUT, style);
+ }
+ break;
+
+ case PE_FrameLineEdit: {
+ GtkWidget *gtkEntry = QGtk::gtkWidget(QLS("GtkEntry"));
+
+ if (option->state & State_HasFocus)
+ GTK_WIDGET_SET_FLAGS(gtkEntry, GTK_HAS_FOCUS);
+ else
+ GTK_WIDGET_UNSET_FLAGS(gtkEntry, GTK_HAS_FOCUS);
+
+ gboolean interior_focus;
+ gint focus_line_width;
+ QRect rect = option->rect;
+ QGtk::gtk_widget_style_get(gtkEntry,
+ "interior-focus", &interior_focus,
+ "focus-line-width", &focus_line_width, NULL);
+
+ if (!interior_focus && option->state & State_HasFocus)
+ rect.adjust(focus_line_width, focus_line_width, -focus_line_width, -focus_line_width);
+
+ gtkPainter.paintShadow(gtkEntry, "entry", rect, option->state & State_Enabled ?
+ GTK_STATE_NORMAL : GTK_STATE_INSENSITIVE,
+ GTK_SHADOW_IN, gtkEntry->style,
+ option->state & State_HasFocus ? QLS("focus") : QString());
+
+ if (!interior_focus && option->state & State_HasFocus)
+ gtkPainter.paintShadow(gtkEntry, "entry", option->rect, option->state & State_Enabled ?
+ GTK_STATE_ACTIVE : GTK_STATE_INSENSITIVE,
+ GTK_SHADOW_IN, gtkEntry->style, QLS("GtkEntryShadowIn"));
+ }
+ break;
+
+ case PE_PanelLineEdit:
+ if (const QStyleOptionFrame *panel = qstyleoption_cast<const QStyleOptionFrame *>(option)) {
+ GtkWidget *gtkEntry = QGtk::gtkWidget(QLS("GtkEntry"));
+ if (panel->lineWidth > 0)
+ drawPrimitive(PE_FrameLineEdit, option, painter, widget);
+ uint resolve_mask = option->palette.resolve();
+ QRect textRect = option->rect.adjusted(gtkEntry->style->xthickness, gtkEntry->style->ythickness,
+ -gtkEntry->style->xthickness, -gtkEntry->style->ythickness);
+
+ if (widget && widget->testAttribute(Qt::WA_SetPalette) &&
+ resolve_mask & (1 << QPalette::Base)) // Palette overridden by user
+ painter->fillRect(textRect, option->palette.base().color());
+ else
+ gtkPainter.paintFlatBox( gtkEntry, "entry_bg", textRect,
+ option->state & State_Enabled ? GTK_STATE_NORMAL : GTK_STATE_INSENSITIVE, GTK_SHADOW_NONE, gtkEntry->style);
+ }
+ break;
+
+ case PE_FrameTabWidget:
+ if (const QStyleOptionTabWidgetFrame *frame = qstyleoption_cast<const QStyleOptionTabWidgetFrame*>(option)) {
+ GtkWidget *gtkNotebook = QGtk::gtkWidget(QLS("GtkNotebook"));
+ style = gtkPainter.getStyle(gtkNotebook);
+ gtkPainter.setAlphaSupport(false);
+ GtkShadowType shadow = GTK_SHADOW_OUT;
+ GtkStateType state = GTK_STATE_NORMAL; // Only state supported by gtknotebook
+ if (const QTabWidget *tabwidget = qobject_cast<const QTabWidget*>(widget)) {
+ // We should introduce QStyleOptionTabWidgetFrameV2 to obtain this information
+ // No gap if we do not show the actual tabs
+ QTabBar *tabBar = tabwidget->findChild<QTabBar*>();
+ if (tabwidget->count() > 0 && tabBar->isVisible()) {
+ QRect tabRect = tabBar->tabRect(tabBar->currentIndex());
+ int begin = 0, size = 0;
+ GtkPositionType frameType = GTK_POS_TOP;
+ QTabBar::Shape shape = frame->shape;
+ if (shape == QTabBar::RoundedNorth || shape == QTabBar::RoundedSouth) {
+ begin = option->direction == Qt::LeftToRight ?
+ frame->leftCornerWidgetSize.width() + tabRect.left() :
+ frame->rect.width() - frame->tabBarSize.width() + tabRect.left()
+ - frame->rightCornerWidgetSize.width();
+ size = tabRect.width();
+ frameType = (shape == QTabBar::RoundedNorth) ? GTK_POS_TOP : GTK_POS_BOTTOM;
+ } else {
+ begin = frame->leftCornerWidgetSize.height() + tabRect.top();
+ size = tabRect.height();
+ frameType = (shape == QTabBar::RoundedWest) ? GTK_POS_LEFT : GTK_POS_RIGHT;
+ }
+ gtkPainter.paintBoxGap(gtkNotebook, "notebook", option->rect, state, shadow, frameType,
+ begin, size, style);
+ break; // done
+ }
+ }
+ // Note this is only the fallback option
+ gtkPainter.paintBox(gtkNotebook, "notebook", option->rect, state, shadow, style);
+ }
+ break;
+
+ case PE_PanelButtonCommand: {
+ bool isDefault = false;
+ if (const QStyleOptionButton *btn = qstyleoption_cast<const QStyleOptionButton*>(option))
+ isDefault = btn->features & QStyleOptionButton::DefaultButton;
+
+ GtkStateType state = gtkPainter.gtkState(option);
+ if (option->state & State_On || option->state & State_Sunken)
+ state = GTK_STATE_ACTIVE;
+ GtkWidget *gtkButton = QGtk::gtkWidget(QLS("GtkButton"));
+ gint focusWidth, focusPad;
+ gboolean interiorFocus = false;
+ QGtk::gtk_widget_style_get (gtkButton,
+ "focus-line-width", &focusWidth,
+ "focus-padding", &focusPad,
+ "interior-focus", &interiorFocus, NULL);
+
+ style = gtkButton->style;
+
+ QRect buttonRect = option->rect;
+
+ QString key;
+ if (isDefault) {
+ key += QLS("def");
+ GTK_WIDGET_SET_FLAGS(gtkButton, GTK_HAS_DEFAULT);
+ gtkPainter.paintBox(gtkButton, "buttondefault", buttonRect, state, GTK_SHADOW_IN,
+ style, isDefault ? QLS("d") : QString());
+ } else
+ GTK_WIDGET_UNSET_FLAGS(gtkButton, GTK_HAS_DEFAULT);
+
+ bool hasFocus = option->state & State_HasFocus;
+
+ if (hasFocus) {
+ key += QLS("def");
+ GTK_WIDGET_SET_FLAGS(gtkButton, GTK_HAS_FOCUS);
+
+ } else {
+ GTK_WIDGET_UNSET_FLAGS(gtkButton, GTK_HAS_FOCUS);
+ }
+
+ if (!interiorFocus)
+ buttonRect = buttonRect.adjusted(focusWidth, focusWidth, -focusWidth, -focusWidth);
+
+ GtkShadowType shadow = (option->state & State_Sunken || option->state & State_On ) ?
+ GTK_SHADOW_IN : GTK_SHADOW_OUT;
+
+ gtkPainter.paintBox(gtkButton, "button", buttonRect, state, shadow,
+ style, key);
+ }
+ break;
+
+ case PE_IndicatorRadioButton: {
+ GtkShadowType shadow = GTK_SHADOW_OUT;
+ GtkStateType state = gtkPainter.gtkState(option);
+
+ if (option->state & State_Sunken)
+ state = GTK_STATE_ACTIVE;
+
+ if (option->state & State_NoChange)
+ shadow = GTK_SHADOW_ETCHED_IN;
+ else if (option->state & State_On)
+ shadow = GTK_SHADOW_IN;
+ else
+ shadow = GTK_SHADOW_OUT;
+
+ GtkWidget *gtkRadioButton = QGtk::gtkWidget(QLS("GtkRadioButton"));
+ gint spacing;
+ QGtk::gtk_widget_style_get(gtkRadioButton, "indicator-spacing", &spacing, NULL);
+ QRect buttonRect = option->rect.adjusted(spacing, spacing, -spacing, -spacing);
+ gtkPainter.setClipRect(option->rect);
+ // ### Note: Ubuntulooks breaks when the proper widget is passed
+ // Murrine engine requires a widget not to get RGBA check - warnings
+ GtkWidget *gtkCheckButton = QGtk::gtkWidget(QLS("GtkCheckButton"));
+ gtkPainter.paintOption(gtkCheckButton , buttonRect, state, shadow, gtkRadioButton->style, QLS("radiobutton"));
+
+ }
+ break;
+
+ case PE_IndicatorCheckBox: {
+ GtkShadowType shadow = GTK_SHADOW_OUT;
+ GtkStateType state = gtkPainter.gtkState(option);
+
+ if (option->state & State_Sunken)
+ state = GTK_STATE_ACTIVE;
+
+ if (option->state & State_NoChange)
+ shadow = GTK_SHADOW_ETCHED_IN;
+ else if (option->state & State_On)
+ shadow = GTK_SHADOW_IN;
+ else
+ shadow = GTK_SHADOW_OUT;
+
+ int spacing;
+
+ GtkWidget *gtkCheckButton = QGtk::gtkWidget(QLS("GtkCheckButton"));
+
+ // Some styles such as aero-clone assume they can paint in the spacing area
+ gtkPainter.setClipRect(option->rect);
+
+ QGtk::gtk_widget_style_get(gtkCheckButton, "indicator-spacing", &spacing, NULL);
+
+ QRect checkRect = option->rect.adjusted(spacing, spacing, -spacing, -spacing);
+
+ gtkPainter.paintCheckbox(gtkCheckButton, checkRect, state, shadow, gtkCheckButton->style,
+ QLS("checkbutton"));
+ }
+ break;
+
+#ifndef QT_NO_TABBAR
+
+ case PE_FrameTabBarBase:
+ if (const QStyleOptionTabBarBase *tbb
+ = qstyleoption_cast<const QStyleOptionTabBarBase *>(option)) {
+ QRect tabRect = tbb->rect;
+ QRegion region(tabRect);
+ painter->save();
+ painter->setPen(QPen(option->palette.dark().color().dark(110), 0));
+ switch (tbb->shape) {
+
+ case QTabBar::RoundedNorth:
+ painter->drawLine(tabRect.topLeft(), tabRect.topRight());
+ break;
+
+ case QTabBar::RoundedWest:
+ painter->drawLine(tabRect.left(), tabRect.top(), tabRect.left(), tabRect.bottom());
+ break;
+
+ case QTabBar::RoundedSouth:
+ painter->drawLine(tbb->rect.left(), tbb->rect.bottom(),
+ tabRect.right(), tabRect.bottom());
+ break;
+
+ case QTabBar::RoundedEast:
+ painter->drawLine(tabRect.topRight(), tabRect.bottomRight());
+ break;
+
+ case QTabBar::TriangularNorth:
+ case QTabBar::TriangularEast:
+ case QTabBar::TriangularWest:
+ case QTabBar::TriangularSouth:
+ painter->restore();
+ QWindowsStyle::drawPrimitive(element, option, painter, widget);
+ return;
+ }
+
+ painter->restore();
+ }
+ return;
+
+#endif // QT_NO_TABBAR
+
+ case PE_Widget:
+ break;
+
+ default:
+ QCleanlooksStyle::drawPrimitive(element, option, painter, widget);
+ }
+}
+
+/*!
+ \reimp
+*/
+void QGtkStyle::drawComplexControl(ComplexControl control, const QStyleOptionComplex *option,
+
+ QPainter *painter, const QWidget *widget) const
+{
+ if (!QGtk::isThemeAvailable()) {
+ QCleanlooksStyle::drawComplexControl(control, option, painter, widget);
+ return;
+ }
+
+ GtkStyle* style = QGtk::gtkStyle();
+ QGtkPainter gtkPainter(painter);
+ QColor button = option->palette.button().color();
+ QColor dark;
+ QColor grooveColor;
+ QColor darkOutline;
+ dark.setHsv(button.hue(),
+ qMin(255, (int)(button.saturation()*1.9)),
+ qMin(255, (int)(button.value()*0.7)));
+ grooveColor.setHsv(button.hue(),
+ qMin(255, (int)(button.saturation()*2.6)),
+ qMin(255, (int)(button.value()*0.9)));
+ darkOutline.setHsv(button.hue(),
+ qMin(255, (int)(button.saturation()*3.0)),
+ qMin(255, (int)(button.value()*0.6)));
+
+ QColor alphaCornerColor;
+
+ if (widget)
+ alphaCornerColor = mergedColors(option->palette.color(widget->backgroundRole()), darkOutline);
+ else
+ alphaCornerColor = mergedColors(option->palette.background().color(), darkOutline);
+
+ QPalette palette = option->palette;
+
+ switch (control) {
+
+ case CC_TitleBar:
+ if (const QStyleOptionTitleBar *tb = qstyleoption_cast<const QStyleOptionTitleBar *>(option)) {
+ // Since this is drawn by metacity and not Gtk we
+ // have to rely on Cleanlooks for a fallback
+ QStyleOptionTitleBar copyOpt = *tb;
+ QPalette pal = copyOpt.palette;
+ // Bg color is closer to the window selection than
+ // the base selection color
+ GdkColor gdkBg = style->bg[GTK_STATE_SELECTED];
+ QColor bgColor(gdkBg.red>>8, gdkBg.green>>8, gdkBg.blue>>8);
+ pal.setBrush(QPalette::Active, QPalette::Highlight, bgColor);
+ copyOpt.palette = pal;
+ QCleanlooksStyle::drawComplexControl(control, &copyOpt, painter, widget);
+ }
+ break;
+
+#ifndef QT_NO_GROUPBOX
+
+ case CC_GroupBox:
+ painter->save();
+
+ if (const QStyleOptionGroupBox *groupBox = qstyleoption_cast<const QStyleOptionGroupBox *>(option)) {
+ QRect textRect = subControlRect(CC_GroupBox, groupBox, SC_GroupBoxLabel, widget);
+ QRect checkBoxRect = subControlRect(CC_GroupBox, groupBox, SC_GroupBoxCheckBox, widget);
+ // Draw title
+
+ if ((groupBox->subControls & QStyle::SC_GroupBoxLabel) && !groupBox->text.isEmpty()) {
+ // Draw prelight background
+ GtkWidget *gtkCheckButton = QGtk::gtkWidget(QLS("GtkCheckButton"));
+
+ if (option->state & State_MouseOver) {
+ QRect bgRect = textRect | checkBoxRect;
+ gtkPainter.paintFlatBox(gtkCheckButton, "checkbutton", bgRect.adjusted(0, 0, 0, -2),
+ GTK_STATE_PRELIGHT, GTK_SHADOW_ETCHED_OUT, gtkCheckButton->style);
+ }
+
+ if (!groupBox->text.isEmpty()) {
+ int alignment = int(groupBox->textAlignment);
+ if (!styleHint(QStyle::SH_UnderlineShortcut, option, widget))
+ alignment |= Qt::TextHideMnemonic;
+ QColor textColor = groupBox->textColor; // Note: custom textColor is currently ignored
+ int labelState = GTK_STATE_INSENSITIVE;
+
+ if (option->state & State_Enabled)
+ labelState = (option->state & State_MouseOver) ? GTK_STATE_PRELIGHT : GTK_STATE_NORMAL;
+
+ GdkColor gdkText = gtkCheckButton->style->fg[labelState];
+ textColor = QColor(gdkText.red>>8, gdkText.green>>8, gdkText.blue>>8);
+ painter->setPen(textColor);
+ QFont font = painter->font();
+ font.setBold(true);
+ painter->setFont(font);
+ painter->drawText(textRect, Qt::TextShowMnemonic | Qt::AlignLeft| alignment, groupBox->text);
+
+ if (option->state & State_HasFocus)
+ gtkPainter.paintFocus( NULL, "tab", textRect.adjusted(-4, -1, 0, -3), GTK_STATE_ACTIVE, style);
+ }
+ }
+
+ if (groupBox->subControls & SC_GroupBoxCheckBox) {
+ QStyleOptionButton box;
+ box.QStyleOption::operator=(*groupBox);
+ box.rect = checkBoxRect;
+ drawPrimitive(PE_IndicatorCheckBox, &box, painter, widget);
+ }
+ }
+
+ painter->restore();
+ break;
+#endif // QT_NO_GROUPBOX
+
+#ifndef QT_NO_COMBOBOX
+
+ case CC_ComboBox:
+ // See: http://live.gnome.org/GnomeArt/Tutorials/GtkThemes/GtkComboBox
+ // and http://live.gnome.org/GnomeArt/Tutorials/GtkThemes/GtkComboBoxEntry
+ if (const QStyleOptionComboBox *comboBox = qstyleoption_cast<const QStyleOptionComboBox *>(option)) {
+ bool sunken = comboBox->state & State_On; // play dead, if combobox has no items
+ BEGIN_STYLE_PIXMAPCACHE(QString::fromLatin1("cb-%0-%1").arg(sunken).arg(comboBox->editable));
+ QGtkPainter gtkCachedPainter(p);
+ gtkCachedPainter.setUsePixmapCache(false); // cached externally
+
+ bool isEnabled = (comboBox->state & State_Enabled);
+ bool focus = isEnabled && (comboBox->state & State_HasFocus);
+ QColor buttonShadow = option->palette.dark().color();
+ GtkStateType state = gtkPainter.gtkState(option);
+ int appears_as_list = !styleHint(QStyle::SH_ComboBox_Popup, comboBox, widget);
+ QPixmap cache;
+ QString pixmapName;
+ QStyleOptionComboBox comboBoxCopy = *comboBox;
+ comboBoxCopy.rect = option->rect;
+
+ bool reverse = (option->direction == Qt::RightToLeft);
+ QRect rect = option->rect;
+ QRect arrowButtonRect = subControlRect(CC_ComboBox, &comboBoxCopy,
+ SC_ComboBoxArrow, widget);
+ QRect editRect = subControlRect(CC_ComboBox, &comboBoxCopy,
+ SC_ComboBoxEditField, widget);
+
+ GtkShadowType shadow = (option->state & State_Sunken || option->state & State_On ) ?
+ GTK_SHADOW_IN : GTK_SHADOW_OUT;
+ QString comboBoxPath = QLS(comboBox->editable ? "GtkComboBoxEntry" : "GtkComboBox");
+
+ // We use the gtk widget to position arrows and separators for us
+ GtkWidget *gtkCombo = QGtk::gtkWidget(comboBoxPath);
+ GtkAllocation geometry = {0, 0, option->rect.width(), option->rect.height()};
+ QGtk::gtk_widget_set_direction(gtkCombo, reverse ? GTK_TEXT_DIR_RTL : GTK_TEXT_DIR_LTR);
+ QGtk::gtk_widget_size_allocate(gtkCombo, &geometry);
+
+ QString buttonPath = comboBoxPath + QLS(".GtkToggleButton");
+ GtkWidget *gtkToggleButton = QGtk::gtkWidget(buttonPath);
+ QGtk::gtk_widget_set_direction(gtkToggleButton, reverse ? GTK_TEXT_DIR_RTL : GTK_TEXT_DIR_LTR);
+ if (gtkToggleButton && (appears_as_list || comboBox->editable)) {
+ // Draw the combo box as a line edit with a button next to it
+ if (comboBox->editable || appears_as_list) {
+ GtkStateType frameState = (state == GTK_STATE_PRELIGHT) ? GTK_STATE_NORMAL : state;
+ QString entryPath = QLS(comboBox->editable ? "GtkComboBoxEntry.GtkEntry" : "GtkComboBox.GtkFrame");
+ GtkWidget *gtkEntry = QGtk::gtkWidget(entryPath);
+ QGtk::gtk_widget_set_direction(gtkEntry, reverse ? GTK_TEXT_DIR_RTL : GTK_TEXT_DIR_LTR);
+ QRect frameRect = option->rect;
+
+ if (reverse)
+ frameRect.setLeft(arrowButtonRect.right());
+ else
+ frameRect.setRight(arrowButtonRect.left());
+
+ // Required for inner blue highlight with clearlooks
+ if (focus) {
+ GTK_WIDGET_SET_FLAGS(gtkEntry, GTK_HAS_FOCUS);
+ GTK_WIDGET_SET_FLAGS(gtkToggleButton, GTK_HAS_FOCUS);
+
+ } else {
+ GTK_WIDGET_UNSET_FLAGS(gtkEntry, GTK_HAS_FOCUS);
+ GTK_WIDGET_UNSET_FLAGS(gtkToggleButton, GTK_HAS_FOCUS);
+ }
+
+ // Fill the line edit background
+ // We could have used flat_box with "entry_bg" but that is probably not worth the overhead
+ uint resolve_mask = option->palette.resolve();
+ int xt = gtkEntry->style->xthickness;
+ int yt = gtkEntry->style->ythickness;
+ QRect contentRect = frameRect.adjusted(xt, yt, -xt, -yt);
+ if (widget && widget->testAttribute(Qt::WA_SetPalette) &&
+ resolve_mask & (1 << QPalette::Base)) // Palette overridden by user
+ p->fillRect(contentRect, option->palette.base().color());
+ else {
+ gtkCachedPainter.paintFlatBox(gtkEntry, "entry_bg", contentRect,
+ option->state & State_Enabled ? GTK_STATE_NORMAL : GTK_STATE_INSENSITIVE,
+ GTK_SHADOW_NONE, style, entryPath + QString::number(focus));
+ }
+
+ gtkCachedPainter.paintShadow(gtkEntry, comboBox->editable ? "entry" : "frame", frameRect, frameState,
+ GTK_SHADOW_IN, gtkEntry->style, entryPath +
+ QString::number(focus) + QString::number(comboBox->editable) +
+ QString::number(option->direction));
+ }
+
+ GtkStateType buttonState = GTK_STATE_NORMAL;
+
+ if (!(option->state & State_Enabled))
+ buttonState = GTK_STATE_INSENSITIVE;
+ else if (option->state & State_Sunken || option->state & State_On)
+ buttonState = GTK_STATE_ACTIVE;
+ else if (option->state & State_MouseOver && comboBox->activeSubControls & SC_ComboBoxArrow)
+ buttonState = GTK_STATE_PRELIGHT;
+
+ QRect buttonrect = QRect(gtkToggleButton->allocation.x, gtkToggleButton->allocation.y,
+ gtkToggleButton->allocation.width, gtkToggleButton->allocation.height);
+
+ Q_ASSERT(gtkToggleButton);
+ gtkCachedPainter.paintBox( gtkToggleButton, "button", arrowButtonRect, buttonState,
+ shadow, gtkToggleButton->style, buttonPath +
+ QString::number(focus) + QString::number(option->direction));
+
+ } else {
+ // Draw combo box as a button
+ QRect buttonRect = option->rect;
+
+ if (focus) { // Clearlooks actually check the widget for the default state
+ GTK_WIDGET_SET_FLAGS(gtkToggleButton, GTK_HAS_FOCUS);
+
+ } else {
+ GTK_WIDGET_UNSET_FLAGS(gtkToggleButton, GTK_HAS_FOCUS);
+ }
+
+ gtkCachedPainter.paintBox(gtkToggleButton, "button",
+ buttonRect, state,
+ shadow, gtkToggleButton->style,
+ buttonPath + QString::number(focus));
+ // Draw the separator between label and arrows
+ QString vSeparatorPath = buttonPath + QLS(".GtkHBox.GtkVSeparator");
+
+ if (GtkWidget *gtkVSeparator = QGtk::gtkWidget(vSeparatorPath)) {
+ QRect vLineRect(gtkVSeparator->allocation.x,
+ gtkVSeparator->allocation.y,
+ gtkVSeparator->allocation.width,
+ gtkVSeparator->allocation.height);
+
+ gtkCachedPainter.paintVline( gtkVSeparator, "vseparator",
+ vLineRect, state, gtkVSeparator->style,
+ 0, vLineRect.height(), 0, vSeparatorPath);
+
+
+ gint interiorFocus = true;
+ QGtk::gtk_widget_style_get(gtkToggleButton, "interior-focus", &interiorFocus, NULL);
+ int xt = interiorFocus ? gtkToggleButton->style->xthickness : 0;
+ int yt = interiorFocus ? gtkToggleButton->style->ythickness : 0;
+ if ((focus && (option->state & State_KeyboardFocusChange)))
+ gtkCachedPainter.paintFocus(gtkToggleButton, "button",
+ option->rect.adjusted(xt, yt, -xt, -yt),
+ option->state & State_Sunken ? GTK_STATE_ACTIVE : GTK_STATE_NORMAL,
+ gtkToggleButton->style);
+ }
+ }
+
+ if (comboBox->subControls & SC_ComboBoxArrow) {
+ if (!isEnabled)
+ state = GTK_STATE_INSENSITIVE;
+ else if (sunken)
+ state = GTK_STATE_ACTIVE;
+ else if (option->state & State_MouseOver)
+ state = GTK_STATE_PRELIGHT;
+ else
+ state = GTK_STATE_NORMAL;
+
+ QString arrowPath = comboBoxPath + QLS(appears_as_list ? ".GtkToggleButton.GtkArrow"
+ : ".GtkToggleButton.GtkHBox.GtkArrow");
+
+ GtkWidget *gtkArrow = QGtk::gtkWidget(arrowPath);
+ gfloat scale = 0.7;
+ gint minSize = 15;
+ QRect arrowWidgetRect;
+
+ if (gtkArrow && !QGtk::gtk_check_version(2, 12, 0)) {
+ QGtk::gtk_widget_style_get(gtkArrow, "arrow-scaling", &scale, NULL);
+ QGtk::gtk_widget_style_get(gtkCombo, "arrow-size", &minSize, NULL);
+ }
+ if (gtkArrow) {
+ arrowWidgetRect = QRect(gtkArrow->allocation.x, gtkArrow->allocation.y,
+ gtkArrow->allocation.width, gtkArrow->allocation.height);
+ style = gtkArrow->style;
+ }
+
+ // Note that for some reason the arrow-size is not properly respected with Hildon
+ // Hence we enforce the minimum "arrow-size" ourselves
+ int arrowSize = qMax(qMin(rect.height() - gtkCombo->style->ythickness * 2, minSize),
+ qMin(arrowWidgetRect.width(), arrowWidgetRect.height()));
+ QRect arrowRect(0, 0, static_cast<int>(arrowSize * scale), static_cast<int>(arrowSize * scale));
+
+ arrowRect.moveCenter(arrowWidgetRect.center());
+
+ if (sunken) {
+ int xoff, yoff;
+ GtkWidget *gtkButton = QGtk::gtkWidget(comboBoxPath + QLS(".GtkToggleButton"));
+ QGtk::gtk_widget_style_get(gtkButton, "child-displacement-x", &xoff, NULL);
+ QGtk::gtk_widget_style_get(gtkButton, "child-displacement-y", &yoff, NULL);
+ arrowRect = arrowRect.adjusted(xoff, yoff, xoff, yoff);
+ }
+
+ // Some styles such as Nimbus paint outside the arrowRect
+ // hence we have provide the whole widget as the cliprect
+ if (gtkArrow) {
+ gtkCachedPainter.setClipRect(option->rect);
+ gtkCachedPainter.paintArrow( gtkArrow, "arrow", arrowRect,
+ GTK_ARROW_DOWN, state, GTK_SHADOW_NONE, TRUE,
+ style, arrowPath + QString::number(option->direction));
+ }
+ }
+ END_STYLE_PIXMAPCACHE;
+ }
+ break;
+#endif // QT_NO_COMBOBOX
+#ifndef QT_NO_TOOLBUTTON
+
+ case CC_ToolButton:
+ if (const QStyleOptionToolButton *toolbutton
+ = qstyleoption_cast<const QStyleOptionToolButton *>(option)) {
+ QRect button, menuarea;
+ button = subControlRect(control, toolbutton, SC_ToolButton, widget);
+ menuarea = subControlRect(control, toolbutton, SC_ToolButtonMenu, widget);
+ State bflags = toolbutton->state & ~(State_Sunken | State_MouseOver);
+
+ if (bflags & State_AutoRaise)
+ if (!(bflags & State_MouseOver))
+ bflags &= ~State_Raised;
+
+ State mflags = bflags;
+
+ if (toolbutton->state & State_Sunken) {
+ if (toolbutton->activeSubControls & SC_ToolButton)
+ bflags |= State_Sunken;
+ if (toolbutton->activeSubControls & SC_ToolButtonMenu)
+ mflags |= State_Sunken;
+ } else if (toolbutton->state & State_MouseOver) {
+ if (toolbutton->activeSubControls & SC_ToolButton)
+ bflags |= State_MouseOver;
+ if (toolbutton->activeSubControls & SC_ToolButtonMenu)
+ mflags |= State_MouseOver;
+ }
+
+ QStyleOption tool(0);
+
+ tool.palette = toolbutton->palette;
+
+ if (toolbutton->subControls & SC_ToolButton) {
+ if (bflags & (State_Sunken | State_On | State_Raised | State_MouseOver)) {
+ tool.rect = button;
+ tool.state = bflags;
+ drawPrimitive(PE_PanelButtonTool, &tool, painter, widget);
+ }
+ }
+
+ bool drawMenuArrow = toolbutton->features & QStyleOptionToolButton::HasMenu &&
+ !(toolbutton->features & QStyleOptionToolButton::MenuButtonPopup);
+ int popupArrowSize = drawMenuArrow ? 7 : 0;
+
+ if (toolbutton->state & State_HasFocus) {
+ QStyleOptionFocusRect fr;
+ fr.QStyleOption::operator=(*toolbutton);
+ fr.rect = subControlRect(CC_ToolButton, toolbutton, SC_ToolButton, widget);
+ fr.rect.adjust(1, 1, -1, -1);
+ drawPrimitive(PE_FrameFocusRect, &fr, painter, widget);
+ }
+
+ QStyleOptionToolButton label = *toolbutton;
+ label.state = bflags;
+ GtkWidget *gtkButton = QGtk::gtkWidget(QLS("GtkButton"));
+ QPalette pal = toolbutton->palette;
+ if (option->state & State_Enabled &&
+ option->state & State_MouseOver && !(widget && widget->testAttribute(Qt::WA_SetPalette))) {
+ GdkColor gdkText = gtkButton->style->fg[GTK_STATE_PRELIGHT];
+ QColor textColor = QColor(gdkText.red>>8, gdkText.green>>8, gdkText.blue>>8);
+ pal.setBrush(QPalette::All, QPalette::ButtonText, textColor);
+ label.palette = pal;
+ }
+ label.rect = button.adjusted(style->xthickness, style->ythickness,
+ -style->xthickness - popupArrowSize, -style->ythickness);
+ drawControl(CE_ToolButtonLabel, &label, painter, widget);
+
+ if (toolbutton->subControls & SC_ToolButtonMenu) {
+ tool.rect = menuarea;
+ tool.state = mflags;
+ if ((mflags & State_Enabled && (mflags & (State_Sunken | State_Raised | State_MouseOver))) || !(mflags & State_AutoRaise))
+ drawPrimitive(PE_IndicatorButtonDropDown, &tool, painter, widget);
+
+ drawPrimitive(PE_IndicatorArrowDown, &tool, painter, widget);
+
+ } else if (drawMenuArrow) {
+ QRect ir = toolbutton->rect;
+ QStyleOptionToolButton newBtn = *toolbutton;
+ newBtn.rect = QRect(ir.right() - popupArrowSize - style->xthickness - 3, ir.height()/2 - 1, popupArrowSize, popupArrowSize);
+ drawPrimitive(PE_IndicatorArrowDown, &newBtn, painter, widget);
+ }
+ }
+ break;
+
+#endif // QT_NO_TOOLBUTTON
+#ifndef QT_NO_SCROLLBAR
+
+ case CC_ScrollBar:
+ if (const QStyleOptionSlider *scrollBar = qstyleoption_cast<const QStyleOptionSlider *>(option)) {
+ GtkWidget *gtkHScrollBar = QGtk::gtkWidget(QLS("GtkHScrollbar"));
+ GtkWidget *gtkVScrollBar = QGtk::gtkWidget(QLS("GtkVScrollbar"));
+
+ // Fill background in case the scrollbar is partially transparent
+ painter->fillRect(option->rect, option->palette.background());
+
+ QRect rect = scrollBar->rect;
+ QRect scrollBarSubLine = subControlRect(control, scrollBar, SC_ScrollBarSubLine, widget);
+ QRect scrollBarAddLine = subControlRect(control, scrollBar, SC_ScrollBarAddLine, widget);
+ QRect scrollBarSlider = subControlRect(control, scrollBar, SC_ScrollBarSlider, widget);
+ QRect grooveRect = subControlRect(control, scrollBar, SC_ScrollBarGroove, widget);
+ bool horizontal = scrollBar->orientation == Qt::Horizontal;
+ GtkWidget * scrollbarWidget = horizontal ? gtkHScrollBar : gtkVScrollBar;
+ style = scrollbarWidget->style;
+ gboolean trough_under_steppers = true;
+ gboolean trough_side_details = false;
+ gboolean stepper_size = 14;
+ gint trough_border = 1;
+ if (!QGtk::gtk_check_version(2, 10, 0)) {
+ QGtk::gtk_widget_style_get((GtkWidget*)(scrollbarWidget),
+ "trough-border", &trough_border,
+ "trough-side-details", &trough_side_details,
+ "trough-under-steppers", &trough_under_steppers,
+ "stepper-size", &stepper_size, NULL);
+ }
+ if (trough_under_steppers) {
+ scrollBarAddLine.adjust(trough_border, trough_border, -trough_border, -trough_border);
+ scrollBarSubLine.adjust(trough_border, trough_border, -trough_border, -trough_border);
+ scrollBarSlider.adjust(horizontal ? -trough_border : 0, horizontal ? 0 : -trough_border,
+ horizontal ? trough_border : 0, horizontal ? 0 : trough_border);
+ }
+
+ // Some styles check the position of scrollbars in order to determine
+ // if lines should be painted when the scrollbar is in max or min positions.
+ int maximum = 2;
+ int fakePos = 0;
+ bool reverse = (option->direction == Qt::RightToLeft);
+ if (scrollBar->minimum == scrollBar->maximum)
+ maximum = 0;
+ if (scrollBar->sliderPosition == scrollBar->maximum)
+ fakePos = maximum;
+ else if (scrollBar->sliderPosition > scrollBar->minimum)
+ fakePos = maximum - 1;
+ GtkObject *adjustment = QGtk::gtk_adjustment_new(fakePos, 0, maximum, 0, 0, 0);
+
+ if (horizontal)
+ QGtk::gtk_range_set_adjustment((GtkRange*)(gtkHScrollBar), (GtkAdjustment*)(adjustment));
+ else
+ QGtk::gtk_range_set_adjustment((GtkRange*)(gtkVScrollBar), (GtkAdjustment*)(adjustment));
+
+ if (scrollBar->subControls & SC_ScrollBarGroove) {
+ GtkStateType state = GTK_STATE_ACTIVE;
+
+ if (!(option->state & State_Enabled))
+ state = GTK_STATE_INSENSITIVE;
+
+ if (trough_under_steppers)
+ grooveRect = option->rect;
+
+ gtkPainter.paintBox( scrollbarWidget, "trough", grooveRect, state, GTK_SHADOW_IN, style);
+ }
+
+ //paint slider
+ if (scrollBar->subControls & SC_ScrollBarSlider) {
+ GtkStateType state = GTK_STATE_NORMAL;
+
+ if (!(option->state & State_Enabled))
+ state = GTK_STATE_INSENSITIVE;
+ else if (option->state & State_MouseOver && (scrollBar->activeSubControls & SC_ScrollBarSlider))
+ state = GTK_STATE_PRELIGHT;
+
+ GtkShadowType shadow = GTK_SHADOW_OUT;
+
+ if (trough_under_steppers) {
+ if (!horizontal)
+ scrollBarSlider.adjust(trough_border, 0, -trough_border, 0);
+ else
+ scrollBarSlider.adjust(0, trough_border, 0, -trough_border);
+ }
+
+ gtkPainter.paintSlider( scrollbarWidget, "slider", scrollBarSlider, state, shadow, style,
+
+ horizontal ? GTK_ORIENTATION_HORIZONTAL : GTK_ORIENTATION_VERTICAL, QString(QLS("%0%1")).arg(fakePos).arg(maximum));
+ }
+
+ if (scrollBar->subControls & SC_ScrollBarAddLine) {
+ gtkVScrollBar->allocation.y = scrollBarAddLine.top();
+ gtkVScrollBar->allocation.height = scrollBarAddLine.height() - rect.height() + 6;
+ gtkHScrollBar->allocation.x = scrollBarAddLine.right();
+ gtkHScrollBar->allocation.width = scrollBarAddLine.width() - rect.width();
+
+ GtkShadowType shadow = GTK_SHADOW_OUT;
+ GtkStateType state = GTK_STATE_NORMAL;
+
+ if (!(option->state & State_Enabled) || (fakePos == maximum))
+ state = GTK_STATE_INSENSITIVE;
+ else if (option->state & State_Sunken && (scrollBar->activeSubControls & SC_ScrollBarAddLine)) {
+ state = GTK_STATE_ACTIVE;
+ shadow = GTK_SHADOW_IN;
+
+ } else if (option->state & State_MouseOver && (scrollBar->activeSubControls & SC_ScrollBarAddLine))
+ state = GTK_STATE_PRELIGHT;
+
+ gtkPainter.paintBox( scrollbarWidget,
+ horizontal ? "hscrollbar" : "vscrollbar", scrollBarAddLine,
+ state, shadow, style, QLS("add"));
+
+ gtkPainter.paintArrow( scrollbarWidget, horizontal ? "hscrollbar" : "vscrollbar", scrollBarAddLine.adjusted(4, 4, -4, -4),
+ horizontal ? (reverse ? GTK_ARROW_LEFT : GTK_ARROW_RIGHT) :
+ GTK_ARROW_DOWN, state, GTK_SHADOW_NONE, FALSE, style);
+ }
+
+ if (scrollBar->subControls & SC_ScrollBarSubLine) {
+ gtkVScrollBar->allocation.y = 0;
+ gtkVScrollBar->allocation.height = scrollBarSubLine.height();
+ gtkHScrollBar->allocation.x = 0;
+ gtkHScrollBar->allocation.width = scrollBarSubLine.width();
+
+ GtkShadowType shadow = GTK_SHADOW_OUT;
+ GtkStateType state = GTK_STATE_NORMAL;
+
+ if (!(option->state & State_Enabled) || (fakePos == 0))
+ state = GTK_STATE_INSENSITIVE;
+ else if (option->state & State_Sunken && (scrollBar->activeSubControls & SC_ScrollBarSubLine)) {
+ shadow = GTK_SHADOW_IN;
+ state = GTK_STATE_ACTIVE;
+
+ } else if (option->state & State_MouseOver && (scrollBar->activeSubControls & SC_ScrollBarSubLine))
+ state = GTK_STATE_PRELIGHT;
+
+ gtkPainter.paintBox(scrollbarWidget, horizontal ? "hscrollbar" : "vscrollbar", scrollBarSubLine,
+ state, shadow, style, QLS("sub"));
+
+ gtkPainter.paintArrow(scrollbarWidget, horizontal ? "hscrollbar" : "vscrollbar", scrollBarSubLine.adjusted(4, 4, -4, -4),
+ horizontal ? (reverse ? GTK_ARROW_RIGHT : GTK_ARROW_LEFT) :
+ GTK_ARROW_UP, state, GTK_SHADOW_NONE, FALSE, style);
+ }
+ }
+ break;
+
+#endif //QT_NO_SCROLLBAR
+#ifndef QT_NO_SPINBOX
+
+ case CC_SpinBox:
+ if (const QStyleOptionSpinBox *spinBox = qstyleoption_cast<const QStyleOptionSpinBox *>(option)) {
+ GtkWidget *gtkSpinButton = QGtk::gtkWidget(QLS("GtkSpinButton"));
+ bool isEnabled = (spinBox->state & State_Enabled);
+ bool hover = isEnabled && (spinBox->state & State_MouseOver);
+ bool sunken = (spinBox->state & State_Sunken);
+ bool upIsActive = (spinBox->activeSubControls == SC_SpinBoxUp);
+ bool downIsActive = (spinBox->activeSubControls == SC_SpinBoxDown);
+ bool reverse = (spinBox->direction == Qt::RightToLeft);
+
+ //### Move this to subControlRect
+ QRect upRect = subControlRect(CC_SpinBox, option, SC_SpinBoxUp, widget);
+ upRect.setTop(option->rect.top());
+
+ if (reverse)
+ upRect.setLeft(option->rect.left());
+ else
+ upRect.setRight(option->rect.right());
+
+ QRect editRect = subControlRect(CC_SpinBox, option, SC_SpinBoxEditField, widget);
+ QRect downRect = subControlRect(CC_SpinBox, option, SC_SpinBoxDown, widget);
+ downRect.setBottom(option->rect.bottom());
+
+ if (reverse)
+ downRect.setLeft(option->rect.left());
+ else
+ downRect.setRight(option->rect.right());
+
+ QRect buttonRect = upRect | downRect;
+ QRect editArea = option->rect;
+
+ if (reverse)
+ editArea.setLeft(upRect.right());
+ else
+ editArea.setRight(upRect.left());
+
+ if (spinBox->frame) {
+ GtkShadowType shadow = GTK_SHADOW_OUT;
+ GtkStateType state = gtkPainter.gtkState(option);
+
+ if (!(option->state & State_Enabled))
+ state = GTK_STATE_INSENSITIVE;
+ else if (option->state & State_HasFocus)
+ state = GTK_STATE_NORMAL;
+ else if (state == GTK_STATE_PRELIGHT)
+ state = GTK_STATE_NORMAL;
+
+ shadow = GTK_SHADOW_IN;
+ style = gtkPainter.getStyle(gtkSpinButton);
+
+ if (option->state & State_HasFocus)
+ GTK_WIDGET_SET_FLAGS(gtkSpinButton, GTK_HAS_FOCUS);
+ else
+ GTK_WIDGET_UNSET_FLAGS(gtkSpinButton, GTK_HAS_FOCUS);
+
+ QString key;
+
+ if (option->state & State_HasFocus)
+ key = QLS("f");
+
+ uint resolve_mask = option->palette.resolve();
+
+ if (resolve_mask & (1 << QPalette::Base)) // Palette overridden by user
+ painter->fillRect(editRect, option->palette.base().color());
+ else
+ gtkPainter.paintFlatBox(gtkSpinButton, "entry_bg", editArea.adjusted(style->xthickness, style->ythickness,
+ -style->xthickness, -style->ythickness),
+ option->state & State_Enabled ?
+ GTK_STATE_NORMAL : GTK_STATE_INSENSITIVE, GTK_SHADOW_NONE, style, key);
+
+ gtkPainter.paintShadow(gtkSpinButton, "entry", editArea, state, GTK_SHADOW_IN, gtkSpinButton->style, key);
+ gtkPainter.paintBox(gtkSpinButton, "spinbutton", buttonRect, state, GTK_SHADOW_IN, style, key);
+
+ upRect.setSize(downRect.size());
+ if (!(option->state & State_Enabled))
+ gtkPainter.paintBox( gtkSpinButton, "spinbutton_up", upRect, GTK_STATE_INSENSITIVE, GTK_SHADOW_IN, style, key);
+ else if (upIsActive && sunken)
+ gtkPainter.paintBox( gtkSpinButton, "spinbutton_up", upRect, GTK_STATE_ACTIVE, GTK_SHADOW_IN, style, key);
+ else if (upIsActive && hover)
+ gtkPainter.paintBox( gtkSpinButton, "spinbutton_up", upRect, GTK_STATE_PRELIGHT, GTK_SHADOW_OUT, style, key);
+ else
+ gtkPainter.paintBox( gtkSpinButton, "spinbutton_up", upRect, GTK_STATE_NORMAL, GTK_SHADOW_OUT, style, key);
+
+ if (!(option->state & State_Enabled))
+ gtkPainter.paintBox( gtkSpinButton, "spinbutton_down", downRect, GTK_STATE_INSENSITIVE, GTK_SHADOW_IN, style, key);
+ else if (downIsActive && sunken)
+ gtkPainter.paintBox( gtkSpinButton, "spinbutton_down", downRect, GTK_STATE_ACTIVE, GTK_SHADOW_IN, style, key);
+ else if (downIsActive && hover)
+ gtkPainter.paintBox( gtkSpinButton, "spinbutton_down", downRect, GTK_STATE_PRELIGHT, GTK_SHADOW_OUT, style, key);
+ else
+ gtkPainter.paintBox( gtkSpinButton, "spinbutton_down", downRect, GTK_STATE_NORMAL, GTK_SHADOW_OUT, style, key);
+ }
+
+ if (spinBox->buttonSymbols == QAbstractSpinBox::PlusMinus) {
+ int centerX = upRect.center().x();
+ int centerY = upRect.center().y();
+ // plus/minus
+
+ if (spinBox->activeSubControls == SC_SpinBoxUp && sunken) {
+ painter->drawLine(1 + centerX - 2, 1 + centerY, 1 + centerX + 2, 1 + centerY);
+ painter->drawLine(1 + centerX, 1 + centerY - 2, 1 + centerX, 1 + centerY + 2);
+
+ } else {
+ painter->drawLine(centerX - 2, centerY, centerX + 2, centerY);
+ painter->drawLine(centerX, centerY - 2, centerX, centerY + 2);
+ }
+ centerX = downRect.center().x();
+ centerY = downRect.center().y();
+
+ if (spinBox->activeSubControls == SC_SpinBoxDown && sunken) {
+ painter->drawLine(1 + centerX - 2, 1 + centerY, 1 + centerX + 2, 1 + centerY);
+ } else {
+ painter->drawLine(centerX - 2, centerY, centerX + 2, centerY);
+ }
+
+ } else {
+ int size = spinboxArrowSize();
+ int w = size / 2 - 1;
+ w -= w % 2 - 1; // force odd
+ int h = (w + 1)/2;
+ QRect arrowRect(0, 0, w, h);
+ arrowRect.moveCenter(upRect.center());
+ // arrows
+ GtkStateType state = GTK_STATE_NORMAL;
+
+ if (!(option->state & State_Enabled) || !(spinBox->stepEnabled & QAbstractSpinBox::StepUpEnabled))
+ state = GTK_STATE_INSENSITIVE;
+
+ gtkPainter.paintArrow( gtkSpinButton, "spinbutton", arrowRect, GTK_ARROW_UP, state,
+ GTK_SHADOW_NONE, FALSE, style);
+
+ arrowRect.moveCenter(downRect.center());
+
+ if (!(option->state & State_Enabled) || !(spinBox->stepEnabled & QAbstractSpinBox::StepDownEnabled))
+ state = GTK_STATE_INSENSITIVE;
+
+ gtkPainter.paintArrow( gtkSpinButton, "spinbutton", arrowRect, GTK_ARROW_DOWN, state,
+ GTK_SHADOW_NONE, FALSE, style);
+ }
+ }
+ break;
+
+#endif // QT_NO_SPINBOX
+
+#ifndef QT_NO_SLIDER
+
+ case CC_Slider:
+ if (const QStyleOptionSlider *slider = qstyleoption_cast<const QStyleOptionSlider *>(option)) {
+ GtkWidget *hScaleWidget = QGtk::gtkWidget(QLS("GtkHScale"));
+ GtkWidget *vScaleWidget = QGtk::gtkWidget(QLS("GtkVScale"));
+
+ QRect groove = subControlRect(CC_Slider, option, SC_SliderGroove, widget);
+ QRect handle = subControlRect(CC_Slider, option, SC_SliderHandle, widget);
+ QRect ticks = subControlRect(CC_Slider, option, SC_SliderTickmarks, widget);
+
+ bool horizontal = slider->orientation == Qt::Horizontal;
+ bool ticksAbove = slider->tickPosition & QSlider::TicksAbove;
+ bool ticksBelow = slider->tickPosition & QSlider::TicksBelow;
+ QColor activeHighlight = option->palette.color(QPalette::Normal, QPalette::Highlight);
+
+ QPixmap cache;
+ QBrush oldBrush = painter->brush();
+ QPen oldPen = painter->pen();
+
+ QColor shadowAlpha(Qt::black);
+ shadowAlpha.setAlpha(10);
+ QColor highlightAlpha(Qt::white);
+ highlightAlpha.setAlpha(80);
+
+ GtkWidget *scaleWidget = horizontal ? hScaleWidget : vScaleWidget;
+ style = scaleWidget->style;
+
+ if ((option->subControls & SC_SliderGroove) && groove.isValid()) {
+ GtkObject *adjustment = QGtk::gtk_adjustment_new(slider->sliderPosition,
+ slider->minimum,
+ slider->maximum,
+ slider->singleStep,
+ slider->singleStep,
+ slider->pageStep);
+ int outerSize;
+ QGtk::gtk_range_set_adjustment ((GtkRange*)(scaleWidget), (GtkAdjustment*)(adjustment));
+ QGtk::gtk_range_set_inverted((GtkRange*)(scaleWidget), !horizontal);
+ QGtk::gtk_widget_style_get(scaleWidget, "trough-border", &outerSize, NULL);
+ outerSize++;
+
+ GtkStateType state = gtkPainter.gtkState(option);
+ int focusFrameMargin = 2;
+ QRect grooveRect = option->rect.adjusted(focusFrameMargin, outerSize + focusFrameMargin,
+ -focusFrameMargin, -outerSize - focusFrameMargin);
+
+ gtkPainter.paintBox( scaleWidget, "trough", grooveRect, state,
+ GTK_SHADOW_IN, style, QString(QLS("p%0")).arg(slider->sliderPosition));
+
+ gboolean trough_side_details = false; // Indicates if the upper or lower scale background differs
+ if (!QGtk::gtk_check_version(2, 10, 0))
+ QGtk::gtk_widget_style_get((GtkWidget*)(scaleWidget), "trough-side-details", &trough_side_details, NULL);
+
+ if (trough_side_details && horizontal) { //### Vertical sliders look broken with this for some reason
+ QRect lowerGroove = grooveRect;
+ lowerGroove.setRight(handle.center().x());
+ gtkPainter.paintBox( scaleWidget, "trough-lower", lowerGroove, state,
+ GTK_SHADOW_IN, style, QString(QLS("p%0")).arg(slider->sliderPosition));
+ }
+
+ }
+
+ if (option->subControls & SC_SliderTickmarks) {
+ painter->setPen(darkOutline);
+ int tickSize = pixelMetric(PM_SliderTickmarkOffset, option, widget);
+ int available = pixelMetric(PM_SliderSpaceAvailable, slider, widget);
+ int interval = slider->tickInterval;
+
+ if (interval <= 0) {
+ interval = slider->singleStep;
+
+ if (QStyle::sliderPositionFromValue(slider->minimum, slider->maximum, interval,
+ available)
+ - QStyle::sliderPositionFromValue(slider->minimum, slider->maximum,
+ 0, available) < 3)
+ interval = slider->pageStep;
+ }
+
+ if (interval <= 0)
+ interval = 1;
+
+ int v = slider->minimum;
+ int len = pixelMetric(PM_SliderLength, slider, widget);
+ while (v <= slider->maximum + 1) {
+ if (v == slider->maximum + 1 && interval == 1)
+ break;
+ const int v_ = qMin(v, slider->maximum);
+ int pos = sliderPositionFromValue(slider->minimum, slider->maximum,
+ v_, (horizontal
+ ? slider->rect.width()
+ : slider->rect.height()) - len,
+ slider->upsideDown) + len / 2;
+ int extra = 2 - ((v_ == slider->minimum || v_ == slider->maximum) ? 1 : 0);
+ if (horizontal) {
+ if (ticksAbove)
+ painter->drawLine(pos, slider->rect.top() + extra,
+ pos, slider->rect.top() + tickSize);
+ if (ticksBelow)
+ painter->drawLine(pos, slider->rect.bottom() - extra,
+ pos, slider->rect.bottom() - tickSize);
+
+ } else {
+ if (ticksAbove)
+ painter->drawLine(slider->rect.left() + extra, pos,
+ slider->rect.left() + tickSize, pos);
+ if (ticksBelow)
+ painter->drawLine(slider->rect.right() - extra, pos,
+ slider->rect.right() - tickSize, pos);
+ }
+
+ // In the case where maximum is max int
+ int nextInterval = v + interval;
+ if (nextInterval < v)
+ break;
+ v = nextInterval;
+ }
+ }
+
+ // Draw slider handle
+ if (option->subControls & SC_SliderHandle) {
+ GtkShadowType shadow = GTK_SHADOW_OUT;
+ GtkStateType state = GTK_STATE_NORMAL;
+
+ if (!(option->state & State_Enabled))
+ state = GTK_STATE_INSENSITIVE;
+ else if (option->state & State_MouseOver && option->activeSubControls & SC_SliderHandle)
+ state = GTK_STATE_PRELIGHT;
+
+ bool horizontal = option->state & State_Horizontal;
+
+ if (slider->state & State_HasFocus) {
+ QStyleOptionFocusRect fropt;
+ fropt.QStyleOption::operator=(*slider);
+ fropt.rect = slider->rect.adjusted(-1, -1 ,1, 1);
+
+ if (horizontal) {
+ fropt.rect.setTop(handle.top() - 3);
+ fropt.rect.setBottom(handle.bottom() + 4);
+
+ } else {
+ fropt.rect.setLeft(handle.left() - 3);
+ fropt.rect.setRight(handle.right() + 3);
+ }
+ drawPrimitive(PE_FrameFocusRect, &fropt, painter, widget);
+ }
+ gtkPainter.paintSlider( scaleWidget, horizontal ? "hscale" : "vscale", handle, state, shadow, style,
+
+ horizontal ? GTK_ORIENTATION_HORIZONTAL : GTK_ORIENTATION_VERTICAL);
+ }
+ painter->setBrush(oldBrush);
+ painter->setPen(oldPen);
+ }
+ break;
+
+#endif // QT_NO_SLIDER
+
+ default:
+ QCleanlooksStyle::drawComplexControl(control, option, painter, widget);
+
+ break;
+ }
+}
+
+
+/*!
+ \reimp
+*/
+void QGtkStyle::drawControl(ControlElement element,
+ const QStyleOption *option,
+ QPainter *painter,
+ const QWidget *widget) const
+{
+ if (!QGtk::isThemeAvailable()) {
+ QCleanlooksStyle::drawControl(element, option, painter, widget);
+ return;
+ }
+
+ GtkStyle* style = QGtk::gtkStyle();
+ QGtkPainter gtkPainter(painter);
+
+ switch (element) {
+ case CE_ProgressBarLabel:
+ if (const QStyleOptionProgressBar *bar = qstyleoption_cast<const QStyleOptionProgressBar *>(option)) {
+ GtkWidget *gtkProgressBar = QGtk::gtkWidget(QLS("GtkProgressBar"));
+ if (!gtkProgressBar)
+ return;
+
+ QRect leftRect;
+ QRect rect = bar->rect;
+ GdkColor gdkText = gtkProgressBar->style->fg[GTK_STATE_NORMAL];
+ QColor textColor = QColor(gdkText.red>>8, gdkText.green>>8, gdkText.blue>>8);
+ gdkText = gtkProgressBar->style->fg[GTK_STATE_PRELIGHT];
+ QColor alternateTextColor= QColor(gdkText.red>>8, gdkText.green>>8, gdkText.blue>>8);
+
+ painter->save();
+ bool vertical = false, inverted = false;
+ if (const QStyleOptionProgressBarV2 *bar2 = qstyleoption_cast<const QStyleOptionProgressBarV2 *>(option)) {
+ vertical = (bar2->orientation == Qt::Vertical);
+ inverted = bar2->invertedAppearance;
+ }
+ if (vertical)
+ rect = QRect(rect.left(), rect.top(), rect.height(), rect.width()); // flip width and height
+ const int progressIndicatorPos = static_cast<int>((bar->progress - qint64(bar->minimum)) /
+ qMax(double(1.0), double(qint64(bar->maximum) - qint64(bar->minimum))) * rect.width());
+ if (progressIndicatorPos >= 0 && progressIndicatorPos <= rect.width())
+ leftRect = QRect(rect.left(), rect.top(), progressIndicatorPos, rect.height());
+ if (vertical)
+ leftRect.translate(rect.width() - progressIndicatorPos, 0);
+
+ bool flip = (!vertical && (((bar->direction == Qt::RightToLeft) && !inverted) ||
+ ((bar->direction == Qt::LeftToRight) && inverted)));
+
+ QRegion rightRect = rect;
+ rightRect = rightRect.subtracted(leftRect);
+ painter->setClipRegion(rightRect);
+ painter->setPen(flip ? alternateTextColor : textColor);
+ painter->drawText(rect, bar->text, QTextOption(Qt::AlignAbsolute | Qt::AlignHCenter | Qt::AlignVCenter));
+ if (!leftRect.isNull()) {
+ painter->setPen(flip ? textColor : alternateTextColor);
+ painter->setClipRect(leftRect);
+ painter->drawText(rect, bar->text, QTextOption(Qt::AlignAbsolute | Qt::AlignHCenter | Qt::AlignVCenter));
+ }
+ painter->restore();
+ }
+ break;
+ case CE_PushButtonLabel:
+ if (const QStyleOptionButton *button = qstyleoption_cast<const QStyleOptionButton *>(option)) {
+ QRect ir = button->rect;
+ uint tf = Qt::AlignVCenter | Qt::TextShowMnemonic;
+ QPoint buttonShift;
+
+ if (option->state & State_Sunken)
+ buttonShift = QPoint(pixelMetric(PM_ButtonShiftHorizontal, option, widget),
+ pixelMetric(PM_ButtonShiftVertical, option, widget));
+
+ if (styleHint(SH_UnderlineShortcut, button, widget))
+ tf |= Qt::TextShowMnemonic;
+ else
+ tf |= Qt::TextHideMnemonic;
+
+ if (!button->icon.isNull()) {
+ //Center both icon and text
+ QPoint point;
+
+ QIcon::Mode mode = button->state & State_Enabled ? QIcon::Normal : QIcon::Disabled;
+ if (mode == QIcon::Normal && button->state & State_HasFocus)
+ mode = QIcon::Active;
+
+ QIcon::State state = QIcon::Off;
+
+ if (button->state & State_On)
+ state = QIcon::On;
+
+ QPixmap pixmap = button->icon.pixmap(button->iconSize, mode, state);
+ int w = pixmap.width();
+ int h = pixmap.height();
+
+ if (!button->text.isEmpty())
+ w += button->fontMetrics.boundingRect(option->rect, tf, button->text).width() + 4;
+
+ point = QPoint(ir.x() + ir.width() / 2 - w / 2,
+ ir.y() + ir.height() / 2 - h / 2);
+
+ if (button->direction == Qt::RightToLeft)
+ point.rx() += pixmap.width();
+
+ painter->drawPixmap(visualPos(button->direction, button->rect, point + buttonShift), pixmap);
+
+ if (button->direction == Qt::RightToLeft)
+ ir.translate(-point.x() - 2, 0);
+ else
+ ir.translate(point.x() + pixmap.width() + 2, 0);
+
+ // left-align text if there is
+ if (!button->text.isEmpty())
+ tf |= Qt::AlignLeft;
+
+ } else {
+ tf |= Qt::AlignHCenter;
+ }
+
+ ir.translate(buttonShift);
+
+ if (button->features & QStyleOptionButton::HasMenu)
+ ir = ir.adjusted(0, 0, -pixelMetric(PM_MenuButtonIndicator, button, widget), 0);
+
+ GtkWidget *gtkButton = QGtk::gtkWidget(QLS("GtkButton"));
+ QPalette pal = button->palette;
+ int labelState = GTK_STATE_INSENSITIVE;
+ if (option->state & State_Enabled)
+ labelState = (option->state & State_MouseOver && !(option->state & State_Sunken)) ?
+ GTK_STATE_PRELIGHT : GTK_STATE_NORMAL;
+
+ GdkColor gdkText = gtkButton->style->fg[labelState];
+ QColor textColor = QColor(gdkText.red>>8, gdkText.green>>8, gdkText.blue>>8);
+ pal.setBrush(QPalette::ButtonText, textColor);
+ drawItemText(painter, ir, tf, pal, (button->state & State_Enabled),
+ button->text, QPalette::ButtonText);
+ }
+ break;
+
+ case CE_RadioButton: // Fall through
+ case CE_CheckBox:
+ if (const QStyleOptionButton *btn = qstyleoption_cast<const QStyleOptionButton *>(option)) {
+ bool isRadio = (element == CE_RadioButton);
+
+ // Draw prelight background
+ GtkWidget *gtkRadioButton = QGtk::gtkWidget(QLS("GtkRadioButton"));
+
+ if (option->state & State_MouseOver) {
+ gtkPainter.paintFlatBox(gtkRadioButton, "checkbutton", option->rect,
+ GTK_STATE_PRELIGHT, GTK_SHADOW_ETCHED_OUT, gtkRadioButton->style);
+ }
+
+ QStyleOptionButton subopt = *btn;
+ subopt.rect = subElementRect(isRadio ? SE_RadioButtonIndicator
+ : SE_CheckBoxIndicator, btn, widget);
+ drawPrimitive(isRadio ? PE_IndicatorRadioButton : PE_IndicatorCheckBox,
+ &subopt, painter, widget);
+ subopt.rect = subElementRect(isRadio ? SE_RadioButtonContents
+ : SE_CheckBoxContents, btn, widget);
+ // Get label text color
+ QPalette pal = subopt.palette;
+ int labelState = GTK_STATE_INSENSITIVE;
+ if (option->state & State_Enabled)
+ labelState = (option->state & State_MouseOver) ? GTK_STATE_PRELIGHT : GTK_STATE_NORMAL;
+
+ GdkColor gdkText = gtkRadioButton->style->fg[labelState];
+ QColor textColor = QColor(gdkText.red>>8, gdkText.green>>8, gdkText.blue>>8);
+ pal.setBrush(QPalette::WindowText, textColor);
+ subopt.palette = pal;
+ drawControl(isRadio ? CE_RadioButtonLabel : CE_CheckBoxLabel, &subopt, painter, widget);
+
+ if (btn->state & State_HasFocus) {
+ QStyleOptionFocusRect fropt;
+ fropt.QStyleOption::operator=(*btn);
+ fropt.rect = subElementRect(isRadio ? SE_RadioButtonFocusRect
+ : SE_CheckBoxFocusRect, btn, widget);
+ drawPrimitive(PE_FrameFocusRect, &fropt, painter, widget);
+ }
+ }
+ break;
+
+#ifndef QT_NO_COMBOBOX
+
+ case CE_ComboBoxLabel:
+ if (const QStyleOptionComboBox *cb = qstyleoption_cast<const QStyleOptionComboBox *>(option)) {
+ QRect editRect = subControlRect(CC_ComboBox, cb, SC_ComboBoxEditField, widget);
+ bool appearsAsList = !styleHint(QStyle::SH_ComboBox_Popup, cb, widget);
+ painter->save();
+ painter->setClipRect(editRect);
+
+ if (!cb->currentIcon.isNull()) {
+ QIcon::Mode mode = cb->state & State_Enabled ? QIcon::Normal
+ : QIcon::Disabled;
+ QPixmap pixmap = cb->currentIcon.pixmap(cb->iconSize, mode);
+ QRect iconRect(editRect);
+ iconRect.setWidth(cb->iconSize.width() + 4);
+
+ iconRect = alignedRect(cb->direction,
+ Qt::AlignLeft | Qt::AlignVCenter,
+ iconRect.size(), editRect);
+
+ if (cb->editable)
+ painter->fillRect(iconRect, option->palette.brush(QPalette::Base));
+
+ drawItemPixmap(painter, iconRect, Qt::AlignCenter, pixmap);
+
+ if (cb->direction == Qt::RightToLeft)
+ editRect.translate(-4 - cb->iconSize.width(), 0);
+ else
+ editRect.translate(cb->iconSize.width() + 4, 0);
+ }
+
+ if (!cb->currentText.isEmpty() && !cb->editable) {
+ GtkWidget *gtkCombo = QGtk::gtkWidget(QLS("GtkComboBox"));
+ QPalette pal = cb->palette;
+ int labelState = GTK_STATE_INSENSITIVE;
+
+ if (option->state & State_Enabled)
+ labelState = (option->state & State_MouseOver && !appearsAsList) ? GTK_STATE_PRELIGHT : GTK_STATE_NORMAL;
+
+ GdkColor gdkText = gtkCombo->style->fg[labelState];
+
+ QColor textColor = QColor(gdkText.red>>8, gdkText.green>>8, gdkText.blue>>8);
+
+ pal.setBrush(QPalette::ButtonText, textColor);
+
+ drawItemText(painter, editRect.adjusted(1, 0, -1, 0),
+ visualAlignment(cb->direction, Qt::AlignLeft | Qt::AlignVCenter),
+ pal, cb->state & State_Enabled, cb->currentText, QPalette::ButtonText);
+ }
+
+ painter->restore();
+ }
+ break;
+
+#endif // QT_NO_COMBOBOX
+
+ case CE_DockWidgetTitle:
+ painter->save();
+ if (const QStyleOptionDockWidget *dwOpt = qstyleoption_cast<const QStyleOptionDockWidget *>(option)) {
+ const QStyleOptionDockWidgetV2 *v2
+ = qstyleoption_cast<const QStyleOptionDockWidgetV2*>(dwOpt);
+ bool verticalTitleBar = v2 == 0 ? false : v2->verticalTitleBar;
+
+ QRect rect = dwOpt->rect;
+ QRect titleRect = subElementRect(SE_DockWidgetTitleBarText, option, widget).adjusted(-2, 0, -2, 0);
+ QRect r = rect.adjusted(0, 0, -1, -1);
+ if (verticalTitleBar)
+ r.adjust(0, 0, 0, -1);
+
+ if (verticalTitleBar) {
+ QRect r = rect;
+ QSize s = r.size();
+ s.transpose();
+ r.setSize(s);
+
+ titleRect = QRect(r.left() + rect.bottom()
+ - titleRect.bottom(),
+ r.top() + titleRect.left() - rect.left(),
+ titleRect.height(), titleRect.width());
+
+ painter->translate(r.left(), r.top() + r.width());
+ painter->rotate(-90);
+ painter->translate(-r.left(), -r.top());
+
+ rect = r;
+ }
+
+ if (!dwOpt->title.isEmpty()) {
+ QString titleText
+ = painter->fontMetrics().elidedText(dwOpt->title,
+ Qt::ElideRight, titleRect.width());
+ drawItemText(painter,
+ titleRect,
+ Qt::AlignLeft | Qt::AlignVCenter | Qt::TextShowMnemonic, dwOpt->palette,
+ dwOpt->state & State_Enabled, titleText,
+ QPalette::WindowText);
+ }
+ }
+ painter->restore();
+ break;
+
+
+
+ case CE_HeaderSection:
+ painter->save();
+
+ // Draws the header in tables.
+ if (const QStyleOptionHeader *header = qstyleoption_cast<const QStyleOptionHeader *>(option)) {
+ Q_UNUSED(header);
+ GtkWidget *gtkTreeView = QGtk::gtkWidget(QLS("GtkTreeView"));
+ // Get the middle column
+ GtkTreeViewColumn *column = QGtk::gtk_tree_view_get_column((GtkTreeView*)gtkTreeView, 1);
+ Q_ASSERT(column);
+
+ GtkWidget *gtkTreeHeader = column->button;
+ GtkStateType state = gtkPainter.gtkState(option);
+ GtkShadowType shadow = GTK_SHADOW_OUT;
+
+ if (option->state & State_Sunken)
+ shadow = GTK_SHADOW_IN;
+
+ gtkPainter.paintBox(gtkTreeHeader, "button", option->rect.adjusted(-1, 0, 0, 0), state, shadow, gtkTreeHeader->style);
+ }
+
+ painter->restore();
+ break;
+
+#ifndef QT_NO_SIZEGRIP
+
+ case CE_SizeGrip: {
+ GtkWidget *gtkStatusbar = QGtk::gtkWidget(QLS("GtkStatusbar.GtkFrame"));
+ QRect gripRect = option->rect.adjusted(0, 0, -gtkStatusbar->style->xthickness, -gtkStatusbar->style->ythickness);
+ gtkPainter.paintResizeGrip( gtkStatusbar, "window", gripRect, GTK_STATE_NORMAL,
+ GTK_SHADOW_OUT, QApplication::isRightToLeft() ?
+ GDK_WINDOW_EDGE_SOUTH_WEST : GDK_WINDOW_EDGE_SOUTH_EAST,
+ gtkStatusbar->style);
+ }
+ break;
+
+#endif // QT_NO_SIZEGRIP
+
+ case CE_MenuBarEmptyArea: {
+ GtkWidget *gtkMenubar = QGtk::gtkWidget(QLS("GtkMenuBar"));
+ GdkColor gdkBg = gtkMenubar->style->bg[GTK_STATE_NORMAL]; // Theme can depend on transparency
+ painter->fillRect(option->rect, QColor(gdkBg.red>>8, gdkBg.green>>8, gdkBg.blue>>8));
+ if (widget) { // See CE_MenuBarItem
+ QRect menuBarRect = widget->rect();
+ QPixmap pixmap(menuBarRect.size());
+ pixmap.fill(Qt::transparent);
+ QPainter pmPainter(&pixmap);
+ QGtkPainter gtkMenuBarPainter(&pmPainter);
+ GtkShadowType shadow_type;
+ QGtk::gtk_widget_style_get(gtkMenubar, "shadow-type", &shadow_type, NULL);
+ gtkMenuBarPainter.paintBox( gtkMenubar, "menubar", menuBarRect,
+ GTK_STATE_NORMAL, shadow_type, gtkMenubar->style);
+ pmPainter.end();
+ painter->drawPixmap(option->rect, pixmap, option->rect);
+ }
+ }
+ break;
+
+ case CE_MenuBarItem:
+ painter->save();
+
+ if (const QStyleOptionMenuItem *mbi = qstyleoption_cast<const QStyleOptionMenuItem *>(option)) {
+ GtkWidget *gtkMenubarItem = QGtk::gtkWidget(QLS("GtkMenuBar.GtkMenuItem"));
+ GtkWidget *gtkMenubar = QGtk::gtkWidget(QLS("GtkMenuBar"));
+
+ style = gtkMenubarItem->style;
+
+ if (widget) {
+ // Since Qt does not currently allow filling the entire background
+ // we use a hack for this by making a complete menubar each time and
+ // paint with the correct offset inside it. Pixmap caching should resolve
+ // most of the performance penalty.
+ QRect menuBarRect = widget->rect();
+ QPixmap pixmap(menuBarRect.size());
+ pixmap.fill(Qt::transparent);
+ QPainter pmPainter(&pixmap);
+ QGtkPainter menubarPainter(&pmPainter);
+ GtkShadowType shadow_type;
+ QGtk::gtk_widget_style_get(gtkMenubar, "shadow-type", &shadow_type, NULL);
+ GdkColor gdkBg = gtkMenubar->style->bg[GTK_STATE_NORMAL]; // Theme can depend on transparency
+ painter->fillRect(option->rect, QColor(gdkBg.red>>8, gdkBg.green>>8, gdkBg.blue>>8));
+ menubarPainter.paintBox(gtkMenubar, "menubar", menuBarRect,
+ GTK_STATE_NORMAL, shadow_type, gtkMenubar->style);
+ pmPainter.end();
+ painter->drawPixmap(option->rect, pixmap, option->rect);
+ }
+
+ QStyleOptionMenuItem item = *mbi;
+ bool act = mbi->state & State_Selected && mbi->state & State_Sunken;
+ bool dis = !(mbi->state & State_Enabled);
+ item.rect = mbi->rect;
+ GdkColor gdkText = gtkMenubarItem->style->fg[dis ? GTK_STATE_INSENSITIVE : GTK_STATE_NORMAL];
+ GdkColor gdkHText = gtkMenubarItem->style->fg[GTK_STATE_PRELIGHT];
+ QColor normalTextColor = QColor(gdkText.red>>8, gdkText.green>>8, gdkText.blue>>8);
+ QColor highlightedTextColor = QColor(gdkHText.red>>8, gdkHText.green>>8, gdkHText.blue>>8);
+ item.palette.setBrush(QPalette::HighlightedText, highlightedTextColor);
+ item.palette.setBrush(QPalette::Text, normalTextColor);
+ item.palette.setBrush(QPalette::ButtonText, normalTextColor);
+ QCommonStyle::drawControl(element, &item, painter, widget);
+
+ if (act) {
+ GtkShadowType shadowType = GTK_SHADOW_NONE;
+ QGtk::gtk_widget_style_get (gtkMenubarItem, "selected-shadow-type", &shadowType, NULL);
+ gtkPainter.paintBox(gtkMenubarItem, "menuitem", option->rect.adjusted(0, 0, 0, 3),
+ GTK_STATE_PRELIGHT, shadowType, gtkMenubarItem->style);
+ //draw text
+ QPalette::ColorRole textRole = dis ? QPalette::Text : QPalette::HighlightedText;
+ uint alignment = Qt::AlignCenter | Qt::TextShowMnemonic | Qt::TextDontClip | Qt::TextSingleLine;
+
+ if (!QCleanlooksStyle::styleHint(SH_UnderlineShortcut, mbi, widget))
+ alignment |= Qt::TextHideMnemonic;
+
+ drawItemText(painter, item.rect, alignment, item.palette, mbi->state & State_Enabled, mbi->text, textRole);
+ }
+ }
+ painter->restore();
+ break;
+
+ case CE_Splitter: {
+ GtkWidget *gtkWindow = QGtk::gtkWidget(QLS("GtkWindow")); // The Murrine Engine currently assumes a widget is passed
+ gtkPainter.paintHandle(gtkWindow, "splitter", option->rect, gtkPainter.gtkState(option), GTK_SHADOW_NONE,
+ !(option->state & State_Horizontal) ? GTK_ORIENTATION_HORIZONTAL : GTK_ORIENTATION_VERTICAL,
+ style);
+ }
+ break;
+
+#ifndef QT_NO_TOOLBAR
+
+ case CE_ToolBar:
+ if (const QStyleOptionToolBar *toolbar = qstyleoption_cast<const QStyleOptionToolBar *>(option)) {
+ // Reserve the beveled appearance only for mainwindow toolbars
+ if (!(widget && qobject_cast<const QMainWindow*> (widget->parentWidget())))
+ break;
+
+ QRect rect = option->rect;
+ // There is a 1 pixel gap between toolbar lines in some styles (i.e Human)
+ if (toolbar->positionWithinLine != QStyleOptionToolBar::End)
+ rect.adjust(0, 0, 1, 0);
+
+ GtkWidget *gtkToolbar = QGtk::gtkWidget(QLS("GtkToolbar"));
+ GtkShadowType shadow_type = GTK_SHADOW_NONE;
+ QGtk::gtk_widget_style_get(gtkToolbar, "shadow-type", &shadow_type, NULL);
+ gtkPainter.paintBox( gtkToolbar, "toolbar", rect,
+ GTK_STATE_NORMAL, shadow_type, gtkToolbar->style);
+ }
+ break;
+
+#endif // QT_NO_TOOLBAR
+
+ case CE_MenuItem:
+ painter->save();
+
+ // Draws one item in a popup menu.
+ if (const QStyleOptionMenuItem *menuItem = qstyleoption_cast<const QStyleOptionMenuItem *>(option)) {
+ const int windowsItemFrame = 2; // menu item frame width
+ const int windowsItemHMargin = 3; // menu item hor text margin
+ const int windowsItemVMargin = 26; // menu item ver text margin
+ const int windowsRightBorder = 15; // right border on windows
+ GtkWidget *gtkMenu = QGtk::gtkWidget(QLS("GtkMenu"));
+ GtkWidget *gtkMenuItem = menuItem->checked ? QGtk::gtkWidget(QLS("GtkMenu.GtkCheckMenuItem")) :
+ QGtk::gtkWidget(QLS("GtkMenu.GtkMenuItem"));
+
+ style = gtkPainter.getStyle(gtkMenuItem);
+ QColor borderColor = option->palette.background().color().darker(160);
+ QColor shadow = option->palette.dark().color();
+
+ if (menuItem->menuItemType == QStyleOptionMenuItem::Separator) {
+ GtkWidget *gtkMenuSeparator = QGtk::gtkWidget(QLS("GtkMenu.GtkSeparatorMenuItem"));
+ painter->setPen(shadow.lighter(106));
+ gboolean wide_separators = 0;
+ gint separator_height = 0;
+ guint horizontal_padding = 3;
+ if (!QGtk::gtk_check_version(2, 10, 0)) {
+ QGtk::gtk_widget_style_get(gtkMenuSeparator,
+ "wide-separators", &wide_separators,
+ "separator-height", &separator_height,
+ "horizontal-padding", &horizontal_padding,
+ NULL);
+ }
+ if (wide_separators)
+ gtkPainter.paintBox( gtkMenuSeparator, "hseparator",
+ option->rect.adjusted(0, 0, 0, -1), GTK_STATE_NORMAL, GTK_SHADOW_NONE, gtkMenu->style);
+ else
+ gtkPainter.paintHline( gtkMenuSeparator, "hseparator",
+ menuItem->rect, GTK_STATE_NORMAL, gtkMenu->style,
+ option->rect.left() + horizontal_padding, option->rect.width() - 2*horizontal_padding, 2);
+ painter->restore();
+ break;
+ }
+
+ bool selected = menuItem->state & State_Selected && menuItem->state & State_Enabled;
+
+ if (selected) {
+ QRect rect = option->rect.adjusted(0, 0, -1, -1);
+ gtkPainter.paintBox( gtkMenuItem, "menuitem", rect, GTK_STATE_PRELIGHT, GTK_SHADOW_OUT, style);
+ }
+
+ bool checkable = menuItem->checkType != QStyleOptionMenuItem::NotCheckable;
+ bool checked = menuItem->checked;
+ bool enabled = menuItem->state & State_Enabled;
+ bool ignoreCheckMark = false;
+
+ gint checkSize;
+ QGtk::gtk_widget_style_get(QGtk::gtkWidget(QLS("GtkMenu.GtkCheckMenuItem")), "indicator-size", &checkSize, NULL);
+
+ int checkcol = qMax(menuItem->maxIconWidth, qMax(20, checkSize));
+
+#ifndef QT_NO_COMBOBOX
+
+ if (qobject_cast<const QComboBox*>(widget))
+ ignoreCheckMark = true; // Ignore the checkmarks provided by the QComboMenuDelegate
+
+#endif
+ if (!ignoreCheckMark) {
+ // Check
+ QRect checkRect(option->rect.left() + 7, option->rect.center().y() - checkSize/2, checkSize, checkSize);
+ checkRect = visualRect(menuItem->direction, menuItem->rect, checkRect);
+
+ if (checkable && menuItem->icon.isNull()) {
+ // Some themes such as aero-clone draw slightly outside the paint rect
+ int spacing = 1; // ### Consider using gtkCheckBox : "indicator-spacing" instead
+
+ if (menuItem->checkType & QStyleOptionMenuItem::Exclusive) {
+ // Radio button
+ GtkShadowType shadow = GTK_SHADOW_OUT;
+ GtkStateType state = gtkPainter.gtkState(option);
+
+ if (selected)
+ state = GTK_STATE_PRELIGHT;
+ if (checked)
+ shadow = GTK_SHADOW_IN;
+
+ gtkPainter.setClipRect(checkRect.adjusted(-spacing, -spacing, spacing, spacing));
+ gtkPainter.paintOption(gtkMenuItem, checkRect.translated(-spacing, -spacing), state, shadow,
+ gtkMenuItem->style, QLS("option"));
+ gtkPainter.setClipRect(QRect());
+
+ } else {
+ // Check box
+ if (menuItem->icon.isNull()) {
+ GtkShadowType shadow = GTK_SHADOW_OUT;
+ GtkStateType state = gtkPainter.gtkState(option);
+
+ if (selected)
+ state = GTK_STATE_PRELIGHT;
+ if (checked)
+ shadow = GTK_SHADOW_IN;
+
+ gtkPainter.setClipRect(checkRect.adjusted(-spacing, -spacing, -spacing, -spacing));
+ gtkPainter.paintCheckbox(gtkMenuItem, checkRect.translated(-spacing, -spacing), state, shadow,
+ gtkMenuItem->style, QLS("check"));
+ gtkPainter.setClipRect(QRect());
+ }
+ }
+ }
+
+ } else {
+ // Ignore checkmark
+ if (menuItem->icon.isNull())
+ checkcol = 0;
+ else
+ checkcol = menuItem->maxIconWidth;
+ }
+
+ bool dis = !(menuItem->state & State_Enabled);
+ bool act = menuItem->state & State_Selected;
+ const QStyleOption *opt = option;
+ const QStyleOptionMenuItem *menuitem = menuItem;
+ QPainter *p = painter;
+ QRect vCheckRect = visualRect(opt->direction, menuitem->rect,
+ QRect(menuitem->rect.x() + 3, menuitem->rect.y(),
+ checkcol, menuitem->rect.height()));
+
+ if (!menuItem->icon.isNull()) {
+ QIcon::Mode mode = dis ? QIcon::Disabled : QIcon::Normal;
+
+ if (act && !dis)
+ mode = QIcon::Active;
+
+ QPixmap pixmap;
+ int smallIconSize = pixelMetric(PM_SmallIconSize, option, widget);
+ QSize iconSize(smallIconSize, smallIconSize);
+
+#ifndef QT_NO_COMBOBOX
+ if (const QComboBox *combo = qobject_cast<const QComboBox*>(widget))
+ iconSize = combo->iconSize();
+
+#endif // QT_NO_COMBOBOX
+ if (checked)
+ pixmap = menuItem->icon.pixmap(iconSize, mode, QIcon::On);
+ else
+ pixmap = menuItem->icon.pixmap(iconSize, mode);
+
+ int pixw = pixmap.width();
+ int pixh = pixmap.height();
+ QRect pmr(0, 0, pixw, pixh);
+ pmr.moveCenter(vCheckRect.center());
+ painter->setPen(menuItem->palette.text().color());
+ if (!ignoreCheckMark && checkable && checked) {
+ QStyleOption opt = *option;
+
+ if (act) {
+ QColor activeColor = mergedColors(option->palette.background().color(),
+ option->palette.highlight().color());
+ opt.palette.setBrush(QPalette::Button, activeColor);
+ }
+ opt.state |= State_Sunken;
+ opt.rect = vCheckRect;
+ drawPrimitive(PE_PanelButtonCommand, &opt, painter, widget);
+ }
+
+ painter->drawPixmap(pmr.topLeft(), pixmap);
+ }
+
+ GdkColor gdkText = gtkMenuItem->style->fg[GTK_STATE_NORMAL];
+ GdkColor gdkDText = gtkMenuItem->style->fg[GTK_STATE_INSENSITIVE];
+ GdkColor gdkHText = gtkMenuItem->style->fg[GTK_STATE_PRELIGHT];
+ QColor textColor = QColor(gdkText.red>>8, gdkText.green>>8, gdkText.blue>>8);
+ QColor disabledTextColor = QColor(gdkDText.red>>8, gdkDText.green>>8, gdkDText.blue>>8);
+ QColor highlightedTextColor = QColor(gdkHText.red>>8, gdkHText.green>>8, gdkHText.blue>>8);
+
+ if (selected)
+ painter->setPen(highlightedTextColor);
+ else
+ painter->setPen(textColor);
+
+ int x, y, w, h;
+ menuitem->rect.getRect(&x, &y, &w, &h);
+ int tab = menuitem->tabWidth;
+ int xm = windowsItemFrame + checkcol + windowsItemHMargin;
+ int xpos = menuitem->rect.x() + xm;
+ QRect textRect(xpos, y + windowsItemVMargin, w - xm - windowsRightBorder - tab + 1, h - 2 * windowsItemVMargin);
+ QRect vTextRect = visualRect(opt->direction, menuitem->rect, textRect);
+ QString s = menuitem->text;
+
+ if (!s.isEmpty()) { // Draw text
+ p->save();
+ int t = s.indexOf(QLatin1Char('\t'));
+ int text_flags = Qt::AlignVCenter | Qt::TextShowMnemonic | Qt::TextDontClip | Qt::TextSingleLine;
+
+ if (!styleHint(SH_UnderlineShortcut, menuitem, widget))
+ text_flags |= Qt::TextHideMnemonic;
+
+ // Draw shortcut right aligned
+ text_flags |= Qt::AlignRight;
+
+ if (t >= 0) {
+ int rightMargin = 12; // Hardcode for now
+ QRect vShortcutRect = visualRect(opt->direction, menuitem->rect,
+ QRect(textRect.topRight(), QPoint(menuitem->rect.right() - rightMargin, textRect.bottom())));
+
+ if (dis)
+ p->setPen(disabledTextColor);
+ p->drawText(vShortcutRect, text_flags , s.mid(t + 1));
+ s = s.left(t);
+ }
+
+ text_flags &= ~Qt::AlignRight;
+ text_flags |= Qt::AlignLeft;
+ QFont font = menuitem->font;
+ if (menuitem->menuItemType == QStyleOptionMenuItem::DefaultItem)
+ font.setBold(true);
+ p->setFont(font);
+
+ if (dis)
+ p->setPen(disabledTextColor);
+ p->drawText(vTextRect, text_flags, s.left(t));
+ p->restore();
+ }
+
+ // Arrow
+ if (menuItem->menuItemType == QStyleOptionMenuItem::SubMenu) {// draw sub menu arrow
+ QPoint buttonShift(pixelMetric(PM_ButtonShiftHorizontal, option, widget),
+ pixelMetric(PM_ButtonShiftVertical, option, widget));
+
+ QFontMetrics fm(menuitem->font);
+ int arrow_size = fm.ascent() + fm.descent() - 2 * gtkMenuItem->style->ythickness;
+ gfloat arrow_scaling = 0.8;
+
+ // "arrow-scaling" is actually hardcoded and fails on hardy (see gtk+-2.12/gtkmenuitem.c)
+ // though the current documentation states otherwise
+ int horizontal_padding;
+ QGtk::gtk_widget_style_get(gtkMenuItem, "horizontal-padding", &horizontal_padding, NULL);
+
+ const int dim = static_cast<int>(arrow_size * arrow_scaling);
+ int xpos = menuItem->rect.left() + menuItem->rect.width() - horizontal_padding - dim;
+ QRect vSubMenuRect = visualRect(option->direction, menuItem->rect,
+ QRect(xpos, menuItem->rect.top() +
+ menuItem->rect.height() / 2 - dim / 2, dim, dim));
+ GtkStateType state = enabled ? (act ? GTK_STATE_PRELIGHT: GTK_STATE_NORMAL) : GTK_STATE_INSENSITIVE;
+ GtkShadowType shadowType = (state == GTK_STATE_PRELIGHT) ? GTK_SHADOW_OUT : GTK_SHADOW_IN;
+ gtkPainter.paintArrow(gtkMenuItem, "menuitem", vSubMenuRect, QApplication::isRightToLeft() ? GTK_ARROW_LEFT : GTK_ARROW_RIGHT, state,
+ shadowType, FALSE, style);
+ }
+ }
+ painter->restore();
+ break;
+
+ case CE_PushButton:
+ if (const QStyleOptionButton *btn = qstyleoption_cast<const QStyleOptionButton *>(option)) {
+ GtkWidget *gtkButton = QGtk::gtkWidget(QLS("GtkButton"));
+ drawControl(CE_PushButtonBevel, btn, painter, widget);
+ QStyleOptionButton subopt = *btn;
+ subopt.rect = subElementRect(SE_PushButtonContents, btn, widget);
+ gint interiorFocus = true;
+ QGtk::gtk_widget_style_get(gtkButton, "interior-focus", &interiorFocus, NULL);
+ int xt = interiorFocus ? gtkButton->style->xthickness : 0;
+ int yt = interiorFocus ? gtkButton->style->ythickness : 0;
+
+ if (btn->features & QStyleOptionButton::Flat && btn->state & State_HasFocus)
+ // The normal button focus rect does not work well for flat buttons in Clearlooks
+ drawPrimitive(PE_FrameFocusRect, option, painter, widget);
+ else if (btn->state & State_HasFocus)
+ gtkPainter.paintFocus(gtkButton, "button",
+ option->rect.adjusted(xt, yt, -xt, -yt),
+ btn->state & State_Sunken ? GTK_STATE_ACTIVE : GTK_STATE_NORMAL,
+ gtkButton->style);
+
+ drawControl(CE_PushButtonLabel, &subopt, painter, widget);
+ }
+ break;
+
+#ifndef QT_NO_TABBAR
+
+ case CE_TabBarTabShape:
+ if (const QStyleOptionTab *tab = qstyleoption_cast<const QStyleOptionTab *>(option)) {
+ GtkWidget *gtkNotebook = QGtk::gtkWidget(QLS("GtkNotebook"));
+ style = gtkPainter.getStyle(gtkNotebook);
+
+ QRect rect = option->rect;
+ GtkShadowType shadow = GTK_SHADOW_OUT;
+ GtkStateType state = GTK_STATE_ACTIVE;
+ if (tab->state & State_Selected)
+ state = GTK_STATE_NORMAL;
+
+ bool first = tab->position == QStyleOptionTab::Beginning || tab->position == QStyleOptionTab::OnlyOneTab;
+ bool last = tab->position == QStyleOptionTab::End || tab->position == QStyleOptionTab::OnlyOneTab;
+ bool selected = (tab->state & State_Selected);
+ if (option->direction == Qt::RightToLeft) {
+ bool tmp = first;
+ first = last;
+ last = tmp;
+ }
+ int topIndent = 3;
+ int bottomIndent = 1;
+ int tabOverlap = 1;
+ painter->save();
+
+ switch (tab->shape) {
+ case QTabBar::RoundedNorth:
+ if (!selected)
+ rect.adjust(first ? 0 : -tabOverlap, topIndent, last ? 0 : tabOverlap, -bottomIndent);
+ gtkPainter.paintExtention( gtkNotebook, "tab", rect,
+ state, shadow, GTK_POS_BOTTOM, style);
+ break;
+
+ case QTabBar::RoundedSouth:
+ if (!selected)
+ rect.adjust(first ? 0 : -tabOverlap, 0, last ? 0 : tabOverlap, -topIndent);
+ gtkPainter.paintExtention( gtkNotebook, "tab", rect.adjusted(0, 1, 0, 0),
+ state, shadow, GTK_POS_TOP, style);
+ break;
+
+ case QTabBar::RoundedWest:
+ if (!selected)
+ rect.adjust(topIndent, 0, -bottomIndent, 0);
+ gtkPainter.paintExtention( gtkNotebook, "tab", rect, state, shadow, GTK_POS_RIGHT, style);
+ break;
+
+ case QTabBar::RoundedEast:
+ if (!selected)
+ rect.adjust(bottomIndent, 0, -topIndent, 0);
+ gtkPainter.paintExtention( gtkNotebook, "tab", rect, state, shadow, GTK_POS_LEFT, style);
+ break;
+
+ default:
+ QCleanlooksStyle::drawControl(element, option, painter, widget);
+ break;
+ }
+
+ painter->restore();
+ }
+
+ break;
+
+#endif //QT_NO_TABBAR
+
+ case CE_ProgressBarGroove:
+ if (const QStyleOptionProgressBar *bar = qstyleoption_cast<const QStyleOptionProgressBar *>(option)) {
+ Q_UNUSED(bar);
+ GtkWidget *gtkProgressBar = QGtk::gtkWidget(QLS("GtkProgressBar"));
+ GtkStateType state = gtkPainter.gtkState(option);
+ gtkPainter.paintBox( gtkProgressBar, "trough", option->rect, state, GTK_SHADOW_IN, gtkProgressBar->style);
+ }
+
+ break;
+
+ case CE_ProgressBarContents:
+ if (const QStyleOptionProgressBar *bar = qstyleoption_cast<const QStyleOptionProgressBar *>(option)) {
+ GtkStateType state = option->state & State_Enabled ? GTK_STATE_NORMAL : GTK_STATE_INSENSITIVE;
+ GtkWidget *gtkProgressBar = QGtk::gtkWidget(QLS("GtkProgressBar"));
+ style = gtkProgressBar->style;
+ gtkPainter.paintBox( gtkProgressBar, "trough", option->rect, state, GTK_SHADOW_IN, style);
+ int xt = style->xthickness;
+ int yt = style->ythickness;
+ QRect rect = bar->rect.adjusted(xt, yt, -xt, -yt);
+ bool vertical = false;
+ bool inverted = false;
+ bool indeterminate = (bar->minimum == 0 && bar->maximum == 0);
+ // Get extra style options if version 2
+
+ if (const QStyleOptionProgressBarV2 *bar2 = qstyleoption_cast<const QStyleOptionProgressBarV2 *>(option)) {
+ vertical = (bar2->orientation == Qt::Vertical);
+ inverted = bar2->invertedAppearance;
+ }
+
+ // If the orientation is vertical, we use a transform to rotate
+ // the progress bar 90 degrees clockwise. This way we can use the
+ // same rendering code for both orientations.
+ if (vertical) {
+ rect.translate(xt, -yt * 2);
+ rect = QRect(rect.left(), rect.top(), rect.height(), rect.width()); // Flip width and height
+ QTransform m;
+ m.translate(rect.height(), 0);
+ m.rotate(90.0);
+ painter->setTransform(m);
+ }
+
+ int maxWidth = rect.width();
+ int minWidth = 4;
+
+ qint64 progress = (qint64)qMax(bar->progress, bar->minimum); // Workaround for bug in QProgressBar
+ double vc6_workaround = ((progress - qint64(bar->minimum)) / double(qint64(bar->maximum) - qint64(bar->minimum))) * maxWidth;
+ int progressBarWidth = (int(vc6_workaround) > minWidth ) ? int(vc6_workaround) : minWidth;
+ int width = indeterminate ? maxWidth : progressBarWidth;
+ bool reverse = (!vertical && (bar->direction == Qt::RightToLeft)) || vertical;
+
+ if (inverted)
+ reverse = !reverse;
+
+ int maximum = 2;
+ int fakePos = 0;
+ if (bar->minimum == bar->maximum)
+ maximum = 0;
+ if (bar->progress == bar->maximum)
+ fakePos = maximum;
+ else if (bar->progress > bar->minimum)
+ fakePos = maximum - 1;
+
+ GtkObject *adjustment = QGtk::gtk_adjustment_new(fakePos, 0, maximum, 0, 0, 0);
+ QGtk::gtk_progress_set_adjustment((GtkProgress*)(gtkProgressBar), (GtkAdjustment*)(adjustment));
+
+ QRect progressBar;
+
+ if (!indeterminate) {
+ if (!reverse)
+ progressBar.setRect(rect.left(), rect.top(), width, rect.height());
+ else
+ progressBar.setRect(rect.right() - width, rect.top(), width, rect.height());
+
+ } else {
+ Q_D(const QGtkStyle);
+ int slideWidth = ((rect.width() - 4) * 2) / 3;
+ int step = ((d->animateStep * slideWidth) / d->animationFps) % slideWidth;
+ if ((((d->animateStep * slideWidth) / d->animationFps) % (2 * slideWidth)) >= slideWidth)
+ step = slideWidth - step;
+ progressBar.setRect(rect.left() + step, rect.top(), slideWidth / 2, rect.height());
+ }
+
+ QString key = QString(QLS("%0")).arg(fakePos);
+ if (inverted) {
+ key += QLatin1String("inv");
+ gtkPainter.setFlipHorizontal(true);
+ }
+ gtkPainter.paintBox( gtkProgressBar, "bar", progressBar, GTK_STATE_SELECTED, GTK_SHADOW_OUT, style, key);
+ }
+
+ break;
+
+ default:
+ QCleanlooksStyle::drawControl(element, option, painter, widget);
+ }
+}
+
+/*!
+ \reimp
+*/
+QRect QGtkStyle::subControlRect(ComplexControl control, const QStyleOptionComplex *option,
+ SubControl subControl, const QWidget *widget) const
+{
+ QRect rect = QWindowsStyle::subControlRect(control, option, subControl, widget);
+ if (!QGtk::isThemeAvailable())
+ return QCleanlooksStyle::subControlRect(control, option, subControl, widget);
+
+ switch (control) {
+ case CC_TitleBar:
+ return QCleanlooksStyle::subControlRect(control, option, subControl, widget);
+
+ case CC_Slider:
+ if (const QStyleOptionSlider *slider = qstyleoption_cast<const QStyleOptionSlider *>(option)) {
+ // Reserve space for outside focus rect
+ QStyleOptionSlider sliderCopy = *slider;
+ sliderCopy.rect = option->rect.adjusted(2, 2, -2, -2);
+ return QCleanlooksStyle::subControlRect(control, &sliderCopy, subControl, widget);
+ }
+
+ break;
+
+#ifndef QT_NO_GROUPBOX
+
+ case CC_GroupBox:
+ if (qstyleoption_cast<const QStyleOptionGroupBox *>(option)) {
+ rect = option->rect.adjusted(0, groupBoxTopMargin, 0, -groupBoxBottomMargin);
+ int topMargin = 0;
+ int topHeight = 0;
+ topHeight = 10;
+ QRect frameRect = rect;
+ frameRect.setTop(topMargin);
+
+ if (subControl == SC_GroupBoxFrame)
+ return rect;
+ else if (subControl == SC_GroupBoxContents) {
+ int margin = 0;
+ int leftMarginExtension = 8;
+ return frameRect.adjusted(leftMarginExtension + margin, margin + topHeight + groupBoxTitleMargin, -margin, -margin);
+ }
+
+ if (const QGroupBox *groupBoxWidget = qobject_cast<const QGroupBox *>(widget)) {
+ //Prepare metrics for a bold font
+ QFont font = widget->font();
+ font.setBold(true);
+ QFontMetrics fontMetrics(font);
+ QSize textRect = fontMetrics.boundingRect(groupBoxWidget->title()).size() + QSize(4, 4);
+ int indicatorWidth = pixelMetric(PM_IndicatorWidth, option, widget);
+ int indicatorHeight = pixelMetric(PM_IndicatorHeight, option, widget);
+
+ if (subControl == SC_GroupBoxCheckBox) {
+ rect.setWidth(indicatorWidth);
+ rect.setHeight(indicatorHeight);
+ rect.moveTop((textRect.height() - indicatorHeight) / 2);
+
+ } else if (subControl == SC_GroupBoxLabel) {
+ if (groupBoxWidget->isCheckable())
+ rect.adjust(indicatorWidth + 4, 0, 0, 0);
+ rect.setSize(textRect);
+ }
+ rect = visualRect(option->direction, option->rect, rect);
+ }
+ }
+
+ return rect;
+
+#endif
+#ifndef QT_NO_SPINBOX
+
+ case CC_SpinBox:
+ if (const QStyleOptionSpinBox *spinbox = qstyleoption_cast<const QStyleOptionSpinBox *>(option)) {
+ GtkWidget *gtkSpinButton = QGtk::gtkWidget(QLS("GtkSpinButton"));
+ int center = spinbox->rect.height() / 2;
+ int xt = spinbox->frame ? gtkSpinButton->style->xthickness : 0;
+ int yt = spinbox->frame ? gtkSpinButton->style->ythickness : 0;
+ int y = yt;
+
+ QSize bs;
+ bs.setHeight(qMax(8, spinbox->rect.height()/2 - y));
+ bs.setWidth(spinboxArrowSize());
+ int x, lx, rx;
+ x = spinbox->rect.width() - y - bs.width() + 2;
+ lx = xt;
+ rx = x - xt;
+
+ switch (subControl) {
+
+ case SC_SpinBoxUp:
+ if (spinbox->buttonSymbols == QAbstractSpinBox::NoButtons)
+ return QRect();
+ rect = QRect(x, xt, bs.width(), center - yt);
+ break;
+
+ case SC_SpinBoxDown:
+ if (spinbox->buttonSymbols == QAbstractSpinBox::NoButtons)
+ return QRect();
+ rect = QRect(x, center, bs.width(), spinbox->rect.bottom() - center - yt + 1);
+ break;
+
+ case SC_SpinBoxEditField:
+ if (spinbox->buttonSymbols == QAbstractSpinBox::NoButtons)
+ rect = QRect(lx, yt, spinbox->rect.width() - 2*xt, spinbox->rect.height() - 2*yt);
+ else
+ rect = QRect(lx, yt, rx - qMax(xt - 1, 0), spinbox->rect.height() - 2*yt);
+ break;
+
+ case SC_SpinBoxFrame:
+ rect = spinbox->rect;
+
+ default:
+ break;
+ }
+
+ rect = visualRect(spinbox->direction, spinbox->rect, rect);
+ }
+
+ break;
+
+#endif // Qt_NO_SPINBOX
+#ifndef QT_NO_COMBOBOX
+
+ case CC_ComboBox:
+ if (const QStyleOptionComboBox *box = qstyleoption_cast<const QStyleOptionComboBox *>(option)) {
+ // We employ the gtk widget to position arrows and separators for us
+ QString comboBoxPath = box->editable ? QLS("GtkComboBoxEntry") : QLS("GtkComboBox");
+ GtkWidget *gtkCombo = QGtk::gtkWidget(comboBoxPath);
+ QGtk::gtk_widget_set_direction(gtkCombo, (option->direction == Qt::RightToLeft) ? GTK_TEXT_DIR_RTL : GTK_TEXT_DIR_LTR);
+ GtkAllocation geometry = {0, 0, qMax(0, option->rect.width()), qMax(0, option->rect.height())};
+ QGtk::gtk_widget_size_allocate(gtkCombo, &geometry);
+ int appears_as_list = !styleHint(QStyle::SH_ComboBox_Popup, option, widget);
+ QString arrowPath = comboBoxPath + QLS(".GtkToggleButton");
+
+ if (!box->editable && !appears_as_list)
+ arrowPath += QLS(".GtkHBox.GtkArrow");
+
+ GtkWidget *arrowWidget = QGtk::gtkWidget(arrowPath);
+ if (!arrowWidget)
+ return QCleanlooksStyle::subControlRect(control, option, subControl, widget);
+
+ QRect buttonRect(arrowWidget->allocation.x, arrowWidget->allocation.y,
+ arrowWidget->allocation.width, arrowWidget->allocation.height);
+
+ switch (subControl) {
+
+ case SC_ComboBoxArrow: // Note: this indicates the arrowbutton for editable combos
+ rect = buttonRect;
+
+ break;
+
+ case SC_ComboBoxEditField: {
+ rect = visualRect(option->direction, option->rect, rect);
+ int xMargin = box->editable ? 1 : 4, yMargin = 2;
+ rect.setRect(option->rect.left() + gtkCombo->style->xthickness + xMargin,
+ option->rect.top() + gtkCombo->style->ythickness + yMargin,
+ option->rect.width() - buttonRect.width() - 2*(gtkCombo->style->xthickness + xMargin),
+ option->rect.height() - 2*(gtkCombo->style->ythickness + yMargin));
+ rect = visualRect(option->direction, option->rect, rect);
+ break;
+ }
+
+ default:
+ break;
+ }
+ }
+
+ break;
+
+#endif // QT_NO_COMBOBOX
+
+ default:
+ break;
+ }
+
+ return rect;
+}
+
+/*!
+ \reimp
+*/
+QSize QGtkStyle::sizeFromContents(ContentsType type, const QStyleOption *option,
+
+ const QSize &size, const QWidget *widget) const
+{
+
+ QSize newSize = QCleanlooksStyle::sizeFromContents(type, option, size, widget);
+ if (!QGtk::isThemeAvailable())
+ return newSize;
+
+ switch (type) {
+
+ case CT_ToolButton:
+ if (const QStyleOptionToolButton *toolbutton = qstyleoption_cast<const QStyleOptionToolButton *>(option)) {
+ GtkWidget *gtkButton = QGtk::gtkWidget(QLS("GtkButton"));
+ newSize = size + QSize(2 * gtkButton->style->xthickness, 1 + 2 * gtkButton->style->ythickness);
+ if (widget && qobject_cast<QToolBar *>(widget->parentWidget())) {
+ QSize minSize(0, 25);
+ if (toolbutton->toolButtonStyle != Qt::ToolButtonTextOnly)
+ minSize = toolbutton->iconSize + QSize(12, 12);
+ newSize = newSize.expandedTo(minSize);
+ }
+
+ if (toolbutton->features & QStyleOptionToolButton::HasMenu)
+ newSize += QSize(6, 0);
+ }
+ break;
+
+ case CT_MenuItem:
+ if (const QStyleOptionMenuItem *menuItem = qstyleoption_cast<const QStyleOptionMenuItem *>(option)) {
+ int textMargin = 8;
+
+ if (menuItem->menuItemType == QStyleOptionMenuItem::Separator) {
+ GtkWidget *gtkMenuSeparator = QGtk::gtkWidget(QLS("GtkMenu.GtkSeparatorMenuItem"));
+ gboolean wide_separators;
+ gint separator_height;
+ QGtk::gtk_widget_style_get(gtkMenuSeparator,
+ "wide-separators", &wide_separators,
+ "separator-height", &separator_height,
+ NULL);
+ newSize = QSize(size.width(), wide_separators ? separator_height - 1 : 7 );
+
+ break;
+ }
+
+ GtkWidget *gtkMenuItem = QGtk::gtkWidget(QLS("GtkMenu.GtkMenuItem"));
+ GtkStyle* style = gtkMenuItem->style;
+ newSize += QSize(textMargin + style->xthickness - 2, style->ythickness - 4);
+
+ // Cleanlooks assumes a check column of 20 pixels so we need to
+ // expand it a bit
+ gint checkSize;
+ QGtk::gtk_widget_style_get(QGtk::gtkWidget(QLS("GtkMenu.GtkCheckMenuItem")), "indicator-size", &checkSize, NULL);
+ newSize.setHeight(qMax(newSize.height(), checkSize + 2));
+ newSize.setWidth(newSize.width() + qMax(0, checkSize - 20));
+ }
+
+ break;
+
+ case CT_Menu:
+ // This is evil, but QMenu adds 1 pixel too much
+ newSize -= QSize(0, 1);
+
+ break;
+
+ case CT_SpinBox:
+ // QSpinBox does some nasty things that depends on CT_LineEdit
+ newSize = size + QSize(0, -QGtk::gtkWidget(QLS("GtkSpinButton"))->style->ythickness * 2 + 2);
+ break;
+
+ case CT_PushButton:
+ if (const QStyleOptionButton *btn = qstyleoption_cast<const QStyleOptionButton *>(option)) {
+ GtkWidget *gtkButton = QGtk::gtkWidget(QLS("GtkButton"));
+ gint focusPadding, focusWidth;
+ QGtk::gtk_widget_style_get(gtkButton, "focus-padding", &focusPadding, NULL);
+ QGtk::gtk_widget_style_get(gtkButton, "focus-line-width", &focusWidth, NULL);
+ newSize = size;
+ newSize += QSize(2*gtkButton->style->xthickness + 4, 2*gtkButton->style->ythickness);
+ newSize += QSize(2*(focusWidth + focusPadding + 2), 2*(focusWidth + focusPadding));
+
+ GtkWidget *gtkButtonBox = QGtk::gtkWidget(QLS("GtkHButtonBox"));
+ gint minWidth = 85, minHeight = 0;
+ QGtk::gtk_widget_style_get(gtkButtonBox, "child-min-width", &minWidth,
+ "child-min-height", &minHeight, NULL);
+ if (!btn->text.isEmpty() && newSize.width() < minWidth)
+ newSize.setWidth(minWidth);
+ if (newSize.height() < minHeight)
+ newSize.setHeight(minHeight);
+ }
+
+ break;
+
+ case CT_Slider: {
+ GtkWidget *gtkSlider = QGtk::gtkWidget(QLS("GtkHScale"));
+ newSize = size + QSize(2*gtkSlider->style->xthickness, 2*gtkSlider->style->ythickness);
+ }
+ break;
+
+ case CT_MenuBarItem://cleanlooks adds 2 pixels
+ newSize = QWindowsStyle::sizeFromContents(type, option, size, widget) + QSize(0, 1);
+ break;
+
+ case CT_LineEdit: {
+ GtkWidget *gtkEntry = QGtk::gtkWidget(QLS("GtkEntry"));
+ newSize = size + QSize(2*gtkEntry->style->xthickness, 2*gtkEntry->style->ythickness);
+ }
+ break;
+
+ case CT_ItemViewItem:
+ newSize += QSize(0, 2);
+ break;
+
+ case CT_ComboBox:
+ if (const QStyleOptionComboBox *combo = qstyleoption_cast<const QStyleOptionComboBox *>(option)) {
+ GtkWidget *gtkCombo = QGtk::gtkWidget(QLS("GtkComboBox"));
+ QRect arrowButtonRect = subControlRect(CC_ComboBox, combo, SC_ComboBoxArrow, widget);
+ newSize = size + QSize(12 + arrowButtonRect.width() + 2*gtkCombo->style->xthickness, 4 + 2*gtkCombo->style->ythickness);
+
+ if (!(widget && qobject_cast<QToolBar *>(widget->parentWidget())))
+ newSize += QSize(0, 3);
+ }
+ break;
+
+ case CT_GroupBox:
+ newSize += QSize(4, groupBoxBottomMargin + groupBoxTopMargin + groupBoxTitleMargin); // Add some space below the groupbox
+ break;
+
+ case CT_TabBarTab:
+ if (const QStyleOptionTab *tab = qstyleoption_cast<const QStyleOptionTab *>(option)) {
+ if (!tab->icon.isNull())
+ newSize += QSize(6, 0);
+ }
+ newSize += QSize(1, 0);
+ break;
+
+ default:
+ break;
+ }
+
+ return newSize;
+}
+
+/*! \reimp */
+QPixmap QGtkStyle::standardPixmap(StandardPixmap sp, const QStyleOption *option,
+ const QWidget *widget) const
+{
+ if (!QGtk::isThemeAvailable())
+ return QCleanlooksStyle::standardPixmap(sp, option, widget);
+
+ QPixmap pixmap;
+ switch (sp) {
+
+ case SP_TitleBarNormalButton: {
+ QImage restoreButton((const char **)dock_widget_restore_xpm);
+ QColor alphaCorner = restoreButton.color(2);
+ alphaCorner.setAlpha(80);
+ restoreButton.setColor(2, alphaCorner.rgba());
+ alphaCorner.setAlpha(180);
+ restoreButton.setColor(4, alphaCorner.rgba());
+ return QPixmap::fromImage(restoreButton);
+ }
+ break;
+
+ case SP_TitleBarCloseButton: // Fall through
+ case SP_DockWidgetCloseButton: {
+
+ QImage closeButton((const char **)dock_widget_close_xpm);
+ QColor alphaCorner = closeButton.color(2);
+ alphaCorner.setAlpha(80);
+ closeButton.setColor(2, alphaCorner.rgba());
+ return QPixmap::fromImage(closeButton);
+ }
+ break;
+
+ case SP_DialogDiscardButton: {
+ return QGtkPainter::getIcon(GTK_STOCK_DELETE);
+ }
+
+ case SP_DialogOkButton: {
+ return QGtkPainter::getIcon(GTK_STOCK_OK);
+ }
+
+ case SP_DialogCancelButton: {
+ return QGtkPainter::getIcon(GTK_STOCK_CANCEL);
+ }
+
+ case SP_DialogYesButton: {
+ return QGtkPainter::getIcon(GTK_STOCK_YES);
+ }
+
+ case SP_DialogNoButton: {
+ return QGtkPainter::getIcon(GTK_STOCK_NO);
+ }
+
+ case SP_DialogOpenButton: {
+ return QGtkPainter::getIcon(GTK_STOCK_OPEN);
+ }
+
+ case SP_DialogCloseButton: {
+ return QGtkPainter::getIcon(GTK_STOCK_CLOSE);
+ }
+
+ case SP_DialogApplyButton: {
+ return QGtkPainter::getIcon(GTK_STOCK_APPLY);
+ }
+
+ case SP_DialogSaveButton: {
+ return QGtkPainter::getIcon(GTK_STOCK_SAVE);
+ }
+
+ case SP_MessageBoxWarning: {
+ return QGtkPainter::getIcon(GTK_STOCK_DIALOG_WARNING, GTK_ICON_SIZE_DIALOG);
+ }
+
+ case SP_MessageBoxQuestion: {
+ return QGtkPainter::getIcon(GTK_STOCK_DIALOG_QUESTION, GTK_ICON_SIZE_DIALOG);
+ }
+
+ case SP_MessageBoxInformation: {
+ return QGtkPainter::getIcon(GTK_STOCK_DIALOG_INFO, GTK_ICON_SIZE_DIALOG);
+ }
+
+ case SP_MessageBoxCritical: {
+ return QGtkPainter::getIcon(GTK_STOCK_DIALOG_ERROR, GTK_ICON_SIZE_DIALOG);
+ }
+
+ default:
+ return QCleanlooksStyle::standardPixmap(sp, option, widget);
+ }
+
+ return QPixmap();
+}
+
+/*! \reimp */
+QRect QGtkStyle::subElementRect(SubElement element, const QStyleOption *option, const QWidget *widget) const
+{
+ QRect r = QCleanlooksStyle::subElementRect(element, option, widget);
+ switch (element) {
+ case SE_ProgressBarLabel:
+ case SE_ProgressBarContents:
+ case SE_ProgressBarGroove:
+ return option->rect;
+ default:
+ break;
+ }
+
+ return r;
+}
+
+/*!
+ \reimp
+*/
+QRect QGtkStyle::itemPixmapRect(const QRect &r, int flags, const QPixmap &pixmap) const
+{
+ return QCleanlooksStyle::itemPixmapRect(r, flags, pixmap);
+}
+
+/*!
+ \reimp
+*/
+void QGtkStyle::drawItemPixmap(QPainter *painter, const QRect &rect,
+ int alignment, const QPixmap &pixmap) const
+{
+ QCleanlooksStyle::drawItemPixmap(painter, rect, alignment, pixmap);
+}
+
+/*!
+ \reimp
+*/
+QStyle::SubControl QGtkStyle::hitTestComplexControl(ComplexControl cc, const QStyleOptionComplex *opt,
+ const QPoint &pt, const QWidget *w) const
+{
+ return QCleanlooksStyle::hitTestComplexControl(cc, opt, pt, w);
+}
+
+/*!
+ \reimp
+*/
+QPixmap QGtkStyle::generatedIconPixmap(QIcon::Mode iconMode, const QPixmap &pixmap,
+ const QStyleOption *opt) const
+{
+ return QCleanlooksStyle::generatedIconPixmap(iconMode, pixmap, opt);
+}
+
+/*!
+ \reimp
+*/
+void QGtkStyle::drawItemText(QPainter *painter, const QRect &rect, int alignment, const QPalette &pal,
+ bool enabled, const QString& text, QPalette::ColorRole textRole) const
+{
+ return QCleanlooksStyle::drawItemText(painter, rect, alignment, pal, enabled, text, textRole);
+}
+
+QT_END_NAMESPACE
+
+#endif //!defined(QT_NO_STYLE_QGTK)
diff --git a/src/gui/styles/qgtkstyle.h b/src/gui/styles/qgtkstyle.h
new file mode 100644
index 0000000000..b7d5bcf09c
--- /dev/null
+++ b/src/gui/styles/qgtkstyle.h
@@ -0,0 +1,118 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QGTKSTYLE_H
+#define QGTKSTYLE_H
+
+#include <QtGui/QCleanlooksStyle>
+#include <QtGui/QPalette>
+#include <QtGui/QFont>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+#if !defined(QT_NO_STYLE_GTK)
+
+class QPainterPath;
+class QGtkStylePrivate;
+
+class Q_GUI_EXPORT QGtkStyle : public QCleanlooksStyle
+{
+ Q_OBJECT
+ Q_DECLARE_PRIVATE(QGtkStyle)
+
+public:
+ QGtkStyle();
+ ~QGtkStyle();
+
+ QPalette standardPalette() const;
+
+ void drawPrimitive(PrimitiveElement element, const QStyleOption *option,
+ QPainter *painter, const QWidget *widget) const;
+ void drawControl(ControlElement control, const QStyleOption *option,
+ QPainter *painter, const QWidget *widget) const;
+ void drawComplexControl(ComplexControl control, const QStyleOptionComplex *option,
+ QPainter *painter, const QWidget *widget) const;
+ void drawItemPixmap(QPainter *painter, const QRect &rect, int alignment,
+ const QPixmap &pixmap) const;
+ void drawItemText(QPainter *painter, const QRect &rect, int alignment, const QPalette &pal,
+ bool enabled, const QString& text, QPalette::ColorRole textRole) const;
+
+ int pixelMetric(PixelMetric metric, const QStyleOption *option = 0,
+ const QWidget *widget = 0) const;
+ int styleHint(StyleHint hint, const QStyleOption *option,
+ const QWidget *widget, QStyleHintReturn *returnData) const;
+
+ QStyle::SubControl hitTestComplexControl(ComplexControl cc, const QStyleOptionComplex *opt,
+ const QPoint &pt, const QWidget *w) const;
+
+ QRect subControlRect(ComplexControl control, const QStyleOptionComplex *option,
+ SubControl subControl, const QWidget *widget) const;
+ QRect subElementRect(SubElement sr, const QStyleOption *opt, const QWidget *w) const;
+ QRect itemPixmapRect(const QRect &r, int flags, const QPixmap &pixmap) const;
+
+
+ QSize sizeFromContents(ContentsType type, const QStyleOption *option,
+ const QSize &size, const QWidget *widget) const;
+ QPixmap standardPixmap(StandardPixmap sp, const QStyleOption *option,
+ const QWidget *widget) const;
+ QPixmap generatedIconPixmap(QIcon::Mode iconMode, const QPixmap &pixmap,
+ const QStyleOption *opt) const;
+
+ void polish(QWidget *widget);
+ void polish(QApplication *app);
+ void polish(QPalette &palette);
+
+ void unpolish(QWidget *widget);
+ void unpolish(QApplication *app);
+};
+
+
+#endif //!defined(QT_NO_STYLE_QGTK)
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif //QGTKSTYLE_H
diff --git a/src/gui/styles/qmacstyle_mac.h b/src/gui/styles/qmacstyle_mac.h
new file mode 100644
index 0000000000..f043a66731
--- /dev/null
+++ b/src/gui/styles/qmacstyle_mac.h
@@ -0,0 +1,144 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QMACSTYLE_MAC_H
+#define QMACSTYLE_MAC_H
+
+#include <QtGui/qwindowsstyle.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+#if defined(Q_WS_MAC) && !defined(QT_NO_STYLE_MAC)
+
+class QPalette;
+
+#if defined(QT_PLUGIN)
+#define Q_GUI_EXPORT_STYLE_MAC
+#else
+#define Q_GUI_EXPORT_STYLE_MAC Q_GUI_EXPORT
+#endif
+
+class QMacStylePrivate;
+class Q_GUI_EXPORT_STYLE_MAC QMacStyle : public QWindowsStyle
+{
+ Q_OBJECT
+public:
+ QMacStyle();
+ virtual ~QMacStyle();
+
+ void polish(QWidget *w);
+ void unpolish(QWidget *w);
+
+ void polish(QApplication*);
+ void unpolish(QApplication*);
+
+ void polish(QPalette &pal);
+
+ void drawPrimitive(PrimitiveElement pe, const QStyleOption *opt, QPainter *p,
+ const QWidget *w = 0) const;
+ void drawControl(ControlElement element, const QStyleOption *opt, QPainter *p,
+ const QWidget *w = 0) const;
+ QRect subElementRect(SubElement r, const QStyleOption *opt, const QWidget *widget = 0) const;
+ void drawComplexControl(ComplexControl cc, const QStyleOptionComplex *opt, QPainter *p,
+ const QWidget *w = 0) const;
+ SubControl hitTestComplexControl(ComplexControl cc, const QStyleOptionComplex *opt,
+ const QPoint &pt, const QWidget *w = 0) const;
+ QRect subControlRect(ComplexControl cc, const QStyleOptionComplex *opt, SubControl sc,
+ const QWidget *w = 0) const;
+ QSize sizeFromContents(ContentsType ct, const QStyleOption *opt,
+ const QSize &contentsSize, const QWidget *w = 0) const;
+
+ int pixelMetric(PixelMetric pm, const QStyleOption *opt = 0, const QWidget *widget = 0) const;
+
+ QPalette standardPalette() const;
+
+ virtual int styleHint(StyleHint sh, const QStyleOption *opt = 0, const QWidget *w = 0,
+ QStyleHintReturn *shret = 0) const;
+
+ enum FocusRectPolicy { FocusEnabled, FocusDisabled, FocusDefault };
+ static void setFocusRectPolicy(QWidget *w, FocusRectPolicy policy);
+ static FocusRectPolicy focusRectPolicy(const QWidget *w);
+
+ enum WidgetSizePolicy { SizeSmall, SizeLarge, SizeMini, SizeDefault
+#ifdef QT3_SUPPORT
+ , SizeNone = SizeDefault
+#endif
+ };
+ static void setWidgetSizePolicy(const QWidget *w, WidgetSizePolicy policy);
+ static WidgetSizePolicy widgetSizePolicy(const QWidget *w);
+
+ QPixmap standardPixmap(StandardPixmap sp, const QStyleOption *opt,
+ const QWidget *widget = 0) const;
+
+ QPixmap generatedIconPixmap(QIcon::Mode iconMode, const QPixmap &pixmap,
+ const QStyleOption *opt) const;
+
+ virtual void drawItemText(QPainter *p, const QRect &r, int flags, const QPalette &pal,
+ bool enabled, const QString &text, QPalette::ColorRole textRole = QPalette::NoRole) const;
+
+ bool event(QEvent *e);
+
+protected Q_SLOTS:
+ QIcon standardIconImplementation(StandardPixmap standardIcon, const QStyleOption *opt = 0,
+ const QWidget *widget = 0) const;
+ int layoutSpacingImplementation(QSizePolicy::ControlType control1,
+ QSizePolicy::ControlType control2,
+ Qt::Orientation orientation,
+ const QStyleOption *option = 0,
+ const QWidget *widget = 0) const;
+
+private:
+ Q_DISABLE_COPY(QMacStyle)
+
+ QMacStylePrivate *d;
+};
+
+#endif // Q_WS_MAC
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QMACSTYLE_H
diff --git a/src/gui/styles/qmacstyle_mac.mm b/src/gui/styles/qmacstyle_mac.mm
new file mode 100644
index 0000000000..a3f5cfe2de
--- /dev/null
+++ b/src/gui/styles/qmacstyle_mac.mm
@@ -0,0 +1,6394 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qmacstyle_mac.h"
+
+#if defined(Q_WS_MAC) && !defined(QT_NO_STYLE_MAC)
+#define QMAC_QAQUASTYLE_SIZE_CONSTRAIN
+//#define DEBUG_SIZE_CONSTRAINT
+
+#include <private/qcombobox_p.h>
+#include <private/qmacstylepixmaps_mac_p.h>
+#include <private/qpaintengine_mac_p.h>
+#include <private/qpainter_p.h>
+#include <private/qprintengine_mac_p.h>
+#include <qapplication.h>
+#include <qbitmap.h>
+#include <qcheckbox.h>
+#include <qcombobox.h>
+#include <qdialogbuttonbox.h>
+#include <qdockwidget.h>
+#include <qevent.h>
+#include <qfocusframe.h>
+#include <qformlayout.h>
+#include <qgroupbox.h>
+#include <qhash.h>
+#include <qheaderview.h>
+#include <qlayout.h>
+#include <qlineedit.h>
+#include <qlistview.h>
+#include <qmainwindow.h>
+#include <qmap.h>
+#include <qmenubar.h>
+#include <qpaintdevice.h>
+#include <qpainter.h>
+#include <qpixmapcache.h>
+#include <qpointer.h>
+#include <qprogressbar.h>
+#include <qpushbutton.h>
+#include <qradiobutton.h>
+#include <qrubberband.h>
+#include <qsizegrip.h>
+#include <qspinbox.h>
+#include <qsplitter.h>
+#include <qstyleoption.h>
+#include <qtextedit.h>
+#include <qtextstream.h>
+#include <qtoolbar.h>
+#include <qtoolbutton.h>
+#include <qtreeview.h>
+#include <qtableview.h>
+#include <qwizard.h>
+#include <qdebug.h>
+#include <qlibrary.h>
+#include <qdatetimeedit.h>
+#include <QtGui/qgraphicsproxywidget.h>
+#include <QtGui/qgraphicsview.h>
+#include <private/qt_cocoa_helpers_mac_p.h>
+
+QT_BEGIN_NAMESPACE
+
+extern QRegion qt_mac_convert_mac_region(RgnHandle); //qregion_mac.cpp
+extern QHash<QByteArray, QFont> *qt_app_fonts_hash(); // qapplication.cpp
+
+// The following constants are used for adjusting the size
+// of push buttons so that they are drawn inside their bounds.
+static const int PushButtonLeftOffset = 6;
+static const int PushButtonTopOffset = 4;
+static const int PushButtonRightOffset = 12;
+static const int PushButtonBottomOffset = 12;
+static const int MiniButtonH = 26;
+static const int SmallButtonH = 30;
+static const int BevelButtonW = 50;
+static const int BevelButtonH = 22;
+static const int PushButtonContentPadding = 6;
+
+// These colors specify the titlebar gradient colors on
+// Leopard. Ideally we should get them from the system.
+static const QColor titlebarGradientActiveBegin(220, 220, 220);
+static const QColor titlebarGradientActiveEnd(151, 151, 151);
+static const QColor titlebarSeparatorLineActive(111, 111, 111);
+static const QColor titlebarGradientInactiveBegin(241, 241, 241);
+static const QColor titlebarGradientInactiveEnd(207, 207, 207);
+static const QColor titlebarSeparatorLineInactive(131, 131, 131);
+
+// Gradient colors used for the dock widget title bar and
+// non-unifed tool bar bacground.
+static const QColor mainWindowGradientBegin(240, 240, 240);
+static const QColor mainWindowGradientEnd(200, 200, 200);
+
+
+// Resolve these at run-time, since the functions was moved in Leopard.
+typedef HIRect * (*PtrHIShapeGetBounds)(HIShapeRef, HIRect *);
+static PtrHIShapeGetBounds ptrHIShapeGetBounds = 0;
+
+static bool isVerticalTabs(const QTabBar::Shape shape) {
+ return (shape == QTabBar::RoundedEast
+ || shape == QTabBar::TriangularEast
+ || shape == QTabBar::RoundedWest
+ || shape == QTabBar::TriangularWest);
+}
+
+static int closeButtonSize = 12;
+
+void drawTabCloseButton(QPainter *p, bool hover, bool active, bool selected)
+{
+ // draw background circle
+ p->setRenderHints(QPainter::Antialiasing);
+ QRect rect(0, 0, closeButtonSize, closeButtonSize);
+ QColor background;
+ if (hover) {
+ background = QColor(124, 124, 124);
+ } else {
+ if (active) {
+ if (selected)
+ background = QColor(104, 104, 104);
+ else
+ background = QColor(83, 83, 83);
+ } else {
+ if (selected)
+ background = QColor(144, 144, 144);
+ else
+ background = QColor(114, 114, 114);
+ }
+ }
+ p->setPen(Qt::transparent);
+ p->setBrush(background);
+ p->drawEllipse(rect);
+
+ // draw cross
+ int min = 3;
+ int max = 9;
+ QPen crossPen;
+ crossPen.setColor(QColor(194, 194, 194));
+ crossPen.setWidthF(1.3);
+ crossPen.setCapStyle(Qt::FlatCap);
+ p->setPen(crossPen);
+ p->drawLine(min, min, max, max);
+ p->drawLine(min, max, max, min);
+}
+
+QRect rotateTabPainter(QPainter *p, QTabBar::Shape shape, QRect tabRect)
+{
+ if (isVerticalTabs(shape)) {
+ int newX, newY, newRot;
+ if (shape == QTabBar::RoundedEast
+ || shape == QTabBar::TriangularEast) {
+ newX = tabRect.width();
+ newY = tabRect.y();
+ newRot = 90;
+ } else {
+ newX = 0;
+ newY = tabRect.y() + tabRect.height();
+ newRot = -90;
+ }
+ tabRect.setRect(0, 0, tabRect.height(), tabRect.width());
+ QMatrix m;
+ m.translate(newX, newY);
+ m.rotate(newRot);
+ p->setMatrix(m, true);
+ }
+ return tabRect;
+}
+
+void drawTabShape(QPainter *p, const QStyleOptionTabV3 *tabOpt)
+{
+ QRect r = tabOpt->rect;
+ p->translate(tabOpt->rect.x(), tabOpt->rect.y());
+ r.moveLeft(0);
+ r.moveTop(0);
+ QRect tabRect = rotateTabPainter(p, tabOpt->shape, r);
+
+ int width = tabRect.width();
+ int height = 20;
+ bool active = (tabOpt->state & QStyle::State_Active);
+ bool selected = (tabOpt->state & QStyle::State_Selected);
+
+ if (selected) {
+ QRect rect(1, 0, width - 2, height);
+
+ // fill body
+ if (active) {
+ p->fillRect(rect, QColor(151, 151, 151));
+ } else {
+ QLinearGradient gradient(rect.topLeft(), rect.bottomLeft());
+ gradient.setColorAt(0, QColor(207, 207, 207));
+ gradient.setColorAt(0.5, QColor(206, 206, 206));
+ gradient.setColorAt(1, QColor(201, 201, 201));
+ p->fillRect(rect, gradient);
+ }
+
+ // draw border
+ QColor borderSides;
+ QColor borderBottom;
+ if (active) {
+ borderSides = QColor(88, 88, 88);
+ borderBottom = QColor(88, 88, 88);
+ } else {
+ borderSides = QColor(121, 121, 121);
+ borderBottom = QColor(116, 116, 116);
+ }
+
+ p->setPen(borderSides);
+
+ int bottom = height;
+ // left line
+ p->drawLine(0, 1, 0, bottom-2);
+ // right line
+ p->drawLine(width-1, 1, width-1, bottom-2);
+
+ // bottom line
+ if (active) {
+ p->setPen(QColor(168, 168, 168));
+ p->drawLine(3, bottom-1, width-3, bottom-1);
+ }
+ p->setPen(borderBottom);
+ p->drawLine(2, bottom, width-2, bottom);
+
+ int w = 3;
+ QRectF rectangleLeft(1, height - w, w, w);
+ QRectF rectangleRight(width - 2, height - 1, w, w);
+ int startAngle = 180 * 16;
+ int spanAngle = 90 * 16;
+ p->setRenderHint(QPainter::Antialiasing);
+ p->drawArc(rectangleLeft, startAngle, spanAngle);
+ p->drawArc(rectangleRight, startAngle, -spanAngle);
+ } else {
+ // when the mouse is over non selected tabs they get a new color
+ bool hover = (tabOpt->state & QStyle::State_MouseOver);
+ if (hover) {
+ QRect rect(1, 2, width - 1, height - 1);
+ p->fillRect(rect, QColor(110, 110, 110));
+ }
+
+ // seperator lines between tabs
+ bool west = (tabOpt->shape == QTabBar::RoundedWest || tabOpt->shape == QTabBar::TriangularWest);
+ bool drawOnRight = !west;
+ if ((!drawOnRight && tabOpt->selectedPosition != QStyleOptionTab::NextIsSelected)
+ || (drawOnRight && tabOpt->selectedPosition != QStyleOptionTab::NextIsSelected)) {
+ QColor borderColor;
+ QColor borderHighlightColor;
+ if (active) {
+ borderColor = QColor(64, 64, 64);
+ borderHighlightColor = QColor(140, 140, 140);
+ } else {
+ borderColor = QColor(135, 135, 135);
+ borderHighlightColor = QColor(178, 178, 178);
+ }
+
+ int x = drawOnRight ? width : 0;
+
+ // tab seperator line
+ p->setPen(borderColor);
+ p->drawLine(x, 2, x, height + 1);
+
+ // tab seperator highlight
+ p->setPen(borderHighlightColor);
+ p->drawLine(x-1, 2, x-1, height + 1);
+ p->drawLine(x+1, 2, x+1, height + 1);
+ }
+ }
+}
+
+void drawTabBase(QPainter *p, const QStyleOptionTabBarBaseV2 *tbb, const QWidget *w)
+{
+ QRect r = tbb->rect;
+ if (isVerticalTabs(tbb->shape)) {
+ r.setWidth(w->width());
+ } else {
+ r.setHeight(w->height());
+ }
+ QRect tabRect = rotateTabPainter(p, tbb->shape, r);
+ int width = tabRect.width();
+ int height = tabRect.height();
+ bool active = (tbb->state & QStyle::State_Active);
+
+ // top border lines
+ QColor borderHighlightTop;
+ QColor borderTop;
+ if (active) {
+ borderTop = QColor(64, 64, 64);
+ borderHighlightTop = QColor(174, 174, 174);
+ } else {
+ borderTop = QColor(135, 135, 135);
+ borderHighlightTop = QColor(207, 207, 207);
+ }
+ p->setPen(borderHighlightTop);
+ p->drawLine(0, 0, width, 0);
+ p->setPen(borderTop);
+ p->drawLine(0, 1, width, 1);
+
+ // center block
+ QRect centralRect(0, 2, width, height - 2);
+ if (active) {
+ QColor mainColor = QColor(120, 120, 120);
+ p->fillRect(centralRect, mainColor);
+ } else {
+ QLinearGradient gradient(centralRect.topLeft(), centralRect.bottomLeft());
+ gradient.setColorAt(0, QColor(165, 165, 165));
+ gradient.setColorAt(0.5, QColor(164, 164, 164));
+ gradient.setColorAt(1, QColor(158, 158, 158));
+ p->fillRect(centralRect, gradient);
+ }
+
+ // bottom border lines
+ QColor borderHighlightBottom;
+ QColor borderBottom;
+ if (active) {
+ borderHighlightBottom = QColor(153, 153, 153);
+ borderBottom = QColor(64, 64, 64);
+ } else {
+ borderHighlightBottom = QColor(177, 177, 177);
+ borderBottom = QColor(127, 127, 127);
+ }
+ p->setPen(borderHighlightBottom);
+ p->drawLine(0, height - 2, width, height - 2);
+ p->setPen(borderBottom);
+ p->drawLine(0, height - 1, width, height - 1);
+}
+
+/*
+ AHIG:
+ Apple Human Interface Guidelines
+ http://developer.apple.com/documentation/UserExperience/Conceptual/OSXHIGuidelines/
+
+ Builder:
+ Apple Interface Builder v. 3.1.1
+*/
+
+// this works as long as we have at most 16 different control types
+#define CT1(c) CT2(c, c)
+#define CT2(c1, c2) ((uint(c1) << 16) | uint(c2))
+
+enum QAquaWidgetSize { QAquaSizeLarge = 0, QAquaSizeSmall = 1, QAquaSizeMini = 2,
+ QAquaSizeUnknown = -1 };
+
+#define SIZE(large, small, mini) \
+ (controlSize == QAquaSizeLarge ? (large) : controlSize == QAquaSizeSmall ? (small) : (mini))
+
+// same as return SIZE(...) but optimized
+#define return_SIZE(large, small, mini) \
+ do { \
+ static const int sizes[] = { (large), (small), (mini) }; \
+ return sizes[controlSize]; \
+ } while (0)
+
+static int getControlSize(const QStyleOption *option, const QWidget *widget)
+{
+ if (option) {
+ if (option->state & (QStyle::State_Small | QStyle::State_Mini))
+ return (option->state & QStyle::State_Mini) ? QAquaSizeMini : QAquaSizeSmall;
+ } else if (widget) {
+ switch (QMacStyle::widgetSizePolicy(widget)) {
+ case QMacStyle::SizeSmall:
+ return QAquaSizeSmall;
+ case QMacStyle::SizeMini:
+ return QAquaSizeMini;
+ default:
+ break;
+ }
+ }
+ return QAquaSizeLarge;
+}
+
+
+static inline bool isTreeView(const QWidget *widget)
+{
+ return (widget && widget->parentWidget() &&
+ (qobject_cast<const QTreeView *>(widget->parentWidget())
+#ifdef QT3_SUPPORT
+ || widget->parentWidget()->inherits("Q3ListView")
+#endif
+ ));
+}
+
+QString qt_mac_removeMnemonics(const QString &original)
+{
+ // copied from qt_format_text (to be bug-for-bug compatible).
+ QString returnText(original.size(), 0);
+ int finalDest = 0;
+ int currPos = 0;
+ int l = original.length();
+ while (l) {
+ if (original.at(currPos) == QLatin1Char('&')) {
+ ++currPos;
+ --l;
+ if (l == 0)
+ break;
+ }
+ returnText[finalDest] = original.at(currPos);
+ ++currPos;
+ ++finalDest;
+ --l;
+ }
+ returnText.truncate(finalDest);
+ return returnText;
+}
+
+static inline ThemeTabDirection getTabDirection(QTabBar::Shape shape)
+{
+ ThemeTabDirection ttd;
+ switch (shape) {
+ case QTabBar::RoundedSouth:
+ case QTabBar::TriangularSouth:
+ ttd = kThemeTabSouth;
+ break;
+ default: // Added to remove the warning, since all values are taken care of, really!
+ case QTabBar::RoundedNorth:
+ case QTabBar::TriangularNorth:
+ ttd = kThemeTabNorth;
+ break;
+ case QTabBar::RoundedWest:
+ case QTabBar::TriangularWest:
+ ttd = kThemeTabWest;
+ break;
+ case QTabBar::RoundedEast:
+ case QTabBar::TriangularEast:
+ ttd = kThemeTabEast;
+ break;
+ }
+ return ttd;
+}
+
+class QMacStylePrivate : public QObject
+{
+ Q_OBJECT
+
+public:
+ QMacStylePrivate(QMacStyle *style);
+
+ // Stuff from QAquaAnimate:
+ bool addWidget(QWidget *);
+ void removeWidget(QWidget *);
+
+ enum Animates { AquaPushButton, AquaProgressBar, AquaListViewItemOpen };
+ bool animatable(Animates, const QWidget *) const;
+ void stopAnimate(Animates, QWidget *);
+ void startAnimate(Animates, QWidget *);
+ static ThemeDrawState getDrawState(QStyle::State flags);
+ QAquaWidgetSize aquaSizeConstrain(const QStyleOption *option, const QWidget *widg,
+ QStyle::ContentsType ct = QStyle::CT_CustomBase,
+ QSize szHint=QSize(-1, -1), QSize *insz = 0) const;
+ void getSliderInfo(QStyle::ComplexControl cc, const QStyleOptionSlider *slider,
+ HIThemeTrackDrawInfo *tdi, const QWidget *needToRemoveMe);
+ bool doAnimate(Animates);
+ inline int animateSpeed(Animates) const { return 33; }
+
+ // Utility functions
+ void drawColorlessButton(const HIRect &macRect, HIThemeButtonDrawInfo *bdi,
+ QPainter *p, const QStyleOption *opt) const;
+
+ void drawPantherTab(const QStyleOptionTab *tab, QPainter *p, const QWidget *w = 0) const;
+
+ QSize pushButtonSizeFromContents(const QStyleOptionButton *btn) const;
+
+ HIRect pushButtonContentBounds(const QStyleOptionButton *btn,
+ const HIThemeButtonDrawInfo *bdi) const;
+
+ void initComboboxBdi(const QStyleOptionComboBox *combo, HIThemeButtonDrawInfo *bdi,
+ const QWidget *widget, const ThemeDrawState &tds);
+
+ static HIRect comboboxInnerBounds(const HIRect &outerBounds, int buttonKind);
+
+ static QRect comboboxEditBounds(const QRect &outerBounds, const HIThemeButtonDrawInfo &bdi);
+
+ static void drawCombobox(const HIRect &outerBounds, const HIThemeButtonDrawInfo &bdi, QPainter *p);
+ static void drawTableHeader(const HIRect &outerBounds, bool drawTopBorder, bool drawLeftBorder,
+ const HIThemeButtonDrawInfo &bdi, QPainter *p);
+ bool contentFitsInPushButton(const QStyleOptionButton *btn, HIThemeButtonDrawInfo *bdi,
+ ThemeButtonKind buttonKindToCheck) const;
+ void initHIThemePushButton(const QStyleOptionButton *btn, const QWidget *widget,
+ const ThemeDrawState tds,
+ HIThemeButtonDrawInfo *bdi) const;
+ QPixmap generateBackgroundPattern() const;
+protected:
+ bool eventFilter(QObject *, QEvent *);
+ void timerEvent(QTimerEvent *);
+
+private slots:
+ void startAnimationTimer();
+
+public:
+ QPointer<QPushButton> defaultButton; //default push buttons
+ int timerID;
+ QList<QPointer<QWidget> > progressBars; //existing progress bars that need animation
+
+ struct ButtonState {
+ int frame;
+ enum { ButtonDark, ButtonLight } dir;
+ } buttonState;
+ UInt8 progressFrame;
+ QPointer<QFocusFrame> focusWidget;
+ CFAbsoluteTime defaultButtonStart;
+ QMacStyle *q;
+ bool mouseDown;
+};
+
+QT_BEGIN_INCLUDE_NAMESPACE
+#include "qmacstyle_mac.moc"
+QT_END_INCLUDE_NAMESPACE
+
+/*****************************************************************************
+ External functions
+ *****************************************************************************/
+extern CGContextRef qt_mac_cg_context(const QPaintDevice *); //qpaintdevice_mac.cpp
+extern QPixmap qt_mac_convert_iconref(const IconRef, int, int); //qpixmap_mac.cpp
+extern QRegion qt_mac_convert_mac_region(HIShapeRef); //qregion_mac.cpp
+void qt_mac_dispose_rgn(RgnHandle r); //qregion_mac.cpp
+extern QPaintDevice *qt_mac_safe_pdev; //qapplication_mac.cpp
+
+/*****************************************************************************
+ QMacCGStyle globals
+ *****************************************************************************/
+const int qt_mac_hitheme_version = 0; //the HITheme version we speak
+const int macSpinBoxSep = 5; // distance between spinwidget and the lineedit
+const int macItemFrame = 2; // menu item frame width
+const int macItemHMargin = 3; // menu item hor text margin
+const int macItemVMargin = 2; // menu item ver text margin
+const int macRightBorder = 12; // right border on mac
+const ThemeWindowType QtWinType = kThemeDocumentWindow; // Window type we use for QTitleBar.
+QPixmap *qt_mac_backgroundPattern = 0; // stores the standard widget background.
+
+/*****************************************************************************
+ QMacCGStyle utility functions
+ *****************************************************************************/
+static inline int qt_mac_hitheme_tab_version()
+{
+#if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4)
+ if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_4)
+ return 1;
+#endif
+ return 0;
+}
+
+static inline HIRect qt_hirectForQRect(const QRect &convertRect, const QRect &rect = QRect())
+{
+ return CGRectMake(convertRect.x() + rect.x(), convertRect.y() + rect.y(),
+ convertRect.width() - rect.width(), convertRect.height() - rect.height());
+}
+
+static inline const QRect qt_qrectForHIRect(const HIRect &hirect)
+{
+ return QRect(QPoint(int(hirect.origin.x), int(hirect.origin.y)),
+ QSize(int(hirect.size.width), int(hirect.size.height)));
+}
+
+inline bool qt_mac_is_metal(const QWidget *w)
+{
+ for (; w; w = w->parentWidget()) {
+ if (w->testAttribute(Qt::WA_MacBrushedMetal))
+ return true;
+ if (w->isWindow() && w->testAttribute(Qt::WA_WState_Created)) { // If not created will fall through to the opaque check and be fine anyway.
+ return macWindowIsTextured(qt_mac_window_for(w));
+ }
+ if (w->d_func()->isOpaque)
+ break;
+ }
+ return false;
+}
+
+static int qt_mac_aqua_get_metric(ThemeMetric met)
+{
+ SInt32 ret;
+ GetThemeMetric(met, &ret);
+ return ret;
+}
+
+static QSize qt_aqua_get_known_size(QStyle::ContentsType ct, const QWidget *widg, QSize szHint,
+ QAquaWidgetSize sz)
+{
+ QSize ret(-1, -1);
+ if (sz != QAquaSizeSmall && sz != QAquaSizeLarge && sz != QAquaSizeMini) {
+ qDebug("Not sure how to return this...");
+ return ret;
+ }
+ if ((widg && widg->testAttribute(Qt::WA_SetFont)) || !QApplication::desktopSettingsAware()) {
+ // If you're using a custom font and it's bigger than the default font,
+ // then no constraints for you. If you are smaller, we can try to help you out
+ QFont font = qt_app_fonts_hash()->value(widg->metaObject()->className(), QFont());
+ if (widg->font().pointSize() > font.pointSize())
+ return ret;
+ }
+
+ if (ct == QStyle::CT_CustomBase && widg) {
+ if (qobject_cast<const QPushButton *>(widg))
+ ct = QStyle::CT_PushButton;
+ else if (qobject_cast<const QRadioButton *>(widg))
+ ct = QStyle::CT_RadioButton;
+ else if (qobject_cast<const QCheckBox *>(widg))
+ ct = QStyle::CT_CheckBox;
+ else if (qobject_cast<const QComboBox *>(widg))
+ ct = QStyle::CT_ComboBox;
+ else if (qobject_cast<const QToolButton *>(widg))
+ ct = QStyle::CT_ToolButton;
+ else if (qobject_cast<const QSlider *>(widg))
+ ct = QStyle::CT_Slider;
+ else if (qobject_cast<const QProgressBar *>(widg))
+ ct = QStyle::CT_ProgressBar;
+ else if (qobject_cast<const QLineEdit *>(widg))
+ ct = QStyle::CT_LineEdit;
+ else if (qobject_cast<const QHeaderView *>(widg)
+#ifdef QT3_SUPPORT
+ || widg->inherits("Q3Header")
+#endif
+ )
+ ct = QStyle::CT_HeaderSection;
+ else if (qobject_cast<const QMenuBar *>(widg)
+#ifdef QT3_SUPPORT
+ || widg->inherits("Q3MenuBar")
+#endif
+ )
+ ct = QStyle::CT_MenuBar;
+ else if (qobject_cast<const QSizeGrip *>(widg))
+ ct = QStyle::CT_SizeGrip;
+ else
+ return ret;
+ }
+
+ switch (ct) {
+ case QStyle::CT_PushButton: {
+ const QPushButton *psh = static_cast<const QPushButton *>(widg);
+ QString buttonText = qt_mac_removeMnemonics(psh->text());
+ if (buttonText.contains(QLatin1Char('\n')))
+ ret = QSize(-1, -1);
+ else if (sz == QAquaSizeLarge)
+ ret = QSize(-1, qt_mac_aqua_get_metric(kThemeMetricPushButtonHeight));
+ else if (sz == QAquaSizeSmall)
+ ret = QSize(-1, qt_mac_aqua_get_metric(kThemeMetricSmallPushButtonHeight));
+ else if (sz == QAquaSizeMini)
+ ret = QSize(-1, qt_mac_aqua_get_metric(kThemeMetricMiniPushButtonHeight));
+
+ if (!psh->icon().isNull()){
+ // If the button got an icon, and the icon is larger than the
+ // button, we can't decide on a default size
+ ret.setWidth(-1);
+ if (ret.height() < psh->iconSize().height())
+ ret.setHeight(-1);
+ }
+ else if (buttonText == QLatin1String("OK") || buttonText == QLatin1String("Cancel")){
+ // Aqua Style guidelines restrict the size of OK and Cancel buttons to 68 pixels.
+ // However, this doesn't work for German, therefore only do it for English,
+ // I suppose it would be better to do some sort of lookups for languages
+ // that like to have really long words.
+ ret.setWidth(77 - 8);
+ }
+
+#if 0 //Not sure we are applying the rules correctly for RadioButtons/CheckBoxes --Sam
+ } else if (ct == QStyle::CT_RadioButton) {
+ QRadioButton *rdo = static_cast<QRadioButton *>(widg);
+ // Exception for case where multiline radio button text requires no size constrainment
+ if (rdo->text().find('\n') != -1)
+ return ret;
+ if (sz == QAquaSizeLarge)
+ ret = QSize(-1, qt_mac_aqua_get_metric(kThemeMetricRadioButtonHeight));
+ else if (sz == QAquaSizeSmall)
+ ret = QSize(-1, qt_mac_aqua_get_metric(kThemeMetricSmallRadioButtonHeight));
+ else if (sz == QAquaSizeMini)
+ ret = QSize(-1, qt_mac_aqua_get_metric(kThemeMetricMiniRadioButtonHeight));
+ } else if (ct == QStyle::CT_CheckBox) {
+ if (sz == QAquaSizeLarge)
+ ret = QSize(-1, qt_mac_aqua_get_metric(kThemeMetricCheckBoxHeight));
+ else if (sz == QAquaSizeSmall)
+ ret = QSize(-1, qt_mac_aqua_get_metric(kThemeMetricSmallCheckBoxHeight));
+ else if (sz == QAquaSizeMini)
+ ret = QSize(-1, qt_mac_aqua_get_metric(kThemeMetricMiniCheckBoxHeight));
+#endif
+ break;
+ }
+ case QStyle::CT_SizeGrip:
+ if (sz == QAquaSizeLarge || sz == QAquaSizeSmall) {
+ HIRect r;
+ HIPoint p = { 0, 0 };
+ HIThemeGrowBoxDrawInfo gbi;
+ gbi.version = 0;
+ gbi.state = kThemeStateActive;
+ gbi.kind = kHIThemeGrowBoxKindNormal;
+ gbi.direction = QApplication::isRightToLeft() ? kThemeGrowLeft | kThemeGrowDown
+ : kThemeGrowRight | kThemeGrowDown;
+ gbi.size = sz == QAquaSizeSmall ? kHIThemeGrowBoxSizeSmall : kHIThemeGrowBoxSizeNormal;
+ if (HIThemeGetGrowBoxBounds(&p, &gbi, &r) == noErr)
+ ret = QSize(r.size.width, r.size.height);
+ }
+ break;
+ case QStyle::CT_ComboBox:
+ switch (sz) {
+ case QAquaSizeLarge:
+ ret = QSize(-1, qt_mac_aqua_get_metric(kThemeMetricPopupButtonHeight));
+ break;
+ case QAquaSizeSmall:
+ ret = QSize(-1, qt_mac_aqua_get_metric(kThemeMetricSmallPopupButtonHeight));
+ break;
+ case QAquaSizeMini:
+ ret = QSize(-1, qt_mac_aqua_get_metric(kThemeMetricMiniPopupButtonHeight));
+ break;
+ default:
+ break;
+ }
+ break;
+ case QStyle::CT_ToolButton:
+ if (sz == QAquaSizeSmall) {
+ int width = 0, height = 0;
+ if (szHint == QSize(-1, -1)) { //just 'guess'..
+ const QToolButton *bt = static_cast<const QToolButton *>(widg);
+ if (!bt->icon().isNull()) {
+ QSize iconSize = bt->iconSize();
+ QSize pmSize = bt->icon().actualSize(QSize(32, 32), QIcon::Normal);
+ width = qMax(width, qMax(iconSize.width(), pmSize.width()));
+ height = qMax(height, qMax(iconSize.height(), pmSize.height()));
+ }
+ if (!bt->text().isNull() && bt->toolButtonStyle() != Qt::ToolButtonIconOnly) {
+ int text_width = bt->fontMetrics().width(bt->text()),
+ text_height = bt->fontMetrics().height();
+ if (bt->toolButtonStyle() == Qt::ToolButtonTextUnderIcon) {
+ width = qMax(width, text_width);
+ height += text_height;
+ } else {
+ width += text_width;
+ width = qMax(height, text_height);
+ }
+ }
+ } else {
+ width = szHint.width();
+ height = szHint.height();
+ }
+ width = qMax(20, width + 5); //border
+ height = qMax(20, height + 5); //border
+ ret = QSize(width, height);
+ }
+ break;
+ case QStyle::CT_Slider: {
+ int w = -1;
+ const QSlider *sld = static_cast<const QSlider *>(widg);
+ if (sz == QAquaSizeLarge) {
+ if (sld->orientation() == Qt::Horizontal) {
+ w = qt_mac_aqua_get_metric(kThemeMetricHSliderHeight);
+ if (sld->tickPosition() != QSlider::NoTicks)
+ w += qt_mac_aqua_get_metric(kThemeMetricHSliderTickHeight);
+ } else {
+ w = qt_mac_aqua_get_metric(kThemeMetricVSliderWidth);
+ if (sld->tickPosition() != QSlider::NoTicks)
+ w += qt_mac_aqua_get_metric(kThemeMetricVSliderTickWidth);
+ }
+ } else if (sz == QAquaSizeSmall) {
+ if (sld->orientation() == Qt::Horizontal) {
+ w = qt_mac_aqua_get_metric(kThemeMetricSmallHSliderHeight);
+ if (sld->tickPosition() != QSlider::NoTicks)
+ w += qt_mac_aqua_get_metric(kThemeMetricSmallHSliderTickHeight);
+ } else {
+ w = qt_mac_aqua_get_metric(kThemeMetricSmallVSliderWidth);
+ if (sld->tickPosition() != QSlider::NoTicks)
+ w += qt_mac_aqua_get_metric(kThemeMetricSmallVSliderTickWidth);
+ }
+ } else if (sz == QAquaSizeMini) {
+ if (sld->orientation() == Qt::Horizontal) {
+ w = qt_mac_aqua_get_metric(kThemeMetricMiniHSliderHeight);
+ if (sld->tickPosition() != QSlider::NoTicks)
+ w += qt_mac_aqua_get_metric(kThemeMetricMiniHSliderTickHeight);
+ } else {
+ w = qt_mac_aqua_get_metric(kThemeMetricMiniVSliderWidth);
+ if (sld->tickPosition() != QSlider::NoTicks)
+ w += qt_mac_aqua_get_metric(kThemeMetricMiniVSliderTickWidth);
+ }
+ }
+ if (sld->orientation() == Qt::Horizontal)
+ ret.setHeight(w);
+ else
+ ret.setWidth(w);
+ break;
+ }
+ case QStyle::CT_ProgressBar: {
+ int finalValue = -1;
+ Qt::Orientation orient = Qt::Horizontal;
+ if (const QProgressBar *pb = qobject_cast<const QProgressBar *>(widg))
+ orient = pb->orientation();
+
+ if (sz == QAquaSizeLarge)
+ finalValue = qt_mac_aqua_get_metric(kThemeMetricLargeProgressBarThickness)
+ + qt_mac_aqua_get_metric(kThemeMetricProgressBarShadowOutset);
+ else
+ finalValue = qt_mac_aqua_get_metric(kThemeMetricNormalProgressBarThickness)
+ + qt_mac_aqua_get_metric(kThemeMetricSmallProgressBarShadowOutset);
+ if (orient == Qt::Horizontal)
+ ret.setHeight(finalValue);
+ else
+ ret.setWidth(finalValue);
+ break;
+ }
+ case QStyle::CT_LineEdit:
+ if (!widg || !qobject_cast<QComboBox *>(widg->parentWidget())) {
+ //should I take into account the font dimentions of the lineedit? -Sam
+ if (sz == QAquaSizeLarge)
+ ret = QSize(-1, 22);
+ else
+ ret = QSize(-1, 19);
+ }
+ break;
+ case QStyle::CT_HeaderSection:
+ if (sz == QAquaSizeLarge && isTreeView(widg))
+ ret = QSize(-1, qt_mac_aqua_get_metric(kThemeMetricListHeaderHeight));
+ break;
+ case QStyle::CT_MenuBar:
+ if (sz == QAquaSizeLarge) {
+#ifndef QT_MAC_USE_COCOA
+ SInt16 size;
+ if (!GetThemeMenuBarHeight(&size))
+ ret = QSize(-1, size);
+#else
+ ret = QSize(-1, [[NSApp mainMenu] menuBarHeight]);
+ // In the qt_mac_set_native_menubar(false) case,
+ // we come it here with a zero-height main menu,
+ // preventing the in-window menu from displaying.
+ // Use 22 pixels for the height, by observation.
+ if (ret.height() <= 0)
+ ret.setHeight(22);
+#endif
+ }
+ break;
+ default:
+ break;
+ }
+ return ret;
+}
+
+
+#if defined(QMAC_QAQUASTYLE_SIZE_CONSTRAIN) || defined(DEBUG_SIZE_CONSTRAINT)
+static QAquaWidgetSize qt_aqua_guess_size(const QWidget *widg, QSize large, QSize small, QSize mini)
+{
+ if (large == QSize(-1, -1)) {
+ if (small != QSize(-1, -1))
+ return QAquaSizeSmall;
+ if (mini != QSize(-1, -1))
+ return QAquaSizeMini;
+ return QAquaSizeUnknown;
+ } else if (small == QSize(-1, -1)) {
+ if (mini != QSize(-1, -1))
+ return QAquaSizeMini;
+ return QAquaSizeLarge;
+ } else if (mini == QSize(-1, -1)) {
+ return QAquaSizeLarge;
+ }
+
+#ifndef QT_NO_MAINWINDOW
+ if (qobject_cast<QDockWidget *>(widg->window()) || !qgetenv("QWIDGET_ALL_SMALL").isNull()) {
+ //if (small.width() != -1 || small.height() != -1)
+ return QAquaSizeSmall;
+ } else if (!qgetenv("QWIDGET_ALL_MINI").isNull()) {
+ return QAquaSizeMini;
+ }
+#endif
+
+#if 0
+ /* Figure out which size we're closer to, I just hacked this in, I haven't
+ tested it as it would probably look pretty strange to have some widgets
+ big and some widgets small in the same window?? -Sam */
+ int large_delta=0;
+ if (large.width() != -1) {
+ int delta = large.width() - widg->width();
+ large_delta += delta * delta;
+ }
+ if (large.height() != -1) {
+ int delta = large.height() - widg->height();
+ large_delta += delta * delta;
+ }
+ int small_delta=0;
+ if (small.width() != -1) {
+ int delta = small.width() - widg->width();
+ small_delta += delta * delta;
+ }
+ if (small.height() != -1) {
+ int delta = small.height() - widg->height();
+ small_delta += delta * delta;
+ }
+ int mini_delta=0;
+ if (mini.width() != -1) {
+ int delta = mini.width() - widg->width();
+ mini_delta += delta * delta;
+ }
+ if (mini.height() != -1) {
+ int delta = mini.height() - widg->height();
+ mini_delta += delta * delta;
+ }
+ if (mini_delta < small_delta && mini_delta < large_delta)
+ return QAquaSizeMini;
+ else if (small_delta < large_delta)
+ return QAquaSizeSmall;
+#endif
+ return QAquaSizeLarge;
+}
+#endif
+
+QAquaWidgetSize QMacStylePrivate::aquaSizeConstrain(const QStyleOption *option, const QWidget *widg,
+ QStyle::ContentsType ct, QSize szHint, QSize *insz) const
+{
+#if defined(QMAC_QAQUASTYLE_SIZE_CONSTRAIN) || defined(DEBUG_SIZE_CONSTRAINT)
+ if (option) {
+ if (option->state & QStyle::State_Small)
+ return QAquaSizeSmall;
+ if (option->state & QStyle::State_Mini)
+ return QAquaSizeMini;
+ }
+
+ if (!widg) {
+ if (insz)
+ *insz = QSize();
+ if (!qgetenv("QWIDGET_ALL_SMALL").isNull())
+ return QAquaSizeSmall;
+ if (!qgetenv("QWIDGET_ALL_MINI").isNull())
+ return QAquaSizeMini;
+ return QAquaSizeUnknown;
+ }
+ QSize large = qt_aqua_get_known_size(ct, widg, szHint, QAquaSizeLarge),
+ small = qt_aqua_get_known_size(ct, widg, szHint, QAquaSizeSmall),
+ mini = qt_aqua_get_known_size(ct, widg, szHint, QAquaSizeMini);
+ bool guess_size = false;
+ QAquaWidgetSize ret = QAquaSizeUnknown;
+ QMacStyle::WidgetSizePolicy wsp = q->widgetSizePolicy(widg);
+ if (wsp == QMacStyle::SizeDefault)
+ guess_size = true;
+ else if (wsp == QMacStyle::SizeMini)
+ ret = QAquaSizeMini;
+ else if (wsp == QMacStyle::SizeSmall)
+ ret = QAquaSizeSmall;
+ else if (wsp == QMacStyle::SizeLarge)
+ ret = QAquaSizeLarge;
+ if (guess_size)
+ ret = qt_aqua_guess_size(widg, large, small, mini);
+
+ QSize *sz = 0;
+ if (ret == QAquaSizeSmall)
+ sz = &small;
+ else if (ret == QAquaSizeLarge)
+ sz = &large;
+ else if (ret == QAquaSizeMini)
+ sz = &mini;
+ if (insz)
+ *insz = sz ? *sz : QSize(-1, -1);
+#ifdef DEBUG_SIZE_CONSTRAINT
+ if (sz) {
+ const char *size_desc = "Unknown";
+ if (sz == &small)
+ size_desc = "Small";
+ else if (sz == &large)
+ size_desc = "Large";
+ else if (sz == &mini)
+ size_desc = "Mini";
+ qDebug("%s - %s: %s taken (%d, %d) [%d, %d]",
+ widg ? widg->objectName().toLatin1().constData() : "*Unknown*",
+ widg ? widg->metaObject()->className() : "*Unknown*", size_desc, widg->width(), widg->height(),
+ sz->width(), sz->height());
+ }
+#endif
+ return ret;
+#else
+ if (insz)
+ *insz = QSize();
+ Q_UNUSED(widg);
+ Q_UNUSED(ct);
+ Q_UNUSED(szHint);
+ return QAquaSizeUnknown;
+#endif
+}
+
+/**
+ Returns the free space awailable for contents inside the
+ button (and not the size of the contents itself)
+*/
+HIRect QMacStylePrivate::pushButtonContentBounds(const QStyleOptionButton *btn,
+ const HIThemeButtonDrawInfo *bdi) const
+{
+ HIRect outerBounds = qt_hirectForQRect(btn->rect);
+ // Adjust the bounds to correct for
+ // carbon not calculating the content bounds fully correct
+ if (bdi->kind == kThemePushButton || bdi->kind == kThemePushButtonSmall){
+ outerBounds.origin.y += PushButtonTopOffset;
+ outerBounds.size.height -= PushButtonBottomOffset;
+ } else if (bdi->kind == kThemePushButtonMini) {
+ outerBounds.origin.y += PushButtonTopOffset;
+ }
+
+ HIRect contentBounds;
+ HIThemeGetButtonContentBounds(&outerBounds, bdi, &contentBounds);
+ return contentBounds;
+}
+
+/**
+ Calculates the size of the button contents.
+ This includes both the text and the icon.
+*/
+QSize QMacStylePrivate::pushButtonSizeFromContents(const QStyleOptionButton *btn) const
+{
+ QSize csz(0, 0);
+ QSize iconSize = btn->icon.isNull() ? QSize(0, 0)
+ : (btn->iconSize + QSize(PushButtonContentPadding, 0));
+ QRect textRect = btn->text.isEmpty() ? QRect(0, 0, 1, 1)
+ : btn->fontMetrics.boundingRect(QRect(), Qt::AlignCenter, btn->text);
+ csz.setWidth(iconSize.width() + textRect.width()
+ + ((btn->features & QStyleOptionButton::HasMenu)
+ ? q->pixelMetric(QStyle::PM_MenuButtonIndicator, btn, 0) : 0));
+ csz.setHeight(qMax(iconSize.height(), textRect.height()));
+ return csz;
+}
+
+/**
+ Checks if the actual contents of btn fits inside the free content bounds of
+ 'buttonKindToCheck'. Meant as a helper function for 'initHIThemePushButton'
+ for determining which button kind to use for drawing.
+*/
+bool QMacStylePrivate::contentFitsInPushButton(const QStyleOptionButton *btn,
+ HIThemeButtonDrawInfo *bdi,
+ ThemeButtonKind buttonKindToCheck) const
+{
+ ThemeButtonKind tmp = bdi->kind;
+ bdi->kind = buttonKindToCheck;
+ QSize contentSize = pushButtonSizeFromContents(btn);
+ QRect freeContentRect = qt_qrectForHIRect(pushButtonContentBounds(btn, bdi));
+ bdi->kind = tmp;
+ return freeContentRect.contains(QRect(freeContentRect.x(), freeContentRect.y(),
+ contentSize.width(), contentSize.height()));
+}
+
+/**
+ Creates a HIThemeButtonDrawInfo structure that specifies the correct button
+ kind and other details to use for drawing the given push button. Which
+ button kind depends on the size of the button, the size of the contents,
+ explicit user style settings, etc.
+*/
+void QMacStylePrivate::initHIThemePushButton(const QStyleOptionButton *btn,
+ const QWidget *widget,
+ const ThemeDrawState tds,
+ HIThemeButtonDrawInfo *bdi) const
+{
+ bool drawColorless = btn->palette.currentColorGroup() == QPalette::Active;
+ ThemeDrawState tdsModified = tds;
+ if (btn->state & QStyle::State_On)
+ tdsModified = kThemeStatePressed;
+ bdi->version = qt_mac_hitheme_version;
+ bdi->state = tdsModified;
+ bdi->value = kThemeButtonOff;
+
+ if (drawColorless && tdsModified == kThemeStateInactive)
+ bdi->state = kThemeStateActive;
+ if (btn->state & QStyle::State_HasFocus)
+ bdi->adornment = kThemeAdornmentFocus;
+ else
+ bdi->adornment = kThemeAdornmentNone;
+
+
+ if (btn->features & (QStyleOptionButton::Flat)) {
+ bdi->kind = kThemeBevelButton;
+ } else {
+ switch (aquaSizeConstrain(btn, widget)) {
+ case QAquaSizeSmall:
+ bdi->kind = kThemePushButtonSmall;
+ break;
+ case QAquaSizeMini:
+ bdi->kind = kThemePushButtonMini;
+ break;
+ case QAquaSizeLarge:
+ // ... We should honor if the user is explicit about using the
+ // large button. But right now Qt will specify the large button
+ // as default rather than QAquaSizeUnknown.
+ // So we treat it like QAquaSizeUnknown
+ // to get the dynamic choosing of button kind.
+ case QAquaSizeUnknown:
+ // Choose the button kind that closest match the button rect, but at the
+ // same time displays the button contents without clipping.
+ bdi->kind = kThemeBevelButton;
+ if (btn->rect.width() >= BevelButtonW && btn->rect.height() >= BevelButtonH){
+ if (widget && widget->testAttribute(Qt::WA_MacVariableSize)) {
+ if (btn->rect.height() <= MiniButtonH){
+ if (contentFitsInPushButton(btn, bdi, kThemePushButtonMini))
+ bdi->kind = kThemePushButtonMini;
+ } else if (btn->rect.height() <= SmallButtonH){
+ if (contentFitsInPushButton(btn, bdi, kThemePushButtonSmall))
+ bdi->kind = kThemePushButtonSmall;
+ } else if (contentFitsInPushButton(btn, bdi, kThemePushButton)) {
+ bdi->kind = kThemePushButton;
+ }
+ } else {
+ bdi->kind = kThemePushButton;
+ }
+ }
+ }
+ }
+}
+
+/**
+ Creates a HIThemeButtonDrawInfo structure that specifies the correct button
+ kind and other details to use for drawing the given combobox. Which button
+ kind depends on the size of the combo, wether or not it is editable,
+ explicit user style settings, etc.
+*/
+void QMacStylePrivate::initComboboxBdi(const QStyleOptionComboBox *combo, HIThemeButtonDrawInfo *bdi,
+ const QWidget *widget, const ThemeDrawState &tds)
+{
+ bdi->version = qt_mac_hitheme_version;
+ bdi->adornment = kThemeAdornmentArrowLeftArrow;
+ bdi->value = kThemeButtonOff;
+ if (combo->state & QStyle::State_HasFocus)
+ bdi->adornment = kThemeAdornmentFocus;
+ bool drawColorless = combo->palette.currentColorGroup() == QPalette::Active && tds == kThemeStateInactive;
+ if (combo->activeSubControls & QStyle::SC_ComboBoxArrow)
+ bdi->state = kThemeStatePressed;
+ else if (drawColorless)
+ bdi->state = kThemeStateActive;
+ else
+ bdi->state = tds;
+
+ QAquaWidgetSize aSize = aquaSizeConstrain(combo, widget);
+ switch (aSize) {
+ case QAquaSizeMini:
+ bdi->kind = combo->editable ? ThemeButtonKind(kThemeComboBoxMini)
+ : ThemeButtonKind(kThemePopupButtonMini);
+ break;
+ case QAquaSizeSmall:
+ bdi->kind = combo->editable ? ThemeButtonKind(kThemeComboBoxSmall)
+ : ThemeButtonKind(kThemePopupButtonSmall);
+ break;
+ case QAquaSizeUnknown:
+ case QAquaSizeLarge:
+ // Unless the user explicitly specified large buttons, determine the
+ // kind by looking at the combox size.
+ // ... specifying small and mini-buttons it not a current feature of
+ // Qt (e.g. QWidget::getAttribute(WA_ButtonSize)). But when it is, add
+ // an extra check here before using the mini and small buttons.
+ int h = combo->rect.size().height();
+ if (combo->editable){
+ if (h < 21)
+ bdi->kind = kThemeComboBoxMini;
+ else if (h < 26)
+ bdi->kind = kThemeComboBoxSmall;
+ else
+ bdi->kind = kThemeComboBox;
+ } else {
+ // Even if we specify that we want the kThemePopupButton, Carbon
+ // will use the kThemePopupButtonSmall if the size matches. So we
+ // do the same size check explicit to have the size of the inner
+ // text field be correct. Therefore, do this even if the user specifies
+ // the use of LargeButtons explicit.
+ if (h < 21)
+ bdi->kind = kThemePopupButtonMini;
+ else if (h < 26)
+ bdi->kind = kThemePopupButtonSmall;
+ else
+ bdi->kind = kThemePopupButton;
+ }
+ break;
+ }
+}
+
+/**
+ Carbon draws comboboxes (and other views) outside the rect given as argument. Use this function to obtain
+ the corresponding inner rect for drawing the same combobox so that it stays inside the given outerBounds.
+*/
+HIRect QMacStylePrivate::comboboxInnerBounds(const HIRect &outerBounds, int buttonKind)
+{
+ HIRect innerBounds = outerBounds;
+ // Carbon draw parts of the view outside the rect.
+ // So make the rect a bit smaller to compensate
+ // (I wish HIThemeGetButtonBackgroundBounds worked)
+ switch (buttonKind){
+ case kThemePopupButton:
+ innerBounds.origin.x += 2;
+ innerBounds.origin.y += 3;
+ innerBounds.size.width -= 5;
+ innerBounds.size.height -= 6;
+ break;
+ case kThemePopupButtonSmall:
+ innerBounds.origin.x += 3;
+ innerBounds.origin.y += 3;
+ innerBounds.size.width -= 6;
+ innerBounds.size.height -= 7;
+ break;
+ case kThemePopupButtonMini:
+ innerBounds.origin.x += 2;
+ innerBounds.origin.y += 2;
+ innerBounds.size.width -= 5;
+ innerBounds.size.height -= 6;
+ break;
+ case kThemeComboBox:
+ innerBounds.origin.x += 3;
+ innerBounds.origin.y += 3;
+ innerBounds.size.width -= 6;
+ innerBounds.size.height -= 6;
+ break;
+ case kThemeComboBoxSmall:
+ innerBounds.origin.x += 3;
+ innerBounds.origin.y += 3;
+ innerBounds.size.width -= 7;
+ innerBounds.size.height -= 8;
+ break;
+ case kThemeComboBoxMini:
+ innerBounds.origin.x += 3;
+ innerBounds.origin.y += 3;
+ innerBounds.size.width -= 4;
+ innerBounds.size.height -= 8;
+ break;
+ default:
+ break;
+ }
+ return innerBounds;
+}
+
+/**
+ Inside a combobox Qt places a line edit widget. The size of this widget should depend on the kind
+ of combobox we choose to draw. This function calculates and returns this size.
+*/
+QRect QMacStylePrivate::comboboxEditBounds(const QRect &outerBounds, const HIThemeButtonDrawInfo &bdi)
+{
+ QRect ret = outerBounds;
+ switch (bdi.kind){
+ case kThemeComboBox:
+ ret.adjust(5, 8, -21, -4);
+ break;
+ case kThemeComboBoxSmall:
+ ret.adjust(4, 5, -18, 0);
+ ret.setHeight(16);
+ break;
+ case kThemeComboBoxMini:
+ ret.adjust(4, 5, -16, 0);
+ ret.setHeight(13);
+ break;
+ case kThemePopupButton:
+ ret.adjust(10, 3, -23, -3);
+ break;
+ case kThemePopupButtonSmall:
+ ret.adjust(9, 3, -20, -3);
+ break;
+ case kThemePopupButtonMini:
+ ret.adjust(8, 3, -19, 0);
+ ret.setHeight(13);
+ break;
+ }
+ return ret;
+}
+
+/**
+ Carbon comboboxes don't scale (sight). If the size of the combo suggest a scaled version,
+ create it manually by drawing a small Carbon combo onto a pixmap (use pixmap cache), chop
+ it up, and copy it back onto the widget. Othervise, draw the combobox supplied by Carbon directly.
+*/
+void QMacStylePrivate::drawCombobox(const HIRect &outerBounds, const HIThemeButtonDrawInfo &bdi, QPainter *p)
+{
+ if (!(bdi.kind == kThemeComboBox && outerBounds.size.height > 28)){
+ // We have an unscaled combobox, or popup-button; use Carbon directly.
+ HIRect innerBounds = QMacStylePrivate::comboboxInnerBounds(outerBounds, bdi.kind);
+ HIThemeDrawButton(&innerBounds, &bdi, QMacCGContext(p), kHIThemeOrientationNormal, 0);
+ } else {
+ QPixmap buffer;
+ QString key = QString(QLatin1String("$qt_cbox%1-%2")).arg(int(bdi.state)).arg(int(bdi.adornment));
+ if (!QPixmapCache::find(key, buffer)) {
+ HIRect innerBoundsSmallCombo = {{3, 3}, {29, 25}};
+ buffer = QPixmap(35, 28);
+ buffer.fill(Qt::transparent);
+ QPainter buffPainter(&buffer);
+ HIThemeDrawButton(&innerBoundsSmallCombo, &bdi, QMacCGContext(&buffPainter), kHIThemeOrientationNormal, 0);
+ buffPainter.end();
+ QPixmapCache::insert(key, buffer);
+ }
+
+ const int bwidth = 20;
+ const int fwidth = 10;
+ const int fheight = 10;
+ int w = qRound(outerBounds.size.width);
+ int h = qRound(outerBounds.size.height);
+ int bstart = w - bwidth;
+ int blower = fheight + 1;
+ int flower = h - fheight;
+ int sheight = flower - fheight;
+ int center = qRound(outerBounds.size.height + outerBounds.origin.y) / 2;
+
+ // Draw upper and lower gap
+ p->drawPixmap(fwidth, 0, bstart - fwidth, fheight, buffer, fwidth, 0, 1, fheight);
+ p->drawPixmap(fwidth, flower, bstart - fwidth, fheight, buffer, fwidth, buffer.height() - fheight, 1, fheight);
+ // Draw left and right gap. Right gap is drawn top and bottom separatly
+ p->drawPixmap(0, fheight, fwidth, sheight, buffer, 0, fheight, fwidth, 1);
+ p->drawPixmap(bstart, fheight, bwidth, center - fheight, buffer, buffer.width() - bwidth, fheight - 1, bwidth, 1);
+ p->drawPixmap(bstart, center, bwidth, sheight / 2, buffer, buffer.width() - bwidth, fheight + 6, bwidth, 1);
+ // Draw arrow
+ p->drawPixmap(bstart, center - 4, bwidth - 3, 6, buffer, buffer.width() - bwidth, fheight, bwidth - 3, 6);
+ // Draw corners
+ p->drawPixmap(0, 0, fwidth, fheight, buffer, 0, 0, fwidth, fheight);
+ p->drawPixmap(bstart, 0, bwidth, fheight, buffer, buffer.width() - bwidth, 0, bwidth, fheight);
+ p->drawPixmap(0, flower, fwidth, fheight, buffer, 0, buffer.height() - fheight, fwidth, fheight);
+ p->drawPixmap(bstart, h - blower, bwidth, blower, buffer, buffer.width() - bwidth, buffer.height() - blower, bwidth, blower);
+ }
+}
+
+/**
+ Carbon tableheaders don't scale (sight). So create it manually by drawing a small Carbon header
+ onto a pixmap (use pixmap cache), chop it up, and copy it back onto the widget.
+*/
+void QMacStylePrivate::drawTableHeader(const HIRect &outerBounds,
+ bool drawTopBorder, bool drawLeftBorder, const HIThemeButtonDrawInfo &bdi, QPainter *p)
+{
+ static SInt32 headerHeight = 0;
+ static OSStatus err = GetThemeMetric(kThemeMetricListHeaderHeight, &headerHeight);
+ Q_UNUSED(err);
+
+ QPixmap buffer;
+ QString key = QString(QLatin1String("$qt_tableh%1-%2-%3")).arg(int(bdi.state)).arg(int(bdi.adornment)).arg(int(bdi.value));
+ if (!QPixmapCache::find(key, buffer)) {
+ HIRect headerNormalRect = {{0., 0.}, {16., CGFloat(headerHeight)}};
+ buffer = QPixmap(headerNormalRect.size.width, headerNormalRect.size.height);
+ buffer.fill(Qt::transparent);
+ QPainter buffPainter(&buffer);
+ HIThemeDrawButton(&headerNormalRect, &bdi, QMacCGContext(&buffPainter), kHIThemeOrientationNormal, 0);
+ buffPainter.end();
+ QPixmapCache::insert(key, buffer);
+ }
+ const int buttonw = qRound(outerBounds.size.width);
+ const int buttonh = qRound(outerBounds.size.height);
+ const int framew = 1;
+ const int frameh_n = 4;
+ const int frameh_s = 3;
+ const int transh = buffer.height() - frameh_n - frameh_s;
+ int center = buttonh - frameh_s - int(transh / 2.0f) + 1; // Align bottom;
+
+ int skipTopBorder = 0;
+ if (!drawTopBorder)
+ skipTopBorder = 1;
+
+ p->translate(outerBounds.origin.x, outerBounds.origin.y);
+
+ p->drawPixmap(QRect(QRect(0, -skipTopBorder, buttonw - framew , frameh_n)), buffer, QRect(framew, 0, 1, frameh_n));
+ p->drawPixmap(QRect(0, buttonh - frameh_s, buttonw - framew, frameh_s), buffer, QRect(framew, buffer.height() - frameh_s, 1, frameh_s));
+ // Draw upper and lower center blocks
+ p->drawPixmap(QRect(0, frameh_n - skipTopBorder, buttonw - framew, center - frameh_n + skipTopBorder), buffer, QRect(framew, frameh_n, 1, 1));
+ p->drawPixmap(QRect(0, center, buttonw - framew, buttonh - center - frameh_s), buffer, QRect(framew, buffer.height() - frameh_s, 1, 1));
+ // Draw right center block borders
+ p->drawPixmap(QRect(buttonw - framew, frameh_n - skipTopBorder, framew, center - frameh_n), buffer, QRect(buffer.width() - framew, frameh_n, framew, 1));
+ p->drawPixmap(QRect(buttonw - framew, center, framew, buttonh - center - 1), buffer, QRect(buffer.width() - framew, buffer.height() - frameh_s, framew, 1));
+ // Draw right corners
+ p->drawPixmap(QRect(buttonw - framew, -skipTopBorder, framew, frameh_n), buffer, QRect(buffer.width() - framew, 0, framew, frameh_n));
+ p->drawPixmap(QRect(buttonw - framew, buttonh - frameh_s, framew, frameh_s), buffer, QRect(buffer.width() - framew, buffer.height() - frameh_s, framew, frameh_s));
+ // Draw center transition block
+ p->drawPixmap(QRect(0, center - qRound(transh / 2.0f), buttonw - framew, buffer.height() - frameh_n - frameh_s), buffer, QRect(framew, frameh_n + 1, 1, transh));
+ // Draw right center transition block border
+ p->drawPixmap(QRect(buttonw - framew, center - qRound(transh / 2.0f), framew, buffer.height() - frameh_n - frameh_s), buffer, QRect(buffer.width() - framew, frameh_n + 1, framew, transh));
+ if (drawLeftBorder){
+ // Draw left center block borders
+ p->drawPixmap(QRect(0, frameh_n - skipTopBorder, framew, center - frameh_n + skipTopBorder), buffer, QRect(0, frameh_n, framew, 1));
+ p->drawPixmap(QRect(0, center, framew, buttonh - center - 1), buffer, QRect(0, buffer.height() - frameh_s, framew, 1));
+ // Draw left corners
+ p->drawPixmap(QRect(0, -skipTopBorder, framew, frameh_n), buffer, QRect(0, 0, framew, frameh_n));
+ p->drawPixmap(QRect(0, buttonh - frameh_s, framew, frameh_s), buffer, QRect(0, buffer.height() - frameh_s, framew, frameh_s));
+ // Draw left center transition block border
+ p->drawPixmap(QRect(0, center - qRound(transh / 2.0f), framew, buffer.height() - frameh_n - frameh_s), buffer, QRect(0, frameh_n + 1, framew, transh));
+ }
+
+ p->translate(-outerBounds.origin.x, -outerBounds.origin.y);
+}
+
+/*
+ Returns cutoff sizes for scroll bars.
+ thumbIndicatorCutoff is the smallest size where the thumb indicator is drawn.
+ scrollButtonsCutoff is the smallest size where the up/down buttons is drawn.
+*/
+enum ScrollBarCutoffType { thumbIndicatorCutoff = 0, scrollButtonsCutoff = 1 };
+static int scrollButtonsCutoffSize(ScrollBarCutoffType cutoffType, QMacStyle::WidgetSizePolicy widgetSize)
+{
+ // Mini scroll bars do not exist as of version 10.4.
+ if (widgetSize == QMacStyle::SizeMini)
+ return 0;
+
+ const int sizeIndex = (widgetSize == QMacStyle::SizeSmall) ? 1 : 0;
+ static const int sizeTable[2][2] = { { 61, 56 }, { 49, 44 } };
+ return sizeTable[sizeIndex][cutoffType];
+}
+
+void QMacStylePrivate::getSliderInfo(QStyle::ComplexControl cc, const QStyleOptionSlider *slider,
+ HIThemeTrackDrawInfo *tdi, const QWidget *needToRemoveMe)
+{
+ memset(tdi, 0, sizeof(HIThemeTrackDrawInfo)); // We don't get it all for some reason or another...
+ tdi->version = qt_mac_hitheme_version;
+ tdi->reserved = 0;
+ tdi->filler1 = 0;
+ bool isScrollbar = (cc == QStyle::CC_ScrollBar);
+ switch (aquaSizeConstrain(0, needToRemoveMe)) {
+ case QAquaSizeUnknown:
+ case QAquaSizeLarge:
+ if (isScrollbar)
+ tdi->kind = kThemeMediumScrollBar;
+ else
+ tdi->kind = kThemeMediumSlider;
+ break;
+ case QAquaSizeMini:
+ if (isScrollbar)
+ tdi->kind = kThemeSmallScrollBar; // should be kThemeMiniScrollBar, but not implemented
+ else
+ tdi->kind = kThemeMiniSlider;
+ break;
+ case QAquaSizeSmall:
+ if (isScrollbar)
+ tdi->kind = kThemeSmallScrollBar;
+ else
+ tdi->kind = kThemeSmallSlider;
+ break;
+ }
+ tdi->bounds = qt_hirectForQRect(slider->rect);
+ tdi->min = slider->minimum;
+ tdi->max = slider->maximum;
+ tdi->value = slider->sliderPosition;
+ tdi->attributes = kThemeTrackShowThumb;
+ if (slider->upsideDown)
+ tdi->attributes |= kThemeTrackRightToLeft;
+ if (slider->orientation == Qt::Horizontal) {
+ tdi->attributes |= kThemeTrackHorizontal;
+ if (isScrollbar && slider->direction == Qt::RightToLeft) {
+ if (!slider->upsideDown)
+ tdi->attributes |= kThemeTrackRightToLeft;
+ else
+ tdi->attributes &= ~kThemeTrackRightToLeft;
+ }
+ }
+
+ // Tiger broke reverse scroll bars so put them back and "fake it"
+ if (isScrollbar && (tdi->attributes & kThemeTrackRightToLeft)
+ && QSysInfo::MacintoshVersion >= QSysInfo::MV_10_4) {
+ tdi->attributes &= ~kThemeTrackRightToLeft;
+ tdi->value = tdi->max - slider->sliderPosition;
+ }
+
+ tdi->enableState = (slider->state & QStyle::State_Enabled) ? kThemeTrackActive
+ : kThemeTrackDisabled;
+ if (!(slider->state & QStyle::State_Active))
+ tdi->enableState = kThemeTrackInactive;
+ if (!isScrollbar) {
+ if (slider->state & QStyle::QStyle::State_HasFocus)
+ tdi->attributes |= kThemeTrackHasFocus;
+ if (slider->tickPosition == QSlider::NoTicks || slider->tickPosition == QSlider::TicksBothSides)
+ tdi->trackInfo.slider.thumbDir = kThemeThumbPlain;
+ else if (slider->tickPosition == QSlider::TicksAbove)
+ tdi->trackInfo.slider.thumbDir = kThemeThumbUpward;
+ else
+ tdi->trackInfo.slider.thumbDir = kThemeThumbDownward;
+ } else {
+ tdi->trackInfo.scrollbar.viewsize = slider->pageStep;
+ }
+}
+#endif
+
+QMacStylePrivate::QMacStylePrivate(QMacStyle *style)
+ : timerID(-1), progressFrame(0), q(style), mouseDown(false)
+{
+ defaultButtonStart = CFAbsoluteTimeGetCurrent();
+ memset(&buttonState, 0, sizeof(ButtonState));
+
+ if (ptrHIShapeGetBounds == 0) {
+ QLibrary library(QLatin1String("/System/Library/Frameworks/Carbon.framework/Carbon"));
+ library.setLoadHints(QLibrary::ExportExternalSymbolsHint);
+ ptrHIShapeGetBounds = reinterpret_cast<PtrHIShapeGetBounds>(library.resolve("HIShapeGetBounds"));
+ }
+
+}
+
+bool QMacStylePrivate::animatable(QMacStylePrivate::Animates as, const QWidget *w) const
+{
+ if (as == AquaPushButton) {
+ QPushButton *pb = const_cast<QPushButton *>(static_cast<const QPushButton *>(w));
+ if (w->window()->isActiveWindow() && pb && !mouseDown) {
+ if (static_cast<const QPushButton *>(w) != defaultButton) {
+ // Changed on its own, update the value.
+ const_cast<QMacStylePrivate *>(this)->stopAnimate(as, defaultButton);
+ const_cast<QMacStylePrivate *>(this)->startAnimate(as, pb);
+ }
+ return true;
+ }
+ } else if (as == AquaProgressBar) {
+ if (progressBars.contains((const_cast<QWidget *>(w))))
+ return true;
+ }
+ return false;
+}
+
+void QMacStylePrivate::stopAnimate(QMacStylePrivate::Animates as, QWidget *w)
+{
+ if (as == AquaPushButton && defaultButton) {
+ QPushButton *tmp = defaultButton;
+ defaultButton = 0;
+ tmp->update();
+ } else if (as == AquaProgressBar) {
+ progressBars.removeAll(w);
+ }
+}
+
+void QMacStylePrivate::startAnimate(QMacStylePrivate::Animates as, QWidget *w)
+{
+ if (as == AquaPushButton)
+ defaultButton = static_cast<QPushButton *>(w);
+ else if (as == AquaProgressBar)
+ progressBars.append(w);
+ startAnimationTimer();
+}
+
+void QMacStylePrivate::startAnimationTimer()
+{
+ if ((defaultButton || !progressBars.isEmpty()) && timerID <= -1)
+ timerID = startTimer(animateSpeed(AquaListViewItemOpen));
+}
+
+enum { TabNormalLeft, TabNormalMid, TabNormalRight, TabSelectedActiveLeft,
+ TabSelectedActiveMid, TabSelectedActiveRight, TabSelectedInactiveLeft,
+ TabSelectedInactiveMid, TabSelectedInactiveRight, TabSelectedActiveGraphiteLeft,
+ TabSelectedActiveGraphiteMid, TabSelectedActiveGraphiteRight,
+ TabPressedLeft, TabPressedMid, TabPressedRight };
+
+static const char * const * const PantherTabXpms[] = {
+ qt_mac_tabnrm_left,
+ qt_mac_tabnrm_mid,
+ qt_mac_tabnrm_right,
+ qt_mac_tabselected_active_left,
+ qt_mac_tabselected_active_mid,
+ qt_mac_tabselected_active_right,
+ qt_mac_tabselected_inactive_left,
+ qt_mac_tabselected_inactive_mid,
+ qt_mac_tabselected_inactive_right,
+ qt_mac_tab_selected_active_graph_left,
+ qt_mac_tab_selected_active_graph_mid,
+ qt_mac_tab_selected_active_graph_right,
+ qt_mac_tab_press_left,
+ qt_mac_tab_press_mid,
+ qt_mac_tab_press_right};
+
+void QMacStylePrivate::drawPantherTab(const QStyleOptionTab *tabOpt, QPainter *p,
+ const QWidget *) const
+{
+ QString tabKey = QLatin1String("$qt_mac_style_tab_");
+ int pantherTabStart;
+ int pantherTabMid;
+ int pantherTabEnd;
+
+ ThemeTabDirection ttd = getTabDirection(tabOpt->shape);
+
+ if (tabOpt->state & QStyle::State_Selected) {
+ if (!(tabOpt->state & QStyle::State_Active)) {
+ pantherTabStart = TabSelectedInactiveLeft;
+ } else {
+ // Draw into a pixmap to determine which version we use, Aqua or Graphite.
+ QPixmap tabPix(20, 20);
+ QPainter pixPainter(&tabPix);
+ HIThemeTabDrawInfo tdi;
+ tdi.version = 0;
+ tdi.style = kThemeTabFront;
+ tdi.direction = kThemeTabNorth;
+ tdi.size = kHIThemeTabSizeNormal;
+ tdi.adornment = kHIThemeTabAdornmentNone;
+ HIRect inRect = CGRectMake(0.0f, 0.0f, 20.0f, 20.0f);
+ HIThemeDrawTab(&inRect, &tdi, QMacCGContext(&pixPainter), kHIThemeOrientationNormal, 0);
+ pixPainter.end();
+ const QRgb GraphiteColor = 0xffa7b0ba;
+ QRgb pmColor = tabPix.toImage().pixel(10, 10);
+ if (qAbs(qRed(pmColor) - qRed(GraphiteColor)) < 3 &&
+ qAbs(qGreen(pmColor) - qGreen(GraphiteColor)) < 3
+ && qAbs(qBlue(pmColor) - qBlue(GraphiteColor)) < 3)
+ pantherTabStart = TabSelectedActiveGraphiteLeft;
+ else
+ pantherTabStart = TabSelectedActiveLeft;
+ }
+ } else if (tabOpt->state & QStyle::State_Sunken) {
+ pantherTabStart = TabPressedLeft;
+ } else {
+ pantherTabStart = TabNormalLeft;
+ }
+
+
+ bool doLine;
+ bool verticalTabs = ttd == kThemeTabWest || ttd == kThemeTabEast;
+
+ QStyleOptionTab::TabPosition tp = tabOpt->position;
+ if (ttd == kThemeTabWest
+ || ((ttd == kThemeTabNorth || ttd == kThemeTabSouth)
+ && tabOpt->direction == Qt::RightToLeft)) {
+ if (tp == QStyleOptionTab::Beginning)
+ tp = QStyleOptionTab::End;
+ else if (tp == QStyleOptionTab::End)
+ tp = QStyleOptionTab::Beginning;
+ }
+
+ switch (tp) {
+ default: // Stupid GCC, being overly pedantic
+ case QStyleOptionTab::Beginning:
+ doLine = false;
+ pantherTabMid = pantherTabEnd = pantherTabStart + 1;
+ break;
+ case QStyleOptionTab::Middle:
+ doLine = true;
+ pantherTabMid = pantherTabEnd = ++pantherTabStart;
+ break;
+ case QStyleOptionTab::End:
+ doLine = true;
+ pantherTabMid = ++pantherTabStart;
+ pantherTabEnd = pantherTabMid + 1;
+ break;
+ case QStyleOptionTab::OnlyOneTab:
+ doLine = false;
+ pantherTabMid = pantherTabStart + 1;
+ pantherTabEnd = pantherTabMid + 1;
+ break;
+ }
+
+ QPixmap pmStart;
+ if (!QPixmapCache::find(tabKey + QString::number(pantherTabStart), pmStart)) {
+ pmStart = QPixmap(PantherTabXpms[pantherTabStart]);
+ QPixmapCache::insert(tabKey + QString::number(pantherTabStart), pmStart);
+ }
+
+ QPixmap pmMid;
+ if (!QPixmapCache::find(tabKey + QString::number(pantherTabMid), pmMid)) {
+ pmMid = QPixmap(PantherTabXpms[pantherTabMid]);
+ QPixmapCache::insert(tabKey + QString::number(pantherTabMid), pmMid);
+ }
+
+ QPixmap pmEnd;
+ if (!QPixmapCache::find(tabKey + QString::number(pantherTabEnd), pmEnd)) {
+ pmEnd = QPixmap(PantherTabXpms[pantherTabEnd]);
+ QPixmapCache::insert(tabKey + QString::number(pantherTabEnd), pmEnd);
+ }
+ QRect tr = tabOpt->rect;
+ if (verticalTabs) {
+ p->save();
+ int newX, newY, newRot;
+ if (tabOpt->shape == QTabBar::RoundedEast || tabOpt->shape == QTabBar::TriangularEast) {
+ newX = tr.width();
+ newY = tr.y();
+ newRot = 90;
+ } else {
+ newX = 0;
+ newY = tr.y() + tr.height();
+ newRot = -90;
+ }
+ tr.setRect(0, 0, tr.height(), tr.width());
+ QMatrix m;
+ if (ttd == kThemeTabEast) {
+ // It's lame but Apple inverts these on the East side.
+ m.scale(-1, 1);
+ m.translate(-tabOpt->rect.width(), 0);
+ }
+ m.translate(newX, newY);
+ m.rotate(newRot);
+ p->setMatrix(m, true);
+ }
+
+ int x = tr.x();
+ int y = tr.y();
+ int endX = x + tr.width() - pmEnd.width();
+
+ p->drawPixmap(x, y, pmStart.width(), tr.height(), pmStart);
+ if (doLine) {
+ QPen oldPen = p->pen();
+ p->setPen(QColor(0, 0, 0, 0x35));
+ p->drawLine(x, y + (verticalTabs ? 0 : 1), x, tr.height() - 2);
+ }
+
+ for (x = x + pmStart.width(); x < endX; x += pmMid.width())
+ p->drawPixmap(x, y, pmMid.width(), tr.height(), pmMid);
+ p->drawPixmap(endX, y, pmEnd.width(), tr.height(), pmEnd);
+ if (verticalTabs)
+ p->restore();
+}
+
+bool QMacStylePrivate::addWidget(QWidget *w)
+{
+ //already knew of it
+ if (static_cast<QPushButton*>(w) == defaultButton
+ || progressBars.contains(static_cast<QProgressBar*>(w)))
+ return false;
+
+ if (QPushButton *btn = qobject_cast<QPushButton *>(w)) {
+ btn->installEventFilter(this);
+ if (btn->isDefault() || (btn->autoDefault() && btn->hasFocus()))
+ startAnimate(AquaPushButton, btn);
+ return true;
+ } else {
+ bool isProgressBar = (qobject_cast<QProgressBar *>(w)
+#ifdef QT3_SUPPORT
+ || w->inherits("Q3ProgressBar")
+#endif
+ );
+ if (isProgressBar) {
+ w->installEventFilter(this);
+ startAnimate(AquaProgressBar, w);
+ return true;
+ }
+ }
+ if (w->isWindow()) {
+ w->installEventFilter(this);
+ return true;
+ }
+ return false;
+}
+
+void QMacStylePrivate::removeWidget(QWidget *w)
+{
+ QPushButton *btn = qobject_cast<QPushButton *>(w);
+ if (btn && btn == defaultButton) {
+ stopAnimate(AquaPushButton, btn);
+ } else if (qobject_cast<QProgressBar *>(w)
+#ifdef QT3_SUPPORT
+ || w->inherits("Q3ProgressBar")
+#endif
+ ) {
+ stopAnimate(AquaProgressBar, w);
+ }
+}
+
+ThemeDrawState QMacStylePrivate::getDrawState(QStyle::State flags)
+{
+ ThemeDrawState tds = kThemeStateActive;
+ if (flags & QStyle::State_Sunken) {
+ tds = kThemeStatePressed;
+ } else if (flags & QStyle::State_Active) {
+ if (!(flags & QStyle::State_Enabled))
+ tds = kThemeStateUnavailable;
+ } else {
+ if (flags & QStyle::State_Enabled)
+ tds = kThemeStateInactive;
+ else
+ tds = kThemeStateUnavailableInactive;
+ }
+ return tds;
+}
+
+void QMacStylePrivate::timerEvent(QTimerEvent *)
+{
+ int animated = 0;
+ if (defaultButton && defaultButton->isEnabled() && defaultButton->window()->isActiveWindow()
+ && defaultButton->isVisibleTo(0) && (defaultButton->isDefault()
+ || (defaultButton->autoDefault() && defaultButton->hasFocus()))
+ && doAnimate(AquaPushButton)) {
+ ++animated;
+ defaultButton->update();
+ }
+ if (!progressBars.isEmpty()) {
+ int i = 0;
+ while (i < progressBars.size()) {
+ QWidget *maybeProgress = progressBars.at(i);
+ if (!maybeProgress) {
+ progressBars.removeAt(i);
+ } else {
+ if (QProgressBar *pb = qobject_cast<QProgressBar *>(maybeProgress)) {
+ if (pb->maximum() == 0 || pb->value() > 0
+ && pb->value() < pb->maximum()) {
+ if (doAnimate(AquaProgressBar))
+ pb->update();
+ }
+ }
+#ifdef QT3_SUPPORT
+ else {
+ // Watch me now...
+ QVariant progress = maybeProgress->property("progress");
+ QVariant totalSteps = maybeProgress->property("totalSteps");
+ if (progress.isValid() && totalSteps.isValid()) {
+ int intProgress = progress.toInt();
+ int intTotalSteps = totalSteps.toInt();
+ if (intTotalSteps == 0 || intProgress > 0 && intProgress < intTotalSteps) {
+ if (doAnimate(AquaProgressBar))
+ maybeProgress->update();
+ }
+ }
+ }
+#endif
+ ++i;
+ }
+ }
+ if (i > 0) {
+ ++progressFrame;
+ animated += i;
+ }
+ }
+ if (animated <= 0) {
+ killTimer(timerID);
+ timerID = -1;
+ }
+}
+
+bool QMacStylePrivate::eventFilter(QObject *o, QEvent *e)
+{
+ //animate
+ if (QProgressBar *pb = qobject_cast<QProgressBar *>(o)) {
+ switch (e->type()) {
+ default:
+ break;
+ case QEvent::Show:
+ if (!progressBars.contains(pb))
+ startAnimate(AquaProgressBar, pb);
+ break;
+ case QEvent::Destroy:
+ case QEvent::Hide:
+ progressBars.removeAll(pb);
+ }
+ } else if (QPushButton *btn = qobject_cast<QPushButton *>(o)) {
+ switch (e->type()) {
+ default:
+ break;
+ case QEvent::FocusIn:
+ if (btn->autoDefault())
+ startAnimate(AquaPushButton, btn);
+ break;
+ case QEvent::Destroy:
+ case QEvent::Hide:
+ if (btn == defaultButton)
+ stopAnimate(AquaPushButton, btn);
+ break;
+ case QEvent::MouseButtonPress:
+ // It is very confusing to keep the button pulsing, so just stop the animation.
+ if (static_cast<QMouseEvent *>(e)->button() == Qt::LeftButton)
+ mouseDown = true;
+ stopAnimate(AquaPushButton, btn);
+ break;
+ case QEvent::MouseButtonRelease:
+ if (static_cast<QMouseEvent *>(e)->button() == Qt::LeftButton)
+ mouseDown = false;
+ // fall through
+ case QEvent::FocusOut:
+ case QEvent::Show:
+ case QEvent::WindowActivate: {
+ QList<QPushButton *> list = qFindChildren<QPushButton *>(btn->window());
+ for (int i = 0; i < list.size(); ++i) {
+ QPushButton *pBtn = list.at(i);
+ if ((e->type() == QEvent::FocusOut
+ && (pBtn->isDefault() || (pBtn->autoDefault() && pBtn->hasFocus()))
+ && pBtn != btn)
+ || ((e->type() == QEvent::Show || e->type() == QEvent::MouseButtonRelease
+ || e->type() == QEvent::WindowActivate)
+ && pBtn->isDefault())) {
+ if (pBtn->window()->isActiveWindow()) {
+ startAnimate(AquaPushButton, pBtn);
+ }
+ break;
+ }
+ }
+ break; }
+ }
+ }
+ return false;
+}
+
+bool QMacStylePrivate::doAnimate(QMacStylePrivate::Animates as)
+{
+ if (as == AquaPushButton) {
+ } else if (as == AquaProgressBar) {
+ // something for later...
+ } else if (as == AquaListViewItemOpen) {
+ // To be revived later...
+ }
+ return true;
+}
+
+void QMacStylePrivate::drawColorlessButton(const HIRect &macRect, HIThemeButtonDrawInfo *bdi,
+ QPainter *p, const QStyleOption *opt) const
+{
+ int xoff = 0,
+ yoff = 0,
+ extraWidth = 0,
+ extraHeight = 0,
+ finalyoff = 0;
+
+ const QStyleOptionComboBox *combo = qstyleoption_cast<const QStyleOptionComboBox *>(opt);
+ int width = int(macRect.size.width) + extraWidth;
+ int height = int(macRect.size.height) + extraHeight;
+
+ if (width <= 0 || height <= 0)
+ return; // nothing to draw
+
+ QString key = QLatin1String("$qt_mac_style_ctb_") + QString::number(bdi->kind) + QLatin1Char('_')
+ + QString::number(bdi->value) + QLatin1Char('_') + QString::number(width)
+ + QLatin1Char('_') + QString::number(height);
+ QPixmap pm;
+ if (!QPixmapCache::find(key, pm)) {
+ QPixmap activePixmap(width, height);
+ activePixmap.fill(Qt::transparent);
+ {
+ if (combo){
+ // Carbon combos don't scale. Therefore we draw it
+ // ourselves, if a scaled version is needed.
+ QPainter tmpPainter(&activePixmap);
+ QMacStylePrivate::drawCombobox(macRect, *bdi, &tmpPainter);
+ }
+ else {
+ QMacCGContext cg(&activePixmap);
+ HIRect newRect = CGRectMake(xoff, yoff, macRect.size.width, macRect.size.height);
+ HIThemeDrawButton(&newRect, bdi, cg, kHIThemeOrientationNormal, 0);
+ }
+ }
+
+ if (!combo && bdi->value == kThemeButtonOff) {
+ pm = activePixmap;
+ } else if (combo) {
+ QImage image = activePixmap.toImage();
+
+ for (int y = 0; y < height; ++y) {
+ QRgb *scanLine = reinterpret_cast<QRgb *>(image.scanLine(y));
+
+ for (int x = 0; x < width; ++x) {
+ QRgb &pixel = scanLine[x];
+
+ int darkest = qRed(pixel);
+ int mid = qGreen(pixel);
+ int lightest = qBlue(pixel);
+
+ if (darkest > mid)
+ qSwap(darkest, mid);
+ if (mid > lightest)
+ qSwap(mid, lightest);
+ if (darkest > mid)
+ qSwap(darkest, mid);
+
+ int gray = (mid + 2 * lightest) / 3;
+ pixel = qRgba(gray, gray, gray, qAlpha(pixel));
+ }
+ }
+ pm = QPixmap::fromImage(image);
+ } else {
+ QImage activeImage = activePixmap.toImage();
+ QImage colorlessImage;
+ {
+ QPixmap colorlessPixmap(width, height);
+ colorlessPixmap.fill(Qt::transparent);
+
+ QMacCGContext cg(&colorlessPixmap);
+ HIRect newRect = CGRectMake(xoff, yoff, macRect.size.width, macRect.size.height);
+ int oldValue = bdi->value;
+ bdi->value = kThemeButtonOff;
+ HIThemeDrawButton(&newRect, bdi, cg, kHIThemeOrientationNormal, 0);
+ bdi->value = oldValue;
+ colorlessImage = colorlessPixmap.toImage();
+ }
+
+ for (int y = 0; y < height; ++y) {
+ QRgb *colorlessScanLine = reinterpret_cast<QRgb *>(colorlessImage.scanLine(y));
+ const QRgb *activeScanLine = reinterpret_cast<const QRgb *>(activeImage.scanLine(y));
+
+ for (int x = 0; x < width; ++x) {
+ QRgb &colorlessPixel = colorlessScanLine[x];
+ QRgb activePixel = activeScanLine[x];
+
+ if (activePixel != colorlessPixel) {
+ int max = qMax(qMax(qRed(activePixel), qGreen(activePixel)),
+ qBlue(activePixel));
+ QRgb newPixel = qRgba(max, max, max, qAlpha(activePixel));
+ if (qGray(newPixel) < qGray(colorlessPixel)
+ || qAlpha(newPixel) > qAlpha(colorlessPixel))
+ colorlessPixel = newPixel;
+ }
+ }
+ }
+ pm = QPixmap::fromImage(colorlessImage);
+ }
+ QPixmapCache::insert(key, pm);
+ }
+ p->drawPixmap(int(macRect.origin.x), int(macRect.origin.y) + finalyoff, width, height, pm);
+}
+
+/*!
+ \class QMacStyle
+ \brief The QMacStyle class provides a Mac OS X style using the Apple Appearance Manager.
+
+ \ingroup appearance
+
+ This class is implemented as a wrapper to the HITheme
+ APIs, allowing applications to be styled according to the current
+ theme in use on Mac OS X. This is done by having primitives
+ in QStyle implemented in terms of what Mac OS X would normally theme.
+
+ \warning This style is only available on Mac OS X because it relies on the
+ HITheme APIs.
+
+ There are additional issues that should be taken
+ into consideration to make an application compatible with the
+ \link http://developer.apple.com/documentation/UserExperience/Conceptual/OSXHIGuidelines/index.html
+ Apple Human Interface Guidelines \endlink. Some of these issues are outlined
+ below.
+
+ \list
+
+ \i Layout - The restrictions on window layout are such that some
+ aspects of layout that are style-dependent cannot be achieved
+ using QLayout. Changes are being considered (and feedback would be
+ appreciated) to make layouts QStyle-able. Some of the restrictions
+ involve horizontal and vertical widget alignment and widget size
+ (covered below).
+
+ \i Widget size - Mac OS X allows widgets to have specific fixed sizes. Qt
+ does not fully implement this behavior so as to maintain cross-platform
+ compatibility. As a result some widgets sizes may be inappropriate (and
+ subsequently not rendered correctly by the HITheme APIs).The
+ QWidget::sizeHint() will return the appropriate size for many
+ managed widgets (widgets enumerated in \l QStyle::ContentsType).
+
+ \i Effects - QMacStyle uses HITheme for performing most of the drawing, but
+ also uses emulation in a few cases where HITheme does not provide the
+ required functionality (for example, tab bars on Panther, the toolbar
+ separator, etc). We tried to make the emulation as close to the original as
+ possible. Please report any issues you see in effects or non-standard
+ widgets.
+
+ \endlist
+
+ There are other issues that need to be considered in the feel of
+ your application (including the general color scheme to match the
+ Aqua colors). The Guidelines mentioned above will remain current
+ with new advances and design suggestions for Mac OS X.
+
+ Note that the functions provided by QMacStyle are
+ reimplementations of QStyle functions; see QStyle for their
+ documentation.
+
+ \img qmacstyle.png
+ \sa QWindowsXPStyle, QWindowsStyle, QPlastiqueStyle, QCDEStyle, QMotifStyle
+*/
+
+
+/*!
+ \enum QMacStyle::WidgetSizePolicy
+
+ \value SizeSmall
+ \value SizeLarge
+ \value SizeMini
+ \value SizeDefault
+ \omitvalue SizeNone
+*/
+
+/*!
+ Constructs a QMacStyle object.
+*/
+QMacStyle::QMacStyle()
+ : QWindowsStyle()
+{
+ d = new QMacStylePrivate(this);
+}
+
+/*!
+ Destructs a QMacStyle object.
+*/
+QMacStyle::~QMacStyle()
+{
+ delete qt_mac_backgroundPattern;
+ qt_mac_backgroundPattern = 0;
+ delete d;
+}
+
+/*! \internal
+ Generates the standard widget background pattern.
+*/
+QPixmap QMacStylePrivate::generateBackgroundPattern() const
+{
+ QPixmap px(4, 4);
+#if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4)
+ if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_4) {
+ QMacCGContext cg(&px);
+ HIThemeSetFill(kThemeBrushDialogBackgroundActive, 0, cg, kHIThemeOrientationNormal);
+ const CGRect cgRect = CGRectMake(0, 0, px.width(), px.height());
+ CGContextFillRect(cg, cgRect);
+ } else
+#endif
+ {
+#ifndef QT_MAC_NO_QUICKDRAW
+ QMacSavedPortInfo port(&px);
+ SetThemeBackground(kThemeBrushDialogBackgroundActive, px.depth(), true);
+ const Rect qdRect = { 0, 0, px.width(), px.height() };
+ EraseRect(&qdRect);
+#endif
+ }
+ return px;
+}
+
+/*! \internal
+ Fills the given \a rect with the pattern stored in \a brush. As an optimization,
+ HIThemeSetFill us used directly if we are filling with the standard background.
+*/
+void qt_mac_fill_background(QPainter *painter, const QRegion &rgn, const QPoint &offset, const QBrush &brush)
+{
+#if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4)
+ QPoint dummy;
+ const QPaintDevice *target = painter->device();
+ const QPaintDevice *redirected = QPainter::redirected(target, &dummy);
+ const bool usePainter = redirected && redirected != target;
+ const QRegion translated = rgn.translated(offset);
+
+ if (!usePainter && QSysInfo::MacintoshVersion >= QSysInfo::MV_10_4 && qt_mac_backgroundPattern
+ && qt_mac_backgroundPattern->cacheKey() == brush.texture().cacheKey()) {
+
+ painter->setClipRegion(translated);
+
+ CGContextRef cg = qt_mac_cg_context(target);
+ CGContextSaveGState(cg);
+ HIThemeSetFill(kThemeBrushDialogBackgroundActive, 0, cg, kHIThemeOrientationInverted);
+
+ const QVector<QRect> &rects = translated.rects();
+ for (int i = 0; i < rects.size(); ++i) {
+ const QRect rect(rects.at(i));
+ // Anchor the pattern to the top so it stays put when the window is resized.
+ CGContextSetPatternPhase(cg, CGSizeMake(rect.width(), rect.height()));
+ CGRect mac_rect = CGRectMake(rect.x(), rect.y(), rect.width(), rect.height());
+ CGContextFillRect(cg, mac_rect);
+ }
+
+ CGContextRestoreGState(cg);
+ } else
+#endif
+ {
+ const QRect rect(translated.boundingRect());
+ painter->setClipRegion(translated);
+ painter->drawTiledPixmap(rect, brush.texture(), rect.topLeft());
+ }
+}
+
+/*! \reimp */
+void QMacStyle::polish(QPalette &pal)
+{
+ if (qt_mac_backgroundPattern == 0)
+ qt_mac_backgroundPattern = new QPixmap(d->generateBackgroundPattern());
+
+ QColor pc(Qt::black);
+ pc = qcolorForTheme(kThemeBrushDialogBackgroundActive);
+ QBrush background(pc, *qt_mac_backgroundPattern);
+ pal.setBrush(QPalette::All, QPalette::Window, background);
+ pal.setBrush(QPalette::All, QPalette::Button, background);
+
+ QCFString theme;
+ const OSErr err = CopyThemeIdentifier(&theme);
+ if (err == noErr && CFStringCompare(theme, kThemeAppearanceAquaGraphite, 0) == kCFCompareEqualTo) {
+ pal.setBrush(QPalette::All, QPalette::AlternateBase, QColor(240, 240, 240));
+ } else {
+ pal.setBrush(QPalette::All, QPalette::AlternateBase, QColor(237, 243, 254));
+ }
+}
+
+/*! \reimp */
+void QMacStyle::polish(QApplication *)
+{
+}
+
+/*! \reimp */
+void QMacStyle::unpolish(QApplication *)
+{
+}
+
+/*! \reimp */
+void QMacStyle::polish(QWidget* w)
+{
+ d->addWidget(w);
+ if (qt_mac_is_metal(w) && !w->testAttribute(Qt::WA_SetPalette)) {
+ // Set a clear brush so that the metal shines through.
+ QPalette pal = w->palette();
+ QBrush background(Qt::transparent);
+ pal.setBrush(QPalette::All, QPalette::Window, background);
+ pal.setBrush(QPalette::All, QPalette::Button, background);
+ w->setPalette(pal);
+ w->setAttribute(Qt::WA_SetPalette, false);
+ }
+
+ if (qobject_cast<QMenu*>(w) || qobject_cast<QComboBoxPrivateContainer *>(w)) {
+ w->setWindowOpacity(0.94);
+ if (!w->testAttribute(Qt::WA_SetPalette)) {
+ QPixmap px(64, 64);
+ HIThemeMenuDrawInfo mtinfo;
+ mtinfo.version = qt_mac_hitheme_version;
+ mtinfo.menuType = kThemeMenuTypePopUp;
+ HIRect rect = CGRectMake(0, 0, px.width(), px.height());
+ HIThemeDrawMenuBackground(&rect, &mtinfo, QCFType<CGContextRef>(qt_mac_cg_context(&px)),
+ kHIThemeOrientationNormal);
+ QPalette pal = w->palette();
+ QBrush background(px);
+ pal.setBrush(QPalette::All, QPalette::Window, background);
+ pal.setBrush(QPalette::All, QPalette::Button, background);
+ w->setPalette(pal);
+ w->setAttribute(Qt::WA_SetPalette, false);
+ }
+ }
+
+ // Adjust the lineedit of the editable combo box
+ if (QSysInfo::MacintoshVersion == QSysInfo::MV_10_3) {
+ if (QLineEdit *lineEdit = qobject_cast<QLineEdit *>(w)) {
+ if (qobject_cast<QComboBox *>(lineEdit->parentWidget())
+ && !lineEdit->testAttribute(Qt::WA_SetFont)) {
+ QFont font = lineEdit->font();
+ font.setPointSize(font.pointSize() - 1);
+ lineEdit->setFont(font);
+ }
+ }
+ }
+
+ if (QTabBar *tb = qobject_cast<QTabBar*>(w)) {
+ if (tb->documentMode()) {
+ w->setAttribute(Qt::WA_Hover);
+ w->setFont(qt_app_fonts_hash()->value("QSmallFont", QFont()));
+ QPalette p = w->palette();
+ p.setColor(QPalette::WindowText, QColor(17, 17, 17));
+ w->setPalette(p);
+ }
+ }
+
+ QWindowsStyle::polish(w);
+
+ if (QRubberBand *rubber = qobject_cast<QRubberBand*>(w)) {
+ rubber->setWindowOpacity(0.25);
+ rubber->setAttribute(Qt::WA_PaintOnScreen, false);
+ rubber->setAttribute(Qt::WA_NoSystemBackground, false);
+ }
+}
+
+/*! \reimp */
+void QMacStyle::unpolish(QWidget* w)
+{
+ d->removeWidget(w);
+ if ((qobject_cast<QMenu*>(w) || qt_mac_is_metal(w)) && !w->testAttribute(Qt::WA_SetPalette)) {
+ QPalette pal = qApp->palette(w);
+ w->setPalette(pal);
+ w->setAttribute(Qt::WA_SetPalette, false);
+ w->setWindowOpacity(1.0);
+ }
+
+ if (QComboBox *combo = qobject_cast<QComboBox *>(w)) {
+ if (!combo->isEditable()) {
+ if (QWidget *widget = combo->findChild<QComboBoxPrivateContainer *>())
+ widget->setWindowOpacity(1.0);
+ }
+ }
+
+ if (QRubberBand *rubber = ::qobject_cast<QRubberBand*>(w)) {
+ rubber->setWindowOpacity(1.0);
+ rubber->setAttribute(Qt::WA_PaintOnScreen, true);
+ rubber->setAttribute(Qt::WA_NoSystemBackground, true);
+ }
+
+ if (QFocusFrame *frame = qobject_cast<QFocusFrame *>(w)) {
+ frame->setAttribute(Qt::WA_NoSystemBackground, true);
+ frame->setAutoFillBackground(true);
+ }
+ QWindowsStyle::unpolish(w);
+}
+
+/*! \reimp */
+int QMacStyle::pixelMetric(PixelMetric metric, const QStyleOption *opt, const QWidget *widget) const
+{
+ int controlSize = getControlSize(opt, widget);
+ SInt32 ret = 0;
+
+ switch (metric) {
+ case PM_TabCloseIndicatorWidth:
+ case PM_TabCloseIndicatorHeight:
+ ret = closeButtonSize;
+ break;
+ case PM_ToolBarIconSize:
+ ret = pixelMetric(PM_LargeIconSize);
+ break;
+ case PM_FocusFrameVMargin:
+ case PM_FocusFrameHMargin:
+ GetThemeMetric(kThemeMetricFocusRectOutset, &ret);
+ break;
+ case PM_DialogButtonsSeparator:
+ ret = -5;
+ break;
+ case PM_DialogButtonsButtonHeight: {
+ QSize sz;
+ ret = d->aquaSizeConstrain(opt, 0, QStyle::CT_PushButton, QSize(-1, -1), &sz);
+ if (sz == QSize(-1, -1))
+ ret = 32;
+ else
+ ret = sz.height();
+ break; }
+ case PM_CheckListButtonSize: {
+ switch (d->aquaSizeConstrain(opt, widget)) {
+ case QAquaSizeUnknown:
+ case QAquaSizeLarge:
+ GetThemeMetric(kThemeMetricCheckBoxWidth, &ret);
+ break;
+ case QAquaSizeMini:
+ GetThemeMetric(kThemeMetricMiniCheckBoxWidth, &ret);
+ break;
+ case QAquaSizeSmall:
+ GetThemeMetric(kThemeMetricSmallCheckBoxWidth, &ret);
+ break;
+ }
+ break; }
+ case PM_DialogButtonsButtonWidth: {
+ QSize sz;
+ ret = d->aquaSizeConstrain(opt, 0, QStyle::CT_PushButton, QSize(-1, -1), &sz);
+ if (sz == QSize(-1, -1))
+ ret = 70;
+ else
+ ret = sz.width();
+ break; }
+
+ case PM_MenuBarHMargin:
+ ret = 8;
+ break;
+
+ case PM_MenuBarVMargin:
+ ret = 0;
+ break;
+
+ case QStyle::PM_MenuDesktopFrameWidth:
+ ret = 5;
+ break;
+
+ case PM_CheckBoxLabelSpacing:
+ case PM_RadioButtonLabelSpacing:
+ ret = 2;
+ break;
+ case PM_MenuScrollerHeight:
+#if 0
+ SInt16 ash, asw;
+ GetThemeMenuItemExtra(kThemeMenuItemScrollUpArrow, &ash, &asw);
+ ret = ash;
+#else
+ ret = 15; // I hate having magic numbers in here...
+#endif
+ break;
+ case PM_DefaultFrameWidth:
+#ifndef QT_NO_MAINWINDOW
+ if (widget && (widget->isWindow() || !widget->parentWidget()
+ || (qobject_cast<const QMainWindow*>(widget->parentWidget())
+ && static_cast<QMainWindow *>(widget->parentWidget())->centralWidget() == widget))
+ && (qobject_cast<const QAbstractScrollArea *>(widget)
+#ifdef QT3_SUPPORT
+ || widget->inherits("QScrollView")
+#endif
+ || widget->inherits("QWorkspaceChild")))
+ ret = 0;
+ else
+#endif
+ // The combo box popup has no frame.
+ if (qstyleoption_cast<const QStyleOptionComboBox *>(opt) != 0)
+ ret = 0;
+ else
+ ret = QWindowsStyle::pixelMetric(metric, opt, widget);
+ break;
+ case PM_MaximumDragDistance:
+ ret = -1;
+ break;
+ case PM_ScrollBarSliderMin:
+ ret = 24;
+ break;
+ case PM_SpinBoxFrameWidth:
+ GetThemeMetric(kThemeMetricEditTextFrameOutset, &ret);
+ ret += 2;
+ break;
+ case PM_ButtonShiftHorizontal:
+ case PM_ButtonShiftVertical:
+ ret = 0;
+ break;
+ case PM_SliderLength:
+ ret = 17;
+ break;
+ case PM_ButtonDefaultIndicator:
+ ret = 0;
+ break;
+ case PM_TitleBarHeight:
+ if (const QStyleOptionTitleBar *tb = qstyleoption_cast<const QStyleOptionTitleBar *>(opt)) {
+ HIThemeWindowDrawInfo wdi;
+ wdi.version = qt_mac_hitheme_version;
+ wdi.state = kThemeStateActive;
+ wdi.windowType = QtWinType;
+ if (tb->titleBarState)
+ wdi.attributes = kThemeWindowHasFullZoom | kThemeWindowHasCloseBox
+ | kThemeWindowHasCollapseBox;
+ else if (tb->titleBarFlags & Qt::WindowSystemMenuHint)
+ wdi.attributes = kThemeWindowHasCloseBox;
+ else
+ wdi.attributes = 0;
+ wdi.titleHeight = tb->rect.height();
+ wdi.titleWidth = tb->rect.width();
+ QCFType<HIShapeRef> region;
+ HIRect hirect = qt_hirectForQRect(tb->rect);
+ if (hirect.size.width == -1)
+ hirect.size.width = 100;
+ if (hirect.size.height == -1)
+ hirect.size.height = 30;
+
+ HIThemeGetWindowShape(&hirect, &wdi, kWindowTitleBarRgn, &region);
+ HIRect rect;
+ ptrHIShapeGetBounds(region, &rect);
+ ret = int(rect.size.height);
+ ret += 4;
+ }
+ break;
+ case PM_TabBarTabVSpace:
+ ret = 4;
+ break;
+ case PM_TabBarTabShiftHorizontal:
+ case PM_TabBarTabShiftVertical:
+ ret = 0;
+ break;
+ case PM_TabBarBaseHeight:
+ ret = 0;
+ break;
+ case PM_TabBarTabOverlap:
+ ret = 0;
+ break;
+ case PM_TabBarBaseOverlap:
+ switch (d->aquaSizeConstrain(opt, widget)) {
+ case QAquaSizeUnknown:
+ case QAquaSizeLarge:
+ ret = 11;
+ break;
+ case QAquaSizeSmall:
+ ret = 8;
+ break;
+ case QAquaSizeMini:
+ ret = 7;
+ break;
+ }
+ break;
+ case PM_ScrollBarExtent: {
+ switch (d->aquaSizeConstrain(opt, widget)) {
+ case QAquaSizeUnknown:
+ case QAquaSizeLarge:
+ GetThemeMetric(kThemeMetricScrollBarWidth, &ret);
+ break;
+ case QAquaSizeMini:
+#if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_3) && 0
+ if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_3) {
+ GetThemeMetric(kThemeMetricMiniScrollBarWidth, &ret);
+ break;
+ }
+#endif
+ case QAquaSizeSmall:
+ GetThemeMetric(kThemeMetricSmallScrollBarWidth, &ret);
+ break;
+ }
+ break; }
+ case PM_IndicatorHeight: {
+ switch (d->aquaSizeConstrain(opt, widget)) {
+ case QAquaSizeUnknown:
+ case QAquaSizeLarge:
+ GetThemeMetric(kThemeMetricCheckBoxHeight, &ret);
+ break;
+ case QAquaSizeMini:
+ GetThemeMetric(kThemeMetricMiniCheckBoxHeight, &ret);
+ break;
+ case QAquaSizeSmall:
+ GetThemeMetric(kThemeMetricSmallCheckBoxHeight, &ret);
+ break;
+ }
+ break; }
+ case PM_IndicatorWidth: {
+ switch (d->aquaSizeConstrain(opt, widget)) {
+ case QAquaSizeUnknown:
+ case QAquaSizeLarge:
+ GetThemeMetric(kThemeMetricCheckBoxWidth, &ret);
+ break;
+ case QAquaSizeMini:
+ GetThemeMetric(kThemeMetricMiniCheckBoxWidth, &ret);
+ break;
+ case QAquaSizeSmall:
+ GetThemeMetric(kThemeMetricSmallCheckBoxWidth, &ret);
+ break;
+ }
+ ++ret;
+ break; }
+ case PM_ExclusiveIndicatorHeight: {
+ switch (d->aquaSizeConstrain(opt, widget)) {
+ case QAquaSizeUnknown:
+ case QAquaSizeLarge:
+ GetThemeMetric(kThemeMetricRadioButtonHeight, &ret);
+ break;
+ case QAquaSizeMini:
+ GetThemeMetric(kThemeMetricMiniRadioButtonHeight, &ret);
+ break;
+ case QAquaSizeSmall:
+ GetThemeMetric(kThemeMetricSmallRadioButtonHeight, &ret);
+ break;
+ }
+ break; }
+ case PM_ExclusiveIndicatorWidth: {
+ switch (d->aquaSizeConstrain(opt, widget)) {
+ case QAquaSizeUnknown:
+ case QAquaSizeLarge:
+ GetThemeMetric(kThemeMetricRadioButtonWidth, &ret);
+ break;
+ case QAquaSizeMini:
+ GetThemeMetric(kThemeMetricMiniRadioButtonWidth, &ret);
+ break;
+ case QAquaSizeSmall:
+ GetThemeMetric(kThemeMetricSmallRadioButtonWidth, &ret);
+ break;
+ }
+ ++ret;
+ break; }
+ case PM_MenuVMargin:
+ ret = 4;
+ break;
+ case PM_MenuPanelWidth:
+ ret = 0;
+ break;
+ case PM_ToolTipLabelFrameWidth:
+ ret = 0;
+ break;
+ case PM_SizeGripSize: {
+ QAquaWidgetSize aSize;
+ if (widget && widget->window()->windowType() == Qt::Tool)
+ aSize = QAquaSizeSmall;
+ else
+ aSize = QAquaSizeLarge;
+ const QSize size = qt_aqua_get_known_size(CT_SizeGrip, widget, QSize(), aSize);
+ ret = size.width();
+ break; }
+ case PM_MdiSubWindowFrameWidth:
+ ret = 1;
+ break;
+ case PM_DockWidgetFrameWidth:
+ ret = 2;
+ break;
+ case PM_DockWidgetTitleMargin:
+ ret = 0;
+ break;
+ case PM_DockWidgetSeparatorExtent:
+ ret = 1;
+ break;
+ case PM_ToolBarHandleExtent:
+ ret = 11;
+ break;
+ case PM_ToolBarItemMargin:
+ ret = 0;
+ break;
+ case PM_ToolBarItemSpacing:
+ ret = 4;
+ break;
+ case PM_MessageBoxIconSize:
+ ret = 64;
+ break;
+ case PM_SplitterWidth:
+ ret = qMax(7, QApplication::globalStrut().width());
+ break;
+ case PM_LayoutLeftMargin:
+ case PM_LayoutTopMargin:
+ case PM_LayoutRightMargin:
+ case PM_LayoutBottomMargin:
+ {
+ bool isWindow = false;
+ if (opt) {
+ isWindow = (opt->state & State_Window);
+ } else if (widget) {
+ isWindow = widget->isWindow();
+ }
+
+ if (isWindow) {
+ bool isMetal = widget && widget->testAttribute(Qt::WA_MacBrushedMetal);
+ if (isMetal) {
+ if (metric == PM_LayoutTopMargin) {
+ return_SIZE(9 /* AHIG */, 6 /* guess */, 6 /* guess */);
+ } else if (metric == PM_LayoutBottomMargin) {
+ return_SIZE(18 /* AHIG */, 15 /* guess */, 13 /* guess */);
+ } else {
+ return_SIZE(14 /* AHIG */, 11 /* guess */, 9 /* guess */);
+ }
+ } else {
+ /*
+ AHIG would have (20, 8, 10) here but that makes
+ no sense. It would also have 14 for the top margin
+ but this contradicts both Builder and most
+ applications.
+ */
+ return_SIZE(20, 10, 10); // AHIG
+ }
+ } else {
+ // hack to detect QTabWidget
+ if (widget && widget->parentWidget()
+ && widget->parentWidget()->sizePolicy().controlType() == QSizePolicy::TabWidget) {
+ if (metric == PM_LayoutTopMargin) {
+ /*
+ Builder would have 14 (= 20 - 6) instead of 12,
+ but that makes the tab look disproportionate.
+ */
+ return_SIZE(12, 6, 6); // guess
+ } else {
+ return_SIZE(20 /* Builder */, 8 /* guess */, 8 /* guess */);
+ }
+ } else {
+ /*
+ Child margins are highly inconsistent in AHIG and Builder.
+ */
+ return_SIZE(12, 8, 6); // guess
+ }
+ }
+ }
+ case PM_LayoutHorizontalSpacing:
+ case PM_LayoutVerticalSpacing:
+ return -1;
+ case QStyle::PM_TabBarTabHSpace:
+ switch (d->aquaSizeConstrain(opt, widget)) {
+ case QAquaSizeLarge:
+ case QAquaSizeUnknown:
+ ret = QWindowsStyle::pixelMetric(metric, opt, widget);
+ break;
+ case QAquaSizeSmall:
+ ret = 20;
+ break;
+ case QAquaSizeMini:
+ ret = 16;
+ break;
+ }
+ break;
+ case PM_MenuHMargin:
+ ret = 0;
+ break;
+ default:
+ ret = QWindowsStyle::pixelMetric(metric, opt, widget);
+ break;
+ }
+ return ret;
+}
+
+/*! \reimp */
+QPalette QMacStyle::standardPalette() const
+{
+ QPalette pal = QWindowsStyle::standardPalette();
+ pal.setColor(QPalette::Disabled, QPalette::Dark, QColor(191, 191, 191));
+ pal.setColor(QPalette::Active, QPalette::Dark, QColor(191, 191, 191));
+ pal.setColor(QPalette::Inactive, QPalette::Dark, QColor(191, 191, 191));
+ return pal;
+}
+
+/*! \reimp */
+int QMacStyle::styleHint(StyleHint sh, const QStyleOption *opt, const QWidget *w,
+ QStyleHintReturn *hret) const
+{
+ SInt32 ret = 0;
+ switch (sh) {
+ case SH_Menu_SelectionWrap:
+ ret = false;
+ break;
+ case SH_Menu_KeyboardSearch:
+ ret = true;
+ break;
+ case SH_Menu_SpaceActivatesItem:
+ ret = true;
+ break;
+ case SH_Slider_AbsoluteSetButtons:
+ ret = Qt::LeftButton|Qt::MidButton;
+ break;
+ case SH_Slider_PageSetButtons:
+ ret = 0;
+ break;
+ case SH_ScrollBar_ContextMenu:
+ ret = false;
+ break;
+ case SH_TitleBar_AutoRaise:
+ ret = true;
+ break;
+ case SH_Menu_AllowActiveAndDisabled:
+ ret = false;
+ break;
+ case SH_Menu_SubMenuPopupDelay:
+ ret = 100;
+ break;
+ case SH_ScrollBar_LeftClickAbsolutePosition: {
+ extern bool qt_scrollbar_jump_to_pos; //qapplication_mac.cpp
+ if(QApplication::keyboardModifiers() & Qt::AltModifier)
+ ret = !qt_scrollbar_jump_to_pos;
+ else
+ ret = qt_scrollbar_jump_to_pos;
+ break; }
+ case SH_TabBar_PreferNoArrows:
+ ret = true;
+ break;
+ case SH_LineEdit_PasswordCharacter:
+ ret = kBulletUnicode;
+ break;
+ /*
+ case SH_DialogButtons_DefaultButton:
+ ret = QDialogButtons::Reject;
+ break;
+ */
+ case SH_Menu_SloppySubMenus:
+ ret = true;
+ break;
+ case SH_GroupBox_TextLabelVerticalAlignment:
+ ret = Qt::AlignTop;
+ break;
+ case SH_ScrollView_FrameOnlyAroundContents:
+ if (w && (w->isWindow() || !w->parentWidget() || w->parentWidget()->isWindow())
+ && (w->inherits("QWorkspaceChild")
+#ifdef QT3_SUPPORT
+ || w->inherits("QScrollView")
+#endif
+ ))
+ ret = true;
+ else
+ ret = QWindowsStyle::styleHint(sh, opt, w, hret);
+ break;
+ case SH_Menu_FillScreenWithScroll:
+ ret = false;
+ break;
+ case SH_Menu_Scrollable:
+ ret = true;
+ break;
+ case SH_RichText_FullWidthSelection:
+ ret = true;
+ break;
+ case SH_BlinkCursorWhenTextSelected:
+ ret = false;
+ break;
+ case SH_ScrollBar_StopMouseOverSlider:
+ ret = true;
+ break;
+ case SH_Q3ListViewExpand_SelectMouseType:
+ ret = QEvent::MouseButtonRelease;
+ break;
+ case SH_TabBar_SelectMouseType:
+ if (const QStyleOptionTabBarBaseV2 *opt2 = qstyleoption_cast<const QStyleOptionTabBarBaseV2 *>(opt)) {
+ ret = opt2->documentMode ? QEvent::MouseButtonPress : QEvent::MouseButtonRelease;
+ } else {
+ ret = QEvent::MouseButtonRelease;
+ }
+ break;
+ case SH_ComboBox_Popup:
+ if (const QStyleOptionComboBox *cmb = qstyleoption_cast<const QStyleOptionComboBox *>(opt))
+ ret = !cmb->editable;
+ else
+ ret = 0;
+ break;
+ case SH_Workspace_FillSpaceOnMaximize:
+ ret = true;
+ break;
+ case SH_Widget_ShareActivation:
+ ret = true;
+ break;
+ case SH_Header_ArrowAlignment:
+ ret = Qt::AlignRight;
+ break;
+ case SH_TabBar_Alignment: {
+ if (const QTabWidget *tab = qobject_cast<const QTabWidget*>(w)) {
+ if (tab->documentMode()) {
+ ret = Qt::AlignLeft;
+ break;
+ }
+ }
+ if (const QTabBar *tab = qobject_cast<const QTabBar*>(w)) {
+ if (tab->documentMode()) {
+ ret = Qt::AlignLeft;
+ break;
+ }
+ }
+ ret = Qt::AlignCenter;
+ } break;
+ case SH_UnderlineShortcut:
+ ret = false;
+ break;
+ case SH_ToolTipLabel_Opacity:
+ ret = 242; // About 95%
+ break;
+ case SH_Button_FocusPolicy:
+ ret = Qt::TabFocus;
+ break;
+ case SH_EtchDisabledText:
+ ret = false;
+ break;
+ case SH_FocusFrame_Mask: {
+ ret = true;
+ if(QStyleHintReturnMask *mask = qstyleoption_cast<QStyleHintReturnMask*>(hret)) {
+ const uchar fillR = 192, fillG = 191, fillB = 190;
+ QImage img;
+
+ QSize pixmapSize = opt->rect.size();
+ if (pixmapSize.isValid()) {
+ QPixmap pix(pixmapSize);
+ pix.fill(QColor(fillR, fillG, fillB));
+ QPainter pix_paint(&pix);
+ drawControl(CE_FocusFrame, opt, &pix_paint, w);
+ pix_paint.end();
+ img = pix.toImage();
+ }
+
+ const QRgb *sptr = (QRgb*)img.bits(), *srow;
+ const int sbpl = img.bytesPerLine();
+ const int w = sbpl/4, h = img.height();
+
+ QImage img_mask(img.width(), img.height(), QImage::Format_ARGB32);
+ QRgb *dptr = (QRgb*)img_mask.bits(), *drow;
+ const int dbpl = img_mask.bytesPerLine();
+
+ for (int y = 0; y < h; ++y) {
+ srow = sptr+((y*sbpl)/4);
+ drow = dptr+((y*dbpl)/4);
+ for (int x = 0; x < w; ++x) {
+ const int diff = (((qRed(*srow)-fillR)*(qRed(*srow)-fillR)) +
+ ((qGreen(*srow)-fillG)*((qGreen(*srow)-fillG))) +
+ ((qBlue(*srow)-fillB)*((qBlue(*srow)-fillB))));
+ (*drow++) = (diff < 100) ? 0xffffffff : 0xff000000;
+ ++srow;
+ }
+ }
+ QBitmap qmask = QBitmap::fromImage(img_mask);
+ mask->region = QRegion(qmask);
+ }
+ break; }
+ case SH_TitleBar_NoBorder:
+ ret = 1;
+ break;
+ case SH_RubberBand_Mask:
+ ret = 0;
+ break;
+ case SH_ComboBox_LayoutDirection:
+ ret = Qt::LeftToRight;
+ break;
+ case SH_ItemView_EllipsisLocation:
+ ret = Qt::AlignHCenter;
+ break;
+ case SH_ItemView_ShowDecorationSelected:
+ ret = true;
+ break;
+ case SH_TitleBar_ModifyNotification:
+ ret = false;
+ break;
+ case SH_ScrollBar_RollBetweenButtons:
+ ret = true;
+ break;
+ case SH_WindowFrame_Mask:
+ ret = 1;
+ if (QStyleHintReturnMask *mask = qstyleoption_cast<QStyleHintReturnMask *>(hret)) {
+ mask->region = opt->rect;
+ mask->region -= QRect(opt->rect.left(), opt->rect.top(), 5, 1);
+ mask->region -= QRect(opt->rect.left(), opt->rect.top() + 1, 3, 1);
+ mask->region -= QRect(opt->rect.left(), opt->rect.top() + 2, 2, 1);
+ mask->region -= QRect(opt->rect.left(), opt->rect.top() + 3, 1, 2);
+
+ mask->region -= QRect(opt->rect.right() - 4, opt->rect.top(), 5, 1);
+ mask->region -= QRect(opt->rect.right() - 2, opt->rect.top() + 1, 3, 1);
+ mask->region -= QRect(opt->rect.right() - 1, opt->rect.top() + 2, 2, 1);
+ mask->region -= QRect(opt->rect.right() , opt->rect.top() + 3, 1, 2);
+ }
+ break;
+ case SH_TabBar_ElideMode:
+ ret = Qt::ElideRight;
+ break;
+ case SH_DialogButtonLayout:
+ ret = QDialogButtonBox::MacLayout;
+ break;
+ case SH_FormLayoutWrapPolicy:
+ ret = QFormLayout::DontWrapRows;
+ break;
+ case SH_FormLayoutFieldGrowthPolicy:
+ ret = QFormLayout::FieldsStayAtSizeHint;
+ break;
+ case SH_FormLayoutFormAlignment:
+ ret = Qt::AlignHCenter | Qt::AlignTop;
+ break;
+ case SH_FormLayoutLabelAlignment:
+ ret = Qt::AlignRight;
+ break;
+ case SH_ComboBox_PopupFrameStyle:
+ ret = QFrame::NoFrame | QFrame::Plain;
+ break;
+ case SH_MessageBox_TextInteractionFlags:
+ ret = Qt::TextSelectableByMouse | Qt::LinksAccessibleByMouse | Qt::TextSelectableByKeyboard;
+ break;
+ case SH_SpellCheckUnderlineStyle:
+ ret = QTextCharFormat::DashUnderline;
+ break;
+ case SH_MessageBox_CenterButtons:
+ ret = false;
+ break;
+ case SH_MenuBar_AltKeyNavigation:
+ ret = false;
+ break;
+ case SH_ItemView_MovementWithoutUpdatingSelection:
+ ret = false;
+ break;
+ case SH_FocusFrame_AboveWidget:
+ ret = true;
+ break;
+ case SH_WizardStyle:
+ ret = QWizard::MacStyle;
+ break;
+ case SH_ItemView_ArrowKeysNavigateIntoChildren:
+ ret = false;
+ break;
+ case SH_Menu_FlashTriggeredItem:
+ ret = true;
+ break;
+ case SH_Menu_FadeOutOnHide:
+ ret = true;
+ break;
+ case SH_Menu_Mask:
+ if (opt) {
+ if (QStyleHintReturnMask *mask = qstyleoption_cast<QStyleHintReturnMask*>(hret)) {
+ ret = true;
+ HIRect menuRect = CGRectMake(opt->rect.x(), opt->rect.y() + 4,
+ opt->rect.width(), opt->rect.height() - 8);
+ HIThemeMenuDrawInfo mdi;
+ mdi.version = 0;
+ if (w && qobject_cast<QMenu *>(w->parentWidget()))
+ mdi.menuType = kThemeMenuTypeHierarchical;
+ else
+ mdi.menuType = kThemeMenuTypePopUp;
+ QCFType<HIShapeRef> shape;
+ HIThemeGetMenuBackgroundShape(&menuRect, &mdi, &shape);
+ mask->region = QRegion::fromHIShapeRef(shape);
+ }
+ }
+ break;
+ case SH_ItemView_PaintAlternatingRowColorsForEmptyArea:
+ ret = true;
+ break;
+ case SH_TabBar_CloseButtonPosition:
+ ret = QTabBar::LeftSide;
+ break;
+ case SH_DockWidget_ButtonsHaveFrame:
+ ret = false;
+ break;
+ default:
+ ret = QWindowsStyle::styleHint(sh, opt, w, hret);
+ break;
+ }
+ return ret;
+}
+
+/*! \reimp */
+QPixmap QMacStyle::generatedIconPixmap(QIcon::Mode iconMode, const QPixmap &pixmap,
+ const QStyleOption *opt) const
+{
+ switch (iconMode) {
+ case QIcon::Disabled: {
+ QImage img = pixmap.toImage().convertToFormat(QImage::Format_ARGB32);
+ int imgh = img.height();
+ int imgw = img.width();
+ QRgb pixel;
+ for (int y = 0; y < imgh; ++y) {
+ for (int x = 0; x < imgw; ++x) {
+ pixel = img.pixel(x, y);
+ img.setPixel(x, y, qRgba(qRed(pixel), qGreen(pixel), qBlue(pixel),
+ qAlpha(pixel) / 2));
+ }
+ }
+ return QPixmap::fromImage(img);
+ }
+ default:
+ ;
+ }
+ return QWindowsStyle::generatedIconPixmap(iconMode, pixmap, opt);
+}
+
+
+/*! \reimp */
+QPixmap QMacStyle::standardPixmap(StandardPixmap standardPixmap, const QStyleOption *opt,
+ const QWidget *widget) const
+{
+ // The default implementation of QStyle::standardIconImplementation() is to call standardPixmap()
+ // I don't want infinite recursion so if we do get in that situation, just return the Window's
+ // standard pixmap instead (since there is no mac-specific icon then). This should be fine until
+ // someone changes how Windows standard
+ // pixmap works.
+ static bool recursionGuard = false;
+
+ if (recursionGuard)
+ return QWindowsStyle::standardPixmap(standardPixmap, opt, widget);
+
+ recursionGuard = true;
+ QIcon icon = standardIconImplementation(standardPixmap, opt, widget);
+ recursionGuard = false;
+ int size;
+ switch (standardPixmap) {
+ default:
+ size = 32;
+ break;
+ case SP_MessageBoxCritical:
+ case SP_MessageBoxQuestion:
+ case SP_MessageBoxInformation:
+ case SP_MessageBoxWarning:
+ size = 64;
+ break;
+ }
+ return icon.pixmap(size, size);
+}
+/*!
+ \enum QMacStyle::FocusRectPolicy
+
+ This type is used to signify a widget's focus rectangle policy.
+
+ \value FocusEnabled show a focus rectangle when the widget has focus.
+ \value FocusDisabled never show a focus rectangle for the widget.
+ \value FocusDefault show a focus rectangle when the widget has
+ focus and the widget is a QSpinWidget, QDateTimeEdit, QLineEdit,
+ QListBox, QListView, editable QTextEdit, or one of their
+ subclasses.
+*/
+
+/*!
+ \obsolete
+ Sets the focus rectangle policy of \a w. The \a policy can be one of
+ \l{QMacStyle::FocusRectPolicy}.
+
+ This is now simply an interface to the Qt::WA_MacShowFocusRect attribute and the
+ FocusDefault value does nothing anymore. If you want to set a widget back
+ to its default value, you must save the old value of the attribute before
+ you change it.
+
+ \sa focusRectPolicy() QWidget::setAttribute()
+*/
+void QMacStyle::setFocusRectPolicy(QWidget *w, FocusRectPolicy policy)
+{
+ switch (policy) {
+ case FocusDefault:
+ break;
+ case FocusEnabled:
+ case FocusDisabled:
+ w->setAttribute(Qt::WA_MacShowFocusRect, policy == FocusEnabled);
+ break;
+ }
+}
+
+/*!
+ \obsolete
+ Returns the focus rectangle policy for the widget \a w.
+
+ The focus rectangle policy can be one of \l{QMacStyle::FocusRectPolicy}.
+
+ In 4.3 and up this function will simply test for the
+ Qt::WA_MacShowFocusRect attribute and will never return
+ QMacStyle::FocusDefault.
+
+ \sa setFocusRectPolicy(), QWidget::testAttribute()
+*/
+QMacStyle::FocusRectPolicy QMacStyle::focusRectPolicy(const QWidget *w)
+{
+ return w->testAttribute(Qt::WA_MacShowFocusRect) ? FocusEnabled : FocusDisabled;
+}
+
+/*!
+ \obsolete
+
+ Call QWidget::setAttribute() with Qt::WA_MacMiniSize, Qt::WA_MacSmallSize,
+ or Qt::WA_MacNormalSize instead.
+*/
+void QMacStyle::setWidgetSizePolicy(const QWidget *widget, WidgetSizePolicy policy)
+{
+ QWidget *wadget = const_cast<QWidget *>(widget);
+ wadget->setAttribute(Qt::WA_MacNormalSize, policy == SizeLarge);
+ wadget->setAttribute(Qt::WA_MacSmallSize, policy == SizeSmall);
+ wadget->setAttribute(Qt::WA_MacMiniSize, policy == SizeMini);
+}
+
+/*!
+ \obsolete
+
+ Call QWidget::testAttribute() with Qt::WA_MacMiniSize, Qt::WA_MacSmallSize,
+ or Qt::WA_MacNormalSize instead.
+*/
+QMacStyle::WidgetSizePolicy QMacStyle::widgetSizePolicy(const QWidget *widget)
+{
+ while (widget) {
+ if (widget->testAttribute(Qt::WA_MacMiniSize)) {
+ return SizeMini;
+ } else if (widget->testAttribute(Qt::WA_MacSmallSize)) {
+ return SizeSmall;
+ } else if (widget->testAttribute(Qt::WA_MacNormalSize)) {
+ return SizeLarge;
+ }
+ widget = widget->parentWidget();
+ }
+ return SizeDefault;
+}
+
+/*! \reimp */
+void QMacStyle::drawPrimitive(PrimitiveElement pe, const QStyleOption *opt, QPainter *p,
+ const QWidget *w) const
+{
+ ThemeDrawState tds = d->getDrawState(opt->state);
+ QMacCGContext cg(p);
+ switch (pe) {
+ case PE_IndicatorArrowUp:
+ case PE_IndicatorArrowDown:
+ case PE_IndicatorArrowRight:
+ case PE_IndicatorArrowLeft: {
+ p->save();
+ p->setRenderHint(QPainter::Antialiasing);
+ int xOffset = opt->direction == Qt::LeftToRight ? 2 : -1;
+ QMatrix matrix;
+ matrix.translate(opt->rect.center().x() + xOffset, opt->rect.center().y() + 2);
+ QPainterPath path;
+ switch(pe) {
+ default:
+ case PE_IndicatorArrowDown:
+ break;
+ case PE_IndicatorArrowUp:
+ matrix.rotate(180);
+ break;
+ case PE_IndicatorArrowLeft:
+ matrix.rotate(90);
+ break;
+ case PE_IndicatorArrowRight:
+ matrix.rotate(-90);
+ break;
+ }
+ path.moveTo(0, 5);
+ path.lineTo(-4, -3);
+ path.lineTo(4, -3);
+ p->setMatrix(matrix);
+ p->setPen(Qt::NoPen);
+ p->setBrush(QColor(0, 0, 0, 135));
+ p->drawPath(path);
+ p->restore();
+ break; }
+ case PE_FrameTabBarBase:
+ if (const QStyleOptionTabBarBaseV2 *tbb
+ = qstyleoption_cast<const QStyleOptionTabBarBaseV2 *>(opt)) {
+ if (tbb->documentMode) {
+ p->save();
+ drawTabBase(p, tbb, w);
+ p->restore();
+ return;
+ }
+
+ QRegion region(tbb->rect);
+ region -= tbb->tabBarRect;
+ p->save();
+ p->setClipRegion(region);
+ QStyleOptionTabWidgetFrame twf;
+ twf.QStyleOption::operator=(*tbb);
+ twf.shape = tbb->shape;
+ switch (getTabDirection(twf.shape)) {
+ case kThemeTabNorth:
+ twf.rect = twf.rect.adjusted(0, 0, 0, 10);
+ break;
+ case kThemeTabSouth:
+ twf.rect = twf.rect.adjusted(0, -10, 0, 0);
+ break;
+ case kThemeTabWest:
+ twf.rect = twf.rect.adjusted(0, 0, 10, 0);
+ break;
+ case kThemeTabEast:
+ twf.rect = twf.rect.adjusted(0, -10, 0, 0);
+ break;
+ }
+ drawPrimitive(PE_FrameTabWidget, &twf, p, w);
+ p->restore();
+ }
+ break;
+ case PE_PanelTipLabel:
+ p->fillRect(opt->rect, opt->palette.brush(QPalette::ToolTipBase));
+ break;
+ case PE_FrameGroupBox:
+ if (const QStyleOptionFrame *groupBox = qstyleoption_cast<const QStyleOptionFrame *>(opt)) {
+ const QStyleOptionFrameV2 *frame2 = qstyleoption_cast<const QStyleOptionFrameV2 *>(opt);
+ if (frame2 && frame2->features & QStyleOptionFrameV2::Flat) {
+ QWindowsStyle::drawPrimitive(pe, groupBox, p, w);
+ } else {
+ HIThemeGroupBoxDrawInfo gdi;
+ gdi.version = qt_mac_hitheme_version;
+ gdi.state = tds;
+ if (w && qobject_cast<QGroupBox *>(w->parentWidget()))
+ gdi.kind = kHIThemeGroupBoxKindSecondary;
+ else
+ gdi.kind = kHIThemeGroupBoxKindPrimary;
+ HIRect hirect = qt_hirectForQRect(opt->rect);
+ HIThemeDrawGroupBox(&hirect, &gdi, cg, kHIThemeOrientationNormal);
+ }
+ }
+ break;
+ case PE_IndicatorToolBarSeparator: {
+ QPainterPath path;
+ if (opt->state & State_Horizontal) {
+ int xpoint = opt->rect.center().x();
+ path.moveTo(xpoint + 0.5, opt->rect.top() + 1);
+ path.lineTo(xpoint + 0.5, opt->rect.bottom());
+ } else {
+ int ypoint = opt->rect.center().y();
+ path.moveTo(opt->rect.left() + 2 , ypoint + 0.5);
+ path.lineTo(opt->rect.right() + 1, ypoint + 0.5);
+ }
+ QPainterPathStroker theStroker;
+ theStroker.setCapStyle(Qt::FlatCap);
+ theStroker.setDashPattern(QVector<qreal>() << 1 << 2);
+ path = theStroker.createStroke(path);
+ p->fillPath(path, QColor(0, 0, 0, 119));
+ }
+ break;
+ case PE_FrameWindow:
+ break;
+ case PE_IndicatorDockWidgetResizeHandle: {
+ // The docwidget resize handle is drawn as a one-pixel wide line.
+ p->save();
+ if (opt->state & State_Horizontal) {
+ p->setPen(QColor(160, 160, 160));
+ p->drawLine(opt->rect.topLeft(), opt->rect.topRight());
+ } else {
+ p->setPen(QColor(145, 145, 145));
+ p->drawLine(opt->rect.topRight(), opt->rect.bottomRight());
+ }
+ p->restore();
+ } break;
+ case PE_IndicatorToolBarHandle: {
+ p->save();
+ QPainterPath path;
+ int x = opt->rect.x() + 6;
+ int y = opt->rect.y() + 5;
+ static const int RectHeight = 2;
+ if (opt->state & State_Horizontal) {
+ while (y < opt->rect.height() - RectHeight - 6) {
+ path.moveTo(x, y);
+ path.addRect(x, y, RectHeight, RectHeight);
+ y += 6;
+ }
+ } else {
+ while (x < opt->rect.width() - RectHeight - 6) {
+ path.moveTo(x, y);
+ path.addRect(x, y, RectHeight, RectHeight);
+ x += 6;
+ }
+ }
+ p->setPen(Qt::NoPen);
+ QColor dark = opt->palette.dark().color();
+ dark.setAlphaF(0.75);
+ QColor light = opt->palette.light().color();
+ light.setAlphaF(0.6);
+ p->fillPath(path, light);
+ p->save();
+ p->translate(1, 1);
+ p->fillPath(path, dark);
+ p->restore();
+ p->translate(3, 3);
+ p->fillPath(path, light);
+ p->translate(1, 1);
+ p->fillPath(path, dark);
+ p->restore();
+
+ break;
+ }
+ case PE_IndicatorHeaderArrow:
+ if (const QStyleOptionHeader *header = qstyleoption_cast<const QStyleOptionHeader *>(opt)) {
+ // In HITheme, up is down, down is up and hamburgers eat people.
+ if (header->sortIndicator != QStyleOptionHeader::None)
+ drawPrimitive(
+ (header->sortIndicator == QStyleOptionHeader::SortDown) ?
+ PE_IndicatorArrowUp : PE_IndicatorArrowDown, header, p, w);
+ }
+ break;
+ case PE_IndicatorMenuCheckMark: {
+ const int checkw = 8;
+ const int checkh = 8;
+ const int xoff = qMax(0, (opt->rect.width() - checkw) / 2);
+ const int yoff = qMax(0, (opt->rect.width() - checkh) / 2);
+ const int x1 = xoff + opt->rect.x();
+ const int y1 = yoff + opt->rect.y() + checkw/2;
+ const int x2 = xoff + opt->rect.x() + checkw/4;
+ const int y2 = yoff + opt->rect.y() + checkh;
+ const int x3 = xoff + opt->rect.x() + checkw;
+ const int y3 = yoff + opt->rect.y();
+
+ QVector<QLineF> a(2);
+ a << QLineF(x1, y1, x2, y2);
+ a << QLineF(x2, y2, x3, y3);
+ if (opt->palette.currentColorGroup() == QPalette::Active)
+ p->setPen(QPen(Qt::white, 3));
+ else
+ p->setPen(QPen(QColor(100, 100, 100), 3));
+ p->save();
+ p->setRenderHint(QPainter::Antialiasing);
+ p->drawLines(a);
+ p->restore();
+ break; }
+ case PE_IndicatorViewItemCheck:
+ case PE_Q3CheckListExclusiveIndicator:
+ case PE_Q3CheckListIndicator:
+ case PE_IndicatorRadioButton:
+ case PE_IndicatorCheckBox: {
+ bool drawColorless = (!(opt->state & State_Active))
+ && opt->palette.currentColorGroup() == QPalette::Active;
+ HIThemeButtonDrawInfo bdi;
+ bdi.version = qt_mac_hitheme_version;
+ bdi.state = tds;
+ if (drawColorless && tds == kThemeStateInactive)
+ bdi.state = kThemeStateActive;
+ bdi.adornment = kThemeDrawIndicatorOnly;
+ if (opt->state & State_HasFocus)
+ bdi.adornment |= kThemeAdornmentFocus;
+ bool isRadioButton = (pe == PE_Q3CheckListExclusiveIndicator
+ || pe == PE_IndicatorRadioButton);
+ switch (d->aquaSizeConstrain(opt, w)) {
+ case QAquaSizeUnknown:
+ case QAquaSizeLarge:
+ if (isRadioButton)
+ bdi.kind = kThemeRadioButton;
+ else
+ bdi.kind = kThemeCheckBox;
+ break;
+ case QAquaSizeMini:
+ if (isRadioButton)
+ bdi.kind = kThemeMiniRadioButton;
+ else
+ bdi.kind = kThemeMiniCheckBox;
+ break;
+ case QAquaSizeSmall:
+ if (isRadioButton)
+ bdi.kind = kThemeSmallRadioButton;
+ else
+ bdi.kind = kThemeSmallCheckBox;
+ break;
+ }
+ if (opt->state & State_NoChange)
+ bdi.value = kThemeButtonMixed;
+ else if (opt->state & State_On)
+ bdi.value = kThemeButtonOn;
+ else
+ bdi.value = kThemeButtonOff;
+ HIRect macRect;
+ if (pe == PE_Q3CheckListExclusiveIndicator || pe == PE_Q3CheckListIndicator)
+ macRect = qt_hirectForQRect(opt->rect);
+ else
+ macRect = qt_hirectForQRect(opt->rect);
+ if (!drawColorless)
+ HIThemeDrawButton(&macRect, &bdi, cg, kHIThemeOrientationNormal, 0);
+ else
+ d->drawColorlessButton(macRect, &bdi, p, opt);
+ break; }
+ case PE_FrameFocusRect:
+ // Use the our own focus widget stuff.
+ break;
+ case PE_IndicatorBranch: {
+ if (!(opt->state & State_Children))
+ break;
+ HIThemeButtonDrawInfo bi;
+ bi.version = qt_mac_hitheme_version;
+ bi.state = tds;
+ if (tds == kThemeStateInactive && opt->palette.currentColorGroup() == QPalette::Active)
+ bi.state = kThemeStateActive;
+ if (opt->state & State_Sunken)
+ bi.state |= kThemeStatePressed;
+ bi.kind = kThemeDisclosureButton;
+ if (opt->state & State_Open)
+ bi.value = kThemeDisclosureDown;
+ else
+ bi.value = opt->direction == Qt::LeftToRight ? kThemeDisclosureRight : kThemeDisclosureLeft;
+ bi.adornment = kThemeAdornmentNone;
+ HIRect hirect = qt_hirectForQRect(opt->rect);
+ HIThemeDrawButton(&hirect, &bi, cg, kHIThemeOrientationNormal, 0);
+ break; }
+ case PE_Frame: {
+ QPen oldPen = p->pen();
+ QPen newPen;
+ newPen.setBrush(opt->palette.dark());
+ p->setPen(newPen);
+ p->drawRect(opt->rect.adjusted(0, 0, -1, -1));
+ p->setPen(oldPen);
+ break; }
+ case PE_FrameLineEdit:
+ if (const QStyleOptionFrame *frame = qstyleoption_cast<const QStyleOptionFrame *>(opt)) {
+ if (frame->state & State_Sunken) {
+ QColor baseColor(frame->palette.background().color());
+ HIThemeFrameDrawInfo fdi;
+ fdi.version = qt_mac_hitheme_version;
+ fdi.state = tds;
+ SInt32 frame_size;
+ if (pe == PE_FrameLineEdit) {
+ fdi.kind = kHIThemeFrameTextFieldSquare;
+ GetThemeMetric(kThemeMetricEditTextFrameOutset, &frame_size);
+ if ((frame->state & State_ReadOnly) || !(frame->state & State_Enabled))
+ fdi.state = kThemeStateInactive;
+ } else {
+ baseColor = QColor(150, 150, 150); //hardcoded since no query function --Sam
+ fdi.kind = kHIThemeFrameListBox;
+ GetThemeMetric(kThemeMetricListBoxFrameOutset, &frame_size);
+ }
+ fdi.isFocused = (frame->state & State_HasFocus);
+ int lw = frame->lineWidth;
+ if (lw <= 0)
+ lw = pixelMetric(PM_DefaultFrameWidth, frame, w);
+ { //clear to base color
+ p->save();
+ p->setPen(QPen(baseColor, lw));
+ p->setBrush(Qt::NoBrush);
+ p->drawRect(frame->rect);
+ p->restore();
+ }
+ HIRect hirect = qt_hirectForQRect(frame->rect,
+ QRect(frame_size, frame_size,
+ frame_size * 2, frame_size * 2));
+
+ HIThemeDrawFrame(&hirect, &fdi, cg, kHIThemeOrientationNormal);
+ } else {
+ QWindowsStyle::drawPrimitive(pe, opt, p, w);
+ }
+ }
+ break;
+ case PE_PanelLineEdit:
+ QWindowsStyle::drawPrimitive(pe, opt, p, w);
+ break;
+ case PE_FrameTabWidget:
+ if (const QStyleOptionTabWidgetFrame *twf
+ = qstyleoption_cast<const QStyleOptionTabWidgetFrame *>(opt)) {
+ HIRect hirect = qt_hirectForQRect(twf->rect);
+#if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4)
+ if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_4) {
+ HIThemeTabPaneDrawInfo tpdi;
+ tpdi.version = qt_mac_hitheme_tab_version();
+ tpdi.state = tds;
+ tpdi.direction = getTabDirection(twf->shape);
+ tpdi.size = kHIThemeTabSizeNormal;
+ if (tpdi.version == 1) {
+ tpdi.kind = kHIThemeTabKindNormal;
+ tpdi.adornment = kHIThemeTabPaneAdornmentNormal;
+ }
+ HIThemeDrawTabPane(&hirect, &tpdi, cg, kHIThemeOrientationNormal);
+ } else
+#endif
+ {
+ HIThemeGroupBoxDrawInfo gdi;
+ gdi.version = qt_mac_hitheme_version;
+ gdi.state = tds;
+ gdi.kind = kHIThemeGroupBoxKindSecondary;
+ HIThemeDrawGroupBox(&hirect, &gdi, cg, kHIThemeOrientationNormal);
+ }
+ }
+ break;
+ case PE_PanelScrollAreaCorner: {
+ const QBrush brush(qApp->palette().brush(QPalette::Base));
+ p->fillRect(opt->rect, brush);
+ p->setPen(QPen(QColor(217, 217, 217)));
+ p->drawLine(opt->rect.topLeft(), opt->rect.topRight());
+ p->drawLine(opt->rect.topLeft(), opt->rect.bottomLeft());
+ } break;
+ case PE_FrameStatusBarItem:
+ QCommonStyle::drawPrimitive(pe, opt, p, w);
+ break;
+ case PE_IndicatorTabClose: {
+ bool hover = (opt->state & State_MouseOver);
+ bool selected = (opt->state & State_Selected);
+ bool active = (opt->state & State_Active);
+ drawTabCloseButton(p, hover, active, selected);
+ } break;
+ case PE_PanelStatusBar: {
+ if (QSysInfo::MacintoshVersion <= QSysInfo::MV_10_4) {
+ QWindowsStyle::drawPrimitive(pe, opt, p, w);
+ break;
+ }
+
+ // Use the Leopard style only if the status bar is the status bar for a
+ // QMainWindow with a unifed toolbar.
+ if (w == 0 || w->parent() == 0 || qobject_cast<QMainWindow *>(w->parent()) == 0 ||
+ qobject_cast<QMainWindow *>(w->parent())->unifiedTitleAndToolBarOnMac() == false ) {
+ QWindowsStyle::drawPrimitive(pe, opt, p, w);
+ break;
+ }
+
+ // Fill the status bar with the titlebar gradient.
+ QLinearGradient linearGrad(0, opt->rect.top(), 0, opt->rect.bottom());
+ if (opt->state & QStyle::State_Active) {
+ linearGrad.setColorAt(0, titlebarGradientActiveBegin);
+ linearGrad.setColorAt(1, titlebarGradientActiveEnd);
+ } else {
+ linearGrad.setColorAt(0, titlebarGradientInactiveBegin);
+ linearGrad.setColorAt(1, titlebarGradientInactiveEnd);
+ }
+ p->fillRect(opt->rect, linearGrad);
+
+ // Draw the black separator line at the top of the status bar.
+ if (opt->state & QStyle::State_Active)
+ p->setPen(titlebarSeparatorLineActive);
+ else
+ p->setPen(titlebarSeparatorLineInactive);
+ p->drawLine(opt->rect.left(), opt->rect.top(), opt->rect.right(), opt->rect.top());
+
+ break;
+ }
+
+ default:
+ QWindowsStyle::drawPrimitive(pe, opt, p, w);
+ break;
+ }
+}
+
+static inline QPixmap darkenPixmap(const QPixmap &pixmap)
+{
+ QImage img = pixmap.toImage().convertToFormat(QImage::Format_ARGB32);
+ int imgh = img.height();
+ int imgw = img.width();
+ int h, s, v, a;
+ QRgb pixel;
+ for (int y = 0; y < imgh; ++y) {
+ for (int x = 0; x < imgw; ++x) {
+ pixel = img.pixel(x, y);
+ a = qAlpha(pixel);
+ QColor hsvColor(pixel);
+ hsvColor.getHsv(&h, &s, &v);
+ s = qMin(100, s * 2);
+ v = v / 2;
+ hsvColor.setHsv(h, s, v);
+ pixel = hsvColor.rgb();
+ img.setPixel(x, y, qRgba(qRed(pixel), qGreen(pixel), qBlue(pixel), a));
+ }
+ }
+ return QPixmap::fromImage(img);
+}
+
+
+
+/*! \reimp */
+void QMacStyle::drawControl(ControlElement ce, const QStyleOption *opt, QPainter *p,
+ const QWidget *w) const
+{
+ ThemeDrawState tds = d->getDrawState(opt->state);
+ QMacCGContext cg(p);
+ switch (ce) {
+ case CE_HeaderSection:
+ if (const QStyleOptionHeader *header = qstyleoption_cast<const QStyleOptionHeader *>(opt)) {
+ HIThemeButtonDrawInfo bdi;
+ bdi.version = qt_mac_hitheme_version;
+ State flags = header->state;
+ QRect ir = header->rect;
+ bdi.kind = kThemeListHeaderButton;
+ bdi.adornment = kThemeAdornmentNone;
+ bdi.state = kThemeStateActive;
+
+ if (flags & State_On)
+ bdi.value = kThemeButtonOn;
+ else
+ bdi.value = kThemeButtonOff;
+
+ if (header->orientation == Qt::Horizontal){
+ switch (header->position) {
+ case QStyleOptionHeader::Beginning:
+ break;
+ case QStyleOptionHeader::Middle:
+ case QStyleOptionHeader::End:
+ ir.adjust(-1, 0, 0, 0);
+ break;
+ default:
+ break;
+ }
+
+ if (header->position != QStyleOptionHeader::Beginning
+ && header->position != QStyleOptionHeader::OnlyOneSection) {
+ bdi.adornment = header->direction == Qt::LeftToRight
+ ? kThemeAdornmentHeaderButtonLeftNeighborSelected
+ : kThemeAdornmentHeaderButtonRightNeighborSelected;
+ }
+ }
+
+ if (flags & State_Active) {
+ if (!(flags & State_Enabled))
+ bdi.state = kThemeStateUnavailable;
+ else if (flags & State_Sunken)
+ bdi.state = kThemeStatePressed;
+ } else {
+ if (flags & State_Enabled)
+ bdi.state = kThemeStateInactive;
+ else
+ bdi.state = kThemeStateUnavailableInactive;
+ }
+
+ if (header->sortIndicator != QStyleOptionHeader::None) {
+ bdi.value = kThemeButtonOn;
+ if (header->sortIndicator == QStyleOptionHeader::SortDown)
+ bdi.adornment = kThemeAdornmentHeaderButtonSortUp;
+ }
+ if (flags & State_HasFocus)
+ bdi.adornment = kThemeAdornmentFocus;
+
+ ir = visualRect(header->direction, header->rect, ir);
+ HIRect bounds = qt_hirectForQRect(ir);
+
+ bool noVerticalHeader = true;
+ if (w)
+ if (const QTableView *table = qobject_cast<const QTableView *>(w->parentWidget()))
+ noVerticalHeader = !table->verticalHeader()->isVisible();
+
+ bool drawTopBorder = header->orientation == Qt::Horizontal;
+ bool drawLeftBorder = header->orientation == Qt::Vertical
+ || header->position == QStyleOptionHeader::OnlyOneSection
+ || (header->position == QStyleOptionHeader::Beginning && noVerticalHeader);
+ d->drawTableHeader(bounds, drawTopBorder, drawLeftBorder, bdi, p);
+ }
+ break;
+ case CE_HeaderLabel:
+ if (const QStyleOptionHeader *header = qstyleoption_cast<const QStyleOptionHeader *>(opt)) {
+ QRect textr = header->rect;
+ if (!header->icon.isNull()) {
+ QIcon::Mode mode = QIcon::Disabled;
+ if (opt->state & State_Enabled)
+ mode = QIcon::Normal;
+ QPixmap pixmap = header->icon.pixmap(pixelMetric(PM_SmallIconSize), mode);
+
+ QRect pixr = header->rect;
+ pixr.setY(header->rect.center().y() - (pixmap.height() - 1) / 2);
+ drawItemPixmap(p, pixr, Qt::AlignVCenter, pixmap);
+ textr.translate(pixmap.width() + 2, 0);
+ }
+
+ drawItemText(p, textr, header->textAlignment | Qt::AlignVCenter, header->palette,
+ header->state & State_Enabled, header->text, QPalette::ButtonText);
+ }
+ break;
+ case CE_ToolButtonLabel:
+ if (const QStyleOptionToolButton *tb = qstyleoption_cast<const QStyleOptionToolButton *>(opt)) {
+ QStyleOptionToolButton myTb = *tb;
+ myTb.state &= ~State_AutoRaise;
+ if (w && qobject_cast<QToolBar *>(w->parentWidget())) {
+ QRect cr = tb->rect;
+ int shiftX = 0;
+ int shiftY = 0;
+ if (tb->state & (State_Sunken | State_On)) {
+ shiftX = pixelMetric(PM_ButtonShiftHorizontal, tb, w);
+ shiftY = pixelMetric(PM_ButtonShiftVertical, tb, w);
+ }
+ // The down state is special for QToolButtons in a toolbar on the Mac
+ // The text is a bit bolder and gets a drop shadow and the icons are also darkened.
+ // This doesn't really fit into any particular case in QIcon, so we
+ // do the majority of the work ourselves.
+ if (tb->state & State_Sunken
+ && !(tb->features & QStyleOptionToolButton::Arrow)) {
+ Qt::ToolButtonStyle tbstyle = tb->toolButtonStyle;
+ if (tb->icon.isNull() && !tb->text.isEmpty())
+ tbstyle = Qt::ToolButtonTextOnly;
+
+ switch (tbstyle) {
+ case Qt::ToolButtonTextOnly:
+ drawItemText(p, cr, Qt::AlignCenter, tb->palette,
+ tb->state & State_Enabled, tb->text);
+ break;
+ case Qt::ToolButtonIconOnly:
+ case Qt::ToolButtonTextBesideIcon:
+ case Qt::ToolButtonTextUnderIcon: {
+ QRect pr = cr;
+ QIcon::Mode iconMode = (tb->state & State_Enabled) ? QIcon::Normal
+ : QIcon::Disabled;
+ QIcon::State iconState = (tb->state & State_On) ? QIcon::On
+ : QIcon::Off;
+ QPixmap pixmap = tb->icon.pixmap(tb->rect.size().boundedTo(tb->iconSize), iconMode, iconState);
+
+ // Draw the text if it's needed.
+ if (tb->toolButtonStyle != Qt::ToolButtonIconOnly) {
+ int alignment = 0;
+ if (tb->toolButtonStyle == Qt::ToolButtonTextUnderIcon) {
+ pr.setHeight(pixmap.size().height() + 6);
+ cr.adjust(0, pr.bottom(), 0, -3);
+ alignment |= Qt::AlignCenter;
+ } else {
+ pr.setWidth(pixmap.width() + 8);
+ cr.adjust(pr.right(), 0, 0, 0);
+ alignment |= Qt::AlignLeft | Qt::AlignVCenter;
+ }
+ cr.translate(shiftX, shiftY);
+ drawItemText(p, cr, alignment, tb->palette,
+ tb->state & State_Enabled, tb->text);
+ cr.adjust(0, 3, 0, -3); // the drop shadow
+ drawItemText(p, cr, alignment, tb->palette,
+ tb->state & State_Enabled, tb->text);
+ }
+ pr.translate(shiftX, shiftY);
+ pixmap = darkenPixmap(pixmap);
+ drawItemPixmap(p, pr, Qt::AlignCenter, pixmap);
+ break; }
+ }
+ } else {
+ QWindowsStyle::drawControl(ce, &myTb, p, w);
+ }
+ } else {
+ QWindowsStyle::drawControl(ce, &myTb, p, w);
+ }
+ }
+ break;
+ case CE_ToolBoxTabShape:
+ QCommonStyle::drawControl(ce, opt, p, w);
+ break;
+ case CE_PushButtonBevel:
+ if (const QStyleOptionButton *btn = ::qstyleoption_cast<const QStyleOptionButton *>(opt)) {
+ if (!(btn->state & (State_Raised | State_Sunken | State_On)))
+ break;
+
+ if (btn->features & QStyleOptionButton::CommandLinkButton) {
+ QWindowsStyle::drawControl(ce, opt, p, w);
+ break;
+ }
+
+ HIThemeButtonDrawInfo bdi;
+ d->initHIThemePushButton(btn, w, tds, &bdi);
+ if (btn->features & QStyleOptionButton::DefaultButton
+ && d->animatable(QMacStylePrivate::AquaPushButton, w)) {
+ bdi.adornment |= kThemeAdornmentDefault;
+ bdi.animation.time.start = d->defaultButtonStart;
+ bdi.animation.time.current = CFAbsoluteTimeGetCurrent();
+ if (d->timerID <= -1)
+ QMetaObject::invokeMethod(d, "startAnimationTimer", Qt::QueuedConnection);
+ }
+ // Unlike Carbon, we want the button to always be drawn inside its bounds.
+ // Therefore, make the button a bit smaller, so that even if it got focus,
+ // the focus 'shadow' will be inside.
+ HIRect newRect = qt_hirectForQRect(btn->rect);
+ if (bdi.kind == kThemePushButton || bdi.kind == kThemePushButtonSmall) {
+ newRect.origin.x += PushButtonLeftOffset;
+ newRect.origin.y += PushButtonTopOffset;
+ newRect.size.width -= PushButtonRightOffset;
+ newRect.size.height -= PushButtonBottomOffset;
+ } else if (bdi.kind == kThemePushButtonMini) {
+ newRect.origin.x += PushButtonLeftOffset - 2;
+ newRect.origin.y += PushButtonTopOffset;
+ newRect.size.width -= PushButtonRightOffset - 4;
+ }
+ HIThemeDrawButton(&newRect, &bdi, cg, kHIThemeOrientationNormal, 0);
+
+ if (btn->features & QStyleOptionButton::HasMenu) {
+ int mbi = pixelMetric(QStyle::PM_MenuButtonIndicator, btn, w);
+ QRect ir = btn->rect;
+ HIRect arrowRect = CGRectMake(ir.right() - mbi - PushButtonRightOffset,
+ ir.height() / 2 - 4, mbi, ir.height() / 2);
+ bool drawColorless = btn->palette.currentColorGroup() == QPalette::Active;
+ if (drawColorless && tds == kThemeStateInactive)
+ tds = kThemeStateActive;
+
+ HIThemePopupArrowDrawInfo pdi;
+ pdi.version = qt_mac_hitheme_version;
+ pdi.state = tds;
+ pdi.orientation = kThemeArrowDown;
+ if (arrowRect.size.width < 8.)
+ pdi.size = kThemeArrow5pt;
+ else
+ pdi.size = kThemeArrow9pt;
+ HIThemeDrawPopupArrow(&arrowRect, &pdi, cg, kHIThemeOrientationNormal);
+ }
+ }
+ break;
+ case CE_PushButtonLabel:
+ if (const QStyleOptionButton *btn = qstyleoption_cast<const QStyleOptionButton *>(opt)) {
+ // We really don't want the label to be drawn the same as on
+ // windows style if it has an icon and text, then it should be more like a
+ // tab. So, cheat a little here. However, if it *is* only an icon
+ // the windows style works great, so just use that implementation.
+ bool hasMenu = btn->features & QStyleOptionButton::HasMenu;
+ bool hasIcon = !btn->icon.isNull();
+ bool hasText = !btn->text.isEmpty();
+ if (!hasIcon && !hasMenu) {
+ // ### this is really overly difficult, simplify.
+ // It basically tries to get the right font for "small" and "mini" icons.
+ QFont oldFont = p->font();
+ QFont newFont = qt_app_fonts_hash()->value("QPushButton", QFont());
+ ThemeFontID themeId = kThemePushButtonFont;
+ if (oldFont == newFont) { // Yes, use HITheme to draw the text for small sizes.
+ switch (d->aquaSizeConstrain(opt, w)) {
+ default:
+ break;
+ case QAquaSizeSmall:
+ themeId = kThemeSmallSystemFont;
+ break;
+ case QAquaSizeMini:
+ themeId = kThemeMiniSystemFont;
+ break;
+ }
+ }
+ if (themeId == kThemePushButtonFont) {
+ QWindowsStyle::drawControl(ce, btn, p, w);
+ } else {
+ p->save();
+ CGContextSetShouldAntialias(cg, true);
+ CGContextSetShouldSmoothFonts(cg, true);
+ HIThemeTextInfo tti;
+ tti.version = qt_mac_hitheme_version;
+ tti.state = tds;
+ QColor textColor = btn->palette.buttonText().color();
+ CGFloat colorComp[] = { textColor.redF(), textColor.greenF(),
+ textColor.blueF(), textColor.alphaF() };
+ CGContextSetFillColorSpace(cg, QCoreGraphicsPaintEngine::macGenericColorSpace());
+ CGContextSetFillColor(cg, colorComp);
+ tti.fontID = themeId;
+ tti.horizontalFlushness = kHIThemeTextHorizontalFlushCenter;
+ tti.verticalFlushness = kHIThemeTextVerticalFlushCenter;
+ tti.options = kHIThemeTextBoxOptionNone;
+ tti.truncationPosition = kHIThemeTextTruncationNone;
+ tti.truncationMaxLines = 1 + btn->text.count(QLatin1Char('\n'));
+ QCFString buttonText = qt_mac_removeMnemonics(btn->text);
+ QRect r = btn->rect;
+ HIRect bounds = qt_hirectForQRect(r);
+ HIThemeDrawTextBox(buttonText, &bounds, &tti,
+ cg, kHIThemeOrientationNormal);
+ p->restore();
+ }
+ } else {
+ if (hasIcon && !hasText) {
+ QWindowsStyle::drawControl(ce, btn, p, w);
+ } else {
+ QRect freeContentRect = btn->rect;
+ QRect textRect = itemTextRect(
+ btn->fontMetrics, freeContentRect, Qt::AlignCenter, btn->state & State_Enabled, btn->text);
+ if (hasMenu)
+ textRect.adjust(-1, 0, -1, 0);
+ // Draw the icon:
+ if (hasIcon) {
+ int contentW = textRect.width();
+ if (hasMenu)
+ contentW += pixelMetric(PM_MenuButtonIndicator) + 4;
+ QIcon::Mode mode = btn->state & State_Enabled ? QIcon::Normal : QIcon::Disabled;
+ if (mode == QIcon::Normal && btn->state & State_HasFocus)
+ mode = QIcon::Active;
+ // Decide if the icon is should be on or off:
+ QIcon::State state = QIcon::Off;
+ if (btn->state & State_On)
+ state = QIcon::On;
+ QPixmap pixmap = btn->icon.pixmap(btn->iconSize, mode, state);
+ contentW += pixmap.width() + PushButtonContentPadding;
+ int iconLeftOffset = freeContentRect.x() + (freeContentRect.width() - contentW) / 2;
+ int iconTopOffset = freeContentRect.y() + (freeContentRect.height() - pixmap.height()) / 2;
+ QRect iconDestRect(iconLeftOffset, iconTopOffset, pixmap.width(), pixmap.height());
+ QRect visualIconDestRect = visualRect(btn->direction, freeContentRect, iconDestRect);
+ drawItemPixmap(p, visualIconDestRect, Qt::AlignLeft | Qt::AlignVCenter, pixmap);
+ int newOffset = iconDestRect.x() + iconDestRect.width()
+ + PushButtonContentPadding - textRect.x();
+ textRect.adjust(newOffset, 0, newOffset, 0);
+ }
+ // Draw the text:
+ if (hasText) {
+ textRect = visualRect(btn->direction, freeContentRect, textRect);
+ drawItemText(p, textRect, Qt::AlignLeft | Qt::AlignVCenter | Qt::TextShowMnemonic, btn->palette,
+ (btn->state & State_Enabled), btn->text, QPalette::ButtonText);
+ }
+ }
+ }
+ }
+ break;
+ case CE_ComboBoxLabel:
+ if (const QStyleOptionComboBox *cb = qstyleoption_cast<const QStyleOptionComboBox *>(opt)) {
+ QStyleOptionComboBox comboCopy = *cb;
+ comboCopy.direction = Qt::LeftToRight;
+ QWindowsStyle::drawControl(CE_ComboBoxLabel, &comboCopy, p, w);
+ }
+ break;
+ case CE_TabBarTabShape:
+ if (const QStyleOptionTabV3 *tabOpt = qstyleoption_cast<const QStyleOptionTabV3 *>(opt)) {
+ if (tabOpt->documentMode) {
+ p->save();
+ QRect tabRect = tabOpt->rect;
+ drawTabShape(p, tabOpt);
+ p->restore();
+ return;
+ }
+
+#if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4)
+ if (QSysInfo::MacintoshVersion > QSysInfo::MV_10_3) {
+ HIThemeTabDrawInfo tdi;
+ tdi.version = 1;
+ tdi.style = kThemeTabNonFront;
+ tdi.direction = getTabDirection(tabOpt->shape);
+ switch (d->aquaSizeConstrain(opt, w)) {
+ default:
+ case QAquaSizeUnknown:
+ case QAquaSizeLarge:
+ tdi.size = kHIThemeTabSizeNormal;
+ break;
+ case QAquaSizeSmall:
+ tdi.size = kHIThemeTabSizeSmall;
+ break;
+ case QAquaSizeMini:
+ tdi.size = kHIThemeTabSizeMini;
+ break;
+ }
+ bool verticalTabs = tdi.direction == kThemeTabWest || tdi.direction == kThemeTabEast;
+ QRect tabRect = tabOpt->rect;
+
+ if ((!verticalTabs && tabRect.height() > 21 || verticalTabs && tabRect.width() > 21)) {
+ d->drawPantherTab(tabOpt, p, w);
+ break;
+ }
+
+ bool selected = tabOpt->state & State_Selected;
+ if (selected) {
+ if (!(tabOpt->state & State_Active))
+ tdi.style = kThemeTabFrontUnavailable;
+ else if (!(tabOpt->state & State_Enabled))
+ tdi.style = kThemeTabFrontInactive;
+ else
+ tdi.style = kThemeTabFront;
+ } else if (!(tabOpt->state & State_Active)) {
+ tdi.style = kThemeTabNonFrontUnavailable;
+ } else if (!(tabOpt->state & State_Enabled)) {
+ tdi.style = kThemeTabNonFrontInactive;
+ } else if (tabOpt->state & State_Sunken) {
+ tdi.style = kThemeTabFrontInactive; // (should be kThemeTabNonFrontPressed)
+ }
+ if (tabOpt->state & State_HasFocus)
+ tdi.adornment = kHIThemeTabAdornmentFocus;
+ else
+ tdi.adornment = kHIThemeTabAdornmentNone;
+ tdi.kind = kHIThemeTabKindNormal;
+ if (!verticalTabs)
+ tabRect.setY(tabRect.y() - 1);
+ else
+ tabRect.setX(tabRect.x() - 1);
+ QStyleOptionTab::TabPosition tp = tabOpt->position;
+ QStyleOptionTab::SelectedPosition sp = tabOpt->selectedPosition;
+ if (tabOpt->direction == Qt::RightToLeft && !verticalTabs) {
+ if (sp == QStyleOptionTab::NextIsSelected)
+ sp = QStyleOptionTab::PreviousIsSelected;
+ else if (sp == QStyleOptionTab::PreviousIsSelected)
+ sp = QStyleOptionTab::NextIsSelected;
+ switch (tp) {
+ case QStyleOptionTab::Beginning:
+ tp = QStyleOptionTab::End;
+ break;
+ case QStyleOptionTab::End:
+ tp = QStyleOptionTab::Beginning;
+ break;
+ default:
+ break;
+ }
+ }
+ switch (tp) {
+ case QStyleOptionTab::Beginning:
+ tdi.position = kHIThemeTabPositionFirst;
+ if (sp != QStyleOptionTab::NextIsSelected)
+ tdi.adornment |= kHIThemeTabAdornmentTrailingSeparator;
+ break;
+ case QStyleOptionTab::Middle:
+ tdi.position = kHIThemeTabPositionMiddle;
+ if (selected)
+ tdi.adornment |= kHIThemeTabAdornmentLeadingSeparator;
+ if (sp != QStyleOptionTab::NextIsSelected) // Also when we're selected.
+ tdi.adornment |= kHIThemeTabAdornmentTrailingSeparator;
+ break;
+ case QStyleOptionTab::End:
+ tdi.position = kHIThemeTabPositionLast;
+ if (selected)
+ tdi.adornment |= kHIThemeTabAdornmentLeadingSeparator;
+ break;
+ case QStyleOptionTab::OnlyOneTab:
+ tdi.position = kHIThemeTabPositionOnly;
+ break;
+ }
+ HIRect hirect = qt_hirectForQRect(tabRect);
+ HIThemeDrawTab(&hirect, &tdi, cg, kHIThemeOrientationNormal, 0);
+ } else
+#endif
+ {
+ d->drawPantherTab(tabOpt, p, w);
+ }
+ }
+ break;
+ case CE_TabBarTabLabel:
+ if (const QStyleOptionTab *tab = qstyleoption_cast<const QStyleOptionTab *>(opt)) {
+ QStyleOptionTabV3 myTab = *tab;
+ ThemeTabDirection ttd = getTabDirection(myTab.shape);
+ bool verticalTabs = ttd == kThemeTabWest || ttd == kThemeTabEast;
+
+ // Check to see if we use have the same as the system font
+ // (QComboMenuItem is internal and should never be seen by the
+ // outside world, unless they read the source, in which case, it's
+ // their own fault).
+ bool nonDefaultFont = p->font() != qt_app_fonts_hash()->value("QComboMenuItem");
+ if (verticalTabs || nonDefaultFont || !tab->icon.isNull()
+ || !myTab.leftButtonSize.isNull() || !myTab.rightButtonSize.isNull()) {
+ int heightOffset = 0;
+ if (verticalTabs) {
+ heightOffset = -1;
+ } else if (nonDefaultFont) {
+ if (p->fontMetrics().height() == myTab.rect.height())
+ heightOffset = 2;
+ }
+ myTab.rect.setHeight(myTab.rect.height() + heightOffset);
+
+ if (myTab.documentMode) {
+ p->save();
+ rotateTabPainter(p, myTab.shape, myTab.rect);
+
+ QPalette np = tab->palette;
+ np.setColor(QPalette::WindowText, QColor(255, 255, 255, 75));
+ QRect nr = subElementRect(SE_TabBarTabText, opt, w);
+ nr.moveTop(+1);
+ int alignment = Qt::AlignCenter | Qt::TextShowMnemonic | Qt::TextHideMnemonic;
+ drawItemText(p, nr, alignment, np, tab->state & State_Enabled, tab->text, QPalette::WindowText);
+ p->restore();
+ }
+
+ QCommonStyle::drawControl(ce, &myTab, p, w);
+ } else {
+ p->save();
+ CGContextSetShouldAntialias(cg, true);
+ CGContextSetShouldSmoothFonts(cg, true);
+ HIThemeTextInfo tti;
+ tti.version = qt_mac_hitheme_version;
+ tti.state = tds;
+ QColor textColor = myTab.palette.windowText().color();
+ CGFloat colorComp[] = { textColor.redF(), textColor.greenF(),
+ textColor.blueF(), textColor.alphaF() };
+ CGContextSetFillColorSpace(cg, QCoreGraphicsPaintEngine::macGenericColorSpace());
+ CGContextSetFillColor(cg, colorComp);
+ switch (d->aquaSizeConstrain(opt, w)) {
+ default:
+ case QAquaSizeUnknown:
+ case QAquaSizeLarge:
+ tti.fontID = kThemeSystemFont;
+ break;
+ case QAquaSizeSmall:
+ tti.fontID = kThemeSmallSystemFont;
+ break;
+ case QAquaSizeMini:
+ tti.fontID = kThemeMiniSystemFont;
+ break;
+ }
+ tti.horizontalFlushness = kHIThemeTextHorizontalFlushCenter;
+ tti.verticalFlushness = kHIThemeTextVerticalFlushCenter;
+ tti.options = verticalTabs ? kHIThemeTextBoxOptionStronglyVertical : kHIThemeTextBoxOptionNone;
+ tti.truncationPosition = kHIThemeTextTruncationNone;
+ tti.truncationMaxLines = 1 + myTab.text.count(QLatin1Char('\n'));
+ QCFString tabText = qt_mac_removeMnemonics(myTab.text);
+ QRect r = myTab.rect.adjusted(0, 0, 0, -1);
+ HIRect bounds = qt_hirectForQRect(r);
+ HIThemeDrawTextBox(tabText, &bounds, &tti, cg, kHIThemeOrientationNormal);
+ p->restore();
+ }
+ }
+ break;
+ case CE_DockWidgetTitle:
+ if (const QDockWidget *dockWidget = qobject_cast<const QDockWidget *>(w)) {
+ bool floating = dockWidget->isFloating();
+ if (floating) {
+ ThemeDrawState tds = d->getDrawState(opt->state);
+ HIThemeWindowDrawInfo wdi;
+ wdi.version = qt_mac_hitheme_version;
+ wdi.state = tds;
+ wdi.windowType = kThemeMovableDialogWindow;
+ wdi.titleHeight = opt->rect.height();
+ wdi.titleWidth = opt->rect.width();
+ wdi.attributes = 0;
+
+ HIRect titleBarRect;
+ HIRect tmpRect = qt_hirectForQRect(opt->rect);
+ {
+ QCFType<HIShapeRef> titleRegion;
+ QRect newr = opt->rect.adjusted(0, 0, 2, 0);
+ HIThemeGetWindowShape(&tmpRect, &wdi, kWindowTitleBarRgn, &titleRegion);
+ ptrHIShapeGetBounds(titleRegion, &tmpRect);
+ newr.translate(newr.x() - int(tmpRect.origin.x), newr.y() - int(tmpRect.origin.y));
+ titleBarRect = qt_hirectForQRect(newr);
+ }
+ QMacCGContext cg(p);
+ HIThemeDrawWindowFrame(&titleBarRect, &wdi, cg, kHIThemeOrientationNormal, 0);
+ } else {
+ // fill title bar background
+ QLinearGradient linearGrad(0, opt->rect.top(), 0, opt->rect.bottom());
+ linearGrad.setColorAt(0, mainWindowGradientBegin);
+ linearGrad.setColorAt(1, mainWindowGradientEnd);
+ p->fillRect(opt->rect, linearGrad);
+
+ // draw horizontal lines at top and bottom
+ p->save();
+ p->setPen(mainWindowGradientBegin.lighter(114));
+ p->drawLine(opt->rect.topLeft(), opt->rect.topRight());
+ p->setPen(mainWindowGradientEnd.darker(114));
+ p->drawLine(opt->rect.bottomLeft(), opt->rect.bottomRight());
+ p->restore();
+ }
+ }
+
+ // Draw the text...
+ if (const QStyleOptionDockWidget *dwOpt = qstyleoption_cast<const QStyleOptionDockWidget *>(opt)) {
+ if (!dwOpt->title.isEmpty()) {
+ const QStyleOptionDockWidgetV2 *v2
+ = qstyleoption_cast<const QStyleOptionDockWidgetV2*>(dwOpt);
+ bool verticalTitleBar = v2 == 0 ? false : v2->verticalTitleBar;
+
+ QRect titleRect = subElementRect(SE_DockWidgetTitleBarText, opt, w);
+ if (verticalTitleBar) {
+ QRect rect = dwOpt->rect;
+ QRect r = rect;
+ QSize s = r.size();
+ s.transpose();
+ r.setSize(s);
+
+ titleRect = QRect(r.left() + rect.bottom()
+ - titleRect.bottom(),
+ r.top() + titleRect.left() - rect.left(),
+ titleRect.height(), titleRect.width());
+
+ p->translate(r.left(), r.top() + r.width());
+ p->rotate(-90);
+ p->translate(-r.left(), -r.top());
+ }
+
+ QFont oldFont = p->font();
+ p->setFont(qt_app_fonts_hash()->value("QToolButton", p->font()));
+ QString text = p->fontMetrics().elidedText(dwOpt->title, Qt::ElideRight,
+ titleRect.width());
+ drawItemText(p, titleRect,
+ Qt::AlignCenter | Qt::TextShowMnemonic, dwOpt->palette,
+ dwOpt->state & State_Enabled, text,
+ QPalette::WindowText);
+ p->setFont(oldFont);
+ }
+ }
+ break;
+ case CE_FocusFrame: {
+ int xOff = pixelMetric(PM_FocusFrameHMargin, opt, w) + 1;
+ int yOff = pixelMetric(PM_FocusFrameVMargin, opt, w) + 1;
+ HIRect hirect = CGRectMake(xOff+opt->rect.x(), yOff+opt->rect.y(), opt->rect.width() - 2 * xOff,
+ opt->rect.height() - 2 * yOff);
+ HIThemeDrawFocusRect(&hirect, true, QMacCGContext(p), kHIThemeOrientationNormal);
+ break; }
+ case CE_MenuItem:
+ case CE_MenuEmptyArea:
+ if (const QStyleOptionMenuItem *mi = qstyleoption_cast<const QStyleOptionMenuItem *>(opt)) {
+ p->fillRect(mi->rect, opt->palette.background());
+ QAquaWidgetSize widgetSize = d->aquaSizeConstrain(opt, w);
+ int tabwidth = mi->tabWidth;
+ int maxpmw = mi->maxIconWidth;
+ bool active = mi->state & State_Selected;
+ bool enabled = mi->state & State_Enabled;
+ HIRect menuRect = qt_hirectForQRect(mi->menuRect);
+ HIRect itemRect = qt_hirectForQRect(mi->rect);
+ HIThemeMenuItemDrawInfo mdi;
+ mdi.version = qt_mac_hitheme_version;
+ mdi.itemType = kThemeMenuItemPlain;
+ if (!mi->icon.isNull())
+ mdi.itemType |= kThemeMenuItemHasIcon;
+ if (mi->menuItemType == QStyleOptionMenuItem::SubMenu)
+ mdi.itemType |= kThemeMenuItemHierarchical | kThemeMenuItemHierBackground;
+ else
+ mdi.itemType |= kThemeMenuItemPopUpBackground;
+ if (enabled)
+ mdi.state = kThemeMenuActive;
+ else
+ mdi.state = kThemeMenuDisabled;
+ if (active)
+ mdi.state |= kThemeMenuSelected;
+ QRect contentRect;
+ if (mi->menuItemType == QStyleOptionMenuItem::Separator) {
+ // First arg should be &menurect, but wacky stuff happens then.
+ HIThemeDrawMenuSeparator(&itemRect, &itemRect, &mdi,
+ cg, kHIThemeOrientationNormal);
+ break;
+ } else {
+ HIRect cr;
+ bool needAlpha = mi->palette.color(QPalette::Button) == Qt::transparent;
+ if (needAlpha) {
+ needAlpha = true;
+ CGContextSaveGState(cg);
+ CGContextSetAlpha(cg, 0.0);
+ }
+ HIThemeDrawMenuItem(&menuRect, &itemRect, &mdi,
+ cg, kHIThemeOrientationNormal, &cr);
+ if (needAlpha)
+ CGContextRestoreGState(cg);
+ if (ce == CE_MenuEmptyArea)
+ break;
+ contentRect = qt_qrectForHIRect(cr);
+ }
+ int xpos = contentRect.x() + 18;
+ int checkcol = maxpmw;
+ if (!enabled)
+ p->setPen(mi->palette.text().color());
+ else if (active)
+ p->setPen(mi->palette.highlightedText().color());
+ else
+ p->setPen(mi->palette.buttonText().color());
+
+ if (mi->checked) {
+ // Use the HIThemeTextInfo foo to draw the check mark correctly, if we do it,
+ // we somehow need to use a special encoding as it doesn't look right with our
+ // drawText().
+ p->save();
+ CGContextSetShouldAntialias(cg, true);
+ CGContextSetShouldSmoothFonts(cg, true);
+ QColor textColor = p->pen().color();
+ CGFloat colorComp[] = { textColor.redF(), textColor.greenF(),
+ textColor.blueF(), textColor.alphaF() };
+ CGContextSetFillColorSpace(cg, QCoreGraphicsPaintEngine::macGenericColorSpace());
+ CGContextSetFillColor(cg, colorComp);
+ HIThemeTextInfo tti;
+ tti.version = qt_mac_hitheme_version;
+ tti.state = tds;
+ if (active && enabled)
+ tti.state = kThemeStatePressed;
+ switch (widgetSize) {
+ case QAquaSizeUnknown:
+ case QAquaSizeLarge:
+ tti.fontID = kThemeMenuItemMarkFont;
+ break;
+ case QAquaSizeSmall:
+ tti.fontID = kThemeSmallSystemFont;
+ break;
+ case QAquaSizeMini:
+ tti.fontID = kThemeMiniSystemFont;
+ break;
+ }
+ tti.horizontalFlushness = kHIThemeTextHorizontalFlushLeft;
+ tti.verticalFlushness = kHIThemeTextVerticalFlushCenter;
+ tti.options = kHIThemeTextBoxOptionNone;
+ tti.truncationPosition = kHIThemeTextTruncationNone;
+ tti.truncationMaxLines = 1;
+ QCFString checkmark;
+#if 0
+ if (mi->checkType == QStyleOptionMenuItem::Exclusive)
+ checkmark = QString(QChar(kDiamondUnicode));
+ else
+#endif
+ checkmark = QString(QChar(kCheckUnicode));
+ int mw = checkcol + macItemFrame;
+ int mh = contentRect.height() - 2 * macItemFrame;
+ int xp = contentRect.x();
+ xp += macItemFrame;
+ CGFloat outWidth, outHeight, outBaseline;
+ HIThemeGetTextDimensions(checkmark, 0, &tti, &outWidth, &outHeight,
+ &outBaseline);
+ if (widgetSize == QAquaSizeMini)
+ outBaseline += 1;
+ QRect r(xp, contentRect.y(), mw, mh);
+ r.translate(0, p->fontMetrics().ascent() - int(outBaseline) + 1);
+ HIRect bounds = qt_hirectForQRect(r);
+ HIThemeDrawTextBox(checkmark, &bounds, &tti,
+ cg, kHIThemeOrientationNormal);
+ p->restore();
+ }
+ if (!mi->icon.isNull()) {
+ QIcon::Mode mode = (mi->state & State_Enabled) ? QIcon::Normal
+ : QIcon::Disabled;
+ // Always be normal or disabled to follow the Mac style.
+ int smallIconSize = pixelMetric(PM_SmallIconSize);
+ QSize iconSize(smallIconSize, smallIconSize);
+ if (const QComboBox *comboBox = qobject_cast<const QComboBox *>(w)) {
+ iconSize = comboBox->iconSize();
+ }
+ QPixmap pixmap = mi->icon.pixmap(iconSize, mode);
+ int pixw = pixmap.width();
+ int pixh = pixmap.height();
+ QRect cr(xpos, contentRect.y(), checkcol, contentRect.height());
+ QRect pmr(0, 0, pixw, pixh);
+ pmr.moveCenter(cr.center());
+ p->drawPixmap(pmr.topLeft(), pixmap);
+ xpos += pixw + 6;
+ }
+
+ QString s = mi->text;
+ if (!s.isEmpty()) {
+ int t = s.indexOf(QLatin1Char('\t'));
+ int text_flags = Qt::AlignRight | Qt::AlignVCenter | Qt::TextHideMnemonic
+ | Qt::TextSingleLine | Qt::AlignAbsolute;
+ int yPos = contentRect.y();
+ if (widgetSize == QAquaSizeMini)
+ yPos += 1;
+ p->save();
+ if (t >= 0) {
+ p->setFont(qt_app_fonts_hash()->value("QMenuItem", p->font()));
+ int xp = contentRect.right() - tabwidth - macRightBorder
+ - macItemHMargin - macItemFrame + 1;
+ p->drawText(xp, yPos, tabwidth, contentRect.height(), text_flags,
+ s.mid(t + 1));
+ s = s.left(t);
+ }
+
+ const int xm = macItemFrame + maxpmw + macItemHMargin;
+ QFont myFont = mi->font;
+ if (mi->state & QStyle::State_Mini)
+ myFont.setPointSize(mi->font.pointSize());
+ p->setFont(myFont);
+ p->drawText(xpos, yPos, contentRect.width() - xm - tabwidth + 1,
+ contentRect.height(), text_flags ^ Qt::AlignRight, s);
+ p->restore();
+ }
+ }
+ break;
+ case CE_MenuHMargin:
+ case CE_MenuVMargin:
+ case CE_MenuTearoff:
+ case CE_MenuScroller:
+ if (const QStyleOptionMenuItem *mi = qstyleoption_cast<const QStyleOptionMenuItem *>(opt)) {
+ p->fillRect(mi->rect, opt->palette.background());
+
+ HIRect menuRect = qt_hirectForQRect(mi->menuRect);
+ HIRect itemRect = qt_hirectForQRect(mi->rect);
+ HIThemeMenuItemDrawInfo mdi;
+ mdi.version = qt_mac_hitheme_version;
+ if (!(opt->state & State_Enabled))
+ mdi.state = kThemeMenuDisabled;
+ else if (opt->state & State_Selected)
+ mdi.state = kThemeMenuSelected;
+ else
+ mdi.state = kThemeMenuActive;
+ if (ce == CE_MenuScroller) {
+ if (opt->state & State_DownArrow)
+ mdi.itemType = kThemeMenuItemScrollDownArrow;
+ else
+ mdi.itemType = kThemeMenuItemScrollUpArrow;
+ } else {
+ mdi.itemType = kThemeMenuItemPlain;
+ }
+ HIThemeDrawMenuItem(&menuRect, &itemRect, &mdi,
+ cg,
+ kHIThemeOrientationNormal, 0);
+ if (ce == CE_MenuTearoff) {
+ p->setPen(QPen(mi->palette.dark().color(), 1, Qt::DashLine));
+ p->drawLine(mi->rect.x() + 2, mi->rect.y() + mi->rect.height() / 2 - 1,
+ mi->rect.x() + mi->rect.width() - 4,
+ mi->rect.y() + mi->rect.height() / 2 - 1);
+ p->setPen(QPen(mi->palette.light().color(), 1, Qt::DashLine));
+ p->drawLine(mi->rect.x() + 2, mi->rect.y() + mi->rect.height() / 2,
+ mi->rect.x() + mi->rect.width() - 4,
+ mi->rect.y() + mi->rect.height() / 2);
+ }
+ }
+ break;
+ case CE_MenuBarItem:
+ if (const QStyleOptionMenuItem *mi = qstyleoption_cast<const QStyleOptionMenuItem *>(opt)) {
+ HIRect menuRect = qt_hirectForQRect(mi->menuRect);
+ HIRect itemRect = qt_hirectForQRect(mi->rect);
+
+ if ((opt->state & State_Selected) && (opt->state & State_Enabled) && (opt->state & State_Sunken)){
+ // Draw a selected menu item background:
+ HIThemeMenuItemDrawInfo mdi;
+ mdi.version = qt_mac_hitheme_version;
+ mdi.state = kThemeMenuSelected;
+ mdi.itemType = kThemeMenuItemPlain;
+ HIThemeDrawMenuItem(&menuRect, &itemRect, &mdi, cg, kHIThemeOrientationNormal, 0);
+ } else {
+ // Draw the toolbar background:
+ HIThemeMenuBarDrawInfo bdi;
+ bdi.version = qt_mac_hitheme_version;
+ bdi.state = kThemeMenuBarNormal;
+ bdi.attributes = 0;
+ HIRect hirect = qt_hirectForQRect(mi->rect);
+ HIThemeDrawMenuBarBackground(&menuRect, &bdi, cg, kHIThemeOrientationNormal);
+ }
+
+ if (!mi->icon.isNull()) {
+ drawItemPixmap(p, mi->rect,
+ Qt::AlignCenter | Qt::TextHideMnemonic | Qt::TextDontClip
+ | Qt::TextSingleLine,
+ mi->icon.pixmap(pixelMetric(PM_SmallIconSize),
+ (mi->state & State_Enabled) ? QIcon::Normal : QIcon::Disabled));
+ } else {
+ drawItemText(p, mi->rect,
+ Qt::AlignCenter | Qt::TextHideMnemonic | Qt::TextDontClip
+ | Qt::TextSingleLine,
+ mi->palette, mi->state & State_Enabled,
+ mi->text, QPalette::ButtonText);
+ }
+ }
+ break;
+ case CE_MenuBarEmptyArea:
+ if (const QStyleOptionMenuItem *mi = qstyleoption_cast<const QStyleOptionMenuItem *>(opt)) {
+ HIThemeMenuBarDrawInfo bdi;
+ bdi.version = qt_mac_hitheme_version;
+ bdi.state = kThemeMenuBarNormal;
+ bdi.attributes = 0;
+ HIRect hirect = qt_hirectForQRect(mi->rect);
+ HIThemeDrawMenuBarBackground(&hirect, &bdi, cg,
+ kHIThemeOrientationNormal);
+ break;
+ }
+ case CE_ProgressBarContents:
+ if (const QStyleOptionProgressBar *pb = qstyleoption_cast<const QStyleOptionProgressBar *>(opt)) {
+ HIThemeTrackDrawInfo tdi;
+ tdi.version = qt_mac_hitheme_version;
+ tdi.reserved = 0;
+ bool isIndeterminate = (pb->minimum == 0 && pb->maximum == 0);
+ bool vertical = false;
+ bool inverted = false;
+ if (const QStyleOptionProgressBarV2 *pb2 = qstyleoption_cast<const QStyleOptionProgressBarV2 *>(opt)) {
+ vertical = (pb2->orientation == Qt::Vertical);
+ inverted = pb2->invertedAppearance;
+ }
+ bool reverse = (!vertical && (pb->direction == Qt::RightToLeft));
+ if (inverted)
+ reverse = !reverse;
+ switch (d->aquaSizeConstrain(opt, w)) {
+ case QAquaSizeUnknown:
+ case QAquaSizeLarge:
+ tdi.kind = !isIndeterminate ? kThemeLargeProgressBar
+ : kThemeLargeIndeterminateBar;
+ break;
+ case QAquaSizeMini:
+ case QAquaSizeSmall:
+ tdi.kind = !isIndeterminate ? kThemeProgressBar : kThemeIndeterminateBar;
+ break;
+ }
+ tdi.bounds = qt_hirectForQRect(pb->rect);
+ tdi.max = pb->maximum;
+ tdi.min = pb->minimum;
+ tdi.value = pb->progress;
+ tdi.attributes = vertical ? 0 : kThemeTrackHorizontal;
+ tdi.trackInfo.progress.phase = d->progressFrame;
+ if (!(pb->state & State_Active))
+ tdi.enableState = kThemeTrackInactive;
+ else if (!(pb->state & State_Enabled))
+ tdi.enableState = kThemeTrackDisabled;
+ else
+ tdi.enableState = kThemeTrackActive;
+ HIThemeDrawTrack(&tdi, 0, cg, kHIThemeOrientationNormal);
+ }
+ break;
+ case CE_ProgressBarLabel:
+ case CE_ProgressBarGroove:
+ break;
+ case CE_SizeGrip: {
+ if (w && w->testAttribute(Qt::WA_MacOpaqueSizeGrip)) {
+ HIThemeGrowBoxDrawInfo gdi;
+ gdi.version = qt_mac_hitheme_version;
+ gdi.state = tds;
+ gdi.kind = kHIThemeGrowBoxKindNormal;
+ gdi.direction = kThemeGrowRight | kThemeGrowDown;
+ gdi.size = kHIThemeGrowBoxSizeNormal;
+ HIPoint pt = CGPointMake(opt->rect.x(), opt->rect.y());
+ HIThemeDrawGrowBox(&pt, &gdi, cg, kHIThemeOrientationNormal);
+ } else {
+ // It isn't possible to draw a transparent size grip with the
+ // native API, so we do it ourselves here.
+ const bool metal = qt_mac_is_metal(w);
+ QPen lineColor = metal ? QColor(236, 236, 236) : QColor(82, 82, 82, 192);
+ QPen metalHighlight = QColor(5, 5, 5, 192);
+ lineColor.setWidth(1);
+ p->save();
+ p->setRenderHint(QPainter::Antialiasing);
+ p->setPen(lineColor);
+ const Qt::LayoutDirection layoutDirection = w ? w->layoutDirection() : qApp->layoutDirection();
+ const int NumLines = metal ? 4 : 3;
+ for (int l = 0; l < NumLines; ++l) {
+ const int offset = (l * 4 + (metal ? 2 : 3));
+ QPoint start, end;
+ if (layoutDirection == Qt::LeftToRight) {
+ start = QPoint(opt->rect.width() - offset, opt->rect.height() - 1);
+ end = QPoint(opt->rect.width() - 1, opt->rect.height() - offset);
+ } else {
+ start = QPoint(offset, opt->rect.height() - 1);
+ end = QPoint(1, opt->rect.height() - offset);
+ }
+ p->drawLine(start, end);
+ if (metal) {
+ p->setPen(metalHighlight);
+ p->setRenderHint(QPainter::Antialiasing, false);
+ p->drawLine(start + QPoint(0, -1), end + QPoint(0, -1));
+ p->setRenderHint(QPainter::Antialiasing, true);
+ p->setPen(lineColor);
+ }
+ }
+ p->restore();
+ }
+ break;
+ }
+ case CE_Splitter: {
+ HIThemeSplitterDrawInfo sdi;
+ sdi.version = qt_mac_hitheme_version;
+ sdi.state = tds;
+ sdi.adornment = qt_mac_is_metal(w) ? kHIThemeSplitterAdornmentMetal
+ : kHIThemeSplitterAdornmentNone;
+ HIRect hirect = qt_hirectForQRect(opt->rect);
+ HIThemeDrawPaneSplitter(&hirect, &sdi, cg, kHIThemeOrientationNormal);
+ break; }
+ case CE_RubberBand:
+ if (const QStyleOptionRubberBand *rubber = qstyleoption_cast<const QStyleOptionRubberBand *>(opt)) {
+ QColor fillColor(opt->palette.color(QPalette::Disabled, QPalette::Highlight));
+ if (!rubber->opaque) {
+ QColor strokeColor;
+ // I retrieved these colors from the Carbon-Dev mailing list
+ strokeColor.setHsvF(0, 0, 0.86, 1.0);
+ fillColor.setHsvF(0, 0, 0.53, 0.25);
+ if (opt->rect.width() * opt->rect.height() <= 3) {
+ p->fillRect(opt->rect, strokeColor);
+ } else {
+ QPen oldPen = p->pen();
+ QBrush oldBrush = p->brush();
+ QPen pen(strokeColor);
+ p->setPen(pen);
+ p->setBrush(fillColor);
+ p->drawRect(opt->rect.adjusted(0, 0, -1, -1));
+ p->setPen(oldPen);
+ p->setBrush(oldBrush);
+ }
+ } else {
+ p->fillRect(opt->rect, fillColor);
+ }
+ }
+ break;
+ case CE_ToolBar: {
+ // For unified tool bars, draw nothing.
+ if (w) {
+ if (QMainWindow * mainWindow = qobject_cast<QMainWindow *>(w->window()))
+ if (mainWindow->unifiedTitleAndToolBarOnMac())
+ break;
+ }
+
+ // draw background gradient
+ QLinearGradient linearGrad;
+ if (opt->state & State_Horizontal)
+ linearGrad = QLinearGradient(0, opt->rect.top(), 0, opt->rect.bottom());
+ else
+ linearGrad = QLinearGradient(opt->rect.left(), 0, opt->rect.right(), 0);
+
+ linearGrad.setColorAt(0, mainWindowGradientBegin);
+ linearGrad.setColorAt(1, mainWindowGradientEnd);
+ p->fillRect(opt->rect, linearGrad);
+
+ p->save();
+ if (opt->state & State_Horizontal) {
+ p->setPen(mainWindowGradientBegin.lighter(114));
+ p->drawLine(opt->rect.topLeft(), opt->rect.topRight());
+ p->setPen(mainWindowGradientEnd.darker(114));
+ p->drawLine(opt->rect.bottomLeft(), opt->rect.bottomRight());
+
+ } else {
+ p->setPen(mainWindowGradientBegin.lighter(114));
+ p->drawLine(opt->rect.topLeft(), opt->rect.bottomLeft());
+ p->setPen(mainWindowGradientEnd.darker(114));
+ p->drawLine(opt->rect.topRight(), opt->rect.bottomRight());
+ }
+ p->restore();
+
+
+ } break;
+ default:
+ QWindowsStyle::drawControl(ce, opt, p, w);
+ break;
+ }
+}
+
+static void setLayoutItemMargins(int left, int top, int right, int bottom, QRect *rect, Qt::LayoutDirection dir)
+{
+ if (dir == Qt::RightToLeft) {
+ rect->adjust(-right, top, -left, bottom);
+ } else {
+ rect->adjust(left, top, right, bottom);
+ }
+}
+/*! \reimp */
+QRect QMacStyle::subElementRect(SubElement sr, const QStyleOption *opt,
+ const QWidget *widget) const
+{
+ QRect rect;
+ int controlSize = getControlSize(opt, widget);
+
+ switch (sr) {
+ case SE_ToolBoxTabContents:
+ rect = QCommonStyle::subElementRect(sr, opt, widget);
+ break;
+ case SE_PushButtonContents:
+ if (const QStyleOptionButton *btn = qstyleoption_cast<const QStyleOptionButton *>(opt)) {
+ // Unlike Carbon, we want the button to always be drawn inside its bounds.
+ // Therefore, the button is a bit smaller, so that even if it got focus,
+ // the focus 'shadow' will be inside. Adjust the content rect likewise.
+ HIThemeButtonDrawInfo bdi;
+ d->initHIThemePushButton(btn, widget, d->getDrawState(opt->state), &bdi);
+ HIRect contentRect = d->pushButtonContentBounds(btn, &bdi);
+ rect = qt_qrectForHIRect(contentRect);
+ }
+ break;
+ case SE_HeaderLabel:
+ if (qstyleoption_cast<const QStyleOptionHeader *>(opt)) {
+ rect = QWindowsStyle::subElementRect(sr, opt, widget);
+ if (widget && widget->height() <= qt_mac_aqua_get_metric(kThemeMetricListHeaderHeight)){
+ // We need to allow the text a bit more space when the header is as
+ // small as kThemeMetricListHeaderHeight, otherwise it gets clipped:
+ rect.setY(0);
+ rect.setHeight(widget->height());
+ }
+ if (opt->direction == Qt::RightToLeft)
+ rect.adjust(15, 0, -20, 0);
+ }
+ break;
+ case SE_ProgressBarGroove:
+ case SE_ProgressBarLabel:
+ break;
+ case SE_ProgressBarContents:
+ rect = opt->rect;
+ break;
+ case SE_TreeViewDisclosureItem: {
+ HIRect inRect = CGRectMake(opt->rect.x(), opt->rect.y(),
+ opt->rect.width(), opt->rect.height());
+ HIThemeButtonDrawInfo bdi;
+ bdi.version = qt_mac_hitheme_version;
+ bdi.state = kThemeStateActive;
+ bdi.kind = kThemeDisclosureButton;
+ bdi.value = kThemeDisclosureRight;
+ bdi.adornment = kThemeAdornmentNone;
+ HIRect contentRect;
+ HIThemeGetButtonContentBounds(&inRect, &bdi, &contentRect);
+ QCFType<HIShapeRef> shape;
+ HIRect outRect;
+ HIThemeGetButtonShape(&inRect, &bdi, &shape);
+ ptrHIShapeGetBounds(shape, &outRect);
+ rect = QRect(int(outRect.origin.x), int(outRect.origin.y),
+ int(contentRect.origin.x - outRect.origin.x), int(outRect.size.height));
+ break;
+ }
+ case SE_TabWidgetLeftCorner:
+ if (const QStyleOptionTabWidgetFrame *twf
+ = qstyleoption_cast<const QStyleOptionTabWidgetFrame *>(opt)) {
+ switch (twf->shape) {
+ case QTabBar::RoundedNorth:
+ case QTabBar::TriangularNorth:
+ rect = QRect(QPoint(0, 0), twf->leftCornerWidgetSize);
+ break;
+ case QTabBar::RoundedSouth:
+ case QTabBar::TriangularSouth:
+ rect = QRect(QPoint(0, twf->rect.height() - twf->leftCornerWidgetSize.height()),
+ twf->leftCornerWidgetSize);
+ break;
+ default:
+ break;
+ }
+ rect = visualRect(twf->direction, twf->rect, rect);
+ }
+ break;
+ case SE_TabWidgetRightCorner:
+ if (const QStyleOptionTabWidgetFrame *twf
+ = qstyleoption_cast<const QStyleOptionTabWidgetFrame *>(opt)) {
+ switch (twf->shape) {
+ case QTabBar::RoundedNorth:
+ case QTabBar::TriangularNorth:
+ rect = QRect(QPoint(twf->rect.width() - twf->rightCornerWidgetSize.width(), 0),
+ twf->rightCornerWidgetSize);
+ break;
+ case QTabBar::RoundedSouth:
+ case QTabBar::TriangularSouth:
+ rect = QRect(QPoint(twf->rect.width() - twf->rightCornerWidgetSize.width(),
+ twf->rect.height() - twf->rightCornerWidgetSize.height()),
+ twf->rightCornerWidgetSize);
+ break;
+ default:
+ break;
+ }
+ rect = visualRect(twf->direction, twf->rect, rect);
+ }
+ break;
+ case SE_TabWidgetTabContents:
+ rect = QWindowsStyle::subElementRect(sr, opt, widget);
+ if (const QStyleOptionTabWidgetFrame *twf
+ = qstyleoption_cast<const QStyleOptionTabWidgetFrame *>(opt)) {
+ if (twf->lineWidth != 0) {
+ switch (getTabDirection(twf->shape)) {
+ case kThemeTabNorth:
+ rect.adjust(+1, +14, -1, -1);
+ break;
+ case kThemeTabSouth:
+ rect.adjust(+1, +1, -1, -14);
+ break;
+ case kThemeTabWest:
+ rect.adjust(+14, +1, -1, -1);
+ break;
+ case kThemeTabEast:
+ rect.adjust(+1, +1, -14, -1);
+ }
+ }
+ }
+ break;
+ case SE_LineEditContents:
+ rect = QWindowsStyle::subElementRect(sr, opt, widget);
+ if(widget->parentWidget() && qobject_cast<const QComboBox*>(widget->parentWidget()))
+ rect.adjust(-1, -2, 0, 0);
+ else
+ rect.adjust(-1, 0, 0, +1);
+ break;
+ case SE_CheckBoxLayoutItem:
+ rect = opt->rect;
+ if (controlSize == QAquaSizeLarge) {
+ setLayoutItemMargins(+2, +3, -9, -4, &rect, opt->direction);
+ } else if (controlSize == QAquaSizeSmall) {
+ setLayoutItemMargins(+1, +5, 0 /* fix */, -6, &rect, opt->direction);
+ } else {
+ setLayoutItemMargins(0, +7, 0 /* fix */, -6, &rect, opt->direction);
+ }
+ break;
+ case SE_ComboBoxLayoutItem:
+ if (widget && qobject_cast<QToolBar *>(widget->parentWidget())) {
+ // Do nothing, because QToolbar needs the entire widget rect.
+ // Otherwise it will be clipped. Equivalent to
+ // widget->setAttribute(Qt::WA_LayoutUsesWidgetRect), but without
+ // all the hassle.
+ } else {
+ rect = opt->rect;
+ if (controlSize == QAquaSizeLarge) {
+ rect.adjust(+3, +2, -3, -4);
+ } else if (controlSize == QAquaSizeSmall) {
+ setLayoutItemMargins(+2, +1, -3, -4, &rect, opt->direction);
+ } else {
+ setLayoutItemMargins(+1, 0, -2, 0, &rect, opt->direction);
+ }
+ }
+ break;
+ case SE_LabelLayoutItem:
+ rect = opt->rect;
+ setLayoutItemMargins(+1, 0 /* SHOULD be -1, done for alignment */, 0, 0 /* SHOULD be -1, done for alignment */, &rect, opt->direction);
+ break;
+ case SE_ProgressBarLayoutItem: {
+ rect = opt->rect;
+ int bottom = SIZE(3, 8, 8);
+ if (opt->state & State_Horizontal) {
+ rect.adjust(0, +1, 0, -bottom);
+ } else {
+ setLayoutItemMargins(+1, 0, -bottom, 0, &rect, opt->direction);
+ }
+ break;
+ }
+ case SE_PushButtonLayoutItem:
+ if (const QStyleOptionButton *buttonOpt
+ = qstyleoption_cast<const QStyleOptionButton *>(opt)) {
+ if ((buttonOpt->features & QStyleOptionButton::Flat))
+ break; // leave rect alone
+ }
+ rect = opt->rect;
+ if (controlSize == QAquaSizeLarge) {
+ rect.adjust(+6, +4, -6, -8);
+ } else if (controlSize == QAquaSizeSmall) {
+ rect.adjust(+5, +4, -5, -6);
+ } else {
+ rect.adjust(+1, 0, -1, -2);
+ }
+ break;
+ case SE_RadioButtonLayoutItem:
+ rect = opt->rect;
+ if (controlSize == QAquaSizeLarge) {
+ setLayoutItemMargins(+2, +2 /* SHOULD BE +3, done for alignment */,
+ 0, -4 /* SHOULD BE -3, done for alignment */, &rect, opt->direction);
+ } else if (controlSize == QAquaSizeSmall) {
+ rect.adjust(0, +6, 0 /* fix */, -5);
+ } else {
+ rect.adjust(0, +6, 0 /* fix */, -7);
+ }
+ break;
+ case SE_SliderLayoutItem:
+ if (const QStyleOptionSlider *sliderOpt
+ = qstyleoption_cast<const QStyleOptionSlider *>(opt)) {
+ rect = opt->rect;
+ if (sliderOpt->tickPosition == QSlider::NoTicks) {
+ int above = SIZE(3, 0, 2);
+ int below = SIZE(4, 3, 0);
+ if (sliderOpt->orientation == Qt::Horizontal) {
+ rect.adjust(0, +above, 0, -below);
+ } else {
+ rect.adjust(+above, 0, -below, 0); //### Seems that QSlider flip the position of the ticks in reverse mode.
+ }
+ } else if (sliderOpt->tickPosition == QSlider::TicksAbove) {
+ int below = SIZE(3, 2, 0);
+ if (sliderOpt->orientation == Qt::Horizontal) {
+ rect.setHeight(rect.height() - below);
+ } else {
+ rect.setWidth(rect.width() - below);
+ }
+ } else if (sliderOpt->tickPosition == QSlider::TicksBelow) {
+ int above = SIZE(3, 2, 0);
+ if (sliderOpt->orientation == Qt::Horizontal) {
+ rect.setTop(rect.top() + above);
+ } else {
+ rect.setLeft(rect.left() + above);
+ }
+ }
+ }
+ break;
+ case SE_FrameLayoutItem:
+ // hack because QStyleOptionFrameV2 doesn't have a frameStyle member
+ if (const QFrame *frame = qobject_cast<const QFrame *>(widget)) {
+ rect = opt->rect;
+ switch (frame->frameStyle() & QFrame::Shape_Mask) {
+ case QFrame::HLine:
+ rect.adjust(0, +1, 0, -1);
+ break;
+ case QFrame::VLine:
+ rect.adjust(+1, 0, -1, 0);
+ break;
+ default:
+ ;
+ }
+ }
+ break;
+ case SE_GroupBoxLayoutItem:
+ rect = opt->rect;
+ if (const QStyleOptionGroupBox *groupBoxOpt =
+ qstyleoption_cast<const QStyleOptionGroupBox *>(opt)) {
+ /*
+ AHIG is very inconsistent when it comes to group boxes.
+ Basically, we make sure that (non-checkable) group boxes
+ and tab widgets look good when laid out side by side.
+ */
+ if (groupBoxOpt->subControls & (QStyle::SC_GroupBoxCheckBox
+ | QStyle::SC_GroupBoxLabel)) {
+ int delta;
+ if (groupBoxOpt->subControls & QStyle::SC_GroupBoxCheckBox) {
+ delta = SIZE(8, 4, 4); // guess
+ } else {
+ delta = SIZE(15, 12, 12); // guess
+ }
+ rect.setTop(rect.top() + delta);
+ }
+ }
+ rect.setBottom(rect.bottom() - 1);
+ break;
+ case SE_TabWidgetLayoutItem:
+ if (const QStyleOptionTabWidgetFrame *tabWidgetOpt =
+ qstyleoption_cast<const QStyleOptionTabWidgetFrame *>(opt)) {
+ /*
+ AHIG specifies "12 or 14" as the distance from the window
+ edge. We choose 14 and since the default top margin is 20,
+ the overlap is 6.
+ */
+ rect = tabWidgetOpt->rect;
+ if (tabWidgetOpt->shape == QTabBar::RoundedNorth)
+ rect.setTop(rect.top() + SIZE(6 /* AHIG */, 3 /* guess */, 2 /* AHIG */));
+ }
+ break;
+ default:
+ rect = QWindowsStyle::subElementRect(sr, opt, widget);
+ break;
+ }
+ return rect;
+}
+
+static inline void drawToolbarButtonArrow(const QRect &toolButtonRect, ThemeDrawState tds, CGContextRef cg)
+{
+ QRect arrowRect = QRect(toolButtonRect.right() - 9, toolButtonRect.bottom() - 9, 7, 5);
+ HIThemePopupArrowDrawInfo padi;
+ padi.version = qt_mac_hitheme_version;
+ padi.state = tds;
+ padi.orientation = kThemeArrowDown;
+ padi.size = kThemeArrow7pt;
+ HIRect hirect = qt_hirectForQRect(arrowRect);
+ HIThemeDrawPopupArrow(&hirect, &padi, cg, kHIThemeOrientationNormal);
+}
+
+/*! \reimp */
+void QMacStyle::drawComplexControl(ComplexControl cc, const QStyleOptionComplex *opt, QPainter *p,
+ const QWidget *widget) const
+{
+ ThemeDrawState tds = d->getDrawState(opt->state);
+ QMacCGContext cg(p);
+ switch (cc) {
+ case CC_Slider:
+ case CC_ScrollBar:
+ if (const QStyleOptionSlider *slider = qstyleoption_cast<const QStyleOptionSlider *>(opt)) {
+ HIThemeTrackDrawInfo tdi;
+ d->getSliderInfo(cc, slider, &tdi, widget);
+ if (slider->state & State_Sunken) {
+ if (cc == CC_Slider) {
+ if (slider->activeSubControls == SC_SliderHandle)
+ tdi.trackInfo.slider.pressState = kThemeThumbPressed;
+ else if (slider->activeSubControls == SC_SliderGroove)
+ tdi.trackInfo.slider.pressState = kThemeLeftTrackPressed;
+ } else {
+ if (slider->activeSubControls == SC_ScrollBarSubLine
+ || slider->activeSubControls == SC_ScrollBarAddLine) {
+ // This test looks complex but it basically boils down
+ // to the following: The "RTL look" on the mac also
+ // changed the directions of the controls, that's not
+ // what people expect (an arrow is an arrow), so we
+ // kind of fake and say the opposite button is hit.
+ // This works great, up until 10.4 which broke the
+ // scroll bars, so I also have actually do something
+ // similar when I have an upside down scroll bar
+ // because on Tiger I only "fake" the reverse stuff.
+ bool reverseHorizontal = (slider->direction == Qt::RightToLeft
+ && slider->orientation == Qt::Horizontal
+ && (!slider->upsideDown
+ || (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_4
+ && slider->upsideDown)));
+ if ((reverseHorizontal
+ && slider->activeSubControls == SC_ScrollBarAddLine)
+ || (!reverseHorizontal
+ && slider->activeSubControls == SC_ScrollBarSubLine)) {
+ tdi.trackInfo.scrollbar.pressState = kThemeRightInsideArrowPressed
+ | kThemeLeftOutsideArrowPressed;
+ } else {
+ tdi.trackInfo.scrollbar.pressState = kThemeLeftInsideArrowPressed
+ | kThemeRightOutsideArrowPressed;
+ }
+ } else if (slider->activeSubControls == SC_ScrollBarAddPage) {
+ tdi.trackInfo.scrollbar.pressState = kThemeRightTrackPressed;
+ } else if (slider->activeSubControls == SC_ScrollBarSubPage) {
+ tdi.trackInfo.scrollbar.pressState = kThemeLeftTrackPressed;
+ } else if (slider->activeSubControls == SC_ScrollBarSlider) {
+ tdi.trackInfo.scrollbar.pressState = kThemeThumbPressed;
+ }
+ }
+ }
+ HIRect macRect;
+ bool tracking = slider->sliderPosition == slider->sliderValue;
+ if (!tracking) {
+ // Small optimization, the same as q->subControlRect
+ QCFType<HIShapeRef> shape;
+ HIThemeGetTrackThumbShape(&tdi, &shape);
+ ptrHIShapeGetBounds(shape, &macRect);
+ tdi.value = slider->sliderValue;
+ }
+
+ // Remove controls from the scroll bar if it is to short to draw them correctly.
+ // This is done in two stages: first the thumb indicator is removed when it is
+ // no longer possible to move it, second the up/down buttons are removed when
+ // there is not enough space for them.
+ if (cc == CC_ScrollBar) {
+ const int scrollBarLenght = (slider->orientation == Qt::Horizontal)
+ ? slider->rect.width() : slider->rect.height();
+ const QMacStyle::WidgetSizePolicy sizePolicy = widgetSizePolicy(widget);
+ if (scrollBarLenght < scrollButtonsCutoffSize(thumbIndicatorCutoff, sizePolicy))
+ tdi.attributes &= ~kThemeTrackShowThumb;
+ if (scrollBarLenght < scrollButtonsCutoffSize(scrollButtonsCutoff, sizePolicy))
+ tdi.enableState = kThemeTrackNothingToScroll;
+ }
+
+ HIThemeDrawTrack(&tdi, tracking ? 0 : &macRect, cg,
+ kHIThemeOrientationNormal);
+ if (cc == CC_Slider && slider->subControls & SC_SliderTickmarks) {
+ if (qt_mac_is_metal(widget)) {
+ if (tdi.enableState == kThemeTrackInactive)
+ tdi.enableState = kThemeTrackActive; // Looks more Cocoa-like
+ }
+ int interval = slider->tickInterval;
+ if (interval == 0) {
+ interval = slider->pageStep;
+ if (interval == 0)
+ interval = slider->singleStep;
+ if (interval == 0)
+ interval = 1;
+ }
+ int numMarks = 1 + ((slider->maximum - slider->minimum) / interval);
+
+ if (tdi.trackInfo.slider.thumbDir == kThemeThumbPlain) {
+ // They asked for both, so we'll give it to them.
+ tdi.trackInfo.slider.thumbDir = kThemeThumbDownward;
+ HIThemeDrawTrackTickMarks(&tdi, numMarks,
+ cg,
+ kHIThemeOrientationNormal);
+ tdi.trackInfo.slider.thumbDir = kThemeThumbUpward;
+ HIThemeDrawTrackTickMarks(&tdi, numMarks,
+ cg,
+ kHIThemeOrientationNormal);
+ } else {
+ HIThemeDrawTrackTickMarks(&tdi, numMarks,
+ cg,
+ kHIThemeOrientationNormal);
+
+ }
+ }
+ }
+ break;
+ case CC_Q3ListView:
+ if (const QStyleOptionQ3ListView *lv = qstyleoption_cast<const QStyleOptionQ3ListView *>(opt)) {
+ if (lv->subControls & SC_Q3ListView)
+ QWindowsStyle::drawComplexControl(cc, lv, p, widget);
+ if (lv->subControls & (SC_Q3ListViewBranch | SC_Q3ListViewExpand)) {
+ int y = lv->rect.y();
+ int h = lv->rect.height();
+ int x = lv->rect.right() - 10;
+ for (int i = 1; i < lv->items.size() && y < h; ++i) {
+ QStyleOptionQ3ListViewItem item = lv->items.at(i);
+ if (y + item.height > 0 && (item.childCount > 0
+ || (item.features & (QStyleOptionQ3ListViewItem::Expandable
+ | QStyleOptionQ3ListViewItem::Visible))
+ == (QStyleOptionQ3ListViewItem::Expandable
+ | QStyleOptionQ3ListViewItem::Visible))) {
+ QStyleOption treeOpt(0);
+ treeOpt.rect.setRect(x, y + item.height / 2 - 4, 9, 9);
+ treeOpt.palette = lv->palette;
+ treeOpt.state = lv->state;
+ treeOpt.state |= State_Children;
+ if (item.state & State_Open)
+ treeOpt.state |= State_Open;
+ drawPrimitive(PE_IndicatorBranch, &treeOpt, p, widget);
+ }
+ y += item.totalHeight;
+ }
+ }
+ }
+ break;
+ case CC_SpinBox:
+ if (const QStyleOptionSpinBox *sb = qstyleoption_cast<const QStyleOptionSpinBox *>(opt)) {
+ QStyleOptionSpinBox newSB = *sb;
+ if (sb->frame && (sb->subControls & SC_SpinBoxFrame)) {
+ SInt32 frame_size;
+ GetThemeMetric(kThemeMetricEditTextFrameOutset, &frame_size);
+
+ QRect lineeditRect = subControlRect(CC_SpinBox, sb, SC_SpinBoxEditField, widget);
+ lineeditRect.adjust(-frame_size, -frame_size, +frame_size, +frame_size);
+
+ HIThemeFrameDrawInfo fdi;
+ fdi.version = qt_mac_hitheme_version;
+ fdi.state = kThemeStateInactive;
+ fdi.kind = kHIThemeFrameTextFieldSquare;
+ fdi.isFocused = false;
+ HIRect hirect = qt_hirectForQRect(lineeditRect);
+ HIThemeDrawFrame(&hirect, &fdi, cg, kHIThemeOrientationNormal);
+ }
+ if (sb->subControls & (SC_SpinBoxUp | SC_SpinBoxDown)) {
+ HIThemeButtonDrawInfo bdi;
+ bdi.version = qt_mac_hitheme_version;
+ QAquaWidgetSize aquaSize = d->aquaSizeConstrain(opt, widget);
+ switch (aquaSize) {
+ case QAquaSizeUnknown:
+ case QAquaSizeLarge:
+ bdi.kind = kThemeIncDecButton;
+ break;
+ case QAquaSizeMini:
+ case QAquaSizeSmall:
+ if (aquaSize == QAquaSizeMini)
+ bdi.kind = kThemeIncDecButtonMini;
+ else
+ bdi.kind = kThemeIncDecButtonSmall;
+ break;
+ }
+ if (!(sb->stepEnabled & (QAbstractSpinBox::StepUpEnabled
+ | QAbstractSpinBox::StepDownEnabled)))
+ tds = kThemeStateUnavailable;
+ if (sb->activeSubControls == SC_SpinBoxDown
+ && (sb->state & State_Sunken))
+ tds = kThemeStatePressedDown;
+ else if (sb->activeSubControls == SC_SpinBoxUp
+ && (sb->state & State_Sunken))
+ tds = kThemeStatePressedUp;
+ bdi.state = tds;
+ if (!(sb->state & State_Active)
+ && sb->palette.currentColorGroup() == QPalette::Active
+ && tds == kThemeStateInactive)
+ bdi.state = kThemeStateActive;
+ bdi.value = kThemeButtonOff;
+ bdi.adornment = kThemeAdornmentNone;
+
+ QRect updown = subControlRect(CC_SpinBox, sb, SC_SpinBoxUp,
+ widget);
+ updown |= subControlRect(CC_SpinBox, sb, SC_SpinBoxDown, widget);
+ HIRect newRect = qt_hirectForQRect(updown);
+ QRect off_rct;
+ HIRect outRect;
+ HIThemeGetButtonBackgroundBounds(&newRect, &bdi, &outRect);
+ off_rct.setRect(int(newRect.origin.x - outRect.origin.x),
+ int(newRect.origin.y - outRect.origin.y),
+ int(outRect.size.width - newRect.size.width),
+ int(outRect.size.height - newRect.size.height));
+
+ // HIThemeGetButtonBackgroundBounds offsets non-focused normal sized
+ // buttons by one in de y direction, account for that here.
+ if (bdi.adornment == kThemeAdornmentNone && bdi.kind == kThemeIncDecButton)
+ off_rct.adjust(0, 1, 0, 0);
+
+ // Adjust the rect for small buttos also.
+ if (bdi.adornment == kThemeAdornmentFocus && bdi.kind == kThemeIncDecButtonSmall)
+ off_rct.adjust(0, 0, 0, -1);
+
+ newRect = qt_hirectForQRect(updown, off_rct);
+ HIThemeDrawButton(&newRect, &bdi, cg, kHIThemeOrientationNormal, 0);
+ }
+ }
+ break;
+ case CC_ComboBox:
+ if (const QStyleOptionComboBox *combo = qstyleoption_cast<const QStyleOptionComboBox *>(opt)){
+ HIThemeButtonDrawInfo bdi;
+ d->initComboboxBdi(combo, &bdi, widget, d->getDrawState(opt->state));
+ bool drawColorless = combo->palette.currentColorGroup() == QPalette::Active && tds == kThemeStateInactive;
+ if (!drawColorless)
+ QMacStylePrivate::drawCombobox(qt_hirectForQRect(combo->rect), bdi, p);
+ else
+ d->drawColorlessButton(qt_hirectForQRect(combo->rect), &bdi, p, opt);
+ }
+ break;
+ case CC_TitleBar:
+ if (const QStyleOptionTitleBar *titlebar
+ = qstyleoption_cast<const QStyleOptionTitleBar *>(opt)) {
+ if (titlebar->state & State_Active) {
+ if (titlebar->titleBarState & State_Active)
+ tds = kThemeStateActive;
+ else
+ tds = kThemeStateInactive;
+ } else {
+ tds = kThemeStateInactive;
+ }
+
+ HIThemeWindowDrawInfo wdi;
+ wdi.version = qt_mac_hitheme_version;
+ wdi.state = tds;
+ wdi.windowType = QtWinType;
+ wdi.titleHeight = titlebar->rect.height();
+ wdi.titleWidth = titlebar->rect.width();
+ wdi.attributes = kThemeWindowHasTitleText;
+ // It seems HIThemeDrawTitleBarWidget is not able to draw a dirty
+ // close button, so use HIThemeDrawWindowFrame instead.
+ if (widget && widget->isWindowModified() && titlebar->subControls & SC_TitleBarCloseButton)
+ wdi.attributes |= kThemeWindowHasCloseBox | kThemeWindowHasDirty;
+
+ HIRect titleBarRect;
+ HIRect tmpRect = qt_hirectForQRect(titlebar->rect);
+ {
+ QCFType<HIShapeRef> titleRegion;
+ QRect newr = titlebar->rect.adjusted(0, 0, 2, 0);
+ HIThemeGetWindowShape(&tmpRect, &wdi, kWindowTitleBarRgn, &titleRegion);
+ ptrHIShapeGetBounds(titleRegion, &tmpRect);
+ newr.translate(newr.x() - int(tmpRect.origin.x), newr.y() - int(tmpRect.origin.y));
+ titleBarRect = qt_hirectForQRect(newr);
+ }
+ HIThemeDrawWindowFrame(&titleBarRect, &wdi, cg, kHIThemeOrientationNormal, 0);
+ if (titlebar->subControls & (SC_TitleBarCloseButton
+ | SC_TitleBarMaxButton
+ | SC_TitleBarMinButton
+ | SC_TitleBarNormalButton)) {
+ HIThemeWindowWidgetDrawInfo wwdi;
+ wwdi.version = qt_mac_hitheme_version;
+ wwdi.widgetState = tds;
+ if (titlebar->state & State_MouseOver)
+ wwdi.widgetState = kThemeStateRollover;
+ wwdi.windowType = QtWinType;
+ wwdi.attributes = wdi.attributes | kThemeWindowHasFullZoom | kThemeWindowHasCloseBox | kThemeWindowHasCollapseBox;
+ wwdi.windowState = wdi.state;
+ wwdi.titleHeight = wdi.titleHeight;
+ wwdi.titleWidth = wdi.titleWidth;
+ ThemeDrawState savedControlState = wwdi.widgetState;
+ uint sc = SC_TitleBarMinButton;
+ ThemeTitleBarWidget tbw = kThemeWidgetCollapseBox;
+ bool active = titlebar->state & State_Active;
+ int border = 2;
+ titleBarRect.origin.x += border;
+ titleBarRect.origin.y -= border;
+
+ while (sc <= SC_TitleBarCloseButton) {
+ if (sc & titlebar->subControls) {
+ uint tmp = sc;
+ wwdi.widgetState = savedControlState;
+ wwdi.widgetType = tbw;
+ if (sc == SC_TitleBarMinButton)
+ tmp |= SC_TitleBarNormalButton;
+ if (active && (titlebar->activeSubControls & tmp)
+ && (titlebar->state & State_Sunken))
+ wwdi.widgetState = kThemeStatePressed;
+ // Draw all sub controllers except the dirty close button
+ // (it is already handled by HIThemeDrawWindowFrame).
+ if (!(widget && widget->isWindowModified() && tbw == kThemeWidgetCloseBox)) {
+ HIThemeDrawTitleBarWidget(&titleBarRect, &wwdi, cg, kHIThemeOrientationNormal);
+ p->paintEngine()->syncState();
+ }
+ }
+ sc = sc << 1;
+ tbw = tbw >> 1;
+ }
+ }
+ p->paintEngine()->syncState();
+ if (titlebar->subControls & SC_TitleBarLabel) {
+ int iw = 0;
+ if (!titlebar->icon.isNull()) {
+ QCFType<HIShapeRef> titleRegion2;
+ HIThemeGetWindowShape(&titleBarRect, &wdi, kWindowTitleProxyIconRgn,
+ &titleRegion2);
+ ptrHIShapeGetBounds(titleRegion2, &tmpRect);
+ if (tmpRect.size.width != 1) {
+ int iconExtent = pixelMetric(PM_SmallIconSize);
+ iw = titlebar->icon.actualSize(QSize(iconExtent, iconExtent)).width();
+ }
+ }
+ if (!titlebar->text.isEmpty()) {
+ p->save();
+ QCFType<HIShapeRef> titleRegion3;
+ HIThemeGetWindowShape(&titleBarRect, &wdi, kWindowTitleTextRgn, &titleRegion3);
+ ptrHIShapeGetBounds(titleRegion3, &tmpRect);
+ p->setClipRect(qt_qrectForHIRect(tmpRect));
+ QRect br = p->clipRegion().boundingRect();
+ int x = br.x(),
+ y = br.y() + (titlebar->rect.height() / 2 - p->fontMetrics().height() / 2);
+ if (br.width() <= (p->fontMetrics().width(titlebar->text) + iw * 2))
+ x += iw;
+ else
+ x += br.width() / 2 - p->fontMetrics().width(titlebar->text) / 2;
+ if (iw)
+ p->drawPixmap(x - iw, y, titlebar->icon.pixmap(pixelMetric(PM_SmallIconSize), QIcon::Normal));
+ drawItemText(p, br, Qt::AlignCenter, opt->palette, tds == kThemeStateActive,
+ titlebar->text, QPalette::Text);
+ p->restore();
+ }
+ }
+ }
+ break;
+ case CC_GroupBox:
+ if (const QStyleOptionGroupBox *groupBox
+ = qstyleoption_cast<const QStyleOptionGroupBox *>(opt)) {
+
+ QStyleOptionGroupBox groupBoxCopy(*groupBox);
+ if ((widget && !widget->testAttribute(Qt::WA_SetFont))
+ && QApplication::desktopSettingsAware())
+ groupBoxCopy.subControls = groupBoxCopy.subControls & ~SC_GroupBoxLabel;
+ QWindowsStyle::drawComplexControl(cc, &groupBoxCopy, p, widget);
+ if (groupBoxCopy.subControls != groupBox->subControls) {
+ bool checkable = groupBox->subControls & SC_GroupBoxCheckBox;
+ p->save();
+ CGContextSetShouldAntialias(cg, true);
+ CGContextSetShouldSmoothFonts(cg, true);
+ HIThemeTextInfo tti;
+ tti.version = qt_mac_hitheme_version;
+ tti.state = tds;
+ QColor textColor = groupBox->palette.windowText().color();
+ CGFloat colorComp[] = { textColor.redF(), textColor.greenF(),
+ textColor.blueF(), textColor.alphaF() };
+ CGContextSetFillColorSpace(cg, QCoreGraphicsPaintEngine::macGenericColorSpace());
+ CGContextSetFillColor(cg, colorComp);
+ tti.fontID = checkable ? kThemeSystemFont : kThemeSmallSystemFont;
+ tti.horizontalFlushness = kHIThemeTextHorizontalFlushCenter;
+ tti.verticalFlushness = kHIThemeTextVerticalFlushCenter;
+ tti.options = kHIThemeTextBoxOptionNone;
+ tti.truncationPosition = kHIThemeTextTruncationNone;
+ tti.truncationMaxLines = 1 + groupBox->text.count(QLatin1Char('\n'));
+ QCFString groupText = qt_mac_removeMnemonics(groupBox->text);
+ QRect r = subControlRect(CC_GroupBox, groupBox, SC_GroupBoxLabel, widget);
+ HIRect bounds = qt_hirectForQRect(r);
+ HIThemeDrawTextBox(groupText, &bounds, &tti, cg, kHIThemeOrientationNormal);
+ p->restore();
+ }
+ }
+ break;
+ case CC_ToolButton:
+ if (const QStyleOptionToolButton *tb
+ = qstyleoption_cast<const QStyleOptionToolButton *>(opt)) {
+ if (widget && qobject_cast<QToolBar *>(widget->parentWidget())) {
+ if (tb->subControls & SC_ToolButtonMenu) {
+ QStyleOption arrowOpt(0);
+ arrowOpt.rect = subControlRect(cc, tb, SC_ToolButtonMenu, widget);
+ arrowOpt.rect.setY(arrowOpt.rect.y() + arrowOpt.rect.height() / 2);
+ arrowOpt.rect.setHeight(arrowOpt.rect.height() / 2);
+ arrowOpt.state = tb->state;
+ arrowOpt.palette = tb->palette;
+ drawPrimitive(PE_IndicatorArrowDown, &arrowOpt, p, widget);
+ } else if (tb->features & QStyleOptionToolButton::HasMenu) {
+ drawToolbarButtonArrow(tb->rect, tds, cg);
+ }
+ if (tb->state & State_On) {
+ QPen oldPen = p->pen();
+ p->setPen(QColor(0, 0, 0, 0x3a));
+ p->fillRect(tb->rect.adjusted(1, 1, -1, -1), QColor(0, 0, 0, 0x12));
+ p->drawLine(tb->rect.left() + 1, tb->rect.top(),
+ tb->rect.right() - 1, tb->rect.top());
+ p->drawLine(tb->rect.left() + 1, tb->rect.bottom(),
+ tb->rect.right() - 1, tb->rect.bottom());
+ p->drawLine(tb->rect.topLeft(), tb->rect.bottomLeft());
+ p->drawLine(tb->rect.topRight(), tb->rect.bottomRight());
+ p->setPen(oldPen);
+ }
+ drawControl(CE_ToolButtonLabel, opt, p, widget);
+ } else {
+ ThemeButtonKind bkind = kThemeBevelButton;
+ switch (d->aquaSizeConstrain(opt, widget)) {
+ case QAquaSizeUnknown:
+ case QAquaSizeLarge:
+ bkind = kThemeBevelButton;
+ break;
+ case QAquaSizeMini:
+#if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_3) && 0
+ if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_3) {
+ bkind = kThemeMiniBevelButton;
+ break;
+ }
+#endif
+ case QAquaSizeSmall:
+ bkind = kThemeSmallBevelButton;
+ break;
+ }
+
+ QRect button, menuarea;
+ button = subControlRect(cc, tb, SC_ToolButton, widget);
+ menuarea = subControlRect(cc, tb, SC_ToolButtonMenu, widget);
+ State bflags = tb->state,
+ mflags = tb->state;
+ if (tb->subControls & SC_ToolButton)
+ bflags |= State_Sunken;
+ if (tb->subControls & SC_ToolButtonMenu)
+ mflags |= State_Sunken;
+
+ if (tb->subControls & SC_ToolButton) {
+ if (bflags & (State_Sunken | State_On | State_Raised)) {
+ HIThemeButtonDrawInfo bdi;
+ bdi.version = qt_mac_hitheme_version;
+ bdi.state = tds;
+ bdi.adornment = kThemeAdornmentNone;
+ bdi.kind = bkind;
+ bdi.value = kThemeButtonOff;
+ if (tb->state & State_HasFocus)
+ bdi.adornment = kThemeAdornmentFocus;
+ if (tb->state & State_Sunken)
+ bdi.state = kThemeStatePressed;
+ if (tb->state & State_On)
+ bdi.value = kThemeButtonOn;
+
+ QRect off_rct(0, 0, 0, 0);
+ HIRect myRect, macRect;
+ myRect = CGRectMake(tb->rect.x(), tb->rect.y(),
+ tb->rect.width(), tb->rect.height());
+ HIThemeGetButtonBackgroundBounds(&myRect, &bdi, &macRect);
+ off_rct.setRect(int(myRect.origin.x - macRect.origin.x),
+ int(myRect.origin.y - macRect.origin.y),
+ int(macRect.size.width - myRect.size.width),
+ int(macRect.size.height - myRect.size.height));
+
+ myRect = qt_hirectForQRect(button, off_rct);
+ HIThemeDrawButton(&myRect, &bdi, cg, kHIThemeOrientationNormal, 0);
+ }
+ }
+
+ if (tb->subControls & SC_ToolButtonMenu) {
+ HIThemeButtonDrawInfo bdi;
+ bdi.version = qt_mac_hitheme_version;
+ bdi.state = tds;
+ bdi.value = kThemeButtonOff;
+ bdi.adornment = kThemeAdornmentNone;
+ bdi.kind = bkind;
+ if (tb->state & State_HasFocus)
+ bdi.adornment = kThemeAdornmentFocus;
+ if (tb->state & (State_On | State_Sunken)
+ || (tb->activeSubControls & SC_ToolButtonMenu))
+ bdi.state = kThemeStatePressed;
+ HIRect hirect = qt_hirectForQRect(menuarea);
+ HIThemeDrawButton(&hirect, &bdi, cg, kHIThemeOrientationNormal, 0);
+ QRect r(menuarea.x() + ((menuarea.width() / 2) - 3), menuarea.height() - 8, 8, 8);
+ HIThemePopupArrowDrawInfo padi;
+ padi.version = qt_mac_hitheme_version;
+ padi.state = tds;
+ padi.orientation = kThemeArrowDown;
+ padi.size = kThemeArrow7pt;
+ hirect = qt_hirectForQRect(r);
+ HIThemeDrawPopupArrow(&hirect, &padi, cg, kHIThemeOrientationNormal);
+ } else if (tb->features & QStyleOptionToolButton::HasMenu) {
+ drawToolbarButtonArrow(tb->rect, tds, cg);
+ }
+ QRect buttonRect = subControlRect(CC_ToolButton, tb, SC_ToolButton, widget);
+ int fw = pixelMetric(PM_DefaultFrameWidth, opt, widget);
+ QStyleOptionToolButton label = *tb;
+ label.rect = buttonRect.adjusted(fw, fw, -fw, -fw);
+ drawControl(CE_ToolButtonLabel, &label, p, widget);
+ }
+ }
+ break;
+ default:
+ QWindowsStyle::drawComplexControl(cc, opt, p, widget);
+ break;
+ }
+}
+
+/*! \reimp */
+QStyle::SubControl QMacStyle::hitTestComplexControl(ComplexControl cc,
+ const QStyleOptionComplex *opt,
+ const QPoint &pt, const QWidget *widget) const
+{
+ SubControl sc = QStyle::SC_None;
+ switch (cc) {
+ case CC_ComboBox:
+ if (const QStyleOptionComboBox *cmb = qstyleoption_cast<const QStyleOptionComboBox *>(opt)) {
+ sc = QWindowsStyle::hitTestComplexControl(cc, cmb, pt, widget);
+ if (!cmb->editable && sc != QStyle::SC_None)
+ sc = SC_ComboBoxArrow; // A bit of a lie, but what we want
+ }
+ break;
+ case CC_Slider:
+ if (const QStyleOptionSlider *slider = qstyleoption_cast<const QStyleOptionSlider *>(opt)) {
+ HIThemeTrackDrawInfo tdi;
+ d->getSliderInfo(cc, slider, &tdi, widget);
+ ControlPartCode part;
+ HIPoint pos = CGPointMake(pt.x(), pt.y());
+ if (HIThemeHitTestTrack(&tdi, &pos, &part)) {
+ if (part == kControlPageUpPart || part == kControlPageDownPart)
+ sc = SC_SliderGroove;
+ else
+ sc = SC_SliderHandle;
+ }
+ }
+ break;
+ case CC_ScrollBar:
+ if (const QStyleOptionSlider *sb = qstyleoption_cast<const QStyleOptionSlider *>(opt)) {
+ HIScrollBarTrackInfo sbi;
+ sbi.version = qt_mac_hitheme_version;
+ if (!(sb->state & State_Active))
+ sbi.enableState = kThemeTrackInactive;
+ else if (sb->state & State_Enabled)
+ sbi.enableState = kThemeTrackActive;
+ else
+ sbi.enableState = kThemeTrackDisabled;
+
+ // The arrow buttons are not drawn if the scroll bar is to short,
+ // exclude them from the hit test.
+ const int scrollBarLenght = (sb->orientation == Qt::Horizontal)
+ ? sb->rect.width() : sb->rect.height();
+ if (scrollBarLenght < scrollButtonsCutoffSize(scrollButtonsCutoff, widgetSizePolicy(widget)))
+ sbi.enableState = kThemeTrackNothingToScroll;
+
+ sbi.viewsize = sb->pageStep;
+ HIPoint pos = CGPointMake(pt.x(), pt.y());
+
+ HIRect macSBRect = qt_hirectForQRect(sb->rect);
+ ControlPartCode part;
+ bool reverseHorizontal = (sb->direction == Qt::RightToLeft
+ && sb->orientation == Qt::Horizontal
+ && (!sb->upsideDown ||
+ (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_4
+ && sb->upsideDown)));
+ if (HIThemeHitTestScrollBarArrows(&macSBRect, &sbi, sb->orientation == Qt::Horizontal,
+ &pos, 0, &part)) {
+ if (part == kControlUpButtonPart)
+ sc = reverseHorizontal ? SC_ScrollBarAddLine : SC_ScrollBarSubLine;
+ else if (part == kControlDownButtonPart)
+ sc = reverseHorizontal ? SC_ScrollBarSubLine : SC_ScrollBarAddLine;
+ } else {
+ HIThemeTrackDrawInfo tdi;
+ d->getSliderInfo(cc, sb, &tdi, widget);
+ if(tdi.enableState == kThemeTrackInactive)
+ tdi.enableState = kThemeTrackActive;
+ if (HIThemeHitTestTrack(&tdi, &pos, &part)) {
+ if (part == kControlPageUpPart)
+ sc = reverseHorizontal ? SC_ScrollBarAddPage
+ : SC_ScrollBarSubPage;
+ else if (part == kControlPageDownPart)
+ sc = reverseHorizontal ? SC_ScrollBarSubPage
+ : SC_ScrollBarAddPage;
+ else
+ sc = SC_ScrollBarSlider;
+ }
+ }
+ }
+ break;
+/*
+ I don't know why, but we only get kWindowContentRgn here, which isn't what we want at all.
+ It would be very nice if this would work.
+ case QStyle::CC_TitleBar:
+ if (const QStyleOptionTitleBar *tbar = qstyleoption_cast<const QStyleOptionTitleBar *>(opt)) {
+ HIThemeWindowDrawInfo wdi;
+ memset(&wdi, 0, sizeof(wdi));
+ wdi.version = qt_mac_hitheme_version;
+ wdi.state = kThemeStateActive;
+ wdi.windowType = QtWinType;
+ wdi.titleWidth = tbar->rect.width();
+ wdi.titleHeight = tbar->rect.height();
+ if (tbar->titleBarState)
+ wdi.attributes |= kThemeWindowHasFullZoom | kThemeWindowHasCloseBox
+ | kThemeWindowHasCollapseBox;
+ else if (tbar->titleBarFlags & Qt::WindowSystemMenuHint)
+ wdi.attributes |= kThemeWindowHasCloseBox;
+ QRect tmpRect = tbar->rect;
+ tmpRect.setHeight(tmpRect.height() + 100);
+ HIRect hirect = qt_hirectForQRect(tmpRect);
+ WindowRegionCode hit;
+ HIPoint hipt = CGPointMake(pt.x(), pt.y());
+ if (HIThemeGetWindowRegionHit(&hirect, &wdi, &hipt, &hit)) {
+ switch (hit) {
+ case kWindowCloseBoxRgn:
+ sc = QStyle::SC_TitleBarCloseButton;
+ break;
+ case kWindowCollapseBoxRgn:
+ sc = QStyle::SC_TitleBarMinButton;
+ break;
+ case kWindowZoomBoxRgn:
+ sc = QStyle::SC_TitleBarMaxButton;
+ break;
+ case kWindowTitleTextRgn:
+ sc = QStyle::SC_TitleBarLabel;
+ break;
+ default:
+ qDebug("got something else %d", hit);
+ break;
+ }
+ }
+ }
+ break;
+*/
+ default:
+ sc = QWindowsStyle::hitTestComplexControl(cc, opt, pt, widget);
+ break;
+ }
+ return sc;
+}
+
+/*! \reimp */
+QRect QMacStyle::subControlRect(ComplexControl cc, const QStyleOptionComplex *opt, SubControl sc,
+ const QWidget *widget) const
+{
+ QRect ret;
+ switch (cc) {
+ case CC_Slider:
+ case CC_ScrollBar:
+ if (const QStyleOptionSlider *slider = qstyleoption_cast<const QStyleOptionSlider *>(opt)) {
+ HIThemeTrackDrawInfo tdi;
+ d->getSliderInfo(cc, slider, &tdi, widget);
+ HIRect macRect;
+ QCFType<HIShapeRef> shape;
+ bool scrollBar = cc == CC_ScrollBar;
+ if ((scrollBar && sc == SC_ScrollBarSlider)
+ || (!scrollBar && sc == SC_SliderHandle)) {
+ HIThemeGetTrackThumbShape(&tdi, &shape);
+ ptrHIShapeGetBounds(shape, &macRect);
+ } else if (!scrollBar && sc == SC_SliderGroove) {
+ HIThemeGetTrackBounds(&tdi, &macRect);
+ } else if (sc == SC_ScrollBarGroove) { // Only scroll bar parts available...
+ HIThemeGetTrackDragRect(&tdi, &macRect);
+ } else {
+ ControlPartCode cpc;
+ if (sc == SC_ScrollBarSubPage || sc == SC_ScrollBarAddPage) {
+ cpc = sc == SC_ScrollBarSubPage ? kControlPageDownPart
+ : kControlPageUpPart;
+ } else {
+ cpc = sc == SC_ScrollBarSubLine ? kControlUpButtonPart
+ : kControlDownButtonPart;
+ if (slider->direction == Qt::RightToLeft
+ && slider->orientation == Qt::Horizontal
+ && (!slider->upsideDown
+ || (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_4
+ && slider->upsideDown))
+ ) {
+ if (cpc == kControlDownButtonPart)
+ cpc = kControlUpButtonPart;
+ else if (cpc == kControlUpButtonPart)
+ cpc = kControlDownButtonPart;
+ }
+ }
+ HIThemeGetTrackPartBounds(&tdi, cpc, &macRect);
+ }
+ ret = qt_qrectForHIRect(macRect);
+
+ // Tweak: the dark line between the sub/add line buttons belong to only one of the buttons
+ // when doing hit-testing, but both of them have to repaint it. Extend the rect to cover
+ // the line in the cases where HIThemeGetTrackPartBounds returns a rect that doesn't.
+ if (slider->orientation == Qt::Horizontal) {
+ if (slider->direction == Qt::LeftToRight && sc == SC_ScrollBarSubLine)
+ ret.adjust(0, 0, 1, 0);
+ else if (slider->direction == Qt::RightToLeft && sc == SC_ScrollBarAddLine)
+ ret.adjust(-1, 0, 1, 0);
+ } else if (sc == SC_ScrollBarAddLine) {
+ ret.adjust(0, -1, 0, 1);
+ }
+ }
+ break;
+ case CC_TitleBar:
+ if (const QStyleOptionTitleBar *titlebar
+ = qstyleoption_cast<const QStyleOptionTitleBar *>(opt)) {
+ HIThemeWindowDrawInfo wdi;
+ memset(&wdi, 0, sizeof(wdi));
+ wdi.version = qt_mac_hitheme_version;
+ wdi.state = kThemeStateActive;
+ wdi.windowType = QtWinType;
+ wdi.titleHeight = titlebar->rect.height();
+ wdi.titleWidth = titlebar->rect.width();
+ wdi.attributes = kThemeWindowHasTitleText;
+ if (titlebar->subControls & SC_TitleBarCloseButton)
+ wdi.attributes |= kThemeWindowHasCloseBox;
+ if (titlebar->subControls & SC_TitleBarMaxButton
+ | SC_TitleBarNormalButton)
+ wdi.attributes |= kThemeWindowHasFullZoom;
+ if (titlebar->subControls & SC_TitleBarMinButton)
+ wdi.attributes |= kThemeWindowHasCollapseBox;
+ WindowRegionCode wrc = kWindowGlobalPortRgn;
+
+ if (sc == SC_TitleBarCloseButton)
+ wrc = kWindowCloseBoxRgn;
+ else if (sc == SC_TitleBarMinButton)
+ wrc = kWindowCollapseBoxRgn;
+ else if (sc == SC_TitleBarMaxButton)
+ wrc = kWindowZoomBoxRgn;
+ else if (sc == SC_TitleBarLabel)
+ wrc = kWindowTitleTextRgn;
+ else if (sc == SC_TitleBarSysMenu)
+ ret.setRect(-1024, -1024, 10, pixelMetric(PM_TitleBarHeight,
+ titlebar, widget));
+ if (wrc != kWindowGlobalPortRgn) {
+ QCFType<HIShapeRef> region;
+ QRect tmpRect = titlebar->rect;
+ HIRect titleRect = qt_hirectForQRect(tmpRect);
+ HIThemeGetWindowShape(&titleRect, &wdi, kWindowTitleBarRgn, &region);
+ ptrHIShapeGetBounds(region, &titleRect);
+ CFRelease(region);
+ tmpRect.translate(tmpRect.x() - int(titleRect.origin.x),
+ tmpRect.y() - int(titleRect.origin.y));
+ titleRect = qt_hirectForQRect(tmpRect);
+ HIThemeGetWindowShape(&titleRect, &wdi, wrc, &region);
+ ptrHIShapeGetBounds(region, &titleRect);
+ ret = qt_qrectForHIRect(titleRect);
+ }
+ }
+ break;
+ case CC_ComboBox:
+ if (const QStyleOptionComboBox *combo = qstyleoption_cast<const QStyleOptionComboBox *>(opt)) {
+ HIThemeButtonDrawInfo bdi;
+ d->initComboboxBdi(combo, &bdi, widget, d->getDrawState(opt->state));
+
+ switch (sc) {
+ case SC_ComboBoxEditField:{
+ ret = QMacStylePrivate::comboboxEditBounds(combo->rect, bdi);
+ // hack to posistion the edit feld correctly for QDateTimeEdits
+ // in calendarPopup mode.
+ if (qobject_cast<const QDateTimeEdit *>(widget)) {
+ ret.moveTop(ret.top() - 2);
+ ret.setHeight(ret.height() +1);
+ }
+ break; }
+ case SC_ComboBoxArrow:{
+ ret = QMacStylePrivate::comboboxEditBounds(combo->rect, bdi);
+ ret.setX(ret.x() + ret.width());
+ ret.setWidth(combo->rect.width() - ret.width() - ret.x());
+ break; }
+ case SC_ComboBoxListBoxPopup:{
+ if (combo->editable) {
+ HIRect inner = QMacStylePrivate::comboboxInnerBounds(qt_hirectForQRect(combo->rect), bdi.kind);
+ QRect editRect = QMacStylePrivate::comboboxEditBounds(combo->rect, bdi);
+ ret.adjust(qRound(inner.origin.x), 0, qRound(inner.origin.x + inner.size.width), editRect.y() + editRect.height() + 2);
+ } else {
+ QRect editRect = QMacStylePrivate::comboboxEditBounds(combo->rect, bdi);
+ ret.adjust(4 - 11, 1, editRect.width() + 10 + 11, 1);
+ }
+ break; }
+ default:
+ break;
+ }
+ }
+ break;
+ case CC_GroupBox:
+ if (const QStyleOptionGroupBox *groupBox = qstyleoption_cast<const QStyleOptionGroupBox *>(opt)) {
+ bool checkable = groupBox->subControls & SC_GroupBoxCheckBox;
+ bool flat = (groupBox->features & QStyleOptionFrameV2::Flat);
+ bool hasNoText = !checkable && groupBox->text.isEmpty();
+ switch (sc) {
+ case SC_GroupBoxLabel:
+ case SC_GroupBoxCheckBox: {
+ // Cheat and use the smaller font if we need to
+ bool checkable = groupBox->subControls & SC_GroupBoxCheckBox;
+ bool fontIsSet = (widget && widget->testAttribute(Qt::WA_SetFont)
+ || !QApplication::desktopSettingsAware());
+ int tw;
+ int h;
+ int margin = flat || hasNoText ? 0 : 12;
+ ret = groupBox->rect.adjusted(margin, 0, -margin, 0);
+
+ if (!fontIsSet) {
+ HIThemeTextInfo tti;
+ tti.version = qt_mac_hitheme_version;
+ tti.state = kThemeStateActive;
+ tti.fontID = checkable ? kThemeSystemFont : kThemeSmallSystemFont;
+ tti.horizontalFlushness = kHIThemeTextHorizontalFlushCenter;
+ tti.verticalFlushness = kHIThemeTextVerticalFlushCenter;
+ tti.options = kHIThemeTextBoxOptionNone;
+ tti.truncationPosition = kHIThemeTextTruncationNone;
+ tti.truncationMaxLines = 1 + groupBox->text.count(QLatin1Char('\n'));
+ CGFloat width;
+ CGFloat height;
+ QCFString groupText = qt_mac_removeMnemonics(groupBox->text);
+ HIThemeGetTextDimensions(groupText, 0, &tti, &width, &height, 0);
+ tw = int(width);
+ h = int(height);
+ } else {
+ QFontMetrics fm = groupBox->fontMetrics;
+ if (!checkable && !fontIsSet)
+ fm = QFontMetrics(qt_app_fonts_hash()->value("QSmallFont", QFont()));
+ h = fm.height();
+ tw = fm.size(Qt::TextShowMnemonic, groupBox->text).width();
+ }
+ ret.setHeight(h);
+
+ QRect labelRect = alignedRect(groupBox->direction, groupBox->textAlignment,
+ QSize(tw, h), ret);
+ int indicatorWidth = pixelMetric(PM_IndicatorWidth, opt, widget);
+ bool rtl = groupBox->direction == Qt::RightToLeft;
+ if (sc == SC_GroupBoxLabel) {
+ if (checkable) {
+ int newSum = indicatorWidth + 1;
+ int newLeft = labelRect.left() + (rtl ? -newSum : newSum);
+ labelRect.moveLeft(newLeft);
+ } else if (flat) {
+ int newLeft = labelRect.left() - (rtl ? 3 : -3);
+ labelRect.moveLeft(newLeft);
+ labelRect.moveTop(labelRect.top() + 3);
+ } else {
+ int newLeft = labelRect.left() - (rtl ? 3 : 2);
+ labelRect.moveLeft(newLeft);
+ labelRect.moveTop(labelRect.top() + 5);
+ }
+ ret = labelRect;
+ }
+
+ if (sc == SC_GroupBoxCheckBox) {
+ int left = rtl ? labelRect.right() - indicatorWidth : labelRect.left();
+ ret.setRect(left, ret.top(),
+ indicatorWidth, pixelMetric(PM_IndicatorHeight, opt, widget));
+ }
+ break;
+ }
+ case SC_GroupBoxContents:
+ case SC_GroupBoxFrame: {
+ if (flat) {
+ ret = QWindowsStyle::subControlRect(cc, groupBox, sc, widget);
+ break;
+ }
+ QFontMetrics fm = groupBox->fontMetrics;
+ bool checkable = groupBox->subControls & SC_GroupBoxCheckBox;
+ int yOffset = 3;
+ if (!checkable) {
+ if (widget && !widget->testAttribute(Qt::WA_SetFont)
+ && QApplication::desktopSettingsAware())
+ fm = QFontMetrics(qt_app_fonts_hash()->value("QSmallFont", QFont()));
+ yOffset = 5;
+ if (hasNoText)
+ yOffset = -fm.height();
+ }
+
+ ret = opt->rect.adjusted(0, fm.height() + yOffset, 0, 0);
+ if (sc == SC_GroupBoxContents)
+ ret.adjust(3, 3, -3, -4); // guess
+ }
+ break;
+ default:
+ ret = QWindowsStyle::subControlRect(cc, groupBox, sc, widget);
+ break;
+ }
+ }
+ break;
+ case CC_SpinBox:
+ if (const QStyleOptionSpinBox *spin = qstyleoption_cast<const QStyleOptionSpinBox *>(opt)) {
+ const int spinner_w = 14,
+ fw = pixelMetric(PM_SpinBoxFrameWidth, spin, widget);
+ switch (sc) {
+ case SC_SpinBoxUp:
+ case SC_SpinBoxDown: {
+ if (spin->buttonSymbols == QAbstractSpinBox::NoButtons)
+ break;
+ const int frameWidth = pixelMetric(PM_SpinBoxFrameWidth, spin, widget);
+ const int spinner_w = 18;
+ const int y = frameWidth;
+ const int x = spin->rect.width() - spinner_w + frameWidth;
+ ret.setRect(x + spin->rect.x(), y + spin->rect.y(), spinner_w, spin->rect.height() - y * 2);
+ HIThemeButtonDrawInfo bdi;
+ bdi.version = qt_mac_hitheme_version;
+ bdi.kind = kThemeIncDecButton;
+ QAquaWidgetSize aquaSize = d->aquaSizeConstrain(opt, widget);
+ switch (aquaSize) {
+ case QAquaSizeUnknown:
+ case QAquaSizeLarge:
+ bdi.kind = kThemeIncDecButton;
+ break;
+ case QAquaSizeMini:
+ case QAquaSizeSmall:
+ if (aquaSize == QAquaSizeMini)
+ bdi.kind = kThemeIncDecButtonMini;
+ else
+ bdi.kind = kThemeIncDecButtonSmall;
+ break;
+ }
+ bdi.state = kThemeStateActive;
+ bdi.value = kThemeButtonOff;
+ bdi.adornment = kThemeAdornmentNone;
+ HIRect hirect = qt_hirectForQRect(ret);
+ HIRect outRect;
+ HIThemeGetButtonBackgroundBounds(&hirect, &bdi, &outRect);
+ ret = qt_qrectForHIRect(outRect);
+ switch (sc) {
+ case SC_SpinBoxUp:
+ ret.setHeight(ret.height() / 2);
+ break;
+ case SC_SpinBoxDown:
+ ret.setY(ret.y() + ret.height() / 2);
+ break;
+ default:
+ Q_ASSERT(0);
+ break;
+ }
+ ret.translate(-1, -2); // hack: position the buttons correctly (weird that we need this)
+ ret = visualRect(spin->direction, spin->rect, ret);
+ break;
+ }
+ case SC_SpinBoxEditField:
+ ret.setRect(fw, fw,
+ spin->rect.width() - spinner_w - fw * 2 - macSpinBoxSep + 1,
+ spin->rect.height() - fw * 2);
+ ret = visualRect(spin->direction, spin->rect, ret);
+ break;
+ default:
+ ret = QWindowsStyle::subControlRect(cc, spin, sc, widget);
+ break;
+ }
+ }
+ break;
+ case CC_ToolButton:
+ ret = QWindowsStyle::subControlRect(cc, opt, sc, widget);
+ if (sc == SC_ToolButtonMenu && widget && !qobject_cast<QToolBar*>(widget->parentWidget())) {
+ ret.adjust(-1, 0, 0, 0);
+ }
+ break;
+ default:
+ ret = QWindowsStyle::subControlRect(cc, opt, sc, widget);
+ break;
+ }
+ return ret;
+}
+
+/*! \reimp */
+QSize QMacStyle::sizeFromContents(ContentsType ct, const QStyleOption *opt,
+ const QSize &csz, const QWidget *widget) const
+{
+ QSize sz(csz);
+ bool useAquaGuideline = true;
+
+ switch (ct) {
+ case QStyle::CT_SpinBox:
+ sz.setWidth(sz.width() + macSpinBoxSep);
+ sz.setHeight(sz.height() - 3); // hack to work around horrible sizeHint() code in QAbstractSpinBox
+ break;
+ case QStyle::CT_TabBarTab:
+ if (const QStyleOptionTabV3 *tab = qstyleoption_cast<const QStyleOptionTabV3 *>(opt)) {
+ bool newStyleTabs =
+#if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4)
+ QSysInfo::MacintoshVersion >= QSysInfo::MV_10_4 ? true :
+#endif
+ false;
+ const QAquaWidgetSize AquaSize = d->aquaSizeConstrain(opt, widget);
+ const bool differentFont = (widget && widget->testAttribute(Qt::WA_SetFont))
+ || !QApplication::desktopSettingsAware();
+ ThemeTabDirection ttd = getTabDirection(tab->shape);
+ bool vertTabs = ttd == kThemeTabWest || ttd == kThemeTabEast;
+ if (vertTabs)
+ sz.transpose();
+ if (newStyleTabs) {
+ int defaultTabHeight;
+ int defaultExtraSpace = pixelMetric(PM_TabBarTabHSpace, tab, widget); // Remove spurious gcc warning (AFAIK)
+ QFontMetrics fm = opt->fontMetrics;
+ switch (AquaSize) {
+ case QAquaSizeUnknown:
+ case QAquaSizeLarge:
+ if (tab->documentMode)
+ defaultTabHeight = 23;
+ else
+ defaultTabHeight = 21;
+ break;
+ case QAquaSizeSmall:
+ defaultTabHeight = 18;
+ break;
+ case QAquaSizeMini:
+ defaultTabHeight = 16;
+ break;
+ }
+
+ bool setWidth = false;
+ if (differentFont || !tab->icon.isNull()) {
+ sz.rheight() = qMax(defaultTabHeight, sz.height());
+ } else {
+ QSize textSize = fm.size(Qt::TextShowMnemonic, tab->text);
+ sz.rheight() = qMax(defaultTabHeight, textSize.height());
+ sz.rwidth() = textSize.width() + defaultExtraSpace;
+ setWidth = true;
+ }
+
+ if (vertTabs)
+ sz.transpose();
+
+ int maxWidgetHeight = qMax(tab->leftButtonSize.height(), tab->rightButtonSize.height());
+ int maxWidgetWidth = qMax(tab->leftButtonSize.width(), tab->rightButtonSize.width());
+
+ int widgetWidth = 0;
+ int widgetHeight = 0;
+ int padding = 0;
+ if (tab->leftButtonSize.isValid()) {
+ padding += 8;
+ widgetWidth += tab->leftButtonSize.width();
+ widgetHeight += tab->leftButtonSize.height();
+ }
+ if (tab->rightButtonSize.isValid()) {
+ padding += 8;
+ widgetWidth += tab->rightButtonSize.width();
+ widgetHeight += tab->rightButtonSize.height();
+ }
+
+ if (vertTabs) {
+ sz.setHeight(sz.height() + widgetHeight + padding);
+ sz.setWidth(qMax(sz.width(), maxWidgetWidth));
+ } else {
+ if (setWidth)
+ sz.setWidth(sz.width() + widgetWidth + padding);
+ sz.setHeight(qMax(sz.height(), maxWidgetHeight));
+ }
+ } else {
+ SInt32 tabh = sz.height();
+ SInt32 overlap = 0;
+ switch (AquaSize) {
+ default:
+ case QAquaSizeUnknown:
+ case QAquaSizeLarge:
+ GetThemeMetric(kThemeLargeTabHeight, &tabh);
+ GetThemeMetric(kThemeMetricTabFrameOverlap, &overlap);
+ break;
+ case QAquaSizeMini:
+ GetThemeMetric(kThemeMetricMiniTabHeight, &tabh);
+ GetThemeMetric(kThemeMetricMiniTabFrameOverlap, &overlap);
+ break;
+ case QAquaSizeSmall:
+ GetThemeMetric(kThemeSmallTabHeight, &tabh);
+ GetThemeMetric(kThemeMetricSmallTabFrameOverlap, &overlap);
+ break;
+ }
+ tabh += overlap;
+ if (sz.height() < tabh)
+ sz.rheight() = tabh;
+ }
+ }
+ break;
+ case QStyle::CT_PushButton:
+ // By default, we fit the contents inside a normal rounded push button.
+ // Do this by add enough space around the contents so that rounded
+ // borders (including highlighting when active) will show.
+ sz.rwidth() += PushButtonLeftOffset + PushButtonRightOffset + 12;
+ sz.rheight() += PushButtonTopOffset + PushButtonBottomOffset;
+ break;
+ case QStyle::CT_MenuItem:
+ if (const QStyleOptionMenuItem *mi = qstyleoption_cast<const QStyleOptionMenuItem *>(opt)) {
+ int maxpmw = mi->maxIconWidth;
+ const QComboBox *comboBox = qobject_cast<const QComboBox *>(widget);
+ int w = sz.width(),
+ h = sz.height();
+ if (mi->menuItemType == QStyleOptionMenuItem::Separator) {
+ w = 10;
+ SInt16 ash;
+ GetThemeMenuSeparatorHeight(&ash);
+ h = ash;
+ } else {
+ h = mi->fontMetrics.height() + 2;
+ if (!mi->icon.isNull()) {
+ if (comboBox) {
+ const QSize &iconSize = comboBox->iconSize();
+ h = qMax(h, iconSize.height() + 4);
+ maxpmw = qMax(maxpmw, iconSize.width());
+ } else {
+ int iconExtent = pixelMetric(PM_SmallIconSize);
+ h = qMax(h, mi->icon.actualSize(QSize(iconExtent, iconExtent)).height() + 4);
+ }
+ }
+ }
+ if (mi->text.contains(QLatin1Char('\t')))
+ w += 12;
+ if (mi->menuItemType == QStyleOptionMenuItem::SubMenu)
+ w += 20;
+ if (maxpmw)
+ w += maxpmw + 6;
+ // add space for a check. All items have place for a check too.
+ w += 20;
+ if (comboBox && comboBox->isVisible()) {
+ QStyleOptionComboBox cmb;
+ cmb.initFrom(comboBox);
+ cmb.editable = false;
+ cmb.subControls = QStyle::SC_ComboBoxEditField;
+ cmb.activeSubControls = QStyle::SC_None;
+ w = qMax(w, subControlRect(QStyle::CC_ComboBox, &cmb,
+ QStyle::SC_ComboBoxEditField,
+ comboBox).width());
+ } else {
+ w += 12;
+ }
+ sz = QSize(w, h);
+ }
+ break;
+ case CT_ToolButton:
+ sz.rwidth() += 10;
+ sz.rheight() += 10;
+ return sz;
+ case CT_ComboBox:
+ sz.rwidth() += 50;
+ break;
+ case CT_Menu: {
+ QStyleHintReturnMask menuMask;
+ QStyleOption myOption = *opt;
+ myOption.rect.setSize(sz);
+ if (styleHint(SH_Menu_Mask, &myOption, widget, &menuMask)) {
+ sz = menuMask.region.boundingRect().size();
+ }
+ break; }
+ case CT_HeaderSection:{
+ const QStyleOptionHeader *header = qstyleoption_cast<const QStyleOptionHeader *>(opt);
+ sz = QWindowsStyle::sizeFromContents(ct, opt, csz, widget);
+ if (header->text.contains(QLatin1Char('\n')))
+ useAquaGuideline = false;
+ break; }
+ case CT_ScrollBar :
+ // Make sure that the scroll bar is large enough to display the thumb indicator.
+ if (const QStyleOptionSlider *slider = qstyleoption_cast<const QStyleOptionSlider *>(opt)) {
+ const int minimumSize = scrollButtonsCutoffSize(thumbIndicatorCutoff, widgetSizePolicy(widget));
+ if (slider->orientation == Qt::Horizontal)
+ sz = sz.expandedTo(QSize(minimumSize, sz.height()));
+ else
+ sz = sz.expandedTo(QSize(sz.width(), minimumSize));
+ }
+ break;
+ default:
+ sz = QWindowsStyle::sizeFromContents(ct, opt, csz, widget);
+ }
+
+ if (useAquaGuideline){
+ QSize macsz;
+ if (d->aquaSizeConstrain(opt, widget, ct, sz, &macsz) != QAquaSizeUnknown) {
+ if (macsz.width() != -1)
+ sz.setWidth(macsz.width());
+ if (macsz.height() != -1)
+ sz.setHeight(macsz.height());
+ }
+ }
+
+ // The sizes that Carbon and the guidelines gives us excludes the focus frame.
+ // We compensate for this by adding some extra space here to make room for the frame when drawing:
+ if (const QStyleOptionComboBox *combo = qstyleoption_cast<const QStyleOptionComboBox *>(opt)){
+ QAquaWidgetSize widgetSize = d->aquaSizeConstrain(opt, widget);
+ int bkind = 0;
+ switch (widgetSize) {
+ default:
+ case QAquaSizeLarge:
+ bkind = combo->editable ? kThemeComboBox : kThemePopupButton;
+ break;
+ case QAquaSizeSmall:
+ bkind = combo->editable ? int(kThemeComboBoxSmall) : int(kThemePopupButtonSmall);
+ break;
+ case QAquaSizeMini:
+ bkind = combo->editable ? kThemeComboBoxMini : kThemePopupButtonMini;
+ break;
+ }
+ HIRect tmpRect = {{0, 0}, {0, 0}};
+ HIRect diffRect = QMacStylePrivate::comboboxInnerBounds(tmpRect, bkind);
+ sz.rwidth() -= qRound(diffRect.size.width);
+ sz.rheight() -= qRound(diffRect.size.height);
+ } else if (ct == CT_PushButton || ct == CT_ToolButton){
+ ThemeButtonKind bkind;
+ QAquaWidgetSize widgetSize = d->aquaSizeConstrain(opt, widget);
+ switch (ct) {
+ default:
+ case CT_PushButton:
+ if (const QStyleOptionButton *btn = qstyleoption_cast<const QStyleOptionButton *>(opt)) {
+ if (btn->features & QStyleOptionButton::CommandLinkButton) {
+ return QWindowsStyle::sizeFromContents(ct, opt, sz, widget);
+ }
+ }
+
+ switch (widgetSize) {
+ default:
+ case QAquaSizeLarge:
+ bkind = kThemePushButton;
+ break;
+ case QAquaSizeSmall:
+ bkind = kThemePushButtonSmall;
+ break;
+ case QAquaSizeMini:
+ bkind = kThemePushButtonMini;
+ break;
+ }
+ break;
+ case CT_ToolButton:
+ switch (widgetSize) {
+ default:
+ case QAquaSizeLarge:
+ bkind = kThemeLargeBevelButton;
+ break;
+ case QAquaSizeMini:
+ case QAquaSizeSmall:
+ bkind = kThemeSmallBevelButton;
+ }
+ break;
+ }
+
+ HIThemeButtonDrawInfo bdi;
+ bdi.version = qt_mac_hitheme_version;
+ bdi.state = kThemeStateActive;
+ bdi.kind = bkind;
+ bdi.value = kThemeButtonOff;
+ bdi.adornment = kThemeAdornmentNone;
+ HIRect macRect, myRect;
+ myRect = CGRectMake(0, 0, sz.width(), sz.height());
+ HIThemeGetButtonBackgroundBounds(&myRect, &bdi, &macRect);
+ // Mini buttons only return their actual size in HIThemeGetButtonBackgroundBounds, so help them out a bit (guess),
+ if (bkind == kThemePushButtonMini)
+ macRect.size.height += 8.;
+ else if (bkind == kThemePushButtonSmall)
+ macRect.size.height -= 10;
+ sz.setWidth(sz.width() + int(macRect.size.width - myRect.size.width));
+ sz.setHeight(sz.height() + int(macRect.size.height - myRect.size.height));
+ }
+ return sz;
+}
+
+/*!
+ \reimp
+*/
+void QMacStyle::drawItemText(QPainter *p, const QRect &r, int flags, const QPalette &pal,
+ bool enabled, const QString &text, QPalette::ColorRole textRole) const
+{
+ if(flags & Qt::TextShowMnemonic)
+ flags |= Qt::TextHideMnemonic;
+ QWindowsStyle::drawItemText(p, r, flags, pal, enabled, text, textRole);
+}
+
+/*!
+ \reimp
+*/
+bool QMacStyle::event(QEvent *e)
+{
+ if(e->type() == QEvent::FocusIn) {
+ QWidget *f = 0;
+ QWidget *focusWidget = QApplication::focusWidget();
+#ifndef QT_NO_GRAPHICSVIEW
+ if (QGraphicsView *graphicsView = qobject_cast<QGraphicsView *>(focusWidget)) {
+ QGraphicsItem *focusItem = graphicsView->scene() ? graphicsView->scene()->focusItem() : 0;
+ if (focusItem && focusItem->type() == QGraphicsProxyWidget::Type) {
+ QGraphicsProxyWidget *proxy = static_cast<QGraphicsProxyWidget *>(focusItem);
+ if (proxy->widget())
+ focusWidget = proxy->widget()->focusWidget();
+ }
+ }
+#endif
+ if (focusWidget && focusWidget->testAttribute(Qt::WA_MacShowFocusRect)) {
+ f = focusWidget;
+ QWidget *top = f->parentWidget();
+ while (top && !top->isWindow() && !(top->windowType() == Qt::SubWindow))
+ top = top->parentWidget();
+#ifndef QT_NO_MAINWINDOW
+ if (qobject_cast<QMainWindow *>(top)) {
+ QWidget *central = static_cast<QMainWindow *>(top)->centralWidget();
+ for (const QWidget *par = f; par; par = par->parentWidget()) {
+ if (par == central) {
+ top = central;
+ break;
+ }
+ if (par->isWindow())
+ break;
+ }
+ }
+#endif
+ }
+ if (f) {
+ if(!d->focusWidget)
+ d->focusWidget = new QFocusFrame(f);
+ d->focusWidget->setWidget(f);
+ } else if(d->focusWidget) {
+ d->focusWidget->setWidget(0);
+ }
+ } else if(e->type() == QEvent::FocusOut) {
+ if(d->focusWidget)
+ d->focusWidget->setWidget(0);
+ }
+ return false;
+}
+
+void qt_mac_constructQIconFromIconRef(const IconRef icon, const IconRef overlayIcon, QIcon *retIcon, QStyle::StandardPixmap standardIcon = QStyle::SP_CustomBase)
+{
+ int size = 16;
+ while (size <= 128) {
+
+ const QString cacheKey = QLatin1String("qt_mac_constructQIconFromIconRef") + QString::number(standardIcon) + QString::number(size);
+ QPixmap mainIcon;
+ if (standardIcon >= QStyle::SP_CustomBase) {
+ mainIcon = qt_mac_convert_iconref(icon, size, size);
+ } else if (QPixmapCache::find(cacheKey, mainIcon) == false) {
+ mainIcon = qt_mac_convert_iconref(icon, size, size);
+ QPixmapCache::insert(cacheKey, mainIcon);
+ }
+
+ if (overlayIcon) {
+ int littleSize = size / 2;
+ QPixmap overlayPix = qt_mac_convert_iconref(overlayIcon, littleSize, littleSize);
+ QPainter painter(&mainIcon);
+ painter.drawPixmap(size - littleSize, size - littleSize, overlayPix);
+ }
+
+ retIcon->addPixmap(mainIcon);
+ size += size; // 16 -> 32 -> 64 -> 128
+ }
+}
+
+/*!
+ \internal
+*/
+QIcon QMacStyle::standardIconImplementation(StandardPixmap standardIcon, const QStyleOption *opt,
+ const QWidget *widget) const
+{
+ OSType iconType = 0;
+ switch (standardIcon) {
+ case QStyle::SP_MessageBoxQuestion:
+ case QStyle::SP_MessageBoxInformation:
+ case QStyle::SP_MessageBoxWarning:
+ case QStyle::SP_MessageBoxCritical:
+ iconType = kGenericApplicationIcon;
+ break;
+ case SP_DesktopIcon:
+ iconType = kDesktopIcon;
+ break;
+ case SP_TrashIcon:
+ iconType = kTrashIcon;
+ break;
+#if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4)
+ case SP_ComputerIcon:
+ iconType = kComputerIcon;
+ break;
+#endif
+ case SP_DriveFDIcon:
+ iconType = kGenericFloppyIcon;
+ break;
+ case SP_DriveHDIcon:
+ iconType = kGenericHardDiskIcon;
+ break;
+ case SP_DriveCDIcon:
+ case SP_DriveDVDIcon:
+ iconType = kGenericCDROMIcon;
+ break;
+ case SP_DriveNetIcon:
+ iconType = kGenericNetworkIcon;
+ break;
+ case SP_DirOpenIcon:
+ iconType = kOpenFolderIcon;
+ break;
+ case SP_DirClosedIcon:
+ case SP_DirLinkIcon:
+ iconType = kGenericFolderIcon;
+ break;
+ case SP_FileLinkIcon:
+ case SP_FileIcon:
+ iconType = kGenericDocumentIcon;
+ break;
+ case SP_ToolBarHorizontalExtensionButton:
+ case SP_ToolBarVerticalExtensionButton: {
+ QPixmap pixmap(qt_mac_toolbar_ext);
+ if (standardIcon == SP_ToolBarVerticalExtensionButton) {
+ QPixmap pix2(pixmap.height(), pixmap.width());
+ pix2.fill(Qt::transparent);
+ QPainter p(&pix2);
+ p.translate(pix2.width(), 0);
+ p.rotate(90);
+ p.drawPixmap(0, 0, pixmap);
+ return pix2;
+ }
+ return pixmap;
+ }
+ break;
+ case SP_DirIcon: {
+ // A rather special case
+ QIcon closeIcon = QStyle::standardIcon(SP_DirClosedIcon, opt, widget);
+ QIcon openIcon = QStyle::standardIcon(SP_DirOpenIcon, opt, widget);
+ closeIcon.addPixmap(openIcon.pixmap(16, 16), QIcon::Normal, QIcon::On);
+ closeIcon.addPixmap(openIcon.pixmap(32, 32), QIcon::Normal, QIcon::On);
+ closeIcon.addPixmap(openIcon.pixmap(64, 64), QIcon::Normal, QIcon::On);
+ closeIcon.addPixmap(openIcon.pixmap(128, 128), QIcon::Normal, QIcon::On);
+ return closeIcon;
+ }
+ case SP_TitleBarNormalButton:
+ case SP_TitleBarCloseButton: {
+ QIcon titleBarIcon;
+ if (standardIcon == SP_TitleBarCloseButton) {
+ titleBarIcon.addFile(QLatin1String(":/trolltech/styles/macstyle/images/closedock-16.png"));
+ titleBarIcon.addFile(QLatin1String(":/trolltech/styles/macstyle/images/closedock-down-16.png"), QSize(16, 16), QIcon::Normal, QIcon::On);
+ } else {
+ titleBarIcon.addFile(QLatin1String(":/trolltech/styles/macstyle/images/dockdock-16.png"));
+ titleBarIcon.addFile(QLatin1String(":/trolltech/styles/macstyle/images/dockdock-down-16.png"), QSize(16, 16), QIcon::Normal, QIcon::On);
+ }
+ return titleBarIcon;
+ }
+ default:
+ break;
+ }
+ if (iconType != 0) {
+ QIcon retIcon;
+ IconRef icon;
+ IconRef overlayIcon = 0;
+ if (iconType != kGenericApplicationIcon) {
+ GetIconRef(kOnSystemDisk, kSystemIconsCreator, iconType, &icon);
+ } else {
+ FSRef fsRef;
+ ProcessSerialNumber psn = { 0, kCurrentProcess };
+ GetProcessBundleLocation(&psn, &fsRef);
+ GetIconRefFromFileInfo(&fsRef, 0, 0, 0, 0, kIconServicesNormalUsageFlag, &icon, 0);
+ if (standardIcon == SP_MessageBoxCritical) {
+ overlayIcon = icon;
+ GetIconRef(kOnSystemDisk, kSystemIconsCreator, kAlertCautionIcon, &icon);
+ }
+ }
+ if (icon) {
+ qt_mac_constructQIconFromIconRef(icon, overlayIcon, &retIcon, standardIcon);
+ ReleaseIconRef(icon);
+ }
+ if (overlayIcon)
+ ReleaseIconRef(overlayIcon);
+ return retIcon;
+ }
+ return QWindowsStyle::standardIconImplementation(standardIcon, opt, widget);
+}
+
+/*!
+ \internal
+*/
+int QMacStyle::layoutSpacingImplementation(QSizePolicy::ControlType control1,
+ QSizePolicy::ControlType control2,
+ Qt::Orientation orientation,
+ const QStyleOption *option,
+ const QWidget *widget) const
+{
+ const int ButtonMask = QSizePolicy::ButtonBox | QSizePolicy::PushButton;
+ bool isMetal = (widget && widget->testAttribute(Qt::WA_MacBrushedMetal));
+ int controlSize = getControlSize(option, widget);
+
+ if (control2 == QSizePolicy::ButtonBox) {
+ /*
+ AHIG seems to prefer a 12-pixel margin between group
+ boxes and the row of buttons. The 20 pixel comes from
+ Builder.
+ */
+ if (isMetal // (AHIG, guess, guess)
+ || (control1 & (QSizePolicy::Frame // guess
+ | QSizePolicy::GroupBox // (AHIG, guess, guess)
+ | QSizePolicy::TabWidget // guess
+ | ButtonMask))) { // AHIG
+ return_SIZE(14, 8, 8);
+ } else if (control1 == QSizePolicy::LineEdit) {
+ return_SIZE(8, 8, 8); // Interface Builder
+ } else {
+ return_SIZE(20, 7, 7); // Interface Builder
+ }
+ }
+
+ if ((control1 | control2) & ButtonMask) {
+ if (control1 == QSizePolicy::LineEdit)
+ return_SIZE(8, 8, 8); // Interface Builder
+ else if (control2 == QSizePolicy::LineEdit) {
+ if (orientation == Qt::Vertical)
+ return_SIZE(20, 7, 7); // Interface Builder
+ else
+ return_SIZE(20, 8, 8);
+ }
+ return_SIZE(14, 8, 8); // Interface Builder
+ }
+
+ switch (CT2(control1, control2)) {
+ case CT1(QSizePolicy::Label): // guess
+ case CT2(QSizePolicy::Label, QSizePolicy::DefaultType): // guess
+ case CT2(QSizePolicy::Label, QSizePolicy::CheckBox): // AHIG
+ case CT2(QSizePolicy::Label, QSizePolicy::ComboBox): // AHIG
+ case CT2(QSizePolicy::Label, QSizePolicy::LineEdit): // guess
+ case CT2(QSizePolicy::Label, QSizePolicy::RadioButton): // AHIG
+ case CT2(QSizePolicy::Label, QSizePolicy::Slider): // guess
+ case CT2(QSizePolicy::Label, QSizePolicy::SpinBox): // guess
+ case CT2(QSizePolicy::Label, QSizePolicy::ToolButton): // guess
+ return_SIZE(8, 6, 5);
+ case CT1(QSizePolicy::ToolButton):
+ return 8; // AHIG
+ case CT1(QSizePolicy::CheckBox):
+ case CT2(QSizePolicy::CheckBox, QSizePolicy::RadioButton):
+ case CT2(QSizePolicy::RadioButton, QSizePolicy::CheckBox):
+ if (orientation == Qt::Vertical)
+ return_SIZE(8, 8, 7); // AHIG and Builder
+ break;
+ case CT1(QSizePolicy::RadioButton):
+ if (orientation == Qt::Vertical)
+ return 5; // (Builder, guess, AHIG)
+ }
+
+ if (orientation == Qt::Horizontal
+ && (control2 & (QSizePolicy::CheckBox | QSizePolicy::RadioButton)))
+ return_SIZE(12, 10, 8); // guess
+
+ if ((control1 | control2) & (QSizePolicy::Frame
+ | QSizePolicy::GroupBox
+ | QSizePolicy::TabWidget)) {
+ /*
+ These values were chosen so that nested container widgets
+ look good side by side. Builder uses 8, which looks way
+ too small, and AHIG doesn't say anything.
+ */
+ return_SIZE(16, 10, 10); // guess
+ }
+
+ if ((control1 | control2) & (QSizePolicy::Line | QSizePolicy::Slider))
+ return_SIZE(12, 10, 8); // AHIG
+
+ if ((control1 | control2) & QSizePolicy::LineEdit)
+ return_SIZE(10, 8, 8); // AHIG
+
+ /*
+ AHIG and Builder differ by up to 4 pixels for stacked editable
+ comboboxes. We use some values that work fairly well in all
+ cases.
+ */
+ if ((control1 | control2) & QSizePolicy::ComboBox)
+ return_SIZE(10, 8, 7); // guess
+
+ /*
+ Builder defaults to 8, 6, 5 in lots of cases, but most of the time the
+ result looks too cramped.
+ */
+ return_SIZE(10, 8, 6); // guess
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/styles/qmacstylepixmaps_mac_p.h b/src/gui/styles/qmacstylepixmaps_mac_p.h
new file mode 100644
index 0000000000..0754508046
--- /dev/null
+++ b/src/gui/styles/qmacstylepixmaps_mac_p.h
@@ -0,0 +1,1467 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//
+// 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.
+//
+
+static const char *const qt_mac_tabnrm_left[]={
+"6 22 71 2",
+"#a c #5f5f5f",
+"## c #6d6d6d",
+".c c #737373",
+".d c #757575",
+".b c #7c7c7c",
+".n c #7d7d7d",
+".r c #828282",
+".f c #838383",
+"#. c #878787",
+".7 c #8c8c8c",
+".v c #8e8e8e",
+".4 c #939393",
+".1 c #969696",
+".j c #979797",
+".a c #9a9a9a",
+".6 c #9b9b9b",
+".A c #a0a0a0",
+".Z c #a6a6a6",
+".C c #aaaaaa",
+".3 c #aeaeae",
+".E c #afafaf",
+".J c #b3b3b3",
+".8 c #b7b7b7",
+".o c #b8b8b8",
+".g c #b9b9b9",
+".P c #bcbcbc",
+".R c #bfbfbf",
+".T c #c2c2c2",
+".s c #c4c4c4",
+"#e c #c6c6c6",
+".V c #c7c7c7",
+".w c #c9c9c9",
+".B c #cacaca",
+"#d c #cbcbcb",
+".z c #cecece",
+".F c #cfcfcf",
+".5 c #d1d1d1",
+"#c c #d3d3d3",
+".K c #d4d4d4",
+".G c #d5d5d5",
+".Q c #d6d6d6",
+".S c #d8d8d8",
+".U c #dadada",
+".2 c #dbdbdb",
+".L c #dcdcdc",
+".H c #dedede",
+".I c #dfdfdf",
+".M c #e0e0e0",
+".X c #e1e1e1",
+".D c #e2e2e2",
+"#b c #e3e3e3",
+".O c #e4e4e4",
+".N c #e6e6e6",
+".9 c #e7e7e7",
+".e c #e8e8e8",
+".# c #e9e9e9",
+".t c #ebebeb",
+"Qt c #ececec",
+".p c #ededed",
+".x c #eeeeee",
+".y c #efefef",
+".u c #f1f1f1",
+".k c #f2f2f2",
+".q c #f3f3f3",
+".W c #f6f6f6",
+".l c #f7f7f7",
+".m c #f8f8f8",
+".Y c #f9f9f9",
+".h c #fcfcfc",
+".i c #fdfdfd",
+".0 c #ffffff",
+"Qt.#.a.b.c.d",
+".e.f.gQt.h.i",
+".j.a.k.l.m.m",
+".n.o.p.k.q.q",
+".r.s.t.u.u.u",
+".v.w.t.x.y.y",
+".a.z.#.p.x.x",
+".A.B.#.t.x.p",
+".C.B.D.t.x.x",
+".E.F.G.H.I.I",
+".J.K.L.M.N.O",
+".P.Q.D.#.e.#",
+".R.S.O.p.x.p",
+".T.U.#.u.u.k",
+".V.I.t.q.W.W",
+".P.X.k.W.Y.Y",
+".Z.M.u.Y.0.0",
+".1.2.l.0.0.0",
+".3.4.m.0.0.0",
+".5.6.7.8.9.0",
+".X.5.3#.###a",
+".O#b.L#c#d#e"};
+
+static const char *const qt_mac_tabnrm_mid[]={
+"6 22 18 1",
+"n c #585858",
+". c #777777",
+"p c #c2c2c2",
+"o c #c3c3c3",
+"g c #dedede",
+"h c #e5e5e5",
+"i c #e8e8e8",
+"f c #ededed",
+"e c #eeeeee",
+"d c #efefef",
+"c c #f1f1f1",
+"b c #f3f3f3",
+"j c #f6f6f6",
+"a c #f8f8f8",
+"k c #f9f9f9",
+"# c #fdfdfd",
+"l c #fefefe",
+"m c #ffffff",
+"......",
+"######",
+"aaaaaa",
+"bbbbbb",
+"cccccc",
+"dddddd",
+"eeeeee",
+"ffffff",
+"eeeeee",
+"gggggg",
+"hhhhhh",
+"iiiiii",
+"eeeeee",
+"cccccc",
+"jjjjjj",
+"kkkkkk",
+"llllll",
+"mmmmmm",
+"mmmmmm",
+"mmmmmm",
+"nnnnnn",
+"oppooo"};
+
+static const char *const qt_mac_tabnrm_right[]={
+"6 22 70 2",
+".9 c #5f5f5f",
+"#. c #6d6d6d",
+".# c #747474",
+"Qt c #757575",
+".a c #7c7c7c",
+".p c #7d7d7d",
+".t c #828282",
+".h c #838383",
+"## c #888888",
+".6 c #8c8c8c",
+".x c #8e8e8e",
+".3 c #929292",
+".2 c #969696",
+".l c #979797",
+".b c #9a9a9a",
+".7 c #9b9b9b",
+".A c #a0a0a0",
+".0 c #a6a6a6",
+".C c #aaaaaa",
+"#a c #aeaeae",
+".H c #afafaf",
+".N c #b3b3b3",
+".5 c #b7b7b7",
+".o c #b8b8b8",
+".g c #b9b9b9",
+".Q c #bcbcbc",
+".S c #bfbfbf",
+".U c #c2c2c2",
+".s c #c4c4c4",
+"#b c #c6c6c6",
+".W c #c7c7c7",
+".w c #c9c9c9",
+".z c #cacaca",
+".y c #cecece",
+".G c #cfcfcf",
+".8 c #d1d1d1",
+"#c c #d3d3d3",
+".M c #d4d4d4",
+".F c #d5d5d5",
+".P c #d6d6d6",
+".R c #d8d8d8",
+".T c #dadada",
+".1 c #dbdbdb",
+".L c #dcdcdc",
+".E c #dedede",
+".D c #dfdfdf",
+".K c #e0e0e0",
+".Y c #e1e1e1",
+".B c #e2e2e2",
+".I c #e4e4e4",
+"#d c #e5e5e5",
+".J c #e6e6e6",
+".4 c #e7e7e7",
+".O c #e8e8e8",
+".c c #e9e9e9",
+".r c #ebebeb",
+".d c #ececec",
+".n c #ededed",
+".v c #eeeeee",
+".u c #efefef",
+".q c #f1f1f1",
+".k c #f2f2f2",
+".m c #f3f3f3",
+".V c #f6f6f6",
+".j c #f7f7f7",
+".i c #f8f8f8",
+".X c #f9f9f9",
+".f c #fcfcfc",
+".e c #fdfdfd",
+".Z c #ffffff",
+"Qt.#.a.b.c.d",
+".e.f.d.g.h.c",
+".i.i.j.k.b.l",
+".m.m.k.n.o.p",
+".q.q.q.r.s.t",
+".u.u.v.r.w.x",
+".v.v.n.c.y.b",
+".n.v.r.c.z.A",
+".v.v.r.B.z.C",
+".D.D.E.F.G.H",
+".I.J.K.L.M.N",
+".c.O.c.B.P.Q",
+".n.v.n.I.R.S",
+".k.q.q.c.T.U",
+".V.V.m.r.D.W",
+".X.X.V.k.Y.Q",
+".Z.Z.X.q.K.0",
+".Z.Z.Z.j.1.2",
+".Z.Z.Z.i.3.H",
+".Z.4.5.6.7.8",
+".9#.###a.8.B",
+"#b.z#c.L.I#d"};
+
+static const char *const qt_mac_tabselected_active_left[]={
+"6 22 130 2",
+".d c #000069",
+".C c #0042b4",
+".I c #0157bb",
+".w c #0c28a0",
+".l c #101094",
+".O c #1163c4",
+".c c #1a1a6e",
+".U c #1e6ec9",
+".r c #214eb7",
+".x c #256cc9",
+".0 c #2877ce",
+".6 c #347fd1",
+".D c #3981d2",
+"#a c #3a81d2",
+"#g c #418ad7",
+".J c #4288d4",
+".q c #45459b",
+".V c #458cd5",
+".g c #46469f",
+".P c #4a8dd7",
+".1 c #4a90db",
+"#m c #4b91db",
+"#s c #4e91dc",
+"#y c #4f8dcd",
+".7 c #5195df",
+".b c #525280",
+"#E c #5984b2",
+"#b c #5b9be1",
+"#R c #5c7e9f",
+".2 c #5d9de6",
+"#5 c #5f5f5f",
+"#Y c #627c8d",
+"#h c #65a4e7",
+".3 c #65a6e8",
+".8 c #67a6eb",
+"#n c #69a6e8",
+"#c c #6cabed",
+"#4 c #6d6d6d",
+".4 c #6dacee",
+".5 c #6eabee",
+"#K c #6f87a1",
+"#L c #70aff1",
+".9 c #70aff2",
+"#t c #71aced",
+"#z c #72afee",
+"#F c #72afef",
+".f c #757592",
+"#i c #75b3f4",
+"#d c #75b4f3",
+"#Z c #77a6b3",
+"#. c #77b1f4",
+"## c #7ab4f4",
+".W c #7cb0e7",
+"#o c #7cb8f9",
+"#f c #80bdf9",
+"#e c #81bbf9",
+"#j c #81bbfc",
+"#u c #86c1ff",
+"#3 c #878787",
+"#k c #89c2fd",
+"#A c #89c3ff",
+".Q c #8bb9e8",
+"#l c #8bc4ff",
+"#p c #8bc6ff",
+"#G c #8dc7ff",
+".K c #8ebae8",
+"#S c #8ecbff",
+"#q c #91c8ff",
+"#M c #92ccff",
+"#v c #93ccff",
+".k c #9494b0",
+"#r c #94caff",
+".E c #96bde8",
+".X c #96c3ee",
+"#w c #96cfff",
+"#B c #97cfff",
+".Y c #99c4ee",
+"#x c #99d0ff",
+".a c #9a9aa4",
+"#X c #9b9b9b",
+".s c #9bbee8",
+".R c #9bc4ee",
+".T c #9cc5ee",
+".Z c #9dc7ef",
+"#C c #9dd3ff",
+".y c #9ec3e8",
+".L c #9ec3ed",
+".M c #9ec5ed",
+"#H c #9ed6ff",
+".S c #9fc5ee",
+"#D c #a1d7ff",
+".N c #a3c8ed",
+"#N c #a3daff",
+".F c #a5c8ed",
+".G c #a7c9ed",
+"#J c #a7ddff",
+"#I c #a7deff",
+".H c #a8caee",
+"#T c #a8e0ff",
+"#0 c #a8e2e6",
+".z c #a9caed",
+"#O c #abe3ff",
+".A c #adcbed",
+"#P c #ade3ff",
+"#Q c #aeaeae",
+".m c #afbbe7",
+".B c #afccee",
+"#U c #b0e9ff",
+".t c #b3d1ed",
+"#V c #b5ebff",
+".u c #bad4ee",
+".v c #bbd4ef",
+".h c #bfc2e8",
+"#1 c #bffdff",
+"a# c #c6c6c6",
+"a. c #cbcbcb",
+".n c #cbddf2",
+".o c #cce0f3",
+".p c #cfe1f4",
+"#W c #d1d1d1",
+"#9 c #d3d3d3",
+"#8 c #dcdcdc",
+"#2 c #e1e1e1",
+"#7 c #e3e3e3",
+"#6 c #e4e4e4",
+".i c #e6e9f6",
+".e c #e8e8e8",
+".# c #e9e9e9",
+".j c #e9edf8",
+"Qt c #ececec",
+"Qt.#.a.b.c.d",
+".e.f.g.h.i.j",
+".k.l.m.n.o.p",
+".q.r.s.t.u.v",
+".w.x.y.z.A.B",
+".C.D.E.F.G.H",
+".I.J.K.L.M.N",
+".O.P.Q.R.S.T",
+".U.V.W.X.Y.Z",
+".0.1.2.3.4.5",
+".6.7.8.9#.##",
+"#a#b#c#d#e#f",
+"#g#h#i#j#k#l",
+"#m#n#o#p#q#r",
+"#s#t#u#v#w#x",
+"#y#z#A#B#C#D",
+"#E#F#G#H#I#J",
+"#K#L#M#N#O#P",
+"#Q#R#S#T#U#V",
+"#W#X#Y#Z#0#1",
+"#2#W#Q#3#4#5",
+"#6#7#8#9a.a#"};
+
+static const char *const qt_mac_tabselected_active_mid[]={
+"6 22 23 1",
+". c #00006d",
+"s c #585858",
+"h c #70adef",
+"i c #7bb5f5",
+"j c #83bcf9",
+"k c #8bc3ff",
+"l c #93c9ff",
+"m c #9ad1ff",
+"f c #9ec5ef",
+"g c #9ec7f0",
+"n c #a0d6ff",
+"e c #a2c7ed",
+"d c #a8caee",
+"o c #a8deff",
+"p c #ade2ff",
+"c c #afceee",
+"q c #b6ecff",
+"b c #bbd5f0",
+"r c #c1feff",
+"u c #c2c2c2",
+"t c #c3c3c3",
+"a c #cfe1f3",
+"# c #e9edf8",
+"......",
+"######",
+"aaaaaa",
+"bbbbbb",
+"cccccc",
+"dddddd",
+"eeeeee",
+"ffffff",
+"gggggg",
+"hhhhhh",
+"iiiiii",
+"jjjjjj",
+"kkkkkk",
+"llllll",
+"mmmmmm",
+"nnnnnn",
+"oooooo",
+"pppppp",
+"qqqqqq",
+"rrrrrr",
+"ssssss",
+"ttttuu"};
+
+static const char *const qt_mac_tabselected_active_right[]={
+"6 22 128 2",
+"Qt c #000069",
+".G c #0042b4",
+".M c #0157bb",
+".A c #0c28a0",
+".n c #101094",
+".S c #1163c4",
+".# c #1b1b6e",
+".Y c #1e6ec9",
+".t c #214eb7",
+".z c #256cc9",
+".4 c #2877ce",
+"#. c #347fd1",
+".F c #3981d2",
+"#e c #3a81d2",
+"#k c #418ad7",
+".L c #4288d4",
+".u c #45459b",
+".X c #458cd5",
+".h c #46469f",
+".R c #4a8dd7",
+".3 c #4a90db",
+"#q c #4b91db",
+"#w c #4e91dc",
+"#C c #4f8dcd",
+".9 c #5195df",
+".a c #525280",
+"#I c #5984b2",
+"#T c #5b7d9f",
+"#d c #5b9be1",
+".2 c #5d9de6",
+"#1 c #5f5f5f",
+"#Y c #627c8d",
+"#j c #65a4e7",
+".1 c #65a6e8",
+".8 c #67a6eb",
+"#p c #69a6e8",
+"#c c #6cabed",
+"#2 c #6d6d6d",
+".0 c #6dacee",
+".Z c #6eabee",
+"#O c #6f87a1",
+"#N c #70aff1",
+".7 c #70aff2",
+"#v c #71aced",
+"#B c #72afee",
+"#H c #72afef",
+".i c #757592",
+"#i c #75b3f4",
+"#b c #75b4f3",
+"#X c #77a6b3",
+".6 c #77b1f4",
+".5 c #7ab4f4",
+".W c #7cb0e7",
+"#o c #7cb8f9",
+"## c #80bdf9",
+"#a c #81bbf9",
+"#h c #81bbfc",
+"#u c #86c1ff",
+"#3 c #888888",
+"#g c #89c2fd",
+"#A c #89c3ff",
+".Q c #8bb9e8",
+"#f c #8bc4ff",
+"#n c #8bc6ff",
+"#G c #8dc7ff",
+".K c #8ebae8",
+"#S c #8ecbff",
+"#m c #91c8ff",
+"#M c #92ccff",
+"#t c #93ccff",
+".o c #9494b0",
+"#l c #94caff",
+".E c #96bde8",
+".V c #96c3ee",
+"#s c #96cfff",
+"#z c #97cfff",
+".U c #99c4ee",
+"#r c #99d0ff",
+".b c #9a9aa4",
+"#Z c #9b9b9b",
+".s c #9bbee8",
+".P c #9bc4ee",
+".N c #9cc5ee",
+".T c #9dc7ef",
+"#y c #9dd3ff",
+".y c #9ec3e8",
+".J c #9ec3ed",
+".I c #9ec5ed",
+"#F c #9ed6ff",
+".O c #9fc5ee",
+"#x c #a1d7ff",
+".H c #a3c8ed",
+"#L c #a3daff",
+".D c #a5c8ed",
+".C c #a7c9ed",
+"#D c #a7ddff",
+"#E c #a7deff",
+".B c #a8caee",
+"#R c #a8e0ff",
+"#W c #a8e2e6",
+".x c #a9caed",
+"#K c #abe3ff",
+".w c #adcbed",
+"#J c #ade3ff",
+"#U c #aeaeae",
+".m c #afbbe7",
+".v c #afccee",
+"#Q c #b0e9ff",
+".r c #b3d1ed",
+"#P c #b5ebff",
+".q c #bad4ee",
+".p c #bbd4ef",
+".g c #bfc2e8",
+"#V c #bffdff",
+"#5 c #c6c6c6",
+"#6 c #cacaca",
+".l c #cbddf2",
+".k c #cce0f3",
+".j c #cfe1f4",
+"#0 c #d1d1d1",
+"#7 c #d3d3d3",
+"#8 c #dcdcdc",
+"#4 c #e2e2e2",
+"#9 c #e4e4e4",
+".f c #e6e9f6",
+".c c #e9e9e9",
+".e c #e9edf8",
+".d c #ececec",
+"Qt.#.a.b.c.d",
+".e.f.g.h.i.c",
+".j.k.l.m.n.o",
+".p.q.r.s.t.u",
+".v.w.x.y.z.A",
+".B.C.D.E.F.G",
+".H.I.J.K.L.M",
+".N.O.P.Q.R.S",
+".T.U.V.W.X.Y",
+".Z.0.1.2.3.4",
+".5.6.7.8.9#.",
+"###a#b#c#d#e",
+"#f#g#h#i#j#k",
+"#l#m#n#o#p#q",
+"#r#s#t#u#v#w",
+"#x#y#z#A#B#C",
+"#D#E#F#G#H#I",
+"#J#K#L#M#N#O",
+"#P#Q#R#S#T#U",
+"#V#W#X#Y#Z#0",
+"#1#2#3#U#0#4",
+"#5#6#7#8#9#9"};
+
+static const char *const qt_mac_tabselected_inactive_left[]={
+"6 22 64 1",
+"9 c #656565",
+"8 c #737373",
+"c c #767676",
+"b c #808080",
+"n c #818181",
+"q c #828282",
+"e c #888888",
+"7 c #8b8b8b",
+"u c #8d8d8d",
+"3 c #939393",
+"k c #979797",
+"0 c #989898",
+"j c #9b9b9b",
+"A c #9d9d9d",
+"a c #9e9e9e",
+"4 c #9f9f9f",
+"Y c #a3a3a3",
+"C c #a6a6a6",
+"E c #aaaaaa",
+"J c #aeaeae",
+"6 c #b0b0b0",
+"2 c #b1b1b1",
+"o c #b2b2b2",
+"f c #b3b3b3",
+"O c #b6b6b6",
+"Q c #b9b9b9",
+"S c #bbbbbb",
+"r c #bdbdbd",
+"U c #c0c0c0",
+"v c #c2c2c2",
+"B c #c3c3c3",
+"y c #c6c6c6",
+"F c #c7c7c7",
+"K c #cccccc",
+"G c #cdcdcd",
+"R c #cfcfcf",
+"T c #d1d1d1",
+"1 c #d2d2d2",
+"L c #d3d3d3",
+"H c #d5d5d5",
+"I c #d6d6d6",
+"W c #d7d7d7",
+"D c #d8d8d8",
+"N c #dadada",
+"M c #dcdcdc",
+"5 c #dddddd",
+"P c #dedede",
+"z c #dfdfdf",
+"s c #e0e0e0",
+"g c #e1e1e1",
+"p c #e2e2e2",
+"w c #e3e3e3",
+"x c #e4e4e4",
+"t c #e6e6e6",
+"l c #e7e7e7",
+"d c #e8e8e8",
+"# c #e9e9e9",
+"V c #eaeaea",
+"m c #ebebeb",
+". c #ececec",
+"X c #ededed",
+"h c #f0f0f0",
+"i c #f1f1f1",
+"Z c #f2f2f2",
+".#abcc",
+"defghi",
+"jklm..",
+"nopldd",
+"qrsttt",
+"uvswxx",
+"kyzpww",
+"ABzswp",
+"CBDsww",
+"EFGHII",
+"JKLIMN",
+"OGDzPz",
+"QRNpwp",
+"STzttl",
+"UIsdVV",
+"OWlVXX",
+"YItXZZ",
+"01mZZZ",
+"23.ZZZ",
+"T4uo5Z",
+"gT6789",
+"xwMLKF"};
+
+static const char *const qt_mac_tabselected_inactive_mid[]={
+"6 22 17 1",
+"m c #5f5f5f",
+". c #787878",
+"o c #c3c3c3",
+"n c #c4c4c4",
+"g c #d5d5d5",
+"h c #dbdbdb",
+"i c #dedede",
+"f c #e2e2e2",
+"e c #e3e3e3",
+"d c #e4e4e4",
+"c c #e6e6e6",
+"b c #e8e8e8",
+"j c #eaeaea",
+"a c #ececec",
+"k c #ededed",
+"# c #f1f1f1",
+"l c #f2f2f2",
+"......",
+"######",
+"aaaaaa",
+"bbbbbb",
+"cccccc",
+"dddddd",
+"eeeeee",
+"ffffff",
+"eeeeee",
+"gggggg",
+"hhhhhh",
+"iiiiii",
+"eeeeee",
+"cccccc",
+"jjjjjj",
+"kkkkkk",
+"llllll",
+"llllll",
+"llllll",
+"llllll",
+"mmmmmm",
+"nnnnno"};
+
+static const char *const qt_mac_tabselected_inactive_right[]={
+"6 22 65 2",
+".6 c #656565",
+".7 c #737373",
+"Qt c #767676",
+".# c #808080",
+".p c #818181",
+".t c #828282",
+".h c #888888",
+".8 c #8c8c8c",
+".x c #8d8d8d",
+".2 c #929292",
+".k c #979797",
+".1 c #989898",
+".l c #9b9b9b",
+".B c #9d9d9d",
+".a c #9e9e9e",
+".5 c #9f9f9f",
+".Z c #a3a3a3",
+".D c #a6a6a6",
+".I c #aaaaaa",
+".N c #aeaeae",
+".9 c #b0b0b0",
+".3 c #b1b1b1",
+".o c #b2b2b2",
+".g c #b3b3b3",
+".P c #b6b6b6",
+".R c #b9b9b9",
+".T c #bbbbbb",
+".s c #bdbdbd",
+".V c #c0c0c0",
+".w c #c2c2c2",
+".A c #c3c3c3",
+".z c #c6c6c6",
+".H c #c7c7c7",
+"#. c #cbcbcb",
+".M c #cccccc",
+".G c #cdcdcd",
+".Q c #cfcfcf",
+".S c #d1d1d1",
+".0 c #d2d2d2",
+".L c #d3d3d3",
+".F c #d5d5d5",
+".E c #d6d6d6",
+".X c #d7d7d7",
+".C c #d8d8d8",
+".J c #dadada",
+".K c #dcdcdc",
+".4 c #dddddd",
+".O c #dedede",
+".y c #dfdfdf",
+".r c #e0e0e0",
+".f c #e1e1e1",
+".n c #e2e2e2",
+".v c #e3e3e3",
+".u c #e4e4e4",
+".q c #e6e6e6",
+".j c #e7e7e7",
+".m c #e8e8e8",
+".b c #e9e9e9",
+".U c #eaeaea",
+".i c #ebebeb",
+".c c #ececec",
+".W c #ededed",
+".e c #f0f0f0",
+".d c #f1f1f1",
+".Y c #f2f2f2",
+"QtQt.#.a.b.c",
+".d.e.f.g.h.b",
+".c.c.i.j.k.l",
+".m.m.j.n.o.p",
+".q.q.q.r.s.t",
+".u.u.v.r.w.x",
+".v.v.n.y.z.k",
+".n.v.r.y.A.B",
+".v.v.r.C.A.D",
+".E.E.F.G.H.I",
+".J.K.E.L.M.N",
+".y.O.y.C.G.P",
+".n.v.n.J.Q.R",
+".j.q.q.y.S.T",
+".U.U.m.r.E.V",
+".W.W.U.j.X.P",
+".Y.Y.W.q.E.Z",
+".Y.Y.Y.i.0.1",
+".Y.Y.Y.c.2.3",
+".Y.4.o.x.5.S",
+".6.7.8.9.S.n",
+".H#..L.K.u.u"};
+
+static const char * const qt_mac_tab_selected_active_graph_left[]={
+"6 22 126 2",
+".d c #15283d",
+".c c #283348",
+".w c #374459",
+".l c #435364",
+".B c #465568",
+".q c #4d5367",
+".b c #525763",
+".H c #536271",
+".N c #5d6a79",
+"#1 c #5f5f5f",
+".T c #647181",
+"#N c #656e77",
+".r c #677282",
+"#B c #687380",
+"#U c #697077",
+".Z c #6c7885",
+".5 c #6c7988",
+"#0 c #6d6d6d",
+"#H c #6f767f",
+".x c #727e8a",
+"## c #757f8d",
+".f c #76767d",
+"#v c #76838f",
+"#e c #778292",
+"#k c #7b8694",
+".U c #7b8794",
+".C c #7b8795",
+".O c #7c8893",
+".I c #7d8896",
+".g c #7e8895",
+".0 c #7e8b99",
+"#q c #818c98",
+".6 c #85909e",
+"#Z c #878787",
+"#a c #8a96a3",
+"#f c #8d99a7",
+".1 c #8e99a6",
+"#V c #929ca5",
+"#l c #939daa",
+".k c #949499",
+".7 c #96a0ad",
+".2 c #98a3af",
+"#r c #99a5b0",
+".a c #9a9a9b",
+"#I c #9aa4b1",
+"#T c #9b9b9b",
+".3 c #9ba6b1",
+"#b c #9ba6b3",
+".8 c #9ca7b4",
+".4 c #9da6b1",
+"#w c #9da7b5",
+"#C c #9ea9b5",
+".V c #a0a9b4",
+"#g c #a0abb8",
+"#. c #a3acb8",
+".9 c #a4adba",
+"#c c #a6afbc",
+"#m c #a8b1bd",
+"#d c #a9b3bf",
+".J c #abb3bc",
+".P c #adb4bb",
+"#h c #adb8c3",
+"#M c #aeaeae",
+"#j c #aeb8c4",
+".W c #b0b8c2",
+"#i c #b0bac6",
+".D c #b3bac3",
+"#x c #b3bcc8",
+"#n c #b3bdc9",
+".Q c #b4bac2",
+"#D c #b5bfc9",
+".y c #b6bdc5",
+"#o c #b6becb",
+"#s c #b7c2ce",
+".K c #b8bdc6",
+".X c #b8bfc8",
+"#p c #b8c1ce",
+".S c #b9bec7",
+".R c #b9bfc8",
+".Y c #b9c1c7",
+".M c #b9c1c8",
+"#J c #bac5cf",
+".L c #bbbfc8",
+"#O c #bbc7d4",
+".s c #bcc3ca",
+"#t c #bcc5d1",
+"#u c #bcc6d2",
+".E c #bdc3ca",
+"#y c #bdc7d2",
+".G c #bec3cb",
+".F c #bec5cb",
+".z c #c2c7ce",
+"#E c #c2cad6",
+".A c #c3c9d0",
+"#z c #c3ccd7",
+"#A c #c3ced7",
+".m c #c5c9d0",
+"#7 c #c6c6c6",
+"#K c #c7d2dd",
+".t c #c8ced3",
+"#F c #c8d3de",
+"#G c #c9d3df",
+"#6 c #cbcbcb",
+".u c #cbd1d6",
+".v c #ccd2d7",
+".h c #ced2d6",
+"#W c #ced8e4",
+"#L c #d0dae3",
+"#S c #d1d1d1",
+"#P c #d1dbe6",
+"#5 c #d3d3d3",
+".n c #d5dadc",
+"#Q c #d5deea",
+".o c #d7dbdf",
+"#R c #d7e1ec",
+".p c #dadde0",
+"#4 c #dcdcdc",
+".i c #e0e2e6",
+"#Y c #e1e1e1",
+"#3 c #e3e3e3",
+"#2 c #e4e4e4",
+".j c #e6e8ea",
+".e c #e8e8e8",
+".# c #e9e9e9",
+"#X c #e9f4ff",
+"Qt c #ececec",
+"Qt.#.a.b.c.d",
+".e.f.g.h.i.j",
+".k.l.m.n.o.p",
+".q.r.s.t.u.v",
+".w.x.y.z.A.A",
+".B.C.D.E.F.G",
+".H.I.J.K.L.M",
+".N.O.P.Q.R.S",
+".T.U.V.W.X.Y",
+".Z.0.1.2.3.4",
+".5.6.7.8.9#.",
+"###a#b#c#d#d",
+"#e#f#g#h#i#j",
+"#k#l#m#n#o#p",
+"#q#r#h#s#t#u",
+"#v#w#x#y#z#A",
+"#B#C#D#E#F#G",
+"#H#I#J#K#L#L",
+"#M#N#O#P#Q#R",
+"#S#T#U#V#W#X",
+"#Y#S#M#Z#0#1",
+"#2#3#4#5#6#7"};
+
+static const char * const qt_mac_tab_selected_active_graph_mid[]={
+"6 22 41 1",
+". c #15273d",
+"# c #16283e",
+"a c #17293e",
+"b c #192a3f",
+"c c #1a2b40",
+"K c #585858",
+"p c #9ea8b4",
+"q c #a4adb9",
+"r c #aab4bf",
+"s c #afb9c6",
+"o c #b6bcc6",
+"n c #b8bec6",
+"t c #b8c2ce",
+"m c #bbc2c8",
+"u c #bbc5d0",
+"l c #bec4cb",
+"v c #bfcad4",
+"L c #c2c2c2",
+"k c #c2c9ce",
+"M c #c3c3c3",
+"w c #c7d1dd",
+"i c #cad0d5",
+"j c #cbd0d5",
+"x c #ccd5e1",
+"y c #cdd6e2",
+"z c #ced6e2",
+"A c #d5dfeb",
+"B c #d6e0ec",
+"C c #d7e1ed",
+"e c #d8dbe0",
+"f c #d8dce0",
+"g c #d9dce0",
+"D c #d9e2ee",
+"h c #dadce0",
+"E c #dae2ef",
+"d c #e7e9eb",
+"J c #e9f2ff",
+"I c #eaf3ff",
+"H c #ebf5ff",
+"G c #ecf6ff",
+"F c #edf6ff",
+"..#abc",
+"dddddd",
+"eeefgh",
+"iiiiij",
+"kkkkkk",
+"llllll",
+"mmmmmm",
+"nnnnnn",
+"oooooo",
+"pppppp",
+"qqqqqq",
+"rrrrrr",
+"ssssss",
+"tttttt",
+"uuuuuu",
+"vvvvvv",
+"wwwwww",
+"xxxxyz",
+"AABCDE",
+"FFGHIJ",
+"KKKKKK",
+"LLLLMM"};
+
+static const char * const qt_mac_tab_selected_active_graph_right[]={
+"6 22 124 2",
+"Qt c #15283d",
+".# c #293448",
+".z c #374459",
+".n c #435364",
+".F c #465568",
+".u c #4d5367",
+".a c #525763",
+".L c #536271",
+".R c #5d6a79",
+"#X c #5f5f5f",
+"#P c #646d77",
+".X c #647181",
+".t c #677282",
+"#F c #687380",
+"#U c #697077",
+".3 c #6c7885",
+".9 c #6c7988",
+"#Y c #6d6d6d",
+"#K c #6f767f",
+".y c #727e8a",
+"#c c #757f8d",
+".i c #76767d",
+"#z c #76838f",
+"#i c #778292",
+"#o c #7b8694",
+".W c #7b8794",
+".E c #7b8795",
+".Q c #7c8893",
+".K c #7d8896",
+".h c #7e8895",
+".2 c #7e8b99",
+"#t c #818c98",
+".8 c #85909e",
+"#Z c #888888",
+"#b c #8a96a3",
+"#h c #8d99a7",
+".1 c #8e99a6",
+"#T c #929ca5",
+"#n c #939daa",
+".o c #949499",
+".7 c #96a0ad",
+".0 c #98a3af",
+"#s c #99a5b0",
+".b c #9a9a9b",
+"#J c #9aa4b1",
+"#V c #9b9b9b",
+".Z c #9ba6b1",
+"#a c #9ba6b3",
+".6 c #9ca7b4",
+".Y c #9da6b1",
+"#y c #9da7b5",
+"#E c #9ea9b5",
+".V c #a0a9b4",
+"#g c #a0abb8",
+".4 c #a3acb8",
+".5 c #a4adba",
+"## c #a6afbc",
+"#m c #a8b1bd",
+"#. c #a9b3bf",
+".J c #abb3bc",
+".P c #adb4bb",
+"#f c #adb8c3",
+"#Q c #aeaeae",
+"#d c #aeb8c4",
+".U c #b0b8c2",
+"#e c #b0bac6",
+".D c #b3bac3",
+"#x c #b3bcc8",
+"#l c #b3bdc9",
+".O c #b4bac2",
+"#D c #b5bfc9",
+".x c #b6bdc5",
+"#k c #b6becb",
+"#r c #b7c2ce",
+".I c #b8bdc6",
+".T c #b8bfc8",
+"#j c #b8c1ce",
+".M c #b9bec7",
+".N c #b9bfc8",
+".S c #b9c1c7",
+".G c #b9c1c8",
+"#I c #bac5cf",
+".H c #bbbfc8",
+"#O c #bbc7d4",
+".s c #bcc3ca",
+"#q c #bcc5d1",
+"#p c #bcc6d2",
+".C c #bdc3ca",
+"#w c #bdc7d2",
+".A c #bec3cb",
+".B c #bec5cb",
+".w c #c2c7ce",
+"#C c #c2cad6",
+".v c #c3c9d0",
+"#v c #c3ccd7",
+"#u c #c3ced7",
+".m c #c5c9d0",
+"#1 c #c6c6c6",
+"#H c #c7d2dd",
+".r c #c8ced3",
+"#B c #c8d3de",
+"#A c #c9d3df",
+"#2 c #cacaca",
+".q c #cbd1d6",
+".p c #ccd2d7",
+".g c #ced2d6",
+"#S c #ced8e4",
+"#G c #d0dae3",
+"#W c #d1d1d1",
+"#N c #d1dbe6",
+"#3 c #d3d3d3",
+".l c #d5dadc",
+"#M c #d5deea",
+".k c #d7dbdf",
+"#L c #d7e1ec",
+".j c #dadde0",
+"#4 c #dcdcdc",
+".f c #e0e2e6",
+"#0 c #e2e2e2",
+"#5 c #e4e4e4",
+".e c #e6e8ea",
+".c c #e9e9e9",
+"#R c #e9f4ff",
+".d c #ececec",
+"Qt.#.a.b.c.d",
+".e.f.g.h.i.c",
+".j.k.l.m.n.o",
+".p.q.r.s.t.u",
+".v.v.w.x.y.z",
+".A.B.C.D.E.F",
+".G.H.I.J.K.L",
+".M.N.O.P.Q.R",
+".S.T.U.V.W.X",
+".Y.Z.0.1.2.3",
+".4.5.6.7.8.9",
+"#.#.###a#b#c",
+"#d#e#f#g#h#i",
+"#j#k#l#m#n#o",
+"#p#q#r#f#s#t",
+"#u#v#w#x#y#z",
+"#A#B#C#D#E#F",
+"#G#G#H#I#J#K",
+"#L#M#N#O#P#Q",
+"#R#S#T#U#V#W",
+"#X#Y#Z#Q#W#0",
+"#1#2#3#4#5#5"};
+
+static const char * const qt_mac_tab_press_left[]={
+"6 22 65 2",
+".8 c #5c5c5c",
+".c c #696969",
+".d c #6a6a6a",
+".b c #767676",
+".f c #7e7e7e",
+".t c #808080",
+".4 c #828282",
+".7 c #838383",
+".2 c #888888",
+".k c #8b8b8b",
+".0 c #8c8c8c",
+".z c #909090",
+".j c #919191",
+".a c #959595",
+".Y c #989898",
+".B c #999999",
+".D c #9e9e9e",
+".I c #a1a1a1",
+".n c #a6a6a6",
+".g c #a7a7a7",
+".6 c #a8a8a8",
+".O c #aaaaaa",
+".1 c #ababab",
+".R c #acacac",
+".T c #afafaf",
+".q c #b1b1b1",
+".V c #b3b3b3",
+".u c #b5b5b5",
+".A c #b6b6b6",
+".x c #bababa",
+".E c #bbbbbb",
+"#. c #bebebe",
+".J c #bfbfbf",
+".F c #c0c0c0",
+".P c #c1c1c1",
+".S c #c3c3c3",
+".U c #c5c5c5",
+".K c #c6c6c6",
+".G c #c8c8c8",
+".H c #c9c9c9",
+".L c #cacaca",
+".X c #cbcbcb",
+".C c #cccccc",
+".3 c #cdcdcd",
+".N c #cecece",
+".M c #cfcfcf",
+".5 c #d0d0d0",
+".Q c #d1d1d1",
+".y c #d2d2d2",
+".r c #d4d4d4",
+".h c #d5d5d5",
+".o c #d6d6d6",
+".v c #d7d7d7",
+".w c #d8d8d8",
+".s c #d9d9d9",
+".l c #dadada",
+".p c #dbdbdb",
+".9 c #dcdcdc",
+".W c #dedede",
+".m c #dfdfdf",
+".e c #e0e0e0",
+".# c #e1e1e1",
+".i c #e3e3e3",
+"Qt c #e4e4e4",
+".Z c #e6e6e6",
+"Qt.#.a.b.c.d",
+".e.f.g.h.iQt",
+".j.k.l.m.e.e",
+".b.n.o.l.p.p",
+".b.q.r.s.s.s",
+".t.u.r.v.w.w",
+".k.x.y.o.v.v",
+".z.A.y.r.v.o",
+".B.A.C.r.v.v",
+".D.E.F.G.H.H",
+".I.J.K.L.M.N",
+".O.P.C.y.Q.y",
+".R.S.N.o.v.o",
+".T.U.y.s.s.l",
+".V.H.r.p.W.W",
+".O.X.l.W.#.#",
+".Y.L.s.#.Z.Z",
+".0.K.m.Z.Z.Z",
+".1.2.e.Z.Z.Z",
+".3.Y.4.n.5.Z",
+".s.H.6.7.d.8",
+".9.l.r.L.S#."};
+
+static const char * const qt_mac_tab_press_mid[]={
+"7 22 19 1",
+"o c #555555",
+". c #6b6b6b",
+"p c #bbbbbb",
+"q c #bcbcbc",
+"g c #c8c8c8",
+"h c #cecece",
+"i c #d1d1d1",
+"f c #d6d6d6",
+"e c #d7d7d7",
+"d c #d8d8d8",
+"c c #d9d9d9",
+"j c #dadada",
+"b c #dbdbdb",
+"k c #dedede",
+"a c #e0e0e0",
+"l c #e1e1e1",
+"# c #e4e4e4",
+"m c #e5e5e5",
+"n c #e6e6e6",
+".......",
+"#######",
+"aaaaaaa",
+"bbbbbbb",
+"ccccccc",
+"ddddddd",
+"eeeeeee",
+"fffffff",
+"eeeeeee",
+"ggggggg",
+"hhhhhhh",
+"iiiiiii",
+"eeeeeee",
+"jjjjjjj",
+"kkkkkkk",
+"lllllll",
+"mmmmmmm",
+"nnnnnnn",
+"nnnnnnn",
+"nnnnnnn",
+"ooooooo",
+"ppppqqq"};
+
+static const char * const qt_mac_tab_press_right[]={
+"6 22 64 1",
+"5 c #5c5c5c",
+". c #6a6a6a",
+"# c #767676",
+"g c #7e7e7e",
+"v c #808080",
+"3 c #828282",
+"6 c #848484",
+"0 c #888888",
+"k c #8b8b8b",
+"Z c #8c8c8c",
+"z c #909090",
+"l c #919191",
+"a c #959595",
+"Y c #989898",
+"B c #999999",
+"G c #9e9e9e",
+"M c #a1a1a1",
+"o c #a6a6a6",
+"f c #a7a7a7",
+"7 c #a8a8a8",
+"P c #aaaaaa",
+"1 c #ababab",
+"R c #acacac",
+"T c #afafaf",
+"r c #b1b1b1",
+"V c #b3b3b3",
+"u c #b5b5b5",
+"y c #b6b6b6",
+"x c #bababa",
+"F c #bbbbbb",
+"8 c #bebebe",
+"L c #bfbfbf",
+"E c #c0c0c0",
+"O c #c1c1c1",
+"9 c #c2c2c2",
+"Q c #c3c3c3",
+"S c #c5c5c5",
+"K c #c6c6c6",
+"D c #c8c8c8",
+"C c #c9c9c9",
+"J c #cacaca",
+"W c #cbcbcb",
+"A c #cccccc",
+"4 c #cdcdcd",
+"H c #cecece",
+"I c #cfcfcf",
+"2 c #d0d0d0",
+"N c #d1d1d1",
+"w c #d2d2d2",
+"q c #d4d4d4",
+"e c #d5d5d5",
+"n c #d6d6d6",
+"t c #d7d7d7",
+"s c #d8d8d8",
+"p c #d9d9d9",
+"j c #dadada",
+"m c #dbdbdb",
+"U c #dedede",
+"i c #dfdfdf",
+"h c #e0e0e0",
+"b c #e1e1e1",
+"d c #e3e3e3",
+"c c #e4e4e4",
+"X c #e6e6e6",
+"..#abc",
+"cdefgb",
+"hhijkl",
+"mmjno#",
+"pppqr#",
+"sstquv",
+"ttnwxk",
+"ntqwyz",
+"ttqAyB",
+"CCDEFG",
+"HIJKLM",
+"wNwAOP",
+"ntnHQR",
+"jppwST",
+"UUmqCV",
+"bbUjWP",
+"XXbpJY",
+"XXXiKZ",
+"XXXh01",
+"X2o3Y4",
+"5.67Cj",
+"89Jqmm"};
+
+static const char * const qt_mac_toolbar_ext[]={
+ "14 9 4 1",
+ "# c #858585",
+ "b c #d9d9d9",
+ ". c #dbdbdb",
+ "a c None",
+ ".###..###.aaaa",
+ "a.###..###.aaa",
+ "aab###bb###baa",
+ "aaab###bb###ba",
+ "aaaa.###..###.",
+ "aaa.###..###.a",
+ "aab###bb###baa",
+ "ab###bb###baaa",
+ ".###..###.aaaa"};
diff --git a/src/gui/styles/qmotifstyle.cpp b/src/gui/styles/qmotifstyle.cpp
new file mode 100644
index 0000000000..7d4fab8d89
--- /dev/null
+++ b/src/gui/styles/qmotifstyle.cpp
@@ -0,0 +1,2719 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qmotifstyle.h"
+#include "qcdestyle.h"
+
+#if !defined(QT_NO_STYLE_MOTIF) || defined(QT_PLUGIN)
+
+#include "qmenu.h"
+#include "qapplication.h"
+#include "qpainter.h"
+#include "qdrawutil.h"
+#include "qpixmap.h"
+#include "qpalette.h"
+#include "qwidget.h"
+#include "qpushbutton.h"
+#include "qscrollbar.h"
+#include "qtabbar.h"
+#include "qtabwidget.h"
+#include "qlistview.h"
+#include "qsplitter.h"
+#include "qslider.h"
+#include "qcombobox.h"
+#include "qlineedit.h"
+#include "qprogressbar.h"
+#include "qimage.h"
+#include "qfocusframe.h"
+#include "qdebug.h"
+#include "qpainterpath.h"
+#include "qmotifstyle_p.h"
+#include "qdialogbuttonbox.h"
+#include "qformlayout.h"
+#include <limits.h>
+#include <QtGui/qgraphicsproxywidget.h>
+#include <QtGui/qgraphicsview.h>
+
+#ifdef Q_WS_X11
+#include "qx11info_x11.h"
+#endif
+
+QT_BEGIN_NAMESPACE
+
+// old constants that might still be useful...
+static const int motifItemFrame = 2; // menu item frame width
+static const int motifSepHeight = 2; // separator item height
+static const int motifItemHMargin = 3; // menu item hor text margin
+static const int motifItemVMargin = 2; // menu item ver text margin
+static const int motifArrowHMargin = 6; // arrow horizontal margin
+static const int motifTabSpacing = 12; // space between text and tab
+static const int motifCheckMarkHMargin = 2; // horiz. margins of check mark
+static const int motifCheckMarkSpace = 16;
+
+
+/*!
+ \class QMotifStyle
+ \brief The QMotifStyle class provides Motif look and feel.
+
+ \ingroup appearance
+
+ This class implements the Motif look and feel. It closely
+ resembles the original Motif look as defined by the Open Group,
+ but with some minor improvements. The Motif style is Qt's default
+ GUI style on Unix platforms.
+
+ \img qmotifstyle.png
+ \sa QWindowsXPStyle, QMacStyle, QWindowsStyle, QPlastiqueStyle, QCDEStyle
+*/
+
+/*!
+ \variable QMotifStyle::focus
+ \internal
+*/
+
+/*!
+ Constructs a QMotifStyle.
+
+ If \a useHighlightCols is false (the default), the style will
+ polish the application's color palette to emulate the Motif way of
+ highlighting, which is a simple inversion between the base and the
+ text color.
+*/
+QMotifStyle::QMotifStyle(bool useHighlightCols)
+ : QCommonStyle(*new QMotifStylePrivate)
+{
+ focus = 0;
+ highlightCols = useHighlightCols;
+}
+
+
+/*!
+ \internal
+*/
+QMotifStyle::QMotifStyle(QMotifStylePrivate &dd, bool useHighlightColors)
+ : QCommonStyle(dd)
+{
+ focus = 0;
+ highlightCols = useHighlightColors;
+}
+
+
+/*!
+ \overload
+
+ Destroys the style.
+*/
+QMotifStyle::~QMotifStyle()
+{
+ delete focus;
+}
+
+/*!
+ \internal
+ Animate indeterminate progress bars only when visible
+*/
+bool QMotifStyle::eventFilter(QObject *o, QEvent *e)
+{
+#ifndef QT_NO_PROGRESSBAR
+ Q_D(QMotifStyle);
+ switch(e->type()) {
+ case QEvent::StyleChange:
+ case QEvent::Show:
+ if (QProgressBar *bar = qobject_cast<QProgressBar *>(o)) {
+ d->bars << bar;
+ if (d->bars.size() == 1) {
+ Q_ASSERT(d->animationFps> 0);
+ d->animateTimer = startTimer(1000 / d->animationFps);
+ }
+ }
+ break;
+ case QEvent::Destroy:
+ case QEvent::Hide:
+ // reinterpret_cast because there is no type info when getting
+ // the destroy event. We know that it is a QProgressBar.
+ if (QProgressBar *bar = reinterpret_cast<QProgressBar *>(o)) {
+ d->bars.removeAll(bar);
+ if (d->bars.isEmpty() && d->animateTimer) {
+ killTimer(d->animateTimer);
+ d->animateTimer = 0;
+ }
+ }
+ default:
+ break;
+ }
+#endif // QT_NO_PROGRESSBAR
+ return QStyle::eventFilter(o, e);
+}
+
+/*!
+ \internal
+*/
+QIcon QMotifStyle::standardIconImplementation(StandardPixmap standardIcon, const QStyleOption *opt,
+ const QWidget *widget) const
+{
+ return QCommonStyle::standardIconImplementation(standardIcon, opt, widget);
+}
+
+/*!
+ \reimp
+*/
+void QMotifStyle::timerEvent(QTimerEvent *event)
+{
+#ifndef QT_NO_PROGRESSBAR
+ Q_D(QMotifStyle);
+ if (event->timerId() == d->animateTimer) {
+ Q_ASSERT(d->animationFps > 0);
+ d->animateStep = d->startTime.elapsed() / (1000 / d->animationFps);
+ foreach (QProgressBar *bar, d->bars) {
+ if ((bar->minimum() == 0 && bar->maximum() == 0))
+ bar->update();
+ }
+ }
+#endif // QT_NO_PROGRESSBAR
+ event->ignore();
+}
+
+
+QMotifStylePrivate::QMotifStylePrivate()
+#ifndef QT_NO_PROGRESSBAR
+ : animationFps(25), animateTimer(0), animateStep(0)
+#endif
+{
+}
+
+/*!
+ If \a arg is false, the style will polish the application's color
+ palette to emulate the Motif way of highlighting, which is a
+ simple inversion between the base and the text color.
+
+ The effect will show up the next time an application palette is
+ set via QApplication::setPalette(). The current color palette of
+ the application remains unchanged.
+
+ \sa QStyle::polish()
+*/
+void QMotifStyle::setUseHighlightColors(bool arg)
+{
+ highlightCols = arg;
+}
+
+/*!
+ Returns true if the style treats the highlight colors of the
+ palette in a Motif-like manner, which is a simple inversion
+ between the base and the text color; otherwise returns false. The
+ default is false.
+*/
+bool QMotifStyle::useHighlightColors() const
+{
+ return highlightCols;
+}
+
+/*! \reimp */
+
+void QMotifStyle::polish(QPalette& pal)
+{
+ if (pal.brush(QPalette::Active, QPalette::Light) == pal.brush(QPalette::Active, QPalette::Base)) {
+ QColor nlight = pal.color(QPalette::Active, QPalette::Light).darker(108);
+ pal.setColor(QPalette::Active, QPalette::Light, nlight) ;
+ pal.setColor(QPalette::Disabled, QPalette::Light, nlight) ;
+ pal.setColor(QPalette::Inactive, QPalette::Light, nlight) ;
+ }
+
+ if (highlightCols)
+ return;
+
+ // force the ugly motif way of highlighting *sigh*
+ pal.setColor(QPalette::Active, QPalette::Highlight,
+ pal.color(QPalette::Active, QPalette::Text));
+ pal.setColor(QPalette::Active, QPalette::HighlightedText,
+ pal.color(QPalette::Active, QPalette::Base));
+ pal.setColor(QPalette::Disabled, QPalette::Highlight,
+ pal.color(QPalette::Disabled, QPalette::Text));
+ pal.setColor(QPalette::Disabled, QPalette::HighlightedText,
+ pal.color(QPalette::Disabled, QPalette::Base));
+ pal.setColor(QPalette::Inactive, QPalette::Highlight,
+ pal.color(QPalette::Active, QPalette::Text));
+ pal.setColor(QPalette::Inactive, QPalette::HighlightedText,
+ pal.color(QPalette::Active, QPalette::Base));
+}
+
+/*!
+ \reimp
+ \internal
+ Keep QStyle::polish() visible.
+*/
+void QMotifStyle::polish(QWidget* widget)
+{
+ QStyle::polish(widget);
+#ifndef QT_NO_PROGRESSBAR
+ if (qobject_cast<QProgressBar *>(widget))
+ widget->installEventFilter(this);
+#endif
+}
+
+/*!
+ \reimp
+ \internal
+ Keep QStyle::polish() visible.
+*/
+void QMotifStyle::unpolish(QWidget* widget)
+{
+ QCommonStyle::unpolish(widget);
+#ifndef QT_NO_PROGRESSBAR
+ if (qobject_cast<QProgressBar *>(widget))
+ widget->removeEventFilter(this);
+#endif
+}
+
+
+/*!
+ \reimp
+ \internal
+ Keep QStyle::polish() visible.
+*/
+void QMotifStyle::polish(QApplication* a)
+{
+ QCommonStyle::polish(a);
+}
+
+
+/*!
+ \reimp
+ \internal
+ Keep QStyle::polish() visible.
+*/
+void QMotifStyle::unpolish(QApplication* a)
+{
+ QCommonStyle::unpolish(a);
+}
+
+static void rot(QPolygon& a, int n)
+{
+ QPolygon r(a.size());
+ for (int i = 0; i < (int)a.size(); i++) {
+ switch (n) {
+ case 1: r.setPoint(i,-a[i].y(),a[i].x()); break;
+ case 2: r.setPoint(i,-a[i].x(),-a[i].y()); break;
+ case 3: r.setPoint(i,a[i].y(),-a[i].x()); break;
+ }
+ }
+ a = r;
+}
+
+
+/*!
+ \reimp
+*/
+void QMotifStyle::drawPrimitive(PrimitiveElement pe, const QStyleOption *opt, QPainter *p,
+ const QWidget *w) const
+{
+ switch(pe) {
+ case PE_Q3CheckListExclusiveIndicator:
+ if (const QStyleOptionQ3ListView *lv = qstyleoption_cast<const QStyleOptionQ3ListView *>(opt)) {
+ if (lv->items.isEmpty())
+ return;
+
+ if (lv->state & State_Enabled)
+ p->setPen(QPen(opt->palette.text().color()));
+ else
+ p->setPen(QPen(lv->palette.color(QPalette::Disabled, QPalette::Text)));
+ QPolygon a;
+
+ int cx = opt->rect.width()/2 - 1;
+ int cy = opt->rect.height()/2;
+ int e = opt->rect.width()/2 - 1;
+ for (int i = 0; i < 3; i++) { //penWidth 2 doesn't quite work
+ a.setPoints(4, cx-e, cy, cx, cy-e, cx+e, cy, cx, cy+e);
+ p->drawPolygon(a);
+ e--;
+ }
+ if (opt->state & State_On) {
+ if (lv->state & State_Enabled)
+ p->setPen(QPen(opt->palette.text().color()));
+ else
+ p->setPen(QPen(lv->palette.color(QPalette::Disabled,
+ QPalette::Text)));
+ QBrush saveBrush = p->brush();
+ p->setBrush(opt->palette.text());
+ e = e - 2;
+ a.setPoints(4, cx-e, cy, cx, cy-e, cx+e, cy, cx, cy+e);
+ p->drawPolygon(a);
+ p->setBrush(saveBrush);
+ }
+ }
+ break;
+
+ case PE_FrameTabWidget:
+ case PE_FrameWindow:
+ qDrawShadePanel(p, opt->rect, opt->palette, QStyle::State_None, pixelMetric(PM_DefaultFrameWidth));
+ break;
+ case PE_FrameFocusRect:
+ if (const QStyleOptionFocusRect *fropt = qstyleoption_cast<const QStyleOptionFocusRect *>(opt)) {
+ if ((fropt->state & State_HasFocus) && focus && focus->isVisible()
+ && !(fropt->state & QStyle::State_Item))
+ break;
+ QCommonStyle::drawPrimitive(pe, opt, p, w);
+ }
+ break;
+
+ case PE_IndicatorToolBarHandle: {
+ p->save();
+ p->translate(opt->rect.x(), opt->rect.y());
+
+ QColor dark(opt->palette.dark().color());
+ QColor light(opt->palette.light().color());
+ int i;
+ if (opt->state & State_Horizontal) {
+ int h = opt->rect.height();
+ if (h > 6) {
+ if (opt->state & State_On)
+ p->fillRect(1, 1, 8, h - 2, opt->palette.highlight());
+ QPolygon a(2 * ((h-6)/3));
+ int y = 3 + (h%3)/2;
+ p->setPen(dark);
+ p->drawLine(8, 1, 8, h-2);
+ for (i=0; 2*i < a.size(); ++i) {
+ a.setPoint(2*i, 5, y+1+3*i);
+ a.setPoint(2*i+1, 2, y+2+3*i);
+ }
+ p->drawPoints(a);
+ p->setPen(light);
+ p->drawLine(9, 1, 9, h-2);
+ for (i=0; 2*i < a.size(); i++) {
+ a.setPoint(2*i, 4, y+3*i);
+ a.setPoint(2*i+1, 1, y+1+3*i);
+ }
+ p->drawPoints(a);
+ // if (drawBorder) {
+ // p->setPen(QPen(Qt::darkGray));
+ // p->drawLine(0, opt->rect.height() - 1,
+ // tbExtent, opt->rect.height() - 1);
+ // }
+ }
+ } else {
+ int w = opt->rect.width();
+ if (w > 6) {
+ if (opt->state & State_On)
+ p->fillRect(1, 1, w - 2, 9, opt->palette.highlight());
+ QPolygon a(2 * ((w-6)/3));
+
+ int x = 3 + (w%3)/2;
+ p->setPen(dark);
+ p->drawLine(1, 8, w-2, 8);
+ for (i=0; 2*i < a.size(); ++i) {
+ a.setPoint(2*i, x+1+3*i, 6);
+ a.setPoint(2*i+1, x+2+3*i, 3);
+ }
+ p->drawPoints(a);
+ p->setPen(light);
+ p->drawLine(1, 9, w-2, 9);
+ for (i=0; 2*i < a.size(); ++i) {
+ a.setPoint(2*i, x+3*i, 5);
+ a.setPoint(2*i+1, x+1+3*i, 2);
+ }
+ p->drawPoints(a);
+ // if (drawBorder) {
+ // p->setPen(QPen(Qt::darkGray));
+ // p->drawLine(opt->rect.width() - 1, 0,
+ // opt->rect.width() - 1, tbExtent);
+ // }
+ }
+ }
+ p->restore();
+ break; }
+
+ case PE_PanelButtonCommand:
+ case PE_PanelButtonBevel:
+ case PE_PanelButtonTool: {
+ QBrush fill;
+ if (opt->state & State_Sunken)
+ fill = opt->palette.brush(QPalette::Mid);
+ else if ((opt->state & State_On) && (opt->state & State_Enabled))
+ fill = QBrush(opt->palette.mid().color(), Qt::Dense4Pattern);
+ else
+ fill = opt->palette.brush(QPalette::Button);
+ if ((opt->state & State_Enabled || opt->state & State_On) || !(opt->state & State_AutoRaise))
+ qDrawShadePanel(p, opt->rect, opt->palette, bool(opt->state & (State_Sunken | State_On)),
+ pixelMetric(PM_DefaultFrameWidth), &fill);
+ break; }
+
+ case PE_IndicatorCheckBox: {
+ bool on = opt->state & State_On;
+ bool down = opt->state & State_Sunken;
+ bool showUp = !(down ^ on);
+ QBrush fill = opt->palette.brush((showUp || opt->state & State_NoChange) ?QPalette::Button : QPalette::Mid);
+ if (opt->state & State_NoChange) {
+ qDrawPlainRect(p, opt->rect, opt->palette.text().color(),
+ 1, &fill);
+ p->drawLine(opt->rect.x() + opt->rect.width() - 1, opt->rect.y(),
+ opt->rect.x(), opt->rect.y() + opt->rect.height() - 1);
+ } else {
+ qDrawShadePanel(p, opt->rect, opt->palette, !showUp,
+ pixelMetric(PM_DefaultFrameWidth), &fill);
+ }
+ if (!(opt->state & State_Enabled) && styleHint(SH_DitherDisabledText))
+ p->fillRect(opt->rect, QBrush(p->background().color(), Qt::Dense5Pattern));
+ break; }
+
+ case PE_IndicatorRadioButton: {
+#define INTARRLEN(x) sizeof(x)/(sizeof(int)*2)
+ int inner_pts[] = { // used for filling diamond
+ 2,opt->rect.height()/2,
+ opt->rect.width()/2,2,
+ opt->rect.width()-3,opt->rect.height()/2,
+ opt->rect.width()/2,opt->rect.height()-3
+ };
+ int top_pts[] = { // top (^) of diamond
+ 0,opt->rect.height()/2,
+ opt->rect.width()/2,0,
+ opt->rect.width()-2,opt->rect.height()/2-1,
+ opt->rect.width()-3,opt->rect.height()/2-1,
+ opt->rect.width()/2,1,
+ 1,opt->rect.height()/2,
+ 2,opt->rect.height()/2,
+ opt->rect.width()/2,2,
+ opt->rect.width()-4,opt->rect.height()/2-1
+ };
+ int bottom_pts[] = { // bottom (v) of diamond
+ 1,opt->rect.height()/2+1,
+ opt->rect.width()/2,opt->rect.height()-1,
+ opt->rect.width()-1,opt->rect.height()/2,
+ opt->rect.width()-2,opt->rect.height()/2,
+ opt->rect.width()/2,opt->rect.height()-2,
+ 2,opt->rect.height()/2+1,
+ 3,opt->rect.height()/2+1,
+ opt->rect.width()/2,opt->rect.height()-3,
+ opt->rect.width()-3,opt->rect.height()/2
+ };
+ bool on = opt->state & State_On;
+ bool down = opt->state & State_Sunken;
+ bool showUp = !(down ^ on);
+ QPen oldPen = p->pen();
+ QBrush oldBrush = p->brush();
+ QPolygon a(INTARRLEN(inner_pts), inner_pts);
+ p->setPen(Qt::NoPen);
+ p->setBrush(opt->palette.brush(showUp ? QPalette::Button : QPalette::Mid));
+ a.translate(opt->rect.x(), opt->rect.y());
+ p->drawPolygon(a);
+ p->setPen(showUp ? opt->palette.light().color() : opt->palette.dark().color());
+ p->setBrush(Qt::NoBrush);
+ a.setPoints(INTARRLEN(top_pts), top_pts);
+ a.translate(opt->rect.x(), opt->rect.y());
+ p->drawPolyline(a);
+ p->setPen(showUp ? opt->palette.dark().color() : opt->palette.light().color());
+ a.setPoints(INTARRLEN(bottom_pts), bottom_pts);
+ a.translate(opt->rect.x(), opt->rect.y());
+ p->drawPolyline(a);
+ if (!(opt->state & State_Enabled) && styleHint(SH_DitherDisabledText))
+ p->fillRect(opt->rect, QBrush(p->background().color(), Qt::Dense5Pattern));
+ p->setPen(oldPen);
+ p->setBrush(oldBrush);
+ break; }
+
+ case PE_IndicatorSpinUp:
+ case PE_IndicatorSpinPlus:
+ case PE_IndicatorSpinDown:
+ case PE_IndicatorSpinMinus:
+ case PE_IndicatorArrowUp:
+ case PE_IndicatorArrowDown:
+ case PE_IndicatorArrowRight:
+ case PE_IndicatorArrowLeft: {
+ QRect rect = opt->rect;
+ QPolygon bFill;
+ QPolygon bTop;
+ QPolygon bBot;
+ QPolygon bLeft;
+ if (pe == PE_IndicatorSpinPlus || pe == PE_IndicatorSpinUp)
+ pe = PE_IndicatorArrowUp;
+ else if (pe == PE_IndicatorSpinMinus || pe == PE_IndicatorSpinDown)
+ pe = PE_IndicatorArrowDown;
+ bool vertical = pe == PE_IndicatorArrowUp || pe == PE_IndicatorArrowDown;
+ bool horizontal = !vertical;
+ int dim = rect.width() < rect.height() ? rect.width() : rect.height();
+ int colspec = 0x0000;
+
+ if (!(opt->state & State_Enabled))
+ dim -= 2;
+ if(dim < 2)
+ break;
+
+ // adjust size and center (to fix rotation below)
+ if (rect.width() > dim) {
+ rect.setX(rect.x() + ((rect.width() - dim) / 2));
+ rect.setWidth(dim);
+ }
+ if (rect.height() > dim) {
+ rect.setY(rect.y() + ((rect.height() - dim) / 2));
+ rect.setHeight(dim);
+ }
+
+ if (dim > 3) {
+ if (pixelMetric(PM_DefaultFrameWidth) < 2) { // thin style
+ bFill.resize( dim & 1 ? 3 : 4 );
+ bTop.resize( 2 );
+ bBot.resize( 2 );
+ bLeft.resize( 2 );
+ bLeft.putPoints( 0, 2, 0, 0, 0, dim-1 );
+ bTop.putPoints( 0, 2, 1, 0, dim-1, dim/2 );
+ bBot.putPoints( 0, 2, 1, dim-1, dim-1, dim/2 );
+
+ if ( dim > 6 ) { // dim>6: must fill interior
+ bFill.putPoints( 0, 2, 0, dim-1, 0, 0 );
+ if ( dim & 1 ) // if size is an odd number
+ bFill.setPoint( 2, dim - 1, dim / 2 );
+ else
+ bFill.putPoints( 2, 2, dim-1, dim/2-1, dim-1, dim/2 );
+ }
+ } else {
+ if (dim > 6)
+ bFill.resize(dim & 1 ? 3 : 4);
+ bTop.resize((dim/2)*2);
+ bBot.resize(dim & 1 ? dim + 1 : dim);
+ bLeft.resize(dim > 4 ? 4 : 2);
+ bLeft.putPoints(0, 2, 0,0, 0,dim-1);
+ if (dim > 4)
+ bLeft.putPoints(2, 2, 1,2, 1,dim-3);
+ bTop.putPoints(0, 4, 1,0, 1,1, 2,1, 3,1);
+ bBot.putPoints(0, 4, 1,dim-1, 1,dim-2, 2,dim-2, 3,dim-2);
+
+ for(int i=0; i<dim/2-2 ; i++) {
+ bTop.putPoints(i*2+4, 2, 2+i*2,2+i, 5+i*2, 2+i);
+ bBot.putPoints(i*2+4, 2, 2+i*2,dim-3-i, 5+i*2,dim-3-i);
+ }
+ if (dim & 1) // odd number size: extra line
+ bBot.putPoints(dim-1, 2, dim-3,dim/2, dim-1,dim/2);
+ if (dim > 6) { // dim>6: must fill interior
+ bFill.putPoints(0, 2, 1,dim-3, 1,2);
+ if (dim & 1) // if size is an odd number
+ bFill.setPoint(2, dim - 3, dim / 2);
+ else
+ bFill.putPoints(2, 2, dim-4,dim/2-1, dim-4,dim/2);
+ }
+ }
+ } else {
+ if (dim == 3) { // 3x3 arrow pattern
+ bLeft.setPoints(4, 0,0, 0,2, 1,1, 1,1);
+ bTop .setPoints(2, 1,0, 1,0);
+ bBot .setPoints(2, 1,2, 2,1);
+ }
+ else { // 2x2 arrow pattern
+ bLeft.setPoints(2, 0,0, 0,1);
+ bTop .setPoints(2, 1,0, 1,0);
+ bBot .setPoints(2, 1,1, 1,1);
+ }
+ }
+
+ // We use rot() and translate() as it is more efficient that
+ // matrix transformations on the painter, and because it still
+ // works with QT_NO_TRANSFORMATIONS defined.
+
+ if (pe == PE_IndicatorArrowUp || pe == PE_IndicatorArrowLeft) {
+ if (vertical) {
+ rot(bFill,3);
+ rot(bLeft,3);
+ rot(bTop,3);
+ rot(bBot,3);
+ bFill.translate(0, rect.height() - 1);
+ bLeft.translate(0, rect.height() - 1);
+ bTop.translate(0, rect.height() - 1);
+ bBot.translate(0, rect.height() - 1);
+ } else {
+ rot(bFill,2);
+ rot(bLeft,2);
+ rot(bTop,2);
+ rot(bBot,2);
+ bFill.translate(rect.width() - 1, rect.height() - 1);
+ bLeft.translate(rect.width() - 1, rect.height() - 1);
+ bTop.translate(rect.width() - 1, rect.height() - 1);
+ bBot.translate(rect.width() - 1, rect.height() - 1);
+ }
+ if (opt->state & State_Sunken)
+ colspec = horizontal ? 0x2334 : 0x2343;
+ else
+ colspec = horizontal ? 0x1443 : 0x1434;
+ } else {
+ if (vertical) {
+ rot(bFill,1);
+ rot(bLeft,1);
+ rot(bTop,1);
+ rot(bBot,1);
+ bFill.translate(rect.width() - 1, 0);
+ bLeft.translate(rect.width() - 1, 0);
+ bTop.translate(rect.width() - 1, 0);
+ bBot.translate(rect.width() - 1, 0);
+ }
+ if (opt->state & State_Sunken)
+ colspec = horizontal ? 0x2443 : 0x2434;
+ else
+ colspec = horizontal ? 0x1334 : 0x1343;
+ }
+ bFill.translate(rect.x(), rect.y());
+ bLeft.translate(rect.x(), rect.y());
+ bTop.translate(rect.x(), rect.y());
+ bBot.translate(rect.x(), rect.y());
+
+ const QColor *cols[5];
+ if (opt->state & State_Enabled) {
+ cols[0] = 0;
+ cols[1] = &opt->palette.button().color();
+ cols[2] = &opt->palette.mid().color();
+ cols[3] = &opt->palette.light().color();
+ cols[4] = &opt->palette.dark().color();
+ } else {
+ cols[0] = 0;
+ cols[1] = &opt->palette.mid().color();
+ cols[2] = &opt->palette.mid().color();
+ cols[3] = &opt->palette.mid().color();
+ cols[4] = &opt->palette.mid().color();
+ }
+
+#define CMID *cols[(colspec>>12) & 0xf]
+#define CLEFT *cols[(colspec>>8) & 0xf]
+#define CTOP *cols[(colspec>>4) & 0xf]
+#define CBOT *cols[colspec & 0xf]
+
+ QPen savePen = p->pen();
+ QBrush saveBrush = p->brush();
+ QPen pen(Qt::NoPen);
+ QBrush brush = opt->palette.brush((opt->state & State_Enabled) ?
+ QPalette::Button : QPalette::Mid);
+ p->setPen(pen);
+ p->setBrush(brush);
+ p->drawPolygon(bFill);
+ p->setBrush(Qt::NoBrush);
+
+ p->setPen(CLEFT);
+ p->drawPolyline(bLeft);
+ p->setPen(CTOP);
+ p->drawPolyline(bTop);
+ p->setPen(CBOT);
+ p->drawPolyline(bBot);
+
+ p->setBrush(saveBrush);
+ p->setPen(savePen);
+#undef CMID
+#undef CLEFT
+#undef CTOP
+#undef CBOT
+ if (!(opt->state & State_Enabled) && styleHint(SH_DitherDisabledText))
+ p->fillRect(opt->rect, QBrush(p->background().color(), Qt::Dense5Pattern));
+ break; }
+
+ case PE_IndicatorDockWidgetResizeHandle: {
+ const int motifOffset = 10;
+ int sw = pixelMetric(PM_SplitterWidth);
+ if (opt->state & State_Horizontal) {
+ int yPos = opt->rect.y() + opt->rect.height() / 2;
+ int kPos = opt->rect.right() - motifOffset - sw;
+ int kSize = sw - 2;
+
+ qDrawShadeLine(p, opt->rect.left(), yPos, kPos, yPos, opt->palette);
+ qDrawShadePanel(p, kPos, yPos - sw / 2 + 1, kSize, kSize,
+ opt->palette, false, 1, &opt->palette.brush(QPalette::Button));
+ qDrawShadeLine(p, kPos + kSize - 1, yPos, opt->rect.right(), yPos, opt->palette);
+ } else {
+ int xPos = opt->rect.x() + opt->rect.width() / 2;
+ int kPos = motifOffset;
+ int kSize = sw - 2;
+
+ qDrawShadeLine(p, xPos, opt->rect.top() + kPos + kSize - 1, xPos, opt->rect.bottom(), opt->palette);
+ qDrawShadePanel(p, xPos - sw / 2 + 1, opt->rect.top() + kPos, kSize, kSize, opt->palette,
+ false, 1, &opt->palette.brush(QPalette::Button));
+ qDrawShadeLine(p, xPos, opt->rect.top(), xPos, opt->rect.top() + kPos, opt->palette);
+ }
+ break; }
+
+ case PE_IndicatorMenuCheckMark: {
+ const int markW = 6;
+ const int markH = 6;
+ int posX = opt->rect.x() + (opt->rect.width() - markW) / 2 - 1;
+ int posY = opt->rect.y() + (opt->rect.height() - markH) / 2;
+ int dfw = pixelMetric(PM_DefaultFrameWidth);
+
+ if (dfw < 2) {
+ // Could do with some optimizing/caching...
+ QPolygon a(7*2);
+ int i, xx, yy;
+ xx = posX;
+ yy = 3 + posY;
+ for (i=0; i<3; i++) {
+ a.setPoint(2*i, xx, yy);
+ a.setPoint(2*i+1, xx, yy+2);
+ xx++; yy++;
+ }
+ yy -= 2;
+ for (i=3; i<7; i++) {
+ a.setPoint(2*i, xx, yy);
+ a.setPoint(2*i+1, xx, yy+2);
+ xx++; yy--;
+ }
+ if (! (opt->state & State_Enabled) && ! (opt->state & State_On)) {
+ int pnt;
+ p->setPen(opt->palette.highlightedText().color());
+ QPoint offset(1,1);
+ for (pnt = 0; pnt < (int)a.size(); pnt++)
+ a[pnt] += offset;
+ p->drawPolyline(a);
+ for (pnt = 0; pnt < (int)a.size(); pnt++)
+ a[pnt] -= offset;
+ }
+ p->setPen(opt->palette.text().color());
+ p->drawPolyline(a);
+
+ qDrawShadePanel(p, posX-2, posY-2, markW+4, markH+6, opt->palette, true, dfw);
+ } else
+ qDrawShadePanel(p, posX, posY, markW, markH, opt->palette, true, dfw,
+ &opt->palette.brush(QPalette::Mid));
+
+ break; }
+
+ case PE_IndicatorProgressChunk:
+ {
+ bool vertical = false;
+ if (const QStyleOptionProgressBarV2 *pb2 = qstyleoption_cast<const QStyleOptionProgressBarV2 *>(opt))
+ vertical = (pb2->orientation == Qt::Vertical);
+ if (!vertical) {
+ p->fillRect(opt->rect.x(), opt->rect.y(), opt->rect.width(),
+ opt->rect.height(), opt->palette.brush(QPalette::Highlight));
+ } else {
+ p->fillRect(opt->rect.x(), opt->rect.y(), opt->rect.width(), opt->rect.height(),
+ opt->palette.brush(QPalette::Highlight));
+ }
+ }
+ break;
+
+ default:
+ QCommonStyle::drawPrimitive(pe, opt, p, w);
+ break;
+ }
+}
+
+
+/*!
+ \reimp
+*/
+void QMotifStyle::drawControl(ControlElement element, const QStyleOption *opt, QPainter *p,
+ const QWidget *widget) const
+{
+ switch(element) {
+ case CE_Splitter: {
+ QStyleOption handleOpt = *opt;
+ if (handleOpt.state & State_Horizontal)
+ handleOpt.state &= ~State_Horizontal;
+ else
+ handleOpt.state |= State_Horizontal;
+ drawPrimitive(PE_IndicatorDockWidgetResizeHandle, &handleOpt, p, widget);
+ break; }
+
+ case CE_ScrollBarSubLine:
+ case CE_ScrollBarAddLine:{
+ PrimitiveElement pe;
+ if (element == CE_ScrollBarAddLine)
+ pe = (opt->state & State_Horizontal) ? (opt->direction == Qt::LeftToRight ? PE_IndicatorArrowRight : PE_IndicatorArrowLeft) : PE_IndicatorArrowDown;
+ else
+ pe = (opt->state & State_Horizontal) ? (opt->direction == Qt::LeftToRight ? PE_IndicatorArrowLeft : PE_IndicatorArrowRight) : PE_IndicatorArrowUp;
+ QStyleOption arrowOpt = *opt;
+ arrowOpt.state |= State_Enabled;
+ drawPrimitive(pe, &arrowOpt, p, widget);
+ if (!(opt->state & State_Enabled) && styleHint(SH_DitherDisabledText)) {
+ int fw = pixelMetric(PM_DefaultFrameWidth);
+ p->fillRect(opt->rect.adjusted(fw, fw, -fw, -fw), QBrush(p->background().color(), Qt::Dense5Pattern));
+ }
+ }break;
+
+ case CE_ScrollBarSubPage:
+ case CE_ScrollBarAddPage:
+ p->fillRect(opt->rect, opt->palette.brush((opt->state & State_Enabled) ? QPalette::Mid : QPalette::Window));
+ break;
+
+ case CE_ScrollBarSlider: {
+ QStyleOption bevelOpt = *opt;
+ bevelOpt.state |= State_Raised;
+ bevelOpt.state &= ~(State_Sunken | State_On);
+ p->save();
+ p->setBrushOrigin(bevelOpt.rect.topLeft());
+ drawPrimitive(PE_PanelButtonBevel, &bevelOpt, p, widget);
+ p->restore();
+ if (!(opt->state & State_Enabled) && styleHint(SH_DitherDisabledText))
+ p->fillRect(opt->rect, QBrush(p->background().color(), Qt::Dense5Pattern));
+ break; }
+
+ case CE_RadioButton:
+ case CE_CheckBox:
+ if (const QStyleOptionButton *btn = qstyleoption_cast<const QStyleOptionButton *>(opt)) {
+ bool isRadio = (element == CE_RadioButton);
+ QStyleOptionButton subopt = *btn;
+ subopt.rect = subElementRect(isRadio ? SE_RadioButtonIndicator
+ : SE_CheckBoxIndicator, btn, widget);
+ drawPrimitive(isRadio ? PE_IndicatorRadioButton : PE_IndicatorCheckBox,
+ &subopt, p, widget);
+ subopt.rect = subElementRect(isRadio ? SE_RadioButtonContents
+ : SE_CheckBoxContents, btn, widget);
+ drawControl(isRadio ? CE_RadioButtonLabel : CE_CheckBoxLabel, &subopt, p, widget);
+ if ((btn->state & State_HasFocus) && (!focus || !focus->isVisible())) {
+ QStyleOptionFocusRect fropt;
+ fropt.QStyleOption::operator=(*btn);
+ fropt.rect = subElementRect(isRadio ? SE_RadioButtonFocusRect
+ : SE_CheckBoxFocusRect, btn, widget);
+ drawPrimitive(PE_FrameFocusRect, &fropt, p, widget);
+ }
+ }
+ break;
+ case CE_PushButton:
+ if (const QStyleOptionButton *btn = qstyleoption_cast<const QStyleOptionButton *>(opt)) {
+ drawControl(CE_PushButtonBevel, btn, p, widget);
+ QStyleOptionButton subopt = *btn;
+ subopt.rect = subElementRect(SE_PushButtonContents, btn, widget);
+ drawControl(CE_PushButtonLabel, &subopt, p, widget);
+ if ((btn->state & State_HasFocus) && (!focus || !focus->isVisible())) {
+ QStyleOptionFocusRect fropt;
+ fropt.QStyleOption::operator=(*btn);
+ fropt.rect = subElementRect(SE_PushButtonFocusRect, btn, widget);
+ drawPrimitive(PE_FrameFocusRect, &fropt, p, widget);
+ }
+ }
+ break;
+ case CE_PushButtonBevel:
+ if (const QStyleOptionButton *btn = qstyleoption_cast<const QStyleOptionButton *>(opt)) {
+ int diw, x1, y1, x2, y2;
+ p->setPen(opt->palette.foreground().color());
+ p->setBrush(QBrush(opt->palette.button().color(), Qt::NoBrush));
+ diw = pixelMetric(PM_ButtonDefaultIndicator);
+ opt->rect.getCoords(&x1, &y1, &x2, &y2);
+ if (btn->features & (QStyleOptionButton::AutoDefaultButton|QStyleOptionButton::DefaultButton)) {
+ x1 += diw;
+ y1 += diw;
+ x2 -= diw;
+ y2 -= diw;
+ }
+ if (btn->features & QStyleOptionButton::DefaultButton) {
+ if (diw == 0) {
+ QPolygon a;
+ a.setPoints(9,
+ x1, y1, x2, y1, x2, y2, x1, y2, x1, y1+1,
+ x2-1, y1+1, x2-1, y2-1, x1+1, y2-1, x1+1, y1+1);
+ p->setPen(opt->palette.shadow().color());
+ p->drawPolygon(a);
+ x1 += 2;
+ y1 += 2;
+ x2 -= 2;
+ y2 -= 2;
+ } else {
+ qDrawShadePanel(p, opt->rect.adjusted(1, 1, -1, -1), opt->palette, true);
+ }
+ }
+ if (!(btn->features & QStyleOptionButton::Flat) ||
+ (btn->state & (State_Sunken | State_On))) {
+ QStyleOptionButton newOpt = *btn;
+ newOpt.rect = QRect(x1, y1, x2 - x1 + 1, y2 - y1 + 1);
+ p->setBrushOrigin(p->brushOrigin());
+ drawPrimitive(PE_PanelButtonCommand, &newOpt, p, widget);
+ }
+ if (btn->features & QStyleOptionButton::HasMenu) {
+ int mbi = pixelMetric(PM_MenuButtonIndicator, btn, widget);
+ QRect ir = btn->rect;
+ QStyleOptionButton newBtn = *btn;
+ newBtn.rect = QRect(ir.right() - mbi - 3, ir.y() + 4, mbi, ir.height() - 8);
+ drawPrimitive(PE_IndicatorArrowDown, &newBtn, p, widget);
+ }
+ break;
+ }
+
+#ifndef QT_NO_TABBAR
+ case CE_TabBarTabShape:
+ if (const QStyleOptionTab *tab = qstyleoption_cast<const QStyleOptionTab *>(opt)) {
+ const int default_frame = pixelMetric(PM_DefaultFrameWidth, tab, widget);
+ const int frame_offset = (default_frame > 1) ? 1 : 0;
+
+ if (tab->shape == QTabBar::RoundedNorth || tab->shape == QTabBar::RoundedEast ||
+ tab->shape == QTabBar::RoundedSouth || tab->shape == QTabBar::RoundedWest) {
+ p->save();
+ QRect tabRect = opt->rect;
+ QColor tabLight = opt->palette.light().color();
+ QColor tabDark = opt->palette.dark().color();
+
+ p->fillRect(opt->rect.adjusted(default_frame, default_frame,
+ -default_frame, -default_frame),
+ tab->palette.background());
+
+ if(tab->shape == QTabBar::RoundedWest) {
+ tabDark = opt->palette.light().color();
+ tabLight = opt->palette.dark().color();
+ tabRect = QRect(0, 0, tabRect.height(), tabRect.width());
+ p->translate(opt->rect.left(), opt->rect.bottom());
+ p->rotate(-90);
+ } else if(tab->shape == QTabBar::RoundedSouth) {
+ tabDark = opt->palette.light().color();
+ tabLight = opt->palette.dark().color();
+ tabRect = QRect(0, 0, tabRect.width(), tabRect.height());
+ p->translate(opt->rect.right(), opt->rect.bottom());
+ p->rotate(180);
+ } else if(tab->shape == QTabBar::RoundedEast) {
+ tabRect = QRect(0, 0, tabRect.height(), tabRect.width());
+ p->translate(opt->rect.right(), opt->rect.top());
+ p->rotate(90);
+ }
+
+ if (default_frame > 1) {
+ p->setPen(tabLight);
+ p->drawLine(tabRect.left(), tabRect.bottom(),
+ tabRect.right(), tabRect.bottom());
+ p->setPen(tabLight);
+ p->drawLine(tabRect.left(), tabRect.bottom()-1,
+ tabRect.right(), tabRect.bottom()-1);
+ if (tabRect.left() == 0)
+ p->drawPoint(tabRect.bottomLeft());
+ } else {
+ p->setPen(tabLight);
+ p->drawLine(tabRect.left(), tabRect.bottom(),
+ tabRect.right(), tabRect.bottom());
+ }
+
+ if (opt->state & State_Selected) {
+ p->fillRect(QRect(tabRect.left()+1, tabRect.bottom()-frame_offset,
+ tabRect.width()-3, 2),
+ tab->palette.brush(QPalette::Active, QPalette::Background));
+ p->setPen(tab->palette.background().color());
+ p->drawLine(tabRect.left()+1, tabRect.bottom(),
+ tabRect.left()+1, tabRect.top()+2);
+ p->setPen(tabLight);
+ } else {
+ p->setPen(tabLight);
+ }
+ p->drawLine(tabRect.left(), tabRect.bottom()-1,
+ tabRect.left(), tabRect.top() + 2);
+ p->drawPoint(tabRect.left()+1, tabRect.top() + 1);
+ p->drawLine(tabRect.left()+2, tabRect.top(),
+ tabRect.right() - 2, tabRect.top());
+ p->drawPoint(tabRect.left(), tabRect.bottom());
+
+ if (default_frame > 1) {
+ p->drawLine(tabRect.left()+1, tabRect.bottom(),
+ tabRect.left()+1, tabRect.top() + 2);
+ p->drawLine(tabRect.left()+2, tabRect.top()+1,
+ tabRect.right() - 2, tabRect.top()+1);
+ }
+
+ p->setPen(tabDark);
+ p->drawLine(tabRect.right() - 1, tabRect.top() + 2,
+ tabRect.right() - 1, tabRect.bottom() - 1 +
+ ((opt->state & State_Selected) ? frame_offset : -frame_offset));
+ if (default_frame > 1) {
+ p->drawPoint(tabRect.right() - 1, tabRect.top() + 1);
+ p->drawLine(tabRect.right(), tabRect.top() + 2, tabRect.right(),
+ tabRect.bottom() -
+ ((opt->state & State_Selected) ?
+ ((tab->position == QStyleOptionTab::End) ? 0:1):1+frame_offset));
+ p->drawPoint(tabRect.right() - 1, tabRect.top() + 1);
+ }
+ p->restore();
+ } else {
+ QCommonStyle::drawControl(element, opt, p, widget);
+ }
+ break; }
+#endif // QT_NO_TABBAR
+ case CE_ProgressBarGroove:
+ qDrawShadePanel(p, opt->rect, opt->palette, true, 2);
+ break;
+
+ case CE_ProgressBarLabel:
+ if (const QStyleOptionProgressBar *pb = qstyleoption_cast<const QStyleOptionProgressBar *>(opt)) {
+ QTransform oldMatrix = p->transform();
+ QRect rect = pb->rect;
+ bool vertical = false;
+ bool invert = false;
+ bool bottomToTop = false;
+ if (const QStyleOptionProgressBarV2 *pb2 = qstyleoption_cast<const QStyleOptionProgressBarV2 *>(opt)) {
+ vertical = (pb2->orientation == Qt::Vertical);
+ invert = pb2->invertedAppearance;
+ bottomToTop = pb2->bottomToTop;
+ }
+ if (vertical) {
+ QTransform m;
+ rect = QRect(rect.left(), rect.top(), rect.height(), rect.width()); // flip width and height
+ if (bottomToTop) {
+ m.translate(0.0, rect.width());
+ m.rotate(-90);
+ } else {
+ m.translate(rect.height(), 0.0);
+ m.rotate(90);
+ }
+ p->setTransform(m, true);
+ }
+ const int unit_width = pixelMetric(PM_ProgressBarChunkWidth, opt, widget);
+ int u = rect.width() / unit_width;
+ int p_v = pb->progress - pb->minimum;
+ int t_s = qMax(0, pb->maximum - pb->minimum);
+ if (u > 0 && pb->progress >= INT_MAX / u && t_s >= u) {
+ // scale down to something usable.
+ p_v /= u;
+ t_s /= u;
+ }
+ if (pb->textVisible && t_s) {
+ int nu = (u * p_v + t_s/2) / t_s;
+ int x = unit_width * nu;
+ QRect left(rect.x(), rect.y(), x, rect.height());
+ QRect right(rect.x() + x, rect.y(), rect.width() - x, rect.height());
+ Qt::LayoutDirection dir;
+ dir = vertical ? (bottomToTop ? Qt::LeftToRight : Qt::RightToLeft) : pb->direction;
+ if (invert)
+ dir = (dir == Qt::LeftToRight) ? Qt::RightToLeft : Qt::LeftToRight;
+ const QRect highlighted = visualRect(dir, rect, left);
+ const QRect background = visualRect(dir, rect, right);
+ p->setPen(opt->palette.highlightedText().color());
+ p->setClipRect(highlighted);
+ p->drawText(rect, Qt::AlignCenter | Qt::TextSingleLine, pb->text);
+
+ if (pb->progress != pb->maximum) {
+ p->setClipRect(background);
+ p->setPen(opt->palette.highlight().color());
+ p->drawText(rect, Qt::AlignCenter | Qt::TextSingleLine, pb->text);
+ }
+ }
+ p->setTransform(oldMatrix, false);
+ break;
+ }
+
+ case CE_MenuTearoff: {
+ if(opt->state & State_Selected) {
+ if(pixelMetric(PM_MenuPanelWidth, opt, widget) > 1)
+ qDrawShadePanel(p, opt->rect.x(), opt->rect.y(), opt->rect.width(),
+ opt->rect.height(), opt->palette, false, motifItemFrame,
+ &opt->palette.brush(QPalette::Button));
+ else
+ qDrawShadePanel(p, opt->rect.x()+1, opt->rect.y()+1, opt->rect.width()-2,
+ opt->rect.height()-2, opt->palette, true, 1, &opt->palette.brush(QPalette::Button));
+ } else {
+ p->fillRect(opt->rect, opt->palette.brush(QPalette::Button));
+ }
+ p->setPen(QPen(opt->palette.dark().color(), 1, Qt::DashLine));
+ p->drawLine(opt->rect.x()+2, opt->rect.y()+opt->rect.height()/2-1, opt->rect.x()+opt->rect.width()-4,
+ opt->rect.y()+opt->rect.height()/2-1);
+ p->setPen(QPen(opt->palette.light().color(), 1, Qt::DashLine));
+ p->drawLine(opt->rect.x()+2, opt->rect.y()+opt->rect.height()/2, opt->rect.x()+opt->rect.width()-4,
+ opt->rect.y()+opt->rect.height()/2);
+ break; }
+
+ case CE_MenuItem:
+ if (const QStyleOptionMenuItem *menuitem = qstyleoption_cast<const QStyleOptionMenuItem *>(opt)) {
+ int maxpmw = menuitem->maxIconWidth;
+ if(menuitem->menuHasCheckableItems)
+ maxpmw = qMax(maxpmw, motifCheckMarkSpace);
+
+ int x, y, w, h;
+ opt->rect.getRect(&x, &y, &w, &h);
+
+ if (menuitem->menuItemType == QStyleOptionMenuItem::Separator) { // draw separator
+ int textWidth = 0;
+ if (!menuitem->text.isEmpty()) {
+ QFont oldFont = p->font();
+ p->setFont(menuitem->font);
+ p->fillRect(x, y, w, h, opt->palette.brush(QPalette::Button));
+ drawItemText(p, menuitem->rect.adjusted(10, 0, -5, 0), Qt::AlignLeft | Qt::AlignVCenter,
+ menuitem->palette, menuitem->state & State_Enabled, menuitem->text,
+ QPalette::Text);
+ textWidth = menuitem->fontMetrics.width(menuitem->text) + 10;
+ y += menuitem->fontMetrics.lineSpacing() / 2;
+ p->setFont(oldFont);
+ }
+ p->setPen(opt->palette.dark().color());
+ p->drawLine(x, y, x + 5, y);
+ p->drawLine(x + 5 + textWidth, y, x+w, y);
+ p->setPen(opt->palette.light().color());
+ p->drawLine(x, y + 1, x + 5, y + 1);
+ p->drawLine(x + 5 + textWidth, y + 1, x+w, y + 1);
+ return;
+ }
+
+ int pw = motifItemFrame;
+ if((opt->state & State_Selected) && (opt->state & State_Enabled)) { // active item frame
+ if(pixelMetric(PM_MenuPanelWidth, opt) > 1)
+ qDrawShadePanel(p, x, y, w, h, opt->palette, false, pw,
+ &opt->palette.brush(QPalette::Button));
+ else
+ qDrawShadePanel(p, x+1, y+1, w-2, h-2, opt->palette, true, 1,
+ &opt->palette.brush(QPalette::Button));
+ } else { // incognito frame
+ p->fillRect(x, y, w, h, opt->palette.brush(QPalette::Button));
+ }
+
+ QRect vrect = visualRect(opt->direction, opt->rect,
+ QRect(x+motifItemFrame, y+motifItemFrame, maxpmw,
+ h-2*motifItemFrame));
+ int xvis = vrect.x();
+ if (menuitem->checked) {
+ if(!menuitem->icon.isNull())
+ qDrawShadePanel(p, xvis, y+motifItemFrame, maxpmw, h-2*motifItemFrame,
+ opt->palette, true, 1, &opt->palette.brush(QPalette::Midlight));
+ } else if (!(opt->state & State_Selected)) {
+ p->fillRect(xvis, y+motifItemFrame, maxpmw, h-2*motifItemFrame,
+ opt->palette.brush(QPalette::Button));
+ }
+
+ if(!menuitem->icon.isNull()) { // draw icon
+ QIcon::Mode mode = QIcon::Normal; // no disabled icons in Motif
+ if ((opt->state & State_Selected) && !!(opt->state & State_Enabled))
+ mode = QIcon::Active;
+ QPixmap pixmap;
+ if (menuitem->checkType != QStyleOptionMenuItem::NotCheckable && menuitem->checked)
+ pixmap = menuitem->icon.pixmap(pixelMetric(PM_SmallIconSize, opt, widget), mode, QIcon::On);
+ else
+ pixmap = menuitem->icon.pixmap(pixelMetric(PM_SmallIconSize, opt, widget), mode);
+
+ int pixw = pixmap.width();
+ int pixh = pixmap.height();
+ QRect pmr(0, 0, pixw, pixh);
+ pmr.moveCenter(vrect.center());
+ p->setPen(opt->palette.text().color());
+ p->drawPixmap(pmr.topLeft(), pixmap);
+
+ } else if (menuitem->checkType != QStyleOptionMenuItem::NotCheckable) { // just "checking"...
+ int mh = h - 2*motifItemFrame;
+
+ QStyleOptionButton newMenuItem;
+ newMenuItem.state = menuitem->checked ? State_On : State_None;
+ if (opt->state & State_Enabled) {
+ newMenuItem.state |= State_Enabled;
+ if (menuitem->state & State_Sunken)
+ newMenuItem.state |= State_Sunken;
+ }
+ if (menuitem->checkType & QStyleOptionMenuItem::Exclusive) {
+ newMenuItem.rect.setRect(xvis + 2, y + motifItemFrame + mh / 4, 11, 11);
+ drawPrimitive(PE_IndicatorRadioButton, &newMenuItem, p, widget);
+ } else {
+ newMenuItem.rect.setRect(xvis + 5, y + motifItemFrame + mh / 4, 9, 9);
+ drawPrimitive(PE_IndicatorCheckBox, &newMenuItem, p, widget);
+ }
+ }
+
+ p->setPen(opt->palette.buttonText().color());
+
+ QColor discol;
+ if (!(opt->state & State_Enabled)) {
+ discol = opt->palette.text().color();
+ p->setPen(discol);
+ }
+
+ int xm = motifItemFrame + maxpmw + motifItemHMargin;
+
+ vrect = visualRect(opt->direction, opt->rect,
+ QRect(x+xm, y+motifItemVMargin, w-xm-menuitem->tabWidth,
+ h-2*motifItemVMargin));
+ xvis = vrect.x();
+
+ QString s = menuitem->text;
+ if (!s.isNull()) { // draw text
+ int t = s.indexOf(QLatin1Char('\t'));
+ int m = motifItemVMargin;
+ int text_flags = Qt::AlignVCenter|Qt::TextShowMnemonic | Qt::TextDontClip | Qt::TextSingleLine;
+ text_flags |= Qt::AlignLeft;
+ QFont oldFont = p->font();
+ p->setFont(menuitem->font);
+ if (t >= 0) { // draw tab text
+ QRect vr = visualRect(opt->direction, opt->rect,
+ QRect(x+w-menuitem->tabWidth-motifItemHMargin-motifItemFrame,
+ y+motifItemVMargin, menuitem->tabWidth,
+ h-2*motifItemVMargin));
+ int xv = vr.x();
+ QRect tr(xv, y+m, menuitem->tabWidth, h-2*m);
+ p->drawText(tr, text_flags, s.mid(t+1));
+ if (!(opt->state & State_Enabled) && styleHint(SH_DitherDisabledText))
+ p->fillRect(tr, QBrush(p->background().color(), Qt::Dense5Pattern));
+ s = s.left(t);
+ }
+ QRect tr(xvis, y+m, w - xm - menuitem->tabWidth + 1, h-2*m);
+ p->drawText(tr, text_flags, s.left(t));
+ p->setFont(oldFont);
+ if (!(opt->state & State_Enabled) && styleHint(SH_DitherDisabledText))
+ p->fillRect(tr, QBrush(p->background().color(), Qt::Dense5Pattern));
+ }
+ if (menuitem->menuItemType == QStyleOptionMenuItem::SubMenu) { // draw sub menu arrow
+ int dim = (h-2*motifItemFrame) / 2;
+ QStyle::PrimitiveElement arrow = (opt->direction == Qt::RightToLeft ? PE_IndicatorArrowLeft : PE_IndicatorArrowRight);
+ QStyleOption arrowOpt = *opt;
+ arrowOpt.rect = visualRect(opt->direction, opt->rect,
+ QRect(x+w - motifArrowHMargin - motifItemFrame - dim,
+ y+h/2-dim/2, dim, dim));
+ if ((opt->state & State_Selected))
+ arrowOpt.state = (State_Sunken | ((opt->state & State_Enabled) ? State_Enabled : State_None));
+ else
+ arrowOpt.state = ((opt->state & State_Enabled) ? State_Enabled : State_None);
+ drawPrimitive(arrow, &arrowOpt, p, widget);
+ }
+ break; }
+
+ case CE_MenuBarItem:
+ if (opt->state & State_Selected) // active item
+ qDrawShadePanel(p, opt->rect, opt->palette, false, motifItemFrame,
+ &opt->palette.brush(QPalette::Button));
+ else // other item
+ p->fillRect(opt->rect, opt->palette.brush(QPalette::Button));
+ QCommonStyle::drawControl(element, opt, p, widget);
+ break;
+
+ case CE_HeaderSection:
+ p->save();
+ p->setBrushOrigin(opt->rect.topLeft());
+ qDrawShadePanel(p, opt->rect, opt->palette, bool(opt->state & (State_Sunken|State_On)),
+ pixelMetric(PM_DefaultFrameWidth),
+ &opt->palette.brush((opt->state & State_Sunken) ? QPalette::Mid : QPalette::Button));
+ p->restore();
+ break;
+ case CE_RubberBand: {
+ QPixmap tiledPixmap(16, 16);
+ QPainter pixmapPainter(&tiledPixmap);
+ pixmapPainter.setPen(Qt::NoPen);
+ pixmapPainter.setBrush(Qt::Dense4Pattern);
+ pixmapPainter.setBackground(QBrush(opt->palette.base()));
+ pixmapPainter.setBackgroundMode(Qt::OpaqueMode);
+ pixmapPainter.drawRect(0, 0, tiledPixmap.width(), tiledPixmap.height());
+ pixmapPainter.end();
+ // ### workaround for borked XRENDER
+ tiledPixmap = QPixmap::fromImage(tiledPixmap.toImage());
+
+ p->save();
+ QRect r = opt->rect;
+ QStyleHintReturnMask mask;
+ if (styleHint(QStyle::SH_RubberBand_Mask, opt, widget, &mask))
+ p->setClipRegion(mask.region);
+ p->drawTiledPixmap(r.x(), r.y(), r.width(), r.height(), tiledPixmap);
+ p->restore();
+ }
+ break;
+#ifndef QT_NO_PROGRESSBAR
+ case CE_ProgressBarContents:
+ if (const QStyleOptionProgressBar *pb = qstyleoption_cast<const QStyleOptionProgressBar *>(opt)) {
+ QRect rect = pb->rect;
+ bool vertical = false;
+ bool inverted = false;
+
+ // Get extra style options if version 2
+ const QStyleOptionProgressBarV2 *pb2 = qstyleoption_cast<const QStyleOptionProgressBarV2 *>(opt);
+ if (pb2) {
+ vertical = (pb2->orientation == Qt::Vertical);
+ inverted = pb2->invertedAppearance;
+ }
+
+ QTransform m;
+ if (vertical) {
+ rect = QRect(rect.left(), rect.top(), rect.height(), rect.width()); // flip width and height
+ m.rotate(90);
+ m.translate(0, -(rect.height() + rect.y()*2));
+ }
+
+ QPalette pal2 = pb->palette;
+ // Correct the highlight color if it is the same as the background
+ if (pal2.highlight() == pal2.background())
+ pal2.setColor(QPalette::Highlight, pb->palette.color(QPalette::Active,
+ QPalette::Highlight));
+ bool reverse = ((!vertical && (pb->direction == Qt::RightToLeft)) || vertical);
+ if (inverted)
+ reverse = !reverse;
+ int w = rect.width();
+ if (pb->minimum == 0 && pb->maximum == 0) {
+ QRect progressBar;
+ Q_D(const QMotifStyle);
+ // draw busy indicator
+ int x = (d->animateStep*8)% (w * 2);
+ if (x > w)
+ x = 2 * w - x;
+ x = reverse ? rect.right() - x : x + rect.x();
+ p->setTransform(m, true);
+ p->setPen(QPen(pal2.highlight().color(), 4));
+ p->drawLine(x, rect.y(), x, rect.height());
+
+ } else
+ QCommonStyle::drawControl(element, opt, p, widget);
+ }
+ break;
+#endif // QT_NO_PROGRESSBAR
+ default:
+ QCommonStyle::drawControl(element, opt, p, widget);
+ break; }
+}
+
+static int get_combo_extra_width(int h, int w, int *return_awh=0)
+{
+ int awh,
+ tmp;
+ if (h < 8) {
+ awh = 6;
+ } else if (h < 14) {
+ awh = h - 2;
+ } else {
+ awh = h/2;
+ }
+ tmp = (awh * 3) / 2;
+ if (tmp > w / 2) {
+ awh = w / 2 - 3;
+ tmp = w / 2 + 3;
+ }
+
+ if (return_awh)
+ *return_awh = awh;
+
+ return tmp;
+}
+
+static void get_combo_parameters(const QRect &r,
+ int &ew, int &awh, int &ax,
+ int &ay, int &sh, int &dh,
+ int &sy)
+{
+ ew = get_combo_extra_width(r.height(), r.width(), &awh);
+
+ sh = (awh+3)/4;
+ if (sh < 3)
+ sh = 3;
+ dh = sh/2 + 1;
+
+ ay = r.y() + (r.height()-awh-sh-dh)/2;
+ if (ay < 0) {
+ //panic mode
+ ay = 0;
+ sy = r.height();
+ } else {
+ sy = ay+awh+dh;
+ }
+ ax = r.x() + r.width() - ew;
+ ax += (ew-awh)/2;
+}
+
+/*!
+ \reimp
+*/
+void QMotifStyle::drawComplexControl(ComplexControl cc, const QStyleOptionComplex *opt, QPainter *p,
+ const QWidget *widget) const
+{
+ switch (cc) {
+ case CC_ToolButton:
+ if (const QStyleOptionToolButton *toolbutton
+ = qstyleoption_cast<const QStyleOptionToolButton *>(opt)) {
+ QRect button, menuarea;
+ button = subControlRect(cc, toolbutton, SC_ToolButton, widget);
+ menuarea = subControlRect(cc, toolbutton, SC_ToolButtonMenu, widget);
+
+ State bflags = toolbutton->state & ~State_Sunken;
+ if (bflags & State_AutoRaise) {
+ if (!(bflags & State_MouseOver) || !(bflags & State_Enabled)) {
+ bflags &= ~State_Raised;
+ }
+ }
+ State mflags = bflags;
+ if (toolbutton->state & State_Sunken) {
+ if (toolbutton->activeSubControls & SC_ToolButton)
+ bflags |= State_Sunken;
+ mflags |= State_Sunken;
+ }
+
+ QStyleOption tool(0);
+ tool.palette = toolbutton->palette;
+ if (toolbutton->subControls & SC_ToolButton) {
+ if (bflags & (State_Sunken | State_On | State_Raised)) {
+ tool.rect = button;
+ tool.state = bflags;
+ drawPrimitive(PE_PanelButtonTool, &tool, p, widget);
+ }
+ }
+
+ if ((toolbutton->state & State_HasFocus) && (!focus || !focus->isVisible())) {
+ QStyleOptionFocusRect fr;
+ fr.QStyleOption::operator=(*toolbutton);
+ fr.rect = toolbutton->rect.adjusted(3, 3, -3, -3);
+ drawPrimitive(PE_FrameFocusRect, &fr, p, widget);
+ }
+ QStyleOptionToolButton label = *toolbutton;
+ label.state = bflags;
+ int fw = pixelMetric(PM_DefaultFrameWidth, opt, widget);
+ label.rect = button.adjusted(fw, fw, -fw, -fw);
+ drawControl(CE_ToolButtonLabel, &label, p, widget);
+
+ if (toolbutton->subControls & SC_ToolButtonMenu) {
+ tool.rect = menuarea;
+ tool.state = mflags;
+ if (mflags & (State_Sunken | State_On | State_Raised))
+ drawPrimitive(PE_IndicatorButtonDropDown, &tool, p, widget);
+ drawPrimitive(PE_IndicatorArrowDown, &tool, p, widget);
+ } else if (toolbutton->features & QStyleOptionToolButton::HasMenu) {
+ int mbi = pixelMetric(PM_MenuButtonIndicator, toolbutton, widget);
+ QRect ir = toolbutton->rect;
+ QStyleOptionToolButton newBtn = *toolbutton;
+ newBtn.rect = QRect(ir.right() + 5 - mbi, ir.height() - mbi + 4, mbi - 6, mbi - 6);
+ drawPrimitive(PE_IndicatorArrowDown, &newBtn, p, widget);
+ }
+ }
+ break;
+#ifndef QT_NO_SPINBOX
+ case CC_SpinBox:
+ if (const QStyleOptionSpinBox *spinbox = qstyleoption_cast<const QStyleOptionSpinBox *>(opt)) {
+ QStyleOptionSpinBox copy = *spinbox;
+ PrimitiveElement pe;
+
+ if (spinbox->frame && (spinbox->subControls & SC_SpinBoxFrame)) {
+ QRect r = subControlRect(CC_SpinBox, spinbox, SC_SpinBoxFrame, widget);
+ qDrawShadePanel(p, r, opt->palette, false, pixelMetric(PM_SpinBoxFrameWidth));
+
+ int fw = pixelMetric(QStyle::PM_DefaultFrameWidth);
+ r = subControlRect(CC_SpinBox, spinbox, SC_SpinBoxEditField, widget).adjusted(-fw,-fw,fw,fw);
+ QStyleOptionFrame lineOpt;
+ lineOpt.QStyleOption::operator=(*opt);
+ lineOpt.rect = r;
+ lineOpt.lineWidth = fw;
+ lineOpt.midLineWidth = 0;
+ lineOpt.state |= QStyle::State_Sunken;
+ drawPrimitive(QStyle::PE_FrameLineEdit, &lineOpt, p, widget);
+ }
+
+ if (spinbox->subControls & SC_SpinBoxUp) {
+ copy.subControls = SC_SpinBoxUp;
+ QPalette pal2 = spinbox->palette;
+ if (!(spinbox->stepEnabled & QAbstractSpinBox::StepUpEnabled)) {
+ pal2.setCurrentColorGroup(QPalette::Disabled);
+ copy.state &= ~State_Enabled;
+ }
+
+ copy.palette = pal2;
+
+ if (spinbox->activeSubControls == SC_SpinBoxUp && (spinbox->state & State_Sunken)) {
+ copy.state |= State_On;
+ copy.state |= State_Sunken;
+ } else {
+ copy.state |= State_Raised;
+ copy.state &= ~State_Sunken;
+ }
+ pe = (spinbox->buttonSymbols == QAbstractSpinBox::PlusMinus ? PE_IndicatorSpinPlus
+ : PE_IndicatorSpinUp);
+
+ copy.rect = subControlRect(CC_SpinBox, spinbox, SC_SpinBoxUp, widget);
+ drawPrimitive(pe, &copy, p, widget);
+ }
+
+ if (spinbox->subControls & SC_SpinBoxDown) {
+ copy.subControls = SC_SpinBoxDown;
+ copy.state = spinbox->state;
+ QPalette pal2 = spinbox->palette;
+ if (!(spinbox->stepEnabled & QAbstractSpinBox::StepDownEnabled)) {
+ pal2.setCurrentColorGroup(QPalette::Disabled);
+ copy.state &= ~State_Enabled;
+ }
+ copy.palette = pal2;
+
+ if (spinbox->activeSubControls == SC_SpinBoxDown && (spinbox->state & State_Sunken)) {
+ copy.state |= State_On;
+ copy.state |= State_Sunken;
+ } else {
+ copy.state |= State_Raised;
+ copy.state &= ~State_Sunken;
+ }
+ pe = (spinbox->buttonSymbols == QAbstractSpinBox::PlusMinus ? PE_IndicatorSpinMinus
+ : PE_IndicatorSpinDown);
+
+ copy.rect = subControlRect(CC_SpinBox, spinbox, SC_SpinBoxDown, widget);
+ drawPrimitive(pe, &copy, p, widget);
+ }
+ }
+ break;
+#endif // QT_NO_SPINBOX
+#ifndef QT_NO_SLIDER
+ case CC_Slider:
+ if (const QStyleOptionSlider *slider = qstyleoption_cast<const QStyleOptionSlider *>(opt)) {
+ QRect groove = subControlRect(CC_Slider, opt, SC_SliderGroove, widget),
+ handle = subControlRect(CC_Slider, opt, SC_SliderHandle, widget);
+
+ if ((opt->subControls & SC_SliderGroove) && groove.isValid()) {
+ qDrawShadePanel(p, groove, opt->palette, true, pixelMetric(PM_DefaultFrameWidth),
+ &opt->palette.brush((opt->state & State_Enabled) ? QPalette::Mid : QPalette::Window));
+ if ((opt->state & State_HasFocus) && (!focus || !focus->isVisible())) {
+ QStyleOption focusOpt = *opt;
+ focusOpt.rect = subElementRect(SE_SliderFocusRect, opt, widget);
+ drawPrimitive(PE_FrameFocusRect, &focusOpt, p, widget);
+ }
+ }
+
+ if ((opt->subControls & SC_SliderHandle) && handle.isValid()) {
+ QStyleOption bevelOpt = *opt;
+ bevelOpt.state = (opt->state | State_Raised) & ~State_Sunken;
+ bevelOpt.rect = handle;
+ p->save();
+ p->setBrushOrigin(bevelOpt.rect.topLeft());
+ drawPrimitive(PE_PanelButtonBevel, &bevelOpt, p, widget);
+ p->restore();
+
+ if (slider->orientation == Qt::Horizontal) {
+ int mid = handle.x() + handle.width() / 2;
+ qDrawShadeLine(p, mid, handle.y(), mid, handle.y() + handle.height() - 2,
+ opt->palette, true, 1);
+ } else {
+ int mid = handle.y() + handle.height() / 2;
+ qDrawShadeLine(p, handle.x(), mid, handle.x() + handle.width() - 2, mid, opt->palette,
+ true, 1);
+ }
+ if (!(opt->state & State_Enabled) && styleHint(SH_DitherDisabledText))
+ p->fillRect(handle, QBrush(p->background().color(), Qt::Dense5Pattern));
+ }
+
+ if (slider->subControls & SC_SliderTickmarks) {
+ QStyleOptionSlider tmpSlider = *slider;
+ tmpSlider.subControls = SC_SliderTickmarks;
+ int frameWidth = pixelMetric(PM_DefaultFrameWidth);
+ tmpSlider.rect.translate(frameWidth - 1, 0);
+ QCommonStyle::drawComplexControl(cc, &tmpSlider, p, widget);
+ }
+ }
+ break;
+#endif // QT_NO_SLIDER
+ case CC_ComboBox:
+ if (const QStyleOptionComboBox *cb = qstyleoption_cast<const QStyleOptionComboBox *>(opt)) {
+ if (opt->subControls & SC_ComboBoxArrow) {
+ int awh, ax, ay, sh, sy, dh, ew;
+ int fw = cb->frame ? pixelMetric(PM_ComboBoxFrameWidth, opt, widget) : 0;
+
+ if (cb->frame) {
+ QStyleOptionButton btn;
+ btn.QStyleOption::operator=(*cb);
+ btn.state |= QStyle::State_Raised;
+ drawPrimitive(PE_PanelButtonCommand, &btn, p, widget);
+ } else {
+ p->fillRect(opt->rect, opt->palette.brush(QPalette::Button));
+ }
+
+ QRect tr = opt->rect;
+ tr.adjust(fw, fw, -fw, -fw);
+ get_combo_parameters(tr, ew, awh, ax, ay, sh, dh, sy);
+
+ QRect ar = QStyle::visualRect(opt->direction, opt->rect, QRect(ax,ay,awh,awh));
+
+ QStyleOption arrowOpt = *opt;
+ arrowOpt.rect = ar;
+ arrowOpt.state |= State_Enabled;
+ drawPrimitive(PE_IndicatorArrowDown, &arrowOpt, p, widget);
+
+
+ // draws the shaded line under the arrow
+ p->setPen(opt->palette.light().color());
+ p->drawLine(ar.x(), sy, ar.x()+awh-1, sy);
+ p->drawLine(ar.x(), sy, ar.x(), sy+sh-1);
+ p->setPen(opt->palette.dark().color());
+ p->drawLine(ar.x()+1, sy+sh-1, ar.x()+awh-1, sy+sh-1);
+ p->drawLine(ar.x()+awh-1, sy+1, ar.x()+awh-1, sy+sh-1);
+
+ if ((cb->state & State_HasFocus) && (!focus || !focus->isVisible())) {
+ QStyleOptionFocusRect focus;
+ focus.QStyleOption::operator=(*opt);
+ focus.rect = subElementRect(SE_ComboBoxFocusRect, opt, widget);
+ focus.backgroundColor = opt->palette.button().color();
+ drawPrimitive(PE_FrameFocusRect, &focus, p, widget);
+ }
+ }
+
+ if (opt->subControls & SC_ComboBoxEditField) {
+ if (cb->editable) {
+ QRect er = subControlRect(CC_ComboBox, opt, SC_ComboBoxEditField, widget);
+ er.adjust(-1, -1, 1, 1);
+ qDrawShadePanel(p, er, opt->palette, true, 1,
+ &opt->palette.brush(QPalette::Base));
+ }
+ }
+ p->setPen(opt->palette.buttonText().color());
+ }
+ break;
+
+#ifndef QT_NO_SCROLLBAR
+ case CC_ScrollBar: {
+ if (opt->subControls & SC_ScrollBarGroove)
+ qDrawShadePanel(p, opt->rect, opt->palette, true,
+ pixelMetric(PM_DefaultFrameWidth, opt, widget),
+ &opt->palette.brush((opt->state & State_Enabled) ? QPalette::Mid : QPalette::Window));
+
+ if (const QStyleOptionSlider *scrollbar = qstyleoption_cast<const QStyleOptionSlider *>(opt)) {
+ QStyleOptionSlider newScrollbar = *scrollbar;
+ if (scrollbar->minimum == scrollbar->maximum)
+ newScrollbar.state |= State_Enabled; // make sure that the slider is drawn.
+ QCommonStyle::drawComplexControl(cc, &newScrollbar, p, widget);
+ }
+ break; }
+#endif
+
+ case CC_Q3ListView:
+ if (opt->subControls & (SC_Q3ListViewBranch | SC_Q3ListViewExpand)) {
+ int i;
+ if (opt->subControls & SC_Q3ListView)
+ QCommonStyle::drawComplexControl(cc, opt, p, widget);
+ if (const QStyleOptionQ3ListView *lv = qstyleoption_cast<const QStyleOptionQ3ListView *>(opt)) {
+ QStyleOptionQ3ListViewItem item = lv->items.at(0);
+ int y = opt->rect.y();
+ int c;
+ QPolygon dotlines;
+ if ((opt->activeSubControls & SC_All) && (opt->subControls & SC_Q3ListViewExpand)) {
+ c = 2;
+ dotlines.resize(2);
+ dotlines[0] = QPoint(opt->rect.right(), opt->rect.top());
+ dotlines[1] = QPoint(opt->rect.right(), opt->rect.bottom());
+ } else {
+ int linetop = 0, linebot = 0;
+ // each branch needs at most two lines, ie. four end points
+ dotlines.resize(item.childCount * 4);
+ c = 0;
+
+ // skip the stuff above the exposed rectangle
+ for (i = 1; i < lv->items.size(); ++i) {
+ QStyleOptionQ3ListViewItem child = lv->items.at(i);
+ if (child.height + y > 0)
+ break;
+ y += child.totalHeight;
+ }
+
+ int bx = opt->rect.width() / 2;
+
+ // paint stuff in the magical area
+ while (i < lv->items.size() && y < lv->rect.height()) {
+ QStyleOptionQ3ListViewItem child = lv->items.at(i);
+ if (child.features & QStyleOptionQ3ListViewItem::Visible) {
+ int lh;
+ if (!(item.features & QStyleOptionQ3ListViewItem::MultiLine))
+ lh = child.height;
+ else
+ lh = p->fontMetrics().height() + 2 * lv->itemMargin;
+ lh = qMax(lh, QApplication::globalStrut().height());
+ if (lh % 2 > 0)
+ lh++;
+ linebot = y + lh/2;
+ if ((child.features & QStyleOptionQ3ListViewItem::Expandable || child.childCount > 0) &&
+ child.height > 0) {
+ // needs a box
+ p->setPen(opt->palette.text().color());
+ p->drawRect(bx-4, linebot-4, 9, 9);
+ QPolygon a;
+ if ((child.state & State_Open))
+ a.setPoints(3, bx-2, linebot-2,
+ bx, linebot+2,
+ bx+2, linebot-2); //Qt::RightArrow
+ else
+ a.setPoints(3, bx-2, linebot-2,
+ bx+2, linebot,
+ bx-2, linebot+2); //Qt::DownArrow
+ p->setBrush(opt->palette.text());
+ p->drawPolygon(a);
+ p->setBrush(Qt::NoBrush);
+ // dotlinery
+ dotlines[c++] = QPoint(bx, linetop);
+ dotlines[c++] = QPoint(bx, linebot - 5);
+ dotlines[c++] = QPoint(bx + 5, linebot);
+ dotlines[c++] = QPoint(opt->rect.width(), linebot);
+ linetop = linebot + 5;
+ } else {
+ // just dotlinery
+ dotlines[c++] = QPoint(bx+1, linebot);
+ dotlines[c++] = QPoint(opt->rect.width(), linebot);
+ }
+ y += child.totalHeight;
+ }
+ ++i;
+ }
+
+ // Expand line height to edge of rectangle if there's any
+ // visible child below
+ while (i < lv->items.size() && lv->items.at(i).height <= 0)
+ ++i;
+ if (i < lv->items.size())
+ linebot = opt->rect.height();
+
+ if (linetop < linebot) {
+ dotlines[c++] = QPoint(bx, linetop);
+ dotlines[c++] = QPoint(bx, linebot);
+ }
+ }
+
+ int line; // index into dotlines
+ p->setPen(opt->palette.text().color());
+ if (opt->subControls & SC_Q3ListViewBranch) for(line = 0; line < c; line += 2) {
+ p->drawLine(dotlines[line].x(), dotlines[line].y(),
+ dotlines[line+1].x(), dotlines[line+1].y());
+ }
+ }
+ break; }
+
+ default:
+ QCommonStyle::drawComplexControl(cc, opt, p, widget);
+ break;
+ }
+}
+
+
+/*! \reimp */
+int QMotifStyle::pixelMetric(PixelMetric pm, const QStyleOption *opt,
+ const QWidget *widget) const
+{
+ int ret = 0;
+
+ switch(pm) {
+ case PM_ButtonDefaultIndicator:
+ ret = 5;
+ break;
+
+ case PM_CheckBoxLabelSpacing:
+ case PM_RadioButtonLabelSpacing:
+ ret = 10;
+ break;
+
+ case PM_ToolBarFrameWidth:
+ ret = pixelMetric(PM_DefaultFrameWidth);
+ break;
+
+ case PM_ToolBarItemMargin:
+ ret = 1;
+ break;
+
+ case PM_ButtonShiftHorizontal:
+ case PM_ButtonShiftVertical:
+ ret = 0;
+ break;
+
+ case PM_SplitterWidth:
+ ret = qMax(10, QApplication::globalStrut().width());
+ break;
+
+ case PM_SliderLength:
+ ret = 30;
+ break;
+
+ case PM_SliderThickness:
+ ret = 16 + 4 * pixelMetric(PM_DefaultFrameWidth);
+ break;
+#ifndef QT_NO_SLIDER
+ case PM_SliderControlThickness:
+ if (const QStyleOptionSlider *sl = qstyleoption_cast<const QStyleOptionSlider *>(opt)) {
+ int space = (sl->orientation == Qt::Horizontal) ? sl->rect.height() : sl->rect.width();
+ int ticks = sl->tickPosition;
+ int n = 0;
+ if (ticks & QSlider::TicksAbove)
+ n++;
+ if (ticks & QSlider::TicksBelow)
+ n++;
+ if (!n) {
+ ret = space;
+ break;
+ }
+
+ int thick = 6; // Magic constant to get 5 + 16 + 5
+
+ space -= thick;
+ //### the two sides may be unequal in size
+ if (space > 0)
+ thick += (space * 2) / (n + 2);
+ ret = thick;
+ }
+ break;
+
+ case PM_SliderSpaceAvailable:
+ if (const QStyleOptionSlider *sl = qstyleoption_cast<const QStyleOptionSlider *>(opt)) {
+ if (sl->orientation == Qt::Horizontal)
+ ret = sl->rect.width() - pixelMetric(PM_SliderLength, opt, widget) - 2 * pixelMetric(PM_DefaultFrameWidth, opt, widget);
+ else
+ ret = sl->rect.height() - pixelMetric(PM_SliderLength, opt, widget) - 2 * pixelMetric(PM_DefaultFrameWidth, opt, widget);
+ }
+ break;
+#endif // QT_NO_SLIDER
+ case PM_DockWidgetFrameWidth:
+ ret = 2;
+ break;
+
+ case PM_DockWidgetHandleExtent:
+ ret = 9;
+ break;
+
+ case PM_ProgressBarChunkWidth:
+ ret = 1;
+ break;
+
+ case PM_ExclusiveIndicatorWidth:
+ case PM_ExclusiveIndicatorHeight:
+ ret = 13;
+ break;
+
+ case PM_MenuBarHMargin:
+ ret = 2; // really ugly, but Motif
+ break;
+
+ case PM_MenuButtonIndicator:
+ if (!opt)
+ ret = 12;
+ else
+ ret = qMax(12, (opt->rect.height() - 4) / 3);
+ break;
+ default:
+ ret = QCommonStyle::pixelMetric(pm, opt, widget);
+ break;
+ }
+ return ret;
+}
+
+
+/*!
+ \reimp
+*/
+QRect
+QMotifStyle::subControlRect(ComplexControl cc, const QStyleOptionComplex *opt,
+ SubControl sc, const QWidget *widget) const
+{
+ switch (cc) {
+#ifndef QT_NO_SPINBOX
+ case CC_SpinBox:
+ if (const QStyleOptionSpinBox *spinbox = qstyleoption_cast<const QStyleOptionSpinBox *>(opt)) {
+ int fw = spinbox->frame ? pixelMetric(PM_SpinBoxFrameWidth, spinbox, widget) : 0;
+ QSize bs;
+ bs.setHeight(opt->rect.height()/2 - fw);
+ bs.setWidth(qMin(bs.height() * 8 / 5, opt->rect.width() / 4)); // 1.6 -approximate golden mean
+ bs = bs.expandedTo(QApplication::globalStrut());
+ int y = fw + spinbox->rect.y();
+ int x, lx, rx;
+ x = spinbox->rect.x() + opt->rect.width() - fw - bs.width();
+ lx = fw;
+ rx = x - fw * 2;
+ const int margin = spinbox->frame ? 4 : 0;
+ switch (sc) {
+ case SC_SpinBoxUp:
+ if (spinbox->buttonSymbols == QAbstractSpinBox::NoButtons)
+ return QRect();
+ return visualRect(spinbox->direction, spinbox->rect,
+ QRect(x, y, bs.width(), bs.height() - 1));
+ case SC_SpinBoxDown:
+ if (spinbox->buttonSymbols == QAbstractSpinBox::NoButtons)
+ return QRect();
+ return visualRect(spinbox->direction, spinbox->rect,
+ QRect(x, y + bs.height() + 1, bs.width(), bs.height() - 1));
+ case SC_SpinBoxEditField:
+ if (spinbox->buttonSymbols == QAbstractSpinBox::NoButtons)
+ return visualRect(spinbox->direction, spinbox->rect,
+ QRect(lx + margin, y + margin,
+ spinbox->rect.width() - 2*fw - 2*margin,
+ spinbox->rect.height() - 2*fw - 2*margin));
+
+ return visualRect(spinbox->direction, spinbox->rect,
+ QRect(lx + margin, y + margin, rx - margin,
+ spinbox->rect.height() - 2*fw - 2 * margin));
+ case SC_SpinBoxFrame:
+ return visualRect(spinbox->direction, spinbox->rect, spinbox->rect);
+ default:
+ break;
+ }
+ break; }
+#endif // QT_NO_SPINBOX
+#ifndef QT_NO_SLIDER
+ case CC_Slider:
+ if (const QStyleOptionSlider *slider = qstyleoption_cast<const QStyleOptionSlider *>(opt)) {
+ if (sc == SC_SliderHandle) {
+ int tickOffset = pixelMetric(PM_SliderTickmarkOffset, opt, widget);
+ int thickness = pixelMetric(PM_SliderControlThickness, opt, widget);
+ bool horizontal = slider->orientation == Qt::Horizontal;
+ int len = pixelMetric(PM_SliderLength, opt, widget);
+ int motifBorder = pixelMetric(PM_DefaultFrameWidth);
+ int sliderPos = sliderPositionFromValue(slider->minimum, slider->maximum, slider->sliderPosition,
+ horizontal ? slider->rect.width() - len - 2 * motifBorder
+ : slider->rect.height() - len - 2 * motifBorder,
+ slider->upsideDown);
+ if (horizontal)
+ return visualRect(slider->direction, slider->rect,
+ QRect(sliderPos + motifBorder, tickOffset + motifBorder, len,
+ thickness - 2 * motifBorder));
+ return visualRect(slider->direction, slider->rect,
+ QRect(tickOffset + motifBorder, sliderPos + motifBorder,
+ thickness - 2 * motifBorder, len));
+ }
+ }
+ break;
+#endif // QT_NO_SLIDER
+#ifndef QT_NO_SCROLLBAR
+ case CC_ScrollBar:
+ if (const QStyleOptionSlider *scrollbar = qstyleoption_cast<const QStyleOptionSlider *>(opt)) {
+ int dfw = pixelMetric(PM_DefaultFrameWidth);
+ QRect rect = visualRect(scrollbar->direction, scrollbar->rect,
+ QCommonStyle::subControlRect(cc, scrollbar, sc, widget));
+ if (sc == SC_ScrollBarSlider) {
+ if (scrollbar->orientation == Qt::Horizontal)
+ rect.adjust(-dfw, dfw, dfw, -dfw);
+ else
+ rect.adjust(dfw, -dfw, -dfw, dfw);
+ } else if (sc != SC_ScrollBarGroove) {
+ if (scrollbar->orientation == Qt::Horizontal)
+ rect.adjust(0, dfw, 0, -dfw);
+ else
+ rect.adjust(dfw, 0, -dfw, 0);
+ }
+ return visualRect(scrollbar->direction, scrollbar->rect, rect);
+ }
+ break;
+#endif // QT_NO_SCROLLBAR
+#ifndef QT_NO_COMBOBOX
+ case CC_ComboBox:
+ if (const QStyleOptionComboBox *cb = qstyleoption_cast<const QStyleOptionComboBox *>(opt)) {
+ switch (sc) {
+ case SC_ComboBoxArrow: {
+ int ew, awh, sh, dh, ax, ay, sy;
+ int fw = cb->frame ? pixelMetric(PM_ComboBoxFrameWidth, opt, widget) : 0;
+ QRect cr = opt->rect;
+ cr.adjust(fw, fw, -fw, -fw);
+ get_combo_parameters(cr, ew, awh, ax, ay, sh, dh, sy);
+ return visualRect(cb->direction, cb->rect, QRect(QPoint(ax, ay), cr.bottomRight()));
+ }
+
+ case SC_ComboBoxEditField: {
+ int fw = cb->frame ? pixelMetric(PM_ComboBoxFrameWidth, opt, widget) : 0;
+ QRect rect = opt->rect;
+ rect.adjust(fw, fw, -fw, -fw);
+ int ew = get_combo_extra_width(rect.height(), rect.width());
+ rect.adjust(1, 1, -1-ew, -1);
+ return visualRect(cb->direction, cb->rect, rect);
+ }
+
+ default:
+ break;
+ }
+ }
+ break;
+#endif // QT_NO_SCROLLBAR
+ default:
+ break;
+ }
+ return QCommonStyle::subControlRect(cc, opt, sc, widget);
+}
+
+/*!
+ \reimp
+*/
+QSize
+QMotifStyle::sizeFromContents(ContentsType ct, const QStyleOption *opt,
+ const QSize &contentsSize, const QWidget *widget) const
+{
+ QSize sz(contentsSize);
+
+ switch(ct) {
+ case CT_Splitter:
+ sz = QSize(10, 10);
+ break;
+
+ case CT_RadioButton:
+ case CT_CheckBox:
+ sz = QCommonStyle::sizeFromContents(ct, opt, contentsSize, widget);
+ sz.rwidth() += motifItemFrame;
+ break;
+
+ case CT_PushButton:
+ if (const QStyleOptionButton *btn = qstyleoption_cast<const QStyleOptionButton *>(opt)) {
+ sz = QCommonStyle::sizeFromContents(ct, opt, contentsSize, widget);
+ if (!btn->text.isEmpty() && (btn->features & (QStyleOptionButton::AutoDefaultButton|QStyleOptionButton::DefaultButton)))
+ sz.setWidth(qMax(75, sz.width()));
+ sz += QSize(0, 1); // magical extra pixel
+ }
+ break;
+
+ case CT_MenuBarItem: {
+ if(!sz.isEmpty())
+ sz += QSize(5*motifItemHMargin+1, 2*motifItemVMargin + motifItemFrame);
+ break; }
+
+ case CT_MenuItem:
+ if (const QStyleOptionMenuItem *mi = qstyleoption_cast<const QStyleOptionMenuItem *>(opt)) {
+ sz = QCommonStyle::sizeFromContents(ct, opt, sz, widget);
+ int w = sz.width(), h = sz.height();
+
+ if (mi->menuItemType == QStyleOptionMenuItem::Separator) {
+ w = 10;
+ h = (mi->text.isEmpty()) ? motifSepHeight : mi->fontMetrics.lineSpacing();
+ }
+
+ // a little bit of border can never harm
+ w += 2*motifItemHMargin + 2*motifItemFrame;
+
+ if (!mi->text.isNull() && mi->text.indexOf(QLatin1Char('\t')) >= 0)
+ // string contains tab
+ w += motifTabSpacing;
+ else if (mi->menuItemType == QStyleOptionMenuItem::SubMenu)
+ // submenu indicator needs some room if we don't have a tab column
+ w += motifArrowHMargin + 4*motifItemFrame;
+
+ int checkColumn = mi->maxIconWidth;
+ if (mi->menuHasCheckableItems)
+ checkColumn = qMax(checkColumn, motifCheckMarkSpace);
+ if (checkColumn > 0)
+ w += checkColumn + motifCheckMarkHMargin;
+
+ sz = QSize(w, h);
+ }
+ break;
+
+
+ default:
+ sz = QCommonStyle::sizeFromContents(ct, opt, contentsSize, widget);
+ break;
+ }
+
+ return sz;
+}
+
+/*!
+ \reimp
+*/
+QRect
+QMotifStyle::subElementRect(SubElement sr, const QStyleOption *opt, const QWidget *widget) const
+{
+ QRect rect;
+
+ switch (sr) {
+ case SE_SliderFocusRect:
+ rect = QCommonStyle::subElementRect(sr, opt, widget);
+ rect.adjust(2, 2, -2, -2);
+ break;
+
+ case SE_CheckBoxIndicator:
+ case SE_RadioButtonIndicator:
+ {
+ rect = visualRect(opt->direction, opt->rect,
+ QCommonStyle::subElementRect(sr, opt, widget));
+ rect.adjust(motifItemFrame,0, motifItemFrame,0);
+ rect = visualRect(opt->direction, opt->rect, rect);
+ }
+ break;
+
+ case SE_ComboBoxFocusRect:
+ {
+ int awh, ax, ay, sh, sy, dh, ew;
+ int fw = pixelMetric(PM_DefaultFrameWidth, opt, widget);
+ QRect tr = opt->rect;
+
+ tr.adjust(fw, fw, -fw, -fw);
+ get_combo_parameters(tr, ew, awh, ax, ay, sh, dh, sy);
+ rect.setRect(ax-2, ay-2, awh+4, awh+sh+dh+4);
+ break;
+ }
+
+ case SE_Q3DockWindowHandleRect:
+ if (const QStyleOptionQ3DockWindow *dw = qstyleoption_cast<const QStyleOptionQ3DockWindow *>(opt)) {
+ if (!dw->docked || !dw->closeEnabled)
+ rect.setRect(0, 0, opt->rect.width(), opt->rect.height());
+ else {
+ if (dw->state == State_Horizontal)
+ rect.setRect(2, 15, opt->rect.width()-2, opt->rect.height() - 15);
+ else
+ rect.setRect(0, 2, opt->rect.width() - 15, opt->rect.height() - 2);
+ }
+ rect = visualRect(dw->direction, dw->rect, rect);
+ }
+ break;
+
+ case SE_ProgressBarLabel:
+ case SE_ProgressBarGroove:
+ case SE_ProgressBarContents:
+ if (const QStyleOptionProgressBar *pb = qstyleoption_cast<const QStyleOptionProgressBar *>(opt)) {
+ int textw = 0;
+ if (pb->textVisible)
+ textw = pb->fontMetrics.width(QLatin1String("100%")) + 6;
+
+ if (pb->textAlignment == Qt::AlignLeft || pb->textAlignment == Qt::AlignCenter) {
+ rect = opt->rect;
+ } else {
+ if(sr == SE_ProgressBarLabel)
+ rect.setCoords(opt->rect.right() - textw, opt->rect.top(),
+ opt->rect.right(), opt->rect.bottom());
+ else
+ rect.setCoords(opt->rect.left(), opt->rect.top(),
+ opt->rect.right() - textw, opt->rect.bottom());
+ }
+ if (sr == SE_ProgressBarContents)
+ rect.adjust(2, 2, -2, -2);
+ rect = visualRect(pb->direction, pb->rect, rect);
+ }
+ break;
+ case SE_CheckBoxClickRect:
+ case SE_RadioButtonClickRect:
+ rect = visualRect(opt->direction, opt->rect, opt->rect);
+ break;
+
+ default:
+ rect = QCommonStyle::subElementRect(sr, opt, widget);
+ }
+ return rect;
+}
+
+#ifndef QT_NO_IMAGEFORMAT_XPM
+static const char * const qt_menu_xpm[] = {
+"16 16 11 1",
+" c #000000",
+", c #336600",
+". c #99CC00",
+"X c #666600",
+"o c #999933",
+"+ c #333300",
+"@ c #669900",
+"# c #999900",
+"$ c #336633",
+"% c #666633",
+"& c #99CC33",
+"................",
+"................",
+".....#,++X#.....",
+"....X X....",
+"...X Xo#% X&..",
+"..# o..&@o o..",
+".., X..#+ @X X..",
+"..+ o.o+ +o# +..",
+"..+ #o+ +## +..",
+".., %@ ++ +, X..",
+"..# o@oo+ #..",
+"...X X##$ o..",
+"....X X..",
+"....&oX++X#oX...",
+"................",
+"................"};
+
+
+static const char * const qt_close_xpm[] = {
+ "12 12 2 1",
+ " s None c None",
+ ". c black",
+ " ",
+ " ",
+ " . . ",
+ " ... ... ",
+ " ...... ",
+ " .... ",
+ " .... ",
+ " ...... ",
+ " ... ... ",
+ " . . ",
+ " ",
+ " "};
+
+static const char * const qt_maximize_xpm[] = {
+ "12 12 2 1",
+ " s None c None",
+ ". c black",
+ " ",
+ " ",
+ " ",
+ " . ",
+ " ... ",
+ " ..... ",
+ " ....... ",
+ " ......... ",
+ " ",
+ " ",
+ " ",
+ " "};
+
+static const char * const qt_minimize_xpm[] = {
+ "12 12 2 1",
+ " s None c None",
+ ". c black",
+ " ",
+ " ",
+ " ",
+ " ",
+ " ......... ",
+ " ....... ",
+ " ..... ",
+ " ... ",
+ " . ",
+ " ",
+ " ",
+ " "};
+
+#if 0 // ### not used???
+static const char * const qt_normalize_xpm[] = {
+ "12 12 2 1",
+ " s None c None",
+ ". c black",
+ " ",
+ " ",
+ " . ",
+ " .. ",
+ " ... ",
+ " .... ",
+ " ..... ",
+ " ...... ",
+ " ....... ",
+ " ",
+ " ",
+ " "};
+#endif
+
+static const char * const qt_normalizeup_xpm[] = {
+ "12 12 2 1",
+ " s None c None",
+ ". c black",
+ " ",
+ " ",
+ " ",
+ " ....... ",
+ " ...... ",
+ " ..... ",
+ " .... ",
+ " ... ",
+ " .. ",
+ " . ",
+ " ",
+ " "};
+
+static const char * const qt_shade_xpm[] = {
+ "12 12 2 1", "# c #000000",
+ ". c None",
+ "............",
+ "............",
+ ".#########..",
+ ".#########..",
+ "............",
+ "............",
+ "............",
+ "............",
+ "............",
+ "............",
+ "............",
+ "............"};
+
+
+static const char * const qt_unshade_xpm[] = {
+ "12 12 2 1",
+ "# c #000000",
+ ". c None",
+ "............",
+ "............",
+ ".#########..",
+ ".#########..",
+ ".#.......#..",
+ ".#.......#..",
+ ".#.......#..",
+ ".#.......#..",
+ ".#.......#..",
+ ".#########..",
+ "............",
+ "............"};
+
+
+static const char * dock_window_close_xpm[] = {
+ "8 8 2 1",
+ "# c #000000",
+ ". c None",
+ "##....##",
+ ".##..##.",
+ "..####..",
+ "...##...",
+ "..####..",
+ ".##..##.",
+ "##....##",
+ "........"};
+
+// Message box icons, from page 210 of the Windows style guide.
+
+// Hand-drawn to resemble Microsoft's icons, but in the Mac/Netscape palette.
+// Thanks to TrueColor displays, it is slightly more efficient to have
+// them duplicated.
+/* XPM */
+static const char * const information_xpm[]={
+ "32 32 5 1",
+ ". c None",
+ "c c #000000",
+ "* c #999999",
+ "a c #ffffff",
+ "b c #0000ff",
+ "...........********.............",
+ "........***aaaaaaaa***..........",
+ "......**aaaaaaaaaaaaaa**........",
+ ".....*aaaaaaaaaaaaaaaaaa*.......",
+ "....*aaaaaaaabbbbaaaaaaaac......",
+ "...*aaaaaaaabbbbbbaaaaaaaac.....",
+ "..*aaaaaaaaabbbbbbaaaaaaaaac....",
+ ".*aaaaaaaaaaabbbbaaaaaaaaaaac...",
+ ".*aaaaaaaaaaaaaaaaaaaaaaaaaac*..",
+ "*aaaaaaaaaaaaaaaaaaaaaaaaaaaac*.",
+ "*aaaaaaaaaabbbbbbbaaaaaaaaaaac*.",
+ "*aaaaaaaaaaaabbbbbaaaaaaaaaaac**",
+ "*aaaaaaaaaaaabbbbbaaaaaaaaaaac**",
+ "*aaaaaaaaaaaabbbbbaaaaaaaaaaac**",
+ "*aaaaaaaaaaaabbbbbaaaaaaaaaaac**",
+ "*aaaaaaaaaaaabbbbbaaaaaaaaaaac**",
+ ".*aaaaaaaaaaabbbbbaaaaaaaaaac***",
+ ".*aaaaaaaaaaabbbbbaaaaaaaaaac***",
+ "..*aaaaaaaaaabbbbbaaaaaaaaac***.",
+ "...caaaaaaabbbbbbbbbaaaaaac****.",
+ "....caaaaaaaaaaaaaaaaaaaac****..",
+ ".....caaaaaaaaaaaaaaaaaac****...",
+ "......ccaaaaaaaaaaaaaacc****....",
+ ".......*cccaaaaaaaaccc*****.....",
+ "........***cccaaaac*******......",
+ "..........****caaac*****........",
+ ".............*caaac**...........",
+ "...............caac**...........",
+ "................cac**...........",
+ ".................cc**...........",
+ "..................***...........",
+ "...................**..........."};
+/* XPM */
+static const char* const warning_xpm[]={
+ "32 32 4 1",
+ ". c None",
+ "a c #ffff00",
+ "* c #000000",
+ "b c #999999",
+ ".............***................",
+ "............*aaa*...............",
+ "...........*aaaaa*b.............",
+ "...........*aaaaa*bb............",
+ "..........*aaaaaaa*bb...........",
+ "..........*aaaaaaa*bb...........",
+ ".........*aaaaaaaaa*bb..........",
+ ".........*aaaaaaaaa*bb..........",
+ "........*aaaaaaaaaaa*bb.........",
+ "........*aaaa***aaaa*bb.........",
+ ".......*aaaa*****aaaa*bb........",
+ ".......*aaaa*****aaaa*bb........",
+ "......*aaaaa*****aaaaa*bb.......",
+ "......*aaaaa*****aaaaa*bb.......",
+ ".....*aaaaaa*****aaaaaa*bb......",
+ ".....*aaaaaa*****aaaaaa*bb......",
+ "....*aaaaaaaa***aaaaaaaa*bb.....",
+ "....*aaaaaaaa***aaaaaaaa*bb.....",
+ "...*aaaaaaaaa***aaaaaaaaa*bb....",
+ "...*aaaaaaaaaa*aaaaaaaaaa*bb....",
+ "..*aaaaaaaaaaa*aaaaaaaaaaa*bb...",
+ "..*aaaaaaaaaaaaaaaaaaaaaaa*bb...",
+ ".*aaaaaaaaaaaa**aaaaaaaaaaa*bb..",
+ ".*aaaaaaaaaaa****aaaaaaaaaa*bb..",
+ "*aaaaaaaaaaaa****aaaaaaaaaaa*bb.",
+ "*aaaaaaaaaaaaa**aaaaaaaaaaaa*bb.",
+ "*aaaaaaaaaaaaaaaaaaaaaaaaaaa*bbb",
+ "*aaaaaaaaaaaaaaaaaaaaaaaaaaa*bbb",
+ ".*aaaaaaaaaaaaaaaaaaaaaaaaa*bbbb",
+ "..*************************bbbbb",
+ "....bbbbbbbbbbbbbbbbbbbbbbbbbbb.",
+ ".....bbbbbbbbbbbbbbbbbbbbbbbbb.."};
+/* XPM */
+static const char* const critical_xpm[]={
+ "32 32 4 1",
+ ". c None",
+ "a c #999999",
+ "* c #ff0000",
+ "b c #ffffff",
+ "...........********.............",
+ ".........************...........",
+ ".......****************.........",
+ "......******************........",
+ ".....********************a......",
+ "....**********************a.....",
+ "...************************a....",
+ "..*******b**********b*******a...",
+ "..******bbb********bbb******a...",
+ ".******bbbbb******bbbbb******a..",
+ ".*******bbbbb****bbbbb*******a..",
+ "*********bbbbb**bbbbb*********a.",
+ "**********bbbbbbbbbb**********a.",
+ "***********bbbbbbbb***********aa",
+ "************bbbbbb************aa",
+ "************bbbbbb************aa",
+ "***********bbbbbbbb***********aa",
+ "**********bbbbbbbbbb**********aa",
+ "*********bbbbb**bbbbb*********aa",
+ ".*******bbbbb****bbbbb*******aa.",
+ ".******bbbbb******bbbbb******aa.",
+ "..******bbb********bbb******aaa.",
+ "..*******b**********b*******aa..",
+ "...************************aaa..",
+ "....**********************aaa...",
+ "....a********************aaa....",
+ ".....a******************aaa.....",
+ "......a****************aaa......",
+ ".......aa************aaaa.......",
+ ".........aa********aaaaa........",
+ "...........aaaaaaaaaaa..........",
+ ".............aaaaaaa............"};
+/* XPM */
+static const char *const question_xpm[] = {
+ "32 32 5 1",
+ ". c None",
+ "c c #000000",
+ "* c #999999",
+ "a c #ffffff",
+ "b c #0000ff",
+ "...........********.............",
+ "........***aaaaaaaa***..........",
+ "......**aaaaaaaaaaaaaa**........",
+ ".....*aaaaaaaaaaaaaaaaaa*.......",
+ "....*aaaaaaaaaaaaaaaaaaaac......",
+ "...*aaaaaaaabbbbbbaaaaaaaac.....",
+ "..*aaaaaaaabaaabbbbaaaaaaaac....",
+ ".*aaaaaaaabbaaaabbbbaaaaaaaac...",
+ ".*aaaaaaaabbbbaabbbbaaaaaaaac*..",
+ "*aaaaaaaaabbbbaabbbbaaaaaaaaac*.",
+ "*aaaaaaaaaabbaabbbbaaaaaaaaaac*.",
+ "*aaaaaaaaaaaaabbbbaaaaaaaaaaac**",
+ "*aaaaaaaaaaaaabbbaaaaaaaaaaaac**",
+ "*aaaaaaaaaaaaabbaaaaaaaaaaaaac**",
+ "*aaaaaaaaaaaaabbaaaaaaaaaaaaac**",
+ "*aaaaaaaaaaaaaaaaaaaaaaaaaaaac**",
+ ".*aaaaaaaaaaaabbaaaaaaaaaaaac***",
+ ".*aaaaaaaaaaabbbbaaaaaaaaaaac***",
+ "..*aaaaaaaaaabbbbaaaaaaaaaac***.",
+ "...caaaaaaaaaabbaaaaaaaaaac****.",
+ "....caaaaaaaaaaaaaaaaaaaac****..",
+ ".....caaaaaaaaaaaaaaaaaac****...",
+ "......ccaaaaaaaaaaaaaacc****....",
+ ".......*cccaaaaaaaaccc*****.....",
+ "........***cccaaaac*******......",
+ "..........****caaac*****........",
+ ".............*caaac**...........",
+ "...............caac**...........",
+ "................cac**...........",
+ ".................cc**...........",
+ "..................***...........",
+ "...................**...........",
+};
+#endif
+
+/*!
+ \reimp
+*/
+QPixmap
+QMotifStyle::standardPixmap(StandardPixmap standardPixmap, const QStyleOption *opt,
+ const QWidget *widget) const
+{
+#ifndef QT_NO_IMAGEFORMAT_XPM
+ switch (standardPixmap) {
+ case SP_TitleBarMenuButton:
+ return QPixmap(qt_menu_xpm);
+ case SP_TitleBarShadeButton:
+ return QPixmap(qt_shade_xpm);
+ case SP_TitleBarUnshadeButton:
+ return QPixmap(qt_unshade_xpm);
+ case SP_TitleBarNormalButton:
+ return QPixmap(qt_normalizeup_xpm);
+ case SP_TitleBarMinButton:
+ return QPixmap(qt_minimize_xpm);
+ case SP_TitleBarMaxButton:
+ return QPixmap(qt_maximize_xpm);
+ case SP_TitleBarCloseButton:
+ return QPixmap(qt_close_xpm);
+ case SP_DockWidgetCloseButton:
+ return QPixmap(dock_window_close_xpm);
+
+ case SP_MessageBoxInformation:
+ case SP_MessageBoxWarning:
+ case SP_MessageBoxCritical:
+ case SP_MessageBoxQuestion:
+ {
+ const char * const * xpm_data;
+ switch (standardPixmap) {
+ case SP_MessageBoxInformation:
+ xpm_data = information_xpm;
+ break;
+ case SP_MessageBoxWarning:
+ xpm_data = warning_xpm;
+ break;
+ case SP_MessageBoxCritical:
+ xpm_data = critical_xpm;
+ break;
+ case SP_MessageBoxQuestion:
+ xpm_data = question_xpm;
+ break;
+ default:
+ xpm_data = 0;
+ break;
+ }
+ QPixmap pm;
+ if (xpm_data) {
+ QImage image((const char **) xpm_data);
+ // All that color looks ugly in Motif
+ const QPalette &pal = QApplication::palette();
+ switch (standardPixmap) {
+ case SP_MessageBoxInformation:
+ case SP_MessageBoxQuestion:
+ image.setColor(2, 0xff000000 |
+ pal.color(QPalette::Active, QPalette::Dark).rgb());
+ image.setColor(3, 0xff000000 |
+ pal.color(QPalette::Active, QPalette::Base).rgb());
+ image.setColor(4, 0xff000000 |
+ pal.color(QPalette::Active, QPalette::Text).rgb());
+ break;
+ case SP_MessageBoxWarning:
+ image.setColor(1, 0xff000000 |
+ pal.color(QPalette::Active, QPalette::Base).rgb());
+ image.setColor(2, 0xff000000 |
+ pal.color(QPalette::Active, QPalette::Text).rgb());
+ image.setColor(3, 0xff000000 |
+ pal.color(QPalette::Active, QPalette::Dark).rgb());
+ break;
+ case SP_MessageBoxCritical:
+ image.setColor(1, 0xff000000 |
+ pal.color(QPalette::Active, QPalette::Dark).rgb());
+ image.setColor(2, 0xff000000 |
+ pal.color(QPalette::Active, QPalette::Text).rgb());
+ image.setColor(3, 0xff000000 |
+ pal.color(QPalette::Active, QPalette::Base).rgb());
+ break;
+ default:
+ break;
+ }
+ pm = QPixmap::fromImage(image);
+ }
+ return pm;
+ }
+
+ default:
+ break;
+ }
+#endif
+
+ return QCommonStyle::standardPixmap(standardPixmap, opt, widget);
+}
+
+/*! \reimp */
+bool QMotifStyle::event(QEvent *e)
+{
+ if(e->type() == QEvent::FocusIn) {
+ if (QWidget *focusWidget = QApplication::focusWidget()) {
+#ifndef QT_NO_GRAPHICSVIEW
+ if (QGraphicsView *graphicsView = qobject_cast<QGraphicsView *>(focusWidget)) {
+ QGraphicsItem *focusItem = graphicsView->scene() ? graphicsView->scene()->focusItem() : 0;
+ if (focusItem && focusItem->type() == QGraphicsProxyWidget::Type) {
+ QGraphicsProxyWidget *proxy = static_cast<QGraphicsProxyWidget *>(focusItem);
+ if (proxy->widget())
+ focusWidget = proxy->widget()->focusWidget();
+ }
+ }
+#endif
+ if(!focus)
+ focus = new QFocusFrame(focusWidget);
+ focus->setWidget(focusWidget);
+ } else {
+ if(focus)
+ focus->setWidget(0);
+ }
+ } else if(e->type() == QEvent::FocusOut) {
+ if(focus)
+ focus->setWidget(0);
+ }
+ return QCommonStyle::event(e);
+}
+
+
+/*! \reimp */
+int
+QMotifStyle::styleHint(StyleHint hint, const QStyleOption *opt, const QWidget *widget,
+ QStyleHintReturn *returnData) const
+{
+ int ret;
+
+ switch (hint) {
+#ifdef QT3_SUPPORT
+ case SH_GUIStyle:
+ ret = Qt::MotifStyle;
+ break;
+#endif
+ case SH_DrawMenuBarSeparator:
+ ret = true;
+ break;
+
+ case SH_ScrollBar_MiddleClickAbsolutePosition:
+ case SH_Slider_SloppyKeyEvents:
+ case SH_ProgressDialog_CenterCancelButton:
+ case SH_Menu_SpaceActivatesItem:
+ case SH_ScrollView_FrameOnlyAroundContents:
+ case SH_DitherDisabledText:
+ ret = 1;
+ break;
+
+ case SH_Menu_SubMenuPopupDelay:
+ ret = 96;
+ break;
+
+ case SH_ProgressDialog_TextLabelAlignment:
+ ret = Qt::AlignLeft | Qt::AlignVCenter;
+ break;
+
+ case SH_ItemView_ChangeHighlightOnFocus:
+ ret = 0;
+ break;
+
+ case SH_MessageBox_UseBorderForButtonSpacing:
+ ret = 1;
+ break;
+
+ case SH_Dial_BackgroundRole:
+ ret = QPalette::Mid;
+ break;
+
+ case SH_DialogButtonLayout:
+ ret = QDialogButtonBox::KdeLayout;
+ break;
+ case SH_LineEdit_PasswordCharacter:
+ ret = '*';
+ break;
+ default:
+ ret = QCommonStyle::styleHint(hint, opt, widget, returnData);
+ break;
+ }
+
+ return ret;
+}
+
+/*! \reimp */
+QPalette QMotifStyle::standardPalette() const
+{
+#ifdef Q_WS_X11
+ QColor background(0xcf, 0xcf, 0xcf);
+ if (QX11Info::appDepth() <= 8)
+ background = QColor(0xc0, 0xc0, 0xc0);
+#else
+ QColor background = QColor(0xcf, 0xcf, 0xcf);
+#endif
+
+ QColor light = background.lighter();
+ QColor mid = QColor(0xa6, 0xa6, 0xa6);
+ QColor dark = QColor(0x79, 0x7d, 0x79);
+ QPalette palette(Qt::black, background, light, dark, mid, Qt::black, Qt::white);
+ palette.setBrush(QPalette::Disabled, QPalette::WindowText, dark);
+ palette.setBrush(QPalette::Disabled, QPalette::Text, dark);
+ palette.setBrush(QPalette::Disabled, QPalette::ButtonText, dark);
+ palette.setBrush(QPalette::Disabled, QPalette::Base, background);
+ return palette;
+}
+
+QT_END_NAMESPACE
+
+#endif // !defined(QT_NO_STYLE_MOTIF) || defined(QT_PLUGIN)
diff --git a/src/gui/styles/qmotifstyle.h b/src/gui/styles/qmotifstyle.h
new file mode 100644
index 0000000000..e244dba68e
--- /dev/null
+++ b/src/gui/styles/qmotifstyle.h
@@ -0,0 +1,128 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QMOTIFSTYLE_H
+#define QMOTIFSTYLE_H
+
+#include <QtGui/qcommonstyle.h>
+#include <QtCore/qpointer.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+#if !defined(QT_NO_STYLE_MOTIF)
+
+class QPalette;
+class QFocusFrame;
+
+class QMotifStylePrivate;
+class Q_GUI_EXPORT QMotifStyle : public QCommonStyle
+{
+ Q_OBJECT
+public:
+ explicit QMotifStyle(bool useHighlightCols=false);
+ virtual ~QMotifStyle();
+
+ void setUseHighlightColors(bool);
+ bool useHighlightColors() const;
+
+ void polish(QPalette&);
+ void polish(QWidget*);
+ void unpolish(QWidget*);
+ void polish(QApplication*);
+ void unpolish(QApplication*);
+
+ void drawPrimitive(PrimitiveElement pe, const QStyleOption *opt, QPainter *p,
+ const QWidget *w = 0) const;
+
+ void drawControl(ControlElement element, const QStyleOption *opt, QPainter *p,
+ const QWidget *w = 0) const;
+
+ void drawComplexControl(ComplexControl cc, const QStyleOptionComplex *opt, QPainter *p,
+ const QWidget *w = 0) const;
+
+ QRect subControlRect(ComplexControl cc, const QStyleOptionComplex *opt,
+ SubControl sc, const QWidget *widget = 0) const;
+
+ int pixelMetric(PixelMetric metric, const QStyleOption *option = 0,
+ const QWidget *widget = 0) const;
+
+ QSize sizeFromContents(ContentsType ct, const QStyleOption *opt,
+ const QSize &contentsSize, const QWidget *widget = 0) const;
+
+ QRect subElementRect(SubElement r, const QStyleOption *opt, const QWidget *widget = 0) const;
+
+ QPixmap standardPixmap(StandardPixmap standardPixmap, const QStyleOption *opt,
+ const QWidget *widget = 0) const;
+
+ int styleHint(StyleHint hint, const QStyleOption *opt = 0, const QWidget *widget = 0,
+ QStyleHintReturn *returnData = 0) const;
+
+ bool event(QEvent *);
+ QPalette standardPalette() const;
+
+protected Q_SLOTS:
+ QIcon standardIconImplementation(StandardPixmap standardIcon, const QStyleOption *opt = 0,
+ const QWidget *widget = 0) const;
+
+protected:
+ QPointer<QFocusFrame> focus;
+ QMotifStyle(QMotifStylePrivate &dd, bool useHighlightCols = false);
+ void timerEvent(QTimerEvent *event);
+ bool eventFilter(QObject *o, QEvent *e);
+
+private:
+ Q_DECLARE_PRIVATE(QMotifStyle)
+ Q_DISABLE_COPY(QMotifStyle)
+
+ bool highlightCols;
+};
+
+#endif // QT_NO_STYLE_MOTIF
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QMOTIFSTYLE_H
diff --git a/src/gui/styles/qmotifstyle_p.h b/src/gui/styles/qmotifstyle_p.h
new file mode 100644
index 0000000000..b42e0d959c
--- /dev/null
+++ b/src/gui/styles/qmotifstyle_p.h
@@ -0,0 +1,82 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QMOTIFSTYLE_P_H
+#define QMOTIFSTYLE_P_H
+#include <qlist.h>
+#include <qdatetime.h>
+#include <qprogressbar.h>
+#include "qmotifstyle.h"
+#include "qcommonstyle_p.h"
+
+QT_BEGIN_NAMESPACE
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of qapplication_*.cpp, qwidget*.cpp and qfiledialog.cpp. This header
+// file may change from version to version without notice, or even be removed.
+//
+// We mean it.
+//
+
+// Private class
+class QMotifStylePrivate : public QCommonStylePrivate
+{
+ Q_DECLARE_PUBLIC(QMotifStyle)
+public:
+ QMotifStylePrivate();
+
+public:
+#ifndef QT_NO_PROGRESSBAR
+ QList<QProgressBar *> bars;
+ int animationFps;
+ int animateTimer;
+ QTime startTime;
+ int animateStep;
+#endif // QT_NO_PROGRESSBAR
+};
+
+QT_END_NAMESPACE
+
+#endif //QMOTIFSTYLE_P_H
diff --git a/src/gui/styles/qplastiquestyle.cpp b/src/gui/styles/qplastiquestyle.cpp
new file mode 100644
index 0000000000..24d77486ff
--- /dev/null
+++ b/src/gui/styles/qplastiquestyle.cpp
@@ -0,0 +1,6024 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qplastiquestyle.h"
+
+#if !defined(QT_NO_STYLE_PLASTIQUE) || defined(QT_PLUGIN)
+
+static bool UsePixmapCache = true;
+static const bool AnimateBusyProgressBar = true;
+static const bool AnimateProgressBar = false;
+// #define QPlastique_MaskButtons
+static const int ProgressBarFps = 25;
+static const int blueFrameWidth = 2; // with of line edit focus frame
+
+#include "qwindowsstyle_p.h"
+#include <qapplication.h>
+#include <qbitmap.h>
+#include <qabstractitemview.h>
+#include <qcheckbox.h>
+#include <qcombobox.h>
+#include <qdatetime.h>
+#include <qdebug.h>
+#include <qdialogbuttonbox.h>
+#include <qformlayout.h>
+#include <qgroupbox.h>
+#include <qimage.h>
+#include <qlineedit.h>
+#include <qmainwindow.h>
+#include <qmenu.h>
+#include <qmenubar.h>
+#include <qpainter.h>
+#include <qpaintengine.h>
+#include <qpainterpath.h>
+#include <qpalette.h>
+#include <qpen.h>
+#include <qpixmap.h>
+#include <qpixmapcache.h>
+#include <qprogressbar.h>
+#include <qpushbutton.h>
+#include <qradiobutton.h>
+#include <qscrollbar.h>
+#include <qspinbox.h>
+#include <qsplitter.h>
+#include <qstyleoption.h>
+#include <qtextedit.h>
+#include <qtoolbar.h>
+#include <qtoolbox.h>
+#include <qtoolbutton.h>
+#include <qworkspace.h>
+#include <qprocess.h>
+#include <qvarlengtharray.h>
+#include <limits.h>
+
+QT_BEGIN_NAMESPACE
+
+// from windows style
+static const int windowsItemFrame = 2; // menu item frame width
+static const int windowsSepHeight = 2; // separator item height
+static const int windowsItemHMargin = 3; // menu item hor text margin
+static const int windowsItemVMargin = 2; // menu item ver text margin
+static const int windowsArrowHMargin = 6; // arrow horizontal margin
+static const int windowsTabSpacing = 12; // space between text and tab
+static const int windowsCheckMarkHMargin = 2; // horiz. margins of check mark
+static const int windowsRightBorder = 15; // right border on windows
+static const int windowsCheckMarkWidth = 12; // checkmarks width on windows
+
+static const char * const qt_plastique_slider_verticalhandle[] = {
+ "15 11 6 1",
+ " c None",
+ "+ c #979797",
+ "@ c #C9C9C9",
+ "$ c #C1C1C1",
+ "b c None",
+ "d c None",
+ " $++++++++$ ",
+ "$+bbbbbbbb+$ ",
+ "+b $$ +$ ",
+ "+b $@ +$ ",
+ "+b +$",
+ "+b d+",
+ "+b d+$",
+ "+b $$ d+$ ",
+ "+b $@ d+$ ",
+ "$+dddddddd+$ ",
+ " $++++++++$ "};
+
+static const char * const qt_plastique_slider_verticalhandle_left[] = {
+ "15 11 6 1",
+ " c None",
+ "+ c #979797",
+ "@ c #C9C9C9",
+ "$ c #C1C1C1",
+ "b c None",
+ "d c None",
+ " $++++++++$ ",
+ " $+bbbbbbbb+$",
+ " $+b $$ d+",
+ " $+b $@ d+",
+ "$+b d+",
+ "+b d+",
+ "$+ d+",
+ " $+ $$ d+",
+ " $+ $@ d+",
+ " $+dddddddd+$",
+ " $++++++++$ "};
+
+static const char * const qt_plastique_slider_horizontalhandle[] = {
+ "11 15 6 1",
+ " c None",
+ "+ c #979797",
+ "@ c #C9C9C9",
+ "$ c #C1C1C1",
+ "b c None",
+ "d c None",
+ " $+++++++$ ",
+ "$+bbbbbbb+$",
+ "+b d+",
+ "+b$$ $$d+",
+ "+b$@ $@d+",
+ "+b d+",
+ "+b d+",
+ "+b d+",
+ "+b d+",
+ "+b d+",
+ "$+ d+$",
+ " $+ d+$ ",
+ " $+ d+$ ",
+ " $+d+$ ",
+ " $+$ "};
+
+static const char * const qt_plastique_slider_horizontalhandle_up[] = {
+ "11 15 6 1",
+ " c None",
+ "+ c #979797",
+ "@ c #C9C9C9",
+ "$ c #C1C1C1",
+ "b c None",
+ "d c None",
+ " $+$ ",
+ " $+b+$ ",
+ " $+b +$ ",
+ " $+b +$ ",
+ "$+b +$",
+ "+b d+",
+ "+b d+",
+ "+b d+",
+ "+b d+",
+ "+b d+",
+ "+b$$ $$d+",
+ "+b$@ $@d+",
+ "+b d+",
+ "$+ddddddd+$",
+ " $+++++++$ "};
+
+static const char * const qt_scrollbar_button_arrow_left[] = {
+ "4 7 2 1",
+ " c None",
+ "* c #BFBFBF",
+ " *",
+ " **",
+ " ***",
+ "****",
+ " ***",
+ " **",
+ " *"};
+
+static const char * const qt_scrollbar_button_arrow_right[] = {
+ "4 7 2 1",
+ " c None",
+ "* c #BFBFBF",
+ "* ",
+ "** ",
+ "*** ",
+ "****",
+ "*** ",
+ "** ",
+ "* "};
+
+static const char * const qt_scrollbar_button_arrow_up[] = {
+ "7 4 2 1",
+ " c None",
+ "* c #BFBFBF",
+ " * ",
+ " *** ",
+ " ***** ",
+ "*******"};
+
+static const char * const qt_scrollbar_button_arrow_down[] = {
+ "7 4 2 1",
+ " c None",
+ "* c #BFBFBF",
+ "*******",
+ " ***** ",
+ " *** ",
+ " * "};
+
+static const char * const qt_scrollbar_button_left[] = {
+ "16 16 6 1",
+ " c None",
+ ". c #BFBFBF",
+ "+ c #979797",
+ "# c #FAFAFA",
+ "< c #FAFAFA",
+ "* c #FAFAFA",
+ " .+++++++++++++.",
+ ".+#############+",
+ "+# <+",
+ "+# <+",
+ "+# <+",
+ "+# <+",
+ "+# <+",
+ "+# <+",
+ "+# <+",
+ "+# <+",
+ "+# <+",
+ "+# <+",
+ "+# <+",
+ "+# <+",
+ ".+<<<<<<<<<<<<<+",
+ " .+++++++++++++."};
+
+static const char * const qt_scrollbar_button_right[] = {
+ "16 16 6 1",
+ " c None",
+ ". c #BFBFBF",
+ "+ c #979797",
+ "# c #FAFAFA",
+ "< c #FAFAFA",
+ "* c #FAFAFA",
+ ".+++++++++++++. ",
+ "+#############+.",
+ "+# <+",
+ "+# <+",
+ "+# <+",
+ "+# <+",
+ "+# <+",
+ "+# <+",
+ "+# <+",
+ "+# <+",
+ "+# <+",
+ "+# <+",
+ "+# <+",
+ "+# <+",
+ "+<<<<<<<<<<<<<+.",
+ ".+++++++++++++. "};
+
+static const char * const qt_scrollbar_button_up[] = {
+ "16 16 6 1",
+ " c None",
+ ". c #BFBFBF",
+ "+ c #979797",
+ "# c #FAFAFA",
+ "< c #FAFAFA",
+ "* c #FAFAFA",
+ " .++++++++++++. ",
+ ".+############+.",
+ "+# <+",
+ "+# <+",
+ "+# <+",
+ "+# <+",
+ "+# <+",
+ "+# <+",
+ "+# <+",
+ "+# <+",
+ "+# <+",
+ "+# <+",
+ "+# <+",
+ "+# <+",
+ "+<<<<<<<<<<<<<<+",
+ ".++++++++++++++."};
+
+static const char * const qt_scrollbar_button_down[] = {
+ "16 16 6 1",
+ " c None",
+ ". c #BFBFBF",
+ "+ c #979797",
+ "# c #FAFAFA",
+ "< c #FAFAFA",
+ "* c #FAFAFA",
+ "++++++++++++++++",
+ "+##############+",
+ "+# <+",
+ "+# <+",
+ "+# <+",
+ "+# <+",
+ "+# <+",
+ "+# <+",
+ "+# <+",
+ "+# <+",
+ "+# <+",
+ "+# <+",
+ "+# <+",
+ "+# <+",
+ ".+<<<<<<<<<<<<+.",
+ " .++++++++++++. "};
+
+static const char * const qt_scrollbar_slider_pattern_vertical[] = {
+ "10 18 3 1",
+ " c None",
+ ". c #BFBFBF",
+ "+ c #979797",
+ ".. .. ..",
+ ".+ .+ .+",
+ " ",
+ " ",
+ ".. .. ..",
+ ".+ .+ .+",
+ " ",
+ " ",
+ ".. .. ..",
+ ".+ .+ .+",
+ " ",
+ " ",
+ ".. .. ..",
+ ".+ .+ .+",
+ " ",
+ " ",
+ ".. .. ..",
+ ".+ .+ .+"};
+
+static const char * const qt_scrollbar_slider_pattern_horizontal[] = {
+ "18 10 3 1",
+ " c None",
+ ". c #BFBFBF",
+ "+ c #979797",
+ ".. .. .. .. ..",
+ ".+ .+ .+ .+ .+",
+ " ",
+ " ",
+ ".. .. .. .. ..",
+ ".+ .+ .+ .+ .+",
+ " ",
+ " ",
+ ".. .. .. .. ..",
+ ".+ .+ .+ .+ .+"};
+
+static const char * const qt_toolbarhandle[] = {
+ "6 6 4 1",
+ " c None",
+ ". c #C5C5C5",
+ "+ c #EEEEEE",
+ "@ c #FAFAFA",
+ ".. ",
+ ".+@ ",
+ " @@ ",
+ " .. ",
+ " .+@",
+ " @@"};
+
+static const char * const qt_simple_toolbarhandle[] = {
+ "3 3 4 1",
+ " c None",
+ ". c #C5C5C5",
+ "+ c #EEEEEE",
+ "@ c #FAFAFA",
+ ".. ",
+ ".+@",
+ " @@"};
+
+static const char * const qt_titlebar_context_help[] = {
+"27 27 5 1",
+" c None",
+". c #0A0C12",
+"+ c #1B202D",
+"@ c #293144",
+"# c #3C435D",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" +@##@+ ",
+" .@@@.+@@.. ",
+" .##+ +@@+. ",
+" .##@ @#@+. ",
+" .... +@+.. ",
+" .@+@@.. ",
+" +#@@+ ",
+" .##. ",
+" .++. ",
+" .++. ",
+" +##+ ",
+" .@@. ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" "};
+
+static QLinearGradient qMapGradientToRect(const QLinearGradient &gradient, const QRectF &rect)
+{
+ QLinearGradient tmpGrad(rect.center().x(), rect.top(),
+ rect.center().x(), rect.bottom());
+ tmpGrad.setStops(gradient.stops());
+ return tmpGrad;
+}
+
+static QBrush qMapBrushToRect(const QBrush &brush, const QRectF &rect)
+{
+ if (!brush.gradient())
+ return brush;
+
+ // ### Ugly assumption that it's a linear gradient
+ QBrush tmp(qMapGradientToRect(*static_cast<const QLinearGradient *>(brush.gradient()), rect));
+ return tmp;
+}
+
+static void qBrushSetAlphaF(QBrush *brush, qreal alpha)
+{
+ if (const QGradient *gradient = brush->gradient()) {
+ // Use the gradient. Call QColor::setAlphaF() on all color stops.
+ QGradientStops stops = gradient->stops();
+ QMutableVectorIterator<QGradientStop> it(stops);
+ QColor tmpColor;
+ while (it.hasNext()) {
+ it.next();
+ tmpColor = it.value().second;
+ tmpColor.setAlphaF(alpha * tmpColor.alphaF());
+ it.setValue(QPair<qreal, QColor>(it.value().first, tmpColor));
+ }
+
+ switch (gradient->type()) {
+ case QGradient::RadialGradient: {
+ QRadialGradient grad = *static_cast<const QRadialGradient *>(gradient);
+ grad.setStops(stops);
+ *brush = QBrush(grad);
+ break;
+ }
+ case QGradient::ConicalGradient: {
+ QConicalGradient grad = *static_cast<const QConicalGradient *>(gradient);
+ grad.setStops(stops);
+ *brush = QBrush(grad);
+ break;
+ }
+ default:
+ qWarning("QPlastiqueStyle::qBrushLight() - unknown gradient type"
+ " - falling back to QLinearGradient");
+ case QGradient::LinearGradient: {
+ QLinearGradient grad = *static_cast<const QLinearGradient *>(gradient);
+ grad.setStops(stops);
+ *brush = QBrush(grad);
+ break;
+ }
+ }
+ } else if (!brush->texture().isNull()) {
+ // Modify the texture - ridiculously expensive.
+ QPixmap texture = brush->texture();
+ QPixmap pixmap;
+ QString name = QString::fromLatin1("qbrushtexture-alpha-%1-%2").arg(alpha).arg(texture.cacheKey());
+ if (UsePixmapCache && !QPixmapCache::find(name, pixmap)) {
+ QImage image = texture.toImage();
+ QRgb *rgb = reinterpret_cast<QRgb *>(image.bits());
+ int pixels = image.width() * image.height();
+ QColor tmpColor;
+ while (pixels--) {
+ tmpColor.setRgb(*rgb);
+ tmpColor.setAlphaF(alpha * tmpColor.alphaF());
+ *rgb++ = tmpColor.rgba();
+ }
+ pixmap = QPixmap::fromImage(image);
+ QPixmapCache::insert(name, pixmap);
+ }
+ brush->setTexture(pixmap);
+ } else {
+ // Use the color
+ QColor tmpColor = brush->color();
+ tmpColor.setAlphaF(alpha * tmpColor.alphaF());
+ brush->setColor(tmpColor);
+ }
+}
+
+static QBrush qBrushLight(QBrush brush, int light)
+{
+ if (const QGradient *gradient = brush.gradient()) {
+ // Use the gradient. Call QColor::lighter() on all color stops.
+ QGradientStops stops = gradient->stops();
+ QMutableVectorIterator<QGradientStop> it(stops);
+ while (it.hasNext()) {
+ it.next();
+ it.setValue(QPair<qreal, QColor>(it.value().first, it.value().second.lighter(light)));
+ }
+
+ switch (gradient->type()) {
+ case QGradient::RadialGradient: {
+ QRadialGradient grad = *static_cast<const QRadialGradient *>(gradient);
+ grad.setStops(stops);
+ brush = QBrush(grad);
+ break;
+ }
+ case QGradient::ConicalGradient: {
+ QConicalGradient grad = *static_cast<const QConicalGradient *>(gradient);
+ grad.setStops(stops);
+ brush = QBrush(grad);
+ break;
+ }
+ default:
+ qWarning("QPlastiqueStyle::qBrushLight() - unknown gradient type"
+ " - falling back to QLinearGradient");
+ case QGradient::LinearGradient: {
+ QLinearGradient grad = *static_cast<const QLinearGradient *>(gradient);
+ grad.setStops(stops);
+ brush = QBrush(grad);
+ break;
+ }
+ }
+ } else if (!brush.texture().isNull()) {
+ // Modify the texture - ridiculously expensive.
+ QPixmap texture = brush.texture();
+ QPixmap pixmap;
+ QString name = QString::fromLatin1("qbrushtexture-light-%1-%2").arg(light).arg(texture.cacheKey());
+ if (UsePixmapCache && !QPixmapCache::find(name, pixmap)) {
+ QImage image = texture.toImage();
+ QRgb *rgb = reinterpret_cast<QRgb *>(image.bits());
+ int pixels = image.width() * image.height();
+ QColor tmpColor;
+ while (pixels--) {
+ tmpColor.setRgb(*rgb);
+ *rgb++ = tmpColor.lighter(light).rgba();
+ }
+ pixmap = QPixmap::fromImage(image);
+ QPixmapCache::insert(name, pixmap);
+ }
+ brush.setTexture(pixmap);
+ } else {
+ // Use the color
+ brush.setColor(brush.color().lighter(light));
+ }
+ return brush;
+}
+
+static QBrush qBrushDark(QBrush brush, int dark)
+{
+ if (const QGradient *gradient = brush.gradient()) {
+ // Use the gradient. Call QColor::darker() on all color stops.
+ QGradientStops stops = gradient->stops();
+ QMutableVectorIterator<QGradientStop> it(stops);
+ while (it.hasNext()) {
+ it.next();
+ it.setValue(QPair<qreal, QColor>(it.value().first, it.value().second.darker(dark)));
+ }
+
+ switch (gradient->type()) {
+ case QGradient::RadialGradient: {
+ QRadialGradient grad = *static_cast<const QRadialGradient *>(gradient);
+ grad.setStops(stops);
+ brush = QBrush(grad);
+ break;
+ }
+ case QGradient::ConicalGradient: {
+ QConicalGradient grad = *static_cast<const QConicalGradient *>(gradient);
+ grad.setStops(stops);
+ brush = QBrush(grad);
+ break;
+ }
+ default:
+ qWarning("QPlastiqueStyle::qBrushDark() - unknown gradient type"
+ " - falling back to QLinearGradient");
+ case QGradient::LinearGradient: {
+ QLinearGradient grad = *static_cast<const QLinearGradient *>(gradient);
+ grad.setStops(stops);
+ brush = QBrush(grad);
+ break;
+ }
+ }
+ } else if (!brush.texture().isNull()) {
+ // Modify the texture - ridiculously expensive.
+ QPixmap texture = brush.texture();
+ QPixmap pixmap;
+ QString name = QString::fromLatin1("qbrushtexture-dark-%1-%2").arg(dark).arg(brush.texture().cacheKey());
+ if (UsePixmapCache && !QPixmapCache::find(name, pixmap)) {
+ QImage image = texture.toImage();
+ QRgb *rgb = reinterpret_cast<QRgb *>(image.bits());
+ int pixels = image.width() * image.height();
+ QColor tmpColor;
+ while (pixels--) {
+ tmpColor.setRgb(*rgb);
+ *rgb++ = tmpColor.darker(dark).rgba();
+ }
+ pixmap = QPixmap::fromImage(image);
+ QPixmapCache::insert(name, pixmap);
+ }
+ brush.setTexture(pixmap);
+ } else {
+ // Use the color
+ brush.setColor(brush.color().darker(dark));
+ }
+ return brush;
+}
+
+/*
+ Draws a rounded frame using the provided brush for 1, and adds 0.5 alpha
+ for 0.
+
+ 0111111110
+ 01 10
+ 1 1
+ 1 1
+ 1 1
+ 01 10
+ 0111111110
+*/
+static void qt_plastique_draw_frame(QPainter *painter, const QRect &rect, const QStyleOption *option,
+ QFrame::Shadow shadow = QFrame::Plain)
+{
+ QPen oldPen = painter->pen();
+ QBrush border;
+ QBrush corner;
+ QBrush innerTopLeft;
+ QBrush innerBottomRight;
+
+ if (shadow != QFrame::Plain && (option->state & QStyle::State_HasFocus)) {
+ border = option->palette.highlight();
+ qBrushSetAlphaF(&border, qreal(0.8));
+ corner = option->palette.highlight();
+ qBrushSetAlphaF(&corner, 0.5);
+ innerTopLeft = qBrushDark(option->palette.highlight(), 125);
+ innerBottomRight = option->palette.highlight();
+ qBrushSetAlphaF(&innerBottomRight, qreal(0.65));
+ } else {
+ border = option->palette.shadow();
+ qBrushSetAlphaF(&border, qreal(0.4));
+ corner = option->palette.shadow();
+ qBrushSetAlphaF(&corner, 0.25);
+ innerTopLeft = option->palette.shadow();
+ innerBottomRight = option->palette.shadow();
+ if (shadow == QFrame::Sunken) {
+ qBrushSetAlphaF(&innerTopLeft, qreal(0.23));
+ qBrushSetAlphaF(&innerBottomRight, qreal(0.075));
+ } else {
+ qBrushSetAlphaF(&innerTopLeft, qreal(0.075));
+ qBrushSetAlphaF(&innerBottomRight, qreal(0.23));
+ }
+ }
+
+ QLine lines[4];
+ QPoint points[8];
+
+ // Opaque corner lines
+ painter->setPen(QPen(border, 0));
+ lines[0] = QLine(rect.left() + 2, rect.top(), rect.right() - 2, rect.top());
+ lines[1] = QLine(rect.left() + 2, rect.bottom(), rect.right() - 2, rect.bottom());
+ lines[2] = QLine(rect.left(), rect.top() + 2, rect.left(), rect.bottom() - 2);
+ lines[3] = QLine(rect.right(), rect.top() + 2, rect.right(), rect.bottom() - 2);
+ painter->drawLines(lines, 4);
+
+ // Opaque corner dots
+ points[0] = QPoint(rect.left() + 1, rect.top() + 1);
+ points[1] = QPoint(rect.left() + 1, rect.bottom() - 1);
+ points[2] = QPoint(rect.right() - 1, rect.top() + 1);
+ points[3] = QPoint(rect.right() - 1, rect.bottom() - 1);
+ painter->drawPoints(points, 4);
+
+ // Shaded corner dots
+ painter->setPen(QPen(corner, 0));
+ points[0] = QPoint(rect.left(), rect.top() + 1);
+ points[1] = QPoint(rect.left(), rect.bottom() - 1);
+ points[2] = QPoint(rect.left() + 1, rect.top());
+ points[3] = QPoint(rect.left() + 1, rect.bottom());
+ points[4] = QPoint(rect.right(), rect.top() + 1);
+ points[5] = QPoint(rect.right(), rect.bottom() - 1);
+ points[6] = QPoint(rect.right() - 1, rect.top());
+ points[7] = QPoint(rect.right() - 1, rect.bottom());
+ painter->drawPoints(points, 8);
+
+ // Shadows
+ if (shadow != QFrame::Plain) {
+ painter->setPen(QPen(innerTopLeft, 0));
+ lines[0] = QLine(rect.left() + 2, rect.top() + 1, rect.right() - 2, rect.top() + 1);
+ lines[1] = QLine(rect.left() + 1, rect.top() + 2, rect.left() + 1, rect.bottom() - 2);
+ painter->drawLines(lines, 2);
+ painter->setPen(QPen(innerBottomRight, 0));
+ lines[0] = QLine(rect.left() + 2, rect.bottom() - 1, rect.right() - 2, rect.bottom() - 1);
+ lines[1] = QLine(rect.right() - 1, rect.top() + 2, rect.right() - 1, rect.bottom() - 2);
+ painter->drawLines(lines, 2);
+ }
+
+ painter->setPen(oldPen);
+}
+
+static QColor mergedColors(const QColor &colorA, const QColor &colorB, int factor = 50)
+{
+ const int maxFactor = 100;
+ QColor tmp = colorA;
+ tmp.setRed((tmp.red() * factor) / maxFactor + (colorB.red() * (maxFactor - factor)) / maxFactor);
+ tmp.setGreen((tmp.green() * factor) / maxFactor + (colorB.green() * (maxFactor - factor)) / maxFactor);
+ tmp.setBlue((tmp.blue() * factor) / maxFactor + (colorB.blue() * (maxFactor - factor)) / maxFactor);
+ return tmp;
+}
+
+static QString uniqueName(const QString &key, const QStyleOption *option, const QSize &size)
+{
+ QString tmp;
+ const QStyleOptionComplex *complexOption = qstyleoption_cast<const QStyleOptionComplex *>(option);
+ tmp.sprintf("%s-%d-%d-%d-%lld-%dx%d", key.toLatin1().constData(), uint(option->state),
+ option->direction, complexOption ? uint(complexOption->activeSubControls) : uint(0),
+ option->palette.cacheKey(), size.width(), size.height());
+ return tmp;
+}
+
+static void qt_plastique_draw_gradient(QPainter *painter, const QRect &rect, const QColor &gradientStart,
+ const QColor &gradientStop)
+{
+ QString gradientName;
+ gradientName.sprintf("%dx%d-%x-%x", rect.width(), rect.height(), gradientStart.rgba(), gradientStop.rgba());
+ QPixmap cache;
+ QPainter *p = painter;
+ QRect r = rect;
+
+ bool doPixmapCache = UsePixmapCache
+ && painter->deviceTransform().isIdentity()
+ && painter->worldMatrix().isIdentity();
+ if (doPixmapCache && QPixmapCache::find(gradientName, cache)) {
+ painter->drawPixmap(rect, cache);
+ } else {
+ if (doPixmapCache) {
+ cache = QPixmap(rect.size());
+ cache.fill(Qt::transparent);
+ p = new QPainter(&cache);
+ r = QRect(0, 0, rect.width(), rect.height());
+ }
+
+ int x = r.center().x();
+ QLinearGradient gradient(x, r.top(), x, r.bottom());
+ gradient.setColorAt(0, gradientStart);
+ gradient.setColorAt(1, gradientStop);
+ p->fillRect(r, gradient);
+
+ if (doPixmapCache) {
+ p->end();
+ delete p;
+ painter->drawPixmap(rect, cache);
+ QPixmapCache::insert(gradientName, cache);
+ }
+ }
+}
+
+static void qt_plastique_drawFrame(QPainter *painter, const QStyleOption *option, const QWidget *widget)
+{
+ QRect rect = option->rect;
+ QPen oldPen = painter->pen();
+
+ QColor borderColor = option->palette.background().color().darker(178);
+ QColor gradientStartColor = option->palette.button().color().lighter(104);
+ QColor gradientStopColor = option->palette.button().color().darker(105);
+ QColor alphaCornerColor;
+ if (widget) {
+ // ### backgroundrole/foregroundrole should be part of the style option
+ alphaCornerColor = mergedColors(option->palette.color(widget->backgroundRole()), borderColor);
+ } else {
+ alphaCornerColor = mergedColors(option->palette.background().color(), borderColor);
+ }
+
+ QLine lines[4];
+ QPoint points[8];
+
+ // outline / border
+ painter->setPen(borderColor);
+ lines[0] = QLine(rect.left() + 2, rect.top(), rect.right() - 2, rect.top());
+ lines[1] = QLine(rect.left() + 2, rect.bottom(), rect.right() - 2, rect.bottom());
+ lines[2] = QLine(rect.left(), rect.top() + 2, rect.left(), rect.bottom() - 2);
+ lines[3] = QLine(rect.right(), rect.top() + 2, rect.right(), rect.bottom() - 2);
+ painter->drawLines(lines, 4);
+
+ points[0] = QPoint(rect.left() + 1, rect.top() + 1);
+ points[1] = QPoint(rect.right() - 1, rect.top() + 1);
+ points[2] = QPoint(rect.left() + 1, rect.bottom() - 1);
+ points[3] = QPoint(rect.right() - 1, rect.bottom() - 1);
+ painter->drawPoints(points, 4);
+
+ painter->setPen(alphaCornerColor);
+
+ points[0] = QPoint(rect.left() + 1, rect.top());
+ points[1] = QPoint(rect.right() - 1, rect.top());
+ points[2] = QPoint(rect.left() + 1, rect.bottom());
+ points[3] = QPoint(rect.right() - 1, rect.bottom());
+ points[4] = QPoint(rect.left(), rect.top() + 1);
+ points[5] = QPoint(rect.right(), rect.top() + 1);
+ points[6] = QPoint(rect.left(), rect.bottom() - 1);
+ points[7] = QPoint(rect.right(), rect.bottom() - 1);
+ painter->drawPoints(points, 8);
+
+ // inner border
+ if ((option->state & QStyle::State_Sunken) || (option->state & QStyle::State_On))
+ painter->setPen(option->palette.button().color().darker(118));
+ else
+ painter->setPen(gradientStartColor);
+
+ lines[0] = QLine(rect.left() + 2, rect.top() + 1, rect.right() - 2, option->rect.top() + 1);
+ lines[1] = QLine(rect.left() + 1, rect.top() + 2, rect.left() + 1, option->rect.bottom() - 2);
+ painter->drawLines(lines, 2);
+
+ if ((option->state & QStyle::State_Sunken) || (option->state & QStyle::State_On))
+ painter->setPen(option->palette.button().color().darker(110));
+ else
+ painter->setPen(gradientStopColor.darker(102));
+
+ lines[0] = QLine(rect.left() + 2, rect.bottom() - 1, rect.right() - 2, rect.bottom() - 1);
+ lines[1] = QLine(rect.right() - 1, rect.top() + 2, rect.right() - 1, rect.bottom() - 2);
+ painter->drawLines(lines, 2);
+
+ painter->setPen(oldPen);
+}
+
+static void qt_plastique_drawShadedPanel(QPainter *painter, const QStyleOption *option, bool base,
+ const QWidget *widget)
+{
+ QRect rect = option->rect;
+ QPen oldPen = painter->pen();
+
+ QColor gradientStartColor = option->palette.button().color().lighter(104);
+ QColor gradientStopColor = option->palette.button().color().darker(105);
+
+ // gradient fill
+ if ((option->state & QStyle::State_Enabled) || !(option->state & QStyle::State_AutoRaise)) {
+ if ((option->state & QStyle::State_Sunken) || (option->state & QStyle::State_On)) {
+ qt_plastique_draw_gradient(painter, rect.adjusted(1, 1, -1, -1),
+ option->palette.button().color().darker(114),
+ option->palette.button().color().darker(106));
+ } else {
+ qt_plastique_draw_gradient(painter, rect.adjusted(1, 1, -1, -1),
+ base ? option->palette.background().color().lighter(105) : gradientStartColor,
+ base ? option->palette.background().color().darker(102) : gradientStopColor);
+ }
+ }
+
+ qt_plastique_drawFrame(painter, option, widget);
+
+ painter->setPen(oldPen);
+}
+
+static void qt_plastique_draw_mdibutton(QPainter *painter, const QStyleOptionTitleBar *option, const QRect &tmp, bool hover, bool sunken)
+{
+ if (tmp.isNull())
+ return;
+ bool active = (option->titleBarState & QStyle::State_Active);
+
+ // ### use palette colors instead
+ QColor mdiButtonGradientStartColor;
+ QColor mdiButtonGradientStopColor;
+ if (active) {
+ mdiButtonGradientStartColor = QColor((hover || sunken) ? 0x7d8bb1 : 0x55689a);
+ mdiButtonGradientStopColor = QColor((hover || sunken) ? 0x939ebe : 0x7381ab);
+ } else {
+ mdiButtonGradientStartColor = QColor((hover || sunken) ? 0x9e9e9e : 0x818181);
+ mdiButtonGradientStopColor = QColor((hover || sunken) ? 0xababab : 0x929292);
+ }
+
+ qt_plastique_draw_gradient(painter, tmp.adjusted(1, 1, -1, -1),
+ mdiButtonGradientStartColor, mdiButtonGradientStopColor);
+
+ QColor mdiButtonBorderColor;
+ if (active) {
+ mdiButtonBorderColor = (hover || sunken) ? QColor(0x627097) : QColor(0x324577);
+ } else {
+ mdiButtonBorderColor = (hover || sunken) ? QColor(0x838383) : QColor(0x5e5e5e);
+ }
+ painter->setPen(QPen(mdiButtonBorderColor, 1));
+
+ const QLine lines[4] = {
+ QLine(tmp.left() + 2, tmp.top(), tmp.right() - 2, tmp.top()),
+ QLine(tmp.left() + 2, tmp.bottom(), tmp.right() - 2, tmp.bottom()),
+ QLine(tmp.left(), tmp.top() + 2, tmp.left(), tmp.bottom() - 2),
+ QLine(tmp.right(), tmp.top() + 2, tmp.right(), tmp.bottom() - 2) };
+ painter->drawLines(lines, 4);
+
+ const QPoint points[4] = {
+ QPoint(tmp.left() + 1, tmp.top() + 1),
+ QPoint(tmp.right() - 1, tmp.top() + 1),
+ QPoint(tmp.left() + 1, tmp.bottom() - 1),
+ QPoint(tmp.right() - 1, tmp.bottom() - 1) };
+ painter->drawPoints(points, 4);
+}
+
+#ifndef QT_NO_DOCKWIDGET
+static QString elliditide(const QString &text, const QFontMetrics &fontMetrics, const QRect &rect, int *textWidth = 0)
+{
+ // Chop and insert ellide into title if text is too wide
+ QString title = text;
+ int width = textWidth ? *textWidth : fontMetrics.width(text);
+ QString ellipsis = QLatin1String("...");
+ if (width > rect.width()) {
+ QString leftHalf = title.left(title.size() / 2);
+ QString rightHalf = title.mid(leftHalf.size() + 1);
+ while (!leftHalf.isEmpty() && !rightHalf.isEmpty()) {
+ leftHalf.chop(1);
+ int width = fontMetrics.width(leftHalf + ellipsis + rightHalf);
+ if (width < rect.width()) {
+ title = leftHalf + ellipsis + rightHalf;
+ width = width;
+ break;
+ }
+ rightHalf.remove(0, 1);
+ width = fontMetrics.width(leftHalf + ellipsis + rightHalf);
+ if (width < rect.width()) {
+ title = leftHalf + ellipsis + rightHalf;
+ width = width;
+ break;
+ }
+ }
+ }
+ if (textWidth)
+ *textWidth = width;
+ return title;
+}
+#endif
+
+#if !defined(QT_NO_DOCKWIDGET) || !defined(QT_NO_SPLITTER)
+static void qt_plastique_draw_handle(QPainter *painter, const QStyleOption *option,
+ const QRect &rect, Qt::Orientation orientation,
+ const QWidget *widget)
+{
+ QColor borderColor = option->palette.background().color().darker(178);
+ QColor alphaCornerColor;
+ if (widget) {
+ // ### backgroundrole/foregroundrole should be part of the style option
+ alphaCornerColor = mergedColors(option->palette.color(widget->backgroundRole()), borderColor);
+ } else {
+ alphaCornerColor = mergedColors(option->palette.background().color(), borderColor);
+ }
+ QImage handle(qt_simple_toolbarhandle);
+ alphaCornerColor.setAlpha(170);
+ handle.setColor(1, alphaCornerColor.rgba());
+ handle.setColor(2, mergedColors(alphaCornerColor, option->palette.light().color()).rgba());
+ handle.setColor(3, option->palette.light().color().rgba());
+
+ const int spacing = 2;
+
+ if (orientation == Qt::Vertical) {
+ int nchunks = rect.width() / (handle.width() + spacing);
+ for (int i = 0; i < nchunks; ++i)
+ painter->drawImage(QPoint(rect.left() + i * (handle.width() + spacing), rect.top()), handle);
+ } else {
+ int nchunks = rect.height() / (handle.height() + spacing);
+ for (int i = 0; i < nchunks; ++i)
+ painter->drawImage(QPoint(rect.left(), rect.top() + i * (handle.height() + spacing)), handle);
+ }
+}
+#endif
+
+class QPlastiqueStylePrivate : public QWindowsStylePrivate
+{
+ Q_DECLARE_PUBLIC(QPlastiqueStyle)
+public:
+ QPlastiqueStylePrivate();
+ virtual ~QPlastiqueStylePrivate();
+ void drawPartialFrame(QPainter *painter, const QStyleOptionComplex *option,
+ const QRect &rect, const QWidget *widget) const;
+
+#ifndef QT_NO_PROGRESSBAR
+ QList<QProgressBar *> bars;
+ int progressBarAnimateTimer;
+ QTime timer;
+#endif
+};
+
+/*!
+ \internal
+ */
+QPlastiqueStylePrivate::QPlastiqueStylePrivate() :
+ QWindowsStylePrivate()
+#ifndef QT_NO_PROGRESSBAR
+ , progressBarAnimateTimer(0)
+#endif
+{
+ if (!qgetenv("QT_STYLE_NO_PIXMAPCACHE").isNull())
+ UsePixmapCache = false;
+}
+
+/*!
+ \internal
+ */
+QPlastiqueStylePrivate::~QPlastiqueStylePrivate()
+{
+}
+
+/*!
+ \class QPlastiqueStyle
+ \brief The QPlastiqueStyle class provides a widget style similar to the
+ Plastik style available in KDE.
+
+ The Plastique style provides a default look and feel for widgets on X11
+ that closely resembles the Plastik style, introduced by Sandro Giessl in
+ KDE 3.2.
+
+ \img qplastiquestyle.png
+ \sa QWindowsXPStyle, QMacStyle, QWindowsStyle, QCDEStyle, QMotifStyle
+*/
+
+/*!
+ Constructs a QPlastiqueStyle object.
+*/
+QPlastiqueStyle::QPlastiqueStyle()
+ : QWindowsStyle(*new QPlastiqueStylePrivate)
+{
+ setObjectName(QLatin1String("Plastique"));
+}
+
+/*!
+ Destructs the QPlastiqueStyle object.
+*/
+QPlastiqueStyle::~QPlastiqueStyle()
+{
+}
+
+/*
+ Used by spin- and combo box.
+ Draws a rounded frame around rect but omits the right hand edge
+*/
+void QPlastiqueStylePrivate::drawPartialFrame(QPainter *painter, const QStyleOptionComplex *option,
+ const QRect &rect, const QWidget *widget) const
+{
+ Q_Q(const QPlastiqueStyle);
+ bool reverse = option->direction == Qt::RightToLeft;
+ QStyleOptionFrame frameOpt;
+#ifndef QT_NO_LINEEDIT
+ if (QLineEdit *lineedit = qFindChild<QLineEdit *>(widget))
+ frameOpt.initFrom(lineedit);
+#else
+ Q_UNUSED(widget)
+#endif // QT_NO_LINEEDIT
+
+ frameOpt.rect = rect;
+ painter->save();
+ frameOpt.rect.adjust(-blueFrameWidth + (reverse ? 1 : 0), -blueFrameWidth,
+ blueFrameWidth + (reverse ? 0 : -1), blueFrameWidth);
+ painter->setClipRect(frameOpt.rect);
+ frameOpt.rect.adjust(reverse ? -2 : 0, 0, reverse ? 0 : 2, 0);
+ frameOpt.lineWidth = q->pixelMetric(QStyle::PM_DefaultFrameWidth);
+ frameOpt.midLineWidth = 0;
+ frameOpt.state |= QStyle::State_Sunken;
+ q->drawPrimitive(QStyle::PE_PanelLineEdit, &frameOpt, painter, widget);
+ painter->restore();
+
+ // Draw a two pixel highlight on the flat edge
+ if (option->state & QStyle::State_HasFocus) {
+ painter->setPen(QPen(option->palette.highlight(), 0));
+ QBrush focusBorder = option->palette.highlight();
+ qBrushSetAlphaF(&focusBorder, qreal(0.65));
+ if (!reverse) {
+ painter->drawLine(rect.topRight() + QPoint(1, -1),
+ rect.bottomRight() + QPoint(1, 1));
+ painter->setPen(QPen(focusBorder, 0));
+ painter->drawLine(rect.topRight(),
+ rect.bottomRight());
+ }
+ else {
+ painter->drawLine(rect.topLeft() + QPoint(-1, -1),
+ rect.bottomLeft() + QPoint(-1, 1));
+ painter->setPen(QPen(focusBorder, 0));
+ painter->drawLine(rect.topLeft(),
+ rect.bottomLeft());
+ }
+ }
+}
+
+/*!
+ \reimp
+*/
+void QPlastiqueStyle::drawPrimitive(PrimitiveElement element, const QStyleOption *option,
+ QPainter *painter, const QWidget *widget) const
+{
+ Q_ASSERT(option);
+
+ QColor borderColor = option->palette.background().color().darker(178);
+ QColor gradientStartColor = option->palette.button().color().lighter(104);
+ QColor gradientStopColor = option->palette.button().color().darker(105);
+ QColor baseGradientStartColor = option->palette.base().color().darker(101);
+ QColor baseGradientStopColor = option->palette.base().color().darker(106);
+ QColor highlightedGradientStartColor = option->palette.button().color().lighter(101);
+ QColor highlightedGradientStopColor = mergedColors(option->palette.button().color(), option->palette.highlight().color(), 85);
+ QColor highlightedBaseGradientStartColor = option->palette.base().color();
+ QColor highlightedBaseGradientStopColor = mergedColors(option->palette.base().color().darker(105), option->palette.highlight().color(), 70);
+ QColor highlightedDarkInnerBorderColor = mergedColors(option->palette.button().color(), option->palette.highlight().color(), 35);
+ QColor highlightedLightInnerBorderColor = mergedColors(option->palette.button().color(), option->palette.highlight().color(), 58);
+ QColor alphaCornerColor;
+ if (widget) {
+ // ### backgroundrole/foregroundrole should be part of the style option
+ alphaCornerColor = mergedColors(option->palette.color(widget->backgroundRole()), borderColor);
+ } else {
+ alphaCornerColor = mergedColors(option->palette.background().color(), borderColor);
+ }
+ QColor alphaInnerColor = mergedColors(highlightedLightInnerBorderColor, gradientStartColor);
+ QColor alphaInnerColorNoHover = mergedColors(borderColor, gradientStartColor);
+ QColor alphaTextColor = mergedColors(option->palette.background().color(), option->palette.text().color());
+ QColor alphaLightTextColor = mergedColors(option->palette.background().color().lighter(250), option->palette.text().color().lighter(250));
+ QColor lightShadow = option->palette.button().color().lighter(105);
+ QColor shadowGradientStartColor = option->palette.button().color().darker(115);
+ QColor shadow = shadowGradientStartColor;
+
+ switch (element) {
+ case PE_IndicatorButtonDropDown:
+ drawPrimitive(PE_PanelButtonTool, option, painter, widget);
+ break;
+ case PE_FrameDefaultButton: {
+ if (!(option->state & QStyle::State_Enabled))
+ break;
+ painter->setPen(QPen(QColor(0, 0, 0, 127), 0));
+ const QLine lines[4] = {
+ QLine(option->rect.left() + 2, option->rect.top(),
+ option->rect.right() - 2, option->rect.top()),
+ QLine(option->rect.left() + 2, option->rect.bottom(),
+ option->rect.right() - 2, option->rect.bottom()),
+ QLine(option->rect.left(), option->rect.top() + 2,
+ option->rect.left(), option->rect.bottom() - 2),
+ QLine(option->rect.right(), option->rect.top() + 2,
+ option->rect.right(), option->rect.bottom() - 2) };
+ painter->drawLines(lines, 4);
+
+ QPoint points[8];
+ points[0] = QPoint(option->rect.left() + 1, option->rect.top() + 1);
+ points[1] = QPoint(option->rect.right() - 1, option->rect.top() + 1);
+ points[2] = QPoint(option->rect.left() + 1, option->rect.bottom() - 1);
+ points[3] = QPoint(option->rect.right() - 1, option->rect.bottom() - 1);
+ painter->drawPoints(points, 4);
+
+ painter->setPen(QPen(QColor(0, 0, 0, 63), 0));
+ points[0] = QPoint(option->rect.left() + 1, option->rect.top());
+ points[1] = QPoint(option->rect.right() - 1, option->rect.top());
+ points[2] = QPoint(option->rect.left(), option->rect.top() + 1);
+ points[3] = QPoint(option->rect.right(), option->rect.top() + 1);
+ points[4] = QPoint(option->rect.left() + 1, option->rect.bottom());
+ points[5] = QPoint(option->rect.right() - 1, option->rect.bottom());
+ points[6] = QPoint(option->rect.left(), option->rect.bottom() - 1);
+ points[7] = QPoint(option->rect.right(), option->rect.bottom() - 1);
+ painter->drawPoints(points, 8);
+
+ break;
+ }
+#ifndef QT_NO_TABWIDGET
+ case PE_FrameTabWidget:
+ if (const QStyleOptionTabWidgetFrame *twf = qstyleoption_cast<const QStyleOptionTabWidgetFrame *>(option)) {
+ if (twf->shape != QTabBar::RoundedNorth && twf->shape != QTabBar::RoundedWest &&
+ twf->shape != QTabBar::RoundedSouth && twf->shape != QTabBar::RoundedEast) {
+ QWindowsStyle::drawPrimitive(element, option, painter, widget);
+ break;
+ }
+
+ int borderThickness = pixelMetric(PM_TabBarBaseOverlap, twf, widget);
+ bool reverse = (twf->direction == Qt::RightToLeft);
+
+ painter->save();
+
+ // Start by filling the contents of the tab widget frame (which is
+ // actually a panel).
+ painter->fillRect(option->rect.adjusted(1, 1, -1, -1), option->palette.window());
+
+ QRect tabBarRect;
+ switch (twf->shape) {
+ case QTabBar::RoundedNorth:
+ if (reverse)
+ tabBarRect = QRect(twf->rect.right() - twf->leftCornerWidgetSize.width() - twf->tabBarSize.width() + 1, twf->rect.top(), twf->tabBarSize.width(), borderThickness);
+ else
+ tabBarRect = QRect(twf->rect.left() + twf->leftCornerWidgetSize.width(), twf->rect.top(), twf->tabBarSize.width(), borderThickness);
+ break ;
+ case QTabBar::RoundedWest:
+ tabBarRect = QRect(twf->rect.left(), twf->rect.top() + twf->leftCornerWidgetSize.height(), borderThickness, twf->tabBarSize.height());
+ break ;
+ case QTabBar::RoundedEast:
+ tabBarRect = QRect(twf->rect.right() - borderThickness + 1, twf->rect.top() + twf->leftCornerWidgetSize.height(),
+ borderThickness, twf->tabBarSize.height());
+ break ;
+ case QTabBar::RoundedSouth:
+ if (reverse)
+ tabBarRect = QRect(twf->rect.right() - twf->leftCornerWidgetSize.width() - twf->tabBarSize.width() + 1,
+ twf->rect.bottom() - borderThickness + 1, twf->tabBarSize.width(), borderThickness);
+ else
+ tabBarRect = QRect(twf->rect.left() + twf->leftCornerWidgetSize.width(),
+ twf->rect.bottom() - borderThickness + 1, twf->tabBarSize.width(), borderThickness);
+ break ;
+ default:
+ break;
+ }
+
+ QRegion region(twf->rect);
+ region -= tabBarRect;
+ painter->setClipRegion(region);
+
+ // Outer border
+ QLine leftLine = QLine(twf->rect.topLeft() + QPoint(0, 2), twf->rect.bottomLeft() - QPoint(0, 2));
+ QLine rightLine = QLine(twf->rect.topRight() + QPoint(0, 2), twf->rect.bottomRight() - QPoint(0, 2));
+ QLine bottomLine = QLine(twf->rect.bottomLeft() + QPoint(2, 0), twf->rect.bottomRight() - QPoint(2, 0));
+ QLine topLine = QLine(twf->rect.topLeft() + QPoint(2, 0), twf->rect.topRight() - QPoint(2, 0));
+
+ QBrush border = option->palette.shadow();
+ qBrushSetAlphaF(&border, qreal(0.4));
+ painter->setPen(QPen(border, 0));
+
+ QVarLengthArray<QLine, 4> lines;
+ QVarLengthArray<QPoint, 8> points;
+
+ lines.append(topLine);
+
+ // Inner border
+ QLine innerLeftLine = QLine(leftLine.p1() + QPoint(1, 0), leftLine.p2() + QPoint(1, 0));
+ QLine innerRightLine = QLine(rightLine.p1() - QPoint(1, 0), rightLine.p2() - QPoint(1, 0));
+ QLine innerBottomLine = QLine(bottomLine.p1() - QPoint(0, 1), bottomLine.p2() - QPoint(0, 1));
+ QLine innerTopLine = QLine(topLine.p1() + QPoint(0, 1), topLine.p2() + QPoint(0, 1));
+
+ // Rounded Corner
+ QPoint leftBottomOuterCorner = QPoint(innerLeftLine.p2() + QPoint(0, 1));
+ QPoint leftBottomInnerCorner1 = QPoint(leftLine.p2() + QPoint(0, 1));
+ QPoint leftBottomInnerCorner2 = QPoint(bottomLine.p1() - QPoint(1, 0));
+ QPoint rightBottomOuterCorner = QPoint(innerRightLine.p2() + QPoint(0, 1));
+ QPoint rightBottomInnerCorner1 = QPoint(rightLine.p2() + QPoint(0, 1));
+ QPoint rightBottomInnerCorner2 = QPoint(bottomLine.p2() + QPoint(1, 0));
+ QPoint rightTopOuterCorner = QPoint(innerRightLine.p1() - QPoint(0, 1));
+ QPoint rightTopInnerCorner1 = QPoint(rightLine.p1() - QPoint(0, 1));
+ QPoint rightTopInnerCorner2 = QPoint(topLine.p2() + QPoint(1, 0));
+ QPoint leftTopOuterCorner = QPoint(innerLeftLine.p1() - QPoint(0, 1));
+ QPoint leftTopInnerCorner1 = QPoint(leftLine.p1() - QPoint(0, 1));
+ QPoint leftTopInnerCorner2 = QPoint(topLine.p1() - QPoint(1, 0));
+
+ lines.append(leftLine);
+ lines.append(rightLine);
+ lines.append(bottomLine);
+
+ painter->drawLines(lines.constData(), lines.size());
+ lines.clear();
+
+ points.append(leftBottomOuterCorner);
+ points.append(rightBottomOuterCorner);
+ points.append(rightTopOuterCorner);
+ points.append(leftTopOuterCorner);
+
+ painter->drawPoints(points.constData(), points.size());
+ points.clear();
+
+ QBrush innerTopLeft = option->palette.shadow();
+ qBrushSetAlphaF(&innerTopLeft, qreal(0.075));
+ painter->setPen(QPen(innerTopLeft, 0));
+
+ lines.append(innerLeftLine);
+ lines.append(innerTopLine);
+ painter->drawLines(lines.constData(), lines.size());
+ lines.clear();
+
+ QBrush innerBottomRight = option->palette.shadow();
+ qBrushSetAlphaF(&innerBottomRight, qreal(0.23));
+ painter->setPen(QPen(innerBottomRight, 0));
+ lines.append(innerRightLine);
+ lines.append(innerBottomLine);
+ painter->drawLines(lines.constData(), lines.size());
+ lines.clear();
+
+ QBrush corner = option->palette.shadow();
+ qBrushSetAlphaF(&corner, 0.25);
+ painter->setPen(QPen(corner, 0));
+ points.append(leftBottomInnerCorner1);
+ points.append(leftBottomInnerCorner2);
+ points.append(rightBottomInnerCorner1);
+ points.append(rightBottomInnerCorner2);
+ points.append(rightTopInnerCorner1);
+ points.append(rightTopInnerCorner2);
+ points.append(leftTopInnerCorner1);
+ points.append(leftTopInnerCorner2);
+ painter->drawPoints(points.constData(), points.size());
+ points.clear();
+
+ painter->restore();
+ }
+ break ;
+#endif // QT_NO_TABWIDGET
+#ifndef QT_NO_TABBAR
+ case PE_FrameTabBarBase:
+ if (const QStyleOptionTabBarBase *tbb = qstyleoption_cast<const QStyleOptionTabBarBase *>(option)) {
+ if (tbb->shape != QTabBar::RoundedNorth && tbb->shape != QTabBar::RoundedWest &&
+ tbb->shape != QTabBar::RoundedSouth && tbb->shape != QTabBar::RoundedEast) {
+ QWindowsStyle::drawPrimitive(element, option, painter, widget);
+ break;
+ }
+
+ painter->save();
+
+ QRegion region(tbb->rect);
+ region -= tbb->tabBarRect;
+ painter->setClipRegion(region);
+
+ QLine topLine = QLine(tbb->rect.bottomLeft() - QPoint(0, 1), tbb->rect.bottomRight() - QPoint(0, 1));
+ QLine bottomLine = QLine(tbb->rect.bottomLeft(), tbb->rect.bottomRight());
+
+ QBrush border = option->palette.shadow();
+ qBrushSetAlphaF(&border, qreal(0.4));
+ QBrush innerTopLeft = option->palette.shadow();
+ qBrushSetAlphaF(&innerTopLeft, qreal(0.075));
+ QBrush innerBottomRight = option->palette.shadow();
+ qBrushSetAlphaF(&innerBottomRight, qreal(0.23));
+ QBrush corner = option->palette.shadow();
+ qBrushSetAlphaF(&corner, 0.25);
+
+ if (tbb->shape == QTabBar::RoundedSouth)
+ painter->setPen(QPen(corner, 0));
+ else
+ painter->setPen(QPen(border, 0));
+ painter->drawLine(topLine);
+
+ if (tbb->shape != QTabBar::RoundedSouth)
+ painter->setPen(QPen(innerTopLeft, 0));
+ else
+ painter->setPen(QPen(border, 0));
+ painter->drawLine(bottomLine);
+
+ painter->restore();
+ }
+ break ;
+#endif // QT_NO_TABBAR
+#ifndef QT_NO_GROUPBOX
+ case PE_FrameGroupBox:
+ if (const QStyleOptionFrame *frame = qstyleoption_cast<const QStyleOptionFrame *>(option)) {
+ QStyleOptionFrameV2 frameV2(*frame);
+ if (frameV2.features & QStyleOptionFrameV2::Flat) {
+ QPen oldPen = painter->pen();
+ painter->setPen(borderColor);
+ painter->drawLine(frameV2.rect.topLeft(), frameV2.rect.topRight());
+ painter->setPen(oldPen);
+ } else {
+ frameV2.state &= ~(State_Sunken | State_HasFocus);
+ drawPrimitive(PE_Frame, &frameV2, painter, widget);
+ }
+ }
+ break;
+#endif // QT_NO_GROUPBOX
+ case PE_Frame: {
+ QFrame::Shadow shadow = QFrame::Plain;
+ if (option->state & State_Sunken)
+ shadow = QFrame::Sunken;
+ else if (option->state & State_Raised)
+ shadow = QFrame::Raised;
+ qt_plastique_draw_frame(painter, option->rect, option, shadow);
+ break;
+ }
+#ifndef QT_NO_LINEEDIT
+ case PE_FrameLineEdit:
+ qt_plastique_draw_frame(painter, option->rect, option, QFrame::Sunken);
+ break;
+ case PE_PanelLineEdit:
+ if (const QStyleOptionFrame *lineEdit = qstyleoption_cast<const QStyleOptionFrame *>(option)) {
+ // Panel of a line edit inside combo box or spin box is drawn in CC_ComboBox and CC_SpinBox
+ if (widget) {
+#ifndef QT_NO_COMBOBOX
+ if (qobject_cast<const QComboBox *>(widget->parentWidget()))
+ break;
+#endif
+#ifndef QT_NO_SPINBOX
+ if (qobject_cast<const QAbstractSpinBox *>(widget->parentWidget()))
+ break;
+#endif
+ }
+
+ painter->save();
+
+ // Fill the line edit insides
+ QRect filledRect = lineEdit->rect.adjusted(1, 1, -1, -1);
+ QBrush baseBrush = qMapBrushToRect(lineEdit->palette.base(), filledRect);
+ painter->setBrushOrigin(filledRect.topLeft());
+ painter->fillRect(filledRect.adjusted(1, 1, -1, -1), baseBrush);
+
+ painter->setPen(QPen(baseBrush, 0));
+ const QLine lines[4] = {
+ QLine(filledRect.left(), filledRect.top() + 1,
+ filledRect.left(), filledRect.bottom() - 1),
+ QLine(filledRect.right(), filledRect.top() + 1,
+ filledRect.right(), filledRect.bottom() - 1),
+ QLine(filledRect.left() + 1, filledRect.top(),
+ filledRect.right() - 1, filledRect.top()),
+ QLine(filledRect.left() + 1, filledRect.bottom(),
+ filledRect.right() - 1, filledRect.bottom()) };
+ painter->drawLines(lines, 4);
+
+ if (lineEdit->lineWidth != 0)
+ qt_plastique_draw_frame(painter, option->rect, option, QFrame::Sunken);
+
+ painter->restore();
+ break;
+ }
+#endif // QT_NO_LINEEDIT
+ case PE_FrameDockWidget:
+ case PE_FrameMenu:
+ case PE_FrameStatusBarItem: {
+ // Draws the frame around a popup menu.
+ QPen oldPen = painter->pen();
+ painter->setPen(borderColor);
+ painter->drawRect(option->rect.adjusted(0, 0, -1, -1));
+ painter->setPen(alphaCornerColor);
+ const QPoint points[4] = {
+ QPoint(option->rect.topLeft()),
+ QPoint(option->rect.topRight()),
+ QPoint(option->rect.bottomLeft()),
+ QPoint(option->rect.bottomRight()) };
+ painter->drawPoints(points, 4);
+ painter->setPen(oldPen);
+ break;
+ }
+#ifdef QT3_SUPPORT
+ case PE_Q3DockWindowSeparator: {
+ QPen oldPen = painter->pen();
+ painter->setPen(alphaCornerColor);
+ QRect rect = option->rect;
+ if (option->state & State_Horizontal) {
+ painter->drawLine(rect.right(), rect.top() + 2, rect.right(), rect.bottom() - 1);
+ } else {
+ painter->drawLine(rect.left() + 2, rect.bottom(), rect.right() - 1, rect.bottom());
+ }
+ painter->setPen(oldPen);
+ break;
+ }
+ case PE_Q3Separator: {
+ QPen oldPen = painter->pen();
+ painter->setPen(alphaCornerColor);
+ if ((option->state & State_Horizontal) == 0)
+ painter->drawLine(option->rect.bottomLeft(), option->rect.bottomRight());
+ else
+ painter->drawLine(option->rect.topRight(), option->rect.bottomRight());
+ painter->setPen(option->palette.background().color().lighter(104));
+ if ((option->state & State_Horizontal) == 0)
+ painter->drawLine(option->rect.topLeft(), option->rect.topRight());
+ else
+ painter->drawLine(option->rect.topLeft(), option->rect.bottomLeft());
+ painter->setPen(oldPen);
+ break;
+ }
+#endif // QT3_SUPPORT
+#ifndef QT_NO_MAINWINDOW
+ case PE_PanelMenuBar:
+ if ((widget && qobject_cast<const QMainWindow *>(widget->parentWidget()))
+#ifdef QT3_SUPPORT
+ || (widget && widget->parentWidget() && widget->parentWidget()->inherits("Q3MainWindow"))
+#endif
+ ) {
+ // Draws the light line above and the dark line below menu bars and
+ // tool bars.
+ QPen oldPen = painter->pen();
+ if (element == PE_PanelMenuBar || (option->state & State_Horizontal)) {
+ painter->setPen(alphaCornerColor);
+ painter->drawLine(option->rect.left(), option->rect.bottom(),
+ option->rect.right(), option->rect.bottom());
+ painter->setPen(option->palette.background().color().lighter(104));
+ painter->drawLine(option->rect.left(), option->rect.top(),
+ option->rect.right(), option->rect.top());
+ } else {
+ painter->setPen(option->palette.background().color().lighter(104));
+ painter->drawLine(option->rect.left(), option->rect.top(),
+ option->rect.left(), option->rect.bottom());
+ painter->setPen(alphaCornerColor);
+ painter->drawLine(option->rect.right(), option->rect.top(),
+ option->rect.right(), option->rect.bottom());
+ }
+ painter->setPen(oldPen);
+ }
+ break;
+#endif // QT_NO_MAINWINDOW
+ case PE_IndicatorHeaderArrow: {
+ bool usedAntialiasing = painter->renderHints() & QPainter::Antialiasing;
+ if (!usedAntialiasing)
+ painter->setRenderHint(QPainter::Antialiasing);
+ QWindowsStyle::drawPrimitive(element, option, painter, widget);
+ if (!usedAntialiasing)
+ painter->setRenderHint(QPainter::Antialiasing, false);
+ break;
+ }
+ case PE_PanelButtonTool:
+ // Draws a tool button (f.ex., in QToolBar and QTabBar)
+ if ((option->state & State_Enabled || option->state & State_On) || !(option->state & State_AutoRaise))
+ qt_plastique_drawShadedPanel(painter, option, true, widget);
+ break;
+#ifndef QT_NO_TOOLBAR
+ case PE_IndicatorToolBarHandle: {
+ QPixmap cache;
+ QRect rect = option->rect;
+#ifdef QT3_SUPPORT
+ if (widget && widget->inherits("Q3DockWindowHandle") && widget->parentWidget()->inherits("Q3DockWindow")) {
+ if (!(option->state & State_Horizontal))
+ rect.adjust(2, 0, -2, 0);
+ }
+#endif
+ QString pixmapName = uniqueName(QLatin1String("toolbarhandle"), option, rect.size());
+ if (!UsePixmapCache || !QPixmapCache::find(pixmapName, cache)) {
+ cache = QPixmap(rect.size());
+ cache.fill(Qt::transparent);
+ QPainter cachePainter(&cache);
+ QRect cacheRect(QPoint(0, 0), rect.size());
+ if (widget)
+ cachePainter.fillRect(cacheRect, option->palette.brush(widget->backgroundRole()));
+ else
+ cachePainter.fillRect(cacheRect, option->palette.background());
+
+ QImage handle(qt_toolbarhandle);
+ alphaCornerColor.setAlpha(170);
+ handle.setColor(1, alphaCornerColor.rgba());
+ handle.setColor(2, mergedColors(alphaCornerColor, option->palette.light().color()).rgba());
+ handle.setColor(3, option->palette.light().color().rgba());
+
+ if (option->state & State_Horizontal) {
+ int nchunks = cacheRect.height() / handle.height();
+ int indent = (cacheRect.height() - (nchunks * handle.height())) / 2;
+ for (int i = 0; i < nchunks; ++i)
+ cachePainter.drawImage(QPoint(cacheRect.left() + 3, cacheRect.top() + indent + i * handle.height()),
+ handle);
+ } else {
+ int nchunks = cacheRect.width() / handle.width();
+ int indent = (cacheRect.width() - (nchunks * handle.width())) / 2;
+ for (int i = 0; i < nchunks; ++i)
+ cachePainter.drawImage(QPoint(cacheRect.left() + indent + i * handle.width(), cacheRect.top() + 3),
+ handle);
+ }
+ cachePainter.end();
+ if (UsePixmapCache)
+ QPixmapCache::insert(pixmapName, cache);
+ }
+ painter->drawPixmap(rect.topLeft(), cache);
+ break;
+ }
+ case PE_IndicatorToolBarSeparator: {
+ QPen oldPen = painter->pen();
+ painter->setPen(alphaCornerColor);
+ if (option->state & State_Horizontal) {
+ painter->drawLine(option->rect.left(), option->rect.top() + 1, option->rect.left(), option->rect.bottom() - 2);
+ painter->setPen(option->palette.base().color());
+ painter->drawLine(option->rect.right(), option->rect.top() + 1, option->rect.right(), option->rect.bottom() - 2);
+ } else {
+ painter->drawLine(option->rect.left() + 1, option->rect.top(), option->rect.right() - 2, option->rect.top());
+ painter->setPen(option->palette.base().color());
+ painter->drawLine(option->rect.left() + 1, option->rect.bottom(), option->rect.right() - 2, option->rect.bottom());
+ }
+ painter->setPen(oldPen);
+ break;
+ }
+#endif // QT_NO_TOOLBAR
+ case PE_PanelButtonCommand:
+ if (const QStyleOptionButton *button = qstyleoption_cast<const QStyleOptionButton *>(option)) {
+ bool sunken = (button->state & State_Sunken) || (button->state & State_On);
+ if ((button->features & QStyleOptionButton::Flat) && !sunken)
+ break;
+
+ bool defaultButton = (button->features & (QStyleOptionButton::DefaultButton
+ | QStyleOptionButton::AutoDefaultButton));
+
+ BEGIN_STYLE_PIXMAPCACHE(QString::fromLatin1("pushbutton-%1").arg(defaultButton))
+
+ QPen oldPen = p->pen();
+ bool hover = (button->state & State_Enabled) && (button->state & State_MouseOver);
+
+ // Give the painter a different brush origin for sunken buttons
+ if (sunken) {
+ // ### No such function
+ // p->setPenOrigin(rect.left() + 1, rect.top() + 1);
+ p->setBrushOrigin(rect.left() + 1, rect.top() + 1);
+ }
+
+ // Draw border
+ qt_plastique_draw_frame(p, rect, option);
+
+ // Fill the panel
+ QRectF fillRect = rect.adjusted(2, 2, -2, -2);
+
+ // Button colors
+ QBrush alphaCornerBrush = qMapBrushToRect(qBrushDark(option->palette.button(), 165), rect);
+ qBrushSetAlphaF(&alphaCornerBrush, 0.5);
+ QBrush buttonGradientBrush;
+ QBrush leftLineGradientBrush;
+ QBrush rightLineGradientBrush;
+ QBrush sunkenButtonGradientBrush;
+ QBrush sunkenLeftLineGradientBrush;
+ QBrush sunkenRightLineGradientBrush;
+ QBrush buttonBrush = qMapBrushToRect(option->palette.button(), rect);
+ if (buttonBrush.gradient() || !buttonBrush.texture().isNull()) {
+ buttonGradientBrush = buttonBrush;
+ sunkenButtonGradientBrush = qBrushDark(buttonBrush, 108);
+ leftLineGradientBrush = qBrushLight(buttonBrush, 105);
+ rightLineGradientBrush = qBrushDark(buttonBrush, 105);
+ sunkenLeftLineGradientBrush = qBrushDark(buttonBrush, 110);
+ sunkenRightLineGradientBrush = qBrushDark(buttonBrush, 106);
+ } else {
+ // Generate gradients
+ QLinearGradient buttonGradient(rect.topLeft(), rect.bottomLeft());
+ if (hover) {
+ buttonGradient.setColorAt(0.0, mergedColors(option->palette.highlight().color(),
+ buttonBrush.color().lighter(104), 6));
+ buttonGradient.setColorAt(1.0, mergedColors(option->palette.highlight().color(),
+ buttonBrush.color().darker(110), 6));
+ } else {
+ buttonGradient.setColorAt(0.0, buttonBrush.color().lighter(104));
+ buttonGradient.setColorAt(1.0, buttonBrush.color().darker(110));
+ }
+ buttonGradientBrush = QBrush(buttonGradient);
+
+ QLinearGradient buttonGradient2(rect.topLeft(), rect.bottomLeft());
+ buttonGradient2.setColorAt(0.0, buttonBrush.color().darker(113));
+ buttonGradient2.setColorAt(1.0, buttonBrush.color().darker(103));
+ sunkenButtonGradientBrush = QBrush(buttonGradient2);
+
+ QLinearGradient buttonGradient3(rect.topLeft(), rect.bottomLeft());
+ buttonGradient3.setColorAt(0.0, buttonBrush.color().lighter(105));
+ buttonGradient3.setColorAt(1.0, buttonBrush.color());
+ leftLineGradientBrush = QBrush(buttonGradient3);
+
+ QLinearGradient buttonGradient4(rect.topLeft(), rect.bottomLeft());
+ buttonGradient4.setColorAt(0.0, buttonBrush.color());
+ buttonGradient4.setColorAt(1.0, buttonBrush.color().darker(110));
+ rightLineGradientBrush = QBrush(buttonGradient4);
+
+ QLinearGradient buttonGradient5(rect.topLeft(), rect.bottomLeft());
+ buttonGradient5.setColorAt(0.0, buttonBrush.color().darker(113));
+ buttonGradient5.setColorAt(1.0, buttonBrush.color().darker(107));
+ sunkenLeftLineGradientBrush = QBrush(buttonGradient5);
+
+ QLinearGradient buttonGradient6(rect.topLeft(), rect.bottomLeft());
+ buttonGradient6.setColorAt(0.0, buttonBrush.color().darker(108));
+ buttonGradient6.setColorAt(1.0, buttonBrush.color().darker(103));
+ sunkenRightLineGradientBrush = QBrush(buttonGradient6);
+ }
+
+ // Main fill
+ p->fillRect(fillRect,
+ qMapBrushToRect(sunken ? sunkenButtonGradientBrush
+ : buttonGradientBrush, rect));
+
+ // Top line
+ p->setPen(QPen(qBrushLight(qMapBrushToRect(sunken ? sunkenButtonGradientBrush
+ : buttonGradientBrush, rect), 105), 0));
+ p->drawLine(QPointF(rect.left() + 2, rect.top() + 1),
+ QPointF(rect.right() - 2, rect.top() + 1));
+
+ // Bottom line
+ p->setPen(QPen(qBrushDark(qMapBrushToRect(sunken ? sunkenButtonGradientBrush
+ : buttonGradientBrush, rect), 105), 0));
+ p->drawLine(QPointF(rect.left() + 2, rect.bottom() - 1),
+ QPointF(rect.right() - 2, rect.bottom() - 1));
+
+ // Left line
+ p->setPen(QPen(qMapBrushToRect(sunken ? sunkenLeftLineGradientBrush
+ : leftLineGradientBrush, rect), 1));
+ p->drawLine(QPointF(rect.left() + 1, rect.top() + 2),
+ QPointF(rect.left() + 1, rect.bottom() - 2));
+
+ // Right line
+ p->setPen(QPen(qMapBrushToRect(sunken ? sunkenRightLineGradientBrush
+ : rightLineGradientBrush, rect), 1));
+ p->drawLine(QPointF(rect.right() - 1, rect.top() + 2),
+ QPointF(rect.right() - 1, rect.bottom() - 2));
+
+ // Hovering
+ if (hover && !sunken) {
+ QBrush hover = qMapBrushToRect(option->palette.highlight(), rect);
+ QBrush hoverOuter = hover;
+ qBrushSetAlphaF(&hoverOuter, qreal(0.7));
+
+ QLine lines[2];
+
+ p->setPen(QPen(hoverOuter, 0));
+ lines[0] = QLine(rect.left() + 1, rect.top() + 1, rect.right() - 1, rect.top() + 1);
+ lines[1] = QLine(rect.left() + 1, rect.bottom() - 1, rect.right() - 1, rect.bottom() - 1);
+ p->drawLines(lines, 2);
+
+ QBrush hoverInner = hover;
+ qBrushSetAlphaF(&hoverInner, qreal(0.45));
+ p->setPen(QPen(hoverInner, 0));
+ lines[0] = QLine(rect.left() + 1, rect.top() + 2, rect.right() - 1, rect.top() + 2);
+ lines[1] = QLine(rect.left() + 1, rect.bottom() - 2, rect.right() - 1, rect.bottom() - 2);
+ p->drawLines(lines, 2);
+
+ QBrush hoverSide = hover;
+ qBrushSetAlphaF(&hoverSide, qreal(0.075));
+ p->setPen(QPen(hoverSide, 0));
+ lines[0] = QLine(rect.left() + 1, rect.top() + 2, rect.left() + 1, rect.bottom() - 2);
+ lines[1] = QLine(rect.right() - 1, rect.top() + 2, rect.right() - 1, rect.bottom() - 2);
+ p->drawLines(lines, 2);
+ }
+
+ p->setPen(oldPen);
+
+ END_STYLE_PIXMAPCACHE
+ }
+ break;
+ case PE_IndicatorCheckBox:
+ if (const QStyleOptionButton *button = qstyleoption_cast<const QStyleOptionButton *>(option)) {
+ BEGIN_STYLE_PIXMAPCACHE(QLatin1String("checkbox"))
+
+ p->save();
+
+ // Outline
+ QBrush border = option->palette.shadow();
+ qBrushSetAlphaF(&border, qreal(0.4));
+ p->setPen(QPen(border, 0));
+ const QLine lines[4] = {
+ QLine(rect.left() + 1, rect.top(), rect.right() - 1, rect.top()),
+ QLine(rect.left() + 1, rect.bottom(), rect.right() - 1, rect.bottom()),
+ QLine(rect.left(), rect.top() + 1, rect.left(), rect.bottom() - 1),
+ QLine(rect.right(), rect.top() + 1, rect.right(), rect.bottom() - 1) };
+ p->drawLines(lines, 4);
+
+ QBrush corner = option->palette.shadow();
+ qBrushSetAlphaF(&corner, qreal(0.2));
+ p->setPen(QPen(corner, 0));
+ const QPoint points[4] = {
+ rect.topLeft(), rect.topRight(),
+ rect.bottomLeft(), rect.bottomRight() };
+ p->drawPoints(points, 4);
+
+ // Fill
+ QBrush baseBrush = qMapBrushToRect(button->palette.base(), rect);
+ if (!baseBrush.gradient() && baseBrush.texture().isNull()) {
+ QLinearGradient gradient(rect.center().x(), rect.top(), rect.center().x(), rect.bottom());
+ gradient.setColorAt(0, baseBrush.color());
+ gradient.setColorAt(1, baseBrush.color().darker(105));
+ baseBrush = gradient;
+ }
+ p->fillRect(rect.adjusted(1, 1, -1, -1), baseBrush);
+
+ // Hover
+ if ((button->state & State_Enabled) && (button->state & State_MouseOver)) {
+ QBrush pen = qMapBrushToRect(button->palette.highlight(), rect);
+ qBrushSetAlphaF(&pen, qreal(0.8));
+ p->setPen(QPen(pen, 0));
+ p->drawRect(rect.adjusted(1, 1, -2, -2));
+ qBrushSetAlphaF(&pen, 0.5);
+ p->setPen(QPen(pen, 0));
+ p->drawRect(rect.adjusted(2, 2, -3, -3));
+
+ qBrushSetAlphaF(&pen, qreal(0.2));
+ p->setBrush(pen);
+ p->drawRect(rect.adjusted(2, 2, -3, -3));
+ }
+
+ // Indicator
+ bool on = button->state & State_On;
+ bool sunken = button->state & State_Sunken;
+ bool unchanged = button->state & State_NoChange;
+ bool enabled = button->state & State_Enabled;
+ if (on || (enabled && sunken) || unchanged) {
+ p->setRenderHint(QPainter::Antialiasing);
+ QBrush pointBrush = qMapBrushToRect(button->palette.text(), rect);
+ if (sunken)
+ qBrushSetAlphaF(&pointBrush, qreal(0.5));
+ else if (unchanged)
+ qBrushSetAlphaF(&pointBrush, qreal(0.3));
+ p->setPen(QPen(pointBrush, 3));
+ const QLine lines[2] = {
+ QLine(rect.left() + 4, rect.top() + 4, rect.right() - 3, rect.bottom() - 3),
+ QLine(rect.right() - 3, rect.top() + 4, rect.left() + 4, rect.bottom() - 3) };
+ p->drawLines(lines, 2);
+ }
+
+ p->restore();
+ END_STYLE_PIXMAPCACHE
+ }
+ break;
+ case PE_IndicatorRadioButton:
+ if (const QStyleOptionButton *button = qstyleoption_cast<const QStyleOptionButton *>(option)) {
+ BEGIN_STYLE_PIXMAPCACHE(QLatin1String("radiobutton"))
+
+ p->save();
+ p->setRenderHint(QPainter::Antialiasing);
+
+ // The the filled ellipse
+ QBrush border = qMapBrushToRect(option->palette.shadow(), rect);
+ qBrushSetAlphaF(&border, qreal(0.51));
+ p->setPen(QPen(border, 0));
+
+ QBrush baseBrush = qMapBrushToRect(button->palette.base(), rect);
+ if (!baseBrush.gradient() && baseBrush.texture().isNull()) {
+ QLinearGradient gradient(rect.center().x(), rect.top(), rect.center().x(), rect.bottom());
+ gradient.setColorAt(0, baseBrush.color());
+ gradient.setColorAt(1, baseBrush.color().darker(105));
+ baseBrush = gradient;
+ }
+ p->setBrush(baseBrush);
+ p->drawEllipse(QRectF(rect).adjusted(1, 1, -1, -1));
+
+ // Hover
+ if ((button->state & State_Enabled) && (button->state & State_MouseOver)) {
+ QBrush pen = qMapBrushToRect(button->palette.highlight(), rect);
+ qBrushSetAlphaF(&pen, qreal(0.8));
+ p->setPen(QPen(pen, 0));
+ qBrushSetAlphaF(&pen, qreal(0.2));
+ p->setBrush(pen);
+ p->drawEllipse(QRectF(rect).adjusted(2, 2, -2, -2));
+ }
+
+ // Indicator
+ bool on = button->state & State_On;
+ bool sunken = button->state & State_Sunken;
+ bool enabled = button->state & State_Enabled;
+ if (on || (enabled && sunken)) {
+ p->setPen(Qt::NoPen);
+ QBrush pointBrush = qMapBrushToRect(button->palette.text(), rect);
+ if (sunken)
+ qBrushSetAlphaF(&pointBrush, 0.5);
+ p->setBrush(pointBrush);
+ p->drawEllipse(QRectF(rect).adjusted(3, 3, -3, -3));
+ }
+
+ p->restore();
+ END_STYLE_PIXMAPCACHE
+ }
+ break;
+#ifndef QT_NO_DOCKWIDGET
+ case PE_IndicatorDockWidgetResizeHandle:
+ if ((option->state & State_Enabled) && (option->state & State_MouseOver))
+ painter->fillRect(option->rect, QColor(255, 255, 255, 128));
+ if (option->state & State_Horizontal) {
+ int width = option->rect.width() / 3;
+ QRect rect(option->rect.center().x() - width / 2,
+ option->rect.top() + (option->rect.height() / 2) - 1, width, 3);
+ qt_plastique_draw_handle(painter, option, rect, Qt::Vertical, widget);
+ } else {
+ int height = option->rect.height() / 3;
+ QRect rect(option->rect.left() + (option->rect.width() / 2 - 1),
+ option->rect.center().y() - height / 2, 3, height);
+ qt_plastique_draw_handle(painter, option, rect, Qt::Horizontal, widget);
+ }
+ break;
+#endif // QT_NO_DOCKWIDGET
+ case PE_IndicatorViewItemCheck: {
+ QStyleOptionButton button;
+ button.QStyleOption::operator=(*option);
+ button.state &= ~State_MouseOver;
+ drawPrimitive(PE_IndicatorCheckBox, &button, painter, widget);
+ break;
+ }
+ case PE_FrameWindow: {
+ painter->save();
+ bool active = (option->state & State_Active);
+ int titleBarStop = option->rect.top() + pixelMetric(PM_TitleBarHeight, option, widget);
+
+ QPalette palette = option->palette;
+ if (!active)
+ palette.setCurrentColorGroup(QPalette::Disabled);
+
+ // Frame and rounded corners
+ painter->setPen(mergedColors(palette.highlight().color(), Qt::black, 50));
+
+ QLine lines[3];
+ QPoint points[4];
+
+ // bottom border line
+ lines[0] = QLine(option->rect.left() + 1, option->rect.bottom(), option->rect.right() - 1, option->rect.bottom());
+
+ // bottom left and right side border lines
+ lines[1] = QLine(option->rect.left(), titleBarStop, option->rect.left(), option->rect.bottom() - 1);
+ lines[2] = QLine(option->rect.right(), titleBarStop, option->rect.right(), option->rect.bottom() - 1);
+ painter->drawLines(lines, 3);
+ points[0] = QPoint(option->rect.left() + 1, option->rect.bottom() - 1);
+ points[1] = QPoint(option->rect.right() - 1, option->rect.bottom() - 1);
+ painter->drawPoints(points, 2);
+
+#ifdef QT3_SUPPORT
+ if (widget && widget->inherits("Q3DockWindow")) {
+ // also draw the frame on the title bar
+ lines[0] = QLine(option->rect.left() + 1, option->rect.top(),
+ option->rect.right() - 1, option->rect.top());
+ lines[1] = QLine(option->rect.left(), option->rect.top() + 1,
+ option->rect.left(), titleBarStop);
+ lines[2] = QLine(option->rect.right(), option->rect.top() + 1,
+ option->rect.right(), titleBarStop);
+ painter->drawLines(lines, 3);
+ }
+#endif
+
+ // alpha corners
+ painter->setPen(mergedColors(palette.highlight().color(), palette.background().color(), 55));
+ points[0] = QPoint(option->rect.left() + 2, option->rect.bottom() - 1);
+ points[1] = QPoint(option->rect.left() + 1, option->rect.bottom() - 2);
+ points[2] = QPoint(option->rect.right() - 2, option->rect.bottom() - 1);
+ points[3] = QPoint(option->rect.right() - 1, option->rect.bottom() - 2);
+ painter->drawPoints(points, 4);
+
+#ifdef QT3_SUPPORT
+ if (widget && widget->inherits("Q3DockWindow")) {
+ // also draw the frame on the title bar
+ points[0] = option->rect.topLeft();
+ points[1] = option->rect.topRight();
+ painter->drawPoints(points, 2);
+ }
+#endif
+
+ // upper and lower left inner
+ painter->setPen(active ? mergedColors(palette.highlight().color(), palette.background().color()) : palette.background().color().darker(120));
+ painter->drawLine(option->rect.left() + 1, titleBarStop, option->rect.left() + 1, option->rect.bottom() - 2);
+
+#ifdef QT3_SUPPORT
+ if (widget && widget->inherits("Q3DockWindow")) {
+ // also draw the frame on the title bar
+ lines[0] = QLine(option->rect.left() + 1, option->rect.top() + 1,
+ option->rect.left() + 1, titleBarStop);
+ lines[1] = QLine(option->rect.right() - 1, option->rect.top() + 1,
+ option->rect.right() - 1, titleBarStop);
+ lines[2] = QLine(option->rect.left() + 1, option->rect.top() + 1,
+ option->rect.right() - 1, option->rect.top() + 1);
+ painter->drawLines(lines, 3);
+ }
+#endif
+
+ painter->setPen(active ? mergedColors(palette.highlight().color(), palette.background().color(), 57) : palette.background().color().darker(130));
+ lines[0] = QLine(option->rect.right() - 1, titleBarStop, option->rect.right() - 1, option->rect.bottom() - 2);
+ lines[1] = QLine(option->rect.left() + 1, option->rect.bottom() - 1, option->rect.right() - 1, option->rect.bottom() - 1);
+ painter->drawLines(lines, 2);
+
+ painter->restore();
+ }
+ break;
+ case PE_IndicatorBranch: {
+ int mid_h = option->rect.x() + option->rect.width() / 2;
+ int mid_v = option->rect.y() + option->rect.height() / 2;
+ int bef_h = mid_h;
+ int bef_v = mid_v;
+ int aft_h = mid_h;
+ int aft_v = mid_v;
+ QBrush brush(option->palette.dark().color(), Qt::Dense4Pattern);
+ if (option->state & State_Item) {
+ if (option->direction == Qt::RightToLeft)
+ painter->fillRect(option->rect.left(), mid_v, bef_h - option->rect.left(), 1, brush);
+ else
+ painter->fillRect(aft_h, mid_v, option->rect.right() - aft_h + 1, 1, brush);
+ }
+ if (option->state & State_Sibling)
+ painter->fillRect(mid_h, aft_v, 1, option->rect.bottom() - aft_v + 1, brush);
+ if (option->state & (State_Open | State_Children | State_Item | State_Sibling))
+ painter->fillRect(mid_h, option->rect.y(), 1, bef_v - option->rect.y(), brush);
+
+ if (option->state & State_Children) {
+ painter->save();
+ QPoint center = option->rect.center();
+ // border
+ QRect fullRect(center.x() - 4, center.y() - 4, 9, 9);
+ painter->setPen(borderColor);
+
+ const QLine lines[4] = {
+ QLine(fullRect.left() + 1, fullRect.top(),
+ fullRect.right() - 1, fullRect.top()),
+ QLine(fullRect.left() + 1, fullRect.bottom(),
+ fullRect.right() - 1, fullRect.bottom()),
+ QLine(fullRect.left(), fullRect.top() + 1,
+ fullRect.left(), fullRect.bottom() - 1),
+ QLine(fullRect.right(), fullRect.top() + 1,
+ fullRect.right(), fullRect.bottom() - 1) };
+ painter->drawLines(lines, 4);
+
+ // "antialiased" corners
+ painter->setPen(alphaCornerColor);
+ const QPoint points[4] = {
+ fullRect.topLeft(),
+ fullRect.topRight(),
+ fullRect.bottomLeft(),
+ fullRect.bottomRight() };
+ painter->drawPoints(points, 4);
+
+ // fill
+ QRect adjustedRect = fullRect;
+ QRect gradientRect(adjustedRect.left() + 1, adjustedRect.top() + 1,
+ adjustedRect.right() - adjustedRect.left() - 1,
+ adjustedRect.bottom() - adjustedRect.top() - 1);
+ qt_plastique_draw_gradient(painter, gradientRect, baseGradientStartColor, baseGradientStopColor);
+ // draw "+" or "-"
+ painter->setPen(alphaTextColor);
+ painter->drawLine(center.x() - 2, center.y(), center.x() + 2, center.y());
+ if (!(option->state & State_Open))
+ painter->drawLine(center.x(), center.y() - 2, center.x(), center.y() + 2);
+ painter->restore();
+ }
+ }
+ break;
+ default:
+ QWindowsStyle::drawPrimitive(element, option, painter, widget);
+ break;
+ }
+}
+
+/*!
+ \reimp
+*/
+void QPlastiqueStyle::drawControl(ControlElement element, const QStyleOption *option,
+ QPainter *painter, const QWidget *widget) const
+{
+ QColor borderColor = option->palette.background().color().darker(178);
+ QColor alphaCornerColor;
+ if (widget) {
+ // ### backgroundrole/foregroundrole should be part of the style option
+ alphaCornerColor = mergedColors(option->palette.color(widget->backgroundRole()), borderColor);
+ } else {
+ alphaCornerColor = mergedColors(option->palette.background().color(), borderColor);
+ }
+ QColor alphaTextColor = mergedColors(option->palette.background().color(), option->palette.text().color());
+
+ QColor gradientStartColor = option->palette.button().color().lighter(104);
+ QColor gradientStopColor = option->palette.button().color().darker(105);
+
+ QColor shadowGradientStartColor = option->palette.button().color().darker(115);
+ QColor shadowGradientStopColor = option->palette.button().color().darker(120);
+
+ QColor highlightedGradientStartColor = option->palette.button().color().lighter(101);
+ QColor highlightedGradientStopColor = mergedColors(option->palette.button().color(), option->palette.highlight().color(), 85);
+
+ QColor lightShadowGradientStartColor = highlightedGradientStartColor.lighter(105);
+ QColor lightShadowGradientStopColor = highlightedGradientStopColor.lighter(105);
+
+ QColor highlightedDarkInnerBorderColor = mergedColors(option->palette.button().color(), option->palette.highlight().color(), 35);
+ QColor highlightedLightInnerBorderColor = mergedColors(option->palette.button().color(), option->palette.highlight().color(), 58);
+
+ QColor alphaInnerColor = mergedColors(highlightedDarkInnerBorderColor, option->palette.base().color());
+ QColor lightShadow = lightShadowGradientStartColor;
+ QColor shadow = shadowGradientStartColor;
+
+ switch (element) {
+#ifndef QT_NO_TABBAR
+ case CE_TabBarTabShape:
+ if (const QStyleOptionTab *tab = qstyleoption_cast<const QStyleOptionTab *>(option)) {
+
+ if (tab->shape != QTabBar::RoundedNorth && tab->shape != QTabBar::RoundedWest &&
+ tab->shape != QTabBar::RoundedSouth && tab->shape != QTabBar::RoundedEast) {
+ QWindowsStyle::drawControl(element, option, painter, widget);
+ break;
+ }
+
+ painter->save();
+
+ // Set up some convenience variables
+ bool disabled = !(tab->state & State_Enabled);
+ bool onlyTab = tab->position == QStyleOptionTab::OnlyOneTab;
+ bool selected = tab->state & State_Selected;
+ bool mouseOver = (tab->state & State_MouseOver) && !selected && !disabled;
+ bool previousSelected = tab->selectedPosition == QStyleOptionTab::PreviousIsSelected;
+ bool nextSelected = tab->selectedPosition == QStyleOptionTab::NextIsSelected;
+ bool leftCornerWidget = (tab->cornerWidgets & QStyleOptionTab::LeftCornerWidget);
+ bool reverse = (tab->direction == Qt::RightToLeft);
+
+ int lowerTop = selected ? 0 : 3; // to make the selected tab bigger than the rest
+ QRect adjustedRect;
+ bool atEnd = (tab->position == QStyleOptionTab::End) || onlyTab;
+ bool atBeginning = ((tab->position == QStyleOptionTab::Beginning) || onlyTab)
+ && !leftCornerWidget;
+ bool reverseShadow = false;
+
+ int borderThickness = pixelMetric(PM_TabBarBaseOverlap, tab, widget);
+ int marginLeft = 0;
+ if ((atBeginning && !selected) || (selected && leftCornerWidget && ((tab->position == QStyleOptionTab::Beginning) || onlyTab))) {
+ marginLeft = 1;
+ }
+
+ // I've set the names based on the natural coordinate system. Vectors are used to rotate everything
+ // if the orientation of the tab bare is different than north.
+ {
+ // Coordinates of corners of rectangle for transformation
+ QPoint topLeft;
+ QPoint topRight;
+ QPoint bottomLeft;
+ QPoint bottomRight;
+
+ // Fill with normalized vectors in the direction of the coordinate system
+ // (down and right should be complement of up and left, or it will look odd)
+ QPoint vectorUp;
+ QPoint vectorDown;
+ QPoint vectorLeft;
+ QPoint vectorRight;
+
+ QBrush border = option->palette.shadow();
+ qBrushSetAlphaF(&border, qreal(0.4));
+ QBrush innerTopLeft = option->palette.shadow();
+ qBrushSetAlphaF(&innerTopLeft, qreal(0.075));
+ QBrush innerBottomRight = option->palette.shadow();
+ qBrushSetAlphaF(&innerBottomRight, qreal(0.23));
+ QBrush corner = option->palette.shadow();
+ qBrushSetAlphaF(&corner, qreal(0.25));
+
+ QBrush baseColor1;
+ QBrush baseColor2;
+
+ switch (tab->shape) {
+ case QTabBar::RoundedNorth:
+ vectorUp = QPoint(0, -1);
+ vectorDown = QPoint(0, 1);
+
+ if (reverse) {
+ vectorLeft = QPoint(1, 0);
+ vectorRight = QPoint(-1, 0);
+ reverseShadow = true;
+ } else {
+ vectorLeft = QPoint(-1, 0);
+ vectorRight = QPoint(1, 0);
+ }
+
+ if (reverse) {
+ topLeft = tab->rect.topRight();
+ topRight = tab->rect.topLeft();
+ bottomLeft = tab->rect.bottomRight();
+ bottomRight = tab->rect.bottomLeft();
+ } else {
+ topLeft = tab->rect.topLeft();
+ topRight = tab->rect.topRight();
+ bottomLeft = tab->rect.bottomLeft();
+ bottomRight = tab->rect.bottomRight();
+ }
+
+
+ baseColor1 = border;
+ baseColor2 = innerTopLeft;
+ break ;
+ case QTabBar::RoundedWest:
+ vectorUp = QPoint(-1, 0);
+ vectorDown = QPoint(1, 0);
+ vectorLeft = QPoint(0, -1);
+ vectorRight = QPoint(0, 1);
+
+ topLeft = tab->rect.topLeft();
+ topRight = tab->rect.bottomLeft();
+ bottomLeft = tab->rect.topRight();
+ bottomRight = tab->rect.bottomRight();
+
+ baseColor1 = border;
+ baseColor2 = innerTopLeft;
+ break ;
+ case QTabBar::RoundedEast:
+ vectorUp = QPoint(1, 0);
+ vectorDown = QPoint(-1, 0);
+ vectorLeft = QPoint(0, -1);
+ vectorRight = QPoint(0, 1);
+
+ topLeft = tab->rect.topRight();
+ topRight = tab->rect.bottomRight();
+ bottomLeft = tab->rect.topLeft();
+ bottomRight = tab->rect.bottomLeft();
+
+ baseColor1 = border;
+ baseColor2 = innerBottomRight;
+ break ;
+ case QTabBar::RoundedSouth:
+ vectorUp = QPoint(0, 1);
+ vectorDown = QPoint(0, -1);
+
+ if (reverse) {
+ vectorLeft = QPoint(1, 0);
+ vectorRight = QPoint(-1, 0);
+ reverseShadow = true;
+
+ topLeft = tab->rect.bottomRight();
+ topRight = tab->rect.bottomLeft();
+ bottomLeft = tab->rect.topRight();
+ bottomRight = tab->rect.topLeft();
+ } else {
+ vectorLeft = QPoint(-1, 0);
+ vectorRight = QPoint(1, 0);
+
+ topLeft = tab->rect.bottomLeft();
+ topRight = tab->rect.bottomRight();
+ bottomLeft = tab->rect.topLeft();
+ bottomRight = tab->rect.topRight();
+ }
+
+ baseColor1 = border;
+ baseColor2 = innerBottomRight;
+ break ;
+ default:
+ break;
+ }
+
+ // Make the tab smaller when it's at the end, so that we are able to draw the corner
+ if (atEnd) {
+ topRight += vectorLeft;
+ bottomRight += vectorLeft;
+ }
+
+ {
+ // Outer border
+ QLine topLine;
+ {
+ QPoint adjustTopLineLeft = (vectorRight * (marginLeft + (previousSelected ? 0 : 1))) +
+ (vectorDown * lowerTop);
+ QPoint adjustTopLineRight = (vectorDown * lowerTop);
+ if (atBeginning || selected)
+ adjustTopLineLeft += vectorRight;
+ if (atEnd || selected)
+ adjustTopLineRight += 2 * vectorLeft;
+
+ topLine = QLine(topLeft + adjustTopLineLeft, topRight + adjustTopLineRight);
+ }
+
+ QLine leftLine;
+ {
+ QPoint adjustLeftLineTop = (vectorRight * marginLeft) + (vectorDown * (lowerTop + 1));
+ QPoint adjustLeftLineBottom = (vectorRight * marginLeft) + (vectorUp * borderThickness);
+ if (atBeginning || selected)
+ adjustLeftLineTop += vectorDown; // Make place for rounded corner
+ if (atBeginning && selected)
+ adjustLeftLineBottom += borderThickness * vectorDown;
+ else if (selected)
+ adjustLeftLineBottom += vectorUp;
+
+ leftLine = QLine(topLeft + adjustLeftLineTop, bottomLeft + adjustLeftLineBottom);
+ }
+
+ QLine rightLine;
+ {
+ QPoint adjustRightLineTop = vectorDown * (2 + lowerTop);
+ QPoint adjustRightLineBottom = vectorUp * borderThickness;
+ if (selected)
+ adjustRightLineBottom += vectorUp;
+
+ rightLine = QLine(topRight + adjustRightLineTop, bottomRight + adjustRightLineBottom);
+ }
+
+ // Background
+ QPoint startPoint = topLine.p1() + vectorDown + vectorLeft;
+ if (mouseOver)
+ startPoint += vectorDown;
+ QPoint endPoint = rightLine.p2();
+
+ if (tab->state & State_Enabled) {
+ QRect fillRect = QRect(startPoint, endPoint).normalized();
+ if (fillRect.isValid()) {
+ if (selected) {
+ fillRect = QRect(startPoint, endPoint + vectorLeft + vectorDown * 3).normalized();
+ painter->fillRect(fillRect, option->palette.window());
+
+ // Connect to the base
+ painter->setPen(QPen(option->palette.window(), 0));
+ QVarLengthArray<QPoint, 6> points;
+ points.append(rightLine.p2() + vectorDown);
+ points.append(rightLine.p2() + vectorDown + vectorDown);
+ points.append(rightLine.p2() + vectorDown + vectorDown + vectorRight);
+ if (tab->position != QStyleOptionTab::Beginning) {
+ points.append(leftLine.p2() + vectorDown);
+ points.append(leftLine.p2() + vectorDown + vectorDown);
+ points.append(leftLine.p2() + vectorDown + vectorDown + vectorLeft);
+ }
+ painter->drawPoints(points.constData(), points.size());
+ } else {
+ QBrush buttonGradientBrush;
+ QBrush buttonBrush = qMapBrushToRect(option->palette.button(), fillRect);
+ if (buttonBrush.gradient() || !buttonBrush.texture().isNull()) {
+ buttonGradientBrush = buttonBrush;
+ } else {
+ // Generate gradients
+ QLinearGradient buttonGradient(fillRect.topLeft(), fillRect.bottomLeft());
+ buttonGradient.setColorAt(0.0, buttonBrush.color().lighter(104));
+ buttonGradient.setColorAt(1.0, buttonBrush.color().darker(110));
+ buttonGradientBrush = QBrush(buttonGradient);
+ }
+
+ painter->fillRect(fillRect, buttonGradientBrush);
+ }
+ }
+ }
+
+ QPoint rightCornerDot = topRight + vectorLeft + (lowerTop + 1)*vectorDown;
+ QPoint leftCornerDot = topLeft + (marginLeft + 1)*vectorRight + (lowerTop + 1)*vectorDown;
+ QPoint bottomRightConnectToBase = rightLine.p2() + vectorRight + vectorDown;
+ QPoint bottomLeftConnectToBase = leftLine.p2() + vectorLeft + vectorDown;
+
+ painter->setPen(QPen(border, 0));
+
+ QVarLengthArray<QLine, 3> lines;
+ QVarLengthArray<QPoint, 7> points;
+
+ lines.append(topLine);
+
+ if (mouseOver) {
+ painter->drawLines(lines.constData(), lines.count());
+ lines.clear();
+
+ QLine secondHoverLine = QLine(topLine.p1() + vectorDown * 2 + vectorLeft, topLine.p2() + vectorDown * 2 + vectorRight);
+ painter->setPen(highlightedLightInnerBorderColor);
+ painter->drawLine(secondHoverLine);
+ }
+
+ if (mouseOver)
+ painter->setPen(QPen(border, 0));
+
+ if (!previousSelected)
+ lines.append(leftLine);
+ if (atEnd || selected) {
+ lines.append(rightLine);
+ points.append(rightCornerDot);
+ }
+ if (atBeginning || selected)
+ points.append(leftCornerDot);
+ if (selected) {
+ points.append(bottomRightConnectToBase);
+ points.append(bottomLeftConnectToBase);
+ }
+ if (lines.size() > 0) {
+ painter->drawLines(lines.constData(), lines.size());
+ lines.clear();
+ }
+ if (points.size() > 0) {
+ painter->drawPoints(points.constData(), points.size());
+ points.clear();
+ }
+
+ // Antialiasing
+ painter->setPen(QPen(corner, 0));
+ if (atBeginning || selected)
+ points.append(topLine.p1() + vectorLeft);
+ if (!previousSelected)
+ points.append(leftLine.p1() + vectorUp);
+ if (atEnd || selected) {
+ points.append(topLine.p2() + vectorRight);
+ points.append(rightLine.p1() + vectorUp);
+ }
+
+ if (selected) {
+ points.append(bottomRightConnectToBase + vectorLeft);
+ if (!atBeginning) {
+ points.append(bottomLeftConnectToBase + vectorRight);
+
+ if (((tab->position == QStyleOptionTab::Beginning) || onlyTab) && leftCornerWidget) {
+ // A special case: When the first tab is selected and
+ // has a left corner widget, it needs to do more work
+ // to connect to the base
+ QPoint p1 = bottomLeftConnectToBase + vectorDown;
+
+ points.append(p1);
+ }
+ }
+ }
+ if (points.size() > 0) {
+ painter->drawPoints(points.constData(), points.size());
+ points.clear();
+ }
+
+ // Inner border
+ QLine innerTopLine = QLine(topLine.p1() + vectorDown, topLine.p2() + vectorDown);
+ if (!selected) {
+ QLinearGradient topLineGradient(innerTopLine.p1(),innerTopLine.p2());
+ topLineGradient.setColorAt(0, lightShadowGradientStartColor);
+ topLineGradient.setColorAt(1, lightShadowGradientStopColor);
+ painter->setPen(QPen(mouseOver ? QBrush(highlightedDarkInnerBorderColor) : QBrush(topLineGradient), 1));
+ } else {
+ painter->setPen(QPen(innerTopLeft, 0));
+ }
+ painter->drawLine(innerTopLine);
+
+ QLine innerLeftLine = QLine(leftLine.p1() + vectorRight + vectorDown, leftLine.p2() + vectorRight);
+ QLine innerRightLine = QLine(rightLine.p1() + vectorLeft + vectorDown, rightLine.p2() + vectorLeft);
+
+ if (selected) {
+ innerRightLine = QLine(innerRightLine.p1() + vectorUp, innerRightLine.p2());
+ innerLeftLine = QLine(innerLeftLine.p1() + vectorUp, innerLeftLine.p2());
+ }
+
+ if (selected || atBeginning) {
+ QBrush leftLineGradientBrush;
+ QRect rect = QRect(innerLeftLine.p1(), innerLeftLine.p2()).normalized();
+ QBrush buttonBrush = qMapBrushToRect(option->palette.button(), rect);
+ if (buttonBrush.gradient() || !buttonBrush.texture().isNull()) {
+ leftLineGradientBrush = qBrushLight(buttonBrush, 105);
+ } else {
+ QLinearGradient buttonGradient3(rect.topLeft(), rect.bottomLeft());
+ buttonGradient3.setColorAt(0.0, buttonBrush.color().lighter(105));
+ buttonGradient3.setColorAt(1.0, buttonBrush.color());
+ leftLineGradientBrush = QBrush(buttonGradient3);
+ }
+
+ if (!selected)
+ painter->setPen(QPen(leftLineGradientBrush, 0));
+
+ // Assume the sun is on the same side in Right-To-Left layouts and draw the
+ // light shadow on the left side always (the right line is on the left side in
+ // reverse layouts for north and south)
+ if (reverseShadow)
+ painter->drawLine(innerRightLine);
+ else
+ painter->drawLine(innerLeftLine);
+ }
+
+ if (atEnd || selected) {
+ if (!selected) {
+ QBrush rightLineGradientBrush;
+ QRect rect = QRect(innerRightLine.p1(), innerRightLine.p2()).normalized();
+ QBrush buttonBrush = qMapBrushToRect(option->palette.button(), rect);
+ if (buttonBrush.gradient() || !buttonBrush.texture().isNull()) {
+ rightLineGradientBrush = qBrushDark(buttonBrush, 105);
+ } else {
+ QLinearGradient buttonGradient4(rect.topLeft(), rect.bottomLeft());
+ buttonGradient4.setColorAt(0.0, buttonBrush.color());
+ buttonGradient4.setColorAt(1.0, buttonBrush.color().darker(110));
+ rightLineGradientBrush = QBrush(buttonGradient4);
+ }
+
+ painter->setPen(QPen(rightLineGradientBrush, 0));
+ } else {
+ painter->setPen(QPen(innerBottomRight, 0));
+ }
+
+ if (reverseShadow)
+ painter->drawLine(innerLeftLine);
+ else
+ painter->drawLine(innerRightLine);
+ }
+
+
+ // Base
+ QLine baseLine = QLine(bottomLeft + marginLeft * 2 * vectorRight, bottomRight);
+ {
+
+ QPoint adjustedLeft;
+ QPoint adjustedRight;
+
+ if (atEnd && !selected) {
+ baseLine = QLine(baseLine.p1(), baseLine.p2() + vectorRight);
+ }
+
+ if (nextSelected) {
+ adjustedRight += vectorLeft;
+ baseLine = QLine(baseLine.p1(), baseLine.p2() + vectorLeft);
+ }
+ if (previousSelected) {
+ adjustedLeft += vectorRight;
+ baseLine = QLine(baseLine.p1() + vectorRight, baseLine.p2());
+ }
+ if (atBeginning)
+ adjustedLeft += vectorRight;
+
+ painter->setPen(QPen(baseColor2, 0));
+ if (!selected)
+ painter->drawLine(baseLine);
+
+ if (atEnd && !selected)
+ painter->drawPoint(baseLine.p2() + vectorRight);
+
+ if (atBeginning && !selected)
+ adjustedLeft = vectorRight;
+ else
+ adjustedLeft = QPoint(0, 0);
+ painter->setPen(QPen(baseColor1, 0));
+ if (!selected)
+ painter->drawLine(bottomLeft + vectorUp + adjustedLeft, baseLine.p2() + vectorUp);
+
+ QPoint endPoint = bottomRight + vectorUp;
+ if (atEnd && !selected)
+ painter->drawPoint(endPoint);
+
+ // For drawing a lower left "fake" corner on the base when the first tab is unselected
+ if (atBeginning && !selected) {
+ painter->drawPoint(baseLine.p1() + vectorLeft);
+ }
+
+ painter->setPen(QPen(corner, 0));
+ if (nextSelected)
+ painter->drawPoint(endPoint);
+ else if (selected)
+ painter->drawPoint(endPoint + vectorRight);
+
+ // For drawing a lower left "fake" corner on the base when the first tab is unselected
+ if (atBeginning && !selected) {
+ painter->drawPoint(baseLine.p1() + 2 * vectorLeft);
+ }
+ }
+ }
+ }
+
+ // Yay we're done
+
+ painter->restore();
+ }
+ break;
+#endif // QT_NO_TABBAR
+#ifndef QT_NO_PROGRESSBAR
+ case CE_ProgressBarGroove:
+ if (const QStyleOptionProgressBar *bar = qstyleoption_cast<const QStyleOptionProgressBar *>(option)) {
+ QRect rect = bar->rect;
+ QPen oldPen = painter->pen();
+
+ QLine lines[4];
+
+ // outline
+ painter->setPen(borderColor);
+ lines[0] = QLine(rect.left() + 2, rect.top(), rect.right() - 2, rect.top());
+ lines[1] = QLine(rect.left() + 2, rect.bottom(), rect.right() - 2, rect.bottom());
+ lines[2] = QLine(rect.left(), rect.top() + 2, rect.left(), rect.bottom() - 2);
+ lines[3] = QLine(rect.right(), rect.top() + 2, rect.right(), rect.bottom() - 2);
+ painter->drawLines(lines, 4);
+
+ QPoint points[8];
+ points[0] = QPoint(rect.left() + 1, rect.top() + 1);
+ points[1] = QPoint(rect.right() - 1, rect.top() + 1);
+ points[2] = QPoint(rect.left() + 1, rect.bottom() - 1);
+ points[3] = QPoint(rect.right() - 1, rect.bottom() - 1);
+ painter->drawPoints(points, 4);
+
+ // alpha corners
+ painter->setPen(alphaCornerColor);
+ points[0] = QPoint(rect.left(), rect.top() + 1);
+ points[1] = QPoint(rect.left() + 1, rect.top());
+ points[2] = QPoint(rect.right(), rect.top() + 1);
+ points[3] = QPoint(rect.right() - 1, rect.top());
+ points[4] = QPoint(rect.left(), rect.bottom() - 1);
+ points[5] = QPoint(rect.left() + 1, rect.bottom());
+ points[6] = QPoint(rect.right(), rect.bottom() - 1);
+ points[7] = QPoint(rect.right() - 1, rect.bottom());
+ painter->drawPoints(points, 8);
+
+ // inner outline, north-west
+ painter->setPen(gradientStartColor.darker(105));
+ lines[0] = QLine(rect.left() + 2, rect.top() + 1, rect.right() - 2, rect.top() + 1);
+ lines[1] = QLine(rect.left() + 1, rect.top() + 2, rect.left() + 1, rect.bottom() - 2);
+ painter->drawLines(lines, 2);
+
+ // base of the groove
+ painter->setPen(QPen());
+ painter->fillRect(rect.adjusted(2, 2, -2, -1), QBrush(bar->palette.base().color()));
+ painter->setPen(bar->palette.base().color());
+ painter->drawLine(rect.right() - 1, rect.top() + 2, rect.right() - 1, rect.bottom() - 2);
+
+ painter->setPen(oldPen);
+ }
+ break;
+ case CE_ProgressBarLabel:
+ if (const QStyleOptionProgressBar *bar = qstyleoption_cast<const QStyleOptionProgressBar *>(option)) {
+ // The busy indicator doesn't draw a label
+ if (bar->minimum == 0 && bar->maximum == 0)
+ return;
+
+ painter->save();
+
+ QRect rect = bar->rect;
+ QRect leftRect;
+
+ QFont font;
+ font.setBold(true);
+ painter->setFont(font);
+ painter->setPen(bar->palette.text().color());
+
+ bool vertical = false;
+ bool inverted = false;
+ bool bottomToTop = false;
+ // Get extra style options if version 2
+ if (const QStyleOptionProgressBarV2 *bar2 = qstyleoption_cast<const QStyleOptionProgressBarV2 *>(option)) {
+ vertical = (bar2->orientation == Qt::Vertical);
+ inverted = bar2->invertedAppearance;
+ bottomToTop = bar2->bottomToTop;
+ }
+
+ if (vertical) {
+ rect = QRect(rect.left(), rect.top(), rect.height(), rect.width()); // flip width and height
+ QTransform m;
+ if (bottomToTop) {
+ m.translate(0.0, rect.width());
+ m.rotate(-90);
+ } else {
+ m.translate(rect.height(), 0.0);
+ m.rotate(90);
+ }
+ painter->setTransform(m, true);
+ }
+
+ double vc6_workaround = ((bar->progress - qint64(bar->minimum)) / qMax(double(1.0), double(qint64(bar->maximum) - qint64(bar->minimum))) * rect.width());
+ int progressIndicatorPos = int(vc6_workaround);
+
+ bool flip = (!vertical && (((bar->direction == Qt::RightToLeft) && !inverted)
+ || ((bar->direction == Qt::LeftToRight) && inverted))) || (vertical && ((!inverted && !bottomToTop) || (inverted && bottomToTop)));
+ if (flip) {
+ int indicatorPos = rect.width() - progressIndicatorPos;
+ if (indicatorPos >= 0 && indicatorPos <= rect.width()) {
+ painter->setPen(bar->palette.base().color());
+ leftRect = QRect(rect.left(), rect.top(), indicatorPos, rect.height());
+ } else if (indicatorPos > rect.width()) {
+ painter->setPen(bar->palette.text().color());
+ } else {
+ painter->setPen(bar->palette.base().color());
+ }
+ } else {
+ if (progressIndicatorPos >= 0 && progressIndicatorPos <= rect.width()) {
+ leftRect = QRect(rect.left(), rect.top(), progressIndicatorPos, rect.height());
+ } else if (progressIndicatorPos > rect.width()) {
+ painter->setPen(bar->palette.base().color());
+ } else {
+ painter->setPen(bar->palette.text().color());
+ }
+ }
+
+ QRegion rightRect = rect;
+ rightRect = rightRect.subtracted(leftRect);
+ painter->setClipRegion(rightRect);
+ painter->drawText(rect, bar->text, QTextOption(Qt::AlignAbsolute | Qt::AlignHCenter | Qt::AlignVCenter));
+ if (!leftRect.isNull()) {
+ painter->setPen(flip ? bar->palette.text().color() : bar->palette.base().color());
+ painter->setClipRect(leftRect);
+ painter->drawText(rect, bar->text, QTextOption(Qt::AlignAbsolute | Qt::AlignHCenter | Qt::AlignVCenter));
+ }
+
+ painter->restore();
+ }
+ break;
+ case CE_ProgressBarContents:
+ if (const QStyleOptionProgressBar *bar = qstyleoption_cast<const QStyleOptionProgressBar *>(option)) {
+ Q_D(const QPlastiqueStyle);
+ QRect rect = bar->rect;
+ bool vertical = false;
+ bool inverted = false;
+ bool indeterminate = (bar->minimum == 0 && bar->maximum == 0);
+ if (!indeterminate && bar->progress == -1)
+ break;
+
+ painter->save();
+
+ // Get extra style options if version 2
+ if (const QStyleOptionProgressBarV2 *bar2 = qstyleoption_cast<const QStyleOptionProgressBarV2 *>(option)) {
+ vertical = (bar2->orientation == Qt::Vertical);
+ inverted = bar2->invertedAppearance;
+ }
+
+ // If the orientation is vertical, we use a transform to rotate
+ // the progress bar 90 degrees clockwise. This way we can use the
+ // same rendering code for both orientations.
+ if (vertical) {
+ rect = QRect(rect.left(), rect.top(), rect.height(), rect.width()); // flip width and height
+ QTransform m;
+ m.translate(rect.height()-1, 0);
+ m.rotate(90.0);
+ painter->setTransform(m, true);
+ }
+
+ int maxWidth = rect.width() - 4;
+ int minWidth = 4;
+ qint64 progress = qMax<qint64>(bar->progress, bar->minimum); // workaround for bug in QProgressBar
+ double vc6_workaround = ((progress - qint64(bar->minimum)) / qMax(double(1.0), double(qint64(bar->maximum) - qint64(bar->minimum))) * maxWidth);
+ int width = indeterminate ? maxWidth : qMax(int(vc6_workaround), minWidth);
+ bool reverse = (!vertical && (bar->direction == Qt::RightToLeft)) || vertical;
+ if (inverted)
+ reverse = !reverse;
+
+ QRect progressBar;
+ if (!indeterminate) {
+ if (!reverse) {
+ progressBar.setRect(rect.left() + 2, rect.top() + 2, width, rect.height() - 4);
+ } else {
+ progressBar.setRect(rect.right() - 1 - width, rect.top() + 2, width, rect.height() - 4);
+ }
+ } else {
+ int slideWidth = ((rect.width() - 4) * 2) / 3;
+ int step = ((d->animateStep * slideWidth) / ProgressBarFps) % slideWidth;
+ if ((((d->animateStep * slideWidth) / ProgressBarFps) % (2 * slideWidth)) >= slideWidth)
+ step = slideWidth - step;
+ progressBar.setRect(rect.left() + 2 + step, rect.top() + 2,
+ slideWidth / 2, rect.height() - 4);
+ }
+
+ // outline
+ painter->setPen(highlightedDarkInnerBorderColor);
+
+ QVarLengthArray<QLine, 4> lines;
+ QVarLengthArray<QPoint, 8> points;
+ if (!reverse) {
+ if (width == minWidth) {
+ points.append(QPoint(progressBar.left() + 1, progressBar.top()));
+ points.append(QPoint(progressBar.left() + 1, progressBar.bottom()));
+ } else {
+ if (indeterminate) {
+ lines.append(QLine(progressBar.left() + 2, progressBar.top(),
+ progressBar.right() - 2, progressBar.top()));
+ lines.append(QLine(progressBar.left() + 2, progressBar.bottom(),
+ progressBar.right() - 2, progressBar.bottom()));
+ } else {
+ lines.append(QLine(progressBar.left() + 1, progressBar.top(),
+ progressBar.right() - 2, progressBar.top()));
+ lines.append(QLine(progressBar.left() + 1, progressBar.bottom(),
+ progressBar.right() - 2, progressBar.bottom()));
+ }
+ }
+
+ if (indeterminate) {
+ lines.append(QLine(progressBar.left(), progressBar.top() + 2,
+ progressBar.left(), progressBar.bottom() - 2));
+ } else {
+ lines.append(QLine(progressBar.left(), progressBar.top() + 1,
+ progressBar.left(), progressBar.bottom() - 1));
+ }
+ lines.append(QLine(progressBar.right(), progressBar.top() + 2,
+ progressBar.right(), progressBar.bottom() - 2));
+ } else {
+ if (width == minWidth) {
+ points.append(QPoint(progressBar.right() - 1, progressBar.top()));
+ points.append(QPoint(progressBar.right() - 1, progressBar.bottom()));
+ } else {
+ if (indeterminate) {
+ lines.append(QLine(progressBar.right() - 2, progressBar.top(),
+ progressBar.left() + 2, progressBar.top()));
+ lines.append(QLine(progressBar.right() - 2, progressBar.bottom(),
+ progressBar.left() + 2, progressBar.bottom()));
+ } else {
+ lines.append(QLine(progressBar.right() - 1, progressBar.top(),
+ progressBar.left() + 2, progressBar.top()));
+ lines.append(QLine(progressBar.right() - 1, progressBar.bottom(),
+ progressBar.left() + 2, progressBar.bottom()));
+ }
+ }
+ if (indeterminate) {
+ lines.append(QLine(progressBar.right(), progressBar.top() + 2,
+ progressBar.right(), progressBar.bottom() - 2));
+ } else {
+ lines.append(QLine(progressBar.right(), progressBar.top() + 1,
+ progressBar.right(), progressBar.bottom() - 1));
+ }
+ lines.append(QLine(progressBar.left(), progressBar.top() + 2,
+ progressBar.left(), progressBar.bottom() - 2));
+ }
+
+ if (points.size() > 0) {
+ painter->drawPoints(points.constData(), points.size());
+ points.clear();
+ }
+ painter->drawLines(lines.constData(), lines.size());
+ lines.clear();
+
+ // alpha corners
+ painter->setPen(alphaInnerColor);
+ if (!reverse) {
+ if (indeterminate) {
+ points.append(QPoint(progressBar.left() + 1, progressBar.top()));
+ points.append(QPoint(progressBar.left(), progressBar.top() + 1));
+ points.append(QPoint(progressBar.left() + 1, progressBar.bottom()));
+ points.append(QPoint(progressBar.left(), progressBar.bottom() - 1));
+ } else {
+ points.append(QPoint(progressBar.left(), progressBar.top()));
+ points.append(QPoint(progressBar.left(), progressBar.bottom()));
+ }
+ points.append(QPoint(progressBar.right() - 1, progressBar.top()));
+ points.append(QPoint(progressBar.right(), progressBar.top() + 1));
+ points.append(QPoint(progressBar.right() - 1, progressBar.bottom()));
+ points.append(QPoint(progressBar.right(), progressBar.bottom() - 1));
+ } else {
+ if (indeterminate) {
+ points.append(QPoint(progressBar.right() - 1, progressBar.top()));
+ points.append(QPoint(progressBar.right(), progressBar.top() + 1));
+ points.append(QPoint(progressBar.right() - 1, progressBar.bottom()));
+ points.append(QPoint(progressBar.right(), progressBar.bottom() - 1));
+ } else {
+ points.append(QPoint(progressBar.right(), progressBar.top()));
+ points.append(QPoint(progressBar.right(), progressBar.bottom()));
+ }
+ points.append(QPoint(progressBar.left() + 1, progressBar.top()));
+ points.append(QPoint(progressBar.left(), progressBar.top() + 1));
+ points.append(QPoint(progressBar.left() + 1, progressBar.bottom()));
+ points.append(QPoint(progressBar.left(), progressBar.bottom() - 1));
+ }
+
+ painter->drawPoints(points.constData(), points.size());
+ points.clear();
+
+ // contents
+ painter->setPen(QPen());
+
+ QString progressBarName = uniqueName(QLatin1String("progressBarContents"),
+ option, rect.size());
+ QPixmap cache;
+ if ((!UsePixmapCache || !QPixmapCache::find(progressBarName, cache)) && rect.height() > 7) {
+ QSize size = rect.size();
+ cache = QPixmap(QSize(size.width() - 6 + 30, size.height() - 6));
+ cache.fill(Qt::white);
+ QPainter cachePainter(&cache);
+ QRect pixmapRect(0, 0, cache.width(), cache.height());
+
+ int leftEdge = 0;
+ bool flip = false;
+ while (leftEdge < cache.width() + 1) {
+ QColor rectColor = option->palette.highlight().color();
+ QColor lineColor = option->palette.highlight().color();
+ if (flip) {
+ flip = false;
+ rectColor = rectColor.lighter(105);
+ lineColor = lineColor.lighter(105);
+ } else {
+ flip = true;
+ }
+
+ cachePainter.setPen(lineColor);
+ const QLine cacheLines[2] = {
+ QLine(pixmapRect.left() + leftEdge - 1, pixmapRect.top(),
+ pixmapRect.left() + leftEdge + 9, pixmapRect.top()),
+ QLine(pixmapRect.left() + leftEdge - 1, pixmapRect.bottom(),
+ pixmapRect.left() + leftEdge + 9, pixmapRect.bottom()) };
+ cachePainter.drawLines(cacheLines, 2);
+ cachePainter.fillRect(QRect(pixmapRect.left() + leftEdge, pixmapRect.top(),
+ 10, pixmapRect.height()), rectColor);
+
+ leftEdge += 10;
+ }
+
+ if (UsePixmapCache)
+ QPixmapCache::insert(progressBarName, cache);
+ }
+ painter->setClipRect(progressBar.adjusted(1, 0, -1, -1));
+
+ if (!vertical)
+ progressBar.adjust(0, 1, 0, 1);
+ if (!indeterminate) {
+ int step = (AnimateProgressBar || (indeterminate && AnimateBusyProgressBar)) ? (d->animateStep % 20) : 0;
+ if (reverse)
+ painter->drawPixmap(progressBar.left() - 25 + step, progressBar.top(), cache);
+ else
+ painter->drawPixmap(progressBar.left() - 25 - step + width % 20, progressBar.top(), cache);
+ } else {
+ painter->drawPixmap(progressBar.left(), progressBar.top(), cache);
+ }
+
+ painter->restore();
+ }
+ break;
+#endif // QT_NO_PROGRESSBAR
+ case CE_HeaderSection:
+ // Draws the header in tables.
+ if (const QStyleOptionHeader *header = qstyleoption_cast<const QStyleOptionHeader *>(option)) {
+ QPixmap cache;
+ QString pixmapName = uniqueName(QLatin1String("headersection"), option, option->rect.size());
+ pixmapName += QLatin1String("-") + QString::number(int(header->position));
+ pixmapName += QLatin1String("-") + QString::number(int(header->orientation));
+
+ if (!UsePixmapCache || !QPixmapCache::find(pixmapName, cache)) {
+ cache = QPixmap(option->rect.size());
+ cache.fill(Qt::white);
+ QRect pixmapRect(0, 0, option->rect.width(), option->rect.height());
+ QPainter cachePainter(&cache);
+
+ bool sunken = (header->state & State_Enabled) && (header->state & State_Sunken);
+
+ QColor headerGradientStart = sunken ? option->palette.background().color().darker(114) : gradientStartColor;
+ QColor headerGradientStop = sunken ? option->palette.background().color().darker(106) : gradientStopColor;
+
+ QColor lightLine = sunken ? option->palette.background().color().darker(118) : gradientStartColor;
+ QColor darkLine = sunken ? option->palette.background().color().darker(110) : gradientStopColor.darker(105);
+
+ qt_plastique_draw_gradient(&cachePainter, pixmapRect,
+ headerGradientStart, headerGradientStop);
+
+ cachePainter.setPen(borderColor);
+ cachePainter.drawRect(pixmapRect.adjusted(0, 0, -1, -1));
+ cachePainter.setPen(alphaCornerColor);
+
+ const QPoint points[4] = {
+ pixmapRect.topLeft(), pixmapRect.topRight(),
+ pixmapRect.bottomLeft(), pixmapRect.bottomRight() };
+ cachePainter.drawPoints(points, 4);
+
+ QLine lines[2];
+
+ // inner lines
+ cachePainter.setPen(lightLine);
+ lines[0] = QLine(pixmapRect.left() + 2, pixmapRect.top() + 1,
+ pixmapRect.right() - 2, pixmapRect.top() + 1);
+ lines[1] = QLine(pixmapRect.left() + 1, pixmapRect.top() + 2,
+ pixmapRect.left() + 1, pixmapRect.bottom() - 2);
+ cachePainter.drawLines(lines, 2);
+
+ cachePainter.setPen(darkLine);
+ lines[0] = QLine(pixmapRect.left() + 2, pixmapRect.bottom() - 1,
+ pixmapRect.right() - 2, pixmapRect.bottom() - 1);
+ lines[1] = QLine(pixmapRect.right() - 1, pixmapRect.bottom() - 2,
+ pixmapRect.right() - 1, pixmapRect.top() + 2);
+ cachePainter.drawLines(lines, 2);
+
+ cachePainter.end();
+ if (UsePixmapCache)
+ QPixmapCache::insert(pixmapName, cache);
+ }
+ painter->drawPixmap(option->rect.topLeft(), cache);
+
+ }
+ break;
+#ifndef QT_NO_MENU
+ case CE_MenuItem:
+ // Draws one item in a popup menu.
+ if (const QStyleOptionMenuItem *menuItem = qstyleoption_cast<const QStyleOptionMenuItem *>(option)) {
+ painter->save();
+ QBrush textBrush;
+ if (option->palette.resolve() & (1 << QPalette::ButtonText))
+ textBrush = option->palette.buttonText();
+ else
+ textBrush = option->palette.windowText(); // KDE uses windowText rather than buttonText for menus
+
+ if (menuItem->menuItemType == QStyleOptionMenuItem::Separator) {
+ painter->fillRect(menuItem->rect, option->palette.background().color().lighter(103));
+
+ int w = 0;
+ if (!menuItem->text.isEmpty()) {
+ painter->setFont(menuItem->font);
+ drawItemText(painter, menuItem->rect.adjusted(5, 0, -5, 0), Qt::AlignLeft | Qt::AlignVCenter,
+ menuItem->palette, menuItem->state & State_Enabled, menuItem->text,
+ QPalette::Text);
+ w = menuItem->fontMetrics.width(menuItem->text) + 5;
+ }
+
+ painter->setPen(alphaCornerColor);
+ bool reverse = menuItem->direction == Qt::RightToLeft;
+ painter->drawLine(menuItem->rect.left() + 5 + (reverse ? 0 : w), menuItem->rect.center().y(),
+ menuItem->rect.right() - 5 - (reverse ? w : 0), menuItem->rect.center().y());
+
+ painter->restore();
+ break;
+ }
+
+ bool selected = menuItem->state & State_Selected;
+ bool checkable = menuItem->checkType != QStyleOptionMenuItem::NotCheckable;
+ bool checked = menuItem->checked;
+
+ if (selected) {
+ qt_plastique_draw_gradient(painter, menuItem->rect,
+ option->palette.highlight().color().lighter(105),
+ option->palette.highlight().color().darker(110));
+
+ painter->setPen(option->palette.highlight().color().lighter(110));
+ painter->drawLine(option->rect.topLeft(), option->rect.topRight());
+ painter->setPen(option->palette.highlight().color().darker(115));
+ painter->drawLine(option->rect.bottomLeft(), option->rect.bottomRight());
+ } else {
+ painter->fillRect(option->rect, option->palette.background().color().lighter(103));
+ }
+
+ // Check
+ QRect checkRect(option->rect.left() + 7, option->rect.center().y() - 6, 13, 13);
+ checkRect = visualRect(menuItem->direction, menuItem->rect, checkRect);
+ if (checkable) {
+ if ((menuItem->checkType & QStyleOptionMenuItem::Exclusive) && menuItem->icon.isNull()) {
+ QStyleOptionButton button;
+ button.rect = checkRect;
+ button.state = menuItem->state;
+ if (checked)
+ button.state |= State_On;
+ button.palette = menuItem->palette;
+ drawPrimitive(PE_IndicatorRadioButton, &button, painter, widget);
+ } else {
+ if (menuItem->icon.isNull()) {
+ QStyleOptionButton button;
+ button.rect = checkRect;
+ button.state = menuItem->state;
+ if (checked)
+ button.state |= State_On;
+ button.palette = menuItem->palette;
+ drawPrimitive(PE_IndicatorCheckBox, &button, painter, widget);
+ } else if (checked) {
+ int iconSize = qMax(menuItem->maxIconWidth, 20);
+ QRect sunkenRect(option->rect.left() + 1,
+ option->rect.top() + (option->rect.height() - iconSize) / 2 + 1,
+ iconSize, iconSize);
+ sunkenRect = visualRect(menuItem->direction, menuItem->rect, sunkenRect);
+
+ QStyleOption opt = *option;
+ opt.state |= State_Sunken;
+ opt.rect = sunkenRect;
+ qt_plastique_drawShadedPanel(painter, &opt, false, widget);
+ }
+ }
+ }
+
+ // Text and icon, ripped from windows style
+ bool dis = !(menuItem->state & State_Enabled);
+ bool act = menuItem->state & State_Selected;
+ const QStyleOption *opt = option;
+ const QStyleOptionMenuItem *menuitem = menuItem;
+ int checkcol = qMax(menuitem->maxIconWidth, 20);
+ QPainter *p = painter;
+ QRect vCheckRect = visualRect(opt->direction, menuitem->rect,
+ QRect(menuitem->rect.x(), menuitem->rect.y(),
+ checkcol, menuitem->rect.height()));
+ if (!menuItem->icon.isNull()) {
+ QIcon::Mode mode = dis ? QIcon::Disabled : QIcon::Normal;
+ if (act && !dis)
+ mode = QIcon::Active;
+ QPixmap pixmap;
+ if (checked)
+ pixmap = menuItem->icon.pixmap(pixelMetric(PM_SmallIconSize, option, widget), mode, QIcon::On);
+ else
+ pixmap = menuItem->icon.pixmap(pixelMetric(PM_SmallIconSize, option, widget), mode);
+ int pixw = pixmap.width();
+ int pixh = pixmap.height();
+
+ QRect pmr(0, 0, pixw, pixh);
+ pmr.moveCenter(vCheckRect.center());
+ painter->setPen(textBrush.color());
+ if (checkable && checked)
+ painter->drawPixmap(QPoint(pmr.left() + 1, pmr.top() + 1), pixmap);
+ else
+ painter->drawPixmap(pmr.topLeft(), pixmap);
+ }
+
+ if (selected) {
+ painter->setPen(menuItem->palette.highlightedText().color());
+ } else {
+ painter->setPen(textBrush.color());
+ }
+ int x, y, w, h;
+ menuitem->rect.getRect(&x, &y, &w, &h);
+ int tab = menuitem->tabWidth;
+ QColor discol;
+ if (dis) {
+ discol = textBrush.color();
+ p->setPen(discol);
+ }
+ int xm = windowsItemFrame + checkcol + windowsItemHMargin;
+ int xpos = menuitem->rect.x() + xm;
+ QRect textRect(xpos, y + windowsItemVMargin, w - xm - windowsRightBorder - tab + 1, h - 2 * windowsItemVMargin);
+ QRect vTextRect = visualRect(opt->direction, menuitem->rect, textRect);
+ QString s = menuitem->text;
+ if (!s.isEmpty()) { // draw text
+ p->save();
+ int t = s.indexOf(QLatin1Char('\t'));
+ int text_flags = Qt::AlignVCenter | Qt::TextShowMnemonic | Qt::TextDontClip | Qt::TextSingleLine;
+ if (!styleHint(SH_UnderlineShortcut, menuitem, widget))
+ text_flags |= Qt::TextHideMnemonic;
+ text_flags |= Qt::AlignLeft;
+ if (t >= 0) {
+ QRect vShortcutRect = visualRect(opt->direction, menuitem->rect,
+ QRect(textRect.topRight(), QPoint(menuitem->rect.right(), textRect.bottom())));
+ if (dis && !act && styleHint(SH_EtchDisabledText, option, widget)) {
+ p->setPen(menuitem->palette.light().color());
+ p->drawText(vShortcutRect.adjusted(1,1,1,1), text_flags, s.mid(t + 1));
+ p->setPen(discol);
+ }
+ p->drawText(vShortcutRect, text_flags, s.mid(t + 1));
+ s = s.left(t);
+ }
+ QFont font = menuitem->font;
+ if (menuitem->menuItemType == QStyleOptionMenuItem::DefaultItem)
+ font.setBold(true);
+ p->setFont(font);
+ if (dis && !act && styleHint(SH_EtchDisabledText, option, widget)) {
+ p->setPen(menuitem->palette.light().color());
+ p->drawText(vTextRect.adjusted(1,1,1,1), text_flags, s.left(t));
+ p->setPen(discol);
+ }
+ p->drawText(vTextRect, text_flags, s.left(t));
+ p->restore();
+ }
+
+ // Arrow
+ if (menuItem->menuItemType == QStyleOptionMenuItem::SubMenu) {// draw sub menu arrow
+ int dim = (menuItem->rect.height() - 4) / 2;
+ PrimitiveElement arrow;
+ arrow = (opt->direction == Qt::RightToLeft) ? PE_IndicatorArrowLeft : PE_IndicatorArrowRight;
+ int xpos = menuItem->rect.left() + menuItem->rect.width() - 6 - 2 - dim;
+ QRect vSubMenuRect = visualRect(option->direction, menuItem->rect,
+ QRect(xpos, menuItem->rect.top() + menuItem->rect.height() / 2 - dim / 2, dim, dim));
+ QStyleOptionMenuItem newMI = *menuItem;
+ newMI.rect = vSubMenuRect;
+ newMI.state = option->state & State_Enabled;
+ if (selected)
+ newMI.palette.setColor(QPalette::ButtonText,
+ newMI.palette.highlightedText().color());
+ else
+ newMI.palette.setColor(QPalette::ButtonText, textBrush.color());
+ drawPrimitive(arrow, &newMI, painter, widget);
+ }
+
+ painter->restore();
+ }
+ break;
+#endif // QT_NO_MENU
+#ifndef QT_NO_MENUBAR
+ case CE_MenuBarItem:
+ // Draws a menu bar item; File, Edit, Help etc..
+ if ((option->state & State_Selected)) {
+ QPixmap cache;
+ QString pixmapName = uniqueName(QLatin1String("menubaritem"), option, option->rect.size());
+ if (!UsePixmapCache || !QPixmapCache::find(pixmapName, cache)) {
+ cache = QPixmap(option->rect.size());
+ cache.fill(Qt::white);
+ QRect pixmapRect(0, 0, option->rect.width(), option->rect.height());
+ QPainter cachePainter(&cache);
+
+ QRect rect = pixmapRect;
+
+ // gradient fill
+ if ((option->state & QStyle::State_Sunken) || (option->state & QStyle::State_On)) {
+ qt_plastique_draw_gradient(&cachePainter, rect.adjusted(1, 1, -1, -1),
+ option->palette.button().color().darker(114),
+ option->palette.button().color().darker(106));
+ } else {
+ qt_plastique_draw_gradient(&cachePainter, rect.adjusted(1, 1, -1, -1),
+ option->palette.background().color().lighter(105),
+ option->palette.background().color().darker(102));
+ }
+
+ // outer border and corners
+ cachePainter.setPen(borderColor);
+ cachePainter.drawRect(rect.adjusted(0, 0, -1, -1));
+ cachePainter.setPen(alphaCornerColor);
+
+ const QPoint points[4] = {
+ rect.topLeft(),
+ rect.topRight(),
+ rect.bottomLeft(),
+ rect.bottomRight() };
+ cachePainter.drawPoints(points, 4);
+
+ // inner border
+ if ((option->state & QStyle::State_Sunken) || (option->state & QStyle::State_On))
+ cachePainter.setPen(option->palette.button().color().darker(118));
+ else
+ cachePainter.setPen(gradientStartColor);
+
+ QLine lines[2];
+ lines[0] = QLine(rect.left() + 1, rect.top() + 1, rect.right() - 1, rect.top() + 1);
+ lines[1] = QLine(rect.left() + 1, rect.top() + 2, rect.left() + 1, rect.bottom() - 2);
+ cachePainter.drawLines(lines, 2);
+
+ if ((option->state & QStyle::State_Sunken) || (option->state & QStyle::State_On))
+ cachePainter.setPen(option->palette.button().color().darker(114));
+ else
+ cachePainter.setPen(gradientStopColor.darker(102));
+ lines[0] = QLine(rect.left() + 1, rect.bottom() - 1, rect.right() - 1, rect.bottom() - 1);
+ lines[1] = QLine(rect.right() - 1, rect.top() + 1, rect.right() - 1, rect.bottom() - 2);
+ cachePainter.drawLines(lines, 2);
+ cachePainter.end();
+ if (UsePixmapCache)
+ QPixmapCache::insert(pixmapName, cache);
+ }
+ painter->drawPixmap(option->rect.topLeft(), cache);
+ } else {
+ painter->fillRect(option->rect, option->palette.background());
+ }
+
+ if (const QStyleOptionMenuItem *mbi = qstyleoption_cast<const QStyleOptionMenuItem *>(option)) {
+ QStyleOptionMenuItem newMI = *mbi;
+ if (!(option->palette.resolve() & (1 << QPalette::ButtonText))) //KDE uses windowText rather than buttonText for menus
+ newMI.palette.setColor(QPalette::ButtonText, newMI.palette.windowText().color());
+ QCommonStyle::drawControl(element, &newMI, painter, widget);
+ }
+ break;
+
+#ifndef QT_NO_MAINWINDOW
+ case CE_MenuBarEmptyArea:
+ if (widget && qobject_cast<const QMainWindow *>(widget->parentWidget())) {
+ painter->fillRect(option->rect, option->palette.window());
+ QPen oldPen = painter->pen();
+ painter->setPen(QPen(option->palette.dark().color()));
+ painter->drawLine(option->rect.bottomLeft(), option->rect.bottomRight());
+ painter->setPen(oldPen);
+ }
+ break;
+#endif // QT_NO_MAINWINDOW
+
+#endif // QT_NO_MENUBAR
+
+#ifndef QT_NO_TOOLBOX
+ case CE_ToolBoxTabShape:
+ if (const QStyleOptionToolBox *toolBox = qstyleoption_cast<const QStyleOptionToolBox *>(option)) {
+ painter->save();
+
+ int width = toolBox->rect.width();
+ int diag = toolBox->rect.height() - 2;
+
+ // The essential points
+ QPoint rightMost;
+ QPoint rightEdge;
+ QPoint leftEdge;
+ QPoint leftMost;
+ QPoint leftOne;
+ QPoint rightOne;
+ QPoint upOne(0, -1);
+ QPoint downOne(0, 1);
+
+ if (toolBox->direction != Qt::RightToLeft) {
+ rightMost = QPoint(toolBox->rect.right(), toolBox->rect.bottom() - 2);
+ rightEdge = QPoint(toolBox->rect.right() - width / 10, toolBox->rect.bottom() - 2);
+ leftEdge = QPoint(toolBox->rect.right() - width / 10 - diag, toolBox->rect.top());
+ leftMost = QPoint(toolBox->rect.left(), toolBox->rect.top());
+ leftOne = QPoint(-1, 0);
+ rightOne = QPoint(1, 0);
+ } else {
+ rightMost = QPoint(toolBox->rect.left(), toolBox->rect.bottom() - 2);
+ rightEdge = QPoint(toolBox->rect.left() + width / 10, toolBox->rect.bottom() - 2);
+ leftEdge = QPoint(toolBox->rect.left() + width / 10 + diag, toolBox->rect.top());
+ leftMost = QPoint(toolBox->rect.right(), toolBox->rect.top());
+ leftOne = QPoint(1, 0);
+ rightOne = QPoint(-1, 0);
+ }
+
+ QLine lines[3];
+
+ // Draw the outline
+ painter->setPen(borderColor);
+ lines[0] = QLine(rightMost, rightEdge);
+ lines[1] = QLine(rightEdge + leftOne, leftEdge);
+ lines[2] = QLine(leftEdge + leftOne, leftMost);
+ painter->drawLines(lines, 3);
+ painter->setPen(toolBox->palette.base().color());
+ lines[0] = QLine(rightMost + downOne, rightEdge + downOne);
+ lines[1] = QLine(rightEdge + leftOne + downOne, leftEdge + downOne);
+ lines[2] = QLine(leftEdge + leftOne + downOne, leftMost + downOne);
+ painter->drawLines(lines, 3);
+
+ painter->restore();
+ }
+ break;
+#endif // QT_NO_TOOLBOX
+#ifndef QT_NO_SPLITTER
+ case CE_Splitter:
+ if ((option->state & State_Enabled) && (option->state & State_MouseOver))
+ painter->fillRect(option->rect, QColor(255, 255, 255, 128));
+ if (option->state & State_Horizontal) {
+ int height = option->rect.height() / 3;
+ QRect rect(option->rect.left() + (option->rect.width() / 2 - 1),
+ option->rect.center().y() - height / 2, 3, height);
+ qt_plastique_draw_handle(painter, option, rect, Qt::Horizontal, widget);
+ } else {
+ int width = option->rect.width() / 3;
+ QRect rect(option->rect.center().x() - width / 2,
+ option->rect.top() + (option->rect.height() / 2) - 1, width, 3);
+ qt_plastique_draw_handle(painter, option, rect, Qt::Vertical, widget);
+ }
+ break;
+#endif // QT_NO_SPLITTER
+#ifndef QT_NO_DOCKWIDGET
+ case CE_DockWidgetTitle:
+ if (const QStyleOptionDockWidget *dockWidget = qstyleoption_cast<const QStyleOptionDockWidget *>(option)) {
+ painter->save();
+
+ const QStyleOptionDockWidgetV2 *v2
+ = qstyleoption_cast<const QStyleOptionDockWidgetV2*>(dockWidget);
+ bool verticalTitleBar = v2 == 0 ? false : v2->verticalTitleBar;
+
+ // Find text width and title rect
+ int textWidth = option->fontMetrics.width(dockWidget->title);
+ int margin = 4;
+ QRect titleRect = subElementRect(SE_DockWidgetTitleBarText, option, widget);
+ QRect rect = dockWidget->rect;
+
+ if (verticalTitleBar) {
+ QRect r = rect;
+ QSize s = r.size();
+ s.transpose();
+ r.setSize(s);
+
+ titleRect = QRect(r.left() + rect.bottom()
+ - titleRect.bottom(),
+ r.top() + titleRect.left() - rect.left(),
+ titleRect.height(), titleRect.width());
+
+ painter->translate(r.left(), r.top() + r.width());
+ painter->rotate(-90);
+ painter->translate(-r.left(), -r.top());
+
+ rect = r;
+ }
+
+ // Chop and insert ellide into title if text is too wide
+ QString title = elliditide(dockWidget->title, dockWidget->fontMetrics, titleRect, &textWidth);
+
+ // Draw the toolbar handle pattern to the left and right of the text
+ QImage handle(qt_toolbarhandle);
+ alphaCornerColor.setAlpha(170);
+ handle.setColor(1, alphaCornerColor.rgba());
+ handle.setColor(2, mergedColors(alphaCornerColor, option->palette.light().color()).rgba());
+ handle.setColor(3, option->palette.light().color().rgba());
+
+ if (title.isEmpty()) {
+ // Joint handle if there's no title
+ QRect r;
+#ifdef QT3_SUPPORT
+ // Q3DockWindow doesn't need space for buttons
+ if (widget && widget->inherits("Q3DockWindowTitleBar")) {
+ r = rect;
+ } else
+#endif
+ r.setRect(titleRect.left(), titleRect.top(), titleRect.width(), titleRect.bottom());
+ int nchunks = (r.width() / handle.width()) - 1;
+ int indent = (r.width() - (nchunks * handle.width())) / 2;
+ for (int i = 0; i < nchunks; ++i) {
+ painter->drawImage(QPoint(r.left() + indent + i * handle.width(),
+ r.center().y() - handle.height() / 2),
+ handle);
+ }
+ } else {
+ // Handle pattern to the left of the title
+ QRect leftSide(titleRect.left(), titleRect.top(),
+ titleRect.width() / 2 - textWidth / 2 - margin, titleRect.bottom());
+ int nchunks = leftSide.width() / handle.width();
+ int indent = (leftSide.width() - (nchunks * handle.width())) / 2;
+ for (int i = 0; i < nchunks; ++i) {
+ painter->drawImage(QPoint(leftSide.left() + indent
+ + i * handle.width(),
+ leftSide.center().y()
+ - handle.height() / 2),
+ handle);
+ }
+
+ // Handle pattern to the right of the title
+ QRect rightSide = titleRect.adjusted(titleRect.width() / 2 + textWidth / 2 + margin, 0, 0, 0);
+ nchunks = rightSide.width() / handle.width();
+ indent = (rightSide.width() - (nchunks * handle.width())) / 2;
+ for (int j = 0; j < nchunks; ++j) {
+ painter->drawImage(QPoint(rightSide.left() + indent + j * handle.width(),
+ rightSide.center().y() - handle.height() / 2),
+ handle);
+ }
+ }
+
+ // Draw the text centered
+ QFont font = painter->font();
+ font.setPointSize(font.pointSize() - 1);
+ painter->setFont(font);
+ painter->setPen(dockWidget->palette.windowText().color());
+ painter->drawText(titleRect,
+ int(Qt::AlignHCenter | Qt::AlignVCenter | Qt::TextShowMnemonic),
+ title);
+
+ painter->restore();
+ }
+
+ break;
+#endif // QT_NO_DOCKWIDGET
+#ifndef QT_NO_TOOLBAR
+ case CE_ToolBar:
+ if (const QStyleOptionToolBar *toolBar = qstyleoption_cast<const QStyleOptionToolBar *>(option)) {
+ // Draws the light line above and the dark line below menu bars and
+ // tool bars.
+ QPen oldPen = painter->pen();
+ if (toolBar->toolBarArea == Qt::TopToolBarArea) {
+ if (toolBar->positionOfLine == QStyleOptionToolBar::End
+ || toolBar->positionOfLine == QStyleOptionToolBar::OnlyOne) {
+ // The end and onlyone top toolbar lines draw a double
+ // line at the bottom to blend with the central
+ // widget.
+ painter->setPen(option->palette.background().color().lighter(104));
+ painter->drawLine(option->rect.bottomLeft(), option->rect.bottomRight());
+ painter->setPen(alphaCornerColor);
+ painter->drawLine(option->rect.left(), option->rect.bottom() - 1,
+ option->rect.right(), option->rect.bottom() - 1);
+ } else {
+ // All others draw a single dark line at the bottom.
+ painter->setPen(alphaCornerColor);
+ painter->drawLine(option->rect.bottomLeft(), option->rect.bottomRight());
+ }
+ // All top toolbar lines draw a light line at the top.
+ painter->setPen(option->palette.background().color().lighter(104));
+ painter->drawLine(option->rect.topLeft(), option->rect.topRight());
+ } else if (toolBar->toolBarArea == Qt::BottomToolBarArea) {
+ if (toolBar->positionOfLine == QStyleOptionToolBar::End
+ || toolBar->positionOfLine == QStyleOptionToolBar::Middle) {
+ // The end and middle bottom tool bar lines draw a dark
+ // line at the bottom.
+ painter->setPen(alphaCornerColor);
+ painter->drawLine(option->rect.bottomLeft(), option->rect.bottomRight());
+ }
+ if (toolBar->positionOfLine == QStyleOptionToolBar::Beginning
+ || toolBar->positionOfLine == QStyleOptionToolBar::OnlyOne) {
+ // The beginning and only one tool bar lines draw a
+ // double line at the bottom to blend with the
+ // status bar.
+ // ### The styleoption could contain whether the
+ // main window has a menu bar and a status bar, and
+ // possibly dock widgets.
+ painter->setPen(alphaCornerColor);
+ painter->drawLine(option->rect.left(), option->rect.bottom() - 1,
+ option->rect.right(), option->rect.bottom() - 1);
+ painter->setPen(option->palette.background().color().lighter(104));
+ painter->drawLine(option->rect.bottomLeft(), option->rect.bottomRight());
+ }
+ if (toolBar->positionOfLine == QStyleOptionToolBar::End) {
+ painter->setPen(alphaCornerColor);
+ painter->drawLine(option->rect.topLeft(), option->rect.topRight());
+ painter->setPen(option->palette.background().color().lighter(104));
+ painter->drawLine(option->rect.left(), option->rect.top() + 1,
+ option->rect.right(), option->rect.top() + 1);
+
+ } else {
+ // All other bottom toolbars draw a light line at the top.
+ painter->setPen(option->palette.background().color().lighter(104));
+ painter->drawLine(option->rect.topLeft(), option->rect.topRight());
+ }
+ }
+ if (toolBar->toolBarArea == Qt::LeftToolBarArea) {
+ if (toolBar->positionOfLine == QStyleOptionToolBar::Middle
+ || toolBar->positionOfLine == QStyleOptionToolBar::End) {
+ // The middle and left end toolbar lines draw a light
+ // line to the left.
+ painter->setPen(option->palette.background().color().lighter(104));
+ painter->drawLine(option->rect.topLeft(), option->rect.bottomLeft());
+ }
+ if (toolBar->positionOfLine == QStyleOptionToolBar::End) {
+ // All other left toolbar lines draw a dark line to the right
+ painter->setPen(alphaCornerColor);
+ painter->drawLine(option->rect.right() - 1, option->rect.top(),
+ option->rect.right() - 1, option->rect.bottom());
+ painter->setPen(option->palette.background().color().lighter(104));
+ painter->drawLine(option->rect.topRight(), option->rect.bottomRight());
+ } else {
+ // All other left toolbar lines draw a dark line to the right
+ painter->setPen(alphaCornerColor);
+ painter->drawLine(option->rect.topRight(), option->rect.bottomRight());
+ }
+ } else if (toolBar->toolBarArea == Qt::RightToolBarArea) {
+ if (toolBar->positionOfLine == QStyleOptionToolBar::Middle
+ || toolBar->positionOfLine == QStyleOptionToolBar::End) {
+ // Right middle and end toolbar lines draw the dark right line
+ painter->setPen(alphaCornerColor);
+ painter->drawLine(option->rect.topRight(), option->rect.bottomRight());
+ }
+ if (toolBar->positionOfLine == QStyleOptionToolBar::End
+ || toolBar->positionOfLine == QStyleOptionToolBar::OnlyOne) {
+ // The right end and single toolbar draws the dark
+ // line on its left edge
+ painter->setPen(alphaCornerColor);
+ painter->drawLine(option->rect.topLeft(), option->rect.bottomLeft());
+ // And a light line next to it
+ painter->setPen(option->palette.background().color().lighter(104));
+ painter->drawLine(option->rect.left() + 1, option->rect.top(),
+ option->rect.left() + 1, option->rect.bottom());
+ } else {
+ // Other right toolbars draw a light line on its left edge
+ painter->setPen(option->palette.background().color().lighter(104));
+ painter->drawLine(option->rect.topLeft(), option->rect.bottomLeft());
+ }
+ }
+ painter->setPen(oldPen);
+ }
+ break;
+#endif // QT_NO_TOOLBAR
+#ifndef QT_NO_SCROLLBAR
+ case CE_ScrollBarAddLine:
+ if (const QStyleOptionSlider *scrollBar = qstyleoption_cast<const QStyleOptionSlider *>(option)) {
+
+ bool horizontal = scrollBar->orientation == Qt::Horizontal;
+ bool reverse = scrollBar->direction == Qt::RightToLeft;
+ bool sunken = scrollBar->state & State_Sunken;
+
+ QString addLinePixmapName = uniqueName(QLatin1String("scrollbar_addline"), option, option->rect.size());
+ QPixmap cache;
+ if (!UsePixmapCache || !QPixmapCache::find(addLinePixmapName, cache)) {
+ cache = QPixmap(option->rect.size());
+ cache.fill(Qt::white);
+ QRect pixmapRect(0, 0, cache.width(), cache.height());
+ QPainter addLinePainter(&cache);
+ addLinePainter.fillRect(pixmapRect, option->palette.background());
+
+ if (option->state & State_Enabled) {
+ // Gradient
+ QLinearGradient gradient(pixmapRect.center().x(), pixmapRect.top() + 2,
+ pixmapRect.center().x(), pixmapRect.bottom() - 2);
+ if ((scrollBar->activeSubControls & SC_ScrollBarAddLine) && sunken) {
+ gradient.setColorAt(0, gradientStopColor);
+ gradient.setColorAt(1, gradientStopColor);
+ } else {
+ gradient.setColorAt(0, gradientStartColor.lighter(105));
+ gradient.setColorAt(1, gradientStopColor);
+ }
+ addLinePainter.fillRect(pixmapRect.left() + 2, pixmapRect.top() + 2,
+ pixmapRect.right() - 3, pixmapRect.bottom() - 3,
+ gradient);
+ }
+
+ // Details
+ QImage addButton;
+ if (horizontal) {
+ addButton = QImage(reverse ? qt_scrollbar_button_left : qt_scrollbar_button_right);
+ } else {
+ addButton = QImage(qt_scrollbar_button_down);
+ }
+ addButton.setColor(1, alphaCornerColor.rgba());
+ addButton.setColor(2, borderColor.rgba());
+ if ((scrollBar->activeSubControls & SC_ScrollBarAddLine) && sunken) {
+ addButton.setColor(3, gradientStopColor.rgba());
+ addButton.setColor(4, gradientStopColor.rgba());
+ } else {
+ addButton.setColor(3, gradientStartColor.lighter(105).rgba());
+ addButton.setColor(4, gradientStopColor.rgba());
+ }
+ addButton.setColor(5, scrollBar->palette.text().color().rgba());
+ addLinePainter.drawImage(pixmapRect, addButton);
+
+ // Arrow
+ if (horizontal) {
+ QImage arrow(reverse ? qt_scrollbar_button_arrow_left : qt_scrollbar_button_arrow_right);
+ arrow.setColor(1, scrollBar->palette.foreground().color().rgba());
+
+ if ((scrollBar->activeSubControls & SC_ScrollBarAddLine) && sunken)
+ addLinePainter.translate(1, 1);
+ addLinePainter.drawImage(QPoint(pixmapRect.center().x() - 2, pixmapRect.center().y() - 3), arrow);
+ } else {
+ QImage arrow(qt_scrollbar_button_arrow_down);
+ arrow.setColor(1, scrollBar->palette.foreground().color().rgba());
+
+ if ((scrollBar->activeSubControls & SC_ScrollBarAddLine) && sunken)
+ addLinePainter.translate(1, 1);
+ addLinePainter.drawImage(QPoint(pixmapRect.center().x() - 3, pixmapRect.center().y() - 2), arrow);
+ }
+ addLinePainter.end();
+ if (UsePixmapCache)
+ QPixmapCache::insert(addLinePixmapName, cache);
+ }
+ painter->drawPixmap(option->rect.topLeft(), cache);
+ }
+ break;
+ case CE_ScrollBarSubPage:
+ case CE_ScrollBarAddPage:
+ if (const QStyleOptionSlider *scrollBar = qstyleoption_cast<const QStyleOptionSlider *>(option)) {
+ bool sunken = scrollBar->state & State_Sunken;
+ bool horizontal = scrollBar->orientation == Qt::Horizontal;
+
+ QString groovePixmapName = uniqueName(QLatin1String("scrollbar_groove"), option, option->rect.size());
+ if (sunken)
+ groovePixmapName += QLatin1String("-sunken");
+ if (element == CE_ScrollBarAddPage)
+ groovePixmapName += QLatin1String("-addpage");
+
+ QPixmap cache;
+ if (!UsePixmapCache || !QPixmapCache::find(groovePixmapName, cache)) {
+ cache = QPixmap(option->rect.size());
+ cache.fill(option->palette.background().color());
+ QPainter groovePainter(&cache);
+ QRect pixmapRect = QRect(0, 0, option->rect.width(), option->rect.height());
+ QColor color = scrollBar->palette.base().color().darker(sunken ? 125 : 100);
+ groovePainter.setBrushOrigin((element == CE_ScrollBarAddPage) ? pixmapRect.width() : 0,
+ (element == CE_ScrollBarAddPage) ? pixmapRect.height() : 0);
+ groovePainter.fillRect(pixmapRect, QBrush(color, Qt::Dense4Pattern));
+
+ QColor edgeColor = scrollBar->palette.base().color().darker(125);
+ if (horizontal) {
+ groovePainter.setBrushOrigin((element == CE_ScrollBarAddPage) ? pixmapRect.width() : 1, 0);
+ groovePainter.fillRect(QRect(pixmapRect.topLeft(), QSize(pixmapRect.width(), 1)),
+ QBrush(edgeColor, Qt::Dense4Pattern));
+ groovePainter.fillRect(QRect(pixmapRect.bottomLeft(), QSize(pixmapRect.width(), 1)),
+ QBrush(edgeColor, Qt::Dense4Pattern));
+ } else {
+ groovePainter.setBrushOrigin(0, (element == CE_ScrollBarAddPage) ? pixmapRect.height() : 1);
+ groovePainter.fillRect(QRect(pixmapRect.topLeft(), QSize(1, pixmapRect.height())),
+ QBrush(edgeColor, Qt::Dense4Pattern));
+ groovePainter.fillRect(QRect(pixmapRect.topRight(), QSize(1, pixmapRect.height())),
+ QBrush(edgeColor, Qt::Dense4Pattern));
+ }
+
+ groovePainter.end();
+ if (UsePixmapCache)
+ QPixmapCache::insert(groovePixmapName, cache);
+ }
+ painter->drawPixmap(option->rect.topLeft(), cache);
+ }
+ break;
+ case CE_ScrollBarSubLine:
+ if (const QStyleOptionSlider *scrollBar = qstyleoption_cast<const QStyleOptionSlider *>(option)) {
+ QRect scrollBarSubLine = scrollBar->rect;
+
+ bool horizontal = scrollBar->orientation == Qt::Horizontal;
+ bool isEnabled = scrollBar->state & State_Enabled;
+ bool reverse = scrollBar->direction == Qt::RightToLeft;
+ bool sunken = scrollBar->state & State_Sunken;
+
+ // The SubLine (up/left) buttons
+ QRect button1;
+ QRect button2;
+ int scrollBarExtent = pixelMetric(PM_ScrollBarExtent, option, widget);
+ if (horizontal) {
+ button1.setRect(scrollBarSubLine.left(), scrollBarSubLine.top(), scrollBarExtent, scrollBarSubLine.height());
+ button2.setRect(scrollBarSubLine.right() - (scrollBarExtent - 1), scrollBarSubLine.top(), scrollBarExtent, scrollBarSubLine.height());
+ } else {
+ button1.setRect(scrollBarSubLine.left(), scrollBarSubLine.top(), scrollBarSubLine.width(), scrollBarExtent);
+ button2.setRect(scrollBarSubLine.left(), scrollBarSubLine.bottom() - (scrollBarExtent - 1), scrollBarSubLine.width(), scrollBarExtent);
+ }
+
+ QString subLinePixmapName = uniqueName(QLatin1String("scrollbar_subline"), option, button1.size());
+ QPixmap cache;
+ if (!UsePixmapCache || !QPixmapCache::find(subLinePixmapName, cache)) {
+ cache = QPixmap(button1.size());
+ cache.fill(Qt::white);
+ QRect pixmapRect(0, 0, cache.width(), cache.height());
+ QPainter subLinePainter(&cache);
+ subLinePainter.fillRect(pixmapRect, option->palette.background());
+
+ if (isEnabled) {
+ // Gradients
+ if ((scrollBar->activeSubControls & SC_ScrollBarSubLine) && sunken) {
+ qt_plastique_draw_gradient(&subLinePainter,
+ QRect(pixmapRect.left() + 2, pixmapRect.top() + 2,
+ pixmapRect.right() - 3, pixmapRect.bottom() - 3),
+ gradientStopColor,
+ gradientStopColor);
+ } else {
+ qt_plastique_draw_gradient(&subLinePainter,
+ QRect(pixmapRect.left() + 2, pixmapRect.top() + 2,
+ pixmapRect.right() - 3, pixmapRect.bottom() - 3),
+ gradientStartColor.lighter(105),
+ gradientStopColor);
+ }
+ }
+
+ // Details
+ QImage subButton;
+ if (horizontal) {
+ subButton = QImage(reverse ? qt_scrollbar_button_right : qt_scrollbar_button_left);
+ } else {
+ subButton = QImage(qt_scrollbar_button_up);
+ }
+ subButton.setColor(1, alphaCornerColor.rgba());
+ subButton.setColor(2, borderColor.rgba());
+ if ((scrollBar->activeSubControls & SC_ScrollBarSubLine) && sunken) {
+ subButton.setColor(3, gradientStopColor.rgba());
+ subButton.setColor(4, gradientStopColor.rgba());
+ } else {
+ subButton.setColor(3, gradientStartColor.lighter(105).rgba());
+ subButton.setColor(4, gradientStopColor.rgba());
+ }
+ subButton.setColor(5, scrollBar->palette.text().color().rgba());
+ subLinePainter.drawImage(pixmapRect, subButton);
+
+ // Arrows
+ if (horizontal) {
+ QImage arrow(reverse ? qt_scrollbar_button_arrow_right : qt_scrollbar_button_arrow_left);
+ arrow.setColor(1, scrollBar->palette.foreground().color().rgba());
+
+ if ((scrollBar->activeSubControls & SC_ScrollBarSubLine) && sunken)
+ subLinePainter.translate(1, 1);
+ subLinePainter.drawImage(QPoint(pixmapRect.center().x() - 2, pixmapRect.center().y() - 3), arrow);
+ } else {
+ QImage arrow(qt_scrollbar_button_arrow_up);
+ arrow.setColor(1, scrollBar->palette.foreground().color().rgba());
+
+ if ((scrollBar->activeSubControls & SC_ScrollBarSubLine) && sunken)
+ subLinePainter.translate(1, 1);
+ subLinePainter.drawImage(QPoint(pixmapRect.center().x() - 3, pixmapRect.center().y() - 2), arrow);
+ }
+ subLinePainter.end();
+ if (UsePixmapCache)
+ QPixmapCache::insert(subLinePixmapName, cache);
+ }
+ painter->drawPixmap(button1.topLeft(), cache);
+ painter->drawPixmap(button2.topLeft(), cache);
+ }
+ break;
+ case CE_ScrollBarSlider:
+ if (const QStyleOptionSlider *scrollBar = qstyleoption_cast<const QStyleOptionSlider *>(option)) {
+ bool horizontal = scrollBar->orientation == Qt::Horizontal;
+ bool isEnabled = scrollBar->state & State_Enabled;
+
+ // The slider
+ if (option->rect.isValid()) {
+ QString sliderPixmapName = uniqueName(QLatin1String("scrollbar_slider"), option, option->rect.size());
+ if (horizontal)
+ sliderPixmapName += QLatin1String("-horizontal");
+
+ QPixmap cache;
+ if (!UsePixmapCache || !QPixmapCache::find(sliderPixmapName, cache)) {
+ cache = QPixmap(option->rect.size());
+ cache.fill(Qt::white);
+ QRect pixmapRect(0, 0, cache.width(), cache.height());
+ QPainter sliderPainter(&cache);
+ bool sunken = (scrollBar->state & State_Sunken);
+
+ if (isEnabled) {
+ QLinearGradient gradient(pixmapRect.left(), pixmapRect.center().y(),
+ pixmapRect.right(), pixmapRect.center().y());
+ if (horizontal)
+ gradient = QLinearGradient(pixmapRect.center().x(), pixmapRect.top(),
+ pixmapRect.center().x(), pixmapRect.bottom());
+
+ if (sunken) {
+ gradient.setColorAt(0, gradientStartColor.lighter(110));
+ gradient.setColorAt(1, gradientStopColor.lighter(105));
+ } else {
+ gradient.setColorAt(0, gradientStartColor.lighter(105));
+ gradient.setColorAt(1, gradientStopColor);
+ }
+ sliderPainter.fillRect(pixmapRect.adjusted(2, 2, -2, -2), gradient);
+ } else {
+ sliderPainter.fillRect(pixmapRect.adjusted(2, 2, -2, -2), option->palette.background());
+ }
+
+ sliderPainter.setPen(borderColor);
+ sliderPainter.drawRect(pixmapRect.adjusted(0, 0, -1, -1));
+ sliderPainter.setPen(alphaCornerColor);
+ QPoint points[4] = {
+ QPoint(pixmapRect.left(), pixmapRect.top()),
+ QPoint(pixmapRect.left(), pixmapRect.bottom()),
+ QPoint(pixmapRect.right(), pixmapRect.top()),
+ QPoint(pixmapRect.right(), pixmapRect.bottom()) };
+ sliderPainter.drawPoints(points, 4);
+
+ QLine lines[2];
+ sliderPainter.setPen(sunken ? gradientStartColor.lighter(110) : gradientStartColor.lighter(105));
+ lines[0] = QLine(pixmapRect.left() + 1, pixmapRect.top() + 1,
+ pixmapRect.right() - 1, pixmapRect.top() + 1);
+ lines[1] = QLine(pixmapRect.left() + 1, pixmapRect.top() + 2,
+ pixmapRect.left() + 1, pixmapRect.bottom() - 2);
+ sliderPainter.drawLines(lines, 2);
+
+ sliderPainter.setPen(sunken ? gradientStopColor.lighter(105) : gradientStopColor);
+ lines[0] = QLine(pixmapRect.left() + 1, pixmapRect.bottom() - 1,
+ pixmapRect.right() - 1, pixmapRect.bottom() - 1);
+ lines[1] = QLine(pixmapRect.right() - 1, pixmapRect.top() + 2,
+ pixmapRect.right() - 1, pixmapRect.bottom() - 1);
+ sliderPainter.drawLines(lines, 2);
+
+ int sliderMinLength = pixelMetric(PM_ScrollBarSliderMin, scrollBar, widget);
+ if ((horizontal && scrollBar->rect.width() > sliderMinLength)
+ || (!horizontal && scrollBar->rect.height() > sliderMinLength)) {
+ QImage pattern(horizontal ? qt_scrollbar_slider_pattern_horizontal
+ : qt_scrollbar_slider_pattern_vertical);
+ pattern.setColor(1, alphaCornerColor.rgba());
+ pattern.setColor(2, (sunken ? gradientStartColor.lighter(110) : gradientStartColor.lighter(105)).rgba());
+
+ if (horizontal) {
+ sliderPainter.drawImage(pixmapRect.center().x() - pattern.width() / 2 + 1,
+ pixmapRect.center().y() - 4,
+ pattern);
+ } else {
+ sliderPainter.drawImage(pixmapRect.center().x() - 4,
+ pixmapRect.center().y() - pattern.height() / 2 + 1,
+ pattern);
+ }
+ }
+ sliderPainter.end();
+ // insert the slider into the cache
+ if (UsePixmapCache)
+ QPixmapCache::insert(sliderPixmapName, cache);
+ }
+ painter->drawPixmap(option->rect.topLeft(), cache);
+ }
+ }
+ break;
+#endif
+#ifndef QT_NO_COMBOBOX
+ case CE_ComboBoxLabel:
+ if (const QStyleOptionComboBox *comboBox = qstyleoption_cast<const QStyleOptionComboBox *>(option)) {
+ painter->save();
+ if (!comboBox->editable) {
+ // Plastique's non-editable combo box is drawn as a button, so
+ // we need the label to be drawn using ButtonText where it
+ // would usually use Text.
+ painter->setPen(QPen(comboBox->palette.buttonText(), 0));
+ QWindowsStyle::drawControl(element, option, painter, widget);
+ } else if (!comboBox->currentIcon.isNull()) {
+ {
+ QRect editRect = subControlRect(CC_ComboBox, comboBox, SC_ComboBoxEditField, widget);
+ if (comboBox->direction == Qt::RightToLeft)
+ editRect.adjust(0, 2, -2, -2);
+ else
+ editRect.adjust(2, 2, 0, -2);
+ painter->save();
+ painter->setClipRect(editRect);
+ if (!comboBox->currentIcon.isNull()) {
+ QIcon::Mode mode = comboBox->state & State_Enabled ? QIcon::Normal
+ : QIcon::Disabled;
+ QPixmap pixmap = comboBox->currentIcon.pixmap(comboBox->iconSize, mode);
+ QRect iconRect(editRect);
+ iconRect.setWidth(comboBox->iconSize.width() + 5);
+ iconRect = alignedRect(QApplication::layoutDirection(),
+ Qt::AlignLeft | Qt::AlignVCenter,
+ iconRect.size(), editRect);
+ painter->fillRect(iconRect, option->palette.brush(QPalette::Base));
+ drawItemPixmap(painter, iconRect, Qt::AlignCenter, pixmap);
+ }
+ painter->restore();
+ }
+ } else {
+ QWindowsStyle::drawControl(element, option, painter, widget);
+ }
+
+ painter->restore();
+ }
+ break;
+#endif
+ default:
+ QWindowsStyle::drawControl(element, option, painter, widget);
+ break;
+ }
+}
+
+/*!
+ \reimp
+*/
+void QPlastiqueStyle::drawComplexControl(ComplexControl control, const QStyleOptionComplex *option,
+ QPainter *painter, const QWidget *widget) const
+{
+ QColor borderColor = option->palette.background().color().darker(178);
+ QColor alphaCornerColor;
+ if (widget) {
+ // ### backgroundrole/foregroundrole should be part of the style option
+ alphaCornerColor = mergedColors(option->palette.color(widget->backgroundRole()), borderColor);
+ } else {
+ alphaCornerColor = mergedColors(option->palette.background().color(), borderColor);
+ }
+ QColor gradientStartColor = option->palette.button().color().lighter(104);
+ QColor gradientStopColor = option->palette.button().color().darker(105);
+ QColor highlightedGradientStartColor = option->palette.button().color().lighter(101);
+ QColor highlightedGradientStopColor = mergedColors(option->palette.button().color(), option->palette.highlight().color(), 85);
+ QColor highlightedDarkInnerBorderColor = mergedColors(option->palette.button().color(), option->palette.highlight().color(), 35);
+ QColor highlightedLightInnerBorderColor = mergedColors(option->palette.button().color(), option->palette.highlight().color(), 58);
+
+ switch (control) {
+#ifndef QT_NO_SLIDER
+ case CC_Slider:
+ if (const QStyleOptionSlider *slider = qstyleoption_cast<const QStyleOptionSlider *>(option)) {
+ QRect grooveRegion = subControlRect(CC_Slider, option, SC_SliderGroove, widget);
+ QRect handle = subControlRect(CC_Slider, option, SC_SliderHandle, widget);
+ QRect ticks = subControlRect(CC_Slider, option, SC_SliderTickmarks, widget);
+ bool horizontal = slider->orientation == Qt::Horizontal;
+ bool ticksAbove = slider->tickPosition & QSlider::TicksAbove;
+ bool ticksBelow = slider->tickPosition & QSlider::TicksBelow;
+
+ QRect groove;
+ //The clickable region is 5 px wider than the visible groove for improved usability
+ if (grooveRegion.isValid())
+ groove = horizontal ? grooveRegion.adjusted(0, 5, 0, -5) : grooveRegion.adjusted(5, 0, -5, 0);
+
+
+ QPixmap cache;
+
+ if ((option->subControls & SC_SliderGroove) && groove.isValid()) {
+ BEGIN_STYLE_PIXMAPCACHE(QString::fromLatin1("slider_groove-%0-%1").arg(ticksAbove).arg(ticksBelow))
+ p->fillRect(groove, option->palette.background());
+
+ // draw groove
+ if (horizontal) {
+ p->setPen(borderColor);
+ const QLine lines[4] = {
+ QLine(groove.left() + 1, groove.top(),
+ groove.right() - 1, groove.top()),
+ QLine(groove.left() + 1, groove.bottom(),
+ groove.right() - 1, groove.bottom()),
+ QLine(groove.left(), groove.top() + 1,
+ groove.left(), groove.bottom() - 1),
+ QLine(groove.right(), groove.top() + 1,
+ groove.right(), groove.bottom() - 1) };
+ p->drawLines(lines, 4);
+
+ p->setPen(alphaCornerColor);
+ const QPoint points[4] = {
+ QPoint(groove.left(), groove.top()),
+ QPoint(groove.left(), groove.bottom()),
+ QPoint(groove.right(), groove.top()),
+ QPoint(groove.right(), groove.bottom()) };
+ p->drawPoints(points, 4);
+ } else {
+ p->setPen(borderColor);
+ const QLine lines[4] = {
+ QLine(groove.left() + 1, groove.top(),
+ groove.right() - 1, groove.top()),
+ QLine(groove.left() + 1, groove.bottom(),
+ groove.right() - 1, groove.bottom()),
+ QLine(groove.left(), groove.top() + 1,
+ groove.left(), groove.bottom() - 1),
+ QLine(groove.right(), groove.top() + 1,
+ groove.right(), groove.bottom() - 1) };
+ p->drawLines(lines, 4);
+
+ p->setPen(alphaCornerColor);
+ const QPoint points[4] = {
+ QPoint(groove.left(), groove.top()),
+ QPoint(groove.right(), groove.top()),
+ QPoint(groove.left(), groove.bottom()),
+ QPoint(groove.right(), groove.bottom()) };
+ p->drawPoints(points, 4);
+ }
+ END_STYLE_PIXMAPCACHE
+ }
+
+ if ((option->subControls & SC_SliderHandle) && handle.isValid()) {
+ QString handlePixmapName = uniqueName(QLatin1String("slider_handle"), option, handle.size());
+ if (ticksAbove && !ticksBelow)
+ handlePixmapName += QLatin1String("-flipped");
+ if ((option->activeSubControls & SC_SliderHandle) && (option->state & State_Sunken))
+ handlePixmapName += QLatin1String("-sunken");
+
+ if (!UsePixmapCache || !QPixmapCache::find(handlePixmapName, cache)) {
+ cache = QPixmap(handle.size());
+ cache.fill(Qt::white);
+ QRect pixmapRect(0, 0, handle.width(), handle.height());
+ QPainter handlePainter(&cache);
+ handlePainter.fillRect(pixmapRect, option->palette.background());
+
+ // draw handle
+ if (horizontal) {
+ QPainterPath path;
+ if (ticksAbove && !ticksBelow) {
+ path.moveTo(QPoint(pixmapRect.right(), pixmapRect.bottom()));
+ path.lineTo(QPoint(pixmapRect.right(), pixmapRect.bottom() - 10));
+ path.lineTo(QPoint(pixmapRect.right() - 5, pixmapRect.bottom() - 14));
+ path.lineTo(QPoint(pixmapRect.left() + 1, pixmapRect.bottom() - 10));
+ path.lineTo(QPoint(pixmapRect.left() + 1, pixmapRect.bottom()));
+ path.lineTo(QPoint(pixmapRect.right(), pixmapRect.bottom()));
+ } else {
+ path.moveTo(QPoint(pixmapRect.right(), pixmapRect.top() + 1));
+ path.lineTo(QPoint(pixmapRect.right(), pixmapRect.top() + 10));
+ path.lineTo(QPoint(pixmapRect.right() - 5, pixmapRect.top() + 14));
+ path.lineTo(QPoint(pixmapRect.left() + 1, pixmapRect.top() + 10));
+ path.lineTo(QPoint(pixmapRect.left() + 1, pixmapRect.top() + 1));
+ path.lineTo(QPoint(pixmapRect.right(), pixmapRect.top() + 1));
+ }
+ if (slider->state & State_Enabled) {
+ QLinearGradient gradient(pixmapRect.center().x(), pixmapRect.top(),
+ pixmapRect.center().x(), pixmapRect.bottom());
+ if ((option->activeSubControls & SC_SliderHandle) && (option->state & State_Sunken)) {
+ gradient.setColorAt(0, gradientStartColor.lighter(110));
+ gradient.setColorAt(1, gradientStopColor.lighter(110));
+ } else {
+ gradient.setColorAt(0, gradientStartColor);
+ gradient.setColorAt(1, gradientStopColor);
+ }
+ handlePainter.fillPath(path, gradient);
+ } else {
+ handlePainter.fillPath(path, slider->palette.background());
+ }
+ } else {
+ QPainterPath path;
+ if (ticksAbove && !ticksBelow) {
+ path.moveTo(QPoint(pixmapRect.right(), pixmapRect.top() + 1));
+ path.lineTo(QPoint(pixmapRect.right() - 10, pixmapRect.top() + 1));
+ path.lineTo(QPoint(pixmapRect.right() - 14, pixmapRect.top() + 5));
+ path.lineTo(QPoint(pixmapRect.right() - 10, pixmapRect.bottom()));
+ path.lineTo(QPoint(pixmapRect.right(), pixmapRect.bottom()));
+ path.lineTo(QPoint(pixmapRect.right(), pixmapRect.top() + 1));
+ } else {
+ path.moveTo(QPoint(pixmapRect.left() + 1, pixmapRect.top() + 1));
+ path.lineTo(QPoint(pixmapRect.left() + 10, pixmapRect.top() + 1));
+ path.lineTo(QPoint(pixmapRect.left() + 14, pixmapRect.top() + 5));
+ path.lineTo(QPoint(pixmapRect.left() + 10, pixmapRect.bottom()));
+ path.lineTo(QPoint(pixmapRect.left() + 1, pixmapRect.bottom()));
+ path.lineTo(QPoint(pixmapRect.left() + 1, pixmapRect.top() + 1));
+ }
+ if (slider->state & State_Enabled) {
+ QLinearGradient gradient(pixmapRect.center().x(), pixmapRect.top(),
+ pixmapRect.center().x(), pixmapRect.bottom());
+ gradient.setColorAt(0, gradientStartColor);
+ gradient.setColorAt(1, gradientStopColor);
+ handlePainter.fillPath(path, gradient);
+ } else {
+ handlePainter.fillPath(path, slider->palette.background());
+ }
+ }
+
+ QImage image;
+ if (horizontal) {
+ image = QImage((ticksAbove && !ticksBelow) ? qt_plastique_slider_horizontalhandle_up : qt_plastique_slider_horizontalhandle);
+ } else {
+ image = QImage((ticksAbove && !ticksBelow) ? qt_plastique_slider_verticalhandle_left : qt_plastique_slider_verticalhandle);
+ }
+
+ image.setColor(1, borderColor.rgba());
+ image.setColor(2, gradientStartColor.rgba());
+ image.setColor(3, alphaCornerColor.rgba());
+ if (option->state & State_Enabled) {
+ image.setColor(4, 0x80ffffff);
+ image.setColor(5, 0x25000000);
+ }
+ handlePainter.drawImage(pixmapRect, image);
+ handlePainter.end();
+ if (UsePixmapCache)
+ QPixmapCache::insert(handlePixmapName, cache);
+ }
+
+ painter->drawPixmap(handle.topLeft(), cache);
+
+ if (slider->state & State_HasFocus) {
+ QStyleOptionFocusRect fropt;
+ fropt.QStyleOption::operator=(*slider);
+ fropt.rect = subElementRect(SE_SliderFocusRect, slider, widget);
+ drawPrimitive(PE_FrameFocusRect, &fropt, painter, widget);
+ }
+ }
+
+ if (option->subControls & SC_SliderTickmarks) {
+ QPen oldPen = painter->pen();
+ painter->setPen(borderColor);
+ int tickSize = pixelMetric(PM_SliderTickmarkOffset, option, widget);
+ int available = pixelMetric(PM_SliderSpaceAvailable, slider, widget);
+ int interval = slider->tickInterval;
+ if (interval <= 0) {
+ interval = slider->singleStep;
+ if (QStyle::sliderPositionFromValue(slider->minimum, slider->maximum, interval,
+ available)
+ - QStyle::sliderPositionFromValue(slider->minimum, slider->maximum,
+ 0, available) < 3)
+ interval = slider->pageStep;
+ }
+ if (interval <= 0)
+ interval = 1;
+
+ int v = slider->minimum;
+ int len = pixelMetric(PM_SliderLength, slider, widget);
+ QVarLengthArray<QLine, 32> lines;
+ while (v <= slider->maximum + 1) {
+ if (v == slider->maximum + 1 && interval == 1)
+ break;
+ const int v_ = qMin(v, slider->maximum);
+ int pos = sliderPositionFromValue(slider->minimum, slider->maximum,
+ v_, (horizontal
+ ? slider->rect.width()
+ : slider->rect.height()) - len,
+ slider->upsideDown) + len / 2;
+
+ int extra = 2 - ((v_ == slider->minimum || v_ == slider->maximum) ? 1 : 0);
+
+ if (horizontal) {
+ if (ticksAbove) {
+ lines.append(QLine(pos, slider->rect.top() + extra,
+ pos, slider->rect.top() + tickSize));
+ }
+ if (ticksBelow) {
+ lines.append(QLine(pos, slider->rect.bottom() - extra,
+ pos, slider->rect.bottom() - tickSize));
+ }
+ } else {
+ if (ticksAbove) {
+ lines.append(QLine(slider->rect.left() + extra, pos,
+ slider->rect.left() + tickSize, pos));
+ }
+ if (ticksBelow) {
+ lines.append(QLine(slider->rect.right() - extra, pos,
+ slider->rect.right() - tickSize, pos));
+ }
+ }
+
+ // in the case where maximum is max int
+ int nextInterval = v + interval;
+ if (nextInterval < v)
+ break;
+ v = nextInterval;
+ }
+ painter->drawLines(lines.constData(), lines.size());
+ painter->setPen(oldPen);
+ }
+ }
+ break;
+#endif // QT_NO_SLIDER
+#ifndef QT_NO_SPINBOX
+ case CC_SpinBox:
+ if (const QStyleOptionSpinBox *spinBox = qstyleoption_cast<const QStyleOptionSpinBox *>(option)) {
+ painter->save();
+ bool upSunken = (spinBox->activeSubControls & SC_SpinBoxUp) && (spinBox->state & (State_Sunken | State_On));
+ bool downSunken = (spinBox->activeSubControls & SC_SpinBoxDown) && (spinBox->state & (State_Sunken | State_On));
+ bool reverse = (spinBox->direction == Qt::RightToLeft);
+
+ // Rects
+ QRect upRect = subControlRect(CC_SpinBox, option, SC_SpinBoxUp, widget);
+ QRect downRect = subControlRect(CC_SpinBox, option, SC_SpinBoxDown, widget);
+ QRect buttonRect = upRect | downRect;
+
+ // Brushes
+ QBrush corner = qMapBrushToRect(option->palette.shadow(), buttonRect);
+ qBrushSetAlphaF(&corner, qreal(0.25));
+ QBrush border = qMapBrushToRect(option->palette.shadow(), buttonRect);
+ qBrushSetAlphaF(&border, qreal(0.4));
+
+ QVarLengthArray<QPoint, 4> points;
+
+ Q_D(const QPlastiqueStyle);
+ if (spinBox->buttonSymbols == QAbstractSpinBox::NoButtons) {
+ QRect filledRect = option->rect.adjusted(1, 1, -1, -1);
+ QBrush baseBrush = qMapBrushToRect(option->palette.base(), filledRect);
+ painter->setBrushOrigin(filledRect.topLeft());
+ painter->fillRect(filledRect.adjusted(1, 1, -1, -1), baseBrush);
+ qt_plastique_draw_frame(painter, option->rect, option, QFrame::Sunken);
+ } else {
+ d->drawPartialFrame(painter,
+ option,
+ subControlRect(CC_SpinBox, spinBox, SC_SpinBoxEditField, widget),
+ widget);
+ }
+ // Paint buttons
+ if (spinBox->buttonSymbols == QAbstractSpinBox::NoButtons) {
+ painter->restore();
+ break;
+ }
+ // Button outlines
+ painter->setPen(QPen(border, 0));
+ if (!reverse)
+ painter->drawLine(buttonRect.topLeft() + QPoint(0, 1), buttonRect.bottomLeft() + QPoint(0, -1));
+ else
+ painter->drawLine(buttonRect.topRight() + QPoint(0, -1), buttonRect.bottomRight() + QPoint(0, 1));
+
+ if (!reverse) {
+ const QLine lines[4] = {
+ QLine(upRect.left(), upRect.top(), upRect.right() - 2, upRect.top()),
+ QLine(upRect.left() + 1, upRect.bottom(), upRect.right() - 1, upRect.bottom()),
+ QLine(downRect.left(), downRect.bottom(), downRect.right() - 2, downRect.bottom()),
+ QLine(buttonRect.right(), buttonRect.top() + 2, buttonRect.right(), buttonRect.bottom() - 2) };
+ painter->drawLines(lines, 4);
+
+ points.append(QPoint(upRect.right() - 1, upRect.top() + 1));
+ points.append(QPoint(downRect.right() - 1, downRect.bottom() - 1));
+ painter->drawPoints(points.constData(), points.size());
+ points.clear();
+ painter->setPen(QPen(corner, 0));
+ points.append(QPoint(upRect.right() - 1, upRect.top()));
+ points.append(QPoint(upRect.right(), upRect.top() + 1));
+ points.append(QPoint(upRect.right(), downRect.bottom() - 1));
+ points.append(QPoint(upRect.right() - 1, downRect.bottom()));
+ } else {
+ const QLine lines[4] = {
+ QLine(upRect.right(), upRect.top(), upRect.left() + 2, upRect.top()),
+ QLine(upRect.right() - 1, upRect.bottom(), upRect.left() + 1, upRect.bottom()),
+ QLine(downRect.right(), downRect.bottom(), downRect.left() + 2, downRect.bottom()),
+ QLine(buttonRect.left(), buttonRect.top() + 2, buttonRect.left(), buttonRect.bottom() - 2) };
+ painter->drawLines(lines, 4);
+
+ points.append(QPoint(upRect.left() + 1, upRect.top() + 1));
+ points.append(QPoint(downRect.left() + 1, downRect.bottom() - 1));
+ painter->drawPoints(points.constData(), points.size());
+ points.clear();
+ painter->setPen(QPen(corner, 0));
+ points.append(QPoint(upRect.left() + 1, upRect.top()));
+ points.append(QPoint(upRect.left(), upRect.top() + 1));
+ points.append(QPoint(upRect.left(), downRect.bottom() - 1));
+ points.append(QPoint(upRect.left() + 1, downRect.bottom()));
+ }
+ painter->drawPoints(points.constData(), points.size());
+ points.clear();
+
+ // Button colors
+ QBrush buttonGradientBrush;
+ QBrush leftLineGradientBrush;
+ QBrush rightLineGradientBrush;
+ QBrush sunkenButtonGradientBrush;
+ QBrush sunkenLeftLineGradientBrush;
+ QBrush sunkenRightLineGradientBrush;
+ QBrush buttonBrush = qMapBrushToRect(option->palette.button(), buttonRect);
+ if (buttonBrush.gradient() || !buttonBrush.texture().isNull()) {
+ buttonGradientBrush = buttonBrush;
+ sunkenButtonGradientBrush = qBrushDark(buttonBrush, 108);
+ leftLineGradientBrush = qBrushLight(buttonBrush, 105);
+ rightLineGradientBrush = qBrushDark(buttonBrush, 105);
+ sunkenLeftLineGradientBrush = qBrushDark(buttonBrush, 110);
+ sunkenRightLineGradientBrush = qBrushDark(buttonBrush, 106);
+ } else {
+ // Generate gradients
+ QLinearGradient buttonGradient(buttonRect.topLeft(), buttonRect.bottomLeft());
+ buttonGradient.setColorAt(0.0, buttonBrush.color().lighter(104));
+ buttonGradient.setColorAt(1.0, buttonBrush.color().darker(110));
+ buttonGradientBrush = QBrush(buttonGradient);
+
+ QLinearGradient buttonGradient2(buttonRect.topLeft(), buttonRect.bottomLeft());
+ buttonGradient2.setColorAt(0.0, buttonBrush.color().darker(113));
+ buttonGradient2.setColorAt(1.0, buttonBrush.color().darker(103));
+ sunkenButtonGradientBrush = QBrush(buttonGradient2);
+
+ QLinearGradient buttonGradient3(buttonRect.topLeft(), buttonRect.bottomLeft());
+ buttonGradient3.setColorAt(0.0, buttonBrush.color().lighter(105));
+ buttonGradient3.setColorAt(1.0, buttonBrush.color());
+ leftLineGradientBrush = QBrush(buttonGradient3);
+
+ QLinearGradient buttonGradient4(buttonRect.topLeft(), buttonRect.bottomLeft());
+ buttonGradient4.setColorAt(0.0, buttonBrush.color());
+ buttonGradient4.setColorAt(1.0, buttonBrush.color().darker(110));
+ rightLineGradientBrush = QBrush(buttonGradient4);
+
+ QLinearGradient buttonGradient5(buttonRect.topLeft(), buttonRect.bottomLeft());
+ buttonGradient5.setColorAt(0.0, buttonBrush.color().darker(113));
+ buttonGradient5.setColorAt(1.0, buttonBrush.color().darker(107));
+ sunkenLeftLineGradientBrush = QBrush(buttonGradient5);
+
+ QLinearGradient buttonGradient6(buttonRect.topLeft(), buttonRect.bottomLeft());
+ buttonGradient6.setColorAt(0.0, buttonBrush.color().darker(108));
+ buttonGradient6.setColorAt(1.0, buttonBrush.color().darker(103));
+ sunkenRightLineGradientBrush = QBrush(buttonGradient6);
+ }
+
+ // Main fill
+ painter->fillRect(upRect.adjusted(2, 2, -2, -2),
+ qMapBrushToRect(upSunken ? sunkenButtonGradientBrush
+ : buttonGradientBrush, upRect));
+ painter->fillRect(downRect.adjusted(2, 2, -2, -2),
+ qMapBrushToRect(downSunken ? sunkenButtonGradientBrush
+ : buttonGradientBrush, downRect));
+
+ // Top line
+ painter->setPen(QPen(qBrushLight(qMapBrushToRect(upSunken ? sunkenButtonGradientBrush
+ : buttonGradientBrush, upRect), 105), 0));
+ if (!reverse) {
+ painter->drawLine(upRect.left() + 1, upRect.top() + 1,
+ upRect.right() - 2, upRect.top() + 1);
+ } else {
+ painter->drawLine(upRect.right() - 1, upRect.top() + 1,
+ upRect.left() + 2, upRect.top() + 1);
+ }
+ painter->setPen(QPen(qBrushLight(qMapBrushToRect(downSunken ? sunkenButtonGradientBrush
+ : buttonGradientBrush, downRect), 105), 0));
+ if (!reverse) {
+ painter->drawLine(downRect.left() + 1, downRect.top() + 1,
+ downRect.right() - 1, downRect.top() + 1);
+ } else {
+ painter->drawLine(downRect.right() - 1, downRect.top() + 1,
+ downRect.left() + 1, downRect.top() + 1);
+ }
+
+ // Left line
+ painter->setPen(QPen(qMapBrushToRect(upSunken ? sunkenLeftLineGradientBrush
+ : leftLineGradientBrush, upRect), 1));
+ if (!reverse) {
+ painter->drawLine(upRect.left() + 1, upRect.top() + 2,
+ upRect.left() + 1, upRect.bottom() - 1);
+ } else {
+ painter->drawLine(upRect.left() + 1, upRect.top() + 2,
+ upRect.left() + 1, upRect.bottom() - 1);
+ }
+ painter->setPen(QPen(qMapBrushToRect(downSunken ? sunkenLeftLineGradientBrush
+ : leftLineGradientBrush, downRect), 1));
+ if (!reverse) {
+ painter->drawLine(downRect.left() + 1, downRect.top() + 2,
+ downRect.left() + 1, downRect.bottom() - 1);
+ } else {
+ painter->drawLine(downRect.left() + 1, downRect.top() + 1,
+ downRect.left() + 1, downRect.bottom() - 2);
+ }
+
+ // Bottom line
+ painter->setPen(QPen(qBrushDark(qMapBrushToRect(upSunken ? sunkenButtonGradientBrush
+ : buttonGradientBrush, upRect), 105), 0));
+ if (!reverse) {
+ painter->drawLine(upRect.left() + 2, upRect.bottom() - 1,
+ upRect.right() - 1, upRect.bottom() - 1);
+ } else {
+ painter->drawLine(upRect.right() - 2, upRect.bottom() - 1,
+ upRect.left() + 1, upRect.bottom() - 1);
+ }
+ painter->setPen(QPen(qBrushDark(qMapBrushToRect(downSunken ? sunkenButtonGradientBrush
+ : buttonGradientBrush, downRect), 105), 0));
+ if (!reverse) {
+ painter->drawLine(downRect.left() + 2, downRect.bottom() - 1,
+ downRect.right() - 2, downRect.bottom() - 1);
+ } else {
+ painter->drawLine(downRect.right() - 2, downRect.bottom() - 1,
+ downRect.left() + 2, downRect.bottom() - 1);
+ }
+
+ // Right line
+ painter->setPen(QPen(qMapBrushToRect(upSunken ? sunkenRightLineGradientBrush
+ : rightLineGradientBrush, upRect), 1));
+ if (!reverse) {
+ painter->drawLine(upRect.right() - 1, upRect.top() + 2,
+ upRect.right() - 1, upRect.bottom() - 1);
+ } else {
+ painter->drawLine(upRect.right() - 1, upRect.top() + 2,
+ upRect.right() - 1, upRect.bottom() - 1);
+ }
+ painter->setPen(QPen(qMapBrushToRect(downSunken ? sunkenRightLineGradientBrush
+ : rightLineGradientBrush, downRect), 1));
+ if (!reverse) {
+ painter->drawLine(downRect.right() - 1, downRect.top() + 1,
+ downRect.right() - 1, downRect.bottom() - 2);
+ } else {
+ painter->drawLine(downRect.right() - 1, downRect.top() + 2,
+ downRect.right() - 1, downRect.bottom() - 1);
+ }
+
+ QBrush indicatorBrush = qMapBrushToRect(option->palette.buttonText(), buttonRect);
+ painter->setPen(QPen(indicatorBrush, 0));
+ if (spinBox->buttonSymbols == QAbstractSpinBox::PlusMinus) {
+ QPoint center;
+ if (spinBox->subControls & SC_SpinBoxUp) {
+ // .......
+ // ...X...
+ // ...X...
+ // .XXXXX.
+ // ...X...
+ // ...X...
+ // .......
+ center = upRect.center();
+ if (upSunken) {
+ ++center.rx();
+ ++center.ry();
+ }
+ painter->drawLine(center.x(), center.y() - 2, center.x(), center.y() + 2);
+ painter->drawLine(center.x() - 2, center.y(), center.x() + 2, center.y());
+ }
+ if (spinBox->subControls & SC_SpinBoxDown) {
+ // .......
+ // .......
+ // .......
+ // .XXXXX.
+ // .......
+ // .......
+ // .......
+ center = downRect.center();
+ if (downSunken) {
+ ++center.rx();
+ ++center.ry();
+ }
+ painter->drawLine(center.x() - 2, center.y(), center.x() + 2, center.y());
+ }
+ } else {
+ int offset;
+ int centerX;
+ if (spinBox->subControls & SC_SpinBoxUp) {
+ // ...........
+ // .....X.....
+ // ....XXX....
+ // ...XXXXX...
+ // ..XXXXXXX..
+ // ...........
+ offset = upSunken ? 1 : 0;
+ QRect upArrowRect(upRect.center().x() - 3 + offset, upRect.center().y() - 2 + offset, 7, 4);
+ centerX = upArrowRect.center().x();
+ painter->drawPoint(centerX, upArrowRect.top());
+ const QLine lines[3] = {
+ QLine(centerX - 1, upArrowRect.top() + 1, centerX + 1, upArrowRect.top() + 1),
+ QLine(centerX - 2, upArrowRect.top() + 2, centerX + 2, upArrowRect.top() + 2),
+ QLine(centerX - 3, upArrowRect.top() + 3, centerX + 3, upArrowRect.top() + 3) };
+ painter->drawLines(lines, 3);
+ }
+ if (spinBox->subControls & SC_SpinBoxDown) {
+ // ...........
+ // ..XXXXXXX..
+ // ...XXXXX...
+ // ....XXX....
+ // .....X.....
+ // ...........
+ offset = downSunken ? 1 : 0;
+ QRect downArrowRect(downRect.center().x() - 3 + offset, downRect.center().y() - 2 + offset + 1, 7, 4);
+ centerX = downArrowRect.center().x();
+ const QLine lines[3] = {
+ QLine(centerX - 3, downArrowRect.top(), centerX + 3, downArrowRect.top()),
+ QLine(centerX - 2, downArrowRect.top() + 1, centerX + 2, downArrowRect.top() + 1),
+ QLine(centerX - 1, downArrowRect.top() + 2, centerX + 1, downArrowRect.top() + 2) };
+ painter->drawLines(lines, 3);
+ painter->drawPoint(centerX, downArrowRect.top() + 3);
+ }
+ }
+ painter->restore();
+ }
+ break;
+#endif // QT_NO_SPINBOX
+#ifndef QT_NO_COMBOBOX
+ case CC_ComboBox:
+ if (const QStyleOptionComboBox *comboBox = qstyleoption_cast<const QStyleOptionComboBox *>(option)) {
+ bool sunken = comboBox->state & State_On; // play dead if combobox has no items
+ bool reverse = comboBox->direction == Qt::RightToLeft;
+ int menuButtonWidth = 16;
+ int xoffset = sunken ? (reverse ? -1 : 1) : 0;
+ int yoffset = sunken ? 1 : 0;
+ QRect rect = comboBox->rect;
+ QPen oldPen = painter->pen();
+
+ // Fill
+ if (comboBox->editable) {
+ // Button colors
+ QBrush alphaCornerBrush = qBrushDark(option->palette.button(), 165);
+ qBrushSetAlphaF(&alphaCornerBrush, 0.5);
+ QBrush buttonGradientBrush;
+ QBrush leftLineGradientBrush;
+ QBrush rightLineGradientBrush;
+ QBrush sunkenButtonGradientBrush;
+ QBrush sunkenLeftLineGradientBrush;
+ QBrush sunkenRightLineGradientBrush;
+ QBrush button = option->palette.button();
+ if (button.gradient() || !button.texture().isNull()) {
+ buttonGradientBrush = button;
+ sunkenButtonGradientBrush = qBrushDark(button, 108);
+ leftLineGradientBrush = qBrushLight(button, 105);
+ rightLineGradientBrush = qBrushDark(button, 105);
+ sunkenLeftLineGradientBrush = qBrushDark(button, 110);
+ sunkenRightLineGradientBrush = qBrushDark(button, 106);
+ } else {
+ // Generate gradients
+ QLinearGradient buttonGradient(option->rect.topLeft(), option->rect.bottomLeft());
+ buttonGradient.setColorAt(0.0, button.color().lighter(104));
+ buttonGradient.setColorAt(1.0, button.color().darker(110));
+ buttonGradientBrush = QBrush(buttonGradient);
+
+ QLinearGradient buttonGradient2(option->rect.topLeft(), option->rect.bottomLeft());
+ buttonGradient2.setColorAt(0.0, button.color().darker(113));
+ buttonGradient2.setColorAt(1.0, button.color().darker(103));
+ sunkenButtonGradientBrush = QBrush(buttonGradient2);
+
+ QLinearGradient buttonGradient3(option->rect.topLeft(), option->rect.bottomLeft());
+ buttonGradient3.setColorAt(0.0, button.color().lighter(105));
+ buttonGradient3.setColorAt(1.0, button.color());
+ leftLineGradientBrush = QBrush(buttonGradient3);
+
+ QLinearGradient buttonGradient4(option->rect.topLeft(), option->rect.bottomLeft());
+ buttonGradient4.setColorAt(0.0, button.color());
+ buttonGradient4.setColorAt(1.0, button.color().darker(110));
+ rightLineGradientBrush = QBrush(buttonGradient4);
+
+ QLinearGradient buttonGradient5(option->rect.topLeft(), option->rect.bottomLeft());
+ buttonGradient5.setColorAt(0.0, button.color().darker(113));
+ buttonGradient5.setColorAt(1.0, button.color().darker(107));
+ sunkenLeftLineGradientBrush = QBrush(buttonGradient5);
+
+ QLinearGradient buttonGradient6(option->rect.topLeft(), option->rect.bottomLeft());
+ buttonGradient6.setColorAt(0.0, button.color().darker(108));
+ buttonGradient6.setColorAt(1.0, button.color().darker(103));
+ sunkenRightLineGradientBrush = QBrush(buttonGradient6);
+ }
+
+ // ComboBox starts with a lineedit in place already.
+ QRect buttonRect;
+ if (!reverse) {
+ buttonRect.setRect(rect.right() - menuButtonWidth, rect.top(), menuButtonWidth + 1, rect.height());
+ } else {
+ buttonRect.setRect(rect.left(), rect.top(), menuButtonWidth + 1, rect.height());
+ }
+
+ Q_D(const QPlastiqueStyle);
+ d->drawPartialFrame(painter,
+ option,
+ subControlRect(CC_ComboBox, option, SC_ComboBoxEditField, widget),
+ widget);
+
+ QBrush border = qMapBrushToRect(option->palette.shadow(), buttonRect);
+ qBrushSetAlphaF(&border, qreal(0.4));
+ painter->setPen(QPen(border, 0));
+ if (!reverse)
+ painter->drawLine(buttonRect.topLeft() + QPoint(0, 1), buttonRect.bottomLeft() + QPoint(0, -1));
+ else
+ painter->drawLine(buttonRect.topRight() + QPoint(0, -1), buttonRect.bottomRight() + QPoint(0, 1));
+
+ // Outline the button border
+ if (!reverse) {
+ const QLine lines[3] = {
+ QLine(buttonRect.left(), buttonRect.top(),
+ buttonRect.right() - 2, buttonRect.top()),
+ QLine(buttonRect.right(), buttonRect.top() + 2,
+ buttonRect.right(), buttonRect.bottom() - 2),
+ QLine(buttonRect.left(), buttonRect.bottom(),
+ buttonRect.right() - 2, buttonRect.bottom()) };
+ painter->drawLines(lines, 3);
+ {
+ const QPoint points[2] = {
+ QPoint(buttonRect.right() - 1, buttonRect.top() + 1),
+ QPoint(buttonRect.right() - 1, buttonRect.bottom() - 1) };
+ painter->drawPoints(points, 2);
+ }
+
+ QBrush corner = qMapBrushToRect(option->palette.shadow(), buttonRect);
+ qBrushSetAlphaF(&corner, qreal(0.16));
+ painter->setPen(QPen(corner, 0));
+ {
+ const QPoint points[4] = {
+ QPoint(buttonRect.right() - 1, buttonRect.top()),
+ QPoint(buttonRect.right() - 1, buttonRect.bottom()),
+ QPoint(buttonRect.right(), buttonRect.top() + 1),
+ QPoint(buttonRect.right(), buttonRect.bottom() - 1) };
+ painter->drawPoints(points, 4);
+ }
+ } else {
+ const QLine lines[3] = {
+ QLine(buttonRect.right(), buttonRect.top(),
+ buttonRect.left() + 2, buttonRect.top()),
+ QLine(buttonRect.left(), buttonRect.top() + 2,
+ buttonRect.left(), buttonRect.bottom() - 2),
+ QLine(buttonRect.right(), buttonRect.bottom(),
+ buttonRect.left() + 2, buttonRect.bottom()) };
+ painter->drawLines(lines, 3);
+ {
+ const QPoint points[2] = {
+ QPoint(buttonRect.left() + 1, buttonRect.top() + 1),
+ QPoint(buttonRect.left() + 1, buttonRect.bottom() - 1) };
+ painter->drawPoints(points, 2);
+ }
+
+ QBrush corner = qMapBrushToRect(option->palette.shadow(), buttonRect);
+ qBrushSetAlphaF(&corner, qreal(0.16));
+ painter->setPen(QPen(corner, 0));
+ {
+ const QPoint points[4] = {
+ QPoint(buttonRect.left() + 1, buttonRect.top()),
+ QPoint(buttonRect.left() + 1, buttonRect.bottom()),
+ QPoint(buttonRect.left(), buttonRect.top() + 1),
+ QPoint(buttonRect.left(), buttonRect.bottom() - 1) };
+ painter->drawPoints(points, 4);
+ }
+ }
+
+ QRect fillRect = buttonRect.adjusted(2, 2, -2, -2);
+ // Main fill
+ painter->fillRect(fillRect,
+ qMapBrushToRect(sunken ? sunkenButtonGradientBrush
+ : buttonGradientBrush, option->rect));
+
+ // Top line
+ painter->setPen(QPen(qBrushLight(qMapBrushToRect(sunken ? sunkenButtonGradientBrush
+ : buttonGradientBrush, option->rect), 105), 0));
+ if (!reverse) {
+ painter->drawLine(QPointF(buttonRect.left() + 1, buttonRect.top() + 1),
+ QPointF(buttonRect.right() - 2, buttonRect.top() + 1));
+ } else {
+ painter->drawLine(QPointF(buttonRect.right() - 1, buttonRect.top() + 1),
+ QPointF(buttonRect.left() + 2, buttonRect.top() + 1));
+ }
+
+ // Bottom line
+ painter->setPen(QPen(qBrushDark(qMapBrushToRect(sunken ? sunkenButtonGradientBrush
+ : buttonGradientBrush, option->rect), 105), 0));
+ if (!reverse) {
+ painter->drawLine(QPointF(buttonRect.left() + 1, buttonRect.bottom() - 1),
+ QPointF(buttonRect.right() - 2, buttonRect.bottom() - 1));
+ } else {
+ painter->drawLine(QPointF(buttonRect.right() - 1, buttonRect.bottom() - 1),
+ QPointF(buttonRect.left() + 2, buttonRect.bottom() - 1));
+ }
+
+ // Left line
+ painter->setPen(QPen(qMapBrushToRect(sunken ? sunkenLeftLineGradientBrush
+ : leftLineGradientBrush, option->rect), 1));
+ if (!reverse) {
+ painter->drawLine(QPointF(buttonRect.left() + 1, buttonRect.top() + 2),
+ QPointF(buttonRect.left() + 1, buttonRect.bottom() - 2));
+ } else {
+ painter->drawLine(QPointF(buttonRect.left() + 1, buttonRect.top() + 2),
+ QPointF(buttonRect.left() + 1, buttonRect.bottom() - 2));
+ }
+
+ // Right line
+ painter->setPen(QPen(qMapBrushToRect(sunken ? sunkenRightLineGradientBrush
+ : rightLineGradientBrush, option->rect), 1));
+ if (!reverse) {
+ painter->drawLine(QPointF(buttonRect.right() - 1, buttonRect.top() + 2),
+ QPointF(buttonRect.right() - 1, buttonRect.bottom() - 2));
+ } else {
+ painter->drawLine(QPointF(buttonRect.right() - 1, buttonRect.top() + 2),
+ QPointF(buttonRect.right() - 1, buttonRect.bottom() - 2));
+ }
+ } else {
+ // Start with a standard panel button fill
+ QStyleOptionButton buttonOption;
+ buttonOption.QStyleOption::operator=(*comboBox);
+ if (!sunken) {
+ buttonOption.state &= ~State_Sunken;
+ }
+ drawPrimitive(PE_PanelButtonCommand, &buttonOption, painter, widget);
+
+ // Draw the menu button separator line
+ QBrush border = qMapBrushToRect(option->palette.shadow(), rect);
+ qBrushSetAlphaF(&border, qreal(0.35));
+ painter->setPen(QPen(border, 0));
+ if (!reverse) {
+ painter->drawLine(rect.right() - menuButtonWidth + xoffset, rect.top() + 1,
+ rect.right() - menuButtonWidth + xoffset, rect.bottom() - 1);
+ } else {
+ painter->drawLine(rect.left() + menuButtonWidth + xoffset, rect.top() + 1,
+ rect.left() + menuButtonWidth + xoffset, rect.bottom() - 1);
+ }
+ }
+
+ // Draw the little arrow
+ if (comboBox->subControls & SC_ComboBoxArrow) {
+ int left = !reverse ? rect.right() - menuButtonWidth : rect.left();
+ int right = !reverse ? rect.right() : rect.left() + menuButtonWidth;
+ QRect arrowRect((left + right) / 2 - 3 + xoffset,
+ rect.center().y() - 1 + yoffset, 7, 4);
+ painter->setPen(QPen(qMapBrushToRect(option->palette.buttonText(), rect), 0));
+ const QLine lines[3] = {
+ QLine(arrowRect.topLeft(), arrowRect.topRight()),
+ QLine(arrowRect.left() + 1, arrowRect.top() + 1,
+ arrowRect.right() - 1, arrowRect.top() + 1),
+ QLine(arrowRect.left() + 2, arrowRect.top() + 2,
+ arrowRect.right() - 2, arrowRect.top() + 2) };
+ painter->drawLines(lines, 3);
+ painter->drawPoint(arrowRect.center().x(), arrowRect.bottom());
+ }
+
+ // Draw the focus rect
+ if (((option->state & State_HasFocus) && (option->state & State_KeyboardFocusChange)) && !comboBox->editable) {
+ QStyleOptionFocusRect focus;
+ focus.rect = subControlRect(CC_ComboBox, option, SC_ComboBoxEditField, widget)
+ .adjusted(-2, 0, 2, 0);
+ drawPrimitive(PE_FrameFocusRect, &focus, painter, widget);
+ }
+
+ painter->setPen(oldPen);
+ }
+ break;
+#endif // QT_NO_COMBOBOX
+ case CC_TitleBar:
+ if (const QStyleOptionTitleBar *titleBar = qstyleoption_cast<const QStyleOptionTitleBar *>(option)) {
+ painter->save();
+ bool active = (titleBar->titleBarState & State_Active);
+ QRect fullRect = titleBar->rect;
+
+ // ### use palette colors instead
+ QColor titleBarGradientStart(active ? 0x3b508a : 0x6e6e6e);
+ QColor titleBarGradientStop(active ? 0x5d6e9e : 0x818181);
+ QColor titleBarFrameBorder(0x393939);
+ QColor titleBarAlphaCorner(active ? 0x4b5e7f : 0x6a6a6a);
+ QColor titleBarInnerTopLine(active ? 0x8e98ba : 0xa4a4a4);
+ QColor titleBarInnerInnerTopLine(active ? 0x57699b : 0x808080);
+ QColor leftCorner(active ? 0x6f7ea8 : 0x8e8e8e);
+ QColor rightCorner(active ? 0x44537d : 0x676767);
+ QColor textColor(active ? 0x282e40 : 0x282e40);
+ QColor textAlphaColor(active ? 0x3f4862 : 0x3f4862);
+
+#ifdef QT3_SUPPORT
+ if (widget && widget->inherits("Q3DockWindowTitleBar")) {
+ QStyleOptionDockWidgetV2 dockwidget;
+ dockwidget.QStyleOption::operator=(*option);
+ dockwidget.title = titleBar->text;
+ drawControl(CE_DockWidgetTitle, &dockwidget, painter, widget);
+ } else
+#endif // QT3_SUPPORT
+
+ {
+ // Fill title bar gradient
+ qt_plastique_draw_gradient(painter, option->rect.adjusted(1, 1, -1, 0),
+ titleBarGradientStart,
+ titleBarGradientStop);
+
+ // Frame and rounded corners
+ painter->setPen(titleBarFrameBorder);
+
+ // top border line
+ {
+ const QLine lines[3] = {
+ QLine(fullRect.left() + 2, fullRect.top(), fullRect.right() - 2, fullRect.top()),
+ QLine(fullRect.left(), fullRect.top() + 2, fullRect.left(), fullRect.bottom()),
+ QLine(fullRect.right(), fullRect.top() + 2, fullRect.right(), fullRect.bottom()) };
+ painter->drawLines(lines, 3);
+ const QPoint points[2] = {
+ QPoint(fullRect.left() + 1, fullRect.top() + 1),
+ QPoint(fullRect.right() - 1, fullRect.top() + 1) };
+ painter->drawPoints(points, 2);
+ }
+
+ // alpha corners
+ painter->setPen(titleBarAlphaCorner);
+ {
+ const QPoint points[4] = {
+ QPoint(fullRect.left() + 2, fullRect.top() + 1),
+ QPoint(fullRect.left() + 1, fullRect.top() + 2),
+ QPoint(fullRect.right() - 2, fullRect.top() + 1),
+ QPoint(fullRect.right() - 1, fullRect.top() + 2) };
+ painter->drawPoints(points, 4);
+ }
+
+ // inner top line
+ painter->setPen(titleBarInnerTopLine);
+ painter->drawLine(fullRect.left() + 3, fullRect.top() + 1, fullRect.right() - 3, fullRect.top() + 1);
+
+ // inner inner top line
+ painter->setPen(titleBarInnerInnerTopLine);
+ painter->drawLine(fullRect.left() + 2, fullRect.top() + 2, fullRect.right() - 2, fullRect.top() + 2);
+
+ // left and right inner
+ painter->setPen(leftCorner);
+ painter->drawLine(fullRect.left() + 1, fullRect.top() + 3, fullRect.left() + 1, fullRect.bottom());
+ painter->setPen(rightCorner);
+ painter->drawLine(fullRect.right() - 1, fullRect.top() + 3, fullRect.right() - 1, fullRect.bottom());
+
+ if (titleBar->titleBarState & Qt::WindowMinimized) {
+ painter->setPen(titleBarFrameBorder);
+ painter->drawLine(fullRect.left() + 2, fullRect.bottom(), fullRect.right() - 2, fullRect.bottom());
+ {
+ const QPoint points[2] = {
+ QPoint(fullRect.left() + 1, fullRect.bottom() - 1),
+ QPoint(fullRect.right() - 1, fullRect.bottom() - 1) };
+ painter->drawPoints(points, 2);
+ }
+ painter->setPen(rightCorner);
+ painter->drawLine(fullRect.left() + 2, fullRect.bottom() - 1, fullRect.right() - 2, fullRect.bottom() - 1);
+ painter->setPen(titleBarAlphaCorner);
+ {
+ const QPoint points[4] = {
+ QPoint(fullRect.left() + 1, fullRect.bottom() - 2),
+ QPoint(fullRect.left() + 2, fullRect.bottom() - 1),
+ QPoint(fullRect.right() - 1, fullRect.bottom() - 2),
+ QPoint(fullRect.right() - 2, fullRect.bottom() - 1) };
+ painter->drawPoints(points, 4);
+ }
+ }
+ // draw title
+ QRect textRect = subControlRect(CC_TitleBar, titleBar, SC_TitleBarLabel, widget);
+
+ QFont font = painter->font();
+ font.setBold(true);
+ painter->setFont(font);
+ painter->setPen(titleBar->palette.text().color());
+
+ // Attempt to align left if there is not enough room for the title
+ // text. Otherwise, align center. QWorkspace does elliding for us,
+ // and it doesn't know about the bold title, so we need to work
+ // around some of the width mismatches.
+ bool tooWide = (QFontMetrics(font).width(titleBar->text) > textRect.width());
+ QTextOption option((tooWide ? Qt::AlignLeft : Qt::AlignHCenter) | Qt::AlignVCenter);
+ option.setWrapMode(QTextOption::NoWrap);
+
+ painter->drawText(textRect.adjusted(1, 1, 1, 1), titleBar->text, option);
+ painter->setPen(titleBar->palette.highlightedText().color());
+ painter->drawText(textRect, titleBar->text, option);
+ }
+
+ // min button
+ if ((titleBar->subControls & SC_TitleBarMinButton)
+ && (titleBar->titleBarFlags & Qt::WindowMinimizeButtonHint)
+ && !(titleBar->titleBarState & Qt::WindowMinimized)) {
+ bool hover = (titleBar->activeSubControls & SC_TitleBarMinButton) && (titleBar->state & State_MouseOver);
+ bool sunken = (titleBar->activeSubControls & SC_TitleBarMinButton) && (titleBar->state & State_Sunken);
+
+ QRect minButtonRect = subControlRect(CC_TitleBar, titleBar, SC_TitleBarMinButton, widget);
+ qt_plastique_draw_mdibutton(painter, titleBar, minButtonRect, hover, sunken);
+
+ int xoffset = minButtonRect.width() / 3;
+ int yoffset = minButtonRect.height() / 3;
+
+ QRect minButtonIconRect(minButtonRect.left() + xoffset, minButtonRect.top() + yoffset,
+ minButtonRect.width() - xoffset * 2, minButtonRect.height() - yoffset * 2);
+
+ painter->setPen(textColor);
+ {
+ const QLine lines[2] = {
+ QLine(minButtonIconRect.center().x() - 2,
+ minButtonIconRect.center().y() + 3,
+ minButtonIconRect.center().x() + 3,
+ minButtonIconRect.center().y() + 3),
+ QLine(minButtonIconRect.center().x() - 2,
+ minButtonIconRect.center().y() + 4,
+ minButtonIconRect.center().x() + 3,
+ minButtonIconRect.center().y() + 4) };
+ painter->drawLines(lines, 2);
+ }
+ painter->setPen(textAlphaColor);
+ {
+ const QLine lines[2] = {
+ QLine(minButtonIconRect.center().x() - 3,
+ minButtonIconRect.center().y() + 3,
+ minButtonIconRect.center().x() - 3,
+ minButtonIconRect.center().y() + 4),
+ QLine(minButtonIconRect.center().x() + 4,
+ minButtonIconRect.center().y() + 3,
+ minButtonIconRect.center().x() + 4,
+ minButtonIconRect.center().y() + 4) };
+ painter->drawLines(lines, 2);
+ }
+ }
+
+ // max button
+ if ((titleBar->subControls & SC_TitleBarMaxButton)
+ && (titleBar->titleBarFlags & Qt::WindowMaximizeButtonHint)
+ && !(titleBar->titleBarState & Qt::WindowMaximized)) {
+ bool hover = (titleBar->activeSubControls & SC_TitleBarMaxButton) && (titleBar->state & State_MouseOver);
+ bool sunken = (titleBar->activeSubControls & SC_TitleBarMaxButton) && (titleBar->state & State_Sunken);
+
+ QRect maxButtonRect = subControlRect(CC_TitleBar, titleBar, SC_TitleBarMaxButton, widget);
+ qt_plastique_draw_mdibutton(painter, titleBar, maxButtonRect, hover, sunken);
+
+ int xoffset = maxButtonRect.width() / 3;
+ int yoffset = maxButtonRect.height() / 3;
+
+ QRect maxButtonIconRect(maxButtonRect.left() + xoffset, maxButtonRect.top() + yoffset,
+ maxButtonRect.width() - xoffset * 2, maxButtonRect.height() - yoffset * 2);
+
+ painter->setPen(textColor);
+ painter->drawRect(maxButtonIconRect.adjusted(0, 0, -1, -1));
+ painter->drawLine(maxButtonIconRect.left() + 1, maxButtonIconRect.top() + 1,
+ maxButtonIconRect.right() - 1, maxButtonIconRect.top() + 1);
+ painter->setPen(textAlphaColor);
+ const QPoint points[4] = {
+ maxButtonIconRect.topLeft(), maxButtonIconRect.topRight(),
+ maxButtonIconRect.bottomLeft(), maxButtonIconRect.bottomRight() };
+ painter->drawPoints(points, 4);
+ }
+
+ // close button
+ if (titleBar->subControls & SC_TitleBarCloseButton && titleBar->titleBarFlags & Qt::WindowSystemMenuHint) {
+ bool hover = (titleBar->activeSubControls & SC_TitleBarCloseButton) && (titleBar->state & State_MouseOver);
+ bool sunken = (titleBar->activeSubControls & SC_TitleBarCloseButton) && (titleBar->state & State_Sunken);
+
+ QRect closeButtonRect = subControlRect(CC_TitleBar, titleBar, SC_TitleBarCloseButton, widget);
+ qt_plastique_draw_mdibutton(painter, titleBar, closeButtonRect, hover, sunken);
+
+ int xoffset = closeButtonRect.width() / 3;
+ int yoffset = closeButtonRect.height() / 3;
+
+ QRect closeIconRect(closeButtonRect.left() + xoffset, closeButtonRect.top() + yoffset,
+ closeButtonRect.width() - xoffset * 2, closeButtonRect.height() - yoffset * 2);
+
+ painter->setPen(textAlphaColor);
+ {
+ const QLine lines[4] = {
+ QLine(closeIconRect.left() + 1, closeIconRect.top(),
+ closeIconRect.right(), closeIconRect.bottom() - 1),
+ QLine(closeIconRect.left(), closeIconRect.top() + 1,
+ closeIconRect.right() - 1, closeIconRect.bottom()),
+ QLine(closeIconRect.right() - 1, closeIconRect.top(),
+ closeIconRect.left(), closeIconRect.bottom() - 1),
+ QLine(closeIconRect.right(), closeIconRect.top() + 1,
+ closeIconRect.left() + 1, closeIconRect.bottom()) };
+ painter->drawLines(lines, 4);
+ const QPoint points[4] = {
+ closeIconRect.topLeft(), closeIconRect.topRight(),
+ closeIconRect.bottomLeft(), closeIconRect.bottomRight() };
+ painter->drawPoints(points, 4);
+ }
+ painter->setPen(textColor);
+ {
+ const QLine lines[2] = {
+ QLine(closeIconRect.left() + 1, closeIconRect.top() + 1,
+ closeIconRect.right() - 1, closeIconRect.bottom() - 1),
+ QLine(closeIconRect.left() + 1, closeIconRect.bottom() - 1,
+ closeIconRect.right() - 1, closeIconRect.top() + 1) };
+ painter->drawLines(lines, 2);
+ }
+ }
+
+ // normalize button
+ if ((titleBar->subControls & SC_TitleBarNormalButton) &&
+ (((titleBar->titleBarFlags & Qt::WindowMinimizeButtonHint) &&
+ (titleBar->titleBarState & Qt::WindowMinimized)) ||
+ ((titleBar->titleBarFlags & Qt::WindowMaximizeButtonHint) &&
+ (titleBar->titleBarState & Qt::WindowMaximized)))) {
+ bool hover = (titleBar->activeSubControls & SC_TitleBarNormalButton) && (titleBar->state & State_MouseOver);
+ bool sunken = (titleBar->activeSubControls & SC_TitleBarNormalButton) && (titleBar->state & State_Sunken);
+
+ QRect normalButtonRect = subControlRect(CC_TitleBar, titleBar, SC_TitleBarNormalButton, widget);
+ qt_plastique_draw_mdibutton(painter, titleBar, normalButtonRect, hover, sunken);
+ int xoffset = int(normalButtonRect.width() / 3.5);
+ int yoffset = int(normalButtonRect.height() / 3.5);
+
+ QRect normalButtonIconRect(normalButtonRect.left() + xoffset, normalButtonRect.top() + yoffset,
+ normalButtonRect.width() - xoffset * 2, normalButtonRect.height() - yoffset * 2);
+
+ QRect frontWindowRect = normalButtonIconRect.adjusted(0, 3, -3, 0);
+ painter->setPen(textColor);
+ painter->drawRect(frontWindowRect.adjusted(0, 0, -1, -1));
+ painter->drawLine(frontWindowRect.left() + 1, frontWindowRect.top() + 1,
+ frontWindowRect.right() - 1, frontWindowRect.top() + 1);
+ painter->setPen(textAlphaColor);
+ {
+ const QPoint points[4] = {
+ frontWindowRect.topLeft(), frontWindowRect.topRight(),
+ frontWindowRect.bottomLeft(), frontWindowRect.bottomRight() };
+ painter->drawPoints(points, 4);
+ }
+
+ QRect backWindowRect = normalButtonIconRect.adjusted(3, 0, 0, -3);
+ QRegion clipRegion = backWindowRect;
+ clipRegion -= frontWindowRect;
+ painter->save();
+ painter->setClipRegion(clipRegion);
+ painter->setPen(textColor);
+ painter->drawRect(backWindowRect.adjusted(0, 0, -1, -1));
+ painter->drawLine(backWindowRect.left() + 1, backWindowRect.top() + 1,
+ backWindowRect.right() - 1, backWindowRect.top() + 1);
+ painter->setPen(textAlphaColor);
+ {
+ const QPoint points[4] = {
+ backWindowRect.topLeft(), backWindowRect.topRight(),
+ backWindowRect.bottomLeft(), backWindowRect.bottomRight() };
+ painter->drawPoints(points, 4);
+ }
+ painter->restore();
+ }
+
+ // context help button
+ if (titleBar->subControls & SC_TitleBarContextHelpButton
+ && (titleBar->titleBarFlags & Qt::WindowContextHelpButtonHint)) {
+ bool hover = (titleBar->activeSubControls & SC_TitleBarContextHelpButton) && (titleBar->state & State_MouseOver);
+ bool sunken = (titleBar->activeSubControls & SC_TitleBarContextHelpButton) && (titleBar->state & State_Sunken);
+
+ QRect contextHelpButtonRect = subControlRect(CC_TitleBar, titleBar, SC_TitleBarContextHelpButton, widget);
+
+ qt_plastique_draw_mdibutton(painter, titleBar, contextHelpButtonRect, hover, sunken);
+
+ QColor blend;
+ // ### Use palette colors
+ if (active) {
+ blend = mergedColors(QColor(hover ? 0x7d8bb1 : 0x55689a),
+ QColor(hover ? 0x939ebe : 0x7381ab));
+ } else {
+ blend = mergedColors(QColor(hover ? 0x9e9e9e : 0x818181),
+ QColor(hover ? 0xababab : 0x929292));
+ }
+ QImage image(qt_titlebar_context_help);
+ image.setColor(4, textColor.rgba());
+ image.setColor(3, mergedColors(blend, textColor, 30).rgba());
+ image.setColor(2, mergedColors(blend, textColor, 70).rgba());
+ image.setColor(1, mergedColors(blend, textColor, 90).rgba());
+
+ painter->drawImage(contextHelpButtonRect, image);
+ }
+
+ // shade button
+ if (titleBar->subControls & SC_TitleBarShadeButton) {
+ bool hover = (titleBar->activeSubControls & SC_TitleBarShadeButton) && (titleBar->state & State_MouseOver);
+ bool sunken = (titleBar->activeSubControls & SC_TitleBarShadeButton) && (titleBar->state & State_Sunken);
+
+ QRect shadeButtonRect = subControlRect(CC_TitleBar, titleBar, SC_TitleBarShadeButton, widget);
+ qt_plastique_draw_mdibutton(painter, titleBar, shadeButtonRect, hover, sunken);
+
+ int xoffset = shadeButtonRect.width() / 3;
+ int yoffset = shadeButtonRect.height() / 3;
+
+ QRect shadeButtonIconRect(shadeButtonRect.left() + xoffset, shadeButtonRect.top() + yoffset,
+ shadeButtonRect.width() - xoffset * 2, shadeButtonRect.height() - yoffset * 2);
+
+ QPainterPath path(shadeButtonIconRect.bottomLeft());
+ path.lineTo(shadeButtonIconRect.center().x(), shadeButtonIconRect.bottom() - shadeButtonIconRect.height() / 2);
+ path.lineTo(shadeButtonIconRect.bottomRight());
+ path.lineTo(shadeButtonIconRect.bottomLeft());
+
+ painter->setPen(textAlphaColor);
+ painter->setBrush(textColor);
+ painter->drawPath(path);
+ }
+
+ // unshade button
+ if (titleBar->subControls & SC_TitleBarUnshadeButton) {
+ bool hover = (titleBar->activeSubControls & SC_TitleBarUnshadeButton) && (titleBar->state & State_MouseOver);
+ bool sunken = (titleBar->activeSubControls & SC_TitleBarUnshadeButton) && (titleBar->state & State_Sunken);
+
+ QRect unshadeButtonRect = subControlRect(CC_TitleBar, titleBar, SC_TitleBarUnshadeButton, widget);
+ qt_plastique_draw_mdibutton(painter, titleBar, unshadeButtonRect, hover, sunken);
+
+ int xoffset = unshadeButtonRect.width() / 3;
+ int yoffset = unshadeButtonRect.height() / 3;
+
+ QRect unshadeButtonIconRect(unshadeButtonRect.left() + xoffset, unshadeButtonRect.top() + yoffset,
+ unshadeButtonRect.width() - xoffset * 2, unshadeButtonRect.height() - yoffset * 2);
+
+ int midY = unshadeButtonIconRect.bottom() - unshadeButtonIconRect.height() / 2;
+ QPainterPath path(QPoint(unshadeButtonIconRect.left(), midY));
+ path.lineTo(unshadeButtonIconRect.right(), midY);
+ path.lineTo(unshadeButtonIconRect.center().x(), unshadeButtonIconRect.bottom());
+ path.lineTo(unshadeButtonIconRect.left(), midY);
+
+ painter->setPen(textAlphaColor);
+ painter->setBrush(textColor);
+ painter->drawPath(path);
+ }
+
+ // from qwindowsstyle.cpp
+ if ((titleBar->subControls & SC_TitleBarSysMenu) && (titleBar->titleBarFlags & Qt::WindowSystemMenuHint)) {
+ bool hover = (titleBar->activeSubControls & SC_TitleBarSysMenu) && (titleBar->state & State_MouseOver);
+ bool sunken = (titleBar->activeSubControls & SC_TitleBarSysMenu) && (titleBar->state & State_Sunken);
+
+ QRect iconRect = subControlRect(CC_TitleBar, titleBar, SC_TitleBarSysMenu, widget);
+ if (hover)
+ qt_plastique_draw_mdibutton(painter, titleBar, iconRect, hover, sunken);
+
+ if (!titleBar->icon.isNull()) {
+ titleBar->icon.paint(painter, iconRect);
+ } else {
+ QStyleOption tool(0);
+ tool.palette = titleBar->palette;
+ QPixmap pm = standardPixmap(SP_TitleBarMenuButton, &tool, widget);
+ tool.rect = iconRect;
+ painter->save();
+ drawItemPixmap(painter, iconRect, Qt::AlignCenter, pm);
+ painter->restore();
+ }
+ }
+ painter->restore();
+ }
+ break;
+ default:
+ QWindowsStyle::drawComplexControl(control, option, painter, widget);
+ break;
+ }
+}
+
+/*!
+ \reimp
+*/
+QSize QPlastiqueStyle::sizeFromContents(ContentsType type, const QStyleOption *option,
+ const QSize &size, const QWidget *widget) const
+{
+ QSize newSize = QWindowsStyle::sizeFromContents(type, option, size, widget);
+
+ switch (type) {
+ case CT_RadioButton:
+ ++newSize.rheight();
+ ++newSize.rwidth();
+ break;
+#ifndef QT_NO_SLIDER
+ case CT_Slider:
+ if (const QStyleOptionSlider *slider = qstyleoption_cast<const QStyleOptionSlider *>(option)) {
+ int tickSize = pixelMetric(PM_SliderTickmarkOffset, option, widget);
+ if (slider->tickPosition & QSlider::TicksBelow) {
+ if (slider->orientation == Qt::Horizontal)
+ newSize.rheight() += tickSize;
+ else
+ newSize.rwidth() += tickSize;
+ }
+ if (slider->tickPosition & QSlider::TicksAbove) {
+ if (slider->orientation == Qt::Horizontal)
+ newSize.rheight() += tickSize;
+ else
+ newSize.rwidth() += tickSize;
+ }
+ }
+ break;
+#endif // QT_NO_SLIDER
+#ifndef QT_NO_SCROLLBAR
+ case CT_ScrollBar:
+ if (const QStyleOptionSlider *scrollBar = qstyleoption_cast<const QStyleOptionSlider *>(option)) {
+ int scrollBarExtent = pixelMetric(PM_ScrollBarExtent, option, widget);
+ int scrollBarSliderMinimum = pixelMetric(PM_ScrollBarSliderMin, option, widget);
+ if (scrollBar->orientation == Qt::Horizontal) {
+ newSize = QSize(scrollBarExtent * 3 + scrollBarSliderMinimum, scrollBarExtent);
+ } else {
+ newSize = QSize(scrollBarExtent, scrollBarExtent * 3 + scrollBarSliderMinimum);
+ }
+ }
+ break;
+#endif // QT_NO_SCROLLBAR
+#ifndef QT_NO_SPINBOX
+ case CT_SpinBox:
+ // Make sure the size is odd
+ newSize.setHeight(sizeFromContents(CT_LineEdit, option, size, widget).height());
+ newSize.rheight() -= ((1 - newSize.rheight()) & 1);
+ break;
+#endif
+#ifndef QT_NO_TOOLBUTTON
+ case CT_ToolButton:
+ newSize.rheight() += 3;
+ newSize.rwidth() += 3;
+ break;
+#endif
+#ifndef QT_NO_COMBOBOX
+ case CT_ComboBox:
+ newSize = sizeFromContents(CT_PushButton, option, size, widget);
+ newSize.rwidth() += 30; // Make room for drop-down indicator
+ newSize.rheight() += 4;
+ break;
+#endif
+ case CT_MenuItem:
+ if (const QStyleOptionMenuItem *menuItem = qstyleoption_cast<const QStyleOptionMenuItem *>(option)) {
+ if (menuItem->menuItemType == QStyleOptionMenuItem::Separator)
+ newSize.setHeight(menuItem->text.isEmpty() ? 2 : menuItem->fontMetrics.lineSpacing());
+ }
+ break;
+ case CT_MenuBarItem:
+ newSize.setHeight(newSize.height());
+ break;
+ default:
+ break;
+ }
+
+ return newSize;
+}
+
+/*!
+ \reimp
+*/
+QRect QPlastiqueStyle::subElementRect(SubElement element, const QStyleOption *option, const QWidget *widget) const
+{
+ QRect rect;
+ switch (element) {
+ case SE_RadioButtonIndicator:
+ rect = visualRect(option->direction, option->rect,
+ QWindowsStyle::subElementRect(element, option, widget)).adjusted(0, 0, 1, 1);
+ break;
+#ifndef QT_NO_PROGRESSBAR
+ case SE_ProgressBarLabel:
+ case SE_ProgressBarContents:
+ case SE_ProgressBarGroove:
+ return option->rect;
+#endif // QT_NO_PROGRESSBAR
+ default:
+ return QWindowsStyle::subElementRect(element, option, widget);
+ }
+
+ return visualRect(option->direction, option->rect, rect);
+}
+
+/*!
+ \reimp
+*/
+QRect QPlastiqueStyle::subControlRect(ComplexControl control, const QStyleOptionComplex *option,
+ SubControl subControl, const QWidget *widget) const
+{
+ QRect rect = QWindowsStyle::subControlRect(control, option, subControl, widget);
+
+ switch (control) {
+#ifndef QT_NO_SLIDER
+ case CC_Slider:
+ if (const QStyleOptionSlider *slider = qstyleoption_cast<const QStyleOptionSlider *>(option)) {
+ int tickSize = pixelMetric(PM_SliderTickmarkOffset, option, widget);
+
+ switch (subControl) {
+ case SC_SliderHandle:
+ if (slider->orientation == Qt::Horizontal) {
+ rect.setWidth(11);
+ rect.setHeight(15);
+ int centerY = slider->rect.center().y() - rect.height() / 2;
+ if (slider->tickPosition & QSlider::TicksAbove)
+ centerY += tickSize;
+ if (slider->tickPosition & QSlider::TicksBelow)
+ centerY -= tickSize;
+ rect.moveTop(centerY);
+ } else {
+ rect.setWidth(15);
+ rect.setHeight(11);
+ int centerX = slider->rect.center().x() - rect.width() / 2;
+ if (slider->tickPosition & QSlider::TicksAbove)
+ centerX += tickSize;
+ if (slider->tickPosition & QSlider::TicksBelow)
+ centerX -= tickSize;
+ rect.moveLeft(centerX);
+ }
+ break;
+ case SC_SliderGroove: {
+ QPoint grooveCenter = slider->rect.center();
+ if (slider->orientation == Qt::Horizontal) {
+ rect.setHeight(14);
+ --grooveCenter.ry();
+ if (slider->tickPosition & QSlider::TicksAbove)
+ grooveCenter.ry() += tickSize;
+ if (slider->tickPosition & QSlider::TicksBelow)
+ grooveCenter.ry() -= tickSize;
+ } else {
+ rect.setWidth(14);
+ --grooveCenter.rx();
+ if (slider->tickPosition & QSlider::TicksAbove)
+ grooveCenter.rx() += tickSize;
+ if (slider->tickPosition & QSlider::TicksBelow)
+ grooveCenter.rx() -= tickSize;
+ }
+ rect.moveCenter(grooveCenter);
+ break;
+ }
+ default:
+ break;
+ }
+ }
+ break;
+#endif // QT_NO_SLIDER
+#ifndef QT_NO_SCROLLBAR
+ case CC_ScrollBar:
+ if (const QStyleOptionSlider *scrollBar = qstyleoption_cast<const QStyleOptionSlider *>(option)) {
+ int scrollBarExtent = pixelMetric(PM_ScrollBarExtent, scrollBar, widget);
+ int sliderMaxLength = ((scrollBar->orientation == Qt::Horizontal) ?
+ scrollBar->rect.width() : scrollBar->rect.height()) - (scrollBarExtent * 3);
+ int sliderMinLength = pixelMetric(PM_ScrollBarSliderMin, scrollBar, widget);
+ int sliderLength;
+
+ // calculate slider length
+ if (scrollBar->maximum != scrollBar->minimum) {
+ uint valueRange = scrollBar->maximum - scrollBar->minimum;
+ sliderLength = (scrollBar->pageStep * sliderMaxLength) / (valueRange + scrollBar->pageStep);
+
+ if (sliderLength < sliderMinLength || valueRange > INT_MAX / 2)
+ sliderLength = sliderMinLength;
+ if (sliderLength > sliderMaxLength)
+ sliderLength = sliderMaxLength;
+ } else {
+ sliderLength = sliderMaxLength;
+ }
+
+ int sliderStart = scrollBarExtent + sliderPositionFromValue(scrollBar->minimum,
+ scrollBar->maximum,
+ scrollBar->sliderPosition,
+ sliderMaxLength - sliderLength,
+ scrollBar->upsideDown);
+
+ QRect scrollBarRect = scrollBar->rect;
+
+ switch (subControl) {
+ case SC_ScrollBarSubLine: // top/left button
+ if (scrollBar->orientation == Qt::Horizontal) {
+ rect.setRect(scrollBarRect.left(), scrollBarRect.top(), scrollBarRect.width() - scrollBarExtent, scrollBarRect.height());
+ } else {
+ rect.setRect(scrollBarRect.left(), scrollBarRect.top(), scrollBarRect.width(), scrollBarRect.height() - scrollBarExtent);
+ }
+ break;
+ case SC_ScrollBarAddLine: // bottom/right button
+ if (scrollBar->orientation == Qt::Horizontal) {
+ rect.setRect(scrollBarRect.right() - (scrollBarExtent - 1), scrollBarRect.top(), scrollBarExtent, scrollBarRect.height());
+ } else {
+ rect.setRect(scrollBarRect.left(), scrollBarRect.bottom() - (scrollBarExtent - 1), scrollBarRect.width(), scrollBarExtent);
+ }
+ break;
+ case SC_ScrollBarSubPage:
+ if (scrollBar->orientation == Qt::Horizontal) {
+ rect.setRect(scrollBarRect.left() + scrollBarExtent, scrollBarRect.top(),
+ sliderStart - (scrollBarRect.left() + scrollBarExtent), scrollBarRect.height());
+ } else {
+ rect.setRect(scrollBarRect.left(), scrollBarRect.top() + scrollBarExtent,
+ scrollBarRect.width(), sliderStart - (scrollBarRect.left() + scrollBarExtent));
+ }
+ break;
+ case SC_ScrollBarAddPage:
+ if (scrollBar->orientation == Qt::Horizontal)
+ rect.setRect(sliderStart + sliderLength, 0,
+ sliderMaxLength - sliderStart - sliderLength + scrollBarExtent, scrollBarRect.height());
+ else
+ rect.setRect(0, sliderStart + sliderLength,
+ scrollBarRect.width(), sliderMaxLength - sliderStart - sliderLength + scrollBarExtent);
+ break;
+ case SC_ScrollBarGroove:
+ if (scrollBar->orientation == Qt::Horizontal) {
+ rect = scrollBarRect.adjusted(scrollBarExtent, 0, -2 * scrollBarExtent, 0);
+ } else {
+ rect = scrollBarRect.adjusted(0, scrollBarExtent, 0, -2 * scrollBarExtent);
+ }
+ break;
+ case SC_ScrollBarSlider:
+ if (scrollBar->orientation == Qt::Horizontal) {
+ rect.setRect(sliderStart, 0, sliderLength, scrollBarRect.height());
+ } else {
+ rect.setRect(0, sliderStart, scrollBarRect.width(), sliderLength);
+ }
+ break;
+ default:
+ break;
+ }
+ rect = visualRect(scrollBar->direction, scrollBarRect, rect);
+ }
+ break;
+#endif // QT_NO_SCROLLBAR
+#ifndef QT_NO_SPINBOX
+ case CC_SpinBox:
+ if (const QStyleOptionSpinBox *spinBox = qstyleoption_cast<const QStyleOptionSpinBox *>(option)) {
+ int center = spinBox->rect.height() / 2;
+ switch (subControl) {
+ case SC_SpinBoxUp:
+ if (spinBox->buttonSymbols == QAbstractSpinBox::NoButtons)
+ return QRect();
+ rect = visualRect(spinBox->direction, spinBox->rect, rect);
+ rect.setRect(spinBox->rect.right() - 16, spinBox->rect.top(), 17, center + 1);
+ rect = visualRect(spinBox->direction, spinBox->rect, rect);
+ break;
+ case SC_SpinBoxDown:
+ if (spinBox->buttonSymbols == QAbstractSpinBox::NoButtons)
+ return QRect();
+ rect = visualRect(spinBox->direction, spinBox->rect, rect);
+ rect.setRect(spinBox->rect.right() - 16, spinBox->rect.top() + center, 17, spinBox->rect.height() - center);
+ rect = visualRect(spinBox->direction, spinBox->rect, rect);
+ break;
+ case SC_SpinBoxEditField:
+ if (spinBox->buttonSymbols != QAbstractSpinBox::NoButtons) {
+ rect = spinBox->rect.adjusted(0, 0, -16, 0);
+ } else {
+ rect = spinBox->rect;
+ }
+ rect.adjust(blueFrameWidth, blueFrameWidth, -blueFrameWidth, -blueFrameWidth);
+ rect = visualRect(spinBox->direction, spinBox->rect, rect);
+ break;
+ default:
+ break;
+ }
+ }
+ break;
+#endif // QT_NO_SPINBOX
+#ifndef QT_NO_COMBOBOX
+ case CC_ComboBox:
+ switch (subControl) {
+ case SC_ComboBoxArrow:
+ rect = visualRect(option->direction, option->rect, rect);
+ rect.setRect(rect.right() - 17, rect.top() - 2,
+ 19, rect.height() + 4);
+ rect = visualRect(option->direction, option->rect, rect);
+ break;
+ case SC_ComboBoxEditField: {
+ if (const QStyleOptionComboBox *box = qstyleoption_cast<const QStyleOptionComboBox *>(option)) {
+ int frameWidth = pixelMetric(PM_DefaultFrameWidth);
+ rect = visualRect(option->direction, option->rect, rect);
+
+ if (box->editable) {
+ rect = box->rect.adjusted(blueFrameWidth, blueFrameWidth, -blueFrameWidth, -blueFrameWidth);
+ rect.setRight(rect.right() - 16); // Overlaps the combobox button by 2 pixels
+ } else {
+ rect.setRect(option->rect.left() + frameWidth, option->rect.top() + frameWidth,
+ option->rect.width() - 16 - 2 * frameWidth,
+ option->rect.height() - 2 * frameWidth);
+ rect.setLeft(rect.left() + 2);
+ rect.setRight(rect.right() - 2);
+ if (box->state & (State_Sunken | State_On))
+ rect.translate(1, 1);
+ }
+ rect = visualRect(option->direction, option->rect, rect);
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ break;
+#endif // QT_NO_COMBOBOX
+ case CC_TitleBar:
+ if (const QStyleOptionTitleBar *tb = qstyleoption_cast<const QStyleOptionTitleBar *>(option)) {
+ SubControl sc = subControl;
+ QRect &ret = rect;
+ const int indent = 3;
+ const int controlTopMargin = 4;
+ const int controlBottomMargin = 3;
+ const int controlWidthMargin = 1;
+ const int controlHeight = tb->rect.height() - controlTopMargin - controlBottomMargin;
+ const int delta = controlHeight + controlWidthMargin;
+ int offset = 0;
+
+ bool isMinimized = tb->titleBarState & Qt::WindowMinimized;
+ bool isMaximized = tb->titleBarState & Qt::WindowMaximized;
+
+ switch (sc) {
+ case SC_TitleBarLabel:
+ if (tb->titleBarFlags & (Qt::WindowTitleHint | Qt::WindowSystemMenuHint)) {
+ ret = tb->rect;
+ if (tb->titleBarFlags & Qt::WindowSystemMenuHint)
+ ret.adjust(delta, 0, -delta, 0);
+ if (tb->titleBarFlags & Qt::WindowMinimizeButtonHint)
+ ret.adjust(0, 0, -delta, 0);
+ if (tb->titleBarFlags & Qt::WindowMaximizeButtonHint)
+ ret.adjust(0, 0, -delta, 0);
+ if (tb->titleBarFlags & Qt::WindowShadeButtonHint)
+ ret.adjust(0, 0, -delta, 0);
+ if (tb->titleBarFlags & Qt::WindowContextHelpButtonHint)
+ ret.adjust(0, 0, -delta, 0);
+ ret.adjusted(indent, 0, -indent, 0);
+ }
+ break;
+ case SC_TitleBarContextHelpButton:
+ if (tb->titleBarFlags & Qt::WindowContextHelpButtonHint)
+ offset += delta;
+ case SC_TitleBarMinButton:
+ if (!isMinimized && (tb->titleBarFlags & Qt::WindowMinimizeButtonHint))
+ offset += delta;
+ else if (sc == SC_TitleBarMinButton)
+ break;
+ case SC_TitleBarNormalButton:
+ if (isMinimized && (tb->titleBarFlags & Qt::WindowMinimizeButtonHint))
+ offset += delta;
+ else if (isMaximized && (tb->titleBarFlags & Qt::WindowMaximizeButtonHint))
+ offset += delta;
+ else if (sc == SC_TitleBarNormalButton)
+ break;
+ case SC_TitleBarMaxButton:
+ if (!isMaximized && (tb->titleBarFlags & Qt::WindowMaximizeButtonHint))
+ offset += delta;
+ else if (sc == SC_TitleBarMaxButton)
+ break;
+ case SC_TitleBarShadeButton:
+ if (!isMinimized && (tb->titleBarFlags & Qt::WindowShadeButtonHint))
+ offset += delta;
+ else if (sc == SC_TitleBarShadeButton)
+ break;
+ case SC_TitleBarUnshadeButton:
+ if (isMinimized && (tb->titleBarFlags & Qt::WindowShadeButtonHint))
+ offset += delta;
+ else if (sc == SC_TitleBarUnshadeButton)
+ break;
+ case SC_TitleBarCloseButton:
+ if (tb->titleBarFlags & Qt::WindowSystemMenuHint)
+ offset += delta;
+ else if (sc == SC_TitleBarCloseButton)
+ break;
+ ret.setRect(tb->rect.right() - indent - offset, tb->rect.top() + controlTopMargin,
+ controlHeight, controlHeight);
+ break;
+ case SC_TitleBarSysMenu:
+ if (tb->titleBarFlags & Qt::WindowSystemMenuHint) {
+ ret.setRect(tb->rect.left() + controlWidthMargin + indent, tb->rect.top() + controlTopMargin,
+ controlHeight, controlHeight);
+ }
+ break;
+ default:
+ break;
+ }
+ ret = visualRect(tb->direction, tb->rect, ret);
+ }
+ break;
+ default:
+ break;
+ }
+
+ return rect;
+}
+
+/*!
+ \reimp
+*/
+int QPlastiqueStyle::styleHint(StyleHint hint, const QStyleOption *option, const QWidget *widget,
+ QStyleHintReturn *returnData) const
+{
+ int ret = 0;
+ switch (hint) {
+ case SH_WindowFrame_Mask:
+ ret = 1;
+ if (QStyleHintReturnMask *mask = qstyleoption_cast<QStyleHintReturnMask *>(returnData)) {
+ mask->region = option->rect;
+ mask->region -= QRect(option->rect.left(), option->rect.top(), 2, 1);
+ mask->region -= QRect(option->rect.right() - 1, option->rect.top(), 2, 1);
+ mask->region -= QRect(option->rect.left(), option->rect.top() + 1, 1, 1);
+ mask->region -= QRect(option->rect.right(), option->rect.top() + 1, 1, 1);
+
+ const QStyleOptionTitleBar *titleBar = qstyleoption_cast<const QStyleOptionTitleBar *>(option);
+ if (titleBar && (titleBar->titleBarState & Qt::WindowMinimized)) {
+ mask->region -= QRect(option->rect.left(), option->rect.bottom(), 2, 1);
+ mask->region -= QRect(option->rect.right() - 1, option->rect.bottom(), 2, 1);
+ mask->region -= QRect(option->rect.left(), option->rect.bottom() - 1, 1, 1);
+ mask->region -= QRect(option->rect.right(), option->rect.bottom() - 1, 1, 1);
+ } else {
+ mask->region -= QRect(option->rect.bottomLeft(), QSize(1, 1));
+ mask->region -= QRect(option->rect.bottomRight(), QSize(1, 1));
+ }
+ }
+ break;
+ case SH_TitleBar_NoBorder:
+ ret = 1;
+ break;
+ case SH_TitleBar_AutoRaise:
+ ret = 1;
+ break;
+ case SH_ItemView_ShowDecorationSelected:
+ ret = true;
+ break;
+ case SH_ToolBox_SelectedPageTitleBold:
+ case SH_ScrollBar_MiddleClickAbsolutePosition:
+ ret = true;
+ break;
+ case SH_MainWindow_SpaceBelowMenuBar:
+ ret = 0;
+ break;
+ case SH_FormLayoutWrapPolicy:
+ ret = QFormLayout::DontWrapRows;
+ break;
+ case SH_FormLayoutFieldGrowthPolicy:
+ ret = QFormLayout::ExpandingFieldsGrow;
+ break;
+ case SH_FormLayoutFormAlignment:
+ ret = Qt::AlignLeft | Qt::AlignTop;
+ break;
+ case SH_FormLayoutLabelAlignment:
+ ret = Qt::AlignRight;
+ break;
+ case SH_MessageBox_TextInteractionFlags:
+ ret = Qt::TextSelectableByMouse | Qt::LinksAccessibleByMouse;
+ break;
+ case SH_LineEdit_PasswordCharacter:
+ ret = QCommonStyle::styleHint(hint, option, widget, returnData);
+ break;
+ case SH_ItemView_ArrowKeysNavigateIntoChildren:
+ ret = true;
+ break;
+ case SH_Menu_SubMenuPopupDelay:
+ ret = 96; // from Plastik
+ break;
+ default:
+ ret = QWindowsStyle::styleHint(hint, option, widget, returnData);
+ break;
+ }
+ return ret;
+}
+
+/*!
+ \reimp
+*/
+QStyle::SubControl QPlastiqueStyle::hitTestComplexControl(ComplexControl control, const QStyleOptionComplex *option,
+ const QPoint &pos, const QWidget *widget) const
+{
+ SubControl ret = SC_None;
+ switch (control) {
+#ifndef QT_NO_SCROLLBAR
+ case CC_ScrollBar:
+ if (const QStyleOptionSlider *scrollBar = qstyleoption_cast<const QStyleOptionSlider *>(option)) {
+ QRect slider = subControlRect(control, scrollBar, SC_ScrollBarSlider, widget);
+ if (slider.contains(pos)) {
+ ret = SC_ScrollBarSlider;
+ break;
+ }
+
+ QRect scrollBarAddLine = subControlRect(control, scrollBar, SC_ScrollBarAddLine, widget);
+ if (scrollBarAddLine.contains(pos)) {
+ ret = SC_ScrollBarAddLine;
+ break;
+ }
+
+ QRect scrollBarSubPage = subControlRect(control, scrollBar, SC_ScrollBarSubPage, widget);
+ if (scrollBarSubPage.contains(pos)) {
+ ret = SC_ScrollBarSubPage;
+ break;
+ }
+
+ QRect scrollBarAddPage = subControlRect(control, scrollBar, SC_ScrollBarAddPage, widget);
+ if (scrollBarAddPage.contains(pos)) {
+ ret = SC_ScrollBarAddPage;
+ break;
+ }
+
+ QRect scrollBarSubLine = subControlRect(control, scrollBar, SC_ScrollBarSubLine, widget);
+ if (scrollBarSubLine.contains(pos)) {
+ ret = SC_ScrollBarSubLine;
+ break;
+ }
+ }
+ break;
+#endif // QT_NO_SCROLLBAR
+ default:
+ break;
+ }
+
+ return ret != SC_None ? ret : QWindowsStyle::hitTestComplexControl(control, option, pos, widget);
+}
+
+/*!
+ \reimp
+*/
+int QPlastiqueStyle::pixelMetric(PixelMetric metric, const QStyleOption *option, const QWidget *widget) const
+{
+ int ret = -1;
+ switch (metric) {
+ case PM_MenuVMargin:
+ case PM_MenuHMargin:
+ ret = 0;
+ break;
+ case PM_ToolBarIconSize:
+ ret = 24;
+ break;
+ case PM_ButtonShiftHorizontal:
+ case PM_ButtonShiftVertical:
+ ret = 1;
+ break;
+ case PM_ButtonDefaultIndicator:
+ ret = 0;
+ break;
+#ifndef QT_NO_SLIDER
+ case PM_SliderThickness:
+ ret = 15;
+ break;
+ case PM_SliderLength:
+ case PM_SliderControlThickness:
+ ret = 11;
+ break;
+ case PM_SliderTickmarkOffset:
+ ret = 5;
+ break;
+ case PM_SliderSpaceAvailable:
+ if (const QStyleOptionSlider *slider = qstyleoption_cast<const QStyleOptionSlider *>(option)) {
+ int size = 15;
+ if (slider->tickPosition & QSlider::TicksBelow)
+ ++size;
+ if (slider->tickPosition & QSlider::TicksAbove)
+ ++size;
+ ret = size;
+ break;
+ }
+#endif // QT_NO_SLIDER
+ case PM_ScrollBarExtent:
+ ret = 16;
+ break;
+ case PM_ScrollBarSliderMin:
+ ret = 26;
+ break;
+ case PM_ProgressBarChunkWidth:
+ ret = 1;
+ break;
+ case PM_MenuBarItemSpacing:
+ ret = 3;
+ break;
+ case PM_MenuBarVMargin:
+ ret = 2;
+ break;
+ case PM_MenuBarHMargin:
+ ret = 0;
+ break;
+ case PM_MenuBarPanelWidth:
+ ret = 1;
+ break;
+ case PM_ToolBarHandleExtent:
+ ret = 9;
+ break;
+ case PM_ToolBarSeparatorExtent:
+ ret = 2;
+ break;
+ case PM_ToolBarItemSpacing:
+ ret = 1;
+ break;
+ case PM_ToolBarItemMargin:
+ ret = 1;
+ break;
+ case PM_ToolBarFrameWidth:
+ ret = 2;
+ break;
+ case PM_SplitterWidth:
+ ret = 6;
+ break;
+ case PM_DockWidgetSeparatorExtent:
+ ret = 6;
+ break;
+ case PM_DockWidgetHandleExtent:
+ ret = 20;
+ break;
+ case PM_DefaultFrameWidth:
+#ifndef QT_NO_MENU
+ if (qobject_cast<const QMenu *>(widget)) {
+ ret = 1;
+ break;
+ }
+#endif
+ ret = 2;
+ break;
+ case PM_MdiSubWindowFrameWidth:
+ ret = 4;
+ break;
+ case PM_TitleBarHeight:
+#ifdef QT3_SUPPORT
+ if (widget && widget->inherits("Q3DockWindowTitleBar")) {
+ // Q3DockWindow has smaller title bars than QDockWidget
+ ret = qMax(widget->fontMetrics().lineSpacing(), 20);
+ } else
+#endif
+ ret = qMax(widget ? widget->fontMetrics().lineSpacing() :
+ (option ? option->fontMetrics.lineSpacing() : 0), 30);
+ break;
+ case PM_MaximumDragDistance:
+ return -1;
+ case PM_DockWidgetTitleMargin:
+ return 2;
+ case PM_LayoutHorizontalSpacing:
+ case PM_LayoutVerticalSpacing:
+ return -1; // rely on layoutHorizontalSpacing()
+ case PM_LayoutLeftMargin:
+ case PM_LayoutTopMargin:
+ case PM_LayoutRightMargin:
+ case PM_LayoutBottomMargin:
+ {
+ bool isWindow = false;
+ if (option) {
+ isWindow = (option->state & State_Window);
+ } else if (widget) {
+ isWindow = widget->isWindow();
+ }
+
+ if (isWindow) {
+ ret = 11;
+ } else {
+ ret = 9;
+ }
+ }
+ default:
+ break;
+ }
+
+ return ret != -1 ? ret : QWindowsStyle::pixelMetric(metric, option, widget);
+}
+
+/*!
+ \reimp
+*/
+QPalette QPlastiqueStyle::standardPalette() const
+{
+ QPalette palette;
+
+ palette.setBrush(QPalette::Disabled, QPalette::WindowText, QColor(QRgb(0xff808080)));
+ palette.setBrush(QPalette::Disabled, QPalette::Button, QColor(QRgb(0xffdddfe4)));
+ palette.setBrush(QPalette::Disabled, QPalette::Light, QColor(QRgb(0xffffffff)));
+ palette.setBrush(QPalette::Disabled, QPalette::Midlight, QColor(QRgb(0xffffffff)));
+ palette.setBrush(QPalette::Disabled, QPalette::Dark, QColor(QRgb(0xff555555)));
+ palette.setBrush(QPalette::Disabled, QPalette::Mid, QColor(QRgb(0xffc7c7c7)));
+ palette.setBrush(QPalette::Disabled, QPalette::Text, QColor(QRgb(0xffc7c7c7)));
+ palette.setBrush(QPalette::Disabled, QPalette::BrightText, QColor(QRgb(0xffffffff)));
+ palette.setBrush(QPalette::Disabled, QPalette::ButtonText, QColor(QRgb(0xff808080)));
+ palette.setBrush(QPalette::Disabled, QPalette::Base, QColor(QRgb(0xffefefef)));
+ palette.setBrush(QPalette::Disabled, QPalette::AlternateBase, palette.color(QPalette::Disabled, QPalette::Base).darker(110));
+ palette.setBrush(QPalette::Disabled, QPalette::Window, QColor(QRgb(0xffefefef)));
+ palette.setBrush(QPalette::Disabled, QPalette::Shadow, QColor(QRgb(0xff000000)));
+ palette.setBrush(QPalette::Disabled, QPalette::Highlight, QColor(QRgb(0xff567594)));
+ palette.setBrush(QPalette::Disabled, QPalette::HighlightedText, QColor(QRgb(0xffffffff)));
+ palette.setBrush(QPalette::Disabled, QPalette::Link, QColor(QRgb(0xff0000ee)));
+ palette.setBrush(QPalette::Disabled, QPalette::LinkVisited, QColor(QRgb(0xff52188b)));
+ palette.setBrush(QPalette::Active, QPalette::WindowText, QColor(QRgb(0xff000000)));
+ palette.setBrush(QPalette::Active, QPalette::Button, QColor(QRgb(0xffdddfe4)));
+ palette.setBrush(QPalette::Active, QPalette::Light, QColor(QRgb(0xffffffff)));
+ palette.setBrush(QPalette::Active, QPalette::Midlight, QColor(QRgb(0xffffffff)));
+ palette.setBrush(QPalette::Active, QPalette::Dark, QColor(QRgb(0xff555555)));
+ palette.setBrush(QPalette::Active, QPalette::Mid, QColor(QRgb(0xffc7c7c7)));
+ palette.setBrush(QPalette::Active, QPalette::Text, QColor(QRgb(0xff000000)));
+ palette.setBrush(QPalette::Active, QPalette::BrightText, QColor(QRgb(0xffffffff)));
+ palette.setBrush(QPalette::Active, QPalette::ButtonText, QColor(QRgb(0xff000000)));
+ palette.setBrush(QPalette::Active, QPalette::Base, QColor(QRgb(0xffffffff)));
+ palette.setBrush(QPalette::Active, QPalette::AlternateBase, palette.color(QPalette::Active, QPalette::Base).darker(110));
+ palette.setBrush(QPalette::Active, QPalette::Window, QColor(QRgb(0xffefefef)));
+ palette.setBrush(QPalette::Active, QPalette::Shadow, QColor(QRgb(0xff000000)));
+ palette.setBrush(QPalette::Active, QPalette::Highlight, QColor(QRgb(0xff678db2)));
+ palette.setBrush(QPalette::Active, QPalette::HighlightedText, QColor(QRgb(0xffffffff)));
+ palette.setBrush(QPalette::Active, QPalette::Link, QColor(QRgb(0xff0000ee)));
+ palette.setBrush(QPalette::Active, QPalette::LinkVisited, QColor(QRgb(0xff52188b)));
+ palette.setBrush(QPalette::Inactive, QPalette::WindowText, QColor(QRgb(0xff000000)));
+ palette.setBrush(QPalette::Inactive, QPalette::Button, QColor(QRgb(0xffdddfe4)));
+ palette.setBrush(QPalette::Inactive, QPalette::Light, QColor(QRgb(0xffffffff)));
+ palette.setBrush(QPalette::Inactive, QPalette::Midlight, QColor(QRgb(0xffffffff)));
+ palette.setBrush(QPalette::Inactive, QPalette::Dark, QColor(QRgb(0xff555555)));
+ palette.setBrush(QPalette::Inactive, QPalette::Mid, QColor(QRgb(0xffc7c7c7)));
+ palette.setBrush(QPalette::Inactive, QPalette::Text, QColor(QRgb(0xff000000)));
+ palette.setBrush(QPalette::Inactive, QPalette::BrightText, QColor(QRgb(0xffffffff)));
+ palette.setBrush(QPalette::Inactive, QPalette::ButtonText, QColor(QRgb(0xff000000)));
+ palette.setBrush(QPalette::Inactive, QPalette::Base, QColor(QRgb(0xffffffff)));
+ palette.setBrush(QPalette::Inactive, QPalette::AlternateBase, palette.color(QPalette::Inactive, QPalette::Base).darker(110));
+ palette.setBrush(QPalette::Inactive, QPalette::Window, QColor(QRgb(0xffefefef)));
+ palette.setBrush(QPalette::Inactive, QPalette::Shadow, QColor(QRgb(0xff000000)));
+ palette.setBrush(QPalette::Inactive, QPalette::Highlight, QColor(QRgb(0xff678db2)));
+ palette.setBrush(QPalette::Inactive, QPalette::HighlightedText, QColor(QRgb(0xffffffff)));
+ palette.setBrush(QPalette::Inactive, QPalette::Link, QColor(QRgb(0xff0000ee)));
+ palette.setBrush(QPalette::Inactive, QPalette::LinkVisited, QColor(QRgb(0xff52188b)));
+ return palette;
+}
+
+/*!
+ \reimp
+*/
+void QPlastiqueStyle::polish(QWidget *widget)
+{
+ if (qobject_cast<QPushButton *>(widget)
+#ifndef QT_NO_COMBOBOX
+ || qobject_cast<QComboBox *>(widget)
+#endif
+#ifndef QT_NO_SPINBOX
+ || qobject_cast<QAbstractSpinBox *>(widget)
+#endif
+ || qobject_cast<QCheckBox *>(widget)
+#ifndef QT_NO_GROUPBOX
+ || qobject_cast<QGroupBox *>(widget)
+#endif
+ || qobject_cast<QRadioButton *>(widget)
+#ifndef QT_NO_SPLITTER
+ || qobject_cast<QSplitterHandle *>(widget)
+#endif
+#ifndef QT_NO_TABBAR
+ || qobject_cast<QTabBar *>(widget)
+#endif
+ ) {
+ widget->setAttribute(Qt::WA_Hover);
+ }
+
+ if (widget->inherits("QWorkspaceTitleBar")
+ || widget->inherits("QDockSeparator")
+ || widget->inherits("QDockWidgetSeparator")
+ || widget->inherits("Q3DockWindowResizeHandle")) {
+ widget->setAttribute(Qt::WA_Hover);
+ }
+
+ if (false // to simplify the #ifdefs
+#ifndef QT_NO_MENUBAR
+ || qobject_cast<QMenuBar *>(widget)
+#endif
+#ifdef QT3_SUPPORT
+ || widget->inherits("Q3ToolBar")
+#endif
+#ifndef QT_NO_TOOLBAR
+ || qobject_cast<QToolBar *>(widget)
+ || (widget && qobject_cast<QToolBar *>(widget->parent()))
+#endif
+ ) {
+ widget->setBackgroundRole(QPalette::Window);
+ }
+
+#ifndef QT_NO_PROGRESSBAR
+ if (AnimateBusyProgressBar && qobject_cast<QProgressBar *>(widget))
+ widget->installEventFilter(this);
+#endif
+
+#if defined QPlastique_MaskButtons
+ if (qobject_cast<QPushButton *>(widget) || qobject_cast<QToolButton *>(widget))
+ widget->installEventFilter(this);
+#endif
+}
+
+/*!
+ \reimp
+*/
+void QPlastiqueStyle::unpolish(QWidget *widget)
+{
+ if (qobject_cast<QPushButton *>(widget)
+#ifndef QT_NO_COMBOBOX
+ || qobject_cast<QComboBox *>(widget)
+#endif
+#ifndef QT_NO_SPINBOX
+ || qobject_cast<QAbstractSpinBox *>(widget)
+#endif
+ || qobject_cast<QCheckBox *>(widget)
+#ifndef QT_NO_GROUPBOX
+ || qobject_cast<QGroupBox *>(widget)
+#endif
+#ifndef QT_NO_SPLITTER
+ || qobject_cast<QSplitterHandle *>(widget)
+#endif
+#ifndef QT_NO_TABBAR
+ || qobject_cast<QTabBar *>(widget)
+#endif
+ || qobject_cast<QRadioButton *>(widget)) {
+ widget->setAttribute(Qt::WA_Hover, false);
+ }
+
+ if (widget->inherits("QWorkspaceTitleBar")
+ || widget->inherits("QDockSeparator")
+ || widget->inherits("QDockWidgetSeparator")
+ || widget->inherits("Q3DockWindowResizeHandle")) {
+ widget->setAttribute(Qt::WA_Hover, false);
+ }
+
+ if (false // to simplify the #ifdefs
+#ifndef QT_NO_MENUBAR
+ || qobject_cast<QMenuBar *>(widget)
+#endif
+#ifndef QT_NO_TOOLBOX
+ || qobject_cast<QToolBox *>(widget)
+#endif
+#ifdef QT3_SUPPORT
+ || widget->inherits("Q3ToolBar")
+#endif
+#ifndef QT_NO_TOOLBAR
+ || qobject_cast<QToolBar *>(widget)
+ || (widget && qobject_cast<QToolBar *>(widget->parent()))
+#endif
+ ) {
+ widget->setBackgroundRole(QPalette::Button);
+ }
+
+#ifndef QT_NO_PROGRESSBAR
+ if (AnimateBusyProgressBar && qobject_cast<QProgressBar *>(widget)) {
+ Q_D(QPlastiqueStyle);
+ widget->removeEventFilter(this);
+ d->bars.removeAll(static_cast<QProgressBar*>(widget));
+ }
+#endif
+
+#if defined QPlastique_MaskButtons
+ if (qobject_cast<QPushButton *>(widget) || qobject_cast<QToolButton *>(widget))
+ widget->removeEventFilter(this);
+#endif
+}
+
+/*!
+ \reimp
+*/
+void QPlastiqueStyle::polish(QApplication *app)
+{
+ QWindowsStyle::polish(app);
+}
+
+/*!
+ \reimp
+*/
+void QPlastiqueStyle::polish(QPalette &pal)
+{
+ QWindowsStyle::polish(pal);
+ pal.setBrush(QPalette::AlternateBase, pal.base().color().darker(110));
+#ifdef Q_WS_MAC
+ pal.setBrush(QPalette::Shadow, Qt::black);
+#endif
+}
+
+/*!
+ \reimp
+*/
+void QPlastiqueStyle::unpolish(QApplication *app)
+{
+ QWindowsStyle::unpolish(app);
+}
+
+/*!
+ \internal
+*/
+QIcon QPlastiqueStyle::standardIconImplementation(StandardPixmap standardIcon, const QStyleOption *option,
+ const QWidget *widget) const
+{
+ return QWindowsStyle::standardIconImplementation(standardIcon, option, widget);
+}
+
+/*!
+ \reimp
+*/
+QPixmap QPlastiqueStyle::standardPixmap(StandardPixmap standardPixmap, const QStyleOption *opt,
+ const QWidget *widget) const
+{
+ return QWindowsStyle::standardPixmap(standardPixmap, opt, widget);
+}
+
+// this works as long as we have at most 16 different control types
+#define CT1(c) CT2(c, c)
+#define CT2(c1, c2) (((uint)c1 << 16) | (uint)c2)
+
+/*!
+ \internal
+*/
+int QPlastiqueStyle::layoutSpacingImplementation(QSizePolicy::ControlType control1,
+ QSizePolicy::ControlType control2,
+ Qt::Orientation orientation,
+ const QStyleOption * /* option */,
+ const QWidget * /* widget */) const
+{
+ const int ButtonMask = QSizePolicy::ButtonBox | QSizePolicy::PushButton;
+
+ if (control2 == QSizePolicy::ButtonBox)
+ return 11;
+
+ if ((control1 | control2) & ButtonMask)
+ return (orientation == Qt::Horizontal) ? 10 : 9;
+
+ switch (CT2(control1, control2)) {
+ case CT1(QSizePolicy::Label):
+ case CT2(QSizePolicy::Label, QSizePolicy::DefaultType):
+ case CT2(QSizePolicy::Label, QSizePolicy::CheckBox):
+ case CT2(QSizePolicy::Label, QSizePolicy::ComboBox):
+ case CT2(QSizePolicy::Label, QSizePolicy::LineEdit):
+ case CT2(QSizePolicy::Label, QSizePolicy::RadioButton):
+ case CT2(QSizePolicy::Label, QSizePolicy::Slider):
+ case CT2(QSizePolicy::Label, QSizePolicy::SpinBox):
+ case CT2(QSizePolicy::Label, QSizePolicy::ToolButton):
+ return 5;
+ case CT2(QSizePolicy::CheckBox, QSizePolicy::RadioButton):
+ case CT2(QSizePolicy::RadioButton, QSizePolicy::CheckBox):
+ case CT1(QSizePolicy::CheckBox):
+ if (orientation == Qt::Vertical)
+ return 2;
+ case CT1(QSizePolicy::RadioButton):
+ if (orientation == Qt::Vertical)
+ return 1;
+ }
+
+ if (orientation == Qt::Horizontal
+ && (control2 & (QSizePolicy::CheckBox | QSizePolicy::RadioButton)))
+ return 8;
+
+ if ((control1 | control2) & (QSizePolicy::Frame
+ | QSizePolicy::GroupBox
+ | QSizePolicy::TabWidget)) {
+ return 11;
+ }
+
+ if ((control1 | control2) & (QSizePolicy::Line | QSizePolicy::Slider
+ | QSizePolicy::LineEdit | QSizePolicy::ComboBox
+ | QSizePolicy::SpinBox))
+ return 7;
+
+ return 6;
+}
+
+/*!
+ \reimp
+*/
+bool QPlastiqueStyle::eventFilter(QObject *watched, QEvent *event)
+{
+#ifndef QT_NO_PROGRESSBAR
+ Q_D(QPlastiqueStyle);
+
+ switch (event->type()) {
+ case QEvent::Show:
+ if (QProgressBar *bar = qobject_cast<QProgressBar *>(watched)) {
+ d->bars.append(bar);
+ if (d->bars.size() == 1) {
+ Q_ASSERT(ProgressBarFps > 0);
+ d->timer.start();
+ d->progressBarAnimateTimer = startTimer(1000 / ProgressBarFps);
+ }
+ }
+ break;
+ case QEvent::Destroy:
+ case QEvent::Hide:
+ if(!d->bars.isEmpty()) {
+ d->bars.removeAll(reinterpret_cast<QProgressBar*>(watched));
+ if (d->bars.isEmpty()) {
+ killTimer(d->progressBarAnimateTimer);
+ d->progressBarAnimateTimer = 0;
+ }
+ }
+ break;
+#if defined QPlastique_MaskButtons
+ case QEvent::Resize:
+ if (qobject_cast<QPushButton *>(watched) || qobject_cast<QToolButton *>(watched)) {
+ QWidget *widget = qobject_cast<QWidget *>(watched);
+ QRect rect = widget->rect();
+ QRegion region(rect);
+ region -= QRect(rect.left(), rect.top(), 2, 1);
+ region -= QRect(rect.left(), rect.top() + 1, 1, 1);
+ region -= QRect(rect.left(), rect.bottom(), 2, 1);
+ region -= QRect(rect.left(), rect.bottom() - 1, 1, 1);
+ region -= QRect(rect.right() - 1, rect.top(), 2, 1);
+ region -= QRect(rect.right(), rect.top() + 1, 1, 1);
+ region -= QRect(rect.right() - 1, rect.bottom(), 2, 1);
+ region -= QRect(rect.right(), rect.bottom() - 1, 1, 1);
+ widget->setMask(region);
+ }
+ break;
+#endif
+ default:
+ break;
+ }
+#endif // QT_NO_PROGRESSBAR
+
+ return QWindowsStyle::eventFilter(watched, event);
+}
+
+/*!
+ \reimp
+*/
+void QPlastiqueStyle::timerEvent(QTimerEvent *event)
+{
+#ifndef QT_NO_PROGRESSBAR
+ Q_D(QPlastiqueStyle);
+
+ if (event->timerId() == d->progressBarAnimateTimer) {
+ Q_ASSERT(ProgressBarFps > 0);
+ d->animateStep = d->timer.elapsed() / (1000 / ProgressBarFps);
+ foreach (QProgressBar *bar, d->bars) {
+ if (AnimateProgressBar || (bar->minimum() == 0 && bar->maximum() == 0))
+ bar->update();
+ }
+ }
+#endif // QT_NO_PROGRESSBAR
+ event->ignore();
+}
+
+QT_END_NAMESPACE
+
+#endif // !defined(QT_NO_STYLE_PLASTIQUE) || defined(QT_PLUGIN)
diff --git a/src/gui/styles/qplastiquestyle.h b/src/gui/styles/qplastiquestyle.h
new file mode 100644
index 0000000000..7e236b8b20
--- /dev/null
+++ b/src/gui/styles/qplastiquestyle.h
@@ -0,0 +1,119 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QPLASTIQUESTYLE_H
+#define QPLASTIQUESTYLE_H
+
+#include <QtGui/qwindowsstyle.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+#if !defined(QT_NO_STYLE_PLASTIQUE)
+
+class QPlastiqueStylePrivate;
+class Q_GUI_EXPORT QPlastiqueStyle : public QWindowsStyle
+{
+ Q_OBJECT
+ Q_DECLARE_PRIVATE(QPlastiqueStyle)
+public:
+ QPlastiqueStyle();
+ ~QPlastiqueStyle();
+
+ void drawPrimitive(PrimitiveElement element, const QStyleOption *option,
+ QPainter *painter, const QWidget *widget = 0) const;
+ void drawControl(ControlElement element, const QStyleOption *option,
+ QPainter *painter, const QWidget *widget) const;
+ void drawComplexControl(ComplexControl control, const QStyleOptionComplex *option,
+ QPainter *painter, const QWidget *widget) const;
+ QSize sizeFromContents(ContentsType type, const QStyleOption *option,
+ const QSize &size, const QWidget *widget) const;
+
+ QRect subElementRect(SubElement element, const QStyleOption *option, const QWidget *widget) const;
+ QRect subControlRect(ComplexControl cc, const QStyleOptionComplex *opt,
+ SubControl sc, const QWidget *widget) const;
+
+ int styleHint(StyleHint hint, const QStyleOption *option = 0, const QWidget *widget = 0,
+ QStyleHintReturn *returnData = 0) const;
+ SubControl hitTestComplexControl(ComplexControl control, const QStyleOptionComplex *option,
+ const QPoint &pos, const QWidget *widget = 0) const;
+
+ int pixelMetric(PixelMetric metric, const QStyleOption *option = 0, const QWidget *widget = 0) const;
+
+ QPixmap standardPixmap(StandardPixmap standardPixmap, const QStyleOption *opt,
+ const QWidget *widget = 0) const;
+
+ void polish(QWidget *widget);
+ void polish(QApplication *app);
+ void polish(QPalette &pal);
+ void unpolish(QWidget *widget);
+ void unpolish(QApplication *app);
+
+ QPalette standardPalette() const;
+
+protected Q_SLOTS:
+ QIcon standardIconImplementation(StandardPixmap standardIcon, const QStyleOption *opt = 0,
+ const QWidget *widget = 0) const;
+ int layoutSpacingImplementation(QSizePolicy::ControlType control1,
+ QSizePolicy::ControlType control2,
+ Qt::Orientation orientation,
+ const QStyleOption *option = 0,
+ const QWidget *widget = 0) const;
+
+protected:
+ bool eventFilter(QObject *watched, QEvent *event);
+ void timerEvent(QTimerEvent *event);
+
+private:
+ Q_DISABLE_COPY(QPlastiqueStyle)
+ void *reserved;
+};
+
+#endif // QT_NO_STYLE_PLASTIQUE
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QPLASTIQUESTYLE_H
diff --git a/src/gui/styles/qstyle.cpp b/src/gui/styles/qstyle.cpp
new file mode 100644
index 0000000000..982f48f942
--- /dev/null
+++ b/src/gui/styles/qstyle.cpp
@@ -0,0 +1,2445 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qstyle.h"
+#include "qapplication.h"
+#include "qpainter.h"
+#include "qwidget.h"
+#include "qbitmap.h"
+#include "qpixmapcache.h"
+#include "qstyleoption.h"
+#include "private/qstyle_p.h"
+#ifndef QT_NO_DEBUG
+#include "qdebug.h"
+#endif
+
+#ifdef Q_WS_X11
+#include <qx11info_x11.h>
+#endif
+
+#include <limits.h>
+
+QT_BEGIN_NAMESPACE
+
+static const int MaxBits = 8 * sizeof(QSizePolicy::ControlType);
+
+static int unpackControlTypes(QSizePolicy::ControlTypes controls, QSizePolicy::ControlType *array)
+{
+ if (!controls)
+ return 0;
+
+ // optimization: exactly one bit is set
+ if ((controls & (controls - 1)) == 0) {
+ array[0] = QSizePolicy::ControlType(uint(controls));
+ return 1;
+ }
+
+ int count = 0;
+ for (int i = 0; i < MaxBits; ++i) {
+ if (uint bit = (controls & (0x1 << i)))
+ array[count++] = QSizePolicy::ControlType(bit);
+ }
+ return count;
+}
+
+/*!
+ \class QStyle
+ \brief The QStyle class is an abstract base class that encapsulates the look and feel of a GUI.
+
+ \ingroup appearance
+
+ Qt contains a set of QStyle subclasses that emulate the styles of
+ the different platforms supported by Qt (QWindowsStyle,
+ QMacStyle, QMotifStyle, etc.). By default, these styles are built
+ into the QtGui library. Styles can also be made available as
+ plugins.
+
+ Qt's built-in widgets use QStyle to perform nearly all of their
+ drawing, ensuring that they look exactly like the equivalent
+ native widgets. The diagram below shows a QComboBox in eight
+ different styles.
+
+ \img qstyle-comboboxes.png Eight combo boxes
+
+ Topics:
+
+ \tableofcontents
+
+ \section1 Setting a Style
+
+ The style of the entire application can be set using the
+ QApplication::setStyle() function. It can also be specified by the
+ user of the application, using the \c -style command-line option:
+
+ \snippet doc/src/snippets/code/src_gui_styles_qstyle.cpp 0
+
+ If no style is specified, Qt will choose the most appropriate
+ style for the user's platform or desktop environment.
+
+ A style can also be set on an individual widget using the
+ QWidget::setStyle() function.
+
+ \section1 Developing Style-Aware Custom Widgets
+
+ If you are developing custom widgets and want them to look good on
+ all platforms, you can use QStyle functions to perform parts of
+ the widget drawing, such as drawItemText(), drawItemPixmap(),
+ drawPrimitive(), drawControl(), and drawComplexControl().
+
+ Most QStyle draw functions take four arguments:
+ \list
+ \o an enum value specifying which graphical element to draw
+ \o a QStyleOption specifying how and where to render that element
+ \o a QPainter that should be used to draw the element
+ \o a QWidget on which the drawing is performed (optional)
+ \endlist
+
+ For example, if you want to draw a focus rectangle on your
+ widget, you can write:
+
+ \snippet doc/src/snippets/styles/styles.cpp 1
+
+ QStyle gets all the information it needs to render the graphical
+ element from QStyleOption. The widget is passed as the last
+ argument in case the style needs it to perform special effects
+ (such as animated default buttons on Mac OS X), but it isn't
+ mandatory. In fact, you can use QStyle to draw on any paint
+ device, not just widgets, by setting the QPainter properly.
+
+ QStyleOption has various subclasses for the various types of
+ graphical elements that can be drawn. For example,
+ PE_FrameFocusRect expects a QStyleOptionFocusRect argument.
+
+ To ensure that drawing operations are as fast as possible,
+ QStyleOption and its subclasses have public data members. See the
+ QStyleOption class documentation for details on how to use it.
+
+ For convenience, Qt provides the QStylePainter class, which
+ combines a QStyle, a QPainter, and a QWidget. This makes it
+ possible to write
+
+ \snippet doc/src/snippets/styles/styles.cpp 5
+ \dots
+ \snippet doc/src/snippets/styles/styles.cpp 7
+
+ instead of
+
+ \snippet doc/src/snippets/styles/styles.cpp 2
+ \dots
+ \snippet doc/src/snippets/styles/styles.cpp 3
+
+ \section1 Creating a Custom Style
+
+ If you want to design a custom look and feel for your application,
+ the first step is to pick one of the styles provided with Qt to
+ build your custom style from. The choice will depend on which
+ existing style resembles your style the most. The most general
+ class that you can use as base is QCommonStyle (and not QStyle).
+ This is because Qt requires its styles to be \l{QCommonStyle}s.
+
+ Depending on which parts of the base style you want to change,
+ you must reimplement the functions that are used to draw those
+ parts of the interface. To illustrate this, we will modify the
+ look of the spin box arrows drawn by QWindowsStyle. The arrows
+ are \e{primitive elements} that are drawn by the drawPrimitive()
+ function, so we need to reimplement that function. We need the
+ following class declaration:
+
+ \snippet doc/src/snippets/customstyle/customstyle.h 0
+
+ To draw its up and down arrows, QSpinBox uses the
+ PE_IndicatorSpinUp and PE_IndicatorSpinDown primitive elements.
+ Here's how to reimplement the drawPrimitive() function to draw
+ them differently:
+
+ \snippet doc/src/snippets/customstyle/customstyle.cpp 2
+ \snippet doc/src/snippets/customstyle/customstyle.cpp 3
+ \snippet doc/src/snippets/customstyle/customstyle.cpp 4
+
+ Notice that we don't use the \c widget argument, except to pass it
+ on to the QWindowStyle::drawPrimitive() function. As mentioned
+ earlier, the information about what is to be drawn and how it
+ should be drawn is specified by a QStyleOption object, so there is
+ no need to ask the widget.
+
+ If you need to use the \c widget argument to obtain additional
+ information, be careful to ensure that it isn't 0 and that it is
+ of the correct type before using it. For example:
+
+ \snippet doc/src/snippets/customstyle/customstyle.cpp 0
+ \dots
+ \snippet doc/src/snippets/customstyle/customstyle.cpp 1
+
+ When implementing a custom style, you cannot assume that the
+ widget is a QSpinBox just because the enum value is called
+ PE_IndicatorSpinUp or PE_IndicatorSpinDown.
+
+ The documentation for the \l{widgets/styles}{Styles} example
+ covers this topic in more detail.
+
+ \warning Qt style sheets are currently not supported for custom QStyle
+ subclasses. We plan to address this in some future release.
+
+
+ \section1 Using a Custom Style
+
+ There are several ways of using a custom style in a Qt
+ application. The simplest way is call the
+ QApplication::setStyle() static function before creating the
+ QApplication object:
+
+ \snippet snippets/customstyle/main.cpp using a custom style
+
+ You can call QApplication::setStyle() at any time, but by calling
+ it before the constructor, you ensure that the user's preference,
+ set using the \c -style command-line option, is respected.
+
+ You may want to make your style available for use in other
+ applications, some of which may not be yours and are not available for
+ you to recompile. The Qt Plugin system makes it possible to create
+ styles as plugins. Styles created as plugins are loaded as shared
+ objects at runtime by Qt itself. Please refer to the \link
+ plugins-howto.html Qt Plugin\endlink documentation for more
+ information on how to go about creating a style plugin.
+
+ Compile your plugin and put it into Qt's \c plugins/styles
+ directory. We now have a pluggable style that Qt can load
+ automatically. To use your new style with existing applications,
+ simply start the application with the following argument:
+
+ \snippet doc/src/snippets/code/src_gui_styles_qstyle.cpp 1
+
+ The application will use the look and feel from the custom style you
+ implemented.
+
+ \section1 Right-to-Left Desktops
+
+ Languages written from right to left (such as Arabic and Hebrew)
+ usually also mirror the whole layout of widgets, and require the
+ light to come from the screen's top-right corner instead of
+ top-left.
+
+ If you create a custom style, you should take special care when
+ drawing asymmetric elements to make sure that they also look
+ correct in a mirrored layout. An easy way to test your styles is
+ to run applications with the \c -reverse command-line option or
+ to call QApplication::setLayoutDirection() in your \c main()
+ function.
+
+ Here are some things to keep in mind when making a style work well in a
+ right-to-left environment:
+
+ \list
+ \o subControlRect() and subElementRect() return rectangles in screen coordinates
+ \o QStyleOption::direction indicates in which direction the item should be drawn in
+ \o If a style is not right-to-left aware it will display items as if it were left-to-right
+ \o visualRect(), visualPos(), and visualAlignment() are helpful functions that will
+ translate from logical to screen representations.
+ \o alignedRect() will return a logical rect aligned for the current direction
+ \endlist
+
+ \section1 Styles in Item Views
+
+ The painting of items in views is performed by a delegate. Qt's
+ default delegate, QStyledItemDelegate, is also used for for calculating bounding
+ rectangles of items, and their sub-elements for the various kind
+ of item \l{Qt::ItemDataRole}{data roles}
+ QStyledItemDelegate supports. See the QStyledItemDelegate class
+ description to find out which datatypes and roles are supported. You
+ can read more about item data roles in \l{Model/View Programming}.
+
+ When QStyledItemDelegate paints its items, it draws
+ CE_ItemViewItem, and calculates their size with CT_ItemViewItem.
+ Note also that it uses SE_ItemViewItemText to set the size of
+ editors. When implementing a style to customize drawing of item
+ views, you need to check the implementation of QCommonStyle (and
+ any other subclasses from which your style
+ inherits). This way, you find out which and how
+ other style elements are painted, and you can then reimplement the
+ painting of elements that should be drawn differently.
+
+ We include a small example where we customize the drawing of item
+ backgrounds.
+
+ \snippet doc/src/snippets/customviewstyle.cpp 0
+
+ The primitive element PE_PanelItemViewItem is responsible for
+ painting the background of items, and is called from
+ \l{QCommonStyle}'s implementation of CE_ItemViewItem.
+
+ To add support for drawing of new datatypes and item data roles,
+ it is necessary to create a custom delegate. But if you only
+ need to support the datatypes implemented by the default
+ delegate, a custom style does not need an accompanying
+ delegate. The QStyledItemDelegate class description gives more
+ information on custom delegates.
+
+ The drawing of item view headers is also done by the style, giving
+ control over size of header items and row and column sizes.
+
+ \sa QStyleOption, QStylePainter, {Styles Example},
+ {Implementing Styles and Style Aware Widgets}, QStyledItemDelegate
+*/
+
+/*!
+ Constructs a style object.
+*/
+QStyle::QStyle()
+ : QObject(*new QStylePrivate)
+{
+}
+
+
+/*!
+ \internal
+
+ Constructs a style object.
+*/
+QStyle::QStyle(QStylePrivate &dd)
+ : QObject(dd)
+{
+}
+
+/*!
+ Destroys the style object.
+*/
+QStyle::~QStyle()
+{
+}
+
+/*!
+ Initializes the appearance of the given \a widget.
+
+ This function is called for every widget at some point after it
+ has been fully created but just \e before it is shown for the very
+ first time.
+
+ Note that the default implementation does nothing. Reasonable
+ actions in this function might be to call the
+ QWidget::setBackgroundMode() function for the widget. Do not use
+ the function to set, for example, the geometry; reimplementing
+ this function do provide a back-door through which the appearance
+ of a widget can be changed, but with Qt 4.0's style engine there
+ is rarely necessary to implement this function; reimplement the
+ drawItemPixmap(), drawItemText(), drawPrimitive(), etc. instead.
+
+ The QWidget::inherits() function may provide enough information to
+ allow class-specific customizations. But because new QStyle
+ subclasses are expected to work reasonably with all current and \e
+ future widgets, limited use of hard-coded customization is
+ recommended.
+
+ \sa unpolish()
+*/
+void QStyle::polish(QWidget * /* widget */)
+{
+}
+
+/*!
+ Uninitialize the given \a{widget}'s appearance.
+
+ This function is the counterpart to polish(). It is called for
+ every polished widget whenever the style is dynamically changed;
+ the former style has to unpolish its settings before the new style
+ can polish them again.
+
+ Note that unpolish() will only be called if the widget is
+ destroyed. This can cause problems in some cases, e.g, if you
+ remove a widget from the UI, cache it, and then reinsert it after
+ the style has changed; some of Qt's classes cache their widgets.
+
+ \sa polish()
+*/
+void QStyle::unpolish(QWidget * /* widget */)
+{
+}
+
+/*!
+ \fn void QStyle::polish(QApplication * application)
+ \overload
+
+ Late initialization of the given \a application object.
+*/
+void QStyle::polish(QApplication * /* app */)
+{
+}
+
+/*!
+ \fn void QStyle::unpolish(QApplication * application)
+ \overload
+
+ Uninitialize the given \a application.
+*/
+void QStyle::unpolish(QApplication * /* app */)
+{
+}
+
+/*!
+ \fn void QStyle::polish(QPalette & palette)
+ \overload
+
+ Changes the \a palette according to style specific requirements
+ for color palettes (if any).
+
+ \sa QPalette, QApplication::setPalette()
+*/
+void QStyle::polish(QPalette & /* pal */)
+{
+}
+
+/*!
+ \fn QRect QStyle::itemTextRect(const QFontMetrics &metrics, const QRect &rectangle, int alignment, bool enabled, const QString &text) const
+
+ Returns the area within the given \a rectangle in which to draw
+ the provided \a text according to the specified font \a metrics
+ and \a alignment. The \a enabled parameter indicates whether or
+ not the associated item is enabled.
+
+ If the given \a rectangle is larger than the area needed to render
+ the \a text, the rectangle that is returned will be offset within
+ \a rectangle according to the specified \a alignment. For
+ example, if \a alignment is Qt::AlignCenter, the returned
+ rectangle will be centered within \a rectangle. If the given \a
+ rectangle is smaller than the area needed, the returned rectangle
+ will be the smallest rectangle large enough to render the \a text.
+
+ \sa Qt::Alignment
+*/
+QRect QStyle::itemTextRect(const QFontMetrics &metrics, const QRect &rect, int alignment, bool enabled,
+ const QString &text) const
+{
+ QRect result;
+ int x, y, w, h;
+ rect.getRect(&x, &y, &w, &h);
+ if (!text.isEmpty()) {
+ result = metrics.boundingRect(x, y, w, h, alignment, text);
+ if (!enabled && styleHint(SH_EtchDisabledText)) {
+ result.setWidth(result.width()+1);
+ result.setHeight(result.height()+1);
+ }
+ } else {
+ result = QRect(x, y, w, h);
+ }
+ return result;
+}
+
+/*!
+ \fn QRect QStyle::itemPixmapRect(const QRect &rectangle, int alignment, const QPixmap &pixmap) const
+
+ Returns the area within the given \a rectangle in which to draw
+ the specified \a pixmap according to the defined \a alignment.
+*/
+QRect QStyle::itemPixmapRect(const QRect &rect, int alignment, const QPixmap &pixmap) const
+{
+ QRect result;
+ int x, y, w, h;
+ rect.getRect(&x, &y, &w, &h);
+ if ((alignment & Qt::AlignVCenter) == Qt::AlignVCenter)
+ y += h/2 - pixmap.height()/2;
+ else if ((alignment & Qt::AlignBottom) == Qt::AlignBottom)
+ y += h - pixmap.height();
+ if ((alignment & Qt::AlignRight) == Qt::AlignRight)
+ x += w - pixmap.width();
+ else if ((alignment & Qt::AlignHCenter) == Qt::AlignHCenter)
+ x += w/2 - pixmap.width()/2;
+ else if ((alignment & Qt::AlignLeft) != Qt::AlignLeft && QApplication::isRightToLeft())
+ x += w - pixmap.width();
+ result = QRect(x, y, pixmap.width(), pixmap.height());
+ return result;
+}
+
+/*!
+ \fn void QStyle::drawItemText(QPainter *painter, const QRect &rectangle, int alignment, const QPalette &palette, bool enabled, const QString& text, QPalette::ColorRole textRole) const
+
+ Draws the given \a text in the specified \a rectangle using the
+ provided \a painter and \a palette.
+
+ The text is drawn using the painter's pen, and aligned and wrapped
+ according to the specified \a alignment. If an explicit \a
+ textRole is specified, the text is drawn using the \a palette's
+ color for the given role. The \a enabled parameter indicates
+ whether or not the item is enabled; when reimplementing this
+ function, the \a enabled parameter should influence how the item is
+ drawn.
+
+ \sa Qt::Alignment, drawItemPixmap()
+*/
+void QStyle::drawItemText(QPainter *painter, const QRect &rect, int alignment, const QPalette &pal,
+ bool enabled, const QString& text, QPalette::ColorRole textRole) const
+{
+ if (text.isEmpty())
+ return;
+ QPen savedPen;
+ if (textRole != QPalette::NoRole) {
+ savedPen = painter->pen();
+ painter->setPen(QPen(pal.brush(textRole), savedPen.widthF()));
+ }
+ if (!enabled) {
+ if (styleHint(SH_DitherDisabledText)) {
+ painter->drawText(rect, alignment, text);
+ painter->fillRect(painter->boundingRect(rect, alignment, text), QBrush(painter->background().color(), Qt::Dense5Pattern));
+ return;
+ } else if (styleHint(SH_EtchDisabledText)) {
+ QPen pen = painter->pen();
+ painter->setPen(pal.light().color());
+ painter->drawText(rect.adjusted(1, 1, 1, 1), alignment, text);
+ painter->setPen(pen);
+ }
+ }
+ painter->drawText(rect, alignment, text);
+ if (textRole != QPalette::NoRole)
+ painter->setPen(savedPen);
+}
+
+/*!
+ \fn void QStyle::drawItemPixmap(QPainter *painter, const QRect &rectangle, int alignment,
+ const QPixmap &pixmap) const
+
+ Draws the given \a pixmap in the specified \a rectangle, according
+ to the specified \a alignment, using the provided \a painter.
+
+ \sa drawItemText()
+*/
+
+void QStyle::drawItemPixmap(QPainter *painter, const QRect &rect, int alignment,
+ const QPixmap &pixmap) const
+{
+ QRect aligned = alignedRect(QApplication::layoutDirection(), QFlag(alignment), pixmap.size(), rect);
+ QRect inter = aligned.intersected(rect);
+
+ painter->drawPixmap(inter.x(), inter.y(), pixmap, inter.x() - aligned.x(), inter.y() - aligned.y(), inter.width(), inter.height());
+}
+
+/*!
+ \enum QStyle::PrimitiveElement
+
+ This enum describes that various primitive elements. A
+ primitive element is a common GUI element, such as a checkbox
+ indicator or button bevel.
+
+ \omitvalue PE_IndicatorViewItemCheck
+ \value PE_FrameStatusBar Frame
+
+ \value PE_PanelButtonCommand Button used to initiate an action, for
+ example, a QPushButton.
+
+ \value PE_FrameDefaultButton This frame around a default button, e.g. in a dialog.
+ \value PE_PanelButtonBevel Generic panel with a button bevel.
+ \value PE_PanelButtonTool Panel for a Tool button, used with QToolButton.
+ \value PE_PanelLineEdit Panel for a QLineEdit.
+ \value PE_IndicatorButtonDropDown Indicator for a drop down button, for example, a tool
+ button that displays a menu.
+
+ \value PE_FrameFocusRect Generic focus indicator.
+
+ \value PE_IndicatorArrowUp Generic Up arrow.
+ \value PE_IndicatorArrowDown Generic Down arrow.
+ \value PE_IndicatorArrowRight Generic Right arrow.
+ \value PE_IndicatorArrowLeft Generic Left arrow.
+
+ \value PE_IndicatorSpinUp Up symbol for a spin widget, for example a QSpinBox.
+ \value PE_IndicatorSpinDown Down symbol for a spin widget.
+ \value PE_IndicatorSpinPlus Increase symbol for a spin widget.
+ \value PE_IndicatorSpinMinus Decrease symbol for a spin widget.
+
+ \value PE_IndicatorItemViewItemCheck On/off indicator for a view item.
+
+ \value PE_IndicatorCheckBox On/off indicator, for example, a QCheckBox.
+ \value PE_IndicatorRadioButton Exclusive on/off indicator, for example, a QRadioButton.
+
+ \value PE_Q3DockWindowSeparator Item separator for Qt 3 compatible dock window
+ and toolbar contents.
+ \value PE_IndicatorDockWidgetResizeHandle Resize handle for dock windows.
+
+ \value PE_Frame Generic frame
+ \value PE_FrameMenu Frame for popup windows/menus; see also QMenu.
+ \value PE_PanelMenuBar Panel for menu bars.
+ \value PE_PanelScrollAreaCorner Panel at the bottom-right (or
+ bottom-left) corner of a scroll area.
+
+ \value PE_FrameDockWidget Panel frame for dock windows and toolbars.
+ \value PE_FrameTabWidget Frame for tab widgets.
+ \value PE_FrameLineEdit Panel frame for line edits.
+ \value PE_FrameGroupBox Panel frame around group boxes.
+ \value PE_FrameButtonBevel Panel frame for a button bevel.
+ \value PE_FrameButtonTool Panel frame for a tool button.
+
+ \value PE_IndicatorHeaderArrow Arrow used to indicate sorting on a list or table
+ header.
+ \value PE_FrameStatusBarItem Frame for an item of a status bar; see also QStatusBar.
+
+ \value PE_FrameWindow Frame around a MDI window or a docking window.
+
+ \value PE_Q3Separator Qt 3 compatible generic separator.
+
+ \value PE_IndicatorMenuCheckMark Check mark used in a menu.
+
+ \value PE_IndicatorProgressChunk Section of a progress bar indicator; see also QProgressBar.
+
+ \value PE_Q3CheckListController Qt 3 compatible controller part of a list view item.
+ \value PE_Q3CheckListIndicator Qt 3 compatible checkbox part of a list view item.
+ \value PE_Q3CheckListExclusiveIndicator Qt 3 compatible radio button part of a list view item.
+
+ \value PE_IndicatorBranch Lines used to represent the branch of a tree in a tree view.
+ \value PE_IndicatorToolBarHandle The handle of a toolbar.
+ \value PE_IndicatorToolBarSeparator The separator in a toolbar.
+ \value PE_PanelToolBar The panel for a toolbar.
+ \value PE_PanelTipLabel The panel for a tip label.
+ \value PE_FrameTabBarBase The frame that is drawn for a tab bar, ususally drawn for a tab bar that isn't part of a tab widget.
+ \value PE_IndicatorTabTear An indicator that a tab is partially scrolled out of the visible tab bar when there are many tabs.
+ \value PE_IndicatorColumnViewArrow An arrow in a QColumnView.
+
+ \value PE_Widget A plain QWidget.
+
+ \value PE_CustomBase Base value for custom primitive elements.
+ All values above this are reserved for custom use. Custom values
+ must be greater than this value.
+
+ \value PE_IndicatorItemViewItemDrop An indicator that is drawn to show where an item in an item view is about to be dropped
+ during a drag-and-drop operation in an item view.
+ \value PE_PanelItemViewItem The background for an item in an item view.
+ \value PE_PanelItemViewRow The background of a row in an item view.
+
+ \value PE_PanelStatusBar The panel for a status bar.
+
+ \value PE_IndicatorTabClose The close button on a tab bar.
+ \value PE_PanelMenu The panel for a menu.
+
+ \sa drawPrimitive()
+*/
+
+/*!
+ \typedef QStyle::SFlags
+ \internal
+*/
+
+/*!
+ \typedef QStyle::SCFlags
+ \internal
+*/
+
+/*!
+ \enum QStyle::StateFlag
+
+ This enum describes flags that are used when drawing primitive
+ elements.
+
+ Note that not all primitives use all of these flags, and that the
+ flags may mean different things to different items.
+
+ \value State_None Indicates that the widget does not have a state.
+ \value State_Active Indicates that the widget is active.
+ \value State_AutoRaise Used to indicate if auto-raise appearance should be usd on a tool button.
+ \value State_Children Used to indicate if an item view branch has children.
+ \value State_DownArrow Used to indicate if a down arrow should be visible on the widget.
+ \value State_Editing Used to indicate if an editor is opened on the widget.
+ \value State_Enabled Used to indicate if the widget is enabled.
+ \value State_HasEditFocus Used to indicate if the widget currently has edit focus.
+ \value State_HasFocus Used to indicate if the widget has focus.
+ \value State_Horizontal Used to indicate if the widget is laid out horizontally, for example. a tool bar.
+ \value State_KeyboardFocusChange Used to indicate if the focus was changed with the keyboard, e.g., tab, backtab or shortcut.
+ \value State_MouseOver Used to indicate if the widget is under the mouse.
+ \value State_NoChange Used to indicate a tri-state checkbox.
+ \value State_Off Used to indicate if the widget is not checked.
+ \value State_On Used to indicate if the widget is checked.
+ \value State_Raised Used to indicate if a button is raised.
+ \value State_ReadOnly Used to indicate if a widget is read-only.
+ \value State_Selected Used to indicate if a widget is selected.
+ \value State_Item Used by item views to indicate if a horizontal branch should be drawn.
+ \value State_Open Used by item views to indicate if the tree branch is open.
+ \value State_Sibling Used by item views to indicate if a vertical line needs to be drawn (for siblings).
+ \value State_Sunken Used to indicate if the widget is sunken or pressed.
+ \value State_UpArrow Used to indicate if an up arrow should be visible on the widget.
+ \value State_Mini Used to indicate a mini style Mac widget or button.
+ \value State_Small Used to indicate a small style Mac widget or button.
+ \omitvalue State_Window
+ \omitvalue State_Bottom
+ \omitvalue State_Default
+ \omitvalue State_FocusAtBorder
+ \omitvalue State_Top
+
+ \sa drawPrimitive()
+*/
+
+/*!
+ \fn void QStyle::drawPrimitive(PrimitiveElement element, const QStyleOption *option, \
+ QPainter *painter, const QWidget *widget) const
+
+ Draws the given primitive \a element with the provided \a painter using the style
+ options specified by \a option.
+
+ The \a widget argument is optional and may contain a widget that may
+ aid in drawing the primitive element.
+
+ The table below is listing the primitive elements and their
+ associated style option subclasses. The style options contain all
+ the parameters required to draw the elements, including
+ QStyleOption::state which holds the style flags that are used when
+ drawing. The table also describes which flags that are set when
+ casting the given option to the appropriate subclass.
+
+ Note that if a primitive element is not listed here, it is because
+ it uses a plain QStyleOption object.
+
+ \table
+ \header \o Primitive Element \o QStyleOption Subclass \o Style Flag \o Remark
+ \row \o \l PE_FrameFocusRect \o \l QStyleOptionFocusRect
+ \o \l State_FocusAtBorder
+ \o Whether the focus is is at the border or inside the widget.
+ \row \o{1,2} \l PE_IndicatorCheckBox \o{1,2} \l QStyleOptionButton
+ \o \l State_NoChange \o Indicates a "tri-state" checkbox.
+ \row \o \l State_On \o Indicates the indicator is checked.
+ \row \o \l PE_IndicatorRadioButton \o \l QStyleOptionButton
+ \o \l State_On \o Indicates that a radio button is selected.
+ \row \o{1,3} \l PE_Q3CheckListExclusiveIndicator, \l PE_Q3CheckListIndicator
+ \o{1,3} \l QStyleOptionQ3ListView \o \l State_On
+ \o Indicates whether or not the controller is selected.
+ \row \o \l State_NoChange \o Indicates a "tri-state" controller.
+ \row \o \l State_Enabled \o Indicates the controller is enabled.
+ \row \o{1,4} \l PE_IndicatorBranch \o{1,4} \l QStyleOption
+ \o \l State_Children \o Indicates that the control for expanding the tree to show child items, should be drawn.
+ \row \o \l State_Item \o Indicates that a horizontal branch (to show a child item), should be drawn.
+ \row \o \l State_Open \o Indicates that the tree branch is expanded.
+ \row \o \l State_Sibling \o Indicates that a vertical line (to show a sibling item), should be drawn.
+ \row \o \l PE_IndicatorHeaderArrow \o \l QStyleOptionHeader
+ \o \l State_UpArrow \o Indicates that the arrow should be drawn up;
+ otherwise it should be down.
+ \row \o \l PE_FrameGroupBox, \l PE_Frame, \l PE_FrameLineEdit,
+ \l PE_FrameMenu, \l PE_FrameDockWidget, \l PE_FrameWindow
+ \o \l QStyleOptionFrame \o \l State_Sunken
+ \o Indicates that the Frame should be sunken.
+ \row \o \l PE_IndicatorToolBarHandle \o \l QStyleOption
+ \o \l State_Horizontal \o Indicates that the window handle is horizontal
+ instead of vertical.
+ \row \o \l PE_Q3DockWindowSeparator \o \l QStyleOption
+ \o \l State_Horizontal \o Indicates that the separator is horizontal
+ instead of vertical.
+ \row \o \l PE_IndicatorSpinPlus, \l PE_IndicatorSpinMinus, \l PE_IndicatorSpinUp,
+ \l PE_IndicatorSpinDown,
+ \o \l QStyleOptionSpinBox
+ \o \l State_Sunken \o Indicates that the button is pressed.
+ \row \o{1,5} \l PE_PanelButtonCommand
+ \o{1,5} \l QStyleOptionButton
+ \o \l State_Enabled \o Set if the button is enabled.
+ \row \o \l State_HasFocus \o Set if the button has input focus.
+ \row \o \l State_Raised \o Set if the button is not down, not on and not flat.
+ \row \o \l State_On \o Set if the button is a toggle button and is toggled on.
+ \row \o \l State_Sunken
+ \o Set if the button is down (i.e., the mouse button or the
+ space bar is pressed on the button).
+ \endtable
+
+ \sa drawComplexControl(), drawControl()
+*/
+
+/*!
+ \enum QStyle::ControlElement
+
+ This enum represents a control element. A control element is a
+ part of a widget that performs some action or displays information
+ to the user.
+
+ \value CE_PushButton A QPushButton, draws CE_PushButtonBevel, CE_PushButtonLabel and PE_FrameFocusRect.
+ \value CE_PushButtonBevel The bevel and default indicator of a QPushButton.
+ \value CE_PushButtonLabel The label (an icon with text or pixmap) of a QPushButton.
+
+ \value CE_DockWidgetTitle Dock window title.
+ \value CE_Splitter Splitter handle; see also QSplitter.
+
+
+ \value CE_CheckBox A QCheckBox, draws a PE_IndicatorCheckBox, a CE_CheckBoxLabel and a PE_FrameFocusRect.
+ \value CE_CheckBoxLabel The label (text or pixmap) of a QCheckBox.
+
+ \value CE_RadioButton A QRadioButton, draws a PE_IndicatorRadioButton, a CE_RadioButtonLabel and a PE_FrameFocusRect.
+ \value CE_RadioButtonLabel The label (text or pixmap) of a QRadioButton.
+
+ \value CE_TabBarTab The tab and label within a QTabBar.
+ \value CE_TabBarTabShape The tab shape within a tab bar.
+ \value CE_TabBarTabLabel The label within a tab.
+
+ \value CE_ProgressBar A QProgressBar, draws CE_ProgressBarGroove, CE_ProgressBarContents and CE_ProgressBarLabel.
+ \value CE_ProgressBarGroove The groove where the progress
+ indicator is drawn in a QProgressBar.
+ \value CE_ProgressBarContents The progress indicator of a QProgressBar.
+ \value CE_ProgressBarLabel The text label of a QProgressBar.
+
+ \value CE_ToolButtonLabel A tool button's label.
+
+ \value CE_MenuBarItem A menu item in a QMenuBar.
+ \value CE_MenuBarEmptyArea The empty area of a QMenuBar.
+
+ \value CE_MenuItem A menu item in a QMenu.
+ \value CE_MenuScroller Scrolling areas in a QMenu when the
+ style supports scrolling.
+ \value CE_MenuTearoff A menu item representing the tear off section of
+ a QMenu.
+ \value CE_MenuEmptyArea The area in a menu without menu items.
+ \value CE_MenuHMargin The horizontal extra space on the left/right of a menu.
+ \value CE_MenuVMargin The vertical extra space on the top/bottom of a menu.
+
+ \value CE_Q3DockWindowEmptyArea The empty area of a QDockWidget.
+
+ \value CE_ToolBoxTab The toolbox's tab and label within a QToolBox.
+ \value CE_SizeGrip Window resize handle; see also QSizeGrip.
+
+ \value CE_Header A header.
+ \value CE_HeaderSection A header section.
+ \value CE_HeaderLabel The header's label.
+
+ \value CE_ScrollBarAddLine Scroll bar line increase indicator.
+ (i.e., scroll down); see also QScrollBar.
+ \value CE_ScrollBarSubLine Scroll bar line decrease indicator (i.e., scroll up).
+ \value CE_ScrollBarAddPage Scolllbar page increase indicator (i.e., page down).
+ \value CE_ScrollBarSubPage Scroll bar page decrease indicator (i.e., page up).
+ \value CE_ScrollBarSlider Scroll bar slider.
+ \value CE_ScrollBarFirst Scroll bar first line indicator (i.e., home).
+ \value CE_ScrollBarLast Scroll bar last line indicator (i.e., end).
+
+ \value CE_RubberBand Rubber band used in for example an icon view.
+
+ \value CE_FocusFrame Focus frame that is style controlled.
+
+ \value CE_ItemViewItem An item inside an item view.
+
+ \value CE_CustomBase Base value for custom control elements;
+ custom values must be greater than this value.
+ \value CE_ComboBoxLabel The label of a non-editable QComboBox.
+ \value CE_ToolBar A toolbar like QToolBar.
+ \value CE_ToolBoxTabShape The toolbox's tab shape.
+ \value CE_ToolBoxTabLabel The toolbox's tab label.
+ \value CE_HeaderEmptyArea The area of a header view where there are no header sections.
+
+ \value CE_ShapedFrame The frame with the shape specified in the QStyleOptionFrameV3; see QFrame.
+
+ \omitvalue CE_ColumnViewGrip
+
+ \sa drawControl()
+*/
+
+/*!
+ \fn void QStyle::drawControl(ControlElement element, const QStyleOption *option, QPainter *painter, const QWidget *widget) const
+
+ Draws the given \a element with the provided \a painter with the
+ style options specified by \a option.
+
+ The \a widget argument is optional and can be used as aid in
+ drawing the control. The \a option parameter is a pointer to a
+ QStyleOption object that can be cast to the correct subclass
+ using the qstyleoption_cast() function.
+
+ The table below is listing the control elements and their
+ associated style option subclass. The style options contain all
+ the parameters required to draw the controls, including
+ QStyleOption::state which holds the style flags that are used when
+ drawing. The table also describes which flags that are set when
+ casting the given option to the appropriate subclass.
+
+ Note that if a control element is not listed here, it is because
+ it uses a plain QStyleOption object.
+
+ \table
+ \header \o Control Element \o QStyleOption Subclass \o Style Flag \o Remark
+ \row \o{1,5} \l CE_MenuItem, \l CE_MenuBarItem
+ \o{1,5} \l QStyleOptionMenuItem
+ \o \l State_Selected \o The menu item is currently selected item.
+ \row \o \l State_Enabled \o The item is enabled.
+ \row \o \l State_DownArrow \o Indicates that a scroll down arrow should be drawn.
+ \row \o \l State_UpArrow \o Indicates that a scroll up arrow should be drawn
+ \row \o \l State_HasFocus \o Set if the menu bar has input focus.
+
+ \row \o{1,5} \l CE_PushButton, \l CE_PushButtonBevel, \l CE_PushButtonLabel
+ \o{1,5} \l QStyleOptionButton
+ \o \l State_Enabled \o Set if the button is enabled.
+ \row \o \l State_HasFocus \o Set if the button has input focus.
+ \row \o \l State_Raised \o Set if the button is not down, not on and not flat.
+ \row \o \l State_On \o Set if the button is a toggle button and is toggled on.
+ \row \o \l State_Sunken
+ \o Set if the button is down (i.e., the mouse button or the
+ space bar is pressed on the button).
+
+ \row \o{1,6} \l CE_RadioButton, \l CE_RadioButtonLabel,
+ \l CE_CheckBox, \l CE_CheckBoxLabel
+ \o{1,6} \l QStyleOptionButton
+ \o \l State_Enabled \o Set if the button is enabled.
+ \row \o \l State_HasFocus \o Set if the button has input focus.
+ \row \o \l State_On \o Set if the button is checked.
+ \row \o \l State_Off \o Set if the button is not checked.
+ \row \o \l State_NoChange \o Set if the button is in the NoChange state.
+ \row \o \l State_Sunken
+ \o Set if the button is down (i.e., the mouse button or
+ the space bar is pressed on the button).
+
+ \row \o{1,2} \l CE_ProgressBarContents, \l CE_ProgressBarLabel,
+ \l CE_ProgressBarGroove
+ \o{1,2} \l QStyleOptionProgressBar
+ \o \l State_Enabled \o Set if the progress bar is enabled.
+ \row \o \l State_HasFocus \o Set if the progress bar has input focus.
+
+ \row \o \l CE_Header, \l CE_HeaderSection, \l CE_HeaderLabel \o \l QStyleOptionHeader \o \o
+
+ \row \o{1,3} \l CE_TabBarTab, CE_TabBarTabShape, CE_TabBarTabLabel
+ \o{1,3} \l QStyleOptionTab
+ \o \l State_Enabled \o Set if the tab bar is enabled.
+ \row \o \l State_Selected \o The tab bar is the currently selected tab bar.
+ \row \o \l State_HasFocus \o Set if the tab bar tab has input focus.
+
+ \row \o{1,7} \l CE_ToolButtonLabel
+ \o{1,7} \l QStyleOptionToolButton
+ \o \l State_Enabled \o Set if the tool button is enabled.
+ \row \o \l State_HasFocus \o Set if the tool button has input focus.
+ \row \o \l State_Sunken
+ \o Set if the tool button is down (i.e., a mouse button or
+ the space bar is pressed).
+ \row \o \l State_On \o Set if the tool button is a toggle button and is toggled on.
+ \row \o \l State_AutoRaise \o Set if the tool button has auto-raise enabled.
+ \row \o \l State_MouseOver \o Set if the mouse pointer is over the tool button.
+ \row \o \l State_Raised \o Set if the button is not down and is not on.
+
+ \row \o \l CE_ToolBoxTab \o \l QStyleOptionToolBox
+ \o \l State_Selected \o The tab is the currently selected tab.
+ \row \o{1,3} \l CE_HeaderSection \o{1,3} \l QStyleOptionHeader
+ \o \l State_Sunken \o Indicates that the section is pressed.
+ \row \o \l State_UpArrow \o Indicates that the sort indicator should be pointing up.
+ \row \o \l State_DownArrow \o Indicates that the sort indicator should be pointing down.
+ \endtable
+
+ \sa drawPrimitive(), drawComplexControl()
+*/
+
+/*!
+ \enum QStyle::SubElement
+
+ This enum represents a sub-area of a widget. Style implementations
+ use these areas to draw the different parts of a widget.
+
+ \value SE_PushButtonContents Area containing the label (icon
+ with text or pixmap).
+ \value SE_PushButtonFocusRect Area for the focus rect (usually
+ larger than the contents rect).
+ \value SE_PushButtonLayoutItem Area that counts for the parent layout.
+
+ \value SE_CheckBoxIndicator Area for the state indicator (e.g., check mark).
+ \value SE_CheckBoxContents Area for the label (text or pixmap).
+ \value SE_CheckBoxFocusRect Area for the focus indicator.
+ \value SE_CheckBoxClickRect Clickable area, defaults to SE_CheckBoxFocusRect.
+ \value SE_CheckBoxLayoutItem Area that counts for the parent layout.
+
+ \value SE_DateTimeEditLayoutItem Area that counts for the parent layout.
+
+ \value SE_RadioButtonIndicator Area for the state indicator.
+ \value SE_RadioButtonContents Area for the label.
+ \value SE_RadioButtonFocusRect Area for the focus indicator.
+ \value SE_RadioButtonClickRect Clickable area, defaults to SE_RadioButtonFocusRect.
+ \value SE_RadioButtonLayoutItem Area that counts for the parent layout.
+
+ \value SE_ComboBoxFocusRect Area for the focus indicator.
+
+ \value SE_SliderFocusRect Area for the focus indicator.
+ \value SE_SliderLayoutItem Area that counts for the parent layout.
+
+ \value SE_SpinBoxLayoutItem Area that counts for the parent layout.
+
+ \value SE_Q3DockWindowHandleRect Area for the tear-off handle.
+
+ \value SE_ProgressBarGroove Area for the groove.
+ \value SE_ProgressBarContents Area for the progress indicator.
+ \value SE_ProgressBarLabel Area for the text label.
+ \value SE_ProgressBarLayoutItem Area that counts for the parent layout.
+
+ \omitvalue SE_DialogButtonAccept
+ \omitvalue SE_DialogButtonReject
+ \omitvalue SE_DialogButtonApply
+ \omitvalue SE_DialogButtonHelp
+ \omitvalue SE_DialogButtonAll
+ \omitvalue SE_DialogButtonRetry
+ \omitvalue SE_DialogButtonAbort
+ \omitvalue SE_DialogButtonIgnore
+ \omitvalue SE_DialogButtonCustom
+ \omitvalue SE_ViewItemCheckIndicator
+
+ \value SE_FrameContents Area for a frame's contents.
+ \value SE_ShapedFrameContents Area for a frame's contents using the shape in QStyleOptionFrameV3; see QFrame
+ \value SE_FrameLayoutItem Area that counts for the parent layout.
+
+ \value SE_HeaderArrow Area for the sort indicator for a header.
+ \value SE_HeaderLabel Area for the label in a header.
+
+ \value SE_LabelLayoutItem Area that counts for the parent layout.
+
+ \value SE_LineEditContents Area for a line edit's contents.
+
+ \value SE_TabWidgetLeftCorner Area for the left corner widget in a tab widget.
+ \value SE_TabWidgetRightCorner Area for the right corner widget in a tab widget.
+ \value SE_TabWidgetTabBar Area for the tab bar widget in a tab widget.
+ \value SE_TabWidgetTabContents Area for the contents of the tab widget.
+ \value SE_TabWidgetTabPane Area for the pane of a tab widget.
+ \value SE_TabWidgetLayoutItem Area that counts for the parent layout.
+
+ \value SE_ToolBoxTabContents Area for a toolbox tab's icon and label.
+
+ \value SE_ToolButtonLayoutItem Area that counts for the parent layout.
+
+ \value SE_ItemViewItemCheckIndicator Area for a view item's check mark.
+
+ \value SE_TabBarTearIndicator Area for the tear indicator on a tab bar with scroll arrows.
+
+ \value SE_TreeViewDisclosureItem Area for the actual disclosure item in a tree branch.
+
+ \value SE_DialogButtonBoxLayoutItem Area that counts for the parent layout.
+
+ \value SE_GroupBoxLayoutItem Area that counts for the parent layout.
+
+ \value SE_CustomBase Base value for custom sub-elements.
+ Custom values must be greater than this value.
+
+ \value SE_DockWidgetFloatButton The float button of a dock
+ widget.
+ \value SE_DockWidgetTitleBarText The text bounds of the dock
+ widgets title.
+ \value SE_DockWidgetCloseButton The close button of a dock
+ widget.
+ \value SE_DockWidgetIcon The icon of a dock widget.
+ \value SE_ComboBoxLayoutItem Area that counts for the parent layout.
+
+
+ \value SE_ItemViewItemDecoration Area for a view item's decoration (icon).
+ \value SE_ItemViewItemText Area for a view item's text.
+ \value SE_ItemViewItemFocusRect Area for a view item's focus rect.
+
+ \value SE_TabBarTabLeftButton Area for a widget on the left side of a tab in a tab bar.
+ \value SE_TabBarTabRightButton Area for a widget on the right side of a tab in a tab bar.
+ \value SE_TabBarTabText Area for the text on a tab in a tab bar.
+
+ \sa subElementRect()
+*/
+
+/*!
+ \fn QRect QStyle::subElementRect(SubElement element, const QStyleOption *option, const QWidget *widget) const
+
+ Returns the sub-area for the given \a element as described in the
+ provided style \a option. The returned rectangle is defined in
+ screen coordinates.
+
+ The \a widget argument is optional and can be used to aid
+ determining the area. The QStyleOption object can be cast to the
+ appropriate type using the qstyleoption_cast() function. See the
+ table below for the appropriate \a option casts:
+
+ \table
+ \header \o Sub Element \o QStyleOption Subclass
+ \row \o \l SE_PushButtonContents \o \l QStyleOptionButton
+ \row \o \l SE_PushButtonFocusRect \o \l QStyleOptionButton
+ \row \o \l SE_CheckBoxIndicator \o \l QStyleOptionButton
+ \row \o \l SE_CheckBoxContents \o \l QStyleOptionButton
+ \row \o \l SE_CheckBoxFocusRect \o \l QStyleOptionButton
+ \row \o \l SE_RadioButtonIndicator \o \l QStyleOptionButton
+ \row \o \l SE_RadioButtonContents \o \l QStyleOptionButton
+ \row \o \l SE_RadioButtonFocusRect \o \l QStyleOptionButton
+ \row \o \l SE_ComboBoxFocusRect \o \l QStyleOptionComboBox
+ \row \o \l SE_Q3DockWindowHandleRect \o \l QStyleOptionQ3DockWindow
+ \row \o \l SE_ProgressBarGroove \o \l QStyleOptionProgressBar
+ \row \o \l SE_ProgressBarContents \o \l QStyleOptionProgressBar
+ \row \o \l SE_ProgressBarLabel \o \l QStyleOptionProgressBar
+ \endtable
+*/
+
+/*!
+ \enum QStyle::ComplexControl
+
+ This enum describes the available complex controls. Complex
+ controls have different behavior depending upon where the user
+ clicks on them or which keys are pressed.
+
+ \value CC_SpinBox A spinbox, like QSpinBox.
+ \value CC_ComboBox A combobox, like QComboBox.
+ \value CC_ScrollBar A scroll bar, like QScrollBar.
+ \value CC_Slider A slider, like QSlider.
+ \value CC_ToolButton A tool button, like QToolButton.
+ \value CC_TitleBar A Title bar, like those used in QWorkspace.
+ \value CC_Q3ListView Used for drawing the Q3ListView class.
+ \value CC_GroupBox A group box, like QGroupBox.
+ \value CC_Dial A dial, like QDial.
+ \value CC_MdiControls The minimize, close, and normal
+ button in the menu bar for a
+ maximized MDI subwindow.
+
+ \value CC_CustomBase Base value for custom complex controls. Custom
+ values must be greater than this value.
+
+ \sa SubControl drawComplexControl()
+*/
+
+/*!
+ \enum QStyle::SubControl
+
+ This enum describes the available sub controls. A subcontrol is a
+ control element within a complex control (ComplexControl).
+
+ \value SC_None Special value that matches no other sub control.
+
+ \value SC_ScrollBarAddLine Scroll bar add line (i.e., down/right
+ arrow); see also QScrollBar.
+ \value SC_ScrollBarSubLine Scroll bar sub line (i.e., up/left arrow).
+ \value SC_ScrollBarAddPage Scroll bar add page (i.e., page down).
+ \value SC_ScrollBarSubPage Scroll bar sub page (i.e., page up).
+ \value SC_ScrollBarFirst Scroll bar first line (i.e., home).
+ \value SC_ScrollBarLast Scroll bar last line (i.e., end).
+ \value SC_ScrollBarSlider Scroll bar slider handle.
+ \value SC_ScrollBarGroove Special sub-control which contains the
+ area in which the slider handle may move.
+
+ \value SC_SpinBoxUp Spin widget up/increase; see also QSpinBox.
+ \value SC_SpinBoxDown Spin widget down/decrease.
+ \value SC_SpinBoxFrame Spin widget frame.
+ \value SC_SpinBoxEditField Spin widget edit field.
+
+ \value SC_ComboBoxEditField Combobox edit field; see also QComboBox.
+ \value SC_ComboBoxArrow Combobox arrow button.
+ \value SC_ComboBoxFrame Combobox frame.
+ \value SC_ComboBoxListBoxPopup The reference rectangle for the combobox popup.
+ Used to calculate the position of the popup.
+
+ \value SC_SliderGroove Special sub-control which contains the area
+ in which the slider handle may move.
+ \value SC_SliderHandle Slider handle.
+ \value SC_SliderTickmarks Slider tickmarks.
+
+ \value SC_ToolButton Tool button (see also QToolButton).
+ \value SC_ToolButtonMenu Sub-control for opening a popup menu in a
+ tool button; see also Q3PopupMenu.
+
+ \value SC_TitleBarSysMenu System menu button (i.e., restore, close, etc.).
+ \value SC_TitleBarMinButton Minimize button.
+ \value SC_TitleBarMaxButton Maximize button.
+ \value SC_TitleBarCloseButton Close button.
+ \value SC_TitleBarLabel Window title label.
+ \value SC_TitleBarNormalButton Normal (restore) button.
+ \value SC_TitleBarShadeButton Shade button.
+ \value SC_TitleBarUnshadeButton Unshade button.
+ \value SC_TitleBarContextHelpButton Context Help button.
+
+ \value SC_Q3ListView The list view area.
+ \value SC_Q3ListViewExpand Expand item (i.e., show/hide child items).
+
+ \value SC_DialHandle The handle of the dial (i.e. what you use to control the dial).
+ \value SC_DialGroove The groove for the dial.
+ \value SC_DialTickmarks The tickmarks for the dial.
+
+ \value SC_GroupBoxFrame The frame of a group box.
+ \value SC_GroupBoxLabel The title of a group box.
+ \value SC_GroupBoxCheckBox The optional check box of a group box.
+ \value SC_GroupBoxContents The group box contents.
+
+ \value SC_MdiNormalButton The normal button for a MDI
+ subwindow in the menu bar.
+ \value SC_MdiMinButton The minimize button for a MDI
+ subwindow in the menu bar.
+ \value SC_MdiCloseButton The close button for a MDI subwindow
+ in the menu bar.
+
+ \value SC_All Special value that matches all sub-controls.
+ \omitvalue SC_Q3ListViewBranch
+
+ \sa ComplexControl
+*/
+
+/*!
+ \fn void QStyle::drawComplexControl(ComplexControl control, const QStyleOptionComplex *option, QPainter *painter, const QWidget *widget) const
+
+ Draws the given \a control using the provided \a painter with the
+ style options specified by \a option.
+
+ The \a widget argument is optional and can be used as aid in
+ drawing the control.
+
+ The \a option parameter is a pointer to a QStyleOptionComplex
+ object that can be cast to the correct subclass using the
+ qstyleoption_cast() function. Note that the \c rect member of the
+ specified \a option must be in logical
+ coordinates. Reimplementations of this function should use
+ visualRect() to change the logical coordinates into screen
+ coordinates before calling the drawPrimitive() or drawControl()
+ function.
+
+ The table below is listing the complex control elements and their
+ associated style option subclass. The style options contain all
+ the parameters required to draw the controls, including
+ QStyleOption::state which holds the \l {QStyle::StateFlag}{style
+ flags} that are used when drawing. The table also describes which
+ flags that are set when casting the given \a option to the
+ appropriate subclass.
+
+ \table
+ \header \o Complex Control \o QStyleOptionComplex Subclass \o Style Flag \o Remark
+ \row \o{1,2} \l{CC_SpinBox} \o{1,2} \l QStyleOptionSpinBox
+ \o \l State_Enabled \o Set if the spin box is enabled.
+ \row \o \l State_HasFocus \o Set if the spin box has input focus.
+
+ \row \o{1,2} \l {CC_ComboBox} \o{1,2} \l QStyleOptionComboBox
+ \o \l State_Enabled \o Set if the combobox is enabled.
+ \row \o \l State_HasFocus \o Set if the combobox has input focus.
+
+ \row \o{1,2} \l {CC_ScrollBar} \o{1,2} \l QStyleOptionSlider
+ \o \l State_Enabled \o Set if the scroll bar is enabled.
+ \row \o \l State_HasFocus \o Set if the scroll bar has input focus.
+
+ \row \o{1,2} \l {CC_Slider} \o{1,2} \l QStyleOptionSlider
+ \o \l State_Enabled \o Set if the slider is enabled.
+ \row \o \l State_HasFocus \o Set if the slider has input focus.
+
+ \row \o{1,2} \l {CC_Dial} \o{1,2} \l QStyleOptionSlider
+ \o \l State_Enabled \o Set if the dial is enabled.
+ \row \o \l State_HasFocus \o Set if the dial has input focus.
+
+ \row \o{1,6} \l {CC_ToolButton} \o{1,6} \l QStyleOptionToolButton
+ \o \l State_Enabled \o Set if the tool button is enabled.
+ \row \o \l State_HasFocus \o Set if the tool button has input focus.
+ \row \o \l State_DownArrow \o Set if the tool button is down (i.e., a mouse
+ button or the space bar is pressed).
+ \row \o \l State_On \o Set if the tool button is a toggle button
+ and is toggled on.
+ \row \o \l State_AutoRaise \o Set if the tool button has auto-raise enabled.
+ \row \o \l State_Raised \o Set if the button is not down, not on, and doesn't
+ contain the mouse when auto-raise is enabled.
+
+ \row \o \l{CC_TitleBar} \o \l QStyleOptionTitleBar
+ \o \l State_Enabled \o Set if the title bar is enabled.
+
+ \row \o \l{CC_Q3ListView} \o \l QStyleOptionQ3ListView
+ \o \l State_Enabled \o Set if the list view is enabled.
+
+ \endtable
+
+ \sa drawPrimitive(), drawControl()
+*/
+
+
+/*!
+ \fn QRect QStyle::subControlRect(ComplexControl control,
+ const QStyleOptionComplex *option, SubControl subControl,
+ const QWidget *widget) const = 0
+
+ Returns the rectangle containing the specified \a subControl of
+ the given complex \a control (with the style specified by \a
+ option). The rectangle is defined in screen coordinates.
+
+ The \a option argument is a pointer to QStyleOptionComplex or
+ one of its subclasses, and can be cast to the appropriate type
+ using the qstyleoption_cast() function. See drawComplexControl()
+ for details. The \a widget is optional and can contain additional
+ information for the function.
+
+ \sa drawComplexControl()
+*/
+
+/*!
+ \fn QStyle::SubControl QStyle::hitTestComplexControl(ComplexControl control,
+ const QStyleOptionComplex *option, const QPoint &position,
+ const QWidget *widget) const = 0
+
+ Returns the sub control at the given \a position in the given
+ complex \a control (with the style options specified by \a
+ option).
+
+ Note that the \a position is expressed in screen coordinates.
+
+ The \a option argument is a pointer to a QStyleOptionComplex
+ object (or one of its subclasses). The object can be cast to the
+ appropriate type using the qstyleoption_cast() function. See
+ drawComplexControl() for details. The \a widget argument is
+ optional and can contain additional information for the function.
+
+ \sa drawComplexControl(), subControlRect()
+*/
+
+/*!
+ \enum QStyle::PixelMetric
+
+ This enum describes the various available pixel metrics. A pixel
+ metric is a style dependent size represented by a single pixel
+ value.
+
+ \value PM_ButtonMargin Amount of whitespace between push button
+ labels and the frame.
+ \value PM_DockWidgetTitleBarButtonMargin Amount of whitespace between dock widget's
+ title bar button labels and the frame.
+ \value PM_ButtonDefaultIndicator Width of the default-button indicator frame.
+ \value PM_MenuButtonIndicator Width of the menu button indicator
+ proportional to the widget height.
+ \value PM_ButtonShiftHorizontal Horizontal contents shift of a
+ button when the button is down.
+ \value PM_ButtonShiftVertical Vertical contents shift of a button when the
+ button is down.
+
+ \value PM_DefaultFrameWidth Default frame width (usually 2).
+ \value PM_SpinBoxFrameWidth Frame width of a spin box, defaults to PM_DefaultFrameWidth.
+ \value PM_ComboBoxFrameWidth Frame width of a combo box, defaults to PM_DefaultFrameWidth.
+
+ \value PM_MDIFrameWidth Obsolete. Use PM_MdiSubWindowFrameWidth instead.
+ \value PM_MdiSubWindowFrameWidth Frame width of an MDI window.
+ \value PM_MDIMinimizedWidth Obsolete. Use PM_MdiSubWindowMinimizedWidth instead.
+ \value PM_MdiSubWindowMinimizedWidth Width of a minimized MDI window.
+
+ \value PM_LayoutLeftMargin Default \l{QLayout::setContentsMargins()}{left margin} for a
+ QLayout.
+ \value PM_LayoutTopMargin Default \l{QLayout::setContentsMargins()}{top margin} for a QLayout.
+ \value PM_LayoutRightMargin Default \l{QLayout::setContentsMargins()}{right margin} for a
+ QLayout.
+ \value PM_LayoutBottomMargin Default \l{QLayout::setContentsMargins()}{bottom margin} for a
+ QLayout.
+ \value PM_LayoutHorizontalSpacing Default \l{QLayout::spacing}{horizontal spacing} for a
+ QLayout.
+ \value PM_LayoutVerticalSpacing Default \l{QLayout::spacing}{vertical spacing} for a QLayout.
+
+ \value PM_MaximumDragDistance The maximum allowed distance between
+ the mouse and a slider when dragging. Exceeding the specified
+ distance will cause the slider to jump back to the original
+ position; a value of -1 disables this behavior.
+
+ \value PM_ScrollBarExtent Width of a vertical scroll bar and the
+ height of a horizontal scroll bar.
+ \value PM_ScrollBarSliderMin The minimum height of a vertical
+ scroll bar's slider and the minimum width of a horizontal
+ scroll bar's slider.
+
+ \value PM_SliderThickness Total slider thickness.
+ \value PM_SliderControlThickness Thickness of the slider handle.
+ \value PM_SliderLength Length of the slider.
+ \value PM_SliderTickmarkOffset The offset between the tickmarks
+ and the slider.
+ \value PM_SliderSpaceAvailable The available space for the slider to move.
+
+ \value PM_DockWidgetSeparatorExtent Width of a separator in a
+ horizontal dock window and the height of a separator in a
+ vertical dock window.
+ \value PM_DockWidgetHandleExtent Width of the handle in a
+ horizontal dock window and the height of the handle in a
+ vertical dock window.
+ \value PM_DockWidgetFrameWidth Frame width of a dock window.
+ \value PM_DockWidgetTitleMargin Margin of the dock window title.
+
+ \value PM_MenuBarPanelWidth Frame width of a menu bar, defaults to PM_DefaultFrameWidth.
+ \value PM_MenuBarItemSpacing Spacing between menu bar items.
+ \value PM_MenuBarHMargin Spacing between menu bar items and left/right of bar.
+ \value PM_MenuBarVMargin Spacing between menu bar items and top/bottom of bar.
+
+ \value PM_ToolBarFrameWidth Width of the frame around toolbars.
+ \value PM_ToolBarHandleExtent Width of a toolbar handle in a
+ horizontal toolbar and the height of the handle in a vertical toolbar.
+ \value PM_ToolBarItemMargin Spacing between the toolbar frame and the items.
+ \value PM_ToolBarItemSpacing Spacing between toolbar items.
+ \value PM_ToolBarSeparatorExtent Width of a toolbar separator in a
+ horizontal toolbar and the height of a separator in a vertical toolbar.
+ \value PM_ToolBarExtensionExtent Width of a toolbar extension
+ button in a horizontal toolbar and the height of the button in a
+ vertical toolbar.
+
+ \value PM_TabBarTabOverlap Number of pixels the tabs should overlap.
+ (Currently only used in styles, not inside of QTabBar)
+ \value PM_TabBarTabHSpace Extra space added to the tab width.
+ \value PM_TabBarTabVSpace Extra space added to the tab height.
+ \value PM_TabBarBaseHeight Height of the area between the tab bar
+ and the tab pages.
+ \value PM_TabBarBaseOverlap Number of pixels the tab bar overlaps
+ the tab bar base.
+ \value PM_TabBarScrollButtonWidth
+ \value PM_TabBarTabShiftHorizontal Horizontal pixel shift when a
+ tab is selected.
+ \value PM_TabBarTabShiftVertical Vertical pixel shift when a
+ tab is selected.
+
+ \value PM_ProgressBarChunkWidth Width of a chunk in a progress bar indicator.
+
+ \value PM_SplitterWidth Width of a splitter.
+
+ \value PM_TitleBarHeight Height of the title bar.
+
+ \value PM_IndicatorWidth Width of a check box indicator.
+ \value PM_IndicatorHeight Height of a checkbox indicator.
+ \value PM_ExclusiveIndicatorWidth Width of a radio button indicator.
+ \value PM_ExclusiveIndicatorHeight Height of a radio button indicator.
+
+ \value PM_MenuPanelWidth Border width (applied on all sides) for a QMenu.
+ \value PM_MenuHMargin Additional border (used on left and right) for a QMenu.
+ \value PM_MenuVMargin Additional border (used for bottom and top) for a QMenu.
+ \value PM_MenuScrollerHeight Height of the scroller area in a QMenu.
+ \value PM_MenuTearoffHeight Height of a tear off area in a QMenu.
+ \value PM_MenuDesktopFrameWidth The frame width for the menu on the desktop.
+
+ \value PM_CheckListButtonSize Area (width/height) of the
+ checkbox/radio button in a Q3CheckListItem.
+ \value PM_CheckListControllerSize Area (width/height) of the
+ controller in a Q3CheckListItem.
+
+ \omitvalue PM_DialogButtonsSeparator
+ \omitvalue PM_DialogButtonsButtonWidth
+ \omitvalue PM_DialogButtonsButtonHeight
+
+ \value PM_HeaderMarkSize The size of the sort indicator in a header.
+ \value PM_HeaderGripMargin The size of the resize grip in a header.
+ \value PM_HeaderMargin The size of the margin between the sort indicator and the text.
+ \value PM_SpinBoxSliderHeight The height of the optional spin box slider.
+
+ \value PM_ToolBarIconSize Default tool bar icon size
+ \value PM_SmallIconSize Default small icon size
+ \value PM_LargeIconSize Default large icon size
+
+ \value PM_FocusFrameHMargin Horizontal margin that the focus frame will outset the widget by.
+ \value PM_FocusFrameVMargin Vertical margin that the focus frame will outset the widget by.
+ \value PM_IconViewIconSize The default size for icons in an icon view.
+ \value PM_ListViewIconSize The default size for icons in a list view.
+
+ \value PM_ToolTipLabelFrameWidth The frame width for a tool tip label.
+ \value PM_CheckBoxLabelSpacing The spacing between a check box indicator and its label.
+ \value PM_RadioButtonLabelSpacing The spacing between a radio button indicator and its label.
+ \value PM_TabBarIconSize The default icon size for a tab bar.
+ \value PM_SizeGripSize The size of a size grip.
+ \value PM_MessageBoxIconSize The size of the standard icons in a message box
+ \value PM_ButtonIconSize The default size of button icons
+ \value PM_TextCursorWidth The width of the cursor in a line edit or text edit
+ \value PM_TabBar_ScrollButtonOverlap The distance between the left and right buttons in a tab bar.
+
+ \value PM_TabCloseIndicatorWidth The default width of a close button on a tab in a tab bar.
+ \value PM_TabCloseIndicatorHeight The default height of a close button on a tab in a tab bar.
+
+ \value PM_CustomBase Base value for custom pixel metrics. Custom
+ values must be greater than this value.
+
+ The following values are obsolete:
+
+ \value PM_DefaultTopLevelMargin Use PM_LayoutLeftMargin,
+ PM_LayoutTopMargin,
+ PM_LayoutRightMargin, and
+ PM_LayoutBottomMargin instead.
+ \value PM_DefaultChildMargin Use PM_LayoutLeftMargin,
+ PM_LayoutTopMargin,
+ PM_LayoutRightMargin, and
+ PM_LayoutBottomMargin instead.
+ \value PM_DefaultLayoutSpacing Use PM_LayoutHorizontalSpacing
+ and PM_LayoutVerticalSpacing
+ instead.
+
+ \value PM_ScrollView_ScrollBarSpacing Distance between frame and scrollbar
+ with SH_ScrollView_FrameOnlyAroundContents set.
+ \value PM_SubMenuOverlap The horizontal overlap between a submenu and its parent.
+
+
+ \sa pixelMetric()
+*/
+
+/*!
+ \fn int QStyle::pixelMetric(PixelMetric metric, const QStyleOption *option, const QWidget *widget) const;
+
+ Returns the value of the given pixel \a metric.
+
+ The specified \a option and \a widget can be used for calculating
+ the metric. In general, the \a widget argument is not used. The \a
+ option can be cast to the appropriate type using the
+ qstyleoption_cast() function. Note that the \a option may be zero
+ even for PixelMetrics that can make use of it. See the table below
+ for the appropriate \a option casts:
+
+ \table
+ \header \o Pixel Metric \o QStyleOption Subclass
+ \row \o \l PM_SliderControlThickness \o \l QStyleOptionSlider
+ \row \o \l PM_SliderLength \o \l QStyleOptionSlider
+ \row \o \l PM_SliderTickmarkOffset \o \l QStyleOptionSlider
+ \row \o \l PM_SliderSpaceAvailable \o \l QStyleOptionSlider
+ \row \o \l PM_ScrollBarExtent \o \l QStyleOptionSlider
+ \row \o \l PM_TabBarTabOverlap \o \l QStyleOptionTab
+ \row \o \l PM_TabBarTabHSpace \o \l QStyleOptionTab
+ \row \o \l PM_TabBarTabVSpace \o \l QStyleOptionTab
+ \row \o \l PM_TabBarBaseHeight \o \l QStyleOptionTab
+ \row \o \l PM_TabBarBaseOverlap \o \l QStyleOptionTab
+ \endtable
+
+ Some pixel metrics are called from widgets and some are only called
+ internally by the style. If the metric is not called by a widget, it is the
+ discretion of the style author to make use of it. For some styles, this
+ may not be appropriate.
+*/
+
+/*!
+ \enum QStyle::ContentsType
+
+ This enum describes the available contents types. These are used to
+ calculate sizes for the contents of various widgets.
+
+ \value CT_CheckBox A check box, like QCheckBox.
+ \value CT_ComboBox A combo box, like QComboBox.
+ \omitvalue CT_DialogButtons
+ \value CT_Q3DockWindow A Q3DockWindow.
+ \value CT_HeaderSection A header section, like QHeader.
+ \value CT_LineEdit A line edit, like QLineEdit.
+ \value CT_Menu A menu, like QMenu.
+ \value CT_Q3Header A Qt 3 header section, like Q3Header.
+ \value CT_MenuBar A menu bar, like QMenuBar.
+ \value CT_MenuBarItem A menu bar item, like the buttons in a QMenuBar.
+ \value CT_MenuItem A menu item, like QMenuItem.
+ \value CT_ProgressBar A progress bar, like QProgressBar.
+ \value CT_PushButton A push button, like QPushButton.
+ \value CT_RadioButton A radio button, like QRadioButton.
+ \value CT_SizeGrip A size grip, like QSizeGrip.
+ \value CT_Slider A slider, like QSlider.
+ \value CT_ScrollBar A scroll bar, like QScrollBar.
+ \value CT_SpinBox A spin box, like QSpinBox.
+ \value CT_Splitter A splitter, like QSplitter.
+ \value CT_TabBarTab A tab on a tab bar, like QTabBar.
+ \value CT_TabWidget A tab widget, like QTabWidget.
+ \value CT_ToolButton A tool button, like QToolButton.
+ \value CT_GroupBox A group box, like QGroupBox.
+ \value CT_ItemViewItem An item inside an item view.
+
+ \value CT_CustomBase Base value for custom contents types.
+ Custom values must be greater than this value.
+
+ \value CT_MdiControls The minimize, normal, and close button
+ in the menu bar for a maximized MDI
+ subwindow.
+
+ \sa sizeFromContents()
+*/
+
+/*!
+ \fn QSize QStyle::sizeFromContents(ContentsType type, const QStyleOption *option, \
+ const QSize &contentsSize, const QWidget *widget) const
+
+ Returns the size of the element described by the specified
+ \a option and \a type, based on the provided \a contentsSize.
+
+ The \a option argument is a pointer to a QStyleOption or one of
+ its subclasses. The \a option can be cast to the appropriate type
+ using the qstyleoption_cast() function. The \a widget is an
+ optional argument and can contain extra information used for
+ calculating the size.
+
+ See the table below for the appropriate \a option casts:
+
+ \table
+ \header \o Contents Type \o QStyleOption Subclass
+ \row \o \l CT_PushButton \o \l QStyleOptionButton
+ \row \o \l CT_CheckBox \o \l QStyleOptionButton
+ \row \o \l CT_RadioButton \o \l QStyleOptionButton
+ \row \o \l CT_ToolButton \o \l QStyleOptionToolButton
+ \row \o \l CT_ComboBox \o \l QStyleOptionComboBox
+ \row \o \l CT_Splitter \o \l QStyleOption
+ \row \o \l CT_Q3DockWindow \o \l QStyleOptionQ3DockWindow
+ \row \o \l CT_ProgressBar \o \l QStyleOptionProgressBar
+ \row \o \l CT_MenuItem \o \l QStyleOptionMenuItem
+ \endtable
+
+ \sa ContentsType QStyleOption
+*/
+
+/*!
+ \enum QStyle::StyleHint
+
+ This enum describes the available style hints. A style hint is a general look
+ and/or feel hint.
+
+ \value SH_EtchDisabledText Disabled text is "etched" as it is on Windows.
+
+ \value SH_DitherDisabledText Disabled text is dithered as it is on Motif.
+
+ \value SH_GUIStyle The GUI style to use.
+
+ \value SH_ScrollBar_ContextMenu Whether or not a scroll bar has a context menu.
+
+ \value SH_ScrollBar_MiddleClickAbsolutePosition A boolean value.
+ If true, middle clicking on a scroll bar causes the slider to
+ jump to that position. If false, middle clicking is
+ ignored.
+
+ \value SH_ScrollBar_LeftClickAbsolutePosition A boolean value.
+ If true, left clicking on a scroll bar causes the slider to
+ jump to that position. If false, left clicking will
+ behave as appropriate for each control.
+
+ \value SH_ScrollBar_ScrollWhenPointerLeavesControl A boolean
+ value. If true, when clicking a scroll bar SubControl, holding
+ the mouse button down and moving the pointer outside the
+ SubControl, the scroll bar continues to scroll. If false, the
+ scollbar stops scrolling when the pointer leaves the
+ SubControl.
+
+ \value SH_ScrollBar_RollBetweenButtons A boolean value.
+ If true, when clicking a scroll bar button (SC_ScrollBarAddLine or
+ SC_ScrollBarSubLine) and dragging over to the opposite button (rolling)
+ will press the new button and release the old one. When it is false, the
+ original button is released and nothing happens (like a push button).
+
+ \value SH_TabBar_Alignment The alignment for tabs in a
+ QTabWidget. Possible values are Qt::AlignLeft,
+ Qt::AlignCenter and Qt::AlignRight.
+
+ \value SH_Header_ArrowAlignment The placement of the sorting
+ indicator may appear in list or table headers. Possible values
+ are Qt::Left or Qt::Right.
+
+ \value SH_Slider_SnapToValue Sliders snap to values while moving,
+ as they do on Windows.
+
+ \value SH_Slider_SloppyKeyEvents Key presses handled in a sloppy
+ manner, i.e., left on a vertical slider subtracts a line.
+
+ \value SH_ProgressDialog_CenterCancelButton Center button on
+ progress dialogs, like Motif, otherwise right aligned.
+
+ \value SH_ProgressDialog_TextLabelAlignment The alignment for text
+ labels in progress dialogs; Qt::AlignCenter on Windows,
+ Qt::AlignVCenter otherwise.
+
+ \value SH_PrintDialog_RightAlignButtons Right align buttons in
+ the print dialog, as done on Windows.
+
+ \value SH_MainWindow_SpaceBelowMenuBar One or two pixel space between
+ the menu bar and the dockarea, as done on Windows.
+
+ \value SH_FontDialog_SelectAssociatedText Select the text in the
+ line edit, or when selecting an item from the listbox, or when
+ the line edit receives focus, as done on Windows.
+
+ \value SH_Menu_KeyboardSearch Typing causes a menu to be search
+ for relevant items, otherwise only mnemnonic is considered.
+
+ \value SH_Menu_AllowActiveAndDisabled Allows disabled menu
+ items to be active.
+
+ \value SH_Menu_SpaceActivatesItem Pressing the space bar activates
+ the item, as done on Motif.
+
+ \value SH_Menu_SubMenuPopupDelay The number of milliseconds
+ to wait before opening a submenu (256 on Windows, 96 on Motif).
+
+ \value SH_Menu_Scrollable Whether popup menus must support scrolling.
+
+ \value SH_Menu_SloppySubMenus Whether popupmenu's must support
+ sloppy submenu; as implemented on Mac OS.
+
+ \value SH_ScrollView_FrameOnlyAroundContents Whether scrollviews
+ draw their frame only around contents (like Motif), or around
+ contents, scroll bars and corner widgets (like Windows).
+
+ \value SH_MenuBar_AltKeyNavigation Menu bars items are navigable
+ by pressing Alt, followed by using the arrow keys to select
+ the desired item.
+
+ \value SH_ComboBox_ListMouseTracking Mouse tracking in combobox
+ drop-down lists.
+
+ \value SH_Menu_MouseTracking Mouse tracking in popup menus.
+
+ \value SH_MenuBar_MouseTracking Mouse tracking in menu bars.
+
+ \value SH_Menu_FillScreenWithScroll Whether scrolling popups
+ should fill the screen as they are scrolled.
+
+ \value SH_Menu_SelectionWrap Whether popups should allow the selections
+ to wrap, that is when selection should the next item be the first item.
+
+ \value SH_ItemView_ChangeHighlightOnFocus Gray out selected items
+ when losing focus.
+
+ \value SH_Widget_ShareActivation Turn on sharing activation with
+ floating modeless dialogs.
+
+ \value SH_TabBar_SelectMouseType Which type of mouse event should
+ cause a tab to be selected.
+
+ \value SH_Q3ListViewExpand_SelectMouseType Which type of mouse event should
+ cause a list view expansion to be selected.
+
+ \value SH_TabBar_PreferNoArrows Whether a tab bar should suggest a size
+ to prevent scoll arrows.
+
+ \value SH_ComboBox_Popup Allows popups as a combobox drop-down
+ menu.
+
+ \value SH_Workspace_FillSpaceOnMaximize The workspace should
+ maximize the client area.
+
+ \value SH_TitleBar_NoBorder The title bar has no border.
+
+ \value SH_ScrollBar_StopMouseOverSlider Obsolete. Use
+ SH_Slider_StopMouseOverSlider instead.
+
+ \value SH_Slider_StopMouseOverSlider Stops auto-repeat when
+ the slider reaches the mouse position.
+
+ \value SH_BlinkCursorWhenTextSelected Whether cursor should blink
+ when text is selected.
+
+ \value SH_RichText_FullWidthSelection Whether richtext selections
+ should extend to the full width of the document.
+
+ \value SH_GroupBox_TextLabelVerticalAlignment How to vertically align a
+ group box's text label.
+
+ \value SH_GroupBox_TextLabelColor How to paint a group box's text label.
+
+ \value SH_DialogButtons_DefaultButton Which button gets the
+ default status in a dialog's button widget.
+
+ \value SH_ToolBox_SelectedPageTitleBold Boldness of the selected
+ page title in a QToolBox.
+
+ \value SH_LineEdit_PasswordCharacter The Unicode character to be
+ used for passwords.
+
+ \value SH_Table_GridLineColor The RGB value of the grid for a table.
+
+ \value SH_UnderlineShortcut Whether shortcuts are underlined.
+
+ \value SH_SpellCheckUnderlineStyle A
+ QTextCharFormat::UnderlineStyle value that specifies the way
+ misspelled words should be underlined.
+
+ \value SH_SpinBox_AnimateButton Animate a click when up or down is
+ pressed in a spin box.
+ \value SH_SpinBox_KeyPressAutoRepeatRate Auto-repeat interval for
+ spinbox key presses.
+ \value SH_SpinBox_ClickAutoRepeatRate Auto-repeat interval for
+ spinbox mouse clicks.
+ \value SH_SpinBox_ClickAutoRepeatThreshold Auto-repeat threshold for
+ spinbox mouse clicks.
+ \value SH_ToolTipLabel_Opacity An integer indicating the opacity for
+ the tip label, 0 is completely transparent, 255 is completely
+ opaque.
+ \value SH_DrawMenuBarSeparator Indicates whether or not the menu bar draws separators.
+ \value SH_TitleBar_ModifyNotification Indicates if the title bar should show
+ a '*' for windows that are modified.
+
+ \value SH_Button_FocusPolicy The default focus policy for buttons.
+
+ \value SH_CustomBase Base value for custom style hints.
+ Custom values must be greater than this value.
+
+ \value SH_MenuBar_DismissOnSecondClick A boolean indicating if a menu in
+ the menu bar should be dismissed when it is clicked on a second time. (Example:
+ Clicking and releasing on the File Menu in a menu bar and then
+ immediately clicking on the File Menu again.)
+
+ \value SH_MessageBox_UseBorderForButtonSpacing A boolean indicating what the to
+ use the border of the buttons (computed as half the button height) for the spacing
+ of the button in a message box.
+
+ \value SH_MessageBox_CenterButtons A boolean indicating whether the buttons in the
+ message box should be centered or not (see QDialogButtonBox::setCentered()).
+
+ \value SH_MessageBox_TextInteractionFlags A boolean indicating if
+ the text in a message box should allow user interfactions (e.g.
+ selection) or not.
+
+ \value SH_TitleBar_AutoRaise A boolean indicating whether
+ controls on a title bar ought to update when the mouse is over them.
+
+ \value SH_ToolButton_PopupDelay An int indicating the popup delay in milliseconds
+ for menus attached to tool buttons.
+
+ \value SH_FocusFrame_Mask The mask of the focus frame.
+
+ \value SH_RubberBand_Mask The mask of the rubber band.
+
+ \value SH_WindowFrame_Mask The mask of the window frame.
+
+ \value SH_SpinControls_DisableOnBounds Determines if the spin controls will shown
+ as disabled when reaching the spin range boundary.
+
+ \value SH_Dial_BackgroundRole Defines the style's preferred
+ background role (as QPalette::ColorRole) for a dial widget.
+
+ \value SH_ScrollBar_BackgroundMode The background mode for a scroll bar.
+
+ \value SH_ComboBox_LayoutDirection The layout direction for the
+ combo box. By default it should be the same as indicated by the
+ QStyleOption::direction variable.
+
+ \value SH_ItemView_EllipsisLocation The location where ellipses should be
+ added for item text that is too long to fit in an view item.
+
+ \value SH_ItemView_ShowDecorationSelected When an item in an item
+ view is selected, also highlight the branch or other decoration.
+
+ \value SH_ItemView_ActivateItemOnSingleClick Emit the activated signal
+ when the user single clicks on an item in an item in an item view.
+ Otherwise the signal is emitted when the user double clicks on an item.
+
+ \value SH_Slider_AbsoluteSetButtons Which mouse buttons cause a slider
+ to set the value to the position clicked on.
+
+ \value SH_Slider_PageSetButtons Which mouse buttons cause a slider
+ to page step the value.
+
+ \value SH_TabBar_ElideMode The default eliding style for a tab bar.
+
+ \value SH_DialogButtonLayout Controls how buttons are laid out in a QDialogButtonBox, returns a QDialogButtonBox::ButtonLayout enum.
+
+ \value SH_WizardStyle Controls the look and feel of a QWizard. Returns a QWizard::WizardStyle enum.
+
+ \value SH_FormLayoutWrapPolicy Provides a default for how rows are wrapped in a QFormLayout. Returns a QFormLayout::RowWrapPolicy enum.
+ \value SH_FormLayoutFieldGrowthPolicy Provides a default for how fields can grow in a QFormLayout. Returns a QFormLayout::FieldGrowthPolicy enum.
+ \value SH_FormLayoutFormAlignment Provides a default for how a QFormLayout aligns its contents within the available space. Returns a Qt::Alignment enum.
+ \value SH_FormLayoutLabelAlignment Provides a default for how a QFormLayout aligns labels within the available space. Returns a Qt::Alignment enum.
+
+ \value SH_ItemView_ArrowKeysNavigateIntoChildren Controls whether the tree view will select the first child when it is exapanded and the right arrow key is pressed.
+ \value SH_ComboBox_PopupFrameStyle The frame style used when drawing a combobox popup menu.
+
+ \value SH_DialogButtonBox_ButtonsHaveIcons Indicates whether or not StandardButtons in QDialogButtonBox should have icons or not.
+ \value SH_ItemView_MovementWithoutUpdatingSelection The item view is able to indicate a current item without changing the selection.
+ \value SH_ToolTip_Mask The mask of a tool tip.
+
+ \value SH_FocusFrame_AboveWidget The FocusFrame is stacked above the widget that it is "focusing on".
+
+ \value SH_TextControl_FocusIndicatorTextCharFormat Specifies the text format used to highlight focused anchors in rich text
+ documents displayed for example in QTextBrowser. The format has to be a QTextCharFormat returned in the variant of the
+ QStyleHintReturnVariant return value. The QTextFormat::OutlinePen property is used for the outline and QTextFormat::BackgroundBrush
+ for the background of the highlighted area.
+
+ \value SH_Menu_FlashTriggeredItem Flash triggered item.
+ \value SH_Menu_FadeOutOnHide Fade out the menu instead of hiding it immediately.
+
+ \value SH_TabWidget_DefaultTabPosition Default position of the tab bar in a tab widget.
+
+ \value SH_ToolBar_Movable Determines if the tool bar is movable by default.
+
+ \value SH_ItemView_PaintAlternatingRowColorsForEmptyArea Whether QTreeView paints alternating row colors for the area that does not have any items.
+
+ \value SH_Menu_Mask The mask for a popup menu.
+
+ \value SH_ItemView_DrawDelegateFrame Determines if there should be a frame for a delegate widget.
+
+ \value SH_TabBar_CloseButtonPosition Determines the position of the close button on a tab in a tab bar.
+
+ \value SH_DockWidget_ButtonsHaveFrame Determines if dockwidget buttons should have frames. Default is true.
+
+ \omitvalue SH_UnderlineAccelerator
+
+ \sa styleHint()
+*/
+
+/*!
+ \fn int QStyle::styleHint(StyleHint hint, const QStyleOption *option, \
+ const QWidget *widget, QStyleHintReturn *returnData) const
+
+ Returns an integer representing the specified style \a hint for
+ the given \a widget described by the provided style \a option.
+
+ Note that currently, the \a returnData and \a widget parameters
+ are not used; they are provided for future enhancement. In
+ addition, the \a option parameter is used only in case of the
+ SH_ComboBox_Popup, SH_ComboBox_LayoutDirection, and
+ SH_GroupBox_TextLabelColor style hints.
+*/
+
+/*!
+ \enum QStyle::StandardPixmap
+
+ This enum describes the available standard pixmaps. A standard pixmap is a pixmap that
+ can follow some existing GUI style or guideline.
+
+ \value SP_TitleBarMinButton Minimize button on title bars (e.g.,
+ in QWorkspace).
+ \value SP_TitleBarMenuButton Menu button on a title bar.
+ \value SP_TitleBarMaxButton Maximize button on title bars.
+ \value SP_TitleBarCloseButton Close button on title bars.
+ \value SP_TitleBarNormalButton Normal (restore) button on title bars.
+ \value SP_TitleBarShadeButton Shade button on title bars.
+ \value SP_TitleBarUnshadeButton Unshade button on title bars.
+ \value SP_TitleBarContextHelpButton The Context help button on title bars.
+ \value SP_MessageBoxInformation The "information" icon.
+ \value SP_MessageBoxWarning The "warning" icon.
+ \value SP_MessageBoxCritical The "critical" icon.
+ \value SP_MessageBoxQuestion The "question" icon.
+ \value SP_DesktopIcon The "desktop" icon.
+ \value SP_TrashIcon The "trash" icon.
+ \value SP_ComputerIcon The "My computer" icon.
+ \value SP_DriveFDIcon The floppy icon.
+ \value SP_DriveHDIcon The harddrive icon.
+ \value SP_DriveCDIcon The CD icon.
+ \value SP_DriveDVDIcon The DVD icon.
+ \value SP_DriveNetIcon The network icon.
+ \value SP_DirHomeIcon The home directory icon.
+ \value SP_DirOpenIcon The open directory icon.
+ \value SP_DirClosedIcon The closed directory icon.
+ \value SP_DirIcon The directory icon.
+ \value SP_DirLinkIcon The link to directory icon.
+ \value SP_FileIcon The file icon.
+ \value SP_FileLinkIcon The link to file icon.
+ \value SP_FileDialogStart The "start" icon in a file dialog.
+ \value SP_FileDialogEnd The "end" icon in a file dialog.
+ \value SP_FileDialogToParent The "parent directory" icon in a file dialog.
+ \value SP_FileDialogNewFolder The "create new folder" icon in a file dialog.
+ \value SP_FileDialogDetailedView The detailed view icon in a file dialog.
+ \value SP_FileDialogInfoView The file info icon in a file dialog.
+ \value SP_FileDialogContentsView The contents view icon in a file dialog.
+ \value SP_FileDialogListView The list view icon in a file dialog.
+ \value SP_FileDialogBack The back arrow in a file dialog.
+ \value SP_DockWidgetCloseButton Close button on dock windows (see also QDockWidget).
+ \value SP_ToolBarHorizontalExtensionButton Extension button for horizontal toolbars.
+ \value SP_ToolBarVerticalExtensionButton Extension button for vertical toolbars.
+ \value SP_DialogOkButton Icon for a standard OK button in a QDialogButtonBox.
+ \value SP_DialogCancelButton Icon for a standard Cancel button in a QDialogButtonBox.
+ \value SP_DialogHelpButton Icon for a standard Help button in a QDialogButtonBox.
+ \value SP_DialogOpenButton Icon for a standard Open button in a QDialogButtonBox.
+ \value SP_DialogSaveButton Icon for a standard Save button in a QDialogButtonBox.
+ \value SP_DialogCloseButton Icon for a standard Close button in a QDialogButtonBox.
+ \value SP_DialogApplyButton Icon for a standard Apply button in a QDialogButtonBox.
+ \value SP_DialogResetButton Icon for a standard Reset button in a QDialogButtonBox.
+ \value SP_DialogDiscardButton Icon for a standard Discard button in a QDialogButtonBox.
+ \value SP_DialogYesButton Icon for a standard Yes button in a QDialogButtonBox.
+ \value SP_DialogNoButton Icon for a standard No button in a QDialogButtonBox.
+ \value SP_ArrowUp Icon arrow pointing up.
+ \value SP_ArrowDown Icon arrow pointing down.
+ \value SP_ArrowLeft Icon arrow pointing left.
+ \value SP_ArrowRight Icon arrow pointing right.
+ \value SP_ArrowBack Equivalent to SP_ArrowLeft when the current layout direction is Qt::LeftToRight, otherwise SP_ArrowRight.
+ \value SP_ArrowForward Equivalent to SP_ArrowRight when the current layout direction is Qt::LeftToRight, otherwise SP_ArrowLeft.
+ \value SP_CommandLink Icon used to indicate a Vista style command link glyph.
+ \value SP_VistaShield Icon used to indicate UAC prompts on Windows Vista. This will return a null pixmap or icon on all other platforms.
+ \value SP_BrowserReload Icon indicating that the current page should be reloaded.
+ \value SP_BrowserStop Icon indicating that the page loading should stop.
+ \value SP_MediaPlay Icon indicating that media should begin playback.
+ \value SP_MediaStop Icon indicating that media should stop playback.
+ \value SP_MediaPause Icon indicating that media should pause playback.
+ \value SP_MediaSkipForward Icon indicating that media should skip forward.
+ \value SP_MediaSkipBackward Icon indicating that media should skip backward.
+ \value SP_MediaSeekForward Icon indicating that media should seek forward.
+ \value SP_MediaSeekBackward Icon indicating that media should seek backward.
+ \value SP_MediaVolume Icon indicating a volume control.
+ \value SP_MediaVolumeMuted Icon indicating a muted volume control.
+ \value SP_CustomBase Base value for custom standard pixmaps;
+ custom values must be greater than this value.
+
+ \sa standardPixmap() standardIcon()
+*/
+
+/*###
+ \enum QStyle::IconMode
+
+ This enum represents the effects performed on a pixmap to achieve a
+ GUI style's perferred way of representing the image in different
+ states.
+
+ \value IM_Disabled A disabled pixmap (drawn on disabled widgets)
+ \value IM_Active An active pixmap (drawn on active tool buttons and menu items)
+ \value IM_CustomBase Base value for custom PixmapTypes; custom
+ values must be greater than this value
+
+ \sa generatedIconPixmap()
+*/
+
+/*!
+ \fn QPixmap QStyle::generatedIconPixmap(QIcon::Mode iconMode,
+ const QPixmap &pixmap, const QStyleOption *option) const
+
+ Returns a copy of the given \a pixmap, styled to conform to the
+ specified \a iconMode and taking into account the palette
+ specified by \a option.
+
+ The \a option parameter can pass extra information, but
+ it must contain a palette.
+
+ Note that not all pixmaps will conform, in which case the returned
+ pixmap is a plain copy.
+
+ \sa QIcon
+*/
+
+/*!
+ \fn QPixmap QStyle::standardPixmap(StandardPixmap standardPixmap, const QStyleOption *option, \
+ const QWidget *widget) const
+
+ \obsolete
+ Returns a pixmap for the given \a standardPixmap.
+
+ A standard pixmap is a pixmap that can follow some existing GUI
+ style or guideline. The \a option argument can be used to pass
+ extra information required when defining the appropriate
+ pixmap. The \a widget argument is optional and can also be used to
+ aid the determination of the pixmap.
+
+ Developers calling standardPixmap() should instead call standardIcon()
+ Developers who re-implemented standardPixmap() should instead re-implement
+ the slot standardIconImplementation().
+
+ \sa standardIcon()
+*/
+
+
+/*!
+ \fn QRect QStyle::visualRect(Qt::LayoutDirection direction, const QRect &boundingRectangle, const QRect &logicalRectangle)
+
+ Returns the given \a logicalRectangle converted to screen
+ coordinates based on the specified \a direction. The \a
+ boundingRectangle is used when performing the translation.
+
+ This function is provided to support right-to-left desktops, and
+ is typically used in implementations of the subControlRect()
+ function.
+
+ \sa QWidget::layoutDirection
+*/
+QRect QStyle::visualRect(Qt::LayoutDirection direction, const QRect &boundingRect, const QRect &logicalRect)
+{
+ if (direction == Qt::LeftToRight)
+ return logicalRect;
+ QRect rect = logicalRect;
+ rect.translate(2 * (boundingRect.right() - logicalRect.right()) +
+ logicalRect.width() - boundingRect.width(), 0);
+ return rect;
+}
+
+/*!
+ \fn QPoint QStyle::visualPos(Qt::LayoutDirection direction, const QRect &boundingRectangle, const QPoint &logicalPosition)
+
+ Returns the given \a logicalPosition converted to screen
+ coordinates based on the specified \a direction. The \a
+ boundingRectangle is used when performing the translation.
+
+ \sa QWidget::layoutDirection
+*/
+QPoint QStyle::visualPos(Qt::LayoutDirection direction, const QRect &boundingRect, const QPoint &logicalPos)
+{
+ if (direction == Qt::LeftToRight)
+ return logicalPos;
+ return QPoint(boundingRect.right() - logicalPos.x(), logicalPos.y());
+}
+
+/*!
+ Returns a new rectangle of the specified \a size that is aligned to the given \a
+ rectangle according to the specified \a alignment and \a direction.
+ */
+QRect QStyle::alignedRect(Qt::LayoutDirection direction, Qt::Alignment alignment, const QSize &size, const QRect &rectangle)
+{
+ alignment = visualAlignment(direction, alignment);
+ int x = rectangle.x();
+ int y = rectangle.y();
+ int w = size.width();
+ int h = size.height();
+ if ((alignment & Qt::AlignVCenter) == Qt::AlignVCenter)
+ y += rectangle.size().height()/2 - h/2;
+ else if ((alignment & Qt::AlignBottom) == Qt::AlignBottom)
+ y += rectangle.size().height() - h;
+ if ((alignment & Qt::AlignRight) == Qt::AlignRight)
+ x += rectangle.size().width() - w;
+ else if ((alignment & Qt::AlignHCenter) == Qt::AlignHCenter)
+ x += rectangle.size().width()/2 - w/2;
+ return QRect(x, y, w, h);
+}
+
+/*!
+ Transforms an \a alignment of Qt::AlignLeft or Qt::AlignRight
+ without Qt::AlignAbsolute into Qt::AlignLeft or Qt::AlignRight with
+ Qt::AlignAbsolute according to the layout \a direction. The other
+ alignment flags are left untouched.
+
+ If no horizontal alignment was specified, the function returns the
+ default alignment for the given layout \a direction.
+
+ QWidget::layoutDirection
+*/
+Qt::Alignment QStyle::visualAlignment(Qt::LayoutDirection direction, Qt::Alignment alignment)
+{
+ if (!(alignment & Qt::AlignHorizontal_Mask))
+ alignment |= Qt::AlignLeft;
+ if ((alignment & Qt::AlignAbsolute) == 0 && (alignment & (Qt::AlignLeft | Qt::AlignRight))) {
+ if (direction == Qt::RightToLeft)
+ alignment ^= (Qt::AlignLeft | Qt::AlignRight);
+ alignment |= Qt::AlignAbsolute;
+ }
+ return alignment;
+}
+
+/*!
+ Converts the given \a logicalValue to a pixel position. The \a min
+ parameter maps to 0, \a max maps to \a span and other values are
+ distributed evenly in-between.
+
+ This function can handle the entire integer range without
+ overflow, providing that \a span is less than 4096.
+
+ By default, this function assumes that the maximum value is on the
+ right for horizontal items and on the bottom for vertical items.
+ Set the \a upsideDown parameter to true to reverse this behavior.
+
+ \sa sliderValueFromPosition()
+*/
+
+int QStyle::sliderPositionFromValue(int min, int max, int logicalValue, int span, bool upsideDown)
+{
+ if (span <= 0 || logicalValue < min || max <= min)
+ return 0;
+ if (logicalValue > max)
+ return upsideDown ? span : min;
+
+ uint range = max - min;
+ uint p = upsideDown ? max - logicalValue : logicalValue - min;
+
+ if (range > (uint)INT_MAX/4096) {
+ double dpos = (double(p))/(double(range)/span);
+ return int(dpos);
+ } 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
+}
+
+/*!
+ \fn int QStyle::sliderValueFromPosition(int min, int max, int position, int span, bool upsideDown)
+
+ Converts the given pixel \a position to a logical value. 0 maps to
+ the \a min parameter, \a span maps to \a max and other values are
+ distributed evenly in-between.
+
+ This function can handle the entire integer range without
+ overflow.
+
+ By default, this function assumes that the maximum value is on the
+ right for horizontal items and on the bottom for vertical
+ items. Set the \a upsideDown parameter to true to reverse this
+ behavior.
+
+ \sa sliderPositionFromValue()
+*/
+
+int QStyle::sliderValueFromPosition(int min, int max, int pos, int span, bool upsideDown)
+{
+ if (span <= 0 || pos <= 0)
+ return upsideDown ? max : min;
+ if (pos >= span)
+ return upsideDown ? min : max;
+
+ uint range = max - min;
+
+ if ((uint)span > range) {
+ int tmp = (2 * pos * range + span) / (2 * span);
+ return upsideDown ? max - tmp : tmp + min;
+ } else {
+ uint div = range / span;
+ uint mod = range % span;
+ int tmp = pos * div + (2 * pos * mod + span) / (2 * span);
+ return upsideDown ? max - tmp : tmp + min;
+ }
+ // equiv. to min + (pos*range)/span + 0.5
+ // no overflow because of this implicit assumption:
+ // pos <= span < sqrt(INT_MAX+0.0625)+0.25 ~ sqrt(INT_MAX)
+}
+
+/*### \fn void QStyle::drawItem(QPainter *p, const QRect &r,
+ int flags, const QColorGroup &colorgroup, bool enabled,
+ const QString &text, int len = -1,
+ const QColor *penColor = 0) const
+
+ Use one of the drawItem() overloads that takes a QPalette instead
+ of a QColorGroup.
+*/
+
+/*### \fn void QStyle::drawItem(QPainter *p, const QRect &r,
+ int flags, const QColorGroup colorgroup, bool enabled,
+ const QPixmap &pixmap,
+ const QColor *penColor = 0) const
+
+ Use one of the drawItem() overloads that takes a QPalette instead
+ of a QColorGroup.
+*/
+
+/*### \fn void QStyle::drawItem(QPainter *p, const QRect &r,
+ int flags, const QColorGroup colorgroup, bool enabled,
+ const QPixmap *pixmap,
+ const QString &text, int len = -1,
+ const QColor *penColor = 0) const
+
+ Use one of the drawItem() overloads that takes a QPalette instead
+ of a QColorGroup.
+*/
+
+/*!
+ Returns the style's standard palette.
+
+ Note that on systems that support system colors, the style's
+ standard palette is not used. In particular, the Windows XP,
+ Vista, and Mac styles do not use the standard palette, but make
+ use of native theme engines. With these styles, you should not set
+ the palette with QApplication::setStandardPalette().
+
+ */
+QPalette QStyle::standardPalette() const
+{
+#ifdef Q_WS_X11
+ QColor background;
+ if (QX11Info::appDepth() > 8)
+ background = QColor(0xd4, 0xd0, 0xc8); // win 2000 grey
+ else
+ background = QColor(192, 192, 192);
+#else
+ QColor background(0xd4, 0xd0, 0xc8); // win 2000 grey
+#endif
+ QColor light(background.lighter());
+ QColor dark(background.darker());
+ QColor mid(Qt::gray);
+ QPalette palette(Qt::black, background, light, dark, mid, Qt::black, Qt::white);
+ palette.setBrush(QPalette::Disabled, QPalette::WindowText, dark);
+ palette.setBrush(QPalette::Disabled, QPalette::Text, dark);
+ palette.setBrush(QPalette::Disabled, QPalette::ButtonText, dark);
+ palette.setBrush(QPalette::Disabled, QPalette::Base, background);
+ return palette;
+}
+
+/*!
+ \since 4.1
+
+ Returns an icon for the given \a standardIcon.
+
+ The \a standardIcon is a standard pixmap which can follow some
+ existing GUI style or guideline. The \a option argument can be
+ used to pass extra information required when defining the
+ appropriate icon. The \a widget argument is optional and can also
+ be used to aid the determination of the icon.
+
+ \warning Because of binary compatibility constraints, this
+ function is not virtual. If you want to provide your own icons in
+ a QStyle subclass, reimplement the standardIconImplementation()
+ slot in your subclass instead. The standardIcon() function will
+ dynamically detect the slot and call it.
+
+ \sa standardIconImplementation(), standardPixmap()
+*/
+QIcon QStyle::standardIcon(StandardPixmap standardIcon, const QStyleOption *option,
+ const QWidget *widget) const
+{
+ QIcon result;
+ // ### Qt 4.1: invokeMethod should accept const functions, to avoid this dirty cast
+ QMetaObject::invokeMethod(const_cast<QStyle*>(this),
+ "standardIconImplementation", Qt::DirectConnection,
+ Q_RETURN_ARG(QIcon, result),
+ Q_ARG(StandardPixmap, standardIcon),
+ Q_ARG(const QStyleOption*, option),
+ Q_ARG(const QWidget*, widget));
+ return result;
+}
+
+/*!
+ \since 4.1
+
+ Returns an icon for the given \a standardIcon.
+
+ Reimplement this slot to provide your own icons in a QStyle
+ subclass; because of binary compatibility constraints, the
+ standardIcon() function (introduced in Qt 4.1) is not
+ virtual. Instead, standardIcon() will dynamically detect and call
+ \e this slot. The default implementation simply calls the
+ standardPixmap() function with the given parameters.
+
+ The \a standardIcon is a standard pixmap which can follow some
+ existing GUI style or guideline. The \a option argument can be
+ used to pass extra information required when defining the
+ appropriate icon. The \a widget argument is optional and can also
+ be used to aid the determination of the icon.
+
+ \sa standardIcon()
+*/
+QIcon QStyle::standardIconImplementation(StandardPixmap standardIcon, const QStyleOption *option,
+ const QWidget *widget) const
+{
+ return QIcon(standardPixmap(standardIcon, option, widget));
+}
+
+/*!
+ \since 4.3
+
+ Returns the spacing that should be used between \a control1 and
+ \a control2 in a layout. \a orientation specifies whether the
+ controls are laid out side by side or stacked vertically. The \a
+ option parameter can be used to pass extra information about the
+ parent widget. The \a widget parameter is optional and can also
+ be used if \a option is 0.
+
+ This function is called by the layout system. It is used only if
+ PM_LayoutHorizontalSpacing or PM_LayoutVerticalSpacing returns a
+ negative value.
+
+ For binary compatibility reasons, this function is not virtual.
+ If you want to specify custom layout spacings in a QStyle
+ subclass, implement a slot called layoutSpacingImplementation().
+ QStyle will discover the slot at run-time (using Qt's
+ \l{meta-object system}) and direct all calls to layoutSpacing()
+ to layoutSpacingImplementation().
+
+ \sa combinedLayoutSpacing(), layoutSpacingImplementation()
+*/
+int QStyle::layoutSpacing(QSizePolicy::ControlType control1, QSizePolicy::ControlType control2,
+ Qt::Orientation orientation, const QStyleOption *option,
+ const QWidget *widget) const
+{
+ Q_D(const QStyle);
+ if (d->layoutSpacingIndex == -1) {
+ d->layoutSpacingIndex = metaObject()->indexOfMethod(
+ "layoutSpacingImplementation(QSizePolicy::ControlType,QSizePolicy::ControlType,"
+ "Qt::Orientation,const QStyleOption*,const QWidget*)"
+ );
+ }
+ if (d->layoutSpacingIndex < 0)
+ return -1;
+ int result = -1;
+ void *param[] = {&result, &control1, &control2, &orientation, &option, &widget};
+
+ const_cast<QStyle *>(this)->qt_metacall(QMetaObject::InvokeMetaMethod,
+ d->layoutSpacingIndex, param);
+ return result;
+}
+
+/*!
+ \since 4.3
+
+ Returns the spacing that should be used between \a controls1 and
+ \a controls2 in a layout. \a orientation specifies whether the
+ controls are laid out side by side or stacked vertically. The \a
+ option parameter can be used to pass extra information about the
+ parent widget. The \a widget parameter is optional and can also
+ be used if \a option is 0.
+
+ \a controls1 and \a controls2 are OR-combination of zero or more
+ \l{QSizePolicy::ControlTypes}{control types}.
+
+ This function is called by the layout system. It is used only if
+ PM_LayoutHorizontalSpacing or PM_LayoutVerticalSpacing returns a
+ negative value.
+
+ \sa layoutSpacing(), layoutSpacingImplementation()
+*/
+int QStyle::combinedLayoutSpacing(QSizePolicy::ControlTypes controls1,
+ QSizePolicy::ControlTypes controls2, Qt::Orientation orientation,
+ QStyleOption *option, QWidget *widget) const
+{
+ QSizePolicy::ControlType array1[MaxBits];
+ QSizePolicy::ControlType array2[MaxBits];
+ int count1 = unpackControlTypes(controls1, array1);
+ int count2 = unpackControlTypes(controls2, array2);
+ int result = -1;
+
+ for (int i = 0; i < count1; ++i) {
+ for (int j = 0; j < count2; ++j) {
+ int spacing = layoutSpacing(array1[i], array2[j], orientation, option, widget);
+ result = qMax(spacing, result);
+ }
+ }
+ return result;
+}
+
+/*!
+ \since 4.3
+
+ This slot is called by layoutSpacing() to determine the spacing
+ that should be used between \a control1 and \a control2 in a
+ layout. \a orientation specifies whether the controls are laid
+ out side by side or stacked vertically. The \a option parameter
+ can be used to pass extra information about the parent widget.
+ The \a widget parameter is optional and can also be used if \a
+ option is 0.
+
+ If you want to provide custom layout spacings in a QStyle
+ subclass, implement a slot called layoutSpacingImplementation()
+ in your subclass. Be aware that this slot will only be called if
+ PM_LayoutHorizontalSpacing or PM_LayoutVerticalSpacing returns a
+ negative value.
+
+ The default implementation returns -1.
+
+ \sa layoutSpacing(), combinedLayoutSpacing()
+*/
+int QStyle::layoutSpacingImplementation(QSizePolicy::ControlType /* control1 */,
+ QSizePolicy::ControlType /* control2 */,
+ Qt::Orientation /*orientation*/,
+ const QStyleOption * /* option */,
+ const QWidget * /* widget */) const
+{
+ return -1;
+}
+
+#if !defined(QT_NO_DEBUG) && !defined(QT_NO_DEBUG_STREAM)
+QT_BEGIN_INCLUDE_NAMESPACE
+#include <QDebug>
+QT_END_INCLUDE_NAMESPACE
+
+QDebug operator<<(QDebug debug, QStyle::State state)
+{
+ debug << "QStyle::State(";
+
+ QStringList states;
+ if (state & QStyle::State_Active) states << QLatin1String("Active");
+ if (state & QStyle::State_AutoRaise) states << QLatin1String("AutoRaise");
+ if (state & QStyle::State_Bottom) states << QLatin1String("Bottom");
+ if (state & QStyle::State_Children) states << QLatin1String("Children");
+ if (state & QStyle::State_DownArrow) states << QLatin1String("DownArrow");
+ if (state & QStyle::State_Editing) states << QLatin1String("Editing");
+ if (state & QStyle::State_Enabled) states << QLatin1String("Enabled");
+ if (state & QStyle::State_FocusAtBorder) states << QLatin1String("FocusAtBorder");
+ if (state & QStyle::State_HasFocus) states << QLatin1String("HasFocus");
+ if (state & QStyle::State_Horizontal) states << QLatin1String("Horizontal");
+ if (state & QStyle::State_Item) states << QLatin1String("Item");
+ if (state & QStyle::State_KeyboardFocusChange) states << QLatin1String("KeyboardFocusChange");
+ if (state & QStyle::State_MouseOver) states << QLatin1String("MouseOver");
+ if (state & QStyle::State_NoChange) states << QLatin1String("NoChange");
+ if (state & QStyle::State_Off) states << QLatin1String("Off");
+ if (state & QStyle::State_On) states << QLatin1String("On");
+ if (state & QStyle::State_Open) states << QLatin1String("Open");
+ if (state & QStyle::State_Raised) states << QLatin1String("Raised");
+ if (state & QStyle::State_ReadOnly) states << QLatin1String("ReadOnly");
+ if (state & QStyle::State_Selected) states << QLatin1String("Selected");
+ if (state & QStyle::State_Sibling) states << QLatin1String("Sibling");
+ if (state & QStyle::State_Sunken) states << QLatin1String("Sunken");
+ if (state & QStyle::State_Top) states << QLatin1String("Top");
+ if (state & QStyle::State_UpArrow) states << QLatin1String("UpArrow");
+
+ qSort(states);
+ debug << states.join(QLatin1String(" | "));
+ debug << ")";
+ return debug;
+}
+#endif
+
+QT_END_NAMESPACE
diff --git a/src/gui/styles/qstyle.h b/src/gui/styles/qstyle.h
new file mode 100644
index 0000000000..6191d5105f
--- /dev/null
+++ b/src/gui/styles/qstyle.h
@@ -0,0 +1,875 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSTYLE_H
+#define QSTYLE_H
+
+#include <QtCore/qobject.h>
+#include <QtCore/qrect.h>
+#include <QtCore/qsize.h>
+#include <QtGui/qicon.h>
+#include <QtGui/qpixmap.h>
+#include <QtGui/qpalette.h>
+#include <QtGui/qsizepolicy.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+class QAction;
+class QDebug;
+class QTab;
+class QFontMetrics;
+class QStyleHintReturn;
+class QStyleOption;
+class QStyleOptionComplex;
+class QStylePrivate;
+
+class Q_GUI_EXPORT QStyle : public QObject
+{
+ Q_OBJECT
+ Q_DECLARE_PRIVATE(QStyle)
+
+protected:
+ QStyle(QStylePrivate &dd);
+
+public:
+ QStyle();
+ virtual ~QStyle();
+
+ virtual void polish(QWidget *);
+ virtual void unpolish(QWidget *);
+
+ virtual void polish(QApplication *);
+ virtual void unpolish(QApplication *);
+
+ virtual void polish(QPalette &);
+
+ virtual QRect itemTextRect(const QFontMetrics &fm, const QRect &r,
+ int flags, bool enabled,
+ const QString &text) const;
+
+ virtual QRect itemPixmapRect(const QRect &r, int flags, const QPixmap &pixmap) const;
+
+ virtual void drawItemText(QPainter *painter, const QRect &rect,
+ int flags, const QPalette &pal, bool enabled,
+ const QString &text, QPalette::ColorRole textRole = QPalette::NoRole) const;
+
+ virtual void drawItemPixmap(QPainter *painter, const QRect &rect,
+ int alignment, const QPixmap &pixmap) const;
+
+ virtual QPalette standardPalette() const;
+
+ enum StateFlag {
+ State_None = 0x00000000,
+#ifdef QT3_SUPPORT
+ State_Default = State_None,
+#endif
+ State_Enabled = 0x00000001,
+ State_Raised = 0x00000002,
+ State_Sunken = 0x00000004,
+ State_Off = 0x00000008,
+ State_NoChange = 0x00000010,
+ State_On = 0x00000020,
+ State_DownArrow = 0x00000040,
+ State_Horizontal = 0x00000080,
+ State_HasFocus = 0x00000100,
+ State_Top = 0x00000200,
+ State_Bottom = 0x00000400,
+ State_FocusAtBorder = 0x00000800,
+ State_AutoRaise = 0x00001000,
+ State_MouseOver = 0x00002000,
+ State_UpArrow = 0x00004000,
+ State_Selected = 0x00008000,
+ State_Active = 0x00010000,
+ State_Window = 0x00020000,
+ State_Open = 0x00040000,
+ State_Children = 0x00080000,
+ State_Item = 0x00100000,
+ State_Sibling = 0x00200000,
+ State_Editing = 0x00400000,
+ State_KeyboardFocusChange = 0x00800000,
+#ifdef QT_KEYPAD_NAVIGATION
+ State_HasEditFocus = 0x01000000,
+#endif
+ State_ReadOnly = 0x02000000,
+ State_Small = 0x04000000,
+ State_Mini = 0x08000000
+ };
+ Q_DECLARE_FLAGS(State, StateFlag)
+
+#ifdef QT3_SUPPORT
+ typedef State SFlags;
+#endif
+
+ enum PrimitiveElement {
+ PE_Q3CheckListController,
+ PE_Q3CheckListExclusiveIndicator,
+ PE_Q3CheckListIndicator,
+ PE_Q3DockWindowSeparator,
+ PE_Q3Separator,
+
+ PE_Frame,
+ PE_FrameDefaultButton,
+ PE_FrameDockWidget,
+ PE_FrameFocusRect,
+ PE_FrameGroupBox,
+ PE_FrameLineEdit,
+ PE_FrameMenu,
+ PE_FrameStatusBar, // obsolete
+ PE_FrameStatusBarItem = PE_FrameStatusBar,
+ PE_FrameTabWidget,
+ PE_FrameWindow,
+ PE_FrameButtonBevel,
+ PE_FrameButtonTool,
+ PE_FrameTabBarBase,
+
+ PE_PanelButtonCommand,
+ PE_PanelButtonBevel,
+ PE_PanelButtonTool,
+ PE_PanelMenuBar,
+ PE_PanelToolBar,
+ PE_PanelLineEdit,
+
+ PE_IndicatorArrowDown,
+ PE_IndicatorArrowLeft,
+ PE_IndicatorArrowRight,
+ PE_IndicatorArrowUp,
+ PE_IndicatorBranch,
+ PE_IndicatorButtonDropDown,
+ PE_IndicatorViewItemCheck,
+ PE_IndicatorItemViewItemCheck = PE_IndicatorViewItemCheck,
+ PE_IndicatorCheckBox,
+ PE_IndicatorDockWidgetResizeHandle,
+ PE_IndicatorHeaderArrow,
+ PE_IndicatorMenuCheckMark,
+ PE_IndicatorProgressChunk,
+ PE_IndicatorRadioButton,
+ PE_IndicatorSpinDown,
+ PE_IndicatorSpinMinus,
+ PE_IndicatorSpinPlus,
+ PE_IndicatorSpinUp,
+ PE_IndicatorToolBarHandle,
+ PE_IndicatorToolBarSeparator,
+ PE_PanelTipLabel,
+ PE_IndicatorTabTear,
+ PE_PanelScrollAreaCorner,
+
+ PE_Widget,
+
+ PE_IndicatorColumnViewArrow,
+ PE_IndicatorItemViewItemDrop,
+
+ PE_PanelItemViewItem,
+ PE_PanelItemViewRow, // ### Qt 5: remove
+
+ PE_PanelStatusBar,
+
+ PE_IndicatorTabClose,
+ PE_PanelMenu,
+
+ // do not add any values below/greater this
+ PE_CustomBase = 0xf000000
+ };
+
+ virtual void drawPrimitive(PrimitiveElement pe, const QStyleOption *opt, QPainter *p,
+ const QWidget *w = 0) const = 0;
+ enum ControlElement {
+ CE_PushButton,
+ CE_PushButtonBevel,
+ CE_PushButtonLabel,
+
+ CE_CheckBox,
+ CE_CheckBoxLabel,
+
+ CE_RadioButton,
+ CE_RadioButtonLabel,
+
+ CE_TabBarTab,
+ CE_TabBarTabShape,
+ CE_TabBarTabLabel,
+
+ CE_ProgressBar,
+ CE_ProgressBarGroove,
+ CE_ProgressBarContents,
+ CE_ProgressBarLabel,
+
+ CE_MenuItem,
+ CE_MenuScroller,
+ CE_MenuVMargin,
+ CE_MenuHMargin,
+ CE_MenuTearoff,
+ CE_MenuEmptyArea,
+
+ CE_MenuBarItem,
+ CE_MenuBarEmptyArea,
+
+ CE_ToolButtonLabel,
+
+ CE_Header,
+ CE_HeaderSection,
+ CE_HeaderLabel,
+
+ CE_Q3DockWindowEmptyArea,
+ CE_ToolBoxTab,
+ CE_SizeGrip,
+ CE_Splitter,
+ CE_RubberBand,
+ CE_DockWidgetTitle,
+
+ CE_ScrollBarAddLine,
+ CE_ScrollBarSubLine,
+ CE_ScrollBarAddPage,
+ CE_ScrollBarSubPage,
+ CE_ScrollBarSlider,
+ CE_ScrollBarFirst,
+ CE_ScrollBarLast,
+
+ CE_FocusFrame,
+ CE_ComboBoxLabel,
+
+ CE_ToolBar,
+ CE_ToolBoxTabShape,
+ CE_ToolBoxTabLabel,
+ CE_HeaderEmptyArea,
+
+ CE_ColumnViewGrip,
+
+ CE_ItemViewItem,
+
+ CE_ShapedFrame,
+
+ // do not add any values below/greater than this
+ CE_CustomBase = 0xf0000000
+ };
+
+ virtual void drawControl(ControlElement element, const QStyleOption *opt, QPainter *p,
+ const QWidget *w = 0) const = 0;
+
+ enum SubElement {
+ SE_PushButtonContents,
+ SE_PushButtonFocusRect,
+
+ SE_CheckBoxIndicator,
+ SE_CheckBoxContents,
+ SE_CheckBoxFocusRect,
+ SE_CheckBoxClickRect,
+
+ SE_RadioButtonIndicator,
+ SE_RadioButtonContents,
+ SE_RadioButtonFocusRect,
+ SE_RadioButtonClickRect,
+
+ SE_ComboBoxFocusRect,
+
+ SE_SliderFocusRect,
+
+ SE_Q3DockWindowHandleRect,
+
+ SE_ProgressBarGroove,
+ SE_ProgressBarContents,
+ SE_ProgressBarLabel,
+
+ // ### Qt 5: These values are unused; eliminate them
+ SE_DialogButtonAccept,
+ SE_DialogButtonReject,
+ SE_DialogButtonApply,
+ SE_DialogButtonHelp,
+ SE_DialogButtonAll,
+ SE_DialogButtonAbort,
+ SE_DialogButtonIgnore,
+ SE_DialogButtonRetry,
+ SE_DialogButtonCustom,
+
+ SE_ToolBoxTabContents,
+
+ SE_HeaderLabel,
+ SE_HeaderArrow,
+
+ SE_TabWidgetTabBar,
+ SE_TabWidgetTabPane,
+ SE_TabWidgetTabContents,
+ SE_TabWidgetLeftCorner,
+ SE_TabWidgetRightCorner,
+
+ SE_ViewItemCheckIndicator,
+ SE_ItemViewItemCheckIndicator = SE_ViewItemCheckIndicator,
+
+ SE_TabBarTearIndicator,
+
+ SE_TreeViewDisclosureItem,
+
+ SE_LineEditContents,
+ SE_FrameContents,
+
+ SE_DockWidgetCloseButton,
+ SE_DockWidgetFloatButton,
+ SE_DockWidgetTitleBarText,
+ SE_DockWidgetIcon,
+
+ SE_CheckBoxLayoutItem,
+ SE_ComboBoxLayoutItem,
+ SE_DateTimeEditLayoutItem,
+ SE_DialogButtonBoxLayoutItem, // ### remove
+ SE_LabelLayoutItem,
+ SE_ProgressBarLayoutItem,
+ SE_PushButtonLayoutItem,
+ SE_RadioButtonLayoutItem,
+ SE_SliderLayoutItem,
+ SE_SpinBoxLayoutItem,
+ SE_ToolButtonLayoutItem,
+
+ SE_FrameLayoutItem,
+ SE_GroupBoxLayoutItem,
+ SE_TabWidgetLayoutItem,
+
+ SE_ItemViewItemDecoration,
+ SE_ItemViewItemText,
+ SE_ItemViewItemFocusRect,
+
+ SE_TabBarTabLeftButton,
+ SE_TabBarTabRightButton,
+ SE_TabBarTabText,
+
+ SE_ShapedFrameContents,
+
+ // do not add any values below/greater than this
+ SE_CustomBase = 0xf0000000
+ };
+
+ virtual QRect subElementRect(SubElement subElement, const QStyleOption *option,
+ const QWidget *widget = 0) const = 0;
+
+
+ enum ComplexControl {
+ CC_SpinBox,
+ CC_ComboBox,
+ CC_ScrollBar,
+ CC_Slider,
+ CC_ToolButton,
+ CC_TitleBar,
+ CC_Q3ListView,
+ CC_Dial,
+ CC_GroupBox,
+ CC_MdiControls,
+
+ // do not add any values below/greater than this
+ CC_CustomBase = 0xf0000000
+ };
+
+ enum SubControl {
+ SC_None = 0x00000000,
+
+ SC_ScrollBarAddLine = 0x00000001,
+ SC_ScrollBarSubLine = 0x00000002,
+ SC_ScrollBarAddPage = 0x00000004,
+ SC_ScrollBarSubPage = 0x00000008,
+ SC_ScrollBarFirst = 0x00000010,
+ SC_ScrollBarLast = 0x00000020,
+ SC_ScrollBarSlider = 0x00000040,
+ SC_ScrollBarGroove = 0x00000080,
+
+ SC_SpinBoxUp = 0x00000001,
+ SC_SpinBoxDown = 0x00000002,
+ SC_SpinBoxFrame = 0x00000004,
+ SC_SpinBoxEditField = 0x00000008,
+
+ SC_ComboBoxFrame = 0x00000001,
+ SC_ComboBoxEditField = 0x00000002,
+ SC_ComboBoxArrow = 0x00000004,
+ SC_ComboBoxListBoxPopup = 0x00000008,
+
+ SC_SliderGroove = 0x00000001,
+ SC_SliderHandle = 0x00000002,
+ SC_SliderTickmarks = 0x00000004,
+
+ SC_ToolButton = 0x00000001,
+ SC_ToolButtonMenu = 0x00000002,
+
+ SC_TitleBarSysMenu = 0x00000001,
+ SC_TitleBarMinButton = 0x00000002,
+ SC_TitleBarMaxButton = 0x00000004,
+ SC_TitleBarCloseButton = 0x00000008,
+ SC_TitleBarNormalButton = 0x00000010,
+ SC_TitleBarShadeButton = 0x00000020,
+ SC_TitleBarUnshadeButton = 0x00000040,
+ SC_TitleBarContextHelpButton = 0x00000080,
+ SC_TitleBarLabel = 0x00000100,
+
+ SC_Q3ListView = 0x00000001,
+ SC_Q3ListViewBranch = 0x00000002,
+ SC_Q3ListViewExpand = 0x00000004,
+
+ SC_DialGroove = 0x00000001,
+ SC_DialHandle = 0x00000002,
+ SC_DialTickmarks = 0x00000004,
+
+ SC_GroupBoxCheckBox = 0x00000001,
+ SC_GroupBoxLabel = 0x00000002,
+ SC_GroupBoxContents = 0x00000004,
+ SC_GroupBoxFrame = 0x00000008,
+
+ SC_MdiMinButton = 0x00000001,
+ SC_MdiNormalButton = 0x00000002,
+ SC_MdiCloseButton = 0x00000004,
+
+ SC_All = 0xffffffff
+ };
+ Q_DECLARE_FLAGS(SubControls, SubControl)
+
+#ifdef QT3_SUPPORT
+ typedef SubControls SCFlags;
+#endif
+
+ virtual void drawComplexControl(ComplexControl cc, const QStyleOptionComplex *opt, QPainter *p,
+ const QWidget *widget = 0) const = 0;
+ virtual SubControl hitTestComplexControl(ComplexControl cc, const QStyleOptionComplex *opt,
+ const QPoint &pt, const QWidget *widget = 0) const = 0;
+ virtual QRect subControlRect(ComplexControl cc, const QStyleOptionComplex *opt,
+ SubControl sc, const QWidget *widget = 0) const = 0;
+
+ enum PixelMetric {
+ PM_ButtonMargin,
+ PM_ButtonDefaultIndicator,
+ PM_MenuButtonIndicator,
+ PM_ButtonShiftHorizontal,
+ PM_ButtonShiftVertical,
+
+ PM_DefaultFrameWidth,
+ PM_SpinBoxFrameWidth,
+ PM_ComboBoxFrameWidth,
+
+ PM_MaximumDragDistance,
+
+ PM_ScrollBarExtent,
+ PM_ScrollBarSliderMin,
+
+ PM_SliderThickness, // total slider thickness
+ PM_SliderControlThickness, // thickness of the business part
+ PM_SliderLength, // total length of slider
+ PM_SliderTickmarkOffset, //
+ PM_SliderSpaceAvailable, // available space for slider to move
+
+ PM_DockWidgetSeparatorExtent,
+ PM_DockWidgetHandleExtent,
+ PM_DockWidgetFrameWidth,
+
+ PM_TabBarTabOverlap,
+ PM_TabBarTabHSpace,
+ PM_TabBarTabVSpace,
+ PM_TabBarBaseHeight,
+ PM_TabBarBaseOverlap,
+
+ PM_ProgressBarChunkWidth,
+
+ PM_SplitterWidth,
+ PM_TitleBarHeight,
+
+ PM_MenuScrollerHeight,
+ PM_MenuHMargin,
+ PM_MenuVMargin,
+ PM_MenuPanelWidth,
+ PM_MenuTearoffHeight,
+ PM_MenuDesktopFrameWidth,
+
+ PM_MenuBarPanelWidth,
+ PM_MenuBarItemSpacing,
+ PM_MenuBarVMargin,
+ PM_MenuBarHMargin,
+
+ PM_IndicatorWidth,
+ PM_IndicatorHeight,
+ PM_ExclusiveIndicatorWidth,
+ PM_ExclusiveIndicatorHeight,
+ PM_CheckListButtonSize,
+ PM_CheckListControllerSize,
+
+ PM_DialogButtonsSeparator,
+ PM_DialogButtonsButtonWidth,
+ PM_DialogButtonsButtonHeight,
+
+ PM_MdiSubWindowFrameWidth,
+ PM_MDIFrameWidth = PM_MdiSubWindowFrameWidth, //obsolete
+ PM_MdiSubWindowMinimizedWidth,
+ PM_MDIMinimizedWidth = PM_MdiSubWindowMinimizedWidth, //obsolete
+
+ PM_HeaderMargin,
+ PM_HeaderMarkSize,
+ PM_HeaderGripMargin,
+ PM_TabBarTabShiftHorizontal,
+ PM_TabBarTabShiftVertical,
+ PM_TabBarScrollButtonWidth,
+
+ PM_ToolBarFrameWidth,
+ PM_ToolBarHandleExtent,
+ PM_ToolBarItemSpacing,
+ PM_ToolBarItemMargin,
+ PM_ToolBarSeparatorExtent,
+ PM_ToolBarExtensionExtent,
+
+ PM_SpinBoxSliderHeight,
+
+ PM_DefaultTopLevelMargin,
+ PM_DefaultChildMargin,
+ PM_DefaultLayoutSpacing,
+
+ PM_ToolBarIconSize,
+ PM_ListViewIconSize,
+ PM_IconViewIconSize,
+ PM_SmallIconSize,
+ PM_LargeIconSize,
+
+ PM_FocusFrameVMargin,
+ PM_FocusFrameHMargin,
+
+ PM_ToolTipLabelFrameWidth,
+ PM_CheckBoxLabelSpacing,
+ PM_TabBarIconSize,
+ PM_SizeGripSize,
+ PM_DockWidgetTitleMargin,
+ PM_MessageBoxIconSize,
+ PM_ButtonIconSize,
+
+ PM_DockWidgetTitleBarButtonMargin,
+
+ PM_RadioButtonLabelSpacing,
+ PM_LayoutLeftMargin,
+ PM_LayoutTopMargin,
+ PM_LayoutRightMargin,
+ PM_LayoutBottomMargin,
+ PM_LayoutHorizontalSpacing,
+ PM_LayoutVerticalSpacing,
+ PM_TabBar_ScrollButtonOverlap,
+
+ PM_TextCursorWidth,
+
+ PM_TabCloseIndicatorWidth,
+ PM_TabCloseIndicatorHeight,
+
+ PM_ScrollView_ScrollBarSpacing,
+ PM_SubMenuOverlap,
+
+ // do not add any values below/greater than this
+ PM_CustomBase = 0xf0000000
+ };
+
+ virtual int pixelMetric(PixelMetric metric, const QStyleOption *option = 0,
+ const QWidget *widget = 0) const = 0;
+
+ enum ContentsType {
+ CT_PushButton,
+ CT_CheckBox,
+ CT_RadioButton,
+ CT_ToolButton,
+ CT_ComboBox,
+ CT_Splitter,
+ CT_Q3DockWindow,
+ CT_ProgressBar,
+ CT_MenuItem,
+ CT_MenuBarItem,
+ CT_MenuBar,
+ CT_Menu,
+ CT_TabBarTab,
+ CT_Slider,
+ CT_ScrollBar,
+ CT_Q3Header,
+ CT_LineEdit,
+ CT_SpinBox,
+ CT_SizeGrip,
+ CT_TabWidget,
+ CT_DialogButtons,
+ CT_HeaderSection,
+ CT_GroupBox,
+ CT_MdiControls,
+ CT_ItemViewItem,
+ // do not add any values below/greater than this
+ CT_CustomBase = 0xf0000000
+ };
+
+ virtual QSize sizeFromContents(ContentsType ct, const QStyleOption *opt,
+ const QSize &contentsSize, const QWidget *w = 0) const = 0;
+
+ enum StyleHint {
+ SH_EtchDisabledText,
+ SH_DitherDisabledText,
+ SH_ScrollBar_MiddleClickAbsolutePosition,
+ SH_ScrollBar_ScrollWhenPointerLeavesControl,
+ SH_TabBar_SelectMouseType,
+ SH_TabBar_Alignment,
+ SH_Header_ArrowAlignment,
+ SH_Slider_SnapToValue,
+ SH_Slider_SloppyKeyEvents,
+ SH_ProgressDialog_CenterCancelButton,
+ SH_ProgressDialog_TextLabelAlignment,
+ SH_PrintDialog_RightAlignButtons,
+ SH_MainWindow_SpaceBelowMenuBar,
+ SH_FontDialog_SelectAssociatedText,
+ SH_Menu_AllowActiveAndDisabled,
+ SH_Menu_SpaceActivatesItem,
+ SH_Menu_SubMenuPopupDelay,
+ SH_ScrollView_FrameOnlyAroundContents,
+ SH_MenuBar_AltKeyNavigation,
+ SH_ComboBox_ListMouseTracking,
+ SH_Menu_MouseTracking,
+ SH_MenuBar_MouseTracking,
+ SH_ItemView_ChangeHighlightOnFocus,
+ SH_Widget_ShareActivation,
+ SH_Workspace_FillSpaceOnMaximize,
+ SH_ComboBox_Popup,
+ SH_TitleBar_NoBorder,
+ SH_Slider_StopMouseOverSlider,
+ SH_ScrollBar_StopMouseOverSlider = SH_Slider_StopMouseOverSlider, // obsolete
+ SH_BlinkCursorWhenTextSelected,
+ SH_RichText_FullWidthSelection,
+ SH_Menu_Scrollable,
+ SH_GroupBox_TextLabelVerticalAlignment,
+ SH_GroupBox_TextLabelColor,
+ SH_Menu_SloppySubMenus,
+ SH_Table_GridLineColor,
+ SH_LineEdit_PasswordCharacter,
+ SH_DialogButtons_DefaultButton,
+ SH_ToolBox_SelectedPageTitleBold,
+ SH_TabBar_PreferNoArrows,
+ SH_ScrollBar_LeftClickAbsolutePosition,
+ SH_Q3ListViewExpand_SelectMouseType,
+ SH_UnderlineShortcut,
+ SH_SpinBox_AnimateButton,
+ SH_SpinBox_KeyPressAutoRepeatRate,
+ SH_SpinBox_ClickAutoRepeatRate,
+ SH_Menu_FillScreenWithScroll,
+ SH_ToolTipLabel_Opacity,
+ SH_DrawMenuBarSeparator,
+ SH_TitleBar_ModifyNotification,
+ SH_Button_FocusPolicy,
+ SH_MenuBar_DismissOnSecondClick,
+ SH_MessageBox_UseBorderForButtonSpacing,
+ SH_TitleBar_AutoRaise,
+ SH_ToolButton_PopupDelay,
+ SH_FocusFrame_Mask,
+ SH_RubberBand_Mask,
+ SH_WindowFrame_Mask,
+ SH_SpinControls_DisableOnBounds,
+ SH_Dial_BackgroundRole,
+ SH_ComboBox_LayoutDirection,
+ SH_ItemView_EllipsisLocation,
+ SH_ItemView_ShowDecorationSelected,
+ SH_ItemView_ActivateItemOnSingleClick,
+ SH_ScrollBar_ContextMenu,
+ SH_ScrollBar_RollBetweenButtons,
+ SH_Slider_AbsoluteSetButtons,
+ SH_Slider_PageSetButtons,
+ SH_Menu_KeyboardSearch,
+ SH_TabBar_ElideMode,
+ SH_DialogButtonLayout,
+ SH_ComboBox_PopupFrameStyle,
+ SH_MessageBox_TextInteractionFlags,
+ SH_DialogButtonBox_ButtonsHaveIcons,
+ SH_SpellCheckUnderlineStyle,
+ SH_MessageBox_CenterButtons,
+ SH_Menu_SelectionWrap,
+ SH_ItemView_MovementWithoutUpdatingSelection,
+ SH_ToolTip_Mask,
+ SH_FocusFrame_AboveWidget,
+ SH_TextControl_FocusIndicatorTextCharFormat,
+ SH_WizardStyle,
+ SH_ItemView_ArrowKeysNavigateIntoChildren,
+ SH_Menu_Mask,
+ SH_Menu_FlashTriggeredItem,
+ SH_Menu_FadeOutOnHide,
+ SH_SpinBox_ClickAutoRepeatThreshold,
+ SH_ItemView_PaintAlternatingRowColorsForEmptyArea,
+ SH_FormLayoutWrapPolicy,
+ SH_TabWidget_DefaultTabPosition,
+ SH_ToolBar_Movable,
+ SH_FormLayoutFieldGrowthPolicy,
+ SH_FormLayoutFormAlignment,
+ SH_FormLayoutLabelAlignment,
+ SH_ItemView_DrawDelegateFrame,
+ SH_TabBar_CloseButtonPosition,
+ SH_DockWidget_ButtonsHaveFrame,
+
+ // Add new style hint values here
+
+#ifdef QT3_SUPPORT
+ SH_GUIStyle = 0x00000100,
+ SH_ScrollBar_BackgroundMode,
+ // Add other compat values here
+
+ SH_UnderlineAccelerator = SH_UnderlineShortcut,
+#endif
+ SH_CustomBase = 0xf0000000
+ };
+
+ virtual int styleHint(StyleHint stylehint, const QStyleOption *opt = 0,
+ const QWidget *widget = 0, QStyleHintReturn* returnData = 0) const = 0;
+
+ enum StandardPixmap {
+ SP_TitleBarMenuButton,
+ SP_TitleBarMinButton,
+ SP_TitleBarMaxButton,
+ SP_TitleBarCloseButton,
+ SP_TitleBarNormalButton,
+ SP_TitleBarShadeButton,
+ SP_TitleBarUnshadeButton,
+ SP_TitleBarContextHelpButton,
+ SP_DockWidgetCloseButton,
+ SP_MessageBoxInformation,
+ SP_MessageBoxWarning,
+ SP_MessageBoxCritical,
+ SP_MessageBoxQuestion,
+ SP_DesktopIcon,
+ SP_TrashIcon,
+ SP_ComputerIcon,
+ SP_DriveFDIcon,
+ SP_DriveHDIcon,
+ SP_DriveCDIcon,
+ SP_DriveDVDIcon,
+ SP_DriveNetIcon,
+ SP_DirOpenIcon,
+ SP_DirClosedIcon,
+ SP_DirLinkIcon,
+ SP_FileIcon,
+ SP_FileLinkIcon,
+ SP_ToolBarHorizontalExtensionButton,
+ SP_ToolBarVerticalExtensionButton,
+ SP_FileDialogStart,
+ SP_FileDialogEnd,
+ SP_FileDialogToParent,
+ SP_FileDialogNewFolder,
+ SP_FileDialogDetailedView,
+ SP_FileDialogInfoView,
+ SP_FileDialogContentsView,
+ SP_FileDialogListView,
+ SP_FileDialogBack,
+ SP_DirIcon,
+ SP_DialogOkButton,
+ SP_DialogCancelButton,
+ SP_DialogHelpButton,
+ SP_DialogOpenButton,
+ SP_DialogSaveButton,
+ SP_DialogCloseButton,
+ SP_DialogApplyButton,
+ SP_DialogResetButton,
+ SP_DialogDiscardButton,
+ SP_DialogYesButton,
+ SP_DialogNoButton,
+ SP_ArrowUp,
+ SP_ArrowDown,
+ SP_ArrowLeft,
+ SP_ArrowRight,
+ SP_ArrowBack,
+ SP_ArrowForward,
+ SP_DirHomeIcon,
+ SP_CommandLink,
+ SP_VistaShield,
+ SP_BrowserReload,
+ SP_BrowserStop,
+ SP_MediaPlay,
+ SP_MediaStop,
+ SP_MediaPause,
+ SP_MediaSkipForward,
+ SP_MediaSkipBackward,
+ SP_MediaSeekForward,
+ SP_MediaSeekBackward,
+ SP_MediaVolume,
+ SP_MediaVolumeMuted,
+ // do not add any values below/greater than this
+ SP_CustomBase = 0xf0000000
+ };
+
+ virtual QPixmap standardPixmap(StandardPixmap standardPixmap, const QStyleOption *opt = 0,
+ const QWidget *widget = 0) const = 0;
+
+ QIcon standardIcon(StandardPixmap standardIcon, const QStyleOption *option = 0,
+ const QWidget *widget = 0) const;
+
+ virtual QPixmap generatedIconPixmap(QIcon::Mode iconMode, const QPixmap &pixmap,
+ const QStyleOption *opt) const = 0;
+
+ static QRect visualRect(Qt::LayoutDirection direction, const QRect &boundingRect,
+ const QRect &logicalRect);
+ static QPoint visualPos(Qt::LayoutDirection direction, const QRect &boundingRect,
+ const QPoint &logicalPos);
+ static int sliderPositionFromValue(int min, int max, int val, int space,
+ bool upsideDown = false);
+ static int sliderValueFromPosition(int min, int max, int pos, int space,
+ bool upsideDown = false);
+ static Qt::Alignment visualAlignment(Qt::LayoutDirection direction, Qt::Alignment alignment);
+ static QRect alignedRect(Qt::LayoutDirection direction, Qt::Alignment alignment,
+ const QSize &size, const QRect &rectangle);
+
+ int layoutSpacing(QSizePolicy::ControlType control1,
+ QSizePolicy::ControlType control2, Qt::Orientation orientation,
+ const QStyleOption *option = 0, const QWidget *widget = 0) const;
+ int combinedLayoutSpacing(QSizePolicy::ControlTypes controls1,
+ QSizePolicy::ControlTypes controls2, Qt::Orientation orientation,
+ QStyleOption *option = 0, QWidget *widget = 0) const;
+
+protected Q_SLOTS:
+ QIcon standardIconImplementation(StandardPixmap standardIcon, const QStyleOption *opt = 0,
+ const QWidget *widget = 0) const;
+ int layoutSpacingImplementation(QSizePolicy::ControlType control1,
+ QSizePolicy::ControlType control2,
+ Qt::Orientation orientation,
+ const QStyleOption *option = 0,
+ const QWidget *widget = 0) const;
+
+private:
+ Q_DISABLE_COPY(QStyle)
+ friend class QWidget;
+ friend class QWidgetPrivate;
+ friend class QApplication;
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(QStyle::State)
+Q_DECLARE_OPERATORS_FOR_FLAGS(QStyle::SubControls)
+
+#if !defined(QT_NO_DEBUG_STREAM) && !defined(QT_NO_DEBUG)
+Q_GUI_EXPORT QDebug operator<<(QDebug debug, QStyle::State state);
+#endif
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QSTYLE_H
diff --git a/src/gui/styles/qstyle.qrc b/src/gui/styles/qstyle.qrc
new file mode 100644
index 0000000000..8654e66a37
--- /dev/null
+++ b/src/gui/styles/qstyle.qrc
@@ -0,0 +1,135 @@
+<!DOCTYPE RCC><RCC version="1.0">
+<qresource prefix="/trolltech/styles/commonstyle">
+<file>images/filelink-16.png</file>
+<file>images/filelink-32.png</file>
+<file>images/filelink-128.png</file>
+<file>images/file-16.png</file>
+<file>images/file-32.png</file>
+<file>images/file-128.png</file>
+<file>images/newdirectory-16.png</file>
+<file>images/newdirectory-32.png</file>
+<file>images/newdirectory-128.png</file>
+<file>images/parentdir-16.png</file>
+<file>images/parentdir-32.png</file>
+<file>images/parentdir-128.png</file>
+<file>images/dvd-16.png</file>
+<file>images/dvd-32.png</file>
+<file>images/dvd-128.png</file>
+<file>images/cdr-16.png</file>
+<file>images/cdr-32.png</file>
+<file>images/cdr-128.png</file>
+<file>images/floppy-16.png</file>
+<file>images/floppy-32.png</file>
+<file>images/floppy-128.png</file>
+<file>images/harddrive-16.png</file>
+<file>images/harddrive-32.png</file>
+<file>images/harddrive-128.png</file>
+<file>images/trash-16.png</file>
+<file>images/trash-32.png</file>
+<file>images/trash-128.png</file>
+<file>images/networkdrive-16.png</file>
+<file>images/networkdrive-32.png</file>
+<file>images/networkdrive-128.png</file>
+<file>images/computer-16.png</file>
+<file>images/computer-32.png</file>
+<file>images/desktop-16.png</file>
+<file>images/desktop-32.png</file>
+<file>images/dirclosed-16.png</file>
+<file>images/dirclosed-32.png</file>
+<file>images/dirclosed-128.png</file>
+<file>images/dirlink-16.png</file>
+<file>images/dirlink-32.png</file>
+<file>images/dirlink-128.png</file>
+<file>images/diropen-16.png</file>
+<file>images/diropen-32.png</file>
+<file>images/diropen-128.png</file>
+<file>images/left-16.png</file>
+<file>images/left-32.png</file>
+<file>images/left-128.png</file>
+<file>images/right-16.png</file>
+<file>images/right-32.png</file>
+<file>images/right-128.png</file>
+<file>images/up-16.png</file>
+<file>images/up-32.png</file>
+<file>images/up-128.png</file>
+<file>images/down-16.png</file>
+<file>images/down-32.png</file>
+<file>images/down-128.png</file>
+<file>images/filecontents-16.png</file>
+<file>images/filecontents-32.png</file>
+<file>images/filecontents-128.png</file>
+<file>images/fileinfo-16.png</file>
+<file>images/fileinfo-32.png</file>
+<file>images/fileinfo-128.png</file>
+<file>images/viewdetailed-16.png</file>
+<file>images/viewdetailed-32.png</file>
+<file>images/viewdetailed-128.png</file>
+<file>images/viewlist-16.png</file>
+<file>images/viewlist-32.png</file>
+<file>images/viewlist-128.png</file>
+<file>images/fontbitmap-16.png</file>
+<file>images/fonttruetype-16.png</file>
+<file>images/standardbutton-apply-128.png</file>
+<file>images/standardbutton-apply-16.png</file>
+<file>images/standardbutton-apply-32.png</file>
+<file>images/standardbutton-cancel-128.png</file>
+<file>images/standardbutton-cancel-16.png</file>
+<file>images/standardbutton-cancel-32.png</file>
+<file>images/standardbutton-clear-128.png</file>
+<file>images/standardbutton-clear-16.png</file>
+<file>images/standardbutton-clear-32.png</file>
+<file>images/standardbutton-close-128.png</file>
+<file>images/standardbutton-close-16.png</file>
+<file>images/standardbutton-close-32.png</file>
+<file>images/standardbutton-delete-128.png</file>
+<file>images/standardbutton-delete-16.png</file>
+<file>images/standardbutton-delete-32.png</file>
+<file>images/standardbutton-help-128.png</file>
+<file>images/standardbutton-help-16.png</file>
+<file>images/standardbutton-help-32.png</file>
+<file>images/standardbutton-no-128.png</file>
+<file>images/standardbutton-no-16.png</file>
+<file>images/standardbutton-no-32.png</file>
+<file>images/standardbutton-ok-128.png</file>
+<file>images/standardbutton-ok-16.png</file>
+<file>images/standardbutton-ok-32.png</file>
+<file>images/standardbutton-open-128.png</file>
+<file>images/standardbutton-open-16.png</file>
+<file>images/standardbutton-open-32.png</file>
+<file>images/standardbutton-save-128.png</file>
+<file>images/standardbutton-save-16.png</file>
+<file>images/standardbutton-save-32.png</file>
+<file>images/standardbutton-yes-128.png</file>
+<file>images/standardbutton-yes-16.png</file>
+<file>images/standardbutton-yes-32.png</file>
+<file>images/standardbutton-closetab-16.png</file>
+<file>images/standardbutton-closetab-down-16.png</file>
+<file>images/standardbutton-closetab-hover-16.png</file>
+<file>images/refresh-24.png</file>
+<file>images/refresh-32.png</file>
+<file>images/stop-24.png</file>
+<file>images/stop-32.png</file>
+<file>images/media-stop-16.png</file>
+<file>images/media-stop-32.png</file>
+<file>images/media-play-16.png</file>
+<file>images/media-play-32.png</file>
+<file>images/media-pause-16.png</file>
+<file>images/media-pause-32.png</file>
+<file>images/media-seek-forward-16.png</file>
+<file>images/media-seek-forward-32.png</file>
+<file>images/media-seek-backward-16.png</file>
+<file>images/media-seek-backward-32.png</file>
+<file>images/media-skip-forward-16.png</file>
+<file>images/media-skip-forward-32.png</file>
+<file>images/media-skip-backward-16.png</file>
+<file>images/media-skip-backward-32.png</file>
+<file>images/media-volume-16.png</file>
+<file>images/media-volume-muted-16.png</file>
+</qresource>
+<qresource prefix="/trolltech/styles/macstyle">
+<file>images/closedock-16.png</file>
+<file>images/closedock-down-16.png</file>
+<file>images/dockdock-16.png</file>
+<file>images/dockdock-down-16.png</file>
+</qresource>
+</RCC>
diff --git a/src/gui/styles/qstyle_p.h b/src/gui/styles/qstyle_p.h
new file mode 100644
index 0000000000..848bad6e66
--- /dev/null
+++ b/src/gui/styles/qstyle_p.h
@@ -0,0 +1,104 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSTYLE_P_H
+#define QSTYLE_P_H
+
+#include "private/qobject_p.h"
+
+QT_BEGIN_NAMESPACE
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of qapplication_*.cpp, qwidget*.cpp and qfiledialog.cpp. This header
+// file may change from version to version without notice, or even be removed.
+//
+// We mean it.
+//
+
+// Private class
+
+class QStylePrivate: public QObjectPrivate
+{
+ Q_DECLARE_PUBLIC(QStyle)
+public:
+ inline QStylePrivate()
+ : layoutSpacingIndex(-1)
+ { }
+ mutable int layoutSpacingIndex;
+};
+
+
+#define BEGIN_STYLE_PIXMAPCACHE(a) \
+ QRect rect = option->rect; \
+ QPixmap internalPixmapCache; \
+ QImage imageCache; \
+ QPainter *p = painter; \
+ QString unique = uniqueName((a), option, option->rect.size()); \
+ int txType = painter->deviceTransform().type() | painter->worldTransform().type(); \
+ bool doPixmapCache = UsePixmapCache && txType <= QTransform::TxTranslate; \
+ if (doPixmapCache && QPixmapCache::find(unique, internalPixmapCache)) { \
+ painter->drawPixmap(option->rect.topLeft(), internalPixmapCache); \
+ } else { \
+ if (doPixmapCache) { \
+ rect.setRect(0, 0, option->rect.width(), option->rect.height()); \
+ imageCache = QImage(option->rect.size(), QImage::Format_ARGB32_Premultiplied); \
+ imageCache.fill(0); \
+ p = new QPainter(&imageCache); \
+ }
+
+
+#define END_STYLE_PIXMAPCACHE \
+ if (doPixmapCache) { \
+ p->end(); \
+ delete p; \
+ internalPixmapCache = QPixmap::fromImage(imageCache); \
+ painter->drawPixmap(option->rect.topLeft(), internalPixmapCache); \
+ QPixmapCache::insert(unique, internalPixmapCache); \
+ } \
+ }
+
+QT_END_NAMESPACE
+
+#endif //QSTYLE_P_H
diff --git a/src/gui/styles/qstyle_wince.qrc b/src/gui/styles/qstyle_wince.qrc
new file mode 100644
index 0000000000..bdcf604625
--- /dev/null
+++ b/src/gui/styles/qstyle_wince.qrc
@@ -0,0 +1,97 @@
+<!DOCTYPE RCC><RCC version="1.0">
+<qresource prefix="/trolltech/styles/commonstyle">
+<file>images/filelink-16.png</file>
+<file>images/filelink-32.png</file>
+<file>images/file-16.png</file>
+<file>images/file-32.png</file>
+<file>images/newdirectory-16.png</file>
+<file>images/newdirectory-32.png</file>
+<file>images/parentdir-16.png</file>
+<file>images/parentdir-32.png</file>
+<file>images/dvd-16.png</file>
+<file>images/dvd-32.png</file>
+<file>images/cdr-16.png</file>
+<file>images/cdr-32.png</file>
+<file>images/floppy-16.png</file>
+<file>images/floppy-32.png</file>
+<file>images/harddrive-16.png</file>
+<file>images/harddrive-32.png</file>
+<file>images/trash-16.png</file>
+<file>images/trash-32.png</file>
+<file>images/networkdrive-16.png</file>
+<file>images/networkdrive-32.png</file>
+<file>images/computer-16.png</file>
+<file>images/computer-32.png</file>
+<file>images/desktop-16.png</file>
+<file>images/desktop-32.png</file>
+<file>images/dirclosed-16.png</file>
+<file>images/dirclosed-32.png</file>
+<file>images/dirlink-16.png</file>
+<file>images/dirlink-32.png</file>
+<file>images/diropen-16.png</file>
+<file>images/diropen-32.png</file>
+<file>images/left-16.png</file>
+<file>images/left-32.png</file>
+<file>images/right-16.png</file>
+<file>images/right-32.png</file>
+<file>images/up-16.png</file>
+<file>images/up-32.png</file>
+<file>images/down-16.png</file>
+<file>images/down-32.png</file>
+<file>images/filecontents-16.png</file>
+<file>images/filecontents-32.png</file>
+<file>images/fileinfo-16.png</file>
+<file>images/fileinfo-32.png</file>
+<file>images/viewdetailed-16.png</file>
+<file>images/viewdetailed-32.png</file>
+<file>images/viewlist-16.png</file>
+<file>images/viewlist-32.png</file>
+<file>images/fontbitmap-16.png</file>
+<file>images/fonttruetype-16.png</file>
+<file>images/standardbutton-apply-16.png</file>
+<file>images/standardbutton-apply-32.png</file>
+<file>images/standardbutton-cancel-16.png</file>
+<file>images/standardbutton-cancel-32.png</file>
+<file>images/standardbutton-clear-16.png</file>
+<file>images/standardbutton-clear-32.png</file>
+<file>images/standardbutton-close-16.png</file>
+<file>images/standardbutton-close-32.png</file>
+<file>images/standardbutton-delete-16.png</file>
+<file>images/standardbutton-delete-32.png</file>
+<file>images/standardbutton-help-16.png</file>
+<file>images/standardbutton-help-32.png</file>
+<file>images/standardbutton-no-16.png</file>
+<file>images/standardbutton-no-32.png</file>
+<file>images/standardbutton-ok-16.png</file>
+<file>images/standardbutton-ok-32.png</file>
+<file>images/standardbutton-open-16.png</file>
+<file>images/standardbutton-open-32.png</file>
+<file>images/standardbutton-save-16.png</file>
+<file>images/standardbutton-save-32.png</file>
+<file>images/standardbutton-yes-16.png</file>
+<file>images/standardbutton-yes-32.png</file>
+<file>images/standardbutton-closetab-16.png</file>
+<file>images/standardbutton-closetab-down-16.png</file>
+<file>images/standardbutton-closetab-hover-16.png</file>
+<file>images/refresh-24.png</file>
+<file>images/refresh-32.png</file>
+<file>images/stop-24.png</file>
+<file>images/stop-32.png</file>
+<file>images/media-stop-16.png</file>
+<file>images/media-stop-32.png</file>
+<file>images/media-play-16.png</file>
+<file>images/media-play-32.png</file>
+<file>images/media-pause-16.png</file>
+<file>images/media-pause-32.png</file>
+<file>images/media-seek-forward-16.png</file>
+<file>images/media-seek-forward-32.png</file>
+<file>images/media-seek-backward-16.png</file>
+<file>images/media-seek-backward-32.png</file>
+<file>images/media-skip-forward-16.png</file>
+<file>images/media-skip-forward-32.png</file>
+<file>images/media-skip-backward-16.png</file>
+<file>images/media-skip-backward-32.png</file>
+<file>images/media-volume-16.png</file>
+<file>images/media-volume-muted-16.png</file>
+</qresource>
+</RCC>
diff --git a/src/gui/styles/qstylefactory.cpp b/src/gui/styles/qstylefactory.cpp
new file mode 100644
index 0000000000..70717dbb8c
--- /dev/null
+++ b/src/gui/styles/qstylefactory.cpp
@@ -0,0 +1,259 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qstylefactory.h"
+#include "qstyleplugin.h"
+#include "private/qfactoryloader_p.h"
+#include "qmutex.h"
+
+#include "qapplication.h"
+#include "qwindowsstyle.h"
+#include "qmotifstyle.h"
+#include "qcdestyle.h"
+#ifndef QT_NO_STYLE_PLASTIQUE
+#include "qplastiquestyle.h"
+#endif
+#ifndef QT_NO_STYLE_CLEANLOOKS
+#include "qcleanlooksstyle.h"
+#endif
+#ifndef QT_NO_STYLE_GTK
+#include "qgtkstyle.h"
+#endif
+#ifndef QT_NO_STYLE_WINDOWSXP
+#include "qwindowsxpstyle.h"
+#endif
+#ifndef QT_NO_STYLE_WINDOWSVISTA
+#include "qwindowsvistastyle.h"
+#endif
+#ifndef QT_NO_STYLE_WINDOWSCE
+#include "qwindowscestyle.h"
+#endif
+#ifndef QT_NO_STYLE_WINDOWSMOBILE
+#include "qwindowsmobilestyle.h"
+#endif
+
+QT_BEGIN_NAMESPACE
+
+#if !defined(QT_NO_STYLE_MAC) && defined(Q_WS_MAC)
+QT_BEGIN_INCLUDE_NAMESPACE
+# include "qmacstyle_mac.h"
+QT_END_INCLUDE_NAMESPACE
+#endif
+
+#if !defined(QT_NO_LIBRARY) && !defined(QT_NO_SETTINGS)
+Q_GLOBAL_STATIC_WITH_ARGS(QFactoryLoader, loader,
+ (QStyleFactoryInterface_iid, QLatin1String("/styles"), Qt::CaseInsensitive))
+#endif
+
+/*!
+ \class QStyleFactory
+ \brief The QStyleFactory class creates QStyle objects.
+
+ \ingroup appearance
+
+ The QStyle class is an abstract base class that encapsulates the
+ look and feel of a GUI. QStyleFactory creates a QStyle object
+ using the create() function and a key identifying the style. The
+ styles are either built-in or dynamically loaded from a style
+ plugin (see QStylePlugin).
+
+ The valid keys can be retrieved using the keys()
+ function. Typically they include "windows", "motif", "cde",
+ "plastique" and "cleanlooks". Depending on the platform,
+ "windowsxp", "windowsvista" and "macintosh" may be available.
+ Note that keys are case insensitive.
+
+ \sa QStyle
+*/
+
+/*!
+ Creates and returns a QStyle object that matches the given \a key, or
+ returns 0 if no matching style is found.
+
+ Both built-in styles and styles from style plugins are queried for a
+ matching style.
+
+ \note The keys used are case insensitive.
+
+ \sa keys()
+*/
+QStyle *QStyleFactory::create(const QString& key)
+{
+ QStyle *ret = 0;
+ QString style = key.toLower();
+#ifndef QT_NO_STYLE_WINDOWS
+ if (style == QLatin1String("windows"))
+ ret = new QWindowsStyle;
+ else
+#endif
+#ifndef QT_NO_STYLE_WINDOWSCE
+ if (style == QLatin1String("windowsce"))
+ ret = new QWindowsCEStyle;
+ else
+#endif
+#ifndef QT_NO_STYLE_WINDOWSMOBILE
+ if (style == QLatin1String("windowsmobile"))
+ ret = new QWindowsMobileStyle;
+ else
+#endif
+#ifndef QT_NO_STYLE_WINDOWSXP
+ if (style == QLatin1String("windowsxp"))
+ ret = new QWindowsXPStyle;
+ else
+#endif
+#ifndef QT_NO_STYLE_WINDOWSVISTA
+ if (style == QLatin1String("windowsvista"))
+ ret = new QWindowsVistaStyle;
+ else
+#endif
+#ifndef QT_NO_STYLE_MOTIF
+ if (style == QLatin1String("motif"))
+ ret = new QMotifStyle;
+ else
+#endif
+#ifndef QT_NO_STYLE_CDE
+ if (style == QLatin1String("cde"))
+ ret = new QCDEStyle;
+ else
+#endif
+#ifndef QT_NO_STYLE_PLASTIQUE
+ if (style == QLatin1String("plastique"))
+ ret = new QPlastiqueStyle;
+ else
+#endif
+#ifndef QT_NO_STYLE_CLEANLOOKS
+ if (style == QLatin1String("cleanlooks"))
+ ret = new QCleanlooksStyle;
+ else
+#endif
+#ifndef QT_NO_STYLE_GTK
+ if (style == QLatin1String("gtk") || style == QLatin1String("gtk+"))
+ ret = new QGtkStyle;
+ else
+#endif
+#ifndef QT_NO_STYLE_MAC
+ if (style.left(9) == QLatin1String("macintosh")) {
+ ret = new QMacStyle;
+# ifdef Q_WS_MAC
+ if (style == QLatin1String("macintosh"))
+ style += QLatin1String(" (aqua)");
+# endif
+ } else
+#endif
+ { } // Keep these here - they make the #ifdefery above work
+#if !defined(QT_NO_LIBRARY) && !defined(QT_NO_SETTINGS)
+ if(!ret) {
+ if (QStyleFactoryInterface *factory = qobject_cast<QStyleFactoryInterface*>(loader()->instance(style)))
+ ret = factory->create(style);
+ }
+#endif
+ if(ret)
+ ret->setObjectName(style);
+ return ret;
+}
+
+/*!
+ Returns the list of valid keys, i.e. the keys this factory can
+ create styles for.
+
+ \sa create()
+*/
+QStringList QStyleFactory::keys()
+{
+#if !defined(QT_NO_LIBRARY) && !defined(QT_NO_SETTINGS)
+ QStringList list = loader()->keys();
+#else
+ QStringList list;
+#endif
+#ifndef QT_NO_STYLE_WINDOWS
+ if (!list.contains(QLatin1String("Windows")))
+ list << QLatin1String("Windows");
+#endif
+#ifndef QT_NO_STYLE_WINDOWSCE
+ if (!list.contains(QLatin1String("WindowsCE")))
+ list << QLatin1String("WindowsCE");
+#endif
+#ifndef QT_NO_STYLE_WINDOWSMOBILE
+ if (!list.contains(QLatin1String("WindowsMobile")))
+ list << QLatin1String("WindowsMobile");
+#endif
+#ifndef QT_NO_STYLE_WINDOWSXP
+ if (!list.contains(QLatin1String("WindowsXP")) &&
+ (QSysInfo::WindowsVersion >= QSysInfo::WV_XP && QSysInfo::WindowsVersion < QSysInfo::WV_NT_based))
+ list << QLatin1String("WindowsXP");
+#endif
+#ifndef QT_NO_STYLE_WINDOWSVISTA
+ if (!list.contains(QLatin1String("WindowsVista")) &&
+ (QSysInfo::WindowsVersion >= QSysInfo::WV_VISTA && QSysInfo::WindowsVersion < QSysInfo::WV_NT_based))
+ list << QLatin1String("WindowsVista");
+#endif
+#ifndef QT_NO_STYLE_MOTIF
+ if (!list.contains(QLatin1String("Motif")))
+ list << QLatin1String("Motif");
+#endif
+#ifndef QT_NO_STYLE_CDE
+ if (!list.contains(QLatin1String("CDE")))
+ list << QLatin1String("CDE");
+#endif
+#ifndef QT_NO_STYLE_PLASTIQUE
+ if (!list.contains(QLatin1String("Plastique")))
+ list << QLatin1String("Plastique");
+#endif
+#ifndef QT_NO_STYLE_GTK
+ if (!list.contains(QLatin1String("GTK+")))
+ list << QLatin1String("GTK+");
+#endif
+#ifndef QT_NO_STYLE_CLEANLOOKS
+ if (!list.contains(QLatin1String("Cleanlooks")))
+ list << QLatin1String("Cleanlooks");
+#endif
+#ifndef QT_NO_STYLE_MAC
+ QString mstyle = QLatin1String("Macintosh");
+# ifdef Q_WS_MAC
+ mstyle += QLatin1String(" (aqua)");
+# endif
+ if (!list.contains(mstyle))
+ list << mstyle;
+#endif
+ return list;
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/styles/qstylefactory.h b/src/gui/styles/qstylefactory.h
new file mode 100644
index 0000000000..c082cbadc2
--- /dev/null
+++ b/src/gui/styles/qstylefactory.h
@@ -0,0 +1,66 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSTYLEFACTORY_H
+#define QSTYLEFACTORY_H
+
+#include <QtCore/qstringlist.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+class QStyle;
+
+class Q_GUI_EXPORT QStyleFactory
+{
+public:
+ static QStringList keys();
+ static QStyle *create(const QString&);
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QSTYLEFACTORY_H
diff --git a/src/gui/styles/qstyleoption.cpp b/src/gui/styles/qstyleoption.cpp
new file mode 100644
index 0000000000..ce053aef31
--- /dev/null
+++ b/src/gui/styles/qstyleoption.cpp
@@ -0,0 +1,5353 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qstyleoption.h"
+#include "qapplication.h"
+#ifdef Q_WS_MAC
+# include "private/qt_mac_p.h"
+# include "qmacstyle_mac.h"
+#endif
+#ifndef QT_NO_DEBUG
+#include <qdebug.h>
+#endif
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QStyleOption
+ \brief The QStyleOption class stores the parameters used by QStyle functions.
+
+ \ingroup appearance
+
+ QStyleOption and its subclasses contain all the information that
+ QStyle functions need to draw a graphical element.
+
+ For performance reasons, there are few member functions and the
+ access to the member variables is direct (i.e., using the \c . or
+ \c -> operator). This low-level feel makes the structures
+ straightforward to use and emphasizes that these are simply
+ parameters used by the style functions.
+
+ The caller of a QStyle function usually creates QStyleOption
+ objects on the stack. This combined with Qt's extensive use of
+ \l{implicit sharing} for types such as QString, QPalette, and
+ QColor ensures that no memory allocation needlessly takes place.
+
+ The following code snippet shows how to use a specific
+ QStyleOption subclass to paint a push button:
+
+ \snippet doc/src/snippets/qstyleoption/main.cpp 0
+
+ In our example, the control is a QStyle::CE_PushButton, and
+ according to the QStyle::drawControl() documentation the
+ corresponding class is QStyleOptionButton.
+
+ When reimplementing QStyle functions that take a QStyleOption
+ parameter, you often need to cast the QStyleOption to a subclass.
+ For safety, you can use qstyleoption_cast() to ensure that the
+ pointer type is correct. For example:
+
+ \snippet doc/src/snippets/qstyleoption/main.cpp 4
+
+ The qstyleoption_cast() function will return 0 if the object to
+ which \c option points is not of the correct type.
+
+ For an example demonstrating how style options can be used, see
+ the \l {widgets/styles}{Styles} example.
+
+ \sa QStyle, QStylePainter
+*/
+
+/*!
+ \enum QStyleOption::OptionType
+
+ This enum is used internally by QStyleOption, its subclasses, and
+ qstyleoption_cast() to determine the type of style option. In
+ general you do not need to worry about this unless you want to
+ create your own QStyleOption subclass and your own styles.
+
+ \value SO_Button \l QStyleOptionButton
+ \value SO_ComboBox \l QStyleOptionComboBox
+ \value SO_Complex \l QStyleOptionComplex
+ \value SO_Default QStyleOption
+ \value SO_DockWidget \l QStyleOptionDockWidget
+ \value SO_FocusRect \l QStyleOptionFocusRect
+ \value SO_Frame \l QStyleOptionFrame \l QStyleOptionFrameV2
+ \value SO_GraphicsItem \l QStyleOptionGraphicsItem
+ \value SO_GroupBox \l QStyleOptionGroupBox
+ \value SO_Header \l QStyleOptionHeader
+ \value SO_MenuItem \l QStyleOptionMenuItem
+ \value SO_ProgressBar \l QStyleOptionProgressBar \l QStyleOptionProgressBarV2
+ \value SO_RubberBand \l QStyleOptionRubberBand
+ \value SO_SizeGrip \l QStyleOptionSizeGrip
+ \value SO_Slider \l QStyleOptionSlider
+ \value SO_SpinBox \l QStyleOptionSpinBox
+ \value SO_Tab \l QStyleOptionTab
+ \value SO_TabBarBase \l QStyleOptionTabBarBase
+ \value SO_TabWidgetFrame \l QStyleOptionTabWidgetFrame
+ \value SO_TitleBar \l QStyleOptionTitleBar
+ \value SO_ToolBar \l QStyleOptionToolBar
+ \value SO_ToolBox \l QStyleOptionToolBox
+ \value SO_ToolButton \l QStyleOptionToolButton
+ \value SO_ViewItem \l QStyleOptionViewItem (used in Interviews)
+
+ The following values are used for custom controls:
+
+ \value SO_CustomBase Reserved for custom QStyleOptions;
+ all custom controls values must be above this value
+ \value SO_ComplexCustomBase Reserved for custom QStyleOptions;
+ all custom complex controls values must be above this value
+
+ Some style options are defined for various Qt3Support controls:
+
+ \value SO_Q3DockWindow \l QStyleOptionQ3DockWindow
+ \value SO_Q3ListView \l QStyleOptionQ3ListView
+ \value SO_Q3ListViewItem \l QStyleOptionQ3ListViewItem
+
+ \sa type
+*/
+
+/*!
+ Constructs a QStyleOption with the specified \a version and \a
+ type.
+
+ The version has no special meaning for QStyleOption; it can be
+ used by subclasses to distinguish between different version of
+ the same option type.
+
+ The \l state member variable is initialized to
+ QStyle::State_None.
+
+ \sa version, type
+*/
+
+QStyleOption::QStyleOption(int version, int type)
+ : version(version), type(type), state(QStyle::State_None),
+ direction(QApplication::layoutDirection()), fontMetrics(QFont())
+{
+}
+
+
+/*!
+ Destroys this style option object.
+*/
+QStyleOption::~QStyleOption()
+{
+}
+
+/*!
+ \fn void QStyleOption::initFrom(const QWidget *widget)
+ \since 4.1
+
+ Initializes the \l state, \l direction, \l rect, \l palette, and
+ \l fontMetrics member variables based on the specified \a widget.
+
+ This is a convenience function; the member variables can also be
+ initialized manually.
+
+ \sa QWidget::layoutDirection(), QWidget::rect(),
+ QWidget::palette(), QWidget::fontMetrics()
+*/
+
+/*!
+ \obsolete
+
+ Use initFrom(\a widget) instead.
+*/
+void QStyleOption::init(const QWidget *widget)
+{
+ QWidget *window = widget->window();
+ state = QStyle::State_None;
+ if (widget->isEnabled())
+ state |= QStyle::State_Enabled;
+ if (widget->hasFocus())
+ state |= QStyle::State_HasFocus;
+ if (window->testAttribute(Qt::WA_KeyboardFocusChange))
+ state |= QStyle::State_KeyboardFocusChange;
+ if (widget->underMouse())
+ state |= QStyle::State_MouseOver;
+ if (window->isActiveWindow())
+ state |= QStyle::State_Active;
+ if (widget->isWindow())
+ state |= QStyle::State_Window;
+#ifdef Q_WS_MAC
+ extern bool qt_mac_can_clickThrough(const QWidget *w); //qwidget_mac.cpp
+ if (!(state & QStyle::State_Active) && !qt_mac_can_clickThrough(widget))
+ state &= ~QStyle::State_Enabled;
+
+ switch (QMacStyle::widgetSizePolicy(widget)) {
+ case QMacStyle::SizeSmall:
+ state |= QStyle::State_Small;
+ break;
+ case QMacStyle::SizeMini:
+ state |= QStyle::State_Mini;
+ break;
+ default:
+ ;
+ }
+#endif
+#ifdef QT_KEYPAD_NAVIGATION
+ if (widget->hasEditFocus())
+ state |= QStyle::State_HasEditFocus;
+#endif
+
+ direction = widget->layoutDirection();
+ rect = widget->rect();
+ palette = widget->palette();
+ fontMetrics = widget->fontMetrics();
+}
+
+/*!
+ Constructs a copy of \a other.
+*/
+QStyleOption::QStyleOption(const QStyleOption &other)
+ : version(Version), type(Type), state(other.state),
+ direction(other.direction), rect(other.rect), fontMetrics(other.fontMetrics),
+ palette(other.palette)
+{
+}
+
+/*!
+ Assign \a other to this QStyleOption.
+*/
+QStyleOption &QStyleOption::operator=(const QStyleOption &other)
+{
+ state = other.state;
+ direction = other.direction;
+ rect = other.rect;
+ fontMetrics = other.fontMetrics;
+ palette = other.palette;
+ return *this;
+}
+
+/*!
+ \enum QStyleOption::StyleOptionType
+
+ This enum is used to hold information about the type of the style option, and
+ is defined for each QStyleOption subclass.
+
+ \value Type The type of style option provided (\l{SO_Default} for
+ this class).
+
+ The type is used internally by QStyleOption, its subclasses, and
+ qstyleoption_cast() to determine the type of style option. In
+ general you do not need to worry about this unless you want to
+ create your own QStyleOption subclass and your own styles.
+
+ \sa StyleOptionVersion
+*/
+
+/*!
+ \enum QStyleOption::StyleOptionVersion
+
+ This enum is used to hold information about the version of the style option, and
+ is defined for each QStyleOption subclass.
+
+ \value Version 1
+
+ The version is used by QStyleOption subclasses to implement
+ extensions without breaking compatibility. If you use
+ qstyleoption_cast(), you normally do not need to check it.
+
+ \sa StyleOptionType
+*/
+
+/*!
+ \variable QStyleOption::palette
+ \brief the palette that should be used when painting the control
+
+ By default, the application's default palette is used.
+
+ \sa initFrom()
+*/
+
+/*!
+ \variable QStyleOption::direction
+ \brief the text layout direction that should be used when drawing text in the control
+
+ By default, the layout direction is Qt::LeftToRight.
+
+ \sa initFrom()
+*/
+
+/*!
+ \variable QStyleOption::fontMetrics
+ \brief the font metrics that should be used when drawing text in the control
+
+ By default, the application's default font is used.
+
+ \sa initFrom()
+*/
+
+/*!
+ \variable QStyleOption::rect
+ \brief the area that should be used for various calculations and painting
+
+ This can have different meanings for different types of elements.
+ For example, for a \l QStyle::CE_PushButton element it would be
+ the rectangle for the entire button, while for a \l
+ QStyle::CE_PushButtonLabel element it would be just the area for
+ the push button label.
+
+ The default value is a null rectangle, i.e. a rectangle with both
+ the width and the height set to 0.
+
+ \sa initFrom()
+*/
+
+/*!
+ \variable QStyleOption::state
+ \brief the style flags that are used when drawing the control
+
+ The default value is QStyle::State_None.
+
+ \sa initFrom(), QStyle::drawPrimitive(), QStyle::drawControl(),
+ QStyle::drawComplexControl(), QStyle::State
+*/
+
+/*!
+ \variable QStyleOption::type
+ \brief the option type of the style option
+
+ The default value is SO_Default.
+
+ \sa OptionType
+*/
+
+/*!
+ \variable QStyleOption::version
+ \brief the version of the style option
+
+ This value can be used by subclasses to implement extensions
+ without breaking compatibility. If you use the qstyleoption_cast()
+ function, you normally do not need to check it.
+
+ The default value is 1.
+*/
+
+/*!
+ \class QStyleOptionFocusRect
+ \brief The QStyleOptionFocusRect class is used to describe the
+ parameters for drawing a focus rectangle with QStyle.
+
+ For performance reasons, the access to the member variables is
+ direct (i.e., using the \c . or \c -> operator). This low-level feel
+ makes the structures straightforward to use and emphasizes that
+ these are simply parameters used by the style functions.
+
+ For an example demonstrating how style options can be used, see
+ the \l {widgets/styles}{Styles} example.
+
+ \sa QStyleOption
+*/
+
+/*!
+ Constructs a QStyleOptionFocusRect, initializing the members
+ variables to their default values.
+*/
+
+QStyleOptionFocusRect::QStyleOptionFocusRect()
+ : QStyleOption(Version, SO_FocusRect)
+{
+ state |= QStyle::State_KeyboardFocusChange; // assume we had one, will be corrected in initFrom()
+}
+
+/*!
+ \internal
+*/
+QStyleOptionFocusRect::QStyleOptionFocusRect(int version)
+ : QStyleOption(version, SO_FocusRect)
+{
+ state |= QStyle::State_KeyboardFocusChange; // assume we had one, will be corrected in initFrom()
+}
+
+/*!
+ \enum QStyleOptionFocusRect::StyleOptionType
+
+ This enum is used to hold information about the type of the style option, and
+ is defined for each QStyleOption subclass.
+
+ \value Type The type of style option provided (\l{SO_FocusRect} for this class).
+
+ The type is used internally by QStyleOption, its subclasses, and
+ qstyleoption_cast() to determine the type of style option. In
+ general you do not need to worry about this unless you want to
+ create your own QStyleOption subclass and your own styles.
+
+ \sa StyleOptionVersion
+*/
+
+/*!
+ \enum QStyleOptionFocusRect::StyleOptionVersion
+
+ This enum is used to hold information about the version of the style option, and
+ is defined for each QStyleOption subclass.
+
+ \value Version 1
+
+ The version is used by QStyleOption subclasses to implement
+ extensions without breaking compatibility. If you use
+ qstyleoption_cast(), you normally do not need to check it.
+
+ \sa StyleOptionType
+*/
+
+/*!
+ \fn QStyleOptionFocusRect::QStyleOptionFocusRect(const QStyleOptionFocusRect &other)
+
+ Constructs a copy of the \a other style option.
+*/
+
+/*!
+ \variable QStyleOptionFocusRect::backgroundColor
+ \brief the background color on which the focus rectangle is being drawn
+
+ The default value is an invalid color with the RGB value (0, 0,
+ 0). An invalid color is a color that is not properly set up for
+ the underlying window system.
+*/
+
+/*!
+ \class QStyleOptionFrame
+ \brief The QStyleOptionFrame class is used to describe the
+ parameters for drawing a frame.
+
+ QStyleOptionFrame is used for drawing several built-in Qt widgets,
+ including QFrame, QGroupBox, QLineEdit, and QMenu. Note that to
+ describe the parameters necessary for drawing a frame in Qt 4.1 or
+ above, you must use the QStyleOptionFrameV2 subclass.
+
+ An instance of the QStyleOptionFrame class has
+ \l{QStyleOption::type} {type} SO_Frame and \l{QStyleOption::version}
+ {version} 1.
+
+ The type is used internally by QStyleOption, its subclasses, and
+ qstyleoption_cast() to determine the type of style option. In
+ general you do not need to worry about this unless you want to
+ create your own QStyleOption subclass and your own styles. The
+ version is used by QStyleOption subclasses to implement extensions
+ without breaking compatibility. If you use qstyleoption_cast(),
+ you normally do not need to check it.
+
+ If you create your own QStyle subclass, you should handle both
+ QStyleOptionFrame and QStyleOptionFrameV2.
+
+ For an example demonstrating how style options can be used, see
+ the \l {widgets/styles}{Styles} example.
+
+ \sa QStyleOptionFrameV2, QStyleOption
+*/
+
+/*!
+ Constructs a QStyleOptionFrame, initializing the members
+ variables to their default values.
+*/
+
+QStyleOptionFrame::QStyleOptionFrame()
+ : QStyleOption(Version, SO_Frame), lineWidth(0), midLineWidth(0)
+{
+}
+
+/*!
+ \internal
+*/
+QStyleOptionFrame::QStyleOptionFrame(int version)
+ : QStyleOption(version, SO_Frame), lineWidth(0), midLineWidth(0)
+{
+}
+
+/*!
+ \fn QStyleOptionFrame::QStyleOptionFrame(const QStyleOptionFrame &other)
+
+ Constructs a copy of the \a other style option.
+*/
+
+/*!
+ \enum QStyleOptionFrame::StyleOptionType
+
+ This enum is used to hold information about the type of the style option, and
+ is defined for each QStyleOption subclass.
+
+ \value Type The type of style option provided (\l{SO_Frame} for this class).
+
+ The type is used internally by QStyleOption, its subclasses, and
+ qstyleoption_cast() to determine the type of style option. In
+ general you do not need to worry about this unless you want to
+ create your own QStyleOption subclass and your own styles.
+
+ \sa StyleOptionVersion
+*/
+
+/*!
+ \enum QStyleOptionFrame::StyleOptionVersion
+
+ This enum is used to hold information about the version of the style option, and
+ is defined for each QStyleOption subclass.
+
+ \value Version 1
+
+ The version is used by QStyleOption subclasses to implement
+ extensions without breaking compatibility. If you use
+ qstyleoption_cast(), you normally do not need to check it.
+
+ \sa StyleOptionType
+*/
+
+/*!
+ \variable QStyleOptionFrame::lineWidth
+ \brief the line width for drawing the frame
+
+ The default value is 0.
+
+ \sa QFrame::lineWidth
+*/
+
+/*!
+ \variable QStyleOptionFrame::midLineWidth
+ \brief the mid-line width for drawing the frame
+
+ This is usually used in drawing sunken or raised frames.
+
+ The default value is 0.
+
+ \sa QFrame::midLineWidth
+*/
+
+/*!
+ \class QStyleOptionFrameV2
+ \brief The QStyleOptionFrameV2 class is used to describe the
+ parameters necessary for drawing a frame in Qt 4.1 or above.
+
+ \since 4.1
+
+ QStyleOptionFrameV2 inherits QStyleOptionFrame which is used for
+ drawing several built-in Qt widgets, including QFrame, QGroupBox,
+ QLineEdit, and QMenu.
+
+ An instance of the QStyleOptionFrameV2 class has
+ \l{QStyleOption::type} {type} SO_Frame and
+ \l{QStyleOption::version} {version} 2. The type is used
+ internally by QStyleOption, its subclasses, and
+ qstyleoption_cast() to determine the type of style option. In
+ general you do not need to worry about this unless you want to
+ create your own QStyleOption subclass and your own styles. The
+ version is used by QStyleOption subclasses to implement extensions
+ without breaking compatibility. If you use qstyleoption_cast(),
+ you normally do not need to check it.
+
+ If you create your own QStyle subclass, you should handle both
+ QStyleOptionFrame and QStyleOptionFrameV2. One way to achieve this
+ is to use the QStyleOptionFrameV2 copy constructor. For example:
+
+ \snippet doc/src/snippets/qstyleoption/main.cpp 1
+
+ In the example above: If the \c frameOption's version is 1, \l
+ FrameFeature is set to \l None for \c frameOptionV2. If \c
+ frameOption's version is 2, the constructor will simply copy the
+ \c frameOption's \l FrameFeature value.
+
+ For an example demonstrating how style options can be used, see
+ the \l {widgets/styles}{Styles} example.
+
+ \sa QStyleOptionFrame, QStyleOption
+*/
+
+/*!
+ Constructs a QStyleOptionFrameV2 object.
+*/
+QStyleOptionFrameV2::QStyleOptionFrameV2()
+ : QStyleOptionFrame(Version), features(None)
+{
+}
+
+/*!
+ \fn QStyleOptionFrameV2::QStyleOptionFrameV2(const QStyleOptionFrameV2 &other)
+
+ Constructs a QStyleOptionFrameV2 copy of the \a other style option.
+*/
+
+/*!
+ \internal
+*/
+QStyleOptionFrameV2::QStyleOptionFrameV2(int version)
+ : QStyleOptionFrame(version), features(None)
+{
+}
+
+/*!
+ Constructs a QStyleOptionFrameV2 copy of the \a other style option
+ which can be either of the QStyleOptionFrameV2 or
+ QStyleOptionFrame types.
+
+ If the \a other style option's version is 1, the new style option's \l
+ FrameFeature value is set to \l QStyleOptionFrameV2::None. If its
+ version is 2, its \l FrameFeature value is simply copied to the
+ new style option.
+
+ \sa version
+*/
+QStyleOptionFrameV2::QStyleOptionFrameV2(const QStyleOptionFrame &other)
+{
+ QStyleOptionFrame::operator=(other);
+
+ const QStyleOptionFrameV2 *f2 = qstyleoption_cast<const QStyleOptionFrameV2 *>(&other);
+ features = f2 ? f2->features : FrameFeatures(QStyleOptionFrameV2::None);
+ version = Version;
+}
+
+/*!
+ Assigns the \a other style option to this style option. The \a
+ other style option can be either of the QStyleOptionFrameV2 or
+ QStyleOptionFrame types.
+
+ If the \a{other} style option's version is 1, this style option's
+ \l FrameFeature value is set to \l QStyleOptionFrameV2::None. If
+ its version is 2, its \l FrameFeature value is simply copied to
+ this style option.
+*/
+QStyleOptionFrameV2 &QStyleOptionFrameV2::operator=(const QStyleOptionFrame &other)
+{
+ QStyleOptionFrame::operator=(other);
+
+ const QStyleOptionFrameV2 *f2 = qstyleoption_cast<const QStyleOptionFrameV2 *>(&other);
+ features = f2 ? f2->features : FrameFeatures(QStyleOptionFrameV2::None);
+ version = Version;
+ return *this;
+}
+
+/*!
+ \enum QStyleOptionFrameV2::FrameFeature
+
+ This enum describles the different types of features a frame can have.
+
+ \value None Indicates a normal frame.
+ \value Flat Indicates a flat frame.
+*/
+
+/*!
+ \variable QStyleOptionFrameV2::features
+ \brief a bitwise OR of the features that describe this frame.
+
+ \sa FrameFeature
+*/
+
+/*!
+ \enum QStyleOptionFrameV2::StyleOptionVersion
+
+ This enum is used to hold information about the version of the
+ style option, and is defined for each QStyleOption subclass.
+
+ \value Version 2
+
+ The version is used by QStyleOption subclasses to implement
+ extensions without breaking compatibility. If you use
+ qstyleoption_cast(), you normally do not need to check it.
+
+ \sa StyleOptionType
+*/
+
+/*!
+ \class QStyleOptionFrameV3
+ \brief The QStyleOptionFrameV3 class is used to describe the
+ parameters necessary for drawing a frame in Qt 4.1 or above.
+
+ \since 4.5
+
+ QStyleOptionFrameV3 inherits QStyleOptionFrameV2
+
+ An instance of the QStyleOptionFrameV3 class has
+ \l{QStyleOption::type} {type} SO_Frame and
+ \l{QStyleOption::version} {version} 3. The type is used
+ internally by QStyleOption, its subclasses, and
+ qstyleoption_cast() to determine the type of style option. In
+ general you do not need to worry about this unless you want to
+ create your own QStyleOption subclass and your own styles. The
+ version is used by QStyleOption subclasses to implement extensions
+ without breaking compatibility. If you use qstyleoption_cast(),
+ you normally do not need to check it.
+
+ \sa QStyleOptionFrameV2, QStyleOption
+*/
+
+/*!
+ Constructs a QStyleOptionFrameV3 object.
+*/
+QStyleOptionFrameV3::QStyleOptionFrameV3()
+ : QStyleOptionFrameV2(Version), frameShape(QFrame::NoFrame)
+{
+}
+
+/*!
+ \fn QStyleOptionFrameV3::QStyleOptionFrameV3(const QStyleOptionFrameV3 &other)
+
+ Constructs a QStyleOptionFrameV3 copy of the \a other style option.
+*/
+
+/*!
+ \internal
+*/
+QStyleOptionFrameV3::QStyleOptionFrameV3(int version)
+ : QStyleOptionFrameV2(version), frameShape(QFrame::NoFrame)
+{
+}
+
+/*!
+ Constructs a QStyleOptionFrameV3 copy of the \a other style option
+ which can be either of the QStyleOptionFrameV3 or
+ QStyleOptionFrame types.
+
+ If the \a other style option's version is 1, the new style
+ option's \l FrameFeature value is set to
+ \l{QStyleOptionFrameV2::None}. If its version is 2 or lower,
+ \l{QStyleOptionFrameV3::frameShape} value is QFrame::NoFrame
+
+ \sa version
+*/
+QStyleOptionFrameV3::QStyleOptionFrameV3(const QStyleOptionFrame &other)
+{
+ operator=(other);
+}
+
+/*!
+ Assigns the \a other style option to this style option. The \a
+ other style option can be either of the QStyleOptionFrameV3,
+ QStyleOptionFrameV2 or QStyleOptionFrame types.
+
+ If the \a other style option's version is 1, the new style
+ option's \l FrameFeature value is set to
+ \l{QStyleOptionFrameV2::None}. If its version is 2 or lower,
+ \l{QStyleOptionFrameV3::frameShape} value is QFrame::NoFrame
+*/
+QStyleOptionFrameV3 &QStyleOptionFrameV3::operator=(const QStyleOptionFrame &other)
+{
+ QStyleOptionFrameV2::operator=(other);
+
+ const QStyleOptionFrameV3 *f3 = qstyleoption_cast<const QStyleOptionFrameV3 *>(&other);
+ frameShape = f3 ? f3->frameShape : QFrame::NoFrame;
+ version = Version;
+ return *this;
+}
+
+
+/*!
+ \variable QStyleOptionFrameV3::frameShape
+ \brief This property holds the frame shape value of the frame.
+
+ \sa QFrame::frameShape
+*/
+
+/*!
+ \enum QStyleOptionFrameV3::StyleOptionVersion
+
+ This enum is used to hold information about the version of the style option, and
+ is defined for each QStyleOption subclass.
+
+ \value Version 3
+
+ The version is used by QStyleOption subclasses to implement
+ extensions without breaking compatibility. If you use
+ qstyleoption_cast(), you normally do not need to check it.
+
+ \sa StyleOptionType
+*/
+
+/*!
+ \class QStyleOptionViewItemV2
+ \brief The QStyleOptionViewItemV2 class is used to describe the
+ parameters necessary for drawing a frame in Qt 4.2 or above.
+ \since 4.2
+
+ QStyleOptionViewItemV2 inherits QStyleOptionViewItem.
+
+ An instance of the QStyleOptionViewItemV2 class has
+ \l{QStyleOption::type} {type} SO_ViewItem and
+ \l{QStyleOption::version} {version} 2. The type is used internally
+ by QStyleOption, its subclasses, and qstyleoption_cast() to
+ determine the type of style option. In general you do not need to
+ worry about this unless you want to create your own QStyleOption
+ subclass and your own styles. The version is used by QStyleOption
+ subclasses to implement extensions without breaking
+ compatibility. If you use qstyleoption_cast(), you normally do not
+ need to check it.
+
+ See QStyleOptionFrameV2's detailed description for a discussion
+ of how to handle "V2" classes.
+
+ \sa QStyleOptionViewItem, QStyleOption
+*/
+
+/*!
+ \enum QStyleOptionViewItemV2::StyleOptionVersion
+
+ This enum is used to hold information about the version of the
+ style option, and is defined for each QStyleOption subclass.
+
+ \value Version 2
+
+ The version is used by QStyleOption subclasses to implement
+ extensions without breaking compatibility. If you use
+ qstyleoption_cast(), you normally do not need to check it.
+
+ \sa StyleOptionType
+*/
+
+/*!
+ \variable QStyleOptionViewItemV2::features
+ \brief a bitwise OR of the features that describe this view item
+
+ \sa ViewItemFeature
+*/
+
+/*!
+ Constructs a QStyleOptionViewItemV2 object.
+*/
+QStyleOptionViewItemV2::QStyleOptionViewItemV2()
+ : QStyleOptionViewItem(Version), features(None)
+{
+}
+
+/*!
+ \fn QStyleOptionViewItemV2::QStyleOptionViewItemV2(const QStyleOptionViewItemV2 &other)
+
+ Constructs a copy of \a other.
+*/
+
+/*!
+ Constructs a QStyleOptionViewItemV2 copy of the \a other style option
+ which can be either of the QStyleOptionViewItemV2 or
+ QStyleOptionViewItem types.
+
+ If the \a other style option's version is 1, the new style option's \l
+ ViewItemFeature value is set to \l QStyleOptionViewItemV2::None. If its
+ version is 2, its \l ViewItemFeature value is simply copied to the
+ new style option.
+
+ \sa version
+*/
+QStyleOptionViewItemV2::QStyleOptionViewItemV2(const QStyleOptionViewItem &other)
+ : QStyleOptionViewItem(Version)
+{
+ (void)QStyleOptionViewItemV2::operator=(other);
+}
+
+/*!
+ \internal
+*/
+QStyleOptionViewItemV2::QStyleOptionViewItemV2(int version)
+ : QStyleOptionViewItem(version)
+{
+
+}
+
+/*!
+ Assigns the \a other style option to this style option. The \a
+ other style option can be either of the QStyleOptionViewItemV2 or
+ QStyleOptionViewItem types.
+
+ If the \a{other} style option's version is 1, this style option's
+ \l ViewItemFeature value is set to \l QStyleOptionViewItemV2::None.
+ If its version is 2, its \l ViewItemFeature value is simply copied
+ to this style option.
+*/
+QStyleOptionViewItemV2 &QStyleOptionViewItemV2::operator=(const QStyleOptionViewItem &other)
+{
+ QStyleOptionViewItem::operator=(other);
+ const QStyleOptionViewItemV2 *v2 = qstyleoption_cast<const QStyleOptionViewItemV2 *>(&other);
+ features = v2 ? v2->features : ViewItemFeatures(QStyleOptionViewItemV2::None);
+ return *this;
+}
+
+/*!
+ \enum QStyleOptionViewItemV2::ViewItemFeature
+
+ This enum describles the different types of features an item can have.
+
+ \value None Indicates a normal item.
+ \value WrapText Indicates an item with wrapped text.
+ \value Alternate Indicates that the item's background is rendered using alternateBase.
+ \value HasCheckIndicator Indicates that the item has a check state indicator.
+ \value HasDisplay Indicates that the item has a display role.
+ \value HasDecoration Indicates that the item has a decoration role.
+*/
+
+
+
+/*!
+ \class QStyleOptionViewItemV3
+ \brief The QStyleOptionViewItemV3 class is used to describe the
+ parameters necessary for drawing a frame in Qt 4.3 or above.
+ \since 4.3
+
+ QStyleOptionViewItemV3 inherits QStyleOptionViewItem.
+
+ An instance of the QStyleOptionViewItemV3 class has
+ \l{QStyleOption::type} {type} SO_ViewItem and \l{QStyleOption::version}
+ {version} 3. The type is used internally by QStyleOption, its subclasses,
+ and qstyleoption_cast() to determine the type of style option. In general
+ you do not need to worry about this unless you want to create your own
+ QStyleOption subclass and your own styles. The version is used by
+ QStyleOption subclasses to implement extensions without breaking
+ compatibility. If you use qstyleoption_cast(), you normally do not need to
+ check it.
+
+ See QStyleOptionFrameV2's detailed description for a discussion
+ of how to handle "V2" and other versioned classes.
+
+ \sa QStyleOptionViewItem, QStyleOption
+*/
+
+/*!
+ \enum QStyleOptionViewItemV3::StyleOptionVersion
+
+ This enum is used to hold information about the version of the
+ style option, and is defined for each QStyleOption subclass.
+
+ \value Version 3
+
+ The version is used by QStyleOption subclasses to implement
+ extensions without breaking compatibility. If you use
+ qstyleoption_cast(), you normally do not need to check it.
+
+ \sa StyleOptionType
+*/
+
+/*!
+ Constructs a QStyleOptionViewItemV3 object.
+*/
+QStyleOptionViewItemV3::QStyleOptionViewItemV3()
+ : QStyleOptionViewItemV2(Version)
+{
+}
+
+/*!
+ Constructs a copy of \a other.
+*/
+QStyleOptionViewItemV3::QStyleOptionViewItemV3(const QStyleOptionViewItem &other)
+ : QStyleOptionViewItemV2(Version)
+{
+ (void)QStyleOptionViewItemV3::operator=(other);
+}
+
+/*!
+ \fn QStyleOptionViewItemV3::QStyleOptionViewItemV3(const QStyleOptionViewItemV3 &other)
+
+ Constructs a copy of \a other.
+*/
+
+/*!
+ Assigns the \a other style option to this style option. The \a
+ other style option can be an instance of the QStyleOptionViewItemV2,
+ QStyleOptionViewItemV3 or QStyleOptionViewItem types.
+*/
+QStyleOptionViewItemV3 &QStyleOptionViewItemV3::operator = (const QStyleOptionViewItem &other)
+{
+ QStyleOptionViewItemV2::operator=(other);
+ const QStyleOptionViewItemV3 *v3 = qstyleoption_cast<const QStyleOptionViewItemV3*>(&other);
+ locale = v3 ? v3->locale : QLocale();
+ widget = v3 ? v3->widget : 0;
+ return *this;
+}
+
+/*!
+ \internal
+*/
+QStyleOptionViewItemV3::QStyleOptionViewItemV3(int version)
+ : QStyleOptionViewItemV2(version)
+{
+}
+
+#ifndef QT_NO_ITEMVIEWS
+
+/*!
+ \class QStyleOptionViewItemV4
+ \brief The QStyleOptionViewItemV4 class is used to describe the
+ parameters necessary for drawing a frame in Qt 4.4 or above.
+ \since 4.4
+
+ QStyleOptionViewItemV4 inherits QStyleOptionViewItemV3.
+
+ An instance of the QStyleOptionViewItemV4 class has
+ \l{QStyleOption::type} {type} SO_ViewItem and
+ \l{QStyleOption::version} {version} 4. The type is used internally
+ by QStyleOption, its subclasses, and qstyleoption_cast() to
+ determine the type of style option. In general you do not need to
+ worry about this unless you want to create your own QStyleOption
+ subclass and your own styles. The version is used by QStyleOption
+ subclasses to implement extensions without breaking
+ compatibility. If you use qstyleoption_cast(), you normally do not
+ need to check it.
+
+ See QStyleOptionViewItemV3's detailed description for a discussion
+ of how to handle "V3" classes.
+
+ \sa QStyleOptionViewItem, QStyleOption
+*/
+
+/*!
+ \variable QStyleOptionViewItemV4::index
+
+ The model index that is to be drawn.
+*/
+
+/*!
+ \variable QStyleOptionViewItemV4::checkState
+
+ If this view item is checkable, i.e.,
+ ViewItemFeature::HasCheckIndicator is true, \c checkState is true
+ if the item is checked; otherwise, it is false.
+
+*/
+
+/*!
+ \variable QStyleOptionViewItemV4::icon
+
+ The icon (if any) to be drawn in the view item.
+*/
+
+
+/*!
+ \variable QStyleOptionViewItemV4::text
+
+ The text (if any) to be drawn in the view item.
+*/
+
+/*!
+ \variable QStyleOptionViewItemV4::backgroundBrush
+
+ The QBrush that should be used to paint the view items
+ background.
+*/
+
+/*!
+ \variable QStyleOptionViewItemV4::viewItemPosition
+
+ Gives the position of this view item relative to other items. See
+ the \l{QStyleOptionViewItemV4::}{ViewItemPosition} enum for the
+ details.
+*/
+
+/*!
+ \enum QStyleOptionViewItemV4::StyleOptionVersion
+
+ This enum is used to hold information about the version of the
+ style option, and is defined for each QStyleOption subclass.
+
+ \value Version 4
+
+ The version is used by QStyleOption subclasses to implement
+ extensions without breaking compatibility. If you use
+ qstyleoption_cast(), you normally do not need to check it.
+
+ \sa StyleOptionType
+*/
+
+/*!
+ \enum QStyleOptionViewItemV4::ViewItemPosition
+
+ This enum is used to represent the placement of the item on
+ a row. This can be used to draw items differently depending
+ on their placement, for example by putting rounded edges at
+ the beginning and end, and straight edges in between.
+
+ \value Invalid The ViewItemPosition is unknown and should be
+ disregarded.
+ \value Beginning The item appears at the beginning of the row.
+ \value Middle The item appears in the middle of the row.
+ \value End The item appears at the end of the row.
+ \value OnlyOne The item is the only one on the row, and is
+ therefore both at the beginning and the end.
+*/
+
+
+/*!
+ Constructs a QStyleOptionViewItemV4 object.
+*/
+QStyleOptionViewItemV4::QStyleOptionViewItemV4()
+: QStyleOptionViewItemV3(Version), checkState(Qt::Unchecked), viewItemPosition(QStyleOptionViewItemV4::Invalid)
+{
+}
+
+/*!
+ \fn QStyleOptionViewItemV4::QStyleOptionViewItemV4(const QStyleOptionViewItemV4 &other)
+
+ Constructs a copy of \a other.
+*/
+
+/*!
+ Constructs a QStyleOptionViewItemV4 copy of the \a other style option
+ which can be either of the QStyleOptionViewItemV3 or
+ QStyleOptionViewItem types.
+
+ \sa version
+*/
+QStyleOptionViewItemV4::QStyleOptionViewItemV4(const QStyleOptionViewItem &other)
+ : QStyleOptionViewItemV3(Version)
+{
+ (void)QStyleOptionViewItemV4::operator=(other);
+}
+
+/*!
+ Assigns the \a other style option to this style option. The \a
+ other style option can be either of the QStyleOptionViewItemV3 or
+ QStyleOptionViewItem types.
+*/
+QStyleOptionViewItemV4 &QStyleOptionViewItemV4::operator = (const QStyleOptionViewItem &other)
+{
+ QStyleOptionViewItemV3::operator=(other);
+ if (const QStyleOptionViewItemV4 *v4 = qstyleoption_cast<const QStyleOptionViewItemV4*>(&other)) {
+ index = v4->index;
+ checkState = v4->checkState;
+ text = v4->text;
+ viewItemPosition = v4->viewItemPosition;
+ backgroundBrush = v4->backgroundBrush;
+ icon = v4->icon;
+ } else {
+ viewItemPosition = QStyleOptionViewItemV4::Invalid;
+ checkState = Qt::Unchecked;
+ }
+ return *this;
+}
+
+/*!
+ \internal
+*/
+QStyleOptionViewItemV4::QStyleOptionViewItemV4(int version)
+ : QStyleOptionViewItemV3(version)
+{
+}
+#endif // QT_NO_ITEMVIEWS
+
+/*!
+ \class QStyleOptionGroupBox
+ \brief The QStyleOptionGroupBox class describes the parameters for
+ drawing a group box.
+
+ \since 4.1
+
+ QStyleOptionButton contains all the information that QStyle
+ functions need the various graphical elements of a group box.
+
+ It holds the lineWidth and the midLineWidth for drawing the panel,
+ the group box's \l {text}{title} and the title's \l
+ {textAlignment}{alignment} and \l {textColor}{color}.
+
+ For performance reasons, the access to the member variables is
+ direct (i.e., using the \c . or \c -> operator). This low-level feel
+ makes the structures straightforward to use and emphasizes that
+ these are simply parameters used by the style functions.
+
+ For an example demonstrating how style options can be used, see
+ the \l {widgets/styles}{Styles} example.
+
+ \sa QStyleOption, QStyleOptionComplex, QGroupBox
+*/
+
+/*!
+ \enum QStyleOptionGroupBox::StyleOptionType
+
+ This enum is used to hold information about the type of the style option, and
+ is defined for each QStyleOption subclass.
+
+ \value Type The type of style option provided (\l{SO_GroupBox} for this class).
+
+ The type is used internally by QStyleOption, its subclasses, and
+ qstyleoption_cast() to determine the type of style option. In
+ general you do not need to worry about this unless you want to
+ create your own QStyleOption subclass and your own styles.
+
+ \sa StyleOptionVersion
+*/
+
+/*!
+ \enum QStyleOptionGroupBox::StyleOptionVersion
+
+ This enum is used to hold information about the version of the style option, and
+ is defined for each QStyleOption subclass.
+
+ \value Version 1
+
+ The version is used by QStyleOption subclasses to implement
+ extensions without breaking compatibility. If you use
+ qstyleoption_cast(), you normally do not need to check it.
+
+ \sa StyleOptionType
+*/
+
+/*!
+ \variable QStyleOptionGroupBox::lineWidth
+ \brief the line width for drawing the panel
+
+ The value of this variable is, currently, always 1.
+
+ \sa QFrame::lineWidth
+*/
+
+/*!
+ \variable QStyleOptionGroupBox::midLineWidth
+ \brief the mid-line width for drawing the panel
+
+ The mid-line width is usually used when drawing sunken or raised
+ group box frames. The value of this variable is, currently, always 0.
+
+ \sa QFrame::midLineWidth
+*/
+
+/*!
+ \variable QStyleOptionGroupBox::text
+ \brief the text of the group box
+
+ The default value is an empty string.
+
+ \sa QGroupBox::title
+*/
+
+/*!
+ \variable QStyleOptionGroupBox::textAlignment
+ \brief the alignment of the group box title
+
+ The default value is Qt::AlignLeft.
+
+ \sa QGroupBox::alignment
+*/
+
+/*!
+ \variable QStyleOptionGroupBox::features
+ \brief the features of the group box frame
+
+ The frame is flat by default.
+
+ \sa QStyleOptionFrameV2::FrameFeature
+*/
+
+/*!
+ \variable QStyleOptionGroupBox::textColor
+ \brief the color of the group box title
+
+ The default value is an invalid color with the RGB value (0, 0,
+ 0). An invalid color is a color that is not properly set up for
+ the underlying window system.
+*/
+
+/*!
+ Constructs a QStyleOptionGroupBox, initializing the members
+ variables to their default values.
+*/
+QStyleOptionGroupBox::QStyleOptionGroupBox()
+ : QStyleOptionComplex(Version, Type), features(QStyleOptionFrameV2::None),
+ textAlignment(Qt::AlignLeft), lineWidth(0), midLineWidth(0)
+{
+}
+
+/*!
+ \fn QStyleOptionGroupBox::QStyleOptionGroupBox(const QStyleOptionGroupBox &other)
+
+ Constructs a copy of the \a other style option.
+*/
+
+/*!
+ \internal
+*/
+QStyleOptionGroupBox::QStyleOptionGroupBox(int version)
+ : QStyleOptionComplex(version, Type), features(QStyleOptionFrameV2::None),
+ textAlignment(Qt::AlignLeft), lineWidth(0), midLineWidth(0)
+{
+}
+
+/*!
+ \class QStyleOptionHeader
+ \brief The QStyleOptionHeader class is used to describe the
+ parameters for drawing a header.
+
+ QStyleOptionHeader contains all the information that QStyle
+ functions need to draw the item views' header pane, header sort
+ arrow, and header label.
+
+ For performance reasons, the access to the member variables is
+ direct (i.e., using the \c . or \c -> operator). This low-level feel
+ makes the structures straightforward to use and emphasizes that
+ these are simply parameters used by the style functions.
+
+ For an example demonstrating how style options can be used, see
+ the \l {widgets/styles}{Styles} example.
+
+ \sa QStyleOption
+*/
+
+/*!
+ Constructs a QStyleOptionHeader, initializing the members
+ variables to their default values.
+*/
+
+QStyleOptionHeader::QStyleOptionHeader()
+ : QStyleOption(QStyleOptionHeader::Version, SO_Header),
+ section(0), textAlignment(Qt::AlignLeft), iconAlignment(Qt::AlignLeft),
+ position(QStyleOptionHeader::Beginning),
+ selectedPosition(QStyleOptionHeader::NotAdjacent), sortIndicator(None),
+ orientation(Qt::Horizontal)
+{
+}
+
+/*!
+ \internal
+*/
+QStyleOptionHeader::QStyleOptionHeader(int version)
+ : QStyleOption(version, SO_Header),
+ section(0), textAlignment(Qt::AlignLeft), iconAlignment(Qt::AlignLeft),
+ position(QStyleOptionHeader::Beginning),
+ selectedPosition(QStyleOptionHeader::NotAdjacent), sortIndicator(None),
+ orientation(Qt::Horizontal)
+{
+}
+
+/*!
+ \variable QStyleOptionHeader::orientation
+ \brief the header's orientation (horizontal or vertical)
+
+ The default orientation is Qt::Horizontal
+*/
+
+/*!
+ \fn QStyleOptionHeader::QStyleOptionHeader(const QStyleOptionHeader &other)
+
+ Constructs a copy of the \a other style option.
+*/
+
+/*!
+ \enum QStyleOptionHeader::StyleOptionType
+
+ This enum is used to hold information about the type of the style option, and
+ is defined for each QStyleOption subclass.
+
+ \value Type The type of style option provided (\l{SO_Header} for this class).
+
+ The type is used internally by QStyleOption, its subclasses, and
+ qstyleoption_cast() to determine the type of style option. In
+ general you do not need to worry about this unless you want to
+ create your own QStyleOption subclass and your own styles.
+
+ \sa StyleOptionVersion
+*/
+
+/*!
+ \enum QStyleOptionHeader::StyleOptionVersion
+
+ This enum is used to hold information about the version of the style option, and
+ is defined for each QStyleOption subclass.
+
+ \value Version 1
+
+ The version is used by QStyleOption subclasses to implement
+ extensions without breaking compatibility. If you use
+ qstyleoption_cast(), you normally do not need to check it.
+
+ \sa StyleOptionType
+*/
+
+/*!
+ \variable QStyleOptionHeader::section
+ \brief which section of the header is being painted
+
+ The default value is 0.
+*/
+
+/*!
+ \variable QStyleOptionHeader::text
+ \brief the text of the header
+
+ The default value is an empty string.
+*/
+
+/*!
+ \variable QStyleOptionHeader::textAlignment
+ \brief the alignment flags for the text of the header
+
+ The default value is Qt::AlignLeft.
+*/
+
+/*!
+ \variable QStyleOptionHeader::icon
+ \brief the icon of the header
+
+ The default value is an empty icon, i.e. an icon with neither a
+ pixmap nor a filename.
+*/
+
+/*!
+ \variable QStyleOptionHeader::iconAlignment
+ \brief the alignment flags for the icon of the header
+
+ The default value is Qt::AlignLeft.
+*/
+
+/*!
+ \variable QStyleOptionHeader::position
+ \brief the section's position in relation to the other sections
+
+ The default value is QStyleOptionHeader::Beginning.
+*/
+
+/*!
+ \variable QStyleOptionHeader::selectedPosition
+ \brief the section's position in relation to the selected section
+
+ The default value is QStyleOptionHeader::NotAdjacent
+*/
+
+/*!
+ \variable QStyleOptionHeader::sortIndicator
+ \brief the direction the sort indicator should be drawn
+
+ The default value is QStyleOptionHeader::None.
+*/
+
+/*!
+ \enum QStyleOptionHeader::SectionPosition
+
+ This enum lets you know where the section's position is in relation to the other sections.
+
+ \value Beginning At the beginining of the header
+ \value Middle In the middle of the header
+ \value End At the end of the header
+ \value OnlyOneSection Only one header section
+
+ \sa position
+*/
+
+/*!
+ \enum QStyleOptionHeader::SelectedPosition
+
+ This enum lets you know where the section's position is in relation to the selected section.
+
+ \value NotAdjacent Not adjacent to the selected section
+ \value NextIsSelected The next section is selected
+ \value PreviousIsSelected The previous section is selected
+ \value NextAndPreviousAreSelected Both the next and previous section are selected
+
+ \sa selectedPosition
+*/
+
+/*!
+ \enum QStyleOptionHeader::SortIndicator
+
+ Indicates which direction the sort indicator should be drawn
+
+ \value None No sort indicator is needed
+ \value SortUp Draw an up indicator
+ \value SortDown Draw a down indicator
+
+ \sa sortIndicator
+*/
+
+/*!
+ \class QStyleOptionButton
+ \brief The QStyleOptionButton class is used to describe the
+ parameters for drawing buttons.
+
+ QStyleOptionButton contains all the information that QStyle
+ functions need to draw graphical elements like QPushButton,
+ QCheckBox, and QRadioButton.
+
+ For performance reasons, the access to the member variables is
+ direct (i.e., using the \c . or \c -> operator). This low-level feel
+ makes the structures straightforward to use and emphasizes that
+ these are simply parameters used by the style functions.
+
+ For an example demonstrating how style options can be used, see
+ the \l {widgets/styles}{Styles} example.
+
+ \sa QStyleOption, QStyleOptionToolButton
+*/
+
+/*!
+ \enum QStyleOptionButton::ButtonFeature
+
+ This enum describes the different types of features a push button can have.
+
+ \value None Indicates a normal push button.
+ \value Flat Indicates a flat push button.
+ \value HasMenu Indicates that the button has a drop down menu.
+ \value DefaultButton Indicates that the button is a default button.
+ \value AutoDefaultButton Indicates that the button is an auto default button.
+ \value CommandLinkButton Indicates that the button is a Windows Vista type command link.
+
+ \sa features
+*/
+
+/*!
+ Constructs a QStyleOptionButton, initializing the members
+ variables to their default values.
+*/
+
+QStyleOptionButton::QStyleOptionButton()
+ : QStyleOption(QStyleOptionButton::Version, SO_Button), features(None)
+{
+}
+
+/*!
+ \internal
+*/
+QStyleOptionButton::QStyleOptionButton(int version)
+ : QStyleOption(version, SO_Button), features(None)
+{
+}
+
+/*!
+ \fn QStyleOptionButton::QStyleOptionButton(const QStyleOptionButton &other)
+
+ Constructs a copy of the \a other style option.
+*/
+
+/*!
+ \enum QStyleOptionButton::StyleOptionType
+
+ This enum is used to hold information about the type of the style option, and
+ is defined for each QStyleOption subclass.
+
+ \value Type The type of style option provided (\l{SO_Button} for this class).
+
+ The type is used internally by QStyleOption, its subclasses, and
+ qstyleoption_cast() to determine the type of style option. In
+ general you do not need to worry about this unless you want to
+ create your own QStyleOption subclass and your own styles.
+
+ \sa StyleOptionVersion
+*/
+
+/*!
+ \enum QStyleOptionButton::StyleOptionVersion
+
+ This enum is used to hold information about the version of the style option, and
+ is defined for each QStyleOption subclass.
+
+ \value Version 1
+
+ The version is used by QStyleOption subclasses to implement
+ extensions without breaking compatibility. If you use
+ qstyleoption_cast(), you normally do not need to check it.
+
+ \sa StyleOptionType
+*/
+
+/*!
+ \variable QStyleOptionButton::features
+ \brief a bitwise OR of the features that describe this button
+
+ \sa ButtonFeature
+*/
+
+/*!
+ \variable QStyleOptionButton::text
+ \brief the text of the button
+
+ The default value is an empty string.
+*/
+
+/*!
+ \variable QStyleOptionButton::icon
+ \brief the icon of the button
+
+ The default value is an empty icon, i.e. an icon with neither a
+ pixmap nor a filename.
+
+ \sa iconSize
+*/
+
+/*!
+ \variable QStyleOptionButton::iconSize
+ \brief the size of the icon for the button
+
+ The default value is QSize(-1, -1), i.e. an invalid size.
+*/
+
+
+#ifndef QT_NO_TOOLBAR
+/*!
+ \class QStyleOptionToolBar
+ \brief The QStyleOptionToolBar class is used to describe the
+ parameters for drawing a toolbar.
+
+ \since 4.1
+
+ QStyleOptionToolBar contains all the information that QStyle
+ functions need to draw QToolBar.
+
+ For performance reasons, the access to the member variables is
+ direct (i.e., using the \c . or \c -> operator). This low-level feel
+ makes the structures straightforward to use and emphasizes that
+ these are simply parameters used by the style functions.
+
+ The QStyleOptionToolBar class holds the lineWidth and the
+ midLineWidth for drawing the widget. It also stores information
+ about which \l {toolBarArea}{area} the toolbar should be located
+ in, whether it is movable or not, which position the toolbar line
+ should have (positionOfLine), and the toolbar's position within
+ the line (positionWithinLine).
+
+ In addition, the class provides a couple of enums: The
+ ToolBarFeature enum is used to describe whether a toolbar is
+ movable or not, and the ToolBarPosition enum is used to describe
+ the position of a toolbar line, as well as the toolbar's position
+ within the line.
+
+ For an example demonstrating how style options can be used, see
+ the \l {widgets/styles}{Styles} example.
+
+ \sa QStyleOption
+*/
+
+/*!
+ Constructs a QStyleOptionToolBar, initializing the members
+ variables to their default values.
+*/
+
+QStyleOptionToolBar::QStyleOptionToolBar()
+ : QStyleOption(Version, SO_ToolBar), positionOfLine(OnlyOne), positionWithinLine(OnlyOne),
+ toolBarArea(Qt::TopToolBarArea), features(None), lineWidth(0), midLineWidth(0)
+{
+}
+
+/*!
+ \fn QStyleOptionToolBar::QStyleOptionToolBar(const QStyleOptionToolBar &other)
+
+ Constructs a copy of the \a other style option.
+*/
+
+/*!
+ \internal
+*/
+QStyleOptionToolBar::QStyleOptionToolBar(int version)
+: QStyleOption(version, SO_ToolBar), positionOfLine(OnlyOne), positionWithinLine(OnlyOne),
+ toolBarArea(Qt::TopToolBarArea), features(None), lineWidth(0), midLineWidth(0)
+{
+
+}
+
+/*!
+ \enum QStyleOptionToolBar::ToolBarPosition
+
+ \image qstyleoptiontoolbar-position.png
+
+ This enum is used to describe the position of a toolbar line, as
+ well as the toolbar's position within the line.
+
+ The order of the positions within a line starts at the top of a
+ vertical line, and from the left within a horizontal line. The
+ order of the positions for the lines is always from the the
+ parent widget's boundary edges.
+
+ \value Beginning The toolbar is located at the beginning of the line,
+ or the toolbar line is the first of several lines. There can
+ only be one toolbar (and only one line) with this position.
+ \value Middle The toolbar is located in the middle of the line,
+ or the toolbar line is in the middle of several lines. There can
+ several toolbars (and lines) with this position.
+ \value End The toolbar is located at the end of the line,
+ or the toolbar line is the last of several lines. There can
+ only be one toolbar (and only one line) with this position.
+ \value OnlyOne There is only one toolbar or line. This is the default value
+ of the positionOfLine and positionWithinLine variables.
+
+ \sa positionWithinLine, positionOfLine
+*/
+
+/*!
+ \enum QStyleOptionToolBar::ToolBarFeature
+
+ This enum is used to describe whether a toolbar is movable or not.
+
+ \value None The toolbar cannot be moved. The default value.
+ \value Movable The toolbar is movable, and a handle will appear when
+ holding the cursor over the toolbar's boundary.
+
+ \sa features, QToolBar::isMovable()
+*/
+
+/*!
+ \variable QStyleOptionToolBar::positionOfLine
+
+ This variable holds the position of the toolbar line.
+
+ The default value is QStyleOptionToolBar::OnlyOne.
+*/
+
+/*!
+ \variable QStyleOptionToolBar::positionWithinLine
+
+ This variable holds the position of the toolbar within a line.
+
+ The default value is QStyleOptionToolBar::OnlyOne.
+*/
+
+/*!
+ \variable QStyleOptionToolBar::toolBarArea
+
+ This variable holds the location for drawing the toolbar.
+
+ The default value is Qt::TopToolBarArea.
+
+ \sa Qt::ToolBarArea
+*/
+
+/*!
+ \variable QStyleOptionToolBar::features
+
+ This variable holds whether the toolbar is movable or not.
+
+ The default value is \l None.
+*/
+
+/*!
+ \variable QStyleOptionToolBar::lineWidth
+
+ This variable holds the line width for drawing the toolbar.
+
+ The default value is 0.
+*/
+
+/*!
+ \variable QStyleOptionToolBar::midLineWidth
+
+ This variable holds the mid-line width for drawing the toolbar.
+
+ The default value is 0.
+*/
+
+/*!
+ \enum QStyleOptionToolBar::StyleOptionType
+
+ This enum is used to hold information about the type of the style
+ option, and is defined for each QStyleOption subclass.
+
+ \value Type The type of style option provided (\l{SO_ToolBar} for
+ this class).
+
+ The type is used internally by QStyleOption, its subclasses, and
+ qstyleoption_cast() to determine the type of style option. In
+ general you do not need to worry about this unless you want to
+ create your own QStyleOption subclass and your own styles.
+
+ \sa StyleOptionVersion
+*/
+
+/*!
+ \enum QStyleOptionToolBar::StyleOptionVersion
+
+ This enum is used to hold information about the version of the
+ style option, and is defined for each QStyleOption subclass.
+
+ \value Version 1
+
+ The version is used by QStyleOption subclasses to implement
+ extensions without breaking compatibility. If you use
+ qstyleoption_cast(), you normally do not need to check it.
+
+ \sa StyleOptionType
+*/
+
+#endif
+
+#ifndef QT_NO_TABBAR
+/*!
+ \class QStyleOptionTab
+ \brief The QStyleOptionTab class is used to describe the
+ parameters for drawing a tab bar.
+
+ The QStyleOptionTab class is used for drawing several built-in Qt
+ widgets including \l QTabBar and the panel for \l QTabWidget. Note
+ that to describe the parameters necessary for drawing a frame in
+ Qt 4.1 or above, you must use the QStyleOptionFrameV2 subclass.
+
+ An instance of the QStyleOptiontabV2 class has
+ \l{QStyleOption::type} {type} \l SO_Tab and
+ \l{QStyleOption::version} {version} 1. The type is used internally
+ by QStyleOption, its subclasses, and qstyleoption_cast() to
+ determine the type of style option. In general you do not need to
+ worry about this unless you want to create your own QStyleOption
+ subclass and your own styles. The version is used by QStyleOption
+ subclasses to implement extensions without breaking
+ compatibility. If you use qstyleoption_cast(), you normally do not
+ need to check it.
+
+ If you create your own QStyle subclass, you should handle both
+ QStyleOptionTab and QStyleOptionTabV2.
+
+ For an example demonstrating how style options can be used, see
+ the \l {widgets/styles}{Styles} example.
+
+ \sa QStyleOptionTabV2, QStyleOption
+*/
+
+/*!
+ Constructs a QStyleOptionTab object, initializing the members
+ variables to their default values.
+*/
+
+QStyleOptionTab::QStyleOptionTab()
+ : QStyleOption(QStyleOptionTab::Version, SO_Tab),
+ shape(QTabBar::RoundedNorth),
+ row(0),
+ position(Beginning),
+ selectedPosition(NotAdjacent), cornerWidgets(QStyleOptionTab::NoCornerWidgets)
+{
+}
+
+/*!
+ \internal
+*/
+QStyleOptionTab::QStyleOptionTab(int version)
+ : QStyleOption(version, SO_Tab),
+ shape(QTabBar::RoundedNorth),
+ row(0),
+ position(Beginning),
+ selectedPosition(NotAdjacent), cornerWidgets(QStyleOptionTab::NoCornerWidgets)
+{
+}
+
+/*!
+ \fn QStyleOptionTab::QStyleOptionTab(const QStyleOptionTab &other)
+
+ Constructs a copy of the \a other style option.
+*/
+
+/*!
+ \enum QStyleOptionTab::StyleOptionType
+
+ This enum is used to hold information about the type of the style option, and
+ is defined for each QStyleOption subclass.
+
+ \value Type The type of style option provided (\l{SO_Tab} for this class).
+
+ The type is used internally by QStyleOption, its subclasses, and
+ qstyleoption_cast() to determine the type of style option. In
+ general you do not need to worry about this unless you want to
+ create your own QStyleOption subclass and your own styles.
+
+ \sa StyleOptionVersion
+*/
+
+/*!
+ \enum QStyleOptionTab::StyleOptionVersion
+
+ This enum is used to hold information about the version of the style option, and
+ is defined for each QStyleOption subclass.
+
+ \value Version 1
+
+ The version is used by QStyleOption subclasses to implement
+ extensions without breaking compatibility. If you use
+ qstyleoption_cast(), you normally do not need to check it.
+
+ \sa StyleOptionType
+*/
+
+/*!
+ \enum QStyleOptionTab::TabPosition
+
+ This enum describes the position of the tab.
+
+ \value Beginning The tab is the first tab in the tab bar.
+ \value Middle The tab is neither the first nor the last tab in the tab bar.
+ \value End The tab is the last tab in the tab bar.
+ \value OnlyOneTab The tab is both the first and the last tab in the tab bar.
+
+ \sa position
+*/
+
+/*!
+ \enum QStyleOptionTab::CornerWidget
+
+ These flags indicate the corner widgets in a tab.
+
+ \value NoCornerWidgets There are no corner widgets
+ \value LeftCornerWidget Left corner widget
+ \value RightCornerWidget Right corner widget
+
+ \sa cornerWidgets
+*/
+
+/*! \enum QStyleOptionTab::SelectedPosition
+
+ This enum describes the position of the selected tab. Some styles
+ need to draw a tab differently depending on whether or not it is
+ adjacent to the selected tab.
+
+ \value NotAdjacent The tab is not adjacent to a selected tab (or is the selected tab).
+ \value NextIsSelected The next tab (typically the tab on the right) is selected.
+ \value PreviousIsSelected The previous tab (typically the tab on the left) is selected.
+
+ \sa selectedPosition
+*/
+
+/*!
+ \variable QStyleOptionTab::selectedPosition
+ \brief the position of the selected tab in relation to this tab
+
+ The default value is NotAdjacent, i.e. the tab is not adjacent to
+ a selected tab nor is it the selected tab.
+*/
+
+/*!
+ \variable QStyleOptionTab::cornerWidgets
+ \brief an OR combination of CornerWidget values indicating the
+ corner widgets of the tab bar
+
+ The default value is NoCornerWidgets.
+
+ \sa CornerWidget
+*/
+
+
+/*!
+ \variable QStyleOptionTab::shape
+ \brief the tab shape used to draw the tab; by default
+ QTabBar::RoundedNorth
+
+ \sa QTabBar::Shape
+*/
+
+/*!
+ \variable QStyleOptionTab::text
+ \brief the text of the tab
+
+ The default value is an empty string.
+*/
+
+/*!
+ \variable QStyleOptionTab::icon
+ \brief the icon for the tab
+
+ The default value is an empty icon, i.e. an icon with neither a
+ pixmap nor a filename.
+*/
+
+/*!
+ \variable QStyleOptionTab::row
+ \brief which row the tab is currently in
+
+ The default value is 0, indicating the front row. Currently this
+ property can only be 0.
+*/
+
+/*!
+ \variable QStyleOptionTab::position
+ \brief the position of the tab in the tab bar
+
+ The default value is \l Beginning, i.e. the tab is the first tab
+ in the tab bar.
+*/
+
+/*!
+ \class QStyleOptionTabV2
+ \brief The QStyleOptionTabV2 class is used to describe the
+ parameters necessary for drawing a tabs in Qt 4.1 or above.
+
+ \since 4.1
+
+ An instance of the QStyleOptionTabV2 class has
+ \l{QStyleOption::type} {type} \l SO_Tab and
+ \l{QStyleOption::version} {version} 2. The type is used internally
+ by QStyleOption, its subclasses, and qstyleoption_cast() to
+ determine the type of style option. In general you do not need to
+ worry about this unless you want to create your own QStyleOption
+ subclass and your own styles. The version is used by QStyleOption
+ subclasses to implement extensions without breaking
+ compatibility. If you use qstyleoption_cast(), you normally do not
+ need to check it.
+
+ If you create your own QStyle subclass, you should handle both
+ QStyleOptionTab and QStyleOptionTabV2. One way to achieve this is
+ to use the QStyleOptionTabV2 copy constructor. For example:
+
+ \snippet doc/src/snippets/qstyleoption/main.cpp 3
+
+ In the example above: If \c tabOption's version is 1, the extra
+ member (\l iconSize) will be set to an invalid size for \c tabV2.
+ If \c tabOption's version is 2, the constructor will simply copy
+ the \c tab's iconSize.
+
+ For an example demonstrating how style options can be used, see
+ the \l {widgets/styles}{Styles} example.
+
+ \sa QStyleOptionTab, QStyleOption
+*/
+
+/*!
+ \enum QStyleOptionTabV2::StyleOptionVersion
+
+ This enum is used to hold information about the version of the style option, and
+ is defined for each QStyleOption subclass.
+
+ \value Version 2
+
+ The version is used by QStyleOption subclasses to implement
+ extensions without breaking compatibility. If you use
+ qstyleoption_cast(), you normally do not need to check it.
+
+ \sa StyleOptionType
+*/
+
+/*!
+ \variable QStyleOptionTabV2::iconSize
+ \brief the size for the icons
+
+ The default value is QSize(-1, -1), i.e. an invalid size; use
+ QStyle::pixelMetric() to find the default icon size for tab bars.
+
+ \sa QTabBar::iconSize()
+*/
+
+/*!
+ Constructs a QStyleOptionTabV2.
+*/
+QStyleOptionTabV2::QStyleOptionTabV2()
+ : QStyleOptionTab(Version)
+{
+}
+
+/*!
+ \internal
+*/
+QStyleOptionTabV2::QStyleOptionTabV2(int version)
+ : QStyleOptionTab(version)
+{
+}
+
+/*!
+ \fn QStyleOptionTabV2::QStyleOptionTabV2(const QStyleOptionTabV2 &other)
+
+ Constructs a copy of the \a other style option.
+*/
+
+/*!
+ Constructs a QStyleOptionTabV2 copy of the \a other style option
+ which can be either of the QStyleOptionTabV2 or QStyleOptionTab
+ types.
+
+ If the other style option's version is 1, the new style option's
+ \c iconSize is set to an invalid value. If its version is 2, its
+ \c iconSize value is simply copied to the new style option.
+*/
+QStyleOptionTabV2::QStyleOptionTabV2(const QStyleOptionTab &other)
+ : QStyleOptionTab(Version)
+{
+ if (const QStyleOptionTabV2 *tab = qstyleoption_cast<const QStyleOptionTabV2 *>(&other)) {
+ *this = *tab;
+ } else {
+ *((QStyleOptionTab *)this) = other;
+ version = Version;
+ }
+}
+
+/*!
+ Assigns the \a other style option to this QStyleOptionTabV2. The
+ \a other style option can be either of the QStyleOptionTabV2 or
+ QStyleOptionTab types.
+
+ If the other style option's version is 1, this style option's \c
+ iconSize is set to an invalid size. If its version is 2, its \c
+ iconSize value is simply copied to this style option.
+*/
+QStyleOptionTabV2 &QStyleOptionTabV2::operator=(const QStyleOptionTab &other)
+{
+ QStyleOptionTab::operator=(other);
+
+ if (const QStyleOptionTabV2 *tab = qstyleoption_cast<const QStyleOptionTabV2 *>(&other))
+ iconSize = tab->iconSize;
+ else
+ iconSize = QSize();
+ return *this;
+}
+
+/*!
+ \class QStyleOptionTabV3
+ \brief The QStyleOptionTabV3 class is used to describe the
+ parameters necessary for drawing a tabs in Qt 4.5 or above.
+
+ \since 4.5
+
+ An instance of the QStyleOptionTabV3 class has
+ \l{QStyleOption::type} {type} \l SO_Tab and
+ \l{QStyleOption::version} {version} 3. The type is used internally
+ by QStyleOption, its subclasses, and qstyleoption_cast() to
+ determine the type of style option. In general you do not need to
+ worry about this unless you want to create your own QStyleOption
+ subclass and your own styles. The version is used by QStyleOption
+ subclasses to implement extensions without breaking
+ compatibility. If you use qstyleoption_cast(), you normally do not
+ need to check it.
+
+ If you create your own QStyle subclass, you should handle both
+ QStyleOptionTab, QStyleOptionTabV2 and QStyleOptionTabV3.
+ One way to achieve this is to use the QStyleOptionTabV3 copy
+ constructor. For example:
+
+ \snippet doc/src/snippets/qstyleoption/main.cpp 3
+
+ In the example above: If \c tabOption's version is 1, the extra
+ member (\l{QStyleOptionTabV2::iconSize}{iconSize}) will be set to
+ an invalid size for \c tabV2. If \c tabOption's version is 2, the
+ constructor will simply copy the \c tab's iconSize.
+
+ For an example demonstrating how style options can be used, see
+ the \l {widgets/styles}{Styles} example.
+
+ \sa QStyleOptionTab, QStyleOption
+*/
+
+/*!
+ \enum QStyleOptionTabV3::StyleOptionVersion
+
+ This enum is used to hold information about the version of the style option, and
+ is defined for each QStyleOption subclass.
+
+ \value Version 3
+
+ The version is used by QStyleOption subclasses to implement
+ extensions without breaking compatibility. If you use
+ qstyleoption_cast(), you normally do not need to check it.
+
+ \sa StyleOptionType
+*/
+
+/*!
+ \variable QStyleOptionTabV3::documentMode
+ \brief whether the tabbar is in document mode.
+
+ The default value is false;
+*/
+
+/*!
+ \variable QStyleOptionTabV3::leftButtonSize
+ \brief the size for the left widget on the tab.
+
+ The default value is QSize(-1, -1), i.e. an invalid size;
+*/
+
+/*!
+ \variable QStyleOptionTabV3::rightButtonSize
+ \brief the size for the right widget on the tab.
+
+ The default value is QSize(-1, -1), i.e. an invalid size;
+*/
+
+/*!
+ Constructs a QStyleOptionTabV3.
+*/
+
+QStyleOptionTabV3::QStyleOptionTabV3()
+ : QStyleOptionTabV2(Version)
+ , documentMode(false)
+{
+}
+
+/*!
+ \internal
+*/
+QStyleOptionTabV3::QStyleOptionTabV3(int version)
+ : QStyleOptionTabV2(version)
+{
+}
+
+/*!
+ \fn QStyleOptionTabV3::QStyleOptionTabV3(const QStyleOptionTabV3 &other)
+
+ Constructs a copy of the \a other style option.
+*/
+
+/*!
+ \fn QStyleOptionTabV3::QStyleOptionTabV3(const QStyleOptionTabV2 &other)
+
+ Constructs a copy of the \a other style option.
+*/
+
+/*!
+ Constructs a QStyleOptionTabV3 copy of the \a other style option
+ which can be either of the QStyleOptionTabV3, QStyleOptionTabV2
+ or QStyleOptionTab types.
+
+ If the other style option's version is 1 or 2, the new style option's
+ \c leftButtonSize and \c rightButtonSize is set to an invalid value. If
+ its version is 3, its \c leftButtonSize and \c rightButtonSize values
+ are simply copied to the new style option.
+*/
+QStyleOptionTabV3::QStyleOptionTabV3(const QStyleOptionTab &other)
+ : QStyleOptionTabV2(Version)
+{
+ if (const QStyleOptionTabV3 *tab = qstyleoption_cast<const QStyleOptionTabV3 *>(&other)) {
+ *this = *tab;
+ } else {
+ *((QStyleOptionTabV2 *)this) = other;
+ version = Version;
+ }
+}
+
+/*!
+ Assigns the \a other style option to this QStyleOptionTabV3. The
+ \a other style option can be either of the QStyleOptionTabV3,
+ QStyleOptionTabV2 or QStyleOptionTab types.
+
+ If the other style option's version is 1 or 2, the new style option's
+ \c leftButtonSize and \c rightButtonSize is set to an invalid value. If
+ its version is 3, its \c leftButtonSize and \c rightButtonSize values
+ are simply copied to the new style option.
+*/
+QStyleOptionTabV3 &QStyleOptionTabV3::operator=(const QStyleOptionTab &other)
+{
+ QStyleOptionTabV2::operator=(other);
+
+ if (const QStyleOptionTabV3 *tab = qstyleoption_cast<const QStyleOptionTabV3 *>(&other)) {
+ leftButtonSize = tab->leftButtonSize;
+ rightButtonSize = tab->rightButtonSize;
+ } else {
+ leftButtonSize = QSize();
+ rightButtonSize = QSize();
+ documentMode = false;
+ }
+ return *this;
+}
+
+#endif // QT_NO_TABBAR
+
+/*!
+ \class QStyleOptionProgressBar
+ \brief The QStyleOptionProgressBar class is used to describe the
+ parameters necessary for drawing a progress bar.
+
+ Since Qt 4.1, Qt uses the QStyleOptionProgressBarV2 subclass for
+ drawing QProgressBar.
+
+ An instance of the QStyleOptionProgressBar class has type
+ SO_ProgressBar and version 1.
+
+ The type is used internally by QStyleOption, its subclasses, and
+ qstyleoption_cast() to determine the type of style option. In
+ general you do not need to worry about this unless you want to
+ create your own QStyleOption subclass and your own styles. The
+ version is used by QStyleOption subclasses to implement extensions
+ without breaking compatibility. If you use qstyleoption_cast(),
+ you normally do not need to check it.
+
+ If you create your own QStyle subclass, you should handle both
+ QStyleOptionProgressBar and QStyleOptionProgressBarV2.
+
+ For an example demonstrating how style options can be used, see
+ the \l {widgets/styles}{Styles} example.
+
+ \sa QStyleOptionProgressBarV2, QStyleOption
+*/
+
+/*!
+ Constructs a QStyleOptionProgressBar, initializing the members
+ variables to their default values.
+*/
+
+QStyleOptionProgressBar::QStyleOptionProgressBar()
+ : QStyleOption(QStyleOptionProgressBar::Version, SO_ProgressBar),
+ minimum(0), maximum(0), progress(0), textAlignment(Qt::AlignLeft), textVisible(false)
+{
+}
+
+/*!
+ \internal
+*/
+QStyleOptionProgressBar::QStyleOptionProgressBar(int version)
+ : QStyleOption(version, SO_ProgressBar),
+ minimum(0), maximum(0), progress(0), textAlignment(Qt::AlignLeft), textVisible(false)
+{
+}
+
+/*!
+ \fn QStyleOptionProgressBar::QStyleOptionProgressBar(const QStyleOptionProgressBar &other)
+
+ Constructs a copy of the \a other style option.
+*/
+
+/*!
+ \enum QStyleOptionProgressBar::StyleOptionType
+
+ This enum is used to hold information about the type of the style option, and
+ is defined for each QStyleOption subclass.
+
+ \value Type The type of style option provided (\l{SO_ProgressBar} for this class).
+
+ The type is used internally by QStyleOption, its subclasses, and
+ qstyleoption_cast() to determine the type of style option. In
+ general you do not need to worry about this unless you want to
+ create your own QStyleOption subclass and your own styles.
+
+ \sa StyleOptionVersion
+*/
+
+/*!
+ \enum QStyleOptionProgressBar::StyleOptionVersion
+
+ This enum is used to hold information about the version of the style option, and
+ is defined for each QStyleOption subclass.
+
+ \value Version 1
+
+ The version is used by QStyleOption subclasses to implement
+ extensions without breaking compatibility. If you use
+ qstyleoption_cast(), you normally do not need to check it.
+
+ \sa StyleOptionType
+*/
+
+/*!
+ \variable QStyleOptionProgressBar::minimum
+ \brief the minimum value for the progress bar
+
+ This is the minimum value in the progress bar. The default value
+ is 0.
+
+ \sa QProgressBar::minimum
+*/
+
+/*!
+ \variable QStyleOptionProgressBar::maximum
+ \brief the maximum value for the progress bar
+
+ This is the maximum value in the progress bar. The default value
+ is 0.
+
+ \sa QProgressBar::maximum
+*/
+
+/*!
+ \variable QStyleOptionProgressBar::text
+ \brief the text for the progress bar
+
+ The progress bar text is usually just the progress expressed as a
+ string. An empty string indicates that the progress bar has not
+ started yet. The default value is an empty string.
+
+ \sa QProgressBar::text
+*/
+
+/*!
+ \variable QStyleOptionProgressBar::textVisible
+ \brief a flag indicating whether or not text is visible
+
+ If this flag is true then the text is visible. Otherwise, the text
+ is not visible. The default value is false.
+
+ \sa QProgressBar::textVisible
+*/
+
+
+/*!
+ \variable QStyleOptionProgressBar::textAlignment
+ \brief the text alignment for the text in the QProgressBar
+
+ This can be used as a guide on where the text should be in the
+ progress bar. The default value is Qt::AlignLeft.
+*/
+
+/*!
+ \variable QStyleOptionProgressBar::progress
+ \brief the current progress for the progress bar
+
+ The current progress. A value of QStyleOptionProgressBar::minimum
+ - 1 indicates that the progress hasn't started yet. The default
+ value is 0.
+
+ \sa QProgressBar::value
+*/
+
+/*!
+ \class QStyleOptionProgressBarV2
+ \brief The QStyleOptionProgressBarV2 class is used to describe the
+ parameters necessary for drawing a progress bar in Qt 4.1 or above.
+
+ \since 4.1
+
+ An instance of this class has \l{QStyleOption::type} {type}
+ SO_ProgressBar and \l{QStyleOption::version} {version} 2.
+
+ The type is used internally by QStyleOption, its subclasses, and
+ qstyleoption_cast() to determine the type of style option. In
+ general you do not need to worry about this unless you want to
+ create your own QStyleOption subclass and your own styles. The
+ version is used by QStyleOption subclasses to implement extensions
+ without breaking compatibility. If you use qstyleoption_cast(),
+ you normally do not need to check it.
+
+ If you create your own QStyle subclass, you should handle both
+ QStyleOptionProgressBar and QStyleOptionProgressBarV2. One way
+ to achieve this is to use the QStyleOptionProgressBarV2 copy
+ constructor. For example:
+
+ \snippet doc/src/snippets/qstyleoption/main.cpp 2
+
+ In the example above: If the \c progressBarOption's version is 1,
+ the extra members (\l orientation, \l invertedAppearance, and \l
+ bottomToTop) are set to default values for \c progressBarV2. If
+ the \c progressBarOption's version is 2, the constructor will
+ simply copy the extra members to progressBarV2.
+
+ For an example demonstrating how style options can be used, see
+ the \l {widgets/styles}{Styles} example.
+
+ \sa QStyleOptionProgressBar, QStyleOption
+*/
+
+/*!
+ Constructs a QStyleOptionProgressBarV2, initializing he members
+ variables to their default values.
+*/
+
+QStyleOptionProgressBarV2::QStyleOptionProgressBarV2()
+ : QStyleOptionProgressBar(2),
+ orientation(Qt::Horizontal), invertedAppearance(false), bottomToTop(false)
+{
+}
+
+/*!
+ \internal
+*/
+QStyleOptionProgressBarV2::QStyleOptionProgressBarV2(int version)
+ : QStyleOptionProgressBar(version),
+ orientation(Qt::Horizontal), invertedAppearance(false), bottomToTop(false)
+{
+}
+
+/*!
+ Constructs a copy of the \a other style option which can be either
+ of the QStyleOptionProgressBar and QStyleOptionProgressBarV2
+ types.
+
+ If the \a{other} style option's version is 1, the extra members (\l
+ orientation, \l invertedAppearance, and \l bottomToTop) are set
+ to default values for the new style option. If \a{other}'s version
+ is 2, the extra members are simply copied.
+
+ \sa version
+*/
+QStyleOptionProgressBarV2::QStyleOptionProgressBarV2(const QStyleOptionProgressBar &other)
+ : QStyleOptionProgressBar(2), orientation(Qt::Horizontal), invertedAppearance(false), bottomToTop(false)
+{
+ const QStyleOptionProgressBarV2 *pb2 = qstyleoption_cast<const QStyleOptionProgressBarV2 *>(&other);
+ if (pb2)
+ *this = *pb2;
+ else
+ *((QStyleOptionProgressBar *)this) = other;
+}
+
+/*!
+ Constructs a copy of the \a other style option.
+*/
+QStyleOptionProgressBarV2::QStyleOptionProgressBarV2(const QStyleOptionProgressBarV2 &other)
+ : QStyleOptionProgressBar(2), orientation(Qt::Horizontal), invertedAppearance(false), bottomToTop(false)
+{
+ *this = other;
+}
+
+/*!
+ Assigns the \a other style option to this style option. The \a
+ other style option can be either of the QStyleOptionProgressBarV2
+ or QStyleOptionProgressBar types.
+
+ If the \a{other} style option's version is 1, the extra members
+ (\l orientation, \l invertedAppearance, and \l bottomToTop) are
+ set to default values for this style option. If \a{other}'s
+ version is 2, the extra members are simply copied to this style
+ option.
+*/
+QStyleOptionProgressBarV2 &QStyleOptionProgressBarV2::operator=(const QStyleOptionProgressBar &other)
+{
+ QStyleOptionProgressBar::operator=(other);
+
+ const QStyleOptionProgressBarV2 *pb2 = qstyleoption_cast<const QStyleOptionProgressBarV2 *>(&other);
+ orientation = pb2 ? pb2->orientation : Qt::Horizontal;
+ invertedAppearance = pb2 ? pb2->invertedAppearance : false;
+ bottomToTop = pb2 ? pb2->bottomToTop : false;
+ return *this;
+}
+
+/*!
+ \variable QStyleOptionProgressBarV2::orientation
+ \brief the progress bar's orientation (horizontal or vertical);
+ the default orentation is Qt::Horizontal
+
+ \sa QProgressBar::orientation
+*/
+
+/*!
+ \variable QStyleOptionProgressBarV2::invertedAppearance
+ \brief whether the progress bar's appearance is inverted
+
+ The default value is false.
+
+ \sa QProgressBar::invertedAppearance
+*/
+
+/*!
+ \variable QStyleOptionProgressBarV2::bottomToTop
+ \brief whether the text reads from bottom to top when the progress
+ bar is vertical
+
+ The default value is false.
+
+ \sa QProgressBar::textDirection
+*/
+
+/*!
+ \enum QStyleOptionProgressBarV2::StyleOptionType
+
+ This enum is used to hold information about the type of the style option, and
+ is defined for each QStyleOption subclass.
+
+ \value Type The type of style option provided (\l{SO_ProgressBar} for this class).
+
+ The type is used internally by QStyleOption, its subclasses, and
+ qstyleoption_cast() to determine the type of style option. In
+ general you do not need to worry about this unless you want to
+ create your own QStyleOption subclass and your own styles.
+
+ \sa StyleOptionVersion
+*/
+
+/*!
+ \enum QStyleOptionProgressBarV2::StyleOptionVersion
+
+ This enum is used to hold information about the version of the style option, and
+ is defined for each QStyleOption subclass.
+
+ \value Version 2
+
+ The version is used by QStyleOption subclasses to implement
+ extensions without breaking compatibility. If you use
+ qstyleoption_cast(), you normally do not need to check it.
+
+ \sa StyleOptionType
+*/
+
+
+/*!
+ \class QStyleOptionMenuItem
+ \brief The QStyleOptionMenuItem class is used to describe the
+ parameter necessary for drawing a menu item.
+
+ QStyleOptionMenuItem contains all the information that QStyle
+ functions need to draw the menu items from \l QMenu. It is also
+ used for drawing other menu-related widgets.
+
+ For performance reasons, the access to the member variables is
+ direct (i.e., using the \c . or \c -> operator). This low-level feel
+ makes the structures straightforward to use and emphasizes that
+ these are simply parameters used by the style functions.
+
+ For an example demonstrating how style options can be used, see
+ the \l {widgets/styles}{Styles} example.
+
+ \sa QStyleOption
+*/
+
+/*!
+ Constructs a QStyleOptionMenuItem, initializing the members
+ variables to their default values.
+*/
+
+QStyleOptionMenuItem::QStyleOptionMenuItem()
+ : QStyleOption(QStyleOptionMenuItem::Version, SO_MenuItem), menuItemType(Normal),
+ checkType(NotCheckable), checked(false), menuHasCheckableItems(true), maxIconWidth(0), tabWidth(0)
+{
+}
+
+/*!
+ \internal
+*/
+QStyleOptionMenuItem::QStyleOptionMenuItem(int version)
+ : QStyleOption(version, SO_MenuItem), menuItemType(Normal),
+ checkType(NotCheckable), checked(false), menuHasCheckableItems(true), maxIconWidth(0), tabWidth(0)
+{
+}
+
+/*!
+ \fn QStyleOptionMenuItem::QStyleOptionMenuItem(const QStyleOptionMenuItem &other)
+
+ Constructs a copy of the \a other style option.
+*/
+
+/*!
+ \enum QStyleOptionMenuItem::StyleOptionType
+
+ This enum is used to hold information about the type of the style option, and
+ is defined for each QStyleOption subclass.
+
+ \value Type The type of style option provided (\l{SO_MenuItem} for this class).
+
+ The type is used internally by QStyleOption, its subclasses, and
+ qstyleoption_cast() to determine the type of style option. In
+ general you do not need to worry about this unless you want to
+ create your own QStyleOption subclass and your own styles.
+
+ \sa StyleOptionVersion
+*/
+
+/*!
+ \enum QStyleOptionMenuItem::StyleOptionVersion
+
+ This enum is used to hold information about the version of the style option, and
+ is defined for each QStyleOption subclass.
+
+ \value Version 1
+
+ The version is used by QStyleOption subclasses to implement
+ extensions without breaking compatibility. If you use
+ qstyleoption_cast(), you normally do not need to check it.
+
+ \sa StyleOptionType
+*/
+
+/*!
+ \enum QStyleOptionMenuItem::MenuItemType
+
+ This enum indicates the type of menu item that the structure describes.
+
+ \value Normal A normal menu item.
+ \value DefaultItem A menu item that is the default action as specified with \l QMenu::defaultAction().
+ \value Separator A menu separator.
+ \value SubMenu Indicates the menu item points to a sub-menu.
+ \value Scroller A popup menu scroller (currently only used on Mac OS X).
+ \value TearOff A tear-off handle for the menu.
+ \value Margin The margin of the menu.
+ \value EmptyArea The empty area of the menu.
+
+ \sa menuItemType
+*/
+
+/*!
+ \enum QStyleOptionMenuItem::CheckType
+
+ This enum is used to indicate whether or not a check mark should be
+ drawn for the item, or even if it should be drawn at all.
+
+ \value NotCheckable The item is not checkable.
+ \value Exclusive The item is an exclusive check item (like a radio button).
+ \value NonExclusive The item is a non-exclusive check item (like a check box).
+
+ \sa checkType, QAction::checkable, QAction::checked, QActionGroup::exclusive
+*/
+
+/*!
+ \variable QStyleOptionMenuItem::menuItemType
+ \brief the type of menu item
+
+ The default value is \l Normal.
+
+ \sa MenuItemType
+*/
+
+/*!
+ \variable QStyleOptionMenuItem::checkType
+ \brief the type of checkmark of the menu item
+
+ The default value is \l NotCheckable.
+
+ \sa CheckType
+*/
+
+/*!
+ \variable QStyleOptionMenuItem::checked
+ \brief whether the menu item is checked or not
+
+ The default value is false.
+*/
+
+/*!
+ \variable QStyleOptionMenuItem::menuHasCheckableItems
+ \brief whether the menu as a whole has checkable items or not
+
+ The default value is true.
+
+ If this option is set to false, then the menu has no checkable
+ items. This makes it possible for GUI styles to save some
+ horizontal space that would normally be used for the check column.
+*/
+
+/*!
+ \variable QStyleOptionMenuItem::menuRect
+ \brief the rectangle for the entire menu
+
+ The default value is a null rectangle, i.e. a rectangle with both
+ the width and the height set to 0.
+*/
+
+/*!
+ \variable QStyleOptionMenuItem::text
+ \brief the text for the menu item
+
+ Note that the text format is something like this "Menu
+ text\bold{\\t}Shortcut".
+
+ If the menu item doesn't have a shortcut, it will just contain the
+ menu item's text. The default value is an empty string.
+*/
+
+/*!
+ \variable QStyleOptionMenuItem::icon
+ \brief the icon for the menu item
+
+ The default value is an empty icon, i.e. an icon with neither a
+ pixmap nor a filename.
+*/
+
+/*!
+ \variable QStyleOptionMenuItem::maxIconWidth
+ \brief the maximum icon width for the icon in the menu item
+
+ This can be used for drawing the icon into the correct place or
+ properly aligning items. The variable must be set regardless of
+ whether or not the menu item has an icon. The default value is 0.
+*/
+
+/*!
+ \variable QStyleOptionMenuItem::tabWidth
+ \brief the tab width for the menu item
+
+ The tab width is the distance between the text of the menu item
+ and the shortcut. The default value is 0.
+*/
+
+
+/*!
+ \variable QStyleOptionMenuItem::font
+ \brief the font used for the menu item text
+
+ This is the font that should be used for drawing the menu text
+ minus the shortcut. The shortcut is usually drawn using the
+ painter's font. By default, the application's default font is
+ used.
+*/
+
+/*!
+ \class QStyleOptionComplex
+ \brief The QStyleOptionComplex class is used to hold parameters that are
+ common to all complex controls.
+
+ This class is not used on its own. Instead it is used to derive
+ other complex control options, for example QStyleOptionSlider and
+ QStyleOptionSpinBox.
+
+ For performance reasons, the access to the member variables is
+ direct (i.e., using the \c . or \c -> operator).
+
+ For an example demonstrating how style options can be used, see
+ the \l {widgets/styles}{Styles} example.
+
+ \sa QStyleOption
+*/
+
+/*!
+ Constructs a QStyleOptionComplex of the specified \a type and \a
+ version, initializing the member variables to their default
+ values. This constructor is usually called by subclasses.
+*/
+
+QStyleOptionComplex::QStyleOptionComplex(int version, int type)
+ : QStyleOption(version, type), subControls(QStyle::SC_All), activeSubControls(QStyle::SC_None)
+{
+}
+
+/*!
+ \fn QStyleOptionComplex::QStyleOptionComplex(const QStyleOptionComplex &other)
+
+ Constructs a copy of the \a other style option.
+*/
+
+/*!
+ \enum QStyleOptionComplex::StyleOptionType
+
+ This enum is used to hold information about the type of the style option, and
+ is defined for each QStyleOption subclass.
+
+ \value Type The type of style option provided (\l{SO_Complex} for this class).
+
+ The type is used internally by QStyleOption, its subclasses, and
+ qstyleoption_cast() to determine the type of style option. In
+ general you do not need to worry about this unless you want to
+ create your own QStyleOption subclass and your own styles.
+
+ \sa StyleOptionVersion
+*/
+
+/*!
+ \enum QStyleOptionComplex::StyleOptionVersion
+
+ This enum is used to hold information about the version of the style option, and
+ is defined for each QStyleOption subclass.
+
+ \value Version 1
+
+ The version is used by QStyleOption subclasses to implement
+ extensions without breaking compatibility. If you use
+ qstyleoption_cast(), you normally do not need to check it.
+
+ \sa StyleOptionType
+*/
+
+/*!
+ \variable QStyleOptionComplex::subControls
+ \brief a bitwise OR of the various sub-controls that need to be
+ drawn for the complex control
+
+ The default value is QStyle::SC_All.
+
+ \sa QStyle::SubControl
+*/
+
+/*!
+ \variable QStyleOptionComplex::activeSubControls
+ \brief a bitwise OR of the various sub-controls that are active
+ (pressed) for the complex control
+
+ The default value is QStyle::SC_None.
+
+ \sa QStyle::SubControl
+*/
+
+#ifndef QT_NO_SLIDER
+/*!
+ \class QStyleOptionSlider
+ \brief The QStyleOptionSlider class is used to describe the
+ parameters needed for drawing a slider.
+
+ QStyleOptionSlider contains all the information that QStyle
+ functions need to draw QSlider and QScrollBar.
+
+ For performance reasons, the access to the member variables is
+ direct (i.e., using the \c . or \c -> operator). This low-level feel
+ makes the structures straightforward to use and emphasizes that
+ these are simply parameters used by the style functions.
+
+ For an example demonstrating how style options can be used, see
+ the \l {widgets/styles}{Styles} example.
+
+ \sa QStyleOptionComplex, QSlider, QScrollBar
+*/
+
+/*!
+ Constructs a QStyleOptionSlider, initializing the members
+ variables to their default values.
+*/
+
+QStyleOptionSlider::QStyleOptionSlider()
+ : QStyleOptionComplex(Version, SO_Slider), orientation(Qt::Horizontal), minimum(0), maximum(0),
+ tickPosition(QSlider::NoTicks), tickInterval(0), upsideDown(false),
+ sliderPosition(0), sliderValue(0), singleStep(0), pageStep(0), notchTarget(0.0),
+ dialWrapping(false)
+{
+}
+
+/*!
+ \internal
+*/
+QStyleOptionSlider::QStyleOptionSlider(int version)
+ : QStyleOptionComplex(version, SO_Slider), orientation(Qt::Horizontal), minimum(0), maximum(0),
+ tickPosition(QSlider::NoTicks), tickInterval(0), upsideDown(false),
+ sliderPosition(0), sliderValue(0), singleStep(0), pageStep(0), notchTarget(0.0),
+ dialWrapping(false)
+{
+}
+
+/*!
+ \fn QStyleOptionSlider::QStyleOptionSlider(const QStyleOptionSlider &other)
+
+ Constructs a copy of the \a other style option.
+*/
+
+/*!
+ \enum QStyleOptionSlider::StyleOptionType
+
+ This enum is used to hold information about the type of the style option, and
+ is defined for each QStyleOption subclass.
+
+ \value Type The type of style option provided (\l{SO_Slider} for this class).
+
+ The type is used internally by QStyleOption, its subclasses, and
+ qstyleoption_cast() to determine the type of style option. In
+ general you do not need to worry about this unless you want to
+ create your own QStyleOption subclass and your own styles.
+
+ \sa StyleOptionVersion
+*/
+
+/*!
+ \enum QStyleOptionSlider::StyleOptionVersion
+
+ This enum is used to hold information about the version of the style option, and
+ is defined for each QStyleOption subclass.
+
+ \value Version 1
+
+ The version is used by QStyleOption subclasses to implement
+ extensions without breaking compatibility. If you use
+ qstyleoption_cast(), you normally do not need to check it.
+
+ \sa StyleOptionType
+*/
+
+/*!
+ \variable QStyleOptionSlider::orientation
+ \brief the slider's orientation (horizontal or vertical)
+
+ The default orientation is Qt::Horizontal.
+
+ \sa Qt::Orientation
+*/
+
+/*!
+ \variable QStyleOptionSlider::minimum
+ \brief the minimum value for the slider
+
+ The default value is 0.
+*/
+
+/*!
+ \variable QStyleOptionSlider::maximum
+ \brief the maximum value for the slider
+
+ The default value is 0.
+*/
+
+/*!
+ \variable QStyleOptionSlider::tickPosition
+ \brief the position of the slider's tick marks, if any
+
+ The default value is QSlider::NoTicks.
+
+ \sa QSlider::TickPosition
+*/
+
+/*!
+ \variable QStyleOptionSlider::tickInterval
+ \brief the interval that should be drawn between tick marks
+
+ The default value is 0.
+*/
+
+/*!
+ \variable QStyleOptionSlider::notchTarget
+ \brief the number of pixel between notches
+
+ The default value is 0.0.
+
+ \sa QDial::notchTarget()
+*/
+
+/*!
+ \variable QStyleOptionSlider::dialWrapping
+ \brief whether the dial should wrap or not
+
+ The default value is false, i.e. the dial is not wrapped.
+
+ \sa QDial::wrapping()
+*/
+
+/*!
+ \variable QStyleOptionSlider::upsideDown
+ \brief the slider control orientation
+
+ Normally a slider increases as it moves up or to the right;
+ upsideDown indicates that it should do the opposite (increase as
+ it moves down or to the left). The default value is false,
+ i.e. the slider increases as it moves up or to the right.
+
+ \sa QStyle::sliderPositionFromValue(),
+ QStyle::sliderValueFromPosition(),
+ QAbstractSlider::invertedAppearance
+*/
+
+/*!
+ \variable QStyleOptionSlider::sliderPosition
+ \brief the position of the slider handle
+
+ If the slider has active feedback (i.e.,
+ QAbstractSlider::tracking is true), this value will be the same as
+ \l sliderValue. Otherwise, it will have the current position of
+ the handle. The default value is 0.
+
+ \sa QAbstractSlider::tracking, sliderValue
+*/
+
+/*!
+ \variable QStyleOptionSlider::sliderValue
+ \brief the value of the slider
+
+ If the slider has active feedback (i.e.,
+ QAbstractSlider::tracking is true), this value will be the same
+ as \l sliderPosition. Otherwise, it will have the value the
+ slider had before the mouse was pressed.
+
+ The default value is 0.
+
+ \sa QAbstractSlider::tracking sliderPosition
+*/
+
+/*!
+ \variable QStyleOptionSlider::singleStep
+ \brief the size of the single step of the slider
+
+ The default value is 0.
+
+ \sa QAbstractSlider::singleStep
+*/
+
+/*!
+ \variable QStyleOptionSlider::pageStep
+ \brief the size of the page step of the slider
+
+ The default value is 0.
+
+ \sa QAbstractSlider::pageStep
+*/
+#endif // QT_NO_SLIDER
+
+#ifndef QT_NO_SPINBOX
+/*!
+ \class QStyleOptionSpinBox
+ \brief The QStyleOptionSpinBox class is used to describe the
+ parameters necessary for drawing a spin box.
+
+ QStyleOptionSpinBox contains all the information that QStyle
+ functions need to draw QSpinBox and QDateTimeEdit.
+
+ For performance reasons, the access to the member variables is
+ direct (i.e., using the \c . or \c -> operator). This low-level feel
+ makes the structures straightforward to use and emphasizes that
+ these are simply parameters used by the style functions.
+
+ For an example demonstrating how style options can be used, see
+ the \l {widgets/styles}{Styles} example.
+
+ \sa QStyleOption, QStyleOptionComplex
+*/
+
+/*!
+ Constructs a QStyleOptionSpinBox, initializing the members
+ variables to their default values.
+*/
+
+QStyleOptionSpinBox::QStyleOptionSpinBox()
+ : QStyleOptionComplex(Version, SO_SpinBox), buttonSymbols(QAbstractSpinBox::UpDownArrows),
+ stepEnabled(QAbstractSpinBox::StepNone), frame(false)
+{
+}
+
+/*!
+ \internal
+*/
+QStyleOptionSpinBox::QStyleOptionSpinBox(int version)
+ : QStyleOptionComplex(version, SO_SpinBox), buttonSymbols(QAbstractSpinBox::UpDownArrows),
+ stepEnabled(QAbstractSpinBox::StepNone), frame(false)
+{
+}
+
+/*!
+ \fn QStyleOptionSpinBox::QStyleOptionSpinBox(const QStyleOptionSpinBox &other)
+
+ Constructs a copy of the \a other style option.
+*/
+
+/*!
+ \enum QStyleOptionSpinBox::StyleOptionType
+
+ This enum is used to hold information about the type of the style option, and
+ is defined for each QStyleOption subclass.
+
+ \value Type The type of style option provided (\l{SO_SpinBox} for this class).
+
+ The type is used internally by QStyleOption, its subclasses, and
+ qstyleoption_cast() to determine the type of style option. In
+ general you do not need to worry about this unless you want to
+ create your own QStyleOption subclass and your own styles.
+
+ \sa StyleOptionVersion
+*/
+
+/*!
+ \enum QStyleOptionSpinBox::StyleOptionVersion
+
+ This enum is used to hold information about the version of the style option, and
+ is defined for each QStyleOption subclass.
+
+ \value Version 1
+
+ The version is used by QStyleOption subclasses to implement
+ extensions without breaking compatibility. If you use
+ qstyleoption_cast(), you normally do not need to check it.
+
+ \sa StyleOptionType
+*/
+
+/*!
+ \variable QStyleOptionSpinBox::buttonSymbols
+ \brief the type of button symbols to draw for the spin box
+
+ The default value is QAbstractSpinBox::UpDownArrows specufying
+ little arrows in the classic style.
+
+ \sa QAbstractSpinBox::ButtonSymbols
+*/
+
+/*!
+ \variable QStyleOptionSpinBox::stepEnabled
+ \brief which buttons of the spin box that are enabled
+
+ The default value is QAbstractSpinBox::StepNone.
+
+ \sa QAbstractSpinBox::StepEnabled
+*/
+
+/*!
+ \variable QStyleOptionSpinBox::frame
+ \brief whether the spin box has a frame
+
+ The default value is false, i.e. the spin box has no frame.
+*/
+#endif // QT_NO_SPINBOX
+
+/*!
+ \class QStyleOptionQ3ListViewItem
+ \brief The QStyleOptionQ3ListViewItem class is used to describe an
+ item drawn in a Q3ListView.
+
+ This class is used for drawing the compatibility Q3ListView's
+ items. \bold {It is not recommended for new classes}.
+
+ QStyleOptionQ3ListViewItem contains all the information that
+ QStyle functions need to draw the Q3ListView items.
+
+ For performance reasons, the access to the member variables is
+ direct (i.e., using the \c . or \c -> operator). This low-level feel
+ makes the structures straightforward to use and emphasizes that
+ these are simply parameters used by the style functions.
+
+ For an example demonstrating how style options can be used, see
+ the \l {widgets/styles}{Styles} example.
+
+ \sa QStyleOption, QStyleOptionQ3ListView, Q3ListViewItem
+*/
+
+/*!
+ \enum QStyleOptionQ3ListViewItem::Q3ListViewItemFeature
+
+ This enum describes the features a list view item can have.
+
+ \value None A standard item.
+ \value Expandable The item has children that can be shown.
+ \value MultiLine The item is more than one line tall.
+ \value Visible The item is visible.
+ \value ParentControl The item's parent is a type of item control (Q3CheckListItem::Controller).
+
+ \sa features, Q3ListViewItem::isVisible(), Q3ListViewItem::multiLinesEnabled(),
+ Q3ListViewItem::isExpandable()
+*/
+
+/*!
+ Constructs a QStyleOptionQ3ListViewItem, initializing the members
+ variables to their default values.
+*/
+
+QStyleOptionQ3ListViewItem::QStyleOptionQ3ListViewItem()
+ : QStyleOption(Version, SO_Q3ListViewItem), features(None), height(0), totalHeight(0),
+ itemY(0), childCount(0)
+{
+}
+
+/*!
+ \internal
+*/
+QStyleOptionQ3ListViewItem::QStyleOptionQ3ListViewItem(int version)
+ : QStyleOption(version, SO_Q3ListViewItem), features(None), height(0), totalHeight(0),
+ itemY(0), childCount(0)
+{
+}
+
+/*!
+ \fn QStyleOptionQ3ListViewItem::QStyleOptionQ3ListViewItem(const QStyleOptionQ3ListViewItem &other)
+
+ Constructs a copy of the \a other style option.
+*/
+
+/*!
+ \enum QStyleOptionQ3ListViewItem::StyleOptionType
+
+ This enum is used to hold information about the type of the style option, and
+ is defined for each QStyleOption subclass.
+
+ \value Type The type of style option provided (\l{SO_Q3ListViewItem} for this class).
+
+ The type is used internally by QStyleOption, its subclasses, and
+ qstyleoption_cast() to determine the type of style option. In
+ general you do not need to worry about this unless you want to
+ create your own QStyleOption subclass and your own styles.
+
+ \sa StyleOptionVersion
+*/
+
+/*!
+ \enum QStyleOptionQ3ListViewItem::StyleOptionVersion
+
+ This enum is used to hold information about the version of the style option, and
+ is defined for each QStyleOption subclass.
+
+ \value Version 1
+
+ The version is used by QStyleOption subclasses to implement
+ extensions without breaking compatibility. If you use
+ qstyleoption_cast(), you normally do not need to check it.
+
+ \sa StyleOptionType
+*/
+
+/*!
+ \variable QStyleOptionQ3ListViewItem::features
+ \brief the features for this item
+
+ This variable is a bitwise OR of the features of the item. The deafult value is \l None.
+
+ \sa Q3ListViewItemFeature
+*/
+
+/*!
+ \variable QStyleOptionQ3ListViewItem::height
+ \brief the height of the item
+
+ This doesn't include the height of the item's children. The default height is 0.
+
+ \sa Q3ListViewItem::height()
+*/
+
+/*!
+ \variable QStyleOptionQ3ListViewItem::totalHeight
+ \brief the total height of the item, including its children
+
+ The default total height is 0.
+
+ \sa Q3ListViewItem::totalHeight()
+*/
+
+/*!
+ \variable QStyleOptionQ3ListViewItem::itemY
+ \brief the Y-coordinate for the item
+
+ The default value is 0.
+
+ \sa Q3ListViewItem::itemPos()
+*/
+
+/*!
+ \variable QStyleOptionQ3ListViewItem::childCount
+ \brief the number of children the item has
+*/
+
+/*!
+ \class QStyleOptionQ3ListView
+ \brief The QStyleOptionQ3ListView class is used to describe the
+ parameters for drawing a Q3ListView.
+
+ This class is used for drawing the compatibility Q3ListView. \bold
+ {It is not recommended for new classes}.
+
+ QStyleOptionQ3ListView contains all the information that QStyle
+ functions need to draw Q3ListView.
+
+ For performance reasons, the access to the member variables is
+ direct (i.e., using the \c . or \c -> operator). This low-level feel
+ makes the structures straightforward to use and emphasizes that
+ these are simply parameters used by the style functions.
+
+ For an example demonstrating how style options can be used, see
+ the \l {widgets/styles}{Styles} example.
+
+ \sa QStyleOptionComplex, Q3ListView, QStyleOptionQ3ListViewItem
+*/
+
+/*!
+ Creates a QStyleOptionQ3ListView, initializing the members
+ variables to their default values.
+*/
+
+QStyleOptionQ3ListView::QStyleOptionQ3ListView()
+ : QStyleOptionComplex(Version, SO_Q3ListView), viewportBGRole(QPalette::Base),
+ sortColumn(0), itemMargin(0), treeStepSize(0), rootIsDecorated(false)
+{
+}
+
+/*!
+ \internal
+*/
+QStyleOptionQ3ListView::QStyleOptionQ3ListView(int version)
+ : QStyleOptionComplex(version, SO_Q3ListView), viewportBGRole(QPalette::Base),
+ sortColumn(0), itemMargin(0), treeStepSize(0), rootIsDecorated(false)
+{
+}
+
+/*!
+ \fn QStyleOptionQ3ListView::QStyleOptionQ3ListView(const QStyleOptionQ3ListView &other)
+
+ Constructs a copy of the \a other style option.
+*/
+
+/*!
+ \enum QStyleOptionQ3ListView::StyleOptionType
+
+ This enum is used to hold information about the type of the style option, and
+ is defined for each QStyleOption subclass.
+
+ \value Type The type of style option provided (\l{SO_Q3ListView} for this class).
+
+ The type is used internally by QStyleOption, its subclasses, and
+ qstyleoption_cast() to determine the type of style option. In
+ general you do not need to worry about this unless you want to
+ create your own QStyleOption subclass and your own styles.
+
+ \sa StyleOptionVersion
+*/
+
+/*!
+ \enum QStyleOptionQ3ListView::StyleOptionVersion
+
+ This enum is used to hold information about the version of the style option, and
+ is defined for each QStyleOption subclass.
+
+ \value Version 1
+
+ The version is used by QStyleOption subclasses to implement
+ extensions without breaking compatibility. If you use
+ qstyleoption_cast(), you normally do not need to check it.
+
+ \sa StyleOptionType
+*/
+
+/*!
+ \variable QStyleOptionQ3ListView::items
+ \brief a list of items in the Q3ListView
+
+ This is a list of \l {QStyleOptionQ3ListViewItem}s. The first item
+ can be used for most of the calculation that are needed for
+ drawing a list view. Any additional items are the children of
+ this first item, which may be used for additional information.
+
+ \sa QStyleOptionQ3ListViewItem
+*/
+
+/*!
+ \variable QStyleOptionQ3ListView::viewportPalette
+ \brief the palette of Q3ListView's viewport
+
+ By default, the application's default palette is used.
+*/
+
+/*!
+ \variable QStyleOptionQ3ListView::viewportBGRole
+ \brief the background role of Q3ListView's viewport
+
+ The default value is QPalette::Base.
+
+ \sa QWidget::backgroundRole()
+*/
+
+/*!
+ \variable QStyleOptionQ3ListView::sortColumn
+ \brief the sort column of the list view
+
+ The default value is 0.
+
+ \sa Q3ListView::sortColumn()
+*/
+
+/*!
+ \variable QStyleOptionQ3ListView::itemMargin
+ \brief the margin for items in the list view
+
+ The default value is 0.
+
+ \sa Q3ListView::itemMargin()
+*/
+
+/*!
+ \variable QStyleOptionQ3ListView::treeStepSize
+ \brief the number of pixel to offset children items from their
+ parents
+
+ The default value is 0.
+
+ \sa Q3ListView::treeStepSize()
+*/
+
+/*!
+ \variable QStyleOptionQ3ListView::rootIsDecorated
+ \brief whether root items are decorated
+
+ The default value is false.
+
+ \sa Q3ListView::rootIsDecorated()
+*/
+
+/*!
+ \class QStyleOptionQ3DockWindow
+ \brief The QStyleOptionQ3DockWindow class is used to describe the
+ parameters for drawing various parts of a Q3DockWindow.
+
+ This class is used for drawing the old Q3DockWindow and its
+ parts. \bold {It is not recommended for new classes}.
+
+ QStyleOptionQ3DockWindow contains all the information that QStyle
+ functions need to draw Q3DockWindow and its parts.
+
+ For performance reasons, the access to the member variables is
+ direct (i.e., using the \c . or \c -> operator). This low-level feel
+ makes the structures straightforward to use and emphasizes that
+ these are simply parameters used by the style functions.
+
+ For an example demonstrating how style options can be used, see
+ the \l {widgets/styles}{Styles} example.
+
+ \sa QStyleOption, Q3DockWindow
+*/
+
+/*!
+ Constructs a QStyleOptionQ3DockWindow, initializing the member
+ variables to their default values.
+*/
+
+QStyleOptionQ3DockWindow::QStyleOptionQ3DockWindow()
+ : QStyleOption(Version, SO_Q3DockWindow), docked(false), closeEnabled(false)
+{
+}
+
+/*!
+ \internal
+*/
+QStyleOptionQ3DockWindow::QStyleOptionQ3DockWindow(int version)
+ : QStyleOption(version, SO_Q3DockWindow), docked(false), closeEnabled(false)
+{
+}
+
+/*!
+ \fn QStyleOptionQ3DockWindow::QStyleOptionQ3DockWindow(const QStyleOptionQ3DockWindow &other)
+
+ Constructs a copy of the \a other style option.
+*/
+
+/*!
+ \enum QStyleOptionQ3DockWindow::StyleOptionType
+
+ This enum is used to hold information about the type of the style option, and
+ is defined for each QStyleOption subclass.
+
+ \value Type The type of style option provided (\l{SO_Q3DockWindow} for this class).
+
+ The type is used internally by QStyleOption, its subclasses, and
+ qstyleoption_cast() to determine the type of style option. In
+ general you do not need to worry about this unless you want to
+ create your own QStyleOption subclass and your own styles.
+
+ \sa StyleOptionVersion
+*/
+
+/*!
+ \enum QStyleOptionQ3DockWindow::StyleOptionVersion
+
+ This enum is used to hold information about the version of the style option, and
+ is defined for each QStyleOption subclass.
+
+ \value Version 1
+
+ The version is used by QStyleOption subclasses to implement
+ extensions without breaking compatibility. If you use
+ qstyleoption_cast(), you normally do not need to check it.
+
+ \sa StyleOptionType
+*/
+
+/*!
+ \variable QStyleOptionQ3DockWindow::docked
+ \brief whether the dock window is currently docked
+
+ The default value is false.
+*/
+
+/*!
+ \variable QStyleOptionQ3DockWindow::closeEnabled
+ \brief whether the dock window has a close button
+
+ The default value is false.
+*/
+
+/*!
+ \class QStyleOptionDockWidget
+ \brief The QStyleOptionDockWidget class is used to describe the
+ parameters for drawing a dock widget.
+
+ QStyleOptionDockWidget contains all the information that QStyle
+ functions need to draw graphical elements like QDockWidget.
+
+ For performance reasons, the access to the member variables is
+ direct (i.e., using the \c . or \c -> operator). This low-level feel
+ makes the structures straightforward to use and emphasizes that
+ these are simply parameters used by the style functions.
+
+ For an example demonstrating how style options can be used, see
+ the \l {widgets/styles}{Styles} example.
+
+ \sa QStyleOption
+*/
+
+/*!
+ Constructs a QStyleOptionDockWidget, initializing the member
+ variables to their default values.
+*/
+
+QStyleOptionDockWidget::QStyleOptionDockWidget()
+ : QStyleOption(Version, SO_DockWidget), movable(false)
+{
+}
+
+/*!
+ \internal
+*/
+QStyleOptionDockWidget::QStyleOptionDockWidget(int version)
+ : QStyleOption(version, SO_DockWidget), closable(false),
+ movable(false), floatable(false)
+{
+}
+
+QStyleOptionDockWidgetV2::QStyleOptionDockWidgetV2()
+ : QStyleOptionDockWidget(Version), verticalTitleBar(false)
+{
+}
+
+QStyleOptionDockWidgetV2::QStyleOptionDockWidgetV2(
+ const QStyleOptionDockWidget &other)
+ : QStyleOptionDockWidget(Version)
+{
+ (void)QStyleOptionDockWidgetV2::operator=(other);
+}
+
+QStyleOptionDockWidgetV2 &QStyleOptionDockWidgetV2::operator = (
+ const QStyleOptionDockWidget &other)
+{
+ QStyleOptionDockWidget::operator=(other);
+ const QStyleOptionDockWidgetV2 *v2
+ = qstyleoption_cast<const QStyleOptionDockWidgetV2*>(&other);
+ verticalTitleBar = v2 ? v2->verticalTitleBar : false;
+ return *this;
+}
+
+QStyleOptionDockWidgetV2::QStyleOptionDockWidgetV2(int version)
+ : QStyleOptionDockWidget(version), verticalTitleBar(false)
+{
+}
+
+/*!
+ \fn QStyleOptionDockWidget::QStyleOptionDockWidget(const QStyleOptionDockWidget &other)
+
+ Constructs a copy of the \a other style option.
+*/
+
+/*!
+ \enum QStyleOptionDockWidget::StyleOptionType
+
+ This enum is used to hold information about the type of the style option, and
+ is defined for each QStyleOption subclass.
+
+ \value Type The type of style option provided (\l{SO_DockWidget} for this class).
+
+ The type is used internally by QStyleOption, its subclasses, and
+ qstyleoption_cast() to determine the type of style option. In
+ general you do not need to worry about this unless you want to
+ create your own QStyleOption subclass and your own styles.
+
+ \sa StyleOptionVersion
+*/
+
+/*!
+ \enum QStyleOptionDockWidget::StyleOptionVersion
+
+ This enum is used to hold information about the version of the style option, and
+ is defined for each QStyleOption subclass.
+
+ \value Version 1
+
+ The version is used by QStyleOption subclasses to implement
+ extensions without breaking compatibility. If you use
+ qstyleoption_cast(), you normally do not need to check it.
+
+ \sa StyleOptionType
+*/
+
+/*!
+ \variable QStyleOptionDockWidget::title
+ \brief the title of the dock window
+
+ The default value is an empty string.
+*/
+
+/*!
+ \variable QStyleOptionDockWidget::closable
+ \brief whether the dock window is closable
+
+ The default value is true.
+*/
+
+/*!
+ \variable QStyleOptionDockWidget::movable
+ \brief whether the dock window is movable
+
+ The default value is false.
+*/
+
+/*!
+ \variable QStyleOptionDockWidget::floatable
+ \brief whether the dock window is floatable
+
+ The default value is true.
+*/
+
+/*!
+ \class QStyleOptionToolButton
+ \brief The QStyleOptionToolButton class is used to describe the
+ parameters for drawing a tool button.
+
+ QStyleOptionToolButton contains all the information that QStyle
+ functions need to draw QToolButton.
+
+ For performance reasons, the access to the member variables is
+ direct (i.e., using the \c . or \c -> operator). This low-level feel
+ makes the structures straightforward to use and emphasizes that
+ these are simply parameters used by the style functions.
+
+ For an example demonstrating how style options can be used, see
+ the \l {widgets/styles}{Styles} example.
+
+ \sa QStyleOption, QStyleOptionComplex, QStyleOptionButton
+*/
+
+/*!
+ \enum QStyleOptionToolButton::ToolButtonFeature
+ Describes the various features that a tool button can have.
+
+ \value None A normal tool button.
+ \value Arrow The tool button is an arrow.
+ \value Menu The tool button has a menu.
+ \value PopupDelay There is a delay to showing the menu.
+ \value HasMenu The button has a popup menu.
+ \value MenuButtonPopup The button should display an arrow to
+ indicate that a menu is present.
+
+ \sa features, QToolButton::toolButtonStyle(), QToolButton::popupMode()
+*/
+
+/*!
+ Constructs a QStyleOptionToolButton, initializing the members
+ variables to their default values.
+*/
+
+QStyleOptionToolButton::QStyleOptionToolButton()
+ : QStyleOptionComplex(Version, SO_ToolButton), features(None), arrowType(Qt::DownArrow)
+ , toolButtonStyle(Qt::ToolButtonIconOnly)
+{
+}
+
+/*!
+ \internal
+*/
+QStyleOptionToolButton::QStyleOptionToolButton(int version)
+ : QStyleOptionComplex(version, SO_ToolButton), features(None), arrowType(Qt::DownArrow)
+ , toolButtonStyle(Qt::ToolButtonIconOnly)
+
+{
+}
+
+/*!
+ \fn QStyleOptionToolButton::QStyleOptionToolButton(const QStyleOptionToolButton &other)
+
+ Constructs a copy of the \a other style option.
+*/
+
+/*!
+ \enum QStyleOptionToolButton::StyleOptionType
+
+ This enum is used to hold information about the type of the style option, and
+ is defined for each QStyleOption subclass.
+
+ \value Type The type of style option provided (\l{SO_ToolButton} for this class).
+
+ The type is used internally by QStyleOption, its subclasses, and
+ qstyleoption_cast() to determine the type of style option. In
+ general you do not need to worry about this unless you want to
+ create your own QStyleOption subclass and your own styles.
+
+ \sa StyleOptionVersion
+*/
+
+/*!
+ \enum QStyleOptionToolButton::StyleOptionVersion
+
+ This enum is used to hold information about the version of the style option, and
+ is defined for each QStyleOption subclass.
+
+ \value Version 1
+
+ The version is used by QStyleOption subclasses to implement
+ extensions without breaking compatibility. If you use
+ qstyleoption_cast(), you normally do not need to check it.
+
+ \sa StyleOptionType
+*/
+
+/*!
+ \variable QStyleOptionToolButton::features
+ \brief an OR combination of the tool button's features
+
+ The default value is \l None.
+
+ \sa ToolButtonFeature
+*/
+
+/*!
+ \variable QStyleOptionToolButton::icon
+ \brief the icon for the tool button
+
+ The default value is an empty icon, i.e. an icon with neither a
+ pixmap nor a filename.
+
+ \sa iconSize
+*/
+
+/*!
+ \variable QStyleOptionToolButton::iconSize
+ \brief the size of the icon for the tool button
+
+ The default value is QSize(-1, -1), i.e. an invalid size.
+*/
+
+/*!
+ \variable QStyleOptionToolButton::text
+ \brief the text of the tool button
+
+ This value is only used if toolButtonStyle is
+ Qt::ToolButtonTextUnderIcon, Qt::ToolButtonTextBesideIcon, or
+ Qt::ToolButtonTextOnly. The default value is an empty string.
+*/
+
+/*!
+ \variable QStyleOptionToolButton::arrowType
+ \brief the direction of the arrow for the tool button
+
+ This value is only used if \l features includes \l Arrow. The
+ default value is Qt::DownArrow.
+*/
+
+/*!
+ \variable QStyleOptionToolButton::toolButtonStyle
+ \brief a Qt::ToolButtonStyle value describing the appearance of
+ the tool button
+
+ The default value is Qt::ToolButtonIconOnly.
+
+ \sa QToolButton::toolButtonStyle()
+*/
+
+/*!
+ \variable QStyleOptionToolButton::pos
+ \brief the position of the tool button
+
+ The default value is a null point, i.e. (0, 0)
+*/
+
+/*!
+ \variable QStyleOptionToolButton::font
+ \brief the font that is used for the text
+
+ This value is only used if toolButtonStyle is
+ Qt::ToolButtonTextUnderIcon, Qt::ToolButtonTextBesideIcon, or
+ Qt::ToolButtonTextOnly. By default, the application's default font
+ is used.
+*/
+
+/*!
+ \class QStyleOptionComboBox
+ \brief The QStyleOptionComboBox class is used to describe the
+ parameter for drawing a combobox.
+
+ QStyleOptionButton contains all the information that QStyle
+ functions need to draw QComboBox.
+
+ For performance reasons, the access to the member variables is
+ direct (i.e., using the \c . or \c -> operator). This low-level feel
+ makes the structures straightforward to use and emphasizes that
+ these are simply parameters used by the style functions.
+
+ For an example demonstrating how style options can be used, see
+ the \l {widgets/styles}{Styles} example.
+
+ \sa QStyleOption, QStyleOptionComplex, QComboBox
+*/
+
+/*!
+ Creates a QStyleOptionComboBox, initializing the members variables
+ to their default values.
+*/
+
+QStyleOptionComboBox::QStyleOptionComboBox()
+ : QStyleOptionComplex(Version, SO_ComboBox), editable(false), frame(true)
+{
+}
+
+/*!
+ \internal
+*/
+QStyleOptionComboBox::QStyleOptionComboBox(int version)
+ : QStyleOptionComplex(version, SO_ComboBox), editable(false), frame(true)
+{
+}
+
+/*!
+ \fn QStyleOptionComboBox::QStyleOptionComboBox(const QStyleOptionComboBox &other)
+
+ Constructs a copy of the \a other style option.
+*/
+
+/*!
+ \enum QStyleOptionComboBox::StyleOptionType
+
+ This enum is used to hold information about the type of the style option, and
+ is defined for each QStyleOption subclass.
+
+ \value Type The type of style option provided (\l{SO_ComboBox} for this class).
+
+ The type is used internally by QStyleOption, its subclasses, and
+ qstyleoption_cast() to determine the type of style option. In
+ general you do not need to worry about this unless you want to
+ create your own QStyleOption subclass and your own styles.
+
+ \sa StyleOptionVersion
+*/
+
+/*!
+ \enum QStyleOptionComboBox::StyleOptionVersion
+
+ This enum is used to hold information about the version of the style option, and
+ is defined for each QStyleOption subclass.
+
+ \value Version 1
+
+ The version is used by QStyleOption subclasses to implement
+ extensions without breaking compatibility. If you use
+ qstyleoption_cast(), you normally do not need to check it.
+
+ \sa StyleOptionType
+*/
+
+/*!
+ \variable QStyleOptionComboBox::editable
+ \brief whether or not the combobox is editable or not
+
+ the default
+ value is false
+
+ \sa QComboBox::isEditable()
+*/
+
+
+/*!
+ \variable QStyleOptionComboBox::frame
+ \brief whether the combo box has a frame
+
+ The default value is true.
+*/
+
+/*!
+ \variable QStyleOptionComboBox::currentText
+ \brief the text for the current item of the combo box
+
+ The default value is an empty string.
+*/
+
+/*!
+ \variable QStyleOptionComboBox::currentIcon
+ \brief the icon for the current item of the combo box
+
+ The default value is an empty icon, i.e. an icon with neither a
+ pixmap nor a filename.
+*/
+
+/*!
+ \variable QStyleOptionComboBox::iconSize
+ \brief the icon size for the current item of the combo box
+
+ The default value is QSize(-1, -1), i.e. an invalid size.
+*/
+
+/*!
+ \variable QStyleOptionComboBox::popupRect
+ \brief the popup rectangle for the combobox
+
+ The default value is a null rectangle, i.e. a rectangle with both
+ the width and the height set to 0.
+
+ This variable is currently unused. You can safely ignore it.
+
+ \sa QStyle::SC_ComboBoxListBoxPopup
+*/
+
+/*!
+ \class QStyleOptionToolBox
+ \brief The QStyleOptionToolBox class is used to describe the
+ parameters needed for drawing a tool box.
+
+ QStyleOptionToolBox contains all the information that QStyle
+ functions need to draw QToolBox.
+
+ For performance reasons, the access to the member variables is
+ direct (i.e., using the \c . or \c -> operator). This low-level feel
+ makes the structures straightforward to use and emphasizes that
+ these are simply parameters used by the style functions.
+
+ For an example demonstrating how style options can be used, see
+ the \l {widgets/styles}{Styles} example.
+
+ \sa QStyleOption, QToolBox
+*/
+
+/*!
+ Creates a QStyleOptionToolBox, initializing the members variables
+ to their default values.
+*/
+
+QStyleOptionToolBox::QStyleOptionToolBox()
+ : QStyleOption(Version, SO_ToolBox)
+{
+}
+
+/*!
+ \internal
+*/
+QStyleOptionToolBox::QStyleOptionToolBox(int version)
+ : QStyleOption(version, SO_ToolBox)
+{
+}
+
+/*!
+ \fn QStyleOptionToolBox::QStyleOptionToolBox(const QStyleOptionToolBox &other)
+
+ Constructs a copy of the \a other style option.
+*/
+
+/*!
+ \enum QStyleOptionToolBox::StyleOptionType
+
+ This enum is used to hold information about the type of the style option, and
+ is defined for each QStyleOption subclass.
+
+ \value Type The type of style option provided (\l{SO_ToolBox} for this class).
+
+ The type is used internally by QStyleOption, its subclasses, and
+ qstyleoption_cast() to determine the type of style option. In
+ general you do not need to worry about this unless you want to
+ create your own QStyleOption subclass and your own styles.
+
+ \sa StyleOptionVersion
+*/
+
+/*!
+ \enum QStyleOptionToolBox::StyleOptionVersion
+
+ This enum is used to hold information about the version of the style option, and
+ is defined for each QStyleOption subclass.
+
+ \value Version 1
+
+ The version is used by QStyleOption subclasses to implement
+ extensions without breaking compatibility. If you use
+ qstyleoption_cast(), you normally do not need to check it.
+
+ \sa StyleOptionType
+*/
+
+/*!
+ \variable QStyleOptionToolBox::icon
+ \brief the icon for the tool box tab
+
+ The default value is an empty icon, i.e. an icon with neither a
+ pixmap nor a filename.
+*/
+
+/*!
+ \variable QStyleOptionToolBox::text
+ \brief the text for the tool box tab
+
+ The default value is an empty string.
+*/
+
+/*!
+ \class QStyleOptionToolBoxV2
+ \brief The QStyleOptionToolBoxV2 class is used to describe the parameters necessary for drawing a frame in Qt 4.3 or above.
+
+ \since 4.3
+ QStyleOptionToolBoxV2 inherits QStyleOptionToolBox which is used for
+ drawing the tabs in a QToolBox.
+
+ An instance of the QStyleOptionToolBoxV2 class has
+ \l{QStyleOption::type} {type} SO_ToolBox and
+ \l{QStyleOption::version} {version} 2. The type is used
+ internally by QStyleOption, its subclasses, and
+ qstyleoption_cast() to determine the type of style option. In
+ general you do not need to worry about this unless you want to
+ create your own QStyleOption subclass and your own styles. The
+ version is used by QStyleOption subclasses to implement extensions
+ without breaking compatibility. If you use qstyleoption_cast(),
+ you normally do not need to check it.
+
+ If you create your own QStyle subclass, you should handle both
+ QStyleOptionToolBox and QStyleOptionToolBoxV2.
+
+ \sa QStyleOptionToolBox, QStyleOption
+*/
+
+/*!
+ Contsructs a QStyleOptionToolBoxV2 object.
+*/
+QStyleOptionToolBoxV2::QStyleOptionToolBoxV2()
+ : QStyleOptionToolBox(Version), position(Beginning), selectedPosition(NotAdjacent)
+{
+}
+
+/*!
+ \fn QStyleOptionToolBoxV2::QStyleOptionToolBoxV2(const QStyleOptionToolBoxV2 &other)
+
+ Constructs a QStyleOptionToolBoxV2 copy of the \a other style option.
+*/
+
+/*!
+ \internal
+*/
+QStyleOptionToolBoxV2::QStyleOptionToolBoxV2(int version)
+ : QStyleOptionToolBox(version), position(Beginning), selectedPosition(NotAdjacent)
+{
+}
+
+/*!
+ Constructs a QStyleOptionToolBoxV2 copy of the \a other style option
+ which can be either of the QStyleOptionToolBoxV2 or
+ QStyleOptionToolBox types.
+
+ If the \a other style option's version is 1, the new style
+ option's \l{QStyleOptionTab::position} {position} value is set to
+ \l QStyleOptionToolBoxV2::Beginning and \l selectedPosition is set
+ to \l QStyleOptionToolBoxV2::NotAdjacent. If its version is 2, the
+ \l{QStyleOptionTab::position} {position} selectedPosition values
+ are simply copied to the new style option.
+
+ \sa version
+*/
+QStyleOptionToolBoxV2::QStyleOptionToolBoxV2(const QStyleOptionToolBox &other)
+{
+ QStyleOptionToolBox::operator=(other);
+
+ const QStyleOptionToolBoxV2 *f2 = qstyleoption_cast<const QStyleOptionToolBoxV2 *>(&other);
+ position = f2 ? f2->position : Beginning;
+ selectedPosition = f2 ? f2->selectedPosition : NotAdjacent;
+ version = Version;
+}
+
+/*!
+ Assigns the \a other style option to this style option. The \a
+ other style option can be either of the QStyleOptionToolBoxV2 or
+ QStyleOptionToolBox types.
+
+ If the \a{other} style option's version is 1, this style option's
+ \l{QStyleOptionTab::position} {position} and \l selectedPosition
+ values are set to \l QStyleOptionToolBoxV2::Beginning and \l
+ QStyleOptionToolBoxV2::NotAdjacent respectively. If its
+ \l{QStyleOption::version} {version} is 2, these values are simply
+ copied to this style option.
+*/
+QStyleOptionToolBoxV2 &QStyleOptionToolBoxV2::operator=(const QStyleOptionToolBox &other)
+{
+ QStyleOptionToolBox::operator=(other);
+
+ const QStyleOptionToolBoxV2 *f2 = qstyleoption_cast<const QStyleOptionToolBoxV2 *>(&other);
+ position = f2 ? f2->position : Beginning;
+ selectedPosition = f2 ? f2->selectedPosition : NotAdjacent;
+ version = Version;
+ return *this;
+}
+
+
+/*!
+ \enum QStyleOptionToolBoxV2::SelectedPosition
+
+ This enum describes the position of the selected tab. Some styles
+ need to draw a tab differently depending on whether or not it is
+ adjacent to the selected tab.
+
+ \value NotAdjacent The tab is not adjacent to a selected tab (or is the selected tab).
+ \value NextIsSelected The next tab (typically the tab on the right) is selected.
+ \value PreviousIsSelected The previous tab (typically the tab on the left) is selected.
+
+ \sa selectedPosition
+*/
+
+/*!
+ \enum QStyleOptionToolBoxV2::StyleOptionVersion
+
+ This enum holds the version of this style option
+
+ \value Version 2
+*/
+
+/*!
+ \enum QStyleOptionToolBoxV2::TabPosition
+
+ This enum describes tab positions relative to other tabs.
+
+ \value Beginning The tab is the first (i.e., top-most) tab in
+ the toolbox.
+ \value Middle The tab is placed in the middle of the toolbox.
+ \value End The tab is placed at the bottom of the toolbox.
+ \value OnlyOneTab There is only one tab in the toolbox.
+*/
+
+/*!
+ \variable QStyleOptionToolBoxV2::selectedPosition
+ \brief the position of the selected tab in relation to this tab
+
+ The default value is NotAdjacent, i.e. the tab is not adjacent to
+ a selected tab nor is it the selected tab.
+*/
+
+#ifndef QT_NO_RUBBERBAND
+/*!
+ \class QStyleOptionRubberBand
+ \brief The QStyleOptionRubberBand class is used to describe the
+ parameters needed for drawing a rubber band.
+
+ QStyleOptionRubberBand contains all the information that
+ QStyle functions need to draw QRubberBand.
+
+ For performance reasons, the access to the member variables is
+ direct (i.e., using the \c . or \c -> operator). This low-level feel
+ makes the structures straightforward to use and emphasizes that
+ these are simply parameters used by the style functions.
+
+ For an example demonstrating how style options can be used, see
+ the \l {widgets/styles}{Styles} example.
+
+ \sa QStyleOption, QRubberBand
+*/
+
+/*!
+ Creates a QStyleOptionRubberBand, initializing the members
+ variables to their default values.
+*/
+
+QStyleOptionRubberBand::QStyleOptionRubberBand()
+ : QStyleOption(Version, SO_RubberBand), shape(QRubberBand::Line), opaque(false)
+{
+}
+
+/*!
+ \internal
+*/
+QStyleOptionRubberBand::QStyleOptionRubberBand(int version)
+ : QStyleOption(version, SO_RubberBand), shape(QRubberBand::Line), opaque(false)
+{
+}
+
+/*!
+ \fn QStyleOptionRubberBand::QStyleOptionRubberBand(const QStyleOptionRubberBand &other)
+
+ Constructs a copy of the \a other style option.
+*/
+
+/*!
+ \enum QStyleOptionRubberBand::StyleOptionType
+
+ This enum is used to hold information about the type of the style option, and
+ is defined for each QStyleOption subclass.
+
+ \value Type The type of style option provided (\l{SO_RubberBand} for this class).
+
+ The type is used internally by QStyleOption, its subclasses, and
+ qstyleoption_cast() to determine the type of style option. In
+ general you do not need to worry about this unless you want to
+ create your own QStyleOption subclass and your own styles.
+
+ \sa StyleOptionVersion
+*/
+
+/*!
+ \enum QStyleOptionRubberBand::StyleOptionVersion
+
+ This enum is used to hold information about the version of the style option, and
+ is defined for each QStyleOption subclass.
+
+ \value Version 1
+
+ The version is used by QStyleOption subclasses to implement
+ extensions without breaking compatibility. If you use
+ qstyleoption_cast(), you normally do not need to check it.
+
+ \sa StyleOptionType
+*/
+
+/*!
+ \variable QStyleOptionRubberBand::shape
+ \brief the shape of the rubber band
+
+ The default shape is QRubberBand::Line.
+*/
+
+/*!
+ \variable QStyleOptionRubberBand::opaque
+ \brief whether the rubber band is required to be drawn in an opaque style
+
+ The default value is true.
+*/
+#endif // QT_NO_RUBBERBAND
+
+/*!
+ \class QStyleOptionTitleBar
+ \brief The QStyleOptionTitleBar class is used to describe the
+ parameters for drawing a title bar.
+
+ QStyleOptionTitleBar contains all the information that QStyle
+ functions need to draw the title bars of QWorkspace's MDI
+ children.
+
+ For performance reasons, the access to the member variables is
+ direct (i.e., using the \c . or \c -> operator). This low-level feel
+ makes the structures straightforward to use and emphasizes that
+ these are simply parameters used by the style functions.
+
+ For an example demonstrating how style options can be used, see
+ the \l {widgets/styles}{Styles} example.
+
+ \sa QStyleOption, QStyleOptionComplex, QWorkspace
+*/
+
+/*!
+ Constructs a QStyleOptionTitleBar, initializing the members
+ variables to their default values.
+*/
+
+QStyleOptionTitleBar::QStyleOptionTitleBar()
+ : QStyleOptionComplex(Version, SO_TitleBar), titleBarState(0), titleBarFlags(0)
+{
+}
+
+/*!
+ \fn QStyleOptionTitleBar::QStyleOptionTitleBar(const QStyleOptionTitleBar &other)
+
+ Constructs a copy of the \a other style option.
+*/
+
+/*!
+ \enum QStyleOptionTitleBar::StyleOptionType
+
+ This enum is used to hold information about the type of the style option, and
+ is defined for each QStyleOption subclass.
+
+ \value Type The type of style option provided (\l{SO_TitleBar} for this class).
+
+ The type is used internally by QStyleOption, its subclasses, and
+ qstyleoption_cast() to determine the type of style option. In
+ general you do not need to worry about this unless you want to
+ create your own QStyleOption subclass and your own styles.
+
+ \sa StyleOptionVersion
+*/
+
+/*!
+ \enum QStyleOptionTitleBar::StyleOptionVersion
+
+ This enum is used to hold information about the version of the style option, and
+ is defined for each QStyleOption subclass.
+
+ \value Version 1
+
+ The version is used by QStyleOption subclasses to implement
+ extensions without breaking compatibility. If you use
+ qstyleoption_cast(), you normally do not need to check it.
+
+ \sa StyleOptionType
+*/
+
+/*!
+ \internal
+*/
+QStyleOptionTitleBar::QStyleOptionTitleBar(int version)
+ : QStyleOptionComplex(version, SO_TitleBar), titleBarState(0), titleBarFlags(0)
+{
+}
+
+
+/*!
+ \variable QStyleOptionTitleBar::text
+ \brief the text of the title bar
+
+ The default value is an empty string.
+*/
+
+/*!
+ \variable QStyleOptionTitleBar::icon
+ \brief the icon for the title bar
+
+ The default value is an empty icon, i.e. an icon with neither a
+ pixmap nor a filename.
+*/
+
+/*!
+ \variable QStyleOptionTitleBar::titleBarState
+ \brief the state of the title bar
+
+ This is basically the window state of the underlying widget. The
+ default value is 0.
+
+ \sa QWidget::windowState()
+*/
+
+/*!
+ \variable QStyleOptionTitleBar::titleBarFlags
+ \brief the widget flags for the title bar
+
+ The default value is Qt::Widget.
+
+ \sa Qt::WindowFlags
+*/
+
+/*!
+ \class QStyleOptionViewItem
+ \brief The QStyleOptionViewItem class is used to describe the
+ parameters used to draw an item in a view widget.
+
+ QStyleOptionViewItem contains all the information that QStyle
+ functions need to draw the items for Qt's model/view classes.
+
+ For performance reasons, the access to the member variables is
+ direct (i.e., using the \c . or \c -> operator). This low-level feel
+ makes the structures straightforward to use and emphasizes that
+ these are simply parameters used by the style functions.
+
+ For an example demonstrating how style options can be used, see
+ the \l {widgets/styles}{Styles} example.
+
+ \sa QStyleOption, {model-view-programming.html}{Model/View
+ Programming}
+*/
+
+/*!
+ \enum QStyleOptionViewItem::Position
+
+ This enum describes the position of the item's decoration.
+
+ \value Left On the left of the text.
+ \value Right On the right of the text.
+ \value Top Above the text.
+ \value Bottom Below the text.
+
+ \sa decorationPosition
+*/
+
+/*!
+ \variable QStyleOptionViewItem::showDecorationSelected
+ \brief whether the decoration should be highlighted on selected
+ items
+
+ If this option is true, the branch and any decorations on selected items
+ should be highlighted, indicating that the item is selected; otherwise, no
+ highlighting is required. The default value is false.
+
+ \sa QStyle::SH_ItemView_ShowDecorationSelected, QAbstractItemView
+*/
+
+/*!
+ \variable QStyleOptionViewItem::textElideMode
+ \brief where ellipsis should be added for text that is too long to fit
+ into an item
+
+ The default value is Qt::ElideMiddle, i.e. the ellipsis appears in
+ the middle of the text.
+
+ \sa Qt::TextElideMode, QStyle::SH_ItemView_EllipsisLocation
+*/
+
+/*!
+ Constructs a QStyleOptionViewItem, initializing the members
+ variables to their default values.
+*/
+
+QStyleOptionViewItem::QStyleOptionViewItem()
+ : QStyleOption(Version, SO_ViewItem),
+ displayAlignment(Qt::AlignLeft), decorationAlignment(Qt::AlignLeft),
+ textElideMode(Qt::ElideMiddle), decorationPosition(Left),
+ showDecorationSelected(false)
+{
+}
+
+/*!
+ \internal
+*/
+QStyleOptionViewItem::QStyleOptionViewItem(int version)
+ : QStyleOption(version, SO_ViewItem),
+ displayAlignment(Qt::AlignLeft), decorationAlignment(Qt::AlignLeft),
+ textElideMode(Qt::ElideMiddle), decorationPosition(Left),
+ showDecorationSelected(false)
+{
+}
+
+/*!
+ \fn QStyleOptionViewItem::QStyleOptionViewItem(const QStyleOptionViewItem &other)
+
+ Constructs a copy of the \a other style option.
+*/
+
+/*!
+ \enum QStyleOptionViewItem::StyleOptionType
+
+ This enum is used to hold information about the type of the style option, and
+ is defined for each QStyleOption subclass.
+
+ \value Type The type of style option provided (\l{SO_ViewItem} for this class).
+
+ The type is used internally by QStyleOption, its subclasses, and
+ qstyleoption_cast() to determine the type of style option. In
+ general you do not need to worry about this unless you want to
+ create your own QStyleOption subclass and your own styles.
+
+ \sa StyleOptionVersion
+*/
+
+/*!
+ \enum QStyleOptionViewItem::StyleOptionVersion
+
+ This enum is used to hold information about the version of the style option, and
+ is defined for each QStyleOption subclass.
+
+ \value Version 1
+
+ The version is used by QStyleOption subclasses to implement
+ extensions without breaking compatibility. If you use
+ qstyleoption_cast(), you normally do not need to check it.
+
+ \sa StyleOptionType
+*/
+
+/*!
+ \variable QStyleOptionViewItem::displayAlignment
+ \brief the alignment of the display value for the item
+
+ The default value is Qt::AlignLeft.
+*/
+
+/*!
+ \variable QStyleOptionViewItem::decorationAlignment
+ \brief the alignment of the decoration for the item
+
+ The default value is Qt::AlignLeft.
+*/
+
+/*!
+ \variable QStyleOptionViewItem::decorationPosition
+ \brief the position of the decoration for the item
+
+ The default value is \l Left.
+
+ \sa Position
+*/
+
+/*!
+ \variable QStyleOptionViewItem::decorationSize
+ \brief the size of the decoration for the item
+
+ The default value is QSize(-1, -1), i.e. an invalid size.
+
+ \sa decorationAlignment, decorationPosition
+*/
+
+/*!
+ \variable QStyleOptionViewItem::font
+ \brief the font used for the item
+
+ By default, the application's default font is used.
+
+ \sa QFont
+*/
+
+/*!
+ \fn T qstyleoption_cast<T>(const QStyleOption *option)
+ \relates QStyleOption
+
+ Returns a T or 0 depending on the \l{QStyleOption::type}{type} and
+ \l{QStyleOption::version}{version} of the given \a option.
+
+ Example:
+
+ \snippet doc/src/snippets/qstyleoption/main.cpp 4
+
+ \sa QStyleOption::type, QStyleOption::version
+*/
+
+/*!
+ \fn T qstyleoption_cast<T>(QStyleOption *option)
+ \overload
+ \relates QStyleOption
+
+ Returns a T or 0 depending on the type of the given \a option.
+*/
+
+#ifndef QT_NO_TABWIDGET
+/*!
+ \class QStyleOptionTabWidgetFrame
+ \brief The QStyleOptionTabWidgetFrame class is used to describe the
+ parameters for drawing the frame around a tab widget.
+
+ QStyleOptionTabWidgetFrame contains all the information that
+ QStyle functions need to draw the frame around QTabWidget.
+
+ For performance reasons, the access to the member variables is
+ direct (i.e., using the \c . or \c -> operator). This low-level feel
+ makes the structures straightforward to use and emphasizes that
+ these are simply parameters used by the style functions.
+
+ For an example demonstrating how style options can be used, see
+ the \l {widgets/styles}{Styles} example.
+
+ \sa QStyleOption, QTabWidget
+*/
+
+/*!
+ Constructs a QStyleOptionTabWidgetFrame, initializing the members
+ variables to their default values.
+*/
+QStyleOptionTabWidgetFrame::QStyleOptionTabWidgetFrame()
+ : QStyleOption(Version, SO_TabWidgetFrame), lineWidth(0), midLineWidth(0),
+ shape(QTabBar::RoundedNorth)
+{
+}
+
+/*!
+ \fn QStyleOptionTabWidgetFrame::QStyleOptionTabWidgetFrame(const QStyleOptionTabWidgetFrame &other)
+
+ Constructs a copy of \a other.
+*/
+
+/*! \internal */
+QStyleOptionTabWidgetFrame::QStyleOptionTabWidgetFrame(int version)
+ : QStyleOption(version, SO_TabWidgetFrame), lineWidth(0), midLineWidth(0),
+ shape(QTabBar::RoundedNorth)
+{
+}
+
+/*!
+ \enum QStyleOptionTabWidgetFrame::StyleOptionType
+
+ This enum is used to hold information about the type of the style option, and
+ is defined for each QStyleOption subclass.
+
+ \value Type The type of style option provided (\l{SO_TabWidgetFrame} for this class).
+
+ The type is used internally by QStyleOption, its subclasses, and
+ qstyleoption_cast() to determine the type of style option. In
+ general you do not need to worry about this unless you want to
+ create your own QStyleOption subclass and your own styles.
+
+ \sa StyleOptionVersion
+*/
+
+/*!
+ \enum QStyleOptionTabWidgetFrame::StyleOptionVersion
+
+ This enum is used to hold information about the version of the style option, and
+ is defined for each QStyleOption subclass.
+
+ \value Version 1
+
+ The version is used by QStyleOption subclasses to implement
+ extensions without breaking compatibility. If you use
+ qstyleoption_cast(), you normally do not need to check it.
+
+ \sa StyleOptionType
+*/
+
+/*!
+ \variable QStyleOptionTabWidgetFrame::lineWidth
+ \brief the line width for drawing the panel
+
+ The default value is 0.
+*/
+
+/*!
+ \variable QStyleOptionTabWidgetFrame::midLineWidth
+ \brief the mid-line width for drawing the panel
+
+ The mid line width is usually used in drawing sunken or raised
+ frames. The default value is 0.
+*/
+
+/*!
+ \variable QStyleOptionTabWidgetFrame::shape
+ \brief the tab shape used to draw the tabs
+
+ The default value is QTabBar::RoundedNorth.
+*/
+
+/*!
+ \variable QStyleOptionTabWidgetFrame::tabBarSize
+ \brief the size of the tab bar
+
+ The default value is QSize(-1, -1), i.e. an invalid size.
+*/
+
+/*!
+ \variable QStyleOptionTabWidgetFrame::rightCornerWidgetSize
+ \brief the size of the right-corner widget
+
+ The default value is QSize(-1, -1), i.e. an invalid size.
+*/
+
+/*! \variable QStyleOptionTabWidgetFrame::leftCornerWidgetSize
+ \brief the size of the left-corner widget
+
+ The default value is QSize(-1, -1), i.e. an invalid size.
+*/
+#endif // QT_NO_TABWIDGET
+
+#ifndef QT_NO_TABBAR
+
+/*!
+ \class QStyleOptionTabBarBase
+ \brief The QStyleOptionTabBarBase class is used to describe
+ the base of a tab bar, i.e. the part that the tab bar usually
+ overlaps with.
+
+ QStyleOptionTabBarBase contains all the information that QStyle
+ functions need to draw the tab bar base. Note that this is only
+ drawn for a standalone QTabBar (one that isn't part of a
+ QTabWidget).
+
+ For performance reasons, the access to the member variables is
+ direct (i.e., using the \c . or \c -> operator). This low-level feel
+ makes the structures straightforward to use and emphasizes that
+ these are simply parameters used by the style functions.
+
+ For an example demonstrating how style options can be used, see
+ the \l {widgets/styles}{Styles} example.
+
+ \sa QStyleOption, QTabBar::drawBase()
+*/
+
+/*!
+ Construct a QStyleOptionTabBarBase, initializing the members
+ vaiables to their default values.
+*/
+QStyleOptionTabBarBase::QStyleOptionTabBarBase()
+ : QStyleOption(Version, SO_TabBarBase), shape(QTabBar::RoundedNorth)
+{
+}
+
+/*! \internal */
+QStyleOptionTabBarBase::QStyleOptionTabBarBase(int version)
+ : QStyleOption(version, SO_TabBarBase), shape(QTabBar::RoundedNorth)
+{
+}
+
+/*!
+ \fn QStyleOptionTabBarBase::QStyleOptionTabBarBase(const QStyleOptionTabBarBase &other)
+
+ Constructs a copy of \a other.
+*/
+
+/*!
+ \enum QStyleOptionTabBarBase::StyleOptionType
+
+ This enum is used to hold information about the type of the style option, and
+ is defined for each QStyleOption subclass.
+
+ \value Type The type of style option provided (\l{SO_TabBarBase} for this class).
+
+ The type is used internally by QStyleOption, its subclasses, and
+ qstyleoption_cast() to determine the type of style option. In
+ general you do not need to worry about this unless you want to
+ create your own QStyleOption subclass and your own styles.
+
+ \sa StyleOptionVersion
+*/
+
+/*!
+ \enum QStyleOptionTabBarBase::StyleOptionVersion
+
+ This enum is used to hold information about the version of the style option, and
+ is defined for each QStyleOption subclass.
+
+ \value Version 1
+
+ The version is used by QStyleOption subclasses to implement
+ extensions without breaking compatibility. If you use
+ qstyleoption_cast(), you normally do not need to check it.
+
+ \sa StyleOptionType
+*/
+
+/*!
+ \variable QStyleOptionTabBarBase::shape
+ \brief the shape of the tab bar
+
+ The default value is QTabBar::RoundedNorth.
+*/
+
+/*!
+ \variable QStyleOptionTabBarBase::tabBarRect
+ \brief the rectangle containing all the tabs
+
+ The default value is a null rectangle, i.e. a rectangle with both
+ the width and the height set to 0.
+*/
+
+/*!
+ \variable QStyleOptionTabBarBase::selectedTabRect
+ \brief the rectangle containing the selected tab
+
+ This rectangle is contained within the tabBarRect. The default
+ value is a null rectangle, i.e. a rectangle with both the width
+ and the height set to 0.
+*/
+
+
+/*!
+ \class QStyleOptionTabBarBaseV2
+ \brief The QStyleOptionTabBarBaseV2 class is used to describe
+ the base of a tab bar, i.e. the part that the tab bar usually
+ overlaps with.
+ \since 4.5
+
+ QStyleOptionTabBarBase contains all the information that QStyle
+ functions need to draw the tab bar base.
+
+ For performance reasons, the access to the member variables is
+ direct (i.e., using the \c . or \c -> operator). This low-level feel
+ makes the structures straightforward to use and emphasizes that
+ these are simply parameters used by the style functions.
+
+ For an example demonstrating how style options can be used, see
+ the \l {widgets/styles}{Styles} example.
+
+ \sa QStyleOption, QTabBar::drawBase()
+*/
+
+/*!
+ \enum QStyleOptionTabBarBaseV2::StyleOptionVersion
+
+ This enum is used to hold information about the version of the style option, and
+ is defined for each QStyleOption subclass.
+
+ \value Version 2
+
+ The version is used by QStyleOption subclasses to implement
+ extensions without breaking compatibility. If you use
+ qstyleoption_cast(), you normally do not need to check it.
+
+ \sa StyleOptionType
+*/
+
+/*!
+ \variable QStyleOptionTabBarBaseV2::documentMode
+ \brief whether the tabbar is in document mode.
+
+ The default value is false;
+*/
+
+/*!
+ Construct a QStyleOptionTabBarBaseV2, initializing the members
+ vaiables to their default values.
+*/
+QStyleOptionTabBarBaseV2::QStyleOptionTabBarBaseV2()
+ : QStyleOptionTabBarBase(Version)
+ , documentMode(false)
+{
+}
+
+/*!
+ \fn QStyleOptionTabBarBaseV2::QStyleOptionTabBarBaseV2(const QStyleOptionTabBarBaseV2 &other)
+
+ Constructs a copy of \a other.
+*/
+
+/*!
+ Constructs a copy of \a other.
+*/
+QStyleOptionTabBarBaseV2::QStyleOptionTabBarBaseV2(const QStyleOptionTabBarBase &other)
+ : QStyleOptionTabBarBase(Version)
+{
+ (void)QStyleOptionTabBarBaseV2::operator=(other);
+}
+
+/*!
+ Constructs a QStyleOptionTabBarBaseV2 copy of the \a other style option
+ which can be either of the QStyleOptionTabBarBaseV2, or QStyleOptionTabBarBase types.
+
+ If the other style option's version is not 1, the new style option's
+ \c documentMode is set to false. If its version is 2, its \c documentMode value
+ is simply copied to the new style option.
+*/
+QStyleOptionTabBarBaseV2 &QStyleOptionTabBarBaseV2::operator = (const QStyleOptionTabBarBase &other)
+{
+ QStyleOptionTabBarBase::operator=(other);
+ const QStyleOptionTabBarBaseV2 *v2 = qstyleoption_cast<const QStyleOptionTabBarBaseV2*>(&other);
+ documentMode = v2 ? v2->documentMode : false;
+ return *this;
+}
+
+/*! \internal */
+QStyleOptionTabBarBaseV2::QStyleOptionTabBarBaseV2(int version)
+ : QStyleOptionTabBarBase(version)
+{
+}
+
+#endif // QT_NO_TABBAR
+
+#ifndef QT_NO_SIZEGRIP
+/*!
+ \class QStyleOptionSizeGrip
+ \brief The QStyleOptionSizeGrip class is used to describe the
+ parameter for drawing a size grip.
+ \since 4.2
+
+ QStyleOptionButton contains all the information that QStyle
+ functions need to draw QSizeGrip.
+
+ For performance reasons, the access to the member variables is
+ direct (i.e., using the \c . or \c -> operator). This low-level feel
+ makes the structures straightforward to use and emphasizes that
+ these are simply parameters used by the style functions.
+
+ For an example demonstrating how style options can be used, see
+ the \l {widgets/styles}{Styles} example.
+
+ \sa QStyleOption, QStyleOptionComplex, QSizeGrip
+*/
+
+/*!
+ Constructs a QStyleOptionSizeGrip.
+*/
+QStyleOptionSizeGrip::QStyleOptionSizeGrip()
+ : QStyleOptionComplex(Version, Type), corner(Qt::BottomRightCorner)
+{
+}
+
+/*!
+ \fn QStyleOptionSizeGrip::QStyleOptionSizeGrip(const QStyleOptionSizeGrip &other)
+
+ Constructs a copy of the \a other style option.
+*/
+
+/*!
+ \internal
+*/
+QStyleOptionSizeGrip::QStyleOptionSizeGrip(int version)
+ : QStyleOptionComplex(version, Type), corner(Qt::BottomRightCorner)
+{
+}
+
+/*!
+ \variable QStyleOptionSizeGrip::corner
+
+ The corner in which the size grip is located.
+*/
+
+/*!
+ \enum QStyleOptionSizeGrip::StyleOptionType
+
+ This enum is used to hold information about the type of the style option, and
+ is defined for each QStyleOption subclass.
+
+ \value Type The type of style option provided (\l{SO_TabBarBase} for this class).
+
+ The type is used internally by QStyleOption, its subclasses, and
+ qstyleoption_cast() to determine the type of style option. In
+ general you do not need to worry about this unless you want to
+ create your own QStyleOption subclass and your own styles.
+
+ \sa StyleOptionVersion
+*/
+
+/*!
+ \enum QStyleOptionSizeGrip::StyleOptionVersion
+
+ This enum is used to hold information about the version of the style option, and
+ is defined for each QStyleOption subclass.
+
+ \value Version 1
+
+ The version is used by QStyleOption subclasses to implement
+ extensions without breaking compatibility. If you use
+ qstyleoption_cast(), you normally do not need to check it.
+
+ \sa StyleOptionType
+*/
+#endif // QT_NO_SIZEGRIP
+
+/*!
+ \class QStyleOptionGraphicsItem
+ \brief The QStyleOptionGraphicsItem class is used to describe
+ the parameters needed to draw a QGraphicsItem.
+ \since 4.2
+ \ingroup multimedia
+
+ For performance reasons, the access to the member variables is
+ direct (i.e., using the \c . or \c -> operator). This low-level feel
+ makes the structures straightforward to use and emphasizes that
+ these are simply parameters.
+
+ For an example demonstrating how style options can be used, see
+ the \l {widgets/styles}{Styles} example.
+
+ \sa QStyleOption, QGraphicsItem::paint()
+*/
+
+/*!
+ \enum QStyleOptionGraphicsItem::StyleOptionType
+
+ This enum is used to hold information about the type of the style option, and
+ is defined for each QStyleOption subclass.
+
+ \value Type The type of style option provided (\l SO_GraphicsItem for this class).
+
+ The type is used internally by QStyleOption, its subclasses, and
+ qstyleoption_cast() to determine the type of style option. In
+ general you do not need to worry about this unless you want to
+ create your own QStyleOption subclass and your own styles.
+
+ \sa StyleOptionVersion
+*/
+
+/*!
+ \enum QStyleOptionGraphicsItem::StyleOptionVersion
+
+ This enum is used to hold information about the version of the style option, and
+ is defined for each QStyleOption subclass.
+
+ \value Version 1
+
+ The version is used by QStyleOption subclasses to implement
+ extensions without breaking compatibility. If you use
+ qstyleoption_cast(), you normally do not need to check it.
+
+ \sa StyleOptionType
+*/
+
+/*!
+ Constructs a QStyleOptionGraphicsItem. The levelOfDetail parameter is
+ initialized to 1.
+*/
+QStyleOptionGraphicsItem::QStyleOptionGraphicsItem()
+ : QStyleOption(Version, Type), levelOfDetail(1)
+{
+}
+
+/*!
+ \internal
+*/
+QStyleOptionGraphicsItem::QStyleOptionGraphicsItem(int version)
+ : QStyleOption(version, Type), levelOfDetail(1)
+{
+}
+
+/*!
+ \fn QStyleOptionGraphicsItem::QStyleOptionGraphicsItem(const QStyleOptionGraphicsItem &other)
+
+ Constructs a copy of \a other.
+*/
+
+/*!
+ \variable QStyleOptionGraphicsItem::exposedRect
+ \brief the exposed rectangle, in item coordinates
+
+ Make use of this rectangle to speed up item drawing when only parts of the
+ item are exposed. If the whole item is exposed, this rectangle will be the
+ same as QGraphicsItem::boundingRect().
+*/
+
+/*!
+ \variable QStyleOptionGraphicsItem::matrix
+ \brief the complete transformation matrix for the item
+
+ This matrix is the sum of the item's scene matrix and the matrix of the
+ painter used for drawing the item. It is provided for convenience,
+ allowing anvanced level-of-detail metrics that can be used to speed up
+ item drawing.
+
+ To find the dimentions of an item in screen coordinates (i.e., pixels),
+ you can use the mapping functions of QMatrix, such as QMatrix::map().
+
+ \sa QStyleOptionGraphicsItem::levelOfDetail
+*/
+
+/*!
+ \variable QStyleOptionGraphicsItem::levelOfDetail
+ \brief a simple metric for determining an item's level of detail
+
+ This simple metric provides an easy way to determine the level of detail
+ for an item. Its value represents the maximum value of the height and
+ width of a unity rectangle, mapped using the complete transformation
+ matrix of the painter used to draw the item. By default, if no
+ transformations are applied, its value is 1. If zoomed out 1:2, the level
+ of detail will be 0.5, and if zoomed in 2:1, its value is 2.
+
+ For more advanced level-of-detail metrics, use
+ QStyleOptionGraphicsItem::matrix directly.
+
+ \sa QStyleOptionGraphicsItem::matrix
+*/
+
+/*!
+ \class QStyleHintReturn
+ \brief The QStyleHintReturn class provides style hints that return more
+ than basic data types.
+
+ \ingroup appearance
+
+ QStyleHintReturn and its subclasses are used to pass information
+ from a style back to the querying widget. This is most useful
+ when the return value from QStyle::styleHint() does not provide enough
+ detail; for example, when a mask is to be returned.
+
+ \omit
+ ### --Sam
+ \endomit
+*/
+
+/*!
+ \enum QStyleHintReturn::HintReturnType
+
+ \value SH_Default QStyleHintReturn
+ \value SH_Mask \l QStyle::SH_RubberBand_Mask QStyle::SH_FocusFrame_Mask
+ \value SH_Variant \l QStyle::SH_TextControl_FocusIndicatorTextCharFormat
+*/
+
+/*!
+ \enum QStyleHintReturn::StyleOptionType
+
+ This enum is used to hold information about the type of the style option, and
+ is defined for each QStyleHintReturn subclass.
+
+ \value Type The type of style option provided (\l SH_Default for
+ this class).
+
+ The type is used internally by QStyleHintReturn, its subclasses, and
+ qstyleoption_cast() to determine the type of style option. In
+ general you do not need to worry about this unless you want to
+ create your own QStyleHintReturn subclass and your own styles.
+
+ \sa StyleOptionVersion
+*/
+
+/*!
+ \enum QStyleHintReturn::StyleOptionVersion
+
+ This enum is used to hold information about the version of the style option, and
+ is defined for each QStyleHintReturn subclass.
+
+ \value Version 1
+
+ The version is used by QStyleHintReturn subclasses to implement
+ extensions without breaking compatibility. If you use
+ qstyleoption_cast(), you normally do not need to check it.
+
+ \sa StyleOptionType
+*/
+
+/*!
+ \variable QStyleHintReturn::type
+ \brief the type of the style hint container
+
+ \sa HintReturnType
+*/
+
+/*!
+ \variable QStyleHintReturn::version
+ \brief the version of the style hint return container
+
+ This value can be used by subclasses to implement extensions
+ without breaking compatibility. If you use qstyleoption_cast<T>(), you
+ normally do not need to check it.
+*/
+
+/*!
+ Constructs a QStyleHintReturn with version \a version and type \a
+ type.
+
+ The version has no special meaning for QStyleHintReturn; it can be
+ used by subclasses to distinguish between different version of
+ the same hint type.
+
+ \sa QStyleOption::version, QStyleOption::type
+*/
+
+QStyleHintReturn::QStyleHintReturn(int version, int type)
+ : version(version), type(type)
+{
+}
+
+/*!
+ \internal
+*/
+
+QStyleHintReturn::~QStyleHintReturn()
+{
+
+}
+
+/*!
+ \class QStyleHintReturnMask
+ \brief The QStyleHintReturnMask class provides style hints that return a QRegion.
+
+ \ingroup appearance
+
+ \omit
+ ### --Sam
+ \endomit
+*/
+
+/*!
+ \variable QStyleHintReturnMask::region
+ \brief the region for style hints that return a QRegion
+*/
+
+/*!
+ Constructs a QStyleHintReturnMask. The member variables are
+ initialized to default values.
+*/
+QStyleHintReturnMask::QStyleHintReturnMask() : QStyleHintReturn(Version, Type)
+{
+}
+
+/*!
+ \enum QStyleHintReturnMask::StyleOptionType
+
+ This enum is used to hold information about the type of the style option, and
+ is defined for each QStyleHintReturn subclass.
+
+ \value Type The type of style option provided (\l{SH_Mask} for
+ this class).
+
+ The type is used internally by QStyleHintReturn, its subclasses, and
+ qstyleoption_cast() to determine the type of style option. In
+ general you do not need to worry about this unless you want to
+ create your own QStyleHintReturn subclass and your own styles.
+
+ \sa StyleOptionVersion
+*/
+
+/*!
+ \enum QStyleHintReturnMask::StyleOptionVersion
+
+ This enum is used to hold information about the version of the style option, and
+ is defined for each QStyleHintReturn subclass.
+
+ \value Version 1
+
+ The version is used by QStyleHintReturn subclasses to implement
+ extensions without breaking compatibility. If you use
+ qstyleoption_cast(), you normally do not need to check it.
+
+ \sa StyleOptionType
+*/
+
+/*!
+ \class QStyleHintReturnVariant
+ \brief The QStyleHintReturnVariant class provides style hints that return a QVariant.
+ \since 4.3
+ \ingroup appearance
+*/
+
+/*!
+ \variable QStyleHintReturnVariant::variant
+ \brief the variant for style hints that return a QVariant
+*/
+
+/*!
+ Constructs a QStyleHintReturnVariant. The member variables are
+ initialized to default values.
+*/
+QStyleHintReturnVariant::QStyleHintReturnVariant() : QStyleHintReturn(Version, Type)
+{
+}
+
+/*!
+ \enum QStyleHintReturnVariant::StyleOptionType
+
+ This enum is used to hold information about the type of the style option, and
+ is defined for each QStyleHintReturn subclass.
+
+ \value Type The type of style option provided (\l{SH_Variant} for
+ this class).
+
+ The type is used internally by QStyleHintReturn, its subclasses, and
+ qstyleoption_cast() to determine the type of style option. In
+ general you do not need to worry about this unless you want to
+ create your own QStyleHintReturn subclass and your own styles.
+
+ \sa StyleOptionVersion
+*/
+
+/*!
+ \enum QStyleHintReturnVariant::StyleOptionVersion
+
+ This enum is used to hold information about the version of the style option, and
+ is defined for each QStyleHintReturn subclass.
+
+ \value Version 1
+
+ The version is used by QStyleHintReturn subclasses to implement
+ extensions without breaking compatibility. If you use
+ qstyleoption_cast(), you normally do not need to check it.
+
+ \sa StyleOptionType
+*/
+
+/*!
+ \fn T qstyleoption_cast<T>(const QStyleHintReturn *hint)
+ \relates QStyleHintReturn
+
+ Returns a T or 0 depending on the \l{QStyleHintReturn::type}{type}
+ and \l{QStyleHintReturn::version}{version} of \a hint.
+
+ Example:
+
+ \snippet doc/src/snippets/code/src_gui_styles_qstyleoption.cpp 0
+
+ \sa QStyleHintReturn::type, QStyleHintReturn::version
+*/
+
+/*!
+ \fn T qstyleoption_cast<T>(QStyleHintReturn *hint)
+ \overload
+ \relates QStyleHintReturn
+
+ Returns a T or 0 depending on the type of \a hint.
+*/
+
+#if !defined(QT_NO_DEBUG) && !defined(QT_NO_DEBUG_STREAM)
+QDebug operator<<(QDebug debug, const QStyleOption::OptionType &optionType)
+{
+ switch (optionType) {
+ case QStyleOption::SO_Default:
+ debug << "SO_Default"; break;
+ case QStyleOption::SO_FocusRect:
+ debug << "SO_FocusRect"; break;
+ case QStyleOption::SO_Button:
+ debug << "SO_Button"; break;
+ case QStyleOption::SO_Tab:
+ debug << "SO_Tab"; break;
+ case QStyleOption::SO_MenuItem:
+ debug << "SO_MenuItem"; break;
+ case QStyleOption::SO_Frame:
+ debug << "SO_Frame"; break;
+ case QStyleOption::SO_ProgressBar:
+ debug << "SO_ProgressBar"; break;
+ case QStyleOption::SO_ToolBox:
+ debug << "SO_ToolBox"; break;
+ case QStyleOption::SO_Header:
+ debug << "SO_Header"; break;
+ case QStyleOption::SO_Q3DockWindow:
+ debug << "SO_Q3DockWindow"; break;
+ case QStyleOption::SO_DockWidget:
+ debug << "SO_DockWidget"; break;
+ case QStyleOption::SO_Q3ListViewItem:
+ debug << "SO_Q3ListViewItem"; break;
+ case QStyleOption::SO_ViewItem:
+ debug << "SO_ViewItem"; break;
+ case QStyleOption::SO_TabWidgetFrame:
+ debug << "SO_TabWidgetFrame"; break;
+ case QStyleOption::SO_TabBarBase:
+ debug << "SO_TabBarBase"; break;
+ case QStyleOption::SO_RubberBand:
+ debug << "SO_RubberBand"; break;
+ case QStyleOption::SO_Complex:
+ debug << "SO_Complex"; break;
+ case QStyleOption::SO_Slider:
+ debug << "SO_Slider"; break;
+ case QStyleOption::SO_SpinBox:
+ debug << "SO_SpinBox"; break;
+ case QStyleOption::SO_ToolButton:
+ debug << "SO_ToolButton"; break;
+ case QStyleOption::SO_ComboBox:
+ debug << "SO_ComboBox"; break;
+ case QStyleOption::SO_Q3ListView:
+ debug << "SO_Q3ListView"; break;
+ case QStyleOption::SO_TitleBar:
+ debug << "SO_TitleBar"; break;
+ case QStyleOption::SO_CustomBase:
+ debug << "SO_CustomBase"; break;
+ case QStyleOption::SO_GroupBox:
+ debug << "SO_GroupBox"; break;
+ case QStyleOption::SO_ToolBar:
+ debug << "SO_ToolBar"; break;
+ case QStyleOption::SO_ComplexCustomBase:
+ debug << "SO_ComplexCustomBase"; break;
+ case QStyleOption::SO_SizeGrip:
+ debug << "SO_SizeGrip"; break;
+ case QStyleOption::SO_GraphicsItem:
+ debug << "SO_GraphicsItem"; break;
+ }
+ return debug;
+}
+
+QDebug operator<<(QDebug debug, const QStyleOption &option)
+{
+ debug << "QStyleOption(";
+ debug << QStyleOption::OptionType(option.type);
+ debug << "," << (option.direction == Qt::RightToLeft ? "RightToLeft" : "LeftToRight");
+ debug << "," << option.state;
+ debug << "," << option.rect;
+ debug << ")";
+ return debug;
+}
+#endif
+
+QT_END_NAMESPACE
diff --git a/src/gui/styles/qstyleoption.h b/src/gui/styles/qstyleoption.h
new file mode 100644
index 0000000000..5759a054a2
--- /dev/null
+++ b/src/gui/styles/qstyleoption.h
@@ -0,0 +1,949 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSTYLEOPTION_H
+#define QSTYLEOPTION_H
+
+#include <QtCore/qvariant.h>
+#include <QtGui/qabstractspinbox.h>
+#include <QtGui/qicon.h>
+#include <QtGui/qmatrix.h>
+#include <QtGui/qslider.h>
+#include <QtGui/qstyle.h>
+#include <QtGui/qtabbar.h>
+#include <QtGui/qtabwidget.h>
+#include <QtGui/qrubberband.h>
+#include <QtGui/qframe.h>
+#ifndef QT_NO_ITEMVIEWS
+# include <QtCore/qabstractitemmodel.h>
+#endif
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+class QDebug;
+
+class Q_GUI_EXPORT QStyleOption
+{
+public:
+ enum OptionType {
+ SO_Default, SO_FocusRect, SO_Button, SO_Tab, SO_MenuItem,
+ SO_Frame, SO_ProgressBar, SO_ToolBox, SO_Header, SO_Q3DockWindow,
+ SO_DockWidget, SO_Q3ListViewItem, SO_ViewItem, SO_TabWidgetFrame,
+ SO_TabBarBase, SO_RubberBand, SO_ToolBar, SO_GraphicsItem,
+
+ SO_Complex = 0xf0000, SO_Slider, SO_SpinBox, SO_ToolButton, SO_ComboBox,
+ SO_Q3ListView, SO_TitleBar, SO_GroupBox, SO_SizeGrip,
+
+ SO_CustomBase = 0xf00,
+ SO_ComplexCustomBase = 0xf000000
+ };
+
+ enum StyleOptionType { Type = SO_Default };
+ enum StyleOptionVersion { Version = 1 };
+
+ int version;
+ int type;
+ QStyle::State state;
+ Qt::LayoutDirection direction;
+ QRect rect;
+ QFontMetrics fontMetrics;
+ QPalette palette;
+
+ QStyleOption(int version = QStyleOption::Version, int type = SO_Default);
+ QStyleOption(const QStyleOption &other);
+ ~QStyleOption();
+
+ void init(const QWidget *w);
+ inline void initFrom(const QWidget *w) { init(w); }
+ QStyleOption &operator=(const QStyleOption &other);
+};
+
+class Q_GUI_EXPORT QStyleOptionFocusRect : public QStyleOption
+{
+public:
+ enum StyleOptionType { Type = SO_FocusRect };
+ enum StyleOptionVersion { Version = 1 };
+
+ QColor backgroundColor;
+
+ QStyleOptionFocusRect();
+ QStyleOptionFocusRect(const QStyleOptionFocusRect &other) : QStyleOption(Version, Type) { *this = other; }
+
+protected:
+ QStyleOptionFocusRect(int version);
+};
+
+class Q_GUI_EXPORT QStyleOptionFrame : public QStyleOption
+{
+public:
+ enum StyleOptionType { Type = SO_Frame };
+ enum StyleOptionVersion { Version = 1 };
+
+ int lineWidth;
+ int midLineWidth;
+
+ QStyleOptionFrame();
+ QStyleOptionFrame(const QStyleOptionFrame &other) : QStyleOption(Version, Type) { *this = other; }
+
+protected:
+ QStyleOptionFrame(int version);
+};
+
+class Q_GUI_EXPORT QStyleOptionFrameV2 : public QStyleOptionFrame
+{
+public:
+ enum StyleOptionVersion { Version = 2 };
+ enum FrameFeature {
+ None = 0x00,
+ Flat = 0x01
+ };
+ Q_DECLARE_FLAGS(FrameFeatures, FrameFeature)
+ FrameFeatures features;
+
+ QStyleOptionFrameV2();
+ QStyleOptionFrameV2(const QStyleOptionFrameV2 &other) : QStyleOptionFrame(Version) { *this = other; }
+ QStyleOptionFrameV2(const QStyleOptionFrame &other);
+ QStyleOptionFrameV2 &operator=(const QStyleOptionFrame &other);
+
+protected:
+ QStyleOptionFrameV2(int version);
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(QStyleOptionFrameV2::FrameFeatures)
+
+
+class Q_GUI_EXPORT QStyleOptionFrameV3 : public QStyleOptionFrameV2
+{
+public:
+ enum StyleOptionVersion { Version = 3 };
+ QFrame::Shape frameShape : 4;
+ uint unused : 28;
+
+ QStyleOptionFrameV3();
+ QStyleOptionFrameV3(const QStyleOptionFrameV3 &other) : QStyleOptionFrameV2(Version) { *this = other; }
+ QStyleOptionFrameV3(const QStyleOptionFrame &other);
+ QStyleOptionFrameV3 &operator=(const QStyleOptionFrame &other);
+
+protected:
+ QStyleOptionFrameV3(int version);
+};
+
+
+#ifndef QT_NO_TABWIDGET
+class Q_GUI_EXPORT QStyleOptionTabWidgetFrame : public QStyleOption
+{
+public:
+ enum StyleOptionType { Type = SO_TabWidgetFrame };
+ enum StyleOptionVersion { Version = 1 };
+
+ int lineWidth;
+ int midLineWidth;
+ QTabBar::Shape shape;
+ QSize tabBarSize;
+ QSize rightCornerWidgetSize;
+ QSize leftCornerWidgetSize;
+
+ QStyleOptionTabWidgetFrame();
+ inline QStyleOptionTabWidgetFrame(const QStyleOptionTabWidgetFrame &other)
+ : QStyleOption(Version, Type) { *this = other; }
+
+protected:
+ QStyleOptionTabWidgetFrame(int version);
+};
+#endif
+
+#ifndef QT_NO_TABBAR
+class Q_GUI_EXPORT QStyleOptionTabBarBase : public QStyleOption
+{
+public:
+ enum StyleOptionType { Type = SO_TabBarBase };
+ enum StyleOptionVersion { Version = 1 };
+
+ QTabBar::Shape shape;
+ QRect tabBarRect;
+ QRect selectedTabRect;
+
+ QStyleOptionTabBarBase();
+ QStyleOptionTabBarBase(const QStyleOptionTabBarBase &other) : QStyleOption(Version, Type) { *this = other; }
+
+protected:
+ QStyleOptionTabBarBase(int version);
+};
+
+class Q_GUI_EXPORT QStyleOptionTabBarBaseV2 : public QStyleOptionTabBarBase
+{
+public:
+ enum StyleOptionVersion { Version = 2 };
+ bool documentMode;
+ QStyleOptionTabBarBaseV2();
+ QStyleOptionTabBarBaseV2(const QStyleOptionTabBarBaseV2 &other) : QStyleOptionTabBarBase(Version) { *this = other; }
+ QStyleOptionTabBarBaseV2(const QStyleOptionTabBarBase &other);
+ QStyleOptionTabBarBaseV2 &operator=(const QStyleOptionTabBarBase &other);
+
+protected:
+ QStyleOptionTabBarBaseV2(int version);
+};
+
+#endif
+
+class Q_GUI_EXPORT QStyleOptionHeader : public QStyleOption
+{
+public:
+ enum StyleOptionType { Type = SO_Header };
+ enum StyleOptionVersion { Version = 1 };
+
+ enum SectionPosition { Beginning, Middle, End, OnlyOneSection };
+ enum SelectedPosition { NotAdjacent, NextIsSelected, PreviousIsSelected,
+ NextAndPreviousAreSelected };
+ enum SortIndicator { None, SortUp, SortDown };
+
+ int section;
+ QString text;
+ Qt::Alignment textAlignment;
+ QIcon icon;
+ Qt::Alignment iconAlignment;
+ SectionPosition position;
+ SelectedPosition selectedPosition;
+ SortIndicator sortIndicator;
+ Qt::Orientation orientation;
+
+ QStyleOptionHeader();
+ QStyleOptionHeader(const QStyleOptionHeader &other) : QStyleOption(Version, Type) { *this = other; }
+
+protected:
+ QStyleOptionHeader(int version);
+};
+
+class Q_GUI_EXPORT QStyleOptionButton : public QStyleOption
+{
+public:
+ enum StyleOptionType { Type = SO_Button };
+ enum StyleOptionVersion { Version = 1 };
+
+ enum ButtonFeature { None = 0x00, Flat = 0x01, HasMenu = 0x02, DefaultButton = 0x04,
+ AutoDefaultButton = 0x08, CommandLinkButton = 0x10 };
+ Q_DECLARE_FLAGS(ButtonFeatures, ButtonFeature)
+
+ ButtonFeatures features;
+ QString text;
+ QIcon icon;
+ QSize iconSize;
+
+ QStyleOptionButton();
+ QStyleOptionButton(const QStyleOptionButton &other) : QStyleOption(Version, Type) { *this = other; }
+
+protected:
+ QStyleOptionButton(int version);
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(QStyleOptionButton::ButtonFeatures)
+
+#ifndef QT_NO_TABBAR
+class Q_GUI_EXPORT QStyleOptionTab : public QStyleOption
+{
+public:
+ enum StyleOptionType { Type = SO_Tab };
+ enum StyleOptionVersion { Version = 1 };
+
+ enum TabPosition { Beginning, Middle, End, OnlyOneTab };
+ enum SelectedPosition { NotAdjacent, NextIsSelected, PreviousIsSelected };
+ enum CornerWidget { NoCornerWidgets = 0x00, LeftCornerWidget = 0x01,
+ RightCornerWidget = 0x02 };
+ Q_DECLARE_FLAGS(CornerWidgets, CornerWidget)
+
+ QTabBar::Shape shape;
+ QString text;
+ QIcon icon;
+ int row;
+ TabPosition position;
+ SelectedPosition selectedPosition;
+ CornerWidgets cornerWidgets;
+
+ QStyleOptionTab();
+ QStyleOptionTab(const QStyleOptionTab &other) : QStyleOption(Version, Type) { *this = other; }
+
+protected:
+ QStyleOptionTab(int version);
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(QStyleOptionTab::CornerWidgets)
+
+class Q_GUI_EXPORT QStyleOptionTabV2 : public QStyleOptionTab
+{
+public:
+ enum StyleOptionVersion { Version = 2 };
+ QSize iconSize;
+ QStyleOptionTabV2();
+ QStyleOptionTabV2(const QStyleOptionTabV2 &other) : QStyleOptionTab(Version) { *this = other; }
+ QStyleOptionTabV2(const QStyleOptionTab &other);
+ QStyleOptionTabV2 &operator=(const QStyleOptionTab &other);
+
+protected:
+ QStyleOptionTabV2(int version);
+};
+
+class Q_GUI_EXPORT QStyleOptionTabV3 : public QStyleOptionTabV2
+{
+public:
+ enum StyleOptionVersion { Version = 3 };
+ bool documentMode;
+ QSize leftButtonSize;
+ QSize rightButtonSize;
+ QStyleOptionTabV3();
+ QStyleOptionTabV3(const QStyleOptionTabV3 &other) : QStyleOptionTabV2(Version) { *this = other; }
+ QStyleOptionTabV3(const QStyleOptionTabV2 &other) : QStyleOptionTabV2(Version) { *this = other; }
+ QStyleOptionTabV3(const QStyleOptionTab &other);
+ QStyleOptionTabV3 &operator=(const QStyleOptionTab &other);
+
+protected:
+ QStyleOptionTabV3(int version);
+};
+
+#endif
+
+
+#ifndef QT_NO_TOOLBAR
+
+class Q_GUI_EXPORT QStyleOptionToolBar : public QStyleOption
+{
+public:
+ enum StyleOptionType { Type = SO_ToolBar };
+ enum StyleOptionVersion { Version = 1 };
+ enum ToolBarPosition { Beginning, Middle, End, OnlyOne };
+ enum ToolBarFeature { None = 0x0, Movable = 0x1 };
+ Q_DECLARE_FLAGS(ToolBarFeatures, ToolBarFeature)
+ ToolBarPosition positionOfLine; // The toolbar line position
+ ToolBarPosition positionWithinLine; // The position within a toolbar
+ Qt::ToolBarArea toolBarArea; // The toolbar docking area
+ ToolBarFeatures features;
+ int lineWidth;
+ int midLineWidth;
+ QStyleOptionToolBar();
+ QStyleOptionToolBar(const QStyleOptionToolBar &other) : QStyleOption(Version, Type) { *this = other; }
+
+protected:
+ QStyleOptionToolBar(int version);
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(QStyleOptionToolBar::ToolBarFeatures)
+
+#endif
+
+
+
+class Q_GUI_EXPORT QStyleOptionProgressBar : public QStyleOption
+{
+public:
+ enum StyleOptionType { Type = SO_ProgressBar };
+ enum StyleOptionVersion { Version = 1 };
+
+ int minimum;
+ int maximum;
+ int progress;
+ QString text;
+ Qt::Alignment textAlignment;
+ bool textVisible;
+
+ QStyleOptionProgressBar();
+ QStyleOptionProgressBar(const QStyleOptionProgressBar &other) : QStyleOption(Version, Type) { *this = other; }
+
+protected:
+ QStyleOptionProgressBar(int version);
+};
+
+// Adds style info for vertical progress bars
+class Q_GUI_EXPORT QStyleOptionProgressBarV2 : public QStyleOptionProgressBar
+{
+public:
+ enum StyleOptionType { Type = SO_ProgressBar };
+ enum StyleOptionVersion { Version = 2 };
+ Qt::Orientation orientation;
+ bool invertedAppearance;
+ bool bottomToTop;
+
+ QStyleOptionProgressBarV2();
+ QStyleOptionProgressBarV2(const QStyleOptionProgressBar &other);
+ QStyleOptionProgressBarV2(const QStyleOptionProgressBarV2 &other);
+ QStyleOptionProgressBarV2 &operator=(const QStyleOptionProgressBar &other);
+
+protected:
+ QStyleOptionProgressBarV2(int version);
+};
+
+class Q_GUI_EXPORT QStyleOptionMenuItem : public QStyleOption
+{
+public:
+ enum StyleOptionType { Type = SO_MenuItem };
+ enum StyleOptionVersion { Version = 1 };
+
+ enum MenuItemType { Normal, DefaultItem, Separator, SubMenu, Scroller, TearOff, Margin,
+ EmptyArea };
+ enum CheckType { NotCheckable, Exclusive, NonExclusive };
+
+ MenuItemType menuItemType;
+ CheckType checkType;
+ bool checked;
+ bool menuHasCheckableItems;
+ QRect menuRect;
+ QString text;
+ QIcon icon;
+ int maxIconWidth;
+ int tabWidth;
+ QFont font;
+
+ QStyleOptionMenuItem();
+ QStyleOptionMenuItem(const QStyleOptionMenuItem &other) : QStyleOption(Version, Type) { *this = other; }
+
+protected:
+ QStyleOptionMenuItem(int version);
+};
+
+class Q_GUI_EXPORT QStyleOptionQ3ListViewItem : public QStyleOption
+{
+public:
+ enum StyleOptionType { Type = SO_Q3ListViewItem };
+ enum StyleOptionVersion { Version = 1 };
+
+ enum Q3ListViewItemFeature { None = 0x00, Expandable = 0x01, MultiLine = 0x02, Visible = 0x04,
+ ParentControl = 0x08 };
+ Q_DECLARE_FLAGS(Q3ListViewItemFeatures, Q3ListViewItemFeature)
+
+ Q3ListViewItemFeatures features;
+ int height;
+ int totalHeight;
+ int itemY;
+ int childCount;
+
+ QStyleOptionQ3ListViewItem();
+ QStyleOptionQ3ListViewItem(const QStyleOptionQ3ListViewItem &other) : QStyleOption(Version, Type) { *this = other; }
+
+protected:
+ QStyleOptionQ3ListViewItem(int version);
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(QStyleOptionQ3ListViewItem::Q3ListViewItemFeatures)
+
+class Q_GUI_EXPORT QStyleOptionQ3DockWindow : public QStyleOption
+{
+public:
+ enum StyleOptionType { Type = SO_Q3DockWindow };
+ enum StyleOptionVersion { Version = 1 };
+
+ bool docked;
+ bool closeEnabled;
+
+ QStyleOptionQ3DockWindow();
+ QStyleOptionQ3DockWindow(const QStyleOptionQ3DockWindow &other) : QStyleOption(Version, Type) { *this = other; }
+
+protected:
+ QStyleOptionQ3DockWindow(int version);
+};
+
+class Q_GUI_EXPORT QStyleOptionDockWidget : public QStyleOption
+{
+public:
+ enum StyleOptionType { Type = SO_DockWidget };
+ enum StyleOptionVersion { Version = 1 };
+
+ QString title;
+ bool closable;
+ bool movable;
+ bool floatable;
+
+ QStyleOptionDockWidget();
+ QStyleOptionDockWidget(const QStyleOptionDockWidget &other) : QStyleOption(Version, Type) { *this = other; }
+
+protected:
+ QStyleOptionDockWidget(int version);
+};
+
+class Q_GUI_EXPORT QStyleOptionDockWidgetV2 : public QStyleOptionDockWidget
+{
+public:
+ enum StyleOptionVersion { Version = 2 };
+
+ bool verticalTitleBar;
+
+ QStyleOptionDockWidgetV2();
+ QStyleOptionDockWidgetV2(const QStyleOptionDockWidgetV2 &other)
+ : QStyleOptionDockWidget(Version) { *this = other; }
+ QStyleOptionDockWidgetV2(const QStyleOptionDockWidget &other);
+ QStyleOptionDockWidgetV2 &operator = (const QStyleOptionDockWidget &other);
+
+protected:
+ QStyleOptionDockWidgetV2(int version);
+};
+
+class Q_GUI_EXPORT QStyleOptionViewItem : public QStyleOption
+{
+public:
+ enum StyleOptionType { Type = SO_ViewItem };
+ enum StyleOptionVersion { Version = 1 };
+
+ enum Position { Left, Right, Top, Bottom };
+
+ Qt::Alignment displayAlignment;
+ Qt::Alignment decorationAlignment;
+ Qt::TextElideMode textElideMode;
+ Position decorationPosition;
+ QSize decorationSize;
+ QFont font;
+ bool showDecorationSelected;
+
+ QStyleOptionViewItem();
+ QStyleOptionViewItem(const QStyleOptionViewItem &other) : QStyleOption(Version, Type) { *this = other; }
+
+protected:
+ QStyleOptionViewItem(int version);
+};
+
+class Q_GUI_EXPORT QStyleOptionViewItemV2 : public QStyleOptionViewItem
+{
+public:
+ enum StyleOptionVersion { Version = 2 };
+
+ enum ViewItemFeature {
+ None = 0x00,
+ WrapText = 0x01,
+ Alternate = 0x02,
+ HasCheckIndicator = 0x04,
+ HasDisplay = 0x08,
+ HasDecoration = 0x10
+ };
+ Q_DECLARE_FLAGS(ViewItemFeatures, ViewItemFeature)
+
+ ViewItemFeatures features;
+
+ QStyleOptionViewItemV2();
+ QStyleOptionViewItemV2(const QStyleOptionViewItemV2 &other) : QStyleOptionViewItem(Version) { *this = other; }
+ QStyleOptionViewItemV2(const QStyleOptionViewItem &other);
+ QStyleOptionViewItemV2 &operator=(const QStyleOptionViewItem &other);
+
+protected:
+ QStyleOptionViewItemV2(int version);
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(QStyleOptionViewItemV2::ViewItemFeatures)
+
+class Q_GUI_EXPORT QStyleOptionViewItemV3 : public QStyleOptionViewItemV2
+{
+public:
+ enum StyleOptionVersion { Version = 3 };
+
+ QLocale locale;
+ const QWidget *widget;
+
+ QStyleOptionViewItemV3();
+ QStyleOptionViewItemV3(const QStyleOptionViewItemV3 &other)
+ : QStyleOptionViewItemV2(Version) { *this = other; }
+ QStyleOptionViewItemV3(const QStyleOptionViewItem &other);
+ QStyleOptionViewItemV3 &operator = (const QStyleOptionViewItem &other);
+
+protected:
+ QStyleOptionViewItemV3(int version);
+};
+
+#ifndef QT_NO_ITEMVIEWS
+class Q_GUI_EXPORT QStyleOptionViewItemV4 : public QStyleOptionViewItemV3
+{
+public:
+ enum StyleOptionVersion { Version = 4 };
+ enum ViewItemPosition { Invalid, Beginning, Middle, End, OnlyOne };
+
+ QModelIndex index;
+ Qt::CheckState checkState;
+ QIcon icon;
+ QString text;
+ ViewItemPosition viewItemPosition;
+ QBrush backgroundBrush;
+
+ QStyleOptionViewItemV4();
+ QStyleOptionViewItemV4(const QStyleOptionViewItemV4 &other)
+ : QStyleOptionViewItemV3(Version) { *this = other; }
+ QStyleOptionViewItemV4(const QStyleOptionViewItem &other);
+ QStyleOptionViewItemV4 &operator = (const QStyleOptionViewItem &other);
+
+protected:
+ QStyleOptionViewItemV4(int version);
+};
+#endif
+
+class Q_GUI_EXPORT QStyleOptionToolBox : public QStyleOption
+{
+public:
+ enum StyleOptionType { Type = SO_ToolBox };
+ enum StyleOptionVersion { Version = 1 };
+
+ QString text;
+ QIcon icon;
+
+ QStyleOptionToolBox();
+ QStyleOptionToolBox(const QStyleOptionToolBox &other) : QStyleOption(Version, Type) { *this = other; }
+
+protected:
+ QStyleOptionToolBox(int version);
+};
+
+class Q_GUI_EXPORT QStyleOptionToolBoxV2 : public QStyleOptionToolBox
+{
+public:
+ enum StyleOptionVersion { Version = 2 };
+ enum TabPosition { Beginning, Middle, End, OnlyOneTab };
+ enum SelectedPosition { NotAdjacent, NextIsSelected, PreviousIsSelected };
+
+ TabPosition position;
+ SelectedPosition selectedPosition;
+
+ QStyleOptionToolBoxV2();
+ QStyleOptionToolBoxV2(const QStyleOptionToolBoxV2 &other) : QStyleOptionToolBox(Version) { *this = other; }
+ QStyleOptionToolBoxV2(const QStyleOptionToolBox &other);
+ QStyleOptionToolBoxV2 &operator=(const QStyleOptionToolBox &other);
+
+protected:
+ QStyleOptionToolBoxV2(int version);
+};
+
+#ifndef QT_NO_RUBBERBAND
+class Q_GUI_EXPORT QStyleOptionRubberBand : public QStyleOption
+{
+public:
+ enum StyleOptionType { Type = SO_RubberBand };
+ enum StyleOptionVersion { Version = 1 };
+
+ QRubberBand::Shape shape;
+ bool opaque;
+
+ QStyleOptionRubberBand();
+ QStyleOptionRubberBand(const QStyleOptionRubberBand &other) : QStyleOption(Version, Type) { *this = other; }
+
+protected:
+ QStyleOptionRubberBand(int version);
+};
+#endif // QT_NO_RUBBERBAND
+
+// -------------------------- Complex style options -------------------------------
+class Q_GUI_EXPORT QStyleOptionComplex : public QStyleOption
+{
+public:
+ enum StyleOptionType { Type = SO_Complex };
+ enum StyleOptionVersion { Version = 1 };
+
+ QStyle::SubControls subControls;
+ QStyle::SubControls activeSubControls;
+
+ QStyleOptionComplex(int version = QStyleOptionComplex::Version, int type = SO_Complex);
+ QStyleOptionComplex(const QStyleOptionComplex &other) : QStyleOption(Version, Type) { *this = other; }
+};
+
+#ifndef QT_NO_SLIDER
+class Q_GUI_EXPORT QStyleOptionSlider : public QStyleOptionComplex
+{
+public:
+ enum StyleOptionType { Type = SO_Slider };
+ enum StyleOptionVersion { Version = 1 };
+
+ Qt::Orientation orientation;
+ int minimum;
+ int maximum;
+ QSlider::TickPosition tickPosition;
+ int tickInterval;
+ bool upsideDown;
+ int sliderPosition;
+ int sliderValue;
+ int singleStep;
+ int pageStep;
+ qreal notchTarget;
+ bool dialWrapping;
+
+ QStyleOptionSlider();
+ QStyleOptionSlider(const QStyleOptionSlider &other) : QStyleOptionComplex(Version, Type) { *this = other; }
+
+protected:
+ QStyleOptionSlider(int version);
+};
+#endif // QT_NO_SLIDER
+
+#ifndef QT_NO_SPINBOX
+class Q_GUI_EXPORT QStyleOptionSpinBox : public QStyleOptionComplex
+{
+public:
+ enum StyleOptionType { Type = SO_SpinBox };
+ enum StyleOptionVersion { Version = 1 };
+
+ QAbstractSpinBox::ButtonSymbols buttonSymbols;
+ QAbstractSpinBox::StepEnabled stepEnabled;
+ bool frame;
+
+ QStyleOptionSpinBox();
+ QStyleOptionSpinBox(const QStyleOptionSpinBox &other) : QStyleOptionComplex(Version, Type) { *this = other; }
+
+protected:
+ QStyleOptionSpinBox(int version);
+};
+#endif // QT_NO_SPINBOX
+
+class Q_GUI_EXPORT QStyleOptionQ3ListView : public QStyleOptionComplex
+{
+public:
+ enum StyleOptionType { Type = SO_Q3ListView };
+ enum StyleOptionVersion { Version = 1 };
+
+ QList<QStyleOptionQ3ListViewItem> items;
+ QPalette viewportPalette;
+ QPalette::ColorRole viewportBGRole;
+ int sortColumn;
+ int itemMargin;
+ int treeStepSize;
+ bool rootIsDecorated;
+
+ QStyleOptionQ3ListView();
+ QStyleOptionQ3ListView(const QStyleOptionQ3ListView &other) : QStyleOptionComplex(Version, Type) { *this = other; }
+
+protected:
+ QStyleOptionQ3ListView(int version);
+};
+
+class Q_GUI_EXPORT QStyleOptionToolButton : public QStyleOptionComplex
+{
+public:
+ enum StyleOptionType { Type = SO_ToolButton };
+ enum StyleOptionVersion { Version = 1 };
+
+ enum ToolButtonFeature { None = 0x00, Arrow = 0x01, Menu = 0x04, MenuButtonPopup = Menu, PopupDelay = 0x08,
+ HasMenu = 0x10 };
+ Q_DECLARE_FLAGS(ToolButtonFeatures, ToolButtonFeature)
+
+ ToolButtonFeatures features;
+ QIcon icon;
+ QSize iconSize;
+ QString text;
+ Qt::ArrowType arrowType;
+ Qt::ToolButtonStyle toolButtonStyle;
+ QPoint pos;
+ QFont font;
+
+ QStyleOptionToolButton();
+ QStyleOptionToolButton(const QStyleOptionToolButton &other) : QStyleOptionComplex(Version, Type) { *this = other; }
+
+protected:
+ QStyleOptionToolButton(int version);
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(QStyleOptionToolButton::ToolButtonFeatures)
+
+class Q_GUI_EXPORT QStyleOptionComboBox : public QStyleOptionComplex
+{
+public:
+ enum StyleOptionType { Type = SO_ComboBox };
+ enum StyleOptionVersion { Version = 1 };
+
+ bool editable;
+ QRect popupRect;
+ bool frame;
+ QString currentText;
+ QIcon currentIcon;
+ QSize iconSize;
+
+ QStyleOptionComboBox();
+ QStyleOptionComboBox(const QStyleOptionComboBox &other) : QStyleOptionComplex(Version, Type) { *this = other; }
+
+protected:
+ QStyleOptionComboBox(int version);
+};
+
+class Q_GUI_EXPORT QStyleOptionTitleBar : public QStyleOptionComplex
+{
+public:
+ enum StyleOptionType { Type = SO_TitleBar };
+ enum StyleOptionVersion { Version = 1 };
+
+ QString text;
+ QIcon icon;
+ int titleBarState;
+ Qt::WindowFlags titleBarFlags;
+
+ QStyleOptionTitleBar();
+ QStyleOptionTitleBar(const QStyleOptionTitleBar &other) : QStyleOptionComplex(Version, Type) { *this = other; }
+
+protected:
+ QStyleOptionTitleBar(int version);
+};
+
+class Q_GUI_EXPORT QStyleOptionGroupBox : public QStyleOptionComplex
+{
+public:
+ enum StyleOptionType { Type = SO_GroupBox };
+ enum StyleOptionVersion { Version = 1 };
+
+ QStyleOptionFrameV2::FrameFeatures features;
+ QString text;
+ Qt::Alignment textAlignment;
+ QColor textColor;
+ int lineWidth;
+ int midLineWidth;
+
+ QStyleOptionGroupBox();
+ QStyleOptionGroupBox(const QStyleOptionGroupBox &other) : QStyleOptionComplex(Version, Type) { *this = other; }
+protected:
+ QStyleOptionGroupBox(int version);
+};
+
+class Q_GUI_EXPORT QStyleOptionSizeGrip : public QStyleOptionComplex
+{
+public:
+ enum StyleOptionType { Type = SO_SizeGrip };
+ enum StyleOptionVersion { Version = 1 };
+
+ Qt::Corner corner;
+
+ QStyleOptionSizeGrip();
+ QStyleOptionSizeGrip(const QStyleOptionSizeGrip &other) : QStyleOptionComplex(Version, Type) { *this = other; }
+protected:
+ QStyleOptionSizeGrip(int version);
+};
+
+class Q_GUI_EXPORT QStyleOptionGraphicsItem : public QStyleOption
+{
+public:
+ enum StyleOptionType { Type = SO_GraphicsItem };
+ enum StyleOptionVersion { Version = 1 };
+
+ QRectF exposedRect;
+ QMatrix matrix;
+ qreal levelOfDetail;
+
+ QStyleOptionGraphicsItem();
+ QStyleOptionGraphicsItem(const QStyleOptionGraphicsItem &other) : QStyleOption(Version, Type) { *this = other; }
+protected:
+ QStyleOptionGraphicsItem(int version);
+};
+
+template <typename T>
+T qstyleoption_cast(const QStyleOption *opt)
+{
+ if (opt && opt->version >= static_cast<T>(0)->Version && (opt->type == static_cast<T>(0)->Type
+ || int(static_cast<T>(0)->Type) == QStyleOption::SO_Default
+ || (int(static_cast<T>(0)->Type) == QStyleOption::SO_Complex
+ && opt->type > QStyleOption::SO_Complex)))
+ return static_cast<T>(opt);
+ return 0;
+}
+
+template <typename T>
+T qstyleoption_cast(QStyleOption *opt)
+{
+ if (opt && opt->version >= static_cast<T>(0)->Version && (opt->type == static_cast<T>(0)->Type
+ || int(static_cast<T>(0)->Type) == QStyleOption::SO_Default
+ || (int(static_cast<T>(0)->Type) == QStyleOption::SO_Complex
+ && opt->type > QStyleOption::SO_Complex)))
+ return static_cast<T>(opt);
+ return 0;
+}
+
+// -------------------------- QStyleHintReturn -------------------------------
+class Q_GUI_EXPORT QStyleHintReturn {
+public:
+ enum HintReturnType {
+ SH_Default=0xf000, SH_Mask, SH_Variant
+ };
+
+ enum StyleOptionType { Type = SH_Default };
+ enum StyleOptionVersion { Version = 1 };
+
+ QStyleHintReturn(int version = QStyleOption::Version, int type = SH_Default);
+ ~QStyleHintReturn();
+
+ int version;
+ int type;
+};
+
+class Q_GUI_EXPORT QStyleHintReturnMask : public QStyleHintReturn {
+public:
+ enum StyleOptionType { Type = SH_Mask };
+ enum StyleOptionVersion { Version = 1 };
+
+ QStyleHintReturnMask();
+
+ QRegion region;
+};
+
+class Q_GUI_EXPORT QStyleHintReturnVariant : public QStyleHintReturn {
+public:
+ enum StyleOptionType { Type = SH_Variant };
+ enum StyleOptionVersion { Version = 1 };
+
+ QStyleHintReturnVariant();
+
+ QVariant variant;
+};
+
+template <typename T>
+T qstyleoption_cast(const QStyleHintReturn *hint)
+{
+ if (hint && hint->version <= static_cast<T>(0)->Version &&
+ (hint->type == static_cast<T>(0)->Type || int(static_cast<T>(0)->Type) == QStyleHintReturn::SH_Default))
+ return static_cast<T>(hint);
+ return 0;
+}
+
+template <typename T>
+T qstyleoption_cast(QStyleHintReturn *hint)
+{
+ if (hint && hint->version <= static_cast<T>(0)->Version &&
+ (hint->type == static_cast<T>(0)->Type || int(static_cast<T>(0)->Type) == QStyleHintReturn::SH_Default))
+ return static_cast<T>(hint);
+ return 0;
+}
+
+#if !defined(QT_NO_DEBUG_STREAM) && !defined(QT_NO_DEBUG)
+Q_GUI_EXPORT QDebug operator<<(QDebug debug, const QStyleOption::OptionType &optionType);
+Q_GUI_EXPORT QDebug operator<<(QDebug debug, const QStyleOption &option);
+#endif
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QSTYLEOPTION_H
diff --git a/src/gui/styles/qstyleplugin.cpp b/src/gui/styles/qstyleplugin.cpp
new file mode 100644
index 0000000000..19c08be0a9
--- /dev/null
+++ b/src/gui/styles/qstyleplugin.cpp
@@ -0,0 +1,115 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qstyleplugin.h"
+#include "qstyle.h"
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QStylePlugin
+ \brief The QStylePlugin class provides an abstract base for custom QStyle plugins.
+
+ \ingroup plugins
+
+ QStylePlugin is a simple plugin interface that makes it easy
+ to create custom styles that can be loaded dynamically into
+ applications using the QStyleFactory class.
+
+ Writing a style plugin is achieved by subclassing this base class,
+ reimplementing the pure virtual keys() and create() functions, and
+ exporting the class using the Q_EXPORT_PLUGIN2() macro. See \l
+ {How to Create Qt Plugins} for details.
+
+ \sa QStyleFactory, QStyle
+*/
+
+/*!
+ \fn QStringList QStylePlugin::keys() const
+
+ Returns the list of style keys this plugin supports.
+
+ These keys are usually the class names of the custom styles that
+ are implemented in the plugin.
+
+ \sa create()
+*/
+
+/*!
+ \fn QStyle *QStylePlugin::create(const QString& key)
+
+ Creates and returns a QStyle object for the given style \a key.
+ If a plugin cannot create a style, it should return 0 instead.
+
+ The style key is usually the class name of the required
+ style. Note that the keys are case insensitive. For example:
+
+ \snippet doc/src/snippets/qstyleplugin/main.cpp 0
+ \codeline
+ \snippet doc/src/snippets/qstyleplugin/main.cpp 1
+ \snippet doc/src/snippets/qstyleplugin/main.cpp 2
+
+ \sa keys()
+*/
+
+/*!
+ Constructs a style plugin with the given \a parent.
+
+ Note that this constructor is invoked automatically by the
+ Q_EXPORT_PLUGIN2() macro, so there is no need for calling it
+ explicitly.
+*/
+QStylePlugin::QStylePlugin(QObject *parent)
+ : QObject(parent)
+{
+}
+
+/*!
+ Destroys the style plugin.
+
+ Note that Qt destroys a plugin automatically when it is no longer
+ used, so there is no need for calling the destructor explicitly.
+*/
+QStylePlugin::~QStylePlugin()
+{
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/styles/qstyleplugin.h b/src/gui/styles/qstyleplugin.h
new file mode 100644
index 0000000000..c7d9202c59
--- /dev/null
+++ b/src/gui/styles/qstyleplugin.h
@@ -0,0 +1,81 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSTYLEPLUGIN_H
+#define QSTYLEPLUGIN_H
+
+#include <QtCore/qplugin.h>
+#include <QtCore/qfactoryinterface.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+class QStyle;
+
+struct Q_GUI_EXPORT QStyleFactoryInterface : public QFactoryInterface
+{
+ virtual QStyle *create(const QString &key) = 0;
+};
+
+#define QStyleFactoryInterface_iid "com.trolltech.Qt.QStyleFactoryInterface"
+
+Q_DECLARE_INTERFACE(QStyleFactoryInterface, QStyleFactoryInterface_iid)
+
+class Q_GUI_EXPORT QStylePlugin : public QObject, public QStyleFactoryInterface
+{
+ Q_OBJECT
+ Q_INTERFACES(QStyleFactoryInterface:QFactoryInterface)
+public:
+ explicit QStylePlugin(QObject *parent = 0);
+ ~QStylePlugin();
+
+ virtual QStringList keys() const = 0;
+ virtual QStyle *create(const QString &key) = 0;
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QSTYLEPLUGIN_H
diff --git a/src/gui/styles/qstylesheetstyle.cpp b/src/gui/styles/qstylesheetstyle.cpp
new file mode 100644
index 0000000000..2558409501
--- /dev/null
+++ b/src/gui/styles/qstylesheetstyle.cpp
@@ -0,0 +1,5946 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <qglobal.h>
+
+#ifndef QT_NO_STYLE_STYLESHEET
+
+#include "qstylesheetstyle_p.h"
+#include "private/qcssutil_p.h"
+#include <qdebug.h>
+#include <qapplication.h>
+#include <qmenu.h>
+#include <qmenubar.h>
+#include <qpainter.h>
+#include <qstyleoption.h>
+#include <qlineedit.h>
+#include <qwindowsstyle.h>
+#include <qcombobox.h>
+#include <qwindowsstyle.h>
+#include <qplastiquestyle.h>
+#include "private/qcssparser_p.h"
+#include "private/qmath_p.h"
+#include <qabstractscrollarea.h>
+#include "private/qabstractscrollarea_p.h"
+#include <qtooltip.h>
+#include <qshareddata.h>
+#include <qradiobutton.h>
+#include <qtoolbutton.h>
+#include <qscrollbar.h>
+#include <qstring.h>
+#include <qfile.h>
+#include <qcheckbox.h>
+#include <qstatusbar.h>
+#include <qheaderview.h>
+#include <qprogressbar.h>
+#include <private/qwindowsstyle_p.h>
+#include <qtabbar.h>
+#include <QMetaProperty>
+#include <qmainwindow.h>
+#include <qdockwidget.h>
+#include <qmdisubwindow.h>
+#include <qdialog.h>
+#include <private/qwidget_p.h>
+#include <QAbstractSpinBox>
+#include <QLabel>
+
+#include <limits.h>
+
+QT_BEGIN_NAMESPACE
+
+using namespace QCss;
+
+
+class QStyleSheetStylePrivate : public QWindowsStylePrivate
+{
+ Q_DECLARE_PUBLIC(QStyleSheetStyle)
+public:
+ QStyleSheetStylePrivate() { }
+};
+
+
+static QHash<const QWidget *, QVector<StyleRule> > *styleRulesCache = 0;
+static QHash<const QWidget *, QHash<int, bool> > *hasStyleRuleCache = 0;
+typedef QHash<int, QHash<quint64, QRenderRule> > QRenderRules;
+static QHash<const QWidget *, QRenderRules> *renderRulesCache = 0;
+static QHash<const QWidget *, QPalette> *customPaletteWidgets = 0; // widgets whose palette we tampered
+static QHash<const void *, StyleSheet> *styleSheetCache = 0; // parsed style sheets
+static QSet<const QWidget *> *autoFillDisabledWidgets = 0;
+
+
+/* RECURSION_GUARD:
+ * the QStyleSheetStyle is a proxy. If used with others proxy style, we may end up with something like:
+ * QStyleSheetStyle -> ProxyStyle -> QStyleSheetStyle -> OriginalStyle
+ * Recursion may happen if the style call the widget()->style() again.
+ * Not to mention the performence penalty of having two lookup of rules.
+ *
+ * The first instance of QStyleSheetStyle will set globalStyleSheetStyle to itself. The second one
+ * will notice the globalStyleSheetStyle is not istelf and call its base style directly.
+ */
+static const QStyleSheetStyle *globalStyleSheetStyle = 0;
+class QStyleSheetStyleRecursionGuard
+{
+ public:
+ QStyleSheetStyleRecursionGuard(const QStyleSheetStyle *that)
+ : guarded(globalStyleSheetStyle == 0)
+ {
+ if (guarded) globalStyleSheetStyle = that;
+ }
+ ~QStyleSheetStyleRecursionGuard() { if (guarded) globalStyleSheetStyle = 0; }
+ bool guarded;
+};
+#define RECURSION_GUARD(RETURN) \
+ if (globalStyleSheetStyle != 0 && globalStyleSheetStyle != this) { RETURN; } \
+ QStyleSheetStyleRecursionGuard recursion_guard(this);
+
+#define ceil(x) ((int)(x) + ((x) > 0 && (x) != (int)(x)))
+
+enum PseudoElement {
+ PseudoElement_None,
+ PseudoElement_DownArrow,
+ PseudoElement_UpArrow,
+ PseudoElement_LeftArrow,
+ PseudoElement_RightArrow,
+ PseudoElement_Indicator,
+ PseudoElement_ExclusiveIndicator,
+ PseudoElement_PushButtonMenuIndicator,
+ PseudoElement_ComboBoxDropDown,
+ PseudoElement_ComboBoxArrow,
+ PseudoElement_Item,
+ PseudoElement_SpinBoxUpButton,
+ PseudoElement_SpinBoxUpArrow,
+ PseudoElement_SpinBoxDownButton,
+ PseudoElement_SpinBoxDownArrow,
+ PseudoElement_GroupBoxTitle,
+ PseudoElement_GroupBoxIndicator,
+ PseudoElement_ToolButtonMenu,
+ PseudoElement_ToolButtonMenuArrow,
+ PseudoElement_ToolButtonDownArrow,
+ PseudoElement_ToolBoxTab,
+ PseudoElement_ScrollBarSlider,
+ PseudoElement_ScrollBarAddPage,
+ PseudoElement_ScrollBarSubPage,
+ PseudoElement_ScrollBarAddLine,
+ PseudoElement_ScrollBarSubLine,
+ PseudoElement_ScrollBarFirst,
+ PseudoElement_ScrollBarLast,
+ PseudoElement_ScrollBarUpArrow,
+ PseudoElement_ScrollBarDownArrow,
+ PseudoElement_ScrollBarLeftArrow,
+ PseudoElement_ScrollBarRightArrow,
+ PseudoElement_SplitterHandle,
+ PseudoElement_ToolBarHandle,
+ PseudoElement_ToolBarSeparator,
+ PseudoElement_MenuScroller,
+ PseudoElement_MenuTearoff,
+ PseudoElement_MenuCheckMark,
+ PseudoElement_MenuSeparator,
+ PseudoElement_MenuIcon,
+ PseudoElement_MenuRightArrow,
+ PseudoElement_TreeViewBranch,
+ PseudoElement_HeaderViewSection,
+ PseudoElement_HeaderViewUpArrow,
+ PseudoElement_HeaderViewDownArrow,
+ PseudoElement_ProgressBarChunk,
+ PseudoElement_TabBarTab,
+ PseudoElement_TabBarScroller,
+ PseudoElement_TabBarTear,
+ PseudoElement_SliderGroove,
+ PseudoElement_SliderHandle,
+ PseudoElement_SliderAddPage,
+ PseudoElement_SliderSubPage,
+ PseudoElement_SliderTickmark,
+ PseudoElement_TabWidgetPane,
+ PseudoElement_TabWidgetTabBar,
+ PseudoElement_TabWidgetLeftCorner,
+ PseudoElement_TabWidgetRightCorner,
+ PseudoElement_DockWidgetTitle,
+ PseudoElement_DockWidgetCloseButton,
+ PseudoElement_DockWidgetFloatButton,
+ PseudoElement_DockWidgetSeparator,
+ PseudoElement_MdiCloseButton,
+ PseudoElement_MdiMinButton,
+ PseudoElement_MdiNormalButton,
+ PseudoElement_TitleBar,
+ PseudoElement_TitleBarCloseButton,
+ PseudoElement_TitleBarMinButton,
+ PseudoElement_TitleBarMaxButton,
+ PseudoElement_TitleBarShadeButton,
+ PseudoElement_TitleBarUnshadeButton,
+ PseudoElement_TitleBarNormalButton,
+ PseudoElement_TitleBarContextHelpButton,
+ PseudoElement_TitleBarSysMenu,
+ PseudoElement_ViewItem,
+ PseudoElement_ViewItemIcon,
+ PseudoElement_ViewItemText,
+ PseudoElement_ViewItemIndicator,
+ PseudoElement_ScrollAreaCorner,
+ NumPseudoElements
+};
+
+struct PseudoElementInfo {
+ QStyle::SubControl subControl;
+ const char *name;
+};
+
+static const PseudoElementInfo knownPseudoElements[NumPseudoElements] = {
+ { QStyle::SC_None, "", },
+ { QStyle::SC_None, "down-arrow" },
+ { QStyle::SC_None, "up-arrow" },
+ { QStyle::SC_None, "left-arrow" },
+ { QStyle::SC_None, "right-arrow" },
+ { QStyle::SC_None, "indicator" },
+ { QStyle::SC_None, "indicator" },
+ { QStyle::SC_None, "menu-indicator" },
+ { QStyle::SC_ComboBoxArrow, "drop-down" },
+ { QStyle::SC_ComboBoxArrow, "down-arrow" },
+ { QStyle::SC_None, "item" },
+ { QStyle::SC_SpinBoxUp, "up-button" },
+ { QStyle::SC_SpinBoxUp, "up-arrow" },
+ { QStyle::SC_SpinBoxDown, "down-button" },
+ { QStyle::SC_SpinBoxDown, "down-arrow" },
+ { QStyle::SC_GroupBoxLabel, "title" },
+ { QStyle::SC_GroupBoxCheckBox, "indicator" },
+ { QStyle::SC_ToolButtonMenu, "menu-button" },
+ { QStyle::SC_ToolButtonMenu, "menu-arrow" },
+ { QStyle::SC_None, "menu-indicator" },
+ { QStyle::SC_None, "tab" },
+ { QStyle::SC_ScrollBarSlider, "handle" },
+ { QStyle::SC_ScrollBarAddPage, "add-page" },
+ { QStyle::SC_ScrollBarSubPage, "sub-page" },
+ { QStyle::SC_ScrollBarAddLine, "add-line" },
+ { QStyle::SC_ScrollBarSubLine, "sub-line" },
+ { QStyle::SC_ScrollBarFirst, "first" },
+ { QStyle::SC_ScrollBarLast, "last" },
+ { QStyle::SC_ScrollBarSubLine, "up-arrow" },
+ { QStyle::SC_ScrollBarAddLine, "down-arrow" },
+ { QStyle::SC_ScrollBarSubLine, "left-arrow" },
+ { QStyle::SC_ScrollBarAddLine, "right-arrow" },
+ { QStyle::SC_None, "handle" },
+ { QStyle::SC_None, "handle" },
+ { QStyle::SC_None, "separator" },
+ { QStyle::SC_None, "scroller" },
+ { QStyle::SC_None, "tearoff" },
+ { QStyle::SC_None, "indicator" },
+ { QStyle::SC_None, "separator" },
+ { QStyle::SC_None, "icon" },
+ { QStyle::SC_None, "right-arrow" },
+ { QStyle::SC_None, "branch" },
+ { QStyle::SC_None, "section" },
+ { QStyle::SC_None, "down-arrow" },
+ { QStyle::SC_None, "up-arrow" },
+ { QStyle::SC_None, "chunk" },
+ { QStyle::SC_None, "tab" },
+ { QStyle::SC_None, "scroller" },
+ { QStyle::SC_None, "tear" },
+ { QStyle::SC_SliderGroove, "groove" },
+ { QStyle::SC_SliderHandle, "handle" },
+ { QStyle::SC_None, "add-page" },
+ { QStyle::SC_None, "sub-page"},
+ { QStyle::SC_SliderTickmarks, "tick-mark" },
+ { QStyle::SC_None, "pane" },
+ { QStyle::SC_None, "tab-bar" },
+ { QStyle::SC_None, "left-corner" },
+ { QStyle::SC_None, "right-corner" },
+ { QStyle::SC_None, "title" },
+ { QStyle::SC_None, "close-button" },
+ { QStyle::SC_None, "float-button" },
+ { QStyle::SC_None, "separator" },
+ { QStyle::SC_MdiCloseButton, "close-button" },
+ { QStyle::SC_MdiMinButton, "minimize-button" },
+ { QStyle::SC_MdiNormalButton, "normal-button" },
+ { QStyle::SC_TitleBarLabel, "title" },
+ { QStyle::SC_TitleBarCloseButton, "close-button" },
+ { QStyle::SC_TitleBarMinButton, "minimize-button" },
+ { QStyle::SC_TitleBarMaxButton, "maximize-button" },
+ { QStyle::SC_TitleBarShadeButton, "shade-button" },
+ { QStyle::SC_TitleBarUnshadeButton, "unshade-button" },
+ { QStyle::SC_TitleBarNormalButton, "normal-button" },
+ { QStyle::SC_TitleBarContextHelpButton, "contexthelp-button" },
+ { QStyle::SC_TitleBarSysMenu, "sys-menu" },
+ { QStyle::SC_None, "item" },
+ { QStyle::SC_None, "icon" },
+ { QStyle::SC_None, "text" },
+ { QStyle::SC_None, "indicator" } ,
+ { QStyle::SC_None, "corner" }
+};
+
+
+struct QStyleSheetBorderImageData : public QSharedData
+{
+ QStyleSheetBorderImageData()
+ : horizStretch(QCss::TileMode_Unknown), vertStretch(QCss::TileMode_Unknown)
+ {
+ for (int i = 0; i < 4; i++)
+ cuts[i] = -1;
+ }
+ QPixmap topEdge, bottomEdge, leftEdge, rightEdge, middle;
+ QRect topEdgeRect, bottomEdgeRect, leftEdgeRect, rightEdgeRect, middleRect;
+ QRect topLeftCorner, topRightCorner, bottomRightCorner, bottomLeftCorner;
+ int cuts[4];
+ QPixmap pixmap;
+ QImage image;
+ QCss::TileMode horizStretch, vertStretch;
+
+ void cutBorderImage();
+};
+
+struct QStyleSheetBackgroundData : public QSharedData
+{
+ QStyleSheetBackgroundData(const QBrush& b, const QPixmap& p, QCss::Repeat r,
+ Qt::Alignment a, QCss::Origin o, Attachment t, QCss::Origin c)
+ : brush(b), pixmap(p), repeat(r), position(a), origin(o), attachment(t), clip(c) { }
+
+ bool isTransparent() const {
+ if (brush.style() != Qt::NoBrush)
+ return !brush.isOpaque();
+ return pixmap.isNull() ? false : pixmap.hasAlpha();
+ }
+ QBrush brush;
+ QPixmap pixmap;
+ QCss::Repeat repeat;
+ Qt::Alignment position;
+ QCss::Origin origin;
+ QCss::Attachment attachment;
+ QCss::Origin clip;
+};
+
+struct QStyleSheetBorderData : public QSharedData
+{
+ QStyleSheetBorderData() : bi(0)
+ {
+ for (int i = 0; i < 4; i++) {
+ borders[i] = 0;
+ styles[i] = QCss::BorderStyle_None;
+ }
+ }
+
+ QStyleSheetBorderData(int *b, QBrush *c, QCss::BorderStyle *s, QSize *r) : bi(0)
+ {
+ for (int i = 0; i < 4; i++) {
+ borders[i] = b[i];
+ styles[i] = s[i];
+ colors[i] = c[i];
+ radii[i] = r[i];
+ }
+ }
+
+ int borders[4];
+ QBrush colors[4];
+ QCss::BorderStyle styles[4];
+ QSize radii[4]; // topleft, topright, bottomleft, bottomright
+
+ const QStyleSheetBorderImageData *borderImage() const
+ { return bi; }
+ bool hasBorderImage() const { return bi!=0; }
+
+ QSharedDataPointer<QStyleSheetBorderImageData> bi;
+
+ bool isOpaque() const
+ {
+ for (int i = 0; i < 4; i++) {
+ if (styles[i] == QCss::BorderStyle_Native || styles[i] == QCss::BorderStyle_None)
+ continue;
+ if (styles[i] >= QCss::BorderStyle_Dotted && styles[i] <= QCss::BorderStyle_DotDotDash
+ && styles[i] != BorderStyle_Solid)
+ return false;
+ if (!colors[i].isOpaque())
+ return false;
+ if (!radii[i].isEmpty())
+ return false;
+ }
+ if (bi != 0 && bi->pixmap.hasAlpha())
+ return false;
+ return true;
+ }
+};
+
+
+struct QStyleSheetOutlineData : public QStyleSheetBorderData
+{
+ QStyleSheetOutlineData()
+ {
+ for (int i = 0; i < 4; i++) {
+ offsets[i] = 0;
+ }
+ }
+
+ QStyleSheetOutlineData(int *b, QBrush *c, QCss::BorderStyle *s, QSize *r, int *o)
+ : QStyleSheetBorderData(b, c, s, r)
+ {
+ for (int i = 0; i < 4; i++) {
+ offsets[i] = o[i];
+ }
+ }
+
+ int offsets[4];
+};
+
+struct QStyleSheetBoxData : public QSharedData
+{
+ QStyleSheetBoxData(int *m, int *p, int s) : spacing(s)
+ {
+ for (int i = 0; i < 4; i++) {
+ margins[i] = m[i];
+ paddings[i] = p[i];
+ }
+ }
+
+ int margins[4];
+ int paddings[4];
+
+ int spacing;
+};
+
+struct QStyleSheetPaletteData : public QSharedData
+{
+ QStyleSheetPaletteData(const QBrush &fg, const QBrush &sfg, const QBrush &sbg,
+ const QBrush &abg)
+ : foreground(fg), selectionForeground(sfg), selectionBackground(sbg),
+ alternateBackground(abg) { }
+
+ QBrush foreground;
+ QBrush selectionForeground;
+ QBrush selectionBackground;
+ QBrush alternateBackground;
+};
+
+struct QStyleSheetGeometryData : public QSharedData
+{
+ QStyleSheetGeometryData(int w, int h, int minw, int minh, int maxw, int maxh)
+ : minWidth(minw), minHeight(minh), width(w), height(h), maxWidth(maxw), maxHeight(maxh) { }
+
+ int minWidth, minHeight, width, height, maxWidth, maxHeight;
+};
+
+struct QStyleSheetPositionData : public QSharedData
+{
+ QStyleSheetPositionData(int l, int t, int r, int b, Origin o, Qt::Alignment p, QCss::PositionMode m, Qt::Alignment a = 0)
+ : left(l), top(t), bottom(b), right(r), origin(o), position(p), mode(m), textAlignment(a) { }
+
+ int left, top, bottom, right;
+ Origin origin;
+ Qt::Alignment position;
+ QCss::PositionMode mode;
+ Qt::Alignment textAlignment;
+};
+
+struct QStyleSheetImageData : public QSharedData
+{
+ QStyleSheetImageData(const QIcon &i, Qt::Alignment a, const QSize &sz)
+ : icon(i), alignment(a), size(sz) { }
+
+ QIcon icon;
+ Qt::Alignment alignment;
+ QSize size;
+};
+
+class QRenderRule
+{
+public:
+ QRenderRule() : features(0), hasFont(false), pal(0), b(0), bg(0), bd(0), ou(0), geo(0), p(0), img(0), clipset(0) { }
+ QRenderRule(const QVector<QCss::Declaration> &, const QWidget *);
+ ~QRenderRule() { }
+
+ QRect borderRect(const QRect &r) const;
+ QRect outlineRect(const QRect &r) const;
+ QRect paddingRect(const QRect &r) const;
+ QRect contentsRect(const QRect &r) const;
+
+ enum { Margin = 1, Border = 2, Padding = 4, All=Margin|Border|Padding };
+ QRect boxRect(const QRect &r, int flags = All) const;
+ QSize boxSize(const QSize &s, int flags = All) const;
+ QRect originRect(const QRect &rect, Origin origin) const;
+
+ QPainterPath borderClip(QRect rect);
+ void drawBorder(QPainter *, const QRect&);
+ void drawOutline(QPainter *, const QRect&);
+ void drawBorderImage(QPainter *, const QRect&);
+ void drawBackground(QPainter *, const QRect&, const QPoint& = QPoint(0, 0));
+ void drawBackgroundImage(QPainter *, const QRect&, QPoint = QPoint(0, 0));
+ void drawFrame(QPainter *, const QRect&);
+ void drawImage(QPainter *p, const QRect &rect);
+ void drawRule(QPainter *, const QRect&);
+ void configurePalette(QPalette *, QPalette::ColorGroup, const QWidget *, bool);
+ void configurePalette(QPalette *p, QPalette::ColorRole fr, QPalette::ColorRole br);
+
+ const QStyleSheetPaletteData *palette() const { return pal; }
+ const QStyleSheetBoxData *box() const { return b; }
+ const QStyleSheetBackgroundData *background() const { return bg; }
+ const QStyleSheetBorderData *border() const { return bd; }
+ const QStyleSheetOutlineData *outline() const { return ou; }
+ const QStyleSheetGeometryData *geometry() const { return geo; }
+ const QStyleSheetPositionData *position() const { return p; }
+
+ bool hasPalette() const { return pal != 0; }
+ bool hasBackground() const { return bg != 0 && (!bg->pixmap.isNull() || bg->brush.style() != Qt::NoBrush); }
+ bool hasGradientBackground() const { return bg && bg->brush.style() >= Qt::LinearGradientPattern
+ && bg->brush.style() <= Qt::ConicalGradientPattern; }
+
+ bool hasNativeBorder() const {
+ return bd == 0
+ || (!bd->hasBorderImage() && bd->styles[0] == BorderStyle_Native);
+ }
+
+ bool hasNativeOutline() const {
+ return (ou == 0
+ || (!ou->hasBorderImage() && ou->styles[0] == BorderStyle_Native));
+ }
+
+ bool baseStyleCanDraw() const {
+ if (!hasBackground() || (background()->brush.style() == Qt::NoBrush && bg->pixmap.isNull()))
+ return true;
+ if (bg && !bg->pixmap.isNull())
+ return false;
+ if (hasGradientBackground())
+ return features & StyleFeature_BackgroundGradient;
+ return features & StyleFeature_BackgroundColor;
+ }
+
+ bool hasBox() const { return b != 0; }
+ bool hasBorder() const { return bd != 0; }
+ bool hasOutline() const { return ou != 0; }
+ bool hasPosition() const { return p != 0; }
+ bool hasGeometry() const { return geo != 0; }
+ bool hasDrawable() const { return !hasNativeBorder() || hasBackground() || hasImage(); }
+ bool hasImage() const { return img != 0; }
+
+ QSize minimumContentsSize() const
+ { return geo ? QSize(geo->minWidth, geo->minHeight) : QSize(0, 0); }
+ QSize minimumSize() const
+ { return boxSize(minimumContentsSize()); }
+
+ QSize contentsSize() const
+ { return geo ? QSize(geo->width, geo->height)
+ : ((img && img->size.isValid()) ? img->size : QSize()); }
+ QSize contentsSize(const QSize &sz) const
+ {
+ QSize csz = contentsSize();
+ if (csz.width() == -1) csz.setWidth(sz.width());
+ if (csz.height() == -1) csz.setHeight(sz.height());
+ return csz;
+ }
+ bool hasContentsSize() const
+ { return (geo && (geo->width != -1 || geo->height != -1)) || (img && img->size.isValid()); }
+
+ QSize size() const { return boxSize(contentsSize()); }
+ QSize size(const QSize &sz) const { return boxSize(contentsSize(sz)); }
+ QSize adjustSize(const QSize &sz)
+ {
+ if (!geo)
+ return sz;
+ QSize csz = contentsSize();
+ if (csz.width() == -1) csz.setWidth(sz.width());
+ if (csz.height() == -1) csz.setHeight(sz.height());
+ if (geo->maxWidth != -1 && csz.width() > geo->maxWidth) csz.setWidth(geo->maxWidth);
+ if (geo->maxHeight != -1 && csz.height() > geo->maxHeight) csz.setHeight(geo->maxHeight);
+ csz=csz.expandedTo(QSize(geo->minWidth, geo->minHeight));
+ return csz;
+ }
+
+ int features;
+ QBrush defaultBackground;
+ QFont font;
+ bool hasFont;
+
+ QHash<QString, QVariant> styleHints;
+ bool hasStyleHint(const QString& sh) const { return styleHints.contains(sh); }
+ QVariant styleHint(const QString& sh) const { return styleHints.value(sh); }
+
+ void fixupBorder(int);
+
+ QSharedDataPointer<QStyleSheetPaletteData> pal;
+ QSharedDataPointer<QStyleSheetBoxData> b;
+ QSharedDataPointer<QStyleSheetBackgroundData> bg;
+ QSharedDataPointer<QStyleSheetBorderData> bd;
+ QSharedDataPointer<QStyleSheetOutlineData> ou;
+ QSharedDataPointer<QStyleSheetGeometryData> geo;
+ QSharedDataPointer<QStyleSheetPositionData> p;
+ QSharedDataPointer<QStyleSheetImageData> img;
+
+ // Shouldn't be here
+ void setClip(QPainter *p, const QRect &rect);
+ void unsetClip(QPainter *);
+ int clipset;
+ QPainterPath clipPath;
+};
+
+///////////////////////////////////////////////////////////////////////////////////////////
+static const char *knownStyleHints[] = {
+ "activate-on-singleclick",
+ "alignment",
+ "arrow-keys-navigate-into-children",
+ "backward-icon",
+ "button-layout",
+ "cd-icon",
+ "combobox-list-mousetracking",
+ "combobox-popup",
+ "computer-icon",
+ "desktop-icon",
+ "dialog-apply-icon",
+ "dialog-cancel-icon",
+ "dialog-close-icon",
+ "dialog-discard-icon",
+ "dialog-help-icon",
+ "dialog-no-icon",
+ "dialog-ok-icon",
+ "dialog-open-icon",
+ "dialog-reset-icon",
+ "dialog-save-icon",
+ "dialog-yes-icon",
+ "dialogbuttonbox-buttons-have-icons",
+ "directory-closed-icon",
+ "directory-icon",
+ "directory-link-icon",
+ "directory-open-icon",
+ "dither-disable-text",
+ "dockwidget-close-icon",
+ "downarrow-icon",
+ "dvd-icon",
+ "etch-disabled-text",
+ "file-icon",
+ "file-link-icon",
+ "filedialog-backward-icon", // unused
+ "filedialog-contentsview-icon",
+ "filedialog-detailedview-icon",
+ "filedialog-end-icon",
+ "filedialog-infoview-icon",
+ "filedialog-listview-icon",
+ "filedialog-new-directory-icon",
+ "filedialog-parent-directory-icon",
+ "filedialog-start-icon",
+ "floppy-icon",
+ "forward-icon",
+ "gridline-color",
+ "harddisk-icon",
+ "home-icon",
+ "icon-size",
+ "leftarrow-icon",
+ "lineedit-password-character",
+ "mdi-fill-space-on-maximize",
+ "menu-scrollable",
+ "menubar-altkey-navigation",
+ "menubar-separator",
+ "messagebox-critical-icon",
+ "messagebox-information-icon",
+ "messagebox-question-icon",
+ "messagebox-text-interaction-flags",
+ "messagebox-warning-icon",
+ "mouse-tracking",
+ "network-icon",
+ "opacity",
+ "paint-alternating-row-colors-for-empty-area",
+ "rightarrow-icon",
+ "scrollbar-contextmenu",
+ "scrollbar-leftclick-absolute-position",
+ "scrollbar-middleclick-absolute-position",
+ "scrollbar-roll-between-buttons",
+ "scrollbar-scroll-when-pointer-leaves-control",
+ "scrollview-frame-around-contents",
+ "show-decoration-selected",
+ "spinbox-click-autorepeat-rate",
+ "spincontrol-disable-on-bounds",
+ "tabbar-elide-mode",
+ "tabbar-prefer-no-arrows",
+ "titlebar-close-icon",
+ "titlebar-contexthelp-icon",
+ "titlebar-maximize-icon",
+ "titlebar-menu-icon",
+ "titlebar-minimize-icon",
+ "titlebar-normal-icon",
+ "titlebar-shade-icon",
+ "titlebar-unshade-icon",
+ "toolbutton-popup-delay",
+ "trash-icon",
+ "uparrow-icon"
+};
+
+static const int numKnownStyleHints = sizeof(knownStyleHints)/sizeof(knownStyleHints[0]);
+
+static QList<QVariant> subControlLayout(const QString& layout)
+{
+ QList<QVariant> buttons;
+ for (int i = 0; i < layout.count(); i++) {
+ int button = layout[i].toAscii();
+ switch (button) {
+ case 'm':
+ buttons.append(PseudoElement_MdiMinButton);
+ buttons.append(PseudoElement_TitleBarMinButton);
+ break;
+ case 'M':
+ buttons.append(PseudoElement_TitleBarMaxButton);
+ break;
+ case 'X':
+ buttons.append(PseudoElement_MdiCloseButton);
+ buttons.append(PseudoElement_TitleBarCloseButton);
+ break;
+ case 'N':
+ buttons.append(PseudoElement_MdiNormalButton);
+ buttons.append(PseudoElement_TitleBarNormalButton);
+ break;
+ case 'I':
+ buttons.append(PseudoElement_TitleBarSysMenu);
+ break;
+ case 'T':
+ buttons.append(PseudoElement_TitleBar);
+ break;
+ case 'H':
+ buttons.append(PseudoElement_TitleBarContextHelpButton);
+ break;
+ case 'S':
+ buttons.append(PseudoElement_TitleBarShadeButton);
+ break;
+ default:
+ buttons.append(button);
+ break;
+ }
+ }
+ return buttons;
+}
+
+namespace {
+ struct ButtonInfo {
+ QRenderRule rule;
+ int element;
+ int offset;
+ int where;
+ int width;
+ };
+}
+
+QHash<QStyle::SubControl, QRect> QStyleSheetStyle::titleBarLayout(const QWidget *w, const QStyleOptionTitleBar *tb) const
+{
+ QHash<QStyle::SubControl, QRect> layoutRects;
+ const bool isMinimized = tb->titleBarState & Qt::WindowMinimized;
+ const bool isMaximized = tb->titleBarState & Qt::WindowMaximized;
+ QRenderRule subRule = renderRule(w, tb);
+ QRect cr = subRule.contentsRect(tb->rect);
+ QList<QVariant> layout = subRule.styleHint(QLatin1String("button-layout")).toList();
+ if (layout.isEmpty())
+ layout = subControlLayout(QLatin1String("I(T)HSmMX"));
+
+ int offsets[3] = { 0, 0, 0 };
+ enum Where { Left, Right, Center, NoWhere } where = Left;
+ QList<ButtonInfo> infos;
+ for (int i = 0; i < layout.count(); i++) {
+ ButtonInfo info;
+ info.element = layout[i].toInt();
+ if (info.element == '(') {
+ where = Center;
+ } else if (info.element == ')') {
+ where = Right;
+ } else {
+ switch (info.element) {
+ case PseudoElement_TitleBar:
+ if (!(tb->titleBarFlags & (Qt::WindowTitleHint | Qt::WindowSystemMenuHint)))
+ continue;
+ break;
+ case PseudoElement_TitleBarContextHelpButton:
+ if (!(tb->titleBarFlags & Qt::WindowContextHelpButtonHint))
+ continue;
+ break;
+ case PseudoElement_TitleBarMinButton:
+ if (!(tb->titleBarFlags & Qt::WindowMinimizeButtonHint))
+ continue;
+ if (isMinimized)
+ info.element = PseudoElement_TitleBarNormalButton;
+ break;
+ case PseudoElement_TitleBarMaxButton:
+ if (!(tb->titleBarFlags & Qt::WindowMaximizeButtonHint))
+ continue;
+ if (isMaximized)
+ info.element = PseudoElement_TitleBarNormalButton;
+ break;
+ case PseudoElement_TitleBarShadeButton:
+ if (!(tb->titleBarFlags & Qt::WindowShadeButtonHint))
+ continue;
+ if (isMinimized)
+ info.element = PseudoElement_TitleBarUnshadeButton;
+ break;
+ case PseudoElement_TitleBarCloseButton:
+ case PseudoElement_TitleBarSysMenu:
+ if (!(tb->titleBarFlags & Qt::WindowSystemMenuHint))
+ continue;
+ break;
+ default:
+ continue;
+ }
+ if (info.element == PseudoElement_TitleBar) {
+ info.width = tb->fontMetrics.width(tb->text) + 6;
+ subRule.geo = new QStyleSheetGeometryData(info.width, tb->fontMetrics.height(), -1, -1, -1, -1);
+ } else {
+ subRule = renderRule(w, tb, info.element);
+ info.width = subRule.size().width();
+ }
+ info.rule = subRule;
+ info.offset = offsets[where];
+ info.where = where;
+ infos.append(info);
+
+ offsets[where] += info.width;
+ }
+ }
+
+ for (int i = 0; i < infos.count(); i++) {
+ ButtonInfo info = infos[i];
+ QRect lr = cr;
+ switch (info.where) {
+ case Center: {
+ lr.setLeft(cr.left() + offsets[Left]);
+ lr.setRight(cr.right() - offsets[Right]);
+ QRect r(0, 0, offsets[Center], lr.height());
+ r.moveCenter(lr.center());
+ r.setLeft(r.left()+info.offset);
+ r.setWidth(info.width);
+ lr = r;
+ break; }
+ case Left:
+ lr.translate(info.offset, 0);
+ lr.setWidth(info.width);
+ break;
+ case Right:
+ lr.moveLeft(cr.right() + 1 - offsets[Right] + info.offset);
+ lr.setWidth(info.width);
+ break;
+ default:
+ break;
+ }
+ QStyle::SubControl control = knownPseudoElements[info.element].subControl;
+ layoutRects[control] = positionRect(w, info.rule, info.element, lr, tb->direction);
+ }
+
+ return layoutRects;
+}
+
+static QStyle::StandardPixmap subControlIcon(int pe)
+{
+ switch (pe) {
+ case PseudoElement_MdiCloseButton: return QStyle::SP_TitleBarCloseButton;
+ case PseudoElement_MdiMinButton: return QStyle::SP_TitleBarMinButton;
+ case PseudoElement_MdiNormalButton: return QStyle::SP_TitleBarNormalButton;
+ case PseudoElement_TitleBarCloseButton: return QStyle::SP_TitleBarCloseButton;
+ case PseudoElement_TitleBarMinButton: return QStyle::SP_TitleBarMinButton;
+ case PseudoElement_TitleBarMaxButton: return QStyle::SP_TitleBarMaxButton;
+ case PseudoElement_TitleBarShadeButton: return QStyle::SP_TitleBarShadeButton;
+ case PseudoElement_TitleBarUnshadeButton: return QStyle::SP_TitleBarUnshadeButton;
+ case PseudoElement_TitleBarNormalButton: return QStyle::SP_TitleBarNormalButton;
+ case PseudoElement_TitleBarContextHelpButton: return QStyle::SP_TitleBarContextHelpButton;
+ default: break;
+ }
+ return QStyle::SP_CustomBase;
+}
+
+QRenderRule::QRenderRule(const QVector<Declaration> &declarations, const QWidget *widget)
+: features(0), hasFont(false), pal(0), b(0), bg(0), bd(0), ou(0), geo(0), p(0), img(0), clipset(0)
+{
+ QPalette palette = qApp->palette(); // ###: ideally widget's palette
+ ValueExtractor v(declarations, palette);
+ features = v.extractStyleFeatures();
+
+ int w = -1, h = -1, minw = -1, minh = -1, maxw = -1, maxh = -1;
+ if (v.extractGeometry(&w, &h, &minw, &minh, &maxw, &maxh))
+ geo = new QStyleSheetGeometryData(w, h, minw, minh, maxw, maxh);
+
+ int left = 0, top = 0, right = 0, bottom = 0;
+ Origin origin = Origin_Unknown;
+ Qt::Alignment position = 0;
+ QCss::PositionMode mode = PositionMode_Unknown;
+ Qt::Alignment textAlignment = 0;
+ if (v.extractPosition(&left, &top, &right, &bottom, &origin, &position, &mode, &textAlignment))
+ p = new QStyleSheetPositionData(left, top, right, bottom, origin, position, mode, textAlignment);
+
+ int margins[4], paddings[4], spacing = -1;
+ for (int i = 0; i < 4; i++)
+ margins[i] = paddings[i] = 0;
+ if (v.extractBox(margins, paddings, &spacing))
+ b = new QStyleSheetBoxData(margins, paddings, spacing);
+
+ int borders[4];
+ QBrush colors[4];
+ QCss::BorderStyle styles[4];
+ QSize radii[4];
+ for (int i = 0; i < 4; i++) {
+ borders[i] = 0;
+ styles[i] = BorderStyle_None;
+ }
+ if (v.extractBorder(borders, colors, styles, radii))
+ bd = new QStyleSheetBorderData(borders, colors, styles, radii);
+
+ int offsets[4];
+ for (int i = 0; i < 4; i++) {
+ borders[i] = offsets[i] = 0;
+ styles[i] = BorderStyle_None;
+ }
+ if (v.extractOutline(borders, colors, styles, radii, offsets))
+ ou = new QStyleSheetOutlineData(borders, colors, styles, radii, offsets);
+
+ QBrush brush;
+ QString uri;
+ Repeat repeat = Repeat_XY;
+ Qt::Alignment alignment = Qt::AlignTop | Qt::AlignLeft;
+ Attachment attachment = Attachment_Scroll;
+ origin = Origin_Padding;
+ Origin clip = Origin_Border;
+ if (v.extractBackground(&brush, &uri, &repeat, &alignment, &origin, &attachment, &clip))
+ bg = new QStyleSheetBackgroundData(brush, QPixmap(uri), repeat, alignment, origin, attachment, clip);
+
+ QBrush sfg, fg;
+ QBrush sbg, abg;
+ if (v.extractPalette(&fg, &sfg, &sbg, &abg))
+ pal = new QStyleSheetPaletteData(fg, sfg, sbg, abg);
+
+ QIcon icon;
+ alignment = Qt::AlignCenter;
+ QSize size;
+ if (v.extractImage(&icon, &alignment, &size))
+ img = new QStyleSheetImageData(icon, alignment, size);
+
+ int adj = -255;
+ hasFont = v.extractFont(&font, &adj);
+
+#ifndef QT_NO_TOOLTIP
+ if (widget && qstrcmp(widget->metaObject()->className(), "QTipLabel") == 0)
+ palette = QToolTip::palette();
+#endif
+
+ for (int i = 0; i < declarations.count(); i++) {
+ const Declaration& decl = declarations.at(i);
+ if (decl.d->propertyId == BorderImage) {
+ QString uri;
+ QCss::TileMode horizStretch, vertStretch;
+ int cuts[4];
+
+ decl.borderImageValue(&uri, cuts, &horizStretch, &vertStretch);
+ if (uri.isEmpty() || uri == QLatin1String("none")) {
+ if (bd && bd->bi)
+ bd->bi->pixmap = QPixmap();
+ } else {
+ if (!bd)
+ bd = new QStyleSheetBorderData;
+ if (!bd->bi)
+ bd->bi = new QStyleSheetBorderImageData;
+
+ QStyleSheetBorderImageData *bi = bd->bi;
+ bi->pixmap = QPixmap(uri);
+ for (int i = 0; i < 4; i++)
+ bi->cuts[i] = cuts[i];
+ bi->horizStretch = horizStretch;
+ bi->vertStretch = vertStretch;
+ }
+ } else if (decl.d->propertyId == QtBackgroundRole) {
+ if (bg && bg->brush.style() != Qt::NoBrush)
+ continue;
+ int role = decl.d->values.at(0).variant.toInt();
+ if (role >= Value_FirstColorRole && role <= Value_LastColorRole)
+ defaultBackground = palette.color((QPalette::ColorRole)(role-Value_FirstColorRole));
+ } else if (decl.d->property.startsWith(QLatin1String("qproperty-"), Qt::CaseInsensitive)) {
+ // intentionally left blank...
+ } else if (decl.d->propertyId == UnknownProperty) {
+ bool knownStyleHint = false;
+ for (int i = 0; i < numKnownStyleHints; i++) {
+ QLatin1String styleHint(knownStyleHints[i]);
+ if (decl.d->property.compare(styleHint) == 0) {
+ QString hintName = QString(styleHint);
+ QVariant hintValue;
+ if (hintName.endsWith(QLatin1String("alignment"))) {
+ hintValue = (int) decl.alignmentValue();
+ } else if (hintName.endsWith(QLatin1String("color"))) {
+ hintValue = (int) decl.colorValue().rgba();
+ } else if (hintName.endsWith(QLatin1String("size"))) {
+ hintValue = decl.sizeValue();
+ } else if (hintName.endsWith(QLatin1String("icon"))) {
+ hintValue = decl.iconValue();
+ } else if (hintName == QLatin1String("button-layout")
+ && decl.d->values.count() != 0 && decl.d->values.at(0).type == Value::String) {
+ hintValue = subControlLayout(decl.d->values.at(0).variant.toString());
+ } else {
+ int integer;
+ decl.intValue(&integer);
+ hintValue = integer;
+ }
+ styleHints[decl.d->property] = hintValue;
+ knownStyleHint = true;
+ break;
+ }
+ }
+ if (!knownStyleHint)
+ qDebug("Unknown property %s", qPrintable(decl.d->property));
+ }
+ }
+
+ if (widget) {
+ QStyleSheetStyle *style = const_cast<QStyleSheetStyle *>(globalStyleSheetStyle);
+ if (!style)
+ style = qobject_cast<QStyleSheetStyle *>(widget->style());
+ if (style)
+ fixupBorder(style->nativeFrameWidth(widget));
+
+ }
+ if (hasBorder() && border()->hasBorderImage())
+ defaultBackground = QBrush();
+}
+
+QRect QRenderRule::borderRect(const QRect& r) const
+{
+ if (!hasBox())
+ return r;
+ const int* m = box()->margins;
+ return r.adjusted(m[LeftEdge], m[TopEdge], -m[RightEdge], -m[BottomEdge]);
+}
+
+QRect QRenderRule::outlineRect(const QRect& r) const
+{
+ QRect br = borderRect(r);
+ if (!hasOutline())
+ return br;
+ const int *b = outline()->borders;
+ return r.adjusted(b[LeftEdge], b[TopEdge], -b[RightEdge], -b[BottomEdge]);
+}
+
+QRect QRenderRule::paddingRect(const QRect& r) const
+{
+ QRect br = borderRect(r);
+ if (!hasBorder())
+ return br;
+ const int *b = border()->borders;
+ return br.adjusted(b[LeftEdge], b[TopEdge], -b[RightEdge], -b[BottomEdge]);
+}
+
+QRect QRenderRule::contentsRect(const QRect& r) const
+{
+ QRect pr = paddingRect(r);
+ if (!hasBox())
+ return pr;
+ const int *p = box()->paddings;
+ return pr.adjusted(p[LeftEdge], p[TopEdge], -p[RightEdge], -p[BottomEdge]);
+}
+
+QRect QRenderRule::boxRect(const QRect& cr, int flags) const
+{
+ QRect r = cr;
+ if (hasBox()) {
+ if (flags & Margin) {
+ const int *m = box()->margins;
+ r.adjust(-m[LeftEdge], -m[TopEdge], m[RightEdge], m[BottomEdge]);
+ }
+ if (flags & Padding) {
+ const int *p = box()->paddings;
+ r.adjust(-p[LeftEdge], -p[TopEdge], p[RightEdge], p[BottomEdge]);
+ }
+ }
+ if (!hasNativeBorder() && (flags & Border)) {
+ const int *b = border()->borders;
+ r.adjust(-b[LeftEdge], -b[TopEdge], b[RightEdge], b[BottomEdge]);
+ }
+ return r;
+}
+
+QSize QRenderRule::boxSize(const QSize &cs, int flags) const
+{
+ QSize bs = boxRect(QRect(QPoint(0, 0), cs), flags).size();
+ if (cs.width() < 0) bs.setWidth(-1);
+ if (cs.height() < 0) bs.setHeight(-1);
+ return bs;
+}
+
+void QRenderRule::fixupBorder(int nativeWidth)
+{
+ if (bd == 0)
+ return;
+
+ if (!bd->hasBorderImage() || bd->bi->pixmap.isNull()) {
+ bd->bi = 0;
+ // ignore the color, border of edges that have none border-style
+ QBrush color = pal ? pal->foreground : QBrush();
+ const bool hasRadius = bd->radii[0].isValid() || bd->radii[1].isValid()
+ || bd->radii[2].isValid() || bd->radii[3].isValid();
+ for (int i = 0; i < 4; i++) {
+ if ((bd->styles[i] == BorderStyle_Native) && hasRadius)
+ bd->styles[i] = BorderStyle_None;
+
+ switch (bd->styles[i]) {
+ case BorderStyle_None:
+ // border-style: none forces width to be 0
+ bd->colors[i] = QBrush();
+ bd->borders[i] = 0;
+ break;
+ case BorderStyle_Native:
+ if (bd->borders[i] == 0)
+ bd->borders[i] = nativeWidth;
+ // intentional fall through
+ default:
+ if (!bd->colors[i].style() != Qt::NoBrush) // auto-acquire 'color'
+ bd->colors[i] = color;
+ break;
+ }
+ }
+
+ return;
+ }
+
+ // inspect the border image
+ QStyleSheetBorderImageData *bi = bd->bi;
+ if (bi->cuts[0] == -1) {
+ for (int i = 0; i < 4; i++) // assume, cut = border
+ bi->cuts[i] = int(border()->borders[i]);
+ }
+ bi->cutBorderImage();
+}
+
+void QStyleSheetBorderImageData::cutBorderImage()
+{
+ const int w = pixmap.width();
+ const int h = pixmap.height();
+ const int &l = cuts[LeftEdge], &r = cuts[RightEdge],
+ &t = cuts[TopEdge], &b = cuts[BottomEdge];
+
+ topEdgeRect = QRect(l, 0, w - r - l, t);
+ bottomEdgeRect = QRect(l, h - b, w - l - r, b);
+ if (horizStretch != TileMode_Stretch) {
+ if (topEdgeRect.isValid())
+ topEdge = pixmap.copy(topEdgeRect).scaledToHeight(t);
+ if (bottomEdgeRect.isValid())
+ bottomEdge = pixmap.copy(bottomEdgeRect).scaledToHeight(b);
+ }
+
+ leftEdgeRect = QRect(0, t, l, h - b - t);
+ rightEdgeRect = QRect(w - r, t, r, h - t- b);
+ if (vertStretch != TileMode_Stretch) {
+ if (leftEdgeRect.isValid())
+ leftEdge = pixmap.copy(leftEdgeRect).scaledToWidth(l);
+ if (rightEdgeRect.isValid())
+ rightEdge = pixmap.copy(rightEdgeRect).scaledToWidth(r);
+ }
+
+ middleRect = QRect(l, t, w - r -l, h - t - b);
+ if (middleRect.isValid()
+ && !(horizStretch == TileMode_Stretch && vertStretch == TileMode_Stretch)) {
+ middle = pixmap.copy(middleRect);
+ }
+}
+
+static void qDrawCenterTiledPixmap(QPainter *p, const QRectF& r, const QPixmap& pix)
+{
+ p->drawTiledPixmap(r, pix, QPoint(pix.width() - int(r.width())%pix.width(),
+ pix.height() - int(r.height())%pix.height()));
+}
+
+// Note: Round is not supported
+void QRenderRule::drawBorderImage(QPainter *p, const QRect& rect)
+{
+ setClip(p, rect);
+ const QRectF br(rect);
+ const int *borders = border()->borders;
+ const int &l = borders[LeftEdge], &r = borders[RightEdge],
+ &t = borders[TopEdge], &b = borders[BottomEdge];
+ QRectF pr = br.adjusted(l, t, -r, -b);
+
+ bool wasSmoothPixmapTransform = p->renderHints() & QPainter::SmoothPixmapTransform;
+ p->setRenderHint(QPainter::SmoothPixmapTransform);
+
+ const QStyleSheetBorderImageData *bi = border()->borderImage();
+ const QPixmap& pix = bi->pixmap;
+ const int *c = bi->cuts;
+ QRectF tlc(0, 0, c[LeftEdge], c[TopEdge]);
+ if (tlc.isValid())
+ p->drawPixmap(QRectF(br.topLeft(), QSizeF(l, t)), pix, tlc);
+ QRectF trc(pix.width() - c[RightEdge], 0, c[RightEdge], c[TopEdge]);
+ if (trc.isValid())
+ p->drawPixmap(QRectF(br.left() + br.width() - r, br.y(), r, t), pix, trc);
+ QRectF blc(0, pix.height() - c[BottomEdge], c[LeftEdge], c[BottomEdge]);
+ if (blc.isValid())
+ p->drawPixmap(QRectF(br.x(), br.y() + br.height() - b, l, b), pix, blc);
+ QRectF brc(pix.width() - c[RightEdge], pix.height() - c[BottomEdge],
+ c[RightEdge], c[BottomEdge]);
+ if (brc.isValid())
+ p->drawPixmap(QRectF(br.x() + br.width() - r, br.y() + br.height() - b, r, b),
+ pix, brc);
+
+ QRectF topEdgeRect(br.x() + l, br.y(), pr.width(), t);
+ QRectF bottomEdgeRect(br.x() + l, br.y() + br.height() - b, pr.width(), b);
+
+ switch (bi->horizStretch) {
+ case TileMode_Stretch:
+ if (bi->topEdgeRect.isValid())
+ p->drawPixmap(topEdgeRect, pix, bi->topEdgeRect);
+ if (bi->bottomEdgeRect.isValid())
+ p->drawPixmap(bottomEdgeRect, pix, bi->bottomEdgeRect);
+ if (bi->middleRect.isValid()) {
+ if (bi->vertStretch == TileMode_Stretch)
+ p->drawPixmap(pr, pix, bi->middleRect);
+ else if (bi->vertStretch == TileMode_Repeat) {
+ QPixmap scaled = bi->middle.scaled(int(pr.width()), bi->middle.height());
+ qDrawCenterTiledPixmap(p, pr, scaled);
+ }
+ }
+ break;
+ case TileMode_Repeat:
+ if (!bi->topEdge.isNull() && !topEdgeRect.isEmpty()) {
+ QPixmap scaled = bi->topEdge.scaled(bi->topEdge.width(), t);
+ qDrawCenterTiledPixmap(p, topEdgeRect, scaled);
+ }
+ if (!bi->bottomEdge.isNull() && !bottomEdgeRect.isEmpty()) {
+ QPixmap scaled = bi->bottomEdge.scaled(bi->bottomEdge.width(), b);
+ qDrawCenterTiledPixmap(p, bottomEdgeRect, scaled);
+ }
+ if (bi->middleRect.isValid()) {
+ if (bi->vertStretch == TileMode_Repeat) {
+ qDrawCenterTiledPixmap(p, pr, bi->middle);
+ } else if (bi->vertStretch == TileMode_Stretch) {
+ QPixmap scaled = bi->middle.scaled(bi->middle.width(), int(pr.height()));
+ qDrawCenterTiledPixmap(p, pr, scaled);
+ }
+ }
+ break;
+ case TileMode_Round:
+ if (!bi->topEdge.isNull()) {
+ int rwh = (int)pr.width()/ceil(pr.width()/bi->topEdge.width());
+ QPixmap scaled = bi->topEdge.scaled(rwh, bi->topEdge.height());
+ int blank = int(pr.width()) % rwh;
+ p->drawTiledPixmap(QRectF(br.x() + l + blank/2, br.y(), pr.width() - blank, t),
+ scaled);
+ }
+ if (!bi->bottomEdge.isNull()) {
+ int rwh = (int) pr.width()/ceil(pr.width()/bi->bottomEdge.width());
+ QPixmap scaled = bi->bottomEdge.scaled(rwh, bi->bottomEdge.height());
+ int blank = int(pr.width()) % rwh;
+ p->drawTiledPixmap(QRectF(br.x() + l+ blank/2, br.y()+br.height()-b,
+ pr.width() - blank, b), scaled);
+ }
+ break;
+ default:
+ break;
+ }
+
+ QRectF leftEdgeRect(br.x(), br.y() + t, l, pr.height());
+ QRectF rightEdgeRect(br.x() + br.width()- r, br.y() + t, r, pr.height());
+
+ switch (bi->vertStretch) {
+ case TileMode_Stretch:
+ if (bi->leftEdgeRect.isValid())
+ p->drawPixmap(leftEdgeRect, pix, bi->leftEdgeRect);
+ if (bi->rightEdgeRect.isValid())
+ p->drawPixmap(rightEdgeRect, pix, bi->rightEdgeRect);
+ break;
+ case TileMode_Repeat:
+ if (!bi->leftEdge.isNull() && !leftEdgeRect.isEmpty()) {
+ QPixmap scaled = bi->leftEdge.scaled(l, bi->leftEdge.height());
+ qDrawCenterTiledPixmap(p, leftEdgeRect, scaled);
+ }
+ if (!bi->rightEdge.isNull() && !rightEdgeRect.isEmpty()) {
+ QPixmap scaled = bi->rightEdge.scaled(r, bi->rightEdge.height());
+ qDrawCenterTiledPixmap(p, rightEdgeRect, scaled);
+ }
+ break;
+ case TileMode_Round:
+ if (!bi->leftEdge.isNull()) {
+ int rwh = (int) pr.height()/ceil(pr.height()/bi->leftEdge.height());
+ QPixmap scaled = bi->leftEdge.scaled(bi->leftEdge.width(), rwh);
+ int blank = int(pr.height()) % rwh;
+ p->drawTiledPixmap(QRectF(br.x(), br.y() + t + blank/2, l, pr.height() - blank),
+ scaled);
+ }
+ if (!bi->rightEdge.isNull()) {
+ int rwh = (int) pr.height()/ceil(pr.height()/bi->rightEdge.height());
+ QPixmap scaled = bi->rightEdge.scaled(bi->rightEdge.width(), rwh);
+ int blank = int(pr.height()) % rwh;
+ p->drawTiledPixmap(QRectF(br.x() + br.width() - r, br.y()+t+blank/2, r,
+ pr.height() - blank), scaled);
+ }
+ break;
+ default:
+ break;
+ }
+
+ p->setRenderHint(QPainter::SmoothPixmapTransform, wasSmoothPixmapTransform);
+ unsetClip(p);
+}
+
+QRect QRenderRule::originRect(const QRect &rect, Origin origin) const
+{
+ switch (origin) {
+ case Origin_Padding:
+ return paddingRect(rect);
+ case Origin_Border:
+ return borderRect(rect);
+ case Origin_Content:
+ return contentsRect(rect);
+ case Origin_Margin:
+ default:
+ return rect;
+ }
+}
+
+void QRenderRule::drawBackgroundImage(QPainter *p, const QRect &rect, QPoint off)
+{
+ if (!hasBackground())
+ return;
+
+ const QPixmap& bgp = background()->pixmap;
+ if (bgp.isNull())
+ return;
+
+ setClip(p, borderRect(rect));
+
+ if (background()->origin != background()->clip) {
+ p->save();
+ p->setClipRect(originRect(rect, background()->clip), Qt::IntersectClip);
+ }
+
+ if (background()->attachment == Attachment_Fixed)
+ off = QPoint(0, 0);
+
+ QRect r = originRect(rect, background()->origin);
+ QRect aligned = QStyle::alignedRect(Qt::LeftToRight, background()->position, bgp.size(), r);
+ QRect inter = aligned.intersected(r);
+
+ switch (background()->repeat) {
+ case Repeat_Y:
+ p->drawTiledPixmap(inter.x(), r.y(), inter.width(), r.height(), bgp,
+ inter.x() - aligned.x() + off.x(),
+ bgp.height() - int(aligned.y() - r.y()) % bgp.height() + off.y());
+ break;
+ case Repeat_X:
+ p->drawTiledPixmap(r.x(), inter.y(), r.width(), inter.height(), bgp,
+ bgp.width() - int(aligned.x() - r.x())%bgp.width() + off.x(),
+ inter.y() - aligned.y() + off.y());
+ break;
+ case Repeat_XY:
+ p->drawTiledPixmap(r, bgp,
+ QPoint(bgp.width() - int(aligned.x() - r.x())% bgp.width() + off.x(),
+ bgp.height() - int(aligned.y() - r.y())%bgp.height() + off.y()));
+ break;
+ case Repeat_None:
+ default:
+ p->drawPixmap(inter.x(), inter.y(), bgp, inter.x() - aligned.x() + off.x(),
+ inter.y() - aligned.y() + off.y(), inter.width(), inter.height());
+ break;
+ }
+
+
+ if (background()->origin != background()->clip)
+ p->restore();
+
+ unsetClip(p);
+}
+
+void QRenderRule::drawOutline(QPainter *p, const QRect &rect)
+{
+ if (!hasOutline())
+ return;
+
+ bool wasAntialiased = p->renderHints() & QPainter::Antialiasing;
+ p->setRenderHint(QPainter::Antialiasing);
+ qDrawBorder(p, rect, ou->styles, ou->borders, ou->colors, ou->radii);
+ p->setRenderHint(QPainter::Antialiasing, wasAntialiased);
+}
+
+void QRenderRule::drawBorder(QPainter *p, const QRect& rect)
+{
+ if (!hasBorder())
+ return;
+
+ if (border()->hasBorderImage()) {
+ drawBorderImage(p, rect);
+ return;
+ }
+
+ bool wasAntialiased = p->renderHints() & QPainter::Antialiasing;
+ p->setRenderHint(QPainter::Antialiasing);
+ qDrawBorder(p, rect, bd->styles, bd->borders, bd->colors, bd->radii);
+ p->setRenderHint(QPainter::Antialiasing, wasAntialiased);
+}
+
+QPainterPath QRenderRule::borderClip(QRect r)
+{
+ if (!hasBorder())
+ return QPainterPath();
+
+ QSize tlr, trr, blr, brr;
+ qNormalizeRadii(r, bd->radii, &tlr, &trr, &blr, &brr);
+ if (tlr.isNull() && trr.isNull() && blr.isNull() && brr.isNull())
+ return QPainterPath();
+
+ const QRectF rect(r);
+ const int *borders = border()->borders;
+ QPainterPath path;
+ qreal curY = rect.y() + borders[TopEdge]/2.0;
+ path.moveTo(rect.x() + tlr.width(), curY);
+ path.lineTo(rect.right() - trr.width(), curY);
+ qreal curX = rect.right() - borders[RightEdge]/2.0;
+ path.arcTo(curX - 2*trr.width() + borders[RightEdge], curY,
+ trr.width()*2 - borders[RightEdge], trr.height()*2 - borders[TopEdge], 90, -90);
+
+ path.lineTo(curX, rect.bottom() - brr.height());
+ curY = rect.bottom() - borders[BottomEdge]/2.0;
+ path.arcTo(curX - 2*brr.width() + borders[RightEdge], curY - 2*brr.height() + borders[BottomEdge],
+ brr.width()*2 - borders[RightEdge], brr.height()*2 - borders[BottomEdge], 0, -90);
+
+ path.lineTo(rect.x() + blr.width(), curY);
+ curX = rect.left() + borders[LeftEdge]/2.0;
+ path.arcTo(curX, rect.bottom() - 2*blr.height() + borders[BottomEdge]/2,
+ blr.width()*2 - borders[LeftEdge], blr.height()*2 - borders[BottomEdge], 270, -90);
+
+ path.lineTo(curX, rect.top() + tlr.height());
+ path.arcTo(curX, rect.top() + borders[TopEdge]/2,
+ tlr.width()*2 - borders[LeftEdge], tlr.height()*2 - borders[TopEdge], 180, -90);
+
+ path.closeSubpath();
+ return path;
+}
+
+/*! \internal
+ Clip the painter to the border (in case we are using radius border)
+ */
+void QRenderRule::setClip(QPainter *p, const QRect &rect)
+{
+ if (clipset++)
+ return;
+ clipPath = borderClip(rect);
+ if (!clipPath.isEmpty()) {
+ p->save();
+ p->setClipPath(clipPath, Qt::IntersectClip);
+ }
+}
+
+void QRenderRule::unsetClip(QPainter *p)
+{
+ if (--clipset)
+ return;
+ if (!clipPath.isEmpty())
+ p->restore();
+}
+
+void QRenderRule::drawBackground(QPainter *p, const QRect& rect, const QPoint& off)
+{
+ setClip(p, borderRect(rect));
+ QBrush brush = hasBackground() ? background()->brush : QBrush();
+ if (brush.style() == Qt::NoBrush)
+ brush = defaultBackground;
+
+ if (brush.style() != Qt::NoBrush) {
+ Origin origin = hasBackground() ? background()->clip : Origin_Border;
+ // ### fix for gradients
+ p->fillRect(originRect(rect, origin), brush);
+ }
+
+ drawBackgroundImage(p, rect, off);
+ unsetClip(p);
+}
+
+void QRenderRule::drawFrame(QPainter *p, const QRect& rect)
+{
+ drawBackground(p, rect);
+ if (hasBorder())
+ drawBorder(p, borderRect(rect));
+}
+
+void QRenderRule::drawImage(QPainter *p, const QRect &rect)
+{
+ if (!hasImage())
+ return;
+ img->icon.paint(p, rect, img->alignment);
+}
+
+void QRenderRule::drawRule(QPainter *p, const QRect& rect)
+{
+ drawFrame(p, rect);
+ drawImage(p, contentsRect(rect));
+}
+
+// *shudder* , *horror*, *whoa* <-- what you might feel when you see the functions below
+void QRenderRule::configurePalette(QPalette *p, QPalette::ColorRole fr, QPalette::ColorRole br)
+{
+ if (bg && bg->brush.style() != Qt::NoBrush) {
+ if (br != QPalette::NoRole)
+ p->setBrush(br, bg->brush);
+ p->setBrush(QPalette::Window, bg->brush);
+ }
+
+ if (!hasPalette())
+ return;
+
+ if (pal->foreground.style() != Qt::NoBrush) {
+ if (fr != QPalette::NoRole)
+ p->setBrush(fr, pal->foreground);
+ p->setBrush(QPalette::WindowText, pal->foreground);
+ p->setBrush(QPalette::Text, pal->foreground);
+ }
+ if (pal->selectionBackground.style() != Qt::NoBrush)
+ p->setBrush(QPalette::Highlight, pal->selectionBackground);
+ if (pal->selectionForeground.style() != Qt::NoBrush)
+ p->setBrush(QPalette::HighlightedText, pal->selectionForeground);
+ if (pal->alternateBackground.style() != Qt::NoBrush)
+ p->setBrush(QPalette::AlternateBase, pal->alternateBackground);
+}
+
+void QRenderRule::configurePalette(QPalette *p, QPalette::ColorGroup cg, const QWidget *w, bool embedded)
+{
+#ifdef QT_NO_COMBOBOX
+ const bool isReadOnlyCombo = false;
+#else
+ const bool isReadOnlyCombo = qobject_cast<const QComboBox *>(w) != 0;
+#endif
+
+ if (bg && bg->brush.style() != Qt::NoBrush) {
+ if (isReadOnlyCombo) {
+ p->setBrush(cg, QPalette::Base, bg->brush); // for windows, windowxp
+ p->setBrush(cg, QPalette::Button, bg->brush); // for plastique
+ } else {
+ p->setBrush(cg, w->backgroundRole(), bg->brush);
+ //p->setBrush(cg, QPalette::Window, bg->brush);
+ }
+ }
+
+ if (embedded) {
+ /* For embedded widgets (ComboBox, SpinBox and ScrollArea) we want the embedded widget
+ * to be transparent when we have a transparent background or border image */
+ if ((hasBackground() && background()->isTransparent())
+ || (hasBorder() && border()->hasBorderImage() && border()->borderImage()->middleRect.isValid()))
+ p->setBrush(cg, w->backgroundRole(), Qt::NoBrush);
+ }
+
+ if (!hasPalette())
+ return;
+
+ if (pal->foreground.style() != Qt::NoBrush) {
+ if (isReadOnlyCombo) {
+ p->setBrush(cg, QPalette::ButtonText, pal->foreground);
+ } else {
+ p->setBrush(cg, w->foregroundRole(), pal->foreground);
+ p->setBrush(cg, QPalette::WindowText, pal->foreground);
+ }
+ p->setBrush(cg, QPalette::Text, pal->foreground);
+ }
+ if (pal->selectionBackground.style() != Qt::NoBrush)
+ p->setBrush(cg, QPalette::Highlight, pal->selectionBackground);
+ if (pal->selectionForeground.style() != Qt::NoBrush)
+ p->setBrush(cg, QPalette::HighlightedText, pal->selectionForeground);
+ if (pal->alternateBackground.style() != Qt::NoBrush)
+ p->setBrush(cg, QPalette::AlternateBase, pal->alternateBackground);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Style rules
+#define WIDGET(x) (static_cast<QWidget *>(x.ptr))
+
+static inline QWidget *parentWidget(const QWidget *w)
+{
+ if(qobject_cast<const QLabel *>(w) && qstrcmp(w->metaObject()->className(), "QTipLabel") == 0) {
+ QWidget *p = qvariant_cast<QWidget *>(w->property("_q_stylesheet_parent"));
+ if (p)
+ return p;
+ }
+ return w->parentWidget();
+}
+
+class QStyleSheetStyleSelector : public StyleSelector
+{
+public:
+ QStyleSheetStyleSelector() { }
+
+ QStringList nodeNames(NodePtr node) const
+ {
+ if (isNullNode(node))
+ return QStringList();
+ const QMetaObject *metaObject = WIDGET(node)->metaObject();
+#ifndef QT_NO_TOOLTIP
+ if (qstrcmp(metaObject->className(), "QTipLabel") == 0)
+ return QStringList(QLatin1String("QToolTip"));
+#endif
+ QStringList result;
+ do {
+ result += QString::fromLatin1(metaObject->className()).replace(QLatin1Char(':'), QLatin1Char('-'));
+ metaObject = metaObject->superClass();
+ } while (metaObject != 0);
+ return result;
+ }
+ QString attribute(NodePtr node, const QString& name) const
+ {
+ if (isNullNode(node))
+ return QString();
+
+ QHash<QString, QString> &cache = m_attributeCache[WIDGET(node)];
+ QHash<QString, QString>::const_iterator cacheIt = cache.constFind(name);
+ if (cacheIt != cache.constEnd())
+ return cacheIt.value();
+
+ QVariant value = WIDGET(node)->property(name.toLatin1());
+ if (!value.isValid()) {
+ if (name == QLatin1String("class")) {
+ QString className = QString::fromLatin1(WIDGET(node)->metaObject()->className());
+ if (className.contains(QLatin1Char(':')))
+ className.replace(QLatin1Char(':'), QLatin1Char('-'));
+ cache[name] = className;
+ return className;
+ } else if (name == QLatin1String("style")) {
+ QStyleSheetStyle *proxy = qobject_cast<QStyleSheetStyle *>(WIDGET(node)->style());
+ if (proxy) {
+ QString styleName = QString::fromLatin1(proxy->baseStyle()->metaObject()->className());
+ cache[name] = styleName;
+ return styleName;
+ }
+ }
+ }
+ QString valueStr;
+ if(value.type() == QVariant::StringList || value.type() == QVariant::List)
+ valueStr = value.toStringList().join(QLatin1String(" "));
+ else
+ valueStr = value.toString();
+ cache[name] = valueStr;
+ return valueStr;
+ }
+ bool nodeNameEquals(NodePtr node, const QString& nodeName) const
+ {
+ if (isNullNode(node))
+ return false;
+ const QMetaObject *metaObject = WIDGET(node)->metaObject();
+#ifndef QT_NO_TOOLTIP
+ if (qstrcmp(metaObject->className(), "QTipLabel") == 0)
+ return nodeName == QLatin1String("QToolTip");
+#endif
+ do {
+ const ushort *uc = (const ushort *)nodeName.constData();
+ const ushort *e = uc + nodeName.length();
+ const uchar *c = (uchar *)metaObject->className();
+ while (*c && uc != e && (*uc == *c || (*c == ':' && *uc == '-'))) {
+ ++uc;
+ ++c;
+ }
+ if (uc == e && !*c)
+ return true;
+ metaObject = metaObject->superClass();
+ } while (metaObject != 0);
+ return false;
+ }
+ bool hasAttributes(NodePtr) const
+ { return true; }
+ QStringList nodeIds(NodePtr node) const
+ { return isNullNode(node) ? QStringList() : QStringList(WIDGET(node)->objectName()); }
+ bool isNullNode(NodePtr node) const
+ { return node.ptr == 0; }
+ NodePtr parentNode(NodePtr node) const
+ { NodePtr n; n.ptr = isNullNode(node) ? 0 : parentWidget(WIDGET(node)); return n; }
+ NodePtr previousSiblingNode(NodePtr) const
+ { NodePtr n; n.ptr = 0; return n; }
+ NodePtr duplicateNode(NodePtr node) const
+ { return node; }
+ void freeNode(NodePtr) const
+ { }
+
+private:
+ mutable QHash<const QWidget *, QHash<QString, QString> > m_attributeCache;
+};
+
+QVector<QCss::StyleRule> QStyleSheetStyle::styleRules(const QWidget *w) const
+{
+ QHash<const QWidget *, QVector<StyleRule> >::const_iterator cacheIt = styleRulesCache->constFind(w);
+ if (cacheIt != styleRulesCache->constEnd())
+ return cacheIt.value();
+
+ if (!initWidget(w)) {
+ return QVector<StyleRule>();
+ }
+
+ QStyleSheetStyleSelector styleSelector;
+
+ StyleSheet defaultSs;
+ QHash<const void *, StyleSheet>::const_iterator defaultCacheIt = styleSheetCache->constFind(baseStyle());
+ if (defaultCacheIt == styleSheetCache->constEnd()) {
+ defaultSs = getDefaultStyleSheet();
+ styleSheetCache->insert(baseStyle(), defaultSs);
+ } else {
+ defaultSs = defaultCacheIt.value();
+ }
+ styleSelector.styleSheets += defaultSs;
+
+ if (!qApp->styleSheet().isEmpty()) {
+ StyleSheet appSs;
+ QHash<const void *, StyleSheet>::const_iterator appCacheIt = styleSheetCache->constFind(qApp);
+ if (appCacheIt == styleSheetCache->constEnd()) {
+ QString ss = qApp->styleSheet();
+ if (ss.startsWith(QLatin1String("file:///")))
+ ss.remove(0, 8);
+ parser.init(ss, qApp->styleSheet() != ss);
+ if (!parser.parse(&appSs))
+ qWarning("Could not parse application stylesheet");
+ appSs.origin = StyleSheetOrigin_Inline;
+ appSs.depth = 1;
+ styleSheetCache->insert(qApp, appSs);
+ } else {
+ appSs = appCacheIt.value();
+ }
+ styleSelector.styleSheets += appSs;
+ }
+
+ QVector<QCss::StyleSheet> widgetSs;
+ for (const QWidget *wid = w; wid; wid = parentWidget(wid)) {
+ if (wid->styleSheet().isEmpty())
+ continue;
+ StyleSheet ss;
+ QHash<const void *, StyleSheet>::const_iterator widCacheIt = styleSheetCache->constFind(wid);
+ if (widCacheIt == styleSheetCache->constEnd()) {
+ parser.init(wid->styleSheet());
+ if (!parser.parse(&ss)) {
+ parser.init(QLatin1String("* {") + wid->styleSheet() + QLatin1String("}"));
+ if (!parser.parse(&ss))
+ qWarning("Could not parse stylesheet of widget %p", wid);
+ }
+ ss.origin = StyleSheetOrigin_Inline;
+ styleSheetCache->insert(wid, ss);
+ } else {
+ ss = widCacheIt.value();
+ }
+ widgetSs.append(ss);
+ }
+
+ for (int i = 0; i < widgetSs.count(); i++)
+ widgetSs[i].depth = widgetSs.count() - i + 2;
+
+ styleSelector.styleSheets += widgetSs;
+
+ StyleSelector::NodePtr n;
+ n.ptr = (void *)w;
+ QVector<QCss::StyleRule> rules = styleSelector.styleRulesForNode(n);
+ styleRulesCache->insert(w, rules);
+ return rules;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Rendering rules
+static QVector<Declaration> declarations(const QVector<StyleRule> &styleRules, const QString &part, quint64 pseudoClass = PseudoClass_Unspecified)
+{
+ QVector<Declaration> decls;
+ for (int i = 0; i < styleRules.count(); i++) {
+ const Selector& selector = styleRules.at(i).selectors.at(0);
+ // Rules with pseudo elements don't cascade. This is an intentional
+ // diversion for CSS
+ if (part.compare(selector.pseudoElement(), Qt::CaseInsensitive) != 0)
+ continue;
+ quint64 negated = 0;
+ quint64 cssClass = selector.pseudoClass(&negated);
+ if ((pseudoClass == PseudoClass_Any) || (cssClass == PseudoClass_Unspecified)
+ || ((((cssClass & pseudoClass) == cssClass)) && ((negated & pseudoClass) == 0)))
+ decls += styleRules.at(i).declarations;
+ }
+ return decls;
+}
+
+int QStyleSheetStyle::nativeFrameWidth(const QWidget *w)
+{
+ QStyle *base = baseStyle();
+
+#ifndef QT_NO_SPINBOX
+ if (qobject_cast<const QAbstractSpinBox *>(w))
+ return base->pixelMetric(QStyle::PM_SpinBoxFrameWidth, 0, w);
+#endif
+
+#ifndef QT_NO_COMBOBOX
+ if (qobject_cast<const QComboBox *>(w))
+ return base->pixelMetric(QStyle::PM_ComboBoxFrameWidth, 0, w);
+#endif
+
+#ifndef QT_NO_MENU
+ if (qobject_cast<const QMenu *>(w))
+ return base->pixelMetric(QStyle::PM_MenuPanelWidth, 0, w);
+#endif
+
+#ifndef QT_NO_MENUBAR
+ if (qobject_cast<const QMenuBar *>(w))
+ return base->pixelMetric(QStyle::PM_MenuBarPanelWidth, 0, w);
+#endif
+#ifndef QT_NO_FRAME
+ if (const QFrame *frame = qobject_cast<const QFrame *>(w)) {
+ if (frame->frameShape() == QFrame::NoFrame)
+ return 0;
+ }
+#endif
+
+ if (qstrcmp(w->metaObject()->className(), "QTipLabel") == 0)
+ return base->pixelMetric(QStyle::PM_ToolTipLabelFrameWidth, 0, w);
+
+ return base->pixelMetric(QStyle::PM_DefaultFrameWidth, 0, w);
+}
+
+static quint64 pseudoClass(QStyle::State state)
+{
+ quint64 pc = 0;
+ if (state & QStyle::State_Enabled) {
+ pc |= PseudoClass_Enabled;
+ if (state & QStyle::State_MouseOver)
+ pc |= PseudoClass_Hover;
+ } else {
+ pc |= PseudoClass_Disabled;
+ }
+ if (state & QStyle::State_Active)
+ pc |= PseudoClass_Active;
+ if (state & QStyle::State_Window)
+ pc |= PseudoClass_Window;
+ if (state & QStyle::State_Sunken)
+ pc |= PseudoClass_Pressed;
+ if (state & QStyle::State_HasFocus)
+ pc |= PseudoClass_Focus;
+ if (state & QStyle::State_On)
+ pc |= (PseudoClass_On | PseudoClass_Checked);
+ if (state & QStyle::State_Off)
+ pc |= (PseudoClass_Off | PseudoClass_Unchecked);
+ if (state & QStyle::State_NoChange)
+ pc |= PseudoClass_Indeterminate;
+ if (state & QStyle::State_Selected)
+ pc |= PseudoClass_Selected;
+ if (state & QStyle::State_Horizontal)
+ pc |= PseudoClass_Horizontal;
+ else
+ pc |= PseudoClass_Vertical;
+ if (state & (QStyle::State_Open | QStyle::State_On | QStyle::State_Sunken))
+ pc |= PseudoClass_Open;
+ else
+ pc |= PseudoClass_Closed;
+ if (state & QStyle::State_Children)
+ pc |= PseudoClass_Children;
+ if (state & QStyle::State_Sibling)
+ pc |= PseudoClass_Sibling;
+ if (state & QStyle::State_ReadOnly)
+ pc |= PseudoClass_ReadOnly;
+ if (state & QStyle::State_Item)
+ pc |= PseudoClass_Item;
+#ifdef QT_KEYPAD_NAVIGATION
+ if (state & QStyle::State_HasEditFocus)
+ pc |= PseudoClass_EditFocus;
+#endif
+ return pc;
+}
+
+static void qt_check_if_internal_widget(const QWidget **w, int *element)
+{
+#ifdef QT_NO_DOCKWIDGET
+ Q_UNUSED(w);
+ Q_UNUSED(element);
+#else
+ if (*w && qstrcmp((*w)->metaObject()->className(), "QDockWidgetTitleButton") == 0) {
+ if ((*w)->objectName() == QLatin1String("qt_dockwidget_closebutton")) {
+ *element = PseudoElement_DockWidgetCloseButton;
+ } else if ((*w)->objectName() == QLatin1String("qt_dockwidget_floatbutton")) {
+ *element = PseudoElement_DockWidgetFloatButton;
+ }
+ *w = (*w)->parentWidget();
+ }
+#endif
+}
+
+QRenderRule QStyleSheetStyle::renderRule(const QWidget *w, int element, quint64 state) const
+{
+ qt_check_if_internal_widget(&w, &element);
+ QHash<quint64, QRenderRule> &cache = (*renderRulesCache)[w][element];
+ QHash<quint64, QRenderRule>::const_iterator cacheIt = cache.constFind(state);
+ if (cacheIt != cache.constEnd())
+ return cacheIt.value();
+
+ if (!initWidget(w))
+ return QRenderRule();
+
+ quint64 stateMask = 0;
+ const QVector<StyleRule> rules = styleRules(w);
+ for (int i = 0; i < rules.count(); i++) {
+ const Selector& selector = rules.at(i).selectors.at(0);
+ quint64 negated = 0;
+ stateMask |= selector.pseudoClass(&negated);
+ stateMask |= negated;
+ }
+
+ cacheIt = cache.constFind(state & stateMask);
+ if (cacheIt != cache.constEnd()) {
+ const QRenderRule &newRule = cacheIt.value();
+ cache[state] = newRule;
+ return newRule;
+ }
+
+
+ const QString part = QLatin1String(knownPseudoElements[element].name);
+ QVector<Declaration> decls = declarations(rules, part, state);
+ QRenderRule newRule(decls, w);
+ cache[state] = newRule;
+ if ((state & stateMask) != state)
+ cache[state&stateMask] = newRule;
+ return newRule;
+}
+
+QRenderRule QStyleSheetStyle::renderRule(const QWidget *w, const QStyleOption *opt, int pseudoElement) const
+{
+ quint64 extraClass = 0;
+ QStyle::State state = opt ? opt->state : QStyle::State(QStyle::State_None);
+
+ if (const QStyleOptionComplex *complex = qstyleoption_cast<const QStyleOptionComplex *>(opt)) {
+ if (pseudoElement != PseudoElement_None) {
+ // if not an active subcontrol, just pass enabled/disabled
+ QStyle::SubControl subControl = knownPseudoElements[pseudoElement].subControl;
+
+ if (!(complex->activeSubControls & subControl))
+ state = QStyle::State(state & (QStyle::State_Enabled | QStyle::State_Horizontal));
+ }
+
+ switch (pseudoElement) {
+ case PseudoElement_ComboBoxDropDown:
+ case PseudoElement_ComboBoxArrow:
+ state |= (complex->state & (QStyle::State_On|QStyle::State_ReadOnly));
+ break;
+ case PseudoElement_SpinBoxUpButton:
+ case PseudoElement_SpinBoxDownButton:
+ case PseudoElement_SpinBoxUpArrow:
+ case PseudoElement_SpinBoxDownArrow:
+#ifndef QT_NO_SPINBOX
+ if (const QStyleOptionSpinBox *sb = qstyleoption_cast<const QStyleOptionSpinBox *>(opt)) {
+ bool on = false;
+ bool up = pseudoElement == PseudoElement_SpinBoxUpButton
+ || pseudoElement == PseudoElement_SpinBoxUpArrow;
+ if ((sb->stepEnabled & QAbstractSpinBox::StepUpEnabled) && up)
+ on = true;
+ else if ((sb->stepEnabled & QAbstractSpinBox::StepDownEnabled) && !up)
+ on = true;
+ state |= (on ? QStyle::State_On : QStyle::State_Off);
+ }
+#endif // QT_NO_SPINBOX
+ break;
+ case PseudoElement_GroupBoxTitle:
+ state |= (complex->state & (QStyle::State_MouseOver | QStyle::State_Sunken));
+ break;
+ case PseudoElement_ToolButtonMenu:
+ case PseudoElement_ToolButtonMenuArrow:
+ case PseudoElement_ToolButtonDownArrow:
+ state |= complex->state & QStyle::State_MouseOver;
+ if (complex->state & QStyle::State_Sunken ||
+ complex->activeSubControls & QStyle::SC_ToolButtonMenu)
+ state |= QStyle::State_Sunken;
+ break;
+ case PseudoElement_SliderGroove:
+ state |= complex->state & QStyle::State_MouseOver;
+ break;
+ default:
+ break;
+ }
+
+ if (const QStyleOptionComboBox *combo = qstyleoption_cast<const QStyleOptionComboBox *>(opt)) {
+ // QStyle::State_On is set when the popup is being shown
+ // Propagate EditField Pressed state
+ if (pseudoElement == PseudoElement_None
+ && (complex->activeSubControls & QStyle::SC_ComboBoxEditField)
+ && (!(state & QStyle::State_MouseOver))) {
+ state |= QStyle::State_Sunken;
+ }
+
+ if (!combo->frame)
+ extraClass |= PseudoClass_Frameless;
+ if (!combo->editable)
+ extraClass |= PseudoClass_ReadOnly;
+ else
+ extraClass |= PseudoClass_Editable;
+#ifndef QT_NO_SPINBOX
+ } else if (const QStyleOptionSpinBox *spin = qstyleoption_cast<const QStyleOptionSpinBox *>(opt)) {
+ if (!spin->frame)
+ extraClass |= PseudoClass_Frameless;
+#endif // QT_NO_SPINBOX
+ } else if (const QStyleOptionGroupBox *gb = qstyleoption_cast<const QStyleOptionGroupBox *>(opt)) {
+ if (gb->features & QStyleOptionFrameV2::Flat)
+ extraClass |= PseudoClass_Flat;
+ if (gb->lineWidth == 0)
+ extraClass |= PseudoClass_Frameless;
+ } else if (const QStyleOptionTitleBar *tb = qstyleoption_cast<const QStyleOptionTitleBar *>(opt)) {
+ if (tb->titleBarState & Qt::WindowMinimized) {
+ extraClass |= PseudoClass_Minimized;
+ }
+ else if (tb->titleBarState & Qt::WindowMaximized)
+ extraClass |= PseudoClass_Maximized;
+ }
+ } else {
+ // handle simple style options
+ if (const QStyleOptionMenuItem *mi = qstyleoption_cast<const QStyleOptionMenuItem *>(opt)) {
+ if (mi->menuItemType == QStyleOptionMenuItem::DefaultItem)
+ extraClass |= PseudoClass_Default;
+ if (mi->checkType == QStyleOptionMenuItem::Exclusive)
+ extraClass |= PseudoClass_Exclusive;
+ else if (mi->checkType == QStyleOptionMenuItem::NonExclusive)
+ extraClass |= PseudoClass_NonExclusive;
+ if (mi->checkType != QStyleOptionMenuItem::NotCheckable)
+ extraClass |= (mi->checked) ? (PseudoClass_On|PseudoClass_Checked)
+ : (PseudoClass_Off|PseudoClass_Unchecked);
+ } else if (const QStyleOptionHeader *hdr = qstyleoption_cast<const QStyleOptionHeader *>(opt)) {
+ if (hdr->position == QStyleOptionHeader::OnlyOneSection)
+ extraClass |= PseudoClass_OnlyOne;
+ else if (hdr->position == QStyleOptionHeader::Beginning)
+ extraClass |= PseudoClass_First;
+ else if (hdr->position == QStyleOptionHeader::End)
+ extraClass |= PseudoClass_Last;
+ else if (hdr->position == QStyleOptionHeader::Middle)
+ extraClass |= PseudoClass_Middle;
+
+ if (hdr->selectedPosition == QStyleOptionHeader::NextAndPreviousAreSelected)
+ extraClass |= (PseudoClass_NextSelected | PseudoClass_PreviousSelected);
+ else if (hdr->selectedPosition == QStyleOptionHeader::NextIsSelected)
+ extraClass |= PseudoClass_NextSelected;
+ else if (hdr->selectedPosition == QStyleOptionHeader::PreviousIsSelected)
+ extraClass |= PseudoClass_PreviousSelected;
+#ifndef QT_NO_TABWIDGET
+ } else if (const QStyleOptionTabWidgetFrame *tab = qstyleoption_cast<const QStyleOptionTabWidgetFrame *>(opt)) {
+ switch (tab->shape) {
+ case QTabBar::RoundedNorth:
+ case QTabBar::TriangularNorth:
+ extraClass |= PseudoClass_Top;
+ break;
+ case QTabBar::RoundedSouth:
+ case QTabBar::TriangularSouth:
+ extraClass |= PseudoClass_Bottom;
+ break;
+ case QTabBar::RoundedEast:
+ case QTabBar::TriangularEast:
+ extraClass |= PseudoClass_Left;
+ break;
+ case QTabBar::RoundedWest:
+ case QTabBar::TriangularWest:
+ extraClass |= PseudoClass_Right;
+ break;
+ default:
+ break;
+ }
+#endif
+#ifndef QT_NO_TABBAR
+ } else if (const QStyleOptionTab *tab = qstyleoption_cast<const QStyleOptionTab *>(opt)) {
+ if (tab->position == QStyleOptionTab::OnlyOneTab)
+ extraClass |= PseudoClass_OnlyOne;
+ else if (tab->position == QStyleOptionTab::Beginning)
+ extraClass |= PseudoClass_First;
+ else if (tab->position == QStyleOptionTab::End)
+ extraClass |= PseudoClass_Last;
+ else if (tab->position == QStyleOptionTab::Middle)
+ extraClass |= PseudoClass_Middle;
+
+ if (tab->selectedPosition == QStyleOptionTab::NextIsSelected)
+ extraClass |= PseudoClass_NextSelected;
+ else if (tab->selectedPosition == QStyleOptionTab::PreviousIsSelected)
+ extraClass |= PseudoClass_PreviousSelected;
+
+ switch (tab->shape) {
+ case QTabBar::RoundedNorth:
+ case QTabBar::TriangularNorth:
+ extraClass |= PseudoClass_Top;
+ break;
+ case QTabBar::RoundedSouth:
+ case QTabBar::TriangularSouth:
+ extraClass |= PseudoClass_Bottom;
+ break;
+ case QTabBar::RoundedEast:
+ case QTabBar::TriangularEast:
+ extraClass |= PseudoClass_Left;
+ break;
+ case QTabBar::RoundedWest:
+ case QTabBar::TriangularWest:
+ extraClass |= PseudoClass_Right;
+ break;
+ default:
+ break;
+ }
+#endif // QT_NO_TABBAR
+ } else if (const QStyleOptionButton *btn = qstyleoption_cast<const QStyleOptionButton *>(opt)) {
+ if (btn->features & QStyleOptionButton::Flat)
+ extraClass |= PseudoClass_Flat;
+ if (btn->features & QStyleOptionButton::DefaultButton)
+ extraClass |= PseudoClass_Default;
+ } else if (const QStyleOptionFrame *frm = qstyleoption_cast<const QStyleOptionFrame *>(opt)) {
+ if (frm->lineWidth == 0)
+ extraClass |= PseudoClass_Frameless;
+ if (const QStyleOptionFrameV2 *frame2 = qstyleoption_cast<const QStyleOptionFrameV2 *>(opt)) {
+ if (frame2->features & QStyleOptionFrameV2::Flat)
+ extraClass |= PseudoClass_Flat;
+ }
+ }
+#ifndef QT_NO_TOOLBAR
+ else if (const QStyleOptionToolBar *tb = qstyleoption_cast<const QStyleOptionToolBar *>(opt)) {
+ if (tb->toolBarArea == Qt::LeftToolBarArea)
+ extraClass |= PseudoClass_Left;
+ else if (tb->toolBarArea == Qt::RightToolBarArea)
+ extraClass |= PseudoClass_Right;
+ else if (tb->toolBarArea == Qt::TopToolBarArea)
+ extraClass |= PseudoClass_Top;
+ else if (tb->toolBarArea == Qt::BottomToolBarArea)
+ extraClass |= PseudoClass_Bottom;
+
+ if (tb->positionWithinLine == QStyleOptionToolBar::Beginning)
+ extraClass |= PseudoClass_First;
+ else if (tb->positionWithinLine == QStyleOptionToolBar::Middle)
+ extraClass |= PseudoClass_Middle;
+ else if (tb->positionWithinLine == QStyleOptionToolBar::End)
+ extraClass |= PseudoClass_Last;
+ else if (tb->positionWithinLine == QStyleOptionToolBar::OnlyOne)
+ extraClass |= PseudoClass_OnlyOne;
+ }
+#endif // QT_NO_TOOLBAR
+#ifndef QT_NO_TOOLBOX
+ else if (const QStyleOptionToolBoxV2 *tab = qstyleoption_cast<const QStyleOptionToolBoxV2 *>(opt)) {
+ if (tab->position == QStyleOptionToolBoxV2::OnlyOneTab)
+ extraClass |= PseudoClass_OnlyOne;
+ else if (tab->position == QStyleOptionToolBoxV2::Beginning)
+ extraClass |= PseudoClass_First;
+ else if (tab->position == QStyleOptionToolBoxV2::End)
+ extraClass |= PseudoClass_Last;
+ else if (tab->position == QStyleOptionToolBoxV2::Middle)
+ extraClass |= PseudoClass_Middle;
+
+ if (tab->selectedPosition == QStyleOptionToolBoxV2::NextIsSelected)
+ extraClass |= PseudoClass_NextSelected;
+ else if (tab->selectedPosition == QStyleOptionToolBoxV2::PreviousIsSelected)
+ extraClass |= PseudoClass_PreviousSelected;
+ }
+#endif // QT_NO_TOOLBOX
+#ifndef QT_NO_DOCKWIDGET
+ else if (const QStyleOptionDockWidgetV2 *dw = qstyleoption_cast<const QStyleOptionDockWidgetV2 *>(opt)) {
+ if (dw->verticalTitleBar)
+ extraClass |= PseudoClass_Vertical;
+ else
+ extraClass |= PseudoClass_Horizontal;
+ if (dw->closable)
+ extraClass |= PseudoClass_Closable;
+ if (dw->floatable)
+ extraClass |= PseudoClass_Floatable;
+ if (dw->movable)
+ extraClass |= PseudoClass_Movable;
+ }
+#endif // QT_NO_DOCKWIDGET
+#ifndef QT_NO_ITEMVIEWS
+ else if (const QStyleOptionViewItemV2 *v2 = qstyleoption_cast<const QStyleOptionViewItemV2 *>(opt)) {
+ if (v2->features & QStyleOptionViewItemV2::Alternate)
+ extraClass |= PseudoClass_Alternate;
+ if (const QStyleOptionViewItemV4 *v4 = qstyleoption_cast<const QStyleOptionViewItemV4 *>(opt)) {
+ if (v4->viewItemPosition == QStyleOptionViewItemV4::OnlyOne)
+ extraClass |= PseudoClass_OnlyOne;
+ else if (v4->viewItemPosition == QStyleOptionViewItemV4::Beginning)
+ extraClass |= PseudoClass_First;
+ else if (v4->viewItemPosition == QStyleOptionViewItemV4::End)
+ extraClass |= PseudoClass_Last;
+ else if (v4->viewItemPosition == QStyleOptionViewItemV4::Middle)
+ extraClass |= PseudoClass_Middle;
+ }
+ }
+#endif
+#ifndef QT_NO_LINEEDIT
+ // LineEdit sets Sunken flag to indicate Sunken frame (argh)
+ if (const QLineEdit *lineEdit = qobject_cast<const QLineEdit *>(w)) {
+ state &= ~QStyle::State_Sunken;
+ if (lineEdit->hasFrame()) {
+ extraClass &= ~PseudoClass_Frameless;
+ } else {
+ extraClass |= PseudoClass_Frameless;
+ }
+ } else
+#endif
+ { } // required for the above ifdef'ery
+ }
+
+ return renderRule(w, pseudoElement, pseudoClass(state) | extraClass);
+}
+
+bool QStyleSheetStyle::hasStyleRule(const QWidget *w, int part) const
+{
+ QHash<int, bool> &cache = (*hasStyleRuleCache)[w];
+ QHash<int, bool>::const_iterator cacheIt = cache.constFind(part);
+ if (cacheIt != cache.constEnd())
+ return cacheIt.value();
+
+ if (!initWidget(w))
+ return false;
+
+
+ const QVector<StyleRule> &rules = styleRules(w);
+ if (part == PseudoElement_None) {
+ bool result = w && !rules.isEmpty();
+ cache[part] = result;
+ return result;
+ }
+
+ QString pseudoElement = QLatin1String(knownPseudoElements[part].name);
+ QVector<Declaration> declarations;
+ for (int i = 0; i < rules.count(); i++) {
+ const Selector& selector = rules.at(i).selectors.at(0);
+ if (pseudoElement.compare(selector.pseudoElement(), Qt::CaseInsensitive) == 0) {
+ cache[part] = true;
+ return true;
+ }
+ }
+
+ cache[part] = false;
+ return false;
+}
+
+static Origin defaultOrigin(int pe)
+{
+ switch (pe) {
+ case PseudoElement_ScrollBarAddPage:
+ case PseudoElement_ScrollBarSubPage:
+ case PseudoElement_ScrollBarAddLine:
+ case PseudoElement_ScrollBarSubLine:
+ case PseudoElement_ScrollBarFirst:
+ case PseudoElement_ScrollBarLast:
+ case PseudoElement_GroupBoxTitle:
+ case PseudoElement_GroupBoxIndicator: // never used
+ case PseudoElement_ToolButtonMenu:
+ case PseudoElement_SliderAddPage:
+ case PseudoElement_SliderSubPage:
+ return Origin_Border;
+
+ case PseudoElement_SpinBoxUpButton:
+ case PseudoElement_SpinBoxDownButton:
+ case PseudoElement_PushButtonMenuIndicator:
+ case PseudoElement_ComboBoxDropDown:
+ case PseudoElement_ToolButtonDownArrow:
+ case PseudoElement_MenuCheckMark:
+ case PseudoElement_MenuIcon:
+ case PseudoElement_MenuRightArrow:
+ return Origin_Padding;
+
+ case PseudoElement_Indicator:
+ case PseudoElement_ExclusiveIndicator:
+ case PseudoElement_ComboBoxArrow:
+ case PseudoElement_ScrollBarSlider:
+ case PseudoElement_ScrollBarUpArrow:
+ case PseudoElement_ScrollBarDownArrow:
+ case PseudoElement_ScrollBarLeftArrow:
+ case PseudoElement_ScrollBarRightArrow:
+ case PseudoElement_SpinBoxUpArrow:
+ case PseudoElement_SpinBoxDownArrow:
+ case PseudoElement_ToolButtonMenuArrow:
+ case PseudoElement_HeaderViewUpArrow:
+ case PseudoElement_HeaderViewDownArrow:
+ case PseudoElement_SliderGroove:
+ case PseudoElement_SliderHandle:
+ return Origin_Content;
+
+ default:
+ return Origin_Margin;
+ }
+}
+
+static Qt::Alignment defaultPosition(int pe)
+{
+ switch (pe) {
+ case PseudoElement_Indicator:
+ case PseudoElement_ExclusiveIndicator:
+ case PseudoElement_MenuCheckMark:
+ case PseudoElement_MenuIcon:
+ return Qt::AlignLeft | Qt::AlignVCenter;
+
+ case PseudoElement_ScrollBarAddLine:
+ case PseudoElement_ScrollBarLast:
+ case PseudoElement_SpinBoxDownButton:
+ case PseudoElement_PushButtonMenuIndicator:
+ case PseudoElement_ToolButtonDownArrow:
+ return Qt::AlignRight | Qt::AlignBottom;
+
+ case PseudoElement_ScrollBarSubLine:
+ case PseudoElement_ScrollBarFirst:
+ case PseudoElement_SpinBoxUpButton:
+ case PseudoElement_ComboBoxDropDown:
+ case PseudoElement_ToolButtonMenu:
+ case PseudoElement_DockWidgetCloseButton:
+ case PseudoElement_DockWidgetFloatButton:
+ return Qt::AlignRight | Qt::AlignTop;
+
+ case PseudoElement_ScrollBarUpArrow:
+ case PseudoElement_ScrollBarDownArrow:
+ case PseudoElement_ScrollBarLeftArrow:
+ case PseudoElement_ScrollBarRightArrow:
+ case PseudoElement_SpinBoxUpArrow:
+ case PseudoElement_SpinBoxDownArrow:
+ case PseudoElement_ComboBoxArrow:
+ case PseudoElement_DownArrow:
+ case PseudoElement_ToolButtonMenuArrow:
+ case PseudoElement_SliderGroove:
+ return Qt::AlignCenter;
+
+ case PseudoElement_GroupBoxTitle:
+ case PseudoElement_GroupBoxIndicator: // never used
+ return Qt::AlignLeft | Qt::AlignTop;
+
+ case PseudoElement_HeaderViewUpArrow:
+ case PseudoElement_HeaderViewDownArrow:
+ case PseudoElement_MenuRightArrow:
+ return Qt::AlignRight | Qt::AlignVCenter;
+
+ default:
+ return 0;
+ }
+}
+
+QSize QStyleSheetStyle::defaultSize(const QWidget *w, QSize sz, const QRect& rect, int pe) const
+{
+ QStyle *base = baseStyle();
+
+ switch (pe) {
+ case PseudoElement_Indicator:
+ case PseudoElement_MenuCheckMark:
+ if (sz.width() == -1)
+ sz.setWidth(base->pixelMetric(PM_IndicatorWidth, 0, w));
+ if (sz.height() == -1)
+ sz.setHeight(base->pixelMetric(PM_IndicatorHeight, 0, w));
+ break;
+
+ case PseudoElement_ExclusiveIndicator:
+ case PseudoElement_GroupBoxIndicator:
+ if (sz.width() == -1)
+ sz.setWidth(base->pixelMetric(PM_ExclusiveIndicatorWidth, 0, w));
+ if (sz.height() == -1)
+ sz.setHeight(base->pixelMetric(PM_ExclusiveIndicatorHeight, 0, w));
+ break;
+
+ case PseudoElement_PushButtonMenuIndicator: {
+ int pm = base->pixelMetric(PM_MenuButtonIndicator, 0, w);
+ if (sz.width() == -1)
+ sz.setWidth(pm);
+ if (sz.height() == -1)
+ sz.setHeight(pm);
+ }
+ break;
+
+ case PseudoElement_ComboBoxDropDown:
+ if (sz.width() == -1)
+ sz.setWidth(16);
+ break;
+
+ case PseudoElement_ComboBoxArrow:
+ case PseudoElement_DownArrow:
+ case PseudoElement_ToolButtonMenuArrow:
+ case PseudoElement_ToolButtonDownArrow:
+ case PseudoElement_MenuRightArrow:
+ if (sz.width() == -1)
+ sz.setWidth(13);
+ if (sz.height() == -1)
+ sz.setHeight(13);
+ break;
+
+ case PseudoElement_SpinBoxUpButton:
+ case PseudoElement_SpinBoxDownButton:
+ if (sz.width() == -1)
+ sz.setWidth(16);
+ if (sz.height() == -1)
+ sz.setHeight(rect.height()/2);
+ break;
+
+ case PseudoElement_ToolButtonMenu:
+ if (sz.width() == -1)
+ sz.setWidth(base->pixelMetric(PM_MenuButtonIndicator, 0, w));
+ break;
+
+ case PseudoElement_HeaderViewUpArrow:
+ case PseudoElement_HeaderViewDownArrow: {
+ int pm = base->pixelMetric(PM_HeaderMargin, 0, w);
+ if (sz.width() == -1)
+ sz.setWidth(pm);
+ if (sz.height() == 1)
+ sz.setHeight(pm);
+ break;
+ }
+
+ case PseudoElement_ScrollBarFirst:
+ case PseudoElement_ScrollBarLast:
+ case PseudoElement_ScrollBarAddLine:
+ case PseudoElement_ScrollBarSubLine:
+ case PseudoElement_ScrollBarSlider: {
+ int pm = pixelMetric(QStyle::PM_ScrollBarExtent, 0, w);
+ if (sz.width() == -1)
+ sz.setWidth(pm);
+ if (sz.height() == -1)
+ sz.setHeight(pm);
+ break;
+ }
+
+ case PseudoElement_DockWidgetCloseButton:
+ case PseudoElement_DockWidgetFloatButton: {
+ int iconSize = pixelMetric(PM_SmallIconSize, 0, w);
+ return QSize(iconSize, iconSize);
+ }
+
+ default:
+ break;
+ }
+
+ // expand to rectangle
+ if (sz.height() == -1)
+ sz.setHeight(rect.height());
+ if (sz.width() == -1)
+ sz.setWidth(rect.width());
+
+ return sz;
+}
+
+static PositionMode defaultPositionMode(int pe)
+{
+ switch (pe) {
+ case PseudoElement_ScrollBarFirst:
+ case PseudoElement_ScrollBarLast:
+ case PseudoElement_ScrollBarAddLine:
+ case PseudoElement_ScrollBarSubLine:
+ case PseudoElement_ScrollBarAddPage:
+ case PseudoElement_ScrollBarSubPage:
+ case PseudoElement_ScrollBarSlider:
+ case PseudoElement_SliderGroove:
+ case PseudoElement_SliderHandle:
+ case PseudoElement_TabWidgetPane:
+ return PositionMode_Absolute;
+ default:
+ return PositionMode_Static;
+ }
+}
+
+QRect QStyleSheetStyle::positionRect(const QWidget *w, const QRenderRule &rule2, int pe,
+ const QRect &originRect, Qt::LayoutDirection dir) const
+{
+ const QStyleSheetPositionData *p = rule2.position();
+ PositionMode mode = (p && p->mode != PositionMode_Unknown) ? p->mode : defaultPositionMode(pe);
+ Qt::Alignment position = (p && p->position != 0) ? p->position : defaultPosition(pe);
+ QRect r;
+
+ if (mode != PositionMode_Absolute) {
+ QSize sz = defaultSize(w, rule2.size(), originRect, pe);
+ sz = sz.expandedTo(rule2.minimumContentsSize());
+ r = QStyle::alignedRect(dir, position, sz, originRect);
+ if (p) {
+ int left = p->left ? p->left : -p->right;
+ int top = p->top ? p->top : -p->bottom;
+ r.translate(dir == Qt::LeftToRight ? left : -left, top);
+ }
+ } else {
+ r = p ? originRect.adjusted(dir == Qt::LeftToRight ? p->left : p->right, p->top,
+ dir == Qt::LeftToRight ? -p->right : -p->left, -p->bottom)
+ : originRect;
+ if (rule2.hasContentsSize()) {
+ QSize sz = rule2.size().expandedTo(rule2.minimumContentsSize());
+ if (sz.width() == -1) sz.setWidth(r.width());
+ if (sz.height() == -1) sz.setHeight(r.height());
+ r = QStyle::alignedRect(dir, position, sz, r);
+ }
+ }
+ return r;
+}
+
+QRect QStyleSheetStyle::positionRect(const QWidget *w, const QRenderRule& rule1, const QRenderRule& rule2, int pe,
+ const QRect& rect, Qt::LayoutDirection dir) const
+{
+ const QStyleSheetPositionData *p = rule2.position();
+ Origin origin = (p && p->origin != Origin_Unknown) ? p->origin : defaultOrigin(pe);
+ QRect originRect = rule1.originRect(rect, origin);
+ return positionRect(w, rule2, pe, originRect, dir);
+}
+
+
+/** \internal
+ For widget that have an embedded widget (such as combobox) return that embedded widget.
+ otherwise return the widget itself
+ */
+static QWidget *embeddedWidget(QWidget *w)
+{
+#ifndef QT_NO_COMBOBOX
+ if (QComboBox *cmb = qobject_cast<QComboBox *>(w)) {
+ if (cmb->isEditable())
+ return cmb->lineEdit();
+ else
+ return cmb;
+ }
+#endif
+
+#ifndef QT_NO_SPINBOX
+ if (QAbstractSpinBox *sb = qobject_cast<QAbstractSpinBox *>(w))
+ return qFindChild<QLineEdit *>(sb);
+#endif
+
+#ifndef QT_NO_SCROLLAREA
+ if (QAbstractScrollArea *sa = qobject_cast<QAbstractScrollArea *>(w))
+ return sa->viewport();
+#endif
+
+ return w;
+}
+
+/** \internal
+ in case w is an embedded widget, return the container widget
+ (i.e, the widget for which the rules actualy apply)
+ (exemple, if w is a lineedit embedded in a combobox, return the combobox)
+
+ if w is not embedded, return w itself
+*/
+static QWidget *containerWidget(const QWidget *w)
+{
+#ifndef QT_NO_LINEEDIT
+ if (qobject_cast<const QLineEdit *>(w)) {
+ //if the QLineEdit is an embeddedWidget, we need the rule of the real widget
+#ifndef QT_NO_COMBOBOX
+ if (qobject_cast<const QComboBox *>(w->parentWidget()))
+ return w->parentWidget();
+#endif
+#ifndef QT_NO_SPINBOX
+ if (qobject_cast<const QAbstractSpinBox *>(w->parentWidget()))
+ return w->parentWidget();
+#endif
+ }
+#endif // QT_NO_LINEEDIT
+
+#ifndef QT_NO_SCROLLAREA
+ if (const QAbstractScrollArea *sa = qobject_cast<const QAbstractScrollArea *>(w->parentWidget())) {
+ if (sa->viewport() == w)
+ return w->parentWidget();
+ }
+#endif
+
+ return const_cast<QWidget *>(w);
+}
+
+/** \internal
+ returns true if the widget can NOT be styled directly
+ */
+static bool unstylable(const QWidget *w)
+{
+ if (w->windowType() == Qt::Desktop)
+ return true;
+
+ if (!w->styleSheet().isEmpty())
+ return false;
+
+ if (containerWidget(w) != w)
+ return true;
+
+#ifndef QT_NO_FRAME
+ // detect QComboBoxPrivateContainer
+ else if (qobject_cast<const QFrame *>(w)) {
+ if (0
+#ifndef QT_NO_COMBOBOX
+ || qobject_cast<const QComboBox *>(w->parentWidget())
+#endif
+ )
+ return true;
+ }
+#endif
+ return false;
+}
+
+static quint64 extendedPseudoClass(const QWidget *w)
+{
+ quint64 pc = w->isWindow() ? quint64(PseudoClass_Window) : 0;
+ if (const QAbstractSlider *slider = qobject_cast<const QAbstractSlider *>(w)) {
+ pc |= ((slider->orientation() == Qt::Vertical) ? PseudoClass_Vertical : PseudoClass_Horizontal);
+ } else
+#ifndef QT_NO_COMBOBOX
+ if (const QComboBox *combo = qobject_cast<const QComboBox *>(w)) {
+ if (combo->isEditable())
+ pc |= (combo->isEditable() ? PseudoClass_Editable : PseudoClass_ReadOnly);
+ } else
+#endif
+#ifndef QT_NO_LINEEDIT
+ if (const QLineEdit *edit = qobject_cast<const QLineEdit *>(w)) {
+ pc |= (edit->isReadOnly() ? PseudoClass_ReadOnly : PseudoClass_Editable);
+ } else
+#endif
+ { } // required for the above ifdef'ery to work
+ return pc;
+}
+
+// sets up the geometry of the widget. We set a dynamic property when
+// we modify the min/max size of the widget. The min/max size is restored
+// to their original value when a new stylesheet that does not contain
+// the CSS properties is set and when the widget has this dynamic property set.
+// This way we don't trample on users who had setup a min/max size in code and
+// don't use stylesheets at all.
+void QStyleSheetStyle::setGeometry(QWidget *w)
+{
+ QRenderRule rule = renderRule(w, PseudoElement_None, PseudoClass_Enabled | extendedPseudoClass(w));
+ const QStyleSheetGeometryData *geo = rule.geometry();
+ if (w->property("_q_stylesheet_minw").toBool()
+ && ((!rule.hasGeometry() || geo->minWidth == -1))) {
+ w->setMinimumWidth(0);
+ w->setProperty("_q_stylesheet_minw", QVariant());
+ }
+ if (w->property("_q_stylesheet_minh").toBool()
+ && ((!rule.hasGeometry() || geo->minHeight == -1))) {
+ w->setMinimumHeight(0);
+ w->setProperty("_q_stylesheet_minh", QVariant());
+ }
+ if (w->property("_q_stylesheet_maxw").toBool()
+ && ((!rule.hasGeometry() || geo->maxWidth == -1))) {
+ w->setMaximumWidth(QWIDGETSIZE_MAX);
+ w->setProperty("_q_stylesheet_maxw", QVariant());
+ }
+ if (w->property("_q_stylesheet_maxh").toBool()
+ && ((!rule.hasGeometry() || geo->maxHeight == -1))) {
+ w->setMaximumHeight(QWIDGETSIZE_MAX);
+ w->setProperty("_q_stylesheet_maxh", QVariant());
+ }
+
+
+ if (rule.hasGeometry()) {
+ if (geo->minWidth != -1) {
+ w->setProperty("_q_stylesheet_minw", true);
+ w->setMinimumWidth(rule.boxSize(QSize(qMax(geo->width, geo->minWidth), 0)).width());
+ }
+ if (geo->minHeight != -1) {
+ w->setProperty("_q_stylesheet_minh", true);
+ w->setMinimumHeight(rule.boxSize(QSize(0, qMax(geo->height, geo->minHeight))).height());
+ }
+ if (geo->maxWidth != -1) {
+ w->setProperty("_q_stylesheet_maxw", true);
+ w->setMaximumWidth(rule.boxSize(QSize(qMin(geo->width == -1 ? QWIDGETSIZE_MAX : geo->width,
+ geo->maxWidth == -1 ? QWIDGETSIZE_MAX : geo->maxWidth), 0)).width());
+ }
+ if (geo->maxHeight != -1) {
+ w->setProperty("_q_stylesheet_maxh", true);
+ w->setMaximumHeight(rule.boxSize(QSize(0, qMin(geo->height == -1 ? QWIDGETSIZE_MAX : geo->height,
+ geo->maxHeight == -1 ? QWIDGETSIZE_MAX : geo->maxHeight))).height());
+ }
+ }
+}
+
+void QStyleSheetStyle::setProperties(QWidget *w)
+{
+ QHash<QString, QVariant> propertyHash;
+ QVector<Declaration> decls = declarations(styleRules(w), QString());
+
+ // run through the declarations in order
+ for (int i = 0; i < decls.count(); i++) {
+ const Declaration &decl = decls.at(i);
+ QString property = decl.d->property;
+ if (!property.startsWith(QLatin1String("qproperty-"), Qt::CaseInsensitive))
+ continue;
+ property.remove(0, 10); // strip "qproperty-"
+ const QVariant value = w->property(property.toLatin1());
+ const QMetaObject *metaObject = w->metaObject();
+ int index = metaObject->indexOfProperty(property.toLatin1());
+ if (index == -1) {
+ qWarning() << w << " does not have a property named " << property;
+ continue;
+ }
+ QMetaProperty metaProperty = metaObject->property(index);
+ if (!metaProperty.isWritable() || !metaProperty.isDesignable()) {
+ qWarning() << w << " cannot design property named " << property;
+ continue;
+ }
+ QVariant v;
+ switch (value.type()) {
+ case QVariant::Icon: v = decl.iconValue(); break;
+ case QVariant::Image: v = QImage(decl.uriValue()); break;
+ case QVariant::Pixmap: v = QPixmap(decl.uriValue()); break;
+ case QVariant::Rect: v = decl.rectValue(); break;
+ case QVariant::Size: v = decl.sizeValue(); break;
+ case QVariant::Color: v = decl.colorValue(); break;
+ case QVariant::Brush: v = decl.brushValue(); break;
+#ifndef QT_NO_SHORTCUT
+ case QVariant::KeySequence: v = QKeySequence(decl.d->values.at(0).variant.toString()); break;
+#endif
+ default: v = decl.d->values.at(0).variant; break;
+ }
+ propertyHash[property] = v;
+ }
+ // apply the values
+ const QList<QString> properties = propertyHash.keys();
+ for (int i = 0; i < properties.count(); i++) {
+ const QString &property = properties.at(i);
+ w->setProperty(property.toLatin1(), propertyHash[property]);
+ }
+}
+
+void QStyleSheetStyle::setPalette(QWidget *w)
+{
+ struct RuleRoleMap {
+ int state;
+ QPalette::ColorGroup group;
+ } map[3] = {
+ { PseudoClass_Active | PseudoClass_Enabled, QPalette::Active },
+ { PseudoClass_Disabled, QPalette::Disabled },
+ { PseudoClass_Enabled, QPalette::Inactive }
+ };
+
+ QPalette p = w->palette();
+ QWidget *ew = embeddedWidget(w);
+
+ for (int i = 0; i < 3; i++) {
+ QRenderRule rule = renderRule(w, PseudoElement_None, map[i].state | extendedPseudoClass(w));
+ if (i == 0) {
+ if (!w->property("_q_styleSheetWidgetFont").isValid()) {
+ saveWidgetFont(w, w->font());
+ }
+ updateStyleSheetFont(w);
+ if (ew != w)
+ updateStyleSheetFont(ew);
+ }
+
+ rule.configurePalette(&p, map[i].group, ew, ew != w);
+ }
+
+ customPaletteWidgets->insert(w, w->palette());
+ w->setPalette(p);
+ if (ew != w)
+ ew->setPalette(p);
+}
+
+void QStyleSheetStyle::unsetPalette(QWidget *w)
+{
+ if (customPaletteWidgets->contains(w)) {
+ QPalette p = customPaletteWidgets->value(w);
+ w->setPalette(p);
+ QWidget *ew = embeddedWidget(w);
+ if (ew != w)
+ ew->setPalette(p);
+ customPaletteWidgets->remove(w);
+ }
+ QVariant oldFont = w->property("_q_styleSheetWidgetFont");
+ if (oldFont.isValid()) {
+ w->setFont(qVariantValue<QFont>(oldFont));
+ }
+ if (autoFillDisabledWidgets->contains(w)) {
+ embeddedWidget(w)->setAutoFillBackground(true);
+ autoFillDisabledWidgets->remove(w);
+ }
+}
+
+static void updateWidgets(const QList<const QWidget *>& widgets)
+{
+ if (!styleRulesCache->isEmpty() || !hasStyleRuleCache->isEmpty() || !renderRulesCache->isEmpty()) {
+ for (int i = 0; i < widgets.size(); ++i) {
+ const QWidget *widget = widgets.at(i);
+ styleRulesCache->remove(widget);
+ hasStyleRuleCache->remove(widget);
+ renderRulesCache->remove(widget);
+ }
+ }
+ for (int i = 0; i < widgets.size(); ++i) {
+ QWidget *widget = const_cast<QWidget *>(widgets.at(i));
+ if (widget == 0)
+ continue;
+ widget->style()->polish(widget);
+ QEvent event(QEvent::StyleChange);
+ qApp->sendEvent(widget, &event);
+ widget->update();
+ widget->updateGeometry();
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// The stylesheet style
+int QStyleSheetStyle::numinstances = 0;
+
+QStyleSheetStyle::QStyleSheetStyle(QStyle *base)
+ : QWindowsStyle(*new QStyleSheetStylePrivate), base(base), refcount(1)
+{
+ ++numinstances;
+ if (numinstances == 1) {
+ styleRulesCache = new QHash<const QWidget *, QVector<StyleRule> >;
+ hasStyleRuleCache = new QHash<const QWidget *, QHash<int, bool> >;
+ renderRulesCache = new QHash<const QWidget *, QRenderRules>;
+ customPaletteWidgets = new QHash<const QWidget *, QPalette>;
+ styleSheetCache = new QHash<const void *, StyleSheet>;
+ autoFillDisabledWidgets = new QSet<const QWidget *>;
+ }
+}
+
+QStyleSheetStyle::~QStyleSheetStyle()
+{
+ --numinstances;
+ if (numinstances == 0) {
+ delete styleRulesCache;
+ styleRulesCache = 0;
+ delete hasStyleRuleCache;
+ hasStyleRuleCache = 0;
+ delete renderRulesCache;
+ renderRulesCache = 0;
+ delete customPaletteWidgets;
+ customPaletteWidgets = 0;
+ delete styleSheetCache;
+ styleSheetCache = 0;
+ delete autoFillDisabledWidgets;
+ autoFillDisabledWidgets = 0;
+ }
+}
+QStyle *QStyleSheetStyle::baseStyle() const
+{
+ if (base)
+ return base;
+ if (QStyleSheetStyle *me = qobject_cast<QStyleSheetStyle *>(qApp->style()))
+ return me->base;
+ return qApp->style();
+}
+
+void QStyleSheetStyle::widgetDestroyed(QObject *o)
+{
+ styleRulesCache->remove((const QWidget *)o);
+ hasStyleRuleCache->remove((const QWidget *)o);
+ renderRulesCache->remove((const QWidget *)o);
+ customPaletteWidgets->remove((const QWidget *)o);
+ styleSheetCache->remove((const QWidget *)o);
+ autoFillDisabledWidgets->remove((const QWidget *)o);
+}
+
+/*!
+ * Make sure that the cache will be clean by connecting destroyed if needed.
+ * return false if the widget is not stylable;
+ */
+bool QStyleSheetStyle::initWidget(const QWidget *w) const
+{
+ if (!w)
+ return false;
+ if(w->testAttribute(Qt::WA_StyleSheet))
+ return true;
+
+ if(unstylable(w))
+ return false;
+
+ const_cast<QWidget *>(w)->setAttribute(Qt::WA_StyleSheet, true);
+ QObject::connect(w, SIGNAL(destroyed(QObject*)), this, SLOT(widgetDestroyed(QObject*)));
+ return true;
+}
+
+void QStyleSheetStyle::polish(QWidget *w)
+{
+ baseStyle()->polish(w);
+ RECURSION_GUARD(return)
+
+ if (!initWidget(w))
+ return;
+
+ if (styleRulesCache->contains(w)) {
+ // the widget accessed its style pointer before polish (or repolish)
+ // (exemple: the QAbstractSpinBox constructor ask for the stylehint)
+ styleRulesCache->remove(w);
+ hasStyleRuleCache->remove(w);
+ renderRulesCache->remove(w);
+ }
+ setGeometry(w);
+ setProperties(w);
+ unsetPalette(w);
+ setPalette(w);
+
+ //set the WA_Hover attribute if one of the selector depends of the hover state
+ QVector<StyleRule> rules = styleRules(w);
+ for (int i = 0; i < rules.count(); i++) {
+ const Selector& selector = rules.at(i).selectors.at(0);
+ quint64 negated = 0;
+ quint64 cssClass = selector.pseudoClass(&negated);
+ if ( cssClass & PseudoClass_Hover || negated & PseudoClass_Hover) {
+ w->setAttribute(Qt::WA_Hover);
+ embeddedWidget(w)->setAttribute(Qt::WA_Hover);
+ }
+ }
+
+
+#ifndef QT_NO_SCROLLAREA
+ if (QAbstractScrollArea *sa = qobject_cast<QAbstractScrollArea *>(w)) {
+ QRenderRule rule = renderRule(sa, PseudoElement_None, PseudoClass_Enabled);
+ if ((rule.hasBorder() && rule.border()->hasBorderImage())
+ || (rule.hasBackground() && !rule.background()->pixmap.isNull())) {
+ QObject::disconnect(sa->horizontalScrollBar(), SIGNAL(valueChanged(int)),
+ sa, SLOT(update()));
+ QObject::disconnect(sa->verticalScrollBar(), SIGNAL(valueChanged(int)),
+ sa, SLOT(update()));
+ QObject::connect(sa->horizontalScrollBar(), SIGNAL(valueChanged(int)),
+ sa, SLOT(update()));
+ QObject::connect(sa->verticalScrollBar(), SIGNAL(valueChanged(int)),
+ sa, SLOT(update()));
+ }
+ }
+#endif
+
+#ifndef QT_NO_PROGRESSBAR
+ if (QProgressBar *pb = qobject_cast<QProgressBar *>(w)) {
+ QWindowsStyle::polish(pb);
+ }
+#endif
+
+ QRenderRule rule = renderRule(w, PseudoElement_None, PseudoClass_Any);
+ if (rule.hasDrawable() || rule.hasBox()) {
+ if (w->metaObject() == &QWidget::staticMetaObject
+#ifndef QT_NO_MENUBAR
+ || qobject_cast<QMenuBar *>(w)
+#endif
+#ifndef QT_NO_MENU
+ || qobject_cast<QMenu *>(w)
+#endif
+#ifndef QT_NO_ITEMVIEWS
+ || qobject_cast<QHeaderView *>(w)
+#endif
+#ifndef QT_NO_TABBAR
+ || qobject_cast<QTabBar *>(w)
+#endif
+#ifndef QT_NO_FRAME
+ || qobject_cast<QFrame *>(w)
+#endif
+#ifndef QT_NO_MAINWINDOW
+ || qobject_cast<QMainWindow *>(w)
+#endif
+#ifndef QT_NO_MDIAREA
+ || qobject_cast<QMdiSubWindow *>(w)
+#endif
+ || qobject_cast<QDialog *>(w)) {
+ w->setAttribute(Qt::WA_StyledBackground, true);
+ }
+ QWidget *ew = embeddedWidget(w);
+ if (ew->autoFillBackground()) {
+ ew->setAutoFillBackground(false);
+ autoFillDisabledWidgets->insert(w);
+ }
+ if (!rule.hasBackground() || rule.background()->isTransparent() || rule.hasBox()
+ || (!rule.hasNativeBorder() && !rule.border()->isOpaque()))
+ w->setAttribute(Qt::WA_OpaquePaintEvent, false);
+ }
+}
+
+void QStyleSheetStyle::polish(QApplication *app)
+{
+ baseStyle()->polish(app);
+}
+
+void QStyleSheetStyle::polish(QPalette &pal)
+{
+ baseStyle()->polish(pal);
+}
+
+void QStyleSheetStyle::repolish(QWidget *w)
+{
+ QList<const QWidget *> children = qFindChildren<const QWidget *>(w, QString());
+ children.append(w);
+ styleSheetCache->remove(w);
+ updateWidgets(children);
+}
+
+void QStyleSheetStyle::repolish(QApplication *app)
+{
+ Q_UNUSED(app);
+ const QList<const QWidget*> allWidgets = styleRulesCache->keys();
+ styleSheetCache->remove(qApp);
+ styleRulesCache->clear();
+ hasStyleRuleCache->clear();
+ renderRulesCache->clear();
+ updateWidgets(allWidgets);
+}
+
+void QStyleSheetStyle::unpolish(QWidget *w)
+{
+ if (!w || !w->testAttribute(Qt::WA_StyleSheet)) {
+ baseStyle()->unpolish(w);
+ return;
+ }
+
+ styleRulesCache->remove(w);
+ hasStyleRuleCache->remove(w);
+ renderRulesCache->remove(w);
+ styleSheetCache->remove(w);
+ unsetPalette(w);
+ w->setProperty("_q_stylesheet_minw", QVariant());
+ w->setProperty("_q_stylesheet_minh", QVariant());
+ w->setProperty("_q_stylesheet_maxw", QVariant());
+ w->setProperty("_q_stylesheet_maxh", QVariant());
+ w->setAttribute(Qt::WA_StyleSheet, false);
+ QObject::disconnect(w, 0, this, 0);
+#ifndef QT_NO_SCROLLAREA
+ if (QAbstractScrollArea *sa = qobject_cast<QAbstractScrollArea *>(w)) {
+ QObject::disconnect(sa->horizontalScrollBar(), SIGNAL(valueChanged(int)),
+ sa, SLOT(update()));
+ QObject::disconnect(sa->verticalScrollBar(), SIGNAL(valueChanged(int)),
+ sa, SLOT(update()));
+ }
+#endif
+#ifndef QT_NO_PROGRESSBAR
+ if (QProgressBar *pb = qobject_cast<QProgressBar *>(w))
+ QWindowsStyle::unpolish(pb);
+#endif
+ baseStyle()->unpolish(w);
+}
+
+void QStyleSheetStyle::unpolish(QApplication *app)
+{
+ baseStyle()->unpolish(app);
+ RECURSION_GUARD(return)
+ styleRulesCache->clear();
+ hasStyleRuleCache->clear();
+ renderRulesCache->clear();
+ styleSheetCache->remove(qApp);
+}
+
+#ifndef QT_NO_TABBAR
+inline static bool verticalTabs(QTabBar::Shape shape)
+{
+ return shape == QTabBar::RoundedWest
+ || shape == QTabBar::RoundedEast
+ || shape == QTabBar::TriangularWest
+ || shape == QTabBar::TriangularEast;
+}
+#endif // QT_NO_TABBAR
+
+void QStyleSheetStyle::drawComplexControl(ComplexControl cc, const QStyleOptionComplex *opt, QPainter *p,
+ const QWidget *w) const
+{
+ RECURSION_GUARD(baseStyle()->drawComplexControl(cc, opt, p, w); return)
+
+ QRenderRule rule = renderRule(w, opt);
+
+ switch (cc) {
+ case CC_ComboBox:
+ if (const QStyleOptionComboBox *cmb = qstyleoption_cast<const QStyleOptionComboBox *>(opt)) {
+ QStyleOptionComboBox cmbOpt(*cmb);
+ cmbOpt.rect = rule.borderRect(opt->rect);
+ if (rule.hasNativeBorder()) {
+ rule.drawBackgroundImage(p, cmbOpt.rect);
+ rule.configurePalette(&cmbOpt.palette, QPalette::ButtonText, QPalette::Button);
+ bool customDropDown = (opt->subControls & QStyle::SC_ComboBoxArrow)
+ && hasStyleRule(w, PseudoElement_ComboBoxDropDown);
+ if (customDropDown)
+ cmbOpt.subControls &= ~QStyle::SC_ComboBoxArrow;
+ if (rule.baseStyleCanDraw()) {
+ baseStyle()->drawComplexControl(cc, &cmbOpt, p, w);
+ } else {
+ QWindowsStyle::drawComplexControl(cc, &cmbOpt, p, w);
+ }
+ if (!customDropDown)
+ return;
+ } else {
+ rule.drawRule(p, opt->rect);
+ }
+
+ if (opt->subControls & QStyle::SC_ComboBoxArrow) {
+ QRenderRule subRule = renderRule(w, opt, PseudoElement_ComboBoxDropDown);
+ if (subRule.hasDrawable()) {
+ QRect r = subControlRect(CC_ComboBox, opt, SC_ComboBoxArrow, w);
+ subRule.drawRule(p, r);
+ QRenderRule subRule2 = renderRule(w, opt, PseudoElement_ComboBoxArrow);
+ r = positionRect(w, subRule, subRule2, PseudoElement_ComboBoxArrow, r, opt->direction);
+ subRule2.drawRule(p, r);
+ } else {
+ cmbOpt.subControls = QStyle::SC_ComboBoxArrow;
+ QWindowsStyle::drawComplexControl(cc, &cmbOpt, p, w);
+ }
+ }
+
+ return;
+ }
+ break;
+
+#ifndef QT_NO_SPINBOX
+ case CC_SpinBox:
+ if (const QStyleOptionSpinBox *spin = qstyleoption_cast<const QStyleOptionSpinBox *>(opt)) {
+ QStyleOptionSpinBox spinOpt(*spin);
+ rule.configurePalette(&spinOpt.palette, QPalette::ButtonText, QPalette::Button);
+ spinOpt.rect = rule.borderRect(opt->rect);
+ bool customUp = true, customDown = true;
+ QRenderRule upRule = renderRule(w, opt, PseudoElement_SpinBoxUpButton);
+ QRenderRule downRule = renderRule(w, opt, PseudoElement_SpinBoxDownButton);
+ bool upRuleMatch = upRule.hasGeometry();
+ bool downRuleMatch = downRule.hasGeometry();
+ if (rule.hasNativeBorder() && !upRuleMatch && !downRuleMatch) {
+ rule.drawBackgroundImage(p, spinOpt.rect);
+ customUp = (opt->subControls & QStyle::SC_SpinBoxUp)
+ && hasStyleRule(w, PseudoElement_SpinBoxUpButton);
+ if (customUp)
+ spinOpt.subControls &= ~QStyle::SC_SpinBoxUp;
+ customDown = (opt->subControls & QStyle::SC_SpinBoxDown)
+ && hasStyleRule(w, PseudoElement_SpinBoxDownButton);
+ if (customDown)
+ spinOpt.subControls &= ~QStyle::SC_SpinBoxDown;
+ if (rule.baseStyleCanDraw()) {
+ baseStyle()->drawComplexControl(cc, &spinOpt, p, w);
+ } else {
+ QWindowsStyle::drawComplexControl(cc, &spinOpt, p, w);
+ }
+ if (!customUp && !customDown)
+ return;
+ } else {
+ rule.drawRule(p, opt->rect);
+ }
+
+ if ((opt->subControls & QStyle::SC_SpinBoxUp) && customUp) {
+ QRenderRule subRule = renderRule(w, opt, PseudoElement_SpinBoxUpButton);
+ if (subRule.hasDrawable()) {
+ QRect r = subControlRect(CC_SpinBox, opt, SC_SpinBoxUp, w);
+ subRule.drawRule(p, r);
+ QRenderRule subRule2 = renderRule(w, opt, PseudoElement_SpinBoxUpArrow);
+ r = positionRect(w, subRule, subRule2, PseudoElement_SpinBoxUpArrow, r, opt->direction);
+ subRule2.drawRule(p, r);
+ } else {
+ spinOpt.subControls = QStyle::SC_SpinBoxUp;
+ QWindowsStyle::drawComplexControl(cc, &spinOpt, p, w);
+ }
+ }
+
+ if ((opt->subControls & QStyle::SC_SpinBoxDown) && customDown) {
+ QRenderRule subRule = renderRule(w, opt, PseudoElement_SpinBoxDownButton);
+ if (subRule.hasDrawable()) {
+ QRect r = subControlRect(CC_SpinBox, opt, SC_SpinBoxDown, w);
+ subRule.drawRule(p, r);
+ QRenderRule subRule2 = renderRule(w, opt, PseudoElement_SpinBoxDownArrow);
+ r = positionRect(w, subRule, subRule2, PseudoElement_SpinBoxDownArrow, r, opt->direction);
+ subRule2.drawRule(p, r);
+ } else {
+ spinOpt.subControls = QStyle::SC_SpinBoxDown;
+ QWindowsStyle::drawComplexControl(cc, &spinOpt, p, w);
+ }
+ }
+ return;
+ }
+ break;
+#endif // QT_NO_SPINBOX
+
+ case CC_GroupBox:
+ if (const QStyleOptionGroupBox *gb = qstyleoption_cast<const QStyleOptionGroupBox *>(opt)) {
+
+ QRect labelRect, checkBoxRect, titleRect, frameRect;
+ bool hasTitle = (gb->subControls & QStyle::SC_GroupBoxCheckBox) || !gb->text.isEmpty();
+
+ if (!rule.hasDrawable() && (!hasTitle || !hasStyleRule(w, PseudoElement_GroupBoxTitle))
+ && !hasStyleRule(w, PseudoElement_Indicator) && !rule.hasBox() && !rule.hasFont && !rule.hasPalette()) {
+ // let the native style draw the combobox if there is no style for it.
+ break;
+ }
+ rule.drawBackground(p, opt->rect);
+
+ QRenderRule titleRule = renderRule(w, opt, PseudoElement_GroupBoxTitle);
+ bool clipSet = false;
+
+ if (hasTitle) {
+ labelRect = subControlRect(CC_GroupBox, opt, SC_GroupBoxLabel, w);
+ //Some native style (such as mac) may return a too small rectangle (because they use smaller fonts), so we may need to expand it a little bit.
+ labelRect.setSize(labelRect.size().expandedTo(ParentStyle::subControlRect(CC_GroupBox, opt, SC_GroupBoxLabel, w).size()));
+ if (gb->subControls & QStyle::SC_GroupBoxCheckBox) {
+ checkBoxRect = subControlRect(CC_GroupBox, opt, SC_GroupBoxCheckBox, w);
+ titleRect = titleRule.boxRect(checkBoxRect.united(labelRect));
+ } else {
+ titleRect = titleRule.boxRect(labelRect);
+ }
+ if (!titleRule.hasBackground() || !titleRule.background()->isTransparent()) {
+ clipSet = true;
+ p->save();
+ p->setClipRegion(QRegion(opt->rect) - titleRect);
+ }
+ }
+
+ frameRect = subControlRect(CC_GroupBox, opt, SC_GroupBoxFrame, w);
+ QStyleOptionFrameV2 frame;
+ frame.QStyleOption::operator=(*gb);
+ frame.features = gb->features;
+ frame.lineWidth = gb->lineWidth;
+ frame.midLineWidth = gb->midLineWidth;
+ frame.rect = frameRect;
+ drawPrimitive(PE_FrameGroupBox, &frame, p, w);
+
+ if (clipSet)
+ p->restore();
+
+ // draw background and frame of the title
+ if (hasTitle)
+ titleRule.drawRule(p, titleRect);
+
+ // draw the indicator
+ if (gb->subControls & QStyle::SC_GroupBoxCheckBox) {
+ QStyleOptionButton box;
+ box.QStyleOption::operator=(*gb);
+ box.rect = checkBoxRect;
+ drawPrimitive(PE_IndicatorCheckBox, &box, p, w);
+ }
+
+ // draw the text
+ if (!gb->text.isEmpty()) {
+ int alignment = int(Qt::AlignCenter | Qt::TextShowMnemonic);
+ if (!styleHint(QStyle::SH_UnderlineShortcut, opt, w)) {
+ alignment |= Qt::TextHideMnemonic;
+ }
+
+ QPalette pal = gb->palette;
+ if (gb->textColor.isValid())
+ pal.setColor(QPalette::WindowText, gb->textColor);
+ titleRule.configurePalette(&pal, QPalette::WindowText, QPalette::Window);
+ drawItemText(p, labelRect, alignment, pal, gb->state & State_Enabled,
+ gb->text, QPalette::WindowText);
+ }
+
+ return;
+ }
+ break;
+
+ case CC_ToolButton:
+ if (const QStyleOptionToolButton *tool = qstyleoption_cast<const QStyleOptionToolButton *>(opt)) {
+ QStyleOptionToolButton toolOpt(*tool);
+ rule.configurePalette(&toolOpt.palette, QPalette::ButtonText, QPalette::Button);
+ toolOpt.rect = rule.borderRect(opt->rect);
+ bool customArrow = (tool->features & (QStyleOptionToolButton::HasMenu | QStyleOptionToolButton::MenuButtonPopup));
+ bool customDropDown = tool->features & QStyleOptionToolButton::MenuButtonPopup;
+ if (rule.hasNativeBorder()) {
+ if (tool->subControls & SC_ToolButton) {
+ //in some case (eg. the button is "auto raised") the style doesn't draw the background
+ //so we need to draw the background.
+ // use the same condition as in QCommonStyle
+ State bflags = tool->state & ~State_Sunken;
+ if (bflags & State_AutoRaise && (!(bflags & State_MouseOver) || !(bflags & State_Enabled)))
+ bflags &= ~State_Raised;
+ if (tool->state & State_Sunken && tool->activeSubControls & SC_ToolButton)
+ bflags |= State_Sunken;
+ if (!(bflags & (State_Sunken | State_On | State_Raised)))
+ rule.drawBackground(p, toolOpt.rect);
+ }
+ customArrow = customArrow && hasStyleRule(w, PseudoElement_ToolButtonDownArrow);
+ if (customArrow)
+ toolOpt.features &= ~QStyleOptionToolButton::HasMenu;
+ customDropDown = customDropDown && hasStyleRule(w, PseudoElement_ToolButtonMenu);
+ if (customDropDown)
+ toolOpt.subControls &= ~QStyle::SC_ToolButtonMenu;
+
+ if (rule.baseStyleCanDraw() && !(tool->features & QStyleOptionToolButton::Arrow)) {
+ baseStyle()->drawComplexControl(cc, &toolOpt, p, w);
+ } else {
+ QWindowsStyle::drawComplexControl(cc, &toolOpt, p, w);
+ }
+
+ if (!customArrow && !customDropDown)
+ return;
+ } else {
+ rule.drawRule(p, opt->rect);
+ toolOpt.rect = rule.contentsRect(opt->rect);
+ if (rule.hasFont)
+ toolOpt.font = rule.font;
+ drawControl(CE_ToolButtonLabel, &toolOpt, p, w);
+ }
+
+ QRenderRule subRule = renderRule(w, opt, PseudoElement_ToolButtonMenu);
+ QRect r = subControlRect(CC_ToolButton, opt, QStyle::SC_ToolButtonMenu, w);
+ if (customDropDown) {
+ if (opt->subControls & QStyle::SC_ToolButtonMenu) {
+ if (subRule.hasDrawable()) {
+ subRule.drawRule(p, r);
+ } else {
+ toolOpt.rect = r;
+ baseStyle()->drawPrimitive(PE_IndicatorButtonDropDown, &toolOpt, p, w);
+ }
+ }
+ }
+
+ if (customArrow) {
+ QRenderRule subRule2 = customDropDown ? renderRule(w, opt, PseudoElement_ToolButtonMenuArrow)
+ : renderRule(w, opt, PseudoElement_ToolButtonDownArrow);
+ QRect r2 = customDropDown
+ ? positionRect(w, subRule, subRule2, PseudoElement_ToolButtonMenuArrow, r, opt->direction)
+ : positionRect(w, rule, subRule2, PseudoElement_ToolButtonDownArrow, opt->rect, opt->direction);
+ if (subRule2.hasDrawable()) {
+ subRule2.drawRule(p, r2);
+ } else {
+ toolOpt.rect = r2;
+ baseStyle()->drawPrimitive(QStyle::PE_IndicatorArrowDown, &toolOpt, p, w);
+ }
+ }
+
+ return;
+ }
+ break;
+
+#ifndef QT_NO_SCROLLBAR
+ case CC_ScrollBar:
+ if (const QStyleOptionSlider *sb = qstyleoption_cast<const QStyleOptionSlider *>(opt)) {
+ QStyleOptionSlider sbOpt(*sb);
+ if (!rule.hasDrawable()) {
+ sbOpt.rect = rule.borderRect(opt->rect);
+ rule.drawBackgroundImage(p, opt->rect);
+ baseStyle()->drawComplexControl(cc, &sbOpt, p, w);
+ } else {
+ rule.drawRule(p, opt->rect);
+ QWindowsStyle::drawComplexControl(cc, opt, p, w);
+ }
+ return;
+ }
+ break;
+#endif // QT_NO_SCROLLBAR
+
+#ifndef QT_NO_SLIDER
+ case CC_Slider:
+ if (const QStyleOptionSlider *slider = qstyleoption_cast<const QStyleOptionSlider *>(opt)) {
+ rule.drawRule(p, opt->rect);
+
+ QRenderRule subRule = renderRule(w, opt, PseudoElement_SliderGroove);
+ if (!subRule.hasDrawable()) {
+ baseStyle()->drawComplexControl(cc, slider, p, w);
+ return;
+ }
+
+ QRect gr = subControlRect(cc, opt, SC_SliderGroove, w);
+ if (slider->subControls & SC_SliderGroove) {
+ subRule.drawRule(p, gr);
+ }
+
+ if (slider->subControls & SC_SliderHandle) {
+ QRenderRule subRule = renderRule(w, opt, PseudoElement_SliderHandle);
+ QRect hr = subControlRect(cc, opt, SC_SliderHandle, w);
+
+ QRenderRule subRule1 = renderRule(w, opt, PseudoElement_SliderSubPage);
+ if (subRule1.hasDrawable()) {
+ QRect r(gr.topLeft(),
+ slider->orientation == Qt::Horizontal
+ ? QPoint(hr.x()+hr.width()/2, gr.y()+gr.height())
+ : QPoint(gr.x()+gr.width(), hr.y()+hr.height()/2));
+ subRule1.drawRule(p, r);
+ }
+
+ QRenderRule subRule2 = renderRule(w, opt, PseudoElement_SliderAddPage);
+ if (subRule2.hasDrawable()) {
+ QRect r(slider->orientation == Qt::Horizontal
+ ? QPoint(hr.x()+hr.width()/2+1, gr.y())
+ : QPoint(gr.x(), hr.y()+hr.height()/2+1),
+ gr.bottomRight());
+ subRule2.drawRule(p, r);
+ }
+
+ subRule.drawRule(p, subRule.boxRect(hr, Margin));
+ }
+
+ if (slider->subControls & SC_SliderTickmarks) {
+ // TODO...
+ }
+
+ return;
+ }
+ break;
+#endif // QT_NO_SLIDER
+
+ case CC_MdiControls:
+ if (hasStyleRule(w, PseudoElement_MdiCloseButton)
+ || hasStyleRule(w, PseudoElement_MdiNormalButton)
+ || hasStyleRule(w, PseudoElement_MdiMinButton)) {
+ QList<QVariant> layout = rule.styleHint(QLatin1String("button-layout")).toList();
+ if (layout.isEmpty())
+ layout = subControlLayout(QLatin1String("mNX"));
+
+ QStyleOptionComplex optCopy(*opt);
+ optCopy.subControls = 0;
+ for (int i = 0; i < layout.count(); i++) {
+ int layoutButton = layout[i].toInt();
+ if (layoutButton < PseudoElement_MdiCloseButton
+ || layoutButton > PseudoElement_MdiNormalButton)
+ continue;
+ QStyle::SubControl control = knownPseudoElements[layoutButton].subControl;
+ if (!(opt->subControls & control))
+ continue;
+ QRenderRule subRule = renderRule(w, opt, layoutButton);
+ if (subRule.hasDrawable()) {
+ QRect rect = subRule.boxRect(subControlRect(CC_MdiControls, opt, control, w), Margin);
+ subRule.drawRule(p, rect);
+ QIcon icon = standardIcon(subControlIcon(layoutButton));
+ icon.paint(p, subRule.contentsRect(rect), Qt::AlignCenter);
+ } else {
+ optCopy.subControls |= control;
+ }
+ }
+
+ if (optCopy.subControls)
+ baseStyle()->drawComplexControl(CC_MdiControls, &optCopy, p, w);
+ return;
+ }
+ break;
+
+ case CC_TitleBar:
+ if (const QStyleOptionTitleBar *tb = qstyleoption_cast<const QStyleOptionTitleBar *>(opt)) {
+ QRenderRule subRule = renderRule(w, opt, PseudoElement_TitleBar);
+ if (!subRule.hasDrawable() && !subRule.hasBox() && !subRule.hasBorder())
+ break;
+ subRule.drawRule(p, opt->rect);
+ QHash<QStyle::SubControl, QRect> layout = titleBarLayout(w, tb);
+
+ QRect ir;
+ ir = layout[SC_TitleBarLabel];
+ if (ir.isValid()) {
+ if (subRule.hasPalette())
+ p->setPen(subRule.palette()->foreground.color());
+ p->fillRect(ir, Qt::white);
+ p->drawText(ir.x(), ir.y(), ir.width(), ir.height(), Qt::AlignLeft | Qt::AlignVCenter | Qt::TextSingleLine, tb->text);
+ }
+
+ QPixmap pm;
+
+ ir = layout[SC_TitleBarSysMenu];
+ if (ir.isValid()) {
+ QRenderRule subSubRule = renderRule(w, opt, PseudoElement_TitleBarSysMenu);
+ subSubRule.drawRule(p, ir);
+ ir = subSubRule.contentsRect(ir);
+ if (!tb->icon.isNull()) {
+ tb->icon.paint(p, ir);
+ } else {
+ int iconSize = pixelMetric(PM_SmallIconSize, tb, w);
+ pm = standardIcon(SP_TitleBarMenuButton, 0, w).pixmap(iconSize, iconSize);
+ drawItemPixmap(p, ir, Qt::AlignCenter, pm);
+ }
+ }
+
+ ir = layout[SC_TitleBarCloseButton];
+ if (ir.isValid()) {
+ QRenderRule subSubRule = renderRule(w, opt, PseudoElement_TitleBarCloseButton);
+ subSubRule.drawRule(p, ir);
+
+ QSize sz = subSubRule.contentsRect(ir).size();
+ if ((tb->titleBarFlags & Qt::WindowType_Mask) == Qt::Tool)
+ pm = standardIcon(SP_DockWidgetCloseButton, 0, w).pixmap(sz);
+ else
+ pm = standardIcon(SP_TitleBarCloseButton, 0, w).pixmap(sz);
+ drawItemPixmap(p, ir, Qt::AlignCenter, pm);
+ }
+
+ int pes[] = {
+ PseudoElement_TitleBarMaxButton,
+ PseudoElement_TitleBarMinButton,
+ PseudoElement_TitleBarNormalButton,
+ PseudoElement_TitleBarShadeButton,
+ PseudoElement_TitleBarUnshadeButton,
+ PseudoElement_TitleBarContextHelpButton
+ };
+
+ for (unsigned int i = 0; i < sizeof(pes)/sizeof(int); i++) {
+ int pe = pes[i];
+ QStyle::SubControl sc = knownPseudoElements[pe].subControl;
+ ir = layout[sc];
+ if (!ir.isValid())
+ continue;
+ QRenderRule subSubRule = renderRule(w, opt, pe);
+ subSubRule.drawRule(p, ir);
+ pm = standardIcon(subControlIcon(pe), 0, w).pixmap(subSubRule.contentsRect(ir).size());
+ drawItemPixmap(p, ir, Qt::AlignCenter, pm);
+ }
+
+ return;
+ }
+ break;
+
+
+ default:
+ break;
+ }
+
+ baseStyle()->drawComplexControl(cc, opt, p, w);
+}
+
+void QStyleSheetStyle::drawControl(ControlElement ce, const QStyleOption *opt, QPainter *p,
+ const QWidget *w) const
+{
+ RECURSION_GUARD(baseStyle()->drawControl(ce, opt, p, w); return)
+
+ QRenderRule rule = renderRule(w, opt);
+ int pe1 = PseudoElement_None, pe2 = PseudoElement_None;
+ bool fallback = false;
+
+ switch (ce) {
+ case CE_ToolButtonLabel:
+ if (const QStyleOptionToolButton *btn = qstyleoption_cast<const QStyleOptionToolButton *>(opt)) {
+ if (rule.hasBox() || btn->features & QStyleOptionToolButton::Arrow) {
+ QCommonStyle::drawControl(ce, opt, p, w);
+ } else {
+ QStyleOptionToolButton butOpt(*btn);
+ rule.configurePalette(&butOpt.palette, QPalette::ButtonText, QPalette::Button);
+ baseStyle()->drawControl(ce, &butOpt, p, w);
+ }
+ return;
+ }
+ break;
+
+ case CE_PushButton:
+ ParentStyle::drawControl(ce, opt, p, w);
+ return;
+
+ case CE_PushButtonBevel:
+ if (const QStyleOptionButton *btn = qstyleoption_cast<const QStyleOptionButton *>(opt)) {
+ QStyleOptionButton btnOpt(*btn);
+ btnOpt.rect = rule.borderRect(opt->rect);
+ if (rule.hasNativeBorder()) {
+ rule.drawBackgroundImage(p, btnOpt.rect);
+ rule.configurePalette(&btnOpt.palette, QPalette::ButtonText, QPalette::Button);
+ bool customMenu = (btn->features & QStyleOptionButton::HasMenu
+ && hasStyleRule(w, PseudoElement_PushButtonMenuIndicator));
+ if (customMenu)
+ btnOpt.features &= ~QStyleOptionButton::HasMenu;
+ if (rule.baseStyleCanDraw()) {
+ baseStyle()->drawControl(ce, &btnOpt, p, w);
+ } else {
+ QWindowsStyle::drawControl(ce, &btnOpt, p, w);
+ }
+ if (!customMenu)
+ return;
+ } else {
+ rule.drawRule(p, opt->rect);
+ }
+
+ if (btn->features & QStyleOptionButton::HasMenu) {
+ QRenderRule subRule = renderRule(w, opt, PseudoElement_PushButtonMenuIndicator);
+ QRect ir = positionRect(w, rule, subRule, PseudoElement_PushButtonMenuIndicator, opt->rect, opt->direction);
+ if (subRule.hasDrawable()) {
+ subRule.drawRule(p, ir);
+ } else {
+ btnOpt.rect = ir;
+ baseStyle()->drawPrimitive(PE_IndicatorArrowDown, &btnOpt, p, w);
+ }
+ }
+ }
+ return;
+
+ case CE_PushButtonLabel:
+ if (const QStyleOptionButton *button = qstyleoption_cast<const QStyleOptionButton *>(opt)) {
+ QStyleOptionButton butOpt(*button);
+ rule.configurePalette(&butOpt.palette, QPalette::ButtonText, QPalette::Button);
+ if (rule.hasPosition() && rule.position()->textAlignment != 0) {
+ Qt::Alignment textAlignment = rule.position()->textAlignment;
+ QRect textRect = button->rect;
+ uint tf = Qt::AlignVCenter | Qt::TextShowMnemonic;
+ if (!styleHint(SH_UnderlineShortcut, button, w))
+ tf |= Qt::TextHideMnemonic;
+ if (!button->icon.isNull()) {
+ //Group both icon and text
+ QRect iconRect;
+ QIcon::Mode mode = button->state & State_Enabled ? QIcon::Normal : QIcon::Disabled;
+ if (mode == QIcon::Normal && button->state & State_HasFocus)
+ mode = QIcon::Active;
+ QIcon::State state = QIcon::Off;
+ if (button->state & State_On)
+ state = QIcon::On;
+
+ QPixmap pixmap = button->icon.pixmap(button->iconSize, mode, state);
+ int labelWidth = pixmap.width();
+ int labelHeight = pixmap.height();
+ int iconSpacing = 4;//### 4 is currently hardcoded in QPushButton::sizeHint()
+ int textWidth = button->fontMetrics.boundingRect(opt->rect, tf, button->text).width();
+ if (!button->text.isEmpty())
+ labelWidth += (textWidth + iconSpacing);
+
+ //Determine label alignment:
+ if (textAlignment & Qt::AlignLeft) { /*left*/
+ iconRect = QRect(textRect.x(), textRect.y() + (textRect.height() - labelHeight) / 2,
+ pixmap.width(), pixmap.height());
+ } else if (textAlignment & Qt::AlignHCenter) { /* center */
+ iconRect = QRect(textRect.x() + (textRect.width() - labelWidth) / 2,
+ textRect.y() + (textRect.height() - labelHeight) / 2,
+ pixmap.width(), pixmap.height());
+ } else { /*right*/
+ iconRect = QRect(textRect.x() + textRect.width() - labelWidth,
+ textRect.y() + (textRect.height() - labelHeight) / 2,
+ pixmap.width(), pixmap.height());
+ }
+
+ iconRect = visualRect(button->direction, textRect, iconRect);
+
+ tf |= Qt::AlignLeft; //left align, we adjust the text-rect instead
+
+ if (button->direction == Qt::RightToLeft)
+ textRect.setRight(iconRect.left() - iconSpacing);
+ else
+ textRect.setLeft(iconRect.left() + iconRect.width() + iconSpacing);
+
+ if (button->state & (State_On | State_Sunken))
+ iconRect.translate(pixelMetric(PM_ButtonShiftHorizontal, opt, w),
+ pixelMetric(PM_ButtonShiftVertical, opt, w));
+ p->drawPixmap(iconRect, pixmap);
+ } else {
+ tf |= textAlignment;
+ }
+ if (button->state & (State_On | State_Sunken))
+ textRect.translate(pixelMetric(PM_ButtonShiftHorizontal, opt, w),
+ pixelMetric(PM_ButtonShiftVertical, opt, w));
+
+ if (button->features & QStyleOptionButton::HasMenu) {
+ int indicatorSize = pixelMetric(PM_MenuButtonIndicator, button, w);
+ if (button->direction == Qt::LeftToRight)
+ textRect = textRect.adjusted(0, 0, -indicatorSize, 0);
+ else
+ textRect = textRect.adjusted(indicatorSize, 0, 0, 0);
+ }
+ drawItemText(p, textRect, tf, butOpt.palette, (button->state & State_Enabled),
+ button->text, QPalette::ButtonText);
+ } else {
+ ParentStyle::drawControl(ce, &butOpt, p, w);
+ }
+ }
+ return;
+
+ case CE_RadioButton:
+ case CE_CheckBox:
+ rule.drawRule(p, opt->rect);
+ ParentStyle::drawControl(ce, opt, p, w);
+ return;
+
+ case CE_RadioButtonLabel:
+ case CE_CheckBoxLabel:
+ if (const QStyleOptionButton *btn = qstyleoption_cast<const QStyleOptionButton *>(opt)) {
+ QStyleOptionButton butOpt(*btn);
+ rule.configurePalette(&butOpt.palette, QPalette::ButtonText, QPalette::Button);
+ ParentStyle::drawControl(ce, &butOpt, p, w);
+ }
+ return;
+
+ case CE_Splitter:
+ pe1 = PseudoElement_SplitterHandle;
+ break;
+
+ case CE_ToolBar:
+ if (rule.hasBackground()) {
+ rule.drawBackground(p, opt->rect);
+ }
+ if (rule.hasBorder()) {
+ rule.drawBorder(p, rule.borderRect(opt->rect));
+ } else {
+#ifndef QT_NO_TOOLBAR
+ if (const QStyleOptionToolBar *tb = qstyleoption_cast<const QStyleOptionToolBar *>(opt)) {
+ QStyleOptionToolBar newTb(*tb);
+ newTb.rect = rule.borderRect(opt->rect);
+ baseStyle()->drawControl(ce, &newTb, p, w);
+ }
+#endif // QT_NO_TOOLBAR
+ }
+ return;
+
+ case CE_MenuEmptyArea:
+ case CE_MenuBarEmptyArea:
+ if (rule.hasDrawable()) {
+ return;
+ }
+ break;
+
+ case CE_MenuTearoff:
+ case CE_MenuScroller:
+ if (const QStyleOptionMenuItem *m = qstyleoption_cast<const QStyleOptionMenuItem *>(opt)) {
+ QStyleOptionMenuItem mi(*m);
+ int pe = ce == CE_MenuTearoff ? PseudoElement_MenuTearoff : PseudoElement_MenuScroller;
+ QRenderRule subRule = renderRule(w, opt, pe);
+ mi.rect = subRule.contentsRect(opt->rect);
+ rule.configurePalette(&mi.palette, QPalette::ButtonText, QPalette::Button);
+ subRule.configurePalette(&mi.palette, QPalette::ButtonText, QPalette::Button);
+
+ if (subRule.hasDrawable()) {
+ subRule.drawRule(p, opt->rect);
+ } else {
+ baseStyle()->drawControl(ce, &mi, p, w);
+ }
+ }
+ return;
+
+ case CE_MenuItem:
+ if (const QStyleOptionMenuItem *m = qstyleoption_cast<const QStyleOptionMenuItem *>(opt)) {
+ QStyleOptionMenuItem mi(*m);
+
+ int pseudo = (mi.menuItemType == QStyleOptionMenuItem::Separator) ? PseudoElement_MenuSeparator : PseudoElement_Item;
+ QRenderRule subRule = renderRule(w, opt, pseudo);
+ mi.rect = subRule.contentsRect(opt->rect);
+ rule.configurePalette(&mi.palette, QPalette::ButtonText, QPalette::Button);
+ rule.configurePalette(&mi.palette, QPalette::HighlightedText, QPalette::Highlight);
+ subRule.configurePalette(&mi.palette, QPalette::ButtonText, QPalette::Button);
+ subRule.configurePalette(&mi.palette, QPalette::HighlightedText, QPalette::Highlight);
+ QFont oldFont = p->font();
+ if (subRule.hasFont)
+ p->setFont(subRule.font.resolve(p->font()));
+
+ // We fall back to drawing with the style sheet code whenever at least one of the
+ // items are styled in an incompatible way, such as having a background image.
+ QRenderRule allRules = renderRule(w, PseudoElement_Item, PseudoClass_Any);
+
+ if ((pseudo == PseudoElement_MenuSeparator) && subRule.hasDrawable()) {
+ subRule.drawRule(p, opt->rect);
+ } else if ((pseudo == PseudoElement_Item)
+ && (allRules.hasBox() || allRules.hasBorder()
+ || (allRules.background() && !allRules.background()->pixmap.isNull()))) {
+ subRule.drawRule(p, opt->rect);
+ if (subRule.hasBackground()) {
+ mi.palette.setBrush(QPalette::Highlight, Qt::NoBrush);
+ mi.palette.setBrush(QPalette::Button, Qt::NoBrush);
+ } else {
+ mi.palette.setBrush(QPalette::Highlight, mi.palette.brush(QPalette::Button));
+ }
+ mi.palette.setBrush(QPalette::HighlightedText, mi.palette.brush(QPalette::ButtonText));
+
+ bool checkable = mi.checkType != QStyleOptionMenuItem::NotCheckable;
+ bool checked = checkable ? mi.checked : false;
+
+ bool dis = !(opt->state & QStyle::State_Enabled),
+ act = opt->state & QStyle::State_Selected;
+
+ if (!mi.icon.isNull()) {
+ QIcon::Mode mode = dis ? QIcon::Disabled : QIcon::Normal;
+ if (act && !dis)
+ mode = QIcon::Active;
+ QPixmap pixmap;
+ if (checked)
+ pixmap = mi.icon.pixmap(pixelMetric(PM_SmallIconSize), mode, QIcon::On);
+ else
+ pixmap = mi.icon.pixmap(pixelMetric(PM_SmallIconSize), mode);
+ int pixw = pixmap.width();
+ int pixh = pixmap.height();
+ QRenderRule iconRule = renderRule(w, opt, PseudoElement_MenuIcon);
+ if (!iconRule.hasGeometry()) {
+ iconRule.geo = new QStyleSheetGeometryData(pixw, pixh, pixw, pixh, -1, -1);
+ } else {
+ iconRule.geo->width = pixw;
+ iconRule.geo->height = pixh;
+ }
+ QRect iconRect = positionRect(w, subRule, iconRule, PseudoElement_MenuIcon, opt->rect, opt->direction);
+ iconRule.drawRule(p, iconRect);
+ QRect pmr(0, 0, pixw, pixh);
+ pmr.moveCenter(iconRect.center());
+ p->drawPixmap(pmr.topLeft(), pixmap);
+ } else if (checkable) {
+ QRenderRule subSubRule = renderRule(w, opt, PseudoElement_MenuCheckMark);
+ if (subSubRule.hasDrawable() || checked) {
+ QStyleOptionMenuItem newMi = mi;
+ newMi.rect = positionRect(w, subRule, subSubRule, PseudoElement_MenuCheckMark, opt->rect, opt->direction);
+ drawPrimitive(PE_IndicatorMenuCheckMark, &newMi, p, w);
+ }
+ }
+
+ QRect textRect = subRule.contentsRect(opt->rect);
+ textRect.setWidth(textRect.width() - mi.tabWidth);
+ QString s = mi.text;
+ p->setPen(mi.palette.buttonText().color());
+ if (!s.isEmpty()) {
+ int text_flags = Qt::AlignLeft | Qt::AlignVCenter | Qt::TextShowMnemonic | Qt::TextDontClip | Qt::TextSingleLine;
+ if (!styleHint(SH_UnderlineShortcut, &mi, w))
+ text_flags |= Qt::TextHideMnemonic;
+ int t = s.indexOf(QLatin1Char('\t'));
+ if (t >= 0) {
+ QRect vShortcutRect = visualRect(opt->direction, mi.rect,
+ QRect(textRect.topRight(), QPoint(mi.rect.right(), textRect.bottom())));
+ p->drawText(vShortcutRect, text_flags, s.mid(t + 1));
+ s = s.left(t);
+ }
+ p->drawText(textRect, text_flags, s.left(t));
+ }
+
+ if (mi.menuItemType == QStyleOptionMenuItem::SubMenu) {// draw sub menu arrow
+ PrimitiveElement arrow = (opt->direction == Qt::RightToLeft) ? PE_IndicatorArrowLeft : PE_IndicatorArrowRight;
+ QRenderRule subRule2 = renderRule(w, opt, PseudoElement_MenuRightArrow);
+ mi.rect = positionRect(w, subRule, subRule2, PseudoElement_MenuRightArrow, opt->rect, mi.direction);
+ drawPrimitive(arrow, &mi, p, w);
+ }
+ } else if (hasStyleRule(w, PseudoElement_MenuCheckMark)) {
+ QWindowsStyle::drawControl(ce, &mi, p, w);
+ } else {
+ if (rule.hasDrawable() && !subRule.hasDrawable() && !(opt->state & QStyle::State_Selected)) {
+ mi.palette.setColor(QPalette::Window, Qt::transparent);
+ mi.palette.setColor(QPalette::Button, Qt::transparent);
+ }
+ if (rule.baseStyleCanDraw() && subRule.baseStyleCanDraw()) {
+ baseStyle()->drawControl(ce, &mi, p, w);
+ } else {
+ ParentStyle::drawControl(ce, &mi, p, w);
+ }
+ }
+
+ if (subRule.hasFont)
+ p->setFont(oldFont);
+
+ return;
+ }
+ return;
+
+ case CE_MenuBarItem:
+ if (const QStyleOptionMenuItem *m = qstyleoption_cast<const QStyleOptionMenuItem *>(opt)) {
+ QStyleOptionMenuItem mi(*m);
+ QRenderRule subRule = renderRule(w, opt, PseudoElement_Item);
+ mi.rect = subRule.contentsRect(opt->rect);
+ rule.configurePalette(&mi.palette, QPalette::ButtonText, QPalette::Button);
+ subRule.configurePalette(&mi.palette, QPalette::ButtonText, QPalette::Button);
+
+ if (subRule.hasDrawable()) {
+ subRule.drawRule(p, opt->rect);
+ QCommonStyle::drawControl(ce, &mi, p, w);
+ } else {
+ baseStyle()->drawControl(ce, &mi, p, w);
+ }
+ }
+ return;
+
+#ifndef QT_NO_COMBOBOX
+ case CE_ComboBoxLabel:
+ if (!rule.hasBox())
+ break;
+ if (const QStyleOptionComboBox *cb = qstyleoption_cast<const QStyleOptionComboBox *>(opt)) {
+ QRect editRect = subControlRect(CC_ComboBox, cb, SC_ComboBoxEditField, w);
+ p->save();
+ p->setClipRect(editRect);
+ if (!cb->currentIcon.isNull()) {
+ int spacing = rule.hasBox() ? rule.box()->spacing : -1;
+ if (spacing == -1)
+ spacing = 6;
+ QIcon::Mode mode = cb->state & State_Enabled ? QIcon::Normal : QIcon::Disabled;
+ QPixmap pixmap = cb->currentIcon.pixmap(cb->iconSize, mode);
+ QRect iconRect(editRect);
+ iconRect.setWidth(cb->iconSize.width());
+ iconRect = alignedRect(QApplication::layoutDirection(),
+ Qt::AlignLeft | Qt::AlignVCenter,
+ iconRect.size(), editRect);
+ drawItemPixmap(p, iconRect, Qt::AlignCenter, pixmap);
+
+ if (cb->direction == Qt::RightToLeft)
+ editRect.translate(-spacing - cb->iconSize.width(), 0);
+ else
+ editRect.translate(cb->iconSize.width() + spacing, 0);
+ }
+ if (!cb->currentText.isEmpty() && !cb->editable) {
+ drawItemText(p, editRect.adjusted(0, 0, 0, 0), Qt::AlignLeft | Qt::AlignVCenter, cb->palette,
+ cb->state & State_Enabled, cb->currentText);
+ }
+ p->restore();
+ return;
+ }
+ break;
+#endif // QT_NO_COMBOBOX
+
+ case CE_Header:
+ if (hasStyleRule(w, PseudoElement_HeaderViewUpArrow)
+ || hasStyleRule(w, PseudoElement_HeaderViewDownArrow)) {
+ ParentStyle::drawControl(ce, opt, p, w);
+ return;
+ }
+ if(hasStyleRule(w, PseudoElement_HeaderViewSection)) {
+ QRenderRule subRule = renderRule(w, opt, PseudoElement_HeaderViewSection);
+ if (!subRule.hasNativeBorder() || !subRule.baseStyleCanDraw()
+ || subRule.hasBackground() || subRule.hasPalette()) {
+ ParentStyle::drawControl(ce, opt, p, w);
+ return;
+ }
+ }
+ break;
+ case CE_HeaderSection:
+ if (const QStyleOptionHeader *header = qstyleoption_cast<const QStyleOptionHeader *>(opt)) {
+ QRenderRule subRule = renderRule(w, opt, PseudoElement_HeaderViewSection);
+ if (subRule.hasNativeBorder()) {
+ QStyleOptionHeader hdr(*header);
+ subRule.configurePalette(&hdr.palette, QPalette::ButtonText, QPalette::Button);
+
+ if (subRule.baseStyleCanDraw()) {
+ baseStyle()->drawControl(CE_HeaderSection, &hdr, p, w);
+ } else {
+ QWindowsStyle::drawControl(CE_HeaderSection, &hdr, p, w);
+ }
+ } else {
+ subRule.drawRule(p, opt->rect);
+ }
+ return;
+ }
+ break;
+
+ case CE_HeaderLabel:
+ if (const QStyleOptionHeader *header = qstyleoption_cast<const QStyleOptionHeader *>(opt)) {
+ QStyleOptionHeader hdr(*header);
+ QRenderRule subRule = renderRule(w, opt, PseudoElement_HeaderViewSection);
+ subRule.configurePalette(&hdr.palette, QPalette::ButtonText, QPalette::Button);
+ QFont oldFont = p->font();
+ if (subRule.hasFont)
+ p->setFont(subRule.font.resolve(p->font()));
+ baseStyle()->drawControl(ce, &hdr, p, w);
+ if (subRule.hasFont)
+ p->setFont(oldFont);
+ return;
+ }
+ break;
+
+ case CE_HeaderEmptyArea:
+ if (rule.hasDrawable()) {
+ return;
+ }
+ break;
+
+ case CE_ProgressBar:
+ QWindowsStyle::drawControl(ce, opt, p, w);
+ return;
+
+ case CE_ProgressBarGroove:
+ if (!rule.hasNativeBorder()) {
+ rule.drawRule(p, rule.boxRect(opt->rect, Margin));
+ return;
+ }
+ break;
+
+ case CE_ProgressBarContents: {
+ QRenderRule subRule = renderRule(w, opt, PseudoElement_ProgressBarChunk);
+ if (subRule.hasDrawable()) {
+ if (const QStyleOptionProgressBarV2 *pb = qstyleoption_cast<const QStyleOptionProgressBarV2 *>(opt)) {
+ p->save();
+ p->setClipRect(pb->rect);
+
+ qint64 minimum = qint64(pb->minimum);
+ qint64 maximum = qint64(pb->maximum);
+ qint64 progress = qint64(pb->progress);
+ bool vertical = (pb->orientation == Qt::Vertical);
+ bool inverted = pb->invertedAppearance;
+
+ QTransform m;
+ QRect rect = pb->rect;
+ if (vertical) {
+ rect = QRect(rect.y(), rect.x(), rect.height(), rect.width());
+ m.rotate(90);
+ m.translate(0, -(rect.height() + rect.y()*2));
+ }
+
+ bool reverse = ((!vertical && (pb->direction == Qt::RightToLeft)) || vertical);
+ if (inverted)
+ reverse = !reverse;
+ const bool indeterminate = pb->minimum == pb->maximum;
+ qreal fillRatio = indeterminate ? 0.50 : qreal(progress - minimum)/(maximum - minimum);
+ int fillWidth = int(rect.width() * fillRatio);
+ int chunkWidth = fillWidth;
+ if (subRule.hasContentsSize()) {
+ QSize sz = subRule.size();
+ chunkWidth = (opt->state & QStyle::State_Horizontal) ? sz.width() : sz.height();
+ }
+
+ QRect r = rect;
+ if (pb->minimum == 0 && pb->maximum == 0) {
+ Q_D(const QWindowsStyle);
+ int chunkCount = fillWidth/chunkWidth;
+ int offset = (d->animateStep*8%rect.width());
+ int x = reverse ? r.left() + r.width() - offset - chunkWidth : r.x() + offset;
+ while (chunkCount > 0) {
+ r.setRect(x, rect.y(), chunkWidth, rect.height());
+ r = m.mapRect(QRectF(r)).toRect();
+ subRule.drawRule(p, r);
+ x += reverse ? -chunkWidth : chunkWidth;
+ if (reverse ? x < rect.left() : x > rect.right())
+ break;
+ --chunkCount;
+ }
+
+ r = rect;
+ x = reverse ? r.right() - (r.left() - x - chunkWidth)
+ : r.left() + (x - r.right() - chunkWidth);
+ while (chunkCount > 0) {
+ r.setRect(x, rect.y(), chunkWidth, rect.height());
+ r = m.mapRect(QRectF(r)).toRect();
+ subRule.drawRule(p, r);
+ x += reverse ? -chunkWidth : chunkWidth;
+ --chunkCount;
+ };
+ } else {
+ int x = reverse ? r.left() + r.width() - chunkWidth : r.x();
+
+ for (int i = 0; i < ceil(qreal(fillWidth)/chunkWidth); ++i) {
+ r.setRect(x, rect.y(), chunkWidth, rect.height());
+ r = m.mapRect(QRectF(r)).toRect();
+ subRule.drawRule(p, r);
+ x += reverse ? -chunkWidth : chunkWidth;
+ }
+ }
+
+ p->restore();
+ return;
+ }
+ }
+ }
+ break;
+
+ case CE_ProgressBarLabel:
+ if (const QStyleOptionProgressBarV2 *pb = qstyleoption_cast<const QStyleOptionProgressBarV2 *>(opt)) {
+ if (rule.hasBox() || rule.hasBorder() || hasStyleRule(w, PseudoElement_ProgressBarChunk)) {
+ drawItemText(p, pb->rect, pb->textAlignment | Qt::TextSingleLine, pb->palette,
+ pb->state & State_Enabled, pb->text, QPalette::Text);
+ } else {
+ QStyleOptionProgressBarV2 pbCopy(*pb);
+ rule.configurePalette(&pbCopy.palette, QPalette::HighlightedText, QPalette::Highlight);
+ baseStyle()->drawControl(ce, &pbCopy, p, w);
+ }
+ return;
+ }
+ break;
+
+ case CE_SizeGrip:
+ if (const QStyleOptionSizeGrip *sgOpt = qstyleoption_cast<const QStyleOptionSizeGrip *>(opt)) {
+ if (rule.hasDrawable()) {
+ rule.drawFrame(p, opt->rect);
+ p->save();
+ switch (sgOpt->corner) {
+ case Qt::BottomRightCorner: break;
+ case Qt::BottomLeftCorner: p->rotate(90); break;
+ case Qt::TopLeftCorner: p->rotate(180); break;
+ case Qt::TopRightCorner: p->rotate(270); break;
+ default: break;
+ }
+ rule.drawImage(p, opt->rect);
+ p->restore();
+ } else {
+ QStyleOptionSizeGrip sg(*sgOpt);
+ sg.rect = rule.contentsRect(opt->rect);
+ baseStyle()->drawControl(CE_SizeGrip, &sg, p, w);
+ }
+ return;
+ }
+ break;
+
+ case CE_ToolBoxTab:
+ QWindowsStyle::drawControl(ce, opt, p, w);
+ return;
+
+ case CE_ToolBoxTabShape: {
+ QRenderRule subRule = renderRule(w, opt, PseudoElement_ToolBoxTab);
+ if (subRule.hasDrawable()) {
+ subRule.drawRule(p, opt->rect);
+ return;
+ }
+ }
+ break;
+
+ case CE_ToolBoxTabLabel:
+ if (const QStyleOptionToolBox *box = qstyleoption_cast<const QStyleOptionToolBox *>(opt)) {
+ QStyleOptionToolBox boxCopy(*box);
+ QRenderRule subRule = renderRule(w, opt, PseudoElement_ToolBoxTab);
+ subRule.configurePalette(&boxCopy.palette, QPalette::ButtonText, QPalette::Button);
+ QFont oldFont = p->font();
+ if (subRule.hasFont)
+ p->setFont(subRule.font);
+ boxCopy.rect = subRule.contentsRect(opt->rect);
+ QWindowsStyle::drawControl(ce, &boxCopy, p , w);
+ if (subRule.hasFont)
+ p->setFont(oldFont);
+ return;
+ }
+ break;
+
+ case CE_ScrollBarAddPage:
+ pe1 = PseudoElement_ScrollBarAddPage;
+ break;
+
+ case CE_ScrollBarSubPage:
+ pe1 = PseudoElement_ScrollBarSubPage;
+ break;
+
+ case CE_ScrollBarAddLine:
+ pe1 = PseudoElement_ScrollBarAddLine;
+ pe2 = (opt->state & QStyle::State_Horizontal) ? PseudoElement_ScrollBarRightArrow : PseudoElement_ScrollBarDownArrow;
+ fallback = true;
+ break;
+
+ case CE_ScrollBarSubLine:
+ pe1 = PseudoElement_ScrollBarSubLine;
+ pe2 = (opt->state & QStyle::State_Horizontal) ? PseudoElement_ScrollBarLeftArrow : PseudoElement_ScrollBarUpArrow;
+ fallback = true;
+ break;
+
+ case CE_ScrollBarFirst:
+ pe1 = PseudoElement_ScrollBarFirst;
+ break;
+
+ case CE_ScrollBarLast:
+ pe1 = PseudoElement_ScrollBarLast;
+ break;
+
+ case CE_ScrollBarSlider:
+ pe1 = PseudoElement_ScrollBarSlider;
+ fallback = true;
+ break;
+
+#ifndef QT_NO_ITEMVIEWS
+ case CE_ItemViewItem:
+ if (const QStyleOptionViewItemV4 *vopt = qstyleoption_cast<const QStyleOptionViewItemV4 *>(opt)) {
+ QRenderRule subRule = renderRule(w, opt, PseudoElement_ViewItem);
+ if (subRule.hasDrawable() || hasStyleRule(w, PseudoElement_Indicator)) {
+ QStyleOptionViewItemV4 optCopy(*vopt);
+ subRule.configurePalette(&optCopy.palette, vopt->state & QStyle::State_Selected ? QPalette::HighlightedText : QPalette::Text,
+ vopt->state & QStyle::State_Selected ? QPalette::Highlight : QPalette::Base);
+ QWindowsStyle::drawControl(ce, &optCopy, p, w);
+ } else {
+ QStyleOptionViewItemV4 voptCopy(*vopt);
+ subRule.configurePalette(&voptCopy.palette, QPalette::Text, QPalette::NoRole);
+ baseStyle()->drawControl(ce, &voptCopy, p, w);
+ }
+ return;
+ }
+ break;
+#endif // QT_NO_ITEMVIEWS
+
+#ifndef QT_NO_TABBAR
+ case CE_TabBarTab:
+ if (hasStyleRule(w, PseudoElement_TabBarTab)) {
+ QWindowsStyle::drawControl(ce, opt, p, w);
+ return;
+ }
+ break;
+
+ case CE_TabBarTabLabel:
+ case CE_TabBarTabShape:
+ if (const QStyleOptionTab *tab = qstyleoption_cast<const QStyleOptionTab *>(opt)) {
+ QStyleOptionTabV3 tabCopy(*tab);
+ QRenderRule subRule = renderRule(w, opt, PseudoElement_TabBarTab);
+ QRect r = positionRect(w, subRule, PseudoElement_TabBarTab, opt->rect, opt->direction);
+ if (ce == CE_TabBarTabShape && subRule.hasDrawable()) {
+ subRule.drawRule(p, r);
+ return;
+ }
+ subRule.configurePalette(&tabCopy.palette, QPalette::WindowText, QPalette::Window);
+ QFont oldFont = p->font();
+ if (subRule.hasFont)
+ p->setFont(subRule.font);
+ if (subRule.hasBox()) {
+ tabCopy.rect = ce == CE_TabBarTabShape ? subRule.borderRect(r)
+ : subRule.contentsRect(r);
+ QWindowsStyle::drawControl(ce, &tabCopy, p, w);
+ } else {
+ baseStyle()->drawControl(ce, &tabCopy, p, w);
+ }
+ if (subRule.hasFont)
+ p->setFont(oldFont);
+
+ return;
+ }
+ break;
+#endif // QT_NO_TABBAR
+
+ case CE_ColumnViewGrip:
+ if (rule.hasDrawable()) {
+ rule.drawRule(p, opt->rect);
+ return;
+ }
+ break;
+
+ case CE_DockWidgetTitle:
+ if (const QStyleOptionDockWidgetV2 *dwOpt = qstyleoption_cast<const QStyleOptionDockWidgetV2 *>(opt)) {
+ QRenderRule subRule = renderRule(w, opt, PseudoElement_DockWidgetTitle);
+ if (!subRule.hasDrawable() && !subRule.hasPosition())
+ break;
+ if (subRule.hasDrawable()) {
+ subRule.drawRule(p, opt->rect);
+ } else {
+ QStyleOptionDockWidgetV2 dwCopy(*dwOpt);
+ dwCopy.title = QString();
+ baseStyle()->drawControl(ce, &dwCopy, p, w);
+ }
+
+ if (!dwOpt->title.isEmpty()) {
+ QRect r = opt->rect;
+ if (dwOpt->verticalTitleBar) {
+ QSize s = r.size();
+ s.transpose();
+ r.setSize(s);
+
+ p->save();
+ p->translate(r.left(), r.top() + r.width());
+ p->rotate(-90);
+ p->translate(-r.left(), -r.top());
+ }
+
+ Qt::Alignment alignment = 0;
+ if (subRule.hasPosition())
+ alignment = subRule.position()->textAlignment;
+ if (alignment == 0)
+ alignment = Qt::AlignLeft;
+ drawItemText(p, subRule.contentsRect(opt->rect),
+ alignment | Qt::TextShowMnemonic, dwOpt->palette,
+ dwOpt->state & State_Enabled, dwOpt->title,
+ QPalette::WindowText);
+
+ if (dwOpt->verticalTitleBar)
+ p->restore();
+ }
+
+ return;
+ }
+ break;
+ case CE_ShapedFrame:
+ if (const QStyleOptionFrame *frm = qstyleoption_cast<const QStyleOptionFrame *>(opt)) {
+ if (rule.hasNativeBorder()) {
+ QStyleOptionFrameV3 frmOpt(*frm);
+ rule.configurePalette(&frmOpt.palette, QPalette::Text, QPalette::Base);
+ frmOpt.rect = rule.borderRect(frmOpt.rect);
+ baseStyle()->drawControl(ce, &frmOpt, p, w);
+ } else {
+ rule.drawBorder(p, rule.borderRect(opt->rect));
+ }
+ }
+ return;
+
+
+ default:
+ break;
+ }
+
+ if (pe1 != PseudoElement_None) {
+ QRenderRule subRule = renderRule(w, opt, pe1);
+ if (subRule.bg != 0 || subRule.hasDrawable()) {
+ //We test subRule.bg dirrectly because hasBackground() would return false for background:none.
+ //But we still don't want the default drawning in that case (example for QScrollBar::add-page) (task 198926)
+ subRule.drawRule(p, opt->rect);
+ } else if (fallback) {
+ QWindowsStyle::drawControl(ce, opt, p, w);
+ pe2 = PseudoElement_None;
+ } else {
+ baseStyle()->drawControl(ce, opt, p, w);
+ }
+ if (pe2 != PseudoElement_None) {
+ QRenderRule subSubRule = renderRule(w, opt, pe2);
+ QRect r = positionRect(w, subRule, subSubRule, pe2, opt->rect, opt->direction);
+ subSubRule.drawRule(p, r);
+ }
+ return;
+ }
+
+ baseStyle()->drawControl(ce, opt, p, w);
+}
+
+void QStyleSheetStyle::drawItemPixmap(QPainter *p, const QRect &rect, int alignment, const
+ QPixmap &pixmap) const
+{
+ baseStyle()->drawItemPixmap(p, rect, alignment, pixmap);
+}
+
+void QStyleSheetStyle::drawItemText(QPainter *painter, const QRect& rect, int alignment, const QPalette &pal,
+ bool enabled, const QString& text, QPalette::ColorRole textRole) const
+{
+ baseStyle()->drawItemText(painter, rect, alignment, pal, enabled, text, textRole);
+}
+
+void QStyleSheetStyle::drawPrimitive(PrimitiveElement pe, const QStyleOption *opt, QPainter *p,
+ const QWidget *w) const
+{
+ RECURSION_GUARD(baseStyle()->drawPrimitive(pe, opt, p, w); return)
+
+ int pseudoElement = PseudoElement_None;
+ QRenderRule rule = renderRule(w, opt);
+ QRect rect = opt->rect;
+
+ switch (pe) {
+ case PE_PanelStatusBar:
+ if (rule.hasDrawable()) {
+ rule.drawRule(p, opt->rect);
+ return;
+ }
+ break;
+
+ case PE_FrameStatusBar: {
+ QRenderRule subRule = renderRule(w->parentWidget(), opt, PseudoElement_Item);
+ if (subRule.hasDrawable()) {
+ subRule.drawRule(p, opt->rect);
+ return;
+ }
+ break;
+ }
+
+ case PE_IndicatorArrowDown:
+ pseudoElement = PseudoElement_DownArrow;
+ break;
+
+ case PE_IndicatorRadioButton:
+ pseudoElement = PseudoElement_ExclusiveIndicator;
+ break;
+
+ case PE_IndicatorViewItemCheck:
+ pseudoElement = PseudoElement_ViewItemIndicator;
+ break;
+
+ case PE_IndicatorCheckBox:
+ pseudoElement = PseudoElement_Indicator;
+ break;
+
+ case PE_IndicatorHeaderArrow:
+ if (const QStyleOptionHeader *hdr = qstyleoption_cast<const QStyleOptionHeader *>(opt)) {
+ pseudoElement = hdr->sortIndicator == QStyleOptionHeader::SortUp
+ ? PseudoElement_HeaderViewUpArrow
+ : PseudoElement_HeaderViewDownArrow;
+ }
+ break;
+
+ case PE_PanelButtonTool:
+ case PE_PanelButtonCommand:
+ if (qobject_cast<const QAbstractButton *>(w) && rule.hasBackground() && rule.hasNativeBorder()) {
+ //the window style will draw the borders
+ ParentStyle::drawPrimitive(pe, opt, p, w);
+ if (!rule.background()->pixmap.isNull() || rule.hasImage()) {
+ rule.drawRule(p, rule.boxRect(opt->rect, QRenderRule::Margin).adjusted(1,1,-1,-1));
+ }
+ return;
+ }
+ if (!rule.hasNativeBorder()) {
+ rule.drawRule(p, rule.boxRect(opt->rect, QRenderRule::Margin));
+ return;
+ }
+ break;
+
+ case PE_IndicatorButtonDropDown: {
+ QRenderRule subRule = renderRule(w, opt, PseudoElement_ToolButtonMenu);
+ if (!subRule.hasNativeBorder()) {
+ rule.drawBorder(p, opt->rect);
+ return;
+ }
+ break;
+ }
+
+ case PE_FrameDefaultButton:
+ if (rule.hasNativeBorder()) {
+ if (rule.baseStyleCanDraw())
+ break;
+ QWindowsStyle::drawPrimitive(pe, opt, p, w);
+ }
+ return;
+
+ case PE_FrameWindow:
+ case PE_FrameDockWidget:
+ case PE_Frame:
+ if (const QStyleOptionFrame *frm = qstyleoption_cast<const QStyleOptionFrame *>(opt)) {
+ if (rule.hasNativeBorder()) {
+ QStyleOptionFrameV2 frmOpt(*frm);
+ rule.configurePalette(&frmOpt.palette, QPalette::Text, QPalette::Base);
+ if (!qstyleoption_cast<const QStyleOptionFrameV3 *>(opt)) //if it comes from CE_ShapedFrame, the margins are already sustracted
+ frmOpt.rect = rule.borderRect(frmOpt.rect);
+ baseStyle()->drawPrimitive(pe, &frmOpt, p, w);
+ } else {
+ rule.drawBorder(p, rule.borderRect(opt->rect));
+ }
+ }
+ return;
+
+ case PE_PanelLineEdit:
+ if (const QStyleOptionFrame *frm = qstyleoption_cast<const QStyleOptionFrame *>(opt)) {
+#ifndef QT_NO_SPINBOX
+ if (w && qobject_cast<const QAbstractSpinBox *>(w->parentWidget())) {
+ QRenderRule spinboxRule = renderRule(w->parentWidget(), opt);
+ if (!spinboxRule.hasNativeBorder() || !spinboxRule.baseStyleCanDraw())
+ return;
+ }
+#endif
+ if (rule.hasNativeBorder()) {
+ QStyleOptionFrame frmOpt(*frm);
+ rule.configurePalette(&frmOpt.palette, QPalette::Text, QPalette::Base);
+ frmOpt.rect = rule.borderRect(frmOpt.rect);
+ if (rule.baseStyleCanDraw()) {
+ rule.drawBackgroundImage(p, opt->rect);
+ baseStyle()->drawPrimitive(pe, &frmOpt, p, w);
+ } else {
+ rule.drawBackground(p, opt->rect);
+ if (frmOpt.lineWidth > 0)
+ baseStyle()->drawPrimitive(PE_FrameLineEdit, &frmOpt, p, w);
+ }
+ } else {
+ rule.drawRule(p, opt->rect);
+ }
+ }
+ return;
+
+ case PE_Widget:
+ if (!rule.hasBackground())
+ break;
+
+#ifndef QT_NO_SCROLLAREA
+ if (const QAbstractScrollArea *sa = qobject_cast<const QAbstractScrollArea *>(w)) {
+ const QAbstractScrollAreaPrivate *sap = sa->d_func();
+ rule.drawBackground(p, opt->rect, sap->contentsOffset());
+ } else
+#endif
+ {
+ rule.drawBackground(p, opt->rect);
+ }
+
+ return;
+
+ case PE_FrameMenu:
+ case PE_PanelMenuBar:
+ if (!rule.hasNativeBorder()) {
+ rule.drawBorder(p, rule.borderRect(opt->rect));
+ return;
+ }
+ break;
+
+ case PE_IndicatorToolBarSeparator:
+ case PE_IndicatorToolBarHandle: {
+ PseudoElement ps = pe == PE_IndicatorToolBarHandle ? PseudoElement_ToolBarHandle : PseudoElement_ToolBarSeparator;
+ QRenderRule subRule = renderRule(w, opt, ps);
+ if (subRule.hasDrawable()) {
+ subRule.drawRule(p, opt->rect);
+ return;
+ }
+ }
+ break;
+
+ case PE_IndicatorMenuCheckMark:
+ pseudoElement = PseudoElement_MenuCheckMark;
+ break;
+
+ case PE_IndicatorArrowLeft:
+ pseudoElement = PseudoElement_LeftArrow;
+ break;
+
+ case PE_IndicatorArrowRight:
+ pseudoElement = PseudoElement_RightArrow;
+ break;
+
+ case PE_IndicatorColumnViewArrow:
+ if (const QStyleOptionViewItem *viewOpt = qstyleoption_cast<const QStyleOptionViewItem *>(opt)) {
+ bool reverse = (viewOpt->direction == Qt::RightToLeft);
+ pseudoElement = reverse ? PseudoElement_LeftArrow : PseudoElement_RightArrow;
+ } else {
+ pseudoElement = PseudoElement_RightArrow;
+ }
+ break;
+
+ case PE_IndicatorBranch:
+ if (const QStyleOptionViewItemV2 *v2 = qstyleoption_cast<const QStyleOptionViewItemV2 *>(opt)) {
+ QRenderRule subRule = renderRule(w, opt, PseudoElement_TreeViewBranch);
+ if (subRule.hasDrawable()) {
+ if ((v2->state & QStyle::State_Selected) && v2->showDecorationSelected)
+ p->fillRect(v2->rect, v2->palette.highlight());
+ else if (v2->features & QStyleOptionViewItemV2::Alternate)
+ p->fillRect(v2->rect, v2->palette.alternateBase());
+ subRule.drawRule(p, opt->rect);
+ } else {
+ QStyleOptionViewItemV2 v2Copy(*v2);
+ if (v2->showDecorationSelected) {
+ QRenderRule subRule2 = renderRule(w, opt, PseudoElement_ViewItem);
+ if (v2->state & QStyle::State_Selected) {
+ subRule2.configurePalette(&v2Copy.palette, QPalette::NoRole, QPalette::Highlight);
+ } else if (v2->features & QStyleOptionViewItemV2::Alternate) {
+ subRule2.configurePalette(&v2Copy.palette, QPalette::NoRole, QPalette::AlternateBase);
+ } else if (subRule2.hasBackground()) {
+ p->fillRect(v2->rect, subRule2.background()->brush);
+ }
+ } else if (v2->features & QStyleOptionViewItemV2::Alternate) {
+ quint64 pc = v2->state & QStyle::State_Enabled ? PseudoClass_Enabled : PseudoClass_Disabled;
+ pc |= PseudoClass_Alternate;
+ QRenderRule subRule2 = renderRule(w, PseudoElement_ViewItem, pc);
+ subRule2.configurePalette(&v2Copy.palette, QPalette::NoRole, QPalette::AlternateBase);
+ }
+ baseStyle()->drawPrimitive(pe, &v2Copy, p, w);
+ }
+ }
+ return;
+
+ case PE_PanelTipLabel:
+ if (!rule.hasDrawable())
+ break;
+
+ if (const QStyleOptionFrame *frmOpt = qstyleoption_cast<const QStyleOptionFrame *>(opt)) {
+ if (rule.hasNativeBorder()) {
+ rule.drawBackground(p, opt->rect);
+ QStyleOptionFrame optCopy(*frmOpt);
+ optCopy.rect = rule.borderRect(opt->rect);
+ optCopy.palette.setBrush(QPalette::Window, Qt::NoBrush); // oh dear
+ baseStyle()->drawPrimitive(pe, &optCopy, p, w);
+ } else {
+ rule.drawRule(p, opt->rect);
+ }
+ }
+ return;
+
+ case PE_FrameGroupBox:
+ if (rule.hasNativeBorder())
+ break;
+ rule.drawBorder(p, opt->rect);
+ return;
+
+#ifndef QT_NO_TABWIDGET
+ case PE_FrameTabWidget:
+ if (const QStyleOptionTabWidgetFrame *frm = qstyleoption_cast<const QStyleOptionTabWidgetFrame *>(opt)) {
+ QRenderRule subRule = renderRule(w, opt, PseudoElement_TabWidgetPane);
+ if (subRule.hasNativeBorder()) {
+ subRule.drawBackground(p, opt->rect);
+ QStyleOptionTabWidgetFrame frmCopy(*frm);
+ subRule.configurePalette(&frmCopy.palette, QPalette::WindowText, QPalette::Window);
+ baseStyle()->drawPrimitive(pe, &frmCopy, p, w);
+ } else {
+ subRule.drawRule(p, opt->rect);
+ }
+ return;
+ }
+ break;
+#endif // QT_NO_TABWIDGET
+
+ case PE_IndicatorProgressChunk:
+ pseudoElement = PseudoElement_ProgressBarChunk;
+ break;
+
+ case PE_IndicatorTabTear:
+ pseudoElement = PseudoElement_TabBarTear;
+ break;
+
+ case PE_FrameFocusRect:
+ if (!rule.hasNativeOutline()) {
+ rule.drawOutline(p, opt->rect);
+ return;
+ }
+ break;
+
+ case PE_IndicatorDockWidgetResizeHandle:
+ pseudoElement = PseudoElement_DockWidgetSeparator;
+ break;
+
+ case PE_PanelItemViewItem:
+ if (!styleHint(SH_ItemView_ShowDecorationSelected, opt, w)) {
+ rect = subElementRect(QStyle::SE_ItemViewItemText, opt, w)
+ | subElementRect(QStyle::SE_ItemViewItemDecoration, opt, w)
+ | subElementRect(QStyle::SE_ItemViewItemCheckIndicator, opt, w);
+ }
+ pseudoElement = PseudoElement_ViewItem;
+ break;
+
+ case PE_PanelItemViewRow:
+ ParentStyle::drawPrimitive(pe, opt, p, w);
+ if (!styleHint(SH_ItemView_ShowDecorationSelected, opt, w))
+ return;
+ pseudoElement = PseudoElement_ViewItem;
+ break;
+
+ case PE_PanelScrollAreaCorner:
+ pseudoElement = PseudoElement_ScrollAreaCorner;
+ break;
+
+ default:
+ break;
+ }
+
+ if (pseudoElement != PseudoElement_None) {
+ QRenderRule subRule = renderRule(w, opt, pseudoElement);
+ if (subRule.hasDrawable()) {
+ subRule.drawRule(p, rect);
+ } else {
+ baseStyle()->drawPrimitive(pe, opt, p, w);
+ }
+ } else {
+ baseStyle()->drawPrimitive(pe, opt, p, w);
+ }
+}
+
+QPixmap QStyleSheetStyle::generatedIconPixmap(QIcon::Mode iconMode, const QPixmap& pixmap,
+ const QStyleOption *option) const
+{
+ return baseStyle()->generatedIconPixmap(iconMode, pixmap, option);
+}
+
+QStyle::SubControl QStyleSheetStyle::hitTestComplexControl(ComplexControl cc, const QStyleOptionComplex *opt,
+ const QPoint &pt, const QWidget *w) const
+{
+ RECURSION_GUARD(return baseStyle()->hitTestComplexControl(cc, opt, pt, w))
+ switch (cc) {
+ case CC_TitleBar:
+ if (const QStyleOptionTitleBar *tb = qstyleoption_cast<const QStyleOptionTitleBar *>(opt)) {
+ QRenderRule rule = renderRule(w, opt, PseudoElement_TitleBar);
+ if (rule.hasDrawable() || rule.hasBox() || rule.hasBorder()) {
+ QHash<QStyle::SubControl, QRect> layout = titleBarLayout(w, tb);
+ QRect r;
+ QStyle::SubControl sc = QStyle::SC_None;
+ uint ctrl = SC_TitleBarSysMenu;
+ while (ctrl <= SC_TitleBarLabel) {
+ r = layout[QStyle::SubControl(ctrl)];
+ if (r.isValid() && r.contains(pt)) {
+ sc = QStyle::SubControl(ctrl);
+ break;
+ }
+ ctrl <<= 1;
+ }
+ return sc;
+ }
+ }
+ break;
+
+ case CC_MdiControls:
+ if (hasStyleRule(w, PseudoElement_MdiCloseButton)
+ || hasStyleRule(w, PseudoElement_MdiNormalButton)
+ || hasStyleRule(w, PseudoElement_MdiMinButton))
+ return QWindowsStyle::hitTestComplexControl(cc, opt, pt, w);
+ break;
+
+ case CC_ScrollBar: {
+ QRenderRule rule = renderRule(w, opt);
+ if (!rule.hasDrawable() && !rule.hasBox())
+ break;
+ }
+ // intentionally falls through
+ case CC_SpinBox:
+ case CC_GroupBox:
+ case CC_ComboBox:
+ case CC_Slider:
+ case CC_ToolButton:
+ return QWindowsStyle::hitTestComplexControl(cc, opt, pt, w);
+ default:
+ break;
+ }
+
+ return baseStyle()->hitTestComplexControl(cc, opt, pt, w);
+}
+
+QRect QStyleSheetStyle::itemPixmapRect(const QRect &rect, int alignment, const QPixmap &pixmap) const
+{
+ return baseStyle()->itemPixmapRect(rect, alignment, pixmap);
+}
+
+QRect QStyleSheetStyle::itemTextRect(const QFontMetrics &metrics, const QRect& rect, int alignment,
+ bool enabled, const QString& text) const
+{
+ return baseStyle()->itemTextRect(metrics, rect, alignment, enabled, text);
+}
+
+int QStyleSheetStyle::pixelMetric(PixelMetric m, const QStyleOption *opt, const QWidget *w) const
+{
+ RECURSION_GUARD(return baseStyle()->pixelMetric(m, opt, w))
+
+ QRenderRule rule = renderRule(w, opt);
+ QRenderRule subRule;
+
+ switch (m) {
+ case PM_MenuButtonIndicator:
+#ifndef QT_NO_TOOLBUTTON
+ // QToolButton adds this directly to the width
+ if (qobject_cast<const QToolButton *>(w) && (rule.hasBox() || !rule.hasNativeBorder()))
+ return 0;
+#endif
+ subRule = renderRule(w, opt, PseudoElement_PushButtonMenuIndicator);
+ if (subRule.hasContentsSize())
+ return subRule.size().width();
+ break;
+
+ case PM_ButtonShiftHorizontal:
+ case PM_ButtonShiftVertical:
+ case PM_ButtonMargin:
+ case PM_ButtonDefaultIndicator:
+ if (rule.hasBox())
+ return 0;
+ break;
+
+ case PM_DefaultFrameWidth:
+ if (!rule.hasNativeBorder())
+ return rule.border()->borders[LeftEdge];
+ break;
+
+ case PM_ExclusiveIndicatorWidth:
+ case PM_IndicatorWidth:
+ case PM_ExclusiveIndicatorHeight:
+ case PM_IndicatorHeight:
+ subRule = renderRule(w, opt, PseudoElement_Indicator);
+ if (subRule.hasContentsSize()) {
+ return (m == PM_ExclusiveIndicatorWidth) || (m == PM_IndicatorWidth)
+ ? subRule.size().width() : subRule.size().height();
+ }
+ break;
+
+ case PM_DockWidgetFrameWidth:
+ case PM_ToolTipLabelFrameWidth: // border + margin + padding (support only one width)
+ if (!rule.hasDrawable())
+ break;
+
+ return (rule.border() ? rule.border()->borders[LeftEdge] : 0)
+ + (rule.hasBox() ? rule.box()->margins[LeftEdge] + rule.box()->paddings[LeftEdge]: 0);
+
+ case PM_ToolBarFrameWidth:
+ if (rule.hasBorder() || rule.hasBox())
+ return (rule.border() ? rule.border()->borders[LeftEdge] : 0)
+ + (rule.hasBox() ? rule.box()->paddings[LeftEdge]: 0);
+ break;
+
+ case PM_MenuPanelWidth:
+ case PM_MenuBarPanelWidth:
+ if (rule.hasBorder() || rule.hasBox())
+ return (rule.border() ? rule.border()->borders[LeftEdge] : 0)
+ + (rule.hasBox() ? rule.box()->margins[LeftEdge]: 0);
+ break;
+
+
+ case PM_MenuHMargin:
+ case PM_MenuBarHMargin:
+ if (rule.hasBox())
+ return rule.box()->paddings[LeftEdge];
+ break;
+
+ case PM_MenuVMargin:
+ case PM_MenuBarVMargin:
+ if (rule.hasBox())
+ return rule.box()->paddings[TopEdge];
+ break;
+
+ case PM_DockWidgetTitleBarButtonMargin:
+ case PM_ToolBarItemMargin:
+ if (rule.hasBox())
+ return rule.box()->margins[TopEdge];
+ break;
+
+ case PM_ToolBarItemSpacing:
+ case PM_MenuBarItemSpacing:
+ if (rule.hasBox() && rule.box()->spacing != -1)
+ return rule.box()->spacing;
+ break;
+
+ case PM_MenuTearoffHeight:
+ case PM_MenuScrollerHeight: {
+ PseudoElement ps = m == PM_MenuTearoffHeight ? PseudoElement_MenuTearoff : PseudoElement_MenuScroller;
+ subRule = renderRule(w, opt, ps);
+ if (subRule.hasContentsSize())
+ return subRule.size().height();
+ break;
+ }
+
+ case PM_ToolBarExtensionExtent:
+ break;
+
+ case PM_SplitterWidth:
+ case PM_ToolBarSeparatorExtent:
+ case PM_ToolBarHandleExtent: {
+ PseudoElement ps;
+ if (m == PM_ToolBarHandleExtent) ps = PseudoElement_ToolBarHandle;
+ else if (m == PM_SplitterWidth) ps = PseudoElement_SplitterHandle;
+ else ps = PseudoElement_ToolBarSeparator;
+ subRule = renderRule(w, opt, ps);
+ if (subRule.hasContentsSize()) {
+ QSize sz = subRule.size();
+ return (opt && opt->state & QStyle::State_Horizontal) ? sz.width() : sz.height();
+ }
+ break;
+ }
+
+ case PM_RadioButtonLabelSpacing:
+ if (rule.hasBox() && rule.box()->spacing != -1)
+ return rule.box()->spacing;
+ break;
+ case PM_CheckBoxLabelSpacing:
+ if (qobject_cast<const QCheckBox *>(w)) {
+ if (rule.hasBox() && rule.box()->spacing != -1)
+ return rule.box()->spacing;
+ }
+ // assume group box
+ subRule = renderRule(w, opt, PseudoElement_GroupBoxTitle);
+ if (subRule.hasBox() && subRule.box()->spacing != -1)
+ return subRule.box()->spacing;
+ break;
+
+#ifndef QT_NO_SCROLLBAR
+ case PM_ScrollBarExtent:
+ if (rule.hasContentsSize()) {
+ QSize sz = rule.size();
+ if (const QStyleOptionSlider *sb = qstyleoption_cast<const QStyleOptionSlider *>(opt))
+ return sb->orientation == Qt::Horizontal ? sz.height() : sz.width();
+ return sz.width() == -1 ? sz.height() : sz.width();
+ }
+ break;
+
+ case PM_ScrollBarSliderMin:
+ if (hasStyleRule(w, PseudoElement_ScrollBarSlider)) {
+ subRule = renderRule(w, opt, PseudoElement_ScrollBarSlider);
+ QSize msz = subRule.minimumSize();
+ if (const QStyleOptionSlider *sb = qstyleoption_cast<const QStyleOptionSlider *>(opt))
+ return sb->orientation == Qt::Horizontal ? msz.width() : msz.height();
+ return msz.width() == -1 ? msz.height() : msz.width();
+ }
+ break;
+#endif // QT_NO_SCROLLBAR
+
+ case PM_ProgressBarChunkWidth:
+ subRule = renderRule(w, opt, PseudoElement_ProgressBarChunk);
+ if (subRule.hasContentsSize()) {
+ QSize sz = subRule.size();
+ return (opt->state & QStyle::State_Horizontal)
+ ? sz.width() : sz.height();
+ }
+ break;
+
+ case PM_TabBarTabHSpace:
+ case PM_TabBarTabVSpace:
+ subRule = renderRule(w, opt, PseudoElement_TabBarTab);
+ if (subRule.hasBox() || subRule.hasBorder())
+ return 0;
+ break;
+
+ case PM_TabBarScrollButtonWidth: {
+ subRule = renderRule(w, opt, PseudoElement_TabBarScroller);
+ if (subRule.hasContentsSize()) {
+ QSize sz = subRule.size();
+ return sz.width() != -1 ? sz.width() : sz.height();
+ }
+ }
+ break;
+
+ case PM_TabBarTabShiftHorizontal:
+ case PM_TabBarTabShiftVertical:
+ subRule = renderRule(w, opt, PseudoElement_TabBarTab);
+ if (subRule.hasBox())
+ return 0;
+ break;
+
+ case PM_TabBarBaseOverlap:
+ if (hasStyleRule(w->parentWidget(), PseudoElement_TabWidgetPane)) {
+ return 0;
+ }
+ break;
+
+ case PM_SliderThickness: // horizontal slider's height (sizeHint)
+ case PM_SliderLength: // minimum length of slider
+ if (rule.hasContentsSize()) {
+ bool horizontal = opt->state & QStyle::State_Horizontal;
+ if (m == PM_SliderThickness) {
+ QSize sz = rule.size();
+ return horizontal ? sz.height() : sz.width();
+ } else {
+ QSize msz = rule.minimumContentsSize();
+ return horizontal ? msz.width() : msz.height();
+ }
+ }
+ break;
+
+ case PM_SliderControlThickness: {
+ QRenderRule subRule = renderRule(w, opt, PseudoElement_SliderHandle);
+ if (!subRule.hasContentsSize())
+ break;
+ QSize size = subRule.size();
+ return (opt->state & QStyle::State_Horizontal) ? size.height() : size.width();
+ }
+
+ case PM_ToolBarIconSize:
+ case PM_ListViewIconSize:
+ case PM_IconViewIconSize:
+ case PM_TabBarIconSize:
+ case PM_MessageBoxIconSize:
+ case PM_ButtonIconSize:
+ case PM_SmallIconSize:
+ if (rule.hasStyleHint(QLatin1String("icon-size"))) {
+ return rule.styleHint(QLatin1String("icon-size")).toSize().width();
+ }
+ break;
+
+ case PM_DockWidgetTitleMargin: {
+ QRenderRule subRule = renderRule(w, opt, PseudoElement_DockWidgetTitle);
+ if (!subRule.hasBox())
+ break;
+ return (subRule.border() ? subRule.border()->borders[TopEdge] : 0)
+ + (subRule.hasBox() ? subRule.box()->margins[TopEdge] + subRule.box()->paddings[TopEdge]: 0);
+ }
+
+ case PM_DockWidgetSeparatorExtent: {
+ QRenderRule subRule = renderRule(w, opt, PseudoElement_DockWidgetSeparator);
+ if (!subRule.hasContentsSize())
+ break;
+ QSize sz = subRule.size();
+ return qMax(sz.width(), sz.height());
+ }
+
+ case PM_TitleBarHeight: {
+ QRenderRule subRule = renderRule(w, opt, PseudoElement_TitleBar);
+ if (subRule.hasContentsSize())
+ return subRule.size().height();
+ else if (subRule.hasBox() || subRule.hasBorder()) {
+ return subRule.size(QSize(0, opt->fontMetrics.lineSpacing())).height();
+ }
+ break;
+ }
+
+ case PM_MdiSubWindowFrameWidth:
+ if (rule.hasBox() || rule.hasBorder()) {
+ return (rule.border() ? rule.border()->borders[LeftEdge] : 0)
+ + (rule.hasBox() ? rule.box()->paddings[LeftEdge]+rule.box()->margins[LeftEdge]: 0);
+ }
+ break;
+
+ case PM_MdiSubWindowMinimizedWidth: {
+ QRenderRule subRule = renderRule(w, PseudoElement_None, PseudoClass_Minimized);
+ int width = subRule.size().width();
+ if (width != -1)
+ return width;
+ break;
+ }
+ default:
+ break;
+ }
+
+ return baseStyle()->pixelMetric(m, opt, w);
+}
+
+QSize QStyleSheetStyle::sizeFromContents(ContentsType ct, const QStyleOption *opt,
+ const QSize &csz, const QWidget *w) const
+{
+ RECURSION_GUARD(return baseStyle()->sizeFromContents(ct, opt, csz, w))
+
+ QRenderRule rule = renderRule(w, opt);
+ QSize sz = rule.adjustSize(csz);
+
+ switch (ct) {
+ case CT_SpinBox: // ### hopelessly broken QAbstractSpinBox (part 1)
+ if (rule.hasBox() || !rule.hasNativeBorder())
+ return csz;
+ return rule.baseStyleCanDraw() ? baseStyle()->sizeFromContents(ct, opt, sz, w)
+ : QWindowsStyle::sizeFromContents(ct, opt, sz, w);
+ case CT_ToolButton:
+ if (rule.hasBox() || !rule.hasNativeBorder() || !rule.baseStyleCanDraw())
+ sz += QSize(3, 3); // ### broken QToolButton
+ //fall thought
+ case CT_ComboBox:
+ case CT_PushButton:
+ if (rule.hasBox() || !rule.hasNativeBorder()) {
+ if(ct == CT_ComboBox) {
+ //add some space for the drop down.
+ QRenderRule subRule = renderRule(w, opt, PseudoElement_ComboBoxDropDown);
+ QRect comboRect = positionRect(w, rule, subRule, PseudoElement_ComboBoxDropDown, opt->rect, opt->direction);
+ //+2 because there is hardcoded margins in QCommonStyle::drawControl(CE_ComboBoxLabel)
+ sz += QSize(comboRect.width() + 2, 0);
+ }
+ return rule.boxSize(sz);
+ }
+ sz = rule.baseStyleCanDraw() ? baseStyle()->sizeFromContents(ct, opt, sz, w)
+ : QWindowsStyle::sizeFromContents(ct, opt, sz, w);
+ return rule.boxSize(sz, Margin);
+
+ case CT_HeaderSection: {
+ if (const QStyleOptionHeader *hdr = qstyleoption_cast<const QStyleOptionHeader *>(opt)) {
+ QRenderRule subRule = renderRule(w, opt, PseudoElement_HeaderViewSection);
+ if (subRule.hasGeometry() || subRule.hasBox() || !subRule.hasNativeBorder()) {
+ sz = subRule.adjustSize(csz);
+ if (!subRule.hasGeometry()) {
+ QSize nativeContentsSize;
+ bool nullIcon = hdr->icon.isNull();
+ int iconSize = nullIcon ? 0 : pixelMetric(QStyle::PM_SmallIconSize, hdr, w);
+ QSize txt = hdr->fontMetrics.size(0, hdr->text);
+ nativeContentsSize.setHeight(qMax(iconSize, txt.height()));
+ nativeContentsSize.setWidth(iconSize + txt.width());
+ sz = sz.expandedTo(nativeContentsSize);
+ }
+ return subRule.size(sz);
+ }
+ return subRule.baseStyleCanDraw() ? baseStyle()->sizeFromContents(ct, opt, sz, w)
+ : QWindowsStyle::sizeFromContents(ct, opt, sz, w);
+ }
+ }
+ break;
+ case CT_GroupBox:
+ case CT_LineEdit:
+#ifndef QT_NO_SPINBOX
+ // ### hopelessly broken QAbstractSpinBox (part 2)
+ if (QAbstractSpinBox *spinBox = qobject_cast<QAbstractSpinBox *>(w->parentWidget())) {
+ QRenderRule rule = renderRule(spinBox, opt);
+ if (rule.hasBox() || !rule.hasNativeBorder())
+ return csz;
+ return rule.baseStyleCanDraw() ? baseStyle()->sizeFromContents(ct, opt, sz, w)
+ : QWindowsStyle::sizeFromContents(ct, opt, sz, w);
+ }
+#endif
+ if (rule.hasBox() || !rule.hasNativeBorder()) {
+ return rule.boxSize(sz);
+ }
+ break;
+
+ case CT_CheckBox:
+ case CT_RadioButton:
+ if (const QStyleOptionButton *btn = qstyleoption_cast<const QStyleOptionButton *>(opt)) {
+ if (rule.hasBox() || rule.hasBorder() || hasStyleRule(w, PseudoElement_Indicator)) {
+ bool isRadio = (ct == CT_RadioButton);
+ int iw = pixelMetric(isRadio ? PM_ExclusiveIndicatorWidth
+ : PM_IndicatorWidth, btn, w);
+ int ih = pixelMetric(isRadio ? PM_ExclusiveIndicatorHeight
+ : PM_IndicatorHeight, btn, w);
+
+ int spacing = pixelMetric(isRadio ? PM_RadioButtonLabelSpacing
+ : PM_CheckBoxLabelSpacing, btn, w);
+ sz.setWidth(sz.width() + iw + spacing);
+ sz.setHeight(qMax(sz.height(), ih));
+ return rule.boxSize(sz);
+ }
+ }
+ break;
+
+ case CT_Menu:
+ case CT_MenuBar: // already has everything!
+ case CT_ScrollBar:
+ if (rule.hasBox() || rule.hasBorder())
+ return sz;
+ break;
+
+ case CT_MenuItem:
+ if (const QStyleOptionMenuItem *mi = qstyleoption_cast<const QStyleOptionMenuItem *>(opt)) {
+ PseudoElement pe = (mi->menuItemType == QStyleOptionMenuItem::Separator)
+ ? PseudoElement_MenuSeparator : PseudoElement_Item;
+ QRenderRule subRule = renderRule(w, opt, pe);
+ if ((pe == PseudoElement_MenuSeparator) && subRule.hasContentsSize()) {
+ return QSize(sz.width(), subRule.size().height());
+ } else if ((pe == PseudoElement_Item) && (subRule.hasBox() || subRule.hasBorder())) {
+ int width = csz.width(), height = qMax(csz.height(), mi->fontMetrics.height());
+ if (!mi->icon.isNull()) {
+ int iconExtent = pixelMetric(PM_SmallIconSize);
+ height = qMax(height, mi->icon.actualSize(QSize(iconExtent, iconExtent)).height());
+ }
+ width += mi->tabWidth;
+ return subRule.boxSize(csz.expandedTo(subRule.minimumContentsSize()));
+ }
+ }
+ break;
+
+ case CT_Splitter:
+ case CT_MenuBarItem: {
+ PseudoElement pe = (ct == CT_Splitter) ? PseudoElement_SplitterHandle : PseudoElement_Item;
+ QRenderRule subRule = renderRule(w, opt, pe);
+ if (subRule.hasBox() || subRule.hasBorder())
+ return subRule.boxSize(sz);
+ break;
+ }
+
+ case CT_ProgressBar:
+ case CT_SizeGrip:
+ return (rule.hasContentsSize())
+ ? rule.size(sz)
+ : rule.boxSize(baseStyle()->sizeFromContents(ct, opt, sz, w));
+ break;
+
+ case CT_Slider:
+ if (rule.hasBorder() || rule.hasBox() || rule.hasGeometry())
+ return rule.boxSize(sz);
+ break;
+
+#ifndef QT_NO_TABBAR
+ case CT_TabBarTab: {
+ QRenderRule subRule = renderRule(w, opt, PseudoElement_TabBarTab);
+ if (subRule.hasBox() || !subRule.hasNativeBorder()) {
+ int spaceForIcon = 0;
+ bool vertical = false;
+ if (const QStyleOptionTab *tab = qstyleoption_cast<const QStyleOptionTab *>(opt)) {
+ if (!tab->icon.isNull())
+ spaceForIcon = 6 /* icon offset */ + 4 /* spacing */ + 2 /* magic */; // ###: hardcoded to match with common style
+ vertical = verticalTabs(tab->shape);
+ }
+ sz = csz + QSize(vertical ? 0 : spaceForIcon, vertical ? spaceForIcon : 0);
+ return subRule.boxSize(subRule.adjustSize(sz));
+ }
+#ifdef Q_WS_MAC
+ if (baseStyle()->inherits("QMacStyle")) {
+ //adjust the size after the call to the style because the mac style ignore the size arguments anyway.
+ //this might cause the (max-){width,height} property to include the native style border while they should not.
+ return subRule.adjustSize(baseStyle()->sizeFromContents(ct, opt, csz, w));
+ }
+#endif
+ sz = subRule.adjustSize(csz);
+ break;
+ }
+#endif // QT_NO_TABBAR
+
+ case CT_MdiControls:
+ if (const QStyleOptionComplex *ccOpt = qstyleoption_cast<const QStyleOptionComplex *>(opt)) {
+ if (!hasStyleRule(w, PseudoElement_MdiCloseButton)
+ && !hasStyleRule(w, PseudoElement_MdiNormalButton)
+ && !hasStyleRule(w, PseudoElement_MdiMinButton))
+ break;
+
+ QList<QVariant> layout = rule.styleHint(QLatin1String("button-layout")).toList();
+ if (layout.isEmpty())
+ layout = subControlLayout(QLatin1String("mNX"));
+
+ int width = 0, height = 0;
+ for (int i = 0; i < layout.count(); i++) {
+ int layoutButton = layout[i].toInt();
+ if (layoutButton < PseudoElement_MdiCloseButton
+ || layoutButton > PseudoElement_MdiNormalButton)
+ continue;
+ QStyle::SubControl sc = knownPseudoElements[layoutButton].subControl;
+ if (!(ccOpt->subControls & sc))
+ continue;
+ QRenderRule subRule = renderRule(w, opt, layoutButton);
+ QSize sz = subRule.size();
+ width += sz.width();
+ height = qMax(height, sz.height());
+ }
+
+ return QSize(width, height);
+ }
+ break;
+
+#ifndef QT_NO_ITEMVIEWS
+ case CT_ItemViewItem: {
+ QRenderRule subRule = renderRule(w, opt, PseudoElement_ViewItem);
+ sz = baseStyle()->sizeFromContents(ct, opt, csz, w);
+ sz = subRule.adjustSize(sz);
+ if (subRule.hasBox() || subRule.hasBorder())
+ sz = subRule.boxSize(sz);
+ return sz;
+ }
+#endif // QT_NO_ITEMVIEWS
+
+ default:
+ break;
+ }
+
+ return baseStyle()->sizeFromContents(ct, opt, sz, w);
+}
+
+/*!
+ \internal
+*/
+static QLatin1String propertyNameForStandardPixmap(QStyle::StandardPixmap sp)
+{
+ switch (sp) {
+ case QStyle::SP_TitleBarMenuButton: return QLatin1String("titlebar-menu-icon");
+ case QStyle::SP_TitleBarMinButton: return QLatin1String("titlebar-minimize-icon");
+ case QStyle::SP_TitleBarMaxButton: return QLatin1String("titlebar-maximize-icon");
+ case QStyle::SP_TitleBarCloseButton: return QLatin1String("titlebar-close-icon");
+ case QStyle::SP_TitleBarNormalButton: return QLatin1String("titlebar-normal-icon");
+ case QStyle::SP_TitleBarShadeButton: return QLatin1String("titlebar-shade-icon");
+ case QStyle::SP_TitleBarUnshadeButton: return QLatin1String("titlebar-unshade-icon");
+ case QStyle::SP_TitleBarContextHelpButton: return QLatin1String("titlebar-contexthelp-icon");
+ case QStyle::SP_DockWidgetCloseButton: return QLatin1String("dockwidget-close-icon");
+ case QStyle::SP_MessageBoxInformation: return QLatin1String("messagebox-information-icon");
+ case QStyle::SP_MessageBoxWarning: return QLatin1String("messagebox-warning-icon");
+ case QStyle::SP_MessageBoxCritical: return QLatin1String("messagebox-critical-icon");
+ case QStyle::SP_MessageBoxQuestion: return QLatin1String("messagebox-question-icon");
+ case QStyle::SP_DesktopIcon: return QLatin1String("desktop-icon");
+ case QStyle::SP_TrashIcon: return QLatin1String("trash-icon");
+ case QStyle::SP_ComputerIcon: return QLatin1String("computer-icon");
+ case QStyle::SP_DriveFDIcon: return QLatin1String("floppy-icon");
+ case QStyle::SP_DriveHDIcon: return QLatin1String("harddisk-icon");
+ case QStyle::SP_DriveCDIcon: return QLatin1String("cd-icon");
+ case QStyle::SP_DriveDVDIcon: return QLatin1String("dvd-icon");
+ case QStyle::SP_DriveNetIcon: return QLatin1String("network-icon");
+ case QStyle::SP_DirOpenIcon: return QLatin1String("directory-open-icon");
+ case QStyle::SP_DirClosedIcon: return QLatin1String("directory-closed-icon");
+ case QStyle::SP_DirLinkIcon: return QLatin1String("directory-link-icon");
+ case QStyle::SP_FileIcon: return QLatin1String("file-icon");
+ case QStyle::SP_FileLinkIcon: return QLatin1String("file-link-icon");
+ case QStyle::SP_FileDialogStart: return QLatin1String("filedialog-start-icon");
+ case QStyle::SP_FileDialogEnd: return QLatin1String("filedialog-end-icon");
+ case QStyle::SP_FileDialogToParent: return QLatin1String("filedialog-parent-directory-icon");
+ case QStyle::SP_FileDialogNewFolder: return QLatin1String("filedialog-new-directory-icon");
+ case QStyle::SP_FileDialogDetailedView: return QLatin1String("filedialog-detailedview-icon");
+ case QStyle::SP_FileDialogInfoView: return QLatin1String("filedialog-infoview-icon");
+ case QStyle::SP_FileDialogContentsView: return QLatin1String("filedialog-contentsview-icon");
+ case QStyle::SP_FileDialogListView: return QLatin1String("filedialog-listview-icon");
+ case QStyle::SP_FileDialogBack: return QLatin1String("filedialog-backward-icon");
+ case QStyle::SP_DirIcon: return QLatin1String("directory-icon");
+ case QStyle::SP_DialogOkButton: return QLatin1String("dialog-ok-icon");
+ case QStyle::SP_DialogCancelButton: return QLatin1String("dialog-cancel-icon");
+ case QStyle::SP_DialogHelpButton: return QLatin1String("dialog-help-icon");
+ case QStyle::SP_DialogOpenButton: return QLatin1String("dialog-open-icon");
+ case QStyle::SP_DialogSaveButton: return QLatin1String("dialog-save-icon");
+ case QStyle::SP_DialogCloseButton: return QLatin1String("dialog-close-icon");
+ case QStyle::SP_DialogApplyButton: return QLatin1String("dialog-apply-icon");
+ case QStyle::SP_DialogResetButton: return QLatin1String("dialog-reset-icon");
+ case QStyle::SP_DialogDiscardButton: return QLatin1String("discard-icon");
+ case QStyle::SP_DialogYesButton: return QLatin1String("dialog-yes-icon");
+ case QStyle::SP_DialogNoButton: return QLatin1String("dialog-no-icon");
+ case QStyle::SP_ArrowUp: return QLatin1String("uparrow-icon");
+ case QStyle::SP_ArrowDown: return QLatin1String("downarrow-icon");
+ case QStyle::SP_ArrowLeft: return QLatin1String("leftarrow-icon");
+ case QStyle::SP_ArrowRight: return QLatin1String("rightarrow-icon");
+ case QStyle::SP_ArrowBack: return QLatin1String("backward-icon");
+ case QStyle::SP_ArrowForward: return QLatin1String("forward-icon");
+ case QStyle::SP_DirHomeIcon: return QLatin1String("home-icon");
+ default: return QLatin1String("");
+ }
+}
+
+QIcon QStyleSheetStyle::standardIconImplementation(StandardPixmap standardIcon, const QStyleOption *opt,
+ const QWidget *w) const
+{
+ RECURSION_GUARD(return baseStyle()->standardIcon(standardIcon, opt, w))
+ QString s = propertyNameForStandardPixmap(standardIcon);
+ if (!s.isEmpty()) {
+ QRenderRule rule = renderRule(w, opt);
+ if (rule.hasStyleHint(s))
+ return qVariantValue<QIcon>(rule.styleHint(s));
+ }
+ return baseStyle()->standardIcon(standardIcon, opt, w);
+}
+
+QPalette QStyleSheetStyle::standardPalette() const
+{
+ return baseStyle()->standardPalette();
+}
+
+QPixmap QStyleSheetStyle::standardPixmap(StandardPixmap standardPixmap, const QStyleOption *opt,
+ const QWidget *w) const
+{
+ RECURSION_GUARD(return baseStyle()->standardPixmap(standardPixmap, opt, w))
+ QString s = propertyNameForStandardPixmap(standardPixmap);
+ if (!s.isEmpty()) {
+ QRenderRule rule = renderRule(w, opt);
+ if (rule.hasStyleHint(s)) {
+ QIcon icon = qVariantValue<QIcon>(rule.styleHint(s));
+ return icon.pixmap(16, 16); // ###: unhard-code this if someone complains
+ }
+ }
+ return baseStyle()->standardPixmap(standardPixmap, opt, w);
+}
+
+int QStyleSheetStyle::layoutSpacing(QSizePolicy::ControlType control1, QSizePolicy::ControlType control2,
+ Qt::Orientation orientation, const QStyleOption *option,
+ const QWidget *widget) const
+{
+ return baseStyle()->layoutSpacing(control1, control2, orientation, option, widget);
+}
+
+int QStyleSheetStyle::layoutSpacingImplementation(QSizePolicy::ControlType control1 ,
+ QSizePolicy::ControlType control2,
+ Qt::Orientation orientation,
+ const QStyleOption * option ,
+ const QWidget * widget) const
+{
+ return baseStyle()->layoutSpacing(control1, control2, orientation, option, widget);
+}
+
+int QStyleSheetStyle::styleHint(StyleHint sh, const QStyleOption *opt, const QWidget *w,
+ QStyleHintReturn *shret) const
+{
+ RECURSION_GUARD(return baseStyle()->styleHint(sh, opt, w, shret))
+ // Prevent endless loop if somebody use isActiveWindow property as selector.
+ // QWidget::isActiveWindow uses this styleHint to determine if the window is active or not
+ if (sh == SH_Widget_ShareActivation)
+ return baseStyle()->styleHint(sh, opt, w, shret);
+
+ QRenderRule rule = renderRule(w, opt);
+ QString s;
+ switch (sh) {
+ case SH_LineEdit_PasswordCharacter: s = QLatin1String("lineedit-password-character"); break;
+ case SH_DitherDisabledText: s = QLatin1String("dither-disabled-text"); break;
+ case SH_EtchDisabledText: s = QLatin1String("etch-disabled-text"); break;
+ case SH_ItemView_ActivateItemOnSingleClick: s = QLatin1String("activate-on-singleclick"); break;
+ case SH_ItemView_ShowDecorationSelected: s = QLatin1String("show-decoration-selected"); break;
+ case SH_Table_GridLineColor: s = QLatin1String("gridline-color"); break;
+ case SH_DialogButtonLayout: s = QLatin1String("button-layout"); break;
+ case SH_ToolTipLabel_Opacity: s = QLatin1String("opacity"); break;
+ case SH_ComboBox_Popup: s = QLatin1String("combobox-popup"); break;
+ case SH_ComboBox_ListMouseTracking: s = QLatin1String("combobox-list-mousetracking"); break;
+ case SH_MenuBar_AltKeyNavigation: s = QLatin1String("menubar-altkey-navigation"); break;
+ case SH_Menu_Scrollable: s = QLatin1String("menu-scrollable"); break;
+ case SH_DrawMenuBarSeparator: s = QLatin1String("menubar-separator"); break;
+ case SH_MenuBar_MouseTracking: s = QLatin1String("mouse-tracking"); break;
+ case SH_SpinBox_ClickAutoRepeatRate: s = QLatin1String("spinbox-click-autorepeat-rate"); break;
+ case SH_SpinControls_DisableOnBounds: s = QLatin1String("spincontrol-disable-on-bounds"); break;
+ case SH_MessageBox_TextInteractionFlags: s = QLatin1String("messagebox-text-interaction-flags"); break;
+ case SH_ToolButton_PopupDelay: s = QLatin1String("toolbutton-popup-delay"); break;
+ case SH_ToolBox_SelectedPageTitleBold:
+ if (renderRule(w, opt, PseudoElement_ToolBoxTab).hasFont)
+ return 0;
+ break;
+ case SH_GroupBox_TextLabelColor:
+ if (rule.hasPalette() && rule.palette()->foreground.style() != Qt::NoBrush)
+ return rule.palette()->foreground.color().rgba();
+ break;
+ case SH_ScrollView_FrameOnlyAroundContents: s = QLatin1String("scrollview-frame-around-contents"); break;
+ case SH_ScrollBar_ContextMenu: s = QLatin1String("scrollbar-contextmenu"); break;
+ case SH_ScrollBar_LeftClickAbsolutePosition: s = QLatin1String("scrollbar-leftclick-absolute-position"); break;
+ case SH_ScrollBar_MiddleClickAbsolutePosition: s = QLatin1String("scrollbar-middleclick-absolute-position"); break;
+ case SH_ScrollBar_RollBetweenButtons: s = QLatin1String("scrollbar-roll-between-buttons"); break;
+ case SH_ScrollBar_ScrollWhenPointerLeavesControl: s = QLatin1String("scrollbar-scroll-when-pointer-leaves-control"); break;
+ case SH_TabBar_Alignment:
+#ifndef QT_NO_TABWIDGET
+ if (qobject_cast<const QTabWidget *>(w)) {
+ rule = renderRule(w, opt, PseudoElement_TabWidgetTabBar);
+ if (rule.hasPosition())
+ return rule.position()->position;
+ }
+#endif // QT_NO_TABWIDGET
+ s = QLatin1String("alignment");
+ break;
+ case SH_TabBar_ElideMode: s = QLatin1String("tabbar-elide-mode"); break;
+ case SH_TabBar_PreferNoArrows: s = QLatin1String("tabbar-prefer-no-arrows"); break;
+ case SH_ComboBox_PopupFrameStyle:
+#ifndef QT_NO_COMBOBOX
+ if (qobject_cast<const QComboBox *>(w)) {
+ QAbstractItemView *view = qFindChild<QAbstractItemView *>(w);
+ QRenderRule subRule = renderRule(view, PseudoElement_None);
+ if (subRule.hasBox() || !subRule.hasNativeBorder())
+ return QFrame::NoFrame;
+ }
+#endif // QT_NO_COMBOBOX
+ break;
+ case SH_DialogButtonBox_ButtonsHaveIcons: s = QLatin1String("dialogbuttonbox-buttons-have-icons"); break;
+ case SH_Workspace_FillSpaceOnMaximize: s = QLatin1String("mdi-fill-space-on-maximize"); break;
+ case SH_TitleBar_NoBorder:
+ if (rule.hasBorder())
+ return !rule.border()->borders[LeftEdge];
+ break;
+ case SH_TitleBar_AutoRaise: { // plain absurd
+ QRenderRule subRule = renderRule(w, opt, PseudoElement_TitleBar);
+ if (subRule.hasDrawable())
+ return 1;
+ break;
+ }
+ case SH_ItemView_ArrowKeysNavigateIntoChildren: s = QLatin1String("arrow-keys-navigate-into-children"); break;
+ case SH_ItemView_PaintAlternatingRowColorsForEmptyArea: s = QLatin1String("paint-alternating-row-colors-for-empty-area"); break;
+ default: break;
+ }
+ if (!s.isEmpty() && rule.hasStyleHint(s)) {
+ return rule.styleHint(s).toInt();
+ }
+
+ return baseStyle()->styleHint(sh, opt, w, shret);
+}
+
+QRect QStyleSheetStyle::subControlRect(ComplexControl cc, const QStyleOptionComplex *opt, SubControl sc,
+ const QWidget *w) const
+{
+ RECURSION_GUARD(return baseStyle()->subControlRect(cc, opt, sc, w))
+
+ QRenderRule rule = renderRule(w, opt);
+ switch (cc) {
+ case CC_ComboBox:
+ if (const QStyleOptionComboBox *cb = qstyleoption_cast<const QStyleOptionComboBox *>(opt)) {
+ if (rule.hasBox() || !rule.hasNativeBorder()) {
+ switch (sc) {
+ case SC_ComboBoxFrame: return rule.borderRect(opt->rect);
+ case SC_ComboBoxEditField:
+ {
+ QRenderRule subRule = renderRule(w, opt, PseudoElement_ComboBoxDropDown);
+ QRect r = rule.contentsRect(opt->rect);
+ QRect r2 = positionRect(w, rule, subRule, PseudoElement_ComboBoxDropDown,
+ opt->rect, opt->direction);
+ if (subRule.hasPosition() && subRule.position()->position & Qt::AlignLeft) {
+ return visualRect(opt->direction, r, r.adjusted(r2.width(),0,0,0));
+ } else {
+ return visualRect(opt->direction, r, r.adjusted(0,0,-r2.width(),0));
+ }
+ }
+ case SC_ComboBoxArrow: {
+ QRenderRule subRule = renderRule(w, opt, PseudoElement_ComboBoxDropDown);
+ return positionRect(w, rule, subRule, PseudoElement_ComboBoxDropDown, opt->rect, opt->direction);
+ }
+ case SC_ComboBoxListBoxPopup:
+ default:
+ return baseStyle()->subControlRect(cc, opt, sc, w);
+ }
+ }
+
+ QStyleOptionComboBox comboBox(*cb);
+ comboBox.rect = rule.borderRect(opt->rect);
+ return rule.baseStyleCanDraw() ? baseStyle()->subControlRect(cc, &comboBox, sc, w)
+ : QWindowsStyle::subControlRect(cc, &comboBox, sc, w);
+ }
+ break;
+
+#ifndef QT_NO_SPINBOX
+ case CC_SpinBox:
+ if (const QStyleOptionSpinBox *spin = qstyleoption_cast<const QStyleOptionSpinBox *>(opt)) {
+ QRenderRule upRule = renderRule(w, opt, PseudoElement_SpinBoxUpButton);
+ QRenderRule downRule = renderRule(w, opt, PseudoElement_SpinBoxDownButton);
+ bool ruleMatch = rule.hasBox() || !rule.hasNativeBorder();
+ bool upRuleMatch = upRule.hasGeometry();
+ bool downRuleMatch = downRule.hasGeometry();
+ if (ruleMatch || upRuleMatch || downRuleMatch) {
+ switch (sc) {
+ case SC_SpinBoxFrame:
+ return rule.borderRect(opt->rect);
+ case SC_SpinBoxEditField:
+ {
+ QRect r = rule.contentsRect(opt->rect);
+ // Use the widest button on each side to determine edit field size.
+ Qt::Alignment upAlign, downAlign;
+
+ upAlign = upRule.hasPosition() ? upRule.position()->position
+ : Qt::Alignment(Qt::AlignRight);
+ upAlign = resolveAlignment(opt->direction, upAlign);
+
+ downAlign = downRule.hasPosition() ? downRule.position()->position
+ : Qt::Alignment(Qt::AlignRight);
+ downAlign = resolveAlignment(opt->direction, downAlign);
+
+ int upSize = subControlRect(CC_SpinBox, opt, SC_SpinBoxUp, w).width();
+ int downSize = subControlRect(CC_SpinBox, opt, SC_SpinBoxDown, w).width();
+ int widestL = qMax((upAlign & Qt::AlignLeft) ? upSize : 0,
+ (downAlign & Qt::AlignLeft) ? downSize : 0);
+ int widestR = qMax((upAlign & Qt::AlignRight) ? upSize : 0,
+ (downAlign & Qt::AlignRight) ? downSize : 0);
+ r.setRight(r.right() - widestR);
+ r.setLeft(r.left() + widestL);
+ return r;
+ }
+ case SC_SpinBoxDown:
+ if (downRuleMatch)
+ return positionRect(w, rule, downRule, PseudoElement_SpinBoxDownButton,
+ opt->rect, opt->direction);
+ break;
+ case SC_SpinBoxUp:
+ if (upRuleMatch)
+ return positionRect(w, rule, upRule, PseudoElement_SpinBoxUpButton,
+ opt->rect, opt->direction);
+ break;
+ default:
+ break;
+ }
+
+ return baseStyle()->subControlRect(cc, opt, sc, w);
+ }
+
+ QStyleOptionSpinBox spinBox(*spin);
+ spinBox.rect = rule.borderRect(opt->rect);
+ return rule.baseStyleCanDraw() ? baseStyle()->subControlRect(cc, &spinBox, sc, w)
+ : QWindowsStyle::subControlRect(cc, &spinBox, sc, w);
+ }
+ break;
+#endif // QT_NO_SPINBOX
+
+ case CC_GroupBox:
+ if (const QStyleOptionGroupBox *gb = qstyleoption_cast<const QStyleOptionGroupBox *>(opt)) {
+ switch (sc) {
+ case SC_GroupBoxFrame:
+ case SC_GroupBoxContents: {
+ if (rule.hasBox() || !rule.hasNativeBorder()) {
+ return sc == SC_GroupBoxFrame ? rule.borderRect(opt->rect)
+ : rule.contentsRect(opt->rect);
+ }
+ QStyleOptionGroupBox groupBox(*gb);
+ groupBox.rect = rule.borderRect(opt->rect);
+ return baseStyle()->subControlRect(cc, &groupBox, sc, w);
+ }
+ default:
+ case SC_GroupBoxLabel:
+ case SC_GroupBoxCheckBox: {
+ QRenderRule indRule = renderRule(w, opt, PseudoElement_GroupBoxIndicator);
+ QRenderRule labelRule = renderRule(w, opt, PseudoElement_GroupBoxTitle);
+ if (!labelRule.hasPosition() && !labelRule.hasGeometry() && !labelRule.hasBox()
+ && !labelRule.hasBorder() && !indRule.hasContentsSize()) {
+ QStyleOptionGroupBox groupBox(*gb);
+ groupBox.rect = rule.borderRect(opt->rect);
+ return baseStyle()->subControlRect(cc, &groupBox, sc, w);
+ }
+ int tw = opt->fontMetrics.width(gb->text);
+ int th = opt->fontMetrics.height();
+ int spacing = pixelMetric(QStyle::PM_CheckBoxLabelSpacing, opt, w);
+ int iw = pixelMetric(QStyle::PM_IndicatorWidth, opt, w);
+ int ih = pixelMetric(QStyle::PM_IndicatorHeight, opt, w);
+
+ if (gb->subControls & QStyle::SC_GroupBoxCheckBox) {
+ tw = tw + iw + spacing;
+ th = qMax(th, ih);
+ }
+ if (!labelRule.hasGeometry()) {
+ labelRule.geo = new QStyleSheetGeometryData(tw, th, tw, th, -1, -1);
+ } else {
+ labelRule.geo->width = tw;
+ labelRule.geo->height = th;
+ }
+ if (!labelRule.hasPosition()) {
+ labelRule.p = new QStyleSheetPositionData(0, 0, 0, 0, defaultOrigin(PseudoElement_GroupBoxTitle),
+ gb->textAlignment, PositionMode_Static);
+ }
+ QRect r = positionRect(w, rule, labelRule, PseudoElement_GroupBoxTitle,
+ opt->rect, opt->direction);
+ if (gb->subControls & SC_GroupBoxCheckBox) {
+ r = labelRule.contentsRect(r);
+ if (sc == SC_GroupBoxLabel) {
+ r.setLeft(r.left() + iw + spacing);
+ r.setTop(r.center().y() - th/2);
+ } else {
+ r = QRect(r.left(), r.center().y() - ih/2, iw, ih);
+ }
+ return r;
+ } else {
+ return labelRule.contentsRect(r);
+ }
+ }
+ } // switch
+ }
+ break;
+
+ case CC_ToolButton:
+ if (const QStyleOptionToolButton *tb = qstyleoption_cast<const QStyleOptionToolButton *>(opt)) {
+ if (rule.hasBox() || !rule.hasNativeBorder()) {
+ switch (sc) {
+ case SC_ToolButton: return rule.borderRect(opt->rect);
+ case SC_ToolButtonMenu: {
+ QRenderRule subRule = renderRule(w, opt, PseudoElement_ToolButtonMenu);
+ return positionRect(w, rule, subRule, PseudoElement_ToolButtonMenu, opt->rect, opt->direction);
+ }
+ default:
+ break;
+ }
+ }
+
+ QStyleOptionToolButton tool(*tb);
+ tool.rect = rule.borderRect(opt->rect);
+ return rule.baseStyleCanDraw() ? baseStyle()->subControlRect(cc, &tool, sc, w)
+ : QWindowsStyle::subControlRect(cc, &tool, sc, w);
+ }
+ break;
+
+#ifndef QT_NO_SCROLLBAR
+ case CC_ScrollBar:
+ if (const QStyleOptionSlider *sb = qstyleoption_cast<const QStyleOptionSlider *>(opt)) {
+ QStyleOptionSlider styleOptionSlider(*sb);
+ styleOptionSlider.rect = rule.borderRect(opt->rect);
+ if (rule.hasDrawable() || rule.hasBox()) {
+ QRect grooveRect;
+ if (!rule.hasBox()) {
+ grooveRect = rule.baseStyleCanDraw() ? baseStyle()->subControlRect(cc, sb, SC_ScrollBarGroove, w)
+ : QWindowsStyle::subControlRect(cc, sb, SC_ScrollBarGroove, w);
+ } else {
+ grooveRect = rule.contentsRect(opt->rect);
+ }
+
+ PseudoElement pe = PseudoElement_None;
+
+ switch (sc) {
+ case SC_ScrollBarGroove:
+ return grooveRect;
+ case SC_ScrollBarAddPage:
+ case SC_ScrollBarSubPage:
+ case SC_ScrollBarSlider: {
+ QRect contentRect = grooveRect;
+ if (hasStyleRule(w, PseudoElement_ScrollBarSlider)) {
+ QRenderRule sliderRule = renderRule(w, opt, PseudoElement_ScrollBarSlider);
+ Origin origin = sliderRule.hasPosition() ? sliderRule.position()->origin : defaultOrigin(PseudoElement_ScrollBarSlider);
+ contentRect = rule.originRect(opt->rect, origin);
+ }
+ int maxlen = (styleOptionSlider.orientation == Qt::Horizontal) ? contentRect.width() : contentRect.height();
+ int sliderlen;
+ if (sb->maximum != sb->minimum) {
+ uint range = sb->maximum - sb->minimum;
+ sliderlen = (qint64(sb->pageStep) * maxlen) / (range + sb->pageStep);
+
+ int slidermin = pixelMetric(PM_ScrollBarSliderMin, sb, w);
+ if (sliderlen < slidermin || range > INT_MAX / 2)
+ sliderlen = slidermin;
+ if (sliderlen > maxlen)
+ sliderlen = maxlen;
+ } else {
+ sliderlen = maxlen;
+ }
+
+ int sliderstart = (styleOptionSlider.orientation == Qt::Horizontal ? contentRect.left() : contentRect.top())
+ + sliderPositionFromValue(sb->minimum, sb->maximum, sb->sliderPosition,
+ maxlen - sliderlen, sb->upsideDown);
+
+ QRect sr = (sb->orientation == Qt::Horizontal)
+ ? QRect(sliderstart, contentRect.top(), sliderlen, contentRect.height())
+ : QRect(contentRect.left(), sliderstart, contentRect.width(), sliderlen);
+ if (sc == SC_ScrollBarSlider) {
+ return sr;
+ } else if (sc == SC_ScrollBarSubPage) {
+ return QRect(contentRect.topLeft(), sb->orientation == Qt::Horizontal ? sr.bottomLeft() : sr.topRight());
+ } else { // SC_ScrollBarAddPage
+ return QRect(sb->orientation == Qt::Horizontal ? sr.topRight() : sr.bottomLeft(), contentRect.bottomRight());
+ }
+ break;
+ }
+ case SC_ScrollBarAddLine: pe = PseudoElement_ScrollBarAddLine; break;
+ case SC_ScrollBarSubLine: pe = PseudoElement_ScrollBarSubLine; break;
+ case SC_ScrollBarFirst: pe = PseudoElement_ScrollBarFirst; break;
+ case SC_ScrollBarLast: pe = PseudoElement_ScrollBarLast; break;
+ default: break;
+ }
+ if (hasStyleRule(w,pe)) {
+ QRenderRule subRule = renderRule(w, opt, pe);
+ if (subRule.hasPosition() || subRule.hasGeometry() || subRule.hasBox()) {
+ const QStyleSheetPositionData *pos = subRule.position();
+ QRect originRect = grooveRect;
+ if (rule.hasBox()) {
+ Origin origin = (pos && pos->origin != Origin_Unknown) ? pos->origin : defaultOrigin(pe);
+ originRect = rule.originRect(opt->rect, origin);
+ }
+ return positionRect(w, subRule, pe, originRect, styleOptionSlider.direction);
+ }
+ }
+ }
+ return rule.baseStyleCanDraw() ? baseStyle()->subControlRect(cc, &styleOptionSlider, sc, w)
+ : QWindowsStyle::subControlRect(cc, &styleOptionSlider, sc, w);
+ }
+ break;
+#endif // QT_NO_SCROLLBAR
+
+#ifndef QT_NO_SLIDER
+ case CC_Slider:
+ if (const QStyleOptionSlider *slider = qstyleoption_cast<const QStyleOptionSlider *>(opt)) {
+ QRenderRule subRule = renderRule(w, opt, PseudoElement_SliderGroove);
+ if (!subRule.hasDrawable())
+ break;
+ subRule.img = 0;
+ QRect gr = positionRect(w, rule, subRule, PseudoElement_SliderGroove, opt->rect, opt->direction);
+ switch (sc) {
+ case SC_SliderGroove:
+ return gr;
+ case SC_SliderHandle: {
+ bool horizontal = slider->orientation & Qt::Horizontal;
+ QRect cr = subRule.contentsRect(gr);
+ QRenderRule subRule2 = renderRule(w, opt, PseudoElement_SliderHandle);
+ int len = horizontal ? subRule2.size().width() : subRule2.size().height();
+ subRule2.img = 0;
+ subRule2.geo = 0;
+ cr = positionRect(w, subRule2, PseudoElement_SliderHandle, cr, opt->direction);
+ int thickness = horizontal ? cr.height() : cr.width();
+ int sliderPos = sliderPositionFromValue(slider->minimum, slider->maximum, slider->sliderPosition,
+ (horizontal ? cr.width() : cr.height()) - len, slider->upsideDown);
+ cr = horizontal ? QRect(cr.x() + sliderPos, cr.y(), len, thickness)
+ : QRect(cr.x(), cr.y() + sliderPos, thickness, len);
+ return subRule2.borderRect(cr);
+ break; }
+ case SC_SliderTickmarks:
+ // TODO...
+ default:
+ break;
+ }
+ }
+ break;
+#endif // QT_NO_SLIDER
+
+ case CC_MdiControls:
+ if (hasStyleRule(w, PseudoElement_MdiCloseButton)
+ || hasStyleRule(w, PseudoElement_MdiNormalButton)
+ || hasStyleRule(w, PseudoElement_MdiMinButton)) {
+ QList<QVariant> layout = rule.styleHint(QLatin1String("button-layout")).toList();
+ if (layout.isEmpty())
+ layout = subControlLayout(QLatin1String("mNX"));
+
+ int x = 0, width = 0;
+ QRenderRule subRule;
+ for (int i = 0; i < layout.count(); i++) {
+ int layoutButton = layout[i].toInt();
+ if (layoutButton < PseudoElement_MdiCloseButton
+ || layoutButton > PseudoElement_MdiNormalButton)
+ continue;
+ QStyle::SubControl control = knownPseudoElements[layoutButton].subControl;
+ if (!(opt->subControls & control))
+ continue;
+ subRule = renderRule(w, opt, layoutButton);
+ width = subRule.size().width();
+ if (sc == control)
+ break;
+ x += width;
+ }
+
+ return subRule.borderRect(QRect(x, opt->rect.top(), width, opt->rect.height()));
+ }
+ break;
+
+ case CC_TitleBar:
+ if (const QStyleOptionTitleBar *tb = qstyleoption_cast<const QStyleOptionTitleBar *>(opt)) {
+ QRenderRule subRule = renderRule(w, opt, PseudoElement_TitleBar);
+ if (!subRule.hasDrawable() && !subRule.hasBox() && !subRule.hasBorder())
+ break;
+ QHash<QStyle::SubControl, QRect> layoutRects = titleBarLayout(w, tb);
+ return layoutRects.value(sc);
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ return baseStyle()->subControlRect(cc, opt, sc, w);
+}
+
+QRect QStyleSheetStyle::subElementRect(SubElement se, const QStyleOption *opt, const QWidget *w) const
+{
+ RECURSION_GUARD(return baseStyle()->subElementRect(se, opt, w))
+
+ QRenderRule rule = renderRule(w, opt);
+#ifndef QT_NO_TABBAR
+ int pe = PseudoElement_None;
+#endif
+
+ switch (se) {
+ case SE_PushButtonContents:
+ case SE_PushButtonFocusRect:
+ if (const QStyleOptionButton *btn = qstyleoption_cast<const QStyleOptionButton *>(opt)) {
+ QStyleOptionButton btnOpt(*btn);
+ if (rule.hasBox() || !rule.hasNativeBorder())
+ return visualRect(opt->direction, opt->rect, rule.contentsRect(opt->rect));
+ return rule.baseStyleCanDraw() ? baseStyle()->subElementRect(se, &btnOpt, w)
+ : QWindowsStyle::subElementRect(se, &btnOpt, w);
+ }
+ break;
+
+ case SE_LineEditContents:
+ case SE_FrameContents:
+ case SE_ShapedFrameContents:
+ if (rule.hasBox() || !rule.hasNativeBorder()) {
+ return visualRect(opt->direction, opt->rect, rule.contentsRect(opt->rect));
+ }
+ break;
+
+ case SE_CheckBoxIndicator:
+ case SE_RadioButtonIndicator:
+ if (rule.hasBox() || rule.hasBorder() || hasStyleRule(w, PseudoElement_Indicator)) {
+ PseudoElement pe = se == SE_CheckBoxIndicator ? PseudoElement_Indicator : PseudoElement_ExclusiveIndicator;
+ QRenderRule subRule = renderRule(w, opt, pe);
+ return positionRect(w, rule, subRule, pe, opt->rect, opt->direction);
+ }
+ break;
+
+ case SE_CheckBoxContents:
+ case SE_RadioButtonContents:
+ if (rule.hasBox() || rule.hasBorder() || hasStyleRule(w, PseudoElement_Indicator)) {
+ bool isRadio = se == SE_RadioButtonContents;
+ QRect ir = subElementRect(isRadio ? SE_RadioButtonIndicator : SE_CheckBoxIndicator,
+ opt, w);
+ ir = visualRect(opt->direction, opt->rect, ir);
+ int spacing = pixelMetric(isRadio ? PM_RadioButtonLabelSpacing : PM_CheckBoxLabelSpacing, 0, w);
+ QRect cr = rule.contentsRect(opt->rect);
+ ir.setRect(ir.left() + ir.width() + spacing, cr.y(),
+ cr.width() - ir.width() - spacing, cr.height());
+ return visualRect(opt->direction, opt->rect, ir);
+ }
+ break;
+
+ case SE_ToolBoxTabContents:
+ if (w && hasStyleRule(w->parentWidget(), PseudoElement_ToolBoxTab)) {
+ QRenderRule subRule = renderRule(w->parentWidget(), opt, PseudoElement_ToolBoxTab);
+ return visualRect(opt->direction, opt->rect, subRule.contentsRect(opt->rect));
+ }
+ break;
+
+ case SE_RadioButtonFocusRect:
+ case SE_RadioButtonClickRect: // focusrect | indicator
+ if (rule.hasBox() || rule.hasBorder() || hasStyleRule(w, PseudoElement_Indicator)) {
+ return opt->rect;
+ }
+ break;
+
+ case SE_CheckBoxFocusRect:
+ case SE_CheckBoxClickRect: // relies on indicator and contents
+ return ParentStyle::subElementRect(se, opt, w);
+
+#ifndef QT_NO_ITEMVIEWS
+ case SE_ViewItemCheckIndicator:
+ if (!qstyleoption_cast<const QStyleOptionViewItemV4 *>(opt)) {
+ return subElementRect(SE_CheckBoxIndicator, opt, w);
+ }
+ // intentionally falls through
+ case SE_ItemViewItemText:
+ case SE_ItemViewItemDecoration:
+ case SE_ItemViewItemFocusRect:
+ if (const QStyleOptionViewItemV4 *vopt = qstyleoption_cast<const QStyleOptionViewItemV4 *>(opt)) {
+ QRenderRule subRule = renderRule(w, opt, PseudoElement_ViewItem);
+ QStyleOptionViewItemV4 optCopy(*vopt);
+ optCopy.rect = subRule.contentsRect(vopt->rect);
+ QRect rect = ParentStyle::subElementRect(se, &optCopy, w);
+ PseudoElement pe = PseudoElement_None;
+ if (se == SE_ItemViewItemText || se == SE_ItemViewItemFocusRect)
+ pe = PseudoElement_ViewItemText;
+ else if (se == SE_ItemViewItemDecoration && vopt->features & QStyleOptionViewItemV2::HasDecoration)
+ pe = PseudoElement_ViewItemIcon;
+ else if (se == SE_ItemViewItemCheckIndicator && vopt->features & QStyleOptionViewItemV2::HasCheckIndicator)
+ pe = PseudoElement_ViewItemIndicator;
+ else
+ break;
+ QRenderRule subRule2 = renderRule(w, opt, pe);
+ return positionRect(w, subRule2, pe, rect, opt->direction);
+ }
+ break;
+#endif // QT_NO_ITEMVIEWS
+
+ case SE_HeaderArrow: {
+ QRenderRule subRule = renderRule(w, opt, PseudoElement_HeaderViewUpArrow);
+ if (subRule.hasPosition() || subRule.hasGeometry())
+ return positionRect(w, rule, subRule, PseudoElement_HeaderViewUpArrow, opt->rect, opt->direction);
+ }
+ break;
+
+ case SE_HeaderLabel: {
+ QRenderRule subRule = renderRule(w, opt, PseudoElement_HeaderViewSection);
+ if (subRule.hasBox() || !subRule.hasNativeBorder())
+ return subRule.contentsRect(opt->rect);
+ }
+ break;
+
+ case SE_ProgressBarGroove:
+ case SE_ProgressBarContents:
+ case SE_ProgressBarLabel:
+ if (const QStyleOptionProgressBarV2 *pb = qstyleoption_cast<const QStyleOptionProgressBarV2 *>(opt)) {
+ if (rule.hasBox() || !rule.hasNativeBorder() || rule.hasPosition() || hasStyleRule(w, PseudoElement_ProgressBarChunk)) {
+ if (se == SE_ProgressBarGroove)
+ return rule.borderRect(pb->rect);
+ else if (se == SE_ProgressBarContents)
+ return rule.contentsRect(pb->rect);
+
+ QSize sz = pb->fontMetrics.size(0, pb->text);
+ return QStyle::alignedRect(Qt::LeftToRight, rule.hasPosition() ? rule.position()->textAlignment : pb->textAlignment,
+ sz, pb->rect);
+ }
+ }
+ break;
+
+#ifndef QT_NO_TABBAR
+ case SE_TabWidgetLeftCorner:
+ pe = PseudoElement_TabWidgetLeftCorner;
+ // intentionally falls through
+ case SE_TabWidgetRightCorner:
+ if (pe == PseudoElement_None)
+ pe = PseudoElement_TabWidgetRightCorner;
+ // intentionally falls through
+ case SE_TabWidgetTabBar:
+ if (pe == PseudoElement_None)
+ pe = PseudoElement_TabWidgetTabBar;
+ // intentionally falls through
+ case SE_TabWidgetTabPane:
+ case SE_TabWidgetTabContents:
+ if (pe == PseudoElement_None)
+ pe = PseudoElement_TabWidgetPane;
+
+ if (hasStyleRule(w, pe)) {
+ QRect r = QWindowsStyle::subElementRect(pe == PseudoElement_TabWidgetPane ? SE_TabWidgetTabPane : se, opt, w);
+ QRenderRule subRule = renderRule(w, opt, pe);
+ r = positionRect(w, subRule, pe, r, opt->direction);
+ if (pe == PseudoElement_TabWidgetTabBar) {
+ Q_ASSERT(opt);
+ r = opt->rect.intersected(r);
+ }
+ if (se == SE_TabWidgetTabContents)
+ r = subRule.contentsRect(r);
+ return r;
+ }
+ break;
+
+ case SE_TabBarTearIndicator: {
+ QRenderRule subRule = renderRule(w, opt, PseudoElement_TabBarTear);
+ if (subRule.hasContentsSize()) {
+ QRect r;
+ if (const QStyleOptionTab *tab = qstyleoption_cast<const QStyleOptionTab *>(opt)) {
+ switch (tab->shape) {
+ case QTabBar::RoundedNorth:
+ case QTabBar::TriangularNorth:
+ case QTabBar::RoundedSouth:
+ case QTabBar::TriangularSouth:
+ r.setRect(tab->rect.left(), tab->rect.top(), subRule.size().width(), opt->rect.height());
+ break;
+ case QTabBar::RoundedWest:
+ case QTabBar::TriangularWest:
+ case QTabBar::RoundedEast:
+ case QTabBar::TriangularEast:
+ r.setRect(tab->rect.left(), tab->rect.top(), opt->rect.width(), subRule.size().height());
+ break;
+ default:
+ break;
+ }
+ r = visualRect(opt->direction, opt->rect, r);
+ }
+ return r;
+ }
+ break;
+ }
+#endif // QT_NO_TABBAR
+
+ case SE_DockWidgetCloseButton:
+ case SE_DockWidgetFloatButton: {
+ PseudoElement pe = (se == SE_DockWidgetCloseButton) ? PseudoElement_DockWidgetCloseButton : PseudoElement_DockWidgetFloatButton;
+ QRenderRule subRule2 = renderRule(w, opt, pe);
+ if (!subRule2.hasPosition())
+ break;
+ QRenderRule subRule = renderRule(w, opt, PseudoElement_DockWidgetTitle);
+ return positionRect(w, subRule, subRule2, pe, opt->rect, opt->direction);
+ }
+
+ default:
+ break;
+ }
+
+ return baseStyle()->subElementRect(se, opt, w);
+}
+
+bool QStyleSheetStyle::event(QEvent *e)
+{
+ return baseStyle()->event(e) || ParentStyle::event(e);
+}
+
+void QStyleSheetStyle::updateStyleSheetFont(QWidget* w) const
+{
+ QWidget *container = containerWidget(w);
+ QRenderRule rule = renderRule(container, PseudoElement_None,
+ PseudoClass_Active | PseudoClass_Enabled | extendedPseudoClass(container));
+ QFont font = rule.font.resolve(w->font());
+
+ if ((!w->isWindow() || w->testAttribute(Qt::WA_WindowPropagation))
+ && isNaturalChild(w) && qobject_cast<QWidget *>(w->parent())) {
+
+ font = font.resolve(static_cast<QWidget *>(w->parent())->font());
+ }
+
+ if (w->data->fnt == font)
+ return;
+
+#ifdef QT3_SUPPORT
+ QFont old = w->data->fnt;
+#endif
+ w->data->fnt = font;
+#if defined(Q_WS_X11)
+ // make sure the font set on this widget is associated with the correct screen
+ //w->data->fnt.x11SetScreen(w->d_func()->xinfo.screen());
+#endif
+
+ QEvent e(QEvent::FontChange);
+ QApplication::sendEvent(w, &e);
+#ifdef QT3_SUPPORT
+ w->fontChange(old);
+#endif
+}
+
+void QStyleSheetStyle::saveWidgetFont(QWidget* w, const QFont& font) const
+{
+ w->setProperty("_q_styleSheetWidgetFont", font);
+}
+
+void QStyleSheetStyle::clearWidgetFont(QWidget* w) const
+{
+ w->setProperty("_q_styleSheetWidgetFont", QVariant(QVariant::Invalid));
+}
+
+// Returns the palette that should be used when the particular widget is focused.
+// This needs to be called by some widgets that do drawing themselves instead
+// of through the style.
+// ### This should be removed ideally by Qt 4.5, and at least by Qt 5, and fixed
+// for good by letting the style draw everything.
+// Returns true if there is a new palette in pal.
+bool QStyleSheetStyle::focusPalette(const QWidget* w, const QStyleOption* opt, QPalette* pal)
+{
+ if (!w || !opt || !pal)
+ return false;
+
+ RECURSION_GUARD(return false)
+
+ w = containerWidget(w);
+
+ QRenderRule rule = renderRule(w, PseudoElement_None, pseudoClass(opt->state) | extendedPseudoClass(w));
+ if (!rule.hasPalette())
+ return false;
+
+ rule.configurePalette(pal, QPalette::NoRole, QPalette::NoRole);
+ return true;
+}
+
+Qt::Alignment QStyleSheetStyle::resolveAlignment(Qt::LayoutDirection layDir, Qt::Alignment src)
+{
+ if (layDir == Qt::LeftToRight || src & Qt::AlignAbsolute)
+ return src;
+
+ if (src & Qt::AlignLeft) {
+ src &= ~Qt::AlignLeft;
+ src |= Qt::AlignRight;
+ } else if (src & Qt::AlignRight) {
+ src &= ~Qt::AlignRight;
+ src |= Qt::AlignLeft;
+ }
+ src |= Qt::AlignAbsolute;
+ return src;
+}
+
+// Returns whether the given QWidget has a "natural" parent, meaning that
+// the parent contains this child as part of its normal operation.
+// An example is the QTabBar inside a QTabWidget.
+// This does not mean that any QTabBar which is a child of QTabWidget will
+// match, only the one that was created by the QTabWidget initialization
+// (and hence has the correct object name).
+bool QStyleSheetStyle::isNaturalChild(const QWidget *w)
+{
+ if (w->objectName().startsWith(QLatin1String("qt_")))
+ return true;
+
+ return false;
+}
+
+QT_END_NAMESPACE
+
+#include "moc_qstylesheetstyle_p.cpp"
+
+#endif // QT_NO_STYLE_STYLESHEET
diff --git a/src/gui/styles/qstylesheetstyle_default.cpp b/src/gui/styles/qstylesheetstyle_default.cpp
new file mode 100644
index 0000000000..742cfe13b9
--- /dev/null
+++ b/src/gui/styles/qstylesheetstyle_default.cpp
@@ -0,0 +1,554 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/* This is the default Qt style sheet.
+
+ IMPORTANT: This style sheet is primarily meant for defining feature
+ capablities of styles. Do NOT add default styling rules here. When in
+ doubt ask the stylesheet maintainer.
+
+ The stylesheet in here used to be in a CSS file, but was moved here to
+ avoid parsing overhead.
+*/
+
+#include "private/qcssparser_p.h"
+#include "qstylesheetstyle_p.h"
+
+#ifndef QT_NO_STYLE_STYLESHEET
+
+QT_BEGIN_NAMESPACE
+
+using namespace QCss;
+
+// This is the class name of the selector.
+// Use an empty string where you would use '*' in CSS.
+// Ex. QHeaderView
+
+#define SET_ELEMENT_NAME(x) \
+ bSelector.elementName = (x)
+
+// This acts as both pseudo state and sub control. The first parameter is the
+// string name, and the second is the PseudoClass_* constant.
+// The sub control specifier is always the first, and has the type
+// PseudoClass_Unknown.
+// If there is no PseudoClass_Unknown as the first pseudo, it is assumed to be
+// a pseudo state.
+// Ex. QComboBox::drop-down:enabled
+// ^ ^
+
+#define ADD_PSEUDO(x, y) \
+ pseudo.type = (y); \
+ pseudo.name = (x); \
+ bSelector.pseudos << pseudo
+
+// This is attributes. The third parameter is AttributeSelector::*
+// Ex. QComboBox[style="QWindowsXPStyle"]
+// ^ ^
+
+#define ADD_ATTRIBUTE_SELECTOR(x, y, z) \
+ attr.name = (x); \
+ attr.value = (y); \
+ attr.valueMatchCriterium = (z); \
+ bSelector.attributeSelectors << attr
+
+// Adds the current basic selector to the rule.
+// Several basic selectors behave as AND (space in CSS).
+
+#define ADD_BASIC_SELECTOR \
+ selector.basicSelectors << bSelector; \
+ bSelector.ids.clear(); \
+ bSelector.pseudos.clear(); \
+ bSelector.attributeSelectors.clear()
+
+// Adds the current selector to the rule.
+// Several selectors behave as OR (comma in CSS).
+
+#define ADD_SELECTOR \
+ styleRule.selectors << selector; \
+ selector.basicSelectors.clear()
+
+// Sets the name of a property.
+// Ex. background: red;
+// ^
+
+#define SET_PROPERTY(x, y) \
+ decl.d->property = (x); \
+ decl.d->propertyId = (y)
+
+// Adds a value to the current property.
+// The first parameter should be Value::KnownIdentifier if the value can be
+// found among the Value_* constants, in which case the second should be that
+// constant. Otherwise the first parameter is Value::Identifier and the second
+// is a string.
+// Adding more values is the same as seperating by spaces in CSS.
+// Ex. border: 2px solid black;
+// ^ ^ ^
+
+#define ADD_VALUE(x, y) \
+ value.type = (x); \
+ value.variant = (y); \
+ decl.d->values << value
+
+// Adds the current declaration to the rule.
+// Ex. border: 2px solid black;
+// \----------------------/
+
+#define ADD_DECLARATION \
+ styleRule.declarations << decl; \
+ decl.d.detach(); \
+ decl.d->values.clear()
+
+// Adds the rule to the stylesheet.
+// Use at the end of every CSS block.
+
+#define ADD_STYLE_RULE \
+ sheet.styleRules << styleRule; \
+ styleRule.selectors.clear(); \
+ styleRule.declarations.clear()
+
+StyleSheet QStyleSheetStyle::getDefaultStyleSheet() const
+{
+ StyleSheet sheet;
+ StyleRule styleRule;
+ BasicSelector bSelector;
+ Selector selector;
+ Declaration decl;
+ Value value;
+ Pseudo pseudo;
+ AttributeSelector attr;
+
+ // pixmap based style doesn't support any features
+ bool styleIsPixmapBased = baseStyle()->inherits("QMacStyle")
+ || baseStyle()->inherits("QWindowsXPStyle")
+ || baseStyle()->inherits("QGtkStyle");
+
+
+ /*QLineEdit {
+ -qt-background-role: base;
+ border: native;
+ -qt-style-features: background-color;
+ }*/
+ {
+ SET_ELEMENT_NAME(QLatin1String("QLineEdit"));
+ ADD_BASIC_SELECTOR;
+ ADD_SELECTOR;
+
+ SET_PROPERTY(QLatin1String("-qt-background-role"), QtBackgroundRole);
+ ADD_VALUE(Value::KnownIdentifier, Value_Base);
+ ADD_DECLARATION;
+
+ SET_PROPERTY(QLatin1String("border"), Border);
+ ADD_VALUE(Value::KnownIdentifier, Value_Native);
+ ADD_DECLARATION;
+
+ SET_PROPERTY(QLatin1String("-qt-style-features"), QtStyleFeatures);
+ ADD_VALUE(Value::Identifier, QString::fromLatin1("background-color"));
+ ADD_DECLARATION;
+
+ ADD_STYLE_RULE;
+ }
+
+ /*QLineEdit:no-frame {
+ border: none;
+ }*/
+ {
+ SET_ELEMENT_NAME(QLatin1String("QLineEdit"));
+ ADD_PSEUDO(QLatin1String("no-frame"), PseudoClass_Frameless);
+ ADD_BASIC_SELECTOR;
+ ADD_SELECTOR;
+
+ SET_PROPERTY(QLatin1String("border"), Border);
+ ADD_VALUE(Value::KnownIdentifier, Value_None);
+ ADD_DECLARATION;
+
+ ADD_STYLE_RULE;
+ }
+
+ /*QLineEdit[style="QCleanlooksStyle"] {
+ padding-top: 2px;
+ padding-bottom: 2px;
+ }*/
+ if (baseStyle()->inherits("QCleanlooksStyle"))
+ {
+ SET_ELEMENT_NAME(QLatin1String("QLineEdit"));
+ ADD_BASIC_SELECTOR;
+ ADD_SELECTOR;
+
+
+ SET_PROPERTY(QLatin1String("padding-top"), PaddingTop);
+ ADD_VALUE(Value::Identifier, QString::fromLatin1("2px"));
+ ADD_DECLARATION;
+
+ SET_PROPERTY(QLatin1String("padding-bottom"), PaddingBottom);
+ ADD_VALUE(Value::Identifier, QString::fromLatin1("2px"));
+ ADD_DECLARATION;
+
+ ADD_STYLE_RULE;
+ }
+
+ /*QLineEdit[style="QWindowsXPStyle"],
+ QLineEdit[style="QWindowsVistaStyle"],
+ QLineEdit[style="QGtkStyle"] {
+ padding-top: 1px;
+ padding-bottom: 1px;
+ }*/
+ if (baseStyle()->inherits("QWindowsXPStyle") || baseStyle()->inherits("QGtkStyle"))
+ {
+ SET_ELEMENT_NAME(QLatin1String("QLineEdit"));
+
+ SET_PROPERTY(QLatin1String("padding-top"), PaddingTop);
+ ADD_VALUE(Value::Identifier, QString::fromLatin1("1px"));
+ ADD_DECLARATION;
+
+ SET_PROPERTY(QLatin1String("padding-bottom"), PaddingBottom);
+ ADD_VALUE(Value::Identifier, QString::fromLatin1("1px"));
+ ADD_DECLARATION;
+
+ ADD_STYLE_RULE;
+ }
+
+ /*QFrame {
+ border: native;
+ }*/
+ {
+ SET_ELEMENT_NAME(QLatin1String("QFrame"));
+ ADD_BASIC_SELECTOR;
+ ADD_SELECTOR;
+
+ SET_PROPERTY(QLatin1String("border"), Border);
+ ADD_VALUE(Value::KnownIdentifier, Value_Native);
+ ADD_DECLARATION;
+
+ ADD_STYLE_RULE;
+ }
+
+ /*QLabel, QToolBox {
+ background: none;
+ border-image: none;
+ }*/
+ {
+ SET_ELEMENT_NAME(QLatin1String("QLabel"));
+ ADD_BASIC_SELECTOR;
+ ADD_SELECTOR;
+
+ SET_ELEMENT_NAME(QLatin1String("QToolBox"));
+ ADD_BASIC_SELECTOR;
+ ADD_SELECTOR;
+
+ SET_PROPERTY(QLatin1String("background"), Background);
+ ADD_VALUE(Value::KnownIdentifier, Value_None);
+ ADD_DECLARATION;
+
+ SET_PROPERTY(QLatin1String("border-image"), BorderImage);
+ ADD_VALUE(Value::KnownIdentifier, Value_None);
+ ADD_DECLARATION;
+
+ ADD_STYLE_RULE;
+ }
+
+ /*QGroupBox {
+ border: native;
+ }*/
+ {
+ SET_ELEMENT_NAME(QLatin1String("QGroupBox"));
+ ADD_BASIC_SELECTOR;
+ ADD_SELECTOR;
+
+ SET_PROPERTY(QLatin1String("border"), Border);
+ ADD_VALUE(Value::KnownIdentifier, Value_Native);
+ ADD_DECLARATION;
+
+ ADD_STYLE_RULE;
+ }
+
+
+ /*QToolTip {
+ -qt-background-role: window;
+ border: native;
+ }*/
+ {
+ SET_ELEMENT_NAME(QLatin1String("QToolTip"));
+ ADD_BASIC_SELECTOR;
+ ADD_SELECTOR;
+
+ SET_PROPERTY(QLatin1String("-qt-background-role"), QtBackgroundRole);
+ ADD_VALUE(Value::KnownIdentifier, Value_Window);
+ ADD_DECLARATION;
+
+ SET_PROPERTY(QLatin1String("border"), Border);
+ ADD_VALUE(Value::KnownIdentifier, Value_Native);
+ ADD_DECLARATION;
+
+ ADD_STYLE_RULE;
+ }
+
+ /*QPushButton, QToolButton {
+ border-style: native;
+ -qt-style-features: background-color; //only for not pixmap based styles
+ }*/
+ {
+ SET_ELEMENT_NAME(QLatin1String("QPushButton"));
+ ADD_BASIC_SELECTOR;
+ ADD_SELECTOR;
+
+ SET_ELEMENT_NAME(QLatin1String("QToolButton"));
+ ADD_BASIC_SELECTOR;
+ ADD_SELECTOR;
+
+ SET_PROPERTY(QLatin1String("border-style"), BorderStyles);
+ ADD_VALUE(Value::KnownIdentifier, Value_Native);
+ ADD_DECLARATION;
+
+ if (!styleIsPixmapBased) {
+ SET_PROPERTY(QLatin1String("-qt-style-features"), QtStyleFeatures);
+ ADD_VALUE(Value::Identifier, QString::fromLatin1("background-color"));
+ ADD_DECLARATION;
+ }
+
+
+ ADD_STYLE_RULE;
+ }
+
+
+ /*QComboBox {
+ border: native;
+ -qt-style-features: background-color background-gradient; //only for not pixmap based styles
+ -qt-background-role: base;
+ }*/
+
+ {
+ SET_ELEMENT_NAME(QLatin1String("QComboBox"));
+ ADD_BASIC_SELECTOR;
+ ADD_SELECTOR;
+
+ SET_PROPERTY(QLatin1String("border"), Border);
+ ADD_VALUE(Value::KnownIdentifier, Value_Native);
+ ADD_DECLARATION;
+
+ if (!styleIsPixmapBased) {
+ SET_PROPERTY(QLatin1String("-qt-style-features"), QtStyleFeatures);
+ ADD_VALUE(Value::Identifier, QString::fromLatin1("background-color"));
+ ADD_VALUE(Value::Identifier, QString::fromLatin1("background-gradient"));
+ ADD_DECLARATION;
+ }
+
+ SET_PROPERTY(QLatin1String("-qt-background-role"), QtBackgroundRole);
+ ADD_VALUE(Value::KnownIdentifier, Value_Base);
+ ADD_DECLARATION;
+
+ ADD_STYLE_RULE;
+ }
+
+ /*QComboBox[style="QPlastiqueStyle"][readOnly="true"],
+ QComboBox[style="QCleanlooksStyle"][readOnly="true"]
+ {
+ -qt-background-role: button;
+ }*/
+ if (baseStyle()->inherits("QPlastiqueStyle") || baseStyle()->inherits("QCleanlooksStyle"))
+ {
+ SET_ELEMENT_NAME(QLatin1String("QComboBox"));
+ ADD_ATTRIBUTE_SELECTOR(QLatin1String("readOnly"), QLatin1String("true"), AttributeSelector::MatchEqual);
+ ADD_BASIC_SELECTOR;
+ ADD_SELECTOR;
+
+ SET_PROPERTY(QLatin1String("-qt-background-role"), QtBackgroundRole);
+ ADD_VALUE(Value::KnownIdentifier, Value_Button);
+ ADD_DECLARATION;
+
+ ADD_STYLE_RULE;
+ }
+
+ /*QAbstractSpinBox {
+ border: native;
+ -qt-style-features: background-color;
+ -qt-background-role: base;
+ }*/
+ {
+ SET_ELEMENT_NAME(QLatin1String("QAbstractSpinBox"));
+ ADD_BASIC_SELECTOR;
+ ADD_SELECTOR;
+
+ SET_PROPERTY(QLatin1String("border"), Border);
+ ADD_VALUE(Value::KnownIdentifier, Value_Native);
+ ADD_DECLARATION;
+
+ SET_PROPERTY(QLatin1String("-qt-style-features"), QtStyleFeatures);
+ ADD_VALUE(Value::Identifier, QString::fromLatin1("background-color"));
+ ADD_DECLARATION;
+
+ SET_PROPERTY(QLatin1String("-qt-background-role"), QtBackgroundRole);
+ ADD_VALUE(Value::KnownIdentifier, Value_Base);
+ ADD_DECLARATION;
+
+ ADD_STYLE_RULE;
+ }
+
+ /*QMenu {
+ -qt-background-role: window;
+ }*/
+ {
+ SET_ELEMENT_NAME(QLatin1String("QMenu"));
+ ADD_BASIC_SELECTOR;
+ ADD_SELECTOR;
+
+ SET_PROPERTY(QLatin1String("-qt-background-role"), QtBackgroundRole);
+ ADD_VALUE(Value::KnownIdentifier, Value_Window);
+ ADD_DECLARATION;
+
+ ADD_STYLE_RULE;
+ }
+ /*QMenu::item {
+ -qt-style-features: background-color;
+ }*/
+ if (!styleIsPixmapBased) {
+ SET_ELEMENT_NAME(QLatin1String("QMenu"));
+ ADD_PSEUDO(QLatin1String("item"), PseudoClass_Unknown);
+ ADD_BASIC_SELECTOR;
+ ADD_SELECTOR;
+
+ SET_PROPERTY(QLatin1String("-qt-style-features"), QtStyleFeatures);
+ ADD_VALUE(Value::Identifier, QString::fromLatin1("background-color"));
+ ADD_DECLARATION;
+
+ ADD_STYLE_RULE;
+ }
+
+ /*QHeaderView {
+ -qt-background-role: window;
+ }*/
+ {
+ SET_ELEMENT_NAME(QLatin1String("QHeaderView"));
+ ADD_BASIC_SELECTOR;
+ ADD_SELECTOR;
+
+ SET_PROPERTY(QLatin1String("-qt-background-role"), QtBackgroundRole);
+ ADD_VALUE(Value::KnownIdentifier, Value_Window);
+ ADD_DECLARATION;
+
+ ADD_STYLE_RULE;
+ }
+
+ /*QTableCornerButton::section, QHeaderView::section {
+ -qt-background-role: button;
+ -qt-style-features: background-color; //if style is not pixmap based
+ border: native;
+ }*/
+ {
+ SET_ELEMENT_NAME(QLatin1String("QTableCornerButton"));
+ ADD_PSEUDO(QLatin1String("section"), PseudoClass_Unknown);
+ ADD_BASIC_SELECTOR;
+ ADD_SELECTOR;
+
+ SET_ELEMENT_NAME(QLatin1String("QHeaderView"));
+ ADD_PSEUDO(QLatin1String("section"), PseudoClass_Unknown);
+ ADD_BASIC_SELECTOR;
+ ADD_SELECTOR;
+
+ SET_PROPERTY(QLatin1String("-qt-background-role"), QtBackgroundRole);
+ ADD_VALUE(Value::KnownIdentifier, Value_Button);
+ ADD_DECLARATION;
+
+ if (!styleIsPixmapBased) {
+ SET_PROPERTY(QLatin1String("-qt-style-features"), QtStyleFeatures);
+ ADD_VALUE(Value::Identifier, QString::fromLatin1("background-color"));
+ ADD_DECLARATION;
+ }
+
+ SET_PROPERTY(QLatin1String("border"), Border);
+ ADD_VALUE(Value::KnownIdentifier, Value_Native);
+ ADD_DECLARATION;
+
+ ADD_STYLE_RULE;
+ }
+
+ /*QProgressBar {
+ -qt-background-role: base;
+ }*/
+ {
+ SET_ELEMENT_NAME(QLatin1String("QProgressBar"));
+ ADD_BASIC_SELECTOR;
+ ADD_SELECTOR;
+
+ SET_PROPERTY(QLatin1String("-qt-background-role"), QtBackgroundRole);
+ ADD_VALUE(Value::KnownIdentifier, Value_Base);
+ ADD_DECLARATION;
+
+ ADD_STYLE_RULE;
+ }
+
+ /*QScrollBar {
+ -qt-background-role: window;
+ }*/
+ {
+ SET_ELEMENT_NAME(QLatin1String("QScrollBar"));
+ ADD_BASIC_SELECTOR;
+ ADD_SELECTOR;
+
+ SET_PROPERTY(QLatin1String("-qt-background-role"), QtBackgroundRole);
+ ADD_VALUE(Value::KnownIdentifier, Value_Window);
+ ADD_DECLARATION;
+
+ ADD_STYLE_RULE;
+ }
+
+ /*QDockWidget {
+ border: native;
+ }*/
+ {
+ SET_ELEMENT_NAME(QLatin1String("QDockWidget"));
+ ADD_BASIC_SELECTOR;
+ ADD_SELECTOR;
+
+ SET_PROPERTY(QLatin1String("border"), Border);
+ ADD_VALUE(Value::KnownIdentifier, Value_Native);
+ ADD_DECLARATION;
+
+ ADD_STYLE_RULE;
+ }
+
+ sheet.origin = StyleSheetOrigin_UserAgent;
+ sheet.buildIndexes();
+ return sheet;
+}
+
+#endif // #ifndef QT_NO_STYLE_STYLESHEET
+
+QT_END_NAMESPACE
diff --git a/src/gui/styles/qstylesheetstyle_p.h b/src/gui/styles/qstylesheetstyle_p.h
new file mode 100644
index 0000000000..1f61445fd9
--- /dev/null
+++ b/src/gui/styles/qstylesheetstyle_p.h
@@ -0,0 +1,191 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSTYLESHEETSTYLE_P_H
+#define QSTYLESHEETSTYLE_P_H
+
+#include "QtGui/qwindowsstyle.h"
+
+#ifndef QT_NO_STYLE_STYLESHEET
+
+#include "QtGui/qstyleoption.h"
+#include "QtCore/qhash.h"
+#include "QtGui/qevent.h"
+#include "QtCore/qvector.h"
+#include "QtGui/qapplication.h"
+#include "private/qcssparser_p.h"
+#include "QtGui/qbrush.h"
+
+QT_BEGIN_NAMESPACE
+
+//
+// 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.
+//
+
+class QRenderRule;
+class QAbstractScrollArea;
+class QStyleSheetStylePrivate;
+class QStyleOptionTitleBar;
+
+class Q_AUTOTEST_EXPORT QStyleSheetStyle : public QWindowsStyle
+{
+ typedef QWindowsStyle ParentStyle;
+
+ Q_OBJECT
+public:
+ QStyleSheetStyle(QStyle *baseStyle);
+ ~QStyleSheetStyle();
+
+ void drawComplexControl(ComplexControl cc, const QStyleOptionComplex *opt, QPainter *p,
+ const QWidget *w = 0) const;
+ void drawControl(ControlElement element, const QStyleOption *opt, QPainter *p,
+ const QWidget *w = 0) const;
+ void drawItemPixmap(QPainter *painter, const QRect &rect, int alignment, const QPixmap &pixmap) const;
+ void drawItemText(QPainter *painter, const QRect& rect, int alignment, const QPalette &pal,
+ bool enabled, const QString& text, QPalette::ColorRole textRole = QPalette::NoRole) const;
+ void drawPrimitive(PrimitiveElement pe, const QStyleOption *opt, QPainter *p,
+ const QWidget *w = 0) const;
+ QPixmap generatedIconPixmap(QIcon::Mode iconMode, const QPixmap &pixmap,
+ const QStyleOption *option) const;
+ SubControl hitTestComplexControl(ComplexControl cc, const QStyleOptionComplex *opt,
+ const QPoint &pt, const QWidget *w = 0) const;
+ QRect itemPixmapRect(const QRect &rect, int alignment, const QPixmap &pixmap) const;
+ QRect itemTextRect(const QFontMetrics &metrics, const QRect &rect, int alignment, bool enabled,
+ const QString &text) const;
+ int pixelMetric(PixelMetric metric, const QStyleOption *option = 0, const QWidget *widget = 0) const;
+ void polish(QWidget *widget);
+ void polish(QApplication *app);
+ void polish(QPalette &pal);
+ QSize sizeFromContents(ContentsType ct, const QStyleOption *opt,
+ const QSize &contentsSize, const QWidget *widget = 0) const;
+ QPalette standardPalette() const;
+ QPixmap standardPixmap(StandardPixmap standardPixmap, const QStyleOption *option = 0,
+ const QWidget *w = 0 ) const;
+ int layoutSpacing(QSizePolicy::ControlType control1, QSizePolicy::ControlType control2,
+ Qt::Orientation orientation, const QStyleOption *option = 0,
+ const QWidget *widget = 0) const;
+ int styleHint(StyleHint sh, const QStyleOption *opt = 0, const QWidget *w = 0,
+ QStyleHintReturn *shret = 0) const;
+ QRect subElementRect(SubElement r, const QStyleOption *opt, const QWidget *widget = 0) const;
+ QRect subControlRect(ComplexControl cc, const QStyleOptionComplex *opt, SubControl sc,
+ const QWidget *w = 0) const;
+
+ // These functions are called from QApplication/QWidget. Be careful.
+ QStyle *baseStyle() const;
+ void repolish(QWidget *widget);
+ void repolish(QApplication *app);
+
+ void unpolish(QWidget *widget);
+ void unpolish(QApplication *app);
+
+ QStyle *base;
+ void ref() { ++refcount; }
+ void deref() { Q_ASSERT(refcount > 0); if (!--refcount) delete this; }
+
+ void updateStyleSheetFont(QWidget* w) const;
+ void saveWidgetFont(QWidget* w, const QFont& font) const;
+ void clearWidgetFont(QWidget* w) const;
+
+ bool focusPalette(const QWidget* w, const QStyleOption* opt, QPalette* pal);
+
+protected Q_SLOTS:
+ QIcon standardIconImplementation(StandardPixmap standardIcon, const QStyleOption *opt = 0,
+ const QWidget *widget = 0) const;
+ int layoutSpacingImplementation(QSizePolicy::ControlType control1,
+ QSizePolicy::ControlType control2,
+ Qt::Orientation orientation,
+ const QStyleOption *option = 0,
+ const QWidget *widget = 0) const;
+
+protected:
+ bool event(QEvent *e);
+
+private Q_SLOTS:
+ void widgetDestroyed(QObject *);
+
+private:
+ int refcount;
+
+ friend class QRenderRule;
+ int nativeFrameWidth(const QWidget *);
+ QRenderRule renderRule(const QWidget *, int, quint64 = 0) const;
+ QRenderRule renderRule(const QWidget *, const QStyleOption *, int = 0) const;
+ QSize defaultSize(const QWidget *, QSize, const QRect&, int) const;
+ QRect positionRect(const QWidget *, const QRenderRule&, const QRenderRule&, int,
+ const QRect&, Qt::LayoutDirection) const;
+ QRect positionRect(const QWidget *w, const QRenderRule &rule2, int pe,
+ const QRect &originRect, Qt::LayoutDirection dir) const;
+
+ mutable QCss::Parser parser;
+
+ void setPalette(QWidget *);
+ void unsetPalette(QWidget *);
+ void setProperties(QWidget *);
+ void setGeometry(QWidget *);
+ QVector<QCss::StyleRule> styleRules(const QWidget *w) const;
+ bool hasStyleRule(const QWidget *w, int part) const;
+
+ QHash<QStyle::SubControl, QRect> titleBarLayout(const QWidget *w, const QStyleOptionTitleBar *tb) const;
+
+ QCss::StyleSheet getDefaultStyleSheet() const;
+
+ static Qt::Alignment resolveAlignment(Qt::LayoutDirection, Qt::Alignment);
+ static bool isNaturalChild(const QWidget *w);
+ bool initWidget(const QWidget *w) const;
+public:
+ static int numinstances;
+
+private:
+ Q_DISABLE_COPY(QStyleSheetStyle)
+ Q_DECLARE_PRIVATE(QStyleSheetStyle)
+};
+
+
+QT_END_NAMESPACE
+#endif // QT_NO_STYLE_STYLESHEET
+#endif // QSTYLESHEETSTYLE_P_H
diff --git a/src/gui/styles/qwindowscestyle.cpp b/src/gui/styles/qwindowscestyle.cpp
new file mode 100644
index 0000000000..5cb72f9712
--- /dev/null
+++ b/src/gui/styles/qwindowscestyle.cpp
@@ -0,0 +1,2422 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qwindowscestyle.h"
+
+#if !defined(QT_NO_STYLE_WINDOWSCE) || defined(QT_PLUGIN)
+
+#include "qpainterpath.h"
+#include "qapplication.h"
+#include "qdockwidget.h"
+#include "qtoolbar.h"
+#include "qpaintengine.h"
+#include "qpainter.h"
+#include "qstyleoption.h"
+#include "qwindowscestyle_p.h"
+#include "qdebug.h"
+
+QT_BEGIN_NAMESPACE
+
+static const int windowsItemFrame = 2; // menu item frame width
+static const int windowsSepHeight = 9; // separator item height
+static const int windowsItemHMargin = 3; // menu item hor text margin
+static const int windowsItemVMargin = 2; // menu item ver text margin
+static const int windowsArrowHMargin = 6; // arrow horizontal margin
+static const int windowsRightBorder = 15; // right border on windows
+static const int windowsCheckMarkWidth = 14; // checkmarks width on windows
+
+static const int windowsCEitemViewCheckBoxSize = 14;
+static const int windowsCEFrameGroupBoxOffset = 9;
+static const int windowsCEIndicatorSize = 14;
+static const int windowsCEExclusiveIndicatorSize = 14;
+static const int windowsCESliderThickness = 24;
+static const int windowsCEIconSize = 16;
+
+static const QColor windowsCECheckBoxGradientColorBegin = QColor(222, 224, 214);
+static const QColor windowsCECheckBoxGradientColorEnd = QColor(255, 255, 255);
+
+enum QSliderDirection { SlUp, SlDown, SlLeft, SlRight };
+
+QWindowsCEStyle::QWindowsCEStyle() : QWindowsStyle() {
+ qApp->setEffectEnabled(Qt::UI_FadeMenu, false);
+ qApp->setEffectEnabled(Qt::UI_AnimateMenu, false);
+}
+
+void QWindowsCEStyle::drawPrimitive(PrimitiveElement element, const QStyleOption *option,
+ QPainter *painter, const QWidget *widget) const {
+
+ bool doRestore = false;
+ QRect rect = option->rect;
+
+ switch (element) {
+ case PE_PanelButtonTool: {
+ if (
+#ifndef QT_NO_TOOLBAR
+ (widget && qobject_cast<QToolBar*>(widget->parentWidget())) ||
+#endif
+#ifndef QT_NO_DOCKWIDGET
+ (widget && widget->inherits("QDockWidgetTitleButton")) ||
+#endif
+ (option->state & (State_Sunken | State_On)))
+ QWindowsCEStylePrivate::drawWinCEButton(painter, option->rect.adjusted(0, 0, 0, 0),
+ option->palette, option->state & (State_Sunken | State_On),
+ &option->palette.button());
+ if (option->state & (State_On)){
+ QBrush fill = QBrush(option->palette.midlight().color(), Qt::Dense4Pattern);
+ painter->fillRect(option->rect.adjusted(windowsItemFrame , windowsItemFrame ,
+ -windowsItemFrame , -windowsItemFrame ), fill);
+ }
+ break; }
+ case PE_IndicatorButtonDropDown:
+ QWindowsCEStylePrivate::drawWinCEButton(painter, option->rect, option->palette,
+ option->state & (State_Sunken | State_On),
+ &option->palette.brush(QPalette::Button));
+ break;
+#ifndef QT_NO_TABBAR
+ case PE_IndicatorTabTear:
+ if (const QStyleOptionTab *tab = qstyleoption_cast<const QStyleOptionTab *>(option)) {
+ bool rtl = tab->direction == Qt::RightToLeft;
+ QRect rect = tab->rect;
+ QPainterPath path;
+ rect.setTop(rect.top() + ((tab->state & State_Selected) ? 1 : 3));
+ rect.setBottom(rect.bottom() - ((tab->state & State_Selected) ? 0 : 2));
+ path.moveTo(QPoint(rtl ? rect.right() : rect.left(), rect.top()));
+ int count = 3;
+ for(int jags = 1; jags <= count; ++jags, rtl = !rtl)
+ path.lineTo(QPoint(rtl ? rect.left() : rect.right(), rect.top() + jags * rect.height()/count));
+
+ painter->setPen(QPen(tab->palette.light(), qreal(.8)));
+ painter->setBrush(tab->palette.background());
+ painter->setRenderHint(QPainter::Antialiasing);
+ painter->drawPath(path);
+ }
+ break;
+#endif //QT_NO_TABBAR
+#ifndef QT_NO_TOOLBAR
+ case PE_IndicatorToolBarSeparator:
+ //nothing to draw on WindowsCE
+ break;
+ case PE_IndicatorToolBarHandle:
+ painter->save();
+ painter->translate(option->rect.x(), option->rect.y());
+ if (option->state & State_Horizontal) {
+ int x = option->rect.width() / 2 - 4;
+ if (QApplication::layoutDirection() == Qt::RightToLeft)
+ x -= 2;
+ if (option->rect.height() > 4) {
+ QWindowsCEStylePrivate::drawWinCEButton(painter,x - 1, 0, 7, option->rect.height(),
+ option->palette, false, 0);
+ QWindowsCEStylePrivate::drawWinCEPanel(painter, x, 1, 3, option->rect.height() - 1,
+ option->palette, false, 0);
+ QWindowsCEStylePrivate::drawWinCEPanel(painter, x + 3, 1, 3, option->rect.height() - 1,
+ option->palette, false, 0);
+ painter->setPen(option->palette.button().color());
+ painter->drawLine(x + 4, 2, x + 4,option->rect.height() - 2);
+ }
+ } else {
+ if (option->rect.width() > 4) {
+ int y = option->rect.height() / 2 - 4;
+ QWindowsCEStylePrivate::drawWinCEPanel(painter, 2, y, option->rect.width() - 2, 3,
+ option->palette, false, 0);
+ QWindowsCEStylePrivate::drawWinCEPanel(painter, 2, y + 3, option->rect.width() - 2, 3,
+ option->palette, false, 0);
+ }
+ }
+ painter->restore();
+ break;
+
+#endif // QT_NO_TOOLBAR
+ case PE_FrameButtonTool: {
+#ifndef QT_NO_DOCKWIDGET
+ if (widget && widget->inherits("QDockWidgetTitleButton")) {
+ if (const QDockWidget *dw = qobject_cast<const QDockWidget *>(widget->parent()))
+ if (dw->isFloating()){
+ QWindowsCEStylePrivate::drawWinCEButton(painter, option->rect.adjusted(1, 1, 0, 0),
+ option->palette, option->state & (State_Sunken | State_On),
+ &option->palette.button());
+ return;
+ }
+ }
+#endif // QT_NO_DOCKWIDGET
+ QBrush fill;
+ bool stippled;
+ bool panel = (element == PE_PanelButtonTool);
+ if ((!(option->state & State_Sunken ))
+ && (!(option->state & State_Enabled)
+ || ((option->state & State_Enabled ) && !(option->state & State_MouseOver)))
+ && (option->state & State_On)) {
+ fill = QBrush(option->palette.light().color(), Qt::Dense4Pattern);
+ stippled = true;
+ } else {
+ fill = option->palette.brush(QPalette::Button);
+ stippled = false;
+ }
+ if (option->state & (State_Raised | State_Sunken | State_On)) {
+ if (option->state & State_AutoRaise) {
+ if(option->state & (State_Enabled | State_Sunken | State_On)){
+ if (panel)
+ QWindowsCEStylePrivate::drawWinCEPanel(painter, option->rect, option->palette,
+ option->state & (State_Sunken | State_On), &fill);
+ else
+ qDrawShadeRect(painter, option->rect, option->palette,
+ option->state & (State_Sunken | State_On), 1);
+ }
+ if (stippled) {
+ painter->setPen(option->palette.button().color());
+ painter->drawRect(option->rect.adjusted(1, 1, -2, -2));
+ }
+ } else {
+ QWindowsCEStylePrivate::drawWinCEButton(painter, option->rect, option->palette,
+ option->state & (State_Sunken | State_On), panel ? &fill : 0);
+ }
+ } else {
+ painter->fillRect(option->rect, fill);
+ }
+ break; }
+
+ case PE_PanelButtonBevel: {
+ QBrush fill;
+ bool panel = element != PE_FrameButtonBevel;
+ painter->setBrushOrigin(option->rect.topLeft());
+ if (!(option->state & State_Sunken) && (option->state & State_On))
+ fill = QBrush(option->palette.light().color(), Qt::Dense4Pattern);
+ else
+ fill = option->palette.brush(QPalette::Button);
+
+ if (option->state & (State_Raised | State_On | State_Sunken)) {
+ QWindowsCEStylePrivate::drawWinCEButton(painter, option->rect, option->palette,
+ option->state & (State_Sunken | State_On),
+ panel ? &fill : 0); ;
+ } else {
+ if (panel)
+ painter->fillRect(option->rect, fill);
+ else
+ painter->drawRect(option->rect);
+ }
+ break; }
+
+ case PE_FrameGroupBox:
+ if (const QStyleOptionFrame *frame = qstyleoption_cast<const QStyleOptionFrame *>(option)) {
+ QRect fr = frame->rect;
+ painter->setPen(frame->palette.shadow().color());
+ painter->drawRect(fr.x(), fr.y(), fr.x() + fr.width() - 1,
+ fr.y() + fr.height() - windowsCEFrameGroupBoxOffset);
+ }
+ break;
+
+ case PE_IndicatorCheckBox: {
+ QBrush fill;
+ if (option->state & State_NoChange)
+ fill = QBrush(option->palette.base().color(), Qt::Dense4Pattern);
+ else if (option->state & State_Sunken)
+ fill = option->palette.button();
+ else if (option->state & State_Enabled)
+ fill = option->palette.base();
+ else
+ fill = option->palette.background();
+ painter->save();
+ doRestore = true;
+ painter->fillRect(option->rect,fill);
+ painter->setPen(option->palette.dark().color());
+ painter->drawRect(option->rect);
+ painter->setPen(option->palette.shadow().color());
+ painter->drawLine(option->rect.x() + 1,option->rect.y() + 1,
+ option->rect.x() + option->rect.width() - 1, option->rect.y() + 1);
+ painter->drawLine(option->rect.x() + 1,option->rect.y() + 1,
+ option->rect.x() + 1, option->rect.y() + option->rect.height() - 1);
+ //fall through...
+ }
+ case PE_IndicatorViewItemCheck:
+ case PE_Q3CheckListIndicator: {
+ if (!doRestore) {
+ painter->save();
+ doRestore = true;
+ }
+ int arrowSize= 2;
+ if (element == PE_Q3CheckListIndicator || element == PE_IndicatorViewItemCheck) {
+ QLinearGradient linearGradient(QPoint(option->rect.x(),option->rect.y()), QPoint(option->rect.x()+option->rect.width(),
+ option->rect.y()+option->rect.height()));
+ linearGradient.setColorAt(0, windowsCECheckBoxGradientColorBegin);
+ linearGradient.setColorAt(1, windowsCECheckBoxGradientColorEnd);
+ painter->setBrush(linearGradient);
+ painter->setPen(Qt::NoPen);
+ if (option->state & State_NoChange)
+ painter->setBrush(option->palette.brush(QPalette::Button));
+ painter->setPen(option->palette.link().color());
+ painter->drawRect(option->rect.x(), option->rect.y(), windowsCEitemViewCheckBoxSize, windowsCEitemViewCheckBoxSize);
+ painter->setPen(option->palette.brightText().color());
+ arrowSize= 3;
+ }
+ if (!(option->state & State_Off)) {
+ QLineF lines[9];
+ int i, xx, yy;
+ xx = option->rect.x() + 4;
+ yy = option->rect.y() + 6;
+ for (i = 0; i < 4; ++i) {
+ lines[i] = QLineF(xx, yy, xx, yy + arrowSize);
+ ++xx;
+ ++yy;
+ }
+ yy -= 2;
+ for (i = 4; i < 9; ++i) {
+ lines[i] = QLineF(xx, yy, xx, yy + arrowSize);
+ ++xx;
+ --yy;
+ }
+ painter->drawLines(lines, 9);
+ }
+ if (doRestore)
+ painter->restore();
+
+ break; }
+ case PE_IndicatorRadioButton: {
+ QRect ir = option->rect;
+ painter->save();
+ painter->setPen(Qt::NoPen);
+ painter->setBrush(option->palette.light());
+ painter->drawEllipse(option->rect);
+ painter->setPen(option->palette.shadow().color());
+ painter->setBrush(option->palette.shadow().color());
+ painter->drawArc(option->rect, 0, 360 * 16);
+ painter->drawArc(option->rect.x() + 1, option->rect.y() + 1, option->rect.width() - 2,
+ option->rect.height() - 2, 40 * 16, 180 * 16);
+ painter->setPen(option->palette.light().color());
+ painter->drawPoint(option->rect.x() + 11, option->rect.y() + 3);
+ painter->drawPoint(option->rect.x() + 3,option->rect.y() + 3);
+ painter->setPen(option->palette.shadow().color());
+ painter->drawPoint(option->rect.x() +3,option->rect.y() + 12);
+ if (option->state & (State_Sunken | State_On)) {
+ painter->setPen(Qt::NoPen);
+ painter->setBrush(option->palette.text());
+ painter->drawEllipse(option->rect.x() +3,option->rect.y()+ 2,9,10);
+ }
+ painter->restore();
+ break; }
+ case PE_PanelMenuBar:
+ painter->save();
+ painter->setPen(option->palette.shadow().color());
+ painter->drawRect(option->rect);
+ painter->restore();
+ break;
+ case PE_PanelButtonCommand:
+ if (const QStyleOptionButton *btn = qstyleoption_cast<const QStyleOptionButton *>(option)) {
+ QBrush fill;
+ State flags = option->state;
+ QPalette pal = option->palette;
+ QRect r = option->rect;
+ if (! (flags & State_Sunken) && (flags & State_On))
+ fill = QBrush(pal.light().color(), Qt::Dense4Pattern);
+ else
+ fill = pal.brush(QPalette::Button);
+ if (btn->features & QStyleOptionButton::DefaultButton && flags & State_Sunken) {
+ painter->setPen(pal.dark().color());
+ painter->setBrush(fill);
+ painter->drawRect(r.adjusted(0, 0, -1, -1));
+ } else if (flags & (State_Raised | State_Sunken | State_On | State_Sunken)) {
+ QWindowsCEStylePrivate::drawWinCEButton(painter, r, pal, flags & (State_Sunken | State_On),
+ &fill);
+ } else {
+ painter->fillRect(r, fill);
+ }
+
+ }
+ break;
+ case PE_FrameDefaultButton: {
+ painter->setPen(option->palette.shadow().color());
+ QRect rect = option->rect;
+ rect.adjust(0, 0, -1, -1);
+ painter->drawRect(rect);
+ break; }
+ case PE_IndicatorSpinPlus:
+ case PE_IndicatorSpinMinus: {
+ QRect r = option->rect;
+ int fw = pixelMetric(PM_DefaultFrameWidth, option, widget)+2;
+ QRect br = r.adjusted(fw, fw, -fw, -fw);
+ int offset = (option->state & State_Sunken) ? 1 : 0;
+ int step = (br.width() + 4) / 5;
+ painter->fillRect(br.x() + offset, br.y() + offset +br.height() / 2 - step / 2,
+ br.width(), step,
+ option->palette.buttonText());
+ if (element == PE_IndicatorSpinPlus)
+ painter->fillRect(br.x() + br.width() / 2 - step / 2 + offset, br.y() + offset+4,
+ step, br.height()-7,
+ option->palette.buttonText());
+ break; }
+ case PE_IndicatorSpinUp:
+ case PE_IndicatorSpinDown: {
+ painter->save();
+ QPoint points[7];
+ switch (element) {
+ case PE_IndicatorSpinUp:
+ points[0] = QPoint(-2, -4);
+ points[1] = QPoint(-2, 2);
+ points[2] = QPoint(-1, -3);
+ points[3] = QPoint(-1, 1);
+ points[4] = QPoint(0, -2);
+ points[5] = QPoint(0, 0);
+ points[6] = QPoint(1, -1);
+ break;
+ case PE_IndicatorSpinDown:
+ points[0] = QPoint(0, -4);
+ points[1] = QPoint(0, 2);
+ points[2] = QPoint(-1, -3);
+ points[3] = QPoint(-1, 1);
+ points[4] = QPoint(-2, -2);
+ points[5] = QPoint(-2, 0);
+ points[6] = QPoint(-3, -1);
+ break;
+ default:
+ break;
+ }
+ if (option->state & State_Sunken)
+ painter->translate(pixelMetric(PM_ButtonShiftHorizontal),
+ pixelMetric(PM_ButtonShiftVertical));
+ if (option->state & State_Enabled) {
+ painter->translate(option->rect.x() + option->rect.width() / 2,
+ option->rect.y() + option->rect.height() / 2);
+ painter->setPen(option->palette.buttonText().color());
+ painter->drawLine(points[0], points[1]);
+ painter->drawLine(points[2], points[3]);
+ painter->drawLine(points[4], points[5]);
+ painter->drawPoint(points[6]);
+ } else {
+ painter->translate(option->rect.x() + option->rect.width() / 2 + 1,
+ option->rect.y() + option->rect.height() / 2 + 1);
+ painter->setPen(option->palette.light().color());
+ painter->drawLine(points[0], points[1]);
+ painter->drawLine(points[2], points[3]);
+ painter->drawLine(points[4], points[5]);
+ painter->drawPoint(points[6]);
+ painter->translate(-1, -1);
+ painter->setPen(option->palette.mid().color());
+ painter->drawLine(points[0], points[1]);
+ painter->drawLine(points[2], points[3]);
+ painter->drawLine(points[4], points[5]);
+ painter->drawPoint(points[6]);
+ }
+
+ painter->restore();
+ break; }
+ case PE_IndicatorArrowUp:
+ case PE_IndicatorArrowDown:
+ case PE_IndicatorArrowRight:
+ case PE_IndicatorArrowLeft: {
+ painter->save();
+ QPoint points[9];
+ switch (element) {
+ case PE_IndicatorArrowUp:
+
+ points[0] = QPoint(-4, 2);
+ points[1] = QPoint(4, 2);
+ points[2] = QPoint(-3, 1);
+ points[3] = QPoint(3, 1);
+ points[4] = QPoint(-2, 0);
+ points[5] = QPoint(2, 0);
+ points[6] = QPoint(-1, -1);
+ points[7] = QPoint(1, -1);
+ points[8] = QPoint(0, -2);
+ break;
+ case PE_IndicatorArrowDown:
+
+ points[0] = QPoint(-4, -2);
+ points[1] = QPoint(4, -2);
+ points[2] = QPoint(-3, -1);
+ points[3] = QPoint(3, -1);
+ points[4] = QPoint(-2, 0);
+ points[5] = QPoint(2, 0);
+ points[6] = QPoint(-1, 1);
+ points[7] = QPoint(1, 1);
+ points[8] = QPoint(0, 2);
+ break;
+ case PE_IndicatorArrowRight:
+ points[0] = QPoint(-3, -4);
+ points[1] = QPoint(-3, 4);
+ points[2] = QPoint(-2, -3);
+ points[3] = QPoint(-2, 3);
+ points[4] = QPoint(-1, -2);
+ points[5] = QPoint(-1, 2);
+ points[6] = QPoint(0, -1);
+ points[7] = QPoint(0, 1);
+ points[8] = QPoint(1, 0);
+ break;
+ case PE_IndicatorArrowLeft:
+ points[0] = QPoint(1, -4);
+ points[1] = QPoint(1, 4);
+ points[2] = QPoint(0, -3);
+ points[3] = QPoint(0, 3);
+ points[4] = QPoint(-1, -2);
+ points[5] = QPoint(-1, 2);
+ points[6] = QPoint(-2, -1);
+ points[7] = QPoint(-2, 1);
+ points[8] = QPoint(-3, 0);
+ break;
+ default:
+ break;
+ }
+ if (option->state & State_Sunken)
+ painter->translate(pixelMetric(PM_ButtonShiftHorizontal),
+ pixelMetric(PM_ButtonShiftVertical));
+ if (option->state & State_Enabled) {
+ painter->translate(option->rect.x() + option->rect.width() / 2,
+ option->rect.y() + option->rect.height() / 2);
+ painter->setPen(option->palette.buttonText().color());
+ painter->drawLine(points[0], points[1]);
+ painter->drawLine(points[2], points[3]);
+ painter->drawLine(points[4], points[5]);
+ painter->drawLine(points[6], points[7]);
+ painter->drawPoint(points[8]);
+ } else {
+ painter->translate(option->rect.x() + option->rect.width() / 2 + 1,
+ option->rect.y() + option->rect.height() / 2 + 1);
+ painter->setPen(option->palette.light().color());
+ painter->drawLine(points[0], points[1]);
+ painter->drawLine(points[2], points[3]);
+ painter->drawLine(points[4], points[5]);
+ painter->drawLine(points[6], points[7]);
+ painter->drawPoint(points[8]);
+ painter->translate(-1, -1);
+ painter->setPen(option->palette.mid().color());
+ painter->drawLine(points[0], points[1]);
+ painter->drawLine(points[2], points[3]);
+ painter->drawLine(points[4], points[5]);
+ painter->drawLine(points[6], points[7]);
+ painter->drawPoint(points[8]);
+ }
+ painter->restore();
+ break; }
+
+ case PE_FrameWindow: {
+ QPalette popupPal = option->palette;
+ popupPal.setColor(QPalette::Light, option->palette.background().color());
+ popupPal.setColor(QPalette::Midlight, option->palette.light().color());
+ QWindowsCEStylePrivate::drawWinCEPanel(painter, option->rect, popupPal, option->state & State_Sunken);
+ break; }
+
+ case PE_Frame:
+ case PE_FrameMenu:
+ if (const QStyleOptionFrame *frame = qstyleoption_cast<const QStyleOptionFrame *>(option)) {
+ QPalette popupPal = frame->palette;
+ QRect r = frame->rect;
+ qDrawPlainRect(painter, r, frame->palette.shadow().color(),1);
+ }
+ break;
+ case PE_FrameStatusBar:
+ QWindowsCEStylePrivate::drawWinCEPanel(painter, option->rect, option->palette, true, 0);
+ break;
+
+ case PE_FrameTabWidget: {
+ QRect rect = option->rect;
+ QPalette pal = option->palette;
+ QWindowsCEStylePrivate::drawWinCEButton(painter, option->rect, option->palette, false, 0);
+ break; }
+ default:
+ QWindowsStyle::drawPrimitive(element, option, painter, widget);
+ break;
+ }
+}
+
+void QWindowsCEStyle::drawControl(ControlElement element, const QStyleOption *option,
+ QPainter *painter, const QWidget *widget) const {
+ switch (element) {
+ #ifndef QT_NO_MENU
+ case CE_MenuTearoff: {
+ if(option->state & State_Selected) {
+ if(pixelMetric(PM_MenuPanelWidth, option, widget) > 1)
+ qDrawShadePanel(painter, option->rect.x(), option->rect.y(), option->rect.width(),
+ option->rect.height(), option->palette, false, 2,
+ &option->palette.brush(QPalette::Button));
+ else
+ qDrawShadePanel(painter, option->rect.x() + 1, option->rect.y() + 1, option->rect.width() - 2,
+ option->rect.height() - 2, option->palette, true, 1, &option->palette.brush(QPalette::Button));
+ } else {
+ painter->fillRect(option->rect, option->palette.brush(QPalette::Button));
+ }
+ painter->setPen(QPen(option->palette.dark().color(), 1, Qt::DashLine));
+ painter->drawLine(option->rect.x()+2, option->rect.y()+option->rect.height()/2-1, option->rect.x()+option->rect.width()-4,
+ option->rect.y()+option->rect.height()/2-1);
+ painter->setPen(QPen(option->palette.light().color(), 1, Qt::DashLine));
+ painter->drawLine(option->rect.x()+2, option->rect.y()+option->rect.height()/2, option->rect.x()+option->rect.width()-4,
+ option->rect.y()+option->rect.height()/2);
+ break; }
+
+
+ case CE_MenuBarItem:
+ if (const QStyleOptionMenuItem *mbi = qstyleoption_cast<const QStyleOptionMenuItem *>(option)) {
+ bool active = mbi->state & State_Selected;
+ bool hasFocus = mbi->state & State_HasFocus;
+ bool down = mbi->state & State_Sunken;
+ QStyleOptionMenuItem newMbi = *mbi;
+ if (active || hasFocus) {
+ QBrush b = mbi->palette.brush(QPalette::Highlight);
+ if (active && down) {
+ painter->fillRect(mbi->rect.adjusted(0, 1, 0, -1), b);
+ }
+ }
+ uint alignment = Qt::AlignCenter | Qt::TextShowMnemonic | Qt::TextDontClip
+ | Qt::TextSingleLine;
+ if (!styleHint(SH_UnderlineShortcut, mbi, widget))
+ alignment |= Qt::TextHideMnemonic;
+
+ painter->save();
+ QFont f = painter->font();
+ f.setBold(true);
+ painter->setFont(f);
+ QPixmap pix = mbi->icon.pixmap(pixelMetric(PM_SmallIconSize),
+ (mbi->state & State_Enabled) ? QIcon::Normal : QIcon::Disabled);
+ if (!pix.isNull())
+ drawItemPixmap(painter,mbi->rect, alignment, pix);
+ else
+ if (active && down)
+ drawItemText(painter, mbi->rect, alignment, mbi->palette, mbi->state & State_Enabled,
+ mbi->text, QPalette::Light);
+ else
+ drawItemText(painter, mbi->rect, alignment, mbi->palette, mbi->state & State_Enabled,
+ mbi->text, QPalette::ButtonText);
+ painter->restore();
+ }
+ break;
+
+ case CE_MenuBarEmptyArea:
+ painter->save();
+ painter->setPen(option->palette.shadow().color());
+ if (widget && !widget->testAttribute(Qt::WA_NoSystemBackground)) {
+ painter->eraseRect(option->rect);
+ QRect r = option->rect;
+ painter->drawLine(r.x() + 1, r.y() + 1, r.x()+ 1, r.y()+ r.height() - 2);
+ painter->drawLine(r.x() - 2 + r.width(), r.y() + 1, r.x() - 2 + r.width(), r.y() + r.height() - 2);
+ painter->drawLine(r.x() + 1, r.y() +1, r.x() - 1 + r.width(), r.y() + 1);
+ painter->drawLine(r.x() + 1, r.y() + r.height()-2 , r.x() - 2 + r.width(), r.y() + r.height() - 2);
+ }
+ painter->restore();
+ break;
+
+ case CE_MenuItem:
+ if (const QStyleOptionMenuItem *menuitem = qstyleoption_cast<const QStyleOptionMenuItem *>(option)) {
+ int x, y, w, h;
+ menuitem->rect.getRect(&x, &y, &w, &h);
+ int tab = menuitem->tabWidth;
+ bool dis = !(menuitem->state & State_Enabled);
+ bool checked = menuitem->checkType != QStyleOptionMenuItem::NotCheckable
+ ? menuitem->checked : false;
+ bool act = menuitem->state & State_Selected;
+
+ // windows always has a check column, regardless whether we have an icon or not
+ int checkcol = qMax(menuitem->maxIconWidth, windowsCheckMarkWidth);
+ QBrush fill = menuitem->palette.brush(act ? QPalette::Highlight : QPalette::Button);
+ painter->fillRect(menuitem->rect.adjusted(1, 1, 0, 0), fill);
+
+ if (menuitem->menuItemType == QStyleOptionMenuItem::Separator) {
+ int yoff = y-1 + h / 2;
+ painter->setPen(menuitem->palette.shadow().color());
+ painter->drawLine(x + 4, yoff + 1, x + w - 8, yoff + 1);
+ return;
+ }
+
+ QRect vCheckRect = visualRect(option->direction, menuitem->rect, QRect(menuitem->rect.x(),
+ menuitem->rect.y(), checkcol, menuitem->rect.height()));
+ if (checked) {
+ if (act && !dis) {
+ qDrawPlainRect(painter, vCheckRect,
+ menuitem->palette.button().color(), 1,
+ &menuitem->palette.brush(QPalette::Button));
+ } else {
+ QBrush fill(menuitem->palette.button().color(), Qt::Dense4Pattern);
+ qDrawPlainRect(painter, vCheckRect,menuitem->palette.button().color(), 1, &fill);
+ }
+ } else if (!act) {
+ painter->fillRect(vCheckRect, menuitem->palette.brush(QPalette::Button));
+ }
+ // On Windows Style, if we have a checkable item and an icon we
+ // draw the icon recessed to indicate an item is checked. If we
+ // have no icon, we draw a checkmark instead.
+ if (!menuitem->icon.isNull()) {
+ QIcon::Mode mode = dis ? QIcon::Disabled : QIcon::Normal;
+ if (act && !dis)
+ mode = QIcon::Active;
+ QPixmap pixmap;
+ if (checked)
+ pixmap = menuitem->icon.pixmap(pixelMetric(PM_SmallIconSize), mode, QIcon::On);
+ else
+ pixmap = menuitem->icon.pixmap(pixelMetric(PM_SmallIconSize), mode);
+ int pixw = pixmap.width();
+ int pixh = pixmap.height();
+ if (act && !dis && !checked)
+ qDrawPlainRect(painter, vCheckRect, menuitem->palette.button().color(), 1,
+ &menuitem->palette.brush(QPalette::Button));
+ QRect pmr(0, 0, pixw, pixh);
+ pmr.moveCenter(vCheckRect.center());
+ painter->setPen(menuitem->palette.text().color());
+ painter->drawPixmap(pmr.topLeft(), pixmap);
+ } else if (checked) {
+ QStyleOptionMenuItem newMi = *menuitem;
+ newMi.state = State_None;
+ if (!dis)
+ newMi.state |= State_Enabled;
+ if (act)
+ newMi.state |= State_On;
+ newMi.rect = visualRect(option->direction, menuitem->rect, QRect(menuitem->rect.x()
+ + windowsItemFrame, menuitem->rect.y() + windowsItemFrame,
+ checkcol - 2 * windowsItemFrame, menuitem->rect.height() - 2*windowsItemFrame));
+ drawPrimitive(PE_IndicatorMenuCheckMark, &newMi, painter, widget);
+ }
+ painter->setPen(act ? menuitem->palette.highlightedText().color() : menuitem->palette.buttonText().color());
+
+ QColor discol;
+ if (dis) {
+ discol = menuitem->palette.text().color();
+ painter->setPen(discol);
+ }
+ int xm = windowsItemFrame + checkcol + windowsItemHMargin;
+ int xpos = menuitem->rect.x() + xm;
+ QRect textRect(xpos, y + windowsItemVMargin, w - xm - windowsRightBorder - tab + 1, h - 2 * windowsItemVMargin);
+ QRect vTextRect = visualRect(option->direction, menuitem->rect, textRect);
+ QString s = menuitem->text;
+ if (!s.isEmpty()) { // draw text
+ painter->save();
+ int t = s.indexOf(QLatin1Char('\t'));
+ int text_flags = Qt::AlignVCenter | Qt::TextShowMnemonic | Qt::TextDontClip | Qt::TextSingleLine;
+ if (!styleHint(SH_UnderlineShortcut, menuitem, widget))
+ text_flags |= Qt::TextHideMnemonic;
+ text_flags |= Qt::AlignLeft;
+ if (t >= 0) {
+ QRect vShortcutRect = visualRect(option->direction, menuitem->rect,
+ QRect(textRect.topRight(), QPoint(menuitem->rect.right(), textRect.bottom())));
+ if (dis && !act)
+ painter->setPen(discol);
+ painter->drawText(vShortcutRect, text_flags, s.mid(t + 1));
+ s = s.left(t);
+ }
+ QFont font = menuitem->font;
+ if (menuitem->menuItemType == QStyleOptionMenuItem::DefaultItem)
+ font.setBold(true);
+ painter->setFont(font);
+ if (dis && !act)
+ painter->setPen(discol);
+ painter->drawText(vTextRect, text_flags, s.left(t));
+ painter->restore();
+ }
+ if (menuitem->menuItemType == QStyleOptionMenuItem::SubMenu) {// draw sub menu arrow
+ int dim = (h - 2 * windowsItemFrame) / 2;
+ PrimitiveElement arrow;
+ arrow = (option->direction == Qt::RightToLeft) ? PE_IndicatorSpinDown : PE_IndicatorSpinUp;
+ xpos = x + w - windowsArrowHMargin - windowsItemFrame - dim;
+ QRect vSubMenuRect = visualRect(option->direction, menuitem->rect, QRect(xpos, y + h / 2 - dim / 2, dim, dim));
+ QStyleOptionMenuItem newMI = *menuitem;
+ newMI.rect = vSubMenuRect;
+ newMI.state = dis ? State_None : State_Enabled;
+ if (act)
+ newMI.palette.setColor(QPalette::ButtonText,
+ newMI.palette.highlightedText().color());
+ drawPrimitive(arrow, &newMI, painter, widget);
+ }
+ }
+ break;
+#endif // QT_NO_MENU
+ case CE_MenuVMargin:
+ painter->fillRect(option->rect, Qt::white);
+ break;
+ case CE_MenuEmptyArea:
+ QWindowsStyle::drawControl(element,option, painter, widget);
+ break;
+
+#ifndef QT_NO_TABBAR
+ case CE_TabBarTab:
+ if (const QStyleOptionTab *tab = qstyleoption_cast<const QStyleOptionTab *>(option)) {
+ drawControl(CE_TabBarTabShape, tab, painter, widget);
+ drawControl(CE_TabBarTabLabel, tab, painter, widget);
+ }
+ break;
+ case CE_TabBarTabShape:
+ if (const QStyleOptionTab *tab = qstyleoption_cast<const QStyleOptionTab *>(option)) {
+ bool rtlHorTabs = (tab->direction == Qt::RightToLeft
+ && (tab->shape == QTabBar::RoundedNorth
+ || tab->shape == QTabBar::RoundedSouth));
+ bool selected = tab->state & State_Selected;
+ bool lastTab = ((!rtlHorTabs && tab->position == QStyleOptionTab::End)
+ || (rtlHorTabs
+ && tab->position == QStyleOptionTab::Beginning));
+ bool firstTab = ((!rtlHorTabs
+ && tab->position == QStyleOptionTab::Beginning)
+ || (rtlHorTabs
+ && tab->position == QStyleOptionTab::End));
+ bool onlyOne = tab->position == QStyleOptionTab::OnlyOneTab;
+ bool previousSelected =
+ ((!rtlHorTabs
+ && tab->selectedPosition == QStyleOptionTab::PreviousIsSelected)
+ || (rtlHorTabs
+ && tab->selectedPosition == QStyleOptionTab::NextIsSelected));
+ bool nextSelected =
+ ((!rtlHorTabs
+ && tab->selectedPosition == QStyleOptionTab::NextIsSelected)
+ || (rtlHorTabs
+ && tab->selectedPosition
+ == QStyleOptionTab::PreviousIsSelected));
+ int tabBarAlignment = styleHint(SH_TabBar_Alignment, tab, widget);
+ bool leftAligned = (!rtlHorTabs && tabBarAlignment == Qt::AlignLeft)
+ || (rtlHorTabs
+ && tabBarAlignment == Qt::AlignRight);
+
+ bool rightAligned = (!rtlHorTabs && tabBarAlignment == Qt::AlignRight)
+ || (rtlHorTabs
+ && tabBarAlignment == Qt::AlignLeft);
+ QColor light = tab->palette.light().color();
+ QColor midlight = tab->palette.midlight().color();
+ QColor dark = tab->palette.dark().color();
+ QColor shadow = tab->palette.shadow().color();
+ QColor background = tab->palette.background().color();
+ int borderThinkness = pixelMetric(PM_TabBarBaseOverlap, tab, widget);
+ if (selected)
+ borderThinkness /= 2;
+ QRect r2(option->rect);
+ int x1 = r2.left();
+ int x2 = r2.right();
+ int y1 = r2.top();
+ int y2 = r2.bottom();
+ switch (tab->shape) {
+ default:
+ QCommonStyle::drawControl(element, tab, painter, widget);
+ break;
+ case QTabBar::RoundedNorth: {
+ if (!selected) {
+ y1 += 2;
+ x1 += firstTab ? borderThinkness : 0;
+ x2 -= lastTab ? borderThinkness : 0;
+ }
+
+ painter->fillRect(QRect(x1 + 1, y1 + 1, (x2 - x1) - 1, (y2 - y1) - 2), tab->palette.background());
+
+ // Delete border
+ if (selected) {
+ painter->setPen(background);
+ painter->drawLine(x1, y2 - 1, x2, y2 - 1);
+ painter->drawLine(x1, y2 + 1, x2, y2 + 1);
+ painter->drawLine(x1, y2, x2, y2);
+ }
+ // Left
+ if (firstTab || selected || onlyOne || !previousSelected) {
+ painter->setPen(dark);
+ painter->drawLine(x1, y1 + 2, x1, y2 - ((onlyOne || firstTab) && selected && leftAligned ? 0 : borderThinkness));
+ painter->drawPoint(x1 + 1, y1 + 1);
+ painter->setPen(midlight);
+ painter->drawLine(x1 + 1, y1 + 2, x1 + 1, y2 -
+ ((onlyOne || firstTab) && selected && leftAligned ? 0 : borderThinkness));
+
+ }
+ // Top
+ {
+ int beg = x1 + (previousSelected ? 0 : 2);
+ int end = x2 - (nextSelected ? 0 : 2);
+ painter->setPen(dark);
+ painter->drawLine(beg, y1, end, y1);
+
+ painter->setPen(midlight);
+ painter->drawLine(beg, y1 + 1, end, y1 + 1);
+
+ }
+ // Right
+ if (lastTab || selected || onlyOne || !nextSelected) {
+ painter->setPen(shadow);
+ painter->drawLine(x2, y1 + 2, x2, y2 - ((onlyOne || lastTab) && selected && rightAligned ? 0 : borderThinkness));
+ painter->drawPoint(x2 - 1, y1 + 1);
+ painter->setPen(dark);
+ painter->drawLine(x2 - 1, y1 + 2, x2 - 1, y2 - ((onlyOne || lastTab) && selected && rightAligned ? 0 : borderThinkness));
+ }
+ break; }
+ case QTabBar::RoundedSouth: {
+ if (!selected) {
+ y2 -= 2;
+ x1 += firstTab ? borderThinkness : 0;
+ x2 -= lastTab ? borderThinkness : 0;
+ }
+
+ painter->fillRect(QRect(x1 + 1, y1 + 2, (x2 - x1) - 1, (y2 - y1) - 1), tab->palette.background());
+
+ // Delete border
+ if (selected) {
+ painter->setPen(background);
+ painter->drawLine(x1, y1 + 1, x2 - 1, y1 + 1);
+ painter->drawLine(x1, y1 - 1, x2 - 1, y1 - 1);
+ painter->drawLine(x1, y1, x2 - 1, y1);
+ }
+ // Left
+ if (firstTab || selected || onlyOne || !previousSelected) {
+ painter->setPen(dark);
+ painter->drawLine(x1, y2 - 2, x1, y1 + ((onlyOne || firstTab) && selected && leftAligned ? 0 : borderThinkness));
+ painter->drawPoint(x1 + 1, y2 - 1);
+ painter->setPen(midlight);
+ painter->drawLine(x1 + 1, y2 - 2, x1 + 1, y1 + ((onlyOne || firstTab) && selected && leftAligned ? 0 : borderThinkness));
+ }
+ // Bottom
+ {
+ int beg = x1 + (previousSelected ? 0 : 2);
+ int end = x2 - (nextSelected ? 0 : 2);
+ painter->setPen(shadow);
+ painter->drawLine(beg, y2, end, y2);
+ painter->setPen(dark);
+ painter->drawLine(beg, y2 - 1, end, y2 - 1);
+ }
+ // Right
+ if (lastTab || selected || onlyOne || !nextSelected) {
+ painter->setPen(shadow);
+ painter->drawLine(x2, y2 - 2, x2, y1 + ((onlyOne || lastTab) && selected &&
+ rightAligned ? 0 : borderThinkness));
+ painter->drawPoint(x2 - 1, y2 - 1);
+ painter->setPen(dark);
+ painter->drawLine(x2 - 1, y2 - 2, x2 - 1, y1 + ((onlyOne || lastTab) && selected &&
+ rightAligned ? 0 : borderThinkness));
+ }
+ break; }
+ case QTabBar::RoundedWest: {
+ if (!selected) {
+ x1 += 2;
+ y1 += firstTab ? borderThinkness : 0;
+ y2 -= lastTab ? borderThinkness : 0;
+ }
+
+ painter->fillRect(QRect(x1 + 1, y1 + 1, (x2 - x1) - 2, (y2 - y1) - 1), tab->palette.background());
+
+ // Delete border
+ if (selected) {
+ painter->setPen(background);
+ painter->drawLine(x2 - 1, y1, x2 - 1, y2);
+ painter->drawLine(x2, y1, x2, y2);
+ }
+ // Top
+ if (firstTab || selected || onlyOne || !previousSelected) {
+ painter->setPen(dark);
+ painter->drawLine(x1 + 2, y1, x2 - ((onlyOne || firstTab) && selected &&
+ leftAligned ? 0 : borderThinkness), y1);
+ painter->drawPoint(x1 + 1, y1 + 1);
+ painter->setPen(midlight);
+ painter->drawLine(x1 + 2, y1 + 1, x2 - ((onlyOne || firstTab) && selected &&
+ leftAligned ? 0 : borderThinkness), y1 + 1);
+ }
+ // Left
+ {
+ int beg = y1 + (previousSelected ? 0 : 2);
+ int end = y2 - (nextSelected ? 0 : 2);
+ painter->setPen(dark);
+ painter->drawLine(x1, beg, x1, end);
+ painter->setPen(midlight);
+ painter->drawLine(x1 + 1, beg, x1 + 1, end);
+ }
+ // Bottom
+ if (lastTab || selected || onlyOne || !nextSelected) {
+ painter->setPen(shadow);
+ painter->drawLine(x1 + 3, y2, x2 - ((onlyOne || lastTab) && selected &&
+ rightAligned ? 0 : borderThinkness), y2);
+ painter->drawPoint(x1 + 2, y2 - 1);
+ painter->setPen(dark);
+ painter->drawLine(x1 + 3, y2 - 1, x2 - ((onlyOne || lastTab) && selected &&
+ rightAligned ? 0 : borderThinkness), y2 - 1);
+ painter->drawPoint(x1 + 1, y2 - 1);
+ painter->drawPoint(x1 + 2, y2);
+ }
+ break; }
+ case QTabBar::RoundedEast: {
+ if (!selected) {
+ x2 -= 2;
+ y1 += firstTab ? borderThinkness : 0;
+ y2 -= lastTab ? borderThinkness : 0;
+ }
+
+ painter->fillRect(QRect(x1 + 2, y1 + 1, (x2 - x1) - 1, (y2 - y1) - 1), tab->palette.background());
+
+ // Delete border
+ if (selected) {
+ painter->setPen(background);
+ painter->drawLine(x1 + 1, y1, x1 + 1, y2 - 1);
+ painter->drawLine(x1, y1, x1, y2 - 1);
+ }
+ // Top
+ if (firstTab || selected || onlyOne || !previousSelected) {
+ painter->setPen(dark);
+ painter->drawLine(x2 - 2, y1, x1 + ((onlyOne || firstTab) && selected &&
+ leftAligned ? 0 : borderThinkness), y1);
+ painter->drawPoint(x2 - 1, y1 + 1);
+ painter->setPen(midlight);
+ painter->drawLine(x2 - 3, y1 + 1, x1 + ((onlyOne || firstTab) &&
+ selected && leftAligned ? 0 : borderThinkness), y1 + 1);
+ painter->drawPoint(x2 - 1, y1);
+
+ }
+ // Right
+ {
+ int beg = y1 + (previousSelected ? 0 : 2);
+ int end = y2 - (nextSelected ? 0 : 2);
+ painter->setPen(shadow);
+ painter->drawLine(x2, beg, x2, end);
+ painter->setPen(dark);
+ painter->drawLine(x2 - 1, beg, x2 - 1, end);
+ }
+ // Bottom
+ if (lastTab || selected || onlyOne || !nextSelected) {
+ painter->setPen(shadow);
+ painter->drawLine(x2 - 2, y2, x1 + ((onlyOne || lastTab) &&
+ selected && rightAligned ? 0 : borderThinkness), y2);
+ painter->drawPoint(x2 - 1, y2 - 1);
+ painter->setPen(dark);
+ painter->drawLine(x2 - 2, y2 - 1, x1 + ((onlyOne || lastTab) &&
+ selected && rightAligned ? 0 : borderThinkness), y2 - 1);
+ }
+ break; }
+ }
+ }
+ break;
+#endif // QT_NO_TABBAR
+
+ case CE_ToolBar: {
+ QRect rect = option->rect;
+ painter->setPen(QPen(option->palette.dark().color()));
+ painter->drawLine(rect.topRight().x()-1,
+ rect.topRight().y(),
+ rect.bottomRight().x()-1,
+ rect.bottomRight().y());
+ painter->drawLine(rect.bottomLeft().x(),
+ rect.bottomLeft().y(),
+ rect.bottomRight().x(),
+ rect.bottomRight().y());
+ painter->setPen(QPen(option->palette.light().color()));
+ painter->drawLine(rect.topRight().x(),
+ rect.topRight().y(),
+ rect.bottomRight().x(),
+ rect.bottomRight().y());
+ painter->drawLine(rect.topLeft().x(),
+ rect.topLeft().y(),
+ rect.topRight().x(),
+ rect.topRight().y());
+
+ break; }
+#ifndef QT_NO_SCROLLBAR
+ case CE_ScrollBarSubLine:
+ case CE_ScrollBarAddLine: {
+ if (option->state & State_Sunken) {
+ QStyleOption buttonOpt = *option;
+
+ drawPrimitive(PE_PanelButtonBevel, &buttonOpt, painter, widget);
+ } else {
+ QStyleOption buttonOpt = *option;
+ if (!(buttonOpt.state & State_Sunken))
+ buttonOpt.state |= State_Raised;
+ drawPrimitive(PE_PanelButtonBevel, &buttonOpt, painter, widget);
+ }
+ PrimitiveElement arrow;
+ if (option->state & State_Horizontal) {
+ if (element == CE_ScrollBarAddLine)
+ arrow = option->direction == Qt::LeftToRight ? PE_IndicatorArrowRight : PE_IndicatorArrowLeft;
+ else
+ arrow = option->direction == Qt::LeftToRight ? PE_IndicatorArrowLeft : PE_IndicatorArrowRight;
+ } else {
+ if (element == CE_ScrollBarAddLine)
+ arrow = PE_IndicatorArrowDown;
+ else
+ arrow = PE_IndicatorArrowUp;
+ }
+ drawPrimitive(arrow, option, painter, widget);
+ break; }
+ case CE_ScrollBarAddPage:
+ case CE_ScrollBarSubPage: {
+ QBrush br;
+ QBrush bg = painter->background();
+ Qt::BGMode bg_mode = painter->backgroundMode();
+ painter->setPen(Qt::NoPen);
+ painter->setBackgroundMode(Qt::OpaqueMode);
+
+ if (option->state & State_Sunken) {
+ br = QBrush(option->palette.shadow().color(), Qt::Dense4Pattern);
+ painter->setBackground(option->palette.dark().color());
+ painter->setBrush(br);
+ } else {
+ QPixmap pm = option->palette.brush(QPalette::Light).texture();
+ if (option->state & State_Enabled)
+ br = !pm.isNull() ? QBrush(pm) : QBrush(option->palette.button().color(), Qt::Dense4Pattern);
+ else
+ br = !pm.isNull() ? QBrush(pm) : QBrush(option->palette.light().color(), Qt::Dense4Pattern);
+ painter->setBackground(option->palette.base().color());
+ painter->setBrush(br);
+ }
+ painter->drawRect(option->rect);
+ painter->setBackground(bg);
+ painter->setBackgroundMode(bg_mode);
+ break; }
+ case CE_ScrollBarSlider:
+ if (!(option->state & State_Enabled)) {
+ QStyleOptionButton buttonOpt;
+ buttonOpt.QStyleOption::operator=(*option);
+ buttonOpt.state = State_Enabled | State_Raised;
+ drawPrimitive(PE_PanelButtonBevel, &buttonOpt, painter, widget);
+ QPixmap pm = option->palette.brush(QPalette::Light).texture();
+ QBrush br = !pm.isNull() ? QBrush(pm) : QBrush(option->palette.light().color(), Qt::Dense4Pattern);
+ painter->setPen(Qt::NoPen);
+ painter->setBrush(br);
+ painter->setBackgroundMode(Qt::OpaqueMode);
+ painter->drawRect(option->rect.adjusted(2, 2, -2, -2));
+ } else {
+ QStyleOptionButton buttonOpt;
+ buttonOpt.QStyleOption::operator=(*option);
+ buttonOpt.state = State_Enabled | State_Raised;
+ drawPrimitive(PE_PanelButtonBevel, &buttonOpt, painter, widget);
+ }
+ break;
+#endif // QT_NO_SCROLLBAR
+ case CE_HeaderSection: {
+ QBrush fill;
+ if (option->state & State_On)
+ fill = QBrush(option->palette.light().color(), Qt::Dense4Pattern);
+ else
+ fill = option->palette.brush(QPalette::Button);
+
+ if (option->state & (State_Raised | State_Sunken)) {
+ QWindowsCEStylePrivate::drawWinCEButton(painter, option->rect, option->palette,
+ option->state & State_Sunken, &fill);
+ } else {
+ painter->fillRect(option->rect, fill);
+ }
+ break; }
+
+ case CE_DockWidgetTitle:
+ QWindowsStyle::drawControl(element,option, painter, widget);
+ break;
+
+ case CE_PushButtonLabel:
+ if (const QStyleOptionButton *btn = qstyleoption_cast<const QStyleOptionButton *>(option)) {
+ painter->save();
+ QFont f = painter->font();
+ f.setBold(true);
+ painter->setFont(f);
+ QRect ir = btn->rect;
+ uint tf = Qt::AlignVCenter | Qt::TextShowMnemonic;
+ if (!styleHint(SH_UnderlineShortcut, btn, widget))
+ tf |= Qt::TextHideMnemonic;
+
+ if (btn->state & (State_On | State_Sunken))
+ ir.translate(pixelMetric(PM_ButtonShiftHorizontal, option, widget),
+ pixelMetric(PM_ButtonShiftVertical, option, widget));
+ if (!btn->icon.isNull()) {
+ QIcon::Mode mode = btn->state & State_Enabled ? QIcon::Normal
+ : QIcon::Disabled;
+ if (mode == QIcon::Normal && btn->state & State_HasFocus)
+ mode = QIcon::Active;
+ QIcon::State state = QIcon::Off;
+ if (btn->state & State_On)
+ state = QIcon::On;
+ QPixmap pixmap = btn->icon.pixmap(btn->iconSize, mode, state);
+ int pixw = pixmap.width();
+ int pixh = pixmap.height();
+ //Center the icon if there is no text
+
+ QPoint point;
+ if (btn->text.isEmpty()) {
+ point = QPoint(ir.x() + ir.width() / 2 - pixw / 2,
+ ir.y() + ir.height() / 2 - pixh / 2);
+ } else {
+ point = QPoint(ir.x() + 2, ir.y() + ir.height() / 2 - pixh / 2);
+ }
+ if (btn->direction == Qt::RightToLeft)
+ point.rx() += pixw;
+
+ if ((btn->state & (State_On | State_Sunken)) && btn->direction == Qt::RightToLeft)
+ point.rx() -= pixelMetric(PM_ButtonShiftHorizontal, option, widget) * 2;
+
+ painter->drawPixmap(visualPos(btn->direction, btn->rect, point), pixmap);
+
+ if (btn->direction == Qt::RightToLeft)
+ ir.translate(-4, 0);
+ else
+ ir.translate(pixw + 4, 0);
+ ir.setWidth(ir.width() - (pixw + 4));
+ // left-align text if there is
+ if (!btn->text.isEmpty())
+ tf |= Qt::AlignLeft;
+ } else {
+ tf |= Qt::AlignHCenter;
+ }
+ drawItemText(painter, ir, tf, btn->palette, (btn->state & State_Enabled),
+ btn->text, QPalette::ButtonText);
+ painter->restore();
+ }
+ break;
+ default:
+ QWindowsStyle::drawControl(element, option, painter, widget);
+ break;
+ }
+}
+
+void QWindowsCEStyle::drawComplexControl(ComplexControl control, const QStyleOptionComplex *option,
+ QPainter *painter, const QWidget *widget) const {
+ switch (control) {
+ #ifndef QT_NO_SLIDER
+ case CC_Slider:
+ if (const QStyleOptionSlider *slider = qstyleoption_cast<const QStyleOptionSlider *>(option)) {
+ int thickness = pixelMetric(PM_SliderControlThickness, slider, widget);
+ int len = pixelMetric(PM_SliderLength, slider, widget);
+ int ticks = slider->tickPosition;
+ QRect groove = subControlRect(CC_Slider, slider, SC_SliderGroove, widget);
+ QRect handle = subControlRect(CC_Slider, slider, SC_SliderHandle, widget);
+
+ if ((slider->subControls & SC_SliderGroove) && groove.isValid()) {
+ int mid = thickness / 2;
+ if (ticks & QSlider::TicksAbove)
+ mid += len / 8;
+ if (ticks & QSlider::TicksBelow)
+ mid -= len / 8;
+
+ painter->setPen(slider->palette.shadow().color());
+ if (slider->orientation == Qt::Horizontal) {
+ QWindowsCEStylePrivate::drawWinCEPanel(painter, groove.x(), groove.y() + mid - 2,
+ groove.width(), 4, option->palette, true);
+ painter->drawLine(groove.x() + 1, groove.y() + mid - 1,
+ groove.x() + groove.width() - 3, groove.y() + mid - 1);
+ } else {
+ QWindowsCEStylePrivate::drawWinCEPanel(painter, groove.x() + mid - 2, groove.y(),
+ 4, groove.height(), option->palette, true);
+ painter->drawLine(groove.x() + mid - 1, groove.y() + 1,
+ groove.x() + mid - 1, groove.y() + groove.height() - 3);
+ }
+ }
+ if (slider->subControls & SC_SliderTickmarks) {
+ QStyleOptionSlider tmpSlider = *slider;
+ tmpSlider.subControls = SC_SliderTickmarks;
+ QCommonStyle::drawComplexControl(control, &tmpSlider, painter, widget);
+ }
+
+ if (slider->subControls & SC_SliderHandle) {
+ // 4444440
+ // 4333310
+ // 4322210
+ // 4322210
+ // 4322210
+ // 4322210
+ // *43210*
+ // **440**
+ // ***0***
+ const QColor c0 = slider->palette.shadow().color();
+ const QColor c1 = slider->palette.dark().color();
+ // const QColor c2 = g.button();
+ const QColor c3 = slider->palette.midlight().color();
+ const QColor c4 = slider->palette.dark().color();
+ QBrush handleBrush;
+
+ if (slider->state & State_Enabled) {
+ handleBrush = slider->palette.color(QPalette::Button);
+ } else {
+ handleBrush = QBrush(slider->palette.color(QPalette::Button),
+ Qt::Dense4Pattern);
+ }
+
+ int x = handle.x(), y = handle.y(),
+ wi = handle.width(), he = handle.height();
+
+ int x1 = x;
+ int x2 = x + wi - 1;
+ int y1 = y;
+ int y2 = y + he - 1;
+
+ Qt::Orientation orient = slider->orientation;
+ bool tickAbove = slider->tickPosition == QSlider::TicksAbove;
+ bool tickBelow = slider->tickPosition == QSlider::TicksBelow;
+
+ if (slider->state & State_HasFocus) {
+ QStyleOptionFocusRect fropt;
+ fropt.QStyleOption::operator=(*slider);
+ fropt.rect = subElementRect(SE_SliderFocusRect, slider, widget);
+ drawPrimitive(PE_FrameFocusRect, &fropt, painter, widget);
+ }
+ if ((tickAbove && tickBelow) || (!tickAbove && !tickBelow)) {
+ Qt::BGMode oldMode = painter->backgroundMode();
+ painter->setBackgroundMode(Qt::OpaqueMode);
+ QWindowsCEStylePrivate::drawWinCEButton(painter, QRect(x, y, wi, he), slider->palette, false,
+ &handleBrush);
+ painter->setBackgroundMode(oldMode);
+ QBrush fill = QBrush(option->palette.light().color(), Qt::Dense4Pattern);
+ if (slider->state & State_Sunken)
+ painter->fillRect(QRectF(x1 + 2, y1 + 2, x2 - x1 - 3, y2 - y1 - 3),fill);
+ return;
+ }
+ QSliderDirection dir;
+ if (orient == Qt::Horizontal)
+ if (tickAbove)
+ dir = SlUp;
+ else
+ dir = SlDown;
+ else
+ if (tickAbove)
+ dir = SlLeft;
+ else
+ dir = SlRight;
+ QPolygon a;
+ int d = 0;
+ switch (dir) {
+ case SlUp:
+ x2++;
+ y1 = y1 + wi / 2;
+ d = (wi + 1) / 2 - 1;
+ a.setPoints(5, x1, y1, x1, y2, x2, y2, x2, y1, x1 + d, y1 - d);
+ break;
+ case SlDown:
+ x2++;
+ y2 = y2 - wi / 2;
+ d = (wi + 1) / 2 - 1;
+ a.setPoints(5, x1, y1, x1, y2, x1 + d, y2+d, x2, y2, x2, y1);
+ break;
+ case SlLeft:
+ d = (he + 1) / 2 - 1;
+ x1 = x1 + he / 2;
+ a.setPoints(5, x1, y1, x1 - d, y1 + d, x1, y2, x2, y2, x2, y1);
+ y1--;
+ break;
+ case SlRight:
+ d = (he + 1) / 2 - 1;
+ x2 = x2 - he / 2;
+ a.setPoints(5, x1, y1, x1, y2, x2, y2, x2 + d, y1 + d, x2, y1);
+ y1--;
+ break;
+ }
+ QBrush oldBrush = painter->brush();
+ painter->setPen(Qt::NoPen);
+ painter->setBrush(handleBrush);
+ Qt::BGMode oldMode = painter->backgroundMode();
+ painter->setBackgroundMode(Qt::OpaqueMode);
+ painter->drawRect(x1, y1, x2 - x1 + 1, y2 - y1 + 1);
+ painter->drawPolygon(a);
+ QBrush fill = QBrush(option->palette.light().color(), Qt::Dense4Pattern);
+ if (slider->state & State_Sunken)
+ painter->fillRect(QRectF(x1, y1, x2 - x1 + 1, y2 - y1 + 1),fill);
+ painter->setBrush(oldBrush);
+ painter->setBackgroundMode(oldMode);
+
+ if (dir != SlUp) {
+ painter->setPen(c4);
+ painter->drawLine(x1, y1, x2, y1);
+ painter->setPen(c3);
+ painter->drawLine(x1, y1 + 1, x2, y1 + 1);
+ }
+ if (dir != SlLeft) {
+ painter->setPen(c3);
+ painter->drawLine(x1 + 1, y1 + 1, x1 + 1, y2);
+ painter->setPen(c4);
+ painter->drawLine(x1, y1, x1, y2);
+ }
+ if (dir != SlRight) {
+ painter->setPen(c0);
+ painter->drawLine(x2, y1, x2, y2);
+ painter->setPen(c1);
+ painter->drawLine(x2 - 1, y1 + 1, x2 - 1, y2 - 1);
+ }
+ if (dir != SlDown) {
+ painter->setPen(c0);
+ painter->drawLine(x1, y2, x2, y2);
+ painter->setPen(c1);
+ painter->drawLine(x1+1, y2 - 1, x2 - 1, y2 - 1);
+ }
+
+ switch (dir) {
+ case SlUp:
+ if (slider->state & State_Sunken)
+ painter->fillRect(QRectF(x1 + 3, y1 - d + 2, x2 - x1 - 4,y1), fill);
+ painter->setPen(c4);
+ painter->drawLine(x1, y1, x1 + d, y1 - d);
+ painter->setPen(c0);
+ d = wi - d - 1;
+ painter->drawLine(x2, y1, x2 - d, y1 - d);
+ d--;
+ painter->setPen(c3);
+ painter->drawLine(x1 + 1, y1, x1 + 1 + d-1, y1 - d + 1);
+ painter->setPen(c1);
+ painter->drawLine(x2 - 1, y1, x2-1 - d, y1 - d);
+ break;
+ case SlDown:
+ if (slider->state & State_Sunken)
+ painter->fillRect(QRectF(x1 + 3, y2 - d, x2 - x1 - 4,y2 - 8), fill);
+ painter->setPen(c4);
+ painter->drawLine(x1, y2, x1 + d, y2 + d);
+ painter->setPen(c0);
+ d = wi - d - 1;
+ painter->drawLine(x2, y2, x2 - d, y2 + d);
+ d--;
+ painter->setPen(c3);
+ painter->drawLine(x1 + 1, y2, x1 + 1 + d - 1, y2 + d - 1);
+ painter->setPen(c1);
+ painter->drawLine(x2 - 1, y2, x2 - 1 - d, y2 + d);
+ break;
+ case SlLeft:
+ if (slider->state & State_Sunken)
+ painter->fillRect(QRectF(x1 - d + 2, y1 + 2, x1,y2 - y1 - 3), fill);
+ painter->setPen(c4);
+ painter->drawLine(x1, y1, x1 - d, y1 + d);
+ painter->setPen(c0);
+ d = he - d - 1;
+ painter->drawLine(x1, y2, x1 - d, y2 - d);
+ d--;
+ painter->setPen(c3);
+ painter->drawLine(x1, y1 + 1, x1 - d + 1, y1 + 1 + d - 1);
+ painter->setPen(c1);
+ painter->drawLine(x1, y2 - 1, x1 - d, y2 - 1 - d);
+ break;
+ case SlRight:
+ if (slider->state & State_Sunken)
+ painter->fillRect(QRectF(x2 - d - 4, y1 + 2, x2 - 4, y2 - y1 - 3), fill);
+ painter->setPen(c4);
+ painter->drawLine(x2, y1, x2 + d, y1 + d);
+ painter->setPen(c0);
+ d = he - d - 1;
+ painter->drawLine(x2, y2, x2 + d, y2 - d);
+ d--;
+ painter->setPen(c3);
+ painter->drawLine(x2, y1 + 1, x2 + d - 1, y1 + 1 + d - 1);
+ painter->setPen(c1);
+ painter->drawLine(x2, y2 - 1, x2 + d, y2 - 1 - d);
+ break;
+ }
+ }
+ }
+ break;
+#endif // QT_NO_SLIDER
+ case CC_ToolButton:
+ if (const QStyleOptionToolButton *toolbutton
+ = qstyleoption_cast<const QStyleOptionToolButton *>(option)) {
+ QRect button, menuarea;
+
+#ifndef QT_NO_TOOLBAR
+ bool flat = !(widget ? qobject_cast<QToolBar*>(widget->parentWidget()) : 0);
+#else
+ bool flat = true;
+#endif
+
+ button = subControlRect(control, toolbutton, SC_ToolButton, widget);
+ menuarea = subControlRect(control, toolbutton, SC_ToolButtonMenu, widget);
+
+ if (flat && (toolbutton->subControls & SC_ToolButtonMenu)) {
+ menuarea.setLeft(menuarea.left() - 4);
+ button.setRight(button.right() - 4);
+ }
+
+ State bflags = toolbutton->state;
+
+ if (bflags & State_AutoRaise)
+ if (!(bflags & State_MouseOver)) {
+ bflags &= ~State_Raised;
+ }
+ State mflags = bflags;
+
+ if (toolbutton->activeSubControls & SC_ToolButton)
+ bflags |= State_Sunken;
+ if (toolbutton->activeSubControls & SC_ToolButtonMenu)
+ mflags |= State_Sunken;
+
+ QStyleOption tool(0);
+ tool.palette = toolbutton->palette;
+ if (toolbutton->subControls & SC_ToolButton) {
+ tool.rect = button;
+ tool.state = bflags;
+ drawPrimitive(PE_PanelButtonTool, &tool, painter, widget);
+ }
+
+ if (toolbutton->subControls & SC_ToolButtonMenu) {
+ tool.rect = menuarea;
+ tool.state = mflags;
+ tool.state = bflags;
+ drawPrimitive(PE_IndicatorButtonDropDown, &tool, painter, widget);
+
+ if (!flat) {
+
+ //connect buttons
+ painter->save();
+ painter->setPen(tool.palette.button().color());
+ painter->drawLine(tool.rect.x() - 2, tool.rect.y(), tool.rect.x() - 2, tool.rect.y() + tool.rect.height());
+ painter->drawLine(tool.rect.x() - 1, tool.rect.y(), tool.rect.x() - 1, tool.rect.y() + tool.rect.height());
+ painter->drawLine(tool.rect.x(), tool.rect.y(), tool.rect.x(), tool.rect.y() + tool.rect.height());
+ painter->drawLine(tool.rect.x() + 1, tool.rect.y(), tool.rect.x() + 1, tool.rect.y() + tool.rect.height());
+
+ if (tool.state & State_Sunken)
+ {
+ painter->setPen(tool.palette.midlight().color());
+ painter->drawLine(tool.rect.x() - 2, tool.rect.y() + tool.rect.height() - 2,
+ tool.rect.x() + 1, tool.rect.y() + tool.rect.height() -2 );
+ painter->setPen(tool.palette.shadow().color());
+ painter->drawLine(tool.rect.x() - 2, tool.rect.y() + 1,tool.rect.x() + 1, tool.rect.y() + 1);
+ painter->drawLine(tool.rect.x() - 2, tool.rect.y(), tool.rect.x() + 1, tool.rect.y());
+ painter->setPen(tool.palette.light().color());
+ painter->drawLine(tool.rect.x() - 2, tool.rect.y() + tool.rect.height() - 1,
+ tool.rect.x() + 1, tool.rect.y() + tool.rect.height() - 1);
+ }
+ else
+ {
+ painter->setPen(tool.palette.dark().color());
+ painter->drawLine(tool.rect.x() - 2, tool.rect.y(),tool.rect.x() + 1, tool.rect.y());
+ painter->drawLine(tool.rect.x() - 2, tool.rect.y()+tool.rect.height() - 2,tool.rect.x() + 1,
+ tool.rect.y() + tool.rect.height() - 2);
+ painter->setPen(tool.palette.midlight().color());
+ painter->drawLine(tool.rect.x() - 2, tool.rect.y() + 1,tool.rect.x() + 1, tool.rect.y() + 1);
+ painter->setPen(tool.palette.shadow().color());
+ painter->drawLine(tool.rect.x() - 2, tool.rect.y() + tool.rect.height() - 1,
+ tool.rect.x() + 1, tool.rect.y() + tool.rect.height() - 1);
+ }
+ painter->restore();
+ }
+
+
+ if (!flat) {
+ tool.rect.adjust(-3,0,-3,0);
+ painter->save();
+ painter->setPen(tool.palette.button().color());
+ if (tool.state & State_Sunken)
+ painter->drawLine(tool.rect.x() + 2, tool.rect.y() + 10,
+ tool.rect.x() + tool.rect.width(), tool.rect.y() + 10);
+ else
+ painter->drawLine(tool.rect.x() + 1, tool.rect.y() + 9, tool.rect.x() +
+ tool.rect.width() - 1, tool.rect.y() + 9);
+ painter->restore();
+ } else {
+ tool.rect.adjust(-1,0,-1,0);
+ }
+
+ drawPrimitive(PE_IndicatorArrowDown, &tool, painter, widget);
+ }
+
+ if (toolbutton->state & State_HasFocus) {
+ QStyleOptionFocusRect fr;
+ fr.QStyleOption::operator=(*toolbutton);
+ fr.rect.adjust(3, 3, -3, -3);
+ if (toolbutton->features & QStyleOptionToolButton::Menu)
+ fr.rect.adjust(0, 0, -pixelMetric(QStyle::PM_MenuButtonIndicator,
+ toolbutton, widget), 0);
+ drawPrimitive(PE_FrameFocusRect, &fr, painter, widget);
+ }
+ QStyleOptionToolButton label = *toolbutton;
+ int fw = pixelMetric(PM_DefaultFrameWidth, option, widget);
+ label.rect = button.adjusted(fw, fw, -fw, -fw);
+ drawControl(CE_ToolButtonLabel, &label, painter, widget);
+ }
+ break;
+
+#ifndef QT_NO_GROUPBOX
+ case CC_GroupBox:
+ if (const QStyleOptionGroupBox *groupBox = qstyleoption_cast<const QStyleOptionGroupBox *>(option)) {
+ // Draw frame
+ painter->save();
+ QFont f = painter->font();
+ f.setBold(true);
+ painter->setFont(f);
+ QStyleOptionGroupBox groupBoxFont = *groupBox;
+ groupBoxFont.fontMetrics = QFontMetrics(f);
+ QRect textRect = subControlRect(CC_GroupBox, &groupBoxFont, SC_GroupBoxLabel, widget);
+ QRect checkBoxRect = subControlRect(CC_GroupBox, option, SC_GroupBoxCheckBox, widget);
+ if (groupBox->subControls & QStyle::SC_GroupBoxFrame) {
+ QStyleOptionFrameV2 frame;
+ frame.QStyleOption::operator=(*groupBox);
+ frame.features = groupBox->features;
+ frame.lineWidth = groupBox->lineWidth;
+ frame.midLineWidth = groupBox->midLineWidth;
+ frame.rect = subControlRect(CC_GroupBox, option, SC_GroupBoxFrame, widget);
+ painter->save();
+
+ QRegion region(groupBox->rect);
+ if (!groupBox->text.isEmpty()) {
+ bool ltr = groupBox->direction == Qt::LeftToRight;
+ QRect finalRect = checkBoxRect.united(textRect);
+ if (groupBox->subControls & QStyle::SC_GroupBoxCheckBox)
+ finalRect.adjust(ltr ? -4 : 0, 0, ltr ? 0 : 4, 0);
+ region -= finalRect;
+ }
+ painter->setClipRegion(region);
+ drawPrimitive(PE_FrameGroupBox, &frame, painter, widget);
+ painter->restore();
+ }
+
+ // Draw title
+ if ((groupBox->subControls & QStyle::SC_GroupBoxLabel) && !groupBox->text.isEmpty()) {
+ QColor textColor = groupBox->textColor;
+ if (textColor.isValid())
+ painter->setPen(textColor);
+ int alignment = int(groupBox->textAlignment);
+ if (!styleHint(QStyle::SH_UnderlineShortcut, option, widget))
+ alignment |= Qt::TextHideMnemonic;
+
+ drawItemText(painter, textRect, Qt::TextShowMnemonic | Qt::AlignHCenter | alignment,
+ groupBox->palette, groupBox->state & State_Enabled, groupBox->text,
+ textColor.isValid() ? QPalette::NoRole : QPalette::WindowText);
+
+ if (groupBox->state & State_HasFocus) {
+ QStyleOptionFocusRect fropt;
+ fropt.QStyleOption::operator=(*groupBox);
+ fropt.rect = textRect;
+ drawPrimitive(PE_FrameFocusRect, &fropt, painter, widget);
+ }
+ }
+ // Draw checkbox
+ if (groupBox->subControls & SC_GroupBoxCheckBox) {
+ QStyleOptionButton box;
+ box.QStyleOption::operator=(*groupBox);
+ box.rect = checkBoxRect;
+ drawPrimitive(PE_IndicatorCheckBox, &box, painter, widget);
+ }
+ painter->restore();
+ }
+ break;
+#endif //QT_NO_GROUPBOX
+#ifndef QT_NO_COMBOBOX
+ case CC_ComboBox:
+ if (const QStyleOptionComboBox *cmb = qstyleoption_cast<const QStyleOptionComboBox *>(option)) {
+ QBrush editBrush = cmb->palette.brush(QPalette::Base);
+ if ((cmb->subControls & SC_ComboBoxFrame) && cmb->frame)
+ QWindowsCEStylePrivate::drawWinCEPanel(painter, option->rect, option->palette, true, &editBrush);
+ else
+ painter->fillRect(option->rect, editBrush);
+
+ if (cmb->subControls & SC_ComboBoxArrow) {
+ State flags = State_None;
+
+ QRect ar = subControlRect(CC_ComboBox, cmb, SC_ComboBoxArrow, widget);
+ if (cmb->activeSubControls == SC_ComboBoxArrow) {
+ painter->setPen(cmb->palette.dark().color());
+ painter->setBrush(cmb->palette.brush(QPalette::Button));
+ painter->drawRect(ar.adjusted(0, 0, -1, -1));
+ QWindowsCEStylePrivate::drawWinCEButton(painter, ar.adjusted(0, 0, -1, -1), option->palette, true,
+ &cmb->palette.brush(QPalette::Button));
+ } else {
+ // Make qDrawWinButton use the right colors for drawing the shade of the button
+
+ QWindowsCEStylePrivate::drawWinCEButton(painter, ar, option->palette, false,
+ &cmb->palette.brush(QPalette::Button));
+ }
+
+ ar.adjust(2, 2, -2, -2);
+ if (option->state & State_Enabled)
+ flags |= State_Enabled;
+
+ if (cmb->activeSubControls == SC_ComboBoxArrow)
+ flags |= State_Sunken;
+ QStyleOption arrowOpt(0);
+ arrowOpt.rect = ar;
+ arrowOpt.palette = cmb->palette;
+ arrowOpt.state = flags;
+ drawPrimitive(PE_IndicatorArrowDown, &arrowOpt, painter, widget);
+ }
+ if (cmb->subControls & SC_ComboBoxEditField) {
+ QRect re = subControlRect(CC_ComboBox, cmb, SC_ComboBoxEditField, widget);
+ if (cmb->state & State_HasFocus && !cmb->editable)
+ painter->fillRect(re.x(), re.y(), re.width(), re.height(),
+ cmb->palette.brush(QPalette::Highlight));
+ if (cmb->state & State_HasFocus) {
+ painter->setPen(cmb->palette.highlightedText().color());
+ painter->setBackground(cmb->palette.highlight());
+ } else {
+ painter->setPen(cmb->palette.text().color());
+ painter->setBackground(cmb->palette.background());
+ }
+ if (cmb->state & State_HasFocus && !cmb->editable) {
+ QStyleOptionFocusRect focus;
+ focus.QStyleOption::operator=(*cmb);
+ focus.rect = subElementRect(SE_ComboBoxFocusRect, cmb, widget);
+ focus.state |= State_FocusAtBorder;
+ focus.backgroundColor = cmb->palette.highlight().color();
+ drawPrimitive(PE_FrameFocusRect, &focus, painter, widget);
+ }
+ }
+ }
+ break;
+#endif // QT_NO_COMBOBOX
+#ifndef QT_NO_SPINBOX
+ case CC_SpinBox:
+ if (const QStyleOptionSpinBox *sb = qstyleoption_cast<const QStyleOptionSpinBox *>(option)) {
+ QStyleOptionSpinBox copy = *sb;
+ PrimitiveElement pe;
+
+ if (sb->frame && (sb->subControls & SC_SpinBoxFrame)) {
+ QRect r = subControlRect(CC_SpinBox, sb, SC_SpinBoxFrame, widget);
+ QWindowsCEStylePrivate::drawWinCEPanel(painter, r, option->palette, true);
+ }
+ QPalette shadePal(option->palette);
+ shadePal.setColor(QPalette::Button, option->palette.light().color());
+ shadePal.setColor(QPalette::Light, option->palette.button().color());
+
+ bool reverse = QApplication::layoutDirection() == Qt::RightToLeft;
+
+ if (sb->subControls & SC_SpinBoxUp) {
+ copy.subControls = SC_SpinBoxUp;
+ QPalette pal2 = sb->palette;
+ if (!(sb->stepEnabled & QAbstractSpinBox::StepUpEnabled)) {
+ pal2.setCurrentColorGroup(QPalette::Disabled);
+ copy.state &= ~State_Enabled;
+ }
+ copy.palette = pal2;
+ if (sb->activeSubControls == SC_SpinBoxUp && (sb->state & State_Sunken)) {
+ copy.state |= State_On;
+ copy.state |= State_Sunken;
+ } else {
+ copy.state |= State_Raised;
+ copy.state &= ~State_Sunken;
+ }
+ if (reverse)
+ pe = (sb->buttonSymbols == QAbstractSpinBox::PlusMinus ? PE_IndicatorSpinMinus
+ : PE_IndicatorSpinDown);
+ else
+ pe = (sb->buttonSymbols == QAbstractSpinBox::PlusMinus ? PE_IndicatorSpinPlus
+ : PE_IndicatorSpinUp);
+ copy.rect = subControlRect(CC_SpinBox, sb, SC_SpinBoxUp, widget);
+ QWindowsCEStylePrivate::drawWinCEButton(painter, copy.rect, option->palette, copy.state & (State_Sunken | State_On),
+ &copy.palette.brush(QPalette::Button));
+ copy.rect.adjust(3, 0, -4, 0);
+ drawPrimitive(pe, &copy, painter, widget);
+ }
+ if (sb->subControls & SC_SpinBoxDown) {
+ copy.subControls = SC_SpinBoxDown;
+ copy.state = sb->state;
+ QPalette pal2 = sb->palette;
+ if (!(sb->stepEnabled & QAbstractSpinBox::StepDownEnabled)) {
+ pal2.setCurrentColorGroup(QPalette::Disabled);
+ copy.state &= ~State_Enabled;
+ }
+ copy.palette = pal2;
+
+ if (sb->activeSubControls == SC_SpinBoxDown && (sb->state & State_Sunken)) {
+ copy.state |= State_On;
+ copy.state |= State_Sunken;
+ } else {
+ copy.state |= State_Raised;
+ copy.state &= ~State_Sunken;
+ }
+ if (reverse)
+ pe = (sb->buttonSymbols == QAbstractSpinBox::PlusMinus ? PE_IndicatorSpinPlus
+ : PE_IndicatorSpinUp);
+ else
+ pe = (sb->buttonSymbols == QAbstractSpinBox::PlusMinus ? PE_IndicatorSpinMinus
+ : PE_IndicatorSpinDown);
+ copy.rect = subControlRect(CC_SpinBox, sb, SC_SpinBoxDown, widget);
+ QWindowsCEStylePrivate::drawWinCEButton(painter, copy.rect, shadePal, copy.state & (State_Sunken | State_On),
+ &copy.palette.brush(QPalette::Button));
+
+ copy.rect.adjust(3, 0, -4, 0);
+ if (pe == PE_IndicatorArrowUp || pe == PE_IndicatorArrowDown) {
+ copy.rect = copy.rect.adjusted(1, 1, -1, -1);
+ drawPrimitive(pe, &copy, painter, widget);
+ }
+ else {
+ drawPrimitive(pe, &copy, painter, widget);
+ }
+ if (sb->frame && (sb->subControls & SC_SpinBoxFrame)) {
+ QRect r = subControlRect(CC_SpinBox, sb, SC_SpinBoxEditField, widget);
+ painter->save();
+ painter->setPen(option->palette.light().color());
+ painter->drawLine(r.x() + 1 + r.width(), r.y() - 2, r.x() + 1 + r.width(), r.y() + r.height() + 1);
+ painter->setPen(option->palette.midlight().color());
+ painter->drawLine(r.x() + r.width(), r.y() - 1, r.x() + r.width(), r.y() + r.height());
+ painter->restore();
+ }
+ }
+ }
+ break;
+#endif // QT_NO_SPINBOX
+
+ default:
+ QWindowsStyle::drawComplexControl(control, option, painter, widget);
+ break;
+ }
+}
+
+void QWindowsCEStyle::drawItemText(QPainter *painter, const QRect &rect, int alignment, const QPalette &pal,
+ bool enabled, const QString& text, QPalette::ColorRole textRole) const {
+ if (text.isEmpty())
+ return;
+ QPen savedPen;
+ if (textRole != QPalette::NoRole) {
+ savedPen = painter->pen();
+ painter->setPen(pal.color(textRole));
+ }
+ if (!enabled) {
+ QPen pen = painter->pen();
+ painter->setPen(pal.light().color());
+ //painter->drawText(rect.adjusted(1, 1, 1, 1), alignment, text);
+ painter->setPen(pen);
+ }
+ painter->drawText(rect, alignment, text);
+ if (textRole != QPalette::NoRole)
+ painter->setPen(savedPen);
+}
+
+
+QSize QWindowsCEStyle::sizeFromContents(ContentsType type, const QStyleOption *option,
+ const QSize &size, const QWidget *widget) const {
+ QSize newSize = QWindowsStyle::sizeFromContents(type, option, size, widget);
+ switch (type) {
+ case CT_PushButton:
+ if (const QStyleOptionButton *btn = qstyleoption_cast<const QStyleOptionButton *>(option)) {
+ newSize = QWindowsStyle::sizeFromContents(type, option, size, widget);
+ int w = newSize.width(),
+ h = newSize.height();
+ int defwidth = 0;
+ if (btn->features & QStyleOptionButton::AutoDefaultButton)
+ defwidth = 2 * pixelMetric(PM_ButtonDefaultIndicator, btn, widget);
+ if (w < 75 + defwidth && btn->icon.isNull())
+ w = 75 + defwidth;
+ if (h < 23 + defwidth)
+ h = 23 + defwidth;
+ newSize = QSize(w+14, h);
+ }
+ break;
+
+ case CT_RadioButton:
+ case CT_CheckBox:
+ if (const QStyleOptionButton *btn = qstyleoption_cast<const QStyleOptionButton *>(option)) {
+ bool isRadio = (type == CT_RadioButton);
+ QRect irect = visualRect(btn->direction, btn->rect,
+ subElementRect(isRadio ? SE_RadioButtonIndicator
+ : SE_CheckBoxIndicator, btn, widget));
+ int h = pixelMetric(isRadio ? PM_ExclusiveIndicatorHeight
+ : PM_IndicatorHeight, btn, widget);
+ int margins = (!btn->icon.isNull() && btn->text.isEmpty()) ? 0 : 10;
+ newSize += QSize(irect.right() + margins, 4);
+ newSize.setHeight(qMax(newSize.height(), h));
+ }
+ break;
+ case CT_ComboBox:
+ if (const QStyleOptionComboBox *cmb = qstyleoption_cast<const QStyleOptionComboBox *>(option)) {
+ int fw = cmb->frame ? pixelMetric(PM_ComboBoxFrameWidth, option, widget) * 2 : 0;
+ newSize = QSize(newSize.width() + fw -1, qMax(24, newSize.height() + fw-1));
+ }
+ break;
+#ifndef QT_NO_SPINBOX
+ case CT_SpinBox:
+ if (const QStyleOptionSpinBox *spnb = qstyleoption_cast<const QStyleOptionSpinBox *>(option)) {
+ int fw = spnb->frame ? pixelMetric(PM_SpinBoxFrameWidth, option, widget) * 2 : 0;
+ newSize = QSize(newSize.width() + fw - 5, newSize.height() + fw - 6);
+ }
+ break;
+#endif
+ case CT_LineEdit:
+ newSize += QSize(0,1);
+ break;
+ case CT_MenuBarItem:
+ newSize += QSize(5, 1);
+ break;
+ case CT_MenuItem:
+ newSize += QSize(0, -2);
+ break;
+ case CT_MenuBar:
+ newSize += QSize(0, -1);
+ break;
+ case CT_ToolButton:
+ if (const QStyleOptionToolButton *b = qstyleoption_cast<const QStyleOptionToolButton *>(option)) {
+ if (b->toolButtonStyle != Qt::ToolButtonIconOnly)
+ newSize = QSize(newSize.width() + 1, newSize.height() - 1);
+ else
+ newSize = QSize(newSize.width() + 1, newSize.height());
+ }
+ break;
+
+ default:
+ break;
+ }
+ return newSize;
+}
+
+QRect QWindowsCEStyle::subElementRect(SubElement element, const QStyleOption *option, const QWidget *widget) const {
+ QRect rect = QWindowsStyle::subElementRect(element, option, widget);
+ switch (element) {
+#ifndef QT_NO_COMBOBOX
+ case SE_ComboBoxFocusRect:
+ if (const QStyleOptionComboBox *cb = qstyleoption_cast<const QStyleOptionComboBox *>(option)) {
+ int margin = cb->frame ? 3 : 0;
+ rect.setRect(margin, margin, option->rect.width() - 2*margin - 20, option->rect.height() - 2*margin);
+ rect = visualRect(option->direction, option->rect, rect);
+ }
+ break;
+#endif // QT_NO_COMBOBOX
+ default:
+ break;
+ }
+ return rect;
+}
+
+QRect QWindowsCEStyle::subControlRect(ComplexControl control, const QStyleOptionComplex *option,
+ SubControl subControl, const QWidget *widget) const {
+ QRect rect = QWindowsStyle::subControlRect(control, option, subControl, widget);
+ switch (control) {
+#ifndef QT_NO_SLIDER
+ case CC_Slider:
+ if (const QStyleOptionSlider *slider = qstyleoption_cast<const QStyleOptionSlider *>(option)) {
+ int tickOffset = pixelMetric(PM_SliderTickmarkOffset, slider, widget);
+ int thickness = pixelMetric(PM_SliderControlThickness, slider, widget);
+
+ switch (subControl) {
+ case SC_SliderHandle: {
+ int sliderPos = 0;
+ int len = pixelMetric(PM_SliderLength, slider, widget);
+ bool horizontal = slider->orientation == Qt::Horizontal;
+ sliderPos = sliderPositionFromValue(slider->minimum, slider->maximum,
+ slider->sliderPosition,
+ (horizontal ? slider->rect.width()
+ : slider->rect.height()) - len,
+ slider->upsideDown);
+ if (horizontal)
+ rect.setRect(slider->rect.x() + sliderPos, slider->rect.y() + tickOffset, len, thickness);
+ else
+ rect.setRect(slider->rect.x() + tickOffset, slider->rect.y() + sliderPos, thickness, len);
+ break; }
+ default:
+ break;
+ }
+ rect = visualRect(slider->direction, slider->rect, rect);
+ }
+ break;
+#endif //QT_NO_SLIDER
+#ifndef QT_NO_COMBOBOX
+ case CC_ComboBox:
+ if (const QStyleOptionComboBox *cb = qstyleoption_cast<const QStyleOptionComboBox *>(option)) {
+ int x = cb->rect.x(),
+ y = cb->rect.y(),
+ wi = cb->rect.width(),
+ he = cb->rect.height();
+ int xpos = x;
+ int margin = cb->frame ? 3 : 0;
+ int bmarg = cb->frame ? 2 : 0;
+ xpos += wi - (he - 2*bmarg) - bmarg;
+ switch (subControl) {
+ case SC_ComboBoxArrow:
+ rect.setRect(xpos, y + bmarg, he - 2*bmarg, he - 2*bmarg);
+ break;
+ case SC_ComboBoxEditField:
+ rect.setRect(x + margin, y + margin, wi - 2 * margin - (he - 2*bmarg), he - 2 * margin);
+ break;
+ case SC_ComboBoxListBoxPopup:
+ rect = cb->rect;
+ break;
+ case SC_ComboBoxFrame:
+ rect = cb->rect;
+ break;
+ default:
+ break;
+ }
+ rect = visualRect(cb->direction, cb->rect, rect);
+ }
+#endif //QT_NO_COMBOBOX
+#ifndef QT_NO_SPINBOX
+ case CC_SpinBox:
+ if (const QStyleOptionSpinBox *spinbox = qstyleoption_cast<const QStyleOptionSpinBox *>(option)) {
+ QSize bs;
+ int fw = spinbox->frame ? pixelMetric(PM_SpinBoxFrameWidth, spinbox, widget) : 0;
+ bs.setWidth(qMax(18, (spinbox->rect.height() / 2 - fw + 1)));
+ // 1.6 -approximate golden mean
+ bs.setHeight(qMax(18, qMin((bs.height() * 8 / 5), (spinbox->rect.width() / 4))));
+ bs = bs.expandedTo(QApplication::globalStrut());
+ int y = fw;
+ int x, lx, rx;
+ x = spinbox->rect.width() - y - bs.width() * 2;
+ lx = fw;
+ rx = x - fw;
+ switch (subControl) {
+ case SC_SpinBoxUp:
+ rect = QRect(x + bs.width(), y, bs.width(), bs.height());
+ break;
+ case SC_SpinBoxDown:
+ rect = QRect(x, y , bs.width(), bs.height());
+ break;
+ case SC_SpinBoxEditField:
+ rect = QRect(lx, fw, rx-2, spinbox->rect.height() - 2*fw);
+ break;
+ case SC_SpinBoxFrame:
+ rect = spinbox->rect;
+ default:
+ break;
+ }
+ rect = visualRect(spinbox->direction, spinbox->rect, rect);
+ }
+ break;
+#endif // Qt_NO_SPINBOX
+#ifndef QT_NO_GROUPBOX
+ case CC_GroupBox: {
+ if (const QStyleOptionGroupBox *groupBox = qstyleoption_cast<const QStyleOptionGroupBox *>(option)) {
+ switch (subControl) {
+ case SC_GroupBoxFrame:
+ // FALL THROUGH
+ case SC_GroupBoxContents: {
+ int topMargin = 0;
+ int topHeight = 0;
+ int bottomMargin = 0;
+ int noLabelMargin = 0;
+ QRect frameRect = groupBox->rect;
+ int verticalAlignment = styleHint(SH_GroupBox_TextLabelVerticalAlignment, groupBox, widget);
+ if (groupBox->text.size()) {
+ topHeight = groupBox->fontMetrics.height();
+ if (verticalAlignment & Qt::AlignVCenter)
+ topMargin = topHeight / 2;
+ else if (verticalAlignment & Qt::AlignTop)
+ topMargin = -topHeight/2;
+ }
+ else {
+ topHeight = groupBox->fontMetrics.height();
+ noLabelMargin = topHeight / 2;
+ if (verticalAlignment & Qt::AlignVCenter) {
+ topMargin = topHeight / 4 - 4;
+ bottomMargin = topHeight / 4 - 4;
+ }
+ else if (verticalAlignment & Qt::AlignTop) {
+ topMargin = topHeight/2 - 4;
+ bottomMargin = topHeight/2 - 4;
+ }
+ }
+
+ if (subControl == SC_GroupBoxFrame) {
+ frameRect.setTop(topMargin);
+ frameRect.setBottom(frameRect.height() + bottomMargin);
+ rect = frameRect;
+ break;
+ }
+
+ int frameWidth = 0;
+ if ((groupBox->features & QStyleOptionFrameV2::Flat) == 0)
+ frameWidth = pixelMetric(PM_DefaultFrameWidth, groupBox, widget);
+ rect = frameRect.adjusted(frameWidth, frameWidth + topHeight, -frameWidth, -frameWidth - noLabelMargin);
+ break;
+ }
+ case SC_GroupBoxCheckBox:
+ // FALL THROUGH
+ case SC_GroupBoxLabel: {
+ QFontMetrics fontMetrics = groupBox->fontMetrics;
+ int h = fontMetrics.height();
+ int tw = fontMetrics.size(Qt::TextShowMnemonic, groupBox->text + QLatin1Char(' ')).width();
+ int marg = (groupBox->features & QStyleOptionFrameV2::Flat) ? 0 : 8;
+ rect = groupBox->rect.adjusted(marg, 0, -marg, 0);
+ rect.setHeight(h);
+
+ int indicatorWidth = pixelMetric(PM_IndicatorWidth, option, widget);
+ int indicatorSpace = pixelMetric(PM_CheckBoxLabelSpacing, option, widget) - 1;
+ bool hasCheckBox = groupBox->subControls & QStyle::SC_GroupBoxCheckBox;
+ int checkBoxSize = hasCheckBox ? (indicatorWidth + indicatorSpace) : 0;
+
+ // Adjusted rect for label + indicatorWidth + indicatorSpace
+ QRect totalRect = alignedRect(groupBox->direction, groupBox->textAlignment,
+ QSize(tw + checkBoxSize, h), rect);
+
+ // Adjust totalRect if checkbox is set
+ if (hasCheckBox) {
+ bool ltr = groupBox->direction == Qt::LeftToRight;
+ int left = 0;
+ // Adjust for check box
+ if (subControl == SC_GroupBoxCheckBox) {
+ int indicatorHeight = pixelMetric(PM_IndicatorHeight, option, widget);
+ left = ltr ? totalRect.left() : (totalRect.right() - indicatorWidth);
+ int top = totalRect.top() + (fontMetrics.height() - indicatorHeight) / 2;
+ totalRect.setRect(left, top, indicatorWidth, indicatorHeight);
+ // Adjust for label
+ } else {
+ left = ltr ? (totalRect.left() + checkBoxSize - 2) : totalRect.left();
+ totalRect.setRect(left, totalRect.top(),
+ totalRect.width() - checkBoxSize, totalRect.height());
+ }
+ }
+ rect = totalRect;
+ break;
+ }
+ default:
+ break;
+ }
+ }
+ break;
+ }
+#endif // QT_NO_GROUPBOX
+ default:
+ break;
+ }
+ return rect;
+}
+
+QStyle::SubControl QWindowsCEStyle::hitTestComplexControl(ComplexControl control, const QStyleOptionComplex *option,
+ const QPoint &pos, const QWidget *widget) const {
+ /*switch (control) {
+ default:
+ break;
+ }*/
+ return QWindowsStyle::hitTestComplexControl(control, option, pos, widget);
+}
+
+
+QPalette QWindowsCEStyle::standardPalette() const {
+ QPalette palette (Qt::black,QColor(198, 195, 198), QColor(222, 223, 222 ),
+ QColor(132, 130, 132), QColor(198, 195, 198) , Qt::black, Qt::white, Qt::white, QColor(198, 195, 198));
+ palette.setColor(QPalette::Window, QColor(198, 195, 198));
+ palette.setColor(QPalette::Base, Qt::white);
+ palette.setColor(QPalette::Button, QColor(198, 195, 198));
+ palette.setColor(QPalette::Highlight, QColor(0, 0, 132));
+ palette.setColor(QPalette::Light, Qt::white);
+ palette.setColor(QPalette::Midlight, QColor(222, 223, 222 ));
+ palette.setColor(QPalette::Dark, QColor(132, 130, 132));
+ palette.setColor(QPalette::Mid, QColor(132, 130, 132));
+ palette.setColor(QPalette::Shadow, QColor(0, 0, 0));
+ palette.setColor(QPalette::BrightText, QColor(33, 162, 33)); //color for ItemView checked indicator (arrow)
+ palette.setColor(QPalette::Link, QColor(24,81,132)); // color for the box around the ItemView indicator
+
+ return palette;
+}
+
+void QWindowsCEStyle::polish(QApplication *app) {
+ QWindowsStyle::polish(app);
+}
+
+void QWindowsCEStyle::polish(QWidget *widget) {
+ QWindowsStyle::polish(widget);
+}
+
+void QWindowsCEStyle::polish(QPalette &palette) {
+ QWindowsStyle::polish(palette);
+}
+
+int QWindowsCEStyle::pixelMetric(PixelMetric pm, const QStyleOption *opt, const QWidget *widget) const {
+ int ret;
+
+ switch (pm) {
+ case PM_DefaultFrameWidth:
+ ret = 1;
+ break;
+
+ case PM_MenuBarHMargin:
+ ret = 2;
+ break;
+ case PM_MenuBarVMargin:
+ ret = 2;
+ break;
+ /*case PM_MenuBarItemSpacing:
+ ret = 2;
+ break;*/
+
+ case PM_MenuButtonIndicator:
+ ret = 10;
+ break;
+
+ case PM_SpinBoxFrameWidth:
+ ret = 2;
+ break;
+ case PM_ButtonDefaultIndicator:
+ case PM_ButtonShiftHorizontal:
+ case PM_ButtonShiftVertical:
+ ret = 1;
+ break;
+#ifndef QT_NO_TABBAR
+ case PM_TabBarTabShiftHorizontal:
+ ret = 0;
+ break;
+ case PM_TabBarTabShiftVertical:
+ ret = 6;
+ break;
+#endif
+ case PM_MaximumDragDistance:
+ ret = 60;
+ break;
+
+ case PM_IndicatorWidth:
+ ret = windowsCEIndicatorSize;
+ break;
+
+ case PM_IndicatorHeight:
+ ret = windowsCEIndicatorSize;
+ break;
+
+ case PM_ExclusiveIndicatorWidth:
+ ret = windowsCEExclusiveIndicatorSize;
+ break;
+
+ case PM_ExclusiveIndicatorHeight:
+ ret = windowsCEExclusiveIndicatorSize;;
+ break;
+
+#ifndef QT_NO_SLIDER
+ case PM_SliderLength:
+ ret = 12;
+ break;
+ case PM_SliderThickness:
+ ret = windowsCESliderThickness;
+ break;
+
+ case PM_TabBarScrollButtonWidth:
+ ret = 18;
+ break;
+
+ // Returns the number of pixels to use for the business part of the
+ // slider (i.e., the non-tickmark portion). The remaining space is shared
+ // equally between the tickmark regions.
+ case PM_SliderControlThickness:
+ if (const QStyleOptionSlider *sl = qstyleoption_cast<const QStyleOptionSlider *>(opt)) {
+ int space = (sl->orientation == Qt::Horizontal) ? sl->rect.height() : sl->rect.width();
+ int ticks = sl->tickPosition;
+ int n = 0;
+ if (ticks & QSlider::TicksAbove)
+ ++n;
+ if (ticks & QSlider::TicksBelow)
+ ++n;
+ if (!n) {
+ ret = space;
+ break;
+ }
+ int thick = 12;
+ if (ticks != QSlider::TicksBothSides && ticks != QSlider::NoTicks)
+ thick += pixelMetric(PM_SliderLength, sl, widget) / 4;
+
+ space -= thick;
+ if (space > 0)
+ thick += (space * 2) / (n + 2);
+ ret = thick;
+ } else {
+ ret = 0;
+ }
+ break;
+#endif // QT_NO_SLIDER
+
+#ifndef QT_NO_MENU
+
+ case PM_SmallIconSize:
+ ret = windowsCEIconSize;
+ break;
+ case PM_ButtonMargin:
+ ret = 6;
+ break;
+
+ case PM_LargeIconSize:
+ ret = 32;
+ break;
+
+ case PM_IconViewIconSize:
+ ret = pixelMetric(PM_LargeIconSize, opt, widget);
+ break;
+
+ case PM_ToolBarIconSize:
+ ret = windowsCEIconSize;
+ break;
+ case PM_DockWidgetTitleMargin:
+ ret = 2;
+ break;
+#if defined(Q_WS_WIN)
+// case PM_DockWidgetFrameWidth:
+// ret = GetSystemMetrics(SM_CXFRAME);
+// break;
+#else
+ case PM_DockWidgetFrameWidth:
+ ret = 4;
+ break;
+#endif // Q_WS_WIN
+ break;
+
+#endif // QT_NO_MENU
+
+ case PM_TitleBarHeight:
+ ret = 30;
+ break;
+ case PM_ScrollBarExtent:
+ ret = 19;
+ break;
+ case PM_SplitterWidth:
+ ret = qMax(4, QApplication::globalStrut().width());
+ break;
+
+#if defined(Q_WS_WIN)
+ case PM_MDIFrameWidth:
+ ret = 3;
+ break;
+#endif
+ case PM_ToolBarItemMargin:
+ ret = 1;
+ break;
+ case PM_ToolBarItemSpacing:
+ ret = 0;
+ break;
+ case PM_ToolBarHandleExtent:
+ ret = 10;
+ break;
+ case PM_ButtonIconSize:
+ ret = 22;
+ break;
+ default:
+ ret = QWindowsStyle::pixelMetric(pm, opt, widget);
+ break;
+ }
+ return ret;
+}
+
+QPixmap QWindowsCEStyle::standardPixmap(StandardPixmap standardPixmap, const QStyleOption *opt,
+ const QWidget *widget) const {
+#ifndef QT_NO_IMAGEFORMAT_XPM
+ /*switch (standardPixmap) {
+
+ default:
+ break;
+ }*/
+#endif //QT_NO_IMAGEFORMAT_XPM
+ return QWindowsStyle::standardPixmap(standardPixmap, opt, widget);
+}
+
+int QWindowsCEStyle::styleHint(StyleHint hint, const QStyleOption *opt, const QWidget *widget,
+ QStyleHintReturn *returnData) const {
+ int ret;
+ switch (hint) {
+ case SH_TabBar_ElideMode:
+ ret = Qt::ElideMiddle;
+ break;
+ case SH_EtchDisabledText:
+ ret = false;
+ default:
+ ret = QWindowsStyle::styleHint(hint, opt, widget, returnData);
+ break;
+ }
+ return ret;
+}
+
+void QWindowsCEStylePrivate::drawWinShades(QPainter *p,
+ int x, int y, int w, int h,
+ const QColor &c1, const QColor &c2,
+ const QColor &c3, const QColor &c4,
+ const QBrush *fill) {
+ if (w < 2 || h < 2) // can't do anything with that
+ return;
+ QPen oldPen = p->pen();
+ QPoint a[3] = { QPoint(x, y+h-2), QPoint(x, y), QPoint(x+w-2, y) };
+ p->setPen(c1);
+ p->drawPolyline(a, 3);
+ QPoint b[3] = { QPoint(x, y+h-1), QPoint(x+w-1, y+h-1), QPoint(x+w-1, y) };
+ p->setPen(c2);
+ p->drawPolyline(b, 3);
+ if (w > 4 && h > 4) {
+ QPoint c[3] = { QPoint(x+1, y+h-3), QPoint(x+1, y+1), QPoint(x+w-3, y+1) };
+ p->setPen(c3);
+ p->drawPolyline(c, 3);
+ QPoint d[3] = { QPoint(x+1, y+h-2), QPoint(x+w-2, y+h-2), QPoint(x+w-2, y+1) };
+ p->setPen(c4);
+ p->drawPolyline(d, 3);
+ if (fill)
+ p->fillRect(QRect(x+2, y+2, w-4, h-4), *fill);
+ }
+ p->setPen(oldPen);
+}
+
+void QWindowsCEStylePrivate::drawWinCEShades(QPainter *p,
+ int x, int y, int w, int h,
+ const QColor &c1, const QColor &c2,
+ const QColor &c3, const QColor &c4,
+ const QBrush *fill) {
+ if (w < 2 || h < 2) // can't do anything with that
+ return;
+ QPen oldPen = p->pen();
+ QPoint b[3] = { QPoint(x, y+h-1), QPoint(x+w-1, y+h-1), QPoint(x+w-1, y) };
+ p->setPen(c2);
+ p->drawPolyline(b, 3);
+ if (w > 4 && h > 4) {
+ QPoint c[3] = { QPoint(x+1, y+h-3), QPoint(x+1, y+1), QPoint(x+w-3, y+1) };
+ p->setPen(c3);
+ p->drawPolyline(c, 3);
+ QPoint d[5] = { QPoint(x, y+h-2), QPoint(x+w-2, y+h-2), QPoint(x+w-2, y), QPoint(x, y), QPoint(x, y+h-2) };
+ p->setPen(c4);
+ p->drawPolyline(d, 5);
+ if (fill)
+ p->fillRect(QRect(x+2, y+2, w-4, h-4), *fill);
+ }
+ QPoint a[3] = { QPoint(x+1, y+h-3), QPoint(x+1, y+1), QPoint(x+w-3, y+1) };
+ p->setPen(c1);
+ p->drawPolyline(a, 3);
+ p->setPen(oldPen);
+}
+
+void QWindowsCEStylePrivate::drawWinCEShadesSunken(QPainter *p,
+ int x, int y, int w, int h,
+ const QColor &c1, const QColor &c2,
+ const QColor &c3, const QColor &c4,
+ const QBrush *fill) {
+ if (w < 2 || h < 2) // can't do anything with that
+ return;
+ QPen oldPen = p->pen();
+
+ QPoint b[3] = { QPoint(x, y+h-1), QPoint(x+w-1, y+h-1), QPoint(x+w-1, y) };
+ p->setPen(c2);
+ p->drawPolyline(b, 3);
+ if (w > 4 && h > 4) {
+ QPoint d[3] = { QPoint(x, y+h-2), QPoint(x+w-2, y+h-2), QPoint(x+w-2, y) };
+ p->setPen(c4);
+ p->drawPolyline(d, 3);
+ QPoint c[3] = { QPoint(x, y+h-2), QPoint(x, y), QPoint(x+w-2, y) };
+ p->setPen(c3);
+ p->drawPolyline(c, 3);
+ if (fill)
+ p->fillRect(QRect(x+2, y+2, w-4, h-4), *fill);
+ }
+ QPoint a[3] = { QPoint(x+1, y+h-3), QPoint(x+1, y+1), QPoint(x+w-3, y+1) };
+ p->setPen(c1);
+ p->drawPolyline(a, 3);
+ p->setPen(oldPen);
+}
+
+
+void QWindowsCEStylePrivate::drawWinCEButton(QPainter *p, int x, int y, int w, int h,
+ const QPalette &pal, bool sunken,
+ const QBrush *fill) {
+ if (sunken)
+ drawWinCEShadesSunken(p, x, y, w, h,
+ pal.shadow().color(), pal.light().color(), pal.shadow().color(),
+ pal.midlight().color(), fill);
+ else
+ drawWinCEShades(p, x, y, w, h,
+ pal.midlight().color(), pal.shadow().color(), pal.button().color(),
+ pal.dark().color(), fill);
+}
+
+void QWindowsCEStylePrivate::drawWinCEPanel(QPainter *p, int x, int y, int w, int h,
+ const QPalette &pal, bool sunken,
+ const QBrush *fill) {
+ if (sunken)
+ drawWinShades(p, x, y, w, h,
+ pal.dark().color(), pal.light().color(), pal.shadow().color(),
+ pal.midlight().color(), fill);
+ else
+ drawWinShades(p, x, y, w, h,
+ pal.light().color(), pal.shadow().color(), pal.button().color(),
+ pal.midlight().color(), fill);
+}
+
+void QWindowsCEStylePrivate::drawWinCEButton(QPainter *p, const QRect &r,
+ const QPalette &pal, bool sunken, const QBrush *fill) {
+ drawWinCEButton(p, r.x(), r.y(), r.width(), r.height(), pal, sunken, fill);
+}
+
+void QWindowsCEStylePrivate::drawWinCEPanel(QPainter *p, const QRect &r,
+ const QPalette &pal, bool sunken, const QBrush *fill) {
+ drawWinCEPanel(p, r.x(), r.y(), r.width(), r.height(), pal, sunken, fill);
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_STYLE_WINDOWSCE
diff --git a/src/gui/styles/qwindowscestyle.h b/src/gui/styles/qwindowscestyle.h
new file mode 100644
index 0000000000..fbb6d691fd
--- /dev/null
+++ b/src/gui/styles/qwindowscestyle.h
@@ -0,0 +1,103 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QWINDOWSCESTYLE_H
+#define QWINDOWSCESTYLE_H
+
+#include <QtGui/qwindowsstyle.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+#if !defined(QT_NO_STYLE_WINDOWSCE)
+
+class Q_GUI_EXPORT QWindowsCEStyle : public QWindowsStyle
+{
+ Q_OBJECT
+public:
+ QWindowsCEStyle();
+
+ void drawPrimitive(PrimitiveElement element, const QStyleOption *option,
+ QPainter *painter, const QWidget *widget = 0) const;
+
+ void drawControl(ControlElement element, const QStyleOption *option,
+ QPainter *painter, const QWidget *widget) const;
+
+ void drawComplexControl(ComplexControl control, const QStyleOptionComplex *option,
+ QPainter *painter, const QWidget *widget) const;
+
+ virtual void drawItemText(QPainter *painter, const QRect &rect,
+ int flags, const QPalette &pal, bool enabled,
+ const QString &text, QPalette::ColorRole textRole = QPalette::NoRole) const;
+
+ QSize sizeFromContents(ContentsType type, const QStyleOption *option,
+ const QSize &size, const QWidget *widget) const;
+
+ QRect subElementRect(SubElement element, const QStyleOption *option, const QWidget *widget) const;
+ QRect subControlRect(ComplexControl cc, const QStyleOptionComplex *opt,
+ SubControl sc, const QWidget *widget) const;
+
+ SubControl hitTestComplexControl(ComplexControl control, const QStyleOptionComplex *option,
+ const QPoint &pos, const QWidget *widget = 0) const;
+
+ QPixmap standardPixmap(StandardPixmap standardPixmap, const QStyleOption *opt,
+ const QWidget *widget = 0) const;
+ int pixelMetric(PixelMetric metric, const QStyleOption *option = 0, const QWidget *widget = 0) const;
+
+ int styleHint(StyleHint hint, const QStyleOption *opt = 0, const QWidget *widget = 0,
+ QStyleHintReturn *returnData = 0) const;
+
+ void polish(QWidget *widget);
+ void polish(QPalette &palette);
+ void polish(QApplication *app);
+ QPalette standardPalette() const;
+};
+
+#endif // QT_NO_STYLE_WINDOWSCE
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QWINDOWSCESTYLE_H
diff --git a/src/gui/styles/qwindowscestyle_p.h b/src/gui/styles/qwindowscestyle_p.h
new file mode 100644
index 0000000000..177d93058d
--- /dev/null
+++ b/src/gui/styles/qwindowscestyle_p.h
@@ -0,0 +1,118 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QWINDOWSCE_P_H
+#define QWINDOWSCE_P_H
+
+#include "qwindowscestyle.h"
+#include <private/qwindowsstyle_p.h>
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of qapplication_*.cpp, qwidget*.cpp and qfiledialog.cpp. This header
+// file may change from version to version without notice, or even be removed.
+//
+// We mean it.
+//
+
+QT_BEGIN_NAMESPACE
+
+class QPainter;
+class QPalette;
+class QPoint;
+class QColor;
+class QBrush;
+class QRect;
+
+// Private class
+class QWindowsCEStylePrivate : public QWindowsStylePrivate
+{
+ Q_DECLARE_PUBLIC(QWindowsCEStyle)
+public:
+ inline QWindowsCEStylePrivate()
+ { }
+
+
+static void drawWinCEButton(QPainter *p, int x, int y, int w, int h,
+ const QPalette &pal, bool sunken = false,
+ const QBrush *fill = 0);
+
+static void drawWinCEButton(QPainter *p, const QRect &r,
+ const QPalette &pal, bool sunken = false,
+ const QBrush *fill = 0);
+
+static void drawWinCEPanel(QPainter *p, int x, int y, int w, int h,
+ const QPalette &pal, bool sunken = false,
+ const QBrush *fill = 0);
+
+static void drawWinCEPanel(QPainter *p, const QRect &r,
+ const QPalette &pal, bool sunken = false,
+ const QBrush *fill = 0);
+
+static void drawWinShades(QPainter *p,
+ int x, int y, int w, int h,
+ const QColor &c1, const QColor &c2,
+ const QColor &c3, const QColor &c4,
+ const QBrush *fill);
+
+static void drawWinCEShades(QPainter *p,
+ int x, int y, int w, int h,
+ const QColor &c1, const QColor &c2,
+ const QColor &c3, const QColor &c4,
+ const QBrush *fill);
+
+static void drawWinCEShadesSunken(QPainter *p,
+ int x, int y, int w, int h,
+ const QColor &c1, const QColor &c2,
+ const QColor &c3, const QColor &c4,
+ const QBrush *fill);
+
+
+
+
+};
+
+QT_END_NAMESPACE
+
+#endif //QWINDOWSCE_P_H
diff --git a/src/gui/styles/qwindowsmobilestyle.cpp b/src/gui/styles/qwindowsmobilestyle.cpp
new file mode 100644
index 0000000000..c52c700367
--- /dev/null
+++ b/src/gui/styles/qwindowsmobilestyle.cpp
@@ -0,0 +1,3503 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qwindowsmobilestyle.h"
+#include "qwindowsmobilestyle_p.h"
+
+#if !defined(QT_NO_STYLE_WINDOWSMOBILE) || defined(QT_PLUGIN)
+
+#include "qpainterpath.h"
+#include "qapplication.h"
+#include "qdesktopwidget.h"
+#include "qwidget.h"
+#include "qdockwidget.h"
+#include "qframe.h"
+#include "qmenu.h"
+#include "qpaintengine.h"
+#include "qpainter.h"
+#include "qgroupbox.h"
+#include "qstyleoption.h"
+#include "qlistview.h"
+#include "qdrawutil.h"
+#include "qtoolbar.h"
+#include "qabstractscrollarea.h"
+#include "qabstractbutton.h"
+#include "qcombobox.h"
+#include "qabstractscrollarea.h"
+#include "qframe.h"
+#include "qscrollbar.h"
+#include "qabstractitemview.h"
+#include "qmenubar.h"
+#include "qtoolbutton.h"
+#include "qtextedit.h"
+#include "qdialog.h"
+#include "qdebug.h"
+#include "qtabwidget.h"
+
+#ifdef Q_OS_WINCE
+#include "qt_windows.h"
+#include "qguifunctions_wince.h"
+extern bool qt_wince_is_high_dpi(); //defined in qguifunctions_wince.cpp
+extern bool qt_wince_is_smartphone(); //defined in qguifunctions_wince.cpp
+#endif // Q_OS_WINCE
+
+QT_BEGIN_NAMESPACE
+
+static const int windowsItemFrame = 1; // menu item frame width
+static const int windowsItemHMargin = 2; // menu item hor text margin
+static const int windowsItemVMargin = 2; // menu item ver text margin
+static const int windowsArrowHMargin = 6; // arrow horizontal margin
+static const int windowsRightBorder = 15; // right border on windows
+static const int windowsCheckMarkWidth = 14; // checkmarks width on windows
+
+static const int windowsMobileitemViewCheckBoxSize = 13;
+static const int windowsMobileFrameGroupBoxOffset = 9;
+static const int windowsMobileIndicatorSize = 14;
+static const int windowsMobileExclusiveIndicatorSize = 14;
+static const int windowsMobileSliderThickness = 6;
+static const int windowsMobileIconSize = 16;
+static const int PE_IndicatorArrowUpBig = 0xf000101;
+static const int PE_IndicatorArrowDownBig = 0xf000102;
+static const int PE_IndicatorArrowLeftBig = 0xf000103;
+static const int PE_IndicatorArrowRightBig = 0xf000104;
+
+/* XPM */
+static const char *const radiobutton_xpm[] = {
+ "30 30 2 1",
+ " c None",
+ ". c #000000",
+ " ........ ",
+ " .............. ",
+ " .... .... ",
+ " .... .... ",
+ " ... ... ",
+ " ... ... ",
+ " .. .. ",
+ " .. .. ",
+ " ... ... ",
+ " .. .. ",
+ " .. .. ",
+ ".. ..",
+ ".. ..",
+ ".. ..",
+ ".. ..",
+ ".. ..",
+ ".. ..",
+ ".. ..",
+ ".. ..",
+ " .. .. ",
+ " .. .. ",
+ " ... ... ",
+ " .. .. ",
+ " .. .. ",
+ " ... ... ",
+ " ... ... ",
+ " .... .... ",
+ " .... .... ",
+ " .............. ",
+ " ........ "};
+
+/* XPM */
+static const char * const radiobutton_low_xpm[] = {
+ "15 15 2 1",
+ " c None",
+ ". c #000000",
+ " ..... ",
+ " .. .. ",
+ " . . ",
+ " . . ",
+ " . . ",
+ ". .",
+ ". .",
+ ". .",
+ ". .",
+ ". .",
+ " . . ",
+ " . . ",
+ " . . ",
+ " .. .. ",
+ " ..... "};
+
+/* XPM */
+ static const char * const arrowleft_big_xpm[] = {
+ "9 17 2 1",
+ " c None",
+ ". c #000000",
+ " .",
+ " ..",
+ " ...",
+ " ....",
+ " .....",
+ " ......",
+ " .......",
+ " ........",
+ ".........",
+ " ........",
+ " .......",
+ " ......",
+ " .....",
+ " ....",
+ " ...",
+ " ..",
+ " ."};
+
+/* XPM */
+ static const char * const arrowleft_xpm[] = {
+ "8 15 2 1",
+ " c None",
+ ". c #000000",
+ " .",
+ " ..",
+ " ...",
+ " ....",
+ " .....",
+ " ......",
+ " .......",
+ "........",
+ " .......",
+ " ......",
+ " .....",
+ " ....",
+ " ...",
+ " ..",
+ " ."};
+
+
+
+/* XPM */
+static const char *const horlines_xpm[] = {
+ "2 2 2 1",
+ " c None",
+ ". c #000000",
+ " ",
+ ".."};
+
+/* XPM */
+static const char *const vertlines_xpm[] = {
+ "2 2 2 1",
+ " c None",
+ ". c #000000",
+ ". ",
+ ". "};
+
+/* XPM */
+static const char *const radiochecked_xpm[] = {
+ "18 18 2 1",
+ " c None",
+ ". c #000000",
+ " ...... ",
+ " .......... ",
+ " .............. ",
+ " .............. ",
+ " ................ ",
+ " ................ ",
+ "..................",
+ "..................",
+ "..................",
+ "..................",
+ "..................",
+ "..................",
+ " ................ ",
+ " ................ ",
+ " .............. ",
+ " .............. ",
+ " .......... ",
+ " ...... "};
+
+/* XPM */
+static const char * const radiochecked_low_xpm[] = {
+ "9 9 2 1",
+ " c None",
+ ". c #000000",
+ " ... ",
+ " ....... ",
+ " ....... ",
+ ".........",
+ ".........",
+ ".........",
+ " ....... ",
+ " ....... ",
+ " ... "};
+
+static const char *const arrowdown_xpm[] = {
+ "15 8 2 1",
+ " c None",
+ ". c #000000",
+ "...............",
+ " ............. ",
+ " ........... ",
+ " ......... ",
+ " ....... ",
+ " ..... ",
+ " ... ",
+ " . "};
+
+
+static const char *const arrowdown_big_xpm[] = {
+ "17 9 2 1",
+ " c None",
+ ". c #000000",
+ ".................",
+ " ............... ",
+ " ............. ",
+ " ........... ",
+ " ......... ",
+ " ....... ",
+ " ..... ",
+ " ... ",
+ " . "};
+
+
+/* XPM */
+static const char *const checkedlight_xpm[] = {
+ "24 24 2 1",
+ " c None",
+ ". c #000000",
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " . ",
+ " .. ",
+ " ... ",
+ " .... ",
+ " ..... ",
+ " ...... ",
+ " . ...... ",
+ " .. ...... ",
+ " ... ...... ",
+ " .... ...... ",
+ " .......... ",
+ " ......... ",
+ " ....... ",
+ " ..... ",
+ " ... ",
+ " . ",
+ " ",
+ " ",
+ " "};
+
+
+/* XPM */
+static const char *const checkedbold_xpm[] = {
+ "26 26 2 1",
+ " c None",
+ ". c #000000",
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " .. ",
+ " ... ",
+ " .... ",
+ " ..... ",
+ " .. ...... ",
+ " ... ....... ",
+ " .... ....... ",
+ " ..... ....... ",
+ " ...... ....... ",
+ " .............. ",
+ " ............ ",
+ " .......... ",
+ " ........ ",
+ " ...... ",
+ " .... ",
+ " .. ",
+ " ",
+ " ",
+ " ",
+ " "};
+
+/* XPM */
+static const char * const checkedbold_low_xpm[] = {
+ "9 8 2 1",
+ " c None",
+ ". c #000000",
+ " .",
+ " ..",
+ ". ...",
+ ".. ... ",
+ "... ... ",
+ " ..... ",
+ " ... ",
+ " . "};
+
+/* XPM */
+static const char * const checkedlight_low_xpm[] = {
+ "8 8 2 1",
+ " c None",
+ ". c #000000",
+ " .",
+ " ..",
+ " ...",
+ ". ... ",
+ ".. ... ",
+ "..... ",
+ " ... ",
+ " . "};
+
+/* XPM */
+static const char * const highlightedradiobutton_xpm[] = {
+ "30 30 3 1",
+ " c None",
+ ". c #000000",
+ "+ c #0078CC",
+ " ........ ",
+ " .............. ",
+ " ....++++++++.... ",
+ " ....++++++++++++.... ",
+ " ...++++ ++++... ",
+ " ...+++ +++... ",
+ " ..++ ++.. ",
+ " ..++ ++.. ",
+ " ...++ ++... ",
+ " ..++ ++.. ",
+ " ..++ ++.. ",
+ "..++ ++..",
+ "..++ ++..",
+ "..++ ++..",
+ "..++ ++..",
+ "..++ ++..",
+ "..++ ++..",
+ "..++ ++..",
+ "..++ ++..",
+ " ..++ ++.. ",
+ " ..++ ++.. ",
+ " ...++ ++... ",
+ " ..++ ++.. ",
+ " ..++ ++.. ",
+ " ...+++ +++... ",
+ " ...++++ ++++... ",
+ " ....++++++++++++.... ",
+ " ....++++++++.... ",
+ " .............. ",
+ " ........ "};
+
+/* XPM */
+static const char * const highlightedradiobutton_low_xpm[] = {
+ "15 15 3 1",
+ " c None",
+ ". c #000000",
+ "+ c #3192D6",
+ " ..... ",
+ " ..+++++.. ",
+ " .++ ++. ",
+ " .+ +. ",
+ " .+ +. ",
+ ".+ +.",
+ ".+ +.",
+ ".+ +.",
+ ".+ +.",
+ ".+ +.",
+ " .+ +. ",
+ " .+ +. ",
+ " .++ ++. ",
+ " ..+++++.. ",
+ " ..... "};
+
+/* XPM */
+static const char * const cross_big_xpm[] = {
+"28 28 4 1",
+" c #09454A",
+". c #218C98",
+"+ c #47D8E5",
+"@ c #FDFFFC",
+" ",
+" ",
+" ++++++++++++++++++++++++ ",
+" ++++++++++++++++++++++++ ",
+" ++....................++ ",
+" ++....................++ ",
+" ++..@@@..........@@@..++ ",
+" ++..@@@@........@@@@..++ ",
+" ++..@@@@@......@@@@@..++ ",
+" ++...@@@@@....@@@@@...++ ",
+" ++....@@@@@..@@@@@....++ ",
+" ++.....@@@@@@@@@@.....++ ",
+" ++......@@@@@@@@......++ ",
+" ++.......@@@@@@.......++ ",
+" ++.......@@@@@@.......++ ",
+" ++......@@@@@@@@......++ ",
+" ++.....@@@@@@@@@@.....++ ",
+" ++....@@@@@..@@@@@....++ ",
+" ++...@@@@@....@@@@@...++ ",
+" ++..@@@@@......@@@@@..++ ",
+" ++..@@@@........@@@@..++ ",
+" ++..@@@..........@@@..++ ",
+" ++....................++ ",
+" ++....................++ ",
+" ++++++++++++++++++++++++ ",
+" ++++++++++++++++++++++++ ",
+" ",
+" "};
+
+/* XPM */
+static const char * const cross_small_xpm[] = {
+"14 14 4 1",
+" c #09454A",
+". c #218C98",
+"+ c #47D8E5",
+"@ c #FCFFFC",
+" ",
+" ++++++++++++ ",
+" +..........+ ",
+" +.@@....@@.+ ",
+" +.@@@..@@@.+ ",
+" +..@@@@@@..+ ",
+" +...@@@@...+ ",
+" +...@@@@...+ ",
+" +..@@@@@@..+ ",
+" +.@@@..@@@.+ ",
+" +.@@....@@.+ ",
+" +..........+ ",
+" ++++++++++++ ",
+" "};
+
+/* XPM */
+static const char * const max_big_xpm[] = {
+"28 28 4 1",
+" c #09454A",
+". c #218C98",
+"+ c #47D8E5",
+"@ c #FDFFFC",
+" ",
+" ",
+" ++++++++++++++++++++++++ ",
+" ++++++++++++++++++++++++ ",
+" ++....................++ ",
+" ++....................++ ",
+" ++....................++ ",
+" ++....................++ ",
+" ++..@@@@@@@@@@@@@@@@..++ ",
+" ++..@@@@@@@@@@@@@@@@..++ ",
+" ++..@@@@@@@@@@@@@@@@..++ ",
+" ++..@@@@@@@@@@@@@@@@..++ ",
+" ++..@@............@@..++ ",
+" ++..@@............@@..++ ",
+" ++..@@............@@..++ ",
+" ++..@@............@@..++ ",
+" ++..@@............@@..++ ",
+" ++..@@............@@..++ ",
+" ++..@@@@@@@@@@@@@@@@..++ ",
+" ++..@@@@@@@@@@@@@@@@..++ ",
+" ++....................++ ",
+" ++....................++ ",
+" ++....................++ ",
+" ++....................++ ",
+" ++++++++++++++++++++++++ ",
+" ++++++++++++++++++++++++ ",
+" ",
+" "};
+
+/* XPM */
+static const char * const max_small_xpm[] = {
+"14 14 4 1",
+" c #09454A",
+". c #218C98",
+"+ c #47D8E5",
+"@ c #FCFFFC",
+" ",
+" ++++++++++++ ",
+" +..........+ ",
+" +..........+ ",
+" +.@@@@@@@@.+ ",
+" +.@@@@@@@@.+ ",
+" +.@......@.+ ",
+" +.@......@.+ ",
+" +.@......@.+ ",
+" +.@@@@@@@@.+ ",
+" +..........+ ",
+" +..........+ ",
+" ++++++++++++ ",
+" "};
+
+/* XPM */
+static const char * const normal_big_xpm[] = {
+"28 28 4 1",
+" c #09454A",
+". c #218C98",
+"+ c #47D8E5",
+"@ c #FDFFFC",
+" ",
+" ",
+" ++++++++++++++++++++++++ ",
+" ++++++++++++++++++++++++ ",
+" ++....................++ ",
+" ++....................++ ",
+" ++..@@@@@@@@@@@@@@@@..++ ",
+" ++..@@@@@@@@@@@@@@@@..++ ",
+" ++..@@............@@..++ ",
+" ++..@@............@@..++ ",
+" ++..@@............@@..++ ",
+" ++..@@............@@..++ ",
+" ++..@@............@@..++ ",
+" ++..@@............@@..++ ",
+" ++..@@............@@..++ ",
+" ++..@@............@@..++ ",
+" ++..@@............@@..++ ",
+" ++..@@............@@..++ ",
+" ++..@@............@@..++ ",
+" ++..@@............@@..++ ",
+" ++..@@@@@@@@@@@@@@@@..++ ",
+" ++..@@@@@@@@@@@@@@@@..++ ",
+" ++....................++ ",
+" ++....................++ ",
+" ++++++++++++++++++++++++ ",
+" ++++++++++++++++++++++++ ",
+" ",
+" "};
+
+/* XPM */
+static const char * const normal_small_xpm[] = {
+"14 14 4 1",
+" c #09454A",
+". c #218C98",
+"+ c #47D8E5",
+"@ c #FCFFFC",
+" ",
+" ++++++++++++ ",
+" +..........+ ",
+" +.@@@@@@@@.+ ",
+" +.@......@.+ ",
+" +.@......@.+ ",
+" +.@......@.+ ",
+" +.@......@.+ ",
+" +.@......@.+ ",
+" +.@......@.+ ",
+" +.@@@@@@@@.+ ",
+" +..........+ ",
+" ++++++++++++ ",
+" "};
+
+
+/* XPM */
+static const char * const min_big_xpm[] = {
+"28 28 4 1",
+" c #09454A",
+". c #218C98",
+"+ c #47D8E5",
+"@ c #FDFFFC",
+" ",
+" ",
+" ++++++++++++++++++++++++ ",
+" ++++++++++++++++++++++++ ",
+" ++....................++ ",
+" ++....................++ ",
+" ++....................++ ",
+" ++....................++ ",
+" ++....................++ ",
+" ++....................++ ",
+" ++....................++ ",
+" ++....................++ ",
+" ++....................++ ",
+" ++....................++ ",
+" ++....................++ ",
+" ++....................++ ",
+" ++....................++ ",
+" ++....................++ ",
+" ++..@@@@@@@@@@@@@@@@..++ ",
+" ++..@@@@@@@@@@@@@@@@..++ ",
+" ++....................++ ",
+" ++....................++ ",
+" ++....................++ ",
+" ++....................++ ",
+" ++++++++++++++++++++++++ ",
+" ++++++++++++++++++++++++ ",
+" ",
+" "};
+
+/* XPM */
+static const char * const min_small_xpm[] = {
+"14 14 4 1",
+" c #09454A",
+". c #218C98",
+"+ c #47D8E5",
+"@ c #FCFFFC",
+" ",
+" ++++++++++++ ",
+" +..........+ ",
+" +..........+ ",
+" +..........+ ",
+" +..........+ ",
+" +..........+ ",
+" +..........+ ",
+" +..........+ ",
+" +.@@@@@@@@.+ ",
+" +..........+ ",
+" +..........+ ",
+" ++++++++++++ ",
+" "};
+
+
+
+enum QSliderDirection { SliderUp, SliderDown, SliderLeft, SliderRight };
+
+QWindowsMobileStyle::QWindowsMobileStyle(QWindowsMobileStylePrivate &dd) : QWindowsStyle(dd) {
+ qApp->setEffectEnabled(Qt::UI_FadeMenu, false);
+ qApp->setEffectEnabled(Qt::UI_AnimateMenu, false);
+}
+
+QWindowsMobileStyle::QWindowsMobileStyle() : QWindowsStyle(*new QWindowsMobileStylePrivate) {
+ qApp->setEffectEnabled(Qt::UI_FadeMenu, false);
+ qApp->setEffectEnabled(Qt::UI_AnimateMenu, false);
+}
+
+QWindowsMobileStylePrivate::QWindowsMobileStylePrivate() :QWindowsStylePrivate() {
+
+#ifdef Q_OS_WINCE
+ doubleControls = qt_wince_is_high_dpi();
+ smartphone = qt_wince_is_smartphone();
+#else
+ doubleControls = false;
+ smartphone = false;
+#endif //Q_OS_WINCE
+
+#ifndef QT_NO_IMAGEFORMAT_XPM
+
+ imageArrowDown = QImage(arrowdown_xpm);
+ imageArrowUp = QImage(arrowdown_xpm).mirrored();
+ imageArrowLeft = QImage(arrowleft_xpm);
+ imageArrowRight = QImage(arrowleft_xpm).mirrored(true, false);
+ if (doubleControls) {
+ imageRadioButton = QImage(radiobutton_xpm);
+ imageRadioButtonChecked = QImage(radiochecked_xpm);
+ imageChecked = QImage(checkedlight_xpm);
+ imageCheckedBold = QImage(checkedbold_xpm);
+ imageRadioButtonHighlighted = QImage(highlightedradiobutton_xpm);
+ imageClose = QImage(cross_big_xpm);
+ imageMaximize = QImage(max_big_xpm);
+ imageMinimize = QImage(min_big_xpm);
+ imageNormalize = QImage(normal_big_xpm);
+ } else {
+ imageRadioButton = QImage(radiobutton_low_xpm);
+ imageRadioButtonChecked = QImage(radiochecked_low_xpm);
+ imageChecked = QImage(checkedlight_low_xpm);
+ imageCheckedBold = QImage(checkedbold_low_xpm);
+ imageRadioButtonHighlighted = QImage(highlightedradiobutton_low_xpm);
+ imageClose = QImage(cross_small_xpm);
+ imageMaximize = QImage(max_small_xpm);
+ imageMinimize = QImage(min_small_xpm);
+ imageNormalize = QImage(normal_small_xpm);
+ }
+
+ imageArrowDownBig = QImage(arrowdown_big_xpm);
+ imageArrowUpBig = QImage(arrowdown_big_xpm).mirrored();
+ imageArrowLeftBig = QImage(arrowleft_big_xpm);
+ imageArrowRightBig = QImage(arrowleft_big_xpm).mirrored(true, false);
+
+#endif
+}
+
+void QWindowsMobileStyle::drawPrimitive(PrimitiveElement element, const QStyleOption *option,
+ QPainter *painter, const QWidget *widget) const {
+
+ QWindowsMobileStylePrivate *d = const_cast<QWindowsMobileStylePrivate*>(d_func());
+
+ bool doRestore = false;
+ QRect rect = option->rect;
+ painter->setClipping(false);
+
+ switch (element) {
+ case PE_PanelButtonTool: {
+ int penSize = 1;
+ if (d->doubleControls)
+ penSize = 2;
+ if (widget)
+ if (QWidget *parent = widget->parentWidget())
+#ifndef QT_NO_TABWIDGET
+ if (qobject_cast<QTabWidget *>(parent->parentWidget())) {
+#else
+ if (false) {
+#endif //QT_NO_TABBAR
+ rect.adjust(0,2*penSize,0,-1*penSize);
+ qDrawPlainRect(painter, rect, option->palette.shadow().color(), penSize, &option->palette.light());
+ if (option->state & (State_Sunken))
+ qDrawPlainRect(painter, rect, option->palette.shadow().color(), penSize, &option->palette.shadow());
+ }
+ else {
+ if (!(option->state & State_AutoRaise) || (option->state & (State_Sunken | State_On)))
+ qDrawPlainRect(painter,option->rect.adjusted(0, penSize, 0, -1 * penSize) ,
+ option->palette.button().color(), 0, &option->palette.button());
+ if (option->state & (State_Sunken)) {
+ qDrawPlainRect(painter, rect, option->palette.shadow().color(), penSize, &option->palette.light());
+ }
+ if (option->state & (State_On)){
+ QBrush fill = QBrush(option->palette.light().color());
+ painter->fillRect(rect.adjusted(windowsItemFrame , windowsItemFrame ,
+ -windowsItemFrame , -windowsItemFrame ), fill);
+ qDrawPlainRect(painter, rect, option->palette.shadow().color(), penSize, &option->palette.light());
+ }
+ }
+ break; }
+ case PE_IndicatorButtonDropDown:
+ if (d->doubleControls)
+ qDrawPlainRect(painter, option->rect, option->palette.shadow().color(), 2, &option->palette.button());
+ else
+ qDrawPlainRect(painter, option->rect, option->palette.shadow().color(), 1, &option->palette.button());
+ break;
+#ifndef QT_NO_TABBAR
+ case PE_IndicatorTabTear:
+ if (const QStyleOptionTab *tab = qstyleoption_cast<const QStyleOptionTab *>(option)) {
+ bool rtl = tab->direction == Qt::RightToLeft;
+ QRect rect = tab->rect;
+ QPainterPath path;
+ rect.setTop(rect.top() + ((tab->state & State_Selected) ? 1 : 3));
+ rect.setBottom(rect.bottom() - ((tab->state & State_Selected) ? 0 : 2));
+ path.moveTo(QPoint(rtl ? rect.right() : rect.left(), rect.top()));
+ int count = 3;
+ for(int jags = 1; jags <= count; ++jags, rtl = !rtl)
+ path.lineTo(QPoint(rtl ? rect.left() : rect.right(), rect.top() + jags * rect.height()/count));
+ painter->setPen(QPen(tab->palette.light(), qreal(.8)));
+ painter->setBrush(tab->palette.background());
+ painter->setRenderHint(QPainter::Antialiasing);
+ painter->drawPath(path);
+ }
+ break;
+#endif //QT_NO_TABBAR
+
+#ifndef QT_NO_TOOLBAR
+ case PE_IndicatorToolBarSeparator: {
+ painter->save();
+ QPoint p1, p2;
+ if (option->state & State_Horizontal) {
+ p1 = QPoint(option->rect.width()/2, 0);
+ p2 = QPoint(p1.x(), option->rect.height());
+ } else {
+ p1 = QPoint(0, option->rect.height()/2);
+ p2 = QPoint(option->rect.width(), p1.y());
+ }
+
+
+ painter->setPen(option->palette.mid().color());
+ if (d->doubleControls) {
+ QPen pen = painter->pen();
+ pen.setWidth(2);
+ pen.setCapStyle(Qt::FlatCap);
+ painter->setPen(pen);
+ }
+ painter->drawLine(p1, p2);
+ painter->restore();
+ break; }
+#endif // QT_NO_TOOLBAR
+ case PE_IndicatorToolBarHandle:
+ painter->save();
+ painter->translate(option->rect.x(), option->rect.y());
+ if (option->state & State_Horizontal) {
+ int x = option->rect.width() / 2 - 4;
+ if (QApplication::layoutDirection() == Qt::RightToLeft)
+ x -= 2;
+ if (option->rect.height() > 4) {
+ qDrawWinButton(painter,x-1,0,7,option->rect.height(), option->palette, false, 0);
+
+ qDrawShadePanel(painter, x, 1, 3, option->rect.height() - 1,
+ option->palette, false, 0);
+ qDrawShadePanel(painter, x + 3, 1, 3, option->rect.height() - 1,
+ option->palette, false, 0);
+ painter->setPen(option->palette.button().color());
+ }
+ } else {
+ if (option->rect.width() > 4) {
+ int y = option->rect.height() / 2 - 4;
+ qDrawShadePanel(painter, 2, y, option->rect.width() - 2, 3,
+ option->palette, false, 0);
+ qDrawShadePanel(painter, 2, y + 3, option->rect.width() - 2, 3,
+ option->palette, false, 0);
+ }
+ }
+ painter->restore();
+ break;
+
+#ifndef QT_NO_PROGRESSBAR
+ case PE_IndicatorProgressChunk: {
+ bool vertical = false;
+ if (const QStyleOptionProgressBarV2 *pb2 = qstyleoption_cast<const QStyleOptionProgressBarV2 *>(option))
+ vertical = (pb2->orientation == Qt::Vertical);
+ if (!vertical) {
+ painter->fillRect(option->rect.x(), option->rect.y()+2, option->rect.width(), option->rect.height()-4,
+ option->palette.brush(QPalette::Highlight));
+ } else {
+ painter->fillRect(option->rect.x()+2, option->rect.y(), option->rect.width()-4, option->rect.height(),
+ option->palette.brush(QPalette::Highlight));
+ }
+ }
+ break;
+#endif // QT_NO_PROGRESSBAR
+
+ case PE_FrameButtonTool: {
+#ifndef QT_NO_DOCKWIDGET
+ if (widget && widget->inherits("QDockWidgetTitleButton")) {
+ if (const QDockWidget *dw = qobject_cast<const QDockWidget *>(widget->parent()))
+ if (dw->isFloating()){
+ qDrawPlainRect(painter,option->rect.adjusted(1, 1, 0, 0),
+ option->palette.shadow().color(),1,&option->palette.button());
+ return;
+ }
+ }
+#endif // QT_NO_DOCKWIDGET
+ QBrush fill;
+ bool stippled;
+ bool panel = (element == PE_PanelButtonTool);
+ if ((!(option->state & State_Sunken ))
+ && (!(option->state & State_Enabled)
+ || ((option->state & State_Enabled ) && !(option->state & State_MouseOver)))
+ && (option->state & State_On)) {
+ fill = QBrush(option->palette.light().color(), Qt::Dense4Pattern);
+ stippled = true;
+ } else {
+ fill = option->palette.brush(QPalette::Button);
+ stippled = false;
+ }
+ if (option->state & (State_Raised | State_Sunken | State_On)) {
+ if (option->state & State_AutoRaise) {
+ if(option->state & (State_Enabled | State_Sunken | State_On)){
+ if (panel)
+ qDrawPlainRect(painter, option->rect,option->palette.shadow().color(),d->doubleControls, &fill);
+ else
+ qDrawPlainRect(painter, option->rect,option->palette.shadow().color(),d->doubleControls, &fill);
+ }
+ if (stippled) {
+ painter->setPen(option->palette.button().color());
+ painter->drawRect(option->rect.adjusted(1, 1, -2, -2));
+ }
+ } else {
+ qDrawPlainRect(painter, option->rect,option->palette.shadow().color(),d->doubleControls, &fill);
+ }
+ } else {
+ painter->fillRect(option->rect, fill);
+ }
+ break; }
+
+ case PE_FrameFocusRect:
+ if (const QStyleOptionFocusRect *fropt = qstyleoption_cast<const QStyleOptionFocusRect *>(option)) {
+ //### check for d->alt_down
+ int penSize;
+ d->doubleControls ? penSize = 2 : penSize = 1;
+ bool alternateFocusStyle = false;
+ if (!widget)
+ alternateFocusStyle = true;
+#ifndef QT_NO_COMBOBOX
+ if (qobject_cast<const QComboBox*>(widget))
+ alternateFocusStyle = true;
+#endif
+ if (!(fropt->state & State_KeyboardFocusChange) && !styleHint(SH_UnderlineShortcut, option))
+ return;
+ QRect r = option->rect;
+ painter->save();
+ painter->setBackgroundMode(Qt::TransparentMode);
+ if (alternateFocusStyle) {
+ QColor bg_col = fropt->backgroundColor;
+ if (!bg_col.isValid())
+ bg_col = painter->background().color();
+ // Create an "XOR" color.
+ QColor patternCol((bg_col.red() ^ 0xff) & 0xff,
+ (bg_col.green() ^ 0xff) & 0xff,
+ (bg_col.blue() ^ 0xff) & 0xff);
+ painter->setBrush(QBrush(patternCol, Qt::Dense4Pattern));
+ painter->setBrushOrigin(r.topLeft());
+ }
+ else {
+ painter->setPen(option->palette.highlight().color());
+ painter->setBrush(option->palette.highlight());
+ }
+ painter->setPen(Qt::NoPen);
+ painter->setBrushOrigin(r.topLeft());
+ painter->drawRect(r.left(), r.top(), r.width(), penSize); // Top
+ painter->drawRect(r.left(), r.bottom(), r.width() + penSize - 1, penSize); // Bottom
+ painter->drawRect(r.left(), r.top(), penSize, r.height()); // Left
+ painter->drawRect(r.right(), r.top(), penSize, r.height()); // Right
+ painter->restore();
+ }
+ break;
+
+ case PE_PanelButtonBevel: {
+ QBrush fill;
+ bool panel = element != PE_FrameButtonBevel;
+ painter->setBrushOrigin(option->rect.topLeft());
+ if (!(option->state & State_Sunken) && (option->state & State_On))
+ fill = QBrush(option->palette.light().color(), Qt::Dense4Pattern);
+ else
+ fill = option->palette.brush(QPalette::Button);
+
+ if (option->state & (State_Raised | State_On | State_Sunken)) {
+ if (d->doubleControls)
+ qDrawPlainRect(painter, option->rect,option->palette.shadow().color(),2,&fill);
+ else
+ qDrawPlainRect(painter, option->rect,option->palette.shadow().color(),1,&fill);
+ } else {
+ if (panel)
+ painter->fillRect(option->rect, fill);
+ else
+ painter->drawRect(option->rect);
+ }
+ break; }
+
+ case PE_FrameGroupBox:
+ if (const QStyleOptionFrame *frame = qstyleoption_cast<const QStyleOptionFrame *>(option)) {
+
+ const QStyleOptionFrameV2 *frame2 = qstyleoption_cast<const QStyleOptionFrameV2 *>(option);
+ if (frame2 && !(frame2->features & QStyleOptionFrameV2::Flat)) {
+ QPen oldPen = painter->pen();
+ QRect r = frame->rect;
+ painter->setPen(frame->palette.shadow().color());
+ painter->fillRect(r.x(), r.y(), r.x() + r.width()-1,
+ r.y() + r.height() - windowsMobileFrameGroupBoxOffset,
+ frame->palette.light());
+ painter ->drawLine(r.topLeft() + QPoint(-2, 1), r.topRight()+ QPoint(0, 1));
+ if (d->doubleControls)
+ painter ->drawLine(r.topLeft() + QPoint(-2, 2), r.topRight()+ QPoint(0, 2));
+ painter->setPen(oldPen);
+ }
+ }
+ break;
+
+ case PE_IndicatorCheckBox: {
+ QBrush fill;
+ QRect r = d->doubleControls ? option->rect.adjusted(0,1,0,-1) : option->rect;
+ if (option->state & State_NoChange)
+ fill = QBrush(option->palette.shadow().color(), Qt::Dense4Pattern);
+ else if (option->state & State_Sunken)
+ fill = option->palette.button();
+ else if (option->state & State_Enabled)
+ fill = option->palette.base();
+ else
+ fill = option->palette.background();
+ painter->save();
+ doRestore = true;
+ if (d->doubleControls && (option->state & State_NoChange))
+ painter->fillRect(r, fill);
+ else
+ painter->fillRect(option->rect, fill);
+ painter->setPen(option->palette.shadow().color());
+ painter->drawLine(r.topLeft(), r.topRight());
+ painter->drawLine(r.topRight(), r.bottomRight());
+ painter->drawLine(r.bottomLeft(), r.bottomRight());
+ painter->drawLine(r.bottomLeft(), r.topLeft());
+ if (d->doubleControls) {
+ QRect r0 = r.adjusted(1, 1, -1, -1);
+ painter->drawLine(r0.topLeft(), r0.topRight());
+ painter->drawLine(r0.topRight(), r0.bottomRight());
+ painter->drawLine(r0.bottomLeft(), r0.bottomRight());
+ painter->drawLine(r0.bottomLeft(), r0.topLeft());
+ }
+ if (option->state & State_HasFocus) {
+ painter->setPen(option->palette.highlight().color());
+ QRect r2 = d->doubleControls ? r.adjusted(2, 2, -2, -2) : r.adjusted(1, 1, -1, -1);
+ painter->drawLine(r2.topLeft(), r2.topRight());
+ painter->drawLine(r2.topRight(), r2.bottomRight());
+ painter->drawLine(r2.bottomLeft(), r2.bottomRight());
+ painter->drawLine(r2.bottomLeft(), r2.topLeft());
+ if (d->doubleControls) {
+ QRect r3 = r2.adjusted(1, 1, -1, -1);
+ painter->drawLine(r3.topLeft(), r3.topRight());
+ painter->drawLine(r3.topRight(), r3.bottomRight());
+ painter->drawLine(r3.bottomLeft(), r3.bottomRight());
+ painter->drawLine(r3.bottomLeft(), r3.topLeft());
+ }
+ painter->setPen(option->palette.shadow().color());
+ }
+ //fall through...
+ }
+ case PE_IndicatorViewItemCheck:
+ case PE_Q3CheckListIndicator: {
+ if (!doRestore) {
+ painter->save();
+ doRestore = true;
+ }
+ if (element == PE_Q3CheckListIndicator || element == PE_IndicatorViewItemCheck) {
+ painter->setPen(option->palette.shadow().color());
+ if (option->state & State_NoChange)
+ painter->setBrush(option->palette.brush(QPalette::Button));
+ if (d->doubleControls) {
+ QRect r = QRect(option->rect.x(), option->rect.y(), windowsMobileitemViewCheckBoxSize * 2, windowsMobileitemViewCheckBoxSize * 2);
+ qDrawPlainRect(painter, r, option->palette.shadow().color(), 2);
+ } else {
+ QRect r = QRect(option->rect.x(), option->rect.y(), windowsMobileitemViewCheckBoxSize, windowsMobileitemViewCheckBoxSize);
+ qDrawPlainRect(painter, r, option->palette.shadow().color(), 1);
+ }
+ if (option->state & State_Enabled)
+ d->imageChecked.setColor(1, option->palette.shadow().color().rgba());
+ else
+ d->imageChecked.setColor(1, option->palette.dark().color().rgba());
+ if (!(option->state & State_Off)) {
+ if (d->doubleControls)
+ painter->drawImage(option->rect.x(), option->rect.y(), d->imageChecked);
+ else
+ painter->drawImage(option->rect.x() + 3, option->rect.y() + 3, d->imageChecked);
+ }
+ }
+ else {
+ if (option->state & State_NoChange)
+ d->imageCheckedBold.setColor(1, option->palette.dark().color().rgba());
+ else if (option->state & State_Enabled)
+ d->imageCheckedBold.setColor(1, option->palette.shadow().color().rgba());
+ else
+ d->imageCheckedBold.setColor(1, option->palette.dark().color().rgba());
+ if (!(option->state & State_Off)) {
+ if (d->doubleControls)
+ painter->drawImage(option->rect.x() + 2, option->rect.y(), d->imageCheckedBold);
+ else
+ painter->drawImage(option->rect.x() + 3, option->rect.y() + 3, d->imageCheckedBold);
+ }
+ }
+ if (doRestore)
+ painter->restore();
+ break; }
+ case PE_IndicatorRadioButton: {
+ painter->save();
+
+ if (option->state & State_HasFocus) {
+ d->imageRadioButtonHighlighted.setColor(1, option->palette.shadow().color().rgba());
+ d->imageRadioButtonHighlighted.setColor(2, option->palette.highlight().color().rgba());
+ painter->drawImage(option->rect.x(), option->rect.y(), d->imageRadioButtonHighlighted);
+ }
+ else {
+ d->imageRadioButton.setColor(1, option->palette.shadow().color().rgba());
+ painter->drawImage(option->rect.x(), option->rect.y(), d->imageRadioButton);
+ }
+ if (option->state & (State_Sunken | State_On)) {
+ if (option->state & State_Enabled)
+ d->imageRadioButtonChecked.setColor(1, option->palette.shadow().color().rgba());
+ else
+ d->imageRadioButtonChecked.setColor(1, option->palette.dark().color().rgba());
+
+ static const int offset = d->doubleControls ? 6 : 3;
+ painter->drawImage(option->rect.x() + offset, option->rect.y() + offset, d->imageRadioButtonChecked);
+ }
+ painter->restore();
+ break; }
+ case PE_PanelButtonCommand:
+ if (const QStyleOptionButton *button = qstyleoption_cast<const QStyleOptionButton *>(option)) {
+ QBrush fill;
+ State flags = option->state;
+ QPalette pal = option->palette;
+ QRect r = option->rect;
+ if ((flags & State_Sunken || flags & State_On) )
+ fill = pal.brush(QPalette::Shadow);
+ else
+ fill = pal.brush(QPalette::Button);
+ int singleLine = 1;
+ int doubleLine = 2;
+ if (d->doubleControls) {
+ singleLine = 2;
+ doubleLine = 4;
+ }
+ if (button->features & QStyleOptionButton::DefaultButton && flags & State_Sunken) {
+ if (d->doubleControls) {
+ qDrawPlainRect(painter, r, pal.shadow().color(), 1, &fill);
+ qDrawPlainRect(painter, r.adjusted(1, 1, -1, 1), pal.shadow().color(), 1, &fill);
+ }
+ else {
+ qDrawPlainRect(painter, r, pal.shadow().color(), 1, &fill);
+ }
+ } else if (flags & (State_Raised | State_Sunken | State_On | State_Sunken)) {
+ qDrawPlainRect(painter, r, pal.shadow().color(), singleLine, &fill);
+ } else {
+ painter->fillRect(r, fill);
+ }
+ }
+ break;
+ case PE_FrameDefaultButton: {
+ painter->save();
+ painter->setPen(option->palette.shadow().color());
+ QRect rect = option->rect;
+ if (d->doubleControls) {
+ rect.adjust(1, 1, -2, -2);
+ painter->drawRect(rect);
+ painter->drawRect(rect.adjusted(1, 1, -1, -1));
+ }
+
+ else {
+ rect.adjust(2, 2, -3, -3);
+ painter->drawRect(rect);
+ }
+ painter->restore();
+ break; }
+ case PE_IndicatorSpinPlus:
+ case PE_IndicatorSpinMinus: {
+ QRect r = option->rect;
+ int fw = pixelMetric(PM_DefaultFrameWidth, option, widget)+2;
+ QRect br = r.adjusted(fw, fw, -fw, -fw);
+ int offset = (option->state & State_Sunken) ? 1 : 0;
+ int step = (br.width() + 4) / 5;
+ painter->fillRect(br.x() + offset, br.y() + offset +br.height() / 2 - step / 2,
+ br.width(), step, option->palette.buttonText());
+ if (element == PE_IndicatorSpinPlus)
+ painter->fillRect(br.x() + br.width() / 2 - step / 2 + offset, br.y() + offset+4,
+ step, br.height() - 7, option->palette.buttonText());
+ break; }
+ case PE_IndicatorSpinUp:
+ case PE_IndicatorSpinDown: {
+ painter->save();
+ QPoint points[7];
+ switch (element) {
+ case PE_IndicatorSpinUp:
+ points[0] = QPoint(-2, -4);
+ points[1] = QPoint(-2, 2);
+ points[2] = QPoint(-1, -3);
+ points[3] = QPoint(-1, 1);
+ points[4] = QPoint(0, -2);
+ points[5] = QPoint(0, 0);
+ points[6] = QPoint(1, -1);
+ break;
+ case PE_IndicatorSpinDown:
+ points[0] = QPoint(0, -4);
+ points[1] = QPoint(0, 2);
+ points[2] = QPoint(-1, -3);
+ points[3] = QPoint(-1, 1);
+ points[4] = QPoint(-2, -2);
+ points[5] = QPoint(-2, 0);
+ points[6] = QPoint(-3, -1);
+ break;
+ default:
+ break;
+ }
+ if (option->state & State_Sunken)
+ painter->translate(pixelMetric(PM_ButtonShiftHorizontal),
+ pixelMetric(PM_ButtonShiftVertical));
+ if (option->state & State_Enabled) {
+ painter->translate(option->rect.x() + option->rect.width() / 2,
+ option->rect.y() + option->rect.height() / 2);
+ painter->setPen(option->palette.buttonText().color());
+ painter->drawLine(points[0], points[1]);
+ painter->drawLine(points[2], points[3]);
+ painter->drawLine(points[4], points[5]);
+ painter->drawPoint(points[6]);
+ } else {
+ painter->translate(option->rect.x() + option->rect.width() / 2 + 1,
+ option->rect.y() + option->rect.height() / 2 + 1);
+ painter->setPen(option->palette.light().color());
+ painter->drawLine(points[0], points[1]);
+ painter->drawLine(points[2], points[3]);
+ painter->drawLine(points[4], points[5]);
+ painter->drawPoint(points[6]);
+ painter->translate(-1, -1);
+ painter->setPen(option->palette.mid().color());
+ painter->drawLine(points[0], points[1]);
+ painter->drawLine(points[2], points[3]);
+ painter->drawLine(points[4], points[5]);
+ painter->drawPoint(points[6]);
+ }
+ painter->restore();
+ break; }
+
+ case PE_IndicatorArrowUpBig:
+ case PE_IndicatorArrowDownBig:
+ case PE_IndicatorArrowLeftBig:
+ case PE_IndicatorArrowRightBig:
+
+ case PE_IndicatorArrowUp:
+ case PE_IndicatorArrowDown:
+ case PE_IndicatorArrowRight:
+ case PE_IndicatorArrowLeft: {
+ painter->save();
+
+ if (d->doubleControls) {
+ QColor color;
+ if (option->state & State_Sunken)
+ color = option->palette.light().color();
+ else
+ color = option->palette.buttonText().color();
+ QImage image;
+ int xoffset, yoffset;
+ switch (element) {
+ case PE_IndicatorArrowUp:
+ image = d->imageArrowUp;
+ xoffset = 1;
+ yoffset = 12;
+ break;
+ case PE_IndicatorArrowDown:
+ image = d->imageArrowDown;
+ xoffset = 1;
+ yoffset =12;
+ break;
+ case PE_IndicatorArrowLeft:
+ image = d->imageArrowLeft;
+ xoffset = 8;
+ yoffset = 2;
+ break;
+ case PE_IndicatorArrowRight:
+ image = d->imageArrowRight;
+ xoffset = 8;
+ yoffset = 2;
+ break;
+ case PE_IndicatorArrowUpBig:
+ image = d->imageArrowUpBig;
+ xoffset = 3;
+ yoffset = 12;
+ break;
+ case PE_IndicatorArrowDownBig:
+ image = d->imageArrowDownBig;
+ xoffset = 2;
+ yoffset =12;
+ break;
+ case PE_IndicatorArrowLeftBig:
+ image = d->imageArrowLeftBig;
+ xoffset = 8;
+ yoffset = 2;
+ break;
+ case PE_IndicatorArrowRightBig:
+ image = d->imageArrowRightBig;
+ xoffset = 8;
+ yoffset = 2;
+ break;
+ default:
+ break;
+ }
+ image.setColor(1, color.rgba());
+ painter->drawImage(option->rect.x() + xoffset, option->rect.y() + yoffset, image);
+ }
+ else {
+ QPoint points[7];
+ switch (element) {
+ case PE_IndicatorArrowUp:
+ case PE_IndicatorArrowUpBig:
+ points[0] = QPoint(-3, 1);
+ points[1] = QPoint(3, 1);
+ points[2] = QPoint(-2, 0);
+ points[3] = QPoint(2, 0);
+ points[4] = QPoint(-1, -1);
+ points[5] = QPoint(1, -1);
+ points[6] = QPoint(0, -2);
+ break;
+ case PE_IndicatorArrowDown:
+ case PE_IndicatorArrowDownBig:
+ points[0] = QPoint(-3, -1);
+ points[1] = QPoint(3, -1);
+ points[2] = QPoint(-2, 0);
+ points[3] = QPoint(2, 0);
+ points[4] = QPoint(-1, 1);
+ points[5] = QPoint(1, 1);
+ points[6] = QPoint(0, 2);
+ break;
+ case PE_IndicatorArrowRight:
+ case PE_IndicatorArrowRightBig:
+ points[0] = QPoint(-2, -3);
+ points[1] = QPoint(-2, 3);
+ points[2] = QPoint(-1, -2);
+ points[3] = QPoint(-1, 2);
+ points[4] = QPoint(0, -1);
+ points[5] = QPoint(0, 1);
+ points[6] = QPoint(1, 0);
+ break;
+ case PE_IndicatorArrowLeft:
+ case PE_IndicatorArrowLeftBig:
+ points[0] = QPoint(0, -3);
+ points[1] = QPoint(0, 3);
+ points[2] = QPoint(-1, -2);
+ points[3] = QPoint(-1, 2);
+ points[4] = QPoint(-2, -1);
+ points[5] = QPoint(-2, 1);
+ points[6] = QPoint(-3, 0);
+ break;
+ default:
+ break;
+ }
+ if (option->state & State_Sunken)
+ painter->setPen(option->palette.light().color());
+ else
+ painter->setPen(option->palette.buttonText().color());
+ if (option->state & State_Enabled) {
+ painter->translate(option->rect.x() + option->rect.width() / 2,
+ option->rect.y() + option->rect.height() / 2 - 1);
+ painter->drawLine(points[0], points[1]);
+ painter->drawLine(points[2], points[3]);
+ painter->drawLine(points[4], points[5]);
+ painter->drawPoint(points[6]);
+ } else {
+ painter->translate(option->rect.x() + option->rect.width() / 2,
+ option->rect.y() + option->rect.height() / 2 - 1);
+ painter->setPen(option->palette.mid().color());
+ painter->drawLine(points[0], points[1]);
+ painter->drawLine(points[2], points[3]);
+ painter->drawLine(points[4], points[5]);
+ painter->drawPoint(points[6]);
+ }
+ }
+ painter->restore();
+ break; }
+#ifndef QT_NO_TABWIDGET
+ case PE_FrameTabWidget:
+ if (const QStyleOptionTabWidgetFrame *tab = qstyleoption_cast<const QStyleOptionTabWidgetFrame *>(option)) {
+ QRect rect = option->rect;
+ QPalette pal = option->palette;
+ painter->save();
+ QBrush fill = pal.light();
+ painter->fillRect(rect, fill);
+ painter->setPen(pal.shadow().color());
+ if (d->doubleControls) {
+ QPen pen = painter->pen();
+ pen.setWidth(2);
+ pen.setCapStyle(Qt::FlatCap);
+ painter->setPen(pen);
+ }
+ switch (tab->shape) {
+ case QTabBar::RoundedNorth:
+ if (d->doubleControls)
+ painter->drawLine(rect.topLeft() + QPoint(0, 1), rect.topRight() + QPoint(0, 1));
+ else
+ painter->drawLine(rect.topLeft(), rect.topRight());
+ break;
+ case QTabBar::RoundedSouth:
+ if (d->doubleControls)
+ painter->drawLine(rect.bottomLeft(), rect.bottomRight());
+ else
+ painter->drawLine(rect.bottomLeft(), rect.bottomRight());
+ break;
+ case QTabBar::RoundedEast:
+ painter->drawLine(rect.topRight(), rect.bottomRight());
+ break;
+ case QTabBar::RoundedWest:
+ painter->drawLine(rect.topLeft(), rect.bottomLeft());
+ break;
+ case QTabBar::TriangularWest:
+ case QTabBar::TriangularEast:
+ case QTabBar::TriangularSouth:
+ case QTabBar::TriangularNorth:
+ if (d->doubleControls)
+ qDrawPlainRect(painter, rect.adjusted(0,-2,0,0), option->palette.shadow().color(),2,&pal.light());
+ else
+ qDrawPlainRect(painter, rect, option->palette.shadow().color(),1,&pal.light());
+ break;
+ default:
+ break;
+ }
+ painter->restore();
+ }
+ break;
+#endif //QT_NO_TABBAR
+
+ case PE_FrameWindow: {
+ QPalette popupPal = option->palette;
+ popupPal.setColor(QPalette::Light, option->palette.background().color());
+ popupPal.setColor(QPalette::Midlight, option->palette.light().color());
+ if (d->doubleControls)
+ qDrawPlainRect(painter, option->rect, popupPal.shadow().color(),2,0);
+ else
+ qDrawPlainRect(painter, option->rect, popupPal.shadow().color(),1,0);
+ break; }
+ case PE_FrameTabBarBase: {
+ break; }
+ case PE_Widget:
+ break;
+ case PE_IndicatorMenuCheckMark: {
+ int markW = option->rect.width() > 7 ? 7 : option->rect.width();
+ int markH = markW;
+ if (d->doubleControls)
+ markW*=2;
+ markH*=2;
+ int posX = option->rect.x() + (option->rect.width() - markW)/2 + 1;
+ int posY = option->rect.y() + (option->rect.height() - markH)/2;
+
+ QVector<QLineF> a;
+ a.reserve(markH);
+
+ int i, xx, yy;
+ xx = posX;
+ yy = 3 + posY;
+ for (i = 0; i < markW/2; ++i) {
+ a << QLineF(xx, yy, xx, yy + 2);
+ ++xx;
+ ++yy;
+ }
+ yy -= 2;
+ for (; i < markH; ++i) {
+ a << QLineF(xx, yy, xx, yy + 2);
+ ++xx;
+ --yy;
+ }
+ if (!(option->state & State_Enabled) && !(option->state & State_On)) {
+ int pnt;
+ painter->setPen(option->palette.highlightedText().color());
+ QPoint offset(1, 1);
+ for (pnt = 0; pnt < a.size(); ++pnt)
+ a[pnt].translate(offset.x(), offset.y());
+ painter->drawLines(a);
+ for (pnt = 0; pnt < a.size(); ++pnt)
+ a[pnt].translate(offset.x(), offset.y());
+ }
+ painter->setPen(option->palette.text().color());
+ painter->drawLines(a);
+ break; }
+ case PE_Frame:
+ if (d->doubleControls)
+ qDrawPlainRect(painter, option->rect, option->palette.shadow().color(),2,&option->palette.light());
+ else
+ qDrawPlainRect(painter, option->rect, option->palette.shadow().color(),1,&option->palette.light());
+ break;
+ case PE_FrameLineEdit:
+ case PE_FrameMenu:
+ if (d->doubleControls)
+ qDrawPlainRect(painter, option->rect, option->palette.shadow().color(),2);
+ else
+ qDrawPlainRect(painter, option->rect, option->palette.shadow().color(),1);
+ break;
+ case PE_FrameStatusBar:
+ if (d->doubleControls)
+ qDrawPlainRect(painter, option->rect, option->palette.shadow().color(),2,0);
+ else
+ qDrawPlainRect(painter, option->rect, option->palette.shadow().color(),1,0);
+ break;
+
+ default:
+ QWindowsStyle::drawPrimitive(element, option, painter, widget);
+ break;
+ }
+}
+
+void QWindowsMobileStyle::drawControl(ControlElement element, const QStyleOption *option,
+ QPainter *painter, const QWidget *widget) const {
+
+
+ QWindowsMobileStylePrivate *d = const_cast<QWindowsMobileStylePrivate*>(d_func());
+
+
+ painter->setClipping(false);
+ switch (element) {
+ case CE_MenuBarEmptyArea:
+ painter->setClipping(true);
+ QWindowsStyle::drawControl(element, option, painter, widget);
+ break;
+ case CE_PushButtonBevel:
+ if (const QStyleOptionButton *button = qstyleoption_cast<const QStyleOptionButton *>(option)) {
+ QRect br = button->rect;
+ int dbi = pixelMetric(PM_ButtonDefaultIndicator, button, widget);
+
+ if (button->features & QStyleOptionButton::AutoDefaultButton)
+ br.setCoords(br.left() + dbi, br.top() + dbi, br.right() - dbi, br.bottom() - dbi);
+ QStyleOptionButton tmpBtn = *button;
+ tmpBtn.rect = br;
+ drawPrimitive(PE_PanelButtonCommand, &tmpBtn, painter, widget);
+ if (button->features & QStyleOptionButton::HasMenu) {
+ int mbi = pixelMetric(PM_MenuButtonIndicator, button, widget);
+ QRect ir = button->rect;
+ QStyleOptionButton newButton = *button;
+ if (d->doubleControls)
+ newButton.rect = QRect(ir.right() - mbi, ir.height() - 30, mbi, ir.height() - 4);
+ else
+ newButton.rect = QRect(ir.right() - mbi, ir.height() - 20, mbi, ir.height() - 4);
+ drawPrimitive(PE_IndicatorArrowDown, &newButton, painter, widget);
+ }
+ if (button->features & QStyleOptionButton::DefaultButton)
+ drawPrimitive(PE_FrameDefaultButton, option, painter, widget);
+ }
+ break;
+ case CE_RadioButton:
+ case CE_CheckBox:
+ if (const QStyleOptionButton *button = qstyleoption_cast<const QStyleOptionButton *>(option)) {
+ bool isRadio = (element == CE_RadioButton);
+ QStyleOptionButton subopt = *button;
+ subopt.rect = subElementRect(isRadio ? SE_RadioButtonIndicator
+ : SE_CheckBoxIndicator, button, widget);
+ drawPrimitive(isRadio ? PE_IndicatorRadioButton : PE_IndicatorCheckBox,
+ &subopt, painter, widget);
+ subopt.rect = subElementRect(isRadio ? SE_RadioButtonContents
+ : SE_CheckBoxContents, button, widget);
+ drawControl(isRadio ? CE_RadioButtonLabel : CE_CheckBoxLabel, &subopt, painter, widget);
+ if (button->state & State_HasFocus) {
+ QStyleOptionFocusRect fropt;
+ fropt.QStyleOption::operator=(*button);
+ fropt.rect = subElementRect(isRadio ? SE_RadioButtonFocusRect
+ : SE_CheckBoxFocusRect, button, widget);
+ drawPrimitive(PE_FrameFocusRect, &fropt, painter, widget);
+ }
+ }
+ break;
+ case CE_RadioButtonLabel:
+ case CE_CheckBoxLabel:
+ if (const QStyleOptionButton *button = qstyleoption_cast<const QStyleOptionButton *>(option)) {
+ uint alignment = visualAlignment(button->direction, Qt::AlignLeft | Qt::AlignVCenter);
+ if (!styleHint(SH_UnderlineShortcut, button, widget))
+ alignment |= Qt::TextHideMnemonic;
+ QPixmap pix;
+ QRect textRect = button->rect;
+ if (!button->icon.isNull()) {
+ pix = button->icon.pixmap(button->iconSize, button->state & State_Enabled ? QIcon::Normal : QIcon::Disabled);
+ drawItemPixmap(painter, button->rect, alignment, pix);
+ if (button->direction == Qt::RightToLeft)
+ textRect.setRight(textRect.right() - button->iconSize.width() - 4);
+ else
+ textRect.setLeft(textRect.left() + button->iconSize.width() + 4);
+ }
+ if (!button->text.isEmpty()){
+ if (button->state & State_Enabled)
+ drawItemText(painter, textRect, alignment | Qt::TextShowMnemonic,
+ button->palette, false, button->text, QPalette::WindowText);
+ else
+ drawItemText(painter, textRect, alignment | Qt::TextShowMnemonic,
+ button->palette, false, button->text, QPalette::Mid);
+ }
+ }
+ break;
+#ifndef QT_NO_PROGRESSBAR
+ case CE_ProgressBarGroove:
+ if (d->doubleControls)
+ qDrawPlainRect(painter, option->rect, option->palette.shadow().color(), 2, &option->palette.brush(QPalette::Window));
+ else
+ qDrawPlainRect(painter, option->rect, option->palette.shadow().color(), 1, &option->palette.brush(QPalette::Window));
+ break;
+#endif //QT_NO_PROGRESSBAR
+#ifndef QT_NO_TABBAR
+ case CE_TabBarTab:
+ if (const QStyleOptionTab *tab = qstyleoption_cast<const QStyleOptionTab *>(option)) {
+ drawControl(CE_TabBarTabShape, tab, painter, widget);
+ drawControl(CE_TabBarTabLabel, tab, painter, widget);
+ }
+ break;
+ case CE_TabBarTabShape:
+ if (const QStyleOptionTab *tab = qstyleoption_cast<const QStyleOptionTab *>(option)) {
+ if (tab->shape == QTabBar::RoundedNorth || tab->shape == QTabBar::RoundedEast ||
+ tab->shape == QTabBar::RoundedSouth || tab->shape == QTabBar::RoundedWest) {
+
+ painter->save();
+ painter->setPen(tab->palette.shadow().color());
+ if (d->doubleControls) {
+ QPen pen = painter->pen();
+ pen.setWidth(2);
+ pen.setCapStyle(Qt::FlatCap);
+ painter->setPen(pen);
+ }
+ if(tab->shape == QTabBar::RoundedNorth) {
+ if (tab->state & State_Selected) {
+ painter->fillRect(tab->rect, tab->palette.light());
+ painter->drawLine(tab->rect.topRight(), tab->rect.bottomRight());
+ }
+ else {
+ painter->fillRect(tab->rect, tab->palette.button());
+ painter->drawLine(tab->rect.bottomLeft() , tab->rect.bottomRight());
+ painter->drawLine(tab->rect.topRight(), tab->rect.bottomRight());
+ }
+ }
+ else if(tab->shape == QTabBar::RoundedSouth) {
+
+ if (tab->state & State_Selected) {
+ painter->fillRect(tab->rect.adjusted(0,-2,0,0), tab->palette.light());
+ painter->drawLine(tab->rect.topRight(), tab->rect.bottomRight());
+ }
+ else {
+ painter->fillRect(tab->rect, tab->palette.button());
+ if (d->doubleControls)
+ painter->drawLine(tab->rect.topLeft() + QPoint(0,1), tab->rect.topRight() + QPoint(0,1));
+ else
+ painter->drawLine(tab->rect.topLeft(), tab->rect.topRight());
+ painter->drawLine(tab->rect.topRight(), tab->rect.bottomRight());
+ }
+ }
+ else if(tab->shape == QTabBar::RoundedEast) {
+ if (tab->state & State_Selected) {
+ painter->fillRect(tab->rect, tab->palette.light());
+ painter->drawLine(tab->rect.topLeft(), tab->rect.topRight());
+ }
+ else {
+ painter->fillRect(tab->rect, tab->palette.button());
+ painter->drawLine(tab->rect.topLeft(), tab->rect.bottomLeft());
+ painter->drawLine(tab->rect.topLeft(), tab->rect.topRight());
+ }
+ }
+ else if(tab->shape == QTabBar::RoundedWest) {
+ if (tab->state & State_Selected) {
+ painter->fillRect(tab->rect, tab->palette.light());
+ painter->drawLine(tab->rect.bottomLeft(), tab->rect.bottomRight());
+ }
+ else {
+ painter->fillRect(tab->rect, tab->palette.button());
+ painter->drawLine(tab->rect.topRight(), tab->rect.bottomRight());
+ painter->drawLine(tab->rect.bottomLeft(), tab->rect.bottomRight());
+ }
+ }
+
+ painter->restore();
+ } else {
+ QCommonStyle::drawControl(element, option, painter, widget);
+ }
+ break; }
+
+#endif // QT_NO_TABBAR
+
+#ifndef QT_NO_TOOLBAR
+ case CE_ToolBar:
+ if (const QStyleOptionToolBar *toolBar = qstyleoption_cast<const QStyleOptionToolBar *>(option)) {
+ QRect rect = option->rect;
+ painter->save();
+ painter->setPen(option->palette.dark().color());
+ painter->fillRect(rect,option->palette.button());
+ if (d->doubleControls) {
+ QPen pen = painter->pen();
+ pen.setWidth(4);
+ painter->setPen(pen);
+ }
+ if (toolBar->toolBarArea == Qt::TopToolBarArea)
+ painter->drawLine(rect.bottomLeft(), rect.bottomRight());
+ else
+ painter->drawLine(rect.topLeft(), rect.topRight());
+ painter->restore();
+ break; }
+#endif //QT_NO_TOOLBAR
+ case CE_Header:
+ if (const QStyleOptionHeader *header = qstyleoption_cast<const QStyleOptionHeader *>(option)) {
+ QRegion clipRegion = painter->clipRegion();
+ painter->setClipRect(option->rect);
+ drawControl(CE_HeaderSection, header, painter, widget);
+ QStyleOptionHeader subopt = *header;
+ subopt.rect = subElementRect(SE_HeaderLabel, header, widget);
+ if (header->state & State_Sunken)
+ subopt.palette.setColor(QPalette::ButtonText, header->palette.brightText().color());
+ subopt.state |= QStyle::State_On;
+ if (subopt.rect.isValid())
+ drawControl(CE_HeaderLabel, &subopt, painter, widget);
+ if (header->sortIndicator != QStyleOptionHeader::None) {
+ subopt.rect = subElementRect(SE_HeaderArrow, option, widget);
+ drawPrimitive(PE_IndicatorHeaderArrow, &subopt, painter, widget);
+ }
+ painter->setClipRegion(clipRegion);
+ }
+ break;
+
+ case CE_HeaderSection:
+ if (const QStyleOptionHeader *header = qstyleoption_cast<const QStyleOptionHeader *>(option)) {
+ QBrush fill;
+ QColor color;
+ QRect rect = option->rect;
+ painter->setPen(option->palette.shadow().color());
+
+ int penSize = 1;
+
+ if (d->doubleControls) {
+ penSize = 2;
+ QPen pen = painter->pen();
+ pen.setWidth(2);
+ pen.setCapStyle(Qt::FlatCap);
+ painter->setPen(pen);
+ }
+
+ //fix Frame
+
+ if (header->position == QStyleOptionHeader::End
+ || (header->position == QStyleOptionHeader::OnlyOneSection
+ && !header->text.isEmpty()))
+ if (Qt::Horizontal == header->orientation )
+ rect.adjust(0, 0, penSize, 0);
+ else
+ rect.adjust(0, 0, 0, penSize);
+
+ if (option->state & State_Sunken) {
+ fill = option->palette.brush(QPalette::Shadow);
+ color = option->palette.light().color();
+ painter->drawLine(rect.bottomLeft(), rect.bottomRight());
+ painter->drawLine(rect.topRight(), rect.bottomRight());
+ rect.adjust(0, 0, -penSize, -penSize);
+ }
+ else {
+ fill = option->palette.brush(QPalette::Button);
+ color = option->palette.shadow().color();
+ if (Qt::Horizontal == header->orientation )
+ rect.adjust(-penSize, 0, 0, 0);
+ else
+ rect.adjust(0, -penSize, 0, 0);
+ }
+ if (Qt::Horizontal == header->orientation )
+ rect.adjust(0,-penSize,0,0);
+ else
+ rect.adjust(-penSize, 0, 0, 0);
+
+ if (option->state & State_Sunken) {
+ qDrawPlainRect(painter, rect, color, penSize, &fill);
+ } else {
+ //Corner
+ rect.adjust(-penSize, 0, 0, 0);
+ qDrawPlainRect(painter, rect, color, penSize, &fill);
+ }
+
+ //Hack to get rid of some double lines... StyleOptions need a clean flag for that
+ rect = option->rect;
+#ifndef QT_NO_SCROLLAREA
+ if (const QAbstractScrollArea *abstractScrollArea = qobject_cast<const QAbstractScrollArea *> (widget) ) {
+ QRect rectScrollArea = abstractScrollArea->geometry();
+ if (Qt::Horizontal == header->orientation )
+ if ((rectScrollArea.right() - rect.right() ) > 1)
+ painter->drawLine(rect.topRight(), rect.bottomRight());
+ else ;
+ else
+ if ((rectScrollArea.bottom() - rect.bottom() ) > 1)
+ painter->drawLine(rect.bottomLeft(), rect.bottomRight());
+ }
+#endif // QT_NO_SCROLLAREA
+ break; }
+
+#ifndef QT_NO_DOCKWIDGET
+ case CE_DockWidgetTitle:
+ if (const QStyleOptionDockWidget *dwOpt = qstyleoption_cast<const QStyleOptionDockWidget *>(option)) {
+ const QStyleOptionDockWidgetV2 *v2
+ = qstyleoption_cast<const QStyleOptionDockWidgetV2*>(option);
+ bool verticalTitleBar = v2 == 0 ? false : v2->verticalTitleBar;
+
+ QRect rect = dwOpt->rect;
+ QRect r = rect;
+
+ if (verticalTitleBar) {
+ QSize s = r.size();
+ s.transpose();
+ r.setSize(s);
+
+ painter->save();
+ painter->translate(r.left(), r.top() + r.width());
+ painter->rotate(-90);
+ painter->translate(-r.left(), -r.top());
+ }
+
+ bool floating = false;
+ bool active = dwOpt->state & State_Active;
+ int menuOffset = 0; //used to center text when floated
+ QColor inactiveCaptionTextColor = option->palette.highlightedText().color();
+ if (dwOpt->movable) {
+ QColor left, right;
+
+ //Titlebar gradient
+ if (widget && widget->isWindow()) {
+ floating = true;
+ if (active) {
+ right = option->palette.highlight().color();
+ left = right.lighter(125);
+ } else {
+ left = option->palette.highlight().color().lighter(125);
+ right = QColor(0xff, 0xff, 0xff);
+ }
+ menuOffset = 2;
+ QBrush fillBrush(left);
+ if (left != right) {
+ QPoint p1(r.x(), r.top() + r.height()/2);
+ QPoint p2(rect.right(), r.top() + r.height()/2);
+ QLinearGradient lg(p1, p2);
+ lg.setColorAt(0, left);
+ lg.setColorAt(1, right);
+ fillBrush = lg;
+ }
+ painter->fillRect(r.adjusted(0, 0, 0, -3), fillBrush);
+ } else {
+ painter->fillRect(r.adjusted(0, 0, 0, -3), option->palette.button().color());
+ }
+ painter->setPen(dwOpt->palette.color(QPalette::Light));
+ if (!widget || !widget->isWindow()) {
+ painter->drawLine(r.topLeft(), r.topRight());
+ painter->setPen(dwOpt->palette.color(QPalette::Dark));
+ painter->drawLine(r.bottomLeft(), r.bottomRight()); }
+ }
+ if (!dwOpt->title.isEmpty()) {
+ QFont oldFont = painter->font();
+ QFont newFont = oldFont;
+ if (newFont.pointSize() > 2)
+ newFont.setPointSize(newFont.pointSize() - 2);
+ if (floating)
+ newFont.setBold(true);
+ painter->setFont(newFont);
+ QPalette palette = dwOpt->palette;
+ palette.setColor(QPalette::Window, inactiveCaptionTextColor);
+ QRect titleRect = subElementRect(SE_DockWidgetTitleBarText, option, widget);
+ if (verticalTitleBar) {
+ titleRect = QRect(r.left() + rect.bottom()
+ - titleRect.bottom(),
+ r.top() + titleRect.left() - rect.left(),
+ titleRect.height(), titleRect.width());
+ }
+ drawItemText(painter, titleRect,
+ Qt::AlignLeft | Qt::AlignVCenter | Qt::TextShowMnemonic, palette,
+ dwOpt->state & State_Enabled, dwOpt->title,
+ floating ? (active ? QPalette::BrightText : QPalette::Window) : QPalette::WindowText);
+ painter->setFont(oldFont);
+ }
+ if (verticalTitleBar)
+ painter->restore();
+ }
+ return;
+#endif // QT_NO_DOCKWIDGET
+
+ case CE_PushButtonLabel:
+ if (const QStyleOptionButton *button = qstyleoption_cast<const QStyleOptionButton *>(option)) {
+ painter->save();
+ QRect ir = button->rect;
+ QPalette::ColorRole colorRole;
+ uint tf = Qt::AlignVCenter | Qt::TextShowMnemonic;
+ if (!styleHint(SH_UnderlineShortcut, button, widget))
+ tf |= Qt::TextHideMnemonic;
+
+ if (button->state & (State_On | State_Sunken))
+ colorRole = QPalette::Light;
+ else
+ colorRole = QPalette::ButtonText;
+
+ if (!button->icon.isNull()) {
+ QIcon::Mode mode = button->state & State_Enabled ? QIcon::Normal
+ : QIcon::Disabled;
+ if (mode == QIcon::Normal && button->state & State_HasFocus)
+ mode = QIcon::Active;
+ QIcon::State state = QIcon::Off;
+ if (button->state & State_On)
+ state = QIcon::On;
+ QPixmap pixmap = button->icon.pixmap(button->iconSize, mode, state);
+ int pixw = pixmap.width();
+ int pixh = pixmap.height();
+ //Center the icon if there is no text
+
+ QPoint point;
+ if (button->text.isEmpty()) {
+ point = QPoint(ir.x() + ir.width() / 2 - pixw / 2,
+ ir.y() + ir.height() / 2 - pixh / 2);
+ } else {
+ point = QPoint(ir.x() + 2, ir.y() + ir.height() / 2 - pixh / 2);
+ }
+ if (button->direction == Qt::RightToLeft)
+ point.rx() += pixw;
+
+ if ((button->state & (State_On | State_Sunken)) && button->direction == Qt::RightToLeft)
+ point.rx() -= pixelMetric(PM_ButtonShiftHorizontal, option, widget) * 2;
+
+ painter->drawPixmap(visualPos(button->direction, button->rect, point), pixmap);
+
+ if (button->direction == Qt::RightToLeft)
+ ir.translate(-4, 0);
+ else
+ ir.translate(pixw + 4, 0);
+ ir.setWidth(ir.width() - (pixw + 4));
+ // left-align text if there is
+ if (!button->text.isEmpty())
+ tf |= Qt::AlignLeft;
+ } else {
+ tf |= Qt::AlignHCenter;
+ }
+ if (button->state & State_Enabled)
+ drawItemText(painter, ir, tf, button->palette, true, button->text, colorRole);
+ else
+ drawItemText(painter, ir, tf, button->palette, true, button->text, QPalette::Mid);
+ painter->restore();
+ }
+ break;
+ default:
+ QWindowsStyle::drawControl(element, option, painter, widget);
+ break;
+ }
+}
+
+void QWindowsMobileStyle::drawComplexControl(ComplexControl control, const QStyleOptionComplex *option,
+ QPainter *painter, const QWidget *widget) const {
+
+ painter->setClipping(false);
+ QWindowsMobileStylePrivate *d = const_cast<QWindowsMobileStylePrivate*>(d_func());
+
+ switch (control) {
+#ifndef QT_NO_SLIDER
+ case CC_Slider:
+ if (const QStyleOptionSlider *slider = qstyleoption_cast<const QStyleOptionSlider *>(option)) {
+ int thickness = pixelMetric(PM_SliderControlThickness, slider, widget);
+ int len = pixelMetric(PM_SliderLength, slider, widget);
+ int ticks = slider->tickPosition;
+ QRect groove = subControlRect(CC_Slider, slider, SC_SliderGroove, widget);
+ QRect handle = subControlRect(CC_Slider, slider, SC_SliderHandle, widget);
+
+ if ((slider->subControls & SC_SliderGroove) && groove.isValid()) {
+ int mid = thickness / 2;
+ if (ticks & QSlider::TicksAbove)
+ mid += len / 8;
+ if (ticks & QSlider::TicksBelow)
+ mid -= len / 8;
+
+ painter->setPen(slider->palette.shadow().color());
+ if (slider->orientation == Qt::Horizontal) {
+ qDrawPlainRect(painter, groove.x(), groove.y() + mid - 2,
+ groove.width(), 4, option->palette.shadow().color(),1,0);
+ } else {
+ qDrawPlainRect(painter, groove.x()+mid-2, groove.y(),
+ 4, groove.height(), option->palette.shadow().color(),1,0);
+ }
+ }
+ if (slider->subControls & SC_SliderTickmarks) {
+ QStyleOptionSlider tmpSlider = *slider;
+ tmpSlider.subControls = SC_SliderTickmarks;
+ QCommonStyle::drawComplexControl(control, &tmpSlider, painter, widget);
+ }
+
+ if (slider->subControls & SC_SliderHandle) {
+ const QColor c0 = slider->palette.shadow().color();
+ const QColor c1 = slider->palette.dark().color();
+ const QColor c3 = slider->palette.midlight().color();
+ const QColor c4 = slider->palette.dark().color();
+ QBrush handleBrush;
+
+ if (slider->state & State_Enabled) {
+ handleBrush = slider->palette.color(QPalette::Light);
+ } else {
+ handleBrush = QBrush(slider->palette.color(QPalette::Shadow),
+ Qt::Dense4Pattern);
+ }
+ int x = handle.x(), y = handle.y(),
+ wi = handle.width(), he = handle.height();
+ int x1 = x;
+ int x2 = x+wi-1;
+ int y1 = y;
+ int y2 = y+he-1;
+
+ Qt::Orientation orient = slider->orientation;
+ bool tickAbove = slider->tickPosition == QSlider::TicksAbove;
+ bool tickBelow = slider->tickPosition == QSlider::TicksBelow;
+
+ if (slider->state & State_HasFocus) {
+ QStyleOptionFocusRect fropt;
+ fropt.QStyleOption::operator=(*slider);
+ fropt.rect = subElementRect(SE_SliderFocusRect, slider, widget);
+ drawPrimitive(PE_FrameFocusRect, &fropt, painter, widget);
+ }
+ if ((tickAbove && tickBelow) || (!tickAbove && !tickBelow)) {
+ Qt::BGMode oldMode = painter->backgroundMode();
+ painter->setBackgroundMode(Qt::OpaqueMode);
+ qDrawPlainRect(painter, QRect(x, y, wi, he)
+ ,slider->palette.shadow().color(),1,&handleBrush);
+ painter->setBackgroundMode(oldMode);
+ QBrush fill = QBrush(option->palette.light().color(), Qt::Dense4Pattern);
+ if (slider->state & State_Sunken)
+ painter->fillRect(QRectF(x1 + 2, y1 + 2, x2 - x1 - 3, y2 - y1 - 3),fill);
+ return;
+ }
+ QSliderDirection dir;
+ if (orient == Qt::Horizontal)
+ if (tickAbove)
+ dir = SliderUp;
+ else
+ dir = SliderDown;
+ else
+ if (tickAbove)
+ dir = SliderLeft;
+ else
+ dir = SliderRight;
+ QPolygon polygon;
+ int d = 0;
+ switch (dir) {
+ case SliderUp:
+ x2++;
+ y1 = y1 + wi / 2;
+ d = (wi + 1) / 2 - 1;
+ polygon.setPoints(5, x1, y1, x1, y2, x2, y2, x2, y1, x1 + d,y1 - d);
+ break;
+ case SliderDown:
+ x2++;
+ y2 = y2 - wi/2;
+ d = (wi + 1) / 2 - 1;
+ polygon.setPoints(5, x1, y1, x1, y2, x1 + d,y2 + d, x2, y2, x2, y1);
+ break;
+ case SliderLeft:
+ d = (he + 1) / 2 - 1;
+ x1 = x1 + he/2;
+ polygon.setPoints(5, x1, y1, x1 - d, y1 + d, x1,y2, x2, y2, x2, y1);
+ y1--;
+ break;
+ case SliderRight:
+ d = (he + 1) / 2 - 1;
+ x2 = x2 - he/2;
+ polygon.setPoints(5, x1, y1, x1, y2, x2,y2, x2 + d, y1 + d, x2, y1);
+ y1--;
+ break;
+ }
+ QBrush oldBrush = painter->brush();
+ painter->setPen(Qt::NoPen);
+ painter->setBrush(handleBrush);
+ Qt::BGMode oldMode = painter->backgroundMode();
+ painter->setBackgroundMode(Qt::OpaqueMode);
+ painter->drawRect(x1, y1, x2-x1+1, y2-y1+1);
+ painter->drawPolygon(polygon);
+ QBrush fill = QBrush(option->palette.button().color(), Qt::Dense4Pattern);
+ painter->setBrush(oldBrush);
+ painter->setBackgroundMode(oldMode);
+ if (slider->state & State_Sunken)
+ painter->fillRect(QRectF(x1, y1, x2 - x1 + 1, y2 - y1 + 1),fill);
+
+ if (dir != SliderUp) {
+ painter->setPen(c0);
+ painter->drawLine(x1, y1, x2, y1);
+ }
+ if (dir != SliderLeft) {
+ painter->setPen(c0);
+ painter->drawLine(x1, y1, x1, y2);
+ }
+ if (dir != SliderRight) {
+ painter->setPen(c0);
+ painter->drawLine(x2, y1, x2, y2);
+ }
+ if (dir != SliderDown) {
+ painter->setPen(c0);
+ painter->drawLine(x1, y2, x2, y2);
+ }
+ switch (dir) {
+ case SliderUp:
+ if (slider->state & State_Sunken)
+ painter->fillRect(QRectF(x1 + 3, y1 - d + 2, x2 - x1 - 4, y1),fill);
+ painter->setPen(c0);
+ painter->drawLine(x1, y1, x1 + d, y1 - d);
+ d = wi - d - 1;
+ painter->drawLine(x2, y1, x2 -d , y1 -d );
+ d--;
+ break;
+ case SliderDown:
+ if (slider->state & State_Sunken)
+ painter->fillRect(QRectF(x1+3, y2 - d, x2 - x1 -4,y2 - 8),fill);
+ painter->setPen(c0);
+ painter->drawLine(x1, y2, x1 + d, y2 + d);
+ d = wi - d - 1;
+ painter->drawLine(x2, y2, x2 - d, y2 + d);
+ d--;
+ break;
+ case SliderLeft:
+ if (slider->state & State_Sunken)
+ painter->fillRect(QRectF(x1 - d + 2, y1 + 2, x1, y2 - y1 - 3),fill);
+ painter->setPen(c0);
+ painter->drawLine(x1, y1, x1 - d, y1 + d);
+ d = he - d - 1;
+ painter->drawLine(x1, y2, x1 - d, y2 - d);
+ d--;
+ break;
+ case SliderRight:
+ if (slider->state & State_Sunken)
+ painter->fillRect(QRectF(x2 - d - 4, y1 + 2, x2 - 4, y2 - y1 - 3),fill);
+ painter->setPen(c0);
+ painter->drawLine(x2, y1, x2 + d, y1 + d);
+ painter->setPen(c0);
+ d = he - d - 1;
+ painter->drawLine(x2, y2, x2 + d, y2 - d);
+ d--;
+ break;
+ }
+ }
+ }
+ break;
+#endif //QT_NO_SLIDER
+#ifndef QT_NO_SCROLLBAR
+ case CC_ScrollBar:
+ painter->save();
+ painter->setPen(option->palette.shadow().color());
+ if (d->doubleControls) {
+ QPen pen = painter->pen();
+ pen.setWidth(2);
+ pen.setCapStyle(Qt::SquareCap);
+ painter->setPen(pen);
+ }
+ if (const QStyleOptionSlider *scrollbar = qstyleoption_cast<const QStyleOptionSlider *>(option)) {
+ // Make a copy here and reset it for each primitive.
+ QBrush fill;
+ if (d->smartphone) {
+ fill = option->palette.light();
+ painter->fillRect(option->rect,fill);
+ fill = option->palette.button();
+ QImage image;
+#ifndef QT_NO_IMAGEFORMAT_XPM
+ if (scrollbar->orientation == Qt::Horizontal)
+ image = QImage(vertlines_xpm);
+ else
+ image = QImage(horlines_xpm);
+#endif
+ image.setColor(1, option->palette.button().color().rgb());
+ fill.setTextureImage(image);
+ }
+ else {
+ fill = option->palette.light();
+ }
+ painter->fillRect(option->rect,fill);
+ QStyleOptionSlider newScrollbar = *scrollbar;
+ State saveFlags = scrollbar->state;
+ //Check if the scrollbar is part of an abstractItemView and draw the frame according
+ bool drawCompleteFrame = true;
+ bool secondScrollBar = false;
+ if (widget)
+ if (QWidget *parent = widget->parentWidget()) {
+ if (QAbstractScrollArea *abstractScrollArea = qobject_cast<QAbstractScrollArea *>(parent->parentWidget())) {
+ drawCompleteFrame = (abstractScrollArea->frameStyle() == QFrame::NoFrame) || (abstractScrollArea->frameStyle() == QFrame::StyledPanel);
+ secondScrollBar = (abstractScrollArea->horizontalScrollBar()->isVisible()
+ && abstractScrollArea->verticalScrollBar()->isVisible()) ;
+ }
+#ifndef QT_NO_LISTVIEW
+ if (QListView *listView = qobject_cast<QListView *>(parent->parentWidget()))
+ drawCompleteFrame = false;
+#endif
+ }
+ if (scrollbar->minimum == scrollbar->maximum)
+ saveFlags |= State_Enabled;
+ if (scrollbar->subControls & SC_ScrollBarSubLine) {
+ newScrollbar.state = saveFlags;
+ newScrollbar.rect = subControlRect(control, &newScrollbar, SC_ScrollBarSubLine, widget);
+ if (newScrollbar.rect.isValid()) {
+ if (!(scrollbar->activeSubControls & SC_ScrollBarSubLine))
+ newScrollbar.state &= ~(State_Sunken | State_MouseOver);
+ QStyleOption arrowOpt = newScrollbar;
+ if (d->doubleControls)
+ arrowOpt.rect = newScrollbar.rect.adjusted(4, 6, -5, -3);
+ else
+ arrowOpt.rect = newScrollbar.rect.adjusted(5, 6, -4, -3);
+ QBrush fill = option->palette.button();
+ if (newScrollbar.state & State_Sunken)
+ fill = option->palette.shadow();
+ if (scrollbar->orientation == Qt::Horizontal) {
+ painter->fillRect(newScrollbar.rect,fill);
+ QRect r = newScrollbar.rect.adjusted(0,0,1,0);
+ painter->drawLine(r.topRight(), r.bottomRight());
+ if (d->doubleControls)
+ arrowOpt.rect.adjust(0, -2 ,0, -2);
+ drawPrimitive(PE_IndicatorArrowLeft, &arrowOpt, painter, widget);
+ }
+ else {
+ painter->fillRect(newScrollbar.rect,fill);
+ QRect r = newScrollbar.rect.adjusted(0, 0, 0, 1);
+ painter->drawLine(r.bottomLeft(), r.bottomRight());
+ if (drawCompleteFrame)
+ arrowOpt.rect.adjust(-2, 0, -2, 0);
+ if (d->doubleControls)
+ arrowOpt.rect.adjust(0, -4 , 0, -4);
+ if (drawCompleteFrame && d->doubleControls)
+ arrowOpt.rect.adjust(2, 0, 2, 0);
+ drawPrimitive(PE_IndicatorArrowUp, &arrowOpt, painter, widget);
+ }
+ }
+ }
+ if (scrollbar->subControls & SC_ScrollBarAddLine) {
+ newScrollbar.rect = scrollbar->rect;
+ newScrollbar.state = saveFlags;
+ newScrollbar.rect = subControlRect(control, &newScrollbar, SC_ScrollBarAddLine, widget);
+ if (newScrollbar.rect.isValid()) {
+ if (!(scrollbar->activeSubControls & SC_ScrollBarAddLine))
+ newScrollbar.state &= ~(State_Sunken | State_MouseOver);
+ QStyleOption arrowOpt = newScrollbar;
+ if (d->doubleControls)
+ arrowOpt.rect = newScrollbar.rect.adjusted(4, 0, -5, 3);
+ else
+ arrowOpt.rect = newScrollbar.rect.adjusted(5, 6, -4, -3);
+ QBrush fill = option->palette.button();
+ if (newScrollbar.state & State_Sunken)
+ fill = option->palette.shadow();
+ if (scrollbar->orientation == Qt::Horizontal) {
+ painter->fillRect(newScrollbar.rect,fill);
+ QRect r = newScrollbar.rect.adjusted(0, 0, 0, 0);
+ painter->drawLine(r.topLeft(), r.bottomLeft());
+ if (secondScrollBar)
+ painter->drawLine(r.topRight(), r.bottomRight());
+ if (d->doubleControls)
+ arrowOpt.rect.adjust(0, 4, 0, 4 );
+ drawPrimitive(PE_IndicatorArrowRight, &arrowOpt, painter, widget);
+ }
+ else {
+ painter->fillRect(newScrollbar.rect,fill);
+ QRect r = newScrollbar.rect.adjusted(0, -1, 0, -1);
+ painter->drawLine(r.topLeft(), r.topRight());
+ if (secondScrollBar)
+ painter->drawLine(r.bottomLeft() + QPoint(0,1), r.bottomRight() + QPoint(0, 1));
+ if (drawCompleteFrame)
+ arrowOpt.rect.adjust(-2, 0, -2, 0);
+ if (d->doubleControls)
+ arrowOpt.rect.adjust(1, 0, 1, 0 );
+ if (drawCompleteFrame && d->doubleControls)
+ arrowOpt.rect.adjust(1, 0, 1, 0);
+ drawPrimitive(PE_IndicatorArrowDown, &arrowOpt, painter, widget);
+ }
+ }
+ }
+ if (scrollbar->subControls & SC_ScrollBarSubPage) {
+ newScrollbar.rect = scrollbar->rect;
+ newScrollbar.state = saveFlags;
+ newScrollbar.rect = subControlRect(control, &newScrollbar, SC_ScrollBarSubPage, widget);
+ if (newScrollbar.rect.isValid()) {
+ if (!(scrollbar->activeSubControls & SC_ScrollBarSubPage))
+ newScrollbar.state &= ~(State_Sunken | State_MouseOver);
+ if (scrollbar->orientation == Qt::Horizontal) {
+ QRect r = newScrollbar.rect.adjusted(0, 0, 0, 0);
+ }
+ else{
+ QRect r = newScrollbar.rect.adjusted(0, 0, 0, 0);
+ }
+ }
+ }
+ if (scrollbar->subControls & SC_ScrollBarAddPage) {
+ newScrollbar.rect = scrollbar->rect;
+ newScrollbar.state = saveFlags;
+ newScrollbar.rect = subControlRect(control, &newScrollbar, SC_ScrollBarAddPage, widget);
+ if (newScrollbar.rect.isValid()) {
+ if (!(scrollbar->activeSubControls & SC_ScrollBarAddPage))
+ newScrollbar.state &= ~(State_Sunken | State_MouseOver);
+ if (scrollbar->orientation == Qt::Horizontal) {
+ QRect r = newScrollbar.rect.adjusted(0, 0, 0, -1);
+ }
+ else {
+ QRect r = newScrollbar.rect.adjusted(0, 0,- 1, 0);
+ }
+ }
+ }
+ if (scrollbar->subControls & SC_ScrollBarFirst) {
+ newScrollbar.rect = scrollbar->rect;
+ newScrollbar.state = saveFlags;
+ newScrollbar.rect = subControlRect(control, &newScrollbar, SC_ScrollBarFirst, widget);
+ if (newScrollbar.rect.isValid()) {
+ if (!(scrollbar->activeSubControls & SC_ScrollBarFirst))
+ newScrollbar.state &= ~(State_Sunken | State_MouseOver);
+ QRect r = newScrollbar.rect;
+ }
+ }
+ if (scrollbar->subControls & SC_ScrollBarLast) {
+ newScrollbar.rect = scrollbar->rect;
+ newScrollbar.state = saveFlags;
+ newScrollbar.rect = subControlRect(control, &newScrollbar, SC_ScrollBarLast, widget);
+ if (newScrollbar.rect.isValid()) {
+ if (!(scrollbar->activeSubControls & SC_ScrollBarLast))
+ newScrollbar.state &= ~(State_Sunken | State_MouseOver);
+ QRect r = newScrollbar.rect;
+ }
+ }
+ if (scrollbar->subControls & SC_ScrollBarSlider) {
+ newScrollbar.rect = scrollbar->rect;
+ newScrollbar.state = saveFlags;
+ newScrollbar.rect = subControlRect(control, &newScrollbar, SC_ScrollBarSlider, widget);
+ if (newScrollbar.rect.isValid()) {
+ if (!(scrollbar->activeSubControls & SC_ScrollBarSlider))
+ newScrollbar.state &= ~(State_Sunken | State_MouseOver);
+ if (scrollbar->orientation == Qt::Horizontal) {
+ painter->fillRect(newScrollbar.rect,option->palette.button());
+ QRect r = newScrollbar.rect;
+ painter->drawLine(r.topLeft(), r.bottomLeft());
+ painter->drawLine(r.topRight(), r.bottomRight());
+ if (d->smartphone) {
+ painter->drawLine(r.topLeft(), r.topRight());
+ painter->drawLine(r.bottomLeft(), r.bottomRight());
+ }
+ }
+ else {
+ painter->fillRect(newScrollbar.rect,option->palette.button());
+ QRect r = newScrollbar.rect;
+ painter->drawLine(r.topLeft(), r.topRight());
+ painter->drawLine(r.bottomLeft(), r.bottomRight());
+ if (d->smartphone) {
+ painter->drawLine(r.topLeft(), r.bottomLeft());
+ painter->drawLine(r.topRight(), r.bottomRight());
+ }
+ }
+ if (scrollbar->state & State_HasFocus) {
+ QStyleOptionFocusRect fropt;
+ fropt.QStyleOption::operator=(newScrollbar);
+ fropt.rect.setRect(newScrollbar.rect.x() + 2, newScrollbar.rect.y() + 2,
+ newScrollbar.rect.width() - 5,
+ newScrollbar.rect.height() - 5);
+ }
+ }
+ }
+ int gripMargin = d->doubleControls ? 4 : 2;
+ int doubleLines = d->doubleControls ? 2 : 1;
+ //If there is a frame around the scrollbar (abstractScrollArea),
+ //then the margin is different, because of the missing frame
+ int gripMarginFrame = d->doubleControls ? 3 : 1;
+ if (drawCompleteFrame)
+ gripMarginFrame = 0;
+ //draw grips
+ if (!d->smartphone)
+ if (scrollbar->orientation == Qt::Horizontal) {
+ for (int i = -3; i < 3; i += 2) {
+ painter->drawLine(
+ QPoint(newScrollbar.rect.center().x() + i * doubleLines + 1,
+ newScrollbar.rect.top() + gripMargin +gripMarginFrame),
+ QPoint(newScrollbar.rect.center().x() + i * doubleLines + 1,
+ newScrollbar.rect.bottom() - gripMargin));
+ }
+ } else {
+ for (int i = -2; i < 4 ; i += 2) {
+ painter->drawLine(
+ QPoint(newScrollbar.rect.left() + gripMargin + gripMarginFrame ,
+ newScrollbar.rect.center().y() + 1 + i * doubleLines - 1),
+ QPoint(newScrollbar.rect.right() - gripMargin,
+ newScrollbar.rect.center().y() + 1 + i * doubleLines - 1));
+ }
+ }
+ if (!d->smartphone) {
+ QRect r;
+ if (d->doubleControls)
+ r = option->rect.adjusted(1, 1, -1, 0);
+ else
+ r = option->rect.adjusted(0, 0, -1, 0);
+ if (drawCompleteFrame && d->doubleControls)
+ r.adjust(0, 0, 0, -1);
+ //Check if the scrollbar is part of an abstractItemView and draw the frame according
+ if (drawCompleteFrame)
+ painter->drawRect(r);
+ else
+ if (scrollbar->orientation == Qt::Horizontal)
+ painter->drawLine(r.topLeft(), r.topRight());
+ else
+ painter->drawLine(r.topLeft(), r.bottomLeft());
+ }
+ }
+ painter->restore();
+ break;
+#endif // QT_NO_SLIDER
+ case CC_ToolButton:
+ if (const QStyleOptionToolButton *toolbutton
+ = qstyleoption_cast<const QStyleOptionToolButton *>(option)) {
+ QRect button, menuarea;
+ bool isTabWidget = false;
+#ifndef QT_NO_TABWIDGET
+ if (widget)
+ if (QWidget *parent = widget->parentWidget())
+ isTabWidget = (qobject_cast<QTabWidget *>(parent->parentWidget()));
+#endif //QT_NO_TABWIDGET
+
+ button = subControlRect(control, toolbutton, SC_ToolButton, widget);
+ menuarea = subControlRect(control, toolbutton, SC_ToolButtonMenu, widget);
+ State buttonFlags = toolbutton->state;
+ if (buttonFlags & State_AutoRaise) {
+ if (!(buttonFlags & State_MouseOver)) {
+ buttonFlags &= ~State_Raised;
+ }
+ }
+ State menuFlags = buttonFlags;
+ if (toolbutton->activeSubControls & SC_ToolButton)
+ buttonFlags |= State_Sunken;
+ if (toolbutton->activeSubControls & SC_ToolButtonMenu)
+ menuFlags |= State_On;
+ QStyleOption tool(0);
+ tool.palette = toolbutton->palette;
+ if (toolbutton->subControls & SC_ToolButton) {
+ tool.rect = button;
+ tool.state = buttonFlags;
+ drawPrimitive(PE_PanelButtonTool, &tool, painter, widget);
+ }
+ if (toolbutton->subControls & SC_ToolButtonMenu) {
+ tool.rect = menuarea;
+ tool.state = buttonFlags & State_Enabled;
+ QStyleOption toolMenu(0);
+ toolMenu = *toolbutton;
+ toolMenu.state = menuFlags;
+ if (buttonFlags & State_Sunken)
+ drawPrimitive(PE_PanelButtonTool, &toolMenu, painter, widget);
+ QStyleOption arrowOpt(0);
+ arrowOpt.rect = tool.rect;
+ arrowOpt.palette = tool.palette;
+ State flags = State_None;
+ if (menuFlags & State_Enabled)
+ flags |= State_Enabled;
+ if ((menuFlags & State_On) && !(buttonFlags & State_Sunken)) {
+ flags |= State_Sunken;
+ painter->fillRect(menuarea, option->palette.shadow());
+ }
+ arrowOpt.state = flags;
+ drawPrimitive(PE_IndicatorArrowDown, &arrowOpt, painter, widget);
+ }
+ if (toolbutton->state & State_HasFocus) {
+ QStyleOptionFocusRect focusRect;
+ focusRect.QStyleOption::operator=(*toolbutton);
+ focusRect.rect.adjust(3, 3, -3, -3);
+ if (toolbutton->features & QStyleOptionToolButton::Menu)
+ focusRect.rect.adjust(0, 0, -pixelMetric(QStyle::PM_MenuButtonIndicator,
+ toolbutton, widget), 0);
+ drawPrimitive(PE_FrameFocusRect, &focusRect, painter, widget);
+ }
+ QStyleOptionToolButton label = *toolbutton;
+ if (isTabWidget)
+ label.state = toolbutton->state;
+ else
+ label.state = toolbutton->state & State_Enabled;
+ int fw = pixelMetric(PM_DefaultFrameWidth, option, widget);
+ label.rect = button.adjusted(fw, fw, -fw, -fw);
+ drawControl(CE_ToolButtonLabel, &label, painter, widget);
+ }
+ break;
+
+#ifndef QT_NO_GROUPBOX
+ case CC_GroupBox:
+ if (const QStyleOptionGroupBox *groupBox = qstyleoption_cast<const QStyleOptionGroupBox *>(option)) {
+ // Draw frame
+ painter->save();
+ QFont font = painter->font();
+ font.setBold(true);
+ painter->setFont(font);
+ QStyleOptionGroupBox groupBoxFont = *groupBox;
+ groupBoxFont.fontMetrics = QFontMetrics(font);
+ QRect textRect = subControlRect(CC_GroupBox, &groupBoxFont, SC_GroupBoxLabel, widget);
+ QRect checkBoxRect = subControlRect(CC_GroupBox, option, SC_GroupBoxCheckBox, widget).adjusted(0,0,0,0);
+ if (groupBox->subControls & QStyle::SC_GroupBoxFrame) {
+ QStyleOptionFrameV2 frame;
+ frame.QStyleOption::operator=(*groupBox);
+ frame.features = groupBox->features;
+ frame.lineWidth = groupBox->lineWidth;
+ frame.midLineWidth = groupBox->midLineWidth;
+ frame.rect = subControlRect(CC_GroupBox, option, SC_GroupBoxFrame, widget);
+ painter->save();
+ QRegion region(groupBox->rect);
+ if (!groupBox->text.isEmpty()) {
+ bool ltr = groupBox->direction == Qt::LeftToRight;
+ QRect finalRect = checkBoxRect.united(textRect);
+ if (groupBox->subControls & QStyle::SC_GroupBoxCheckBox)
+ finalRect.adjust(ltr ? -4 : 0, 0, ltr ? 0 : 4, 0);
+ region -= finalRect;
+ }
+ drawPrimitive(PE_FrameGroupBox, &frame, painter, widget);
+ painter->restore();
+ }
+ // Draw checkbox
+ if (groupBox->subControls & SC_GroupBoxCheckBox) {
+ QStyleOptionButton box;
+ box.QStyleOption::operator=(*groupBox);
+ box.rect = checkBoxRect;
+ drawPrimitive(PE_IndicatorCheckBox, &box, painter, widget);
+ }
+ // Draw title
+ if ((groupBox->subControls & QStyle::SC_GroupBoxLabel) && !groupBox->text.isEmpty()) {
+ QColor textColor = groupBox->textColor;
+ if (textColor.isValid())
+ painter->setPen(textColor);
+ else
+ painter->setPen(groupBox->palette.link().color());
+ painter->setPen(groupBox->palette.link().color());
+
+ int alignment = int(groupBox->textAlignment);
+ if (!styleHint(QStyle::SH_UnderlineShortcut, option, widget))
+ alignment |= Qt::TextHideMnemonic;
+
+ if (groupBox->state & State_Enabled)
+ drawItemText(painter, textRect, Qt::TextShowMnemonic | Qt::AlignHCenter | alignment,
+ groupBox->palette, true, groupBox->text,
+ textColor.isValid() ? QPalette::NoRole : QPalette::Link);
+ else
+ drawItemText(painter, textRect, Qt::TextShowMnemonic | Qt::AlignHCenter | alignment,
+ groupBox->palette, true, groupBox->text, QPalette::Mid);
+ if (groupBox->state & State_HasFocus) {
+ QStyleOptionFocusRect fropt;
+ fropt.QStyleOption::operator=(*groupBox);
+ fropt.rect = textRect;
+ drawPrimitive(PE_FrameFocusRect, &fropt, painter, widget);
+ }
+ }
+ painter->restore();
+ }
+ break;
+#endif //QT_NO_GROUPBOX
+
+#ifndef QT_NO_COMBOBOX
+ case CC_ComboBox:
+ if (const QStyleOptionComboBox *cmb = qstyleoption_cast<const QStyleOptionComboBox *>(option)) {
+ QBrush editBrush = cmb->palette.brush(QPalette::Base);
+ if ((cmb->subControls & SC_ComboBoxFrame) && cmb->frame)
+ qDrawPlainRect(painter, option->rect, option->palette.shadow().color(), pixelMetric(PM_ComboBoxFrameWidth, option, widget), &editBrush);
+ else
+ painter->fillRect(option->rect, editBrush);
+ State flags = State_None;
+ QRect ar = subControlRect(CC_ComboBox, cmb, SC_ComboBoxArrow, widget);
+ if ((option->state & State_On)) {
+ painter->fillRect(ar.adjusted(0, 0, 1, 1),cmb->palette.brush(QPalette::Shadow));
+ }
+ if (d->doubleControls)
+ ar.adjust(5, 0, 5, 0);
+ else
+ ar.adjust(2, 0, -2, 0);
+ if (option->state & State_Enabled)
+ flags |= State_Enabled;
+ if (option->state & State_On)
+ flags |= State_Sunken;
+ QStyleOption arrowOpt(0);
+ arrowOpt.rect = ar;
+ arrowOpt.palette = cmb->palette;
+ arrowOpt.state = flags;
+ drawPrimitive(PrimitiveElement(PE_IndicatorArrowDownBig), &arrowOpt, painter, widget);
+ if (cmb->subControls & SC_ComboBoxEditField) {
+ QRect re = subControlRect(CC_ComboBox, cmb, SC_ComboBoxEditField, widget);
+ if (cmb->state & State_HasFocus && !cmb->editable)
+ painter->fillRect(re.x(), re.y(), re.width(), re.height(),
+ cmb->palette.brush(QPalette::Highlight));
+ if (cmb->state & State_HasFocus) {
+ painter->setPen(cmb->palette.highlightedText().color());
+ painter->setBackground(cmb->palette.highlight());
+ } else {
+ painter->setPen(cmb->palette.text().color());
+ painter->setBackground(cmb->palette.background());
+ }
+ if (cmb->state & State_HasFocus && !cmb->editable) {
+ QStyleOptionFocusRect focus;
+ focus.QStyleOption::operator=(*cmb);
+ focus.rect = subElementRect(SE_ComboBoxFocusRect, cmb, widget);
+ focus.state |= State_FocusAtBorder;
+ focus.backgroundColor = cmb->palette.highlight().color();
+ if ((option->state & State_On))
+ drawPrimitive(PE_FrameFocusRect, &focus, painter, widget);
+ }
+ }
+ }
+ break;
+#endif // QT_NO_COMBOBOX
+
+
+#ifndef QT_NO_SPINBOX
+ case CC_SpinBox:
+ if (const QStyleOptionSpinBox *spinBox = qstyleoption_cast<const QStyleOptionSpinBox *>(option)) {
+ QStyleOptionSpinBox copy = *spinBox;
+ //PrimitiveElement primitiveElement;
+ int primitiveElement;
+
+ if (spinBox->frame && (spinBox->subControls & SC_SpinBoxFrame)) {
+ QRect r = subControlRect(CC_SpinBox, spinBox, SC_SpinBoxFrame, widget);
+ qDrawPlainRect(painter, r, option->palette.shadow().color(),pixelMetric(PM_SpinBoxFrameWidth, option, widget),0);
+ }
+ QPalette shadePal(option->palette);
+ shadePal.setColor(QPalette::Button, option->palette.light().color());
+ shadePal.setColor(QPalette::Light, option->palette.base().color());
+ if (spinBox->subControls & SC_SpinBoxUp) {
+ copy.subControls = SC_SpinBoxUp;
+ QPalette pal2 = spinBox->palette;
+ if (!(spinBox->stepEnabled & QAbstractSpinBox::StepUpEnabled)) {
+ pal2.setCurrentColorGroup(QPalette::Disabled);
+ copy.state &= ~State_Enabled;
+ }
+ copy.palette = pal2;
+ if (spinBox->activeSubControls == SC_SpinBoxUp && (spinBox->state & State_Sunken)) {
+ copy.state |= State_On;
+ copy.state |= State_Sunken;
+ } else {
+ copy.state |= State_Raised;
+ copy.state &= ~State_Sunken;
+ }
+ primitiveElement = (spinBox->buttonSymbols == QAbstractSpinBox::PlusMinus ? PE_IndicatorArrowUpBig
+ : PE_IndicatorArrowUpBig);
+ copy.rect = subControlRect(CC_SpinBox, spinBox, SC_SpinBoxUp, widget);
+ if (copy.state & (State_Sunken | State_On))
+ qDrawPlainRect(painter, copy.rect, option->palette.shadow().color(),pixelMetric(PM_SpinBoxFrameWidth, option, widget), &copy.palette.brush(QPalette::Shadow));
+ else
+ qDrawPlainRect(painter, copy.rect, option->palette.shadow().color(),pixelMetric(PM_SpinBoxFrameWidth, option, widget), &copy.palette.brush(QPalette::Base));
+ copy.rect.adjust(pixelMetric(PM_SpinBoxFrameWidth, option, widget), 0, -pixelMetric(PM_SpinBoxFrameWidth, option, widget), 0);
+ drawPrimitive(PrimitiveElement(primitiveElement), &copy, painter, widget);
+ }
+ if (spinBox->subControls & SC_SpinBoxDown) {
+ copy.subControls = SC_SpinBoxDown;
+ copy.state = spinBox->state;
+ QPalette pal2 = spinBox->palette;
+ if (!(spinBox->stepEnabled & QAbstractSpinBox::StepDownEnabled)) {
+ pal2.setCurrentColorGroup(QPalette::Disabled);
+ copy.state &= ~State_Enabled;
+ }
+ copy.palette = pal2;
+ if (spinBox->activeSubControls == SC_SpinBoxDown && (spinBox->state & State_Sunken)) {
+ copy.state |= State_On;
+ copy.state |= State_Sunken;
+ } else {
+ copy.state |= State_Raised;
+ copy.state &= ~State_Sunken;
+ }
+ primitiveElement = (spinBox->buttonSymbols == QAbstractSpinBox::PlusMinus ? PE_IndicatorArrowDownBig
+ : PE_IndicatorArrowDownBig);
+ copy.rect = subControlRect(CC_SpinBox, spinBox, SC_SpinBoxDown, widget);
+ qDrawPlainRect(painter, copy.rect, option->palette.shadow().color(),pixelMetric(PM_SpinBoxFrameWidth, option, widget), &copy.palette.brush(QPalette::Base));
+ if (copy.state & (State_Sunken | State_On))
+ qDrawPlainRect(painter, copy.rect, option->palette.shadow().color(),pixelMetric(PM_SpinBoxFrameWidth, option, widget), &copy.palette.brush(QPalette::Shadow));
+ else
+ qDrawPlainRect(painter, copy.rect, option->palette.shadow().color(),pixelMetric(PM_SpinBoxFrameWidth, option, widget), &copy.palette.brush(QPalette::Base));
+ copy.rect.adjust(3, 0, -4, 0);
+ if (primitiveElement == PE_IndicatorArrowUp || primitiveElement == PE_IndicatorArrowDown) {
+ int frameWidth = pixelMetric(PM_SpinBoxFrameWidth, option, widget);
+ copy.rect = copy.rect.adjusted(frameWidth, frameWidth, -frameWidth, -frameWidth);
+ drawPrimitive(PrimitiveElement(primitiveElement), &copy, painter, widget);
+ }
+ else {
+ drawPrimitive(PrimitiveElement(primitiveElement), &copy, painter, widget);
+ }
+ if (spinBox->frame && (spinBox->subControls & SC_SpinBoxFrame)) {
+ QRect r = subControlRect(CC_SpinBox, spinBox, SC_SpinBoxEditField, widget);
+ }
+ }
+ }
+ break;
+#endif // QT_NO_SPINBOX
+
+ default:
+ QWindowsStyle::drawComplexControl(control, option, painter, widget);
+ break;
+ }
+}
+
+QSize QWindowsMobileStyle::sizeFromContents(ContentsType type, const QStyleOption *option,
+ const QSize &size, const QWidget *widget) const {
+
+ QSize newSize = QWindowsStyle::sizeFromContents(type, option, size, widget);
+ switch (type) {
+ case CT_PushButton:
+ if (const QStyleOptionButton *button = qstyleoption_cast<const QStyleOptionButton *>(option)) {
+ newSize = QWindowsStyle::sizeFromContents(type, option, size, widget);
+ int w = newSize.width(),
+ h = newSize.height();
+ int defwidth = 0;
+ if (button->features & QStyleOptionButton::AutoDefaultButton)
+ defwidth = 2 * pixelMetric(PM_ButtonDefaultIndicator, button, widget);
+ if (w < 75 + defwidth && button->icon.isNull())
+ w = 75 + defwidth;
+ if (h < 23 + defwidth)
+ h = 23 + defwidth;
+ newSize = QSize(w + 4, h + 4);
+ }
+ break;
+
+#ifndef QT_NO_GROUPBOX
+ case CT_GroupBox:
+ if (const QGroupBox *grb = static_cast<const QGroupBox *>(widget)) {
+ newSize = size + QSize(!grb->isFlat() ? 16 : 0, !grb->isFlat() ? 16 : 0);
+ }
+ break;
+#endif // QT_NO_GROUPBOX
+
+ case CT_RadioButton:
+ case CT_CheckBox:
+ newSize = size;
+ if (const QStyleOptionButton *button = qstyleoption_cast<const QStyleOptionButton *>(option)) {
+ bool isRadio = (type == CT_RadioButton);
+ QRect irect = visualRect(button->direction, button->rect,
+ subElementRect(isRadio ? SE_RadioButtonIndicator
+ : SE_CheckBoxIndicator, button, widget));
+ int h = pixelMetric(isRadio ? PM_ExclusiveIndicatorHeight
+ : PM_IndicatorHeight, button, widget);
+ int margins = (!button->icon.isNull() && button->text.isEmpty()) ? 0 : 10;
+ if (d_func()->doubleControls)
+ margins *= 2;
+ newSize += QSize(irect.right() + margins, 1);
+ newSize.setHeight(qMax(newSize.height(), h));
+ }
+ break;
+#ifndef QT_NO_COMBOBOX
+ case CT_ComboBox:
+ if (const QStyleOptionComboBox *comboBox = qstyleoption_cast<const QStyleOptionComboBox *>(option)) {
+ int fw = comboBox->frame ? pixelMetric(PM_ComboBoxFrameWidth, option, widget) * 2 : 0;
+ newSize = QSize(newSize.width() + fw + 9, newSize.height() + fw-4); //Nine is a magic Number - See CommonStyle for real magic (23)
+ }
+ break;
+#endif
+#ifndef QT_NO_SPINBOX
+ case CT_SpinBox:
+ if (const QStyleOptionSpinBox *spinBox = qstyleoption_cast<const QStyleOptionSpinBox *>(option)) {
+ int fw = spinBox->frame ? pixelMetric(PM_SpinBoxFrameWidth, option, widget) * 2 : 0;
+ newSize = QSize(newSize.width() + fw-5, newSize.height() + fw-6);
+ }
+ break;
+#endif
+#ifndef QT_NO_LINEEDIT
+ case CT_LineEdit:
+ newSize += QSize(0,1);
+ break;
+#endif
+ case CT_ToolButton:
+ newSize = QSize(newSize.width() + 1, newSize.height());
+ break;
+ case CT_TabBarTab:
+ newSize += QSize(0,0);
+ break;
+ case CT_HeaderSection:
+ newSize += QSize(4, 2);
+ break;
+ default:
+ break;
+ }
+ return newSize;
+}
+
+QRect QWindowsMobileStyle::subElementRect(SubElement element, const QStyleOption *option, const QWidget *widget) const {
+
+ QWindowsMobileStylePrivate *d = const_cast<QWindowsMobileStylePrivate*>(d_func());
+ QRect rect = QWindowsStyle::subElementRect(element, option, widget);
+ switch (element) {
+#ifndef QT_NO_TABWIDGET
+ case SE_TabWidgetTabBar:
+ if (d->doubleControls)
+ rect.adjust(-2, 0, 2, 0);
+ else
+ rect.adjust(-2, 0, 2, 0);
+ break;
+#endif //QT_NO_TABWIDGET
+ case SE_CheckBoxFocusRect:
+ rect.adjust(1,0,-2,-1);
+ break;
+ case SE_RadioButtonFocusRect:
+ rect.adjust(1,1,-2,-2);
+ break;
+ default:
+ break;
+ #ifndef QT_NO_SLIDER
+ case SE_SliderFocusRect:
+ if (const QStyleOptionSlider *slider = qstyleoption_cast<const QStyleOptionSlider *>(option)) {
+ rect = slider->rect;
+ }
+ break;
+ case SE_PushButtonFocusRect:
+ if (d->doubleControls)
+ rect.adjust(-1, -1, 0, 0);
+ break;
+#endif // QT_NO_SLIDER
+ }
+ return rect;
+}
+
+QRect QWindowsMobileStyle::subControlRect(ComplexControl control, const QStyleOptionComplex *option,
+ SubControl subControl, const QWidget *widget) const {
+
+ QWindowsMobileStylePrivate *d = const_cast<QWindowsMobileStylePrivate*>(d_func());
+
+ QRect rect = QCommonStyle::subControlRect(control, option, subControl, widget);
+ switch (control) {
+
+#ifndef QT_NO_SCROLLBAR
+ case CC_ScrollBar:
+ if (const QStyleOptionSlider *scrollbar = qstyleoption_cast<const QStyleOptionSlider *>(option)) {
+ int sliderButtonExtent = pixelMetric(PM_ScrollBarExtent, scrollbar, widget);
+ int sliderlen;
+ float stretchFactor = 1.4f;
+ int sliderButtonExtentDir = int (sliderButtonExtent * stretchFactor);
+ int maxlen = ((scrollbar->orientation == Qt::Horizontal) ?
+ scrollbar->rect.width() : scrollbar->rect.height()) - (sliderButtonExtentDir * 2);
+ // calculate slider length
+ if (scrollbar->maximum != scrollbar->minimum) {
+ uint range = scrollbar->maximum - scrollbar->minimum;
+ sliderlen = (qint64(scrollbar->pageStep) * maxlen) / (range + scrollbar->pageStep);
+
+ int slidermin = pixelMetric(PM_ScrollBarSliderMin, scrollbar, widget);
+ if (sliderlen < slidermin || range > INT_MAX / 2)
+ sliderlen = slidermin;
+ if (sliderlen > maxlen)
+ sliderlen = maxlen;
+ } else {
+ sliderlen = maxlen;
+ }
+ int sliderstart = sliderButtonExtentDir + sliderPositionFromValue(scrollbar->minimum,
+ scrollbar->maximum,
+ scrollbar->sliderPosition,
+ maxlen - sliderlen,
+ scrollbar->upsideDown);
+ if (d->smartphone) {
+ sliderstart -= sliderButtonExtentDir;
+ sliderlen += 2*sliderButtonExtent;
+ }
+ switch (subControl) {
+ case SC_ScrollBarSubLine: // top/left button
+ if (scrollbar->orientation == Qt::Horizontal) {
+ int buttonWidth = qMin(scrollbar->rect.width() / 2, sliderButtonExtentDir );
+ rect.setRect(0, 0, buttonWidth, sliderButtonExtent);
+ } else {
+ int buttonHeight = qMin(scrollbar->rect.height() / 2, sliderButtonExtentDir);
+ rect.setRect(0, 0, sliderButtonExtent, buttonHeight);
+ }
+ if (d->smartphone)
+ rect.setRect(0, 0, 0, 0);
+ break;
+ case SC_ScrollBarAddLine: // bottom/right button
+ if (scrollbar->orientation == Qt::Horizontal) {
+ int buttonWidth = qMin(scrollbar->rect.width()/2, sliderButtonExtentDir);
+ rect.setRect(scrollbar->rect.width() - buttonWidth, 0, buttonWidth, sliderButtonExtent);
+ } else {
+ int buttonHeight = qMin(scrollbar->rect.height()/2, sliderButtonExtentDir );
+ rect.setRect(0, scrollbar->rect.height() - buttonHeight, sliderButtonExtent, buttonHeight);
+ }
+ if (d->smartphone)
+ rect.setRect(0, 0, 0, 0);
+ break;
+ case SC_ScrollBarSubPage: // between top/left button and slider
+ if (scrollbar->orientation == Qt::Horizontal)
+ if (d->smartphone)
+ rect.setRect(0, 0, sliderstart, sliderButtonExtent);
+ else
+ rect.setRect(sliderButtonExtent, 0, sliderstart - sliderButtonExtent, sliderButtonExtent);
+ else
+ if (d->smartphone)
+ rect.setRect(0, 0, sliderButtonExtent, sliderstart);
+ else
+ rect.setRect(0, sliderButtonExtent, sliderButtonExtent, sliderstart - sliderButtonExtent);
+ break;
+ case SC_ScrollBarAddPage: // between bottom/right button and slider
+ if (scrollbar->orientation == Qt::Horizontal)
+ if (d->smartphone)
+ rect.setRect(sliderstart + sliderlen, 0,
+ maxlen - sliderstart - sliderlen + 2*sliderButtonExtent, sliderButtonExtent);
+ else
+ rect.setRect(sliderstart + sliderlen, 0,
+ maxlen - sliderstart - sliderlen + sliderButtonExtent, sliderButtonExtent);
+ else
+ if (d->smartphone)
+ rect.setRect(0, sliderstart + sliderlen, sliderButtonExtent,
+ maxlen - sliderstart - sliderlen + 2*sliderButtonExtent);
+ else
+ rect.setRect(0, sliderstart + sliderlen, sliderButtonExtent,
+ maxlen - sliderstart - sliderlen + sliderButtonExtent);
+ break;
+ case SC_ScrollBarGroove:
+ if (scrollbar->orientation == Qt::Horizontal)
+ rect.setRect(sliderButtonExtent, 0, scrollbar->rect.width() - sliderButtonExtent * 2,
+ scrollbar->rect.height());
+ else
+ rect.setRect(0, sliderButtonExtent, scrollbar->rect.width(),
+ scrollbar->rect.height() - sliderButtonExtent * 2);
+ break;
+ case SC_ScrollBarSlider:
+ if (scrollbar->orientation == Qt::Horizontal)
+ rect.setRect(sliderstart, 0, sliderlen, sliderButtonExtent);
+ else
+ rect.setRect(0, sliderstart, sliderButtonExtent, sliderlen);
+ break;
+ default:
+ break;
+ }
+ rect = visualRect(scrollbar->direction, scrollbar->rect, rect);
+ }
+ break;
+#endif // QT_NO_SCROLLBAR
+
+
+
+#ifndef QT_NO_TOOLBUTTON
+ case CC_ToolButton:
+ if (const QStyleOptionToolButton *toolButton = qstyleoption_cast<const QStyleOptionToolButton *>(option)) {
+ int mbi = pixelMetric(PM_MenuButtonIndicator, toolButton, widget);
+ rect = toolButton->rect;
+ switch (subControl) {
+ case SC_ToolButton:
+ if ((toolButton->features
+ & (QStyleOptionToolButton::Menu | QStyleOptionToolButton::PopupDelay))
+ == QStyleOptionToolButton::Menu)
+ rect.adjust(0, 0, -mbi, 0);
+ break;
+ case SC_ToolButtonMenu:
+ if ((toolButton->features
+ & (QStyleOptionToolButton::Menu | QStyleOptionToolButton::PopupDelay))
+ == QStyleOptionToolButton::Menu)
+ rect.adjust(rect.width() - mbi, 1, 0, 1);
+ break;
+ default:
+ break;
+ }
+ rect = visualRect(toolButton->direction, toolButton->rect, rect);
+ }
+ break;
+#endif // QT_NO_TOOLBUTTON
+
+#ifndef QT_NO_SLIDER
+ case CC_Slider:
+ if (const QStyleOptionSlider *slider = qstyleoption_cast<const QStyleOptionSlider *>(option)) {
+ int tickOffset = pixelMetric(PM_SliderTickmarkOffset, slider, widget);
+ int thickness = pixelMetric(PM_SliderControlThickness, slider, widget);
+ switch (subControl) {
+ case SC_SliderHandle: {
+ int sliderPos = 0;
+ int len = pixelMetric(PM_SliderLength, slider, widget);
+ bool horizontal = slider->orientation == Qt::Horizontal;
+ sliderPos = sliderPositionFromValue(slider->minimum, slider->maximum,
+ slider->sliderPosition,
+ (horizontal ? slider->rect.width()
+ : slider->rect.height()) - len,
+ slider->upsideDown);
+ if (horizontal)
+ rect.setRect(slider->rect.x() + sliderPos, slider->rect.y() + tickOffset, len, thickness);
+ else
+ rect.setRect(slider->rect.x() + tickOffset, slider->rect.y() + sliderPos, thickness, len);
+ break; }
+ default:
+ break;
+ }
+ rect = visualRect(slider->direction, slider->rect, rect);
+ }
+ break;
+#endif //QT_NO_SLIDER
+#ifndef QT_NO_COMBOBOX
+ case CC_ComboBox:
+ if (const QStyleOptionComboBox *comboBox = qstyleoption_cast<const QStyleOptionComboBox *>(option)) {
+ int x = comboBox->rect.x(),
+ y = comboBox->rect.y(),
+ wi = comboBox->rect.width(),
+ he = comboBox->rect.height();
+ int xpos = x;
+ int margin = comboBox->frame ? (d->doubleControls ? 2 : 1) : 0;
+ int bmarg = comboBox->frame ? (d->doubleControls ? 2 : 1) : 0;
+ if (subControl == SC_ComboBoxArrow)
+ xpos += wi - int((he - 2*bmarg)*0.9) - bmarg;
+ else
+ xpos += wi - (he - 2*bmarg) - bmarg;
+ switch (subControl) {
+ case SC_ComboBoxArrow:
+ rect.setRect(xpos, y + bmarg, he - 2*bmarg, he - 2*bmarg);
+ rect.setRect(xpos, y + bmarg, int((he - 2*bmarg)), he - 2*bmarg);
+ break;
+ case SC_ComboBoxEditField:
+ rect.setRect(x + margin+4, y + margin+2, wi - 4 * margin - int((he - 2*bmarg) * 0.84f) -2, he - 2 * margin-4);
+ break;
+ case SC_ComboBoxFrame:
+ rect = comboBox->rect;
+ break;
+ default:
+ break;
+ }
+ }
+#endif //QT_NO_COMBOBOX
+#ifndef QT_NO_SPINBOX
+ case CC_SpinBox:
+ if (const QStyleOptionSpinBox *spinBox = qstyleoption_cast<const QStyleOptionSpinBox *>(option)) {
+ QSize bs;
+ int fw = spinBox->frame ? pixelMetric(PM_SpinBoxFrameWidth, spinBox, widget) : 0;
+ bs.setHeight(qMax(d->doubleControls ? 28 : 14, (spinBox->rect.height())));
+ // 1.6 -approximate golden mean
+ bs.setWidth(qMax(d->doubleControls ? 28 : 14, qMin((bs.height()*7/8), (spinBox->rect.width() / 8))));
+ bs = bs.expandedTo(QApplication::globalStrut());
+ int x, lx, rx;
+ x = spinBox->rect.width() - bs.width()*2;
+ lx = fw;
+ rx = x - fw;
+ switch (subControl) {
+ case SC_SpinBoxUp:
+ rect = QRect(x+pixelMetric(PM_SpinBoxFrameWidth, option, widget), 0 , bs.width(), bs.height());
+ break;
+ case SC_SpinBoxDown:
+ rect = QRect(x + bs.width(), 0, bs.width(), bs.height());
+ break;
+ case SC_SpinBoxEditField:
+ rect = QRect(lx, fw, rx-2, spinBox->rect.height() - 2*fw);
+ break;
+ case SC_SpinBoxFrame:
+ rect = spinBox->rect;
+ default:
+ break;
+ }
+ rect = visualRect(spinBox->direction, spinBox->rect, rect);
+ }
+ break;
+#endif // Qt_NO_SPINBOX
+#ifndef QT_NO_GROUPBOX
+ case CC_GroupBox: {
+ if (const QStyleOptionGroupBox *groupBox = qstyleoption_cast<const QStyleOptionGroupBox *>(option)) {
+ switch (subControl) {
+ case SC_GroupBoxFrame:
+ // FALL THROUGH
+ case SC_GroupBoxContents: {
+ int topMargin = 0;
+ int topHeight = 0;
+ int bottomMargin = 0;
+ int labelMargin = 2;
+
+ QRect frameRect = groupBox->rect;
+ int verticalAlignment = styleHint(SH_GroupBox_TextLabelVerticalAlignment, groupBox, widget);
+ if (groupBox->text.size()) {
+ topHeight = groupBox->fontMetrics.height();
+ if (verticalAlignment & Qt::AlignVCenter)
+ topMargin = topHeight+5;
+ else if (verticalAlignment & Qt::AlignTop)
+ topMargin = -topHeight+5;
+ }
+ if (subControl == SC_GroupBoxFrame) {
+ frameRect.setTop(topMargin);
+ frameRect.setBottom(frameRect.height() + bottomMargin);
+ rect = frameRect;
+ break;
+ }
+ int frameWidth = 0;
+ if (groupBox->text.size()) {
+ frameWidth = pixelMetric(PM_DefaultFrameWidth, groupBox, widget);
+ rect = frameRect.adjusted(frameWidth, frameWidth + topHeight + labelMargin, -frameWidth, -frameWidth);
+ }
+ else {
+ rect = groupBox->rect;
+ }
+ break;
+ }
+ case SC_GroupBoxCheckBox:
+ // FALL THROUGH
+ case SC_GroupBoxLabel: {
+ QFontMetrics fontMetrics = groupBox->fontMetrics;
+ int h = fontMetrics.height();
+ int textWidth = fontMetrics.size(Qt::TextShowMnemonic, groupBox->text + QLatin1Char(' ')).width();
+ int margX = (groupBox->features & QStyleOptionFrameV2::Flat) ? 0 : 2;
+ int margY = (groupBox->features & QStyleOptionFrameV2::Flat) ? 0 : 2;
+ rect = groupBox->rect.adjusted(margX, margY, -margX, 0);
+ if (groupBox->text.size())
+ rect.setHeight(h);
+ else
+ rect.setHeight(0);
+ int indicatorWidth = pixelMetric(PM_IndicatorWidth, option, widget);
+ int indicatorSpace = pixelMetric(PM_CheckBoxLabelSpacing, option, widget) - 1;
+ bool hasCheckBox = groupBox->subControls & QStyle::SC_GroupBoxCheckBox;
+ int checkBoxSize = hasCheckBox ? (indicatorWidth + indicatorSpace) : 0;
+
+ // Adjusted rect for label + indicatorWidth + indicatorSpace
+ QRect totalRect = alignedRect(groupBox->direction, groupBox->textAlignment,
+ QSize(textWidth + checkBoxSize, h), rect);
+
+ // Adjust totalRect if checkbox is set
+ if (hasCheckBox) {
+ bool ltr = groupBox->direction == Qt::LeftToRight;
+ int left = 2;
+ // Adjust for check box
+ if (subControl == SC_GroupBoxCheckBox) {
+ int indicatorHeight = pixelMetric(PM_IndicatorHeight, option, widget);
+ left = ltr ? totalRect.left() : (totalRect.right() - indicatorWidth);
+ int top = totalRect.top() + (fontMetrics.height() - indicatorHeight) / 2;
+ totalRect.setRect(left, top, indicatorWidth, indicatorHeight);
+ // Adjust for label
+ } else {
+ left = ltr ? (totalRect.left() + checkBoxSize - 2) : totalRect.left();
+ totalRect.setRect(left, totalRect.top(),
+ totalRect.width() - checkBoxSize, totalRect.height());
+ }
+ }
+ if ((subControl== SC_GroupBoxLabel))
+ totalRect.adjust(-2,0,6,0);
+ rect = totalRect;
+ break;
+ }
+ default:
+ break;
+ }
+ }
+ break;
+ }
+#endif // QT_NO_GROUPBOX
+ default:
+ break;
+ }
+ return rect;
+}
+
+QPalette QWindowsMobileStyle::standardPalette() const {
+ QPalette palette (Qt::black,QColor(198, 195, 198), QColor(222, 223, 222 ),
+ QColor(132, 130, 132), QColor(198, 195, 198), Qt::black, Qt::white, Qt::white, QColor(198, 195, 198));
+ palette.setColor(QPalette::Window, QColor(206, 223, 239));
+ palette.setColor(QPalette::Link, QColor(8,77,123)); //Alternate TextColor for labels...
+ palette.setColor(QPalette::Base, Qt::white);
+ palette.setColor(QPalette::Button, QColor(206, 223, 239));
+ palette.setColor(QPalette::Highlight, QColor(49, 146, 214));
+ palette.setColor(QPalette::Light, Qt::white);
+ palette.setColor(QPalette::Text, Qt::black);
+ palette.setColor(QPalette::ButtonText, Qt::black);
+ palette.setColor(QPalette::Midlight, QColor(222, 223, 222 ));
+ palette.setColor(QPalette::Dark, QColor(132, 130, 132));
+ palette.setColor(QPalette::Mid, QColor(189, 190, 189));
+ palette.setColor(QPalette::Shadow, QColor(0, 0, 0));
+ palette.setColor(QPalette::BrightText, QColor(33, 162, 33)); //color for ItemView checked indicator (arrow)
+ return palette;
+}
+
+
+/*! \reimp */
+void QWindowsMobileStyle::polish(QApplication *application) {
+ QWindowsStyle::polish(application);
+}
+
+/*! \reimp */
+void QWindowsMobileStyle::polish(QWidget *widget) {
+
+#ifndef QT_NO_TOOLBAR
+ if (QToolBar *toolBar = qobject_cast<QToolBar*>(widget)) {
+ QPalette pal = toolBar->palette();
+ pal.setColor(QPalette::Background, pal.button().color());
+ toolBar->setPalette(pal);
+ }
+ else
+#endif //QT_NO_TOOLBAR
+
+#ifndef QT_NO_PROPERTIES
+ if (QAbstractButton *pushButton = qobject_cast<QAbstractButton*>(widget)) {
+ QVariant oldFont = widget->property("_q_styleWindowsMobileFont");
+ if (!oldFont.isValid()) {
+ QFont f = pushButton->font();
+ widget->setProperty("_q_styleWindowsMobileFont", f);
+ f.setBold(true);
+ int p = f.pointSize();
+ if (p > 2)
+ f.setPointSize(p-1);
+ pushButton->setFont(f);
+ }
+ }
+#endif
+ QWindowsStyle::polish(widget);
+}
+
+void QWindowsMobileStyle::unpolish(QWidget *widget)
+{
+#ifndef QT_NO_PROPERTIES
+ if (QAbstractButton *pushButton = qobject_cast<QAbstractButton*>(widget)) {
+ QVariant oldFont = widget->property("_q_styleWindowsMobileFont");
+ if (oldFont.isValid()) {
+ widget->setFont(qVariantValue<QFont>(oldFont));
+ widget->setProperty("_q_styleWindowsMobileFont", QVariant());
+ }
+ }
+#endif
+ QWindowsStyle::unpolish(widget);
+}
+
+void QWindowsMobileStyle::unpolish(QApplication *app)
+{
+ QWindowsStyle::unpolish(app);
+}
+
+/*! \reimp */
+void QWindowsMobileStyle::polish(QPalette &palette) {
+ QWindowsStyle::polish(palette);
+}
+
+int QWindowsMobileStyle::pixelMetric(PixelMetric pm, const QStyleOption *opt, const QWidget *widget) const {
+
+ QWindowsMobileStylePrivate *d = const_cast<QWindowsMobileStylePrivate*>(d_func());
+ int ret;
+
+ switch (pm) {
+ case PM_DefaultTopLevelMargin:
+ ret =0;
+ break;
+ case PM_DefaultLayoutSpacing:
+ d->doubleControls ? ret = 8 : ret = 4;
+ break;
+ case PM_HeaderMargin:
+ d->doubleControls ? ret = 2 : ret = 1;
+ break;
+ case PM_DefaultChildMargin:
+ d->doubleControls ? ret = 10 : ret = 5;
+ break;
+ case PM_ToolBarSeparatorExtent:
+ d->doubleControls ? ret = 6 : ret = 3;
+ break;
+ case PM_DefaultFrameWidth:
+ d->doubleControls ? ret = 2 : ret = 1;
+ break;
+ case PM_MenuVMargin:
+ ret = 1;
+ break;
+ case PM_MenuHMargin:
+ ret = 1;
+ break;
+ case PM_MenuButtonIndicator:
+ ret = d->doubleControls ? 24 : 14;
+ break;
+ case PM_ComboBoxFrameWidth:
+ d->doubleControls ? ret = 2 : ret = 1;
+ break;
+ case PM_SpinBoxFrameWidth:
+ d->doubleControls ? ret = 2 : ret = 1;
+ break;
+ case PM_ButtonDefaultIndicator:
+ case PM_ButtonShiftHorizontal:
+ case PM_ButtonShiftVertical:
+ d->doubleControls ? ret = 2 : ret = 1;
+ break;
+#ifndef QT_NO_TABBAR
+ case PM_TabBarTabShiftHorizontal:
+ ret = 0;
+ break;
+ case PM_TabBarTabShiftVertical:
+ ret = 0;
+ break;
+#endif
+ case PM_MaximumDragDistance:
+ ret = 60;
+ break;
+ case PM_TabBarTabVSpace:
+ ret = d->doubleControls ? 12 : 6;
+ break;
+ case PM_TabBarBaseHeight:
+ ret = 0;
+ break;
+ case PM_IndicatorWidth:
+ ret = d->doubleControls ? windowsMobileIndicatorSize * 2 : windowsMobileIndicatorSize;
+ break;
+ case PM_IndicatorHeight:
+ ret = d->doubleControls ? windowsMobileIndicatorSize * 2 : windowsMobileIndicatorSize;
+ break;
+ case PM_ExclusiveIndicatorWidth:
+ ret = d->doubleControls ? windowsMobileExclusiveIndicatorSize * 2 + 4: windowsMobileExclusiveIndicatorSize + 2;
+ break;
+ case PM_ExclusiveIndicatorHeight:
+ ret = d->doubleControls ? windowsMobileExclusiveIndicatorSize * 2 + 4: windowsMobileExclusiveIndicatorSize + 2;
+ break;
+#ifndef QT_NO_SLIDER
+ case PM_SliderLength:
+ ret = d->doubleControls ? 16 : 8;
+ break;
+ case PM_FocusFrameHMargin:
+ ret = d->doubleControls ? 1 : 2;
+ break;
+ case PM_SliderThickness:
+ ret = d->doubleControls ? windowsMobileSliderThickness * 2: windowsMobileSliderThickness;
+ break;
+ case PM_TabBarScrollButtonWidth:
+ ret = d->doubleControls ? 14 * 2 : 18;
+ case PM_CheckBoxLabelSpacing:
+ case PM_RadioButtonLabelSpacing:
+ ret = d->doubleControls ? 6 * 2 : 6;
+ break;
+ break;
+ // Returns the number of pixels to use for the business part of the
+ // slider (i.e., the non-tickmark portion). The remaining space is shared
+ // equally between the tickmark regions.
+ case PM_SliderControlThickness:
+ if (const QStyleOptionSlider *sl = qstyleoption_cast<const QStyleOptionSlider *>(opt)) {
+ int space = (sl->orientation == Qt::Horizontal) ? sl->rect.height() : sl->rect.width();
+ int ticks = sl->tickPosition;
+ int n = 0;
+ if (ticks & QSlider::TicksAbove)
+ ++n;
+ if (ticks & QSlider::TicksBelow)
+ ++n;
+ if (!n) {
+ ret = space;
+ break;
+ }
+ int thick = 8;
+ if (ticks != QSlider::TicksBothSides && ticks != QSlider::NoTicks)
+ thick += pixelMetric(PM_SliderLength, sl, widget) / 4;
+
+ space -= thick;
+ if (space > 0)
+ thick += (space * 2) / (n + 2);
+ ret = thick;
+ } else {
+ ret = 0;
+ }
+ break;
+#endif // QT_NO_SLIDER
+#ifndef QT_NO_MENU
+ case PM_SmallIconSize:
+ d->doubleControls ? ret = windowsMobileIconSize * 2 : ret = windowsMobileIconSize;
+ break;
+ case PM_ButtonMargin:
+ d->doubleControls ? ret = 8 : ret = 4;
+ break;
+ case PM_LargeIconSize:
+ d->doubleControls ? ret = 64 : ret = 32;
+ break;
+ case PM_IconViewIconSize:
+ ret = pixelMetric(PM_LargeIconSize, opt, widget);
+ break;
+ case PM_ToolBarIconSize:
+ d->doubleControls ? ret = 2 * windowsMobileIconSize : ret = windowsMobileIconSize;
+ break;
+ case PM_DockWidgetTitleMargin:
+ ret = 2;
+ break;
+#if defined(Q_WS_WIN)
+#else
+ case PM_DockWidgetFrameWidth:
+ ret = 4;
+ break;
+#endif // Q_WS_WIN
+ break;
+#endif // QT_NO_MENU
+
+ case PM_TitleBarHeight:
+ d->doubleControls ? ret = 42 : ret = 21;
+ break;
+ case PM_ScrollBarSliderMin:
+ d->doubleControls ? ret = 36 : ret = 18;
+ break;
+ case PM_ScrollBarExtent: {
+ //Check if the scrollbar is part of an abstractItemView and set size according
+ if (d->smartphone)
+ ret = 9;
+ else
+ d->doubleControls ? ret = 25 : ret = 13;
+#ifndef QT_NO_SCROLLAREA
+ if (widget)
+ if (QWidget *parent = widget->parentWidget())
+ if (qobject_cast<QAbstractScrollArea *>(parent->parentWidget()))
+ if (d->smartphone)
+ ret = 8;
+ else
+ d->doubleControls ? ret = 24 : ret = 12;
+#endif
+ }
+ break;
+ case PM_SplitterWidth:
+ ret = qMax(4, QApplication::globalStrut().width());
+ break;
+
+#if defined(Q_WS_WIN)
+ case PM_MDIFrameWidth:
+ ret = 1;
+ break;
+#endif
+ case PM_ToolBarExtensionExtent:
+ d->doubleControls ? ret = 32 : ret = 16;
+ break;
+ case PM_ToolBarItemMargin:
+ d->doubleControls ? ret = 2 : ret = 1;
+ break;
+ case PM_ToolBarItemSpacing:
+ d->doubleControls ? ret = 2 : ret = 1;
+ break;
+ case PM_ToolBarHandleExtent:
+ d->doubleControls ? ret = 16 : ret = 8;
+ break;
+ case PM_ButtonIconSize:
+ d->doubleControls ? ret = 32 : ret = 16;
+ break;
+ case PM_TextCursorWidth:
+ ret = 2;
+ break;
+ case PM_TabBar_ScrollButtonOverlap:
+ ret = 0;
+ break;
+ default:
+ ret = QWindowsStyle::pixelMetric(pm, opt, widget);
+ break;
+ }
+ return ret;
+}
+
+int QWindowsMobileStyle::styleHint(StyleHint hint, const QStyleOption *opt, const QWidget *widget,
+ QStyleHintReturn *returnData) const {
+
+ int ret;
+ switch (hint) {
+ case SH_Menu_MouseTracking:
+ case SH_ComboBox_ListMouseTracking:
+ case SH_EtchDisabledText:
+ ret = 0;
+ break;
+ case SH_DitherDisabledText:
+ ret = 0;
+ break;
+ case SH_ItemView_ShowDecorationSelected:
+ ret = 0;
+ break;
+#ifndef QT_NO_TABWIDGET
+ case SH_TabWidget_DefaultTabPosition:
+ ret = QTabWidget::South;
+ break;
+#endif
+ case SH_ToolBar_Movable:
+ ret = false;
+ break;
+ case SH_ScrollBar_ContextMenu:
+ ret = false;
+ break;
+ default:
+ ret = QWindowsStyle::styleHint(hint, opt, widget, returnData);
+ break;
+ }
+ return ret;
+}
+
+QPixmap QWindowsMobileStyle::standardPixmap(StandardPixmap sp, const QStyleOption *option,
+ const QWidget *widget) const {
+
+ QWindowsMobileStylePrivate *d = const_cast<QWindowsMobileStylePrivate*>(d_func());
+ switch (sp) {
+#ifndef QT_NO_IMAGEFORMAT_XPM
+ case SP_ToolBarHorizontalExtensionButton: {
+ QPixmap pixmap = QCommonStyle::standardPixmap(sp, option, widget);
+ if (d->doubleControls)
+ return pixmap.scaledToHeight(pixmap.height() * 2);
+ else
+ return pixmap;
+ }
+ case SP_TitleBarMaxButton:
+ case SP_TitleBarCloseButton:
+ case SP_TitleBarNormalButton:
+ case SP_TitleBarMinButton: {
+ QImage image;
+ switch (sp) {
+ case SP_TitleBarMaxButton:
+ image = d->imageMaximize;
+ break;
+ case SP_TitleBarCloseButton:
+ image = d->imageClose;
+ break;
+ case SP_TitleBarNormalButton:
+ image = d->imageNormalize;
+ break;
+ case SP_TitleBarMinButton:
+ image = d->imageMinimize;
+ break;
+ default:
+ break;
+ }
+ if (option) {
+ image.setColor(0, option->palette.shadow().color().rgba());
+ image.setColor(1, option->palette.highlight().color().rgba());
+ image.setColor(2, option->palette.highlight().color().lighter(150).rgba());
+ image.setColor(3, option->palette.highlightedText().color().rgba());
+ }
+
+ return QPixmap::fromImage(image);
+ }
+
+#endif
+ default:
+ return QWindowsStyle::standardPixmap(sp, option, widget);
+ }
+}
+
+QPixmap QWindowsMobileStyle::generatedIconPixmap(QIcon::Mode iconMode, const QPixmap &pixmap,
+ const QStyleOption *option) const {
+
+ switch (iconMode) {
+ case QIcon::Selected: {
+ QImage img = pixmap.toImage().convertToFormat(QImage::Format_ARGB32);
+ int imgh = img.height();
+ int imgw = img.width();
+ for (int y = 0; y < imgh; y += 2) {
+ for (int x = 0; x < imgw; x += 2) {
+ QColor c = option->palette.highlight().color().rgb();
+ c.setAlpha( qAlpha(img.pixel(x, y)));
+ QRgb pixel = c.rgba();
+ img.setPixel(x, y, pixel);
+ }
+ }
+ return QPixmap::fromImage(img);
+ }
+ default:
+ break;
+ }
+ return QWindowsStyle::generatedIconPixmap(iconMode, pixmap, option);
+}
+
+
+bool QWindowsMobileStyle::doubleControls() const {
+
+ QWindowsMobileStylePrivate *d = const_cast<QWindowsMobileStylePrivate*>(d_func());
+
+ return d->doubleControls;
+}
+
+void QWindowsMobileStyle::setDoubleControls(bool doubleControls) {
+
+ QWindowsMobileStylePrivate *d = const_cast<QWindowsMobileStylePrivate*>(d_func());
+
+ d->doubleControls = doubleControls;
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_STYLE_WINDOWSMOBILE
+
diff --git a/src/gui/styles/qwindowsmobilestyle.h b/src/gui/styles/qwindowsmobilestyle.h
new file mode 100644
index 0000000000..f23ecc9928
--- /dev/null
+++ b/src/gui/styles/qwindowsmobilestyle.h
@@ -0,0 +1,116 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QWINDOWSMOBILESTYLE_H
+#define QWINDOWSMOBILESTYLE_H
+
+#include <QtGui/qwindowsstyle.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+#if !defined(QT_NO_STYLE_WINDOWSMOBILE)
+
+class QWindowsMobileStylePrivate;
+
+class Q_GUI_EXPORT QWindowsMobileStyle : public QWindowsStyle
+{
+ Q_OBJECT
+public:
+ QWindowsMobileStyle();
+
+ void drawPrimitive(PrimitiveElement element, const QStyleOption *option,
+ QPainter *painter, const QWidget *widget = 0) const;
+
+ void drawControl(ControlElement element, const QStyleOption *option,
+ QPainter *painter, const QWidget *widget) const;
+
+ void drawComplexControl(ComplexControl control, const QStyleOptionComplex *option,
+ QPainter *painter, const QWidget *widget) const;
+
+ QSize sizeFromContents(ContentsType type, const QStyleOption *option,
+ const QSize &size, const QWidget *widget) const;
+
+ QRect subElementRect(SubElement element, const QStyleOption *option, const QWidget *widget) const;
+
+ QRect subControlRect(ComplexControl cc, const QStyleOptionComplex *opt,
+ SubControl sc, const QWidget *widget) const;
+
+ QPixmap generatedIconPixmap(QIcon::Mode iconMode, const QPixmap &pixmap,
+ const QStyleOption *option) const;
+
+ QPixmap standardPixmap(StandardPixmap sp, const QStyleOption *option,
+ const QWidget *widget) const;
+
+ int pixelMetric(PixelMetric metric, const QStyleOption *option = 0, const QWidget *widget = 0) const;
+
+ int styleHint(StyleHint hint, const QStyleOption *opt = 0, const QWidget *widget = 0,
+ QStyleHintReturn *returnData = 0) const;
+
+ void polish(QApplication*);
+ void unpolish(QApplication*);
+ void polish(QWidget *widget);
+ void unpolish(QWidget *widget);
+ void polish(QPalette &);
+
+ QPalette standardPalette() const;
+
+ bool doubleControls() const;
+
+ void setDoubleControls(bool);
+
+protected:
+ QWindowsMobileStyle(QWindowsMobileStylePrivate &dd);
+
+private:
+ Q_DECLARE_PRIVATE(QWindowsMobileStyle)
+};
+
+#endif // QT_NO_STYLE_WINDOWSMOBILE
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif //QWINDOWSMOBILESTYLE_H
diff --git a/src/gui/styles/qwindowsmobilestyle_p.h b/src/gui/styles/qwindowsmobilestyle_p.h
new file mode 100644
index 0000000000..83129cf58b
--- /dev/null
+++ b/src/gui/styles/qwindowsmobilestyle_p.h
@@ -0,0 +1,93 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QWINDOWSMOBILESTYLE_P_H
+#define QWINDOWSMOBILESTYLE_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of qapplication_*.cpp, qwidget*.cpp and qfiledialog.cpp. This header
+// file may change from version to version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qwindowsmobilestyle.h"
+#include "qwindowsstyle_p.h"
+
+QT_BEGIN_NAMESPACE
+
+#ifndef QT_NO_STYLE_WINDOWSMOBILE
+
+class QWindowsMobileStylePrivate : public QWindowsStylePrivate
+{
+ Q_DECLARE_PUBLIC(QWindowsMobileStyle)
+public:
+ QWindowsMobileStylePrivate();
+ bool doubleControls;
+ bool smartphone;
+
+ QImage imageRadioButton;
+ QImage imageRadioButtonChecked;
+ QImage imageRadioButtonHighlighted;
+ QImage imageChecked;
+ QImage imageCheckedBold;
+ QImage imageArrowDown;
+ QImage imageArrowUp;
+ QImage imageArrowLeft;
+ QImage imageArrowRight;
+ QImage imageArrowDownBig;
+ QImage imageArrowUpBig;
+ QImage imageArrowLeftBig;
+ QImage imageArrowRightBig;
+ QImage imageClose;
+ QImage imageMaximize;
+ QImage imageNormalize;
+ QImage imageMinimize;
+};
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_STYLE_WINDOWSMOBILE
+#endif //QWINDOWSMOBILESTYLE_P_H
diff --git a/src/gui/styles/qwindowsstyle.cpp b/src/gui/styles/qwindowsstyle.cpp
new file mode 100644
index 0000000000..00c3f99cc2
--- /dev/null
+++ b/src/gui/styles/qwindowsstyle.cpp
@@ -0,0 +1,3408 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qwindowsstyle.h"
+#include "qwindowsstyle_p.h"
+#include <private/qpixmapdata_p.h>
+
+#if !defined(QT_NO_STYLE_WINDOWS) || defined(QT_PLUGIN)
+
+#include "qlibrary.h"
+#include "qapplication.h"
+#include "qbitmap.h"
+#include "qdrawutil.h" // for now
+#include "qevent.h"
+#include "qmenu.h"
+#include "qmenubar.h"
+#include <private/qmenubar_p.h>
+#include "qpaintengine.h"
+#include "qpainter.h"
+#include "qprogressbar.h"
+#include "qrubberband.h"
+#include "qstyleoption.h"
+#include "qtabbar.h"
+#include "qwidget.h"
+#include "qdebug.h"
+#include "qmainwindow.h"
+#include "qfile.h"
+#include "qtextstream.h"
+#include "qpixmapcache.h"
+#include "qwizard.h"
+#include "qlistview.h"
+
+#ifdef Q_WS_X11
+#include "qfileinfo.h"
+#include "qdir.h"
+#include <private/qt_x11_p.h>
+#endif
+
+QT_BEGIN_NAMESPACE
+
+#if defined(Q_WS_WIN)
+
+QT_BEGIN_INCLUDE_NAMESPACE
+#include "qt_windows.h"
+QT_END_INCLUDE_NAMESPACE
+# ifndef COLOR_GRADIENTACTIVECAPTION
+# define COLOR_GRADIENTACTIVECAPTION 27
+# endif
+# ifndef COLOR_GRADIENTINACTIVECAPTION
+# define COLOR_GRADIENTINACTIVECAPTION 28
+# endif
+
+
+typedef struct
+{
+ DWORD cbSize;
+ HICON hIcon;
+ int iSysImageIndex;
+ int iIcon;
+ WCHAR szPath[MAX_PATH];
+} QSHSTOCKICONINFO;
+
+#define _SHGFI_SMALLICON 0x000000001
+#define _SHGFI_LARGEICON 0x000000000
+#define _SHGFI_ICON 0x000000100
+#define _SIID_SHIELD 77
+
+typedef HRESULT (WINAPI *PtrSHGetStockIconInfo)(int siid, int uFlags, QSHSTOCKICONINFO *psii);
+static PtrSHGetStockIconInfo pSHGetStockIconInfo = 0;
+
+#endif //Q_WS_WIN
+
+QT_BEGIN_INCLUDE_NAMESPACE
+#include <limits.h>
+QT_END_INCLUDE_NAMESPACE
+
+static const int windowsItemFrame = 2; // menu item frame width
+static const int windowsSepHeight = 9; // separator item height
+static const int windowsItemHMargin = 3; // menu item hor text margin
+static const int windowsItemVMargin = 2; // menu item ver text margin
+static const int windowsArrowHMargin = 6; // arrow horizontal margin
+static const int windowsTabSpacing = 12; // space between text and tab
+static const int windowsCheckMarkHMargin = 2; // horiz. margins of check mark
+static const int windowsRightBorder = 15; // right border on windows
+static const int windowsCheckMarkWidth = 12; // checkmarks width on windows
+
+static bool use2000style = true;
+
+enum QSliderDirection { SlUp, SlDown, SlLeft, SlRight };
+
+/*
+ \internal
+*/
+QWindowsStylePrivate::QWindowsStylePrivate()
+ : alt_down(false), menuBarTimer(0), animationFps(10), animateTimer(0), animateStep(0)
+{
+#if defined(Q_WS_WIN) && !defined(Q_OS_WINCE)
+ if ((QSysInfo::WindowsVersion >= QSysInfo::WV_VISTA
+ && QSysInfo::WindowsVersion < QSysInfo::WV_NT_based)) {
+ QLibrary shellLib(QLatin1String("shell32"));
+ pSHGetStockIconInfo = (PtrSHGetStockIconInfo)shellLib.resolve("SHGetStockIconInfo");
+ }
+#endif
+}
+
+// Returns true if the toplevel parent of \a widget has seen the Alt-key
+bool QWindowsStylePrivate::hasSeenAlt(const QWidget *widget) const
+{
+ widget = widget->window();
+ return seenAlt.contains(widget);
+}
+
+/*!
+ \reimp
+*/
+void QWindowsStyle::timerEvent(QTimerEvent *event)
+{
+#ifndef QT_NO_PROGRESSBAR
+ Q_D(QWindowsStyle);
+ if (event->timerId() == d->animateTimer) {
+ Q_ASSERT(d->animationFps> 0);
+ d->animateStep = d->startTime.elapsed() / (1000 / d->animationFps);
+ foreach (QProgressBar *bar, d->bars) {
+ if ((bar->minimum() == 0 && bar->maximum() == 0))
+ bar->update();
+ }
+ }
+#endif // QT_NO_PROGRESSBAR
+ event->ignore();
+}
+
+/*!
+ \reimp
+*/
+bool QWindowsStyle::eventFilter(QObject *o, QEvent *e)
+{
+ // Records Alt- and Focus events
+ if (!o->isWidgetType())
+ return QObject::eventFilter(o, e);
+
+ QWidget *widget = qobject_cast<QWidget*>(o);
+ Q_D(QWindowsStyle);
+ switch(e->type()) {
+ case QEvent::KeyPress:
+ if (static_cast<QKeyEvent *>(e)->key() == Qt::Key_Alt) {
+ widget = widget->window();
+
+ // Alt has been pressed - find all widgets that care
+ QList<QWidget *> l = qFindChildren<QWidget *>(widget);
+ for (int pos=0 ; pos < l.size() ; ++pos) {
+ QWidget *w = l.at(pos);
+ if (w->isWindow() || !w->isVisible() ||
+ w->style()->styleHint(SH_UnderlineShortcut, 0, w))
+ l.removeAt(pos);
+ }
+ // Update states before repainting
+ d->seenAlt.append(widget);
+ d->alt_down = true;
+
+ // Repaint all relevant widgets
+ for (int pos = 0; pos < l.size(); ++pos)
+ l.at(pos)->update();
+ }
+ break;
+ case QEvent::KeyRelease:
+ if (static_cast<QKeyEvent*>(e)->key() == Qt::Key_Alt) {
+ widget = widget->window();
+
+ // Update state and repaint the menu bars.
+ d->alt_down = false;
+#ifndef QT_NO_MENUBAR
+ QList<QMenuBar *> l = qFindChildren<QMenuBar *>(widget);
+ for (int i = 0; i < l.size(); ++i)
+ l.at(i)->update();
+#endif
+ }
+ break;
+ case QEvent::Close:
+ // Reset widget when closing
+ d->seenAlt.removeAll(widget);
+ d->seenAlt.removeAll(widget->window());
+ break;
+#ifndef QT_NO_PROGRESSBAR
+ case QEvent::StyleChange:
+ case QEvent::Show:
+ if (QProgressBar *bar = qobject_cast<QProgressBar *>(o)) {
+ d->bars << bar;
+ if (d->bars.size() == 1) {
+ Q_ASSERT(d->animationFps> 0);
+ d->animateTimer = startTimer(1000 / d->animationFps);
+ }
+ }
+ break;
+ case QEvent::Destroy:
+ case QEvent::Hide:
+ // reinterpret_cast because there is no type info when getting
+ // the destroy event. We know that it is a QProgressBar.
+ if (QProgressBar *bar = reinterpret_cast<QProgressBar *>(o)) {
+ d->bars.removeAll(bar);
+ if (d->bars.isEmpty() && d->animateTimer) {
+ killTimer(d->animateTimer);
+ d->animateTimer = 0;
+ }
+ }
+ break;
+#endif // QT_NO_PROGRESSBAR
+ default:
+ break;
+ }
+ return QCommonStyle::eventFilter(o, e);
+}
+
+/*!
+ \class QWindowsStyle
+ \brief The QWindowsStyle class provides a Microsoft Windows-like look and feel.
+
+ \ingroup appearance
+
+ This style is Qt's default GUI style on Windows.
+
+ \img qwindowsstyle.png
+ \sa QWindowsXPStyle, QMacStyle, QPlastiqueStyle, QCDEStyle, QMotifStyle
+*/
+
+/*!
+ Constructs a QWindowsStyle object.
+*/
+QWindowsStyle::QWindowsStyle() : QCommonStyle(*new QWindowsStylePrivate)
+{
+#if defined(Q_OS_WIN32)
+ use2000style = QSysInfo::WindowsVersion != QSysInfo::WV_NT && QSysInfo::WindowsVersion != QSysInfo::WV_95;
+#endif
+}
+
+/*!
+ \internal
+
+ Constructs a QWindowsStyle object.
+*/
+QWindowsStyle::QWindowsStyle(QWindowsStylePrivate &dd) : QCommonStyle(dd)
+{
+#if defined(Q_OS_WIN32)
+ use2000style = QSysInfo::WindowsVersion != QSysInfo::WV_NT && QSysInfo::WindowsVersion != QSysInfo::WV_95;
+#endif
+}
+
+
+/*! Destroys the QWindowsStyle object. */
+QWindowsStyle::~QWindowsStyle()
+{
+}
+
+#ifdef Q_WS_WIN
+static inline QRgb colorref2qrgb(COLORREF col)
+{
+ return qRgb(GetRValue(col), GetGValue(col), GetBValue(col));
+}
+#endif
+
+/*! \reimp */
+void QWindowsStyle::polish(QApplication *app)
+{
+ QCommonStyle::polish(app);
+ QWindowsStylePrivate *d = const_cast<QWindowsStylePrivate*>(d_func());
+ // We only need the overhead when shortcuts are sometimes hidden
+ if (!styleHint(SH_UnderlineShortcut, 0) && app)
+ app->installEventFilter(this);
+
+ d->activeCaptionColor = app->palette().highlight().color();
+ d->activeGradientCaptionColor = app->palette().highlight() .color();
+ d->inactiveCaptionColor = app->palette().dark().color();
+ d->inactiveGradientCaptionColor = app->palette().dark().color();
+ d->inactiveCaptionText = app->palette().background().color();
+
+#if defined(Q_WS_WIN) //fetch native title bar colors
+ if(app->desktopSettingsAware()){
+ DWORD activeCaption = GetSysColor(COLOR_ACTIVECAPTION);
+ DWORD gradientActiveCaption = GetSysColor(COLOR_GRADIENTACTIVECAPTION);
+ DWORD inactiveCaption = GetSysColor(COLOR_INACTIVECAPTION);
+ DWORD gradientInactiveCaption = GetSysColor(COLOR_GRADIENTINACTIVECAPTION);
+ DWORD inactiveCaptionText = GetSysColor(COLOR_INACTIVECAPTIONTEXT);
+ d->activeCaptionColor = colorref2qrgb(activeCaption);
+ d->activeGradientCaptionColor = colorref2qrgb(gradientActiveCaption);
+ d->inactiveCaptionColor = colorref2qrgb(inactiveCaption);
+ d->inactiveGradientCaptionColor = colorref2qrgb(gradientInactiveCaption);
+ d->inactiveCaptionText = colorref2qrgb(inactiveCaptionText);
+ }
+#endif
+}
+
+/*! \reimp */
+void QWindowsStyle::unpolish(QApplication *app)
+{
+ QCommonStyle::unpolish(app);
+ app->removeEventFilter(this);
+}
+
+/*! \reimp */
+void QWindowsStyle::polish(QWidget *widget)
+{
+ QCommonStyle::polish(widget);
+#ifndef QT_NO_PROGRESSBAR
+ if (qobject_cast<QProgressBar *>(widget))
+ widget->installEventFilter(this);
+#endif
+}
+
+/*! \reimp */
+void QWindowsStyle::unpolish(QWidget *widget)
+{
+ QCommonStyle::unpolish(widget);
+#ifndef QT_NO_PROGRESSBAR
+ if (QProgressBar *bar=qobject_cast<QProgressBar *>(widget)) {
+ Q_D(QWindowsStyle);
+ widget->removeEventFilter(this);
+ d->bars.removeAll(bar);
+ }
+#endif
+}
+
+/*!
+ \reimp
+*/
+void QWindowsStyle::polish(QPalette &pal)
+{
+ QCommonStyle::polish(pal);
+}
+
+/*!
+ \reimp
+*/
+int QWindowsStyle::pixelMetric(PixelMetric pm, const QStyleOption *opt, const QWidget *widget) const
+{
+ int ret;
+
+ switch (pm) {
+ case PM_ButtonDefaultIndicator:
+ case PM_ButtonShiftHorizontal:
+ case PM_ButtonShiftVertical:
+ case PM_MenuHMargin:
+ case PM_MenuVMargin:
+ ret = 1;
+ break;
+#ifndef QT_NO_TABBAR
+ case PM_TabBarTabShiftHorizontal:
+ ret = 0;
+ break;
+ case PM_TabBarTabShiftVertical:
+ ret = 2;
+ break;
+#endif
+ case PM_MaximumDragDistance:
+#if defined(Q_WS_WIN)
+ {
+ HDC hdcScreen = GetDC(0);
+ int dpi = GetDeviceCaps(hdcScreen, LOGPIXELSX);
+ ReleaseDC(0, hdcScreen);
+ ret = (int)(dpi * 1.375);
+ }
+#else
+ ret = 60;
+#endif
+ break;
+
+#ifndef QT_NO_SLIDER
+ case PM_SliderLength:
+ ret = 11;
+ break;
+
+ // Returns the number of pixels to use for the business part of the
+ // slider (i.e., the non-tickmark portion). The remaining space is shared
+ // equally between the tickmark regions.
+ case PM_SliderControlThickness:
+ if (const QStyleOptionSlider *sl = qstyleoption_cast<const QStyleOptionSlider *>(opt)) {
+ int space = (sl->orientation == Qt::Horizontal) ? sl->rect.height() : sl->rect.width();
+ int ticks = sl->tickPosition;
+ int n = 0;
+ if (ticks & QSlider::TicksAbove)
+ ++n;
+ if (ticks & QSlider::TicksBelow)
+ ++n;
+ if (!n) {
+ ret = space;
+ break;
+ }
+
+ int thick = 6; // Magic constant to get 5 + 16 + 5
+ if (ticks != QSlider::TicksBothSides && ticks != QSlider::NoTicks)
+ thick += pixelMetric(PM_SliderLength, sl, widget) / 4;
+
+ space -= thick;
+ if (space > 0)
+ thick += (space * 2) / (n + 2);
+ ret = thick;
+ } else {
+ ret = 0;
+ }
+ break;
+#endif // QT_NO_SLIDER
+
+#ifndef QT_NO_MENU
+ case PM_MenuBarHMargin:
+ ret = 0;
+ break;
+
+ case PM_MenuBarVMargin:
+ ret = 0;
+ break;
+
+ case PM_MenuBarPanelWidth:
+ ret = 0;
+ break;
+
+ case PM_SmallIconSize:
+ ret = 16;
+ break;
+
+ case PM_LargeIconSize:
+ ret = 32;
+ break;
+
+ case PM_IconViewIconSize:
+ ret = pixelMetric(PM_LargeIconSize, opt, widget);
+ break;
+
+ case PM_ToolBarIconSize:
+ ret = 24;
+ break;
+ case PM_DockWidgetTitleMargin:
+ ret = 2;
+ break;
+ case PM_DockWidgetTitleBarButtonMargin:
+ ret = 4;
+ break;
+#if defined(Q_WS_WIN)
+ case PM_DockWidgetFrameWidth:
+#if defined(Q_OS_WINCE)
+ ret = GetSystemMetrics(SM_CXDLGFRAME);
+#else
+ ret = GetSystemMetrics(SM_CXFRAME);
+#endif
+ break;
+#else
+ case PM_DockWidgetFrameWidth:
+ ret = 4;
+ break;
+#endif // Q_WS_WIN
+ break;
+
+#endif // QT_NO_MENU
+
+
+#if defined(Q_WS_WIN)
+ case PM_TitleBarHeight:
+#ifdef QT3_SUPPORT
+ // qt3 dockwindow height should be equal to tool windows
+ if (widget && widget->inherits("Q3DockWindowTitleBar")) {
+ ret = GetSystemMetrics(SM_CYSMCAPTION) - 1;
+ } else
+#endif
+ if (widget && (widget->windowType() == Qt::Tool)) {
+ // MS always use one less than they say
+#if defined(Q_OS_WINCE)
+ ret = GetSystemMetrics(SM_CYCAPTION) - 1;
+#else
+ ret = GetSystemMetrics(SM_CYSMCAPTION) - 1;
+#endif
+ } else {
+ ret = GetSystemMetrics(SM_CYCAPTION) - 1;
+ }
+
+ break;
+
+ case PM_ScrollBarExtent:
+ {
+#ifndef Q_OS_WINCE
+ NONCLIENTMETRICS ncm;
+ ncm.cbSize = sizeof(NONCLIENTMETRICS);
+ if (SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(NONCLIENTMETRICS), &ncm, 0))
+ ret = qMax(ncm.iScrollHeight, ncm.iScrollWidth);
+ else
+#endif
+ ret = QCommonStyle::pixelMetric(pm, opt, widget);
+ }
+ break;
+#endif // Q_WS_WIN
+
+ case PM_SplitterWidth:
+ ret = qMax(4, QApplication::globalStrut().width());
+ break;
+
+#if defined(Q_WS_WIN)
+ case PM_MdiSubWindowFrameWidth:
+#if defined(Q_OS_WINCE)
+ ret = GetSystemMetrics(SM_CYDLGFRAME);
+#else
+ ret = GetSystemMetrics(SM_CYFRAME);
+#endif
+ break;
+ case PM_TextCursorWidth: {
+ DWORD caretWidth = 1;
+#if defined(SPI_GETCARETWIDTH)
+ SystemParametersInfo(SPI_GETCARETWIDTH, 0, &caretWidth, 0);
+#endif
+ ret = (int)caretWidth;
+ break; }
+#endif
+ case PM_ToolBarItemMargin:
+ ret = 1;
+ break;
+ case PM_ToolBarItemSpacing:
+ ret = 0;
+ break;
+ case PM_ToolBarHandleExtent:
+ ret = 10;
+ break;
+ default:
+ ret = QCommonStyle::pixelMetric(pm, opt, widget);
+ break;
+ }
+
+ return ret;
+}
+
+#ifndef QT_NO_IMAGEFORMAT_XPM
+
+/* XPM */
+static const char * const qt_menu_xpm[] = {
+"16 16 72 1",
+" c None",
+". c #65AF36",
+"+ c #66B036",
+"@ c #77B94C",
+"# c #A7D28C",
+"$ c #BADBA4",
+"% c #A4D088",
+"& c #72B646",
+"* c #9ACB7A",
+"= c #7FBD56",
+"- c #85C05F",
+"; c #F4F9F0",
+"> c #FFFFFF",
+", c #E5F1DC",
+"' c #ECF5E7",
+") c #7ABA50",
+"! c #83BF5C",
+"~ c #AED595",
+"{ c #D7EACA",
+"] c #A9D28D",
+"^ c #BCDDA8",
+"/ c #C4E0B1",
+"( c #81BE59",
+"_ c #D0E7C2",
+": c #D4E9C6",
+"< c #6FB542",
+"[ c #6EB440",
+"} c #88C162",
+"| c #98CA78",
+"1 c #F4F9F1",
+"2 c #8FC56C",
+"3 c #F1F8EC",
+"4 c #E8F3E1",
+"5 c #D4E9C7",
+"6 c #74B748",
+"7 c #80BE59",
+"8 c #73B747",
+"9 c #6DB43F",
+"0 c #CBE4BA",
+"a c #80BD58",
+"b c #6DB33F",
+"c c #FEFFFE",
+"d c #68B138",
+"e c #F9FCF7",
+"f c #91C66F",
+"g c #E8F3E0",
+"h c #DCEDD0",
+"i c #91C66E",
+"j c #A3CF86",
+"k c #C9E3B8",
+"l c #B0D697",
+"m c #E3F0DA",
+"n c #95C873",
+"o c #E6F2DE",
+"p c #9ECD80",
+"q c #BEDEAA",
+"r c #C7E2B6",
+"s c #79BA4F",
+"t c #6EB441",
+"u c #BCDCA7",
+"v c #FAFCF8",
+"w c #F6FAF3",
+"x c #84BF5D",
+"y c #EDF6E7",
+"z c #FAFDF9",
+"A c #88C263",
+"B c #98CA77",
+"C c #CDE5BE",
+"D c #67B037",
+"E c #D9EBCD",
+"F c #6AB23C",
+"G c #77B94D",
+" .++++++++++++++",
+".+++++++++++++++",
+"+++@#$%&+++*=+++",
+"++-;>,>')+!>~+++",
+"++{>]+^>/(_>:~<+",
+"+[>>}+|>123>456+",
+"+7>>8+->>90>~+++",
+"+a>>b+a>c[0>~+++",
+"+de>=+f>g+0>~+++",
+"++h>i+j>k+0>~+++",
+"++l>mno>p+q>rst+",
+"++duv>wl++xy>zA+",
+"++++B>Cb++++&D++",
+"+++++0zE++++++++",
+"++++++FG+++++++.",
+"++++++++++++++. "};
+
+static const char * const qt_close_xpm[] = {
+"10 10 2 1",
+"# c #000000",
+". c None",
+"..........",
+".##....##.",
+"..##..##..",
+"...####...",
+"....##....",
+"...####...",
+"..##..##..",
+".##....##.",
+"..........",
+".........."};
+
+static const char * const qt_maximize_xpm[]={
+"10 10 2 1",
+"# c #000000",
+". c None",
+"#########.",
+"#########.",
+"#.......#.",
+"#.......#.",
+"#.......#.",
+"#.......#.",
+"#.......#.",
+"#.......#.",
+"#########.",
+".........."};
+
+static const char * const qt_minimize_xpm[] = {
+"10 10 2 1",
+"# c #000000",
+". c None",
+"..........",
+"..........",
+"..........",
+"..........",
+"..........",
+"..........",
+"..........",
+".#######..",
+".#######..",
+".........."};
+
+static const char * const qt_normalizeup_xpm[] = {
+"10 10 2 1",
+"# c #000000",
+". c None",
+"...######.",
+"...######.",
+"...#....#.",
+".######.#.",
+".######.#.",
+".#....###.",
+".#....#...",
+".#....#...",
+".######...",
+".........."};
+
+static const char * const qt_help_xpm[] = {
+"10 10 2 1",
+". c None",
+"# c #000000",
+"..........",
+"..######..",
+".##....##.",
+"......##..",
+".....##...",
+"....##....",
+"....##....",
+"..........",
+"....##....",
+".........."};
+
+static const char * const qt_shade_xpm[] = {
+"10 10 2 1",
+"# c #000000",
+". c None",
+"..........",
+"..........",
+"..........",
+"..........",
+"....#.....",
+"...###....",
+"..#####...",
+".#######..",
+"..........",
+".........."};
+
+static const char * const qt_unshade_xpm[] = {
+"10 10 2 1",
+"# c #000000",
+". c None",
+"..........",
+"..........",
+"..........",
+".#######..",
+"..#####...",
+"...###....",
+"....#.....",
+"..........",
+"..........",
+".........."};
+
+static const char * dock_widget_close_xpm[] = {
+"8 8 2 1",
+"# c #000000",
+". c None",
+"........",
+".##..##.",
+"..####..",
+"...##...",
+"..####..",
+".##..##.",
+"........",
+"........"};
+
+/* XPM */
+static const char * const information_xpm[]={
+"32 32 5 1",
+". c None",
+"c c #000000",
+"* c #999999",
+"a c #ffffff",
+"b c #0000ff",
+"...........********.............",
+"........***aaaaaaaa***..........",
+"......**aaaaaaaaaaaaaa**........",
+".....*aaaaaaaaaaaaaaaaaa*.......",
+"....*aaaaaaaabbbbaaaaaaaac......",
+"...*aaaaaaaabbbbbbaaaaaaaac.....",
+"..*aaaaaaaaabbbbbbaaaaaaaaac....",
+".*aaaaaaaaaaabbbbaaaaaaaaaaac...",
+".*aaaaaaaaaaaaaaaaaaaaaaaaaac*..",
+"*aaaaaaaaaaaaaaaaaaaaaaaaaaaac*.",
+"*aaaaaaaaaabbbbbbbaaaaaaaaaaac*.",
+"*aaaaaaaaaaaabbbbbaaaaaaaaaaac**",
+"*aaaaaaaaaaaabbbbbaaaaaaaaaaac**",
+"*aaaaaaaaaaaabbbbbaaaaaaaaaaac**",
+"*aaaaaaaaaaaabbbbbaaaaaaaaaaac**",
+"*aaaaaaaaaaaabbbbbaaaaaaaaaaac**",
+".*aaaaaaaaaaabbbbbaaaaaaaaaac***",
+".*aaaaaaaaaaabbbbbaaaaaaaaaac***",
+"..*aaaaaaaaaabbbbbaaaaaaaaac***.",
+"...caaaaaaabbbbbbbbbaaaaaac****.",
+"....caaaaaaaaaaaaaaaaaaaac****..",
+".....caaaaaaaaaaaaaaaaaac****...",
+"......ccaaaaaaaaaaaaaacc****....",
+".......*cccaaaaaaaaccc*****.....",
+"........***cccaaaac*******......",
+"..........****caaac*****........",
+".............*caaac**...........",
+"...............caac**...........",
+"................cac**...........",
+".................cc**...........",
+"..................***...........",
+"...................**..........."};
+/* XPM */
+static const char* const warning_xpm[]={
+"32 32 4 1",
+". c None",
+"a c #ffff00",
+"* c #000000",
+"b c #999999",
+".............***................",
+"............*aaa*...............",
+"...........*aaaaa*b.............",
+"...........*aaaaa*bb............",
+"..........*aaaaaaa*bb...........",
+"..........*aaaaaaa*bb...........",
+".........*aaaaaaaaa*bb..........",
+".........*aaaaaaaaa*bb..........",
+"........*aaaaaaaaaaa*bb.........",
+"........*aaaa***aaaa*bb.........",
+".......*aaaa*****aaaa*bb........",
+".......*aaaa*****aaaa*bb........",
+"......*aaaaa*****aaaaa*bb.......",
+"......*aaaaa*****aaaaa*bb.......",
+".....*aaaaaa*****aaaaaa*bb......",
+".....*aaaaaa*****aaaaaa*bb......",
+"....*aaaaaaaa***aaaaaaaa*bb.....",
+"....*aaaaaaaa***aaaaaaaa*bb.....",
+"...*aaaaaaaaa***aaaaaaaaa*bb....",
+"...*aaaaaaaaaa*aaaaaaaaaa*bb....",
+"..*aaaaaaaaaaa*aaaaaaaaaaa*bb...",
+"..*aaaaaaaaaaaaaaaaaaaaaaa*bb...",
+".*aaaaaaaaaaaa**aaaaaaaaaaa*bb..",
+".*aaaaaaaaaaa****aaaaaaaaaa*bb..",
+"*aaaaaaaaaaaa****aaaaaaaaaaa*bb.",
+"*aaaaaaaaaaaaa**aaaaaaaaaaaa*bb.",
+"*aaaaaaaaaaaaaaaaaaaaaaaaaaa*bbb",
+"*aaaaaaaaaaaaaaaaaaaaaaaaaaa*bbb",
+".*aaaaaaaaaaaaaaaaaaaaaaaaa*bbbb",
+"..*************************bbbbb",
+"....bbbbbbbbbbbbbbbbbbbbbbbbbbb.",
+".....bbbbbbbbbbbbbbbbbbbbbbbbb.."};
+/* XPM */
+static const char* const critical_xpm[]={
+"32 32 4 1",
+". c None",
+"a c #999999",
+"* c #ff0000",
+"b c #ffffff",
+"...........********.............",
+".........************...........",
+".......****************.........",
+"......******************........",
+".....********************a......",
+"....**********************a.....",
+"...************************a....",
+"..*******b**********b*******a...",
+"..******bbb********bbb******a...",
+".******bbbbb******bbbbb******a..",
+".*******bbbbb****bbbbb*******a..",
+"*********bbbbb**bbbbb*********a.",
+"**********bbbbbbbbbb**********a.",
+"***********bbbbbbbb***********aa",
+"************bbbbbb************aa",
+"************bbbbbb************aa",
+"***********bbbbbbbb***********aa",
+"**********bbbbbbbbbb**********aa",
+"*********bbbbb**bbbbb*********aa",
+".*******bbbbb****bbbbb*******aa.",
+".******bbbbb******bbbbb******aa.",
+"..******bbb********bbb******aaa.",
+"..*******b**********b*******aa..",
+"...************************aaa..",
+"....**********************aaa...",
+"....a********************aaa....",
+".....a******************aaa.....",
+"......a****************aaa......",
+".......aa************aaaa.......",
+".........aa********aaaaa........",
+"...........aaaaaaaaaaa..........",
+".............aaaaaaa............"};
+/* XPM */
+static const char *const question_xpm[] = {
+"32 32 5 1",
+". c None",
+"c c #000000",
+"* c #999999",
+"a c #ffffff",
+"b c #0000ff",
+"...........********.............",
+"........***aaaaaaaa***..........",
+"......**aaaaaaaaaaaaaa**........",
+".....*aaaaaaaaaaaaaaaaaa*.......",
+"....*aaaaaaaaaaaaaaaaaaaac......",
+"...*aaaaaaaabbbbbbaaaaaaaac.....",
+"..*aaaaaaaabaaabbbbaaaaaaaac....",
+".*aaaaaaaabbaaaabbbbaaaaaaaac...",
+".*aaaaaaaabbbbaabbbbaaaaaaaac*..",
+"*aaaaaaaaabbbbaabbbbaaaaaaaaac*.",
+"*aaaaaaaaaabbaabbbbaaaaaaaaaac*.",
+"*aaaaaaaaaaaaabbbbaaaaaaaaaaac**",
+"*aaaaaaaaaaaaabbbaaaaaaaaaaaac**",
+"*aaaaaaaaaaaaabbaaaaaaaaaaaaac**",
+"*aaaaaaaaaaaaabbaaaaaaaaaaaaac**",
+"*aaaaaaaaaaaaaaaaaaaaaaaaaaaac**",
+".*aaaaaaaaaaaabbaaaaaaaaaaaac***",
+".*aaaaaaaaaaabbbbaaaaaaaaaaac***",
+"..*aaaaaaaaaabbbbaaaaaaaaaac***.",
+"...caaaaaaaaaabbaaaaaaaaaac****.",
+"....caaaaaaaaaaaaaaaaaaaac****..",
+".....caaaaaaaaaaaaaaaaaac****...",
+"......ccaaaaaaaaaaaaaacc****....",
+".......*cccaaaaaaaaccc*****.....",
+"........***cccaaaac*******......",
+"..........****caaac*****........",
+".............*caaac**...........",
+"...............caac**...........",
+"................cac**...........",
+".................cc**...........",
+"..................***...........",
+"...................**..........."};
+
+#endif //QT_NO_IMAGEFORMAT_XPM
+
+/*!
+ \reimp
+ */
+QPixmap QWindowsStyle::standardPixmap(StandardPixmap standardPixmap, const QStyleOption *opt,
+ const QWidget *widget) const
+{
+#if defined(Q_WS_WIN) && !defined(Q_OS_WINCE)
+ QPixmap desktopIcon;
+ switch(standardPixmap) {
+ case SP_DriveCDIcon:
+ case SP_DriveDVDIcon:
+ {
+ desktopIcon = loadIconFromShell32(12, 16);
+ break;
+ }
+ case SP_DriveNetIcon:
+ {
+ desktopIcon = loadIconFromShell32(10, 16);
+ break;
+ }
+ case SP_DriveHDIcon:
+ {
+ desktopIcon = loadIconFromShell32(9, 16);
+ break;
+ }
+ case SP_DriveFDIcon:
+ {
+ desktopIcon = loadIconFromShell32(7, 16);
+ break;
+ }
+ case SP_FileIcon:
+ {
+ desktopIcon = loadIconFromShell32(1, 16);
+ break;
+ }
+ case SP_FileLinkIcon:
+ {
+ desktopIcon = loadIconFromShell32(1, 16);
+ QPainter painter(&desktopIcon);
+ QPixmap link = loadIconFromShell32(30, 16);
+ painter.drawPixmap(0, 0, 16, 16, link);
+ break;
+ }
+ case SP_DirLinkIcon:
+ {
+ desktopIcon = loadIconFromShell32(4, 16);
+ QPainter painter(&desktopIcon);
+ QPixmap link = loadIconFromShell32(30, 16);
+ painter.drawPixmap(0, 0, 16, 16, link);
+ break;
+ }
+ case SP_DirClosedIcon:
+ {
+ desktopIcon = loadIconFromShell32(4, 16);
+ break;
+ }
+ case SP_DesktopIcon:
+ {
+ desktopIcon = loadIconFromShell32(35, 16);
+ break;
+ }
+ case SP_ComputerIcon:
+ {
+ desktopIcon = loadIconFromShell32(16, 16);
+ break;
+ }
+ case SP_DirOpenIcon:
+ {
+ desktopIcon = loadIconFromShell32(5, 16);
+ break;
+ }
+ case SP_FileDialogNewFolder:
+ {
+ desktopIcon = loadIconFromShell32(319, 16);
+ break;
+ }
+ case SP_DirHomeIcon:
+ {
+ desktopIcon = loadIconFromShell32(235, 16);
+ break;
+ }
+ case SP_TrashIcon:
+ {
+ desktopIcon = loadIconFromShell32(191, 16);
+ break;
+ }
+ case SP_MessageBoxInformation:
+ {
+ HICON iconHandle = LoadIcon(NULL, IDI_INFORMATION);
+ desktopIcon = convertHIconToPixmap( iconHandle );
+ DestroyIcon(iconHandle);
+ break;
+ }
+ case SP_MessageBoxWarning:
+ {
+ HICON iconHandle = LoadIcon(NULL, IDI_WARNING);
+ desktopIcon = convertHIconToPixmap( iconHandle );
+ DestroyIcon(iconHandle);
+ break;
+ }
+ case SP_MessageBoxCritical:
+ {
+ HICON iconHandle = LoadIcon(NULL, IDI_ERROR);
+ desktopIcon = convertHIconToPixmap( iconHandle );
+ DestroyIcon(iconHandle);
+ break;
+ }
+ case SP_MessageBoxQuestion:
+ {
+ HICON iconHandle = LoadIcon(NULL, IDI_QUESTION);
+ desktopIcon = convertHIconToPixmap( iconHandle );
+ DestroyIcon(iconHandle);
+ break;
+ }
+ case SP_VistaShield:
+ {
+ if (QSysInfo::WindowsVersion >= QSysInfo::WV_VISTA
+ && QSysInfo::WindowsVersion < QSysInfo::WV_NT_based
+ && pSHGetStockIconInfo)
+ {
+ QPixmap pixmap;
+ QSHSTOCKICONINFO iconInfo;
+ memset(&iconInfo, 0, sizeof(iconInfo));
+ iconInfo.cbSize = sizeof(iconInfo);
+ if (pSHGetStockIconInfo(_SIID_SHIELD, _SHGFI_ICON | _SHGFI_SMALLICON, &iconInfo) == S_OK) {
+ pixmap = convertHIconToPixmap(iconInfo.hIcon);
+ DestroyIcon(iconInfo.hIcon);
+ return pixmap;
+ }
+ }
+ }
+ break;
+ }
+ if (!desktopIcon.isNull()) {
+ return desktopIcon;
+ }
+#endif
+#ifndef QT_NO_IMAGEFORMAT_XPM
+ switch (standardPixmap) {
+ case SP_TitleBarMenuButton:
+ return QPixmap(qt_menu_xpm);
+ case SP_TitleBarShadeButton:
+ return QPixmap(qt_shade_xpm);
+ case SP_TitleBarUnshadeButton:
+ return QPixmap(qt_unshade_xpm);
+ case SP_TitleBarNormalButton:
+ return QPixmap(qt_normalizeup_xpm);
+ case SP_TitleBarMinButton:
+ return QPixmap(qt_minimize_xpm);
+ case SP_TitleBarMaxButton:
+ return QPixmap(qt_maximize_xpm);
+ case SP_TitleBarCloseButton:
+ return QPixmap(qt_close_xpm);
+ case SP_TitleBarContextHelpButton:
+ return QPixmap(qt_help_xpm);
+ case SP_DockWidgetCloseButton:
+ return QPixmap(dock_widget_close_xpm);
+ case SP_MessageBoxInformation:
+ return QPixmap(information_xpm);
+ case SP_MessageBoxWarning:
+ return QPixmap(warning_xpm);
+ case SP_MessageBoxCritical:
+ return QPixmap(critical_xpm);
+ case SP_MessageBoxQuestion:
+ return QPixmap(question_xpm);
+ default:
+ break;
+ }
+#endif //QT_NO_IMAGEFORMAT_XPM
+ return QCommonStyle::standardPixmap(standardPixmap, opt, widget);
+}
+
+/*! \reimp */
+int QWindowsStyle::styleHint(StyleHint hint, const QStyleOption *opt, const QWidget *widget,
+ QStyleHintReturn *returnData) const
+{
+ int ret = 0;
+
+ switch (hint) {
+ case SH_EtchDisabledText:
+ case SH_Slider_SnapToValue:
+ case SH_PrintDialog_RightAlignButtons:
+ case SH_FontDialog_SelectAssociatedText:
+ case SH_Menu_AllowActiveAndDisabled:
+ case SH_MenuBar_AltKeyNavigation:
+ case SH_MenuBar_MouseTracking:
+ case SH_Menu_MouseTracking:
+ case SH_ComboBox_ListMouseTracking:
+ case SH_ScrollBar_StopMouseOverSlider:
+ case SH_MainWindow_SpaceBelowMenuBar:
+ ret = 1;
+
+ break;
+ case SH_ItemView_ShowDecorationSelected:
+#ifndef QT_NO_LISTVIEW
+ if (qobject_cast<const QListView*>(widget))
+ ret = 1;
+#endif
+ break;
+ case SH_ItemView_ChangeHighlightOnFocus:
+#if defined(Q_WS_WIN)
+ if (QSysInfo::WindowsVersion != QSysInfo::WV_95 && QSysInfo::WindowsVersion != QSysInfo::WV_NT)
+ ret = 1;
+ else
+#endif
+ ret = 0;
+ break;
+ case SH_ToolBox_SelectedPageTitleBold:
+ ret = 0;
+ break;
+
+#if defined(Q_WS_WIN)
+ case SH_UnderlineShortcut:
+ ret = 1;
+ if (QSysInfo::WindowsVersion != QSysInfo::WV_95
+ && QSysInfo::WindowsVersion != QSysInfo::WV_98
+ && QSysInfo::WindowsVersion != QSysInfo::WV_NT) {
+ BOOL cues;
+ SystemParametersInfo(SPI_GETKEYBOARDCUES, 0, &cues, 0);
+ ret = int(cues);
+ // Do nothing if we always paint underlines
+ Q_D(const QWindowsStyle);
+ if (!ret && widget && d) {
+#ifndef QT_NO_MENUBAR
+ const QMenuBar *menuBar = qobject_cast<const QMenuBar *>(widget);
+ if (!menuBar && qobject_cast<const QMenu *>(widget)) {
+ QWidget *w = QApplication::activeWindow();
+ if (w && w != widget)
+ menuBar = qFindChild<QMenuBar *>(w);
+ }
+ // If we paint a menu bar draw underlines if is in the keyboardState
+ if (menuBar) {
+ if (menuBar->d_func()->keyboardState || d->altDown())
+ ret = 1;
+ // Otherwise draw underlines if the toplevel widget has seen an alt-press
+ } else
+#endif // QT_NO_MENUBAR
+ if (d->hasSeenAlt(widget)) {
+ ret = 1;
+ }
+ }
+ }
+ break;
+#endif
+#ifndef QT_NO_RUBBERBAND
+ case SH_RubberBand_Mask:
+ if (const QStyleOptionRubberBand *rbOpt = qstyleoption_cast<const QStyleOptionRubberBand *>(opt)) {
+ ret = 0;
+ if (rbOpt->shape == QRubberBand::Rectangle) {
+ ret = true;
+ if(QStyleHintReturnMask *mask = qstyleoption_cast<QStyleHintReturnMask*>(returnData)) {
+ mask->region = opt->rect;
+ int size = 1;
+ if (widget && widget->isWindow())
+ size = 4;
+ mask->region -= opt->rect.adjusted(size, size, -size, -size);
+ }
+ }
+ }
+ break;
+#endif // QT_NO_RUBBERBAND
+ case SH_LineEdit_PasswordCharacter:
+ {
+#ifdef Q_WS_WIN
+ if (widget && (QSysInfo::WindowsVersion >= QSysInfo::WV_XP && QSysInfo::WindowsVersion < QSysInfo::WV_NT_based)) {
+ const QFontMetrics &fm = widget->fontMetrics();
+ if (fm.inFont(QChar(0x25CF)))
+ ret = 0x25CF;
+ else if (fm.inFont(QChar(0x2022)))
+ ret = 0x2022;
+ }
+#endif
+ if (!ret)
+ ret = '*';
+ }
+ break;
+#ifndef QT_NO_WIZARD
+ case SH_WizardStyle:
+ ret = QWizard::ModernStyle;
+ break;
+#endif
+ case SH_ItemView_ArrowKeysNavigateIntoChildren:
+ ret = true;
+ break;
+ default:
+ ret = QCommonStyle::styleHint(hint, opt, widget, returnData);
+ break;
+ }
+ return ret;
+}
+
+/*! \reimp */
+void QWindowsStyle::drawPrimitive(PrimitiveElement pe, const QStyleOption *opt, QPainter *p,
+ const QWidget *w) const
+{
+ // Used to restore across fallthrough cases. Currently only used in PE_IndicatorCheckBox
+ bool doRestore = false;
+
+ switch (pe) {
+#ifndef QT_NO_TOOLBAR
+ case PE_IndicatorToolBarSeparator:
+ {
+ QRect rect = opt->rect;
+ const int margin = 2;
+ QPen oldPen = p->pen();
+ if(opt->state & State_Horizontal){
+ const int offset = rect.width()/2;
+ p->setPen(QPen(opt->palette.dark().color()));
+ p->drawLine(rect.bottomLeft().x() + offset,
+ rect.bottomLeft().y() - margin,
+ rect.topLeft().x() + offset,
+ rect.topLeft().y() + margin);
+ p->setPen(QPen(opt->palette.light().color()));
+ p->drawLine(rect.bottomLeft().x() + offset + 1,
+ rect.bottomLeft().y() - margin,
+ rect.topLeft().x() + offset + 1,
+ rect.topLeft().y() + margin);
+ }
+ else{ //Draw vertical separator
+ const int offset = rect.height()/2;
+ p->setPen(QPen(opt->palette.dark().color()));
+ p->drawLine(rect.topLeft().x() + margin ,
+ rect.topLeft().y() + offset,
+ rect.topRight().x() - margin,
+ rect.topRight().y() + offset);
+ p->setPen(QPen(opt->palette.light().color()));
+ p->drawLine(rect.topLeft().x() + margin ,
+ rect.topLeft().y() + offset + 1,
+ rect.topRight().x() - margin,
+ rect.topRight().y() + offset + 1);
+ }
+ p->setPen(oldPen);
+ }
+ break;
+ case PE_IndicatorToolBarHandle:
+ p->save();
+ p->translate(opt->rect.x(), opt->rect.y());
+ if (opt->state & State_Horizontal) {
+ int x = opt->rect.width() / 2 - 4;
+ if (QApplication::layoutDirection() == Qt::RightToLeft)
+ x -= 2;
+ if (opt->rect.height() > 4) {
+ qDrawShadePanel(p, x, 2, 3, opt->rect.height() - 4,
+ opt->palette, false, 1, 0);
+ qDrawShadePanel(p, x + 3, 2, 3, opt->rect.height() - 4,
+ opt->palette, false, 1, 0);
+ }
+ } else {
+ if (opt->rect.width() > 4) {
+ int y = opt->rect.height() / 2 - 4;
+ qDrawShadePanel(p, 2, y, opt->rect.width() - 4, 3,
+ opt->palette, false, 1, 0);
+ qDrawShadePanel(p, 2, y + 3, opt->rect.width() - 4, 3,
+ opt->palette, false, 1, 0);
+ }
+ }
+ p->restore();
+ break;
+
+#endif // QT_NO_TOOLBAR
+ case PE_FrameButtonTool:
+ case PE_PanelButtonTool: {
+ QPen oldPen = p->pen();
+#ifndef QT_NO_DOCKWIDGET
+ if (w && w->inherits("QDockWidgetTitleButton")) {
+ if (const QWidget *dw = w->parentWidget())
+ if (dw->isWindow()){
+ qDrawWinButton(p, opt->rect.adjusted(1, 1, 0, 0), opt->palette, opt->state & (State_Sunken | State_On),
+ &opt->palette.button());
+
+ return;
+ }
+ }
+#endif // QT_NO_DOCKWIDGET
+ QBrush fill;
+ bool stippled;
+ bool panel = (pe == PE_PanelButtonTool);
+ if ((!(opt->state & State_Sunken ))
+ && (!(opt->state & State_Enabled)
+ || !(opt->state & State_MouseOver && opt->state & State_AutoRaise))
+ && (opt->state & State_On) && use2000style) {
+ fill = QBrush(opt->palette.light().color(), Qt::Dense4Pattern);
+ stippled = true;
+ } else {
+ fill = opt->palette.brush(QPalette::Button);
+ stippled = false;
+ }
+
+ if (opt->state & (State_Raised | State_Sunken | State_On)) {
+ if (opt->state & State_AutoRaise) {
+ if(opt->state & (State_Enabled | State_Sunken | State_On)){
+ if (panel)
+ qDrawShadePanel(p, opt->rect, opt->palette,
+ opt->state & (State_Sunken | State_On), 1, &fill);
+ else
+ qDrawShadeRect(p, opt->rect, opt->palette,
+ opt->state & (State_Sunken | State_On), 1);
+ }
+ if (stippled) {
+ p->setPen(opt->palette.button().color());
+ p->drawRect(opt->rect.adjusted(1,1,-2,-2));
+ }
+ } else {
+ qDrawWinButton(p, opt->rect, opt->palette,
+ opt->state & (State_Sunken | State_On), panel ? &fill : 0);
+ }
+ } else {
+ p->fillRect(opt->rect, fill);
+ }
+ p->setPen(oldPen);
+ break; }
+ case PE_PanelButtonCommand:
+ if (const QStyleOptionButton *btn = qstyleoption_cast<const QStyleOptionButton *>(opt)) {
+ QBrush fill;
+ State flags = opt->state;
+ QPalette pal = opt->palette;
+ QRect r = opt->rect;
+ if (! (flags & State_Sunken) && (flags & State_On))
+ fill = QBrush(pal.light().color(), Qt::Dense4Pattern);
+ else
+ fill = pal.brush(QPalette::Button);
+
+ if (btn->features & QStyleOptionButton::DefaultButton && flags & State_Sunken) {
+ p->setPen(pal.dark().color());
+ p->setBrush(fill);
+ p->drawRect(r.adjusted(0, 0, -1, -1));
+ } else if (flags & (State_Raised | State_Sunken | State_On | State_Sunken)) {
+ qDrawWinButton(p, r, pal, flags & (State_Sunken | State_On),
+ &fill);
+ } else {
+ p->fillRect(r, fill);
+ }
+ }
+ break;
+ case PE_FrameDefaultButton: {
+ QPen oldPen = p->pen();
+ p->setPen(opt->palette.shadow().color());
+ QRect rect = opt->rect;
+ rect.adjust(0, 0, -1, -1);
+ p->drawRect(rect);
+ p->setPen(oldPen);
+ break;
+ }
+ case PE_IndicatorArrowUp:
+ case PE_IndicatorArrowDown:
+ case PE_IndicatorArrowRight:
+ case PE_IndicatorArrowLeft:
+ {
+ if (opt->rect.width() <= 1 || opt->rect.height() <= 1)
+ break;
+ QRect r = opt->rect;
+ int size = qMin(r.height(), r.width());
+ QPixmap pixmap;
+ QString pixmapName;
+ pixmapName.sprintf("%s-%s-%d-%d-%d-%lld",
+ "$qt_ia", metaObject()->className(),
+ uint(opt->state), pe,
+ size, opt->palette.cacheKey());
+ if (!QPixmapCache::find(pixmapName, pixmap)) {
+ int border = size/5;
+ int sqsize = 2*(size/2);
+ QImage image(sqsize, sqsize, QImage::Format_ARGB32);
+ image.fill(Qt::transparent);
+ QPainter imagePainter(&image);
+
+ QPolygon a;
+ switch (pe) {
+ case PE_IndicatorArrowUp:
+ a.setPoints(3, border, sqsize/2, sqsize/2, border, sqsize - border, sqsize/2);
+ break;
+ case PE_IndicatorArrowDown:
+ a.setPoints(3, border, sqsize/2, sqsize/2, sqsize - border, sqsize - border, sqsize/2);
+ break;
+ case PE_IndicatorArrowRight:
+ a.setPoints(3, sqsize - border, sqsize/2, sqsize/2, border, sqsize/2, sqsize - border);
+ break;
+ case PE_IndicatorArrowLeft:
+ a.setPoints(3, border, sqsize/2, sqsize/2, border, sqsize/2, sqsize - border);
+ break;
+ default:
+ break;
+ }
+
+ int bsx = 0;
+ int bsy = 0;
+
+ if (opt->state & State_Sunken) {
+ bsx = pixelMetric(PM_ButtonShiftHorizontal, opt, w);
+ bsy = pixelMetric(PM_ButtonShiftVertical, opt, w);
+ }
+
+ QRect bounds = a.boundingRect();
+ int sx = sqsize / 2 - bounds.center().x() - 1;
+ int sy = sqsize / 2 - bounds.center().y() - 1;
+ imagePainter.translate(sx + bsx, sy + bsy);
+ imagePainter.setPen(opt->palette.buttonText().color());
+ imagePainter.setBrush(opt->palette.buttonText());
+
+ if (!(opt->state & State_Enabled)) {
+ imagePainter.translate(1, 1);
+ imagePainter.setBrush(opt->palette.light().color());
+ imagePainter.setPen(opt->palette.light().color());
+ imagePainter.drawPolygon(a);
+ imagePainter.translate(-1, -1);
+ imagePainter.setBrush(opt->palette.mid().color());
+ imagePainter.setPen(opt->palette.mid().color());
+ }
+
+ imagePainter.drawPolygon(a);
+ imagePainter.end();
+ pixmap = QPixmap::fromImage(image);
+ QPixmapCache::insert(pixmapName, pixmap);
+ }
+ int xOffset = r.x() + (r.width() - size)/2;
+ int yOffset = r.y() + (r.height() - size)/2;
+ p->drawPixmap(xOffset, yOffset, pixmap);
+ }
+ break;
+ case PE_IndicatorCheckBox: {
+ QBrush fill;
+ if (opt->state & State_NoChange)
+ fill = QBrush(opt->palette.base().color(), Qt::Dense4Pattern);
+ else if (opt->state & State_Sunken)
+ fill = opt->palette.button();
+ else if (opt->state & State_Enabled)
+ fill = opt->palette.base();
+ else
+ fill = opt->palette.background();
+ p->save();
+ doRestore = true;
+ qDrawWinPanel(p, opt->rect, opt->palette, true, &fill);
+ if (opt->state & State_NoChange)
+ p->setPen(opt->palette.dark().color());
+ else
+ p->setPen(opt->palette.text().color());
+ } // Fall through!
+ case PE_IndicatorViewItemCheck:
+ case PE_Q3CheckListIndicator:
+ if (!doRestore) {
+ p->save();
+ doRestore = true;
+ }
+ if (pe == PE_Q3CheckListIndicator || pe == PE_IndicatorViewItemCheck) {
+ const QStyleOptionViewItem *itemViewOpt = qstyleoption_cast<const QStyleOptionViewItem *>(opt);
+ p->setPen(itemViewOpt
+ && itemViewOpt->showDecorationSelected
+ && opt->state & State_Selected
+ ? opt->palette.highlightedText().color()
+ : opt->palette.text().color());
+ if (opt->state & State_NoChange)
+ p->setBrush(opt->palette.brush(QPalette::Button));
+ p->drawRect(opt->rect.x() + 1, opt->rect.y() + 1, 11, 11);
+ }
+ if (!(opt->state & State_Off)) {
+ QLineF lines[7];
+ int i, xx, yy;
+ xx = opt->rect.x() + 3;
+ yy = opt->rect.y() + 5;
+ for (i = 0; i < 3; ++i) {
+ lines[i] = QLineF(xx, yy, xx, yy + 2);
+ ++xx;
+ ++yy;
+ }
+ yy -= 2;
+ for (i = 3; i < 7; ++i) {
+ lines[i] = QLineF(xx, yy, xx, yy + 2);
+ ++xx;
+ --yy;
+ }
+ p->drawLines(lines, 7);
+ }
+ if (doRestore)
+ p->restore();
+ break;
+ case PE_FrameFocusRect:
+ if (const QStyleOptionFocusRect *fropt = qstyleoption_cast<const QStyleOptionFocusRect *>(opt)) {
+ //### check for d->alt_down
+ if (!(fropt->state & State_KeyboardFocusChange) && !styleHint(SH_UnderlineShortcut, opt))
+ return;
+ QRect r = opt->rect;
+ p->save();
+ p->setBackgroundMode(Qt::TransparentMode);
+ QColor bg_col = fropt->backgroundColor;
+ if (!bg_col.isValid())
+ bg_col = p->background().color();
+ // Create an "XOR" color.
+ QColor patternCol((bg_col.red() ^ 0xff) & 0xff,
+ (bg_col.green() ^ 0xff) & 0xff,
+ (bg_col.blue() ^ 0xff) & 0xff);
+ p->setBrush(QBrush(patternCol, Qt::Dense4Pattern));
+ p->setBrushOrigin(r.topLeft());
+ p->setPen(Qt::NoPen);
+ p->drawRect(r.left(), r.top(), r.width(), 1); // Top
+ p->drawRect(r.left(), r.bottom(), r.width(), 1); // Bottom
+ p->drawRect(r.left(), r.top(), 1, r.height()); // Left
+ p->drawRect(r.right(), r.top(), 1, r.height()); // Right
+ p->restore();
+ }
+ break;
+ case PE_IndicatorRadioButton:
+ {
+#define PTSARRLEN(x) sizeof(x)/(sizeof(QPoint))
+ static const QPoint pts1[] = { // dark lines
+ QPoint(1, 9), QPoint(1, 8), QPoint(0, 7), QPoint(0, 4), QPoint(1, 3), QPoint(1, 2),
+ QPoint(2, 1), QPoint(3, 1), QPoint(4, 0), QPoint(7, 0), QPoint(8, 1), QPoint(9, 1)
+ };
+ static const QPoint pts2[] = { // black lines
+ QPoint(2, 8), QPoint(1, 7), QPoint(1, 4), QPoint(2, 3), QPoint(2, 2), QPoint(3, 2),
+ QPoint(4, 1), QPoint(7, 1), QPoint(8, 2), QPoint(9, 2)
+ };
+ static const QPoint pts3[] = { // background lines
+ QPoint(2, 9), QPoint(3, 9), QPoint(4, 10), QPoint(7, 10), QPoint(8, 9), QPoint(9, 9),
+ QPoint(9, 8), QPoint(10, 7), QPoint(10, 4), QPoint(9, 3)
+ };
+ static const QPoint pts4[] = { // white lines
+ QPoint(2, 10), QPoint(3, 10), QPoint(4, 11), QPoint(7, 11), QPoint(8, 10),
+ QPoint(9, 10), QPoint(10, 9), QPoint(10, 8), QPoint(11, 7), QPoint(11, 4),
+ QPoint(10, 3), QPoint(10, 2)
+ };
+ static const QPoint pts5[] = { // inner fill
+ QPoint(4, 2), QPoint(7, 2), QPoint(9, 4), QPoint(9, 7), QPoint(7, 9), QPoint(4, 9),
+ QPoint(2, 7), QPoint(2, 4)
+ };
+
+ // make sure the indicator is square
+ QRect ir = opt->rect;
+
+ if (opt->rect.width() < opt->rect.height()) {
+ ir.setTop(opt->rect.top() + (opt->rect.height() - opt->rect.width()) / 2);
+ ir.setHeight(opt->rect.width());
+ } else if (opt->rect.height() < opt->rect.width()) {
+ ir.setLeft(opt->rect.left() + (opt->rect.width() - opt->rect.height()) / 2);
+ ir.setWidth(opt->rect.height());
+ }
+
+ p->save();
+ bool down = opt->state & State_Sunken;
+ bool enabled = opt->state & State_Enabled;
+ bool on = opt->state & State_On;
+ QPolygon a;
+
+ //center when rect is larger than indicator size
+ int xOffset = 0;
+ int yOffset = 0;
+ int indicatorWidth = pixelMetric(PM_ExclusiveIndicatorWidth);
+ int indicatorHeight = pixelMetric(PM_ExclusiveIndicatorWidth);
+ if (ir.width() > indicatorWidth)
+ xOffset += (ir.width() - indicatorWidth)/2;
+ if (ir.height() > indicatorHeight)
+ yOffset += (ir.height() - indicatorHeight)/2;
+ p->translate(xOffset, yOffset);
+
+ p->translate(ir.x(), ir.y());
+
+ p->setPen(opt->palette.dark().color());
+ p->drawPolyline(pts1, PTSARRLEN(pts1));
+
+ p->setPen(opt->palette.shadow().color());
+ p->drawPolyline(pts2, PTSARRLEN(pts2));
+
+ p->setPen(opt->palette.midlight().color());
+ p->drawPolyline(pts3, PTSARRLEN(pts3));
+
+ p->setPen(opt->palette.light().color());
+ p->drawPolyline(pts4, PTSARRLEN(pts4));
+
+ QColor fillColor = (down || !enabled)
+ ? opt->palette.button().color()
+ : opt->palette.base().color();
+ p->setPen(fillColor);
+ p->setBrush(fillColor) ;
+ p->drawPolygon(pts5, PTSARRLEN(pts5));
+
+ p->translate(-ir.x(), -ir.y()); // restore translate
+
+ if (on) {
+ p->setPen(Qt::NoPen);
+ p->setBrush(opt->palette.text());
+ p->drawRect(ir.x() + 5, ir.y() + 4, 2, 4);
+ p->drawRect(ir.x() + 4, ir.y() + 5, 4, 2);
+ }
+ p->restore();
+ break;
+ }
+#ifndef QT_NO_FRAME
+ case PE_Frame:
+ case PE_FrameMenu:
+ if (const QStyleOptionFrame *frame = qstyleoption_cast<const QStyleOptionFrame *>(opt)) {
+ if (frame->lineWidth == 2 || pe == PE_Frame) {
+ QPalette popupPal = frame->palette;
+ if (pe == PE_FrameMenu) {
+ popupPal.setColor(QPalette::Light, frame->palette.background().color());
+ popupPal.setColor(QPalette::Midlight, frame->palette.light().color());
+ }
+ if (use2000style && pe == PE_Frame && (frame->state & State_Raised))
+ qDrawWinButton(p, frame->rect, popupPal, frame->state & State_Sunken);
+ else if (use2000style && pe == PE_Frame && (frame->state & State_Sunken))
+ {
+ popupPal.setColor(QPalette::Midlight, frame->palette.background().color());
+ qDrawWinPanel(p, frame->rect, popupPal, frame->state & State_Sunken);
+ }
+ else
+ qDrawWinPanel(p, frame->rect, popupPal, frame->state & State_Sunken);
+ } else {
+ QCommonStyle::drawPrimitive(pe, opt, p, w);
+ }
+ } else {
+ QPalette popupPal = opt->palette;
+ popupPal.setColor(QPalette::Light, opt->palette.background().color());
+ popupPal.setColor(QPalette::Midlight, opt->palette.light().color());
+ qDrawWinPanel(p, opt->rect, popupPal, opt->state & State_Sunken);
+ }
+ break;
+#endif // QT_NO_FRAME
+ case PE_IndicatorBranch: {
+ // This is _way_ too similar to the common style.
+ static const int decoration_size = 9;
+ int mid_h = opt->rect.x() + opt->rect.width() / 2;
+ int mid_v = opt->rect.y() + opt->rect.height() / 2;
+ int bef_h = mid_h;
+ int bef_v = mid_v;
+ int aft_h = mid_h;
+ int aft_v = mid_v;
+ if (opt->state & State_Children) {
+ int delta = decoration_size / 2;
+ bef_h -= delta;
+ bef_v -= delta;
+ aft_h += delta;
+ aft_v += delta;
+ p->drawLine(bef_h + 2, bef_v + 4, bef_h + 6, bef_v + 4);
+ if (!(opt->state & State_Open))
+ p->drawLine(bef_h + 4, bef_v + 2, bef_h + 4, bef_v + 6);
+ QPen oldPen = p->pen();
+ p->setPen(opt->palette.dark().color());
+ p->drawRect(bef_h, bef_v, decoration_size - 1, decoration_size - 1);
+ p->setPen(oldPen);
+ }
+ QBrush brush(opt->palette.dark().color(), Qt::Dense4Pattern);
+ if (opt->state & State_Item) {
+ if (opt->direction == Qt::RightToLeft)
+ p->fillRect(opt->rect.left(), mid_v, bef_h - opt->rect.left(), 1, brush);
+ else
+ p->fillRect(aft_h, mid_v, opt->rect.right() - aft_h + 1, 1, brush);
+ }
+ if (opt->state & State_Sibling)
+ p->fillRect(mid_h, aft_v, 1, opt->rect.bottom() - aft_v + 1, brush);
+ if (opt->state & (State_Open | State_Children | State_Item | State_Sibling))
+ p->fillRect(mid_h, opt->rect.y(), 1, bef_v - opt->rect.y(), brush);
+ break; }
+ case PE_FrameButtonBevel:
+ case PE_PanelButtonBevel: {
+ QBrush fill;
+ bool panel = pe != PE_FrameButtonBevel;
+ p->setBrushOrigin(opt->rect.topLeft());
+ if (!(opt->state & State_Sunken) && (opt->state & State_On))
+ fill = QBrush(opt->palette.light().color(), Qt::Dense4Pattern);
+ else
+ fill = opt->palette.brush(QPalette::Button);
+
+ if (opt->state & (State_Raised | State_On | State_Sunken)) {
+ qDrawWinButton(p, opt->rect, opt->palette, opt->state & (State_Sunken | State_On),
+ panel ? &fill : 0);
+ } else {
+ if (panel)
+ p->fillRect(opt->rect, fill);
+ else
+ p->drawRect(opt->rect);
+ }
+ break; }
+ case PE_FrameWindow: {
+ QPalette popupPal = opt->palette;
+ popupPal.setColor(QPalette::Light, opt->palette.background().color());
+ popupPal.setColor(QPalette::Midlight, opt->palette.light().color());
+ qDrawWinPanel(p, opt->rect, popupPal, opt->state & State_Sunken);
+ break; }
+#ifndef QT_NO_DOCKWIDGET
+ case PE_IndicatorDockWidgetResizeHandle: {
+ QPen oldPen = p->pen();
+ p->setPen(opt->palette.light().color());
+ if (opt->state & State_Horizontal) {
+ p->drawLine(opt->rect.left(), opt->rect.top(),
+ opt->rect.right(), opt->rect.top());
+ p->setPen(opt->palette.dark().color());
+ p->drawLine(opt->rect.left(), opt->rect.bottom() - 1,
+ opt->rect.right(), opt->rect.bottom() - 1);
+ p->setPen(opt->palette.shadow().color());
+ p->drawLine(opt->rect.left(), opt->rect.bottom(),
+ opt->rect.right(), opt->rect.bottom());
+ } else {
+ p->drawLine(opt->rect.left(), opt->rect.top(),
+ opt->rect.left(), opt->rect.bottom());
+ p->setPen(opt->palette.dark().color());
+ p->drawLine(opt->rect.right() - 1, opt->rect.top(),
+ opt->rect.right() - 1, opt->rect.bottom());
+ p->setPen(opt->palette.shadow().color());
+ p->drawLine(opt->rect.right(), opt->rect.top(),
+ opt->rect.right(), opt->rect.bottom());
+ }
+ p->setPen(oldPen);
+ break; }
+case PE_FrameDockWidget:
+ if (qstyleoption_cast<const QStyleOptionFrame *>(opt)) {
+ drawPrimitive(QStyle::PE_FrameWindow, opt, p, w);
+ }
+ break;
+#endif // QT_NO_DOCKWIDGET
+
+ case PE_FrameStatusBarItem:
+ qDrawShadePanel(p, opt->rect, opt->palette, true, 1, 0);
+ break;
+
+#ifndef QT_NO_PROGRESSBAR
+ case PE_IndicatorProgressChunk:
+ {
+ bool vertical = false, inverted = false;
+ if (const QStyleOptionProgressBarV2 *pb2 = qstyleoption_cast<const QStyleOptionProgressBarV2 *>(opt)) {
+ vertical = (pb2->orientation == Qt::Vertical);
+ inverted = pb2->invertedAppearance;
+ }
+
+ int space = 2;
+ int chunksize = pixelMetric(PM_ProgressBarChunkWidth, opt, w) - space;
+ if (!vertical) {
+ if (opt->rect.width() <= chunksize)
+ space = 0;
+
+ if (inverted)
+ p->fillRect(opt->rect.x() + space, opt->rect.y(), opt->rect.width() - space, opt->rect.height(),
+ opt->palette.brush(QPalette::Highlight));
+ else
+ p->fillRect(opt->rect.x(), opt->rect.y(), opt->rect.width() - space, opt->rect.height(),
+ opt->palette.brush(QPalette::Highlight));
+ } else {
+ if (opt->rect.height() <= chunksize)
+ space = 0;
+
+ if (inverted)
+ p->fillRect(opt->rect.x(), opt->rect.y(), opt->rect.width(), opt->rect.height() - space,
+ opt->palette.brush(QPalette::Highlight));
+ else
+ p->fillRect(opt->rect.x(), opt->rect.y() + space, opt->rect.width(), opt->rect.height() - space,
+ opt->palette.brush(QPalette::Highlight));
+ }
+ }
+ break;
+#endif // QT_NO_PROGRESSBAR
+
+ case PE_FrameTabWidget:
+ if (use2000style) {
+ QRect rect = opt->rect;
+ QPalette pal = opt->palette;
+ qDrawWinButton(p, opt->rect, opt->palette, false, 0);
+ break;
+ }
+ default:
+ QCommonStyle::drawPrimitive(pe, opt, p, w);
+ }
+}
+
+/*! \reimp */
+void QWindowsStyle::drawControl(ControlElement ce, const QStyleOption *opt, QPainter *p,
+ const QWidget *widget) const
+{
+ switch (ce) {
+#ifndef QT_NO_RUBBERBAND
+ case CE_RubberBand:
+ if (qstyleoption_cast<const QStyleOptionRubberBand *>(opt)) {
+ // ### workaround for slow general painter path
+ QPixmap tiledPixmap(16, 16);
+ QPainter pixmapPainter(&tiledPixmap);
+ pixmapPainter.setPen(Qt::NoPen);
+ pixmapPainter.setBrush(Qt::Dense4Pattern);
+ pixmapPainter.setBackground(Qt::white);
+ pixmapPainter.setBackgroundMode(Qt::OpaqueMode);
+ pixmapPainter.drawRect(0, 0, tiledPixmap.width(), tiledPixmap.height());
+ pixmapPainter.end();
+ tiledPixmap = QPixmap::fromImage(tiledPixmap.toImage());
+ p->save();
+ QRect r = opt->rect;
+ QStyleHintReturnMask mask;
+ if (styleHint(QStyle::SH_RubberBand_Mask, opt, widget, &mask))
+ p->setClipRegion(mask.region);
+ p->drawTiledPixmap(r.x(), r.y(), r.width(), r.height(), tiledPixmap);
+ p->restore();
+ return;
+ }
+ break;
+#endif // QT_NO_RUBBERBAND
+
+#if !defined(QT_NO_MENU) && !defined(QT_NO_MAINWINDOW)
+ case CE_MenuBarEmptyArea:
+ if (widget && qobject_cast<const QMainWindow *>(widget->parentWidget())) {
+ p->fillRect(opt->rect, opt->palette.button());
+ QPen oldPen = p->pen();
+ p->setPen(QPen(opt->palette.dark().color()));
+ p->drawLine(opt->rect.bottomLeft(), opt->rect.bottomRight());
+ p->setPen(oldPen);
+ }
+ break;
+#endif
+#ifndef QT_NO_MENU
+ case CE_MenuItem:
+ if (const QStyleOptionMenuItem *menuitem = qstyleoption_cast<const QStyleOptionMenuItem *>(opt)) {
+ int x, y, w, h;
+ menuitem->rect.getRect(&x, &y, &w, &h);
+ int tab = menuitem->tabWidth;
+ bool dis = !(menuitem->state & State_Enabled);
+ bool checked = menuitem->checkType != QStyleOptionMenuItem::NotCheckable
+ ? menuitem->checked : false;
+ bool act = menuitem->state & State_Selected;
+
+ // windows always has a check column, regardless whether we have an icon or not
+ int checkcol = qMax(menuitem->maxIconWidth, windowsCheckMarkWidth);
+
+ QBrush fill = menuitem->palette.brush(act ? QPalette::Highlight : QPalette::Button);
+ p->fillRect(menuitem->rect.adjusted(0, 0, -1, 0), fill);
+
+ if (menuitem->menuItemType == QStyleOptionMenuItem::Separator){
+ int yoff = y-1 + h / 2;
+ p->setPen(menuitem->palette.dark().color());
+ p->drawLine(x + 2, yoff, x + w - 4, yoff);
+ p->setPen(menuitem->palette.light().color());
+ p->drawLine(x + 2, yoff + 1, x + w - 4, yoff + 1);
+ return;
+ }
+
+ QRect vCheckRect = visualRect(opt->direction, menuitem->rect, QRect(menuitem->rect.x(), menuitem->rect.y(), checkcol, menuitem->rect.height()));
+ if (checked) {
+ if (act && !dis) {
+ qDrawShadePanel(p, vCheckRect,
+ menuitem->palette, true, 1,
+ &menuitem->palette.brush(QPalette::Button));
+ } else {
+ QBrush fill(menuitem->palette.light().color(), Qt::Dense4Pattern);
+ qDrawShadePanel(p, vCheckRect, menuitem->palette, true, 1, &fill);
+ }
+ } else if (!act) {
+ p->fillRect(vCheckRect, menuitem->palette.brush(QPalette::Button));
+ }
+
+ // On Windows Style, if we have a checkable item and an icon we
+ // draw the icon recessed to indicate an item is checked. If we
+ // have no icon, we draw a checkmark instead.
+ if (!menuitem->icon.isNull()) {
+ QIcon::Mode mode = dis ? QIcon::Disabled : QIcon::Normal;
+ if (act && !dis)
+ mode = QIcon::Active;
+ QPixmap pixmap;
+ if (checked)
+ pixmap = menuitem->icon.pixmap(pixelMetric(PM_SmallIconSize, opt, widget), mode, QIcon::On);
+ else
+ pixmap = menuitem->icon.pixmap(pixelMetric(PM_SmallIconSize, opt, widget), mode);
+ int pixw = pixmap.width();
+ int pixh = pixmap.height();
+ if (act && !dis && !checked)
+ qDrawShadePanel(p, vCheckRect, menuitem->palette, false, 1,
+ &menuitem->palette.brush(QPalette::Button));
+ QRect pmr(0, 0, pixw, pixh);
+ pmr.moveCenter(vCheckRect.center());
+ p->setPen(menuitem->palette.text().color());
+ p->drawPixmap(pmr.topLeft(), pixmap);
+ } else if (checked) {
+ QStyleOptionMenuItem newMi = *menuitem;
+ newMi.state = State_None;
+ if (!dis)
+ newMi.state |= State_Enabled;
+ if (act)
+ newMi.state |= State_On;
+ newMi.rect = visualRect(opt->direction, menuitem->rect, QRect(menuitem->rect.x() + windowsItemFrame, menuitem->rect.y() + windowsItemFrame,
+ checkcol - 2 * windowsItemFrame, menuitem->rect.height() - 2*windowsItemFrame));
+ drawPrimitive(PE_IndicatorMenuCheckMark, &newMi, p, widget);
+ }
+ p->setPen(act ? menuitem->palette.highlightedText().color() : menuitem->palette.buttonText().color());
+
+ QColor discol;
+ if (dis) {
+ discol = menuitem->palette.text().color();
+ p->setPen(discol);
+ }
+
+ int xm = windowsItemFrame + checkcol + windowsItemHMargin;
+ int xpos = menuitem->rect.x() + xm;
+ QRect textRect(xpos, y + windowsItemVMargin, w - xm - windowsRightBorder - tab + 1, h - 2 * windowsItemVMargin);
+ QRect vTextRect = visualRect(opt->direction, menuitem->rect, textRect);
+ QString s = menuitem->text;
+ if (!s.isEmpty()) { // draw text
+ p->save();
+ int t = s.indexOf(QLatin1Char('\t'));
+ int text_flags = Qt::AlignVCenter | Qt::TextShowMnemonic | Qt::TextDontClip | Qt::TextSingleLine;
+ if (!styleHint(SH_UnderlineShortcut, menuitem, widget))
+ text_flags |= Qt::TextHideMnemonic;
+ text_flags |= Qt::AlignLeft;
+ if (t >= 0) {
+ QRect vShortcutRect = visualRect(opt->direction, menuitem->rect,
+ QRect(textRect.topRight(), QPoint(menuitem->rect.right(), textRect.bottom())));
+ if (dis && !act && styleHint(SH_EtchDisabledText, opt, widget)) {
+ p->setPen(menuitem->palette.light().color());
+ p->drawText(vShortcutRect.adjusted(1,1,1,1), text_flags, s.mid(t + 1));
+ p->setPen(discol);
+ }
+ p->drawText(vShortcutRect, text_flags, s.mid(t + 1));
+ s = s.left(t);
+ }
+ QFont font = menuitem->font;
+ if (menuitem->menuItemType == QStyleOptionMenuItem::DefaultItem)
+ font.setBold(true);
+ p->setFont(font);
+ if (dis && !act && styleHint(SH_EtchDisabledText, opt, widget)) {
+ p->setPen(menuitem->palette.light().color());
+ p->drawText(vTextRect.adjusted(1,1,1,1), text_flags, s.left(t));
+ p->setPen(discol);
+ }
+ p->drawText(vTextRect, text_flags, s.left(t));
+ p->restore();
+ }
+ if (menuitem->menuItemType == QStyleOptionMenuItem::SubMenu) {// draw sub menu arrow
+ int dim = (h - 2 * windowsItemFrame) / 2;
+ PrimitiveElement arrow;
+ arrow = (opt->direction == Qt::RightToLeft) ? PE_IndicatorArrowLeft : PE_IndicatorArrowRight;
+ xpos = x + w - windowsArrowHMargin - windowsItemFrame - dim;
+ QRect vSubMenuRect = visualRect(opt->direction, menuitem->rect, QRect(xpos, y + h / 2 - dim / 2, dim, dim));
+ QStyleOptionMenuItem newMI = *menuitem;
+ newMI.rect = vSubMenuRect;
+ newMI.state = dis ? State_None : State_Enabled;
+ if (act)
+ newMI.palette.setColor(QPalette::ButtonText,
+ newMI.palette.highlightedText().color());
+ drawPrimitive(arrow, &newMI, p, widget);
+ }
+
+ }
+ break;
+#endif // QT_NO_MENU
+#ifndef QT_NO_MENUBAR
+ case CE_MenuBarItem:
+ if (const QStyleOptionMenuItem *mbi = qstyleoption_cast<const QStyleOptionMenuItem *>(opt)) {
+ bool active = mbi->state & State_Selected;
+ bool hasFocus = mbi->state & State_HasFocus;
+ bool down = mbi->state & State_Sunken;
+ QStyleOptionMenuItem newMbi = *mbi;
+ p->fillRect(mbi->rect, mbi->palette.brush(QPalette::Button));
+ if (active || hasFocus) {
+ QBrush b = mbi->palette.brush(QPalette::Button);
+ if (active && down)
+ p->setBrushOrigin(p->brushOrigin() + QPoint(1, 1));
+ if (active && hasFocus)
+ qDrawShadeRect(p, mbi->rect.x(), mbi->rect.y(), mbi->rect.width(),
+ mbi->rect.height(), mbi->palette, active && down, 1, 0, &b);
+ if (active && down) {
+ newMbi.rect.translate(pixelMetric(PM_ButtonShiftHorizontal, mbi, widget),
+ pixelMetric(PM_ButtonShiftVertical, mbi, widget));
+ p->setBrushOrigin(p->brushOrigin() - QPoint(1, 1));
+ }
+ }
+ QCommonStyle::drawControl(ce, &newMbi, p, widget);
+ }
+ break;
+#endif // QT_NO_MENUBAR
+#ifndef QT_NO_TABBAR
+ case CE_TabBarTabShape:
+ if (const QStyleOptionTab *tab = qstyleoption_cast<const QStyleOptionTab *>(opt)) {
+ bool rtlHorTabs = (tab->direction == Qt::RightToLeft
+ && (tab->shape == QTabBar::RoundedNorth
+ || tab->shape == QTabBar::RoundedSouth));
+ bool selected = tab->state & State_Selected;
+ bool lastTab = ((!rtlHorTabs && tab->position == QStyleOptionTab::End)
+ || (rtlHorTabs
+ && tab->position == QStyleOptionTab::Beginning));
+ bool firstTab = ((!rtlHorTabs
+ && tab->position == QStyleOptionTab::Beginning)
+ || (rtlHorTabs
+ && tab->position == QStyleOptionTab::End));
+ bool onlyOne = tab->position == QStyleOptionTab::OnlyOneTab;
+ bool previousSelected =
+ ((!rtlHorTabs
+ && tab->selectedPosition == QStyleOptionTab::PreviousIsSelected)
+ || (rtlHorTabs
+ && tab->selectedPosition == QStyleOptionTab::NextIsSelected));
+ bool nextSelected =
+ ((!rtlHorTabs
+ && tab->selectedPosition == QStyleOptionTab::NextIsSelected)
+ || (rtlHorTabs
+ && tab->selectedPosition
+ == QStyleOptionTab::PreviousIsSelected));
+ int tabBarAlignment = styleHint(SH_TabBar_Alignment, tab, widget);
+ bool leftAligned = (!rtlHorTabs && tabBarAlignment == Qt::AlignLeft)
+ || (rtlHorTabs
+ && tabBarAlignment == Qt::AlignRight);
+
+ bool rightAligned = (!rtlHorTabs && tabBarAlignment == Qt::AlignRight)
+ || (rtlHorTabs
+ && tabBarAlignment == Qt::AlignLeft);
+
+ QColor light = tab->palette.light().color();
+ QColor midlight = tab->palette.midlight().color();
+ QColor dark = tab->palette.dark().color();
+ QColor shadow = tab->palette.shadow().color();
+ QColor background = tab->palette.background().color();
+ int borderThinkness = pixelMetric(PM_TabBarBaseOverlap, tab, widget);
+ if (selected)
+ borderThinkness /= 2;
+ QRect r2(opt->rect);
+ int x1 = r2.left();
+ int x2 = r2.right();
+ int y1 = r2.top();
+ int y2 = r2.bottom();
+ switch (tab->shape) {
+ default:
+ QCommonStyle::drawControl(ce, tab, p, widget);
+ break;
+ case QTabBar::RoundedNorth: {
+ if (!selected) {
+ y1 += 2;
+ x1 += onlyOne || firstTab ? borderThinkness : 0;
+ x2 -= onlyOne || lastTab ? borderThinkness : 0;
+ }
+
+ p->fillRect(QRect(x1 + 1, y1 + 1, (x2 - x1) - 1, (y2 - y1) - 2), tab->palette.background());
+
+ // Delete border
+ if (selected) {
+ p->fillRect(QRect(x1,y2-1,x2-x1,1), tab->palette.background());
+ p->fillRect(QRect(x1,y2,x2-x1,1), tab->palette.background());
+ }
+ // Left
+ if (firstTab || selected || onlyOne || !previousSelected) {
+ p->setPen(light);
+ p->drawLine(x1, y1 + 2, x1, y2 - ((onlyOne || firstTab) && selected && leftAligned ? 0 : borderThinkness));
+ p->drawPoint(x1 + 1, y1 + 1);
+ if (!use2000style) {
+ p->setPen(midlight);
+ p->drawLine(x1 + 1, y1 + 2, x1 + 1, y2 - ((onlyOne || firstTab) && selected && leftAligned ? 0 : borderThinkness));
+ }
+ }
+ // Top
+ {
+ int beg = x1 + (previousSelected ? 0 : 2);
+ int end = x2 - (nextSelected ? 0 : 2);
+ p->setPen(light);
+ p->drawLine(beg, y1, end, y1);
+ if (!use2000style) {
+ p->setPen(midlight);
+ p->drawLine(beg, y1 + 1, end, y1 + 1);
+ }
+ }
+ // Right
+ if (lastTab || selected || onlyOne || !nextSelected) {
+ p->setPen(shadow);
+ p->drawLine(x2, y1 + 2, x2, y2 - ((onlyOne || lastTab) && selected && rightAligned ? 0 : borderThinkness));
+ p->drawPoint(x2 - 1, y1 + 1);
+ p->setPen(dark);
+ p->drawLine(x2 - 1, y1 + 2, x2 - 1, y2 - ((onlyOne || lastTab) && selected && rightAligned ? 0 : borderThinkness));
+ }
+ break; }
+ case QTabBar::RoundedSouth: {
+ if (!selected) {
+ y2 -= 2;
+ x1 += firstTab ? borderThinkness : 0;
+ x2 -= lastTab ? borderThinkness : 0;
+ }
+
+ p->fillRect(QRect(x1 + 1, y1 + 2, (x2 - x1) - 1, (y2 - y1) - 1), tab->palette.background());
+
+ // Delete border
+ if (selected) {
+ p->fillRect(QRect(x1, y1 + 1, (x2 - 1)-x1, 1), tab->palette.background());
+ p->fillRect(QRect(x1, y1, (x2 - 1)-x1, 1), tab->palette.background());
+ }
+ // Left
+ if (firstTab || selected || onlyOne || !previousSelected) {
+ p->setPen(light);
+ p->drawLine(x1, y2 - 2, x1, y1 + ((onlyOne || firstTab) && selected && leftAligned ? 0 : borderThinkness));
+ p->drawPoint(x1 + 1, y2 - 1);
+ if (!use2000style) {
+ p->setPen(midlight);
+ p->drawLine(x1 + 1, y2 - 2, x1 + 1, y1 + ((onlyOne || firstTab) && selected && leftAligned ? 0 : borderThinkness));
+ }
+ }
+ // Bottom
+ {
+ int beg = x1 + (previousSelected ? 0 : 2);
+ int end = x2 - (nextSelected ? 0 : 2);
+ p->setPen(shadow);
+ p->drawLine(beg, y2, end, y2);
+ p->setPen(dark);
+ p->drawLine(beg, y2 - 1, end, y2 - 1);
+ }
+ // Right
+ if (lastTab || selected || onlyOne || !nextSelected) {
+ p->setPen(shadow);
+ p->drawLine(x2, y2 - 2, x2, y1 + ((onlyOne || lastTab) && selected && rightAligned ? 0 : borderThinkness));
+ p->drawPoint(x2 - 1, y2 - 1);
+ p->setPen(dark);
+ p->drawLine(x2 - 1, y2 - 2, x2 - 1, y1 + ((onlyOne || lastTab) && selected && rightAligned ? 0 : borderThinkness));
+ }
+ break; }
+ case QTabBar::RoundedWest: {
+ if (!selected) {
+ x1 += 2;
+ y1 += firstTab ? borderThinkness : 0;
+ y2 -= lastTab ? borderThinkness : 0;
+ }
+
+ p->fillRect(QRect(x1 + 1, y1 + 1, (x2 - x1) - 2, (y2 - y1) - 1), tab->palette.background());
+
+ // Delete border
+ if (selected) {
+ p->fillRect(QRect(x2 - 1, y1, 1, y2-y1), tab->palette.background());
+ p->fillRect(QRect(x2, y1, 1, y2-y1), tab->palette.background());
+ }
+ // Top
+ if (firstTab || selected || onlyOne || !previousSelected) {
+ p->setPen(light);
+ p->drawLine(x1 + 2, y1, x2 - ((onlyOne || firstTab) && selected && leftAligned ? 0 : borderThinkness), y1);
+ p->drawPoint(x1 + 1, y1 + 1);
+ if (!use2000style) {
+ p->setPen(midlight);
+ p->drawLine(x1 + 2, y1 + 1, x2 - ((onlyOne || firstTab) && selected && leftAligned ? 0 : borderThinkness), y1 + 1);
+ }
+ }
+ // Left
+ {
+ int beg = y1 + (previousSelected ? 0 : 2);
+ int end = y2 - (nextSelected ? 0 : 2);
+ p->setPen(light);
+ p->drawLine(x1, beg, x1, end);
+ if (!use2000style) {
+ p->setPen(midlight);
+ p->drawLine(x1 + 1, beg, x1 + 1, end);
+ }
+ }
+ // Bottom
+ if (lastTab || selected || onlyOne || !nextSelected) {
+ p->setPen(shadow);
+ p->drawLine(x1 + 3, y2, x2 - ((onlyOne || lastTab) && selected && rightAligned ? 0 : borderThinkness), y2);
+ p->drawPoint(x1 + 2, y2 - 1);
+ p->setPen(dark);
+ p->drawLine(x1 + 3, y2 - 1, x2 - ((onlyOne || lastTab) && selected && rightAligned ? 0 : borderThinkness), y2 - 1);
+ p->drawPoint(x1 + 1, y2 - 1);
+ p->drawPoint(x1 + 2, y2);
+ }
+ break; }
+ case QTabBar::RoundedEast: {
+ if (!selected) {
+ x2 -= 2;
+ y1 += firstTab ? borderThinkness : 0;
+ y2 -= lastTab ? borderThinkness : 0;
+ }
+
+ p->fillRect(QRect(x1 + 2, y1 + 1, (x2 - x1) - 1, (y2 - y1) - 1), tab->palette.background());
+
+ // Delete border
+ if (selected) {
+ p->fillRect(QRect(x1 + 1, y1, 1, (y2 - 1)-y1),tab->palette.background());
+ p->fillRect(QRect(x1, y1, 1, (y2-1)-y1), tab->palette.background());
+ }
+ // Top
+ if (firstTab || selected || onlyOne || !previousSelected) {
+ p->setPen(light);
+ p->drawLine(x2 - 2, y1, x1 + ((onlyOne || firstTab) && selected && leftAligned ? 0 : borderThinkness), y1);
+ p->drawPoint(x2 - 1, y1 + 1);
+ if (!use2000style) {
+ p->setPen(midlight);
+ p->drawLine(x2 - 3, y1 + 1, x1 + ((onlyOne || firstTab) && selected && leftAligned ? 0 : borderThinkness), y1 + 1);
+ p->drawPoint(x2 - 1, y1);
+ }
+ }
+ // Right
+ {
+ int beg = y1 + (previousSelected ? 0 : 2);
+ int end = y2 - (nextSelected ? 0 : 2);
+ p->setPen(shadow);
+ p->drawLine(x2, beg, x2, end);
+ p->setPen(dark);
+ p->drawLine(x2 - 1, beg, x2 - 1, end);
+ }
+ // Bottom
+ if (lastTab || selected || onlyOne || !nextSelected) {
+ p->setPen(shadow);
+ p->drawLine(x2 - 2, y2, x1 + ((onlyOne || lastTab) && selected && rightAligned ? 0 : borderThinkness), y2);
+ p->drawPoint(x2 - 1, y2 - 1);
+ p->setPen(dark);
+ p->drawLine(x2 - 2, y2 - 1, x1 + ((onlyOne || lastTab) && selected && rightAligned ? 0 : borderThinkness), y2 - 1);
+ }
+ break; }
+ }
+ }
+ break;
+#endif // QT_NO_TABBAR
+ case CE_ToolBoxTabShape:
+ qDrawShadePanel(p, opt->rect, opt->palette,
+ opt->state & (State_Sunken | State_On), 1,
+ &opt->palette.brush(QPalette::Button));
+ break;
+#ifndef QT_NO_SPLITTER
+ case CE_Splitter:
+ p->eraseRect(opt->rect);
+ break;
+#endif // QT_NO_SPLITTER
+#ifndef QT_NO_SCROLLBAR
+ case CE_ScrollBarSubLine:
+ case CE_ScrollBarAddLine: {
+ if (use2000style && (opt->state & State_Sunken)) {
+ p->setPen(opt->palette.dark().color());
+ p->setBrush(opt->palette.brush(QPalette::Button));
+ p->drawRect(opt->rect.adjusted(0, 0, -1, -1));
+ } else {
+ QStyleOption buttonOpt = *opt;
+ if (!(buttonOpt.state & State_Sunken))
+ buttonOpt.state |= State_Raised;
+ QPalette pal(opt->palette);
+ pal.setColor(QPalette::Button, opt->palette.light().color());
+ pal.setColor(QPalette::Light, opt->palette.button().color());
+ qDrawWinButton(p, opt->rect, pal, opt->state & (State_Sunken | State_On),
+ &opt->palette.brush(QPalette::Button));
+ }
+ PrimitiveElement arrow;
+ if (opt->state & State_Horizontal) {
+ if (ce == CE_ScrollBarAddLine)
+ arrow = opt->direction == Qt::LeftToRight ? PE_IndicatorArrowRight : PE_IndicatorArrowLeft;
+ else
+ arrow = opt->direction == Qt::LeftToRight ? PE_IndicatorArrowLeft : PE_IndicatorArrowRight;
+ } else {
+ if (ce == CE_ScrollBarAddLine)
+ arrow = PE_IndicatorArrowDown;
+ else
+ arrow = PE_IndicatorArrowUp;
+ }
+ QStyleOption arrowOpt = *opt;
+ arrowOpt.rect = opt->rect.adjusted(4, 4, -4, -4);
+ drawPrimitive(arrow, &arrowOpt, p, widget);
+ break; }
+ case CE_ScrollBarAddPage:
+ case CE_ScrollBarSubPage: {
+ QBrush br;
+ QBrush bg = p->background();
+ Qt::BGMode bg_mode = p->backgroundMode();
+ p->setPen(Qt::NoPen);
+ p->setBackgroundMode(Qt::OpaqueMode);
+
+ if (opt->state & State_Sunken) {
+ br = QBrush(opt->palette.shadow().color(), Qt::Dense4Pattern);
+ p->setBackground(opt->palette.dark().color());
+ p->setBrush(br);
+ } else {
+ QPixmap pm = opt->palette.brush(QPalette::Light).texture();
+ br = !pm.isNull() ? QBrush(pm) : QBrush(opt->palette.light().color(), Qt::Dense4Pattern);
+ p->setBackground(opt->palette.background().color());
+ p->setBrush(br);
+ }
+ p->drawRect(opt->rect);
+ p->setBackground(bg);
+ p->setBackgroundMode(bg_mode);
+ break; }
+ case CE_ScrollBarSlider:
+ if (!(opt->state & State_Enabled)) {
+ QPixmap pm = opt->palette.brush(QPalette::Light).texture();
+ QBrush br = !pm.isNull() ? QBrush(pm) : QBrush(opt->palette.light().color(), Qt::Dense4Pattern);
+ p->setPen(Qt::NoPen);
+ p->setBrush(br);
+ p->setBackgroundMode(Qt::OpaqueMode);
+ p->drawRect(opt->rect);
+ } else {
+ QStyleOptionButton buttonOpt;
+ buttonOpt.QStyleOption::operator=(*opt);
+ buttonOpt.state = State_Enabled | State_Raised;
+
+ QPalette pal(opt->palette);
+ pal.setColor(QPalette::Button, opt->palette.light().color());
+ pal.setColor(QPalette::Light, opt->palette.button().color());
+ qDrawWinButton(p, opt->rect, pal, false, &opt->palette.brush(QPalette::Button));
+ }
+ break;
+#endif // QT_NO_SCROLLBAR
+ case CE_HeaderSection: {
+ QBrush fill;
+ if (opt->state & State_On)
+ fill = QBrush(opt->palette.light().color(), Qt::Dense4Pattern);
+ else
+ fill = opt->palette.brush(QPalette::Button);
+
+ if (opt->state & (State_Raised | State_Sunken)) {
+ qDrawWinButton(p, opt->rect, opt->palette, opt->state & State_Sunken, &fill);
+ } else {
+ p->fillRect(opt->rect, fill);
+ }
+ break; }
+#ifndef QT_NO_TOOLBAR
+ case CE_ToolBar:
+ if (const QStyleOptionToolBar *toolbar = qstyleoption_cast<const QStyleOptionToolBar *>(opt)) {
+ QRect rect = opt->rect;
+
+ bool paintLeftBorder = true;
+ bool paintRightBorder = true;
+ bool paintBottomBorder = true;
+
+ switch (toolbar->toolBarArea){
+ case Qt::BottomToolBarArea :
+ switch(toolbar->positionOfLine){
+ case QStyleOptionToolBar::Beginning:
+ case QStyleOptionToolBar::OnlyOne:
+ paintBottomBorder = false;
+ default:
+ break;
+ }
+ case Qt::TopToolBarArea :
+ switch(toolbar->positionWithinLine){
+ case QStyleOptionToolBar::Beginning:
+ paintLeftBorder = false;
+ break;
+ case QStyleOptionToolBar::End:
+ paintRightBorder = false;
+ break;
+ case QStyleOptionToolBar::OnlyOne:
+ paintRightBorder = false;
+ paintLeftBorder = false;
+ default:
+ break;
+ }
+ if(QApplication::layoutDirection() == Qt::RightToLeft){ //reverse layout changes the order of Beginning/end
+ bool tmp = paintLeftBorder;
+ paintRightBorder=paintLeftBorder;
+ paintLeftBorder=tmp;
+ }
+ break;
+ case Qt::RightToolBarArea :
+ switch (toolbar->positionOfLine){
+ case QStyleOptionToolBar::Beginning:
+ case QStyleOptionToolBar::OnlyOne:
+ paintRightBorder = false;
+ break;
+ default:
+ break;
+ }
+ break;
+ case Qt::LeftToolBarArea :
+ switch (toolbar->positionOfLine){
+ case QStyleOptionToolBar::Beginning:
+ case QStyleOptionToolBar::OnlyOne:
+ paintLeftBorder = false;
+ break;
+ default:
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+
+
+ //draw top border
+ p->setPen(QPen(opt->palette.light().color()));
+ p->drawLine(rect.topLeft().x(),
+ rect.topLeft().y(),
+ rect.topRight().x(),
+ rect.topRight().y());
+
+ if (paintLeftBorder){
+ p->setPen(QPen(opt->palette.light().color()));
+ p->drawLine(rect.topLeft().x(),
+ rect.topLeft().y(),
+ rect.bottomLeft().x(),
+ rect.bottomLeft().y());
+ }
+
+ if (paintRightBorder){
+ p->setPen(QPen(opt->palette.dark().color()));
+ p->drawLine(rect.topRight().x(),
+ rect.topRight().y(),
+ rect.bottomRight().x(),
+ rect.bottomRight().y());
+ }
+
+ if (paintBottomBorder){
+ p->setPen(QPen(opt->palette.dark().color()));
+ p->drawLine(rect.bottomLeft().x(),
+ rect.bottomLeft().y(),
+ rect.bottomRight().x(),
+ rect.bottomRight().y());
+ }
+ }
+ break;
+
+
+#endif // QT_NO_TOOLBAR
+#ifndef QT_NO_PROGRESSBAR
+ case CE_ProgressBarContents:
+ if (const QStyleOptionProgressBar *pb = qstyleoption_cast<const QStyleOptionProgressBar *>(opt)) {
+
+ QRect rect = pb->rect;
+ bool vertical = false;
+ bool inverted = false;
+
+ // Get extra style options if version 2
+ const QStyleOptionProgressBarV2 *pb2 = qstyleoption_cast<const QStyleOptionProgressBarV2 *>(opt);
+ if (pb2) {
+ vertical = (pb2->orientation == Qt::Vertical);
+ inverted = pb2->invertedAppearance;
+ }
+ QMatrix m;
+ if (vertical) {
+ rect = QRect(rect.y(), rect.x(), rect.height(), rect.width()); // flip width and height
+ m.rotate(90);
+ m.translate(0, -(rect.height() + rect.y()*2));
+ }
+ QPalette pal2 = pb->palette;
+ // Correct the highlight color if it is the same as the background
+ if (pal2.highlight() == pal2.background())
+ pal2.setColor(QPalette::Highlight, pb->palette.color(QPalette::Active,
+ QPalette::Highlight));
+ bool reverse = ((!vertical && (pb->direction == Qt::RightToLeft)) || vertical);
+ if (inverted)
+ reverse = !reverse;
+ int w = rect.width();
+ if (pb->minimum == 0 && pb->maximum == 0) {
+ Q_D(const QWindowsStyle);
+ const int unit_width = pixelMetric(PM_ProgressBarChunkWidth, pb, widget);
+ QStyleOptionProgressBarV2 pbBits = *pb;
+ Q_ASSERT(unit_width >0);
+
+ pbBits.rect = rect;
+ pbBits.palette = pal2;
+
+ int chunkCount = w / unit_width + 1;
+ int step = d->animateStep%chunkCount;
+ int chunksInRow = 5;
+ int myY = pbBits.rect.y();
+ int myHeight = pbBits.rect.height();
+ int chunksToDraw = chunksInRow;
+
+ if(step > chunkCount - 5)chunksToDraw = (chunkCount - step);
+ p->save();
+ p->setClipRect(m.mapRect(QRectF(rect)).toRect());
+
+ int x0 = reverse ? rect.left() + rect.width() - unit_width*(step) - unit_width : rect.left() + unit_width * step;
+ int x = 0;
+
+ for (int i = 0; i < chunksToDraw ; ++i) {
+ pbBits.rect.setRect(x0 + x, myY, unit_width, myHeight);
+ pbBits.rect = m.mapRect(QRectF(pbBits.rect)).toRect();
+ drawPrimitive(PE_IndicatorProgressChunk, &pbBits, p, widget);
+ x += reverse ? -unit_width : unit_width;
+ }
+ //Draw wrap-around chunks
+ if( step > chunkCount-5){
+ x0 = reverse ? rect.left() + rect.width() - unit_width : rect.left() ;
+ x = 0;
+ int chunksToDraw = step - (chunkCount - chunksInRow);
+ for (int i = 0; i < chunksToDraw ; ++i) {
+ pbBits.rect.setRect(x0 + x, myY, unit_width, myHeight);
+ pbBits.rect = m.mapRect(QRectF(pbBits.rect)).toRect();
+ drawPrimitive(PE_IndicatorProgressChunk, &pbBits, p, widget);
+ x += reverse ? -unit_width : unit_width;
+ }
+ }
+ p->restore(); //restore state
+ }
+ else {
+ QCommonStyle::drawControl(ce, opt, p, widget);
+ }
+ }
+ break;
+#endif // QT_NO_PROGRESSBAR
+
+#ifndef QT_NO_DOCKWIDGET
+ case CE_DockWidgetTitle:
+
+ if (const QStyleOptionDockWidget *dwOpt = qstyleoption_cast<const QStyleOptionDockWidget *>(opt)) {
+ Q_D(const QWindowsStyle);
+
+ const QStyleOptionDockWidgetV2 *v2
+ = qstyleoption_cast<const QStyleOptionDockWidgetV2*>(opt);
+ bool verticalTitleBar = v2 == 0 ? false : v2->verticalTitleBar;
+
+ QRect rect = dwOpt->rect;
+ QRect r = rect;
+
+ if (verticalTitleBar) {
+ QSize s = r.size();
+ s.transpose();
+ r.setSize(s);
+
+ p->save();
+ p->translate(r.left(), r.top() + r.width());
+ p->rotate(-90);
+ p->translate(-r.left(), -r.top());
+ }
+
+ bool floating = false;
+ bool active = dwOpt->state & State_Active;
+ int menuOffset = 0; //used to center text when floated
+ QColor inactiveCaptionTextColor = d->inactiveCaptionText;
+ if (dwOpt->movable) {
+ QColor left, right;
+
+ //Titlebar gradient
+ if (widget && widget->isWindow()) {
+ floating = true;
+ if (active) {
+ left = d->activeCaptionColor;
+ right = d->activeGradientCaptionColor;
+ } else {
+ left = d->inactiveCaptionColor;
+ right = d->inactiveGradientCaptionColor;
+ }
+ menuOffset = 2;
+ QBrush fillBrush(left);
+ if (left != right) {
+ QPoint p1(r.x(), r.top() + r.height()/2);
+ QPoint p2(rect.right(), r.top() + r.height()/2);
+ QLinearGradient lg(p1, p2);
+ lg.setColorAt(0, left);
+ lg.setColorAt(1, right);
+ fillBrush = lg;
+ }
+ p->fillRect(r.adjusted(0, 0, 0, -3), fillBrush);
+ }
+ p->setPen(dwOpt->palette.color(QPalette::Light));
+ if (!widget || !widget->isWindow()) {
+ p->drawLine(r.topLeft(), r.topRight());
+ p->setPen(dwOpt->palette.color(QPalette::Dark));
+ p->drawLine(r.bottomLeft(), r.bottomRight()); }
+ }
+ if (!dwOpt->title.isEmpty()) {
+ QFont oldFont = p->font();
+ if (floating) {
+ QFont font = oldFont;
+ font.setBold(true);
+ p->setFont(font);
+ }
+ QPalette palette = dwOpt->palette;
+ palette.setColor(QPalette::Window, inactiveCaptionTextColor);
+ QRect titleRect = subElementRect(SE_DockWidgetTitleBarText, opt, widget);
+ if (verticalTitleBar) {
+ titleRect = QRect(r.left() + rect.bottom()
+ - titleRect.bottom(),
+ r.top() + titleRect.left() - rect.left(),
+ titleRect.height(), titleRect.width());
+ }
+ drawItemText(p, titleRect,
+ Qt::AlignLeft | Qt::AlignVCenter | Qt::TextShowMnemonic, palette,
+ dwOpt->state & State_Enabled, dwOpt->title,
+ floating ? (active ? QPalette::BrightText : QPalette::Window) : QPalette::WindowText);
+ p->setFont(oldFont);
+ }
+ if (verticalTitleBar)
+ p->restore();
+ }
+ return;
+#endif // QT_NO_DOCKWIDGET
+ default:
+ QCommonStyle::drawControl(ce, opt, p, widget);
+ }
+}
+
+/*! \reimp */
+QRect QWindowsStyle::subElementRect(SubElement sr, const QStyleOption *opt, const QWidget *w) const
+{
+ QRect r;
+ switch (sr) {
+ case SE_SliderFocusRect:
+ case SE_ToolBoxTabContents:
+ r = visualRect(opt->direction, opt->rect, opt->rect);
+ break;
+ case SE_DockWidgetTitleBarText: {
+ r = QCommonStyle::subElementRect(sr, opt, w);
+ const QStyleOptionDockWidgetV2 *v2
+ = qstyleoption_cast<const QStyleOptionDockWidgetV2*>(opt);
+ bool verticalTitleBar = v2 == 0 ? false : v2->verticalTitleBar;
+ int m = pixelMetric(PM_DockWidgetTitleMargin, opt, w);
+ if (verticalTitleBar) {
+ r.adjust(0, 0, 0, -m);
+ } else {
+ if (QApplication::layoutDirection() == Qt::LeftToRight)
+ r.adjust(m, 0, 0, 0);
+ else
+ r.adjust(0, 0, -m, 0);
+ }
+ break;
+ }
+ case SE_ProgressBarContents:
+ r = QCommonStyle::subElementRect(SE_ProgressBarGroove, opt, w);
+ r.adjust(3, 3, -3, -3);
+ break;
+ default:
+ r = QCommonStyle::subElementRect(sr, opt, w);
+ }
+ return r;
+}
+
+#ifdef QT3_SUPPORT
+Q_GLOBAL_STATIC_WITH_ARGS(QBitmap, globalVerticalLine, (1, 129))
+Q_GLOBAL_STATIC_WITH_ARGS(QBitmap, globalHorizontalLine, (128, 1))
+#endif
+
+/*! \reimp */
+void QWindowsStyle::drawComplexControl(ComplexControl cc, const QStyleOptionComplex *opt,
+ QPainter *p, const QWidget *widget) const
+{
+ switch (cc) {
+#ifndef QT_NO_SLIDER
+ case CC_Slider:
+ if (const QStyleOptionSlider *slider = qstyleoption_cast<const QStyleOptionSlider *>(opt)) {
+ int thickness = pixelMetric(PM_SliderControlThickness, slider, widget);
+ int len = pixelMetric(PM_SliderLength, slider, widget);
+ int ticks = slider->tickPosition;
+ QRect groove = subControlRect(CC_Slider, slider, SC_SliderGroove, widget);
+ QRect handle = subControlRect(CC_Slider, slider, SC_SliderHandle, widget);
+
+ if ((slider->subControls & SC_SliderGroove) && groove.isValid()) {
+ int mid = thickness / 2;
+
+ if (ticks & QSlider::TicksAbove)
+ mid += len / 8;
+ if (ticks & QSlider::TicksBelow)
+ mid -= len / 8;
+
+ p->setPen(slider->palette.shadow().color());
+ if (slider->orientation == Qt::Horizontal) {
+ qDrawWinPanel(p, groove.x(), groove.y() + mid - 2,
+ groove.width(), 4, slider->palette, true);
+ p->drawLine(groove.x() + 1, groove.y() + mid - 1,
+ groove.x() + groove.width() - 3, groove.y() + mid - 1);
+ } else {
+ qDrawWinPanel(p, groove.x() + mid - 2, groove.y(),
+ 4, groove.height(), slider->palette, true);
+ p->drawLine(groove.x() + mid - 1, groove.y() + 1,
+ groove.x() + mid - 1, groove.y() + groove.height() - 3);
+ }
+ }
+
+ if (slider->subControls & SC_SliderTickmarks) {
+ QStyleOptionSlider tmpSlider = *slider;
+ tmpSlider.subControls = SC_SliderTickmarks;
+ QCommonStyle::drawComplexControl(cc, &tmpSlider, p, widget);
+ }
+
+ if (slider->subControls & SC_SliderHandle) {
+ // 4444440
+ // 4333310
+ // 4322210
+ // 4322210
+ // 4322210
+ // 4322210
+ // *43210*
+ // **410**
+ // ***0***
+ const QColor c0 = slider->palette.shadow().color();
+ const QColor c1 = slider->palette.dark().color();
+ // const QColor c2 = g.button();
+ const QColor c3 = slider->palette.midlight().color();
+ const QColor c4 = slider->palette.light().color();
+ QBrush handleBrush;
+
+ if (slider->state & State_Enabled) {
+ handleBrush = slider->palette.color(QPalette::Button);
+ } else {
+ handleBrush = QBrush(slider->palette.color(QPalette::Button),
+ Qt::Dense4Pattern);
+ }
+
+
+ int x = handle.x(), y = handle.y(),
+ wi = handle.width(), he = handle.height();
+
+ int x1 = x;
+ int x2 = x+wi-1;
+ int y1 = y;
+ int y2 = y+he-1;
+
+ Qt::Orientation orient = slider->orientation;
+ bool tickAbove = slider->tickPosition == QSlider::TicksAbove;
+ bool tickBelow = slider->tickPosition == QSlider::TicksBelow;
+
+ if (slider->state & State_HasFocus) {
+ QStyleOptionFocusRect fropt;
+ fropt.QStyleOption::operator=(*slider);
+ fropt.rect = subElementRect(SE_SliderFocusRect, slider, widget);
+ drawPrimitive(PE_FrameFocusRect, &fropt, p, widget);
+ }
+
+ if ((tickAbove && tickBelow) || (!tickAbove && !tickBelow)) {
+ Qt::BGMode oldMode = p->backgroundMode();
+ p->setBackgroundMode(Qt::OpaqueMode);
+ qDrawWinButton(p, QRect(x, y, wi, he), slider->palette, false,
+ &handleBrush);
+ p->setBackgroundMode(oldMode);
+ return;
+ }
+
+ QSliderDirection dir;
+
+ if (orient == Qt::Horizontal)
+ if (tickAbove)
+ dir = SlUp;
+ else
+ dir = SlDown;
+ else
+ if (tickAbove)
+ dir = SlLeft;
+ else
+ dir = SlRight;
+
+ QPolygon a;
+
+ int d = 0;
+ switch (dir) {
+ case SlUp:
+ y1 = y1 + wi/2;
+ d = (wi + 1) / 2 - 1;
+ a.setPoints(5, x1,y1, x1,y2, x2,y2, x2,y1, x1+d,y1-d);
+ break;
+ case SlDown:
+ y2 = y2 - wi/2;
+ d = (wi + 1) / 2 - 1;
+ a.setPoints(5, x1,y1, x1,y2, x1+d,y2+d, x2,y2, x2,y1);
+ break;
+ case SlLeft:
+ d = (he + 1) / 2 - 1;
+ x1 = x1 + he/2;
+ a.setPoints(5, x1,y1, x1-d,y1+d, x1,y2, x2,y2, x2,y1);
+ break;
+ case SlRight:
+ d = (he + 1) / 2 - 1;
+ x2 = x2 - he/2;
+ a.setPoints(5, x1,y1, x1,y2, x2,y2, x2+d,y1+d, x2,y1);
+ break;
+ }
+
+ QBrush oldBrush = p->brush();
+ p->setPen(Qt::NoPen);
+ p->setBrush(handleBrush);
+ Qt::BGMode oldMode = p->backgroundMode();
+ p->setBackgroundMode(Qt::OpaqueMode);
+ p->drawRect(x1, y1, x2-x1+1, y2-y1+1);
+ p->drawPolygon(a);
+ p->setBrush(oldBrush);
+ p->setBackgroundMode(oldMode);
+
+ if (dir != SlUp) {
+ p->setPen(c4);
+ p->drawLine(x1, y1, x2, y1);
+ p->setPen(c3);
+ p->drawLine(x1, y1+1, x2, y1+1);
+ }
+ if (dir != SlLeft) {
+ p->setPen(c3);
+ p->drawLine(x1+1, y1+1, x1+1, y2);
+ p->setPen(c4);
+ p->drawLine(x1, y1, x1, y2);
+ }
+ if (dir != SlRight) {
+ p->setPen(c0);
+ p->drawLine(x2, y1, x2, y2);
+ p->setPen(c1);
+ p->drawLine(x2-1, y1+1, x2-1, y2-1);
+ }
+ if (dir != SlDown) {
+ p->setPen(c0);
+ p->drawLine(x1, y2, x2, y2);
+ p->setPen(c1);
+ p->drawLine(x1+1, y2-1, x2-1, y2-1);
+ }
+
+ switch (dir) {
+ case SlUp:
+ p->setPen(c4);
+ p->drawLine(x1, y1, x1+d, y1-d);
+ p->setPen(c0);
+ d = wi - d - 1;
+ p->drawLine(x2, y1, x2-d, y1-d);
+ d--;
+ p->setPen(c3);
+ p->drawLine(x1+1, y1, x1+1+d, y1-d);
+ p->setPen(c1);
+ p->drawLine(x2-1, y1, x2-1-d, y1-d);
+ break;
+ case SlDown:
+ p->setPen(c4);
+ p->drawLine(x1, y2, x1+d, y2+d);
+ p->setPen(c0);
+ d = wi - d - 1;
+ p->drawLine(x2, y2, x2-d, y2+d);
+ d--;
+ p->setPen(c3);
+ p->drawLine(x1+1, y2, x1+1+d, y2+d);
+ p->setPen(c1);
+ p->drawLine(x2-1, y2, x2-1-d, y2+d);
+ break;
+ case SlLeft:
+ p->setPen(c4);
+ p->drawLine(x1, y1, x1-d, y1+d);
+ p->setPen(c0);
+ d = he - d - 1;
+ p->drawLine(x1, y2, x1-d, y2-d);
+ d--;
+ p->setPen(c3);
+ p->drawLine(x1, y1+1, x1-d, y1+1+d);
+ p->setPen(c1);
+ p->drawLine(x1, y2-1, x1-d, y2-1-d);
+ break;
+ case SlRight:
+ p->setPen(c4);
+ p->drawLine(x2, y1, x2+d, y1+d);
+ p->setPen(c0);
+ d = he - d - 1;
+ p->drawLine(x2, y2, x2+d, y2-d);
+ d--;
+ p->setPen(c3);
+ p->drawLine(x2, y1+1, x2+d, y1+1+d);
+ p->setPen(c1);
+ p->drawLine(x2, y2-1, x2+d, y2-1-d);
+ break;
+ }
+ }
+ }
+ break;
+#endif // QT_NO_SLIDER
+#ifndef QT_NO_SCROLLBAR
+ case CC_ScrollBar:
+ if (const QStyleOptionSlider *scrollbar = qstyleoption_cast<const QStyleOptionSlider *>(opt)) {
+ QStyleOptionSlider newScrollbar = *scrollbar;
+ if (scrollbar->minimum == scrollbar->maximum)
+ newScrollbar.state &= ~State_Enabled; //do not draw the slider.
+ QCommonStyle::drawComplexControl(cc, &newScrollbar, p, widget);
+ }
+ break;
+#endif // QT_NO_SCROLLBAR
+#ifdef QT3_SUPPORT
+ case CC_Q3ListView:
+ if (const QStyleOptionQ3ListView *lv = qstyleoption_cast<const QStyleOptionQ3ListView *>(opt)) {
+ int i;
+ if (lv->subControls & SC_Q3ListView)
+ QCommonStyle::drawComplexControl(cc, lv, p, widget);
+ if (lv->subControls & (SC_Q3ListViewBranch | SC_Q3ListViewExpand)) {
+ if (lv->items.isEmpty())
+ break;
+ QStyleOptionQ3ListViewItem item = lv->items.at(0);
+ int y = lv->rect.y();
+ int c;
+ int dotoffset = 0;
+ QPolygon dotlines;
+ if ((lv->activeSubControls & SC_All) && (lv->subControls & SC_Q3ListViewExpand)) {
+ c = 2;
+ dotlines.resize(2);
+ dotlines[0] = QPoint(lv->rect.right(), lv->rect.top());
+ dotlines[1] = QPoint(lv->rect.right(), lv->rect.bottom());
+ } else {
+ int linetop = 0, linebot = 0;
+ // each branch needs at most two lines, ie. four end points
+ dotoffset = (item.itemY + item.height - y) % 2;
+ dotlines.resize(item.childCount * 4);
+ c = 0;
+
+ // skip the stuff above the exposed rectangle
+ for (i = 1; i < lv->items.size(); ++i) {
+ QStyleOptionQ3ListViewItem child = lv->items.at(i);
+ if (child.height + y > 0)
+ break;
+ y += child.totalHeight;
+ }
+ int bx = lv->rect.width() / 2;
+
+ // paint stuff in the magical area
+ while (i < lv->items.size() && y < lv->rect.height()) {
+ QStyleOptionQ3ListViewItem child = lv->items.at(i);
+ if (child.features & QStyleOptionQ3ListViewItem::Visible) {
+ int lh;
+ if (!(item.features & QStyleOptionQ3ListViewItem::MultiLine))
+ lh = child.height;
+ else
+ lh = p->fontMetrics().height() + 2 * lv->itemMargin;
+ lh = qMax(lh, QApplication::globalStrut().height());
+ if (lh % 2 > 0)
+ ++lh;
+ linebot = y + lh / 2;
+ if (child.features & QStyleOptionQ3ListViewItem::Expandable
+ || (child.childCount > 0 && child.height > 0)) {
+ // needs a box
+ p->setPen(lv->palette.mid().color());
+ p->drawRect(bx - 4, linebot - 4, 8, 8);
+ // plus or minus
+ p->setPen(lv->palette.text().color());
+ p->drawLine(bx - 2, linebot, bx + 2, linebot);
+ if (!(child.state & State_Open))
+ p->drawLine(bx, linebot - 2, bx, linebot + 2);
+ // dotlinery
+ p->setPen(lv->palette.mid().color());
+ dotlines[c++] = QPoint(bx, linetop);
+ dotlines[c++] = QPoint(bx, linebot - 4);
+ dotlines[c++] = QPoint(bx + 5, linebot);
+ dotlines[c++] = QPoint(lv->rect.width(), linebot);
+ linetop = linebot + 5;
+ } else {
+ // just dotlinery
+ dotlines[c++] = QPoint(bx+1, linebot -1);
+ dotlines[c++] = QPoint(lv->rect.width(), linebot -1);
+ }
+ y += child.totalHeight;
+ }
+ ++i;
+ }
+
+ // Expand line height to edge of rectangle if there's any
+ // visible child below
+ while (i < lv->items.size() && lv->items.at(i).height <= 0)
+ ++i;
+ if (i < lv->items.size())
+ linebot = lv->rect.height();
+
+ if (linetop < linebot) {
+ dotlines[c++] = QPoint(bx, linetop);
+ dotlines[c++] = QPoint(bx, linebot);
+ }
+ }
+ p->setPen(lv->palette.text().color());
+ QBitmap *verticalLine = globalVerticalLine();
+ QBitmap *horizontalLine = globalHorizontalLine();
+ static bool isInit = false;
+ if (!isInit) {
+ isInit = true;
+ // make 128*1 and 1*128 bitmaps that can be used for
+ // drawing the right sort of lines.
+ verticalLine->clear();
+ horizontalLine->clear();
+ QPolygon a(64);
+ QPainter p;
+ p.begin(verticalLine);
+ for(i = 0; i < 64; ++i)
+ a.setPoint(i, 0, i * 2 + 1);
+ p.setPen(Qt::color1);
+ p.drawPoints(a);
+ p.end();
+ QApplication::flush();
+ verticalLine->setMask(*verticalLine);
+ p.begin(horizontalLine);
+ for(i = 0; i < 64; ++i)
+ a.setPoint(i, i * 2 + 1, 0);
+ p.setPen(Qt::color1);
+ p.drawPoints(a);
+ p.end();
+ QApplication::flush();
+ horizontalLine->setMask(*horizontalLine);
+ }
+
+ int line; // index into dotlines
+ if (lv->subControls & SC_Q3ListViewBranch) for(line = 0; line < c; line += 2) {
+ // assumptions here: lines are horizontal or vertical.
+ // lines always start with the numerically lowest
+ // coordinate.
+
+ // point ... relevant coordinate of current point
+ // end ..... same coordinate of the end of the current line
+ // other ... the other coordinate of the current point/line
+ if (dotlines[line].y() == dotlines[line+1].y()) {
+ int end = dotlines[line + 1].x();
+ int point = dotlines[line].x();
+ int other = dotlines[line].y();
+ while (point < end) {
+ int i = 128;
+ if (i + point > end)
+ i = end-point;
+ p->drawPixmap(point, other, *horizontalLine, 0, 0, i, 1);
+ point += i;
+ }
+ } else {
+ int end = dotlines[line + 1].y();
+ int point = dotlines[line].y();
+ int other = dotlines[line].x();
+ int pixmapoffset = ((point & 1) != dotoffset) ? 1 : 0;
+ while(point < end) {
+ int i = 128;
+ if (i + point > end)
+ i = end-point;
+ p->drawPixmap(other, point, *verticalLine, 0, pixmapoffset, 1, i);
+ point += i;
+ }
+ }
+ }
+ }
+ }
+ break;
+#endif // QT3_SUPPORT
+#ifndef QT_NO_COMBOBOX
+ case CC_ComboBox:
+ if (const QStyleOptionComboBox *cmb = qstyleoption_cast<const QStyleOptionComboBox *>(opt)) {
+ QBrush editBrush = cmb->palette.brush(QPalette::Base);
+ if ((cmb->subControls & SC_ComboBoxFrame)) {
+ if (cmb->frame) {
+ QPalette shadePal = opt->palette;
+ if (use2000style)
+ shadePal.setColor(QPalette::Midlight, shadePal.button().color());
+ qDrawWinPanel(p, opt->rect, shadePal, true, &editBrush);
+ }
+ else {
+ p->fillRect(opt->rect, editBrush);
+ }
+ }
+ if (cmb->subControls & SC_ComboBoxArrow) {
+ State flags = State_None;
+
+ QRect ar = subControlRect(CC_ComboBox, cmb, SC_ComboBoxArrow, widget);
+ bool sunkenArrow = cmb->activeSubControls == SC_ComboBoxArrow
+ && cmb->state & State_Sunken;
+ if (sunkenArrow) {
+ p->setPen(cmb->palette.dark().color());
+ p->setBrush(cmb->palette.brush(QPalette::Button));
+ p->drawRect(ar.adjusted(0,0,-1,-1));
+ } else {
+ // Make qDrawWinButton use the right colors for drawing the shade of the button
+ QPalette pal(cmb->palette);
+ pal.setColor(QPalette::Button, cmb->palette.light().color());
+ pal.setColor(QPalette::Light, cmb->palette.button().color());
+ qDrawWinButton(p, ar, pal, false,
+ &cmb->palette.brush(QPalette::Button));
+ }
+
+ ar.adjust(2, 2, -2, -2);
+ if (opt->state & State_Enabled)
+ flags |= State_Enabled;
+
+ if (sunkenArrow)
+ flags |= State_Sunken;
+ QStyleOption arrowOpt(0);
+ arrowOpt.rect = ar.adjusted(1, 1, -1, -1);
+ arrowOpt.palette = cmb->palette;
+ arrowOpt.state = flags;
+ drawPrimitive(PE_IndicatorArrowDown, &arrowOpt, p, widget);
+ }
+
+ if (cmb->subControls & SC_ComboBoxEditField) {
+ QRect re = subControlRect(CC_ComboBox, cmb, SC_ComboBoxEditField, widget);
+ if (cmb->state & State_HasFocus && !cmb->editable)
+ p->fillRect(re.x(), re.y(), re.width(), re.height(),
+ cmb->palette.brush(QPalette::Highlight));
+
+ if (cmb->state & State_HasFocus) {
+ p->setPen(cmb->palette.highlightedText().color());
+ p->setBackground(cmb->palette.highlight());
+
+ } else {
+ p->setPen(cmb->palette.text().color());
+ p->setBackground(cmb->palette.background());
+ }
+
+ if (cmb->state & State_HasFocus && !cmb->editable) {
+ QStyleOptionFocusRect focus;
+ focus.QStyleOption::operator=(*cmb);
+ focus.rect = subElementRect(SE_ComboBoxFocusRect, cmb, widget);
+ focus.state |= State_FocusAtBorder;
+ focus.backgroundColor = cmb->palette.highlight().color();
+ drawPrimitive(PE_FrameFocusRect, &focus, p, widget);
+ }
+ }
+ }
+ break;
+#endif // QT_NO_COMBOBOX
+#ifndef QT_NO_SPINBOX
+ case CC_SpinBox:
+ if (const QStyleOptionSpinBox *sb = qstyleoption_cast<const QStyleOptionSpinBox *>(opt)) {
+ QStyleOptionSpinBox copy = *sb;
+ PrimitiveElement pe;
+ bool enabled = opt->state & State_Enabled;
+ if (sb->frame && (sb->subControls & SC_SpinBoxFrame)) {
+ QBrush editBrush = sb->palette.brush(QPalette::Base);
+ QRect r = subControlRect(CC_SpinBox, sb, SC_SpinBoxFrame, widget);
+ QPalette shadePal = sb->palette;
+ if (use2000style)
+ shadePal.setColor(QPalette::Midlight, shadePal.button().color());
+ qDrawWinPanel(p, r, shadePal, true, &editBrush);
+ }
+
+ QPalette shadePal(opt->palette);
+ shadePal.setColor(QPalette::Button, opt->palette.light().color());
+ shadePal.setColor(QPalette::Light, opt->palette.button().color());
+
+ if (sb->subControls & SC_SpinBoxUp) {
+ copy.subControls = SC_SpinBoxUp;
+ QPalette pal2 = sb->palette;
+ if (!(sb->stepEnabled & QAbstractSpinBox::StepUpEnabled)) {
+ pal2.setCurrentColorGroup(QPalette::Disabled);
+ copy.state &= ~State_Enabled;
+ }
+
+ copy.palette = pal2;
+
+ if (sb->activeSubControls == SC_SpinBoxUp && (sb->state & State_Sunken)) {
+ copy.state |= State_On;
+ copy.state |= State_Sunken;
+ } else {
+ copy.state |= State_Raised;
+ copy.state &= ~State_Sunken;
+ }
+ pe = (sb->buttonSymbols == QAbstractSpinBox::PlusMinus ? PE_IndicatorSpinPlus
+ : PE_IndicatorSpinUp);
+
+ copy.rect = subControlRect(CC_SpinBox, sb, SC_SpinBoxUp, widget);
+ qDrawWinButton(p, copy.rect, shadePal, copy.state & (State_Sunken | State_On),
+ &copy.palette.brush(QPalette::Button));
+ copy.rect.adjust(4, 1, -5, -1);
+ if (!enabled || !(sb->stepEnabled & QAbstractSpinBox::StepUpEnabled) ) {
+ QStyleOptionSpinBox lightCopy = copy;
+ lightCopy.rect.adjust(1, 1, 1, 1);
+ lightCopy.palette.setBrush(QPalette::ButtonText, copy.palette.light());
+ drawPrimitive(pe, &lightCopy, p, widget);
+ }
+ drawPrimitive(pe, &copy, p, widget);
+ }
+
+ if (sb->subControls & SC_SpinBoxDown) {
+ copy.subControls = SC_SpinBoxDown;
+ copy.state = sb->state;
+ QPalette pal2 = sb->palette;
+ if (!(sb->stepEnabled & QAbstractSpinBox::StepDownEnabled)) {
+ pal2.setCurrentColorGroup(QPalette::Disabled);
+ copy.state &= ~State_Enabled;
+ }
+ copy.palette = pal2;
+
+ if (sb->activeSubControls == SC_SpinBoxDown && (sb->state & State_Sunken)) {
+ copy.state |= State_On;
+ copy.state |= State_Sunken;
+ } else {
+ copy.state |= State_Raised;
+ copy.state &= ~State_Sunken;
+ }
+ pe = (sb->buttonSymbols == QAbstractSpinBox::PlusMinus ? PE_IndicatorSpinMinus
+ : PE_IndicatorSpinDown);
+
+ copy.rect = subControlRect(CC_SpinBox, sb, SC_SpinBoxDown, widget);
+ qDrawWinButton(p, copy.rect, shadePal, copy.state & (State_Sunken | State_On),
+ &copy.palette.brush(QPalette::Button));
+ copy.rect.adjust(4, 0, -5, -1);
+ if (!enabled || !(sb->stepEnabled & QAbstractSpinBox::StepDownEnabled) ) {
+ QStyleOptionSpinBox lightCopy = copy;
+ lightCopy.rect.adjust(1, 1, 1, 1);
+ lightCopy.palette.setBrush(QPalette::ButtonText, copy.palette.light());
+ drawPrimitive(pe, &lightCopy, p, widget);
+ }
+ drawPrimitive(pe, &copy, p, widget);
+ }
+ }
+ break;
+#endif // QT_NO_SPINBOX
+ default:
+ QCommonStyle::drawComplexControl(cc, opt, p, widget);
+ }
+}
+
+/*! \reimp */
+QSize QWindowsStyle::sizeFromContents(ContentsType ct, const QStyleOption *opt,
+ const QSize &csz, const QWidget *widget) const
+{
+ QSize sz(csz);
+ switch (ct) {
+ case CT_PushButton:
+ if (const QStyleOptionButton *btn = qstyleoption_cast<const QStyleOptionButton *>(opt)) {
+ sz = QCommonStyle::sizeFromContents(ct, opt, csz, widget);
+ int w = sz.width(),
+ h = sz.height();
+ int defwidth = 0;
+ if (btn->features & QStyleOptionButton::AutoDefaultButton)
+ defwidth = 2 * pixelMetric(PM_ButtonDefaultIndicator, btn, widget);
+#ifndef QT_QWS_SMALL_PUSHBUTTON
+ if (w < 75 + defwidth && !btn->text.isEmpty())
+ w = 75 + defwidth;
+ if (h < 23 + defwidth)
+ h = 23 + defwidth;
+#endif
+ sz = QSize(w, h);
+ }
+ break;
+#ifndef QT_NO_MENU
+ case CT_MenuItem:
+ if (const QStyleOptionMenuItem *mi = qstyleoption_cast<const QStyleOptionMenuItem *>(opt)) {
+ int w = sz.width();
+ sz = QCommonStyle::sizeFromContents(ct, opt, csz, widget);
+
+ if (mi->menuItemType == QStyleOptionMenuItem::Separator) {
+ sz = QSize(10, windowsSepHeight);
+ }
+ else if (mi->icon.isNull()) {
+ sz.setHeight(sz.height() - 2);
+ w -= 6;
+ }
+
+ if (mi->menuItemType != QStyleOptionMenuItem::Separator && !mi->icon.isNull()) {
+ int iconExtent = pixelMetric(PM_SmallIconSize, opt, widget);
+ sz.setHeight(qMax(sz.height(),
+ mi->icon.actualSize(QSize(iconExtent, iconExtent)).height()
+ + 2 * windowsItemFrame));
+ }
+ int maxpmw = mi->maxIconWidth;
+ int tabSpacing = use2000style ? 20 :windowsTabSpacing;
+ if (mi->text.contains(QLatin1Char('\t')))
+ w += tabSpacing;
+ else if (mi->menuItemType == QStyleOptionMenuItem::SubMenu)
+ w += 2 * windowsArrowHMargin;
+ else if (mi->menuItemType == QStyleOptionMenuItem::DefaultItem) {
+ // adjust the font and add the difference in size.
+ // it would be better if the font could be adjusted in the initStyleOption qmenu func!!
+ QFontMetrics fm(mi->font);
+ QFont fontBold = mi->font;
+ fontBold.setBold(true);
+ QFontMetrics fmBold(fontBold);
+ w += fmBold.width(mi->text) - fm.width(mi->text);
+ }
+
+ int checkcol = qMax(maxpmw, windowsCheckMarkWidth); // Windows always shows a check column
+ w += checkcol;
+ w += windowsRightBorder + 10;
+ sz.setWidth(w);
+ }
+ break;
+#endif // QT_NO_MENU
+#ifndef QT_NO_MENUBAR
+ case CT_MenuBarItem:
+ if (!sz.isEmpty())
+ sz += QSize(windowsItemHMargin * 4, windowsItemVMargin * 2);
+ break;
+#endif
+ // Otherwise, fall through
+ case CT_ToolButton:
+ if (qstyleoption_cast<const QStyleOptionToolButton *>(opt))
+ return sz += QSize(7, 6);
+ // Otherwise, fall through
+
+ default:
+ sz = QCommonStyle::sizeFromContents(ct, opt, csz, widget);
+ }
+ return sz;
+}
+
+/*!
+ \internal
+*/
+QIcon QWindowsStyle::standardIconImplementation(StandardPixmap standardIcon, const QStyleOption *option,
+ const QWidget *widget) const
+{
+ QIcon icon;
+ QPixmap pixmap;
+#ifdef Q_OS_WIN
+ switch (standardIcon) {
+ case SP_FileDialogNewFolder:
+ {
+ for (int size = 16 ; size <= 32 ; size += 16) {
+ pixmap = loadIconFromShell32(319, size);
+ icon.addPixmap(pixmap, QIcon::Normal);
+ }
+ break;
+ }
+ case SP_DirHomeIcon:
+ {
+ for (int size = 16 ; size <= 32 ; size += 16) {
+ pixmap = loadIconFromShell32(235, size);
+ icon.addPixmap(pixmap, QIcon::Normal);
+ }
+ break;
+ }
+ case SP_DirIcon:
+ for (int size = 16 ; size <= 32 ; size += 16) {
+ pixmap = loadIconFromShell32(4, size);
+ icon.addPixmap(pixmap, QIcon::Normal, QIcon::Off);
+ pixmap = loadIconFromShell32(5, size);
+ icon.addPixmap(pixmap, QIcon::Normal, QIcon::On);
+ }
+ break;
+ case SP_DirLinkIcon:
+ for (int size = 16 ; size <= 32 ; size += 16) {
+ QPixmap link = loadIconFromShell32(30, size);
+ pixmap = loadIconFromShell32(4, size);
+ if (!pixmap.isNull() && !link.isNull()) {
+ QPainter painter(&pixmap);
+ painter.drawPixmap(0, 0, size, size, link);
+ icon.addPixmap(pixmap, QIcon::Normal, QIcon::Off);
+ }
+ link = loadIconFromShell32(30, size);
+ pixmap = loadIconFromShell32(5, size);
+ if (!pixmap.isNull() && !link.isNull()) {
+ QPainter painter(&pixmap);
+ painter.drawPixmap(0, 0, size, size, link);
+ icon.addPixmap(pixmap, QIcon::Normal, QIcon::On);
+ }
+ }
+ break;
+ case SP_FileIcon:
+ for (int size = 16 ; size <= 32 ; size += 16) {
+ pixmap = loadIconFromShell32(1, size);
+ icon.addPixmap(pixmap, QIcon::Normal);
+ }
+ break;
+ case SP_ComputerIcon:
+ for (int size = 16 ; size <= 32 ; size += 16) {
+ pixmap = loadIconFromShell32(16, size);
+ icon.addPixmap(pixmap, QIcon::Normal);
+ }
+ break;
+
+ case SP_DesktopIcon:
+ for (int size = 16 ; size <= 32 ; size += 16) {
+ pixmap = loadIconFromShell32(35, size);
+ icon.addPixmap(pixmap, QIcon::Normal);
+ }
+ break;
+ case SP_DriveCDIcon:
+ case SP_DriveDVDIcon:
+ for (int size = 16 ; size <= 32 ; size += 16) {
+ pixmap = loadIconFromShell32(12, size);
+ icon.addPixmap(pixmap, QIcon::Normal);
+ }
+ break;
+ case SP_DriveNetIcon:
+ for (int size = 16 ; size <= 32 ; size += 16) {
+ pixmap = loadIconFromShell32(10, size);
+ icon.addPixmap(pixmap, QIcon::Normal);
+ }
+ break;
+ case SP_DriveHDIcon:
+ for (int size = 16 ; size <= 32 ; size += 16) {
+ pixmap = loadIconFromShell32(9, size);
+ icon.addPixmap(pixmap, QIcon::Normal);
+ }
+ break;
+ case SP_DriveFDIcon:
+ for (int size = 16 ; size <= 32 ; size += 16) {
+ pixmap = loadIconFromShell32(7, size);
+ icon.addPixmap(pixmap, QIcon::Normal);
+ }
+ break;
+ case SP_FileLinkIcon:
+ for (int size = 16 ; size <= 32 ; size += 16) {
+ QPixmap link;
+ link = loadIconFromShell32(30, size);
+ pixmap = loadIconFromShell32(1, size);
+ if (!pixmap.isNull() && !link.isNull()) {
+ QPainter painter(&pixmap);
+ painter.drawPixmap(0, 0, size, size, link);
+ icon.addPixmap(pixmap, QIcon::Normal);
+ }
+ }
+ break;
+ case SP_VistaShield:
+ {
+ if (QSysInfo::WindowsVersion >= QSysInfo::WV_VISTA
+ && QSysInfo::WindowsVersion < QSysInfo::WV_NT_based
+ && pSHGetStockIconInfo)
+ {
+ icon.addPixmap(standardPixmap(SP_VistaShield, option, widget)); //fetches small icon
+ QSHSTOCKICONINFO iconInfo; //append large icon
+ memset(&iconInfo, 0, sizeof(iconInfo));
+ iconInfo.cbSize = sizeof(iconInfo);
+ if (pSHGetStockIconInfo(_SIID_SHIELD, _SHGFI_ICON | _SHGFI_LARGEICON, &iconInfo) == S_OK) {
+ icon.addPixmap(convertHIconToPixmap(iconInfo.hIcon));
+ DestroyIcon(iconInfo.hIcon);
+ }
+ }
+ }
+ break;
+ default:
+ break;
+ }
+#endif
+
+ if (icon.isNull())
+ icon = QCommonStyle::standardIconImplementation(standardIcon, option, widget);
+ return icon;
+}
+
+
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_STYLE_WINDOWS
diff --git a/src/gui/styles/qwindowsstyle.h b/src/gui/styles/qwindowsstyle.h
new file mode 100644
index 0000000000..7f45f206ca
--- /dev/null
+++ b/src/gui/styles/qwindowsstyle.h
@@ -0,0 +1,111 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QWINDOWSSTYLE_H
+#define QWINDOWSSTYLE_H
+
+#include <QtGui/qcommonstyle.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+#if !defined(QT_NO_STYLE_WINDOWS)
+
+class QWindowsStylePrivate;
+
+class Q_GUI_EXPORT QWindowsStyle : public QCommonStyle
+{
+ Q_OBJECT
+public:
+ QWindowsStyle();
+ ~QWindowsStyle();
+
+ void polish(QApplication*);
+ void unpolish(QApplication*);
+
+ void polish(QWidget*);
+ void unpolish(QWidget*);
+
+ void polish(QPalette &);
+
+ void drawPrimitive(PrimitiveElement pe, const QStyleOption *opt, QPainter *p,
+ const QWidget *w = 0) const;
+ void drawControl(ControlElement element, const QStyleOption *opt, QPainter *p,
+ const QWidget *w = 0) const;
+ QRect subElementRect(SubElement r, const QStyleOption *opt, const QWidget *widget = 0) const;
+ void drawComplexControl(ComplexControl cc, const QStyleOptionComplex *opt, QPainter *p,
+ const QWidget *w = 0) const;
+ QSize sizeFromContents(ContentsType ct, const QStyleOption *opt,
+ const QSize &contentsSize, const QWidget *widget = 0) const;
+
+ int pixelMetric(PixelMetric pm, const QStyleOption *option = 0, const QWidget *widget = 0) const;
+
+ int styleHint(StyleHint hint, const QStyleOption *opt = 0, const QWidget *widget = 0,
+ QStyleHintReturn *returnData = 0) const;
+
+ QPixmap standardPixmap(StandardPixmap standardPixmap, const QStyleOption *opt,
+ const QWidget *widget = 0) const;
+
+protected Q_SLOTS:
+ QIcon standardIconImplementation(StandardPixmap standardIcon, const QStyleOption *option,
+ const QWidget *widget = 0) const;
+
+protected:
+ bool eventFilter(QObject *o, QEvent *e);
+ void timerEvent(QTimerEvent *event);
+ QWindowsStyle(QWindowsStylePrivate &dd);
+
+private:
+ Q_DISABLE_COPY(QWindowsStyle)
+ Q_DECLARE_PRIVATE(QWindowsStyle)
+ void *reserved;
+};
+
+#endif // QT_NO_STYLE_WINDOWS
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QWINDOWSSTYLE_H
diff --git a/src/gui/styles/qwindowsstyle_p.h b/src/gui/styles/qwindowsstyle_p.h
new file mode 100644
index 0000000000..1d1bdf5ee6
--- /dev/null
+++ b/src/gui/styles/qwindowsstyle_p.h
@@ -0,0 +1,96 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QWINDOWSSTYLE_P_H
+#define QWINDOWSSTYLE_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of qapplication_*.cpp, qwidget*.cpp and qfiledialog.cpp. This header
+// file may change from version to version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qwindowsstyle.h"
+#include "qcommonstyle_p.h"
+
+#ifndef QT_NO_STYLE_WINDOWS
+#include <qlist.h>
+#include <qdatetime.h>
+#include <qhash.h>
+
+QT_BEGIN_NAMESPACE
+
+class QTime;
+class QProgressBar;
+
+class QWindowsStylePrivate : public QCommonStylePrivate
+{
+ Q_DECLARE_PUBLIC(QWindowsStyle)
+public:
+ QWindowsStylePrivate();
+ bool hasSeenAlt(const QWidget *widget) const;
+ bool altDown() const { return alt_down; }
+ bool alt_down;
+ QList<const QWidget *> seenAlt;
+ int menuBarTimer;
+
+ QList<QProgressBar *> bars;
+ int animationFps;
+ int animateTimer;
+ QTime startTime;
+ int animateStep;
+ QColor inactiveCaptionText;
+ QColor activeCaptionColor;
+ QColor activeGradientCaptionColor;
+ QColor inactiveCaptionColor;
+ QColor inactiveGradientCaptionColor;
+ };
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_STYLE_WINDOWS
+
+#endif //QWINDOWSSTYLE_P_H
diff --git a/src/gui/styles/qwindowsvistastyle.cpp b/src/gui/styles/qwindowsvistastyle.cpp
new file mode 100644
index 0000000000..4c3060d063
--- /dev/null
+++ b/src/gui/styles/qwindowsvistastyle.cpp
@@ -0,0 +1,2650 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qwindowsvistastyle.h"
+#include "qwindowsvistastyle_p.h"
+
+#if !defined(QT_NO_STYLE_WINDOWSVISTA) || defined(QT_PLUGIN)
+
+QT_BEGIN_NAMESPACE
+
+static const int windowsItemFrame = 2; // menu item frame width
+static const int windowsItemHMargin = 3; // menu item hor text margin
+static const int windowsItemVMargin = 4; // menu item ver text margin
+static const int windowsArrowHMargin = 6; // arrow horizontal margin
+static const int windowsRightBorder = 15; // right border on windows
+
+#ifndef TMT_CONTENTMARGINS
+# define TMT_CONTENTMARGINS 3602
+#endif
+#ifndef TMT_SIZINGMARGINS
+# define TMT_SIZINGMARGINS 3601
+#endif
+#ifndef LISS_NORMAL
+# define LISS_NORMAL 1
+# define LISS_HOT 2
+# define LISS_SELECTED 3
+# define LISS_DISABLED 4
+# define LISS_SELECTEDNOTFOCUS 5
+# define LISS_HOTSELECTED 6
+#endif
+#ifndef BP_COMMANDLINK
+# define BP_COMMANDLINK 6
+# define BP_COMMANDLINKGLYPH 7
+# define CMDLGS_NORMAL 1
+# define CMDLGS_HOT 2
+# define CMDLGS_PRESSED 3
+# define CMDLGS_DISABLED 4
+#endif
+
+// Runtime resolved theme engine function calls
+
+
+typedef HRESULT (WINAPI *PtrGetThemePartSize)(HTHEME hTheme, HDC hdc, int iPartId, int iStateId, OPTIONAL RECT *prc, enum THEMESIZE eSize, OUT SIZE *psz);
+typedef HTHEME (WINAPI *PtrOpenThemeData)(HWND hwnd, LPCWSTR pszClassList);
+typedef HTHEME (WINAPI *PtrOpenThemeData)(HWND hwnd, LPCWSTR pszClassList);
+typedef HRESULT (WINAPI *PtrCloseThemeData)(HTHEME hTheme);
+typedef HRESULT (WINAPI *PtrDrawThemeBackground)(HTHEME hTheme, HDC hdc, int iPartId, int iStateId, const RECT *pRect, OPTIONAL const RECT *pClipRect);
+typedef HRESULT (WINAPI *PtrDrawThemeBackgroundEx)(HTHEME hTheme, HDC hdc, int iPartId, int iStateId, const RECT *pRect, OPTIONAL const DTBGOPTS *pOptions);
+typedef HRESULT (WINAPI *PtrGetCurrentThemeName)(OUT LPWSTR pszThemeFileName, int cchMaxNameChars, OUT OPTIONAL LPWSTR pszColorBuff, int cchMaxColorChars, OUT OPTIONAL LPWSTR pszSizeBuff, int cchMaxSizeChars);
+typedef HRESULT (WINAPI *PtrGetThemeBool)(HTHEME hTheme, int iPartId, int iStateId, int iPropId, OUT BOOL *pfVal);
+typedef HRESULT (WINAPI *PtrGetThemeColor)(HTHEME hTheme, int iPartId, int iStateId, int iPropId, OUT COLORREF *pColor);
+typedef HRESULT (WINAPI *PtrGetThemeEnumValue)(HTHEME hTheme, int iPartId, int iStateId, int iPropId, OUT int *piVal);
+typedef HRESULT (WINAPI *PtrGetThemeFilename)(HTHEME hTheme, int iPartId, int iStateId, int iPropId, OUT LPWSTR pszThemeFileName, int cchMaxBuffChars);
+typedef HRESULT (WINAPI *PtrGetThemeFont)(HTHEME hTheme, OPTIONAL HDC hdc, int iPartId, int iStateId, int iPropId, OUT LOGFONT *pFont);
+typedef HRESULT (WINAPI *PtrGetThemeInt)(HTHEME hTheme, int iPartId, int iStateId, int iPropId, OUT int *piVal);
+typedef HRESULT (WINAPI *PtrGetThemeIntList)(HTHEME hTheme, int iPartId, int iStateId, int iPropId, OUT INTLIST *pIntList);
+typedef HRESULT (WINAPI *PtrGetThemeMargins)(HTHEME hTheme, OPTIONAL HDC hdc, int iPartId, int iStateId, int iPropId, OPTIONAL RECT *prc, OUT MARGINS *pMargins);
+typedef HRESULT (WINAPI *PtrGetThemeMetric)(HTHEME hTheme, OPTIONAL HDC hdc, int iPartId, int iStateId, int iPropId, OUT int *piVal);
+typedef HRESULT (WINAPI *PtrGetThemePartSize)(HTHEME hTheme, HDC hdc, int iPartId, int iStateId, OPTIONAL RECT *prc, enum THEMESIZE eSize, OUT SIZE *psz);
+typedef HRESULT (WINAPI *PtrGetThemePosition)(HTHEME hTheme, int iPartId, int iStateId, int iPropId, OUT POINT *pPoint);
+typedef HRESULT (WINAPI *PtrGetThemeRect)(HTHEME hTheme, int iPartId, int iStateId, int iPropId, OUT RECT *pRect);
+typedef HRESULT (WINAPI *PtrGetThemeString)(HTHEME hTheme, int iPartId, int iStateId, int iPropId, OUT LPWSTR pszBuff, int cchMaxBuffChars);
+typedef HRESULT (WINAPI *PtrGetThemeTransitionDuration)(HTHEME hTheme, int iPartId, int iStateFromId, int iStateToId, int iPropId, int *pDuration);
+typedef HRESULT (WINAPI *PtrIsThemePartDefined)(HTHEME hTheme, int iPartId, int iStateId);
+typedef HRESULT (WINAPI *PtrSetWindowTheme)(HWND hwnd, LPCWSTR pszSubAppName, LPCWSTR pszSubIdList);
+typedef HRESULT (WINAPI *PtrGetThemePropertyOrigin)(HTHEME hTheme, int iPartId, int iStateId, int iPropId, OUT enum PROPERTYORIGIN *pOrigin);
+
+static PtrIsThemePartDefined pIsThemePartDefined = 0;
+static PtrOpenThemeData pOpenThemeData = 0;
+static PtrCloseThemeData pCloseThemeData = 0;
+static PtrDrawThemeBackground pDrawThemeBackground = 0;
+static PtrDrawThemeBackgroundEx pDrawThemeBackgroundEx = 0;
+static PtrGetCurrentThemeName pGetCurrentThemeName = 0;
+static PtrGetThemeBool pGetThemeBool = 0;
+static PtrGetThemeColor pGetThemeColor = 0;
+static PtrGetThemeEnumValue pGetThemeEnumValue = 0;
+static PtrGetThemeFilename pGetThemeFilename = 0;
+static PtrGetThemeFont pGetThemeFont = 0;
+static PtrGetThemeInt pGetThemeInt = 0;
+static PtrGetThemeIntList pGetThemeIntList = 0;
+static PtrGetThemeMargins pGetThemeMargins = 0;
+static PtrGetThemeMetric pGetThemeMetric = 0;
+static PtrGetThemePartSize pGetThemePartSize = 0;
+static PtrGetThemePosition pGetThemePosition = 0;
+static PtrGetThemeRect pGetThemeRect = 0;
+static PtrGetThemeString pGetThemeString = 0;
+static PtrGetThemeTransitionDuration pGetThemeTransitionDuration= 0;
+static PtrSetWindowTheme pSetWindowTheme = 0;
+static PtrGetThemePropertyOrigin pGetThemePropertyOrigin = 0;
+
+/* \internal
+ Checks if we should use Vista style , or if we should
+ fall back to Windows style.
+*/
+bool QWindowsVistaStylePrivate::useVista()
+{
+ return (QWindowsVistaStylePrivate::useXP() &&
+ (QSysInfo::WindowsVersion >= QSysInfo::WV_VISTA &&
+ QSysInfo::WindowsVersion < QSysInfo::WV_NT_based));
+}
+
+/*!
+ \class QWindowsVistaStyle
+ \brief The QWindowsVistaStyle class provides a look and feel suitable for applications on Microsoft Windows Vista.
+ \since 4.3
+ \ingroup appearance
+
+ \warning This style is only available on the Windows Vista platform
+ because it makes use of Windows Vista's style engine.
+
+ \sa QMacStyle, QWindowsXPStyle, QPlastiqueStyle, QCleanlooksStyle, QMotifStyle
+*/
+
+/*!
+ Constructs a QWindowsVistaStyle object.
+*/
+QWindowsVistaStyle::QWindowsVistaStyle()
+ : QWindowsXPStyle(*new QWindowsVistaStylePrivate)
+{
+}
+
+//convert Qt state flags to uxtheme button states
+int buttonStateId(int flags, int partId)
+{
+ int stateId = 0;
+ if (partId == BP_RADIOBUTTON || partId == BP_CHECKBOX) {
+ if (!(flags & QStyle::State_Enabled))
+ stateId = RBS_UNCHECKEDDISABLED;
+ else if (flags & QStyle::State_Sunken)
+ stateId = RBS_UNCHECKEDPRESSED;
+ else if (flags & QStyle::State_MouseOver)
+ stateId = RBS_UNCHECKEDHOT;
+ else
+ stateId = RBS_UNCHECKEDNORMAL;
+
+ if (flags & QStyle::State_On)
+ stateId += RBS_CHECKEDNORMAL-1;
+
+ } else if (partId == BP_PUSHBUTTON) {
+ if (!(flags & QStyle::State_Enabled))
+ stateId = PBS_DISABLED;
+ else if (flags & (QStyle::State_Sunken | QStyle::State_On))
+ stateId = PBS_PRESSED;
+ else if (flags & QStyle::State_MouseOver)
+ stateId = PBS_HOT;
+ else
+ stateId = PBS_NORMAL;
+ } else {
+ Q_ASSERT(1);
+ }
+ return stateId;
+}
+
+void Animation::paint(QPainter *painter, const QStyleOption *option)
+{
+ Q_UNUSED(option);
+ Q_UNUSED(painter);
+}
+
+/*
+* ! \internal
+*
+* Helperfunction to paint the current transition state
+* between two animation frames.
+*
+* The result is a blended image consisting of
+* ((alpha)*_primaryImage) + ((1-alpha)*_secondaryImage)
+*
+*/
+
+void Animation::drawBlendedImage(QPainter *painter, QRect rect, float alpha) {
+ if (_secondaryImage.isNull() || _primaryImage.isNull())
+ return;
+
+ if (_tempImage.isNull())
+ _tempImage = _secondaryImage;
+
+ const int a = qRound(alpha*256);
+ const int ia = 256 - a;
+ const int sw = _primaryImage.width();
+ const int sh = _primaryImage.height();
+ const int bpl = _primaryImage.bytesPerLine();
+ switch(_primaryImage.depth()) {
+ case 32:
+ {
+ uchar *mixed_data = _tempImage.bits();
+ const uchar *back_data = _primaryImage.bits();
+ const uchar *front_data = _secondaryImage.bits();
+ for (int sy = 0; sy < sh; sy++) {
+ quint32* mixed = (quint32*)mixed_data;
+ const quint32* back = (const quint32*)back_data;
+ const quint32* front = (const quint32*)front_data;
+ for (int sx = 0; sx < sw; sx++) {
+ quint32 bp = back[sx];
+ quint32 fp = front[sx];
+ mixed[sx] = qRgba ((qRed(bp)*ia + qRed(fp)*a)>>8,
+ (qGreen(bp)*ia + qGreen(fp)*a)>>8,
+ (qBlue(bp)*ia + qBlue(fp)*a)>>8,
+ (qAlpha(bp)*ia + qAlpha(fp)*a)>>8);
+ }
+ mixed_data += bpl;
+ back_data += bpl;
+ front_data += bpl;
+ }
+ }
+ default:
+ break;
+ }
+ painter->drawImage(rect, _tempImage);
+}
+
+/*
+* ! \internal
+*
+* Paints a transition state. The result will be a mix between the
+* initial and final state of the transition, depending on the
+* time difference between _startTime and current time.
+*/
+
+void Transition::paint(QPainter *painter, const QStyleOption *option)
+{
+ float alpha = 1.0;
+ if (_duration > 0) {
+ QTime current = QTime::currentTime();
+
+ if (_startTime > current)
+ _startTime = current;
+
+ int timeDiff = _startTime.msecsTo(current);
+ alpha = timeDiff/(float)_duration;
+ if (timeDiff > _duration) {
+ _running = false;
+ alpha = 1.0;
+ }
+ }
+ else {
+ _running = false;
+ }
+ drawBlendedImage(painter, option->rect, alpha);
+}
+
+/*
+* ! \internal
+*
+* Paints a pulse. The result will be a mix between the
+* primary and secondary pulse images depending on the
+* time difference between _startTime and current time.
+*/
+
+
+void Pulse::paint(QPainter *painter, const QStyleOption *option)
+{
+ float alpha = 1.0;
+ if (_duration > 0) {
+ QTime current = QTime::currentTime();
+
+ if (_startTime > current)
+ _startTime = current;
+
+ int timeDiff = _startTime.msecsTo(current) % _duration*2;
+ if (timeDiff > _duration)
+ timeDiff = _duration*2 - timeDiff;
+ alpha = timeDiff/(float)_duration;
+ } else {
+ _running = false;
+ }
+ drawBlendedImage(painter, option->rect, alpha);
+}
+
+
+/*!
+ \reimp
+ *
+ * Animations are used for some state transitions on specific widgets.
+ *
+ * Only one running animation can exist for a widget at any specific time.
+ * Animations can be added through QWindowsVistaStylePrivate::startAnimation(Animation *)
+ * and any existing animation on a widget can be retrieved with
+ * QWindowsVistaStylePrivate::widgetAnimation(Widget *).
+ *
+ * Once an animation has been started, QWindowsVistaStylePrivate::timerEvent(QTimerEvent *)
+ * will continuously call update() on the widget until it is stopped, meaning that drawPrimitive
+ * will be called many times until the transition has completed. During this time, the result
+ * will be retrieved by the Animation::paint(...) function and not by the style itself.
+ *
+ * To determine if a transition should occur, the style needs to know the previous state of the
+ * widget as well as the current one. This is solved by updating dynamic properties on the widget
+ * every time the function is called.
+ *
+ * Transitions interrupting existing transitions should always be smooth, so whenever a hover-transition
+ * is started on a pulsating button, it uses the current frame of the pulse-animation as the
+ * starting image for the hover transition.
+ *
+ */
+void QWindowsVistaStyle::drawPrimitive(PrimitiveElement element, const QStyleOption *option,
+ QPainter *painter, const QWidget *widget) const
+{
+ QWindowsVistaStylePrivate *d = const_cast<QWindowsVistaStylePrivate*>(d_func());
+
+ int state = option->state;
+ if (!QWindowsVistaStylePrivate::useVista()) {
+ QWindowsStyle::drawPrimitive(element, option, painter, widget);
+ return;
+ }
+
+ QRect oldRect;
+ QRect newRect;
+
+ if (widget && d->transitionsEnabled())
+ {
+ /* all widgets that supports state transitions : */
+ if (
+#ifndef QT_NO_LINEEDIT
+ (qobject_cast<const QLineEdit*>(widget) && element == PE_FrameLineEdit) ||
+#endif // QT_NO_LINEEDIT
+ (qobject_cast<const QRadioButton*>(widget)&& element == PE_IndicatorRadioButton) ||
+ (qobject_cast<const QCheckBox*>(widget) && element == PE_IndicatorCheckBox) ||
+ (qobject_cast<const QGroupBox *>(widget)&& element == PE_IndicatorCheckBox) ||
+ (qobject_cast<const QToolButton*>(widget) && element == PE_PanelButtonBevel)
+ )
+ {
+ // Retrieve and update the dynamic properties tracking
+ // the previous state of the widget:
+ QWidget *w = const_cast<QWidget *> (widget);
+ int oldState = w->property("_q_stylestate").toInt();
+ oldRect = w->property("_q_stylerect").toRect();
+ newRect = w->rect();
+ w->setProperty("_q_stylestate", (int)option->state);
+ w->setProperty("_q_stylerect", w->rect());
+
+ bool doTransition = ((state & State_Sunken) != (oldState & State_Sunken) ||
+ (state & State_On) != (oldState & State_On) ||
+ (state & State_MouseOver) != (oldState & State_MouseOver));
+
+ if (oldRect != newRect ||
+ (state & State_Enabled) != (oldState & State_Enabled) ||
+ (state & State_Active) != (oldState & State_Active))
+ d->stopAnimation(widget);
+
+#ifndef QT_NO_LINEEDIT
+ if (const QLineEdit *edit = qobject_cast<const QLineEdit *>(widget))
+ if (edit->isReadOnly() && element == PE_FrameLineEdit) // Do not animate read only line edits
+ doTransition = false;
+#endif // QT_NO_LINEEDIT
+
+ if (doTransition) {
+
+ // We create separate images for the initial and final transition states and store them in the
+ // Transition object.
+ QImage startImage(option->rect.size(), QImage::Format_ARGB32_Premultiplied);
+ QImage endImage(option->rect.size(), QImage::Format_ARGB32_Premultiplied);
+ QStyleOption opt = *option;
+
+ opt.rect.setRect(0, 0, option->rect.width(), option->rect.height());
+ opt.state = (QStyle::State)oldState;
+ startImage.fill(0);
+ QPainter startPainter(&startImage);
+
+ Animation *anim = d->widgetAnimation(widget);
+ Transition *t = new Transition;
+ t->setWidget(w);
+
+ // If we have a running animation on the widget already, we will use that to paint the initial
+ // state of the new transition, this ensures a smooth transition from a current animation such as a
+ // pulsating default button into the intended target state.
+
+ if (!anim)
+ drawPrimitive(element, &opt, &startPainter, 0); // Note that the widget pointer is intentionally 0
+ else // this ensures that we do not recurse in the animation logic above
+ anim->paint(&startPainter, &opt);
+
+ d->startAnimation(t);
+ t->setStartImage(startImage);
+
+ // The end state of the transition is simply the result we would have painted
+ // if the style was not animated.
+
+ QPainter endPainter(&endImage);
+ endImage.fill(0);
+ QStyleOption opt2 = opt;
+ opt2.state = option->state;
+ drawPrimitive(element, &opt2, &endPainter, 0); // Note that the widget pointer is intentionally 0
+ // this ensures that we do not recurse in the animation logic above
+ t->setEndImage(endImage);
+
+ HTHEME theme;
+ int partId;
+ int duration;
+ int fromState = 0;
+ int toState = 0;
+
+ //translate state flags to UXTHEME states :
+ if (element == PE_FrameLineEdit) {
+ theme = pOpenThemeData(0, L"Edit");
+ partId = EP_EDITBORDER_NOSCROLL;
+
+ if (oldState & State_MouseOver)
+ fromState = ETS_HOT;
+ else if (oldState & State_HasFocus)
+ fromState = ETS_FOCUSED;
+ else
+ fromState = ETS_NORMAL;
+
+ if (state & State_MouseOver)
+ toState = ETS_HOT;
+ else if (state & State_HasFocus)
+ toState = ETS_FOCUSED;
+ else
+ toState = ETS_NORMAL;
+
+ } else {
+ theme = pOpenThemeData(0, L"Button");
+ if (element == PE_IndicatorRadioButton)
+ partId = BP_RADIOBUTTON;
+ else if (element == PE_IndicatorCheckBox)
+ partId = BP_CHECKBOX;
+ else
+ partId = BP_PUSHBUTTON;
+
+ fromState = buttonStateId(oldState, partId);
+ toState = buttonStateId(option->state, partId);
+ }
+
+ // Retrieve the transition time between the states from the system.
+ if (theme && pGetThemeTransitionDuration(theme, partId, fromState, toState,
+ TMT_TRANSITIONDURATIONS, &duration) == S_OK)
+ {
+ t->setDuration(duration);
+ }
+ t->setStartTime(QTime::currentTime());
+ }
+ }
+ } // End of animation part
+
+
+ QRect rect = option->rect;
+
+ switch (element) {
+ case PE_IndicatorHeaderArrow:
+ if (const QStyleOptionHeader *header = qstyleoption_cast<const QStyleOptionHeader *>(option)) {
+ int stateId = HSAS_SORTEDDOWN;
+ if (header->sortIndicator & QStyleOptionHeader::SortDown)
+ stateId = HSAS_SORTEDUP; //note that the uxtheme sort down indicator is the inverse of ours
+ XPThemeData theme(widget, painter, QLatin1String("HEADER"), HP_HEADERSORTARROW, stateId, option->rect);
+ d->drawBackground(theme);
+ }
+ break;
+
+ case PE_IndicatorBranch:
+ {
+ XPThemeData theme(d->treeViewHelper(), painter, QLatin1String("TREEVIEW"));
+ static const int decoration_size = 16;
+ int mid_h = option->rect.x() + option->rect.width() / 2;
+ int mid_v = option->rect.y() + option->rect.height() / 2;
+ int bef_h = mid_h;
+ int bef_v = mid_v;
+ int aft_h = mid_h;
+ int aft_v = mid_v;
+ if (option->state & State_Children) {
+ int delta = decoration_size / 2;
+ theme.rect = QRect(bef_h - delta, bef_v - delta, decoration_size, decoration_size);
+ theme.partId = option->state & State_MouseOver ? TVP_HOTGLYPH : TVP_GLYPH;
+ theme.stateId = option->state & QStyle::State_Open ? GLPS_OPENED : GLPS_CLOSED;
+ if (option->direction == Qt::RightToLeft)
+ theme.mirrorHorizontally = true;
+ d->drawBackground(theme);
+ bef_h -= delta + 2;
+ bef_v -= delta + 2;
+ aft_h += delta - 2;
+ aft_v += delta - 2;
+ }
+#if 0
+ QBrush brush(option->palette.dark().color(), Qt::Dense4Pattern);
+ if (option->state & State_Item) {
+ if (option->direction == Qt::RightToLeft)
+ painter->fillRect(option->rect.left(), mid_v, bef_h - option->rect.left(), 1, brush);
+ else
+ painter->fillRect(aft_h, mid_v, option->rect.right() - aft_h + 1, 1, brush);
+ }
+ if (option->state & State_Sibling && option->rect.bottom() > aft_v)
+ painter->fillRect(mid_h, aft_v, 1, option->rect.bottom() - aft_v + 1, brush);
+ if (option->state & (State_Open | State_Children | State_Item | State_Sibling) && (bef_v > option->rect.y()))
+ painter->fillRect(mid_h, option->rect.y(), 1, bef_v - option->rect.y(), brush);
+#endif
+ }
+ break;
+
+ case PE_PanelButtonBevel:
+ case PE_IndicatorCheckBox:
+ case PE_IndicatorRadioButton:
+ {
+ if (Animation *a = d->widgetAnimation(widget)) {
+ a->paint(painter, option);
+ } else {
+ QWindowsXPStyle::drawPrimitive(element, option, painter, widget);
+ }
+ }
+ break;
+
+ case PE_IndicatorToolBarHandle:
+ {
+ XPThemeData theme;
+ if (option->state & State_Horizontal)
+ theme = XPThemeData(widget, painter, QLatin1String("REBAR"), RP_GRIPPER, ETS_NORMAL, option->rect.adjusted(0, 1, -2, -2));
+ else
+ theme = XPThemeData(widget, painter, QLatin1String("REBAR"), RP_GRIPPERVERT, ETS_NORMAL, option->rect.adjusted(0, 1, -2, -2));
+ d->drawBackground(theme);
+ }
+ break;
+
+ case PE_FrameMenu:
+ {
+ int stateId = option->state & State_Active ? MB_ACTIVE : MB_INACTIVE;
+ XPThemeData theme(widget, painter, QLatin1String("MENU"), MENU_POPUPBORDERS, stateId, option->rect);
+ d->drawBackground(theme);
+ }
+ break;
+ case PE_Frame:
+#ifndef QT_NO_TEXTEDIT
+ if (const QTextEdit *edit = qobject_cast<const QTextEdit*>(widget)) {
+ painter->save();
+ int stateId = ETS_NORMAL;
+ if (!(state & State_Enabled))
+ stateId = ETS_DISABLED;
+ else if (edit->isReadOnly())
+ stateId = ETS_READONLY;
+ else if (state & State_HasFocus)
+ stateId = ETS_SELECTED;
+ XPThemeData theme(widget, painter, QLatin1String("EDIT"), EP_EDITBORDER_HVSCROLL, stateId, option->rect);
+ uint resolve_mask = option->palette.resolve();
+ if (resolve_mask & (1 << QPalette::Base)) {
+ // Since EP_EDITBORDER_HVSCROLL does not us borderfill, theme.noContent cannot be used for clipping
+ int borderSize = 1;
+ pGetThemeInt(theme.handle(), theme.partId, theme.stateId, TMT_BORDERSIZE, &borderSize);
+ QRegion clipRegion = option->rect;
+ QRegion content = option->rect.adjusted(borderSize, borderSize, -borderSize, -borderSize);
+ clipRegion ^= content;
+ painter->setClipRegion(clipRegion);
+ }
+ d->drawBackground(theme);
+ painter->restore();
+ } else
+#endif // QT_NO_TEXTEDIT
+ QWindowsXPStyle::drawPrimitive(element, option, painter, widget);
+ break;
+
+ case PE_PanelLineEdit:
+ if (const QStyleOptionFrame *panel = qstyleoption_cast<const QStyleOptionFrame *>(option)) {
+ QBrush bg;
+ bool usePalette = false;
+ bool isEnabled = option->state & State_Enabled;
+ uint resolve_mask = panel->palette.resolve();
+ if (widget) {
+ //Since spin box and combo box includes a line edit we need to resolve the palette on the parent instead
+#ifndef QT_NO_SPINBOX
+ if (QAbstractSpinBox *spinbox = qobject_cast<QAbstractSpinBox*>(widget->parentWidget()))
+ resolve_mask = spinbox->palette().resolve();
+#endif // QT_NO_SPINBOX
+#ifndef QT_NO_COMBOBOX
+ if (QComboBox *combobox = qobject_cast<QComboBox*>(widget->parentWidget()))
+ resolve_mask = combobox->palette().resolve();
+#endif // QT_NO_COMBOBOX
+ }
+ if (resolve_mask & (1 << QPalette::Base)) {
+ // Base color is set for this widget, so use it
+ bg = panel->palette.brush(QPalette::Base);
+ usePalette = true;
+ }
+ if (usePalette) {
+ painter->fillRect(panel->rect, bg);
+ } else {
+ int partId = EP_BACKGROUND;
+ int stateId = EBS_NORMAL;
+ if (!isEnabled)
+ stateId = EBS_DISABLED;
+ else if (state & State_ReadOnly)
+ stateId = EBS_READONLY;
+ else if (state & State_MouseOver)
+ stateId = EBS_HOT;
+
+ XPThemeData theme(0, painter, QLatin1String("EDIT"), partId, stateId, rect);
+ if (!theme.isValid()) {
+ QWindowsStyle::drawPrimitive(element, option, painter, widget);
+ return;
+ }
+ int bgType;
+ pGetThemeEnumValue( theme.handle(),
+ partId,
+ stateId,
+ TMT_BGTYPE,
+ &bgType);
+ if( bgType == BT_IMAGEFILE ) {
+ d->drawBackground(theme);
+ } else {
+ QBrush fillColor = option->palette.brush(QPalette::Base);
+ if (!isEnabled) {
+ PROPERTYORIGIN origin = PO_NOTFOUND;
+ pGetThemePropertyOrigin(theme.handle(), theme.partId, theme.stateId, TMT_FILLCOLOR, &origin);
+ // Use only if the fill property comes from our part
+ if ((origin == PO_PART || origin == PO_STATE)) {
+ COLORREF bgRef;
+ pGetThemeColor(theme.handle(), partId, stateId, TMT_FILLCOLOR, &bgRef);
+ fillColor = QBrush(qRgb(GetRValue(bgRef), GetGValue(bgRef), GetBValue(bgRef)));
+ }
+ }
+ painter->fillRect(option->rect, fillColor);
+ }
+ }
+ if (panel->lineWidth > 0)
+ drawPrimitive(PE_FrameLineEdit, panel, painter, widget);
+ return;
+ }
+ break;
+
+ case PE_FrameLineEdit:
+ if (Animation *anim = d->widgetAnimation(widget)) {
+ anim->paint(painter, option);
+ } else {
+ QPainter *p = painter;
+ QWidget *parentWidget = 0;
+ if (widget) {
+ parentWidget = widget->parentWidget();
+ if (parentWidget)
+ parentWidget = parentWidget->parentWidget();
+ }
+ if (widget && widget->inherits("QLineEdit")
+ && parentWidget && parentWidget->inherits("QAbstractItemView")) {
+ // we try to check if this lineedit is a delegate on a QAbstractItemView-derived class.
+ QPen oldPen = p->pen();
+ // Inner white border
+ p->setPen(QPen(option->palette.base().color(), 1));
+ p->drawRect(option->rect.adjusted(1, 1, -2, -2));
+ // Outer dark border
+ p->setPen(QPen(option->palette.shadow().color(), 1));
+ p->drawRect(option->rect.adjusted(0, 0, -1, -1));
+ p->setPen(oldPen);
+ return;
+ } else {
+ int stateId = ETS_NORMAL;
+ if (!(state & State_Enabled))
+ stateId = ETS_DISABLED;
+ else if (state & State_ReadOnly)
+ stateId = ETS_READONLY;
+ else if (state & State_MouseOver)
+ stateId = ETS_HOT;
+ else if (state & State_HasFocus)
+ stateId = ETS_SELECTED;
+ XPThemeData theme(widget, painter, QLatin1String("EDIT"), EP_EDITBORDER_NOSCROLL, stateId, option->rect);
+ painter->save();
+ QRegion clipRegion = option->rect;
+ clipRegion -= option->rect.adjusted(2, 2, -2, -2);
+ painter->setClipRegion(clipRegion);
+ d->drawBackground(theme);
+ painter->restore();
+ }
+ }
+ break;
+
+ case PE_IndicatorToolBarSeparator:
+ {
+ QPen pen = painter->pen();
+ int margin = 3;
+ painter->setPen(option->palette.background().color().darker(114));
+ if (option->state & State_Horizontal) {
+ int x1 = option->rect.center().x();
+ painter->drawLine(QPoint(x1, option->rect.top() + margin), QPoint(x1, option->rect.bottom() - margin));
+ } else {
+ int y1 = option->rect.center().y();
+ painter->drawLine(QPoint(option->rect.left() + margin, y1), QPoint(option->rect.right() - margin, y1));
+ }
+ painter->setPen(pen);
+ }
+ break;
+
+ case PE_PanelTipLabel: {
+ XPThemeData theme(widget, painter, QLatin1String("TOOLTIP"), TTP_STANDARD, TTSS_NORMAL, option->rect);
+ d->drawBackground(theme);
+ break;
+ }
+
+ case PE_PanelItemViewItem:
+ {
+ const QStyleOptionViewItemV4 *vopt;
+ const QAbstractItemView *view = qobject_cast<const QAbstractItemView *>(widget);
+ bool newStyle = false;
+
+ if (const QListView *listview = qobject_cast<const QListView *>(widget)) {
+ if (listview->viewMode() == QListView::IconMode)
+ newStyle = true;
+ } else if (const QTreeView* treeview = qobject_cast<const QTreeView *>(widget)) {
+ newStyle = true;
+ }
+ if (newStyle && view && (vopt = qstyleoption_cast<const QStyleOptionViewItemV4 *>(option))) {
+ bool selected = vopt->state & QStyle::State_Selected;
+ bool hover = vopt->state & QStyle::State_MouseOver;
+ bool active = vopt->state & QStyle::State_Active;
+
+ if (vopt->features & QStyleOptionViewItemV2::Alternate)
+ painter->fillRect(vopt->rect, vopt->palette.alternateBase());
+
+ QPalette::ColorGroup cg = vopt->state & QStyle::State_Enabled
+ ? QPalette::Normal : QPalette::Disabled;
+ if (cg == QPalette::Normal && !(vopt->state & QStyle::State_Active))
+ cg = QPalette::Inactive;
+
+ QRect textRect = subElementRect(QStyle::SE_ItemViewItemText, option, widget);
+ QRect itemRect = subElementRect(QStyle::SE_ItemViewItemFocusRect, option, widget).adjusted(-1, 0, 1, 0);
+ itemRect.setTop(vopt->rect.top());
+ itemRect.setBottom(vopt->rect.bottom());
+
+ QSize sectionSize = itemRect.size();
+ if (vopt->showDecorationSelected)
+ sectionSize = vopt->rect.size();
+
+ if (view->selectionBehavior() == QAbstractItemView::SelectRows)
+ sectionSize.setWidth(vopt->rect.width());
+ if (view->selectionMode() == QAbstractItemView::NoSelection)
+ hover = false;
+ QPixmap pixmap;
+
+ if (vopt->backgroundBrush.style() != Qt::NoBrush) {
+ QPointF oldBO = painter->brushOrigin();
+ painter->setBrushOrigin(vopt->rect.topLeft());
+ painter->fillRect(vopt->rect, vopt->backgroundBrush);
+ }
+
+ if (hover || selected) {
+ QString key = QString::fromLatin1("qvdelegate-%1-%2-%3-%4-%5").arg(sectionSize.width())
+ .arg(sectionSize.height()).arg(selected).arg(active).arg(hover);
+ if (!QPixmapCache::find(key, pixmap)) {
+ pixmap = QPixmap(sectionSize);
+ pixmap.fill(Qt::transparent);
+
+ int state;
+ if (selected && hover)
+ state = LISS_HOTSELECTED;
+ else if (selected && !active)
+ state = LISS_SELECTEDNOTFOCUS;
+ else if (selected)
+ state = LISS_SELECTED;
+ else
+ state = LISS_HOT;
+
+ QPainter pixmapPainter(&pixmap);
+ XPThemeData theme(d->treeViewHelper(), &pixmapPainter, QLatin1String("TREEVIEW"),
+ LVP_LISTITEM, state, QRect(0, 0, sectionSize.width(), sectionSize.height()));
+ if (theme.isValid()) {
+ d->drawBackground(theme);
+ } else {
+ QWindowsXPStyle::drawPrimitive(PE_PanelItemViewItem, option, painter, widget);
+ break;;
+ }
+ QPixmapCache::insert(key, pixmap);
+ }
+
+ if (vopt->showDecorationSelected) {
+ const int frame = 2; //Assumes a 2 pixel pixmap border
+ QRect srcRect = QRect(0, 0, sectionSize.width(), sectionSize.height());
+ QRect pixmapRect = vopt->rect;
+ bool reverse = vopt->direction == Qt::RightToLeft;
+ bool leftSection = vopt->viewItemPosition == QStyleOptionViewItemV4::Beginning;
+ bool rightSection = vopt->viewItemPosition == QStyleOptionViewItemV4::End;
+ if (vopt->viewItemPosition == QStyleOptionViewItemV4::OnlyOne
+ || vopt->viewItemPosition == QStyleOptionViewItemV4::Invalid)
+ painter->drawPixmap(pixmapRect.topLeft(), pixmap);
+ else if (reverse ? rightSection : leftSection)
+ painter->drawPixmap(pixmapRect, pixmap, srcRect.adjusted(0, 0, -frame, 0));
+ else if (reverse ? leftSection : rightSection)
+ painter->drawPixmap(pixmapRect, pixmap,
+ srcRect.adjusted(frame, 0, 0, 0));
+ else if (vopt->viewItemPosition == QStyleOptionViewItemV4::Middle)
+ painter->drawPixmap(pixmapRect, pixmap,
+ srcRect.adjusted(frame, 0, -frame, 0));
+ } else {
+ if (vopt->text.isEmpty() && vopt->icon.isNull())
+ break;
+ painter->drawPixmap(itemRect.topLeft(), pixmap);
+ }
+ }
+ } else {
+ QWindowsXPStyle::drawPrimitive(element, option, painter, widget);
+ }
+ break;
+ }
+ case PE_Widget:
+ {
+ const QDialogButtonBox *buttonBox = 0;
+
+ if (qobject_cast<const QMessageBox *> (widget))
+ buttonBox = qFindChild<const QDialogButtonBox *>(widget,QLatin1String("qt_msgbox_buttonbox"));
+#ifndef QT_NO_INPUTDIALOG
+ else if (qobject_cast<const QInputDialog *> (widget))
+ buttonBox = qFindChild<const QDialogButtonBox *>(widget,QLatin1String("qt_inputdlg_buttonbox"));
+#endif // QT_NO_INPUTDIALOG
+
+ if (buttonBox) {
+ //draw white panel part
+ XPThemeData theme(widget, painter, QLatin1String("TASKDIALOG"), TDLG_PRIMARYPANEL, 0, option->rect);
+ QRect toprect = option->rect;
+ toprect.setBottom(buttonBox->geometry().top());
+ theme.rect = toprect;
+ d->drawBackground(theme);
+
+ //draw bottom panel part
+ QRect buttonRect = option->rect;
+ buttonRect.setTop(buttonBox->geometry().top());
+ theme.rect = buttonRect;
+ theme.partId = TDLG_SECONDARYPANEL;
+ d->drawBackground(theme);
+ }
+ }
+ break;
+ default:
+ QWindowsXPStyle::drawPrimitive(element, option, painter, widget);
+ break;
+ }
+}
+
+
+/*!
+
+ \reimp
+
+ see drawPrimitive for comments on the animation support
+
+ */
+void QWindowsVistaStyle::drawControl(ControlElement element, const QStyleOption *option,
+ QPainter *painter, const QWidget *widget) const
+{
+ QWindowsVistaStylePrivate *d = const_cast<QWindowsVistaStylePrivate*>(d_func());
+
+ if (!QWindowsVistaStylePrivate::useVista()) {
+ QWindowsStyle::drawControl(element, option, painter, widget);
+ return;
+ }
+
+ bool selected = option->state & State_Selected;
+ bool pressed = option->state & State_Sunken;
+ bool disabled = !(option->state & State_Enabled);
+
+ int state = option->state;
+ QString name;
+
+ QRect rect(option->rect);
+ State flags = option->state;
+ int partId = 0;
+ int stateId = 0;
+
+ QRect oldRect;
+ QRect newRect;
+
+ if (d->transitionsEnabled() && widget) {
+ if (const QStyleOptionButton *button = qstyleoption_cast<const QStyleOptionButton *>(option)) {
+ if ((qobject_cast<const QPushButton*>(widget) && element == CE_PushButtonBevel))
+ {
+ QWidget *w = const_cast<QWidget *> (widget);
+ int oldState = w->property("_q_stylestate").toInt();
+ oldRect = w->property("_q_stylerect").toRect();
+ newRect = w->rect();
+ w->setProperty("_q_stylestate", (int)option->state);
+ w->setProperty("_q_stylerect", w->rect());
+
+ bool wasDefault = w->property("_q_isdefault").toBool();
+ bool isDefault = button->features & QStyleOptionButton::DefaultButton;
+ w->setProperty("_q_isdefault", isDefault);
+
+ bool doTransition = ((state & State_Sunken) != (oldState & State_Sunken) ||
+ (state & State_On) != (oldState & State_On) ||
+ (state & State_MouseOver) != (oldState & State_MouseOver));
+
+ if (oldRect != newRect || (wasDefault && !isDefault))
+ {
+ doTransition = false;
+ d->stopAnimation(widget);
+ }
+
+ if (doTransition) {
+ QImage startImage(option->rect.size(), QImage::Format_ARGB32_Premultiplied);
+ QImage endImage(option->rect.size(), QImage::Format_ARGB32_Premultiplied);
+ Animation *anim = d->widgetAnimation(widget);
+
+ QStyleOptionButton opt = *button;
+ opt.state = (QStyle::State)oldState;
+
+ startImage.fill(0);
+ Transition *t = new Transition;
+ t->setWidget(w);
+ QPainter startPainter(&startImage);
+
+ if (!anim) {
+ drawControl(element, &opt, &startPainter, 0 /* Intentional */);
+ } else {
+ anim->paint(&startPainter, &opt);
+ d->stopAnimation(widget);
+ }
+
+ t->setStartImage(startImage);
+ d->startAnimation(t);
+
+ endImage.fill(0);
+ QPainter endPainter(&endImage);
+ drawControl(element, option, &endPainter, 0 /* Intentional */);
+ t->setEndImage(endImage);
+ int duration = 0;
+ HTHEME theme = pOpenThemeData(0, L"Button");
+
+ int fromState = buttonStateId(oldState, BP_PUSHBUTTON);
+ int toState = buttonStateId(option->state, BP_PUSHBUTTON);
+ if (pGetThemeTransitionDuration(theme, BP_PUSHBUTTON, fromState, toState, TMT_TRANSITIONDURATIONS, &duration) == S_OK)
+ t->setDuration(duration);
+ else
+ t->setDuration(0);
+ t->setStartTime(QTime::currentTime());
+ }
+ }
+ }
+ }
+ switch (element) {
+ case CE_PushButtonBevel:
+ if (const QStyleOptionButton *btn = qstyleoption_cast<const QStyleOptionButton *>(option))
+ {
+
+ if (Animation *anim = d->widgetAnimation(widget)) {
+ anim->paint(painter, option);
+ } else {
+ name = QLatin1String("BUTTON");
+ partId = BP_PUSHBUTTON;
+ if (btn->features & QStyleOptionButton::CommandLinkButton)
+ partId = BP_COMMANDLINK;
+ bool justFlat = (btn->features & QStyleOptionButton::Flat) && !(flags & (State_On|State_Sunken));
+ if (!(flags & State_Enabled) && !(btn->features & QStyleOptionButton::Flat))
+ stateId = PBS_DISABLED;
+ else if (justFlat)
+ ;
+ else if (flags & (State_Sunken | State_On))
+ stateId = PBS_PRESSED;
+ else if (flags & State_MouseOver)
+ stateId = PBS_HOT;
+ else if (btn->features & QStyleOptionButton::DefaultButton && (state & State_Active))
+ stateId = PBS_DEFAULTED;
+ else
+ stateId = PBS_NORMAL;
+
+ if (!justFlat) {
+
+ if (widget && d->transitionsEnabled() && (btn->features & QStyleOptionButton::DefaultButton) &&
+ !(state & (State_Sunken | State_On)) && !(state & State_MouseOver) &&
+ (state & State_Enabled) && (state & State_Active))
+ {
+ Animation *anim = d->widgetAnimation(widget);
+ if (!anim && widget) {
+ QImage startImage(option->rect.size(), QImage::Format_ARGB32_Premultiplied);
+ startImage.fill(0);
+ QImage alternateImage(option->rect.size(), QImage::Format_ARGB32_Premultiplied);
+ alternateImage.fill(0);
+
+ Pulse *pulse = new Pulse;
+ pulse->setWidget(const_cast<QWidget*>(widget));
+
+ QPainter startPainter(&startImage);
+ stateId = PBS_DEFAULTED;
+ XPThemeData theme(widget, &startPainter, name, partId, stateId, rect);
+ d->drawBackground(theme);
+
+ QPainter alternatePainter(&alternateImage);
+ theme.stateId = PBS_DEFAULTED_ANIMATING;
+ theme.painter = &alternatePainter;
+ d->drawBackground(theme);
+ pulse->setPrimaryImage(startImage);
+ pulse->setAlternateImage(alternateImage);
+ pulse->setStartTime(QTime::currentTime());
+ pulse->setDuration(2000);
+ d->startAnimation(pulse);
+ anim = pulse;
+ }
+
+ if (anim)
+ anim->paint(painter, option);
+ else {
+ XPThemeData theme(widget, painter, name, partId, stateId, rect);
+ d->drawBackground(theme);
+ }
+ }
+ else {
+ d->stopAnimation(widget);
+ XPThemeData theme(widget, painter, name, partId, stateId, rect);
+ d->drawBackground(theme);
+ }
+ }
+ }
+ if (btn->features & QStyleOptionButton::HasMenu) {
+ int mbiw = 0, mbih = 0;
+ XPThemeData theme(widget, 0, QLatin1String("TOOLBAR"), TP_DROPDOWNBUTTON);
+ if (theme.isValid()) {
+ SIZE size;
+ if (pGetThemePartSize(theme.handle(), 0, theme.partId, theme.stateId, 0, TS_TRUE, &size) == S_OK) {
+ mbiw = size.cx;
+ mbih = size.cy;
+ }
+ }
+ QRect ir = subElementRect(SE_PushButtonContents, option, 0);
+ QStyleOptionButton newBtn = *btn;
+ newBtn.rect = QStyle::visualRect(option->direction, option->rect,
+ QRect(ir.right() - mbiw - 2, (option->rect.height()/2) - (mbih/2),
+ mbiw + 1, mbih + 1));
+ drawPrimitive(PE_IndicatorArrowDown, &newBtn, painter, widget);
+ }
+ return;
+ }
+ break;
+#ifndef QT_NO_PROGRESSBAR
+ case CE_ProgressBarContents:
+ if (const QStyleOptionProgressBar *bar
+ = qstyleoption_cast<const QStyleOptionProgressBar *>(option)) {
+ int stateId = MBI_NORMAL;
+ if (disabled)
+ stateId = MBI_DISABLED;
+ bool isIndeterminate = (bar->minimum == 0 && bar->maximum == 0);
+ bool vertical = false;
+ bool inverted = false;
+ if (const QStyleOptionProgressBarV2 *pb2 = qstyleoption_cast<const QStyleOptionProgressBarV2 *>(option)) {
+ vertical = (pb2->orientation == Qt::Vertical);
+ inverted = pb2->invertedAppearance;
+ }
+
+ if (const QProgressBar *progressbar = qobject_cast<const QProgressBar *>(widget)) {
+ if (((progressbar->value() > 0 && d->transitionsEnabled()) || isIndeterminate)) {
+ if (!d->widgetAnimation(progressbar) && progressbar->value() < progressbar->maximum()) {
+ Animation *a = new Animation;
+ a->setWidget(const_cast<QWidget*>(widget));
+ a->setStartTime(QTime::currentTime());
+ d->startAnimation(a);
+ }
+ } else {
+ d->stopAnimation(progressbar);
+ }
+ }
+
+ XPThemeData theme(widget, painter, QLatin1String("PROGRESS"), vertical ? PP_FILLVERT : PP_FILL);
+ theme.rect = option->rect;
+ bool reverse = bar->direction == Qt::LeftToRight && inverted || bar->direction == Qt::RightToLeft && !inverted;
+ QTime current = QTime::currentTime();
+
+ if (isIndeterminate) {
+ if (Animation *a = d->widgetAnimation(widget)) {
+ int glowSize = 120;
+ int animationWidth = glowSize * 2 + (vertical ? theme.rect.height() : theme.rect.width());
+ int animOffset = a->startTime().msecsTo(current) / 4;
+ if (animOffset > animationWidth)
+ a->setStartTime(QTime::currentTime());
+ painter->save();
+ painter->setClipRect(theme.rect);
+ QRect animRect;
+ QSize pixmapSize(14, 14);
+ if (vertical) {
+ animRect = QRect(theme.rect.left(),
+ inverted ? rect.top() - glowSize + animOffset :
+ rect.bottom() + glowSize - animOffset,
+ rect.width(), glowSize);
+ pixmapSize.setHeight(animRect.height());
+ } else {
+ animRect = QRect(rect.left() - glowSize + animOffset,
+ rect.top(), glowSize, rect.height());
+ animRect = QStyle::visualRect(reverse ? Qt::RightToLeft : Qt::LeftToRight,
+ option->rect, animRect);
+ pixmapSize.setWidth(animRect.width());
+ }
+ QString name = QString::fromLatin1("qiprogress-%1-%2").arg(pixmapSize.width()).arg(pixmapSize.height());
+ QPixmap pixmap;
+ if (!QPixmapCache::find(name, pixmap)) {
+ QImage image(pixmapSize, QImage::Format_ARGB32);
+ image.fill(Qt::transparent);
+ QPainter imagePainter(&image);
+ theme.painter = &imagePainter;
+ theme.partId = vertical ? PP_FILLVERT : PP_FILL;
+ theme.rect = QRect(QPoint(0,0), theme.rect.size());
+ QLinearGradient alphaGradient(0, 0, vertical ? 0 : image.width(),
+ vertical ? image.height() : 0);
+ alphaGradient.setColorAt(0, QColor(0, 0, 0, 0));
+ alphaGradient.setColorAt(0.5, QColor(0, 0, 0, 220));
+ alphaGradient.setColorAt(1, QColor(0, 0, 0, 0));
+ imagePainter.fillRect(image.rect(), alphaGradient);
+ imagePainter.setCompositionMode(QPainter::CompositionMode_SourceIn);
+ d->drawBackground(theme);
+ imagePainter.end();
+ pixmap = QPixmap::fromImage(image);
+ QPixmapCache::insert(name, pixmap);
+ }
+ painter->drawPixmap(animRect, pixmap);
+ painter->restore();
+ }
+ }
+ else {
+ qint64 progress = qMax<qint64>(bar->progress, bar->minimum); // workaround for bug in QProgressBar
+
+ if (vertical) {
+ int maxHeight = option->rect.height();
+ int minHeight = 0;
+ double vc6_workaround = ((progress - qint64(bar->minimum)) / qMax(double(1.0), double(qint64(bar->maximum) - qint64(bar->minimum))) * maxHeight);
+ int height = isIndeterminate ? maxHeight: qMax(int(vc6_workaround), minHeight);
+ theme.rect.setHeight(height);
+ if (!inverted)
+ theme.rect.moveTop(rect.height() - theme.rect.height());
+ } else {
+ int maxWidth = option->rect.width();
+ int minWidth = 0;
+ double vc6_workaround = ((progress - qint64(bar->minimum)) / qMax(double(1.0), double(qint64(bar->maximum) - qint64(bar->minimum))) * maxWidth);
+ int width = isIndeterminate ? maxWidth : qMax(int(vc6_workaround), minWidth);
+ theme.rect.setWidth(width);
+ theme.rect = QStyle::visualRect(reverse ? Qt::RightToLeft : Qt::LeftToRight,
+ option->rect, theme.rect);
+ }
+ d->drawBackground(theme);
+
+ if (Animation *a = d->widgetAnimation(widget)) {
+ int glowSize = 140;
+ int animationWidth = glowSize * 2 + (vertical ? theme.rect.height() : theme.rect.width());
+ int animOffset = a->startTime().msecsTo(current) / 4;
+ theme.partId = vertical ? PP_MOVEOVERLAYVERT : PP_MOVEOVERLAY;
+ if (animOffset > animationWidth) {
+ if (bar->progress < bar->maximum)
+ a->setStartTime(QTime::currentTime());
+ else
+ d->stopAnimation(widget); //we stop the glow motion only after it has
+ //moved out of view
+ }
+ painter->save();
+ painter->setClipRect(theme.rect);
+ if (vertical) {
+ theme.rect = QRect(theme.rect.left(),
+ inverted ? rect.top() - glowSize + animOffset :
+ rect.bottom() + glowSize - animOffset,
+ rect.width(), glowSize);
+ } else {
+ theme.rect = QRect(rect.left() - glowSize + animOffset,rect.top(), glowSize, rect.height());
+ theme.rect = QStyle::visualRect(reverse ? Qt::RightToLeft : Qt::LeftToRight, option->rect, theme.rect);
+ }
+ d->drawBackground(theme);
+ painter->restore();
+ }
+ }
+ }
+ break;
+#endif // QT_NO_PROGRESSBAR
+ case CE_MenuBarItem:
+ {
+
+ if (const QStyleOptionMenuItem *mbi = qstyleoption_cast<const QStyleOptionMenuItem *>(option))
+ {
+ if (mbi->menuItemType == QStyleOptionMenuItem::DefaultItem)
+ break;
+
+ QPalette::ColorRole textRole = disabled ? QPalette::Text : QPalette::ButtonText;
+ QPixmap pix = mbi->icon.pixmap(pixelMetric(PM_SmallIconSize, option, widget), QIcon::Normal);
+
+ uint alignment = Qt::AlignCenter | Qt::TextShowMnemonic | Qt::TextDontClip | Qt::TextSingleLine;
+ if (!styleHint(SH_UnderlineShortcut, mbi, widget))
+ alignment |= Qt::TextHideMnemonic;
+
+ //The rect adjustment is a workaround for the menu not really filling its background.
+ XPThemeData theme(widget, painter, QLatin1String("MENU"), MENU_BARBACKGROUND, 0, option->rect.adjusted(-1, 1 , 2, 1));
+ d->drawBackground(theme);
+
+ int stateId = MBI_NORMAL;
+ if (disabled)
+ stateId = MBI_DISABLED;
+ else if (pressed)
+ stateId = MBI_PUSHED;
+ else if (selected)
+ stateId = MBI_HOT;
+
+ XPThemeData theme2(widget, painter, QLatin1String("MENU"), MENU_BARITEM, stateId, option->rect);
+ d->drawBackground(theme2);
+
+ if (!pix.isNull())
+ drawItemPixmap(painter, mbi->rect, alignment, pix);
+ else
+ drawItemText(painter, mbi->rect, alignment, mbi->palette, mbi->state & State_Enabled, mbi->text, textRole);
+ }
+ }
+ break;
+#ifndef QT_NO_MENU
+ case CE_MenuItem:
+ if (const QStyleOptionMenuItem *menuitem = qstyleoption_cast<const QStyleOptionMenuItem *>(option)) {
+ // windows always has a check column, regardless whether we have an icon or not
+ int checkcol = qMax(menuitem->maxIconWidth, 28);
+ QColor darkLine = option->palette.background().color().darker(108);
+ QColor lightLine = option->palette.background().color().lighter(107);
+ QRect rect = option->rect;
+ QStyleOptionMenuItem mbiCopy = *menuitem;
+
+ //draw vertical menu line
+ QPoint p1 = QStyle::visualPos(option->direction, menuitem->rect, QPoint(checkcol, rect.top()));
+ QPoint p2 = QStyle::visualPos(option->direction, menuitem->rect, QPoint(checkcol, rect.bottom()));
+ QRect gutterRect(p1.x(), p1.y(), 3, p2.y() - p1.y() + 1);
+ XPThemeData theme2(widget, painter, QLatin1String("MENU"), MENU_POPUPGUTTER, stateId, gutterRect);
+ d->drawBackground(theme2);
+
+ int x, y, w, h;
+ menuitem->rect.getRect(&x, &y, &w, &h);
+ int tab = menuitem->tabWidth;
+ bool dis = !(menuitem->state & State_Enabled);
+ bool checked = menuitem->checkType != QStyleOptionMenuItem::NotCheckable
+ ? menuitem->checked : false;
+ bool act = menuitem->state & State_Selected;
+
+ if (menuitem->menuItemType == QStyleOptionMenuItem::Separator) {
+ int yoff = y-2 + h / 2;
+ QPoint p1 = QPoint(x + checkcol, yoff);
+ QPoint p2 = QPoint(x + w + 6 , yoff);
+ int stateId = stateId = MBI_HOT;
+ QRect subRect(p1.x(), p1.y(), p2.x() - p1.x(), 6);
+ subRect = QStyle::visualRect(option->direction, option->rect, subRect );
+ XPThemeData theme2(widget, painter, QLatin1String("MENU"), MENU_POPUPSEPARATOR, stateId, subRect);
+ d->drawBackground(theme2);
+ return;
+ }
+
+ QRect vCheckRect = visualRect(option->direction, menuitem->rect, QRect(menuitem->rect.x(),
+ menuitem->rect.y(), checkcol - 6, menuitem->rect.height()));
+
+ if (act) {
+ int stateId = stateId = MBI_HOT;
+ XPThemeData theme2(widget, painter, QLatin1String("MENU"), MENU_POPUPITEM, stateId, option->rect);
+ d->drawBackground(theme2);
+ }
+
+ if (checked) {
+ XPThemeData theme(widget, painter, QLatin1String("MENU"), MENU_POPUPCHECKBACKGROUND,
+ menuitem->icon.isNull() ? MBI_HOT : MBI_PUSHED, vCheckRect);
+ d->drawBackground(theme);
+ if (menuitem->icon.isNull()) {
+ theme.partId = MENU_POPUPCHECK;
+ bool bullet = menuitem->checkType & QStyleOptionMenuItem::Exclusive;
+ if (dis)
+ theme.stateId = bullet ? MC_BULLETDISABLED: MC_CHECKMARKDISABLED;
+ else
+ theme.stateId = bullet ? MC_BULLETNORMAL: MC_CHECKMARKNORMAL;
+ d->drawBackground(theme);
+ }
+ }
+
+ if (!menuitem->icon.isNull()) {
+ QIcon::Mode mode = dis ? QIcon::Disabled : QIcon::Normal;
+ if (act && !dis)
+ mode = QIcon::Active;
+ QPixmap pixmap;
+ if (checked)
+ pixmap = menuitem->icon.pixmap(pixelMetric(PM_SmallIconSize, option, widget), mode, QIcon::On);
+ else
+ pixmap = menuitem->icon.pixmap(pixelMetric(PM_SmallIconSize, option, widget), mode);
+ int pixw = pixmap.width();
+ int pixh = pixmap.height();
+ QRect pmr(0, 0, pixw, pixh);
+ pmr.moveCenter(vCheckRect.center());
+ painter->setPen(menuitem->palette.text().color());
+ painter->drawPixmap(pmr.topLeft(), pixmap);
+ }
+
+ painter->setPen(menuitem->palette.buttonText().color());
+
+ QColor discol;
+ if (dis) {
+ discol = menuitem->palette.text().color();
+ painter->setPen(discol);
+ }
+
+ int xm = windowsItemFrame + checkcol + windowsItemHMargin;
+ int xpos = menuitem->rect.x() + xm;
+ QRect textRect(xpos, y + windowsItemVMargin, w - xm - windowsRightBorder - tab + 1, h - 2 * windowsItemVMargin);
+ QRect vTextRect = visualRect(option->direction, menuitem->rect, textRect);
+ QString s = menuitem->text;
+ if (!s.isEmpty()) { // draw text
+ painter->save();
+ int t = s.indexOf(QLatin1Char('\t'));
+ int text_flags = Qt::AlignVCenter | Qt::TextShowMnemonic | Qt::TextDontClip | Qt::TextSingleLine;
+ if (!styleHint(SH_UnderlineShortcut, menuitem, widget))
+ text_flags |= Qt::TextHideMnemonic;
+ text_flags |= Qt::AlignLeft;
+ if (t >= 0) {
+ QRect vShortcutRect = visualRect(option->direction, menuitem->rect,
+ QRect(textRect.topRight(), QPoint(menuitem->rect.right(), textRect.bottom())));
+ painter->drawText(vShortcutRect, text_flags, s.mid(t + 1));
+ s = s.left(t);
+ }
+ QFont font = menuitem->font;
+ if (menuitem->menuItemType == QStyleOptionMenuItem::DefaultItem)
+ font.setBold(true);
+ painter->setFont(font);
+ painter->setPen(discol);
+ painter->drawText(vTextRect, text_flags, s.left(t));
+ painter->restore();
+ }
+ if (menuitem->menuItemType == QStyleOptionMenuItem::SubMenu) {// draw sub menu arrow
+ int dim = (h - 2 * windowsItemFrame) / 2;
+ PrimitiveElement arrow;
+ arrow = (option->direction == Qt::RightToLeft) ? PE_IndicatorArrowLeft : PE_IndicatorArrowRight;
+ xpos = x + w - windowsArrowHMargin - windowsItemFrame - dim;
+ QRect vSubMenuRect = visualRect(option->direction, menuitem->rect, QRect(xpos, y + h / 2 - dim / 2, dim, dim));
+ QStyleOptionMenuItem newMI = *menuitem;
+ newMI.rect = vSubMenuRect;
+ newMI.state = dis ? State_None : State_Enabled;
+ drawPrimitive(arrow, &newMI, painter, widget);
+ }
+ }
+ break;
+#endif // QT_NO_MENU
+ case CE_HeaderSection:
+ if (const QStyleOptionHeader *header = qstyleoption_cast<const QStyleOptionHeader *>(option)) {
+ name = QLatin1String("HEADER");
+ partId = HP_HEADERITEM;
+ if (flags & State_Sunken)
+ stateId = HIS_PRESSED;
+ else if (flags & State_MouseOver)
+ stateId = HIS_HOT;
+ else
+ stateId = HIS_NORMAL;
+
+ if (header->sortIndicator != QStyleOptionHeader::None)
+ stateId += 3;
+
+ XPThemeData theme(widget, painter, name, partId, stateId, option->rect);
+ d->drawBackground(theme);
+ }
+ break;
+ case CE_MenuBarEmptyArea:
+ {
+ int stateId = MBI_NORMAL;
+ if (!(state & State_Enabled))
+ stateId = MBI_DISABLED;
+ XPThemeData theme(widget, painter, QLatin1String("MENU"), MENU_BARBACKGROUND, stateId, option->rect);
+ d->drawBackground(theme);
+ }
+ break;
+ case CE_ToolBar:
+ if (const QStyleOptionToolBar *toolbar = qstyleoption_cast<const QStyleOptionToolBar *>(option)) {
+ QPalette pal = option->palette;
+ pal.setColor(QPalette::Dark, option->palette.background().color().darker(130));
+ QStyleOptionToolBar copyOpt = *toolbar;
+ copyOpt.palette = pal;
+ QWindowsStyle::drawControl(element, &copyOpt, painter, widget);
+ }
+ break;
+ case CE_DockWidgetTitle:
+ if (const QDockWidget *dockWidget = qobject_cast<const QDockWidget *>(widget)) {
+ QRect rect = option->rect;
+ if (dockWidget->isFloating()) {
+ QWindowsXPStyle::drawControl(element, option, painter, widget);
+ break; //otherwise fall through
+ }
+
+ if (const QStyleOptionDockWidget *dwOpt = qstyleoption_cast<const QStyleOptionDockWidget *>(option)) {
+
+ const QStyleOptionDockWidgetV2 *v2
+ = qstyleoption_cast<const QStyleOptionDockWidgetV2*>(dwOpt);
+ bool verticalTitleBar = v2 == 0 ? false : v2->verticalTitleBar;
+
+ if (verticalTitleBar) {
+ QSize s = rect.size();
+ s.transpose();
+ rect.setSize(s);
+
+ painter->translate(rect.left() - 1, rect.top() + rect.width());
+ painter->rotate(-90);
+ painter->translate(-rect.left() + 1, -rect.top());
+ }
+
+ painter->setBrush(option->palette.background().color().darker(110));
+ painter->setPen(option->palette.background().color().darker(130));
+ painter->drawRect(rect.adjusted(0, 1, -1, -3));
+
+ int buttonMargin = 4;
+ int mw = pixelMetric(QStyle::PM_DockWidgetTitleMargin, dwOpt, widget);
+ int fw = pixelMetric(PM_DockWidgetFrameWidth, dwOpt, widget);
+ const QDockWidget *dw = qobject_cast<const QDockWidget *>(widget);
+ bool isFloating = dw != 0 && dw->isFloating();
+
+ QRect r = option->rect.adjusted(0, 2, -1, -3);
+ QRect titleRect = r;
+
+ if (dwOpt->closable) {
+ QSize sz = standardIcon(QStyle::SP_TitleBarCloseButton, dwOpt, widget).actualSize(QSize(10, 10));
+ titleRect.adjust(0, 0, -sz.width() - mw - buttonMargin, 0);
+ }
+
+ if (dwOpt->floatable) {
+ QSize sz = standardIcon(QStyle::SP_TitleBarMaxButton, dwOpt, widget).actualSize(QSize(10, 10));
+ titleRect.adjust(0, 0, -sz.width() - mw - buttonMargin, 0);
+ }
+
+ if (isFloating) {
+ titleRect.adjust(0, -fw, 0, 0);
+ if (widget != 0 && widget->windowIcon().cacheKey() != qApp->windowIcon().cacheKey())
+ titleRect.adjust(titleRect.height() + mw, 0, 0, 0);
+ } else {
+ titleRect.adjust(mw, 0, 0, 0);
+ if (!dwOpt->floatable && !dwOpt->closable)
+ titleRect.adjust(0, 0, -mw, 0);
+ }
+ if (!verticalTitleBar)
+ titleRect = visualRect(dwOpt->direction, r, titleRect);
+
+ if (!dwOpt->title.isEmpty()) {
+ QString titleText = painter->fontMetrics().elidedText(dwOpt->title, Qt::ElideRight,
+ verticalTitleBar ? titleRect.height() : titleRect.width());
+ const int indent = painter->fontMetrics().descent();
+ drawItemText(painter, rect.adjusted(indent + 1, 1, -indent - 1, -1),
+ Qt::AlignLeft | Qt::AlignVCenter, dwOpt->palette,
+ dwOpt->state & State_Enabled, titleText,
+ QPalette::WindowText);
+ }
+ }
+ break;
+ }
+#ifndef QT_NO_ITEMVIEWS
+ case CE_ItemViewItem:
+ {
+ const QStyleOptionViewItemV4 *vopt;
+ const QAbstractItemView *view = qobject_cast<const QAbstractItemView *>(widget);
+ bool newStyle = false;
+
+ if (const QListView *listview = qobject_cast<const QListView *>(widget)) {
+ if (listview->viewMode() == QListView::IconMode)
+ newStyle = true;
+ } else if (const QTreeView* treeview = qobject_cast<const QTreeView *>(widget)) {
+ newStyle = true;
+ }
+ if (newStyle && view && (vopt = qstyleoption_cast<const QStyleOptionViewItemV4 *>(option))) {
+ /*
+ // We cannot currently get the correct selection color for "explorer style" views
+ COLORREF cref = 0;
+ XPThemeData theme(d->treeViewHelper(), 0, QLatin1String("LISTVIEW"), 0, 0);
+ unsigned int res = pGetThemeColor(theme.handle(), LVP_LISTITEM, LISS_SELECTED, TMT_TEXTCOLOR, &cref);
+ QColor textColor(GetRValue(cref), GetGValue(cref), GetBValue(cref));
+ */
+ QPalette palette = vopt->palette;
+ palette.setColor(QPalette::All, QPalette::HighlightedText, palette.color(QPalette::Active, QPalette::Text));
+ // Note that setting a saturated color here results in ugly XOR colors in the focus rect
+ palette.setColor(QPalette::All, QPalette::Highlight, palette.base().color().darker(108));
+ QStyleOptionViewItemV4 adjustedOption = *vopt;
+ adjustedOption.palette = palette;
+ // We hide the focusrect in singleselection as it is not required
+ if ((view->selectionMode() == QAbstractItemView::SingleSelection)
+ && !(vopt->state & State_KeyboardFocusChange))
+ adjustedOption.state &= ~State_HasFocus;
+ QWindowsXPStyle::drawControl(element, &adjustedOption, painter, widget);
+ } else {
+ QWindowsXPStyle::drawControl(element, option, painter, widget);
+ }
+ break;
+ }
+#endif // QT_NO_ITEMVIEWS
+
+ default:
+ QWindowsXPStyle::drawControl(element, option, painter, widget);
+ break;
+ }
+}
+
+/*!
+ \reimp
+
+ see drawPrimitive for comments on the animation support
+
+ */
+void QWindowsVistaStyle::drawComplexControl(ComplexControl control, const QStyleOptionComplex *option,
+ QPainter *painter, const QWidget *widget) const
+{
+ QWindowsVistaStylePrivate *d = const_cast<QWindowsVistaStylePrivate*>(d_func());
+ if (!QWindowsVistaStylePrivate::useVista()) {
+ QWindowsStyle::drawComplexControl(control, option, painter, widget);
+ return;
+ }
+
+ State state = option->state;
+ SubControls sub = option->subControls;
+ QRect r = option->rect;
+
+ int partId = 0;
+ int stateId = 0;
+
+ State flags = option->state;
+ if (widget && widget->testAttribute(Qt::WA_UnderMouse) && widget->isActiveWindow())
+ flags |= State_MouseOver;
+
+ if (d->transitionsEnabled() && widget) {
+ if ((qobject_cast<const QScrollBar *>(widget) && control == CC_ScrollBar)
+#ifndef QT_NO_SPINBOX
+ || (qobject_cast<const QAbstractSpinBox*>(widget) && control == CC_SpinBox)
+#endif // QT_NO_SPINBOX
+#ifndef QT_NO_COMBOBOX
+ || (qobject_cast<const QComboBox*>(widget) && control == CC_ComboBox)
+#endif // QT_NO_COMBOBOX
+ )
+ {
+ QWidget *w = const_cast<QWidget *> (widget);
+
+ int oldState = w->property("_q_stylestate").toInt();
+ int oldActiveControls = w->property("_q_stylecontrols").toInt();
+ QRect oldRect = w->property("_q_stylerect").toRect();
+ w->setProperty("_q_stylestate", (int)option->state);
+ w->setProperty("_q_stylecontrols", (int)option->activeSubControls);
+ w->setProperty("_q_stylerect", w->rect());
+
+ bool doTransition = ((state & State_Sunken) != (oldState & State_Sunken) ||
+ (state & State_On) != (oldState & State_On) ||
+ (state & State_MouseOver) != (oldState & State_MouseOver) ||
+ oldActiveControls != option->activeSubControls);
+
+
+ if (qstyleoption_cast<const QStyleOptionSlider *>(option)) {
+ QRect oldSliderPos = w->property("_q_stylesliderpos").toRect();
+ QRect currentPos = subControlRect(CC_ScrollBar, option, SC_ScrollBarSlider, widget);
+ w->setProperty("_q_stylesliderpos", currentPos);
+ if (oldSliderPos != currentPos) {
+ doTransition = false;
+ d->stopAnimation(widget);
+ }
+ } else if (control == CC_SpinBox) {
+ //spinboxes have a transition when focus changes
+ if (!doTransition)
+ doTransition = (state & State_HasFocus) != (oldState & State_HasFocus);
+ }
+
+ if (oldRect != option->rect) {
+ doTransition = false;
+ d->stopAnimation(widget);
+ }
+
+ if (doTransition) {
+ QImage startImage(option->rect.size(), QImage::Format_ARGB32_Premultiplied);
+ QImage endImage(option->rect.size(), QImage::Format_ARGB32_Premultiplied);
+ Animation *anim = d->widgetAnimation(widget);
+ Transition *t = new Transition;
+ t->setWidget(w);
+ if (!anim) {
+ if (const QStyleOptionComboBox *combo = qstyleoption_cast<const QStyleOptionComboBox*>(option)) {
+ //Combo boxes are special cased to avoid cleartype issues
+ startImage.fill(0);
+ QPainter startPainter(&startImage);
+ QStyleOptionComboBox startCombo = *combo;
+ startCombo.state = (QStyle::State)oldState;
+ startCombo.activeSubControls = (QStyle::SubControl)oldActiveControls;
+ drawComplexControl(control, &startCombo, &startPainter, 0 /* Intentional */);
+ t->setStartImage(startImage);
+ } else if (const QStyleOptionSlider *slider = qstyleoption_cast<const QStyleOptionSlider*>(option)) {
+ //This is a workaround for the direct3d engine as it currently has some issues with grabWindow
+ startImage.fill(0);
+ QPainter startPainter(&startImage);
+ QStyleOptionSlider startSlider = *slider;
+ startSlider.state = (QStyle::State)oldState;
+ startSlider.activeSubControls = (QStyle::SubControl)oldActiveControls;
+ drawComplexControl(control, &startSlider, &startPainter, 0 /* Intentional */);
+ t->setStartImage(startImage);
+ } else {
+ QPoint offset(0, 0);
+ if (!widget->internalWinId())
+ offset = widget->mapTo(widget->nativeParentWidget(), offset);
+ t->setStartImage(QPixmap::grabWindow(widget->effectiveWinId(), offset.x(), offset.y(),
+ option->rect.width(), option->rect.height()).toImage());
+ }
+ } else {
+ startImage.fill(0);
+ QPainter startPainter(&startImage);
+ anim->paint(&startPainter, option);
+ t->setStartImage(startImage);
+ }
+ d->startAnimation(t);
+ endImage.fill(0);
+ QPainter endPainter(&endImage);
+ drawComplexControl(control, option, &endPainter, 0 /* Intentional */);
+ t->setEndImage(endImage);
+ t->setStartTime(QTime::currentTime());
+
+ if (option->state & State_MouseOver || option->state & State_Sunken)
+ t->setDuration(150);
+ else
+ t->setDuration(500);
+ }
+
+ if (Animation *anim = d->widgetAnimation(widget)) {
+ anim->paint(painter, option);
+ return;
+ }
+
+ }
+ }
+
+ switch (control) {
+ case CC_ComboBox:
+ if (const QStyleOptionComboBox *cmb = qstyleoption_cast<const QStyleOptionComboBox *>(option))
+ {
+ if (cmb->editable) {
+ if (sub & SC_ComboBoxEditField) {
+ partId = EP_EDITBORDER_NOSCROLL;
+ if (!(flags & State_Enabled))
+ stateId = ETS_DISABLED;
+ else if (flags & State_MouseOver)
+ stateId = ETS_HOT;
+ else if (flags & State_HasFocus)
+ stateId = ETS_FOCUSED;
+ else
+ stateId = ETS_NORMAL;
+
+ XPThemeData theme(widget, painter, QLatin1String("EDIT"), partId, stateId, r);
+
+ d->drawBackground(theme);
+ }
+ if (sub & SC_ComboBoxArrow) {
+ QRect subRect = subControlRect(CC_ComboBox, option, SC_ComboBoxArrow, widget);
+ XPThemeData theme(widget, painter, QLatin1String("COMBOBOX"));
+ theme.rect = subControlRect(CC_ComboBox, option, SC_ComboBoxArrow, widget);
+ partId = option->direction == Qt::RightToLeft ? CP_DROPDOWNBUTTONLEFT : CP_DROPDOWNBUTTONRIGHT;
+
+ if (!(cmb->state & State_Enabled))
+ stateId = CBXS_DISABLED;
+ else if (cmb->state & State_Sunken || cmb->state & State_On)
+ stateId = CBXS_PRESSED;
+ else if (cmb->state & State_MouseOver && option->activeSubControls & SC_ComboBoxArrow)
+ stateId = CBXS_HOT;
+ else
+ stateId = CBXS_NORMAL;
+
+ theme.partId = partId;
+ theme.stateId = stateId;
+ d->drawBackground(theme);
+ }
+
+ } else {
+ if (sub & SC_ComboBoxFrame) {
+ QStyleOptionButton btn;
+ btn.QStyleOption::operator=(*option);
+ btn.rect = option->rect.adjusted(-1, -1, 1, 1);
+ if (sub & SC_ComboBoxArrow)
+ btn.features = QStyleOptionButton::HasMenu;
+ drawControl(QStyle::CE_PushButton, &btn, painter, widget);
+ }
+ }
+ }
+ break;
+ case CC_ScrollBar:
+ if (const QStyleOptionSlider *scrollbar = qstyleoption_cast<const QStyleOptionSlider *>(option))
+ {
+ XPThemeData theme(widget, painter, QLatin1String("SCROLLBAR"));
+
+ bool maxedOut = (scrollbar->maximum == scrollbar->minimum);
+ if (maxedOut)
+ flags &= ~State_Enabled;
+
+ bool isHorz = flags & State_Horizontal;
+ bool isRTL = option->direction == Qt::RightToLeft;
+ if (sub & SC_ScrollBarAddLine) {
+ theme.rect = subControlRect(CC_ScrollBar, option, SC_ScrollBarAddLine, widget);
+ partId = SBP_ARROWBTN;
+ if (!(flags & State_Enabled))
+ stateId = (isHorz ? (isRTL ? ABS_LEFTDISABLED : ABS_RIGHTDISABLED) : ABS_DOWNDISABLED);
+ else if (scrollbar->activeSubControls & SC_ScrollBarAddLine && (scrollbar->state & State_Sunken))
+ stateId = (isHorz ? (isRTL ? ABS_LEFTPRESSED : ABS_RIGHTPRESSED) : ABS_DOWNPRESSED);
+ else if (scrollbar->activeSubControls & SC_ScrollBarAddLine && (scrollbar->state & State_MouseOver))
+ stateId = (isHorz ? (isRTL ? ABS_LEFTHOT : ABS_RIGHTHOT) : ABS_DOWNHOT);
+ else if (scrollbar->state & State_MouseOver)
+ stateId = (isHorz ? (isRTL ? ABS_LEFTHOVER : ABS_RIGHTHOVER) : ABS_DOWNHOVER);
+ else
+ stateId = (isHorz ? (isRTL ? ABS_LEFTNORMAL : ABS_RIGHTNORMAL) : ABS_DOWNNORMAL);
+ theme.partId = partId;
+ theme.stateId = stateId;
+ d->drawBackground(theme);
+ }
+ if (sub & SC_ScrollBarSubLine) {
+ theme.rect = subControlRect(CC_ScrollBar, option, SC_ScrollBarSubLine, widget);
+ partId = SBP_ARROWBTN;
+ if (!(flags & State_Enabled))
+ stateId = (isHorz ? (isRTL ? ABS_RIGHTDISABLED : ABS_LEFTDISABLED) : ABS_UPDISABLED);
+ else if (scrollbar->activeSubControls & SC_ScrollBarSubLine && (scrollbar->state & State_Sunken))
+ stateId = (isHorz ? (isRTL ? ABS_RIGHTPRESSED : ABS_LEFTPRESSED) : ABS_UPPRESSED);
+ else if (scrollbar->activeSubControls & SC_ScrollBarSubLine && (scrollbar->state & State_MouseOver))
+ stateId = (isHorz ? (isRTL ? ABS_RIGHTHOT : ABS_LEFTHOT) : ABS_UPHOT);
+ else if (scrollbar->state & State_MouseOver)
+ stateId = (isHorz ? (isRTL ? ABS_RIGHTHOVER : ABS_LEFTHOVER) : ABS_UPHOVER);
+ else
+ stateId = (isHorz ? (isRTL ? ABS_RIGHTNORMAL : ABS_LEFTNORMAL) : ABS_UPNORMAL);
+ theme.partId = partId;
+ theme.stateId = stateId;
+ d->drawBackground(theme);
+ }
+ if (maxedOut) {
+ theme.rect = subControlRect(CC_ScrollBar, option, SC_ScrollBarSlider, widget);
+ theme.rect = theme.rect.united(subControlRect(CC_ScrollBar, option, SC_ScrollBarSubPage, widget));
+ theme.rect = theme.rect.united(subControlRect(CC_ScrollBar, option, SC_ScrollBarAddPage, widget));
+ partId = flags & State_Horizontal ? SBP_LOWERTRACKHORZ : SBP_LOWERTRACKVERT;
+ stateId = SCRBS_DISABLED;
+ theme.partId = partId;
+ theme.stateId = stateId;
+ d->drawBackground(theme);
+ } else {
+ if (sub & SC_ScrollBarSubPage) {
+ theme.rect = subControlRect(CC_ScrollBar, option, SC_ScrollBarSubPage, widget);
+ partId = flags & State_Horizontal ? SBP_UPPERTRACKHORZ : SBP_UPPERTRACKVERT;
+ if (!(flags & State_Enabled))
+ stateId = SCRBS_DISABLED;
+ else if (scrollbar->activeSubControls & SC_ScrollBarSubPage && (scrollbar->state & State_Sunken))
+ stateId = SCRBS_PRESSED;
+ else if (scrollbar->activeSubControls & SC_ScrollBarSubPage && (scrollbar->state & State_MouseOver))
+ stateId = SCRBS_HOT;
+ else
+ stateId = SCRBS_NORMAL;
+ theme.partId = partId;
+ theme.stateId = stateId;
+ d->drawBackground(theme);
+ }
+ if (sub & SC_ScrollBarAddPage) {
+ theme.rect = subControlRect(CC_ScrollBar, option, SC_ScrollBarAddPage, widget);
+ partId = flags & State_Horizontal ? SBP_LOWERTRACKHORZ : SBP_LOWERTRACKVERT;
+ if (!(flags & State_Enabled))
+ stateId = SCRBS_DISABLED;
+ else if (scrollbar->activeSubControls & SC_ScrollBarAddPage && (scrollbar->state & State_Sunken))
+ stateId = SCRBS_PRESSED;
+ else if (scrollbar->activeSubControls & SC_ScrollBarAddPage && (scrollbar->state & State_MouseOver))
+ stateId = SCRBS_HOT;
+ else
+ stateId = SCRBS_NORMAL;
+ theme.partId = partId;
+ theme.stateId = stateId;
+ d->drawBackground(theme);
+ }
+ if (sub & SC_ScrollBarSlider) {
+ theme.rect = subControlRect(CC_ScrollBar, option, SC_ScrollBarSlider, widget);
+ if (!(flags & State_Enabled))
+ stateId = SCRBS_DISABLED;
+ else if (scrollbar->activeSubControls & SC_ScrollBarSlider && (scrollbar->state & State_Sunken))
+ stateId = SCRBS_PRESSED;
+ else if (scrollbar->activeSubControls & SC_ScrollBarSlider && (scrollbar->state & State_MouseOver))
+ stateId = SCRBS_HOT;
+ else if (option->state & State_MouseOver)
+ stateId = SCRBS_HOVER;
+ else
+ stateId = SCRBS_NORMAL;
+
+ // Draw handle
+ theme.rect = subControlRect(CC_ScrollBar, option, SC_ScrollBarSlider, widget);
+ theme.partId = flags & State_Horizontal ? SBP_THUMBBTNHORZ : SBP_THUMBBTNVERT;
+ theme.stateId = stateId;
+ d->drawBackground(theme);
+
+ // Calculate rect of gripper
+ const int swidth = theme.rect.width();
+ const int sheight = theme.rect.height();
+
+ MARGINS contentsMargin;
+ RECT rect = theme.toRECT(theme.rect);
+ pGetThemeMargins(theme.handle(), 0, theme.partId, theme.stateId, TMT_SIZINGMARGINS, &rect, &contentsMargin);
+
+ SIZE size;
+ theme.partId = flags & State_Horizontal ? SBP_GRIPPERHORZ : SBP_GRIPPERVERT;
+ pGetThemePartSize(theme.handle(), 0, theme.partId, theme.stateId, 0, TS_TRUE, &size);
+ int gw = size.cx, gh = size.cy;
+
+
+ QRect gripperBounds;
+ if (flags & State_Horizontal && ((swidth - contentsMargin.cxLeftWidth - contentsMargin.cxRightWidth) > gw)) {
+ gripperBounds.setLeft(theme.rect.left() + swidth/2 - gw/2);
+ gripperBounds.setTop(theme.rect.top() + sheight/2 - gh/2);
+ gripperBounds.setWidth(gw);
+ gripperBounds.setHeight(gh);
+ } else if ((sheight - contentsMargin.cyTopHeight - contentsMargin.cyBottomHeight) > gh) {
+ gripperBounds.setLeft(theme.rect.left() + swidth/2 - gw/2);
+ gripperBounds.setTop(theme.rect.top() + sheight/2 - gh/2);
+ gripperBounds.setWidth(gw);
+ gripperBounds.setHeight(gh);
+ }
+
+ // Draw gripper if there is enough space
+ if (!gripperBounds.isEmpty() && flags & State_Enabled) {
+ painter->save();
+ XPThemeData grippBackground = theme;
+ grippBackground.partId = flags & State_Horizontal ? SBP_LOWERTRACKHORZ : SBP_LOWERTRACKVERT;
+ theme.rect = gripperBounds;
+ painter->setClipRegion(d->region(theme));// Only change inside the region of the gripper
+ d->drawBackground(grippBackground);// The gutter is the grippers background
+ d->drawBackground(theme); // Transparent gripper ontop of background
+ painter->restore();
+ }
+ }
+ }
+ }
+ break;
+#ifndef QT_NO_SPINBOX
+ case CC_SpinBox:
+ if (const QStyleOptionSpinBox *sb = qstyleoption_cast<const QStyleOptionSpinBox *>(option))
+ {
+ XPThemeData theme(widget, painter, QLatin1String("SPIN"));
+ if (sb->frame && (sub & SC_SpinBoxFrame)) {
+ partId = EP_EDITBORDER_NOSCROLL;
+ if (!(flags & State_Enabled))
+ stateId = ETS_DISABLED;
+ else if (flags & State_MouseOver)
+ stateId = ETS_HOT;
+ else if (flags & State_HasFocus)
+ stateId = ETS_SELECTED;
+ else
+ stateId = ETS_NORMAL;
+
+ XPThemeData ftheme(widget, painter, QLatin1String("EDIT"), partId, stateId, r);
+ ftheme.noContent = true;
+ d->drawBackground(ftheme);
+ }
+ if (sub & SC_SpinBoxUp) {
+ theme.rect = subControlRect(CC_SpinBox, option, SC_SpinBoxUp, widget).adjusted(0, 0, 0, 1);
+ partId = SPNP_UP;
+ if (!(sb->stepEnabled & QAbstractSpinBox::StepUpEnabled) || !(flags & State_Enabled))
+ stateId = UPS_DISABLED;
+ else if (sb->activeSubControls == SC_SpinBoxUp && (sb->state & State_Sunken))
+ stateId = UPS_PRESSED;
+ else if (sb->activeSubControls == SC_SpinBoxUp && (sb->state & State_MouseOver))
+ stateId = UPS_HOT;
+ else
+ stateId = UPS_NORMAL;
+ theme.partId = partId;
+ theme.stateId = stateId;
+ d->drawBackground(theme);
+ }
+ if (sub & SC_SpinBoxDown) {
+ theme.rect = subControlRect(CC_SpinBox, option, SC_SpinBoxDown, widget);
+ partId = SPNP_DOWN;
+ if (!(sb->stepEnabled & QAbstractSpinBox::StepDownEnabled) || !(flags & State_Enabled))
+ stateId = DNS_DISABLED;
+ else if (sb->activeSubControls == SC_SpinBoxDown && (sb->state & State_Sunken))
+ stateId = DNS_PRESSED;
+ else if (sb->activeSubControls == SC_SpinBoxDown && (sb->state & State_MouseOver))
+ stateId = DNS_HOT;
+ else
+ stateId = DNS_NORMAL;
+ theme.partId = partId;
+ theme.stateId = stateId;
+ d->drawBackground(theme);
+ }
+ }
+ break;
+#endif // QT_NO_SPINBOX
+ default:
+ QWindowsXPStyle::drawComplexControl(control, option, painter, widget);
+ break;
+ }
+}
+
+/*!
+ \reimp
+ */
+QSize QWindowsVistaStyle::sizeFromContents(ContentsType type, const QStyleOption *option,
+ const QSize &size, const QWidget *widget) const
+{
+ if (!QWindowsVistaStylePrivate::useVista())
+ return QWindowsStyle::sizeFromContents(type, option, size, widget);
+
+ QSize sz(size);
+
+ QSize newSize = QWindowsXPStyle::sizeFromContents(type, option, size, widget);
+ switch (type) {
+ case CT_LineEdit:
+ case CT_ComboBox:
+ {
+ HTHEME theme = pOpenThemeData(0, L"Button");
+ MARGINS borderSize;
+ if (theme) {
+ int result = pGetThemeMargins(theme,
+ NULL,
+ BP_PUSHBUTTON,
+ PBS_NORMAL,
+ TMT_CONTENTMARGINS,
+ NULL,
+ &borderSize);
+ if (result == S_OK) {
+ sz += QSize(borderSize.cxLeftWidth + borderSize.cxRightWidth - 2,
+ borderSize.cyBottomHeight + borderSize.cyTopHeight - 2);
+ }
+ sz += QSize(23, 0); //arrow button
+ }
+ }
+ return sz;
+ case CT_MenuItem:
+ sz = QWindowsXPStyle::sizeFromContents(type, option, size, widget);
+ sz.rwidth() += 28;
+ if (const QStyleOptionMenuItem *menuitem = qstyleoption_cast<const QStyleOptionMenuItem *>(option)) {
+ int minimumHeight = qMax<qint32>(22, sz.height());
+ if (menuitem->menuItemType != QStyleOptionMenuItem::Separator)
+ sz.setHeight(minimumHeight);
+ }
+ return sz;
+#ifndef QT_NO_MENUBAR
+ case CT_MenuBarItem:
+ if (!sz.isEmpty())
+ sz += QSize(windowsItemHMargin * 5 + 1, 5);
+ return sz;
+ break;
+#endif
+ case CT_ItemViewItem:
+ sz = QWindowsXPStyle::sizeFromContents(type, option, size, widget);
+ sz.rheight() += 2;
+ return sz;
+ case CT_SpinBox:
+ {
+ //Spinbox adds frame twice
+ sz = QWindowsStyle::sizeFromContents(type, option, size, widget);
+ int border = pixelMetric(PM_SpinBoxFrameWidth, option, widget);
+ sz -= QSize(2*border, 2*border);
+ }
+ return sz;
+ default:
+ break;
+ }
+ return newSize;
+}
+
+/*!
+ \reimp
+ */
+QRect QWindowsVistaStyle::subElementRect(SubElement element, const QStyleOption *option, const QWidget *widget) const
+{
+ if (!QWindowsVistaStylePrivate::useVista())
+ return QWindowsStyle::subElementRect(element, option, widget);
+
+ QRect rect = QWindowsXPStyle::subElementRect(element, option, widget);
+ switch (element) {
+
+ case SE_PushButtonContents:
+ if (const QStyleOptionButton *btn = qstyleoption_cast<const QStyleOptionButton *>(option)) {
+ MARGINS borderSize;
+ HTHEME theme = pOpenThemeData(widget ? QWindowsVistaStylePrivate::winId(widget) : 0, L"Button");
+ if (theme) {
+ int stateId;
+ if (!(option->state & State_Enabled))
+ stateId = PBS_DISABLED;
+ else if (option->state & State_Sunken)
+ stateId = PBS_PRESSED;
+ else if (option->state & State_MouseOver)
+ stateId = PBS_HOT;
+ else if (btn->features & QStyleOptionButton::DefaultButton)
+ stateId = PBS_DEFAULTED;
+ else
+ stateId = PBS_NORMAL;
+
+ int border = pixelMetric(PM_DefaultFrameWidth, btn, widget);
+ rect = option->rect.adjusted(border, border, -border, -border);
+
+ int result = pGetThemeMargins(theme,
+ NULL,
+ BP_PUSHBUTTON,
+ stateId,
+ TMT_CONTENTMARGINS,
+ NULL,
+ &borderSize);
+
+ if (result == S_OK) {
+ rect.adjust(borderSize.cxLeftWidth, borderSize.cyTopHeight,
+ -borderSize.cxRightWidth, -borderSize.cyBottomHeight);
+ rect = visualRect(option->direction, option->rect, rect);
+ }
+ }
+ }
+ break;
+
+ case SE_HeaderArrow:
+ {
+ QRect r = rect;
+ int h = option->rect.height();
+ int w = option->rect.width();
+ int x = option->rect.x();
+ int y = option->rect.y();
+ int margin = pixelMetric(QStyle::PM_HeaderMargin, option, widget);
+
+ XPThemeData theme(widget, 0, QLatin1String("HEADER"), HP_HEADERSORTARROW, HSAS_SORTEDDOWN, option->rect);
+
+ int arrowWidth = 13;
+ int arrowHeight = 5;
+ if (theme.isValid()) {
+ SIZE size;
+ if (pGetThemePartSize(theme.handle(), 0, theme.partId, theme.stateId, 0, TS_TRUE, &size) == S_OK) {
+ arrowWidth = size.cx;
+ arrowHeight = size.cy;
+ }
+ }
+ if (option->state & State_Horizontal) {
+ r.setRect(x + w/2 - arrowWidth/2, y , arrowWidth, arrowHeight);
+ } else {
+ int vert_size = w / 2;
+ r.setRect(x + 5, y + h - margin * 2 - vert_size,
+ w - margin * 2 - 5, vert_size);
+ }
+ rect = visualRect(option->direction, option->rect, r);
+ }
+ break;
+
+ case SE_HeaderLabel:
+ {
+ int margin = pixelMetric(QStyle::PM_HeaderMargin, option, widget);
+ QRect r = option->rect;
+ r.setRect(option->rect.x() + margin, option->rect.y() + margin,
+ option->rect.width() - margin * 2, option->rect.height() - margin * 2);
+ if (const QStyleOptionHeader *header = qstyleoption_cast<const QStyleOptionHeader *>(option)) {
+ // Subtract width needed for arrow, if there is one
+ if (header->sortIndicator != QStyleOptionHeader::None) {
+ if (!(option->state & State_Horizontal)) //horizontal arrows are positioned on top
+ r.setHeight(r.height() - (option->rect.width() / 2) - (margin * 2));
+ }
+ }
+ rect = visualRect(option->direction, option->rect, r);
+ }
+ break;
+ case SE_ProgressBarContents:
+ rect = QCommonStyle::subElementRect(SE_ProgressBarGroove, option, widget);
+ break;
+ case SE_ItemViewItemDecoration:
+ if (const QStyleOptionViewItemV4 *vopt = qstyleoption_cast<const QStyleOptionViewItemV4 *>(option))
+ rect.adjust(-2, 0, 2, 0);
+ break;
+ case SE_ItemViewItemFocusRect:
+ if (const QStyleOptionViewItemV4 *vopt = qstyleoption_cast<const QStyleOptionViewItemV4 *>(option)) {
+ QRect textRect = subElementRect(QStyle::SE_ItemViewItemText, option, widget);
+ QRect displayRect = subElementRect(QStyle::SE_ItemViewItemDecoration, option, widget);
+ if (!vopt->icon.isNull())
+ rect = textRect.united(displayRect);
+ else
+ rect = textRect;
+ rect = rect.adjusted(1, 0, -1, 0);
+ }
+ break;
+ default:
+ break;
+ }
+ return rect;
+}
+
+
+/*
+ This function is used by subControlRect to check if a button
+ should be drawn for the given subControl given a set of window flags.
+*/
+static bool buttonVisible(const QStyle::SubControl sc, const QStyleOptionTitleBar *tb){
+
+ bool isMinimized = tb->titleBarState & Qt::WindowMinimized;
+ bool isMaximized = tb->titleBarState & Qt::WindowMaximized;
+ const uint flags = tb->titleBarFlags;
+ bool retVal = false;
+ switch (sc) {
+ case QStyle::SC_TitleBarContextHelpButton:
+ if (flags & Qt::WindowContextHelpButtonHint)
+ retVal = true;
+ break;
+ case QStyle::SC_TitleBarMinButton:
+ if (!isMinimized && (flags & Qt::WindowMinimizeButtonHint))
+ retVal = true;
+ break;
+ case QStyle::SC_TitleBarNormalButton:
+ if (isMinimized && (flags & Qt::WindowMinimizeButtonHint))
+ retVal = true;
+ else if (isMaximized && (flags & Qt::WindowMaximizeButtonHint))
+ retVal = true;
+ break;
+ case QStyle::SC_TitleBarMaxButton:
+ if (!isMaximized && (flags & Qt::WindowMaximizeButtonHint))
+ retVal = true;
+ break;
+ case QStyle::SC_TitleBarShadeButton:
+ if (!isMinimized && flags & Qt::WindowShadeButtonHint)
+ retVal = true;
+ break;
+ case QStyle::SC_TitleBarUnshadeButton:
+ if (isMinimized && flags & Qt::WindowShadeButtonHint)
+ retVal = true;
+ break;
+ case QStyle::SC_TitleBarCloseButton:
+ if (flags & Qt::WindowSystemMenuHint)
+ retVal = true;
+ break;
+ case QStyle::SC_TitleBarSysMenu:
+ if (flags & Qt::WindowSystemMenuHint)
+ retVal = true;
+ break;
+ default :
+ retVal = true;
+ }
+ return retVal;
+}
+
+
+/*! \reimp */
+int QWindowsVistaStyle::styleHint(StyleHint hint, const QStyleOption *option, const QWidget *widget,
+ QStyleHintReturn *returnData) const
+{
+ QWindowsVistaStylePrivate *d = const_cast<QWindowsVistaStylePrivate*>(d_func());
+ int ret = 0;
+ switch (hint) {
+ case SH_MessageBox_CenterButtons:
+ ret = false;
+ break;
+ case SH_ToolTip_Mask:
+ if (option) {
+ if (QStyleHintReturnMask *mask = qstyleoption_cast<QStyleHintReturnMask*>(returnData)) {
+ ret = true;
+ XPThemeData themeData(widget, 0, QLatin1String("TOOLTIP"), TTP_STANDARD, TTSS_NORMAL, option->rect);
+ mask->region = d->region(themeData);
+ }
+ }
+ break;
+ case SH_Table_GridLineColor:
+ if (option)
+ ret = option->palette.color(QPalette::Base).darker(118).rgb();
+ else
+ ret = -1;
+ break;
+ default:
+ ret = QWindowsXPStyle::styleHint(hint, option, widget, returnData);
+ break;
+ }
+ return ret;
+}
+
+
+/*!
+ \reimp
+ */
+QRect QWindowsVistaStyle::subControlRect(ComplexControl control, const QStyleOptionComplex *option,
+ SubControl subControl, const QWidget *widget) const
+{
+ if (!QWindowsVistaStylePrivate::useVista())
+ return QWindowsStyle::subControlRect(control, option, subControl, widget);
+
+ QRect rect = QWindowsXPStyle::subControlRect(control, option, subControl, widget);
+ switch (control) {
+#ifndef QT_NO_COMBOBOX
+ case CC_ComboBox:
+ if (const QStyleOptionComboBox *cb = qstyleoption_cast<const QStyleOptionComboBox *>(option)) {
+ int x = cb->rect.x(),
+ y = cb->rect.y(),
+ wi = cb->rect.width(),
+ he = cb->rect.height();
+ int xpos = x;
+ int margin = cb->frame ? 3 : 0;
+ int bmarg = cb->frame ? 2 : 0;
+ xpos += wi - bmarg - 16;
+
+ switch (subControl) {
+ case SC_ComboBoxFrame:
+ rect = cb->rect;
+ break;
+ case SC_ComboBoxArrow:
+ rect.setRect(cb->editable ? xpos : 0, y , wi - xpos, he);
+ break;
+ case SC_ComboBoxEditField:
+ rect.setRect(x + margin, y + margin, wi - 2 * margin - 16, he - 2 * margin);
+ break;
+ case SC_ComboBoxListBoxPopup:
+ rect = cb->rect;
+ break;
+ default:
+ break;
+ }
+ rect = visualRect(cb->direction, cb->rect, rect);
+ return rect;
+ }
+#endif // QT_NO_COMBOBOX
+ case CC_TitleBar:
+ if (const QStyleOptionTitleBar *tb = qstyleoption_cast<const QStyleOptionTitleBar *>(option)) {
+ if (!buttonVisible(subControl, tb))
+ return rect;
+ const bool isToolTitle = false;
+ const int height = tb->rect.height();
+ const int width = tb->rect.width();
+ int buttonWidth = GetSystemMetrics(SM_CXSIZE) - 4;
+
+ const int frameWidth = pixelMetric(PM_MdiSubWindowFrameWidth, option, widget);
+ const bool sysmenuHint = (tb->titleBarFlags & Qt::WindowSystemMenuHint) != 0;
+ const bool minimizeHint = (tb->titleBarFlags & Qt::WindowMinimizeButtonHint) != 0;
+ const bool maximizeHint = (tb->titleBarFlags & Qt::WindowMaximizeButtonHint) != 0;
+ const bool contextHint = (tb->titleBarFlags & Qt::WindowContextHelpButtonHint) != 0;
+ const bool shadeHint = (tb->titleBarFlags & Qt::WindowShadeButtonHint) != 0;
+
+ switch (subControl) {
+ case SC_TitleBarLabel:
+ rect = QRect(frameWidth, 0, width - (buttonWidth + frameWidth + 10), height);
+ if (isToolTitle) {
+ if (sysmenuHint) {
+ rect.adjust(0, 0, -buttonWidth - 3, 0);
+ }
+ if (minimizeHint || maximizeHint)
+ rect.adjust(0, 0, -buttonWidth - 2, 0);
+ } else {
+ if (sysmenuHint) {
+ const int leftOffset = height - 8;
+ rect.adjust(leftOffset, 0, 0, 4);
+ }
+ if (minimizeHint)
+ rect.adjust(0, 0, -buttonWidth - 2, 0);
+ if (maximizeHint)
+ rect.adjust(0, 0, -buttonWidth - 2, 0);
+ if (contextHint)
+ rect.adjust(0, 0, -buttonWidth - 2, 0);
+ if (shadeHint)
+ rect.adjust(0, 0, -buttonWidth - 2, 0);
+ }
+ rect.translate(0, 2);
+ rect = visualRect(option->direction, option->rect, rect);
+ break;
+ case SC_TitleBarSysMenu:
+ {
+ const int controlTop = 6;
+ const int controlHeight = height - controlTop - 3;
+ int iconExtent = pixelMetric(PM_SmallIconSize);
+ QSize iconSize = tb->icon.actualSize(QSize(iconExtent, iconExtent));
+ if (tb->icon.isNull())
+ iconSize = QSize(controlHeight, controlHeight);
+ int hPad = (controlHeight - iconSize.height())/2;
+ int vPad = (controlHeight - iconSize.width())/2;
+ rect = QRect(frameWidth + hPad, controlTop + vPad, iconSize.width(), iconSize.height());
+ rect.translate(0, 3);
+ rect = visualRect(option->direction, option->rect, rect);
+ }
+ break;
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ return rect;
+}
+
+/*!
+ \reimp
+ */
+bool QWindowsVistaStyle::event(QEvent *e)
+{
+ Q_D(QWindowsVistaStyle);
+ switch (e->type()) {
+ case QEvent::Timer:
+ {
+ QTimerEvent *timerEvent = (QTimerEvent *)e;
+ if (d->animationTimer.timerId() == timerEvent->timerId()) {
+ d->timerEvent();
+ e->accept();
+ return true;
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ return QWindowsXPStyle::event(e);
+}
+
+/*!
+ \reimp
+ */
+QStyle::SubControl QWindowsVistaStyle::hitTestComplexControl(ComplexControl control, const QStyleOptionComplex *option,
+ const QPoint &pos, const QWidget *widget) const
+{
+ if (!QWindowsVistaStylePrivate::useVista()) {
+ return QWindowsStyle::hitTestComplexControl(control, option, pos, widget);
+ }
+ return QWindowsXPStyle::hitTestComplexControl(control, option, pos, widget);
+}
+
+/*!
+ \reimp
+ */
+int QWindowsVistaStyle::pixelMetric(PixelMetric metric, const QStyleOption *option, const QWidget *widget) const
+{
+ if (!QWindowsVistaStylePrivate::useVista()) {
+ return QWindowsStyle::pixelMetric(metric, option, widget);
+ }
+ switch (metric) {
+
+ case PM_DockWidgetTitleBarButtonMargin:
+ return 5;
+ case PM_ScrollBarSliderMin:
+ return 18;
+ case PM_MenuHMargin:
+ case PM_MenuVMargin:
+ return 0;
+ case PM_MenuPanelWidth:
+ return 3;
+ default:
+ break;
+ }
+ return QWindowsXPStyle::pixelMetric(metric, option, widget);
+}
+
+/*!
+ \reimp
+ */
+QPalette QWindowsVistaStyle::standardPalette() const
+{
+ return QWindowsXPStyle::standardPalette();
+}
+
+/*!
+ \reimp
+ */
+void QWindowsVistaStyle::polish(QApplication *app)
+{
+ QWindowsXPStyle::polish(app);
+}
+
+/*!
+ \reimp
+ */
+void QWindowsVistaStyle::polish(QWidget *widget)
+{
+ QWindowsXPStyle::polish(widget);
+#ifndef QT_NO_LINEEDIT
+ if (qobject_cast<QLineEdit*>(widget))
+ widget->setAttribute(Qt::WA_Hover);
+ else
+#endif // QT_NO_LINEEDIT
+ if (qobject_cast<QGroupBox*>(widget))
+ widget->setAttribute(Qt::WA_Hover);
+ else if (qobject_cast<QCommandLinkButton*>(widget)) {
+ QFont buttonFont = widget->font();
+ buttonFont.setFamily(QLatin1String("Segoe UI"));
+ widget->setFont(buttonFont);
+ }
+ else if (widget->inherits("QTipLabel")){
+ //note that since tooltips are not reused
+ //we do not have to care about unpolishing
+ widget->setContentsMargins(3, 0, 4, 0);
+ COLORREF bgRef;
+ HTHEME theme = pOpenThemeData(widget ? QWindowsVistaStylePrivate::winId(widget) : 0, L"TOOLTIP");
+ if (theme) {
+ if (pGetThemeColor(theme, TTP_STANDARD, TTSS_NORMAL, TMT_TEXTCOLOR, &bgRef) == S_OK) {
+ QColor textColor = QColor::fromRgb(bgRef);
+ QPalette pal;
+ pal.setColor(QPalette::All, QPalette::ToolTipText, textColor);
+ widget->setPalette(pal);
+ }
+ }
+ } else if (qobject_cast<QMessageBox *> (widget)) {
+ widget->setAttribute(Qt::WA_StyledBackground);
+ QDialogButtonBox *buttonBox = qFindChild<QDialogButtonBox *>(widget,QLatin1String("qt_msgbox_buttonbox"));
+ if (buttonBox)
+ buttonBox->setContentsMargins(0, 9, 0, 0);
+ }
+#ifndef QT_NO_INPUTDIALOG
+ else if (qobject_cast<QInputDialog *> (widget)) {
+ widget->setAttribute(Qt::WA_StyledBackground);
+ QDialogButtonBox *buttonBox = qFindChild<QDialogButtonBox *>(widget,QLatin1String("qt_inputdlg_buttonbox"));
+ if (buttonBox)
+ buttonBox->setContentsMargins(0, 9, 0, 0);
+ }
+#endif // QT_NO_INPUTDIALOG
+ else if (QTreeView *tree = qobject_cast<QTreeView *> (widget)) {
+ tree->viewport()->setAttribute(Qt::WA_Hover);
+ }
+}
+
+/*!
+ \reimp
+ */
+void QWindowsVistaStyle::unpolish(QWidget *widget)
+{
+ QWindowsXPStyle::unpolish(widget);
+
+ QWindowsVistaStylePrivate *d = const_cast<QWindowsVistaStylePrivate*>(d_func());
+ d->stopAnimation(widget);
+
+#ifndef QT_NO_LINEEDIT
+ if (qobject_cast<QLineEdit*>(widget))
+ widget->setAttribute(Qt::WA_Hover, false);
+ else
+#endif // QT_NO_LINEEDIT
+ if (qobject_cast<QGroupBox*>(widget))
+ widget->setAttribute(Qt::WA_Hover, false);
+ else if (qobject_cast<QMessageBox *> (widget)) {
+ widget->setAttribute(Qt::WA_StyledBackground, false);
+ QDialogButtonBox *buttonBox = qFindChild<QDialogButtonBox *>(widget,QLatin1String("qt_msgbox_buttonbox"));
+ if (buttonBox)
+ buttonBox->setContentsMargins(0, 0, 0, 0);
+ }
+#ifndef QT_NO_INPUTDIALOG
+ else if (qobject_cast<QInputDialog *> (widget)) {
+ widget->setAttribute(Qt::WA_StyledBackground, false);
+ QDialogButtonBox *buttonBox = qFindChild<QDialogButtonBox *>(widget,QLatin1String("qt_inputdlg_buttonbox"));
+ if (buttonBox)
+ buttonBox->setContentsMargins(0, 0, 0, 0);
+ }
+#endif // QT_NO_INPUTDIALOG
+ else if (QTreeView *tree = qobject_cast<QTreeView *> (widget)) {
+ tree->viewport()->setAttribute(Qt::WA_Hover, false);
+ } else if (qobject_cast<QCommandLinkButton*>(widget)) {
+ QFont font = qApp->font("QCommandLinkButton");
+ QFont widgetFont = widget->font();
+ widgetFont.setFamily(font.family()); //Only family set by polish
+ widget->setFont(widgetFont);
+ }
+}
+
+
+/*!
+ \reimp
+ */
+void QWindowsVistaStyle::unpolish(QApplication *app)
+{
+ QWindowsXPStyle::unpolish(app);
+}
+
+/*!
+ \reimp
+ */
+void QWindowsVistaStyle::polish(QPalette &pal)
+{
+ QWindowsStyle::polish(pal);
+ pal.setBrush(QPalette::AlternateBase, pal.base().color().darker(104));
+}
+
+/*!
+ \reimp
+ */
+QPixmap QWindowsVistaStyle::standardPixmap(StandardPixmap standardPixmap, const QStyleOption *option,
+ const QWidget *widget) const
+{
+ if (!QWindowsVistaStylePrivate::useVista()) {
+ return QWindowsStyle::standardPixmap(standardPixmap, option, widget);
+ }
+ return QWindowsXPStyle::standardPixmap(standardPixmap, option, widget);
+}
+
+QWindowsVistaStylePrivate::QWindowsVistaStylePrivate() :
+ QWindowsXPStylePrivate(), m_treeViewHelper(0)
+{
+ resolveSymbols();
+}
+
+QWindowsVistaStylePrivate::~QWindowsVistaStylePrivate()
+{
+ delete m_treeViewHelper;
+}
+
+void QWindowsVistaStylePrivate::timerEvent()
+{
+ for (int i = animations.size() - 1 ; i >= 0 ; --i) {
+
+ if (animations[i]->widget())
+ animations[i]->widget()->update();
+
+ if (!animations[i]->widget() ||
+ !animations[i]->widget()->isEnabled() ||
+ !animations[i]->widget()->isVisible() ||
+ animations[i]->widget()->window()->isMinimized() ||
+ !animations[i]->running() ||
+ !QWindowsVistaStylePrivate::useVista())
+ {
+ Animation *a = animations.takeAt(i);
+ delete a;
+ }
+ }
+ if (animations.size() == 0 && animationTimer.isActive()) {
+ animationTimer.stop();
+ }
+}
+
+void QWindowsVistaStylePrivate::stopAnimation(const QWidget *w)
+{
+ for (int i = animations.size() - 1 ; i >= 0 ; --i) {
+ if (animations[i]->widget() == w) {
+ Animation *a = animations.takeAt(i);
+ delete a;
+ break;
+ }
+ }
+}
+
+void QWindowsVistaStylePrivate::startAnimation(Animation *t)
+{
+ Q_Q(QWindowsVistaStyle);
+ stopAnimation(t->widget());
+ animations.append(t);
+ if (animations.size() > 0 && !animationTimer.isActive()) {
+ animationTimer.start(45, q);
+ }
+}
+
+bool QWindowsVistaStylePrivate::transitionsEnabled() const
+{
+ BOOL animEnabled = false;
+ if (QT_WA_INLINE(SystemParametersInfo(SPI_GETCLIENTAREAANIMATION, 0, &animEnabled, 0),
+ SystemParametersInfoA(SPI_GETCLIENTAREAANIMATION, 0, &animEnabled, 0)
+ ))
+ {
+ if (animEnabled)
+ return true;
+ }
+ return false;
+}
+
+
+Animation * QWindowsVistaStylePrivate::widgetAnimation(const QWidget *widget) const
+{
+ if (!widget)
+ return 0;
+ foreach (Animation *a, animations) {
+ if (a->widget() == widget)
+ return a;
+ }
+ return 0;
+}
+
+
+/*! \internal
+ Returns true if all the necessary theme engine symbols were
+ resolved.
+*/
+bool QWindowsVistaStylePrivate::resolveSymbols()
+{
+ static bool tried = false;
+ if (!tried) {
+ tried = true;
+ QLibrary themeLib(QLatin1String("uxtheme"));
+ pSetWindowTheme = (PtrSetWindowTheme )themeLib.resolve("SetWindowTheme");
+ pIsThemePartDefined = (PtrIsThemePartDefined )themeLib.resolve("IsThemePartDefined");
+ pGetThemePartSize = (PtrGetThemePartSize )themeLib.resolve("GetThemePartSize");
+ pOpenThemeData = (PtrOpenThemeData )themeLib.resolve("OpenThemeData");
+ pCloseThemeData = (PtrCloseThemeData )themeLib.resolve("CloseThemeData");
+ pDrawThemeBackground = (PtrDrawThemeBackground )themeLib.resolve("DrawThemeBackground");
+ pDrawThemeBackgroundEx = (PtrDrawThemeBackgroundEx )themeLib.resolve("DrawThemeBackgroundEx");
+ pGetCurrentThemeName = (PtrGetCurrentThemeName )themeLib.resolve("GetCurrentThemeName");
+ pGetThemeBool = (PtrGetThemeBool )themeLib.resolve("GetThemeBool");
+ pGetThemeColor = (PtrGetThemeColor )themeLib.resolve("GetThemeColor");
+ pGetThemeEnumValue = (PtrGetThemeEnumValue )themeLib.resolve("GetThemeEnumValue");
+ pGetThemeFilename = (PtrGetThemeFilename )themeLib.resolve("GetThemeFilename");
+ pGetThemeFont = (PtrGetThemeFont )themeLib.resolve("GetThemeFont");
+ pGetThemeInt = (PtrGetThemeInt )themeLib.resolve("GetThemeInt");
+ pGetThemeIntList = (PtrGetThemeIntList )themeLib.resolve("GetThemeIntList");
+ pGetThemeMargins = (PtrGetThemeMargins )themeLib.resolve("GetThemeMargins");
+ pGetThemeMetric = (PtrGetThemeMetric )themeLib.resolve("GetThemeMetric");
+ pGetThemePartSize = (PtrGetThemePartSize )themeLib.resolve("GetThemePartSize");
+ pGetThemePosition = (PtrGetThemePosition )themeLib.resolve("GetThemePosition");
+ pGetThemeRect = (PtrGetThemeRect )themeLib.resolve("GetThemeRect");
+ pGetThemeString = (PtrGetThemeString )themeLib.resolve("GetThemeString");
+ pGetThemeTransitionDuration = (PtrGetThemeTransitionDuration)themeLib.resolve("GetThemeTransitionDuration");
+ pGetThemePropertyOrigin = (PtrGetThemePropertyOrigin)themeLib.resolve("GetThemePropertyOrigin");
+ }
+ return pGetThemeTransitionDuration != 0;
+}
+
+/*
+ * We need to set the windows explorer theme explicitly on a native widget
+ * in order to get Vista-style item view themes
+ */
+QWidget *QWindowsVistaStylePrivate::treeViewHelper()
+{
+ if (!m_treeViewHelper) {
+ m_treeViewHelper = new QWidget(0);
+ pSetWindowTheme(m_treeViewHelper->winId(), L"explorer", NULL);
+ }
+ return m_treeViewHelper;
+}
+
+
+/*!
+\internal
+*/
+QIcon QWindowsVistaStyle::standardIconImplementation(StandardPixmap standardIcon,
+ const QStyleOption *option,
+ const QWidget *widget) const
+{
+ if (!QWindowsVistaStylePrivate::useVista()) {
+ return QWindowsStyle::standardIconImplementation(standardIcon, option, widget);
+ }
+
+ QWindowsVistaStylePrivate *d = const_cast<QWindowsVistaStylePrivate *>(d_func());
+ switch(standardIcon) {
+ case SP_CommandLink:
+ {
+ XPThemeData theme(0, 0, QLatin1String("BUTTON"), BP_COMMANDLINKGLYPH, CMDLGS_NORMAL);
+ if (theme.isValid()) {
+ SIZE size;
+ pGetThemePartSize(theme.handle(), 0, theme.partId, theme.stateId, 0, TS_TRUE, &size);
+ QIcon linkGlyph;
+ QPixmap pm = QPixmap(size.cx, size.cy);
+ pm.fill(Qt::transparent);
+ QPainter p(&pm);
+ theme.painter = &p;
+ theme.rect = QRect(0, 0, size.cx, size.cy);
+ d->drawBackground(theme);
+ linkGlyph.addPixmap(pm, QIcon::Normal, QIcon::Off); // Normal
+ pm.fill(Qt::transparent);
+
+ theme.stateId = CMDLGS_PRESSED;
+ d->drawBackground(theme);
+ linkGlyph.addPixmap(pm, QIcon::Normal, QIcon::On); // Pressed
+ pm.fill(Qt::transparent);
+
+ theme.stateId = CMDLGS_HOT;
+ d->drawBackground(theme);
+ linkGlyph.addPixmap(pm, QIcon::Active, QIcon::Off); // Hover
+ pm.fill(Qt::transparent);
+
+ theme.stateId = CMDLGS_DISABLED;
+ d->drawBackground(theme);
+ linkGlyph.addPixmap(pm, QIcon::Disabled, QIcon::Off); // Disabled
+ return linkGlyph;
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ return QWindowsXPStyle::standardIconImplementation(standardIcon, option, widget);
+}
+
+QT_END_NAMESPACE
+
+#endif //QT_NO_WINDOWSVISTA
diff --git a/src/gui/styles/qwindowsvistastyle.h b/src/gui/styles/qwindowsvistastyle.h
new file mode 100644
index 0000000000..0d1f20201f
--- /dev/null
+++ b/src/gui/styles/qwindowsvistastyle.h
@@ -0,0 +1,108 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QWINDOWSVISTASTYLE_H
+#define QWINDOWSVISTASTYLE_H
+
+#include <QtGui/qwindowsxpstyle.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+#if !defined(QT_NO_STYLE_WINDOWSVISTA)
+
+class QWindowsVistaStylePrivate;
+class Q_GUI_EXPORT QWindowsVistaStyle : public QWindowsXPStyle
+{
+ Q_OBJECT
+public:
+ QWindowsVistaStyle();
+
+ void drawPrimitive(PrimitiveElement element, const QStyleOption *option,
+ QPainter *painter, const QWidget *widget = 0) const;
+ void drawControl(ControlElement element, const QStyleOption *option,
+ QPainter *painter, const QWidget *widget) const;
+ void drawComplexControl(ComplexControl control, const QStyleOptionComplex *option,
+ QPainter *painter, const QWidget *widget) const;
+ QSize sizeFromContents(ContentsType type, const QStyleOption *option,
+ const QSize &size, const QWidget *widget) const;
+
+ QRect subElementRect(SubElement element, const QStyleOption *option, const QWidget *widget) const;
+ QRect subControlRect(ComplexControl cc, const QStyleOptionComplex *opt,
+ SubControl sc, const QWidget *widget) const;
+
+ SubControl hitTestComplexControl(ComplexControl control, const QStyleOptionComplex *option,
+ const QPoint &pos, const QWidget *widget = 0) const;
+
+ QPixmap standardPixmap(StandardPixmap standardPixmap, const QStyleOption *opt,
+ const QWidget *widget = 0) const;
+ int pixelMetric(PixelMetric metric, const QStyleOption *option = 0, const QWidget *widget = 0) const;
+ int styleHint(StyleHint hint, const QStyleOption *opt = 0, const QWidget *widget = 0,
+ QStyleHintReturn *returnData = 0) const;
+
+
+ void polish(QWidget *widget);
+ void unpolish(QWidget *widget);
+ void polish(QPalette &pal);
+ void polish(QApplication *app);
+ void unpolish(QApplication *app);
+ bool event(QEvent *event);
+ QPalette standardPalette() const;
+
+protected Q_SLOTS:
+ QIcon standardIconImplementation(StandardPixmap standardIcon, const QStyleOption *option,
+ const QWidget *widget = 0) const;
+
+private:
+ Q_DISABLE_COPY(QWindowsVistaStyle)
+ Q_DECLARE_PRIVATE(QWindowsVistaStyle)
+ friend class QStyleFactory;
+};
+#endif //QT_NO_STYLE_WINDOWSVISTA
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif //QWINDOWSVISTASTYLE_H
diff --git a/src/gui/styles/qwindowsvistastyle_p.h b/src/gui/styles/qwindowsvistastyle_p.h
new file mode 100644
index 0000000000..877bc50469
--- /dev/null
+++ b/src/gui/styles/qwindowsvistastyle_p.h
@@ -0,0 +1,217 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QWINDOWSVISTASTYLE_P_H
+#define QWINDOWSVISTASTYLE_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of qapplication_*.cpp, qwidget*.cpp and qfiledialog.cpp. This header
+// file may change from version to version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qwindowsvistastyle.h"
+
+#if !defined(QT_NO_STYLE_WINDOWSVISTA)
+#include <private/qwindowsxpstyle_p.h>
+#include <private/qpaintengine_raster_p.h>
+#include <qlibrary.h>
+#include <qpaintengine.h>
+#include <qwidget.h>
+#include <qapplication.h>
+#include <qpixmapcache.h>
+#include <qstyleoption.h>
+#include <qpushbutton.h>
+#include <qradiobutton.h>
+#include <qcheckbox.h>
+#include <qlineedit.h>
+#include <qgroupbox.h>
+#include <qtoolbutton.h>
+#include <qspinbox.h>
+#include <qtoolbar.h>
+#include <qcombobox.h>
+#include <qscrollbar.h>
+#include <qprogressbar.h>
+#include <qdockwidget.h>
+#include <qtreeview.h>
+#include <qtextedit.h>
+#include <qmessagebox.h>
+#include <qdialogbuttonbox.h>
+#include <qinputdialog.h>
+#include <qtreeview.h>
+#include <qlistview.h>
+#include <qbasictimer.h>
+#include <qcommandlinkbutton.h>
+
+QT_BEGIN_NAMESPACE
+
+#if !defined(SCHEMA_VERIFY_VSSYM32)
+#define TMT_ANIMATIONDURATION 5006
+#define TMT_TRANSITIONDURATIONS 6000
+#define EP_EDITBORDER_NOSCROLL 6
+#define EP_EDITBORDER_HVSCROLL 9
+#define EP_BACKGROUND 3
+#define EBS_NORMAL 1
+#define EBS_HOT 2
+#define EBS_DISABLED 3
+#define EBS_READONLY 5
+#define PBS_DEFAULTED_ANIMATING 6
+#define MBI_NORMAL 1
+#define MBI_HOT 2
+#define MBI_PUSHED 3
+#define MBI_DISABLED 4
+#define MB_ACTIVE 1
+#define MB_INACTIVE 2
+#define PP_FILL 5
+#define PP_FILLVERT 6
+#define PP_MOVEOVERLAY 8
+#define PP_MOVEOVERLAYVERT 10
+#define MENU_BARBACKGROUND 7
+#define MENU_BARITEM 8
+#define MENU_POPUPCHECK 11
+#define MENU_POPUPCHECKBACKGROUND 12
+#define MENU_POPUPGUTTER 13
+#define MENU_POPUPITEM 14
+#define MENU_POPUPBORDERS 10
+#define MENU_POPUPSEPARATOR 15
+#define MC_CHECKMARKNORMAL 1
+#define MC_CHECKMARKDISABLED 2
+#define MC_BULLETNORMAL 3
+#define MC_BULLETDISABLED 4
+#define ABS_UPHOVER 17
+#define ABS_DOWNHOVER 18
+#define ABS_LEFTHOVER 19
+#define ABS_RIGHTHOVER 20
+#define CP_DROPDOWNBUTTONRIGHT 6
+#define CP_DROPDOWNBUTTONLEFT 7
+#define SCRBS_HOVER 5
+#define TVP_HOTGLYPH 4
+#define SPI_GETCLIENTAREAANIMATION 0x1042
+#define TDLG_PRIMARYPANEL 1
+#define TDLG_SECONDARYPANEL 8
+#endif
+
+class Animation
+{
+public :
+ Animation() : _running(true) { }
+ virtual ~Animation() { }
+ QWidget * widget() const { return _widget; }
+ bool running() const { return _running; }
+ const QTime &startTime() const { return _startTime; }
+ void setRunning(bool val) { _running = val; }
+ void setWidget(QWidget *widget) { _widget = widget; }
+ void setStartTime(const QTime &startTime) { _startTime = startTime; }
+ virtual void paint(QPainter *painter, const QStyleOption *option);
+
+protected:
+ void drawBlendedImage(QPainter *painter, QRect rect, float value);
+ QTime _startTime;
+ QPointer<QWidget> _widget;
+ QImage _primaryImage;
+ QImage _secondaryImage;
+ QImage _tempImage;
+ bool _running;
+};
+
+
+// Handles state transition animations
+class Transition : public Animation
+{
+public :
+ Transition() : Animation() {}
+ virtual ~Transition() { }
+ void setDuration(int duration) { _duration = duration; }
+ void setStartImage(const QImage &image) { _primaryImage = image; }
+ void setEndImage(const QImage &image) { _secondaryImage = image; }
+ virtual void paint(QPainter *painter, const QStyleOption *option);
+ int duration() const { return _duration; }
+ int _duration; //set time in ms to complete a state transition
+};
+
+
+// Handles pulse animations (default buttons)
+class Pulse: public Animation
+{
+public :
+ Pulse() : Animation() {}
+ virtual ~Pulse() { }
+ void setDuration(int duration) { _duration = duration; }
+ void setPrimaryImage(const QImage &image) { _primaryImage = image; }
+ void setAlternateImage(const QImage &image) { _secondaryImage = image; }
+ virtual void paint(QPainter *painter, const QStyleOption *option);
+ int duration() const { return _duration; }
+ int _duration; //time in ms to complete a pulse cycle
+};
+
+
+class QWindowsVistaStylePrivate : public QWindowsXPStylePrivate
+{
+ Q_DECLARE_PUBLIC(QWindowsVistaStyle)
+
+public:
+ QWindowsVistaStylePrivate();
+ ~QWindowsVistaStylePrivate();
+ static bool resolveSymbols();
+ static inline bool useVista();
+ void startAnimation(Animation *);
+ void stopAnimation(const QWidget *);
+ Animation* widgetAnimation(const QWidget *) const;
+ void timerEvent();
+ bool transitionsEnabled() const;
+ QWidget *treeViewHelper();
+
+private:
+ QList <Animation*> animations;
+ QBasicTimer animationTimer;
+ QWidget *m_treeViewHelper;
+};
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_STYLE_WINDOWSVISTA
+
+#endif // QWINDOWSVISTASTYLE_P_H
diff --git a/src/gui/styles/qwindowsxpstyle.cpp b/src/gui/styles/qwindowsxpstyle.cpp
new file mode 100644
index 0000000000..9d735a7078
--- /dev/null
+++ b/src/gui/styles/qwindowsxpstyle.cpp
@@ -0,0 +1,4205 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#include "qwindowsxpstyle.h"
+#include "qwindowsxpstyle_p.h"
+
+#if !defined(QT_NO_STYLE_WINDOWSXP) || defined(QT_PLUGIN)
+
+#include <private/qobject_p.h>
+#include <private/qpaintengine_raster_p.h>
+#include <private/qapplication_p.h>
+#include <qlibrary.h>
+#include <qpainter.h>
+#include <qpaintengine.h>
+#include <qwidget.h>
+#include <qapplication.h>
+#include <qpixmapcache.h>
+
+#include <qdesktopwidget.h>
+#include <qtoolbutton.h>
+#include <qtabbar.h>
+#include <qcombobox.h>
+#include <qscrollbar.h>
+#include <qheaderview.h>
+#include <qspinbox.h>
+#include <qlistview.h>
+#include <qstackedwidget.h>
+#include <qpushbutton.h>
+#include <qtoolbar.h>
+#include <qlabel.h>
+#include <qvarlengtharray.h>
+#include <qdebug.h>
+
+QT_BEGIN_NAMESPACE
+
+// Runtime resolved theme engine function calls
+typedef bool (WINAPI *PtrIsAppThemed)();
+typedef bool (WINAPI *PtrIsThemeActive)();
+typedef HRESULT (WINAPI *PtrGetThemePartSize)(HTHEME hTheme, HDC hdc, int iPartId, int iStateId, OPTIONAL RECT *prc, enum THEMESIZE eSize, OUT SIZE *psz);
+typedef HTHEME (WINAPI *PtrOpenThemeData)(HWND hwnd, LPCWSTR pszClassList);
+typedef HRESULT (WINAPI *PtrCloseThemeData)(HTHEME hTheme);
+typedef HRESULT (WINAPI *PtrDrawThemeBackground)(HTHEME hTheme, HDC hdc, int iPartId, int iStateId, const RECT *pRect, OPTIONAL const RECT *pClipRect);
+typedef HRESULT (WINAPI *PtrDrawThemeBackgroundEx)(HTHEME hTheme, HDC hdc, int iPartId, int iStateId, const RECT *pRect, OPTIONAL const DTBGOPTS *pOptions);
+typedef HRESULT (WINAPI *PtrGetCurrentThemeName)(OUT LPWSTR pszThemeFileName, int cchMaxNameChars, OUT OPTIONAL LPWSTR pszColorBuff, int cchMaxColorChars, OUT OPTIONAL LPWSTR pszSizeBuff, int cchMaxSizeChars);
+typedef HRESULT (WINAPI *PtrGetThemeDocumentationProperty)(LPCWSTR pszThemeName, LPCWSTR pszPropertyName, OUT LPWSTR pszValueBuff, int cchMaxValChars);
+typedef HRESULT (WINAPI *PtrGetThemeBool)(HTHEME hTheme, int iPartId, int iStateId, int iPropId, OUT BOOL *pfVal);
+typedef HRESULT (WINAPI *PtrGetThemeColor)(HTHEME hTheme, int iPartId, int iStateId, int iPropId, OUT COLORREF *pColor);
+typedef HRESULT (WINAPI *PtrGetThemeEnumValue)(HTHEME hTheme, int iPartId, int iStateId, int iPropId, OUT int *piVal);
+typedef HRESULT (WINAPI *PtrGetThemeFilename)(HTHEME hTheme, int iPartId, int iStateId, int iPropId, OUT LPWSTR pszThemeFileName, int cchMaxBuffChars);
+typedef HRESULT (WINAPI *PtrGetThemeFont)(HTHEME hTheme, OPTIONAL HDC hdc, int iPartId, int iStateId, int iPropId, OUT LOGFONT *pFont);
+typedef HRESULT (WINAPI *PtrGetThemeInt)(HTHEME hTheme, int iPartId, int iStateId, int iPropId, OUT int *piVal);
+typedef HRESULT (WINAPI *PtrGetThemeIntList)(HTHEME hTheme, int iPartId, int iStateId, int iPropId, OUT INTLIST *pIntList);
+typedef HRESULT (WINAPI *PtrGetThemeMargins)(HTHEME hTheme, OPTIONAL HDC hdc, int iPartId, int iStateId, int iPropId, OPTIONAL RECT *prc, OUT MARGINS *pMargins);
+typedef HRESULT (WINAPI *PtrGetThemeMetric)(HTHEME hTheme, OPTIONAL HDC hdc, int iPartId, int iStateId, int iPropId, OUT int *piVal);
+typedef HRESULT (WINAPI *PtrGetThemePartSize)(HTHEME hTheme, HDC hdc, int iPartId, int iStateId, OPTIONAL RECT *prc, enum THEMESIZE eSize, OUT SIZE *psz);
+typedef HRESULT (WINAPI *PtrGetThemePosition)(HTHEME hTheme, int iPartId, int iStateId, int iPropId, OUT POINT *pPoint);
+typedef HRESULT (WINAPI *PtrGetThemePropertyOrigin)(HTHEME hTheme, int iPartId, int iStateId, int iPropId, OUT enum PROPERTYORIGIN *pOrigin);
+typedef HRESULT (WINAPI *PtrGetThemeRect)(HTHEME hTheme, int iPartId, int iStateId, int iPropId, OUT RECT *pRect);
+typedef HRESULT (WINAPI *PtrGetThemeString)(HTHEME hTheme, int iPartId, int iStateId, int iPropId, OUT LPWSTR pszBuff, int cchMaxBuffChars);
+typedef HRESULT (WINAPI *PtrGetThemeBackgroundRegion)(HTHEME hTheme, OPTIONAL HDC hdc, int iPartId, int iStateId, const RECT *pRect, OUT HRGN *pRegion);
+typedef BOOL (WINAPI *PtrIsThemeBackgroundPartiallyTransparent)(HTHEME hTheme, int iPartId, int iStateId);
+
+static PtrIsAppThemed pIsAppThemed = 0;
+static PtrIsThemeActive pIsThemeActive = 0;
+static PtrOpenThemeData pOpenThemeData = 0;
+static PtrCloseThemeData pCloseThemeData = 0;
+static PtrDrawThemeBackground pDrawThemeBackground = 0;
+static PtrDrawThemeBackgroundEx pDrawThemeBackgroundEx = 0;
+static PtrGetCurrentThemeName pGetCurrentThemeName = 0;
+static PtrGetThemeBool pGetThemeBool = 0;
+static PtrGetThemeColor pGetThemeColor = 0;
+static PtrGetThemeEnumValue pGetThemeEnumValue = 0;
+static PtrGetThemeFilename pGetThemeFilename = 0;
+static PtrGetThemeFont pGetThemeFont = 0;
+static PtrGetThemeInt pGetThemeInt = 0;
+static PtrGetThemeIntList pGetThemeIntList = 0;
+static PtrGetThemeMargins pGetThemeMargins = 0;
+static PtrGetThemeMetric pGetThemeMetric = 0;
+static PtrGetThemePartSize pGetThemePartSize = 0;
+static PtrGetThemePosition pGetThemePosition = 0;
+static PtrGetThemePropertyOrigin pGetThemePropertyOrigin = 0;
+static PtrGetThemeRect pGetThemeRect = 0;
+static PtrGetThemeString pGetThemeString = 0;
+static PtrGetThemeBackgroundRegion pGetThemeBackgroundRegion = 0;
+static PtrGetThemeDocumentationProperty pGetThemeDocumentationProperty = 0;
+static PtrIsThemeBackgroundPartiallyTransparent pIsThemeBackgroundPartiallyTransparent = 0;
+
+// General const values
+static const int windowsItemFrame = 2; // menu item frame width
+static const int windowsSepHeight = 9; // separator item height
+static const int windowsItemHMargin = 3; // menu item hor text margin
+static const int windowsItemVMargin = 0; // menu item ver text margin
+static const int windowsArrowHMargin = 6; // arrow horizontal margin
+static const int windowsCheckMarkHMargin = 0; // horiz. margins of check mark
+static const int windowsRightBorder = 12; // right border on windows
+
+// External function calls
+extern Q_GUI_EXPORT HDC qt_win_display_dc();
+
+
+
+// Theme data helper ------------------------------------------------------------------------------
+/* \internal
+ Returns true if the themedata is valid for use.
+*/
+bool XPThemeData::isValid()
+{
+ return QWindowsXPStylePrivate::useXP() && name.size() && handle();
+}
+
+
+/* \internal
+ Returns the theme engine handle to the specific class.
+ If the handle hasn't been opened before, it opens the data, and
+ adds it to a static map, for caching.
+*/
+HTHEME XPThemeData::handle()
+{
+ if (!QWindowsXPStylePrivate::useXP())
+ return 0;
+
+ if (!htheme && QWindowsXPStylePrivate::handleMap)
+ htheme = QWindowsXPStylePrivate::handleMap->operator[](name);
+
+ if (!htheme) {
+ htheme = pOpenThemeData(QWindowsXPStylePrivate::winId(widget),
+ (TCHAR*)name.utf16());
+ if (htheme) {
+ if (!QWindowsXPStylePrivate::handleMap)
+ QWindowsXPStylePrivate::handleMap = new QMap<QString, HTHEME>;
+ QWindowsXPStylePrivate::handleMap->operator[](name) = htheme;
+ }
+ }
+
+ return htheme;
+}
+
+/* \internal
+ Converts a QRect to the native RECT structure.
+*/
+RECT XPThemeData::toRECT(const QRect &qr)
+{
+ RECT r;
+ r.left = qr.x();
+ r.right = qr.x() + qr.width();
+ r.top = qr.y();
+ r.bottom = qr.y() + qr.height();
+ return r;
+}
+
+/* \internal
+ Returns the native region of a part, if the part is considered
+ transparent. The region is scaled to the parts size (rect).
+*/
+HRGN XPThemeData::mask()
+{
+ if (!pIsThemeBackgroundPartiallyTransparent(handle(), partId, stateId))
+ return 0;
+
+ HRGN hrgn;
+ HDC dc = painter == 0 ? 0 : painter->paintEngine()->getDC();
+ RECT nativeRect = toRECT(rect);
+ pGetThemeBackgroundRegion(handle(), dc, partId, stateId, &nativeRect, &hrgn);
+ if (dc)
+ painter->paintEngine()->releaseDC(dc);
+ return hrgn;
+}
+
+// QWindowsXPStylePrivate -------------------------------------------------------------------------
+// Static initializations
+QWidget *QWindowsXPStylePrivate::limboWidget = 0;
+QPixmap *QWindowsXPStylePrivate::tabbody = 0;
+QMap<QString,HTHEME> *QWindowsXPStylePrivate::handleMap = 0;
+bool QWindowsXPStylePrivate::use_xp = false;
+QBasicAtomicInt QWindowsXPStylePrivate::ref = Q_BASIC_ATOMIC_INITIALIZER(-1); // -1 based refcounting
+
+/* \internal
+ Checks if the theme engine can/should be used, or if we should
+ fall back to Windows style.
+*/
+bool QWindowsXPStylePrivate::useXP(bool update)
+{
+ if (!update)
+ return use_xp;
+ return (use_xp = resolveSymbols() && pIsThemeActive()
+ && (pIsAppThemed() || !QApplication::instance()));
+}
+
+/* \internal
+ Handles refcounting, and queries the theme engine for usage.
+*/
+void QWindowsXPStylePrivate::init(bool force)
+{
+ if (ref.ref() && !force)
+ return;
+ if (!force) // -1 based atomic refcounting
+ ref.ref();
+
+ useXP(true);
+}
+
+/* \internal
+ Cleans up all static data.
+*/
+void QWindowsXPStylePrivate::cleanup(bool force)
+{
+ if(bufferBitmap) {
+ if (bufferDC && nullBitmap)
+ SelectObject(bufferDC, nullBitmap);
+ DeleteObject(bufferBitmap);
+ bufferBitmap = 0;
+ }
+
+ if(bufferDC)
+ DeleteDC(bufferDC);
+ bufferDC = 0;
+
+ if (ref.deref() && !force)
+ return;
+ if (!force) // -1 based atomic refcounting
+ ref.deref();
+
+ use_xp = false;
+ cleanupHandleMap();
+ if (limboWidget) {
+ if (qApp->closingDown())
+ delete limboWidget;
+ else
+ limboWidget->deleteLater();
+ }
+ delete tabbody;
+ limboWidget = 0;
+ tabbody = 0;
+}
+
+/* \internal
+ Closes all open theme data handles to ensure that we don't leak
+ resources, and that we don't refere to old handles when for
+ example the user changes the theme style.
+*/
+void QWindowsXPStylePrivate::cleanupHandleMap()
+{
+ if (!handleMap)
+ return;
+
+ QMap<QString, HTHEME>::Iterator it;
+ for (it = handleMap->begin(); it != handleMap->end(); ++it)
+ pCloseThemeData(it.value());
+ delete handleMap;
+ handleMap = 0;
+}
+
+/*! \internal
+ This function will always return a valid window handle, and might
+ create a limbo widget to do so.
+ We often need a window handle to for example open theme data, so
+ this function ensures that we get one.
+*/
+HWND QWindowsXPStylePrivate::winId(const QWidget *widget)
+{
+ if (widget && widget->internalWinId())
+ return widget->internalWinId();
+
+ if (!limboWidget) {
+ limboWidget = new QWidget(0);
+ limboWidget->setObjectName(QLatin1String("xp_limbo_widget"));
+ }
+
+ return limboWidget->winId();
+}
+
+/*! \internal
+ Returns the pointer to a tab widgets body pixmap, scaled to the
+ height of the screen. This way the theme engine doesn't need to
+ scale the body for every time we ask for it. (Speed optimization)
+*/
+const QPixmap *QWindowsXPStylePrivate::tabBody(QWidget *)
+{
+ if (!tabbody) {
+ SIZE sz;
+ XPThemeData theme(0, 0, QLatin1String("TAB"), TABP_BODY);
+ pGetThemePartSize(theme.handle(), qt_win_display_dc(), TABP_BODY, 0, 0, TS_TRUE, &sz);
+
+ tabbody = new QPixmap(sz.cx, QApplication::desktop()->screenGeometry().height());
+ QPainter painter(tabbody);
+ theme.rect = QRect(0, 0, sz.cx, sz.cy);
+ drawBackground(theme);
+ // We fill with the last line of the themedata, that
+ // way we don't get a tiled pixmap inside big tabs
+ QPixmap temp(sz.cx, 1);
+ painter.drawPixmap(0, 0, temp, 0, sz.cy-1, -1, -1);
+ painter.drawTiledPixmap(0, sz.cy, sz.cx, tabbody->height()-sz.cy, temp);
+ }
+ return tabbody;
+}
+
+/*! \internal
+ Returns true if all the necessary theme engine symbols were
+ resolved.
+*/
+bool QWindowsXPStylePrivate::resolveSymbols()
+{
+ static bool tried = false;
+ if (!tried) {
+ tried = true;
+ QLibrary themeLib(QLatin1String("uxtheme"));
+ pIsAppThemed = (PtrIsAppThemed)themeLib.resolve("IsAppThemed");
+ if (pIsAppThemed) {
+ pIsThemeActive = (PtrIsThemeActive )themeLib.resolve("IsThemeActive");
+ pGetThemePartSize = (PtrGetThemePartSize )themeLib.resolve("GetThemePartSize");
+ pOpenThemeData = (PtrOpenThemeData )themeLib.resolve("OpenThemeData");
+ pCloseThemeData = (PtrCloseThemeData )themeLib.resolve("CloseThemeData");
+ pDrawThemeBackground = (PtrDrawThemeBackground )themeLib.resolve("DrawThemeBackground");
+ pDrawThemeBackgroundEx = (PtrDrawThemeBackgroundEx )themeLib.resolve("DrawThemeBackgroundEx");
+ pGetCurrentThemeName = (PtrGetCurrentThemeName )themeLib.resolve("GetCurrentThemeName");
+ pGetThemeBool = (PtrGetThemeBool )themeLib.resolve("GetThemeBool");
+ pGetThemeColor = (PtrGetThemeColor )themeLib.resolve("GetThemeColor");
+ pGetThemeEnumValue = (PtrGetThemeEnumValue )themeLib.resolve("GetThemeEnumValue");
+ pGetThemeFilename = (PtrGetThemeFilename )themeLib.resolve("GetThemeFilename");
+ pGetThemeFont = (PtrGetThemeFont )themeLib.resolve("GetThemeFont");
+ pGetThemeInt = (PtrGetThemeInt )themeLib.resolve("GetThemeInt");
+ pGetThemeIntList = (PtrGetThemeIntList )themeLib.resolve("GetThemeIntList");
+ pGetThemeMargins = (PtrGetThemeMargins )themeLib.resolve("GetThemeMargins");
+ pGetThemeMetric = (PtrGetThemeMetric )themeLib.resolve("GetThemeMetric");
+ pGetThemePartSize = (PtrGetThemePartSize )themeLib.resolve("GetThemePartSize");
+ pGetThemePosition = (PtrGetThemePosition )themeLib.resolve("GetThemePosition");
+ pGetThemePropertyOrigin = (PtrGetThemePropertyOrigin)themeLib.resolve("GetThemePropertyOrigin");
+ pGetThemeRect = (PtrGetThemeRect )themeLib.resolve("GetThemeRect");
+ pGetThemeString = (PtrGetThemeString )themeLib.resolve("GetThemeString");
+ pGetThemeBackgroundRegion = (PtrGetThemeBackgroundRegion )themeLib.resolve("GetThemeBackgroundRegion");
+ pGetThemeDocumentationProperty = (PtrGetThemeDocumentationProperty )themeLib.resolve("GetThemeDocumentationProperty");
+ pIsThemeBackgroundPartiallyTransparent = (PtrIsThemeBackgroundPartiallyTransparent)themeLib.resolve("IsThemeBackgroundPartiallyTransparent");
+ }
+ }
+
+ return pIsAppThemed != 0;
+}
+
+/*! \internal
+ Returns a native buffer (DIB section) of at least the size of
+ ( \a x , \a y ). The buffer has a 32 bit depth, to not lose
+ the alpha values on proper alpha-pixmaps.
+*/
+HBITMAP QWindowsXPStylePrivate::buffer(int w, int h)
+{
+ // If we already have a HBITMAP which is of adequate size, just return that
+ if (bufferBitmap) {
+ if (bufferW >= w && bufferH >= h)
+ return bufferBitmap;
+ // Not big enough, discard the old one
+ if (bufferDC && nullBitmap)
+ SelectObject(bufferDC, nullBitmap);
+ DeleteObject(bufferBitmap);
+ bufferBitmap = 0;
+ }
+
+ w = qMax(bufferW, w);
+ h = qMax(bufferH, h);
+
+ if (!bufferDC)
+ bufferDC = CreateCompatibleDC(qt_win_display_dc());
+
+ // Define the header
+ BITMAPINFO bmi;
+ memset(&bmi, 0, sizeof(bmi));
+ bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
+ bmi.bmiHeader.biWidth = w;
+ bmi.bmiHeader.biHeight = -h;
+ bmi.bmiHeader.biPlanes = 1;
+ bmi.bmiHeader.biBitCount = 32;
+ bmi.bmiHeader.biCompression = BI_RGB;
+
+ // Create the pixmap
+ bufferPixels = 0;
+ bufferBitmap = CreateDIBSection(bufferDC, &bmi, DIB_RGB_COLORS, (void **) &bufferPixels, 0, 0);
+ GdiFlush();
+ nullBitmap = (HBITMAP)SelectObject(bufferDC, bufferBitmap);
+
+ if (!bufferBitmap) {
+ qErrnoWarning("QWindowsXPStylePrivate::buffer(w,h), failed to create dibsection");
+ bufferW = 0;
+ bufferH = 0;
+ return 0;
+ }
+ if (!bufferPixels) {
+ qErrnoWarning("QWindowsXPStylePrivate::buffer(w,h), did not allocate pixel data");
+ bufferW = 0;
+ bufferH = 0;
+ return 0;
+ }
+ bufferW = w;
+ bufferH = h;
+#ifdef DEBUG_XP_STYLE
+ qDebug("Creating new dib section (%d, %d)", w, h);
+#endif
+ return bufferBitmap;
+}
+
+/*! \internal
+ Returns true if the part contains any transparency at all. This does
+ not indicate what kind of transparency we're dealing with. It can be
+ - Alpha transparency
+ - Masked transparency
+*/
+bool QWindowsXPStylePrivate::isTransparent(XPThemeData &themeData)
+{
+ return pIsThemeBackgroundPartiallyTransparent(themeData.handle(), themeData.partId,
+ themeData.stateId);
+}
+
+/*! \internal
+ Returns a QRegion of the region of the part
+*/
+QRegion QWindowsXPStylePrivate::region(XPThemeData &themeData)
+{
+ HRGN hRgn = 0;
+ RECT rect = themeData.toRECT(themeData.rect);
+ if (!SUCCEEDED(pGetThemeBackgroundRegion(themeData.handle(), bufferHDC(), themeData.partId,
+ themeData.stateId, &rect, &hRgn)))
+ return QRegion();
+
+ QRegion rgn = QRegion(0,0,1,1);
+ const bool success = CombineRgn(rgn.handle(), hRgn, 0, RGN_COPY) != ERROR;
+ DeleteObject(hRgn);
+ if (success)
+ return rgn;
+ return QRegion();
+}
+
+/*! \internal
+ Sets the parts region on a window.
+*/
+void QWindowsXPStylePrivate::setTransparency(QWidget *widget, XPThemeData &themeData)
+{
+ HRGN hrgn = themeData.mask();
+ if (hrgn && widget)
+ SetWindowRgn(winId(widget), hrgn, true);
+}
+
+/*! \internal
+ Returns true if the native doublebuffer contains a pixel which
+ has a non-0xFF alpha value. Should only be use when its
+ guaranteed that data painted into the buffer wasn't a proper
+ alpha pixmap.
+*/
+bool QWindowsXPStylePrivate::hasAnyData(const QRect &rect)
+{
+ const int startX = rect.left();
+ const int startY = rect.top();
+ const int w = rect.width();
+ const int h = rect.height();
+
+ for (int y = startY; y < h; ++y) {
+ register DWORD *buffer = (DWORD*)bufferPixels + (y * bufferW);
+ for (int x = startX; x < w; ++x, ++buffer) {
+ int alpha = (*buffer) >> 24;
+ if (alpha != 0xFF) // buffer has been touched
+ return true;
+ }
+ }
+ return false;
+}
+
+/*! \internal
+ Returns true if the native doublebuffer contains pixels with
+ varying alpha value.
+*/
+bool QWindowsXPStylePrivate::hasAlphaChannel(const QRect &rect)
+{
+ const int startX = rect.left();
+ const int startY = rect.top();
+ const int w = rect.width();
+ const int h = rect.height();
+
+ int firstAlpha = -1;
+ for (int y = startY; y < h/2; ++y) {
+ register DWORD *buffer = (DWORD*)bufferPixels + (y * bufferW);
+ for (int x = startX; x < w; ++x, ++buffer) {
+ int alpha = (*buffer) >> 24;
+ if (firstAlpha == -1)
+ firstAlpha = alpha;
+ else if (alpha != firstAlpha)
+ return true;
+ }
+ }
+ return false;
+}
+
+/*! \internal
+ When the theme engine paints both a true alpha pixmap and a glyph
+ into our buffer, the glyph might not contain a proper alpha value.
+ The rule of thumb for premultiplied pixmaps is that the color
+ values of a pixel can never be higher than the alpha values, so
+ we use this to our advantage here, and fix all instances where
+ this occures.
+*/
+bool QWindowsXPStylePrivate::fixAlphaChannel(const QRect &rect)
+{
+ const int startX = rect.left();
+ const int startY = rect.top();
+ const int w = rect.width();
+ const int h = rect.height();
+ bool hasFixedAlphaValue = false;
+
+ for (int y = startY; y < h; ++y) {
+ register DWORD *buffer = (DWORD*)bufferPixels + (y * bufferW);
+ for (register int x = startX; x < w; ++x, ++buffer) {
+ uint pixel = *buffer;
+ int alpha = qAlpha(pixel);
+ if (qRed(pixel) > alpha || qGreen(pixel) > alpha || qBlue(pixel) > alpha) {
+ *buffer |= 0xff000000;
+ hasFixedAlphaValue = true;
+ }
+ }
+ }
+ return hasFixedAlphaValue;
+}
+
+/*! \internal
+ Swaps the alpha values on certain pixels:
+ 0xFF?????? -> 0x00??????
+ 0x00?????? -> 0xFF??????
+ Used to determin the mask of a non-alpha transparent pixmap in
+ the native doublebuffer, and swap the alphas so we may paint
+ the image as a Premultiplied QImage with drawImage(), and obtain
+ the mask transparency.
+*/
+bool QWindowsXPStylePrivate::swapAlphaChannel(const QRect &rect, bool allPixels)
+{
+ const int startX = rect.left();
+ const int startY = rect.top();
+ const int w = rect.width();
+ const int h = rect.height();
+ bool valueChange = false;
+
+ // Flip the alphas, so that 255-alpha pixels are 0, and 0-alpha are 255.
+ for (int y = startY; y < h; ++y) {
+ register DWORD *buffer = (DWORD*)bufferPixels + (y * bufferW);
+ for (register int x = startX; x < w; ++x, ++buffer) {
+ if (allPixels) {
+ *buffer |= 0xFF000000;
+ continue;
+ }
+ register unsigned int alphaValue = (*buffer) & 0xFF000000;
+ if (alphaValue == 0xFF000000) {
+ *buffer &= 0x00FFFFFF;
+ valueChange = true;
+ } else if (alphaValue == 0) {
+ *buffer |= 0xFF000000;
+ valueChange = true;
+ }
+ }
+ }
+ return valueChange;
+}
+
+/*! \internal
+ Main theme drawing function.
+ Determines the correct lowlevel drawing method depending on several
+ factors.
+ Use drawBackgroundThruNativeBuffer() if:
+ - Painter does not have an HDC
+ - Theme part is flipped (mirrored horizontally)
+ else use drawBackgroundDirectly().
+*/
+void QWindowsXPStylePrivate::drawBackground(XPThemeData &themeData)
+{
+ if (themeData.rect.isEmpty())
+ return;
+
+ QPainter *painter = themeData.painter;
+ Q_ASSERT_X(painter != 0, "QWindowsXPStylePrivate::drawBackground()", "Trying to draw a theme part without a painter");
+ if (!painter)
+ return;
+
+ painter->save();
+
+ QMatrix m = painter->matrix();
+ bool complexXForm = m.m11() != 1.0 || m.m22() != 1.0 || m.m12() != 0.0 || m.m21() != 0.0;
+
+ bool useFallback = painter->paintEngine()->getDC() == 0
+ || painter->opacity() != 1.0
+ || themeData.rotate
+ || complexXForm
+ || themeData.mirrorVertically
+ || (themeData.mirrorHorizontally && pDrawThemeBackgroundEx == 0);
+ if (!useFallback)
+ drawBackgroundDirectly(themeData);
+ else
+ drawBackgroundThruNativeBuffer(themeData);
+
+ painter->restore();
+}
+
+/*! \internal
+ This function draws the theme parts directly to the paintengines HDC.
+ Do not use this if you need to perform other transformations on the
+ resulting data.
+*/
+void QWindowsXPStylePrivate::drawBackgroundDirectly(XPThemeData &themeData)
+{
+ QPainter *painter = themeData.painter;
+ HDC dc = painter->paintEngine()->getDC();
+
+ QPoint redirectionDelta(int(painter->deviceMatrix().dx()),
+ int(painter->deviceMatrix().dy()));
+ QRect area = themeData.rect.translated(redirectionDelta);
+
+ QRegion sysRgn = painter->paintEngine()->systemClip();
+ if (sysRgn.isEmpty())
+ sysRgn = area;
+ else
+ sysRgn &= area;
+ if (painter->hasClipping())
+ sysRgn &= painter->clipRegion().translated(redirectionDelta);
+ SelectClipRgn(dc, sysRgn.handle());
+
+#ifdef DEBUG_XP_STYLE
+ printf("---[ DIRECT PAINTING ]------------------> Name(%-10s) Part(%d) State(%d)\n",
+ qPrintable(themeData.name), themeData.partId, themeData.stateId);
+ showProperties(themeData);
+#endif
+
+ RECT drawRECT = themeData.toRECT(area);
+ DTBGOPTS drawOptions;
+ drawOptions.dwSize = sizeof(drawOptions);
+ drawOptions.rcClip = themeData.toRECT(sysRgn.boundingRect());
+ drawOptions.dwFlags = DTBG_CLIPRECT
+ | (themeData.noBorder ? DTBG_OMITBORDER : 0)
+ | (themeData.noContent ? DTBG_OMITCONTENT : 0)
+ | (themeData.mirrorHorizontally ? DTBG_MIRRORDC : 0);
+
+ if (pDrawThemeBackgroundEx != 0) {
+ pDrawThemeBackgroundEx(themeData.handle(), dc, themeData.partId, themeData.stateId, &(drawRECT), &drawOptions);
+ } else {
+ // We are running on a system where the uxtheme.dll does not have
+ // the DrawThemeBackgroundEx function, so we need to clip away
+ // borders or contents manually. All flips and mirrors uses the
+ // fallback implementation
+
+ int borderSize = 0;
+ PROPERTYORIGIN origin = PO_NOTFOUND;
+ pGetThemePropertyOrigin(themeData.handle(), themeData.partId, themeData.stateId, TMT_BORDERSIZE, &origin);
+ pGetThemeInt(themeData.handle(), themeData.partId, themeData.stateId, TMT_BORDERSIZE, &borderSize);
+
+ // Clip away border region
+ QRegion extraClip = sysRgn;
+ if ((origin == PO_CLASS || origin == PO_PART || origin == PO_STATE) && borderSize > 0) {
+ if (themeData.noBorder) {
+ // extraClip &= area is already done
+ drawRECT = themeData.toRECT(area.adjusted(-borderSize, -borderSize, borderSize, borderSize));
+ }
+
+ // Clip away content region
+ if (themeData.noContent) {
+ QRegion content = area.adjusted(borderSize, borderSize, -borderSize, -borderSize);
+ extraClip ^= content;
+ }
+
+ // Set the clip region, if used..
+ if (themeData.noBorder || themeData.noContent)
+ SelectClipRgn(dc, extraClip.handle());
+ }
+
+ pDrawThemeBackground(themeData.handle(), dc, themeData.partId, themeData.stateId, &(drawRECT), &(drawOptions.rcClip));
+ }
+ SelectClipRgn(dc, 0);
+}
+
+/*! \internal
+ This function uses a secondary Native doublebuffer for painting parts.
+ It should only be used when the painteengine doesn't provide a proper
+ HDC for direct painting (e.g. when doing a grabWidget(), painting to
+ other pixmaps etc), or when special transformations are needed (e.g.
+ flips (horizonal mirroring only, vertical are handled by the theme
+ engine).
+*/
+void QWindowsXPStylePrivate::drawBackgroundThruNativeBuffer(XPThemeData &themeData)
+{
+ QPainter *painter = themeData.painter;
+ QRect rect = themeData.rect;
+
+ if ((themeData.rotate + 90) % 180 == 0) { // Catch 90,270,etc.. degree flips.
+ rect = QRect(0, 0, rect.height(), rect.width());
+ }
+ rect.moveTo(0,0);
+ int partId = themeData.partId;
+ int stateId = themeData.stateId;
+ int w = rect.width();
+ int h = rect.height();
+
+ // Values initialized later, either from cached values, or from function calls
+ AlphaChannelType alphaType = UnknownAlpha;
+ bool stateHasData = true; // We assume so;
+ bool hasAlpha = false;
+ bool partIsTransparent;
+ bool inspectData;
+ bool potentialInvalidAlpha;
+
+ QString pixmapCacheKey = QString::fromLatin1("$qt_xp_%1p%2s%3s%4b%5c%6w%7h").arg(themeData.name)
+ .arg(partId).arg(stateId).arg(!themeData.noBorder).arg(!themeData.noContent)
+ .arg(w).arg(h);
+ QPixmap cachedPixmap;
+ ThemeMapKey key(themeData);
+ ThemeMapData data = alphaCache.value(key);
+
+ bool haveCachedPixmap = false;
+ bool isCached = data.dataValid;
+ if (isCached) {
+ if (!(stateHasData = data.hasAnyData))
+ return; // Cached NOOP
+ inspectData = data.wasAlphaSwapped;
+ partIsTransparent = data.partIsTransparent;
+ hasAlpha = data.hasAlphaChannel;
+ alphaType = data.alphaType;
+ potentialInvalidAlpha = data.hadInvalidAlpha;
+
+ haveCachedPixmap = QPixmapCache::find(pixmapCacheKey, cachedPixmap);
+
+#ifdef DEBUG_XP_STYLE
+ char buf[25];
+ ::sprintf(buf, "+ Pixmap(%3d, %3d) ]", w, h);
+ printf("---[ CACHED %s--------> Name(%-10s) Part(%d) State(%d)\n",
+ haveCachedPixmap ? buf : "]-------------------",
+ qPrintable(themeData.name), themeData.partId, themeData.stateId);
+#endif
+ } else {
+ // Not cached, so get values from Theme Engine
+ BOOL tmt_borderonly = false;
+ COLORREF tmt_transparentcolor = 0x0;
+ PROPERTYORIGIN proporigin = PO_NOTFOUND;
+ pGetThemeBool(themeData.handle(), themeData.partId, themeData.stateId, TMT_BORDERONLY, &tmt_borderonly);
+ pGetThemeColor(themeData.handle(), themeData.partId, themeData.stateId, TMT_TRANSPARENTCOLOR, &tmt_transparentcolor);
+ pGetThemePropertyOrigin(themeData.handle(), themeData.partId, themeData.stateId, TMT_CAPTIONMARGINS, &proporigin);
+ inspectData = (tmt_transparentcolor != 0 || tmt_borderonly || proporigin == PO_PART || proporigin == PO_STATE);
+
+ // ### This is a vista-specific workaround for broken alpha in titlebar pixmaps
+ if ((QSysInfo::WindowsVersion >= QSysInfo::WV_VISTA && QSysInfo::WindowsVersion < QSysInfo::WV_NT_based)) {
+ if (themeData.partId == WP_CAPTION || themeData.partId == WP_SMALLCAPTION)
+ inspectData = false;
+ }
+
+ partIsTransparent = isTransparent(themeData);
+
+ potentialInvalidAlpha = false;
+ pGetThemePropertyOrigin(themeData.handle(), themeData.partId, themeData.stateId, TMT_GLYPHTYPE, &proporigin);
+ if (proporigin == PO_PART || proporigin == PO_STATE) {
+ int tmt_glyphtype = GT_NONE;
+ pGetThemeEnumValue(themeData.handle(), themeData.partId, themeData.stateId, TMT_GLYPHTYPE, &tmt_glyphtype);
+ potentialInvalidAlpha = partIsTransparent && !inspectData && tmt_glyphtype == GT_IMAGEGLYPH;
+ }
+
+#ifdef DEBUG_XP_STYLE
+ printf("---[ NOT CACHED ]-----------------------> Name(%-10s) Part(%d) State(%d)\n",
+ qPrintable(themeData.name), themeData.partId, themeData.stateId);
+ printf("-->partIsTransparen = %d\n", partIsTransparent);
+ printf("-->inspectData = %d\n", inspectData);
+ printf("-->potentialInvalidAlpha = %d\n", potentialInvalidAlpha);
+ showProperties(themeData);
+#endif
+ }
+ bool wasAlphaSwapped = false;
+ bool wasAlphaFixed = false;
+
+ // OLD PSDK Workaround ------------------------------------------------------------------------
+ // See if we need extra clipping for the older PSDK, which does
+ // not have a DrawThemeBackgroundEx function for DTGB_OMITBORDER
+ // and DTGB_OMITCONTENT
+ bool addBorderContentClipping = false;
+ QRegion extraClip;
+ QRect area = rect;
+ if (themeData.noBorder || themeData.noContent) {
+ extraClip = area;
+ // We are running on a system where the uxtheme.dll does not have
+ // the DrawThemeBackgroundEx function, so we need to clip away
+ // borders or contents manually.
+
+ int borderSize = 0;
+ PROPERTYORIGIN origin = PO_NOTFOUND;
+ pGetThemePropertyOrigin(themeData.handle(), themeData.partId, themeData.stateId, TMT_BORDERSIZE, &origin);
+ pGetThemeInt(themeData.handle(), themeData.partId, themeData.stateId, TMT_BORDERSIZE, &borderSize);
+
+ // Clip away border region
+ if ((origin == PO_CLASS || origin == PO_PART || origin == PO_STATE) && borderSize > 0) {
+ if (themeData.noBorder) {
+ extraClip &= area;
+ area = area.adjusted(-borderSize, -borderSize, borderSize, borderSize);
+ }
+
+ // Clip away content region
+ if (themeData.noContent) {
+ QRegion content = area.adjusted(borderSize, borderSize, -borderSize, -borderSize);
+ extraClip ^= content;
+ }
+ }
+ addBorderContentClipping = (themeData.noBorder | themeData.noContent);
+ }
+
+ QImage img;
+ if (!haveCachedPixmap) { // If the pixmap is not cached, generate it! -------------------------
+ buffer(w, h); // Ensure a buffer of at least (w, h) in size
+ HDC dc = bufferHDC();
+
+ // Clear the buffer
+ if (alphaType != NoAlpha) {
+ // Consider have separate "memset" function for small chunks for more speedup
+ memset(bufferPixels, inspectData ? 0xFF : 0x00, bufferW * h * 4);
+ }
+
+ // Difference between area and rect
+ int dx = area.x() - rect.x();
+ int dy = area.y() - rect.y();
+ int dr = area.right() - rect.right();
+ int db = area.bottom() - rect.bottom();
+
+ // Adjust so painting rect starts from Origo
+ rect.moveTo(0,0);
+ area.moveTo(dx,dy);
+ DTBGOPTS drawOptions;
+ drawOptions.dwSize = sizeof(drawOptions);
+ drawOptions.rcClip = themeData.toRECT(rect);
+ drawOptions.dwFlags = DTBG_CLIPRECT
+ | (themeData.noBorder ? DTBG_OMITBORDER : 0)
+ | (themeData.noContent ? DTBG_OMITCONTENT : 0);
+
+ // Drawing the part into the backing store
+ if (pDrawThemeBackgroundEx != 0) {
+ RECT rect(themeData.toRECT(area));
+ pDrawThemeBackgroundEx(themeData.handle(), dc, themeData.partId, themeData.stateId, &rect, &drawOptions);
+ } else {
+ // Set the clip region, if used..
+ if (addBorderContentClipping) {
+ SelectClipRgn(dc, extraClip.handle());
+ // Compensate for the noBorder area difference (noContent has the same area)
+ drawOptions.rcClip = themeData.toRECT(rect.adjusted(dx, dy, dr, db));
+ }
+
+ pDrawThemeBackground(themeData.handle(), dc, themeData.partId, themeData.stateId, &(drawOptions.rcClip), 0);
+
+ if (addBorderContentClipping)
+ SelectClipRgn(dc, 0);
+ }
+
+ // If not cached, analyze the buffer data to figure
+ // out alpha type, and if it contains data
+ if (!isCached) {
+ if (inspectData)
+ stateHasData = hasAnyData(rect);
+ // SHORTCUT: If the part's state has no data, cache it for NOOP later
+ if (!stateHasData) {
+ memset(&data, 0, sizeof(data));
+ data.dataValid = true;
+ alphaCache.insert(key, data);
+ return;
+ }
+ hasAlpha = hasAlphaChannel(rect);
+ if (!hasAlpha && partIsTransparent)
+ potentialInvalidAlpha = true;
+#if defined(DEBUG_XP_STYLE) && 1
+ dumpNativeDIB(w, h);
+#endif
+ }
+
+ // Swap alpha values, if needed
+ if (inspectData)
+ wasAlphaSwapped = swapAlphaChannel(rect);
+
+ // Fix alpha values, if needed
+ if (potentialInvalidAlpha)
+ wasAlphaFixed = fixAlphaChannel(rect);
+
+ QImage::Format format;
+ if ((partIsTransparent && !wasAlphaSwapped) || (!partIsTransparent && hasAlpha)) {
+ format = QImage::Format_ARGB32_Premultiplied;
+ alphaType = RealAlpha;
+ } else if (wasAlphaSwapped) {
+ format = QImage::Format_ARGB32_Premultiplied;
+ alphaType = MaskAlpha;
+ } else {
+ format = QImage::Format_RGB32;
+ // The image data we got from the theme engine does not have any transparency,
+ // thus the alpha channel is set to 0.
+ // However, Format_RGB32 requires the alpha part to be set to 0xff, thus
+ // we must flip it from 0x00 to 0xff
+ swapAlphaChannel(rect, true);
+ alphaType = NoAlpha;
+ }
+#if defined(DEBUG_XP_STYLE) && 1
+ printf("Image format is: %s\n", alphaType == RealAlpha ? "Real Alpha" : alphaType == MaskAlpha ? "Masked Alpha" : "No Alpha");
+#endif
+ img = QImage(bufferPixels, bufferW, bufferH, format);
+ }
+
+ // Blitting backing store
+ bool useRegion = partIsTransparent && !hasAlpha && !wasAlphaSwapped;
+
+ QRegion newRegion;
+ QRegion oldRegion;
+ if (useRegion) {
+ newRegion = region(themeData);
+ oldRegion = painter->clipRegion();
+ painter->setClipRegion(newRegion);
+#if defined(DEBUG_XP_STYLE) && 0
+ printf("Using region:\n");
+ QVector<QRect> rects = newRegion.rects();
+ for (int i = 0; i < rects.count(); ++i) {
+ const QRect &r = rects.at(i);
+ printf(" (%d, %d, %d, %d)\n", r.x(), r.y(), r.right(), r.bottom());
+ }
+#endif
+ }
+
+ if (addBorderContentClipping)
+ painter->setClipRegion(extraClip, Qt::IntersectClip);
+
+ if (!themeData.mirrorHorizontally && !themeData.mirrorVertically && !themeData.rotate) {
+ if (!haveCachedPixmap)
+ painter->drawImage(themeData.rect, img, rect);
+ else
+ painter->drawPixmap(themeData.rect, cachedPixmap);
+ } else {
+ // This is _slow_!
+ // Make a copy containing only the necessary data, and mirror
+ // on all wanted axes. Then draw the copy.
+ // If cached, the normal pixmap is cached, instead of caching
+ // all possible orientations for each part and state.
+ QImage imgCopy;
+ if (!haveCachedPixmap)
+ imgCopy = img.copy(rect);
+ else
+ imgCopy = cachedPixmap.toImage();
+
+ if (themeData.rotate) {
+ QMatrix rotMatrix;
+ rotMatrix.rotate(themeData.rotate);
+ imgCopy = imgCopy.transformed(rotMatrix);
+ }
+ if (themeData.mirrorHorizontally || themeData.mirrorVertically) {
+ imgCopy = imgCopy.mirrored(themeData.mirrorHorizontally, themeData.mirrorVertically);
+ }
+ painter->drawImage(themeData.rect,
+ imgCopy);
+ }
+
+ if (useRegion || addBorderContentClipping) {
+ if (oldRegion.isEmpty())
+ painter->setClipping(false);
+ else
+ painter->setClipRegion(oldRegion);
+ }
+
+ // Cache the pixmap to avoid expensive swapAlphaChannel() calls
+ if (!haveCachedPixmap && w && h) {
+ QPixmap pix = QPixmap::fromImage(img).copy(rect);
+ QPixmapCache::insert(pixmapCacheKey, pix);
+#ifdef DEBUG_XP_STYLE
+ printf("+++Adding pixmap to cache, size(%d, %d), wasAlphaSwapped(%d), wasAlphaFixed(%d), name(%s)\n",
+ w, h, wasAlphaSwapped, wasAlphaFixed, qPrintable(pixmapCacheKey));
+#endif
+ }
+
+ // Add to theme part cache
+ if (!isCached) {
+ memset(&data, 0, sizeof(data));
+ data.dataValid = true;
+ data.partIsTransparent = partIsTransparent;
+ data.alphaType = alphaType;
+ data.hasAlphaChannel = hasAlpha;
+ data.hasAnyData = stateHasData;
+ data.wasAlphaSwapped = wasAlphaSwapped;
+ data.hadInvalidAlpha = wasAlphaFixed;
+ alphaCache.insert(key, data);
+ }
+}
+
+
+// ------------------------------------------------------------------------------------------------
+
+/*!
+ \class QWindowsXPStyle
+ \brief The QWindowsXPStyle class provides a Microsoft Windows XP-like look and feel.
+
+ \ingroup appearance
+
+ \warning This style is only available on the Windows XP platform
+ because it makes use of Windows XP's style engine.
+
+ Most of the functions are documented in the base classes
+ QWindowsStyle, QCommonStyle, and QStyle, but the
+ QWindowsXPStyle overloads of drawComplexControl(), drawControl(),
+ drawControlMask(), drawPrimitive(), subControlRect(), and
+ sizeFromContents(), are documented here.
+
+ \img qwindowsxpstyle.png
+ \sa QMacStyle, QWindowsStyle, QPlastiqueStyle, QCDEStyle, QMotifStyle
+*/
+
+/*!
+ Constructs a QWindowsStyle
+*/
+QWindowsXPStyle::QWindowsXPStyle()
+ : QWindowsStyle(*new QWindowsXPStylePrivate)
+{
+}
+
+/*!
+ Destroys the style.
+*/
+QWindowsXPStyle::~QWindowsXPStyle()
+{
+}
+
+/*! \reimp */
+void QWindowsXPStyle::unpolish(QApplication *app)
+{
+ QWindowsStyle::unpolish(app);
+}
+
+/*! \reimp */
+void QWindowsXPStyle::polish(QApplication *app)
+{
+ QWindowsStyle::polish(app);
+ if (!QWindowsXPStylePrivate::useXP())
+ return;
+}
+
+/*! \reimp */
+void QWindowsXPStyle::polish(QWidget *widget)
+{
+ QWindowsStyle::polish(widget);
+ if (!QWindowsXPStylePrivate::useXP())
+ return;
+
+ if (qobject_cast<QAbstractButton*>(widget)
+ || qobject_cast<QToolButton*>(widget)
+ || qobject_cast<QTabBar*>(widget)
+#ifndef QT_NO_COMBOBOX
+ || qobject_cast<QComboBox*>(widget)
+#endif // QT_NO_COMBOBOX
+ || qobject_cast<QScrollBar*>(widget)
+ || qobject_cast<QSlider*>(widget)
+ || qobject_cast<QHeaderView*>(widget)
+#ifndef QT_NO_SPINBOX
+ || qobject_cast<QAbstractSpinBox*>(widget)
+ || qobject_cast<QSpinBox*>(widget)
+#endif // QT_NO_SPINBOX
+ || widget->inherits("QWorkspaceChild")
+ || widget->inherits("Q3TitleBar"))
+ widget->setAttribute(Qt::WA_Hover);
+
+#ifndef QT_NO_RUBBERBAND
+ if (qobject_cast<QRubberBand*>(widget)) {
+ widget->setWindowOpacity(0.6);
+ }
+#endif
+ if (qobject_cast<QStackedWidget*>(widget) &&
+ qobject_cast<QTabWidget*>(widget->parent()))
+ widget->parentWidget()->setAttribute(Qt::WA_ContentsPropagated);
+
+ Q_D(QWindowsXPStyle);
+ if (!d->hasInitColors) {
+ // Get text color for group box labels
+ COLORREF cref;
+ XPThemeData theme(0, 0, QLatin1String("BUTTON"), 0, 0);
+ pGetThemeColor(theme.handle(), BP_GROUPBOX, GBS_NORMAL, TMT_TEXTCOLOR, &cref);
+ d->groupBoxTextColor = qRgb(GetRValue(cref), GetGValue(cref), GetBValue(cref));
+ pGetThemeColor(theme.handle(), BP_GROUPBOX, GBS_DISABLED, TMT_TEXTCOLOR, &cref);
+ d->groupBoxTextColorDisabled = qRgb(GetRValue(cref), GetGValue(cref), GetBValue(cref));
+ // Where does this color come from?
+ //pGetThemeColor(theme.handle(), TKP_TICS, TSS_NORMAL, TMT_COLOR, &cref);
+ d->sliderTickColor = qRgb(165, 162, 148);
+ d->hasInitColors = true;
+ }
+}
+
+/*! \reimp */
+void QWindowsXPStyle::polish(QPalette &pal)
+{
+ QWindowsStyle::polish(pal);
+ pal.setBrush(QPalette::AlternateBase, pal.base().color().darker(110));
+}
+
+/*! \reimp */
+void QWindowsXPStyle::unpolish(QWidget *widget)
+{
+#ifndef QT_NO_RUBBERBAND
+ if (qobject_cast<QRubberBand*>(widget)) {
+ widget->setWindowOpacity(1.0);
+ }
+#endif
+ Q_D(QWindowsXPStyle);
+ // Unpolish of widgets is the first thing that
+ // happens when a theme changes, or the theme
+ // engine is turned off. So we detect it here.
+ bool oldState = QWindowsXPStylePrivate::useXP();
+ bool newState = QWindowsXPStylePrivate::useXP(true);
+ if ((oldState != newState) && newState) {
+ d->cleanup(true);
+ d->init(true);
+ } else {
+ // Cleanup handle map, if just changing style,
+ // or turning it on. In both cases the values
+ // already in the map might be old (other style).
+ d->cleanupHandleMap();
+ }
+ if (qobject_cast<QAbstractButton*>(widget)
+ || qobject_cast<QToolButton*>(widget)
+ || qobject_cast<QTabBar*>(widget)
+#ifndef QT_NO_COMBOBOX
+ || qobject_cast<QComboBox*>(widget)
+#endif // QT_NO_COMBOBOX
+ || qobject_cast<QScrollBar*>(widget)
+ || qobject_cast<QSlider*>(widget)
+ || qobject_cast<QHeaderView*>(widget)
+#ifndef QT_NO_SPINBOX
+ || qobject_cast<QAbstractSpinBox*>(widget)
+ || qobject_cast<QSpinBox*>(widget)
+#endif // QT_NO_SPINBOX
+ || widget->inherits("QWorkspaceChild")
+ || widget->inherits("Q3TitleBar"))
+ widget->setAttribute(Qt::WA_Hover, false);
+ QWindowsStyle::unpolish(widget);
+}
+
+/*! \reimp */
+QRect QWindowsXPStyle::subElementRect(SubElement sr, const QStyleOption *option, const QWidget *widget) const
+{
+ if (!QWindowsXPStylePrivate::useXP()) {
+ return QWindowsStyle::subElementRect(sr, option, widget);
+ }
+
+ QRect rect(option->rect);
+ switch(sr) {
+ case SE_DockWidgetCloseButton:
+ case SE_DockWidgetFloatButton:
+ rect = QWindowsStyle::subElementRect(sr, option, widget);
+ return rect.translated(0, 1);
+ break;
+ case SE_TabWidgetTabContents:
+ if (qstyleoption_cast<const QStyleOptionTabWidgetFrame *>(option))
+ {
+ rect = QWindowsStyle::subElementRect(sr, option, widget);
+ if (sr == SE_TabWidgetTabContents)
+ rect.adjust(0, 0, -2, -2);
+ }
+ break;
+ case SE_TabWidgetTabBar: {
+ rect = QWindowsStyle::subElementRect(sr, option, widget);
+ const QStyleOptionTabWidgetFrame *twfOption =
+ qstyleoption_cast<const QStyleOptionTabWidgetFrame *>(option);
+ if (twfOption && twfOption->direction == Qt::RightToLeft
+ && (twfOption->shape == QTabBar::RoundedNorth
+ || twfOption->shape == QTabBar::RoundedSouth))
+ {
+ QStyleOptionTab otherOption;
+ otherOption.shape = (twfOption->shape == QTabBar::RoundedNorth
+ ? QTabBar::RoundedEast : QTabBar::RoundedSouth);
+ int overlap = pixelMetric(PM_TabBarBaseOverlap, &otherOption, widget);
+ int borderThickness = pixelMetric(PM_DefaultFrameWidth, option, widget);
+ rect.adjust(-overlap + borderThickness, 0, -overlap + borderThickness, 0);
+ }
+ break;}
+
+ case SE_PushButtonContents:
+ if (const QStyleOptionButton *btn = qstyleoption_cast<const QStyleOptionButton *>(option)) {
+ MARGINS borderSize;
+ if (widget) {
+ HTHEME theme = pOpenThemeData(QWindowsXPStylePrivate::winId(widget), L"Button");
+ if (theme) {
+ int stateId;
+ if (!(option->state & State_Enabled))
+ stateId = PBS_DISABLED;
+ else if (option->state & State_Sunken)
+ stateId = PBS_PRESSED;
+ else if (option->state & State_MouseOver)
+ stateId = PBS_HOT;
+ else if (btn->features & QStyleOptionButton::DefaultButton)
+ stateId = PBS_DEFAULTED;
+ else
+ stateId = PBS_NORMAL;
+
+ int border = pixelMetric(PM_DefaultFrameWidth, btn, widget);
+ rect = option->rect.adjusted(border, border, -border, -border);
+
+ int result = pGetThemeMargins(theme,
+ NULL,
+ BP_PUSHBUTTON,
+ stateId,
+ TMT_CONTENTMARGINS,
+ NULL,
+ &borderSize);
+
+ if (result == S_OK) {
+ rect.adjust(borderSize.cxLeftWidth, borderSize.cyTopHeight,
+ -borderSize.cxRightWidth, -borderSize.cyBottomHeight);
+ rect = visualRect(option->direction, option->rect, rect);
+ }
+ }
+ }
+ }
+ break;
+ case SE_ProgressBarContents:
+ rect = QCommonStyle::subElementRect(SE_ProgressBarGroove, option, widget);
+ if (option->state & QStyle::State_Horizontal)
+ rect.adjust(4, 3, -4, -3);
+ else
+ rect.adjust(3, 2, -3, -2);
+ break;
+ default:
+ rect = QWindowsStyle::subElementRect(sr, option, widget);
+ }
+ return rect;
+}
+
+/*!
+ \reimp
+*/
+void QWindowsXPStyle::drawPrimitive(PrimitiveElement pe, const QStyleOption *option, QPainter *p,
+ const QWidget *widget) const
+{
+ QWindowsXPStylePrivate *d = const_cast<QWindowsXPStylePrivate*>(d_func());
+
+ if (!QWindowsXPStylePrivate::useXP()) {
+ QWindowsStyle::drawPrimitive(pe, option, p, widget);
+ return;
+ }
+
+ QString name;
+ int partId = 0;
+ int stateId = 0;
+ QRect rect = option->rect;
+ State flags = option->state;
+ bool hMirrored = false;
+ bool vMirrored = false;
+ bool noBorder = false;
+ bool noContent = false;
+ int rotate = 0;
+
+ switch (pe) {
+ case PE_FrameTabBarBase:
+ if (const QStyleOptionTabBarBase *tbb
+ = qstyleoption_cast<const QStyleOptionTabBarBase *>(option)) {
+ p->save();
+ switch (tbb->shape) {
+ case QTabBar::RoundedNorth:
+ p->setPen(QPen(tbb->palette.dark(), 0));
+ p->drawLine(tbb->rect.topLeft(), tbb->rect.topRight());
+ break;
+ case QTabBar::RoundedWest:
+ p->setPen(QPen(tbb->palette.dark(), 0));
+ p->drawLine(tbb->rect.left(), tbb->rect.top(), tbb->rect.left(), tbb->rect.bottom());
+ break;
+ case QTabBar::RoundedSouth:
+ p->setPen(QPen(tbb->palette.dark(), 0));
+ p->drawLine(tbb->rect.left(), tbb->rect.top(),
+ tbb->rect.right(), tbb->rect.top());
+ break;
+ case QTabBar::RoundedEast:
+ p->setPen(QPen(tbb->palette.dark(), 0));
+ p->drawLine(tbb->rect.topLeft(), tbb->rect.bottomLeft());
+ break;
+ case QTabBar::TriangularNorth:
+ case QTabBar::TriangularEast:
+ case QTabBar::TriangularWest:
+ case QTabBar::TriangularSouth:
+ p->restore();
+ QWindowsStyle::drawPrimitive(pe, option, p, widget);
+ return;
+ }
+ p->restore();
+ }
+ return;
+ case PE_PanelButtonBevel:
+ name = QLatin1String("BUTTON");
+ partId = BP_PUSHBUTTON;
+ if (!(flags & State_Enabled))
+ stateId = PBS_DISABLED;
+ else if ((flags & State_Sunken) || (flags & State_On))
+ stateId = PBS_PRESSED;
+ else if (flags & State_MouseOver)
+ stateId = PBS_HOT;
+ //else if (flags & State_ButtonDefault)
+ // stateId = PBS_DEFAULTED;
+ else
+ stateId = PBS_NORMAL;
+ break;
+
+ case PE_PanelButtonTool:
+ if (widget && widget->inherits("QDockWidgetTitleButton")) {
+ if (const QWidget *dw = widget->parentWidget())
+ if (dw->isWindow())
+ return;
+ }
+ name = QLatin1String("TOOLBAR");
+ partId = TP_BUTTON;
+ if (!(flags & State_Enabled))
+ stateId = TS_DISABLED;
+ else if (flags & State_Sunken)
+ stateId = TS_PRESSED;
+ else if (flags & State_MouseOver)
+ stateId = flags & State_On ? TS_HOTCHECKED : TS_HOT;
+ else if (flags & State_On)
+ stateId = TS_CHECKED;
+ else if (!(flags & State_AutoRaise))
+ stateId = TS_HOT;
+ else
+ stateId = TS_NORMAL;
+ break;
+
+ case PE_IndicatorButtonDropDown:
+ name = QLatin1String("TOOLBAR");
+ partId = TP_SPLITBUTTONDROPDOWN;
+ if (!(flags & State_Enabled))
+ stateId = TS_DISABLED;
+ else if (flags & State_Sunken)
+ stateId = TS_PRESSED;
+ else if (flags & State_MouseOver)
+ stateId = flags & State_On ? TS_HOTCHECKED : TS_HOT;
+ else if (flags & State_On)
+ stateId = TS_CHECKED;
+ else if (!(flags & State_AutoRaise))
+ stateId = TS_HOT;
+ else
+ stateId = TS_NORMAL;
+ if (option->direction == Qt::RightToLeft)
+ hMirrored = true;
+ break;
+
+ case PE_IndicatorCheckBox:
+ name = QLatin1String("BUTTON");
+ partId = BP_CHECKBOX;
+ if (!(flags & State_Enabled))
+ stateId = CBS_UNCHECKEDDISABLED;
+ else if (flags & State_Sunken)
+ stateId = CBS_UNCHECKEDPRESSED;
+ else if (flags & State_MouseOver)
+ stateId = CBS_UNCHECKEDHOT;
+ else
+ stateId = CBS_UNCHECKEDNORMAL;
+
+ if (flags & State_On)
+ stateId += CBS_CHECKEDNORMAL-1;
+ else if (flags & State_NoChange)
+ stateId += CBS_MIXEDNORMAL-1;
+
+ break;
+
+ case PE_IndicatorRadioButton:
+ name = QLatin1String("BUTTON");
+ partId = BP_RADIOBUTTON;
+ if (!(flags & State_Enabled))
+ stateId = RBS_UNCHECKEDDISABLED;
+ else if (flags & State_Sunken)
+ stateId = RBS_UNCHECKEDPRESSED;
+ else if (flags & State_MouseOver)
+ stateId = RBS_UNCHECKEDHOT;
+ else
+ stateId = RBS_UNCHECKEDNORMAL;
+
+ if (flags & State_On)
+ stateId += RBS_CHECKEDNORMAL-1;
+ break;
+
+ case PE_IndicatorDockWidgetResizeHandle:
+ return;
+
+case PE_Frame:
+ {
+ if (flags & State_Raised)
+ return;
+ name = QLatin1String("LISTVIEW");
+ partId = LVP_LISTGROUP;
+ XPThemeData theme(0, 0, name, partId, 0);
+
+ if (!(flags & State_Enabled))
+ stateId = ETS_DISABLED;
+ else
+ stateId = ETS_NORMAL;
+ int fillType;
+ if (pGetThemeEnumValue(theme.handle(), partId, stateId, TMT_BGTYPE, &fillType) == S_OK) {
+ if (fillType == BT_BORDERFILL) {
+ COLORREF bcRef;
+ pGetThemeColor(theme.handle(), partId, stateId, TMT_BORDERCOLOR, &bcRef);
+ QColor bordercolor(qRgb(GetRValue(bcRef), GetGValue(bcRef), GetBValue(bcRef)));
+ QPen oldPen = p->pen();
+ // int borderSize = 1;
+ // pGetThemeInt(theme.handle(), partId, stateId, TMT_BORDERCOLOR, &borderSize);
+
+ // Inner white border
+ p->setPen(QPen(option->palette.base().color(), 1));
+ p->drawRect(option->rect.adjusted(1, 1, -2, -2));
+ // Outer dark border
+ p->setPen(QPen(bordercolor, 1));
+ p->drawRect(option->rect.adjusted(0, 0, -1, -1));
+ p->setPen(oldPen);
+ return;
+ } else if (fillType == BT_NONE) {
+ return;
+ } else {
+ break;
+ }
+ }
+ }
+ case PE_FrameLineEdit: {
+ // we try to check if this lineedit is a delegate on a QAbstractItemView-derived class.
+ QWidget *parentWidget = 0;
+ if (widget)
+ parentWidget = widget->parentWidget();
+ if (parentWidget)
+ parentWidget = parentWidget->parentWidget();
+ if (widget && widget->inherits("QLineEdit")
+ && parentWidget && parentWidget->inherits("QAbstractItemView")) {
+ QPen oldPen = p->pen();
+ // Inner white border
+ p->setPen(QPen(option->palette.base().color(), 1));
+ p->drawRect(option->rect.adjusted(1, 1, -2, -2));
+ // Outer dark border
+ p->setPen(QPen(option->palette.shadow().color(), 1));
+ p->drawRect(option->rect.adjusted(0, 0, -1, -1));
+ p->setPen(oldPen);
+ return;
+ } else if (qstyleoption_cast<const QStyleOptionFrame *>(option)) {
+ name = QLatin1String("EDIT");
+ partId = EP_EDITTEXT;
+ noContent = true;
+ if (!(flags & State_Enabled))
+ stateId = ETS_DISABLED;
+ else
+ stateId = ETS_NORMAL;
+ }
+ break;
+ }
+
+ case PE_PanelLineEdit:
+ if (const QStyleOptionFrame *panel = qstyleoption_cast<const QStyleOptionFrame *>(option)) {
+ name = QLatin1String("EDIT");
+ partId = EP_EDITTEXT;
+ noBorder = true;
+ QBrush bg;
+ bool usePalette = false;
+ bool isEnabled = flags & State_Enabled;
+ uint resolve_mask = panel->palette.resolve();
+
+#ifndef QT_NO_SPINBOX
+ //Since spin box includes a line edit we need to resolve the palette on the spin box instead
+ if (widget) {
+ if (QAbstractSpinBox *spinbox = qobject_cast<QAbstractSpinBox*>(widget->parentWidget()))
+ resolve_mask = spinbox->palette().resolve();
+ }
+#endif // QT_NO_SPINBOX
+ if (resolve_mask & (1 << QPalette::Base)) {
+ // Base color is set for this widget, so use it
+ bg = panel->palette.brush(QPalette::Base);
+ usePalette = true;
+ }
+
+ stateId = isEnabled ? ETS_NORMAL : ETS_DISABLED;
+
+ if (usePalette) {
+ p->fillRect(panel->rect, bg);
+ } else {
+ XPThemeData theme(0, p, name, partId, stateId, rect);
+ if (!theme.isValid()) {
+ QWindowsStyle::drawPrimitive(pe, option, p, widget);
+ return;
+ }
+ int bgType;
+ pGetThemeEnumValue( theme.handle(),
+ partId,
+ stateId,
+ TMT_BGTYPE,
+ &bgType);
+ if( bgType == BT_IMAGEFILE ) {
+ theme.mirrorHorizontally = hMirrored;
+ theme.mirrorVertically = vMirrored;
+ theme.noBorder = noBorder;
+ theme.noContent = noContent;
+ theme.rotate = rotate;
+ d->drawBackground(theme);
+ } else {
+ QBrush fillColor = option->palette.brush(QPalette::Base);
+
+ if (!isEnabled) {
+ PROPERTYORIGIN origin = PO_NOTFOUND;
+ pGetThemePropertyOrigin(theme.handle(), theme.partId, theme.stateId, TMT_FILLCOLOR, &origin);
+ // Use only if the fill property comes from our part
+ if ((origin == PO_PART || origin == PO_STATE)) {
+ COLORREF bgRef;
+ pGetThemeColor(theme.handle(), partId, stateId, TMT_FILLCOLOR, &bgRef);
+ fillColor = QBrush(qRgb(GetRValue(bgRef), GetGValue(bgRef), GetBValue(bgRef)));
+ }
+ }
+ p->fillRect(option->rect, fillColor);
+ }
+ }
+
+ if (panel->lineWidth > 0)
+ drawPrimitive(PE_FrameLineEdit, panel, p, widget);
+ return;
+ }
+ break;
+
+ case PE_FrameTabWidget:
+ if (const QStyleOptionTabWidgetFrame *tab = qstyleoption_cast<const QStyleOptionTabWidgetFrame *>(option))
+ {
+ name = QLatin1String("TAB");
+ partId = TABP_PANE;
+
+ if (widget) {
+ bool useGradient = true;
+ const int maxlength = 256;
+ WCHAR themeFileName[maxlength];
+ WCHAR themeColor[maxlength];
+ // Due to a a scaling issue with the XP Silver theme, tab gradients are not used with it
+ if (pGetCurrentThemeName(themeFileName, maxlength, themeColor, maxlength, NULL, 0) == S_OK) {
+ WCHAR* offset;
+ if ((offset = wcsrchr(themeFileName, QChar(QLatin1Char('\\')).unicode())) != NULL) {
+ offset++;
+ if (!lstrcmp(offset, L"Luna.msstyles") && !lstrcmp(offset, L"Metallic")) {
+ useGradient = false;
+ }
+ }
+ }
+ // This should work, but currently there's an error in the ::drawBackgroundDirectly()
+ // code, when using the HDC directly..
+ if (useGradient) {
+ QStyleOptionTabWidgetFrame frameOpt = *tab;
+ frameOpt.rect = widget->rect();
+ QRect contentsRect = subElementRect(SE_TabWidgetTabContents, &frameOpt, widget);
+ QRegion reg = option->rect;
+ reg -= contentsRect;
+ p->setClipRegion(reg);
+ XPThemeData theme(widget, p, name, partId, stateId, rect);
+ theme.mirrorHorizontally = hMirrored;
+ theme.mirrorVertically = vMirrored;
+ d->drawBackground(theme);
+ p->setClipRect(contentsRect);
+ partId = TABP_BODY;
+ }
+ }
+ switch (tab->shape) {
+ case QTabBar::RoundedNorth:
+ case QTabBar::TriangularNorth:
+ break;
+ case QTabBar::RoundedSouth:
+ case QTabBar::TriangularSouth:
+ vMirrored = true;
+ break;
+ case QTabBar::RoundedEast:
+ case QTabBar::TriangularEast:
+ rotate = 90;
+ break;
+ case QTabBar::RoundedWest:
+ case QTabBar::TriangularWest:
+ rotate = 90;
+ hMirrored = true;
+ break;
+ default:
+ break;
+ }
+ }
+ break;
+
+ case PE_FrameMenu:
+ p->save();
+ p->setPen(option->palette.dark().color());
+ p->drawRect(rect.adjusted(0, 0, -1, -1));
+ p->restore();
+ return;
+
+ case PE_PanelMenuBar:
+ break;
+
+ case PE_FrameDockWidget:
+ if (const QStyleOptionFrame *frm = qstyleoption_cast<const QStyleOptionFrame *>(option))
+ {
+ name = QLatin1String("WINDOW");
+ if (flags & State_Active)
+ stateId = FS_ACTIVE;
+ else
+ stateId = FS_INACTIVE;
+
+ int fwidth = pixelMetric(PM_DockWidgetFrameWidth, frm, widget);
+
+ XPThemeData theme(widget, p, name, 0, stateId);
+ if (!theme.isValid())
+ break;
+ theme.rect = QRect(frm->rect.x(), frm->rect.y(), frm->rect.x()+fwidth, frm->rect.height()-fwidth); theme.partId = WP_SMALLFRAMELEFT;
+ d->drawBackground(theme);
+ theme.rect = QRect(frm->rect.width()-fwidth, frm->rect.y(), fwidth, frm->rect.height()-fwidth);
+ theme.partId = WP_SMALLFRAMERIGHT;
+ d->drawBackground(theme);
+ theme.rect = QRect(frm->rect.x(), frm->rect.bottom()-fwidth+1, frm->rect.width(), fwidth);
+ theme.partId = WP_SMALLFRAMEBOTTOM;
+ d->drawBackground(theme);
+ return;
+ }
+ break;
+
+ case PE_IndicatorHeaderArrow:
+ {
+#if 0 // XP theme engine doesn't know about this :(
+ name = QLatin1String("HEADER");
+ partId = HP_HEADERSORTARROW;
+ if (flags & State_Down)
+ stateId = HSAS_SORTEDDOWN;
+ else
+ stateId = HSAS_SORTEDUP;
+#else
+ if (const QStyleOptionHeader *header = qstyleoption_cast<const QStyleOptionHeader *>(option)) {
+ p->save();
+ p->setPen(option->palette.dark().color());
+ p->translate(0, option->rect.height()/2 - 4);
+ if (header->sortIndicator & QStyleOptionHeader::SortUp) { // invert logic to follow Windows style guide
+ p->drawLine(option->rect.x(), option->rect.y(), option->rect.x()+8, option->rect.y());
+ p->drawLine(option->rect.x()+1, option->rect.y()+1, option->rect.x()+7, option->rect.y()+1);
+ p->drawLine(option->rect.x()+2, option->rect.y()+2, option->rect.x()+6, option->rect.y()+2);
+ p->drawLine(option->rect.x()+3, option->rect.y()+3, option->rect.x()+5, option->rect.y()+3);
+ p->drawPoint(option->rect.x()+4, option->rect.y()+4);
+ } else if(header->sortIndicator & QStyleOptionHeader::SortDown) {
+ p->drawLine(option->rect.x(), option->rect.y()+4, option->rect.x()+8, option->rect.y()+4);
+ p->drawLine(option->rect.x()+1, option->rect.y()+3, option->rect.x()+7, option->rect.y()+3);
+ p->drawLine(option->rect.x()+2, option->rect.y()+2, option->rect.x()+6, option->rect.y()+2);
+ p->drawLine(option->rect.x()+3, option->rect.y()+1, option->rect.x()+5, option->rect.y()+1);
+ p->drawPoint(option->rect.x()+4, option->rect.y());
+ }
+ p->restore();
+ return;
+ }
+#endif
+ }
+ break;
+
+ case PE_FrameStatusBarItem:
+ name = QLatin1String("STATUS");
+ partId = SP_PANE;
+ break;
+
+ case PE_FrameGroupBox:
+ name = QLatin1String("BUTTON");
+ partId = BP_GROUPBOX;
+ if (!(flags & State_Enabled))
+ stateId = GBS_DISABLED;
+ else
+ stateId = GBS_NORMAL;
+ if (const QStyleOptionFrame *frame = qstyleoption_cast<const QStyleOptionFrame *>(option)) {
+ const QStyleOptionFrameV2 *frame2 = qstyleoption_cast<const QStyleOptionFrameV2 *>(option);
+ if (frame2->features & QStyleOptionFrameV2::Flat) {
+ // Windows XP does not have a theme part for a flat GroupBox, paint it with the windows style
+ QRect fr = frame->rect;
+ QPoint p1(fr.x(), fr.y() + 1);
+ QPoint p2(fr.x() + fr.width(), p1.y() + 1);
+ rect = QRect(p1, p2);
+ name = QLatin1String("");
+ }
+ }
+ break;
+
+ case PE_IndicatorProgressChunk:
+ {
+ Qt::Orientation orient = Qt::Horizontal;
+ bool inverted = false;
+ if (const QStyleOptionProgressBarV2 *pb2 = qstyleoption_cast<const QStyleOptionProgressBarV2 *>(option)) {
+ orient = pb2->orientation;
+ if (pb2->invertedAppearance)
+ inverted = true;
+ }
+ if (orient == Qt::Horizontal) {
+ partId = PP_CHUNK;
+ rect = QRect(option->rect.x(), option->rect.y(), option->rect.width(), option->rect.height() );
+ if (inverted && option->direction == Qt::LeftToRight)
+ hMirrored = true;
+ } else {
+ partId = PP_CHUNKVERT;
+ rect = QRect(option->rect.x(), option->rect.y(), option->rect.width(), option->rect.height());
+ }
+ name = QLatin1String("PROGRESS");
+ stateId = 1;
+ }
+ break;
+
+ case PE_Q3DockWindowSeparator:
+ name = QLatin1String("TOOLBAR");
+ if (flags & State_Horizontal)
+ partId = TP_SEPARATOR;
+ else
+ partId = TP_SEPARATORVERT;
+ break;
+
+ case PE_FrameWindow:
+ if (const QStyleOptionFrame *frm = qstyleoption_cast<const QStyleOptionFrame *>(option))
+ {
+ name = QLatin1String("WINDOW");
+ if (flags & State_Active)
+ stateId = FS_ACTIVE;
+ else
+ stateId = FS_INACTIVE;
+
+ int fwidth = frm->lineWidth + frm->midLineWidth;
+
+ XPThemeData theme(0, p, name, 0, stateId);
+ if (!theme.isValid())
+ break;
+
+ theme.rect = QRect(option->rect.x(), option->rect.y()+fwidth, option->rect.x()+fwidth, option->rect.height()-fwidth);
+ theme.partId = WP_FRAMELEFT;
+ d->drawBackground(theme);
+ theme.rect = QRect(option->rect.width()-fwidth, option->rect.y()+fwidth, fwidth, option->rect.height()-fwidth);
+ theme.partId = WP_FRAMERIGHT;
+ d->drawBackground(theme);
+ theme.rect = QRect(option->rect.x(), option->rect.height()-fwidth, option->rect.width(), fwidth);
+ theme.partId = WP_FRAMEBOTTOM;
+ d->drawBackground(theme);
+ theme.rect = QRect(option->rect.x(), option->rect.y(), option->rect.width(), option->rect.y()+fwidth);
+ theme.partId = WP_CAPTION;
+ d->drawBackground(theme);
+ return;
+ }
+ break;
+
+ case PE_IndicatorBranch:
+ {
+ static const int decoration_size = 9;
+ int mid_h = option->rect.x() + option->rect.width() / 2;
+ int mid_v = option->rect.y() + option->rect.height() / 2;
+ int bef_h = mid_h;
+ int bef_v = mid_v;
+ int aft_h = mid_h;
+ int aft_v = mid_v;
+ QBrush brush(option->palette.dark().color(), Qt::Dense4Pattern);
+ if (option->state & State_Item) {
+ if (option->direction == Qt::RightToLeft)
+ p->fillRect(option->rect.left(), mid_v, bef_h - option->rect.left(), 1, brush);
+ else
+ p->fillRect(aft_h, mid_v, option->rect.right() - aft_h + 1, 1, brush);
+ }
+ if (option->state & State_Sibling)
+ p->fillRect(mid_h, aft_v, 1, option->rect.bottom() - aft_v + 1, brush);
+ if (option->state & (State_Open | State_Children | State_Item | State_Sibling))
+ p->fillRect(mid_h, option->rect.y(), 1, bef_v - option->rect.y(), brush);
+ if (option->state & State_Children) {
+ int delta = decoration_size / 2;
+ bef_h -= delta;
+ bef_v -= delta;
+ aft_h += delta;
+ aft_v += delta;
+ XPThemeData theme(0, p, QLatin1String("TREEVIEW"));
+ theme.rect = QRect(bef_h, bef_v, decoration_size, decoration_size);
+ theme.partId = TVP_GLYPH;
+ theme.stateId = flags & QStyle::State_Open ? GLPS_OPENED : GLPS_CLOSED;
+ d->drawBackground(theme);
+ }
+ }
+ return;
+
+ case PE_IndicatorToolBarSeparator:
+
+ name = QLatin1String("TOOLBAR");
+ partId = TP_SEPARATOR;
+
+ if (option->state & State_Horizontal)
+ partId = TP_SEPARATOR;
+ else
+ partId = TP_SEPARATORVERT;
+
+ break;
+
+ case PE_IndicatorToolBarHandle:
+
+ name = QLatin1String("REBAR");
+ partId = RP_GRIPPER;
+ if (option->state & State_Horizontal) {
+ partId = RP_GRIPPER;
+ rect.adjust(0, 0, -2, 0);
+ }
+ else {
+ partId = RP_GRIPPERVERT;
+ rect.adjust(0, 0, 0, -2);
+ }
+ break;
+
+ case PE_IndicatorItemViewItemCheck: {
+ QStyleOptionButton button;
+ button.QStyleOption::operator=(*option);
+ button.state &= ~State_MouseOver;
+ drawPrimitive(PE_IndicatorCheckBox, &button, p, widget);
+ return;
+ }
+
+ default:
+ break;
+ }
+
+ XPThemeData theme(0, p, name, partId, stateId, rect);
+ if (!theme.isValid()) {
+ QWindowsStyle::drawPrimitive(pe, option, p, widget);
+ return;
+ }
+ theme.mirrorHorizontally = hMirrored;
+ theme.mirrorVertically = vMirrored;
+ theme.noBorder = noBorder;
+ theme.noContent = noContent;
+ theme.rotate = rotate;
+ d->drawBackground(theme);
+}
+
+/*!
+ \reimp
+*/
+void QWindowsXPStyle::drawControl(ControlElement element, const QStyleOption *option, QPainter *p,
+ const QWidget *widget) const
+{
+ QWindowsXPStylePrivate *d = const_cast<QWindowsXPStylePrivate*>(d_func());
+ if (!QWindowsXPStylePrivate::useXP()) {
+ QWindowsStyle::drawControl(element, option, p, widget);
+ return;
+ }
+
+ QRect rect(option->rect);
+ State flags = option->state;
+
+ int rotate = 0;
+ bool hMirrored = false;
+ bool vMirrored = false;
+
+ QString name;
+ int partId = 0;
+ int stateId = 0;
+ switch (element) {
+ case CE_SizeGrip:
+ {
+ name = QLatin1String("STATUS");
+ partId = SP_GRIPPER;
+ SIZE sz;
+ XPThemeData theme(0, p, name, partId, 0);
+ pGetThemePartSize(theme.handle(), 0, partId, 0, 0, TS_TRUE, &sz);
+ --sz.cy;
+ if (const QStyleOptionSizeGrip *sg = qstyleoption_cast<const QStyleOptionSizeGrip *>(option)) {
+ switch (sg->corner) {
+ case Qt::BottomRightCorner:
+ rect = QRect(rect.right() - sz.cx, rect.bottom() - sz.cy, sz.cx, sz.cy);
+ break;
+ case Qt::BottomLeftCorner:
+ rect = QRect(rect.left() + 1, rect.bottom() - sz.cy, sz.cx, sz.cy);
+ hMirrored = true;
+ break;
+ case Qt::TopRightCorner:
+ rect = QRect(rect.right() - sz.cx, rect.top() + 1, sz.cx, sz.cy);
+ vMirrored = true;
+ break;
+ case Qt::TopLeftCorner:
+ rect = QRect(rect.left() + 1, rect.top() + 1, sz.cx, sz.cy);
+ hMirrored = vMirrored = true;
+ }
+ }
+ }
+ break;
+
+ case CE_HeaderSection:
+ name = QLatin1String("HEADER");
+ partId = HP_HEADERITEM;
+ if (flags & State_Sunken)
+ stateId = HIS_PRESSED;
+ else if (flags & State_MouseOver)
+ stateId = HIS_HOT;
+ else
+ stateId = HIS_NORMAL;
+ break;
+
+ case CE_Splitter:
+ p->eraseRect(option->rect);
+ return;
+
+ case CE_PushButtonBevel:
+ if (const QStyleOptionButton *btn = qstyleoption_cast<const QStyleOptionButton *>(option))
+ {
+ name = QLatin1String("BUTTON");
+ partId = BP_PUSHBUTTON;
+ bool justFlat = (btn->features & QStyleOptionButton::Flat) && !(flags & (State_On|State_Sunken))
+ || (btn->features & QStyleOptionButton::CommandLinkButton
+ && !(flags & State_MouseOver)
+ && !(btn->features & QStyleOptionButton::DefaultButton));
+ if (!(flags & State_Enabled) && !(btn->features & QStyleOptionButton::Flat))
+ stateId = PBS_DISABLED;
+ else if (justFlat)
+ ;
+ else if (flags & (State_Sunken | State_On))
+ stateId = PBS_PRESSED;
+ else if (flags & State_MouseOver)
+ stateId = PBS_HOT;
+ else if (btn->features & QStyleOptionButton::DefaultButton)
+ stateId = PBS_DEFAULTED;
+ else
+ stateId = PBS_NORMAL;
+
+ if (!justFlat) {
+ XPThemeData theme(widget, p, name, partId, stateId, rect);
+ d->drawBackground(theme);
+ }
+
+ if (btn->features & QStyleOptionButton::HasMenu) {
+ int mbiw = 0, mbih = 0;
+ XPThemeData theme(widget, 0, QLatin1String("TOOLBAR"), TP_SPLITBUTTONDROPDOWN);
+ if (theme.isValid()) {
+ SIZE size;
+ pGetThemePartSize(theme.handle(), 0, theme.partId, theme.stateId, 0, TS_TRUE, &size);
+ mbiw = size.cx;
+ mbih = size.cy;
+ }
+
+ QRect ir = btn->rect;
+ QStyleOptionButton newBtn = *btn;
+ newBtn.rect = QRect(ir.right() - mbiw - 1, 1 + (ir.height()/2) - (mbih/2), mbiw, mbih);
+ drawPrimitive(PE_IndicatorArrowDown, &newBtn, p, widget);
+ }
+ return;
+ }
+ break;
+ case CE_TabBarTab:
+ if (const QStyleOptionTab *tab = qstyleoption_cast<const QStyleOptionTab *>(option))
+ {
+ stateId = tab->state & State_Enabled ? TIS_NORMAL : TIS_DISABLED;
+ }
+ break;
+
+ case CE_TabBarTabShape:
+ if (const QStyleOptionTab *tab = qstyleoption_cast<const QStyleOptionTab *>(option))
+ {
+ name = QLatin1String("TAB");
+ bool isDisabled = !(tab->state & State_Enabled);
+ bool hasFocus = tab->state & State_HasFocus;
+ bool isHot = tab->state & State_MouseOver;
+ bool selected = tab->state & State_Selected;
+ bool lastTab = tab->position == QStyleOptionTab::End;
+ bool firstTab = tab->position == QStyleOptionTab::Beginning;
+ bool onlyOne = tab->position == QStyleOptionTab::OnlyOneTab;
+ bool leftAligned = styleHint(SH_TabBar_Alignment, tab, widget) == Qt::AlignLeft;
+ bool centerAligned = styleHint(SH_TabBar_Alignment, tab, widget) == Qt::AlignCenter;
+ int borderThickness = pixelMetric(PM_DefaultFrameWidth, option, widget);
+ int tabOverlap = pixelMetric(PM_TabBarTabOverlap, option, widget);
+
+ if (isDisabled)
+ stateId = TIS_DISABLED;
+ else if (selected)
+ stateId = TIS_SELECTED;
+ else if (hasFocus)
+ stateId = TIS_FOCUSED;
+ else if (isHot)
+ stateId = TIS_HOT;
+ else
+ stateId = TIS_NORMAL;
+
+ // Selecting proper part depending on position
+ if (firstTab || onlyOne) {
+ if (leftAligned) {
+ partId = TABP_TABITEMLEFTEDGE;
+ } else if (centerAligned) {
+ partId = TABP_TABITEM;
+ } else { // rightAligned
+ partId = TABP_TABITEMRIGHTEDGE;
+ }
+ } else {
+ partId = TABP_TABITEM;
+ }
+
+ if (tab->direction == Qt::RightToLeft
+ && (tab->shape == QTabBar::RoundedNorth
+ || tab->shape == QTabBar::RoundedSouth)) {
+ bool temp = firstTab;
+ firstTab = lastTab;
+ lastTab = temp;
+ }
+ bool begin = firstTab || onlyOne;
+ bool end = lastTab || onlyOne;
+ switch (tab->shape) {
+ case QTabBar::RoundedNorth:
+ if (selected)
+ rect.adjust(begin ? 0 : -tabOverlap, 0, end ? 0 : tabOverlap, borderThickness);
+ else
+ rect.adjust(begin? tabOverlap : 0, tabOverlap, end ? -tabOverlap : 0, 0);
+ break;
+ case QTabBar::RoundedSouth:
+ //vMirrored = true;
+ rotate = 180; // Not 100% correct, but works
+ if (selected)
+ rect.adjust(begin ? 0 : -tabOverlap , -borderThickness, end ? 0 : tabOverlap, 0);
+ else
+ rect.adjust(begin ? tabOverlap : 0, 0, end ? -tabOverlap : 0 , -tabOverlap);
+ break;
+ case QTabBar::RoundedEast:
+ rotate = 90;
+ if (selected) {
+ rect.adjust(-borderThickness, begin ? 0 : -tabOverlap, 0, end ? 0 : tabOverlap);
+ }else{
+ rect.adjust(0, begin ? tabOverlap : 0, -tabOverlap, end ? -tabOverlap : 0);
+ }
+ break;
+ case QTabBar::RoundedWest:
+ hMirrored = true;
+ rotate = 90;
+ if (selected) {
+ rect.adjust(0, begin ? 0 : -tabOverlap, borderThickness, end ? 0 : tabOverlap);
+ }else{
+ rect.adjust(tabOverlap, begin ? tabOverlap : 0, 0, end ? -tabOverlap : 0);
+ }
+ break;
+ default:
+ name = QLatin1String(""); // Do our own painting for triangular
+ break;
+ }
+
+ if (!selected) {
+ switch (tab->shape) {
+ case QTabBar::RoundedNorth:
+ rect.adjust(0,0, 0,-1);
+ break;
+ case QTabBar::RoundedSouth:
+ rect.adjust(0,1, 0,0);
+ break;
+ case QTabBar::RoundedEast:
+ rect.adjust( 1,0, 0,0);
+ break;
+ case QTabBar::RoundedWest:
+ rect.adjust(0,0, -1,0);
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ break;
+
+ case CE_ProgressBarGroove:
+ {
+ Qt::Orientation orient = Qt::Horizontal;
+ if (const QStyleOptionProgressBarV2 *pb2 = qstyleoption_cast<const QStyleOptionProgressBarV2 *>(option))
+ orient = pb2->orientation;
+ partId = (orient == Qt::Horizontal) ? PP_BAR : PP_BARVERT;
+ name = QLatin1String("PROGRESS");
+ stateId = 1;
+ }
+ break;
+
+ case CE_MenuEmptyArea:
+ case CE_MenuItem:
+ if (const QStyleOptionMenuItem *menuitem = qstyleoption_cast<const QStyleOptionMenuItem *>(option))
+ {
+ int tab = menuitem->tabWidth;
+ bool dis = !(menuitem->state & State_Enabled);
+ bool act = menuitem->state & State_Selected;
+ bool checkable = menuitem->menuHasCheckableItems;
+ bool checked = checkable ? menuitem->checked : false;
+
+ // windows always has a check column, regardless whether we have an icon or not
+ int checkcol = qMax(menuitem->maxIconWidth, 12);
+
+ int x, y, w, h;
+ rect.getRect(&x, &y, &w, &h);
+
+ QBrush fill = menuitem->palette.brush(act ? QPalette::Highlight : QPalette::Button);
+ p->fillRect(rect, fill);
+
+ if (element == CE_MenuEmptyArea)
+ break;
+
+ // draw separator -------------------------------------------------
+ if (menuitem->menuItemType == QStyleOptionMenuItem::Separator) {
+ int yoff = y-1 + h / 2;
+ p->setPen(menuitem->palette.dark().color());
+ p->drawLine(x, yoff, x+w, yoff);
+ ++yoff;
+ p->setPen(menuitem->palette.light().color());
+ p->drawLine(x, yoff, x+w, yoff);
+ return;
+ }
+
+ int xpos = x;
+
+ // draw icon ------------------------------------------------------
+ if (!menuitem->icon.isNull()) {
+ QIcon::Mode mode = dis ? QIcon::Disabled : QIcon::Normal;
+ if (act && !dis)
+ mode = QIcon::Active;
+ QPixmap pixmap = checked ?
+ menuitem->icon.pixmap(pixelMetric(PM_SmallIconSize, option, widget), mode, QIcon::On) :
+ menuitem->icon.pixmap(pixelMetric(PM_SmallIconSize, option, widget), mode);
+ int pixw = pixmap.width();
+ int pixh = pixmap.height();
+ QRect iconRect(0, 0, pixw, pixh);
+ iconRect.moveCenter(QRect(xpos, y, checkcol, h).center());
+ QRect vIconRect = visualRect(option->direction, option->rect, iconRect);
+ p->setPen(menuitem->palette.text().color());
+ p->setBrush(Qt::NoBrush);
+ if (checked)
+ p->drawRect(vIconRect.adjusted(-1, -2, 1, 1));
+ p->drawPixmap(vIconRect.topLeft(), pixmap);
+
+ // draw checkmark -------------------------------------------------
+ } else if (checked) {
+ QStyleOptionMenuItem newMi = *menuitem;
+ newMi.state = State_None;
+ if (!dis)
+ newMi.state |= State_Enabled;
+ if (act)
+ newMi.state |= State_On;
+
+ QRect checkMarkRect = QRect(menuitem->rect.x() + windowsItemFrame,
+ menuitem->rect.y() + windowsItemFrame,
+ checkcol - 2 * windowsItemFrame,
+ menuitem->rect.height() - 2*windowsItemFrame);
+ newMi.rect = visualRect(option->direction, option->rect, checkMarkRect);
+ drawPrimitive(PE_IndicatorMenuCheckMark, &newMi, p, widget);
+ }
+
+ QColor textColor = dis ? menuitem->palette.text().color() :
+ act ? menuitem->palette.highlightedText().color() : menuitem->palette.buttonText().color();
+ p->setPen(textColor);
+
+ // draw text ------------------------------------------------------
+ int xm = windowsItemFrame + checkcol + windowsItemHMargin;
+ xpos = menuitem->rect.x() + xm;
+ QRect textRect(xpos, y + windowsItemVMargin, w - xm - windowsRightBorder - tab + 1, h - 2 * windowsItemVMargin);
+ QRect vTextRect = visualRect(option->direction, option->rect, textRect);
+ QString s = menuitem->text;
+ if (!s.isEmpty()) {
+ p->save();
+ int t = s.indexOf(QLatin1Char('\t'));
+ int text_flags = Qt::AlignVCenter|Qt::TextShowMnemonic | Qt::TextDontClip | Qt::TextSingleLine | Qt::AlignLeft;
+ if (!styleHint(SH_UnderlineShortcut, menuitem, widget))
+ text_flags |= Qt::TextHideMnemonic;
+ // draw tab text ----------------
+ if (t >= 0) {
+ QRect vShortcutRect = visualRect(option->direction, option->rect, QRect(textRect.topRight(), menuitem->rect.bottomRight()));
+ if (dis && !act && styleHint(SH_EtchDisabledText, option, widget)) {
+ p->setPen(menuitem->palette.light().color());
+ p->drawText(vShortcutRect.adjusted(1,1,1,1), text_flags, s.mid(t + 1));
+ p->setPen(textColor);
+ }
+ p->drawText(vShortcutRect, text_flags, s.mid(t + 1));
+ s = s.left(t);
+ }
+ QFont font = menuitem->font;
+ if (menuitem->menuItemType == QStyleOptionMenuItem::DefaultItem)
+ font.setBold(true);
+ p->setFont(font);
+ if (dis && !act && styleHint(SH_EtchDisabledText, option, widget)) {
+ p->setPen(menuitem->palette.light().color());
+ p->drawText(vTextRect.adjusted(1,1,1,1), text_flags, s.left(t));
+ p->setPen(textColor);
+ }
+ p->drawText(vTextRect, text_flags, s);
+ p->restore();
+ }
+
+ // draw sub menu arrow --------------------------------------------
+ if (menuitem->menuItemType == QStyleOptionMenuItem::SubMenu) {
+ int dim = (h - 2) / 2;
+ PrimitiveElement arrow;
+ arrow = (option->direction == Qt::RightToLeft) ? PE_IndicatorArrowLeft : PE_IndicatorArrowRight;
+ xpos = x + w - windowsArrowHMargin - windowsItemFrame - dim;
+ QRect vSubMenuRect = visualRect(option->direction, option->rect, QRect(xpos, y + h / 2 - dim / 2, dim, dim));
+ QStyleOptionMenuItem newMI = *menuitem;
+ newMI.rect = vSubMenuRect;
+ newMI.state = dis ? State_None : State_Enabled;
+ if (act)
+ newMI.palette.setColor(QPalette::ButtonText, newMI.palette.highlightedText().color());
+ drawPrimitive(arrow, &newMI, p, widget);
+ }
+ }
+ return;
+
+ case CE_MenuBarItem:
+ if (const QStyleOptionMenuItem *mbi = qstyleoption_cast<const QStyleOptionMenuItem *>(option))
+ {
+ if (mbi->menuItemType == QStyleOptionMenuItem::DefaultItem)
+ break;
+
+ bool act = mbi->state & State_Selected;
+ bool dis = !(mbi->state & State_Enabled);
+
+ QBrush fill = mbi->palette.brush(act ? QPalette::Highlight : QPalette::Button);
+ QPalette::ColorRole textRole = dis ? QPalette::Text:
+ act ? QPalette::HighlightedText : QPalette::ButtonText;
+ QPixmap pix = mbi->icon.pixmap(pixelMetric(PM_SmallIconSize, option, widget), QIcon::Normal);
+
+ uint alignment = Qt::AlignCenter | Qt::TextShowMnemonic | Qt::TextDontClip | Qt::TextSingleLine;
+ if (!styleHint(SH_UnderlineShortcut, mbi, widget))
+ alignment |= Qt::TextHideMnemonic;
+
+ p->fillRect(rect, fill);
+ if (!pix.isNull())
+ drawItemPixmap(p, mbi->rect, alignment, pix);
+ else
+ drawItemText(p, mbi->rect, alignment, mbi->palette, mbi->state & State_Enabled, mbi->text, textRole);
+ }
+ return;
+#ifndef QT_NO_DOCKWIDGET
+ case CE_DockWidgetTitle:
+ if (const QStyleOptionDockWidget *dwOpt = qstyleoption_cast<const QStyleOptionDockWidget *>(option))
+ {
+ int buttonMargin = 4;
+ int mw = pixelMetric(QStyle::PM_DockWidgetTitleMargin, dwOpt, widget);
+ int fw = pixelMetric(PM_DockWidgetFrameWidth, dwOpt, widget);
+ bool isFloating = widget && widget->isWindow();
+ bool isActive = dwOpt->state & State_Active;
+
+ const QStyleOptionDockWidgetV2 *v2
+ = qstyleoption_cast<const QStyleOptionDockWidgetV2*>(dwOpt);
+ bool verticalTitleBar = v2 == 0 ? false : v2->verticalTitleBar;
+
+ if (verticalTitleBar) {
+ QSize s = rect.size();
+ s.transpose();
+ rect.setSize(s);
+
+ p->translate(rect.left() - 1, rect.top() + rect.width());
+ p->rotate(-90);
+ p->translate(-rect.left() + 1, -rect.top());
+ }
+ QRect r = rect.adjusted(0, 2, -1, -3);
+ QRect titleRect = r;
+
+ if (dwOpt->closable) {
+ QSize sz = standardIcon(QStyle::SP_TitleBarCloseButton, dwOpt, widget).actualSize(QSize(10, 10));
+ titleRect.adjust(0, 0, -sz.width() - mw - buttonMargin, 0);
+ }
+
+ if (dwOpt->floatable) {
+ QSize sz = standardIcon(QStyle::SP_TitleBarMaxButton, dwOpt, widget).actualSize(QSize(10, 10));
+ titleRect.adjust(0, 0, -sz.width() - mw - buttonMargin, 0);
+ }
+
+ if (isFloating) {
+ titleRect.adjust(0, -fw, 0, 0);
+ if (widget != 0 && widget->windowIcon().cacheKey() != qApp->windowIcon().cacheKey())
+ titleRect.adjust(titleRect.height() + mw, 0, 0, 0);
+ } else {
+ titleRect.adjust(mw, 0, 0, 0);
+ if (!dwOpt->floatable && !dwOpt->closable)
+ titleRect.adjust(0, 0, -mw, 0);
+ }
+
+ if (!verticalTitleBar)
+ titleRect = visualRect(dwOpt->direction, r, titleRect);
+
+ if (!isFloating) {
+ QPen oldPen = p->pen();
+ QString titleText = p->fontMetrics().elidedText(dwOpt->title, Qt::ElideRight, titleRect.width());
+ p->setPen(dwOpt->palette.color(QPalette::Dark));
+ p->drawRect(r);
+
+ if (!titleText.isEmpty()) {
+ drawItemText(p, titleRect,
+ Qt::AlignLeft | Qt::AlignVCenter | Qt::TextShowMnemonic, dwOpt->palette,
+ dwOpt->state & State_Enabled, titleText,
+ QPalette::WindowText);
+ }
+
+ p->setPen(oldPen);
+ } else {
+ name = QLatin1String("WINDOW");
+ if (isActive)
+ stateId = CS_ACTIVE;
+ else
+ stateId = CS_INACTIVE;
+
+ int titleHeight = rect.height() - 2;
+ rect = rect.adjusted(-fw, -fw, fw, 0);
+
+ XPThemeData theme(widget, p, name, 0, stateId);
+ if (!theme.isValid())
+ break;
+
+ // Draw small type title bar
+ theme.rect = rect;
+ theme.partId = WP_SMALLCAPTION;
+ d->drawBackground(theme);
+
+ // Figure out maximal button space on title bar
+
+ QIcon ico = widget->windowIcon();
+ bool hasIcon = (ico.cacheKey() != qApp->windowIcon().cacheKey());
+ if (hasIcon) {
+ QPixmap pxIco = ico.pixmap(titleHeight);
+ if (!verticalTitleBar && QApplication::layoutDirection() == Qt::RightToLeft)
+ p->drawPixmap(rect.width() - titleHeight - pxIco.width(), rect.bottom() - titleHeight - 2, pxIco);
+ else
+ p->drawPixmap(fw, rect.bottom() - titleHeight - 2, pxIco);
+ }
+ if (!dwOpt->title.isEmpty()) {
+ QPen oldPen = p->pen();
+ QFont oldFont = p->font();
+ QFont titleFont = oldFont;
+ titleFont.setBold(true);
+ p->setFont(titleFont);
+ QString titleText
+ = p->fontMetrics().elidedText(dwOpt->title, Qt::ElideRight, titleRect.width());
+
+ int result = TST_NONE;
+ pGetThemeEnumValue(theme.handle(), WP_SMALLCAPTION, isActive ? CS_ACTIVE : CS_INACTIVE, TMT_TEXTSHADOWTYPE, &result);
+ if (result != TST_NONE) {
+ COLORREF textShadowRef;
+ pGetThemeColor(theme.handle(), WP_SMALLCAPTION, isActive ? CS_ACTIVE : CS_INACTIVE, TMT_TEXTSHADOWCOLOR, &textShadowRef);
+ QColor textShadow = qRgb(GetRValue(textShadowRef), GetGValue(textShadowRef), GetBValue(textShadowRef));
+ p->setPen(textShadow);
+ drawItemText(p, titleRect.adjusted(1, 1, 1, 1),
+ Qt::AlignLeft | Qt::AlignBottom, dwOpt->palette,
+ dwOpt->state & State_Enabled, titleText);
+ }
+
+ COLORREF captionText = GetSysColor(isActive ? COLOR_CAPTIONTEXT : COLOR_INACTIVECAPTIONTEXT);
+ QColor textColor = qRgb(GetRValue(captionText), GetGValue(captionText), GetBValue(captionText));
+ p->setPen(textColor);
+ drawItemText(p, titleRect,
+ Qt::AlignLeft | Qt::AlignBottom, dwOpt->palette,
+ dwOpt->state & State_Enabled, titleText);
+ p->setFont(oldFont);
+ p->setPen(oldPen);
+ }
+
+ }
+
+ return;
+ }
+ break;
+#endif // QT_NO_DOCKWIDGET
+#ifndef QT_NO_RUBBERBAND
+ case CE_RubberBand:
+ if (qstyleoption_cast<const QStyleOptionRubberBand *>(option)) {
+ QColor highlight = option->palette.color(QPalette::Active, QPalette::Highlight);
+ p->save();
+ QRect r = option->rect;
+ p->setPen(highlight.darker(120));
+ QColor dimHighlight(qMin(highlight.red()/2 + 110, 255),
+ qMin(highlight.green()/2 + 110, 255),
+ qMin(highlight.blue()/2 + 110, 255),
+ (widget && widget->isTopLevel())? 255 : 127);
+ p->setBrush(dimHighlight);
+ p->drawRect(option->rect.adjusted(0, 0, -1, -1));
+ p->restore();
+ return;
+ }
+#endif // QT_NO_RUBBERBAND
+ case CE_HeaderEmptyArea:
+ if (option->state & State_Horizontal)
+ {
+ name = QLatin1String("HEADER");
+ stateId = HIS_NORMAL;
+ }
+ else {
+ QWindowsStyle::drawControl(CE_HeaderEmptyArea, option, p, widget);
+ return;
+ }
+ break;
+ default:
+ break;
+ }
+
+ XPThemeData theme(widget, p, name, partId, stateId, rect);
+ if (!theme.isValid()) {
+ QWindowsStyle::drawControl(element, option, p, widget);
+ return;
+ }
+
+ theme.rotate = rotate;
+ theme.mirrorHorizontally = hMirrored;
+ theme.mirrorVertically = vMirrored;
+ d->drawBackground(theme);
+}
+
+
+/*!
+ \reimp
+*/
+void QWindowsXPStyle::drawComplexControl(ComplexControl cc, const QStyleOptionComplex *option,
+ QPainter *p, const QWidget *widget) const
+{
+ QWindowsXPStylePrivate *d = const_cast<QWindowsXPStylePrivate*>(d_func());
+
+ if (!QWindowsXPStylePrivate::useXP()) {
+ QWindowsStyle::drawComplexControl(cc, option, p, widget);
+ return;
+ }
+
+ State flags = option->state;
+ SubControls sub = option->subControls;
+ QRect r = option->rect;
+
+ int partId = 0;
+ int stateId = 0;
+ if (widget && widget->testAttribute(Qt::WA_UnderMouse) && widget->isActiveWindow())
+ flags |= State_MouseOver;
+
+ switch (cc) {
+#ifndef QT_NO_SPINBOX
+ case CC_SpinBox:
+ if (const QStyleOptionSpinBox *sb = qstyleoption_cast<const QStyleOptionSpinBox *>(option))
+ {
+ XPThemeData theme(widget, p, QLatin1String("SPIN"));
+
+ if (sb->frame && (sub & SC_SpinBoxFrame)) {
+ partId = EP_EDITTEXT;
+ if (!(flags & State_Enabled))
+ stateId = ETS_DISABLED;
+ else if (flags & State_HasFocus)
+ stateId = ETS_FOCUSED;
+ else
+ stateId = ETS_NORMAL;
+
+ XPThemeData ftheme(widget, p, QLatin1String("EDIT"), partId, stateId, r);
+ ftheme.noContent = true;
+ d->drawBackground(ftheme);
+ }
+ if (sub & SC_SpinBoxUp) {
+ theme.rect = subControlRect(CC_SpinBox, option, SC_SpinBoxUp, widget);
+ partId = SPNP_UP;
+ if (!(sb->stepEnabled & QAbstractSpinBox::StepUpEnabled) || !(flags & State_Enabled))
+ stateId = UPS_DISABLED;
+ else if (sb->activeSubControls == SC_SpinBoxUp && (sb->state & State_Sunken))
+ stateId = UPS_PRESSED;
+ else if (sb->activeSubControls == SC_SpinBoxUp && (sb->state & State_MouseOver))
+ stateId = UPS_HOT;
+ else
+ stateId = UPS_NORMAL;
+ theme.partId = partId;
+ theme.stateId = stateId;
+ d->drawBackground(theme);
+ }
+ if (sub & SC_SpinBoxDown) {
+ theme.rect = subControlRect(CC_SpinBox, option, SC_SpinBoxDown, widget);
+ partId = SPNP_DOWN;
+ if (!(sb->stepEnabled & QAbstractSpinBox::StepDownEnabled) || !(flags & State_Enabled))
+ stateId = DNS_DISABLED;
+ else if (sb->activeSubControls == SC_SpinBoxDown && (sb->state & State_Sunken))
+ stateId = DNS_PRESSED;
+ else if (sb->activeSubControls == SC_SpinBoxDown && (sb->state & State_MouseOver))
+ stateId = DNS_HOT;
+ else
+ stateId = DNS_NORMAL;
+ theme.partId = partId;
+ theme.stateId = stateId;
+ d->drawBackground(theme);
+ }
+ }
+ break;
+#endif // QT_NO_SPINBOX
+#ifndef QT_NO_COMBOBOX
+ case CC_ComboBox:
+ if (const QStyleOptionComboBox *cmb = qstyleoption_cast<const QStyleOptionComboBox *>(option))
+ {
+ if (sub & SC_ComboBoxEditField) {
+ if (cmb->frame) {
+ partId = EP_EDITTEXT;
+ if (!(flags & State_Enabled))
+ stateId = ETS_DISABLED;
+ else if (flags & State_HasFocus)
+ stateId = ETS_FOCUSED;
+ else
+ stateId = ETS_NORMAL;
+ XPThemeData theme(widget, p, QLatin1String("EDIT"), partId, stateId, r);
+ d->drawBackground(theme);
+ } else {
+ QBrush editBrush = cmb->palette.brush(QPalette::Base);
+ p->fillRect(option->rect, editBrush);
+ }
+ if (!cmb->editable) {
+ QRect re = subControlRect(CC_ComboBox, option, SC_ComboBoxEditField, widget);
+ if (option->state & State_HasFocus) {
+ p->fillRect(re, option->palette.highlight());
+ p->setPen(option->palette.highlightedText().color());
+ p->setBackground(option->palette.highlight());
+ } else {
+ p->fillRect(re, option->palette.base());
+ p->setPen(option->palette.text().color());
+ p->setBackground(option->palette.base());
+ }
+ }
+ }
+
+ if (sub & SC_ComboBoxArrow) {
+ XPThemeData theme(widget, p, QLatin1String("COMBOBOX"));
+ theme.rect = subControlRect(CC_ComboBox, option, SC_ComboBoxArrow, widget);
+ partId = CP_DROPDOWNBUTTON;
+ if (!(flags & State_Enabled))
+ stateId = CBXS_DISABLED;
+ else if (cmb->activeSubControls == SC_ComboBoxArrow && (cmb->state & State_Sunken))
+ stateId = CBXS_PRESSED;
+ else if (cmb->activeSubControls == SC_ComboBoxArrow && (cmb->state & State_MouseOver))
+ stateId = CBXS_HOT;
+ else
+ stateId = CBXS_NORMAL;
+ theme.partId = partId;
+ theme.stateId = stateId;
+ d->drawBackground(theme);
+ }
+ }
+ break;
+#endif // QT_NO_COMBOBOX
+ case CC_ScrollBar:
+ if (const QStyleOptionSlider *scrollbar = qstyleoption_cast<const QStyleOptionSlider *>(option))
+ {
+ XPThemeData theme(widget, p, QLatin1String("SCROLLBAR"));
+ bool maxedOut = (scrollbar->maximum == scrollbar->minimum);
+ if (maxedOut)
+ flags &= ~State_Enabled;
+
+ bool isHorz = flags & State_Horizontal;
+ bool isRTL = option->direction == Qt::RightToLeft;
+ if (sub & SC_ScrollBarAddLine) {
+ theme.rect = subControlRect(CC_ScrollBar, option, SC_ScrollBarAddLine, widget);
+ partId = SBP_ARROWBTN;
+ if (!(flags & State_Enabled))
+ stateId = (isHorz ? (isRTL ? ABS_LEFTDISABLED : ABS_RIGHTDISABLED) : ABS_DOWNDISABLED);
+ else if (scrollbar->activeSubControls & SC_ScrollBarAddLine && (scrollbar->state & State_Sunken))
+ stateId = (isHorz ? (isRTL ? ABS_LEFTPRESSED : ABS_RIGHTPRESSED) : ABS_DOWNPRESSED);
+ else if (scrollbar->activeSubControls & SC_ScrollBarAddLine && (scrollbar->state & State_MouseOver))
+ stateId = (isHorz ? (isRTL ? ABS_LEFTHOT : ABS_RIGHTHOT) : ABS_DOWNHOT);
+ else
+ stateId = (isHorz ? (isRTL ? ABS_LEFTNORMAL : ABS_RIGHTNORMAL) : ABS_DOWNNORMAL);
+ theme.partId = partId;
+ theme.stateId = stateId;
+ d->drawBackground(theme);
+ }
+ if (sub & SC_ScrollBarSubLine) {
+ theme.rect = subControlRect(CC_ScrollBar, option, SC_ScrollBarSubLine, widget);
+ partId = SBP_ARROWBTN;
+ if (!(flags & State_Enabled))
+ stateId = (isHorz ? (isRTL ? ABS_RIGHTDISABLED : ABS_LEFTDISABLED) : ABS_UPDISABLED);
+ else if (scrollbar->activeSubControls & SC_ScrollBarSubLine && (scrollbar->state & State_Sunken))
+ stateId = (isHorz ? (isRTL ? ABS_RIGHTPRESSED : ABS_LEFTPRESSED) : ABS_UPPRESSED);
+ else if (scrollbar->activeSubControls & SC_ScrollBarSubLine && (scrollbar->state & State_MouseOver))
+ stateId = (isHorz ? (isRTL ? ABS_RIGHTHOT : ABS_LEFTHOT) : ABS_UPHOT);
+ else
+ stateId = (isHorz ? (isRTL ? ABS_RIGHTNORMAL : ABS_LEFTNORMAL) : ABS_UPNORMAL);
+ theme.partId = partId;
+ theme.stateId = stateId;
+ d->drawBackground(theme);
+ }
+ if (maxedOut) {
+ theme.rect = subControlRect(CC_ScrollBar, option, SC_ScrollBarSlider, widget);
+ theme.rect = theme.rect.united(subControlRect(CC_ScrollBar, option, SC_ScrollBarSubPage, widget));
+ theme.rect = theme.rect.united(subControlRect(CC_ScrollBar, option, SC_ScrollBarAddPage, widget));
+ partId = scrollbar->orientation == Qt::Horizontal ? SBP_LOWERTRACKHORZ : SBP_LOWERTRACKVERT;
+ stateId = SCRBS_DISABLED;
+ theme.partId = partId;
+ theme.stateId = stateId;
+ d->drawBackground(theme);
+ } else {
+ if (sub & SC_ScrollBarSubPage) {
+ theme.rect = subControlRect(CC_ScrollBar, option, SC_ScrollBarSubPage, widget);
+ partId = flags & State_Horizontal ? SBP_UPPERTRACKHORZ : SBP_UPPERTRACKVERT;
+ if (!(flags & State_Enabled))
+ stateId = SCRBS_DISABLED;
+ else if (scrollbar->activeSubControls & SC_ScrollBarSubPage && (scrollbar->state & State_Sunken))
+ stateId = SCRBS_PRESSED;
+ else if (scrollbar->activeSubControls & SC_ScrollBarSubPage && (scrollbar->state & State_MouseOver))
+ stateId = SCRBS_HOT;
+ else
+ stateId = SCRBS_NORMAL;
+ theme.partId = partId;
+ theme.stateId = stateId;
+ d->drawBackground(theme);
+ }
+ if (sub & SC_ScrollBarAddPage) {
+ theme.rect = subControlRect(CC_ScrollBar, option, SC_ScrollBarAddPage, widget);
+ partId = flags & State_Horizontal ? SBP_LOWERTRACKHORZ : SBP_LOWERTRACKVERT;
+ if (!(flags & State_Enabled))
+ stateId = SCRBS_DISABLED;
+ else if (scrollbar->activeSubControls & SC_ScrollBarAddPage && (scrollbar->state & State_Sunken))
+ stateId = SCRBS_PRESSED;
+ else if (scrollbar->activeSubControls & SC_ScrollBarAddPage && (scrollbar->state & State_MouseOver))
+ stateId = SCRBS_HOT;
+ else
+ stateId = SCRBS_NORMAL;
+ theme.partId = partId;
+ theme.stateId = stateId;
+ d->drawBackground(theme);
+ }
+ if (sub & SC_ScrollBarSlider) {
+ theme.rect = subControlRect(CC_ScrollBar, option, SC_ScrollBarSlider, widget);
+ if (!(flags & State_Enabled))
+ stateId = SCRBS_DISABLED;
+ else if (scrollbar->activeSubControls & SC_ScrollBarSlider && (scrollbar->state & State_Sunken))
+ stateId = SCRBS_PRESSED;
+ else if (scrollbar->activeSubControls & SC_ScrollBarSlider && (scrollbar->state & State_MouseOver))
+ stateId = SCRBS_HOT;
+ else
+ stateId = SCRBS_NORMAL;
+
+ // Draw handle
+ theme.rect = subControlRect(CC_ScrollBar, option, SC_ScrollBarSlider, widget);
+ theme.partId = flags & State_Horizontal ? SBP_THUMBBTNHORZ : SBP_THUMBBTNVERT;
+ theme.stateId = stateId;
+ d->drawBackground(theme);
+
+ // Calculate rect of gripper
+ const int swidth = theme.rect.width();
+ const int sheight = theme.rect.height();
+
+ MARGINS contentsMargin;
+ RECT rect = theme.toRECT(theme.rect);
+ pGetThemeMargins(theme.handle(), 0, theme.partId, theme.stateId, TMT_SIZINGMARGINS, &rect, &contentsMargin);
+
+ SIZE size;
+ theme.partId = flags & State_Horizontal ? SBP_GRIPPERHORZ : SBP_GRIPPERVERT;
+ pGetThemePartSize(theme.handle(), 0, theme.partId, theme.stateId, 0, TS_TRUE, &size);
+ int gw = size.cx, gh = size.cy;
+
+
+ QRect gripperBounds;
+ if (flags & State_Horizontal && ((swidth - contentsMargin.cxLeftWidth - contentsMargin.cxRightWidth) > gw)) {
+ gripperBounds.setLeft(theme.rect.left() + swidth/2 - gw/2);
+ gripperBounds.setTop(theme.rect.top() + sheight/2 - gh/2);
+ gripperBounds.setWidth(gw);
+ gripperBounds.setHeight(gh);
+ } else if ((sheight - contentsMargin.cyTopHeight - contentsMargin.cyBottomHeight) > gh) {
+ gripperBounds.setLeft(theme.rect.left() + swidth/2 - gw/2);
+ gripperBounds.setTop(theme.rect.top() + sheight/2 - gh/2);
+ gripperBounds.setWidth(gw);
+ gripperBounds.setHeight(gh);
+ }
+
+ // Draw gripper if there is enough space
+ if (!gripperBounds.isEmpty()) {
+ p->save();
+ XPThemeData grippBackground = theme;
+ grippBackground.partId = flags & State_Horizontal ? SBP_LOWERTRACKHORZ : SBP_LOWERTRACKVERT;
+ theme.rect = gripperBounds;
+ p->setClipRegion(d->region(theme));// Only change inside the region of the gripper
+ d->drawBackground(grippBackground);// The gutter is the grippers background
+ d->drawBackground(theme); // Transparent gripper ontop of background
+ p->restore();
+ }
+ }
+ }
+ }
+ break;
+
+#ifndef QT_NO_SLIDER
+ case CC_Slider:
+ if (const QStyleOptionSlider *slider = qstyleoption_cast<const QStyleOptionSlider *>(option))
+ {
+ XPThemeData theme(widget, p, QLatin1String("TRACKBAR"));
+ QRect slrect = slider->rect;
+ QRegion tickreg = slrect;
+ if (sub & SC_SliderGroove) {
+ theme.rect = subControlRect(CC_Slider, option, SC_SliderGroove, widget);
+ if (slider->orientation == Qt::Horizontal) {
+ partId = TKP_TRACK;
+ stateId = TRS_NORMAL;
+ theme.rect = QRect(slrect.left(), theme.rect.center().y() - 2, slrect.width(), 4);
+ } else {
+ partId = TKP_TRACKVERT;
+ stateId = TRVS_NORMAL;
+ theme.rect = QRect(theme.rect.center().x() - 2, slrect.top(), 4, slrect.height());
+ }
+ theme.partId = partId;
+ theme.stateId = stateId;
+ d->drawBackground(theme);
+ tickreg -= theme.rect;
+ }
+ if (sub & SC_SliderTickmarks) {
+ int tickOffset = pixelMetric(PM_SliderTickmarkOffset, slider, widget);
+ int ticks = slider->tickPosition;
+ int thickness = pixelMetric(PM_SliderControlThickness, slider, widget);
+ int len = pixelMetric(PM_SliderLength, slider, widget);
+ int available = pixelMetric(PM_SliderSpaceAvailable, slider, widget);
+ int interval = slider->tickInterval;
+ if (interval <= 0) {
+ interval = slider->singleStep;
+ if (QStyle::sliderPositionFromValue(slider->minimum, slider->maximum, interval,
+ available)
+ - QStyle::sliderPositionFromValue(slider->minimum, slider->maximum,
+ 0, available) < 3)
+ interval = slider->pageStep;
+ }
+ if (!interval)
+ interval = 1;
+ int fudge = len / 2;
+ int pos;
+ int bothOffset = (ticks & QSlider::TicksAbove && ticks & QSlider::TicksBelow) ? 1 : 0;
+ p->setPen(d->sliderTickColor);
+ QVarLengthArray<QLine, 32> lines;
+ int v = slider->minimum;
+ while (v <= slider->maximum + 1) {
+ if (v == slider->maximum + 1 && interval == 1)
+ break;
+ const int v_ = qMin(v, slider->maximum);
+ int tickLength = (v_ == slider->minimum || v_ >= slider->maximum) ? 4 : 3;
+ pos = QStyle::sliderPositionFromValue(slider->minimum, slider->maximum,
+ v_, available) + fudge;
+ if (slider->orientation == Qt::Horizontal) {
+ if (ticks & QSlider::TicksAbove)
+ lines.append(QLine(pos, tickOffset - 1 - bothOffset,
+ pos, tickOffset - 1 - bothOffset - tickLength));
+
+ if (ticks & QSlider::TicksBelow)
+ lines.append(QLine(pos, tickOffset + thickness + bothOffset,
+ pos, tickOffset + thickness + bothOffset + tickLength));
+ } else {
+ if (ticks & QSlider::TicksAbove)
+ lines.append(QLine(tickOffset - 1 - bothOffset, pos,
+ tickOffset - 1 - bothOffset - tickLength, pos));
+
+ if (ticks & QSlider::TicksBelow)
+ lines.append(QLine(tickOffset + thickness + bothOffset, pos,
+ tickOffset + thickness + bothOffset + tickLength, pos));
+ }
+ // in the case where maximum is max int
+ int nextInterval = v + interval;
+ if (nextInterval < v)
+ break;
+ v = nextInterval;
+ }
+ if (lines.size() > 0) {
+ p->save();
+ p->translate(slrect.topLeft());
+ p->drawLines(lines.constData(), lines.size());
+ p->restore();
+ }
+ }
+ if (sub & SC_SliderHandle) {
+ theme.rect = subControlRect(CC_Slider, option, SC_SliderHandle, widget);
+ if (slider->orientation == Qt::Horizontal) {
+ if (slider->tickPosition == QSlider::TicksAbove)
+ partId = TKP_THUMBTOP;
+ else if (slider->tickPosition == QSlider::TicksBelow)
+ partId = TKP_THUMBBOTTOM;
+ else
+ partId = TKP_THUMB;
+
+ if (!(slider->state & State_Enabled))
+ stateId = TUS_DISABLED;
+ else if (slider->activeSubControls & SC_SliderHandle && (slider->state & State_Sunken))
+ stateId = TUS_PRESSED;
+ else if (slider->activeSubControls & SC_SliderHandle && (slider->state & State_MouseOver))
+ stateId = TUS_HOT;
+ else if (flags & State_HasFocus)
+ stateId = TUS_FOCUSED;
+ else
+ stateId = TUS_NORMAL;
+ } else {
+ if (slider->tickPosition == QSlider::TicksLeft)
+ partId = TKP_THUMBLEFT;
+ else if (slider->tickPosition == QSlider::TicksRight)
+ partId = TKP_THUMBRIGHT;
+ else
+ partId = TKP_THUMBVERT;
+
+ if (!(slider->state & State_Enabled))
+ stateId = TUVS_DISABLED;
+ else if (slider->activeSubControls & SC_SliderHandle && (slider->state & State_Sunken))
+ stateId = TUVS_PRESSED;
+ else if (slider->activeSubControls & SC_SliderHandle && (slider->state & State_MouseOver))
+ stateId = TUVS_HOT;
+ else if (flags & State_HasFocus)
+ stateId = TUVS_FOCUSED;
+ else
+ stateId = TUVS_NORMAL;
+ }
+ theme.partId = partId;
+ theme.stateId = stateId;
+ d->drawBackground(theme);
+ }
+ if (slider->state & State_HasFocus) {
+ QStyleOptionFocusRect fropt;
+ fropt.QStyleOption::operator=(*slider);
+ fropt.rect = subElementRect(SE_SliderFocusRect, slider, widget);
+ drawPrimitive(PE_FrameFocusRect, &fropt, p, widget);
+ }
+ }
+ break;
+#endif
+#ifndef QT_NO_TOOLBUTTON
+ case CC_ToolButton:
+ if (const QStyleOptionToolButton *toolbutton
+ = qstyleoption_cast<const QStyleOptionToolButton *>(option)) {
+ QRect button, menuarea;
+ button = subControlRect(cc, toolbutton, SC_ToolButton, widget);
+ menuarea = subControlRect(cc, toolbutton, SC_ToolButtonMenu, widget);
+
+ State bflags = toolbutton->state & ~State_Sunken;
+ State mflags = bflags;
+
+ if (bflags & State_AutoRaise) {
+ if (!(bflags & State_MouseOver) || !(bflags & State_Enabled)) {
+ bflags &= ~State_Raised;
+ }
+ }
+
+ if (toolbutton->state & State_Sunken) {
+ if (toolbutton->activeSubControls & SC_ToolButton) {
+ bflags |= State_Sunken;
+ mflags |= State_MouseOver | State_Sunken;
+ } else if (toolbutton->activeSubControls & SC_ToolButtonMenu) {
+ mflags |= State_Sunken;
+ bflags |= State_MouseOver;
+ }
+ }
+
+ QStyleOption tool(0);
+ tool.palette = toolbutton->palette;
+ if (toolbutton->subControls & SC_ToolButton) {
+ if (flags & (State_Sunken | State_On | State_Raised) || !(flags & State_AutoRaise)) {
+ if (toolbutton->features & QStyleOptionToolButton::MenuButtonPopup) {
+ XPThemeData theme(widget, p, QLatin1String("TOOLBAR"));
+ theme.partId = TP_SPLITBUTTON;
+ theme.rect = button;
+ if (!(bflags & State_Enabled))
+ stateId = TS_DISABLED;
+ else if (bflags & State_Sunken)
+ stateId = TS_PRESSED;
+ else if (bflags & State_MouseOver || !(flags & State_AutoRaise))
+ stateId = flags & State_On ? TS_HOTCHECKED : TS_HOT;
+ else if (bflags & State_On)
+ stateId = TS_CHECKED;
+ else
+ stateId = TS_NORMAL;
+ if (option->direction == Qt::RightToLeft)
+ theme.mirrorHorizontally = true;
+ theme.stateId = stateId;
+ d->drawBackground(theme);
+ } else {
+ tool.rect = button;
+ tool.state = bflags;
+ if (widget && !qobject_cast<QToolBar*>(widget->parentWidget())
+ && !(bflags & State_AutoRaise))
+ drawPrimitive(PE_PanelButtonBevel, &tool, p, widget);
+ else
+ drawPrimitive(PE_PanelButtonTool, &tool, p, widget);
+ }
+ }
+ }
+
+ if (toolbutton->state & State_HasFocus) {
+ QStyleOptionFocusRect fr;
+ fr.QStyleOption::operator=(*toolbutton);
+ fr.rect.adjust(3, 3, -3, -3);
+ if (toolbutton->features & QStyleOptionToolButton::MenuButtonPopup)
+ fr.rect.adjust(0, 0, -pixelMetric(QStyle::PM_MenuButtonIndicator,
+ toolbutton, widget), 0);
+ drawPrimitive(PE_FrameFocusRect, &fr, p, widget);
+ }
+ QStyleOptionToolButton label = *toolbutton;
+ label.state = bflags;
+ int fw = 2;
+ label.rect = button.adjusted(fw, fw, -fw, -fw);
+ drawControl(CE_ToolButtonLabel, &label, p, widget);
+
+ if (toolbutton->subControls & SC_ToolButtonMenu) {
+ tool.rect = menuarea;
+ tool.state = mflags;
+ drawPrimitive(PE_IndicatorButtonDropDown, &tool, p, widget);
+ } else if (toolbutton->features & QStyleOptionToolButton::HasMenu) {
+ int mbi = pixelMetric(PM_MenuButtonIndicator, toolbutton, widget);
+ QRect ir = toolbutton->rect;
+ QStyleOptionToolButton newBtn = *toolbutton;
+ newBtn.rect = QRect(ir.right() + 4 - mbi, ir.height() - mbi + 4, mbi - 5, mbi - 5);
+ drawPrimitive(PE_IndicatorArrowDown, &newBtn, p, widget);
+ }
+ }
+ break;
+#endif // QT_NO_TOOLBUTTON
+
+ case CC_TitleBar:
+ {
+ if (const QStyleOptionTitleBar *tb = qstyleoption_cast<const QStyleOptionTitleBar *>(option))
+ {
+ bool isActive = tb->titleBarState & QStyle::State_Active;
+ XPThemeData theme(widget, p, QLatin1String("WINDOW"));
+ if (sub & SC_TitleBarLabel) {
+
+#ifdef QT3_SUPPORT
+ if (widget && widget->inherits("Q3DockWindowTitleBar")) {
+ partId = WP_SMALLCAPTION;
+ } else
+#endif
+ partId = (tb->titleBarState & Qt::WindowMinimized) ? WP_MINCAPTION : WP_CAPTION;
+ theme.rect = option->rect;
+ if (widget && !widget->isEnabled())
+ stateId = CS_DISABLED;
+ else if (isActive)
+ stateId = CS_ACTIVE;
+ else
+ stateId = CS_INACTIVE;
+
+ theme.partId = partId;
+ theme.stateId = stateId;
+ d->drawBackground(theme);
+
+ QRect ir = subControlRect(CC_TitleBar, tb, SC_TitleBarLabel, widget);
+
+ int result = TST_NONE;
+ pGetThemeEnumValue(theme.handle(), WP_CAPTION, isActive ? CS_ACTIVE : CS_INACTIVE, TMT_TEXTSHADOWTYPE, &result);
+ if (result != TST_NONE) {
+ COLORREF textShadowRef;
+ pGetThemeColor(theme.handle(), WP_CAPTION, isActive ? CS_ACTIVE : CS_INACTIVE, TMT_TEXTSHADOWCOLOR, &textShadowRef);
+ QColor textShadow = qRgb(GetRValue(textShadowRef), GetGValue(textShadowRef), GetBValue(textShadowRef));
+ p->setPen(textShadow);
+ p->drawText(ir.x() + 3, ir.y() + 2, ir.width() - 1, ir.height(),
+ Qt::AlignLeft | Qt::AlignVCenter | Qt::TextSingleLine, tb->text);
+ }
+ COLORREF captionText = GetSysColor(isActive ? COLOR_CAPTIONTEXT : COLOR_INACTIVECAPTIONTEXT);
+ QColor textColor = qRgb(GetRValue(captionText), GetGValue(captionText), GetBValue(captionText));
+ p->setPen(textColor);
+ p->drawText(ir.x() + 2, ir.y() + 1, ir.width() - 2, ir.height(),
+ Qt::AlignLeft | Qt::AlignVCenter | Qt::TextSingleLine, tb->text);
+ }
+ if (sub & SC_TitleBarSysMenu && tb->titleBarFlags & Qt::WindowSystemMenuHint) {
+ theme.rect = subControlRect(CC_TitleBar, option, SC_TitleBarSysMenu, widget);
+ partId = WP_SYSBUTTON;
+ if ((widget && !widget->isEnabled()) || !isActive)
+ stateId = SBS_DISABLED;
+ else if (option->activeSubControls == SC_TitleBarSysMenu && (option->state & State_Sunken))
+ stateId = SBS_PUSHED;
+ else if (option->activeSubControls == SC_TitleBarSysMenu && (option->state & State_MouseOver))
+ stateId = SBS_HOT;
+ else
+ stateId = SBS_NORMAL;
+ if (!tb->icon.isNull()) {
+ tb->icon.paint(p, theme.rect);
+ } else {
+ theme.partId = partId;
+ theme.stateId = stateId;
+ SIZE sz;
+ pGetThemePartSize(theme.handle(), qt_win_display_dc(), theme.partId, theme.stateId, 0, TS_TRUE, &sz);
+ if (sz.cx == 0 || sz.cy == 0) {
+ int iconSize = pixelMetric(PM_SmallIconSize, tb, widget);
+ QPixmap pm = standardIcon(SP_TitleBarMenuButton, tb, widget).pixmap(iconSize, iconSize);
+ p->save();
+ drawItemPixmap(p, theme.rect, Qt::AlignCenter, pm);
+ p->restore();
+ } else {
+ d->drawBackground(theme);
+ }
+ }
+ }
+
+ if (sub & SC_TitleBarMinButton && tb->titleBarFlags & Qt::WindowMinimizeButtonHint
+ && !(tb->titleBarState & Qt::WindowMinimized)) {
+ theme.rect = subControlRect(CC_TitleBar, option, SC_TitleBarMinButton, widget);
+ partId = WP_MINBUTTON;
+ if (widget && !widget->isEnabled())
+ stateId = MINBS_DISABLED;
+ else if (option->activeSubControls == SC_TitleBarMinButton && (option->state & State_Sunken))
+ stateId = MINBS_PUSHED;
+ else if (option->activeSubControls == SC_TitleBarMinButton && (option->state & State_MouseOver))
+ stateId = MINBS_HOT;
+ else if (!isActive)
+ stateId = MINBS_INACTIVE;
+ else
+ stateId = MINBS_NORMAL;
+ theme.partId = partId;
+ theme.stateId = stateId;
+ d->drawBackground(theme);
+ }
+ if (sub & SC_TitleBarMaxButton && tb->titleBarFlags & Qt::WindowMaximizeButtonHint
+ && !(tb->titleBarState & Qt::WindowMaximized)) {
+ theme.rect = subControlRect(CC_TitleBar, option, SC_TitleBarMaxButton, widget);
+ partId = WP_MAXBUTTON;
+ if (widget && !widget->isEnabled())
+ stateId = MAXBS_DISABLED;
+ else if (option->activeSubControls == SC_TitleBarMaxButton && (option->state & State_Sunken))
+ stateId = MAXBS_PUSHED;
+ else if (option->activeSubControls == SC_TitleBarMaxButton && (option->state & State_MouseOver))
+ stateId = MAXBS_HOT;
+ else if (!isActive)
+ stateId = MAXBS_INACTIVE;
+ else
+ stateId = MAXBS_NORMAL;
+ theme.partId = partId;
+ theme.stateId = stateId;
+ d->drawBackground(theme);
+ }
+ if (sub & SC_TitleBarContextHelpButton
+ && tb->titleBarFlags & Qt::WindowContextHelpButtonHint) {
+ theme.rect = subControlRect(CC_TitleBar, option, SC_TitleBarContextHelpButton, widget);
+ partId = WP_HELPBUTTON;
+ if (widget && !widget->isEnabled())
+ stateId = MINBS_DISABLED;
+ else if (option->activeSubControls == SC_TitleBarContextHelpButton && (option->state & State_Sunken))
+ stateId = MINBS_PUSHED;
+ else if (option->activeSubControls == SC_TitleBarContextHelpButton && (option->state & State_MouseOver))
+ stateId = MINBS_HOT;
+ else if (!isActive)
+ stateId = MINBS_INACTIVE;
+ else
+ stateId = MINBS_NORMAL;
+ theme.partId = partId;
+ theme.stateId = stateId;
+ d->drawBackground(theme);
+ }
+ bool drawNormalButton = (sub & SC_TitleBarNormalButton)
+ && (((tb->titleBarFlags & Qt::WindowMinimizeButtonHint)
+ && (tb->titleBarState & Qt::WindowMinimized))
+ || ((tb->titleBarFlags & Qt::WindowMaximizeButtonHint)
+ && (tb->titleBarState & Qt::WindowMaximized)));
+ if (drawNormalButton) {
+ theme.rect = subControlRect(CC_TitleBar, option, SC_TitleBarNormalButton, widget);
+ partId = WP_RESTOREBUTTON;
+ if (widget && !widget->isEnabled())
+ stateId = RBS_DISABLED;
+ else if (option->activeSubControls == SC_TitleBarNormalButton && (option->state & State_Sunken))
+ stateId = RBS_PUSHED;
+ else if (option->activeSubControls == SC_TitleBarNormalButton && (option->state & State_MouseOver))
+ stateId = RBS_HOT;
+ else if (!isActive)
+ stateId = RBS_INACTIVE;
+ else
+ stateId = RBS_NORMAL;
+ theme.partId = partId;
+ theme.stateId = stateId;
+ d->drawBackground(theme);
+ }
+ if (sub & SC_TitleBarShadeButton && tb->titleBarFlags & Qt::WindowShadeButtonHint
+ && !(tb->titleBarState & Qt::WindowMinimized)) {
+ theme.rect = subControlRect(CC_TitleBar, option, SC_TitleBarShadeButton, widget);
+ partId = WP_MINBUTTON;
+ if (widget && !widget->isEnabled())
+ stateId = MINBS_DISABLED;
+ else if (option->activeSubControls == SC_TitleBarShadeButton && (option->state & State_Sunken))
+ stateId = MINBS_PUSHED;
+ else if (option->activeSubControls == SC_TitleBarShadeButton && (option->state & State_MouseOver))
+ stateId = MINBS_HOT;
+ else if (!isActive)
+ stateId = MINBS_INACTIVE;
+ else
+ stateId = MINBS_NORMAL;
+ theme.partId = partId;
+ theme.stateId = stateId;
+ d->drawBackground(theme);
+ }
+ if (sub & SC_TitleBarUnshadeButton && tb->titleBarFlags & Qt::WindowShadeButtonHint
+ && tb->titleBarState & Qt::WindowMinimized) {
+ theme.rect = subControlRect(CC_TitleBar, option, SC_TitleBarUnshadeButton, widget);
+ partId = WP_RESTOREBUTTON;
+ if (widget && !widget->isEnabled())
+ stateId = RBS_DISABLED;
+ else if (option->activeSubControls == SC_TitleBarUnshadeButton && (option->state & State_Sunken))
+ stateId = RBS_PUSHED;
+ else if (option->activeSubControls == SC_TitleBarUnshadeButton && (option->state & State_MouseOver))
+ stateId = RBS_HOT;
+ else if (!isActive)
+ stateId = RBS_INACTIVE;
+ else
+ stateId = RBS_NORMAL;
+ theme.partId = partId;
+ theme.stateId = stateId;
+ d->drawBackground(theme);
+ }
+ if (sub & SC_TitleBarCloseButton && tb->titleBarFlags & Qt::WindowSystemMenuHint) {
+ theme.rect = subControlRect(CC_TitleBar, option, SC_TitleBarCloseButton, widget);
+ //partId = titlebar->testWFlags(Qt::WA_WState_Tool) ? WP_SMALLCLOSEBUTTON : WP_CLOSEBUTTON;
+ partId = WP_CLOSEBUTTON;
+ if (widget && !widget->isEnabled())
+ stateId = CBS_DISABLED;
+ else if (option->activeSubControls == SC_TitleBarCloseButton && (option->state & State_Sunken))
+ stateId = CBS_PUSHED;
+ else if (option->activeSubControls == SC_TitleBarCloseButton && (option->state & State_MouseOver))
+ stateId = CBS_HOT;
+ else if (!isActive)
+ stateId = CBS_INACTIVE;
+ else
+ stateId = CBS_NORMAL;
+ theme.partId = partId;
+ theme.stateId = stateId;
+ d->drawBackground(theme);
+ }
+ }
+ }
+ break;
+
+#ifndef QT_NO_WORKSPACE
+ case CC_MdiControls:
+ {
+ QRect buttonRect;
+ XPThemeData theme(widget, p, QLatin1String("WINDOW"), WP_MDICLOSEBUTTON, CBS_NORMAL);
+
+ if (option->subControls & SC_MdiCloseButton) {
+ buttonRect = subControlRect(CC_MdiControls, option, SC_MdiCloseButton, widget);
+ if (theme.isValid()) {
+ theme.partId = WP_MDICLOSEBUTTON;
+ theme.rect = buttonRect;
+ if (!(flags & State_Enabled))
+ theme.stateId = CBS_INACTIVE;
+ else if (flags & State_Sunken && (option->activeSubControls & SC_MdiCloseButton))
+ theme.stateId = CBS_PUSHED;
+ else if (flags & State_MouseOver && (option->activeSubControls & SC_MdiCloseButton))
+ theme.stateId = CBS_HOT;
+ else
+ theme.stateId = CBS_NORMAL;
+ d->drawBackground(theme);
+ }
+ }
+ if (option->subControls & SC_MdiNormalButton) {
+ buttonRect = subControlRect(CC_MdiControls, option, SC_MdiNormalButton, widget);
+ if (theme.isValid()) {
+ theme.partId = WP_MDIRESTOREBUTTON;
+ theme.rect = buttonRect;
+ if (!(flags & State_Enabled))
+ theme.stateId = CBS_INACTIVE;
+ else if (flags & State_Sunken && (option->activeSubControls & SC_MdiNormalButton))
+ theme.stateId = CBS_PUSHED;
+ else if (flags & State_MouseOver && (option->activeSubControls & SC_MdiNormalButton))
+ theme.stateId = CBS_HOT;
+ else
+ theme.stateId = CBS_NORMAL;
+ d->drawBackground(theme);
+ }
+ }
+ if (option->subControls & QStyle::SC_MdiMinButton) {
+ buttonRect = subControlRect(CC_MdiControls, option, SC_MdiMinButton, widget);
+ if (theme.isValid()) {
+ theme.partId = WP_MDIMINBUTTON;
+ theme.rect = buttonRect;
+ if (!(flags & State_Enabled))
+ theme.stateId = CBS_INACTIVE;
+ else if (flags & State_Sunken && (option->activeSubControls & SC_MdiMinButton))
+ theme.stateId = CBS_PUSHED;
+ else if (flags & State_MouseOver && (option->activeSubControls & SC_MdiMinButton))
+ theme.stateId = CBS_HOT;
+ else
+ theme.stateId = CBS_NORMAL;
+ d->drawBackground(theme);
+ }
+ }
+ }
+ break;
+#endif //QT_NO_WORKSPACE
+ default:
+ QWindowsStyle::drawComplexControl(cc, option, p, widget);
+ break;
+ }
+}
+
+/*! \reimp */
+int QWindowsXPStyle::pixelMetric(PixelMetric pm, const QStyleOption *option, const QWidget *widget) const
+{
+ if (!QWindowsXPStylePrivate::useXP())
+ return QWindowsStyle::pixelMetric(pm, option, widget);
+
+ int res = 0;
+ switch (pm) {
+ case PM_MenuBarPanelWidth:
+ res = 0;
+ break;
+
+ case PM_DefaultFrameWidth:
+ if (qobject_cast<const QListView*>(widget))
+ res = 2;
+ else
+ res = 1;
+ break;
+ case PM_MenuPanelWidth:
+ case PM_SpinBoxFrameWidth:
+ res = 1;
+ break;
+
+ case PM_TabBarTabOverlap:
+ case PM_MenuHMargin:
+ case PM_MenuVMargin:
+ res = 2;
+ break;
+
+ case PM_TabBarBaseOverlap:
+ if (const QStyleOptionTab *tab = qstyleoption_cast<const QStyleOptionTab *>(option)) {
+ switch (tab->shape) {
+ case QTabBar::RoundedNorth:
+ case QTabBar::TriangularNorth:
+ res = 1;
+ break;
+ case QTabBar::RoundedSouth:
+ case QTabBar::TriangularSouth:
+ res = 2;
+ break;
+ case QTabBar::RoundedEast:
+ case QTabBar::TriangularEast:
+ res = 3;
+ break;
+ case QTabBar::RoundedWest:
+ case QTabBar::TriangularWest:
+ res = 1;
+ break;
+ }
+ }
+ break;
+
+ case PM_SplitterWidth:
+ res = qMax(5, QApplication::globalStrut().width());
+ break;
+
+ case PM_IndicatorWidth:
+ case PM_IndicatorHeight:
+ {
+ XPThemeData theme(widget, 0, QLatin1String("BUTTON"), BP_CHECKBOX, CBS_UNCHECKEDNORMAL);
+ if (theme.isValid()) {
+ SIZE size;
+ pGetThemePartSize(theme.handle(), 0, theme.partId, theme.stateId, 0, TS_TRUE, &size);
+ res = (pm == PM_IndicatorWidth ? size.cx+2 : res = size.cy+2);
+ }
+ }
+ break;
+
+ case PM_ExclusiveIndicatorWidth:
+ case PM_ExclusiveIndicatorHeight:
+ {
+ XPThemeData theme(widget, 0, QLatin1String("BUTTON"), BP_RADIOBUTTON, RBS_UNCHECKEDNORMAL);
+ if (theme.isValid()) {
+ SIZE size;
+ pGetThemePartSize(theme.handle(), 0, theme.partId, theme.stateId, 0, TS_TRUE, &size);
+ res = (pm == PM_ExclusiveIndicatorWidth ? size.cx+2 : res = size.cy+2);
+ }
+ }
+ break;
+
+ case PM_ProgressBarChunkWidth:
+ {
+ Qt::Orientation orient = Qt::Horizontal;
+ if (const QStyleOptionProgressBarV2 *pb2 = qstyleoption_cast<const QStyleOptionProgressBarV2 *>(option))
+ orient = pb2->orientation;
+ XPThemeData theme(widget, 0, QLatin1String("PROGRESS"), (orient == Qt::Horizontal) ? PP_CHUNK : PP_CHUNKVERT);
+ if (theme.isValid()) {
+ SIZE size;
+ pGetThemePartSize(theme.handle(), 0, theme.partId, theme.stateId, 0, TS_TRUE, &size);
+ res = (orient == Qt::Horizontal) ? size.cx : size.cy;
+ }
+ }
+ break;
+
+ case PM_SliderThickness:
+ {
+ XPThemeData theme(widget, 0, QLatin1String("TRACKBAR"), TKP_THUMB);
+ if (theme.isValid()) {
+ SIZE size;
+ pGetThemePartSize(theme.handle(), 0, theme.partId, theme.stateId, 0, TS_TRUE, &size);
+ res = size.cy;
+ }
+ }
+ break;
+
+ case PM_MenuButtonIndicator:
+ {
+ XPThemeData theme(widget, 0, QLatin1String("TOOLBAR"), TP_SPLITBUTTONDROPDOWN);
+ if (theme.isValid()) {
+ SIZE size;
+ pGetThemePartSize(theme.handle(), 0, theme.partId, theme.stateId, 0, TS_TRUE, &size);
+ res = size.cx;
+ }
+ }
+ break;
+
+ case PM_TitleBarHeight:
+ {
+#ifdef QT3_SUPPORT
+ if (widget && widget->inherits("Q3DockWindowTitleBar")) {
+ res = GetSystemMetrics(SM_CYSMCAPTION) + GetSystemMetrics(SM_CXSIZEFRAME);
+ } else
+#endif
+ if (widget && (widget->windowType() == Qt::Tool))
+ res = GetSystemMetrics(SM_CYSMCAPTION) + GetSystemMetrics(SM_CXSIZEFRAME);
+ else
+ res = GetSystemMetrics(SM_CYCAPTION) + GetSystemMetrics(SM_CXSIZEFRAME);
+ }
+ break;
+
+ case PM_MdiSubWindowFrameWidth:
+ {
+ XPThemeData theme(widget, 0, QLatin1String("WINDOW"), WP_FRAMELEFT, FS_ACTIVE);
+ if (theme.isValid()) {
+ SIZE size;
+ pGetThemePartSize(theme.handle(), 0, WP_FRAMELEFT, FS_ACTIVE, 0, TS_TRUE, &size);
+ res = size.cx-1;
+ }
+ }
+ break;
+
+ case PM_MdiSubWindowMinimizedWidth:
+ res = 160;
+ break;
+
+#ifndef QT_NO_TOOLBAR
+ case PM_ToolBarHandleExtent:
+ res = 8;
+ break;
+
+#endif // QT_NO_TOOLBAR
+ case PM_DockWidgetFrameWidth:
+ {
+ XPThemeData theme(widget, 0, QLatin1String("WINDOW"), WP_SMALLFRAMERIGHT, FS_ACTIVE);
+ if (theme.isValid()) {
+ SIZE size;
+ pGetThemePartSize(theme.handle(), 0, theme.partId, theme.stateId, 0, TS_TRUE, &size);
+ res = size.cx;
+ }
+ }
+ break;
+ case PM_DockWidgetSeparatorExtent:
+ res = 4;
+ break;
+ case PM_DockWidgetTitleMargin:
+ res = 4;
+ break;
+
+ case PM_ButtonShiftHorizontal:
+ case PM_ButtonShiftVertical:
+ if (qstyleoption_cast<const QStyleOptionToolButton *>(option))
+ res = 1;
+ else
+ res = 0;
+ break;
+
+ default:
+ res = QWindowsStyle::pixelMetric(pm, option, widget);
+ }
+
+ return res;
+}
+
+/*
+ This function is used by subControlRect to check if a button
+ should be drawn for the given subControl given a set of window flags.
+*/
+static bool buttonVisible(const QStyle::SubControl sc, const QStyleOptionTitleBar *tb){
+
+ bool isMinimized = tb->titleBarState & Qt::WindowMinimized;
+ bool isMaximized = tb->titleBarState & Qt::WindowMaximized;
+ const uint flags = tb->titleBarFlags;
+ bool retVal = false;
+ switch (sc) {
+ case QStyle::SC_TitleBarContextHelpButton:
+ if (flags & Qt::WindowContextHelpButtonHint)
+ retVal = true;
+ break;
+ case QStyle::SC_TitleBarMinButton:
+ if (!isMinimized && (flags & Qt::WindowMinimizeButtonHint))
+ retVal = true;
+ break;
+ case QStyle::SC_TitleBarNormalButton:
+ if (isMinimized && (flags & Qt::WindowMinimizeButtonHint))
+ retVal = true;
+ else if (isMaximized && (flags & Qt::WindowMaximizeButtonHint))
+ retVal = true;
+ break;
+ case QStyle::SC_TitleBarMaxButton:
+ if (!isMaximized && (flags & Qt::WindowMaximizeButtonHint))
+ retVal = true;
+ break;
+ case QStyle::SC_TitleBarShadeButton:
+ if (!isMinimized && flags & Qt::WindowShadeButtonHint)
+ retVal = true;
+ break;
+ case QStyle::SC_TitleBarUnshadeButton:
+ if (isMinimized && flags & Qt::WindowShadeButtonHint)
+ retVal = true;
+ break;
+ case QStyle::SC_TitleBarCloseButton:
+ if (flags & Qt::WindowSystemMenuHint)
+ retVal = true;
+ break;
+ case QStyle::SC_TitleBarSysMenu:
+ if (flags & Qt::WindowSystemMenuHint)
+ retVal = true;
+ break;
+ default :
+ retVal = true;
+ }
+ return retVal;
+}
+
+/*!
+ \reimp
+*/
+QRect QWindowsXPStyle::subControlRect(ComplexControl cc, const QStyleOptionComplex *option,
+ SubControl subControl, const QWidget *widget) const
+{
+ if (!QWindowsXPStylePrivate::useXP())
+ return QWindowsStyle::subControlRect(cc, option, subControl, widget);
+
+ QRect rect;
+
+ switch (cc) {
+ case CC_TitleBar:
+ if (const QStyleOptionTitleBar *tb = qstyleoption_cast<const QStyleOptionTitleBar *>(option)) {
+ if (!buttonVisible(subControl, tb))
+ return rect;
+ const bool isToolTitle = false;
+ const int height = tb->rect.height();
+ const int width = tb->rect.width();
+ int buttonHeight = GetSystemMetrics(SM_CYSIZE) - 4;
+ int buttonWidth = GetSystemMetrics(SM_CXSIZE) - 4;
+ const int delta = buttonWidth + 2;
+ int controlTop = option->rect.bottom() - buttonHeight - 2;
+ const int frameWidth = pixelMetric(PM_MdiSubWindowFrameWidth, option, widget);
+ const bool sysmenuHint = (tb->titleBarFlags & Qt::WindowSystemMenuHint) != 0;
+ const bool minimizeHint = (tb->titleBarFlags & Qt::WindowMinimizeButtonHint) != 0;
+ const bool maximizeHint = (tb->titleBarFlags & Qt::WindowMaximizeButtonHint) != 0;
+ const bool contextHint = (tb->titleBarFlags & Qt::WindowContextHelpButtonHint) != 0;
+ const bool shadeHint = (tb->titleBarFlags & Qt::WindowShadeButtonHint) != 0;
+ bool isMinimized = tb->titleBarState & Qt::WindowMinimized;
+ bool isMaximized = tb->titleBarState & Qt::WindowMaximized;
+ int offset = 0;
+
+ switch (subControl) {
+ case SC_TitleBarLabel:
+ rect = QRect(frameWidth, 0, width - (buttonWidth + frameWidth + 10), height);
+ if (isToolTitle) {
+ if (sysmenuHint) {
+ rect.adjust(0, 0, -buttonWidth - 3, 0);
+ }
+ if (minimizeHint || maximizeHint)
+ rect.adjust(0, 0, -buttonWidth - 2, 0);
+ } else {
+ if (sysmenuHint) {
+ const int leftOffset = height - 8;
+ rect.adjust(leftOffset, 0, 0, 0);
+ }
+ if (minimizeHint)
+ rect.adjust(0, 0, -buttonWidth - 2, 0);
+ if (maximizeHint)
+ rect.adjust(0, 0, -buttonWidth - 2, 0);
+ if (contextHint)
+ rect.adjust(0, 0, -buttonWidth - 2, 0);
+ if (shadeHint)
+ rect.adjust(0, 0, -buttonWidth - 2, 0);
+ }
+ break;
+
+ case SC_TitleBarContextHelpButton:
+ if (tb->titleBarFlags & Qt::WindowContextHelpButtonHint)
+ offset += delta;
+ //fall through
+ case SC_TitleBarMinButton:
+ if (!isMinimized && (tb->titleBarFlags & Qt::WindowMinimizeButtonHint))
+ offset += delta;
+ else if (subControl == SC_TitleBarMinButton)
+ break;
+ //fall through
+ case SC_TitleBarNormalButton:
+ if (isMinimized && (tb->titleBarFlags & Qt::WindowMinimizeButtonHint))
+ offset += delta;
+ else if (isMaximized && (tb->titleBarFlags & Qt::WindowMaximizeButtonHint))
+ offset += delta;
+ else if (subControl == SC_TitleBarNormalButton)
+ break;
+ //fall through
+ case SC_TitleBarMaxButton:
+ if (!isMaximized && (tb->titleBarFlags & Qt::WindowMaximizeButtonHint))
+ offset += delta;
+ else if (subControl == SC_TitleBarMaxButton)
+ break;
+ //fall through
+ case SC_TitleBarShadeButton:
+ if (!isMinimized && (tb->titleBarFlags & Qt::WindowShadeButtonHint))
+ offset += delta;
+ else if (subControl == SC_TitleBarShadeButton)
+ break;
+ //fall through
+ case SC_TitleBarUnshadeButton:
+ if (isMinimized && (tb->titleBarFlags & Qt::WindowShadeButtonHint))
+ offset += delta;
+ else if (subControl == SC_TitleBarUnshadeButton)
+ break;
+ //fall through
+ case SC_TitleBarCloseButton:
+ if (tb->titleBarFlags & Qt::WindowSystemMenuHint)
+ offset += delta;
+ else if (subControl == SC_TitleBarCloseButton)
+ break;
+
+ rect.setRect(width - offset - controlTop + 1, controlTop,
+ buttonWidth, buttonHeight);
+ break;
+
+ case SC_TitleBarSysMenu:
+ {
+ const int controlTop = 6;
+ const int controlHeight = height - controlTop - 3;
+ const int iconExtent = pixelMetric(PM_SmallIconSize);
+ QSize iconSize = tb->icon.actualSize(QSize(iconExtent, iconExtent));
+ if (tb->icon.isNull())
+ iconSize = QSize(controlHeight, controlHeight);
+ int hPad = (controlHeight - iconSize.height())/2;
+ int vPad = (controlHeight - iconSize.width())/2;
+ rect = QRect(frameWidth + hPad, controlTop + vPad, iconSize.width(), iconSize.height());
+ }
+ break;
+ }
+ }
+ break;
+
+ case CC_ComboBox:
+ if (const QStyleOptionComboBox *cmb = qstyleoption_cast<const QStyleOptionComboBox *>(option)) {
+ int x = cmb->rect.x(), y = cmb->rect.y(), wi = cmb->rect.width(), he = cmb->rect.height();
+ int xpos = x;
+ xpos += wi - 1 - 16;
+
+ switch (subControl) {
+ case SC_ComboBoxFrame:
+ rect = cmb->rect;
+ break;
+
+ case SC_ComboBoxArrow:
+ rect = QRect(xpos, y+1, 16, he-2);
+ break;
+
+ case SC_ComboBoxEditField:
+ rect = QRect(x+2, y+2, wi-3-16, he-4);
+ break;
+
+ case SC_ComboBoxListBoxPopup:
+ rect = cmb->rect;
+ break;
+ }
+ }
+ break;
+#ifndef QT_NO_WORKSPACE
+ case CC_MdiControls:
+ {
+ int numSubControls = 0;
+ if (option->subControls & SC_MdiCloseButton)
+ ++numSubControls;
+ if (option->subControls & SC_MdiMinButton)
+ ++numSubControls;
+ if (option->subControls & SC_MdiNormalButton)
+ ++numSubControls;
+ if (numSubControls == 0)
+ break;
+
+ int buttonWidth = option->rect.width()/ numSubControls;
+ int offset = 0;
+ switch (subControl) {
+ case SC_MdiCloseButton:
+ // Only one sub control, no offset needed.
+ if (numSubControls == 1)
+ break;
+ offset += buttonWidth;
+ //FALL THROUGH
+ case SC_MdiNormalButton:
+ // No offset needed if
+ // 1) There's only one sub control
+ // 2) We have a close button and a normal button (offset already added in SC_MdiClose)
+ if (numSubControls == 1 || (numSubControls == 2 && !(option->subControls & SC_MdiMinButton)))
+ break;
+ if (option->subControls & SC_MdiNormalButton)
+ offset += buttonWidth;
+ break;
+ default:
+ break;
+ }
+ rect = QRect(offset, 0, buttonWidth, option->rect.height());
+ break;
+ }
+#endif // QT_NO_WORKSPACE
+
+ default:
+ rect = visualRect(option->direction, option->rect,
+ QWindowsStyle::subControlRect(cc, option, subControl, widget));
+ break;
+ }
+ return visualRect(option->direction, option->rect, rect);
+}
+
+/*!
+ \reimp
+*/
+QSize QWindowsXPStyle::sizeFromContents(ContentsType ct, const QStyleOption *option,
+ const QSize &contentsSize, const QWidget *widget) const
+{
+ if (!QWindowsXPStylePrivate::useXP())
+ return QWindowsStyle::sizeFromContents(ct, option, contentsSize, widget);
+
+ QSize sz(contentsSize);
+ switch (ct) {
+ case CT_LineEdit:
+ case CT_ComboBox:
+ {
+ HTHEME theme = pOpenThemeData(QWindowsXPStylePrivate::winId(widget), L"Button");
+ MARGINS borderSize;
+ if (theme) {
+ int result = pGetThemeMargins(theme,
+ NULL,
+ BP_PUSHBUTTON,
+ PBS_NORMAL,
+ TMT_CONTENTMARGINS,
+ NULL,
+ &borderSize);
+ if (result == S_OK) {
+ sz += QSize(borderSize.cxLeftWidth + borderSize.cxRightWidth - 2,
+ borderSize.cyBottomHeight + borderSize.cyTopHeight - 2);
+ }
+ sz += QSize(23, 0); //arrow button
+ }
+ }
+ break;
+ case CT_SpinBox:
+ {
+ //Spinbox adds frame twice
+ sz = QWindowsStyle::sizeFromContents(ct, option, contentsSize, widget);
+ int border = pixelMetric(PM_SpinBoxFrameWidth, option, widget);
+ sz -= QSize(2*border, 2*border);
+ }
+ break;
+ case CT_TabWidget:
+ sz += QSize(6, 6);
+ break;
+ case CT_Menu:
+ sz += QSize(1, 0);
+ break;
+#ifndef QT_NO_MENUBAR
+ case CT_MenuBarItem:
+ if (!sz.isEmpty())
+ sz += QSize(windowsItemHMargin * 5 + 1, 6);
+ break;
+#endif
+ case CT_MenuItem:
+ if (const QStyleOptionMenuItem *menuitem = qstyleoption_cast<const QStyleOptionMenuItem *>(option))
+ {
+ if (menuitem->menuItemType != QStyleOptionMenuItem::Separator) {
+ sz = QWindowsStyle::sizeFromContents(ct, option, sz, widget);
+ sz.setHeight(sz.height() - 2);
+ return sz;
+ }
+ }
+ sz = QWindowsStyle::sizeFromContents(ct, option, sz, widget);
+ break;
+
+ case CT_MdiControls:
+ if (const QStyleOptionComplex *styleOpt = qstyleoption_cast<const QStyleOptionComplex *>(option)) {
+ int width = 0;
+ if (styleOpt->subControls & SC_MdiMinButton)
+ width += 17 + 1;
+ if (styleOpt->subControls & SC_MdiNormalButton)
+ width += 17 + 1;
+ if (styleOpt->subControls & SC_MdiCloseButton)
+ width += 17 + 1;
+ sz = QSize(width, 19);
+ } else {
+ sz = QSize(54, 19);
+ }
+ break;
+
+ default:
+ sz = QWindowsStyle::sizeFromContents(ct, option, sz, widget);
+ break;
+ }
+
+ return sz;
+}
+
+
+/*! \reimp */
+int QWindowsXPStyle::styleHint(StyleHint hint, const QStyleOption *option, const QWidget *widget,
+ QStyleHintReturn *returnData) const
+{
+ QWindowsXPStylePrivate *d = const_cast<QWindowsXPStylePrivate*>(d_func());
+ if (!QWindowsXPStylePrivate::useXP())
+ return QWindowsStyle::styleHint(hint, option, widget, returnData);
+
+ int res = 0;
+ switch (hint) {
+
+ case SH_EtchDisabledText:
+ res = (qobject_cast<const QLabel*>(widget) != 0);
+ break;
+
+ case SH_SpinControls_DisableOnBounds:
+ res = 0;
+ break;
+
+ case SH_TitleBar_AutoRaise:
+ case SH_TitleBar_NoBorder:
+ res = 1;
+ break;
+
+ case SH_GroupBox_TextLabelColor:
+ if (!widget || (widget && widget->isEnabled()))
+ res = d->groupBoxTextColor;
+ else
+ res = d->groupBoxTextColorDisabled;
+ break;
+
+ case SH_Table_GridLineColor:
+ res = 0xC0C0C0;
+ break;
+
+ case SH_WindowFrame_Mask:
+ {
+ res = 1;
+ QStyleHintReturnMask *mask = qstyleoption_cast<QStyleHintReturnMask *>(returnData);
+ const QStyleOptionTitleBar *titlebar = qstyleoption_cast<const QStyleOptionTitleBar *>(option);
+ if (mask && titlebar) {
+ XPThemeData themeData;
+ if (titlebar->titleBarState & Qt::WindowMinimized) {
+ themeData = XPThemeData(widget, 0, QLatin1String("WINDOW"), WP_MINCAPTION, CS_ACTIVE, option->rect);
+ } else
+ themeData = XPThemeData(widget, 0, QLatin1String("WINDOW"), WP_CAPTION, CS_ACTIVE, option->rect);
+ mask->region = d->region(themeData);
+ }
+ }
+ break;
+#ifndef QT_NO_RUBBERBAND
+ case SH_RubberBand_Mask:
+ if (qstyleoption_cast<const QStyleOptionRubberBand *>(option)) {
+ res = 0;
+ break;
+ }
+#endif // QT_NO_RUBBERBAND
+
+ case SH_ItemView_DrawDelegateFrame:
+ res = 1;
+ break;
+
+ default:
+ res =QWindowsStyle::styleHint(hint, option, widget, returnData);
+ }
+
+ return res;
+}
+
+/*! \reimp */
+QPalette QWindowsXPStyle::standardPalette() const
+{
+ if (QWindowsXPStylePrivate::useXP() && QApplicationPrivate::sys_pal)
+ return *QApplicationPrivate::sys_pal;
+ else
+ return QWindowsStyle::standardPalette();
+}
+
+/*!
+ \reimp
+*/
+QPixmap QWindowsXPStyle::standardPixmap(StandardPixmap standardPixmap, const QStyleOption *option,
+ const QWidget *widget) const
+{
+ if (!QWindowsXPStylePrivate::useXP())
+ return QWindowsStyle::standardPixmap(standardPixmap, option, widget);
+
+ switch(standardPixmap) {
+ case SP_TitleBarMaxButton:
+ case SP_TitleBarCloseButton:
+ if (qstyleoption_cast<const QStyleOptionDockWidget *>(option))
+ {
+ if (widget && widget->isWindow()) {
+ XPThemeData theme(widget, 0, QLatin1String("WINDOW"), WP_SMALLCLOSEBUTTON, CBS_NORMAL);
+ if (theme.isValid()) {
+ SIZE sz;
+ pGetThemePartSize(theme.handle(), 0, theme.partId, theme.stateId, 0, TS_TRUE, &sz);
+ return QIcon(QWindowsStyle::standardPixmap(standardPixmap, option, widget)).pixmap(QSize(sz.cx, sz.cy));
+ }
+ }
+ }
+ break;
+ }
+ return QWindowsStyle::standardPixmap(standardPixmap, option, widget);
+}
+
+/*!
+ \internal
+*/
+QIcon QWindowsXPStyle::standardIconImplementation(StandardPixmap standardIcon,
+ const QStyleOption *option,
+ const QWidget *widget) const
+{
+ if (!QWindowsXPStylePrivate::useXP()) {
+ return QWindowsStyle::standardIconImplementation(standardIcon, option, widget);
+ }
+
+ QWindowsXPStylePrivate *d = const_cast<QWindowsXPStylePrivate*>(d_func());
+ switch(standardIcon) {
+ case SP_TitleBarMaxButton:
+ if (qstyleoption_cast<const QStyleOptionDockWidget *>(option))
+ {
+ if (d->dockFloat.isNull()) {
+ XPThemeData themeSize(0, 0, QLatin1String("WINDOW"), WP_SMALLCLOSEBUTTON, CBS_NORMAL);
+ XPThemeData theme(0, 0, QLatin1String("WINDOW"), WP_MAXBUTTON, MAXBS_NORMAL);
+ if (theme.isValid()) {
+ SIZE size;
+ pGetThemePartSize(themeSize.handle(), 0, themeSize.partId, themeSize.stateId, 0, TS_TRUE, &size);
+ QPixmap pm = QPixmap(size.cx, size.cy);
+ pm.fill(Qt::transparent);
+ QPainter p(&pm);
+ theme.painter = &p;
+ theme.rect = QRect(0, 0, size.cx, size.cy);
+ d->drawBackground(theme);
+ d->dockFloat.addPixmap(pm, QIcon::Normal, QIcon::Off); // Normal
+ pm.fill(Qt::transparent);
+ theme.stateId = MAXBS_PUSHED;
+ d->drawBackground(theme);
+ d->dockFloat.addPixmap(pm, QIcon::Normal, QIcon::On); // Pressed
+ pm.fill(Qt::transparent);
+ theme.stateId = MAXBS_HOT;
+ d->drawBackground(theme);
+ d->dockFloat.addPixmap(pm, QIcon::Active, QIcon::Off); // Hover
+ pm.fill(Qt::transparent);
+ theme.stateId = MAXBS_INACTIVE;
+ d->drawBackground(theme);
+ d->dockFloat.addPixmap(pm, QIcon::Disabled, QIcon::Off); // Disabled
+ }
+ }
+ if (widget && widget->isWindow())
+ return d->dockFloat;
+
+ }
+ break;
+ case SP_TitleBarCloseButton:
+ if (qstyleoption_cast<const QStyleOptionDockWidget *>(option))
+ {
+ if (d->dockClose.isNull()) {
+ XPThemeData theme(0, 0, QLatin1String("WINDOW"), WP_SMALLCLOSEBUTTON, CBS_NORMAL);
+ if (theme.isValid()) {
+ SIZE size;
+ pGetThemePartSize(theme.handle(), 0, theme.partId, theme.stateId, 0, TS_TRUE, &size);
+ QPixmap pm = QPixmap(size.cx, size.cy);
+ pm.fill(Qt::transparent);
+ QPainter p(&pm);
+ theme.painter = &p;
+ theme.partId = WP_CLOSEBUTTON; // ####
+ theme.rect = QRect(0, 0, size.cx, size.cy);
+ d->drawBackground(theme);
+ d->dockClose.addPixmap(pm, QIcon::Normal, QIcon::Off); // Normal
+ pm.fill(Qt::transparent);
+ theme.stateId = CBS_PUSHED;
+ d->drawBackground(theme);
+ d->dockClose.addPixmap(pm, QIcon::Normal, QIcon::On); // Pressed
+ pm.fill(Qt::transparent);
+ theme.stateId = CBS_HOT;
+ d->drawBackground(theme);
+ d->dockClose.addPixmap(pm, QIcon::Active, QIcon::Off); // Hover
+ pm.fill(Qt::transparent);
+ theme.stateId = CBS_INACTIVE;
+ d->drawBackground(theme);
+ d->dockClose.addPixmap(pm, QIcon::Disabled, QIcon::Off); // Disabled
+ }
+ }
+ if (widget && widget->isWindow())
+ return d->dockClose;
+ }
+ break;
+ case SP_TitleBarNormalButton:
+ if (qstyleoption_cast<const QStyleOptionDockWidget *>(option))
+ {
+ if (d->dockFloat.isNull()) {
+ XPThemeData themeSize(0, 0, QLatin1String("WINDOW"), WP_SMALLCLOSEBUTTON, CBS_NORMAL);
+ XPThemeData theme(0, 0, QLatin1String("WINDOW"), WP_RESTOREBUTTON, RBS_NORMAL);
+ if (theme.isValid()) {
+ SIZE size;
+ pGetThemePartSize(themeSize.handle(), 0, themeSize.partId, themeSize.stateId, 0, TS_TRUE, &size);
+ QPixmap pm = QPixmap(size.cx, size.cy);
+ pm.fill(Qt::transparent);
+ QPainter p(&pm);
+ theme.painter = &p;
+ theme.rect = QRect(0, 0, size.cx, size.cy);
+ d->drawBackground(theme);
+ d->dockFloat.addPixmap(pm, QIcon::Normal, QIcon::Off); // Normal
+ pm.fill(Qt::transparent);
+ theme.stateId = RBS_PUSHED;
+ d->drawBackground(theme);
+ d->dockFloat.addPixmap(pm, QIcon::Normal, QIcon::On); // Pressed
+ pm.fill(Qt::transparent);
+ theme.stateId = RBS_HOT;
+ d->drawBackground(theme);
+ d->dockFloat.addPixmap(pm, QIcon::Active, QIcon::Off); // Hover
+ pm.fill(Qt::transparent);
+ theme.stateId = RBS_INACTIVE;
+ d->drawBackground(theme);
+ d->dockFloat.addPixmap(pm, QIcon::Disabled, QIcon::Off); // Disabled
+ }
+ }
+ if (widget && widget->isWindow())
+ return d->dockFloat;
+
+ }
+ break;
+ }
+
+ return QWindowsStyle::standardIconImplementation(standardIcon, option, widget);
+}
+
+/*!
+ \internal
+
+ Constructs a QWindowsXPStyle object.
+*/
+QWindowsXPStyle::QWindowsXPStyle(QWindowsXPStylePrivate &dd) : QWindowsStyle(dd)
+{
+}
+
+
+// Debugging code ---------------------------------------------------------------------[ START ]---
+// The code for this point on is not compiled by default, but only used as assisting
+// debugging code when you uncomment the DEBUG_XP_STYLE define at the top of the file.
+
+#ifdef DEBUG_XP_STYLE
+// The schema file expects these to be defined by the user.
+#define TMT_ENUMDEF 8
+#define TMT_ENUMVAL TEXT('A')
+#define TMT_ENUM TEXT('B')
+#define SCHEMA_STRINGS // For 2nd pass on schema file
+QT_BEGIN_INCLUDE_NAMESPACE
+#include <tmschema.h>
+QT_END_INCLUDE_NAMESPACE
+
+// A property's value, type and name combo
+struct PropPair {
+ int propValue;
+ int propType;
+ LPCWSTR propName;
+};
+
+// Operator for sorting of PropPairs
+bool operator<(PropPair a, PropPair b) {
+ return wcscmp(a.propName, b.propName) < 0;
+}
+
+// Our list of all possible properties
+static QList<PropPair> all_props;
+
+
+/*! \internal
+ Dumps a portion of the full native DIB section double buffer.
+ The DIB section double buffer is only used when doing special
+ transformations to the theme part, or when the real double
+ buffer in the paintengine does not have an HDC we may use
+ directly.
+ Since we cannot rely on the pixel data we get from Microsoft
+ when drawing into the DIB section, we use this function to
+ see the actual data we got, and can determin the appropriate
+ action.
+*/
+void QWindowsXPStylePrivate::dumpNativeDIB(int w, int h)
+{
+ if (w && h) {
+ static int pCount = 0;
+ DWORD *bufPix = (DWORD*)bufferPixels;
+
+ char *bufferDump = new char[bufferH * bufferW * 16];
+ char *bufferPos = bufferDump;
+
+ memset(bufferDump, 0, sizeof(bufferDump));
+ bufferPos += sprintf(bufferPos, "const int pixelBufferW%d = %d;\n", pCount, w);
+ bufferPos += sprintf(bufferPos, "const int pixelBufferH%d = %d;\n", pCount, h);
+ bufferPos += sprintf(bufferPos, "const unsigned DWORD pixelBuffer%d[] = {", pCount);
+ for (int iy = 0; iy < h; ++iy) {
+ bufferPos += sprintf(bufferPos, "\n ");
+ bufPix = (DWORD*)(bufferPixels + (iy * bufferW * 4));
+ for (int ix = 0; ix < w; ++ix) {
+ bufferPos += sprintf(bufferPos, "0x%08x, ", *bufPix);
+ ++bufPix;
+ }
+ }
+ bufferPos += sprintf(bufferPos, "\n};\n\n");
+ printf(bufferDump);
+
+ delete bufferDump;
+ ++pCount;
+ }
+}
+
+/*! \internal
+ Shows the value of a given property for a part.
+*/
+static void showProperty(XPThemeData &themeData, const PropPair &prop)
+{
+ PROPERTYORIGIN origin = PO_NOTFOUND;
+ pGetThemePropertyOrigin(themeData.handle(), themeData.partId, themeData.stateId, prop.propValue, &origin);
+ const char *originStr;
+ switch(origin) {
+ case PO_STATE:
+ originStr = "State ";
+ break;
+ case PO_PART:
+ originStr = "Part ";
+ break;
+ case PO_CLASS:
+ originStr = "Class ";
+ break;
+ case PO_GLOBAL:
+ originStr = "Globl ";
+ break;
+ case PO_NOTFOUND:
+ default:
+ originStr = "Unkwn ";
+ break;
+ }
+
+ switch(prop.propType) {
+ case TMT_STRING:
+ {
+ wchar_t buffer[512];
+ pGetThemeString(themeData.handle(), themeData.partId, themeData.stateId, prop.propValue, buffer, 512);
+ printf(" (%sString) %-20S: %S\n", originStr, prop.propName, buffer);
+ }
+ break;
+ case TMT_ENUM:
+ {
+ int result = -1;
+ pGetThemeEnumValue(themeData.handle(), themeData.partId, themeData.stateId, prop.propValue, &result);
+ printf(" (%sEnum) %-20S: %d\n", originStr, prop.propName, result);
+ }
+ break;
+ case TMT_INT:
+ {
+ int result = -1;
+ pGetThemeInt(themeData.handle(), themeData.partId, themeData.stateId, prop.propValue, &result);
+ printf(" (%sint) %-20S: %d\n", originStr, prop.propName, result);
+ }
+ break;
+ case TMT_BOOL:
+ {
+ BOOL result = false;
+ pGetThemeBool(themeData.handle(), themeData.partId, themeData.stateId, prop.propValue, &result);
+ printf(" (%sbool) %-20S: %d\n", originStr, prop.propName, result);
+ }
+ break;
+ case TMT_COLOR:
+ {
+ COLORREF result = 0;
+ pGetThemeColor(themeData.handle(), themeData.partId, themeData.stateId, prop.propValue, &result);
+ printf(" (%scolor) %-20S: 0x%08X\n", originStr, prop.propName, result);
+ }
+ break;
+ case TMT_MARGINS:
+ {
+ MARGINS result;
+ memset(&result, 0, sizeof(result));
+ pGetThemeMargins(themeData.handle(), 0, themeData.partId, themeData.stateId, prop.propValue, 0, &result);
+ printf(" (%smargins) %-20S: (%d, %d, %d, %d)\n", originStr,
+ prop.propName, result.cxLeftWidth, result.cyTopHeight, result.cxRightWidth, result.cyBottomHeight);
+ }
+ break;
+ case TMT_FILENAME:
+ {
+ wchar_t buffer[512];
+ pGetThemeFilename(themeData.handle(), themeData.partId, themeData.stateId, prop.propValue, buffer, 512);
+ printf(" (%sfilename)%-20S: %S\n", originStr, prop.propName, buffer);
+ }
+ break;
+ case TMT_SIZE:
+ {
+ SIZE result1;
+ SIZE result2;
+ SIZE result3;
+ memset(&result1, 0, sizeof(result1));
+ memset(&result2, 0, sizeof(result2));
+ memset(&result3, 0, sizeof(result3));
+ pGetThemePartSize(themeData.handle(), 0, themeData.partId, themeData.stateId, 0, TS_MIN, &result1);
+ pGetThemePartSize(themeData.handle(), 0, themeData.partId, themeData.stateId, 0, TS_TRUE, &result2);
+ pGetThemePartSize(themeData.handle(), 0, themeData.partId, themeData.stateId, 0, TS_DRAW, &result3);
+ printf(" (%ssize) %-20S: Min (%d, %d), True(%d, %d), Draw(%d, %d)\n", originStr, prop.propName,
+ result1.cx, result1.cy, result2.cx, result2.cy, result3.cx, result3.cy);
+ }
+ break;
+ case TMT_POSITION:
+ {
+ POINT result;
+ memset(&result, 0, sizeof(result));
+ pGetThemePosition(themeData.handle(), themeData.partId, themeData.stateId, prop.propValue, &result);
+ printf(" (%sPosition)%-20S: (%d, %d)\n", originStr, prop.propName, result.x, result.y);
+ }
+ break;
+ case TMT_RECT:
+ {
+ RECT result;
+ memset(&result, 0, sizeof(result));
+ pGetThemeRect(themeData.handle(), themeData.partId, themeData.stateId, prop.propValue, &result);
+ printf(" (%sRect) %-20S: (%d, %d, %d, %d)\n", originStr, prop.propName, result.left, result.top, result.right, result.bottom);
+ }
+ break;
+ case TMT_FONT:
+ {
+ LOGFONT result;
+ memset(&result, 0, sizeof(result));
+ pGetThemeFont(themeData.handle(), 0, themeData.partId, themeData.stateId, prop.propValue, &result);
+ printf(" (%sFont) %-20S: %S height(%d) width(%d) weight(%d)\n", originStr, prop.propName,
+ result.lfFaceName, result.lfHeight, result.lfWidth, result.lfWeight);
+ }
+ break;
+ case TMT_INTLIST:
+ {
+ INTLIST result;
+ memset(&result, 0, sizeof(result));
+ pGetThemeIntList(themeData.handle(), themeData.partId, themeData.stateId, prop.propValue, &result);
+ printf(" (%sInt list)%-20S: { ", originStr, prop.propName);
+ for (int i = 0; i < result.iValueCount; ++i)
+ printf("%d ", result.iValues[i]);
+ printf("}\n");
+ }
+ break;
+ default:
+ printf(" %s%S : Unknown property type (%d)!\n", originStr, prop.propName, prop.propType);
+ }
+}
+
+/*! \internal
+ Dump all valid properties for a part.
+ If it's the first time this function is called, then the name,
+ enum value and documentation of all properties are shown, as
+ well as all global properties.
+*/
+void QWindowsXPStylePrivate::showProperties(XPThemeData &themeData)
+{
+ if (!all_props.count()) {
+ const TMSCHEMAINFO *infoTable = GetSchemaInfo();
+ for (int i = 0; i < infoTable->iPropCount; ++i) {
+ int propType = infoTable->pPropTable[i].bPrimVal;
+ int propValue = infoTable->pPropTable[i].sEnumVal;
+ LPCWSTR propName = infoTable->pPropTable[i].pszName;
+
+ switch(propType) {
+ case TMT_ENUMDEF:
+ case TMT_ENUMVAL:
+ continue;
+ default:
+ if (propType != propValue) {
+ PropPair prop;
+ prop.propValue = propValue;
+ prop.propName = propName;
+ prop.propType = propType;
+ all_props.append(prop);
+ }
+ }
+ }
+ qSort(all_props);
+
+ {// List all properties
+ printf("part properties count = %d:\n", all_props.count());
+ printf(" Enum Property Name Description\n");
+ printf("-----------------------------------------------------------\n");
+ wchar_t themeName[256];
+ pGetCurrentThemeName(themeName, 256, 0, 0, 0, 0);
+ for (int j = 0; j < all_props.count(); ++j) {
+ PropPair prop = all_props.at(j);
+ wchar_t buf[500];
+ pGetThemeDocumentationProperty(themeName, prop.propName, buf, 500);
+ printf("%3d: (%4d) %-20S %S\n", j, prop.propValue, prop.propName, buf);
+ }
+ }
+
+ {// Show Global values
+ printf("Global Properties:\n");
+ for (int j = 0; j < all_props.count(); ++j) {
+ PropPair prop = all_props.at(j);
+ PROPERTYORIGIN origin = PO_NOTFOUND;
+ pGetThemePropertyOrigin(themeData.handle(), themeData.partId, themeData.stateId, prop.propValue, &origin);
+ if (origin == PO_GLOBAL) {
+ showProperty(themeData, prop);
+ }
+ }
+ }
+ }
+
+ for (int j = 0; j < all_props.count(); ++j) {
+ PropPair prop = all_props.at(j);
+ PROPERTYORIGIN origin = PO_NOTFOUND;
+ pGetThemePropertyOrigin(themeData.handle(), themeData.partId, themeData.stateId, prop.propValue, &origin);
+ if (origin != PO_NOTFOUND)
+ {
+ showProperty(themeData, prop);
+ }
+ }
+}
+#endif
+// Debugging code -----------------------------------------------------------------------[ END ]---
+
+
+QT_END_NAMESPACE
+
+#endif //QT_NO_WINDOWSXP
diff --git a/src/gui/styles/qwindowsxpstyle.h b/src/gui/styles/qwindowsxpstyle.h
new file mode 100644
index 0000000000..8faae8224f
--- /dev/null
+++ b/src/gui/styles/qwindowsxpstyle.h
@@ -0,0 +1,107 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QWINDOWSXPSTYLE_H
+#define QWINDOWSXPSTYLE_H
+
+#include <QtGui/qwindowsstyle.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+#if !defined(QT_NO_STYLE_WINDOWSXP)
+
+class QWindowsXPStylePrivate;
+class Q_GUI_EXPORT QWindowsXPStyle : public QWindowsStyle
+{
+ Q_OBJECT
+public:
+ QWindowsXPStyle();
+ QWindowsXPStyle(QWindowsXPStylePrivate &dd);
+ ~QWindowsXPStyle();
+
+ void unpolish(QApplication*);
+ void polish(QApplication*);
+ void polish(QWidget*);
+ void polish(QPalette&);
+ void unpolish(QWidget*);
+
+ void drawPrimitive(PrimitiveElement pe, const QStyleOption *option, QPainter *p,
+ const QWidget *widget = 0) const;
+ void drawControl(ControlElement element, const QStyleOption *option, QPainter *p,
+ const QWidget *wwidget = 0) const;
+ QRect subElementRect(SubElement r, const QStyleOption *option, const QWidget *widget = 0) const;
+ QRect subControlRect(ComplexControl cc, const QStyleOptionComplex *option, SubControl sc,
+ const QWidget *widget = 0) const;
+ void drawComplexControl(ComplexControl cc, const QStyleOptionComplex *option, QPainter *p,
+ const QWidget *widget = 0) const;
+ QSize sizeFromContents(ContentsType ct, const QStyleOption *option, const QSize &contentsSize,
+ const QWidget *widget = 0) const;
+ int pixelMetric(PixelMetric pm, const QStyleOption *option = 0,
+ const QWidget *widget = 0) const;
+ int styleHint(StyleHint hint, const QStyleOption *option = 0, const QWidget *widget = 0,
+ QStyleHintReturn *returnData = 0) const;
+
+ QPalette standardPalette() const;
+ QPixmap standardPixmap(StandardPixmap standardIcon, const QStyleOption *option,
+ const QWidget *widget = 0) const;
+
+protected Q_SLOTS:
+ QIcon standardIconImplementation(StandardPixmap standardIcon, const QStyleOption *option,
+ const QWidget *widget = 0) const;
+
+private:
+ Q_DISABLE_COPY(QWindowsXPStyle)
+ Q_DECLARE_PRIVATE(QWindowsXPStyle)
+ friend class QStyleFactory;
+ void *reserved;
+};
+
+#endif // QT_NO_STYLE_WINDOWSXP
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QWINDOWSXPSTYLE_H
diff --git a/src/gui/styles/qwindowsxpstyle_p.h b/src/gui/styles/qwindowsxpstyle_p.h
new file mode 100644
index 0000000000..717162930e
--- /dev/null
+++ b/src/gui/styles/qwindowsxpstyle_p.h
@@ -0,0 +1,356 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QWINDOWSXPSTYLE_P_H
+#define QWINDOWSXPSTYLE_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of qapplication_*.cpp, qwidget*.cpp and qfiledialog.cpp. This header
+// file may change from version to version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qwindowsxpstyle.h"
+#include "qwindowsstyle_p.h"
+#include <qmap.h>
+#include <qt_windows.h>
+
+// Note, these tests are duplicated in qwizard_win.cpp.
+#ifdef Q_CC_GNU
+# include <w32api.h>
+# if (__W32API_MAJOR_VERSION >= 3 || (__W32API_MAJOR_VERSION == 2 && __W32API_MINOR_VERSION >= 5))
+# ifdef _WIN32_WINNT
+# undef _WIN32_WINNT
+# endif
+# define _WIN32_WINNT 0x0501
+# include <commctrl.h>
+# endif
+#endif
+
+#include <uxtheme.h>
+
+#if WINVER >= 0x0600
+#include <vssym32.h>
+#else
+#include <tmschema.h>
+#endif
+
+#include <limits.h>
+
+QT_BEGIN_NAMESPACE
+
+// Older Platform SDKs do not have the extended DrawThemeBackgroundEx
+// function. We add the needed parts here, and use the extended
+// function dynamically, if available in uxtheme.dll. Else, we revert
+// back to using the DrawThemeBackground function.
+#ifndef DTBG_OMITBORDER
+# ifndef DTBG_CLIPRECT
+# define DTBG_CLIPRECT 0x00000001
+# endif
+# ifndef DTBG_DRAWSOLID
+# define DTBG_DRAWSOLID 0x00000002
+# endif
+# ifndef DTBG_OMITBORDER
+# define DTBG_OMITBORDER 0x00000004
+# endif
+# ifndef DTBG_OMITCONTENT
+# define DTBG_OMITCONTENT 0x00000008
+# endif
+# ifndef DTBG_COMPUTINGREGION
+# define DTBG_COMPUTINGREGION 0x00000010
+# endif
+# ifndef DTBG_MIRRORDC
+# define DTBG_MIRRORDC 0x00000020
+# endif
+ typedef struct _DTBGOPTS
+ {
+ DWORD dwSize;
+ DWORD dwFlags;
+ RECT rcClip;
+ } DTBGOPTS, *PDTBGOPTS;
+#endif // _DTBGOPTS
+
+// Undefined for some compile environments
+#ifndef TMT_TEXTCOLOR
+# define TMT_TEXTCOLOR 3803
+#endif
+#ifndef TMT_BORDERCOLORHINT
+# define TMT_BORDERCOLORHINT 3822
+#endif
+#ifndef TMT_BORDERSIZE
+# define TMT_BORDERSIZE 2403
+#endif
+#ifndef TMT_BORDERONLY
+# define TMT_BORDERONLY 2203
+#endif
+#ifndef TMT_TRANSPARENTCOLOR
+# define TMT_TRANSPARENTCOLOR 3809
+#endif
+#ifndef TMT_CAPTIONMARGINS
+# define TMT_CAPTIONMARGINS 3603
+#endif
+#ifndef TMT_CONTENTMARGINS
+# define TMT_CONTENTMARGINS 3602
+#endif
+#ifndef TMT_SIZINGMARGINS
+# define TMT_SIZINGMARGINS 3601
+#endif
+#ifndef TMT_GLYPHTYPE
+# define TMT_GLYPHTYPE 4012
+#endif
+#ifndef TMT_BGTYPE
+# define TMT_BGTYPE 4001
+#endif
+#ifndef TMT_TEXTSHADOWTYPE
+# define TMT_TEXTSHADOWTYPE 4010
+#endif
+#ifndef TMT_BORDERCOLOR
+# define TMT_BORDERCOLOR 3801
+#endif
+#ifndef BT_IMAGEFILE
+# define BT_IMAGEFILE 0
+#endif
+#ifndef BT_BORDERFILL
+# define BT_BORDERFILL 1
+#endif
+#ifndef BT_NONE
+# define BT_NONE 2
+#endif
+#ifndef TMT_FILLCOLOR
+# define TMT_FILLCOLOR 3802
+#endif
+#ifndef TMT_PROGRESSCHUNKSIZE
+# define TMT_PROGRESSCHUNKSIZE 2411
+#endif
+
+// TMT_TEXTSHADOWCOLOR is wrongly defined in mingw
+#if TMT_TEXTSHADOWCOLOR != 3818
+#undef TMT_TEXTSHADOWCOLOR
+#define TMT_TEXTSHADOWCOLOR 3818
+#endif
+#ifndef TST_NONE
+# define TST_NONE 0
+#endif
+
+#ifndef GT_NONE
+# define GT_NONE 0
+#endif
+#ifndef GT_IMAGEGLYPH
+# define GT_IMAGEGLYPH 1
+#endif
+
+// These defines are missing from the tmschema, but still exist as
+// states for their parts
+#ifndef MINBS_INACTIVE
+#define MINBS_INACTIVE 5
+#endif
+#ifndef MAXBS_INACTIVE
+#define MAXBS_INACTIVE 5
+#endif
+#ifndef RBS_INACTIVE
+#define RBS_INACTIVE 5
+#endif
+#ifndef HBS_INACTIVE
+#define HBS_INACTIVE 5
+#endif
+#ifndef CBS_INACTIVE
+#define CBS_INACTIVE 5
+#endif
+
+// Uncomment define below to build debug assisting code, and output
+// #define DEBUG_XP_STYLE
+
+#if !defined(QT_NO_STYLE_WINDOWSXP)
+
+// Declarations -----------------------------------------------------------------------------------
+class XPThemeData
+{
+public:
+ XPThemeData(const QWidget *w = 0, QPainter *p = 0, const QString &theme = QString(),
+ int part = 0, int state = 0, const QRect &r = QRect())
+ : widget(w), painter(p), name(theme), htheme(0), partId(part), stateId(state),
+ mirrorHorizontally(false), mirrorVertically(false), noBorder(false),
+ noContent(false), rotate(0), rect(r)
+ {}
+
+ HRGN mask();
+ HTHEME handle();
+
+ RECT toRECT(const QRect &qr);
+ bool isValid();
+
+ const QWidget *widget;
+ QPainter *painter;
+ QString name;
+ HTHEME htheme;
+ int partId;
+ int stateId;
+
+ uint mirrorHorizontally : 1;
+ uint mirrorVertically : 1;
+ uint noBorder : 1;
+ uint noContent : 1;
+ uint rotate;
+ QRect rect;
+};
+
+struct ThemeMapKey {
+ QString name;
+ int partId;
+ int stateId;
+ bool noBorder;
+ bool noContent;
+
+ ThemeMapKey() : partId(-1), stateId(-1) {}
+ ThemeMapKey(const XPThemeData &data)
+ : name(data.name), partId(data.partId), stateId(data.stateId),
+ noBorder(data.noBorder), noContent(data.noContent) {}
+
+};
+
+inline uint qHash(const ThemeMapKey &key)
+{ return qHash(key.name) ^ key.partId ^ key.stateId; }
+
+inline bool operator==(const ThemeMapKey &k1, const ThemeMapKey &k2)
+{
+ return k1.name == k2.name
+ && k1.partId == k2.partId
+ && k1.stateId == k2.stateId;
+}
+
+enum AlphaChannelType {
+ UnknownAlpha = -1, // Alpha of part & state not yet known
+ NoAlpha, // Totally opaque, no need to touch alpha (RGB)
+ MaskAlpha, // Alpha channel must be fixed (ARGB)
+ RealAlpha // Proper alpha values from Windows (ARGB_Premultiplied)
+};
+
+struct ThemeMapData {
+ AlphaChannelType alphaType; // Which type of alpha on part & state
+
+ bool dataValid : 1; // Only used to detect if hash value is ok
+ bool partIsTransparent : 1;
+ bool hasAnyData : 1; // False = part & state has not data, NOP
+ bool hasAlphaChannel : 1; // True = part & state has real Alpha
+ bool wasAlphaSwapped : 1; // True = alpha channel needs to be swapped
+ bool hadInvalidAlpha : 1; // True = alpha channel contained invalid alpha values
+
+ ThemeMapData() : dataValid(false), partIsTransparent(false), hasAnyData(false),
+ hasAlphaChannel(false), wasAlphaSwapped(false), hadInvalidAlpha(false) {}
+};
+
+class QWindowsXPStylePrivate : public QWindowsStylePrivate
+{
+ Q_DECLARE_PUBLIC(QWindowsXPStyle)
+public:
+ QWindowsXPStylePrivate()
+ : QWindowsStylePrivate(), hasInitColors(false), bufferDC(0), bufferBitmap(0), nullBitmap(0),
+ bufferPixels(0), bufferW(0), bufferH(0)
+ { init(); }
+
+ ~QWindowsXPStylePrivate()
+ { cleanup(); }
+
+ static HWND winId(const QWidget *widget);
+
+ void init(bool force = false);
+ void cleanup(bool force = false);
+ void cleanupHandleMap();
+ const QPixmap *tabBody(QWidget *widget);
+
+ HBITMAP buffer(int w = 0, int h = 0);
+ HDC bufferHDC()
+ { return bufferDC;}
+
+ static bool resolveSymbols();
+ static bool useXP(bool update = false);
+
+ bool isTransparent(XPThemeData &themeData);
+ QRegion region(XPThemeData &themeData);
+
+ void setTransparency(QWidget *widget, XPThemeData &themeData);
+ void drawBackground(XPThemeData &themeData);
+ void drawBackgroundThruNativeBuffer(XPThemeData &themeData);
+ void drawBackgroundDirectly(XPThemeData &themeData);
+
+ bool hasAnyData(const QRect &rect);
+ bool hasAlphaChannel(const QRect &rect);
+ bool fixAlphaChannel(const QRect &rect);
+ bool swapAlphaChannel(const QRect &rect, bool allPixels = false);
+
+ QRgb groupBoxTextColor;
+ QRgb groupBoxTextColorDisabled;
+ QRgb sliderTickColor;
+ bool hasInitColors;
+
+ static QMap<QString,HTHEME> *handleMap;
+
+ QIcon dockFloat, dockClose;
+
+private:
+#ifdef DEBUG_XP_STYLE
+ void dumpNativeDIB(int w, int h);
+ void showProperties(XPThemeData &themeData);
+#endif
+
+ static QBasicAtomicInt ref;
+ static bool use_xp;
+ static QWidget *limboWidget;
+ static QPixmap *tabbody;
+
+ QHash<ThemeMapKey, ThemeMapData> alphaCache;
+ HDC bufferDC;
+ HBITMAP bufferBitmap;
+ HBITMAP nullBitmap;
+ uchar *bufferPixels;
+ int bufferW, bufferH;
+};
+
+#endif // QT_NO_STYLE_WINDOWS
+
+QT_END_NAMESPACE
+
+#endif //QWINDOWSXPSTYLE_P_H
diff --git a/src/gui/styles/styles.pri b/src/gui/styles/styles.pri
new file mode 100644
index 0000000000..376f8341fe
--- /dev/null
+++ b/src/gui/styles/styles.pri
@@ -0,0 +1,157 @@
+# Qt styles module
+
+HEADERS += \
+ styles/qstyle.h \
+ styles/qstylefactory.h \
+ styles/qstyleoption.h \
+ styles/qstyleplugin.h \
+ styles/qcommonstylepixmaps_p.h \
+ styles/qcommonstyle.h \
+ styles/qstylesheetstyle_p.h
+SOURCES += \
+ styles/qstyle.cpp \
+ styles/qstylefactory.cpp \
+ styles/qstyleoption.cpp \
+ styles/qstyleplugin.cpp \
+ styles/qcommonstyle.cpp \
+ styles/qstylesheetstyle.cpp \
+ styles/qstylesheetstyle_default.cpp
+
+!wince* {
+ RESOURCES += styles/qstyle.qrc
+} else {
+ RESOURCES += styles/qstyle_wince.qrc
+}
+
+contains( styles, all ) {
+ styles = mac windows windowsxp windowsvista
+}
+
+x11|embedded|!macx-*:styles -= mac
+
+x11{
+ QMAKE_CXXFLAGS += $$QT_CFLAGS_QGTKSTYLE
+ LIBS += $$QT_LIBS_QGTKSTYLE
+ styles += gtk
+}
+
+contains( styles, mac ) {
+ HEADERS += \
+ styles/qmacstyle_mac.h \
+ styles/qmacstylepixmaps_mac_p.h
+ OBJECTIVE_SOURCES += styles/qmacstyle_mac.mm
+
+ !contains( styles, windows ) {
+ message( mac requires windows )
+ styles += windows
+ DEFINES+= QT_STYLE_WINDOWS
+ }
+} else {
+ DEFINES += QT_NO_STYLE_MAC
+}
+
+contains( styles, cde ) {
+ HEADERS += styles/qcdestyle.h
+ SOURCES += styles/qcdestyle.cpp
+
+ !contains( styles, motif ) {
+ message( cde requires motif )
+ styles += motif
+ DEFINES+= QT_STYLE_MOTIF
+ }
+} else {
+ DEFINES += QT_NO_STYLE_CDE
+}
+
+contains( styles, windowsvista ) {
+ HEADERS += styles/qwindowsvistastyle.h
+ HEADERS += styles/qwindowsvistastyle_p.h
+ SOURCES += styles/qwindowsvistastyle.cpp
+ !contains( styles, windowsxp ) {
+ message( windowsvista requires windowsxp )
+ styles += windowsxp
+ DEFINES+= QT_STYLE_WINDOWSXP
+ }
+} else {
+ DEFINES += QT_NO_STYLE_WINDOWSVISTA
+}
+
+contains( styles, windowsxp ) {
+ HEADERS += styles/qwindowsxpstyle.h
+ SOURCES += styles/qwindowsxpstyle.cpp
+ !contains( styles, windows ) {
+ message( windowsxp requires windows )
+ styles += windows
+ DEFINES+= QT_STYLE_WINDOWS
+ }
+} else {
+ DEFINES += QT_NO_STYLE_WINDOWSXP
+}
+
+contains( styles, plastique ) {
+ HEADERS += styles/qplastiquestyle.h
+ SOURCES += styles/qplastiquestyle.cpp
+ !contains( styles, windows ) {
+ message( plastique requires windows )
+ styles += windows
+ DEFINES+= QT_STYLE_WINDOWS
+ }
+} else {
+ DEFINES += QT_NO_STYLE_PLASTIQUE
+}
+
+contains( styles, gtk ) {
+ HEADERS += styles/qgtkstyle.h
+ HEADERS += styles/qgtkpainter_p.h
+ HEADERS += styles/gtksymbols_p.h
+ SOURCES += styles/qgtkstyle.cpp
+ SOURCES += styles/qgtkpainter.cpp
+ SOURCES += styles/gtksymbols.cpp
+ !contains( styles, cleanlooks ) {
+ styles += cleanlooks
+ DEFINES+= QT_STYLE_CLEANLOOKS
+ }
+} else {
+ DEFINES += QT_NO_STYLE_GTK
+}
+
+contains( styles, cleanlooks ) {
+ HEADERS += styles/qcleanlooksstyle.h
+ HEADERS += styles/qcleanlooksstyle_p.h
+ SOURCES += styles/qcleanlooksstyle.cpp
+ !contains( styles, windows ) {
+ styles += windows
+ DEFINES+= QT_STYLE_WINDOWS
+ }
+} else {
+ DEFINES += QT_NO_STYLE_CLEANLOOKS
+}
+
+contains( styles, windows ) {
+ HEADERS += styles/qwindowsstyle.h
+ SOURCES += styles/qwindowsstyle.cpp
+} else {
+ DEFINES += QT_NO_STYLE_WINDOWS
+}
+
+contains( styles, motif ) {
+ HEADERS += styles/qmotifstyle.h
+ SOURCES += styles/qmotifstyle.cpp
+} else {
+ DEFINES += QT_NO_STYLE_MOTIF
+}
+
+contains( styles, windowsce ) {
+ HEADERS += styles/qwindowscestyle.h
+ SOURCES += styles/qwindowscestyle.cpp
+} else {
+ DEFINES += QT_NO_STYLE_WINDOWSCE
+}
+
+contains( styles, windowsmobile ) {
+ HEADERS += styles/qwindowsmobilestyle.h
+ SOURCES += styles/qwindowsmobilestyle.cpp
+} else {
+ DEFINES += QT_NO_STYLE_WINDOWSMOBILE
+}
+
diff --git a/src/gui/text/qabstractfontengine_p.h b/src/gui/text/qabstractfontengine_p.h
new file mode 100644
index 0000000000..4752de5d0c
--- /dev/null
+++ b/src/gui/text/qabstractfontengine_p.h
@@ -0,0 +1,110 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QABSTRACTFONTENGINE_P_H
+#define QABSTRACTFONTENGINE_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 "qfontengine_p.h"
+#include "qabstractfontengine_qws.h"
+
+QT_BEGIN_NAMESPACE
+
+class QCustomFontEngine;
+
+class QProxyFontEngine : public QFontEngine
+{
+ Q_OBJECT
+public:
+ QProxyFontEngine(QAbstractFontEngine *engine, const QFontDef &def);
+ virtual ~QProxyFontEngine();
+
+ virtual bool stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs, int *nglyphs, QTextEngine::ShaperFlags flags) const;
+ virtual void recalcAdvances(QGlyphLayout *, QTextEngine::ShaperFlags) const;
+ virtual QImage alphaMapForGlyph(glyph_t);
+ virtual void addGlyphsToPath(glyph_t *glyphs, QFixedPoint *positions, int nglyphs, QPainterPath *path, QTextItem::RenderFlags flags);
+ virtual glyph_metrics_t boundingBox(const QGlyphLayout &glyphs);
+ virtual glyph_metrics_t boundingBox(glyph_t glyph);
+
+ virtual QFixed ascent() const;
+ virtual QFixed descent() const;
+ virtual QFixed leading() const;
+ virtual QFixed xHeight() const;
+ virtual QFixed averageCharWidth() const;
+ virtual QFixed lineThickness() const;
+ virtual QFixed underlinePosition() const;
+ virtual qreal maxCharWidth() const;
+ virtual qreal minLeftBearing() const;
+ virtual qreal minRightBearing() const;
+ virtual int glyphCount() const;
+
+ virtual bool canRender(const QChar *string, int len);
+
+ virtual Type type() const { return Proxy; }
+ virtual const char *name() const { return "proxy engine"; }
+
+#if !defined(Q_WS_X11) && !defined(Q_WS_WIN) && !defined(Q_WS_MAC)
+ virtual void draw(QPaintEngine *, qreal, qreal, const QTextItemInt &);
+#endif
+
+ inline QAbstractFontEngine::Capabilities capabilities() const
+ { return engineCapabilities; }
+
+ bool drawAsOutline() const;
+
+private:
+ QAbstractFontEngine *engine;
+ QAbstractFontEngine::Capabilities engineCapabilities;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/gui/text/qabstractfontengine_qws.cpp b/src/gui/text/qabstractfontengine_qws.cpp
new file mode 100644
index 0000000000..3f2579a50b
--- /dev/null
+++ b/src/gui/text/qabstractfontengine_qws.cpp
@@ -0,0 +1,776 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qabstractfontengine_qws.h"
+#include "qabstractfontengine_p.h"
+
+#include <private/qtextengine_p.h>
+#include <private/qpaintengine_raster_p.h>
+
+#include <qmath.h>
+
+QT_BEGIN_NAMESPACE
+
+class QFontEngineInfoPrivate
+{
+public:
+ inline QFontEngineInfoPrivate()
+ : pixelSize(0), weight(QFont::Normal), style(QFont::StyleNormal)
+ {}
+
+ QString family;
+ qreal pixelSize;
+ int weight;
+ QFont::Style style;
+ QList<QFontDatabase::WritingSystem> writingSystems;
+};
+
+/*!
+ \class QFontEngineInfo
+ \preliminary
+ \brief The QFontEngineInfo class describes a specific font provided by a font engine plugin.
+ \since 4.3
+ \ingroup qws
+
+ \tableofcontents
+
+ QFontEngineInfo is used to describe a request of a font to a font engine plugin as well as to
+ describe the actual fonts a plugin provides.
+
+ \sa QAbstractFontEngine, QFontEnginePlugin
+*/
+
+/*!
+ Constructs a new empty QFontEngineInfo.
+*/
+QFontEngineInfo::QFontEngineInfo()
+{
+ d = new QFontEngineInfoPrivate;
+}
+
+/*!
+ Constructs a new QFontEngineInfo with the specified \a family.
+ The resulting object represents a freely scalable font with normal
+ weight and style.
+*/
+QFontEngineInfo::QFontEngineInfo(const QString &family)
+{
+ d = new QFontEngineInfoPrivate;
+ d->family = family;
+}
+
+/*!
+ Creates a new font engine info object with the same attributes as \a other.
+*/
+QFontEngineInfo::QFontEngineInfo(const QFontEngineInfo &other)
+ : d(new QFontEngineInfoPrivate(*other.d))
+{
+}
+
+/*!
+ Assigns \a other to this font engine info object, and returns a reference
+ to this.
+*/
+QFontEngineInfo &QFontEngineInfo::operator=(const QFontEngineInfo &other)
+{
+ *d = *other.d;
+ return *this;
+}
+
+/*!
+ Destroys this QFontEngineInfo object.
+*/
+QFontEngineInfo::~QFontEngineInfo()
+{
+ delete d;
+}
+
+/*!
+ \property QFontEngineInfo::family
+ the family name of the font
+*/
+
+void QFontEngineInfo::setFamily(const QString &family)
+{
+ d->family = family;
+}
+
+QString QFontEngineInfo::family() const
+{
+ return d->family;
+}
+
+/*!
+ \property QFontEngineInfo::pixelSize
+ the pixel size of the font
+
+ A pixel size of 0 represents a freely scalable font.
+*/
+
+void QFontEngineInfo::setPixelSize(qreal size)
+{
+ d->pixelSize = size;
+}
+
+qreal QFontEngineInfo::pixelSize() const
+{
+ return d->pixelSize;
+}
+
+/*!
+ \property QFontEngineInfo::weight
+ the weight of the font
+
+ The value should be from the \l{QFont::Weight} enumeration.
+*/
+
+void QFontEngineInfo::setWeight(int weight)
+{
+ d->weight = weight;
+}
+
+int QFontEngineInfo::weight() const
+{
+ return d->weight;
+}
+
+/*!
+ \property QFontEngineInfo::style
+ the style of the font
+*/
+
+void QFontEngineInfo::setStyle(QFont::Style style)
+{
+ d->style = style;
+}
+
+QFont::Style QFontEngineInfo::style() const
+{
+ return d->style;
+}
+
+/*!
+ \property QFontEngineInfo::writingSystems
+ the writing systems supported by the font
+
+ An empty list means that any writing system is supported.
+*/
+
+QList<QFontDatabase::WritingSystem> QFontEngineInfo::writingSystems() const
+{
+ return d->writingSystems;
+}
+
+void QFontEngineInfo::setWritingSystems(const QList<QFontDatabase::WritingSystem> &writingSystems)
+{
+ d->writingSystems = writingSystems;
+}
+
+class QFontEnginePluginPrivate : public QObjectPrivate
+{
+ Q_DECLARE_PUBLIC(QFontEnginePlugin)
+
+ QString foundry;
+};
+
+/*!
+ \class QFontEnginePlugin
+ \preliminary
+ \brief The QFontEnginePlugin class is the base class for font engine factory plugins in Qt for Embedded Linux.
+ \since 4.3
+ \ingroup qws
+ \ingroup plugins
+
+ \tableofcontents
+
+ QFontEnginePlugin is provided by font engine plugins to create
+ instances of subclasses of QAbstractFontEngine.
+
+ The member functions create() and availableFontEngines() must be
+ implemented.
+
+ \sa QAbstractFontEngine, QFontEngineInfo
+*/
+
+/*!
+ Creates a font engine plugin that creates font engines with the
+ specified \a foundry and \a parent.
+*/
+QFontEnginePlugin::QFontEnginePlugin(const QString &foundry, QObject *parent)
+ : QObject(*new QFontEnginePluginPrivate, parent)
+{
+ Q_D(QFontEnginePlugin);
+ d->foundry = foundry;
+}
+
+/*!
+ Destroys this font engine plugin.
+*/
+QFontEnginePlugin::~QFontEnginePlugin()
+{
+}
+
+/*!
+ Returns a list of foundries the font engine plugin provides.
+ The default implementation returns the foundry specified with the constructor.
+*/
+QStringList QFontEnginePlugin::keys() const
+{
+ Q_D(const QFontEnginePlugin);
+ return QStringList(d->foundry);
+}
+
+/*!
+ \fn QAbstractFontEngine *QFontEnginePlugin::create(const QFontEngineInfo &info)
+
+ Implemented in subclasses to create a new font engine that provides a font that
+ matches \a info.
+*/
+
+/*!
+ \fn QList<QFontEngineInfo> QFontEnginePlugin::availableFontEngines() const
+
+ Implemented in subclasses to return a list of QFontEngineInfo objects that represents all font
+ engines the plugin can create.
+*/
+
+class QAbstractFontEnginePrivate : public QObjectPrivate
+{
+ Q_DECLARE_PUBLIC(QAbstractFontEngine)
+public:
+};
+
+//The <classname> class is|provides|contains|specifies...
+/*!
+ \class QAbstractFontEngine
+ \preliminary
+ \brief The QAbstractFontEngine class is the base class for font engine plugins in Qt for Embedded Linux.
+ \since 4.3
+ \ingroup qws
+
+ \tableofcontents
+
+ QAbstractFontEngine is implemented by font engine plugins through QFontEnginePlugin.
+
+ \sa QFontEnginePlugin, QFontEngineInfo
+*/
+
+/*!
+ \enum QAbstractFontEngine::Capability
+
+ This enum describes the capabilities of a font engine.
+
+ \value CanRenderGlyphs_Gray The font engine can render individual glyphs into 8 bpp images.
+ \value CanRenderGlyphs_Mono The font engine can render individual glyphs into 1 bpp images.
+ \value CanRenderGlyphs The font engine can render individual glyphs into images.
+ \value CanOutlineGlyphs The font engine can convert glyphs to painter paths.
+*/
+
+/*!
+ \enum QAbstractFontEngine::FontProperty
+
+ This enum describes the properties of a font provided by a font engine.
+
+ \value Ascent The ascent of the font, specified as a 26.6 fixed point value.
+ \value Descent The descent of the font, specified as a 26.6 fixed point value.
+ \value Leading The leading of the font, specified as a 26.6 fixed point value.
+ \value XHeight The 'x' height of the font, specified as a 26.6 fixed point value.
+ \value AverageCharWidth The average character width of the font, specified as a 26.6 fixed point value.
+ \value LineThickness The thickness of the underline and strikeout lines for the font, specified as a 26.6 fixed point value.
+ \value UnderlinePosition The distance from the base line to the underline position for the font, specified as a 26.6 fixed point value.
+ \value MaxCharWidth The width of the widest character in the font, specified as a 26.6 fixed point value.
+ \value MinLeftBearing The minimum left bearing of the font, specified as a 26.6 fixed point value.
+ \value MinRightBearing The maximum right bearing of the font, specified as a 26.6 fixed point value.
+ \value GlyphCount The number of glyphs in the font, specified as an integer value.
+ \value CacheGlyphsHint A boolean value specifying whether rendered glyphs should be cached by Qt.
+ \value OutlineGlyphsHint A boolean value specifying whether the font engine prefers outline drawing over image rendering for uncached glyphs.
+*/
+
+/*!
+ \enum QAbstractFontEngine::TextShapingFlag
+
+ This enum describes flags controlling conversion of characters to glyphs and their metrics.
+
+ \value RightToLeft The text is used in a right-to-left context.
+ \value ReturnDesignMetrics Return font design metrics instead of pixel metrics.
+*/
+
+/*!
+ \typedef QAbstractFontEngine::Fixed
+
+ This type is \c int, interpreted as a 26.6 fixed point value.
+*/
+
+/*!
+ \class QAbstractFontEngine::GlyphMetrics
+ \brief QAbstractFontEngine::GlyphMetrics defines the metrics of a single glyph.
+ \preliminary
+ \since 4.3
+*/
+
+/*!
+ \variable QAbstractFontEngine::GlyphMetrics::x
+
+ The horizontal offset from the origin.
+*/
+
+/*!
+ \fn QAbstractFontEngine::GlyphMetrics::GlyphMetrics()
+
+ Constructs an empty glyph metrics object with all values
+ set to zero.
+*/
+
+/*!
+ \variable QAbstractFontEngine::GlyphMetrics::y
+
+ The vertical offset from the origin (baseline).
+*/
+
+/*!
+ \variable QAbstractFontEngine::GlyphMetrics::width
+
+ The width of the glyph.
+*/
+
+/*!
+ \variable QAbstractFontEngine::GlyphMetrics::height
+
+ The height of the glyph.
+*/
+
+/*!
+ \variable QAbstractFontEngine::GlyphMetrics::advance
+
+ The advance of the glyph.
+*/
+
+/*!
+ \class QAbstractFontEngine::FixedPoint
+ \brief QAbstractFontEngine::FixedPoint defines a point in the place using 26.6 fixed point precision.
+ \preliminary
+ \since 4.3
+*/
+
+/*!
+ \variable QAbstractFontEngine::FixedPoint::x
+
+ The x coordinate of this point.
+*/
+
+/*!
+ \variable QAbstractFontEngine::FixedPoint::y
+
+ The y coordinate of this point.
+*/
+
+/*!
+ Constructs a new QAbstractFontEngine with the given \a parent.
+*/
+QAbstractFontEngine::QAbstractFontEngine(QObject *parent)
+ : QObject(*new QAbstractFontEnginePrivate, parent)
+{
+}
+
+/*!
+ Destroys this QAbstractFontEngine object.
+*/
+QAbstractFontEngine::~QAbstractFontEngine()
+{
+}
+
+/*!
+ \fn QAbstractFontEngine::Capabilities QAbstractFontEngine::capabilities() const
+
+ Implemented in subclasses to specify the font engine's capabilities. The return value
+ may be cached by the caller and is expected not to change during the lifetime of the
+ font engine.
+*/
+
+/*!
+ \fn QVariant QAbstractFontEngine::fontProperty(FontProperty property) const
+
+ Implemented in subclasses to return the value of the font attribute \a property. The return
+ value may be cached by the caller and is expected not to change during the lifetime of the font
+ engine.
+*/
+
+/*!
+ \fn bool QAbstractFontEngine::convertStringToGlyphIndices(const QChar *string, int length, uint *glyphs, int *numGlyphs, TextShapingFlags flags) const
+
+ Implemented in subclasses to convert the characters specified by \a string and \a length to
+ glyph indicies, using \a flags. The glyph indicies should be returned in the \a glyphs array
+ provided by the caller. The maximum size of \a glyphs is specified by the value pointed to by \a
+ numGlyphs. If successful, the subclass implementation sets the value pointed to by \a numGlyphs
+ to the actual number of glyph indices generated, and returns true. Otherwise, e.g. if there is
+ not enough space in the provided \a glyphs array, it should set \a numGlyphs to the number of
+ glyphs needed for the conversion and return false.
+*/
+
+/*!
+ \fn void QAbstractFontEngine::getGlyphAdvances(const uint *glyphs, int numGlyphs, Fixed *advances, TextShapingFlags flags) const
+
+ Implemented in subclasses to retrieve the advances of the array specified by \a glyphs and \a
+ numGlyphs, using \a flags. The result is returned in \a advances, which is allocated by the
+ caller and contains \a numGlyphs elements.
+*/
+
+/*!
+ \fn QAbstractFontEngine::GlyphMetrics QAbstractFontEngine::glyphMetrics(uint glyph) const
+
+ Implemented in subclass to return the metrics for \a glyph.
+*/
+
+/*!
+ Implemented in subclasses to render the specified \a glyph into a \a buffer with the given \a depth ,
+ \a bytesPerLine and \a height.
+
+ Returns true if rendering succeeded, false otherwise.
+*/
+bool QAbstractFontEngine::renderGlyph(uint glyph, int depth, int bytesPerLine, int height, uchar *buffer)
+{
+ Q_UNUSED(glyph)
+ Q_UNUSED(depth)
+ Q_UNUSED(bytesPerLine)
+ Q_UNUSED(height)
+ Q_UNUSED(buffer)
+ qWarning("QAbstractFontEngine: renderGlyph is not implemented in font plugin!");
+ return false;
+}
+
+/*!
+ Implemented in subclasses to add the outline of the glyphs specified by \a glyphs and \a
+ numGlyphs at the specified \a positions to the painter path \a path.
+*/
+void QAbstractFontEngine::addGlyphOutlinesToPath(uint *glyphs, int numGlyphs, FixedPoint *positions, QPainterPath *path)
+{
+ Q_UNUSED(glyphs)
+ Q_UNUSED(numGlyphs)
+ Q_UNUSED(positions)
+ Q_UNUSED(path)
+ qWarning("QAbstractFontEngine: addGlyphOutlinesToPath is not implemented in font plugin!");
+}
+
+/*
+bool QAbstractFontEngine::supportsExtension(Extension extension) const
+{
+ Q_UNUSED(extension)
+ return false;
+}
+
+QVariant QAbstractFontEngine::extension(Extension extension, const QVariant &argument)
+{
+ Q_UNUSED(argument)
+ Q_UNUSED(extension)
+ return QVariant();
+}
+*/
+
+QProxyFontEngine::QProxyFontEngine(QAbstractFontEngine *customEngine, const QFontDef &def)
+ : engine(customEngine)
+{
+ fontDef = def;
+ engineCapabilities = engine->capabilities();
+}
+
+QProxyFontEngine::~QProxyFontEngine()
+{
+ delete engine;
+}
+
+bool QProxyFontEngine::stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs, int *nglyphs, QTextEngine::ShaperFlags flags) const
+{
+ if (*nglyphs < len) {
+ *nglyphs = len;
+ return false;
+ }
+
+ QVarLengthArray<uint> glyphIndicies(*nglyphs);
+ if (!engine->convertStringToGlyphIndices(str, len, glyphIndicies.data(), nglyphs, QAbstractFontEngine::TextShapingFlags(int(flags))))
+ return false;
+
+ // ### use memcopy instead
+ for (int i = 0; i < *nglyphs; ++i) {
+ glyphs->glyphs[i] = glyphIndicies[i];
+ }
+ glyphs->numGlyphs = *nglyphs;
+
+ recalcAdvances(glyphs, flags);
+ return true;
+}
+
+void QProxyFontEngine::recalcAdvances(QGlyphLayout *glyphs, QTextEngine::ShaperFlags flags) const
+{
+ const int nglyphs = glyphs->numGlyphs;
+
+ QVarLengthArray<QAbstractFontEngine::Fixed> advances(nglyphs);
+ engine->getGlyphAdvances(glyphs->glyphs, nglyphs, advances.data(), QAbstractFontEngine::TextShapingFlags(int(flags)));
+
+
+ // ### use memcopy instead
+ for (int i = 0; i < nglyphs; ++i) {
+ glyphs->advances_x[i] = QFixed::fromFixed(advances[i]);
+ glyphs->advances_y[i] = 0;
+ }
+}
+
+
+static QImage alphaMapFromPath(QFontEngine *fe, glyph_t glyph)
+{
+ glyph_metrics_t gm = fe->boundingBox(glyph);
+ int glyph_x = qFloor(gm.x.toReal());
+ int glyph_y = qFloor(gm.y.toReal());
+ int glyph_width = qCeil((gm.x + gm.width).toReal()) - glyph_x;
+ int glyph_height = qCeil((gm.y + gm.height).toReal()) - glyph_y;
+
+ if (glyph_width <= 0 || glyph_height <= 0)
+ return QImage();
+ QFixedPoint pt;
+ pt.x = 0;
+ pt.y = -glyph_y; // the baseline
+ QPainterPath path;
+ QImage im(glyph_width + qAbs(glyph_x) + 4, glyph_height, QImage::Format_ARGB32_Premultiplied);
+ im.fill(Qt::transparent);
+ QPainter p(&im);
+ p.setRenderHint(QPainter::Antialiasing);
+ fe->addGlyphsToPath(&glyph, &pt, 1, &path, 0);
+ p.setPen(Qt::NoPen);
+ p.setBrush(Qt::black);
+ p.drawPath(path);
+ p.end();
+
+ QImage indexed(im.width(), im.height(), QImage::Format_Indexed8);
+ QVector<QRgb> colors(256);
+ for (int i=0; i<256; ++i)
+ colors[i] = qRgba(0, 0, 0, i);
+ indexed.setColorTable(colors);
+
+ for (int y=0; y<im.height(); ++y) {
+ uchar *dst = (uchar *) indexed.scanLine(y);
+ uint *src = (uint *) im.scanLine(y);
+ for (int x=0; x<im.width(); ++x)
+ dst[x] = qAlpha(src[x]);
+ }
+
+ return indexed;
+}
+
+
+QImage QProxyFontEngine::alphaMapForGlyph(glyph_t glyph)
+{
+ if (!(engineCapabilities & QAbstractFontEngine::CanRenderGlyphs_Gray))
+ return alphaMapFromPath(this, glyph);
+
+ QAbstractFontEngine::GlyphMetrics metrics = engine->glyphMetrics(glyph);
+ if (metrics.width <= 0 || metrics.height <= 0)
+ return QImage();
+
+ QImage img(metrics.width >> 6, metrics.height >> 6, QImage::Format_Indexed8);
+
+ // ### we should have QImage::Format_GrayScale8
+ static QVector<QRgb> colorMap;
+ if (colorMap.isEmpty()) {
+ colorMap.resize(256);
+ for (int i=0; i<256; ++i)
+ colorMap[i] = qRgba(0, 0, 0, i);
+ }
+
+ img.setColorTable(colorMap);
+
+ engine->renderGlyph(glyph, /*depth*/8, img.bytesPerLine(), img.height(), img.bits());
+
+ return img;
+}
+
+void QProxyFontEngine::addGlyphsToPath(glyph_t *glyphs, QFixedPoint *positions, int nglyphs, QPainterPath *path, QTextItem::RenderFlags flags)
+{
+ if (engineCapabilities & QAbstractFontEngine::CanOutlineGlyphs)
+ engine->addGlyphOutlinesToPath(glyphs, nglyphs, reinterpret_cast<QAbstractFontEngine::FixedPoint *>(positions), path);
+ else
+ QFontEngine::addGlyphsToPath(glyphs, positions, nglyphs, path, flags);
+}
+
+glyph_metrics_t QProxyFontEngine::boundingBox(const QGlyphLayout &glyphs)
+{
+ if (glyphs.numGlyphs == 0)
+ return glyph_metrics_t();
+
+ QFixed w = 0;
+ for (int i = 0; i < glyphs.numGlyphs; ++i)
+ w += glyphs.effectiveAdvance(i);
+
+ return glyph_metrics_t(0, -ascent(), w, ascent() + descent(), w, 0);
+}
+
+glyph_metrics_t QProxyFontEngine::boundingBox(glyph_t glyph)
+{
+ glyph_metrics_t m;
+
+ QAbstractFontEngine::GlyphMetrics metrics = engine->glyphMetrics(glyph);
+ m.x = QFixed::fromFixed(metrics.x);
+ m.y = QFixed::fromFixed(metrics.y);
+ m.width = QFixed::fromFixed(metrics.width);
+ m.height = QFixed::fromFixed(metrics.height);
+ m.xoff = QFixed::fromFixed(metrics.advance);
+
+ return m;
+}
+
+QFixed QProxyFontEngine::ascent() const
+{
+ return QFixed::fromFixed(engine->fontProperty(QAbstractFontEngine::Ascent).toInt());
+}
+
+QFixed QProxyFontEngine::descent() const
+{
+ return QFixed::fromFixed(engine->fontProperty(QAbstractFontEngine::Descent).toInt());
+}
+
+QFixed QProxyFontEngine::leading() const
+{
+ return QFixed::fromFixed(engine->fontProperty(QAbstractFontEngine::Leading).toInt());
+}
+
+QFixed QProxyFontEngine::xHeight() const
+{
+ return QFixed::fromFixed(engine->fontProperty(QAbstractFontEngine::XHeight).toInt());
+}
+
+QFixed QProxyFontEngine::averageCharWidth() const
+{
+ return QFixed::fromFixed(engine->fontProperty(QAbstractFontEngine::AverageCharWidth).toInt());
+}
+
+QFixed QProxyFontEngine::lineThickness() const
+{
+ return QFixed::fromFixed(engine->fontProperty(QAbstractFontEngine::LineThickness).toInt());
+}
+
+QFixed QProxyFontEngine::underlinePosition() const
+{
+ return QFixed::fromFixed(engine->fontProperty(QAbstractFontEngine::UnderlinePosition).toInt());
+}
+
+qreal QProxyFontEngine::maxCharWidth() const
+{
+ return QFixed::fromFixed(engine->fontProperty(QAbstractFontEngine::MaxCharWidth).toInt()).toReal();
+}
+
+qreal QProxyFontEngine::minLeftBearing() const
+{
+ return QFixed::fromFixed(engine->fontProperty(QAbstractFontEngine::MinLeftBearing).toInt()).toReal();
+}
+
+qreal QProxyFontEngine::minRightBearing() const
+{
+ return QFixed::fromFixed(engine->fontProperty(QAbstractFontEngine::MinRightBearing).toInt()).toReal();
+}
+
+int QProxyFontEngine::glyphCount() const
+{
+ return engine->fontProperty(QAbstractFontEngine::GlyphCount).toInt();
+}
+
+bool QProxyFontEngine::canRender(const QChar *string, int len)
+{
+ QVarLengthArray<uint> glyphs(len);
+ int numGlyphs = len;
+
+ if (!engine->convertStringToGlyphIndices(string, len, glyphs.data(), &numGlyphs, /*flags*/0))
+ return false;
+
+ for (int i = 0; i < numGlyphs; ++i)
+ if (!glyphs[i])
+ return false;
+
+ return true;
+}
+
+void QProxyFontEngine::draw(QPaintEngine *p, qreal _x, qreal _y, const QTextItemInt &si)
+{
+ QPaintEngineState *pState = p->state;
+ QRasterPaintEngine *paintEngine = static_cast<QRasterPaintEngine*>(p);
+
+ QTransform matrix = pState->transform();
+ matrix.translate(_x, _y);
+ QFixed x = QFixed::fromReal(matrix.dx());
+ QFixed y = QFixed::fromReal(matrix.dy());
+
+ QVarLengthArray<QFixedPoint> positions;
+ QVarLengthArray<glyph_t> glyphs;
+ getGlyphPositions(si.glyphs, matrix, si.flags, glyphs, positions);
+ if (glyphs.size() == 0)
+ return;
+
+ for(int i = 0; i < glyphs.size(); i++) {
+ QImage glyph = alphaMapForGlyph(glyphs[i]);
+ if (glyph.isNull())
+ continue;
+
+ if (glyph.format() != QImage::Format_Indexed8
+ && glyph.format() != QImage::Format_Mono)
+ continue;
+
+ QAbstractFontEngine::GlyphMetrics metrics = engine->glyphMetrics(glyphs[i]);
+
+ int depth = glyph.format() == QImage::Format_Mono ? 1 : 8;
+ paintEngine->alphaPenBlt(glyph.bits(), glyph.bytesPerLine(), depth,
+ qRound(positions[i].x + QFixed::fromFixed(metrics.x)),
+ qRound(positions[i].y + QFixed::fromFixed(metrics.y)),
+ glyph.width(), glyph.height());
+ }
+}
+
+/*
+ * This is only called when we use the proxy fontengine directly (without sharing the rendered
+ * glyphs). So we prefer outline rendering over rendering of unshared glyphs. That decision is
+ * done in qfontdatabase_qws.cpp by looking at the ShareGlyphsHint and the pixel size of the font.
+ */
+bool QProxyFontEngine::drawAsOutline() const
+{
+ if (!(engineCapabilities & QAbstractFontEngine::CanOutlineGlyphs))
+ return false;
+
+ QVariant outlineHint = engine->fontProperty(QAbstractFontEngine::OutlineGlyphsHint);
+ return !outlineHint.isValid() || outlineHint.toBool();
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/text/qabstractfontengine_qws.h b/src/gui/text/qabstractfontengine_qws.h
new file mode 100644
index 0000000000..3c6a1ea654
--- /dev/null
+++ b/src/gui/text/qabstractfontengine_qws.h
@@ -0,0 +1,221 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QABSTRACTFONTENGINE_QWS_H
+#define QABSTRACTFONTENGINE_QWS_H
+
+#include <QtCore/qobject.h>
+#include <QtCore/qhash.h>
+#include <QtCore/qvariant.h>
+#include <QtCore/qfactoryinterface.h>
+#include <QtGui/qpaintengine.h>
+#include <QtGui/qfontdatabase.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+class QFontEngineInfoPrivate;
+
+class Q_GUI_EXPORT QFontEngineInfo
+{
+public:
+ QDOC_PROPERTY(QString family READ family WRITE setFamily)
+ QDOC_PROPERTY(qreal pixelSize READ pixelSize WRITE setPixelSize)
+ QDOC_PROPERTY(int weight READ weight WRITE setWeight)
+ QDOC_PROPERTY(QFont::Style style READ style WRITE setStyle)
+ QDOC_PROPERTY(QList<QFontDatabase::WritingSystem> writingSystems READ writingSystems WRITE setWritingSystems)
+
+ QFontEngineInfo();
+ explicit QFontEngineInfo(const QString &family);
+ QFontEngineInfo(const QFontEngineInfo &other);
+ QFontEngineInfo &operator=(const QFontEngineInfo &other);
+ ~QFontEngineInfo();
+
+ void setFamily(const QString &name);
+ QString family() const;
+
+ void setPixelSize(qreal size);
+ qreal pixelSize() const;
+
+ void setWeight(int weight);
+ int weight() const;
+
+ void setStyle(QFont::Style style);
+ QFont::Style style() const;
+
+ QList<QFontDatabase::WritingSystem> writingSystems() const;
+ void setWritingSystems(const QList<QFontDatabase::WritingSystem> &writingSystems);
+
+private:
+ QFontEngineInfoPrivate *d;
+};
+
+class QAbstractFontEngine;
+
+struct Q_GUI_EXPORT QFontEngineFactoryInterface : public QFactoryInterface
+{
+ virtual QAbstractFontEngine *create(const QFontEngineInfo &info) = 0;
+ virtual QList<QFontEngineInfo> availableFontEngines() const = 0;
+};
+
+#define QFontEngineFactoryInterface_iid "com.trolltech.Qt.QFontEngineFactoryInterface"
+Q_DECLARE_INTERFACE(QFontEngineFactoryInterface, QFontEngineFactoryInterface_iid)
+
+class QFontEnginePluginPrivate;
+
+class Q_GUI_EXPORT QFontEnginePlugin : public QObject, public QFontEngineFactoryInterface
+{
+ Q_OBJECT
+ Q_INTERFACES(QFontEngineFactoryInterface:QFactoryInterface)
+public:
+ QFontEnginePlugin(const QString &foundry, QObject *parent = 0);
+ ~QFontEnginePlugin();
+
+ virtual QStringList keys() const;
+
+ virtual QAbstractFontEngine *create(const QFontEngineInfo &info) = 0;
+ virtual QList<QFontEngineInfo> availableFontEngines() const = 0;
+
+private:
+ Q_DECLARE_PRIVATE(QFontEnginePlugin)
+ Q_DISABLE_COPY(QFontEnginePlugin)
+};
+
+class QAbstractFontEnginePrivate;
+
+class Q_GUI_EXPORT QAbstractFontEngine : public QObject
+{
+ Q_OBJECT
+public:
+ enum Capability {
+ CanOutlineGlyphs = 1,
+ CanRenderGlyphs_Mono = 2,
+ CanRenderGlyphs_Gray = 4,
+ CanRenderGlyphs = CanRenderGlyphs_Mono | CanRenderGlyphs_Gray
+ };
+ Q_DECLARE_FLAGS(Capabilities, Capability)
+
+ explicit QAbstractFontEngine(QObject *parent = 0);
+ ~QAbstractFontEngine();
+
+ typedef int Fixed; // 26.6
+
+ struct FixedPoint
+ {
+ Fixed x;
+ Fixed y;
+ };
+
+ struct GlyphMetrics
+ {
+ inline GlyphMetrics()
+ : x(0), y(0), width(0), height(0),
+ advance(0) {}
+ Fixed x;
+ Fixed y;
+ Fixed width;
+ Fixed height;
+ Fixed advance;
+ };
+
+ enum FontProperty {
+ Ascent,
+ Descent,
+ Leading,
+ XHeight,
+ AverageCharWidth,
+ LineThickness,
+ UnderlinePosition,
+ MaxCharWidth,
+ MinLeftBearing,
+ MinRightBearing,
+ GlyphCount,
+
+ // hints
+ CacheGlyphsHint,
+ OutlineGlyphsHint
+ };
+
+ // keep in sync with QTextEngine::ShaperFlag!!
+ enum TextShapingFlag {
+ RightToLeft = 0x0001,
+ ReturnDesignMetrics = 0x0002
+ };
+ Q_DECLARE_FLAGS(TextShapingFlags, TextShapingFlag)
+
+ virtual Capabilities capabilities() const = 0;
+ virtual QVariant fontProperty(FontProperty property) const = 0;
+
+ virtual bool convertStringToGlyphIndices(const QChar *string, int length, uint *glyphs, int *numGlyphs, TextShapingFlags flags) const = 0;
+
+ virtual void getGlyphAdvances(const uint *glyphs, int numGlyphs, Fixed *advances, TextShapingFlags flags) const = 0;
+
+ virtual GlyphMetrics glyphMetrics(uint glyph) const = 0;
+
+ virtual bool renderGlyph(uint glyph, int depth, int bytesPerLine, int height, uchar *buffer);
+
+ virtual void addGlyphOutlinesToPath(uint *glyphs, int numGlyphs, FixedPoint *positions, QPainterPath *path);
+
+ /*
+ enum Extension {
+ GetTrueTypeTable
+ };
+
+ virtual bool supportsExtension(Extension extension) const;
+ virtual QVariant extension(Extension extension, const QVariant &argument = QVariant());
+ */
+
+private:
+ Q_DECLARE_PRIVATE(QAbstractFontEngine)
+ Q_DISABLE_COPY(QAbstractFontEngine)
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(QAbstractFontEngine::Capabilities)
+Q_DECLARE_OPERATORS_FOR_FLAGS(QAbstractFontEngine::TextShapingFlags)
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif
diff --git a/src/gui/text/qabstracttextdocumentlayout.cpp b/src/gui/text/qabstracttextdocumentlayout.cpp
new file mode 100644
index 0000000000..6ad5775b8b
--- /dev/null
+++ b/src/gui/text/qabstracttextdocumentlayout.cpp
@@ -0,0 +1,622 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <qabstracttextdocumentlayout.h>
+#include <qtextformat.h>
+#include "qtextdocument_p.h"
+#include "qtextengine_p.h"
+
+#include "qabstracttextdocumentlayout_p.h"
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QAbstractTextDocumentLayout
+ \reentrant
+
+ \brief The QAbstractTextDocumentLayout class is an abstract base
+ class used to implement custom layouts for QTextDocuments.
+
+ \ingroup text
+
+ The standard layout provided by Qt can handle simple word processing
+ including inline images, lists and tables.
+
+ Some applications, e.g., a word processor or a DTP application might need
+ more features than the ones provided by Qt's layout engine, in which case
+ you can subclass QAbstractTextDocumentLayout to provide custom layout
+ behavior for your text documents.
+
+ An instance of the QAbstractTextDocumentLayout subclass can be installed
+ on a QTextDocument object with the
+ \l{QTextDocument::}{setDocumentLayout()} function.
+
+ You can insert custom objects into a QTextDocument; see the
+ QTextObjectInterface class description for details.
+
+ \sa QTextObjectInterface
+*/
+
+/*!
+ \class QTextObjectInterface
+ \brief The QTextObjectInterface class allows drawing of
+ custom text objects in \l{QTextDocument}s.
+
+ A text object describes the structure of one or more elements in a
+ text document; for instance, images imported from HTML are
+ implemented using text objects. A text object knows how to lay out
+ and draw its elements when a document is being rendered.
+
+ Qt allows custom text objects to be inserted into a document by
+ registering a custom \l{QTextCharFormat::objectType()}{object
+ type} with QTextCharFormat. A QTextObjectInterface must also be
+ implemented for this type and be
+ \l{QAbstractTextDocumentLayout::registerHandler()}{registered}
+ with the QAbstractTextDocumentLayout of the document. When the
+ object type is encountered while rendering a QTextDocument, the
+ intrinsicSize() and drawObject() functions of the interface are
+ called.
+
+ The following list explains the required steps of inserting a
+ custom text object into a document:
+
+ \list
+ \o Choose an \a objectType. The \a objectType is an integer with a
+ value greater or equal to QTextFormat::UserObject.
+ \o Create a QTextCharFormat object and set the object type to the
+ chosen type using the setObjectType() function.
+ \o Implement the QTextObjectInterface class.
+ \o Call QAbstractTextDocumentLayout::registerHandler() with an instance of your
+ QTextObjectInterface subclass to register your object type.
+ \o Insert QChar::ObjectReplacementCharacter with the aforementioned
+ QTextCharFormat of the chosen object type into the document.
+ As mentioned, the functions of QTextObjectInterface
+ \l{QTextObjectInterface::}{intrinsicSize()} and
+ \l{QTextObjectInterface::}{drawObject()} will then be called with the
+ QTextFormat as parameter whenever the replacement character is
+ encountered.
+ \endlist
+
+ A class implementing a text object needs to inherit both QObject
+ and QTextObjectInterface. QObject must be the first class
+ inherited. For instance:
+
+ \snippet examples/richtext/textobject/svgtextobject.h 1
+
+ The data of a text object is usually stored in the QTextCharFormat
+ using QTextCharFormat::setProperty(), and then retrieved with
+ QTextCharFormat::property().
+
+ \warning Copy and Paste operations ignore custom text objects.
+
+ \sa {Text Object Example}, QTextCharFormat, QTextLayout
+*/
+
+/*!
+ \fn QTextObjectInterface::~QTextObjectInterface()
+
+ Destroys this QTextObjectInterface.
+*/
+
+/*!
+ \fn virtual QSizeF QTextObjectInterface::intrinsicSize(QTextDocument *doc, int posInDocument, const QTextFormat &format) = 0
+
+ The intrinsicSize() function returns the size of the text object
+ represented by \a format in the given document (\a doc) at the
+ given position (\a posInDocument).
+
+ The size calculated will be used for subsequent calls to
+ drawObject() for this \a format.
+
+ \sa drawObject()
+*/
+
+/*!
+ \fn virtual void QTextObjectInterface::drawObject(QPainter *painter, const QRectF &rect, QTextDocument *doc, int posInDocument, const QTextFormat &format) = 0
+
+ Draws this text object using the specified \a painter.
+
+ The size of the rectangle, \a rect, to draw in is the size
+ previously calculated by intrinsicSize(). The rectangles position
+ is relative to the \a painter.
+
+ You also get the document (\a doc) and the position (\a
+ posInDocument) of the \a format in that document.
+
+ \sa intrinsicSize()
+*/
+
+/*!
+ \fn void QAbstractTextDocumentLayout::update(const QRectF &rect)
+
+ This signal is emitted when the rectangle \a rect has been updated.
+
+ Subclasses of QAbstractTextDocumentLayout should emit this signal when
+ the layout of the contents change in order to repaint.
+*/
+
+/*!
+ \fn void QAbstractTextDocumentLayout::updateBlock(const QTextBlock &block)
+ \since 4.4
+
+ This signal is emitted when the specified \a block has been updated.
+
+ Subclasses of QAbstractTextDocumentLayout should emit this signal when
+ the layout of \a block has changed in order to repaint.
+*/
+
+/*!
+ \fn void QAbstractTextDocumentLayout::documentSizeChanged(const QSizeF &newSize)
+
+ This signal is emitted when the size of the document layout changes to
+ \a newSize.
+
+ Subclasses of QAbstractTextDocumentLayout should emit this signal when the
+ document's entire layout size changes. This signal is useful for widgets
+ that display text documents since it enables them to update their scroll
+ bars correctly.
+
+ \sa documentSize()
+*/
+
+/*!
+ \fn void QAbstractTextDocumentLayout::pageCountChanged(int newPages)
+
+ This signal is emitted when the number of pages in the layout changes;
+ \a newPages is the updated page count.
+
+ Subclasses of QAbstractTextDocumentLayout should emit this signal when
+ the number of pages in the layout has changed. Changes to the page count
+ are caused by changes to the layout or the document content itself.
+
+ \sa pageCount()
+*/
+
+/*!
+ \fn int QAbstractTextDocumentLayout::pageCount() const
+
+ Returns the number of pages contained in the layout.
+
+ \sa pageCountChanged()
+*/
+
+/*!
+ \fn QSizeF QAbstractTextDocumentLayout::documentSize() const
+
+ Returns the total size of the document's layout.
+
+ This information can be used by display widgets to update their scroll bars
+ correctly.
+
+ \sa documentSizeChanged(), QTextDocument::pageSize
+*/
+
+/*!
+ \fn void QAbstractTextDocumentLayout::draw(QPainter *painter, const PaintContext &context)
+
+ Draws the layout with the given \a painter using the given \a context.
+*/
+
+/*!
+ \fn int QAbstractTextDocumentLayout::hitTest(const QPointF &point, Qt::HitTestAccuracy accuracy) const
+
+ Returns the cursor postion for the given \a point with the specified
+ \a accuracy. Returns -1 if no valid cursor position was found.
+*/
+
+/*!
+ \fn void QAbstractTextDocumentLayout::documentChanged(int position, int charsRemoved, int charsAdded)
+
+ This function is called whenever the contents of the document change. A
+ change occurs when text is inserted, removed, or a combination of these
+ two. The change is specified by \a position, \a charsRemoved, and
+ \a charsAdded corresponding to the starting character position of the
+ change, the number of characters removed from the document, and the
+ number of characters added.
+
+ For example, when inserting the text "Hello" into an empty document,
+ \a charsRemoved would be 0 and \a charsAdded would be 5 (the length of
+ the string).
+
+ Replacing text is a combination of removing and inserting. For example, if
+ the text "Hello" gets replaced by "Hi", \a charsRemoved would be 5 and
+ \a charsAdded would be 2.
+
+ For subclasses of QAbstractTextDocumentLayout, this is the central function
+ where a large portion of the work to lay out and position document contents
+ is done.
+
+ For example, in a subclass that only arranges blocks of text, an
+ implementation of this function would have to do the following:
+
+ \list
+ \o Determine the list of changed \l{QTextBlock}(s) using the parameters
+ provided.
+ \o Each QTextBlock object's corresponding QTextLayout object needs to
+ be processed. You can access the \l{QTextBlock}'s layout using the
+ QTextBlock::layout() function. This processing should take the
+ document's page size into consideration.
+ \o If the total number of pages changed, the pageCountChanged() signal
+ should be emitted.
+ \o If the total size changed, the documentSizeChanged() signal should
+ be emitted.
+ \o The update() signal should be emitted to schedule a repaint of areas
+ in the layout that require repainting.
+ \endlist
+
+ \sa QTextLayout
+*/
+
+/*!
+ \class QAbstractTextDocumentLayout::PaintContext
+ \reentrant
+
+ \brief The QAbstractTextDocumentLayout::PaintContext class is a convenience
+ class defining the parameters used when painting a document's layout.
+
+ A paint context is used when rendering custom layouts for QTextDocuments
+ with the QAbstractTextDocumentLayout::draw() function. It is specified by
+ a \l {cursorPosition}{cursor position}, \l {palette}{default text color},
+ \l clip rectangle and a collection of \l selections.
+
+ \sa QAbstractTextDocumentLayout
+*/
+
+/*!
+ \fn QAbstractTextDocumentLayout::PaintContext::PaintContext()
+ \internal
+*/
+
+/*!
+ \variable QAbstractTextDocumentLayout::PaintContext::cursorPosition
+
+ \brief the position within the document, where the cursor line should be
+ drawn.
+
+ The default value is -1.
+*/
+
+/*!
+ \variable QAbstractTextDocumentLayout::PaintContext::palette
+
+ \brief the default color that is used for the text, when no color is
+ specified.
+
+ The default value is the application's default palette.
+*/
+
+/*!
+ \variable QAbstractTextDocumentLayout::PaintContext::clip
+
+ \brief a hint to the layout specifying the area around paragraphs, frames
+ or text require painting.
+
+ Everything outside of this rectangle does not need to be painted.
+
+ Specifying a clip rectangle can speed up drawing of large documents
+ significantly. Note that the clip rectangle is in document coordinates (not
+ in viewport coordinates). It is not a substitute for a clip region set on
+ the painter but merely a hint.
+
+ The default value is a null rectangle indicating everything needs to be
+ painted.
+*/
+
+/*!
+ \variable QAbstractTextDocumentLayout::PaintContext::selections
+
+ \brief the collection of selections that will be rendered when passing this
+ paint context to QAbstractTextDocumentLayout's draw() function.
+
+ The default value is an empty vector indicating no selection.
+*/
+
+/*!
+ \class QAbstractTextDocumentLayout::Selection
+ \reentrant
+
+ \brief The QAbstractTextDocumentLayout::Selection class is a convenience
+ class defining the parameters of a selection.
+
+ A selection can be used to specify a part of a document that should be
+ highlighted when drawing custom layouts for QTextDocuments with the
+ QAbstractTextDocumentLayout::draw() function. It is specified using
+ \l cursor and a \l format.
+
+ \sa QAbstractTextDocumentLayout, PaintContext
+*/
+
+/*!
+ \variable QAbstractTextDocumentLayout::Selection::format
+
+ \brief the format of the selection
+
+ The default value is QTextFormat::InvalidFormat.
+*/
+
+/*!
+ \variable QAbstractTextDocumentLayout::Selection::cursor
+ \brief the selection's cursor
+
+ The default value is a null cursor.
+*/
+
+/*!
+ Creates a new text document layout for the given \a document.
+*/
+QAbstractTextDocumentLayout::QAbstractTextDocumentLayout(QTextDocument *document)
+ : QObject(*new QAbstractTextDocumentLayoutPrivate, document)
+{
+ Q_D(QAbstractTextDocumentLayout);
+ d->setDocument(document);
+}
+
+/*!
+ \internal
+*/
+QAbstractTextDocumentLayout::QAbstractTextDocumentLayout(QAbstractTextDocumentLayoutPrivate &p, QTextDocument *document)
+ :QObject(p, document)
+{
+ Q_D(QAbstractTextDocumentLayout);
+ d->setDocument(document);
+}
+
+/*!
+ \internal
+*/
+QAbstractTextDocumentLayout::~QAbstractTextDocumentLayout()
+{
+}
+
+/*!
+ \fn void QAbstractTextDocumentLayout::registerHandler(int objectType, QObject *component)
+
+ Registers the given \a component as a handler for items of the given \a objectType.
+
+ \note registerHandler() has to be called once for each object type. This
+ means that there is only one handler for multiple replacement characters
+ of the same object type.
+*/
+void QAbstractTextDocumentLayout::registerHandler(int formatType, QObject *component)
+{
+ Q_D(QAbstractTextDocumentLayout);
+
+ QTextObjectInterface *iface = qobject_cast<QTextObjectInterface *>(component);
+ if (!iface)
+ return; // ### print error message on terminal?
+
+ connect(component, SIGNAL(destroyed(QObject*)), this, SLOT(_q_handlerDestroyed(QObject*)));
+
+ QTextObjectHandler h;
+ h.iface = iface;
+ h.component = component;
+ d->handlers.insert(formatType, h);
+}
+
+/*!
+ Returns a handler for objects of the given \a objectType.
+*/
+QTextObjectInterface *QAbstractTextDocumentLayout::handlerForObject(int objectType) const
+{
+ Q_D(const QAbstractTextDocumentLayout);
+
+ QTextObjectHandler handler = d->handlers.value(objectType);
+ if (!handler.component)
+ return 0;
+
+ return handler.iface;
+}
+
+/*!
+ Sets the size of the inline object \a item corresponding to the text
+ \a format.
+
+ \a posInDocument specifies the position of the object within the document.
+
+ The default implementation resizes the \a item to the size returned by
+ the object handler's intrinsicSize() function. This function is called only
+ within Qt. Subclasses can reimplement this function to customize the
+ resizing of inline objects.
+*/
+void QAbstractTextDocumentLayout::resizeInlineObject(QTextInlineObject item, int posInDocument, const QTextFormat &format)
+{
+ Q_D(QAbstractTextDocumentLayout);
+
+ QTextCharFormat f = format.toCharFormat();
+ Q_ASSERT(f.isValid());
+ QTextObjectHandler handler = d->handlers.value(f.objectType());
+ if (!handler.component)
+ return;
+
+ QSizeF s = handler.iface->intrinsicSize(document(), posInDocument, format);
+ item.setWidth(s.width());
+ item.setAscent(s.height() - 1);
+ item.setDescent(0);
+}
+
+/*!
+ Lays out the inline object \a item using the given text \a format.
+
+ \a posInDocument specifies the position of the object within the document.
+
+ The default implementation does nothing. This function is called only
+ within Qt. Subclasses can reimplement this function to customize the
+ position of inline objects.
+
+ \sa drawInlineObject()
+*/
+void QAbstractTextDocumentLayout::positionInlineObject(QTextInlineObject item, int posInDocument, const QTextFormat &format)
+{
+ Q_UNUSED(item);
+ Q_UNUSED(posInDocument);
+ Q_UNUSED(format);
+}
+
+/*!
+ \fn void QAbstractTextDocumentLayout::drawInlineObject(QPainter *painter, const QRectF &rect, QTextInlineObject object, int posInDocument, const QTextFormat &format)
+
+ This function is called to draw the inline object, \a object, with the
+ given \a painter within the rectangle specified by \a rect using the
+ specified text \a format.
+
+ \a posInDocument specifies the position of the object within the document.
+
+ The default implementation calls drawObject() on the object handlers. This
+ function is called only within Qt. Subclasses can reimplement this function
+ to customize the drawing of inline objects.
+
+ \sa draw()
+*/
+void QAbstractTextDocumentLayout::drawInlineObject(QPainter *p, const QRectF &rect, QTextInlineObject item,
+ int posInDocument, const QTextFormat &format)
+{
+ Q_UNUSED(item);
+ Q_D(QAbstractTextDocumentLayout);
+
+ QTextCharFormat f = format.toCharFormat();
+ Q_ASSERT(f.isValid());
+ QTextObjectHandler handler = d->handlers.value(f.objectType());
+ if (!handler.component)
+ return;
+
+ handler.iface->drawObject(p, rect, document(), posInDocument, format);
+}
+
+void QAbstractTextDocumentLayoutPrivate::_q_handlerDestroyed(QObject *obj)
+{
+ HandlerHash::Iterator it = handlers.begin();
+ while (it != handlers.end())
+ if ((*it).component == obj)
+ it = handlers.erase(it);
+ else
+ ++it;
+}
+
+/*!
+ \internal
+
+ Returns the index of the format at position \a pos.
+*/
+int QAbstractTextDocumentLayout::formatIndex(int pos)
+{
+ QTextDocumentPrivate *pieceTable = qobject_cast<QTextDocument *>(parent())->docHandle();
+ return pieceTable->find(pos).value()->format;
+}
+
+/*!
+ \fn QTextCharFormat QAbstractTextDocumentLayout::format(int position)
+
+ Returns the character format that is applicable at the given \a position.
+*/
+QTextCharFormat QAbstractTextDocumentLayout::format(int pos)
+{
+ QTextDocumentPrivate *pieceTable = qobject_cast<QTextDocument *>(parent())->docHandle();
+ int idx = pieceTable->find(pos).value()->format;
+ return pieceTable->formatCollection()->charFormat(idx);
+}
+
+
+
+/*!
+ Returns the text document that this layout is operating on.
+*/
+QTextDocument *QAbstractTextDocumentLayout::document() const
+{
+ Q_D(const QAbstractTextDocumentLayout);
+ return d->document;
+}
+
+/*!
+ \fn QString QAbstractTextDocumentLayout::anchorAt(const QPointF &position) const
+
+ Returns the reference of the anchor the given \a position, or an empty
+ string if no anchor exists at that point.
+*/
+QString QAbstractTextDocumentLayout::anchorAt(const QPointF& pos) const
+{
+ int cursorPos = hitTest(pos, Qt::ExactHit);
+ if (cursorPos == -1)
+ return QString();
+
+ QTextDocumentPrivate *pieceTable = qobject_cast<const QTextDocument *>(parent())->docHandle();
+ QTextDocumentPrivate::FragmentIterator it = pieceTable->find(cursorPos);
+ QTextCharFormat fmt = pieceTable->formatCollection()->charFormat(it->format);
+ return fmt.anchorHref();
+}
+
+/*!
+ \fn QRectF QAbstractTextDocumentLayout::frameBoundingRect(QTextFrame *frame) const
+
+ Returns the bounding rectangle of \a frame.
+*/
+
+/*!
+ \fn QRectF QAbstractTextDocumentLayout::blockBoundingRect(const QTextBlock &block) const
+
+ Returns the bounding rectangle of \a block.
+*/
+
+/*!
+ Sets the paint device used for rendering the document's layout to the given
+ \a device.
+
+ \sa paintDevice()
+*/
+void QAbstractTextDocumentLayout::setPaintDevice(QPaintDevice *device)
+{
+ Q_D(QAbstractTextDocumentLayout);
+ d->paintDevice = device;
+}
+
+/*!
+ Returns the paint device used to render the document's layout.
+
+ \sa setPaintDevice()
+*/
+QPaintDevice *QAbstractTextDocumentLayout::paintDevice() const
+{
+ Q_D(const QAbstractTextDocumentLayout);
+ return d->paintDevice;
+}
+
+QT_END_NAMESPACE
+
+#include "moc_qabstracttextdocumentlayout.cpp"
diff --git a/src/gui/text/qabstracttextdocumentlayout.h b/src/gui/text/qabstracttextdocumentlayout.h
new file mode 100644
index 0000000000..4c376cc39a
--- /dev/null
+++ b/src/gui/text/qabstracttextdocumentlayout.h
@@ -0,0 +1,149 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QABSTRACTTEXTDOCUMENTLAYOUT_H
+#define QABSTRACTTEXTDOCUMENTLAYOUT_H
+
+#include <QtCore/qobject.h>
+#include <QtGui/qtextlayout.h>
+#include <QtGui/qtextdocument.h>
+#include <QtGui/qtextcursor.h>
+#include <QtGui/qpalette.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+class QAbstractTextDocumentLayoutPrivate;
+class QTextBlock;
+class QTextObjectInterface;
+class QTextFrame;
+
+class Q_GUI_EXPORT QAbstractTextDocumentLayout : public QObject
+{
+ Q_OBJECT
+ Q_DECLARE_PRIVATE(QAbstractTextDocumentLayout)
+
+public:
+ explicit QAbstractTextDocumentLayout(QTextDocument *doc);
+ ~QAbstractTextDocumentLayout();
+
+ struct Selection
+ {
+ QTextCursor cursor;
+ QTextCharFormat format;
+ };
+
+ struct PaintContext
+ {
+ PaintContext()
+ : cursorPosition(-1)
+ {}
+ int cursorPosition;
+ QPalette palette;
+ QRectF clip;
+ QVector<Selection> selections;
+ };
+
+ virtual void draw(QPainter *painter, const PaintContext &context) = 0;
+ virtual int hitTest(const QPointF &point, Qt::HitTestAccuracy accuracy) const = 0;
+ QString anchorAt(const QPointF& pos) const;
+
+ virtual int pageCount() const = 0;
+ virtual QSizeF documentSize() const = 0;
+
+ virtual QRectF frameBoundingRect(QTextFrame *frame) const = 0;
+ virtual QRectF blockBoundingRect(const QTextBlock &block) const = 0;
+
+ void setPaintDevice(QPaintDevice *device);
+ QPaintDevice *paintDevice() const;
+
+ QTextDocument *document() const;
+
+ void registerHandler(int objectType, QObject *component);
+ QTextObjectInterface *handlerForObject(int objectType) const;
+
+Q_SIGNALS:
+ void update(const QRectF & = QRectF(0., 0., 1000000000., 1000000000.));
+ void updateBlock(const QTextBlock &block);
+ void documentSizeChanged(const QSizeF &newSize);
+ void pageCountChanged(int newPages);
+
+protected:
+ QAbstractTextDocumentLayout(QAbstractTextDocumentLayoutPrivate &, QTextDocument *);
+
+ virtual void documentChanged(int from, int charsRemoved, int charsAdded) = 0;
+
+ virtual void resizeInlineObject(QTextInlineObject item, int posInDocument, const QTextFormat &format);
+ virtual void positionInlineObject(QTextInlineObject item, int posInDocument, const QTextFormat &format);
+ virtual void drawInlineObject(QPainter *painter, const QRectF &rect, QTextInlineObject object, int posInDocument, const QTextFormat &format);
+
+ int formatIndex(int pos);
+ QTextCharFormat format(int pos);
+
+private:
+ friend class QTextDocument;
+ friend class QTextDocumentPrivate;
+ friend class QTextEngine;
+ friend class QTextLayout;
+ friend class QTextLine;
+ Q_PRIVATE_SLOT(d_func(), void _q_handlerDestroyed(QObject *obj))
+ Q_PRIVATE_SLOT(d_func(), int _q_dynamicPageCountSlot())
+ Q_PRIVATE_SLOT(d_func(), QSizeF _q_dynamicDocumentSizeSlot())
+};
+
+class Q_GUI_EXPORT QTextObjectInterface
+{
+public:
+ virtual ~QTextObjectInterface() {}
+ virtual QSizeF intrinsicSize(QTextDocument *doc, int posInDocument, const QTextFormat &format) = 0;
+ virtual void drawObject(QPainter *painter, const QRectF &rect, QTextDocument *doc, int posInDocument, const QTextFormat &format) = 0;
+};
+
+Q_DECLARE_INTERFACE(QTextObjectInterface, "com.trolltech.Qt.QTextObjectInterface")
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QABSTRACTTEXTDOCUMENTLAYOUT_H
diff --git a/src/gui/text/qabstracttextdocumentlayout_p.h b/src/gui/text/qabstracttextdocumentlayout_p.h
new file mode 100644
index 0000000000..5674e17b58
--- /dev/null
+++ b/src/gui/text/qabstracttextdocumentlayout_p.h
@@ -0,0 +1,100 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QABSTRACTTEXTDOCUMENTLAYOUT_P_H
+#define QABSTRACTTEXTDOCUMENTLAYOUT_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/qobject_p.h"
+#include "QtCore/qhash.h"
+
+QT_BEGIN_NAMESPACE
+
+struct QTextObjectHandler
+{
+ QTextObjectHandler() : iface(0) {}
+ QTextObjectInterface *iface;
+ QPointer<QObject> component;
+};
+typedef QHash<int, QTextObjectHandler> HandlerHash;
+
+class QAbstractTextDocumentLayoutPrivate : public QObjectPrivate
+{
+public:
+ Q_DECLARE_PUBLIC(QAbstractTextDocumentLayout)
+
+ inline QAbstractTextDocumentLayoutPrivate()
+ : paintDevice(0) {}
+
+ inline void setDocument(QTextDocument *doc) {
+ document = doc;
+ docPrivate = 0;
+ if (doc)
+ docPrivate = doc->docHandle();
+ }
+
+ inline int _q_dynamicPageCountSlot() const
+ { return q_func()->pageCount(); }
+ inline QSizeF _q_dynamicDocumentSizeSlot() const
+ { return q_func()->documentSize(); }
+
+ HandlerHash handlers;
+
+ void _q_handlerDestroyed(QObject *obj);
+ QPaintDevice *paintDevice;
+
+ QTextDocument *document;
+ QTextDocumentPrivate *docPrivate;
+};
+
+QT_END_NAMESPACE
+
+#endif // QABSTRACTTEXTDOCUMENTLAYOUT_P_H
diff --git a/src/gui/text/qcssparser.cpp b/src/gui/text/qcssparser.cpp
new file mode 100644
index 0000000000..db1e781c4d
--- /dev/null
+++ b/src/gui/text/qcssparser.cpp
@@ -0,0 +1,2808 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qcssparser_p.h"
+
+#include <qdebug.h>
+#include <qcolor.h>
+#include <qfont.h>
+#include <qfileinfo.h>
+#include <qfontmetrics.h>
+#include <qbrush.h>
+#include <qimagereader.h>
+
+#ifndef QT_NO_CSSPARSER
+
+QT_BEGIN_NAMESPACE
+
+#include "qcssscanner.cpp"
+
+using namespace QCss;
+
+const char *Scanner::tokenName(QCss::TokenType t)
+{
+ switch (t) {
+ case NONE: return "NONE";
+ case S: return "S";
+ case CDO: return "CDO";
+ case CDC: return "CDC";
+ case INCLUDES: return "INCLUDES";
+ case DASHMATCH: return "DASHMATCH";
+ case LBRACE: return "LBRACE";
+ case PLUS: return "PLUS";
+ case GREATER: return "GREATER";
+ case COMMA: return "COMMA";
+ case STRING: return "STRING";
+ case INVALID: return "INVALID";
+ case IDENT: return "IDENT";
+ case HASH: return "HASH";
+ case ATKEYWORD_SYM: return "ATKEYWORD_SYM";
+ case EXCLAMATION_SYM: return "EXCLAMATION_SYM";
+ case LENGTH: return "LENGTH";
+ case PERCENTAGE: return "PERCENTAGE";
+ case NUMBER: return "NUMBER";
+ case FUNCTION: return "FUNCTION";
+ case COLON: return "COLON";
+ case SEMICOLON: return "SEMICOLON";
+ case RBRACE: return "RBRACE";
+ case SLASH: return "SLASH";
+ case MINUS: return "MINUS";
+ case DOT: return "DOT";
+ case STAR: return "STAR";
+ case LBRACKET: return "LBRACKET";
+ case RBRACKET: return "RBRACKET";
+ case EQUAL: return "EQUAL";
+ case LPAREN: return "LPAREN";
+ case RPAREN: return "RPAREN";
+ case OR: return "OR";
+ }
+ return "";
+}
+
+struct QCssKnownValue
+{
+ const char *name;
+ quint64 id;
+};
+
+static const QCssKnownValue properties[NumProperties - 1] = {
+ { "-qt-background-role", QtBackgroundRole },
+ { "-qt-block-indent", QtBlockIndent },
+ { "-qt-list-indent", QtListIndent },
+ { "-qt-paragraph-type", QtParagraphType },
+ { "-qt-style-features", QtStyleFeatures },
+ { "-qt-table-type", QtTableType },
+ { "-qt-user-state", QtUserState },
+ { "alternate-background-color", QtAlternateBackground },
+ { "background", Background },
+ { "background-attachment", BackgroundAttachment },
+ { "background-clip", BackgroundClip },
+ { "background-color", BackgroundColor },
+ { "background-image", BackgroundImage },
+ { "background-origin", BackgroundOrigin },
+ { "background-position", BackgroundPosition },
+ { "background-repeat", BackgroundRepeat },
+ { "border", Border },
+ { "border-bottom", BorderBottom },
+ { "border-bottom-color", BorderBottomColor },
+ { "border-bottom-left-radius", BorderBottomLeftRadius },
+ { "border-bottom-right-radius", BorderBottomRightRadius },
+ { "border-bottom-style", BorderBottomStyle },
+ { "border-bottom-width", BorderBottomWidth },
+ { "border-color", BorderColor },
+ { "border-image", BorderImage },
+ { "border-left", BorderLeft },
+ { "border-left-color", BorderLeftColor },
+ { "border-left-style", BorderLeftStyle },
+ { "border-left-width", BorderLeftWidth },
+ { "border-radius", BorderRadius },
+ { "border-right", BorderRight },
+ { "border-right-color", BorderRightColor },
+ { "border-right-style", BorderRightStyle },
+ { "border-right-width", BorderRightWidth },
+ { "border-style", BorderStyles },
+ { "border-top", BorderTop },
+ { "border-top-color", BorderTopColor },
+ { "border-top-left-radius", BorderTopLeftRadius },
+ { "border-top-right-radius", BorderTopRightRadius },
+ { "border-top-style", BorderTopStyle },
+ { "border-top-width", BorderTopWidth },
+ { "border-width", BorderWidth },
+ { "bottom", Bottom },
+ { "color", Color },
+ { "float", Float },
+ { "font", Font },
+ { "font-family", FontFamily },
+ { "font-size", FontSize },
+ { "font-style", FontStyle },
+ { "font-variant", FontVariant },
+ { "font-weight", FontWeight },
+ { "height", Height },
+ { "image", QtImage },
+ { "image-position", QtImageAlignment },
+ { "left", Left },
+ { "list-style", ListStyle },
+ { "list-style-type", ListStyleType },
+ { "margin" , Margin },
+ { "margin-bottom", MarginBottom },
+ { "margin-left", MarginLeft },
+ { "margin-right", MarginRight },
+ { "margin-top", MarginTop },
+ { "max-height", MaximumHeight },
+ { "max-width", MaximumWidth },
+ { "min-height", MinimumHeight },
+ { "min-width", MinimumWidth },
+ { "outline", Outline },
+ { "outline-bottom-left-radius", OutlineBottomLeftRadius },
+ { "outline-bottom-right-radius", OutlineBottomRightRadius },
+ { "outline-color", OutlineColor },
+ { "outline-offset", OutlineOffset },
+ { "outline-radius", OutlineRadius },
+ { "outline-style", OutlineStyle },
+ { "outline-top-left-radius", OutlineTopLeftRadius },
+ { "outline-top-right-radius", OutlineTopRightRadius },
+ { "outline-width", OutlineWidth },
+ { "padding", Padding },
+ { "padding-bottom", PaddingBottom },
+ { "padding-left", PaddingLeft },
+ { "padding-right", PaddingRight },
+ { "padding-top", PaddingTop },
+ { "page-break-after", PageBreakAfter },
+ { "page-break-before", PageBreakBefore },
+ { "position", Position },
+ { "right", Right },
+ { "selection-background-color", QtSelectionBackground },
+ { "selection-color", QtSelectionForeground },
+ { "spacing", QtSpacing },
+ { "subcontrol-origin", QtOrigin },
+ { "subcontrol-position", QtPosition },
+ { "text-align", TextAlignment },
+ { "text-decoration", TextDecoration },
+ { "text-indent", TextIndent },
+ { "text-transform", TextTransform },
+ { "text-underline-style", TextUnderlineStyle },
+ { "top", Top },
+ { "vertical-align", VerticalAlignment },
+ { "white-space", Whitespace },
+ { "width", Width }
+};
+
+static const QCssKnownValue values[NumKnownValues - 1] = {
+ { "active", Value_Active },
+ { "alternate-base", Value_AlternateBase },
+ { "always", Value_Always },
+ { "auto", Value_Auto },
+ { "base", Value_Base },
+ { "bold", Value_Bold },
+ { "bottom", Value_Bottom },
+ { "bright-text", Value_BrightText },
+ { "button", Value_Button },
+ { "button-text", Value_ButtonText },
+ { "center", Value_Center },
+ { "circle", Value_Circle },
+ { "dark", Value_Dark },
+ { "dashed", Value_Dashed },
+ { "decimal", Value_Decimal },
+ { "disabled", Value_Disabled },
+ { "disc", Value_Disc },
+ { "dot-dash", Value_DotDash },
+ { "dot-dot-dash", Value_DotDotDash },
+ { "dotted", Value_Dotted },
+ { "double", Value_Double },
+ { "groove", Value_Groove },
+ { "highlight", Value_Highlight },
+ { "highlighted-text", Value_HighlightedText },
+ { "inset", Value_Inset },
+ { "italic", Value_Italic },
+ { "large", Value_Large },
+ { "left", Value_Left },
+ { "light", Value_Light },
+ { "line-through", Value_LineThrough },
+ { "link", Value_Link },
+ { "link-visited", Value_LinkVisited },
+ { "lower-alpha", Value_LowerAlpha },
+ { "lowercase", Value_Lowercase },
+ { "medium", Value_Medium },
+ { "mid", Value_Mid },
+ { "middle", Value_Middle },
+ { "midlight", Value_Midlight },
+ { "native", Value_Native },
+ { "none", Value_None },
+ { "normal", Value_Normal },
+ { "nowrap", Value_NoWrap },
+ { "oblique", Value_Oblique },
+ { "off", Value_Off },
+ { "on", Value_On },
+ { "outset", Value_Outset },
+ { "overline", Value_Overline },
+ { "pre", Value_Pre },
+ { "pre-wrap", Value_PreWrap },
+ { "ridge", Value_Ridge },
+ { "right", Value_Right },
+ { "selected", Value_Selected },
+ { "shadow", Value_Shadow },
+ { "small" , Value_Small },
+ { "small-caps", Value_SmallCaps },
+ { "solid", Value_Solid },
+ { "square", Value_Square },
+ { "sub", Value_Sub },
+ { "super", Value_Super },
+ { "text", Value_Text },
+ { "top", Value_Top },
+ { "transparent", Value_Transparent },
+ { "underline", Value_Underline },
+ { "upper-alpha", Value_UpperAlpha },
+ { "uppercase", Value_Uppercase },
+ { "wave", Value_Wave },
+ { "window", Value_Window },
+ { "window-text", Value_WindowText },
+ { "x-large", Value_XLarge },
+ { "xx-large", Value_XXLarge }
+};
+
+QString Value::toString() const
+{
+ static int indexOfId[NumKnownValues - 1];
+ static bool hasCachedIndexes = false;
+
+ if (type == KnownIdentifier) {
+ if (!hasCachedIndexes) {
+ for (int i = 0; i < NumKnownValues - 1; ++i)
+ indexOfId[values[i].id] = i;
+
+ hasCachedIndexes = true;
+ }
+
+ return QLatin1String(values[indexOfId[variant.toInt()]].name);
+ } else {
+ return variant.toString();
+ }
+}
+
+static const QCssKnownValue pseudos[NumPseudos - 1] = {
+ { "active", PseudoClass_Active },
+ { "adjoins-item", PseudoClass_Item },
+ { "alternate", PseudoClass_Alternate },
+ { "bottom", PseudoClass_Bottom },
+ { "checked", PseudoClass_Checked },
+ { "closable", PseudoClass_Closable },
+ { "closed", PseudoClass_Closed },
+ { "default", PseudoClass_Default },
+ { "disabled", PseudoClass_Disabled },
+ { "edit-focus", PseudoClass_EditFocus },
+ { "editable", PseudoClass_Editable },
+ { "enabled", PseudoClass_Enabled },
+ { "exclusive", PseudoClass_Exclusive },
+ { "first", PseudoClass_First },
+ { "flat", PseudoClass_Flat },
+ { "floatable", PseudoClass_Floatable },
+ { "focus", PseudoClass_Focus },
+ { "has-children", PseudoClass_Children },
+ { "has-siblings", PseudoClass_Sibling },
+ { "horizontal", PseudoClass_Horizontal },
+ { "hover", PseudoClass_Hover },
+ { "indeterminate" , PseudoClass_Indeterminate },
+ { "last", PseudoClass_Last },
+ { "left", PseudoClass_Left },
+ { "maximized", PseudoClass_Maximized },
+ { "middle", PseudoClass_Middle },
+ { "minimized", PseudoClass_Minimized },
+ { "movable", PseudoClass_Movable },
+ { "next-selected", PseudoClass_NextSelected },
+ { "no-frame", PseudoClass_Frameless },
+ { "non-exclusive", PseudoClass_NonExclusive },
+ { "off", PseudoClass_Unchecked },
+ { "on", PseudoClass_Checked },
+ { "only-one", PseudoClass_OnlyOne },
+ { "open", PseudoClass_Open },
+ { "pressed", PseudoClass_Pressed },
+ { "previous-selected", PseudoClass_PreviousSelected },
+ { "read-only", PseudoClass_ReadOnly },
+ { "right", PseudoClass_Right },
+ { "selected", PseudoClass_Selected },
+ { "top", PseudoClass_Top },
+ { "unchecked" , PseudoClass_Unchecked },
+ { "vertical", PseudoClass_Vertical },
+ { "window", PseudoClass_Window }
+};
+
+static const QCssKnownValue origins[NumKnownOrigins - 1] = {
+ { "border", Origin_Border },
+ { "content", Origin_Content },
+ { "margin", Origin_Margin }, // not in css
+ { "padding", Origin_Padding }
+};
+
+static const QCssKnownValue repeats[NumKnownRepeats - 1] = {
+ { "no-repeat", Repeat_None },
+ { "repeat-x", Repeat_X },
+ { "repeat-xy", Repeat_XY },
+ { "repeat-y", Repeat_Y }
+};
+
+static const QCssKnownValue tileModes[NumKnownTileModes - 1] = {
+ { "repeat", TileMode_Repeat },
+ { "round", TileMode_Round },
+ { "stretch", TileMode_Stretch },
+};
+
+static const QCssKnownValue positions[NumKnownPositionModes - 1] = {
+ { "absolute", PositionMode_Absolute },
+ { "fixed", PositionMode_Fixed },
+ { "relative", PositionMode_Relative },
+ { "static", PositionMode_Static }
+};
+
+static const QCssKnownValue attachments[NumKnownAttachments - 1] = {
+ { "fixed", Attachment_Fixed },
+ { "scroll", Attachment_Scroll }
+};
+
+static const QCssKnownValue styleFeatures[NumKnownStyleFeatures - 1] = {
+ { "background-color", StyleFeature_BackgroundColor },
+ { "background-gradient", StyleFeature_BackgroundGradient },
+ { "none", StyleFeature_None }
+};
+
+static bool operator<(const QString &name, const QCssKnownValue &prop)
+{
+ return QString::compare(name, QLatin1String(prop.name), Qt::CaseInsensitive) < 0;
+}
+
+static bool operator<(const QCssKnownValue &prop, const QString &name)
+{
+ return QString::compare(QLatin1String(prop.name), name, Qt::CaseInsensitive) < 0;
+}
+
+static quint64 findKnownValue(const QString &name, const QCssKnownValue *start, int numValues)
+{
+ const QCssKnownValue *end = &start[numValues - 1];
+ const QCssKnownValue *prop = qBinaryFind(start, end, name);
+ if (prop == end)
+ return 0;
+ return prop->id;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Value Extractor
+ValueExtractor::ValueExtractor(const QVector<Declaration> &decls, const QPalette &pal)
+: declarations(decls), adjustment(0), fontExtracted(false), pal(pal)
+{
+}
+
+LengthData ValueExtractor::lengthValue(const Value& v)
+{
+ QString s = v.variant.toString();
+ s.reserve(s.length());
+ LengthData data;
+ data.unit = LengthData::None;
+ if (s.endsWith(QLatin1String("px"), Qt::CaseInsensitive))
+ data.unit = LengthData::Px;
+ else if (s.endsWith(QLatin1String("ex"), Qt::CaseInsensitive))
+ data.unit = LengthData::Ex;
+ else if (s.endsWith(QLatin1String("em"), Qt::CaseInsensitive))
+ data.unit = LengthData::Em;
+
+ if (data.unit != LengthData::None)
+ s.chop(2);
+
+ bool ok;
+ data.number = s.toDouble(&ok);
+ if (!ok)
+ data.number = 0;
+ return data;
+}
+
+static int lengthValueFromData(const LengthData& data, const QFont& f)
+{
+ if (data.unit == LengthData::Ex)
+ return qRound(QFontMetrics(f).xHeight() * data.number);
+ else if (data.unit == LengthData::Em)
+ return qRound(QFontMetrics(f).height() * data.number);
+ return qRound(data.number);
+}
+
+int ValueExtractor::lengthValue(const Declaration &decl)
+{
+ if (decl.d->parsed.isValid())
+ return lengthValueFromData(qvariant_cast<LengthData>(decl.d->parsed), f);
+ if (decl.d->values.count() < 1)
+ return 0;
+ LengthData data = lengthValue(decl.d->values.at(0));
+ decl.d->parsed = qVariantFromValue<LengthData>(data);
+ return lengthValueFromData(data,f);
+}
+
+void ValueExtractor::lengthValues(const Declaration &decl, int *m)
+{
+ if (decl.d->parsed.isValid()) {
+ QList<QVariant> v = decl.d->parsed.toList();
+ for (int i = 0; i < 4; i++)
+ m[i] = lengthValueFromData(qvariant_cast<LengthData>(v.at(i)), f);
+ return;
+ }
+
+ LengthData datas[4];
+ int i;
+ for (i = 0; i < qMin(decl.d->values.count(), 4); i++)
+ datas[i] = lengthValue(decl.d->values[i]);
+
+ if (i == 0) {
+ LengthData zero = {0.0, LengthData::None};
+ datas[0] = datas[1] = datas[2] = datas[3] = zero;
+ } else if (i == 1) {
+ datas[3] = datas[2] = datas[1] = datas[0];
+ } else if (i == 2) {
+ datas[2] = datas[0];
+ datas[3] = datas[1];
+ } else if (i == 3) {
+ datas[3] = datas[1];
+ }
+
+ QList<QVariant> v;
+ for (i = 0; i < 4; i++) {
+ v += qVariantFromValue<LengthData>(datas[i]);
+ m[i] = lengthValueFromData(datas[i], f);
+ }
+ decl.d->parsed = v;
+}
+
+bool ValueExtractor::extractGeometry(int *w, int *h, int *minw, int *minh, int *maxw, int *maxh)
+{
+ extractFont();
+ bool hit = false;
+ for (int i = 0; i < declarations.count(); i++) {
+ const Declaration &decl = declarations.at(i);
+ switch (decl.d->propertyId) {
+ case Width: *w = lengthValue(decl); break;
+ case Height: *h = lengthValue(decl); break;
+ case MinimumWidth: *minw = lengthValue(decl); break;
+ case MinimumHeight: *minh = lengthValue(decl); break;
+ case MaximumWidth: *maxw = lengthValue(decl); break;
+ case MaximumHeight: *maxh = lengthValue(decl); break;
+ default: continue;
+ }
+ hit = true;
+ }
+
+ return hit;
+}
+
+bool ValueExtractor::extractPosition(int *left, int *top, int *right, int *bottom, QCss::Origin *origin,
+ Qt::Alignment *position, QCss::PositionMode *mode, Qt::Alignment *textAlignment)
+{
+ extractFont();
+ bool hit = false;
+ for (int i = 0; i < declarations.count(); i++) {
+ const Declaration &decl = declarations.at(i);
+ switch (decl.d->propertyId) {
+ case Left: *left = lengthValue(decl); break;
+ case Top: *top = lengthValue(decl); break;
+ case Right: *right = lengthValue(decl); break;
+ case Bottom: *bottom = lengthValue(decl); break;
+ case QtOrigin: *origin = decl.originValue(); break;
+ case QtPosition: *position = decl.alignmentValue(); break;
+ case TextAlignment: *textAlignment = decl.alignmentValue(); break;
+ case Position: *mode = decl.positionValue(); break;
+ default: continue;
+ }
+ hit = true;
+ }
+
+ return hit;
+}
+
+bool ValueExtractor::extractBox(int *margins, int *paddings, int *spacing)
+{
+ extractFont();
+ bool hit = false;
+ for (int i = 0; i < declarations.count(); i++) {
+ const Declaration &decl = declarations.at(i);
+ switch (decl.d->propertyId) {
+ case PaddingLeft: paddings[LeftEdge] = lengthValue(decl); break;
+ case PaddingRight: paddings[RightEdge] = lengthValue(decl); break;
+ case PaddingTop: paddings[TopEdge] = lengthValue(decl); break;
+ case PaddingBottom: paddings[BottomEdge] = lengthValue(decl); break;
+ case Padding: lengthValues(decl, paddings); break;
+
+ case MarginLeft: margins[LeftEdge] = lengthValue(decl); break;
+ case MarginRight: margins[RightEdge] = lengthValue(decl); break;
+ case MarginTop: margins[TopEdge] = lengthValue(decl); break;
+ case MarginBottom: margins[BottomEdge] = lengthValue(decl); break;
+ case Margin: lengthValues(decl, margins); break;
+ case QtSpacing: if (spacing) *spacing = lengthValue(decl); break;
+
+ default: continue;
+ }
+ hit = true;
+ }
+
+ return hit;
+}
+
+int ValueExtractor::extractStyleFeatures()
+{
+ int features = StyleFeature_None;
+ for (int i = 0; i < declarations.count(); i++) {
+ const Declaration &decl = declarations.at(i);
+ if (decl.d->propertyId == QtStyleFeatures)
+ features = decl.styleFeaturesValue();
+ }
+ return features;
+}
+
+QSize ValueExtractor::sizeValue(const Declaration &decl)
+{
+ if (decl.d->parsed.isValid()) {
+ QList<QVariant> v = decl.d->parsed.toList();
+ return QSize(lengthValueFromData(qvariant_cast<LengthData>(v.at(0)), f),
+ lengthValueFromData(qvariant_cast<LengthData>(v.at(1)), f));
+ }
+
+ LengthData x[2] = { {0, LengthData::None }, {0, LengthData::None} };
+ if (decl.d->values.count() > 0)
+ x[0] = lengthValue(decl.d->values.at(0));
+ if (decl.d->values.count() > 1)
+ x[1] = lengthValue(decl.d->values.at(1));
+ else
+ x[1] = x[0];
+ QList<QVariant> v;
+ v << qVariantFromValue<LengthData>(x[0]) << qVariantFromValue<LengthData>(x[1]);
+ decl.d->parsed = v;
+ return QSize(lengthValueFromData(x[0], f), lengthValueFromData(x[1], f));
+}
+
+void ValueExtractor::sizeValues(const Declaration &decl, QSize *radii)
+{
+ radii[0] = sizeValue(decl);
+ for (int i = 1; i < 4; i++)
+ radii[i] = radii[0];
+}
+
+bool ValueExtractor::extractBorder(int *borders, QBrush *colors, BorderStyle *styles,
+ QSize *radii)
+{
+ extractFont();
+ bool hit = false;
+ for (int i = 0; i < declarations.count(); i++) {
+ const Declaration &decl = declarations.at(i);
+ switch (decl.d->propertyId) {
+ case BorderLeftWidth: borders[LeftEdge] = lengthValue(decl); break;
+ case BorderRightWidth: borders[RightEdge] = lengthValue(decl); break;
+ case BorderTopWidth: borders[TopEdge] = lengthValue(decl); break;
+ case BorderBottomWidth: borders[BottomEdge] = lengthValue(decl); break;
+ case BorderWidth: lengthValues(decl, borders); break;
+
+ case BorderLeftColor: colors[LeftEdge] = decl.brushValue(pal); break;
+ case BorderRightColor: colors[RightEdge] = decl.brushValue(pal); break;
+ case BorderTopColor: colors[TopEdge] = decl.brushValue(pal); break;
+ case BorderBottomColor: colors[BottomEdge] = decl.brushValue(pal); break;
+ case BorderColor: decl.brushValues(colors, pal); break;
+
+ case BorderTopStyle: styles[TopEdge] = decl.styleValue(); break;
+ case BorderBottomStyle: styles[BottomEdge] = decl.styleValue(); break;
+ case BorderLeftStyle: styles[LeftEdge] = decl.styleValue(); break;
+ case BorderRightStyle: styles[RightEdge] = decl.styleValue(); break;
+ case BorderStyles: decl.styleValues(styles); break;
+
+ case BorderTopLeftRadius: radii[0] = sizeValue(decl); break;
+ case BorderTopRightRadius: radii[1] = sizeValue(decl); break;
+ case BorderBottomLeftRadius: radii[2] = sizeValue(decl); break;
+ case BorderBottomRightRadius: radii[3] = sizeValue(decl); break;
+ case BorderRadius: sizeValues(decl, radii); break;
+
+ case BorderLeft:
+ borderValue(decl, &borders[LeftEdge], &styles[LeftEdge], &colors[LeftEdge]);
+ break;
+ case BorderTop:
+ borderValue(decl, &borders[TopEdge], &styles[TopEdge], &colors[TopEdge]);
+ break;
+ case BorderRight:
+ borderValue(decl, &borders[RightEdge], &styles[RightEdge], &colors[RightEdge]);
+ break;
+ case BorderBottom:
+ borderValue(decl, &borders[BottomEdge], &styles[BottomEdge], &colors[BottomEdge]);
+ break;
+ case Border:
+ borderValue(decl, &borders[LeftEdge], &styles[LeftEdge], &colors[LeftEdge]);
+ borders[TopEdge] = borders[RightEdge] = borders[BottomEdge] = borders[LeftEdge];
+ styles[TopEdge] = styles[RightEdge] = styles[BottomEdge] = styles[LeftEdge];
+ colors[TopEdge] = colors[RightEdge] = colors[BottomEdge] = colors[LeftEdge];
+ break;
+
+ default: continue;
+ }
+ hit = true;
+ }
+
+ return hit;
+}
+
+bool ValueExtractor::extractOutline(int *borders, QBrush *colors, BorderStyle *styles,
+ QSize *radii, int *offsets)
+{
+ extractFont();
+ bool hit = false;
+ for (int i = 0; i < declarations.count(); i++) {
+ const Declaration &decl = declarations.at(i);
+ switch (decl.d->propertyId) {
+ case OutlineWidth: lengthValues(decl, borders); break;
+ case OutlineColor: decl.brushValues(colors, pal); break;
+ case OutlineStyle: decl.styleValues(styles); break;
+
+ case OutlineTopLeftRadius: radii[0] = sizeValue(decl); break;
+ case OutlineTopRightRadius: radii[1] = sizeValue(decl); break;
+ case OutlineBottomLeftRadius: radii[2] = sizeValue(decl); break;
+ case OutlineBottomRightRadius: radii[3] = sizeValue(decl); break;
+ case OutlineRadius: sizeValues(decl, radii); break;
+ case OutlineOffset: lengthValues(decl, offsets); break;
+
+ case Outline:
+ borderValue(decl, &borders[LeftEdge], &styles[LeftEdge], &colors[LeftEdge]);
+ borders[TopEdge] = borders[RightEdge] = borders[BottomEdge] = borders[LeftEdge];
+ styles[TopEdge] = styles[RightEdge] = styles[BottomEdge] = styles[LeftEdge];
+ colors[TopEdge] = colors[RightEdge] = colors[BottomEdge] = colors[LeftEdge];
+ break;
+
+ default: continue;
+ }
+ hit = true;
+ }
+
+ return hit;
+}
+
+static Qt::Alignment parseAlignment(const Value *values, int count)
+{
+ Qt::Alignment a[2] = { 0, 0 };
+ for (int i = 0; i < qMin(2, count); i++) {
+ if (values[i].type != Value::KnownIdentifier)
+ break;
+ switch (values[i].variant.toInt()) {
+ case Value_Left: a[i] = Qt::AlignLeft; break;
+ case Value_Right: a[i] = Qt::AlignRight; break;
+ case Value_Top: a[i] = Qt::AlignTop; break;
+ case Value_Bottom: a[i] = Qt::AlignBottom; break;
+ case Value_Center: a[i] = Qt::AlignCenter; break;
+ default: break;
+ }
+ }
+
+ if (a[0] == Qt::AlignCenter && a[1] != 0 && a[1] != Qt::AlignCenter)
+ a[0] = (a[1] == Qt::AlignLeft || a[1] == Qt::AlignRight) ? Qt::AlignVCenter : Qt::AlignHCenter;
+ if ((a[1] == 0 || a[1] == Qt::AlignCenter) && a[0] != Qt::AlignCenter)
+ a[1] = (a[0] == Qt::AlignLeft || a[0] == Qt::AlignRight) ? Qt::AlignVCenter : Qt::AlignHCenter;
+ return a[0] | a[1];
+}
+
+static ColorData parseColorValue(Value v)
+{
+ if (v.type == Value::Identifier || v.type == Value::String) {
+ v.variant.convert(QVariant::Color);
+ v.type = Value::Color;
+ }
+
+ if (v.type == Value::Color)
+ return qvariant_cast<QColor>(v.variant);
+
+ if (v.type == Value::KnownIdentifier && v.variant.toInt() == Value_Transparent)
+ return QColor(Qt::transparent);
+
+ if (v.type != Value::Function)
+ return ColorData();
+
+ QStringList lst = v.variant.toStringList();
+ if (lst.count() != 2)
+ return ColorData();
+
+ if ((lst.at(0).compare(QLatin1String("palette"), Qt::CaseInsensitive)) == 0) {
+ int role = findKnownValue(lst.at(1).trimmed(), values, NumKnownValues);
+ if (role >= Value_FirstColorRole && role <= Value_LastColorRole)
+ return (QPalette::ColorRole)(role-Value_FirstColorRole);
+
+ return ColorData();
+ }
+
+ bool rgb = lst.at(0).startsWith(QLatin1String("rgb"));
+
+ Parser p(lst.at(1));
+ if (!p.testExpr())
+ return ColorData();
+
+ QVector<Value> colorDigits;
+ if (!p.parseExpr(&colorDigits))
+ return ColorData();
+
+ for (int i = 0; i < qMin(colorDigits.count(), 7); i += 2) {
+ if (colorDigits.at(i).type == Value::Percentage) {
+ colorDigits[i].variant = colorDigits.at(i).variant.toDouble() * 255. / 100.;
+ colorDigits[i].type = Value::Number;
+ } else if (colorDigits.at(i).type != Value::Number) {
+ return ColorData();
+ }
+ }
+
+ int v1 = colorDigits.at(0).variant.toInt();
+ int v2 = colorDigits.at(2).variant.toInt();
+ int v3 = colorDigits.at(4).variant.toInt();
+ int alpha = colorDigits.count() >= 7 ? colorDigits.at(6).variant.toInt() : 255;
+
+ return rgb ? QColor::fromRgb(v1, v2, v3, alpha)
+ : QColor::fromHsv(v1, v2, v3, alpha);
+}
+
+static QColor colorFromData(const ColorData& c, const QPalette &pal)
+{
+ if (c.type == ColorData::Color) {
+ return c.color;
+ } else if (c.type == ColorData::Role) {
+ return pal.color(c.role);
+ }
+ return QColor();
+}
+
+static BrushData parseBrushValue(const Value &v, const QPalette &pal)
+{
+ ColorData c = parseColorValue(v);
+ if (c.type == ColorData::Color) {
+ return QBrush(c.color);
+ } else if (c.type == ColorData::Role) {
+ return c.role;
+ }
+
+ if (v.type != Value::Function)
+ return BrushData();
+
+ QStringList lst = v.variant.toStringList();
+ if (lst.count() != 2)
+ return BrushData();
+
+ QStringList gradFuncs;
+ gradFuncs << QLatin1String("qlineargradient") << QLatin1String("qradialgradient") << QLatin1String("qconicalgradient") << QLatin1String("qgradient");
+ int gradType = -1;
+
+ if ((gradType = gradFuncs.indexOf(lst.at(0).toLower())) == -1)
+ return BrushData();
+
+ QHash<QString, qreal> vars;
+ QVector<QGradientStop> stops;
+
+ int spread = -1;
+ QStringList spreads;
+ spreads << QLatin1String("pad") << QLatin1String("reflect") << QLatin1String("repeat");
+
+ bool dependsOnThePalette = false;
+ Parser parser(lst.at(1));
+ while (parser.hasNext()) {
+ parser.skipSpace();
+ if (!parser.test(IDENT))
+ return BrushData();
+ QString attr = parser.lexem();
+ parser.skipSpace();
+ if (!parser.test(COLON))
+ return BrushData();
+ parser.skipSpace();
+ if (attr.compare(QLatin1String("stop"), Qt::CaseInsensitive) == 0) {
+ Value stop, color;
+ parser.next();
+ if (!parser.parseTerm(&stop)) return BrushData();
+ parser.skipSpace();
+ parser.next();
+ if (!parser.parseTerm(&color)) return BrushData();
+ ColorData cd = parseColorValue(color);
+ if(cd.type == ColorData::Role)
+ dependsOnThePalette = true;
+ stops.append(QGradientStop(stop.variant.toDouble(), colorFromData(cd, pal)));
+ } else {
+ parser.next();
+ Value value;
+ parser.parseTerm(&value);
+ if (attr.compare(QLatin1String("spread"), Qt::CaseInsensitive) == 0) {
+ spread = spreads.indexOf(value.variant.toString());
+ } else {
+ vars[attr] = value.variant.toString().toDouble();
+ }
+ }
+ parser.skipSpace();
+ parser.test(COMMA);
+ }
+
+ if (gradType == 0) {
+ QLinearGradient lg(vars.value(QLatin1String("x1")), vars.value(QLatin1String("y1")),
+ vars.value(QLatin1String("x2")), vars.value(QLatin1String("y2")));
+ lg.setCoordinateMode(QGradient::ObjectBoundingMode);
+ lg.setStops(stops);
+ if (spread != -1)
+ lg.setSpread(QGradient::Spread(spread));
+ BrushData bd = QBrush(lg);
+ if (dependsOnThePalette)
+ bd.type = BrushData::DependsOnThePalette;
+ return bd;
+ }
+
+ if (gradType == 1) {
+ QRadialGradient rg(vars.value(QLatin1String("cx")), vars.value(QLatin1String("cy")),
+ vars.value(QLatin1String("radius")), vars.value(QLatin1String("fx")),
+ vars.value(QLatin1String("fy")));
+ rg.setCoordinateMode(QGradient::ObjectBoundingMode);
+ rg.setStops(stops);
+ if (spread != -1)
+ rg.setSpread(QGradient::Spread(spread));
+ BrushData bd = QBrush(rg);
+ if (dependsOnThePalette)
+ bd.type = BrushData::DependsOnThePalette;
+ return bd;
+ }
+
+ if (gradType == 2) {
+ QConicalGradient cg(vars.value(QLatin1String("cx")), vars.value(QLatin1String("cy")),
+ vars.value(QLatin1String("angle")));
+ cg.setCoordinateMode(QGradient::ObjectBoundingMode);
+ cg.setStops(stops);
+ if (spread != -1)
+ cg.setSpread(QGradient::Spread(spread));
+ BrushData bd = QBrush(cg);
+ if (dependsOnThePalette)
+ bd.type = BrushData::DependsOnThePalette;
+ return bd;
+ }
+
+ return BrushData();
+}
+
+static QBrush brushFromData(const BrushData& c, const QPalette &pal)
+{
+ if (c.type == BrushData::Role) {
+ return pal.color(c.role);
+ } else {
+ return c.brush;
+ }
+}
+
+static BorderStyle parseStyleValue(Value v)
+{
+ if (v.type == Value::KnownIdentifier) {
+ switch (v.variant.toInt()) {
+ case Value_None:
+ return BorderStyle_None;
+ case Value_Dotted:
+ return BorderStyle_Dotted;
+ case Value_Dashed:
+ return BorderStyle_Dashed;
+ case Value_Solid:
+ return BorderStyle_Solid;
+ case Value_Double:
+ return BorderStyle_Double;
+ case Value_DotDash:
+ return BorderStyle_DotDash;
+ case Value_DotDotDash:
+ return BorderStyle_DotDotDash;
+ case Value_Groove:
+ return BorderStyle_Groove;
+ case Value_Ridge:
+ return BorderStyle_Ridge;
+ case Value_Inset:
+ return BorderStyle_Inset;
+ case Value_Outset:
+ return BorderStyle_Outset;
+ case Value_Native:
+ return BorderStyle_Native;
+ default:
+ break;
+ }
+ }
+
+ return BorderStyle_Unknown;
+}
+
+void ValueExtractor::borderValue(const Declaration &decl, int *width, QCss::BorderStyle *style, QBrush *color)
+{
+ if (decl.d->parsed.isValid()) {
+ BorderData data = qvariant_cast<BorderData>(decl.d->parsed);
+ *width = lengthValueFromData(data.width, f);
+ *style = data.style;
+ *color = brushFromData(data.color, pal);
+ return;
+ }
+
+ *width = 0;
+ *style = BorderStyle_None;
+ *color = QColor();
+
+ if (decl.d->values.isEmpty())
+ return;
+
+ BorderData data;
+ data.width.number = 0;
+ data.width.unit = LengthData::None;
+ data.style = BorderStyle_None;
+
+ int i = 0;
+ if (decl.d->values.at(i).type == Value::Length || decl.d->values.at(i).type == Value::Number) {
+ data.width = lengthValue(decl.d->values.at(i));
+ *width = lengthValueFromData(data.width, f);
+ if (++i >= decl.d->values.count()) {
+ decl.d->parsed = qVariantFromValue<BorderData>(data);
+ return;
+ }
+ }
+
+ data.style = parseStyleValue(decl.d->values.at(i));
+ if (data.style != BorderStyle_Unknown) {
+ *style = data.style;
+ if (++i >= decl.d->values.count()) {
+ decl.d->parsed = qVariantFromValue<BorderData>(data);
+ return;
+ }
+ } else {
+ data.style = BorderStyle_None;
+ }
+
+ data.color = parseBrushValue(decl.d->values.at(i), pal);
+ *color = brushFromData(data.color, pal);
+ if (data.color.type != BrushData::DependsOnThePalette)
+ decl.d->parsed = qVariantFromValue<BorderData>(data);
+}
+
+static void parseShorthandBackgroundProperty(const QVector<Value> &values, BrushData *brush, QString *image, Repeat *repeat, Qt::Alignment *alignment, const QPalette &pal)
+{
+ *brush = BrushData();
+ *image = QString();
+ *repeat = Repeat_XY;
+ *alignment = Qt::AlignTop | Qt::AlignLeft;
+
+ for (int i = 0; i < values.count(); ++i) {
+ const Value &v = values.at(i);
+ if (v.type == Value::Uri) {
+ *image = v.variant.toString();
+ continue;
+ } else if (v.type == Value::KnownIdentifier && v.variant.toInt() == Value_None) {
+ *image = QString();
+ continue;
+ } else if (v.type == Value::KnownIdentifier && v.variant.toInt() == Value_Transparent) {
+ *brush = QBrush(Qt::transparent);
+ }
+
+ Repeat repeatAttempt = static_cast<Repeat>(findKnownValue(v.variant.toString(),
+ repeats, NumKnownRepeats));
+ if (repeatAttempt != Repeat_Unknown) {
+ *repeat = repeatAttempt;
+ continue;
+ }
+
+ if (v.type == Value::KnownIdentifier) {
+ const int start = i;
+ int count = 1;
+ if (i < values.count() - 1
+ && values.at(i + 1).type == Value::KnownIdentifier) {
+ ++i;
+ ++count;
+ }
+ Qt::Alignment a = parseAlignment(values.constData() + start, count);
+ if (int(a) != 0) {
+ *alignment = a;
+ continue;
+ }
+ i -= count - 1;
+ }
+
+ *brush = parseBrushValue(v, pal);
+ }
+}
+
+bool ValueExtractor::extractBackground(QBrush *brush, QString *image, Repeat *repeat,
+ Qt::Alignment *alignment, Origin *origin, Attachment *attachment,
+ Origin *clip)
+{
+ bool hit = false;
+ for (int i = 0; i < declarations.count(); ++i) {
+ const Declaration &decl = declarations.at(i);
+ if (decl.d->values.isEmpty())
+ continue;
+ const Value &val = decl.d->values.at(0);
+ switch (decl.d->propertyId) {
+ case BackgroundColor:
+ *brush = decl.brushValue();
+ break;
+ case BackgroundImage:
+ if (val.type == Value::Uri)
+ *image = val.variant.toString();
+ break;
+ case BackgroundRepeat:
+ if (decl.d->parsed.isValid()) {
+ *repeat = static_cast<Repeat>(decl.d->parsed.toInt());
+ } else {
+ *repeat = static_cast<Repeat>(findKnownValue(val.variant.toString(),
+ repeats, NumKnownRepeats));
+ decl.d->parsed = *repeat;
+ }
+ break;
+ case BackgroundPosition:
+ *alignment = decl.alignmentValue();
+ break;
+ case BackgroundOrigin:
+ *origin = decl.originValue();
+ break;
+ case BackgroundClip:
+ *clip = decl.originValue();
+ break;
+ case Background:
+ if (decl.d->parsed.isValid()) {
+ BackgroundData data = qvariant_cast<BackgroundData>(decl.d->parsed);
+ *brush = brushFromData(data.brush, pal);
+ *image = data.image;
+ *repeat = data.repeat;
+ *alignment = data.alignment;
+ } else {
+ BrushData brushData;
+ parseShorthandBackgroundProperty(decl.d->values, &brushData, image, repeat, alignment, pal);
+ *brush = brushFromData(brushData, pal);
+ if (brushData.type != BrushData::DependsOnThePalette) {
+#if defined Q_CC_MSVC && _MSC_VER <= 1300
+ BackgroundData data;
+ data.brush = brushData;
+ data.image = *image;
+ data.repeat = *repeat;
+ data.alignment = *alignment;
+#else
+ BackgroundData data = { brushData, *image, *repeat, *alignment };
+#endif
+ decl.d->parsed = qVariantFromValue<BackgroundData>(data);
+ }
+ }
+ break;
+ case BackgroundAttachment:
+ *attachment = decl.attachmentValue();
+ break;
+ default: continue;
+ }
+ hit = true;
+ }
+ return hit;
+}
+
+static bool setFontSizeFromValue(Value value, QFont *font, int *fontSizeAdjustment)
+{
+ if (value.type == Value::KnownIdentifier) {
+ bool valid = true;
+ switch (value.variant.toInt()) {
+ case Value_Small: *fontSizeAdjustment = -1; break;
+ case Value_Medium: *fontSizeAdjustment = 0; break;
+ case Value_Large: *fontSizeAdjustment = 1; break;
+ case Value_XLarge: *fontSizeAdjustment = 2; break;
+ case Value_XXLarge: *fontSizeAdjustment = 3; break;
+ default: valid = false; break;
+ }
+ return valid;
+ }
+ if (value.type != Value::Length)
+ return false;
+
+ bool valid = false;
+ QString s = value.variant.toString();
+ if (s.endsWith(QLatin1String("pt"), Qt::CaseInsensitive)) {
+ s.chop(2);
+ value.variant = s;
+ if (value.variant.convert(QVariant::Double)) {
+ font->setPointSizeF(value.variant.toDouble());
+ valid = true;
+ }
+ } else if (s.endsWith(QLatin1String("px"), Qt::CaseInsensitive)) {
+ s.chop(2);
+ value.variant = s;
+ if (value.variant.convert(QVariant::Int)) {
+ font->setPixelSize(value.variant.toInt());
+ valid = true;
+ }
+ }
+ return valid;
+}
+
+static bool setFontStyleFromValue(const Value &value, QFont *font)
+{
+ if (value.type != Value::KnownIdentifier)
+ return false ;
+ switch (value.variant.toInt()) {
+ case Value_Normal: font->setStyle(QFont::StyleNormal); return true;
+ case Value_Italic: font->setStyle(QFont::StyleItalic); return true;
+ case Value_Oblique: font->setStyle(QFont::StyleOblique); return true;
+ default: break;
+ }
+ return false;
+}
+
+static bool setFontWeightFromValue(const Value &value, QFont *font)
+{
+ if (value.type == Value::KnownIdentifier) {
+ switch (value.variant.toInt()) {
+ case Value_Normal: font->setWeight(QFont::Normal); return true;
+ case Value_Bold: font->setWeight(QFont::Bold); return true;
+ default: break;
+ }
+ return false;
+ }
+ if (value.type != Value::Number)
+ return false;
+ font->setWeight(qMin(value.variant.toInt() / 8, 99));
+ return true;
+}
+
+static bool setFontFamilyFromValues(const QVector<Value> &values, QFont *font)
+{
+ QString family;
+ for (int i = 0; i < values.count(); ++i) {
+ const Value &v = values.at(i);
+ if (v.type == Value::TermOperatorComma)
+ break;
+ const QString str = v.variant.toString();
+ if (str.isEmpty())
+ break;
+ family += str;
+ family += QLatin1Char(' ');
+ }
+ family = family.simplified();
+ if (family.isEmpty())
+ return false;
+ font->setFamily(family);
+ return true;
+}
+
+static void setTextDecorationFromValues(const QVector<Value> &values, QFont *font)
+{
+ for (int i = 0; i < values.count(); ++i) {
+ if (values.at(i).type != Value::KnownIdentifier)
+ continue;
+ switch (values.at(i).variant.toInt()) {
+ case Value_Underline: font->setUnderline(true); break;
+ case Value_Overline: font->setOverline(true); break;
+ case Value_LineThrough: font->setStrikeOut(true); break;
+ case Value_None:
+ font->setUnderline(false);
+ font->setOverline(false);
+ font->setStrikeOut(false);
+ break;
+ default: break;
+ }
+ }
+}
+
+static void parseShorthandFontProperty(const QVector<Value> &values, QFont *font, int *fontSizeAdjustment)
+{
+ font->setStyle(QFont::StyleNormal);
+ font->setWeight(QFont::Normal);
+ *fontSizeAdjustment = 0;
+
+ int i = 0;
+ while (i < values.count()) {
+ if (setFontStyleFromValue(values.at(i), font)
+ || setFontWeightFromValue(values.at(i), font))
+ ++i;
+ else
+ break;
+ }
+
+ if (i < values.count()) {
+ setFontSizeFromValue(values.at(i), font, fontSizeAdjustment);
+ ++i;
+ }
+
+ if (i < values.count()) {
+ QString fam = values.at(i).variant.toString();
+ if (!fam.isEmpty())
+ font->setFamily(fam);
+ }
+}
+
+static void setFontVariantFromValue(const Value &value, QFont *font)
+{
+ if (value.type == Value::KnownIdentifier) {
+ switch (value.variant.toInt()) {
+ case Value_Normal: font->setCapitalization(QFont::MixedCase); break;
+ case Value_SmallCaps: font->setCapitalization(QFont::SmallCaps); break;
+ default: break;
+ }
+ }
+}
+
+static void setTextTransformFromValue(const Value &value, QFont *font)
+{
+ if (value.type == Value::KnownIdentifier) {
+ switch (value.variant.toInt()) {
+ case Value_None: font->setCapitalization(QFont::MixedCase); break;
+ case Value_Uppercase: font->setCapitalization(QFont::AllUppercase); break;
+ case Value_Lowercase: font->setCapitalization(QFont::AllLowercase); break;
+ default: break;
+ }
+ }
+}
+
+bool ValueExtractor::extractFont(QFont *font, int *fontSizeAdjustment)
+{
+ if (fontExtracted) {
+ *font = f;
+ *fontSizeAdjustment = adjustment;
+ return fontExtracted == 1;
+ }
+
+ bool hit = false;
+ for (int i = 0; i < declarations.count(); ++i) {
+ const Declaration &decl = declarations.at(i);
+ if (decl.d->values.isEmpty())
+ continue;
+ const Value &val = decl.d->values.at(0);
+ switch (decl.d->propertyId) {
+ case FontSize: setFontSizeFromValue(val, font, fontSizeAdjustment); break;
+ case FontStyle: setFontStyleFromValue(val, font); break;
+ case FontWeight: setFontWeightFromValue(val, font); break;
+ case FontFamily: setFontFamilyFromValues(decl.d->values, font); break;
+ case TextDecoration: setTextDecorationFromValues(decl.d->values, font); break;
+ case Font: parseShorthandFontProperty(decl.d->values, font, fontSizeAdjustment); break;
+ case FontVariant: setFontVariantFromValue(val, font); break;
+ case TextTransform: setTextTransformFromValue(val, font); break;
+ default: continue;
+ }
+ hit = true;
+ }
+
+ f = *font;
+ adjustment = *fontSizeAdjustment;
+ fontExtracted = hit ? 1 : 2;
+ return hit;
+}
+
+bool ValueExtractor::extractPalette(QBrush *fg, QBrush *sfg, QBrush *sbg, QBrush *abg)
+{
+ bool hit = false;
+ for (int i = 0; i < declarations.count(); ++i) {
+ const Declaration &decl = declarations.at(i);
+ switch (decl.d->propertyId) {
+ case Color: *fg = decl.brushValue(pal); break;
+ case QtSelectionForeground: *sfg = decl.brushValue(pal); break;
+ case QtSelectionBackground: *sbg = decl.brushValue(pal); break;
+ case QtAlternateBackground: *abg = decl.brushValue(pal); break;
+ default: continue;
+ }
+ hit = true;
+ }
+ return hit;
+}
+
+void ValueExtractor::extractFont()
+{
+ if (fontExtracted)
+ return;
+ int dummy = -255;
+ extractFont(&f, &dummy);
+}
+
+bool ValueExtractor::extractImage(QIcon *icon, Qt::Alignment *a, QSize *size)
+{
+ bool hit = false;
+ for (int i = 0; i < declarations.count(); ++i) {
+ const Declaration &decl = declarations.at(i);
+ switch (decl.d->propertyId) {
+ case QtImage:
+ *icon = decl.iconValue();
+ if (decl.d->values.count() > 0 && decl.d->values.at(0).type == Value::Uri) {
+ // try to pull just the size from the image...
+ QImageReader imageReader(decl.d->values.at(0).variant.toString());
+ if ((*size = imageReader.size()).isNull()) {
+ // but we'll have to load the whole image if the
+ // format doesn't support just reading the size
+ *size = imageReader.read().size();
+ }
+ }
+ break;
+ case QtImageAlignment: *a = decl.alignmentValue(); break;
+ default: continue;
+ }
+ hit = true;
+ }
+ return hit;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Declaration
+QColor Declaration::colorValue(const QPalette &pal) const
+{
+ if (d->values.count() != 1)
+ return QColor();
+
+ if (d->parsed.isValid()) {
+ if (d->parsed.type() == QVariant::Color)
+ return qvariant_cast<QColor>(d->parsed);
+ if (d->parsed.type() == QVariant::Int)
+ return pal.color((QPalette::ColorRole)(d->parsed.toInt()));
+ }
+
+ ColorData color = parseColorValue(d->values.at(0));
+ if(color.type == ColorData::Role) {
+ d->parsed = qVariantFromValue<int>(color.role);
+ return pal.color((QPalette::ColorRole)(color.role));
+ } else {
+ d->parsed = qVariantFromValue<QColor>(color.color);
+ return color.color;
+ }
+}
+
+QBrush Declaration::brushValue(const QPalette &pal) const
+{
+ if (d->values.count() != 1)
+ return QBrush();
+
+ if (d->parsed.isValid()) {
+ if (d->parsed.type() == QVariant::Brush)
+ return qvariant_cast<QBrush>(d->parsed);
+ if (d->parsed.type() == QVariant::Int)
+ return pal.color((QPalette::ColorRole)(d->parsed.toInt()));
+ }
+
+ BrushData data = parseBrushValue(d->values.at(0), pal);
+
+ if(data.type == BrushData::Role) {
+ d->parsed = qVariantFromValue<int>(data.role);
+ return pal.color((QPalette::ColorRole)(data.role));
+ } else {
+ if (data.type != BrushData::DependsOnThePalette)
+ d->parsed = qVariantFromValue<QBrush>(data.brush);
+ return data.brush;
+ }
+}
+
+void Declaration::brushValues(QBrush *c, const QPalette &pal) const
+{
+ int needParse = 0x1f; // bits 0..3 say if we should parse the corresponding value.
+ // the bit 4 say we need to update d->parsed
+ int i = 0;
+ if (d->parsed.isValid()) {
+ needParse = 0;
+ QList<QVariant> v = d->parsed.toList();
+ for (i = 0; i < qMin(v.count(), 4); i++) {
+ if (v.at(i).type() == QVariant::Brush) {
+ c[i] = qvariant_cast<QBrush>(v.at(i));
+ } else if (v.at(i).type() == QVariant::Int) {
+ c[i] = pal.color((QPalette::ColorRole)(v.at(i).toInt()));
+ } else {
+ needParse |= (1<<i);
+ }
+ }
+ }
+ if (needParse != 0) {
+ QList<QVariant> v;
+ for (i = 0; i < qMin(d->values.count(), 4); i++) {
+ if (!(needParse & (1<<i)))
+ continue;
+ BrushData data = parseBrushValue(d->values.at(i), pal);
+ if(data.type == BrushData::Role) {
+ v += qVariantFromValue<int>(data.role);
+ c[i] = pal.color((QPalette::ColorRole)(data.role));
+ } else {
+ if (data.type != BrushData::DependsOnThePalette) {
+ v += qVariantFromValue<QBrush>(data.brush);
+ } else {
+ v += QVariant();
+ }
+ c[i] = data.brush;
+ }
+ }
+ if (needParse & 0x10)
+ d->parsed = v;
+ }
+ if (i == 0) c[0] = c[1] = c[2] = c[3] = QBrush();
+ else if (i == 1) c[3] = c[2] = c[1] = c[0];
+ else if (i == 2) c[2] = c[0], c[3] = c[1];
+ else if (i == 3) c[3] = c[1];
+}
+
+bool Declaration::realValue(qreal *real, const char *unit) const
+{
+ if (d->values.count() != 1)
+ return false;
+ const Value &v = d->values.at(0);
+ if (unit && v.type != Value::Length)
+ return false;
+ QString s = v.variant.toString();
+ if (unit) {
+ if (!s.endsWith(QLatin1String(unit), Qt::CaseInsensitive))
+ return false;
+ s.chop(qstrlen(unit));
+ }
+ bool ok = false;
+ qreal val = s.toDouble(&ok);
+ if (ok)
+ *real = val;
+ return ok;
+}
+
+static bool intValueHelper(const Value &v, int *i, const char *unit)
+{
+ if (unit && v.type != Value::Length)
+ return false;
+ QString s = v.variant.toString();
+ if (unit) {
+ if (!s.endsWith(QLatin1String(unit), Qt::CaseInsensitive))
+ return false;
+ s.chop(qstrlen(unit));
+ }
+ bool ok = false;
+ int val = s.toInt(&ok);
+ if (ok)
+ *i = val;
+ return ok;
+}
+
+bool Declaration::intValue(int *i, const char *unit) const
+{
+ if (d->values.count() != 1)
+ return false;
+ return intValueHelper(d->values.at(0), i, unit);
+}
+
+QSize Declaration::sizeValue() const
+{
+ if (d->parsed.isValid())
+ return qvariant_cast<QSize>(d->parsed);
+
+ int x[2] = { 0, 0 };
+ if (d->values.count() > 0)
+ intValueHelper(d->values.at(0), &x[0], "px");
+ if (d->values.count() > 1)
+ intValueHelper(d->values.at(1), &x[1], "px");
+ else
+ x[1] = x[0];
+ QSize size(x[0], x[1]);
+ d->parsed = qVariantFromValue<QSize>(size);
+ return size;
+}
+
+QRect Declaration::rectValue() const
+{
+ if (d->values.count() != 1)
+ return QRect();
+
+ if (d->parsed.isValid())
+ return qvariant_cast<QRect>(d->parsed);
+
+ const Value &v = d->values.at(0);
+ if (v.type != Value::Function)
+ return QRect();
+ QStringList func = v.variant.toStringList();
+ if (func.count() != 2 || func.at(0).compare(QLatin1String("rect")) != 0)
+ return QRect();
+ QStringList args = func[1].split(QLatin1String(" "), QString::SkipEmptyParts);
+ if (args.count() != 4)
+ return QRect();
+ QRect rect(args[0].toInt(), args[1].toInt(), args[2].toInt(), args[3].toInt());
+ d->parsed = qVariantFromValue<QRect>(rect);
+ return rect;
+}
+
+void Declaration::colorValues(QColor *c, const QPalette &pal) const
+{
+ int i;
+ if (d->parsed.isValid()) {
+ QList<QVariant> v = d->parsed.toList();
+ for (i = 0; i < qMin(d->values.count(), 4); i++) {
+ if (v.at(i).type() == QVariant::Color) {
+ c[i] = qvariant_cast<QColor>(v.at(i));
+ } else {
+ c[i] = pal.color((QPalette::ColorRole)(v.at(i).toInt()));
+ }
+ }
+ } else {
+ QList<QVariant> v;
+ for (i = 0; i < qMin(d->values.count(), 4); i++) {
+ ColorData color = parseColorValue(d->values.at(i));
+ if(color.type == ColorData::Role) {
+ v += qVariantFromValue<int>(color.role);
+ c[i] = pal.color((QPalette::ColorRole)(color.role));
+ } else {
+ v += qVariantFromValue<QColor>(color.color);
+ c[i] = color.color;
+ }
+ }
+ d->parsed = v;
+ }
+
+ if (i == 0) c[0] = c[1] = c[2] = c[3] = QColor();
+ else if (i == 1) c[3] = c[2] = c[1] = c[0];
+ else if (i == 2) c[2] = c[0], c[3] = c[1];
+ else if (i == 3) c[3] = c[1];
+}
+
+BorderStyle Declaration::styleValue() const
+{
+ if (d->values.count() != 1)
+ return BorderStyle_None;
+ return parseStyleValue(d->values.at(0));
+}
+
+void Declaration::styleValues(BorderStyle *s) const
+{
+ int i;
+ for (i = 0; i < qMin(d->values.count(), 4); i++)
+ s[i] = parseStyleValue(d->values.at(i));
+ if (i == 0) s[0] = s[1] = s[2] = s[3] = BorderStyle_None;
+ else if (i == 1) s[3] = s[2] = s[1] = s[0];
+ else if (i == 2) s[2] = s[0], s[3] = s[1];
+ else if (i == 3) s[3] = s[1];
+}
+
+Repeat Declaration::repeatValue() const
+{
+ if (d->parsed.isValid())
+ return static_cast<Repeat>(d->parsed.toInt());
+ if (d->values.count() != 1)
+ return Repeat_Unknown;
+ int v = findKnownValue(d->values.at(0).variant.toString(),
+ repeats, NumKnownRepeats);
+ d->parsed = v;
+ return static_cast<Repeat>(v);
+}
+
+Origin Declaration::originValue() const
+{
+ if (d->parsed.isValid())
+ return static_cast<Origin>(d->parsed.toInt());
+ if (d->values.count() != 1)
+ return Origin_Unknown;
+ int v = findKnownValue(d->values.at(0).variant.toString(),
+ origins, NumKnownOrigins);
+ d->parsed = v;
+ return static_cast<Origin>(v);
+}
+
+PositionMode Declaration::positionValue() const
+{
+ if (d->parsed.isValid())
+ return static_cast<PositionMode>(d->parsed.toInt());
+ if (d->values.count() != 1)
+ return PositionMode_Unknown;
+ int v = findKnownValue(d->values.at(0).variant.toString(),
+ positions, NumKnownPositionModes);
+ d->parsed = v;
+ return static_cast<PositionMode>(v);
+}
+
+Attachment Declaration::attachmentValue() const
+{
+ if (d->parsed.isValid())
+ return static_cast<Attachment>(d->parsed.toInt());
+ if (d->values.count() != 1)
+ return Attachment_Unknown;
+ int v = findKnownValue(d->values.at(0).variant.toString(),
+ attachments, NumKnownAttachments);
+ d->parsed = v;
+ return static_cast<Attachment>(v);
+}
+
+int Declaration::styleFeaturesValue() const
+{
+ Q_ASSERT(d->propertyId == QtStyleFeatures);
+ if (d->parsed.isValid())
+ return d->parsed.toInt();
+ int features = StyleFeature_None;
+ for (int i = 0; i < d->values.count(); i++) {
+ features |= static_cast<int>(findKnownValue(d->values.value(i).variant.toString(),
+ styleFeatures, NumKnownStyleFeatures));
+ }
+ d->parsed = features;
+ return features;
+}
+
+QString Declaration::uriValue() const
+{
+ if (d->values.isEmpty() || d->values.at(0).type != Value::Uri)
+ return QString();
+ return d->values.at(0).variant.toString();
+}
+
+Qt::Alignment Declaration::alignmentValue() const
+{
+ if (d->parsed.isValid())
+ return Qt::Alignment(d->parsed.toInt());
+ if (d->values.isEmpty() || d->values.count() > 2)
+ return Qt::AlignLeft | Qt::AlignTop;
+
+ Qt::Alignment v = parseAlignment(d->values.constData(), d->values.count());
+ d->parsed = int(v);
+ return v;
+}
+
+void Declaration::borderImageValue(QString *image, int *cuts,
+ TileMode *h, TileMode *v) const
+{
+ *image = uriValue();
+ for (int i = 0; i < 4; i++)
+ cuts[i] = -1;
+ *h = *v = TileMode_Stretch;
+
+ if (d->values.count() < 2)
+ return;
+
+ if (d->values.at(1).type == Value::Number) { // cuts!
+ int i;
+ for (i = 0; i < qMin(d->values.count()-1, 4); i++) {
+ const Value& v = d->values.at(i+1);
+ if (v.type != Value::Number)
+ break;
+ cuts[i] = v.variant.toString().toInt();
+ }
+ if (i == 0) cuts[0] = cuts[1] = cuts[2] = cuts[3] = 0;
+ else if (i == 1) cuts[3] = cuts[2] = cuts[1] = cuts[0];
+ else if (i == 2) cuts[2] = cuts[0], cuts[3] = cuts[1];
+ else if (i == 3) cuts[3] = cuts[1];
+ }
+
+ if (d->values.last().type == Value::Identifier) {
+ *v = static_cast<TileMode>(findKnownValue(d->values.last().variant.toString(),
+ tileModes, NumKnownTileModes));
+ }
+ if (d->values[d->values.count() - 2].type == Value::Identifier) {
+ *h = static_cast<TileMode>
+ (findKnownValue(d->values[d->values.count()-2].variant.toString(),
+ tileModes, NumKnownTileModes));
+ } else
+ *h = *v;
+}
+
+QIcon Declaration::iconValue() const
+{
+ if (d->parsed.isValid())
+ return qvariant_cast<QIcon>(d->parsed);
+
+ QIcon icon;
+ for (int i = 0; i < d->values.count();) {
+ const Value &value = d->values.at(i++);
+ if (value.type != Value::Uri)
+ break;
+ QString uri = value.variant.toString();
+ QIcon::Mode mode = QIcon::Normal;
+ QIcon::State state = QIcon::Off;
+ for (int j = 0; j < 2; j++) {
+ if (i != d->values.count() && d->values.at(i).type == Value::KnownIdentifier) {
+ switch (d->values.at(i).variant.toInt()) {
+ case Value_Disabled: mode = QIcon::Disabled; break;
+ case Value_Active: mode = QIcon::Active; break;
+ case Value_Selected: mode = QIcon::Selected; break;
+ case Value_Normal: mode = QIcon::Normal; break;
+ case Value_On: state = QIcon::On; break;
+ case Value_Off: state = QIcon::Off; break;
+ default: break;
+ }
+ ++i;
+ } else {
+ break;
+ }
+ }
+
+ // QIcon is soo broken
+ if (icon.isNull())
+ icon = QIcon(uri);
+ else
+ icon.addPixmap(uri, mode, state);
+
+ if (i == d->values.count())
+ break;
+
+ if (d->values.at(i).type == Value::TermOperatorComma)
+ i++;
+ }
+
+ d->parsed = qVariantFromValue<QIcon>(icon);
+ return icon;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Selector
+int Selector::specificity() const
+{
+ int val = 0;
+ for (int i = 0; i < basicSelectors.count(); ++i) {
+ const BasicSelector &sel = basicSelectors.at(i);
+ if (!sel.elementName.isEmpty())
+ val += 1;
+
+ val += (sel.pseudos.count() + sel.attributeSelectors.count()) * 0x10;
+ val += sel.ids.count() * 0x100;
+ }
+ return val;
+}
+
+QString Selector::pseudoElement() const
+{
+ const BasicSelector& bs = basicSelectors.last();
+ if (!bs.pseudos.isEmpty() && bs.pseudos.at(0).type == PseudoClass_Unknown)
+ return bs.pseudos.at(0).name;
+ return QString();
+}
+
+quint64 Selector::pseudoClass(quint64 *negated) const
+{
+ const BasicSelector& bs = basicSelectors.last();
+ if (bs.pseudos.isEmpty())
+ return PseudoClass_Unspecified;
+ quint64 pc = PseudoClass_Unknown;
+ for (int i = !pseudoElement().isEmpty(); i < bs.pseudos.count(); i++) {
+ const Pseudo &pseudo = bs.pseudos.at(i);
+ if (pseudo.type == PseudoClass_Unknown)
+ return PseudoClass_Unknown;
+ if (!pseudo.negated)
+ pc |= pseudo.type;
+ else if (negated)
+ *negated |= pseudo.type;
+ }
+ return pc;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// StyleSheet
+void StyleSheet::buildIndexes(Qt::CaseSensitivity nameCaseSensitivity)
+{
+ QVector<StyleRule> universals;
+ for (int i = 0; i < styleRules.count(); ++i) {
+ const StyleRule &rule = styleRules.at(i);
+ QVector<Selector> universalsSelectors;
+ for (int j = 0; j < rule.selectors.count(); ++j) {
+ const Selector& selector = rule.selectors.at(j);
+
+ if (selector.basicSelectors.isEmpty())
+ continue;
+
+ if (selector.basicSelectors.at(0).relationToNext == BasicSelector::NoRelation) {
+ if (selector.basicSelectors.count() != 1)
+ continue;
+ } else if (selector.basicSelectors.count() <= 1) {
+ continue;
+ }
+
+ const BasicSelector &sel = selector.basicSelectors.at(selector.basicSelectors.count() - 1);
+
+ if (!sel.ids.isEmpty()) {
+ StyleRule nr;
+ nr.selectors += selector;
+ nr.declarations = rule.declarations;
+ nr.order = i;
+ idIndex.insert(sel.ids.at(0), nr);
+ } else if (!sel.elementName.isEmpty()) {
+ StyleRule nr;
+ nr.selectors += selector;
+ nr.declarations = rule.declarations;
+ nr.order = i;
+ QString name = sel.elementName;
+ if (nameCaseSensitivity == Qt::CaseInsensitive)
+ name=name.toLower();
+ nameIndex.insert(name, nr);
+ } else {
+ universalsSelectors += selector;
+ }
+ }
+ if (!universalsSelectors.isEmpty()) {
+ StyleRule nr;
+ nr.selectors = universalsSelectors;
+ nr.declarations = rule.declarations;
+ nr.order = i;
+ universals << nr;
+ }
+ }
+ styleRules = universals;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// StyleSelector
+StyleSelector::~StyleSelector()
+{
+}
+
+bool StyleSelector::nodeNameEquals(NodePtr node, const QString& nodeName) const
+{
+ return nodeNames(node).contains(nodeName, nameCaseSensitivity);
+}
+
+QStringList StyleSelector::nodeIds(NodePtr node) const
+{
+ return QStringList(attribute(node, QLatin1String("id")));
+}
+
+bool StyleSelector::selectorMatches(const Selector &selector, NodePtr node)
+{
+ if (selector.basicSelectors.isEmpty())
+ return false;
+
+ if (selector.basicSelectors.at(0).relationToNext == BasicSelector::NoRelation) {
+ if (selector.basicSelectors.count() != 1)
+ return false;
+ return basicSelectorMatches(selector.basicSelectors.at(0), node);
+ }
+ if (selector.basicSelectors.count() <= 1)
+ return false;
+
+ int i = selector.basicSelectors.count() - 1;
+ node = duplicateNode(node);
+ bool match = true;
+
+ BasicSelector sel = selector.basicSelectors.at(i);
+ do {
+ match = basicSelectorMatches(sel, node);
+ if (!match) {
+ if (sel.relationToNext == BasicSelector::MatchNextSelectorIfParent
+ || i == selector.basicSelectors.count() - 1) // first element must always match!
+ break;
+ }
+
+ if (match || sel.relationToNext != BasicSelector::MatchNextSelectorIfAncestor)
+ --i;
+
+ if (i < 0)
+ break;
+
+ sel = selector.basicSelectors.at(i);
+ if (sel.relationToNext == BasicSelector::MatchNextSelectorIfAncestor
+ || sel.relationToNext == BasicSelector::MatchNextSelectorIfParent) {
+
+ NodePtr nextParent = parentNode(node);
+ freeNode(node);
+ node = nextParent;
+ } else if (sel.relationToNext == BasicSelector::MatchNextSelectorIfPreceeds) {
+ NodePtr previousSibling = previousSiblingNode(node);
+ freeNode(node);
+ node = previousSibling;
+ }
+ if (isNullNode(node)) {
+ match = false;
+ break;
+ }
+ } while (i >= 0 && (match || sel.relationToNext == BasicSelector::MatchNextSelectorIfAncestor));
+
+ freeNode(node);
+
+ return match;
+}
+
+bool StyleSelector::basicSelectorMatches(const BasicSelector &sel, NodePtr node)
+{
+ if (!sel.attributeSelectors.isEmpty()) {
+ if (!hasAttributes(node))
+ return false;
+
+ for (int i = 0; i < sel.attributeSelectors.count(); ++i) {
+ const QCss::AttributeSelector &a = sel.attributeSelectors.at(i);
+
+ const QString attrValue = attribute(node, a.name);
+ if (attrValue.isNull())
+ return false;
+
+ if (a.valueMatchCriterium == QCss::AttributeSelector::MatchContains) {
+
+ QStringList lst = attrValue.split(QLatin1Char(' '));
+ if (!lst.contains(a.value))
+ return false;
+ } else if (
+ (a.valueMatchCriterium == QCss::AttributeSelector::MatchEqual
+ && attrValue != a.value)
+ ||
+ (a.valueMatchCriterium == QCss::AttributeSelector::MatchBeginsWith
+ && !attrValue.startsWith(a.value))
+ )
+ return false;
+ }
+ }
+
+ if (!sel.elementName.isEmpty()
+ && !nodeNameEquals(node, sel.elementName))
+ return false;
+
+ if (!sel.ids.isEmpty()
+ && sel.ids != nodeIds(node))
+ return false;
+
+ return true;
+}
+
+void StyleSelector::matchRule(NodePtr node, const StyleRule &rule, StyleSheetOrigin origin,
+ int depth, QMap<uint, StyleRule> *weightedRules)
+{
+ for (int j = 0; j < rule.selectors.count(); ++j) {
+ const Selector& selector = rule.selectors.at(j);
+ if (selectorMatches(selector, node)) {
+ uint weight = rule.order
+ + selector.specificity() *0x100
+ + (uint(origin) + depth)*0x100000;
+ StyleRule newRule = rule;
+ if(rule.selectors.count() > 1) {
+ newRule.selectors.resize(1);
+ newRule.selectors[0] = selector;
+ }
+ //We might have rules with the same weight if they came from a rule with several selectors
+ weightedRules->insertMulti(weight, newRule);
+ }
+ }
+}
+
+// Returns style rules that are in ascending order of specificity
+// Each of the StyleRule returned will contain exactly one Selector
+QVector<StyleRule> StyleSelector::styleRulesForNode(NodePtr node)
+{
+ QVector<StyleRule> rules;
+ if (styleSheets.isEmpty())
+ return rules;
+
+ QMap<uint, StyleRule> weightedRules; // (spec, rule) that will be sorted below
+
+ //prune using indexed stylesheet
+ for (int sheetIdx = 0; sheetIdx < styleSheets.count(); ++sheetIdx) {
+ const StyleSheet &styleSheet = styleSheets.at(sheetIdx);
+ for (int i = 0; i < styleSheet.styleRules.count(); ++i) {
+ matchRule(node, styleSheet.styleRules.at(i), styleSheet.origin, styleSheet.depth, &weightedRules);
+ }
+
+ if (!styleSheet.idIndex.isEmpty()) {
+ QStringList ids = nodeIds(node);
+ for (int i = 0; i < ids.count(); i++) {
+ const QString &key = ids.at(i);
+ QMultiHash<QString, StyleRule>::const_iterator it = styleSheet.idIndex.constFind(key);
+ while (it != styleSheet.idIndex.constEnd() && it.key() == key) {
+ matchRule(node, it.value(), styleSheet.origin, styleSheet.depth, &weightedRules);
+ ++it;
+ }
+ }
+ }
+ if (!styleSheet.nameIndex.isEmpty()) {
+ QStringList names = nodeNames(node);
+ for (int i = 0; i < names.count(); i++) {
+ QString name = names.at(i);
+ if (nameCaseSensitivity == Qt::CaseInsensitive)
+ name = name.toLower();
+ QMultiHash<QString, StyleRule>::const_iterator it = styleSheet.nameIndex.constFind(name);
+ while (it != styleSheet.nameIndex.constEnd() && it.key() == name) {
+ matchRule(node, it.value(), styleSheet.origin, styleSheet.depth, &weightedRules);
+ ++it;
+ }
+ }
+ }
+ if (!medium.isEmpty()) {
+ for (int i = 0; i < styleSheet.mediaRules.count(); ++i) {
+ if (styleSheet.mediaRules.at(i).media.contains(medium, Qt::CaseInsensitive)) {
+ for (int j = 0; j < styleSheet.mediaRules.at(i).styleRules.count(); ++j) {
+ matchRule(node, styleSheet.mediaRules.at(i).styleRules.at(j), styleSheet.origin,
+ styleSheet.depth, &weightedRules);
+ }
+ }
+ }
+ }
+ }
+
+ rules.reserve(weightedRules.count());
+ QMap<uint, StyleRule>::const_iterator it = weightedRules.constBegin();
+ for ( ; it != weightedRules.constEnd() ; ++it)
+ rules += *it;
+
+ return rules;
+}
+
+// for qtexthtmlparser which requires just the declarations with Enabled state
+// and without pseudo elements
+QVector<Declaration> StyleSelector::declarationsForNode(NodePtr node, const char *extraPseudo)
+{
+ QVector<Declaration> decls;
+ QVector<StyleRule> rules = styleRulesForNode(node);
+ for (int i = 0; i < rules.count(); i++) {
+ const Selector& selector = rules.at(i).selectors.at(0);
+ const QString pseudoElement = selector.pseudoElement();
+
+ if (extraPseudo && pseudoElement == QLatin1String(extraPseudo)) {
+ decls += rules.at(i).declarations;
+ continue;
+ }
+
+ if (!pseudoElement.isEmpty()) // skip rules with pseudo elements
+ continue;
+ quint64 pseudoClass = selector.pseudoClass();
+ if (pseudoClass == PseudoClass_Enabled || pseudoClass == PseudoClass_Unspecified)
+ decls += rules.at(i).declarations;
+ }
+ return decls;
+}
+
+static inline bool isHexDigit(const char c)
+{
+ return (c >= '0' && c <= '9')
+ || (c >= 'a' && c <= 'f')
+ || (c >= 'A' && c <= 'F')
+ ;
+}
+
+QString Scanner::preprocess(const QString &input, bool *hasEscapeSequences)
+{
+ QString output = input;
+
+ if (hasEscapeSequences)
+ *hasEscapeSequences = false;
+
+ int i = 0;
+ while (i < output.size()) {
+ if (output.at(i) == QLatin1Char('\\')) {
+
+ ++i;
+ // test for unicode hex escape
+ int hexCount = 0;
+ const int hexStart = i;
+ while (i < output.size()
+ && isHexDigit(output.at(i).toLatin1())
+ && hexCount < 7) {
+ ++hexCount;
+ ++i;
+ }
+ if (hexCount == 0) {
+ if (hasEscapeSequences)
+ *hasEscapeSequences = true;
+ continue;
+ }
+
+ hexCount = qMin(hexCount, 6);
+ bool ok = false;
+ ushort code = output.mid(hexStart, hexCount).toUShort(&ok, 16);
+ if (ok) {
+ output.replace(hexStart - 1, hexCount + 1, QChar(code));
+ i = hexStart;
+ } else {
+ i = hexStart;
+ }
+ } else {
+ ++i;
+ }
+ }
+ return output;
+}
+
+int QCssScanner_Generated::handleCommentStart()
+{
+ while (pos < input.size() - 1) {
+ if (input.at(pos) == QLatin1Char('*')
+ && input.at(pos + 1) == QLatin1Char('/')) {
+ pos += 2;
+ break;
+ }
+ ++pos;
+ }
+ return S;
+}
+
+void Scanner::scan(const QString &preprocessedInput, QVector<Symbol> *symbols)
+{
+ QCssScanner_Generated scanner(preprocessedInput);
+ Symbol sym;
+ int tok = scanner.lex();
+ while (tok != -1) {
+ sym.token = static_cast<QCss::TokenType>(tok);
+ sym.text = scanner.input;
+ sym.start = scanner.lexemStart;
+ sym.len = scanner.lexemLength;
+ symbols->append(sym);
+ tok = scanner.lex();
+ }
+}
+
+QString Symbol::lexem() const
+{
+ QString result;
+ if (len > 0)
+ result.reserve(len);
+ for (int i = 0; i < len; ++i) {
+ if (text.at(start + i) == QLatin1Char('\\') && i < len - 1)
+ ++i;
+ result += text.at(start + i);
+ }
+ return result;
+}
+
+Parser::Parser(const QString &css, bool isFile)
+{
+ init(css, isFile);
+}
+
+Parser::Parser()
+{
+ index = 0;
+ errorIndex = -1;
+ hasEscapeSequences = false;
+}
+
+void Parser::init(const QString &css, bool isFile)
+{
+ QString styleSheet = css;
+ if (isFile) {
+ QFile file(css);
+ if (file.open(QFile::ReadOnly)) {
+ sourcePath = QFileInfo(styleSheet).absolutePath() + QLatin1String("/");
+ QTextStream stream(&file);
+ styleSheet = stream.readAll();
+ } else {
+ qWarning() << "QCss::Parser - Failed to load file " << css;
+ styleSheet.clear();
+ }
+ } else {
+ sourcePath.clear();
+ }
+
+ hasEscapeSequences = false;
+ symbols.resize(0);
+ Scanner::scan(Scanner::preprocess(styleSheet, &hasEscapeSequences), &symbols);
+ index = 0;
+ errorIndex = -1;
+}
+
+bool Parser::parse(StyleSheet *styleSheet, Qt::CaseSensitivity nameCaseSensitivity)
+{
+ if (testTokenAndEndsWith(ATKEYWORD_SYM, QLatin1String("charset"))) {
+ if (!next(STRING)) return false;
+ if (!next(SEMICOLON)) return false;
+ }
+
+ while (test(S) || test(CDO) || test(CDC)) {}
+
+ while (testImport()) {
+ ImportRule rule;
+ if (!parseImport(&rule)) return false;
+ styleSheet->importRules.append(rule);
+ while (test(S) || test(CDO) || test(CDC)) {}
+ }
+
+ do {
+ if (testMedia()) {
+ MediaRule rule;
+ if (!parseMedia(&rule)) return false;
+ styleSheet->mediaRules.append(rule);
+ } else if (testPage()) {
+ PageRule rule;
+ if (!parsePage(&rule)) return false;
+ styleSheet->pageRules.append(rule);
+ } else if (testRuleset()) {
+ StyleRule rule;
+ if (!parseRuleset(&rule)) return false;
+ styleSheet->styleRules.append(rule);
+ } else if (test(ATKEYWORD_SYM)) {
+ if (!until(RBRACE)) return false;
+ } else if (hasNext()) {
+ return false;
+ }
+ while (test(S) || test(CDO) || test(CDC)) {}
+ } while (hasNext());
+ styleSheet->buildIndexes(nameCaseSensitivity);
+ return true;
+}
+
+Symbol Parser::errorSymbol()
+{
+ if (errorIndex == -1) return Symbol();
+ return symbols.at(errorIndex);
+}
+
+static inline void removeOptionalQuotes(QString *str)
+{
+ if (!str->startsWith(QLatin1Char('\''))
+ && !str->startsWith(QLatin1Char('\"')))
+ return;
+ str->remove(0, 1);
+ str->chop(1);
+}
+
+bool Parser::parseImport(ImportRule *importRule)
+{
+ skipSpace();
+
+ if (test(STRING)) {
+ importRule->href = lexem();
+ } else {
+ if (!testAndParseUri(&importRule->href)) return false;
+ }
+ removeOptionalQuotes(&importRule->href);
+
+ skipSpace();
+
+ if (testMedium()) {
+ if (!parseMedium(&importRule->media)) return false;
+
+ while (test(COMMA)) {
+ skipSpace();
+ if (!parseNextMedium(&importRule->media)) return false;
+ }
+ }
+
+ if (!next(SEMICOLON)) return false;
+
+ skipSpace();
+ return true;
+}
+
+bool Parser::parseMedia(MediaRule *mediaRule)
+{
+ do {
+ skipSpace();
+ if (!parseNextMedium(&mediaRule->media)) return false;
+ } while (test(COMMA));
+
+ if (!next(LBRACE)) return false;
+ skipSpace();
+
+ while (testRuleset()) {
+ StyleRule rule;
+ if (!parseRuleset(&rule)) return false;
+ mediaRule->styleRules.append(rule);
+ }
+
+ if (!next(RBRACE)) return false;
+ skipSpace();
+ return true;
+}
+
+bool Parser::parseMedium(QStringList *media)
+{
+ media->append(lexem());
+ skipSpace();
+ return true;
+}
+
+bool Parser::parsePage(PageRule *pageRule)
+{
+ skipSpace();
+
+ if (testPseudoPage())
+ if (!parsePseudoPage(&pageRule->selector)) return false;
+
+ skipSpace();
+ if (!next(LBRACE)) return false;
+
+ do {
+ skipSpace();
+ Declaration decl;
+ if (!parseNextDeclaration(&decl)) return false;
+ if (!decl.isEmpty())
+ pageRule->declarations.append(decl);
+ } while (test(SEMICOLON));
+
+ if (!next(RBRACE)) return false;
+ skipSpace();
+ return true;
+}
+
+bool Parser::parsePseudoPage(QString *selector)
+{
+ if (!next(IDENT)) return false;
+ *selector = lexem();
+ return true;
+}
+
+bool Parser::parseNextOperator(Value *value)
+{
+ if (!hasNext()) return true;
+ switch (next()) {
+ case SLASH: value->type = Value::TermOperatorSlash; skipSpace(); break;
+ case COMMA: value->type = Value::TermOperatorComma; skipSpace(); break;
+ default: prev(); break;
+ }
+ return true;
+}
+
+bool Parser::parseCombinator(BasicSelector::Relation *relation)
+{
+ *relation = BasicSelector::NoRelation;
+ if (lookup() == S) {
+ *relation = BasicSelector::MatchNextSelectorIfAncestor;
+ skipSpace();
+ } else {
+ prev();
+ }
+ if (test(PLUS)) {
+ *relation = BasicSelector::MatchNextSelectorIfPreceeds;
+ } else if (test(GREATER)) {
+ *relation = BasicSelector::MatchNextSelectorIfParent;
+ }
+ skipSpace();
+ return true;
+}
+
+bool Parser::parseProperty(Declaration *decl)
+{
+ decl->d->property = lexem();
+ decl->d->propertyId = static_cast<Property>(findKnownValue(decl->d->property, properties, NumProperties));
+ skipSpace();
+ return true;
+}
+
+bool Parser::parseRuleset(StyleRule *styleRule)
+{
+ Selector sel;
+ if (!parseSelector(&sel)) return false;
+ styleRule->selectors.append(sel);
+
+ while (test(COMMA)) {
+ skipSpace();
+ Selector sel;
+ if (!parseNextSelector(&sel)) return false;
+ styleRule->selectors.append(sel);
+ }
+
+ skipSpace();
+ if (!next(LBRACE)) return false;
+ const int declarationStart = index;
+
+ do {
+ skipSpace();
+ Declaration decl;
+ const int rewind = index;
+ if (!parseNextDeclaration(&decl)) {
+ index = rewind;
+ const bool foundSemicolon = until(SEMICOLON);
+ const int semicolonIndex = index;
+
+ index = declarationStart;
+ const bool foundRBrace = until(RBRACE);
+
+ if (foundSemicolon && semicolonIndex < index) {
+ decl = Declaration();
+ index = semicolonIndex - 1;
+ } else {
+ skipSpace();
+ return foundRBrace;
+ }
+ }
+ if (!decl.isEmpty())
+ styleRule->declarations.append(decl);
+ } while (test(SEMICOLON));
+
+ if (!next(RBRACE)) return false;
+ skipSpace();
+ return true;
+}
+
+bool Parser::parseSelector(Selector *sel)
+{
+ BasicSelector basicSel;
+ if (!parseSimpleSelector(&basicSel)) return false;
+ while (testCombinator()) {
+ if (!parseCombinator(&basicSel.relationToNext)) return false;
+
+ if (!testSimpleSelector()) break;
+ sel->basicSelectors.append(basicSel);
+
+ basicSel = BasicSelector();
+ if (!parseSimpleSelector(&basicSel)) return false;
+ }
+ sel->basicSelectors.append(basicSel);
+ return true;
+}
+
+bool Parser::parseSimpleSelector(BasicSelector *basicSel)
+{
+ int minCount = 0;
+ if (lookupElementName()) {
+ if (!parseElementName(&basicSel->elementName)) return false;
+ } else {
+ prev();
+ minCount = 1;
+ }
+ bool onceMore;
+ int count = 0;
+ do {
+ onceMore = false;
+ if (test(HASH)) {
+ QString theid = lexem();
+ // chop off leading #
+ theid.remove(0, 1);
+ basicSel->ids.append(theid);
+ onceMore = true;
+ } else if (testClass()) {
+ onceMore = true;
+ AttributeSelector a;
+ a.name = QLatin1String("class");
+ a.valueMatchCriterium = AttributeSelector::MatchContains;
+ if (!parseClass(&a.value)) return false;
+ basicSel->attributeSelectors.append(a);
+ } else if (testAttrib()) {
+ onceMore = true;
+ AttributeSelector a;
+ if (!parseAttrib(&a)) return false;
+ basicSel->attributeSelectors.append(a);
+ } else if (testPseudo()) {
+ onceMore = true;
+ Pseudo ps;
+ if (!parsePseudo(&ps)) return false;
+ basicSel->pseudos.append(ps);
+ }
+ if (onceMore) ++count;
+ } while (onceMore);
+ return count >= minCount;
+}
+
+bool Parser::parseClass(QString *name)
+{
+ if (!next(IDENT)) return false;
+ *name = lexem();
+ return true;
+}
+
+bool Parser::parseElementName(QString *name)
+{
+ switch (lookup()) {
+ case STAR: name->clear(); break;
+ case IDENT: *name = lexem(); break;
+ default: return false;
+ }
+ return true;
+}
+
+bool Parser::parseAttrib(AttributeSelector *attr)
+{
+ skipSpace();
+ if (!next(IDENT)) return false;
+ attr->name = lexem();
+ skipSpace();
+
+ if (test(EQUAL)) {
+ attr->valueMatchCriterium = AttributeSelector::MatchEqual;
+ } else if (test(INCLUDES)) {
+ attr->valueMatchCriterium = AttributeSelector::MatchContains;
+ } else if (test(DASHMATCH)) {
+ attr->valueMatchCriterium = AttributeSelector::MatchBeginsWith;
+ } else {
+ return next(RBRACKET);
+ }
+
+ skipSpace();
+
+ if (!test(IDENT) && !test(STRING)) return false;
+ attr->value = unquotedLexem();
+
+ skipSpace();
+ return next(RBRACKET);
+}
+
+bool Parser::parsePseudo(Pseudo *pseudo)
+{
+ test(COLON);
+ pseudo->negated = test(EXCLAMATION_SYM);
+ if (test(IDENT)) {
+ pseudo->name = lexem();
+ pseudo->type = static_cast<quint64>(findKnownValue(pseudo->name, pseudos, NumPseudos));
+ return true;
+ }
+ if (!next(FUNCTION)) return false;
+ pseudo->function = lexem();
+ // chop off trailing parenthesis
+ pseudo->function.chop(1);
+ skipSpace();
+ if (!test(IDENT)) return false;
+ pseudo->name = lexem();
+ skipSpace();
+ return next(RPAREN);
+}
+
+bool Parser::parseNextDeclaration(Declaration *decl)
+{
+ if (!testProperty())
+ return true; // not an error!
+ if (!parseProperty(decl)) return false;
+ if (!next(COLON)) return false;
+ skipSpace();
+ if (!parseNextExpr(&decl->d->values)) return false;
+ if (testPrio())
+ if (!parsePrio(decl)) return false;
+ return true;
+}
+
+bool Parser::testPrio()
+{
+ const int rewind = index;
+ if (!test(EXCLAMATION_SYM)) return false;
+ skipSpace();
+ if (!test(IDENT)) {
+ index = rewind;
+ return false;
+ }
+ if (lexem().compare(QLatin1String("important"), Qt::CaseInsensitive) != 0) {
+ index = rewind;
+ return false;
+ }
+ return true;
+}
+
+bool Parser::parsePrio(Declaration *declaration)
+{
+ declaration->d->important = true;
+ skipSpace();
+ return true;
+}
+
+bool Parser::parseExpr(QVector<Value> *values)
+{
+ Value val;
+ if (!parseTerm(&val)) return false;
+ values->append(val);
+ bool onceMore;
+ do {
+ onceMore = false;
+ val = Value();
+ if (!parseNextOperator(&val)) return false;
+ if (val.type != QCss::Value::Unknown)
+ values->append(val);
+ if (testTerm()) {
+ onceMore = true;
+ val = Value();
+ if (!parseTerm(&val)) return false;
+ values->append(val);
+ }
+ } while (onceMore);
+ return true;
+}
+
+bool Parser::testTerm()
+{
+ return test(PLUS) || test(MINUS)
+ || test(NUMBER)
+ || test(PERCENTAGE)
+ || test(LENGTH)
+ || test(STRING)
+ || test(IDENT)
+ || testHexColor()
+ || testFunction();
+}
+
+bool Parser::parseTerm(Value *value)
+{
+ QString str = lexem();
+ bool haveUnary = false;
+ if (lookup() == PLUS || lookup() == MINUS) {
+ haveUnary = true;
+ if (!hasNext()) return false;
+ next();
+ str += lexem();
+ }
+
+ value->variant = str;
+ value->type = QCss::Value::String;
+ switch (lookup()) {
+ case NUMBER:
+ value->type = Value::Number;
+ value->variant.convert(QVariant::Double);
+ break;
+ case PERCENTAGE:
+ value->type = Value::Percentage;
+ str.chop(1); // strip off %
+ value->variant = str;
+ break;
+ case LENGTH:
+ value->type = Value::Length;
+ break;
+
+ case STRING:
+ if (haveUnary) return false;
+ value->type = Value::String;
+ str.chop(1);
+ str.remove(0, 1);
+ value->variant = str;
+ break;
+ case IDENT: {
+ if (haveUnary) return false;
+ value->type = Value::Identifier;
+ const int theid = findKnownValue(str, values, NumKnownValues);
+ if (theid != 0) {
+ value->type = Value::KnownIdentifier;
+ value->variant = theid;
+ }
+ break;
+ }
+ default: {
+ if (haveUnary) return false;
+ prev();
+ if (testHexColor()) {
+ QColor col;
+ if (!parseHexColor(&col)) return false;
+ value->type = Value::Color;
+ value->variant = col;
+ } else if (testFunction()) {
+ QString name, args;
+ if (!parseFunction(&name, &args)) return false;
+ if (name == QLatin1String("url")) {
+ value->type = Value::Uri;
+ removeOptionalQuotes(&args);
+ if (QFileInfo(args).isRelative() && !sourcePath.isEmpty()) {
+ args.prepend(sourcePath);
+ }
+ value->variant = args;
+ } else {
+ value->type = Value::Function;
+ value->variant = QStringList() << name << args;
+ }
+ } else {
+ return recordError();
+ }
+ return true;
+ }
+ }
+ skipSpace();
+ return true;
+}
+
+bool Parser::parseFunction(QString *name, QString *args)
+{
+ *name = lexem();
+ name->chop(1);
+ skipSpace();
+ const int start = index;
+ if (!until(RPAREN)) return false;
+ for (int i = start; i < index - 1; ++i)
+ args->append(symbols.at(i).lexem());
+ /*
+ if (!nextExpr(&arguments)) return false;
+ if (!next(RPAREN)) return false;
+ */
+ skipSpace();
+ return true;
+}
+
+bool Parser::parseHexColor(QColor *col)
+{
+ col->setNamedColor(lexem());
+ if (!col->isValid()) {
+ qWarning("QCssParser::parseHexColor: Unknown color name '%s'",lexem().toLatin1().constData());
+ return false;
+ }
+ skipSpace();
+ return true;
+}
+
+bool Parser::testAndParseUri(QString *uri)
+{
+ const int rewind = index;
+ if (!testFunction()) return false;
+
+ QString name, args;
+ if (!parseFunction(&name, &args)) {
+ index = rewind;
+ return false;
+ }
+ if (name.toLower() != QLatin1String("url")) {
+ index = rewind;
+ return false;
+ }
+ *uri = args;
+ removeOptionalQuotes(uri);
+ return true;
+}
+
+bool Parser::testSimpleSelector()
+{
+ return testElementName()
+ || (test(HASH))
+ || testClass()
+ || testAttrib()
+ || testPseudo();
+}
+
+bool Parser::next(QCss::TokenType t)
+{
+ if (hasNext() && next() == t)
+ return true;
+ return recordError();
+}
+
+bool Parser::test(QCss::TokenType t)
+{
+ if (index >= symbols.count())
+ return false;
+ if (symbols.at(index).token == t) {
+ ++index;
+ return true;
+ }
+ return false;
+}
+
+QString Parser::unquotedLexem() const
+{
+ QString s = lexem();
+ if (lookup() == STRING) {
+ s.chop(1);
+ s.remove(0, 1);
+ }
+ return s;
+}
+
+QString Parser::lexemUntil(QCss::TokenType t)
+{
+ QString lexem;
+ while (hasNext() && next() != t)
+ lexem += symbol().lexem();
+ return lexem;
+}
+
+bool Parser::until(QCss::TokenType target, QCss::TokenType target2)
+{
+ int braceCount = 0;
+ int brackCount = 0;
+ int parenCount = 0;
+ if (index) {
+ switch(symbols.at(index-1).token) {
+ case LBRACE: ++braceCount; break;
+ case LBRACKET: ++brackCount; break;
+ case FUNCTION:
+ case LPAREN: ++parenCount; break;
+ default: ;
+ }
+ }
+ while (index < symbols.size()) {
+ QCss::TokenType t = symbols.at(index++).token;
+ switch (t) {
+ case LBRACE: ++braceCount; break;
+ case RBRACE: --braceCount; break;
+ case LBRACKET: ++brackCount; break;
+ case RBRACKET: --brackCount; break;
+ case FUNCTION:
+ case LPAREN: ++parenCount; break;
+ case RPAREN: --parenCount; break;
+ default: break;
+ }
+ if ((t == target || (target2 != NONE && t == target2))
+ && braceCount <= 0
+ && brackCount <= 0
+ && parenCount <= 0)
+ return true;
+
+ if (braceCount < 0 || brackCount < 0 || parenCount < 0) {
+ --index;
+ break;
+ }
+ }
+ return false;
+}
+
+bool Parser::testTokenAndEndsWith(QCss::TokenType t, const QLatin1String &str)
+{
+ if (!test(t)) return false;
+ if (!lexem().endsWith(str, Qt::CaseInsensitive)) {
+ prev();
+ return false;
+ }
+ return true;
+}
+
+QT_END_NAMESPACE
+#endif // QT_NO_CSSPARSER
diff --git a/src/gui/text/qcssparser_p.h b/src/gui/text/qcssparser_p.h
new file mode 100644
index 0000000000..97a0aef383
--- /dev/null
+++ b/src/gui/text/qcssparser_p.h
@@ -0,0 +1,835 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QCSSPARSER_P_H
+#define QCSSPARSER_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of the QLibrary class. This header file may change from
+// version to version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtCore/QStringList>
+#include <QtCore/QVector>
+#include <QtCore/QVariant>
+#include <QtCore/QPair>
+#include <QtCore/QSize>
+#include <QtCore/QMultiHash>
+#include <QtGui/QFont>
+#include <QtGui/QPalette>
+#include <QtGui/QIcon>
+#include <QtCore/QSharedData>
+
+
+#ifndef QT_NO_CSSPARSER
+
+QT_BEGIN_NAMESPACE
+
+namespace QCss
+{
+
+enum Property {
+ UnknownProperty,
+ BackgroundColor,
+ Color,
+ Float,
+ Font,
+ FontFamily,
+ FontSize,
+ FontStyle,
+ FontWeight,
+ Margin,
+ MarginBottom,
+ MarginLeft,
+ MarginRight,
+ MarginTop,
+ QtBlockIndent,
+ QtListIndent,
+ QtParagraphType,
+ QtTableType,
+ QtUserState,
+ TextDecoration,
+ TextIndent,
+ TextUnderlineStyle,
+ VerticalAlignment,
+ Whitespace,
+ QtSelectionForeground,
+ QtSelectionBackground,
+ Border,
+ BorderLeft,
+ BorderRight,
+ BorderTop,
+ BorderBottom,
+ Padding,
+ PaddingLeft,
+ PaddingRight,
+ PaddingTop,
+ PaddingBottom,
+ PageBreakBefore,
+ PageBreakAfter,
+ QtAlternateBackground,
+ BorderLeftStyle,
+ BorderRightStyle,
+ BorderTopStyle,
+ BorderBottomStyle,
+ BorderStyles,
+ BorderLeftColor,
+ BorderRightColor,
+ BorderTopColor,
+ BorderBottomColor,
+ BorderColor,
+ BorderLeftWidth,
+ BorderRightWidth,
+ BorderTopWidth,
+ BorderBottomWidth,
+ BorderWidth,
+ BorderTopLeftRadius,
+ BorderTopRightRadius,
+ BorderBottomLeftRadius,
+ BorderBottomRightRadius,
+ BorderRadius,
+ Background,
+ BackgroundOrigin,
+ BackgroundClip,
+ BackgroundRepeat,
+ BackgroundPosition,
+ BackgroundAttachment,
+ BackgroundImage,
+ BorderImage,
+ QtSpacing,
+ Width,
+ Height,
+ MinimumWidth,
+ MinimumHeight,
+ MaximumWidth,
+ MaximumHeight,
+ QtImage,
+ Left,
+ Right,
+ Top,
+ Bottom,
+ QtOrigin,
+ QtPosition,
+ Position,
+ QtStyleFeatures,
+ QtBackgroundRole,
+ ListStyleType,
+ ListStyle,
+ QtImageAlignment,
+ TextAlignment,
+ Outline,
+ OutlineOffset,
+ OutlineWidth,
+ OutlineColor,
+ OutlineStyle,
+ OutlineRadius,
+ OutlineTopLeftRadius,
+ OutlineTopRightRadius,
+ OutlineBottomLeftRadius,
+ OutlineBottomRightRadius,
+ FontVariant,
+ TextTransform,
+ NumProperties
+};
+
+enum KnownValue {
+ UnknownValue,
+ Value_Normal,
+ Value_Pre,
+ Value_NoWrap,
+ Value_PreWrap,
+ Value_Small,
+ Value_Medium,
+ Value_Large,
+ Value_XLarge,
+ Value_XXLarge,
+ Value_Italic,
+ Value_Oblique,
+ Value_Bold,
+ Value_Underline,
+ Value_Overline,
+ Value_LineThrough,
+ Value_Sub,
+ Value_Super,
+ Value_Left,
+ Value_Right,
+ Value_Top,
+ Value_Bottom,
+ Value_Center,
+ Value_Native,
+ Value_Solid,
+ Value_Dotted,
+ Value_Dashed,
+ Value_DotDash,
+ Value_DotDotDash,
+ Value_Double,
+ Value_Groove,
+ Value_Ridge,
+ Value_Inset,
+ Value_Outset,
+ Value_Wave,
+ Value_Middle,
+ Value_Auto,
+ Value_Always,
+ Value_None,
+ Value_Transparent,
+ Value_Disc,
+ Value_Circle,
+ Value_Square,
+ Value_Decimal,
+ Value_LowerAlpha,
+ Value_UpperAlpha,
+ Value_SmallCaps,
+ Value_Uppercase,
+ Value_Lowercase,
+
+ /* keep these in same order as QPalette::ColorRole */
+ Value_FirstColorRole,
+ Value_WindowText = Value_FirstColorRole,
+ Value_Button,
+ Value_Light,
+ Value_Midlight,
+ Value_Dark,
+ Value_Mid,
+ Value_Text,
+ Value_BrightText,
+ Value_ButtonText,
+ Value_Base,
+ Value_Window,
+ Value_Shadow,
+ Value_Highlight,
+ Value_HighlightedText,
+ Value_Link,
+ Value_LinkVisited,
+ Value_AlternateBase,
+ Value_LastColorRole = Value_AlternateBase,
+
+ Value_Disabled,
+ Value_Active,
+ Value_Selected,
+ Value_On,
+ Value_Off,
+
+ NumKnownValues
+};
+
+enum BorderStyle {
+ BorderStyle_Unknown,
+ BorderStyle_None,
+ BorderStyle_Dotted,
+ BorderStyle_Dashed,
+ BorderStyle_Solid,
+ BorderStyle_Double,
+ BorderStyle_DotDash,
+ BorderStyle_DotDotDash,
+ BorderStyle_Groove,
+ BorderStyle_Ridge,
+ BorderStyle_Inset,
+ BorderStyle_Outset,
+ BorderStyle_Native,
+ NumKnownBorderStyles
+};
+
+enum Edge {
+ TopEdge,
+ RightEdge,
+ BottomEdge,
+ LeftEdge,
+ NumEdges
+};
+
+enum Corner {
+ TopLeftCorner,
+ TopRightCorner,
+ BottomLeftCorner,
+ BottomRightCorner
+};
+
+enum TileMode {
+ TileMode_Unknown,
+ TileMode_Round,
+ TileMode_Stretch,
+ TileMode_Repeat,
+ NumKnownTileModes
+};
+
+enum Repeat {
+ Repeat_Unknown,
+ Repeat_None,
+ Repeat_X,
+ Repeat_Y,
+ Repeat_XY,
+ NumKnownRepeats
+};
+
+enum Origin {
+ Origin_Unknown,
+ Origin_Padding,
+ Origin_Border,
+ Origin_Content,
+ Origin_Margin,
+ NumKnownOrigins
+};
+
+enum PositionMode {
+ PositionMode_Unknown,
+ PositionMode_Static,
+ PositionMode_Relative,
+ PositionMode_Absolute,
+ PositionMode_Fixed,
+ NumKnownPositionModes
+};
+
+enum Attachment {
+ Attachment_Unknown,
+ Attachment_Fixed,
+ Attachment_Scroll,
+ NumKnownAttachments
+};
+
+enum StyleFeature {
+ StyleFeature_None = 0,
+ StyleFeature_BackgroundColor = 1,
+ StyleFeature_BackgroundGradient = 2,
+ NumKnownStyleFeatures = 4
+};
+
+struct Q_GUI_EXPORT Value
+{
+ enum Type {
+ Unknown,
+ Number,
+ Percentage,
+ Length,
+ String,
+ Identifier,
+ KnownIdentifier,
+ Uri,
+ Color,
+ Function,
+ TermOperatorSlash,
+ TermOperatorComma
+ };
+ inline Value() : type(Unknown) { }
+ Type type;
+ QVariant variant;
+ QString toString() const;
+};
+
+struct ColorData {
+ ColorData() : type(Invalid) {}
+ ColorData(const QColor &col) : color(col) , type(Color) {}
+ ColorData(QPalette::ColorRole r) : role(r) , type(Role) {}
+ QColor color;
+ QPalette::ColorRole role;
+ enum { Invalid, Color, Role} type;
+};
+
+struct BrushData {
+ BrushData() : type(Invalid) {}
+ BrushData(const QBrush &br) : brush(br) , type(Brush) {}
+ BrushData(QPalette::ColorRole r) : role(r) , type(Role) {}
+ QBrush brush;
+ QPalette::ColorRole role;
+ enum { Invalid, Brush, Role, DependsOnThePalette } type;
+};
+
+struct BackgroundData {
+ BrushData brush;
+ QString image;
+ Repeat repeat;
+ Qt::Alignment alignment;
+};
+
+struct LengthData {
+ qreal number;
+ enum { None, Px, Ex, Em } unit;
+};
+
+struct BorderData {
+ LengthData width;
+ BorderStyle style;
+ BrushData color;
+};
+
+
+// 1. StyleRule - x:hover, y:clicked > z:checked { prop1: value1; prop2: value2; }
+// 2. QVector<Selector> - x:hover, y:clicked z:checked
+// 3. QVector<BasicSelector> - y:clicked z:checked
+// 4. QVector<Declaration> - { prop1: value1; prop2: value2; }
+// 5. Declaration - prop1: value1;
+
+struct Q_GUI_EXPORT Declaration
+{
+ struct DeclarationData : public QSharedData
+ {
+ inline DeclarationData() : propertyId(UnknownProperty), important(false) {}
+ QString property;
+ Property propertyId;
+ QVector<Value> values;
+ QVariant parsed;
+ bool important;
+ };
+ QExplicitlySharedDataPointer<DeclarationData> d;
+ inline Declaration() : d(new DeclarationData()) {}
+ inline bool isEmpty() const { return d->property.isEmpty() && d->propertyId == UnknownProperty; }
+
+ // helper functions
+ QColor colorValue(const QPalette & = QPalette()) const;
+ void colorValues(QColor *c, const QPalette & = QPalette()) const;
+ QBrush brushValue(const QPalette & = QPalette()) const;
+ void brushValues(QBrush *c, const QPalette & = QPalette()) const;
+
+ BorderStyle styleValue() const;
+ void styleValues(BorderStyle *s) const;
+
+ Origin originValue() const;
+ Repeat repeatValue() const;
+ Qt::Alignment alignmentValue() const;
+ PositionMode positionValue() const;
+ Attachment attachmentValue() const;
+ int styleFeaturesValue() const;
+
+ bool intValue(int *i, const char *unit = 0) const;
+ bool realValue(qreal *r, const char *unit = 0) const;
+
+ QSize sizeValue() const;
+ QRect rectValue() const;
+ QString uriValue() const;
+ QIcon iconValue() const;
+
+ void borderImageValue(QString *image, int *cuts, TileMode *h, TileMode *v) const;
+};
+
+const quint64 PseudoClass_Unknown = Q_UINT64_C(0x0000000000000000);
+const quint64 PseudoClass_Enabled = Q_UINT64_C(0x0000000000000001);
+const quint64 PseudoClass_Disabled = Q_UINT64_C(0x0000000000000002);
+const quint64 PseudoClass_Pressed = Q_UINT64_C(0x0000000000000004);
+const quint64 PseudoClass_Focus = Q_UINT64_C(0x0000000000000008);
+const quint64 PseudoClass_Hover = Q_UINT64_C(0x0000000000000010);
+const quint64 PseudoClass_Checked = Q_UINT64_C(0x0000000000000020);
+const quint64 PseudoClass_Unchecked = Q_UINT64_C(0x0000000000000040);
+const quint64 PseudoClass_Indeterminate = Q_UINT64_C(0x0000000000000080);
+const quint64 PseudoClass_Unspecified = Q_UINT64_C(0x0000000000000100);
+const quint64 PseudoClass_Selected = Q_UINT64_C(0x0000000000000200);
+const quint64 PseudoClass_Horizontal = Q_UINT64_C(0x0000000000000400);
+const quint64 PseudoClass_Vertical = Q_UINT64_C(0x0000000000000800);
+const quint64 PseudoClass_Window = Q_UINT64_C(0x0000000000001000);
+const quint64 PseudoClass_Children = Q_UINT64_C(0x0000000000002000);
+const quint64 PseudoClass_Sibling = Q_UINT64_C(0x0000000000004000);
+const quint64 PseudoClass_Default = Q_UINT64_C(0x0000000000008000);
+const quint64 PseudoClass_First = Q_UINT64_C(0x0000000000010000);
+const quint64 PseudoClass_Last = Q_UINT64_C(0x0000000000020000);
+const quint64 PseudoClass_Middle = Q_UINT64_C(0x0000000000040000);
+const quint64 PseudoClass_OnlyOne = Q_UINT64_C(0x0000000000080000);
+const quint64 PseudoClass_PreviousSelected = Q_UINT64_C(0x0000000000100000);
+const quint64 PseudoClass_NextSelected = Q_UINT64_C(0x0000000000200000);
+const quint64 PseudoClass_Flat = Q_UINT64_C(0x0000000000400000);
+const quint64 PseudoClass_Left = Q_UINT64_C(0x0000000000800000);
+const quint64 PseudoClass_Right = Q_UINT64_C(0x0000000001000000);
+const quint64 PseudoClass_Top = Q_UINT64_C(0x0000000002000000);
+const quint64 PseudoClass_Bottom = Q_UINT64_C(0x0000000004000000);
+const quint64 PseudoClass_Exclusive = Q_UINT64_C(0x0000000008000000);
+const quint64 PseudoClass_NonExclusive = Q_UINT64_C(0x0000000010000000);
+const quint64 PseudoClass_Frameless = Q_UINT64_C(0x0000000020000000);
+const quint64 PseudoClass_ReadOnly = Q_UINT64_C(0x0000000040000000);
+const quint64 PseudoClass_Active = Q_UINT64_C(0x0000000080000000);
+const quint64 PseudoClass_Closable = Q_UINT64_C(0x0000000100000000);
+const quint64 PseudoClass_Movable = Q_UINT64_C(0x0000000200000000);
+const quint64 PseudoClass_Floatable = Q_UINT64_C(0x0000000400000000);
+const quint64 PseudoClass_Minimized = Q_UINT64_C(0x0000000800000000);
+const quint64 PseudoClass_Maximized = Q_UINT64_C(0x0000001000000000);
+const quint64 PseudoClass_On = Q_UINT64_C(0x0000002000000000);
+const quint64 PseudoClass_Off = Q_UINT64_C(0x0000004000000000);
+const quint64 PseudoClass_Editable = Q_UINT64_C(0x0000008000000000);
+const quint64 PseudoClass_Item = Q_UINT64_C(0x0000010000000000);
+const quint64 PseudoClass_Closed = Q_UINT64_C(0x0000020000000000);
+const quint64 PseudoClass_Open = Q_UINT64_C(0x0000040000000000);
+const quint64 PseudoClass_EditFocus = Q_UINT64_C(0x0000080000000000);
+const quint64 PseudoClass_Alternate = Q_UINT64_C(0x0000100000000000);
+// The Any specifier is never generated, but can be used as a wildcard in searches.
+const quint64 PseudoClass_Any = Q_UINT64_C(0x0000ffffffffffff);
+const int NumPseudos = 46;
+
+struct Q_GUI_EXPORT Pseudo
+{
+ Pseudo() : negated(false) { }
+ quint64 type;
+ QString name;
+ QString function;
+ bool negated;
+};
+
+struct Q_GUI_EXPORT AttributeSelector
+{
+ enum ValueMatchType {
+ NoMatch,
+ MatchEqual,
+ MatchContains,
+ MatchBeginsWith
+ };
+ inline AttributeSelector() : valueMatchCriterium(NoMatch) {}
+
+ QString name;
+ QString value;
+ ValueMatchType valueMatchCriterium;
+};
+
+struct Q_GUI_EXPORT BasicSelector
+{
+ inline BasicSelector() : relationToNext(NoRelation) {}
+
+ enum Relation {
+ NoRelation,
+ MatchNextSelectorIfAncestor,
+ MatchNextSelectorIfParent,
+ MatchNextSelectorIfPreceeds
+ };
+
+ QString elementName;
+
+ QStringList ids;
+ QVector<Pseudo> pseudos;
+ QVector<AttributeSelector> attributeSelectors;
+
+ Relation relationToNext;
+};
+
+struct Q_GUI_EXPORT Selector
+{
+ QVector<BasicSelector> basicSelectors;
+ int specificity() const;
+ quint64 pseudoClass(quint64 *negated = 0) const;
+ QString pseudoElement() const;
+};
+
+struct StyleRule;
+struct MediaRule;
+struct PageRule;
+struct ImportRule;
+
+struct Q_GUI_EXPORT ValueExtractor
+{
+ ValueExtractor(const QVector<Declaration> &declarations, const QPalette & = QPalette());
+
+ bool extractFont(QFont *font, int *fontSizeAdjustment);
+ bool extractBackground(QBrush *, QString *, Repeat *, Qt::Alignment *, QCss::Origin *, QCss::Attachment *,
+ QCss::Origin *);
+ bool extractGeometry(int *w, int *h, int *minw, int *minh, int *maxw, int *maxh);
+ bool extractPosition(int *l, int *t, int *r, int *b, QCss::Origin *, Qt::Alignment *,
+ QCss::PositionMode *, Qt::Alignment *);
+ bool extractBox(int *margins, int *paddings, int *spacing = 0);
+ bool extractBorder(int *borders, QBrush *colors, BorderStyle *Styles, QSize *radii);
+ bool extractOutline(int *borders, QBrush *colors, BorderStyle *Styles, QSize *radii, int *offsets);
+ bool extractPalette(QBrush *fg, QBrush *sfg, QBrush *sbg, QBrush *abg);
+ int extractStyleFeatures();
+ bool extractImage(QIcon *icon, Qt::Alignment *a, QSize *size);
+
+ int lengthValue(const Declaration &decl);
+
+private:
+ void extractFont();
+ void borderValue(const Declaration &decl, int *width, QCss::BorderStyle *style, QBrush *color);
+ LengthData lengthValue(const Value& v);
+ void lengthValues(const Declaration &decl, int *m);
+ QSize sizeValue(const Declaration &decl);
+ void sizeValues(const Declaration &decl, QSize *radii);
+
+ QVector<Declaration> declarations;
+ QFont f;
+ int adjustment;
+ int fontExtracted;
+ QPalette pal;
+};
+
+struct Q_GUI_EXPORT StyleRule
+{
+ StyleRule() : order(0) { }
+ QVector<Selector> selectors;
+ QVector<Declaration> declarations;
+ int order;
+};
+
+struct Q_GUI_EXPORT MediaRule
+{
+ QStringList media;
+ QVector<StyleRule> styleRules;
+};
+
+struct Q_GUI_EXPORT PageRule
+{
+ QString selector;
+ QVector<Declaration> declarations;
+};
+
+struct Q_GUI_EXPORT ImportRule
+{
+ QString href;
+ QStringList media;
+};
+
+enum StyleSheetOrigin {
+ StyleSheetOrigin_Unspecified,
+ StyleSheetOrigin_UserAgent,
+ StyleSheetOrigin_User,
+ StyleSheetOrigin_Author,
+ StyleSheetOrigin_Inline
+};
+
+struct Q_GUI_EXPORT StyleSheet
+{
+ StyleSheet() : origin(StyleSheetOrigin_Unspecified), depth(0) { }
+ QVector<StyleRule> styleRules; //only contains rules that are not indexed
+ QVector<MediaRule> mediaRules;
+ QVector<PageRule> pageRules;
+ QVector<ImportRule> importRules;
+ StyleSheetOrigin origin;
+ int depth; // applicable only for inline style sheets
+ QMultiHash<QString, StyleRule> nameIndex;
+ QMultiHash<QString, StyleRule> idIndex;
+ void buildIndexes(Qt::CaseSensitivity nameCaseSensitivity = Qt::CaseSensitive);
+};
+
+class Q_GUI_EXPORT StyleSelector
+{
+public:
+ StyleSelector() : nameCaseSensitivity(Qt::CaseSensitive) {}
+ virtual ~StyleSelector();
+
+ union NodePtr {
+ void *ptr;
+ int id;
+ };
+
+ QVector<StyleRule> styleRulesForNode(NodePtr node);
+ QVector<Declaration> declarationsForNode(NodePtr node, const char *extraPseudo = 0);
+
+ virtual bool nodeNameEquals(NodePtr node, const QString& nodeName) const;
+ virtual QString attribute(NodePtr node, const QString &name) const = 0;
+ virtual bool hasAttributes(NodePtr node) const = 0;
+ virtual QStringList nodeIds(NodePtr node) const;
+ virtual QStringList nodeNames(NodePtr node) const = 0;
+ virtual bool isNullNode(NodePtr node) const = 0;
+ virtual NodePtr parentNode(NodePtr node) const = 0;
+ virtual NodePtr previousSiblingNode(NodePtr node) const = 0;
+ virtual NodePtr duplicateNode(NodePtr node) const = 0;
+ virtual void freeNode(NodePtr node) const = 0;
+
+ QVector<StyleSheet> styleSheets;
+ QString medium;
+ Qt::CaseSensitivity nameCaseSensitivity;
+private:
+ void matchRule(NodePtr node, const StyleRule &rules, StyleSheetOrigin origin,
+ int depth, QMap<uint, StyleRule> *weightedRules);
+ bool selectorMatches(const Selector &rule, NodePtr node);
+ bool basicSelectorMatches(const BasicSelector &rule, NodePtr node);
+};
+
+enum TokenType {
+ NONE,
+
+ S,
+
+ CDO,
+ CDC,
+ INCLUDES,
+ DASHMATCH,
+
+ LBRACE,
+ PLUS,
+ GREATER,
+ COMMA,
+
+ STRING,
+ INVALID,
+
+ IDENT,
+
+ HASH,
+
+ ATKEYWORD_SYM,
+
+ EXCLAMATION_SYM,
+
+ LENGTH,
+
+ PERCENTAGE,
+ NUMBER,
+
+ FUNCTION,
+
+ COLON,
+ SEMICOLON,
+ RBRACE,
+ SLASH,
+ MINUS,
+ DOT,
+ STAR,
+ LBRACKET,
+ RBRACKET,
+ EQUAL,
+ LPAREN,
+ RPAREN,
+ OR
+};
+
+struct Q_GUI_EXPORT Symbol
+{
+ inline Symbol() : start(0), len(-1) {}
+ TokenType token;
+ QString text;
+ int start, len;
+ QString lexem() const;
+};
+
+class Q_AUTOTEST_EXPORT Scanner
+{
+public:
+ static QString preprocess(const QString &input, bool *hasEscapeSequences = 0);
+ static void scan(const QString &preprocessedInput, QVector<Symbol> *symbols);
+ static const char *tokenName(TokenType t);
+};
+
+class Q_GUI_EXPORT Parser
+{
+public:
+ Parser();
+ Parser(const QString &css, bool file = false);
+
+ void init(const QString &css, bool file = false);
+ bool parse(StyleSheet *styleSheet, Qt::CaseSensitivity nameCaseSensitivity = Qt::CaseSensitive);
+ Symbol errorSymbol();
+
+ bool parseImport(ImportRule *importRule);
+ bool parseMedia(MediaRule *mediaRule);
+ bool parseMedium(QStringList *media);
+ bool parsePage(PageRule *pageRule);
+ bool parsePseudoPage(QString *selector);
+ bool parseNextOperator(Value *value);
+ bool parseCombinator(BasicSelector::Relation *relation);
+ bool parseProperty(Declaration *decl);
+ bool parseRuleset(StyleRule *styleRule);
+ bool parseSelector(Selector *sel);
+ bool parseSimpleSelector(BasicSelector *basicSel);
+ bool parseClass(QString *name);
+ bool parseElementName(QString *name);
+ bool parseAttrib(AttributeSelector *attr);
+ bool parsePseudo(Pseudo *pseudo);
+ bool parseNextDeclaration(Declaration *declaration);
+ bool parsePrio(Declaration *declaration);
+ bool parseExpr(QVector<Value> *values);
+ bool parseTerm(Value *value);
+ bool parseFunction(QString *name, QString *args);
+ bool parseHexColor(QColor *col);
+ bool testAndParseUri(QString *uri);
+
+ inline bool testRuleset() { return testSelector(); }
+ inline bool testSelector() { return testSimpleSelector(); }
+ inline bool parseNextSelector(Selector *sel) { if (!testSelector()) return recordError(); return parseSelector(sel); }
+ bool testSimpleSelector();
+ inline bool parseNextSimpleSelector(BasicSelector *basicSel) { if (!testSimpleSelector()) return recordError(); return parseSimpleSelector(basicSel); }
+ inline bool testElementName() { return test(IDENT) || test(STAR); }
+ inline bool testClass() { return test(DOT); }
+ inline bool testAttrib() { return test(LBRACKET); }
+ inline bool testPseudo() { return test(COLON); }
+ inline bool testMedium() { return test(IDENT); }
+ inline bool parseNextMedium(QStringList *media) { if (!testMedium()) return recordError(); return parseMedium(media); }
+ inline bool testPseudoPage() { return test(COLON); }
+ inline bool testImport() { return testTokenAndEndsWith(ATKEYWORD_SYM, QLatin1String("import")); }
+ inline bool testMedia() { return testTokenAndEndsWith(ATKEYWORD_SYM, QLatin1String("media")); }
+ inline bool testPage() { return testTokenAndEndsWith(ATKEYWORD_SYM, QLatin1String("page")); }
+ inline bool testCombinator() { return test(PLUS) || test(GREATER) || test(S); }
+ inline bool testProperty() { return test(IDENT); }
+ bool testTerm();
+ inline bool testExpr() { return testTerm(); }
+ inline bool parseNextExpr(QVector<Value> *values) { if (!testExpr()) return recordError(); return parseExpr(values); }
+ bool testPrio();
+ inline bool testHexColor() { return test(HASH); }
+ inline bool testFunction() { return test(FUNCTION); }
+ inline bool parseNextFunction(QString *name, QString *args) { if (!testFunction()) return recordError(); return parseFunction(name, args); }
+
+ inline bool lookupElementName() const { return lookup() == IDENT || lookup() == STAR; }
+
+ inline void skipSpace() { while (test(S)) {}; }
+
+ inline bool hasNext() const { return index < symbols.count(); }
+ inline TokenType next() { return symbols.at(index++).token; }
+ bool next(TokenType t);
+ bool test(TokenType t);
+ inline void prev() { index--; }
+ inline const Symbol &symbol() const { return symbols.at(index - 1); }
+ inline QString lexem() const { return symbol().lexem(); }
+ QString unquotedLexem() const;
+ QString lexemUntil(TokenType t);
+ bool until(TokenType target, TokenType target2 = NONE);
+ inline TokenType lookup() const {
+ return (index - 1) < symbols.count() ? symbols.at(index - 1).token : NONE;
+ }
+
+ bool testTokenAndEndsWith(TokenType t, const QLatin1String &str);
+
+ inline bool recordError() { errorIndex = index; return false; }
+
+ QVector<Symbol> symbols;
+ int index;
+ int errorIndex;
+ bool hasEscapeSequences;
+ QString sourcePath;
+};
+
+} // namespace QCss
+
+QT_END_NAMESPACE
+
+Q_DECLARE_METATYPE( QCss::BackgroundData )
+Q_DECLARE_METATYPE( QCss::LengthData )
+Q_DECLARE_METATYPE( QCss::BorderData )
+
+
+#endif // QT_NO_CSSPARSER
+
+#endif
diff --git a/src/gui/text/qcssscanner.cpp b/src/gui/text/qcssscanner.cpp
new file mode 100644
index 0000000000..2f3fdb790b
--- /dev/null
+++ b/src/gui/text/qcssscanner.cpp
@@ -0,0 +1,1146 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+// auto generated. DO NOT EDIT.
+class QCssScanner_Generated
+{
+public:
+ QCssScanner_Generated(const QString &inp);
+
+ inline QChar next() {
+ return (pos < input.length()) ? input.at(pos++).toLower() : QChar();
+ }
+ int handleCommentStart();
+ int lex();
+
+ QString input;
+ int pos;
+ int lexemStart;
+ int lexemLength;
+};
+
+QCssScanner_Generated::QCssScanner_Generated(const QString &inp)
+{
+ input = inp;
+ pos = 0;
+ lexemStart = 0;
+ lexemLength = 0;
+}
+
+
+int QCssScanner_Generated::lex()
+{
+ lexemStart = pos;
+ lexemLength = 0;
+ int lastAcceptingPos = -1;
+ int token = -1;
+ QChar ch;
+
+ // initial state
+ ch = next();
+ if (ch.unicode() >= 9 && ch.unicode() <= 10)
+ goto state_1;
+ if (ch.unicode() >= 12 && ch.unicode() <= 13)
+ goto state_1;
+ if (ch.unicode() == 32)
+ goto state_1;
+ if (ch.unicode() == 33) {
+ token = QCss::EXCLAMATION_SYM;
+ goto found;
+ }
+ if (ch.unicode() == 34)
+ goto state_3;
+ if (ch.unicode() == 35)
+ goto state_4;
+ if (ch.unicode() == 39)
+ goto state_5;
+ if (ch.unicode() == 40) {
+ token = QCss::LPAREN;
+ goto found;
+ }
+ if (ch.unicode() == 41) {
+ token = QCss::RPAREN;
+ goto found;
+ }
+ if (ch.unicode() == 42) {
+ token = QCss::STAR;
+ goto found;
+ }
+ if (ch.unicode() == 43)
+ goto state_9;
+ if (ch.unicode() == 44)
+ goto state_10;
+ if (ch.unicode() == 45)
+ goto state_11;
+ if (ch.unicode() == 46)
+ goto state_12;
+ if (ch.unicode() == 47)
+ goto state_13;
+ if (ch.unicode() >= 48 && ch.unicode() <= 57)
+ goto state_14;
+ if (ch.unicode() == 58) {
+ token = QCss::COLON;
+ goto found;
+ }
+ if (ch.unicode() == 59) {
+ token = QCss::SEMICOLON;
+ goto found;
+ }
+ if (ch.unicode() == 60)
+ goto state_17;
+ if (ch.unicode() == 61) {
+ token = QCss::EQUAL;
+ goto found;
+ }
+ if (ch.unicode() == 62)
+ goto state_19;
+ if (ch.unicode() == 64)
+ goto state_20;
+ if (ch.unicode() == 91) {
+ token = QCss::LBRACKET;
+ goto found;
+ }
+ if (ch.unicode() == 92)
+ goto state_22;
+ if (ch.unicode() == 93) {
+ token = QCss::RBRACKET;
+ goto found;
+ }
+ if (ch.unicode() == 95)
+ goto state_24;
+ if ((ch.unicode() >= 'a' && ch.unicode() <= 'z') || ch.unicode() >= 256)
+ goto state_24;
+ if (ch.unicode() == 123)
+ goto state_25;
+ if (ch.unicode() == 124)
+ goto state_26;
+ if (ch.unicode() == 125) {
+ token = QCss::RBRACE;
+ goto found;
+ }
+ if (ch.unicode() == 126)
+ goto state_28;
+ goto out;
+ state_1:
+ lastAcceptingPos = pos;
+ token = QCss::S;
+ ch = next();
+ if (ch.unicode() >= 9 && ch.unicode() <= 10)
+ goto state_29;
+ if (ch.unicode() >= 12 && ch.unicode() <= 13)
+ goto state_29;
+ if (ch.unicode() == 32)
+ goto state_29;
+ if (ch.unicode() == 43)
+ goto state_9;
+ if (ch.unicode() == 44)
+ goto state_10;
+ if (ch.unicode() == 62)
+ goto state_19;
+ if (ch.unicode() == 123)
+ goto state_25;
+ goto out;
+ state_3:
+ lastAcceptingPos = pos;
+ token = QCss::INVALID;
+ ch = next();
+ if (ch.unicode() >= 1 && ch.unicode() <= 9)
+ goto state_30;
+ if (ch.unicode() == 11)
+ goto state_30;
+ if (ch.unicode() >= 14 && ch.unicode() <= 33)
+ goto state_30;
+ if (ch.unicode() == 34)
+ goto state_31;
+ if (ch.unicode() >= 35 && ch.unicode() <= 91)
+ goto state_30;
+ if (ch.unicode() == 92)
+ goto state_32;
+ if (ch.unicode() >= 93 && ch.unicode() <= 96)
+ goto state_30;
+ if ((ch.unicode() >= 'a' && ch.unicode() <= 'z') || ch.unicode() >= 256)
+ goto state_30;
+ if (ch.unicode() >= 123)
+ goto state_30;
+ goto out;
+ state_4:
+ ch = next();
+ if (ch.unicode() == 45)
+ goto state_33;
+ if (ch.unicode() >= 48 && ch.unicode() <= 57)
+ goto state_33;
+ if (ch.unicode() == 92)
+ goto state_34;
+ if (ch.unicode() == 95)
+ goto state_33;
+ if ((ch.unicode() >= 'a' && ch.unicode() <= 'z') || ch.unicode() >= 256)
+ goto state_33;
+ goto out;
+ state_5:
+ lastAcceptingPos = pos;
+ token = QCss::INVALID;
+ ch = next();
+ if (ch.unicode() >= 1 && ch.unicode() <= 9)
+ goto state_35;
+ if (ch.unicode() == 11)
+ goto state_35;
+ if (ch.unicode() >= 14 && ch.unicode() <= 38)
+ goto state_35;
+ if (ch.unicode() == 39)
+ goto state_36;
+ if (ch.unicode() >= 40 && ch.unicode() <= 91)
+ goto state_35;
+ if (ch.unicode() == 92)
+ goto state_37;
+ if (ch.unicode() >= 93 && ch.unicode() <= 96)
+ goto state_35;
+ if ((ch.unicode() >= 'a' && ch.unicode() <= 'z') || ch.unicode() >= 256)
+ goto state_35;
+ if (ch.unicode() >= 123)
+ goto state_35;
+ goto out;
+ state_9:
+ lastAcceptingPos = pos;
+ token = QCss::PLUS;
+ goto out;
+ state_10:
+ lastAcceptingPos = pos;
+ token = QCss::COMMA;
+ goto out;
+ state_11:
+ lastAcceptingPos = pos;
+ token = QCss::MINUS;
+ ch = next();
+ if (ch.unicode() == 45)
+ goto state_38;
+ if (ch.unicode() == 92)
+ goto state_22;
+ if (ch.unicode() == 95)
+ goto state_24;
+ if ((ch.unicode() >= 'a' && ch.unicode() <= 'z') || ch.unicode() >= 256)
+ goto state_24;
+ goto out;
+ state_12:
+ lastAcceptingPos = pos;
+ token = QCss::DOT;
+ ch = next();
+ if (ch.unicode() >= 48 && ch.unicode() <= 57)
+ goto state_39;
+ goto out;
+ state_13:
+ lastAcceptingPos = pos;
+ token = QCss::SLASH;
+ ch = next();
+ if (ch.unicode() == 42) {
+ token = handleCommentStart();
+ goto found;
+ }
+ goto out;
+ state_14:
+ lastAcceptingPos = pos;
+ token = QCss::NUMBER;
+ ch = next();
+ if (ch.unicode() == 37)
+ goto state_41;
+ if (ch.unicode() == 45)
+ goto state_42;
+ if (ch.unicode() == 46)
+ goto state_43;
+ if (ch.unicode() >= 48 && ch.unicode() <= 57)
+ goto state_44;
+ if (ch.unicode() == 92)
+ goto state_45;
+ if (ch.unicode() == 95)
+ goto state_46;
+ if ((ch.unicode() >= 'a' && ch.unicode() <= 'z') || ch.unicode() >= 256)
+ goto state_46;
+ goto out;
+ state_17:
+ ch = next();
+ if (ch.unicode() == 33)
+ goto state_47;
+ goto out;
+ state_19:
+ lastAcceptingPos = pos;
+ token = QCss::GREATER;
+ goto out;
+ state_20:
+ ch = next();
+ if (ch.unicode() == 45)
+ goto state_48;
+ if (ch.unicode() == 92)
+ goto state_49;
+ if (ch.unicode() == 95)
+ goto state_50;
+ if ((ch.unicode() >= 'a' && ch.unicode() <= 'z') || ch.unicode() >= 256)
+ goto state_50;
+ goto out;
+ state_22:
+ ch = next();
+ if (ch.unicode() >= 1 && ch.unicode() <= 9)
+ goto state_51;
+ if (ch.unicode() == 11)
+ goto state_51;
+ if (ch.unicode() >= 14 && ch.unicode() <= 47)
+ goto state_51;
+ if (ch.unicode() >= 58 && ch.unicode() <= 96)
+ goto state_51;
+ if (ch.unicode() >= 103)
+ goto state_51;
+ goto out;
+ state_24:
+ lastAcceptingPos = pos;
+ token = QCss::IDENT;
+ ch = next();
+ if (ch.unicode() == 40)
+ goto state_52;
+ if (ch.unicode() == 45)
+ goto state_53;
+ if (ch.unicode() >= 48 && ch.unicode() <= 57)
+ goto state_53;
+ if (ch.unicode() == 92)
+ goto state_54;
+ if (ch.unicode() == 95)
+ goto state_53;
+ if ((ch.unicode() >= 'a' && ch.unicode() <= 'z') || ch.unicode() >= 256)
+ goto state_53;
+ goto out;
+ state_25:
+ lastAcceptingPos = pos;
+ token = QCss::LBRACE;
+ goto out;
+ state_26:
+ lastAcceptingPos = pos;
+ token = QCss::OR;
+ ch = next();
+ if (ch.unicode() == 61) {
+ token = QCss::DASHMATCH;
+ goto found;
+ }
+ goto out;
+ state_28:
+ ch = next();
+ if (ch.unicode() == 61) {
+ token = QCss::INCLUDES;
+ goto found;
+ }
+ goto out;
+ state_29:
+ lastAcceptingPos = pos;
+ token = QCss::S;
+ ch = next();
+ if (ch.unicode() >= 9 && ch.unicode() <= 10)
+ goto state_29;
+ if (ch.unicode() >= 12 && ch.unicode() <= 13)
+ goto state_29;
+ if (ch.unicode() == 32)
+ goto state_29;
+ if (ch.unicode() == 43)
+ goto state_9;
+ if (ch.unicode() == 44)
+ goto state_10;
+ if (ch.unicode() == 62)
+ goto state_19;
+ if (ch.unicode() == 123)
+ goto state_25;
+ goto out;
+ state_30:
+ lastAcceptingPos = pos;
+ token = QCss::INVALID;
+ ch = next();
+ if (ch.unicode() >= 1 && ch.unicode() <= 9)
+ goto state_30;
+ if (ch.unicode() == 11)
+ goto state_30;
+ if (ch.unicode() >= 14 && ch.unicode() <= 33)
+ goto state_30;
+ if (ch.unicode() == 34)
+ goto state_31;
+ if (ch.unicode() >= 35 && ch.unicode() <= 91)
+ goto state_30;
+ if (ch.unicode() == 92)
+ goto state_32;
+ if (ch.unicode() >= 93 && ch.unicode() <= 96)
+ goto state_30;
+ if ((ch.unicode() >= 'a' && ch.unicode() <= 'z') || ch.unicode() >= 256)
+ goto state_30;
+ if (ch.unicode() >= 123)
+ goto state_30;
+ goto out;
+ state_31:
+ lastAcceptingPos = pos;
+ token = QCss::STRING;
+ goto out;
+ state_32:
+ ch = next();
+ if (ch.unicode() >= 1 && ch.unicode() <= 9)
+ goto state_57;
+ if (ch.unicode() == 10)
+ goto state_58;
+ if (ch.unicode() == 11)
+ goto state_57;
+ if (ch.unicode() == 12)
+ goto state_59;
+ if (ch.unicode() == 13)
+ goto state_60;
+ if (ch.unicode() >= 14 && ch.unicode() <= 47)
+ goto state_57;
+ if (ch.unicode() >= 58 && ch.unicode() <= 96)
+ goto state_57;
+ if (ch.unicode() >= 103)
+ goto state_57;
+ goto out;
+ state_33:
+ lastAcceptingPos = pos;
+ token = QCss::HASH;
+ ch = next();
+ if (ch.unicode() == 45)
+ goto state_61;
+ if (ch.unicode() >= 48 && ch.unicode() <= 57)
+ goto state_61;
+ if (ch.unicode() == 92)
+ goto state_62;
+ if (ch.unicode() == 95)
+ goto state_61;
+ if ((ch.unicode() >= 'a' && ch.unicode() <= 'z') || ch.unicode() >= 256)
+ goto state_61;
+ goto out;
+ state_34:
+ ch = next();
+ if (ch.unicode() >= 1 && ch.unicode() <= 9)
+ goto state_63;
+ if (ch.unicode() == 11)
+ goto state_63;
+ if (ch.unicode() >= 14 && ch.unicode() <= 47)
+ goto state_63;
+ if (ch.unicode() >= 58 && ch.unicode() <= 96)
+ goto state_63;
+ if (ch.unicode() >= 103)
+ goto state_63;
+ goto out;
+ state_35:
+ lastAcceptingPos = pos;
+ token = QCss::INVALID;
+ ch = next();
+ if (ch.unicode() >= 1 && ch.unicode() <= 9)
+ goto state_35;
+ if (ch.unicode() == 11)
+ goto state_35;
+ if (ch.unicode() >= 14 && ch.unicode() <= 38)
+ goto state_35;
+ if (ch.unicode() == 39)
+ goto state_36;
+ if (ch.unicode() >= 40 && ch.unicode() <= 91)
+ goto state_35;
+ if (ch.unicode() == 92)
+ goto state_37;
+ if (ch.unicode() >= 93 && ch.unicode() <= 96)
+ goto state_35;
+ if ((ch.unicode() >= 'a' && ch.unicode() <= 'z') || ch.unicode() >= 256)
+ goto state_35;
+ if (ch.unicode() >= 123)
+ goto state_35;
+ goto out;
+ state_36:
+ lastAcceptingPos = pos;
+ token = QCss::STRING;
+ goto out;
+ state_37:
+ ch = next();
+ if (ch.unicode() >= 1 && ch.unicode() <= 9)
+ goto state_64;
+ if (ch.unicode() == 10)
+ goto state_65;
+ if (ch.unicode() == 11)
+ goto state_64;
+ if (ch.unicode() == 12)
+ goto state_66;
+ if (ch.unicode() == 13)
+ goto state_67;
+ if (ch.unicode() >= 14 && ch.unicode() <= 47)
+ goto state_64;
+ if (ch.unicode() >= 58 && ch.unicode() <= 96)
+ goto state_64;
+ if (ch.unicode() >= 103)
+ goto state_64;
+ goto out;
+ state_38:
+ ch = next();
+ if (ch.unicode() == 62) {
+ token = QCss::CDC;
+ goto found;
+ }
+ goto out;
+ state_39:
+ lastAcceptingPos = pos;
+ token = QCss::NUMBER;
+ ch = next();
+ if (ch.unicode() == 37)
+ goto state_41;
+ if (ch.unicode() == 45)
+ goto state_42;
+ if (ch.unicode() >= 48 && ch.unicode() <= 57)
+ goto state_69;
+ if (ch.unicode() == 92)
+ goto state_45;
+ if (ch.unicode() == 95)
+ goto state_46;
+ if ((ch.unicode() >= 'a' && ch.unicode() <= 'z') || ch.unicode() >= 256)
+ goto state_46;
+ goto out;
+ state_41:
+ lastAcceptingPos = pos;
+ token = QCss::PERCENTAGE;
+ goto out;
+ state_42:
+ ch = next();
+ if (ch.unicode() == 92)
+ goto state_45;
+ if (ch.unicode() == 95)
+ goto state_46;
+ if ((ch.unicode() >= 'a' && ch.unicode() <= 'z') || ch.unicode() >= 256)
+ goto state_46;
+ goto out;
+ state_43:
+ ch = next();
+ if (ch.unicode() >= 48 && ch.unicode() <= 57)
+ goto state_39;
+ goto out;
+ state_44:
+ lastAcceptingPos = pos;
+ token = QCss::NUMBER;
+ ch = next();
+ if (ch.unicode() == 37)
+ goto state_41;
+ if (ch.unicode() == 45)
+ goto state_42;
+ if (ch.unicode() == 46)
+ goto state_43;
+ if (ch.unicode() >= 48 && ch.unicode() <= 57)
+ goto state_44;
+ if (ch.unicode() == 92)
+ goto state_45;
+ if (ch.unicode() == 95)
+ goto state_46;
+ if ((ch.unicode() >= 'a' && ch.unicode() <= 'z') || ch.unicode() >= 256)
+ goto state_46;
+ goto out;
+ state_45:
+ ch = next();
+ if (ch.unicode() >= 1 && ch.unicode() <= 9)
+ goto state_70;
+ if (ch.unicode() == 11)
+ goto state_70;
+ if (ch.unicode() >= 14 && ch.unicode() <= 47)
+ goto state_70;
+ if (ch.unicode() >= 58 && ch.unicode() <= 96)
+ goto state_70;
+ if (ch.unicode() >= 103)
+ goto state_70;
+ goto out;
+ state_46:
+ lastAcceptingPos = pos;
+ token = QCss::LENGTH;
+ ch = next();
+ if (ch.unicode() == 45)
+ goto state_71;
+ if (ch.unicode() >= 48 && ch.unicode() <= 57)
+ goto state_71;
+ if (ch.unicode() == 92)
+ goto state_72;
+ if (ch.unicode() == 95)
+ goto state_71;
+ if ((ch.unicode() >= 'a' && ch.unicode() <= 'z') || ch.unicode() >= 256)
+ goto state_71;
+ goto out;
+ state_47:
+ ch = next();
+ if (ch.unicode() == 45)
+ goto state_73;
+ goto out;
+ state_48:
+ ch = next();
+ if (ch.unicode() == 92)
+ goto state_49;
+ if (ch.unicode() == 95)
+ goto state_50;
+ if ((ch.unicode() >= 'a' && ch.unicode() <= 'z') || ch.unicode() >= 256)
+ goto state_50;
+ goto out;
+ state_49:
+ ch = next();
+ if (ch.unicode() >= 1 && ch.unicode() <= 9)
+ goto state_74;
+ if (ch.unicode() == 11)
+ goto state_74;
+ if (ch.unicode() >= 14 && ch.unicode() <= 47)
+ goto state_74;
+ if (ch.unicode() >= 58 && ch.unicode() <= 96)
+ goto state_74;
+ if (ch.unicode() >= 103)
+ goto state_74;
+ goto out;
+ state_50:
+ lastAcceptingPos = pos;
+ token = QCss::ATKEYWORD_SYM;
+ ch = next();
+ if (ch.unicode() == 45)
+ goto state_75;
+ if (ch.unicode() >= 48 && ch.unicode() <= 57)
+ goto state_75;
+ if (ch.unicode() == 92)
+ goto state_76;
+ if (ch.unicode() == 95)
+ goto state_75;
+ if ((ch.unicode() >= 'a' && ch.unicode() <= 'z') || ch.unicode() >= 256)
+ goto state_75;
+ goto out;
+ state_51:
+ lastAcceptingPos = pos;
+ token = QCss::IDENT;
+ ch = next();
+ if (ch.unicode() == 40)
+ goto state_52;
+ if (ch.unicode() == 45)
+ goto state_53;
+ if (ch.unicode() >= 48 && ch.unicode() <= 57)
+ goto state_53;
+ if (ch.unicode() == 92)
+ goto state_54;
+ if (ch.unicode() == 95)
+ goto state_53;
+ if ((ch.unicode() >= 'a' && ch.unicode() <= 'z') || ch.unicode() >= 256)
+ goto state_53;
+ goto out;
+ state_52:
+ lastAcceptingPos = pos;
+ token = QCss::FUNCTION;
+ goto out;
+ state_53:
+ lastAcceptingPos = pos;
+ token = QCss::IDENT;
+ ch = next();
+ if (ch.unicode() == 40)
+ goto state_52;
+ if (ch.unicode() == 45)
+ goto state_53;
+ if (ch.unicode() >= 48 && ch.unicode() <= 57)
+ goto state_53;
+ if (ch.unicode() == 92)
+ goto state_54;
+ if (ch.unicode() == 95)
+ goto state_53;
+ if ((ch.unicode() >= 'a' && ch.unicode() <= 'z') || ch.unicode() >= 256)
+ goto state_53;
+ goto out;
+ state_54:
+ ch = next();
+ if (ch.unicode() >= 1 && ch.unicode() <= 9)
+ goto state_77;
+ if (ch.unicode() == 11)
+ goto state_77;
+ if (ch.unicode() >= 14 && ch.unicode() <= 47)
+ goto state_77;
+ if (ch.unicode() >= 58 && ch.unicode() <= 96)
+ goto state_77;
+ if (ch.unicode() >= 103)
+ goto state_77;
+ goto out;
+ state_57:
+ lastAcceptingPos = pos;
+ token = QCss::INVALID;
+ ch = next();
+ if (ch.unicode() >= 1 && ch.unicode() <= 9)
+ goto state_30;
+ if (ch.unicode() == 11)
+ goto state_30;
+ if (ch.unicode() >= 14 && ch.unicode() <= 33)
+ goto state_30;
+ if (ch.unicode() == 34)
+ goto state_31;
+ if (ch.unicode() >= 35 && ch.unicode() <= 91)
+ goto state_30;
+ if (ch.unicode() == 92)
+ goto state_32;
+ if (ch.unicode() >= 93 && ch.unicode() <= 96)
+ goto state_30;
+ if ((ch.unicode() >= 'a' && ch.unicode() <= 'z') || ch.unicode() >= 256)
+ goto state_30;
+ if (ch.unicode() >= 123)
+ goto state_30;
+ goto out;
+ state_58:
+ lastAcceptingPos = pos;
+ token = QCss::INVALID;
+ ch = next();
+ if (ch.unicode() >= 1 && ch.unicode() <= 9)
+ goto state_30;
+ if (ch.unicode() == 11)
+ goto state_30;
+ if (ch.unicode() >= 14 && ch.unicode() <= 33)
+ goto state_30;
+ if (ch.unicode() == 34)
+ goto state_31;
+ if (ch.unicode() >= 35 && ch.unicode() <= 91)
+ goto state_30;
+ if (ch.unicode() == 92)
+ goto state_32;
+ if (ch.unicode() >= 93 && ch.unicode() <= 96)
+ goto state_30;
+ if ((ch.unicode() >= 'a' && ch.unicode() <= 'z') || ch.unicode() >= 256)
+ goto state_30;
+ if (ch.unicode() >= 123)
+ goto state_30;
+ goto out;
+ state_59:
+ lastAcceptingPos = pos;
+ token = QCss::INVALID;
+ ch = next();
+ if (ch.unicode() >= 1 && ch.unicode() <= 9)
+ goto state_30;
+ if (ch.unicode() == 11)
+ goto state_30;
+ if (ch.unicode() >= 14 && ch.unicode() <= 33)
+ goto state_30;
+ if (ch.unicode() == 34)
+ goto state_31;
+ if (ch.unicode() >= 35 && ch.unicode() <= 91)
+ goto state_30;
+ if (ch.unicode() == 92)
+ goto state_32;
+ if (ch.unicode() >= 93 && ch.unicode() <= 96)
+ goto state_30;
+ if ((ch.unicode() >= 'a' && ch.unicode() <= 'z') || ch.unicode() >= 256)
+ goto state_30;
+ if (ch.unicode() >= 123)
+ goto state_30;
+ goto out;
+ state_60:
+ lastAcceptingPos = pos;
+ token = QCss::INVALID;
+ ch = next();
+ if (ch.unicode() >= 1 && ch.unicode() <= 9)
+ goto state_30;
+ if (ch.unicode() == 10)
+ goto state_78;
+ if (ch.unicode() == 11)
+ goto state_30;
+ if (ch.unicode() >= 14 && ch.unicode() <= 33)
+ goto state_30;
+ if (ch.unicode() == 34)
+ goto state_31;
+ if (ch.unicode() >= 35 && ch.unicode() <= 91)
+ goto state_30;
+ if (ch.unicode() == 92)
+ goto state_32;
+ if (ch.unicode() >= 93 && ch.unicode() <= 96)
+ goto state_30;
+ if ((ch.unicode() >= 'a' && ch.unicode() <= 'z') || ch.unicode() >= 256)
+ goto state_30;
+ if (ch.unicode() >= 123)
+ goto state_30;
+ goto out;
+ state_61:
+ lastAcceptingPos = pos;
+ token = QCss::HASH;
+ ch = next();
+ if (ch.unicode() == 45)
+ goto state_61;
+ if (ch.unicode() >= 48 && ch.unicode() <= 57)
+ goto state_61;
+ if (ch.unicode() == 92)
+ goto state_62;
+ if (ch.unicode() == 95)
+ goto state_61;
+ if ((ch.unicode() >= 'a' && ch.unicode() <= 'z') || ch.unicode() >= 256)
+ goto state_61;
+ goto out;
+ state_62:
+ ch = next();
+ if (ch.unicode() >= 1 && ch.unicode() <= 9)
+ goto state_79;
+ if (ch.unicode() == 11)
+ goto state_79;
+ if (ch.unicode() >= 14 && ch.unicode() <= 47)
+ goto state_79;
+ if (ch.unicode() >= 58 && ch.unicode() <= 96)
+ goto state_79;
+ if (ch.unicode() >= 103)
+ goto state_79;
+ goto out;
+ state_63:
+ lastAcceptingPos = pos;
+ token = QCss::HASH;
+ ch = next();
+ if (ch.unicode() == 45)
+ goto state_61;
+ if (ch.unicode() >= 48 && ch.unicode() <= 57)
+ goto state_61;
+ if (ch.unicode() == 92)
+ goto state_62;
+ if (ch.unicode() == 95)
+ goto state_61;
+ if ((ch.unicode() >= 'a' && ch.unicode() <= 'z') || ch.unicode() >= 256)
+ goto state_61;
+ goto out;
+ state_64:
+ lastAcceptingPos = pos;
+ token = QCss::INVALID;
+ ch = next();
+ if (ch.unicode() >= 1 && ch.unicode() <= 9)
+ goto state_35;
+ if (ch.unicode() == 11)
+ goto state_35;
+ if (ch.unicode() >= 14 && ch.unicode() <= 38)
+ goto state_35;
+ if (ch.unicode() == 39)
+ goto state_36;
+ if (ch.unicode() >= 40 && ch.unicode() <= 91)
+ goto state_35;
+ if (ch.unicode() == 92)
+ goto state_37;
+ if (ch.unicode() >= 93 && ch.unicode() <= 96)
+ goto state_35;
+ if ((ch.unicode() >= 'a' && ch.unicode() <= 'z') || ch.unicode() >= 256)
+ goto state_35;
+ if (ch.unicode() >= 123)
+ goto state_35;
+ goto out;
+ state_65:
+ lastAcceptingPos = pos;
+ token = QCss::INVALID;
+ ch = next();
+ if (ch.unicode() >= 1 && ch.unicode() <= 9)
+ goto state_35;
+ if (ch.unicode() == 11)
+ goto state_35;
+ if (ch.unicode() >= 14 && ch.unicode() <= 38)
+ goto state_35;
+ if (ch.unicode() == 39)
+ goto state_36;
+ if (ch.unicode() >= 40 && ch.unicode() <= 91)
+ goto state_35;
+ if (ch.unicode() == 92)
+ goto state_37;
+ if (ch.unicode() >= 93 && ch.unicode() <= 96)
+ goto state_35;
+ if ((ch.unicode() >= 'a' && ch.unicode() <= 'z') || ch.unicode() >= 256)
+ goto state_35;
+ if (ch.unicode() >= 123)
+ goto state_35;
+ goto out;
+ state_66:
+ lastAcceptingPos = pos;
+ token = QCss::INVALID;
+ ch = next();
+ if (ch.unicode() >= 1 && ch.unicode() <= 9)
+ goto state_35;
+ if (ch.unicode() == 11)
+ goto state_35;
+ if (ch.unicode() >= 14 && ch.unicode() <= 38)
+ goto state_35;
+ if (ch.unicode() == 39)
+ goto state_36;
+ if (ch.unicode() >= 40 && ch.unicode() <= 91)
+ goto state_35;
+ if (ch.unicode() == 92)
+ goto state_37;
+ if (ch.unicode() >= 93 && ch.unicode() <= 96)
+ goto state_35;
+ if ((ch.unicode() >= 'a' && ch.unicode() <= 'z') || ch.unicode() >= 256)
+ goto state_35;
+ if (ch.unicode() >= 123)
+ goto state_35;
+ goto out;
+ state_67:
+ lastAcceptingPos = pos;
+ token = QCss::INVALID;
+ ch = next();
+ if (ch.unicode() >= 1 && ch.unicode() <= 9)
+ goto state_35;
+ if (ch.unicode() == 10)
+ goto state_80;
+ if (ch.unicode() == 11)
+ goto state_35;
+ if (ch.unicode() >= 14 && ch.unicode() <= 38)
+ goto state_35;
+ if (ch.unicode() == 39)
+ goto state_36;
+ if (ch.unicode() >= 40 && ch.unicode() <= 91)
+ goto state_35;
+ if (ch.unicode() == 92)
+ goto state_37;
+ if (ch.unicode() >= 93 && ch.unicode() <= 96)
+ goto state_35;
+ if ((ch.unicode() >= 'a' && ch.unicode() <= 'z') || ch.unicode() >= 256)
+ goto state_35;
+ if (ch.unicode() >= 123)
+ goto state_35;
+ goto out;
+ state_69:
+ lastAcceptingPos = pos;
+ token = QCss::NUMBER;
+ ch = next();
+ if (ch.unicode() == 37)
+ goto state_41;
+ if (ch.unicode() == 45)
+ goto state_42;
+ if (ch.unicode() >= 48 && ch.unicode() <= 57)
+ goto state_69;
+ if (ch.unicode() == 92)
+ goto state_45;
+ if (ch.unicode() == 95)
+ goto state_46;
+ if ((ch.unicode() >= 'a' && ch.unicode() <= 'z') || ch.unicode() >= 256)
+ goto state_46;
+ goto out;
+ state_70:
+ lastAcceptingPos = pos;
+ token = QCss::LENGTH;
+ ch = next();
+ if (ch.unicode() == 45)
+ goto state_71;
+ if (ch.unicode() >= 48 && ch.unicode() <= 57)
+ goto state_71;
+ if (ch.unicode() == 92)
+ goto state_72;
+ if (ch.unicode() == 95)
+ goto state_71;
+ if ((ch.unicode() >= 'a' && ch.unicode() <= 'z') || ch.unicode() >= 256)
+ goto state_71;
+ goto out;
+ state_71:
+ lastAcceptingPos = pos;
+ token = QCss::LENGTH;
+ ch = next();
+ if (ch.unicode() == 45)
+ goto state_71;
+ if (ch.unicode() >= 48 && ch.unicode() <= 57)
+ goto state_71;
+ if (ch.unicode() == 92)
+ goto state_72;
+ if (ch.unicode() == 95)
+ goto state_71;
+ if ((ch.unicode() >= 'a' && ch.unicode() <= 'z') || ch.unicode() >= 256)
+ goto state_71;
+ goto out;
+ state_72:
+ ch = next();
+ if (ch.unicode() >= 1 && ch.unicode() <= 9)
+ goto state_81;
+ if (ch.unicode() == 11)
+ goto state_81;
+ if (ch.unicode() >= 14 && ch.unicode() <= 47)
+ goto state_81;
+ if (ch.unicode() >= 58 && ch.unicode() <= 96)
+ goto state_81;
+ if (ch.unicode() >= 103)
+ goto state_81;
+ goto out;
+ state_73:
+ ch = next();
+ if (ch.unicode() == 45) {
+ token = QCss::CDO;
+ goto found;
+ }
+ goto out;
+ state_74:
+ lastAcceptingPos = pos;
+ token = QCss::ATKEYWORD_SYM;
+ ch = next();
+ if (ch.unicode() == 45)
+ goto state_75;
+ if (ch.unicode() >= 48 && ch.unicode() <= 57)
+ goto state_75;
+ if (ch.unicode() == 92)
+ goto state_76;
+ if (ch.unicode() == 95)
+ goto state_75;
+ if ((ch.unicode() >= 'a' && ch.unicode() <= 'z') || ch.unicode() >= 256)
+ goto state_75;
+ goto out;
+ state_75:
+ lastAcceptingPos = pos;
+ token = QCss::ATKEYWORD_SYM;
+ ch = next();
+ if (ch.unicode() == 45)
+ goto state_75;
+ if (ch.unicode() >= 48 && ch.unicode() <= 57)
+ goto state_75;
+ if (ch.unicode() == 92)
+ goto state_76;
+ if (ch.unicode() == 95)
+ goto state_75;
+ if ((ch.unicode() >= 'a' && ch.unicode() <= 'z') || ch.unicode() >= 256)
+ goto state_75;
+ goto out;
+ state_76:
+ ch = next();
+ if (ch.unicode() >= 1 && ch.unicode() <= 9)
+ goto state_83;
+ if (ch.unicode() == 11)
+ goto state_83;
+ if (ch.unicode() >= 14 && ch.unicode() <= 47)
+ goto state_83;
+ if (ch.unicode() >= 58 && ch.unicode() <= 96)
+ goto state_83;
+ if (ch.unicode() >= 103)
+ goto state_83;
+ goto out;
+ state_77:
+ lastAcceptingPos = pos;
+ token = QCss::IDENT;
+ ch = next();
+ if (ch.unicode() == 40)
+ goto state_52;
+ if (ch.unicode() == 45)
+ goto state_53;
+ if (ch.unicode() >= 48 && ch.unicode() <= 57)
+ goto state_53;
+ if (ch.unicode() == 92)
+ goto state_54;
+ if (ch.unicode() == 95)
+ goto state_53;
+ if ((ch.unicode() >= 'a' && ch.unicode() <= 'z') || ch.unicode() >= 256)
+ goto state_53;
+ goto out;
+ state_78:
+ lastAcceptingPos = pos;
+ token = QCss::INVALID;
+ ch = next();
+ if (ch.unicode() >= 1 && ch.unicode() <= 9)
+ goto state_30;
+ if (ch.unicode() == 11)
+ goto state_30;
+ if (ch.unicode() >= 14 && ch.unicode() <= 33)
+ goto state_30;
+ if (ch.unicode() == 34)
+ goto state_31;
+ if (ch.unicode() >= 35 && ch.unicode() <= 91)
+ goto state_30;
+ if (ch.unicode() == 92)
+ goto state_32;
+ if (ch.unicode() >= 93 && ch.unicode() <= 96)
+ goto state_30;
+ if ((ch.unicode() >= 'a' && ch.unicode() <= 'z') || ch.unicode() >= 256)
+ goto state_30;
+ if (ch.unicode() >= 123)
+ goto state_30;
+ goto out;
+ state_79:
+ lastAcceptingPos = pos;
+ token = QCss::HASH;
+ ch = next();
+ if (ch.unicode() == 45)
+ goto state_61;
+ if (ch.unicode() >= 48 && ch.unicode() <= 57)
+ goto state_61;
+ if (ch.unicode() == 92)
+ goto state_62;
+ if (ch.unicode() == 95)
+ goto state_61;
+ if ((ch.unicode() >= 'a' && ch.unicode() <= 'z') || ch.unicode() >= 256)
+ goto state_61;
+ goto out;
+ state_80:
+ lastAcceptingPos = pos;
+ token = QCss::INVALID;
+ ch = next();
+ if (ch.unicode() >= 1 && ch.unicode() <= 9)
+ goto state_35;
+ if (ch.unicode() == 11)
+ goto state_35;
+ if (ch.unicode() >= 14 && ch.unicode() <= 38)
+ goto state_35;
+ if (ch.unicode() == 39)
+ goto state_36;
+ if (ch.unicode() >= 40 && ch.unicode() <= 91)
+ goto state_35;
+ if (ch.unicode() == 92)
+ goto state_37;
+ if (ch.unicode() >= 93 && ch.unicode() <= 96)
+ goto state_35;
+ if ((ch.unicode() >= 'a' && ch.unicode() <= 'z') || ch.unicode() >= 256)
+ goto state_35;
+ if (ch.unicode() >= 123)
+ goto state_35;
+ goto out;
+ state_81:
+ lastAcceptingPos = pos;
+ token = QCss::LENGTH;
+ ch = next();
+ if (ch.unicode() == 45)
+ goto state_71;
+ if (ch.unicode() >= 48 && ch.unicode() <= 57)
+ goto state_71;
+ if (ch.unicode() == 92)
+ goto state_72;
+ if (ch.unicode() == 95)
+ goto state_71;
+ if ((ch.unicode() >= 'a' && ch.unicode() <= 'z') || ch.unicode() >= 256)
+ goto state_71;
+ goto out;
+ state_83:
+ lastAcceptingPos = pos;
+ token = QCss::ATKEYWORD_SYM;
+ ch = next();
+ if (ch.unicode() == 45)
+ goto state_75;
+ if (ch.unicode() >= 48 && ch.unicode() <= 57)
+ goto state_75;
+ if (ch.unicode() == 92)
+ goto state_76;
+ if (ch.unicode() == 95)
+ goto state_75;
+ if ((ch.unicode() >= 'a' && ch.unicode() <= 'z') || ch.unicode() >= 256)
+ goto state_75;
+ goto out;
+ found:
+ lastAcceptingPos = pos;
+
+ out:
+ if (lastAcceptingPos != -1) {
+ lexemLength = lastAcceptingPos - lexemStart;
+ pos = lastAcceptingPos;
+ }
+ return token;
+}
+
diff --git a/src/gui/text/qfont.cpp b/src/gui/text/qfont.cpp
new file mode 100644
index 0000000000..43f5b1e4a8
--- /dev/null
+++ b/src/gui/text/qfont.cpp
@@ -0,0 +1,3018 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qfont.h"
+#include "qdebug.h"
+#include "qpaintdevice.h"
+#include "qfontdatabase.h"
+#include "qfontmetrics.h"
+#include "qfontinfo.h"
+#include "qpainter.h"
+#include "qhash.h"
+#include "qdatastream.h"
+#include "qapplication.h"
+#include "qstringlist.h"
+
+#include "qthread.h"
+#include "qthreadstorage.h"
+
+#include <private/qunicodetables_p.h>
+#include "qfont_p.h"
+#include <private/qfontengine_p.h>
+#include <private/qpainter_p.h>
+#include <private/qtextengine_p.h>
+#include <limits.h>
+
+#ifdef Q_WS_X11
+#include "qx11info_x11.h"
+#include <private/qt_x11_p.h>
+#endif
+#ifdef Q_WS_QWS
+#include "qscreen_qws.h"
+#if !defined(QT_NO_QWS_QPF2)
+#include <qfile.h>
+#include "qfontengine_qpf_p.h"
+#endif
+#endif
+
+#include <QMutexLocker>
+
+// #define QFONTCACHE_DEBUG
+#ifdef QFONTCACHE_DEBUG
+# define FC_DEBUG qDebug
+#else
+# define FC_DEBUG if (false) qDebug
+#endif
+
+QT_BEGIN_NAMESPACE
+
+#ifdef Q_WS_WIN
+extern HDC shared_dc();
+#endif
+
+#ifdef Q_WS_X11
+extern const QX11Info *qt_x11Info(const QPaintDevice *pd);
+#endif
+
+bool QFontDef::exactMatch(const QFontDef &other) const
+{
+ /*
+ QFontDef comparison is more complicated than just simple
+ per-member comparisons.
+
+ When comparing point/pixel sizes, either point or pixelsize
+ could be -1. in This case we have to compare the non negative
+ size value.
+
+ This test will fail if the point-sizes differ by 1/2 point or
+ more or they do not round to the same value. We have to do this
+ since our API still uses 'int' point-sizes in the API, but store
+ deci-point-sizes internally.
+
+ To compare the family members, we need to parse the font names
+ and compare the family/foundry strings separately. This allows
+ us to compare e.g. "Helvetica" and "Helvetica [Adobe]" with
+ positive results.
+ */
+ if (pixelSize != -1 && other.pixelSize != -1) {
+ if (pixelSize != other.pixelSize)
+ return false;
+ } else if (pointSize != -1 && other.pointSize != -1) {
+ if (pointSize != other.pointSize)
+ return false;
+ } else {
+ return false;
+ }
+
+ if (!ignorePitch && !other.ignorePitch && fixedPitch != other.fixedPitch)
+ return false;
+
+ if (stretch != 0 && other.stretch != 0 && stretch != other.stretch)
+ return false;
+
+ QString this_family, this_foundry, other_family, other_foundry;
+ QFontDatabase::parseFontName(family, this_foundry, this_family);
+ QFontDatabase::parseFontName(other.family, other_foundry, other_family);
+
+ return (styleHint == other.styleHint
+ && styleStrategy == other.styleStrategy
+ && weight == other.weight
+ && style == other.style
+ && this_family == other_family
+ && (this_foundry.isEmpty()
+ || other_foundry.isEmpty()
+ || this_foundry == other_foundry)
+#ifdef Q_WS_X11
+ && addStyle == other.addStyle
+#endif // Q_WS_X11
+ );
+}
+
+extern bool qt_is_gui_used;
+
+Q_GUI_EXPORT int qt_defaultDpiX()
+{
+ if (!qt_is_gui_used)
+ return 75;
+
+ int dpi;
+#ifdef Q_WS_X11
+ dpi = QX11Info::appDpiX();
+#elif defined(Q_WS_WIN)
+ dpi = GetDeviceCaps(shared_dc(),LOGPIXELSX);
+#elif defined(Q_WS_MAC)
+ extern float qt_mac_defaultDpi_x(); //qpaintdevice_mac.cpp
+ dpi = qt_mac_defaultDpi_x();
+#elif defined(Q_WS_QWS)
+ if (!qt_screen)
+ return 72;
+ QScreen *screen = qt_screen;
+ const QList<QScreen*> subScreens = qt_screen->subScreens();
+ if (!subScreens.isEmpty())
+ screen = subScreens.at(0);
+ dpi = qRound(screen->width() / (screen->physicalWidth() / qreal(25.4)));
+#endif // Q_WS_X11
+
+ return dpi;
+}
+
+Q_GUI_EXPORT int qt_defaultDpiY()
+{
+ if (!qt_is_gui_used)
+ return 75;
+
+ int dpi;
+#ifdef Q_WS_X11
+ dpi = QX11Info::appDpiY();
+#elif defined(Q_WS_WIN)
+ dpi = GetDeviceCaps(shared_dc(),LOGPIXELSY);
+#elif defined(Q_WS_MAC)
+ extern float qt_mac_defaultDpi_y(); //qpaintdevice_mac.cpp
+ dpi = qt_mac_defaultDpi_y();
+#elif defined(Q_WS_QWS)
+ if (!qt_screen)
+ return 72;
+ QScreen *screen = qt_screen;
+ const QList<QScreen*> subScreens = qt_screen->subScreens();
+ if (!subScreens.isEmpty())
+ screen = subScreens.at(0);
+ dpi = qRound(screen->height() / (screen->physicalHeight() / qreal(25.4)));
+#endif // Q_WS_X11
+
+ return dpi;
+}
+
+Q_GUI_EXPORT int qt_defaultDpi()
+{
+ return qt_defaultDpiY();
+}
+
+QFontPrivate::QFontPrivate()
+ : engineData(0), dpi(qt_defaultDpi()), screen(0),
+ rawMode(false), underline(false), overline(false), strikeOut(false), kerning(true),
+ capital(0), letterSpacingIsAbsolute(false), scFont(0)
+{
+ ref = 1;
+#ifdef Q_WS_X11
+ if (QX11Info::display())
+ screen = QX11Info::appScreen();
+ else
+ screen = 0;
+#endif
+#ifdef Q_WS_WIN
+ hdc = 0;
+#endif
+}
+
+QFontPrivate::QFontPrivate(const QFontPrivate &other)
+ : request(other.request), engineData(0), dpi(other.dpi), screen(other.screen),
+ rawMode(other.rawMode), underline(other.underline), overline(other.overline),
+ strikeOut(other.strikeOut), kerning(other.kerning),
+ capital(other.capital), letterSpacingIsAbsolute(other.letterSpacingIsAbsolute),
+ letterSpacing(other.letterSpacing), wordSpacing(other.wordSpacing),
+ scFont(other.scFont)
+{
+ ref = 1;
+#ifdef Q_WS_WIN
+ hdc = other.hdc;
+#endif
+ if (scFont && scFont != this)
+ scFont->ref.ref();
+}
+
+QFontPrivate::~QFontPrivate()
+{
+ if (engineData)
+ engineData->ref.deref();
+ engineData = 0;
+ if (scFont && scFont != this)
+ scFont->ref.deref();
+ scFont = 0;
+}
+
+#if !defined(Q_WS_MAC)
+extern QMutex *qt_fontdatabase_mutex();
+
+QFontEngine *QFontPrivate::engineForScript(int script) const
+{
+ QMutexLocker locker(qt_fontdatabase_mutex());
+ if (script >= QUnicodeTables::Inherited)
+ script = QUnicodeTables::Common;
+ if (engineData && engineData->fontCache != QFontCache::instance()) {
+ // throw out engineData that came from a different thread
+ engineData->ref.deref();
+ engineData = 0;
+ }
+ if (!engineData || !engineData->engines[script])
+ QFontDatabase::load(this, script);
+ return engineData->engines[script];
+}
+#else
+QFontEngine *QFontPrivate::engineForScript(int script) const
+{
+ extern QMutex *qt_fontdatabase_mutex();
+ QMutexLocker locker(qt_fontdatabase_mutex());
+ if (script >= QUnicodeTables::Inherited)
+ script = QUnicodeTables::Common;
+ if (engineData && engineData->fontCache != QFontCache::instance()) {
+ // throw out engineData that came from a different thread
+ engineData->ref.deref();
+ engineData = 0;
+ }
+ if (!engineData || !engineData->engine)
+ QFontDatabase::load(this, script);
+ return engineData->engine;
+}
+#endif
+
+void QFontPrivate::alterCharForCapitalization(QChar &c) const {
+ switch (capital) {
+ case QFont::AllUppercase:
+ case QFont::SmallCaps:
+ c = c.toUpper();
+ break;
+ case QFont::AllLowercase:
+ c = c.toLower();
+ break;
+ case QFont::MixedCase:
+ break;
+ }
+}
+
+QFontPrivate *QFontPrivate::smallCapsFontPrivate() const
+{
+ if (scFont)
+ return scFont;
+ QFont font(const_cast<QFontPrivate *>(this));
+ qreal pointSize = font.pointSizeF();
+ if (pointSize > 0)
+ font.setPointSizeF(pointSize * .7);
+ else
+ font.setPixelSize((font.pixelSize() * 7 + 5) / 10);
+ scFont = font.d;
+ if (scFont != this)
+ scFont->ref.ref();
+ return scFont;
+}
+
+
+void QFontPrivate::resolve(uint mask, const QFontPrivate *other)
+{
+ Q_ASSERT(other != 0);
+
+ dpi = other->dpi;
+
+ if ((mask & QFont::AllPropertiesResolved) == QFont::AllPropertiesResolved) return;
+
+ // assign the unset-bits with the set-bits of the other font def
+ if (! (mask & QFont::FamilyResolved))
+ request.family = other->request.family;
+
+ if (! (mask & QFont::SizeResolved)) {
+ request.pointSize = other->request.pointSize;
+ request.pixelSize = other->request.pixelSize;
+ }
+
+ if (! (mask & QFont::StyleHintResolved))
+ request.styleHint = other->request.styleHint;
+
+ if (! (mask & QFont::StyleStrategyResolved))
+ request.styleStrategy = other->request.styleStrategy;
+
+ if (! (mask & QFont::WeightResolved))
+ request.weight = other->request.weight;
+
+ if (! (mask & QFont::StyleResolved))
+ request.style = other->request.style;
+
+ if (! (mask & QFont::FixedPitchResolved))
+ request.fixedPitch = other->request.fixedPitch;
+
+ if (! (mask & QFont::StretchResolved))
+ request.stretch = other->request.stretch;
+
+ if (! (mask & QFont::UnderlineResolved))
+ underline = other->underline;
+
+ if (! (mask & QFont::OverlineResolved))
+ overline = other->overline;
+
+ if (! (mask & QFont::StrikeOutResolved))
+ strikeOut = other->strikeOut;
+
+ if (! (mask & QFont::KerningResolved))
+ kerning = other->kerning;
+
+ if (! (mask & QFont::LetterSpacingResolved)) {
+ letterSpacing = other->letterSpacing;
+ letterSpacingIsAbsolute = other->letterSpacingIsAbsolute;
+ }
+ if (! (mask & QFont::WordSpacingResolved))
+ wordSpacing = other->wordSpacing;
+ if (! (mask & QFont::CapitalizationResolved))
+ capital = other->capital;
+}
+
+
+
+
+QFontEngineData::QFontEngineData()
+ : ref(1), fontCache(QFontCache::instance())
+{
+#if !defined(Q_WS_MAC)
+ memset(engines, 0, QUnicodeTables::ScriptCount * sizeof(QFontEngine *));
+#else
+ engine = 0;
+#endif
+}
+
+QFontEngineData::~QFontEngineData()
+{
+#if !defined(Q_WS_MAC)
+ for (int i = 0; i < QUnicodeTables::ScriptCount; ++i) {
+ if (engines[i])
+ engines[i]->ref.deref();
+ engines[i] = 0;
+ }
+#else
+ if (engine)
+ engine->ref.deref();
+ engine = 0;
+#endif // Q_WS_X11 || Q_WS_WIN || Q_WS_MAC
+}
+
+
+
+
+/*!
+ \class QFont
+ \reentrant
+
+ \brief The QFont class specifies a font used for drawing text.
+
+ \ingroup multimedia
+ \ingroup appearance
+ \ingroup shared
+ \ingroup text
+ \mainclass
+
+ When you create a QFont object you specify various attributes that
+ you want the font to have. Qt will use the font with the specified
+ attributes, or if no matching font exists, Qt will use the closest
+ matching installed font. The attributes of the font that is
+ actually used are retrievable from a QFontInfo object. If the
+ window system provides an exact match exactMatch() returns true.
+ Use QFontMetrics to get measurements, e.g. the pixel length of a
+ string using QFontMetrics::width().
+
+ Note that a QApplication instance must exist before a QFont can be
+ used. You can set the application's default font with
+ QApplication::setFont().
+
+ If a chosen font does not include all the characters that
+ need to be displayed, QFont will try to find the characters in the
+ nearest equivalent fonts. When a QPainter draws a character from a
+ font the QFont will report whether or not it has the character; if
+ it does not, QPainter will draw an unfilled square.
+
+ Create QFonts like this:
+
+ \snippet doc/src/snippets/code/src_gui_text_qfont.cpp 0
+
+ The attributes set in the constructor can also be set later, e.g.
+ setFamily(), setPointSize(), setPointSizeFloat(), setWeight() and
+ setItalic(). The remaining attributes must be set after
+ contstruction, e.g. setBold(), setUnderline(), setOverline(),
+ setStrikeOut() and setFixedPitch(). QFontInfo objects should be
+ created \e after the font's attributes have been set. A QFontInfo
+ object will not change, even if you change the font's
+ attributes. The corresponding "get" functions, e.g. family(),
+ pointSize(), etc., return the values that were set, even though
+ the values used may differ. The actual values are available from a
+ QFontInfo object.
+
+ If the requested font family is unavailable you can influence the
+ \link #fontmatching font matching algorithm\endlink by choosing a
+ particular \l{QFont::StyleHint} and \l{QFont::StyleStrategy} with
+ setStyleHint(). The default family (corresponding to the current
+ style hint) is returned by defaultFamily().
+
+ The font-matching algorithm has a lastResortFamily() and
+ lastResortFont() in cases where a suitable match cannot be found.
+ You can provide substitutions for font family names using
+ insertSubstitution() and insertSubstitutions(). Substitutions can
+ be removed with removeSubstitution(). Use substitute() to retrieve
+ a family's first substitute, or the family name itself if it has
+ no substitutes. Use substitutes() to retrieve a list of a family's
+ substitutes (which may be empty).
+
+ Every QFont has a key() which you can use, for example, as the key
+ in a cache or dictionary. If you want to store a user's font
+ preferences you could use QSettings, writing the font information
+ with toString() and reading it back with fromString(). The
+ operator<<() and operator>>() functions are also available, but
+ they work on a data stream.
+
+ It is possible to set the height of characters shown on the screen
+ to a specified number of pixels with setPixelSize(); however using
+ setPointSize() has a similar effect and provides device
+ independence.
+
+ In X11 you can set a font using its system
+ specific name with setRawName().
+
+ Loading fonts can be expensive, especially on X11. QFont contains
+ extensive optimizations to make the copying of QFont objects fast,
+ and to cache the results of the slow window system functions it
+ depends upon.
+
+ \target fontmatching
+ The font matching algorithm works as follows:
+ \list 1
+ \o The specified font family is searched for.
+ \o If not found, the styleHint() is used to select a replacement
+ family.
+ \o Each replacement font family is searched for.
+ \o If none of these are found or there was no styleHint(), "helvetica"
+ will be searched for.
+ \o If "helvetica" isn't found Qt will try the lastResortFamily().
+ \o If the lastResortFamily() isn't found Qt will try the
+ lastResortFont() which will always return a name of some kind.
+ \endlist
+
+ Note that the actual font matching algorithm varies from platform to platform.
+
+ In Windows a request for the "Courier" font is automatically changed to
+ "Courier New", an improved version of Courier that allows for smooth scaling.
+ The older "Courier" bitmap font can be selected by setting the PreferBitmap
+ style strategy (see setStyleStrategy()).
+
+ Once a font is found, the remaining attributes are matched in order of
+ priority:
+ \list 1
+ \o fixedPitch()
+ \o pointSize() (see below)
+ \o weight()
+ \o style()
+ \endlist
+
+ If you have a font which matches on family, even if none of the
+ other attributes match, this font will be chosen in preference to
+ a font which doesn't match on family but which does match on the
+ other attributes. This is because font family is the dominant
+ search criteria.
+
+ The point size is defined to match if it is within 20% of the
+ requested point size. When several fonts match and are only
+ distinguished by point size, the font with the closest point size
+ to the one requested will be chosen.
+
+ The actual family, font size, weight and other font attributes
+ used for drawing text will depend on what's available for the
+ chosen family under the window system. A QFontInfo object can be
+ used to determine the actual values used for drawing the text.
+
+ Examples:
+
+ \snippet doc/src/snippets/code/src_gui_text_qfont.cpp 1
+ If you had both an Adobe and a Cronyx Helvetica, you might get
+ either.
+
+ \snippet doc/src/snippets/code/src_gui_text_qfont.cpp 2
+
+ You can specify the foundry you want in the family name. The font f
+ in the above example will be set to "Helvetica
+ [Cronyx]".
+
+ To determine the attributes of the font actually used in the window
+ system, use a QFontInfo object, e.g.
+
+ \snippet doc/src/snippets/code/src_gui_text_qfont.cpp 3
+
+ To find out font metrics use a QFontMetrics object, e.g.
+
+ \snippet doc/src/snippets/code/src_gui_text_qfont.cpp 4
+
+ For more general information on fonts, see the
+ \link http://nwalsh.com/comp.fonts/FAQ/ comp.fonts FAQ.\endlink
+ Information on encodings can be found from
+ \link http://czyborra.com/ Roman Czyborra's\endlink page.
+
+ \sa QFontComboBox, QFontMetrics, QFontInfo, QFontDatabase, {Character Map Example}
+*/
+
+/*!
+ \internal
+ \enum QFont::ResolveProperties
+
+ This enum describes the properties of a QFont that can be set on a font
+ individually and then considered resolved.
+
+ \value FamilyResolved
+ \value SizeResolved
+ \value StyleHintResolved
+ \value StyleStrategyResolved
+ \value WeightResolved
+ \value StyleResolved
+ \value UnderlineResolved
+ \value OverlineResolved
+ \value StrikeOutResolved
+ \value FixedPitchResolved
+ \value StretchResolved
+ \value KerningResolved
+ \value CapitalizationResolved
+ \value LetterSpacingResolved
+ \value WordSpacingResolved
+ \value CompletelyResolved
+*/
+
+/*!
+ \enum QFont::Style
+
+ This enum describes the different styles of glyphs that are used to
+ display text.
+
+ \value StyleNormal Normal glyphs used in unstyled text.
+ \value StyleItalic Italic glyphs that are specifically designed for
+ the purpose of representing italicized text.
+ \value StyleOblique Glyphs with an italic appearance that are typically
+ based on the unstyled glyphs, but are not fine-tuned
+ for the purpose of representing italicized text.
+
+ \sa Weight
+*/
+
+/*!
+ \fn Qt::HANDLE QFont::handle() const
+
+ Returns the window system handle to the font, for low-level
+ access. Using this function is \e not portable.
+*/
+
+/*!
+ \fn FT_Face QFont::freetypeFace() const
+
+ Returns the handle to the primary FreeType face of the font. If font merging is not disabled a
+ QFont can contain several physical fonts.
+
+ Returns 0 if the font does not contain a FreeType face.
+
+ \note This function is only available on platforms that provide the FreeType library;
+ i.e., X11 and some Embedded Linux platforms.
+*/
+
+/*!
+ \fn QString QFont::rawName() const
+
+ Returns the name of the font within the underlying window system.
+
+ Only on X11 when Qt was built without FontConfig support the XLFD (X Logical Font Description)
+ is returned; otherwise an empty string.
+
+ Using the return value of this function is usually \e not \e
+ portable.
+
+ \sa setRawName()
+*/
+
+/*!
+ \fn void QFont::setRawName(const QString &name)
+
+ Sets a font by its system specific name. The function is
+ particularly useful under X, where system font settings (for
+ example X resources) are usually available in XLFD (X Logical Font
+ Description) form only. You can pass an XLFD as \a name to this
+ function.
+
+ A font set with setRawName() is still a full-featured QFont. It can
+ be queried (for example with italic()) or modified (for example with
+ setItalic()) and is therefore also suitable for rendering rich text.
+
+ If Qt's internal font database cannot resolve the raw name, the
+ font becomes a raw font with \a name as its family.
+
+ Note that the present implementation does not handle wildcards in
+ XLFDs well, and that font aliases (file \c fonts.alias in the font
+ directory on X11) are not supported.
+
+ \sa rawName(), setRawMode(), setFamily()
+*/
+
+/*!
+ \fn QString QFont::lastResortFamily() const
+
+ Returns the "last resort" font family name.
+
+ The current implementation tries a wide variety of common fonts,
+ returning the first one it finds. Is is possible that no family is
+ found in which case an empty string is returned.
+
+ \sa lastResortFont()
+*/
+
+/*!
+ \fn QString QFont::defaultFamily() const
+
+ Returns the family name that corresponds to the current style
+ hint.
+
+ \sa StyleHint styleHint() setStyleHint()
+*/
+
+/*!
+ \fn QString QFont::lastResortFont() const
+
+ Returns a "last resort" font name for the font matching algorithm.
+ This is used if the last resort family is not available. It will
+ always return a name, if necessary returning something like
+ "fixed" or "system".
+
+ The current implementation tries a wide variety of common fonts,
+ returning the first one it finds. The implementation may change
+ at any time, but this function will always return a string
+ containing something.
+
+ It is theoretically possible that there really isn't a
+ lastResortFont() in which case Qt will abort with an error
+ message. We have not been able to identify a case where this
+ happens. Please \link bughowto.html report it as a bug\endlink if
+ it does, preferably with a list of the fonts you have installed.
+
+ \sa lastResortFamily() rawName()
+*/
+
+/*!
+ Constructs a font from \a font for use on the paint device \a pd.
+*/
+QFont::QFont(const QFont &font, QPaintDevice *pd)
+ : resolve_mask(font.resolve_mask)
+{
+ Q_ASSERT(pd != 0);
+ int dpi = pd->logicalDpiY();
+#ifdef Q_WS_X11
+ const QX11Info *info = qt_x11Info(pd);
+ int screen = info ? info->screen() : 0;
+#else
+ const int screen = 0;
+#endif
+ if (font.d->dpi != dpi || font.d->screen != screen ) {
+ d = new QFontPrivate(*font.d);
+ d->dpi = dpi;
+ d->screen = screen;
+ } else {
+ d = font.d;
+ d->ref.ref();
+ }
+#ifdef Q_WS_WIN
+ if (pd->devType() == QInternal::Printer && pd->getDC())
+ d->hdc = pd->getDC();
+#endif
+}
+
+/*!
+ \internal
+*/
+QFont::QFont(QFontPrivate *data)
+ : resolve_mask(QFont::AllPropertiesResolved)
+{
+ d = data;
+ d->ref.ref();
+}
+
+/*! \internal
+ Detaches the font object from common font data.
+*/
+void QFont::detach()
+{
+ if (d->ref == 1) {
+ if (d->engineData)
+ d->engineData->ref.deref();
+ d->engineData = 0;
+ if (d->scFont && d->scFont != d)
+ d->scFont->ref.deref();
+ d->scFont = 0;
+ return;
+ }
+
+ qAtomicDetach(d);
+}
+
+/*!
+ Constructs a font object that uses the application's default font.
+
+ \sa QApplication::setFont(), QApplication::font()
+*/
+QFont::QFont()
+ :d(QApplication::font().d), resolve_mask(0)
+{
+ d->ref.ref();
+}
+
+/*!
+ Constructs a font object with the specified \a family, \a
+ pointSize, \a weight and \a italic settings.
+
+ If \a pointSize is <= 0, it is set to 12.
+
+ The \a family name may optionally also include a foundry name,
+ e.g. "Helvetica [Cronyx]". If the \a family is
+ available from more than one foundry and the foundry isn't
+ specified, an arbitrary foundry is chosen. If the family isn't
+ available a family will be set using the \l{QFont}{font matching}
+ algorithm.
+
+ \sa Weight, setFamily(), setPointSize(), setWeight(), setItalic(),
+ setStyleHint() QApplication::font()
+*/
+QFont::QFont(const QString &family, int pointSize, int weight, bool italic)
+ :d(new QFontPrivate)
+{
+ resolve_mask = QFont::FamilyResolved;
+
+ if (pointSize <= 0) {
+ pointSize = 12;
+ } else {
+ resolve_mask |= QFont::SizeResolved;
+ }
+
+ if (weight < 0) {
+ weight = Normal;
+ } else {
+ resolve_mask |= QFont::WeightResolved | QFont::StyleResolved;
+ }
+
+ d->request.family = family;
+ d->request.pointSize = qreal(pointSize);
+ d->request.pixelSize = -1;
+ d->request.weight = weight;
+ d->request.style = italic ? QFont::StyleItalic : QFont::StyleNormal;
+}
+
+/*!
+ Constructs a font that is a copy of \a font.
+*/
+QFont::QFont(const QFont &font)
+{
+ d = font.d;
+ d->ref.ref();
+ resolve_mask = font.resolve_mask;
+}
+
+/*!
+ Destroys the font object and frees all allocated resources.
+*/
+QFont::~QFont()
+{
+ if (!d->ref.deref())
+ delete d;
+}
+
+/*!
+ Assigns \a font to this font and returns a reference to it.
+*/
+QFont &QFont::operator=(const QFont &font)
+{
+ qAtomicAssign(d, font.d);
+ resolve_mask = font.resolve_mask;
+ return *this;
+}
+
+/*!
+ Returns the requested font family name, i.e. the name set in the
+ constructor or the last setFont() call.
+
+ \sa setFamily() substitutes() substitute()
+*/
+QString QFont::family() const
+{
+ return d->request.family;
+}
+
+/*!
+ Sets the family name of the font. The name is case insensitive and
+ may include a foundry name.
+
+ The \a family name may optionally also include a foundry name,
+ e.g. "Helvetica [Cronyx]". If the \a family is
+ available from more than one foundry and the foundry isn't
+ specified, an arbitrary foundry is chosen. If the family isn't
+ available a family will be set using the \l{QFont}{font matching}
+ algorithm.
+
+ \sa family(), setStyleHint(), QFontInfo
+*/
+void QFont::setFamily(const QString &family)
+{
+ detach();
+
+ d->request.family = family;
+#if defined(Q_WS_X11)
+ d->request.addStyle.clear();
+#endif // Q_WS_X11
+
+ resolve_mask |= QFont::FamilyResolved;
+}
+
+/*!
+ Returns the point size of the font. Returns -1 if the font size
+ was specified in pixels.
+
+ \sa setPointSize() pointSizeF()
+*/
+int QFont::pointSize() const
+{
+ return qRound(d->request.pointSize);
+}
+
+/*!
+ Sets the point size to \a pointSize. The point size must be
+ greater than zero.
+
+ \sa pointSize() setPointSizeF()
+*/
+void QFont::setPointSize(int pointSize)
+{
+ Q_ASSERT_X (pointSize > 0, "QFont::setPointSize", "point size must be greater than 0");
+
+ detach();
+
+ d->request.pointSize = qreal(pointSize);
+ d->request.pixelSize = -1;
+
+ resolve_mask |= QFont::SizeResolved;
+}
+
+/*!
+ Sets the point size to \a pointSize. The point size must be
+ greater than zero. The requested precision may not be achieved on
+ all platforms.
+
+ \sa pointSizeF() setPointSize() setPixelSize()
+*/
+void QFont::setPointSizeF(qreal pointSize)
+{
+ Q_ASSERT_X(pointSize > 0.0, "QFont::setPointSizeF", "point size must be greater than 0");
+
+ detach();
+
+ d->request.pointSize = pointSize;
+ d->request.pixelSize = -1;
+
+ resolve_mask |= QFont::SizeResolved;
+}
+
+/*!
+ Returns the point size of the font. Returns -1 if the font size was
+ specified in pixels.
+
+ \sa pointSize() setPointSizeF() pixelSize() QFontInfo::pointSize() QFontInfo::pixelSize()
+*/
+qreal QFont::pointSizeF() const
+{
+ return d->request.pointSize;
+}
+
+/*!
+ Sets the font size to \a pixelSize pixels.
+
+ Using this function makes the font device dependent. Use
+ setPointSize() or setPointSizeF() to set the size of the font
+ in a device independent manner.
+
+ \sa pixelSize()
+*/
+void QFont::setPixelSize(int pixelSize)
+{
+ if (pixelSize <= 0) {
+ qWarning("QFont::setPixelSize: Pixel size <= 0 (%d)", pixelSize);
+ return;
+ }
+
+ detach();
+
+ d->request.pixelSize = pixelSize;
+ d->request.pointSize = -1;
+
+ resolve_mask |= QFont::SizeResolved;
+}
+
+/*!
+ Returns the pixel size of the font if it was set with
+ setPixelSize(). Returns -1 if the size was set with setPointSize()
+ or setPointSizeF().
+
+ \sa setPixelSize() pointSize() QFontInfo::pointSize() QFontInfo::pixelSize()
+*/
+int QFont::pixelSize() const
+{
+ return d->request.pixelSize;
+}
+
+#ifdef QT3_SUPPORT
+/*! \obsolete
+
+ Sets the logical pixel height of font characters when shown on
+ the screen to \a pixelSize.
+*/
+void QFont::setPixelSizeFloat(qreal pixelSize)
+{
+ setPixelSize((int)pixelSize);
+}
+#endif
+
+/*!
+ \fn bool QFont::italic() const
+
+ Returns true if the style() of the font is not QFont::StyleNormal
+
+ \sa setItalic() style()
+*/
+
+/*!
+ \fn void QFont::setItalic(bool enable)
+
+ Sets the style() of the font to QFont::StyleItalic if \a enable is true;
+ otherwise the style is set to QFont::StyleNormal.
+
+ \sa italic() QFontInfo
+*/
+
+/*!
+ Returns the style of the font.
+
+ \sa setStyle()
+*/
+QFont::Style QFont::style() const
+{
+ return (QFont::Style)d->request.style;
+}
+
+
+/*!
+ Sets the style of the font to \a style.
+
+ \sa italic(), QFontInfo
+*/
+void QFont::setStyle(Style style)
+{
+ detach();
+
+ d->request.style = style;
+ resolve_mask |= QFont::StyleResolved;
+}
+
+/*!
+ Returns the weight of the font which is one of the enumerated
+ values from \l{QFont::Weight}.
+
+ \sa setWeight(), Weight, QFontInfo
+*/
+int QFont::weight() const
+{
+ return d->request.weight;
+}
+
+/*!
+ \enum QFont::Weight
+
+ Qt uses a weighting scale from 0 to 99 similar to, but not the
+ same as, the scales used in Windows or CSS. A weight of 0 is
+ ultralight, whilst 99 will be an extremely black.
+
+ This enum contains the predefined font weights:
+
+ \value Light 25
+ \value Normal 50
+ \value DemiBold 63
+ \value Bold 75
+ \value Black 87
+*/
+
+/*!
+ Sets the weight the font to \a weight, which should be a value
+ from the \l QFont::Weight enumeration.
+
+ \sa weight(), QFontInfo
+*/
+void QFont::setWeight(int weight)
+{
+ Q_ASSERT_X(weight >= 0 && weight <= 99, "QFont::setWeight", "Weight must be between 0 and 99");
+
+ detach();
+
+ d->request.weight = weight;
+ resolve_mask |= QFont::WeightResolved;
+}
+
+/*!
+ \fn bool QFont::bold() const
+
+ Returns true if weight() is a value greater than \link Weight
+ QFont::Normal \endlink; otherwise returns false.
+
+ \sa weight(), setBold(), QFontInfo::bold()
+*/
+
+/*!
+ \fn void QFont::setBold(bool enable)
+
+ If \a enable is true sets the font's weight to \link Weight
+ QFont::Bold \endlink; otherwise sets the weight to \link Weight
+ QFont::Normal\endlink.
+
+ For finer boldness control use setWeight().
+
+ \sa bold(), setWeight()
+*/
+
+/*!
+ Returns true if underline has been set; otherwise returns false.
+
+ \sa setUnderline()
+*/
+bool QFont::underline() const
+{
+ return d->underline;
+}
+
+/*!
+ If \a enable is true, sets underline on; otherwise sets underline
+ off.
+
+ \sa underline(), QFontInfo
+*/
+void QFont::setUnderline(bool enable)
+{
+ detach();
+
+ d->underline = enable;
+ resolve_mask |= QFont::UnderlineResolved;
+}
+
+/*!
+ Returns true if overline has been set; otherwise returns false.
+
+ \sa setOverline()
+*/
+bool QFont::overline() const
+{
+ return d->overline;
+}
+
+/*!
+ If \a enable is true, sets overline on; otherwise sets overline off.
+
+ \sa overline(), QFontInfo
+*/
+void QFont::setOverline(bool enable)
+{
+ detach();
+
+ d->overline = enable;
+ resolve_mask |= QFont::OverlineResolved;
+}
+
+/*!
+ Returns true if strikeout has been set; otherwise returns false.
+
+ \sa setStrikeOut()
+*/
+bool QFont::strikeOut() const
+{
+ return d->strikeOut;
+}
+
+/*!
+ If \a enable is true, sets strikeout on; otherwise sets strikeout
+ off.
+
+ \sa strikeOut(), QFontInfo
+*/
+void QFont::setStrikeOut(bool enable)
+{
+ detach();
+
+ d->strikeOut = enable;
+ resolve_mask |= QFont::StrikeOutResolved;
+}
+
+/*!
+ Returns true if fixed pitch has been set; otherwise returns false.
+
+ \sa setFixedPitch(), QFontInfo::fixedPitch()
+*/
+bool QFont::fixedPitch() const
+{
+ return d->request.fixedPitch;
+}
+
+/*!
+ If \a enable is true, sets fixed pitch on; otherwise sets fixed
+ pitch off.
+
+ \sa fixedPitch(), QFontInfo
+*/
+void QFont::setFixedPitch(bool enable)
+{
+ detach();
+
+ d->request.fixedPitch = enable;
+ d->request.ignorePitch = false;
+ resolve_mask |= QFont::FixedPitchResolved;
+}
+
+/*!
+ Returns true if kerning should be used when drawing text with this font.
+
+ \sa setKerning()
+*/
+bool QFont::kerning() const
+{
+ return d->kerning;
+}
+
+/*!
+ Enables kerning for this font if \a enable is true; otherwise
+ disables it. By default, kerning is enabled.
+
+ When kerning is enabled, glyph metrics do not add up anymore,
+ even for Latin text. In other words, the assumption that
+ width('a') + width('b') is equal to width("ab") is not
+ neccesairly true.
+
+ \sa kerning(), QFontMetrics
+*/
+void QFont::setKerning(bool enable)
+{
+ detach();
+ d->kerning = enable;
+ resolve_mask |= QFont::KerningResolved;
+}
+
+/*!
+ Returns the StyleStrategy.
+
+ The style strategy affects the \l{QFont}{font matching} algorithm.
+ See \l QFont::StyleStrategy for the list of available strategies.
+
+ \sa setStyleHint() QFont::StyleHint
+*/
+QFont::StyleStrategy QFont::styleStrategy() const
+{
+ return (StyleStrategy) d->request.styleStrategy;
+}
+
+/*!
+ Returns the StyleHint.
+
+ The style hint affects the \l{QFont}{font matching} algorithm.
+ See \l QFont::StyleHint for the list of available hints.
+
+ \sa setStyleHint(), QFont::StyleStrategy QFontInfo::styleHint()
+*/
+QFont::StyleHint QFont::styleHint() const
+{
+ return (StyleHint) d->request.styleHint;
+}
+
+/*!
+ \enum QFont::StyleHint
+
+ Style hints are used by the \l{QFont}{font matching} algorithm to
+ find an appropriate default family if a selected font family is
+ not available.
+
+ \value AnyStyle leaves the font matching algorithm to choose the
+ family. This is the default.
+
+ \value SansSerif the font matcher prefer sans serif fonts.
+ \value Helvetica is a synonym for \c SansSerif.
+
+ \value Serif the font matcher prefers serif fonts.
+ \value Times is a synonym for \c Serif.
+
+ \value TypeWriter the font matcher prefers fixed pitch fonts.
+ \value Courier a synonym for \c TypeWriter.
+
+ \value OldEnglish the font matcher prefers decorative fonts.
+ \value Decorative is a synonym for \c OldEnglish.
+
+ \value System the font matcher prefers system fonts.
+*/
+
+/*!
+ \enum QFont::StyleStrategy
+
+ The style strategy tells the \l{QFont}{font matching} algorithm
+ what type of fonts should be used to find an appropriate default
+ family.
+
+ The following strategies are available:
+
+ \value PreferDefault the default style strategy. It does not prefer
+ any type of font.
+ \value PreferBitmap prefers bitmap fonts (as opposed to outline
+ fonts).
+ \value PreferDevice prefers device fonts.
+ \value PreferOutline prefers outline fonts (as opposed to bitmap fonts).
+ \value ForceOutline forces the use of outline fonts.
+ \value NoAntialias don't antialias the fonts.
+ \value PreferAntialias antialias if possible.
+ \value OpenGLCompatible forces the use of OpenGL compatible
+ fonts.
+ \value NoFontMerging If a font does not contain a character requested
+ to draw then Qt automatically chooses a similar looking for that contains
+ the character. This flag disables this feature.
+
+ Any of these may be OR-ed with one of these flags:
+
+ \value PreferMatch prefer an exact match. The font matcher will try to
+ use the exact font size that has been specified.
+ \value PreferQuality prefer the best quality font. The font matcher
+ will use the nearest standard point size that the font
+ supports.
+*/
+
+/*!
+ Sets the style hint and strategy to \a hint and \a strategy,
+ respectively.
+
+ If these aren't set explicitly the style hint will default to
+ \c AnyStyle and the style strategy to \c PreferDefault.
+
+ Qt does not support style hints on X11 since this information
+ is not provided by the window system.
+
+ \sa StyleHint, styleHint(), StyleStrategy, styleStrategy(), QFontInfo
+*/
+void QFont::setStyleHint(StyleHint hint, StyleStrategy strategy)
+{
+ detach();
+
+ if ((resolve_mask & (QFont::StyleHintResolved | QFont::StyleStrategyResolved)) &&
+ (StyleHint) d->request.styleHint == hint &&
+ (StyleStrategy) d->request.styleStrategy == strategy)
+ return;
+
+ d->request.styleHint = hint;
+ d->request.styleStrategy = strategy;
+ resolve_mask |= QFont::StyleHintResolved;
+ resolve_mask |= QFont::StyleStrategyResolved;
+
+#if defined(Q_WS_X11)
+ d->request.addStyle.clear();
+#endif // Q_WS_X11
+}
+
+/*!
+ Sets the style strategy for the font to \a s.
+
+ \sa QFont::StyleStrategy
+*/
+void QFont::setStyleStrategy(StyleStrategy s)
+{
+ detach();
+
+ if ((resolve_mask & QFont::StyleStrategyResolved) &&
+ s == (StyleStrategy)d->request.styleStrategy)
+ return;
+
+ d->request.styleStrategy = s;
+ resolve_mask |= QFont::StyleStrategyResolved;
+}
+
+
+/*!
+ \enum QFont::Stretch
+
+ Predefined stretch values that follow the CSS naming convention. The higher
+ the value, the more stretched the text is.
+
+ \value UltraCondensed 50
+ \value ExtraCondensed 62
+ \value Condensed 75
+ \value SemiCondensed 87
+ \value Unstretched 100
+ \value SemiExpanded 112
+ \value Expanded 125
+ \value ExtraExpanded 150
+ \value UltraExpanded 200
+
+ \sa setStretch() stretch()
+*/
+
+/*!
+ Returns the stretch factor for the font.
+
+ \sa setStretch()
+ */
+int QFont::stretch() const
+{
+ return d->request.stretch;
+}
+
+/*!
+ Sets the stretch factor for the font.
+
+ The stretch factor changes the width of all characters in the font
+ by \a factor percent. For example, setting \a factor to 150
+ results in all characters in the font being 1.5 times (ie. 150%)
+ wider. The default stretch factor is 100. The minimum stretch
+ factor is 1, and the maximum stretch factor is 4000.
+
+ The stretch factor is only applied to outline fonts. The stretch
+ factor is ignored for bitmap fonts.
+
+ NOTE: QFont cannot stretch XLFD fonts. When loading XLFD fonts on
+ X11, the stretch factor is matched against a predefined set of
+ values for the SETWIDTH_NAME field of the XLFD.
+
+ \sa stretch() QFont::Stretch
+*/
+void QFont::setStretch(int factor)
+{
+ if (factor < 1 || factor > 4000) {
+ qWarning("QFont::setStretch: Parameter '%d' out of range", factor);
+ return;
+ }
+
+ if ((resolve_mask & QFont::StretchResolved) &&
+ d->request.stretch == (uint)factor)
+ return;
+
+ detach();
+
+ d->request.stretch = (uint)factor;
+ resolve_mask |= QFont::StretchResolved;
+}
+
+/*!
+ \enum QFont::SpacingType
+ \since 4.4
+
+ \value PercentageSpacing A value of 100 will keep the spacing unchanged; a value of 200 will enlarge the
+ spacing after a character by the width of the character itself.
+ \value AbsoluteSpacing A positive value increases the letter spacing by the corresponding pixels; a negative
+ value decreases the spacing.
+*/
+
+/*!
+ \since 4.4
+ Returns the letter spacing for the font.
+
+ \sa setLetterSpacing(), letterSpacingType(), setWordSpacing()
+ */
+qreal QFont::letterSpacing() const
+{
+ return d->letterSpacing.toReal();
+}
+
+/*!
+ \since 4.4
+ Sets the letter spacing for the font to \a spacing and the type
+ of spacing to \a type.
+
+ Letter spacing changes the default spacing between individual
+ letters in the font. The spacing between the letters can be
+ made smaller as well as larger.
+
+ \sa letterSpacing(), letterSpacingType(), setWordSpacing()
+*/
+void QFont::setLetterSpacing(SpacingType type, qreal spacing)
+{
+ const QFixed newSpacing = QFixed::fromReal(spacing);
+ const bool absoluteSpacing = type == AbsoluteSpacing;
+ if ((resolve_mask & QFont::LetterSpacingResolved) &&
+ d->letterSpacingIsAbsolute == absoluteSpacing &&
+ d->letterSpacing == newSpacing)
+ return;
+
+ detach();
+
+ d->letterSpacing = newSpacing;
+ d->letterSpacingIsAbsolute = absoluteSpacing;
+ resolve_mask |= QFont::LetterSpacingResolved;
+}
+
+/*!
+ \since 4.4
+ Returns the spacing type used for letter spacing.
+
+ \sa letterSpacing(), setLetterSpacing(), setWordSpacing()
+*/
+QFont::SpacingType QFont::letterSpacingType() const
+{
+ return d->letterSpacingIsAbsolute ? AbsoluteSpacing : PercentageSpacing;
+}
+
+/*!
+ \since 4.4
+ Returns the word spacing for the font.
+
+ \sa setWordSpacing(), setLetterSpacing()
+ */
+qreal QFont::wordSpacing() const
+{
+ return d->wordSpacing.toReal();
+}
+
+/*!
+ \since 4.4
+ Sets the word spacing for the font to \a spacing.
+
+ Word spacing changes the default spacing between individual
+ words. A positive value increases the word spacing
+ by a corresponding amount of pixels, while a negative value
+ decreases the inter-word spacing accordingly.
+
+ Word spacing will not apply to writing systems, where indiviaul
+ words are not separated by white space.
+
+ \sa wordSpacing(), setLetterSpacing()
+*/
+void QFont::setWordSpacing(qreal spacing)
+{
+ const QFixed newSpacing = QFixed::fromReal(spacing);
+ if ((resolve_mask & QFont::WordSpacingResolved) &&
+ d->wordSpacing == newSpacing)
+ return;
+
+ detach();
+
+ d->wordSpacing = newSpacing;
+ resolve_mask |= QFont::WordSpacingResolved;
+}
+
+/*!
+ \enum QFont::Capitalization
+ \since 4.4
+
+ Rendering option for text this font applies to.
+
+
+ \value MixedCase This is the normal text rendering option where no capitalization change is applied.
+ \value AllUppercase This alters the text to be rendered in all uppercase type.
+ \value AllLowercase This alters the text to be rendered in all lowercase type.
+ \value SmallCaps This alters the text to be rendered in small-caps type.
+ \value Capitalize This alters the text to be rendered with the first character of each word as an uppercase character.
+*/
+
+/*!
+ \since 4.4
+ Sets the capitalization of the text in this font to \a caps.
+
+ A font's capitalization makes the text appear in the selected capitalization mode.
+
+ \sa capitalization()
+*/
+void QFont::setCapitalization(Capitalization caps)
+{
+ if ((resolve_mask & QFont::CapitalizationResolved) &&
+ capitalization() == caps)
+ return;
+
+ detach();
+
+ d->capital = caps;
+ resolve_mask |= QFont::CapitalizationResolved;
+}
+
+/*!
+ \since 4.4
+ Returns the current capitalization type of the font.
+
+ \sa setCapitalization()
+*/
+QFont::Capitalization QFont::capitalization() const
+{
+ return static_cast<QFont::Capitalization> (d->capital);
+}
+
+
+/*!
+ If \a enable is true, turns raw mode on; otherwise turns raw mode
+ off. This function only has an effect under X11.
+
+ If raw mode is enabled, Qt will search for an X font with a
+ complete font name matching the family name, ignoring all other
+ values set for the QFont. If the font name matches several fonts,
+ Qt will use the first font returned by X. QFontInfo \e cannot be
+ used to fetch information about a QFont using raw mode (it will
+ return the values set in the QFont for all parameters, including
+ the family name).
+
+ \warning Do not use raw mode unless you really, really need it! In
+ most (if not all) cases, setRawName() is a much better choice.
+
+ \sa rawMode(), setRawName()
+*/
+void QFont::setRawMode(bool enable)
+{
+ detach();
+
+ if ((bool) d->rawMode == enable) return;
+
+ d->rawMode = enable;
+}
+
+/*!
+ Returns true if a window system font exactly matching the settings
+ of this font is available.
+
+ \sa QFontInfo
+*/
+bool QFont::exactMatch() const
+{
+ QFontEngine *engine = d->engineForScript(QUnicodeTables::Common);
+ Q_ASSERT(engine != 0);
+ return (d->rawMode
+ ? engine->type() != QFontEngine::Box
+ : d->request.exactMatch(engine->fontDef));
+}
+
+/*!
+ Returns true if this font is equal to \a f; otherwise returns
+ false.
+
+ Two QFonts are considered equal if their font attributes are
+ equal. If rawMode() is enabled for both fonts, only the family
+ fields are compared.
+
+ \sa operator!=() isCopyOf()
+*/
+bool QFont::operator==(const QFont &f) const
+{
+ return (f.d == d
+ || (f.d->request == d->request
+ && f.d->request.pointSize == d->request.pointSize
+ && f.d->underline == d->underline
+ && f.d->overline == d->overline
+ && f.d->strikeOut == d->strikeOut
+ && f.d->kerning == d->kerning));
+}
+
+
+/*!
+ Provides an arbitrary comparison of this font and font \a f.
+ All that is guaranteed is that the operator returns false if both
+ fonts are equal and that (f1 \< f2) == !(f2 \< f1) if the fonts
+ are not equal.
+
+ This function is useful in some circumstances, for example if you
+ want to use QFont objects as keys in a QMap.
+
+ \sa operator==() operator!=() isCopyOf()
+*/
+bool QFont::operator<(const QFont &f) const
+{
+ if (f.d == d) return false;
+ // the < operator for fontdefs ignores point sizes.
+ QFontDef &r1 = f.d->request;
+ QFontDef &r2 = d->request;
+ if (r1.pointSize != r2.pointSize) return r1.pointSize < r2.pointSize;
+ if (r1.pixelSize != r2.pixelSize) return r1.pixelSize < r2.pixelSize;
+ if (r1.weight != r2.weight) return r1.weight < r2.weight;
+ if (r1.style != r2.style) return r1.style < r2.style;
+ if (r1.stretch != r2.stretch) return r1.stretch < r2.stretch;
+ if (r1.styleHint != r2.styleHint) return r1.styleHint < r2.styleHint;
+ if (r1.styleStrategy != r2.styleStrategy) return r1.styleStrategy < r2.styleStrategy;
+ if (r1.family != r2.family) return r1.family < r2.family;
+#ifdef Q_WS_X11
+ if (r1.addStyle != r2.addStyle) return r1.addStyle < r2.addStyle;
+#endif // Q_WS_X11
+
+ int f1attrs = (f.d->underline << 3) + (f.d->overline << 2) + (f.d->strikeOut<<1) + f.d->kerning;
+ int f2attrs = (d->underline << 3) + (d->overline << 2) + (d->strikeOut<<1) + d->kerning;
+ return f1attrs < f2attrs;
+}
+
+
+/*!
+ Returns true if this font is different from \a f; otherwise
+ returns false.
+
+ Two QFonts are considered to be different if their font attributes
+ are different. If rawMode() is enabled for both fonts, only the
+ family fields are compared.
+
+ \sa operator==()
+*/
+bool QFont::operator!=(const QFont &f) const
+{
+ return !(operator==(f));
+}
+
+/*!
+ Returns the font as a QVariant
+*/
+QFont::operator QVariant() const
+{
+ return QVariant(QVariant::Font, this);
+}
+
+/*!
+ Returns true if this font and \a f are copies of each other, i.e.
+ one of them was created as a copy of the other and neither has
+ been modified since. This is much stricter than equality.
+
+ \sa operator=() operator==()
+*/
+bool QFont::isCopyOf(const QFont & f) const
+{
+ return d == f.d;
+}
+
+/*!
+ Returns true if raw mode is used for font name matching; otherwise
+ returns false.
+
+ \sa setRawMode() rawName()
+*/
+bool QFont::rawMode() const
+{
+ return d->rawMode;
+}
+
+/*!
+ Returns a new QFont that has attributes copied from \a other that
+ have not been previously set on this font.
+*/
+QFont QFont::resolve(const QFont &other) const
+{
+ if (*this == other
+ && (resolve_mask == other.resolve_mask || resolve_mask == 0)
+ && d->dpi == other.d->dpi) {
+ QFont o = other;
+ o.resolve_mask = resolve_mask;
+ return o;
+ }
+
+ QFont font(*this);
+ font.detach();
+ font.d->resolve(resolve_mask, other.d);
+
+ return font;
+}
+
+/*!
+ \fn uint QFont::resolve() const
+ \internal
+*/
+
+/*!
+ \fn void QFont::resolve(uint mask)
+ \internal
+*/
+
+#ifdef QT3_SUPPORT
+
+/*! \obsolete
+
+ Please use QApplication::font() instead.
+*/
+QFont QFont::defaultFont()
+{
+ return QApplication::font();
+}
+
+/*! \obsolete
+
+ Please use QApplication::setFont() instead.
+*/
+void QFont::setDefaultFont(const QFont &f)
+{
+ QApplication::setFont(f);
+}
+
+/*!
+ \fn qreal QFont::pointSizeFloat() const
+ \compat
+
+ Use pointSizeF() instead.
+*/
+
+/*!
+ \fn void QFont::setPointSizeFloat(qreal size)
+ \compat
+
+ Use setPointSizeF() instead.
+*/
+#endif
+
+
+
+
+/*****************************************************************************
+ QFont substitution management
+ *****************************************************************************/
+
+typedef QHash<QString, QStringList> QFontSubst;
+Q_GLOBAL_STATIC(QFontSubst, globalFontSubst)
+
+// create substitution dict
+static void initFontSubst()
+{
+ // default substitutions
+ static const char *initTbl[] = {
+
+#if defined(Q_WS_X11)
+ "arial", "helvetica",
+ "times new roman", "times",
+ "courier new", "courier",
+ "sans serif", "helvetica",
+#elif defined(Q_WS_WIN)
+ "times", "times new roman",
+ "courier", "courier new",
+ "helvetica", "arial",
+ "sans serif", "arial",
+#endif
+
+ 0, 0
+ };
+
+ QFontSubst *fontSubst = globalFontSubst();
+ Q_ASSERT(fontSubst != 0);
+ if (!fontSubst->isEmpty())
+ return;
+#if defined(Q_WS_X11) && !defined(QT_NO_FONTCONFIG)
+ if (X11->has_fontconfig)
+ return;
+#endif
+
+ for (int i=0; initTbl[i] != 0; i += 2) {
+ QStringList &list = (*fontSubst)[QString::fromLatin1(initTbl[i])];
+ list.append(QString::fromLatin1(initTbl[i+1]));
+ }
+}
+
+
+/*!
+ Returns the first family name to be used whenever \a familyName is
+ specified. The lookup is case insensitive.
+
+ If there is no substitution for \a familyName, \a familyName is
+ returned.
+
+ To obtain a list of substitutions use substitutes().
+
+ \sa setFamily() insertSubstitutions() insertSubstitution() removeSubstitution()
+*/
+QString QFont::substitute(const QString &familyName)
+{
+ initFontSubst();
+
+ QFontSubst *fontSubst = globalFontSubst();
+ Q_ASSERT(fontSubst != 0);
+ QFontSubst::ConstIterator it = fontSubst->constFind(familyName.toLower());
+ if (it != fontSubst->constEnd() && !(*it).isEmpty())
+ return (*it).first();
+
+ return familyName;
+}
+
+
+/*!
+ Returns a list of family names to be used whenever \a familyName
+ is specified. The lookup is case insensitive.
+
+ If there is no substitution for \a familyName, an empty list is
+ returned.
+
+ \sa substitute() insertSubstitutions() insertSubstitution() removeSubstitution()
+ */
+QStringList QFont::substitutes(const QString &familyName)
+{
+ initFontSubst();
+
+ QFontSubst *fontSubst = globalFontSubst();
+ Q_ASSERT(fontSubst != 0);
+ return fontSubst->value(familyName.toLower(), QStringList());
+}
+
+
+/*!
+ Inserts \a substituteName into the substitution
+ table for the family \a familyName.
+
+ \sa insertSubstitutions() removeSubstitution() substitutions() substitute() substitutes()
+*/
+void QFont::insertSubstitution(const QString &familyName,
+ const QString &substituteName)
+{
+ initFontSubst();
+
+ QFontSubst *fontSubst = globalFontSubst();
+ Q_ASSERT(fontSubst != 0);
+ QStringList &list = (*fontSubst)[familyName.toLower()];
+ QString s = substituteName.toLower();
+ if (!list.contains(s))
+ list.append(s);
+}
+
+
+/*!
+ Inserts the list of families \a substituteNames into the
+ substitution list for \a familyName.
+
+ \sa insertSubstitution(), removeSubstitution(), substitutions(), substitute()
+*/
+void QFont::insertSubstitutions(const QString &familyName,
+ const QStringList &substituteNames)
+{
+ initFontSubst();
+
+ QFontSubst *fontSubst = globalFontSubst();
+ Q_ASSERT(fontSubst != 0);
+ QStringList &list = (*fontSubst)[familyName.toLower()];
+ QStringList::ConstIterator it = substituteNames.constBegin();
+ while (it != substituteNames.constEnd()) {
+ QString s = (*it).toLower();
+ if (!list.contains(s))
+ list.append(s);
+ it++;
+ }
+}
+
+// ### mark: should be called removeSubstitutions()
+/*!
+ Removes all the substitutions for \a familyName.
+
+ \sa insertSubstitutions(), insertSubstitution(), substitutions(), substitute()
+*/
+void QFont::removeSubstitution(const QString &familyName)
+{ // ### function name should be removeSubstitutions() or
+ // ### removeSubstitutionList()
+ initFontSubst();
+
+ QFontSubst *fontSubst = globalFontSubst();
+ Q_ASSERT(fontSubst != 0);
+ fontSubst->remove(familyName.toLower());
+}
+
+
+/*!
+ Returns a sorted list of substituted family names.
+
+ \sa insertSubstitution(), removeSubstitution(), substitute()
+*/
+QStringList QFont::substitutions()
+{
+ initFontSubst();
+
+ QFontSubst *fontSubst = globalFontSubst();
+ Q_ASSERT(fontSubst != 0);
+ QStringList ret;
+ QFontSubst::ConstIterator it = fontSubst->constBegin();
+
+ while (it != fontSubst->constEnd()) {
+ ret.append(it.key());
+ ++it;
+ }
+
+ ret.sort();
+ return ret;
+}
+
+
+/* \internal
+ Internal function. Converts boolean font settings to an unsigned
+ 8-bit number. Used for serialization etc.
+*/
+static quint8 get_font_bits(int version, const QFontPrivate *f)
+{
+ Q_ASSERT(f != 0);
+ quint8 bits = 0;
+ if (f->request.style)
+ bits |= 0x01;
+ if (f->underline)
+ bits |= 0x02;
+ if (f->overline)
+ bits |= 0x40;
+ if (f->strikeOut)
+ bits |= 0x04;
+ if (f->request.fixedPitch)
+ bits |= 0x08;
+ // if (f.hintSetByUser)
+ // bits |= 0x10;
+ if (f->rawMode)
+ bits |= 0x20;
+ if (version >= QDataStream::Qt_4_0) {
+ if (f->kerning)
+ bits |= 0x10;
+ }
+ if (f->request.style == QFont::StyleOblique)
+ bits |= 0x80;
+ return bits;
+}
+
+static quint8 get_extended_font_bits(const QFontPrivate *f)
+{
+ Q_ASSERT(f != 0);
+ quint8 bits = 0;
+ if (f->request.ignorePitch)
+ bits |= 0x01;
+ if (f->letterSpacingIsAbsolute)
+ bits |= 0x02;
+ return bits;
+}
+
+#ifndef QT_NO_DATASTREAM
+
+/* \internal
+ Internal function. Sets boolean font settings from an unsigned
+ 8-bit number. Used for serialization etc.
+*/
+static void set_font_bits(int version, quint8 bits, QFontPrivate *f)
+{
+ Q_ASSERT(f != 0);
+ f->request.style = (bits & 0x01) != 0 ? QFont::StyleItalic : QFont::StyleNormal;
+ f->underline = (bits & 0x02) != 0;
+ f->overline = (bits & 0x40) != 0;
+ f->strikeOut = (bits & 0x04) != 0;
+ f->request.fixedPitch = (bits & 0x08) != 0;
+ // f->hintSetByUser = (bits & 0x10) != 0;
+ f->rawMode = (bits & 0x20) != 0;
+ if (version >= QDataStream::Qt_4_0)
+ f->kerning = (bits & 0x10) != 0;
+ if ((bits & 0x80) != 0)
+ f->request.style = QFont::StyleOblique;
+}
+
+static void set_extended_font_bits(quint8 bits, QFontPrivate *f)
+{
+ Q_ASSERT(f != 0);
+ f->request.ignorePitch = (bits & 0x01) != 0;
+ f->letterSpacingIsAbsolute = (bits & 0x02) != 0;
+}
+#endif
+
+
+/*!
+ Returns the font's key, a textual representation of a font. It is
+ typically used as the key for a cache or dictionary of fonts.
+
+ \sa QMap
+*/
+QString QFont::key() const
+{
+ return toString();
+}
+
+/*!
+ Returns a description of the font. The description is a
+ comma-separated list of the attributes, perfectly suited for use
+ in QSettings.
+
+ \sa fromString()
+ */
+QString QFont::toString() const
+{
+ const QChar comma(QLatin1Char(','));
+ return family() + comma +
+ QString::number( pointSizeF()) + comma +
+ QString::number( pixelSize()) + comma +
+ QString::number((int) styleHint()) + comma +
+ QString::number( weight()) + comma +
+ QString::number((int) style()) + comma +
+ QString::number((int) underline()) + comma +
+ QString::number((int) strikeOut()) + comma +
+ QString::number((int)fixedPitch()) + comma +
+ QString::number((int) rawMode());
+}
+
+
+/*!
+ Sets this font to match the description \a descrip. The description
+ is a comma-separated list of the font attributes, as returned by
+ toString().
+
+ \sa toString()
+ */
+bool QFont::fromString(const QString &descrip)
+{
+ QStringList l(descrip.split(QLatin1Char(',')));
+
+ int count = l.count();
+ if (!count || (count > 2 && count < 9) || count > 11) {
+ qWarning("QFont::fromString: Invalid description '%s'",
+ descrip.isEmpty() ? "(empty)" : descrip.toLatin1().data());
+ return false;
+ }
+
+ setFamily(l[0]);
+ if (count > 1 && l[1].toDouble() > 0.0)
+ setPointSizeF(l[1].toDouble());
+ if (count == 9) {
+ setStyleHint((StyleHint) l[2].toInt());
+ setWeight(qMax(qMin(99, l[3].toInt()), 0));
+ setItalic(l[4].toInt());
+ setUnderline(l[5].toInt());
+ setStrikeOut(l[6].toInt());
+ setFixedPitch(l[7].toInt());
+ setRawMode(l[8].toInt());
+ } else if (count == 10) {
+ if (l[2].toInt() > 0)
+ setPixelSize(l[2].toInt());
+ setStyleHint((StyleHint) l[3].toInt());
+ setWeight(qMax(qMin(99, l[4].toInt()), 0));
+ setStyle((QFont::Style)l[5].toInt());
+ setUnderline(l[6].toInt());
+ setStrikeOut(l[7].toInt());
+ setFixedPitch(l[8].toInt());
+ setRawMode(l[9].toInt());
+ }
+ if (count >= 9 && !d->request.fixedPitch) // assume 'false' fixedPitch equals default
+ d->request.ignorePitch = true;
+
+ return true;
+}
+
+#if !defined(Q_WS_QWS)
+/*! \internal
+
+ Internal function that dumps font cache statistics.
+*/
+void QFont::cacheStatistics()
+{
+
+
+}
+#endif // !Q_WS_QWS
+
+
+
+/*****************************************************************************
+ QFont stream functions
+ *****************************************************************************/
+#ifndef QT_NO_DATASTREAM
+
+/*!
+ \relates QFont
+
+ Writes the font \a font to the data stream \a s. (toString()
+ writes to a text stream.)
+
+ \sa \link datastreamformat.html Format of the QDataStream operators \endlink
+*/
+QDataStream &operator<<(QDataStream &s, const QFont &font)
+{
+ if (s.version() == 1) {
+ s << font.d->request.family.toLatin1();
+ } else {
+ s << font.d->request.family;
+ }
+
+ if (s.version() >= QDataStream::Qt_4_0) {
+ // 4.0
+ double pointSize = font.d->request.pointSize;
+ qint32 pixelSize = font.d->request.pixelSize;
+ s << pointSize;
+ s << pixelSize;
+ } else if (s.version() <= 3) {
+ qint16 pointSize = (qint16) (font.d->request.pointSize * 10);
+ if (pointSize < 0) {
+#ifdef Q_WS_X11
+ pointSize = (qint16)(font.d->request.pixelSize*720/QX11Info::appDpiY());
+#else
+ pointSize = (qint16)QFontInfo(font).pointSize() * 10;
+#endif
+ }
+ s << pointSize;
+ } else {
+ s << (qint16) (font.d->request.pointSize * 10);
+ s << (qint16) font.d->request.pixelSize;
+ }
+
+ s << (quint8) font.d->request.styleHint;
+ if (s.version() >= QDataStream::Qt_3_1)
+ s << (quint8) font.d->request.styleStrategy;
+ s << (quint8) 0
+ << (quint8) font.d->request.weight
+ << get_font_bits(s.version(), font.d);
+ if (s.version() >= QDataStream::Qt_4_3)
+ s << (quint16)font.d->request.stretch;
+ if (s.version() >= QDataStream::Qt_4_4)
+ s << get_extended_font_bits(font.d);
+ if (s.version() >= QDataStream::Qt_4_5) {
+ s << font.d->letterSpacing.value();
+ s << font.d->wordSpacing.value();
+ }
+ return s;
+}
+
+
+/*!
+ \relates QFont
+
+ Reads the font \a font from the data stream \a s. (fromString()
+ reads from a text stream.)
+
+ \sa \link datastreamformat.html Format of the QDataStream operators \endlink
+*/
+QDataStream &operator>>(QDataStream &s, QFont &font)
+{
+ if (!font.d->ref.deref())
+ delete font.d;
+
+ font.d = new QFontPrivate;
+ font.resolve_mask = QFont::AllPropertiesResolved;
+
+ quint8 styleHint, styleStrategy = QFont::PreferDefault, charSet, weight, bits;
+
+ if (s.version() == 1) {
+ QByteArray fam;
+ s >> fam;
+ font.d->request.family = QString::fromLatin1(fam);
+ } else {
+ s >> font.d->request.family;
+ }
+
+ if (s.version() >= QDataStream::Qt_4_0) {
+ // 4.0
+ double pointSize;
+ qint32 pixelSize;
+ s >> pointSize;
+ s >> pixelSize;
+ font.d->request.pointSize = qreal(pointSize);
+ font.d->request.pixelSize = pixelSize;
+ } else {
+ qint16 pointSize, pixelSize = -1;
+ s >> pointSize;
+ if (s.version() >= 4)
+ s >> pixelSize;
+ font.d->request.pointSize = qreal(pointSize / 10.);
+ font.d->request.pixelSize = pixelSize;
+ }
+ s >> styleHint;
+ if (s.version() >= QDataStream::Qt_3_1)
+ s >> styleStrategy;
+
+ s >> charSet;
+ s >> weight;
+ s >> bits;
+
+ font.d->request.styleHint = styleHint;
+ font.d->request.styleStrategy = styleStrategy;
+ font.d->request.weight = weight;
+
+ set_font_bits(s.version(), bits, font.d);
+
+ if (s.version() >= QDataStream::Qt_4_3) {
+ quint16 stretch;
+ s >> stretch;
+ font.d->request.stretch = stretch;
+ }
+
+ if (s.version() >= QDataStream::Qt_4_4) {
+ quint8 extendedBits;
+ s >> extendedBits;
+ set_extended_font_bits(extendedBits, font.d);
+ }
+ if (s.version() >= QDataStream::Qt_4_5) {
+ int value;
+ s >> value;
+ font.d->letterSpacing.setValue(value);
+ s >> value;
+ font.d->wordSpacing.setValue(value);
+ }
+
+ return s;
+}
+
+#endif // QT_NO_DATASTREAM
+
+
+/*****************************************************************************
+ QFontInfo member functions
+ *****************************************************************************/
+
+/*!
+ \class QFontInfo
+ \reentrant
+
+ \brief The QFontInfo class provides general information about fonts.
+
+ \ingroup multimedia
+ \ingroup shared
+ \ingroup text
+
+ The QFontInfo class provides the same access functions as QFont,
+ e.g. family(), pointSize(), italic(), weight(), fixedPitch(),
+ styleHint() etc. But whilst the QFont access functions return the
+ values that were set, a QFontInfo object returns the values that
+ apply to the font that will actually be used to draw the text.
+
+ For example, when the program asks for a 25pt Courier font on a
+ machine that has a non-scalable 24pt Courier font, QFont will
+ (normally) use the 24pt Courier for rendering. In this case,
+ QFont::pointSize() returns 25 and QFontInfo::pointSize() returns
+ 24.
+
+ There are three ways to create a QFontInfo object.
+ \list 1
+ \o Calling the QFontInfo constructor with a QFont creates a font
+ info object for a screen-compatible font, i.e. the font cannot be
+ a printer font. If the font is changed later, the font
+ info object is \e not updated.
+
+ (Note: If you use a printer font the values returned may be
+ inaccurate. Printer fonts are not always accessible so the nearest
+ screen font is used if a printer font is supplied.)
+
+ \o QWidget::fontInfo() returns the font info for a widget's font.
+ This is equivalent to calling QFontInfo(widget->font()). If the
+ widget's font is changed later, the font info object is \e not
+ updated.
+
+ \o QPainter::fontInfo() returns the font info for a painter's
+ current font. If the painter's font is changed later, the font
+ info object is \e not updated.
+ \endlist
+
+ \sa QFont QFontMetrics QFontDatabase
+*/
+
+/*!
+ Constructs a font info object for \a font.
+
+ The font must be screen-compatible, i.e. a font you use when
+ drawing text in \link QWidget widgets\endlink or \link QPixmap
+ pixmaps\endlink, not QPicture or QPrinter.
+
+ The font info object holds the information for the font that is
+ passed in the constructor at the time it is created, and is not
+ updated if the font's attributes are changed later.
+
+ Use QPainter::fontInfo() to get the font info when painting.
+ This will give correct results also when painting on paint device
+ that is not screen-compatible.
+*/
+QFontInfo::QFontInfo(const QFont &font)
+ : d(font.d)
+{ d->ref.ref(); }
+
+/*!
+ Constructs a copy of \a fi.
+*/
+QFontInfo::QFontInfo(const QFontInfo &fi)
+ : d(fi.d)
+{ d->ref.ref(); }
+
+/*!
+ Destroys the font info object.
+*/
+QFontInfo::~QFontInfo()
+{
+ if (!d->ref.deref())
+ delete d;
+}
+
+/*!
+ Assigns the font info in \a fi.
+*/
+QFontInfo &QFontInfo::operator=(const QFontInfo &fi)
+{
+ qAtomicAssign(d, fi.d);
+ return *this;
+}
+
+/*!
+ Returns the family name of the matched window system font.
+
+ \sa QFont::family()
+*/
+QString QFontInfo::family() const
+{
+ QFontEngine *engine = d->engineForScript(QUnicodeTables::Common);
+ Q_ASSERT(engine != 0);
+ return engine->fontDef.family;
+}
+
+/*!
+ Returns the point size of the matched window system font.
+
+ \sa pointSizeF() QFont::pointSize()
+*/
+int QFontInfo::pointSize() const
+{
+ QFontEngine *engine = d->engineForScript(QUnicodeTables::Common);
+ Q_ASSERT(engine != 0);
+ return qRound(engine->fontDef.pointSize);
+}
+
+/*!
+ Returns the point size of the matched window system font.
+
+ \sa QFont::pointSizeF()
+*/
+qreal QFontInfo::pointSizeF() const
+{
+ QFontEngine *engine = d->engineForScript(QUnicodeTables::Common);
+ Q_ASSERT(engine != 0);
+ return engine->fontDef.pointSize;
+}
+
+/*!
+ Returns the pixel size of the matched window system font.
+
+ \sa QFont::pointSize()
+*/
+int QFontInfo::pixelSize() const
+{
+ QFontEngine *engine = d->engineForScript(QUnicodeTables::Common);
+ Q_ASSERT(engine != 0);
+ return engine->fontDef.pixelSize;
+}
+
+/*!
+ Returns the italic value of the matched window system font.
+
+ \sa QFont::italic()
+*/
+bool QFontInfo::italic() const
+{
+ QFontEngine *engine = d->engineForScript(QUnicodeTables::Common);
+ Q_ASSERT(engine != 0);
+ return engine->fontDef.style != QFont::StyleNormal;
+}
+
+/*!
+ Returns the style value of the matched window system font.
+
+ \sa QFont::style()
+*/
+QFont::Style QFontInfo::style() const
+{
+ QFontEngine *engine = d->engineForScript(QUnicodeTables::Common);
+ Q_ASSERT(engine != 0);
+ return (QFont::Style)engine->fontDef.style;
+}
+
+/*!
+ Returns the weight of the matched window system font.
+
+ \sa QFont::weight(), bold()
+*/
+int QFontInfo::weight() const
+{
+ QFontEngine *engine = d->engineForScript(QUnicodeTables::Common);
+ Q_ASSERT(engine != 0);
+ return engine->fontDef.weight;
+
+}
+
+/*!
+ \fn bool QFontInfo::bold() const
+
+ Returns true if weight() would return a value greater than
+ QFont::Normal; otherwise returns false.
+
+ \sa weight(), QFont::bold()
+*/
+
+/*!
+ Returns the underline value of the matched window system font.
+
+ \sa QFont::underline()
+
+ \internal
+
+ Here we read the underline flag directly from the QFont.
+ This is OK for X11 and for Windows because we always get what we want.
+*/
+bool QFontInfo::underline() const
+{
+ return d->underline;
+}
+
+/*!
+ Returns the overline value of the matched window system font.
+
+ \sa QFont::overline()
+
+ \internal
+
+ Here we read the overline flag directly from the QFont.
+ This is OK for X11 and for Windows because we always get what we want.
+*/
+bool QFontInfo::overline() const
+{
+ return d->overline;
+}
+
+/*!
+ Returns the strikeout value of the matched window system font.
+
+ \sa QFont::strikeOut()
+
+ \internal Here we read the strikeOut flag directly from the QFont.
+ This is OK for X11 and for Windows because we always get what we want.
+*/
+bool QFontInfo::strikeOut() const
+{
+ return d->strikeOut;
+}
+
+/*!
+ Returns the fixed pitch value of the matched window system font.
+
+ \sa QFont::fixedPitch()
+*/
+bool QFontInfo::fixedPitch() const
+{
+ QFontEngine *engine = d->engineForScript(QUnicodeTables::Common);
+ Q_ASSERT(engine != 0);
+#ifdef Q_OS_MAC
+ if (!engine->fontDef.fixedPitchComputed) {
+ QChar ch[2] = { QLatin1Char('i'), QLatin1Char('m') };
+ QGlyphLayoutArray<2> g;
+ int l = 2;
+ engine->stringToCMap(ch, 2, &g, &l, 0);
+ engine->fontDef.fixedPitch = g.advances_x[0] == g.advances_x[1];
+ engine->fontDef.fixedPitchComputed = true;
+ }
+#endif
+ return engine->fontDef.fixedPitch;
+}
+
+/*!
+ Returns the style of the matched window system font.
+
+ Currently only returns the style hint set in QFont.
+
+ \sa QFont::styleHint() QFont::StyleHint
+*/
+QFont::StyleHint QFontInfo::styleHint() const
+{
+ QFontEngine *engine = d->engineForScript(QUnicodeTables::Common);
+ Q_ASSERT(engine != 0);
+ return (QFont::StyleHint) engine->fontDef.styleHint;
+}
+
+/*!
+ Returns true if the font is a raw mode font; otherwise returns
+ false.
+
+ If it is a raw mode font, all other functions in QFontInfo will
+ return the same values set in the QFont, regardless of the font
+ actually used.
+
+ \sa QFont::rawMode()
+*/
+bool QFontInfo::rawMode() const
+{
+ return d->rawMode;
+}
+
+/*!
+ Returns true if the matched window system font is exactly the same
+ as the one specified by the font; otherwise returns false.
+
+ \sa QFont::exactMatch()
+*/
+bool QFontInfo::exactMatch() const
+{
+ QFontEngine *engine = d->engineForScript(QUnicodeTables::Common);
+ Q_ASSERT(engine != 0);
+ return (d->rawMode
+ ? engine->type() != QFontEngine::Box
+ : d->request.exactMatch(engine->fontDef));
+}
+
+
+
+
+// **********************************************************************
+// QFontCache
+// **********************************************************************
+
+#ifdef QFONTCACHE_DEBUG
+// fast timeouts for debugging
+static const int fast_timeout = 1000; // 1s
+static const int slow_timeout = 5000; // 5s
+#else
+static const int fast_timeout = 10000; // 10s
+static const int slow_timeout = 300000; // 5m
+#endif // QFONTCACHE_DEBUG
+
+const uint QFontCache::min_cost = 4*1024; // 4mb
+
+#ifdef QT_NO_THREAD
+Q_GLOBAL_STATIC(QFontCache, theFontCache)
+
+QFontCache *QFontCache::instance()
+{
+ return theFontCache();
+}
+
+void QFontCache::cleanup()
+{
+}
+#else
+Q_GLOBAL_STATIC(QThreadStorage<QFontCache *>, theFontCache)
+
+QFontCache *QFontCache::instance()
+{
+ QFontCache *&fontCache = theFontCache()->localData();
+ if (!fontCache)
+ fontCache = new QFontCache;
+ return fontCache;
+}
+
+void QFontCache::cleanup()
+{
+ theFontCache()->setLocalData(0);
+}
+#endif // QT_NO_THREAD
+
+QFontCache::QFontCache()
+ : QObject(), total_cost(0), max_cost(min_cost),
+ current_timestamp(0), fast(false), timer_id(-1)
+{
+}
+
+QFontCache::~QFontCache()
+{
+ {
+ EngineDataCache::Iterator it = engineDataCache.begin(),
+ end = engineDataCache.end();
+ while (it != end) {
+ if (it.value()->ref == 0)
+ delete it.value();
+ else
+ FC_DEBUG("QFontCache::~QFontCache: engineData %p still has refcount %d",
+ it.value(), int(it.value()->ref));
+ ++it;
+ }
+ }
+ EngineCache::Iterator it = engineCache.begin(),
+ end = engineCache.end();
+ while (it != end) {
+ if (--it.value().data->cache_count == 0) {
+ if (it.value().data->ref == 0) {
+ FC_DEBUG("QFontCache::~QFontCache: deleting engine %p key=(%d / %g %d %d %d %d)",
+ it.value().data, it.key().script, it.key().def.pointSize,
+ it.key().def.pixelSize, it.key().def.weight, it.key().def.style,
+ it.key().def.fixedPitch);
+
+ delete it.value().data;
+ } else {
+ FC_DEBUG("QFontCache::~QFontCache: engine = %p still has refcount %d",
+ it.value().data, int(it.value().data->ref));
+ }
+ }
+ ++it;
+ }
+}
+
+void QFontCache::clear()
+{
+ {
+ EngineDataCache::Iterator it = engineDataCache.begin(),
+ end = engineDataCache.end();
+ while (it != end) {
+ QFontEngineData *data = it.value();
+#if !defined(Q_WS_MAC)
+ for (int i = 0; i < QUnicodeTables::ScriptCount; ++i) {
+ if (data->engines[i]) {
+ data->engines[i]->ref.deref();
+ data->engines[i] = 0;
+ }
+ }
+#else
+ if (data->engine) {
+ data->engine->ref.deref();
+ data->engine = 0;
+ }
+#endif
+ ++it;
+ }
+ }
+
+ for (EngineCache::Iterator it = engineCache.begin(), end = engineCache.end();
+ it != end; ++it) {
+ if (it->data->ref == 0) {
+ delete it->data;
+ it->data = 0;
+ }
+ }
+
+ for (EngineCache::Iterator it = engineCache.begin(), end = engineCache.end();
+ it != end; ++it) {
+ if (it->data && it->data->ref == 0) {
+ delete it->data;
+ it->data = 0;
+ }
+ }
+
+ engineCache.clear();
+}
+
+#if defined(Q_WS_QWS) && !defined(QT_NO_QWS_QPF2)
+void QFontCache::removeEngineForFont(const QByteArray &_fontName)
+{
+
+ /* This could be optimized but the code becomes much more complex if we want to handle multi
+ * font engines and it is probably not worth it. Therefore we just clear the entire font cache.
+ */
+ Q_UNUSED(_fontName);
+ clear();
+}
+#endif
+
+QFontEngineData *QFontCache::findEngineData(const Key &key) const
+{
+ EngineDataCache::ConstIterator it = engineDataCache.find(key),
+ end = engineDataCache.end();
+ if (it == end) return 0;
+
+ // found
+ return it.value();
+}
+
+void QFontCache::insertEngineData(const Key &key, QFontEngineData *engineData)
+{
+ FC_DEBUG("QFontCache: inserting new engine data %p", engineData);
+
+ engineDataCache.insert(key, engineData);
+ increaseCost(sizeof(QFontEngineData));
+}
+
+QFontEngine *QFontCache::findEngine(const Key &key)
+{
+ EngineCache::Iterator it = engineCache.find(key),
+ end = engineCache.end();
+ if (it == end) return 0;
+
+ // found... update the hitcount and timestamp
+ it.value().hits++;
+ it.value().timestamp = ++current_timestamp;
+
+ FC_DEBUG("QFontCache: found font engine\n"
+ " %p: timestamp %4u hits %3u ref %2d/%2d, type '%s'",
+ it.value().data, it.value().timestamp, it.value().hits,
+ int(it.value().data->ref), it.value().data->cache_count,
+ it.value().data->name());
+
+ return it.value().data;
+}
+
+void QFontCache::insertEngine(const Key &key, QFontEngine *engine)
+{
+ FC_DEBUG("QFontCache: inserting new engine %p", engine);
+
+ Engine data(engine);
+ data.timestamp = ++current_timestamp;
+
+ engineCache.insert(key, data);
+
+ // only increase the cost if this is the first time we insert the engine
+ if (engine->cache_count == 0)
+ increaseCost(engine->cache_cost);
+
+ ++engine->cache_count;
+}
+
+void QFontCache::increaseCost(uint cost)
+{
+ cost = (cost + 512) / 1024; // store cost in kb
+ cost = cost > 0 ? cost : 1;
+ total_cost += cost;
+
+ FC_DEBUG(" COST: increased %u kb, total_cost %u kb, max_cost %u kb",
+ cost, total_cost, max_cost);
+
+ if (total_cost > max_cost) {
+ max_cost = total_cost;
+
+ if (timer_id == -1 || ! fast) {
+ FC_DEBUG(" TIMER: starting fast timer (%d ms)", fast_timeout);
+
+ if (timer_id != -1) killTimer(timer_id);
+ timer_id = startTimer(fast_timeout);
+ fast = true;
+ }
+ }
+}
+
+void QFontCache::decreaseCost(uint cost)
+{
+ cost = (cost + 512) / 1024; // cost is stored in kb
+ cost = cost > 0 ? cost : 1;
+ Q_ASSERT(cost <= total_cost);
+ total_cost -= cost;
+
+ FC_DEBUG(" COST: decreased %u kb, total_cost %u kb, max_cost %u kb",
+ cost, total_cost, max_cost);
+}
+
+#if defined(Q_WS_WIN) || defined (Q_WS_QWS)
+void QFontCache::cleanupPrinterFonts()
+{
+ FC_DEBUG("QFontCache::cleanupPrinterFonts");
+
+ {
+ FC_DEBUG(" CLEAN engine data:");
+
+ // clean out all unused engine data
+ EngineDataCache::Iterator it = engineDataCache.begin(),
+ end = engineDataCache.end();
+ while (it != end) {
+ if (it.key().screen == 0) {
+ ++it;
+ continue;
+ }
+
+ if(it.value()->ref != 0) {
+ for(int i = 0; i < QUnicodeTables::ScriptCount; ++i) {
+ if(it.value()->engines[i]) {
+ it.value()->engines[i]->ref.deref();
+ it.value()->engines[i] = 0;
+ }
+ }
+ ++it;
+ } else {
+
+ EngineDataCache::Iterator rem = it++;
+
+ decreaseCost(sizeof(QFontEngineData));
+
+ FC_DEBUG(" %p", rem.value());
+
+ delete rem.value();
+ engineDataCache.erase(rem);
+ }
+ }
+ }
+
+ EngineCache::Iterator it = engineCache.begin(),
+ end = engineCache.end();
+ while(it != end) {
+ if (it.value().data->ref != 0 || it.key().screen == 0) {
+ ++it;
+ continue;
+ }
+
+ FC_DEBUG(" %p: timestamp %4u hits %2u ref %2d/%2d, type '%s'",
+ it.value().data, it.value().timestamp, it.value().hits,
+ int(it.value().data->ref), it.value().data->cache_count,
+ it.value().data->name());
+
+ if (--it.value().data->cache_count == 0) {
+ FC_DEBUG(" DELETE: last occurrence in cache");
+
+ decreaseCost(it.value().data->cache_cost);
+ delete it.value().data;
+ }
+
+ engineCache.erase(it++);
+ }
+}
+#endif
+
+void QFontCache::timerEvent(QTimerEvent *)
+{
+ FC_DEBUG("QFontCache::timerEvent: performing cache maintenance (timestamp %u)",
+ current_timestamp);
+
+ if (total_cost <= max_cost && max_cost <= min_cost) {
+ FC_DEBUG(" cache redused sufficiently, stopping timer");
+
+ killTimer(timer_id);
+ timer_id = -1;
+ fast = false;
+
+ return;
+ }
+
+ // go through the cache and count up everything in use
+ uint in_use_cost = 0;
+
+ {
+ FC_DEBUG(" SWEEP engine data:");
+
+ // make sure the cost of each engine data is at least 1kb
+ const uint engine_data_cost =
+ sizeof(QFontEngineData) > 1024 ? sizeof(QFontEngineData) : 1024;
+
+ EngineDataCache::ConstIterator it = engineDataCache.constBegin(),
+ end = engineDataCache.constEnd();
+ for (; it != end; ++it) {
+#ifdef QFONTCACHE_DEBUG
+ FC_DEBUG(" %p: ref %2d", it.value(), int(it.value()->ref));
+
+# if defined(Q_WS_X11) || defined(Q_WS_WIN)
+ // print out all engines
+ for (int i = 0; i < QUnicodeTables::ScriptCount; ++i) {
+ if (! it.value()->engines[i])
+ continue;
+ FC_DEBUG(" contains %p", it.value()->engines[i]);
+ }
+# endif // Q_WS_X11 || Q_WS_WIN
+#endif // QFONTCACHE_DEBUG
+
+ if (it.value()->ref != 0)
+ in_use_cost += engine_data_cost;
+ }
+ }
+
+ {
+ FC_DEBUG(" SWEEP engine:");
+
+ EngineCache::ConstIterator it = engineCache.constBegin(),
+ end = engineCache.constEnd();
+ for (; it != end; ++it) {
+ FC_DEBUG(" %p: timestamp %4u hits %2u ref %2d/%2d, cost %u bytes",
+ it.value().data, it.value().timestamp, it.value().hits,
+ int(it.value().data->ref), it.value().data->cache_count,
+ it.value().data->cache_cost);
+
+ if (it.value().data->ref != 0)
+ in_use_cost += it.value().data->cache_cost / it.value().data->cache_count;
+ }
+
+ // attempt to make up for rounding errors
+ in_use_cost += engineCache.size();
+ }
+
+ in_use_cost = (in_use_cost + 512) / 1024; // cost is stored in kb
+
+ /*
+ calculate the new maximum cost for the cache
+
+ NOTE: in_use_cost is *not* correct due to rounding errors in the
+ above algorithm. instead of worrying about getting the
+ calculation correct, we are more interested in speed, and use
+ in_use_cost as a floor for new_max_cost
+ */
+ uint new_max_cost = qMax(qMax(max_cost / 2, in_use_cost), min_cost);
+
+ FC_DEBUG(" after sweep, in use %u kb, total %u kb, max %u kb, new max %u kb",
+ in_use_cost, total_cost, max_cost, new_max_cost);
+
+ if (new_max_cost == max_cost) {
+ if (fast) {
+ FC_DEBUG(" cannot shrink cache, slowing timer");
+
+ killTimer(timer_id);
+ timer_id = startTimer(slow_timeout);
+ fast = false;
+ }
+
+ return;
+ } else if (! fast) {
+ FC_DEBUG(" dropping into passing gear");
+
+ killTimer(timer_id);
+ timer_id = startTimer(fast_timeout);
+ fast = true;
+ }
+
+ max_cost = new_max_cost;
+
+ {
+ FC_DEBUG(" CLEAN engine data:");
+
+ // clean out all unused engine data
+ EngineDataCache::Iterator it = engineDataCache.begin(),
+ end = engineDataCache.end();
+ while (it != end) {
+ if (it.value()->ref != 0) {
+ ++it;
+ continue;
+ }
+
+ EngineDataCache::Iterator rem = it++;
+
+ decreaseCost(sizeof(QFontEngineData));
+
+ FC_DEBUG(" %p", rem.value());
+
+ delete rem.value();
+ engineDataCache.erase(rem);
+ }
+ }
+
+ // clean out the engine cache just enough to get below our new max cost
+ uint current_cost;
+ do {
+ current_cost = total_cost;
+
+ EngineCache::Iterator it = engineCache.begin(),
+ end = engineCache.end();
+ // determine the oldest and least popular of the unused engines
+ uint oldest = ~0u;
+ uint least_popular = ~0u;
+
+ for (; it != end; ++it) {
+ if (it.value().data->ref != 0)
+ continue;
+
+ if (it.value().timestamp < oldest &&
+ it.value().hits <= least_popular) {
+ oldest = it.value().timestamp;
+ least_popular = it.value().hits;
+ }
+ }
+
+ FC_DEBUG(" oldest %u least popular %u", oldest, least_popular);
+
+ for (it = engineCache.begin(); it != end; ++it) {
+ if (it.value().data->ref == 0 &&
+ it.value().timestamp == oldest &&
+ it.value().hits == least_popular)
+ break;
+ }
+
+ if (it != end) {
+ FC_DEBUG(" %p: timestamp %4u hits %2u ref %2d/%2d, type '%s'",
+ it.value().data, it.value().timestamp, it.value().hits,
+ int(it.value().data->ref), it.value().data->cache_count,
+ it.value().data->name());
+
+ if (--it.value().data->cache_count == 0) {
+ FC_DEBUG(" DELETE: last occurrence in cache");
+
+ decreaseCost(it.value().data->cache_cost);
+ delete it.value().data;
+ } else {
+ /*
+ this particular font engine is in the cache multiple
+ times... set current_cost to zero, so that we can
+ keep looping to get rid of all occurrences
+ */
+ current_cost = 0;
+ }
+
+ engineCache.erase(it);
+ }
+ } while (current_cost != total_cost && total_cost > max_cost);
+}
+
+
+#ifndef QT_NO_DEBUG_STREAM
+QDebug operator<<(QDebug stream, const QFont &font)
+{
+ return stream << "QFont(" << font.toString() << ')';
+}
+#endif
+
+QT_END_NAMESPACE
diff --git a/src/gui/text/qfont.h b/src/gui/text/qfont.h
new file mode 100644
index 0000000000..eec83b586b
--- /dev/null
+++ b/src/gui/text/qfont.h
@@ -0,0 +1,354 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QFONT_H
+#define QFONT_H
+
+#include <QtGui/qwindowdefs.h>
+#include <QtCore/qstring.h>
+
+#if defined(Q_WS_X11) || defined(Q_WS_QWS)
+typedef struct FT_FaceRec_* FT_Face;
+#endif
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+class QFontPrivate; /* don't touch */
+class QStringList;
+class QVariant;
+class Q3TextFormatCollection;
+
+class Q_GUI_EXPORT QFont
+{
+ Q_GADGET
+ Q_ENUMS(StyleStrategy)
+public:
+ enum StyleHint {
+ Helvetica, SansSerif = Helvetica,
+ Times, Serif = Times,
+ Courier, TypeWriter = Courier,
+ OldEnglish, Decorative = OldEnglish,
+ System,
+ AnyStyle
+ };
+
+ enum StyleStrategy {
+ PreferDefault = 0x0001,
+ PreferBitmap = 0x0002,
+ PreferDevice = 0x0004,
+ PreferOutline = 0x0008,
+ ForceOutline = 0x0010,
+ PreferMatch = 0x0020,
+ PreferQuality = 0x0040,
+ PreferAntialias = 0x0080,
+ NoAntialias = 0x0100,
+ OpenGLCompatible = 0x0200,
+ NoFontMerging = 0x8000
+ };
+
+ enum Weight {
+ Light = 25,
+ Normal = 50,
+ DemiBold = 63,
+ Bold = 75,
+ Black = 87
+ };
+
+ enum Style {
+ StyleNormal,
+ StyleItalic,
+ StyleOblique
+ };
+
+ enum Stretch {
+ UltraCondensed = 50,
+ ExtraCondensed = 62,
+ Condensed = 75,
+ SemiCondensed = 87,
+ Unstretched = 100,
+ SemiExpanded = 112,
+ Expanded = 125,
+ ExtraExpanded = 150,
+ UltraExpanded = 200
+ };
+
+ enum Capitalization {
+ MixedCase,
+ AllUppercase,
+ AllLowercase,
+ SmallCaps,
+ Capitalize
+ };
+
+ enum SpacingType {
+ PercentageSpacing,
+ AbsoluteSpacing
+ };
+
+ enum ResolveProperties {
+ FamilyResolved = 0x0001,
+ SizeResolved = 0x0002,
+ StyleHintResolved = 0x0004,
+ StyleStrategyResolved = 0x0008,
+ WeightResolved = 0x0010,
+ StyleResolved = 0x0020,
+ UnderlineResolved = 0x0040,
+ OverlineResolved = 0x0080,
+ StrikeOutResolved = 0x0100,
+ FixedPitchResolved = 0x0200,
+ StretchResolved = 0x0400,
+ KerningResolved = 0x0800,
+ CapitalizationResolved = 0x1000,
+ LetterSpacingResolved = 0x2000,
+ WordSpacingResolved = 0x4000,
+ AllPropertiesResolved = 0x7fff
+ };
+
+ QFont();
+ QFont(const QString &family, int pointSize = -1, int weight = -1, bool italic = false);
+ QFont(const QFont &, QPaintDevice *pd);
+ QFont(const QFont &);
+ ~QFont();
+
+ QString family() const;
+ void setFamily(const QString &);
+
+ int pointSize() const;
+ void setPointSize(int);
+ qreal pointSizeF() const;
+ void setPointSizeF(qreal);
+
+ int pixelSize() const;
+ void setPixelSize(int);
+
+ int weight() const;
+ void setWeight(int);
+
+ inline bool bold() const;
+ inline void setBold(bool);
+
+ void setStyle(Style style);
+ Style style() const;
+
+ inline bool italic() const;
+ inline void setItalic(bool b);
+
+ bool underline() const;
+ void setUnderline(bool);
+
+ bool overline() const;
+ void setOverline(bool);
+
+ bool strikeOut() const;
+ void setStrikeOut(bool);
+
+ bool fixedPitch() const;
+ void setFixedPitch(bool);
+
+ bool kerning() const;
+ void setKerning(bool);
+
+ StyleHint styleHint() const;
+ StyleStrategy styleStrategy() const;
+ void setStyleHint(StyleHint, StyleStrategy = PreferDefault);
+ void setStyleStrategy(StyleStrategy s);
+
+ int stretch() const;
+ void setStretch(int);
+
+ qreal letterSpacing() const;
+ SpacingType letterSpacingType() const;
+ void setLetterSpacing(SpacingType type, qreal spacing);
+
+ qreal wordSpacing() const;
+ void setWordSpacing(qreal spacing);
+
+ void setCapitalization(Capitalization);
+ Capitalization capitalization() const;
+
+ // is raw mode still needed?
+ bool rawMode() const;
+ void setRawMode(bool);
+
+ // dupicated from QFontInfo
+ bool exactMatch() const;
+
+ QFont &operator=(const QFont &);
+ bool operator==(const QFont &) const;
+ bool operator!=(const QFont &) const;
+ bool operator<(const QFont &) const;
+ operator QVariant() const;
+ bool isCopyOf(const QFont &) const;
+
+
+#ifdef Q_WS_WIN
+ HFONT handle() const;
+#else // !Q_WS_WIN
+ Qt::HANDLE handle() const;
+#endif // Q_WS_WIN
+#ifdef Q_WS_MAC
+ quint32 macFontID() const;
+#endif
+#if defined(Q_WS_X11) || defined(Q_WS_QWS)
+ FT_Face freetypeFace() const;
+#endif
+
+ // needed for X11
+ void setRawName(const QString &);
+ QString rawName() const;
+
+ QString key() const;
+
+ QString toString() const;
+ bool fromString(const QString &);
+
+ static QString substitute(const QString &);
+ static QStringList substitutes(const QString &);
+ static QStringList substitutions();
+ static void insertSubstitution(const QString&, const QString &);
+ static void insertSubstitutions(const QString&, const QStringList &);
+ static void removeSubstitution(const QString &);
+ static void initialize();
+ static void cleanup();
+#ifndef Q_WS_QWS
+ static void cacheStatistics();
+#endif
+
+ QString defaultFamily() const;
+ QString lastResortFamily() const;
+ QString lastResortFont() const;
+
+ QFont resolve(const QFont &) const;
+ inline uint resolve() const { return resolve_mask; }
+ inline void resolve(uint mask) { resolve_mask = mask; }
+
+#ifdef QT3_SUPPORT
+ static QT3_SUPPORT QFont defaultFont();
+ static QT3_SUPPORT void setDefaultFont(const QFont &);
+ QT3_SUPPORT void setPixelSizeFloat(qreal);
+ QT3_SUPPORT qreal pointSizeFloat() const { return pointSizeF(); }
+ QT3_SUPPORT void setPointSizeFloat(qreal size) { setPointSizeF(size); }
+#endif
+
+private:
+ QFont(QFontPrivate *);
+
+ void detach();
+
+#if defined(Q_WS_MAC)
+ void macSetFont(QPaintDevice *);
+#elif defined(Q_WS_X11)
+ void x11SetScreen(int screen = -1);
+ int x11Screen() const;
+#endif
+
+ friend class QFontPrivate;
+ friend class QFontDialogPrivate;
+ friend class QFontMetrics;
+ friend class QFontMetricsF;
+ friend class QFontInfo;
+ friend class QPainter;
+ friend class QPSPrintEngineFont;
+ friend class QApplication;
+ friend class QWidget;
+ friend class QWidgetPrivate;
+ friend class Q3TextFormatCollection;
+ friend class QTextLayout;
+ friend class QTextEngine;
+ friend class QStackTextEngine;
+ friend class QTextLine;
+ friend struct QScriptLine;
+ friend class QGLContext;
+ friend class QWin32PaintEngine;
+ friend class QAlphaPaintEngine;
+ friend class QPainterPath;
+ friend class QTextItemInt;
+ friend class QPicturePaintEngine;
+
+#ifndef QT_NO_DATASTREAM
+ friend Q_GUI_EXPORT QDataStream &operator<<(QDataStream &, const QFont &);
+ friend Q_GUI_EXPORT QDataStream &operator>>(QDataStream &, QFont &);
+#endif
+
+ QFontPrivate *d;
+ uint resolve_mask;
+};
+
+
+inline bool QFont::bold() const
+{ return weight() > Normal; }
+
+
+inline void QFont::setBold(bool enable)
+{ setWeight(enable ? Bold : Normal); }
+
+inline bool QFont::italic() const
+{
+ return (style() != StyleNormal);
+}
+
+inline void QFont::setItalic(bool b) {
+ setStyle(b ? StyleItalic : StyleNormal);
+}
+
+
+/*****************************************************************************
+ QFont stream functions
+ *****************************************************************************/
+
+#ifndef QT_NO_DATASTREAM
+Q_GUI_EXPORT QDataStream &operator<<(QDataStream &, const QFont &);
+Q_GUI_EXPORT QDataStream &operator>>(QDataStream &, QFont &);
+#endif
+
+#ifndef QT_NO_DEBUG_STREAM
+Q_GUI_EXPORT QDebug operator<<(QDebug, const QFont &);
+#endif
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QFONT_H
diff --git a/src/gui/text/qfont_mac.cpp b/src/gui/text/qfont_mac.cpp
new file mode 100644
index 0000000000..8320f714c8
--- /dev/null
+++ b/src/gui/text/qfont_mac.cpp
@@ -0,0 +1,158 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qfont.h"
+#include "qfont_p.h"
+#include "qfontengine_p.h"
+#include "qfontinfo.h"
+#include "qfontmetrics.h"
+#include "qpaintdevice.h"
+#include "qstring.h"
+#include <private/qt_mac_p.h>
+#include <private/qtextengine_p.h>
+#include <private/qunicodetables_p.h>
+#include <qapplication.h>
+#include "qfontdatabase.h"
+#include <qpainter.h>
+#include "qtextengine_p.h"
+#include <stdlib.h>
+
+QT_BEGIN_NAMESPACE
+
+extern float qt_mac_defaultDpi_x(); //qpaintdevice_mac.cpp
+
+int qt_mac_pixelsize(const QFontDef &def, int dpi)
+{
+ float ret;
+ if(def.pixelSize == -1)
+ ret = def.pointSize * dpi / qt_mac_defaultDpi_x();
+ else
+ ret = def.pixelSize;
+ return qRound(ret);
+}
+int qt_mac_pointsize(const QFontDef &def, int dpi)
+{
+ float ret;
+ if(def.pointSize < 0)
+ ret = def.pixelSize * qt_mac_defaultDpi_x() / float(dpi);
+ else
+ ret = def.pointSize;
+ return qRound(ret);
+}
+
+QString QFont::rawName() const
+{
+ return family();
+}
+
+void QFont::setRawName(const QString &name)
+{
+ setFamily(name);
+}
+
+void QFont::cleanup()
+{
+ QFontCache::cleanup();
+}
+
+/*!
+ Returns an ATSUFontID
+*/
+quint32 QFont::macFontID() const // ### need 64-bit version
+{
+#ifdef QT_MAC_USE_COCOA
+ return 0;
+#elif 1
+ QFontEngine *fe = d->engineForScript(QUnicodeTables::Common);
+ if (fe && fe->type() == QFontEngine::Multi)
+ return static_cast<QFontEngineMacMulti*>(fe)->macFontID();
+#else
+ Str255 name;
+ if(FMGetFontFamilyName((FMFontFamily)((UInt32)handle()), name) == noErr) {
+ short fnum;
+ GetFNum(name, &fnum);
+ return fnum;
+ }
+#endif
+ return 0;
+}
+
+// Returns an ATSUFonFamilyRef
+Qt::HANDLE QFont::handle() const
+{
+#if 0
+ QFontEngine *fe = d->engineForScript(QUnicodeTables::Common);
+ if (fe && fe->type() == QFontEngine::Mac)
+ return (Qt::HANDLE)static_cast<QFontEngineMacMulti*>(fe)->fontFamilyRef();
+#endif
+ return 0;
+}
+
+void QFont::initialize()
+{ }
+
+QString QFont::defaultFamily() const
+{
+ switch(d->request.styleHint) {
+ case QFont::Times:
+ return QString::fromLatin1("Times New Roman");
+ case QFont::Courier:
+ return QString::fromLatin1("Courier New");
+ case QFont::Decorative:
+ return QString::fromLatin1("Bookman Old Style");
+ case QFont::Helvetica:
+ case QFont::System:
+ default:
+ return QString::fromLatin1("Helvetica");
+ }
+}
+
+QString QFont::lastResortFamily() const
+{
+ return QString::fromLatin1("Helvetica");
+}
+
+QString QFont::lastResortFont() const
+{
+ return QString::fromLatin1("Geneva");
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/text/qfont_p.h b/src/gui/text/qfont_p.h
new file mode 100644
index 0000000000..4e8a7b74e3
--- /dev/null
+++ b/src/gui/text/qfont_p.h
@@ -0,0 +1,278 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QFONT_P_H
+#define QFONT_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of internal files. This header file may change from version to version
+// without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "QtGui/qfont.h"
+#include "QtCore/qmap.h"
+#include "QtCore/qobject.h"
+#include <private/qunicodetables_p.h>
+#include <QtGui/qfontdatabase.h>
+#include "private/qfixed_p.h"
+
+QT_BEGIN_NAMESPACE
+
+// forwards
+class QFontCache;
+class QFontEngine;
+
+struct QFontDef
+{
+ inline QFontDef()
+ : pointSize(-1.0), pixelSize(-1),
+ styleStrategy(QFont::PreferDefault), styleHint(QFont::AnyStyle),
+ weight(50), fixedPitch(false), style(QFont::StyleNormal), stretch(100),
+ ignorePitch(true)
+#ifdef Q_WS_MAC
+ ,fixedPitchComputed(false)
+#endif
+ {
+ }
+
+ QString family;
+
+#ifdef Q_WS_X11
+ QString addStyle;
+#endif // Q_WS_X11
+
+ qreal pointSize;
+ int pixelSize;
+
+ uint styleStrategy : 16;
+ uint styleHint : 8;
+
+ uint weight : 7; // 0-99
+ uint fixedPitch : 1;
+ uint style : 2;
+ uint stretch : 12; // 0-400
+
+ uint ignorePitch : 1;
+ uint fixedPitchComputed : 1; // for Mac OS X only
+ int reserved : 16; // for future extensions
+
+ bool exactMatch(const QFontDef &other) const;
+ bool operator==(const QFontDef &other) const
+ {
+ return pixelSize == other.pixelSize
+ && weight == other.weight
+ && style == other.style
+ && stretch == other.stretch
+ && styleHint == other.styleHint
+ && styleStrategy == other.styleStrategy
+ && ignorePitch == other.ignorePitch && fixedPitch == other.fixedPitch
+ && family == other.family
+#ifdef Q_WS_X11
+ && addStyle == other.addStyle
+#endif
+ ;
+ }
+ inline bool operator<(const QFontDef &other) const
+ {
+ if (pixelSize != other.pixelSize) return pixelSize < other.pixelSize;
+ if (weight != other.weight) return weight < other.weight;
+ if (style != other.style) return style < other.style;
+ if (stretch != other.stretch) return stretch < other.stretch;
+ if (styleHint != other.styleHint) return styleHint < other.styleHint;
+ if (styleStrategy != other.styleStrategy) return styleStrategy < other.styleStrategy;
+ if (family != other.family) return family < other.family;
+
+#ifdef Q_WS_X11
+ if (addStyle != other.addStyle) return addStyle < other.addStyle;
+#endif // Q_WS_X11
+
+ if (ignorePitch != other.ignorePitch) return ignorePitch < other.ignorePitch;
+ if (fixedPitch != other.fixedPitch) return fixedPitch < other.fixedPitch;
+ return false;
+ }
+};
+
+class QFontEngineData
+{
+public:
+ QFontEngineData();
+ ~QFontEngineData();
+
+ QAtomicInt ref;
+ QFontCache *fontCache;
+
+#if !defined(Q_WS_MAC)
+ QFontEngine *engines[QUnicodeTables::ScriptCount];
+#else
+ QFontEngine *engine;
+#endif
+};
+
+
+class Q_GUI_EXPORT QFontPrivate
+{
+public:
+#ifdef Q_WS_X11
+ static int defaultEncodingID;
+#endif // Q_WS_X11
+
+ QFontPrivate();
+ QFontPrivate(const QFontPrivate &other);
+ ~QFontPrivate();
+
+ QFontEngine *engineForScript(int script) const;
+ void alterCharForCapitalization(QChar &c) const;
+
+ QAtomicInt ref;
+ QFontDef request;
+ mutable QFontEngineData *engineData;
+ int dpi;
+ int screen;
+
+#ifdef Q_WS_WIN
+ HDC hdc;
+#endif
+
+ uint rawMode : 1;
+ uint underline : 1;
+ uint overline : 1;
+ uint strikeOut : 1;
+ uint kerning : 1;
+ uint capital : 3;
+ bool letterSpacingIsAbsolute : 1;
+
+ QFixed letterSpacing;
+ QFixed wordSpacing;
+
+ mutable QFontPrivate *scFont;
+ QFont smallCapsFont() const { return QFont(smallCapsFontPrivate()); }
+ QFontPrivate *smallCapsFontPrivate() const;
+
+ void resolve(uint mask, const QFontPrivate *other);
+private:
+ QFontPrivate &operator=(const QFontPrivate &) { return *this; }
+};
+
+
+class QFontCache : public QObject
+{
+ Q_OBJECT
+public:
+ // note: these static functions work on a per-thread basis
+ static QFontCache *instance();
+ static void cleanup();
+
+ QFontCache();
+ ~QFontCache();
+
+ void clear();
+#if defined(Q_WS_QWS) && !defined(QT_NO_QWS_QPF2)
+ void removeEngineForFont(const QByteArray &fontName);
+#endif
+ // universal key structure. QFontEngineDatas and QFontEngines are cached using
+ // the same keys
+ struct Key {
+ Key() : script(0), screen(0) { }
+ Key(const QFontDef &d, int c, int s = 0)
+ : def(d), script(c), screen(s) { }
+
+ QFontDef def;
+ int script;
+ int screen;
+
+ inline bool operator<(const Key &other) const
+ {
+ if (script != other.script) return script < other.script;
+ if (screen != other.screen) return screen < other.screen;
+ return def < other.def;
+ }
+ inline bool operator==(const Key &other) const
+ { return def == other.def && script == other.script && screen == other.screen; }
+ };
+
+ // QFontEngineData cache
+ typedef QMap<Key,QFontEngineData*> EngineDataCache;
+ EngineDataCache engineDataCache;
+
+ QFontEngineData *findEngineData(const Key &key) const;
+ void insertEngineData(const Key &key, QFontEngineData *engineData);
+
+ // QFontEngine cache
+ struct Engine {
+ Engine() : data(0), timestamp(0), hits(0) { }
+ Engine(QFontEngine *d) : data(d), timestamp(0), hits(0) { }
+
+ QFontEngine *data;
+ uint timestamp;
+ uint hits;
+ };
+
+ typedef QMap<Key,Engine> EngineCache;
+ EngineCache engineCache;
+
+ QFontEngine *findEngine(const Key &key);
+ void insertEngine(const Key &key, QFontEngine *engine);
+
+#if defined(Q_WS_WIN) || defined(Q_WS_QWS)
+ void cleanupPrinterFonts();
+#endif
+
+ private:
+ void increaseCost(uint cost);
+ void decreaseCost(uint cost);
+ void timerEvent(QTimerEvent *event);
+
+ static const uint min_cost;
+ uint total_cost, max_cost;
+ uint current_timestamp;
+ bool fast;
+ int timer_id;
+};
+
+QT_END_NAMESPACE
+
+#endif // QFONT_P_H
diff --git a/src/gui/text/qfont_qws.cpp b/src/gui/text/qfont_qws.cpp
new file mode 100644
index 0000000000..f07341d0c0
--- /dev/null
+++ b/src/gui/text/qfont_qws.cpp
@@ -0,0 +1,134 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qwidget.h"
+#include "qpainter.h"
+#include "qfont_p.h"
+#include <private/qunicodetables_p.h>
+#include "qfontdatabase.h"
+#include "qtextcodec.h"
+#include "qapplication.h"
+#include "qfile.h"
+#include "qtextstream.h"
+#include "qmap.h"
+//#include "qmemorymanager_qws.h"
+#include "qtextengine_p.h"
+#include "qfontengine_p.h"
+#if !defined(QT_NO_FREETYPE)
+#include "qfontengine_ft_p.h"
+#endif
+
+QT_BEGIN_NAMESPACE
+
+void QFont::initialize()
+{ }
+
+void QFont::cleanup()
+{
+ QFontCache::cleanup();
+}
+
+
+/*****************************************************************************
+ QFont member functions
+ *****************************************************************************/
+
+Qt::HANDLE QFont::handle() const
+{
+#ifndef QT_NO_FREETYPE
+ return freetypeFace();
+#endif
+ return 0;
+}
+
+FT_Face QFont::freetypeFace() const
+{
+#ifndef QT_NO_FREETYPE
+ QFontEngine *engine = d->engineForScript(QUnicodeTables::Common);
+ if (engine->type() == QFontEngine::Multi)
+ engine = static_cast<QFontEngineMulti *>(engine)->engine(0);
+ if (engine->type() == QFontEngine::Freetype) {
+ const QFontEngineFT *ft = static_cast<const QFontEngineFT *>(engine);
+ return ft->non_locked_face();
+ }
+#endif
+ return 0;
+}
+
+QString QFont::rawName() const
+{
+ return QLatin1String("unknown");
+}
+
+void QFont::setRawName(const QString &)
+{
+}
+
+QString QFont::defaultFamily() const
+{
+ switch(d->request.styleHint) {
+ case QFont::Times:
+ return QString::fromLatin1("times");
+ case QFont::Courier:
+ return QString::fromLatin1("courier");
+ case QFont::Decorative:
+ return QString::fromLatin1("old english");
+ case QFont::Helvetica:
+ case QFont::System:
+ default:
+ return QString::fromLatin1("helvetica");
+ }
+}
+
+QString QFont::lastResortFamily() const
+{
+ return QString::fromLatin1("helvetica");
+}
+
+QString QFont::lastResortFont() const
+{
+ qFatal("QFont::lastResortFont: Cannot find any reasonable font");
+ // Shut compiler up
+ return QString();
+}
+
+
+QT_END_NAMESPACE
diff --git a/src/gui/text/qfont_win.cpp b/src/gui/text/qfont_win.cpp
new file mode 100644
index 0000000000..5db5a6847d
--- /dev/null
+++ b/src/gui/text/qfont_win.cpp
@@ -0,0 +1,178 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+// the miscrosoft platform SDK says that the Unicode versions of
+// TextOut and GetTextExtentsPoint32 are supported on all platforms, so we use them
+// exclusively to simplify code, save a lot of conversions into the local encoding
+// and have generally better unicode support :)
+
+#include "qfont.h"
+#include "qfont_p.h"
+#include "qfontengine_p.h"
+#include "qtextengine_p.h"
+#include "qfontmetrics.h"
+#include "qfontinfo.h"
+
+#include "qwidget.h"
+#include "qpainter.h"
+#include <limits.h>
+#include "qt_windows.h"
+#include <private/qapplication_p.h>
+#include "qapplication.h"
+#include <private/qunicodetables_p.h>
+#include <qfontdatabase.h>
+
+QT_BEGIN_NAMESPACE
+
+extern HDC shared_dc(); // common dc for all fonts
+
+// ### maybe move to qapplication_win
+QFont qt_LOGFONTtoQFont(LOGFONT& lf, bool /*scale*/)
+{
+ QString family = QT_WA_INLINE(QString::fromUtf16((ushort*)lf.lfFaceName),
+ QString::fromLocal8Bit((char*)lf.lfFaceName));
+ QFont qf(family);
+ qf.setItalic(lf.lfItalic);
+ if (lf.lfWeight != FW_DONTCARE) {
+ int weight;
+ if (lf.lfWeight < 400)
+ weight = QFont::Light;
+ else if (lf.lfWeight < 600)
+ weight = QFont::Normal;
+ else if (lf.lfWeight < 700)
+ weight = QFont::DemiBold;
+ else if (lf.lfWeight < 800)
+ weight = QFont::Bold;
+ else
+ weight = QFont::Black;
+ qf.setWeight(weight);
+ }
+ int lfh = qAbs(lf.lfHeight);
+ qf.setPointSizeF(lfh * 72.0 / GetDeviceCaps(shared_dc(),LOGPIXELSY));
+ qf.setUnderline(false);
+ qf.setOverline(false);
+ qf.setStrikeOut(false);
+ return qf;
+}
+
+
+static inline float pixelSize(const QFontDef &request, int dpi)
+{
+ float pSize;
+ if (request.pointSize != -1)
+ pSize = request.pointSize * dpi/ 72.;
+ else
+ pSize = request.pixelSize;
+ return pSize;
+}
+
+static inline float pointSize(const QFontDef &fd, int dpi)
+{
+ float pSize;
+ if (fd.pointSize < 0)
+ pSize = fd.pixelSize * 72. / ((float)dpi);
+ else
+ pSize = fd.pointSize;
+ return pSize;
+}
+
+/*****************************************************************************
+ QFont member functions
+ *****************************************************************************/
+
+void QFont::initialize()
+{
+}
+
+void QFont::cleanup()
+{
+ QFontCache::cleanup();
+}
+
+HFONT QFont::handle() const
+{
+ QFontEngine *engine = d->engineForScript(QUnicodeTables::Common);
+ Q_ASSERT(engine != 0);
+ if (engine->type() == QFontEngine::Multi)
+ engine = static_cast<QFontEngineMulti *>(engine)->engine(0);
+ if (engine->type() == QFontEngine::Win)
+ return static_cast<QFontEngineWin *>(engine)->hfont;
+ return 0;
+}
+
+QString QFont::rawName() const
+{
+ return family();
+}
+
+void QFont::setRawName(const QString &name)
+{
+ setFamily(name);
+}
+
+QString QFont::defaultFamily() const
+{
+ switch(d->request.styleHint) {
+ case QFont::Times:
+ return QString::fromLatin1("Times New Roman");
+ case QFont::Courier:
+ return QString::fromLatin1("Courier New");
+ case QFont::Decorative:
+ return QString::fromLatin1("Bookman Old Style");
+ case QFont::Helvetica:
+ return QString::fromLatin1("Arial");
+ case QFont::System:
+ default:
+ return QString::fromLatin1("MS Sans Serif");
+ }
+}
+
+QString QFont::lastResortFamily() const
+{
+ return QString::fromLatin1("helvetica");
+}
+
+QString QFont::lastResortFont() const
+{
+ return QString::fromLatin1("arial");
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/text/qfont_x11.cpp b/src/gui/text/qfont_x11.cpp
new file mode 100644
index 0000000000..710792c9de
--- /dev/null
+++ b/src/gui/text/qfont_x11.cpp
@@ -0,0 +1,370 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#define QT_FATAL_ASSERT
+
+#include "qplatformdefs.h"
+
+#include "qfont.h"
+#include "qapplication.h"
+#include "qfontinfo.h"
+#include "qfontdatabase.h"
+#include "qfontmetrics.h"
+#include "qpaintdevice.h"
+#include "qtextcodec.h"
+#include "qiodevice.h"
+#include "qhash.h"
+
+#include <private/qunicodetables_p.h>
+#include "qfont_p.h"
+#include "qfontengine_p.h"
+#include "qfontengine_x11_p.h"
+#include "qtextengine_p.h"
+
+#include <private/qt_x11_p.h>
+#include "qx11info_x11.h"
+
+#include <time.h>
+#include <stdlib.h>
+#include <ctype.h>
+
+#define QFONTLOADER_DEBUG
+#define QFONTLOADER_DEBUG_VERBOSE
+
+QT_BEGIN_NAMESPACE
+
+double qt_pixelSize(double pointSize, int dpi)
+{
+ if (pointSize < 0)
+ return -1.;
+ if (dpi == 75) // the stupid 75 dpi setting on X11
+ dpi = 72;
+ return (pointSize * dpi) /72.;
+}
+
+double qt_pointSize(double pixelSize, int dpi)
+{
+ if (pixelSize < 0)
+ return -1.;
+ if (dpi == 75) // the stupid 75 dpi setting on X11
+ dpi = 72;
+ return pixelSize * 72. / ((double) dpi);
+}
+
+/*
+ Removes wildcards from an XLFD.
+
+ Returns \a xlfd with all wildcards removed if a match for \a xlfd is
+ found, otherwise it returns \a xlfd.
+*/
+static QByteArray qt_fixXLFD(const QByteArray &xlfd)
+{
+ QByteArray ret = xlfd;
+ int count = 0;
+ char **fontNames =
+ XListFonts(QX11Info::display(), xlfd, 32768, &count);
+ if (count > 0)
+ ret = fontNames[0];
+ XFreeFontNames(fontNames);
+ return ret ;
+}
+
+typedef QHash<int, QString> FallBackHash;
+Q_GLOBAL_STATIC(FallBackHash, fallBackHash)
+
+// Returns the user-configured fallback family for the specified script.
+QString qt_fallback_font_family(int script)
+{
+ FallBackHash *hash = fallBackHash();
+ return hash->value(script);
+}
+
+// Sets the fallback family for the specified script.
+Q_GUI_EXPORT void qt_x11_set_fallback_font_family(int script, const QString &family)
+{
+ FallBackHash *hash = fallBackHash();
+ if (!family.isEmpty())
+ hash->insert(script, family);
+ else
+ hash->remove(script);
+}
+
+int QFontPrivate::defaultEncodingID = -1;
+
+/*!
+ Internal function that initializes the font system.
+
+ \internal
+ The font cache and font dict do not alloc the keys. The key is a QString
+ which is shared between QFontPrivate and QXFontName.
+*/
+void QFont::initialize()
+{
+ extern int qt_encoding_id_for_mib(int mib); // from qfontdatabase_x11.cpp
+ QTextCodec *codec = QTextCodec::codecForLocale();
+ // determine the default encoding id using the locale, otherwise
+ // fallback to latin1 (mib == 4)
+ int mib = codec ? codec->mibEnum() : 4;
+
+ // for asian locales, use the mib for the font codec instead of the locale codec
+ switch (mib) {
+ case 38: // eucKR
+ mib = 36;
+ break;
+
+ case 2025: // GB2312
+ mib = 57;
+ break;
+
+ case 113: // GBK
+ mib = -113;
+ break;
+
+ case 114: // GB18030
+ mib = -114;
+ break;
+
+ case 2026: // Big5
+ mib = -2026;
+ break;
+
+ case 2101: // Big5-HKSCS
+ mib = -2101;
+ break;
+
+ case 16: // JIS7
+ mib = 15;
+ break;
+
+ case 17: // SJIS
+ case 18: // eucJP
+ mib = 63;
+ break;
+ }
+
+ // get the default encoding id for the locale encoding...
+ QFontPrivate::defaultEncodingID = qt_encoding_id_for_mib(mib);
+}
+
+/*! \internal
+
+ Internal function that cleans up the font system.
+*/
+void QFont::cleanup()
+{
+ QFontCache::cleanup();
+}
+
+/*!
+ \internal
+ X11 Only: Returns the screen with which this font is associated.
+*/
+int QFont::x11Screen() const
+{
+ return d->screen;
+}
+
+/*! \internal
+ X11 Only: Associate the font with the specified \a screen.
+*/
+void QFont::x11SetScreen(int screen)
+{
+ if (screen < 0) // assume default
+ screen = QX11Info::appScreen();
+
+ if (screen == d->screen)
+ return; // nothing to do
+
+ detach();
+ d->screen = screen;
+}
+
+Qt::HANDLE QFont::handle() const
+{
+ QFontEngine *engine = d->engineForScript(QUnicodeTables::Common);
+ Q_ASSERT(engine != 0);
+ if (engine->type() == QFontEngine::Multi)
+ engine = static_cast<QFontEngineMulti *>(engine)->engine(0);
+ if (engine->type() == QFontEngine::XLFD)
+ return static_cast<QFontEngineXLFD *>(engine)->fontStruct()->fid;
+ return 0;
+}
+
+
+FT_Face QFont::freetypeFace() const
+{
+#ifndef QT_NO_FREETYPE
+ QFontEngine *engine = d->engineForScript(QUnicodeTables::Common);
+ if (engine->type() == QFontEngine::Multi)
+ engine = static_cast<QFontEngineMulti *>(engine)->engine(0);
+#ifndef QT_NO_FONTCONFIG
+ if (engine->type() == QFontEngine::Freetype) {
+ const QFontEngineFT *ft = static_cast<const QFontEngineFT *>(engine);
+ return ft->non_locked_face();
+ } else
+#endif
+ if (engine->type() == QFontEngine::XLFD) {
+ const QFontEngineXLFD *xlfd = static_cast<const QFontEngineXLFD *>(engine);
+ return xlfd->non_locked_face();
+ }
+#endif
+ return 0;
+}
+
+QString QFont::rawName() const
+{
+ QFontEngine *engine = d->engineForScript(QUnicodeTables::Common);
+ Q_ASSERT(engine != 0);
+ if (engine->type() == QFontEngine::Multi)
+ engine = static_cast<QFontEngineMulti *>(engine)->engine(0);
+ if (engine->type() == QFontEngine::XLFD)
+ return QString::fromLatin1(engine->name());
+ return QString();
+}
+struct QtFontDesc;
+
+void QFont::setRawName(const QString &name)
+{
+ detach();
+
+ // from qfontdatabase_x11.cpp
+ extern bool qt_fillFontDef(const QByteArray &xlfd, QFontDef *fd, int dpi, QtFontDesc *desc);
+
+ if (!qt_fillFontDef(qt_fixXLFD(name.toLatin1()), &d->request, d->dpi, 0)) {
+ qWarning("QFont::setRawName: Invalid XLFD: \"%s\"", name.toLatin1().constData());
+
+ setFamily(name);
+ setRawMode(true);
+ } else {
+ resolve_mask = QFont::AllPropertiesResolved;
+ }
+}
+
+QString QFont::lastResortFamily() const
+{
+ return QString::fromLatin1("Helvetica");
+}
+
+QString QFont::defaultFamily() const
+{
+ switch (d->request.styleHint) {
+ case QFont::Times:
+ return QString::fromLatin1("Times");
+
+ case QFont::Courier:
+ return QString::fromLatin1("Courier");
+
+ case QFont::Decorative:
+ return QString::fromLatin1("Old English");
+
+ case QFont::Helvetica:
+ case QFont::System:
+ default:
+ return QString::fromLatin1("Helvetica");
+ }
+}
+
+/*
+ Returns a last resort raw font name for the font matching algorithm.
+ This is used if even the last resort family is not available. It
+ returns \e something, almost no matter what. The current
+ implementation tries a wide variety of common fonts, returning the
+ first one it finds. The implementation may change at any time.
+*/
+static const char * const tryFonts[] = {
+ "-*-helvetica-medium-r-*-*-*-120-*-*-*-*-*-*",
+ "-*-courier-medium-r-*-*-*-120-*-*-*-*-*-*",
+ "-*-times-medium-r-*-*-*-120-*-*-*-*-*-*",
+ "-*-lucida-medium-r-*-*-*-120-*-*-*-*-*-*",
+ "-*-helvetica-*-*-*-*-*-120-*-*-*-*-*-*",
+ "-*-courier-*-*-*-*-*-120-*-*-*-*-*-*",
+ "-*-times-*-*-*-*-*-120-*-*-*-*-*-*",
+ "-*-lucida-*-*-*-*-*-120-*-*-*-*-*-*",
+ "-*-helvetica-*-*-*-*-*-*-*-*-*-*-*-*",
+ "-*-courier-*-*-*-*-*-*-*-*-*-*-*-*",
+ "-*-times-*-*-*-*-*-*-*-*-*-*-*-*",
+ "-*-lucida-*-*-*-*-*-*-*-*-*-*-*-*",
+ "-*-fixed-*-*-*-*-*-*-*-*-*-*-*-*",
+ "6x13",
+ "7x13",
+ "8x13",
+ "9x15",
+ "fixed",
+ 0
+};
+
+// Returns true if the font exists, false otherwise
+static bool fontExists(const QString &fontName)
+{
+ int count;
+ char **fontNames = XListFonts(QX11Info::display(), (char*)fontName.toLatin1().constData(), 32768, &count);
+ if (fontNames) XFreeFontNames(fontNames);
+
+ return count != 0;
+}
+
+QString QFont::lastResortFont() const
+{
+ static QString last;
+
+ // already found
+ if (! last.isNull())
+ return last;
+
+ int i = 0;
+ const char* f;
+
+ while ((f = tryFonts[i])) {
+ last = QString::fromLatin1(f);
+
+ if (fontExists(last))
+ return last;
+
+ i++;
+ }
+
+#if defined(CHECK_NULL)
+ qFatal("QFontPrivate::lastResortFont: Cannot find any reasonable font");
+#endif
+ return last;
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/text/qfontdatabase.cpp b/src/gui/text/qfontdatabase.cpp
new file mode 100644
index 0000000000..c3fc9f55cb
--- /dev/null
+++ b/src/gui/text/qfontdatabase.cpp
@@ -0,0 +1,2435 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <qdir.h>
+#include "qfontdatabase.h"
+#include "qdebug.h"
+#include "qalgorithms.h"
+#include "qapplication.h"
+#include "qvarlengtharray.h" // here or earlier - workaround for VC++6
+#include "qthread.h"
+#include "qmutex.h"
+#include "private/qunicodetables_p.h"
+#include "qfontengine_p.h"
+
+#ifdef Q_WS_X11
+#include <locale.h>
+#endif
+#include <stdlib.h>
+#include <limits.h>
+
+// #define QFONTDATABASE_DEBUG
+#ifdef QFONTDATABASE_DEBUG
+# define FD_DEBUG qDebug
+#else
+# define FD_DEBUG if (false) qDebug
+#endif
+
+// #define FONT_MATCH_DEBUG
+#ifdef FONT_MATCH_DEBUG
+# define FM_DEBUG qDebug
+#else
+# define FM_DEBUG if (false) qDebug
+#endif
+
+#if defined(Q_CC_MSVC) && !defined(Q_CC_MSVC_NET)
+# define for if(0){}else for
+#endif
+
+QT_BEGIN_NAMESPACE
+
+extern int qt_defaultDpiY(); // in qfont.cpp
+
+Q_GUI_EXPORT bool qt_enable_test_font = false;
+
+static int getFontWeight(const QString &weightString)
+{
+ QString s = weightString.toLower();
+
+ // Test in decreasing order of commonness
+ if (s == QLatin1String("medium") ||
+ s == QLatin1String("normal")
+ || s.compare(qApp->translate("QFontDatabase", "Normal"), Qt::CaseInsensitive) == 0)
+ return QFont::Normal;
+ if (s == QLatin1String("bold")
+ || s.compare(qApp->translate("QFontDatabase", "Bold"), Qt::CaseInsensitive) == 0)
+ return QFont::Bold;
+ if (s == QLatin1String("demibold") || s == QLatin1String("demi bold")
+ || s.compare(qApp->translate("QFontDatabase", "Demi Bold"), Qt::CaseInsensitive) == 0)
+ return QFont::DemiBold;
+ if (s == QLatin1String("black")
+ || s.compare(qApp->translate("QFontDatabase", "Black"), Qt::CaseInsensitive) == 0)
+ return QFont::Black;
+ if (s == QLatin1String("light"))
+ return QFont::Light;
+
+ if (s.contains(QLatin1String("bold"))
+ || s.contains(qApp->translate("QFontDatabase", "Bold"), Qt::CaseInsensitive)) {
+ if (s.contains(QLatin1String("demi"))
+ || s.compare(qApp->translate("QFontDatabase", "Demi"), Qt::CaseInsensitive) == 0)
+ return (int) QFont::DemiBold;
+ return (int) QFont::Bold;
+ }
+
+ if (s.contains(QLatin1String("light"))
+ || s.compare(qApp->translate("QFontDatabase", "Light"), Qt::CaseInsensitive) == 0)
+ return (int) QFont::Light;
+
+ if (s.contains(QLatin1String("black"))
+ || s.compare(qApp->translate("QFontDatabase", "Black"), Qt::CaseInsensitive) == 0)
+ return (int) QFont::Black;
+
+ return (int) QFont::Normal;
+}
+
+struct QtFontEncoding
+{
+ signed int encoding : 16;
+
+ uint xpoint : 16;
+ uint xres : 8;
+ uint yres : 8;
+ uint avgwidth : 16;
+ uchar pitch : 8;
+};
+
+struct QtFontSize
+{
+ unsigned short pixelSize;
+
+#ifdef Q_WS_X11
+ int count;
+ QtFontEncoding *encodings;
+ QtFontEncoding *encodingID(int id, uint xpoint = 0, uint xres = 0,
+ uint yres = 0, uint avgwidth = 0, bool add = false);
+#endif // Q_WS_X11
+#ifdef Q_WS_QWS
+ QByteArray fileName;
+ int fileIndex;
+#endif
+};
+
+
+#ifdef Q_WS_X11
+QtFontEncoding *QtFontSize::encodingID(int id, uint xpoint, uint xres,
+ uint yres, uint avgwidth, bool add)
+{
+ // we don't match using the xpoint, xres and yres parameters, only the id
+ for (int i = 0; i < count; ++i) {
+ if (encodings[i].encoding == id)
+ return encodings + i;
+ }
+
+ if (!add) return 0;
+
+ if (!(count % 4))
+ encodings = (QtFontEncoding *)
+ realloc(encodings,
+ (((count+4) >> 2) << 2) * sizeof(QtFontEncoding));
+ encodings[count].encoding = id;
+ encodings[count].xpoint = xpoint;
+ encodings[count].xres = xres;
+ encodings[count].yres = yres;
+ encodings[count].avgwidth = avgwidth;
+ encodings[count].pitch = '*';
+ return encodings + count++;
+}
+#endif // Q_WS_X11
+
+struct QtFontStyle
+{
+ struct Key {
+ Key(const QString &styleString);
+ Key() : style(QFont::StyleNormal),
+ weight(QFont::Normal), stretch(0) { }
+ Key(const Key &o) : style(o.style),
+ weight(o.weight), stretch(o.stretch) { }
+ uint style : 2;
+ signed int weight : 8;
+ signed int stretch : 12;
+
+ bool operator==(const Key & other) {
+ return (style == other.style &&
+ weight == other.weight &&
+ (stretch == 0 || other.stretch == 0 || stretch == other.stretch));
+ }
+ bool operator!=(const Key &other) {
+ return !operator==(other);
+ }
+ bool operator <(const Key &o) {
+ int x = (style << 12) + (weight << 14) + stretch;
+ int y = (o.style << 12) + (o.weight << 14) + o.stretch;
+ return (x < y);
+ }
+ };
+
+ QtFontStyle(const Key &k)
+ : key(k), bitmapScalable(false), smoothScalable(false),
+ count(0), pixelSizes(0)
+ {
+#if defined(Q_WS_X11)
+ weightName = setwidthName = 0;
+#endif // Q_WS_X11
+ }
+
+ ~QtFontStyle() {
+#ifdef Q_WS_X11
+ delete [] weightName;
+ delete [] setwidthName;
+#endif
+#if defined(Q_WS_X11) || defined(Q_WS_QWS)
+ while (count--) {
+#ifdef Q_WS_X11
+ free(pixelSizes[count].encodings);
+#endif
+#ifdef Q_WS_QWS
+ pixelSizes[count].fileName.~QByteArray();
+#endif
+ }
+#endif
+ free(pixelSizes);
+ }
+
+ Key key;
+ bool bitmapScalable : 1;
+ bool smoothScalable : 1;
+ signed int count : 30;
+ QtFontSize *pixelSizes;
+
+#ifdef Q_WS_X11
+ const char *weightName;
+ const char *setwidthName;
+#endif // Q_WS_X11
+#ifdef Q_WS_QWS
+ bool antialiased;
+#endif
+
+ QtFontSize *pixelSize(unsigned short size, bool = false);
+};
+
+QtFontStyle::Key::Key(const QString &styleString)
+ : style(QFont::StyleNormal), weight(QFont::Normal), stretch(0)
+{
+ weight = getFontWeight(styleString);
+
+ if (styleString.contains(QLatin1String("Italic"))
+ || styleString.contains(qApp->translate("QFontDatabase", "Italic")))
+ style = QFont::StyleItalic;
+ else if (styleString.contains(QLatin1String("Oblique"))
+ || styleString.contains(qApp->translate("QFontDatabase", "Oblique")))
+ style = QFont::StyleOblique;
+}
+
+QtFontSize *QtFontStyle::pixelSize(unsigned short size, bool add)
+{
+ for (int i = 0; i < count; i++) {
+ if (pixelSizes[i].pixelSize == size)
+ return pixelSizes + i;
+ }
+ if (!add)
+ return 0;
+
+ if (!(count % 8))
+ pixelSizes = (QtFontSize *)
+ realloc(pixelSizes,
+ (((count+8) >> 3) << 3) * sizeof(QtFontSize));
+ pixelSizes[count].pixelSize = size;
+#ifdef Q_WS_X11
+ pixelSizes[count].count = 0;
+ pixelSizes[count].encodings = 0;
+#endif
+#ifdef Q_WS_QWS
+ new (&pixelSizes[count].fileName) QByteArray;
+ pixelSizes[count].fileIndex = 0;
+#endif
+ return pixelSizes + (count++);
+}
+
+struct QtFontFoundry
+{
+ QtFontFoundry(const QString &n) : name(n), count(0), styles(0) {}
+ ~QtFontFoundry() {
+ while (count--)
+ delete styles[count];
+ free(styles);
+ }
+
+ QString name;
+
+ int count;
+ QtFontStyle **styles;
+ QtFontStyle *style(const QtFontStyle::Key &, bool = false);
+};
+
+QtFontStyle *QtFontFoundry::style(const QtFontStyle::Key &key, bool create)
+{
+ int pos = 0;
+ if (count) {
+ int low = 0;
+ int high = count;
+ pos = count / 2;
+ while (high > low) {
+ if (styles[pos]->key == key)
+ return styles[pos];
+ if (styles[pos]->key < key)
+ low = pos + 1;
+ else
+ high = pos;
+ pos = (high + low) / 2;
+ };
+ pos = low;
+ }
+ if (!create)
+ return 0;
+
+// qDebug("adding key (weight=%d, style=%d, oblique=%d stretch=%d) at %d", key.weight, key.style, key.oblique, key.stretch, pos);
+ if (!(count % 8))
+ styles = (QtFontStyle **)
+ realloc(styles, (((count+8) >> 3) << 3) * sizeof(QtFontStyle *));
+
+ memmove(styles + pos + 1, styles + pos, (count-pos)*sizeof(QtFontStyle *));
+ styles[pos] = new QtFontStyle(key);
+ count++;
+ return styles[pos];
+}
+
+
+struct QtFontFamily
+{
+ enum WritingSystemStatus {
+ Unknown = 0,
+ Supported = 1,
+ UnsupportedFT = 2,
+ UnsupportedXLFD = 4,
+ Unsupported = UnsupportedFT | UnsupportedXLFD
+ };
+
+ QtFontFamily(const QString &n)
+ :
+#ifdef Q_WS_X11
+ fixedPitch(true), ftWritingSystemCheck(false),
+ xlfdLoaded(false), synthetic(false), symbol_checked(false),
+#else
+ fixedPitch(false),
+#endif
+#ifdef Q_WS_WIN
+ writingSystemCheck(false),
+ loaded(false),
+#endif
+#if !defined(QWS) && defined(Q_OS_MAC)
+ fixedPitchComputed(false),
+#endif
+ name(n), count(0), foundries(0)
+#if defined(Q_WS_QWS)
+ , bogusWritingSystems(false)
+#endif
+ {
+ memset(writingSystems, 0, sizeof(writingSystems));
+ }
+ ~QtFontFamily() {
+ while (count--)
+ delete foundries[count];
+ free(foundries);
+ }
+
+ bool fixedPitch : 1;
+#ifdef Q_WS_X11
+ bool ftWritingSystemCheck : 1;
+ bool xlfdLoaded : 1;
+ bool synthetic : 1;
+#endif
+#ifdef Q_WS_WIN
+ bool writingSystemCheck : 1;
+ bool loaded : 1;
+#endif
+#if !defined(QWS) && defined(Q_OS_MAC)
+ bool fixedPitchComputed : 1;
+#endif
+#ifdef Q_WS_X11
+ bool symbol_checked;
+#endif
+
+ QString name;
+#ifdef Q_WS_X11
+ QByteArray fontFilename;
+ int fontFileIndex;
+#endif
+#ifdef Q_WS_WIN
+ QString english_name;
+#endif
+ int count;
+ QtFontFoundry **foundries;
+
+#ifdef Q_WS_QWS
+ bool bogusWritingSystems;
+ QStringList fallbackFamilies;
+#endif
+ unsigned char writingSystems[QFontDatabase::WritingSystemsCount];
+
+ QtFontFoundry *foundry(const QString &f, bool = false);
+};
+
+#if !defined(QWS) && defined(Q_OS_MAC)
+inline static void qt_mac_get_fixed_pitch(QtFontFamily *f)
+{
+ if(f && !f->fixedPitchComputed) {
+ QFontMetrics fm(f->name);
+ f->fixedPitch = fm.width(QLatin1Char('i')) == fm.width(QLatin1Char('m'));
+ f->fixedPitchComputed = true;
+ }
+}
+#endif
+
+
+QtFontFoundry *QtFontFamily::foundry(const QString &f, bool create)
+{
+ if (f.isNull() && count == 1)
+ return foundries[0];
+
+ for (int i = 0; i < count; i++) {
+ if (foundries[i]->name.compare(f, Qt::CaseInsensitive) == 0)
+ return foundries[i];
+ }
+ if (!create)
+ return 0;
+
+ if (!(count % 8))
+ foundries = (QtFontFoundry **)
+ realloc(foundries,
+ (((count+8) >> 3) << 3) * sizeof(QtFontFoundry *));
+
+ foundries[count] = new QtFontFoundry(f);
+ return foundries[count++];
+}
+
+// ### copied to tools/makeqpf/qpf2.cpp
+
+#if (defined(Q_WS_QWS) && !defined(QT_NO_FREETYPE)) || defined(Q_WS_WIN) || defined(Q_WS_MAC)
+// see the Unicode subset bitfields in the MSDN docs
+static int requiredUnicodeBits[QFontDatabase::WritingSystemsCount][2] = {
+ // Any,
+ { 127, 127 },
+ // Latin,
+ { 0, 127 },
+ // Greek,
+ { 7, 127 },
+ // Cyrillic,
+ { 9, 127 },
+ // Armenian,
+ { 10, 127 },
+ // Hebrew,
+ { 11, 127 },
+ // Arabic,
+ { 13, 127 },
+ // Syriac,
+ { 71, 127 },
+ //Thaana,
+ { 72, 127 },
+ //Devanagari,
+ { 15, 127 },
+ //Bengali,
+ { 16, 127 },
+ //Gurmukhi,
+ { 17, 127 },
+ //Gujarati,
+ { 18, 127 },
+ //Oriya,
+ { 19, 127 },
+ //Tamil,
+ { 20, 127 },
+ //Telugu,
+ { 21, 127 },
+ //Kannada,
+ { 22, 127 },
+ //Malayalam,
+ { 23, 127 },
+ //Sinhala,
+ { 73, 127 },
+ //Thai,
+ { 24, 127 },
+ //Lao,
+ { 25, 127 },
+ //Tibetan,
+ { 70, 127 },
+ //Myanmar,
+ { 74, 127 },
+ // Georgian,
+ { 26, 127 },
+ // Khmer,
+ { 80, 127 },
+ // SimplifiedChinese,
+ { 126, 127 },
+ // TraditionalChinese,
+ { 126, 127 },
+ // Japanese,
+ { 126, 127 },
+ // Korean,
+ { 56, 127 },
+ // Vietnamese,
+ { 0, 127 }, // same as latin1
+ // Other,
+ { 126, 127 }
+};
+
+#define SimplifiedChineseCsbBit 18
+#define TraditionalChineseCsbBit 20
+#define JapaneseCsbBit 17
+#define KoreanCsbBit 21
+
+static QList<QFontDatabase::WritingSystem> determineWritingSystemsFromTrueTypeBits(quint32 unicodeRange[4], quint32 codePageRange[2])
+{
+ QList<QFontDatabase::WritingSystem> writingSystems;
+ bool hasScript = false;
+
+ int i;
+ for(i = 0; i < QFontDatabase::WritingSystemsCount; i++) {
+ int bit = requiredUnicodeBits[i][0];
+ int index = bit/32;
+ int flag = 1 << (bit&31);
+ if (bit != 126 && unicodeRange[index] & flag) {
+ bit = requiredUnicodeBits[i][1];
+ index = bit/32;
+
+ flag = 1 << (bit&31);
+ if (bit == 127 || unicodeRange[index] & flag) {
+ writingSystems.append(QFontDatabase::WritingSystem(i));
+ hasScript = true;
+ // qDebug("font %s: index=%d, flag=%8x supports script %d", familyName.latin1(), index, flag, i);
+ }
+ }
+ }
+ if(codePageRange[0] & (1 << SimplifiedChineseCsbBit)) {
+ writingSystems.append(QFontDatabase::SimplifiedChinese);
+ hasScript = true;
+ //qDebug("font %s supports Simplified Chinese", familyName.latin1());
+ }
+ if(codePageRange[0] & (1 << TraditionalChineseCsbBit)) {
+ writingSystems.append(QFontDatabase::TraditionalChinese);
+ hasScript = true;
+ //qDebug("font %s supports Traditional Chinese", familyName.latin1());
+ }
+ if(codePageRange[0] & (1 << JapaneseCsbBit)) {
+ writingSystems.append(QFontDatabase::Japanese);
+ hasScript = true;
+ //qDebug("font %s supports Japanese", familyName.latin1());
+ }
+ if(codePageRange[0] & (1 << KoreanCsbBit)) {
+ writingSystems.append(QFontDatabase::Korean);
+ hasScript = true;
+ //qDebug("font %s supports Korean", familyName.latin1());
+ }
+ if (!hasScript)
+ writingSystems.append(QFontDatabase::Symbol);
+
+ return writingSystems;
+}
+#endif
+
+class QFontDatabasePrivate
+{
+public:
+ QFontDatabasePrivate()
+ : count(0), families(0), reregisterAppFonts(false)
+#if defined(Q_WS_QWS)
+ , stream(0)
+#endif
+ { }
+ ~QFontDatabasePrivate() {
+ free();
+ }
+ QtFontFamily *family(const QString &f, bool = false);
+ void free() {
+ while (count--)
+ delete families[count];
+ ::free(families);
+ families = 0;
+ count = 0;
+ // don't clear the memory fonts!
+ }
+
+ int count;
+ QtFontFamily **families;
+
+ struct ApplicationFont {
+ QString fileName;
+ QByteArray data;
+#if defined(Q_OS_WIN)
+ HANDLE handle;
+ bool memoryFont;
+ QVector<FONTSIGNATURE> signatures;
+#elif defined(Q_WS_MAC)
+ ATSFontContainerRef handle;
+#endif
+ QStringList families;
+ };
+ QVector<ApplicationFont> applicationFonts;
+ int addAppFont(const QByteArray &fontData, const QString &fileName);
+ bool reregisterAppFonts;
+ bool isApplicationFont(const QString &fileName);
+
+ void invalidate();
+
+#if defined(Q_WS_QWS)
+ bool loadFromCache(const QString &fontPath);
+ void addFont(const QString &familyname, const char *foundryname, int weight,
+ bool italic, int pixelSize, const QByteArray &file, int fileIndex,
+ bool antialiased,
+ const QList<QFontDatabase::WritingSystem> &writingSystems = QList<QFontDatabase::WritingSystem>());
+ void addQPF2File(const QByteArray &file);
+#ifndef QT_NO_FREETYPE
+ QStringList addTTFile(const QByteArray &file, const QByteArray &fontData = QByteArray());
+#endif
+
+ QDataStream *stream;
+ QStringList fallbackFamilies;
+#endif
+};
+
+void QFontDatabasePrivate::invalidate()
+{
+ QFontCache::instance()->clear();
+ free();
+ emit static_cast<QApplication *>(QApplication::instance())->fontDatabaseChanged();
+}
+
+QtFontFamily *QFontDatabasePrivate::family(const QString &f, bool create)
+{
+ int low = 0;
+ int high = count;
+ int pos = count / 2;
+ int res = 1;
+ if (count) {
+ while ((res = families[pos]->name.compare(f, Qt::CaseInsensitive)) && pos != low) {
+ if (res > 0)
+ high = pos;
+ else
+ low = pos;
+ pos = (high + low) / 2;
+ };
+ if (!res)
+ return families[pos];
+ }
+ if (!create)
+ return 0;
+
+ if (res < 0)
+ pos++;
+
+ // qDebug("adding family %s at %d total=%d", f.latin1(), pos, count);
+ if (!(count % 8))
+ families = (QtFontFamily **)
+ realloc(families,
+ (((count+8) >> 3) << 3) * sizeof(QtFontFamily *));
+
+ memmove(families + pos + 1, families + pos, (count-pos)*sizeof(QtFontFamily *));
+ families[pos] = new QtFontFamily(f);
+ count++;
+ return families[pos];
+}
+
+
+static const int scriptForWritingSystem[] = {
+ QUnicodeTables::Common, // Any
+ QUnicodeTables::Latin, // Latin
+ QUnicodeTables::Greek, // Greek
+ QUnicodeTables::Cyrillic, // Cyrillic
+ QUnicodeTables::Armenian, // Armenian
+ QUnicodeTables::Hebrew, // Hebrew
+ QUnicodeTables::Arabic, // Arabic
+ QUnicodeTables::Syriac, // Syriac
+ QUnicodeTables::Thaana, // Thaana
+ QUnicodeTables::Devanagari, // Devanagari
+ QUnicodeTables::Bengali, // Bengali
+ QUnicodeTables::Gurmukhi, // Gurmukhi
+ QUnicodeTables::Gujarati, // Gujarati
+ QUnicodeTables::Oriya, // Oriya
+ QUnicodeTables::Tamil, // Tamil
+ QUnicodeTables::Telugu, // Telugu
+ QUnicodeTables::Kannada, // Kannada
+ QUnicodeTables::Malayalam, // Malayalam
+ QUnicodeTables::Sinhala, // Sinhala
+ QUnicodeTables::Thai, // Thai
+ QUnicodeTables::Lao, // Lao
+ QUnicodeTables::Tibetan, // Tibetan
+ QUnicodeTables::Myanmar, // Myanmar
+ QUnicodeTables::Georgian, // Georgian
+ QUnicodeTables::Khmer, // Khmer
+ QUnicodeTables::Common, // SimplifiedChinese
+ QUnicodeTables::Common, // TraditionalChinese
+ QUnicodeTables::Common, // Japanese
+ QUnicodeTables::Hangul, // Korean
+ QUnicodeTables::Common, // Vietnamese
+ QUnicodeTables::Common, // Yi
+ QUnicodeTables::Common, // Tagalog
+ QUnicodeTables::Common, // Hanunoo
+ QUnicodeTables::Common, // Buhid
+ QUnicodeTables::Common, // Tagbanwa
+ QUnicodeTables::Common, // Limbu
+ QUnicodeTables::Common, // TaiLe
+ QUnicodeTables::Common, // Braille
+ QUnicodeTables::Common, // Symbol
+ QUnicodeTables::Ogham, // Ogham
+ QUnicodeTables::Runic // Runic
+};
+
+
+#if defined Q_WS_QWS || (defined(Q_WS_X11) && !defined(QT_NO_FONTCONFIG)) || defined(Q_WS_WIN)
+static inline bool requiresOpenType(int writingSystem)
+{
+ return ((writingSystem >= QFontDatabase::Syriac && writingSystem <= QFontDatabase::Sinhala)
+ || writingSystem == QFontDatabase::Khmer);
+}
+static inline bool scriptRequiresOpenType(int script)
+{
+ return ((script >= QUnicodeTables::Syriac && script <= QUnicodeTables::Sinhala)
+ || script == QUnicodeTables::Khmer);
+}
+#endif
+
+
+/*!
+ \internal
+
+ This makes sense of the font family name:
+
+ if the family name contains a '[' and a ']', then we take the text
+ between the square brackets as the foundry, and the text before the
+ square brackets as the family (ie. "Arial [Monotype]")
+*/
+static void parseFontName(const QString &name, QString &foundry, QString &family)
+{
+ int i = name.indexOf(QLatin1Char('['));
+ int li = name.lastIndexOf(QLatin1Char(']'));
+ if (i >= 0 && li >= 0 && i < li) {
+ foundry = name.mid(i + 1, li - i - 1);
+ if (i > 0 && name[i - 1] == QLatin1Char(' '))
+ i--;
+ family = name.left(i);
+ } else {
+ foundry.clear();
+ family = name;
+ }
+
+ // capitalize the family/foundry names
+ bool space = true;
+ QChar *s = family.data();
+ int len = family.length();
+ while(len--) {
+ if (space) *s = s->toUpper();
+ space = s->isSpace();
+ ++s;
+ }
+
+ space = true;
+ s = foundry.data();
+ len = foundry.length();
+ while(len--) {
+ if (space) *s = s->toUpper();
+ space = s->isSpace();
+ ++s;
+ }
+}
+
+
+struct QtFontDesc
+{
+ inline QtFontDesc() : family(0), foundry(0), style(0), size(0), encoding(0), familyIndex(-1) {}
+ QtFontFamily *family;
+ QtFontFoundry *foundry;
+ QtFontStyle *style;
+ QtFontSize *size;
+ QtFontEncoding *encoding;
+ int familyIndex;
+};
+
+#if !defined(Q_WS_MAC)
+static void match(int script, const QFontDef &request,
+ const QString &family_name, const QString &foundry_name, int force_encoding_id,
+ QtFontDesc *desc, const QList<int> &blacklistedFamilies = QList<int>());
+
+#if defined(Q_WS_X11) || defined(Q_WS_QWS)
+static void initFontDef(const QtFontDesc &desc, const QFontDef &request, QFontDef *fontDef)
+{
+ fontDef->family = desc.family->name;
+ if (! desc.foundry->name.isEmpty() && desc.family->count > 1) {
+ fontDef->family += QString::fromLatin1(" [");
+ fontDef->family += desc.foundry->name;
+ fontDef->family += QString::fromLatin1("]");
+ }
+
+ if (desc.style->smoothScalable)
+ fontDef->pixelSize = request.pixelSize;
+ else if ((desc.style->bitmapScalable && (request.styleStrategy & QFont::PreferMatch)))
+ fontDef->pixelSize = request.pixelSize;
+ else
+ fontDef->pixelSize = desc.size->pixelSize;
+
+ fontDef->styleHint = request.styleHint;
+ fontDef->styleStrategy = request.styleStrategy;
+
+ fontDef->weight = desc.style->key.weight;
+ fontDef->style = desc.style->key.style;
+ fontDef->fixedPitch = desc.family->fixedPitch;
+ fontDef->stretch = desc.style->key.stretch;
+ fontDef->ignorePitch = false;
+}
+#endif
+#endif
+
+#if defined(Q_WS_X11) || defined(Q_WS_WIN)
+static void getEngineData(const QFontPrivate *d, const QFontCache::Key &key)
+{
+ // look for the requested font in the engine data cache
+ d->engineData = QFontCache::instance()->findEngineData(key);
+ if (!d->engineData) {
+ // create a new one
+ d->engineData = new QFontEngineData;
+ QFontCache::instance()->insertEngineData(key, d->engineData);
+ } else {
+ d->engineData->ref.ref();
+ }
+}
+
+static QStringList familyList(const QFontDef &req)
+{
+ // list of families to try
+ QStringList family_list;
+ if (req.family.isEmpty())
+ return family_list;
+
+ QStringList list = req.family.split(QLatin1Char(','));
+ for (int i = 0; i < list.size(); ++i) {
+ QString str = list.at(i).trimmed();
+ if ((str.startsWith(QLatin1Char('"')) && str.endsWith(QLatin1Char('"')))
+ || (str.startsWith(QLatin1Char('\'')) && str.endsWith(QLatin1Char('\''))))
+ str = str.mid(1, str.length() - 2);
+ family_list << str;
+ }
+
+ // append the substitute list for each family in family_list
+ QStringList subs_list;
+ QStringList::ConstIterator it = family_list.constBegin(), end = family_list.constEnd();
+ for (; it != end; ++it)
+ subs_list += QFont::substitutes(*it);
+// qDebug() << "adding substs: " << subs_list;
+
+ family_list += subs_list;
+
+ return family_list;
+}
+#endif
+
+Q_GLOBAL_STATIC(QFontDatabasePrivate, privateDb)
+Q_GLOBAL_STATIC_WITH_ARGS(QMutex, fontDatabaseMutex, (QMutex::Recursive))
+
+// used in qfontengine_x11.cpp
+QMutex *qt_fontdatabase_mutex()
+{
+ return fontDatabaseMutex();
+}
+
+#define SMOOTH_SCALABLE 0xffff
+
+QT_BEGIN_INCLUDE_NAMESPACE
+#if defined(Q_WS_X11)
+# include "qfontdatabase_x11.cpp"
+#elif defined(Q_WS_MAC)
+# include "qfontdatabase_mac.cpp"
+#elif defined(Q_WS_WIN)
+# include "qfontdatabase_win.cpp"
+#elif defined(Q_WS_QWS)
+# include "qfontdatabase_qws.cpp"
+#endif
+QT_END_INCLUDE_NAMESPACE
+
+static QtFontStyle *bestStyle(QtFontFoundry *foundry, const QtFontStyle::Key &styleKey)
+{
+ int best = 0;
+ int dist = 0xffff;
+
+ for ( int i = 0; i < foundry->count; i++ ) {
+ QtFontStyle *style = foundry->styles[i];
+
+ int d = qAbs( styleKey.weight - style->key.weight );
+
+ if ( styleKey.stretch != 0 && style->key.stretch != 0 ) {
+ d += qAbs( styleKey.stretch - style->key.stretch );
+ }
+
+ if (styleKey.style != style->key.style) {
+ if (styleKey.style != QFont::StyleNormal && style->key.style != QFont::StyleNormal)
+ // one is italic, the other oblique
+ d += 0x0001;
+ else
+ d += 0x1000;
+ }
+
+ if ( d < dist ) {
+ best = i;
+ dist = d;
+ }
+ }
+
+ FM_DEBUG( " best style has distance 0x%x", dist );
+ return foundry->styles[best];
+}
+
+#if defined(Q_WS_X11)
+static QtFontEncoding *findEncoding(int script, int styleStrategy,
+ QtFontSize *size, int force_encoding_id)
+{
+ QtFontEncoding *encoding = 0;
+
+ if (force_encoding_id >= 0) {
+ encoding = size->encodingID(force_encoding_id);
+ if (!encoding)
+ FM_DEBUG(" required encoding_id not available");
+ return encoding;
+ }
+
+ if (styleStrategy & (QFont::OpenGLCompatible | QFont::PreferBitmap)) {
+ FM_DEBUG(" PreferBitmap and/or OpenGL set, skipping Freetype");
+ } else {
+ encoding = size->encodingID(-1); // -1 == prefer Freetype
+ if (encoding)
+ return encoding;
+ }
+
+ // FT not available, find an XLFD font, trying the default encoding first
+ encoding = size->encodingID(QFontPrivate::defaultEncodingID);
+ if (encoding) {
+ // does it support the requested script?
+ bool supportsScript = false;
+ for (int ws = 1; !supportsScript && ws < QFontDatabase::WritingSystemsCount; ++ws) {
+ if (scriptForWritingSystem[ws] != script)
+ continue;
+ supportsScript = writingSystems_for_xlfd_encoding[encoding->encoding][ws];
+ }
+ if (!supportsScript)
+ encoding = 0;
+ }
+ // find the first encoding that supports the requested script
+ for (int ws = 1; !encoding && ws < QFontDatabase::WritingSystemsCount; ++ws) {
+ if (scriptForWritingSystem[ws] != script)
+ continue;
+ for (int x = 0; !encoding && x < size->count; ++x) {
+ const int enc = size->encodings[x].encoding;
+ if (writingSystems_for_xlfd_encoding[enc][ws])
+ encoding = size->encodings + x;
+ }
+ }
+
+ return encoding;
+}
+#endif // Q_WS_X11
+
+#if !defined(Q_WS_MAC)
+static
+unsigned int bestFoundry(int script, unsigned int score, int styleStrategy,
+ const QtFontFamily *family, const QString &foundry_name,
+ QtFontStyle::Key styleKey, int pixelSize, char pitch,
+ QtFontDesc *desc, int force_encoding_id)
+{
+ Q_UNUSED(force_encoding_id);
+ Q_UNUSED(script);
+ Q_UNUSED(pitch);
+
+ desc->foundry = 0;
+ desc->style = 0;
+ desc->size = 0;
+ desc->encoding = 0;
+
+
+ FM_DEBUG(" REMARK: looking for best foundry for family '%s' [%d]", family->name.toLatin1().constData(), family->count);
+
+ for (int x = 0; x < family->count; ++x) {
+ QtFontFoundry *foundry = family->foundries[x];
+ if (!foundry_name.isEmpty() && foundry->name.compare(foundry_name, Qt::CaseInsensitive) != 0)
+ continue;
+
+ FM_DEBUG(" looking for matching style in foundry '%s' %d",
+ foundry->name.isEmpty() ? "-- none --" : foundry->name.toLatin1().constData(), foundry->count);
+
+ QtFontStyle *style = bestStyle(foundry, styleKey);
+
+ if (!style->smoothScalable && (styleStrategy & QFont::ForceOutline)) {
+ FM_DEBUG(" ForceOutline set, but not smoothly scalable");
+ continue;
+ }
+
+ int px = -1;
+ QtFontSize *size = 0;
+
+ // 1. see if we have an exact matching size
+ if (!(styleStrategy & QFont::ForceOutline)) {
+ size = style->pixelSize(pixelSize);
+ if (size) {
+ FM_DEBUG(" found exact size match (%d pixels)", size->pixelSize);
+ px = size->pixelSize;
+ }
+ }
+
+ // 2. see if we have a smoothly scalable font
+ if (!size && style->smoothScalable && ! (styleStrategy & QFont::PreferBitmap)) {
+ size = style->pixelSize(SMOOTH_SCALABLE);
+ if (size) {
+ FM_DEBUG(" found smoothly scalable font (%d pixels)", pixelSize);
+ px = pixelSize;
+ }
+ }
+
+ // 3. see if we have a bitmap scalable font
+ if (!size && style->bitmapScalable && (styleStrategy & QFont::PreferMatch)) {
+ size = style->pixelSize(0);
+ if (size) {
+ FM_DEBUG(" found bitmap scalable font (%d pixels)", pixelSize);
+ px = pixelSize;
+ }
+ }
+
+#ifdef Q_WS_X11
+ QtFontEncoding *encoding = 0;
+#endif
+
+ // 4. find closest size match
+ if (! size) {
+ unsigned int distance = ~0u;
+ for (int x = 0; x < style->count; ++x) {
+#ifdef Q_WS_X11
+ encoding =
+ findEncoding(script, styleStrategy, style->pixelSizes + x, force_encoding_id);
+ if (!encoding) {
+ FM_DEBUG(" size %3d does not support the script we want",
+ style->pixelSizes[x].pixelSize);
+ continue;
+ }
+#endif
+
+ unsigned int d;
+ if (style->pixelSizes[x].pixelSize < pixelSize) {
+ // penalize sizes that are smaller than the
+ // requested size, due to truncation from floating
+ // point to integer conversions
+ d = pixelSize - style->pixelSizes[x].pixelSize + 1;
+ } else {
+ d = style->pixelSizes[x].pixelSize - pixelSize;
+ }
+
+ if (d < distance) {
+ distance = d;
+ size = style->pixelSizes + x;
+ FM_DEBUG(" best size so far: %3d (%d)", size->pixelSize, pixelSize);
+ }
+ }
+
+ if (!size) {
+ FM_DEBUG(" no size supports the script we want");
+ continue;
+ }
+
+ if (style->bitmapScalable && ! (styleStrategy & QFont::PreferQuality) &&
+ (distance * 10 / pixelSize) >= 2) {
+ // the closest size is not close enough, go ahead and
+ // use a bitmap scaled font
+ size = style->pixelSize(0);
+ px = pixelSize;
+ } else {
+ px = size->pixelSize;
+ }
+ }
+
+#ifdef Q_WS_X11
+ if (size) {
+ encoding = findEncoding(script, styleStrategy, size, force_encoding_id);
+ if (!encoding) size = 0;
+ }
+ if (! encoding) {
+ FM_DEBUG(" foundry doesn't support the script we want");
+ continue;
+ }
+#endif // Q_WS_X11
+
+ unsigned int this_score = 0x0000;
+ enum {
+ PitchMismatch = 0x4000,
+ StyleMismatch = 0x2000,
+ BitmapScaledPenalty = 0x1000,
+ EncodingMismatch = 0x0002,
+ XLFDPenalty = 0x0001
+ };
+#ifdef Q_WS_X11
+ if (encoding->encoding != -1) {
+ this_score += XLFDPenalty;
+ if (encoding->encoding != QFontPrivate::defaultEncodingID)
+ this_score += EncodingMismatch;
+ }
+ if (pitch != '*') {
+ if (!(pitch == 'm' && encoding->pitch == 'c') && pitch != encoding->pitch)
+ this_score += PitchMismatch;
+ }
+#else
+ if (pitch != '*') {
+#if !defined(QWS) && defined(Q_OS_MAC)
+ qt_mac_get_fixed_pitch(const_cast<QtFontFamily*>(family));
+#endif
+ if ((pitch == 'm' && !family->fixedPitch)
+ || (pitch == 'p' && family->fixedPitch))
+ this_score += PitchMismatch;
+ }
+#endif
+ if (styleKey != style->key)
+ this_score += StyleMismatch;
+ if (!style->smoothScalable && px != size->pixelSize) // bitmap scaled
+ this_score += BitmapScaledPenalty;
+ if (px != pixelSize) // close, but not exact, size match
+ this_score += qAbs(px - pixelSize);
+
+ if (this_score < score) {
+ FM_DEBUG(" found a match: score %x best score so far %x",
+ this_score, score);
+
+ score = this_score;
+ desc->foundry = foundry;
+ desc->style = style;
+ desc->size = size;
+#ifdef Q_WS_X11
+ desc->encoding = encoding;
+#endif // Q_WS_X11
+ } else {
+ FM_DEBUG(" score %x no better than best %x", this_score, score);
+ }
+ }
+
+ return score;
+}
+#endif
+
+#if !defined(Q_WS_MAC)
+/*!
+ \internal
+
+ Tries to find the best match for a given request and family/foundry
+*/
+static void match(int script, const QFontDef &request,
+ const QString &family_name, const QString &foundry_name, int force_encoding_id,
+ QtFontDesc *desc, const QList<int> &blacklistedFamilies)
+{
+ Q_UNUSED(force_encoding_id);
+
+ QtFontStyle::Key styleKey;
+ styleKey.style = request.style;
+ styleKey.weight = request.weight;
+ styleKey.stretch = request.stretch;
+ char pitch = request.ignorePitch ? '*' : request.fixedPitch ? 'm' : 'p';
+
+ FM_DEBUG("QFontDatabase::match\n"
+ " request:\n"
+ " family: %s [%s], script: %d\n"
+ " weight: %d, style: %d\n"
+ " stretch: %d\n"
+ " pixelSize: %d\n"
+ " pitch: %c",
+ family_name.isEmpty() ? "-- first in script --" : family_name.toLatin1().constData(),
+ foundry_name.isEmpty() ? "-- any --" : foundry_name.toLatin1().constData(),
+ script, request.weight, request.style, request.stretch, request.pixelSize, pitch);
+#if defined(FONT_MATCH_DEBUG) && defined(Q_WS_X11)
+ if (force_encoding_id >= 0) {
+ FM_DEBUG(" required encoding: %d", force_encoding_id);
+ }
+#endif
+
+ desc->family = 0;
+ desc->foundry = 0;
+ desc->style = 0;
+ desc->size = 0;
+ desc->encoding = 0;
+ desc->familyIndex = -1;
+
+ unsigned int score = ~0u;
+
+ load(family_name, script);
+
+ QFontDatabasePrivate *db = privateDb();
+ for (int x = 0; x < db->count; ++x) {
+ if (blacklistedFamilies.contains(x))
+ continue;
+ QtFontDesc test;
+ test.family = db->families[x];
+ test.familyIndex = x;
+
+ if (!family_name.isEmpty()
+ && test.family->name.compare(family_name, Qt::CaseInsensitive) != 0
+#ifdef Q_WS_WIN
+ && test.family->english_name.compare(family_name, Qt::CaseInsensitive) != 0
+#endif
+ )
+ continue;
+
+ if (family_name.isEmpty())
+ load(test.family->name, script);
+
+ uint score_adjust = 0;
+
+ bool supported = (script == QUnicodeTables::Common);
+ for (int ws = 1; !supported && ws < QFontDatabase::WritingSystemsCount; ++ws) {
+ if (scriptForWritingSystem[ws] != script)
+ continue;
+ if (test.family->writingSystems[ws] & QtFontFamily::Supported)
+ supported = true;
+ }
+ if (!supported) {
+ // family not supported in the script we want
+ continue;
+ }
+
+ // as we know the script is supported, we can be sure
+ // to find a matching font here.
+ unsigned int newscore =
+ bestFoundry(script, score, request.styleStrategy,
+ test.family, foundry_name, styleKey, request.pixelSize, pitch,
+ &test, force_encoding_id);
+ if (test.foundry == 0) {
+ // the specific foundry was not found, so look for
+ // any foundry matching our requirements
+ newscore = bestFoundry(script, score, request.styleStrategy, test.family,
+ QString(), styleKey, request.pixelSize,
+ pitch, &test, force_encoding_id);
+ }
+ newscore += score_adjust;
+
+ if (newscore < score) {
+ score = newscore;
+ *desc = test;
+ }
+ if (newscore < 10) // xlfd instead of FT... just accept it
+ break;
+ }
+}
+#endif
+
+static QString styleStringHelper(int weight, QFont::Style style)
+{
+ QString result;
+ if (weight >= QFont::Black)
+ result = qApp->translate("QFontDatabase", "Black");
+ else if (weight >= QFont::Bold)
+ result = qApp->translate("QFontDatabase", "Bold");
+ else if (weight >= QFont::DemiBold)
+ result = qApp->translate("QFontDatabase", "Demi Bold");
+ else if (weight < QFont::Normal)
+ result = qApp->translate("QFontDatabase", "Light");
+
+ if (style == QFont::StyleItalic)
+ result += QLatin1Char(' ') + qApp->translate("QFontDatabase", "Italic");
+ else if (style == QFont::StyleOblique)
+ result += QLatin1Char(' ') + qApp->translate("QFontDatabase", "Oblique");
+
+ if (result.isEmpty())
+ result = qApp->translate("QFontDatabase", "Normal");
+
+ return result.simplified();
+}
+
+/*!
+ Returns a string that describes the style of the \a font. For
+ example, "Bold Italic", "Bold", "Italic" or "Normal". An empty
+ string may be returned.
+*/
+QString QFontDatabase::styleString(const QFont &font)
+{
+ return styleStringHelper(font.weight(), font.style());
+}
+
+/*!
+ Returns a string that describes the style of the \a fontInfo. For
+ example, "Bold Italic", "Bold", "Italic" or "Normal". An empty
+ string may be returned.
+*/
+QString QFontDatabase::styleString(const QFontInfo &fontInfo)
+{
+ return styleStringHelper(fontInfo.weight(), fontInfo.style());
+}
+
+
+/*!
+ \class QFontDatabase
+ \threadsafe
+
+ \brief The QFontDatabase class provides information about the fonts available in the underlying window system.
+
+ \ingroup environment
+ \ingroup multimedia
+ \ingroup text
+
+ The most common uses of this class are to query the database for
+ the list of font families() and for the pointSizes() and styles()
+ that are available for each family. An alternative to pointSizes()
+ is smoothSizes() which returns the sizes at which a given family
+ and style will look attractive.
+
+ If the font family is available from two or more foundries the
+ foundry name is included in the family name, e.g. "Helvetica
+ [Adobe]" and "Helvetica [Cronyx]". When you specify a family you
+ can either use the old hyphenated Qt 2.x "foundry-family" format,
+ e.g. "Cronyx-Helvetica", or the new bracketed Qt 3.x "family
+ [foundry]" format e.g. "Helvetica [Cronyx]". If the family has a
+ foundry it is always returned, e.g. by families(), using the
+ bracketed format.
+
+ The font() function returns a QFont given a family, style and
+ point size.
+
+ A family and style combination can be checked to see if it is
+ italic() or bold(), and to retrieve its weight(). Similarly we can
+ call isBitmapScalable(), isSmoothlyScalable(), isScalable() and
+ isFixedPitch().
+
+ Use the styleString() to obtain a text version of a style.
+
+ The QFontDatabase class also supports some static functions, for
+ example, standardSizes(). You can retrieve the description of a
+ writing system using writingSystemName(), and a sample of
+ characters in a writing system with writingSystemSample().
+
+ Example:
+
+ \snippet doc/src/snippets/qfontdatabase/main.cpp 0
+ \snippet doc/src/snippets/qfontdatabase/main.cpp 1
+
+ This example gets the list of font families, the list of
+ styles for each family, and the point sizes that are available for
+ each combination of family and style, displaying this information
+ in a tree view.
+
+ \sa QFont, QFontInfo, QFontMetrics, QFontComboBox, {Character Map Example}
+*/
+
+/*!
+ Creates a font database object.
+*/
+QFontDatabase::QFontDatabase()
+{
+ QMutexLocker locker(fontDatabaseMutex());
+ createDatabase();
+ d = privateDb();
+}
+
+/*!
+ \enum QFontDatabase::WritingSystem
+
+ \value Any
+ \value Latin
+ \value Greek
+ \value Cyrillic
+ \value Armenian
+ \value Hebrew
+ \value Arabic
+ \value Syriac
+ \value Thaana
+ \value Devanagari
+ \value Bengali
+ \value Gurmukhi
+ \value Gujarati
+ \value Oriya
+ \value Tamil
+ \value Telugu
+ \value Kannada
+ \value Malayalam
+ \value Sinhala
+ \value Thai
+ \value Lao
+ \value Tibetan
+ \value Myanmar
+ \value Georgian
+ \value Khmer
+ \value SimplifiedChinese
+ \value TraditionalChinese
+ \value Japanese
+ \value Korean
+ \value Vietnamese
+ \value Symbol
+ \value Other (the same as Symbol)
+ \value Ogham
+ \value Runic
+
+ \omitvalue WritingSystemsCount
+*/
+
+/*!
+ Returns a sorted list of the available writing systems. This is
+ list generated from information about all installed fonts on the
+ system.
+
+ \sa families()
+*/
+QList<QFontDatabase::WritingSystem> QFontDatabase::writingSystems() const
+{
+ QMutexLocker locker(fontDatabaseMutex());
+
+ QT_PREPEND_NAMESPACE(load)();
+#ifdef Q_WS_X11
+ checkSymbolFonts();
+#endif
+
+ QList<WritingSystem> list;
+ for (int i = 0; i < d->count; ++i) {
+ QtFontFamily *family = d->families[i];
+ if (family->count == 0)
+ continue;
+ for (int x = Latin; x < WritingSystemsCount; ++x) {
+ const WritingSystem writingSystem = WritingSystem(x);
+ if (!(family->writingSystems[writingSystem] & QtFontFamily::Supported))
+ continue;
+ if (!list.contains(writingSystem))
+ list.append(writingSystem);
+ }
+ }
+ qSort(list);
+ return list;
+}
+
+
+/*!
+ Returns a sorted list of the writing systems supported by a given
+ font \a family.
+
+ \sa families()
+*/
+QList<QFontDatabase::WritingSystem> QFontDatabase::writingSystems(const QString &family) const
+{
+ QString familyName, foundryName;
+ parseFontName(family, foundryName, familyName);
+
+ QMutexLocker locker(fontDatabaseMutex());
+
+ QT_PREPEND_NAMESPACE(load)();
+#ifdef Q_WS_X11
+ checkSymbolFonts(familyName);
+#endif
+
+ QList<WritingSystem> list;
+ QtFontFamily *f = d->family(familyName);
+ if (!f || f->count == 0)
+ return list;
+
+ for (int x = Latin; x < WritingSystemsCount; ++x) {
+ const WritingSystem writingSystem = WritingSystem(x);
+ if (f->writingSystems[writingSystem] & QtFontFamily::Supported)
+ list.append(writingSystem);
+ }
+ return list;
+}
+
+
+/*!
+ Returns a sorted list of the available font families which support
+ the \a writingSystem.
+
+ If a family exists in several foundries, the returned name for
+ that font is in the form "family [foundry]". Examples: "Times
+ [Adobe]", "Times [Cronyx]", "Palatino".
+
+ \sa writingSystems()
+*/
+QStringList QFontDatabase::families(WritingSystem writingSystem) const
+{
+ QMutexLocker locker(fontDatabaseMutex());
+
+ QT_PREPEND_NAMESPACE(load)();
+#ifdef Q_WS_X11
+ if (writingSystem != Any)
+ checkSymbolFonts();
+#endif
+
+ QStringList flist;
+ for (int i = 0; i < d->count; i++) {
+ QtFontFamily *f = d->families[i];
+ if (f->count == 0)
+ continue;
+ if (writingSystem != Any && (f->writingSystems[writingSystem] != QtFontFamily::Supported))
+ continue;
+ if (f->count == 1) {
+ flist.append(f->name);
+ } else {
+ for (int j = 0; j < f->count; j++) {
+ QString str = f->name;
+ QString foundry = f->foundries[j]->name;
+ if (!foundry.isEmpty()) {
+ str += QLatin1String(" [");
+ str += foundry;
+ str += QLatin1String("]");
+ }
+ flist.append(str);
+ }
+ }
+ }
+ return flist;
+}
+
+/*!
+ Returns a list of the styles available for the font family \a
+ family. Some example styles: "Light", "Light Italic", "Bold",
+ "Oblique", "Demi". The list may be empty.
+
+ \sa families()
+*/
+QStringList QFontDatabase::styles(const QString &family) const
+{
+ QString familyName, foundryName;
+ parseFontName(family, foundryName, familyName);
+
+ QMutexLocker locker(fontDatabaseMutex());
+
+ QT_PREPEND_NAMESPACE(load)(familyName);
+
+ QStringList l;
+ QtFontFamily *f = d->family(familyName);
+ if (!f)
+ return l;
+
+ QtFontFoundry allStyles(foundryName);
+ for (int j = 0; j < f->count; j++) {
+ QtFontFoundry *foundry = f->foundries[j];
+ if (foundryName.isEmpty() || foundry->name.compare(foundryName, Qt::CaseInsensitive) == 0) {
+ for (int k = 0; k < foundry->count; k++) {
+ QtFontStyle::Key ke(foundry->styles[k]->key);
+ ke.stretch = 0;
+ allStyles.style(ke, true);
+ }
+ }
+ }
+
+ for (int i = 0; i < allStyles.count; i++)
+ l.append(styleStringHelper(allStyles.styles[i]->key.weight, (QFont::Style)allStyles.styles[i]->key.style));
+ return l;
+}
+
+/*!
+ Returns true if the font that has family \a family and style \a
+ style is fixed pitch; otherwise returns false.
+*/
+
+bool QFontDatabase::isFixedPitch(const QString &family,
+ const QString &style) const
+{
+ Q_UNUSED(style);
+
+ QString familyName, foundryName;
+ parseFontName(family, foundryName, familyName);
+
+ QMutexLocker locker(fontDatabaseMutex());
+
+ QT_PREPEND_NAMESPACE(load)(familyName);
+
+ QtFontFamily *f = d->family(familyName);
+#if !defined(QWS) && defined(Q_OS_MAC)
+ qt_mac_get_fixed_pitch(f);
+#endif
+ return (f && f->fixedPitch);
+}
+
+/*!
+ Returns true if the font that has family \a family and style \a
+ style is a scalable bitmap font; otherwise returns false. Scaling
+ a bitmap font usually produces an unattractive hardly readable
+ result, because the pixels of the font are scaled. If you need to
+ scale a bitmap font it is better to scale it to one of the fixed
+ sizes returned by smoothSizes().
+
+ \sa isScalable(), isSmoothlyScalable()
+*/
+bool QFontDatabase::isBitmapScalable(const QString &family,
+ const QString &style) const
+{
+ bool bitmapScalable = false;
+ QString familyName, foundryName;
+ parseFontName(family, foundryName, familyName);
+
+ QMutexLocker locker(fontDatabaseMutex());
+
+ QT_PREPEND_NAMESPACE(load)(familyName);
+
+ QtFontStyle::Key styleKey(style);
+
+ QtFontFamily *f = d->family(familyName);
+ if (!f) return bitmapScalable;
+
+ for (int j = 0; j < f->count; j++) {
+ QtFontFoundry *foundry = f->foundries[j];
+ if (foundryName.isEmpty() || foundry->name.compare(foundryName, Qt::CaseInsensitive) == 0) {
+ for (int k = 0; k < foundry->count; k++)
+ if ((style.isEmpty() || foundry->styles[k]->key == styleKey)
+ && foundry->styles[k]->bitmapScalable && !foundry->styles[k]->smoothScalable) {
+ bitmapScalable = true;
+ goto end;
+ }
+ }
+ }
+ end:
+ return bitmapScalable;
+}
+
+
+/*!
+ Returns true if the font that has family \a family and style \a
+ style is smoothly scalable; otherwise returns false. If this
+ function returns true, it's safe to scale this font to any size,
+ and the result will always look attractive.
+
+ \sa isScalable(), isBitmapScalable()
+*/
+bool QFontDatabase::isSmoothlyScalable(const QString &family, const QString &style) const
+{
+ bool smoothScalable = false;
+ QString familyName, foundryName;
+ parseFontName(family, foundryName, familyName);
+
+ QMutexLocker locker(fontDatabaseMutex());
+
+ QT_PREPEND_NAMESPACE(load)(familyName);
+
+ QtFontStyle::Key styleKey(style);
+
+ QtFontFamily *f = d->family(familyName);
+ if (!f) return smoothScalable;
+
+ for (int j = 0; j < f->count; j++) {
+ QtFontFoundry *foundry = f->foundries[j];
+ if (foundryName.isEmpty() || foundry->name.compare(foundryName, Qt::CaseInsensitive) == 0) {
+ for (int k = 0; k < foundry->count; k++)
+ if ((style.isEmpty() || foundry->styles[k]->key == styleKey) && foundry->styles[k]->smoothScalable) {
+ smoothScalable = true;
+ goto end;
+ }
+ }
+ }
+ end:
+ return smoothScalable;
+}
+
+/*!
+ Returns true if the font that has family \a family and style \a
+ style is scalable; otherwise returns false.
+
+ \sa isBitmapScalable(), isSmoothlyScalable()
+*/
+bool QFontDatabase::isScalable(const QString &family,
+ const QString &style) const
+{
+ QMutexLocker locker(fontDatabaseMutex());
+ if (isSmoothlyScalable(family, style))
+ return true;
+ return isBitmapScalable(family, style);
+}
+
+
+/*!
+ Returns a list of the point sizes available for the font that has
+ family \a family and style \a style. The list may be empty.
+
+ \sa smoothSizes(), standardSizes()
+*/
+QList<int> QFontDatabase::pointSizes(const QString &family,
+ const QString &style)
+{
+#if defined(Q_WS_WIN)
+ // windows and macosx are always smoothly scalable
+ Q_UNUSED(family);
+ Q_UNUSED(style);
+ return standardSizes();
+#else
+ bool smoothScalable = false;
+ QString familyName, foundryName;
+ parseFontName(family, foundryName, familyName);
+
+ QMutexLocker locker(fontDatabaseMutex());
+
+ QT_PREPEND_NAMESPACE(load)(familyName);
+
+ QtFontStyle::Key styleKey(style);
+
+ QList<int> sizes;
+
+ QtFontFamily *fam = d->family(familyName);
+ if (!fam) return sizes;
+
+
+#ifdef Q_WS_X11
+ int dpi = QX11Info::appDpiY();
+#else
+ const int dpi = qt_defaultDpiY(); // embedded
+#endif
+
+ for (int j = 0; j < fam->count; j++) {
+ QtFontFoundry *foundry = fam->foundries[j];
+ if (foundryName.isEmpty() || foundry->name.compare(foundryName, Qt::CaseInsensitive) == 0) {
+ QtFontStyle *style = foundry->style(styleKey);
+ if (!style) continue;
+
+ if (style->smoothScalable) {
+ smoothScalable = true;
+ goto end;
+ }
+ for (int l = 0; l < style->count; l++) {
+ const QtFontSize *size = style->pixelSizes + l;
+
+ if (size->pixelSize != 0 && size->pixelSize != USHRT_MAX) {
+ const uint pointSize = qRound(size->pixelSize * 72.0 / dpi);
+ if (! sizes.contains(pointSize))
+ sizes.append(pointSize);
+ }
+ }
+ }
+ }
+ end:
+ if (smoothScalable)
+ return standardSizes();
+
+ qSort(sizes);
+ return sizes;
+#endif
+}
+
+/*!
+ Returns a QFont object that has family \a family, style \a style
+ and point size \a pointSize. If no matching font could be created,
+ a QFont object that uses the application's default font is
+ returned.
+*/
+QFont QFontDatabase::font(const QString &family, const QString &style,
+ int pointSize) const
+{
+ QString familyName, foundryName;
+ parseFontName(family, foundryName, familyName);
+
+ QMutexLocker locker(fontDatabaseMutex());
+
+ QT_PREPEND_NAMESPACE(load)(familyName);
+
+ QtFontFoundry allStyles(foundryName);
+ QtFontFamily *f = d->family(familyName);
+ if (!f) return QApplication::font();
+
+ for (int j = 0; j < f->count; j++) {
+ QtFontFoundry *foundry = f->foundries[j];
+ if (foundryName.isEmpty() || foundry->name.compare(foundryName, Qt::CaseInsensitive) == 0) {
+ for (int k = 0; k < foundry->count; k++)
+ allStyles.style(foundry->styles[k]->key, true);
+ }
+ }
+
+ QtFontStyle::Key styleKey(style);
+ QtFontStyle *s = bestStyle(&allStyles, styleKey);
+
+ if (!s) // no styles found?
+ return QApplication::font();
+ QFont fnt(family, pointSize, s->key.weight);
+ fnt.setStyle((QFont::Style)s->key.style);
+ return fnt;
+}
+
+
+/*!
+ Returns the point sizes of a font that has family \a family and
+ style \a style that will look attractive. The list may be empty.
+ For non-scalable fonts and bitmap scalable fonts, this function
+ is equivalent to pointSizes().
+
+ \sa pointSizes(), standardSizes()
+*/
+QList<int> QFontDatabase::smoothSizes(const QString &family,
+ const QString &style)
+{
+#ifdef Q_WS_WIN
+ Q_UNUSED(family);
+ Q_UNUSED(style);
+ return QFontDatabase::standardSizes();
+#else
+ bool smoothScalable = false;
+ QString familyName, foundryName;
+ parseFontName(family, foundryName, familyName);
+
+ QMutexLocker locker(fontDatabaseMutex());
+
+ QT_PREPEND_NAMESPACE(load)(familyName);
+
+ QtFontStyle::Key styleKey(style);
+
+ QList<int> sizes;
+
+ QtFontFamily *fam = d->family(familyName);
+ if (!fam)
+ return sizes;
+
+#ifdef Q_WS_X11
+ int dpi = QX11Info::appDpiY();
+#else
+ const int dpi = qt_defaultDpiY(); // embedded
+#endif
+
+ for (int j = 0; j < fam->count; j++) {
+ QtFontFoundry *foundry = fam->foundries[j];
+ if (foundryName.isEmpty() || foundry->name.compare(foundryName, Qt::CaseInsensitive) == 0) {
+ QtFontStyle *style = foundry->style(styleKey);
+ if (!style) continue;
+
+ if (style->smoothScalable) {
+ smoothScalable = true;
+ goto end;
+ }
+ for (int l = 0; l < style->count; l++) {
+ const QtFontSize *size = style->pixelSizes + l;
+
+ if (size->pixelSize != 0 && size->pixelSize != USHRT_MAX) {
+ const uint pointSize = qRound(size->pixelSize * 72.0 / dpi);
+ if (! sizes.contains(pointSize))
+ sizes.append(pointSize);
+ }
+ }
+ }
+ }
+ end:
+ if (smoothScalable)
+ return QFontDatabase::standardSizes();
+
+ qSort(sizes);
+ return sizes;
+#endif
+}
+
+
+/*!
+ Returns a list of standard font sizes.
+
+ \sa smoothSizes(), pointSizes()
+*/
+QList<int> QFontDatabase::standardSizes()
+{
+ QList<int> ret;
+ static const unsigned short standard[] =
+ { 6, 7, 8, 9, 10, 11, 12, 14, 16, 18, 20, 22, 24, 26, 28, 36, 48, 72, 0 };
+ const unsigned short *sizes = standard;
+ while (*sizes) ret << *sizes++;
+ return ret;
+}
+
+
+/*!
+ Returns true if the font that has family \a family and style \a
+ style is italic; otherwise returns false.
+
+ \sa weight(), bold()
+*/
+bool QFontDatabase::italic(const QString &family, const QString &style) const
+{
+ QString familyName, foundryName;
+ parseFontName(family, foundryName, familyName);
+
+ QMutexLocker locker(fontDatabaseMutex());
+
+ QT_PREPEND_NAMESPACE(load)(familyName);
+
+ QtFontFoundry allStyles(foundryName);
+ QtFontFamily *f = d->family(familyName);
+ if (!f) return false;
+
+ for (int j = 0; j < f->count; j++) {
+ QtFontFoundry *foundry = f->foundries[j];
+ if (foundryName.isEmpty() || foundry->name.compare(foundryName, Qt::CaseInsensitive) == 0) {
+ for (int k = 0; k < foundry->count; k++)
+ allStyles.style(foundry->styles[k]->key, true);
+ }
+ }
+
+ QtFontStyle::Key styleKey(style);
+ QtFontStyle *s = allStyles.style(styleKey);
+ return s && s->key.style == QFont::StyleItalic;
+}
+
+
+/*!
+ Returns true if the font that has family \a family and style \a
+ style is bold; otherwise returns false.
+
+ \sa italic(), weight()
+*/
+bool QFontDatabase::bold(const QString &family,
+ const QString &style) const
+{
+ QString familyName, foundryName;
+ parseFontName(family, foundryName, familyName);
+
+ QMutexLocker locker(fontDatabaseMutex());
+
+ QT_PREPEND_NAMESPACE(load)(familyName);
+
+ QtFontFoundry allStyles(foundryName);
+ QtFontFamily *f = d->family(familyName);
+ if (!f) return false;
+
+ for (int j = 0; j < f->count; j++) {
+ QtFontFoundry *foundry = f->foundries[j];
+ if (foundryName.isEmpty() ||
+ foundry->name.compare(foundryName, Qt::CaseInsensitive) == 0) {
+ for (int k = 0; k < foundry->count; k++)
+ allStyles.style(foundry->styles[k]->key, true);
+ }
+ }
+
+ QtFontStyle::Key styleKey(style);
+ QtFontStyle *s = allStyles.style(styleKey);
+ return s && s->key.weight >= QFont::Bold;
+}
+
+
+/*!
+ Returns the weight of the font that has family \a family and style
+ \a style. If there is no such family and style combination,
+ returns -1.
+
+ \sa italic(), bold()
+*/
+int QFontDatabase::weight(const QString &family,
+ const QString &style) const
+{
+ QString familyName, foundryName;
+ parseFontName(family, foundryName, familyName);
+
+ QMutexLocker locker(fontDatabaseMutex());
+
+ QT_PREPEND_NAMESPACE(load)(familyName);
+
+ QtFontFoundry allStyles(foundryName);
+ QtFontFamily *f = d->family(familyName);
+ if (!f) return -1;
+
+ for (int j = 0; j < f->count; j++) {
+ QtFontFoundry *foundry = f->foundries[j];
+ if (foundryName.isEmpty() ||
+ foundry->name.compare(foundryName, Qt::CaseInsensitive) == 0) {
+ for (int k = 0; k < foundry->count; k++)
+ allStyles.style(foundry->styles[k]->key, true);
+ }
+ }
+
+ QtFontStyle::Key styleKey(style);
+ QtFontStyle *s = allStyles.style(styleKey);
+ return s ? s->key.weight : -1;
+}
+
+
+/*!
+ Returns the names the \a writingSystem (e.g. for displaying to the
+ user in a dialog).
+*/
+QString QFontDatabase::writingSystemName(WritingSystem writingSystem)
+{
+ const char *name = 0;
+ switch (writingSystem) {
+ case Any:
+ name = QT_TRANSLATE_NOOP("QFontDatabase", "Any");
+ break;
+ case Latin:
+ name = QT_TRANSLATE_NOOP("QFontDatabase", "Latin");
+ break;
+ case Greek:
+ name = QT_TRANSLATE_NOOP("QFontDatabase", "Greek");
+ break;
+ case Cyrillic:
+ name = QT_TRANSLATE_NOOP("QFontDatabase", "Cyrillic");
+ break;
+ case Armenian:
+ name = QT_TRANSLATE_NOOP("QFontDatabase", "Armenian");
+ break;
+ case Hebrew:
+ name = QT_TRANSLATE_NOOP("QFontDatabase", "Hebrew");
+ break;
+ case Arabic:
+ name = QT_TRANSLATE_NOOP("QFontDatabase", "Arabic");
+ break;
+ case Syriac:
+ name = QT_TRANSLATE_NOOP("QFontDatabase", "Syriac");
+ break;
+ case Thaana:
+ name = QT_TRANSLATE_NOOP("QFontDatabase", "Thaana");
+ break;
+ case Devanagari:
+ name = QT_TRANSLATE_NOOP("QFontDatabase", "Devanagari");
+ break;
+ case Bengali:
+ name = QT_TRANSLATE_NOOP("QFontDatabase", "Bengali");
+ break;
+ case Gurmukhi:
+ name = QT_TRANSLATE_NOOP("QFontDatabase", "Gurmukhi");
+ break;
+ case Gujarati:
+ name = QT_TRANSLATE_NOOP("QFontDatabase", "Gujarati");
+ break;
+ case Oriya:
+ name = QT_TRANSLATE_NOOP("QFontDatabase", "Oriya");
+ break;
+ case Tamil:
+ name = QT_TRANSLATE_NOOP("QFontDatabase", "Tamil");
+ break;
+ case Telugu:
+ name = QT_TRANSLATE_NOOP("QFontDatabase", "Telugu");
+ break;
+ case Kannada:
+ name = QT_TRANSLATE_NOOP("QFontDatabase", "Kannada");
+ break;
+ case Malayalam:
+ name = QT_TRANSLATE_NOOP("QFontDatabase", "Malayalam");
+ break;
+ case Sinhala:
+ name = QT_TRANSLATE_NOOP("QFontDatabase", "Sinhala");
+ break;
+ case Thai:
+ name = QT_TRANSLATE_NOOP("QFontDatabase", "Thai");
+ break;
+ case Lao:
+ name = QT_TRANSLATE_NOOP("QFontDatabase", "Lao");
+ break;
+ case Tibetan:
+ name = QT_TRANSLATE_NOOP("QFontDatabase", "Tibetan");
+ break;
+ case Myanmar:
+ name = QT_TRANSLATE_NOOP("QFontDatabase", "Myanmar");
+ break;
+ case Georgian:
+ name = QT_TRANSLATE_NOOP("QFontDatabase", "Georgian");
+ break;
+ case Khmer:
+ name = QT_TRANSLATE_NOOP("QFontDatabase", "Khmer");
+ break;
+ case SimplifiedChinese:
+ name = QT_TRANSLATE_NOOP("QFontDatabase", "Simplified Chinese");
+ break;
+ case TraditionalChinese:
+ name = QT_TRANSLATE_NOOP("QFontDatabase", "Traditional Chinese");
+ break;
+ case Japanese:
+ name = QT_TRANSLATE_NOOP("QFontDatabase", "Japanese");
+ break;
+ case Korean:
+ name = QT_TRANSLATE_NOOP("QFontDatabase", "Korean");
+ break;
+ case Vietnamese:
+ name = QT_TRANSLATE_NOOP("QFontDatabase", "Vietnamese");
+ break;
+ case Symbol:
+ name = QT_TRANSLATE_NOOP("QFontDatabase", "Symbol");
+ break;
+ case Ogham:
+ name = QT_TRANSLATE_NOOP("QFontDatabase", "Ogham");
+ break;
+ case Runic:
+ name = QT_TRANSLATE_NOOP("QFontDatabase", "Runic");
+ break;
+ default:
+ Q_ASSERT_X(false, "QFontDatabase::writingSystemName", "invalid 'writingSystem' parameter");
+ break;
+ }
+ return qApp ? qApp->translate("QFontDatabase", name) : QString::fromLatin1(name);
+}
+
+
+/*!
+ Returns a string with sample characters from \a writingSystem.
+*/
+QString QFontDatabase::writingSystemSample(WritingSystem writingSystem)
+{
+ QString sample;
+ switch (writingSystem) {
+ case Any:
+ case Symbol:
+ // show only ascii characters
+ sample += QLatin1String("AaBbzZ");
+ break;
+ case Latin:
+ // This is cheating... we only show latin-1 characters so that we don't
+ // end up loading lots of fonts - at least on X11...
+ sample = QLatin1String("Aa");
+ sample += QChar(0x00C3);
+ sample += QChar(0x00E1);
+ sample += QLatin1String("Zz");
+ break;
+ case Greek:
+ sample += QChar(0x0393);
+ sample += QChar(0x03B1);
+ sample += QChar(0x03A9);
+ sample += QChar(0x03C9);
+ break;
+ case Cyrillic:
+ sample += QChar(0x0414);
+ sample += QChar(0x0434);
+ sample += QChar(0x0436);
+ sample += QChar(0x044f);
+ break;
+ case Armenian:
+ sample += QChar(0x053f);
+ sample += QChar(0x054f);
+ sample += QChar(0x056f);
+ sample += QChar(0x057f);
+ break;
+ case Hebrew:
+ sample += QChar(0x05D0);
+ sample += QChar(0x05D1);
+ sample += QChar(0x05D2);
+ sample += QChar(0x05D3);
+ break;
+ case Arabic:
+ sample += QChar(0x0628);
+ sample += QChar(0x0629);
+ sample += QChar(0x062A);
+ sample += QChar(0x063A);
+ break;
+ case Syriac:
+ sample += QChar(0x0715);
+ sample += QChar(0x0725);
+ sample += QChar(0x0716);
+ sample += QChar(0x0726);
+ break;
+ case Thaana:
+ sample += QChar(0x0784);
+ sample += QChar(0x0794);
+ sample += QChar(0x078c);
+ sample += QChar(0x078d);
+ break;
+ case Devanagari:
+ sample += QChar(0x0905);
+ sample += QChar(0x0915);
+ sample += QChar(0x0925);
+ sample += QChar(0x0935);
+ break;
+ case Bengali:
+ sample += QChar(0x0986);
+ sample += QChar(0x0996);
+ sample += QChar(0x09a6);
+ sample += QChar(0x09b6);
+ break;
+ case Gurmukhi:
+ sample += QChar(0x0a05);
+ sample += QChar(0x0a15);
+ sample += QChar(0x0a25);
+ sample += QChar(0x0a35);
+ break;
+ case Gujarati:
+ sample += QChar(0x0a85);
+ sample += QChar(0x0a95);
+ sample += QChar(0x0aa5);
+ sample += QChar(0x0ab5);
+ break;
+ case Oriya:
+ sample += QChar(0x0b06);
+ sample += QChar(0x0b16);
+ sample += QChar(0x0b2b);
+ sample += QChar(0x0b36);
+ break;
+ case Tamil:
+ sample += QChar(0x0b89);
+ sample += QChar(0x0b99);
+ sample += QChar(0x0ba9);
+ sample += QChar(0x0bb9);
+ break;
+ case Telugu:
+ sample += QChar(0x0c05);
+ sample += QChar(0x0c15);
+ sample += QChar(0x0c25);
+ sample += QChar(0x0c35);
+ break;
+ case Kannada:
+ sample += QChar(0x0c85);
+ sample += QChar(0x0c95);
+ sample += QChar(0x0ca5);
+ sample += QChar(0x0cb5);
+ break;
+ case Malayalam:
+ sample += QChar(0x0d05);
+ sample += QChar(0x0d15);
+ sample += QChar(0x0d25);
+ sample += QChar(0x0d35);
+ break;
+ case Sinhala:
+ sample += QChar(0x0d90);
+ sample += QChar(0x0da0);
+ sample += QChar(0x0db0);
+ sample += QChar(0x0dc0);
+ break;
+ case Thai:
+ sample += QChar(0x0e02);
+ sample += QChar(0x0e12);
+ sample += QChar(0x0e22);
+ sample += QChar(0x0e32);
+ break;
+ case Lao:
+ sample += QChar(0x0e8d);
+ sample += QChar(0x0e9d);
+ sample += QChar(0x0ead);
+ sample += QChar(0x0ebd);
+ break;
+ case Tibetan:
+ sample += QChar(0x0f00);
+ sample += QChar(0x0f01);
+ sample += QChar(0x0f02);
+ sample += QChar(0x0f03);
+ break;
+ case Myanmar:
+ sample += QChar(0x1000);
+ sample += QChar(0x1001);
+ sample += QChar(0x1002);
+ sample += QChar(0x1003);
+ break;
+ case Georgian:
+ sample += QChar(0x10a0);
+ sample += QChar(0x10b0);
+ sample += QChar(0x10c0);
+ sample += QChar(0x10d0);
+ break;
+ case Khmer:
+ sample += QChar(0x1780);
+ sample += QChar(0x1790);
+ sample += QChar(0x17b0);
+ sample += QChar(0x17c0);
+ break;
+ case SimplifiedChinese:
+ sample += QChar(0x4e2d);
+ sample += QChar(0x6587);
+ sample += QChar(0x8303);
+ sample += QChar(0x4f8b);
+ break;
+ case TraditionalChinese:
+ sample += QChar(0x4e2d);
+ sample += QChar(0x6587);
+ sample += QChar(0x7bc4);
+ sample += QChar(0x4f8b);
+ break;
+ case Japanese:
+ sample += QChar(0x3050);
+ sample += QChar(0x3060);
+ sample += QChar(0x30b0);
+ sample += QChar(0x30c0);
+ break;
+ case Korean:
+ sample += QChar(0xac00);
+ sample += QChar(0xac11);
+ sample += QChar(0xac1a);
+ sample += QChar(0xac2f);
+ break;
+ case Vietnamese:
+ break;
+ case Ogham:
+ sample += QChar(0x1681);
+ sample += QChar(0x1682);
+ sample += QChar(0x1683);
+ sample += QChar(0x1684);
+ break;
+ case Runic:
+ sample += QChar(0x16a0);
+ sample += QChar(0x16a1);
+ sample += QChar(0x16a2);
+ sample += QChar(0x16a3);
+ break;
+ default:
+ break;
+ }
+ return sample;
+}
+
+
+void QFontDatabase::parseFontName(const QString &name, QString &foundry, QString &family)
+{
+ QT_PREPEND_NAMESPACE(parseFontName)(name, foundry, family);
+}
+
+void QFontDatabase::createDatabase()
+{ initializeDb(); }
+
+// used from qfontengine_ft.cpp
+QByteArray qt_fontdata_from_index(int index)
+{
+ QMutexLocker locker(fontDatabaseMutex());
+ return privateDb()->applicationFonts.value(index).data;
+}
+
+int QFontDatabasePrivate::addAppFont(const QByteArray &fontData, const QString &fileName)
+{
+ QFontDatabasePrivate::ApplicationFont font;
+ font.data = fontData;
+ font.fileName = fileName;
+
+ int i;
+ for (i = 0; i < applicationFonts.count(); ++i)
+ if (applicationFonts.at(i).families.isEmpty())
+ break;
+ if (i >= applicationFonts.count()) {
+ applicationFonts.append(ApplicationFont());
+ i = applicationFonts.count() - 1;
+ }
+
+ if (font.fileName.isEmpty() && !fontData.isEmpty())
+ font.fileName = QString::fromLatin1(":qmemoryfonts/") + QString::number(i);
+
+ registerFont(&font);
+ if (font.families.isEmpty())
+ return -1;
+
+ applicationFonts[i] = font;
+
+ invalidate();
+ return i;
+}
+
+bool QFontDatabasePrivate::isApplicationFont(const QString &fileName)
+{
+ for (int i = 0; i < applicationFonts.count(); ++i)
+ if (applicationFonts.at(i).fileName == fileName)
+ return true;
+ return false;
+}
+
+/*!
+ \since 4.2
+
+ Loads the font from the file specified by \a fileName and makes it available to
+ the application. An ID is returned that can be used to remove the font again
+ with removeApplicationFont() or to retrieve the list of family names contained
+ in the font.
+
+ The function returns -1 if the font could not be loaded.
+
+ Currently only TrueType fonts, TrueType font collections, and OpenType fonts are
+ supported.
+
+ \note Adding application fonts on Unix/X11 platforms without fontconfig is
+ currently not supported.
+
+ \sa addApplicationFontFromData(), applicationFontFamilies(), removeApplicationFont()
+*/
+int QFontDatabase::addApplicationFont(const QString &fileName)
+{
+ QByteArray data;
+ QFile f(fileName);
+ if (!(f.fileEngine()->fileFlags(QAbstractFileEngine::FlagsMask) & QAbstractFileEngine::LocalDiskFlag)) {
+ if (!f.open(QIODevice::ReadOnly))
+ return -1;
+ data = f.readAll();
+ }
+ QMutexLocker locker(fontDatabaseMutex());
+ return privateDb()->addAppFont(data, fileName);
+}
+
+/*!
+ \since 4.2
+
+ Loads the font from binary data specified by \a fontData and makes it available to
+ the application. An ID is returned that can be used to remove the font again
+ with removeApplicationFont() or to retrieve the list of family names contained
+ in the font.
+
+ The function returns -1 if the font could not be loaded.
+
+ Currently only TrueType fonts and TrueType font collections are supported.
+
+ \bold{Note:} Adding application fonts on Unix/X11 platforms without fontconfig is
+ currently not supported.
+
+ \sa addApplicationFont(), applicationFontFamilies(), removeApplicationFont()
+*/
+int QFontDatabase::addApplicationFontFromData(const QByteArray &fontData)
+{
+ QMutexLocker locker(fontDatabaseMutex());
+ return privateDb()->addAppFont(fontData, QString() /* fileName */);
+}
+
+/*!
+ \since 4.2
+
+ Returns a list of font families for the given application font identified by
+ \a id.
+
+ \sa addApplicationFont(), addApplicationFontFromData()
+*/
+QStringList QFontDatabase::applicationFontFamilies(int id)
+{
+ QMutexLocker locker(fontDatabaseMutex());
+ return privateDb()->applicationFonts.value(id).families;
+}
+
+/*!
+ \fn bool QFontDatabase::removeApplicationFont(int id)
+ \since 4.2
+
+ Removes the previously loaded application font identified by \a
+ id. Returns true if unloading of the font succeeded; otherwise
+ returns false.
+
+ \sa removeAllApplicationFonts(), addApplicationFont(),
+ addApplicationFontFromData()
+*/
+
+/*!
+ \fn bool QFontDatabase::removeAllApplicationFonts()
+ \since 4.2
+
+ Removes all application-local fonts previously added using addApplicationFont()
+ and addApplicationFontFromData().
+
+ Returns true if unloading of the fonts succeeded; otherwise
+ returns false.
+
+ \sa removeApplicationFont(), addApplicationFont(), addApplicationFontFromData()
+*/
+
+/*!
+ \fn bool QFontDatabase::supportsThreadedFontRendering()
+ \since 4.4
+
+ Returns true if font rendering is supported outside the GUI
+ thread, false otherwise. In other words, a return value of false
+ means that all QPainter::drawText() calls outside the GUI thread
+ will not produce readable output.
+
+ \sa threads.html#painting-in-threads
+*/
+
+
+QT_END_NAMESPACE
+
diff --git a/src/gui/text/qfontdatabase.h b/src/gui/text/qfontdatabase.h
new file mode 100644
index 0000000000..eadb6babdb
--- /dev/null
+++ b/src/gui/text/qfontdatabase.h
@@ -0,0 +1,176 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QFONTDATABASE_H
+#define QFONTDATABASE_H
+
+#include <QtGui/qwindowdefs.h>
+#include <QtCore/qstring.h>
+#include <QtGui/qfont.h>
+#ifdef QT3_SUPPORT
+#include <QtCore/qstringlist.h>
+#include <QtCore/qlist.h>
+#endif
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+class QStringList;
+template <class T> class QList;
+struct QFontDef;
+class QFontEngine;
+
+class QFontDatabasePrivate;
+
+class Q_GUI_EXPORT QFontDatabase
+{
+ Q_GADGET
+ Q_ENUMS(WritingSystem)
+public:
+ // do not re-order or delete entries from this enum without updating the
+ // QPF2 format and makeqpf!!
+ enum WritingSystem {
+ Any,
+
+ Latin,
+ Greek,
+ Cyrillic,
+ Armenian,
+ Hebrew,
+ Arabic,
+ Syriac,
+ Thaana,
+ Devanagari,
+ Bengali,
+ Gurmukhi,
+ Gujarati,
+ Oriya,
+ Tamil,
+ Telugu,
+ Kannada,
+ Malayalam,
+ Sinhala,
+ Thai,
+ Lao,
+ Tibetan,
+ Myanmar,
+ Georgian,
+ Khmer,
+ SimplifiedChinese,
+ TraditionalChinese,
+ Japanese,
+ Korean,
+ Vietnamese,
+
+ Symbol,
+ Other = Symbol,
+
+ Ogham,
+ Runic,
+
+ WritingSystemsCount
+ };
+
+ static QList<int> standardSizes();
+
+ QFontDatabase();
+
+ QList<WritingSystem> writingSystems() const;
+ QList<WritingSystem> writingSystems(const QString &family) const;
+
+ QStringList families(WritingSystem writingSystem = Any) const;
+ QStringList styles(const QString &family) const;
+ QList<int> pointSizes(const QString &family, const QString &style = QString());
+ QList<int> smoothSizes(const QString &family, const QString &style);
+ QString styleString(const QFont &font);
+ QString styleString(const QFontInfo &fontInfo);
+
+ QFont font(const QString &family, const QString &style, int pointSize) const;
+
+ bool isBitmapScalable(const QString &family, const QString &style = QString()) const;
+ bool isSmoothlyScalable(const QString &family, const QString &style = QString()) const;
+ bool isScalable(const QString &family, const QString &style = QString()) const;
+ bool isFixedPitch(const QString &family, const QString &style = QString()) const;
+
+ bool italic(const QString &family, const QString &style) const;
+ bool bold(const QString &family, const QString &style) const;
+ int weight(const QString &family, const QString &style) const;
+
+ static QString writingSystemName(WritingSystem writingSystem);
+ static QString writingSystemSample(WritingSystem writingSystem);
+
+ static int addApplicationFont(const QString &fileName);
+ static int addApplicationFontFromData(const QByteArray &fontData);
+ static QStringList applicationFontFamilies(int id);
+ static bool removeApplicationFont(int id);
+ static bool removeAllApplicationFonts();
+
+ static bool supportsThreadedFontRendering();
+
+private:
+ static void createDatabase();
+ static void parseFontName(const QString &name, QString &foundry, QString &family);
+#if defined(Q_WS_QWS)
+ static QFontEngine *findFont(int script, const QFontPrivate *fp, const QFontDef &request);
+#endif
+ static void load(const QFontPrivate *d, int script);
+#ifdef Q_WS_X11
+ static QFontEngine *loadXlfd(int screen, int script, const QFontDef &request, int force_encoding_id = -1);
+#endif
+
+ friend struct QFontDef;
+ friend class QFontPrivate;
+ friend class QFontDialog;
+ friend class QFontDialogPrivate;
+ friend class QFontEngineMultiXLFD;
+ friend class QFontEngineMultiQWS;
+
+ QFontDatabasePrivate *d;
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QFONTDATABASE_H
diff --git a/src/gui/text/qfontdatabase_mac.cpp b/src/gui/text/qfontdatabase_mac.cpp
new file mode 100644
index 0000000000..80ddbd5c60
--- /dev/null
+++ b/src/gui/text/qfontdatabase_mac.cpp
@@ -0,0 +1,509 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <private/qt_mac_p.h>
+#include "qfontengine_p.h"
+#include <qfile.h>
+#include <qabstractfileengine.h>
+#include <stdlib.h>
+#include <qendian.h>
+
+QT_BEGIN_NAMESPACE
+
+int qt_mac_pixelsize(const QFontDef &def, int dpi); //qfont_mac.cpp
+int qt_mac_pointsize(const QFontDef &def, int dpi); //qfont_mac.cpp
+
+static void initWritingSystems(QtFontFamily *family, ATSFontRef atsFont)
+{
+ ByteCount length = 0;
+ if (ATSFontGetTable(atsFont, MAKE_TAG('O', 'S', '/', '2'), 0, 0, 0, &length) != noErr)
+ return;
+ QVarLengthArray<uchar> os2Table(length);
+ if (length < 86
+ || ATSFontGetTable(atsFont, MAKE_TAG('O', 'S', '/', '2'), 0, length, os2Table.data(), &length) != noErr)
+ return;
+
+ // See also qfontdatabase_win.cpp, offsets taken from OS/2 table in the TrueType spec
+ quint32 unicodeRange[4] = {
+ qFromBigEndian<quint32>(os2Table.data() + 42),
+ qFromBigEndian<quint32>(os2Table.data() + 46),
+ qFromBigEndian<quint32>(os2Table.data() + 50),
+ qFromBigEndian<quint32>(os2Table.data() + 54)
+ };
+ quint32 codePageRange[2] = { qFromBigEndian<quint32>(os2Table.data() + 78), qFromBigEndian<quint32>(os2Table.data() + 82) };
+ QList<QFontDatabase::WritingSystem> systems = determineWritingSystemsFromTrueTypeBits(unicodeRange, codePageRange);
+#if 0
+ QCFString name;
+ ATSFontGetName(atsFont, kATSOptionFlagsDefault, &name);
+ qDebug() << systems.count() << "writing systems for" << QString(name);
+qDebug() << "first char" << hex << unicodeRange[0];
+ for (int i = 0; i < systems.count(); ++i)
+ qDebug() << QFontDatabase::writingSystemName(systems.at(i));
+#endif
+ for (int i = 0; i < systems.count(); ++i)
+ family->writingSystems[systems.at(i)] = QtFontFamily::Supported;
+}
+
+static void initializeDb()
+{
+ QFontDatabasePrivate *db = privateDb();
+ if(!db || db->count)
+ return;
+
+#if defined(QT_MAC_USE_COCOA) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5
+if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_5) {
+ QCFType<CTFontCollectionRef> collection = CTFontCollectionCreateFromAvailableFonts(0);
+ if(!collection)
+ return;
+ QCFType<CFArrayRef> fonts = CTFontCollectionCreateMatchingFontDescriptors(collection);
+ if(!fonts)
+ return;
+ QString foundry_name = "CoreText";
+ const int numFonts = CFArrayGetCount(fonts);
+ for(int i = 0; i < numFonts; ++i) {
+ CTFontDescriptorRef font = (CTFontDescriptorRef)CFArrayGetValueAtIndex(fonts, i);
+
+ QCFString family_name = (CFStringRef)CTFontDescriptorCopyAttribute(font, kCTFontFamilyNameAttribute);
+ QtFontFamily *family = db->family(family_name, true);
+ for(int ws = 1; ws < QFontDatabase::WritingSystemsCount; ++ws)
+ family->writingSystems[ws] = QtFontFamily::Supported;
+ QtFontFoundry *foundry = family->foundry(foundry_name, true);
+
+ QtFontStyle::Key styleKey;
+ if(QCFType<CFDictionaryRef> styles = (CFDictionaryRef)CTFontDescriptorCopyAttribute(font, kCTFontTraitsAttribute)) {
+ if(CFNumberRef weight = (CFNumberRef)CFDictionaryGetValue(styles, kCTFontWeightTrait)) {
+ Q_ASSERT(CFNumberIsFloatType(weight));
+ double d;
+ if(CFNumberGetValue(weight, kCFNumberDoubleType, &d)) {
+ //qDebug() << "BOLD" << (QString)family_name << d;
+ styleKey.weight = (d > 0.0) ? QFont::Bold : QFont::Normal;
+ }
+ }
+ if(CFNumberRef italic = (CFNumberRef)CFDictionaryGetValue(styles, kCTFontSlantTrait)) {
+ Q_ASSERT(CFNumberIsFloatType(italic));
+ double d;
+ if(CFNumberGetValue(italic, kCFNumberDoubleType, &d)) {
+ //qDebug() << "ITALIC" << (QString)family_name << d;
+ if (d > 0.0)
+ styleKey.style = QFont::StyleItalic;
+ }
+ }
+ }
+
+ QtFontStyle *style = foundry->style(styleKey, true);
+ style->smoothScalable = true;
+ if(QCFType<CFNumberRef> size = (CFNumberRef)CTFontDescriptorCopyAttribute(font, kCTFontSizeAttribute)) {
+ //qDebug() << "WHEE";
+ int pixel_size=0;
+ if(CFNumberIsFloatType(size)) {
+ double d;
+ CFNumberGetValue(size, kCFNumberDoubleType, &d);
+ pixel_size = d;
+ } else {
+ CFNumberGetValue(size, kCFNumberIntType, &pixel_size);
+ }
+ //qDebug() << "SIZE" << (QString)family_name << pixel_size;
+ if(pixel_size)
+ style->pixelSize(pixel_size, true);
+ } else {
+ //qDebug() << "WTF?";
+ }
+ }
+} else
+#endif
+ {
+#ifndef Q_WS_MAC64
+ FMFontIterator it;
+ if (!FMCreateFontIterator(0, 0, kFMUseGlobalScopeOption, &it)) {
+ while (true) {
+ FMFont fmFont;
+ if (FMGetNextFont(&it, &fmFont) != noErr)
+ break;
+
+ FMFontFamily fmFamily;
+ FMFontStyle fmStyle;
+ QString familyName;
+
+ QtFontStyle::Key styleKey;
+
+ ATSFontRef atsFont = FMGetATSFontRefFromFont(fmFont);
+
+ if (!FMGetFontFamilyInstanceFromFont(fmFont, &fmFamily, &fmStyle)) {
+ { //sanity check the font, and see if we can use it at all! --Sam
+ ATSUFontID fontID;
+ if(ATSUFONDtoFontID(fmFamily, 0, &fontID) != noErr)
+ continue;
+ }
+
+ if (fmStyle & ::italic)
+ styleKey.style = QFont::StyleItalic;
+ if (fmStyle & ::bold)
+ styleKey.weight = QFont::Bold;
+
+ ATSFontFamilyRef familyRef = FMGetATSFontFamilyRefFromFontFamily(fmFamily);
+ QCFString cfFamilyName;;
+ ATSFontFamilyGetName(familyRef, kATSOptionFlagsDefault, &cfFamilyName);
+ familyName = cfFamilyName;
+ } else {
+ QCFString cfFontName;
+ ATSFontGetName(atsFont, kATSOptionFlagsDefault, &cfFontName);
+ familyName = cfFontName;
+ quint16 macStyle = 0;
+ {
+ uchar data[4];
+ ByteCount len = 4;
+ if (ATSFontGetTable(atsFont, MAKE_TAG('h', 'e', 'a', 'd'), 44, 4, &data, &len) == noErr)
+ macStyle = qFromBigEndian<quint16>(data);
+ }
+ if (macStyle & 1)
+ styleKey.weight = QFont::Bold;
+ if (macStyle & 2)
+ styleKey.style = QFont::StyleItalic;
+ }
+
+ QtFontFamily *family = db->family(familyName, true);
+ QtFontFoundry *foundry = family->foundry(QString(), true);
+ QtFontStyle *style = foundry->style(styleKey, true);
+ style->pixelSize(0, true);
+ style->smoothScalable = true;
+
+ initWritingSystems(family, atsFont);
+ }
+ FMDisposeFontIterator(&it);
+ }
+#endif
+ }
+
+}
+
+static inline void load(const QString & = QString(), int = -1)
+{
+ initializeDb();
+}
+
+static const char *styleHint(const QFontDef &request)
+{
+ const char *stylehint = 0;
+ switch (request.styleHint) {
+ case QFont::SansSerif:
+ stylehint = "Arial";
+ break;
+ case QFont::Serif:
+ stylehint = "Times New Roman";
+ break;
+ case QFont::TypeWriter:
+ stylehint = "Courier New";
+ break;
+ default:
+ if (request.fixedPitch)
+ stylehint = "Courier New";
+ break;
+ }
+ return stylehint;
+}
+
+void QFontDatabase::load(const QFontPrivate *d, int script)
+{
+ // sanity checks
+ if(!qApp)
+ qWarning("QFont: Must construct a QApplication before a QFont");
+
+ Q_ASSERT(script >= 0 && script < QUnicodeTables::ScriptCount);
+ Q_UNUSED(script);
+
+ QFontDef req = d->request;
+ req.pixelSize = qt_mac_pixelsize(req, d->dpi);
+
+ // set the point size to 0 to get better caching
+ req.pointSize = 0;
+ QFontCache::Key key = QFontCache::Key(req, QUnicodeTables::Common, d->screen);
+
+ if(!(d->engineData = QFontCache::instance()->findEngineData(key))) {
+ d->engineData = new QFontEngineData;
+ QFontCache::instance()->insertEngineData(key, d->engineData);
+ } else {
+ d->engineData->ref.ref();
+ }
+ if(d->engineData->engine) // already loaded
+ return;
+
+ // set it to the actual pointsize, so QFontInfo will do the right thing
+ req.pointSize = qRound(qt_mac_pointsize(d->request, d->dpi));
+
+ QFontEngine *e = QFontCache::instance()->findEngine(key);
+ if(!e && qt_enable_test_font && req.family == QLatin1String("__Qt__Box__Engine__")) {
+ e = new QTestFontEngine(req.pixelSize);
+ e->fontDef = req;
+ }
+
+ if(e) {
+ e->ref.ref();
+ d->engineData->engine = e;
+ return; // the font info and fontdef should already be filled
+ }
+
+ //find the font
+ QStringList family_list = req.family.split(QLatin1Char(','));
+ // append the substitute list for each family in family_list
+ {
+ QStringList subs_list;
+ for(QStringList::ConstIterator it = family_list.constBegin(); it != family_list.constEnd(); ++it)
+ subs_list += QFont::substitutes(*it);
+ family_list += subs_list;
+ }
+
+ const char *stylehint = styleHint(req);
+ if (stylehint)
+ family_list << QLatin1String(stylehint);
+
+ // add QFont::defaultFamily() to the list, for compatibility with
+ // previous versions
+ family_list << QApplication::font().defaultFamily();
+
+ ATSFontFamilyRef familyRef = 0;
+ ATSFontRef fontRef = 0;
+
+ QMutexLocker locker(fontDatabaseMutex());
+ QFontDatabasePrivate *db = privateDb();
+ if (!db->count)
+ initializeDb();
+ for(int i = 0; i < family_list.size(); ++i) {
+ for (int k = 0; k < db->count; ++k) {
+ if (db->families[k]->name.compare(family_list.at(i), Qt::CaseInsensitive) == 0) {
+ QByteArray family_name = db->families[k]->name.toUtf8();
+ familyRef = ATSFontFamilyFindFromName(QCFString(db->families[k]->name), kATSOptionFlagsDefault);
+ if (familyRef) {
+ fontRef = ATSFontFindFromName(QCFString(db->families[k]->name), kATSOptionFlagsDefault);
+ goto FamilyFound;
+ }
+ }
+ }
+ }
+FamilyFound:
+ //fill in the engine's font definition
+ QFontDef fontDef = d->request; //copy..
+ if(fontDef.pointSize < 0)
+ fontDef.pointSize = qt_mac_pointsize(fontDef, d->dpi);
+ else
+ fontDef.pixelSize = qt_mac_pixelsize(fontDef, d->dpi);
+#if 0
+ ItemCount name_count;
+ if(ATSUCountFontNames(fontID, &name_count) == noErr && name_count) {
+ ItemCount actualName_size;
+ if(ATSUGetIndFontName(fontID, 0, 0, 0, &actualName_size, 0, 0, 0, 0) == noErr && actualName_size) {
+ QByteArray actualName(actualName_size);
+ if(ATSUGetIndFontName(fontID, 0, actualName_size, actualName.data(), &actualName_size, 0, 0, 0, 0) == noErr && actualName_size)
+ fontDef.family = QString::fromUtf8(actualName);
+ }
+ }
+#else
+ {
+ QCFString actualName;
+ if(ATSFontFamilyGetName(familyRef, kATSOptionFlagsDefault, &actualName) == noErr)
+ fontDef.family = actualName;
+ }
+#endif
+
+#ifdef QT_MAC_USE_COCOA
+ QFontEngine *engine = new QCoreTextFontEngineMulti(familyRef, fontRef, fontDef, d->kerning);
+#elif 1
+ QFontEngine *engine = new QFontEngineMacMulti(familyRef, fontRef, fontDef, d->kerning);
+#else
+ ATSFontFamilyRef atsFamily = familyRef;
+ ATSFontFamilyRef atsFontRef = fontRef;
+
+ FMFont fontID;
+ FMFontFamily fmFamily;
+ FMFontStyle fntStyle = 0;
+ fmFamily = FMGetFontFamilyFromATSFontFamilyRef(atsFamily);
+ if (fmFamily == kInvalidFontFamily) {
+ // Use the ATSFont then...
+ fontID = FMGetFontFromATSFontRef(atsFontRef);
+ } else {
+ if (fontDef.weight >= QFont::Bold)
+ fntStyle |= ::bold;
+ if (fontDef.style != QFont::StyleNormal)
+ fntStyle |= ::italic;
+
+ FMFontStyle intrinsicStyle;
+ FMFont fnt = 0;
+ if (FMGetFontFromFontFamilyInstance(fmFamily, fntStyle, &fnt, &intrinsicStyle) == noErr)
+ fontID = FMGetATSFontRefFromFont(fnt);
+ }
+
+ OSStatus status;
+
+ const int maxAttributeCount = 5;
+ ATSUAttributeTag tags[maxAttributeCount + 1];
+ ByteCount sizes[maxAttributeCount + 1];
+ ATSUAttributeValuePtr values[maxAttributeCount + 1];
+ int attributeCount = 0;
+
+ Fixed size = FixRatio(fontDef.pixelSize, 1);
+ tags[attributeCount] = kATSUSizeTag;
+ sizes[attributeCount] = sizeof(size);
+ values[attributeCount] = &size;
+ ++attributeCount;
+
+ tags[attributeCount] = kATSUFontTag;
+ sizes[attributeCount] = sizeof(fontID);
+ values[attributeCount] = &fontID;
+ ++attributeCount;
+
+ CGAffineTransform transform = CGAffineTransformIdentity;
+ if (fontDef.stretch != 100) {
+ transform = CGAffineTransformMakeScale(float(fontDef.stretch) / float(100), 1);
+ tags[attributeCount] = kATSUFontMatrixTag;
+ sizes[attributeCount] = sizeof(transform);
+ values[attributeCount] = &transform;
+ ++attributeCount;
+ }
+
+ ATSUStyle style;
+ status = ATSUCreateStyle(&style);
+ Q_ASSERT(status == noErr);
+
+ Q_ASSERT(attributeCount < maxAttributeCount + 1);
+ status = ATSUSetAttributes(style, attributeCount, tags, sizes, values);
+ Q_ASSERT(status == noErr);
+
+ QFontEngine *engine = new QFontEngineMac(style, fontID, fontDef, /*multiEngine*/ 0);
+ ATSUDisposeStyle(style);
+#endif
+ d->engineData->engine = engine;
+ engine->ref.ref(); //a ref for the engineData->engine
+ QFontCache::instance()->insertEngine(key, engine);
+}
+
+static void registerFont(QFontDatabasePrivate::ApplicationFont *fnt)
+{
+ ATSFontContainerRef handle;
+ OSStatus e = noErr;
+
+ if(fnt->data.isEmpty()) {
+#if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5)
+ if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_5) {
+ extern OSErr qt_mac_create_fsref(const QString &, FSRef *); // qglobal.cpp
+ FSRef ref;
+ if(qt_mac_create_fsref(fnt->fileName, &ref) != noErr)
+ return;
+
+ ATSFontActivateFromFileReference(&ref, kATSFontContextLocal, kATSFontFormatUnspecified, 0, kATSOptionFlagsDefault, &handle);
+ } else
+#endif
+ {
+#ifndef Q_WS_MAC64
+ extern Q_CORE_EXPORT OSErr qt_mac_create_fsspec(const QString &, FSSpec *); // global.cpp
+ FSSpec spec;
+ if(qt_mac_create_fsspec(fnt->fileName, &spec) != noErr)
+ return;
+
+ e = ATSFontActivateFromFileSpecification(&spec, kATSFontContextLocal, kATSFontFormatUnspecified,
+ 0, kATSOptionFlagsDefault, &handle);
+#endif
+ }
+ } else {
+ e = ATSFontActivateFromMemory((void *)fnt->data.constData(), fnt->data.size(), kATSFontContextLocal,
+ kATSFontFormatUnspecified, 0, kATSOptionFlagsDefault, &handle);
+
+ fnt->data = QByteArray();
+ }
+
+ if(e != noErr)
+ return;
+
+ ItemCount fontCount = 0;
+ e = ATSFontFindFromContainer(handle, kATSOptionFlagsDefault, 0, 0, &fontCount);
+ if(e != noErr)
+ return;
+
+ QVarLengthArray<ATSFontRef> containedFonts(fontCount);
+ e = ATSFontFindFromContainer(handle, kATSOptionFlagsDefault, fontCount, containedFonts.data(), &fontCount);
+ if(e != noErr)
+ return;
+
+ fnt->families.clear();
+ for(int i = 0; i < containedFonts.size(); ++i) {
+ QCFString family;
+ ATSFontGetName(containedFonts[i], kATSOptionFlagsDefault, &family);
+ fnt->families.append(family);
+ }
+
+ fnt->handle = handle;
+}
+
+bool QFontDatabase::removeApplicationFont(int handle)
+{
+ QMutexLocker locker(fontDatabaseMutex());
+
+ QFontDatabasePrivate *db = privateDb();
+ if(handle < 0 || handle >= db->applicationFonts.count())
+ return false;
+
+ OSStatus e = ATSFontDeactivate(db->applicationFonts.at(handle).handle,
+ /*iRefCon=*/0, kATSOptionFlagsDefault);
+ if(e != noErr)
+ return false;
+
+ db->applicationFonts[handle] = QFontDatabasePrivate::ApplicationFont();
+
+ db->invalidate();
+ return true;
+}
+
+bool QFontDatabase::removeAllApplicationFonts()
+{
+ QMutexLocker locker(fontDatabaseMutex());
+
+ QFontDatabasePrivate *db = privateDb();
+ for(int i = 0; i < db->applicationFonts.count(); ++i) {
+ if(!removeApplicationFont(i))
+ return false;
+ }
+ return true;
+}
+
+bool QFontDatabase::supportsThreadedFontRendering()
+{
+ return true;
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/text/qfontdatabase_qws.cpp b/src/gui/text/qfontdatabase_qws.cpp
new file mode 100644
index 0000000000..eb8a0cf5c2
--- /dev/null
+++ b/src/gui/text/qfontdatabase_qws.cpp
@@ -0,0 +1,1062 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qdir.h"
+#include "qscreen_qws.h" //so we can check for rotation
+#include "qwindowsystem_qws.h"
+#include "qlibraryinfo.h"
+#include "qabstractfileengine.h"
+#include <QtCore/qsettings.h>
+#if !defined(QT_NO_FREETYPE)
+#include "qfontengine_ft_p.h"
+
+#include <ft2build.h>
+#include FT_FREETYPE_H
+#include FT_TRUETYPE_TABLES_H
+
+#endif
+#include "qfontengine_qpf_p.h"
+#include "private/qfactoryloader_p.h"
+#include "qabstractfontengine_qws.h"
+#include "qabstractfontengine_p.h"
+#include <qdatetime.h>
+
+// for mmap
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#ifdef QT_FONTS_ARE_RESOURCES
+#include <qresource.h>
+#endif
+
+QT_BEGIN_NAMESPACE
+
+#ifndef QT_NO_LIBRARY
+Q_GLOBAL_STATIC_WITH_ARGS(QFactoryLoader, loader,
+ (QFontEngineFactoryInterface_iid, QLatin1String("/fontengines"), Qt::CaseInsensitive))
+#endif
+
+const quint8 DatabaseVersion = 4;
+
+void QFontDatabasePrivate::addFont(const QString &familyname, const char *foundryname, int weight, bool italic, int pixelSize,
+ const QByteArray &file, int fileIndex, bool antialiased,
+ const QList<QFontDatabase::WritingSystem> &writingSystems)
+{
+// qDebug() << "Adding font" << familyname << weight << italic << pixelSize << file << fileIndex << antialiased;
+ QtFontStyle::Key styleKey;
+ styleKey.style = italic ? QFont::StyleItalic : QFont::StyleNormal;
+ styleKey.weight = weight;
+ styleKey.stretch = 100;
+ QtFontFamily *f = family(familyname, true);
+
+ if (writingSystems.isEmpty()) {
+ for (int ws = 1; ws < QFontDatabase::WritingSystemsCount; ++ws) {
+ f->writingSystems[ws] = QtFontFamily::Supported;
+ }
+ f->bogusWritingSystems = true;
+ } else {
+ for (int i = 0; i < writingSystems.count(); ++i) {
+ f->writingSystems[writingSystems.at(i)] = QtFontFamily::Supported;
+ }
+ }
+
+ QtFontFoundry *foundry = f->foundry(QString::fromLatin1(foundryname), true);
+ QtFontStyle *style = foundry->style(styleKey, true);
+ style->smoothScalable = (pixelSize == 0);
+ style->antialiased = antialiased;
+ QtFontSize *size = style->pixelSize(pixelSize?pixelSize:SMOOTH_SCALABLE, true);
+ size->fileName = file;
+ size->fileIndex = fileIndex;
+
+ if (stream) {
+ *stream << familyname << foundry->name << weight << quint8(italic) << pixelSize
+ << file << fileIndex << quint8(antialiased);
+ *stream << quint8(writingSystems.count());
+ for (int i = 0; i < writingSystems.count(); ++i)
+ *stream << quint8(writingSystems.at(i));
+ }
+}
+
+#ifndef QT_NO_QWS_QPF2
+void QFontDatabasePrivate::addQPF2File(const QByteArray &file)
+{
+#ifndef QT_FONTS_ARE_RESOURCES
+ struct stat st;
+ if (stat(file.constData(), &st))
+ return;
+ int f = ::open(file, O_RDONLY);
+ if (f < 0)
+ return;
+ const uchar *data = (const uchar *)mmap(0, st.st_size, PROT_READ, MAP_SHARED, f, 0);
+ const int dataSize = st.st_size;
+#else
+ QResource res(QLatin1String(file.constData()));
+ const uchar *data = res.data();
+ const int dataSize = res.size();
+ //qDebug() << "addQPF2File" << file << data;
+#endif
+ if (data && data != (const uchar *)MAP_FAILED) {
+ if (QFontEngineQPF::verifyHeader(data, dataSize)) {
+ QString fontName = QFontEngineQPF::extractHeaderField(data, QFontEngineQPF::Tag_FontName).toString();
+ int pixelSize = QFontEngineQPF::extractHeaderField(data, QFontEngineQPF::Tag_PixelSize).toInt();
+ QVariant weight = QFontEngineQPF::extractHeaderField(data, QFontEngineQPF::Tag_Weight);
+ QVariant style = QFontEngineQPF::extractHeaderField(data, QFontEngineQPF::Tag_Style);
+ QByteArray writingSystemBits = QFontEngineQPF::extractHeaderField(data, QFontEngineQPF::Tag_WritingSystems).toByteArray();
+
+ if (!fontName.isEmpty() && pixelSize) {
+ int fontWeight = 50;
+ if (weight.type() == QVariant::Int || weight.type() == QVariant::UInt)
+ fontWeight = weight.toInt();
+
+ bool italic = static_cast<QFont::Style>(style.toInt()) & QFont::StyleItalic;
+
+ QList<QFontDatabase::WritingSystem> writingSystems;
+ for (int i = 0; i < writingSystemBits.count(); ++i) {
+ uchar currentByte = writingSystemBits.at(i);
+ for (int j = 0; j < 8; ++j) {
+ if (currentByte & 1)
+ writingSystems << QFontDatabase::WritingSystem(i * 8 + j);
+ currentByte >>= 1;
+ }
+ }
+
+ addFont(fontName, /*foundry*/ "prerendered", fontWeight, italic,
+ pixelSize, file, /*fileIndex*/ 0,
+ /*antialiased*/ true, writingSystems);
+ }
+ } else {
+ qDebug() << "header verification of QPF2 font" << file << "failed. maybe it is corrupt?";
+ }
+#ifndef QT_FONTS_ARE_RESOURCES
+ munmap((void *)data, st.st_size);
+#endif
+ }
+#ifndef QT_FONTS_ARE_RESOURCES
+ ::close(f);
+#endif
+}
+#endif
+
+#ifndef QT_NO_FREETYPE
+QStringList QFontDatabasePrivate::addTTFile(const QByteArray &file, const QByteArray &fontData)
+{
+ QStringList families;
+ extern FT_Library qt_getFreetype();
+ FT_Library library = qt_getFreetype();
+
+ int index = 0;
+ int numFaces = 0;
+ do {
+ FT_Face face;
+ FT_Error error;
+ if (!fontData.isEmpty()) {
+ error = FT_New_Memory_Face(library, (const FT_Byte *)fontData.constData(), fontData.size(), index, &face);
+ } else {
+ error = FT_New_Face(library, file, index, &face);
+ }
+ if (error != FT_Err_Ok) {
+ qDebug() << "FT_New_Face failed with index" << index << ":" << hex << error;
+ break;
+ }
+ numFaces = face->num_faces;
+
+ int weight = QFont::Normal;
+ bool italic = face->style_flags & FT_STYLE_FLAG_ITALIC;
+
+ if (face->style_flags & FT_STYLE_FLAG_BOLD)
+ weight = QFont::Bold;
+
+ QList<QFontDatabase::WritingSystem> writingSystems;
+ // detect symbol fonts
+ for (int i = 0; i < face->num_charmaps; ++i) {
+ FT_CharMap cm = face->charmaps[i];
+ if (cm->encoding == ft_encoding_adobe_custom
+ || cm->encoding == ft_encoding_symbol) {
+ writingSystems.append(QFontDatabase::Symbol);
+ break;
+ }
+ }
+ if (writingSystems.isEmpty()) {
+ TT_OS2 *os2 = (TT_OS2 *)FT_Get_Sfnt_Table(face, ft_sfnt_os2);
+ if (os2) {
+ quint32 unicodeRange[4] = {
+ os2->ulUnicodeRange1, os2->ulUnicodeRange2, os2->ulUnicodeRange3, os2->ulUnicodeRange4
+ };
+ quint32 codePageRange[2] = {
+ os2->ulCodePageRange1, os2->ulCodePageRange2
+ };
+
+ writingSystems = determineWritingSystemsFromTrueTypeBits(unicodeRange, codePageRange);
+ //for (int i = 0; i < writingSystems.count(); ++i)
+ // qDebug() << QFontDatabase::writingSystemName(writingSystems.at(i));
+ }
+ }
+
+ QString family = QString::fromAscii(face->family_name);
+ families.append(family);
+ addFont(family, /*foundry*/ "", weight, italic,
+ /*pixelsize*/ 0, file, index, /*antialias*/ true, writingSystems);
+
+ FT_Done_Face(face);
+ ++index;
+ } while (index < numFaces);
+ return families;
+}
+#endif
+
+static void registerFont(QFontDatabasePrivate::ApplicationFont *fnt);
+
+extern QString qws_fontCacheDir();
+
+#ifndef QT_FONTS_ARE_RESOURCES
+bool QFontDatabasePrivate::loadFromCache(const QString &fontPath)
+{
+ const bool weAreTheServer = QWSServer::instance();
+
+ QString fontDirFile = fontPath + QLatin1String("/fontdir");
+
+ QFile binaryDb(qws_fontCacheDir() + QLatin1String("/fontdb"));
+
+ if (weAreTheServer) {
+ QDateTime dbTimeStamp = QFileInfo(binaryDb.fileName()).lastModified();
+
+ QDateTime fontPathTimeStamp = QFileInfo(fontPath).lastModified();
+ if (dbTimeStamp < fontPathTimeStamp)
+ return false; // let the caller create the cache
+
+ if (QFile::exists(fontDirFile)) {
+ QDateTime fontDirTimeStamp = QFileInfo(fontDirFile).lastModified();
+ if (dbTimeStamp < fontDirTimeStamp)
+ return false;
+ }
+ }
+
+ if (!binaryDb.open(QIODevice::ReadOnly)) {
+ if (weAreTheServer)
+ return false; // let the caller create the cache
+ qFatal("QFontDatabase::loadFromCache: Could not open font database cache!");
+ }
+
+ QDataStream stream(&binaryDb);
+ quint8 version = 0;
+ quint8 dataStreamVersion = 0;
+ stream >> version >> dataStreamVersion;
+ if (version != DatabaseVersion || dataStreamVersion != stream.version()) {
+ if (weAreTheServer)
+ return false; // let the caller create the cache
+ qFatal("QFontDatabase::loadFromCache: Wrong version of the font database cache detected. Found %d/%d expected %d/%d",
+ version, dataStreamVersion, DatabaseVersion, stream.version());
+ }
+
+ QString originalFontPath;
+ stream >> originalFontPath;
+ if (originalFontPath != fontPath) {
+ if (weAreTheServer)
+ return false; // let the caller create the cache
+ qFatal("QFontDatabase::loadFromCache: Font path doesn't match. Found %s in database, expected %s", qPrintable(originalFontPath), qPrintable(fontPath));
+ }
+
+ QString familyname;
+ stream >> familyname;
+ //qDebug() << "populating database from" << binaryDb.fileName();
+ while (!familyname.isEmpty() && !stream.atEnd()) {
+ QString foundryname;
+ int weight;
+ quint8 italic;
+ int pixelSize;
+ QByteArray file;
+ int fileIndex;
+ quint8 antialiased;
+ quint8 writingSystemCount;
+
+ QList<QFontDatabase::WritingSystem> writingSystems;
+
+ stream >> foundryname >> weight >> italic >> pixelSize
+ >> file >> fileIndex >> antialiased >> writingSystemCount;
+
+ for (quint8 i = 0; i < writingSystemCount; ++i) {
+ quint8 ws;
+ stream >> ws;
+ writingSystems.append(QFontDatabase::WritingSystem(ws));
+ }
+
+ addFont(familyname, foundryname.toLatin1().constData(), weight, italic, pixelSize, file, fileIndex, antialiased,
+ writingSystems);
+
+ stream >> familyname;
+ }
+
+ stream >> fallbackFamilies;
+ //qDebug() << "fallback families from cache:" << fallbackFamilies;
+ return true;
+}
+#endif // QT_FONTS_ARE_RESOURCES
+
+/*!
+ \internal
+*/
+
+static QString qwsFontPath()
+{
+ QString fontpath = QString::fromLocal8Bit(qgetenv("QT_QWS_FONTDIR"));
+ if (fontpath.isEmpty()) {
+#ifdef QT_FONTS_ARE_RESOURCES
+ fontpath = QLatin1String(":/qt/fonts");
+#else
+#ifndef QT_NO_SETTINGS
+ fontpath = QLibraryInfo::location(QLibraryInfo::LibrariesPath);
+ fontpath += QLatin1String("/fonts");
+#else
+ fontpath = QLatin1String("/lib/fonts");
+#endif
+#endif //QT_FONTS_ARE_RESOURCES
+ }
+
+ return fontpath;
+}
+
+#ifdef QFONTDATABASE_DEBUG
+class FriendlyResource : public QResource
+{
+public:
+ bool isDir () const { return QResource::isDir(); }
+ bool isFile () const { return QResource::isFile(); }
+ QStringList children () const { return QResource::children(); }
+};
+#endif
+/*!
+ \internal
+*/
+static void initializeDb()
+{
+ QFontDatabasePrivate *db = privateDb();
+ if (!db || db->count)
+ return;
+
+ QString fontpath = qwsFontPath();
+#ifndef QT_FONTS_ARE_RESOURCES
+ QString fontDirFile = fontpath + QLatin1String("/fontdir");
+
+ if(!QFile::exists(fontpath)) {
+ qFatal("QFontDatabase: Cannot find font directory %s - is Qt installed correctly?",
+ fontpath.toLocal8Bit().constData());
+ }
+
+ const bool loaded = db->loadFromCache(fontpath);
+
+ if (db->reregisterAppFonts) {
+ db->reregisterAppFonts = false;
+ for (int i = 0; i < db->applicationFonts.count(); ++i)
+ if (!db->applicationFonts.at(i).families.isEmpty()) {
+ registerFont(&db->applicationFonts[i]);
+ }
+ }
+
+ if (loaded)
+ return;
+
+ QString dbFileName = qws_fontCacheDir() + QLatin1String("/fontdb");
+
+ QFile binaryDb(dbFileName + QLatin1String(".tmp"));
+ binaryDb.open(QIODevice::WriteOnly | QIODevice::Truncate);
+ db->stream = new QDataStream(&binaryDb);
+ *db->stream << DatabaseVersion << quint8(db->stream->version()) << fontpath;
+// qDebug() << "creating binary database at" << binaryDb.fileName();
+
+ // Load in font definition file
+ FILE* fontdef=fopen(fontDirFile.toLocal8Bit().constData(),"r");
+ if (fontdef) {
+ char buf[200]="";
+ char name[200]="";
+ char render[200]="";
+ char file[200]="";
+ char isitalic[10]="";
+ char flags[10]="";
+ do {
+ fgets(buf,200,fontdef);
+ if (buf[0] != '#') {
+ int weight=50;
+ int size=0;
+ sscanf(buf,"%s %s %s %s %d %d %s",name,file,render,isitalic,&weight,&size,flags);
+ QString filename;
+ if (file[0] != '/')
+ filename.append(fontpath).append(QLatin1Char('/'));
+ filename += QLatin1String(file);
+ bool italic = isitalic[0] == 'y';
+ bool smooth = QByteArray(flags).contains('s');
+ if (file[0] && QFile::exists(filename))
+ db->addFont(QString::fromUtf8(name), /*foundry*/"", weight, italic, size/10, QFile::encodeName(filename), /*fileIndex*/ 0, smooth);
+ }
+ } while (!feof(fontdef));
+ fclose(fontdef);
+ }
+
+
+ QDir dir(fontpath, QLatin1String("*.qpf"));
+ for (int i=0; i<int(dir.count()); i++) {
+ int u0 = dir[i].indexOf(QLatin1Char('_'));
+ int u1 = dir[i].indexOf(QLatin1Char('_'), u0+1);
+ int u2 = dir[i].indexOf(QLatin1Char('_'), u1+1);
+ int u3 = dir[i].indexOf(QLatin1Char('.'), u1+1);
+ if (u2 < 0) u2 = u3;
+
+ QString familyname = dir[i].left(u0);
+ int pixelSize = dir[i].mid(u0+1,u1-u0-1).toInt()/10;
+ bool italic = dir[i].mid(u2-1,1) == QLatin1String("i");
+ int weight = dir[i].mid(u1+1,u2-u1-1-(italic?1:0)).toInt();
+
+ db->addFont(familyname, /*foundry*/ "qt", weight, italic, pixelSize, QFile::encodeName(dir.absoluteFilePath(dir[i])),
+ /*fileIndex*/ 0, /*antialiased*/ true);
+ }
+
+#ifndef QT_NO_FREETYPE
+ dir.setNameFilters(QStringList() << QLatin1String("*.ttf")
+ << QLatin1String("*.ttc") << QLatin1String("*.pfa")
+ << QLatin1String("*.pfb"));
+ dir.refresh();
+ for (int i = 0; i < int(dir.count()); ++i) {
+ const QByteArray file = QFile::encodeName(dir.absoluteFilePath(dir[i]));
+// qDebug() << "looking at" << file;
+ db->addTTFile(file);
+ }
+#endif
+
+#ifndef QT_NO_QWS_QPF2
+ dir.setNameFilters(QStringList() << QLatin1String("*.qpf2"));
+ dir.refresh();
+ for (int i = 0; i < int(dir.count()); ++i) {
+ const QByteArray file = QFile::encodeName(dir.absoluteFilePath(dir[i]));
+// qDebug() << "looking at" << file;
+ db->addQPF2File(file);
+ }
+#endif
+
+#else //QT_FONTS_ARE_RESOURCES
+#ifdef QFONTDATABASE_DEBUG
+ {
+ QResource fontdir(fontpath);
+ FriendlyResource *fr = static_cast<FriendlyResource*>(&fontdir);
+ qDebug() << "fontdir" << fr->isValid() << fr->isDir() << fr->children();
+
+ }
+#endif
+ QDir dir(fontpath, QLatin1String("*.qpf2"));
+ for (int i = 0; i < int(dir.count()); ++i) {
+ const QByteArray file = QFile::encodeName(dir.absoluteFilePath(dir[i]));
+ //qDebug() << "looking at" << file;
+ db->addQPF2File(file);
+ }
+#endif //QT_FONTS_ARE_RESOURCES
+
+
+#ifdef QFONTDATABASE_DEBUG
+ // print the database
+ for (int f = 0; f < db->count; f++) {
+ QtFontFamily *family = db->families[f];
+ FD_DEBUG("'%s' %s", qPrintable(family->name), (family->fixedPitch ? "fixed" : ""));
+#if 0
+ for (int i = 0; i < QFont::LastPrivateScript; ++i) {
+ FD_DEBUG("\t%s: %s", qPrintable(QFontDatabase::scriptName((QFont::Script) i)),
+ ((family->scripts[i] & QtFontFamily::Supported) ? "Supported" :
+ (family->scripts[i] & QtFontFamily::UnSupported) == QtFontFamily::UnSupported ?
+ "UnSupported" : "Unknown"));
+ }
+#endif
+
+ for (int fd = 0; fd < family->count; fd++) {
+ QtFontFoundry *foundry = family->foundries[fd];
+ FD_DEBUG("\t\t'%s'", qPrintable(foundry->name));
+ for (int s = 0; s < foundry->count; s++) {
+ QtFontStyle *style = foundry->styles[s];
+ FD_DEBUG("\t\t\tstyle: style=%d weight=%d\n"
+ "\t\t\tstretch=%d",
+ style->key.style, style->key.weight,
+ style->key.stretch);
+ if (style->smoothScalable)
+ FD_DEBUG("\t\t\t\tsmooth scalable");
+ else if (style->bitmapScalable)
+ FD_DEBUG("\t\t\t\tbitmap scalable");
+ if (style->pixelSizes) {
+ FD_DEBUG("\t\t\t\t%d pixel sizes", style->count);
+ for (int z = 0; z < style->count; ++z) {
+ QtFontSize *size = style->pixelSizes + z;
+ FD_DEBUG("\t\t\t\t size %5d",
+ size->pixelSize);
+ }
+ }
+ }
+ }
+ }
+#endif // QFONTDATABASE_DEBUG
+
+#ifndef QT_NO_LIBRARY
+ QStringList pluginFoundries = loader()->keys();
+// qDebug() << "plugin foundries:" << pluginFoundries;
+ for (int i = 0; i < pluginFoundries.count(); ++i) {
+ const QString foundry(pluginFoundries.at(i));
+
+ QFontEngineFactoryInterface *factory = qobject_cast<QFontEngineFactoryInterface *>(loader()->instance(foundry));
+ if (!factory) {
+ qDebug() << "Could not load plugin for foundry" << foundry;
+ continue;
+ }
+
+ QList<QFontEngineInfo> fonts = factory->availableFontEngines();
+ for (int i = 0; i < fonts.count(); ++i) {
+ QFontEngineInfo info = fonts.at(i);
+
+ int weight = info.weight();
+ if (weight <= 0)
+ weight = QFont::Normal;
+
+ db->addFont(info.family(), foundry.toLatin1().constData(),
+ weight, info.style() != QFont::StyleNormal,
+ qRound(info.pixelSize()), /*file*/QByteArray(),
+ /*fileIndex*/0, /*antiAliased*/true,
+ info.writingSystems());
+ }
+ }
+#endif
+
+#ifndef QT_FONTS_ARE_RESOURCES
+ // the empty string/familyname signifies the end of the font list.
+ *db->stream << QString();
+#endif
+ {
+ bool coveredWritingSystems[QFontDatabase::WritingSystemsCount] = { 0 };
+
+ db->fallbackFamilies.clear();
+
+ for (int i = 0; i < db->count; ++i) {
+ QtFontFamily *family = db->families[i];
+ bool add = false;
+ if (family->count == 0)
+ continue;
+ if (family->bogusWritingSystems)
+ continue;
+ for (int ws = 1; ws < QFontDatabase::WritingSystemsCount; ++ws) {
+ if (coveredWritingSystems[ws])
+ continue;
+ if (family->writingSystems[ws] & QtFontFamily::Supported) {
+ coveredWritingSystems[ws] = true;
+ add = true;
+ }
+ }
+ if (add)
+ db->fallbackFamilies << family->name;
+ }
+ //qDebug() << "fallbacks on the server:" << db->fallbackFamilies;
+#ifndef QT_FONTS_ARE_RESOURCES
+ *db->stream << db->fallbackFamilies;
+#endif
+ }
+#ifndef QT_FONTS_ARE_RESOURCES
+ delete db->stream;
+ db->stream = 0;
+ QFile::remove(dbFileName);
+ binaryDb.rename(dbFileName);
+#endif
+}
+
+// called from qwindowsystem_qws.cpp
+void qt_qws_init_fontdb()
+{
+ initializeDb();
+}
+
+#ifndef QT_NO_SETTINGS
+// called from qapplication_qws.cpp
+void qt_applyFontDatabaseSettings(const QSettings &settings)
+{
+ initializeDb();
+ QFontDatabasePrivate *db = privateDb();
+ for (int i = 0; i < db->count; ++i) {
+ QtFontFamily *family = db->families[i];
+ if (settings.contains(family->name))
+ family->fallbackFamilies = settings.value(family->name).toStringList();
+ }
+
+ if (settings.contains(QLatin1String("Global Fallbacks")))
+ db->fallbackFamilies = settings.value(QLatin1String("Global Fallbacks")).toStringList();
+}
+#endif // QT_NO_SETTINGS
+
+static inline void load(const QString & = QString(), int = -1)
+{
+ initializeDb();
+}
+
+#ifndef QT_NO_FREETYPE
+
+#if (FREETYPE_MAJOR*10000+FREETYPE_MINOR*100+FREETYPE_PATCH) >= 20105
+#define X_SIZE(face,i) ((face)->available_sizes[i].x_ppem)
+#define Y_SIZE(face,i) ((face)->available_sizes[i].y_ppem)
+#else
+#define X_SIZE(face,i) ((face)->available_sizes[i].width << 6)
+#define Y_SIZE(face,i) ((face)->available_sizes[i].height << 6)
+#endif
+
+#endif // QT_NO_FREETYPE
+
+static
+QFontEngine *loadSingleEngine(int script, const QFontPrivate *fp,
+ const QFontDef &request,
+ QtFontFamily *family, QtFontFoundry *foundry,
+ QtFontStyle *style, QtFontSize *size)
+{
+ Q_UNUSED(script);
+ Q_UNUSED(fp);
+#ifdef QT_NO_FREETYPE
+ Q_UNUSED(foundry);
+#endif
+#ifdef QT_NO_QWS_QPF
+ Q_UNUSED(family);
+#endif
+ Q_ASSERT(size);
+
+ int pixelSize = size->pixelSize;
+ if (!pixelSize || (style->smoothScalable && pixelSize == SMOOTH_SCALABLE))
+ pixelSize = request.pixelSize;
+
+#ifndef QT_NO_QWS_QPF2
+ if (foundry->name == QLatin1String("prerendered")) {
+#ifdef QT_FONTS_ARE_RESOURCES
+ QResource res(QLatin1String(size->fileName.constData()));
+ if (res.isValid()) {
+ QFontEngineQPF *fe = new QFontEngineQPF(request, res.data(), res.size());
+ if (fe->isValid())
+ return fe;
+ qDebug() << "fontengine is not valid! " << size->fileName;
+ delete fe;
+ } else {
+ qDebug() << "Resource not valid" << size->fileName;
+ }
+#else
+ int f = ::open(size->fileName, O_RDONLY);
+ if (f >= 0) {
+ QFontEngineQPF *fe = new QFontEngineQPF(request, f);
+ if (fe->isValid())
+ return fe;
+ qDebug() << "fontengine is not valid!";
+ delete fe; // will close f
+ }
+#endif
+ } else
+#endif
+ if ( foundry->name != QLatin1String("qt") ) { ///#### is this the best way????
+ QString file = QFile::decodeName(size->fileName);
+
+ QFontDef def = request;
+ def.pixelSize = pixelSize;
+
+ static bool dontShareFonts = !qgetenv("QWS_NO_SHARE_FONTS").isEmpty();
+ bool shareFonts = !dontShareFonts;
+
+ QFontEngine *engine = 0;
+
+#ifndef QT_NO_LIBRARY
+ QFontEngineFactoryInterface *factory = qobject_cast<QFontEngineFactoryInterface *>(loader()->instance(foundry->name));
+ if (factory) {
+ QFontEngineInfo info;
+ info.setFamily(request.family);
+ info.setPixelSize(request.pixelSize);
+ info.setStyle(QFont::Style(request.style));
+ info.setWeight(request.weight);
+ // #### antialiased
+
+ QAbstractFontEngine *customEngine = factory->create(info);
+ if (customEngine) {
+ engine = new QProxyFontEngine(customEngine, def);
+
+ if (shareFonts) {
+ QVariant hint = customEngine->fontProperty(QAbstractFontEngine::CacheGlyphsHint);
+ if (hint.isValid())
+ shareFonts = hint.toBool();
+ else
+ shareFonts = (pixelSize < 64);
+ }
+ }
+ }
+#endif // QT_NO_LIBRARY
+ if (!engine && !file.isEmpty() && QFile::exists(file) || privateDb()->isApplicationFont(file)) {
+ QFontEngine::FaceId faceId;
+ faceId.filename = file.toLocal8Bit();
+ faceId.index = size->fileIndex;
+
+#ifndef QT_NO_FREETYPE
+
+ QFontEngineFT *fte = new QFontEngineFT(def);
+ if (fte->init(faceId, style->antialiased,
+ style->antialiased ? QFontEngineFT::Format_A8 : QFontEngineFT::Format_Mono)) {
+#ifdef QT_NO_QWS_QPF2
+ return fte;
+#else
+ engine = fte;
+ // try to distinguish between bdf and ttf fonts we can pre-render
+ // and don't try to share outline fonts
+ shareFonts = shareFonts
+ && !fte->defaultGlyphs()->outline_drawing
+ && !fte->getSfntTable(MAKE_TAG('h', 'e', 'a', 'd')).isEmpty();
+#endif
+ } else {
+ delete fte;
+ }
+#endif // QT_NO_FREETYPE
+ }
+ if (engine) {
+#if !defined(QT_NO_QWS_QPF2) && !defined(QT_FONTS_ARE_RESOURCES)
+ if (shareFonts) {
+ QFontEngineQPF *fe = new QFontEngineQPF(def, -1, engine);
+ engine = 0;
+ if (fe->isValid())
+ return fe;
+ qWarning("Initializing QFontEngineQPF failed for %s", qPrintable(file));
+ engine = fe->takeRenderingEngine();
+ delete fe;
+ }
+#endif
+ return engine;
+ }
+ } else
+ {
+#ifndef QT_NO_QWS_QPF
+ QString fn = qwsFontPath();
+ fn += QLatin1Char('/');
+ fn += family->name.toLower()
+ + QLatin1String("_") + QString::number(pixelSize*10)
+ + QLatin1String("_") + QString::number(style->key.weight)
+ + (style->key.style == QFont::StyleItalic ?
+ QLatin1String("i.qpf") : QLatin1String(".qpf"));
+ //###rotation ###
+
+ QFontEngine *fe = new QFontEngineQPF1(request, fn);
+ return fe;
+#endif // QT_NO_QWS_QPF
+ }
+ return new QFontEngineBox(pixelSize);
+}
+
+static
+QFontEngine *loadEngine(int script, const QFontPrivate *fp,
+ const QFontDef &request,
+ QtFontFamily *family, QtFontFoundry *foundry,
+ QtFontStyle *style, QtFontSize *size)
+{
+ QFontEngine *fe = loadSingleEngine(script, fp, request, family, foundry,
+ style, size);
+ if (fe
+ && script == QUnicodeTables::Common
+ && !(request.styleStrategy & QFont::NoFontMerging) && !fe->symbol) {
+
+ QStringList fallbacks = privateDb()->fallbackFamilies;
+
+ if (family && !family->fallbackFamilies.isEmpty())
+ fallbacks = family->fallbackFamilies;
+
+ fe = new QFontEngineMultiQWS(fe, script, fallbacks);
+ }
+ return fe;
+}
+
+static void registerFont(QFontDatabasePrivate::ApplicationFont *fnt)
+{
+ QFontDatabasePrivate *db = privateDb();
+#ifdef QT_NO_FREETYPE
+ Q_UNUSED(fnt);
+#else
+ fnt->families = db->addTTFile(QFile::encodeName(fnt->fileName), fnt->data);
+ db->fallbackFamilies += fnt->families;
+#endif
+ db->reregisterAppFonts = true;
+}
+
+bool QFontDatabase::removeApplicationFont(int handle)
+{
+ QMutexLocker locker(fontDatabaseMutex());
+
+ QFontDatabasePrivate *db = privateDb();
+ if (handle < 0 || handle >= db->applicationFonts.count())
+ return false;
+
+ db->applicationFonts[handle] = QFontDatabasePrivate::ApplicationFont();
+
+ db->reregisterAppFonts = true;
+ db->invalidate();
+ return true;
+}
+
+bool QFontDatabase::removeAllApplicationFonts()
+{
+ QMutexLocker locker(fontDatabaseMutex());
+
+ QFontDatabasePrivate *db = privateDb();
+ if (db->applicationFonts.isEmpty())
+ return false;
+
+ db->applicationFonts.clear();
+ db->invalidate();
+ return true;
+}
+
+bool QFontDatabase::supportsThreadedFontRendering()
+{
+ return true;
+}
+
+/*!
+ \internal
+*/
+QFontEngine *
+QFontDatabase::findFont(int script, const QFontPrivate *fp,
+ const QFontDef &request)
+{
+ QMutexLocker locker(fontDatabaseMutex());
+
+ const int force_encoding_id = -1;
+
+ if (!privateDb()->count)
+ initializeDb();
+
+ QFontEngine *fe = 0;
+ if (fp) {
+ if (fp->rawMode) {
+ fe = loadEngine(script, fp, request, 0, 0, 0, 0
+ );
+
+ // if we fail to load the rawmode font, use a 12pixel box engine instead
+ if (! fe) fe = new QFontEngineBox(12);
+ return fe;
+ }
+
+ QFontCache::Key key(request, script);
+ fe = QFontCache::instance()->findEngine(key);
+ if (fe)
+ return fe;
+ }
+
+ QString family_name, foundry_name;
+ QtFontStyle::Key styleKey;
+ styleKey.style = request.style;
+ styleKey.weight = request.weight;
+ styleKey.stretch = request.stretch;
+ char pitch = request.ignorePitch ? '*' : request.fixedPitch ? 'm' : 'p';
+
+ parseFontName(request.family, foundry_name, family_name);
+
+ FM_DEBUG("QFontDatabase::findFont\n"
+ " request:\n"
+ " family: %s [%s], script: %d\n"
+ " weight: %d, style: %d\n"
+ " stretch: %d\n"
+ " pixelSize: %d\n"
+ " pitch: %c",
+ family_name.isEmpty() ? "-- first in script --" : family_name.toLatin1().constData(),
+ foundry_name.isEmpty() ? "-- any --" : foundry_name.toLatin1().constData(),
+ script, request.weight, request.style, request.stretch, request.pixelSize, pitch);
+
+ if (qt_enable_test_font && request.family == QLatin1String("__Qt__Box__Engine__")) {
+ fe = new QTestFontEngine(request.pixelSize);
+ fe->fontDef = request;
+ }
+
+ if (!fe)
+ {
+ QtFontDesc desc;
+ match(script, request, family_name, foundry_name, force_encoding_id, &desc);
+
+ if (desc.family != 0 && desc.foundry != 0 && desc.style != 0
+ ) {
+ FM_DEBUG(" BEST:\n"
+ " family: %s [%s]\n"
+ " weight: %d, style: %d\n"
+ " stretch: %d\n"
+ " pixelSize: %d\n"
+ " pitch: %c\n"
+ " encoding: %d\n",
+ desc.family->name.toLatin1().constData(),
+ desc.foundry->name.isEmpty() ? "-- none --" : desc.foundry->name.toLatin1().constData(),
+ desc.style->key.weight, desc.style->key.style,
+ desc.style->key.stretch, desc.size ? desc.size->pixelSize : 0xffff,
+ 'p', 0
+ );
+
+ fe = loadEngine(script, fp, request, desc.family, desc.foundry, desc.style, desc.size
+ );
+ } else {
+ FM_DEBUG(" NO MATCH FOUND\n");
+ }
+ if (fe)
+ initFontDef(desc, request, &fe->fontDef);
+ }
+
+ if (fe) {
+ if (fp) {
+ QFontDef def = request;
+ if (def.family.isEmpty()) {
+ def.family = fp->request.family;
+ def.family = def.family.left(def.family.indexOf(QLatin1Char(',')));
+ }
+ QFontCache::Key key(def, script);
+ QFontCache::instance()->insertEngine(key, fe);
+ }
+
+#ifndef QT_NO_FREETYPE
+ if (scriptRequiresOpenType(script) && fe->type() == QFontEngine::Freetype) {
+ HB_Face hbFace = static_cast<QFontEngineFT *>(fe)->harfbuzzFace();
+ if (!hbFace || !hbFace->supported_scripts[script]) {
+ FM_DEBUG(" OpenType support missing for script\n");
+ delete fe;
+ fe = 0;
+ }
+ }
+#endif
+ }
+
+ if (!fe) {
+ if (!request.family.isEmpty())
+ return 0;
+
+ FM_DEBUG("returning box engine");
+
+ fe = new QFontEngineBox(request.pixelSize);
+
+ if (fp) {
+ QFontCache::Key key(request, script);
+ QFontCache::instance()->insertEngine(key, fe);
+ }
+ }
+
+ if (fp && fp->dpi > 0) {
+ fe->fontDef.pointSize = qreal(double((fe->fontDef.pixelSize * 72) / fp->dpi));
+ } else {
+ fe->fontDef.pointSize = request.pointSize;
+ }
+
+ return fe;
+}
+
+void QFontDatabase::load(const QFontPrivate *d, int script)
+{
+ QFontDef req = d->request;
+
+ if (req.pixelSize == -1)
+ req.pixelSize = qRound(req.pointSize*d->dpi/72);
+ if (req.pointSize < 0)
+ req.pointSize = req.pixelSize*72.0/d->dpi;
+
+ if (!d->engineData) {
+ QFontCache::Key key(req, script);
+
+ // look for the requested font in the engine data cache
+ d->engineData = QFontCache::instance()->findEngineData(key);
+
+ if (!d->engineData) {
+ // create a new one
+ d->engineData = new QFontEngineData;
+ QFontCache::instance()->insertEngineData(key, d->engineData);
+ } else {
+ d->engineData->ref.ref();
+ }
+ }
+
+ // the cached engineData could have already loaded the engine we want
+ if (d->engineData->engines[script]) return;
+
+ // load the font
+ QFontEngine *engine = 0;
+ // double scale = 1.0; // ### TODO: fix the scale calculations
+
+ // list of families to try
+ QStringList family_list;
+
+ if (!req.family.isEmpty()) {
+ family_list = req.family.split(QLatin1Char(','));
+
+ // append the substitute list for each family in family_list
+ QStringList subs_list;
+ QStringList::ConstIterator it = family_list.constBegin(), end = family_list.constEnd();
+ for (; it != end; ++it)
+ subs_list += QFont::substitutes(*it);
+ family_list += subs_list;
+
+ // append the default fallback font for the specified script
+ // family_list << ... ; ###########
+
+ // add the default family
+ QString defaultFamily = QApplication::font().family();
+ if (! family_list.contains(defaultFamily))
+ family_list << defaultFamily;
+
+ // add QFont::defaultFamily() to the list, for compatibility with
+ // previous versions
+ family_list << QApplication::font().defaultFamily();
+ }
+
+ // null family means find the first font matching the specified script
+ family_list << QString();
+
+ QStringList::ConstIterator it = family_list.constBegin(), end = family_list.constEnd();
+ for (; ! engine && it != end; ++it) {
+ req.family = *it;
+
+ engine = QFontDatabase::findFont(script, d, req);
+ if (engine) {
+ if (engine->type() != QFontEngine::Box)
+ break;
+
+ if (! req.family.isEmpty())
+ engine = 0;
+
+ continue;
+ }
+ }
+
+ engine->ref.ref();
+ d->engineData->engines[script] = engine;
+}
+
+
+QT_END_NAMESPACE
diff --git a/src/gui/text/qfontdatabase_win.cpp b/src/gui/text/qfontdatabase_win.cpp
new file mode 100644
index 0000000000..c9f558664d
--- /dev/null
+++ b/src/gui/text/qfontdatabase_win.cpp
@@ -0,0 +1,1288 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qt_windows.h"
+#include <private/qapplication_p.h>
+#include "qfont_p.h"
+#include "qfontengine_p.h"
+#include "qpaintdevice.h"
+#include "qlibrary.h"
+#include "qabstractfileengine.h"
+#include "qendian.h"
+
+#ifdef Q_OS_WINCE
+# include <QTemporaryFile>
+#endif
+
+QT_BEGIN_NAMESPACE
+
+extern HDC shared_dc(); // common dc for all fonts
+
+#ifdef MAKE_TAG
+#undef MAKE_TAG
+#endif
+// GetFontData expects the tags in little endian ;(
+#define MAKE_TAG(ch1, ch2, ch3, ch4) (\
+ (((quint32)(ch4)) << 24) | \
+ (((quint32)(ch3)) << 16) | \
+ (((quint32)(ch2)) << 8) | \
+ ((quint32)(ch1)) \
+ )
+
+static HFONT stock_sysfont = 0;
+
+static bool localizedName(const QString &name)
+{
+ const QChar *c = name.unicode();
+ for(int i = 0; i < name.length(); ++i) {
+ if(c[i].unicode() >= 0x100)
+ return true;
+ }
+ return false;
+}
+
+static inline quint16 getUShort(const unsigned char *p)
+{
+ quint16 val;
+ val = *p++ << 8;
+ val |= *p;
+
+ return val;
+}
+
+static QString getEnglishName(const uchar *table, quint32 bytes)
+{
+ QString i18n_name;
+ enum {
+ NameRecordSize = 12,
+ FamilyId = 1,
+ MS_LangIdEnglish = 0x009
+ };
+
+ // get the name table
+ quint16 count;
+ quint16 string_offset;
+ const unsigned char *names;
+
+ int microsoft_id = -1;
+ int apple_id = -1;
+ int unicode_id = -1;
+
+ if(getUShort(table) != 0)
+ goto error;
+
+ count = getUShort(table+2);
+ string_offset = getUShort(table+4);
+ names = table + 6;
+
+ if(string_offset >= bytes || 6 + count*NameRecordSize > string_offset)
+ goto error;
+
+ for(int i = 0; i < count; ++i) {
+ // search for the correct name entry
+
+ quint16 platform_id = getUShort(names + i*NameRecordSize);
+ quint16 encoding_id = getUShort(names + 2 + i*NameRecordSize);
+ quint16 language_id = getUShort(names + 4 + i*NameRecordSize);
+ quint16 name_id = getUShort(names + 6 + i*NameRecordSize);
+
+ if(name_id != FamilyId)
+ continue;
+
+ enum {
+ PlatformId_Unicode = 0,
+ PlatformId_Apple = 1,
+ PlatformId_Microsoft = 3
+ };
+
+ quint16 length = getUShort(names + 8 + i*NameRecordSize);
+ quint16 offset = getUShort(names + 10 + i*NameRecordSize);
+ if(DWORD(string_offset + offset + length) >= bytes)
+ continue;
+
+ if ((platform_id == PlatformId_Microsoft
+ && (encoding_id == 0 || encoding_id == 1))
+ && (language_id & 0x3ff) == MS_LangIdEnglish
+ && microsoft_id == -1)
+ microsoft_id = i;
+ // not sure if encoding id 4 for Unicode is utf16 or ucs4...
+ else if(platform_id == PlatformId_Unicode && encoding_id < 4 && unicode_id == -1)
+ unicode_id = i;
+ else if(platform_id == PlatformId_Apple && encoding_id == 0 && language_id == 0)
+ apple_id = i;
+ }
+ {
+ bool unicode = false;
+ int id = -1;
+ if(microsoft_id != -1) {
+ id = microsoft_id;
+ unicode = true;
+ } else if(apple_id != -1) {
+ id = apple_id;
+ unicode = false;
+ } else if (unicode_id != -1) {
+ id = unicode_id;
+ unicode = true;
+ }
+ if(id != -1) {
+ quint16 length = getUShort(names + 8 + id*NameRecordSize);
+ quint16 offset = getUShort(names + 10 + id*NameRecordSize);
+ if(unicode) {
+ // utf16
+
+ length /= 2;
+ i18n_name.resize(length);
+ QChar *uc = (QChar *) i18n_name.unicode();
+ const unsigned char *string = table + string_offset + offset;
+ for(int i = 0; i < length; ++i)
+ uc[i] = getUShort(string + 2*i);
+ } else {
+ // Apple Roman
+
+ i18n_name.resize(length);
+ QChar *uc = (QChar *) i18n_name.unicode();
+ const unsigned char *string = table + string_offset + offset;
+ for(int i = 0; i < length; ++i)
+ uc[i] = QLatin1Char(string[i]);
+ }
+ }
+ }
+ error:
+ //qDebug("got i18n name of '%s' for font '%s'", i18n_name.latin1(), familyName.toLocal8Bit().data());
+ return i18n_name;
+}
+
+static QString getEnglishName(const QString &familyName)
+{
+ QString i18n_name;
+
+ HDC hdc = GetDC( 0 );
+ HFONT hfont;
+ QT_WA( {
+ LOGFONTW lf;
+ memset( &lf, 0, sizeof( LOGFONTW ) );
+ memcpy( lf.lfFaceName, familyName.utf16(), qMin(LF_FACESIZE, familyName.length())*sizeof(QChar) );
+ lf.lfCharSet = DEFAULT_CHARSET;
+ hfont = CreateFontIndirectW( &lf );
+ }, {
+ LOGFONTA lf;
+ memset( &lf, 0, sizeof( LOGFONTA ) );
+ QByteArray lfam = familyName.toLocal8Bit();
+ memcpy( lf.lfFaceName, lfam, qMin(LF_FACESIZE, lfam.size()) );
+ lf.lfCharSet = DEFAULT_CHARSET;
+ hfont = CreateFontIndirectA( &lf );
+ } );
+ if(!hfont) {
+ ReleaseDC(0, hdc);
+ return QString();
+ }
+
+ HGDIOBJ oldobj = SelectObject( hdc, hfont );
+
+ const DWORD name_tag = MAKE_TAG( 'n', 'a', 'm', 'e' );
+
+ // get the name table
+ unsigned char *table = 0;
+
+ DWORD bytes = GetFontData( hdc, name_tag, 0, 0, 0 );
+ if ( bytes == GDI_ERROR ) {
+ // ### Unused variable
+ /* int err = GetLastError(); */
+ goto error;
+ }
+
+ table = new unsigned char[bytes];
+ GetFontData(hdc, name_tag, 0, table, bytes);
+ if ( bytes == GDI_ERROR )
+ goto error;
+
+ i18n_name = getEnglishName(table, bytes);
+error:
+ delete [] table;
+ SelectObject( hdc, oldobj );
+ DeleteObject( hfont );
+ ReleaseDC( 0, hdc );
+
+ //qDebug("got i18n name of '%s' for font '%s'", i18n_name.latin1(), familyName.toLocal8Bit().data());
+ return i18n_name;
+}
+
+static void getFontSignature(const QString &familyName,
+ NEWTEXTMETRICEX *textmetric,
+ FONTSIGNATURE *signature)
+{
+ QT_WA({
+ Q_UNUSED(familyName);
+ *signature = textmetric->ntmFontSig;
+ }, {
+ // the textmetric structure we get from EnumFontFamiliesEx on Win9x has
+ // a FONTSIGNATURE, but that one is uninitialized and doesn't work. Have to go
+ // the hard way and load the font to find out.
+ HDC hdc = GetDC(0);
+ LOGFONTA lf;
+ memset(&lf, 0, sizeof(LOGFONTA));
+ QByteArray lfam = familyName.toLocal8Bit();
+ memcpy(lf.lfFaceName, lfam.data(), qMin(LF_FACESIZE, lfam.length()));
+ lf.lfCharSet = DEFAULT_CHARSET;
+ HFONT hfont = CreateFontIndirectA(&lf);
+ HGDIOBJ oldobj = SelectObject(hdc, hfont);
+ GetTextCharsetInfo(hdc, signature, 0);
+ SelectObject(hdc, oldobj);
+ DeleteObject(hfont);
+ ReleaseDC(0, hdc);
+ });
+}
+
+static
+void addFontToDatabase(QString familyName, const QString &scriptName,
+ TEXTMETRIC *textmetric,
+ const FONTSIGNATURE *signature,
+ int type)
+{
+ const int script = -1;
+ const QString foundryName;
+ Q_UNUSED(script);
+
+ bool italic = false;
+ int weight;
+ bool fixed;
+ bool ttf;
+ bool scalable;
+ int size;
+
+// QString escript = QString::fromUtf16((ushort *)f->elfScript);
+// qDebug("script=%s", escript.latin1());
+
+ QT_WA({
+ NEWTEXTMETRIC *tm = (NEWTEXTMETRIC *)textmetric;
+ fixed = !(tm->tmPitchAndFamily & TMPF_FIXED_PITCH);
+ ttf = (tm->tmPitchAndFamily & TMPF_TRUETYPE);
+ scalable = tm->tmPitchAndFamily & (TMPF_VECTOR|TMPF_TRUETYPE);
+ size = scalable ? SMOOTH_SCALABLE : tm->tmHeight;
+ italic = tm->tmItalic;
+ weight = tm->tmWeight;
+ } , {
+ NEWTEXTMETRICA *tm = (NEWTEXTMETRICA *)textmetric;
+ fixed = !(tm->tmPitchAndFamily & TMPF_FIXED_PITCH);
+ ttf = (tm->tmPitchAndFamily & TMPF_TRUETYPE);
+ scalable = tm->tmPitchAndFamily & (TMPF_VECTOR|TMPF_TRUETYPE);
+ size = scalable ? SMOOTH_SCALABLE : tm->tmHeight;
+ italic = tm->tmItalic;
+ weight = tm->tmWeight;
+ });
+ // the "@family" fonts are just the same as "family". Ignore them.
+ if (familyName[0] != QLatin1Char('@') && !familyName.startsWith(QLatin1String("WST_"))) {
+ QtFontStyle::Key styleKey;
+ styleKey.style = italic ? QFont::StyleItalic : QFont::StyleNormal;
+ if (weight < 400)
+ styleKey.weight = QFont::Light;
+ else if (weight < 600)
+ styleKey.weight = QFont::Normal;
+ else if (weight < 700)
+ styleKey.weight = QFont::DemiBold;
+ else if (weight < 800)
+ styleKey.weight = QFont::Bold;
+ else
+ styleKey.weight = QFont::Black;
+
+ QtFontFamily *family = privateDb()->family(familyName, true);
+
+ if(ttf && localizedName(familyName) && family->english_name.isEmpty())
+ family->english_name = getEnglishName(familyName);
+
+ QtFontFoundry *foundry = family->foundry(foundryName, true);
+ QtFontStyle *style = foundry->style(styleKey, true);
+ style->smoothScalable = scalable;
+ style->pixelSize( size, TRUE);
+
+ // add fonts windows can generate for us:
+ if (styleKey.weight <= QFont::DemiBold) {
+ QtFontStyle::Key key(styleKey);
+ key.weight = QFont::Bold;
+ QtFontStyle *style = foundry->style(key, true);
+ style->smoothScalable = scalable;
+ style->pixelSize( size, TRUE);
+ }
+ if (styleKey.style != QFont::StyleItalic) {
+ QtFontStyle::Key key(styleKey);
+ key.style = QFont::StyleItalic;
+ QtFontStyle *style = foundry->style(key, true);
+ style->smoothScalable = scalable;
+ style->pixelSize( size, TRUE);
+ }
+ if (styleKey.weight <= QFont::DemiBold && styleKey.style != QFont::StyleItalic) {
+ QtFontStyle::Key key(styleKey);
+ key.weight = QFont::Bold;
+ key.style = QFont::StyleItalic;
+ QtFontStyle *style = foundry->style(key, true);
+ style->smoothScalable = scalable;
+ style->pixelSize( size, TRUE);
+ }
+
+ family->fixedPitch = fixed;
+
+ if (!family->writingSystemCheck && type & TRUETYPE_FONTTYPE) {
+ quint32 unicodeRange[4] = {
+ signature->fsUsb[0], signature->fsUsb[1],
+ signature->fsUsb[2], signature->fsUsb[3]
+ };
+#ifdef Q_OS_WINCE
+ if (signature->fsUsb[0] == 0) {
+ // If the unicode ranges bit mask is zero then
+ // EnumFontFamiliesEx failed to determine it properly.
+ // In this case we just pretend that the font supports all languages.
+ unicodeRange[0] = 0xbfffffff; // second most significant bit must be zero
+ unicodeRange[1] = 0xffffffff;
+ unicodeRange[2] = 0xffffffff;
+ unicodeRange[3] = 0xffffffff;
+ }
+#endif
+ quint32 codePageRange[2] = {
+ signature->fsCsb[0], signature->fsCsb[1]
+ };
+ QList<QFontDatabase::WritingSystem> systems = determineWritingSystemsFromTrueTypeBits(unicodeRange, codePageRange);
+ for (int i = 0; i < systems.count(); ++i)
+ family->writingSystems[systems.at(i)] = QtFontFamily::Supported;
+ } else if (!family->writingSystemCheck) {
+ //qDebug("family='%s' script=%s", family->name.latin1(), script.latin1());
+ if (scriptName == QLatin1String("Western")
+ || scriptName == QLatin1String("Baltic")
+ || scriptName == QLatin1String("Central European")
+ || scriptName == QLatin1String("Turkish")
+ || scriptName == QLatin1String("Vietnamese"))
+ family->writingSystems[QFontDatabase::Latin] = QtFontFamily::Supported;
+ else if (scriptName == QLatin1String("Thai"))
+ family->writingSystems[QFontDatabase::Thai] = QtFontFamily::Supported;
+ else if (scriptName == QLatin1String("Symbol")
+ || scriptName == QLatin1String("Other"))
+ family->writingSystems[QFontDatabase::Symbol] = QtFontFamily::Supported;
+ else if (scriptName == QLatin1String("OEM/Dos"))
+ family->writingSystems[QFontDatabase::Latin] = QtFontFamily::Supported;
+ else if (scriptName == QLatin1String("CHINESE_GB2312"))
+ family->writingSystems[QFontDatabase::SimplifiedChinese] = QtFontFamily::Supported;
+ else if (scriptName == QLatin1String("CHINESE_BIG5"))
+ family->writingSystems[QFontDatabase::TraditionalChinese] = QtFontFamily::Supported;
+ else if (scriptName == QLatin1String("Cyrillic"))
+ family->writingSystems[QFontDatabase::Cyrillic] = QtFontFamily::Supported;
+ else if (scriptName == QLatin1String("Hangul"))
+ family->writingSystems[QFontDatabase::Korean] = QtFontFamily::Supported;
+ else if (scriptName == QLatin1String("Hebrew"))
+ family->writingSystems[QFontDatabase::Hebrew] = QtFontFamily::Supported;
+ else if (scriptName == QLatin1String("Greek"))
+ family->writingSystems[QFontDatabase::Greek] = QtFontFamily::Supported;
+ else if (scriptName == QLatin1String("Japanese"))
+ family->writingSystems[QFontDatabase::Japanese] = QtFontFamily::Supported;
+ else if (scriptName == QLatin1String("Arabic"))
+ family->writingSystems[QFontDatabase::Arabic] = QtFontFamily::Supported;
+ }
+ }
+}
+
+static
+int CALLBACK
+storeFont(ENUMLOGFONTEX* f, NEWTEXTMETRICEX *textmetric, int type, LPARAM /*p*/)
+{
+ QString familyName;
+ QT_WA({
+ familyName = QString::fromUtf16((ushort*)f->elfLogFont.lfFaceName);
+ },{
+ ENUMLOGFONTEXA *fa = (ENUMLOGFONTEXA *)f;
+ familyName = QString::fromLocal8Bit(fa->elfLogFont.lfFaceName);
+ });
+ QString script = QT_WA_INLINE(QString::fromUtf16((const ushort *)f->elfScript),
+ QString::fromLocal8Bit((const char *)((ENUMLOGFONTEXA *)f)->elfScript));
+
+ FONTSIGNATURE signature;
+ getFontSignature(familyName, textmetric, &signature);
+
+ // NEWTEXTMETRICEX is a NEWTEXTMETRIC, which according to the documentation is
+ // identical to a TEXTMETRIC except for the last four members, which we don't use
+ // anyway
+ addFontToDatabase(familyName, script, (TEXTMETRIC *)textmetric, &signature, type);
+ // keep on enumerating
+ return 1;
+}
+
+static
+void populate_database(const QString& fam)
+{
+ QFontDatabasePrivate *d = privateDb();
+ if (!d)
+ return;
+
+ QtFontFamily *family = 0;
+ if(!fam.isEmpty()) {
+ family = d->family(fam);
+ if(family && family->loaded)
+ return;
+ } else if (d->count) {
+ return;
+ }
+
+ HDC dummy = GetDC(0);
+
+ QT_WA({
+ LOGFONT lf;
+ lf.lfCharSet = DEFAULT_CHARSET;
+ if (fam.isNull()) {
+ lf.lfFaceName[0] = 0;
+ } else {
+ memcpy(lf.lfFaceName, fam.utf16(), sizeof(TCHAR)*qMin(fam.length()+1,32)); // 32 = Windows hard-coded
+ }
+ lf.lfPitchAndFamily = 0;
+
+ EnumFontFamiliesEx(dummy, &lf,
+ (FONTENUMPROC)storeFont, (LPARAM)privateDb(), 0);
+ } , {
+ LOGFONTA lf;
+ lf.lfCharSet = DEFAULT_CHARSET;
+ if (fam.isNull()) {
+ lf.lfFaceName[0] = 0;
+ } else {
+ QByteArray lname = fam.toLocal8Bit();
+ memcpy(lf.lfFaceName,lname.data(),
+ qMin(lname.length()+1,32)); // 32 = Windows hard-coded
+ }
+ lf.lfPitchAndFamily = 0;
+
+ EnumFontFamiliesExA(dummy, &lf,
+ (FONTENUMPROCA)storeFont, (LPARAM)privateDb(), 0);
+ });
+
+ ReleaseDC(0, dummy);
+
+ for (int i = 0; i < d->applicationFonts.count(); ++i) {
+ QFontDatabasePrivate::ApplicationFont fnt = d->applicationFonts.at(i);
+ if (!fnt.memoryFont)
+ continue;
+ for (int j = 0; j < fnt.families.count(); ++j) {
+ const QString familyName = fnt.families.at(j);
+ HDC hdc = GetDC(0);
+ HFONT hfont;
+ QT_WA({
+ LOGFONTW lf;
+ memset(&lf, 0, sizeof(LOGFONTW));
+ memcpy(lf.lfFaceName, familyName.utf16(), qMin(LF_FACESIZE, familyName.size()));
+ lf.lfCharSet = DEFAULT_CHARSET;
+ hfont = CreateFontIndirectW(&lf);
+ } , {
+ LOGFONTA lf;
+ memset(&lf, 0, sizeof(LOGFONTA));
+ QByteArray lfam = familyName.toLocal8Bit();
+ memcpy(lf.lfFaceName, lfam.data(), qMin(LF_FACESIZE, lfam.length()));
+ lf.lfCharSet = DEFAULT_CHARSET;
+ hfont = CreateFontIndirectA(&lf);
+ });
+ HGDIOBJ oldobj = SelectObject(hdc, hfont);
+
+ TEXTMETRIC textMetrics;
+ GetTextMetrics(hdc, &textMetrics);
+
+ addFontToDatabase(familyName, QString(),
+ &textMetrics,
+ &fnt.signatures.at(j),
+ TRUETYPE_FONTTYPE);
+
+ SelectObject(hdc, oldobj);
+ DeleteObject(hfont);
+ ReleaseDC(0, hdc);
+ }
+ }
+
+ if(!fam.isEmpty()) {
+ family = d->family(fam);
+ if(family) {
+ if(!family->writingSystemCheck) {
+ }
+ family->loaded = true;
+ }
+ }
+}
+
+static void initializeDb()
+{
+ QFontDatabasePrivate *db = privateDb();
+ if (!db || db->count)
+ return;
+
+ populate_database(QString());
+
+#ifdef QFONTDATABASE_DEBUG
+ // print the database
+ for (int f = 0; f < db->count; f++) {
+ QtFontFamily *family = db->families[f];
+ qDebug(" %s: %p", family->name.latin1(), family);
+ populate_database(family->name);
+
+#if 0
+ qDebug(" scripts supported:");
+ for (int i = 0; i < QUnicodeTables::ScriptCount; i++)
+ if(family->writingSystems[i] & QtFontFamily::Supported)
+ qDebug(" %d", i);
+ for (int fd = 0; fd < family->count; fd++) {
+ QtFontFoundry *foundry = family->foundries[fd];
+ qDebug(" %s", foundry->name.latin1());
+ for (int s = 0; s < foundry->count; s++) {
+ QtFontStyle *style = foundry->styles[s];
+ qDebug(" style: style=%d weight=%d smooth=%d", style->key.style,
+ style->key.weight, style->smoothScalable );
+ if(!style->smoothScalable) {
+ for(int i = 0; i < style->count; ++i) {
+ qDebug(" %d", style->pixelSizes[i].pixelSize);
+ }
+ }
+ }
+ }
+#endif
+ }
+#endif // QFONTDATABASE_DEBUG
+
+}
+
+static inline void load(const QString &family = QString(), int = -1)
+{
+ populate_database(family);
+}
+
+
+
+
+
+// --------------------------------------------------------------------------------------
+// font loader
+// --------------------------------------------------------------------------------------
+
+
+
+static void initFontInfo(QFontEngineWin *fe, const QFontDef &request, const QFontPrivate *fp)
+{
+ fe->fontDef = request; // most settings are equal
+
+ HDC dc = ((request.styleStrategy & QFont::PreferDevice) && fp->hdc) ? fp->hdc : shared_dc();
+ SelectObject(dc, fe->hfont);
+ QT_WA({
+ TCHAR n[64];
+ GetTextFaceW(dc, 64, n);
+ fe->fontDef.family = QString::fromUtf16((ushort*)n);
+ fe->fontDef.fixedPitch = !(fe->tm.w.tmPitchAndFamily & TMPF_FIXED_PITCH);
+ } , {
+ char an[64];
+ GetTextFaceA(dc, 64, an);
+ fe->fontDef.family = QString::fromLocal8Bit(an);
+ fe->fontDef.fixedPitch = !(fe->tm.a.tmPitchAndFamily & TMPF_FIXED_PITCH);
+ });
+ if (fe->fontDef.pointSize < 0) {
+ fe->fontDef.pointSize = fe->fontDef.pixelSize * 72. / fp->dpi;
+ } else if (fe->fontDef.pixelSize == -1) {
+ fe->fontDef.pixelSize = qRound(fe->fontDef.pointSize * fp->dpi / 72.);
+ }
+}
+
+
+static const char *other_tryFonts[] = {
+ "Arial",
+ "MS UI Gothic",
+ "Gulim",
+ "SimSun",
+ "PMingLiU",
+ "Arial Unicode MS",
+ 0
+};
+
+static const char *jp_tryFonts [] = {
+ "MS UI Gothic",
+ "Arial",
+ "Gulim",
+ "SimSun",
+ "PMingLiU",
+ "Arial Unicode MS",
+ 0
+};
+
+static const char *ch_CN_tryFonts [] = {
+ "SimSun",
+ "Arial",
+ "PMingLiU",
+ "Gulim",
+ "MS UI Gothic",
+ "Arial Unicode MS",
+ 0
+};
+
+static const char *ch_TW_tryFonts [] = {
+ "PMingLiU",
+ "Arial",
+ "SimSun",
+ "Gulim",
+ "MS UI Gothic",
+ "Arial Unicode MS",
+ 0
+};
+
+static const char *kr_tryFonts[] = {
+ "Gulim",
+ "Arial",
+ "PMingLiU",
+ "SimSun",
+ "MS UI Gothic",
+ "Arial Unicode MS",
+ 0
+};
+
+static const char **tryFonts = 0;
+
+
+static inline HFONT systemFont()
+{
+ if (stock_sysfont == 0)
+ stock_sysfont = (HFONT)GetStockObject(SYSTEM_FONT);
+ return stock_sysfont;
+}
+
+#if !defined(DEFAULT_GUI_FONT)
+#define DEFAULT_GUI_FONT 17
+#endif
+
+static
+QFontEngine *loadEngine(int script, const QFontPrivate *fp, const QFontDef &request, const QtFontDesc *desc,
+ const QStringList &family_list)
+{
+ LOGFONT lf;
+ memset(&lf, 0, sizeof(LOGFONT));
+
+ bool useDevice = (request.styleStrategy & QFont::PreferDevice) && fp->hdc;
+
+ HDC hdc = shared_dc();
+ QString font_name = desc->family->name;
+
+ if (useDevice) {
+ hdc = fp->hdc;
+ font_name = request.family;
+ }
+
+ bool stockFont = false;
+
+ HFONT hfont = 0;
+
+ if (fp->rawMode) { // will choose a stock font
+ int f, deffnt;
+ // ### why different?
+ if ((QSysInfo::WindowsVersion & QSysInfo::WV_NT_based) || QSysInfo::WindowsVersion == QSysInfo::WV_32s)
+ deffnt = SYSTEM_FONT;
+ else
+ deffnt = DEFAULT_GUI_FONT;
+ QString fam = desc->family->name.toLower();
+ if (fam == QLatin1String("default"))
+ f = deffnt;
+ else if (fam == QLatin1String("system"))
+ f = SYSTEM_FONT;
+#ifndef Q_OS_WINCE
+ else if (fam == QLatin1String("system_fixed"))
+ f = SYSTEM_FIXED_FONT;
+ else if (fam == QLatin1String("ansi_fixed"))
+ f = ANSI_FIXED_FONT;
+ else if (fam == QLatin1String("ansi_var"))
+ f = ANSI_VAR_FONT;
+ else if (fam == QLatin1String("device_default"))
+ f = DEVICE_DEFAULT_FONT;
+ else if (fam == QLatin1String("oem_fixed"))
+ f = OEM_FIXED_FONT;
+#endif
+ else if (fam[0] == QLatin1Char('#'))
+ f = fam.right(fam.length()-1).toInt();
+ else
+ f = deffnt;
+ hfont = (HFONT)GetStockObject(f);
+ if (!hfont) {
+ qErrnoWarning("QFontEngine::loadEngine: GetStockObject failed");
+ hfont = systemFont();
+ }
+ stockFont = true;
+ } else {
+
+ int hint = FF_DONTCARE;
+ switch (request.styleHint) {
+ case QFont::Helvetica:
+ hint = FF_SWISS;
+ break;
+ case QFont::Times:
+ hint = FF_ROMAN;
+ break;
+ case QFont::Courier:
+ hint = FF_MODERN;
+ break;
+ case QFont::OldEnglish:
+ hint = FF_DECORATIVE;
+ break;
+ case QFont::System:
+ hint = FF_MODERN;
+ break;
+ default:
+ break;
+ }
+
+ lf.lfHeight = -request.pixelSize;
+ lf.lfWidth = 0;
+ lf.lfEscapement = 0;
+ lf.lfOrientation = 0;
+ if (desc->style->key.weight == 50)
+ lf.lfWeight = FW_DONTCARE;
+ else
+ lf.lfWeight = (desc->style->key.weight*900)/99;
+ lf.lfItalic = (desc->style->key.style != QFont::StyleNormal);
+ lf.lfCharSet = DEFAULT_CHARSET;
+
+ int strat = OUT_DEFAULT_PRECIS;
+ if (request.styleStrategy & QFont::PreferBitmap) {
+ strat = OUT_RASTER_PRECIS;
+#ifndef Q_OS_WINCE
+ } else if (request.styleStrategy & QFont::PreferDevice) {
+ strat = OUT_DEVICE_PRECIS;
+ } else if (request.styleStrategy & QFont::PreferOutline) {
+ QT_WA({
+ strat = OUT_OUTLINE_PRECIS;
+ } , {
+ strat = OUT_TT_PRECIS;
+ });
+ } else if (request.styleStrategy & QFont::ForceOutline) {
+ strat = OUT_TT_ONLY_PRECIS;
+#endif
+ }
+
+ lf.lfOutPrecision = strat;
+
+ int qual = DEFAULT_QUALITY;
+
+ if (request.styleStrategy & QFont::PreferMatch)
+ qual = DRAFT_QUALITY;
+#ifndef Q_OS_WINCE
+ else if (request.styleStrategy & QFont::PreferQuality)
+ qual = PROOF_QUALITY;
+#endif
+
+ if (request.styleStrategy & QFont::PreferAntialias) {
+ if (QSysInfo::WindowsVersion >= QSysInfo::WV_XP)
+ qual = 5; // == CLEARTYPE_QUALITY;
+ else
+ qual = ANTIALIASED_QUALITY;
+ } else if (request.styleStrategy & QFont::NoAntialias) {
+ qual = NONANTIALIASED_QUALITY;
+ }
+
+ lf.lfQuality = qual;
+
+ lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
+ lf.lfPitchAndFamily = DEFAULT_PITCH | hint;
+
+ QString fam = font_name;
+
+ if(fam.isEmpty())
+ fam = QLatin1String("MS Sans Serif");
+
+ if ((fam == QLatin1String("MS Sans Serif"))
+ && (request.style == QFont::StyleItalic || (-lf.lfHeight > 18 && -lf.lfHeight != 24))) {
+ fam = QLatin1String("Arial"); // MS Sans Serif has bearing problems in italic, and does not scale
+ }
+ if (fam == QLatin1String("Courier") && !(request.styleStrategy & QFont::PreferBitmap))
+ fam = QLatin1String("Courier New");
+
+ QT_WA({
+ memcpy(lf.lfFaceName, fam.utf16(), sizeof(TCHAR)*qMin(fam.length()+1,32)); // 32 = Windows hard-coded
+ hfont = CreateFontIndirect(&lf);
+ } , {
+ // LOGFONTA and LOGFONTW are binary compatible
+ QByteArray lname = fam.toLocal8Bit();
+ memcpy(lf.lfFaceName,lname.data(),
+ qMin(lname.length()+1,32)); // 32 = Windows hard-coded
+ hfont = CreateFontIndirectA((LOGFONTA*)&lf);
+ });
+ if (!hfont)
+ qErrnoWarning("QFontEngine::loadEngine: CreateFontIndirect failed");
+
+ stockFont = (hfont == 0);
+ bool ttf = false;
+ int avWidth = 0;
+ BOOL res;
+ HGDIOBJ oldObj = SelectObject(hdc, hfont);
+ QT_WA({
+ TEXTMETRICW tm;
+ res = GetTextMetricsW(hdc, &tm);
+ avWidth = tm.tmAveCharWidth;
+ ttf = tm.tmPitchAndFamily & TMPF_TRUETYPE;
+ } , {
+ TEXTMETRICA tm;
+ res = GetTextMetricsA(hdc, &tm);
+ avWidth = tm.tmAveCharWidth;
+ ttf = tm.tmPitchAndFamily & TMPF_TRUETYPE;
+ });
+ SelectObject(hdc, oldObj);
+
+ if (hfont && (!ttf || request.stretch != 100)) {
+ DeleteObject(hfont);
+ if (!res)
+ qErrnoWarning("QFontEngine::loadEngine: GetTextMetrics failed");
+ lf.lfWidth = avWidth * request.stretch/100;
+ QT_WA({
+ hfont = CreateFontIndirect(&lf);
+ } , {
+ hfont = CreateFontIndirectA((LOGFONTA*)&lf);
+ });
+ if (!hfont)
+ qErrnoWarning("QFontEngine::loadEngine: CreateFontIndirect with stretch failed");
+ }
+
+#ifndef Q_OS_WINCE
+ if (hfont == 0) {
+ hfont = (HFONT)GetStockObject(ANSI_VAR_FONT);
+ stockFont = true;
+ }
+#else
+ if (hfont == 0) {
+ hfont = (HFONT)GetStockObject(SYSTEM_FONT);
+ stockFont = true;
+ }
+#endif
+
+ }
+ QFontEngineWin *few = new QFontEngineWin(font_name, hfont, stockFont, lf);
+
+ // Also check for OpenType tables when using complex scripts
+ // ### TODO: This only works for scripts that require OpenType. More generally
+ // for scripts that do not require OpenType we should just look at the list of
+ // supported writing systems in the font's OS/2 table.
+ if (scriptRequiresOpenType(script)) {
+ HB_Face hbFace = few->harfbuzzFace();
+ if (!hbFace || !hbFace->supported_scripts[script]) {
+ FM_DEBUG(" OpenType support missing for script\n");
+ delete few;
+ return 0;
+ }
+ }
+
+ QFontEngine *fe = few;
+ initFontInfo(few, request, fp);
+ if(script == QUnicodeTables::Common
+ && !(request.styleStrategy & QFont::NoFontMerging)
+ && !(desc->family->writingSystems[QFontDatabase::Symbol] & QtFontFamily::Supported)) {
+ if(!tryFonts) {
+ LANGID lid = GetUserDefaultLangID();
+ switch( lid&0xff ) {
+ case LANG_CHINESE: // Chinese (Taiwan)
+ if ( lid == 0x0804 ) // Taiwan
+ tryFonts = ch_TW_tryFonts;
+ else
+ tryFonts = ch_CN_tryFonts;
+ break;
+ case LANG_JAPANESE:
+ tryFonts = jp_tryFonts;
+ break;
+ case LANG_KOREAN:
+ tryFonts = kr_tryFonts;
+ break;
+ default:
+ tryFonts = other_tryFonts;
+ break;
+ }
+ }
+ QStringList fm = QFontDatabase().families();
+ QStringList list = family_list;
+ const char **tf = tryFonts;
+ while(tf && *tf) {
+ if(fm.contains(QLatin1String(*tf)))
+ list << QLatin1String(*tf);
+ ++tf;
+ }
+ QFontEngine *mfe = new QFontEngineMultiWin(few, list);
+ mfe->fontDef = fe->fontDef;
+ fe = mfe;
+ }
+ return fe;
+}
+
+const char *styleHint(const QFontDef &request)
+{
+ const char *stylehint = 0;
+ switch (request.styleHint) {
+ case QFont::SansSerif:
+ stylehint = "Arial";
+ break;
+ case QFont::Serif:
+ stylehint = "Times New Roman";
+ break;
+ case QFont::TypeWriter:
+ stylehint = "Courier New";
+ break;
+ default:
+ if (request.fixedPitch)
+ stylehint = "Courier New";
+ break;
+ }
+ return stylehint;
+}
+
+static QFontEngine *loadWin(const QFontPrivate *d, int script, const QFontDef &req)
+{
+ // list of families to try
+ QStringList family_list = familyList(req);
+
+ if(QSysInfo::WindowsVersion & QSysInfo::WV_DOS_based && req.family.toLower() == QLatin1String("ms sans serif")) {
+ // small hack for Dos based machines to get the right font for non
+ // latin text when using the default font.
+ family_list << QLatin1String("Arial");
+ }
+
+ const char *stylehint = styleHint(d->request);
+ if (stylehint)
+ family_list << QLatin1String(stylehint);
+
+ // append the default fallback font for the specified script
+ // family_list << ... ; ###########
+
+ // add the default family
+ QString defaultFamily = QApplication::font().family();
+ if (! family_list.contains(defaultFamily))
+ family_list << defaultFamily;
+
+ // add QFont::defaultFamily() to the list, for compatibility with
+ // previous versions
+ family_list << QApplication::font().defaultFamily();
+
+ // null family means find the first font matching the specified script
+ family_list << QString();
+
+ QtFontDesc desc;
+ QFontEngine *fe = 0;
+ QList<int> blacklistedFamilies;
+
+ while (!fe) {
+ for (int i = 0; i < family_list.size(); ++i) {
+ QString family, foundry;
+ parseFontName(family_list.at(i), foundry, family);
+ FM_DEBUG("loadWin: >>>>>>>>>>>>>>trying to match '%s'", family.toLatin1().data());
+ QT_PREPEND_NAMESPACE(match)(script, req, family, foundry, -1, &desc, blacklistedFamilies);
+ if (desc.family)
+ break;
+ }
+ if (!desc.family)
+ break;
+ fe = loadEngine(script, d, req, &desc, family_list);
+ if (!fe)
+ blacklistedFamilies.append(desc.familyIndex);
+ }
+ return fe;
+}
+
+
+void QFontDatabase::load(const QFontPrivate *d, int script)
+{
+ // sanity checks
+ if (!qApp)
+ qWarning("QFontDatabase::load: Must construct QApplication first");
+ Q_ASSERT(script >= 0 && script < QUnicodeTables::ScriptCount);
+
+ // normalize the request to get better caching
+ QFontDef req = d->request;
+ if (req.pixelSize <= 0)
+ req.pixelSize = qMax(1, qRound(req.pointSize * d->dpi / 72.));
+ req.pointSize = 0;
+ if (req.weight == 0)
+ req.weight = QFont::Normal;
+ if (req.stretch == 0)
+ req.stretch = 100;
+
+ QFontCache::Key key(req, d->rawMode ? QUnicodeTables::Common : script, d->screen);
+ if (!d->engineData)
+ getEngineData(d, key);
+
+ // the cached engineData could have already loaded the engine we want
+ if (d->engineData->engines[script])
+ return;
+
+ QFontEngine *fe = QFontCache::instance()->findEngine(key);
+
+ // set it to the actual pointsize, so QFontInfo will do the right thing
+ req.pointSize = req.pixelSize*72./d->dpi;
+
+ if (!fe) {
+ if (qt_enable_test_font && req.family == QLatin1String("__Qt__Box__Engine__")) {
+ fe = new QTestFontEngine(req.pixelSize);
+ fe->fontDef = req;
+ } else {
+ QMutexLocker locker(fontDatabaseMutex());
+ if (!privateDb()->count)
+ initializeDb();
+ fe = loadWin(d, script, req);
+ }
+ if (!fe) {
+ fe = new QFontEngineBox(req.pixelSize);
+ fe->fontDef = QFontDef();
+ }
+ }
+ d->engineData->engines[script] = fe;
+ fe->ref.ref();
+ QFontCache::instance()->insertEngine(key, fe);
+}
+
+#if !defined(FR_PRIVATE)
+#define FR_PRIVATE 0x10
+#endif
+
+typedef int (WINAPI *PtrAddFontResourceExW)(LPCWSTR, DWORD, PVOID);
+typedef HANDLE (WINAPI *PtrAddFontMemResourceEx)(PVOID, DWORD, PVOID, DWORD *);
+typedef BOOL (WINAPI *PtrRemoveFontResourceExW)(LPCWSTR, DWORD, PVOID);
+typedef BOOL (WINAPI *PtrRemoveFontMemResourceEx)(HANDLE);
+
+static QList<quint32> getTrueTypeFontOffsets(const uchar *fontData)
+{
+ QList<quint32> offsets;
+ const quint32 headerTag = *reinterpret_cast<const quint32 *>(fontData);
+ if (headerTag != MAKE_TAG('t', 't', 'c', 'f')) {
+ if (headerTag != MAKE_TAG(0, 1, 0, 0)
+ && headerTag != MAKE_TAG('O', 'T', 'T', 'O')
+ && headerTag != MAKE_TAG('t', 'r', 'u', 'e')
+ && headerTag != MAKE_TAG('t', 'y', 'p', '1'))
+ return offsets;
+ offsets << 0;
+ return offsets;
+ }
+ const quint32 numFonts = qFromBigEndian<quint32>(fontData + 8);
+ for (uint i = 0; i < numFonts; ++i) {
+ offsets << qFromBigEndian<quint32>(fontData + 12 + i * 4);
+ }
+ return offsets;
+}
+
+static void getFontTable(const uchar *fileBegin, const uchar *data, quint32 tag, const uchar **table, quint32 *length)
+{
+ const quint16 numTables = qFromBigEndian<quint16>(data + 4);
+ for (uint i = 0; i < numTables; ++i) {
+ const quint32 offset = 12 + 16 * i;
+ if (*reinterpret_cast<const quint32 *>(data + offset) == tag) {
+ *table = fileBegin + qFromBigEndian<quint32>(data + offset + 8);
+ *length = qFromBigEndian<quint32>(data + offset + 12);
+ return;
+ }
+ }
+ *table = 0;
+ *length = 0;
+ return;
+}
+
+static void getFamiliesAndSignatures(const QByteArray &fontData, QFontDatabasePrivate::ApplicationFont *appFont)
+{
+ const uchar *data = reinterpret_cast<const uchar *>(fontData.constData());
+
+ QList<quint32> offsets = getTrueTypeFontOffsets(data);
+ if (offsets.isEmpty())
+ return;
+
+ for (int i = 0; i < offsets.count(); ++i) {
+ const uchar *font = data + offsets.at(i);
+ const uchar *table;
+ quint32 length;
+ getFontTable(data, font, MAKE_TAG('n', 'a', 'm', 'e'), &table, &length);
+ if (!table)
+ continue;
+ QString name = getEnglishName(table, length);
+ if (name.isEmpty())
+ continue;
+
+ appFont->families << name;
+ FONTSIGNATURE signature;
+ getFontTable(data, font, MAKE_TAG('O', 'S', '/', '2'), &table, &length);
+ if (table && length >= 86) {
+ // See also qfontdatabase_mac.cpp, offsets taken from OS/2 table in the TrueType spec
+ signature.fsUsb[0] = qFromBigEndian<quint32>(table + 42);
+ signature.fsUsb[1] = qFromBigEndian<quint32>(table + 46);
+ signature.fsUsb[2] = qFromBigEndian<quint32>(table + 50);
+ signature.fsUsb[3] = qFromBigEndian<quint32>(table + 54);
+
+ signature.fsCsb[0] = qFromBigEndian<quint32>(table + 78);
+ signature.fsCsb[1] = qFromBigEndian<quint32>(table + 82);
+ }
+ appFont->signatures << signature;
+ }
+}
+
+static void registerFont(QFontDatabasePrivate::ApplicationFont *fnt)
+{
+ if(!fnt->data.isEmpty()) {
+#ifndef Q_OS_WINCE
+ PtrAddFontMemResourceEx ptrAddFontMemResourceEx = (PtrAddFontMemResourceEx)QLibrary::resolve(QLatin1String("gdi32"),
+ "AddFontMemResourceEx");
+ if (!ptrAddFontMemResourceEx)
+ return;
+#endif
+ getFamiliesAndSignatures(fnt->data, fnt);
+ if (fnt->families.isEmpty())
+ return;
+
+#ifdef Q_OS_WINCE
+ HANDLE handle = 0;
+
+ {
+#ifdef QT_NO_TEMPORARYFILE
+ TCHAR lpBuffer[MAX_PATH];
+ GetTempPath(MAX_PATH, lpBuffer);
+ QString s = QString::fromUtf16((const ushort *) lpBuffer);
+ QFile tempfile(s + QLatin1String("/font") + QString::number(GetTickCount()) + QLatin1String(".ttf"));
+ if (!tempfile.open(QIODevice::ReadWrite))
+#else
+ QTemporaryFile tempfile(QLatin1String("XXXXXXXX.ttf"));
+ if (!tempfile.open())
+#endif
+ return;
+ if (tempfile.write(fnt->data) == -1)
+ return;
+
+#ifndef QT_NO_TEMPORARYFILE
+ tempfile.setAutoRemove(false);
+#endif
+ fnt->fileName = QFileInfo(tempfile.fileName()).absoluteFilePath();
+ }
+
+ if (AddFontResource((LPCWSTR)fnt->fileName.utf16()) == 0) {
+ QFile(fnt->fileName).remove();
+ return;
+ }
+#else
+ DWORD dummy = 0;
+ HANDLE handle = ptrAddFontMemResourceEx((void *)fnt->data.constData(),
+ fnt->data.size(),
+ 0,
+ &dummy);
+ if (handle == 0)
+ return;
+#endif
+
+ fnt->handle = handle;
+ fnt->data = QByteArray();
+ fnt->memoryFont = true;
+ } else {
+ QFile f(fnt->fileName);
+ if (!f.open(QIODevice::ReadOnly))
+ return;
+ QByteArray data = f.readAll();
+ f.close();
+ getFamiliesAndSignatures(data, fnt);
+
+#ifdef Q_OS_WINCE
+ QFileInfo fileinfo(fnt->fileName);
+ fnt->fileName = fileinfo.absoluteFilePath();
+ if (AddFontResource((LPCWSTR)fnt->fileName.utf16()) == 0)
+ return;
+#else
+ // supported from 2000 on, so no need to deal with the *A variant
+ PtrAddFontResourceExW ptrAddFontResourceExW = (PtrAddFontResourceExW)QLibrary::resolve(QLatin1String("gdi32"),
+ "AddFontResourceExW");
+ if (!ptrAddFontResourceExW)
+ return;
+
+ if (ptrAddFontResourceExW((LPCWSTR)fnt->fileName.utf16(), FR_PRIVATE, 0) == 0)
+ return;
+#endif
+
+ fnt->memoryFont = false;
+ }
+}
+
+bool QFontDatabase::removeApplicationFont(int handle)
+{
+ QMutexLocker locker(fontDatabaseMutex());
+
+ QFontDatabasePrivate *db = privateDb();
+ if (handle < 0 || handle >= db->applicationFonts.count())
+ return false;
+
+ const QFontDatabasePrivate::ApplicationFont font = db->applicationFonts.at(handle);
+ db->applicationFonts[handle] = QFontDatabasePrivate::ApplicationFont();
+ if (font.memoryFont) {
+#ifdef Q_OS_WINCE
+ bool removeSucceeded = RemoveFontResource((LPCWSTR)font.fileName.utf16());
+ QFile tempfile(font.fileName);
+ tempfile.remove();
+ if (!removeSucceeded)
+ return false;
+#else
+ PtrRemoveFontMemResourceEx ptrRemoveFontMemResourceEx = (PtrRemoveFontMemResourceEx)QLibrary::resolve(QLatin1String("gdi32"),
+ "RemoveFontMemResourceEx");
+ if (!ptrRemoveFontMemResourceEx)
+ return false;
+
+ if (!ptrRemoveFontMemResourceEx(font.handle))
+ return false;
+#endif
+ } else {
+#ifdef Q_OS_WINCE
+ if (!RemoveFontResource((LPCWSTR)font.fileName.utf16()))
+ return false;
+#else
+ PtrRemoveFontResourceExW ptrRemoveFontResourceExW = (PtrRemoveFontResourceExW)QLibrary::resolve(QLatin1String("gdi32"),
+ "RemoveFontResourceExW");
+ if (!ptrRemoveFontResourceExW)
+ return false;
+
+ if (!ptrRemoveFontResourceExW((LPCWSTR)font.fileName.utf16(), FR_PRIVATE, 0))
+ return false;
+#endif
+ }
+
+ db->invalidate();
+ return true;
+}
+
+bool QFontDatabase::removeAllApplicationFonts()
+{
+ QMutexLocker locker(fontDatabaseMutex());
+
+ QFontDatabasePrivate *db = privateDb();
+ for (int i = 0; i < db->applicationFonts.count(); ++i)
+ if (!removeApplicationFont(i))
+ return false;
+ return true;
+}
+
+bool QFontDatabase::supportsThreadedFontRendering()
+{
+ return true;
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/text/qfontdatabase_x11.cpp b/src/gui/text/qfontdatabase_x11.cpp
new file mode 100644
index 0000000000..15e626ea02
--- /dev/null
+++ b/src/gui/text/qfontdatabase_x11.cpp
@@ -0,0 +1,2064 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <qplatformdefs.h>
+
+#include <qdatetime.h>
+#include <qdebug.h>
+#include <qpaintdevice.h>
+
+#include <private/qt_x11_p.h>
+#include "qx11info_x11.h"
+#include <qdebug.h>
+#include <qfile.h>
+#include <qtemporaryfile.h>
+#include <qabstractfileengine.h>
+
+#include <ctype.h>
+#include <stdlib.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <sys/mman.h>
+
+#include <private/qfontengine_x11_p.h>
+
+#ifndef QT_NO_FONTCONFIG
+#include <ft2build.h>
+#include FT_FREETYPE_H
+
+#if FC_VERSION >= 20402
+#include <fontconfig/fcfreetype.h>
+#endif
+#endif
+
+QT_BEGIN_NAMESPACE
+
+// from qfont_x11.cpp
+extern double qt_pointSize(double pixelSize, int dpi);
+extern double qt_pixelSize(double pointSize, int dpi);
+
+static inline void capitalize (char *s)
+{
+ bool space = true;
+ while(*s) {
+ if (space)
+ *s = toupper(*s);
+ space = (*s == ' ');
+ ++s;
+ }
+}
+
+
+/*
+ To regenerate the writingSystems_for_xlfd_encoding table, run
+ 'util/unicode/x11/makeencodings' and paste the generated
+ 'encodings.c' here.
+*/
+// ----- begin of generated code -----
+
+#define make_tag( c1, c2, c3, c4 ) \
+ ((((unsigned int)c1)<<24) | (((unsigned int)c2)<<16) | \
+ (((unsigned int)c3)<<8) | ((unsigned int)c4))
+
+struct XlfdEncoding {
+ const char *name;
+ int id;
+ int mib;
+ unsigned int hash1;
+ unsigned int hash2;
+};
+
+static const XlfdEncoding xlfd_encoding[] = {
+ { "iso8859-1", 0, 4, make_tag('i','s','o','8'), make_tag('5','9','-','1') },
+ { "iso8859-2", 1, 5, make_tag('i','s','o','8'), make_tag('5','9','-','2') },
+ { "iso8859-3", 2, 6, make_tag('i','s','o','8'), make_tag('5','9','-','3') },
+ { "iso8859-4", 3, 7, make_tag('i','s','o','8'), make_tag('5','9','-','4') },
+ { "iso8859-9", 4, 12, make_tag('i','s','o','8'), make_tag('5','9','-','9') },
+ { "iso8859-10", 5, 13, make_tag('i','s','o','8'), make_tag('9','-','1','0') },
+ { "iso8859-13", 6, 109, make_tag('i','s','o','8'), make_tag('9','-','1','3') },
+ { "iso8859-14", 7, 110, make_tag('i','s','o','8'), make_tag('9','-','1','4') },
+ { "iso8859-15", 8, 111, make_tag('i','s','o','8'), make_tag('9','-','1','5') },
+ { "hp-roman8", 9, 2004, make_tag('h','p','-','r'), make_tag('m','a','n','8') },
+ { "iso8859-5", 10, 8, make_tag('i','s','o','8'), make_tag('5','9','-','5') },
+ { "*-cp1251", 11, 2251, 0, make_tag('1','2','5','1') },
+ { "koi8-ru", 12, 2084, make_tag('k','o','i','8'), make_tag('8','-','r','u') },
+ { "koi8-u", 13, 2088, make_tag('k','o','i','8'), make_tag('i','8','-','u') },
+ { "koi8-r", 14, 2084, make_tag('k','o','i','8'), make_tag('i','8','-','r') },
+ { "iso8859-7", 15, 10, make_tag('i','s','o','8'), make_tag('5','9','-','7') },
+ { "iso8859-8", 16, 85, make_tag('i','s','o','8'), make_tag('5','9','-','8') },
+ { "gb18030-0", 17, -114, make_tag('g','b','1','8'), make_tag('3','0','-','0') },
+ { "gb18030.2000-0", 18, -113, make_tag('g','b','1','8'), make_tag('0','0','-','0') },
+ { "gbk-0", 19, -113, make_tag('g','b','k','-'), make_tag('b','k','-','0') },
+ { "gb2312.*-0", 20, 57, make_tag('g','b','2','3'), 0 },
+ { "jisx0201*-0", 21, 15, make_tag('j','i','s','x'), 0 },
+ { "jisx0208*-0", 22, 63, make_tag('j','i','s','x'), 0 },
+ { "ksc5601*-*", 23, 36, make_tag('k','s','c','5'), 0 },
+ { "big5hkscs-0", 24, -2101, make_tag('b','i','g','5'), make_tag('c','s','-','0') },
+ { "hkscs-1", 25, -2101, make_tag('h','k','s','c'), make_tag('c','s','-','1') },
+ { "big5*-*", 26, -2026, make_tag('b','i','g','5'), 0 },
+ { "tscii-*", 27, 2028, make_tag('t','s','c','i'), 0 },
+ { "tis620*-*", 28, 2259, make_tag('t','i','s','6'), 0 },
+ { "iso8859-11", 29, 2259, make_tag('i','s','o','8'), make_tag('9','-','1','1') },
+ { "mulelao-1", 30, -4242, make_tag('m','u','l','e'), make_tag('a','o','-','1') },
+ { "ethiopic-unicode", 31, 0, make_tag('e','t','h','i'), make_tag('c','o','d','e') },
+ { "iso10646-1", 32, 0, make_tag('i','s','o','1'), make_tag('4','6','-','1') },
+ { "unicode-*", 33, 0, make_tag('u','n','i','c'), 0 },
+ { "*-symbol", 34, 0, 0, make_tag('m','b','o','l') },
+ { "*-fontspecific", 35, 0, 0, make_tag('i','f','i','c') },
+ { "fontspecific-*", 36, 0, make_tag('f','o','n','t'), 0 },
+ { 0, 0, 0, 0, 0 }
+};
+
+static const char writingSystems_for_xlfd_encoding[sizeof(xlfd_encoding)][QFontDatabase::WritingSystemsCount] = {
+ // iso8859-1
+ { 0, 1, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0 },
+ // iso8859-2
+ { 0, 1, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0 },
+ // iso8859-3
+ { 0, 1, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0 },
+ // iso8859-4
+ { 0, 1, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0 },
+ // iso8859-9
+ { 0, 1, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0 },
+ // iso8859-10
+ { 0, 1, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0 },
+ // iso8859-13
+ { 0, 1, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0 },
+ // iso8859-14
+ { 0, 1, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0 },
+ // iso8859-15
+ { 0, 1, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0 },
+ // hp-roman8
+ { 0, 1, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0 },
+ // iso8859-5
+ { 0, 0, 0, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0 },
+ // *-cp1251
+ { 0, 0, 0, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0 },
+ // koi8-ru
+ { 0, 0, 0, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0 },
+ // koi8-u
+ { 0, 0, 0, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0 },
+ // koi8-r
+ { 0, 0, 0, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0 },
+ // iso8859-7
+ { 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0 },
+ // iso8859-8
+ { 0, 0, 0, 0, 0, 1, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0 },
+ // gb18030-0
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 1, 0, 0, 0, 0,
+ 0 },
+ // gb18030.2000-0
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 1, 0, 0, 0, 0,
+ 0 },
+ // gbk-0
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 1, 0, 0, 0, 0,
+ 0 },
+ // gb2312.*-0
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 1, 0, 0, 0, 0,
+ 0 },
+ // jisx0201*-0
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0,
+ 0 },
+ // jisx0208*-0
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0,
+ 0 },
+ // ksc5601*-*
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 1, 0,
+ 0 },
+ // big5hkscs-0
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 1, 0, 0, 0,
+ 0 },
+ // hkscs-1
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 1, 0, 0, 0,
+ 0 },
+ // big5*-*
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 1, 0, 0, 0,
+ 0 },
+ // tscii-*
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0 },
+ // tis620*-*
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0 },
+ // iso8859-11
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0 },
+ // mulelao-1
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0 },
+ // ethiopic-unicode
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0 },
+ // iso10646-1
+ { 0, 1, 1, 1, 1, 1, 1, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
+ 1, 1, 0, 1, 0, 1, 1, 0, 0, 0,
+ 0 },
+ // unicode-*
+ { 0, 1, 1, 1, 1, 1, 1, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
+ 1, 1, 0, 1, 0, 1, 1, 0, 0, 0,
+ 0 },
+ // *-symbol
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 1 },
+ // *-fontspecific
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 1 },
+ // fontspecific-*
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 1 }
+
+};
+
+// ----- end of generated code -----
+
+
+const int numEncodings = sizeof(xlfd_encoding) / sizeof(XlfdEncoding) - 1;
+
+int qt_xlfd_encoding_id(const char *encoding)
+{
+ // qDebug("looking for encoding id for '%s'", encoding);
+ int len = strlen(encoding);
+ if (len < 4)
+ return -1;
+ unsigned int hash1 = make_tag(encoding[0], encoding[1], encoding[2], encoding[3]);
+ const char *ch = encoding + len - 4;
+ unsigned int hash2 = make_tag(ch[0], ch[1], ch[2], ch[3]);
+
+ const XlfdEncoding *enc = xlfd_encoding;
+ for (; enc->name; ++enc) {
+ if ((enc->hash1 && enc->hash1 != hash1) ||
+ (enc->hash2 && enc->hash2 != hash2))
+ continue;
+ // hashes match, do a compare if strings match
+ // the enc->name can contain '*'s we have to interpret correctly
+ const char *n = enc->name;
+ const char *e = encoding;
+ while (1) {
+ // qDebug("bol: *e='%c', *n='%c'", *e, *n);
+ if (*e == '\0') {
+ if (*n)
+ break;
+ // qDebug("found encoding id %d", enc->id);
+ return enc->id;
+ }
+ if (*e == *n) {
+ ++e;
+ ++n;
+ continue;
+ }
+ if (*n != '*')
+ break;
+ ++n;
+ // qDebug("skip: *e='%c', *n='%c'", *e, *n);
+ while (*e && *e != *n)
+ ++e;
+ }
+ }
+ // qDebug("couldn't find encoding %s", encoding);
+ return -1;
+}
+
+int qt_mib_for_xlfd_encoding(const char *encoding)
+{
+ int id = qt_xlfd_encoding_id(encoding);
+ if (id != -1) return xlfd_encoding[id].mib;
+ return 0;
+};
+
+int qt_encoding_id_for_mib(int mib)
+{
+ const XlfdEncoding *enc = xlfd_encoding;
+ for (; enc->name; ++enc) {
+ if (enc->mib == mib)
+ return enc->id;
+ }
+ return -1;
+}
+
+static const char * xlfd_for_id(int id)
+{
+ // special case: -1 returns the "*-*" encoding, allowing us to do full
+ // database population in a single X server round trip.
+ if (id < 0 || id > numEncodings)
+ return "*-*";
+ return xlfd_encoding[id].name;
+}
+
+enum XLFDFieldNames {
+ Foundry,
+ Family,
+ Weight,
+ Slant,
+ Width,
+ AddStyle,
+ PixelSize,
+ PointSize,
+ ResolutionX,
+ ResolutionY,
+ Spacing,
+ AverageWidth,
+ CharsetRegistry,
+ CharsetEncoding,
+ NFontFields
+};
+
+// Splits an X font name into fields separated by '-'
+static bool parseXFontName(char *fontName, char **tokens)
+{
+ if (! fontName || fontName[0] == '0' || fontName[0] != '-') {
+ tokens[0] = 0;
+ return false;
+ }
+
+ int i;
+ ++fontName;
+ for (i = 0; i < NFontFields && fontName && fontName[0]; ++i) {
+ tokens[i] = fontName;
+ for (;; ++fontName) {
+ if (*fontName == '-')
+ break;
+ if (! *fontName) {
+ fontName = 0;
+ break;
+ }
+ }
+
+ if (fontName) *fontName++ = '\0';
+ }
+
+ if (i < NFontFields) {
+ for (int j = i ; j < NFontFields; ++j)
+ tokens[j] = 0;
+ return false;
+ }
+
+ return true;
+}
+
+static inline bool isZero(char *x)
+{
+ return (x[0] == '0' && x[1] == 0);
+}
+
+static inline bool isScalable(char **tokens)
+{
+ return (isZero(tokens[PixelSize]) &&
+ isZero(tokens[PointSize]) &&
+ isZero(tokens[AverageWidth]));
+}
+
+static inline bool isSmoothlyScalable(char **tokens)
+{
+ return (isZero(tokens[ResolutionX]) &&
+ isZero(tokens[ResolutionY]));
+}
+
+static inline bool isFixedPitch(char **tokens)
+{
+ return (tokens[Spacing][0] == 'm' ||
+ tokens[Spacing][0] == 'c' ||
+ tokens[Spacing][0] == 'M' ||
+ tokens[Spacing][0] == 'C');
+}
+
+/*
+ Fills in a font definition (QFontDef) from an XLFD (X Logical Font
+ Description).
+
+ Returns true if the the given xlfd is valid.
+*/
+bool qt_fillFontDef(const QByteArray &xlfd, QFontDef *fd, int dpi, QtFontDesc *desc)
+{
+ char *tokens[NFontFields];
+ QByteArray buffer = xlfd;
+ if (! parseXFontName(buffer.data(), tokens))
+ return false;
+
+ capitalize(tokens[Family]);
+ capitalize(tokens[Foundry]);
+
+ fd->styleStrategy |= QFont::NoAntialias;
+ fd->family = QString::fromLatin1(tokens[Family]);
+ QString foundry = QString::fromLatin1(tokens[Foundry]);
+ if (! foundry.isEmpty() && foundry != QString::fromLatin1("*") && (!desc || desc->family->count > 1))
+ fd->family +=
+ QString::fromLatin1(" [") + foundry + QString::fromLatin1("]");
+
+ if (qstrlen(tokens[AddStyle]) > 0)
+ fd->addStyle = QString::fromLatin1(tokens[AddStyle]);
+ else
+ fd->addStyle.clear();
+
+ fd->pointSize = atoi(tokens[PointSize])/10.;
+ fd->styleHint = QFont::AnyStyle; // ### any until we match families
+
+ char slant = tolower((uchar) tokens[Slant][0]);
+ fd->style = (slant == 'o' ? QFont::StyleOblique : (slant == 'i' ? QFont::StyleItalic : QFont::StyleNormal));
+ char fixed = tolower((uchar) tokens[Spacing][0]);
+ fd->fixedPitch = (fixed == 'm' || fixed == 'c');
+ fd->weight = getFontWeight(QLatin1String(tokens[Weight]));
+
+ int r = atoi(tokens[ResolutionY]);
+ fd->pixelSize = atoi(tokens[PixelSize]);
+ // not "0" or "*", or required DPI
+ if (r && fd->pixelSize && r != dpi) {
+ // calculate actual pointsize for display DPI
+ fd->pointSize = qt_pointSize(fd->pixelSize, dpi);
+ } else if (fd->pixelSize == 0 && fd->pointSize) {
+ // calculate pixel size from pointsize/dpi
+ fd->pixelSize = qRound(qt_pixelSize(fd->pointSize, dpi));
+ }
+
+ return true;
+}
+
+/*
+ Fills in a font definition (QFontDef) from the font properties in an
+ XFontStruct.
+
+ Returns true if the QFontDef could be filled with properties from
+ the XFontStruct.
+*/
+static bool qt_fillFontDef(XFontStruct *fs, QFontDef *fd, int dpi, QtFontDesc *desc)
+{
+ unsigned long value;
+ if (!fs || !XGetFontProperty(fs, XA_FONT, &value))
+ return false;
+
+ char *n = XGetAtomName(QX11Info::display(), value);
+ QByteArray xlfd(n);
+ if (n)
+ XFree(n);
+ return qt_fillFontDef(xlfd.toLower(), fd, dpi, desc);
+}
+
+
+static QtFontStyle::Key getStyle(char ** tokens)
+{
+ QtFontStyle::Key key;
+
+ char slant0 = tolower((uchar) tokens[Slant][0]);
+
+ if (slant0 == 'r') {
+ if (tokens[Slant][1]) {
+ char slant1 = tolower((uchar) tokens[Slant][1]);
+
+ if (slant1 == 'o')
+ key.style = QFont::StyleOblique;
+ else if (slant1 == 'i')
+ key.style = QFont::StyleItalic;
+ }
+ } else if (slant0 == 'o')
+ key.style = QFont::StyleOblique;
+ else if (slant0 == 'i')
+ key.style = QFont::StyleItalic;
+
+ key.weight = getFontWeight(QLatin1String(tokens[Weight]));
+
+ if (qstrcmp(tokens[Width], "normal") == 0) {
+ key.stretch = 100;
+ } else if (qstrcmp(tokens[Width], "semi condensed") == 0 ||
+ qstrcmp(tokens[Width], "semicondensed") == 0) {
+ key.stretch = 90;
+ } else if (qstrcmp(tokens[Width], "condensed") == 0) {
+ key.stretch = 80;
+ } else if (qstrcmp(tokens[Width], "narrow") == 0) {
+ key.stretch = 60;
+ }
+
+ return key;
+}
+
+
+static bool xlfdsFullyLoaded = false;
+static unsigned char encodingLoaded[numEncodings];
+
+static void loadXlfds(const char *reqFamily, int encoding_id)
+{
+ QFontDatabasePrivate *db = privateDb();
+ QtFontFamily *fontFamily = reqFamily ? db->family(QLatin1String(reqFamily)) : 0;
+
+ // make sure we don't load twice
+ if ((encoding_id == -1 && xlfdsFullyLoaded)
+ || (encoding_id != -1 && encodingLoaded[encoding_id]))
+ return;
+ if (fontFamily && fontFamily->xlfdLoaded)
+ return;
+
+ int fontCount;
+ // force the X server to give us XLFDs
+ QByteArray xlfd_pattern("-*-");
+ xlfd_pattern += (reqFamily && reqFamily[0] != '\0') ? reqFamily : "*";
+ xlfd_pattern += "-*-*-*-*-*-*-*-*-*-*-";
+ xlfd_pattern += xlfd_for_id(encoding_id);
+
+ char **fontList = XListFonts(QX11Info::display(),
+ xlfd_pattern,
+ 0xffff, &fontCount);
+ // qDebug("requesting xlfd='%s', got %d fonts", xlfd_pattern.data(), fontCount);
+
+
+ char *tokens[NFontFields];
+
+ for(int i = 0 ; i < fontCount ; i++) {
+ if (! parseXFontName(fontList[i], tokens))
+ continue;
+
+ // get the encoding_id for this xlfd. we need to do this
+ // here, since we can pass -1 to this function to do full
+ // database population
+ *(tokens[CharsetEncoding] - 1) = '-';
+ int encoding_id = qt_xlfd_encoding_id(tokens[CharsetRegistry]);
+ if (encoding_id == -1)
+ continue;
+
+ char *familyName = tokens[Family];
+ capitalize(familyName);
+ char *foundryName = tokens[Foundry];
+ capitalize(foundryName);
+ QtFontStyle::Key styleKey = getStyle(tokens);
+
+ bool smooth_scalable = false;
+ bool bitmap_scalable = false;
+ if (isScalable(tokens)) {
+ if (isSmoothlyScalable(tokens))
+ smooth_scalable = true;
+ else
+ bitmap_scalable = true;
+ }
+ uint pixelSize = atoi(tokens[PixelSize]);
+ uint xpointSize = atoi(tokens[PointSize]);
+ uint xres = atoi(tokens[ResolutionX]);
+ uint yres = atoi(tokens[ResolutionY]);
+ uint avgwidth = atoi(tokens[AverageWidth]);
+ bool fixedPitch = isFixedPitch(tokens);
+
+ if (avgwidth == 0 && pixelSize != 0) {
+ /*
+ Ignore bitmap scalable fonts that are automatically
+ generated by some X servers. We know they are bitmap
+ scalable because even though they have a specified pixel
+ size, the average width is zero.
+ */
+ continue;
+ }
+
+ QtFontFamily *family = fontFamily ? fontFamily : db->family(QLatin1String(familyName), true);
+ family->fontFileIndex = -1;
+ family->symbol_checked = true;
+ QtFontFoundry *foundry = family->foundry(QLatin1String(foundryName), true);
+ QtFontStyle *style = foundry->style(styleKey, true);
+
+ delete [] style->weightName;
+ style->weightName = qstrdup(tokens[Weight]);
+ delete [] style->setwidthName;
+ style->setwidthName = qstrdup(tokens[Width]);
+
+ if (smooth_scalable) {
+ style->smoothScalable = true;
+ style->bitmapScalable = false;
+ pixelSize = SMOOTH_SCALABLE;
+ }
+ if (!style->smoothScalable && bitmap_scalable)
+ style->bitmapScalable = true;
+ if (!fixedPitch)
+ family->fixedPitch = false;
+
+ QtFontSize *size = style->pixelSize(pixelSize, true);
+ QtFontEncoding *enc =
+ size->encodingID(encoding_id, xpointSize, xres, yres, avgwidth, true);
+ enc->pitch = *tokens[Spacing];
+ if (!enc->pitch) enc->pitch = '*';
+
+ for (int i = 0; i < QFontDatabase::WritingSystemsCount; ++i) {
+ if (writingSystems_for_xlfd_encoding[encoding_id][i])
+ family->writingSystems[i] = QtFontFamily::Supported;
+ }
+ }
+ if (!reqFamily) {
+ // mark encoding as loaded
+ if (encoding_id == -1)
+ xlfdsFullyLoaded = true;
+ else
+ encodingLoaded[encoding_id] = true;
+ }
+
+ XFreeFontNames(fontList);
+}
+
+
+#ifndef QT_NO_FONTCONFIG
+
+#ifndef FC_WIDTH
+#define FC_WIDTH "width"
+#endif
+
+static int getFCWeight(int fc_weight)
+{
+ int qtweight = QFont::Black;
+ if (fc_weight <= (FC_WEIGHT_LIGHT + FC_WEIGHT_MEDIUM) / 2)
+ qtweight = QFont::Light;
+ else if (fc_weight <= (FC_WEIGHT_MEDIUM + FC_WEIGHT_DEMIBOLD) / 2)
+ qtweight = QFont::Normal;
+ else if (fc_weight <= (FC_WEIGHT_DEMIBOLD + FC_WEIGHT_BOLD) / 2)
+ qtweight = QFont::DemiBold;
+ else if (fc_weight <= (FC_WEIGHT_BOLD + FC_WEIGHT_BLACK) / 2)
+ qtweight = QFont::Bold;
+
+ return qtweight;
+}
+
+QFontDef qt_FcPatternToQFontDef(FcPattern *pattern, const QFontDef &request)
+{
+ QFontDef fontDef;
+ fontDef.styleStrategy = request.styleStrategy;
+
+ FcChar8 *value = 0;
+ if (FcPatternGetString(pattern, FC_FAMILY, 0, &value) == FcResultMatch) {
+ fontDef.family = QString::fromUtf8(reinterpret_cast<const char *>(value));
+ }
+
+ double dpi;
+ if (FcPatternGetDouble(pattern, FC_DPI, 0, &dpi) != FcResultMatch) {
+ if (X11->display)
+ dpi = QX11Info::appDpiY();
+ else
+ dpi = 96; // ####
+ }
+
+ double size;
+ if (FcPatternGetDouble(pattern, FC_PIXEL_SIZE, 0, &size) == FcResultMatch)
+ fontDef.pixelSize = qRound(size);
+ else
+ fontDef.pixelSize = 12;
+
+ fontDef.pointSize = qt_pointSize(fontDef.pixelSize, qRound(dpi));
+
+ /* ###
+ fontDef.styleHint
+ */
+
+ int weight;
+ if (FcPatternGetInteger(pattern, FC_WEIGHT, 0, &weight) != FcResultMatch)
+ weight = FC_WEIGHT_MEDIUM;
+ fontDef.weight = getFCWeight(weight);
+
+ int slant;
+ if (FcPatternGetInteger(pattern, FC_SLANT, 0, &slant) != FcResultMatch)
+ slant = FC_SLANT_ROMAN;
+ fontDef.style = (slant == FC_SLANT_ITALIC)
+ ? QFont::StyleItalic
+ : ((slant == FC_SLANT_OBLIQUE)
+ ? QFont::StyleOblique
+ : QFont::StyleNormal);
+
+
+ FcBool scalable;
+ if (FcPatternGetBool(pattern, FC_SCALABLE, 0, &scalable) != FcResultMatch)
+ scalable = false;
+ if (scalable) {
+ fontDef.stretch = request.stretch;
+ fontDef.style = request.style;
+ } else {
+ int width;
+ if (FcPatternGetInteger(pattern, FC_WIDTH, 0, &width) == FcResultMatch)
+ fontDef.stretch = width;
+ else
+ fontDef.stretch = 100;
+ }
+
+ int spacing;
+ if (FcPatternGetInteger(pattern, FC_SPACING, 0, &spacing) == FcResultMatch) {
+ fontDef.fixedPitch = (spacing >= FC_MONO);
+ fontDef.ignorePitch = false;
+ } else {
+ fontDef.ignorePitch = true;
+ }
+
+ return fontDef;
+}
+
+static const char *specialLanguages[] = {
+ "en", // Common
+ "el", // Greek
+ "ru", // Cyrillic
+ "hy", // Armenian
+ "he", // Hebrew
+ "ar", // Arabic
+ "syr", // Syriac
+ "div", // Thaana
+ "hi", // Devanagari
+ "bn", // Bengali
+ "pa", // Gurmukhi
+ "gu", // Gujarati
+ "or", // Oriya
+ "ta", // Tamil
+ "te", // Telugu
+ "kn", // Kannada
+ "ml", // Malayalam
+ "si", // Sinhala
+ "th", // Thai
+ "lo", // Lao
+ "bo", // Tibetan
+ "my", // Myanmar
+ "ka", // Georgian
+ "ko", // Hangul
+ "", // Ogham
+ "", // Runic
+ "km" // Khmer
+};
+enum { SpecialLanguageCount = sizeof(specialLanguages) / sizeof(const char *) };
+
+static const ushort specialChars[] = {
+ 0, // English
+ 0, // Greek
+ 0, // Cyrillic
+ 0, // Armenian
+ 0, // Hebrew
+ 0, // Arabic
+ 0, // Syriac
+ 0, // Thaana
+ 0, // Devanagari
+ 0, // Bengali
+ 0, // Gurmukhi
+ 0, // Gujarati
+ 0, // Oriya
+ 0, // Tamil
+ 0xc15, // Telugu
+ 0xc95, // Kannada
+ 0xd15, // Malayalam
+ 0xd9a, // Sinhala
+ 0, // Thai
+ 0, // Lao
+ 0, // Tibetan
+ 0x1000, // Myanmar
+ 0, // Georgian
+ 0, // Hangul
+ 0x1681, // Ogham
+ 0x16a0, // Runic
+ 0 // Khmer
+};
+enum { SpecialCharCount = sizeof(specialChars) / sizeof(ushort) };
+
+// this could become a list of all languages used for each writing
+// system, instead of using the single most common language.
+static const char *languageForWritingSystem[] = {
+ 0, // Any
+ "en", // Latin
+ "el", // Greek
+ "ru", // Cyrillic
+ "hy", // Armenian
+ "he", // Hebrew
+ "ar", // Arabic
+ "syr", // Syriac
+ "div", // Thaana
+ "hi", // Devanagari
+ "bn", // Bengali
+ "pa", // Gurmukhi
+ "gu", // Gujarati
+ "or", // Oriya
+ "ta", // Tamil
+ "te", // Telugu
+ "kn", // Kannada
+ "ml", // Malayalam
+ "si", // Sinhala
+ "th", // Thai
+ "lo", // Lao
+ "bo", // Tibetan
+ "my", // Myanmar
+ "ka", // Georgian
+ "km", // Khmer
+ "zh-cn", // SimplifiedChinese
+ "zh-tw", // TraditionalChinese
+ "ja", // Japanese
+ "ko", // Korean
+ "vi", // Vietnamese
+ 0, // Symbol
+ 0, // Ogham
+ 0 // Runic
+};
+enum { LanguageCount = sizeof(languageForWritingSystem) / sizeof(const char *) };
+
+// Unfortunately FontConfig doesn't know about some languages. We have to test these through the
+// charset. The lists below contain the systems where we need to do this.
+static const ushort sampleCharForWritingSystem[] = {
+ 0, // Any
+ 0, // Latin
+ 0, // Greek
+ 0, // Cyrillic
+ 0, // Armenian
+ 0, // Hebrew
+ 0, // Arabic
+ 0, // Syriac
+ 0, // Thaana
+ 0, // Devanagari
+ 0, // Bengali
+ 0, // Gurmukhi
+ 0, // Gujarati
+ 0, // Oriya
+ 0, // Tamil
+ 0xc15, // Telugu
+ 0xc95, // Kannada
+ 0xd15, // Malayalam
+ 0xd9a, // Sinhala
+ 0, // Thai
+ 0, // Lao
+ 0, // Tibetan
+ 0x1000, // Myanmar
+ 0, // Georgian
+ 0, // Khmer
+ 0, // SimplifiedChinese
+ 0, // TraditionalChinese
+ 0, // Japanese
+ 0, // Korean
+ 0, // Vietnamese
+ 0, // Symbol
+ 0x1681, // Ogham
+ 0x16a0 // Runic
+};
+enum { SampleCharCount = sizeof(sampleCharForWritingSystem) / sizeof(ushort) };
+
+// Newer FontConfig let's us sort out fonts that contain certain glyphs, but no
+// open type tables for is directly. Do this so we don't pick some strange
+// pseudo unicode font
+static const char *openType[] = {
+ 0, // Any
+ 0, // Latin
+ 0, // Greek
+ 0, // Cyrillic
+ 0, // Armenian
+ 0, // Hebrew
+ 0, // Arabic
+ "syrc", // Syriac
+ "thaa", // Thaana
+ "deva", // Devanagari
+ "beng", // Bengali
+ "guru", // Gurmukhi
+ "gurj", // Gujarati
+ "orya", // Oriya
+ "taml", // Tamil
+ "telu", // Telugu
+ "knda", // Kannada
+ "mlym", // Malayalam
+ "sinh", // Sinhala
+ 0, // Thai
+ 0, // Lao
+ "tibt", // Tibetan
+ "mymr", // Myanmar
+ 0, // Georgian
+ "khmr", // Khmer
+ 0, // SimplifiedChinese
+ 0, // TraditionalChinese
+ 0, // Japanese
+ 0, // Korean
+ 0, // Vietnamese
+ 0, // Symbol
+ 0, // Ogham
+ 0 // Runic
+};
+enum { OpenTypeCount = sizeof(openType) / sizeof(const char *) };
+
+
+static void loadFontConfig()
+{
+ Q_ASSERT_X(X11, "QFontDatabase",
+ "A QApplication object needs to be constructed before FontConfig is used.");
+ if (!X11->has_fontconfig)
+ return;
+
+ Q_ASSERT_X(int(QUnicodeTables::ScriptCount) == SpecialLanguageCount,
+ "QFontDatabase", "New scripts have been added.");
+ Q_ASSERT_X(int(QUnicodeTables::ScriptCount) == SpecialCharCount,
+ "QFontDatabase", "New scripts have been added.");
+ Q_ASSERT_X(int(QFontDatabase::WritingSystemsCount) == LanguageCount,
+ "QFontDatabase", "New writing systems have been added.");
+ Q_ASSERT_X(int(QFontDatabase::WritingSystemsCount) == SampleCharCount,
+ "QFontDatabase", "New writing systems have been added.");
+ Q_ASSERT_X(int(QFontDatabase::WritingSystemsCount) == OpenTypeCount,
+ "QFontDatabase", "New writing systems have been added.");
+
+ QFontDatabasePrivate *db = privateDb();
+ FcFontSet *fonts;
+
+ QString familyName;
+ FcChar8 *value = 0;
+ int weight_value;
+ int slant_value;
+ int spacing_value;
+ FcChar8 *file_value;
+ int index_value;
+ FcChar8 *foundry_value;
+ FcBool scalable;
+
+ {
+ FcObjectSet *os = FcObjectSetCreate();
+ FcPattern *pattern = FcPatternCreate();
+ const char *properties [] = {
+ FC_FAMILY, FC_WEIGHT, FC_SLANT,
+ FC_SPACING, FC_FILE, FC_INDEX,
+ FC_LANG, FC_CHARSET, FC_FOUNDRY, FC_SCALABLE, FC_PIXEL_SIZE, FC_WEIGHT,
+ FC_WIDTH,
+#if FC_VERSION >= 20297
+ FC_CAPABILITY,
+#endif
+ (const char *)0
+ };
+ const char **p = properties;
+ while (*p) {
+ FcObjectSetAdd(os, *p);
+ ++p;
+ }
+ fonts = FcFontList(0, pattern, os);
+ FcObjectSetDestroy(os);
+ FcPatternDestroy(pattern);
+ }
+
+ for (int i = 0; i < fonts->nfont; i++) {
+ if (FcPatternGetString(fonts->fonts[i], FC_FAMILY, 0, &value) != FcResultMatch)
+ continue;
+ // capitalize(value);
+ familyName = QString::fromUtf8((const char *)value);
+ slant_value = FC_SLANT_ROMAN;
+ weight_value = FC_WEIGHT_MEDIUM;
+ spacing_value = FC_PROPORTIONAL;
+ file_value = 0;
+ index_value = 0;
+ scalable = FcTrue;
+
+ if (FcPatternGetInteger (fonts->fonts[i], FC_SLANT, 0, &slant_value) != FcResultMatch)
+ slant_value = FC_SLANT_ROMAN;
+ if (FcPatternGetInteger (fonts->fonts[i], FC_WEIGHT, 0, &weight_value) != FcResultMatch)
+ weight_value = FC_WEIGHT_MEDIUM;
+ if (FcPatternGetInteger (fonts->fonts[i], FC_SPACING, 0, &spacing_value) != FcResultMatch)
+ spacing_value = FC_PROPORTIONAL;
+ if (FcPatternGetString (fonts->fonts[i], FC_FILE, 0, &file_value) != FcResultMatch)
+ file_value = 0;
+ if (FcPatternGetInteger (fonts->fonts[i], FC_INDEX, 0, &index_value) != FcResultMatch)
+ index_value = 0;
+ if (FcPatternGetBool(fonts->fonts[i], FC_SCALABLE, 0, &scalable) != FcResultMatch)
+ scalable = FcTrue;
+ if (FcPatternGetString(fonts->fonts[i], FC_FOUNDRY, 0, &foundry_value) != FcResultMatch)
+ foundry_value = 0;
+ QtFontFamily *family = db->family(familyName, true);
+
+ FcLangSet *langset = 0;
+ FcResult res = FcPatternGetLangSet(fonts->fonts[i], FC_LANG, 0, &langset);
+ if (res == FcResultMatch) {
+ for (int i = 1; i < LanguageCount; ++i) {
+ const FcChar8 *lang = (const FcChar8*) languageForWritingSystem[i];
+ if (!lang) {
+ family->writingSystems[i] |= QtFontFamily::UnsupportedFT;
+ } else {
+ FcLangResult langRes = FcLangSetHasLang(langset, lang);
+ if (langRes != FcLangDifferentLang)
+ family->writingSystems[i] = QtFontFamily::Supported;
+ else
+ family->writingSystems[i] |= QtFontFamily::UnsupportedFT;
+ }
+ }
+ family->writingSystems[QFontDatabase::Other] = QtFontFamily::UnsupportedFT;
+ family->ftWritingSystemCheck = true;
+ } else {
+ // we set Other to supported for symbol fonts. It makes no
+ // sense to merge these with other ones, as they are
+ // special in a way.
+ for (int i = 1; i < LanguageCount; ++i)
+ family->writingSystems[i] |= QtFontFamily::UnsupportedFT;
+ family->writingSystems[QFontDatabase::Other] = QtFontFamily::Supported;
+ }
+
+ FcCharSet *cs = 0;
+ res = FcPatternGetCharSet(fonts->fonts[i], FC_CHARSET, 0, &cs);
+ if (res == FcResultMatch) {
+ // some languages are not supported by FontConfig, we rather check the
+ // charset to detect these
+ for (int i = 1; i < SampleCharCount; ++i) {
+ if (!sampleCharForWritingSystem[i])
+ continue;
+ if (FcCharSetHasChar(cs, sampleCharForWritingSystem[i]))
+ family->writingSystems[i] = QtFontFamily::Supported;
+ }
+ }
+
+#if FC_VERSION >= 20297
+ for (int j = 1; j < LanguageCount; ++j) {
+ if (family->writingSystems[j] == QtFontFamily::Supported && requiresOpenType(j) && openType[j]) {
+ FcChar8 *cap;
+ res = FcPatternGetString (fonts->fonts[i], FC_CAPABILITY, 0, &cap);
+ if (res != FcResultMatch || !strstr((const char *)cap, openType[j]))
+ family->writingSystems[j] = QtFontFamily::UnsupportedFT;
+ }
+ }
+#endif
+
+ QByteArray file((const char *)file_value);
+ family->fontFilename = file;
+ family->fontFileIndex = index_value;
+
+ QtFontStyle::Key styleKey;
+ styleKey.style = (slant_value == FC_SLANT_ITALIC)
+ ? QFont::StyleItalic
+ : ((slant_value == FC_SLANT_OBLIQUE)
+ ? QFont::StyleOblique
+ : QFont::StyleNormal);
+ styleKey.weight = getFCWeight(weight_value);
+ if (!scalable) {
+ int width = 100;
+ FcPatternGetInteger (fonts->fonts[i], FC_WIDTH, 0, &width);
+ styleKey.stretch = width;
+ }
+
+ QtFontFoundry *foundry
+ = family->foundry(foundry_value ? QString::fromUtf8((const char *)foundry_value) : QString(), true);
+ QtFontStyle *style = foundry->style(styleKey, true);
+
+ if (spacing_value < FC_MONO)
+ family->fixedPitch = false;
+
+ QtFontSize *size;
+ if (scalable) {
+ style->smoothScalable = true;
+ size = style->pixelSize(SMOOTH_SCALABLE, true);
+ } else {
+ double pixel_size = 0;
+ FcPatternGetDouble (fonts->fonts[i], FC_PIXEL_SIZE, 0, &pixel_size);
+ size = style->pixelSize((int)pixel_size, true);
+ }
+ QtFontEncoding *enc = size->encodingID(-1, 0, 0, 0, 0, true);
+ enc->pitch = (spacing_value >= FC_CHARCELL ? 'c' :
+ (spacing_value >= FC_MONO ? 'm' : 'p'));
+ }
+
+ FcFontSetDestroy (fonts);
+
+ struct FcDefaultFont {
+ const char *qtname;
+ const char *rawname;
+ bool fixed;
+ };
+ const FcDefaultFont defaults[] = {
+ { "Serif", "serif", false },
+ { "Sans Serif", "sans-serif", false },
+ { "Monospace", "monospace", true },
+ { 0, 0, false }
+ };
+ const FcDefaultFont *f = defaults;
+ while (f->qtname) {
+ QtFontFamily *family = db->family(QLatin1String(f->qtname), true);
+ family->fixedPitch = f->fixed;
+ family->synthetic = true;
+ QtFontFoundry *foundry = family->foundry(QString(), true);
+
+ // aliases only make sense for 'common', not for any of the specials
+ for (int i = 1; i < LanguageCount; ++i) {
+ if (requiresOpenType(i))
+ family->writingSystems[i] = QtFontFamily::UnsupportedFT;
+ else
+ family->writingSystems[i] = QtFontFamily::Supported;
+ }
+ family->writingSystems[QFontDatabase::Other] = QtFontFamily::UnsupportedFT;
+
+ QtFontStyle::Key styleKey;
+ for (int i = 0; i < 4; ++i) {
+ styleKey.style = (i%2) ? QFont::StyleNormal : QFont::StyleItalic;
+ styleKey.weight = (i > 1) ? QFont::Bold : QFont::Normal;
+ QtFontStyle *style = foundry->style(styleKey, true);
+ style->smoothScalable = true;
+ QtFontSize *size = style->pixelSize(SMOOTH_SCALABLE, true);
+ QtFontEncoding *enc = size->encodingID(-1, 0, 0, 0, 0, true);
+ enc->pitch = (f->fixed ? 'm' : 'p');
+ }
+ ++f;
+ }
+}
+#endif // QT_NO_FONTCONFIG
+
+static void initializeDb();
+
+static void load(const QString &family = QString(), int script = -1)
+{
+ if (X11->has_fontconfig) {
+ initializeDb();
+ return;
+ }
+
+#ifdef QFONTDATABASE_DEBUG
+ QTime t;
+ t.start();
+#endif
+
+ if (family.isNull() && script == -1) {
+ loadXlfds(0, -1);
+ } else {
+ if (family.isNull()) {
+ // load all families in all writing systems that match \a script
+ for (int ws = 1; ws < QFontDatabase::WritingSystemsCount; ++ws) {
+ if (scriptForWritingSystem[ws] != script)
+ continue;
+ for (int i = 0; i < numEncodings; ++i) {
+ if (writingSystems_for_xlfd_encoding[i][ws])
+ loadXlfds(0, i);
+ }
+ }
+ } else {
+ QtFontFamily *f = privateDb()->family(family);
+ // could reduce this further with some more magic:
+ // would need to remember the encodings loaded for the family.
+ if (!f || !f->xlfdLoaded)
+ loadXlfds(family.toLatin1(), -1);
+ }
+ }
+
+#ifdef QFONTDATABASE_DEBUG
+ FD_DEBUG("QFontDatabase: load(%s, %d) took %d ms",
+ family.toLatin1().constData(), script, t.elapsed());
+#endif
+}
+
+static void checkSymbolFont(QtFontFamily *family)
+{
+ if (!family || family->symbol_checked || family->fontFilename.isEmpty())
+ return;
+// qDebug() << "checking " << family->rawName;
+ family->symbol_checked = true;
+
+ QFontEngine::FaceId id;
+ id.filename = family->fontFilename;
+ id.index = family->fontFileIndex;
+ QFreetypeFace *f = QFreetypeFace::getFace(id);
+ if (!f) {
+ qWarning("checkSymbolFonts: Couldn't open face %s (%s/%d)",
+ qPrintable(family->name), family->fontFilename.data(), family->fontFileIndex);
+ return;
+ }
+ for (int i = 0; i < f->face->num_charmaps; ++i) {
+ FT_CharMap cm = f->face->charmaps[i];
+ if (cm->encoding == FT_ENCODING_ADOBE_CUSTOM
+ || cm->encoding == FT_ENCODING_MS_SYMBOL) {
+ for (int x = QFontDatabase::Latin; x < QFontDatabase::Other; ++x)
+ family->writingSystems[x] = QtFontFamily::Unsupported;
+ family->writingSystems[QFontDatabase::Other] = QtFontFamily::Supported;
+ break;
+ }
+ }
+ f->release(id);
+}
+
+static void checkSymbolFonts(const QString &family = QString())
+{
+#ifndef QT_NO_FONTCONFIG
+ QFontDatabasePrivate *d = privateDb();
+
+ if (family.isEmpty()) {
+ for (int i = 0; i < d->count; ++i)
+ checkSymbolFont(d->families[i]);
+ } else {
+ checkSymbolFont(d->family(family));
+ }
+#endif
+}
+
+static void registerFont(QFontDatabasePrivate::ApplicationFont *fnt);
+
+static void initializeDb()
+{
+ QFontDatabasePrivate *db = privateDb();
+ if (!db || db->count)
+ return;
+
+ QTime t;
+ t.start();
+
+#ifndef QT_NO_FONTCONFIG
+ if (db->reregisterAppFonts) {
+ db->reregisterAppFonts = false;
+ for (int i = 0; i < db->applicationFonts.count(); ++i)
+ if (!db->applicationFonts.at(i).families.isEmpty()) {
+ registerFont(&db->applicationFonts[i]);
+ }
+ }
+
+ loadFontConfig();
+ FD_DEBUG("QFontDatabase: loaded FontConfig: %d ms", t.elapsed());
+#endif
+
+ t.start();
+
+#ifndef QT_NO_FONTCONFIG
+ for (int i = 0; i < db->count; i++) {
+ for (int j = 0; j < db->families[i]->count; ++j) { // each foundry
+ QtFontFoundry *foundry = db->families[i]->foundries[j];
+ for (int k = 0; k < foundry->count; ++k) {
+ QtFontStyle *style = foundry->styles[k];
+ if (style->key.style != QFont::StyleNormal) continue;
+
+ QtFontSize *size = style->pixelSize(SMOOTH_SCALABLE);
+ if (! size) continue; // should not happen
+ QtFontEncoding *enc = size->encodingID(-1, 0, 0, 0, 0, true);
+ if (! enc) continue; // should not happen either
+
+ QtFontStyle::Key key = style->key;
+
+ // does this style have an italic equivalent?
+ key.style = QFont::StyleItalic;
+ QtFontStyle *equiv = foundry->style(key);
+ if (equiv) continue;
+
+ // does this style have an oblique equivalent?
+ key.style = QFont::StyleOblique;
+ equiv = foundry->style(key);
+ if (equiv) continue;
+
+ // let's fake one...
+ equiv = foundry->style(key, true);
+ equiv->smoothScalable = true;
+
+ QtFontSize *equiv_size = equiv->pixelSize(SMOOTH_SCALABLE, true);
+ QtFontEncoding *equiv_enc = equiv_size->encodingID(-1, 0, 0, 0, 0, true);
+
+ // keep the same pitch
+ equiv_enc->pitch = enc->pitch;
+ }
+ }
+ }
+#endif
+
+
+#ifdef QFONTDATABASE_DEBUG
+#ifndef QT_NO_FONTCONFIG
+ if (!X11->has_fontconfig)
+#endif
+ // load everything at startup in debug mode.
+ loadXlfds(0, -1);
+
+ // print the database
+ for (int f = 0; f < db->count; f++) {
+ QtFontFamily *family = db->families[f];
+ FD_DEBUG("'%s' %s fixed=%s", family->name.latin1(), (family->fixedPitch ? "fixed" : ""),
+ (family->fixedPitch ? "yes" : "no"));
+ for (int i = 0; i < QFontDatabase::WritingSystemsCount; ++i) {
+ QFontDatabase::WritingSystem ws = QFontDatabase::WritingSystem(i);
+ FD_DEBUG("\t%s: %s", QFontDatabase::writingSystemName(ws).toLatin1().constData(),
+ ((family->writingSystems[i] & QtFontFamily::Supported) ? "Supported" :
+ (family->writingSystems[i] & QtFontFamily::Unsupported) == QtFontFamily::Unsupported ?
+ "Unsupported" : "Unknown"));
+ }
+
+ for (int fd = 0; fd < family->count; fd++) {
+ QtFontFoundry *foundry = family->foundries[fd];
+ FD_DEBUG("\t\t'%s'", foundry->name.latin1());
+ for (int s = 0; s < foundry->count; s++) {
+ QtFontStyle *style = foundry->styles[s];
+ FD_DEBUG("\t\t\tstyle: style=%d weight=%d (%s)\n"
+ "\t\t\tstretch=%d (%s)",
+ style->key.style, style->key.weight,
+ style->weightName, style->key.stretch,
+ style->setwidthName ? style->setwidthName : "nil");
+ if (style->smoothScalable)
+ FD_DEBUG("\t\t\t\tsmooth scalable");
+ else if (style->bitmapScalable)
+ FD_DEBUG("\t\t\t\tbitmap scalable");
+ if (style->pixelSizes) {
+ qDebug("\t\t\t\t%d pixel sizes", style->count);
+ for (int z = 0; z < style->count; ++z) {
+ QtFontSize *size = style->pixelSizes + z;
+ for (int e = 0; e < size->count; ++e) {
+ FD_DEBUG("\t\t\t\t size %5d pitch %c encoding %s",
+ size->pixelSize,
+ size->encodings[e].pitch,
+ xlfd_for_id(size->encodings[e].encoding));
+ }
+ }
+ }
+ }
+ }
+ }
+#endif // QFONTDATABASE_DEBUG
+}
+
+
+// --------------------------------------------------------------------------------------
+// font loader
+// --------------------------------------------------------------------------------------
+
+static const char *styleHint(const QFontDef &request)
+{
+ const char *stylehint = 0;
+ switch (request.styleHint) {
+ case QFont::SansSerif:
+ stylehint = "sans-serif";
+ break;
+ case QFont::Serif:
+ stylehint = "serif";
+ break;
+ case QFont::TypeWriter:
+ stylehint = "monospace";
+ break;
+ default:
+ if (request.fixedPitch)
+ stylehint = "monospace";
+ break;
+ }
+ return stylehint;
+}
+
+#ifndef QT_NO_FONTCONFIG
+
+void qt_addPatternProps(FcPattern *pattern, int screen, int script, const QFontDef &request)
+{
+ int weight_value = FC_WEIGHT_BLACK;
+ if (request.weight == 0)
+ weight_value = FC_WEIGHT_MEDIUM;
+ else if (request.weight < (QFont::Light + QFont::Normal) / 2)
+ weight_value = FC_WEIGHT_LIGHT;
+ else if (request.weight < (QFont::Normal + QFont::DemiBold) / 2)
+ weight_value = FC_WEIGHT_MEDIUM;
+ else if (request.weight < (QFont::DemiBold + QFont::Bold) / 2)
+ weight_value = FC_WEIGHT_DEMIBOLD;
+ else if (request.weight < (QFont::Bold + QFont::Black) / 2)
+ weight_value = FC_WEIGHT_BOLD;
+ FcPatternAddInteger(pattern, FC_WEIGHT, weight_value);
+
+ int slant_value = FC_SLANT_ROMAN;
+ if (request.style == QFont::StyleItalic)
+ slant_value = FC_SLANT_ITALIC;
+ else if (request.style == QFont::StyleOblique)
+ slant_value = FC_SLANT_OBLIQUE;
+ FcPatternAddInteger(pattern, FC_SLANT, slant_value);
+
+ double size_value = qMax(1, request.pixelSize);
+ FcPatternAddDouble(pattern, FC_PIXEL_SIZE, size_value);
+
+ int stretch = request.stretch;
+ if (!stretch)
+ stretch = 100;
+ FcPatternAddInteger(pattern, FC_WIDTH, stretch);
+
+ if (X11->display && QX11Info::appDepth(screen) <= 8) {
+ // can't do antialiasing on 8bpp
+ FcPatternAddBool(pattern, FC_ANTIALIAS, false);
+ } else if (request.styleStrategy & (QFont::PreferAntialias|QFont::NoAntialias)) {
+ FcPatternAddBool(pattern, FC_ANTIALIAS,
+ !(request.styleStrategy & QFont::NoAntialias));
+ }
+
+ if (script != QUnicodeTables::Common) {
+ Q_ASSERT(script < QUnicodeTables::ScriptCount);
+ FcLangSet *ls = FcLangSetCreate();
+ FcLangSetAdd(ls, (const FcChar8*)specialLanguages[script]);
+ FcPatternAddLangSet(pattern, FC_LANG, ls);
+ FcLangSetDestroy(ls);
+ }
+}
+
+static bool preferScalable(const QFontDef &request)
+{
+ return request.styleStrategy & (QFont::PreferOutline|QFont::ForceOutline|QFont::PreferQuality|QFont::PreferAntialias);
+}
+
+
+static FcPattern *getFcPattern(const QFontPrivate *fp, int script, const QFontDef &request)
+{
+ if (!X11->has_fontconfig)
+ return 0;
+
+ FcPattern *pattern = FcPatternCreate();
+ if (!pattern)
+ return 0;
+
+ FcValue value;
+ value.type = FcTypeString;
+
+ QtFontDesc desc;
+ QStringList families_and_foundries = familyList(request);
+ for (int i = 0; i < families_and_foundries.size(); ++i) {
+ QString family, foundry;
+ parseFontName(families_and_foundries.at(i), foundry, family);
+ if (!family.isEmpty()) {
+ QByteArray cs = family.toUtf8();
+ value.u.s = (const FcChar8 *)cs.data();
+ FcPatternAdd(pattern, FC_FAMILY, value, FcTrue);
+ }
+ if (i == 0) {
+ QT_PREPEND_NAMESPACE(match)(script, request, family, foundry, -1, &desc);
+ if (!foundry.isEmpty()) {
+ QByteArray cs = foundry.toUtf8();
+ value.u.s = (const FcChar8 *)cs.data();
+ FcPatternAddWeak(pattern, FC_FOUNDRY, value, FcTrue);
+ }
+ }
+ }
+
+ const char *stylehint = styleHint(request);
+ if (stylehint) {
+ value.u.s = (const FcChar8 *)stylehint;
+ FcPatternAddWeak(pattern, FC_FAMILY, value, FcTrue);
+ }
+
+ if (!request.ignorePitch) {
+ char pitch_value = FC_PROPORTIONAL;
+ if (request.fixedPitch || (desc.family && desc.family->fixedPitch))
+ pitch_value = FC_MONO;
+ FcPatternAddInteger(pattern, FC_SPACING, pitch_value);
+ }
+ FcPatternAddBool(pattern, FC_OUTLINE, !(request.styleStrategy & QFont::PreferBitmap));
+ if (preferScalable(request) || (desc.style && desc.style->smoothScalable))
+ FcPatternAddBool(pattern, FC_SCALABLE, true);
+
+ qt_addPatternProps(pattern, fp->screen, script, request);
+
+ FcDefaultSubstitute(pattern);
+ FcConfigSubstitute(0, pattern, FcMatchPattern);
+ FcConfigSubstitute(0, pattern, FcMatchFont);
+
+ // these should only get added to the pattern _after_ substitution
+ // append the default fallback font for the specified script
+ extern QString qt_fallback_font_family(int);
+ QString fallback = qt_fallback_font_family(script);
+ if (!fallback.isEmpty()) {
+ QByteArray cs = fallback.toUtf8();
+ value.u.s = (const FcChar8 *)cs.data();
+ FcPatternAddWeak(pattern, FC_FAMILY, value, FcTrue);
+ }
+
+ // add the default family
+ QString defaultFamily = QApplication::font().family();
+ QByteArray cs = defaultFamily.toUtf8();
+ value.u.s = (const FcChar8 *)cs.data();
+ FcPatternAddWeak(pattern, FC_FAMILY, value, FcTrue);
+
+ // add QFont::defaultFamily() to the list, for compatibility with
+ // previous versions
+ defaultFamily = QApplication::font().defaultFamily();
+ cs = defaultFamily.toUtf8();
+ value.u.s = (const FcChar8 *)cs.data();
+ FcPatternAddWeak(pattern, FC_FAMILY, value, FcTrue);
+
+ return pattern;
+}
+
+
+static void FcFontSetRemove(FcFontSet *fs, int at)
+{
+ Q_ASSERT(at < fs->nfont);
+ FcPatternDestroy(fs->fonts[at]);
+ int len = (--fs->nfont - at) * sizeof(FcPattern *);;
+ if (len > 0)
+ memmove(fs->fonts + at, fs->fonts + at + 1, len);
+}
+
+static QFontEngine *tryPatternLoad(FcPattern *p, int screen,
+ const QFontDef &request, int script, FcPattern **matchedPattern = 0)
+{
+#ifdef FONT_MATCH_DEBUG
+ FcChar8 *fam;
+ FcPatternGetString(p, FC_FAMILY, 0, &fam);
+ FM_DEBUG("==== trying %s\n", fam);
+#endif
+ FM_DEBUG("passes charset test\n");
+ FcPattern *pattern = FcPatternDuplicate(p);
+ // add properties back in as the font selected from the
+ // list doesn't contain them.
+ qt_addPatternProps(pattern, screen, script, request);
+
+ FcConfigSubstitute(0, pattern, FcMatchPattern);
+ FcDefaultSubstitute(pattern);
+ FcResult res;
+ FcPattern *match = FcFontMatch(0, pattern, &res);
+
+ if (matchedPattern)
+ *matchedPattern = 0;
+
+ QFontEngineX11FT *engine = 0;
+ if (!match) // probably no fonts available.
+ goto done;
+
+ if (matchedPattern)
+ *matchedPattern = FcPatternDuplicate(match);
+
+ if (script != QUnicodeTables::Common) {
+ // skip font if it doesn't support the language we want
+ if (specialChars[script]) {
+ // need to check the charset, as the langset doesn't work for these scripts
+ FcCharSet *cs;
+ if (FcPatternGetCharSet(match, FC_CHARSET, 0, &cs) != FcResultMatch)
+ goto done;
+ if (!FcCharSetHasChar(cs, specialChars[script]))
+ goto done;
+ } else {
+ FcLangSet *langSet = 0;
+ if (FcPatternGetLangSet(match, FC_LANG, 0, &langSet) != FcResultMatch)
+ goto done;
+ if (FcLangSetHasLang(langSet, (const FcChar8*)specialLanguages[script]) != FcLangEqual)
+ goto done;
+ }
+ }
+
+ // enforce non-antialiasing if requested. the ft font engine looks at this property.
+ if (request.styleStrategy & QFont::NoAntialias) {
+ FcPatternDel(match, FC_ANTIALIAS);
+ FcPatternAddBool(match, FC_ANTIALIAS, false);
+ }
+
+ engine = new QFontEngineX11FT(match, qt_FcPatternToQFontDef(match, request), screen);
+ if (engine->invalid()) {
+ FM_DEBUG(" --> invalid!\n");
+ delete engine;
+ engine = 0;
+ } else if (scriptRequiresOpenType(script)) {
+ HB_Face hbFace = engine->harfbuzzFace();
+ if (!hbFace || !hbFace->supported_scripts[script]) {
+ FM_DEBUG(" OpenType support missing for script\n");
+ delete engine;
+ engine = 0;
+ }
+ }
+done:
+ FcPatternDestroy(pattern);
+ if (!engine && matchedPattern && *matchedPattern) {
+ FcPatternDestroy(*matchedPattern);
+ *matchedPattern = 0;
+ }
+ return engine;
+}
+
+FcFontSet *qt_fontSetForPattern(FcPattern *pattern, const QFontDef &request)
+{
+ FcResult result;
+ FcFontSet *fs = FcFontSort(0, pattern, FcTrue, 0, &result);
+#ifdef FONT_MATCH_DEBUG
+ FM_DEBUG("first font in fontset:\n");
+ FcPatternPrint(fs->fonts[0]);
+#endif
+
+ FcBool forceScalable = request.styleStrategy & QFont::ForceOutline;
+
+ // remove fonts if they are not scalable (and should be)
+ if (forceScalable && fs) {
+ for (int i = 0; i < fs->nfont; ++i) {
+ FcPattern *font = fs->fonts[i];
+ FcResult res;
+ FcBool scalable;
+ res = FcPatternGetBool(font, FC_SCALABLE, 0, &scalable);
+ if (res != FcResultMatch || !scalable) {
+ FcFontSetRemove(fs, i);
+#ifdef FONT_MATCH_DEBUG
+ FM_DEBUG("removing pattern:");
+ FcPatternPrint(font);
+#endif
+ --i; // go back one
+ }
+ }
+ }
+
+ FM_DEBUG("final pattern contains %d fonts\n", fs->nfont);
+
+ return fs;
+}
+
+static QFontEngine *loadFc(const QFontPrivate *fp, int script, const QFontDef &request)
+{
+ FM_DEBUG("===================== loadFc: script=%d family='%s'\n", script, request.family.toLatin1().data());
+ FcPattern *pattern = getFcPattern(fp, script, request);
+
+#ifdef FONT_MATCH_DEBUG
+ FM_DEBUG("\n\nfinal FcPattern contains:\n");
+ FcPatternPrint(pattern);
+#endif
+
+ QFontEngine *fe = 0;
+ FcPattern *matchedPattern = 0;
+ fe = tryPatternLoad(pattern, fp->screen, request, script, &matchedPattern);
+ if (!fe) {
+ FcFontSet *fs = qt_fontSetForPattern(pattern, request);
+
+ if (fs) {
+ for (int i = 0; !fe && i < fs->nfont; ++i)
+ fe = tryPatternLoad(fs->fonts[i], fp->screen, request, script, &matchedPattern);
+ FcFontSetDestroy(fs);
+ }
+ FM_DEBUG("engine for script %d is %s\n", script, fe ? fe->fontDef.family.toLatin1().data(): "(null)");
+ }
+ if (fe
+ && script == QUnicodeTables::Common
+ && !(request.styleStrategy & QFont::NoFontMerging) && !fe->symbol) {
+ fe = new QFontEngineMultiFT(fe, matchedPattern, pattern, fp->screen, request);
+ } else {
+ FcPatternDestroy(pattern);
+ if (matchedPattern)
+ FcPatternDestroy(matchedPattern);
+ }
+ return fe;
+}
+
+static FcPattern *queryFont(const FcChar8 *file, const QByteArray &data, int id, FcBlanks *blanks, int *count)
+{
+#if FC_VERSION < 20402
+ Q_UNUSED(data)
+ return FcFreeTypeQuery(file, id, blanks, count);
+#else
+ if (data.isEmpty())
+ return FcFreeTypeQuery(file, id, blanks, count);
+
+ extern FT_Library qt_getFreetype();
+ FT_Library lib = qt_getFreetype();
+
+ FcPattern *pattern = 0;
+
+ FT_Face face;
+ if (!FT_New_Memory_Face(lib, (const FT_Byte *)data.constData(), data.size(), id, &face)) {
+ *count = face->num_faces;
+
+ pattern = FcFreeTypeQueryFace(face, file, id, blanks);
+
+ FT_Done_Face(face);
+ }
+
+ return pattern;
+#endif
+}
+#endif // QT_NO_FONTCONFIG
+
+static QFontEngine *loadRaw(const QFontPrivate *fp, const QFontDef &request)
+{
+ Q_ASSERT(fp && fp->rawMode);
+
+ QByteArray xlfd = request.family.toLatin1();
+ FM_DEBUG("Loading XLFD (rawmode) '%s'", xlfd.data());
+
+ QFontEngine *fe;
+ XFontStruct *xfs;
+ if (!(xfs = XLoadQueryFont(QX11Info::display(), xlfd.data())))
+ if (!(xfs = XLoadQueryFont(QX11Info::display(), "fixed")))
+ return 0;
+
+ fe = new QFontEngineXLFD(xfs, xlfd, 0);
+ if (! qt_fillFontDef(xfs, &fe->fontDef, fp->dpi, 0) &&
+ ! qt_fillFontDef(xlfd, &fe->fontDef, fp->dpi, 0))
+ fe->fontDef = QFontDef();
+ return fe;
+}
+
+QFontEngine *QFontDatabase::loadXlfd(int screen, int script, const QFontDef &request, int force_encoding_id)
+{
+ QMutexLocker locker(fontDatabaseMutex());
+
+ QtFontDesc desc;
+ FM_DEBUG() << "---> loadXlfd: request is" << request.family;
+ QStringList families_and_foundries = familyList(request);
+ const char *stylehint = styleHint(request);
+ if (stylehint)
+ families_and_foundries << QString::fromLatin1(stylehint);
+ families_and_foundries << QString();
+ FM_DEBUG() << "loadXlfd: list is" << families_and_foundries;
+ for (int i = 0; i < families_and_foundries.size(); ++i) {
+ QString family, foundry;
+ QT_PREPEND_NAMESPACE(parseFontName)(families_and_foundries.at(i), foundry, family);
+ FM_DEBUG("loadXlfd: >>>>>>>>>>>>>>trying to match '%s' encoding=%d", family.toLatin1().data(), force_encoding_id);
+ QT_PREPEND_NAMESPACE(match)(script, request, family, foundry, force_encoding_id, &desc);
+ if (desc.family)
+ break;
+ }
+
+ QFontEngine *fe = 0;
+ if (force_encoding_id != -1
+ || (request.styleStrategy & QFont::NoFontMerging)
+ || (desc.family && desc.family->writingSystems[QFontDatabase::Symbol] & QtFontFamily::Supported)) {
+ if (desc.family) {
+ int px = desc.size->pixelSize;
+ if (desc.style->smoothScalable && px == SMOOTH_SCALABLE)
+ px = request.pixelSize;
+ else if (desc.style->bitmapScalable && px == 0)
+ px = request.pixelSize;
+
+ QByteArray xlfd("-");
+ xlfd += desc.foundry->name.isEmpty() ? QByteArray("*") : desc.foundry->name.toLatin1();
+ xlfd += "-";
+ xlfd += desc.family->name.isEmpty() ? QByteArray("*") : desc.family->name.toLatin1();
+ xlfd += "-";
+ xlfd += desc.style->weightName ? desc.style->weightName : "*";
+ xlfd += "-";
+ xlfd += (desc.style->key.style == QFont::StyleItalic
+ ? "i"
+ : (desc.style->key.style == QFont::StyleOblique ? "o" : "r"));
+ xlfd += "-";
+ xlfd += desc.style->setwidthName ? desc.style->setwidthName : "*";
+ // ### handle add-style
+ xlfd += "-*-";
+ xlfd += QByteArray::number(px);
+ xlfd += "-";
+ xlfd += QByteArray::number(desc.encoding->xpoint);
+ xlfd += "-";
+ xlfd += QByteArray::number(desc.encoding->xres);
+ xlfd += "-";
+ xlfd += QByteArray::number(desc.encoding->yres);
+ xlfd += "-";
+ xlfd += desc.encoding->pitch;
+ xlfd += "-";
+ xlfd += QByteArray::number(desc.encoding->avgwidth);
+ xlfd += "-";
+ xlfd += xlfd_for_id(desc.encoding->encoding);
+
+ FM_DEBUG(" using XLFD: %s\n", xlfd.data());
+
+ const int mib = xlfd_encoding[desc.encoding->encoding].mib;
+ XFontStruct *xfs;
+ if ((xfs = XLoadQueryFont(QX11Info::display(), xlfd))) {
+ fe = new QFontEngineXLFD(xfs, xlfd, mib);
+ const int dpi = QX11Info::appDpiY();
+ if (!qt_fillFontDef(xfs, &fe->fontDef, dpi, &desc)
+ && !qt_fillFontDef(xlfd, &fe->fontDef, dpi, &desc)) {
+ initFontDef(desc, request, &fe->fontDef);
+ }
+ }
+ }
+ if (!fe) {
+ fe = new QFontEngineBox(request.pixelSize);
+ fe->fontDef = QFontDef();
+ }
+ } else {
+ QList<int> encodings;
+ if (desc.encoding)
+ encodings.append(int(desc.encoding->encoding));
+
+ if (desc.size) {
+ // append all other encodings for the matched font
+ for (int i = 0; i < desc.size->count; ++i) {
+ QtFontEncoding *e = desc.size->encodings + i;
+ if (e == desc.encoding)
+ continue;
+ encodings.append(int(e->encoding));
+ }
+ }
+ // fill in the missing encodings
+ const XlfdEncoding *enc = xlfd_encoding;
+ for (; enc->name; ++enc) {
+ if (!encodings.contains(enc->id))
+ encodings.append(enc->id);
+ }
+
+#if defined(FONT_MATCH_DEBUG)
+ FM_DEBUG(" using MultiXLFD, encodings:");
+ for (int i = 0; i < encodings.size(); ++i) {
+ const int id = encodings.at(i);
+ FM_DEBUG(" %2d: %s", xlfd_encoding[id].id, xlfd_encoding[id].name);
+ }
+#endif
+
+ fe = new QFontEngineMultiXLFD(request, encodings, screen);
+ }
+ return fe;
+}
+
+/*! \internal
+ Loads a QFontEngine for the specified \a script that matches the
+ QFontDef \e request member variable.
+*/
+void QFontDatabase::load(const QFontPrivate *d, int script)
+{
+ Q_ASSERT(script >= 0 && script < QUnicodeTables::ScriptCount);
+
+ // normalize the request to get better caching
+ QFontDef req = d->request;
+ if (req.pixelSize <= 0)
+ req.pixelSize = qRound(qt_pixelSize(req.pointSize, d->dpi));
+ req.pointSize = 0;
+ if (req.weight == 0)
+ req.weight = QFont::Normal;
+ if (req.stretch == 0)
+ req.stretch = 100;
+
+ QFontCache::Key key(req, d->rawMode ? QUnicodeTables::Common : script, d->screen);
+ if (!d->engineData)
+ getEngineData(d, key);
+
+ // the cached engineData could have already loaded the engine we want
+ if (d->engineData->engines[script])
+ return;
+
+ // set it to the actual pointsize, so QFontInfo will do the right thing
+ req.pointSize = qt_pointSize(req.pixelSize, d->dpi);
+
+ QFontEngine *fe = QFontCache::instance()->findEngine(key);
+
+ if (!fe) {
+ QMutexLocker locker(fontDatabaseMutex());
+ if (!privateDb()->count)
+ initializeDb();
+
+ const bool mainThread = (qApp->thread() == QThread::currentThread());
+ if (qt_enable_test_font && req.family == QLatin1String("__Qt__Box__Engine__")) {
+ fe = new QTestFontEngine(req.pixelSize);
+ fe->fontDef = req;
+ } else if (d->rawMode) {
+ if (mainThread)
+ fe = loadRaw(d, req);
+#ifndef QT_NO_FONTCONFIG
+ } else if (X11->has_fontconfig) {
+ fe = loadFc(d, script, req);
+#endif
+ } else if (mainThread) {
+ fe = loadXlfd(d->screen, script, req);
+ }
+ if (!fe) {
+ fe = new QFontEngineBox(req.pixelSize);
+ fe->fontDef = QFontDef();
+ }
+ }
+ if (fe->symbol || (d->request.styleStrategy & QFont::NoFontMerging)) {
+ for (int i = 0; i < QUnicodeTables::ScriptCount; ++i) {
+ if (!d->engineData->engines[i]) {
+ d->engineData->engines[i] = fe;
+ fe->ref.ref();
+ }
+ }
+ } else {
+ d->engineData->engines[script] = fe;
+ fe->ref.ref();
+ }
+ QFontCache::instance()->insertEngine(key, fe);
+}
+
+static void registerFont(QFontDatabasePrivate::ApplicationFont *fnt)
+{
+#if defined(QT_NO_FONTCONFIG)
+ return;
+#else
+ if (!X11->has_fontconfig)
+ return;
+
+ FcConfig *config = FcConfigGetCurrent();
+ if (!config)
+ return;
+
+ FcFontSet *set = FcConfigGetFonts(config, FcSetApplication);
+ if (!set) {
+ FcConfigAppFontAddFile(config, (const FcChar8 *)":/non-existant");
+ set = FcConfigGetFonts(config, FcSetApplication); // try again
+ if (!set)
+ return;
+ }
+
+ QString fileNameForQuery = fnt->fileName;
+#if FC_VERSION < 20402
+ QTemporaryFile tmp;
+
+ if (!fnt->data.isEmpty()) {
+ if (!tmp.open())
+ return;
+ tmp.write(fnt->data);
+ tmp.flush();
+ fileNameForQuery = tmp.fileName();
+ }
+#endif
+
+ int id = 0;
+ FcBlanks *blanks = FcConfigGetBlanks(0);
+ int count = 0;
+
+ QStringList families;
+
+ FcPattern *pattern = 0;
+ do {
+ pattern = queryFont((const FcChar8 *)QFile::encodeName(fileNameForQuery).constData(),
+ fnt->data, id, blanks, &count);
+ if (!pattern)
+ return;
+
+ FcPatternDel(pattern, FC_FILE);
+ FcPatternAddString(pattern, FC_FILE, (const FcChar8 *)fnt->fileName.toUtf8().constData());
+
+ FcChar8 *fam = 0;
+ if (FcPatternGetString(pattern, FC_FAMILY, 0, &fam) == FcResultMatch) {
+ QString family = QString::fromUtf8(reinterpret_cast<const char *>(fam));
+ families << family;
+ }
+
+ if (!FcFontSetAdd(set, pattern))
+ return;
+
+ ++id;
+ } while (pattern && id < count);
+
+ fnt->families = families;
+#endif
+}
+
+bool QFontDatabase::removeApplicationFont(int handle)
+{
+#if defined(QT_NO_FONTCONFIG)
+ return false;
+#else
+ QMutexLocker locker(fontDatabaseMutex());
+
+ QFontDatabasePrivate *db = privateDb();
+ if (handle < 0 || handle >= db->applicationFonts.count())
+ return false;
+
+ FcConfigAppFontClear(0);
+
+ db->applicationFonts[handle] = QFontDatabasePrivate::ApplicationFont();
+
+ db->reregisterAppFonts = true;
+ db->invalidate();
+ return true;
+#endif
+}
+
+bool QFontDatabase::removeAllApplicationFonts()
+{
+#if defined(QT_NO_FONTCONFIG)
+ return false;
+#else
+ QMutexLocker locker(fontDatabaseMutex());
+
+ QFontDatabasePrivate *db = privateDb();
+ if (db->applicationFonts.isEmpty())
+ return false;
+
+ FcConfigAppFontClear(0);
+ db->applicationFonts.clear();
+ db->invalidate();
+ return true;
+#endif
+}
+
+bool QFontDatabase::supportsThreadedFontRendering()
+{
+#if defined(QT_NO_FONTCONFIG)
+ return false;
+#else
+ return X11->has_fontconfig;
+#endif
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/text/qfontengine.cpp b/src/gui/text/qfontengine.cpp
new file mode 100644
index 0000000000..c4dffdf92b
--- /dev/null
+++ b/src/gui/text/qfontengine.cpp
@@ -0,0 +1,1623 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <qdebug.h>
+#include <private/qfontengine_p.h>
+
+#include "qbitmap.h"
+#include "qpainter.h"
+#include "qpainterpath.h"
+#include "qvarlengtharray.h"
+#include <private/qpdf_p.h>
+#include <qmath.h>
+#include <qendian.h>
+#include <private/qharfbuzz_p.h>
+
+QT_BEGIN_NAMESPACE
+
+static inline bool qtransform_equals_no_translate(const QTransform &a, const QTransform &b)
+{
+ if (a.type() <= QTransform::TxTranslate && b.type() <= QTransform::TxTranslate) {
+ return true;
+ } else {
+ // We always use paths for perspective text anyway, so no
+ // point in checking the full matrix...
+ Q_ASSERT(a.type() < QTransform::TxProject);
+ Q_ASSERT(b.type() < QTransform::TxProject);
+
+ return a.m11() == b.m11()
+ && a.m12() == b.m12()
+ && a.m21() == b.m21()
+ && a.m22() == b.m22();
+ }
+}
+
+
+
+QFontEngineGlyphCache::~QFontEngineGlyphCache()
+{
+}
+
+// Harfbuzz helper functions
+
+static HB_Bool hb_stringToGlyphs(HB_Font font, const HB_UChar16 *string, hb_uint32 length, HB_Glyph *glyphs, hb_uint32 *numGlyphs, HB_Bool rightToLeft)
+{
+ QFontEngine *fe = (QFontEngine *)font->userData;
+
+ QVarLengthGlyphLayoutArray qglyphs(*numGlyphs);
+
+ QTextEngine::ShaperFlags shaperFlags(QTextEngine::GlyphIndicesOnly);
+ if (rightToLeft)
+ shaperFlags |= QTextEngine::RightToLeft;
+
+ int nGlyphs = *numGlyphs;
+ bool result = fe->stringToCMap(reinterpret_cast<const QChar *>(string), length, &qglyphs, &nGlyphs, shaperFlags);
+ *numGlyphs = nGlyphs;
+ if (!result)
+ return false;
+
+ for (hb_uint32 i = 0; i < *numGlyphs; ++i)
+ glyphs[i] = qglyphs.glyphs[i];
+
+ return true;
+}
+
+static void hb_getAdvances(HB_Font font, const HB_Glyph *glyphs, hb_uint32 numGlyphs, HB_Fixed *advances, int flags)
+{
+ QFontEngine *fe = (QFontEngine *)font->userData;
+
+ QVarLengthGlyphLayoutArray qglyphs(numGlyphs);
+
+ for (hb_uint32 i = 0; i < numGlyphs; ++i)
+ qglyphs.glyphs[i] = glyphs[i];
+
+ fe->recalcAdvances(&qglyphs, flags & HB_ShaperFlag_UseDesignMetrics ? QFlags<QTextEngine::ShaperFlag>(QTextEngine::DesignMetrics) : QFlags<QTextEngine::ShaperFlag>(0));
+
+ for (hb_uint32 i = 0; i < numGlyphs; ++i)
+ advances[i] = qglyphs.advances_x[i].value();
+}
+
+static HB_Bool hb_canRender(HB_Font font, const HB_UChar16 *string, hb_uint32 length)
+{
+ QFontEngine *fe = (QFontEngine *)font->userData;
+ return fe->canRender(reinterpret_cast<const QChar *>(string), length);
+}
+
+static void hb_getGlyphMetrics(HB_Font font, HB_Glyph glyph, HB_GlyphMetrics *metrics)
+{
+ QFontEngine *fe = (QFontEngine *)font->userData;
+ glyph_metrics_t m = fe->boundingBox(glyph);
+ metrics->x = m.x.value();
+ metrics->y = m.y.value();
+ metrics->width = m.width.value();
+ metrics->height = m.height.value();
+ metrics->xOffset = m.xoff.value();
+ metrics->yOffset = m.yoff.value();
+}
+
+static HB_Fixed hb_getFontMetric(HB_Font font, HB_FontMetric metric)
+{
+ if (metric == HB_FontAscent) {
+ QFontEngine *fe = (QFontEngine *)font->userData;
+ return fe->ascent().value();
+ }
+ return 0;
+}
+
+HB_Error QFontEngine::getPointInOutline(HB_Glyph glyph, int flags, hb_uint32 point, HB_Fixed *xpos, HB_Fixed *ypos, hb_uint32 *nPoints)
+{
+ Q_UNUSED(glyph)
+ Q_UNUSED(flags)
+ Q_UNUSED(point)
+ Q_UNUSED(xpos)
+ Q_UNUSED(ypos)
+ Q_UNUSED(nPoints)
+ return HB_Err_Not_Covered;
+}
+
+static HB_Error hb_getPointInOutline(HB_Font font, HB_Glyph glyph, int flags, hb_uint32 point, HB_Fixed *xpos, HB_Fixed *ypos, hb_uint32 *nPoints)
+{
+ QFontEngine *fe = (QFontEngine *)font->userData;
+ return fe->getPointInOutline(glyph, flags, point, xpos, ypos, nPoints);
+}
+
+static const HB_FontClass hb_fontClass = {
+ hb_stringToGlyphs, hb_getAdvances, hb_canRender, hb_getPointInOutline,
+ hb_getGlyphMetrics, hb_getFontMetric
+};
+
+static HB_Error hb_getSFntTable(void *font, HB_Tag tableTag, HB_Byte *buffer, HB_UInt *length)
+{
+ QFontEngine *fe = (QFontEngine *)font;
+ if (!fe->getSfntTableData(tableTag, buffer, length))
+ return HB_Err_Invalid_Argument;
+ return HB_Err_Ok;
+}
+
+// QFontEngine
+
+QFontEngine::QFontEngine()
+ : QObject()
+{
+ ref = 0;
+ cache_count = 0;
+ fsType = 0;
+ symbol = false;
+ memset(&hbFont, 0, sizeof(hbFont));
+ hbFont.klass = &hb_fontClass;
+ hbFont.userData = this;
+
+ hbFace = 0;
+ glyphFormat = -1;
+}
+
+QFontEngine::~QFontEngine()
+{
+ for (GlyphPointerHash::iterator it = m_glyphPointerHash.begin(), end = m_glyphPointerHash.end();
+ it != end; ++it) {
+ for (QList<QFontEngineGlyphCache*>::iterator it2 = it.value().begin(), end2 = it.value().end();
+ it2 != end2; ++it2)
+ delete *it2;
+ }
+ m_glyphPointerHash.clear();
+ for (GlyphIntHash::iterator it = m_glyphIntHash.begin(), end = m_glyphIntHash.end();
+ it != end; ++it) {
+ for (QList<QFontEngineGlyphCache*>::iterator it2 = it.value().begin(), end2 = it.value().end();
+ it2 != end2; ++it2)
+ delete *it2;
+ }
+ m_glyphIntHash.clear();
+ qHBFreeFace(hbFace);
+}
+
+QFixed QFontEngine::lineThickness() const
+{
+ // ad hoc algorithm
+ int score = fontDef.weight * fontDef.pixelSize;
+ int lw = score / 700;
+
+ // looks better with thicker line for small pointsizes
+ if (lw < 2 && score >= 1050) lw = 2;
+ if (lw == 0) lw = 1;
+
+ return lw;
+}
+
+QFixed QFontEngine::underlinePosition() const
+{
+ return ((lineThickness() * 2) + 3) / 6;
+}
+
+HB_Font QFontEngine::harfbuzzFont() const
+{
+ if (!hbFont.x_ppem) {
+ QFixed emSquare = emSquareSize();
+ hbFont.x_ppem = fontDef.pixelSize;
+ hbFont.y_ppem = fontDef.pixelSize * fontDef.stretch / 100;
+ hbFont.x_scale = (QFixed(hbFont.x_ppem * (1 << 16)) / emSquare).value();
+ hbFont.y_scale = (QFixed(hbFont.y_ppem * (1 << 16)) / emSquare).value();
+ }
+ return &hbFont;
+}
+
+HB_Face QFontEngine::harfbuzzFace() const
+{
+ if (!hbFace)
+ hbFace = qHBNewFace(const_cast<QFontEngine *>(this), hb_getSFntTable);
+ return hbFace;
+}
+
+glyph_metrics_t QFontEngine::boundingBox(glyph_t glyph, const QTransform &matrix)
+{
+ glyph_metrics_t metrics = boundingBox(glyph);
+
+ if (matrix.type() > QTransform::TxTranslate) {
+ return metrics.transformed(matrix);
+ }
+ return metrics;
+}
+
+QFixed QFontEngine::xHeight() const
+{
+ QGlyphLayoutArray<8> glyphs;
+ int nglyphs = 7;
+ QChar x((ushort)'x');
+ stringToCMap(&x, 1, &glyphs, &nglyphs, QTextEngine::GlyphIndicesOnly);
+
+ glyph_metrics_t bb = const_cast<QFontEngine *>(this)->boundingBox(glyphs.glyphs[0]);
+ return bb.height;
+}
+
+QFixed QFontEngine::averageCharWidth() const
+{
+ QGlyphLayoutArray<8> glyphs;
+ int nglyphs = 7;
+ QChar x((ushort)'x');
+ stringToCMap(&x, 1, &glyphs, &nglyphs, QTextEngine::GlyphIndicesOnly);
+
+ glyph_metrics_t bb = const_cast<QFontEngine *>(this)->boundingBox(glyphs.glyphs[0]);
+ return bb.xoff;
+}
+
+
+void QFontEngine::getGlyphPositions(const QGlyphLayout &glyphs, const QTransform &matrix, QTextItem::RenderFlags flags,
+ QVarLengthArray<glyph_t> &glyphs_out, QVarLengthArray<QFixedPoint> &positions)
+{
+ QFixed xpos;
+ QFixed ypos;
+
+ const bool transform = matrix.m11() != 1.
+ || matrix.m12() != 0.
+ || matrix.m21() != 0.
+ || matrix.m22() != 1.;
+ if (!transform) {
+ xpos = QFixed::fromReal(matrix.dx());
+ ypos = QFixed::fromReal(matrix.dy());
+ }
+
+ int current = 0;
+ if (flags & QTextItem::RightToLeft) {
+ int i = glyphs.numGlyphs;
+ int totalKashidas = 0;
+ while(i--) {
+ xpos += glyphs.advances_x[i] + QFixed::fromFixed(glyphs.justifications[i].space_18d6);
+ ypos += glyphs.advances_y[i];
+ totalKashidas += glyphs.justifications[i].nKashidas;
+ }
+ positions.resize(glyphs.numGlyphs+totalKashidas);
+ glyphs_out.resize(glyphs.numGlyphs+totalKashidas);
+
+ i = 0;
+ while(i < glyphs.numGlyphs) {
+ if (glyphs.attributes[i].dontPrint) {
+ ++i;
+ continue;
+ }
+ xpos -= glyphs.advances_x[i];
+ ypos -= glyphs.advances_y[i];
+
+ QFixed gpos_x = xpos + glyphs.offsets[i].x;
+ QFixed gpos_y = ypos + glyphs.offsets[i].y;
+ if (transform) {
+ QPointF gpos(gpos_x.toReal(), gpos_y.toReal());
+ gpos = gpos * matrix;
+ gpos_x = QFixed::fromReal(gpos.x());
+ gpos_y = QFixed::fromReal(gpos.y());
+ }
+ positions[current].x = gpos_x;
+ positions[current].y = gpos_y;
+ glyphs_out[current] = glyphs.glyphs[i];
+ ++current;
+ if (glyphs.justifications[i].nKashidas) {
+ QChar ch(0x640); // Kashida character
+ QGlyphLayoutArray<8> g;
+ int nglyphs = 7;
+ stringToCMap(&ch, 1, &g, &nglyphs, 0);
+ for (uint k = 0; k < glyphs.justifications[i].nKashidas; ++k) {
+ xpos -= g.advances_x[0];
+ ypos -= g.advances_y[0];
+
+ QFixed gpos_x = xpos + glyphs.offsets[i].x;
+ QFixed gpos_y = ypos + glyphs.offsets[i].y;
+ if (transform) {
+ QPointF gpos(gpos_x.toReal(), gpos_y.toReal());
+ gpos = gpos * matrix;
+ gpos_x = QFixed::fromReal(gpos.x());
+ gpos_y = QFixed::fromReal(gpos.y());
+ }
+ positions[current].x = gpos_x;
+ positions[current].y = gpos_y;
+ glyphs_out[current] = g.glyphs[0];
+ ++current;
+ }
+ } else {
+ xpos -= QFixed::fromFixed(glyphs.justifications[i].space_18d6);
+ }
+ ++i;
+ }
+ } else {
+ positions.resize(glyphs.numGlyphs);
+ glyphs_out.resize(glyphs.numGlyphs);
+ int i = 0;
+ if (!transform) {
+ while (i < glyphs.numGlyphs) {
+ if (!glyphs.attributes[i].dontPrint) {
+ positions[current].x = xpos + glyphs.offsets[i].x;
+ positions[current].y = ypos + glyphs.offsets[i].y;
+ glyphs_out[current] = glyphs.glyphs[i];
+ xpos += glyphs.advances_x[i] + QFixed::fromFixed(glyphs.justifications[i].space_18d6);
+ ypos += glyphs.advances_y[i];
+ ++current;
+ }
+ ++i;
+ }
+ } else {
+ positions.resize(glyphs.numGlyphs);
+ glyphs_out.resize(glyphs.numGlyphs);
+ int i = 0;
+ while (i < glyphs.numGlyphs) {
+ if (!glyphs.attributes[i].dontPrint) {
+ QFixed gpos_x = xpos + glyphs.offsets[i].x;
+ QFixed gpos_y = ypos + glyphs.offsets[i].y;
+ QPointF gpos(gpos_x.toReal(), gpos_y.toReal());
+ gpos = gpos * matrix;
+ positions[current].x = QFixed::fromReal(gpos.x());
+ positions[current].y = QFixed::fromReal(gpos.y());
+ glyphs_out[current] = glyphs.glyphs[i];
+ xpos += glyphs.advances_x[i] + QFixed::fromFixed(glyphs.justifications[i].space_18d6);
+ ypos += glyphs.advances_y[i];
+ ++current;
+ }
+ ++i;
+ }
+ }
+ }
+ positions.resize(current);
+ glyphs_out.resize(current);
+ Q_ASSERT(positions.size() == glyphs_out.size());
+}
+
+
+glyph_metrics_t QFontEngine::tightBoundingBox(const QGlyphLayout &glyphs)
+{
+ glyph_metrics_t overall;
+
+ QFixed ymax = 0;
+ QFixed xmax = 0;
+ for (int i = 0; i < glyphs.numGlyphs; i++) {
+ glyph_metrics_t bb = boundingBox(glyphs.glyphs[i]);
+ QFixed x = overall.xoff + glyphs.offsets[i].x + bb.x;
+ QFixed y = overall.yoff + glyphs.offsets[i].y + bb.y;
+ overall.x = qMin(overall.x, x);
+ overall.y = qMin(overall.y, y);
+ xmax = qMax(xmax, x + bb.width);
+ ymax = qMax(ymax, y + bb.height);
+ overall.xoff += bb.xoff;
+ overall.yoff += bb.yoff;
+ }
+ overall.height = qMax(overall.height, ymax - overall.y);
+ overall.width = xmax - overall.x;
+
+ return overall;
+}
+
+
+void QFontEngine::addOutlineToPath(qreal x, qreal y, const QGlyphLayout &glyphs, QPainterPath *path,
+ QTextItem::RenderFlags flags)
+{
+ if (!glyphs.numGlyphs)
+ return;
+
+ QVarLengthArray<QFixedPoint> positions;
+ QVarLengthArray<glyph_t> positioned_glyphs;
+ QTransform matrix;
+ matrix.translate(x, y);
+ getGlyphPositions(glyphs, matrix, flags, positioned_glyphs, positions);
+ addGlyphsToPath(positioned_glyphs.data(), positions.data(), positioned_glyphs.size(), path, flags);
+}
+
+#define GRID(x, y) grid[(y)*(w+1) + (x)]
+#define SET(x, y) (*(image_data + (y)*bpl + ((x) >> 3)) & (0x80 >> ((x) & 7)))
+
+enum { EdgeRight = 0x1,
+ EdgeDown = 0x2,
+ EdgeLeft = 0x4,
+ EdgeUp = 0x8
+};
+
+static void collectSingleContour(qreal x0, qreal y0, uint *grid, int x, int y, int w, int h, QPainterPath *path)
+{
+ Q_UNUSED(h);
+
+ path->moveTo(x + x0, y + y0);
+ while (GRID(x, y)) {
+ if (GRID(x, y) & EdgeRight) {
+ while (GRID(x, y) & EdgeRight) {
+ GRID(x, y) &= ~EdgeRight;
+ ++x;
+ }
+ Q_ASSERT(x <= w);
+ path->lineTo(x + x0, y + y0);
+ continue;
+ }
+ if (GRID(x, y) & EdgeDown) {
+ while (GRID(x, y) & EdgeDown) {
+ GRID(x, y) &= ~EdgeDown;
+ ++y;
+ }
+ Q_ASSERT(y <= h);
+ path->lineTo(x + x0, y + y0);
+ continue;
+ }
+ if (GRID(x, y) & EdgeLeft) {
+ while (GRID(x, y) & EdgeLeft) {
+ GRID(x, y) &= ~EdgeLeft;
+ --x;
+ }
+ Q_ASSERT(x >= 0);
+ path->lineTo(x + x0, y + y0);
+ continue;
+ }
+ if (GRID(x, y) & EdgeUp) {
+ while (GRID(x, y) & EdgeUp) {
+ GRID(x, y) &= ~EdgeUp;
+ --y;
+ }
+ Q_ASSERT(y >= 0);
+ path->lineTo(x + x0, y + y0);
+ continue;
+ }
+ }
+ path->closeSubpath();
+}
+
+void qt_addBitmapToPath(qreal x0, qreal y0, const uchar *image_data, int bpl, int w, int h, QPainterPath *path)
+{
+ uint *grid = new uint[(w+1)*(h+1)];
+ // set up edges
+ for (int y = 0; y <= h; ++y) {
+ for (int x = 0; x <= w; ++x) {
+ bool topLeft = (x == 0)|(y == 0) ? false : SET(x - 1, y - 1);
+ bool topRight = (x == w)|(y == 0) ? false : SET(x, y - 1);
+ bool bottomLeft = (x == 0)|(y == h) ? false : SET(x - 1, y);
+ bool bottomRight = (x == w)|(y == h) ? false : SET(x, y);
+
+ GRID(x, y) = 0;
+ if ((!topRight) & bottomRight)
+ GRID(x, y) |= EdgeRight;
+ if ((!bottomRight) & bottomLeft)
+ GRID(x, y) |= EdgeDown;
+ if ((!bottomLeft) & topLeft)
+ GRID(x, y) |= EdgeLeft;
+ if ((!topLeft) & topRight)
+ GRID(x, y) |= EdgeUp;
+ }
+ }
+
+ // collect edges
+ for (int y = 0; y < h; ++y) {
+ for (int x = 0; x < w; ++x) {
+ if (!GRID(x, y))
+ continue;
+ // found start of a contour, follow it
+ collectSingleContour(x0, y0, grid, x, y, w, h, path);
+ }
+ }
+ delete [] grid;
+}
+
+#undef GRID
+#undef SET
+
+
+void QFontEngine::addBitmapFontToPath(qreal x, qreal y, const QGlyphLayout &glyphs,
+ QPainterPath *path, QTextItem::RenderFlags flags)
+{
+// TODO what to do with 'flags' ??
+ Q_UNUSED(flags);
+ QFixed advanceX = QFixed::fromReal(x);
+ QFixed advanceY = QFixed::fromReal(y);
+ for (int i=0; i < glyphs.numGlyphs; ++i) {
+ glyph_metrics_t metrics = boundingBox(glyphs.glyphs[i]);
+ if (metrics.width.value() == 0 || metrics.height.value() == 0) {
+ advanceX += glyphs.advances_x[i];
+ advanceY += glyphs.advances_y[i];
+ continue;
+ }
+ QImage alphaMask = alphaMapForGlyph(glyphs.glyphs[i]);
+
+ const int w = alphaMask.width();
+ const int h = alphaMask.height();
+ const int srcBpl = alphaMask.bytesPerLine();
+ QImage bitmap;
+ if (alphaMask.depth() == 1) {
+ bitmap = alphaMask;
+ } else {
+ bitmap = QImage(w, h, QImage::Format_Mono);
+ const uchar *imageData = alphaMask.bits();
+ const int destBpl = bitmap.bytesPerLine();
+ uchar *bitmapData = bitmap.bits();
+
+ for (int yi = 0; yi < h; ++yi) {
+ const uchar *src = imageData + yi*srcBpl;
+ uchar *dst = bitmapData + yi*destBpl;
+ for (int xi = 0; xi < w; ++xi) {
+ const int byte = xi / 8;
+ const int bit = xi % 8;
+ if (bit == 0)
+ dst[byte] = 0;
+ if (src[xi])
+ dst[byte] |= 128 >> bit;
+ }
+ }
+ }
+ const uchar *bitmap_data = bitmap.bits();
+ QFixedPoint offset = glyphs.offsets[i];
+ advanceX += offset.x;
+ advanceY += offset.y;
+ qt_addBitmapToPath((advanceX + metrics.x).toReal(), (advanceY + metrics.y).toReal(), bitmap_data, bitmap.bytesPerLine(), w, h, path);
+ advanceX += glyphs.advances_x[i];
+ advanceY += glyphs.advances_y[i];
+ }
+}
+
+void QFontEngine::addGlyphsToPath(glyph_t *glyphs, QFixedPoint *positions, int nGlyphs,
+ QPainterPath *path, QTextItem::RenderFlags flags)
+{
+ qreal x = positions[0].x.toReal();
+ qreal y = positions[0].y.toReal();
+ QVarLengthGlyphLayoutArray g(nGlyphs);
+
+ for (int i = 0; i < nGlyphs; ++i) {
+ g.glyphs[i] = glyphs[i];
+ if (i < nGlyphs - 1) {
+ g.advances_x[i] = positions[i+1].x - positions[i].x;
+ g.advances_y[i] = positions[i+1].y - positions[i].y;
+ } else {
+ g.advances_x[i] = QFixed::fromReal(maxCharWidth());
+ g.advances_y[i] = 0;
+ }
+ }
+
+ addBitmapFontToPath(x, y, g, path, flags);
+}
+
+QImage QFontEngine::alphaMapForGlyph(glyph_t glyph, const QTransform &t)
+{
+ QImage i = alphaMapForGlyph(glyph);
+ if (t.type() > QTransform::TxTranslate)
+ i = i.transformed(t);
+ Q_ASSERT(i.depth() <= 8); // To verify that transformed didn't change the format...
+ return i;
+}
+
+QImage QFontEngine::alphaRGBMapForGlyph(glyph_t glyph, int /* margin */, const QTransform &t)
+{
+ QImage alphaMask = alphaMapForGlyph(glyph, t);
+ QImage rgbMask(alphaMask.width(), alphaMask.height(), QImage::Format_RGB32);
+
+ for (int y=0; y<alphaMask.height(); ++y) {
+ uint *dst = (uint *) rgbMask.scanLine(y);
+ uchar *src = (uchar *) alphaMask.scanLine(y);
+ for (int x=0; x<alphaMask.width(); ++x)
+ dst[x] = qRgb(src[x], src[x], src[x]);
+ }
+
+ return rgbMask;
+}
+
+
+void QFontEngine::removeGlyphFromCache(glyph_t)
+{
+}
+
+QFontEngine::Properties QFontEngine::properties() const
+{
+ Properties p;
+#ifndef QT_NO_PRINTER
+ QByteArray psname = QPdf::stripSpecialCharacters(fontDef.family.toUtf8());
+#else
+ QByteArray psname = fontDef.family.toUtf8();
+#endif
+ psname += '-';
+ psname += QByteArray::number(fontDef.style);
+ psname += '-';
+ psname += QByteArray::number(fontDef.weight);
+
+ p.postscriptName = psname;
+ p.ascent = ascent();
+ p.descent = descent();
+ p.leading = leading();
+ p.emSquare = p.ascent;
+ p.boundingBox = QRectF(0, -p.ascent.toReal(), maxCharWidth(), (p.ascent + p.descent).toReal());
+ p.italicAngle = 0;
+ p.capHeight = p.ascent;
+ p.lineWidth = lineThickness();
+ return p;
+}
+
+void QFontEngine::getUnscaledGlyph(glyph_t glyph, QPainterPath *path, glyph_metrics_t *metrics)
+{
+ *metrics = boundingBox(glyph);
+ QFixedPoint p;
+ p.x = 0;
+ p.y = 0;
+ addGlyphsToPath(&glyph, &p, 1, path, QFlag(0));
+}
+
+QByteArray QFontEngine::getSfntTable(uint tag) const
+{
+ QByteArray table;
+ uint len = 0;
+ if (!getSfntTableData(tag, 0, &len))
+ return table;
+ if (!len)
+ return table;
+ table.resize(len);
+ if (!getSfntTableData(tag, reinterpret_cast<uchar *>(table.data()), &len))
+ return QByteArray();
+ return table;
+}
+
+void QFontEngine::expireGlyphCache()
+{
+ if (m_glyphCacheQueue.count() > 10) { // hold only 10 caches in memory.
+ QFontEngineGlyphCache *old = m_glyphCacheQueue.takeFirst();
+ // remove the value from either of our hashes
+ for (GlyphPointerHash::iterator i = m_glyphPointerHash.begin(); i != m_glyphPointerHash.end(); ++i) {
+ QList<QFontEngineGlyphCache *> list = i.value();
+ if (list.removeAll(old)) {
+ if (list.isEmpty())
+ m_glyphPointerHash.remove(i.key());
+ else
+ m_glyphPointerHash.insert(i.key(), list);
+ break;
+ }
+ }
+ for (GlyphIntHash::iterator i = m_glyphIntHash.begin(); i != m_glyphIntHash.end(); ++i) {
+ QList<QFontEngineGlyphCache *> list = i.value();
+ if (list.removeAll(old)) {
+ if (list.isEmpty())
+ m_glyphIntHash.remove(i.key());
+ else
+ m_glyphIntHash.insert(i.key(), list);
+ break;
+ }
+ }
+ delete old;
+ }
+}
+
+void QFontEngine::setGlyphCache(void *key, QFontEngineGlyphCache *data)
+{
+ Q_ASSERT(data);
+ QList<QFontEngineGlyphCache*> items = m_glyphPointerHash.value(key);
+
+ for (QList<QFontEngineGlyphCache*>::iterator it = items.begin(), end = items.end(); it != end; ++it) {
+ QFontEngineGlyphCache *c = *it;
+ if (qtransform_equals_no_translate(c->m_transform, data->m_transform)) {
+ if (c == data)
+ return;
+ items.removeAll(c);
+ delete c;
+ break;
+ }
+ }
+ items.append(data);
+ m_glyphPointerHash.insert(key, items);
+
+ m_glyphCacheQueue.append(data);
+ expireGlyphCache();
+}
+
+void QFontEngine::setGlyphCache(QFontEngineGlyphCache::Type key, QFontEngineGlyphCache *data)
+{
+ Q_ASSERT(data);
+ QList<QFontEngineGlyphCache*> items = m_glyphIntHash.value(key);
+
+ for (QList<QFontEngineGlyphCache*>::iterator it = items.begin(), end = items.end(); it != end; ++it) {
+ QFontEngineGlyphCache *c = *it;
+ if (qtransform_equals_no_translate(c->m_transform, data->m_transform)) {
+ if (c == data)
+ return;
+ items.removeAll(c);
+ delete c;
+ break;
+ }
+ }
+ items.append(data);
+ m_glyphIntHash.insert(key, items);
+
+ m_glyphCacheQueue.append(data);
+ expireGlyphCache();
+}
+
+QFontEngineGlyphCache *QFontEngine::glyphCache(void *key, const QTransform &transform) const
+{
+ QList<QFontEngineGlyphCache*> items = m_glyphPointerHash.value(key);
+
+ for (QList<QFontEngineGlyphCache*>::iterator it = items.begin(), end = items.end(); it != end; ++it) {
+ QFontEngineGlyphCache *c = *it;
+ if (qtransform_equals_no_translate(c->m_transform, transform)) {
+ m_glyphCacheQueue.removeAll(c); // last used, move it up
+ m_glyphCacheQueue.append(c);
+ return c;
+ }
+ }
+ return 0;
+}
+
+QFontEngineGlyphCache *QFontEngine::glyphCache(QFontEngineGlyphCache::Type key, const QTransform &transform) const
+{
+ QList<QFontEngineGlyphCache*> items = m_glyphIntHash.value(key);
+
+ for (QList<QFontEngineGlyphCache*>::iterator it = items.begin(), end = items.end(); it != end; ++it) {
+ QFontEngineGlyphCache *c = *it;
+ if (qtransform_equals_no_translate(c->m_transform, transform)) {
+ m_glyphCacheQueue.removeAll(c); // last used, move it up
+ m_glyphCacheQueue.append(c);
+ return c;
+ }
+ }
+ return 0;
+}
+
+#if defined(Q_WS_WIN) || defined(Q_WS_X11) || defined(Q_WS_QWS)
+static inline QFixed kerning(int left, int right, const QFontEngine::KernPair *pairs, int numPairs)
+{
+ uint left_right = (left << 16) + right;
+
+ left = 0, right = numPairs - 1;
+ while (left <= right) {
+ int middle = left + ( ( right - left ) >> 1 );
+
+ if(pairs[middle].left_right == left_right)
+ return pairs[middle].adjust;
+
+ if (pairs[middle].left_right < left_right)
+ left = middle + 1;
+ else
+ right = middle - 1;
+ }
+ return 0;
+}
+
+void QFontEngine::doKerning(QGlyphLayout *glyphs, QTextEngine::ShaperFlags flags) const
+{
+ int numPairs = kerning_pairs.size();
+ if(!numPairs)
+ return;
+
+ const KernPair *pairs = kerning_pairs.constData();
+
+ if(flags & QTextEngine::DesignMetrics) {
+ for(int i = 0; i < glyphs->numGlyphs - 1; ++i)
+ glyphs->advances_x[i] += kerning(glyphs->glyphs[i], glyphs->glyphs[i+1] , pairs, numPairs);
+ } else {
+ for(int i = 0; i < glyphs->numGlyphs - 1; ++i)
+ glyphs->advances_x[i] += qRound(kerning(glyphs->glyphs[i], glyphs->glyphs[i+1] , pairs, numPairs));
+ }
+}
+
+void QFontEngine::loadKerningPairs(QFixed scalingFactor)
+{
+ kerning_pairs.clear();
+
+ QByteArray tab = getSfntTable(MAKE_TAG('k', 'e', 'r', 'n'));
+ if (tab.isEmpty())
+ return;
+
+ const uchar *table = reinterpret_cast<const uchar *>(tab.constData());
+
+ unsigned short version = qFromBigEndian<quint16>(table);
+ if (version != 0) {
+// qDebug("wrong version");
+ return;
+ }
+
+ unsigned short numTables = qFromBigEndian<quint16>(table + 2);
+ {
+ int offset = 4;
+ for(int i = 0; i < numTables; ++i) {
+ if (offset + 6 > tab.size()) {
+// qDebug("offset out of bounds");
+ goto end;
+ }
+ const uchar *header = table + offset;
+
+ ushort version = qFromBigEndian<quint16>(header);
+ ushort length = qFromBigEndian<quint16>(header+2);
+ ushort coverage = qFromBigEndian<quint16>(header+4);
+// qDebug("subtable: version=%d, coverage=%x",version, coverage);
+ if(version == 0 && coverage == 0x0001) {
+ if (offset + length > tab.size()) {
+// qDebug("length ouf ot bounds");
+ goto end;
+ }
+ const uchar *data = table + offset + 6;
+
+ ushort nPairs = qFromBigEndian<quint16>(data);
+ if(nPairs * 6 + 8 > length - 6) {
+// qDebug("corrupt table!");
+ // corrupt table
+ goto end;
+ }
+
+ int off = 8;
+ for(int i = 0; i < nPairs; ++i) {
+ QFontEngine::KernPair p;
+ p.left_right = (((uint)qFromBigEndian<quint16>(data+off)) << 16) + qFromBigEndian<quint16>(data+off+2);
+ p.adjust = QFixed(((int)(short)qFromBigEndian<quint16>(data+off+4))) / scalingFactor;
+ kerning_pairs.append(p);
+ off += 6;
+ }
+ }
+ offset += length;
+ }
+ }
+end:
+ qSort(kerning_pairs);
+// for (int i = 0; i < kerning_pairs.count(); ++i)
+// qDebug() << "i" << i << "left_right" << hex << kerning_pairs.at(i).left_right;
+}
+
+#else
+void QFontEngine::doKerning(QGlyphLayout *, QTextEngine::ShaperFlags) const
+{
+}
+#endif
+
+int QFontEngine::glyphCount() const
+{
+ QByteArray maxpTable = getSfntTable(MAKE_TAG('m', 'a', 'x', 'p'));
+ if (maxpTable.size() < 6)
+ return 0;
+ return qFromBigEndian<quint16>(reinterpret_cast<const uchar *>(maxpTable.constData() + 4));
+}
+
+const uchar *QFontEngine::getCMap(const uchar *table, uint tableSize, bool *isSymbolFont, int *cmapSize)
+{
+ const uchar *header = table;
+ if (tableSize < 4)
+ return 0;
+
+ const uchar *endPtr = table + tableSize;
+
+ // version check
+ if (qFromBigEndian<quint16>(header) != 0)
+ return 0;
+
+ unsigned short numTables = qFromBigEndian<quint16>(header + 2);
+ const uchar *maps = table + 4;
+ if (maps + 8 * numTables > endPtr)
+ return 0;
+
+ int tableToUse = -1;
+ int score = 0;
+ for (int n = 0; n < numTables; ++n) {
+ const quint16 platformId = qFromBigEndian<quint16>(maps + 8 * n);
+ const quint16 platformSpecificId = qFromBigEndian<quint16>(maps + 8 * n + 2);
+ switch (platformId) {
+ case 0: // Unicode
+ if (score < 4 &&
+ (platformSpecificId == 0 ||
+ platformSpecificId == 2 ||
+ platformSpecificId == 3)) {
+ tableToUse = n;
+ score = 4;
+ } else if (score < 3 && platformSpecificId == 1) {
+ tableToUse = n;
+ score = 3;
+ }
+ break;
+ case 1: // Apple
+ if (score < 2 && platformSpecificId == 0) { // Apple Roman
+ tableToUse = n;
+ score = 2;
+ }
+ break;
+ case 3: // Microsoft
+ switch (platformSpecificId) {
+ case 0:
+ if (score < 1) {
+ tableToUse = n;
+ score = 1;
+ }
+ break;
+ case 1:
+ if (score < 5) {
+ tableToUse = n;
+ score = 5;
+ }
+ break;
+ case 0xa:
+ if (score < 6) {
+ tableToUse = n;
+ score = 6;
+ }
+ break;
+ default:
+ break;
+ }
+ default:
+ break;
+ }
+ }
+ if(tableToUse < 0)
+ return 0;
+ *isSymbolFont = (score == 1);
+
+ unsigned int unicode_table = qFromBigEndian<quint32>(maps + 8*tableToUse + 4);
+
+ if (!unicode_table || unicode_table + 8 > tableSize)
+ return 0;
+
+ // get the header of the unicode table
+ header = table + unicode_table;
+
+ unsigned short format = qFromBigEndian<quint16>(header);
+ unsigned int length;
+ if(format < 8)
+ length = qFromBigEndian<quint16>(header + 2);
+ else
+ length = qFromBigEndian<quint32>(header + 4);
+
+ if (table + unicode_table + length > endPtr)
+ return 0;
+ *cmapSize = length;
+ return table + unicode_table;
+}
+
+quint32 QFontEngine::getTrueTypeGlyphIndex(const uchar *cmap, uint unicode)
+{
+ unsigned short format = qFromBigEndian<quint16>(cmap);
+ if (format == 0) {
+ if (unicode < 256)
+ return (int) *(cmap+6+unicode);
+ } else if (format == 4) {
+ /* some fonts come with invalid cmap tables, where the last segment
+ specified end = start = rangeoffset = 0xffff, delta = 0x0001
+ Since 0xffff is never a valid Unicode char anyway, we just get rid of the issue
+ by returning 0 for 0xffff
+ */
+ if(unicode >= 0xffff)
+ return 0;
+ quint16 segCountX2 = qFromBigEndian<quint16>(cmap + 6);
+ const unsigned char *ends = cmap + 14;
+ quint16 endIndex = 0;
+ int i = 0;
+ for (; i < segCountX2/2 && (endIndex = qFromBigEndian<quint16>(ends + 2*i)) < unicode; i++) {}
+
+ const unsigned char *idx = ends + segCountX2 + 2 + 2*i;
+ quint16 startIndex = qFromBigEndian<quint16>(idx);
+
+ if (startIndex > unicode)
+ return 0;
+
+ idx += segCountX2;
+ qint16 idDelta = (qint16)qFromBigEndian<quint16>(idx);
+ idx += segCountX2;
+ quint16 idRangeoffset_t = (quint16)qFromBigEndian<quint16>(idx);
+
+ quint16 glyphIndex;
+ if (idRangeoffset_t) {
+ quint16 id = qFromBigEndian<quint16>(idRangeoffset_t + 2*(unicode - startIndex) + idx);
+ if (id)
+ glyphIndex = (idDelta + id) % 0x10000;
+ else
+ glyphIndex = 0;
+ } else {
+ glyphIndex = (idDelta + unicode) % 0x10000;
+ }
+ return glyphIndex;
+ } else if (format == 6) {
+ quint16 tableSize = qFromBigEndian<quint16>(cmap + 2);
+
+ quint16 firstCode6 = qFromBigEndian<quint16>(cmap + 6);
+ if (unicode < firstCode6)
+ return 0;
+
+ quint16 entryCount6 = qFromBigEndian<quint16>(cmap + 8);
+ if (entryCount6 * 2 + 10 > tableSize)
+ return 0;
+
+ quint16 sentinel6 = firstCode6 + entryCount6;
+ if (unicode >= sentinel6)
+ return 0;
+
+ quint16 entryIndex6 = unicode - firstCode6;
+ return qFromBigEndian<quint16>(cmap + 10 + (entryIndex6 * 2));
+ } else if (format == 12) {
+ quint32 nGroups = qFromBigEndian<quint32>(cmap + 12);
+
+ cmap += 16; // move to start of groups
+
+ int left = 0, right = nGroups - 1;
+ while (left <= right) {
+ int middle = left + ( ( right - left ) >> 1 );
+
+ quint32 startCharCode = qFromBigEndian<quint32>(cmap + 12*middle);
+ if(unicode < startCharCode)
+ right = middle - 1;
+ else {
+ quint32 endCharCode = qFromBigEndian<quint32>(cmap + 12*middle + 4);
+ if(unicode <= endCharCode)
+ return qFromBigEndian<quint32>(cmap + 12*middle + 8) + unicode - startCharCode;
+ left = middle + 1;
+ }
+ }
+ } else {
+ qDebug("cmap table of format %d not implemented", format);
+ }
+
+ return 0;
+}
+
+// ------------------------------------------------------------------
+// The box font engine
+// ------------------------------------------------------------------
+
+QFontEngineBox::QFontEngineBox(int size)
+ : _size(size)
+{
+ cache_cost = sizeof(QFontEngineBox);
+}
+
+QFontEngineBox::~QFontEngineBox()
+{
+}
+
+bool QFontEngineBox::stringToCMap(const QChar *, int len, QGlyphLayout *glyphs, int *nglyphs, QTextEngine::ShaperFlags) const
+{
+ if (*nglyphs < len) {
+ *nglyphs = len;
+ return false;
+ }
+
+ for (int i = 0; i < len; i++) {
+ glyphs->glyphs[i] = 0;
+ glyphs->advances_x[i] = _size;
+ glyphs->advances_y[i] = 0;
+ }
+
+ *nglyphs = len;
+ glyphs->numGlyphs = len;
+ return true;
+}
+
+void QFontEngineBox::recalcAdvances(QGlyphLayout *glyphs, QTextEngine::ShaperFlags) const
+{
+ for (int i = 0; i < glyphs->numGlyphs; i++) {
+ glyphs->advances_x[i] = _size;
+ glyphs->advances_y[i] = 0;
+ }
+}
+
+void QFontEngineBox::addOutlineToPath(qreal x, qreal y, const QGlyphLayout &glyphs, QPainterPath *path, QTextItem::RenderFlags flags)
+{
+ if (!glyphs.numGlyphs)
+ return;
+
+ QVarLengthArray<QFixedPoint> positions;
+ QVarLengthArray<glyph_t> positioned_glyphs;
+ QTransform matrix;
+ matrix.translate(x, y - _size);
+ getGlyphPositions(glyphs, matrix, flags, positioned_glyphs, positions);
+
+ QSize s(_size - 3, _size - 3);
+ for (int k = 0; k < positions.size(); k++)
+ path->addRect(QRectF(positions[k].toPointF(), s));
+}
+
+glyph_metrics_t QFontEngineBox::boundingBox(const QGlyphLayout &glyphs)
+{
+ glyph_metrics_t overall;
+ overall.width = _size*glyphs.numGlyphs;
+ overall.height = _size;
+ overall.xoff = overall.width;
+ return overall;
+}
+
+#if defined(Q_WS_QWS)
+void QFontEngineBox::draw(QPaintEngine *p, qreal x, qreal y, const QTextItemInt &ti)
+{
+ if (!ti.glyphs.numGlyphs)
+ return;
+
+ // any fixes here should probably also be done in QPaintEnginePrivate::drawBoxTextItem
+ QSize s(_size - 3, _size - 3);
+
+ QVarLengthArray<QFixedPoint> positions;
+ QVarLengthArray<glyph_t> glyphs;
+ QTransform matrix;
+ matrix.translate(x, y - _size);
+ ti.fontEngine->getGlyphPositions(ti.glyphs, matrix, ti.flags, glyphs, positions);
+ if (glyphs.size() == 0)
+ return;
+
+
+ QPainter *painter = p->painter();
+ painter->save();
+ painter->setBrush(Qt::NoBrush);
+ QPen pen = painter->pen();
+ pen.setWidthF(lineThickness().toReal());
+ painter->setPen(pen);
+ for (int k = 0; k < positions.size(); k++)
+ painter->drawRect(QRectF(positions[k].toPointF(), s));
+ painter->restore();
+}
+#endif
+
+glyph_metrics_t QFontEngineBox::boundingBox(glyph_t)
+{
+ return glyph_metrics_t(0, -_size, _size, _size, _size, 0);
+}
+
+
+
+QFixed QFontEngineBox::ascent() const
+{
+ return _size;
+}
+
+QFixed QFontEngineBox::descent() const
+{
+ return 0;
+}
+
+QFixed QFontEngineBox::leading() const
+{
+ QFixed l = _size * QFixed::fromReal(qreal(0.15));
+ return l.ceil();
+}
+
+qreal QFontEngineBox::maxCharWidth() const
+{
+ return _size;
+}
+
+#ifdef Q_WS_X11
+int QFontEngineBox::cmap() const
+{
+ return -1;
+}
+#endif
+
+const char *QFontEngineBox::name() const
+{
+ return "null";
+}
+
+bool QFontEngineBox::canRender(const QChar *, int)
+{
+ return true;
+}
+
+QFontEngine::Type QFontEngineBox::type() const
+{
+ return Box;
+}
+
+QImage QFontEngineBox::alphaMapForGlyph(glyph_t)
+{
+ QImage image(_size, _size, QImage::Format_Indexed8);
+ QVector<QRgb> colors(256);
+ for (int i=0; i<256; ++i)
+ colors[i] = qRgba(0, 0, 0, i);
+ image.setColorTable(colors);
+ image.fill(0);
+
+ // can't use qpainter for index8; so use setPixel to draw our rectangle.
+ for (int i=2; i <= _size-3; ++i) {
+ image.setPixel(i, 2, 255);
+ image.setPixel(i, _size-3, 255);
+ image.setPixel(2, i, 255);
+ image.setPixel(_size-3, i, 255);
+ }
+ return image;
+}
+
+// ------------------------------------------------------------------
+// Multi engine
+// ------------------------------------------------------------------
+
+static inline uchar highByte(glyph_t glyph)
+{ return glyph >> 24; }
+
+// strip high byte from glyph
+static inline glyph_t stripped(glyph_t glyph)
+{ return glyph & 0x00ffffff; }
+
+QFontEngineMulti::QFontEngineMulti(int engineCount)
+{
+ engines.fill(0, engineCount);
+ cache_cost = 0;
+}
+
+QFontEngineMulti::~QFontEngineMulti()
+{
+ for (int i = 0; i < engines.size(); ++i) {
+ QFontEngine *fontEngine = engines.at(i);
+ if (fontEngine) {
+ fontEngine->ref.deref();
+ if (fontEngine->cache_count == 0 && fontEngine->ref == 0)
+ delete fontEngine;
+ }
+ }
+}
+
+bool QFontEngineMulti::stringToCMap(const QChar *str, int len,
+ QGlyphLayout *glyphs, int *nglyphs,
+ QTextEngine::ShaperFlags flags) const
+{
+ int ng = *nglyphs;
+ if (!engine(0)->stringToCMap(str, len, glyphs, &ng, flags))
+ return false;
+
+ int glyph_pos = 0;
+ for (int i = 0; i < len; ++i) {
+ bool surrogate = (str[i].unicode() >= 0xd800 && str[i].unicode() < 0xdc00 && i < len-1
+ && str[i+1].unicode() >= 0xdc00 && str[i+1].unicode() < 0xe000);
+ if (glyphs->glyphs[glyph_pos] == 0) {
+
+ QGlyphLayoutInstance tmp = glyphs->instance(glyph_pos);
+ for (int x = 1; x < engines.size(); ++x) {
+ QFontEngine *engine = engines.at(x);
+ if (!engine) {
+ const_cast<QFontEngineMulti *>(this)->loadEngine(x);
+ engine = engines.at(x);
+ }
+ Q_ASSERT(engine != 0);
+ if (engine->type() == Box)
+ continue;
+ glyphs->advances_x[glyph_pos] = glyphs->advances_y[glyph_pos] = 0;
+ glyphs->offsets[glyph_pos] = QFixedPoint();
+ int num = 2;
+ QGlyphLayout offs = glyphs->mid(glyph_pos, num);
+ engine->stringToCMap(str + i, surrogate ? 2 : 1, &offs, &num, flags);
+ Q_ASSERT(num == 1); // surrogates only give 1 glyph
+ if (glyphs->glyphs[glyph_pos]) {
+ // set the high byte to indicate which engine the glyph came from
+ glyphs->glyphs[glyph_pos] |= (x << 24);
+ break;
+ }
+ }
+ // ensure we use metrics from the 1st font when we use the fallback image.
+ if (!glyphs->glyphs[glyph_pos]) {
+ glyphs->setInstance(glyph_pos, tmp);
+ }
+ }
+ if (surrogate)
+ ++i;
+ ++glyph_pos;
+ }
+
+ *nglyphs = ng;
+ glyphs->numGlyphs = ng;
+ return true;
+}
+
+glyph_metrics_t QFontEngineMulti::boundingBox(const QGlyphLayout &glyphs)
+{
+ if (glyphs.numGlyphs <= 0)
+ return glyph_metrics_t();
+
+ glyph_metrics_t overall;
+
+ int which = highByte(glyphs.glyphs[0]);
+ int start = 0;
+ int end, i;
+ for (end = 0; end < glyphs.numGlyphs; ++end) {
+ const int e = highByte(glyphs.glyphs[end]);
+ if (e == which)
+ continue;
+
+ // set the high byte to zero
+ for (i = start; i < end; ++i)
+ glyphs.glyphs[i] = stripped(glyphs.glyphs[i]);
+
+ // merge the bounding box for this run
+ const glyph_metrics_t gm = engine(which)->boundingBox(glyphs.mid(start, end - start));
+
+ overall.x = qMin(overall.x, gm.x);
+ overall.y = qMin(overall.y, gm.y);
+ overall.width = overall.xoff + gm.width;
+ overall.height = qMax(overall.height + overall.y, gm.height + gm.y) -
+ qMin(overall.y, gm.y);
+ overall.xoff += gm.xoff;
+ overall.yoff += gm.yoff;
+
+ // reset the high byte for all glyphs
+ const int hi = which << 24;
+ for (i = start; i < end; ++i)
+ glyphs.glyphs[i] = hi | glyphs.glyphs[i];
+
+ // change engine
+ start = end;
+ which = e;
+ }
+
+ // set the high byte to zero
+ for (i = start; i < end; ++i)
+ glyphs.glyphs[i] = stripped(glyphs.glyphs[i]);
+
+ // merge the bounding box for this run
+ const glyph_metrics_t gm = engine(which)->boundingBox(glyphs.mid(start, end - start));
+
+ overall.x = qMin(overall.x, gm.x);
+ overall.y = qMin(overall.y, gm.y);
+ overall.width = overall.xoff + gm.width;
+ overall.height = qMax(overall.height + overall.y, gm.height + gm.y) -
+ qMin(overall.y, gm.y);
+ overall.xoff += gm.xoff;
+ overall.yoff += gm.yoff;
+
+ // reset the high byte for all glyphs
+ const int hi = which << 24;
+ for (i = start; i < end; ++i)
+ glyphs.glyphs[i] = hi | glyphs.glyphs[i];
+
+ return overall;
+}
+
+void QFontEngineMulti::addOutlineToPath(qreal x, qreal y, const QGlyphLayout &glyphs,
+ QPainterPath *path, QTextItem::RenderFlags flags)
+{
+ if (glyphs.numGlyphs <= 0)
+ return;
+
+ int which = highByte(glyphs.glyphs[0]);
+ int start = 0;
+ int end, i;
+ if (flags & QTextItem::RightToLeft) {
+ for (int gl = 0; gl < glyphs.numGlyphs; gl++) {
+ x += glyphs.advances_x[gl].toReal();
+ y += glyphs.advances_y[gl].toReal();
+ }
+ }
+ for (end = 0; end < glyphs.numGlyphs; ++end) {
+ const int e = highByte(glyphs.glyphs[end]);
+ if (e == which)
+ continue;
+
+ if (flags & QTextItem::RightToLeft) {
+ for (i = start; i < end; ++i) {
+ x -= glyphs.advances_x[i].toReal();
+ y -= glyphs.advances_y[i].toReal();
+ }
+ }
+
+ // set the high byte to zero
+ for (i = start; i < end; ++i)
+ glyphs.glyphs[i] = stripped(glyphs.glyphs[i]);
+ engine(which)->addOutlineToPath(x, y, glyphs.mid(start, end - start), path, flags);
+ // reset the high byte for all glyphs and update x and y
+ const int hi = which << 24;
+ for (i = start; i < end; ++i)
+ glyphs.glyphs[i] = hi | glyphs.glyphs[i];
+
+ if (!(flags & QTextItem::RightToLeft)) {
+ for (i = start; i < end; ++i) {
+ x += glyphs.advances_x[i].toReal();
+ y += glyphs.advances_y[i].toReal();
+ }
+ }
+
+ // change engine
+ start = end;
+ which = e;
+ }
+
+ if (flags & QTextItem::RightToLeft) {
+ for (i = start; i < end; ++i) {
+ x -= glyphs.advances_x[i].toReal();
+ y -= glyphs.advances_y[i].toReal();
+ }
+ }
+
+ // set the high byte to zero
+ for (i = start; i < end; ++i)
+ glyphs.glyphs[i] = stripped(glyphs.glyphs[i]);
+
+ engine(which)->addOutlineToPath(x, y, glyphs.mid(start, end - start), path, flags);
+
+ // reset the high byte for all glyphs
+ const int hi = which << 24;
+ for (i = start; i < end; ++i)
+ glyphs.glyphs[i] = hi | glyphs.glyphs[i];
+}
+
+void QFontEngineMulti::recalcAdvances(QGlyphLayout *glyphs, QTextEngine::ShaperFlags flags) const
+{
+ if (glyphs->numGlyphs <= 0)
+ return;
+
+ int which = highByte(glyphs->glyphs[0]);
+ int start = 0;
+ int end, i;
+ for (end = 0; end < glyphs->numGlyphs; ++end) {
+ const int e = highByte(glyphs->glyphs[end]);
+ if (e == which)
+ continue;
+
+ // set the high byte to zero
+ for (i = start; i < end; ++i)
+ glyphs->glyphs[i] = stripped(glyphs->glyphs[i]);
+
+ QGlyphLayout offs = glyphs->mid(start, end - start);
+ engine(which)->recalcAdvances(&offs, flags);
+
+ // reset the high byte for all glyphs and update x and y
+ const int hi = which << 24;
+ for (i = start; i < end; ++i)
+ glyphs->glyphs[i] = hi | glyphs->glyphs[i];
+
+ // change engine
+ start = end;
+ which = e;
+ }
+
+ // set the high byte to zero
+ for (i = start; i < end; ++i)
+ glyphs->glyphs[i] = stripped(glyphs->glyphs[i]);
+
+ QGlyphLayout offs = glyphs->mid(start, end - start);
+ engine(which)->recalcAdvances(&offs, flags);
+
+ // reset the high byte for all glyphs
+ const int hi = which << 24;
+ for (i = start; i < end; ++i)
+ glyphs->glyphs[i] = hi | glyphs->glyphs[i];
+}
+
+void QFontEngineMulti::doKerning(QGlyphLayout *glyphs, QTextEngine::ShaperFlags flags) const
+{
+ if (glyphs->numGlyphs <= 0)
+ return;
+
+ int which = highByte(glyphs->glyphs[0]);
+ int start = 0;
+ int end, i;
+ for (end = 0; end < glyphs->numGlyphs; ++end) {
+ const int e = highByte(glyphs->glyphs[end]);
+ if (e == which)
+ continue;
+
+ // set the high byte to zero
+ for (i = start; i < end; ++i)
+ glyphs->glyphs[i] = stripped(glyphs->glyphs[i]);
+
+ QGlyphLayout offs = glyphs->mid(start, end - start);
+ engine(which)->doKerning(&offs, flags);
+
+ // reset the high byte for all glyphs and update x and y
+ const int hi = which << 24;
+ for (i = start; i < end; ++i)
+ glyphs->glyphs[i] = hi | glyphs->glyphs[i];
+
+ // change engine
+ start = end;
+ which = e;
+ }
+
+ // set the high byte to zero
+ for (i = start; i < end; ++i)
+ glyphs->glyphs[i] = stripped(glyphs->glyphs[i]);
+
+ QGlyphLayout offs = glyphs->mid(start, end - start);
+ engine(which)->doKerning(&offs, flags);
+
+ // reset the high byte for all glyphs
+ const int hi = which << 24;
+ for (i = start; i < end; ++i)
+ glyphs->glyphs[i] = hi | glyphs->glyphs[i];
+}
+
+glyph_metrics_t QFontEngineMulti::boundingBox(glyph_t glyph)
+{
+ const int which = highByte(glyph);
+ Q_ASSERT(which < engines.size());
+ return engine(which)->boundingBox(stripped(glyph));
+}
+
+QFixed QFontEngineMulti::ascent() const
+{ return engine(0)->ascent(); }
+
+QFixed QFontEngineMulti::descent() const
+{ return engine(0)->descent(); }
+
+QFixed QFontEngineMulti::leading() const
+{
+ return engine(0)->leading();
+}
+
+QFixed QFontEngineMulti::xHeight() const
+{
+ return engine(0)->xHeight();
+}
+
+QFixed QFontEngineMulti::averageCharWidth() const
+{
+ return engine(0)->averageCharWidth();
+}
+
+QFixed QFontEngineMulti::lineThickness() const
+{
+ return engine(0)->lineThickness();
+}
+
+QFixed QFontEngineMulti::underlinePosition() const
+{
+ return engine(0)->underlinePosition();
+}
+
+qreal QFontEngineMulti::maxCharWidth() const
+{
+ return engine(0)->maxCharWidth();
+}
+
+qreal QFontEngineMulti::minLeftBearing() const
+{
+ return engine(0)->minLeftBearing();
+}
+
+qreal QFontEngineMulti::minRightBearing() const
+{
+ return engine(0)->minRightBearing();
+}
+
+bool QFontEngineMulti::canRender(const QChar *string, int len)
+{
+ if (engine(0)->canRender(string, len))
+ return true;
+
+ QVarLengthGlyphLayoutArray glyphs(len);
+ int nglyphs = len;
+ if (stringToCMap(string, len, &glyphs, &nglyphs, QTextEngine::GlyphIndicesOnly) == false) {
+ glyphs.resize(nglyphs);
+ stringToCMap(string, len, &glyphs, &nglyphs, QTextEngine::GlyphIndicesOnly);
+ }
+
+ bool allExist = true;
+ for (int i = 0; i < nglyphs; i++) {
+ if (!glyphs.glyphs[i]) {
+ allExist = false;
+ break;
+ }
+ }
+
+ return allExist;
+}
+
+QFontEngine *QFontEngineMulti::engine(int at) const
+{
+ Q_ASSERT(at < engines.size());
+ return engines.at(at);
+}
+
+QImage QFontEngineMulti::alphaMapForGlyph(glyph_t)
+{
+ Q_ASSERT(false);
+ return QImage();
+}
+
+
+QT_END_NAMESPACE
diff --git a/src/gui/text/qfontengine_ft.cpp b/src/gui/text/qfontengine_ft.cpp
new file mode 100644
index 0000000000..45a25a47b9
--- /dev/null
+++ b/src/gui/text/qfontengine_ft.cpp
@@ -0,0 +1,1904 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qdir.h"
+#include "qmetatype.h"
+#include "qtextstream.h"
+#include "qvariant.h"
+#include "qfontengine_ft_p.h"
+
+#ifndef QT_NO_FREETYPE
+
+#include "qfile.h"
+#include "qabstractfileengine.h"
+#include "qthreadstorage.h"
+#include <qmath.h>
+#include <private/qpdf_p.h>
+#include <private/qharfbuzz_p.h>
+
+#include <private/qpdf_p.h>
+
+#include "qfontengine_ft_p.h"
+#include <ft2build.h>
+#include FT_FREETYPE_H
+#include FT_OUTLINE_H
+#include FT_TRUETYPE_TABLES_H
+#include FT_TYPE1_TABLES_H
+#include FT_GLYPH_H
+
+#if defined(FT_LCD_FILTER_H)
+#include FT_LCD_FILTER_H
+#endif
+
+#if defined(FT_CONFIG_OPTIONS_H)
+#include FT_CONFIG_OPTIONS_H
+#endif
+
+#if defined(FT_LCD_FILTER_H) && defined(FT_CONFIG_OPTION_SUBPIXEL_RENDERING)
+#define QT_USE_FREETYPE_LCDFILTER
+#endif
+
+#ifdef QT_LINUXBASE
+#include FT_ERRORS_H
+#endif
+
+QT_BEGIN_NAMESPACE
+
+/*
+ * Freetype 2.1.7 and earlier used width/height
+ * for matching sizes in the BDF and PCF loaders.
+ * This has been fixed for 2.1.8.
+ */
+#if (FREETYPE_MAJOR*10000+FREETYPE_MINOR*100+FREETYPE_PATCH) >= 20105
+#define X_SIZE(face,i) ((face)->available_sizes[i].x_ppem)
+#define Y_SIZE(face,i) ((face)->available_sizes[i].y_ppem)
+#else
+#define X_SIZE(face,i) ((face)->available_sizes[i].width << 6)
+#define Y_SIZE(face,i) ((face)->available_sizes[i].height << 6)
+#endif
+
+#define FLOOR(x) ((x) & -64)
+#define CEIL(x) (((x)+63) & -64)
+#define TRUNC(x) ((x) >> 6)
+#define ROUND(x) (((x)+32) & -64)
+
+static HB_Error hb_getSFntTable(void *font, HB_Tag tableTag, HB_Byte *buffer, HB_UInt *length)
+{
+#if (FREETYPE_MAJOR*10000 + FREETYPE_MINOR*100 + FREETYPE_PATCH) > 20103
+ FT_Face face = (FT_Face)font;
+ FT_ULong ftlen = *length;
+ FT_Error error = 0;
+
+ if ( !FT_IS_SFNT(face) )
+ return HB_Err_Invalid_Argument;
+
+ error = FT_Load_Sfnt_Table(face, tableTag, 0, buffer, &ftlen);
+ *length = ftlen;
+ return (HB_Error)error;
+#else
+ return HB_Err_Invalid_Argument;
+#endif
+}
+
+// -------------------------- Freetype support ------------------------------
+
+class QtFreetypeData
+{
+public:
+ QtFreetypeData()
+ : library(0)
+ { }
+
+ FT_Library library;
+ QHash<QFontEngine::FaceId, QFreetypeFace *> faces;
+};
+
+#ifdef QT_NO_THREAD
+Q_GLOBAL_STATIC(QtFreetypeData, theFreetypeData)
+
+QtFreetypeData *qt_getFreetypeData()
+{
+ return theFreetypeData();
+}
+#else
+Q_GLOBAL_STATIC(QThreadStorage<QtFreetypeData *>, theFreetypeData)
+
+QtFreetypeData *qt_getFreetypeData()
+{
+ QtFreetypeData *&freetypeData = theFreetypeData()->localData();
+ if (!freetypeData)
+ freetypeData = new QtFreetypeData;
+ return freetypeData;
+}
+#endif
+
+FT_Library qt_getFreetype()
+{
+ QtFreetypeData *freetypeData = qt_getFreetypeData();
+ if (!freetypeData->library)
+ FT_Init_FreeType(&freetypeData->library);
+ return freetypeData->library;
+}
+
+int QFreetypeFace::fsType() const
+{
+ int fsType = 0;
+ TT_OS2 *os2 = (TT_OS2 *)FT_Get_Sfnt_Table(face, ft_sfnt_os2);
+ if (os2)
+ fsType = os2->fsType;
+ return fsType;
+}
+
+HB_Error QFreetypeFace::getPointInOutline(HB_Glyph glyph, int flags, hb_uint32 point, HB_Fixed *xpos, HB_Fixed *ypos, hb_uint32 *nPoints)
+{
+ int load_flags = (flags & HB_ShaperFlag_UseDesignMetrics) ? FT_LOAD_NO_HINTING : FT_LOAD_DEFAULT;
+
+ if (HB_Error error = (HB_Error)FT_Load_Glyph(face, glyph, load_flags))
+ return error;
+
+ if (face->glyph->format != FT_GLYPH_FORMAT_OUTLINE)
+ return HB_Err_Invalid_SubTable;
+
+ *nPoints = face->glyph->outline.n_points;
+ if (!(*nPoints))
+ return HB_Err_Ok;
+
+ if (point > *nPoints)
+ return HB_Err_Invalid_SubTable;
+
+ *xpos = face->glyph->outline.points[point].x;
+ *ypos = face->glyph->outline.points[point].y;
+
+ return HB_Err_Ok;
+}
+
+/*
+ * One font file can contain more than one font (bold/italic for example)
+ * find the right one and return it.
+ */
+QFreetypeFace *QFreetypeFace::getFace(const QFontEngine::FaceId &face_id)
+{
+ if (face_id.filename.isEmpty())
+ return 0;
+
+ QtFreetypeData *freetypeData = qt_getFreetypeData();
+ if (!freetypeData->library)
+ FT_Init_FreeType(&freetypeData->library);
+
+ QFreetypeFace *freetype = freetypeData->faces.value(face_id, 0);
+ if (!freetype) {
+ freetype = new QFreetypeFace;
+ FT_Face face;
+ QFile file(QString::fromUtf8(face_id.filename));
+ if (face_id.filename.startsWith(":qmemoryfonts/")) {
+ // from qfontdatabase.cpp
+ extern QByteArray qt_fontdata_from_index(int);
+ QByteArray idx = face_id.filename;
+ idx.remove(0, 14); // remove ':qmemoryfonts/'
+ bool ok = false;
+ freetype->fontData = qt_fontdata_from_index(idx.toInt(&ok));
+ if (!ok)
+ freetype->fontData = QByteArray();
+ } else if (!(file.fileEngine()->fileFlags(QAbstractFileEngine::FlagsMask) & QAbstractFileEngine::LocalDiskFlag)) {
+ if (!file.open(QIODevice::ReadOnly)) {
+ delete freetype;
+ return 0;
+ }
+ freetype->fontData = file.readAll();
+ }
+ if (!freetype->fontData.isEmpty()) {
+ if (FT_New_Memory_Face(freetypeData->library, (const FT_Byte *)freetype->fontData.constData(), freetype->fontData.size(), face_id.index, &face)) {
+ delete freetype;
+ return 0;
+ }
+ } else if (FT_New_Face(freetypeData->library, face_id.filename, face_id.index, &face)) {
+ delete freetype;
+ return 0;
+ }
+ freetype->face = face;
+
+ freetype->hbFace = qHBNewFace(face, hb_getSFntTable);
+ freetype->ref = 0;
+ freetype->xsize = 0;
+ freetype->ysize = 0;
+ freetype->matrix.xx = 0x10000;
+ freetype->matrix.yy = 0x10000;
+ freetype->matrix.xy = 0;
+ freetype->matrix.yx = 0;
+ freetype->unicode_map = 0;
+ freetype->symbol_map = 0;
+#ifndef QT_NO_FONTCONFIG
+ freetype->charset = 0;
+#endif
+
+ memset(freetype->cmapCache, 0, sizeof(freetype->cmapCache));
+
+ for (int i = 0; i < freetype->face->num_charmaps; ++i) {
+ FT_CharMap cm = freetype->face->charmaps[i];
+ switch(cm->encoding) {
+ case FT_ENCODING_UNICODE:
+ freetype->unicode_map = cm;
+ break;
+ case FT_ENCODING_APPLE_ROMAN:
+ case FT_ENCODING_ADOBE_LATIN_1:
+ if (!freetype->unicode_map || freetype->unicode_map->encoding != FT_ENCODING_UNICODE)
+ freetype->unicode_map = cm;
+ break;
+ case FT_ENCODING_ADOBE_CUSTOM:
+ case FT_ENCODING_MS_SYMBOL:
+ if (!freetype->symbol_map)
+ freetype->symbol_map = cm;
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (!FT_IS_SCALABLE(freetype->face) && freetype->face->num_fixed_sizes == 1)
+ FT_Set_Char_Size (face, X_SIZE(freetype->face, 0), Y_SIZE(freetype->face, 0), 0, 0);
+# if 0
+ FcChar8 *name;
+ FcPatternGetString(pattern, FC_FAMILY, 0, &name);
+ qDebug("%s: using maps: default: %x unicode: %x, symbol: %x", name,
+ freetype->face->charmap ? freetype->face->charmap->encoding : 0,
+ freetype->unicode_map ? freetype->unicode_map->encoding : 0,
+ freetype->symbol_map ? freetype->symbol_map->encoding : 0);
+
+ for (int i = 0; i < 256; i += 8)
+ qDebug(" %x: %d %d %d %d %d %d %d %d", i,
+ FcCharSetHasChar(freetype->charset, i), FcCharSetHasChar(freetype->charset, i),
+ FcCharSetHasChar(freetype->charset, i), FcCharSetHasChar(freetype->charset, i),
+ FcCharSetHasChar(freetype->charset, i), FcCharSetHasChar(freetype->charset, i),
+ FcCharSetHasChar(freetype->charset, i), FcCharSetHasChar(freetype->charset, i));
+#endif
+
+ FT_Set_Charmap(freetype->face, freetype->unicode_map);
+ freetypeData->faces.insert(face_id, freetype);
+ }
+ freetype->ref.ref();
+ return freetype;
+}
+
+void QFreetypeFace::release(const QFontEngine::FaceId &face_id)
+{
+ QtFreetypeData *freetypeData = qt_getFreetypeData();
+ if (!ref.deref()) {
+ qHBFreeFace(hbFace);
+ FT_Done_Face(face);
+#ifndef QT_NO_FONTCONFIG
+ if (charset)
+ FcCharSetDestroy(charset);
+#endif
+ freetypeData->faces.take(face_id);
+ delete this;
+ }
+ if (freetypeData->faces.isEmpty()) {
+ FT_Done_FreeType(freetypeData->library);
+ freetypeData->library = 0;
+ }
+}
+
+
+void QFreetypeFace::computeSize(const QFontDef &fontDef, int *xsize, int *ysize, bool *outline_drawing)
+{
+ *ysize = fontDef.pixelSize << 6;
+ *xsize = *ysize * fontDef.stretch / 100;
+ *outline_drawing = false;
+
+ /*
+ * Bitmap only faces must match exactly, so find the closest
+ * one (height dominant search)
+ */
+ if (!(face->face_flags & FT_FACE_FLAG_SCALABLE)) {
+ int best = 0;
+ for (int i = 1; i < face->num_fixed_sizes; i++) {
+ if (qAbs(*ysize - Y_SIZE(face,i)) <
+ qAbs (*ysize - Y_SIZE(face, best)) ||
+ (qAbs (*ysize - Y_SIZE(face, i)) ==
+ qAbs (*ysize - Y_SIZE(face, best)) &&
+ qAbs (*xsize - X_SIZE(face, i)) <
+ qAbs (*xsize - X_SIZE(face, best)))) {
+ best = i;
+ }
+ }
+ if (FT_Set_Char_Size (face, X_SIZE(face, best), Y_SIZE(face, best), 0, 0) == 0) {
+ *xsize = X_SIZE(face, best);
+ *ysize = Y_SIZE(face, best);
+ } else {
+ int err = 1;
+ if (!(face->face_flags & FT_FACE_FLAG_SCALABLE) && ysize == 0 && face->num_fixed_sizes >= 1) {
+ // work around FT 2.1.10 problem with BDF without PIXEL_SIZE property
+ err = FT_Set_Pixel_Sizes(face, face->available_sizes[0].width, face->available_sizes[0].height);
+ if (err && face->num_fixed_sizes == 1)
+ err = 0; //even more of a workaround...
+ }
+
+ if (err)
+ *xsize = *ysize = 0;
+ }
+ } else {
+ *outline_drawing = (*xsize > (64<<6) || *ysize > (64<<6));
+ }
+}
+
+QFontEngine::Properties QFreetypeFace::properties() const
+{
+ QFontEngine::Properties p;
+ p.postscriptName = FT_Get_Postscript_Name(face);
+ PS_FontInfoRec font_info;
+ if (FT_Get_PS_Font_Info(face, &font_info) == 0)
+ p.copyright = font_info.notice;
+ if (FT_IS_SCALABLE(face)) {
+ p.ascent = face->ascender;
+ p.descent = -face->descender;
+ p.leading = face->height - face->ascender + face->descender;
+ p.emSquare = face->units_per_EM;
+ p.boundingBox = QRectF(face->bbox.xMin, -face->bbox.yMax,
+ face->bbox.xMax - face->bbox.xMin,
+ face->bbox.yMax - face->bbox.yMin);
+ } else {
+ p.ascent = QFixed::fromFixed(face->size->metrics.ascender);
+ p.descent = QFixed::fromFixed(-face->size->metrics.descender);
+ p.leading = QFixed::fromFixed(face->size->metrics.height - face->size->metrics.ascender + face->size->metrics.descender);
+ p.emSquare = face->size->metrics.y_ppem;
+ p.boundingBox = QRectF(-p.ascent.toReal(), 0, (p.ascent + p.descent).toReal(), face->size->metrics.max_advance/64.);
+ }
+ p.italicAngle = 0;
+ p.capHeight = p.ascent;
+ p.lineWidth = face->underline_thickness;
+ return p;
+}
+
+bool QFreetypeFace::getSfntTable(uint tag, uchar *buffer, uint *length) const
+{
+ bool result = false;
+#if (FREETYPE_MAJOR*10000 + FREETYPE_MINOR*100 + FREETYPE_PATCH) > 20103
+ if (FT_IS_SFNT(face)) {
+ FT_ULong len = *length;
+ result = FT_Load_Sfnt_Table(face, tag, 0, buffer, &len) == FT_Err_Ok;
+ *length = len;
+ }
+#endif
+ return result;
+}
+
+/* Some fonts (such as MingLiu rely on hinting to scale different
+ components to their correct sizes. While this is really broken (it
+ should be done in the component glyph itself, not the hinter) we
+ will have to live with it.
+
+ This means we can not use FT_LOAD_NO_HINTING to get the glyph
+ outline. All we can do is to load the unscaled glyph and scale it
+ down manually when required.
+*/
+static void scaleOutline(FT_Face face, FT_GlyphSlot g, FT_Fixed x_scale, FT_Fixed y_scale)
+{
+ x_scale = FT_MulDiv(x_scale, 1 << 10, face->units_per_EM);
+ y_scale = FT_MulDiv(y_scale, 1 << 10, face->units_per_EM);
+ FT_Vector *p = g->outline.points;
+ const FT_Vector *e = p + g->outline.n_points;
+ while (p < e) {
+ p->x = FT_MulFix(p->x, x_scale);
+ p->y = FT_MulFix(p->y, y_scale);
+ ++p;
+ }
+}
+
+void QFreetypeFace::addGlyphToPath(FT_Face face, FT_GlyphSlot g, const QFixedPoint &point, QPainterPath *path, FT_Fixed x_scale, FT_Fixed y_scale)
+{
+ const qreal factor = 1/64.;
+ scaleOutline(face, g, x_scale, y_scale);
+
+ QPointF cp = point.toPointF();
+
+ // convert the outline to a painter path
+ int i = 0;
+ for (int j = 0; j < g->outline.n_contours; ++j) {
+ int last_point = g->outline.contours[j];
+ QPointF start = cp + QPointF(g->outline.points[i].x*factor, -g->outline.points[i].y*factor);
+ if(!(g->outline.tags[i] & 1)) {
+ start += cp + QPointF(g->outline.points[last_point].x*factor, -g->outline.points[last_point].y*factor);
+ start /= 2;
+ }
+// qDebug("contour: %d -- %d", i, g->outline.contours[j]);
+// qDebug("first point at %f %f", start.x(), start.y());
+ path->moveTo(start);
+
+ QPointF c[4];
+ c[0] = start;
+ int n = 1;
+ while (i < last_point) {
+ ++i;
+ c[n] = cp + QPointF(g->outline.points[i].x*factor, -g->outline.points[i].y*factor);
+// qDebug() << " i=" << i << " flag=" << (int)g->outline.tags[i] << "point=" << c[n];
+ ++n;
+ switch (g->outline.tags[i] & 3) {
+ case 2:
+ // cubic bezier element
+ if (n < 4)
+ continue;
+ c[3] = (c[3] + c[2])/2;
+ --i;
+ break;
+ case 0:
+ // quadratic bezier element
+ if (n < 3)
+ continue;
+ c[3] = (c[1] + c[2])/2;
+ c[2] = (2*c[1] + c[3])/3;
+ c[1] = (2*c[1] + c[0])/3;
+ --i;
+ break;
+ case 1:
+ case 3:
+ if (n == 2) {
+// qDebug() << "lineTo" << c[1];
+ path->lineTo(c[1]);
+ c[0] = c[1];
+ n = 1;
+ continue;
+ } else if (n == 3) {
+ c[3] = c[2];
+ c[2] = (2*c[1] + c[3])/3;
+ c[1] = (2*c[1] + c[0])/3;
+ }
+ break;
+ }
+// qDebug() << "cubicTo" << c[1] << c[2] << c[3];
+ path->cubicTo(c[1], c[2], c[3]);
+ c[0] = c[3];
+ n = 1;
+ }
+ if (n == 1) {
+// qDebug() << "closeSubpath";
+ path->closeSubpath();
+ } else {
+ c[3] = start;
+ if (n == 2) {
+ c[2] = (2*c[1] + c[3])/3;
+ c[1] = (2*c[1] + c[0])/3;
+ }
+// qDebug() << "cubicTo" << c[1] << c[2] << c[3];
+ path->cubicTo(c[1], c[2], c[3]);
+ }
+ ++i;
+ }
+}
+
+extern void qt_addBitmapToPath(qreal x0, qreal y0, const uchar *image_data, int bpl, int w, int h, QPainterPath *path);
+
+void QFreetypeFace::addBitmapToPath(FT_GlyphSlot slot, const QFixedPoint &point, QPainterPath *path, bool)
+{
+ if (slot->format != FT_GLYPH_FORMAT_BITMAP
+ || slot->bitmap.pixel_mode != FT_PIXEL_MODE_MONO)
+ return;
+
+ QPointF cp = point.toPointF();
+ qt_addBitmapToPath(cp.x() + TRUNC(slot->metrics.horiBearingX), cp.y() - TRUNC(slot->metrics.horiBearingY),
+ slot->bitmap.buffer, slot->bitmap.pitch, slot->bitmap.width, slot->bitmap.rows, path);
+}
+
+QFontEngineFT::Glyph::~Glyph()
+{
+ delete [] data;
+}
+
+static const uint subpixel_filter[3][3] = {
+ { 180, 60, 16 },
+ { 38, 180, 38 },
+ { 16, 60, 180 }
+};
+
+static inline uint filterPixel(uint red, uint green, uint blue, bool legacyFilter)
+{
+ uint res;
+ if (legacyFilter) {
+ uint high = (red*subpixel_filter[0][0] + green*subpixel_filter[0][1] + blue*subpixel_filter[0][2]) >> 8;
+ uint mid = (red*subpixel_filter[1][0] + green*subpixel_filter[1][1] + blue*subpixel_filter[1][2]) >> 8;
+ uint low = (red*subpixel_filter[2][0] + green*subpixel_filter[2][1] + blue*subpixel_filter[2][2]) >> 8;
+ res = (mid << 24) + (high << 16) + (mid << 8) + low;
+ } else {
+ uint alpha = green;
+ res = (alpha << 24) + (red << 16) + (green << 8) + blue;
+ }
+ return res;
+}
+
+static void convertRGBToARGB(const uchar *src, uint *dst, int width, int height, int src_pitch, bool bgr, bool legacyFilter)
+{
+ int h = height;
+ const int offs = bgr ? -1 : 1;
+ const int w = width * 3;
+ while (h--) {
+ uint *dd = dst;
+ for (int x = 0; x < w; x += 3) {
+ uint red = src[x+1-offs];
+ uint green = src[x+1];
+ uint blue = src[x+1+offs];
+ *dd = filterPixel(red, green, blue, legacyFilter);
+ ++dd;
+ }
+ dst += width;
+ src += src_pitch;
+ }
+}
+
+static void convertRGBToARGB_V(const uchar *src, uint *dst, int width, int height, int src_pitch, bool bgr, bool legacyFilter)
+{
+ int h = height;
+ const int offs = bgr ? -src_pitch : src_pitch;
+ while (h--) {
+ for (int x = 0; x < width; x++) {
+ uint red = src[x+src_pitch-offs];
+ uint green = src[x+src_pitch];
+ uint blue = src[x+src_pitch+offs];
+ dst[x] = filterPixel(red, green, blue, legacyFilter);
+ }
+ dst += width;
+ src += 3*src_pitch;
+ }
+}
+
+static void convoluteBitmap(const uchar *src, uchar *dst, int width, int height, int pitch)
+{
+ // convolute the bitmap with a triangle filter to get rid of color fringes
+ // If we take account for a gamma value of 2, we end up with
+ // weights of 1, 4, 9, 4, 1. We use an approximation of 1, 3, 8, 3, 1 here,
+ // as this nicely sums up to 16 :)
+ int h = height;
+ while (h--) {
+ dst[0] = dst[1] = 0;
+ //
+ for (int x = 2; x < width - 2; ++x) {
+ uint sum = src[x-2] + 3*src[x-1] + 8*src[x] + 3*src[x+1] + src[x+2];
+ dst[x] = (uchar) (sum >> 4);
+ }
+ dst[width - 2] = dst[width - 1] = 0;
+ src += pitch;
+ dst += pitch;
+ }
+}
+
+QFontEngineFT::QFontEngineFT(const QFontDef &fd)
+{
+ fontDef = fd;
+ matrix.xx = 0x10000;
+ matrix.yy = 0x10000;
+ matrix.xy = 0;
+ matrix.yx = 0;
+ cache_cost = 100;
+ kerning_pairs_loaded = false;
+ transform = false;
+ antialias = true;
+ default_load_flags = 0;
+ default_hint_style = HintNone;
+ subpixelType = Subpixel_None;
+ lcdFilterType = 0;
+#if defined(FT_LCD_FILTER_H)
+ lcdFilterType = (int) FT_LCD_FILTER_DEFAULT;
+#endif
+ defaultFormat = Format_None;
+ canUploadGlyphsToServer = false;
+ embeddedbitmap = false;
+}
+
+QFontEngineFT::~QFontEngineFT()
+{
+ if (freetype)
+ freetype->release(face_id);
+ hbFace = 0; // we share the face in QFreeTypeFace, don't let ~QFontEngine delete it
+}
+
+void QFontEngineFT::freeGlyphSets()
+{
+ freeServerGlyphSet(defaultGlyphSet.id);
+ for (int i = 0; i < transformedGlyphSets.count(); ++i)
+ freeServerGlyphSet(transformedGlyphSets.at(i).id);
+}
+
+bool QFontEngineFT::init(FaceId faceId, bool antialias, GlyphFormat format)
+{
+ defaultFormat = format;
+ this->antialias = antialias;
+ if (!antialias)
+ glyphFormat = QFontEngineGlyphCache::Raster_Mono;
+ face_id = faceId;
+ freetype = QFreetypeFace::getFace(face_id);
+ if (!freetype) {
+ xsize = 0;
+ ysize = 0;
+ return false;
+ }
+
+ symbol = freetype->symbol_map != 0;
+ PS_FontInfoRec psrec;
+ // don't assume that type1 fonts are symbol fonts by default
+ if (FT_Get_PS_Font_Info(freetype->face, &psrec) == FT_Err_Ok) {
+ symbol = bool(fontDef.family.contains(QLatin1String("symbol"), Qt::CaseInsensitive));
+ }
+ // #####
+ freetype->hbFace->isSymbolFont = symbol;
+
+ lbearing = rbearing = SHRT_MIN;
+ freetype->computeSize(fontDef, &xsize, &ysize, &defaultGlyphSet.outline_drawing);
+
+ FT_Face face = lockFace();
+
+ //underline metrics
+ if (FT_IS_SCALABLE(face)) {
+ line_thickness = QFixed::fromFixed(FT_MulFix(face->underline_thickness, face->size->metrics.y_scale));
+ underline_position = QFixed::fromFixed(-FT_MulFix(face->underline_position, face->size->metrics.y_scale));
+ bool fake_oblique = (fontDef.style != QFont::StyleNormal) && !(face->style_flags & FT_STYLE_FLAG_ITALIC);
+ if (fake_oblique)
+ matrix.xy = 0x10000*3/10;
+ FT_Set_Transform(face, &matrix, 0);
+ freetype->matrix = matrix;
+ if (fake_oblique)
+ transform = true;
+ } else {
+ // copied from QFontEngineQPF
+ // ad hoc algorithm
+ int score = fontDef.weight * fontDef.pixelSize;
+ line_thickness = score / 700;
+ // looks better with thicker line for small pointsizes
+ if (line_thickness < 2 && score >= 1050)
+ line_thickness = 2;
+ underline_position = ((line_thickness * 2) + 3) / 6;
+ }
+ if (line_thickness < 1)
+ line_thickness = 1;
+
+ hbFont.x_ppem = face->size->metrics.x_ppem;
+ hbFont.y_ppem = face->size->metrics.y_ppem;
+ hbFont.x_scale = face->size->metrics.x_scale;
+ hbFont.y_scale = face->size->metrics.y_scale;
+
+ hbFace = freetype->hbFace;
+
+ metrics = face->size->metrics;
+#if defined(Q_WS_QWS)
+ /*
+ TrueType fonts with embedded bitmaps may have a bitmap font specific
+ ascent/descent in the EBLC table. There is no direct public API
+ to extract those values. The only way we've found is to trick freetype
+ into thinking that it's not a scalable font in FT_SelectSize so that
+ the metrics are retrieved from the bitmap strikes.
+ */
+ if (FT_IS_SCALABLE(face)) {
+ for (int i = 0; i < face->num_fixed_sizes; ++i) {
+ if (xsize == X_SIZE(face, i) && ysize == Y_SIZE(face, i)) {
+ face->face_flags &= ~FT_FACE_FLAG_SCALABLE;
+
+ FT_Select_Size(face, i);
+ metrics.ascender = face->size->metrics.ascender;
+ metrics.descender = face->size->metrics.descender;
+ FT_Set_Char_Size(face, xsize, ysize, 0, 0);
+
+ face->face_flags |= FT_FACE_FLAG_SCALABLE;
+ break;
+ }
+ }
+ }
+#endif
+
+ unlockFace();
+
+ fsType = freetype->fsType();
+ defaultGlyphSet.id = allocateServerGlyphSet();
+ return true;
+}
+
+QFontEngineFT::Glyph *QFontEngineFT::loadGlyphMetrics(QGlyphSet *set, uint glyph) const
+{
+ Glyph *g = set->glyph_data.value(glyph);
+ if (g)
+ return g;
+
+ int load_flags = FT_LOAD_DEFAULT | default_load_flags;
+ if (set->outline_drawing)
+ load_flags = FT_LOAD_NO_BITMAP;
+
+ // apply our matrix to this, but note that the metrics will not be affected by this.
+ FT_Matrix matrix = freetype->matrix;
+ FT_Face face = lockFace();
+ matrix = this->matrix;
+ FT_Matrix_Multiply(&set->transformationMatrix, &matrix);
+ FT_Set_Transform(face, &matrix, 0);
+ freetype->matrix = matrix;
+
+ bool transform = matrix.xx != 0x10000 || matrix.yy != 0x10000 || matrix.xy != 0 || matrix.yx != 0;
+ if (transform)
+ load_flags |= FT_LOAD_NO_BITMAP;
+
+ FT_Error err = FT_Load_Glyph(face, glyph, load_flags);
+ if (err && (load_flags & FT_LOAD_NO_BITMAP)) {
+ load_flags &= ~FT_LOAD_NO_BITMAP;
+ err = FT_Load_Glyph(face, glyph, load_flags);
+ }
+ if (err == FT_Err_Too_Few_Arguments) {
+ // this is an error in the bytecode interpreter, just try to run without it
+ load_flags |= FT_LOAD_FORCE_AUTOHINT;
+ err = FT_Load_Glyph(face, glyph, load_flags);
+ }
+ if (err != FT_Err_Ok)
+ qWarning("load glyph failed err=%x face=%p, glyph=%d", err, face, glyph);
+
+ unlockFace();
+ if (set->outline_drawing)
+ return 0;
+
+ if (!g) {
+ g = new Glyph;
+ g->uploadedToServer = false;
+ g->data = 0;
+ }
+
+ FT_GlyphSlot slot = face->glyph;
+ int left = slot->metrics.horiBearingX;
+ int right = slot->metrics.horiBearingX + slot->metrics.width;
+ int top = slot->metrics.horiBearingY;
+ int bottom = slot->metrics.horiBearingY - slot->metrics.height;
+ if(transform && slot->format != FT_GLYPH_FORMAT_BITMAP) { // freetype doesn't apply the transformation on the metrics
+ int l, r, t, b;
+ FT_Vector vector;
+ vector.x = left;
+ vector.y = top;
+ FT_Vector_Transform(&vector, &matrix);
+ l = r = vector.x;
+ t = b = vector.y;
+ vector.x = right;
+ vector.y = top;
+ FT_Vector_Transform(&vector, &matrix);
+ if (l > vector.x) l = vector.x;
+ if (r < vector.x) r = vector.x;
+ if (t < vector.y) t = vector.y;
+ if (b > vector.y) b = vector.y;
+ vector.x = right;
+ vector.y = bottom;
+ FT_Vector_Transform(&vector, &matrix);
+ if (l > vector.x) l = vector.x;
+ if (r < vector.x) r = vector.x;
+ if (t < vector.y) t = vector.y;
+ if (b > vector.y) b = vector.y;
+ vector.x = left;
+ vector.y = bottom;
+ FT_Vector_Transform(&vector, &matrix);
+ if (l > vector.x) l = vector.x;
+ if (r < vector.x) r = vector.x;
+ if (t < vector.y) t = vector.y;
+ if (b > vector.y) b = vector.y;
+ left = l;
+ right = r;
+ top = t;
+ bottom = b;
+ }
+ left = FLOOR(left);
+ right = CEIL(right);
+ bottom = FLOOR(bottom);
+ top = CEIL(top);
+
+ g->linearAdvance = face->glyph->linearHoriAdvance >> 10;
+ g->width = TRUNC(right-left);
+ g->height = TRUNC(top-bottom);
+ g->x = TRUNC(left);
+ g->y = TRUNC(top);
+ g->advance = TRUNC(ROUND(face->glyph->advance.x));
+ g->format = Format_None;
+
+ return g;
+}
+
+QFontEngineFT::Glyph *QFontEngineFT::loadGlyph(QGlyphSet *set, uint glyph, GlyphFormat format, bool fetchMetricsOnly) const
+{
+// Q_ASSERT(freetype->lock == 1);
+
+ bool uploadToServer = false;
+ if (format == Format_None) {
+ if (defaultFormat != Format_None) {
+ format = defaultFormat;
+ if (canUploadGlyphsToServer)
+ uploadToServer = true;
+ } else {
+ format = Format_Mono;
+ }
+ }
+
+ Glyph *g = set->glyph_data.value(glyph);
+ if (g && g->format == format) {
+ if (uploadToServer && !g->uploadedToServer) {
+ set->glyph_data[glyph] = 0;
+ delete g;
+ g = 0;
+ } else {
+ return g;
+ }
+ }
+
+ QFontEngineFT::GlyphInfo info;
+
+ Q_ASSERT(format != Format_None);
+ bool hsubpixel = false;
+ int vfactor = 1;
+ int load_flags = FT_LOAD_DEFAULT | default_load_flags;
+
+ int load_target = default_hint_style == HintLight
+ ? FT_LOAD_TARGET_LIGHT
+ : FT_LOAD_TARGET_NORMAL;
+
+ if (set->outline_drawing)
+ load_flags |= FT_LOAD_NO_BITMAP;
+
+ if (format == Format_Mono) {
+ load_target = FT_LOAD_TARGET_MONO;
+ } else if (format == Format_A32) {
+ if (subpixelType == QFontEngineFT::Subpixel_RGB || subpixelType == QFontEngineFT::Subpixel_BGR) {
+ if (default_hint_style == HintFull)
+ load_target = FT_LOAD_TARGET_LCD;
+ hsubpixel = true;
+ } else if (subpixelType == QFontEngineFT::Subpixel_VRGB || subpixelType == QFontEngineFT::Subpixel_VBGR) {
+ if (default_hint_style == HintFull)
+ load_target = FT_LOAD_TARGET_LCD_V;
+ vfactor = 3;
+ }
+ }
+
+ if (default_hint_style == HintNone)
+ load_flags |= FT_LOAD_NO_HINTING;
+ else
+ load_flags |= load_target;
+
+#ifndef Q_WS_QWS
+ if (format != Format_Mono && !embeddedbitmap)
+ load_flags |= FT_LOAD_NO_BITMAP;
+#endif
+
+ FT_Matrix matrix = freetype->matrix;
+ bool transform = matrix.xx != 0x10000
+ || matrix.yy != 0x10000
+ || matrix.xy != 0
+ || matrix.yx != 0;
+
+ if (transform)
+ load_flags |= FT_LOAD_NO_BITMAP;
+
+ FT_Face face = freetype->face;
+ FT_Error err = FT_Load_Glyph(face, glyph, load_flags);
+ if (err && (load_flags & FT_LOAD_NO_BITMAP)) {
+ load_flags &= ~FT_LOAD_NO_BITMAP;
+ err = FT_Load_Glyph(face, glyph, load_flags);
+ }
+ if (err == FT_Err_Too_Few_Arguments) {
+ // this is an error in the bytecode interpreter, just try to run without it
+ load_flags |= FT_LOAD_FORCE_AUTOHINT;
+ err = FT_Load_Glyph(face, glyph, load_flags);
+ }
+ if (err != FT_Err_Ok)
+ qWarning("load glyph failed err=%x face=%p, glyph=%d", err, face, glyph);
+
+ if (set->outline_drawing && fetchMetricsOnly)
+ return 0;
+
+ FT_GlyphSlot slot = face->glyph;
+ FT_Library library = qt_getFreetype();
+
+ info.xOff = TRUNC(ROUND(slot->advance.x));
+ info.yOff = 0;
+
+ uchar *glyph_buffer = 0;
+ int glyph_buffer_size = 0;
+#if defined(QT_USE_FREETYPE_LCDFILTER)
+ bool useFreetypeRenderGlyph = false;
+ if (slot->format == FT_GLYPH_FORMAT_OUTLINE && (hsubpixel || vfactor != 1)) {
+ err = FT_Library_SetLcdFilter(library, (FT_LcdFilter)lcdFilterType);
+ if (err == FT_Err_Ok)
+ useFreetypeRenderGlyph = true;
+ }
+
+ if (useFreetypeRenderGlyph) {
+ err = FT_Render_Glyph(slot, hsubpixel ? FT_RENDER_MODE_LCD : FT_RENDER_MODE_LCD_V);
+
+ if (err != FT_Err_Ok)
+ qWarning("render glyph failed err=%x face=%p, glyph=%d", err, face, glyph);
+
+ FT_Library_SetLcdFilter(library, FT_LCD_FILTER_NONE);
+
+ info.height = slot->bitmap.rows / vfactor;
+ info.width = hsubpixel ? slot->bitmap.width / 3 : slot->bitmap.width;
+ info.x = -slot->bitmap_left;
+ info.y = slot->bitmap_top;
+
+ glyph_buffer_size = info.width * info.height * 4;
+ glyph_buffer = new uchar[glyph_buffer_size];
+
+ if (hsubpixel)
+ convertRGBToARGB(slot->bitmap.buffer, (uint *)glyph_buffer, info.width, info.height, slot->bitmap.pitch, subpixelType != QFontEngineFT::Subpixel_RGB, false);
+ else if (vfactor != 1)
+ convertRGBToARGB_V(slot->bitmap.buffer, (uint *)glyph_buffer, info.width, info.height, slot->bitmap.pitch, subpixelType != QFontEngineFT::Subpixel_VRGB, false);
+ } else
+#endif
+ {
+ int left = slot->metrics.horiBearingX;
+ int right = slot->metrics.horiBearingX + slot->metrics.width;
+ int top = slot->metrics.horiBearingY;
+ int bottom = slot->metrics.horiBearingY - slot->metrics.height;
+ if(transform && slot->format != FT_GLYPH_FORMAT_BITMAP) {
+ int l, r, t, b;
+ FT_Vector vector;
+ vector.x = left;
+ vector.y = top;
+ FT_Vector_Transform(&vector, &matrix);
+ l = r = vector.x;
+ t = b = vector.y;
+ vector.x = right;
+ vector.y = top;
+ FT_Vector_Transform(&vector, &matrix);
+ if (l > vector.x) l = vector.x;
+ if (r < vector.x) r = vector.x;
+ if (t < vector.y) t = vector.y;
+ if (b > vector.y) b = vector.y;
+ vector.x = right;
+ vector.y = bottom;
+ FT_Vector_Transform(&vector, &matrix);
+ if (l > vector.x) l = vector.x;
+ if (r < vector.x) r = vector.x;
+ if (t < vector.y) t = vector.y;
+ if (b > vector.y) b = vector.y;
+ vector.x = left;
+ vector.y = bottom;
+ FT_Vector_Transform(&vector, &matrix);
+ if (l > vector.x) l = vector.x;
+ if (r < vector.x) r = vector.x;
+ if (t < vector.y) t = vector.y;
+ if (b > vector.y) b = vector.y;
+ left = l;
+ right = r;
+ top = t;
+ bottom = b;
+ }
+ left = FLOOR(left);
+ right = CEIL(right);
+ bottom = FLOOR(bottom);
+ top = CEIL(top);
+
+ int hpixels = TRUNC(right - left);
+ if (hsubpixel)
+ hpixels = hpixels*3 + 8;
+ info.width = hpixels;
+ info.height = TRUNC(top - bottom);
+ info.x = -TRUNC(left);
+ info.y = TRUNC(top);
+ if (hsubpixel) {
+ info.width /= 3;
+ info.x += 1;
+ }
+
+ bool large_glyph = (((short)(slot->linearHoriAdvance>>10) != slot->linearHoriAdvance>>10)
+ || ((uchar)(info.width) != info.width)
+ || ((uchar)(info.height) != info.height)
+ || ((signed char)(info.x) != info.x)
+ || ((signed char)(info.y) != info.y)
+ || ((signed char)(info.xOff) != info.xOff));
+
+ if (large_glyph) {
+ delete [] glyph_buffer;
+ return 0;
+ }
+
+ int pitch = (format == Format_Mono ? ((info.width + 31) & ~31) >> 3 :
+ (format == Format_A8 ? (info.width + 3) & ~3 : info.width * 4));
+ glyph_buffer_size = pitch * info.height;
+ glyph_buffer = new uchar[glyph_buffer_size];
+
+ if (slot->format == FT_GLYPH_FORMAT_OUTLINE) {
+ FT_Bitmap bitmap;
+ bitmap.rows = info.height*vfactor;
+ bitmap.width = hpixels;
+ bitmap.pitch = format == Format_Mono ? (((info.width + 31) & ~31) >> 3) : ((bitmap.width + 3) & ~3);
+ if (!hsubpixel && vfactor == 1)
+ bitmap.buffer = glyph_buffer;
+ else
+ bitmap.buffer = new uchar[bitmap.rows*bitmap.pitch];
+ memset(bitmap.buffer, 0, bitmap.rows*bitmap.pitch);
+ bitmap.pixel_mode = format == Format_Mono ? FT_PIXEL_MODE_MONO : FT_PIXEL_MODE_GRAY;
+ FT_Matrix matrix;
+ matrix.xx = (hsubpixel ? 3 : 1) << 16;
+ matrix.yy = vfactor << 16;
+ matrix.yx = matrix.xy = 0;
+
+ FT_Outline_Transform(&slot->outline, &matrix);
+ FT_Outline_Translate (&slot->outline, (hsubpixel ? -3*left +(4<<6) : -left), -bottom*vfactor);
+ FT_Outline_Get_Bitmap(library, &slot->outline, &bitmap);
+ if (hsubpixel) {
+ Q_ASSERT (bitmap.pixel_mode == FT_PIXEL_MODE_GRAY);
+ Q_ASSERT(antialias);
+ uchar *convoluted = new uchar[bitmap.rows*bitmap.pitch];
+ bool useLegacyLcdFilter = false;
+#if defined(FT_LCD_FILTER_H)
+ useLegacyLcdFilter = (lcdFilterType == FT_LCD_FILTER_LEGACY);
+#endif
+ uchar *buffer = bitmap.buffer;
+ if (!useLegacyLcdFilter) {
+ convoluteBitmap(bitmap.buffer, convoluted, bitmap.width, info.height, bitmap.pitch);
+ buffer = convoluted;
+ }
+ convertRGBToARGB(buffer + 1, (uint *)glyph_buffer, info.width, info.height, bitmap.pitch, subpixelType != QFontEngineFT::Subpixel_RGB, useLegacyLcdFilter);
+ delete [] convoluted;
+ } else if (vfactor != 1) {
+ convertRGBToARGB_V(bitmap.buffer, (uint *)glyph_buffer, info.width, info.height, bitmap.pitch, subpixelType != QFontEngineFT::Subpixel_VRGB, true);
+ }
+
+ if (bitmap.buffer != glyph_buffer)
+ delete [] bitmap.buffer;
+ } else if (slot->format == FT_GLYPH_FORMAT_BITMAP) {
+ Q_ASSERT(slot->bitmap.pixel_mode == FT_PIXEL_MODE_MONO);
+ uchar *src = slot->bitmap.buffer;
+ uchar *dst = glyph_buffer;
+ int h = slot->bitmap.rows;
+ if (format == Format_Mono) {
+ int bytes = ((info.width + 7) & ~7) >> 3;
+ while (h--) {
+ memcpy (dst, src, bytes);
+ dst += pitch;
+ src += slot->bitmap.pitch;
+ }
+ } else {
+ if (hsubpixel) {
+ while (h--) {
+ uint *dd = (uint *)dst;
+ *dd++ = 0;
+ for (int x = 0; x < slot->bitmap.width; x++) {
+ uint a = ((src[x >> 3] & (0x80 >> (x & 7))) ? 0xffffff : 0x000000);
+ *dd++ = a;
+ }
+ *dd++ = 0;
+ dst += pitch;
+ src += slot->bitmap.pitch;
+ }
+ } else if (vfactor != 1) {
+ while (h--) {
+ uint *dd = (uint *)dst;
+ for (int x = 0; x < slot->bitmap.width; x++) {
+ uint a = ((src[x >> 3] & (0x80 >> (x & 7))) ? 0xffffff : 0x000000);
+ *dd++ = a;
+ }
+ dst += pitch;
+ src += slot->bitmap.pitch;
+ }
+ } else {
+ while (h--) {
+ for (int x = 0; x < slot->bitmap.width; x++) {
+ unsigned char a = ((src[x >> 3] & (0x80 >> (x & 7))) ? 0xff : 0x00);
+ dst[x] = a;
+ }
+ dst += pitch;
+ src += slot->bitmap.pitch;
+ }
+ }
+ }
+ } else {
+ qWarning("QFontEngine: Glyph neither outline nor bitmap format=%d", slot->format);
+ delete [] glyph_buffer;
+ return 0;
+ }
+ }
+
+
+ if (!g) {
+ g = new Glyph;
+ g->uploadedToServer = false;
+ g->data = 0;
+ }
+
+ g->linearAdvance = slot->linearHoriAdvance >> 10;
+ g->width = info.width;
+ g->height = info.height;
+ g->x = -info.x;
+ g->y = info.y;
+ g->advance = info.xOff;
+ g->format = format;
+ delete [] g->data;
+ g->data = glyph_buffer;
+
+ if (uploadToServer) {
+ uploadGlyphToServer(set, glyph, g, &info, glyph_buffer_size);
+ }
+
+ set->glyph_data[glyph] = g;
+
+ return g;
+}
+
+bool QFontEngineFT::uploadGlyphToServer(QGlyphSet *set, uint glyphid, Glyph *g, GlyphInfo *info, int glyphDataSize) const
+{
+ Q_UNUSED(set);
+ Q_UNUSED(glyphid);
+ Q_UNUSED(g);
+ Q_UNUSED(info);
+ Q_UNUSED(glyphDataSize);
+ return false;
+}
+
+QFontEngine::FaceId QFontEngineFT::faceId() const
+{
+ return face_id;
+}
+
+QFontEngine::Properties QFontEngineFT::properties() const
+{
+ Properties p = freetype->properties();
+ if (p.postscriptName.isEmpty()) {
+ p.postscriptName = fontDef.family.toUtf8();
+#ifndef QT_NO_PRINTER
+ p.postscriptName = QPdf::stripSpecialCharacters(p.postscriptName);
+#endif
+ }
+
+ return freetype->properties();
+}
+
+QFixed QFontEngineFT::emSquareSize() const
+{
+ if (FT_IS_SCALABLE(freetype->face))
+ return freetype->face->units_per_EM;
+ else
+ return freetype->face->size->metrics.y_ppem;
+}
+
+bool QFontEngineFT::getSfntTableData(uint tag, uchar *buffer, uint *length) const
+{
+ return freetype->getSfntTable(tag, buffer, length);
+}
+
+int QFontEngineFT::synthesized() const
+{
+ int s = 0;
+ if ((fontDef.style != QFont::StyleNormal) && !(freetype->face->style_flags & FT_STYLE_FLAG_ITALIC))
+ s = SynthesizedItalic;
+ if (fontDef.stretch != 100 && FT_IS_SCALABLE(freetype->face))
+ s |= SynthesizedStretch;
+ return s;
+}
+
+QFixed QFontEngineFT::ascent() const
+{
+ return QFixed::fromFixed(metrics.ascender);
+}
+
+QFixed QFontEngineFT::descent() const
+{
+ return QFixed::fromFixed(-metrics.descender);
+}
+
+QFixed QFontEngineFT::leading() const
+{
+ return QFixed::fromFixed(metrics.height - metrics.ascender + metrics.descender);
+}
+
+QFixed QFontEngineFT::xHeight() const
+{
+ TT_OS2 *os2 = (TT_OS2 *)FT_Get_Sfnt_Table(freetype->face, ft_sfnt_os2);
+ if (os2 && os2->sxHeight) {
+ lockFace();
+ QFixed answer = QFixed(os2->sxHeight*freetype->face->size->metrics.y_ppem)/freetype->face->units_per_EM;
+ unlockFace();
+ return answer;
+ }
+ return QFontEngine::xHeight();
+}
+
+QFixed QFontEngineFT::averageCharWidth() const
+{
+ TT_OS2 *os2 = (TT_OS2 *)FT_Get_Sfnt_Table(freetype->face, ft_sfnt_os2);
+ if (os2 && os2->xAvgCharWidth) {
+ lockFace();
+ QFixed answer = QFixed(os2->xAvgCharWidth*freetype->face->size->metrics.x_ppem)/freetype->face->units_per_EM;
+ unlockFace();
+ return answer;
+ }
+ return QFontEngine::averageCharWidth();
+}
+
+qreal QFontEngineFT::maxCharWidth() const
+{
+ return metrics.max_advance >> 6;
+}
+
+static const ushort char_table[] = {
+ 40,
+ 67,
+ 70,
+ 75,
+ 86,
+ 88,
+ 89,
+ 91,
+ 102,
+ 114,
+ 124,
+ 127,
+ 205,
+ 645,
+ 884,
+ 922,
+ 1070,
+ 12386
+};
+
+static const int char_table_entries = sizeof(char_table)/sizeof(ushort);
+
+
+qreal QFontEngineFT::minLeftBearing() const
+{
+ if (lbearing == SHRT_MIN)
+ (void) minRightBearing(); // calculates both
+ return lbearing.toReal();
+}
+
+qreal QFontEngineFT::minRightBearing() const
+{
+ if (rbearing == SHRT_MIN) {
+ lbearing = rbearing = 0;
+ const QChar *ch = (const QChar *)(const void*)char_table;
+ QGlyphLayoutArray<char_table_entries> glyphs;
+ int ng = char_table_entries;
+ stringToCMap(ch, char_table_entries, &glyphs, &ng, QTextEngine::GlyphIndicesOnly);
+ while (--ng) {
+ if (glyphs.glyphs[ng]) {
+ glyph_metrics_t gi = const_cast<QFontEngineFT *>(this)->boundingBox(glyphs.glyphs[ng]);
+ lbearing = qMin(lbearing, gi.x);
+ rbearing = qMin(rbearing, (gi.xoff - gi.x - gi.width));
+ }
+ }
+ }
+ return rbearing.toReal();
+}
+
+QFixed QFontEngineFT::lineThickness() const
+{
+ return line_thickness;
+}
+
+QFixed QFontEngineFT::underlinePosition() const
+{
+ return underline_position;
+}
+
+void QFontEngineFT::doKerning(QGlyphLayout *g, QTextEngine::ShaperFlags flags) const
+{
+ if (!kerning_pairs_loaded) {
+ kerning_pairs_loaded = true;
+ lockFace();
+ if (freetype->face->size->metrics.x_ppem != 0) {
+ QFixed scalingFactor(freetype->face->units_per_EM/freetype->face->size->metrics.x_ppem);
+ unlockFace();
+ const_cast<QFontEngineFT *>(this)->loadKerningPairs(scalingFactor);
+ } else {
+ unlockFace();
+ }
+ }
+ QFontEngine::doKerning(g, flags);
+}
+
+QFontEngineFT::QGlyphSet *QFontEngineFT::loadTransformedGlyphSet(const QTransform &matrix)
+{
+ if (matrix.type() > QTransform::TxShear)
+ return 0;
+
+ // FT_Set_Transform only supports scalable fonts
+ if (!FT_IS_SCALABLE(freetype->face))
+ return 0;
+
+ FT_Matrix m;
+ m.xx = FT_Fixed(matrix.m11() * 65536);
+ m.xy = FT_Fixed(-matrix.m21() * 65536);
+ m.yx = FT_Fixed(-matrix.m12() * 65536);
+ m.yy = FT_Fixed(matrix.m22() * 65536);
+
+ QGlyphSet *gs = 0;
+
+ for (int i = 0; i < transformedGlyphSets.count(); ++i) {
+ const QGlyphSet &g = transformedGlyphSets.at(i);
+ if (g.transformationMatrix.xx == m.xx
+ && g.transformationMatrix.xy == m.xy
+ && g.transformationMatrix.yx == m.yx
+ && g.transformationMatrix.yy == m.yy) {
+
+ // found a match, move it to the front
+ transformedGlyphSets.move(i, 0);
+ gs = &transformedGlyphSets[0];
+ break;
+ }
+ }
+
+ if (!gs) {
+ // don't try to load huge fonts
+ bool draw_as_outline = fontDef.pixelSize * qSqrt(matrix.det()) >= 64;
+ if (draw_as_outline)
+ return 0;
+
+ // don't cache more than 10 transformations
+ if (transformedGlyphSets.count() >= 10) {
+ transformedGlyphSets.move(transformedGlyphSets.size() - 1, 0);
+ freeServerGlyphSet(transformedGlyphSets.at(0).id);
+ } else {
+ transformedGlyphSets.prepend(QGlyphSet());
+ }
+ gs = &transformedGlyphSets[0];
+
+ qDeleteAll(gs->glyph_data);
+ gs->glyph_data.clear();
+
+ gs->id = allocateServerGlyphSet();
+
+ gs->transformationMatrix = m;
+ gs->outline_drawing = draw_as_outline;
+ }
+
+ return gs;
+}
+
+bool QFontEngineFT::loadGlyphs(QGlyphSet *gs, glyph_t *glyphs, int num_glyphs, GlyphFormat format)
+{
+ FT_Face face = 0;
+
+ for (int i = 0; i < num_glyphs; ++i) {
+ if (!gs->glyph_data.contains(glyphs[i])
+ || gs->glyph_data.value(glyphs[i])->format != format) {
+ if (!face) {
+ face = lockFace();
+ FT_Matrix m = matrix;
+ FT_Matrix_Multiply(&gs->transformationMatrix, &m);
+ FT_Set_Transform(face, &m, 0);
+ freetype->matrix = m;
+ }
+ if (!loadGlyph(gs, glyphs[i], format)) {
+ unlockFace();
+ return false;
+ }
+ }
+ }
+
+ if (face)
+ unlockFace();
+
+ return true;
+}
+
+void QFontEngineFT::getUnscaledGlyph(glyph_t glyph, QPainterPath *path, glyph_metrics_t *metrics)
+{
+ FT_Face face = lockFace(Unscaled);
+ FT_Set_Transform(face, 0, 0);
+ FT_Load_Glyph(face, glyph, FT_LOAD_NO_BITMAP);
+
+ int left = face->glyph->metrics.horiBearingX;
+ int right = face->glyph->metrics.horiBearingX + face->glyph->metrics.width;
+ int top = face->glyph->metrics.horiBearingY;
+ int bottom = face->glyph->metrics.horiBearingY - face->glyph->metrics.height;
+
+ QFixedPoint p;
+ p.x = 0;
+ p.y = 0;
+
+ metrics->width = QFixed::fromFixed(right-left);
+ metrics->height = QFixed::fromFixed(top-bottom);
+ metrics->x = QFixed::fromFixed(left);
+ metrics->y = QFixed::fromFixed(-top);
+ metrics->xoff = QFixed::fromFixed(face->glyph->advance.x);
+
+ if (!FT_IS_SCALABLE(freetype->face))
+ QFreetypeFace::addBitmapToPath(face->glyph, p, path);
+ else
+ QFreetypeFace::addGlyphToPath(face, face->glyph, p, path, face->units_per_EM << 6, face->units_per_EM << 6);
+
+ FT_Set_Transform(face, &freetype->matrix, 0);
+ unlockFace();
+}
+
+static inline unsigned int getChar(const QChar *str, int &i, const int len)
+{
+ unsigned int uc = str[i].unicode();
+ if (uc >= 0xd800 && uc < 0xdc00 && i < len-1) {
+ uint low = str[i+1].unicode();
+ if (low >= 0xdc00 && low < 0xe000) {
+ uc = (uc - 0xd800)*0x400 + (low - 0xdc00) + 0x10000;
+ ++i;
+ }
+ }
+ return uc;
+}
+
+bool QFontEngineFT::canRender(const QChar *string, int len)
+{
+ FT_Face face = freetype->face;
+#if 0
+ if (_cmap != -1) {
+ lockFace();
+ for ( int i = 0; i < len; i++ ) {
+ unsigned int uc = getChar(string, i, len);
+ if (!FcCharSetHasChar (_font->charset, uc) && getAdobeCharIndex(face, _cmap, uc) == 0) {
+ allExist = false;
+ break;
+ }
+ }
+ unlockFace();
+ } else
+#endif
+ {
+ for ( int i = 0; i < len; i++ ) {
+ unsigned int uc = getChar(string, i, len);
+ if (!FT_Get_Char_Index(face, uc))
+ return false;
+ }
+ }
+ return true;
+}
+
+void QFontEngineFT::addOutlineToPath(qreal x, qreal y, const QGlyphLayout &glyphs, QPainterPath *path, QTextItem::RenderFlags flags)
+{
+ if (!glyphs.numGlyphs)
+ return;
+
+ if (FT_IS_SCALABLE(freetype->face)) {
+ QFontEngine::addOutlineToPath(x, y, glyphs, path, flags);
+ } else {
+ QVarLengthArray<QFixedPoint> positions;
+ QVarLengthArray<glyph_t> positioned_glyphs;
+ QTransform matrix;
+ matrix.translate(x, y);
+ getGlyphPositions(glyphs, matrix, flags, positioned_glyphs, positions);
+
+ FT_Face face = lockFace(Unscaled);
+ for (int gl = 0; gl < glyphs.numGlyphs; gl++) {
+ FT_UInt glyph = positioned_glyphs[gl];
+ FT_Load_Glyph(face, glyph, FT_LOAD_TARGET_MONO);
+ freetype->addBitmapToPath(face->glyph, positions[gl], path);
+ }
+ unlockFace();
+ }
+}
+
+void QFontEngineFT::addGlyphsToPath(glyph_t *glyphs, QFixedPoint *positions, int numGlyphs,
+ QPainterPath *path, QTextItem::RenderFlags)
+{
+ FT_Face face = lockFace(Unscaled);
+
+ for (int gl = 0; gl < numGlyphs; gl++) {
+ FT_UInt glyph = glyphs[gl];
+
+ FT_Load_Glyph(face, glyph, FT_LOAD_NO_BITMAP);
+
+ FT_GlyphSlot g = face->glyph;
+ if (g->format != FT_GLYPH_FORMAT_OUTLINE)
+ continue;
+ QFreetypeFace::addGlyphToPath(face, g, positions[gl], path, xsize, ysize);
+ }
+ unlockFace();
+}
+
+bool QFontEngineFT::stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs, int *nglyphs,
+ QTextEngine::ShaperFlags flags) const
+{
+ if (*nglyphs < len) {
+ *nglyphs = len;
+ return false;
+ }
+
+ bool mirrored = flags & QTextEngine::RightToLeft;
+ int glyph_pos = 0;
+ if (freetype->symbol_map) {
+ FT_Face face = freetype->face;
+ for ( int i = 0; i < len; ++i ) {
+ unsigned int uc = getChar(str, i, len);
+ if (mirrored)
+ uc = QChar::mirroredChar(uc);
+ glyphs->glyphs[glyph_pos] = uc < QFreetypeFace::cmapCacheSize ? freetype->cmapCache[uc] : 0;
+ if ( !glyphs->glyphs[glyph_pos] ) {
+ glyph_t glyph;
+#if !defined(QT_NO_FONTCONFIG)
+ if (FcCharSetHasChar(freetype->charset, uc)) {
+#else
+ if (false) {
+#endif
+ redo0:
+ glyph = FT_Get_Char_Index(face, uc);
+ if (!glyph && (uc == 0xa0 || uc == 0x9)) {
+ uc = 0x20;
+ goto redo0;
+ }
+ } else {
+ FT_Set_Charmap(face, freetype->symbol_map);
+ glyph = FT_Get_Char_Index(face, uc);
+ FT_Set_Charmap(face, freetype->unicode_map);
+ }
+ glyphs->glyphs[glyph_pos] = glyph;
+ if (uc < QFreetypeFace::cmapCacheSize)
+ freetype->cmapCache[uc] = glyph;
+ }
+ ++glyph_pos;
+ }
+ } else {
+ FT_Face face = freetype->face;
+ for (int i = 0; i < len; ++i) {
+ unsigned int uc = getChar(str, i, len);
+ if (mirrored)
+ uc = QChar::mirroredChar(uc);
+ glyphs->glyphs[glyph_pos] = uc < QFreetypeFace::cmapCacheSize ? freetype->cmapCache[uc] : 0;
+ if (!glyphs->glyphs[glyph_pos]
+#if !defined(QT_NO_FONTCONFIG)
+ && FcCharSetHasChar(freetype->charset, uc)
+#endif
+ ) {
+ redo:
+ glyph_t glyph = FT_Get_Char_Index(face, uc);
+ if (!glyph && (uc == 0xa0 || uc == 0x9)) {
+ uc = 0x20;
+ goto redo;
+ }
+ glyphs->glyphs[glyph_pos] = glyph;
+ if (uc < QFreetypeFace::cmapCacheSize)
+ freetype->cmapCache[uc] = glyph;
+ }
+ ++glyph_pos;
+ }
+ }
+
+ *nglyphs = glyph_pos;
+ glyphs->numGlyphs = glyph_pos;
+
+ if (flags & QTextEngine::GlyphIndicesOnly)
+ return true;
+
+ recalcAdvances(glyphs, flags);
+
+ return true;
+}
+
+void QFontEngineFT::recalcAdvances(QGlyphLayout *glyphs, QTextEngine::ShaperFlags flags) const
+{
+ FT_Face face = 0;
+ if (flags & QTextEngine::DesignMetrics) {
+ for (int i = 0; i < glyphs->numGlyphs; i++) {
+ Glyph *g = defaultGlyphSet.glyph_data.value(glyphs->glyphs[i]);
+ if (g) {
+ glyphs->advances_x[i] = QFixed::fromFixed(g->linearAdvance);
+ } else {
+ if (!face)
+ face = lockFace();
+ g = loadGlyph(glyphs->glyphs[i], Format_None, true);
+ glyphs->advances_x[i] = QFixed::fromFixed(face->glyph->linearHoriAdvance >> 10);
+ }
+ glyphs->advances_y[i] = 0;
+ }
+ } else {
+ for (int i = 0; i < glyphs->numGlyphs; i++) {
+ Glyph *g = defaultGlyphSet.glyph_data.value(glyphs->glyphs[i]);
+ if (g) {
+ glyphs->advances_x[i] = QFixed(g->advance);
+ } else {
+ if (!face)
+ face = lockFace();
+ g = loadGlyph(glyphs->glyphs[i], Format_None, true);
+ glyphs->advances_x[i] = QFixed::fromFixed(face->glyph->metrics.horiAdvance).round();
+ }
+ glyphs->advances_y[i] = 0;
+ }
+ }
+ if (face)
+ unlockFace();
+}
+
+glyph_metrics_t QFontEngineFT::boundingBox(const QGlyphLayout &glyphs)
+{
+
+ FT_Face face = 0;
+
+ glyph_metrics_t overall;
+ // initialize with line height, we get the same behaviour on all platforms
+ overall.y = -ascent();
+ overall.height = ascent() + descent() + 1;
+
+ QFixed ymax = 0;
+ QFixed xmax = 0;
+ for (int i = 0; i < glyphs.numGlyphs; i++) {
+ Glyph *g = defaultGlyphSet.glyph_data.value(glyphs.glyphs[i]);
+ if (!g) {
+ if (!face)
+ face = lockFace();
+ g = loadGlyph(glyphs.glyphs[i], Format_None, true);
+ }
+ if (g) {
+ QFixed x = overall.xoff + glyphs.offsets[i].x + g->x;
+ QFixed y = overall.yoff + glyphs.offsets[i].y - g->y;
+ overall.x = qMin(overall.x, x);
+ overall.y = qMin(overall.y, y);
+ xmax = qMax(xmax, x + g->width);
+ ymax = qMax(ymax, y + g->height);
+ overall.xoff += qRound(g->advance);
+ } else {
+ int left = FLOOR(face->glyph->metrics.horiBearingX);
+ int right = CEIL(face->glyph->metrics.horiBearingX + face->glyph->metrics.width);
+ int top = CEIL(face->glyph->metrics.horiBearingY);
+ int bottom = FLOOR(face->glyph->metrics.horiBearingY - face->glyph->metrics.height);
+
+ QFixed x = overall.xoff + glyphs.offsets[i].x - (-TRUNC(left));
+ QFixed y = overall.yoff + glyphs.offsets[i].y - TRUNC(top);
+ overall.x = qMin(overall.x, x);
+ overall.y = qMin(overall.y, y);
+ xmax = qMax(xmax, x + TRUNC(right - left));
+ ymax = qMax(ymax, y + TRUNC(top - bottom));
+ overall.xoff += qRound(TRUNC(ROUND(face->glyph->advance.x)));
+ }
+ }
+ overall.height = qMax(overall.height, ymax - overall.y);
+ overall.width = xmax - overall.x;
+
+ if (face)
+ unlockFace();
+
+ return overall;
+}
+
+glyph_metrics_t QFontEngineFT::boundingBox(glyph_t glyph)
+{
+ FT_Face face = 0;
+ glyph_metrics_t overall;
+ Glyph *g = defaultGlyphSet.glyph_data.value(glyph);
+ if (!g) {
+ face = lockFace();
+ g = loadGlyph(glyph, Format_None, true);
+ }
+ if (g) {
+ overall.x = g->x;
+ overall.y = -g->y;
+ overall.width = g->width;
+ overall.height = g->height;
+ overall.xoff = g->advance;
+ } else {
+ int left = FLOOR(face->glyph->metrics.horiBearingX);
+ int right = CEIL(face->glyph->metrics.horiBearingX + face->glyph->metrics.width);
+ int top = CEIL(face->glyph->metrics.horiBearingY);
+ int bottom = FLOOR(face->glyph->metrics.horiBearingY - face->glyph->metrics.height);
+
+ overall.width = TRUNC(right-left);
+ overall.height = TRUNC(top-bottom);
+ overall.x = TRUNC(left);
+ overall.y = -TRUNC(top);
+ overall.xoff = TRUNC(ROUND(face->glyph->advance.x));
+ }
+ if (face)
+ unlockFace();
+ return overall;
+}
+
+glyph_metrics_t QFontEngineFT::boundingBox(glyph_t glyph, const QTransform &matrix)
+{
+ FT_Face face = 0;
+ glyph_metrics_t overall;
+ QGlyphSet *glyphSet = 0;
+ if (matrix.type() > QTransform::TxTranslate && FT_IS_SCALABLE(freetype->face)) {
+ // TODO move everything here to a method of its own to access glyphSets
+ // to be shared with a new method that will replace loadTransformedGlyphSet()
+ FT_Matrix m;
+ m.xx = FT_Fixed(matrix.m11() * 65536);
+ m.xy = FT_Fixed(-matrix.m21() * 65536);
+ m.yx = FT_Fixed(-matrix.m12() * 65536);
+ m.yy = FT_Fixed(matrix.m22() * 65536);
+ for (int i = 0; i < transformedGlyphSets.count(); ++i) {
+ const QGlyphSet &g = transformedGlyphSets.at(i);
+ if (g.transformationMatrix.xx == m.xx
+ && g.transformationMatrix.xy == m.xy
+ && g.transformationMatrix.yx == m.yx
+ && g.transformationMatrix.yy == m.yy) {
+
+ // found a match, move it to the front
+ transformedGlyphSets.move(i, 0);
+ glyphSet = &transformedGlyphSets[0];
+ break;
+ }
+ }
+
+ if (!glyphSet) {
+ // don't cache more than 10 transformations
+ if (transformedGlyphSets.count() >= 10) {
+ transformedGlyphSets.move(transformedGlyphSets.size() - 1, 0);
+ freeServerGlyphSet(transformedGlyphSets.at(0).id);
+ } else {
+ transformedGlyphSets.prepend(QGlyphSet());
+ }
+ glyphSet = &transformedGlyphSets[0];
+ qDeleteAll(glyphSet->glyph_data);
+ glyphSet->glyph_data.clear();
+ glyphSet->id = allocateServerGlyphSet();
+ glyphSet->transformationMatrix = m;
+ }
+ Q_ASSERT(glyphSet);
+ } else {
+ glyphSet = &defaultGlyphSet;
+ }
+ Glyph * g = glyphSet->glyph_data.value(glyph);
+ if (!g) {
+ face = lockFace();
+ g = loadGlyphMetrics(glyphSet, glyph);
+ }
+
+ if (g) {
+ overall.x = g->x;
+ overall.y = -g->y;
+ overall.width = g->width;
+ overall.height = g->height;
+ overall.xoff = g->advance;
+ } else {
+ int left = FLOOR(face->glyph->metrics.horiBearingX);
+ int right = CEIL(face->glyph->metrics.horiBearingX + face->glyph->metrics.width);
+ int top = CEIL(face->glyph->metrics.horiBearingY);
+ int bottom = FLOOR(face->glyph->metrics.horiBearingY - face->glyph->metrics.height);
+
+ overall.width = TRUNC(right-left);
+ overall.height = TRUNC(top-bottom);
+ overall.x = TRUNC(left);
+ overall.y = -TRUNC(top);
+ overall.xoff = TRUNC(ROUND(face->glyph->advance.x));
+ }
+ if (face)
+ unlockFace();
+ return overall;
+}
+
+QImage QFontEngineFT::alphaMapForGlyph(glyph_t g)
+{
+ lockFace();
+
+ GlyphFormat glyph_format = antialias ? Format_A8 : Format_Mono;
+
+ Glyph *glyph = loadGlyph(g, glyph_format);
+ if (!glyph)
+ return QImage();
+
+ const int pitch = antialias ? (glyph->width + 3) & ~3 : ((glyph->width + 31)/32) * 4;
+
+ QImage img(glyph->width, glyph->height, antialias ? QImage::Format_Indexed8 : QImage::Format_Mono);
+ if (antialias) {
+ QVector<QRgb> colors(256);
+ for (int i=0; i<256; ++i)
+ colors[i] = qRgba(0, 0, 0, i);
+ img.setColorTable(colors);
+ } else {
+ QVector<QRgb> colors(2);
+ colors[0] = qRgba(0, 0, 0, 0);
+ colors[1] = qRgba(0, 0, 0, 255);
+ img.setColorTable(colors);
+ }
+ Q_ASSERT(img.bytesPerLine() == pitch);
+ if (glyph->width) {
+ for (int y = 0; y < glyph->height; ++y)
+ memcpy(img.scanLine(y), &glyph->data[y * pitch], pitch);
+ }
+ unlockFace();
+
+ return img;
+}
+
+void QFontEngineFT::removeGlyphFromCache(glyph_t glyph)
+{
+ delete defaultGlyphSet.glyph_data.take(glyph);
+}
+
+int QFontEngineFT::glyphCount() const
+{
+ int count = 0;
+ FT_Face face = lockFace();
+ if (face) {
+ count = face->num_glyphs;
+ unlockFace();
+ }
+ return count;
+}
+
+FT_Face QFontEngineFT::lockFace(Scaling scale) const
+{
+ freetype->lock();
+ FT_Face face = freetype->face;
+ if (scale == Unscaled) {
+ FT_Set_Char_Size(face, face->units_per_EM << 6, face->units_per_EM << 6, 0, 0);
+ freetype->xsize = face->units_per_EM << 6;
+ freetype->ysize = face->units_per_EM << 6;
+ } else if (freetype->xsize != xsize || freetype->ysize != ysize) {
+ FT_Set_Char_Size(face, xsize, ysize, 0, 0);
+ freetype->xsize = xsize;
+ freetype->ysize = ysize;
+ }
+ if (freetype->matrix.xx != matrix.xx ||
+ freetype->matrix.yy != matrix.yy ||
+ freetype->matrix.xy != matrix.xy ||
+ freetype->matrix.yx != matrix.yx) {
+ freetype->matrix = matrix;
+ FT_Set_Transform(face, &freetype->matrix, 0);
+ }
+
+ return face;
+}
+
+void QFontEngineFT::unlockFace() const
+{
+ freetype->unlock();
+}
+
+FT_Face QFontEngineFT::non_locked_face() const
+{
+ return freetype->face;
+}
+
+
+QFontEngineFT::QGlyphSet::QGlyphSet()
+ : id(0), outline_drawing(false)
+{
+ transformationMatrix.xx = 0x10000;
+ transformationMatrix.yy = 0x10000;
+ transformationMatrix.xy = 0;
+ transformationMatrix.yx = 0;
+}
+
+QFontEngineFT::QGlyphSet::~QGlyphSet()
+{
+ qDeleteAll(glyph_data);
+}
+
+unsigned long QFontEngineFT::allocateServerGlyphSet()
+{
+ return 0;
+}
+
+void QFontEngineFT::freeServerGlyphSet(unsigned long id)
+{
+ Q_UNUSED(id);
+}
+
+HB_Error QFontEngineFT::getPointInOutline(HB_Glyph glyph, int flags, hb_uint32 point, HB_Fixed *xpos, HB_Fixed *ypos, hb_uint32 *nPoints)
+{
+ lockFace();
+ HB_Error result = freetype->getPointInOutline(glyph, flags, point, xpos, ypos, nPoints);
+ unlockFace();
+ return result;
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_FREETYPE
diff --git a/src/gui/text/qfontengine_ft_p.h b/src/gui/text/qfontengine_ft_p.h
new file mode 100644
index 0000000000..284904bee1
--- /dev/null
+++ b/src/gui/text/qfontengine_ft_p.h
@@ -0,0 +1,323 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#ifndef QFONTENGINE_FT_P_H
+#define QFONTENGINE_FT_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 "qfontengine_p.h"
+
+#ifndef QT_NO_FREETYPE
+
+#include <ft2build.h>
+#include FT_FREETYPE_H
+
+#if defined(Q_WS_X11)
+#include <private/qt_x11_p.h>
+#endif
+
+#include <unistd.h>
+
+#ifndef QT_NO_FONTCONFIG
+#include <fontconfig/fontconfig.h>
+#endif
+
+#include <qmutex.h>
+
+#include <harfbuzz-shaper.h>
+
+QT_BEGIN_NAMESPACE
+
+/*
+ * This struct represents one font file on disk (like Arial.ttf) and is shared between all the font engines
+ * that show this font file (at different pixel sizes).
+ */
+struct QFreetypeFace
+{
+ void computeSize(const QFontDef &fontDef, int *xsize, int *ysize, bool *outline_drawing);
+ QFontEngine::Properties properties() const;
+ bool getSfntTable(uint tag, uchar *buffer, uint *length) const;
+
+ static QFreetypeFace *getFace(const QFontEngine::FaceId &face_id);
+ void release(const QFontEngine::FaceId &face_id);
+
+ // locks the struct for usage. Any read/write operations require locking.
+ void lock()
+ {
+ _lock.lock();
+ }
+ void unlock()
+ {
+ _lock.unlock();
+ }
+
+ FT_Face face;
+ HB_Face hbFace;
+#ifndef QT_NO_FONTCONFIG
+ FcCharSet *charset;
+#endif
+ int xsize; // 26.6
+ int ysize; // 26.6
+ FT_Matrix matrix;
+ FT_CharMap unicode_map;
+ FT_CharMap symbol_map;
+
+ enum { cmapCacheSize = 0x200 };
+ glyph_t cmapCache[cmapCacheSize];
+
+ int fsType() const;
+
+ HB_Error getPointInOutline(HB_Glyph glyph, int flags, hb_uint32 point, HB_Fixed *xpos, HB_Fixed *ypos, hb_uint32 *nPoints);
+
+ static void addGlyphToPath(FT_Face face, FT_GlyphSlot g, const QFixedPoint &point, QPainterPath *path, FT_Fixed x_scale, FT_Fixed y_scale);
+ static void addBitmapToPath(FT_GlyphSlot slot, const QFixedPoint &point, QPainterPath *path, bool = false);
+
+private:
+ QFreetypeFace() : _lock(QMutex::Recursive) {}
+ ~QFreetypeFace() {}
+ QAtomicInt ref;
+ QMutex _lock;
+ QByteArray fontData;
+};
+
+class Q_GUI_EXPORT QFontEngineFT : public QFontEngine
+{
+public:
+ enum GlyphFormat {
+ Format_None,
+ Format_Render = Format_None,
+ Format_Mono,
+ Format_A8,
+ Format_A32
+ };
+
+ /* we don't cache glyphs that are too large anyway, so we can make this struct rather small */
+ struct Glyph {
+ ~Glyph();
+ short linearAdvance;
+ unsigned char width;
+ unsigned char height;
+ signed char x;
+ signed char y;
+ signed char advance;
+ signed char format;
+ uchar *data;
+ unsigned int uploadedToServer : 1;
+ };
+
+ enum SubpixelAntialiasingType {
+ Subpixel_None,
+ Subpixel_RGB,
+ Subpixel_BGR,
+ Subpixel_VRGB,
+ Subpixel_VBGR
+ };
+
+#if defined(Q_WS_X11) && !defined(QT_NO_XRENDER)
+ typedef XGlyphInfo GlyphInfo;
+#else
+ struct GlyphInfo {
+ unsigned short width;
+ unsigned short height;
+ short x;
+ short y;
+ short xOff;
+ short yOff;
+ };
+#endif
+
+ struct QGlyphSet
+ {
+ QGlyphSet();
+ ~QGlyphSet();
+ FT_Matrix transformationMatrix;
+ unsigned long id; // server sided id, GlyphSet for X11
+ bool outline_drawing;
+ mutable QHash<int, Glyph *> glyph_data; // maps from glyph index to glyph data
+ };
+
+ virtual QFontEngine::FaceId faceId() const;
+ virtual QFontEngine::Properties properties() const;
+ virtual QFixed emSquareSize() const;
+
+ virtual bool getSfntTableData(uint tag, uchar *buffer, uint *length) const;
+ virtual int synthesized() const;
+
+ virtual QFixed ascent() const;
+ virtual QFixed descent() const;
+ virtual QFixed leading() const;
+ virtual QFixed xHeight() const;
+ virtual QFixed averageCharWidth() const;
+
+ virtual qreal maxCharWidth() const;
+ virtual qreal minLeftBearing() const;
+ virtual qreal minRightBearing() const;
+ virtual QFixed lineThickness() const;
+ virtual QFixed underlinePosition() const;
+
+ void doKerning(QGlyphLayout *, QTextEngine::ShaperFlags) const;
+
+ inline virtual Type type() const
+ { return QFontEngine::Freetype; }
+ inline virtual const char *name() const
+ { return "freetype"; }
+
+ virtual void getUnscaledGlyph(glyph_t glyph, QPainterPath *path, glyph_metrics_t *metrics);
+
+ virtual bool canRender(const QChar *string, int len);
+
+ virtual void addGlyphsToPath(glyph_t *glyphs, QFixedPoint *positions, int nglyphs,
+ QPainterPath *path, QTextItem::RenderFlags flags);
+ virtual void addOutlineToPath(qreal x, qreal y, const QGlyphLayout &glyphs,
+ QPainterPath *path, QTextItem::RenderFlags flags);
+
+ virtual bool stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs, int *nglyphs,
+ QTextEngine::ShaperFlags flags) const;
+
+ virtual glyph_metrics_t boundingBox(const QGlyphLayout &glyphs);
+ virtual glyph_metrics_t boundingBox(glyph_t glyph);
+ virtual glyph_metrics_t boundingBox(glyph_t glyph, const QTransform &matrix);
+
+ virtual void recalcAdvances(QGlyphLayout *glyphs, QTextEngine::ShaperFlags flags) const;
+ virtual QImage alphaMapForGlyph(glyph_t);
+ virtual void removeGlyphFromCache(glyph_t glyph);
+
+ virtual int glyphCount() const;
+
+ enum Scaling {
+ Scaled,
+ Unscaled
+ };
+ FT_Face lockFace(Scaling scale = Scaled) const;
+ void unlockFace() const;
+
+ FT_Face non_locked_face() const;
+
+ inline bool drawAntialiased() const { return antialias; }
+ inline bool invalid() const { return xsize == 0 && ysize == 0; }
+ inline bool isBitmapFont() const { return defaultFormat == Format_Mono; }
+
+ inline Glyph *loadGlyph(uint glyph, GlyphFormat format = Format_None, bool fetchMetricsOnly = false) const
+ { return loadGlyph(&defaultGlyphSet, glyph, format, fetchMetricsOnly); }
+ Glyph *loadGlyph(QGlyphSet *set, uint glyph, GlyphFormat = Format_None, bool fetchMetricsOnly = false) const;
+
+ QGlyphSet *defaultGlyphs() { return &defaultGlyphSet; }
+ GlyphFormat defaultGlyphFormat() const { return defaultFormat; }
+
+ inline Glyph *cachedGlyph(glyph_t g) const { return defaultGlyphSet.glyph_data.value(g); }
+
+ QGlyphSet *loadTransformedGlyphSet(const QTransform &matrix);
+ bool loadGlyphs(QGlyphSet *gs, glyph_t *glyphs, int num_glyphs, GlyphFormat format = Format_Render);
+
+#if defined(Q_WS_QWS)
+ virtual void draw(QPaintEngine * /*p*/, qreal /*x*/, qreal /*y*/, const QTextItemInt & /*si*/) {}
+#endif
+
+ QFontEngineFT(const QFontDef &fd);
+ virtual ~QFontEngineFT();
+
+ bool init(FaceId faceId, bool antiaalias, GlyphFormat defaultFormat = Format_None);
+
+ virtual HB_Error getPointInOutline(HB_Glyph glyph, int flags, hb_uint32 point, HB_Fixed *xpos, HB_Fixed *ypos, hb_uint32 *nPoints);
+
+protected:
+
+ void freeGlyphSets();
+
+ virtual bool uploadGlyphToServer(QGlyphSet *set, uint glyphid, Glyph *g, GlyphInfo *info, int glyphDataSize) const;
+ virtual unsigned long allocateServerGlyphSet();
+ virtual void freeServerGlyphSet(unsigned long id);
+
+ QFreetypeFace *freetype;
+ int default_load_flags;
+
+ enum HintStyle {
+ HintNone,
+ HintLight,
+ HintMedium,
+ HintFull
+ };
+
+ HintStyle default_hint_style;
+
+ bool antialias;
+ bool transform;
+ SubpixelAntialiasingType subpixelType;
+ int lcdFilterType;
+ bool canUploadGlyphsToServer;
+ bool embeddedbitmap;
+
+private:
+ QFontEngineFT::Glyph *loadGlyphMetrics(QGlyphSet *set, uint glyph) const;
+
+ GlyphFormat defaultFormat;
+ FT_Matrix matrix;
+
+ QList<QGlyphSet> transformedGlyphSets;
+ mutable QGlyphSet defaultGlyphSet;
+
+ QFontEngine::FaceId face_id;
+
+ int xsize;
+ int ysize;
+
+ mutable QFixed lbearing;
+ mutable QFixed rbearing;
+ QFixed line_thickness;
+ QFixed underline_position;
+
+ FT_Size_Metrics metrics;
+ mutable bool kerning_pairs_loaded;
+};
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_FREETYPE
+
+#endif // QFONTENGINE_FT_P_H
diff --git a/src/gui/text/qfontengine_mac.mm b/src/gui/text/qfontengine_mac.mm
new file mode 100644
index 0000000000..40d145a13c
--- /dev/null
+++ b/src/gui/text/qfontengine_mac.mm
@@ -0,0 +1,1701 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <private/qapplication_p.h>
+#include <private/qfontengine_p.h>
+#include <private/qpainter_p.h>
+#include <private/qtextengine_p.h>
+#include <qbitmap.h>
+#include <private/qpaintengine_mac_p.h>
+#include <private/qprintengine_mac_p.h>
+#include <private/qpdf_p.h>
+#include <qglobal.h>
+#include <qpixmap.h>
+#include <qpixmapcache.h>
+#include <qvarlengtharray.h>
+#include <qdebug.h>
+#include <qendian.h>
+
+#include <ApplicationServices/ApplicationServices.h>
+#include <AppKit/AppKit.h>
+
+QT_BEGIN_NAMESPACE
+
+/*****************************************************************************
+ QFontEngine debug facilities
+ *****************************************************************************/
+//#define DEBUG_ADVANCES
+
+extern int qt_antialiasing_threshold; // QApplication.cpp
+
+#ifndef FixedToQFixed
+#define FixedToQFixed(a) QFixed::fromFixed((a) >> 10)
+#define QFixedToFixed(x) ((x).value() << 10)
+#endif
+
+class QMacFontPath
+{
+ float x, y;
+ QPainterPath *path;
+public:
+ inline QMacFontPath(float _x, float _y, QPainterPath *_path) : x(_x), y(_y), path(_path) { }
+ inline void setPosition(float _x, float _y) { x = _x; y = _y; }
+ inline void advance(float _x) { x += _x; }
+ static OSStatus lineTo(const Float32Point *, void *);
+ static OSStatus cubicTo(const Float32Point *, const Float32Point *,
+ const Float32Point *, void *);
+ static OSStatus moveTo(const Float32Point *, void *);
+ static OSStatus closePath(void *);
+};
+
+OSStatus QMacFontPath::lineTo(const Float32Point *pt, void *data)
+
+{
+ QMacFontPath *p = static_cast<QMacFontPath*>(data);
+ p->path->lineTo(p->x + pt->x, p->y + pt->y);
+ return noErr;
+}
+
+OSStatus QMacFontPath::cubicTo(const Float32Point *cp1, const Float32Point *cp2,
+ const Float32Point *ep, void *data)
+
+{
+ QMacFontPath *p = static_cast<QMacFontPath*>(data);
+ p->path->cubicTo(p->x + cp1->x, p->y + cp1->y,
+ p->x + cp2->x, p->y + cp2->y,
+ p->x + ep->x, p->y + ep->y);
+ return noErr;
+}
+
+OSStatus QMacFontPath::moveTo(const Float32Point *pt, void *data)
+{
+ QMacFontPath *p = static_cast<QMacFontPath*>(data);
+ p->path->moveTo(p->x + pt->x, p->y + pt->y);
+ return noErr;
+}
+
+OSStatus QMacFontPath::closePath(void *data)
+{
+ static_cast<QMacFontPath*>(data)->path->closeSubpath();
+ return noErr;
+}
+
+
+#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5
+QCoreTextFontEngineMulti::QCoreTextFontEngineMulti(const ATSFontFamilyRef &, const ATSFontRef &atsFontRef, const QFontDef &fontDef, bool)
+ : QFontEngineMulti(0)
+{
+ this->fontDef = fontDef;
+ CTFontSymbolicTraits symbolicTraits = 0;
+ if (fontDef.weight >= QFont::Bold)
+ symbolicTraits |= kCTFontBoldTrait;
+ switch (fontDef.style) {
+ case QFont::StyleNormal:
+ break;
+ case QFont::StyleItalic:
+ case QFont::StyleOblique:
+ symbolicTraits |= kCTFontItalicTrait;
+ break;
+ }
+
+ QCFString name;
+ ATSFontGetName(atsFontRef, kATSOptionFlagsDefault, &name);
+ QCFType<CTFontDescriptorRef> descriptor = CTFontDescriptorCreateWithNameAndSize(name, fontDef.pixelSize);
+ QCFType<CTFontRef> baseFont = CTFontCreateWithFontDescriptor(descriptor, fontDef.pixelSize, 0);
+ ctfont = CTFontCreateCopyWithSymbolicTraits(baseFont, fontDef.pixelSize, 0, symbolicTraits, symbolicTraits);
+
+ // CTFontCreateCopyWithSymbolicTraits returns NULL if we ask for a trait that does
+ // not exist for the given font. (for example italic)
+ if (ctfont == 0) {
+ ctfont = baseFont;
+ CFRetain(ctfont);
+ }
+
+ const void *keys[] = { NSFontAttributeName };
+ const void *values[] = { ctfont };
+ attributeDict = CFDictionaryCreate(0, keys, values, 1,
+ &kCFTypeDictionaryKeyCallBacks,
+ &kCFTypeDictionaryValueCallBacks);
+
+ QCoreTextFontEngine *fe = new QCoreTextFontEngine(ctfont, fontDef, this);
+ fe->ref.ref();
+ engines.append(fe);
+
+}
+
+QCoreTextFontEngineMulti::~QCoreTextFontEngineMulti()
+{
+ CFRelease(ctfont);
+}
+
+uint QCoreTextFontEngineMulti::fontIndexForFont(CTFontRef id) const
+{
+ for (int i = 0; i < engines.count(); ++i) {
+ if (CFEqual(engineAt(i)->ctfont, id))
+ return i;
+ }
+
+ QCoreTextFontEngineMulti *that = const_cast<QCoreTextFontEngineMulti *>(this);
+ QCoreTextFontEngine *fe = new QCoreTextFontEngine(id, fontDef, that);
+ fe->ref.ref();
+ that->engines.append(fe);
+ return engines.count() - 1;
+}
+
+bool QCoreTextFontEngineMulti::stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs, int *nglyphs, QTextEngine::ShaperFlags,
+ unsigned short *logClusters, const HB_CharAttributes *) const
+{
+ QCFType<CFStringRef> cfstring = CFStringCreateWithCharactersNoCopy(0,
+ reinterpret_cast<const UniChar *>(str),
+ len, kCFAllocatorNull);
+ QCFType<CFAttributedStringRef> attributedString = CFAttributedStringCreate(0, cfstring, attributeDict);
+ QCFType<CTTypesetterRef> typeSetter = CTTypesetterCreateWithAttributedString(attributedString);
+ CFRange range = {0, 0};
+ QCFType<CTLineRef> line = CTTypesetterCreateLine(typeSetter, range);
+ CFArrayRef array = CTLineGetGlyphRuns(line);
+ uint arraySize = CFArrayGetCount(array);
+ glyph_t *outGlyphs = glyphs->glyphs;
+ HB_GlyphAttributes *outAttributes = glyphs->attributes;
+ QFixed *outAdvances_x = glyphs->advances_x;
+ QFixed *outAdvances_y = glyphs->advances_y;
+ glyph_t *initialGlyph = outGlyphs;
+
+ if (arraySize == 0)
+ return false;
+
+ const bool rtl = (CTRunGetStatus(static_cast<CTRunRef>(CFArrayGetValueAtIndex(array, 0))) & kCTRunStatusRightToLeft);
+
+ bool outOBounds = false;
+ for (uint i = 0; i < arraySize; ++i) {
+ CTRunRef run = static_cast<CTRunRef>(CFArrayGetValueAtIndex(array, rtl ? (arraySize - 1 - i) : i));
+ CFIndex glyphCount = CTRunGetGlyphCount(run);
+ if (glyphCount == 0)
+ continue;
+
+ Q_ASSERT((CTRunGetStatus(run) & kCTRunStatusRightToLeft) == rtl);
+
+ if (!outOBounds && outGlyphs + glyphCount - initialGlyph > *nglyphs) {
+ outOBounds = true;
+ }
+ if (!outOBounds) {
+ CFDictionaryRef runAttribs = CTRunGetAttributes(run);
+ //NSLog(@"Dictionary %@", runAttribs);
+ if (!runAttribs)
+ runAttribs = attributeDict;
+ CTFontRef runFont = static_cast<CTFontRef>(CFDictionaryGetValue(runAttribs, NSFontAttributeName));
+ const uint fontIndex = (fontIndexForFont(runFont) << 24);
+ //NSLog(@"Run Font Name = %@", CTFontCopyFamilyName(runFont));
+ QVarLengthArray<CGGlyph, 512> cgglyphs(0);
+ const CGGlyph *tmpGlyphs = CTRunGetGlyphsPtr(run);
+ if (!tmpGlyphs) {
+ cgglyphs.resize(glyphCount);
+ CTRunGetGlyphs(run, range, cgglyphs.data());
+ tmpGlyphs = cgglyphs.constData();
+ }
+ QVarLengthArray<CGPoint, 512> cgpoints(0);
+ const CGPoint *tmpPoints = CTRunGetPositionsPtr(run);
+ if (!tmpPoints) {
+ cgpoints.resize(glyphCount);
+ CTRunGetPositions(run, range, cgpoints.data());
+ tmpPoints = cgpoints.constData();
+ }
+
+ const int rtlOffset = rtl ? (glyphCount - 1) : 0;
+ const int rtlSign = rtl ? -1 : 1;
+
+ if (logClusters) {
+ CFRange stringRange = CTRunGetStringRange(run);
+ QVarLengthArray<CFIndex, 512> stringIndices(0);
+ const CFIndex *tmpIndices = CTRunGetStringIndicesPtr(run);
+ if (!tmpIndices) {
+ stringIndices.resize(glyphCount);
+ CTRunGetStringIndices(run, range, stringIndices.data());
+ tmpIndices = stringIndices.constData();
+ }
+
+ const int firstGlyphIndex = outGlyphs - initialGlyph;
+ outAttributes[0].clusterStart = true;
+
+ CFIndex k = 0;
+ CFIndex i = 0;
+ for (i = stringRange.location;
+ (i < stringRange.location + stringRange.length) && (k < glyphCount); ++i) {
+ if (tmpIndices[k * rtlSign + rtlOffset] == i || i == stringRange.location) {
+ logClusters[i] = k + firstGlyphIndex;
+ outAttributes[k].clusterStart = true;
+ ++k;
+ } else {
+ logClusters[i] = k + firstGlyphIndex - 1;
+ }
+ }
+ // in case of a ligature at the end, fill the remaining logcluster entries
+ for (;i < stringRange.location + stringRange.length; i++) {
+ logClusters[i] = k + firstGlyphIndex - 1;
+ }
+ }
+ for (CFIndex i = 0; i < glyphCount - 1; ++i) {
+ int idx = rtlOffset + rtlSign * i;
+ outGlyphs[idx] = tmpGlyphs[i] | fontIndex;
+ outAdvances_x[idx] = QFixed::fromReal(tmpPoints[i + 1].x - tmpPoints[i].x);
+ outAdvances_y[idx] = QFixed::fromReal(tmpPoints[i + 1].y - tmpPoints[i].y);
+ }
+ double runWidth = ceil(CTRunGetTypographicBounds(run, range, 0, 0, 0));
+ runWidth += tmpPoints[0].x;
+ outGlyphs[rtl ? 0 : (glyphCount - 1)] = tmpGlyphs[glyphCount - 1] | fontIndex;
+ outAdvances_x[rtl ? 0 : (glyphCount - 1)] = QFixed::fromReal(runWidth - tmpPoints[glyphCount - 1].x);
+ }
+ outGlyphs += glyphCount;
+ outAttributes += glyphCount;
+ outAdvances_x += glyphCount;
+ outAdvances_y += glyphCount;
+ }
+ *nglyphs = (outGlyphs - initialGlyph);
+ return !outOBounds;
+}
+
+bool QCoreTextFontEngineMulti::stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs,
+ int *nglyphs, QTextEngine::ShaperFlags flags) const
+{
+ return stringToCMap(str, len, glyphs, nglyphs, flags, 0, 0);
+}
+
+void QCoreTextFontEngineMulti::recalcAdvances(int , QGlyphLayout *, QTextEngine::ShaperFlags) const
+{
+}
+void QCoreTextFontEngineMulti::doKerning(int , QGlyphLayout *, QTextEngine::ShaperFlags) const
+{
+}
+
+void QCoreTextFontEngineMulti::loadEngine(int)
+{
+ // Do nothing
+ Q_ASSERT(false);
+}
+
+
+
+QCoreTextFontEngine::QCoreTextFontEngine(CTFontRef font, const QFontDef &def,
+ QCoreTextFontEngineMulti *multiEngine)
+{
+ fontDef = def;
+ parentEngine = multiEngine;
+ synthesisFlags = 0;
+ ctfont = font;
+ CFRetain(ctfont);
+ ATSFontRef atsfont = CTFontGetPlatformFont(ctfont, 0);
+ cgFont = CGFontCreateWithPlatformFont(&atsfont);
+ CTFontSymbolicTraits traits = CTFontGetSymbolicTraits(ctfont);
+ if (fontDef.weight >= QFont::Bold && !(traits & kCTFontBoldTrait)) {
+ synthesisFlags |= SynthesizedBold;
+ }
+
+ if (fontDef.style != QFont::StyleNormal && !(traits & kCTFontItalicTrait)) {
+ synthesisFlags |= SynthesizedItalic;
+ }
+
+ QByteArray os2Table = getSfntTable(MAKE_TAG('O', 'S', '/', '2'));
+ if (os2Table.size() >= 10)
+ fsType = qFromBigEndian<quint16>(reinterpret_cast<const uchar *>(os2Table.constData() + 8));
+}
+
+QCoreTextFontEngine::~QCoreTextFontEngine()
+{
+ CFRelease(ctfont);
+ CFRelease(cgFont);
+}
+
+bool QCoreTextFontEngine::stringToCMap(const QChar *, int, QGlyphLayout *, int *, QTextEngine::ShaperFlags) const
+{
+ return false;
+}
+
+glyph_metrics_t QCoreTextFontEngine::boundingBox(const QGlyphLayout &glyphs)
+{
+ QFixed w;
+ for (int i = 0; i < glyphs.numGlyphs; ++i)
+ w += glyphs.effectiveAdvance(i);
+ return glyph_metrics_t(0, -(ascent()), w, ascent()+descent(), w, 0);
+}
+glyph_metrics_t QCoreTextFontEngine::boundingBox(glyph_t glyph)
+{
+ glyph_metrics_t ret;
+ CGGlyph g = glyph;
+ CGRect rect = CTFontGetBoundingRectsForGlyphs(ctfont, kCTFontHorizontalOrientation, &g, 0, 1);
+ ret.width = QFixed::fromReal(rect.size.width);
+ ret.height = QFixed::fromReal(rect.size.height);
+ ret.x = QFixed::fromReal(rect.origin.x);
+ ret.y = -QFixed::fromReal(rect.origin.y) - ret.height;
+ CGSize advances[1];
+ CTFontGetAdvancesForGlyphs(ctfont, kCTFontHorizontalOrientation, &g, advances, 1);
+ ret.xoff = QFixed::fromReal(advances[0].width);
+ ret.yoff = QFixed::fromReal(advances[0].height);
+ return ret;
+}
+
+QFixed QCoreTextFontEngine::ascent() const
+{
+ return QFixed::fromReal(CTFontGetAscent(ctfont));
+}
+QFixed QCoreTextFontEngine::descent() const
+{
+ return QFixed::fromReal(CTFontGetDescent(ctfont));
+}
+QFixed QCoreTextFontEngine::leading() const
+{
+ return QFixed::fromReal(CTFontGetLeading(ctfont));
+}
+QFixed QCoreTextFontEngine::xHeight() const
+{
+ return QFixed::fromReal(CTFontGetXHeight(ctfont));
+}
+QFixed QCoreTextFontEngine::averageCharWidth() const
+{
+ // ### Need to implement properly and get the information from the OS/2 Table.
+ return QFontEngine::averageCharWidth();
+}
+
+qreal QCoreTextFontEngine::maxCharWidth() const
+{
+ // ### Max Help!
+ return 0;
+
+}
+qreal QCoreTextFontEngine::minLeftBearing() const
+{
+ // ### Min Help!
+ return 0;
+
+}
+qreal QCoreTextFontEngine::minRightBearing() const
+{
+ // ### Max Help! (even thought it's right)
+ return 0;
+
+}
+
+void QCoreTextFontEngine::draw(CGContextRef ctx, qreal x, qreal y, const QTextItemInt &ti, int paintDeviceHeight)
+{
+ QVarLengthArray<QFixedPoint> positions;
+ QVarLengthArray<glyph_t> glyphs;
+ QTransform matrix;
+ matrix.translate(x, y);
+ getGlyphPositions(ti.glyphs, matrix, ti.flags, glyphs, positions);
+ if (glyphs.size() == 0)
+ return;
+
+ CGContextSetFontSize(ctx, fontDef.pixelSize);
+
+ CGAffineTransform oldTextMatrix = CGContextGetTextMatrix(ctx);
+
+ CGAffineTransform cgMatrix = CGAffineTransformMake(1, 0, 0, -1, 0, -paintDeviceHeight);
+
+ CGAffineTransformConcat(cgMatrix, oldTextMatrix);
+
+ if (synthesisFlags & QFontEngine::SynthesizedItalic)
+ cgMatrix = CGAffineTransformConcat(cgMatrix, CGAffineTransformMake(1, 0, -tanf(14 * acosf(0) / 90), 1, 0, 0));
+
+// ### cgMatrix = CGAffineTransformConcat(cgMatrix, transform);
+
+ CGContextSetTextMatrix(ctx, cgMatrix);
+
+ CGContextSetTextDrawingMode(ctx, kCGTextFill);
+
+
+ QVarLengthArray<CGSize> advances(glyphs.size());
+ QVarLengthArray<CGGlyph> cgGlyphs(glyphs.size());
+
+ for (int i = 0; i < glyphs.size() - 1; ++i) {
+ advances[i].width = (positions[i + 1].x - positions[i].x).toReal();
+ advances[i].height = (positions[i + 1].y - positions[i].y).toReal();
+ cgGlyphs[i] = glyphs[i];
+ }
+ advances[glyphs.size() - 1].width = 0;
+ advances[glyphs.size() - 1].height = 0;
+ cgGlyphs[glyphs.size() - 1] = glyphs[glyphs.size() - 1];
+
+ CGContextSetFont(ctx, cgFont);
+ //NSLog(@"Font inDraw %@ ctfont %@", CGFontCopyFullName(cgFont), CTFontCopyFamilyName(ctfont));
+
+ CGContextSetTextPosition(ctx, positions[0].x.toReal(), positions[0].y.toReal());
+
+ CGContextShowGlyphsWithAdvances(ctx, cgGlyphs.data(), advances.data(), glyphs.size());
+
+ if (synthesisFlags & QFontEngine::SynthesizedBold) {
+ CGContextSetTextPosition(ctx, positions[0].x.toReal() + 0.5 * lineThickness().toReal(),
+ positions[0].y.toReal());
+
+ CGContextShowGlyphsWithAdvances(ctx, cgGlyphs.data(), advances.data(), glyphs.size());
+ }
+
+ CGContextSetTextMatrix(ctx, oldTextMatrix);
+}
+
+struct ConvertPathInfo
+{
+ ConvertPathInfo(QPainterPath *newPath, const QPointF &newPos) : path(newPath), pos(newPos) {}
+ QPainterPath *path;
+ QPointF pos;
+};
+
+static void convertCGPathToQPainterPath(void *info, const CGPathElement *element)
+{
+ ConvertPathInfo *myInfo = static_cast<ConvertPathInfo *>(info);
+ switch(element->type) {
+ case kCGPathElementMoveToPoint:
+ myInfo->path->moveTo(element->points[0].x + myInfo->pos.x(),
+ element->points[0].y + myInfo->pos.y());
+ break;
+ case kCGPathElementAddLineToPoint:
+ myInfo->path->lineTo(element->points[0].x + myInfo->pos.x(),
+ element->points[0].y + myInfo->pos.y());
+ break;
+ case kCGPathElementAddQuadCurveToPoint:
+ myInfo->path->quadTo(element->points[0].x + myInfo->pos.x(),
+ element->points[0].y + myInfo->pos.y(),
+ element->points[1].x + myInfo->pos.x(),
+ element->points[1].y + myInfo->pos.y());
+ break;
+ case kCGPathElementAddCurveToPoint:
+ myInfo->path->cubicTo(element->points[0].x + myInfo->pos.x(),
+ element->points[0].y + myInfo->pos.y(),
+ element->points[1].x + myInfo->pos.x(),
+ element->points[1].y + myInfo->pos.y(),
+ element->points[2].x + myInfo->pos.x(),
+ element->points[2].y + myInfo->pos.y());
+ break;
+ case kCGPathElementCloseSubpath:
+ myInfo->path->closeSubpath();
+ break;
+ default:
+ qDebug() << "Unhandled path transform type: " << element->type;
+ }
+
+}
+
+void QCoreTextFontEngine::addGlyphsToPath(glyph_t *glyphs, QFixedPoint *positions, int nGlyphs,
+ QPainterPath *path, QTextItem::RenderFlags)
+{
+
+ CGAffineTransform cgMatrix = CGAffineTransformIdentity;
+ cgMatrix = CGAffineTransformScale(cgMatrix, 1, -1);
+
+ if (synthesisFlags & QFontEngine::SynthesizedItalic)
+ cgMatrix = CGAffineTransformConcat(cgMatrix, CGAffineTransformMake(1, 0, tanf(14 * acosf(0) / 90), 1, 0, 0));
+
+
+ for (int i = 0; i < nGlyphs; ++i) {
+ QCFType<CGPathRef> cgpath = CTFontCreatePathForGlyph(ctfont, glyphs[i], &cgMatrix);
+ ConvertPathInfo info(path, positions[i].toPointF());
+ CGPathApply(cgpath, &info, convertCGPathToQPainterPath);
+ }
+}
+
+QImage QCoreTextFontEngine::alphaMapForGlyph(glyph_t glyph)
+{
+ const glyph_metrics_t br = boundingBox(glyph);
+ QImage im(qRound(br.width)+2, qRound(br.height)+2, QImage::Format_RGB32);
+ im.fill(0);
+
+ CGColorSpaceRef colorspace = QCoreGraphicsPaintEngine::macGenericColorSpace();
+#if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4)
+ uint cgflags = kCGImageAlphaNoneSkipFirst;
+#ifdef kCGBitmapByteOrder32Host //only needed because CGImage.h added symbols in the minor version
+ if(QSysInfo::MacintoshVersion >= QSysInfo::MV_10_4)
+ cgflags |= kCGBitmapByteOrder32Host;
+#endif
+#else
+ CGImageAlphaInfo cgflags = kCGImageAlphaNoneSkipFirst;
+#endif
+ CGContextRef ctx = CGBitmapContextCreate(im.bits(), im.width(), im.height(),
+ 8, im.bytesPerLine(), colorspace,
+ cgflags);
+ CGContextSetFontSize(ctx, fontDef.pixelSize);
+ CGContextSetShouldAntialias(ctx, fontDef.pointSize > qt_antialiasing_threshold && !(fontDef.styleStrategy & QFont::NoAntialias));
+ CGAffineTransform oldTextMatrix = CGContextGetTextMatrix(ctx);
+ CGAffineTransform cgMatrix = CGAffineTransformMake(1, 0, 0, 1, 0, 0);
+
+ CGAffineTransformConcat(cgMatrix, oldTextMatrix);
+
+ if (synthesisFlags & QFontEngine::SynthesizedItalic)
+ cgMatrix = CGAffineTransformConcat(cgMatrix, CGAffineTransformMake(1, 0, tanf(14 * acosf(0) / 90), 1, 0, 0));
+
+// ### cgMatrix = CGAffineTransformConcat(cgMatrix, transform);
+
+ CGContextSetTextMatrix(ctx, cgMatrix);
+ CGContextSetRGBFillColor(ctx, 1, 1, 1, 1);
+ CGContextSetTextDrawingMode(ctx, kCGTextFill);
+
+ ATSFontRef atsfont = CTFontGetPlatformFont(ctfont, 0);
+ QCFType<CGFontRef> cgFont = CGFontCreateWithPlatformFont(&atsfont);
+ CGContextSetFont(ctx, cgFont);
+
+ qreal pos_x = -br.x.toReal()+1, pos_y = im.height()+br.y.toReal();
+ CGContextSetTextPosition(ctx, pos_x, pos_y);
+
+ CGSize advance;
+ advance.width = 0;
+ advance.height = 0;
+ CGGlyph cgGlyph = glyph;
+ CGContextShowGlyphsWithAdvances(ctx, &cgGlyph, &advance, 1);
+
+ if (synthesisFlags & QFontEngine::SynthesizedBold) {
+ CGContextSetTextPosition(ctx, pos_x + 0.5 * lineThickness().toReal(), pos_y);
+ CGContextShowGlyphsWithAdvances(ctx, &cgGlyph, &advance, 1);
+ }
+
+ CGContextRelease(ctx);
+
+ QImage indexed(im.width(), im.height(), QImage::Format_Indexed8);
+ QVector<QRgb> colors(256);
+ for (int i=0; i<256; ++i)
+ colors[i] = qRgba(0, 0, 0, i);
+ indexed.setColorTable(colors);
+
+ for (int y=0; y<im.height(); ++y) {
+ uint *src = (uint*) im.scanLine(y);
+ uchar *dst = indexed.scanLine(y);
+ for (int x=0; x<im.width(); ++x) {
+ *dst = qGray(*src);
+ ++dst;
+ ++src;
+ }
+ }
+
+ return indexed;
+}
+
+void QCoreTextFontEngine::recalcAdvances(int numGlyphs, QGlyphLayout *glyphs, QTextEngine::ShaperFlags flags) const
+{
+ Q_ASSERT(false);
+ Q_UNUSED(numGlyphs);
+ Q_UNUSED(glyphs);
+ Q_UNUSED(flags);
+}
+
+QFontEngine::FaceId QCoreTextFontEngine::faceId() const
+{
+ return QFontEngine::FaceId();
+}
+
+bool QCoreTextFontEngine::canRender(const QChar *string, int len)
+{
+ QCFType<CTFontRef> retFont = CTFontCreateForString(ctfont,
+ QCFType<CFStringRef>(CFStringCreateWithCharactersNoCopy(0,
+ reinterpret_cast<const UniChar *>(string),
+ len, kCFAllocatorNull)),
+ CFRangeMake(0, len));
+ return retFont != 0;
+ return false;
+}
+
+ bool QCoreTextFontEngine::getSfntTableData(uint tag, uchar *buffer, uint *length) const
+ {
+ QCFType<CFDataRef> table = CTFontCopyTable(ctfont, tag, 0);
+ if (!table || !length)
+ return false;
+ CFIndex tableLength = CFDataGetLength(table);
+ int availableLength = *length;
+ *length = tableLength;
+ if (buffer) {
+ if (tableLength > availableLength)
+ return false;
+ CFDataGetBytes(table, CFRangeMake(0, tableLength), buffer);
+ }
+ return true;
+ }
+
+void QCoreTextFontEngine::getUnscaledGlyph(glyph_t, QPainterPath *, glyph_metrics_t *)
+{
+ // ###
+}
+
+#endif // MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5
+
+#ifndef QT_MAC_USE_COCOA
+QFontEngineMacMulti::QFontEngineMacMulti(const ATSFontFamilyRef &atsFamily, const ATSFontRef &atsFontRef, const QFontDef &fontDef, bool kerning)
+ : QFontEngineMulti(0)
+{
+ this->fontDef = fontDef;
+ this->kerning = kerning;
+
+ // hopefully (CTFontCreateWithName or CTFontCreateWithFontDescriptor) + CTFontCreateCopyWithSymbolicTraits
+ // (or CTFontCreateWithQuickdrawInstance)
+ FMFontFamily fmFamily;
+ FMFontStyle fntStyle = 0;
+ fmFamily = FMGetFontFamilyFromATSFontFamilyRef(atsFamily);
+ if (fmFamily == kInvalidFontFamily) {
+ // Use the ATSFont then...
+ fontID = FMGetFontFromATSFontRef(atsFontRef);
+ } else {
+ if (fontDef.weight >= QFont::Bold)
+ fntStyle |= ::bold;
+ if (fontDef.style != QFont::StyleNormal)
+ fntStyle |= ::italic;
+
+ FMFontStyle intrinsicStyle;
+ FMFont fnt = 0;
+ if (FMGetFontFromFontFamilyInstance(fmFamily, fntStyle, &fnt, &intrinsicStyle) == noErr)
+ fontID = FMGetATSFontRefFromFont(fnt);
+ }
+
+ // CFDictionaryRef, <CTStringAttributes.h>
+ OSStatus status;
+
+ status = ATSUCreateTextLayout(&textLayout);
+ Q_ASSERT(status == noErr);
+
+ const int maxAttributeCount = 5;
+ ATSUAttributeTag tags[maxAttributeCount + 1];
+ ByteCount sizes[maxAttributeCount + 1];
+ ATSUAttributeValuePtr values[maxAttributeCount + 1];
+ int attributeCount = 0;
+
+ Fixed size = FixRatio(fontDef.pixelSize, 1);
+ tags[attributeCount] = kATSUSizeTag;
+ sizes[attributeCount] = sizeof(size);
+ values[attributeCount] = &size;
+ ++attributeCount;
+
+ tags[attributeCount] = kATSUFontTag;
+ sizes[attributeCount] = sizeof(fontID);
+ values[attributeCount] = &this->fontID;
+ ++attributeCount;
+
+ transform = CGAffineTransformIdentity;
+ if (fontDef.stretch != 100) {
+ transform = CGAffineTransformMakeScale(float(fontDef.stretch) / float(100), 1);
+ tags[attributeCount] = kATSUFontMatrixTag;
+ sizes[attributeCount] = sizeof(transform);
+ values[attributeCount] = &transform;
+ ++attributeCount;
+ }
+
+ status = ATSUCreateStyle(&style);
+ Q_ASSERT(status == noErr);
+
+ Q_ASSERT(attributeCount < maxAttributeCount + 1);
+ status = ATSUSetAttributes(style, attributeCount, tags, sizes, values);
+ Q_ASSERT(status == noErr);
+
+ QFontEngineMac *fe = new QFontEngineMac(style, fontID, fontDef, this);
+ fe->ref.ref();
+ engines.append(fe);
+}
+
+QFontEngineMacMulti::~QFontEngineMacMulti()
+{
+ ATSUDisposeTextLayout(textLayout);
+ ATSUDisposeStyle(style);
+
+ for (int i = 0; i < engines.count(); ++i) {
+ QFontEngineMac *fe = const_cast<QFontEngineMac *>(static_cast<const QFontEngineMac *>(engines.at(i)));
+ fe->multiEngine = 0;
+ if (!fe->ref.deref())
+ delete fe;
+ }
+ engines.clear();
+}
+
+struct QGlyphLayoutInfo
+{
+ QGlyphLayout *glyphs;
+ int *numGlyphs;
+ bool callbackCalled;
+ int *mappedFonts;
+ QTextEngine::ShaperFlags flags;
+ QFontEngineMacMulti::ShaperItem *shaperItem;
+};
+
+static OSStatus atsuPostLayoutCallback(ATSULayoutOperationSelector selector, ATSULineRef lineRef, URefCon refCon,
+ void *operationExtraParameter, ATSULayoutOperationCallbackStatus *callbackStatus)
+{
+ Q_UNUSED(selector);
+ Q_UNUSED(operationExtraParameter);
+
+ QGlyphLayoutInfo *nfo = reinterpret_cast<QGlyphLayoutInfo *>(refCon);
+ nfo->callbackCalled = true;
+
+ ATSLayoutRecord *layoutData = 0;
+ ItemCount itemCount = 0;
+
+ OSStatus e = noErr;
+ e = ATSUDirectGetLayoutDataArrayPtrFromLineRef(lineRef, kATSUDirectDataLayoutRecordATSLayoutRecordCurrent,
+ /*iCreate =*/ false,
+ (void **) &layoutData,
+ &itemCount);
+ if (e != noErr)
+ return e;
+
+ *nfo->numGlyphs = itemCount - 1;
+
+ Fixed *baselineDeltas = 0;
+
+ e = ATSUDirectGetLayoutDataArrayPtrFromLineRef(lineRef, kATSUDirectDataBaselineDeltaFixedArray,
+ /*iCreate =*/ true,
+ (void **) &baselineDeltas,
+ &itemCount);
+ if (e != noErr)
+ return e;
+
+ int nextCharStop = -1;
+ int currentClusterGlyph = -1; // first glyph in log cluster
+ QFontEngineMacMulti::ShaperItem *item = nfo->shaperItem;
+ if (item->charAttributes) {
+ item = nfo->shaperItem;
+#if !defined(QT_NO_DEBUG)
+ int surrogates = 0;
+ const QChar *str = item->string;
+ for (int i = item->from; i < item->from + item->length - 1; ++i) {
+ surrogates += (str[i].unicode() >= 0xd800 && str[i].unicode() < 0xdc00
+ && str[i+1].unicode() >= 0xdc00 && str[i+1].unicode() < 0xe000);
+ }
+ Q_ASSERT(*nfo->numGlyphs == item->length - surrogates);
+#endif
+ for (nextCharStop = item->from; nextCharStop < item->from + item->length; ++nextCharStop)
+ if (item->charAttributes[nextCharStop].charStop)
+ break;
+ nextCharStop -= item->from;
+ }
+
+ nfo->glyphs->attributes[0].clusterStart = true;
+ int glyphIdx = 0;
+ int glyphIncrement = 1;
+ if (nfo->flags & QTextEngine::RightToLeft) {
+ glyphIdx = itemCount - 2;
+ glyphIncrement = -1;
+ }
+ for (int i = 0; i < *nfo->numGlyphs; ++i, glyphIdx += glyphIncrement) {
+
+ int charOffset = layoutData[glyphIdx].originalOffset / sizeof(UniChar);
+ const int fontIdx = nfo->mappedFonts[charOffset];
+
+ ATSGlyphRef glyphId = layoutData[glyphIdx].glyphID;
+
+ QFixed yAdvance = FixedToQFixed(baselineDeltas[glyphIdx]);
+ QFixed xAdvance = FixedToQFixed(layoutData[glyphIdx + 1].realPos - layoutData[glyphIdx].realPos);
+
+ if (glyphId != 0xffff || i == 0) {
+ nfo->glyphs->glyphs[i] = (glyphId & 0x00ffffff) | (fontIdx << 24);
+
+ nfo->glyphs->advances_y[i] = yAdvance;
+ nfo->glyphs->advances_x[i] = xAdvance;
+ } else {
+ // ATSUI gives us 0xffff as glyph id at the index in the glyph array for
+ // a character position that maps to a ligtature. Such a glyph id does not
+ // result in any visual glyph, but it may have an advance, which is why we
+ // sum up the glyph advances.
+ --i;
+ nfo->glyphs->advances_y[i] += yAdvance;
+ nfo->glyphs->advances_x[i] += xAdvance;
+ *nfo->numGlyphs -= 1;
+ }
+
+ if (item->log_clusters) {
+ if (charOffset >= nextCharStop) {
+ nfo->glyphs->attributes[i].clusterStart = true;
+ currentClusterGlyph = i;
+
+ ++nextCharStop;
+ for (; nextCharStop < item->length; ++nextCharStop)
+ if (item->charAttributes[item->from + nextCharStop].charStop)
+ break;
+ } else {
+ if (currentClusterGlyph == -1)
+ currentClusterGlyph = i;
+ }
+ item->log_clusters[charOffset] = currentClusterGlyph;
+
+ // surrogate handling
+ if (charOffset < item->length - 1) {
+ QChar current = item->string[item->from + charOffset];
+ QChar next = item->string[item->from + charOffset + 1];
+ if (current.unicode() >= 0xd800 && current.unicode() < 0xdc00
+ && next.unicode() >= 0xdc00 && next.unicode() < 0xe000) {
+ item->log_clusters[charOffset + 1] = currentClusterGlyph;
+ }
+ }
+ }
+ }
+
+ /*
+ if (item) {
+ qDebug() << "resulting logclusters:";
+ for (int i = 0; i < item->length; ++i)
+ qDebug() << "logClusters[" << i << "] =" << item->log_clusters[i];
+ qDebug() << "clusterstarts:";
+ for (int i = 0; i < *nfo->numGlyphs; ++i)
+ qDebug() << "clusterStart[" << i << "] =" << nfo->glyphs[i].attributes.clusterStart;
+ }
+ */
+
+ ATSUDirectReleaseLayoutDataArrayPtr(lineRef, kATSUDirectDataBaselineDeltaFixedArray,
+ (void **) &baselineDeltas);
+
+ ATSUDirectReleaseLayoutDataArrayPtr(lineRef, kATSUDirectDataLayoutRecordATSLayoutRecordCurrent,
+ (void **) &layoutData);
+
+ *callbackStatus = kATSULayoutOperationCallbackStatusHandled;
+ return noErr;
+}
+
+int QFontEngineMacMulti::fontIndexForFontID(ATSUFontID id) const
+{
+ for (int i = 0; i < engines.count(); ++i) {
+ if (engineAt(i)->fontID == id)
+ return i;
+ }
+
+ QFontEngineMacMulti *that = const_cast<QFontEngineMacMulti *>(this);
+ QFontEngineMac *fe = new QFontEngineMac(style, id, fontDef, that);
+ fe->ref.ref();
+ that->engines.append(fe);
+ return engines.count() - 1;
+}
+
+bool QFontEngineMacMulti::stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs, int *nglyphs, QTextEngine::ShaperFlags flags) const
+{
+ return stringToCMap(str, len, glyphs, nglyphs, flags, /*logClusters=*/0, /*charAttributes=*/0);
+}
+
+bool QFontEngineMacMulti::stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs, int *nglyphs, QTextEngine::ShaperFlags flags,
+ unsigned short *logClusters, const HB_CharAttributes *charAttributes) const
+{
+ if (*nglyphs < len) {
+ *nglyphs = len;
+ return false;
+ }
+
+ ShaperItem shaperItem;
+ shaperItem.string = str;
+ shaperItem.from = 0;
+ shaperItem.length = len;
+ shaperItem.glyphs = *glyphs;
+ shaperItem.glyphs.numGlyphs = *nglyphs;
+ shaperItem.flags = flags;
+ shaperItem.log_clusters = logClusters;
+ shaperItem.charAttributes = charAttributes;
+
+ const int maxChars = qMax(1,
+ int(SHRT_MAX / maxCharWidth())
+ - 10 // subtract a few to be on the safe side
+ );
+ if (len < maxChars || !charAttributes)
+ return stringToCMapInternal(str, len, glyphs, nglyphs, flags, &shaperItem);
+
+ int charIdx = 0;
+ int glyphIdx = 0;
+ ShaperItem tmpItem = shaperItem;
+
+ do {
+ tmpItem.from = shaperItem.from + charIdx;
+
+ int charCount = qMin(maxChars, len - charIdx);
+
+ int lastWhitespace = tmpItem.from + charCount - 1;
+ int lastSoftBreak = lastWhitespace;
+ int lastCharStop = lastSoftBreak;
+ for (int i = lastCharStop; i >= tmpItem.from; --i) {
+ if (tmpItem.charAttributes[i].whiteSpace) {
+ lastWhitespace = i;
+ break;
+ } if (tmpItem.charAttributes[i].lineBreakType != HB_NoBreak) {
+ lastSoftBreak = i;
+ } if (tmpItem.charAttributes[i].charStop) {
+ lastCharStop = i;
+ }
+ }
+ charCount = qMin(lastWhitespace, qMin(lastSoftBreak, lastCharStop)) - tmpItem.from + 1;
+
+ int glyphCount = shaperItem.glyphs.numGlyphs - glyphIdx;
+ if (glyphCount <= 0)
+ return false;
+ tmpItem.length = charCount;
+ tmpItem.glyphs = shaperItem.glyphs.mid(glyphIdx, glyphCount);
+ tmpItem.log_clusters = shaperItem.log_clusters + charIdx;
+ if (!stringToCMapInternal(tmpItem.string + tmpItem.from, tmpItem.length,
+ &tmpItem.glyphs, &glyphCount, flags,
+ &tmpItem)) {
+ *nglyphs = glyphIdx + glyphCount;
+ return false;
+ }
+ for (int i = 0; i < charCount; ++i)
+ tmpItem.log_clusters[i] += glyphIdx;
+ glyphIdx += glyphCount;
+ charIdx += charCount;
+ } while (charIdx < len);
+ *nglyphs = glyphIdx;
+ glyphs->numGlyphs = glyphIdx;
+
+ return true;
+}
+
+bool QFontEngineMacMulti::stringToCMapInternal(const QChar *str, int len, QGlyphLayout *glyphs, int *nglyphs, QTextEngine::ShaperFlags flags,ShaperItem *shaperItem) const
+{
+ //qDebug() << "stringToCMap" << QString(str, len);
+
+ OSStatus e = noErr;
+
+ e = ATSUSetTextPointerLocation(textLayout, (UniChar *)(str), 0, len, len);
+ if (e != noErr) {
+ qWarning("Qt: internal: %ld: Error ATSUSetTextPointerLocation %s: %d", long(e), __FILE__, __LINE__);
+ return false;
+ }
+
+ QGlyphLayoutInfo nfo;
+ nfo.glyphs = glyphs;
+ nfo.numGlyphs = nglyphs;
+ nfo.callbackCalled = false;
+ nfo.flags = flags;
+ nfo.shaperItem = shaperItem;
+
+ QVarLengthArray<int> mappedFonts(len);
+ for (int i = 0; i < len; ++i)
+ mappedFonts[i] = 0;
+ nfo.mappedFonts = mappedFonts.data();
+
+ Q_ASSERT(sizeof(void *) <= sizeof(URefCon));
+ e = ATSUSetTextLayoutRefCon(textLayout, (URefCon)&nfo);
+ if (e != noErr) {
+ qWarning("Qt: internal: %ld: Error ATSUSetTextLayoutRefCon %s: %d", long(e), __FILE__, __LINE__);
+ return false;
+ }
+
+ {
+ const int maxAttributeCount = 3;
+ ATSUAttributeTag tags[maxAttributeCount + 1];
+ ByteCount sizes[maxAttributeCount + 1];
+ ATSUAttributeValuePtr values[maxAttributeCount + 1];
+ int attributeCount = 0;
+
+ tags[attributeCount] = kATSULineLayoutOptionsTag;
+ ATSLineLayoutOptions layopts = kATSLineHasNoOpticalAlignment
+ | kATSLineIgnoreFontLeading
+ | kATSLineNoSpecialJustification // we do kashidas ourselves
+ | kATSLineDisableAllJustification
+ ;
+
+ if (!(flags & QTextEngine::DesignMetrics)) {
+ layopts |= kATSLineFractDisable | kATSLineUseDeviceMetrics
+ | kATSLineDisableAutoAdjustDisplayPos;
+ }
+
+ if (fontDef.styleStrategy & QFont::NoAntialias)
+ layopts |= kATSLineNoAntiAliasing;
+
+ if (!kerning)
+ layopts |= kATSLineDisableAllKerningAdjustments;
+
+ values[attributeCount] = &layopts;
+ sizes[attributeCount] = sizeof(layopts);
+ ++attributeCount;
+
+ tags[attributeCount] = kATSULayoutOperationOverrideTag;
+ ATSULayoutOperationOverrideSpecifier spec;
+ spec.operationSelector = kATSULayoutOperationPostLayoutAdjustment;
+ spec.overrideUPP = atsuPostLayoutCallback;
+ values[attributeCount] = &spec;
+ sizes[attributeCount] = sizeof(spec);
+ ++attributeCount;
+
+ // CTWritingDirection
+ Boolean direction;
+ if (flags & QTextEngine::RightToLeft)
+ direction = kATSURightToLeftBaseDirection;
+ else
+ direction = kATSULeftToRightBaseDirection;
+ tags[attributeCount] = kATSULineDirectionTag;
+ values[attributeCount] = &direction;
+ sizes[attributeCount] = sizeof(direction);
+ ++attributeCount;
+
+ Q_ASSERT(attributeCount < maxAttributeCount + 1);
+ e = ATSUSetLayoutControls(textLayout, attributeCount, tags, sizes, values);
+ if (e != noErr) {
+ qWarning("Qt: internal: %ld: Error ATSUSetLayoutControls %s: %d", long(e), __FILE__, __LINE__);
+ return false;
+ }
+
+ }
+
+ e = ATSUSetRunStyle(textLayout, style, 0, len);
+ if (e != noErr) {
+ qWarning("Qt: internal: %ld: Error ATSUSetRunStyle %s: %d", long(e), __FILE__, __LINE__);
+ return false;
+ }
+
+ if (!(fontDef.styleStrategy & QFont::NoFontMerging)) {
+ int pos = 0;
+ do {
+ ATSUFontID substFont = 0;
+ UniCharArrayOffset changedOffset = 0;
+ UniCharCount changeCount = 0;
+
+ e = ATSUMatchFontsToText(textLayout, pos, len - pos,
+ &substFont, &changedOffset,
+ &changeCount);
+ if (e == kATSUFontsMatched) {
+ int fontIdx = fontIndexForFontID(substFont);
+ for (uint i = 0; i < changeCount; ++i)
+ mappedFonts[changedOffset + i] = fontIdx;
+ pos = changedOffset + changeCount;
+ ATSUSetRunStyle(textLayout, engineAt(fontIdx)->style, changedOffset, changeCount);
+ } else if (e == kATSUFontsNotMatched) {
+ pos = changedOffset + changeCount;
+ }
+ } while (pos < len && e != noErr);
+ }
+ { // trigger the a layout
+ // CFAttributedStringCreate, CTFramesetterCreateWithAttributedString (or perhaps Typesetter)
+ Rect rect;
+ e = ATSUMeasureTextImage(textLayout, kATSUFromTextBeginning, kATSUToTextEnd,
+ /*iLocationX =*/ 0, /*iLocationY =*/ 0,
+ &rect);
+ if (e != noErr) {
+ qWarning("Qt: internal: %ld: Error ATSUMeasureTextImage %s: %d", long(e), __FILE__, __LINE__);
+ return false;
+ }
+ }
+
+ if (!nfo.callbackCalled) {
+ qWarning("Qt: internal: %ld: Error ATSUMeasureTextImage did not trigger callback %s: %d", long(e), __FILE__, __LINE__);
+ return false;
+ }
+
+ ATSUClearLayoutCache(textLayout, kATSUFromTextBeginning);
+ return true;
+}
+
+void QFontEngineMacMulti::recalcAdvances(QGlyphLayout *glyphs, QTextEngine::ShaperFlags flags) const
+{
+ Q_ASSERT(false);
+ Q_UNUSED(glyphs);
+ Q_UNUSED(flags);
+}
+
+void QFontEngineMacMulti::doKerning(QGlyphLayout *, QTextEngine::ShaperFlags) const
+{
+ //Q_ASSERT(false);
+}
+
+void QFontEngineMacMulti::loadEngine(int /*at*/)
+{
+ // should never be called!
+ Q_ASSERT(false);
+}
+
+bool QFontEngineMacMulti::canRender(const QChar *string, int len)
+{
+ ATSUSetTextPointerLocation(textLayout, reinterpret_cast<const UniChar *>(string), 0, len, len);
+ ATSUSetRunStyle(textLayout, style, 0, len);
+
+ OSStatus e = noErr;
+ int pos = 0;
+ do {
+ FMFont substFont = 0;
+ UniCharArrayOffset changedOffset = 0;
+ UniCharCount changeCount = 0;
+
+ // CTFontCreateForString
+ e = ATSUMatchFontsToText(textLayout, pos, len - pos,
+ &substFont, &changedOffset,
+ &changeCount);
+ if (e == kATSUFontsMatched) {
+ pos = changedOffset + changeCount;
+ } else if (e == kATSUFontsNotMatched) {
+ break;
+ }
+ } while (pos < len && e != noErr);
+
+ return e == noErr || e == kATSUFontsMatched;
+}
+
+QFontEngineMac::QFontEngineMac(ATSUStyle baseStyle, ATSUFontID fontID, const QFontDef &def, QFontEngineMacMulti *multiEngine)
+ : fontID(fontID), multiEngine(multiEngine), cmap(0), symbolCMap(false)
+{
+ fontDef = def;
+ ATSUCreateAndCopyStyle(baseStyle, &style);
+ ATSFontRef atsFont = FMGetATSFontRefFromFont(fontID);
+ cgFont = CGFontCreateWithPlatformFont(&atsFont);
+
+ const int maxAttributeCount = 4;
+ ATSUAttributeTag tags[maxAttributeCount + 1];
+ ByteCount sizes[maxAttributeCount + 1];
+ ATSUAttributeValuePtr values[maxAttributeCount + 1];
+ int attributeCount = 0;
+
+ synthesisFlags = 0;
+
+ // synthesizing using CG is not recommended
+ quint16 macStyle = 0;
+ {
+ uchar data[4];
+ ByteCount len = 4;
+ if (ATSFontGetTable(atsFont, MAKE_TAG('h', 'e', 'a', 'd'), 44, 4, &data, &len) == noErr)
+ macStyle = qFromBigEndian<quint16>(data);
+ }
+
+ Boolean atsuBold = false;
+ Boolean atsuItalic = false;
+ if (fontDef.weight >= QFont::Bold) {
+ if (!(macStyle & 1)) {
+ synthesisFlags |= SynthesizedBold;
+ atsuBold = true;
+ tags[attributeCount] = kATSUQDBoldfaceTag;
+ sizes[attributeCount] = sizeof(atsuBold);
+ values[attributeCount] = &atsuBold;
+ ++attributeCount;
+ }
+ }
+ if (fontDef.style != QFont::StyleNormal) {
+ if (!(macStyle & 2)) {
+ synthesisFlags |= SynthesizedItalic;
+ atsuItalic = true;
+ tags[attributeCount] = kATSUQDItalicTag;
+ sizes[attributeCount] = sizeof(atsuItalic);
+ values[attributeCount] = &atsuItalic;
+ ++attributeCount;
+ }
+ }
+
+ tags[attributeCount] = kATSUFontTag;
+ values[attributeCount] = &fontID;
+ sizes[attributeCount] = sizeof(fontID);
+ ++attributeCount;
+
+ Q_ASSERT(attributeCount < maxAttributeCount + 1);
+ OSStatus err = ATSUSetAttributes(style, attributeCount, tags, sizes, values);
+ Q_ASSERT(err == noErr);
+ Q_UNUSED(err);
+
+ // CTFontCopyTable
+ quint16 tmpFsType;
+ if (ATSFontGetTable(atsFont, MAKE_TAG('O', 'S', '/', '2'), 8, 2, &tmpFsType, 0) == noErr)
+ fsType = qFromBigEndian<quint16>(tmpFsType);
+ else
+ fsType = 0;
+
+ if (multiEngine)
+ transform = multiEngine->transform;
+ else
+ transform = CGAffineTransformIdentity;
+
+ ATSUTextMeasurement metric;
+
+ ATSUGetAttribute(style, kATSUAscentTag, sizeof(metric), &metric, 0);
+ m_ascent = FixRound(metric);
+
+ ATSUGetAttribute(style, kATSUDescentTag, sizeof(metric), &metric, 0);
+ m_descent = FixRound(metric);
+
+ ATSUGetAttribute(style, kATSULeadingTag, sizeof(metric), &metric, 0);
+ m_leading = FixRound(metric);
+
+ ATSFontMetrics metrics;
+
+ ATSFontGetHorizontalMetrics(FMGetATSFontRefFromFont(fontID), kATSOptionFlagsDefault, &metrics);
+ m_maxCharWidth = metrics.maxAdvanceWidth * fontDef.pointSize;
+
+ ATSFontGetHorizontalMetrics(FMGetATSFontRefFromFont(fontID), kATSOptionFlagsDefault, &metrics);
+ m_xHeight = QFixed::fromReal(metrics.xHeight * fontDef.pointSize);
+
+ ATSFontGetHorizontalMetrics(FMGetATSFontRefFromFont(fontID), kATSOptionFlagsDefault, &metrics);
+ m_averageCharWidth = QFixed::fromReal(metrics.avgAdvanceWidth * fontDef.pointSize);
+
+ // Use width of 'X' if ATSFontGetHorizontalMetrics returns 0 for avgAdvanceWidth.
+ if (m_averageCharWidth == QFixed(0)) {
+ QChar c('X');
+ QGlyphLayoutArray<1> glyphs;
+ int nglyphs = 1;
+ stringToCMap(&c, 1, &glyphs, &nglyphs, 0);
+ glyph_metrics_t metrics = boundingBox(glyphs);
+ m_averageCharWidth = metrics.width;
+ }
+}
+
+QFontEngineMac::~QFontEngineMac()
+{
+ ATSUDisposeStyle(style);
+}
+
+static inline unsigned int getChar(const QChar *str, int &i, const int len)
+{
+ unsigned int uc = str[i].unicode();
+ if (uc >= 0xd800 && uc < 0xdc00 && i < len-1) {
+ uint low = str[i+1].unicode();
+ if (low >= 0xdc00 && low < 0xe000) {
+ uc = (uc - 0xd800)*0x400 + (low - 0xdc00) + 0x10000;
+ ++i;
+ }
+ }
+ return uc;
+}
+
+bool QFontEngineMac::stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs, int *nglyphs, QTextEngine::ShaperFlags flags) const
+{
+ if (!cmap) {
+ cmapTable = getSfntTable(MAKE_TAG('c', 'm', 'a', 'p'));
+ int size = 0;
+ cmap = getCMap(reinterpret_cast<const uchar *>(cmapTable.constData()), cmapTable.size(), &symbolCMap, &size);
+ if (!cmap)
+ return false;
+ }
+ if (symbolCMap) {
+ for (int i = 0; i < len; ++i) {
+ unsigned int uc = getChar(str, i, len);
+ glyphs->glyphs[i] = getTrueTypeGlyphIndex(cmap, uc);
+ if(!glyphs->glyphs[i] && uc < 0x100)
+ glyphs->glyphs[i] = getTrueTypeGlyphIndex(cmap, uc + 0xf000);
+ }
+ } else {
+ for (int i = 0; i < len; ++i) {
+ unsigned int uc = getChar(str, i, len);
+ glyphs->glyphs[i] = getTrueTypeGlyphIndex(cmap, uc);
+ }
+ }
+
+ *nglyphs = len;
+ glyphs->numGlyphs = *nglyphs;
+
+ if (!(flags & QTextEngine::GlyphIndicesOnly))
+ recalcAdvances(glyphs, flags);
+
+ return true;
+}
+
+void QFontEngineMac::recalcAdvances(QGlyphLayout *glyphs, QTextEngine::ShaperFlags flags) const
+{
+ Q_UNUSED(flags)
+
+ QVarLengthArray<GlyphID> atsuGlyphs(glyphs->numGlyphs);
+ for (int i = 0; i < glyphs->numGlyphs; ++i)
+ atsuGlyphs[i] = glyphs->glyphs[i];
+
+ QVarLengthArray<ATSGlyphScreenMetrics> metrics(glyphs->numGlyphs);
+
+ ATSUGlyphGetScreenMetrics(style, glyphs->numGlyphs, atsuGlyphs.data(), sizeof(GlyphID),
+ /* iForcingAntiAlias =*/ false,
+ /* iAntiAliasSwitch =*/true,
+ metrics.data());
+
+ for (int i = 0; i < glyphs->numGlyphs; ++i) {
+ glyphs->advances_x[i] = QFixed::fromReal(metrics[i].deviceAdvance.x);
+ glyphs->advances_y[i] = QFixed::fromReal(metrics[i].deviceAdvance.y);
+ }
+}
+
+glyph_metrics_t QFontEngineMac::boundingBox(const QGlyphLayout &glyphs)
+{
+ QFixed w;
+ for (int i = 0; i < glyphs.numGlyphs; ++i)
+ w += glyphs.effectiveAdvance(i);
+ return glyph_metrics_t(0, -(ascent()), w, ascent()+descent(), w, 0);
+}
+
+glyph_metrics_t QFontEngineMac::boundingBox(glyph_t glyph)
+{
+ GlyphID atsuGlyph = glyph;
+
+ ATSGlyphScreenMetrics metrics;
+
+ ATSUGlyphGetScreenMetrics(style, 1, &atsuGlyph, 0,
+ /* iForcingAntiAlias =*/ false,
+ /* iAntiAliasSwitch =*/true,
+ &metrics);
+
+ // ### check again
+
+ glyph_metrics_t gm;
+ gm.width = int(metrics.width);
+ gm.height = int(metrics.height);
+ gm.x = QFixed::fromReal(metrics.topLeft.x);
+ gm.y = -QFixed::fromReal(metrics.topLeft.y);
+ gm.xoff = QFixed::fromReal(metrics.deviceAdvance.x);
+ gm.yoff = QFixed::fromReal(metrics.deviceAdvance.y);
+
+ return gm;
+}
+
+QFixed QFontEngineMac::ascent() const
+{
+ return m_ascent;
+}
+
+QFixed QFontEngineMac::descent() const
+{
+ return m_descent;
+}
+
+QFixed QFontEngineMac::leading() const
+{
+ return m_leading;
+}
+
+qreal QFontEngineMac::maxCharWidth() const
+{
+ return m_maxCharWidth;
+}
+
+QFixed QFontEngineMac::xHeight() const
+{
+ return m_xHeight;
+}
+
+QFixed QFontEngineMac::averageCharWidth() const
+{
+ return m_averageCharWidth;
+}
+
+static void addGlyphsToPathHelper(ATSUStyle style, glyph_t *glyphs, QFixedPoint *positions, int numGlyphs, QPainterPath *path)
+{
+ if (!numGlyphs)
+ return;
+
+ OSStatus e;
+
+ QMacFontPath fontpath(0, 0, path);
+ ATSCubicMoveToUPP moveTo = NewATSCubicMoveToUPP(QMacFontPath::moveTo);
+ ATSCubicLineToUPP lineTo = NewATSCubicLineToUPP(QMacFontPath::lineTo);
+ ATSCubicCurveToUPP cubicTo = NewATSCubicCurveToUPP(QMacFontPath::cubicTo);
+ ATSCubicClosePathUPP closePath = NewATSCubicClosePathUPP(QMacFontPath::closePath);
+
+ // CTFontCreatePathForGlyph
+ for (int i = 0; i < numGlyphs; ++i) {
+ GlyphID glyph = glyphs[i];
+
+ fontpath.setPosition(positions[i].x.toReal(), positions[i].y.toReal());
+ ATSUGlyphGetCubicPaths(style, glyph, moveTo, lineTo,
+ cubicTo, closePath, &fontpath, &e);
+ }
+
+ DisposeATSCubicMoveToUPP(moveTo);
+ DisposeATSCubicLineToUPP(lineTo);
+ DisposeATSCubicCurveToUPP(cubicTo);
+ DisposeATSCubicClosePathUPP(closePath);
+}
+
+void QFontEngineMac::addGlyphsToPath(glyph_t *glyphs, QFixedPoint *positions, int numGlyphs, QPainterPath *path,
+ QTextItem::RenderFlags)
+{
+ addGlyphsToPathHelper(style, glyphs, positions, numGlyphs, path);
+}
+
+QImage QFontEngineMac::alphaMapForGlyph(glyph_t glyph)
+{
+ const glyph_metrics_t br = boundingBox(glyph);
+ QImage im(qRound(br.width)+2, qRound(br.height)+4, QImage::Format_RGB32);
+ im.fill(0);
+
+ CGColorSpaceRef colorspace = QCoreGraphicsPaintEngine::macGenericColorSpace();
+#if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4)
+ uint cgflags = kCGImageAlphaNoneSkipFirst;
+#ifdef kCGBitmapByteOrder32Host //only needed because CGImage.h added symbols in the minor version
+ if(QSysInfo::MacintoshVersion >= QSysInfo::MV_10_4)
+ cgflags |= kCGBitmapByteOrder32Host;
+#endif
+#else
+ CGImageAlphaInfo cgflags = kCGImageAlphaNoneSkipFirst;
+#endif
+ CGContextRef ctx = CGBitmapContextCreate(im.bits(), im.width(), im.height(),
+ 8, im.bytesPerLine(), colorspace,
+ cgflags);
+ CGContextSetFontSize(ctx, fontDef.pixelSize);
+ CGContextSetShouldAntialias(ctx, fontDef.pointSize > qt_antialiasing_threshold && !(fontDef.styleStrategy & QFont::NoAntialias));
+ // turn off sub-pixel hinting - no support for that in OpenGL
+ CGContextSetShouldSmoothFonts(ctx, false);
+ CGAffineTransform oldTextMatrix = CGContextGetTextMatrix(ctx);
+ CGAffineTransform cgMatrix = CGAffineTransformMake(1, 0, 0, 1, 0, 0);
+ CGAffineTransformConcat(cgMatrix, oldTextMatrix);
+
+ if (synthesisFlags & QFontEngine::SynthesizedItalic)
+ cgMatrix = CGAffineTransformConcat(cgMatrix, CGAffineTransformMake(1, 0, tanf(14 * acosf(0) / 90), 1, 0, 0));
+
+ cgMatrix = CGAffineTransformConcat(cgMatrix, transform);
+
+ CGContextSetTextMatrix(ctx, cgMatrix);
+ CGContextSetRGBFillColor(ctx, 1, 1, 1, 1);
+ CGContextSetTextDrawingMode(ctx, kCGTextFill);
+ CGContextSetFont(ctx, cgFont);
+
+ qreal pos_x = -br.x.toReal() + 1;
+ qreal pos_y = im.height() + br.y.toReal() - 2;
+ CGContextSetTextPosition(ctx, pos_x, pos_y);
+
+ CGSize advance;
+ advance.width = 0;
+ advance.height = 0;
+ CGGlyph cgGlyph = glyph;
+ CGContextShowGlyphsWithAdvances(ctx, &cgGlyph, &advance, 1);
+
+ if (synthesisFlags & QFontEngine::SynthesizedBold) {
+ CGContextSetTextPosition(ctx, pos_x + 0.5 * lineThickness().toReal(), pos_y);
+ CGContextShowGlyphsWithAdvances(ctx, &cgGlyph, &advance, 1);
+ }
+
+ CGContextRelease(ctx);
+
+ QImage indexed(im.width(), im.height(), QImage::Format_Indexed8);
+ QVector<QRgb> colors(256);
+ for (int i=0; i<256; ++i)
+ colors[i] = qRgba(0, 0, 0, i);
+ indexed.setColorTable(colors);
+
+ for (int y=0; y<im.height(); ++y) {
+ uint *src = (uint*) im.scanLine(y);
+ uchar *dst = indexed.scanLine(y);
+ for (int x=0; x<im.width(); ++x) {
+ *dst = qGray(*src);
+ ++dst;
+ ++src;
+ }
+ }
+
+ return indexed;
+}
+
+bool QFontEngineMac::canRender(const QChar *string, int len)
+{
+ Q_ASSERT(false);
+ Q_UNUSED(string);
+ Q_UNUSED(len);
+ return false;
+}
+
+void QFontEngineMac::draw(CGContextRef ctx, qreal x, qreal y, const QTextItemInt &ti, int paintDeviceHeight)
+{
+ QVarLengthArray<QFixedPoint> positions;
+ QVarLengthArray<glyph_t> glyphs;
+ QTransform matrix;
+ matrix.translate(x, y);
+ getGlyphPositions(ti.glyphs, matrix, ti.flags, glyphs, positions);
+ if (glyphs.size() == 0)
+ return;
+
+ CGContextSetFontSize(ctx, fontDef.pixelSize);
+
+ CGAffineTransform oldTextMatrix = CGContextGetTextMatrix(ctx);
+
+ CGAffineTransform cgMatrix = CGAffineTransformMake(1, 0, 0, -1, 0, -paintDeviceHeight);
+
+ CGAffineTransformConcat(cgMatrix, oldTextMatrix);
+
+ if (synthesisFlags & QFontEngine::SynthesizedItalic)
+ cgMatrix = CGAffineTransformConcat(cgMatrix, CGAffineTransformMake(1, 0, -tanf(14 * acosf(0) / 90), 1, 0, 0));
+
+ cgMatrix = CGAffineTransformConcat(cgMatrix, transform);
+
+ CGContextSetTextMatrix(ctx, cgMatrix);
+
+ CGContextSetTextDrawingMode(ctx, kCGTextFill);
+
+
+ QVarLengthArray<CGSize> advances(glyphs.size());
+ QVarLengthArray<CGGlyph> cgGlyphs(glyphs.size());
+
+ for (int i = 0; i < glyphs.size() - 1; ++i) {
+ advances[i].width = (positions[i + 1].x - positions[i].x).toReal();
+ advances[i].height = (positions[i + 1].y - positions[i].y).toReal();
+ cgGlyphs[i] = glyphs[i];
+ }
+ advances[glyphs.size() - 1].width = 0;
+ advances[glyphs.size() - 1].height = 0;
+ cgGlyphs[glyphs.size() - 1] = glyphs[glyphs.size() - 1];
+
+ CGContextSetFont(ctx, cgFont);
+
+ CGContextSetTextPosition(ctx, positions[0].x.toReal(), positions[0].y.toReal());
+
+ CGContextShowGlyphsWithAdvances(ctx, cgGlyphs.data(), advances.data(), glyphs.size());
+
+ if (synthesisFlags & QFontEngine::SynthesizedBold) {
+ CGContextSetTextPosition(ctx, positions[0].x.toReal() + 0.5 * lineThickness().toReal(),
+ positions[0].y.toReal());
+
+ CGContextShowGlyphsWithAdvances(ctx, cgGlyphs.data(), advances.data(), glyphs.size());
+ }
+
+ CGContextSetTextMatrix(ctx, oldTextMatrix);
+}
+
+QFontEngine::FaceId QFontEngineMac::faceId() const
+{
+ FaceId ret;
+#if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5)
+ // CTFontGetPlatformFont
+ FSRef ref;
+ if (ATSFontGetFileReference(FMGetATSFontRefFromFont(fontID), &ref) != noErr)
+ return ret;
+ ret.filename = QByteArray(128, 0);
+ ret.index = fontID;
+ FSRefMakePath(&ref, (UInt8 *)ret.filename.data(), ret.filename.size());
+#else
+ FSSpec spec;
+ if (ATSFontGetFileSpecification(FMGetATSFontRefFromFont(fontID), &spec) != noErr)
+ return ret;
+
+ FSRef ref;
+ FSpMakeFSRef(&spec, &ref);
+ ret.filename = QByteArray(128, 0);
+ ret.index = fontID;
+ FSRefMakePath(&ref, (UInt8 *)ret.filename.data(), ret.filename.size());
+#endif
+ return ret;
+}
+
+QByteArray QFontEngineMac::getSfntTable(uint tag) const
+{
+ ATSFontRef atsFont = FMGetATSFontRefFromFont(fontID);
+
+ ByteCount length;
+ OSStatus status = ATSFontGetTable(atsFont, tag, 0, 0, 0, &length);
+ if (status != noErr)
+ return QByteArray();
+ QByteArray table(length, 0);
+ // CTFontCopyTable
+ status = ATSFontGetTable(atsFont, tag, 0, table.length(), table.data(), &length);
+ if (status != noErr)
+ return QByteArray();
+ return table;
+}
+
+QFontEngine::Properties QFontEngineMac::properties() const
+{
+ QFontEngine::Properties props;
+ ATSFontRef atsFont = FMGetATSFontRefFromFont(fontID);
+ quint16 tmp;
+ // CTFontGetUnitsPerEm
+ if (ATSFontGetTable(atsFont, MAKE_TAG('h', 'e', 'a', 'd'), 18, 2, &tmp, 0) == noErr)
+ props.emSquare = qFromBigEndian<quint16>(tmp);
+ struct {
+ qint16 xMin;
+ qint16 yMin;
+ qint16 xMax;
+ qint16 yMax;
+ } bbox;
+ bbox.xMin = bbox.xMax = bbox.yMin = bbox.yMax = 0;
+ // CTFontGetBoundingBox
+ if (ATSFontGetTable(atsFont, MAKE_TAG('h', 'e', 'a', 'd'), 36, 8, &bbox, 0) == noErr) {
+ bbox.xMin = qFromBigEndian<quint16>(bbox.xMin);
+ bbox.yMin = qFromBigEndian<quint16>(bbox.yMin);
+ bbox.xMax = qFromBigEndian<quint16>(bbox.xMax);
+ bbox.yMax = qFromBigEndian<quint16>(bbox.yMax);
+ }
+ struct {
+ qint16 ascender;
+ qint16 descender;
+ qint16 linegap;
+ } metrics;
+ metrics.ascender = metrics.descender = metrics.linegap = 0;
+ // CTFontGetAscent, etc.
+ if (ATSFontGetTable(atsFont, MAKE_TAG('h', 'h', 'e', 'a'), 4, 6, &metrics, 0) == noErr) {
+ metrics.ascender = qFromBigEndian<quint16>(metrics.ascender);
+ metrics.descender = qFromBigEndian<quint16>(metrics.descender);
+ metrics.linegap = qFromBigEndian<quint16>(metrics.linegap);
+ }
+ props.ascent = metrics.ascender;
+ props.descent = -metrics.descender;
+ props.leading = metrics.linegap;
+ props.boundingBox = QRectF(bbox.xMin, -bbox.yMax,
+ bbox.xMax - bbox.xMin,
+ bbox.yMax - bbox.yMin);
+ props.italicAngle = 0;
+ props.capHeight = props.ascent;
+
+ qint16 lw = 0;
+ // fonts lie
+ if (ATSFontGetTable(atsFont, MAKE_TAG('p', 'o', 's', 't'), 10, 2, &lw, 0) == noErr)
+ lw = qFromBigEndian<quint16>(lw);
+ props.lineWidth = lw;
+
+ // CTFontCopyPostScriptName
+ QCFString psName;
+ if (ATSFontGetPostScriptName(FMGetATSFontRefFromFont(fontID), kATSOptionFlagsDefault, &psName) == noErr)
+ props.postscriptName = QString(psName).toUtf8();
+ props.postscriptName = QPdf::stripSpecialCharacters(props.postscriptName);
+ return props;
+}
+
+void QFontEngineMac::getUnscaledGlyph(glyph_t glyph, QPainterPath *path, glyph_metrics_t *metrics)
+{
+ ATSUStyle unscaledStyle;
+ ATSUCreateAndCopyStyle(style, &unscaledStyle);
+
+ int emSquare = properties().emSquare.toInt();
+
+ const int maxAttributeCount = 4;
+ ATSUAttributeTag tags[maxAttributeCount + 1];
+ ByteCount sizes[maxAttributeCount + 1];
+ ATSUAttributeValuePtr values[maxAttributeCount + 1];
+ int attributeCount = 0;
+
+ Fixed size = FixRatio(emSquare, 1);
+ tags[attributeCount] = kATSUSizeTag;
+ sizes[attributeCount] = sizeof(size);
+ values[attributeCount] = &size;
+ ++attributeCount;
+
+ Q_ASSERT(attributeCount < maxAttributeCount + 1);
+ OSStatus err = ATSUSetAttributes(unscaledStyle, attributeCount, tags, sizes, values);
+ Q_ASSERT(err == noErr);
+ Q_UNUSED(err);
+
+ // various CTFont metrics functions: CTFontGetBoundingRectsForGlyphs, CTFontGetAdvancesForGlyphs
+ GlyphID atsuGlyph = glyph;
+ ATSGlyphScreenMetrics atsuMetrics;
+ ATSUGlyphGetScreenMetrics(unscaledStyle, 1, &atsuGlyph, 0,
+ /* iForcingAntiAlias =*/ false,
+ /* iAntiAliasSwitch =*/true,
+ &atsuMetrics);
+
+ metrics->width = int(atsuMetrics.width);
+ metrics->height = int(atsuMetrics.height);
+ metrics->x = QFixed::fromReal(atsuMetrics.topLeft.x);
+ metrics->y = -QFixed::fromReal(atsuMetrics.topLeft.y);
+ metrics->xoff = QFixed::fromReal(atsuMetrics.deviceAdvance.x);
+ metrics->yoff = QFixed::fromReal(atsuMetrics.deviceAdvance.y);
+
+ QFixedPoint p;
+ addGlyphsToPathHelper(unscaledStyle, &glyph, &p, 1, path);
+
+ ATSUDisposeStyle(unscaledStyle);
+}
+#endif // !QT_MAC_USE_COCOA
+
+QT_END_NAMESPACE
diff --git a/src/gui/text/qfontengine_p.h b/src/gui/text/qfontengine_p.h
new file mode 100644
index 0000000000..e289aa9174
--- /dev/null
+++ b/src/gui/text/qfontengine_p.h
@@ -0,0 +1,617 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QFONTENGINE_P_H
+#define QFONTENGINE_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 "QtCore/qglobal.h"
+#include "QtCore/qatomic.h"
+#include <QtCore/qvarlengtharray.h>
+#include "private/qtextengine_p.h"
+#include "private/qfont_p.h"
+
+#ifdef Q_WS_WIN
+# include "QtCore/qt_windows.h"
+#endif
+
+#ifdef Q_WS_MAC
+# include "private/qt_mac_p.h"
+# include "QtCore/qmap.h"
+# include "QtCore/qcache.h"
+# include "private/qcore_mac_p.h"
+#endif
+
+#include "qfontengineglyphcache_p.h"
+
+struct glyph_metrics_t;
+typedef unsigned int glyph_t;
+
+QT_BEGIN_NAMESPACE
+
+class QChar;
+class QPainterPath;
+
+class QTextEngine;
+struct QGlyphLayout;
+
+#define MAKE_TAG(ch1, ch2, ch3, ch4) (\
+ (((quint32)(ch1)) << 24) | \
+ (((quint32)(ch2)) << 16) | \
+ (((quint32)(ch3)) << 8) | \
+ ((quint32)(ch4)) \
+ )
+
+
+class Q_GUI_EXPORT QFontEngine : public QObject
+{
+ Q_OBJECT
+public:
+ enum Type {
+ Box,
+ Multi,
+
+ // X11 types
+ XLFD,
+
+ // MS Windows types
+ Win,
+
+ // Apple Mac OS types
+ Mac,
+
+ // Trolltech QWS types
+ Freetype,
+ QPF1,
+ QPF2,
+ Proxy,
+ TestFontEngine = 0x1000
+ };
+
+ QFontEngine();
+ virtual ~QFontEngine();
+
+ // all of these are in unscaled metrics if the engine supports uncsaled metrics,
+ // otherwise in design metrics
+ struct Properties {
+ QByteArray postscriptName;
+ QByteArray copyright;
+ QRectF boundingBox;
+ QFixed emSquare;
+ QFixed ascent;
+ QFixed descent;
+ QFixed leading;
+ QFixed italicAngle;
+ QFixed capHeight;
+ QFixed lineWidth;
+ };
+ virtual Properties properties() const;
+ virtual void getUnscaledGlyph(glyph_t glyph, QPainterPath *path, glyph_metrics_t *metrics);
+ QByteArray getSfntTable(uint /*tag*/) const;
+ virtual bool getSfntTableData(uint /*tag*/, uchar * /*buffer*/, uint * /*length*/) const { return false; }
+
+ struct FaceId {
+ FaceId() : index(0), encoding(0) {}
+ QByteArray filename;
+ int index;
+ int encoding;
+ };
+ virtual FaceId faceId() const { return FaceId(); }
+ enum SynthesizedFlags {
+ SynthesizedItalic = 0x1,
+ SynthesizedBold = 0x2,
+ SynthesizedStretch = 0x4
+ };
+ virtual int synthesized() const { return 0; }
+
+ virtual QFixed emSquareSize() const { return ascent(); }
+
+ /* returns 0 as glyph index for non existant glyphs */
+ virtual bool stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs, int *nglyphs, QTextEngine::ShaperFlags flags) const = 0;
+
+ /**
+ * This is a callback from harfbuzz. The font engine uses the font-system in use to find out the
+ * advances of each glyph and set it on the layout.
+ */
+ virtual void recalcAdvances(QGlyphLayout *, QTextEngine::ShaperFlags) const {}
+ virtual void doKerning(QGlyphLayout *, QTextEngine::ShaperFlags) const;
+
+#if !defined(Q_WS_X11) && !defined(Q_WS_WIN) && !defined(Q_WS_MAC)
+ virtual void draw(QPaintEngine *p, qreal x, qreal y, const QTextItemInt &si) = 0;
+#endif
+ virtual void addGlyphsToPath(glyph_t *glyphs, QFixedPoint *positions, int nglyphs,
+ QPainterPath *path, QTextItem::RenderFlags flags);
+ void getGlyphPositions(const QGlyphLayout &glyphs, const QTransform &matrix, QTextItem::RenderFlags flags,
+ QVarLengthArray<glyph_t> &glyphs_out, QVarLengthArray<QFixedPoint> &positions);
+
+ virtual void addOutlineToPath(qreal, qreal, const QGlyphLayout &, QPainterPath *, QTextItem::RenderFlags flags);
+ void addBitmapFontToPath(qreal x, qreal y, const QGlyphLayout &, QPainterPath *, QTextItem::RenderFlags);
+ /**
+ * Create a qimage with the alpha values for the glyph.
+ * Returns an image indexed_8 with index values ranging from 0=fully transparant to 255=opaque
+ */
+ virtual QImage alphaMapForGlyph(glyph_t) = 0;
+ virtual QImage alphaMapForGlyph(glyph_t, const QTransform &t);
+ virtual QImage alphaRGBMapForGlyph(glyph_t, int margin, const QTransform &t);
+
+ virtual void removeGlyphFromCache(glyph_t);
+
+ virtual glyph_metrics_t boundingBox(const QGlyphLayout &glyphs) = 0;
+ virtual glyph_metrics_t boundingBox(glyph_t glyph) = 0;
+ virtual glyph_metrics_t boundingBox(glyph_t glyph, const QTransform &matrix);
+ glyph_metrics_t tightBoundingBox(const QGlyphLayout &glyphs);
+
+ virtual QFixed ascent() const = 0;
+ virtual QFixed descent() const = 0;
+ virtual QFixed leading() const = 0;
+ virtual QFixed xHeight() const;
+ virtual QFixed averageCharWidth() const;
+
+ virtual QFixed lineThickness() const;
+ virtual QFixed underlinePosition() const;
+
+ virtual qreal maxCharWidth() const = 0;
+ virtual qreal minLeftBearing() const { return qreal(); }
+ virtual qreal minRightBearing() const { return qreal(); }
+
+ virtual const char *name() const = 0;
+
+ virtual bool canRender(const QChar *string, int len) = 0;
+
+ virtual Type type() const = 0;
+
+ virtual int glyphCount() const;
+
+ HB_Font harfbuzzFont() const;
+ HB_Face harfbuzzFace() const;
+
+ virtual HB_Error getPointInOutline(HB_Glyph glyph, int flags, hb_uint32 point, HB_Fixed *xpos, HB_Fixed *ypos, hb_uint32 *nPoints);
+
+ void setGlyphCache(void *key, QFontEngineGlyphCache *data);
+ void setGlyphCache(QFontEngineGlyphCache::Type key, QFontEngineGlyphCache *data);
+ QFontEngineGlyphCache *glyphCache(void *key, const QTransform &transform) const;
+ QFontEngineGlyphCache *glyphCache(QFontEngineGlyphCache::Type key, const QTransform &transform) const;
+
+ static const uchar *getCMap(const uchar *table, uint tableSize, bool *isSymbolFont, int *cmapSize);
+ static quint32 getTrueTypeGlyphIndex(const uchar *cmap, uint unicode);
+
+ QAtomicInt ref;
+ QFontDef fontDef;
+ uint cache_cost; // amount of mem used in kb by the font
+ int cache_count;
+ uint fsType : 16;
+ bool symbol;
+ mutable HB_FontRec hbFont;
+ mutable HB_Face hbFace;
+#if defined(Q_WS_WIN) || defined(Q_WS_X11) || defined(Q_WS_QWS)
+ struct KernPair {
+ uint left_right;
+ QFixed adjust;
+
+ inline bool operator<(const KernPair &other) const
+ {
+ return left_right < other.left_right;
+ }
+ };
+ QVector<KernPair> kerning_pairs;
+ void loadKerningPairs(QFixed scalingFactor);
+#endif
+
+ int glyphFormat;
+
+private:
+ /// remove old entries from the glyph cache. Helper method for the setGlyphCache ones.
+ void expireGlyphCache();
+
+ GlyphPointerHash m_glyphPointerHash;
+ GlyphIntHash m_glyphIntHash;
+ mutable QList<QFontEngineGlyphCache*> m_glyphCacheQueue;
+};
+
+inline bool operator ==(const QFontEngine::FaceId &f1, const QFontEngine::FaceId &f2)
+{
+ return (f1.index == f2.index) && (f1.encoding == f2.encoding) && (f1.filename == f2.filename);
+}
+
+inline uint qHash(const QFontEngine::FaceId &f)
+{
+ return qHash((f.index << 16) + f.encoding) + qHash(f.filename);
+}
+
+
+class QGlyph;
+
+#if defined(Q_WS_QWS)
+
+#ifndef QT_NO_QWS_QPF
+
+class QFontEngineQPF1Data;
+
+class QFontEngineQPF1 : public QFontEngine
+{
+public:
+ QFontEngineQPF1(const QFontDef&, const QString &fn);
+ ~QFontEngineQPF1();
+
+ virtual bool stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs, int *nglyphs, QTextEngine::ShaperFlags flags) const;
+ virtual void recalcAdvances(QGlyphLayout *, QTextEngine::ShaperFlags) const;
+
+ virtual void draw(QPaintEngine *p, qreal x, qreal y, const QTextItemInt &si);
+ virtual void addOutlineToPath(qreal x, qreal y, const QGlyphLayout &glyphs, QPainterPath *path, QTextItem::RenderFlags flags);
+
+ virtual glyph_metrics_t boundingBox(const QGlyphLayout &glyphs);
+ virtual glyph_metrics_t boundingBox(glyph_t glyph);
+
+ virtual QFixed ascent() const;
+ virtual QFixed descent() const;
+ virtual QFixed leading() const;
+ virtual qreal maxCharWidth() const;
+ virtual qreal minLeftBearing() const;
+ virtual qreal minRightBearing() const;
+ virtual QFixed underlinePosition() const;
+ virtual QFixed lineThickness() const;
+
+ virtual Type type() const;
+
+ virtual bool canRender(const QChar *string, int len);
+ inline const char *name() const { return 0; }
+ virtual QImage alphaMapForGlyph(glyph_t);
+
+
+ QFontEngineQPF1Data *d;
+};
+#endif // QT_NO_QWS_QPF
+
+#endif // QWS
+
+
+class QFontEngineBox : public QFontEngine
+{
+public:
+ QFontEngineBox(int size);
+ ~QFontEngineBox();
+
+ virtual bool stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs, int *nglyphs, QTextEngine::ShaperFlags flags) const;
+ virtual void recalcAdvances(QGlyphLayout *, QTextEngine::ShaperFlags) const;
+
+#if !defined(Q_WS_X11) && !defined(Q_WS_WIN) && !defined(Q_WS_MAC)
+ void draw(QPaintEngine *p, qreal x, qreal y, const QTextItemInt &si);
+#endif
+ virtual void addOutlineToPath(qreal x, qreal y, const QGlyphLayout &glyphs, QPainterPath *path, QTextItem::RenderFlags flags);
+
+ virtual glyph_metrics_t boundingBox(const QGlyphLayout &glyphs);
+ virtual glyph_metrics_t boundingBox(glyph_t glyph);
+
+ virtual QFixed ascent() const;
+ virtual QFixed descent() const;
+ virtual QFixed leading() const;
+ virtual qreal maxCharWidth() const;
+ virtual qreal minLeftBearing() const { return 0; }
+ virtual qreal minRightBearing() const { return 0; }
+ virtual QImage alphaMapForGlyph(glyph_t);
+
+#ifdef Q_WS_X11
+ int cmap() const;
+#endif
+ virtual const char *name() const;
+
+ virtual bool canRender(const QChar *string, int len);
+
+ virtual Type type() const;
+ inline int size() const { return _size; }
+
+private:
+ friend class QFontPrivate;
+ int _size;
+};
+
+class Q_GUI_EXPORT QFontEngineMulti : public QFontEngine
+{
+public:
+ explicit QFontEngineMulti(int engineCount);
+ ~QFontEngineMulti();
+
+ virtual bool stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs, int *nglyphs,
+ QTextEngine::ShaperFlags flags) const;
+
+ virtual glyph_metrics_t boundingBox(const QGlyphLayout &glyphs);
+ virtual glyph_metrics_t boundingBox(glyph_t glyph);
+
+ virtual void recalcAdvances(QGlyphLayout *, QTextEngine::ShaperFlags) const;
+ virtual void doKerning(QGlyphLayout *, QTextEngine::ShaperFlags) const;
+ virtual void addOutlineToPath(qreal, qreal, const QGlyphLayout &, QPainterPath *, QTextItem::RenderFlags flags);
+
+ virtual QFixed ascent() const;
+ virtual QFixed descent() const;
+ virtual QFixed leading() const;
+ virtual QFixed xHeight() const;
+ virtual QFixed averageCharWidth() const;
+ virtual QImage alphaMapForGlyph(glyph_t);
+
+ virtual QFixed lineThickness() const;
+ virtual QFixed underlinePosition() const;
+ virtual qreal maxCharWidth() const;
+ virtual qreal minLeftBearing() const;
+ virtual qreal minRightBearing() const;
+
+ virtual inline Type type() const
+ { return QFontEngine::Multi; }
+
+ virtual bool canRender(const QChar *string, int len);
+ inline virtual const char *name() const
+ { return "Multi"; }
+
+ QFontEngine *engine(int at) const;
+
+protected:
+ friend class QPSPrintEnginePrivate;
+ friend class QPSPrintEngineFontMulti;
+ virtual void loadEngine(int at) = 0;
+ QVector<QFontEngine *> engines;
+};
+
+#if defined(Q_WS_MAC)
+
+struct QCharAttributes;
+class QFontEngineMacMulti;
+# if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5
+class QCoreTextFontEngineMulti;
+class QCoreTextFontEngine : public QFontEngine
+{
+public:
+ QCoreTextFontEngine(CTFontRef font, const QFontDef &def,
+ QCoreTextFontEngineMulti *multiEngine = 0);
+ ~QCoreTextFontEngine();
+ virtual bool stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs, int *nglyphs, QTextEngine::ShaperFlags flags) const;
+ virtual void recalcAdvances(int , QGlyphLayout *, QTextEngine::ShaperFlags) const;
+
+ virtual glyph_metrics_t boundingBox(const QGlyphLayout &glyphs);
+ virtual glyph_metrics_t boundingBox(glyph_t glyph);
+
+ virtual QFixed ascent() const;
+ virtual QFixed descent() const;
+ virtual QFixed leading() const;
+ virtual QFixed xHeight() const;
+ virtual qreal maxCharWidth() const;
+ virtual QFixed averageCharWidth() const;
+
+ virtual void addGlyphsToPath(glyph_t *glyphs, QFixedPoint *positions, int numGlyphs,
+ QPainterPath *path, QTextItem::RenderFlags);
+
+ virtual const char *name() const { return "QCoreTextFontEngine"; }
+
+ virtual bool canRender(const QChar *string, int len);
+
+ virtual int synthesized() const { return synthesisFlags; }
+
+ virtual Type type() const { return QFontEngine::Mac; }
+
+ void draw(CGContextRef ctx, qreal x, qreal y, const QTextItemInt &ti, int paintDeviceHeight);
+
+ virtual FaceId faceId() const;
+ virtual bool getSfntTableData(uint /*tag*/, uchar * /*buffer*/, uint * /*length*/) const;
+ virtual void getUnscaledGlyph(glyph_t glyph, QPainterPath *path, glyph_metrics_t *metrics);
+ virtual QImage alphaMapForGlyph(glyph_t);
+ virtual qreal minRightBearing() const;
+ virtual qreal minLeftBearing() const;
+
+
+
+private:
+ CTFontRef ctfont;
+ CGFontRef cgFont;
+ QCoreTextFontEngineMulti *parentEngine;
+ int synthesisFlags;
+ friend class QCoreTextFontEngineMulti;
+};
+
+class QCoreTextFontEngineMulti : public QFontEngineMulti
+{
+public:
+ QCoreTextFontEngineMulti(const ATSFontFamilyRef &atsFamily, const ATSFontRef &atsFontRef,
+ const QFontDef &fontDef, bool kerning);
+ ~QCoreTextFontEngineMulti();
+
+ virtual bool stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs, int *nglyphs,
+ QTextEngine::ShaperFlags flags) const;
+ bool stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs, int *nglyphs,
+ QTextEngine::ShaperFlags flags,
+ unsigned short *logClusters, const HB_CharAttributes *charAttributes) const;
+
+
+ virtual void recalcAdvances(int , QGlyphLayout *, QTextEngine::ShaperFlags) const;
+ virtual void doKerning(int , QGlyphLayout *, QTextEngine::ShaperFlags) const;
+
+ virtual const char *name() const { return "CoreText"; }
+protected:
+ virtual void loadEngine(int at);
+
+private:
+ inline const QCoreTextFontEngine *engineAt(int i) const
+ { return static_cast<const QCoreTextFontEngine *>(engines.at(i)); }
+
+ uint fontIndexForFont(CTFontRef id) const;
+ CTFontRef ctfont;
+ mutable QCFType<CFDictionaryRef> attributeDict;
+
+ friend class QFontDialogPrivate;
+};
+# endif //MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5
+
+#ifndef QT_MAC_USE_COCOA
+class QFontEngineMac : public QFontEngine
+{
+ friend class QFontEngineMacMulti;
+public:
+ QFontEngineMac(ATSUStyle baseStyle, ATSUFontID fontID, const QFontDef &def, QFontEngineMacMulti *multiEngine = 0);
+ virtual ~QFontEngineMac();
+
+ virtual bool stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs, int *numGlyphs, QTextEngine::ShaperFlags flags) const;
+ virtual void recalcAdvances(QGlyphLayout *, QTextEngine::ShaperFlags) const;
+
+ virtual glyph_metrics_t boundingBox(const QGlyphLayout &glyphs);
+ virtual glyph_metrics_t boundingBox(glyph_t glyph);
+
+ virtual QFixed ascent() const;
+ virtual QFixed descent() const;
+ virtual QFixed leading() const;
+ virtual QFixed xHeight() const;
+ virtual qreal maxCharWidth() const;
+ virtual QFixed averageCharWidth() const;
+
+ virtual void addGlyphsToPath(glyph_t *glyphs, QFixedPoint *positions, int numGlyphs,
+ QPainterPath *path, QTextItem::RenderFlags);
+
+ virtual const char *name() const { return "QFontEngineMac"; }
+
+ virtual bool canRender(const QChar *string, int len);
+
+ virtual int synthesized() const { return synthesisFlags; }
+
+ virtual Type type() const { return QFontEngine::Mac; }
+
+ void draw(CGContextRef ctx, qreal x, qreal y, const QTextItemInt &ti, int paintDeviceHeight);
+
+ virtual FaceId faceId() const;
+ virtual QByteArray getSfntTable(uint tag) const;
+ virtual Properties properties() const;
+ virtual void getUnscaledGlyph(glyph_t glyph, QPainterPath *path, glyph_metrics_t *metrics);
+ virtual QImage alphaMapForGlyph(glyph_t);
+
+private:
+ ATSUFontID fontID;
+ QCFType<CGFontRef> cgFont;
+ ATSUStyle style;
+ int synthesisFlags;
+ mutable QGlyphLayout kashidaGlyph;
+ QFontEngineMacMulti *multiEngine;
+ mutable const unsigned char *cmap;
+ mutable bool symbolCMap;
+ mutable QByteArray cmapTable;
+ CGAffineTransform transform;
+ QFixed m_ascent;
+ QFixed m_descent;
+ QFixed m_leading;
+ qreal m_maxCharWidth;
+ QFixed m_xHeight;
+ QFixed m_averageCharWidth;
+};
+
+class QFontEngineMacMulti : public QFontEngineMulti
+{
+ friend class QFontEngineMac;
+public:
+ // internal
+ struct ShaperItem
+ {
+ inline ShaperItem() : string(0), from(0), length(0),
+ log_clusters(0), charAttributes(0) {}
+
+ const QChar *string;
+ int from;
+ int length;
+ QGlyphLayout glyphs;
+ unsigned short *log_clusters;
+ const HB_CharAttributes *charAttributes;
+ QTextEngine::ShaperFlags flags;
+ };
+
+ QFontEngineMacMulti(const ATSFontFamilyRef &atsFamily, const ATSFontRef &atsFontRef, const QFontDef &fontDef, bool kerning);
+ virtual ~QFontEngineMacMulti();
+
+ virtual bool stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs, int *nglyphs, QTextEngine::ShaperFlags flags) const;
+ bool stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs, int *nglyphs, QTextEngine::ShaperFlags flags,
+ unsigned short *logClusters, const HB_CharAttributes *charAttributes) const;
+
+ virtual void recalcAdvances(QGlyphLayout *, QTextEngine::ShaperFlags) const;
+ virtual void doKerning(QGlyphLayout *, QTextEngine::ShaperFlags) const;
+
+ virtual const char *name() const { return "ATSUI"; }
+
+ virtual bool canRender(const QChar *string, int len);
+
+ inline ATSUFontID macFontID() const { return fontID; }
+
+protected:
+ virtual void loadEngine(int at);
+
+private:
+ inline const QFontEngineMac *engineAt(int i) const
+ { return static_cast<const QFontEngineMac *>(engines.at(i)); }
+
+ bool stringToCMapInternal(const QChar *str, int len, QGlyphLayout *glyphs, int *nglyphs, QTextEngine::ShaperFlags flags, ShaperItem *item) const;
+
+ int fontIndexForFontID(ATSUFontID id) const;
+
+ ATSUFontID fontID;
+ uint kerning : 1;
+
+ mutable ATSUTextLayout textLayout;
+ mutable ATSUStyle style;
+ CGAffineTransform transform;
+};
+#endif //!QT_MAC_USE_COCOA
+#endif
+
+class QTestFontEngine : public QFontEngineBox
+{
+public:
+ QTestFontEngine(int size) : QFontEngineBox(size) {}
+ virtual Type type() const { return TestFontEngine; }
+};
+
+QT_END_NAMESPACE
+
+#ifdef Q_WS_WIN
+# include "private/qfontengine_win_p.h"
+#endif
+
+#endif // QFONTENGINE_P_H
diff --git a/src/gui/text/qfontengine_qpf.cpp b/src/gui/text/qfontengine_qpf.cpp
new file mode 100644
index 0000000000..e9fcac4cd8
--- /dev/null
+++ b/src/gui/text/qfontengine_qpf.cpp
@@ -0,0 +1,1161 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qfontengine_qpf_p.h"
+
+#include "private/qpaintengine_raster_p.h"
+#include <QtCore/qlibraryinfo.h>
+#include <QtCore/qfileinfo.h>
+
+#include <QtCore/qfile.h>
+#include <QtCore/qdir.h>
+#include <QtCore/qbuffer.h>
+#if !defined(QT_NO_FREETYPE)
+#include "private/qfontengine_ft_p.h"
+#endif
+
+// for mmap
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <fcntl.h>
+#include <errno.h>
+
+QT_BEGIN_NAMESPACE
+
+#ifndef QT_NO_QWS_QPF2
+
+QT_BEGIN_INCLUDE_NAMESPACE
+#include "qpfutil.cpp"
+
+#if defined(Q_WS_QWS)
+# include "private/qwscommand_qws_p.h"
+# include "qwsdisplay_qws.h"
+# include "qabstractfontengine_p.h"
+#endif
+#include "qplatformdefs.h"
+QT_END_INCLUDE_NAMESPACE
+
+//#define DEBUG_HEADER
+//#define DEBUG_FONTENGINE
+
+#if defined(DEBUG_HEADER)
+# define DEBUG_VERIFY qDebug
+#else
+# define DEBUG_VERIFY if (0) qDebug
+#endif
+
+#define READ_VERIFY(type, variable) \
+ if (tagPtr + sizeof(type) > endPtr) { \
+ DEBUG_VERIFY() << "read verify failed in line" << __LINE__; \
+ return 0; \
+ } \
+ variable = qFromBigEndian<type>(tagPtr); \
+ DEBUG_VERIFY() << "read value" << variable << "of type " #type; \
+ tagPtr += sizeof(type)
+
+template <typename T>
+T readValue(const uchar *&data)
+{
+ T value = qFromBigEndian<T>(data);
+ data += sizeof(T);
+ return value;
+}
+
+#define VERIFY(condition) \
+ if (!(condition)) { \
+ DEBUG_VERIFY() << "condition " #condition " failed in line" << __LINE__; \
+ return 0; \
+ }
+
+#define VERIFY_TAG(condition) \
+ if (!(condition)) { \
+ DEBUG_VERIFY() << "verifying tag condition " #condition " failed in line" << __LINE__ << "with tag" << tag; \
+ return 0; \
+ }
+
+static inline const uchar *verifyTag(const uchar *tagPtr, const uchar *endPtr)
+{
+ quint16 tag, length;
+ READ_VERIFY(quint16, tag);
+ READ_VERIFY(quint16, length);
+ if (tag == QFontEngineQPF::Tag_EndOfHeader)
+ return endPtr;
+ if (tag < QFontEngineQPF::NumTags) {
+ switch (tagTypes[tag]) {
+ case QFontEngineQPF::BitFieldType:
+ case QFontEngineQPF::StringType:
+ // can't do anything...
+ break;
+ case QFontEngineQPF::UInt32Type:
+ VERIFY_TAG(length == sizeof(quint32));
+ break;
+ case QFontEngineQPF::FixedType:
+ VERIFY_TAG(length == sizeof(quint32));
+ break;
+ case QFontEngineQPF::UInt8Type:
+ VERIFY_TAG(length == sizeof(quint8));
+ break;
+ }
+#if defined(DEBUG_HEADER)
+ if (length == 1)
+ qDebug() << "tag data" << hex << *tagPtr;
+ else if (length == 4)
+ qDebug() << "tag data" << hex << tagPtr[0] << tagPtr[1] << tagPtr[2] << tagPtr[3];
+#endif
+ }
+ return tagPtr + length;
+}
+
+const QFontEngineQPF::Glyph *QFontEngineQPF::findGlyph(glyph_t g) const
+{
+ if (!g || g >= glyphMapEntries)
+ return 0;
+ const quint32 *gmapPtr = reinterpret_cast<const quint32 *>(fontData + glyphMapOffset);
+ quint32 glyphPos = qFromBigEndian<quint32>(gmapPtr[g]);
+ if (glyphPos > glyphDataSize) {
+ if (glyphPos == 0xffffffff)
+ return 0;
+#if defined(DEBUG_FONTENGINE)
+ qDebug() << "glyph" << g << "outside of glyphData, remapping font file";
+#endif
+#if !defined(QT_NO_FREETYPE) && !defined(QT_FONTS_ARE_RESOURCES)
+ const_cast<QFontEngineQPF *>(this)->remapFontData();
+#endif
+ if (glyphPos > glyphDataSize)
+ return 0;
+ }
+ return reinterpret_cast<const Glyph *>(fontData + glyphDataOffset + glyphPos);
+}
+
+bool QFontEngineQPF::verifyHeader(const uchar *data, int size)
+{
+ VERIFY(size >= int(sizeof(Header)));
+ const Header *header = reinterpret_cast<const Header *>(data);
+ if (header->magic[0] != 'Q'
+ || header->magic[1] != 'P'
+ || header->magic[2] != 'F'
+ || header->magic[3] != '2')
+ return false;
+
+ VERIFY(header->majorVersion <= CurrentMajorVersion);
+ const quint16 dataSize = qFromBigEndian<quint16>(header->dataSize);
+ VERIFY(size >= int(sizeof(Header)) + dataSize);
+
+ const uchar *tagPtr = data + sizeof(Header);
+ const uchar *tagEndPtr = tagPtr + dataSize;
+ while (tagPtr < tagEndPtr - 3) {
+ tagPtr = verifyTag(tagPtr, tagEndPtr);
+ VERIFY(tagPtr);
+ }
+
+ VERIFY(tagPtr <= tagEndPtr);
+ return true;
+}
+
+QVariant QFontEngineQPF::extractHeaderField(const uchar *data, HeaderTag requestedTag)
+{
+ const Header *header = reinterpret_cast<const Header *>(data);
+ const uchar *tagPtr = data + sizeof(Header);
+ const uchar *endPtr = tagPtr + qFromBigEndian<quint16>(header->dataSize);
+ while (tagPtr < endPtr - 3) {
+ quint16 tag = readValue<quint16>(tagPtr);
+ quint16 length = readValue<quint16>(tagPtr);
+ if (tag == requestedTag) {
+ switch (tagTypes[requestedTag]) {
+ case StringType:
+ return QVariant(QString::fromUtf8(reinterpret_cast<const char *>(tagPtr), length));
+ case UInt32Type:
+ return QVariant(readValue<quint32>(tagPtr));
+ case UInt8Type:
+ return QVariant(uint(*tagPtr));
+ case FixedType:
+ return QVariant(QFixed::fromFixed(readValue<quint32>(tagPtr)).toReal());
+ case BitFieldType:
+ return QVariant(QByteArray(reinterpret_cast<const char *>(tagPtr), length));
+ }
+ return QVariant();
+ } else if (tag == Tag_EndOfHeader) {
+ break;
+ }
+ tagPtr += length;
+ }
+
+ return QVariant();
+}
+
+#endif // QT_NO_QWS_QPF2
+
+QString qws_fontCacheDir()
+{
+ QString dir;
+#if defined(Q_WS_QWS)
+ extern QString qws_dataDir();
+ dir = qws_dataDir();
+#else
+ dir = QDir::tempPath();
+#endif
+ dir.append(QLatin1String("/fonts/"));
+ QDir qd(dir);
+ if (!qd.exists() && !qd.mkpath(dir))
+ dir = QDir::tempPath();
+ return dir;
+}
+
+#ifndef QT_NO_QWS_QPF2
+
+#ifndef QT_FONTS_ARE_RESOURCES
+QList<QByteArray> QFontEngineQPF::cleanUpAfterClientCrash(const QList<int> &crashedClientIds)
+{
+ QList<QByteArray> removedFonts;
+ QDir dir(qws_fontCacheDir(), QLatin1String("*.qsf"));
+ for (int i = 0; i < int(dir.count()); ++i) {
+ const QByteArray fileName = QFile::encodeName(dir.absoluteFilePath(dir[i]));
+
+ int fd = ::open(fileName.constData(), O_RDONLY);
+ if (fd >= 0) {
+ void *header = ::mmap(0, sizeof(QFontEngineQPF::Header), PROT_READ, MAP_SHARED, fd, 0);
+ if (header && header != MAP_FAILED) {
+ quint32 lockValue = reinterpret_cast<QFontEngineQPF::Header *>(header)->lock;
+
+ if (lockValue && crashedClientIds.contains(lockValue)) {
+ removedFonts.append(fileName);
+ QFile::remove(QFile::decodeName(fileName));
+ }
+
+ ::munmap(header, sizeof(QFontEngineQPF::Header));
+ }
+ ::close(fd);
+ }
+ }
+ if (!removedFonts.isEmpty())
+ qDebug() << "list of corrupted and removed fonts:" << removedFonts;
+ return removedFonts;
+}
+#endif
+
+static inline unsigned int getChar(const QChar *str, int &i, const int len)
+{
+ unsigned int uc = str[i].unicode();
+ if (uc >= 0xd800 && uc < 0xdc00 && i < len-1) {
+ uint low = str[i+1].unicode();
+ if (low >= 0xdc00 && low < 0xe000) {
+ uc = (uc - 0xd800)*0x400 + (low - 0xdc00) + 0x10000;
+ ++i;
+ }
+ }
+ return uc;
+}
+#ifdef QT_FONTS_ARE_RESOURCES
+QFontEngineQPF::QFontEngineQPF(const QFontDef &def, const uchar *bytes, int size)
+ : fd(-1), fontData(bytes), dataSize(size), renderingFontEngine(0)
+#else
+QFontEngineQPF::QFontEngineQPF(const QFontDef &def, int fileDescriptor, QFontEngine *fontEngine)
+ : fd(fileDescriptor), fontData(0), dataSize(0), renderingFontEngine(fontEngine)
+#endif
+{
+ fontDef = def;
+ cache_cost = 100;
+ freetype = 0;
+ externalCMap = 0;
+ cmapOffset = 0;
+ cmapSize = 0;
+ glyphMapOffset = 0;
+ glyphMapEntries = 0;
+ glyphDataOffset = 0;
+ glyphDataSize = 0;
+ kerning_pairs_loaded = false;
+ readOnly = true;
+
+#if defined(DEBUG_FONTENGINE)
+ qDebug() << "QFontEngineQPF::QFontEngineQPF( fd =" << fd << ", renderingFontEngine =" << renderingFontEngine << ")";
+#endif
+
+#ifndef QT_FONTS_ARE_RESOURCES
+ if (fd < 0) {
+ if (!renderingFontEngine)
+ return;
+
+ fileName = fontDef.family.toLower() + QLatin1String("_")
+ + QString::number(fontDef.pixelSize)
+ + QLatin1String("_") + QString::number(fontDef.weight)
+ + (fontDef.style != QFont::StyleNormal ?
+ QLatin1String("_italic") : QLatin1String(""))
+ + QLatin1String(".qsf");
+ fileName.replace(QLatin1Char(' '), QLatin1Char('_'));
+ fileName.prepend(qws_fontCacheDir());
+
+ const QByteArray encodedName = QFile::encodeName(fileName);
+ if (::access(encodedName, F_OK) == 0) {
+#if defined(DEBUG_FONTENGINE)
+ qDebug() << "found existing qpf:" << fileName;
+#endif
+ if (::access(encodedName, W_OK | R_OK) == 0)
+ fd = ::open(encodedName, O_RDWR);
+ else if (::access(encodedName, R_OK) == 0)
+ fd = ::open(encodedName, O_RDONLY);
+ } else {
+#if defined(DEBUG_FONTENGINE)
+ qDebug() << "creating qpf on the fly:" << fileName;
+#endif
+ if (::access(QFile::encodeName(qws_fontCacheDir()), W_OK) == 0) {
+ fd = ::open(encodedName, O_RDWR | O_EXCL | O_CREAT, 0644);
+
+ QBuffer buffer;
+ buffer.open(QIODevice::ReadWrite);
+ QPFGenerator generator(&buffer, renderingFontEngine);
+ generator.generate();
+ buffer.close();
+ const QByteArray &data = buffer.data();
+ ::write(fd, data.constData(), data.size());
+ }
+ }
+ }
+
+ QT_STATBUF st;
+ if (QT_FSTAT(fd, &st)) {
+#if defined(DEBUG_FONTENGINE)
+ qDebug() << "stat failed!";
+#endif
+ return;
+ }
+ dataSize = st.st_size;
+
+
+ fontData = (const uchar *)::mmap(0, st.st_size, PROT_READ | (renderingFontEngine ? PROT_WRITE : 0), MAP_SHARED, fd, 0);
+ if (!fontData || fontData == (const uchar *)MAP_FAILED) {
+#if defined(DEBUG_FONTENGINE)
+ perror("mmap failed");
+#endif
+ fontData = 0;
+ return;
+ }
+#endif //QT_FONTS_ARE_RESOURCES
+
+ if (!verifyHeader(fontData, dataSize)) {
+#if defined(DEBUG_FONTENGINE)
+ qDebug() << "verifyHeader failed!";
+#endif
+ return;
+ }
+
+ const Header *header = reinterpret_cast<const Header *>(fontData);
+
+ readOnly = (header->lock == 0xffffffff);
+
+ const uchar *data = fontData + sizeof(Header) + qFromBigEndian<quint16>(header->dataSize);
+ const uchar *endPtr = fontData + dataSize;
+ while (data <= endPtr - 8) {
+ quint16 blockTag = readValue<quint16>(data);
+ data += 2; // skip padding
+ quint32 blockSize = readValue<quint32>(data);
+
+ if (blockTag == CMapBlock) {
+ cmapOffset = data - fontData;
+ cmapSize = blockSize;
+ } else if (blockTag == GMapBlock) {
+ glyphMapOffset = data - fontData;
+ glyphMapEntries = blockSize / 4;
+ } else if (blockTag == GlyphBlock) {
+ glyphDataOffset = data - fontData;
+ glyphDataSize = blockSize;
+ }
+
+ data += blockSize;
+ }
+
+ face_id.filename = QFile::encodeName(extractHeaderField(fontData, Tag_FileName).toString());
+ face_id.index = extractHeaderField(fontData, Tag_FileIndex).toInt();
+#if !defined(QT_NO_FREETYPE) && !defined(QT_FONTS_ARE_RESOURCES)
+ freetype = QFreetypeFace::getFace(face_id);
+ if (!freetype) {
+ QString newPath =
+#ifndef QT_NO_SETTINGS
+ QLibraryInfo::location(QLibraryInfo::LibrariesPath) +
+#endif
+ QLatin1String("/fonts/") +
+ QFileInfo(QFile::decodeName(face_id.filename)).fileName();
+ face_id.filename = QFile::encodeName(newPath);
+ freetype = QFreetypeFace::getFace(face_id);
+ }
+ if (freetype) {
+ const quint32 qpfTtfRevision = extractHeaderField(fontData, Tag_FontRevision).toUInt();
+ uchar data[4];
+ uint length = 4;
+ bool ok = freetype->getSfntTable(MAKE_TAG('h', 'e', 'a', 'd'), data, &length);
+ if (!ok || length != 4
+ || qFromBigEndian<quint32>(data) != qpfTtfRevision) {
+ freetype->release(face_id);
+ freetype = 0;
+ }
+ }
+ if (!cmapOffset && freetype) {
+ freetypeCMapTable = getSfntTable(MAKE_TAG('c', 'm', 'a', 'p'));
+ externalCMap = reinterpret_cast<const uchar *>(freetypeCMapTable.constData());
+ cmapSize = freetypeCMapTable.size();
+ }
+#endif
+
+ // get the real cmap
+ if (cmapOffset) {
+ int tableSize = cmapSize;
+ const uchar *cmapPtr = getCMap(fontData + cmapOffset, tableSize, &symbol, &cmapSize);
+ if (cmapPtr)
+ cmapOffset = cmapPtr - fontData;
+ else
+ cmapOffset = 0;
+ } else if (externalCMap) {
+ int tableSize = cmapSize;
+ externalCMap = getCMap(externalCMap, tableSize, &symbol, &cmapSize);
+ }
+
+ // verify all the positions in the glyphMap
+ if (glyphMapOffset) {
+ const quint32 *gmapPtr = reinterpret_cast<const quint32 *>(fontData + glyphMapOffset);
+ for (uint i = 0; i < glyphMapEntries; ++i) {
+ quint32 glyphDataPos = qFromBigEndian<quint32>(gmapPtr[i]);
+ if (glyphDataPos == 0xffffffff)
+ continue;
+ if (glyphDataPos >= glyphDataSize) {
+ // error
+ glyphMapOffset = 0;
+ glyphMapEntries = 0;
+ break;
+ }
+ }
+ }
+
+#if defined(DEBUG_FONTENGINE)
+ if (!isValid())
+ qDebug() << "fontData" << fontData << "dataSize" << dataSize
+ << "externalCMap" << externalCMap << "cmapOffset" << cmapOffset
+ << "glyphMapOffset" << glyphMapOffset << "glyphDataOffset" << glyphDataOffset
+ << "fd" << fd << "glyphDataSize" << glyphDataSize;
+#endif
+#if defined(Q_WS_QWS)
+ if (isValid() && renderingFontEngine)
+ qt_fbdpy->sendFontCommand(QWSFontCommand::StartedUsingFont, QFile::encodeName(fileName));
+#endif
+}
+
+QFontEngineQPF::~QFontEngineQPF()
+{
+#if defined(Q_WS_QWS)
+ if (isValid() && renderingFontEngine)
+ qt_fbdpy->sendFontCommand(QWSFontCommand::StoppedUsingFont, QFile::encodeName(fileName));
+#endif
+ delete renderingFontEngine;
+ if (fontData)
+ munmap((void *)fontData, dataSize);
+ if (fd != -1)
+ ::close(fd);
+#if !defined(QT_NO_FREETYPE)
+ if (freetype)
+ freetype->release(face_id);
+#endif
+}
+
+bool QFontEngineQPF::getSfntTableData(uint tag, uchar *buffer, uint *length) const
+{
+#if !defined(QT_NO_FREETYPE)
+ if (freetype)
+ return freetype->getSfntTable(tag, buffer, length);
+#endif
+ Q_UNUSED(tag);
+ Q_UNUSED(buffer);
+ *length = 0;
+ return false;
+}
+
+bool QFontEngineQPF::stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs, int *nglyphs, QTextEngine::ShaperFlags flags) const
+{
+ if (!externalCMap && !cmapOffset && renderingFontEngine) {
+ if (!renderingFontEngine->stringToCMap(str, len, glyphs, nglyphs, flags))
+ return false;
+#ifndef QT_NO_FREETYPE
+ const_cast<QFontEngineQPF *>(this)->ensureGlyphsLoaded(*glyphs);
+#endif
+ return true;
+ }
+
+ if (*nglyphs < len) {
+ *nglyphs = len;
+ return false;
+ }
+
+#if defined(DEBUG_FONTENGINE)
+ QSet<QChar> seenGlyphs;
+#endif
+
+ const uchar *cmap = externalCMap ? externalCMap : (fontData + cmapOffset);
+
+ bool mirrored = flags & QTextEngine::RightToLeft;
+ int glyph_pos = 0;
+ if (symbol) {
+ for (int i = 0; i < len; ++i) {
+ unsigned int uc = getChar(str, i, len);
+ if (mirrored)
+ uc = QChar::mirroredChar(uc);
+ glyphs->glyphs[glyph_pos] = getTrueTypeGlyphIndex(cmap, uc);
+ if(!glyphs->glyphs[glyph_pos] && uc < 0x100)
+ glyphs->glyphs[glyph_pos] = getTrueTypeGlyphIndex(cmap, uc + 0xf000);
+ ++glyph_pos;
+ }
+ } else {
+ for (int i = 0; i < len; ++i) {
+ unsigned int uc = getChar(str, i, len);
+ if (mirrored)
+ uc = QChar::mirroredChar(uc);
+ glyphs->glyphs[glyph_pos] = getTrueTypeGlyphIndex(cmap, uc);
+#if 0 && defined(DEBUG_FONTENGINE)
+ QChar c(uc);
+ if (!findGlyph(glyphs[glyph_pos].glyph) && !seenGlyphs.contains(c))
+ qDebug() << "glyph for character" << c << "/" << hex << uc << "is" << dec << glyphs[glyph_pos].glyph;
+
+ seenGlyphs.insert(c);
+#endif
+ ++glyph_pos;
+ }
+ }
+
+ *nglyphs = glyph_pos;
+ glyphs->numGlyphs = glyph_pos;
+ recalcAdvances(glyphs, flags);
+ return true;
+}
+
+void QFontEngineQPF::recalcAdvances(QGlyphLayout *glyphs, QTextEngine::ShaperFlags) const
+{
+#ifndef QT_NO_FREETYPE
+ const_cast<QFontEngineQPF *>(this)->ensureGlyphsLoaded(*glyphs);
+#endif
+ for (int i = 0; i < glyphs->numGlyphs; ++i) {
+ const Glyph *g = findGlyph(glyphs->glyphs[i]);
+ if (!g) {
+ glyphs->glyphs[i] = 0;
+ continue;
+ }
+ glyphs->advances_x[i] = g->advance;
+ glyphs->advances_y[i] = 0;
+ }
+}
+
+QImage QFontEngineQPF::alphaMapForGlyph(glyph_t g)
+{
+ const Glyph *glyph = findGlyph(g);
+ if (!glyph)
+ QImage();
+
+ const uchar *bits = ((const uchar *) glyph) + sizeof(Glyph);
+
+ QImage image(glyph->width, glyph->height, QImage::Format_Indexed8);
+ for (int j=0; j<256; ++j)
+ image.setColor(j, 0xff000000 | j | (j<<8) | (j<<16));
+
+ for (int i=0; i<glyph->height; ++i) {
+ memcpy(image.scanLine(i), bits, glyph->bytesPerLine);
+ bits += glyph->bytesPerLine;
+ }
+ return image;
+}
+
+void QFontEngineQPF::draw(QPaintEngine *p, qreal _x, qreal _y, const QTextItemInt &si)
+{
+ QPaintEngineState *pState = p->state;
+ QRasterPaintEngine *paintEngine = static_cast<QRasterPaintEngine*>(p);
+
+ QTransform matrix = pState->transform();
+ matrix.translate(_x, _y);
+ QFixed x = QFixed::fromReal(matrix.dx());
+ QFixed y = QFixed::fromReal(matrix.dy());
+
+ QVarLengthArray<QFixedPoint> positions;
+ QVarLengthArray<glyph_t> glyphs;
+ getGlyphPositions(si.glyphs, matrix, si.flags, glyphs, positions);
+ if (glyphs.size() == 0)
+ return;
+
+ for(int i = 0; i < glyphs.size(); i++) {
+ const Glyph *glyph = findGlyph(glyphs[i]);
+ if (!glyph)
+ continue;
+
+ const int depth = 8; //###
+
+ paintEngine->alphaPenBlt(reinterpret_cast<const uchar *>(glyph) + sizeof(Glyph), glyph->bytesPerLine, depth,
+ qRound(positions[i].x) + glyph->x,
+ qRound(positions[i].y) + glyph->y,
+ glyph->width, glyph->height);
+ }
+}
+
+void QFontEngineQPF::addOutlineToPath(qreal x, qreal y, const QGlyphLayout &glyphs, QPainterPath *path, QTextItem::RenderFlags flags)
+{
+ if (renderingFontEngine &&
+ (renderingFontEngine->type() != QFontEngine::Proxy
+ || static_cast<QProxyFontEngine *>(renderingFontEngine)->capabilities() & QAbstractFontEngine::CanOutlineGlyphs)) {
+ renderingFontEngine->addOutlineToPath(x, y, glyphs, path, flags);
+ return;
+ }
+ addBitmapFontToPath(x, y, glyphs, path, flags);
+}
+
+glyph_metrics_t QFontEngineQPF::boundingBox(const QGlyphLayout &glyphs)
+{
+#ifndef QT_NO_FREETYPE
+ const_cast<QFontEngineQPF *>(this)->ensureGlyphsLoaded(glyphs);
+#endif
+
+ glyph_metrics_t overall;
+ // initialize with line height, we get the same behaviour on all platforms
+ overall.y = -ascent();
+ overall.height = ascent() + descent() + 1;
+
+ QFixed ymax = 0;
+ QFixed xmax = 0;
+ for (int i = 0; i < glyphs.numGlyphs; i++) {
+ const Glyph *g = findGlyph(glyphs.glyphs[i]);
+ if (!g)
+ continue;
+
+ QFixed x = overall.xoff + glyphs.offsets[i].x + g->x;
+ QFixed y = overall.yoff + glyphs.offsets[i].y + g->y;
+ overall.x = qMin(overall.x, x);
+ overall.y = qMin(overall.y, y);
+ xmax = qMax(xmax, x + g->width);
+ ymax = qMax(ymax, y + g->height);
+ overall.xoff += g->advance;
+ }
+ overall.height = qMax(overall.height, ymax - overall.y);
+ overall.width = xmax - overall.x;
+
+ return overall;
+}
+
+glyph_metrics_t QFontEngineQPF::boundingBox(glyph_t glyph)
+{
+#ifndef QT_NO_FREETYPE
+ {
+ QGlyphLayoutArray<1> tmp;
+ tmp.glyphs[0] = glyph;
+ const_cast<QFontEngineQPF *>(this)->ensureGlyphsLoaded(tmp);
+ }
+#endif
+ glyph_metrics_t overall;
+ const Glyph *g = findGlyph(glyph);
+ if (!g)
+ return overall;
+ overall.x = g->x;
+ overall.y = g->y;
+ overall.width = g->width;
+ overall.height = g->height;
+ overall.xoff = g->advance;
+ return overall;
+}
+
+QFixed QFontEngineQPF::ascent() const
+{
+ return QFixed::fromReal(extractHeaderField(fontData, Tag_Ascent).value<qreal>());
+}
+
+QFixed QFontEngineQPF::descent() const
+{
+ return QFixed::fromReal(extractHeaderField(fontData, Tag_Descent).value<qreal>());
+}
+
+QFixed QFontEngineQPF::leading() const
+{
+ return QFixed::fromReal(extractHeaderField(fontData, Tag_Leading).value<qreal>());
+}
+
+qreal QFontEngineQPF::maxCharWidth() const
+{
+ return extractHeaderField(fontData, Tag_MaxCharWidth).value<qreal>();
+}
+
+qreal QFontEngineQPF::minLeftBearing() const
+{
+ return extractHeaderField(fontData, Tag_MinLeftBearing).value<qreal>();
+}
+
+qreal QFontEngineQPF::minRightBearing() const
+{
+ return extractHeaderField(fontData, Tag_MinRightBearing).value<qreal>();
+}
+
+QFixed QFontEngineQPF::underlinePosition() const
+{
+ return QFixed::fromReal(extractHeaderField(fontData, Tag_UnderlinePosition).value<qreal>());
+}
+
+QFixed QFontEngineQPF::lineThickness() const
+{
+ return QFixed::fromReal(extractHeaderField(fontData, Tag_LineThickness).value<qreal>());
+}
+
+QFontEngine::Type QFontEngineQPF::type() const
+{
+ return QFontEngine::QPF2;
+}
+
+bool QFontEngineQPF::canRender(const QChar *string, int len)
+{
+ const uchar *cmap = externalCMap ? externalCMap : (fontData + cmapOffset);
+
+ if (symbol) {
+ for (int i = 0; i < len; ++i) {
+ unsigned int uc = getChar(string, i, len);
+ glyph_t g = getTrueTypeGlyphIndex(cmap, uc);
+ if(!g && uc < 0x100)
+ g = getTrueTypeGlyphIndex(cmap, uc + 0xf000);
+ if (!g)
+ return false;
+ }
+ } else {
+ for (int i = 0; i < len; ++i) {
+ unsigned int uc = getChar(string, i, len);
+ if (!getTrueTypeGlyphIndex(cmap, uc))
+ return false;
+ }
+ }
+ return true;
+}
+
+bool QFontEngineQPF::isValid() const
+{
+ return fontData && dataSize && (cmapOffset || externalCMap || renderingFontEngine)
+ && glyphMapOffset && glyphDataOffset && (fd >= 0 || glyphDataSize > 0);
+}
+
+#if !defined(QT_NO_FREETYPE)
+FT_Face QFontEngineQPF::lockFace() const
+{
+ Q_ASSERT(freetype);
+ freetype->lock();
+ FT_Face face = freetype->face;
+
+ // ### not perfect
+ const int ysize = fontDef.pixelSize << 6;
+ const int xsize = ysize;
+
+ if (freetype->xsize != xsize || freetype->ysize != ysize) {
+ FT_Set_Char_Size(face, xsize, ysize, 0, 0);
+ freetype->xsize = xsize;
+ freetype->ysize = ysize;
+ }
+ FT_Matrix identityMatrix;
+ identityMatrix.xx = 0x10000;
+ identityMatrix.yy = 0x10000;
+ identityMatrix.xy = 0;
+ identityMatrix.yx = 0;
+ if (freetype->matrix.xx != identityMatrix.xx ||
+ freetype->matrix.yy != identityMatrix.yy ||
+ freetype->matrix.xy != identityMatrix.xy ||
+ freetype->matrix.yx != identityMatrix.yx) {
+ freetype->matrix = identityMatrix;
+ FT_Set_Transform(face, &freetype->matrix, 0);
+ }
+ return face;
+}
+
+void QFontEngineQPF::unlockFace() const
+{
+ freetype->unlock();
+}
+
+void QFontEngineQPF::doKerning(QGlyphLayout *g, QTextEngine::ShaperFlags flags) const
+{
+ if (!kerning_pairs_loaded) {
+ kerning_pairs_loaded = true;
+ if (freetype) {
+ lockFace();
+ if (freetype->face->size->metrics.x_ppem != 0) {
+ QFixed scalingFactor(freetype->face->units_per_EM/freetype->face->size->metrics.x_ppem);
+ unlockFace();
+ const_cast<QFontEngineQPF *>(this)->loadKerningPairs(scalingFactor);
+ } else {
+ unlockFace();
+ }
+ }
+ }
+ QFontEngine::doKerning(g, flags);
+}
+
+HB_Error QFontEngineQPF::getPointInOutline(HB_Glyph glyph, int flags, hb_uint32 point, HB_Fixed *xpos, HB_Fixed *ypos, hb_uint32 *nPoints)
+{
+ if (!freetype)
+ return HB_Err_Not_Covered;
+ lockFace();
+ HB_Error result = freetype->getPointInOutline(glyph, flags, point, xpos, ypos, nPoints);
+ unlockFace();
+ return result;
+}
+
+QFixed QFontEngineQPF::emSquareSize() const
+{
+ if (!freetype)
+ return QFontEngine::emSquareSize();
+ if (FT_IS_SCALABLE(freetype->face))
+ return freetype->face->units_per_EM;
+ else
+ return freetype->face->size->metrics.y_ppem;
+}
+
+void QFontEngineQPF::ensureGlyphsLoaded(const QGlyphLayout &glyphs)
+{
+ if (readOnly)
+ return;
+ bool locked = false;
+ for (int i = 0; i < glyphs.numGlyphs; ++i) {
+ if (!glyphs.glyphs[i])
+ continue;
+ const Glyph *g = findGlyph(glyphs.glyphs[i]);
+ if (g)
+ continue;
+ if (!locked) {
+ if (!lockFile())
+ return;
+ locked = true;
+ g = findGlyph(glyphs.glyphs[i]);
+ if (g)
+ continue;
+ }
+ loadGlyph(glyphs.glyphs[i]);
+ }
+ if (locked) {
+ unlockFile();
+#if defined(DEBUG_FONTENGINE)
+ qDebug() << "Finished rendering glyphs\n";
+#endif
+ }
+}
+
+void QFontEngineQPF::loadGlyph(glyph_t glyph)
+{
+ quint32 glyphPos = ~0;
+
+ if (!renderingFontEngine)
+ return;
+
+ QImage img = renderingFontEngine->alphaMapForGlyph(glyph).convertToFormat(QImage::Format_Indexed8);
+ glyph_metrics_t metrics = renderingFontEngine->boundingBox(glyph);
+ renderingFontEngine->removeGlyphFromCache(glyph);
+
+ off_t oldSize = ::lseek(fd, 0, SEEK_END);
+ if (oldSize == (off_t)-1)
+ return;
+
+ Glyph g;
+ g.width = img.width();
+ g.height = img.height();
+ g.bytesPerLine = img.bytesPerLine();
+ g.x = qRound(metrics.x);
+ g.y = qRound(metrics.y);
+ g.advance = qRound(metrics.xoff);
+
+ ::write(fd, &g, sizeof(g));
+ ::write(fd, img.bits(), img.numBytes());
+
+ glyphPos = oldSize - glyphDataOffset;
+#if 0 && defined(DEBUG_FONTENGINE)
+ qDebug() << "glyphPos for new glyph" << glyph << "is" << glyphPos << "oldSize" << oldSize << "glyphDataOffset" << glyphDataOffset;
+#endif
+
+ quint32 *gmap = (quint32 *)(fontData + glyphMapOffset);
+ gmap[glyph] = qToBigEndian(glyphPos);
+
+ glyphDataSize = glyphPos + sizeof(g) + img.numBytes();
+ quint32 *blockSizePtr = (quint32 *)(fontData + glyphDataOffset - 4);
+ *blockSizePtr = qToBigEndian(glyphDataSize);
+}
+
+bool QFontEngineQPF::lockFile()
+{
+ // #### this does not handle the case when the process holding the
+ // lock hangs for some reason
+ struct flock lock;
+ lock.l_type = F_WRLCK;
+ lock.l_whence = SEEK_SET;
+ lock.l_start = 0;
+ lock.l_len = 0; // lock the whole file
+ while (fcntl(fd, F_SETLKW, &lock) != 0) {
+ if (errno == EINTR)
+ continue;
+ perror("locking qpf");
+ return false;
+ }
+ Header *header = (Header *)fontData;
+ if (header->lock) {
+ lock.l_type = F_UNLCK;
+ if (fcntl(fd, F_SETLK, &lock) != 0)
+ perror("unlocking possibly corrupt qpf");
+ return false;
+ }
+#if defined(Q_WS_QWS)
+ extern int qws_client_id;
+ // qws_client_id == 0 means we're the server. in this case we just
+ // set the id to 1
+ header->lock = qws_client_id ? qws_client_id : 1;
+#else
+ header->lock = 1;
+#endif
+ return true;
+}
+
+void QFontEngineQPF::unlockFile()
+{
+ ((Header *)fontData)->lock = 0;
+
+ struct flock lock;
+ lock.l_type = F_UNLCK;
+ lock.l_whence = SEEK_SET;
+ lock.l_start = 0;
+ lock.l_len = 0; // lock the whole file
+ if (fcntl(fd, F_SETLK, &lock) != 0) {
+ perror("unlocking qpf");
+ }
+
+ remapFontData();
+}
+
+void QFontEngineQPF::remapFontData()
+{
+ off_t newFileSize = ::lseek(fd, 0, SEEK_END);
+ if (newFileSize == (off_t)-1) {
+#ifdef DEBUG_FONTENGINE
+ perror("QFontEngineQPF::remapFontData: lseek failed");
+#endif
+ fontData = 0;
+ return;
+ }
+
+#ifndef QT_NO_MREMAP
+ fontData = static_cast<uchar *>(::mremap(const_cast<uchar *>(fontData), dataSize, newFileSize, MREMAP_MAYMOVE));
+ if (!fontData || fontData == (const uchar *)MAP_FAILED) {
+# if defined(DEBUG_FONTENGINE)
+ perror("QFontEngineQPF::remapFontData(): mremap failed");
+# endif
+ fontData = 0;
+ }
+
+ if (!fontData)
+#endif // QT_NO_MREMAP
+ {
+ int status = ::munmap((void *)fontData, dataSize);
+ if (status != 0)
+ qErrnoWarning(status, "QFontEngineQPF::remapFomrData: munmap failed!");
+
+ fontData = (const uchar *)::mmap(0, newFileSize, PROT_READ | (renderingFontEngine ? PROT_WRITE : 0),
+ MAP_SHARED, fd, 0);
+ if (!fontData || fontData == (const uchar *)MAP_FAILED) {
+# if defined(DEBUG_FONTENGINE)
+ perror("mmap failed");
+# endif
+ fontData = 0;
+ return;
+ }
+ }
+
+ dataSize = newFileSize;
+ glyphDataSize = newFileSize - glyphDataOffset;
+#if defined(DEBUG_FONTENGINE)
+ qDebug() << "remapped the font file to" << newFileSize << "bytes";
+#endif
+}
+
+#endif // QT_NO_FREETYPE
+
+void QPFGenerator::generate()
+{
+ writeHeader();
+ writeGMap();
+ writeBlock(QFontEngineQPF::GlyphBlock, QByteArray());
+
+ dev->seek(4); // position of header.lock
+ writeUInt32(0);
+}
+
+void QPFGenerator::writeHeader()
+{
+ QFontEngineQPF::Header header;
+
+ header.magic[0] = 'Q';
+ header.magic[1] = 'P';
+ header.magic[2] = 'F';
+ header.magic[3] = '2';
+ header.lock = 1;
+ header.majorVersion = QFontEngineQPF::CurrentMajorVersion;
+ header.minorVersion = QFontEngineQPF::CurrentMinorVersion;
+ header.dataSize = 0;
+ dev->write((const char *)&header, sizeof(header));
+
+ writeTaggedString(QFontEngineQPF::Tag_FontName, fe->fontDef.family.toUtf8());
+
+ QFontEngine::FaceId face = fe->faceId();
+ writeTaggedString(QFontEngineQPF::Tag_FileName, face.filename);
+ writeTaggedUInt32(QFontEngineQPF::Tag_FileIndex, face.index);
+
+ {
+ uchar data[4];
+ uint len = 4;
+ bool ok = fe->getSfntTableData(MAKE_TAG('h', 'e', 'a', 'd'), data, &len);
+ if (ok) {
+ const quint32 revision = qFromBigEndian<quint32>(data);
+ writeTaggedUInt32(QFontEngineQPF::Tag_FontRevision, revision);
+ }
+ }
+
+ writeTaggedQFixed(QFontEngineQPF::Tag_Ascent, fe->ascent());
+ writeTaggedQFixed(QFontEngineQPF::Tag_Descent, fe->descent());
+ writeTaggedQFixed(QFontEngineQPF::Tag_Leading, fe->leading());
+ writeTaggedQFixed(QFontEngineQPF::Tag_XHeight, fe->xHeight());
+ writeTaggedQFixed(QFontEngineQPF::Tag_AverageCharWidth, fe->averageCharWidth());
+ writeTaggedQFixed(QFontEngineQPF::Tag_MaxCharWidth, QFixed::fromReal(fe->maxCharWidth()));
+ writeTaggedQFixed(QFontEngineQPF::Tag_LineThickness, fe->lineThickness());
+ writeTaggedQFixed(QFontEngineQPF::Tag_MinLeftBearing, QFixed::fromReal(fe->minLeftBearing()));
+ writeTaggedQFixed(QFontEngineQPF::Tag_MinRightBearing, QFixed::fromReal(fe->minRightBearing()));
+ writeTaggedQFixed(QFontEngineQPF::Tag_UnderlinePosition, fe->underlinePosition());
+ writeTaggedUInt8(QFontEngineQPF::Tag_PixelSize, fe->fontDef.pixelSize);
+ writeTaggedUInt8(QFontEngineQPF::Tag_Weight, fe->fontDef.weight);
+ writeTaggedUInt8(QFontEngineQPF::Tag_Style, fe->fontDef.style);
+
+ writeTaggedUInt8(QFontEngineQPF::Tag_GlyphFormat, QFontEngineQPF::AlphamapGlyphs);
+
+ writeTaggedString(QFontEngineQPF::Tag_EndOfHeader, QByteArray());
+ align4();
+
+ const quint64 size = dev->pos();
+ header.dataSize = qToBigEndian<quint16>(size - sizeof(header));
+ dev->seek(0);
+ dev->write((const char *)&header, sizeof(header));
+ dev->seek(size);
+}
+
+void QPFGenerator::writeGMap()
+{
+ const quint16 glyphCount = fe->glyphCount();
+
+ writeUInt16(QFontEngineQPF::GMapBlock);
+ writeUInt16(0); // padding
+ writeUInt32(glyphCount * 4);
+
+ QByteArray &buffer = dev->buffer();
+ const int numBytes = glyphCount * sizeof(quint32);
+ qint64 pos = buffer.size();
+ buffer.resize(pos + numBytes);
+ qMemSet(buffer.data() + pos, 0xff, numBytes);
+ dev->seek(pos + numBytes);
+}
+
+void QPFGenerator::writeBlock(QFontEngineQPF::BlockTag tag, const QByteArray &data)
+{
+ writeUInt16(tag);
+ writeUInt16(0); // padding
+ const int padSize = ((data.size() + 3) / 4) * 4 - data.size();
+ writeUInt32(data.size() + padSize);
+ dev->write(data);
+ for (int i = 0; i < padSize; ++i)
+ writeUInt8(0);
+}
+
+void QPFGenerator::writeTaggedString(QFontEngineQPF::HeaderTag tag, const QByteArray &string)
+{
+ writeUInt16(tag);
+ writeUInt16(string.length());
+ dev->write(string);
+}
+
+void QPFGenerator::writeTaggedUInt32(QFontEngineQPF::HeaderTag tag, quint32 value)
+{
+ writeUInt16(tag);
+ writeUInt16(sizeof(value));
+ writeUInt32(value);
+}
+
+void QPFGenerator::writeTaggedUInt8(QFontEngineQPF::HeaderTag tag, quint8 value)
+{
+ writeUInt16(tag);
+ writeUInt16(sizeof(value));
+ writeUInt8(value);
+}
+
+void QPFGenerator::writeTaggedQFixed(QFontEngineQPF::HeaderTag tag, QFixed value)
+{
+ writeUInt16(tag);
+ writeUInt16(sizeof(quint32));
+ writeUInt32(value.value());
+}
+
+#endif // QT_NO_QWS_QPF2
+
+QFontEngineMultiQWS::QFontEngineMultiQWS(QFontEngine *fe, int _script, const QStringList &fallbacks)
+ : QFontEngineMulti(fallbacks.size() + 1),
+ fallbackFamilies(fallbacks), script(_script)
+{
+ engines[0] = fe;
+ fe->ref.ref();
+ fontDef = engines[0]->fontDef;
+}
+
+void QFontEngineMultiQWS::loadEngine(int at)
+{
+ Q_ASSERT(at < engines.size());
+ Q_ASSERT(engines.at(at) == 0);
+
+ QFontDef request = fontDef;
+ request.styleStrategy |= QFont::NoFontMerging;
+ request.family = fallbackFamilies.at(at-1);
+ engines[at] = QFontDatabase::findFont(script,
+ /*fontprivate*/0,
+ request);
+ Q_ASSERT(engines[at]);
+ engines[at]->ref.ref();
+ engines[at]->fontDef = request;
+}
+
+void QFontEngineMultiQWS::draw(QPaintEngine */*p*/, qreal /*x*/, qreal /*y*/, const QTextItemInt &/*si*/)
+{
+ qFatal("QFontEngineMultiQWS::draw should never be called!");
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/text/qfontengine_qpf_p.h b/src/gui/text/qfontengine_qpf_p.h
new file mode 100644
index 0000000000..a9b87ff981
--- /dev/null
+++ b/src/gui/text/qfontengine_qpf_p.h
@@ -0,0 +1,298 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QFONTENGINE_QPF_P_H
+#define QFONTENGINE_QPF_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 "qfontengine_p.h"
+#include <qendian.h>
+#include <qbuffer.h>
+
+#ifndef QT_NO_QWS_QPF2
+#if !defined(QT_NO_FREETYPE)
+# include "qfontengine_ft_p.h"
+#endif
+#endif
+
+QT_BEGIN_NAMESPACE
+
+#ifndef QT_NO_QWS_QPF2
+
+class QFontEngine;
+class QFreetypeFace;
+
+class Q_GUI_EXPORT QFontEngineQPF : public QFontEngine
+{
+public:
+ // if you add new tags please make sure to update the tables in
+ // qpfutil.cpp and tools/makeqpf/qpf2.cpp
+ enum HeaderTag {
+ Tag_FontName, // 0 string
+ Tag_FileName, // 1 string
+ Tag_FileIndex, // 2 quint32
+ Tag_FontRevision, // 3 quint32
+ Tag_FreeText, // 4 string
+ Tag_Ascent, // 5 QFixed
+ Tag_Descent, // 6 QFixed
+ Tag_Leading, // 7 QFixed
+ Tag_XHeight, // 8 QFixed
+ Tag_AverageCharWidth, // 9 QFixed
+ Tag_MaxCharWidth, // 10 QFixed
+ Tag_LineThickness, // 11 QFixed
+ Tag_MinLeftBearing, // 12 QFixed
+ Tag_MinRightBearing, // 13 QFixed
+ Tag_UnderlinePosition, // 14 QFixed
+ Tag_GlyphFormat, // 15 quint8
+ Tag_PixelSize, // 16 quint8
+ Tag_Weight, // 17 quint8
+ Tag_Style, // 18 quint8
+ Tag_EndOfHeader, // 19 string
+ Tag_WritingSystems, // 20 bitfield
+
+ NumTags
+ };
+
+ enum TagType {
+ StringType,
+ FixedType,
+ UInt8Type,
+ UInt32Type,
+ BitFieldType
+ };
+
+ struct Tag
+ {
+ quint16 tag;
+ quint16 size;
+ };
+
+ enum GlyphFormat {
+ BitmapGlyphs = 1,
+ AlphamapGlyphs = 8
+ };
+
+ enum {
+ CurrentMajorVersion = 2,
+ CurrentMinorVersion = 0
+ };
+
+ // The CMap is identical to the TrueType CMap table format
+ // The GMap table is a normal array with the total number of
+ // covered glyphs in the TrueType font
+ enum BlockTag {
+ CMapBlock,
+ GMapBlock,
+ GlyphBlock
+ };
+
+ struct Q_PACKED Header
+ {
+ char magic[4]; // 'QPF2'
+ quint32 lock; // values: 0 = unlocked, 0xffffffff = read-only, otherwise qws client id of locking process
+ quint8 majorVersion;
+ quint8 minorVersion;
+ quint16 dataSize;
+ };
+
+ struct Q_PACKED Block
+ {
+ quint16 tag;
+ quint16 pad;
+ quint32 dataSize;
+ };
+
+ struct Q_PACKED Glyph
+ {
+ quint8 width;
+ quint8 height;
+ quint8 bytesPerLine;
+ qint8 x;
+ qint8 y;
+ qint8 advance;
+ };
+
+#ifdef QT_FONTS_ARE_RESOURCES
+ QFontEngineQPF(const QFontDef &def, const uchar *bytes, int size);
+#else
+ QFontEngineQPF(const QFontDef &def, int fd, QFontEngine *renderingFontEngine = 0);
+#endif
+ ~QFontEngineQPF();
+
+ FaceId faceId() const { return face_id; }
+ bool getSfntTableData(uint tag, uchar *buffer, uint *length) const;
+
+ bool stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs, int *nglyphs, QTextEngine::ShaperFlags flags) const;
+ void recalcAdvances(QGlyphLayout *, QTextEngine::ShaperFlags) const;
+
+ void draw(QPaintEngine *p, qreal x, qreal y, const QTextItemInt &si);
+ void addOutlineToPath(qreal x, qreal y, const QGlyphLayout &glyphs, QPainterPath *path, QTextItem::RenderFlags flags);
+ QImage alphaMapForGlyph(glyph_t t);
+
+ glyph_metrics_t boundingBox(const QGlyphLayout &glyphs);
+ glyph_metrics_t boundingBox(glyph_t glyph);
+
+ QFixed ascent() const;
+ QFixed descent() const;
+ QFixed leading() const;
+ qreal maxCharWidth() const;
+ qreal minLeftBearing() const;
+ qreal minRightBearing() const;
+ QFixed underlinePosition() const;
+ QFixed lineThickness() const;
+
+ Type type() const;
+
+ bool canRender(const QChar *string, int len);
+ inline const char *name() const { return "QPF2"; }
+
+ virtual int glyphCount() const { return glyphMapEntries; }
+
+ bool isValid() const;
+
+ const Glyph *findGlyph(glyph_t g) const;
+
+ static bool verifyHeader(const uchar *data, int size);
+ static QVariant extractHeaderField(const uchar *data, HeaderTag tag);
+ static QList<QByteArray> cleanUpAfterClientCrash(const QList<int> &crashedClientIds);
+
+#if !defined(QT_NO_FREETYPE)
+ FT_Face lockFace() const;
+ void unlockFace() const;
+ void doKerning(QGlyphLayout *g, QTextEngine::ShaperFlags flags) const;
+ virtual HB_Error getPointInOutline(HB_Glyph glyph, int flags, hb_uint32 point, HB_Fixed *xpos, HB_Fixed *ypos, hb_uint32 *nPoints);
+ virtual QFixed emSquareSize() const;
+#endif
+
+ inline QString fontFile() const { return fileName; }
+
+ QFontEngine *renderingEngine() const { return renderingFontEngine; }
+
+ QFontEngine *takeRenderingEngine()
+ {
+ QFontEngine *engine = renderingFontEngine;
+ renderingFontEngine = 0;
+ return engine;
+ }
+
+private:
+#if !defined(QT_NO_FREETYPE)
+ void ensureGlyphsLoaded(const QGlyphLayout &glyphs);
+ void loadGlyph(glyph_t glyph);
+ bool lockFile();
+ void unlockFile();
+ void remapFontData();
+#endif
+
+ int fd;
+ const uchar *fontData;
+ int dataSize;
+ const uchar *externalCMap;
+ quint32 cmapOffset;
+ int cmapSize;
+ quint32 glyphMapOffset;
+ quint32 glyphMapEntries;
+ quint32 glyphDataOffset;
+ quint32 glyphDataSize;
+ QString fileName;
+ bool readOnly;
+
+ QFreetypeFace *freetype;
+ FaceId face_id;
+ QByteArray freetypeCMapTable;
+ mutable bool kerning_pairs_loaded;
+ QFontEngine *renderingFontEngine;
+};
+
+struct QPFGenerator
+{
+ QPFGenerator(QBuffer *device, QFontEngine *engine)
+ : dev(device), fe(engine) {}
+
+ void generate();
+ void writeHeader();
+ void writeGMap();
+ void writeBlock(QFontEngineQPF::BlockTag tag, const QByteArray &data);
+
+ void writeTaggedString(QFontEngineQPF::HeaderTag tag, const QByteArray &string);
+ void writeTaggedUInt32(QFontEngineQPF::HeaderTag tag, quint32 value);
+ void writeTaggedUInt8(QFontEngineQPF::HeaderTag tag, quint8 value);
+ void writeTaggedQFixed(QFontEngineQPF::HeaderTag tag, QFixed value);
+
+ void writeUInt16(quint16 value) { value = qToBigEndian(value); dev->write((const char *)&value, sizeof(value)); }
+ void writeUInt32(quint32 value) { value = qToBigEndian(value); dev->write((const char *)&value, sizeof(value)); }
+ void writeUInt8(quint8 value) { dev->write((const char *)&value, sizeof(value)); }
+ void writeInt8(qint8 value) { dev->write((const char *)&value, sizeof(value)); }
+
+ void align4() { while (dev->pos() & 3) { dev->putChar('\0'); } }
+
+ QBuffer *dev;
+ QFontEngine *fe;
+};
+
+#endif // QT_NO_QWS_QPF2
+
+class QFontEngineMultiQWS : public QFontEngineMulti
+{
+public:
+ QFontEngineMultiQWS(QFontEngine *fe, int script, const QStringList &fallbacks);
+
+ void loadEngine(int at);
+ void draw(QPaintEngine *p, qreal x, qreal y, const QTextItemInt &si);
+
+private:
+ QStringList fallbackFamilies;
+ int script;
+};
+
+QT_END_NAMESPACE
+
+#endif // QFONTENGINE_QPF_P_H
diff --git a/src/gui/text/qfontengine_qws.cpp b/src/gui/text/qfontengine_qws.cpp
new file mode 100644
index 0000000000..d77632990f
--- /dev/null
+++ b/src/gui/text/qfontengine_qws.cpp
@@ -0,0 +1,625 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qfontengine_p.h"
+#include <private/qunicodetables_p.h>
+#include <qwsdisplay_qws.h>
+#include <qvarlengtharray.h>
+#include <private/qpainter_p.h>
+#include <private/qpaintengine_raster_p.h>
+#include <private/qpdf_p.h>
+#include "qtextengine_p.h"
+
+#include <qdebug.h>
+
+
+#ifndef QT_NO_QWS_QPF
+
+#include "qfile.h"
+#include "qdir.h"
+
+#define QT_USE_MMAP
+#include <stdlib.h>
+
+#ifdef QT_USE_MMAP
+// for mmap
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <fcntl.h>
+#include <errno.h>
+
+# if defined(QT_LINUXBASE) && !defined(MAP_FILE)
+ // LSB 3.2 does not define MAP_FILE
+# define MAP_FILE 0
+# endif
+
+#endif
+
+#endif // QT_NO_QWS_QPF
+
+QT_BEGIN_NAMESPACE
+
+#ifndef QT_NO_QWS_QPF
+QT_BEGIN_INCLUDE_NAMESPACE
+#include "qplatformdefs.h"
+QT_END_INCLUDE_NAMESPACE
+
+static inline unsigned int getChar(const QChar *str, int &i, const int len)
+{
+ unsigned int uc = str[i].unicode();
+ if (uc >= 0xd800 && uc < 0xdc00 && i < len-1) {
+ uint low = str[i+1].unicode();
+ if (low >= 0xdc00 && low < 0xe000) {
+ uc = (uc - 0xd800)*0x400 + (low - 0xdc00) + 0x10000;
+ ++i;
+ }
+ }
+ return uc;
+}
+
+#define FM_SMOOTH 1
+
+
+class Q_PACKED QPFGlyphMetrics {
+
+public:
+ quint8 linestep;
+ quint8 width;
+ quint8 height;
+ quint8 flags;
+
+ qint8 bearingx; // Difference from pen position to glyph's left bbox
+ quint8 advance; // Difference between pen positions
+ qint8 bearingy; // Used for putting characters on baseline
+
+ qint8 reserved; // Do not use
+
+ // Flags:
+ // RendererOwnsData - the renderer is responsible for glyph data
+ // memory deletion otherwise QPFGlyphTree must
+ // delete [] the data when the glyph is deleted.
+ enum Flags { RendererOwnsData=0x01 };
+};
+
+class QPFGlyph {
+public:
+ QPFGlyph() { metrics=0; data=0; }
+ QPFGlyph(QPFGlyphMetrics* m, uchar* d) :
+ metrics(m), data(d) { }
+ ~QPFGlyph() {}
+
+ QPFGlyphMetrics* metrics;
+ uchar* data;
+};
+
+struct Q_PACKED QPFFontMetrics{
+ qint8 ascent,descent;
+ qint8 leftbearing,rightbearing;
+ quint8 maxwidth;
+ qint8 leading;
+ quint8 flags;
+ quint8 underlinepos;
+ quint8 underlinewidth;
+ quint8 reserved3;
+};
+
+
+class QPFGlyphTree {
+public:
+ /* reads in a tree like this:
+
+ A-Z
+ / \
+ 0-9 a-z
+
+ etc.
+
+ */
+ glyph_t min,max;
+ QPFGlyphTree* less;
+ QPFGlyphTree* more;
+ QPFGlyph* glyph;
+public:
+#ifdef QT_USE_MMAP
+ QPFGlyphTree(uchar*& data)
+ {
+ read(data);
+ }
+#else
+ QPFGlyphTree(QIODevice& f)
+ {
+ read(f);
+ }
+#endif
+
+ ~QPFGlyphTree()
+ {
+ // NOTE: does not delete glyph[*].metrics or .data.
+ // the caller does this (only they know who owns
+ // the data). See clear().
+ delete less;
+ delete more;
+ delete [] glyph;
+ }
+
+ bool inFont(glyph_t g) const
+ {
+ if ( g < min ) {
+ if ( !less )
+ return false;
+ return less->inFont(g);
+ } else if ( g > max ) {
+ if ( !more )
+ return false;
+ return more->inFont(g);
+ }
+ return true;
+ }
+
+ QPFGlyph* get(glyph_t g)
+ {
+ if ( g < min ) {
+ if ( !less )
+ return 0;
+ return less->get(g);
+ } else if ( g > max ) {
+ if ( !more )
+ return 0;
+ return more->get(g);
+ }
+ return &glyph[g - min];
+ }
+ int totalChars() const
+ {
+ if ( !this ) return 0;
+ return max-min+1 + less->totalChars() + more->totalChars();
+ }
+ int weight() const
+ {
+ if ( !this ) return 0;
+ return 1 + less->weight() + more->weight();
+ }
+
+ void dump(int indent=0)
+ {
+ for (int i=0; i<indent; i++) printf(" ");
+ printf("%d..%d",min,max);
+ //if ( indent == 0 )
+ printf(" (total %d)",totalChars());
+ printf("\n");
+ if ( less ) less->dump(indent+1);
+ if ( more ) more->dump(indent+1);
+ }
+
+private:
+ QPFGlyphTree()
+ {
+ }
+
+#ifdef QT_USE_MMAP
+ void read(uchar*& data)
+ {
+ // All node data first
+ readNode(data);
+ // Then all non-video data
+ readMetrics(data);
+ // Then all video data
+ readData(data);
+ }
+#else
+ void read(QIODevice& f)
+ {
+ // All node data first
+ readNode(f);
+ // Then all non-video data
+ readMetrics(f);
+ // Then all video data
+ readData(f);
+ }
+#endif
+
+#ifdef QT_USE_MMAP
+ void readNode(uchar*& data)
+ {
+ uchar rw = *data++;
+ uchar cl = *data++;
+ min = (rw << 8) | cl;
+ rw = *data++;
+ cl = *data++;
+ max = (rw << 8) | cl;
+ int flags = *data++;
+ if ( flags & 1 )
+ less = new QPFGlyphTree;
+ else
+ less = 0;
+ if ( flags & 2 )
+ more = new QPFGlyphTree;
+ else
+ more = 0;
+ int n = max-min+1;
+ glyph = new QPFGlyph[n];
+
+ if ( less )
+ less->readNode(data);
+ if ( more )
+ more->readNode(data);
+ }
+#else
+ void readNode(QIODevice& f)
+ {
+ uchar rw = f.getch();
+ uchar cl = f.getch();
+ min = (rw << 8) | cl;
+ rw = f.getch();
+ cl = f.getch();
+ max = (rw << 8) | cl;
+ int flags = f.getch();
+ if ( flags & 1 )
+ less = new QPFGlyphTree;
+ else
+ less = 0;
+ if ( flags & 2 )
+ more = new QPFGlyphTree;
+ else
+ more = 0;
+ int n = max-min+1;
+ glyph = new QPFGlyph[n];
+
+ if ( less )
+ less->readNode(f);
+ if ( more )
+ more->readNode(f);
+ }
+#endif
+
+#ifdef QT_USE_MMAP
+ void readMetrics(uchar*& data)
+ {
+ int n = max-min+1;
+ for (int i=0; i<n; i++) {
+ glyph[i].metrics = (QPFGlyphMetrics*)data;
+ data += sizeof(QPFGlyphMetrics);
+ }
+ if ( less )
+ less->readMetrics(data);
+ if ( more )
+ more->readMetrics(data);
+ }
+#else
+ void readMetrics(QIODevice& f)
+ {
+ int n = max-min+1;
+ for (int i=0; i<n; i++) {
+ glyph[i].metrics = new QPFGlyphMetrics;
+ f.readBlock((char*)glyph[i].metrics, sizeof(QPFGlyphMetrics));
+ }
+ if ( less )
+ less->readMetrics(f);
+ if ( more )
+ more->readMetrics(f);
+ }
+#endif
+
+#ifdef QT_USE_MMAP
+ void readData(uchar*& data)
+ {
+ int n = max-min+1;
+ for (int i=0; i<n; i++) {
+ QSize s( glyph[i].metrics->width, glyph[i].metrics->height );
+ //######### s = qt_screen->mapToDevice( s );
+ uint datasize = glyph[i].metrics->linestep * s.height();
+ glyph[i].data = data; data += datasize;
+ }
+ if ( less )
+ less->readData(data);
+ if ( more )
+ more->readData(data);
+ }
+#else
+ void readData(QIODevice& f)
+ {
+ int n = max-min+1;
+ for (int i=0; i<n; i++) {
+ QSize s( glyph[i].metrics->width, glyph[i].metrics->height );
+ //############### s = qt_screen->mapToDevice( s );
+ uint datasize = glyph[i].metrics->linestep * s.height();
+ glyph[i].data = new uchar[datasize]; // ### deleted?
+ f.readBlock((char*)glyph[i].data, datasize);
+ }
+ if ( less )
+ less->readData(f);
+ if ( more )
+ more->readData(f);
+ }
+#endif
+
+};
+
+class QFontEngineQPF1Data
+{
+public:
+ QPFFontMetrics fm;
+ QPFGlyphTree *tree;
+};
+
+
+QFontEngineQPF1::QFontEngineQPF1(const QFontDef&, const QString &fn)
+{
+ cache_cost = 1;
+
+ int f = ::open( QFile::encodeName(fn), O_RDONLY );
+ Q_ASSERT(f>=0);
+ QT_STATBUF st;
+ if ( QT_FSTAT( f, &st ) )
+ qFatal("Failed to stat %s",QFile::encodeName(fn).data());
+ uchar* data = (uchar*)mmap( 0, // any address
+ st.st_size, // whole file
+ PROT_READ, // read-only memory
+#if !defined(Q_OS_SOLARIS) && !defined(Q_OS_QNX4) && !defined(Q_OS_INTEGRITY)
+ MAP_FILE | MAP_PRIVATE, // swap-backed map from file
+#else
+ MAP_PRIVATE,
+#endif
+ f, 0 ); // from offset 0 of f
+#if defined(Q_OS_QNX4) && !defined(MAP_FAILED)
+#define MAP_FAILED ((void *)-1)
+#endif
+ if ( !data || data == (uchar*)MAP_FAILED )
+ qFatal("Failed to mmap %s",QFile::encodeName(fn).data());
+ ::close(f);
+
+ d = new QFontEngineQPF1Data;
+ memcpy(reinterpret_cast<char*>(&d->fm),data,sizeof(d->fm));
+
+ data += sizeof(d->fm);
+ d->tree = new QPFGlyphTree(data);
+ glyphFormat = (d->fm.flags & FM_SMOOTH) ? QFontEngineGlyphCache::Raster_A8
+ : QFontEngineGlyphCache::Raster_Mono;
+#if 0
+ qDebug() << "font file" << fn
+ << "ascent" << d->fm.ascent << "descent" << d->fm.descent
+ << "leftbearing" << d->fm.leftbearing
+ << "rightbearing" << d->fm.rightbearing
+ << "maxwidth" << d->fm.maxwidth
+ << "leading" << d->fm.leading
+ << "flags" << d->fm.flags
+ << "underlinepos" << d->fm.underlinepos
+ << "underlinewidth" << d->fm.underlinewidth;
+#endif
+}
+
+QFontEngineQPF1::~QFontEngineQPF1()
+{
+ delete d->tree;
+ delete d;
+}
+
+
+bool QFontEngineQPF1::stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs, int *nglyphs, QTextEngine::ShaperFlags flags) const
+{
+ if(*nglyphs < len) {
+ *nglyphs = len;
+ return false;
+ }
+ *nglyphs = 0;
+
+ bool mirrored = flags & QTextEngine::RightToLeft;
+ for(int i = 0; i < len; i++) {
+ unsigned int uc = getChar(str, i, len);
+ if (mirrored)
+ uc = QChar::mirroredChar(uc);
+ glyphs->glyphs[*nglyphs] = uc < 0x10000 ? uc : 0;
+ ++*nglyphs;
+ }
+
+ glyphs->numGlyphs = *nglyphs;
+
+ if (flags & QTextEngine::GlyphIndicesOnly)
+ return true;
+
+ recalcAdvances(glyphs, flags);
+
+ return true;
+}
+
+void QFontEngineQPF1::recalcAdvances(QGlyphLayout *glyphs, QTextEngine::ShaperFlags) const
+{
+ for(int i = 0; i < glyphs->numGlyphs; i++) {
+ QPFGlyph *glyph = d->tree->get(glyphs->glyphs[i]);
+
+ glyphs->advances_x[i] = glyph ? glyph->metrics->advance : 0;
+ glyphs->advances_y[i] = 0;
+
+ if (!glyph)
+ glyphs->glyphs[i] = 0;
+ }
+}
+
+void QFontEngineQPF1::draw(QPaintEngine *p, qreal _x, qreal _y, const QTextItemInt &si)
+{
+ QPaintEngineState *pState = p->state;
+ QRasterPaintEngine *paintEngine = static_cast<QRasterPaintEngine*>(p);
+
+ QTransform matrix = pState->transform();
+ matrix.translate(_x, _y);
+ QFixed x = QFixed::fromReal(matrix.dx());
+ QFixed y = QFixed::fromReal(matrix.dy());
+
+ QVarLengthArray<QFixedPoint> positions;
+ QVarLengthArray<glyph_t> glyphs;
+ getGlyphPositions(si.glyphs, matrix, si.flags, glyphs, positions);
+ if (glyphs.size() == 0)
+ return;
+
+ int depth = (d->fm.flags & FM_SMOOTH) ? 8 : 1;
+ for(int i = 0; i < glyphs.size(); i++) {
+ const QPFGlyph *glyph = d->tree->get(glyphs[i]);
+ if (!glyph)
+ continue;
+
+ int bpl = glyph->metrics->linestep;
+
+ if(glyph->data)
+ paintEngine->alphaPenBlt(glyph->data, bpl, depth,
+ qRound(positions[i].x) + glyph->metrics->bearingx,
+ qRound(positions[i].y) - glyph->metrics->bearingy,
+ glyph->metrics->width,glyph->metrics->height);
+ }
+}
+
+
+QImage QFontEngineQPF1::alphaMapForGlyph(glyph_t g)
+{
+ const QPFGlyph *glyph = d->tree->get(g);
+ if (!glyph)
+ return QImage();
+
+ int mono = !(d->fm.flags & FM_SMOOTH);
+
+ const uchar *bits = glyph->data;//((const uchar *) glyph);
+
+ QImage image;
+ if (mono) {
+ image = QImage((glyph->metrics->width+7)&~7, glyph->metrics->height, QImage::Format_Mono);
+ } else {
+ image = QImage(glyph->metrics->width, glyph->metrics->height, QImage::Format_Indexed8);
+ for (int j=0; j<256; ++j)
+ image.setColor(j, 0xff000000 | j | (j<<8) | (j<<16));
+ }
+ for (int i=0; i<glyph->metrics->height; ++i) {
+ memcpy(image.scanLine(i), bits, glyph->metrics->linestep);
+ bits += glyph->metrics->linestep;
+ }
+ return image;
+}
+
+
+
+void QFontEngineQPF1::addOutlineToPath(qreal x, qreal y, const QGlyphLayout &glyphs, QPainterPath *path, QTextItem::RenderFlags flags)
+{
+ addBitmapFontToPath(x, y, glyphs, path, flags);
+}
+
+glyph_metrics_t QFontEngineQPF1::boundingBox(const QGlyphLayout &glyphs)
+{
+ if (glyphs.numGlyphs == 0)
+ return glyph_metrics_t();
+
+ QFixed w = 0;
+ for (int i = 0; i < glyphs.numGlyphs; ++i)
+ w += glyphs.effectiveAdvance(i);
+ return glyph_metrics_t(0, -ascent(), w, ascent()+descent()+1, w, 0);
+}
+
+glyph_metrics_t QFontEngineQPF1::boundingBox(glyph_t glyph)
+{
+ const QPFGlyph *g = d->tree->get(glyph);
+ if (!g)
+ return glyph_metrics_t();
+ Q_ASSERT(g);
+ return glyph_metrics_t(g->metrics->bearingx, -g->metrics->bearingy,
+ g->metrics->width, g->metrics->height,
+ g->metrics->advance, 0);
+}
+
+QFixed QFontEngineQPF1::ascent() const
+{
+ return d->fm.ascent;
+}
+
+QFixed QFontEngineQPF1::descent() const
+{
+ return d->fm.descent;
+}
+
+QFixed QFontEngineQPF1::leading() const
+{
+ return d->fm.leading;
+}
+
+qreal QFontEngineQPF1::maxCharWidth() const
+{
+ return d->fm.maxwidth;
+}
+/*
+const char *QFontEngineQPF1::name() const
+{
+ return "qt";
+}
+*/
+bool QFontEngineQPF1::canRender(const QChar *str, int len)
+{
+ for(int i = 0; i < len; i++)
+ if (!d->tree->inFont(str[i].unicode()))
+ return false;
+ return true;
+}
+
+QFontEngine::Type QFontEngineQPF1::type() const
+{
+ return QPF1;
+}
+
+qreal QFontEngineQPF1::minLeftBearing() const
+{
+ return d->fm.leftbearing;
+}
+
+qreal QFontEngineQPF1::minRightBearing() const
+{
+ return d->fm.rightbearing;
+}
+
+QFixed QFontEngineQPF1::underlinePosition() const
+{
+ return d->fm.underlinepos;
+}
+
+QFixed QFontEngineQPF1::lineThickness() const
+{
+ return d->fm.underlinewidth;
+}
+
+#endif //QT_NO_QWS_QPF
+
+QT_END_NAMESPACE
diff --git a/src/gui/text/qfontengine_win.cpp b/src/gui/text/qfontengine_win.cpp
new file mode 100644
index 0000000000..1996d44a4e
--- /dev/null
+++ b/src/gui/text/qfontengine_win.cpp
@@ -0,0 +1,1575 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qfontengine_p.h"
+#include "qtextengine_p.h"
+#include <qglobal.h>
+#include "qt_windows.h"
+#include <private/qapplication_p.h>
+
+#include <qlibrary.h>
+#include <qpaintdevice.h>
+#include <qpainter.h>
+#include <qlibrary.h>
+#include <limits.h>
+
+#include <qendian.h>
+#include <qmath.h>
+#include <qthreadstorage.h>
+
+#include <private/qunicodetables_p.h>
+#include <qbitmap.h>
+
+#include <private/qpainter_p.h>
+#include <private/qpdf_p.h>
+#include "qpaintengine.h"
+#include "qvarlengtharray.h"
+#include <private/qpaintengine_raster_p.h>
+#include <private/qnativeimage_p.h>
+
+#if defined(Q_OS_WINCE)
+#include "qguifunctions_wince.h"
+#endif
+
+//### mingw needed define
+#ifndef TT_PRIM_CSPLINE
+#define TT_PRIM_CSPLINE 3
+#endif
+
+#ifdef MAKE_TAG
+#undef MAKE_TAG
+#endif
+// GetFontData expects the tags in little endian ;(
+#define MAKE_TAG(ch1, ch2, ch3, ch4) (\
+ (((quint32)(ch4)) << 24) | \
+ (((quint32)(ch3)) << 16) | \
+ (((quint32)(ch2)) << 8) | \
+ ((quint32)(ch1)) \
+ )
+
+typedef BOOL (WINAPI *PtrGetCharWidthI)(HDC, UINT, UINT, LPWORD, LPINT);
+
+// common DC for all fonts
+
+QT_BEGIN_NAMESPACE
+
+class QtHDC
+{
+ HDC _hdc;
+public:
+ QtHDC()
+ {
+ HDC displayDC = GetDC(0);
+ _hdc = CreateCompatibleDC(displayDC);
+ ReleaseDC(0, displayDC);
+ }
+ ~QtHDC()
+ {
+ if (_hdc)
+ DeleteDC(_hdc);
+ }
+ HDC hdc() const
+ {
+ return _hdc;
+ }
+};
+
+#ifndef QT_NO_THREAD
+Q_GLOBAL_STATIC(QThreadStorage<QtHDC *>, local_shared_dc)
+HDC shared_dc()
+{
+ QtHDC *&hdc = local_shared_dc()->localData();
+ if (!hdc)
+ hdc = new QtHDC;
+ return hdc->hdc();
+}
+#else
+HDC shared_dc()
+{
+ return 0;
+}
+#endif
+
+static HFONT stock_sysfont = 0;
+
+static PtrGetCharWidthI ptrGetCharWidthI = 0;
+static bool resolvedGetCharWidthI = false;
+
+static void resolveGetCharWidthI()
+{
+ if (resolvedGetCharWidthI)
+ return;
+ resolvedGetCharWidthI = true;
+ ptrGetCharWidthI = (PtrGetCharWidthI)QLibrary::resolve(QLatin1String("gdi32"), "GetCharWidthI");
+}
+
+// Copy a LOGFONTW struct into a LOGFONTA by converting the face name to an 8 bit value.
+// This is needed when calling CreateFontIndirect on non-unicode windowses.
+inline static void wa_copy_logfont(LOGFONTW *lfw, LOGFONTA *lfa)
+{
+ lfa->lfHeight = lfw->lfHeight;
+ lfa->lfWidth = lfw->lfWidth;
+ lfa->lfEscapement = lfw->lfEscapement;
+ lfa->lfOrientation = lfw->lfOrientation;
+ lfa->lfWeight = lfw->lfWeight;
+ lfa->lfItalic = lfw->lfItalic;
+ lfa->lfUnderline = lfw->lfUnderline;
+ lfa->lfCharSet = lfw->lfCharSet;
+ lfa->lfOutPrecision = lfw->lfOutPrecision;
+ lfa->lfClipPrecision = lfw->lfClipPrecision;
+ lfa->lfQuality = lfw->lfQuality;
+ lfa->lfPitchAndFamily = lfw->lfPitchAndFamily;
+
+ QString fam = QString::fromUtf16((const ushort*)lfw->lfFaceName);
+ memcpy(lfa->lfFaceName, fam.toLocal8Bit().constData(), fam.length() + 1);
+}
+
+// defined in qtextengine_win.cpp
+typedef void *SCRIPT_CACHE;
+typedef HRESULT (WINAPI *fScriptFreeCache)(SCRIPT_CACHE *);
+extern fScriptFreeCache ScriptFreeCache;
+
+static inline quint32 getUInt(unsigned char *p)
+{
+ quint32 val;
+ val = *p++ << 24;
+ val |= *p++ << 16;
+ val |= *p++ << 8;
+ val |= *p;
+
+ return val;
+}
+
+static inline quint16 getUShort(unsigned char *p)
+{
+ quint16 val;
+ val = *p++ << 8;
+ val |= *p;
+
+ return val;
+}
+
+static inline HFONT systemFont()
+{
+ if (stock_sysfont == 0)
+ stock_sysfont = (HFONT)GetStockObject(SYSTEM_FONT);
+ return stock_sysfont;
+}
+
+
+// general font engine
+
+QFixed QFontEngineWin::lineThickness() const
+{
+ if(lineWidth > 0)
+ return lineWidth;
+
+ return QFontEngine::lineThickness();
+}
+
+#if defined(Q_OS_WINCE)
+static OUTLINETEXTMETRICW *getOutlineTextMetric(HDC hdc)
+{
+ int size;
+ size = GetOutlineTextMetricsW(hdc, 0, 0);
+ OUTLINETEXTMETRICW *otm = (OUTLINETEXTMETRICW *)malloc(size);
+ GetOutlineTextMetricsW(hdc, size, otm);
+ return otm;
+}
+#else
+static OUTLINETEXTMETRICA *getOutlineTextMetric(HDC hdc)
+{
+ int size;
+ size = GetOutlineTextMetricsA(hdc, 0, 0);
+ OUTLINETEXTMETRICA *otm = (OUTLINETEXTMETRICA *)malloc(size);
+ GetOutlineTextMetricsA(hdc, size, otm);
+ return otm;
+}
+#endif
+
+void QFontEngineWin::getCMap()
+{
+ QT_WA({
+ ttf = (bool)(tm.w.tmPitchAndFamily & TMPF_TRUETYPE);
+ } , {
+ ttf = (bool)(tm.a.tmPitchAndFamily & TMPF_TRUETYPE);
+ });
+ HDC hdc = shared_dc();
+ SelectObject(hdc, hfont);
+ bool symb = false;
+ if (ttf) {
+ cmapTable = getSfntTable(qbswap<quint32>(MAKE_TAG('c', 'm', 'a', 'p')));
+ int size = 0;
+ cmap = QFontEngine::getCMap(reinterpret_cast<const uchar *>(cmapTable.constData()),
+ cmapTable.size(), &symb, &size);
+ }
+ if (!cmap) {
+ ttf = false;
+ symb = false;
+ }
+ symbol = symb;
+ designToDevice = 1;
+ _faceId.index = 0;
+ if(cmap) {
+#if defined(Q_OS_WINCE)
+ OUTLINETEXTMETRICW *otm = getOutlineTextMetric(hdc);
+#else
+ OUTLINETEXTMETRICA *otm = getOutlineTextMetric(hdc);
+#endif
+ designToDevice = QFixed((int)otm->otmEMSquare)/int(otm->otmTextMetrics.tmHeight);
+ unitsPerEm = otm->otmEMSquare;
+ x_height = (int)otm->otmsXHeight;
+ loadKerningPairs(designToDevice);
+ _faceId.filename = (char *)otm + (int)otm->otmpFullName;
+ lineWidth = otm->otmsUnderscoreSize;
+ fsType = otm->otmfsType;
+ free(otm);
+ } else {
+ unitsPerEm = tm.w.tmHeight;
+ }
+}
+
+
+inline unsigned int getChar(const QChar *str, int &i, const int len)
+{
+ unsigned int uc = str[i].unicode();
+ if (uc >= 0xd800 && uc < 0xdc00 && i < len-1) {
+ uint low = str[i+1].unicode();
+ if (low >= 0xdc00 && low < 0xe000) {
+ uc = (uc - 0xd800)*0x400 + (low - 0xdc00) + 0x10000;
+ ++i;
+ }
+ }
+ return uc;
+}
+
+int QFontEngineWin::getGlyphIndexes(const QChar *str, int numChars, QGlyphLayout *glyphs, bool mirrored) const
+{
+ int i = 0;
+ int glyph_pos = 0;
+ if (mirrored) {
+#if defined(Q_OS_WINCE)
+ {
+#else
+ if (symbol) {
+ for (; i < numChars; ++i, ++glyph_pos) {
+ unsigned int uc = getChar(str, i, numChars);
+ glyphs->glyphs[glyph_pos] = getTrueTypeGlyphIndex(cmap, uc);
+ if (!glyphs->glyphs[glyph_pos] && uc < 0x100)
+ glyphs->glyphs[glyph_pos] = getTrueTypeGlyphIndex(cmap, uc + 0xf000);
+ }
+ } else if (ttf) {
+ for (; i < numChars; ++i, ++glyph_pos) {
+ unsigned int uc = getChar(str, i, numChars);
+ glyphs->glyphs[glyph_pos] = getTrueTypeGlyphIndex(cmap, QChar::mirroredChar(uc));
+ }
+ } else {
+#endif
+ ushort first, last;
+ QT_WA({
+ first = tm.w.tmFirstChar;
+ last = tm.w.tmLastChar;
+ }, {
+ first = tm.a.tmFirstChar;
+ last = tm.a.tmLastChar;
+ });
+ for (; i < numChars; ++i, ++glyph_pos) {
+ uint ucs = QChar::mirroredChar(getChar(str, i, numChars));
+ if (
+#ifdef Q_OS_WINCE
+ tm.w.tmFirstChar > 60000 || // see line 375
+#endif
+ ucs >= first && ucs <= last)
+ glyphs->glyphs[glyph_pos] = ucs;
+ else
+ glyphs->glyphs[glyph_pos] = 0;
+ }
+ }
+ } else {
+#if defined(Q_OS_WINCE)
+ {
+#else
+ if (symbol) {
+ for (; i < numChars; ++i, ++glyph_pos) {
+ unsigned int uc = getChar(str, i, numChars);
+ glyphs->glyphs[i] = getTrueTypeGlyphIndex(cmap, uc);
+ if(!glyphs->glyphs[glyph_pos] && uc < 0x100)
+ glyphs->glyphs[glyph_pos] = getTrueTypeGlyphIndex(cmap, uc + 0xf000);
+ }
+ } else if (ttf) {
+ for (; i < numChars; ++i, ++glyph_pos) {
+ unsigned int uc = getChar(str, i, numChars);
+ glyphs->glyphs[glyph_pos] = getTrueTypeGlyphIndex(cmap, uc);
+ }
+ } else {
+#endif
+ ushort first, last;
+ QT_WA({
+ first = tm.w.tmFirstChar;
+ last = tm.w.tmLastChar;
+ }, {
+ first = tm.a.tmFirstChar;
+ last = tm.a.tmLastChar;
+ });
+ for (; i < numChars; ++i, ++glyph_pos) {
+ uint uc = getChar(str, i, numChars);
+ if (
+#ifdef Q_OS_WINCE
+ tm.w.tmFirstChar > 60000 || // see comment in QFontEngineWin
+#endif
+ uc >= first && uc <= last)
+ glyphs->glyphs[glyph_pos] = uc;
+ else
+ glyphs->glyphs[glyph_pos] = 0;
+ }
+ }
+ }
+ glyphs->numGlyphs = glyph_pos;
+ return glyph_pos;
+}
+
+
+QFontEngineWin::QFontEngineWin(const QString &name, HFONT _hfont, bool stockFont, LOGFONT lf)
+{
+ //qDebug("regular windows font engine created: font='%s', size=%d", name, lf.lfHeight);
+
+ _name = name;
+
+ cmap = 0;
+ hfont = _hfont;
+ logfont = lf;
+ HDC hdc = shared_dc();
+ SelectObject(hdc, hfont);
+ this->stockFont = stockFont;
+ fontDef.pixelSize = -lf.lfHeight;
+
+ lbearing = SHRT_MIN;
+ rbearing = SHRT_MIN;
+ synthesized_flags = -1;
+ lineWidth = -1;
+ x_height = -1;
+
+ BOOL res;
+ QT_WA({
+ res = GetTextMetricsW(hdc, &tm.w);
+ } , {
+ res = GetTextMetricsA(hdc, &tm.a);
+ });
+ fontDef.fixedPitch = !(tm.w.tmPitchAndFamily & TMPF_FIXED_PITCH);
+ if (!res)
+ qErrnoWarning("QFontEngineWin: GetTextMetrics failed");
+
+ cache_cost = tm.w.tmHeight * tm.w.tmAveCharWidth * 2000;
+ getCMap();
+
+ useTextOutA = false;
+#ifndef Q_OS_WINCE
+ // TextOutW doesn't work for symbol fonts on Windows 95!
+ // since we're using glyph indices we don't care for ttfs about this!
+ if (QSysInfo::WindowsVersion == QSysInfo::WV_95 && !ttf &&
+ (_name == QLatin1String("Marlett") || _name == QLatin1String("Symbol") ||
+ _name == QLatin1String("Webdings") || _name == QLatin1String("Wingdings")))
+ useTextOutA = true;
+#endif
+ widthCache = 0;
+ widthCacheSize = 0;
+ designAdvances = 0;
+ designAdvancesSize = 0;
+
+ if (!resolvedGetCharWidthI)
+ resolveGetCharWidthI();
+}
+
+QFontEngineWin::~QFontEngineWin()
+{
+ if (designAdvances)
+ free(designAdvances);
+
+ if (widthCache)
+ free(widthCache);
+
+ // make sure we aren't by accident still selected
+ SelectObject(shared_dc(), systemFont());
+
+ if (!stockFont) {
+ if (!DeleteObject(hfont))
+ qErrnoWarning("QFontEngineWin: failed to delete non-stock font...");
+ }
+}
+
+HGDIOBJ QFontEngineWin::selectDesignFont(QFixed *overhang) const
+{
+ LOGFONT f = logfont;
+ f.lfHeight = unitsPerEm;
+ HFONT designFont;
+ QT_WA({
+ designFont = CreateFontIndirectW(&f);
+ }, {
+ LOGFONTA fa;
+ wa_copy_logfont(&f, &fa);
+ designFont = CreateFontIndirectA(&fa);
+ });
+ HGDIOBJ oldFont = SelectObject(shared_dc(), designFont);
+
+ if (QSysInfo::WindowsVersion & QSysInfo::WV_DOS_based) {
+ BOOL res;
+ QT_WA({
+ TEXTMETRICW tm;
+ res = GetTextMetricsW(shared_dc(), &tm);
+ if (!res)
+ qErrnoWarning("QFontEngineWin: GetTextMetrics failed");
+ *overhang = QFixed((int)tm.tmOverhang) / designToDevice;
+ } , {
+ TEXTMETRICA tm;
+ res = GetTextMetricsA(shared_dc(), &tm);
+ if (!res)
+ qErrnoWarning("QFontEngineWin: GetTextMetrics failed");
+ *overhang = QFixed((int)tm.tmOverhang) / designToDevice;
+ });
+ } else {
+ *overhang = 0;
+ }
+ return oldFont;
+}
+
+bool QFontEngineWin::stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs, int *nglyphs, QTextEngine::ShaperFlags flags) const
+{
+ if (*nglyphs < len) {
+ *nglyphs = len;
+ return false;
+ }
+
+ *nglyphs = getGlyphIndexes(str, len, glyphs, flags & QTextEngine::RightToLeft);
+
+ if (flags & QTextEngine::GlyphIndicesOnly)
+ return true;
+
+#if defined(Q_OS_WINCE)
+ HDC hdc = shared_dc();
+ if (flags & QTextEngine::DesignMetrics) {
+ HGDIOBJ oldFont = 0;
+ QFixed overhang = 0;
+
+ int glyph_pos = 0;
+ for(register int i = 0; i < len; i++) {
+ bool surrogate = (str[i].unicode() >= 0xd800 && str[i].unicode() < 0xdc00 && i < len-1
+ && str[i+1].unicode() >= 0xdc00 && str[i+1].unicode() < 0xe000);
+ unsigned int glyph = glyphs->glyphs[glyph_pos];
+ if(int(glyph) >= designAdvancesSize) {
+ int newSize = (glyph + 256) >> 8 << 8;
+ designAdvances = (QFixed *)realloc(designAdvances, newSize*sizeof(QFixed));
+ for(int i = designAdvancesSize; i < newSize; ++i)
+ designAdvances[i] = -1000000;
+ designAdvancesSize = newSize;
+ }
+ if(designAdvances[glyph] < -999999) {
+ if(!oldFont)
+ oldFont = selectDesignFont(&overhang);
+ SIZE size = {0, 0};
+ GetTextExtentPoint32W(hdc, (wchar_t *)(str+i), surrogate ? 2 : 1, &size);
+ designAdvances[glyph] = QFixed((int)size.cx)/designToDevice;
+ }
+ glyphs->advances_x[glyph_pos] = designAdvances[glyph];
+ glyphs->advances_y[glyph_pos] = 0;
+ if (surrogate)
+ ++i;
+ ++glyph_pos;
+ }
+ if(oldFont)
+ DeleteObject(SelectObject(hdc, oldFont));
+ } else {
+ int glyph_pos = 0;
+ HGDIOBJ oldFont = 0;
+
+ for(register int i = 0; i < len; i++) {
+ bool surrogate = (str[i].unicode() >= 0xd800 && str[i].unicode() < 0xdc00 && i < len-1
+ && str[i+1].unicode() >= 0xdc00 && str[i+1].unicode() < 0xe000);
+ unsigned int glyph = glyphs->glyphs[glyph_pos];
+
+ glyphs->advances_y[glyph_pos] = 0;
+
+ if (glyph >= widthCacheSize) {
+ int newSize = (glyph + 256) >> 8 << 8;
+ widthCache = (unsigned char *)realloc(widthCache, newSize*sizeof(QFixed));
+ memset(widthCache + widthCacheSize, 0, newSize - widthCacheSize);
+ widthCacheSize = newSize;
+ }
+ glyphs->advances_x[glyph_pos] = widthCache[glyph];
+ // font-width cache failed
+ if (glyphs->advances_x[glyph_pos] == 0) {
+ SIZE size = {0, 0};
+ if (!oldFont)
+ oldFont = SelectObject(hdc, hfont);
+ GetTextExtentPoint32W(hdc, (wchar_t *)str + i, surrogate ? 2 : 1, &size);
+ glyphs->advances_x[glyph_pos] = size.cx;
+ // if glyph's within cache range, store it for later
+ if (size.cx > 0 && size.cx < 0x100)
+ widthCache[glyph] = size.cx;
+ }
+
+ if (surrogate)
+ ++i;
+ ++glyph_pos;
+ }
+
+ if (oldFont)
+ SelectObject(hdc, oldFont);
+ }
+#else
+ recalcAdvances(glyphs, flags);
+#endif
+ return true;
+}
+
+void QFontEngineWin::recalcAdvances(QGlyphLayout *glyphs, QTextEngine::ShaperFlags flags) const
+{
+ HGDIOBJ oldFont = 0;
+ HDC hdc = shared_dc();
+ if (ttf && (flags & QTextEngine::DesignMetrics)) {
+ QFixed overhang = 0;
+
+ for(int i = 0; i < glyphs->numGlyphs; i++) {
+ unsigned int glyph = glyphs->glyphs[i];
+ if(int(glyph) >= designAdvancesSize) {
+ int newSize = (glyph + 256) >> 8 << 8;
+ designAdvances = (QFixed *)realloc(designAdvances, newSize*sizeof(QFixed));
+ for(int i = designAdvancesSize; i < newSize; ++i)
+ designAdvances[i] = -1000000;
+ designAdvancesSize = newSize;
+ }
+ if(designAdvances[glyph] < -999999) {
+ if(!oldFont)
+ oldFont = selectDesignFont(&overhang);
+
+ if (ptrGetCharWidthI) {
+ int width = 0;
+ ptrGetCharWidthI(hdc, glyph, 1, 0, &width);
+
+ designAdvances[glyph] = QFixed(width) / designToDevice;
+ } else {
+#ifndef Q_OS_WINCE
+ GLYPHMETRICS gm;
+ DWORD res = GDI_ERROR;
+ MAT2 mat;
+ mat.eM11.value = mat.eM22.value = 1;
+ mat.eM11.fract = mat.eM22.fract = 0;
+ mat.eM21.value = mat.eM12.value = 0;
+ mat.eM21.fract = mat.eM12.fract = 0;
+ QT_WA({
+ res = GetGlyphOutlineW(hdc, glyph, GGO_METRICS|GGO_GLYPH_INDEX|GGO_NATIVE, &gm, 0, 0, &mat);
+ } , {
+ res = GetGlyphOutlineA(hdc, glyph, GGO_METRICS|GGO_GLYPH_INDEX|GGO_NATIVE, &gm, 0, 0, &mat);
+ });
+
+ if (res != GDI_ERROR) {
+ designAdvances[glyph] = QFixed(gm.gmCellIncX) / designToDevice;
+ }
+#endif
+ }
+ }
+ glyphs->advances_x[i] = designAdvances[glyph];
+ glyphs->advances_y[i] = 0;
+ }
+ if(oldFont)
+ DeleteObject(SelectObject(hdc, oldFont));
+ } else {
+ int overhang = (QSysInfo::WindowsVersion & QSysInfo::WV_DOS_based) ? tm.a.tmOverhang : 0;
+
+ for(int i = 0; i < glyphs->numGlyphs; i++) {
+ unsigned int glyph = glyphs->glyphs[i];
+
+ glyphs->advances_y[i] = 0;
+
+ if (glyph >= widthCacheSize) {
+ int newSize = (glyph + 256) >> 8 << 8;
+ widthCache = (unsigned char *)realloc(widthCache, newSize*sizeof(QFixed));
+ memset(widthCache + widthCacheSize, 0, newSize - widthCacheSize);
+ widthCacheSize = newSize;
+ }
+ glyphs->advances_x[i] = widthCache[glyph];
+ // font-width cache failed
+ if (glyphs->advances_x[i] == 0) {
+ int width = 0;
+ if (!oldFont)
+ oldFont = SelectObject(hdc, hfont);
+
+ if (!ttf) {
+ QChar ch[2] = { ushort(glyph), 0 };
+ int chrLen = 1;
+ if (glyph > 0xffff) {
+ ch[0] = QChar::highSurrogate(glyph);
+ ch[1] = QChar::lowSurrogate(glyph);
+ ++chrLen;
+ }
+ SIZE size = {0, 0};
+ GetTextExtentPoint32W(hdc, (wchar_t *)ch, chrLen, &size);
+ width = size.cx;
+ } else if (ptrGetCharWidthI) {
+ ptrGetCharWidthI(hdc, glyph, 1, 0, &width);
+
+ width -= overhang;
+ } else {
+#ifndef Q_OS_WINCE
+ GLYPHMETRICS gm;
+ DWORD res = GDI_ERROR;
+ MAT2 mat;
+ mat.eM11.value = mat.eM22.value = 1;
+ mat.eM11.fract = mat.eM22.fract = 0;
+ mat.eM21.value = mat.eM12.value = 0;
+ mat.eM21.fract = mat.eM12.fract = 0;
+ QT_WA({
+ res = GetGlyphOutlineW(hdc, glyph, GGO_METRICS|GGO_GLYPH_INDEX, &gm, 0, 0, &mat);
+ } , {
+ res = GetGlyphOutlineA(hdc, glyph, GGO_METRICS|GGO_GLYPH_INDEX, &gm, 0, 0, &mat);
+ });
+
+ if (res != GDI_ERROR) {
+ width = gm.gmCellIncX;
+ }
+#endif
+ }
+ glyphs->advances_x[i] = width;
+ // if glyph's within cache range, store it for later
+ if (width > 0 && width < 0x100)
+ widthCache[glyph] = width;
+ }
+ }
+
+ if (oldFont)
+ SelectObject(hdc, oldFont);
+ }
+}
+
+glyph_metrics_t QFontEngineWin::boundingBox(const QGlyphLayout &glyphs)
+{
+ if (glyphs.numGlyphs == 0)
+ return glyph_metrics_t();
+
+ QFixed w = 0;
+ for (int i = 0; i < glyphs.numGlyphs; ++i)
+ w += glyphs.effectiveAdvance(i);
+
+ return glyph_metrics_t(0, -tm.w.tmAscent, w, tm.w.tmHeight, w, 0);
+}
+
+
+
+
+#ifndef Q_OS_WINCE
+typedef HRESULT (WINAPI *pGetCharABCWidthsFloat)(HDC, UINT, UINT, LPABCFLOAT);
+static pGetCharABCWidthsFloat qt_GetCharABCWidthsFloat = 0;
+#endif
+
+glyph_metrics_t QFontEngineWin::boundingBox(glyph_t glyph, const QTransform &t)
+{
+#ifndef Q_OS_WINCE
+ GLYPHMETRICS gm;
+
+ HDC hdc = shared_dc();
+ SelectObject(hdc, hfont);
+ if(!ttf) {
+ SIZE s = {0, 0};
+ WCHAR ch = glyph;
+ int width;
+ int overhang = 0;
+ static bool resolved = false;
+ if (!resolved) {
+ QLibrary lib(QLatin1String("gdi32"));
+ qt_GetCharABCWidthsFloat = (pGetCharABCWidthsFloat) lib.resolve("GetCharABCWidthsFloatW");
+ resolved = true;
+ }
+ if (QT_WA_INLINE(true, false) && qt_GetCharABCWidthsFloat) {
+ ABCFLOAT abc;
+ qt_GetCharABCWidthsFloat(hdc, ch, ch, &abc);
+ width = qRound(abc.abcfB);
+ } else {
+ GetTextExtentPoint32W(hdc, &ch, 1, &s);
+ overhang = (QSysInfo::WindowsVersion & QSysInfo::WV_DOS_based) ? tm.a.tmOverhang : 0;
+ width = s.cx;
+ }
+
+ return glyph_metrics_t(0, -tm.a.tmAscent,
+ width, tm.a.tmHeight,
+ width-overhang, 0).transformed(t);
+ } else {
+ DWORD res = 0;
+ MAT2 mat;
+ mat.eM11.value = mat.eM22.value = 1;
+ mat.eM11.fract = mat.eM22.fract = 0;
+ mat.eM21.value = mat.eM12.value = 0;
+ mat.eM21.fract = mat.eM12.fract = 0;
+
+ if (t.type() > QTransform::TxTranslate) {
+ // We need to set the transform using the HDC's world
+ // matrix rather than using the MAT2 above, because the
+ // results provided when transforming via MAT2 does not
+ // match the glyphs that are drawn using a WorldTransform
+ XFORM xform;
+ xform.eM11 = t.m11();
+ xform.eM12 = t.m12();
+ xform.eM21 = t.m21();
+ xform.eM22 = t.m22();
+ xform.eDx = 0;
+ xform.eDy = 0;
+ SetGraphicsMode(hdc, GM_ADVANCED);
+ SetWorldTransform(hdc, &xform);
+ }
+
+ QT_WA({
+ res = GetGlyphOutlineW(hdc, glyph, GGO_METRICS|GGO_GLYPH_INDEX, &gm, 0, 0, &mat);
+ } , {
+ res = GetGlyphOutlineA(hdc, glyph, GGO_METRICS|GGO_GLYPH_INDEX, &gm, 0, 0, &mat);
+ });
+ if (t.type() > QTransform::TxTranslate) {
+ XFORM xform;
+ xform.eM11 = xform.eM22 = 1;
+ xform.eM12 = xform.eM21 = xform.eDx = xform.eDy = 0;
+ SetWorldTransform(hdc, &xform);
+ SetGraphicsMode(hdc, GM_COMPATIBLE);
+ }
+
+ if (res != GDI_ERROR) {
+ return glyph_metrics_t(gm.gmptGlyphOrigin.x, -gm.gmptGlyphOrigin.y,
+ (int)gm.gmBlackBoxX, (int)gm.gmBlackBoxY, gm.gmCellIncX, gm.gmCellIncY);
+ }
+ }
+ return glyph_metrics_t();
+#else
+ HDC hdc = shared_dc();
+ HGDIOBJ oldFont = SelectObject(hdc, hfont);
+
+ ABC abc;
+ int width;
+ int advance;
+#ifdef GWES_MGTT // true type fonts
+ if (GetCharABCWidths(hdc, glyph, glyph, &abc)) {
+ width = qAbs(abc.abcA) + abc.abcB + qAbs(abc.abcC);
+ advance = abc.abcA + abc.abcB + abc.abcC;
+ }
+ else
+#endif
+#if defined(GWES_MGRAST) || defined(GWES_MGRAST2) // raster fonts
+ if (GetCharWidth32(hdc, glyph, glyph, &width)) {
+ advance = width;
+ }
+ else
+#endif
+ { // fallback
+ width = tm.w.tmMaxCharWidth;
+ advance = width;
+ }
+
+ SelectObject(hdc, oldFont);
+ return glyph_metrics_t(0, -tm.w.tmAscent, width, tm.w.tmHeight, advance, 0).transformed(t);
+#endif
+}
+
+QFixed QFontEngineWin::ascent() const
+{
+ return tm.w.tmAscent;
+}
+
+QFixed QFontEngineWin::descent() const
+{
+ return tm.w.tmDescent;
+}
+
+QFixed QFontEngineWin::leading() const
+{
+ return tm.w.tmExternalLeading;
+}
+
+
+QFixed QFontEngineWin::xHeight() const
+{
+ if(x_height >= 0)
+ return x_height;
+ return QFontEngine::xHeight();
+}
+
+QFixed QFontEngineWin::averageCharWidth() const
+{
+ return tm.w.tmAveCharWidth;
+}
+
+qreal QFontEngineWin::maxCharWidth() const
+{
+ return tm.w.tmMaxCharWidth;
+}
+
+enum { max_font_count = 256 };
+static const ushort char_table[] = {
+ 40,
+ 67,
+ 70,
+ 75,
+ 86,
+ 88,
+ 89,
+ 91,
+ 102,
+ 114,
+ 124,
+ 127,
+ 205,
+ 645,
+ 884,
+ 922,
+ 1070,
+ 12386,
+ 0
+};
+
+static const int char_table_entries = sizeof(char_table)/sizeof(ushort);
+
+
+qreal QFontEngineWin::minLeftBearing() const
+{
+ if (lbearing == SHRT_MIN)
+ minRightBearing(); // calculates both
+
+ return lbearing;
+}
+
+qreal QFontEngineWin::minRightBearing() const
+{
+#ifdef Q_OS_WINCE
+ if (rbearing == SHRT_MIN) {
+ int ml = 0;
+ int mr = 0;
+ HDC hdc = shared_dc();
+ SelectObject(hdc, hfont);
+ if (ttf) {
+ ABC *abc = 0;
+ int n = QT_WA_INLINE(tm.w.tmLastChar - tm.w.tmFirstChar, tm.a.tmLastChar - tm.a.tmFirstChar);
+ if (n <= max_font_count) {
+ abc = new ABC[n+1];
+ GetCharABCWidths(hdc, tm.w.tmFirstChar, tm.w.tmLastChar, abc);
+ } else {
+ abc = new ABC[char_table_entries+1];
+ for(int i = 0; i < char_table_entries; i++)
+ GetCharABCWidths(hdc, char_table[i], char_table[i], abc+i);
+ n = char_table_entries;
+ }
+ ml = abc[0].abcA;
+ mr = abc[0].abcC;
+ for (int i = 1; i < n; i++) {
+ if (abc[i].abcA + abc[i].abcB + abc[i].abcC != 0) {
+ ml = qMin(ml,abc[i].abcA);
+ mr = qMin(mr,abc[i].abcC);
+ }
+ }
+ delete [] abc;
+ } else {
+ ml = 0;
+ mr = -tm.a.tmOverhang;
+ }
+ lbearing = ml;
+ rbearing = mr;
+ }
+
+ return rbearing;
+#else
+ if (rbearing == SHRT_MIN) {
+ int ml = 0;
+ int mr = 0;
+ HDC hdc = shared_dc();
+ SelectObject(hdc, hfont);
+ if (ttf) {
+ ABC *abc = 0;
+ int n = QT_WA_INLINE(tm.w.tmLastChar - tm.w.tmFirstChar, tm.a.tmLastChar - tm.a.tmFirstChar);
+ if (n <= max_font_count) {
+ abc = new ABC[n+1];
+ QT_WA({
+ GetCharABCWidths(hdc, tm.w.tmFirstChar, tm.w.tmLastChar, abc);
+ }, {
+ GetCharABCWidthsA(hdc,tm.a.tmFirstChar,tm.a.tmLastChar,abc);
+ });
+ } else {
+ abc = new ABC[char_table_entries+1];
+ QT_WA({
+ for(int i = 0; i < char_table_entries; i++)
+ GetCharABCWidths(hdc, char_table[i], char_table[i], abc+i);
+ }, {
+ for(int i = 0; i < char_table_entries; i++) {
+ QByteArray w = QString(QChar(char_table[i])).toLocal8Bit();
+ if (w.length() == 1) {
+ uint ch8 = (uchar)w[0];
+ GetCharABCWidthsA(hdc, ch8, ch8, abc+i);
+ }
+ }
+ });
+ n = char_table_entries;
+ }
+ ml = abc[0].abcA;
+ mr = abc[0].abcC;
+ for (int i = 1; i < n; i++) {
+ if (abc[i].abcA + abc[i].abcB + abc[i].abcC != 0) {
+ ml = qMin(ml,abc[i].abcA);
+ mr = qMin(mr,abc[i].abcC);
+ }
+ }
+ delete [] abc;
+ } else {
+ QT_WA({
+ ABCFLOAT *abc = 0;
+ int n = tm.w.tmLastChar - tm.w.tmFirstChar+1;
+ if (n <= max_font_count) {
+ abc = new ABCFLOAT[n];
+ GetCharABCWidthsFloat(hdc, tm.w.tmFirstChar, tm.w.tmLastChar, abc);
+ } else {
+ abc = new ABCFLOAT[char_table_entries];
+ for(int i = 0; i < char_table_entries; i++)
+ GetCharABCWidthsFloat(hdc, char_table[i], char_table[i], abc+i);
+ n = char_table_entries;
+ }
+ float fml = abc[0].abcfA;
+ float fmr = abc[0].abcfC;
+ for (int i=1; i<n; i++) {
+ if (abc[i].abcfA + abc[i].abcfB + abc[i].abcfC != 0) {
+ fml = qMin(fml,abc[i].abcfA);
+ fmr = qMin(fmr,abc[i].abcfC);
+ }
+ }
+ ml = int(fml-0.9999);
+ mr = int(fmr-0.9999);
+ delete [] abc;
+ } , {
+ ml = 0;
+ mr = -tm.a.tmOverhang;
+ });
+ }
+ lbearing = ml;
+ rbearing = mr;
+ }
+
+ return rbearing;
+#endif
+}
+
+
+const char *QFontEngineWin::name() const
+{
+ return 0;
+}
+
+bool QFontEngineWin::canRender(const QChar *string, int len)
+{
+ if (symbol) {
+ for (int i = 0; i < len; ++i) {
+ unsigned int uc = getChar(string, i, len);
+ if (getTrueTypeGlyphIndex(cmap, uc) == 0) {
+ if (uc < 0x100) {
+ if (getTrueTypeGlyphIndex(cmap, uc + 0xf000) == 0)
+ return false;
+ } else {
+ return false;
+ }
+ }
+ }
+ } else if (ttf) {
+ for (int i = 0; i < len; ++i) {
+ unsigned int uc = getChar(string, i, len);
+ if (getTrueTypeGlyphIndex(cmap, uc) == 0)
+ return false;
+ }
+ } else {
+ QT_WA({
+ while(len--) {
+ if (tm.w.tmFirstChar > string->unicode() || tm.w.tmLastChar < string->unicode())
+ return false;
+ }
+ }, {
+ while(len--) {
+ if (tm.a.tmFirstChar > string->unicode() || tm.a.tmLastChar < string->unicode())
+ return false;
+ }
+ });
+ }
+ return true;
+}
+
+QFontEngine::Type QFontEngineWin::type() const
+{
+ return QFontEngine::Win;
+}
+
+static inline double qt_fixed_to_double(const FIXED &p) {
+ return ((p.value << 16) + p.fract) / 65536.0;
+}
+
+static inline QPointF qt_to_qpointf(const POINTFX &pt, qreal scale) {
+ return QPointF(qt_fixed_to_double(pt.x) * scale, -qt_fixed_to_double(pt.y) * scale);
+}
+
+#ifndef GGO_UNHINTED
+#define GGO_UNHINTED 0x0100
+#endif
+
+static bool addGlyphToPath(glyph_t glyph, const QFixedPoint &position, HDC hdc,
+ QPainterPath *path, bool ttf, glyph_metrics_t *metric = 0, qreal scale = 1)
+{
+#if defined(Q_OS_WINCE)
+ Q_UNUSED(glyph);
+ Q_UNUSED(hdc);
+#endif
+ MAT2 mat;
+ mat.eM11.value = mat.eM22.value = 1;
+ mat.eM11.fract = mat.eM22.fract = 0;
+ mat.eM21.value = mat.eM12.value = 0;
+ mat.eM21.fract = mat.eM12.fract = 0;
+ uint glyphFormat = GGO_NATIVE;
+
+ if (ttf)
+ glyphFormat |= GGO_GLYPH_INDEX;
+
+ GLYPHMETRICS gMetric;
+ memset(&gMetric, 0, sizeof(GLYPHMETRICS));
+ int bufferSize = GDI_ERROR;
+#if !defined(Q_OS_WINCE)
+ QT_WA( {
+ bufferSize = GetGlyphOutlineW(hdc, glyph, glyphFormat, &gMetric, 0, 0, &mat);
+ }, {
+ bufferSize = GetGlyphOutlineA(hdc, glyph, glyphFormat, &gMetric, 0, 0, &mat);
+ });
+#endif
+ if ((DWORD)bufferSize == GDI_ERROR) {
+ return false;
+ }
+
+ void *dataBuffer = new char[bufferSize];
+ DWORD ret = GDI_ERROR;
+#if !defined(Q_OS_WINCE)
+ QT_WA( {
+ ret = GetGlyphOutlineW(hdc, glyph, glyphFormat, &gMetric, bufferSize,
+ dataBuffer, &mat);
+ }, {
+ ret = GetGlyphOutlineA(hdc, glyph, glyphFormat, &gMetric, bufferSize,
+ dataBuffer, &mat);
+ } );
+#endif
+ if (ret == GDI_ERROR) {
+ delete [](char *)dataBuffer;
+ return false;
+ }
+
+ if(metric) {
+ // #### obey scale
+ *metric = glyph_metrics_t(gMetric.gmptGlyphOrigin.x, -gMetric.gmptGlyphOrigin.y,
+ (int)gMetric.gmBlackBoxX, (int)gMetric.gmBlackBoxY,
+ gMetric.gmCellIncX, gMetric.gmCellIncY);
+ }
+
+ int offset = 0;
+ int headerOffset = 0;
+ TTPOLYGONHEADER *ttph = 0;
+
+ QPointF oset = position.toPointF();
+ while (headerOffset < bufferSize) {
+ ttph = (TTPOLYGONHEADER*)((char *)dataBuffer + headerOffset);
+
+ QPointF lastPoint(qt_to_qpointf(ttph->pfxStart, scale));
+ path->moveTo(lastPoint + oset);
+ offset += sizeof(TTPOLYGONHEADER);
+ TTPOLYCURVE *curve;
+ while (offset<int(headerOffset + ttph->cb)) {
+ curve = (TTPOLYCURVE*)((char*)(dataBuffer) + offset);
+ switch (curve->wType) {
+ case TT_PRIM_LINE: {
+ for (int i=0; i<curve->cpfx; ++i) {
+ QPointF p = qt_to_qpointf(curve->apfx[i], scale) + oset;
+ path->lineTo(p);
+ }
+ break;
+ }
+ case TT_PRIM_QSPLINE: {
+ const QPainterPath::Element &elm = path->elementAt(path->elementCount()-1);
+ QPointF prev(elm.x, elm.y);
+ QPointF endPoint;
+ for (int i=0; i<curve->cpfx - 1; ++i) {
+ QPointF p1 = qt_to_qpointf(curve->apfx[i], scale) + oset;
+ QPointF p2 = qt_to_qpointf(curve->apfx[i+1], scale) + oset;
+ if (i < curve->cpfx - 2) {
+ endPoint = QPointF((p1.x() + p2.x()) / 2, (p1.y() + p2.y()) / 2);
+ } else {
+ endPoint = p2;
+ }
+
+ path->quadTo(p1, endPoint);
+ prev = endPoint;
+ }
+
+ break;
+ }
+ case TT_PRIM_CSPLINE: {
+ for (int i=0; i<curve->cpfx; ) {
+ QPointF p2 = qt_to_qpointf(curve->apfx[i++], scale) + oset;
+ QPointF p3 = qt_to_qpointf(curve->apfx[i++], scale) + oset;
+ QPointF p4 = qt_to_qpointf(curve->apfx[i++], scale) + oset;
+ path->cubicTo(p2, p3, p4);
+ }
+ break;
+ }
+ default:
+ qWarning("QFontEngineWin::addOutlineToPath, unhandled switch case");
+ }
+ offset += sizeof(TTPOLYCURVE) + (curve->cpfx-1) * sizeof(POINTFX);
+ }
+ path->closeSubpath();
+ headerOffset += ttph->cb;
+ }
+ delete [] (char*)dataBuffer;
+
+ return true;
+}
+
+void QFontEngineWin::addGlyphsToPath(glyph_t *glyphs, QFixedPoint *positions, int nglyphs,
+ QPainterPath *path, QTextItem::RenderFlags)
+{
+ LOGFONT lf = logfont;
+ // The sign must be negative here to make sure we match against character height instead of
+ // hinted cell height. This ensures that we get linear matching, and we need this for
+ // paths since we later on apply a scaling transform to the glyph outline to get the
+ // font at the correct pixel size.
+ lf.lfHeight = -unitsPerEm;
+ lf.lfWidth = 0;
+ HFONT hf;
+ QT_WA({
+ hf = CreateFontIndirectW(&lf);
+ }, {
+ LOGFONTA lfa;
+ wa_copy_logfont(&lf, &lfa);
+ hf = CreateFontIndirectA(&lfa);
+ });
+ HDC hdc = shared_dc();
+ HGDIOBJ oldfont = SelectObject(hdc, hf);
+
+ for(int i = 0; i < nglyphs; ++i) {
+ if (!addGlyphToPath(glyphs[i], positions[i], hdc, path, ttf, /*metric*/0,
+ qreal(fontDef.pixelSize) / unitsPerEm)) {
+ // Some windows fonts, like "Modern", are vector stroke
+ // fonts, which are reported as TMPF_VECTOR but do not
+ // support GetGlyphOutline, and thus we set this bit so
+ // that addOutLineToPath can check it and return safely...
+ hasOutline = false;
+ break;
+ }
+ }
+ DeleteObject(SelectObject(hdc, oldfont));
+}
+
+void QFontEngineWin::addOutlineToPath(qreal x, qreal y, const QGlyphLayout &glyphs,
+ QPainterPath *path, QTextItem::RenderFlags flags)
+{
+#if !defined(Q_OS_WINCE)
+ if(tm.w.tmPitchAndFamily & (TMPF_TRUETYPE | TMPF_VECTOR)) {
+ hasOutline = true;
+ QFontEngine::addOutlineToPath(x, y, glyphs, path, flags);
+ if (hasOutline) {
+ // has_outline is set to false if addGlyphToPath gets
+ // false from GetGlyphOutline, meaning its not an outline
+ // font.
+ return;
+ }
+ }
+#endif
+ QFontEngine::addBitmapFontToPath(x, y, glyphs, path, flags);
+}
+
+QFontEngine::FaceId QFontEngineWin::faceId() const
+{
+ return _faceId;
+}
+
+QT_BEGIN_INCLUDE_NAMESPACE
+#include <qdebug.h>
+QT_END_INCLUDE_NAMESPACE
+
+int QFontEngineWin::synthesized() const
+{
+ if(synthesized_flags == -1) {
+ synthesized_flags = 0;
+ if(ttf) {
+ const DWORD HEAD = MAKE_TAG('h', 'e', 'a', 'd');
+ HDC hdc = shared_dc();
+ SelectObject(hdc, hfont);
+ uchar data[4];
+ GetFontData(hdc, HEAD, 44, &data, 4);
+ USHORT macStyle = getUShort(data);
+ if (tm.w.tmItalic && !(macStyle & 2))
+ synthesized_flags = SynthesizedItalic;
+ if (fontDef.stretch != 100 && ttf)
+ synthesized_flags |= SynthesizedStretch;
+ if (tm.w.tmWeight >= 500 && !(macStyle & 1))
+ synthesized_flags |= SynthesizedBold;
+ //qDebug() << "font is" << _name <<
+ // "it=" << (macStyle & 2) << fontDef.style << "flags=" << synthesized_flags;
+ }
+ }
+ return synthesized_flags;
+}
+
+QFixed QFontEngineWin::emSquareSize() const
+{
+ return unitsPerEm;
+}
+
+QFontEngine::Properties QFontEngineWin::properties() const
+{
+
+ LOGFONT lf = logfont;
+ lf.lfHeight = unitsPerEm;
+ HFONT hf;
+ QT_WA({
+ hf = CreateFontIndirectW(&lf);
+ }, {
+ LOGFONTA lfa;
+ wa_copy_logfont(&lf, &lfa);
+ hf = CreateFontIndirectA(&lfa);
+ });
+ HDC hdc = shared_dc();
+ HGDIOBJ oldfont = SelectObject(hdc, hf);
+#if defined(Q_OS_WINCE)
+ OUTLINETEXTMETRICW *otm = getOutlineTextMetric(hdc);
+#else
+ OUTLINETEXTMETRICA *otm = getOutlineTextMetric(hdc);
+#endif
+ Properties p;
+ p.emSquare = unitsPerEm;
+ p.italicAngle = otm->otmItalicAngle;
+ p.postscriptName = (char *)otm + (int)otm->otmpFamilyName;
+ p.postscriptName += (char *)otm + (int)otm->otmpStyleName;
+#ifndef QT_NO_PRINTER
+ p.postscriptName = QPdf::stripSpecialCharacters(p.postscriptName);
+#endif
+ p.boundingBox = QRectF(otm->otmrcFontBox.left, -otm->otmrcFontBox.top,
+ otm->otmrcFontBox.right - otm->otmrcFontBox.left,
+ otm->otmrcFontBox.top - otm->otmrcFontBox.bottom);
+ p.ascent = otm->otmAscent;
+ p.descent = -otm->otmDescent;
+ p.leading = (int)otm->otmLineGap;
+ p.capHeight = 0;
+ p.lineWidth = otm->otmsUnderscoreSize;
+ free(otm);
+ DeleteObject(SelectObject(hdc, oldfont));
+ return p;
+}
+
+void QFontEngineWin::getUnscaledGlyph(glyph_t glyph, QPainterPath *path, glyph_metrics_t *metrics)
+{
+ LOGFONT lf = logfont;
+ lf.lfHeight = unitsPerEm;
+ int flags = synthesized();
+ if(flags & SynthesizedItalic)
+ lf.lfItalic = false;
+ lf.lfWidth = 0;
+ HFONT hf;
+ QT_WA({
+ hf = CreateFontIndirectW(&lf);
+ }, {
+ LOGFONTA lfa;
+ wa_copy_logfont(&lf, &lfa);
+ hf = CreateFontIndirectA(&lfa);
+ });
+ HDC hdc = shared_dc();
+ HGDIOBJ oldfont = SelectObject(hdc, hf);
+ QFixedPoint p;
+ p.x = 0;
+ p.y = 0;
+ addGlyphToPath(glyph, p, hdc, path, ttf, metrics);
+ DeleteObject(SelectObject(hdc, oldfont));
+}
+
+bool QFontEngineWin::getSfntTableData(uint tag, uchar *buffer, uint *length) const
+{
+ if (!ttf)
+ return false;
+ HDC hdc = shared_dc();
+ SelectObject(hdc, hfont);
+ DWORD t = qbswap<quint32>(tag);
+ *length = GetFontData(hdc, t, 0, buffer, *length);
+ return *length != GDI_ERROR;
+}
+
+#if !defined(CLEARTYPE_QUALITY)
+# define CLEARTYPE_QUALITY 5
+#endif
+
+
+QNativeImage *QFontEngineWin::drawGDIGlyph(HFONT font, glyph_t glyph, int margin,
+ const QTransform &t)
+{
+ glyph_metrics_t gm = boundingBox(glyph);
+
+// printf(" -> for glyph %4x\n", glyph);
+
+ int gx = gm.x.toInt();
+ int gy = gm.y.toInt();
+ int iw = gm.width.toInt();
+ int ih = gm.height.toInt();
+
+ if (iw <= 0 || iw <= 0)
+ return 0;
+
+ bool has_transformation = t.type() > QTransform::TxTranslate;
+
+#ifndef Q_OS_WINCE
+ unsigned int options = ttf ? ETO_GLYPH_INDEX : 0;
+ XFORM xform;
+
+ if (has_transformation) {
+ xform.eM11 = t.m11();
+ xform.eM12 = t.m12();
+ xform.eM21 = t.m21();
+ xform.eM22 = t.m22();
+ xform.eDx = margin;
+ xform.eDy = margin;
+
+ QtHDC qthdc;
+ HDC hdc = qthdc.hdc();
+
+ SetGraphicsMode(hdc, GM_ADVANCED);
+ SetWorldTransform(hdc, &xform);
+ HGDIOBJ old_font = SelectObject(hdc, font);
+
+ int ggo_options = GGO_METRICS | (ttf ? GGO_GLYPH_INDEX : 0);
+ GLYPHMETRICS tgm;
+ MAT2 mat;
+ memset(&mat, 0, sizeof(mat));
+ mat.eM11.value = mat.eM22.value = 1;
+
+ int error = 0;
+ QT_WA( {
+ error = GetGlyphOutlineW(hdc, glyph, ggo_options, &tgm, 0, 0, &mat);
+ }, {
+ error = GetGlyphOutlineA(hdc, glyph, ggo_options, &tgm, 0, 0, &mat);
+ } );
+
+ if (error == GDI_ERROR) {
+ qWarning("QWinFontEngine: unable to query transformed glyph metrics...");
+ return 0;
+ }
+
+ iw = tgm.gmBlackBoxX;
+ ih = tgm.gmBlackBoxY;
+
+ xform.eDx -= tgm.gmptGlyphOrigin.x;
+ xform.eDy += tgm.gmptGlyphOrigin.y;
+
+ SetGraphicsMode(hdc, GM_COMPATIBLE);
+ SelectObject(hdc, old_font);
+ }
+#else // else winc
+ unsigned int options = 0;
+#ifdef DEBUG
+ Q_ASSERT(!has_transformation);
+#else
+ Q_UNUSED(has_transformation);
+#endif
+#endif
+
+ QNativeImage *ni = new QNativeImage(iw + 2 * margin,
+ ih + 2 * margin,
+ QNativeImage::systemFormat(), true);
+ ni->image.fill(0xffffffff);
+
+ HDC hdc = ni->hdc;
+
+ SelectObject(hdc, GetStockObject(NULL_BRUSH));
+ SelectObject(hdc, GetStockObject(BLACK_PEN));
+ SetTextColor(hdc, RGB(0,0,0));
+ SetBkMode(hdc, TRANSPARENT);
+ SetTextAlign(hdc, TA_BASELINE);
+
+ HGDIOBJ old_font = SelectObject(hdc, font);
+
+#ifndef Q_OS_WINCE
+ if (has_transformation) {
+ SetGraphicsMode(hdc, GM_ADVANCED);
+ SetWorldTransform(hdc, &xform);
+ ExtTextOutW(hdc, 0, 0, options, 0, (LPCWSTR) &glyph, 1, 0);
+ } else
+#endif
+ {
+ ExtTextOutW(hdc, -gx + margin, -gy + margin, options, 0, (LPCWSTR) &glyph, 1, 0);
+ }
+
+ SelectObject(hdc, old_font);
+ return ni;
+}
+
+
+extern bool qt_cleartype_enabled;
+extern uint qt_pow_gamma[256];
+
+QImage QFontEngineWin::alphaMapForGlyph(glyph_t glyph, const QTransform &xform)
+{
+ HFONT font = hfont;
+ if (qt_cleartype_enabled) {
+ LOGFONT lf = logfont;
+ lf.lfQuality = ANTIALIASED_QUALITY;
+ font = CreateFontIndirectW(&lf);
+ }
+
+ QNativeImage *mask = drawGDIGlyph(font, glyph, 0, xform);
+ if (mask == 0)
+ return QImage();
+
+ QImage indexed(mask->width(), mask->height(), QImage::Format_Indexed8);
+
+ // ### This part is kinda pointless, but we'll crash later if we dont because some
+ // code paths expects there to be colortables for index8-bit...
+ QVector<QRgb> colors(256);
+ for (int i=0; i<256; ++i)
+ colors[i] = qRgba(0, 0, 0, i);
+ indexed.setColorTable(colors);
+
+ // Copy data... Cannot use QPainter here as GDI has messed up the
+ // Alpha channel of the ni.image pixels...
+ for (int y=0; y<mask->height(); ++y) {
+ uchar *dest = indexed.scanLine(y);
+ if (mask->systemFormat() == QImage::Format_RGB16) {
+ const qint16 *src = (qint16 *) ((const QImage &) mask->image).scanLine(y);
+ for (int x=0; x<mask->width(); ++x) {
+#ifdef Q_OS_WINCE
+ dest[x] = 255 - qGray(src[x]);
+#else
+ dest[x] = 255 - (qt_pow_gamma[qGray(src[x])] * 255. / 2047.);
+#endif
+ }
+ } else {
+ const uint *src = (uint *) ((const QImage &) mask->image).scanLine(y);
+ for (int x=0; x<mask->width(); ++x) {
+#ifdef Q_OS_WINCE
+ dest[x] = 255 - qGray(src[x]);
+#else
+ dest[x] = 255 - (qt_pow_gamma[qGray(src[x])] * 255. / 2047.);
+#endif
+ }
+ }
+ }
+
+ // Cleanup...
+ delete mask;
+ if (qt_cleartype_enabled) {
+ DeleteObject(font);
+ }
+
+ return indexed;
+}
+
+#define SPI_GETFONTSMOOTHINGCONTRAST 0x200C
+#define SPI_SETFONTSMOOTHINGCONTRAST 0x200D
+
+QImage QFontEngineWin::alphaRGBMapForGlyph(glyph_t glyph, int margin, const QTransform &t)
+{
+ HFONT font = hfont;
+
+ int contrast;
+ SystemParametersInfo(SPI_GETFONTSMOOTHINGCONTRAST, 0, &contrast, 0);
+ SystemParametersInfo(SPI_SETFONTSMOOTHINGCONTRAST, 0, (void *) 1000, 0);
+
+ QNativeImage *mask = drawGDIGlyph(font, glyph, margin, t);
+ SystemParametersInfo(SPI_SETFONTSMOOTHINGCONTRAST, 0, (void *) contrast, 0);
+
+ if (mask == 0)
+ return QImage();
+
+ // Gracefully handle the odd case when the display is 16-bit
+ const QImage source = mask->image.depth() == 32
+ ? mask->image
+ : mask->image.convertToFormat(QImage::Format_RGB32);
+
+ QImage rgbMask(mask->width(), mask->height(), QImage::Format_RGB32);
+ for (int y=0; y<mask->height(); ++y) {
+ uint *dest = (uint *) rgbMask.scanLine(y);
+ const uint *src = (uint *) source.scanLine(y);
+ for (int x=0; x<mask->width(); ++x) {
+ dest[x] = 0xffffffff - (0x00ffffff & src[x]);
+ }
+ }
+
+ delete mask;
+
+ return rgbMask;
+}
+
+// -------------------------------------- Multi font engine
+
+QFontEngineMultiWin::QFontEngineMultiWin(QFontEngineWin *first, const QStringList &fallbacks)
+ : QFontEngineMulti(fallbacks.size()+1),
+ fallbacks(fallbacks)
+{
+ engines[0] = first;
+ first->ref.ref();
+ fontDef = engines[0]->fontDef;
+}
+
+void QFontEngineMultiWin::loadEngine(int at)
+{
+ Q_ASSERT(at < engines.size());
+ Q_ASSERT(engines.at(at) == 0);
+
+ QString fam = fallbacks.at(at-1);
+
+ LOGFONT lf = static_cast<QFontEngineWin *>(engines.at(0))->logfont;
+ HFONT hfont;
+ QT_WA({
+ memcpy(lf.lfFaceName, fam.utf16(), sizeof(TCHAR)*qMin(fam.length()+1,32)); // 32 = Windows hard-coded
+ hfont = CreateFontIndirectW(&lf);
+ } , {
+ // LOGFONTA and LOGFONTW are binary compatible
+ QByteArray lname = fam.toLocal8Bit();
+ memcpy(lf.lfFaceName,lname.data(),
+ qMin(lname.length()+1,32)); // 32 = Windows hard-coded
+ hfont = CreateFontIndirectA((LOGFONTA*)&lf);
+ });
+ bool stockFont = false;
+ if (hfont == 0) {
+ hfont = (HFONT)GetStockObject(ANSI_VAR_FONT);
+ stockFont = true;
+ }
+ engines[at] = new QFontEngineWin(fam, hfont, stockFont, lf);
+ engines[at]->ref.ref();
+ engines[at]->fontDef = fontDef;
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/text/qfontengine_win_p.h b/src/gui/text/qfontengine_win_p.h
new file mode 100644
index 0000000000..6f37e91bbf
--- /dev/null
+++ b/src/gui/text/qfontengine_win_p.h
@@ -0,0 +1,156 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QFONTENGINE_WIN_P_H
+#define QFONTENGINE_WIN_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 <QtCore/qconfig.h>
+
+QT_BEGIN_NAMESPACE
+
+class QNativeImage;
+
+class QFontEngineWin : public QFontEngine
+{
+public:
+ QFontEngineWin(const QString &name, HFONT, bool, LOGFONT);
+ ~QFontEngineWin();
+
+ virtual QFixed lineThickness() const;
+ virtual Properties properties() const;
+ virtual void getUnscaledGlyph(glyph_t glyph, QPainterPath *path, glyph_metrics_t *metrics);
+ virtual FaceId faceId() const;
+ virtual bool getSfntTableData(uint tag, uchar *buffer, uint *length) const;
+ virtual int synthesized() const;
+ virtual QFixed emSquareSize() const;
+
+ virtual bool stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs, int *nglyphs, QTextEngine::ShaperFlags flags) const;
+ virtual void recalcAdvances(QGlyphLayout *glyphs, QTextEngine::ShaperFlags) const;
+
+ virtual void addOutlineToPath(qreal x, qreal y, const QGlyphLayout &glyphs, QPainterPath *path, QTextItem::RenderFlags flags);
+ virtual void addGlyphsToPath(glyph_t *glyphs, QFixedPoint *positions, int nglyphs,
+ QPainterPath *path, QTextItem::RenderFlags flags);
+
+ HGDIOBJ selectDesignFont(QFixed *) const;
+
+ virtual glyph_metrics_t boundingBox(const QGlyphLayout &glyphs);
+ virtual glyph_metrics_t boundingBox(glyph_t g) { return boundingBox(g, QTransform()); }
+ virtual glyph_metrics_t boundingBox(glyph_t g, const QTransform &t);
+
+
+ virtual QFixed ascent() const;
+ virtual QFixed descent() const;
+ virtual QFixed leading() const;
+ virtual QFixed xHeight() const;
+ virtual QFixed averageCharWidth() const;
+ virtual qreal maxCharWidth() const;
+ virtual qreal minLeftBearing() const;
+ virtual qreal minRightBearing() const;
+
+ virtual const char *name() const;
+
+ bool canRender(const QChar *string, int len);
+
+ Type type() const;
+
+ virtual QImage alphaMapForGlyph(glyph_t t) { return alphaMapForGlyph(t, QTransform()); }
+ virtual QImage alphaMapForGlyph(glyph_t, const QTransform &xform);
+ virtual QImage alphaRGBMapForGlyph(glyph_t t, int margin, const QTransform &xform);
+
+ int getGlyphIndexes(const QChar *ch, int numChars, QGlyphLayout *glyphs, bool mirrored) const;
+ void getCMap();
+
+ QString _name;
+ HFONT hfont;
+ LOGFONT logfont;
+ uint stockFont : 1;
+ uint useTextOutA : 1;
+ uint ttf : 1;
+ uint hasOutline : 1;
+ union {
+ TEXTMETRICW w;
+ TEXTMETRICA a;
+ } tm;
+ int lw;
+ const unsigned char *cmap;
+ QByteArray cmapTable;
+ mutable qreal lbearing;
+ mutable qreal rbearing;
+ QFixed designToDevice;
+ int unitsPerEm;
+ QFixed x_height;
+ FaceId _faceId;
+
+ mutable int synthesized_flags;
+ mutable QFixed lineWidth;
+ mutable unsigned char *widthCache;
+ mutable uint widthCacheSize;
+ mutable QFixed *designAdvances;
+ mutable int designAdvancesSize;
+
+private:
+ QNativeImage *drawGDIGlyph(HFONT font, glyph_t, int margin, const QTransform &xform);
+
+};
+
+class QFontEngineMultiWin : public QFontEngineMulti
+{
+public:
+ QFontEngineMultiWin(QFontEngineWin *first, const QStringList &fallbacks);
+ void loadEngine(int at);
+
+ QStringList fallbacks;
+};
+
+QT_END_NAMESPACE
+
+#endif // QFONTENGINE_WIN_P_H
diff --git a/src/gui/text/qfontengine_x11.cpp b/src/gui/text/qfontengine_x11.cpp
new file mode 100644
index 0000000000..0972b2b345
--- /dev/null
+++ b/src/gui/text/qfontengine_x11.cpp
@@ -0,0 +1,1180 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qbitmap.h"
+
+// #define FONTENGINE_DEBUG
+
+#include <qapplication.h>
+#include <qbytearray.h>
+#include <qdebug.h>
+#include <qtextcodec.h>
+#include <qthread.h>
+
+#include "qfontdatabase.h"
+#include "qpaintdevice.h"
+#include "qpainter.h"
+#include "qvarlengtharray.h"
+#include "qwidget.h"
+#include "qsettings.h"
+#include "qfile.h"
+
+#include <private/qpaintengine_x11_p.h>
+#include "qfont.h"
+#include "qfont_p.h"
+#include "qfontengine_p.h"
+#include <qhash.h>
+
+#include <private/qpainter_p.h>
+#include <private/qunicodetables_p.h>
+
+#include <private/qt_x11_p.h>
+#include <private/qpixmap_x11_p.h>
+#include "qx11info_x11.h"
+#include "qfontengine_x11_p.h"
+
+#include <limits.h>
+
+#include <ft2build.h>
+#if defined(FT_LCD_FILTER_H)
+#include FT_LCD_FILTER_H
+#endif
+
+#if defined(FC_LCD_FILTER)
+
+#ifndef FC_LCD_FILTER_NONE
+#define FC_LCD_FILTER_NONE FC_LCD_NONE
+#endif
+
+#ifndef FC_LCD_FILTER_DEFAULT
+#define FC_LCD_FILTER_DEFAULT FC_LCD_DEFAULT
+#endif
+
+#ifndef FC_LCD_FILTER_LIGHT
+#define FC_LCD_FILTER_LIGHT FC_LCD_LIGHT
+#endif
+
+#ifndef FC_LCD_FILTER_LEGACY
+#define FC_LCD_FILTER_LEGACY FC_LCD_LEGACY
+#endif
+
+#endif
+
+QT_BEGIN_NAMESPACE
+
+
+// ------------------------------------------------------------------
+// Multi XLFD engine
+// ------------------------------------------------------------------
+
+QFontEngineMultiXLFD::QFontEngineMultiXLFD(const QFontDef &r, const QList<int> &l, int s)
+ : QFontEngineMulti(l.size()), encodings(l), screen(s), request(r)
+{
+ loadEngine(0);
+ fontDef = engines[0]->fontDef;
+}
+
+QFontEngineMultiXLFD::~QFontEngineMultiXLFD()
+{ }
+
+void QFontEngineMultiXLFD::loadEngine(int at)
+{
+ Q_ASSERT(at < engines.size());
+ Q_ASSERT(engines.at(at) == 0);
+ const int encoding = encodings.at(at);
+ QFontEngine *fontEngine = QFontDatabase::loadXlfd(0, QUnicodeTables::Common, request, encoding);
+ Q_ASSERT(fontEngine != 0);
+ fontEngine->ref.ref();
+ engines[at] = fontEngine;
+}
+
+// ------------------------------------------------------------------
+// Xlfd font engine
+// ------------------------------------------------------------------
+
+#ifndef QT_NO_FREETYPE
+
+static QStringList *qt_fontpath = 0;
+
+static QStringList fontPath()
+{
+ if (qt_fontpath)
+ return *qt_fontpath;
+
+ // append qsettings fontpath
+ QSettings settings(QSettings::UserScope, QLatin1String("Trolltech"));
+ settings.beginGroup(QLatin1String("Qt"));
+
+ QStringList fontpath;
+
+ int npaths;
+ char** font_path;
+ font_path = XGetFontPath(X11->display, &npaths);
+ bool xfsconfig_read = false;
+ for (int i=0; i<npaths; i++) {
+ // If we're using xfs, append font paths from /etc/X11/fs/config
+ // can't hurt, and chances are we'll get all fonts that way.
+ if (((font_path[i])[0] != '/') && !xfsconfig_read) {
+ // We're using xfs -> read its config
+ bool finished = false;
+ QFile f(QLatin1String("/etc/X11/fs/config"));
+ if (!f.exists())
+ f.setFileName(QLatin1String("/usr/X11R6/lib/X11/fs/config"));
+ if (!f.exists())
+ f.setFileName(QLatin1String("/usr/X11/lib/X11/fs/config"));
+ if (f.exists()) {
+ f.open(QIODevice::ReadOnly);
+ while (f.error()==QFile::NoError && !finished) {
+ QString fs = QString::fromLocal8Bit(f.readLine(1024));
+ fs=fs.trimmed();
+ if (fs.left(9)==QLatin1String("catalogue") && fs.contains(QLatin1Char('='))) {
+ fs = fs.mid(fs.indexOf(QLatin1Char('=')) + 1).trimmed();
+ bool end = false;
+ while (f.error()==QFile::NoError && !end) {
+ if (fs[int(fs.length())-1] == QLatin1Char(','))
+ fs = fs.left(fs.length()-1);
+ else
+ end = true;
+
+ fs = fs.left(fs.indexOf(QLatin1String(":unscaled")));
+ if (fs[0] != QLatin1Char('#'))
+ fontpath += fs;
+ fs = QLatin1String(f.readLine(1024));
+ fs = fs.trimmed();
+ if (fs.isEmpty())
+ end = true;
+ }
+ finished = true;
+ }
+ }
+ f.close();
+ }
+ xfsconfig_read = true;
+ } else {
+ QString fs = QString::fromLocal8Bit(font_path[i]);
+ fontpath += fs.left(fs.indexOf(QLatin1String(":unscaled")));
+ }
+ }
+ XFreeFontPath(font_path);
+
+ // append qsettings fontpath
+ QStringList fp = settings.value(QLatin1String("fontPath")).toStringList();
+ if (!fp.isEmpty())
+ fontpath += fp;
+
+ qt_fontpath = new QStringList(fontpath);
+ return fontpath;
+}
+
+static QFontEngine::FaceId fontFile(const QByteArray &_xname, QFreetypeFace **freetype, int *synth)
+{
+ *freetype = 0;
+ *synth = 0;
+
+ QByteArray xname = _xname.toLower();
+
+ int pos = 0;
+ int minus = 0;
+ while (minus < 5 && (pos = xname.indexOf('-', pos + 1)))
+ ++minus;
+ QByteArray searchname = xname.left(pos);
+ while (minus < 12 && (pos = xname.indexOf('-', pos + 1)))
+ ++minus;
+ QByteArray encoding = xname.mid(pos + 1);
+ //qDebug("xname='%s', searchname='%s', encoding='%s'", xname.data(), searchname.data(), encoding.data());
+ QStringList fontpath = fontPath();
+ QFontEngine::FaceId face_id;
+ face_id.index = 0;
+
+ QByteArray best_mapping;
+
+ for (QStringList::ConstIterator it = fontpath.constBegin(); it != fontpath.constEnd(); ++it) {
+ if ((*it).left(1) != QLatin1String("/"))
+ continue; // not a path name, a font server
+ QString fontmapname;
+ int num = 0;
+ // search font.dir and font.scale for the right file
+ while (num < 2) {
+ if (num == 0)
+ fontmapname = (*it) + QLatin1String("/fonts.scale");
+ else
+ fontmapname = (*it) + QLatin1String("/fonts.dir");
+ ++num;
+ //qWarning(fontmapname);
+ QFile fontmap(fontmapname);
+ if (!fontmap.open(QIODevice::ReadOnly))
+ continue;
+ while (!fontmap.atEnd()) {
+ QByteArray mapping = fontmap.readLine();
+ QByteArray lmapping = mapping.toLower();
+
+ //qWarning(xfontname);
+ //qWarning(mapping);
+ if (!lmapping.contains(searchname))
+ continue;
+ int index = mapping.indexOf(' ');
+ QByteArray ffn = mapping.mid(0,index);
+ // remove bitmap formats freetype can't handle
+ if (ffn.contains(".spd") || ffn.contains(".phont"))
+ continue;
+ bool best_match = false;
+ if (!best_mapping.isEmpty()) {
+ if (lmapping.contains("-0-0-0-0-")) { // scalable font
+ best_match = true;
+ goto found;
+ }
+ if (lmapping.contains(encoding) && !best_mapping.toLower().contains(encoding))
+ goto found;
+ continue;
+ }
+
+ found:
+ int colon = ffn.lastIndexOf(':');
+ if (colon != -1) {
+ QByteArray s = ffn.left(colon);
+ ffn = ffn.mid(colon + 1);
+ if (s.contains("ds="))
+ *synth |= QFontEngine::SynthesizedBold;
+ if (s.contains("ai="))
+ *synth |= QFontEngine::SynthesizedItalic;
+ }
+ face_id.filename = (*it).toLocal8Bit() + '/' + ffn;
+ best_mapping = mapping;
+ if (best_match)
+ goto end;
+ }
+ }
+ }
+end:
+// qDebug("fontfile for %s is from '%s'\n got %s synth=%d", xname.data(),
+// best_mapping.data(), face_id.filename.data(), *synth);
+ *freetype = QFreetypeFace::getFace(face_id);
+ if (!*freetype) {
+ face_id.index = 0;
+ face_id.filename = QByteArray();
+ }
+ return face_id;
+}
+
+#endif // QT_NO_FREETYPE
+
+// defined in qfontdatabase_x11.cpp
+extern int qt_mib_for_xlfd_encoding(const char *encoding);
+extern int qt_xlfd_encoding_id(const char *encoding);
+
+static inline XCharStruct *charStruct(XFontStruct *xfs, uint ch)
+{
+ XCharStruct *xcs = 0;
+ unsigned char r = ch>>8;
+ unsigned char c = ch&0xff;
+ if (xfs->per_char &&
+ r >= xfs->min_byte1 &&
+ r <= xfs->max_byte1 &&
+ c >= xfs->min_char_or_byte2 &&
+ c <= xfs->max_char_or_byte2) {
+ xcs = xfs->per_char + ((r - xfs->min_byte1) *
+ (xfs->max_char_or_byte2 -
+ xfs->min_char_or_byte2 + 1)) +
+ (c - xfs->min_char_or_byte2);
+ if (xcs->width == 0 && xcs->ascent == 0 && xcs->descent == 0)
+ xcs = 0;
+ }
+ return xcs;
+}
+
+QFontEngineXLFD::QFontEngineXLFD(XFontStruct *fs, const QByteArray &name, int mib)
+ : _fs(fs), _name(name), _codec(0), _cmap(mib)
+{
+ if (_cmap) _codec = QTextCodec::codecForMib(_cmap);
+
+ cache_cost = (((fs->max_byte1 - fs->min_byte1) *
+ (fs->max_char_or_byte2 - fs->min_char_or_byte2 + 1)) +
+ fs->max_char_or_byte2 - fs->min_char_or_byte2);
+ cache_cost = ((fs->max_bounds.ascent + fs->max_bounds.descent) *
+ (fs->max_bounds.width * cache_cost / 8));
+ lbearing = SHRT_MIN;
+ rbearing = SHRT_MIN;
+ face_id.index = -1;
+ freetype = 0;
+ synth = 0;
+}
+
+QFontEngineXLFD::~QFontEngineXLFD()
+{
+ XFreeFont(QX11Info::display(), _fs);
+ _fs = 0;
+#ifndef QT_NO_FREETYPE
+ if (freetype)
+ freetype->release(face_id);
+#endif
+}
+
+bool QFontEngineXLFD::stringToCMap(const QChar *s, int len, QGlyphLayout *glyphs, int *nglyphs, QTextEngine::ShaperFlags flags) const
+{
+ if (*nglyphs < len) {
+ *nglyphs = len;
+ return false;
+ }
+
+ // filter out surrogates, we can't handle them anyway with XLFD fonts
+ QVarLengthArray<ushort> _s(len);
+ QChar *str = (QChar *)_s.data();
+ for (int i = 0; i < len; ++i) {
+ if (i < len - 1
+ && s[i].unicode() >= 0xd800 && s[i].unicode() < 0xdc00
+ && s[i+1].unicode() >= 0xdc00 && s[i].unicode() < 0xe000) {
+ *str = QChar();
+ ++i;
+ } else {
+ *str = s[i];
+ }
+ ++str;
+ }
+
+ len = str - (QChar *)_s.data();
+ str = (QChar *)_s.data();
+
+ bool mirrored = flags & QTextEngine::RightToLeft;
+ if (_codec) {
+ bool haveNbsp = false;
+ for (int i = 0; i < len; i++)
+ if (str[i].unicode() == 0xa0) {
+ haveNbsp = true;
+ break;
+ }
+
+ QVarLengthArray<unsigned short> ch(len);
+ QChar *chars = (QChar *)ch.data();
+ if (haveNbsp || mirrored) {
+ for (int i = 0; i < len; i++)
+ chars[i] = (str[i].unicode() == 0xa0 ? 0x20 :
+ (mirrored ? QChar::mirroredChar(str[i].unicode()) : str[i].unicode()));
+ } else {
+ for (int i = 0; i < len; i++)
+ chars[i] = str[i].unicode();
+ }
+ QTextCodec::ConverterState state;
+ state.flags = QTextCodec::ConvertInvalidToNull;
+ QByteArray ba = _codec->fromUnicode(chars, len, &state);
+ if (ba.length() == 2*len) {
+ // double byte encoding
+ const uchar *data = (const uchar *)ba.constData();
+ for (int i = 0; i < len; i++) {
+ glyphs->glyphs[i] = ((ushort)data[0] << 8) + data[1];
+ data += 2;
+ }
+ } else {
+ const uchar *data = (const uchar *)ba.constData();
+ for (int i = 0; i < len; i++)
+ glyphs->glyphs[i] = (ushort)data[i];
+ }
+ } else {
+ int i = len;
+ const QChar *c = str + len;
+ if (mirrored) {
+ while (c != str)
+ glyphs->glyphs[--i] = (--c)->unicode() == 0xa0 ? 0x20 : QChar::mirroredChar(c->unicode());
+ } else {
+ while (c != str)
+ glyphs->glyphs[--i] = (--c)->unicode() == 0xa0 ? 0x20 : c->unicode();
+ }
+ }
+ *nglyphs = len;
+ glyphs->numGlyphs = len;
+
+ if (!(flags & QTextEngine::GlyphIndicesOnly))
+ recalcAdvances(glyphs, flags);
+ return true;
+}
+
+void QFontEngineXLFD::recalcAdvances(QGlyphLayout *glyphs, QTextEngine::ShaperFlags /*flags*/) const
+{
+ int i = glyphs->numGlyphs;
+ XCharStruct *xcs;
+ // inlined for better perfomance
+ if (!_fs->per_char) {
+ xcs = &_fs->min_bounds;
+ while (i != 0) {
+ --i;
+ const unsigned char r = glyphs->glyphs[i] >> 8;
+ const unsigned char c = glyphs->glyphs[i] & 0xff;
+ if (r >= _fs->min_byte1 &&
+ r <= _fs->max_byte1 &&
+ c >= _fs->min_char_or_byte2 &&
+ c <= _fs->max_char_or_byte2) {
+ glyphs->advances_x[i] = xcs->width;
+ } else {
+ glyphs->glyphs[i] = 0;
+ }
+ }
+ }
+ else if (!_fs->max_byte1) {
+ XCharStruct *base = _fs->per_char - _fs->min_char_or_byte2;
+ while (i != 0) {
+ unsigned int gl = glyphs->glyphs[--i];
+ xcs = (gl >= _fs->min_char_or_byte2 && gl <= _fs->max_char_or_byte2) ?
+ base + gl : 0;
+ if (!xcs || (!xcs->width && !xcs->ascent && !xcs->descent)) {
+ glyphs->glyphs[i] = 0;
+ } else {
+ glyphs->advances_x[i] = xcs->width;
+ }
+ }
+ }
+ else {
+ while (i != 0) {
+ xcs = charStruct(_fs, glyphs->glyphs[--i]);
+ if (!xcs) {
+ glyphs->glyphs[i] = 0;
+ } else {
+ glyphs->advances_x[i] = xcs->width;
+ }
+ }
+ }
+}
+
+glyph_metrics_t QFontEngineXLFD::boundingBox(const QGlyphLayout &glyphs)
+{
+ int i;
+
+ glyph_metrics_t overall;
+ // initialize with line height, we get the same behaviour on all platforms
+ overall.y = -ascent();
+ overall.height = ascent() + descent() + 1;
+ QFixed ymax;
+ QFixed xmax;
+ for (i = 0; i < glyphs.numGlyphs; i++) {
+ XCharStruct *xcs = charStruct(_fs, glyphs.glyphs[i]);
+ if (xcs) {
+ QFixed x = overall.xoff + glyphs.offsets[i].x + xcs->lbearing;
+ QFixed y = overall.yoff + glyphs.offsets[i].y - xcs->ascent;
+ overall.x = qMin(overall.x, x);
+ overall.y = qMin(overall.y, y);
+ xmax = qMax(xmax, overall.xoff + glyphs.offsets[i].x + xcs->rbearing);
+ ymax = qMax(ymax, y + xcs->ascent + xcs->descent);
+ overall.xoff += glyphs.advances_x[i];
+ } else {
+ QFixed size = _fs->ascent;
+ overall.x = qMin(overall.x, overall.xoff);
+ overall.y = qMin(overall.y, overall.yoff - size);
+ ymax = qMax(ymax, overall.yoff);
+ overall.xoff += size;
+ xmax = qMax(xmax, overall.xoff);
+ }
+ }
+ overall.height = qMax(overall.height, ymax - overall.y);
+ overall.width = xmax - overall.x;
+
+ return overall;
+}
+
+glyph_metrics_t QFontEngineXLFD::boundingBox(glyph_t glyph)
+{
+ glyph_metrics_t gm;
+ XCharStruct *xcs = charStruct(_fs, glyph);
+ if (xcs) {
+ gm = glyph_metrics_t(xcs->lbearing, -xcs->ascent, xcs->rbearing- xcs->lbearing, xcs->ascent + xcs->descent,
+ xcs->width, 0);
+ } else {
+ QFixed size = ascent();
+ gm = glyph_metrics_t(0, size, size, size, size, 0);
+ }
+ return gm;
+}
+
+QFixed QFontEngineXLFD::ascent() const
+{
+ return _fs->ascent;
+}
+
+QFixed QFontEngineXLFD::descent() const
+{
+ return (_fs->descent-1);
+}
+
+QFixed QFontEngineXLFD::leading() const
+{
+ QFixed l = QFixed(qMin<int>(_fs->ascent, _fs->max_bounds.ascent)
+ + qMin<int>(_fs->descent, _fs->max_bounds.descent)) * QFixed::fromReal(0.15);
+ return l.ceil();
+}
+
+qreal QFontEngineXLFD::maxCharWidth() const
+{
+ return _fs->max_bounds.width;
+}
+
+
+// Loads the font for the specified script
+static inline int maxIndex(XFontStruct *f) {
+ return (((f->max_byte1 - f->min_byte1) *
+ (f->max_char_or_byte2 - f->min_char_or_byte2 + 1)) +
+ f->max_char_or_byte2 - f->min_char_or_byte2);
+}
+
+qreal QFontEngineXLFD::minLeftBearing() const
+{
+ if (lbearing == SHRT_MIN) {
+ if (_fs->per_char) {
+ XCharStruct *cs = _fs->per_char;
+ int nc = maxIndex(_fs) + 1;
+ int mx = cs->lbearing;
+
+ for (int c = 1; c < nc; c++) {
+ // ignore the bearings for characters whose ink is
+ // completely outside the normal bounding box
+ if ((cs[c].lbearing <= 0 && cs[c].rbearing <= 0) ||
+ (cs[c].lbearing >= cs[c].width && cs[c].rbearing >= cs[c].width))
+ continue;
+
+ int nmx = cs[c].lbearing;
+
+ if (nmx < mx)
+ mx = nmx;
+ }
+
+ ((QFontEngineXLFD *)this)->lbearing = mx;
+ } else
+ ((QFontEngineXLFD *)this)->lbearing = _fs->min_bounds.lbearing;
+ }
+ return lbearing;
+}
+
+qreal QFontEngineXLFD::minRightBearing() const
+{
+ if (rbearing == SHRT_MIN) {
+ if (_fs->per_char) {
+ XCharStruct *cs = _fs->per_char;
+ int nc = maxIndex(_fs) + 1;
+ int mx = cs->rbearing;
+
+ for (int c = 1; c < nc; c++) {
+ // ignore the bearings for characters whose ink is
+ // completely outside the normal bounding box
+ if ((cs[c].lbearing <= 0 && cs[c].rbearing <= 0) ||
+ (cs[c].lbearing >= cs[c].width && cs[c].rbearing >= cs[c].width))
+ continue;
+
+ int nmx = cs[c].rbearing;
+
+ if (nmx < mx)
+ mx = nmx;
+ }
+
+ ((QFontEngineXLFD *)this)->rbearing = mx;
+ } else
+ ((QFontEngineXLFD *)this)->rbearing = _fs->min_bounds.rbearing;
+ }
+ return rbearing;
+}
+
+const char *QFontEngineXLFD::name() const
+{
+ return _name;
+}
+
+bool QFontEngineXLFD::canRender(const QChar *string, int len)
+{
+ QVarLengthGlyphLayoutArray glyphs(len);
+ int nglyphs = len;
+ if (stringToCMap(string, len, &glyphs, &nglyphs, 0) == false) {
+ glyphs.resize(nglyphs);
+ stringToCMap(string, len, &glyphs, &nglyphs, 0);
+ }
+
+ bool allExist = true;
+ for (int i = 0; i < nglyphs; i++) {
+ if (!glyphs.glyphs[i] || !charStruct(_fs, glyphs.glyphs[i])) {
+ allExist = false;
+ break;
+ }
+ }
+
+ return allExist;
+}
+
+QBitmap QFontEngineXLFD::bitmapForGlyphs(const QGlyphLayout &glyphs, const glyph_metrics_t &metrics, QTextItem::RenderFlags flags)
+{
+ int w = metrics.width.toInt();
+ int h = metrics.height.toInt();
+ if (w <= 0 || h <= 0)
+ return QBitmap();
+
+ QPixmapData *data = new QX11PixmapData(QPixmapData::BitmapType);
+ data->resize(w, h);
+ QPixmap bm(data);
+ QPainter p(&bm);
+ p.fillRect(0, 0, w, h, Qt::color0);
+ p.setPen(Qt::color1);
+
+ QTextItemInt item;
+ item.flags = flags;
+ item.ascent = -metrics.y;
+ item.descent = metrics.height - item.ascent;
+ item.width = metrics.width;
+ item.chars = 0;
+ item.num_chars = 0;
+ item.logClusters = 0;
+ item.glyphs = glyphs;
+ item.fontEngine = this;
+ item.f = 0;
+
+ p.drawTextItem(QPointF(-metrics.x.toReal(), item.ascent.toReal()), item);
+ p.end();
+
+ return QBitmap(bm);
+}
+
+void QFontEngineXLFD::addOutlineToPath(qreal x, qreal y, const QGlyphLayout &glyphs, QPainterPath *path, QTextItem::RenderFlags flags)
+{
+ // cannot use QFontEngine::addBitmapFontToPath(), since we don't
+ // have direct access to the glyph bitmaps, so we have to draw
+ // onto a QBitmap, then convert to QImage, then to path
+ glyph_metrics_t metrics = boundingBox(glyphs);
+
+ QImage image = bitmapForGlyphs(glyphs, metrics, flags).toImage();
+ if (image.isNull())
+ return;
+
+ image = image.convertToFormat(QImage::Format_Mono);
+ const uchar *image_data = image.bits();
+ uint bpl = image.bytesPerLine();
+ // from qfontengine.cpp
+ extern void qt_addBitmapToPath(qreal x0, qreal y0, const uchar *image_data,
+ int bpl, int w, int h, QPainterPath *path);
+ qt_addBitmapToPath(x, y + metrics.y.toReal(), image_data, bpl, image.width(), image.height(), path);
+}
+
+QFontEngine::FaceId QFontEngineXLFD::faceId() const
+{
+#ifndef QT_NO_FREETYPE
+ if (face_id.index == -1) {
+ face_id = fontFile(_name, &freetype, &synth);
+ if (_codec)
+ face_id.encoding = _codec->mibEnum();
+ if (freetype) {
+ const_cast<QFontEngineXLFD *>(this)->fsType = freetype->fsType();
+ } else {
+ QFontEngine::Properties properties = QFontEngine::properties();
+ face_id.index = 0;
+ face_id.filename = "-" + properties.postscriptName;
+ }
+ }
+#endif
+
+ return face_id;
+}
+
+QFontEngine::Properties QFontEngineXLFD::properties() const
+{
+ if (face_id.index == -1)
+ (void)faceId();
+
+#ifndef QT_NO_FREETYPE
+ if (freetype)
+ return freetype->properties();
+#endif
+ return QFontEngine::properties();
+}
+
+void QFontEngineXLFD::getUnscaledGlyph(glyph_t glyph, QPainterPath *path, glyph_metrics_t *metrics)
+{
+ if (face_id.index == -1)
+ (void)faceId();
+#ifndef QT_NO_FREETYPE
+ if (!freetype)
+#endif
+ {
+ QFontEngine::getUnscaledGlyph(glyph, path, metrics);
+ return;
+ }
+
+#ifndef QT_NO_FREETYPE
+ freetype->lock();
+
+ FT_Face face = freetype->face;
+ FT_Set_Char_Size(face, face->units_per_EM << 6, face->units_per_EM << 6, 0, 0);
+ freetype->xsize = face->units_per_EM << 6;
+ freetype->ysize = face->units_per_EM << 6;
+ FT_Set_Transform(face, 0, 0);
+ glyph = glyphIndexToFreetypeGlyphIndex(glyph);
+ FT_Load_Glyph(face, glyph, FT_LOAD_NO_BITMAP);
+
+ int left = face->glyph->metrics.horiBearingX;
+ int right = face->glyph->metrics.horiBearingX + face->glyph->metrics.width;
+ int top = face->glyph->metrics.horiBearingY;
+ int bottom = face->glyph->metrics.horiBearingY - face->glyph->metrics.height;
+
+ QFixedPoint p;
+ p.x = 0;
+ p.y = 0;
+ metrics->width = QFixed::fromFixed(right-left);
+ metrics->height = QFixed::fromFixed(top-bottom);
+ metrics->x = QFixed::fromFixed(left);
+ metrics->y = QFixed::fromFixed(-top);
+ metrics->xoff = QFixed::fromFixed(face->glyph->advance.x);
+
+ if (!FT_IS_SCALABLE(freetype->face))
+ QFreetypeFace::addBitmapToPath(face->glyph, p, path);
+ else
+ QFreetypeFace::addGlyphToPath(face, face->glyph, p, path, face->units_per_EM << 6, face->units_per_EM << 6);
+
+ FT_Set_Transform(face, &freetype->matrix, 0);
+ freetype->unlock();
+#endif // QT_NO_FREETYPE
+}
+
+
+bool QFontEngineXLFD::getSfntTableData(uint tag, uchar *buffer, uint *length) const
+{
+#ifndef QT_NO_FREETYPE
+ if (face_id.index == -1)
+ (void)faceId();
+ if (!freetype)
+ return false;
+ return freetype->getSfntTable(tag, buffer, length);
+#else
+ Q_UNUSED(tag);
+ Q_UNUSED(buffer);
+ Q_UNUSED(length);
+ return false;
+#endif
+}
+
+int QFontEngineXLFD::synthesized() const
+{
+ return synth;
+}
+
+QImage QFontEngineXLFD::alphaMapForGlyph(glyph_t glyph)
+{
+ glyph_metrics_t metrics = boundingBox(glyph);
+
+/*
+ printf("a) w=%.2f, h=%.2f, xoff=%.2f, yoff=%.2f, x=%.2f, y=%.2f\n",
+ metrics.width.toReal(),
+ metrics.height.toReal(),
+ metrics.xoff.toReal(),
+ metrics.yoff.toReal(),
+ metrics.x.toReal(),
+ metrics.y.toReal());
+*/
+
+ QGlyphLayoutArray<1> glyphs;
+ glyphs.glyphs[0] = glyph;
+
+ QImage image = bitmapForGlyphs(glyphs, metrics).toImage();
+//image.save(QString::fromLatin1("x11cache-%1.png").arg((int)glyph));
+
+ image = image.convertToFormat(QImage::Format_Indexed8);
+ QVector<QRgb> colors(256);
+ for (int i = 0; i < 256; ++i)
+ colors[i] = qRgba(0, 0, 0, i);
+ image.setColorTable(colors);
+
+ int width = image.width();
+ int height = image.height();
+ for (int y = 0; y < height; ++y) {
+ uchar *bits = image.scanLine(y);
+ for (int x = 0; x < width; ++x)
+ bits[x] = ~(bits[x]-1);
+ }
+
+ return image;
+}
+
+#ifndef QT_NO_FREETYPE
+
+FT_Face QFontEngineXLFD::non_locked_face() const
+{
+ return freetype ? freetype->face : 0;
+}
+
+uint QFontEngineXLFD::toUnicode(glyph_t g) const
+{
+ if (_codec) {
+ QTextCodec::ConverterState state;
+ state.flags = QTextCodec::ConvertInvalidToNull;
+ uchar data[2];
+ int l = 1;
+ if (g > 255) {
+ data[0] = (g >> 8);
+ data[1] = (g & 255);
+ l = 2;
+ } else {
+ data[0] = g;
+ }
+ QString s = _codec->toUnicode((char *)data, l, &state);
+ Q_ASSERT(s.length() == 1);
+ g = s.at(0).unicode();
+ }
+ return g;
+}
+
+glyph_t QFontEngineXLFD::glyphIndexToFreetypeGlyphIndex(glyph_t g) const
+{
+ return FT_Get_Char_Index(freetype->face, toUnicode(g));
+}
+#endif
+
+#ifndef QT_NO_FONTCONFIG
+
+// ------------------------------------------------------------------
+// Multi FT engine
+// ------------------------------------------------------------------
+
+static QFontEngine *engineForPattern(FcPattern *pattern, const QFontDef &request,
+ int screen)
+{
+ FcResult res;
+ FcPattern *match = FcFontMatch(0, pattern, &res);
+ QFontEngineX11FT *engine = new QFontEngineX11FT(match, request, screen);
+ if (!engine->invalid())
+ return engine;
+
+ delete engine;
+ QFontEngine *fe = new QFontEngineBox(request.pixelSize);
+ fe->fontDef = request;
+ return fe;
+}
+
+QFontEngineMultiFT::QFontEngineMultiFT(QFontEngine *fe, FcPattern *matchedPattern, FcPattern *p, int s, const QFontDef &req)
+ : QFontEngineMulti(2), request(req), pattern(p), firstEnginePattern(matchedPattern), fontSet(0), screen(s)
+{
+
+ engines[0] = fe;
+ engines.at(0)->ref.ref();
+ fontDef = engines[0]->fontDef;
+ cache_cost = 100;
+ firstFontIndex = 1;
+}
+
+QFontEngineMultiFT::~QFontEngineMultiFT()
+{
+ extern QMutex *qt_fontdatabase_mutex();
+ QMutexLocker locker(qt_fontdatabase_mutex());
+
+ FcPatternDestroy(pattern);
+ if (firstEnginePattern)
+ FcPatternDestroy(firstEnginePattern);
+ if (fontSet)
+ FcFontSetDestroy(fontSet);
+}
+
+
+void QFontEngineMultiFT::loadEngine(int at)
+{
+ extern QMutex *qt_fontdatabase_mutex();
+ QMutexLocker locker(qt_fontdatabase_mutex());
+
+ extern void qt_addPatternProps(FcPattern *pattern, int screen, int script,
+ const QFontDef &request);
+ extern QFontDef qt_FcPatternToQFontDef(FcPattern *pattern, const QFontDef &);
+ extern FcFontSet *qt_fontSetForPattern(FcPattern *pattern, const QFontDef &request);
+
+ Q_ASSERT(at > 0);
+ if (!fontSet) {
+ fontSet = qt_fontSetForPattern(pattern, request);
+
+ // it may happen that the fontset of fallbacks consists of only one font. In this case we
+ // have to fall back to the box fontengine as we cannot render the glyph.
+ if (fontSet->nfont == 1 && at == 1 && engines.size() == 2) {
+ Q_ASSERT(engines.at(at) == 0);
+ QFontEngine *fe = new QFontEngineBox(request.pixelSize);
+ fe->fontDef = request;
+ engines[at] = fe;
+ return;
+ }
+
+ if (firstEnginePattern) {
+
+ if (!FcPatternEqual(firstEnginePattern, fontSet->fonts[0]))
+ firstFontIndex = 0;
+
+ FcPatternDestroy(firstEnginePattern);
+ firstEnginePattern = 0;
+ }
+
+ engines.resize(fontSet->nfont + 1 - firstFontIndex);
+ }
+ Q_ASSERT(at < engines.size());
+ Q_ASSERT(engines.at(at) == 0);
+
+ FcPattern *pattern = FcPatternDuplicate(fontSet->fonts[at + firstFontIndex - 1]);
+ qt_addPatternProps(pattern, screen, QUnicodeTables::Common, request);
+
+ QFontDef fontDef = qt_FcPatternToQFontDef(pattern, this->request);
+
+ // note: we use -1 for the script to make sure that we keep real
+ // FT engines separate from Multi engines in the font cache
+ QFontCache::Key key(fontDef, -1, screen);
+ QFontEngine *fontEngine = QFontCache::instance()->findEngine(key);
+ if (!fontEngine) {
+ FcConfigSubstitute(0, pattern, FcMatchPattern);
+ FcDefaultSubstitute(pattern);
+ fontEngine = engineForPattern(pattern, request, screen);
+ QFontCache::instance()->insertEngine(key, fontEngine);
+ }
+ FcPatternDestroy(pattern);
+ fontEngine->ref.ref();
+ engines[at] = fontEngine;
+}
+
+// ------------------------------------------------------------------
+// X11 FT engine
+// ------------------------------------------------------------------
+
+
+
+Q_GUI_EXPORT void qt_x11ft_convert_pattern(FcPattern *pattern, QByteArray *file_name, int *index, bool *antialias)
+{
+ FcChar8 *fileName;
+ FcPatternGetString(pattern, FC_FILE, 0, &fileName);
+ *file_name = (const char *)fileName;
+ if (!FcPatternGetInteger(pattern, FC_INDEX, 0, index))
+ index = 0;
+ FcBool b;
+ if (FcPatternGetBool(pattern, FC_ANTIALIAS, 0, &b) == FcResultMatch)
+ *antialias = b;
+}
+
+
+QFontEngineX11FT::QFontEngineX11FT(FcPattern *pattern, const QFontDef &fd, int screen)
+ : QFontEngineFT(fd)
+{
+// FcPatternPrint(pattern);
+
+ bool antialias = X11->fc_antialias;
+ QByteArray file_name;
+ int face_index;
+ qt_x11ft_convert_pattern(pattern, &file_name, &face_index, &antialias);
+ QFontEngine::FaceId face_id;
+ face_id.filename = file_name;
+ face_id.index = face_index;
+
+ canUploadGlyphsToServer = qApp->thread() == QThread::currentThread();
+
+ subpixelType = Subpixel_None;
+ if (antialias) {
+ int subpixel = X11->display ? X11->screens[screen].subpixel : FC_RGBA_UNKNOWN;
+ if (subpixel == FC_RGBA_UNKNOWN)
+ (void) FcPatternGetInteger(pattern, FC_RGBA, 0, &subpixel);
+ if (!antialias || subpixel == FC_RGBA_UNKNOWN)
+ subpixel = FC_RGBA_NONE;
+
+ switch (subpixel) {
+ case FC_RGBA_NONE: subpixelType = Subpixel_None; break;
+ case FC_RGBA_RGB: subpixelType = Subpixel_RGB; break;
+ case FC_RGBA_BGR: subpixelType = Subpixel_BGR; break;
+ case FC_RGBA_VRGB: subpixelType = Subpixel_VRGB; break;
+ case FC_RGBA_VBGR: subpixelType = Subpixel_VBGR; break;
+ default: break;
+ }
+ }
+
+#ifdef FC_HINT_STYLE
+ {
+ int hint_style = 0;
+ if (FcPatternGetInteger (pattern, FC_HINT_STYLE, 0, &hint_style) == FcResultNoMatch)
+ hint_style = X11->fc_hint_style;
+
+ switch (hint_style) {
+ case FC_HINT_NONE:
+ default_hint_style = HintNone;
+ break;
+ case FC_HINT_SLIGHT:
+ default_hint_style = HintLight;
+ break;
+ case FC_HINT_MEDIUM:
+ default_hint_style = HintMedium;
+ break;
+ default:
+ default_hint_style = HintFull;
+ break;
+ }
+ }
+#endif
+
+#if defined(FC_AUTOHINT) && defined(FT_LOAD_FORCE_AUTOHINT)
+ {
+ bool autohint = false;
+
+ FcBool b;
+ if (FcPatternGetBool(pattern, FC_AUTOHINT, 0, &b) == FcResultMatch)
+ autohint = b;
+
+ if (autohint)
+ default_load_flags |= FT_LOAD_FORCE_AUTOHINT;
+ }
+#endif
+
+#if defined(FC_LCD_FILTER) && defined(FT_LCD_FILTER_H)
+ {
+ int filter = FC_LCD_FILTER_NONE;
+ if (FcPatternGetInteger(pattern, FC_LCD_FILTER, 0, &filter) == FcResultMatch) {
+ switch (filter) {
+ case FC_LCD_FILTER_NONE:
+ lcdFilterType = FT_LCD_FILTER_NONE;
+ break;
+ case FC_LCD_FILTER_DEFAULT:
+ lcdFilterType = FT_LCD_FILTER_DEFAULT;
+ break;
+ case FC_LCD_FILTER_LIGHT:
+ lcdFilterType = FT_LCD_FILTER_LIGHT;
+ break;
+ case FC_LCD_FILTER_LEGACY:
+ lcdFilterType = FT_LCD_FILTER_LEGACY;
+ break;
+ default:
+ // new unknown lcd filter type?!
+ break;
+ }
+ }
+ }
+#endif
+
+#ifdef FC_EMBEDDED_BITMAP
+ {
+ FcBool b;
+ if (FcPatternGetBool(pattern, FC_EMBEDDED_BITMAP, 0, &b) == FcResultMatch)
+ embeddedbitmap = b;
+ }
+#endif
+
+ GlyphFormat defaultFormat = Format_None;
+
+#ifndef QT_NO_XRENDER
+ if (X11->use_xrender) {
+ int format = PictStandardA8;
+ if (!antialias)
+ format = PictStandardA1;
+ else if (subpixelType == Subpixel_RGB
+ || subpixelType == Subpixel_BGR
+ || subpixelType == Subpixel_VRGB
+ || subpixelType == Subpixel_VBGR)
+ format = PictStandardARGB32;
+ xglyph_format = format;
+
+ if (subpixelType != QFontEngineFT::Subpixel_None)
+ defaultFormat = Format_A32;
+ else if (antialias)
+ defaultFormat = Format_A8;
+ else
+ defaultFormat = Format_Mono;
+ }
+#endif
+
+ if (!init(face_id, antialias, defaultFormat)) {
+ FcPatternDestroy(pattern);
+ return;
+ }
+
+ if (!freetype->charset) {
+ FcCharSet *cs;
+ FcPatternGetCharSet (pattern, FC_CHARSET, 0, &cs);
+ freetype->charset = FcCharSetCopy(cs);
+ }
+ FcPatternDestroy(pattern);
+}
+
+QFontEngineX11FT::~QFontEngineX11FT()
+{
+ freeGlyphSets();
+}
+
+unsigned long QFontEngineX11FT::allocateServerGlyphSet()
+{
+#ifndef QT_NO_XRENDER
+ if (!canUploadGlyphsToServer || !X11->use_xrender)
+ return 0;
+ return XRenderCreateGlyphSet(X11->display, XRenderFindStandardFormat(X11->display, xglyph_format));
+#else
+ return 0;
+#endif
+}
+
+void QFontEngineX11FT::freeServerGlyphSet(unsigned long id)
+{
+#ifndef QT_NO_XRENDER
+ if (!id)
+ return;
+ XRenderFreeGlyphSet(X11->display, id);
+#endif
+}
+
+bool QFontEngineX11FT::uploadGlyphToServer(QGlyphSet *set, uint glyphid, Glyph *g, GlyphInfo *info, int glyphDataSize) const
+{
+#ifndef QT_NO_XRENDER
+ if (!canUploadGlyphsToServer)
+ return false;
+ if (g->format == Format_Mono) {
+ /*
+ * swap bit order around; FreeType is always MSBFirst
+ */
+ if (BitmapBitOrder(X11->display) != MSBFirst) {
+ unsigned char *line = g->data;
+ int i = glyphDataSize;
+ while (i--) {
+ unsigned char c;
+ c = *line;
+ c = ((c << 1) & 0xaa) | ((c >> 1) & 0x55);
+ c = ((c << 2) & 0xcc) | ((c >> 2) & 0x33);
+ c = ((c << 4) & 0xf0) | ((c >> 4) & 0x0f);
+ *line++ = c;
+ }
+ }
+ }
+
+ ::Glyph xglyph = glyphid;
+ XRenderAddGlyphs (X11->display, set->id, &xglyph, info, 1, (const char *)g->data, glyphDataSize);
+ delete [] g->data;
+ g->data = 0;
+ g->format = Format_None;
+ g->uploadedToServer = true;
+ return true;
+#else
+ return false;
+#endif
+}
+
+#endif // QT_NO_FONTCONFIG
+
+QT_END_NAMESPACE
diff --git a/src/gui/text/qfontengine_x11_p.h b/src/gui/text/qfontengine_x11_p.h
new file mode 100644
index 0000000000..eb00383189
--- /dev/null
+++ b/src/gui/text/qfontengine_x11_p.h
@@ -0,0 +1,177 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QFONTENGINE_X11_P_H
+#define QFONTENGINE_X11_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/qt_x11_p.h>
+
+#include <private/qfontengine_ft_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QFreetypeFace;
+
+// --------------------------------------------------------------------------
+
+class QFontEngineMultiXLFD : public QFontEngineMulti
+{
+public:
+ QFontEngineMultiXLFD(const QFontDef &r, const QList<int> &l, int s);
+ ~QFontEngineMultiXLFD();
+
+ void loadEngine(int at);
+
+private:
+ QList<int> encodings;
+ int screen;
+ QFontDef request;
+};
+
+/**
+ * \internal The font engine for X Logical Font Description (XLFD) fonts, which is for X11 systems without freetype.
+ */
+class QFontEngineXLFD : public QFontEngine
+{
+public:
+ QFontEngineXLFD(XFontStruct *f, const QByteArray &name, int mib);
+ ~QFontEngineXLFD();
+
+ virtual QFontEngine::FaceId faceId() const;
+ QFontEngine::Properties properties() const;
+ virtual void getUnscaledGlyph(glyph_t glyph, QPainterPath *path, glyph_metrics_t *metrics);
+ virtual bool getSfntTableData(uint tag, uchar *buffer, uint *length) const;
+ virtual int synthesized() const;
+
+ virtual bool stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs, int *nglyphs,
+ QTextEngine::ShaperFlags flags) const;
+ virtual void recalcAdvances(QGlyphLayout *, QTextEngine::ShaperFlags) const;
+
+ virtual glyph_metrics_t boundingBox(const QGlyphLayout &glyphs);
+ virtual glyph_metrics_t boundingBox(glyph_t glyph);
+
+ virtual void addOutlineToPath(qreal x, qreal y, const QGlyphLayout &glyphs, QPainterPath *path, QTextItem::RenderFlags);
+ virtual QFixed ascent() const;
+ virtual QFixed descent() const;
+ virtual QFixed leading() const;
+ virtual qreal maxCharWidth() const;
+ virtual qreal minLeftBearing() const;
+ virtual qreal minRightBearing() const;
+ virtual QImage alphaMapForGlyph(glyph_t);
+
+ virtual inline Type type() const
+ { return QFontEngine::XLFD; }
+
+ virtual bool canRender(const QChar *string, int len);
+ virtual const char *name() const;
+
+ inline XFontStruct *fontStruct() const
+ { return _fs; }
+
+#ifndef QT_NO_FREETYPE
+ FT_Face non_locked_face() const;
+ glyph_t glyphIndexToFreetypeGlyphIndex(glyph_t g) const;
+#endif
+ uint toUnicode(glyph_t g) const;
+
+private:
+ QBitmap bitmapForGlyphs(const QGlyphLayout &glyphs, const glyph_metrics_t &metrics, QTextItem::RenderFlags flags = 0);
+
+ XFontStruct *_fs;
+ QByteArray _name;
+ QTextCodec *_codec;
+ int _cmap;
+ int lbearing, rbearing;
+ mutable QFontEngine::FaceId face_id;
+ mutable QFreetypeFace *freetype;
+ mutable int synth;
+};
+
+#ifndef QT_NO_FONTCONFIG
+
+class Q_GUI_EXPORT QFontEngineMultiFT : public QFontEngineMulti
+{
+public:
+ QFontEngineMultiFT(QFontEngine *fe, FcPattern *firstEnginePattern, FcPattern *p, int s, const QFontDef &request);
+ ~QFontEngineMultiFT();
+
+ void loadEngine(int at);
+
+private:
+ QFontDef request;
+ FcPattern *pattern;
+ FcPattern *firstEnginePattern;
+ FcFontSet *fontSet;
+ int screen;
+ int firstFontIndex; // first font in fontset
+};
+
+class Q_GUI_EXPORT QFontEngineX11FT : public QFontEngineFT
+{
+public:
+ explicit QFontEngineX11FT(FcPattern *pattern, const QFontDef &fd, int screen);
+ ~QFontEngineX11FT();
+
+#ifndef QT_NO_XRENDER
+ int xglyph_format;
+#endif
+
+protected:
+ virtual bool uploadGlyphToServer(QGlyphSet *set, uint glyphid, Glyph *g, GlyphInfo *info, int glyphDataSize) const;
+ virtual unsigned long allocateServerGlyphSet();
+ virtual void freeServerGlyphSet(unsigned long id);
+};
+
+#endif // QT_NO_FONTCONFIG
+
+QT_END_NAMESPACE
+
+#endif // QFONTENGINE_X11_P_H
diff --git a/src/gui/text/qfontengineglyphcache_p.h b/src/gui/text/qfontengineglyphcache_p.h
new file mode 100644
index 0000000000..8589cc68de
--- /dev/null
+++ b/src/gui/text/qfontengineglyphcache_p.h
@@ -0,0 +1,95 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QFONTENGINEGLYPHCACHE_P_H
+#define QFONTENGINEGLYPHCACHE_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 "QtCore/qglobal.h"
+#include "QtCore/qatomic.h"
+#include <QtCore/qvarlengtharray.h>
+#include "private/qfont_p.h"
+
+#ifdef Q_WS_WIN
+# include "QtCore/qt_windows.h"
+#endif
+
+#ifdef Q_WS_MAC
+# include "private/qt_mac_p.h"
+# include "QtCore/qmap.h"
+# include "QtCore/qcache.h"
+# include "private/qcore_mac_p.h"
+#endif
+
+QT_BEGIN_NAMESPACE
+
+class Q_GUI_EXPORT QFontEngineGlyphCache
+{
+public:
+ QFontEngineGlyphCache(const QTransform &matrix) : m_transform(matrix) { }
+
+ enum Type {
+ Raster_RGBMask,
+ Raster_A8,
+ Raster_Mono
+ };
+
+ virtual ~QFontEngineGlyphCache();
+
+ QTransform m_transform;
+};
+typedef QHash<void *, QList<QFontEngineGlyphCache *> > GlyphPointerHash;
+typedef QHash<int, QList<QFontEngineGlyphCache *> > GlyphIntHash;
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/gui/text/qfontinfo.h b/src/gui/text/qfontinfo.h
new file mode 100644
index 0000000000..c1b0bb8934
--- /dev/null
+++ b/src/gui/text/qfontinfo.h
@@ -0,0 +1,87 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QFONTINFO_H
+#define QFONTINFO_H
+
+#include <QtGui/qfont.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+class Q_GUI_EXPORT QFontInfo
+{
+public:
+ QFontInfo(const QFont &);
+ QFontInfo(const QFontInfo &);
+ ~QFontInfo();
+
+ QFontInfo &operator=(const QFontInfo &);
+
+ QString family() const;
+ int pixelSize() const;
+ int pointSize() const;
+ qreal pointSizeF() const;
+ bool italic() const;
+ QFont::Style style() const;
+ int weight() const;
+ inline bool bold() const { return weight() > QFont::Normal; }
+ bool underline() const;
+ bool overline() const;
+ bool strikeOut() const;
+ bool fixedPitch() const;
+ QFont::StyleHint styleHint() const;
+ bool rawMode() const;
+
+ bool exactMatch() const;
+
+private:
+ QFontPrivate *d;
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QFONTINFO_H
diff --git a/src/gui/text/qfontmetrics.cpp b/src/gui/text/qfontmetrics.cpp
new file mode 100644
index 0000000000..88d0610f86
--- /dev/null
+++ b/src/gui/text/qfontmetrics.cpp
@@ -0,0 +1,1739 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qfont.h"
+#include "qpaintdevice.h"
+#include "qfontmetrics.h"
+
+#include "qfont_p.h"
+#include "qfontengine_p.h"
+#include <private/qunicodetables_p.h>
+
+#include <math.h>
+
+#ifdef Q_WS_X11
+#include "qx11info_x11.h"
+#endif
+
+QT_BEGIN_NAMESPACE
+
+#ifdef Q_WS_X11
+extern const QX11Info *qt_x11Info(const QPaintDevice *pd);
+#endif
+
+extern void qt_format_text(const QFont& font, const QRectF &_r,
+ int tf, const QString &text, QRectF *brect,
+ int tabStops, int *tabArray, int tabArrayLen,
+ QPainter *painter);
+extern int qt_defaultDpi();
+
+/*****************************************************************************
+ QFontMetrics member functions
+ *****************************************************************************/
+
+/*!
+ \class QFontMetrics
+ \reentrant
+
+ \brief The QFontMetrics class provides font metrics information.
+
+ \ingroup multimedia
+ \ingroup shared
+ \ingroup text
+
+ QFontMetrics functions calculate the size of characters and
+ strings for a given font. There are three ways you can create a
+ QFontMetrics object:
+
+ \list 1
+ \o Calling the QFontMetrics constructor with a QFont creates a
+ font metrics object for a screen-compatible font, i.e. the font
+ cannot be a printer font. If the font is changed
+ later, the font metrics object is \e not updated.
+
+ (Note: If you use a printer font the values returned may be
+ inaccurate. Printer fonts are not always accessible so the nearest
+ screen font is used if a printer font is supplied.)
+
+ \o QWidget::fontMetrics() returns the font metrics for a widget's
+ font. This is equivalent to QFontMetrics(widget->font()). If the
+ widget's font is changed later, the font metrics object is \e not
+ updated.
+
+ \o QPainter::fontMetrics() returns the font metrics for a
+ painter's current font. If the painter's font is changed later, the
+ font metrics object is \e not updated.
+ \endlist
+
+ Once created, the object provides functions to access the
+ individual metrics of the font, its characters, and for strings
+ rendered in the font.
+
+ There are several functions that operate on the font: ascent(),
+ descent(), height(), leading() and lineSpacing() return the basic
+ size properties of the font. The underlinePos(), overlinePos(),
+ strikeOutPos() and lineWidth() functions, return the properties of
+ the line that underlines, overlines or strikes out the
+ characters. These functions are all fast.
+
+ There are also some functions that operate on the set of glyphs in
+ the font: minLeftBearing(), minRightBearing() and maxWidth().
+ These are by necessity slow, and we recommend avoiding them if
+ possible.
+
+ For each character, you can get its width(), leftBearing() and
+ rightBearing() and find out whether it is in the font using
+ inFont(). You can also treat the character as a string, and use
+ the string functions on it.
+
+ The string functions include width(), to return the width of a
+ string in pixels (or points, for a printer), boundingRect(), to
+ return a rectangle large enough to contain the rendered string,
+ and size(), to return the size of that rectangle.
+
+ Example:
+ \snippet doc/src/snippets/code/src_gui_text_qfontmetrics.cpp 0
+
+ \sa QFont, QFontInfo, QFontDatabase, QFontComboBox, {Character Map Example}
+*/
+
+/*!
+ \fn QRect QFontMetrics::boundingRect(int x, int y, int width, int height,
+ int flags, const QString &text, int tabStops, int *tabArray) const
+ \overload
+
+ Returns the bounding rectangle for the given \a text within the
+ rectangle specified by the \a x and \a y coordinates, \a width, and
+ \a height.
+
+ If Qt::TextExpandTabs is set in \a flags and \a tabArray is
+ non-null, it specifies a 0-terminated sequence of pixel-positions
+ for tabs; otherwise, if \a tabStops is non-zero, it is used as the
+ tab spacing (in pixels).
+*/
+
+/*!
+ Constructs a font metrics object for \a font.
+
+ The font metrics will be compatible with the paintdevice used to
+ create \a font.
+
+ The font metrics object holds the information for the font that is
+ passed in the constructor at the time it is created, and is not
+ updated if the font's attributes are changed later.
+
+ Use QFontMetrics(const QFont &, QPaintDevice *) to get the font
+ metrics that are compatible with a certain paint device.
+*/
+QFontMetrics::QFontMetrics(const QFont &font)
+ : d(font.d)
+{
+ d->ref.ref();
+}
+
+/*!
+ Constructs a font metrics object for \a font and \a paintdevice.
+
+ The font metrics will be compatible with the paintdevice passed.
+ If the \a paintdevice is 0, the metrics will be screen-compatible,
+ ie. the metrics you get if you use the font for drawing text on a
+ \link QWidget widgets\endlink or \link QPixmap pixmaps\endlink,
+ not on a QPicture or QPrinter.
+
+ The font metrics object holds the information for the font that is
+ passed in the constructor at the time it is created, and is not
+ updated if the font's attributes are changed later.
+*/
+QFontMetrics::QFontMetrics(const QFont &font, QPaintDevice *paintdevice)
+{
+ int dpi = paintdevice ? paintdevice->logicalDpiY() : qt_defaultDpi();
+#ifdef Q_WS_X11
+ const QX11Info *info = qt_x11Info(paintdevice);
+ int screen = info ? info->screen() : 0;
+#else
+ const int screen = 0;
+#endif
+ if (font.d->dpi != dpi || font.d->screen != screen ) {
+ d = new QFontPrivate(*font.d);
+ d->dpi = dpi;
+ d->screen = screen;
+ } else {
+ d = font.d;
+ d->ref.ref();
+ }
+
+}
+
+/*!
+ Constructs a copy of \a fm.
+*/
+QFontMetrics::QFontMetrics(const QFontMetrics &fm)
+ : d(fm.d)
+{ d->ref.ref(); }
+
+/*!
+ Destroys the font metrics object and frees all allocated
+ resources.
+*/
+QFontMetrics::~QFontMetrics()
+{
+ if (!d->ref.deref())
+ delete d;
+}
+
+/*!
+ Assigns the font metrics \a fm.
+*/
+QFontMetrics &QFontMetrics::operator=(const QFontMetrics &fm)
+{
+ qAtomicAssign(d, fm.d);
+ return *this;
+}
+
+/*!
+ \overload
+ Returns true if \a other is equal to this object; otherwise
+ returns false.
+
+ Two font metrics are considered equal if they were constructed
+ from the same QFont and the paint devices they were constructed
+ for are considered compatible.
+
+ \sa operator!=()
+*/
+bool QFontMetrics::operator ==(const QFontMetrics &other) const
+{
+ return d == other.d;
+}
+
+/*!
+ Returns true if \a other is equal to this object; otherwise
+ returns false.
+
+ Two font metrics are considered equal if they were constructed
+ from the same QFont and the paint devices they were constructed
+ for are considered compatible.
+
+ \sa operator!=()
+*/
+bool QFontMetrics::operator ==(const QFontMetrics &other)
+{
+ return d == other.d;
+}
+
+/*!
+ \fn bool QFontMetrics::operator!=(const QFontMetrics &other)
+
+ Returns true if \a other is not equal to this object; otherwise returns false.
+
+ Two font metrics are considered equal if they were constructed
+ from the same QFont and the paint devices they were constructed
+ for are considered compatible.
+
+ \sa operator==()
+*/
+
+/*!
+ \fn bool QFontMetrics::operator !=(const QFontMetrics &other) const
+
+ Returns true if \a other is not equal to this object; otherwise returns false.
+
+ Two font metrics are considered equal if they were constructed
+ from the same QFont and the paint devices they were constructed
+ for are considered compatible.
+
+ \sa operator==()
+*/
+
+/*!
+ Returns the ascent of the font.
+
+ The ascent of a font is the distance from the baseline to the
+ highest position characters extend to. In practice, some font
+ designers break this rule, e.g. when they put more than one accent
+ on top of a character, or to accommodate an unusual character in
+ an exotic language, so it is possible (though rare) that this
+ value will be too small.
+
+ \sa descent()
+*/
+int QFontMetrics::ascent() const
+{
+ QFontEngine *engine = d->engineForScript(QUnicodeTables::Common);
+ Q_ASSERT(engine != 0);
+ return qRound(engine->ascent());
+}
+
+
+/*!
+ Returns the descent of the font.
+
+ The descent is the distance from the base line to the lowest point
+ characters extend to. In practice, some font designers break this rule,
+ e.g. to accommodate an unusual character in an exotic language, so
+ it is possible (though rare) that this value will be too small.
+
+ \sa ascent()
+*/
+int QFontMetrics::descent() const
+{
+ QFontEngine *engine = d->engineForScript(QUnicodeTables::Common);
+ Q_ASSERT(engine != 0);
+ return qRound(engine->descent());
+}
+
+/*!
+ Returns the height of the font.
+
+ This is always equal to ascent()+descent()+1 (the 1 is for the
+ base line).
+
+ \sa leading(), lineSpacing()
+*/
+int QFontMetrics::height() const
+{
+ QFontEngine *engine = d->engineForScript(QUnicodeTables::Common);
+ Q_ASSERT(engine != 0);
+ return qRound(engine->ascent() + engine->descent()) + 1;
+}
+
+/*!
+ Returns the leading of the font.
+
+ This is the natural inter-line spacing.
+
+ \sa height(), lineSpacing()
+*/
+int QFontMetrics::leading() const
+{
+ QFontEngine *engine = d->engineForScript(QUnicodeTables::Common);
+ Q_ASSERT(engine != 0);
+ return qRound(engine->leading());
+}
+
+/*!
+ Returns the distance from one base line to the next.
+
+ This value is always equal to leading()+height().
+
+ \sa height(), leading()
+*/
+int QFontMetrics::lineSpacing() const
+{
+ QFontEngine *engine = d->engineForScript(QUnicodeTables::Common);
+ Q_ASSERT(engine != 0);
+ return qRound(engine->leading() + engine->ascent() + engine->descent()) + 1;
+}
+
+/*!
+ Returns the minimum left bearing of the font.
+
+ This is the smallest leftBearing(char) of all characters in the
+ font.
+
+ Note that this function can be very slow if the font is large.
+
+ \sa minRightBearing(), leftBearing()
+*/
+int QFontMetrics::minLeftBearing() const
+{
+ QFontEngine *engine = d->engineForScript(QUnicodeTables::Common);
+ Q_ASSERT(engine != 0);
+ return qRound(engine->minLeftBearing());
+}
+
+/*!
+ Returns the minimum right bearing of the font.
+
+ This is the smallest rightBearing(char) of all characters in the
+ font.
+
+ Note that this function can be very slow if the font is large.
+
+ \sa minLeftBearing(), rightBearing()
+*/
+int QFontMetrics::minRightBearing() const
+{
+ QFontEngine *engine = d->engineForScript(QUnicodeTables::Common);
+ Q_ASSERT(engine != 0);
+ return qRound(engine->minRightBearing());
+}
+
+/*!
+ Returns the width of the widest character in the font.
+*/
+int QFontMetrics::maxWidth() const
+{
+ QFontEngine *engine = d->engineForScript(QUnicodeTables::Common);
+ Q_ASSERT(engine != 0);
+ return qRound(engine->maxCharWidth());
+}
+
+/*!
+ Returns the 'x' height of the font. This is often but not always
+ the same as the height of the character 'x'.
+*/
+int QFontMetrics::xHeight() const
+{
+ QFontEngine *engine = d->engineForScript(QUnicodeTables::Common);
+ Q_ASSERT(engine != 0);
+ if (d->capital == QFont::SmallCaps)
+ return qRound(d->smallCapsFontPrivate()->engineForScript(QUnicodeTables::Common)->ascent());
+ return qRound(engine->xHeight());
+}
+
+/*!
+ \since 4.2
+
+ Returns the average width of glyphs in the font.
+*/
+int QFontMetrics::averageCharWidth() const
+{
+ QFontEngine *engine = d->engineForScript(QUnicodeTables::Common);
+ Q_ASSERT(engine != 0);
+ return qRound(engine->averageCharWidth());
+}
+
+/*!
+ Returns true if character \a ch is a valid character in the font;
+ otherwise returns false.
+*/
+bool QFontMetrics::inFont(QChar ch) const
+{
+ const int script = QUnicodeTables::script(ch);
+ QFontEngine *engine = d->engineForScript(script);
+ Q_ASSERT(engine != 0);
+ if (engine->type() == QFontEngine::Box)
+ return false;
+ return engine->canRender(&ch, 1);
+}
+
+/*!
+ Returns the left bearing of character \a ch in the font.
+
+ The left bearing is the right-ward distance of the left-most pixel
+ of the character from the logical origin of the character. This
+ value is negative if the pixels of the character extend to the
+ left of the logical origin.
+
+ See width(QChar) for a graphical description of this metric.
+
+ \sa rightBearing(), minLeftBearing(), width()
+*/
+int QFontMetrics::leftBearing(QChar ch) const
+{
+ const int script = QUnicodeTables::script(ch);
+ QFontEngine *engine;
+ if (d->capital == QFont::SmallCaps && ch.isLower())
+ engine = d->smallCapsFontPrivate()->engineForScript(script);
+ else
+ engine = d->engineForScript(script);
+ Q_ASSERT(engine != 0);
+ if (engine->type() == QFontEngine::Box)
+ return 0;
+
+ d->alterCharForCapitalization(ch);
+
+ QGlyphLayoutArray<10> glyphs;
+ int nglyphs = 9;
+ engine->stringToCMap(&ch, 1, &glyphs, &nglyphs, 0);
+ // ### can nglyphs != 1 happen at all? Not currently I think
+ glyph_metrics_t gi = engine->boundingBox(glyphs.glyphs[0]);
+ return qRound(gi.x);
+}
+
+/*!
+ Returns the right bearing of character \a ch in the font.
+
+ The right bearing is the left-ward distance of the right-most
+ pixel of the character from the logical origin of a subsequent
+ character. This value is negative if the pixels of the character
+ extend to the right of the width() of the character.
+
+ See width() for a graphical description of this metric.
+
+ \sa leftBearing(), minRightBearing(), width()
+*/
+int QFontMetrics::rightBearing(QChar ch) const
+{
+ const int script = QUnicodeTables::script(ch);
+ QFontEngine *engine;
+ if (d->capital == QFont::SmallCaps && ch.isLower())
+ engine = d->smallCapsFontPrivate()->engineForScript(script);
+ else
+ engine = d->engineForScript(script);
+ Q_ASSERT(engine != 0);
+ if (engine->type() == QFontEngine::Box)
+ return 0;
+
+ d->alterCharForCapitalization(ch);
+
+ QGlyphLayoutArray<10> glyphs;
+ int nglyphs = 9;
+ engine->stringToCMap(&ch, 1, &glyphs, &nglyphs, 0);
+ // ### can nglyphs != 1 happen at all? Not currently I think
+ glyph_metrics_t gi = engine->boundingBox(glyphs.glyphs[0]);
+ return qRound(gi.xoff - gi.x - gi.width);
+}
+
+/*!
+ Returns the width in pixels of the first \a len characters of \a
+ text. If \a len is negative (the default), the entire string is
+ used.
+
+ Note that this value is \e not equal to boundingRect().width();
+ boundingRect() returns a rectangle describing the pixels this
+ string will cover whereas width() returns the distance to where
+ the next string should be drawn.
+
+ \sa boundingRect()
+*/
+int QFontMetrics::width(const QString &text, int len) const
+{
+ if (len < 0)
+ len = text.length();
+ if (len == 0)
+ return 0;
+
+ QTextEngine layout(text, d);
+ layout.ignoreBidi = true;
+ return qRound(layout.width(0, len));
+}
+
+/*!
+ \overload
+
+ \img bearings.png Bearings
+
+ Returns the logical width of character \a ch in pixels. This is a
+ distance appropriate for drawing a subsequent character after \a
+ ch.
+
+ Some of the metrics are described in the image to the right. The
+ central dark rectangles cover the logical width() of each
+ character. The outer pale rectangles cover the leftBearing() and
+ rightBearing() of each character. Notice that the bearings of "f"
+ in this particular font are both negative, while the bearings of
+ "o" are both positive.
+
+ \warning This function will produce incorrect results for Arabic
+ characters or non-spacing marks in the middle of a string, as the
+ glyph shaping and positioning of marks that happens when
+ processing strings cannot be taken into account. Use charWidth()
+ instead if you aren't looking for the width of isolated
+ characters.
+
+ \sa boundingRect(), charWidth()
+*/
+int QFontMetrics::width(QChar ch) const
+{
+ if (QChar::category(ch.unicode()) == QChar::Mark_NonSpacing)
+ return 0;
+
+ const int script = QUnicodeTables::script(ch);
+ QFontEngine *engine;
+ if (d->capital == QFont::SmallCaps && ch.isLower())
+ engine = d->smallCapsFontPrivate()->engineForScript(script);
+ else
+ engine = d->engineForScript(script);
+ Q_ASSERT(engine != 0);
+
+ d->alterCharForCapitalization(ch);
+
+ QGlyphLayoutArray<8> glyphs;
+ int nglyphs = 7;
+ engine->stringToCMap(&ch, 1, &glyphs, &nglyphs, 0);
+ return qRound(glyphs.advances_x[0]);
+}
+
+/*! \obsolete
+
+ Returns the width of the character at position \a pos in the
+ string \a text.
+
+ The whole string is needed, as the glyph drawn may change
+ depending on the context (the letter before and after the current
+ one) for some languages (e.g. Arabic).
+
+ This function also takes non spacing marks and ligatures into
+ account.
+*/
+int QFontMetrics::charWidth(const QString &text, int pos) const
+{
+ if (pos < 0 || pos > (int)text.length())
+ return 0;
+
+ QChar ch = text.unicode()[pos];
+ const int script = QUnicodeTables::script(ch);
+ int width;
+
+ if (script != QUnicodeTables::Common) {
+ // complex script shaping. Have to do some hard work
+ int from = qMax(0, pos - 8);
+ int to = qMin(text.length(), pos + 8);
+ QString cstr = QString::fromRawData(text.unicode() + from, to - from);
+ QTextEngine layout(cstr, d);
+ layout.ignoreBidi = true;
+ layout.itemize();
+ width = qRound(layout.width(pos-from, 1));
+ } else if (QChar::category(ch.unicode()) == QChar::Mark_NonSpacing) {
+ width = 0;
+ } else {
+ QFontEngine *engine;
+ if (d->capital == QFont::SmallCaps && ch.isLower())
+ engine = d->smallCapsFontPrivate()->engineForScript(script);
+ else
+ engine = d->engineForScript(script);
+ Q_ASSERT(engine != 0);
+
+ d->alterCharForCapitalization(ch);
+
+ QGlyphLayoutArray<8> glyphs;
+ int nglyphs = 7;
+ engine->stringToCMap(&ch, 1, &glyphs, &nglyphs, 0);
+ width = qRound(glyphs.advances_x[0]);
+ }
+ return width;
+}
+
+/*!
+ Returns the bounding rectangle of the characters in the string
+ specified by \a text. The bounding rectangle always covers at least
+ the set of pixels the text would cover if drawn at (0, 0).
+
+ Note that the bounding rectangle may extend to the left of (0, 0),
+ e.g. for italicized fonts, and that the width of the returned
+ rectangle might be different than what the width() method returns.
+
+ If you want to know the advance width of the string (to layout
+ a set of strings next to each other), use width() instead.
+
+ Newline characters are processed as normal characters, \e not as
+ linebreaks.
+
+ The height of the bounding rectangle is at least as large as the
+ value returned by height().
+
+ \sa width(), height(), QPainter::boundingRect(), tightBoundingRect()
+*/
+QRect QFontMetrics::boundingRect(const QString &text) const
+{
+ if (text.length() == 0)
+ return QRect();
+
+ QTextEngine layout(text, d);
+ layout.ignoreBidi = true;
+ layout.itemize();
+ glyph_metrics_t gm = layout.boundingBox(0, text.length());
+ return QRect(qRound(gm.x), qRound(gm.y), qRound(gm.width), qRound(gm.height));
+}
+
+/*!
+ Returns the rectangle that is covered by ink if character \a ch
+ were to be drawn at the origin of the coordinate system.
+
+ Note that the bounding rectangle may extend to the left of (0, 0),
+ e.g. for italicized fonts, and that the text output may cover \e
+ all pixels in the bounding rectangle. For a space character the rectangle
+ will usually be empty.
+
+ Note that the rectangle usually extends both above and below the
+ base line.
+
+ \warning The width of the returned rectangle is not the advance width
+ of the character. Use boundingRect(const QString &) or width() instead.
+
+ \sa width()
+*/
+QRect QFontMetrics::boundingRect(QChar ch) const
+{
+ const int script = QUnicodeTables::script(ch);
+ QFontEngine *engine;
+ if (d->capital == QFont::SmallCaps && ch.isLower())
+ engine = d->smallCapsFontPrivate()->engineForScript(script);
+ else
+ engine = d->engineForScript(script);
+ Q_ASSERT(engine != 0);
+
+ d->alterCharForCapitalization(ch);
+
+ QGlyphLayoutArray<10> glyphs;
+ int nglyphs = 9;
+ engine->stringToCMap(&ch, 1, &glyphs, &nglyphs, 0);
+ glyph_metrics_t gm = engine->boundingBox(glyphs.glyphs[0]);
+ return QRect(qRound(gm.x), qRound(gm.y), qRound(gm.width), qRound(gm.height));
+}
+
+/*!
+ \overload
+
+ Returns the bounding rectangle of the characters in the string
+ specified by \a text, which is the set of pixels the text would
+ cover if drawn at (0, 0). The drawing, and hence the bounding
+ rectangle, is constrained to the rectangle \a rect.
+
+ The \a flags argument is the bitwise OR of the following flags:
+ \list
+ \o Qt::AlignLeft aligns to the left border, except for
+ Arabic and Hebrew where it aligns to the right.
+ \o Qt::AlignRight aligns to the right border, except for
+ Arabic and Hebrew where it aligns to the left.
+ \o Qt::AlignJustify produces justified text.
+ \o Qt::AlignHCenter aligns horizontally centered.
+ \o Qt::AlignTop aligns to the top border.
+ \o Qt::AlignBottom aligns to the bottom border.
+ \o Qt::AlignVCenter aligns vertically centered
+ \o Qt::AlignCenter (== \c{Qt::AlignHCenter | Qt::AlignVCenter})
+ \o Qt::TextSingleLine ignores newline characters in the text.
+ \o Qt::TextExpandTabs expands tabs (see below)
+ \o Qt::TextShowMnemonic interprets "&amp;x" as \underline{x}, i.e. underlined.
+ \o Qt::TextWordWrap breaks the text to fit the rectangle.
+ \endlist
+
+ Qt::Horizontal alignment defaults to Qt::AlignLeft and vertical
+ alignment defaults to Qt::AlignTop.
+
+ If several of the horizontal or several of the vertical alignment
+ flags are set, the resulting alignment is undefined.
+
+ If Qt::TextExpandTabs is set in \a flags, then: if \a tabArray is
+ non-null, it specifies a 0-terminated sequence of pixel-positions
+ for tabs; otherwise if \a tabStops is non-zero, it is used as the
+ tab spacing (in pixels).
+
+ Note that the bounding rectangle may extend to the left of (0, 0),
+ e.g. for italicized fonts, and that the text output may cover \e
+ all pixels in the bounding rectangle.
+
+ Newline characters are processed as linebreaks.
+
+ Despite the different actual character heights, the heights of the
+ bounding rectangles of "Yes" and "yes" are the same.
+
+ The bounding rectangle returned by this function is somewhat larger
+ than that calculated by the simpler boundingRect() function. This
+ function uses the \link minLeftBearing() maximum left \endlink and
+ \link minRightBearing() right \endlink font bearings as is
+ necessary for multi-line text to align correctly. Also,
+ fontHeight() and lineSpacing() are used to calculate the height,
+ rather than individual character heights.
+
+ \sa width(), QPainter::boundingRect(), Qt::Alignment
+*/
+QRect QFontMetrics::boundingRect(const QRect &rect, int flags, const QString &text, int tabStops,
+ int *tabArray) const
+{
+ int tabArrayLen = 0;
+ if (tabArray)
+ while (tabArray[tabArrayLen])
+ tabArrayLen++;
+
+ QRectF rb;
+ QRectF rr(rect);
+ qt_format_text(QFont(d), rr, flags | Qt::TextDontPrint, text, &rb, tabStops, tabArray,
+ tabArrayLen, 0);
+
+ return rb.toAlignedRect();
+}
+
+/*!
+ Returns the size in pixels of \a text.
+
+ The \a flags argument is the bitwise OR of the following flags:
+ \list
+ \o Qt::TextSingleLine ignores newline characters.
+ \o Qt::TextExpandTabs expands tabs (see below)
+ \o Qt::TextShowMnemonic interprets "&amp;x" as \underline{x}, i.e. underlined.
+ \o Qt::TextWordBreak breaks the text to fit the rectangle.
+ \endlist
+
+ If Qt::TextExpandTabs is set in \a flags, then: if \a tabArray is
+ non-null, it specifies a 0-terminated sequence of pixel-positions
+ for tabs; otherwise if \a tabStops is non-zero, it is used as the
+ tab spacing (in pixels).
+
+ Newline characters are processed as linebreaks.
+
+ Despite the different actual character heights, the heights of the
+ bounding rectangles of "Yes" and "yes" are the same.
+
+ \sa boundingRect()
+*/
+QSize QFontMetrics::size(int flags, const QString &text, int tabStops, int *tabArray) const
+{
+ return boundingRect(QRect(0,0,0,0), flags, text, tabStops, tabArray).size();
+}
+
+/*!
+ \since 4.3
+
+ Returns a tight bounding rectangle around the characters in the
+ string specified by \a text. The bounding rectangle always covers
+ at least the set of pixels the text would cover if drawn at (0,
+ 0).
+
+ Note that the bounding rectangle may extend to the left of (0, 0),
+ e.g. for italicized fonts, and that the width of the returned
+ rectangle might be different than what the width() method returns.
+
+ If you want to know the advance width of the string (to layout
+ a set of strings next to each other), use width() instead.
+
+ Newline characters are processed as normal characters, \e not as
+ linebreaks.
+
+ \warning Calling this method is very slow on Windows.
+
+ \sa width(), height(), boundingRect()
+*/
+QRect QFontMetrics::tightBoundingRect(const QString &text) const
+{
+ if (text.length() == 0)
+ return QRect();
+
+ QTextEngine layout(text, d);
+ layout.ignoreBidi = true;
+ layout.itemize();
+ glyph_metrics_t gm = layout.tightBoundingBox(0, text.length());
+ return QRect(qRound(gm.x), qRound(gm.y), qRound(gm.width), qRound(gm.height));
+}
+
+
+/*!
+ \since 4.2
+
+ If the string \a text is wider than \a width, returns an elided
+ version of the string (i.e., a string with "..." in it).
+ Otherwise, returns the original string.
+
+ The \a mode parameter specifies whether the text is elided on the
+ left (e.g., "...tech"), in the middle (e.g., "Tr...ch"), or on
+ the right (e.g., "Trol...").
+
+ The \a width is specified in pixels, not characters.
+
+ The \a flags argument is optional and currently only supports
+ Qt::TextShowMnemonic as value.
+
+ The elide mark will follow the \l{Qt::LayoutDirection}{layout
+ direction}; it will be on the right side of the text for
+ right-to-left layouts, and on the left side for right-to-left
+ layouts. Note that this behavior is independent of the text
+ language.
+
+*/
+QString QFontMetrics::elidedText(const QString &text, Qt::TextElideMode mode, int width, int flags) const
+{
+ QStackTextEngine engine(text, QFont(d));
+ return engine.elidedText(mode, width, flags);
+}
+
+/*!
+ Returns the distance from the base line to where an underscore
+ should be drawn.
+
+ \sa overlinePos(), strikeOutPos(), lineWidth()
+*/
+int QFontMetrics::underlinePos() const
+{
+ QFontEngine *engine = d->engineForScript(QUnicodeTables::Common);
+ Q_ASSERT(engine != 0);
+ return qRound(engine->underlinePosition());
+}
+
+/*!
+ Returns the distance from the base line to where an overline
+ should be drawn.
+
+ \sa underlinePos(), strikeOutPos(), lineWidth()
+*/
+int QFontMetrics::overlinePos() const
+{
+ return ascent() + 1;
+}
+
+/*!
+ Returns the distance from the base line to where the strikeout
+ line should be drawn.
+
+ \sa underlinePos(), overlinePos(), lineWidth()
+*/
+int QFontMetrics::strikeOutPos() const
+{
+ int pos = ascent() / 3;
+ return pos > 0 ? pos : 1;
+}
+
+/*!
+ Returns the width of the underline and strikeout lines, adjusted
+ for the point size of the font.
+
+ \sa underlinePos(), overlinePos(), strikeOutPos()
+*/
+int QFontMetrics::lineWidth() const
+{
+ QFontEngine *engine = d->engineForScript(QUnicodeTables::Common);
+ Q_ASSERT(engine != 0);
+ return qRound(engine->lineThickness());
+}
+
+
+
+
+/*****************************************************************************
+ QFontMetricsF member functions
+ *****************************************************************************/
+
+/*!
+ \class QFontMetricsF
+ \reentrant
+
+ \brief The QFontMetricsF class provides font metrics information.
+
+ \ingroup multimedia
+ \ingroup shared
+ \ingroup text
+
+ QFontMetricsF functions calculate the size of characters and
+ strings for a given font. You can construct a QFontMetricsF object
+ with an existing QFont to obtain metrics for that font. If the
+ font is changed later, the font metrics object is \e not updated.
+
+ Once created, the object provides functions to access the
+ individual metrics of the font, its characters, and for strings
+ rendered in the font.
+
+ There are several functions that operate on the font: ascent(),
+ descent(), height(), leading() and lineSpacing() return the basic
+ size properties of the font. The underlinePos(), overlinePos(),
+ strikeOutPos() and lineWidth() functions, return the properties of
+ the line that underlines, overlines or strikes out the
+ characters. These functions are all fast.
+
+ There are also some functions that operate on the set of glyphs in
+ the font: minLeftBearing(), minRightBearing() and maxWidth().
+ These are by necessity slow, and we recommend avoiding them if
+ possible.
+
+ For each character, you can get its width(), leftBearing() and
+ rightBearing() and find out whether it is in the font using
+ inFont(). You can also treat the character as a string, and use
+ the string functions on it.
+
+ The string functions include width(), to return the width of a
+ string in pixels (or points, for a printer), boundingRect(), to
+ return a rectangle large enough to contain the rendered string,
+ and size(), to return the size of that rectangle.
+
+ Example:
+ \snippet doc/src/snippets/code/src_gui_text_qfontmetrics.cpp 1
+
+ \sa QFont QFontInfo QFontDatabase
+*/
+
+/*!
+ \since 4.2
+
+ Constructs a font metrics object with floating point precision
+ from the given \a fontMetrics object.
+*/
+QFontMetricsF::QFontMetricsF(const QFontMetrics &fontMetrics)
+ : d(fontMetrics.d)
+{
+ d->ref.ref();
+}
+
+/*!
+ \since 4.2
+
+ Assigns \a other to this object.
+*/
+QFontMetricsF &QFontMetricsF::operator=(const QFontMetrics &other)
+{
+ qAtomicAssign(d, other.d);
+ return *this;
+}
+
+/*!
+ Constructs a font metrics object for \a font.
+
+ The font metrics will be compatible with the paintdevice used to
+ create \a font.
+
+ The font metrics object holds the information for the font that is
+ passed in the constructor at the time it is created, and is not
+ updated if the font's attributes are changed later.
+
+ Use QFontMetricsF(const QFont &, QPaintDevice *) to get the font
+ metrics that are compatible with a certain paint device.
+*/
+QFontMetricsF::QFontMetricsF(const QFont &font)
+ : d(font.d)
+{
+ d->ref.ref();
+}
+
+/*!
+ Constructs a font metrics object for \a font and \a paintdevice.
+
+ The font metrics will be compatible with the paintdevice passed.
+ If the \a paintdevice is 0, the metrics will be screen-compatible,
+ ie. the metrics you get if you use the font for drawing text on a
+ \link QWidget widgets\endlink or \link QPixmap pixmaps\endlink,
+ not on a QPicture or QPrinter.
+
+ The font metrics object holds the information for the font that is
+ passed in the constructor at the time it is created, and is not
+ updated if the font's attributes are changed later.
+*/
+QFontMetricsF::QFontMetricsF(const QFont &font, QPaintDevice *paintdevice)
+{
+ int dpi = paintdevice ? paintdevice->logicalDpiY() : qt_defaultDpi();
+#ifdef Q_WS_X11
+ const QX11Info *info = qt_x11Info(paintdevice);
+ int screen = info ? info->screen() : 0;
+#else
+ const int screen = 0;
+#endif
+ if (font.d->dpi != dpi || font.d->screen != screen ) {
+ d = new QFontPrivate(*font.d);
+ d->dpi = dpi;
+ d->screen = screen;
+ } else {
+ d = font.d;
+ d->ref.ref();
+ }
+
+}
+
+/*!
+ Constructs a copy of \a fm.
+*/
+QFontMetricsF::QFontMetricsF(const QFontMetricsF &fm)
+ : d(fm.d)
+{ d->ref.ref(); }
+
+/*!
+ Destroys the font metrics object and frees all allocated
+ resources.
+*/
+QFontMetricsF::~QFontMetricsF()
+{
+ if (!d->ref.deref())
+ delete d;
+}
+
+/*!
+ Assigns the font metrics \a fm to this font metrics object.
+*/
+QFontMetricsF &QFontMetricsF::operator=(const QFontMetricsF &fm)
+{
+ qAtomicAssign(d, fm.d);
+ return *this;
+}
+
+/*!
+ \overload
+ Returns true if the font metrics are equal to the \a other font
+ metrics; otherwise returns false.
+
+ Two font metrics are considered equal if they were constructed from the
+ same QFont and the paint devices they were constructed for are
+ considered to be compatible.
+*/
+bool QFontMetricsF::operator ==(const QFontMetricsF &other) const
+{
+ return d == other.d;
+}
+
+/*!
+ Returns true if the font metrics are equal to the \a other font
+ metrics; otherwise returns false.
+
+ Two font metrics are considered equal if they were constructed from the
+ same QFont and the paint devices they were constructed for are
+ considered to be compatible.
+*/
+bool QFontMetricsF::operator ==(const QFontMetricsF &other)
+{
+ return d == other.d;
+}
+
+/*!
+ \fn bool QFontMetricsF::operator!=(const QFontMetricsF &other)
+
+ Returns true if the font metrics are not equal to the \a other font
+ metrics; otherwise returns false.
+
+ \sa operator==()
+*/
+
+/*!
+ \fn bool QFontMetricsF::operator !=(const QFontMetricsF &other) const
+ \overload
+
+ Returns true if the font metrics are not equal to the \a other font
+ metrics; otherwise returns false.
+
+ \sa operator==()
+*/
+
+/*!
+ Returns the ascent of the font.
+
+ The ascent of a font is the distance from the baseline to the
+ highest position characters extend to. In practice, some font
+ designers break this rule, e.g. when they put more than one accent
+ on top of a character, or to accommodate an unusual character in
+ an exotic language, so it is possible (though rare) that this
+ value will be too small.
+
+ \sa descent()
+*/
+qreal QFontMetricsF::ascent() const
+{
+ QFontEngine *engine = d->engineForScript(QUnicodeTables::Common);
+ Q_ASSERT(engine != 0);
+ return engine->ascent().toReal();
+}
+
+
+/*!
+ Returns the descent of the font.
+
+ The descent is the distance from the base line to the lowest point
+ characters extend to. (Note that this is different from X, which
+ adds 1 pixel.) In practice, some font designers break this rule,
+ e.g. to accommodate an unusual character in an exotic language, so
+ it is possible (though rare) that this value will be too small.
+
+ \sa ascent()
+*/
+qreal QFontMetricsF::descent() const
+{
+ QFontEngine *engine = d->engineForScript(QUnicodeTables::Common);
+ Q_ASSERT(engine != 0);
+ return engine->descent().toReal();
+}
+
+/*!
+ Returns the height of the font.
+
+ This is always equal to ascent()+descent()+1 (the 1 is for the
+ base line).
+
+ \sa leading(), lineSpacing()
+*/
+qreal QFontMetricsF::height() const
+{
+ QFontEngine *engine = d->engineForScript(QUnicodeTables::Common);
+ Q_ASSERT(engine != 0);
+
+ return (engine->ascent() + engine->descent() + 1).toReal();
+}
+
+/*!
+ Returns the leading of the font.
+
+ This is the natural inter-line spacing.
+
+ \sa height(), lineSpacing()
+*/
+qreal QFontMetricsF::leading() const
+{
+ QFontEngine *engine = d->engineForScript(QUnicodeTables::Common);
+ Q_ASSERT(engine != 0);
+ return engine->leading().toReal();
+}
+
+/*!
+ Returns the distance from one base line to the next.
+
+ This value is always equal to leading()+height().
+
+ \sa height(), leading()
+*/
+qreal QFontMetricsF::lineSpacing() const
+{
+ QFontEngine *engine = d->engineForScript(QUnicodeTables::Common);
+ Q_ASSERT(engine != 0);
+ return (engine->leading() + engine->ascent() + engine->descent() + 1).toReal();
+}
+
+/*!
+ Returns the minimum left bearing of the font.
+
+ This is the smallest leftBearing(char) of all characters in the
+ font.
+
+ Note that this function can be very slow if the font is large.
+
+ \sa minRightBearing(), leftBearing()
+*/
+qreal QFontMetricsF::minLeftBearing() const
+{
+ QFontEngine *engine = d->engineForScript(QUnicodeTables::Common);
+ Q_ASSERT(engine != 0);
+ return engine->minLeftBearing();
+}
+
+/*!
+ Returns the minimum right bearing of the font.
+
+ This is the smallest rightBearing(char) of all characters in the
+ font.
+
+ Note that this function can be very slow if the font is large.
+
+ \sa minLeftBearing(), rightBearing()
+*/
+qreal QFontMetricsF::minRightBearing() const
+{
+ QFontEngine *engine = d->engineForScript(QUnicodeTables::Common);
+ Q_ASSERT(engine != 0);
+ return engine->minRightBearing();
+}
+
+/*!
+ Returns the width of the widest character in the font.
+*/
+qreal QFontMetricsF::maxWidth() const
+{
+ QFontEngine *engine = d->engineForScript(QUnicodeTables::Common);
+ Q_ASSERT(engine != 0);
+ return engine->maxCharWidth();
+}
+
+/*!
+ Returns the 'x' height of the font. This is often but not always
+ the same as the height of the character 'x'.
+*/
+qreal QFontMetricsF::xHeight() const
+{
+ QFontEngine *engine = d->engineForScript(QUnicodeTables::Common);
+ Q_ASSERT(engine != 0);
+ if (d->capital == QFont::SmallCaps)
+ return d->smallCapsFontPrivate()->engineForScript(QUnicodeTables::Common)->ascent().toReal();
+ return engine->xHeight().toReal();
+}
+
+/*!
+ \since 4.2
+
+ Returns the average width of glyphs in the font.
+*/
+qreal QFontMetricsF::averageCharWidth() const
+{
+ QFontEngine *engine = d->engineForScript(QUnicodeTables::Common);
+ Q_ASSERT(engine != 0);
+ return engine->averageCharWidth().toReal();
+}
+
+/*!
+ Returns true if character \a ch is a valid character in the font;
+ otherwise returns false.
+*/
+bool QFontMetricsF::inFont(QChar ch) const
+{
+ const int script = QUnicodeTables::script(ch);
+ QFontEngine *engine = d->engineForScript(script);
+ Q_ASSERT(engine != 0);
+ if (engine->type() == QFontEngine::Box)
+ return false;
+ return engine->canRender(&ch, 1);
+}
+
+/*!
+ Returns the left bearing of character \a ch in the font.
+
+ The left bearing is the right-ward distance of the left-most pixel
+ of the character from the logical origin of the character. This
+ value is negative if the pixels of the character extend to the
+ left of the logical origin.
+
+ See width(QChar) for a graphical description of this metric.
+
+ \sa rightBearing(), minLeftBearing(), width()
+*/
+qreal QFontMetricsF::leftBearing(QChar ch) const
+{
+ const int script = QUnicodeTables::script(ch);
+ QFontEngine *engine;
+ if (d->capital == QFont::SmallCaps && ch.isLower())
+ engine = d->smallCapsFontPrivate()->engineForScript(script);
+ else
+ engine = d->engineForScript(script);
+ Q_ASSERT(engine != 0);
+ if (engine->type() == QFontEngine::Box)
+ return 0;
+
+ d->alterCharForCapitalization(ch);
+
+ QGlyphLayoutArray<10> glyphs;
+ int nglyphs = 9;
+ engine->stringToCMap(&ch, 1, &glyphs, &nglyphs, 0);
+ // ### can nglyphs != 1 happen at all? Not currently I think
+ glyph_metrics_t gi = engine->boundingBox(glyphs.glyphs[0]);
+ return gi.x.toReal();
+}
+
+/*!
+ Returns the right bearing of character \a ch in the font.
+
+ The right bearing is the left-ward distance of the right-most
+ pixel of the character from the logical origin of a subsequent
+ character. This value is negative if the pixels of the character
+ extend to the right of the width() of the character.
+
+ See width() for a graphical description of this metric.
+
+ \sa leftBearing(), minRightBearing(), width()
+*/
+qreal QFontMetricsF::rightBearing(QChar ch) const
+{
+ const int script = QUnicodeTables::script(ch);
+ QFontEngine *engine;
+ if (d->capital == QFont::SmallCaps && ch.isLower())
+ engine = d->smallCapsFontPrivate()->engineForScript(script);
+ else
+ engine = d->engineForScript(script);
+ Q_ASSERT(engine != 0);
+ if (engine->type() == QFontEngine::Box)
+ return 0;
+
+ d->alterCharForCapitalization(ch);
+
+ QGlyphLayoutArray<10> glyphs;
+ int nglyphs = 9;
+ engine->stringToCMap(&ch, 1, &glyphs, &nglyphs, 0);
+ // ### can nglyphs != 1 happen at all? Not currently I think
+ glyph_metrics_t gi = engine->boundingBox(glyphs.glyphs[0]);
+ return (gi.xoff - gi.x - gi.width).toReal();
+}
+
+/*!
+ Returns the width in pixels of the characters in the given \a text.
+
+ Note that this value is \e not equal to the width returned by
+ boundingRect().width() because boundingRect() returns a rectangle
+ describing the pixels this string will cover whereas width()
+ returns the distance to where the next string should be drawn.
+
+ \sa boundingRect()
+*/
+qreal QFontMetricsF::width(const QString &text) const
+{
+ QTextEngine layout(text, d);
+ layout.ignoreBidi = true;
+ layout.itemize();
+ return layout.width(0, text.length()).toReal();
+}
+
+/*!
+ \overload
+
+ \img bearings.png Bearings
+
+ Returns the logical width of character \a ch in pixels. This is a
+ distance appropriate for drawing a subsequent character after \a
+ ch.
+
+ Some of the metrics are described in the image to the right. The
+ central dark rectangles cover the logical width() of each
+ character. The outer pale rectangles cover the leftBearing() and
+ rightBearing() of each character. Notice that the bearings of "f"
+ in this particular font are both negative, while the bearings of
+ "o" are both positive.
+
+ \warning This function will produce incorrect results for Arabic
+ characters or non-spacing marks in the middle of a string, as the
+ glyph shaping and positioning of marks that happens when
+ processing strings cannot be taken into account. Use charWidth()
+ instead if you aren't looking for the width of isolated
+ characters.
+
+ \sa boundingRect()
+*/
+qreal QFontMetricsF::width(QChar ch) const
+{
+ if (QChar::category(ch.unicode()) == QChar::Mark_NonSpacing)
+ return 0.;
+
+ const int script = QUnicodeTables::script(ch);
+ QFontEngine *engine;
+ if (d->capital == QFont::SmallCaps && ch.isLower())
+ engine = d->smallCapsFontPrivate()->engineForScript(script);
+ else
+ engine = d->engineForScript(script);
+ Q_ASSERT(engine != 0);
+
+ d->alterCharForCapitalization(ch);
+
+ QGlyphLayoutArray<8> glyphs;
+ int nglyphs = 7;
+ engine->stringToCMap(&ch, 1, &glyphs, &nglyphs, 0);
+ return glyphs.advances_x[0].toReal();
+}
+
+/*!
+ Returns the bounding rectangle of the characters in the string
+ specified by \a text. The bounding rectangle always covers at least
+ the set of pixels the text would cover if drawn at (0, 0).
+
+ Note that the bounding rectangle may extend to the left of (0, 0),
+ e.g. for italicized fonts, and that the width of the returned
+ rectangle might be different than what the width() method returns.
+
+ If you want to know the advance width of the string (to layout
+ a set of strings next to each other), use width() instead.
+
+ Newline characters are processed as normal characters, \e not as
+ linebreaks.
+
+ The height of the bounding rectangle is at least as large as the
+ value returned height().
+
+ \sa width(), height(), QPainter::boundingRect()
+*/
+QRectF QFontMetricsF::boundingRect(const QString &text) const
+{
+ int len = text.length();
+ if (len == 0)
+ return QRectF();
+
+ QTextEngine layout(text, d);
+ layout.ignoreBidi = true;
+ layout.itemize();
+ glyph_metrics_t gm = layout.boundingBox(0, len);
+ return QRectF(gm.x.toReal(), gm.y.toReal(),
+ gm.width.toReal(), gm.height.toReal());
+}
+
+/*!
+ Returns the bounding rectangle of the character \a ch relative to
+ the left-most point on the base line.
+
+ Note that the bounding rectangle may extend to the left of (0, 0),
+ e.g. for italicized fonts, and that the text output may cover \e
+ all pixels in the bounding rectangle.
+
+ Note that the rectangle usually extends both above and below the
+ base line.
+
+ \sa width()
+*/
+QRectF QFontMetricsF::boundingRect(QChar ch) const
+{
+ const int script = QUnicodeTables::script(ch);
+ QFontEngine *engine;
+ if (d->capital == QFont::SmallCaps && ch.isLower())
+ engine = d->smallCapsFontPrivate()->engineForScript(script);
+ else
+ engine = d->engineForScript(script);
+ Q_ASSERT(engine != 0);
+
+ d->alterCharForCapitalization(ch);
+
+ QGlyphLayoutArray<10> glyphs;
+ int nglyphs = 9;
+ engine->stringToCMap(&ch, 1, &glyphs, &nglyphs, 0);
+ glyph_metrics_t gm = engine->boundingBox(glyphs.glyphs[0]);
+ return QRectF(gm.x.toReal(), gm.y.toReal(), gm.width.toReal(), gm.height.toReal());
+}
+
+/*!
+ \overload
+
+ Returns the bounding rectangle of the characters in the given \a text.
+ This is the set of pixels the text would cover if drawn when constrained
+ to the bounding rectangle specified by \a rect.
+
+ The \a flags argument is the bitwise OR of the following flags:
+ \list
+ \o Qt::AlignLeft aligns to the left border, except for
+ Arabic and Hebrew where it aligns to the right.
+ \o Qt::AlignRight aligns to the right border, except for
+ Arabic and Hebrew where it aligns to the left.
+ \o Qt::AlignJustify produces justified text.
+ \o Qt::AlignHCenter aligns horizontally centered.
+ \o Qt::AlignTop aligns to the top border.
+ \o Qt::AlignBottom aligns to the bottom border.
+ \o Qt::AlignVCenter aligns vertically centered
+ \o Qt::AlignCenter (== \c{Qt::AlignHCenter | Qt::AlignVCenter})
+ \o Qt::TextSingleLine ignores newline characters in the text.
+ \o Qt::TextExpandTabs expands tabs (see below)
+ \o Qt::TextShowMnemonic interprets "&amp;x" as \underline{x}, i.e. underlined.
+ \o Qt::TextWordWrap breaks the text to fit the rectangle.
+ \endlist
+
+ Qt::Horizontal alignment defaults to Qt::AlignLeft and vertical
+ alignment defaults to Qt::AlignTop.
+
+ If several of the horizontal or several of the vertical alignment
+ flags are set, the resulting alignment is undefined.
+
+ These flags are defined in \l{Qt::AlignmentFlag}.
+
+ If Qt::TextExpandTabs is set in \a flags, the following behavior is
+ used to interpret tab characters in the text:
+ \list
+ \o If \a tabArray is non-null, it specifies a 0-terminated sequence of
+ pixel-positions for tabs in the text.
+ \o If \a tabStops is non-zero, it is used as the tab spacing (in pixels).
+ \endlist
+
+ Note that the bounding rectangle may extend to the left of (0, 0),
+ e.g. for italicized fonts.
+
+ Newline characters are processed as line breaks.
+
+ Despite the different actual character heights, the heights of the
+ bounding rectangles of "Yes" and "yes" are the same.
+
+ The bounding rectangle returned by this function is somewhat larger
+ than that calculated by the simpler boundingRect() function. This
+ function uses the \link minLeftBearing() maximum left \endlink and
+ \link minRightBearing() right \endlink font bearings as is
+ necessary for multi-line text to align correctly. Also,
+ fontHeight() and lineSpacing() are used to calculate the height,
+ rather than individual character heights.
+
+ \sa width(), QPainter::boundingRect(), Qt::Alignment
+*/
+QRectF QFontMetricsF::boundingRect(const QRectF &rect, int flags, const QString& text,
+ int tabStops, int *tabArray) const
+{
+ int tabArrayLen = 0;
+ if (tabArray)
+ while (tabArray[tabArrayLen])
+ tabArrayLen++;
+
+ QRectF rb;
+ qt_format_text(QFont(d), rect, flags | Qt::TextDontPrint, text, &rb, tabStops, tabArray,
+ tabArrayLen, 0);
+ return rb;
+}
+
+/*!
+ Returns the size in pixels of the characters in the given \a text.
+
+ The \a flags argument is the bitwise OR of the following flags:
+ \list
+ \o Qt::TextSingleLine ignores newline characters.
+ \o Qt::TextExpandTabs expands tabs (see below)
+ \o Qt::TextShowMnemonic interprets "&amp;x" as \underline{x}, i.e. underlined.
+ \o Qt::TextWordBreak breaks the text to fit the rectangle.
+ \endlist
+
+ These flags are defined in \l{Qt::TextFlags}.
+
+ If Qt::TextExpandTabs is set in \a flags, the following behavior is
+ used to interpret tab characters in the text:
+ \list
+ \o If \a tabArray is non-null, it specifies a 0-terminated sequence of
+ pixel-positions for tabs in the text.
+ \o If \a tabStops is non-zero, it is used as the tab spacing (in pixels).
+ \endlist
+
+ Newline characters are processed as line breaks.
+
+ Note: Despite the different actual character heights, the heights of the
+ bounding rectangles of "Yes" and "yes" are the same.
+
+ \sa boundingRect()
+*/
+QSizeF QFontMetricsF::size(int flags, const QString &text, int tabStops, int *tabArray) const
+{
+ return boundingRect(QRectF(), flags, text, tabStops, tabArray).size();
+}
+
+/*!
+ \since 4.3
+
+ Returns a tight bounding rectangle around the characters in the
+ string specified by \a text. The bounding rectangle always covers
+ at least the set of pixels the text would cover if drawn at (0,
+ 0).
+
+ Note that the bounding rectangle may extend to the left of (0, 0),
+ e.g. for italicized fonts, and that the width of the returned
+ rectangle might be different than what the width() method returns.
+
+ If you want to know the advance width of the string (to layout
+ a set of strings next to each other), use width() instead.
+
+ Newline characters are processed as normal characters, \e not as
+ linebreaks.
+
+ \warning Calling this method is very slow on Windows.
+
+ \sa width(), height(), boundingRect()
+*/
+QRectF QFontMetricsF::tightBoundingRect(const QString &text) const
+{
+ if (text.length() == 0)
+ return QRect();
+
+ QTextEngine layout(text, d);
+ layout.ignoreBidi = true;
+ layout.itemize();
+ glyph_metrics_t gm = layout.tightBoundingBox(0, text.length());
+ return QRectF(gm.x.toReal(), gm.y.toReal(), gm.width.toReal(), gm.height.toReal());
+}
+
+/*!
+ \since 4.2
+
+ If the string \a text is wider than \a width, returns an elided
+ version of the string (i.e., a string with "..." in it).
+ Otherwise, returns the original string.
+
+ The \a mode parameter specifies whether the text is elided on the
+ left (e.g., "...tech"), in the middle (e.g., "Tr...ch"), or on
+ the right (e.g., "Trol...").
+
+ The \a width is specified in pixels, not characters.
+
+ The \a flags argument is optional and currently only supports
+ Qt::TextShowMnemonic as value.
+*/
+QString QFontMetricsF::elidedText(const QString &text, Qt::TextElideMode mode, qreal width, int flags) const
+{
+ QStackTextEngine engine(text, QFont(d));
+ return engine.elidedText(mode, QFixed::fromReal(width), flags);
+}
+
+/*!
+ Returns the distance from the base line to where an underscore
+ should be drawn.
+
+ \sa overlinePos(), strikeOutPos(), lineWidth()
+*/
+qreal QFontMetricsF::underlinePos() const
+{
+ QFontEngine *engine = d->engineForScript(QUnicodeTables::Common);
+ Q_ASSERT(engine != 0);
+ return engine->underlinePosition().toReal();
+}
+
+/*!
+ Returns the distance from the base line to where an overline
+ should be drawn.
+
+ \sa underlinePos(), strikeOutPos(), lineWidth()
+*/
+qreal QFontMetricsF::overlinePos() const
+{
+ return ascent() + 1;
+}
+
+/*!
+ Returns the distance from the base line to where the strikeout
+ line should be drawn.
+
+ \sa underlinePos(), overlinePos(), lineWidth()
+*/
+qreal QFontMetricsF::strikeOutPos() const
+{
+ return ascent() / 3.;
+}
+
+/*!
+ Returns the width of the underline and strikeout lines, adjusted
+ for the point size of the font.
+
+ \sa underlinePos(), overlinePos(), strikeOutPos()
+*/
+qreal QFontMetricsF::lineWidth() const
+{
+ QFontEngine *engine = d->engineForScript(QUnicodeTables::Common);
+ Q_ASSERT(engine != 0);
+ return engine->lineThickness().toReal();
+}
+
+/*!
+ \fn QSize QFontMetrics::size(int flags, const QString &text, int len,
+ int tabStops, int *tabArray) const
+ \compat
+
+ Use the size() function in combination with QString::left()
+ instead.
+
+ \oldcode
+ QSize size = size(flags, str, len, tabstops, tabarray);
+ \newcode
+ QSize size = size(flags, str.left(len), tabstops, tabarray);
+ \endcode
+*/
+
+/*!
+ \fn QRect QFontMetrics::boundingRect(int x, int y, int w, int h, int flags,
+ const QString& text, int len, int tabStops, int *tabArray) const
+ \compat
+
+ Use the boundingRect() function in combination with
+ QString::left() and a QRect constructor instead.
+
+ \oldcode
+ QRect rect = boundingRect(x, y, w, h , flags, text, len,
+ tabStops, tabArray);
+ \newcode
+ QRect rect = boundingRect(QRect(x, y, w, h), flags, text.left(len),
+ tabstops, tabarray);
+ \endcode
+
+*/
+
+/*!
+ \fn QRect QFontMetrics::boundingRect(const QString &text, int len) const
+ \compat
+
+ Use the boundingRect() function in combination with
+ QString::left() instead.
+
+ \oldcode
+ QRect rect = boundingRect(text, len);
+ \newcode
+ QRect rect = boundingRect(text.left(len));
+ \endcode
+*/
+
+QT_END_NAMESPACE
diff --git a/src/gui/text/qfontmetrics.h b/src/gui/text/qfontmetrics.h
new file mode 100644
index 0000000000..95f71dfa4b
--- /dev/null
+++ b/src/gui/text/qfontmetrics.h
@@ -0,0 +1,197 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QFONTMETRICS_H
+#define QFONTMETRICS_H
+
+#include <QtGui/qfont.h>
+#ifndef QT_INCLUDE_COMPAT
+#include <QtCore/qrect.h>
+#endif
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+#ifdef Q_WS_QWS
+class QFontEngine;
+#endif
+
+class QTextCodec;
+class QRect;
+
+
+class Q_GUI_EXPORT QFontMetrics
+{
+public:
+ QFontMetrics(const QFont &);
+ QFontMetrics(const QFont &, QPaintDevice *pd);
+ QFontMetrics(const QFontMetrics &);
+ ~QFontMetrics();
+
+ QFontMetrics &operator=(const QFontMetrics &);
+
+ int ascent() const;
+ int descent() const;
+ int height() const;
+ int leading() const;
+ int lineSpacing() const;
+ int minLeftBearing() const;
+ int minRightBearing() const;
+ int maxWidth() const;
+
+ int xHeight() const;
+ int averageCharWidth() const;
+
+ bool inFont(QChar) const;
+
+ int leftBearing(QChar) const;
+ int rightBearing(QChar) const;
+ int width(const QString &, int len = -1) const;
+
+ int width(QChar) const;
+ int charWidth(const QString &str, int pos) const;
+
+ QRect boundingRect(QChar) const;
+
+ QRect boundingRect(const QString &text) const;
+ QRect boundingRect(const QRect &r, int flags, const QString &text, int tabstops=0, int *tabarray=0) const;
+ inline QRect boundingRect(int x, int y, int w, int h, int flags, const QString &text,
+ int tabstops=0, int *tabarray=0) const
+ { return boundingRect(QRect(x, y, w, h), flags, text, tabstops, tabarray); }
+ QSize size(int flags, const QString& str, int tabstops=0, int *tabarray=0) const;
+
+ QRect tightBoundingRect(const QString &text) const;
+
+ QString elidedText(const QString &text, Qt::TextElideMode mode, int width, int flags = 0) const;
+
+ int underlinePos() const;
+ int overlinePos() const;
+ int strikeOutPos() const;
+ int lineWidth() const;
+
+ bool operator==(const QFontMetrics &other); // 5.0 - remove me
+ bool operator==(const QFontMetrics &other) const;
+ inline bool operator !=(const QFontMetrics &other) { return !operator==(other); } // 5.0 - remove me
+ inline bool operator !=(const QFontMetrics &other) const { return !operator==(other); }
+
+#ifdef QT3_SUPPORT
+ inline QRect boundingRect(const QString &text, int len) const
+ { return boundingRect(text.left(len)); }
+ inline QRect boundingRect(int x, int y, int w, int h, int flags, const QString& str, int len,
+ int tabstops=0, int *tabarray=0) const
+ { return boundingRect(QRect(x, y, w, h), flags, str.left(len), tabstops, tabarray); }
+ inline QSize size(int flags, const QString& str, int len, int tabstops=0, int *tabarray=0) const
+ { return size(flags, str.left(len), tabstops, tabarray); }
+#endif
+private:
+#if defined(Q_WS_MAC)
+ friend class QFontPrivate;
+#endif
+ friend class QFontMetricsF;
+ friend class QStackTextEngine;
+
+ QFontPrivate *d;
+};
+
+
+class Q_GUI_EXPORT QFontMetricsF
+{
+public:
+ QFontMetricsF(const QFont &);
+ QFontMetricsF(const QFont &, QPaintDevice *pd);
+ QFontMetricsF(const QFontMetrics &);
+ QFontMetricsF(const QFontMetricsF &);
+ ~QFontMetricsF();
+
+ QFontMetricsF &operator=(const QFontMetricsF &);
+ QFontMetricsF &operator=(const QFontMetrics &);
+
+ qreal ascent() const;
+ qreal descent() const;
+ qreal height() const;
+ qreal leading() const;
+ qreal lineSpacing() const;
+ qreal minLeftBearing() const;
+ qreal minRightBearing() const;
+ qreal maxWidth() const;
+
+ qreal xHeight() const;
+ qreal averageCharWidth() const;
+
+ bool inFont(QChar) const;
+
+ qreal leftBearing(QChar) const;
+ qreal rightBearing(QChar) const;
+ qreal width(const QString &string) const;
+
+ qreal width(QChar) const;
+
+ QRectF boundingRect(const QString &string) const;
+ QRectF boundingRect(QChar) const;
+ QRectF boundingRect(const QRectF &r, int flags, const QString& string, int tabstops=0, int *tabarray=0) const;
+ QSizeF size(int flags, const QString& str, int tabstops=0, int *tabarray=0) const;
+
+ QRectF tightBoundingRect(const QString &text) const;
+
+ QString elidedText(const QString &text, Qt::TextElideMode mode, qreal width, int flags = 0) const;
+
+ qreal underlinePos() const;
+ qreal overlinePos() const;
+ qreal strikeOutPos() const;
+ qreal lineWidth() const;
+
+ bool operator==(const QFontMetricsF &other); // 5.0 - remove me
+ bool operator==(const QFontMetricsF &other) const;
+ inline bool operator !=(const QFontMetricsF &other) { return !operator==(other); } // 5.0 - remove me
+ inline bool operator !=(const QFontMetricsF &other) const { return !operator==(other); }
+
+private:
+ QFontPrivate *d;
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QFONTMETRICS_H
diff --git a/src/gui/text/qfontsubset.cpp b/src/gui/text/qfontsubset.cpp
new file mode 100644
index 0000000000..0d1a8846ef
--- /dev/null
+++ b/src/gui/text/qfontsubset.cpp
@@ -0,0 +1,1743 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#include <qdebug.h>
+#include "qfontsubset_p.h"
+#include <qendian.h>
+#include <qpainterpath.h>
+#include "private/qpdf_p.h"
+#include "private/qfunctions_p.h"
+
+#ifdef Q_WS_X11
+#include "private/qfontengine_x11_p.h"
+#endif
+
+#ifndef QT_NO_FREETYPE
+#if defined(Q_WS_X11) || defined(Q_WS_QWS)
+# include "private/qfontengine_ft_p.h"
+#endif
+#include <ft2build.h>
+#include FT_FREETYPE_H
+#endif
+
+#ifndef QT_NO_PRINTER
+
+QT_BEGIN_NAMESPACE
+
+static const char * const agl =
+".notdef\0space\0exclam\0quotedbl\0numbersign\0dollar\0percent\0ampersand\0"
+"quotesingle\0parenleft\0parenright\0asterisk\0plus\0comma\0hyphen\0period\0"
+"slash\0zero\0one\0two\0three\0four\0five\0six\0seven\0eight\0nine\0colon\0"
+"semicolon\0less\0equal\0greater\0question\0at\0A\0B\0C\0D\0E\0F\0G\0H\0I\0J\0"
+"K\0L\0M\0N\0O\0P\0Q\0R\0S\0T\0U\0V\0W\0X\0Y\0Z\0bracketleft\0backslash\0"
+"bracketright\0asciicircum\0underscore\0grave\0a\0b\0c\0d\0e\0f\0g\0h\0i\0j\0"
+"k\0l\0m\0n\0o\0p\0q\0r\0s\0t\0u\0v\0w\0x\0y\0z\0braceleft\0bar\0braceright\0"
+"asciitilde\0space\0exclamdown\0cent\0sterling\0currency\0yen\0brokenbar\0"
+"section\0dieresis\0copyright\0ordfeminine\0guillemotleft\0logicalnot\0"
+"hyphen\0registered\0macron\0degree\0plusminus\0twosuperior\0threesuperior\0"
+"acute\0mu\0paragraph\0periodcentered\0cedilla\0onesuperior\0ordmasculine\0"
+"guillemotright\0onequarter\0onehalf\0threequarters\0questiondown\0Agrave\0"
+"Aacute\0Acircumflex\0Atilde\0Adieresis\0Aring\0AE\0Ccedilla\0Egrave\0Eacute\0"
+"Ecircumflex\0Edieresis\0Igrave\0Iacute\0Icircumflex\0Idieresis\0Eth\0Ntilde\0"
+"Ograve\0Oacute\0Ocircumflex\0Otilde\0Odieresis\0multiply\0Oslash\0Ugrave\0"
+"Uacute\0Ucircumflex\0Udieresis\0Yacute\0Thorn\0germandbls\0agrave\0aacute\0"
+"acircumflex\0atilde\0adieresis\0aring\0ae\0ccedilla\0egrave\0eacute\0"
+"ecircumflex\0edieresis\0igrave\0iacute\0icircumflex\0idieresis\0eth\0ntilde\0"
+"ograve\0oacute\0ocircumflex\0otilde\0odieresis\0divide\0oslash\0ugrave\0"
+"uacute\0ucircumflex\0udieresis\0yacute\0thorn\0ydieresis\0Amacron\0amacron\0"
+"Abreve\0abreve\0Aogonek\0aogonek\0Cacute\0cacute\0Ccircumflex\0ccircumflex\0"
+"Cdotaccent\0cdotaccent\0Ccaron\0ccaron\0Dcaron\0dcaron\0Dcroat\0dcroat\0"
+"Emacron\0emacron\0Ebreve\0ebreve\0Edotaccent\0edotaccent\0Eogonek\0eogonek\0"
+"Ecaron\0ecaron\0Gcircumflex\0gcircumflex\0Gbreve\0gbreve\0Gdotaccent\0"
+"gdotaccent\0Gcommaaccent\0gcommaaccent\0Hcircumflex\0hcircumflex\0Hbar\0"
+"hbar\0Itilde\0itilde\0Imacron\0imacron\0Ibreve\0ibreve\0Iogonek\0iogonek\0"
+"Idotaccent\0dotlessi\0IJ\0ij\0Jcircumflex\0jcircumflex\0Kcommaaccent\0"
+"kcommaaccent\0kgreenlandic\0Lacute\0lacute\0Lcommaaccent\0lcommaaccent\0"
+"Lcaron\0lcaron\0Ldot\0ldot\0Lslash\0lslash\0Nacute\0nacute\0Ncommaaccent\0"
+"ncommaaccent\0Ncaron\0ncaron\0napostrophe\0Eng\0eng\0Omacron\0omacron\0"
+"Obreve\0obreve\0Ohungarumlaut\0ohungarumlaut\0OE\0oe\0Racute\0racute\0"
+"Rcommaaccent\0rcommaaccent\0Rcaron\0rcaron\0Sacute\0sacute\0Scircumflex\0"
+"scircumflex\0Scedilla\0scedilla\0Scaron\0scaron\0Tcaron\0tcaron\0Tbar\0tbar\0"
+"Utilde\0utilde\0Umacron\0umacron\0Ubreve\0ubreve\0Uring\0uring\0"
+"Uhungarumlaut\0uhungarumlaut\0Uogonek\0uogonek\0Wcircumflex\0wcircumflex\0"
+"Ycircumflex\0ycircumflex\0Ydieresis\0Zacute\0zacute\0Zdotaccent\0zdotaccent\0"
+"Zcaron\0zcaron\0longs\0florin\0Ohorn\0ohorn\0Uhorn\0uhorn\0Gcaron\0gcaron\0"
+"Aringacute\0aringacute\0AEacute\0aeacute\0Oslashacute\0oslashacute\0"
+"Scommaaccent\0scommaaccent\0Tcommaaccent\0tcommaaccent\0afii57929\0"
+"afii64937\0circumflex\0caron\0breve\0dotaccent\0ring\0ogonek\0tilde\0"
+"hungarumlaut\0gravecomb\0acutecomb\0tildecomb\0hookabovecomb\0dotbelowcomb\0"
+"tonos\0dieresistonos\0Alphatonos\0anoteleia\0Epsilontonos\0Etatonos\0"
+"Iotatonos\0Omicrontonos\0Upsilontonos\0Omegatonos\0iotadieresistonos\0Alpha\0"
+"Beta\0Gamma\0Delta\0Epsilon\0Zeta\0Eta\0Theta\0Iota\0Kappa\0Lambda\0Mu\0Nu\0"
+"Xi\0Omicron\0Pi\0Rho\0Sigma\0Tau\0Upsilon\0Phi\0Chi\0Psi\0Omega\0"
+"Iotadieresis\0Upsilondieresis\0alphatonos\0epsilontonos\0etatonos\0"
+"iotatonos\0upsilondieresistonos\0alpha\0beta\0gamma\0delta\0epsilon\0zeta\0"
+"eta\0theta\0iota\0kappa\0lambda\0mu\0nu\0xi\0omicron\0pi\0rho\0sigma1\0"
+"sigma\0tau\0upsilon\0phi\0chi\0psi\0omega\0iotadieresis\0upsilondieresis\0"
+;
+
+static const struct { quint16 u; quint16 index; } unicode_to_aglindex[] = {
+ {0x0000, 0}, {0x0020, 8}, {0x0021, 14}, {0x0022, 21},
+ {0x0023, 30}, {0x0024, 41}, {0x0025, 48}, {0x0026, 56},
+ {0x0027, 66}, {0x0028, 78}, {0x0029, 88}, {0x002A, 99},
+ {0x002B, 108}, {0x002C, 113}, {0x002D, 119}, {0x002E, 126},
+ {0x002F, 133}, {0x0030, 139}, {0x0031, 144}, {0x0032, 148},
+ {0x0033, 152}, {0x0034, 158}, {0x0035, 163}, {0x0036, 168},
+ {0x0037, 172}, {0x0038, 178}, {0x0039, 184}, {0x003A, 189},
+ {0x003B, 195}, {0x003C, 205}, {0x003D, 210}, {0x003E, 216},
+ {0x003F, 224}, {0x0040, 233}, {0x0041, 236}, {0x0042, 238},
+ {0x0043, 240}, {0x0044, 242}, {0x0045, 244}, {0x0046, 246},
+ {0x0047, 248}, {0x0048, 250}, {0x0049, 252}, {0x004A, 254},
+ {0x004B, 256}, {0x004C, 258}, {0x004D, 260}, {0x004E, 262},
+ {0x004F, 264}, {0x0050, 266}, {0x0051, 268}, {0x0052, 270},
+ {0x0053, 272}, {0x0054, 274}, {0x0055, 276}, {0x0056, 278},
+ {0x0057, 280}, {0x0058, 282}, {0x0059, 284}, {0x005A, 286},
+ {0x005B, 288}, {0x005C, 300}, {0x005D, 310}, {0x005E, 323},
+ {0x005F, 335}, {0x0060, 346}, {0x0061, 352}, {0x0062, 354},
+ {0x0063, 356}, {0x0064, 358}, {0x0065, 360}, {0x0066, 362},
+ {0x0067, 364}, {0x0068, 366}, {0x0069, 368}, {0x006A, 370},
+ {0x006B, 372}, {0x006C, 374}, {0x006D, 376}, {0x006E, 378},
+ {0x006F, 380}, {0x0070, 382}, {0x0071, 384}, {0x0072, 386},
+ {0x0073, 388}, {0x0074, 390}, {0x0075, 392}, {0x0076, 394},
+ {0x0077, 396}, {0x0078, 398}, {0x0079, 400}, {0x007A, 402},
+ {0x007B, 404}, {0x007C, 414}, {0x007D, 418}, {0x007E, 429},
+ {0x00A0, 440}, {0x00A1, 446}, {0x00A2, 457}, {0x00A3, 462},
+ {0x00A4, 471}, {0x00A5, 480}, {0x00A6, 484}, {0x00A7, 494},
+ {0x00A8, 502}, {0x00A9, 511}, {0x00AA, 521}, {0x00AB, 533},
+ {0x00AC, 547}, {0x00AD, 558}, {0x00AE, 565}, {0x00AF, 576},
+ {0x00B0, 583}, {0x00B1, 590}, {0x00B2, 600}, {0x00B3, 612},
+ {0x00B4, 626}, {0x00B5, 632}, {0x00B6, 635}, {0x00B7, 645},
+ {0x00B8, 660}, {0x00B9, 668}, {0x00BA, 680}, {0x00BB, 693},
+ {0x00BC, 708}, {0x00BD, 719}, {0x00BE, 727}, {0x00BF, 741},
+ {0x00C0, 754}, {0x00C1, 761}, {0x00C2, 768}, {0x00C3, 780},
+ {0x00C4, 787}, {0x00C5, 797}, {0x00C6, 803}, {0x00C7, 806},
+ {0x00C8, 815}, {0x00C9, 822}, {0x00CA, 829}, {0x00CB, 841},
+ {0x00CC, 851}, {0x00CD, 858}, {0x00CE, 865}, {0x00CF, 877},
+ {0x00D0, 887}, {0x00D1, 891}, {0x00D2, 898}, {0x00D3, 905},
+ {0x00D4, 912}, {0x00D5, 924}, {0x00D6, 931}, {0x00D7, 941},
+ {0x00D8, 950}, {0x00D9, 957}, {0x00DA, 964}, {0x00DB, 971},
+ {0x00DC, 983}, {0x00DD, 993}, {0x00DE, 1000}, {0x00DF, 1006},
+ {0x00E0, 1017}, {0x00E1, 1024}, {0x00E2, 1031}, {0x00E3, 1043},
+ {0x00E4, 1050}, {0x00E5, 1060}, {0x00E6, 1066}, {0x00E7, 1069},
+ {0x00E8, 1078}, {0x00E9, 1085}, {0x00EA, 1092}, {0x00EB, 1104},
+ {0x00EC, 1114}, {0x00ED, 1121}, {0x00EE, 1128}, {0x00EF, 1140},
+ {0x00F0, 1150}, {0x00F1, 1154}, {0x00F2, 1161}, {0x00F3, 1168},
+ {0x00F4, 1175}, {0x00F5, 1187}, {0x00F6, 1194}, {0x00F7, 1204},
+ {0x00F8, 1211}, {0x00F9, 1218}, {0x00FA, 1225}, {0x00FB, 1232},
+ {0x00FC, 1244}, {0x00FD, 1254}, {0x00FE, 1261}, {0x00FF, 1267},
+ {0x0100, 1277}, {0x0101, 1285}, {0x0102, 1293}, {0x0103, 1300},
+ {0x0104, 1307}, {0x0105, 1315}, {0x0106, 1323}, {0x0107, 1330},
+ {0x0108, 1337}, {0x0109, 1349}, {0x010A, 1361}, {0x010B, 1372},
+ {0x010C, 1383}, {0x010D, 1390}, {0x010E, 1397}, {0x010F, 1404},
+ {0x0110, 1411}, {0x0111, 1418}, {0x0112, 1425}, {0x0113, 1433},
+ {0x0114, 1441}, {0x0115, 1448}, {0x0116, 1455}, {0x0117, 1466},
+ {0x0118, 1477}, {0x0119, 1485}, {0x011A, 1493}, {0x011B, 1500},
+ {0x011C, 1507}, {0x011D, 1519}, {0x011E, 1531}, {0x011F, 1538},
+ {0x0120, 1545}, {0x0121, 1556}, {0x0122, 1567}, {0x0123, 1580},
+ {0x0124, 1593}, {0x0125, 1605}, {0x0126, 1617}, {0x0127, 1622},
+ {0x0128, 1627}, {0x0129, 1634}, {0x012A, 1641}, {0x012B, 1649},
+ {0x012C, 1657}, {0x012D, 1664}, {0x012E, 1671}, {0x012F, 1679},
+ {0x0130, 1687}, {0x0131, 1698}, {0x0132, 1707}, {0x0133, 1710},
+ {0x0134, 1713}, {0x0135, 1725}, {0x0136, 1737}, {0x0137, 1750},
+ {0x0138, 1763}, {0x0139, 1776}, {0x013A, 1783}, {0x013B, 1790},
+ {0x013C, 1803}, {0x013D, 1816}, {0x013E, 1823}, {0x013F, 1830},
+ {0x0140, 1835}, {0x0141, 1840}, {0x0142, 1847}, {0x0143, 1854},
+ {0x0144, 1861}, {0x0145, 1868}, {0x0146, 1881}, {0x0147, 1894},
+ {0x0148, 1901}, {0x0149, 1908}, {0x014A, 1920}, {0x014B, 1924},
+ {0x014C, 1928}, {0x014D, 1936}, {0x014E, 1944}, {0x014F, 1951},
+ {0x0150, 1958}, {0x0151, 1972}, {0x0152, 1986}, {0x0153, 1989},
+ {0x0154, 1992}, {0x0155, 1999}, {0x0156, 2006}, {0x0157, 2019},
+ {0x0158, 2032}, {0x0159, 2039}, {0x015A, 2046}, {0x015B, 2053},
+ {0x015C, 2060}, {0x015D, 2072}, {0x015E, 2084}, {0x015F, 2093},
+ {0x0160, 2102}, {0x0161, 2109}, {0x0164, 2116}, {0x0165, 2123},
+ {0x0166, 2130}, {0x0167, 2135}, {0x0168, 2140}, {0x0169, 2147},
+ {0x016A, 2154}, {0x016B, 2162}, {0x016C, 2170}, {0x016D, 2177},
+ {0x016E, 2184}, {0x016F, 2190}, {0x0170, 2196}, {0x0171, 2210},
+ {0x0172, 2224}, {0x0173, 2232}, {0x0174, 2240}, {0x0175, 2252},
+ {0x0176, 2264}, {0x0177, 2276}, {0x0178, 2288}, {0x0179, 2298},
+ {0x017A, 2305}, {0x017B, 2312}, {0x017C, 2323}, {0x017D, 2334},
+ {0x017E, 2341}, {0x017F, 2348}, {0x0192, 2354}, {0x01A0, 2361},
+ {0x01A1, 2367}, {0x01AF, 2373}, {0x01B0, 2379}, {0x01E6, 2385},
+ {0x01E7, 2392}, {0x01FA, 2399}, {0x01FB, 2410}, {0x01FC, 2421},
+ {0x01FD, 2429}, {0x01FE, 2437}, {0x01FF, 2449}, {0x0218, 2461},
+ {0x0219, 2474}, {0x021A, 2487}, {0x021B, 2500}, {0x02BC, 2513},
+ {0x02BD, 2523}, {0x02C6, 2533}, {0x02C7, 2544}, {0x02D8, 2550},
+ {0x02D9, 2556}, {0x02DA, 2566}, {0x02DB, 2571}, {0x02DC, 2578},
+ {0x02DD, 2584}, {0x0300, 2597}, {0x0301, 2607}, {0x0303, 2617},
+ {0x0309, 2627}, {0x0323, 2641}, {0x0384, 2654}, {0x0385, 2660},
+ {0x0386, 2674}, {0x0387, 2685}, {0x0388, 2695}, {0x0389, 2708},
+ {0x038A, 2717}, {0x038C, 2727}, {0x038E, 2740}, {0x038F, 2753},
+ {0x0390, 2764}, {0x0391, 2782}, {0x0392, 2788}, {0x0393, 2793},
+ {0x0394, 2799}, {0x0395, 2805}, {0x0396, 2813}, {0x0397, 2818},
+ {0x0398, 2822}, {0x0399, 2828}, {0x039A, 2833}, {0x039B, 2839},
+ {0x039C, 2846}, {0x039D, 2849}, {0x039E, 2852}, {0x039F, 2855},
+ {0x03A0, 2863}, {0x03A1, 2866}, {0x03A3, 2870}, {0x03A4, 2876},
+ {0x03A5, 2880}, {0x03A6, 2888}, {0x03A7, 2892}, {0x03A8, 2896},
+ {0x03A9, 2900}, {0x03AA, 2906}, {0x03AB, 2919}, {0x03AC, 2935},
+ {0x03AD, 2946}, {0x03AE, 2959}, {0x03AF, 2968}, {0x03B0, 2978},
+ {0x03B1, 2999}, {0x03B2, 3005}, {0x03B3, 3010}, {0x03B4, 3016},
+ {0x03B5, 3022}, {0x03B6, 3030}, {0x03B7, 3035}, {0x03B8, 3039},
+ {0x03B9, 3045}, {0x03BA, 3050}, {0x03BB, 3056}, {0x03BC, 3063},
+ {0x03BD, 3066}, {0x03BE, 3069}, {0x03BF, 3072}, {0x03C0, 3080},
+ {0x03C1, 3083}, {0x03C2, 3087}, {0x03C3, 3094}, {0x03C4, 3100},
+ {0x03C5, 3104}, {0x03C6, 3112}, {0x03C7, 3116}, {0x03C8, 3120},
+ {0x03C9, 3124}, {0x03CA, 3130}, {0x03CB, 3143}, {0x03CC, 3159},
+ {0x03CD, 3172}, {0x03CE, 3185}, {0x03D1, 3196}, {0x03D2, 3203},
+ {0x03D5, 3212}, {0x03D6, 3217}, {0xFFFF, 3224}
+};
+
+// This map is used for symbol fonts to get the correct glyph names for the latin range
+static const unsigned short symbol_map[0x100] = {
+ 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007,
+ 0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f,
+ 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017,
+ 0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, 0x001f,
+ 0x0020, 0x0021, 0x2200, 0x0023, 0x2203, 0x0025, 0x0026, 0x220b,
+ 0x0028, 0x0029, 0x2217, 0x002b, 0x002c, 0x2212, 0x002e, 0x002f,
+ 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037,
+ 0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f,
+
+ 0x2245, 0x0391, 0x0392, 0x03a7, 0x0394, 0x0395, 0x03a6, 0x0393,
+ 0x0397, 0x0399, 0x03d1, 0x039a, 0x039b, 0x039c, 0x039d, 0x039f,
+ 0x03a0, 0x0398, 0x03a1, 0x03a3, 0x03a4, 0x03a5, 0x03c2, 0x03a9,
+ 0x039e, 0x03a8, 0x0396, 0x005b, 0x2234, 0x005d, 0x22a5, 0x005f,
+ 0xf8e5, 0x03b1, 0x03b2, 0x03c7, 0x03b4, 0x03b5, 0x03c6, 0x03b3,
+ 0x03b7, 0x03b9, 0x03d5, 0x03ba, 0x03bb, 0x03bc, 0x03bd, 0x03bf,
+ 0x03c0, 0x03b8, 0x03c1, 0x03c3, 0x03c4, 0x03c5, 0x03d6, 0x03c9,
+ 0x03be, 0x03c8, 0x03b6, 0x007b, 0x007c, 0x007d, 0x223c, 0x007f,
+
+ 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087,
+ 0x0088, 0x0089, 0x008a, 0x008b, 0x008c, 0x008d, 0x008e, 0x008f,
+ 0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097,
+ 0x0098, 0x0099, 0x009a, 0x009b, 0x009c, 0x009d, 0x009e, 0x009f,
+ 0x20ac, 0x03d2, 0x2023, 0x2264, 0x2044, 0x221e, 0x0192, 0x2263,
+ 0x2666, 0x2665, 0x2660, 0x2194, 0x2190, 0x2191, 0x2192, 0x2193,
+ 0x00b0, 0x00b1, 0x2033, 0x2265, 0x00d7, 0x221d, 0x2202, 0x2022,
+ 0x00f7, 0x2260, 0x2261, 0x2248, 0x2026, 0xf8e6, 0xf8e7, 0x21b5,
+
+ 0x2135, 0x2111, 0x211c, 0x2118, 0x2297, 0x2295, 0x2205, 0x2229,
+ 0x222a, 0x2283, 0x2287, 0x2284, 0x2282, 0x2286, 0x2208, 0x2209,
+ 0x2220, 0x2207, 0xf6da, 0xf6d9, 0xf6db, 0x220f, 0x221a, 0x22c5,
+ 0x00ac, 0x2227, 0x2228, 0x21d4, 0x21d0, 0x21d1, 0x21d2, 0x21d3,
+ 0x25ca, 0x2329, 0xf8e8, 0xf8e9, 0xf8ea, 0x2211, 0xf8eb, 0xf8ec,
+ 0xf8ed, 0xf8ee, 0xf8ef, 0xf8f0, 0xf8f1, 0xf8f2, 0xf8f3, 0xf8f4,
+ 0x0000, 0x232a, 0x222b, 0x2320, 0xf8f5, 0x2321, 0xf8f6, 0xf8f7,
+ 0xf8f8, 0xf8f9, 0xf8fa, 0xf8fb, 0xf8fc, 0xf8fd, 0xf8fe, 0x0000,
+};
+
+// ---------------------------- PS/PDF helper methods -----------------------------------
+
+QByteArray QFontSubset::glyphName(unsigned short unicode, bool symbol)
+{
+ if (symbol && unicode < 0x100)
+ // map from latin1 to symbol
+ unicode = symbol_map[unicode];
+
+ int l = 0;
+ while(unicode_to_aglindex[l].u < unicode)
+ l++;
+ if (unicode_to_aglindex[l].u == unicode)
+ return agl + unicode_to_aglindex[l].index;
+
+ char buffer[8];
+ buffer[0] = 'u';
+ buffer[1] = 'n';
+ buffer[2] = 'i';
+ QPdf::toHex(unicode, buffer+3);
+ return buffer;
+}
+
+#ifndef QT_NO_FREETYPE
+static FT_Face ft_face(const QFontEngine *engine)
+{
+#ifdef Q_WS_X11
+#ifndef QT_NO_FONTCONFIG
+ if (engine->type() == QFontEngine::Freetype) {
+ const QFontEngineFT *ft = static_cast<const QFontEngineFT *>(engine);
+ return ft->non_locked_face();
+ } else
+#endif
+ if (engine->type() == QFontEngine::XLFD) {
+ const QFontEngineXLFD *xlfd = static_cast<const QFontEngineXLFD *>(engine);
+ return xlfd->non_locked_face();
+ }
+#endif
+#ifdef Q_WS_QWS
+ if (engine->type() == QFontEngine::Freetype) {
+ const QFontEngineFT *ft = static_cast<const QFontEngineFT *>(engine);
+ return ft->non_locked_face();
+ }
+#endif
+ return 0;
+}
+#endif
+
+QByteArray QFontSubset::glyphName(unsigned int glyph, const QVector<int> reverseMap) const
+{
+ uint glyphIndex = glyph_indices[glyph];
+
+ if (glyphIndex == 0)
+ return "/.notdef";
+
+ QByteArray ba;
+ QPdf::ByteStream s(&ba);
+#ifndef QT_NO_FREETYPE
+ FT_Face face = ft_face(fontEngine);
+
+ char name[32];
+ name[0] = 0;
+ if (face && FT_HAS_GLYPH_NAMES(face)) {
+#if defined(Q_WS_X11)
+ if (fontEngine->type() == QFontEngine::XLFD)
+ glyphIndex = static_cast<QFontEngineXLFD *>(fontEngine)->glyphIndexToFreetypeGlyphIndex(glyphIndex);
+#endif
+ FT_Get_Glyph_Name(face, glyphIndex, &name, 32);
+ if (name[0] == '.') // fix broken PS fonts returning .notdef for many glyphs
+ name[0] = 0;
+ }
+ if (name[0]) {
+ s << "/" << name;
+ } else
+#endif
+#if defined(Q_WS_X11)
+ if (fontEngine->type() == QFontEngine::XLFD) {
+ uint uc = static_cast<QFontEngineXLFD *>(fontEngine)->toUnicode(glyphIndex);
+ s << "/" << glyphName(uc, false /* ### */);
+ } else
+#endif
+ if (reverseMap[glyphIndex] && reverseMap[glyphIndex] < 0x10000) {
+ s << "/" << glyphName(reverseMap[glyphIndex], false);
+ } else {
+ s << "/gl" << (int)glyphIndex;
+ }
+ return ba;
+}
+
+
+QByteArray QFontSubset::widthArray() const
+{
+ Q_ASSERT(!widths.isEmpty());
+
+ QFontEngine::Properties properties = fontEngine->properties();
+
+ QByteArray width;
+ QPdf::ByteStream s(&width);
+ QFixed scale = QFixed(1000)/emSquare;
+
+ QFixed defWidth = widths[0];
+ //qDebug("defWidth=%d, scale=%f", defWidth.toInt(), scale.toReal());
+ for (int i = 0; i < nGlyphs(); ++i) {
+ if (defWidth != widths[i])
+ defWidth = 0;
+ }
+ if (defWidth > 0) {
+ s << "/DW " << (defWidth*scale).toInt();
+ } else {
+ s << "/W [";
+ for (int g = 0; g < nGlyphs();) {
+ QFixed w = widths[g];
+ int start = g;
+ int startLinear = 0;
+ ++g;
+ while (g < nGlyphs()) {
+ QFixed nw = widths[g];
+ if (nw == w) {
+ if (!startLinear)
+ startLinear = g - 1;
+ } else {
+ if (startLinear > 0 && g - startLinear >= 10)
+ break;
+ startLinear = 0;
+ }
+ w = nw;
+ ++g;
+ }
+ // qDebug("start=%x startLinear=%x g-1=%x",start,startLinear,g-1);
+ if (g - startLinear < 10)
+ startLinear = 0;
+ int endnonlinear = startLinear ? startLinear : g;
+ // qDebug(" startLinear=%x endnonlinear=%x", startLinear,endnonlinear);
+ if (endnonlinear > start) {
+ s << start << "[";
+ for (int i = start; i < endnonlinear; ++i)
+ s << (widths[i]*scale).toInt();
+ s << "]\n";
+ }
+ if (startLinear)
+ s << startLinear << g - 1 << (widths[startLinear]*scale).toInt() << "\n";
+ }
+ s << "]\n";
+ }
+ return width;
+}
+
+static void checkRanges(QPdf::ByteStream &ts, QByteArray &ranges, int &nranges)
+{
+ if (++nranges > 100) {
+ ts << nranges << "beginbfrange\n"
+ << ranges << "endbfrange\n";
+ ranges = QByteArray();
+ nranges = 0;
+ }
+}
+
+QVector<int> QFontSubset::getReverseMap() const
+{
+ QVector<int> reverseMap;
+ reverseMap.resize(0x10000);
+ for (uint i = 0; i < 0x10000; ++i)
+ reverseMap[i] = 0;
+ QGlyphLayoutArray<10> glyphs;
+ for (uint uc = 0; uc < 0x10000; ++uc) {
+ QChar ch(uc);
+ int nglyphs = 10;
+ fontEngine->stringToCMap(&ch, 1, &glyphs, &nglyphs, QTextEngine::GlyphIndicesOnly);
+ int idx = glyph_indices.indexOf(glyphs.glyphs[0]);
+ if (idx >= 0 && !reverseMap.at(idx))
+ reverseMap[idx] = uc;
+ }
+ return reverseMap;
+}
+
+QByteArray QFontSubset::createToUnicodeMap() const
+{
+ QVector<int> reverseMap = getReverseMap();
+
+ QByteArray touc;
+ QPdf::ByteStream ts(&touc);
+ ts << "/CIDInit /ProcSet findresource begin\n"
+ "12 dict begin\n"
+ "begincmap\n"
+ "/CIDSystemInfo << /Registry (Adobe) /Ordering (UCS) /Supplement 0 >> def\n"
+ "/CMapName /Adobe-Identity-UCS def\n"
+ "/CMapType 2 def\n"
+ "1 begincodespacerange\n"
+ "<0000> <FFFF>\n"
+ "endcodespacerange\n";
+
+ int nranges = 1;
+ QByteArray ranges = "<0000> <0000> <0000>\n";
+ QPdf::ByteStream s(&ranges);
+
+ char buf[5];
+ for (int g = 1; g < nGlyphs(); ) {
+ int uc0 = reverseMap.at(g);
+ if (!uc0) {
+ ++g;
+ continue;
+ }
+ int start = g;
+ int startLinear = 0;
+ ++g;
+ while (g < nGlyphs()) {
+ int uc = reverseMap[g];
+ // cmaps can't have the high byte changing within one range, so we need to break on that as well
+ if (!uc || (g>>8) != (start >> 8))
+ break;
+ if (uc == uc0 + 1) {
+ if (!startLinear)
+ startLinear = g - 1;
+ } else {
+ if (startLinear > 0 && g - startLinear >= 10)
+ break;
+ startLinear = 0;
+ }
+ uc0 = uc;
+ ++g;
+ }
+ // qDebug("start=%x startLinear=%x g-1=%x",start,startLinear,g-1);
+ if (g - startLinear < 10)
+ startLinear = 0;
+ int endnonlinear = startLinear ? startLinear : g;
+ // qDebug(" startLinear=%x endnonlinear=%x", startLinear,endnonlinear);
+ if (endnonlinear > start) {
+ s << "<" << QPdf::toHex((ushort)start, buf) << "> <";
+ s << QPdf::toHex((ushort)(endnonlinear - 1), buf) << "> ";
+ if (endnonlinear == start + 1) {
+ s << "<" << QPdf::toHex((ushort)reverseMap[start], buf) << ">\n";
+ } else {
+ s << "[";
+ for (int i = start; i < endnonlinear; ++i) {
+ s << "<" << QPdf::toHex((ushort)reverseMap[i], buf) << "> ";
+ }
+ s << "]\n";
+ }
+ checkRanges(ts, ranges, nranges);
+ }
+ if (startLinear) {
+ while (startLinear < g) {
+ int len = g - startLinear;
+ int uc_start = reverseMap[startLinear];
+ int uc_end = uc_start + len - 1;
+ if ((uc_end >> 8) != (uc_start >> 8))
+ len = 256 - (uc_start & 0xff);
+ s << "<" << QPdf::toHex((ushort)startLinear, buf) << "> <";
+ s << QPdf::toHex((ushort)(startLinear + len - 1), buf) << "> ";
+ s << "<" << QPdf::toHex((ushort)reverseMap[startLinear], buf) << ">\n";
+ checkRanges(ts, ranges, nranges);
+ startLinear += len;
+ }
+ }
+ }
+ if (nranges) {
+ ts << nranges << "beginbfrange\n"
+ << ranges << "endbfrange\n";
+ }
+ ts << "endcmap\n"
+ "CMapName currentdict /CMap defineresource pop\n"
+ "end\n"
+ "end\n";
+
+ return touc;
+}
+
+int QFontSubset::addGlyph(int index)
+{
+ int idx = glyph_indices.indexOf(index);
+ if (idx < 0) {
+ idx = glyph_indices.size();
+ glyph_indices.append(index);
+ }
+ return idx;
+}
+
+
+// ------------------------------ Truetype generation ----------------------------------------------
+
+typedef qint16 F2DOT14;
+typedef quint32 Tag;
+typedef quint16 GlyphID;
+typedef quint16 Offset;
+
+
+class QTtfStream {
+public:
+ QTtfStream(QByteArray &ba) : data((uchar *)ba.data()) { start = data; }
+ QTtfStream &operator <<(quint8 v) { *data = v; ++data; return *this; }
+ QTtfStream &operator <<(quint16 v) { qToBigEndian(v, data); data += sizeof(v); return *this; }
+ QTtfStream &operator <<(quint32 v) { qToBigEndian(v, data); data += sizeof(v); return *this; }
+ QTtfStream &operator <<(qint8 v) { *data = quint8(v); ++data; return *this; }
+ QTtfStream &operator <<(qint16 v) { qToBigEndian(v, data); data += sizeof(v); return *this; }
+ QTtfStream &operator <<(qint32 v) { qToBigEndian(v, data); data += sizeof(v); return *this; }
+ QTtfStream &operator <<(qint64 v) { qToBigEndian(v, data); data += sizeof(v); return *this; }
+
+ int offset() const { return data - start; }
+ void setOffset(int o) { data = start + o; }
+ void align4() { while (offset() & 3) { *data = '\0'; ++data; } }
+private:
+ uchar *data;
+ uchar *start;
+};
+
+struct QTtfTable {
+ Tag tag;
+ QByteArray data;
+};
+Q_DECLARE_TYPEINFO(QTtfTable, Q_MOVABLE_TYPE);
+
+
+struct qttf_head_table {
+ qint32 font_revision;
+ quint16 flags;
+ qint64 created;
+ qint64 modified;
+ qint16 xMin;
+ qint16 yMin;
+ qint16 xMax;
+ qint16 yMax;
+ quint16 macStyle;
+ qint16 indexToLocFormat;
+};
+
+
+struct qttf_hhea_table {
+ qint16 ascender;
+ qint16 descender;
+ qint16 lineGap;
+ quint16 maxAdvanceWidth;
+ qint16 minLeftSideBearing;
+ qint16 minRightSideBearing;
+ qint16 xMaxExtent;
+ quint16 numberOfHMetrics;
+};
+
+
+struct qttf_maxp_table {
+ quint16 numGlyphs;
+ quint16 maxPoints;
+ quint16 maxContours;
+ quint16 maxCompositePoints;
+ quint16 maxCompositeContours;
+ quint16 maxComponentElements;
+ quint16 maxComponentDepth;
+};
+
+struct qttf_name_table {
+ QString copyright;
+ QString family;
+ QString subfamily;
+ QString postscript_name;
+};
+
+
+static QTtfTable generateHead(const qttf_head_table &head);
+static QTtfTable generateHhea(const qttf_hhea_table &hhea);
+static QTtfTable generateMaxp(const qttf_maxp_table &maxp);
+static QTtfTable generateName(const qttf_name_table &name);
+
+struct qttf_font_tables
+{
+ qttf_head_table head;
+ qttf_hhea_table hhea;
+ qttf_maxp_table maxp;
+};
+
+
+struct QTtfGlyph {
+ quint16 index;
+ qint16 xMin;
+ qint16 xMax;
+ qint16 yMin;
+ qint16 yMax;
+ quint16 advanceWidth;
+ qint16 lsb;
+ quint16 numContours;
+ quint16 numPoints;
+ QByteArray data;
+};
+Q_DECLARE_TYPEINFO(QTtfGlyph, Q_MOVABLE_TYPE);
+
+static QTtfGlyph generateGlyph(int index, const QPainterPath &path, qreal advance, qreal lsb, qreal ppem);
+// generates glyf, loca and hmtx
+static QList<QTtfTable> generateGlyphTables(qttf_font_tables &tables, const QList<QTtfGlyph> &_glyphs);
+
+static QByteArray bindFont(const QList<QTtfTable>& _tables);
+
+
+static quint32 checksum(const QByteArray &table)
+{
+ quint32 sum = 0;
+ int offset = 0;
+ const uchar *d = (uchar *)table.constData();
+ while (offset <= table.size()-3) {
+ sum += qFromBigEndian<quint32>(d + offset);
+ offset += 4;
+ }
+ int shift = 24;
+ quint32 x = 0;
+ while (offset < table.size()) {
+ x |= ((quint32)d[offset]) << shift;
+ ++offset;
+ shift -= 8;
+ }
+ sum += x;
+
+ return sum;
+}
+
+static QTtfTable generateHead(const qttf_head_table &head)
+{
+ const int head_size = 54;
+ QTtfTable t;
+ t.tag = MAKE_TAG('h', 'e', 'a', 'd');
+ t.data.resize(head_size);
+
+ QTtfStream s(t.data);
+
+// qint32 Table version number 0x00010000 for version 1.0.
+// qint32 fontRevision Set by font manufacturer.
+ s << qint32(0x00010000)
+ << head.font_revision
+// quint32 checkSumAdjustment To compute: set it to 0, sum the entire font as quint32, then store 0xB1B0AFBA - sum.
+ << quint32(0)
+// quint32 magicNumber Set to 0x5F0F3CF5.
+ << quint32(0x5F0F3CF5)
+// quint16 flags Bit 0: Baseline for font at y=0;
+// Bit 1: Left sidebearing point at x=0;
+// Bit 2: Instructions may depend on point size;
+// Bit 3: Force ppem to integer values for all internal scaler math; may use fractional ppem sizes if this bit is clear;
+// Bit 4: Instructions may alter advance width (the advance widths might not scale linearly);
+// Bits 5-10: These should be set according to Apple's specification . However, they are not implemented in OpenType.
+// Bit 11: Font data is 'lossless,' as a result of having been compressed and decompressed with the Agfa MicroType Express engine.
+// Bit 12: Font converted (produce compatible metrics)
+// Bit 13: Font optimised for ClearType
+// Bit 14: Reserved, set to 0
+// Bit 15: Reserved, set to 0
+ << quint16(0)
+
+// quint16 unitsPerEm Valid range is from 16 to 16384. This value should be a power of 2 for fonts that have TrueType outlines.
+ << quint16(2048)
+// qint64 created Number of seconds since 12:00 midnight, January 1, 1904. 64-bit integer
+ << head.created
+// qint64 modified Number of seconds since 12:00 midnight, January 1, 1904. 64-bit integer
+ << head.modified
+// qint16 xMin For all glyph bounding boxes.
+// qint16 yMin For all glyph bounding boxes.
+// qint16 xMax For all glyph bounding boxes.
+// qint16 yMax For all glyph bounding boxes.
+ << head.xMin
+ << head.yMin
+ << head.xMax
+ << head.yMax
+// quint16 macStyle Bit 0: Bold (if set to 1);
+// Bit 1: Italic (if set to 1)
+// Bit 2: Underline (if set to 1)
+// Bit 3: Outline (if set to 1)
+// Bit 4: Shadow (if set to 1)
+// Bit 5: Condensed (if set to 1)
+// Bit 6: Extended (if set to 1)
+// Bits 7-15: Reserved (set to 0).
+ << head.macStyle
+// quint16 lowestRecPPEM Smallest readable size in pixels.
+ << quint16(6) // just a wild guess
+// qint16 fontDirectionHint 0: Fully mixed directional glyphs;
+ << qint16(0)
+// 1: Only strongly left to right;
+// 2: Like 1 but also contains neutrals;
+// -1: Only strongly right to left;
+// -2: Like -1 but also contains neutrals. 1
+// qint16 indexToLocFormat 0 for short offsets, 1 for long.
+ << head.indexToLocFormat
+// qint16 glyphDataFormat 0 for current format.
+ << qint16(0);
+
+ Q_ASSERT(s.offset() == head_size);
+ return t;
+}
+
+
+static QTtfTable generateHhea(const qttf_hhea_table &hhea)
+{
+ const int hhea_size = 36;
+ QTtfTable t;
+ t.tag = MAKE_TAG('h', 'h', 'e', 'a');
+ t.data.resize(hhea_size);
+
+ QTtfStream s(t.data);
+// qint32 Table version number 0x00010000 for version 1.0.
+ s << qint32(0x00010000)
+// qint16 Ascender Typographic ascent. (Distance from baseline of highest ascender)
+ << hhea.ascender
+// qint16 Descender Typographic descent. (Distance from baseline of lowest descender)
+ << hhea.descender
+// qint16 LineGap Typographic line gap.
+// Negative LineGap values are treated as zero
+// in Windows 3.1, System 6, and
+// System 7.
+ << hhea.lineGap
+// quint16 advanceWidthMax Maximum advance width value in 'hmtx' table.
+ << hhea.maxAdvanceWidth
+// qint16 minLeftSideBearing Minimum left sidebearing value in 'hmtx' table.
+ << hhea.minLeftSideBearing
+// qint16 minRightSideBearing Minimum right sidebearing value; calculated as Min(aw - lsb - (xMax - xMin)).
+ << hhea.minRightSideBearing
+// qint16 xMaxExtent Max(lsb + (xMax - xMin)).
+ << hhea.xMaxExtent
+// qint16 caretSlopeRise Used to calculate the slope of the cursor (rise/run); 1 for vertical.
+ << qint16(1)
+// qint16 caretSlopeRun 0 for vertical.
+ << qint16(0)
+// qint16 caretOffset The amount by which a slanted highlight on a glyph needs to be shifted to produce the best appearance. Set to 0 for non-slanted fonts
+ << qint16(0)
+// qint16 (reserved) set to 0
+ << qint16(0)
+// qint16 (reserved) set to 0
+ << qint16(0)
+// qint16 (reserved) set to 0
+ << qint16(0)
+// qint16 (reserved) set to 0
+ << qint16(0)
+// qint16 metricDataFormat 0 for current format.
+ << qint16(0)
+// quint16 numberOfHMetrics Number of hMetric entries in 'hmtx' table
+ << hhea.numberOfHMetrics;
+
+ Q_ASSERT(s.offset() == hhea_size);
+ return t;
+}
+
+
+static QTtfTable generateMaxp(const qttf_maxp_table &maxp)
+{
+ const int maxp_size = 32;
+ QTtfTable t;
+ t.tag = MAKE_TAG('m', 'a', 'x', 'p');
+ t.data.resize(maxp_size);
+
+ QTtfStream s(t.data);
+
+// qint32 Table version number 0x00010000 for version 1.0.
+ s << qint32(0x00010000)
+// quint16 numGlyphs The number of glyphs in the font.
+ << maxp.numGlyphs
+// quint16 maxPoints Maximum points in a non-composite glyph.
+ << maxp.maxPoints
+// quint16 maxContours Maximum contours in a non-composite glyph.
+ << maxp.maxContours
+// quint16 maxCompositePoints Maximum points in a composite glyph.
+ << maxp.maxCompositePoints
+// quint16 maxCompositeContours Maximum contours in a composite glyph.
+ << maxp.maxCompositeContours
+// quint16 maxZones 1 if instructions do not use the twilight zone (Z0), or 2 if instructions do use Z0; should be set to 2 in most cases.
+ << quint16(1) // we do not embed instructions
+// quint16 maxTwilightPoints Maximum points used in Z0.
+ << quint16(0)
+// quint16 maxStorage Number of Storage Area locations.
+ << quint16(0)
+// quint16 maxFunctionDefs Number of FDEFs.
+ << quint16(0)
+// quint16 maxInstructionDefs Number of IDEFs.
+ << quint16(0)
+// quint16 maxStackElements Maximum stack depth2.
+ << quint16(0)
+// quint16 maxSizeOfInstructions Maximum byte count for glyph instructions.
+ << quint16(0)
+// quint16 maxComponentElements Maximum number of components referenced at "top level" for any composite glyph.
+ << maxp.maxComponentElements
+// quint16 maxComponentDepth Maximum levels of recursion; 1 for simple components.
+ << maxp.maxComponentDepth;
+
+ Q_ASSERT(s.offset() == maxp_size);
+ return t;
+}
+
+struct QTtfNameRecord {
+ quint16 nameId;
+ QString value;
+};
+
+static QTtfTable generateName(const QList<QTtfNameRecord> &name);
+
+static QTtfTable generateName(const qttf_name_table &name)
+{
+ QList<QTtfNameRecord> list;
+ QTtfNameRecord rec;
+ rec.nameId = 0;
+ rec.value = name.copyright;
+ list.append(rec);
+ rec.nameId = 1;
+ rec.value = name.family;
+ list.append(rec);
+ rec.nameId = 2;
+ rec.value = name.subfamily;
+ list.append(rec);
+ rec.nameId = 4;
+ rec.value = name.family;
+ if (name.subfamily != QLatin1String("Regular"))
+ rec.value += QLatin1Char(' ') + name.subfamily;
+ list.append(rec);
+ rec.nameId = 6;
+ rec.value = name.postscript_name;
+ list.append(rec);
+
+ return generateName(list);
+}
+
+// ####### should probably generate Macintosh/Roman name entries as well
+static QTtfTable generateName(const QList<QTtfNameRecord> &name)
+{
+ const int char_size = 2;
+
+ QTtfTable t;
+ t.tag = MAKE_TAG('n', 'a', 'm', 'e');
+
+ const int name_size = 6 + 12*name.size();
+ int string_size = 0;
+ for (int i = 0; i < name.size(); ++i) {
+ string_size += name.at(i).value.length()*char_size;
+ }
+ t.data.resize(name_size + string_size);
+
+ QTtfStream s(t.data);
+// quint16 format Format selector (=0).
+ s << quint16(0)
+// quint16 count Number of name records.
+ << quint16(name.size())
+// quint16 stringOffset Offset to start of string storage (from start of table).
+ << quint16(name_size);
+// NameRecord nameRecord[count] The name records where count is the number of records.
+// (Variable)
+
+ int off = 0;
+ for (int i = 0; i < name.size(); ++i) {
+ int len = name.at(i).value.length()*char_size;
+// quint16 platformID Platform ID.
+// quint16 encodingID Platform-specific encoding ID.
+// quint16 languageID Language ID.
+ s << quint16(3)
+ << quint16(1)
+ << quint16(0x0409) // en_US
+// quint16 nameId Name ID.
+ << name.at(i).nameId
+// quint16 length String length (in bytes).
+ << quint16(len)
+// quint16 offset String offset from start of storage area (in bytes).
+ << quint16(off);
+ off += len;
+ }
+ for (int i = 0; i < name.size(); ++i) {
+ const QString &n = name.at(i).value;
+ const ushort *uc = n.utf16();
+ for (int i = 0; i < n.length(); ++i) {
+ s << quint16(*uc);
+ ++uc;
+ }
+ }
+ return t;
+}
+
+
+enum Flags {
+ OffCurve = 0,
+ OnCurve = (1 << 0),
+ XShortVector = (1 << 1),
+ YShortVector = (1 << 2),
+ Repeat = (1 << 3),
+ XSame = (1 << 4),
+ XShortPositive = (1 << 4),
+ YSame = (1 << 5),
+ YShortPositive = (1 << 5)
+};
+struct TTF_POINT {
+ qint16 x;
+ qint16 y;
+ quint8 flags;
+};
+Q_DECLARE_TYPEINFO(TTF_POINT, Q_PRIMITIVE_TYPE);
+
+static void convertPath(const QPainterPath &path, QList<TTF_POINT> *points, QList<int> *endPoints, qreal ppem)
+{
+ int numElements = path.elementCount();
+ for (int i = 0; i < numElements - 1; ++i) {
+ const QPainterPath::Element &e = path.elementAt(i);
+ TTF_POINT p;
+ p.x = qRound(e.x * 2048. / ppem);
+ p.y = qRound(-e.y * 2048. / ppem);
+ p.flags = 0;
+
+ switch(e.type) {
+ case QPainterPath::MoveToElement:
+ if (i != 0) {
+ // see if start and end points of the last contour agree
+ int start = endPoints->size() ? endPoints->at(endPoints->size()-1) - 1 : 0;
+ int end = points->size() - 1;
+ if (points->at(end).x == points->at(start).x
+ && points->at(end).y == points->at(start).y)
+ points->takeLast();
+ endPoints->append(points->size() - 1);
+ }
+ // fall through
+ case QPainterPath::LineToElement:
+ p.flags = OnCurve;
+ break;
+ case QPainterPath::CurveToElement: {
+ // cubic bezier curve, we need to reduce to a list of quadratic curves
+ TTF_POINT list[3*16 + 4]; // we need max 16 subdivisions
+ list[3] = points->at(points->size() - 1);
+ list[2] = p;
+ const QPainterPath::Element &e2 = path.elementAt(++i);
+ list[1].x = qRound(e2.x * 2048. / ppem);
+ list[1].y = qRound(-e2.y * 2048. / ppem);
+ const QPainterPath::Element &e3 = path.elementAt(++i);
+ list[0].x = qRound(e3.x * 2048. / ppem);
+ list[0].y = qRound(-e3.y * 2048. / ppem);
+
+ TTF_POINT *base = list;
+
+ bool try_reduce = points->size() > 1
+ && points->at(points->size() - 1).flags == OnCurve
+ && points->at(points->size() - 2).flags == OffCurve;
+// qDebug("generating beziers:");
+ while (base >= list) {
+ const int split_limit = 3;
+// {
+// qDebug("iteration:");
+// TTF_POINT *x = list;
+// while (x <= base + 3) {
+// qDebug() << " " << QPoint(x->x, x->y);
+// ++x;
+// }
+// }
+ Q_ASSERT(base - list < 3*16 + 1);
+ // first see if we can easily reduce the cubic to a quadratic bezier curve
+ int i1_x = base[1].x + ((base[1].x - base[0].x) >> 1);
+ int i1_y = base[1].y + ((base[1].y - base[0].y) >> 1);
+ int i2_x = base[2].x + ((base[2].x - base[3].x) >> 1);
+ int i2_y = base[2].y + ((base[2].y - base[3].y) >> 1);
+// qDebug() << "checking: i1=" << QPoint(i1_x, i1_y) << " i2=" << QPoint(i2_x, i2_y);
+ if (qAbs(i1_x - i2_x) <= split_limit && qAbs(i1_y - i2_y) <= split_limit) {
+ // got a quadratic bezier curve
+ TTF_POINT np;
+ np.x = (i1_x + i2_x) >> 1;
+ np.y = (i1_y + i2_y) >> 1;
+ if (try_reduce) {
+ // see if we can optimise out the last onCurve point
+ int mx = (points->at(points->size() - 2).x + base[2].x) >> 1;
+ int my = (points->at(points->size() - 2).y + base[2].y) >> 1;
+ if (qAbs(mx - base[3].x) <= split_limit && qAbs(my = base[3].y) <= split_limit)
+ points->takeLast();
+ try_reduce = false;
+ }
+ np.flags = OffCurve;
+ points->append(np);
+// qDebug() << " appending offcurve point " << QPoint(np.x, np.y);
+ base -= 3;
+ } else {
+ // need to split
+// qDebug() << " -> splitting";
+ qint16 a, b, c, d;
+ base[6].x = base[3].x;
+ c = base[1].x;
+ d = base[2].x;
+ base[1].x = a = ( base[0].x + c ) >> 1;
+ base[5].x = b = ( base[3].x + d ) >> 1;
+ c = ( c + d ) >> 1;
+ base[2].x = a = ( a + c ) >> 1;
+ base[4].x = b = ( b + c ) >> 1;
+ base[3].x = ( a + b ) >> 1;
+
+ base[6].y = base[3].y;
+ c = base[1].y;
+ d = base[2].y;
+ base[1].y = a = ( base[0].y + c ) >> 1;
+ base[5].y = b = ( base[3].y + d ) >> 1;
+ c = ( c + d ) >> 1;
+ base[2].y = a = ( a + c ) >> 1;
+ base[4].y = b = ( b + c ) >> 1;
+ base[3].y = ( a + b ) >> 1;
+ base += 3;
+ }
+ }
+ p = list[0];
+ p.flags = OnCurve;
+ break;
+ }
+ case QPainterPath::CurveToDataElement:
+ Q_ASSERT(false);
+ break;
+ }
+// qDebug() << " appending oncurve point " << QPoint(p.x, p.y);
+ points->append(p);
+ }
+ int start = endPoints->size() ? endPoints->at(endPoints->size()-1) + 1 : 0;
+ int end = points->size() - 1;
+ if (points->at(end).x == points->at(start).x
+ && points->at(end).y == points->at(start).y)
+ points->takeLast();
+ endPoints->append(points->size() - 1);
+}
+
+static void getBounds(const QList<TTF_POINT> &points, qint16 *xmin, qint16 *xmax, qint16 *ymin, qint16 *ymax)
+{
+ *xmin = points.at(0).x;
+ *xmax = *xmin;
+ *ymin = points.at(0).y;
+ *ymax = *ymin;
+
+ for (int i = 1; i < points.size(); ++i) {
+ *xmin = qMin(*xmin, points.at(i).x);
+ *xmax = qMax(*xmax, points.at(i).x);
+ *ymin = qMin(*ymin, points.at(i).y);
+ *ymax = qMax(*ymax, points.at(i).y);
+ }
+}
+
+static int convertToRelative(QList<TTF_POINT> *points)
+{
+ // convert points to relative and setup flags
+// qDebug() << "relative points:";
+ qint16 prev_x = 0;
+ qint16 prev_y = 0;
+ int point_array_size = 0;
+ for (int i = 0; i < points->size(); ++i) {
+ const int x = points->at(i).x;
+ const int y = points->at(i).y;
+ TTF_POINT rel;
+ rel.x = x - prev_x;
+ rel.y = y - prev_y;
+ rel.flags = points->at(i).flags;
+ Q_ASSERT(rel.flags < 2);
+ if (!rel.x) {
+ rel.flags |= XSame;
+ } else if (rel.x > 0 && rel.x < 256) {
+ rel.flags |= XShortVector|XShortPositive;
+ point_array_size++;
+ } else if (rel.x < 0 && rel.x > -256) {
+ rel.flags |= XShortVector;
+ rel.x = -rel.x;
+ point_array_size++;
+ } else {
+ point_array_size += 2;
+ }
+ if (!rel.y) {
+ rel.flags |= YSame;
+ } else if (rel.y > 0 && rel.y < 256) {
+ rel.flags |= YShortVector|YShortPositive;
+ point_array_size++;
+ } else if (rel.y < 0 && rel.y > -256) {
+ rel.flags |= YShortVector;
+ rel.y = -rel.y;
+ point_array_size++;
+ } else {
+ point_array_size += 2;
+ }
+ (*points)[i] = rel;
+// #define toString(x) ((rel.flags & x) ? #x : "")
+// qDebug() << " " << QPoint(rel.x, rel.y) << "flags="
+// << toString(OnCurve) << toString(XShortVector)
+// << (rel.flags & XShortVector ? toString(XShortPositive) : toString(XSame))
+// << toString(YShortVector)
+// << (rel.flags & YShortVector ? toString(YShortPositive) : toString(YSame));
+
+ prev_x = x;
+ prev_y = y;
+ }
+ return point_array_size;
+}
+
+static void getGlyphData(QTtfGlyph *glyph, const QList<TTF_POINT> &points, const QList<int> &endPoints, int point_array_size)
+{
+ const int max_size = 5*sizeof(qint16) // header
+ + endPoints.size()*sizeof(quint16) // end points of contours
+ + sizeof(quint16) // instruction length == 0
+ + points.size()*(1) // flags
+ + point_array_size; // coordinates
+
+ glyph->data.resize(max_size);
+
+ QTtfStream s(glyph->data);
+ s << qint16(endPoints.size())
+ << glyph->xMin << glyph->yMin << glyph->xMax << glyph->yMax;
+
+ for (int i = 0; i < endPoints.size(); ++i)
+ s << quint16(endPoints.at(i));
+ s << quint16(0); // instruction length
+
+ // emit flags
+ for (int i = 0; i < points.size(); ++i)
+ s << quint8(points.at(i).flags);
+ // emit points
+ for (int i = 0; i < points.size(); ++i) {
+ quint8 flags = points.at(i).flags;
+ qint16 x = points.at(i).x;
+
+ if (flags & XShortVector)
+ s << quint8(x);
+ else if (!(flags & XSame))
+ s << qint16(x);
+ }
+ for (int i = 0; i < points.size(); ++i) {
+ quint8 flags = points.at(i).flags;
+ qint16 y = points.at(i).y;
+
+ if (flags & YShortVector)
+ s << quint8(y);
+ else if (!(flags & YSame))
+ s << qint16(y);
+ }
+
+// qDebug() << "offset=" << s.offset() << "max_size=" << max_size << "point_array_size=" << point_array_size;
+ Q_ASSERT(s.offset() == max_size);
+
+ glyph->numContours = endPoints.size();
+ glyph->numPoints = points.size();
+}
+
+static QTtfGlyph generateGlyph(int index, const QPainterPath &path, qreal advance, qreal lsb, qreal ppem)
+{
+ QList<TTF_POINT> points;
+ QList<int> endPoints;
+ QTtfGlyph glyph;
+ glyph.index = index;
+ glyph.advanceWidth = qRound(advance * 2048. / ppem);
+ glyph.lsb = qRound(lsb * 2048. / ppem);
+
+ if (!path.elementCount()) {
+ //qDebug("glyph %d is empty", index);
+ lsb = 0;
+ glyph.xMin = glyph.xMax = glyph.yMin = glyph.yMax = 0;
+ glyph.numContours = 0;
+ glyph.numPoints = 0;
+ return glyph;
+ }
+
+ convertPath(path, &points, &endPoints, ppem);
+
+// qDebug() << "number of contours=" << endPoints.size();
+// for (int i = 0; i < points.size(); ++i)
+// qDebug() << " point[" << i << "] = " << QPoint(points.at(i).x, points.at(i).y) << " flags=" << points.at(i).flags;
+// qDebug() << "endPoints:";
+// for (int i = 0; i < endPoints.size(); ++i)
+// qDebug() << endPoints.at(i);
+
+ getBounds(points, &glyph.xMin, &glyph.xMax, &glyph.yMin, &glyph.yMax);
+ int point_array_size = convertToRelative(&points);
+ getGlyphData(&glyph, points, endPoints, point_array_size);
+ return glyph;
+}
+
+Q_STATIC_GLOBAL_OPERATOR bool operator <(const QTtfGlyph &g1, const QTtfGlyph &g2)
+{
+ return g1.index < g2.index;
+}
+
+static QList<QTtfTable> generateGlyphTables(qttf_font_tables &tables, const QList<QTtfGlyph> &_glyphs)
+{
+ const int max_size_small = 65536*2;
+ QList<QTtfGlyph> glyphs = _glyphs;
+ qSort(glyphs);
+
+ Q_ASSERT(tables.maxp.numGlyphs == glyphs.at(glyphs.size()-1).index + 1);
+ int nGlyphs = tables.maxp.numGlyphs;
+
+ int glyf_size = 0;
+ for (int i = 0; i < glyphs.size(); ++i)
+ glyf_size += (glyphs.at(i).data.size() + 3) & ~3;
+
+ tables.head.indexToLocFormat = glyf_size < max_size_small ? 0 : 1;
+ tables.hhea.numberOfHMetrics = nGlyphs;
+
+ QTtfTable glyf;
+ glyf.tag = MAKE_TAG('g', 'l', 'y', 'f');
+
+ QTtfTable loca;
+ loca.tag = MAKE_TAG('l', 'o', 'c', 'a');
+ loca.data.resize(glyf_size < max_size_small ? (nGlyphs+1)*sizeof(quint16) : (nGlyphs+1)*sizeof(quint32));
+ QTtfStream ls(loca.data);
+
+ QTtfTable hmtx;
+ hmtx.tag = MAKE_TAG('h', 'm', 't', 'x');
+ hmtx.data.resize(nGlyphs*4);
+ QTtfStream hs(hmtx.data);
+
+ int pos = 0;
+ for (int i = 0; i < nGlyphs; ++i) {
+ int gpos = glyf.data.size();
+ quint16 advance = 0;
+ qint16 lsb = 0;
+
+ if (glyphs[pos].index == i) {
+ // emit glyph
+// qDebug("emitting glyph %d: size=%d", i, glyphs.at(i).data.size());
+ glyf.data += glyphs.at(pos).data;
+ while (glyf.data.size() & 1)
+ glyf.data.append('\0');
+ advance = glyphs.at(pos).advanceWidth;
+ lsb = glyphs.at(pos).lsb;
+ ++pos;
+ }
+ if (glyf_size < max_size_small) {
+ // use short loca format
+ ls << quint16(gpos>>1);
+ } else {
+ // use long loca format
+ ls << quint32(gpos);
+ }
+ hs << advance
+ << lsb;
+ }
+ if (glyf_size < max_size_small) {
+ // use short loca format
+ ls << quint16(glyf.data.size()>>1);
+ } else {
+ // use long loca format
+ ls << quint32(glyf.data.size());
+ }
+
+ Q_ASSERT(loca.data.size() == ls.offset());
+ Q_ASSERT(hmtx.data.size() == hs.offset());
+
+ QList<QTtfTable> list;
+ list.append(glyf);
+ list.append(loca);
+ list.append(hmtx);
+ return list;
+}
+
+Q_STATIC_GLOBAL_OPERATOR bool operator <(const QTtfTable &t1, const QTtfTable &t2)
+{
+ return t1.tag < t2.tag;
+}
+
+static QByteArray bindFont(const QList<QTtfTable>& _tables)
+{
+ QList<QTtfTable> tables = _tables;
+
+ qSort(tables);
+
+ QByteArray font;
+ const int header_size = sizeof(qint32) + 4*sizeof(quint16);
+ const int directory_size = 4*sizeof(quint32)*tables.size();
+ font.resize(header_size + directory_size);
+
+ int log2 = 0;
+ int pow = 1;
+ int n = tables.size() >> 1;
+ while (n) {
+ ++log2;
+ pow <<= 1;
+ n >>= 1;
+ }
+
+ quint32 head_offset = 0;
+ {
+ QTtfStream f(font);
+// Offset Table
+// Type Name Description
+// qint32 sfnt version 0x00010000 for version 1.0.
+// quint16 numTables Number of tables.
+// quint16 searchRange (Maximum power of 2 <= numTables) x 16.
+// quint16 entrySelector Log2(maximum power of 2 <= numTables).
+// quint16 rangeShift NumTables x 16-searchRange.
+ f << qint32(0x00010000)
+ << quint16(tables.size())
+ << quint16(16*pow)
+ << quint16(log2)
+ << quint16(16*(tables.size() - pow));
+
+// Table Directory
+// Type Name Description
+// quint32 tag 4 -byte identifier.
+// quint32 checkSum CheckSum for this table.
+// quint32 offset Offset from beginning of TrueType font file.
+// quint32 length Length of this table.
+ quint32 table_offset = header_size + directory_size;
+ for (int i = 0; i < tables.size(); ++i) {
+ const QTtfTable &t = tables.at(i);
+ const quint32 size = (t.data.size() + 3) & ~3;
+ if (t.tag == MAKE_TAG('h', 'e', 'a', 'd'))
+ head_offset = table_offset;
+ f << t.tag
+ << checksum(t.data)
+ << table_offset
+ << t.data.size();
+ table_offset += size;
+#define TAG(x) char(t.tag >> 24) << char((t.tag >> 16) & 0xff) << char((t.tag >> 8) & 0xff) << char(t.tag & 0xff)
+ //qDebug() << "table " << TAG(t.tag) << "has size " << t.data.size() << "stream at " << f.offset();
+ }
+ }
+ for (int i = 0; i < tables.size(); ++i) {
+ const QByteArray &t = tables.at(i).data;
+ font += t;
+ int s = t.size();
+ while (s & 3) { font += '\0'; ++s; }
+ }
+
+ if (!head_offset) {
+ qWarning("QFontSubset: Font misses 'head' table");
+ return QByteArray();
+ }
+
+ // calculate the fonts checksum and qToBigEndian into 'head's checksum_adjust
+ quint32 checksum_adjust = 0xB1B0AFBA - checksum(font);
+ qToBigEndian(checksum_adjust, (uchar *)font.data() + head_offset + 8);
+
+ return font;
+}
+
+
+/*
+ PDF requires the following tables:
+
+ head, hhea, loca, maxp, cvt , prep, glyf, hmtx, fpgm
+
+ This means we don't have to add a os/2, post or name table. cvt , prep and fpgm could be empty
+ if really required.
+*/
+
+QByteArray QFontSubset::toTruetype() const
+{
+ qttf_font_tables font;
+ memset(&font, 0, sizeof(qttf_font_tables));
+
+ qreal ppem = fontEngine->fontDef.pixelSize;
+#define TO_TTF(x) qRound(x * 2048. / ppem)
+ QList<QTtfGlyph> glyphs;
+
+ QFontEngine::Properties properties = fontEngine->properties();
+ // initialize some stuff needed in createWidthArray
+ emSquare = 2048;
+ widths.resize(nGlyphs());
+
+ // head table
+ font.head.font_revision = 0x00010000;
+ font.head.flags = (1 << 2) | (1 << 4);
+ font.head.created = 0; // ###
+ font.head.modified = 0; // ###
+ font.head.xMin = SHRT_MAX;
+ font.head.xMax = SHRT_MIN;
+ font.head.yMin = SHRT_MAX;
+ font.head.yMax = SHRT_MIN;
+ font.head.macStyle = (fontEngine->fontDef.weight > QFont::Normal) ? 1 : 0;
+ font.head.macStyle |= (fontEngine->fontDef.styleHint != QFont::StyleNormal) ? 1 : 0;
+
+ // hhea table
+ font.hhea.ascender = qRound(properties.ascent);
+ font.hhea.descender = -qRound(properties.descent);
+ font.hhea.lineGap = qRound(properties.leading);
+ font.hhea.maxAdvanceWidth = TO_TTF(fontEngine->maxCharWidth());
+ font.hhea.minLeftSideBearing = TO_TTF(fontEngine->minLeftBearing());
+ font.hhea.minRightSideBearing = TO_TTF(fontEngine->minRightBearing());
+ font.hhea.xMaxExtent = SHRT_MIN;
+
+ font.maxp.numGlyphs = 0;
+ font.maxp.maxPoints = 0;
+ font.maxp.maxContours = 0;
+ font.maxp.maxCompositePoints = 0;
+ font.maxp.maxCompositeContours = 0;
+ font.maxp.maxComponentElements = 0;
+ font.maxp.maxComponentDepth = 0;
+ font.maxp.numGlyphs = nGlyphs();
+
+
+
+ uint sumAdvances = 0;
+ for (int i = 0; i < nGlyphs(); ++i) {
+ glyph_t g = glyph_indices.at(i);
+ QPainterPath path;
+ glyph_metrics_t metric;
+ fontEngine->getUnscaledGlyph(g, &path, &metric);
+ if (noEmbed) {
+ path = QPainterPath();
+ if (g == 0)
+ path.addRect(QRectF(0, 0, 1000, 1000));
+ }
+ QTtfGlyph glyph = generateGlyph(i, path, metric.xoff.toReal(), metric.x.toReal(), properties.emSquare.toReal());
+
+ font.head.xMin = qMin(font.head.xMin, glyph.xMin);
+ font.head.xMax = qMax(font.head.xMax, glyph.xMax);
+ font.head.yMin = qMin(font.head.yMin, glyph.yMin);
+ font.head.yMax = qMax(font.head.yMax, glyph.yMax);
+
+ font.hhea.xMaxExtent = qMax(font.hhea.xMaxExtent, (qint16)(glyph.lsb + glyph.xMax - glyph.xMin));
+
+ font.maxp.maxPoints = qMax(font.maxp.maxPoints, glyph.numPoints);
+ font.maxp.maxContours = qMax(font.maxp.maxContours, glyph.numContours);
+
+ if (glyph.xMax > glyph.xMin)
+ sumAdvances += glyph.xMax - glyph.xMin;
+
+// qDebug("adding glyph %d size=%d", glyph.index, glyph.data.size());
+ glyphs.append(glyph);
+ widths[i] = glyph.advanceWidth;
+ }
+
+
+ QList<QTtfTable> tables = generateGlyphTables(font, glyphs);
+ tables.append(generateHead(font.head));
+ tables.append(generateHhea(font.hhea));
+ tables.append(generateMaxp(font.maxp));
+ // name
+ QTtfTable name_table;
+ name_table.tag = MAKE_TAG('n', 'a', 'm', 'e');
+ if (!noEmbed)
+ name_table.data = fontEngine->getSfntTable(name_table.tag);
+ if (name_table.data.isEmpty()) {
+ qttf_name_table name;
+ if (noEmbed)
+ name.copyright = QLatin1String("Fake font");
+ else
+ name.copyright = QLatin1String(properties.copyright);
+ name.family = fontEngine->fontDef.family;
+ name.subfamily = QLatin1String("Regular"); // ######
+ name.postscript_name = QLatin1String(properties.postscriptName);
+ name_table = generateName(name);
+ }
+ tables.append(name_table);
+
+ if (!noEmbed) {
+ QTtfTable os2;
+ os2.tag = MAKE_TAG('O', 'S', '/', '2');
+ os2.data = fontEngine->getSfntTable(os2.tag);
+ if (!os2.data.isEmpty())
+ tables.append(os2);
+ }
+
+ return bindFont(tables);
+}
+
+// ------------------ Type 1 generation ---------------------------
+
+// needs at least 6 bytes of space in tmp
+static const char *encodeNumber(int num, char *tmp)
+{
+ const char *ret = tmp;
+ if(num >= -107 && num <= 107) {
+ QPdf::toHex((uchar)(num + 139), tmp);
+ tmp += 2;
+ } else if (num > 107 && num <= 1131) {
+ num -= 108;
+ QPdf::toHex((uchar)((num >> 8) + 247), tmp);
+ tmp += 2;
+ QPdf::toHex((uchar)(num & 0xff), tmp);
+ tmp += 2;
+ } else if(num < - 107 && num >= -1131) {
+ num += 108;
+ num = -num;
+ QPdf::toHex((uchar)((num >> 8) + 251), tmp);
+ tmp += 2;
+ QPdf::toHex((uchar)(num & 0xff), tmp);
+ tmp += 2;
+ } else {
+ *tmp++ = 'f';
+ *tmp++ = 'f';
+ QPdf::toHex((uchar)(num >> 24), tmp);
+ tmp += 2;
+ QPdf::toHex((uchar)(num >> 16), tmp);
+ tmp += 2;
+ QPdf::toHex((uchar)(num >> 8), tmp);
+ tmp += 2;
+ QPdf::toHex((uchar)(num >> 0), tmp);
+ tmp += 2;
+ }
+ *tmp = 0;
+// qDebug("encodeNumber: %d -> '%s'", num, ret);
+ return ret;
+}
+
+static QByteArray charString(const QPainterPath &path, qreal advance, qreal lsb, qreal ppem)
+{
+ // the charstring commands we need
+ const char *hsbw = "0D";
+ const char *closepath = "09";
+ const char *moveto[3] = { "16", "04", "15" };
+ const char *lineto[3] = { "06", "07", "05" };
+ const char *rcurveto = "08";
+ const char *endchar = "0E";
+
+ enum { horizontal = 1, vertical = 2 };
+
+ char tmp[16];
+
+ qreal factor = 1000./ppem;
+
+ int lsb_i = qRound(lsb*factor);
+ int advance_i = qRound(advance*factor);
+// qDebug("--- charstring");
+
+ // first of all add lsb and width to the charstring using the hsbw command
+ QByteArray charstring;
+ charstring += encodeNumber(lsb_i, tmp);
+ charstring += encodeNumber(advance_i, tmp);
+ charstring += hsbw;
+
+ // add the path
+ int xl = lsb_i;
+ int yl = 0;
+ bool openpath = false;
+ for (int i = 0; i < path.elementCount(); ++i) {
+ const QPainterPath::Element &elm = path.elementAt(i);
+ int x = qRound(elm.x*factor);
+ int y = -qRound(elm.y*factor);
+ int dx = x - xl;
+ int dy = y - yl;
+ if (elm.type == QPainterPath::MoveToElement && openpath) {
+// qDebug("closepath %s", closepath);
+ charstring += closepath;
+ }
+ if (elm.type == QPainterPath::MoveToElement ||
+ elm.type == QPainterPath::LineToElement) {
+ int type = -1;
+ if (dx || !dy) {
+ charstring += encodeNumber(dx, tmp);
+ type += horizontal;
+// qDebug("horizontal");
+ }
+ if (dy) {
+ charstring += encodeNumber(dy, tmp);
+ type += vertical;
+// qDebug("vertical");
+ }
+// qDebug("moveto/lineto %s", (elm.type == QPainterPath::MoveToElement ? moveto[type] : lineto[type]));
+ charstring += (elm.type == QPainterPath::MoveToElement ? moveto[type] : lineto[type]);
+ openpath = true;
+ xl = x;
+ yl = y;
+ } else {
+ Q_ASSERT(elm.type == QPainterPath::CurveToElement);
+ const QPainterPath::Element &elm2 = path.elementAt(++i);
+ const QPainterPath::Element &elm3 = path.elementAt(++i);
+ int x2 = qRound(elm2.x*factor);
+ int y2 = -qRound(elm2.y*factor);
+ int x3 = qRound(elm3.x*factor);
+ int y3 = -qRound(elm3.y*factor);
+ charstring += encodeNumber(dx, tmp);
+ charstring += encodeNumber(dy, tmp);
+ charstring += encodeNumber(x2 - x, tmp);
+ charstring += encodeNumber(y2 - y, tmp);
+ charstring += encodeNumber(x3 - x2, tmp);
+ charstring += encodeNumber(y3 - y2, tmp);
+ charstring += rcurveto;
+ openpath = true;
+ xl = x3;
+ yl = y3;
+// qDebug("rcurveto");
+ }
+ }
+ if (openpath)
+ charstring += closepath;
+ charstring += endchar;
+ if (charstring.length() > 240) {
+ int pos = 240;
+ while (pos < charstring.length()) {
+ charstring.insert(pos, '\n');
+ pos += 241;
+ }
+ }
+ return charstring;
+}
+
+#ifndef QT_NO_FREETYPE
+static const char *helvetica_styles[4] = {
+ "Helvetica",
+ "Helvetica-Bold",
+ "Helvetica-Oblique",
+ "Helvetica-BoldOblique"
+};
+static const char *times_styles[4] = {
+ "Times-Regular",
+ "Times-Bold",
+ "Times-Italic",
+ "Times-BoldItalic"
+};
+static const char *courier_styles[4] = {
+ "Courier",
+ "Courier-Bold",
+ "Courier-Oblique",
+ "Courier-BoldOblique"
+};
+#endif
+
+QByteArray QFontSubset::toType1() const
+{
+ QFontEngine::Properties properties = fontEngine->properties();
+ QVector<int> reverseMap = getReverseMap();
+
+ QByteArray font;
+ QPdf::ByteStream s(&font);
+
+ QByteArray id = QByteArray::number(object_id);
+ QByteArray psname = properties.postscriptName;
+ psname.replace(" ", "");
+
+ standard_font = false;
+
+#ifndef QT_NO_FREETYPE
+ FT_Face face = ft_face(fontEngine);
+ if (face && !FT_IS_SCALABLE(face)) {
+ int style = 0;
+ if (fontEngine->fontDef.style)
+ style += 2;
+ if (fontEngine->fontDef.weight >= QFont::Bold)
+ style++;
+ if (fontEngine->fontDef.family.contains(QLatin1String("Helvetica"))) {
+ psname = helvetica_styles[style];
+ standard_font = true;
+ } else if (fontEngine->fontDef.family.contains(QLatin1String("Times"))) {
+ psname = times_styles[style];
+ standard_font = true;
+ } else if (fontEngine->fontDef.family.contains(QLatin1String("Courier"))) {
+ psname = courier_styles[style];
+ standard_font = true;
+ }
+ }
+#endif
+ s << "/F" << id << "-Base\n";
+ if (standard_font) {
+ s << "/" << psname << " findfont\n"
+ "0 dict copy dup /NumGlyphs 0 put dup /CMap 256 array put def\n";
+ } else {
+ s << "<<\n";
+ if(!psname.isEmpty())
+ s << "/FontName /" << psname << "\n";
+ s << "/FontInfo <</FsType " << (int)fontEngine->fsType << ">>\n"
+ "/FontType 1\n"
+ "/PaintType 0\n"
+ "/FontMatrix [.001 0 0 .001 0 0]\n"
+ "/FontBBox { 0 0 0 0 }\n"
+ "/Private <<\n"
+ "/password 5839\n"
+ "/MinFeature {16 16}\n"
+ "/BlueValues []\n"
+ "/lenIV -1\n"
+ ">>\n"
+ "/CharStrings << >>\n"
+ "/NumGlyphs 0\n"
+ "/CMap 256 array\n"
+ ">> def\n";
+ }
+ s << type1AddedGlyphs();
+ downloaded_glyphs = glyph_indices.size();
+
+ return font;
+}
+
+QByteArray QFontSubset::type1AddedGlyphs() const
+{
+ if (downloaded_glyphs == glyph_indices.size())
+ return QByteArray();
+
+ QFontEngine::Properties properties = fontEngine->properties();
+ QVector<int> reverseMap = getReverseMap();
+ QByteArray glyphs;
+ QPdf::ByteStream s(&glyphs);
+
+ int nGlyphs = glyph_indices.size();
+ QByteArray id = QByteArray::number(object_id);
+
+ s << "F" << id << "-Base [\n";
+ for (int i = downloaded_glyphs; i < nGlyphs; ++i) {
+ glyph_t g = glyph_indices.at(i);
+ QPainterPath path;
+ glyph_metrics_t metric;
+ fontEngine->getUnscaledGlyph(g, &path, &metric);
+ QByteArray charstring = charString(path, metric.xoff.toReal(), metric.x.toReal(),
+ properties.emSquare.toReal());
+ s << glyphName(i, reverseMap);
+ if (!standard_font)
+ s << "\n<" << charstring << ">\n";
+ }
+ s << (standard_font ? "] T1AddMapping\n" : "] T1AddGlyphs\n");
+ return glyphs;
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_PRINTER
diff --git a/src/gui/text/qfontsubset_p.h b/src/gui/text/qfontsubset_p.h
new file mode 100644
index 0000000000..3106ba83a6
--- /dev/null
+++ b/src/gui/text/qfontsubset_p.h
@@ -0,0 +1,99 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QFONTSUBSET_P_H
+#define QFONTSUBSET_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/qfontengine_p.h"
+
+#ifndef QT_NO_PRINTER
+
+QT_BEGIN_NAMESPACE
+
+class QFontSubset
+{
+public:
+ QFontSubset(QFontEngine *fe, int obj_id = 0)
+ : object_id(obj_id), noEmbed(false), fontEngine(fe), downloaded_glyphs(0), standard_font(false)
+ { fontEngine->ref.ref(); addGlyph(0); }
+ ~QFontSubset() {
+ if (!fontEngine->ref.deref())
+ delete fontEngine;
+ }
+
+ QByteArray toTruetype() const;
+ QByteArray toType1() const;
+ QByteArray type1AddedGlyphs() const;
+ QByteArray widthArray() const;
+ QByteArray createToUnicodeMap() const;
+ QVector<int> getReverseMap() const;
+ QByteArray glyphName(unsigned int glyph, const QVector<int> reverseMap) const;
+
+ static QByteArray glyphName(unsigned short unicode, bool symbol);
+
+ int addGlyph(int index);
+ const int object_id;
+ bool noEmbed;
+ QFontEngine *fontEngine;
+ QList<int> glyph_indices;
+ mutable int downloaded_glyphs;
+ mutable bool standard_font;
+ int nGlyphs() const { return glyph_indices.size(); }
+ mutable QFixed emSquare;
+ mutable QVector<QFixed> widths;
+};
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_PRINTER
+
+#endif // QFONTSUBSET_P_H
diff --git a/src/gui/text/qfragmentmap.cpp b/src/gui/text/qfragmentmap.cpp
new file mode 100644
index 0000000000..c3ce685ba6
--- /dev/null
+++ b/src/gui/text/qfragmentmap.cpp
@@ -0,0 +1,46 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <private/qtools_p.h>
+
+#include "qfragmentmap_p.h"
+
+
diff --git a/src/gui/text/qfragmentmap_p.h b/src/gui/text/qfragmentmap_p.h
new file mode 100644
index 0000000000..737a71775d
--- /dev/null
+++ b/src/gui/text/qfragmentmap_p.h
@@ -0,0 +1,872 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QFRAGMENTMAP_P_H
+#define QFRAGMENTMAP_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 "QtCore/qglobal.h"
+#include <stdlib.h>
+#include <private/qtools_p.h>
+
+QT_BEGIN_NAMESPACE
+
+
+template <int N = 1>
+class QFragment
+{
+public:
+ quint32 parent;
+ quint32 left;
+ quint32 right;
+ quint32 color;
+ quint32 size_left_array[N];
+ quint32 size_array[N];
+ enum {size_array_max = N };
+};
+
+template <class Fragment>
+class QFragmentMapData
+{
+ enum Color { Red, Black };
+public:
+ QFragmentMapData();
+ ~QFragmentMapData();
+
+ void init();
+
+ class Header
+ {
+ public:
+ quint32 root; // this relies on being at the same position as parent in the fragment struct
+ quint32 tag;
+ quint32 freelist;
+ quint32 node_count;
+ quint32 allocated;
+ };
+
+
+ enum {fragmentSize = sizeof(Fragment) };
+
+
+ int length(uint field = 0) const;
+
+
+ inline Fragment *fragment(uint index) {
+ return (fragments + index);
+ }
+ inline const Fragment *fragment(uint index) const {
+ return (fragments + index);
+ }
+
+
+ inline Fragment &F(uint index) { return fragments[index] ; }
+ inline const Fragment &F(uint index) const { return fragments[index] ; }
+
+ inline bool isRoot(uint index) const {
+ return !fragment(index)->parent;
+ }
+
+ inline uint position(uint node, uint field = 0) const {
+ Q_ASSERT(field < Fragment::size_array_max);
+ const Fragment *f = fragment(node);
+ uint offset = f->size_left_array[field];
+ while (f->parent) {
+ uint p = f->parent;
+ f = fragment(p);
+ if (f->right == node)
+ offset += f->size_left_array[field] + f->size_array[field];
+ node = p;
+ }
+ return offset;
+ }
+ inline uint sizeRight(uint node, uint field = 0) const {
+ Q_ASSERT(field < Fragment::size_array_max);
+ uint sr = 0;
+ const Fragment *f = fragment(node);
+ node = f->right;
+ while (node) {
+ f = fragment(node);
+ sr += f->size_left_array[field] + f->size_array[field];
+ node = f->right;
+ }
+ return sr;
+ }
+ inline uint sizeLeft(uint node, uint field = 0) const {
+ Q_ASSERT(field < Fragment::size_array_max);
+ return fragment(node)->size_left_array[field];
+ }
+
+
+ inline uint size(uint node, uint field = 0) const {
+ Q_ASSERT(field < Fragment::size_array_max);
+ return fragment(node)->size_array[field];
+ }
+
+ inline void setSize(uint node, int new_size, uint field = 0) {
+ Q_ASSERT(field < Fragment::size_array_max);
+ Fragment *f = fragment(node);
+ int diff = new_size - f->size_array[field];
+ f->size_array[field] = new_size;
+ while (f->parent) {
+ uint p = f->parent;
+ f = fragment(p);
+ if (f->left == node)
+ f->size_left_array[field] += diff;
+ node = p;
+ }
+ }
+
+
+ uint findNode(int k, uint field = 0) const;
+
+ uint insert_single(int key, uint length);
+ uint erase_single(uint f);
+
+ uint minimum(uint n) const {
+ while (n && fragment(n)->left)
+ n = fragment(n)->left;
+ return n;
+ }
+
+ uint maximum(uint n) const {
+ while (n && fragment(n)->right)
+ n = fragment(n)->right;
+ return n;
+ }
+
+ uint next(uint n) const;
+ uint previous(uint n) const;
+
+ inline uint root() const {
+ Q_ASSERT(!head->root || !fragment(head->root)->parent);
+ return head->root;
+ }
+ inline void setRoot(uint new_root) {
+ Q_ASSERT(!head->root || !fragment(new_root)->parent);
+ head->root = new_root;
+ }
+
+ union {
+ Header *head;
+ Fragment *fragments;
+ };
+
+private:
+
+ void rotateLeft(uint x);
+ void rotateRight(uint x);
+ void rebalance(uint x);
+ void removeAndRebalance(uint z);
+
+ uint createFragment();
+ void freeFragment(uint f);
+
+};
+
+template <class Fragment>
+QFragmentMapData<Fragment>::QFragmentMapData()
+{
+ init();
+}
+
+template <class Fragment>
+void QFragmentMapData<Fragment>::init()
+{
+ fragments = (Fragment *)malloc(64*fragmentSize);
+ head->tag = (((quint32)'p') << 24) | (((quint32)'m') << 16) | (((quint32)'a') << 8) | 'p'; //TAG('p', 'm', 'a', 'p');
+ head->root = 0;
+ head->freelist = 1;
+ head->node_count = 0;
+ head->allocated = 64;
+ // mark all items to the right as unused
+ F(head->freelist).right = 0;
+}
+
+template <class Fragment>
+QFragmentMapData<Fragment>::~QFragmentMapData()
+{
+ free(head);
+}
+
+template <class Fragment>
+uint QFragmentMapData<Fragment>::createFragment()
+{
+ Q_ASSERT(head->freelist <= head->allocated);
+
+ uint freePos = head->freelist;
+ if (freePos == head->allocated) {
+ // need to create some free space
+ uint needed = qAllocMore((freePos+1)*fragmentSize, 0);
+ Q_ASSERT(needed/fragmentSize > head->allocated);
+ fragments = (Fragment *)realloc(fragments, needed);
+ head->allocated = needed/fragmentSize;
+ F(freePos).right = 0;
+ }
+
+ uint nextPos = F(freePos).right;
+ if (!nextPos) {
+ nextPos = freePos+1;
+ if (nextPos < head->allocated)
+ F(nextPos).right = 0;
+ }
+
+ head->freelist = nextPos;
+
+ ++head->node_count;
+
+ return freePos;
+}
+
+template <class Fragment>
+void QFragmentMapData<Fragment>::freeFragment(uint i)
+{
+ F(i).right = head->freelist;
+ head->freelist = i;
+
+ --head->node_count;
+}
+
+
+template <class Fragment>
+uint QFragmentMapData<Fragment>::next(uint n) const {
+ Q_ASSERT(n);
+ if (F(n).right) {
+ n = F(n).right;
+ while (F(n).left)
+ n = F(n).left;
+ } else {
+ uint y = F(n).parent;
+ while (F(n).parent && n == F(y).right) {
+ n = y;
+ y = F(y).parent;
+ }
+ n = y;
+ }
+ return n;
+}
+
+template <class Fragment>
+uint QFragmentMapData<Fragment>::previous(uint n) const {
+ if (!n)
+ return maximum(root());
+
+ if (F(n).left) {
+ n = F(n).left;
+ while (F(n).right)
+ n = F(n).right;
+ } else {
+ uint y = F(n).parent;
+ while (F(n).parent && n == F(y).left) {
+ n = y;
+ y = F(y).parent;
+ }
+ n = y;
+ }
+ return n;
+}
+
+
+/*
+ x y
+ \ / \
+ y --> x b
+ / \ \
+ a b a
+*/
+template <class Fragment>
+void QFragmentMapData<Fragment>::rotateLeft(uint x)
+{
+ uint p = F(x).parent;
+ uint y = F(x).right;
+
+
+ if (y) {
+ F(x).right = F(y).left;
+ if (F(y).left)
+ F(F(y).left).parent = x;
+ F(y).left = x;
+ F(y).parent = p;
+ } else {
+ F(x).right = 0;
+ }
+ if (!p) {
+ Q_ASSERT(head->root == x);
+ head->root = y;
+ }
+ else if (x == F(p).left)
+ F(p).left = y;
+ else
+ F(p).right = y;
+ F(x).parent = y;
+ for (uint field = 0; field < Fragment::size_array_max; ++field)
+ F(y).size_left_array[field] += F(x).size_left_array[field] + F(x).size_array[field];
+}
+
+
+/*
+ x y
+ / / \
+ y --> a x
+ / \ /
+ a b b
+*/
+template <class Fragment>
+void QFragmentMapData<Fragment>::rotateRight(uint x)
+{
+ uint y = F(x).left;
+ uint p = F(x).parent;
+
+ if (y) {
+ F(x).left = F(y).right;
+ if (F(y).right)
+ F(F(y).right).parent = x;
+ F(y).right = x;
+ F(y).parent = p;
+ } else {
+ F(x).left = 0;
+ }
+ if (!p) {
+ Q_ASSERT(head->root == x);
+ head->root = y;
+ }
+ else if (x == F(p).right)
+ F(p).right = y;
+ else
+ F(p).left = y;
+ F(x).parent = y;
+ for (uint field = 0; field < Fragment::size_array_max; ++field)
+ F(x).size_left_array[field] -= F(y).size_left_array[field] + F(y).size_array[field];
+}
+
+
+template <class Fragment>
+void QFragmentMapData<Fragment>::rebalance(uint x)
+{
+ F(x).color = Red;
+
+ while (F(x).parent && F(F(x).parent).color == Red) {
+ uint p = F(x).parent;
+ uint pp = F(p).parent;
+ Q_ASSERT(pp);
+ if (p == F(pp).left) {
+ uint y = F(pp).right;
+ if (y && F(y).color == Red) {
+ F(p).color = Black;
+ F(y).color = Black;
+ F(pp).color = Red;
+ x = pp;
+ } else {
+ if (x == F(p).right) {
+ x = p;
+ rotateLeft(x);
+ p = F(x).parent;
+ pp = F(p).parent;
+ }
+ F(p).color = Black;
+ if (pp) {
+ F(pp).color = Red;
+ rotateRight(pp);
+ }
+ }
+ } else {
+ uint y = F(pp).left;
+ if (y && F(y).color == Red) {
+ F(p).color = Black;
+ F(y).color = Black;
+ F(pp).color = Red;
+ x = pp;
+ } else {
+ if (x == F(p).left) {
+ x = p;
+ rotateRight(x);
+ p = F(x).parent;
+ pp = F(p).parent;
+ }
+ F(p).color = Black;
+ if (pp) {
+ F(pp).color = Red;
+ rotateLeft(pp);
+ }
+ }
+ }
+ }
+ F(root()).color = Black;
+}
+
+
+template <class Fragment>
+uint QFragmentMapData<Fragment>::erase_single(uint z)
+{
+ uint w = previous(z);
+ uint y = z;
+ uint x;
+ uint p;
+
+ if (!F(y).left) {
+ x = F(y).right;
+ } else if (!F(y).right) {
+ x = F(y).left;
+ } else {
+ y = F(y).right;
+ while (F(y).left)
+ y = F(y).left;
+ x = F(y).right;
+ }
+
+ if (y != z) {
+ F(F(z).left).parent = y;
+ F(y).left = F(z).left;
+ for (uint field = 0; field < Fragment::size_array_max; ++field)
+ F(y).size_left_array[field] = F(z).size_left_array[field];
+ if (y != F(z).right) {
+ /*
+ z y
+ / \ / \
+ a b a b
+ / /
+ ... --> ...
+ / /
+ y x
+ / \
+ 0 x
+ */
+ p = F(y).parent;
+ if (x)
+ F(x).parent = p;
+ F(p).left = x;
+ F(y).right = F(z).right;
+ F(F(z).right).parent = y;
+ uint n = p;
+ while (n != y) {
+ for (uint field = 0; field < Fragment::size_array_max; ++field)
+ F(n).size_left_array[field] -= F(y).size_array[field];
+ n = F(n).parent;
+ }
+ } else {
+ /*
+ z y
+ / \ / \
+ a y --> a x
+ / \
+ 0 x
+ */
+ p = y;
+ }
+ uint zp = F(z).parent;
+ if (!zp) {
+ Q_ASSERT(head->root == z);
+ head->root = y;
+ } else if (F(zp).left == z) {
+ F(zp).left = y;
+ for (uint field = 0; field < Fragment::size_array_max; ++field)
+ F(zp).size_left_array[field] -= F(z).size_array[field];
+ } else {
+ F(zp).right = y;
+ }
+ F(y).parent = zp;
+ // Swap the colors
+ uint c = F(y).color;
+ F(y).color = F(z).color;
+ F(z).color = c;
+ y = z;
+ } else {
+ /*
+ p p p p
+ / / \ \
+ z --> x z --> x
+ | |
+ x x
+ */
+ p = F(z).parent;
+ if (x)
+ F(x).parent = p;
+ if (!p) {
+ Q_ASSERT(head->root == z);
+ head->root = x;
+ } else if (F(p).left == z) {
+ F(p).left = x;
+ for (uint field = 0; field < Fragment::size_array_max; ++field)
+ F(p).size_left_array[field] -= F(z).size_array[field];
+ } else {
+ F(p).right = x;
+ }
+ }
+ uint n = z;
+ while (F(n).parent) {
+ uint p = F(n).parent;
+ if (F(p).left == n) {
+ for (uint field = 0; field < Fragment::size_array_max; ++field)
+ F(p).size_left_array[field] -= F(z).size_array[field];
+ }
+ n = p;
+ }
+
+ freeFragment(z);
+
+
+ if (F(y).color != Red) {
+ while (F(x).parent && (x == 0 || F(x).color == Black)) {
+ if (x == F(p).left) {
+ uint w = F(p).right;
+ if (F(w).color == Red) {
+ F(w).color = Black;
+ F(p).color = Red;
+ rotateLeft(p);
+ w = F(p).right;
+ }
+ if ((F(w).left == 0 || F(F(w).left).color == Black) &&
+ (F(w).right == 0 || F(F(w).right).color == Black)) {
+ F(w).color = Red;
+ x = p;
+ p = F(x).parent;
+ } else {
+ if (F(w).right == 0 || F(F(w).right).color == Black) {
+ if (F(w).left)
+ F(F(w).left).color = Black;
+ F(w).color = Red;
+ rotateRight(F(p).right);
+ w = F(p).right;
+ }
+ F(w).color = F(p).color;
+ F(p).color = Black;
+ if (F(w).right)
+ F(F(w).right).color = Black;
+ rotateLeft(p);
+ break;
+ }
+ } else {
+ uint w = F(p).left;
+ if (F(w).color == Red) {
+ F(w).color = Black;
+ F(p).color = Red;
+ rotateRight(p);
+ w = F(p).left;
+ }
+ if ((F(w).right == 0 || F(F(w).right).color == Black) &&
+ (F(w).left == 0 || F(F(w).left).color == Black)) {
+ F(w).color = Red;
+ x = p;
+ p = F(x).parent;
+ } else {
+ if (F(w).left == 0 || F(F(w).left).color == Black) {
+ if (F(w).right)
+ F(F(w).right).color = Black;
+ F(w).color = Red;
+ rotateLeft(F(p).left);
+ w = F(p).left;
+ }
+ F(w).color = F(p).color;
+ F(p).color = Black;
+ if (F(w).left)
+ F(F(w).left).color = Black;
+ rotateRight(p);
+ break;
+ }
+ }
+ }
+ if (x)
+ F(x).color = Black;
+ }
+
+ return w;
+}
+
+template <class Fragment>
+uint QFragmentMapData<Fragment>::findNode(int k, uint field) const
+{
+ Q_ASSERT(field < Fragment::size_array_max);
+ uint x = root();
+
+ uint s = k;
+ while (x) {
+ if (sizeLeft(x, field) <= s) {
+ if (s < sizeLeft(x, field) + size(x, field))
+ return x;
+ s -= sizeLeft(x, field) + size(x, field);
+ x = F(x).right;
+ } else {
+ x = F(x).left;
+ }
+ }
+ return 0;
+}
+
+template <class Fragment>
+uint QFragmentMapData<Fragment>::insert_single(int key, uint length)
+{
+ Q_ASSERT(!findNode(key) || (int)this->position(findNode(key)) == key);
+
+ uint z = createFragment();
+
+ F(z).left = 0;
+ F(z).right = 0;
+ F(z).size_array[0] = length;
+ for (uint field = 1; field < Fragment::size_array_max; ++field)
+ F(z).size_array[field] = 1;
+ for (uint field = 0; field < Fragment::size_array_max; ++field)
+ F(z).size_left_array[field] = 0;
+
+ uint y = 0;
+ uint x = root();
+
+ Q_ASSERT(!x || F(x).parent == 0);
+
+ uint s = key;
+ bool right = false;
+ while (x) {
+ y = x;
+ if (s <= F(x).size_left_array[0]) {
+ x = F(x).left;
+ right = false;
+ } else {
+ s -= F(x).size_left_array[0] + F(x).size_array[0];
+ x = F(x).right;
+ right = true;
+ }
+ }
+
+ F(z).parent = y;
+ if (!y) {
+ head->root = z;
+ } else if (!right) {
+ F(y).left = z;
+ for (uint field = 0; field < Fragment::size_array_max; ++field)
+ F(y).size_left_array[field] = F(z).size_array[field];
+ } else {
+ F(y).right = z;
+ }
+ while (y && F(y).parent) {
+ uint p = F(y).parent;
+ if (F(p).left == y) {
+ for (uint field = 0; field < Fragment::size_array_max; ++field)
+ F(p).size_left_array[field] += F(z).size_array[field];
+ }
+ y = p;
+ }
+ rebalance(z);
+
+ return z;
+}
+
+
+template <class Fragment>
+int QFragmentMapData<Fragment>::length(uint field) const {
+ uint root = this->root();
+ return root ? sizeLeft(root, field) + size(root, field) + sizeRight(root, field) : 0;
+}
+
+
+template <class Fragment> // NOTE: must inherit QFragment
+class QFragmentMap
+{
+public:
+ class Iterator
+ {
+ public:
+ QFragmentMap *pt;
+ quint32 n;
+
+ Iterator() : pt(0), n(0) {}
+ Iterator(QFragmentMap *p, int node) : pt(p), n(node) {}
+ Iterator(const Iterator& it) : pt(it.pt), n(it.n) {}
+
+ inline bool atEnd() const { return !n; }
+
+ bool operator==(const Iterator& it) const { return pt == it.pt && n == it.n; }
+ bool operator!=(const Iterator& it) const { return pt != it.pt || n != it.n; }
+ bool operator<(const Iterator &it) const { return position() < it.position(); }
+
+ Fragment *operator*() { Q_ASSERT(!atEnd()); return pt->fragment(n); }
+ const Fragment *operator*() const { Q_ASSERT(!atEnd()); return pt->fragment(n); }
+ Fragment *operator->() { Q_ASSERT(!atEnd()); return pt->fragment(n); }
+ const Fragment *operator->() const { Q_ASSERT(!atEnd()); return pt->fragment(n); }
+
+ int position() const { Q_ASSERT(!atEnd()); return pt->data.position(n); }
+ const Fragment *value() const { Q_ASSERT(!atEnd()); return pt->fragment(n); }
+ Fragment *value() { Q_ASSERT(!atEnd()); return pt->fragment(n); }
+
+ Iterator& operator++() {
+ n = pt->data.next(n);
+ return *this;
+ }
+ Iterator& operator--() {
+ n = pt->data.previous(n);
+ return *this;
+ }
+
+ };
+
+
+ class ConstIterator
+ {
+ public:
+ const QFragmentMap *pt;
+ quint32 n;
+
+ /**
+ * Functions
+ */
+ ConstIterator() : pt(0), n(0) {}
+ ConstIterator(const QFragmentMap *p, int node) : pt(p), n(node) {}
+ ConstIterator(const ConstIterator& it) : pt(it.pt), n(it.n) {}
+ ConstIterator(const Iterator& it) : pt(it.pt), n(it.n) {}
+
+ inline bool atEnd() const { return !n; }
+
+ bool operator==(const ConstIterator& it) const { return pt == it.pt && n == it.n; }
+ bool operator!=(const ConstIterator& it) const { return pt != it.pt || n != it.n; }
+ bool operator<(const ConstIterator &it) const { return position() < it.position(); }
+
+ const Fragment *operator*() const { Q_ASSERT(!atEnd()); return pt->fragment(n); }
+ const Fragment *operator->() const { Q_ASSERT(!atEnd()); return pt->fragment(n); }
+
+ int position() const { Q_ASSERT(!atEnd()); return pt->data.position(n); }
+ int size() const { Q_ASSERT(!atEnd()); return pt->data.size(n); }
+ const Fragment *value() const { Q_ASSERT(!atEnd()); return pt->fragment(n); }
+
+ ConstIterator& operator++() {
+ n = pt->data.next(n);
+ return *this;
+ }
+ ConstIterator& operator--() {
+ n = pt->data.previous(n);
+ return *this;
+ }
+ };
+
+
+ QFragmentMap() {}
+ ~QFragmentMap()
+ {
+ for (Iterator it = begin(); !it.atEnd(); ++it)
+ it.value()->free();
+ }
+
+ inline void clear() {
+ for (Iterator it = begin(); !it.atEnd(); ++it)
+ it.value()->free();
+ ::free(data.head);
+ data.init();
+ }
+
+ inline Iterator begin() { return Iterator(this, data.minimum(data.root())); }
+ inline Iterator end() { return Iterator(this, 0); }
+ inline ConstIterator begin() const { return ConstIterator(this, data.minimum(data.root())); }
+ inline ConstIterator end() const { return ConstIterator(this, 0); }
+
+ inline ConstIterator last() const { return ConstIterator(this, data.maximum(data.root())); }
+
+ inline bool isEmpty() const { return data.head->node_count == 0; }
+ inline int numNodes() const { return data.head->node_count; }
+ int length(uint field = 0) const { return data.length(field); }
+
+ Iterator find(int k, uint field = 0) { return Iterator(this, data.findNode(k, field)); }
+ ConstIterator find(int k, uint field = 0) const { return ConstIterator(this, data.findNode(k, field)); }
+
+ uint findNode(int k, uint field = 0) const { return data.findNode(k, field); }
+
+ uint insert_single(int key, uint length)
+ {
+ uint f = data.insert_single(key, length);
+ if (f != 0) {
+ Fragment *frag = fragment(f);
+ Q_ASSERT(frag);
+ frag->initialize();
+ }
+ return f;
+ }
+ uint erase_single(uint f)
+ {
+ if (f != 0) {
+ Fragment *frag = fragment(f);
+ Q_ASSERT(frag);
+ frag->free();
+ }
+ return data.erase_single(f);
+ }
+
+ inline Fragment *fragment(uint index) {
+ Q_ASSERT(index != 0);
+ return data.fragment(index);
+ }
+ inline const Fragment *fragment(uint index) const {
+ Q_ASSERT(index != 0);
+ return data.fragment(index);
+ }
+ inline uint position(uint node, uint field = 0) const { return data.position(node, field); }
+ inline uint next(uint n) const { return data.next(n); }
+ inline uint previous(uint n) const { return data.previous(n); }
+ inline uint size(uint node, uint field = 0) const { return data.size(node, field); }
+ inline void setSize(uint node, int new_size, uint field = 0)
+ { data.setSize(node, new_size, field);
+ if (node != 0 && field == 0) {
+ Fragment *frag = fragment(node);
+ Q_ASSERT(frag);
+ frag->invalidate();
+ }
+ }
+
+ inline int firstNode() const { return data.minimum(data.root()); }
+
+private:
+ friend class Iterator;
+ friend class ConstIterator;
+
+ QFragmentMapData<Fragment> data;
+
+ QFragmentMap(const QFragmentMap& m);
+ QFragmentMap& operator= (const QFragmentMap& m);
+};
+
+QT_END_NAMESPACE
+
+#endif // QFRAGMENTMAP_P_H
diff --git a/src/gui/text/qpfutil.cpp b/src/gui/text/qpfutil.cpp
new file mode 100644
index 0000000000..6fba213c48
--- /dev/null
+++ b/src/gui/text/qpfutil.cpp
@@ -0,0 +1,66 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+static QFontEngineQPF::TagType tagTypes[QFontEngineQPF::NumTags] = {
+ QFontEngineQPF::StringType, // FontName
+ QFontEngineQPF::StringType, // FileName
+ QFontEngineQPF::UInt32Type, // FileIndex
+ QFontEngineQPF::UInt32Type, // FontRevision
+ QFontEngineQPF::StringType, // FreeText
+ QFontEngineQPF::FixedType, // Ascent
+ QFontEngineQPF::FixedType, // Descent
+ QFontEngineQPF::FixedType, // Leading
+ QFontEngineQPF::FixedType, // XHeight
+ QFontEngineQPF::FixedType, // AverageCharWidth
+ QFontEngineQPF::FixedType, // MaxCharWidth
+ QFontEngineQPF::FixedType, // LineThickness
+ QFontEngineQPF::FixedType, // MinLeftBearing
+ QFontEngineQPF::FixedType, // MinRightBearing
+ QFontEngineQPF::FixedType, // UnderlinePosition
+ QFontEngineQPF::UInt8Type, // GlyphFormat
+ QFontEngineQPF::UInt8Type, // PixelSize
+ QFontEngineQPF::UInt8Type, // Weight
+ QFontEngineQPF::UInt8Type, // Style
+ QFontEngineQPF::StringType, // EndOfHeader
+ QFontEngineQPF::BitFieldType// WritingSystems
+};
+
+
diff --git a/src/gui/text/qsyntaxhighlighter.cpp b/src/gui/text/qsyntaxhighlighter.cpp
new file mode 100644
index 0000000000..87648d5177
--- /dev/null
+++ b/src/gui/text/qsyntaxhighlighter.cpp
@@ -0,0 +1,618 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qsyntaxhighlighter.h"
+
+#ifndef QT_NO_SYNTAXHIGHLIGHTER
+#include <private/qobject_p.h>
+#include <qtextdocument.h>
+#include <private/qtextdocument_p.h>
+#include <qtextlayout.h>
+#include <qpointer.h>
+#include <qtextobject.h>
+#include <qtextcursor.h>
+#include <qdebug.h>
+#include <qtextedit.h>
+#include <qtimer.h>
+
+QT_BEGIN_NAMESPACE
+
+class QSyntaxHighlighterPrivate : public QObjectPrivate
+{
+ Q_DECLARE_PUBLIC(QSyntaxHighlighter)
+public:
+ inline QSyntaxHighlighterPrivate() : rehighlightPending(false) {}
+
+ QPointer<QTextDocument> doc;
+
+ void _q_reformatBlocks(int from, int charsRemoved, int charsAdded);
+ void reformatBlock(QTextBlock block);
+
+ inline void _q_delayedRehighlight() {
+ if (!rehighlightPending)
+ return;
+ rehighlightPending = false;
+ q_func()->rehighlight();
+ return;
+ }
+
+ void applyFormatChanges();
+ QVector<QTextCharFormat> formatChanges;
+ QTextBlock currentBlock;
+ bool rehighlightPending;
+};
+
+void QSyntaxHighlighterPrivate::applyFormatChanges()
+{
+ QTextLayout *layout = currentBlock.layout();
+
+ QList<QTextLayout::FormatRange> ranges = layout->additionalFormats();
+
+ const int preeditAreaStart = layout->preeditAreaPosition();
+ const int preeditAreaLength = layout->preeditAreaText().length();
+
+ QList<QTextLayout::FormatRange>::Iterator it = ranges.begin();
+ while (it != ranges.end()) {
+ if (it->start >= preeditAreaStart
+ && it->start + it->length <= preeditAreaStart + preeditAreaLength)
+ ++it;
+ else
+ it = ranges.erase(it);
+ }
+
+ QTextCharFormat emptyFormat;
+
+ QTextLayout::FormatRange r;
+ r.start = r.length = -1;
+
+ int i = 0;
+ while (i < formatChanges.count()) {
+
+ while (i < formatChanges.count() && formatChanges.at(i) == emptyFormat)
+ ++i;
+
+ if (i >= formatChanges.count())
+ break;
+
+ r.start = i;
+ r.format = formatChanges.at(i);
+
+ while (i < formatChanges.count() && formatChanges.at(i) == r.format)
+ ++i;
+
+ if (i >= formatChanges.count())
+ break;
+
+ r.length = i - r.start;
+
+ if (r.start >= preeditAreaStart) {
+ r.start += preeditAreaLength;
+ } else if (r.start + r.length >= preeditAreaStart) {
+ r.length += preeditAreaLength;
+ }
+
+ ranges << r;
+ r.start = r.length = -1;
+ }
+
+ if (r.start != -1) {
+ r.length = formatChanges.count() - r.start;
+
+ if (r.start >= preeditAreaStart) {
+ r.start += preeditAreaLength;
+ } else if (r.start + r.length >= preeditAreaStart) {
+ r.length += preeditAreaLength;
+ }
+
+ ranges << r;
+ }
+
+ layout->setAdditionalFormats(ranges);
+}
+
+void QSyntaxHighlighterPrivate::_q_reformatBlocks(int from, int charsRemoved, int charsAdded)
+{
+ Q_UNUSED(charsRemoved);
+ rehighlightPending = false;
+
+ QTextBlock block = doc->findBlock(from);
+ if (!block.isValid())
+ return;
+
+ int endPosition;
+ QTextBlock lastBlock = doc->findBlock(from + charsAdded);
+ if (lastBlock.isValid())
+ endPosition = lastBlock.position() + lastBlock.length();
+ else
+ endPosition = doc->docHandle()->length();
+
+ bool forceHighlightOfNextBlock = false;
+
+ while (block.isValid() && (block.position() < endPosition || forceHighlightOfNextBlock)) {
+ const int stateBeforeHighlight = block.userState();
+
+ reformatBlock(block);
+
+ forceHighlightOfNextBlock = (block.userState() != stateBeforeHighlight);
+
+ block = block.next();
+ }
+
+ formatChanges.clear();
+}
+
+void QSyntaxHighlighterPrivate::reformatBlock(QTextBlock block)
+{
+ Q_Q(QSyntaxHighlighter);
+
+ Q_ASSERT_X(!currentBlock.isValid(), "QSyntaxHighlighter::reformatBlock()", "reFormatBlock() called recursively");
+
+ currentBlock = block;
+ QTextBlock previous = block.previous();
+
+ formatChanges.fill(QTextCharFormat(), block.length() - 1);
+ q->highlightBlock(block.text());
+ applyFormatChanges();
+
+ doc->markContentsDirty(block.position(), block.length());
+
+ currentBlock = QTextBlock();
+}
+
+/*!
+ \class QSyntaxHighlighter
+ \reentrant
+
+ \brief The QSyntaxHighlighter class allows you to define syntax
+ highlighting rules, and in addition you can use the class to query
+ a document's current formatting or user data.
+
+ \since 4.1
+
+ \ingroup text
+
+ The QSyntaxHighlighter class is a base class for implementing
+ QTextEdit syntax highlighters. A syntax highligher automatically
+ highlights parts of the text in a QTextEdit, or more generally in
+ a QTextDocument. 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, you must subclass
+ QSyntaxHighlighter and reimplement highlightBlock().
+
+ When you create an instance of your QSyntaxHighlighter subclass,
+ pass it the QTextEdit or QTextDocument that you want the syntax
+ highlighting to be applied to. For example:
+
+ \snippet doc/src/snippets/code/src_gui_text_qsyntaxhighlighter.cpp 0
+
+ After this your highlightBlock() function will be called
+ automatically whenever necessary. Use your highlightBlock()
+ function to apply formatting (e.g. setting the font and color) to
+ the text that is passed to it. QSyntaxHighlighter provides the
+ setFormat() function which applies a given QTextCharFormat on
+ the current text block. For example:
+
+ \snippet doc/src/snippets/code/src_gui_text_qsyntaxhighlighter.cpp 1
+
+ Some syntaxes can have constructs that span several text
+ blocks. For example, a C++ syntax highlighter should be able to
+ cope with \c{/}\c{*...*}\c{/} multiline comments. To deal with
+ these cases it is necessary to know the end state of the previous
+ text block (e.g. "in comment").
+
+ Inside your highlightBlock() implementation you can query the end
+ state of the previous text block using the previousBlockState()
+ function. After parsing the block you can save the last state
+ using setCurrentBlockState().
+
+ The currentBlockState() and previousBlockState() functions return
+ an int value. If no state is set, the returned value is -1. You
+ can designate any other value to identify any given state using
+ the setCurrentBlockState() function. Once the state is set the
+ QTextBlock keeps that value until it is set set again or until the
+ corresponding paragraph of text is deleted.
+
+ For example, if you're writing a simple C++ syntax highlighter,
+ you might designate 1 to signify "in comment":
+
+ \snippet doc/src/snippets/code/src_gui_text_qsyntaxhighlighter.cpp 2
+
+ In the example above, we first set the current block state to
+ 0. Then, if the previous block ended within a comment, we higlight
+ from the beginning of the current block (\c {startIndex =
+ 0}). Otherwise, we search for the given start expression. If the
+ specified end expression cannot be found in the text block, we
+ change the current block state by calling setCurrentBlockState(),
+ and make sure that the rest of the block is higlighted.
+
+ In addition you can query the current formatting and user data
+ using the format() and currentBlockUserData() functions
+ respectively. You can also attach user data to the current text
+ block using the setCurrentBlockUserData() function.
+ QTextBlockUserData can be used to store custom settings. In the
+ case of syntax highlighting, it is in particular interesting as
+ cache storage for information that you may figure out while
+ parsing the paragraph's text. For an example, see the
+ setCurrentBlockUserData() documentation.
+
+ \sa QTextEdit, {Syntax Highlighter Example}
+*/
+
+/*!
+ Constructs a QSyntaxHighlighter with the given \a parent.
+*/
+QSyntaxHighlighter::QSyntaxHighlighter(QObject *parent)
+ : QObject(*new QSyntaxHighlighterPrivate, parent)
+{
+}
+
+/*!
+ Constructs a QSyntaxHighlighter and installs it on \a parent.
+ The specified QTextDocument also becomes the owner of the
+ QSyntaxHighlighter.
+*/
+QSyntaxHighlighter::QSyntaxHighlighter(QTextDocument *parent)
+ : QObject(*new QSyntaxHighlighterPrivate, parent)
+{
+ setDocument(parent);
+}
+
+/*!
+ Constructs a QSyntaxHighlighter and installs it on \a parent 's
+ QTextDocument. The specified QTextEdit also becomes the owner of
+ the QSyntaxHighlighter.
+*/
+QSyntaxHighlighter::QSyntaxHighlighter(QTextEdit *parent)
+ : QObject(*new QSyntaxHighlighterPrivate, parent)
+{
+ setDocument(parent->document());
+}
+
+/*!
+ Destructor. Uninstalls this syntax highlighter from the text document.
+*/
+QSyntaxHighlighter::~QSyntaxHighlighter()
+{
+ setDocument(0);
+}
+
+/*!
+ Installs the syntax highlighter on the given QTextDocument \a doc.
+ A QSyntaxHighlighter can only be used with one document at a time.
+*/
+void QSyntaxHighlighter::setDocument(QTextDocument *doc)
+{
+ Q_D(QSyntaxHighlighter);
+ if (d->doc) {
+ disconnect(d->doc, SIGNAL(contentsChange(int,int,int)),
+ this, SLOT(_q_reformatBlocks(int,int,int)));
+
+ QTextCursor cursor(d->doc);
+ cursor.beginEditBlock();
+ for (QTextBlock blk = d->doc->begin(); blk.isValid(); blk = blk.next())
+ blk.layout()->clearAdditionalFormats();
+ cursor.endEditBlock();
+ }
+ d->doc = doc;
+ if (d->doc) {
+ connect(d->doc, SIGNAL(contentsChange(int,int,int)),
+ this, SLOT(_q_reformatBlocks(int,int,int)));
+ QTimer::singleShot(0, this, SLOT(_q_delayedRehighlight()));
+ d->rehighlightPending = true;
+ }
+}
+
+/*!
+ Returns the QTextDocument on which this syntax highlighter is
+ installed.
+*/
+QTextDocument *QSyntaxHighlighter::document() const
+{
+ Q_D(const QSyntaxHighlighter);
+ return d->doc;
+}
+
+/*!
+ \since 4.2
+
+ Redoes the highlighting of the whole document.
+*/
+void QSyntaxHighlighter::rehighlight()
+{
+ Q_D(QSyntaxHighlighter);
+ if (!d->doc)
+ return;
+
+ disconnect(d->doc, SIGNAL(contentsChange(int,int,int)),
+ this, SLOT(_q_reformatBlocks(int,int,int)));
+ QTextCursor cursor(d->doc);
+ cursor.beginEditBlock();
+ cursor.movePosition(QTextCursor::End);
+ d->_q_reformatBlocks(0, 0, cursor.position());
+ cursor.endEditBlock();
+ connect(d->doc, SIGNAL(contentsChange(int,int,int)),
+ this, SLOT(_q_reformatBlocks(int,int,int)));
+}
+
+/*!
+ \fn void QSyntaxHighlighter::highlightBlock(const QString &text)
+
+ Highlights the given text block. This function is called when
+ necessary by the rich text engine, i.e. on text blocks which have
+ changed.
+
+ To provide your own syntax highlighting, you must subclass
+ QSyntaxHighlighter and reimplement highlightBlock(). In your
+ reimplementation you should parse the block's \a text and call
+ setFormat() as often as necessary to apply any font and color
+ changes that you require. For example:
+
+ \snippet doc/src/snippets/code/src_gui_text_qsyntaxhighlighter.cpp 3
+
+ Some syntaxes can have constructs that span several text
+ blocks. For example, a C++ syntax highlighter should be able to
+ cope with \c{/}\c{*...*}\c{/} multiline comments. To deal with
+ these cases it is necessary to know the end state of the previous
+ text block (e.g. "in comment").
+
+ Inside your highlightBlock() implementation you can query the end
+ state of the previous text block using the previousBlockState()
+ function. After parsing the block you can save the last state
+ using setCurrentBlockState().
+
+ The currentBlockState() and previousBlockState() functions return
+ an int value. If no state is set, the returned value is -1. You
+ can designate any other value to identify any given state using
+ the setCurrentBlockState() function. Once the state is set the
+ QTextBlock keeps that value until it is set set again or until the
+ corresponding paragraph of text gets deleted.
+
+ For example, if you're writing a simple C++ syntax highlighter,
+ you might designate 1 to signify "in comment". For a text block
+ that ended in the middle of a comment you'd set 1 using
+ setCurrentBlockState, and for other paragraphs you'd set 0.
+ In your parsing code if the return value of previousBlockState()
+ is 1, you would highlight the text as a C++ comment until you
+ reached the closing \c{*}\c{/}.
+
+ \sa previousBlockState(), setFormat(), setCurrentBlockState()
+*/
+
+/*!
+ This function is applied to the syntax highlighter's current text
+ block (i.e. the text that is passed to the highlightBlock()
+ function).
+
+ The specified \a format is applied to the text from the \a start
+ position for a length of \a count characters (if \a count is 0,
+ nothing is done). The formatting properties set in \a format are
+ merged at display time with the formatting information stored
+ directly in the document, for example as previously set with
+ QTextCursor's functions. Note that the document itself remains
+ unmodified by the format set through this function.
+
+ \sa format(), highlightBlock()
+*/
+void QSyntaxHighlighter::setFormat(int start, int count, const QTextCharFormat &format)
+{
+ Q_D(QSyntaxHighlighter);
+
+ if (start < 0 || start >= d->formatChanges.count())
+ return;
+
+ const int end = qMin(start + count, d->formatChanges.count());
+ for (int i = start; i < end; ++i)
+ d->formatChanges[i] = format;
+}
+
+/*!
+ \overload
+
+ The specified \a color is applied to the current text block from
+ the \a start position for a length of \a count characters.
+
+ The other attributes of the current text block, e.g. the font and
+ background color, are reset to default values.
+
+ \sa format(), highlightBlock()
+*/
+void QSyntaxHighlighter::setFormat(int start, int count, const QColor &color)
+{
+ QTextCharFormat format;
+ format.setForeground(color);
+ setFormat(start, count, format);
+}
+
+/*!
+ \overload
+
+ The specified \a font is applied to the current text block from
+ the \a start position for a length of \a count characters.
+
+ The other attributes of the current text block, e.g. the font and
+ background color, are reset to default values.
+
+ \sa format(), highlightBlock()
+*/
+void QSyntaxHighlighter::setFormat(int start, int count, const QFont &font)
+{
+ QTextCharFormat format;
+ format.setFont(font);
+ setFormat(start, count, format);
+}
+
+/*!
+ \fn QTextCharFormat QSyntaxHighlighter::format(int position) const
+
+ Returns the format at \a position inside the syntax highlighter's
+ current text block.
+*/
+QTextCharFormat QSyntaxHighlighter::format(int pos) const
+{
+ Q_D(const QSyntaxHighlighter);
+ if (pos < 0 || pos >= d->formatChanges.count())
+ return QTextCharFormat();
+ return d->formatChanges.at(pos);
+}
+
+/*!
+ Returns the end state of the text block previous to the
+ syntax highlighter's current block. If no value was
+ previously set, the returned value is -1.
+
+ \sa highlightBlock(), setCurrentBlockState()
+*/
+int QSyntaxHighlighter::previousBlockState() const
+{
+ Q_D(const QSyntaxHighlighter);
+ if (!d->currentBlock.isValid())
+ return -1;
+
+ const QTextBlock previous = d->currentBlock.previous();
+ if (!previous.isValid())
+ return -1;
+
+ return previous.userState();
+}
+
+/*!
+ Returns the state of the current text block. If no value is set,
+ the returned value is -1.
+*/
+int QSyntaxHighlighter::currentBlockState() const
+{
+ Q_D(const QSyntaxHighlighter);
+ if (!d->currentBlock.isValid())
+ return -1;
+
+ return d->currentBlock.userState();
+}
+
+/*!
+ Sets the state of the current text block to \a newState.
+
+ \sa highlightBlock()
+*/
+void QSyntaxHighlighter::setCurrentBlockState(int newState)
+{
+ Q_D(QSyntaxHighlighter);
+ if (!d->currentBlock.isValid())
+ return;
+
+ d->currentBlock.setUserState(newState);
+}
+
+/*!
+ Attaches the given \a data to the current text block. The
+ ownership is passed to the underlying text document, i.e. the
+ provided QTextBlockUserData object will be deleted if the
+ corresponding text block gets deleted.
+
+ QTextBlockUserData can be used to store custom settings. In the
+ case of syntax highlighting, it is in particular interesting as
+ cache storage for information that you may figure out while
+ parsing the paragraph's text.
+
+ For example while parsing the text, you can keep track of
+ parenthesis characters that you encounter ('{[(' and the like),
+ and store their relative position and the actual QChar in a simple
+ class derived from QTextBlockUserData:
+
+ \snippet doc/src/snippets/code/src_gui_text_qsyntaxhighlighter.cpp 4
+
+ During cursor navigation in the associated editor, you can ask the
+ current QTextBlock (retrieved using the QTextCursor::block()
+ function) if it has a user data object set and cast it to your \c
+ BlockData object. Then you can check if the current cursor
+ position matches with a previously recorded parenthesis position,
+ and, depending on the type of parenthesis (opening or closing),
+ find the next opening or closing parenthesis on the same level.
+
+ In this way you can do a visual parenthesis matching and highlight
+ from the current cursor position to the matching parenthesis. That
+ makes it easier to spot a missing parenthesis in your code and to
+ find where a corresponding opening/closing parenthesis is when
+ editing parenthesis intensive code.
+
+ \sa QTextBlock::setUserData()
+*/
+void QSyntaxHighlighter::setCurrentBlockUserData(QTextBlockUserData *data)
+{
+ Q_D(QSyntaxHighlighter);
+ if (!d->currentBlock.isValid())
+ return;
+
+ d->currentBlock.setUserData(data);
+}
+
+/*!
+ Returns the QTextBlockUserData object previously attached to the
+ current text block.
+
+ \sa QTextBlock::userData(), setCurrentBlockUserData()
+*/
+QTextBlockUserData *QSyntaxHighlighter::currentBlockUserData() const
+{
+ Q_D(const QSyntaxHighlighter);
+ if (!d->currentBlock.isValid())
+ return 0;
+
+ return d->currentBlock.userData();
+}
+
+/*!
+ \since 4.4
+
+ Returns the current text block.
+ */
+QTextBlock QSyntaxHighlighter::currentBlock() const
+{
+ Q_D(const QSyntaxHighlighter);
+ return d->currentBlock;
+}
+
+QT_END_NAMESPACE
+
+#include "moc_qsyntaxhighlighter.cpp"
+
+#endif // QT_NO_SYNTAXHIGHLIGHTER
diff --git a/src/gui/text/qsyntaxhighlighter.h b/src/gui/text/qsyntaxhighlighter.h
new file mode 100644
index 0000000000..481dfd4f77
--- /dev/null
+++ b/src/gui/text/qsyntaxhighlighter.h
@@ -0,0 +1,111 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSYNTAXHIGHLIGHTER_H
+#define QSYNTAXHIGHLIGHTER_H
+
+#include <QtCore/qglobal.h>
+
+#ifndef QT_NO_SYNTAXHIGHLIGHTER
+
+#include <QtCore/qobject.h>
+#include <QtGui/qtextobject.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+class QTextDocument;
+class QSyntaxHighlighterPrivate;
+class QTextCharFormat;
+class QFont;
+class QColor;
+class QTextBlockUserData;
+class QTextEdit;
+
+class Q_GUI_EXPORT QSyntaxHighlighter : public QObject
+{
+ Q_OBJECT
+ Q_DECLARE_PRIVATE(QSyntaxHighlighter)
+public:
+ QSyntaxHighlighter(QObject *parent);
+ QSyntaxHighlighter(QTextDocument *parent);
+ QSyntaxHighlighter(QTextEdit *parent);
+ virtual ~QSyntaxHighlighter();
+
+ void setDocument(QTextDocument *doc);
+ QTextDocument *document() const;
+
+public Q_SLOTS:
+ void rehighlight();
+
+protected:
+ virtual void highlightBlock(const QString &text) = 0;
+
+ void setFormat(int start, int count, const QTextCharFormat &format);
+ void setFormat(int start, int count, const QColor &color);
+ void setFormat(int start, int count, const QFont &font);
+ QTextCharFormat format(int pos) const;
+
+ int previousBlockState() const;
+ int currentBlockState() const;
+ void setCurrentBlockState(int newState);
+
+ void setCurrentBlockUserData(QTextBlockUserData *data);
+ QTextBlockUserData *currentBlockUserData() const;
+
+ QTextBlock currentBlock() const;
+
+private:
+ Q_DISABLE_COPY(QSyntaxHighlighter)
+ Q_PRIVATE_SLOT(d_func(), void _q_reformatBlocks(int from, int charsRemoved, int charsAdded))
+ Q_PRIVATE_SLOT(d_func(), void _q_delayedRehighlight())
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QT_NO_SYNTAXHIGHLIGHTER
+
+#endif // QSYNTAXHIGHLIGHTER_H
diff --git a/src/gui/text/qtextcontrol.cpp b/src/gui/text/qtextcontrol.cpp
new file mode 100644
index 0000000000..f3d025c77a
--- /dev/null
+++ b/src/gui/text/qtextcontrol.cpp
@@ -0,0 +1,2981 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qtextcontrol_p.h"
+#include "qtextcontrol_p_p.h"
+
+#ifndef QT_NO_TEXTCONTROL
+
+#include <qfont.h>
+#include <qpainter.h>
+#include <qevent.h>
+#include <qdebug.h>
+#include <qmime.h>
+#include <qdrag.h>
+#include <qclipboard.h>
+#include <qmenu.h>
+#include <qstyle.h>
+#include <qtimer.h>
+#include "private/qtextdocumentlayout_p.h"
+#include "private/qtextedit_p.h"
+#include "qtextdocument.h"
+#include "private/qtextdocument_p.h"
+#include "qtextlist.h"
+#include "private/qtextcontrol_p.h"
+#include "qgraphicssceneevent.h"
+#include "qprinter.h"
+#include "qtextdocumentwriter.h"
+
+#include <qtextformat.h>
+#include <qdatetime.h>
+#include <qbuffer.h>
+#include <qapplication.h>
+#include <limits.h>
+#include <qtexttable.h>
+#include <qvariant.h>
+#include <qurl.h>
+#include <qdesktopservices.h>
+#include <qinputcontext.h>
+#include <qtooltip.h>
+#include <qstyleoption.h>
+#include <QtGui/qlineedit.h>
+
+#ifndef QT_NO_SHORTCUT
+#include "private/qapplication_p.h"
+#include "private/qshortcutmap_p.h"
+#include <qkeysequence.h>
+#define ACCEL_KEY(k) (!qApp->d_func()->shortcutMap.hasShortcutForKeySequence(k) ? QLatin1String("\t") + QString(QKeySequence(k)) : QString())
+#else
+#define ACCEL_KEY(k) QString()
+#endif
+
+QT_BEGIN_NAMESPACE
+
+#ifndef QT_NO_CONTEXTMENU
+#if defined(Q_WS_WIN)
+extern bool qt_use_rtl_extensions;
+#endif
+#endif
+
+// could go into QTextCursor...
+static QTextLine currentTextLine(const QTextCursor &cursor)
+{
+ const QTextBlock block = cursor.block();
+ if (!block.isValid())
+ return QTextLine();
+
+ const QTextLayout *layout = block.layout();
+ if (!layout)
+ return QTextLine();
+
+ const int relativePos = cursor.position() - block.position();
+ return layout->lineForTextPosition(relativePos);
+}
+
+QTextControlPrivate::QTextControlPrivate()
+ : doc(0), cursorOn(false), cursorIsFocusIndicator(false),
+ interactionFlags(Qt::TextEditorInteraction),
+#ifndef QT_NO_DRAGANDDROP
+ mousePressed(false), mightStartDrag(false),
+#endif
+ lastSelectionState(false), ignoreAutomaticScrollbarAdjustement(false),
+ overwriteMode(false),
+ acceptRichText(true),
+ preeditCursor(0), hideCursor(false),
+ hasFocus(false),
+#ifdef QT_KEYPAD_NAVIGATION
+ hasEditFocus(false),
+#endif
+ isEnabled(true),
+ hadSelectionOnMousePress(false),
+ openExternalLinks(false)
+{}
+
+bool QTextControlPrivate::cursorMoveKeyEvent(QKeyEvent *e)
+{
+#ifdef QT_NO_SHORTCUT
+ Q_UNUSED(e);
+#endif
+
+ Q_Q(QTextControl);
+ if (cursor.isNull())
+ return false;
+
+ const QTextCursor oldSelection = cursor;
+ const int oldCursorPos = cursor.position();
+
+ QTextCursor::MoveMode mode = QTextCursor::MoveAnchor;
+ QTextCursor::MoveOperation op = QTextCursor::NoMove;
+
+ if (false) {
+ }
+#ifndef QT_NO_SHORTCUT
+ if (e == QKeySequence::MoveToNextChar) {
+ op = QTextCursor::Right;
+ }
+ else if (e == QKeySequence::MoveToPreviousChar) {
+ op = QTextCursor::Left;
+ }
+ else if (e == QKeySequence::SelectNextChar) {
+ op = QTextCursor::Right;
+ mode = QTextCursor::KeepAnchor;
+ }
+ else if (e == QKeySequence::SelectPreviousChar) {
+ op = QTextCursor::Left;
+ mode = QTextCursor::KeepAnchor;
+ }
+ else if (e == QKeySequence::SelectNextWord) {
+ op = QTextCursor::WordRight;
+ mode = QTextCursor::KeepAnchor;
+ }
+ else if (e == QKeySequence::SelectPreviousWord) {
+ op = QTextCursor::WordLeft;
+ mode = QTextCursor::KeepAnchor;
+ }
+ else if (e == QKeySequence::SelectStartOfLine) {
+ op = QTextCursor::StartOfLine;
+ mode = QTextCursor::KeepAnchor;
+ }
+ else if (e == QKeySequence::SelectEndOfLine) {
+ op = QTextCursor::EndOfLine;
+ mode = QTextCursor::KeepAnchor;
+ }
+ else if (e == QKeySequence::SelectStartOfBlock) {
+ op = QTextCursor::StartOfBlock;
+ mode = QTextCursor::KeepAnchor;
+ }
+ else if (e == QKeySequence::SelectEndOfBlock) {
+ op = QTextCursor::EndOfBlock;
+ mode = QTextCursor::KeepAnchor;
+ }
+ else if (e == QKeySequence::SelectStartOfDocument) {
+ op = QTextCursor::Start;
+ mode = QTextCursor::KeepAnchor;
+ }
+ else if (e == QKeySequence::SelectEndOfDocument) {
+ op = QTextCursor::End;
+ mode = QTextCursor::KeepAnchor;
+ }
+ else if (e == QKeySequence::SelectPreviousLine) {
+ op = QTextCursor::Up;
+ mode = QTextCursor::KeepAnchor;
+ }
+ else if (e == QKeySequence::SelectNextLine) {
+ op = QTextCursor::Down;
+ mode = QTextCursor::KeepAnchor;
+ {
+ QTextBlock block = cursor.block();
+ QTextLine line = currentTextLine(cursor);
+ if (!block.next().isValid()
+ && line.isValid()
+ && line.lineNumber() == block.layout()->lineCount() - 1)
+ op = QTextCursor::End;
+ }
+ }
+ else if (e == QKeySequence::MoveToNextWord) {
+ op = QTextCursor::WordRight;
+ }
+ else if (e == QKeySequence::MoveToPreviousWord) {
+ op = QTextCursor::WordLeft;
+ }
+ else if (e == QKeySequence::MoveToEndOfBlock) {
+ op = QTextCursor::EndOfBlock;
+ }
+ else if (e == QKeySequence::MoveToStartOfBlock) {
+ op = QTextCursor::StartOfBlock;
+ }
+ else if (e == QKeySequence::MoveToNextLine) {
+ op = QTextCursor::Down;
+ }
+ else if (e == QKeySequence::MoveToPreviousLine) {
+ op = QTextCursor::Up;
+ }
+ else if (e == QKeySequence::MoveToPreviousLine) {
+ op = QTextCursor::Up;
+ }
+ else if (e == QKeySequence::MoveToStartOfLine) {
+ op = QTextCursor::StartOfLine;
+ }
+ else if (e == QKeySequence::MoveToEndOfLine) {
+ op = QTextCursor::EndOfLine;
+ }
+ else if (e == QKeySequence::MoveToStartOfDocument) {
+ op = QTextCursor::Start;
+ }
+ else if (e == QKeySequence::MoveToEndOfDocument) {
+ op = QTextCursor::End;
+ }
+#endif // QT_NO_SHORTCUT
+ else {
+ return false;
+ }
+
+// Except for pageup and pagedown, Mac OS X has very different behavior, we don't do it all, but
+// here's the breakdown:
+// Shift still works as an anchor, but only one of the other keys can be down Ctrl (Command),
+// Alt (Option), or Meta (Control).
+// Command/Control + Left/Right -- Move to left or right of the line
+// + Up/Down -- Move to top bottom of the file. (Control doesn't move the cursor)
+// Option + Left/Right -- Move one word Left/right.
+// + Up/Down -- Begin/End of Paragraph.
+// Home/End Top/Bottom of file. (usually don't move the cursor, but will select)
+
+ bool visualNavigation = cursor.visualNavigation();
+ cursor.setVisualNavigation(true);
+ const bool moved = cursor.movePosition(op, mode);
+ cursor.setVisualNavigation(visualNavigation);
+ q->ensureCursorVisible();
+
+ if (moved) {
+ if (cursor.position() != oldCursorPos)
+ emit q->cursorPositionChanged();
+ emit q->microFocusChanged();
+ }
+#ifdef QT_KEYPAD_NAVIGATION
+ else if (QApplication::keypadNavigationEnabled()
+ && (e->key() == Qt::Key_Up || e->key() == Qt::Key_Down)) {
+ return false;
+ }
+#endif
+
+ selectionChanged(/*forceEmitSelectionChanged =*/(mode == QTextCursor::KeepAnchor));
+
+ repaintOldAndNewSelection(oldSelection);
+
+ return true;
+}
+
+void QTextControlPrivate::updateCurrentCharFormat()
+{
+ Q_Q(QTextControl);
+
+ QTextCharFormat fmt = cursor.charFormat();
+ if (fmt == lastCharFormat)
+ return;
+ lastCharFormat = fmt;
+
+ emit q->currentCharFormatChanged(fmt);
+ emit q->microFocusChanged();
+}
+
+void QTextControlPrivate::indent()
+{
+ QTextBlockFormat blockFmt = cursor.blockFormat();
+
+ QTextList *list = cursor.currentList();
+ if (!list) {
+ QTextBlockFormat modifier;
+ modifier.setIndent(blockFmt.indent() + 1);
+ cursor.mergeBlockFormat(modifier);
+ } else {
+ QTextListFormat format = list->format();
+ format.setIndent(format.indent() + 1);
+
+ if (list->itemNumber(cursor.block()) == 1)
+ list->setFormat(format);
+ else
+ cursor.createList(format);
+ }
+}
+
+void QTextControlPrivate::outdent()
+{
+ QTextBlockFormat blockFmt = cursor.blockFormat();
+
+ QTextList *list = cursor.currentList();
+
+ if (!list) {
+ QTextBlockFormat modifier;
+ modifier.setIndent(blockFmt.indent() - 1);
+ cursor.mergeBlockFormat(modifier);
+ } else {
+ QTextListFormat listFmt = list->format();
+ listFmt.setIndent(listFmt.indent() - 1);
+ list->setFormat(listFmt);
+ }
+}
+
+void QTextControlPrivate::gotoNextTableCell()
+{
+ QTextTable *table = cursor.currentTable();
+ QTextTableCell cell = table->cellAt(cursor);
+
+ int newColumn = cell.column() + cell.columnSpan();
+ int newRow = cell.row();
+
+ if (newColumn >= table->columns()) {
+ newColumn = 0;
+ ++newRow;
+ if (newRow >= table->rows())
+ table->insertRows(table->rows(), 1);
+ }
+
+ cell = table->cellAt(newRow, newColumn);
+ cursor = cell.firstCursorPosition();
+}
+
+void QTextControlPrivate::gotoPreviousTableCell()
+{
+ QTextTable *table = cursor.currentTable();
+ QTextTableCell cell = table->cellAt(cursor);
+
+ int newColumn = cell.column() - 1;
+ int newRow = cell.row();
+
+ if (newColumn < 0) {
+ newColumn = table->columns() - 1;
+ --newRow;
+ if (newRow < 0)
+ return;
+ }
+
+ cell = table->cellAt(newRow, newColumn);
+ cursor = cell.firstCursorPosition();
+}
+
+void QTextControlPrivate::createAutoBulletList()
+{
+ cursor.beginEditBlock();
+
+ QTextBlockFormat blockFmt = cursor.blockFormat();
+
+ QTextListFormat listFmt;
+ listFmt.setStyle(QTextListFormat::ListDisc);
+ listFmt.setIndent(blockFmt.indent() + 1);
+
+ blockFmt.setIndent(0);
+ cursor.setBlockFormat(blockFmt);
+
+ cursor.createList(listFmt);
+
+ cursor.endEditBlock();
+}
+
+void QTextControlPrivate::init(Qt::TextFormat format, const QString &text, QTextDocument *document)
+{
+ Q_Q(QTextControl);
+ setContent(format, text, document);
+
+ QWidget *parentWidget = qobject_cast<QWidget*>(q->parent());
+ if (parentWidget) {
+ QTextOption opt = doc->defaultTextOption();
+ opt.setTextDirection(parentWidget->layoutDirection());
+ doc->setDefaultTextOption(opt);
+ }
+ doc->setUndoRedoEnabled(interactionFlags & Qt::TextEditable);
+ q->setCursorWidth(-1);
+}
+
+void QTextControlPrivate::setContent(Qt::TextFormat format, const QString &text, QTextDocument *document)
+{
+ Q_Q(QTextControl);
+
+ // for use when called from setPlainText. we may want to re-use the currently
+ // set char format then.
+ const QTextCharFormat charFormatForInsertion = cursor.charFormat();
+
+ bool clearDocument = true;
+ if (!doc) {
+ if (document) {
+ doc = document;
+ clearDocument = false;
+ } else {
+ palette = QApplication::palette("QTextControl");
+ doc = new QTextDocument(q);
+ }
+ _q_documentLayoutChanged();
+ cursor = QTextCursor(doc);
+
+// #### doc->documentLayout()->setPaintDevice(viewport);
+
+ QObject::connect(doc, SIGNAL(contentsChanged()), q, SLOT(_q_updateCurrentCharFormatAndSelection()));
+ QObject::connect(doc, SIGNAL(cursorPositionChanged(QTextCursor)), q, SLOT(_q_emitCursorPosChanged(QTextCursor)));
+ QObject::connect(doc, SIGNAL(documentLayoutChanged()), q, SLOT(_q_documentLayoutChanged()));
+
+ // convenience signal forwards
+ QObject::connect(doc, SIGNAL(contentsChanged()), q, SIGNAL(textChanged()));
+ QObject::connect(doc, SIGNAL(undoAvailable(bool)), q, SIGNAL(undoAvailable(bool)));
+ QObject::connect(doc, SIGNAL(redoAvailable(bool)), q, SIGNAL(redoAvailable(bool)));
+ QObject::connect(doc, SIGNAL(modificationChanged(bool)), q, SIGNAL(modificationChanged(bool)));
+ QObject::connect(doc, SIGNAL(blockCountChanged(int)), q, SIGNAL(blockCountChanged(int)));
+ }
+
+ bool previousUndoRedoState = doc->isUndoRedoEnabled();
+ if (!document)
+ doc->setUndoRedoEnabled(false);
+
+ // avoid multiple textChanged() signals being emitted
+ QObject::disconnect(doc, SIGNAL(contentsChanged()), q, SIGNAL(textChanged()));
+
+ if (!text.isEmpty()) {
+ // clear 'our' cursor for insertion to prevent
+ // the emission of the cursorPositionChanged() signal.
+ // instead we emit it only once at the end instead of
+ // at the end of the document after loading and when
+ // positioning the cursor again to the start of the
+ // document.
+ cursor = QTextCursor();
+ if (format == Qt::PlainText) {
+ QTextCursor formatCursor(doc);
+ // put the setPlainText and the setCharFormat into one edit block,
+ // so that the syntax highlight triggers only /once/ for the entire
+ // document, not twice.
+ formatCursor.beginEditBlock();
+ doc->setPlainText(text);
+ doc->setUndoRedoEnabled(false);
+ formatCursor.select(QTextCursor::Document);
+ formatCursor.setCharFormat(charFormatForInsertion);
+ formatCursor.endEditBlock();
+ } else {
+#ifndef QT_NO_TEXTHTMLPARSER
+ doc->setHtml(text);
+#else
+ doc->setPlainText(text);
+#endif
+ doc->setUndoRedoEnabled(false);
+ }
+ cursor = QTextCursor(doc);
+ } else if (clearDocument) {
+ doc->clear();
+ }
+ cursor.setCharFormat(charFormatForInsertion);
+
+ QObject::connect(doc, SIGNAL(contentsChanged()), q, SIGNAL(textChanged()));
+ emit q->textChanged();
+ if (!document)
+ doc->setUndoRedoEnabled(previousUndoRedoState);
+ _q_updateCurrentCharFormatAndSelection();
+ if (!document)
+ doc->setModified(false);
+
+ q->ensureCursorVisible();
+ emit q->cursorPositionChanged();
+}
+
+void QTextControlPrivate::startDrag()
+{
+#ifndef QT_NO_DRAGANDDROP
+ Q_Q(QTextControl);
+ mousePressed = false;
+ if (!contextWidget)
+ return;
+ QMimeData *data = q->createMimeDataFromSelection();
+
+ QDrag *drag = new QDrag(contextWidget);
+ drag->setMimeData(data);
+
+ Qt::DropActions actions = Qt::CopyAction;
+ if (interactionFlags & Qt::TextEditable)
+ actions |= Qt::MoveAction;
+ Qt::DropAction action = drag->exec(actions, Qt::MoveAction);
+
+ if (action == Qt::MoveAction && drag->target() != contextWidget)
+ cursor.removeSelectedText();
+#endif
+}
+
+void QTextControlPrivate::setCursorPosition(const QPointF &pos)
+{
+ Q_Q(QTextControl);
+ const int cursorPos = q->hitTest(pos, Qt::FuzzyHit);
+ if (cursorPos == -1)
+ return;
+ cursor.setPosition(cursorPos);
+}
+
+void QTextControlPrivate::setCursorPosition(int pos, QTextCursor::MoveMode mode)
+{
+ cursor.setPosition(pos, mode);
+
+ if (mode != QTextCursor::KeepAnchor) {
+ selectedWordOnDoubleClick = QTextCursor();
+ selectedBlockOnTrippleClick = QTextCursor();
+ }
+}
+
+void QTextControlPrivate::repaintCursor()
+{
+ Q_Q(QTextControl);
+ emit q->updateRequest(cursorRectPlusUnicodeDirectionMarkers(cursor));
+}
+
+void QTextControlPrivate::repaintOldAndNewSelection(const QTextCursor &oldSelection)
+{
+ Q_Q(QTextControl);
+ if (cursor.hasSelection()
+ && oldSelection.hasSelection()
+ && cursor.currentFrame() == oldSelection.currentFrame()
+ && !cursor.hasComplexSelection()
+ && !oldSelection.hasComplexSelection()
+ && cursor.anchor() == oldSelection.anchor()
+ ) {
+ QTextCursor differenceSelection(doc);
+ differenceSelection.setPosition(oldSelection.position());
+ differenceSelection.setPosition(cursor.position(), QTextCursor::KeepAnchor);
+ emit q->updateRequest(q->selectionRect(differenceSelection));
+ } else {
+ if (!oldSelection.isNull())
+ emit q->updateRequest(q->selectionRect(oldSelection) | cursorRectPlusUnicodeDirectionMarkers(oldSelection));
+ emit q->updateRequest(q->selectionRect() | cursorRectPlusUnicodeDirectionMarkers(cursor));
+ }
+}
+
+void QTextControlPrivate::selectionChanged(bool forceEmitSelectionChanged /*=false*/)
+{
+ Q_Q(QTextControl);
+ if (forceEmitSelectionChanged)
+ emit q->selectionChanged();
+
+ bool current = cursor.hasSelection();
+ if (current == lastSelectionState)
+ return;
+
+ lastSelectionState = current;
+ emit q->copyAvailable(current);
+ if (!forceEmitSelectionChanged)
+ emit q->selectionChanged();
+ emit q->microFocusChanged();
+}
+
+void QTextControlPrivate::_q_updateCurrentCharFormatAndSelection()
+{
+ updateCurrentCharFormat();
+ selectionChanged();
+}
+
+#ifndef QT_NO_CLIPBOARD
+void QTextControlPrivate::setClipboardSelection()
+{
+ QClipboard *clipboard = QApplication::clipboard();
+ if (!cursor.hasSelection() || !clipboard->supportsSelection())
+ return;
+ Q_Q(QTextControl);
+ QMimeData *data = q->createMimeDataFromSelection();
+ clipboard->setMimeData(data, QClipboard::Selection);
+}
+#endif
+
+void QTextControlPrivate::_q_emitCursorPosChanged(const QTextCursor &someCursor)
+{
+ Q_Q(QTextControl);
+ if (someCursor.isCopyOf(cursor)) {
+ emit q->cursorPositionChanged();
+ emit q->microFocusChanged();
+ }
+}
+
+void QTextControlPrivate::_q_documentLayoutChanged()
+{
+ Q_Q(QTextControl);
+ QAbstractTextDocumentLayout *layout = doc->documentLayout();
+ QObject::connect(layout, SIGNAL(update(QRectF)), q, SIGNAL(updateRequest(QRectF)));
+ QObject::connect(layout, SIGNAL(updateBlock(QTextBlock)), q, SLOT(_q_updateBlock(QTextBlock)));
+ QObject::connect(layout, SIGNAL(documentSizeChanged(QSizeF)), q, SIGNAL(documentSizeChanged(QSizeF)));
+
+}
+
+void QTextControlPrivate::setBlinkingCursorEnabled(bool enable)
+{
+ Q_Q(QTextControl);
+
+ if (enable && QApplication::cursorFlashTime() > 0)
+ cursorBlinkTimer.start(QApplication::cursorFlashTime() / 2, q);
+ else
+ cursorBlinkTimer.stop();
+
+ cursorOn = enable;
+
+ repaintCursor();
+}
+
+void QTextControlPrivate::extendWordwiseSelection(int suggestedNewPosition, qreal mouseXPosition)
+{
+ Q_Q(QTextControl);
+
+ // if inside the initial selected word keep that
+ if (suggestedNewPosition >= selectedWordOnDoubleClick.selectionStart()
+ && suggestedNewPosition <= selectedWordOnDoubleClick.selectionEnd()) {
+ q->setTextCursor(selectedWordOnDoubleClick);
+ return;
+ }
+
+ QTextCursor curs = selectedWordOnDoubleClick;
+ curs.setPosition(suggestedNewPosition, QTextCursor::KeepAnchor);
+
+ if (!curs.movePosition(QTextCursor::StartOfWord))
+ return;
+ const int wordStartPos = curs.position();
+
+ const int blockPos = curs.block().position();
+ const QPointF blockCoordinates = q->blockBoundingRect(curs.block()).topLeft();
+
+ QTextLine line = currentTextLine(curs);
+ if (!line.isValid())
+ return;
+
+ const qreal wordStartX = line.cursorToX(curs.position() - blockPos) + blockCoordinates.x();
+
+ if (!curs.movePosition(QTextCursor::EndOfWord))
+ return;
+ const int wordEndPos = curs.position();
+
+ const QTextLine otherLine = currentTextLine(curs);
+ if (otherLine.textStart() != line.textStart()
+ || wordEndPos == wordStartPos)
+ return;
+
+ const qreal wordEndX = line.cursorToX(curs.position() - blockPos) + blockCoordinates.x();
+
+ if (mouseXPosition < wordStartX || mouseXPosition > wordEndX)
+ return;
+
+ // keep the already selected word even when moving to the left
+ // (#39164)
+ if (suggestedNewPosition < selectedWordOnDoubleClick.position())
+ cursor.setPosition(selectedWordOnDoubleClick.selectionEnd());
+ else
+ cursor.setPosition(selectedWordOnDoubleClick.selectionStart());
+
+ const qreal differenceToStart = mouseXPosition - wordStartX;
+ const qreal differenceToEnd = wordEndX - mouseXPosition;
+
+ if (differenceToStart < differenceToEnd)
+ setCursorPosition(wordStartPos, QTextCursor::KeepAnchor);
+ else
+ setCursorPosition(wordEndPos, QTextCursor::KeepAnchor);
+
+ if (interactionFlags & Qt::TextSelectableByMouse) {
+#ifndef QT_NO_CLIPBOARD
+ setClipboardSelection();
+#endif
+ selectionChanged(true);
+ }
+}
+
+void QTextControlPrivate::extendBlockwiseSelection(int suggestedNewPosition)
+{
+ Q_Q(QTextControl);
+
+ // if inside the initial selected line keep that
+ if (suggestedNewPosition >= selectedBlockOnTrippleClick.selectionStart()
+ && suggestedNewPosition <= selectedBlockOnTrippleClick.selectionEnd()) {
+ q->setTextCursor(selectedBlockOnTrippleClick);
+ return;
+ }
+
+ if (suggestedNewPosition < selectedBlockOnTrippleClick.position()) {
+ cursor.setPosition(selectedBlockOnTrippleClick.selectionEnd());
+ cursor.setPosition(suggestedNewPosition, QTextCursor::KeepAnchor);
+ cursor.movePosition(QTextCursor::StartOfBlock, QTextCursor::KeepAnchor);
+ } else {
+ cursor.setPosition(selectedBlockOnTrippleClick.selectionStart());
+ cursor.setPosition(suggestedNewPosition, QTextCursor::KeepAnchor);
+ cursor.movePosition(QTextCursor::EndOfBlock, QTextCursor::KeepAnchor);
+ cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor);
+ }
+
+ if (interactionFlags & Qt::TextSelectableByMouse) {
+#ifndef QT_NO_CLIPBOARD
+ setClipboardSelection();
+#endif
+ selectionChanged(true);
+ }
+}
+
+void QTextControlPrivate::_q_deleteSelected()
+{
+ if (!(interactionFlags & Qt::TextEditable) || !cursor.hasSelection())
+ return;
+ cursor.removeSelectedText();
+}
+
+void QTextControl::undo()
+{
+ Q_D(QTextControl);
+ d->repaintSelection();
+ d->doc->undo(&d->cursor);
+ ensureCursorVisible();
+}
+
+void QTextControl::redo()
+{
+ Q_D(QTextControl);
+ d->repaintSelection();
+ d->doc->redo(&d->cursor);
+ ensureCursorVisible();
+}
+
+QTextControl::QTextControl(QObject *parent)
+ : QObject(*new QTextControlPrivate, parent)
+{
+ Q_D(QTextControl);
+ d->init();
+}
+
+QTextControl::QTextControl(const QString &text, QObject *parent)
+ : QObject(*new QTextControlPrivate, parent)
+{
+ Q_D(QTextControl);
+ d->init(Qt::RichText, text);
+}
+
+QTextControl::QTextControl(QTextDocument *doc, QObject *parent)
+ : QObject(*new QTextControlPrivate, parent)
+{
+ Q_D(QTextControl);
+ d->init(Qt::RichText, QString(), doc);
+}
+
+QTextControl::~QTextControl()
+{
+}
+
+void QTextControl::setDocument(QTextDocument *document)
+{
+ Q_D(QTextControl);
+ if (d->doc == document)
+ return;
+
+ d->doc->disconnect(this);
+ d->doc->documentLayout()->disconnect(this);
+ d->doc->documentLayout()->setPaintDevice(0);
+
+ if (d->doc->parent() == this)
+ delete d->doc;
+
+ d->doc = 0;
+ d->setContent(Qt::RichText, QString(), document);
+}
+
+QTextDocument *QTextControl::document() const
+{
+ Q_D(const QTextControl);
+ return d->doc;
+}
+
+void QTextControl::setTextCursor(const QTextCursor &cursor)
+{
+ Q_D(QTextControl);
+ d->cursorIsFocusIndicator = false;
+ const bool posChanged = cursor.position() != d->cursor.position();
+ const QTextCursor oldSelection = d->cursor;
+ d->cursor = cursor;
+ d->cursorOn = d->hasFocus && (d->interactionFlags & Qt::TextEditable);
+ d->_q_updateCurrentCharFormatAndSelection();
+ ensureCursorVisible();
+ d->repaintOldAndNewSelection(oldSelection);
+ if (posChanged)
+ emit cursorPositionChanged();
+}
+
+QTextCursor QTextControl::textCursor() const
+{
+ Q_D(const QTextControl);
+ return d->cursor;
+}
+
+#ifndef QT_NO_CLIPBOARD
+
+void QTextControl::cut()
+{
+ Q_D(QTextControl);
+ if (!(d->interactionFlags & Qt::TextEditable) || !d->cursor.hasSelection())
+ return;
+ copy();
+ d->cursor.removeSelectedText();
+}
+
+void QTextControl::copy()
+{
+ Q_D(QTextControl);
+ if (!d->cursor.hasSelection())
+ return;
+ QMimeData *data = createMimeDataFromSelection();
+ QApplication::clipboard()->setMimeData(data);
+}
+
+void QTextControl::paste()
+{
+ const QMimeData *md = QApplication::clipboard()->mimeData();
+ if (md)
+ insertFromMimeData(md);
+}
+#endif
+
+void QTextControl::clear()
+{
+ Q_D(QTextControl);
+ // clears and sets empty content
+ d->extraSelections.clear();
+ d->setContent();
+}
+
+
+void QTextControl::selectAll()
+{
+ Q_D(QTextControl);
+ const int selectionLength = qAbs(d->cursor.position() - d->cursor.anchor());
+ d->cursor.select(QTextCursor::Document);
+ d->selectionChanged(selectionLength != qAbs(d->cursor.position() - d->cursor.anchor()));
+ d->cursorIsFocusIndicator = false;
+ emit updateRequest();
+}
+
+void QTextControl::processEvent(QEvent *e, const QPointF &coordinateOffset, QWidget *contextWidget)
+{
+ QMatrix m;
+ m.translate(coordinateOffset.x(), coordinateOffset.y());
+ processEvent(e, m, contextWidget);
+}
+
+void QTextControl::processEvent(QEvent *e, const QMatrix &matrix, QWidget *contextWidget)
+{
+ Q_D(QTextControl);
+ if (d->interactionFlags & Qt::NoTextInteraction)
+ return;
+
+ d->contextWidget = contextWidget;
+
+ if (!d->contextWidget) {
+ switch (e->type()) {
+#ifndef QT_NO_GRAPHICSVIEW
+ case QEvent::GraphicsSceneMouseMove:
+ case QEvent::GraphicsSceneMousePress:
+ case QEvent::GraphicsSceneMouseRelease:
+ case QEvent::GraphicsSceneMouseDoubleClick:
+ case QEvent::GraphicsSceneContextMenu:
+ case QEvent::GraphicsSceneHoverEnter:
+ case QEvent::GraphicsSceneHoverMove:
+ case QEvent::GraphicsSceneHoverLeave:
+ case QEvent::GraphicsSceneHelp:
+ case QEvent::GraphicsSceneDragEnter:
+ case QEvent::GraphicsSceneDragMove:
+ case QEvent::GraphicsSceneDragLeave:
+ case QEvent::GraphicsSceneDrop: {
+ QGraphicsSceneEvent *ev = static_cast<QGraphicsSceneEvent *>(e);
+ d->contextWidget = ev->widget();
+ break;
+ }
+#endif // QT_NO_GRAPHICSVIEW
+ default: break;
+ };
+ }
+
+ switch (e->type()) {
+ case QEvent::KeyPress:
+ d->keyPressEvent(static_cast<QKeyEvent *>(e));
+ break;
+ case QEvent::MouseButtonPress: {
+ QMouseEvent *ev = static_cast<QMouseEvent *>(e);
+ d->mousePressEvent(ev->button(), matrix.map(ev->pos()), ev->modifiers(),
+ ev->buttons(), ev->globalPos());
+ break; }
+ case QEvent::MouseMove: {
+ QMouseEvent *ev = static_cast<QMouseEvent *>(e);
+ d->mouseMoveEvent(ev->buttons(), matrix.map(ev->pos()));
+ break; }
+ case QEvent::MouseButtonRelease: {
+ QMouseEvent *ev = static_cast<QMouseEvent *>(e);
+ d->mouseReleaseEvent(ev->button(), matrix.map(ev->pos()));
+ break; }
+ case QEvent::MouseButtonDblClick: {
+ QMouseEvent *ev = static_cast<QMouseEvent *>(e);
+ d->mouseDoubleClickEvent(e, ev->button(), matrix.map(ev->pos()));
+ break; }
+ case QEvent::InputMethod:
+ d->inputMethodEvent(static_cast<QInputMethodEvent *>(e));
+ break;
+#ifndef QT_NO_CONTEXTMENU
+ case QEvent::ContextMenu: {
+ QContextMenuEvent *ev = static_cast<QContextMenuEvent *>(e);
+ d->contextMenuEvent(ev->globalPos(), matrix.map(ev->pos()), contextWidget);
+ break; }
+#endif // QT_NO_CONTEXTMENU
+ case QEvent::FocusIn:
+ case QEvent::FocusOut:
+ d->focusEvent(static_cast<QFocusEvent *>(e));
+ break;
+
+ case QEvent::EnabledChange:
+ d->isEnabled = e->isAccepted();
+ break;
+
+#ifndef QT_NO_TOOLTIP
+ case QEvent::ToolTip: {
+ QHelpEvent *ev = static_cast<QHelpEvent *>(e);
+ d->showToolTip(ev->globalPos(), matrix.map(ev->pos()), contextWidget);
+ break;
+ }
+#endif // QT_NO_TOOLTIP
+
+#ifndef QT_NO_DRAGANDDROP
+ case QEvent::DragEnter: {
+ QDragEnterEvent *ev = static_cast<QDragEnterEvent *>(e);
+ if (d->dragEnterEvent(e, ev->mimeData()))
+ ev->acceptProposedAction();
+ break;
+ }
+ case QEvent::DragLeave:
+ d->dragLeaveEvent();
+ break;
+ case QEvent::DragMove: {
+ QDragMoveEvent *ev = static_cast<QDragMoveEvent *>(e);
+ if (d->dragMoveEvent(e, ev->mimeData(), matrix.map(ev->pos())))
+ ev->acceptProposedAction();
+ break;
+ }
+ case QEvent::Drop: {
+ QDropEvent *ev = static_cast<QDropEvent *>(e);
+ if (d->dropEvent(ev->mimeData(), matrix.map(ev->pos()), ev->dropAction(), ev->source()))
+ ev->acceptProposedAction();
+ break;
+ }
+#endif
+
+#ifndef QT_NO_GRAPHICSVIEW
+ case QEvent::GraphicsSceneMousePress: {
+ QGraphicsSceneMouseEvent *ev = static_cast<QGraphicsSceneMouseEvent *>(e);
+ d->mousePressEvent(ev->button(), matrix.map(ev->pos()), ev->modifiers(), ev->buttons(),
+ ev->screenPos());
+ break; }
+ case QEvent::GraphicsSceneMouseMove: {
+ QGraphicsSceneMouseEvent *ev = static_cast<QGraphicsSceneMouseEvent *>(e);
+ d->mouseMoveEvent(ev->buttons(), matrix.map(ev->pos()));
+ break; }
+ case QEvent::GraphicsSceneMouseRelease: {
+ QGraphicsSceneMouseEvent *ev = static_cast<QGraphicsSceneMouseEvent *>(e);
+ d->mouseReleaseEvent(ev->button(), matrix.map(ev->pos()));
+ break; }
+ case QEvent::GraphicsSceneMouseDoubleClick: {
+ QGraphicsSceneMouseEvent *ev = static_cast<QGraphicsSceneMouseEvent *>(e);
+ d->mouseDoubleClickEvent(e, ev->button(), matrix.map(ev->pos()));
+ break; }
+ case QEvent::GraphicsSceneContextMenu: {
+ QGraphicsSceneContextMenuEvent *ev = static_cast<QGraphicsSceneContextMenuEvent *>(e);
+ d->contextMenuEvent(ev->screenPos(), matrix.map(ev->pos()), contextWidget);
+ break; }
+
+ case QEvent::GraphicsSceneHoverMove: {
+ QGraphicsSceneHoverEvent *ev = static_cast<QGraphicsSceneHoverEvent *>(e);
+ d->mouseMoveEvent(Qt::NoButton, matrix.map(ev->pos()));
+ break; }
+
+ case QEvent::GraphicsSceneDragEnter: {
+ QGraphicsSceneDragDropEvent *ev = static_cast<QGraphicsSceneDragDropEvent *>(e);
+ if (d->dragEnterEvent(e, ev->mimeData()))
+ ev->acceptProposedAction();
+ break; }
+ case QEvent::GraphicsSceneDragLeave:
+ d->dragLeaveEvent();
+ break;
+ case QEvent::GraphicsSceneDragMove: {
+ QGraphicsSceneDragDropEvent *ev = static_cast<QGraphicsSceneDragDropEvent *>(e);
+ if (d->dragMoveEvent(e, ev->mimeData(), matrix.map(ev->pos())))
+ ev->acceptProposedAction();
+ break; }
+ case QEvent::GraphicsSceneDrop: {
+ QGraphicsSceneDragDropEvent *ev = static_cast<QGraphicsSceneDragDropEvent *>(e);
+ if (d->dropEvent(ev->mimeData(), matrix.map(ev->pos()), ev->dropAction(), ev->source()))
+ ev->accept();
+ break; }
+#endif // QT_NO_GRAPHICSVIEW
+#ifdef QT_KEYPAD_NAVIGATION
+ case QEvent::EnterEditFocus:
+ case QEvent::LeaveEditFocus:
+ if (QApplication::keypadNavigationEnabled())
+ d->editFocusEvent(e);
+ break;
+#endif
+ case QEvent::ShortcutOverride:
+ if (d->interactionFlags & Qt::TextEditable) {
+ QKeyEvent* ke = static_cast<QKeyEvent *>(e);
+ if (ke->modifiers() == Qt::NoModifier
+ || ke->modifiers() == Qt::ShiftModifier
+ || ke->modifiers() == Qt::KeypadModifier) {
+ 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:
+ case Qt::Key_Up:
+ case Qt::Key_Down:
+ case Qt::Key_Tab:
+ ke->accept();
+ default:
+ break;
+ }
+ }
+#ifndef QT_NO_SHORTCUT
+ } else if (ke == QKeySequence::Copy
+ || ke == QKeySequence::Paste
+ || ke == QKeySequence::Cut
+ || ke == QKeySequence::Redo
+ || ke == QKeySequence::Undo
+ || ke == QKeySequence::MoveToNextWord
+ || ke == QKeySequence::MoveToPreviousWord
+ || ke == QKeySequence::MoveToStartOfDocument
+ || ke == QKeySequence::MoveToEndOfDocument
+ || ke == QKeySequence::SelectNextWord
+ || ke == QKeySequence::SelectPreviousWord
+ || ke == QKeySequence::SelectStartOfLine
+ || ke == QKeySequence::SelectEndOfLine
+ || ke == QKeySequence::SelectStartOfBlock
+ || ke == QKeySequence::SelectEndOfBlock
+ || ke == QKeySequence::SelectStartOfDocument
+ || ke == QKeySequence::SelectEndOfDocument
+ || ke == QKeySequence::SelectAll
+ ) {
+ ke->accept();
+#endif
+ }
+ }
+ break;
+ case QEvent::LayoutDirectionChange: {
+ if (contextWidget) {
+ QTextOption opt = document()->defaultTextOption();
+ opt.setTextDirection(contextWidget->layoutDirection());
+ document()->setDefaultTextOption(opt);
+ }
+ }
+ // FALL THROUGH
+ default:
+ break;
+ }
+}
+
+bool QTextControl::event(QEvent *e)
+{
+ return QObject::event(e);
+}
+
+void QTextControl::timerEvent(QTimerEvent *e)
+{
+ Q_D(QTextControl);
+ if (e->timerId() == d->cursorBlinkTimer.timerId()) {
+ d->cursorOn = !d->cursorOn;
+
+ if (d->cursor.hasSelection())
+ d->cursorOn &= (QApplication::style()->styleHint(QStyle::SH_BlinkCursorWhenTextSelected)
+ != 0);
+
+ d->repaintCursor();
+ } else if (e->timerId() == d->trippleClickTimer.timerId()) {
+ d->trippleClickTimer.stop();
+ }
+}
+
+void QTextControl::setPlainText(const QString &text)
+{
+ Q_D(QTextControl);
+ d->setContent(Qt::PlainText, text);
+}
+
+void QTextControl::setHtml(const QString &text)
+{
+ Q_D(QTextControl);
+ d->setContent(Qt::RichText, text);
+}
+
+void QTextControlPrivate::keyPressEvent(QKeyEvent *e)
+{
+ Q_Q(QTextControl);
+#ifndef QT_NO_SHORTCUT
+ if (e == QKeySequence::SelectAll) {
+ e->accept();
+ q->selectAll();
+ return;
+ }
+#ifndef QT_NO_CLIPBOARD
+ else if (e == QKeySequence::Copy) {
+ e->accept();
+ q->copy();
+ return;
+ }
+#endif
+#endif // QT_NO_SHORTCUT
+
+ if (interactionFlags & Qt::TextSelectableByKeyboard
+ && cursorMoveKeyEvent(e))
+ goto accept;
+
+ if (interactionFlags & Qt::LinksAccessibleByKeyboard) {
+ if ((e->key() == Qt::Key_Return
+ || e->key() == Qt::Key_Enter
+#ifdef QT_KEYPAD_NAVIGATION
+ || e->key() == Qt::Key_Select
+#endif
+ )
+ && cursor.hasSelection()) {
+
+ e->accept();
+ activateLinkUnderCursor();
+ return;
+ }
+ }
+
+ if (!(interactionFlags & Qt::TextEditable)) {
+ e->ignore();
+ return;
+ }
+
+ if (e->key() == Qt::Key_Direction_L || e->key() == Qt::Key_Direction_R) {
+ QTextBlockFormat fmt;
+ fmt.setLayoutDirection((e->key() == Qt::Key_Direction_L) ? Qt::LeftToRight : Qt::RightToLeft);
+ cursor.mergeBlockFormat(fmt);
+ goto accept;
+ }
+
+ // schedule a repaint of the region of the cursor, as when we move it we
+ // want to make sure the old cursor disappears (not noticeable when moving
+ // only a few pixels but noticeable when jumping between cells in tables for
+ // example)
+ repaintSelection();
+
+ if (e->key() == Qt::Key_Backspace && !(e->modifiers() & ~Qt::ShiftModifier)) {
+ QTextBlockFormat blockFmt = cursor.blockFormat();
+ QTextList *list = cursor.currentList();
+ if (list && cursor.atBlockStart() && !cursor.hasSelection()) {
+ list->remove(cursor.block());
+ } else if (cursor.atBlockStart() && blockFmt.indent() > 0) {
+ blockFmt.setIndent(blockFmt.indent() - 1);
+ cursor.setBlockFormat(blockFmt);
+ } else {
+ cursor.deletePreviousChar();
+ }
+ goto accept;
+ }
+#ifndef QT_NO_SHORTCUT
+ else if (e == QKeySequence::InsertParagraphSeparator) {
+ cursor.insertBlock();
+ e->accept();
+ goto accept;
+ } else if (e == QKeySequence::InsertLineSeparator) {
+ cursor.insertText(QString(QChar::LineSeparator));
+ e->accept();
+ goto accept;
+ }
+#endif
+ if (false) {
+ }
+#ifndef QT_NO_SHORTCUT
+ else if (e == QKeySequence::Undo) {
+ q->undo();
+ }
+ else if (e == QKeySequence::Redo) {
+ q->redo();
+ }
+#ifndef QT_NO_CLIPBOARD
+ else if (e == QKeySequence::Cut) {
+ q->cut();
+ }
+ else if (e == QKeySequence::Paste) {
+ q->paste();
+ }
+#endif
+ else if (e == QKeySequence::Delete) {
+ cursor.deleteChar();
+ }
+ else if (e == QKeySequence::DeleteEndOfWord) {
+ cursor.movePosition(QTextCursor::NextWord, QTextCursor::KeepAnchor);
+ cursor.removeSelectedText();
+ }
+ else if (e == QKeySequence::DeleteStartOfWord) {
+ cursor.movePosition(QTextCursor::PreviousWord, QTextCursor::KeepAnchor);
+ cursor.removeSelectedText();
+ }
+ else if (e == QKeySequence::DeleteEndOfLine) {
+ QTextBlock block = cursor.block();
+ if (cursor.position() == block.position() + block.length() - 2)
+ cursor.movePosition(QTextCursor::Right, QTextCursor::KeepAnchor);
+ else
+ cursor.movePosition(QTextCursor::EndOfBlock, QTextCursor::KeepAnchor);
+ cursor.removeSelectedText();
+ }
+#endif // QT_NO_SHORTCUT
+ else {
+ goto process;
+ }
+ goto accept;
+
+process:
+ {
+ QString text = e->text();
+ if (!text.isEmpty() && (text.at(0).isPrint() || text.at(0) == QLatin1Char('\t'))) {
+ if (overwriteMode
+ // no need to call deleteChar() if we have a selection, insertText
+ // does it already
+ && !cursor.hasSelection()
+ && !cursor.atBlockEnd())
+ cursor.deleteChar();
+
+ cursor.insertText(text);
+ selectionChanged();
+ } else {
+ e->ignore();
+ return;
+ }
+ }
+
+ accept:
+
+ e->accept();
+ cursorOn = true;
+
+ q->ensureCursorVisible();
+
+ updateCurrentCharFormat();
+}
+
+QVariant QTextControl::loadResource(int type, const QUrl &name)
+{
+#ifdef QT_NO_TEXTEDIT
+ Q_UNUSED(type);
+ Q_UNUSED(name);
+#else
+ if (QTextEdit *textEdit = qobject_cast<QTextEdit *>(parent())) {
+ QUrl resolvedName = textEdit->d_func()->resolveUrl(name);
+ return textEdit->loadResource(type, resolvedName);
+ }
+#endif
+ return QVariant();
+}
+
+void QTextControlPrivate::_q_updateBlock(const QTextBlock &block)
+{
+ Q_Q(QTextControl);
+ emit q->updateRequest(q->blockBoundingRect(block));
+}
+
+QRectF QTextControlPrivate::rectForPosition(int position) const
+{
+ Q_Q(const QTextControl);
+ const QTextBlock block = doc->findBlock(position);
+ if (!block.isValid())
+ return QRectF();
+ const QAbstractTextDocumentLayout *docLayout = doc->documentLayout();
+ const QTextLayout *layout = block.layout();
+ const QPointF layoutPos = q->blockBoundingRect(block).topLeft();
+ int relativePos = position - block.position();
+ if (preeditCursor != 0) {
+ int preeditPos = layout->preeditAreaPosition();
+ if (relativePos == preeditPos)
+ relativePos += preeditCursor;
+ else if (relativePos > preeditPos)
+ relativePos += layout->preeditAreaText().length();
+ }
+ QTextLine line = layout->lineForTextPosition(relativePos);
+
+ int cursorWidth;
+ {
+ bool ok = false;
+#ifndef QT_NO_PROPERTIES
+ cursorWidth = docLayout->property("cursorWidth").toInt(&ok);
+#endif
+ if (!ok)
+ cursorWidth = 1;
+ }
+
+ QRectF r;
+
+ if (line.isValid()) {
+ qreal x = line.cursorToX(relativePos);
+ qreal w = 0;
+ if (overwriteMode) {
+ if (relativePos < line.textLength() - line.textStart())
+ w = line.cursorToX(relativePos + 1) - x;
+ else
+ w = QFontMetrics(block.layout()->font()).width(QLatin1Char(' ')); // in sync with QTextLine::draw()
+ }
+ r = QRectF(layoutPos.x() + x, layoutPos.y() + line.y(),
+ cursorWidth + w, line.height());
+ } else {
+ r = QRectF(layoutPos.x(), layoutPos.y(), cursorWidth, 10); // #### correct height
+ }
+
+ return r;
+}
+
+static inline bool firstFramePosLessThanCursorPos(QTextFrame *frame, int position)
+{
+ return frame->firstPosition() < position;
+}
+
+static inline bool cursorPosLessThanLastFramePos(int position, QTextFrame *frame)
+{
+ return position < frame->lastPosition();
+}
+
+static QRectF boundingRectOfFloatsInSelection(const QTextCursor &cursor)
+{
+ QRectF r;
+ QTextFrame *frame = cursor.currentFrame();
+ const QList<QTextFrame *> children = frame->childFrames();
+
+ const QList<QTextFrame *>::ConstIterator firstFrame = qLowerBound(children.constBegin(), children.constEnd(),
+ cursor.selectionStart(), firstFramePosLessThanCursorPos);
+ const QList<QTextFrame *>::ConstIterator lastFrame = qUpperBound(children.constBegin(), children.constEnd(),
+ cursor.selectionEnd(), cursorPosLessThanLastFramePos);
+ for (QList<QTextFrame *>::ConstIterator it = firstFrame; it != lastFrame; ++it) {
+ if ((*it)->frameFormat().position() != QTextFrameFormat::InFlow)
+ r |= frame->document()->documentLayout()->frameBoundingRect(*it);
+ }
+ return r;
+}
+
+QRectF QTextControl::selectionRect(const QTextCursor &cursor) const
+{
+ Q_D(const QTextControl);
+
+ QRectF r = d->rectForPosition(cursor.selectionStart());
+
+ if (cursor.hasComplexSelection() && cursor.currentTable()) {
+ QTextTable *table = cursor.currentTable();
+
+ r = d->doc->documentLayout()->frameBoundingRect(table);
+ /*
+ int firstRow, numRows, firstColumn, numColumns;
+ cursor.selectedTableCells(&firstRow, &numRows, &firstColumn, &numColumns);
+
+ const QTextTableCell firstCell = table->cellAt(firstRow, firstColumn);
+ const QTextTableCell lastCell = table->cellAt(firstRow + numRows - 1, firstColumn + numColumns - 1);
+
+ const QAbstractTextDocumentLayout * const layout = doc->documentLayout();
+
+ QRectF tableSelRect = layout->blockBoundingRect(firstCell.firstCursorPosition().block());
+
+ for (int col = firstColumn; col < firstColumn + numColumns; ++col) {
+ const QTextTableCell cell = table->cellAt(firstRow, col);
+ const qreal y = layout->blockBoundingRect(cell.firstCursorPosition().block()).top();
+
+ tableSelRect.setTop(qMin(tableSelRect.top(), y));
+ }
+
+ for (int row = firstRow; row < firstRow + numRows; ++row) {
+ const QTextTableCell cell = table->cellAt(row, firstColumn);
+ const qreal x = layout->blockBoundingRect(cell.firstCursorPosition().block()).left();
+
+ tableSelRect.setLeft(qMin(tableSelRect.left(), x));
+ }
+
+ for (int col = firstColumn; col < firstColumn + numColumns; ++col) {
+ const QTextTableCell cell = table->cellAt(firstRow + numRows - 1, col);
+ const qreal y = layout->blockBoundingRect(cell.lastCursorPosition().block()).bottom();
+
+ tableSelRect.setBottom(qMax(tableSelRect.bottom(), y));
+ }
+
+ for (int row = firstRow; row < firstRow + numRows; ++row) {
+ const QTextTableCell cell = table->cellAt(row, firstColumn + numColumns - 1);
+ const qreal x = layout->blockBoundingRect(cell.lastCursorPosition().block()).right();
+
+ tableSelRect.setRight(qMax(tableSelRect.right(), x));
+ }
+
+ r = tableSelRect.toRect();
+ */
+ } else if (cursor.hasSelection()) {
+ const int position = cursor.selectionStart();
+ const int anchor = cursor.selectionEnd();
+ const QTextBlock posBlock = d->doc->findBlock(position);
+ const QTextBlock anchorBlock = d->doc->findBlock(anchor);
+ if (posBlock == anchorBlock && posBlock.isValid() && posBlock.layout()->lineCount()) {
+ const QTextLine posLine = posBlock.layout()->lineForTextPosition(position - posBlock.position());
+ const QTextLine anchorLine = anchorBlock.layout()->lineForTextPosition(anchor - anchorBlock.position());
+
+ const int firstLine = qMin(posLine.lineNumber(), anchorLine.lineNumber());
+ const int lastLine = qMax(posLine.lineNumber(), anchorLine.lineNumber());
+ const QTextLayout *layout = posBlock.layout();
+ r = QRectF();
+ for (int i = firstLine; i <= lastLine; ++i) {
+ r |= layout->lineAt(i).rect();
+ r |= layout->lineAt(i).naturalTextRect(); // might be bigger in the case of wrap not enabled
+ }
+ r.translate(blockBoundingRect(posBlock).topLeft());
+ } else {
+ QRectF anchorRect = d->rectForPosition(cursor.selectionEnd());
+ r |= anchorRect;
+ r |= boundingRectOfFloatsInSelection(cursor);
+ QRectF frameRect(d->doc->documentLayout()->frameBoundingRect(cursor.currentFrame()));
+ r.setLeft(frameRect.left());
+ r.setRight(frameRect.right());
+ }
+ if (r.isValid())
+ r.adjust(-1, -1, 1, 1);
+ }
+
+ return r;
+}
+
+QRectF QTextControl::selectionRect() const
+{
+ Q_D(const QTextControl);
+ return selectionRect(d->cursor);
+}
+
+void QTextControlPrivate::mousePressEvent(Qt::MouseButton button, const QPointF &pos, Qt::KeyboardModifiers modifiers,
+ Qt::MouseButtons buttons, const QPoint &globalPos)
+{
+ Q_Q(QTextControl);
+
+ if (interactionFlags & Qt::LinksAccessibleByMouse) {
+ anchorOnMousePress = q->anchorAt(pos);
+
+ if (cursorIsFocusIndicator) {
+ cursorIsFocusIndicator = false;
+ repaintSelection();
+ cursor.clearSelection();
+ }
+ }
+ if (!(button & Qt::LeftButton))
+ return;
+
+ if (!((interactionFlags & Qt::TextSelectableByMouse) || (interactionFlags & Qt::TextEditable)))
+ return;
+
+ cursorIsFocusIndicator = false;
+ const QTextCursor oldSelection = cursor;
+ const int oldCursorPos = cursor.position();
+
+ mousePressed = true;
+#ifndef QT_NO_DRAGANDDROP
+ mightStartDrag = false;
+#endif
+
+ if (trippleClickTimer.isActive()
+ && ((pos - trippleClickPoint).toPoint().manhattanLength() < QApplication::startDragDistance())) {
+
+ cursor.movePosition(QTextCursor::StartOfBlock);
+ cursor.movePosition(QTextCursor::EndOfBlock, QTextCursor::KeepAnchor);
+ cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor);
+ selectedBlockOnTrippleClick = cursor;
+
+ anchorOnMousePress = QString();
+
+ trippleClickTimer.stop();
+ } else {
+ int cursorPos = q->hitTest(pos, Qt::FuzzyHit);
+ if (cursorPos == -1)
+ return;
+
+#if !defined(QT_NO_IM)
+ QTextLayout *layout = cursor.block().layout();
+ if (contextWidget && layout && !layout->preeditAreaText().isEmpty()) {
+ QInputContext *ctx = inputContext();
+ if (ctx) {
+ QMouseEvent ev(QEvent::MouseButtonPress, contextWidget->mapFromGlobal(globalPos), globalPos,
+ button, buttons, modifiers);
+ ctx->mouseHandler(cursorPos - cursor.position(), &ev);
+ }
+ if (!layout->preeditAreaText().isEmpty())
+ return;
+ }
+#endif
+ if (modifiers == Qt::ShiftModifier) {
+ if (selectedBlockOnTrippleClick.hasSelection())
+ extendBlockwiseSelection(cursorPos);
+ else if (selectedWordOnDoubleClick.hasSelection())
+ extendWordwiseSelection(cursorPos, pos.x());
+ else
+ setCursorPosition(cursorPos, QTextCursor::KeepAnchor);
+ } else {
+
+ if (cursor.hasSelection()
+ && !cursorIsFocusIndicator
+ && cursorPos >= cursor.selectionStart()
+ && cursorPos <= cursor.selectionEnd()
+ && q->hitTest(pos, Qt::ExactHit) != -1) {
+#ifndef QT_NO_DRAGANDDROP
+ mightStartDrag = true;
+ dragStartPos = pos.toPoint();
+#endif
+ return;
+ }
+
+ setCursorPosition(cursorPos);
+ }
+ }
+
+ if (interactionFlags & Qt::TextEditable) {
+ q->ensureCursorVisible();
+ if (cursor.position() != oldCursorPos)
+ emit q->cursorPositionChanged();
+ _q_updateCurrentCharFormatAndSelection();
+ } else {
+ if (cursor.position() != oldCursorPos)
+ emit q->cursorPositionChanged();
+ selectionChanged();
+ }
+ repaintOldAndNewSelection(oldSelection);
+ hadSelectionOnMousePress = cursor.hasSelection();
+}
+
+void QTextControlPrivate::mouseMoveEvent(Qt::MouseButtons buttons, const QPointF &mousePos)
+{
+ Q_Q(QTextControl);
+
+ if (interactionFlags & Qt::LinksAccessibleByMouse) {
+ QString anchor = q->anchorAt(mousePos);
+ if (anchor != highlightedAnchor) {
+ highlightedAnchor = anchor;
+ emit q->linkHovered(anchor);
+ }
+ }
+
+ if (!(buttons & Qt::LeftButton))
+ return;
+
+ if (!((interactionFlags & Qt::TextSelectableByMouse) || (interactionFlags & Qt::TextEditable)))
+ return;
+
+ if (!(mousePressed
+ || selectedWordOnDoubleClick.hasSelection()
+ || selectedBlockOnTrippleClick.hasSelection()))
+ return;
+
+ const QTextCursor oldSelection = cursor;
+ const int oldCursorPos = cursor.position();
+
+ if (mightStartDrag) {
+ if ((mousePos.toPoint() - dragStartPos).manhattanLength() > QApplication::startDragDistance())
+ startDrag();
+ return;
+ }
+ const qreal mouseX = qreal(mousePos.x());
+
+#if !defined(QT_NO_IM)
+ QTextLayout *layout = cursor.block().layout();
+ if (layout && !layout->preeditAreaText().isEmpty())
+ return;
+#endif
+
+ int newCursorPos = q->hitTest(mousePos, Qt::FuzzyHit);
+ if (newCursorPos == -1)
+ return;
+
+ if (selectedBlockOnTrippleClick.hasSelection())
+ extendBlockwiseSelection(newCursorPos);
+ else if (selectedWordOnDoubleClick.hasSelection())
+ extendWordwiseSelection(newCursorPos, mouseX);
+ else
+ setCursorPosition(newCursorPos, QTextCursor::KeepAnchor);
+
+ if (interactionFlags & Qt::TextEditable) {
+ // don't call ensureVisible for the visible cursor to avoid jumping
+ // scrollbars. the autoscrolling ensures smooth scrolling if necessary.
+ //q->ensureCursorVisible();
+ if (cursor.position() != oldCursorPos)
+ emit q->cursorPositionChanged();
+ _q_updateCurrentCharFormatAndSelection();
+ } else {
+ //emit q->visibilityRequest(QRectF(mousePos, QSizeF(1, 1)));
+ if (cursor.position() != oldCursorPos)
+ emit q->cursorPositionChanged();
+ }
+ selectionChanged(true);
+ repaintOldAndNewSelection(oldSelection);
+}
+
+void QTextControlPrivate::mouseReleaseEvent(Qt::MouseButton button, const QPointF &pos)
+{
+ Q_Q(QTextControl);
+
+ const QTextCursor oldSelection = cursor;
+ const int oldCursorPos = cursor.position();
+
+#ifndef QT_NO_DRAGANDDROP
+ if (mightStartDrag && (button & Qt::LeftButton)) {
+ mousePressed = false;
+ setCursorPosition(pos);
+ cursor.clearSelection();
+ selectionChanged();
+ }
+#endif
+ if (mousePressed) {
+ mousePressed = false;
+#ifndef QT_NO_CLIPBOARD
+ if (interactionFlags & Qt::TextSelectableByMouse) {
+ setClipboardSelection();
+ selectionChanged(true);
+ }
+ } else if (button == Qt::MidButton
+ && (interactionFlags & Qt::TextEditable)
+ && QApplication::clipboard()->supportsSelection()) {
+ setCursorPosition(pos);
+ const QMimeData *md = QApplication::clipboard()->mimeData(QClipboard::Selection);
+ if (md)
+ q->insertFromMimeData(md);
+#endif
+ }
+
+ repaintOldAndNewSelection(oldSelection);
+
+ if (cursor.position() != oldCursorPos)
+ emit q->cursorPositionChanged();
+
+ if (interactionFlags & Qt::LinksAccessibleByMouse) {
+ if (!(button & Qt::LeftButton))
+ return;
+
+ const QString anchor = q->anchorAt(pos);
+
+ if (anchor.isEmpty())
+ return;
+
+ if (!cursor.hasSelection()
+ || (anchor == anchorOnMousePress && hadSelectionOnMousePress)) {
+
+ const int anchorPos = q->hitTest(pos, Qt::ExactHit);
+ if (anchorPos != -1) {
+ cursor.setPosition(anchorPos);
+
+ QString anchor = anchorOnMousePress;
+ anchorOnMousePress = QString();
+ activateLinkUnderCursor(anchor);
+ }
+ }
+ }
+}
+
+void QTextControlPrivate::mouseDoubleClickEvent(QEvent *e, Qt::MouseButton button, const QPointF &pos)
+{
+ Q_Q(QTextControl);
+ if (button != Qt::LeftButton
+ || !(interactionFlags & Qt::TextSelectableByMouse)) {
+ e->ignore();
+ return;
+ }
+#if !defined(QT_NO_IM)
+ QTextLayout *layout = cursor.block().layout();
+ if (layout && !layout->preeditAreaText().isEmpty())
+ return;
+#endif
+
+#ifndef QT_NO_DRAGANDDROP
+ mightStartDrag = false;
+#endif
+ const QTextCursor oldSelection = cursor;
+ setCursorPosition(pos);
+ QTextLine line = currentTextLine(cursor);
+ bool doEmit = false;
+ if (line.isValid() && line.textLength()) {
+ cursor.select(QTextCursor::WordUnderCursor);
+ doEmit = true;
+ }
+ repaintOldAndNewSelection(oldSelection);
+
+ cursorIsFocusIndicator = false;
+ selectedWordOnDoubleClick = cursor;
+
+ trippleClickPoint = pos;
+ trippleClickTimer.start(qApp->doubleClickInterval(), q);
+ if (doEmit) {
+ selectionChanged();
+#ifndef QT_NO_CLIPBOARD
+ setClipboardSelection();
+#endif
+ emit q->cursorPositionChanged();
+ }
+}
+
+void QTextControlPrivate::contextMenuEvent(const QPoint &screenPos, const QPointF &docPos, QWidget *contextWidget)
+{
+#ifdef QT_NO_CONTEXTMENU
+ Q_UNUSED(screenPos);
+ Q_UNUSED(docPos);
+ Q_UNUSED(contextWidget);
+#else
+ Q_Q(QTextControl);
+ if (!hasFocus)
+ return;
+ QMenu *menu = q->createStandardContextMenu(docPos, contextWidget);
+ if (!menu)
+ return;
+ menu->exec(screenPos);
+ delete menu;
+#endif
+}
+
+bool QTextControlPrivate::dragEnterEvent(QEvent *e, const QMimeData *mimeData)
+{
+ Q_Q(QTextControl);
+ if (!(interactionFlags & Qt::TextEditable) || !q->canInsertFromMimeData(mimeData)) {
+ e->ignore();
+ return false;
+ }
+
+ dndFeedbackCursor = QTextCursor();
+
+ return true; // accept proposed action
+}
+
+void QTextControlPrivate::dragLeaveEvent()
+{
+ Q_Q(QTextControl);
+
+ const QRectF crect = q->cursorRect(dndFeedbackCursor);
+ dndFeedbackCursor = QTextCursor();
+
+ if (crect.isValid())
+ emit q->updateRequest(crect);
+}
+
+bool QTextControlPrivate::dragMoveEvent(QEvent *e, const QMimeData *mimeData, const QPointF &pos)
+{
+ Q_Q(QTextControl);
+ if (!(interactionFlags & Qt::TextEditable) || !q->canInsertFromMimeData(mimeData)) {
+ e->ignore();
+ return false;
+ }
+
+ const int cursorPos = q->hitTest(pos, Qt::FuzzyHit);
+ if (cursorPos != -1) {
+ QRectF crect = q->cursorRect(dndFeedbackCursor);
+ if (crect.isValid())
+ emit q->updateRequest(crect);
+
+ dndFeedbackCursor = cursor;
+ dndFeedbackCursor.setPosition(cursorPos);
+
+ crect = q->cursorRect(dndFeedbackCursor);
+ emit q->updateRequest(crect);
+ }
+
+ return true; // accept proposed action
+}
+
+bool QTextControlPrivate::dropEvent(const QMimeData *mimeData, const QPointF &pos, Qt::DropAction dropAction, QWidget *source)
+{
+ Q_Q(QTextControl);
+ dndFeedbackCursor = QTextCursor();
+
+ if (!(interactionFlags & Qt::TextEditable) || !q->canInsertFromMimeData(mimeData))
+ return false;
+
+ repaintSelection();
+
+ QTextCursor insertionCursor = q->cursorForPosition(pos);
+ insertionCursor.beginEditBlock();
+
+ if (dropAction == Qt::MoveAction && source == contextWidget)
+ cursor.removeSelectedText();
+
+ cursor = insertionCursor;
+ q->insertFromMimeData(mimeData);
+ insertionCursor.endEditBlock();
+ q->ensureCursorVisible();
+ return true; // accept proposed action
+}
+
+void QTextControlPrivate::inputMethodEvent(QInputMethodEvent *e)
+{
+ if (!(interactionFlags & Qt::TextEditable) || cursor.isNull()) {
+ e->ignore();
+ return;
+ }
+ cursor.beginEditBlock();
+
+ cursor.removeSelectedText();
+
+ // insert commit string
+ if (!e->commitString().isEmpty() || e->replacementLength()) {
+ QTextCursor c = cursor;
+ c.setPosition(c.position() + e->replacementStart());
+ c.setPosition(c.position() + e->replacementLength(), QTextCursor::KeepAnchor);
+ c.insertText(e->commitString());
+ }
+
+ QTextBlock block = cursor.block();
+ QTextLayout *layout = block.layout();
+ layout->setPreeditArea(cursor.position() - block.position(), e->preeditString());
+ QList<QTextLayout::FormatRange> overrides;
+ preeditCursor = e->preeditString().length();
+ hideCursor = false;
+ for (int i = 0; i < e->attributes().size(); ++i) {
+ const QInputMethodEvent::Attribute &a = e->attributes().at(i);
+ if (a.type == QInputMethodEvent::Cursor) {
+ preeditCursor = a.start;
+ hideCursor = !a.length;
+ } else if (a.type == QInputMethodEvent::TextFormat) {
+ QTextCharFormat f = qvariant_cast<QTextFormat>(a.value).toCharFormat();
+ if (f.isValid()) {
+ QTextLayout::FormatRange o;
+ o.start = a.start + cursor.position() - block.position();
+ o.length = a.length;
+ o.format = f;
+ overrides.append(o);
+ }
+ }
+ }
+ layout->setAdditionalFormats(overrides);
+ cursor.endEditBlock();
+}
+
+QVariant QTextControl::inputMethodQuery(Qt::InputMethodQuery property) const
+{
+ Q_D(const QTextControl);
+ QTextBlock block = d->cursor.block();
+ switch(property) {
+ case Qt::ImMicroFocus:
+ return cursorRect();
+ case Qt::ImFont:
+ return QVariant(d->cursor.charFormat().font());
+ case Qt::ImCursorPosition:
+ return QVariant(d->cursor.selectionEnd() - block.position());
+ case Qt::ImSurroundingText:
+ return QVariant(block.text());
+ case Qt::ImCurrentSelection:
+ return QVariant(d->cursor.selectedText());
+ default:
+ return QVariant();
+ }
+}
+
+void QTextControl::setFocus(bool focus, Qt::FocusReason reason)
+{
+ QFocusEvent ev(focus ? QEvent::FocusIn : QEvent::FocusOut,
+ reason);
+ processEvent(&ev);
+}
+
+void QTextControlPrivate::focusEvent(QFocusEvent *e)
+{
+ Q_Q(QTextControl);
+ emit q->updateRequest(q->selectionRect());
+ if (e->gotFocus()) {
+#ifdef QT_KEYPAD_NAVIGATION
+ if (!QApplication::keypadNavigationEnabled() || (hasEditFocus && e->reason() == Qt::PopupFocusReason)) {
+#endif
+ cursorOn = (interactionFlags & Qt::TextSelectableByKeyboard);
+ if (interactionFlags & Qt::TextEditable) {
+ setBlinkingCursorEnabled(true);
+ }
+#ifdef QT_KEYPAD_NAVIGATION
+ }
+#endif
+ } else {
+ setBlinkingCursorEnabled(false);
+
+ if (cursorIsFocusIndicator
+ && e->reason() != Qt::ActiveWindowFocusReason
+ && e->reason() != Qt::PopupFocusReason
+ && cursor.hasSelection()) {
+ cursor.clearSelection();
+ }
+ }
+ hasFocus = e->gotFocus();
+}
+
+QString QTextControlPrivate::anchorForCursor(const QTextCursor &anchorCursor) const
+{
+ if (anchorCursor.hasSelection()) {
+ QTextCursor cursor = anchorCursor;
+ if (cursor.selectionStart() != cursor.position())
+ cursor.setPosition(cursor.selectionStart());
+ cursor.movePosition(QTextCursor::NextCharacter);
+ QTextCharFormat fmt = cursor.charFormat();
+ if (fmt.isAnchor() && fmt.hasProperty(QTextFormat::AnchorHref))
+ return fmt.stringProperty(QTextFormat::AnchorHref);
+ }
+ return QString();
+}
+
+#ifdef QT_KEYPAD_NAVIGATION
+void QTextControlPrivate::editFocusEvent(QEvent *e)
+{
+ Q_Q(QTextControl);
+
+ if (QApplication::keypadNavigationEnabled()) {
+ if (e->type() == QEvent::EnterEditFocus && interactionFlags & Qt::TextEditable) {
+ const QTextCursor oldSelection = cursor;
+ const int oldCursorPos = cursor.position();
+ const bool moved = cursor.movePosition(QTextCursor::End, QTextCursor::MoveAnchor);
+ q->ensureCursorVisible();
+ if (moved) {
+ if (cursor.position() != oldCursorPos)
+ emit q->cursorPositionChanged();
+ emit q->microFocusChanged();
+ }
+ selectionChanged();
+ repaintOldAndNewSelection(oldSelection);
+
+ setBlinkingCursorEnabled(true);
+ } else
+ setBlinkingCursorEnabled(false);
+ }
+
+ hasEditFocus = e->type() == QEvent::EnterEditFocus ? true : false;
+}
+#endif
+
+#ifndef QT_NO_CONTEXTMENU
+QMenu *QTextControl::createStandardContextMenu(const QPointF &pos, QWidget *parent)
+{
+ Q_D(QTextControl);
+
+ const bool showTextSelectionActions = d->interactionFlags & (Qt::TextEditable | Qt::TextSelectableByKeyboard | Qt::TextSelectableByMouse);
+
+ d->linkToCopy = QString();
+ if (!pos.isNull())
+ d->linkToCopy = anchorAt(pos);
+
+ if (d->linkToCopy.isEmpty() && !showTextSelectionActions)
+ return 0;
+
+ QMenu *menu = new QMenu(parent);
+ QAction *a;
+
+ if (d->interactionFlags & Qt::TextEditable) {
+ a = menu->addAction(tr("&Undo") + ACCEL_KEY(QKeySequence::Undo), this, SLOT(undo()));
+ a->setEnabled(d->doc->isUndoAvailable());
+ a = menu->addAction(tr("&Redo") + ACCEL_KEY(QKeySequence::Redo), this, SLOT(redo()));
+ a->setEnabled(d->doc->isRedoAvailable());
+ menu->addSeparator();
+
+ a = menu->addAction(tr("Cu&t") + ACCEL_KEY(QKeySequence::Cut), this, SLOT(cut()));
+ a->setEnabled(d->cursor.hasSelection());
+ }
+
+ if (showTextSelectionActions) {
+ a = menu->addAction(tr("&Copy") + ACCEL_KEY(QKeySequence::Copy), this, SLOT(copy()));
+ a->setEnabled(d->cursor.hasSelection());
+ }
+
+ if ((d->interactionFlags & Qt::LinksAccessibleByKeyboard)
+ || (d->interactionFlags & Qt::LinksAccessibleByMouse)) {
+
+ a = menu->addAction(tr("Copy &Link Location"), this, SLOT(_q_copyLink()));
+ a->setEnabled(!d->linkToCopy.isEmpty());
+ }
+
+ if (d->interactionFlags & Qt::TextEditable) {
+#if !defined(QT_NO_CLIPBOARD)
+ a = menu->addAction(tr("&Paste") + ACCEL_KEY(QKeySequence::Paste), this, SLOT(paste()));
+ a->setEnabled(canPaste());
+#endif
+ a = menu->addAction(tr("Delete"), this, SLOT(_q_deleteSelected()));
+ a->setEnabled(d->cursor.hasSelection());
+ }
+
+
+ if (showTextSelectionActions) {
+ menu->addSeparator();
+ a = menu->addAction(tr("Select All") + ACCEL_KEY(QKeySequence::SelectAll), this, SLOT(selectAll()));
+ a->setEnabled(!d->doc->isEmpty());
+ }
+
+#if !defined(QT_NO_IM)
+ if (d->contextWidget) {
+ QInputContext *qic = d->inputContext();
+ if (qic) {
+ QList<QAction *> imActions = qic->actions();
+ for (int i = 0; i < imActions.size(); ++i)
+ menu->addAction(imActions.at(i));
+ }
+ }
+#endif
+
+#if defined(Q_WS_WIN)
+ if ((d->interactionFlags & Qt::TextEditable) && qt_use_rtl_extensions) {
+#else
+ if (d->interactionFlags & Qt::TextEditable) {
+#endif
+ menu->addSeparator();
+ QUnicodeControlCharacterMenu *ctrlCharacterMenu = new QUnicodeControlCharacterMenu(this, menu);
+ menu->addMenu(ctrlCharacterMenu);
+ }
+
+ return menu;
+}
+#endif // QT_NO_CONTEXTMENU
+
+QTextCursor QTextControl::cursorForPosition(const QPointF &pos) const
+{
+ Q_D(const QTextControl);
+ int cursorPos = hitTest(pos, Qt::FuzzyHit);
+ if (cursorPos == -1)
+ cursorPos = 0;
+ QTextCursor c(d->doc);
+ c.setPosition(cursorPos);
+ return c;
+}
+
+QRectF QTextControl::cursorRect(const QTextCursor &cursor) const
+{
+ Q_D(const QTextControl);
+ if (cursor.isNull())
+ return QRectF();
+
+ return d->rectForPosition(cursor.position());
+}
+
+QRectF QTextControl::cursorRect() const
+{
+ Q_D(const QTextControl);
+ return cursorRect(d->cursor);
+}
+
+QRectF QTextControlPrivate::cursorRectPlusUnicodeDirectionMarkers(const QTextCursor &cursor) const
+{
+ if (cursor.isNull())
+ return QRectF();
+
+ return rectForPosition(cursor.position()).adjusted(-4, 0, 4, 0);
+}
+
+QString QTextControl::anchorAt(const QPointF &pos) const
+{
+ Q_D(const QTextControl);
+ return d->doc->documentLayout()->anchorAt(pos);
+}
+
+QString QTextControl::anchorAtCursor() const
+{
+ Q_D(const QTextControl);
+
+ return d->anchorForCursor(d->cursor);
+}
+
+bool QTextControl::overwriteMode() const
+{
+ Q_D(const QTextControl);
+ return d->overwriteMode;
+}
+
+void QTextControl::setOverwriteMode(bool overwrite)
+{
+ Q_D(QTextControl);
+ d->overwriteMode = overwrite;
+}
+
+int QTextControl::cursorWidth() const
+{
+#ifndef QT_NO_PROPERTIES
+ Q_D(const QTextControl);
+ return d->doc->documentLayout()->property("cursorWidth").toInt();
+#else
+ return 1;
+#endif
+}
+
+void QTextControl::setCursorWidth(int width)
+{
+ Q_D(QTextControl);
+#ifdef QT_NO_PROPERTIES
+ Q_UNUSED(width);
+#else
+ if (width == -1)
+ width = QApplication::style()->pixelMetric(QStyle::PM_TextCursorWidth);
+ d->doc->documentLayout()->setProperty("cursorWidth", width);
+#endif
+ d->repaintCursor();
+}
+
+bool QTextControl::acceptRichText() const
+{
+ Q_D(const QTextControl);
+ return d->acceptRichText;
+}
+
+void QTextControl::setAcceptRichText(bool accept)
+{
+ Q_D(QTextControl);
+ d->acceptRichText = accept;
+}
+
+#ifndef QT_NO_TEXTEDIT
+
+void QTextControl::setExtraSelections(const QList<QTextEdit::ExtraSelection> &selections)
+{
+ Q_D(QTextControl);
+
+ QHash<int, int> hash;
+ for (int i = 0; i < d->extraSelections.count(); ++i) {
+ const QAbstractTextDocumentLayout::Selection &esel = d->extraSelections.at(i);
+ hash.insertMulti(esel.cursor.anchor(), i);
+ }
+
+ for (int i = 0; i < selections.count(); ++i) {
+ const QTextEdit::ExtraSelection &sel = selections.at(i);
+ QHash<int, int>::iterator it = hash.find(sel.cursor.anchor());
+ if (it != hash.end()) {
+ const QAbstractTextDocumentLayout::Selection &esel = d->extraSelections.at(it.value());
+ if (esel.cursor.position() == sel.cursor.position()
+ && esel.format == sel.format) {
+ hash.erase(it);
+ continue;
+ }
+ }
+ QRectF r = selectionRect(sel.cursor);
+ if (sel.format.boolProperty(QTextFormat::FullWidthSelection)) {
+ r.setLeft(0);
+ r.setWidth(qreal(INT_MAX));
+ }
+ emit updateRequest(r);
+ }
+
+ for (QHash<int, int>::iterator it = hash.begin(); it != hash.end(); ++it) {
+ const QAbstractTextDocumentLayout::Selection &esel = d->extraSelections.at(it.value());
+ QRectF r = selectionRect(esel.cursor);
+ if (esel.format.boolProperty(QTextFormat::FullWidthSelection)) {
+ r.setLeft(0);
+ r.setWidth(qreal(INT_MAX));
+ }
+ emit updateRequest(r);
+ }
+
+ d->extraSelections.resize(selections.count());
+ for (int i = 0; i < selections.count(); ++i) {
+ d->extraSelections[i].cursor = selections.at(i).cursor;
+ d->extraSelections[i].format = selections.at(i).format;
+ }
+}
+
+QList<QTextEdit::ExtraSelection> QTextControl::extraSelections() const
+{
+ Q_D(const QTextControl);
+ QList<QTextEdit::ExtraSelection> selections;
+ for (int i = 0; i < d->extraSelections.count(); ++i) {
+ QTextEdit::ExtraSelection sel;
+ sel.cursor = d->extraSelections.at(i).cursor;
+ sel.format = d->extraSelections.at(i).format;
+ selections.append(sel);
+ }
+ return selections;
+}
+
+#endif // QT_NO_TEXTEDIT
+
+void QTextControl::setTextWidth(qreal width)
+{
+ Q_D(QTextControl);
+ d->doc->setTextWidth(width);
+}
+
+qreal QTextControl::textWidth() const
+{
+ Q_D(const QTextControl);
+ return d->doc->textWidth();
+}
+
+QSizeF QTextControl::size() const
+{
+ Q_D(const QTextControl);
+ return d->doc->size();
+}
+
+void QTextControl::setOpenExternalLinks(bool open)
+{
+ Q_D(QTextControl);
+ d->openExternalLinks = open;
+}
+
+bool QTextControl::openExternalLinks() const
+{
+ Q_D(const QTextControl);
+ return d->openExternalLinks;
+}
+
+void QTextControl::moveCursor(QTextCursor::MoveOperation op, QTextCursor::MoveMode mode)
+{
+ Q_D(QTextControl);
+ const QTextCursor oldSelection = d->cursor;
+ const bool moved = d->cursor.movePosition(op, mode);
+ d->_q_updateCurrentCharFormatAndSelection();
+ ensureCursorVisible();
+ d->repaintOldAndNewSelection(oldSelection);
+ if (moved)
+ emit cursorPositionChanged();
+}
+
+bool QTextControl::canPaste() const
+{
+#ifndef QT_NO_CLIPBOARD
+ Q_D(const QTextControl);
+ if (d->interactionFlags & Qt::TextEditable) {
+ const QMimeData *md = QApplication::clipboard()->mimeData();
+ return md && canInsertFromMimeData(md);
+ }
+#endif
+ return false;
+}
+
+void QTextControl::setCursorIsFocusIndicator(bool b)
+{
+ Q_D(QTextControl);
+ d->cursorIsFocusIndicator = b;
+ d->repaintCursor();
+}
+
+bool QTextControl::cursorIsFocusIndicator() const
+{
+ Q_D(const QTextControl);
+ return d->cursorIsFocusIndicator;
+}
+
+#ifndef QT_NO_PRINTER
+void QTextControl::print(QPrinter *printer) const
+{
+#ifndef QT_NO_PRINTER
+ Q_D(const QTextControl);
+ if (printer && !printer->isValid())
+ return;
+ QTextDocument *tempDoc = 0;
+ const QTextDocument *doc = d->doc;
+ if (printer->printRange() == QPrinter::Selection) {
+ if (!d->cursor.hasSelection())
+ return;
+ tempDoc = new QTextDocument(const_cast<QTextDocument *>(doc));
+ tempDoc->setMetaInformation(QTextDocument::DocumentTitle, doc->metaInformation(QTextDocument::DocumentTitle));
+ tempDoc->setPageSize(doc->pageSize());
+ tempDoc->setDefaultFont(doc->defaultFont());
+ tempDoc->setUseDesignMetrics(doc->useDesignMetrics());
+ QTextCursor(tempDoc).insertFragment(d->cursor.selection());
+ doc = tempDoc;
+ }
+ doc->print(printer);
+ delete tempDoc;
+#endif
+}
+#endif // QT_NO_PRINTER
+
+QMimeData *QTextControl::createMimeDataFromSelection() const
+{
+ Q_D(const QTextControl);
+ const QTextDocumentFragment fragment(d->cursor);
+ return new QTextEditMimeData(fragment);
+}
+
+bool QTextControl::canInsertFromMimeData(const QMimeData *source) const
+{
+ Q_D(const QTextControl);
+ if (d->acceptRichText)
+ return (source->hasText() && !source->text().isEmpty())
+ || source->hasHtml()
+ || source->hasFormat(QLatin1String("application/x-qrichtext"))
+ || source->hasFormat(QLatin1String("application/x-qt-richtext"));
+ else
+ return source->hasText() && !source->text().isEmpty();
+}
+
+void QTextControl::insertFromMimeData(const QMimeData *source)
+{
+ Q_D(QTextControl);
+ if (!(d->interactionFlags & Qt::TextEditable) || !source)
+ return;
+
+ bool hasData = false;
+ QTextDocumentFragment fragment;
+#ifndef QT_NO_TEXTHTMLPARSER
+ if (source->hasFormat(QLatin1String("application/x-qrichtext")) && d->acceptRichText) {
+ // x-qrichtext is always UTF-8 (taken from Qt3 since we don't use it anymore).
+ QString richtext = QString::fromUtf8(source->data(QLatin1String("application/x-qrichtext")));
+ richtext.prepend(QLatin1String("<meta name=\"qrichtext\" content=\"1\" />"));
+ fragment = QTextDocumentFragment::fromHtml(richtext, d->doc);
+ hasData = true;
+ } else if (source->hasHtml() && d->acceptRichText) {
+ fragment = QTextDocumentFragment::fromHtml(source->html(), d->doc);
+ hasData = true;
+ } else {
+ QString text = source->text();
+ if (!text.isNull()) {
+ fragment = QTextDocumentFragment::fromPlainText(text);
+ hasData = true;
+ }
+ }
+#else
+ fragment = QTextDocumentFragment::fromPlainText(source->text());
+#endif // QT_NO_TEXTHTMLPARSER
+
+ if (hasData)
+ d->cursor.insertFragment(fragment);
+ ensureCursorVisible();
+}
+
+bool QTextControl::findNextPrevAnchor(const QTextCursor &startCursor, bool next, QTextCursor &newAnchor)
+{
+ Q_D(QTextControl);
+
+ int anchorStart = -1;
+ QString anchorHref;
+ int anchorEnd = -1;
+
+ if (next) {
+ const int startPos = startCursor.selectionEnd();
+
+ QTextBlock block = d->doc->findBlock(startPos);
+ QTextBlock::Iterator it = block.begin();
+
+ while (!it.atEnd() && it.fragment().position() < startPos)
+ ++it;
+
+ while (block.isValid()) {
+ anchorStart = -1;
+
+ // find next anchor
+ for (; !it.atEnd(); ++it) {
+ const QTextFragment fragment = it.fragment();
+ const QTextCharFormat fmt = fragment.charFormat();
+
+ if (fmt.isAnchor() && fmt.hasProperty(QTextFormat::AnchorHref)) {
+ anchorStart = fragment.position();
+ anchorHref = fmt.anchorHref();
+ break;
+ }
+ }
+
+ if (anchorStart != -1) {
+ anchorEnd = -1;
+
+ // find next non-anchor fragment
+ for (; !it.atEnd(); ++it) {
+ const QTextFragment fragment = it.fragment();
+ const QTextCharFormat fmt = fragment.charFormat();
+
+ if (!fmt.isAnchor() || fmt.anchorHref() != anchorHref) {
+ anchorEnd = fragment.position();
+ break;
+ }
+ }
+
+ if (anchorEnd == -1)
+ anchorEnd = block.position() + block.length() - 1;
+
+ // make found selection
+ break;
+ }
+
+ block = block.next();
+ it = block.begin();
+ }
+ } else {
+ int startPos = startCursor.selectionStart();
+ if (startPos > 0)
+ --startPos;
+
+ QTextBlock block = d->doc->findBlock(startPos);
+ QTextBlock::Iterator blockStart = block.begin();
+ QTextBlock::Iterator it = block.end();
+
+ if (startPos == block.position()) {
+ it = block.begin();
+ } else {
+ do {
+ if (it == blockStart) {
+ it = QTextBlock::Iterator();
+ block = QTextBlock();
+ } else {
+ --it;
+ }
+ } while (!it.atEnd() && it.fragment().position() + it.fragment().length() - 1 > startPos);
+ }
+
+ while (block.isValid()) {
+ anchorStart = -1;
+
+ if (!it.atEnd()) {
+ do {
+ const QTextFragment fragment = it.fragment();
+ const QTextCharFormat fmt = fragment.charFormat();
+
+ if (fmt.isAnchor() && fmt.hasProperty(QTextFormat::AnchorHref)) {
+ anchorStart = fragment.position() + fragment.length();
+ anchorHref = fmt.anchorHref();
+ break;
+ }
+
+ if (it == blockStart)
+ it = QTextBlock::Iterator();
+ else
+ --it;
+ } while (!it.atEnd());
+ }
+
+ if (anchorStart != -1 && !it.atEnd()) {
+ anchorEnd = -1;
+
+ do {
+ const QTextFragment fragment = it.fragment();
+ const QTextCharFormat fmt = fragment.charFormat();
+
+ if (!fmt.isAnchor() || fmt.anchorHref() != anchorHref) {
+ anchorEnd = fragment.position() + fragment.length();
+ break;
+ }
+
+ if (it == blockStart)
+ it = QTextBlock::Iterator();
+ else
+ --it;
+ } while (!it.atEnd());
+
+ if (anchorEnd == -1)
+ anchorEnd = qMax(0, block.position());
+
+ break;
+ }
+
+ block = block.previous();
+ it = block.end();
+ if (it != block.begin())
+ --it;
+ blockStart = block.begin();
+ }
+
+ }
+
+ if (anchorStart != -1 && anchorEnd != -1) {
+ newAnchor = d->cursor;
+ newAnchor.setPosition(anchorStart);
+ newAnchor.setPosition(anchorEnd, QTextCursor::KeepAnchor);
+ return true;
+ }
+
+ return false;
+}
+
+void QTextControlPrivate::activateLinkUnderCursor(QString href)
+{
+ QTextCursor oldCursor = cursor;
+
+ if (href.isEmpty()) {
+ QTextCursor tmp = cursor;
+ if (tmp.selectionStart() != tmp.position())
+ tmp.setPosition(tmp.selectionStart());
+ tmp.movePosition(QTextCursor::NextCharacter);
+ href = tmp.charFormat().anchorHref();
+ }
+ if (href.isEmpty())
+ return;
+
+ if (!cursor.hasSelection()) {
+ QTextBlock block = cursor.block();
+ const int cursorPos = cursor.position();
+
+ QTextBlock::Iterator it = block.begin();
+ QTextBlock::Iterator linkFragment;
+
+ for (; !it.atEnd(); ++it) {
+ QTextFragment fragment = it.fragment();
+ const int fragmentPos = fragment.position();
+ if (fragmentPos <= cursorPos &&
+ fragmentPos + fragment.length() > cursorPos) {
+ linkFragment = it;
+ break;
+ }
+ }
+
+ if (!linkFragment.atEnd()) {
+ it = linkFragment;
+ cursor.setPosition(it.fragment().position());
+ if (it != block.begin()) {
+ do {
+ --it;
+ QTextFragment fragment = it.fragment();
+ if (fragment.charFormat().anchorHref() != href)
+ break;
+ cursor.setPosition(fragment.position());
+ } while (it != block.begin());
+ }
+
+ for (it = linkFragment; !it.atEnd(); ++it) {
+ QTextFragment fragment = it.fragment();
+ if (fragment.charFormat().anchorHref() != href)
+ break;
+ cursor.setPosition(fragment.position() + fragment.length(), QTextCursor::KeepAnchor);
+ }
+ }
+ }
+
+ if (hasFocus) {
+ cursorIsFocusIndicator = true;
+ } else {
+ cursorIsFocusIndicator = false;
+ cursor.clearSelection();
+ }
+ repaintOldAndNewSelection(oldCursor);
+
+#ifndef QT_NO_DESKTOPSERVICES
+ if (openExternalLinks)
+ QDesktopServices::openUrl(href);
+ else
+#endif
+ emit q_func()->linkActivated(href);
+}
+
+#ifndef QT_NO_TOOLTIP
+void QTextControlPrivate::showToolTip(const QPoint &globalPos, const QPointF &pos, QWidget *contextWidget)
+{
+ const QString toolTip = q_func()->cursorForPosition(pos).charFormat().toolTip();
+ if (toolTip.isEmpty())
+ return;
+ QToolTip::showText(globalPos, toolTip, contextWidget);
+}
+#endif // QT_NO_TOOLTIP
+
+bool QTextControl::setFocusToNextOrPreviousAnchor(bool next)
+{
+ Q_D(QTextControl);
+
+ if (!(d->interactionFlags & Qt::LinksAccessibleByKeyboard))
+ return false;
+
+ QRectF crect = selectionRect();
+ emit updateRequest(crect);
+
+ // If we don't have a current anchor, we start from the start/end
+ if (!d->cursor.hasSelection()) {
+ d->cursor = QTextCursor(d->doc);
+ if (next)
+ d->cursor.movePosition(QTextCursor::Start);
+ else
+ d->cursor.movePosition(QTextCursor::End);
+ }
+
+ QTextCursor newAnchor;
+ if (findNextPrevAnchor(d->cursor, next, newAnchor)) {
+ d->cursor = newAnchor;
+ d->cursorIsFocusIndicator = true;
+ } else {
+ d->cursor.clearSelection();
+ }
+
+ if (d->cursor.hasSelection()) {
+ crect = selectionRect();
+ emit updateRequest(crect);
+ emit visibilityRequest(crect);
+ return true;
+ } else {
+ return false;
+ }
+}
+
+bool QTextControl::setFocusToAnchor(const QTextCursor &newCursor)
+{
+ Q_D(QTextControl);
+
+ if (!(d->interactionFlags & Qt::LinksAccessibleByKeyboard))
+ return false;
+
+ // Verify that this is an anchor.
+ const QString anchorHref = d->anchorForCursor(newCursor);
+ if (anchorHref.isEmpty())
+ return false;
+
+ // and process it
+ QRectF crect = selectionRect();
+ emit updateRequest(crect);
+
+ d->cursor.setPosition(newCursor.selectionStart());
+ d->cursor.setPosition(newCursor.selectionEnd(), QTextCursor::KeepAnchor);
+ d->cursorIsFocusIndicator = true;
+
+ crect = selectionRect();
+ emit updateRequest(crect);
+ emit visibilityRequest(crect);
+ return true;
+}
+
+void QTextControl::setTextInteractionFlags(Qt::TextInteractionFlags flags)
+{
+ Q_D(QTextControl);
+ if (flags == d->interactionFlags)
+ return;
+ d->interactionFlags = flags;
+
+ if (d->hasFocus)
+ d->setBlinkingCursorEnabled(flags & Qt::TextEditable);
+}
+
+Qt::TextInteractionFlags QTextControl::textInteractionFlags() const
+{
+ Q_D(const QTextControl);
+ return d->interactionFlags;
+}
+
+void QTextControl::mergeCurrentCharFormat(const QTextCharFormat &modifier)
+{
+ Q_D(QTextControl);
+ d->cursor.mergeCharFormat(modifier);
+ d->updateCurrentCharFormat();
+}
+
+void QTextControl::setCurrentCharFormat(const QTextCharFormat &format)
+{
+ Q_D(QTextControl);
+ d->cursor.setCharFormat(format);
+ d->updateCurrentCharFormat();
+}
+
+QTextCharFormat QTextControl::currentCharFormat() const
+{
+ Q_D(const QTextControl);
+ return d->cursor.charFormat();
+}
+
+void QTextControl::insertPlainText(const QString &text)
+{
+ Q_D(QTextControl);
+ d->cursor.insertText(text);
+}
+
+#ifndef QT_NO_TEXTHTMLPARSER
+void QTextControl::insertHtml(const QString &text)
+{
+ Q_D(QTextControl);
+ d->cursor.insertHtml(text);
+}
+#endif // QT_NO_TEXTHTMLPARSER
+
+QPointF QTextControl::anchorPosition(const QString &name) const
+{
+ Q_D(const QTextControl);
+ if (name.isEmpty())
+ return QPointF();
+
+ QRectF r;
+ for (QTextBlock block = d->doc->begin(); block.isValid(); block = block.next()) {
+ QTextCharFormat format = block.charFormat();
+ if (format.isAnchor() && format.anchorNames().contains(name)) {
+ r = d->rectForPosition(block.position());
+ break;
+ }
+
+ for (QTextBlock::Iterator it = block.begin(); !it.atEnd(); ++it) {
+ QTextFragment fragment = it.fragment();
+ format = fragment.charFormat();
+ if (format.isAnchor() && format.anchorNames().contains(name)) {
+ r = d->rectForPosition(fragment.position());
+ block = QTextBlock();
+ break;
+ }
+ }
+ }
+ if (!r.isValid())
+ return QPointF();
+ return QPointF(0, r.top());
+}
+
+void QTextControl::adjustSize()
+{
+ Q_D(QTextControl);
+ d->doc->adjustSize();
+}
+
+bool QTextControl::find(const QString &exp, QTextDocument::FindFlags options)
+{
+ Q_D(QTextControl);
+ QTextCursor search = d->doc->find(exp, d->cursor, options);
+ if (search.isNull())
+ return false;
+
+ setTextCursor(search);
+ return true;
+}
+
+
+
+void QTextControlPrivate::append(const QString &text, Qt::TextFormat format)
+{
+ QTextCursor tmp(doc);
+ tmp.beginEditBlock();
+ tmp.movePosition(QTextCursor::End);
+
+ if (!doc->isEmpty())
+ tmp.insertBlock(cursor.blockFormat(), cursor.charFormat());
+ else
+ tmp.setCharFormat(cursor.charFormat());
+
+ // preserve the char format
+ QTextCharFormat oldCharFormat = cursor.charFormat();
+
+#ifndef QT_NO_TEXTHTMLPARSER
+ if (format == Qt::RichText || (format == Qt::AutoText && Qt::mightBeRichText(text))) {
+ tmp.insertHtml(text);
+ } else {
+ tmp.insertText(text);
+ }
+#else
+ tmp.insertText(text);
+#endif // QT_NO_TEXTHTMLPARSER
+ if (!cursor.hasSelection())
+ cursor.setCharFormat(oldCharFormat);
+
+ tmp.endEditBlock();
+}
+
+void QTextControl::append(const QString &text)
+{
+ Q_D(QTextControl);
+ d->append(text, Qt::AutoText);
+}
+
+void QTextControl::appendHtml(const QString &html)
+{
+ Q_D(QTextControl);
+ d->append(html, Qt::RichText);
+}
+
+void QTextControl::appendPlainText(const QString &text)
+{
+ Q_D(QTextControl);
+ d->append(text, Qt::PlainText);
+}
+
+
+void QTextControl::ensureCursorVisible()
+{
+ Q_D(QTextControl);
+ QRectF crect = d->rectForPosition(d->cursor.position()).adjusted(-5, 0, 5, 0);
+ emit visibilityRequest(crect);
+ emit microFocusChanged();
+}
+
+QPalette QTextControl::palette() const
+{
+ Q_D(const QTextControl);
+ return d->palette;
+}
+
+void QTextControl::setPalette(const QPalette &pal)
+{
+ Q_D(QTextControl);
+ d->palette = pal;
+}
+
+QAbstractTextDocumentLayout::PaintContext QTextControl::getPaintContext(QWidget *widget) const
+{
+ Q_D(const QTextControl);
+
+ QAbstractTextDocumentLayout::PaintContext ctx;
+
+ ctx.selections = d->extraSelections;
+ ctx.palette = d->palette;
+ if (d->cursorOn && d->isEnabled) {
+ if (d->hideCursor)
+ ctx.cursorPosition = -1;
+ else if (d->preeditCursor != 0)
+ ctx.cursorPosition = - (d->preeditCursor + 2);
+ else
+ ctx.cursorPosition = d->cursor.position();
+ }
+
+ if (!d->dndFeedbackCursor.isNull())
+ ctx.cursorPosition = d->dndFeedbackCursor.position();
+#ifdef QT_KEYPAD_NAVIGATION
+ if (!QApplication::keypadNavigationEnabled() || d->hasEditFocus)
+#endif
+ if (d->cursor.hasSelection()) {
+ QAbstractTextDocumentLayout::Selection selection;
+ selection.cursor = d->cursor;
+ if (d->cursorIsFocusIndicator) {
+ QStyleOption opt;
+ opt.palette = ctx.palette;
+ QStyleHintReturnVariant ret;
+ QStyle *style = QApplication::style();
+ if (widget)
+ style = widget->style();
+ style->styleHint(QStyle::SH_TextControl_FocusIndicatorTextCharFormat, &opt, widget, &ret);
+ selection.format = qVariantValue<QTextFormat>(ret.variant).toCharFormat();
+ } else {
+ QPalette::ColorGroup cg = d->hasFocus ? QPalette::Active : QPalette::Inactive;
+ selection.format.setBackground(ctx.palette.brush(cg, QPalette::Highlight));
+ selection.format.setForeground(ctx.palette.brush(cg, QPalette::HighlightedText));
+ QStyleOption opt;
+ QStyle *style = QApplication::style();
+ if (widget) {
+ opt.initFrom(widget);
+ style = widget->style();
+ }
+ if (style->styleHint(QStyle::SH_RichText_FullWidthSelection, &opt, widget))
+ selection.format.setProperty(QTextFormat::FullWidthSelection, true);
+ }
+ ctx.selections.append(selection);
+ }
+
+ return ctx;
+}
+
+void QTextControl::drawContents(QPainter *p, const QRectF &rect, QWidget *widget)
+{
+ Q_D(QTextControl);
+ p->save();
+ QAbstractTextDocumentLayout::PaintContext ctx = getPaintContext(widget);
+ if (rect.isValid())
+ p->setClipRect(rect, Qt::IntersectClip);
+ ctx.clip = rect;
+
+ d->doc->documentLayout()->draw(p, ctx);
+ p->restore();
+}
+
+void QTextControlPrivate::_q_copyLink()
+{
+#ifndef QT_NO_CLIPBOARD
+ QMimeData *md = new QMimeData;
+ md->setText(linkToCopy);
+ QApplication::clipboard()->setMimeData(md);
+#endif
+}
+
+QInputContext *QTextControlPrivate::inputContext()
+{
+ QInputContext *ctx = contextWidget->inputContext();
+ if (!ctx && contextWidget->parentWidget())
+ ctx = contextWidget->parentWidget()->inputContext();
+ return ctx;
+}
+
+int QTextControl::hitTest(const QPointF &point, Qt::HitTestAccuracy accuracy) const
+{
+ Q_D(const QTextControl);
+ return d->doc->documentLayout()->hitTest(point, accuracy);
+}
+
+QRectF QTextControl::blockBoundingRect(const QTextBlock &block) const
+{
+ Q_D(const QTextControl);
+ return d->doc->documentLayout()->blockBoundingRect(block);
+}
+
+#ifndef QT_NO_CONTEXTMENU
+#define NUM_CONTROL_CHARACTERS 10
+const struct QUnicodeControlCharacter {
+ const char *text;
+ ushort character;
+} qt_controlCharacters[NUM_CONTROL_CHARACTERS] = {
+ { QT_TRANSLATE_NOOP("QUnicodeControlCharacterMenu", "LRM Left-to-right mark"), 0x200e },
+ { QT_TRANSLATE_NOOP("QUnicodeControlCharacterMenu", "RLM Right-to-left mark"), 0x200f },
+ { QT_TRANSLATE_NOOP("QUnicodeControlCharacterMenu", "ZWJ Zero width joiner"), 0x200d },
+ { QT_TRANSLATE_NOOP("QUnicodeControlCharacterMenu", "ZWNJ Zero width non-joiner"), 0x200c },
+ { QT_TRANSLATE_NOOP("QUnicodeControlCharacterMenu", "ZWSP Zero width space"), 0x200b },
+ { QT_TRANSLATE_NOOP("QUnicodeControlCharacterMenu", "LRE Start of left-to-right embedding"), 0x202a },
+ { QT_TRANSLATE_NOOP("QUnicodeControlCharacterMenu", "RLE Start of right-to-left embedding"), 0x202b },
+ { QT_TRANSLATE_NOOP("QUnicodeControlCharacterMenu", "LRO Start of left-to-right override"), 0x202d },
+ { QT_TRANSLATE_NOOP("QUnicodeControlCharacterMenu", "RLO Start of right-to-left override"), 0x202e },
+ { QT_TRANSLATE_NOOP("QUnicodeControlCharacterMenu", "PDF Pop directional formatting"), 0x202c },
+};
+
+QUnicodeControlCharacterMenu::QUnicodeControlCharacterMenu(QObject *_editWidget, QWidget *parent)
+ : QMenu(parent), editWidget(_editWidget)
+{
+ setTitle(tr("Insert Unicode control character"));
+ for (int i = 0; i < NUM_CONTROL_CHARACTERS; ++i) {
+ addAction(tr(qt_controlCharacters[i].text), this, SLOT(menuActionTriggered()));
+ }
+}
+
+void QUnicodeControlCharacterMenu::menuActionTriggered()
+{
+ QAction *a = qobject_cast<QAction *>(sender());
+ int idx = actions().indexOf(a);
+ if (idx < 0 || idx >= NUM_CONTROL_CHARACTERS)
+ return;
+ QChar c(qt_controlCharacters[idx].character);
+ QString str(c);
+
+#ifndef QT_NO_TEXTEDIT
+ if (QTextEdit *edit = qobject_cast<QTextEdit *>(editWidget)) {
+ edit->insertPlainText(str);
+ return;
+ }
+#endif
+ if (QTextControl *control = qobject_cast<QTextControl *>(editWidget)) {
+ control->insertPlainText(str);
+ }
+#ifndef QT_NO_LINEEDIT
+ if (QLineEdit *edit = qobject_cast<QLineEdit *>(editWidget)) {
+ edit->insert(str);
+ return;
+ }
+#endif
+}
+#endif // QT_NO_CONTEXTMENU
+
+QStringList QTextEditMimeData::formats() const
+{
+ if (!fragment.isEmpty())
+ return QStringList() << QString::fromLatin1("text/plain") << QString::fromLatin1("text/html")
+#ifndef QT_NO_TEXTODFWRITER
+ << QString::fromLatin1("application/vnd.oasis.opendocument.text")
+#endif
+ ;
+ else
+ return QMimeData::formats();
+}
+
+QVariant QTextEditMimeData::retrieveData(const QString &mimeType, QVariant::Type type) const
+{
+ if (!fragment.isEmpty())
+ setup();
+ return QMimeData::retrieveData(mimeType, type);
+}
+
+void QTextEditMimeData::setup() const
+{
+ QTextEditMimeData *that = const_cast<QTextEditMimeData *>(this);
+#ifndef QT_NO_TEXTHTMLPARSER
+ that->setData(QLatin1String("text/html"), fragment.toHtml("utf-8").toUtf8());
+#endif
+#ifndef QT_NO_TEXTODFWRITER
+ {
+ QBuffer buffer;
+ QTextDocumentWriter writer(&buffer, "ODF");
+ writer.write(fragment);
+ buffer.close();
+ that->setData(QLatin1String("application/vnd.oasis.opendocument.text"), buffer.data());
+ }
+#endif
+ that->setText(fragment.toPlainText());
+ fragment = QTextDocumentFragment();
+}
+
+QT_END_NAMESPACE
+
+#include "moc_qtextcontrol_p.cpp"
+
+#endif // QT_NO_TEXTCONTROL
diff --git a/src/gui/text/qtextcontrol_p.h b/src/gui/text/qtextcontrol_p.h
new file mode 100644
index 0000000000..e50540aa34
--- /dev/null
+++ b/src/gui/text/qtextcontrol_p.h
@@ -0,0 +1,303 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QTEXTCONTROL_P_H
+#define QTEXTCONTROL_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 <QtGui/qtextdocument.h>
+#include <QtGui/qtextoption.h>
+#include <QtGui/qtextcursor.h>
+#include <QtGui/qtextformat.h>
+#include <QtGui/qtextedit.h>
+#include <QtGui/qmenu.h>
+#include <QtCore/qrect.h>
+#include <QtGui/qabstracttextdocumentlayout.h>
+#include <QtGui/qtextdocumentfragment.h>
+
+#ifdef QT3_SUPPORT
+#include <QtGui/qtextobject.h>
+#include <QtGui/qtextlayout.h>
+#endif
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+class QStyleSheet;
+class QTextDocument;
+class QMenu;
+class QTextControlPrivate;
+class QMimeData;
+class QAbstractScrollArea;
+class QEvent;
+class QTimerEvent;
+
+class Q_GUI_EXPORT QTextControl : public QObject
+{
+ Q_OBJECT
+ Q_DECLARE_PRIVATE(QTextControl)
+#ifndef QT_NO_TEXTHTMLPARSER
+ Q_PROPERTY(QString html READ toHtml WRITE setHtml NOTIFY textChanged USER true)
+#endif
+ Q_PROPERTY(bool overwriteMode READ overwriteMode WRITE setOverwriteMode)
+ Q_PROPERTY(bool acceptRichText READ acceptRichText WRITE setAcceptRichText)
+ Q_PROPERTY(int cursorWidth READ cursorWidth WRITE setCursorWidth)
+ Q_PROPERTY(Qt::TextInteractionFlags textInteractionFlags READ textInteractionFlags WRITE setTextInteractionFlags)
+ Q_PROPERTY(bool openExternalLinks READ openExternalLinks WRITE setOpenExternalLinks)
+public:
+ explicit QTextControl(QObject *parent = 0);
+ explicit QTextControl(const QString &text, QObject *parent = 0);
+ explicit QTextControl(QTextDocument *doc, QObject *parent = 0);
+ virtual ~QTextControl();
+
+ void setDocument(QTextDocument *document);
+ QTextDocument *document() const;
+
+ void setTextCursor(const QTextCursor &cursor);
+ QTextCursor textCursor() const;
+
+ void setTextInteractionFlags(Qt::TextInteractionFlags flags);
+ Qt::TextInteractionFlags textInteractionFlags() const;
+
+ void mergeCurrentCharFormat(const QTextCharFormat &modifier);
+
+ void setCurrentCharFormat(const QTextCharFormat &format);
+ QTextCharFormat currentCharFormat() const;
+
+ bool find(const QString &exp, QTextDocument::FindFlags options = 0);
+
+ inline QString toPlainText() const
+ { return document()->toPlainText(); }
+#ifndef QT_NO_TEXTHTMLPARSER
+ inline QString toHtml() const
+ { return document()->toHtml(); }
+#endif
+
+ virtual void ensureCursorVisible();
+
+ virtual QVariant loadResource(int type, const QUrl &name);
+#ifndef QT_NO_CONTEXTMENU
+ QMenu *createStandardContextMenu(const QPointF &pos, QWidget *parent);
+#endif
+
+ QTextCursor cursorForPosition(const QPointF &pos) const;
+ QRectF cursorRect(const QTextCursor &cursor) const;
+ QRectF cursorRect() const;
+ QRectF selectionRect(const QTextCursor &cursor) const;
+ QRectF selectionRect() const;
+
+ QString anchorAt(const QPointF &pos) const;
+ QPointF anchorPosition(const QString &name) const;
+
+ QString anchorAtCursor() const;
+
+ bool overwriteMode() const;
+ void setOverwriteMode(bool overwrite);
+
+ int cursorWidth() const;
+ void setCursorWidth(int width);
+
+ bool acceptRichText() const;
+ void setAcceptRichText(bool accept);
+
+#ifndef QT_NO_TEXTEDIT
+ void setExtraSelections(const QList<QTextEdit::ExtraSelection> &selections);
+ QList<QTextEdit::ExtraSelection> extraSelections() const;
+#endif
+
+ void setTextWidth(qreal width);
+ qreal textWidth() const;
+ QSizeF size() const;
+
+ void setOpenExternalLinks(bool open);
+ bool openExternalLinks() const;
+
+ void moveCursor(QTextCursor::MoveOperation op, QTextCursor::MoveMode mode = QTextCursor::MoveAnchor);
+
+ bool canPaste() const;
+
+ void setCursorIsFocusIndicator(bool b);
+ bool cursorIsFocusIndicator() const;
+
+#ifndef QT_NO_PRINTER
+ void print(QPrinter *printer) const;
+#endif
+
+ virtual int hitTest(const QPointF &point, Qt::HitTestAccuracy accuracy) const;
+ virtual QRectF blockBoundingRect(const QTextBlock &block) const;
+ QAbstractTextDocumentLayout::PaintContext getPaintContext(QWidget *widget) const;
+
+
+
+public Q_SLOTS:
+ void setPlainText(const QString &text);
+ void setHtml(const QString &text);
+
+#ifndef QT_NO_CLIPBOARD
+ void cut();
+ void copy();
+ void paste();
+#endif
+
+ void undo();
+ void redo();
+
+ void clear();
+ void selectAll();
+
+ void insertPlainText(const QString &text);
+#ifndef QT_NO_TEXTHTMLPARSER
+ void insertHtml(const QString &text);
+#endif
+
+ void append(const QString &text);
+ void appendHtml(const QString &html);
+ void appendPlainText(const QString &text);
+
+ void adjustSize();
+
+Q_SIGNALS:
+ void textChanged();
+ void undoAvailable(bool b);
+ void redoAvailable(bool b);
+ void currentCharFormatChanged(const QTextCharFormat &format);
+ void copyAvailable(bool b);
+ void selectionChanged();
+ void cursorPositionChanged();
+
+ // control signals
+ void updateRequest(const QRectF &rect = QRectF());
+ void documentSizeChanged(const QSizeF &);
+ void blockCountChanged(int newBlockCount);
+ void visibilityRequest(const QRectF &rect);
+ void microFocusChanged();
+ void linkActivated(const QString &link);
+ void linkHovered(const QString &);
+ void modificationChanged(bool m);
+
+public:
+ // control properties
+ QPalette palette() const;
+ void setPalette(const QPalette &pal);
+
+ virtual void processEvent(QEvent *e, const QMatrix &matrix, QWidget *contextWidget = 0);
+ void processEvent(QEvent *e, const QPointF &coordinateOffset = QPointF(), QWidget *contextWidget = 0);
+
+ // control methods
+ void drawContents(QPainter *painter, const QRectF &rect = QRectF(), QWidget *widget = 0);
+
+ void setFocus(bool focus, Qt::FocusReason = Qt::OtherFocusReason);
+
+ virtual QVariant inputMethodQuery(Qt::InputMethodQuery property) const;
+
+ virtual QMimeData *createMimeDataFromSelection() const;
+ virtual bool canInsertFromMimeData(const QMimeData *source) const;
+ virtual void insertFromMimeData(const QMimeData *source);
+
+ bool setFocusToAnchor(const QTextCursor &newCursor);
+ bool setFocusToNextOrPreviousAnchor(bool next);
+ bool findNextPrevAnchor(const QTextCursor& from, bool next, QTextCursor& newAnchor);
+
+protected:
+ virtual void timerEvent(QTimerEvent *e);
+
+ virtual bool event(QEvent *e);
+
+private:
+ Q_DISABLE_COPY(QTextControl)
+ Q_PRIVATE_SLOT(d_func(), void _q_updateCurrentCharFormatAndSelection())
+ Q_PRIVATE_SLOT(d_func(), void _q_emitCursorPosChanged(const QTextCursor &))
+ Q_PRIVATE_SLOT(d_func(), void _q_deleteSelected())
+ Q_PRIVATE_SLOT(d_func(), void _q_copyLink())
+ Q_PRIVATE_SLOT(d_func(), void _q_updateBlock(const QTextBlock &))
+ Q_PRIVATE_SLOT(d_func(), void _q_documentLayoutChanged())
+};
+
+
+#ifndef QT_NO_CONTEXTMENU
+class QUnicodeControlCharacterMenu : public QMenu
+{
+ Q_OBJECT
+public:
+ QUnicodeControlCharacterMenu(QObject *editWidget, QWidget *parent);
+
+private Q_SLOTS:
+ void menuActionTriggered();
+
+private:
+ QObject *editWidget;
+};
+#endif // QT_NO_CONTEXTMENU
+
+
+// also used by QLabel
+class QTextEditMimeData : public QMimeData
+{
+public:
+ inline QTextEditMimeData(const QTextDocumentFragment &aFragment) : fragment(aFragment) {}
+
+ virtual QStringList formats() const;
+protected:
+ virtual QVariant retrieveData(const QString &mimeType, QVariant::Type type) const;
+private:
+ void setup() const;
+
+ mutable QTextDocumentFragment fragment;
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QTEXTCONTROL_H
diff --git a/src/gui/text/qtextcontrol_p_p.h b/src/gui/text/qtextcontrol_p_p.h
new file mode 100644
index 0000000000..06955ce9ac
--- /dev/null
+++ b/src/gui/text/qtextcontrol_p_p.h
@@ -0,0 +1,219 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QTEXTCONTROL_P_P_H
+#define QTEXTCONTROL_P_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 "QtGui/qtextdocumentfragment.h"
+#include "QtGui/qscrollbar.h"
+#include "QtGui/qtextcursor.h"
+#include "QtGui/qtextformat.h"
+#include "QtGui/qmenu.h"
+#include "QtGui/qabstracttextdocumentlayout.h"
+#include "QtCore/qbasictimer.h"
+#include "QtCore/qpointer.h"
+#include "private/qobject_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class QMimeData;
+class QAbstractScrollArea;
+class QInputContext;
+
+class QTextControlPrivate : public QObjectPrivate
+{
+ Q_DECLARE_PUBLIC(QTextControl)
+public:
+ QTextControlPrivate();
+
+ bool cursorMoveKeyEvent(QKeyEvent *e);
+
+ void updateCurrentCharFormat();
+
+ void indent();
+ void outdent();
+
+ void gotoNextTableCell();
+ void gotoPreviousTableCell();
+
+ void createAutoBulletList();
+
+ void init(Qt::TextFormat format = Qt::RichText, const QString &text = QString(),
+ QTextDocument *document = 0);
+ void setContent(Qt::TextFormat format = Qt::RichText, const QString &text = QString(),
+ QTextDocument *document = 0);
+ void startDrag();
+
+ void paste(const QMimeData *source);
+
+ void setCursorPosition(const QPointF &pos);
+ void setCursorPosition(int pos, QTextCursor::MoveMode mode = QTextCursor::MoveAnchor);
+
+ void repaintCursor();
+ inline void repaintSelection()
+ { repaintOldAndNewSelection(QTextCursor()); }
+ void repaintOldAndNewSelection(const QTextCursor &oldSelection);
+
+ void selectionChanged(bool forceEmitSelectionChanged = false);
+
+ void _q_updateCurrentCharFormatAndSelection();
+
+#ifndef QT_NO_CLIPBOARD
+ void setClipboardSelection();
+#endif
+
+ void _q_emitCursorPosChanged(const QTextCursor &someCursor);
+
+ void setBlinkingCursorEnabled(bool enable);
+
+ void extendWordwiseSelection(int suggestedNewPosition, qreal mouseXPosition);
+ void extendBlockwiseSelection(int suggestedNewPosition);
+
+ void _q_deleteSelected();
+
+ void _q_setCursorAfterUndoRedo(int undoPosition, int charsAdded, int charsRemoved);
+
+ QRectF cursorRectPlusUnicodeDirectionMarkers(const QTextCursor &cursor) const;
+ QRectF rectForPosition(int position) const;
+ QRectF selectionRect(const QTextCursor &cursor) const;
+ inline QRectF selectionRect() const
+ { return selectionRect(this->cursor); }
+
+ QString anchorForCursor(const QTextCursor &anchor) const;
+
+ void keyPressEvent(QKeyEvent *e);
+ void mousePressEvent(Qt::MouseButton button, const QPointF &pos,
+ Qt::KeyboardModifiers modifiers,
+ Qt::MouseButtons buttons,
+ const QPoint &globalPos = QPoint());
+ void mouseMoveEvent(Qt::MouseButtons buttons, const QPointF &pos);
+ void mouseReleaseEvent(Qt::MouseButton button, const QPointF &pos);
+ void mouseDoubleClickEvent(QEvent *e, Qt::MouseButton button, const QPointF &pos);
+ void contextMenuEvent(const QPoint &screenPos, const QPointF &docPos, QWidget *contextWidget);
+ void focusEvent(QFocusEvent *e);
+#ifdef QT_KEYPAD_NAVIGATION
+ void editFocusEvent(QEvent *e);
+#endif
+ bool dragEnterEvent(QEvent *e, const QMimeData *mimeData);
+ void dragLeaveEvent();
+ bool dragMoveEvent(QEvent *e, const QMimeData *mimeData, const QPointF &pos);
+ bool dropEvent(const QMimeData *mimeData, const QPointF &pos, Qt::DropAction dropAction, QWidget *source);
+
+ void inputMethodEvent(QInputMethodEvent *);
+
+ void activateLinkUnderCursor(QString href = QString());
+
+#ifndef QT_NO_TOOLTIP
+ void showToolTip(const QPoint &globalPos, const QPointF &pos, QWidget *contextWidget);
+#endif
+
+ void append(const QString &text, Qt::TextFormat format = Qt::AutoText);
+
+ QInputContext *inputContext();
+
+ QTextDocument *doc;
+ bool cursorOn;
+ QTextCursor cursor;
+ bool cursorIsFocusIndicator;
+ QTextCharFormat lastCharFormat;
+
+ QTextCursor dndFeedbackCursor;
+
+ Qt::TextInteractionFlags interactionFlags;
+
+ QBasicTimer cursorBlinkTimer;
+ QBasicTimer trippleClickTimer;
+ QPointF trippleClickPoint;
+
+ bool mousePressed;
+
+ bool mightStartDrag;
+ QPoint dragStartPos;
+ QPointer<QWidget> contextWidget;
+
+ bool lastSelectionState;
+
+ bool ignoreAutomaticScrollbarAdjustement;
+
+ QTextCursor selectedWordOnDoubleClick;
+ QTextCursor selectedBlockOnTrippleClick;
+
+ bool overwriteMode;
+ bool acceptRichText;
+
+ int preeditCursor;
+ bool hideCursor; // used to hide the cursor in the preedit area
+
+ QVector<QAbstractTextDocumentLayout::Selection> extraSelections;
+
+ QPalette palette;
+ bool hasFocus;
+#ifdef QT_KEYPAD_NAVIGATION
+ bool hasEditFocus;
+#endif
+ bool isEnabled;
+
+ QString highlightedAnchor; // Anchor below cursor
+ QString anchorOnMousePress;
+ bool hadSelectionOnMousePress;
+
+ bool openExternalLinks;
+
+ QString linkToCopy;
+ void _q_copyLink();
+ void _q_updateBlock(const QTextBlock &);
+ void _q_documentLayoutChanged();
+};
+
+QT_END_NAMESPACE
+
+#endif // QTEXTCONTROL_P_H
diff --git a/src/gui/text/qtextcursor.cpp b/src/gui/text/qtextcursor.cpp
new file mode 100644
index 0000000000..c327b9f2c9
--- /dev/null
+++ b/src/gui/text/qtextcursor.cpp
@@ -0,0 +1,2420 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qtextcursor.h"
+#include "qtextcursor_p.h"
+#include "qglobal.h"
+#include "qtextdocumentfragment.h"
+#include "qtextdocumentfragment_p.h"
+#include "qtextlist.h"
+#include "qtexttable.h"
+#include "qtexttable_p.h"
+#include "qtextengine_p.h"
+#include "qabstracttextdocumentlayout.h"
+
+#include <qtextlayout.h>
+#include <qdebug.h>
+
+QT_BEGIN_NAMESPACE
+
+enum {
+ AdjustPrev = 0x1,
+ AdjustUp = 0x3,
+ AdjustNext = 0x4,
+ AdjustDown = 0x12
+};
+
+QTextCursorPrivate::QTextCursorPrivate(QTextDocumentPrivate *p)
+ : priv(p), x(0), position(0), anchor(0), adjusted_anchor(0),
+ currentCharFormat(-1), visualNavigation(false)
+{
+ priv->addCursor(this);
+}
+
+QTextCursorPrivate::QTextCursorPrivate(const QTextCursorPrivate &rhs)
+ : QSharedData(rhs)
+{
+ position = rhs.position;
+ anchor = rhs.anchor;
+ adjusted_anchor = rhs.adjusted_anchor;
+ priv = rhs.priv;
+ x = rhs.x;
+ currentCharFormat = rhs.currentCharFormat;
+ visualNavigation = rhs.visualNavigation;
+ priv->addCursor(this);
+}
+
+QTextCursorPrivate::~QTextCursorPrivate()
+{
+ if (priv)
+ priv->removeCursor(this);
+}
+
+QTextCursorPrivate::AdjustResult QTextCursorPrivate::adjustPosition(int positionOfChange, int charsAddedOrRemoved, QTextUndoCommand::Operation op)
+{
+ QTextCursorPrivate::AdjustResult result = QTextCursorPrivate::CursorMoved;
+ // not(!) <= , so that inserting text adjusts the cursor correctly
+ if (position < positionOfChange ||
+ (position == positionOfChange && op == QTextUndoCommand::KeepCursor)) {
+ result = CursorUnchanged;
+ } else {
+ if (charsAddedOrRemoved < 0 && position < positionOfChange - charsAddedOrRemoved)
+ position = positionOfChange;
+ else
+ position += charsAddedOrRemoved;
+
+ currentCharFormat = -1;
+ }
+
+ if (anchor >= positionOfChange
+ && (anchor != positionOfChange || op != QTextUndoCommand::KeepCursor)) {
+ if (charsAddedOrRemoved < 0 && anchor < positionOfChange - charsAddedOrRemoved)
+ anchor = positionOfChange;
+ else
+ anchor += charsAddedOrRemoved;
+ }
+
+ if (adjusted_anchor >= positionOfChange
+ && (adjusted_anchor != positionOfChange || op != QTextUndoCommand::KeepCursor)) {
+ if (charsAddedOrRemoved < 0 && adjusted_anchor < positionOfChange - charsAddedOrRemoved)
+ adjusted_anchor = positionOfChange;
+ else
+ adjusted_anchor += charsAddedOrRemoved;
+ }
+
+ return result;
+}
+
+void QTextCursorPrivate::setX()
+{
+ if (priv && priv->isInEditBlock()) {
+ x = -1; // mark dirty
+ return;
+ }
+
+ QTextBlock block = this->block();
+ const QTextLayout *layout = blockLayout(block);
+ int pos = position - block.position();
+
+ QTextLine line = layout->lineForTextPosition(pos);
+ if (line.isValid())
+ x = line.cursorToX(pos);
+ else
+ x = -1; // delayed init. Makes movePosition() call setX later on again.
+}
+
+void QTextCursorPrivate::remove()
+{
+ if (anchor == position)
+ return;
+ priv->beginEditBlock();
+ currentCharFormat = -1;
+ int pos1 = position;
+ int pos2 = adjusted_anchor;
+ QTextUndoCommand::Operation op = QTextUndoCommand::KeepCursor;
+ if (pos1 > pos2) {
+ pos1 = adjusted_anchor;
+ pos2 = position;
+ op = QTextUndoCommand::MoveCursor;
+ }
+
+ // deleting inside table? -> delete only content
+ QTextTable *table = complexSelectionTable();
+ if (table) {
+ int startRow, startCol, numRows, numCols;
+ selectedTableCells(&startRow, &numRows, &startCol, &numCols);
+ clearCells(table, startRow, startCol, numRows, numCols, op);
+ } else {
+ priv->remove(pos1, pos2-pos1, op);
+ }
+
+ adjusted_anchor = anchor = position;
+ priv->endEditBlock();
+}
+
+void QTextCursorPrivate::clearCells(QTextTable *table, int startRow, int startCol, int numRows, int numCols, QTextUndoCommand::Operation op)
+{
+ priv->beginEditBlock();
+
+ for (int row = startRow; row < startRow + numRows; ++row)
+ for (int col = startCol; col < startCol + numCols; ++col) {
+ QTextTableCell cell = table->cellAt(row, col);
+ const int startPos = cell.firstPosition();
+ const int endPos = cell.lastPosition();
+ Q_ASSERT(startPos <= endPos);
+ priv->remove(startPos, endPos - startPos, op);
+ }
+
+ priv->endEditBlock();
+}
+
+bool QTextCursorPrivate::canDelete(int pos) const
+{
+ QTextDocumentPrivate::FragmentIterator fit = priv->find(pos);
+ QTextCharFormat fmt = priv->formatCollection()->charFormat((*fit)->format);
+ return (fmt.objectIndex() == -1 || fmt.objectType() == QTextFormat::ImageObject);
+}
+
+void QTextCursorPrivate::insertBlock(const QTextBlockFormat &format, const QTextCharFormat &charFormat)
+{
+ QTextFormatCollection *formats = priv->formatCollection();
+ int idx = formats->indexForFormat(format);
+ Q_ASSERT(formats->format(idx).isBlockFormat());
+
+ priv->insertBlock(position, idx, formats->indexForFormat(charFormat));
+ currentCharFormat = -1;
+}
+
+void QTextCursorPrivate::adjustCursor(QTextCursor::MoveOperation m)
+{
+ adjusted_anchor = anchor;
+ if (position == anchor)
+ return;
+
+ QTextFrame *f_position = priv->frameAt(position);
+ QTextFrame *f_anchor = priv->frameAt(adjusted_anchor);
+
+ if (f_position != f_anchor) {
+ // find common parent frame
+ QList<QTextFrame *> positionChain;
+ QList<QTextFrame *> anchorChain;
+ QTextFrame *f = f_position;
+ while (f) {
+ positionChain.prepend(f);
+ f = f->parentFrame();
+ }
+ f = f_anchor;
+ while (f) {
+ anchorChain.prepend(f);
+ f = f->parentFrame();
+ }
+ Q_ASSERT(positionChain.at(0) == anchorChain.at(0));
+ int i = 1;
+ int l = qMin(positionChain.size(), anchorChain.size());
+ for (; i < l; ++i) {
+ if (positionChain.at(i) != anchorChain.at(i))
+ break;
+ }
+
+ if (m <= QTextCursor::WordLeft) {
+ if (i < positionChain.size())
+ position = positionChain.at(i)->firstPosition() - 1;
+ } else {
+ if (i < positionChain.size())
+ position = positionChain.at(i)->lastPosition() + 1;
+ }
+ if (position < adjusted_anchor) {
+ if (i < anchorChain.size())
+ adjusted_anchor = anchorChain.at(i)->lastPosition() + 1;
+ } else {
+ if (i < anchorChain.size())
+ adjusted_anchor = anchorChain.at(i)->firstPosition() - 1;
+ }
+
+ f_position = positionChain.at(i-1);
+ }
+
+ // same frame, either need to adjust to cell boundaries or return
+ QTextTable *table = qobject_cast<QTextTable *>(f_position);
+ if (!table)
+ return;
+
+ QTextTableCell c_position = table->cellAt(position);
+ QTextTableCell c_anchor = table->cellAt(adjusted_anchor);
+ if (c_position != c_anchor) {
+ bool before;
+ int col_position = c_position.column();
+ int col_anchor = c_anchor.column();
+ if (col_position == col_anchor) {
+ before = c_position.row() < c_anchor.row();
+ } else {
+ before = col_position < col_anchor;
+ }
+
+ // adjust to cell boundaries
+ if (m <= QTextCursor::WordLeft) {
+ position = c_position.firstPosition();
+ if (!before)
+ --position;
+ } else {
+ position = c_position.lastPosition();
+ if (before)
+ ++position;
+ }
+ if (position < adjusted_anchor)
+ adjusted_anchor = c_anchor.lastPosition();
+ else
+ adjusted_anchor = c_anchor.firstPosition();
+ }
+ currentCharFormat = -1;
+}
+
+void QTextCursorPrivate::aboutToRemoveCell(int from, int to)
+{
+ Q_ASSERT(from <= to);
+ if (position == anchor)
+ return;
+
+ QTextTable *t = qobject_cast<QTextTable *>(priv->frameAt(position));
+ if (!t)
+ return;
+ QTextTableCell removedCellFrom = t->cellAt(from);
+ QTextTableCell removedCellEnd = t->cellAt(to);
+ if (! removedCellFrom.isValid() || !removedCellEnd.isValid())
+ return;
+
+ int curFrom = position;
+ int curTo = adjusted_anchor;
+ if (curTo < curFrom)
+ qSwap(curFrom, curTo);
+
+ QTextTableCell cellStart = t->cellAt(curFrom);
+ QTextTableCell cellEnd = t->cellAt(curTo);
+
+ if (cellStart.row() >= removedCellFrom.row() && cellEnd.row() <= removedCellEnd.row()
+ && cellStart.column() >= removedCellFrom.column()
+ && cellEnd.column() <= removedCellEnd.column()) { // selection is completely removed
+ // find a new position, as close as possible to where we were.
+ QTextTableCell cell;
+ if (removedCellFrom.row() == 0 && removedCellEnd.row() == t->rows()-1) // removed n columns
+ cell = t->cellAt(cellStart.row(), removedCellEnd.column()+1);
+ else if (removedCellFrom.column() == 0 && removedCellEnd.column() == t->columns()-1) // removed n rows
+ cell = t->cellAt(removedCellEnd.row() + 1, cellStart.column());
+
+ int newPosition;
+ if (cell.isValid())
+ newPosition = cell.firstPosition();
+ else
+ newPosition = t->lastPosition()+1;
+
+ setPosition(newPosition);
+ anchor = newPosition;
+ adjusted_anchor = newPosition;
+ x = 0;
+ }
+ else if (cellStart.row() >= removedCellFrom.row() && cellStart.row() <= removedCellEnd.row()
+ && cellEnd.row() > removedCellEnd.row()) {
+ int newPosition = t->cellAt(removedCellEnd.row() + 1, cellStart.column()).firstPosition();
+ if (position < anchor)
+ position = newPosition;
+ else
+ anchor = adjusted_anchor = newPosition;
+ }
+ else if (cellStart.column() >= removedCellFrom.column() && cellStart.column() <= removedCellEnd.column()
+ && cellEnd.column() > removedCellEnd.column()) {
+ int newPosition = t->cellAt(cellStart.row(), removedCellEnd.column()+1).firstPosition();
+ if (position < anchor)
+ position = newPosition;
+ else
+ anchor = adjusted_anchor = newPosition;
+ }
+}
+
+bool QTextCursorPrivate::movePosition(QTextCursor::MoveOperation op, QTextCursor::MoveMode mode)
+{
+ currentCharFormat = -1;
+ bool adjustX = true;
+ QTextBlock blockIt = block();
+
+ if (op >= QTextCursor::Left && op <= QTextCursor::WordRight
+ && blockIt.blockFormat().layoutDirection() == Qt::RightToLeft) {
+ if (op == QTextCursor::Left)
+ op = QTextCursor::NextCharacter;
+ else if (op == QTextCursor::Right)
+ op = QTextCursor::PreviousCharacter;
+ else if (op == QTextCursor::WordLeft)
+ op = QTextCursor::NextWord;
+ else if (op == QTextCursor::WordRight)
+ op = QTextCursor::PreviousWord;
+ }
+
+ const QTextLayout *layout = blockLayout(blockIt);
+ int relativePos = position - blockIt.position();
+ QTextLine line;
+ if (!priv->isInEditBlock())
+ line = layout->lineForTextPosition(relativePos);
+
+ Q_ASSERT(priv->frameAt(position) == priv->frameAt(adjusted_anchor));
+
+ int newPosition = position;
+
+ if (x == -1 && !priv->isInEditBlock() && (op == QTextCursor::Up || op == QTextCursor::Down))
+ setX();
+
+ switch(op) {
+ case QTextCursor::NoMove:
+ return true;
+
+ case QTextCursor::Start:
+ newPosition = 0;
+ break;
+ case QTextCursor::StartOfLine: {
+ newPosition = blockIt.position();
+ if (line.isValid())
+ newPosition += line.textStart();
+
+ break;
+ }
+ case QTextCursor::StartOfBlock: {
+ newPosition = blockIt.position();
+ break;
+ }
+ case QTextCursor::PreviousBlock: {
+ if (blockIt == priv->blocksBegin())
+ return false;
+ blockIt = blockIt.previous();
+
+ newPosition = blockIt.position();
+ break;
+ }
+ case QTextCursor::PreviousCharacter:
+ case QTextCursor::Left:
+ newPosition = priv->previousCursorPosition(position, QTextLayout::SkipCharacters);
+ break;
+ case QTextCursor::StartOfWord: {
+ if (relativePos == 0)
+ break;
+
+ // skip if already at word start
+ QTextEngine *engine = layout->engine();
+ engine->attributes();
+ if ((relativePos == blockIt.length() - 1)
+ && (engine->atSpace(relativePos - 1) || engine->atWordSeparator(relativePos - 1)))
+ return false;
+
+ if (relativePos < blockIt.length()-1)
+ ++position;
+
+ // FALL THROUGH!
+ }
+ case QTextCursor::PreviousWord:
+ case QTextCursor::WordLeft:
+ newPosition = priv->previousCursorPosition(position, QTextLayout::SkipWords);
+ break;
+ case QTextCursor::Up: {
+ int i = line.lineNumber() - 1;
+ if (i == -1) {
+ if (blockIt == priv->blocksBegin())
+ return false;
+ int blockPosition = blockIt.position();
+ QTextTable *table = qobject_cast<QTextTable *>(priv->frameAt(blockPosition));
+ if (table) {
+ QTextTableCell cell = table->cellAt(blockPosition);
+ if (cell.firstPosition() == blockPosition) {
+ int row = cell.row() - 1;
+ if (row >= 0) {
+ blockPosition = table->cellAt(row, cell.column()).lastPosition();
+ } else {
+ // move to line above the table
+ blockPosition = table->firstPosition() - 1;
+ }
+ blockIt = priv->blocksFind(blockPosition);
+ } else {
+ blockIt = blockIt.previous();
+ }
+ } else {
+ blockIt = blockIt.previous();
+ }
+ layout = blockLayout(blockIt);
+ i = layout->lineCount()-1;
+ }
+ if (layout->lineCount()) {
+ QTextLine line = layout->lineAt(i);
+ newPosition = line.xToCursor(x) + blockIt.position();
+ } else {
+ newPosition = blockIt.position();
+ }
+ adjustX = false;
+ break;
+ }
+
+ case QTextCursor::End:
+ newPosition = priv->length() - 1;
+ break;
+ case QTextCursor::EndOfLine: {
+ if (!line.isValid() || line.textLength() == 0) {
+ if (blockIt.length() >= 1)
+ // position right before the block separator
+ newPosition = blockIt.position() + blockIt.length() - 1;
+ break;
+ }
+ newPosition = blockIt.position() + line.textStart() + line.textLength();
+ if (line.lineNumber() < layout->lineCount() - 1) {
+ const QString text = blockIt.text();
+ // ###### this relies on spaces being the cause for linebreaks.
+ // this doesn't work with japanese
+ if (text.at(line.textStart() + line.textLength() - 1).isSpace())
+ --newPosition;
+ }
+ break;
+ }
+ case QTextCursor::EndOfWord: {
+ QTextEngine *engine = layout->engine();
+ engine->attributes();
+ const int len = blockIt.length() - 1;
+ if (relativePos >= len)
+ return false;
+ if (engine->atWordSeparator(relativePos)) {
+ ++relativePos;
+ while (relativePos < len && engine->atWordSeparator(relativePos))
+ ++relativePos;
+ } else {
+ while (relativePos < len && !engine->atSpace(relativePos) && !engine->atWordSeparator(relativePos))
+ ++relativePos;
+ }
+ newPosition = blockIt.position() + relativePos;
+ break;
+ }
+ case QTextCursor::EndOfBlock:
+ if (blockIt.length() >= 1)
+ // position right before the block separator
+ newPosition = blockIt.position() + blockIt.length() - 1;
+ break;
+ case QTextCursor::NextBlock: {
+ blockIt = blockIt.next();
+ if (!blockIt.isValid())
+ return false;
+
+ newPosition = blockIt.position();
+ break;
+ }
+ case QTextCursor::NextCharacter:
+ case QTextCursor::Right:
+ newPosition = priv->nextCursorPosition(position, QTextLayout::SkipCharacters);
+ break;
+ case QTextCursor::NextWord:
+ case QTextCursor::WordRight:
+ newPosition = priv->nextCursorPosition(position, QTextLayout::SkipWords);
+ break;
+
+ case QTextCursor::Down: {
+ int i = line.lineNumber() + 1;
+
+ if (i >= layout->lineCount()) {
+ int blockPosition = blockIt.position() + blockIt.length() - 1;
+ QTextTable *table = qobject_cast<QTextTable *>(priv->frameAt(blockPosition));
+ if (table) {
+ QTextTableCell cell = table->cellAt(blockPosition);
+ if (cell.lastPosition() == blockPosition) {
+ int row = cell.row() + cell.rowSpan();
+ if (row < table->rows()) {
+ blockPosition = table->cellAt(row, cell.column()).firstPosition();
+ } else {
+ // move to line below the table
+ blockPosition = table->lastPosition() + 1;
+ }
+ blockIt = priv->blocksFind(blockPosition);
+ } else {
+ blockIt = blockIt.next();
+ }
+ } else {
+ blockIt = blockIt.next();
+ }
+
+ if (blockIt == priv->blocksEnd())
+ return false;
+ layout = blockLayout(blockIt);
+ i = 0;
+ }
+ if (layout->lineCount()) {
+ QTextLine line = layout->lineAt(i);
+ newPosition = line.xToCursor(x) + blockIt.position();
+ } else {
+ newPosition = blockIt.position();
+ }
+ adjustX = false;
+ break;
+ }
+ case QTextCursor::NextCell: // fall through
+ case QTextCursor::PreviousCell: // fall through
+ case QTextCursor::NextRow: // fall through
+ case QTextCursor::PreviousRow: {
+ QTextTable *table = qobject_cast<QTextTable *>(priv->frameAt(position));
+ if (!table)
+ return false;
+
+ QTextTableCell cell = table->cellAt(position);
+ Q_ASSERT(cell.isValid());
+ int column = cell.column();
+ int row = cell.row();
+ const int currentRow = row;
+ if (op == QTextCursor::NextCell || op == QTextCursor::NextRow) {
+ do {
+ column += cell.columnSpan();
+ if (column >= table->columns()) {
+ column = 0;
+ ++row;
+ }
+ cell = table->cellAt(row, column);
+ // note we also continue while we have not reached a cell thats not merged with one above us
+ } while (cell.isValid()
+ && ((op == QTextCursor::NextRow && currentRow == cell.row())
+ || cell.row() < row));
+ }
+ else if (op == QTextCursor::PreviousCell || op == QTextCursor::PreviousRow) {
+ do {
+ --column;
+ if (column < 0) {
+ column = table->columns()-1;
+ --row;
+ }
+ cell = table->cellAt(row, column);
+ // note we also continue while we have not reached a cell thats not merged with one above us
+ } while (cell.isValid()
+ && ((op == QTextCursor::PreviousRow && currentRow == cell.row())
+ || cell.row() < row));
+ }
+ if (cell.isValid())
+ newPosition = cell.firstPosition();
+ break;
+ }
+ }
+
+ if (mode == QTextCursor::KeepAnchor) {
+ QTextTable *table = qobject_cast<QTextTable *>(priv->frameAt(position));
+ if (table && ((op >= QTextCursor::PreviousBlock && op <= QTextCursor::WordLeft)
+ || (op >= QTextCursor::NextBlock && op <= QTextCursor::WordRight))) {
+ int oldColumn = table->cellAt(position).column();
+
+ const QTextTableCell otherCell = table->cellAt(newPosition);
+ if (!otherCell.isValid())
+ return false;
+
+ int newColumn = otherCell.column();
+ if ((oldColumn > newColumn && op >= QTextCursor::End)
+ || (oldColumn < newColumn && op <= QTextCursor::WordLeft))
+ return false;
+ }
+ }
+
+ const bool moved = setPosition(newPosition);
+
+ if (mode == QTextCursor::MoveAnchor) {
+ anchor = position;
+ adjusted_anchor = position;
+ } else {
+ adjustCursor(op);
+ }
+
+ if (adjustX)
+ setX();
+
+ return moved;
+}
+
+QTextTable *QTextCursorPrivate::complexSelectionTable() const
+{
+ if (position == anchor)
+ return 0;
+
+ QTextTable *t = qobject_cast<QTextTable *>(priv->frameAt(position));
+ if (t) {
+ QTextTableCell cell_pos = t->cellAt(position);
+ QTextTableCell cell_anchor = t->cellAt(adjusted_anchor);
+
+ Q_ASSERT(cell_anchor.isValid());
+
+ if (cell_pos == cell_anchor)
+ t = 0;
+ }
+ return t;
+}
+
+void QTextCursorPrivate::selectedTableCells(int *firstRow, int *numRows, int *firstColumn, int *numColumns) const
+{
+ *firstRow = -1;
+ *firstColumn = -1;
+ *numRows = -1;
+ *numColumns = -1;
+
+ if (position == anchor)
+ return;
+
+ QTextTable *t = qobject_cast<QTextTable *>(priv->frameAt(position));
+ if (!t)
+ return;
+
+ QTextTableCell cell_pos = t->cellAt(position);
+ QTextTableCell cell_anchor = t->cellAt(adjusted_anchor);
+
+ Q_ASSERT(cell_anchor.isValid());
+
+ if (cell_pos == cell_anchor)
+ return;
+
+ *firstRow = qMin(cell_pos.row(), cell_anchor.row());
+ *firstColumn = qMin(cell_pos.column(), cell_anchor.column());
+ *numRows = qMax(cell_pos.row() + cell_pos.rowSpan(), cell_anchor.row() + cell_anchor.rowSpan()) - *firstRow;
+ *numColumns = qMax(cell_pos.column() + cell_pos.columnSpan(), cell_anchor.column() + cell_anchor.columnSpan()) - *firstColumn;
+}
+
+static void setBlockCharFormatHelper(QTextDocumentPrivate *priv, int pos1, int pos2,
+ const QTextCharFormat &format, QTextDocumentPrivate::FormatChangeMode changeMode)
+{
+ QTextBlock it = priv->blocksFind(pos1);
+ QTextBlock end = priv->blocksFind(pos2);
+ if (end.isValid())
+ end = end.next();
+
+ for (; it != end; it = it.next()) {
+ priv->setCharFormat(it.position() - 1, 1, format, changeMode);
+ }
+}
+
+void QTextCursorPrivate::setBlockCharFormat(const QTextCharFormat &_format,
+ QTextDocumentPrivate::FormatChangeMode changeMode)
+{
+ priv->beginEditBlock();
+
+ QTextCharFormat format = _format;
+ format.clearProperty(QTextFormat::ObjectIndex);
+
+ QTextTable *table = complexSelectionTable();
+ if (table) {
+ int row_start, col_start, num_rows, num_cols;
+ selectedTableCells(&row_start, &num_rows, &col_start, &num_cols);
+
+ Q_ASSERT(row_start != -1);
+ for (int r = row_start; r < row_start + num_rows; ++r) {
+ for (int c = col_start; c < col_start + num_cols; ++c) {
+ QTextTableCell cell = table->cellAt(r, c);
+ int rspan = cell.rowSpan();
+ int cspan = cell.columnSpan();
+ if (rspan != 1) {
+ int cr = cell.row();
+ if (cr != r)
+ continue;
+ }
+ if (cspan != 1) {
+ int cc = cell.column();
+ if (cc != c)
+ continue;
+ }
+
+ int pos1 = cell.firstPosition();
+ int pos2 = cell.lastPosition();
+ setBlockCharFormatHelper(priv, pos1, pos2, format, changeMode);
+ }
+ }
+ } else {
+ int pos1 = position;
+ int pos2 = adjusted_anchor;
+ if (pos1 > pos2) {
+ pos1 = adjusted_anchor;
+ pos2 = position;
+ }
+
+ setBlockCharFormatHelper(priv, pos1, pos2, format, changeMode);
+ }
+ priv->endEditBlock();
+}
+
+
+void QTextCursorPrivate::setBlockFormat(const QTextBlockFormat &format, QTextDocumentPrivate::FormatChangeMode changeMode)
+{
+ QTextTable *table = complexSelectionTable();
+ if (table) {
+ priv->beginEditBlock();
+ int row_start, col_start, num_rows, num_cols;
+ selectedTableCells(&row_start, &num_rows, &col_start, &num_cols);
+
+ Q_ASSERT(row_start != -1);
+ for (int r = row_start; r < row_start + num_rows; ++r) {
+ for (int c = col_start; c < col_start + num_cols; ++c) {
+ QTextTableCell cell = table->cellAt(r, c);
+ int rspan = cell.rowSpan();
+ int cspan = cell.columnSpan();
+ if (rspan != 1) {
+ int cr = cell.row();
+ if (cr != r)
+ continue;
+ }
+ if (cspan != 1) {
+ int cc = cell.column();
+ if (cc != c)
+ continue;
+ }
+
+ int pos1 = cell.firstPosition();
+ int pos2 = cell.lastPosition();
+ priv->setBlockFormat(priv->blocksFind(pos1), priv->blocksFind(pos2), format, changeMode);
+ }
+ }
+ priv->endEditBlock();
+ } else {
+ int pos1 = position;
+ int pos2 = adjusted_anchor;
+ if (pos1 > pos2) {
+ pos1 = adjusted_anchor;
+ pos2 = position;
+ }
+
+ priv->setBlockFormat(priv->blocksFind(pos1), priv->blocksFind(pos2), format, changeMode);
+ }
+}
+
+void QTextCursorPrivate::setCharFormat(const QTextCharFormat &_format, QTextDocumentPrivate::FormatChangeMode changeMode)
+{
+ Q_ASSERT(position != anchor);
+
+ QTextCharFormat format = _format;
+ format.clearProperty(QTextFormat::ObjectIndex);
+
+ QTextTable *table = complexSelectionTable();
+ if (table) {
+ priv->beginEditBlock();
+ int row_start, col_start, num_rows, num_cols;
+ selectedTableCells(&row_start, &num_rows, &col_start, &num_cols);
+
+ Q_ASSERT(row_start != -1);
+ for (int r = row_start; r < row_start + num_rows; ++r) {
+ for (int c = col_start; c < col_start + num_cols; ++c) {
+ QTextTableCell cell = table->cellAt(r, c);
+ int rspan = cell.rowSpan();
+ int cspan = cell.columnSpan();
+ if (rspan != 1) {
+ int cr = cell.row();
+ if (cr != r)
+ continue;
+ }
+ if (cspan != 1) {
+ int cc = cell.column();
+ if (cc != c)
+ continue;
+ }
+
+ int pos1 = cell.firstPosition();
+ int pos2 = cell.lastPosition();
+ priv->setCharFormat(pos1, pos2-pos1, format, changeMode);
+ }
+ }
+ priv->endEditBlock();
+ } else {
+ int pos1 = position;
+ int pos2 = adjusted_anchor;
+ if (pos1 > pos2) {
+ pos1 = adjusted_anchor;
+ pos2 = position;
+ }
+
+ priv->setCharFormat(pos1, pos2-pos1, format, changeMode);
+ }
+}
+
+
+QTextLayout *QTextCursorPrivate::blockLayout(QTextBlock &block) const{
+ QTextLayout *tl = block.layout();
+ if (!tl->lineCount() && priv->layout())
+ priv->layout()->blockBoundingRect(block);
+ return tl;
+}
+
+/*!
+ \class QTextCursor
+ \reentrant
+
+ \brief The QTextCursor class offers an API to access and modify QTextDocuments.
+
+ \ingroup text
+ \ingroup shared
+ \mainclass
+
+ Text cursors are objects that are used to access and modify the contents
+ and underlying structure of text documents via a programming interface
+ that mimics the behavior of a cursor in a text editor. QTextCursor contains
+ information about both the cursor's position within a QTextDocument and any
+ selection that it has made.
+
+ QTextCursor is modeled on the way a text cursor behaves in a text
+ editor, providing a programmatic means of performing standard actions
+ through the user interface. A document can be thought of as a
+ single string of characters with the cursor's position() being \e
+ between any two characters (or at the very beginning or very end
+ of the document). Documents can also contain tables, lists,
+ images, and other objects in addition to text but, from the developer's
+ point of view, the document can be treated as one long string.
+ Some portions of that string can be considered to lie within particular
+ blocks (e.g. paragraphs), or within a table's cell, or a list's item,
+ or other structural elements. When we refer to "current character" we
+ mean the character immediately after the cursor position() in the
+ document; similarly the "current block" is the block that contains the
+ cursor position().
+
+ A QTextCursor also has an anchor() position. The text that is
+ between the anchor() and the position() is the selection. If
+ anchor() == position() there is no selection.
+
+ The cursor position can be changed programmatically using
+ setPosition() and movePosition(); the latter can also be used to
+ select text. For selections see selectionStart(), selectionEnd(),
+ hasSelection(), clearSelection(), and removeSelectedText().
+
+ If the position() is at the start of a block atBlockStart()
+ returns true; and if it is at the end of a block atBlockEnd() returns
+ true. The format of the current character is returned by
+ charFormat(), and the format of the current block is returned by
+ blockFormat().
+
+ Formatting can be applied to the current text document using the
+ setCharFormat(), mergeCharFormat(), setBlockFormat() and
+ mergeBlockFormat() functions. The 'set' functions will replace the
+ cursor's current character or block format, while the 'merge'
+ functions add the given format properties to the cursor's current
+ format. If the cursor has a selection the given format is applied
+ to the current selection. Note that when only parts of a block is
+ selected the block format is applied to the entire block. The text
+ at the current character position can be turned into a list using
+ createList().
+
+ Deletions can be achieved using deleteChar(),
+ deletePreviousChar(), and removeSelectedText().
+
+ Text strings can be inserted into the document with the insertText()
+ function, blocks (representing new paragraphs) can be inserted with
+ insertBlock().
+
+ Existing fragments of text can be inserted with insertFragment() but,
+ if you want to insert pieces of text in various formats, it is usually
+ still easier to use insertText() and supply a character format.
+
+ Various types of higher-level structure can also be inserted into the
+ document with the cursor:
+
+ \list
+ \i Lists are ordered sequences of block elements that are decorated with
+ bullet points or symbols. These are inserted in a specified format
+ with insertList().
+ \i Tables are inserted with the insertTable() function, and can be
+ given an optional format. These contain an array of cells that can
+ be traversed using the cursor.
+ \i Inline images are inserted with insertImage(). The image to be
+ used can be specified in an image format, or by name.
+ \i Frames are inserted by calling insertFrame() with a specified format.
+ \endlist
+
+ Actions can be grouped (i.e. treated as a single action for
+ undo/redo) using beginEditBlock() and endEditBlock().
+
+ Cursor movements are limited to valid cursor positions. In Latin
+ writing this is usually after every character in the text. In some
+ other writing systems cursor movements are limited to "clusters"
+ (e.g. a syllable in Devanagari, or a base letter plus diacritics).
+ Functions such as movePosition() and deleteChar() limit cursor
+ movement to these valid positions.
+
+ \sa \link richtext.html Rich Text Processing\endlink
+
+*/
+
+/*!
+ \enum QTextCursor::MoveOperation
+
+ \value NoMove Keep the cursor where it is
+
+ \value Start Move to the start of the document.
+ \value StartOfLine Move to the start of the current line.
+ \value StartOfBlock Move to the start of the current block.
+ \value StartOfWord Move to the start of the current word.
+ \value PreviousBlock Move to the start of the previous block.
+ \value PreviousCharacter Move to the previous character.
+ \value PreviousWord Move to the beginning of the previous word.
+ \value Up Move up one line.
+ \value Left Move left one character.
+ \value WordLeft Move left one word.
+
+ \value End Move to the end of the document.
+ \value EndOfLine Move to the end of the current line.
+ \value EndOfWord Move to the end of the current word.
+ \value EndOfBlock Move to the end of the current block.
+ \value NextBlock Move to the beginning of the next block.
+ \value NextCharacter Move to the next character.
+ \value NextWord Move to the next word.
+ \value Down Move down one line.
+ \value Right Move right one character.
+ \value WordRight Move right one word.
+
+ \value NextCell Move to the beginning of the next table cell inside the
+ current table. If the current cell is the last cell in the row, the
+ cursor will move to the first cell in the next row.
+ \value PreviousCell Move to the beginning of the previous table cell
+ inside the current table. If the current cell is the first cell in
+ the row, the cursor will move to the last cell in the previous row.
+ \value NextRow Move to the first new cell of the next row in the current
+ table.
+ \value PreviousRow Move to the last cell of the previous row in the
+ current table.
+
+ \sa movePosition()
+*/
+
+/*!
+ \enum QTextCursor::MoveMode
+
+ \value MoveAnchor Moves the anchor to the same position as the cursor itself.
+ \value KeepAnchor Keeps the anchor where it is.
+
+ If the anchor() is kept where it is and the position() is moved,
+ the text in between will be selected.
+*/
+
+/*!
+ \enum QTextCursor::SelectionType
+
+ This enum describes the types of selection that can be applied with the
+ select() function.
+
+ \value Document Selects the entire document.
+ \value BlockUnderCursor Selects the block of text under the cursor.
+ \value LineUnderCursor Selects the line of text under the cursor.
+ \value WordUnderCursor Selects the word under the cursor. If the cursor
+ is not positioned within a string of selectable characters, no
+ text is selected.
+*/
+
+/*!
+ Constructs a null cursor.
+ */
+QTextCursor::QTextCursor()
+ : d(0)
+{
+}
+
+/*!
+ Constructs a cursor pointing to the beginning of the \a document.
+ */
+QTextCursor::QTextCursor(QTextDocument *document)
+ : d(new QTextCursorPrivate(document->docHandle()))
+{
+}
+
+/*!
+ Constructs a cursor pointing to the beginning of the \a frame.
+*/
+QTextCursor::QTextCursor(QTextFrame *frame)
+ : d(new QTextCursorPrivate(frame->document()->docHandle()))
+{
+ d->adjusted_anchor = d->anchor = d->position = frame->firstPosition();
+}
+
+
+/*!
+ Constructs a cursor pointing to the beginning of the \a block.
+*/
+QTextCursor::QTextCursor(const QTextBlock &block)
+ : d(new QTextCursorPrivate(block.docHandle()))
+{
+ d->adjusted_anchor = d->anchor = d->position = block.position();
+}
+
+
+/*!
+ \internal
+ */
+QTextCursor::QTextCursor(QTextDocumentPrivate *p, int pos)
+ : d(new QTextCursorPrivate(p))
+{
+ d->adjusted_anchor = d->anchor = d->position = pos;
+
+ d->setX();
+}
+
+/*!
+ \internal
+*/
+QTextCursor::QTextCursor(QTextCursorPrivate *d)
+{
+ Q_ASSERT(d);
+ this->d = d;
+}
+
+/*!
+ Constructs a new cursor that is a copy of \a cursor.
+ */
+QTextCursor::QTextCursor(const QTextCursor &cursor)
+{
+ d = cursor.d;
+}
+
+/*!
+ Makes a copy of \a cursor and assigns it to this QTextCursor.
+ */
+QTextCursor &QTextCursor::operator=(const QTextCursor &cursor)
+{
+ d = cursor.d;
+ return *this;
+}
+
+/*!
+ Destroys the QTextCursor.
+ */
+QTextCursor::~QTextCursor()
+{
+}
+
+/*!
+ Returns true if the cursor is null; otherwise returns false. A null
+ cursor is created by the default constructor.
+ */
+bool QTextCursor::isNull() const
+{
+ return !d || !d->priv;
+}
+
+/*!
+ Moves the cursor to the absolute position in the document specified by
+ \a pos using a \c MoveMode specified by \a m. The cursor is positioned
+ between characters.
+
+ \sa position() movePosition() anchor()
+*/
+void QTextCursor::setPosition(int pos, MoveMode m)
+{
+ if (!d || !d->priv)
+ return;
+
+ if (pos < 0 || pos >= d->priv->length()) {
+ qWarning("QTextCursor::setPosition: Position '%d' out of range", pos);
+ return;
+ }
+
+ d->setPosition(pos);
+ if (m == MoveAnchor) {
+ d->anchor = pos;
+ d->adjusted_anchor = pos;
+ } else { // keep anchor
+ QTextCursor::MoveOperation op;
+ if (pos < d->anchor)
+ op = QTextCursor::Left;
+ else
+ op = QTextCursor::Right;
+ d->adjustCursor(op);
+ }
+ d->setX();
+}
+
+/*!
+ Returns the absolute position of the cursor within the document.
+ The cursor is positioned between characters.
+
+ \sa setPosition() movePosition() anchor()
+*/
+int QTextCursor::position() const
+{
+ if (!d || !d->priv)
+ return -1;
+ return d->position;
+}
+
+/*!
+ Returns the anchor position; this is the same as position() unless
+ there is a selection in which case position() marks one end of the
+ selection and anchor() marks the other end. Just like the cursor
+ position, the anchor position is between characters.
+
+ \sa position() setPosition() movePosition() selectionStart() selectionEnd()
+*/
+int QTextCursor::anchor() const
+{
+ if (!d || !d->priv)
+ return -1;
+ return d->anchor;
+}
+
+/*!
+ \fn bool QTextCursor::movePosition(MoveOperation operation, MoveMode mode, int n)
+
+ Moves the cursor by performing the given \a operation \a n times, using the specified
+ \a mode, and returns true if all operations were completed successfully; otherwise
+ returns false.
+
+ For example, if this function is repeatedly used to seek to the end of the next
+ word, it will eventually fail when the end of the document is reached.
+
+ By default, the move operation is performed once (\a n = 1).
+
+ If \a mode is \c KeepAnchor, the cursor selects the text it moves
+ over. This is the same effect that the user achieves when they
+ hold down the Shift key and move the cursor with the cursor keys.
+
+ \sa setVisualNavigation()
+*/
+bool QTextCursor::movePosition(MoveOperation op, MoveMode mode, int n)
+{
+ if (!d || !d->priv)
+ return false;
+ switch (op) {
+ case Start:
+ case StartOfLine:
+ case End:
+ case EndOfLine:
+ n = 1;
+ break;
+ default: break;
+ }
+
+ int previousPosition = d->position;
+ for (; n > 0; --n) {
+ if (!d->movePosition(op, mode))
+ return false;
+ }
+
+ if (d->visualNavigation && !d->block().isVisible()) {
+ QTextBlock b = d->block();
+ if (previousPosition < d->position) {
+ while (!b.next().isVisible())
+ b = b.next();
+ d->setPosition(b.position() + b.length() - 1);
+ } else {
+ while (!b.previous().isVisible())
+ b = b.previous();
+ d->setPosition(b.position());
+ }
+ if (mode == QTextCursor::MoveAnchor)
+ d->anchor = d->position;
+ while (d->movePosition(op, mode)
+ && !d->block().isVisible())
+ ;
+
+ }
+ return true;
+}
+
+/*!
+ \since 4.4
+
+ Returns true if the cursor does visual navigation; otherwise
+ returns false.
+
+ Visual navigation means skipping over hidden text pragraphs. The
+ default is false.
+
+ \sa setVisualNavigation(), movePosition()
+ */
+bool QTextCursor::visualNavigation() const
+{
+ return d ? d->visualNavigation : false;
+}
+
+/*!
+ \since 4.4
+
+ Sets visual navigation to \a b.
+
+ Visual navigation means skipping over hidden text pragraphs. The
+ default is false.
+
+ \sa visualNavigation(), movePosition()
+ */
+void QTextCursor::setVisualNavigation(bool b)
+{
+ if (d)
+ d->visualNavigation = b;
+}
+
+/*!
+ Inserts \a text at the current position, using the current
+ character format.
+
+ If there is a selection, the selection is deleted and replaced by
+ \a text, for example:
+ \snippet doc/src/snippets/code/src_gui_text_qtextcursor.cpp 0
+ This clears any existing selection, selects the word at the cursor
+ (i.e. from position() forward), and replaces the selection with
+ the phrase "Hello World".
+
+ Any ASCII linefeed characters (\\n) in the inserted text are transformed
+ into unicode block separators, corresponding to insertBlock() calls.
+
+ \sa charFormat() hasSelection()
+*/
+void QTextCursor::insertText(const QString &text)
+{
+ QTextCharFormat fmt = charFormat();
+ fmt.clearProperty(QTextFormat::ObjectType);
+ insertText(text, fmt);
+}
+
+/*!
+ \fn void QTextCursor::insertText(const QString &text, const QTextCharFormat &format)
+ \overload
+
+ Inserts \a text at the current position with the given \a format.
+*/
+void QTextCursor::insertText(const QString &text, const QTextCharFormat &_format)
+{
+ if (!d || !d->priv)
+ return;
+
+ Q_ASSERT(_format.isValid());
+
+ QTextCharFormat format = _format;
+ format.clearProperty(QTextFormat::ObjectIndex);
+
+ d->priv->beginEditBlock();
+
+ d->remove();
+ if (!text.isEmpty()) {
+ QTextFormatCollection *formats = d->priv->formatCollection();
+ int formatIdx = formats->indexForFormat(format);
+ Q_ASSERT(formats->format(formatIdx).isCharFormat());
+
+ QTextBlockFormat blockFmt = blockFormat();
+
+
+ int textStart = d->priv->text.length();
+ int blockStart = 0;
+ d->priv->text += text;
+ int textEnd = d->priv->text.length();
+
+ for (int i = 0; i < text.length(); ++i) {
+ QChar ch = text.at(i);
+
+ const int blockEnd = i;
+
+ if (ch == QLatin1Char('\r')
+ && (i + 1) < text.length()
+ && text.at(i + 1) == QLatin1Char('\n')) {
+ ++i;
+ ch = text.at(i);
+ }
+
+ if (ch == QLatin1Char('\n')
+ || ch == QChar::ParagraphSeparator
+ || ch == QLatin1Char('\r')) {
+
+ if (blockEnd > blockStart)
+ d->priv->insert(d->position, textStart + blockStart, blockEnd - blockStart, formatIdx);
+
+ d->insertBlock(blockFmt, format);
+ blockStart = i + 1;
+ }
+ }
+ if (textStart + blockStart < textEnd)
+ d->priv->insert(d->position, textStart + blockStart, textEnd - textStart - blockStart, formatIdx);
+ }
+ d->priv->endEditBlock();
+ d->setX();
+}
+
+/*!
+ If there is no selected text, deletes the character \e at the
+ current cursor position; otherwise deletes the selected text.
+
+ \sa deletePreviousChar() hasSelection() clearSelection()
+*/
+void QTextCursor::deleteChar()
+{
+ if (!d || !d->priv)
+ return;
+
+ if (d->position == d->anchor) {
+ if (!d->canDelete(d->position))
+ return;
+ d->adjusted_anchor = d->anchor =
+ d->priv->nextCursorPosition(d->anchor, QTextLayout::SkipCharacters);
+ }
+ d->remove();
+ d->setX();
+}
+
+/*!
+ If there is no selected text, deletes the character \e before the
+ current cursor position; otherwise deletes the selected text.
+
+ \sa deleteChar() hasSelection() clearSelection()
+*/
+void QTextCursor::deletePreviousChar()
+{
+ if (!d || !d->priv)
+ return;
+
+ if (d->position == d->anchor) {
+ if (d->anchor < 1 || !d->canDelete(d->anchor-1))
+ return;
+ d->anchor--;
+
+ QTextDocumentPrivate::FragmentIterator fragIt = d->priv->find(d->anchor);
+ const QTextFragmentData * const frag = fragIt.value();
+ int fpos = fragIt.position();
+ QChar uc = d->priv->buffer().at(d->anchor - fpos + frag->stringPosition);
+ if (d->anchor > fpos && uc.unicode() >= 0xdc00 && uc.unicode() < 0xe000) {
+ // second half of a surrogate, check if we have the first half as well,
+ // if yes delete both at once
+ uc = d->priv->buffer().at(d->anchor - 1 - fpos + frag->stringPosition);
+ if (uc.unicode() >= 0xd800 && uc.unicode() < 0xdc00)
+ --d->anchor;
+ }
+
+ d->adjusted_anchor = d->anchor;
+ }
+
+ d->remove();
+ d->setX();
+}
+
+/*!
+ Selects text in the document according to the given \a selection.
+*/
+void QTextCursor::select(SelectionType selection)
+{
+ if (!d || !d->priv)
+ return;
+
+ clearSelection();
+
+ const QTextBlock block = d->block();
+
+ switch (selection) {
+ case LineUnderCursor:
+ movePosition(StartOfLine);
+ movePosition(EndOfLine, KeepAnchor);
+ break;
+ case WordUnderCursor:
+ movePosition(StartOfWord);
+ movePosition(EndOfWord, KeepAnchor);
+ break;
+ case BlockUnderCursor:
+ if (block.length() == 1) // no content
+ break;
+ movePosition(StartOfBlock);
+ // also select the paragraph separator
+ if (movePosition(PreviousBlock)) {
+ movePosition(EndOfBlock);
+ movePosition(NextBlock, KeepAnchor);
+ }
+ movePosition(EndOfBlock, KeepAnchor);
+ break;
+ case Document:
+ movePosition(Start);
+ movePosition(End, KeepAnchor);
+ break;
+ }
+}
+
+/*!
+ Returns true if the cursor contains a selection; otherwise returns false.
+*/
+bool QTextCursor::hasSelection() const
+{
+ return !!d && d->position != d->anchor;
+}
+
+
+/*!
+ Returns true if the cursor contains a selection that is not simply a
+ range from selectionStart() to selectionEnd(); otherwise returns false.
+
+ Complex selections are ones that span at least two cells in a table;
+ their extent is specified by selectedTableCells().
+*/
+bool QTextCursor::hasComplexSelection() const
+{
+ if (!d)
+ return false;
+
+ return d->complexSelectionTable() != 0;
+}
+
+/*!
+ If the selection spans over table cells, \a firstRow is populated
+ with the number of the first row in the selection, \a firstColumn
+ with the number of the first column in the selection, and \a
+ numRows and \a numColumns with the number of rows and columns in
+ the selection. If the selection does not span any table cells the
+ results are harmless but undefined.
+*/
+void QTextCursor::selectedTableCells(int *firstRow, int *numRows, int *firstColumn, int *numColumns) const
+{
+ *firstRow = -1;
+ *firstColumn = -1;
+ *numRows = -1;
+ *numColumns = -1;
+
+ if (!d || d->position == d->anchor)
+ return;
+
+ d->selectedTableCells(firstRow, numRows, firstColumn, numColumns);
+}
+
+
+/*!
+ Clears the current selection by setting the anchor to the cursor position.
+
+ Note that it does \bold{not} delete the text of the selection.
+
+ \sa removeSelectedText() hasSelection()
+*/
+void QTextCursor::clearSelection()
+{
+ if (!d)
+ return;
+ d->adjusted_anchor = d->anchor = d->position;
+ d->currentCharFormat = -1;
+}
+
+/*!
+ If there is a selection, its content is deleted; otherwise does
+ nothing.
+
+ \sa hasSelection()
+*/
+void QTextCursor::removeSelectedText()
+{
+ if (!d || !d->priv || d->position == d->anchor)
+ return;
+
+ d->remove();
+ d->setX();
+}
+
+/*!
+ Returns the start of the selection or position() if the
+ cursor doesn't have a selection.
+
+ \sa selectionEnd() position() anchor()
+*/
+int QTextCursor::selectionStart() const
+{
+ if (!d || !d->priv)
+ return -1;
+ return qMin(d->position, d->adjusted_anchor);
+}
+
+/*!
+ Returns the end of the selection or position() if the cursor
+ doesn't have a selection.
+
+ \sa selectionStart() position() anchor()
+*/
+int QTextCursor::selectionEnd() const
+{
+ if (!d || !d->priv)
+ return -1;
+ return qMax(d->position, d->adjusted_anchor);
+}
+
+static void getText(QString &text, QTextDocumentPrivate *priv, const QString &docText, int pos, int end)
+{
+ while (pos < end) {
+ QTextDocumentPrivate::FragmentIterator fragIt = priv->find(pos);
+ const QTextFragmentData * const frag = fragIt.value();
+
+ const int offsetInFragment = qMax(0, pos - fragIt.position());
+ const int len = qMin(int(frag->size_array[0] - offsetInFragment), end - pos);
+
+ text += QString(docText.constData() + frag->stringPosition + offsetInFragment, len);
+ pos += len;
+ }
+}
+
+/*!
+ Returns the current selection's text (which may be empty). This
+ only returns the text, with no rich text formatting information.
+ If you want a document fragment (i.e. formatted rich text) use
+ selection() instead.
+
+ \note If the selection obtained from an editor spans a line break,
+ the text will contain a Unicode U+2029 paragraph separator character
+ instead of a newline \c{\n} character. Use QString::replace() to
+ replace these characters with newlines.
+*/
+QString QTextCursor::selectedText() const
+{
+ if (!d || !d->priv || d->position == d->anchor)
+ return QString();
+
+ const QString docText = d->priv->buffer();
+ QString text;
+
+ QTextTable *table = d->complexSelectionTable();
+ if (table) {
+ int row_start, col_start, num_rows, num_cols;
+ selectedTableCells(&row_start, &num_rows, &col_start, &num_cols);
+
+ Q_ASSERT(row_start != -1);
+ for (int r = row_start; r < row_start + num_rows; ++r) {
+ for (int c = col_start; c < col_start + num_cols; ++c) {
+ QTextTableCell cell = table->cellAt(r, c);
+ int rspan = cell.rowSpan();
+ int cspan = cell.columnSpan();
+ if (rspan != 1) {
+ int cr = cell.row();
+ if (cr != r)
+ continue;
+ }
+ if (cspan != 1) {
+ int cc = cell.column();
+ if (cc != c)
+ continue;
+ }
+
+ getText(text, d->priv, docText, cell.firstPosition(), cell.lastPosition());
+ }
+ }
+ } else {
+ getText(text, d->priv, docText, selectionStart(), selectionEnd());
+ }
+
+ return text;
+}
+
+/*!
+ Returns the current selection (which may be empty) with all its
+ formatting information. If you just want the selected text (i.e.
+ plain text) use selectedText() instead.
+
+ \note Unlike QTextDocumentFragment::toPlainText(),
+ selectedText() may include special unicode characters such as
+ QChar::ParagraphSeparator.
+
+ \sa QTextDocumentFragment::toPlainText()
+*/
+QTextDocumentFragment QTextCursor::selection() const
+{
+ return QTextDocumentFragment(*this);
+}
+
+/*!
+ Returns the block that contains the cursor.
+*/
+QTextBlock QTextCursor::block() const
+{
+ if (!d || !d->priv)
+ return QTextBlock();
+ return d->block();
+}
+
+/*!
+ Returns the block format of the block the cursor is in.
+
+ \sa setBlockFormat() charFormat()
+ */
+QTextBlockFormat QTextCursor::blockFormat() const
+{
+ if (!d || !d->priv)
+ return QTextBlockFormat();
+
+ return d->block().blockFormat();
+}
+
+/*!
+ Sets the block format of the current block (or all blocks that
+ are contained in the selection) to \a format.
+
+ \sa blockFormat(), mergeBlockFormat()
+*/
+void QTextCursor::setBlockFormat(const QTextBlockFormat &format)
+{
+ if (!d || !d->priv)
+ return;
+
+ d->setBlockFormat(format, QTextDocumentPrivate::SetFormat);
+}
+
+/*!
+ Modifies the block format of the current block (or all blocks that
+ are contained in the selection) with the block format specified by
+ \a modifier.
+
+ \sa setBlockFormat(), blockFormat()
+*/
+void QTextCursor::mergeBlockFormat(const QTextBlockFormat &modifier)
+{
+ if (!d || !d->priv)
+ return;
+
+ d->setBlockFormat(modifier, QTextDocumentPrivate::MergeFormat);
+}
+
+/*!
+ Returns the block character format of the block the cursor is in.
+
+ The block char format is the format used when inserting text at the
+ beginning of an empty block.
+
+ \sa setBlockCharFormat()
+ */
+QTextCharFormat QTextCursor::blockCharFormat() const
+{
+ if (!d || !d->priv)
+ return QTextCharFormat();
+
+ return d->block().charFormat();
+}
+
+/*!
+ Sets the block char format of the current block (or all blocks that
+ are contained in the selection) to \a format.
+
+ \sa blockCharFormat()
+*/
+void QTextCursor::setBlockCharFormat(const QTextCharFormat &format)
+{
+ if (!d || !d->priv)
+ return;
+
+ d->setBlockCharFormat(format, QTextDocumentPrivate::SetFormatAndPreserveObjectIndices);
+}
+
+/*!
+ Modifies the block char format of the current block (or all blocks that
+ are contained in the selection) with the block format specified by
+ \a modifier.
+
+ \sa setBlockCharFormat()
+*/
+void QTextCursor::mergeBlockCharFormat(const QTextCharFormat &modifier)
+{
+ if (!d || !d->priv)
+ return;
+
+ d->setBlockCharFormat(modifier, QTextDocumentPrivate::MergeFormat);
+}
+
+/*!
+ Returns the format of the character immediately before the cursor position(). If the cursor is
+ positioned at the beginning of a text block that is not empty then the format of the character
+ immediately after the cursor is returned.
+
+ \sa insertText(), blockFormat()
+ */
+QTextCharFormat QTextCursor::charFormat() const
+{
+ if (!d || !d->priv)
+ return QTextCharFormat();
+
+ int idx = d->currentCharFormat;
+ if (idx == -1) {
+ QTextBlock block = d->block();
+
+ int pos;
+ if (d->position == block.position()
+ && block.length() > 1)
+ pos = d->position;
+ else
+ pos = d->position - 1;
+
+ if (pos == -1) {
+ idx = d->priv->blockCharFormatIndex(d->priv->blockMap().firstNode());
+ } else {
+ Q_ASSERT(pos >= 0 && pos < d->priv->length());
+
+ QTextDocumentPrivate::FragmentIterator it = d->priv->find(pos);
+ Q_ASSERT(!it.atEnd());
+ idx = it.value()->format;
+ }
+ }
+
+ QTextCharFormat cfmt = d->priv->formatCollection()->charFormat(idx);
+ cfmt.clearProperty(QTextFormat::ObjectIndex);
+
+ Q_ASSERT(cfmt.isValid());
+ return cfmt;
+}
+
+/*!
+ Sets the cursor's current character format to the given \a
+ format. If the cursor has a selection, the given \a format is
+ applied to the current selection.
+
+ \sa hasSelection(), mergeCharFormat()
+*/
+void QTextCursor::setCharFormat(const QTextCharFormat &format)
+{
+ if (!d || !d->priv)
+ return;
+ if (d->position == d->anchor) {
+ d->currentCharFormat = d->priv->formatCollection()->indexForFormat(format);
+ return;
+ }
+ d->setCharFormat(format, QTextDocumentPrivate::SetFormatAndPreserveObjectIndices);
+}
+
+/*!
+ Merges the cursor's current character format with the properties
+ described by format \a modifier. If the cursor has a selection,
+ this function applies all the properties set in \a modifier to all
+ the character formats that are part of the selection.
+
+ \sa hasSelection(), setCharFormat()
+*/
+void QTextCursor::mergeCharFormat(const QTextCharFormat &modifier)
+{
+ if (!d || !d->priv)
+ return;
+ if (d->position == d->anchor) {
+ QTextCharFormat format = charFormat();
+ format.merge(modifier);
+ d->currentCharFormat = d->priv->formatCollection()->indexForFormat(format);
+ return;
+ }
+
+ d->setCharFormat(modifier, QTextDocumentPrivate::MergeFormat);
+}
+
+/*!
+ Returns true if the cursor is at the start of a block; otherwise
+ returns false.
+
+ \sa atBlockEnd(), atStart()
+*/
+bool QTextCursor::atBlockStart() const
+{
+ if (!d || !d->priv)
+ return false;
+
+ return d->position == d->block().position();
+}
+
+/*!
+ Returns true if the cursor is at the end of a block; otherwise
+ returns false.
+
+ \sa atBlockStart(), atEnd()
+*/
+bool QTextCursor::atBlockEnd() const
+{
+ if (!d || !d->priv)
+ return false;
+
+ return d->position == d->block().position() + d->block().length() - 1;
+}
+
+/*!
+ Returns true if the cursor is at the start of the document;
+ otherwise returns false.
+
+ \sa atBlockStart(), atEnd()
+*/
+bool QTextCursor::atStart() const
+{
+ if (!d || !d->priv)
+ return false;
+
+ return d->position == 0;
+}
+
+/*!
+ Returns true if the cursor is at the end of the document;
+ otherwise returns false.
+
+ \sa atStart(), atBlockEnd()
+*/
+bool QTextCursor::atEnd() const
+{
+ if (!d || !d->priv)
+ return false;
+
+ return d->position == d->priv->length() - 1;
+}
+
+/*!
+ Inserts a new empty block at the cursor position() with the
+ current blockFormat() and charFormat().
+
+ \sa setBlockFormat()
+*/
+void QTextCursor::insertBlock()
+{
+ insertBlock(blockFormat());
+}
+
+/*!
+ \overload
+
+ Inserts a new empty block at the cursor position() with block
+ format \a format and the current charFormat() as block char format.
+
+ \sa setBlockFormat()
+*/
+void QTextCursor::insertBlock(const QTextBlockFormat &format)
+{
+ QTextCharFormat charFmt = charFormat();
+ charFmt.clearProperty(QTextFormat::ObjectType);
+ insertBlock(format, charFmt);
+}
+
+/*!
+ \fn void QTextCursor::insertBlock(const QTextBlockFormat &format, const QTextCharFormat &charFormat)
+ \overload
+
+ Inserts a new empty block at the cursor position() with block
+ format \a format and \a charFormat as block char format.
+
+ \sa setBlockFormat()
+*/
+void QTextCursor::insertBlock(const QTextBlockFormat &format, const QTextCharFormat &_charFormat)
+{
+ if (!d || !d->priv)
+ return;
+
+ QTextCharFormat charFormat = _charFormat;
+ charFormat.clearProperty(QTextFormat::ObjectIndex);
+
+ d->priv->beginEditBlock();
+ d->remove();
+ d->insertBlock(format, charFormat);
+ d->priv->endEditBlock();
+ d->setX();
+}
+
+/*!
+ Inserts a new block at the current position and makes it the first
+ list item of a newly created list with the given \a format. Returns
+ the created list.
+
+ \sa currentList() createList() insertBlock()
+ */
+QTextList *QTextCursor::insertList(const QTextListFormat &format)
+{
+ insertBlock();
+ return createList(format);
+}
+
+/*!
+ \overload
+
+ Inserts a new block at the current position and makes it the first
+ list item of a newly created list with the given \a style. Returns
+ the created list.
+
+ \sa currentList(), createList(), insertBlock()
+ */
+QTextList *QTextCursor::insertList(QTextListFormat::Style style)
+{
+ insertBlock();
+ return createList(style);
+}
+
+/*!
+ Creates and returns a new list with the given \a format, and makes the
+ current paragraph the cursor is in the first list item.
+
+ \sa insertList() currentList()
+ */
+QTextList *QTextCursor::createList(const QTextListFormat &format)
+{
+ if (!d || !d->priv)
+ return 0;
+
+ QTextList *list = static_cast<QTextList *>(d->priv->createObject(format));
+ QTextBlockFormat modifier;
+ modifier.setObjectIndex(list->objectIndex());
+ mergeBlockFormat(modifier);
+ return list;
+}
+
+/*!
+ \overload
+
+ Creates and returns a new list with the given \a style, making the
+ cursor's current paragraph the first list item.
+
+ The style to be used is defined by the QTextListFormat::Style enum.
+
+ \sa insertList() currentList()
+ */
+QTextList *QTextCursor::createList(QTextListFormat::Style style)
+{
+ QTextListFormat fmt;
+ fmt.setStyle(style);
+ return createList(fmt);
+}
+
+/*!
+ Returns the current list if the cursor position() is inside a
+ block that is part of a list; otherwise returns 0.
+
+ \sa insertList() createList()
+ */
+QTextList *QTextCursor::currentList() const
+{
+ if (!d || !d->priv)
+ return 0;
+
+ QTextBlockFormat b = blockFormat();
+ QTextObject *o = d->priv->objectForFormat(b);
+ return qobject_cast<QTextList *>(o);
+}
+
+/*!
+ \fn QTextTable *QTextCursor::insertTable(int rows, int columns)
+
+ \overload
+
+ Creates a new table with the given number of \a rows and \a columns,
+ inserts it at the current cursor position() in the document, and returns
+ the table object. The cursor is moved to the beginning of the first cell.
+
+ There must be at least one row and one column in the table.
+
+ \sa currentTable()
+ */
+QTextTable *QTextCursor::insertTable(int rows, int cols)
+{
+ return insertTable(rows, cols, QTextTableFormat());
+}
+
+/*!
+ \fn QTextTable *QTextCursor::insertTable(int rows, int columns, const QTextTableFormat &format)
+
+ Creates a new table with the given number of \a rows and \a columns
+ in the specified \a format, inserts it at the current cursor position()
+ in the document, and returns the table object. The cursor is moved to
+ the beginning of the first cell.
+
+ There must be at least one row and one column in the table.
+
+ \sa currentTable()
+*/
+QTextTable *QTextCursor::insertTable(int rows, int cols, const QTextTableFormat &format)
+{
+ if(!d || !d->priv || rows == 0 || cols == 0)
+ return 0;
+
+ int pos = d->position;
+ QTextTable *t = QTextTablePrivate::createTable(d->priv, d->position, rows, cols, format);
+ d->setPosition(pos+1);
+ // ##### what should we do if we have a selection?
+ d->anchor = d->position;
+ d->adjusted_anchor = d->anchor;
+ return t;
+}
+
+/*!
+ Returns a pointer to the current table if the cursor position()
+ is inside a block that is part of a table; otherwise returns 0.
+
+ \sa insertTable()
+*/
+QTextTable *QTextCursor::currentTable() const
+{
+ if(!d || !d->priv)
+ return 0;
+
+ QTextFrame *frame = d->priv->frameAt(d->position);
+ while (frame) {
+ QTextTable *table = qobject_cast<QTextTable *>(frame);
+ if (table)
+ return table;
+ frame = frame->parentFrame();
+ }
+ return 0;
+}
+
+/*!
+ Inserts a frame with the given \a format at the current cursor position(),
+ moves the cursor position() inside the frame, and returns the frame.
+
+ If the cursor holds a selection, the whole selection is moved inside the
+ frame.
+
+ \sa hasSelection()
+*/
+QTextFrame *QTextCursor::insertFrame(const QTextFrameFormat &format)
+{
+ if (!d || !d->priv)
+ return 0;
+
+ return d->priv->insertFrame(selectionStart(), selectionEnd(), format);
+}
+
+/*!
+ Returns a pointer to the current frame. Returns 0 if the cursor is invalid.
+
+ \sa insertFrame()
+*/
+QTextFrame *QTextCursor::currentFrame() const
+{
+ if(!d || !d->priv)
+ return 0;
+
+ return d->priv->frameAt(d->position);
+}
+
+
+/*!
+ Inserts the text \a fragment at the current position().
+*/
+void QTextCursor::insertFragment(const QTextDocumentFragment &fragment)
+{
+ if (!d || !d->priv || fragment.isEmpty())
+ return;
+
+ d->priv->beginEditBlock();
+ d->remove();
+ fragment.d->insert(*this);
+ d->priv->endEditBlock();
+
+ if (fragment.d && fragment.d->doc)
+ d->priv->mergeCachedResources(fragment.d->doc->docHandle());
+}
+
+/*!
+ \since 4.2
+ Inserts the text \a html at the current position(). The text is interpreted as
+ HTML.
+
+ \note When using this function with a style sheet, the style sheet will
+ only apply to the current block in the document. In order to apply a style
+ sheet throughout a document, use QTextDocument::setDefaultStyleSheet()
+ instead.
+*/
+
+#ifndef QT_NO_TEXTHTMLPARSER
+
+void QTextCursor::insertHtml(const QString &html)
+{
+ if (!d || !d->priv)
+ return;
+ QTextDocumentFragment fragment = QTextDocumentFragment::fromHtml(html, d->priv->document());
+ insertFragment(fragment);
+}
+
+#endif // QT_NO_TEXTHTMLPARSER
+
+/*!
+ \overload
+ \since 4.2
+
+ Inserts the image defined by the given \a format at the cursor's current position
+ with the specified \a alignment.
+
+ \sa position()
+*/
+void QTextCursor::insertImage(const QTextImageFormat &format, QTextFrameFormat::Position alignment)
+{
+ if (!d || !d->priv)
+ return;
+
+ QTextFrameFormat ffmt;
+ ffmt.setPosition(alignment);
+ QTextObject *obj = d->priv->createObject(ffmt);
+
+ QTextImageFormat fmt = format;
+ fmt.setObjectIndex(obj->objectIndex());
+
+ d->priv->beginEditBlock();
+ d->remove();
+ const int idx = d->priv->formatCollection()->indexForFormat(fmt);
+ d->priv->insert(d->position, QString(QChar(QChar::ObjectReplacementCharacter)), idx);
+ d->priv->endEditBlock();
+}
+
+/*!
+ Inserts the image defined by \a format at the current position().
+*/
+void QTextCursor::insertImage(const QTextImageFormat &format)
+{
+ insertText(QString(QChar::ObjectReplacementCharacter), format);
+}
+
+/*!
+ \overload
+
+ Convenience method for inserting the image with the given \a name at the
+ current position().
+
+ \snippet doc/src/snippets/code/src_gui_text_qtextcursor.cpp 1
+*/
+void QTextCursor::insertImage(const QString &name)
+{
+ QTextImageFormat format;
+ format.setName(name);
+ insertImage(format);
+}
+
+/*!
+ \since 4.5
+ \overload
+
+ Convenience function for inserting the given \a image with an optional
+ \a name at the current position().
+*/
+void QTextCursor::insertImage(const QImage &image, const QString &name)
+{
+ if (image.isNull()) {
+ qWarning("QTextCursor::insertImage: attempt to add an invalid image");
+ return;
+ }
+ QString imageName = name;
+ if (name.isEmpty())
+ imageName = QString::number(image.serialNumber());
+ d->priv->document()->addResource(QTextDocument::ImageResource, QUrl(imageName), image);
+ QTextImageFormat format;
+ format.setName(imageName);
+ insertImage(format);
+}
+
+/*!
+ \fn bool QTextCursor::operator!=(const QTextCursor &other) const
+
+ Returns true if the \a other cursor is at a different position in
+ the document as this cursor; otherwise returns false.
+*/
+bool QTextCursor::operator!=(const QTextCursor &rhs) const
+{
+ return !operator==(rhs);
+}
+
+/*!
+ \fn bool QTextCursor::operator<(const QTextCursor &other) const
+
+ Returns true if the \a other cursor is positioned later in the
+ document than this cursor; otherwise returns false.
+*/
+bool QTextCursor::operator<(const QTextCursor &rhs) const
+{
+ if (!d)
+ return !!rhs.d;
+
+ if (!rhs.d)
+ return false;
+
+ Q_ASSERT_X(d->priv == rhs.d->priv, "QTextCursor::operator<", "cannot compare cursors attached to different documents");
+
+ return d->position < rhs.d->position;
+}
+
+/*!
+ \fn bool QTextCursor::operator<=(const QTextCursor &other) const
+
+ Returns true if the \a other cursor is positioned later or at the
+ same position in the document as this cursor; otherwise returns
+ false.
+*/
+bool QTextCursor::operator<=(const QTextCursor &rhs) const
+{
+ if (!d)
+ return true;
+
+ if (!rhs.d)
+ return false;
+
+ Q_ASSERT_X(d->priv == rhs.d->priv, "QTextCursor::operator<=", "cannot compare cursors attached to different documents");
+
+ return d->position <= rhs.d->position;
+}
+
+/*!
+ \fn bool QTextCursor::operator==(const QTextCursor &other) const
+
+ Returns true if the \a other cursor is at the same position in the
+ document as this cursor; otherwise returns false.
+*/
+bool QTextCursor::operator==(const QTextCursor &rhs) const
+{
+ if (!d)
+ return !rhs.d;
+
+ if (!rhs.d)
+ return false;
+
+ return d->position == rhs.d->position && d->priv == rhs.d->priv;
+}
+
+/*!
+ \fn bool QTextCursor::operator>=(const QTextCursor &other) const
+
+ Returns true if the \a other cursor is positioned earlier or at the
+ same position in the document as this cursor; otherwise returns
+ false.
+*/
+bool QTextCursor::operator>=(const QTextCursor &rhs) const
+{
+ if (!d)
+ return false;
+
+ if (!rhs.d)
+ return true;
+
+ Q_ASSERT_X(d->priv == rhs.d->priv, "QTextCursor::operator>=", "cannot compare cursors attached to different documents");
+
+ return d->position >= rhs.d->position;
+}
+
+/*!
+ \fn bool QTextCursor::operator>(const QTextCursor &other) const
+
+ Returns true if the \a other cursor is positioned earlier in the
+ document than this cursor; otherwise returns false.
+*/
+bool QTextCursor::operator>(const QTextCursor &rhs) const
+{
+ if (!d)
+ return false;
+
+ if (!rhs.d)
+ return true;
+
+ Q_ASSERT_X(d->priv == rhs.d->priv, "QTextCursor::operator>", "cannot compare cursors attached to different documents");
+
+ return d->position > rhs.d->position;
+}
+
+/*!
+ Indicates the start of a block of editing operations on the
+ document that should appear as a single operation from an
+ undo/redo point of view.
+
+ For example:
+
+ \snippet doc/src/snippets/code/src_gui_text_qtextcursor.cpp 2
+
+ The call to undo() will cause both insertions to be undone,
+ causing both "World" and "Hello" to be removed.
+
+ It is possible to nest calls to beginEditBlock and endEditBlock. The
+ top-most pair will determine the scope of the undo/redo operation.
+
+ \sa endEditBlock()
+ */
+void QTextCursor::beginEditBlock()
+{
+ if (!d || !d->priv)
+ return;
+
+ d->priv->beginEditBlock();
+}
+
+/*!
+ Like beginEditBlock() indicates the start of a block of editing operations
+ that should appear as a single operation for undo/redo. However unlike
+ beginEditBlock() it does not start a new block but reverses the previous call to
+ endEditBlock() and therefore makes following operations part of the previous edit block created.
+
+ For example:
+
+ \snippet doc/src/snippets/code/src_gui_text_qtextcursor.cpp 3
+
+ The call to undo() will cause all three insertions to be undone.
+
+ \sa beginEditBlock(), endEditBlock()
+ */
+void QTextCursor::joinPreviousEditBlock()
+{
+ if (!d || !d->priv)
+ return;
+
+ d->priv->joinPreviousEditBlock();
+}
+
+/*!
+ Indicates the end of a block of editing operations on the document
+ that should appear as a single operation from an undo/redo point
+ of view.
+
+ \sa beginEditBlock()
+ */
+
+void QTextCursor::endEditBlock()
+{
+ if (!d || !d->priv)
+ return;
+
+ d->priv->endEditBlock();
+}
+
+/*!
+ Returns true if this cursor and \a other are copies of each other, i.e.
+ one of them was created as a copy of the other and neither has moved since.
+ This is much stricter than equality.
+
+ \sa operator=() operator==()
+*/
+bool QTextCursor::isCopyOf(const QTextCursor &other) const
+{
+ return d == other.d;
+}
+
+/*!
+ \since 4.2
+ Returns the number of the block the cursor is in, or 0 if the cursor is invalid.
+
+ Note that this function only makes sense in documents without complex objects such
+ as tables or frames.
+*/
+int QTextCursor::blockNumber() const
+{
+ if (!d || !d->priv)
+ return 0;
+
+ return d->block().blockNumber();
+}
+
+/*!
+ \since 4.2
+ Returns the position of the cursor within its containing line.
+*/
+int QTextCursor::columnNumber() const
+{
+ if (!d || !d->priv)
+ return 0;
+
+ QTextBlock block = d->block();
+ if (!block.isValid())
+ return 0;
+
+ const QTextLayout *layout = d->blockLayout(block);
+
+ const int relativePos = d->position - block.position();
+
+ if (layout->lineCount() == 0)
+ return relativePos;
+
+ QTextLine line = layout->lineForTextPosition(relativePos);
+ if (!line.isValid())
+ return 0;
+ return relativePos - line.textStart();
+}
+
+/*!
+ \since 4.5
+ Returns the document this cursor is associated with.
+*/
+QTextDocument *QTextCursor::document() const
+{
+ if (d->priv)
+ return d->priv->document();
+ return 0; // document went away
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/text/qtextcursor.h b/src/gui/text/qtextcursor.h
new file mode 100644
index 0000000000..97af32321e
--- /dev/null
+++ b/src/gui/text/qtextcursor.h
@@ -0,0 +1,232 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QTEXTCURSOR_H
+#define QTEXTCURSOR_H
+
+#include <QtCore/qstring.h>
+#include <QtCore/qshareddata.h>
+#include <QtGui/qtextformat.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+class QTextDocument;
+class QTextCursorPrivate;
+class QTextDocumentFragment;
+class QTextCharFormat;
+class QTextBlockFormat;
+class QTextListFormat;
+class QTextTableFormat;
+class QTextFrameFormat;
+class QTextImageFormat;
+class QTextDocumentPrivate;
+class QTextList;
+class QTextTable;
+class QTextFrame;
+class QTextBlock;
+
+class Q_GUI_EXPORT QTextCursor
+{
+public:
+ QTextCursor();
+ explicit QTextCursor(QTextDocument *document);
+ QTextCursor(QTextDocumentPrivate *p, int pos);
+ explicit QTextCursor(QTextFrame *frame);
+ explicit QTextCursor(const QTextBlock &block);
+ explicit QTextCursor(QTextCursorPrivate *d);
+ QTextCursor(const QTextCursor &cursor);
+ QTextCursor &operator=(const QTextCursor &other);
+ ~QTextCursor();
+
+ bool isNull() const;
+
+ enum MoveMode {
+ MoveAnchor,
+ KeepAnchor
+ };
+
+ void setPosition(int pos, MoveMode mode = MoveAnchor);
+ int position() const;
+
+ int anchor() const;
+
+ void insertText(const QString &text);
+ void insertText(const QString &text, const QTextCharFormat &format);
+
+ enum MoveOperation {
+ NoMove,
+
+ Start,
+ Up,
+ StartOfLine,
+ StartOfBlock,
+ StartOfWord,
+ PreviousBlock,
+ PreviousCharacter,
+ PreviousWord,
+ Left,
+ WordLeft,
+
+ End,
+ Down,
+ EndOfLine,
+ EndOfWord,
+ EndOfBlock,
+ NextBlock,
+ NextCharacter,
+ NextWord,
+ Right,
+ WordRight,
+
+ NextCell,
+ PreviousCell,
+ NextRow,
+ PreviousRow
+ };
+
+ bool movePosition(MoveOperation op, MoveMode = MoveAnchor, int n = 1);
+
+ bool visualNavigation() const;
+ void setVisualNavigation(bool b);
+
+ void deleteChar();
+ void deletePreviousChar();
+
+ enum SelectionType {
+ WordUnderCursor,
+ LineUnderCursor,
+ BlockUnderCursor,
+ Document
+ };
+ void select(SelectionType selection);
+
+ bool hasSelection() const;
+ bool hasComplexSelection() const;
+ void removeSelectedText();
+ void clearSelection();
+ int selectionStart() const;
+ int selectionEnd() const;
+
+ QString selectedText() const;
+ QTextDocumentFragment selection() const;
+ void selectedTableCells(int *firstRow, int *numRows, int *firstColumn, int *numColumns) const;
+
+ QTextBlock block() const;
+
+ QTextCharFormat charFormat() const;
+ void setCharFormat(const QTextCharFormat &format);
+ void mergeCharFormat(const QTextCharFormat &modifier);
+
+ QTextBlockFormat blockFormat() const;
+ void setBlockFormat(const QTextBlockFormat &format);
+ void mergeBlockFormat(const QTextBlockFormat &modifier);
+
+ QTextCharFormat blockCharFormat() const;
+ void setBlockCharFormat(const QTextCharFormat &format);
+ void mergeBlockCharFormat(const QTextCharFormat &modifier);
+
+ bool atBlockStart() const;
+ bool atBlockEnd() const;
+ bool atStart() const;
+ bool atEnd() const;
+
+ void insertBlock();
+ void insertBlock(const QTextBlockFormat &format);
+ void insertBlock(const QTextBlockFormat &format, const QTextCharFormat &charFormat);
+
+ QTextList *insertList(const QTextListFormat &format);
+ QTextList *insertList(QTextListFormat::Style style);
+
+ QTextList *createList(const QTextListFormat &format);
+ QTextList *createList(QTextListFormat::Style style);
+ QTextList *currentList() const;
+
+ QTextTable *insertTable(int rows, int cols, const QTextTableFormat &format);
+ QTextTable *insertTable(int rows, int cols);
+ QTextTable *currentTable() const;
+
+ QTextFrame *insertFrame(const QTextFrameFormat &format);
+ QTextFrame *currentFrame() const;
+
+ void insertFragment(const QTextDocumentFragment &fragment);
+
+#ifndef QT_NO_TEXTHTMLPARSER
+ void insertHtml(const QString &html);
+#endif // QT_NO_TEXTHTMLPARSER
+
+ void insertImage(const QTextImageFormat &format, QTextFrameFormat::Position alignment);
+ void insertImage(const QTextImageFormat &format);
+ void insertImage(const QString &name);
+ void insertImage(const QImage &image, const QString &name = QString());
+
+ void beginEditBlock();
+ void joinPreviousEditBlock();
+ void endEditBlock();
+
+ bool operator!=(const QTextCursor &rhs) const;
+ bool operator<(const QTextCursor &rhs) const;
+ bool operator<=(const QTextCursor &rhs) const;
+ bool operator==(const QTextCursor &rhs) const;
+ bool operator>=(const QTextCursor &rhs) const;
+ bool operator>(const QTextCursor &rhs) const;
+
+ bool isCopyOf(const QTextCursor &other) const;
+
+ int blockNumber() const;
+ int columnNumber() const;
+
+ QTextDocument *document() const;
+
+private:
+ QSharedDataPointer<QTextCursorPrivate> d;
+ friend class QTextDocumentFragmentPrivate;
+ friend class QTextCopyHelper;
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QTEXTCURSOR_H
diff --git a/src/gui/text/qtextcursor_p.h b/src/gui/text/qtextcursor_p.h
new file mode 100644
index 0000000000..b16af219e7
--- /dev/null
+++ b/src/gui/text/qtextcursor_p.h
@@ -0,0 +1,120 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QTEXTCURSOR_P_H
+#define QTEXTCURSOR_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 "qtextcursor.h"
+#include "qtextdocument.h"
+#include "qtextdocument_p.h"
+#include <private/qtextformat_p.h>
+#include "qtextobject.h"
+
+QT_BEGIN_NAMESPACE
+
+class QTextCursorPrivate : public QSharedData
+{
+public:
+ QTextCursorPrivate(QTextDocumentPrivate *p);
+ QTextCursorPrivate(const QTextCursorPrivate &rhs);
+ ~QTextCursorPrivate();
+
+ enum AdjustResult { CursorMoved, CursorUnchanged };
+ AdjustResult adjustPosition(int positionOfChange, int charsAddedOrRemoved, QTextUndoCommand::Operation op);
+
+ void adjustCursor(QTextCursor::MoveOperation m);
+
+ void remove();
+ void clearCells(QTextTable *table, int startRow, int startCol, int numRows, int numCols, QTextUndoCommand::Operation op);
+ inline bool setPosition(int newPosition) {
+ Q_ASSERT(newPosition >= 0 && newPosition < priv->length());
+ bool moved = position != newPosition;
+ if (moved) {
+ position = newPosition;
+ currentCharFormat = -1;
+ }
+ return moved;
+ }
+ void setX();
+ bool canDelete(int pos) const;
+
+ void insertBlock(const QTextBlockFormat &format, const QTextCharFormat &charFormat);
+ bool movePosition(QTextCursor::MoveOperation op, QTextCursor::MoveMode mode = QTextCursor::MoveAnchor);
+
+ inline QTextBlock block() const
+ { return QTextBlock(priv, priv->blockMap().findNode(position)); }
+ inline QTextBlockFormat blockFormat() const
+ { return block().blockFormat(); }
+
+ QTextLayout *blockLayout(QTextBlock &block) const;
+
+ QTextTable *complexSelectionTable() const;
+ void selectedTableCells(int *firstRow, int *numRows, int *firstColumn, int *numColumns) const;
+
+ void setBlockCharFormat(const QTextCharFormat &format, QTextDocumentPrivate::FormatChangeMode changeMode);
+ void setBlockFormat(const QTextBlockFormat &format, QTextDocumentPrivate::FormatChangeMode changeMode);
+ void setCharFormat(const QTextCharFormat &format, QTextDocumentPrivate::FormatChangeMode changeMode);
+
+ void aboutToRemoveCell(int from, int to);
+
+ QTextDocumentPrivate *priv;
+ qreal x;
+ int position;
+ int anchor;
+ int adjusted_anchor;
+ int currentCharFormat;
+ bool visualNavigation;
+};
+
+QT_END_NAMESPACE
+
+#endif // QTEXTCURSOR_P_H
diff --git a/src/gui/text/qtextdocument.cpp b/src/gui/text/qtextdocument.cpp
new file mode 100644
index 0000000000..e84b3240ff
--- /dev/null
+++ b/src/gui/text/qtextdocument.cpp
@@ -0,0 +1,2929 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qtextdocument.h"
+#include <qtextformat.h>
+#include "qtextdocumentlayout_p.h"
+#include "qtextdocumentfragment.h"
+#include "qtextdocumentfragment_p.h"
+#include "qtexttable.h"
+#include "qtextlist.h"
+#include <qdebug.h>
+#include <qregexp.h>
+#include <qvarlengtharray.h>
+#include <qtextcodec.h>
+#include <qthread.h>
+#include "qtexthtmlparser_p.h"
+#include "qpainter.h"
+#include "qprinter.h"
+#include "qtextedit.h"
+#include <qfile.h>
+#include <qfileinfo.h>
+#include <qdir.h>
+#include <qapplication.h>
+#include "qtextcontrol_p.h"
+#include "private/qtextedit_p.h"
+
+#include "qtextdocument_p.h"
+#include <private/qprinter_p.h>
+
+#include <limits.h>
+
+QT_BEGIN_NAMESPACE
+
+Q_CORE_EXPORT unsigned int qt_int_sqrt(unsigned int n);
+
+/*!
+ 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.
+
+ This function is defined in the \c <QTextDocument> header file.
+*/
+bool Qt::mightBeRichText(const QString& text)
+{
+ if (text.isEmpty())
+ return false;
+ int start = 0;
+
+ while (start < text.length() && text.at(start).isSpace())
+ ++start;
+
+ // skip a leading <?xml ... ?> as for example with xhtml
+ if (text.mid(start, 5) == QLatin1String("<?xml")) {
+ while (start < text.length()) {
+ if (text.at(start) == QLatin1Char('?')
+ && start + 2 < text.length()
+ && text.at(start + 1) == QLatin1Char('>')) {
+ start += 2;
+ break;
+ }
+ ++start;
+ }
+
+ while (start < text.length() && text.at(start).isSpace())
+ ++start;
+ }
+
+ if (text.mid(start, 5).toLower() == QLatin1String("<!doc"))
+ return true;
+ int open = start;
+ while (open < text.length() && text.at(open) != QLatin1Char('<')
+ && text.at(open) != QLatin1Char('\n')) {
+ if (text.at(open) == QLatin1Char('&') && text.mid(open+1,3) == QLatin1String("lt;"))
+ return true; // support desperate attempt of user to see <...>
+ ++open;
+ }
+ if (open < text.length() && text.at(open) == QLatin1Char('<')) {
+ const int close = text.indexOf(QLatin1Char('>'), open);
+ if (close > -1) {
+ QString tag;
+ for (int i = open+1; i < close; ++i) {
+ if (text[i].isDigit() || text[i].isLetter())
+ tag += text[i];
+ else if (!tag.isEmpty() && text[i].isSpace())
+ break;
+ else if (!text[i].isSpace() && (!tag.isEmpty() || text[i] != QLatin1Char('!')))
+ return false; // that's not a tag
+ }
+#ifndef QT_NO_TEXTHTMLPARSER
+ return QTextHtmlParser::lookupElement(tag.toLower()) != -1;
+#else
+ return false;
+#endif // QT_NO_TEXTHTMLPARSER
+ }
+ }
+ return false;
+}
+
+/*!
+ Converts the plain text string \a plain to a HTML string with
+ HTML metacharacters \c{<}, \c{>}, and \c{&} replaced by HTML
+ entities.
+
+ Example:
+
+ \snippet doc/src/snippets/code/src_gui_text_qtextdocument.cpp 0
+
+ This function is defined in the \c <QTextDocument> header file.
+
+ \sa convertFromPlainText(), mightBeRichText()
+*/
+QString Qt::escape(const QString& plain)
+{
+ QString rich;
+ rich.reserve(int(plain.length() * 1.1));
+ for (int i = 0; i < plain.length(); ++i) {
+ if (plain.at(i) == QLatin1Char('<'))
+ rich += QLatin1String("&lt;");
+ else if (plain.at(i) == QLatin1Char('>'))
+ rich += QLatin1String("&gt;");
+ else if (plain.at(i) == QLatin1Char('&'))
+ rich += QLatin1String("&amp;");
+ else
+ rich += plain.at(i);
+ }
+ return rich;
+}
+
+/*!
+ \fn QString Qt::convertFromPlainText(const QString &plain, WhiteSpaceMode mode)
+
+ Converts the plain text string \a plain to an HTML-formatted
+ paragraph while preserving most of its look.
+
+ \a mode defines how whitespace is handled.
+
+ This function is defined in the \c <QTextDocument> header file.
+
+ \sa escape(), mightBeRichText()
+*/
+QString Qt::convertFromPlainText(const QString &plain, Qt::WhiteSpaceMode mode)
+{
+ int col = 0;
+ QString rich;
+ rich += QLatin1String("<p>");
+ for (int i = 0; i < plain.length(); ++i) {
+ if (plain[i] == QLatin1Char('\n')){
+ int c = 1;
+ while (i+1 < plain.length() && plain[i+1] == QLatin1Char('\n')) {
+ i++;
+ c++;
+ }
+ if (c == 1)
+ rich += QLatin1String("<br>\n");
+ else {
+ rich += QLatin1String("</p>\n");
+ while (--c > 1)
+ rich += QLatin1String("<br>\n");
+ rich += QLatin1String("<p>");
+ }
+ col = 0;
+ } else {
+ if (mode == Qt::WhiteSpacePre && plain[i] == QLatin1Char('\t')){
+ rich += QChar(0x00a0U);
+ ++col;
+ while (col % 8) {
+ rich += QChar(0x00a0U);
+ ++col;
+ }
+ }
+ else if (mode == Qt::WhiteSpacePre && plain[i].isSpace())
+ rich += QChar(0x00a0U);
+ else if (plain[i] == QLatin1Char('<'))
+ rich += QLatin1String("&lt;");
+ else if (plain[i] == QLatin1Char('>'))
+ rich += QLatin1String("&gt;");
+ else if (plain[i] == QLatin1Char('&'))
+ rich += QLatin1String("&amp;");
+ else
+ rich += plain[i];
+ ++col;
+ }
+ }
+ if (col != 0)
+ rich += QLatin1String("</p>");
+ return rich;
+}
+
+#ifndef QT_NO_TEXTCODEC
+/*!
+ \internal
+
+ This function is defined in the \c <QTextDocument> header file.
+*/
+QTextCodec *Qt::codecForHtml(const QByteArray &ba)
+{
+ return QTextCodec::codecForHtml(ba);
+}
+#endif
+
+/*!
+ \class QTextDocument
+ \reentrant
+
+ \brief The QTextDocument class holds formatted text that can be
+ viewed and edited using a QTextEdit.
+
+ \ingroup text
+ \mainclass
+
+ QTextDocument is a container for structured rich text documents, providing
+ support for styled text and various types of document elements, such as
+ lists, tables, frames, and images.
+ They can be created for use in a QTextEdit, or used independently.
+
+ Each document element is described by an associated format object. Each
+ format object is treated as a unique object by QTextDocuments, and can be
+ passed to objectForFormat() to obtain the document element that it is
+ applied to.
+
+ A QTextDocument can be edited programmatically using a QTextCursor, and
+ its contents can be examined by traversing the document structure. The
+ entire document structure is stored as a hierarchy of document elements
+ beneath the root frame, found with the rootFrame() function. Alternatively,
+ if you just want to iterate over the textual contents of the document you
+ can use begin(), end(), and findBlock() to retrieve text blocks that you
+ can examine and iterate over.
+
+ The layout of a document is determined by the documentLayout();
+ you can create your own QAbstractTextDocumentLayout subclass and
+ set it using setDocumentLayout() if you want to use your own
+ layout logic. The document's title and other meta-information can be
+ obtained by calling the metaInformation() function. For documents that
+ are exposed to users through the QTextEdit class, the document title
+ is also available via the QTextEdit::documentTitle() function.
+
+ The toPlainText() and toHtml() convenience functions allow you to retrieve the
+ contents of the document as plain text and HTML.
+ The document's text can be searched using the find() functions.
+
+ Undo/redo of operations performed on the document can be controlled using
+ the setUndoRedoEnabled() function. The undo/redo system can be controlled
+ by an editor widget through the undo() and redo() slots; the document also
+ provides contentsChanged(), undoAvailable(), and redoAvailable() signals
+ that inform connected editor widgets about the state of the undo/redo
+ system.
+
+ \sa QTextCursor QTextEdit \link richtext.html Rich Text Processing\endlink
+*/
+
+/*!
+ \property QTextDocument::defaultFont
+ \brief the default font used to display the document's text
+*/
+
+/*!
+ \property QTextDocument::defaultTextOption
+ \brief the default text option will be set on all \l{QTextLayout}s in the document.
+
+ When \l{QTextBlock}s are created, the defaultTextOption is set on their
+ QTextLayout. This allows setting global properties for the document such as the
+ default word wrap mode.
+ */
+
+/*!
+ Constructs an empty QTextDocument with the given \a parent.
+*/
+QTextDocument::QTextDocument(QObject *parent)
+ : QObject(*new QTextDocumentPrivate, parent)
+{
+ Q_D(QTextDocument);
+ d->init();
+}
+
+/*!
+ Constructs a QTextDocument containing the plain (unformatted) \a text
+ specified, and with the given \a parent.
+*/
+QTextDocument::QTextDocument(const QString &text, QObject *parent)
+ : QObject(*new QTextDocumentPrivate, parent)
+{
+ Q_D(QTextDocument);
+ d->init();
+ QTextCursor(this).insertText(text);
+}
+
+/*!
+ \internal
+*/
+QTextDocument::QTextDocument(QTextDocumentPrivate &dd, QObject *parent)
+ : QObject(dd, parent)
+{
+ Q_D(QTextDocument);
+ d->init();
+}
+
+/*!
+ Destroys the document.
+*/
+QTextDocument::~QTextDocument()
+{
+}
+
+
+/*!
+ Creates a new QTextDocument that is a copy of this text document. \a
+ parent is the parent of the returned text document.
+*/
+QTextDocument *QTextDocument::clone(QObject *parent) const
+{
+ Q_D(const QTextDocument);
+ QTextDocument *doc = new QTextDocument(parent);
+ QTextCursor(doc).insertFragment(QTextDocumentFragment(this));
+ doc->rootFrame()->setFrameFormat(rootFrame()->frameFormat());
+ QTextDocumentPrivate *priv = doc->d_func();
+ priv->title = d->title;
+ priv->url = d->url;
+ priv->pageSize = d->pageSize;
+ priv->indentWidth = d->indentWidth;
+ priv->defaultTextOption = d->defaultTextOption;
+ priv->setDefaultFont(d->defaultFont());
+ priv->resources = d->resources;
+ priv->cachedResources.clear();
+#ifndef QT_NO_CSSPARSER
+ priv->defaultStyleSheet = d->defaultStyleSheet;
+ priv->parsedDefaultStyleSheet = d->parsedDefaultStyleSheet;
+#endif
+ return doc;
+}
+
+/*!
+ Returns true if the document is empty; otherwise returns false.
+*/
+bool QTextDocument::isEmpty() const
+{
+ Q_D(const QTextDocument);
+ /* because if we're empty we still have one single paragraph as
+ * one single fragment */
+ return d->length() <= 1;
+}
+
+/*!
+ Clears the document.
+*/
+void QTextDocument::clear()
+{
+ Q_D(QTextDocument);
+ d->clear();
+ d->resources.clear();
+}
+
+/*!
+ \since 4.2
+
+ Undoes the last editing operation on the document if undo is
+ available. The provided \a cursor is positioned at the end of the
+ location where the edition operation was undone.
+
+ See the \l {Overview of Qt's Undo Framework}{Qt Undo Framework}
+ documentation for details.
+
+ \sa undoAvailable(), isUndoRedoEnabled()
+*/
+void QTextDocument::undo(QTextCursor *cursor)
+{
+ Q_D(QTextDocument);
+ const int pos = d->undoRedo(true);
+ if (cursor && pos >= 0) {
+ *cursor = QTextCursor(this);
+ cursor->setPosition(pos);
+ }
+}
+
+/*!
+ \since 4.2
+ Redoes the last editing operation on the document if \link
+ QTextDocument::isRedoAvailable() redo is available\endlink.
+
+ The provided \a cursor is positioned at the end of the location where
+ the edition operation was redone.
+*/
+void QTextDocument::redo(QTextCursor *cursor)
+{
+ Q_D(QTextDocument);
+ const int pos = d->undoRedo(false);
+ if (cursor && pos >= 0) {
+ *cursor = QTextCursor(this);
+ cursor->setPosition(pos);
+ }
+}
+
+/*!
+ \overload
+
+*/
+void QTextDocument::undo()
+{
+ Q_D(QTextDocument);
+ d->undoRedo(true);
+}
+
+/*!
+ \overload
+ Redoes the last editing operation on the document if \link
+ QTextDocument::isRedoAvailable() redo is available\endlink.
+*/
+void QTextDocument::redo()
+{
+ Q_D(QTextDocument);
+ d->undoRedo(false);
+}
+
+/*!
+ \internal
+
+ Appends a custom undo \a item to the undo stack.
+*/
+void QTextDocument::appendUndoItem(QAbstractUndoItem *item)
+{
+ Q_D(QTextDocument);
+ d->appendUndoItem(item);
+}
+
+/*!
+ \property QTextDocument::undoRedoEnabled
+ \brief whether undo/redo are enabled for this document
+
+ This defaults to true. If disabled, the undo stack is cleared and
+ no items will be added to it.
+*/
+void QTextDocument::setUndoRedoEnabled(bool enable)
+{
+ Q_D(QTextDocument);
+ d->enableUndoRedo(enable);
+}
+
+bool QTextDocument::isUndoRedoEnabled() const
+{
+ Q_D(const QTextDocument);
+ return d->isUndoRedoEnabled();
+}
+
+/*!
+ \property QTextDocument::maximumBlockCount
+ \since 4.2
+ \brief Specifies the limit for blocks in the document.
+
+ Specifies the maximum number of blocks the document may have. If there are
+ more blocks in the document that specified with this property blocks are removed
+ from the beginning of the document.
+
+ A negative or zero value specifies that the document may contain an unlimited
+ amount of blocks.
+
+ The default value is 0.
+
+ Note that setting this property will apply the limit immediately to the document
+ contents.
+
+ Setting this property also disables the undo redo history.
+
+ This property is undefined in documents with tables or frames.
+*/
+int QTextDocument::maximumBlockCount() const
+{
+ Q_D(const QTextDocument);
+ return d->maximumBlockCount;
+}
+
+void QTextDocument::setMaximumBlockCount(int maximum)
+{
+ Q_D(QTextDocument);
+ d->maximumBlockCount = maximum;
+ d->ensureMaximumBlockCount();
+ setUndoRedoEnabled(false);
+}
+
+/*!
+ \since 4.3
+
+ The default text option is used on all QTextLayout objects in the document.
+ This allows setting global properties for the document such as the default
+ word wrap mode.
+*/
+QTextOption QTextDocument::defaultTextOption() const
+{
+ Q_D(const QTextDocument);
+ return d->defaultTextOption;
+}
+
+/*!
+ \since 4.3
+
+ Sets the default text option.
+*/
+void QTextDocument::setDefaultTextOption(const QTextOption &option)
+{
+ Q_D(QTextDocument);
+ d->defaultTextOption = option;
+ if (d->lout)
+ d->lout->documentChanged(0, 0, d->length());
+}
+
+/*!
+ \fn void QTextDocument::markContentsDirty(int position, int length)
+
+ Marks the contents specified by the given \a position and \a length
+ as "dirty", informing the document that it needs to be laid out
+ again.
+*/
+void QTextDocument::markContentsDirty(int from, int length)
+{
+ Q_D(QTextDocument);
+ if (!d->inContentsChange)
+ d->beginEditBlock();
+ d->documentChange(from, length);
+ if (!d->inContentsChange)
+ d->endEditBlock();
+}
+
+/*!
+ \property QTextDocument::useDesignMetrics
+ \since 4.1
+ \brief whether the document uses design metrics of fonts to improve the accuracy of text layout
+
+ If this property is set to true, the layout will use design metrics.
+ Otherwise, the metrics of the paint device as set on
+ QAbstractTextDocumentLayout::setPaintDevice() will be used.
+
+ Using design metrics makes a layout have a width that is no longer dependent on hinting
+ and pixel-rounding. This means that WYSIWYG text layout becomes possible because the width
+ scales much more linearly based on paintdevice metrics than it would otherwise.
+
+ By default, this property is false.
+*/
+
+void QTextDocument::setUseDesignMetrics(bool b)
+{
+ Q_D(QTextDocument);
+ if (b == d->defaultTextOption.useDesignMetrics())
+ return;
+ d->defaultTextOption.setUseDesignMetrics(b);
+ if (d->lout)
+ d->lout->documentChanged(0, 0, d->length());
+}
+
+bool QTextDocument::useDesignMetrics() const
+{
+ Q_D(const QTextDocument);
+ return d->defaultTextOption.useDesignMetrics();
+}
+
+/*!
+ \since 4.2
+
+ Draws the content of the document with painter \a p, clipped to \a rect.
+ If \a rect is a null rectangle (default) then the document is painted unclipped.
+*/
+void QTextDocument::drawContents(QPainter *p, const QRectF &rect)
+{
+ p->save();
+ QAbstractTextDocumentLayout::PaintContext ctx;
+ if (rect.isValid()) {
+ p->setClipRect(rect);
+ ctx.clip = rect;
+ }
+ documentLayout()->draw(p, ctx);
+ p->restore();
+}
+
+/*!
+ \property QTextDocument::textWidth
+ \since 4.2
+
+ The text width specifies the preferred width for text in the document. If
+ the text (or content in general) is wider than the specified with it is broken
+ into multiple lines and grows vertically. If the text cannot be broken into multiple
+ lines to fit into the specified text width it will be larger and the size() and the
+ idealWidth() property will reflect that.
+
+ If the text width is set to -1 then the text will not be broken into multiple lines
+ unless it is enforced through an explicit line break or a new paragraph.
+
+ The default value is -1.
+
+ Setting the text width will also set the page height to -1, causing the document to
+ grow or shrink vertically in a continuous way. If you want the document layout to break
+ the text into multiple pages then you have to set the pageSize property instead.
+
+ \sa size(), idealWidth(), pageSize()
+*/
+void QTextDocument::setTextWidth(qreal width)
+{
+ Q_D(QTextDocument);
+ QSizeF sz = d->pageSize;
+ sz.setWidth(width);
+ sz.setHeight(-1);
+ setPageSize(sz);
+}
+
+qreal QTextDocument::textWidth() const
+{
+ Q_D(const QTextDocument);
+ return d->pageSize.width();
+}
+
+/*!
+ \since 4.2
+
+ Returns the ideal width of the text document. The ideal width is the actually used width
+ of the document without optional alignments taken into account. It is always <= size().width().
+
+ \sa adjustSize(), textWidth
+*/
+qreal QTextDocument::idealWidth() const
+{
+ if (QTextDocumentLayout *lout = qobject_cast<QTextDocumentLayout *>(documentLayout()))
+ return lout->idealWidth();
+ return textWidth();
+}
+
+/*!
+ \property QTextDocument::documentMargin
+ \since 4.5
+
+ The margin around the document. The default is 4.
+*/
+qreal QTextDocument::documentMargin() const
+{
+ Q_D(const QTextDocument);
+ return d->documentMargin;
+}
+
+void QTextDocument::setDocumentMargin(qreal margin)
+{
+ Q_D(QTextDocument);
+ if (d->documentMargin != margin) {
+ d->documentMargin = margin;
+
+ QTextFrame* root = rootFrame();
+ QTextFrameFormat format = root->frameFormat();
+ format.setMargin(margin);
+ root->setFrameFormat(format);
+
+ if (d->lout)
+ d->lout->documentChanged(0, 0, d->length());
+ }
+}
+
+
+/*!
+ \property QTextDocument::indentWidth
+ \since 4.4
+
+ Returns the width used for text list and text block indenting.
+
+ The indent properties of QTextListFormat and QTextBlockFormat specify
+ multiples of this value. The default indent width is 40.
+*/
+qreal QTextDocument::indentWidth() const
+{
+ Q_D(const QTextDocument);
+ return d->indentWidth;
+}
+
+
+/*!
+ \since 4.4
+
+ Sets the \a width used for text list and text block indenting.
+
+ The indent properties of QTextListFormat and QTextBlockFormat specify
+ multiples of this value. The default indent width is 40 .
+
+ \sa indentWidth()
+*/
+void QTextDocument::setIndentWidth(qreal width)
+{
+ Q_D(QTextDocument);
+ if (d->indentWidth != width) {
+ d->indentWidth = width;
+ if (d->lout)
+ d->lout->documentChanged(0, 0, d->length());
+ }
+}
+
+
+
+
+/*!
+ \since 4.2
+
+ Adjusts the document to a reasonable size.
+
+ \sa idealWidth(), textWidth, size
+*/
+void QTextDocument::adjustSize()
+{
+ // Pull this private function in from qglobal.cpp
+ QFont f = defaultFont();
+ QFontMetrics fm(f);
+ int mw = fm.width(QLatin1Char('x')) * 80;
+ int w = mw;
+ setTextWidth(w);
+ QSizeF size = documentLayout()->documentSize();
+ if (size.width() != 0) {
+ w = qt_int_sqrt((uint)(5 * size.height() * size.width() / 3));
+ setTextWidth(qMin(w, mw));
+
+ size = documentLayout()->documentSize();
+ if (w*3 < 5*size.height()) {
+ w = qt_int_sqrt((uint)(2 * size.height() * size.width()));
+ setTextWidth(qMin(w, mw));
+ }
+ }
+ setTextWidth(idealWidth());
+}
+
+/*!
+ \property QTextDocument::size
+ \since 4.2
+
+ Returns the actual size of the document.
+ This is equivalent to documentLayout()->documentSize();
+
+ The size of the document can be changed either by setting
+ a text width or setting an entire page size.
+
+ Note that the width is always >= pageSize().width().
+
+ By default, for a newly-created, empty document, this property contains
+ a configuration-dependent size.
+
+ \sa setTextWidth(), setPageSize(), idealWidth()
+*/
+QSizeF QTextDocument::size() const
+{
+ return documentLayout()->documentSize();
+}
+
+/*!
+ \property QTextDocument::blockCount
+ \since 4.2
+
+ Returns the number of text blocks in the document.
+
+ The value of this property is undefined in documents with tables or frames.
+
+ By default, if defined, this property contains a value of 1.
+ \sa lineCount(), characterCount()
+*/
+int QTextDocument::blockCount() const
+{
+ Q_D(const QTextDocument);
+ return d->blockMap().numNodes();
+}
+
+
+/*!
+ \since 4.5
+
+ Returns the number of lines of this document (if the layout supports
+ this). Otherwise, this is identical to the number of blocks.
+
+ \sa blockCount(), characterCount()
+ */
+int QTextDocument::lineCount() const
+{
+ Q_D(const QTextDocument);
+ return d->blockMap().length(2);
+}
+
+/*!
+ \since 4.5
+
+ Returns the number of characters of this document.
+
+ \sa blockCount(), characterAt()
+ */
+int QTextDocument::characterCount() const
+{
+ Q_D(const QTextDocument);
+ return d->length();
+}
+
+/*!
+ \since 4.5
+
+ Returns the character at position \a pos, or a null character if the
+ position is out of range.
+
+ \sa characterCount()
+ */
+QChar QTextDocument::characterAt(int pos) const
+{
+ Q_D(const QTextDocument);
+ if (pos < 0 || pos >= d->length())
+ return QChar();
+ QTextDocumentPrivate::FragmentIterator fragIt = d->find(pos);
+ const QTextFragmentData * const frag = fragIt.value();
+ const int offsetInFragment = qMax(0, pos - fragIt.position());
+ return d->text.at(frag->stringPosition + offsetInFragment);
+}
+
+
+/*!
+ \property QTextDocument::defaultStyleSheet
+ \since 4.2
+
+ The default style sheet is applied to all newly HTML formatted text that is
+ inserted into the document, for example using setHtml() or QTextCursor::insertHtml().
+
+ The style sheet needs to be compliant to CSS 2.1 syntax.
+
+ \bold{Note:} Changing the default style sheet does not have any effect to the existing content
+ of the document.
+
+ \sa {Supported HTML Subset}
+*/
+
+#ifndef QT_NO_CSSPARSER
+void QTextDocument::setDefaultStyleSheet(const QString &sheet)
+{
+ Q_D(QTextDocument);
+ d->defaultStyleSheet = sheet;
+ QCss::Parser parser(sheet);
+ d->parsedDefaultStyleSheet = QCss::StyleSheet();
+ d->parsedDefaultStyleSheet.origin = QCss::StyleSheetOrigin_UserAgent;
+ parser.parse(&d->parsedDefaultStyleSheet);
+}
+
+QString QTextDocument::defaultStyleSheet() const
+{
+ Q_D(const QTextDocument);
+ return d->defaultStyleSheet;
+}
+#endif // QT_NO_CSSPARSER
+
+/*!
+ \fn void QTextDocument::contentsChanged()
+
+ This signal is emitted whenever the document's content changes; for
+ example, when text is inserted or deleted, or when formatting is applied.
+
+ \sa contentsChange()
+*/
+
+/*!
+ \fn void QTextDocument::contentsChange(int position, int charsRemoved, int charsAdded)
+
+ This signal is emitted whenever the document's content changes; for
+ example, when text is inserted or deleted, or when formatting is applied.
+
+ Information is provided about the \a position of the character in the
+ document where the change occurred, the number of characters removed
+ (\a charsRemoved), and the number of characters added (\a charsAdded).
+
+ The signal is emitted before the document's layout manager is notified
+ about the change. This hook allows you to implement syntax highlighting
+ for the document.
+
+ \sa QAbstractTextDocumentLayout::documentChanged(), contentsChanged()
+*/
+
+
+/*!
+ \fn QTextDocument::undoAvailable(bool available);
+
+ This signal is emitted whenever undo operations become available
+ (\a available is true) or unavailable (\a available is false).
+
+ See the \l {Overview of Qt's Undo Framework}{Qt Undo Framework}
+ documentation for details.
+
+ \sa undo(), isUndoRedoEnabled()
+*/
+
+/*!
+ \fn QTextDocument::redoAvailable(bool available);
+
+ This signal is emitted whenever redo operations become available
+ (\a available is true) or unavailable (\a available is false).
+*/
+
+/*!
+ \fn QTextDocument::cursorPositionChanged(const QTextCursor &cursor);
+
+ This signal is emitted whenever the position of a cursor changed
+ due to an editing operation. The cursor that changed is passed in
+ \a cursor. If you need a signal when the cursor is moved with the
+ arrow keys you can use the \l{QTextEdit::}{cursorPositionChanged()} signal in
+ QTextEdit.
+*/
+
+/*!
+ \fn QTextDocument::blockCountChanged(int newBlockCount);
+ \since 4.3
+
+ This signal is emitted when the total number of text blocks in the
+ document changes. The value passed in \a newBlockCount is the new
+ total.
+*/
+
+/*!
+ \fn QTextDocument::documentLayoutChanged();
+ \since 4.4
+
+ This signal is emitted when a new document layout is set.
+
+ \sa setDocumentLayout()
+
+*/
+
+
+/*!
+ Returns true if undo is available; otherwise returns false.
+*/
+bool QTextDocument::isUndoAvailable() const
+{
+ Q_D(const QTextDocument);
+ return d->isUndoAvailable();
+}
+
+/*!
+ Returns true if redo is available; otherwise returns false.
+*/
+bool QTextDocument::isRedoAvailable() const
+{
+ Q_D(const QTextDocument);
+ return d->isRedoAvailable();
+}
+
+
+/*! \since 4.4
+
+ Returns the document's revision (if undo is enabled).
+
+ The revision is guaranteed to increase when a document that is not
+ modified is edited.
+
+ \sa QTextBlock::revision(), isModified()
+ */
+int QTextDocument::revision() const
+{
+ Q_D(const QTextDocument);
+ return d->undoState;
+}
+
+
+
+/*!
+ Sets the document to use the given \a layout. The previous layout
+ is deleted.
+
+ \sa documentLayoutChanged()
+*/
+void QTextDocument::setDocumentLayout(QAbstractTextDocumentLayout *layout)
+{
+ Q_D(QTextDocument);
+ d->setLayout(layout);
+}
+
+/*!
+ Returns the document layout for this document.
+*/
+QAbstractTextDocumentLayout *QTextDocument::documentLayout() const
+{
+ Q_D(const QTextDocument);
+ if (!d->lout) {
+ QTextDocument *that = const_cast<QTextDocument *>(this);
+ that->d_func()->setLayout(new QTextDocumentLayout(that));
+ }
+ return d->lout;
+}
+
+
+/*!
+ Returns meta information about the document of the type specified by
+ \a info.
+
+ \sa setMetaInformation()
+*/
+QString QTextDocument::metaInformation(MetaInformation info) const
+{
+ Q_D(const QTextDocument);
+ switch (info) {
+ case DocumentTitle:
+ return d->title;
+ case DocumentUrl:
+ return d->url;
+ }
+ return QString();
+}
+
+/*!
+ Sets the document's meta information of the type specified by \a info
+ to the given \a string.
+
+ \sa metaInformation()
+*/
+void QTextDocument::setMetaInformation(MetaInformation info, const QString &string)
+{
+ Q_D(QTextDocument);
+ switch (info) {
+ case DocumentTitle:
+ d->title = string;
+ break;
+ case DocumentUrl:
+ d->url = string;
+ break;
+ }
+}
+
+/*!
+ Returns the plain text contained in the document. If you want
+ formatting information use a QTextCursor instead.
+
+ \sa toHtml()
+*/
+QString QTextDocument::toPlainText() const
+{
+ Q_D(const QTextDocument);
+ QString txt = d->plainText();
+
+ QChar *uc = txt.data();
+ QChar *e = uc + txt.size();
+
+ for (; uc != e; ++uc) {
+ switch (uc->unicode()) {
+ case 0xfdd0: // QTextBeginningOfFrame
+ case 0xfdd1: // QTextEndOfFrame
+ case QChar::ParagraphSeparator:
+ case QChar::LineSeparator:
+ *uc = QLatin1Char('\n');
+ break;
+ case QChar::Nbsp:
+ *uc = QLatin1Char(' ');
+ break;
+ default:
+ ;
+ }
+ }
+ return txt;
+}
+
+/*!
+ Replaces the entire contents of the document with the given plain
+ \a text.
+
+ \sa setHtml()
+*/
+void QTextDocument::setPlainText(const QString &text)
+{
+ Q_D(QTextDocument);
+ bool previousState = d->isUndoRedoEnabled();
+ d->enableUndoRedo(false);
+ d->clear();
+ QTextCursor(this).insertText(text);
+ d->enableUndoRedo(previousState);
+}
+
+/*!
+ Replaces the entire contents of the document with the given
+ HTML-formatted text in the \a html string.
+
+ The HTML formatting is respected as much as possible; for example,
+ "<b>bold</b> text" will produce text where the first word has a font
+ weight that gives it a bold appearance: "\bold{bold} text".
+
+ \note It is the responsibility of the caller to make sure that the
+ text is correctly decoded when a QString containing HTML is created
+ and passed to setHtml().
+
+ \sa setPlainText(), {Supported HTML Subset}
+*/
+
+#ifndef QT_NO_TEXTHTMLPARSER
+
+void QTextDocument::setHtml(const QString &html)
+{
+ Q_D(QTextDocument);
+ bool previousState = d->isUndoRedoEnabled();
+ d->enableUndoRedo(false);
+ d->clear();
+ QTextHtmlImporter(this, html, QTextHtmlImporter::ImportToDocument).import();
+ d->enableUndoRedo(previousState);
+}
+
+#endif // QT_NO_TEXTHTMLPARSER
+
+/*!
+ \enum QTextDocument::FindFlag
+
+ This enum describes the options available to QTextDocument's find function. The options
+ can be OR-ed together from the following list:
+
+ \value FindBackward Search backwards instead of forwards.
+ \value FindCaseSensitively By default find works case insensitive. Specifying this option
+ changes the behaviour to a case sensitive find operation.
+ \value FindWholeWords Makes find match only complete words.
+*/
+
+/*!
+ \enum QTextDocument::MetaInformation
+
+ This enum describes the different types of meta information that can be
+ added to a document.
+
+ \value DocumentTitle The title of the document.
+ \value DocumentUrl The url of the document. The loadResource() function uses
+ this url as the base when loading relative resources.
+
+ \sa metaInformation(), setMetaInformation()
+*/
+
+/*!
+ \fn QTextCursor QTextDocument::find(const QString &subString, int position, FindFlags options) const
+
+ \overload
+
+ Finds the next occurrence of the string, \a subString, in the document.
+ The search starts at the given \a position, and proceeds forwards
+ through the document unless specified otherwise in the search options.
+ The \a options control the type of search performed.
+
+ Returns a cursor with the match selected if \a subString
+ was found; otherwise returns a null cursor.
+
+ If the \a position is 0 (the default) the search begins from the beginning
+ of the document; otherwise it begins at the specified position.
+*/
+QTextCursor QTextDocument::find(const QString &subString, int from, FindFlags options) const
+{
+ QRegExp expr(subString);
+ expr.setPatternSyntax(QRegExp::FixedString);
+ expr.setCaseSensitivity((options & QTextDocument::FindCaseSensitively) ? Qt::CaseSensitive : Qt::CaseInsensitive);
+
+ return find(expr, from, options);
+}
+
+/*!
+ \fn QTextCursor QTextDocument::find(const QString &subString, const QTextCursor &cursor, FindFlags options) const
+
+ Finds the next occurrence of the string, \a subString, in the document.
+ The search starts at the position of the given \a cursor, and proceeds
+ forwards through the document unless specified otherwise in the search
+ options. The \a options control the type of search performed.
+
+ Returns a cursor with the match selected if \a subString was found; otherwise
+ returns a null cursor.
+
+ If the given \a cursor has a selection, the search begins after the
+ selection; otherwise it begins at the cursor's position.
+
+ By default the search is case-sensitive, and can match text anywhere in the
+ document.
+*/
+QTextCursor QTextDocument::find(const QString &subString, const QTextCursor &from, FindFlags options) const
+{
+ int pos = 0;
+ if (!from.isNull()) {
+ if (options & QTextDocument::FindBackward)
+ pos = from.selectionStart();
+ else
+ pos = from.selectionEnd();
+ }
+ QRegExp expr(subString);
+ expr.setPatternSyntax(QRegExp::FixedString);
+ expr.setCaseSensitivity((options & QTextDocument::FindCaseSensitively) ? Qt::CaseSensitive : Qt::CaseInsensitive);
+
+ return find(expr, pos, options);
+}
+
+
+static bool findInBlock(const QTextBlock &block, const QRegExp &expression, int offset,
+ QTextDocument::FindFlags options, QTextCursor &cursor)
+{
+ const QRegExp expr(expression);
+ QString text = block.text();
+ text.replace(QChar::Nbsp, QLatin1Char(' '));
+
+ int idx = -1;
+ while (offset >=0 && offset <= text.length()) {
+ idx = (options & QTextDocument::FindBackward) ?
+ expr.lastIndexIn(text, offset) : expr.indexIn(text, offset);
+ if (idx == -1)
+ return false;
+
+ if (options & QTextDocument::FindWholeWords) {
+ const int start = idx;
+ const int end = start + expr.matchedLength();
+ if ((start != 0 && text.at(start - 1).isLetterOrNumber())
+ || (end != text.length() && text.at(end).isLetterOrNumber())) {
+ //if this is not a whole word, continue the search in the string
+ offset = (options & QTextDocument::FindBackward) ? idx-1 : end+1;
+ idx = -1;
+ continue;
+ }
+ }
+ //we have a hit, return the cursor for that.
+ break;
+ }
+ if (idx == -1)
+ return false;
+ cursor = QTextCursor(block.docHandle(), block.position() + idx);
+ cursor.setPosition(cursor.position() + expr.matchedLength(), QTextCursor::KeepAnchor);
+ return true;
+}
+
+/*!
+ \fn QTextCursor QTextDocument::find(const QRegExp & expr, int position, FindFlags options) const
+
+ \overload
+
+ Finds the next occurrence, matching the regular expression, \a expr, in the document.
+ The search starts at the given \a position, and proceeds forwards
+ through the document unless specified otherwise in the search options.
+ The \a options control the type of search performed. The FindCaseSensitively
+ option is ignored for this overload, use QRegExp::caseSensitivity instead.
+
+ Returns a cursor with the match selected if a match was found; otherwise
+ returns a null cursor.
+
+ If the \a position is 0 (the default) the search begins from the beginning
+ of the document; otherwise it begins at the specified position.
+*/
+QTextCursor QTextDocument::find(const QRegExp & expr, int from, FindFlags options) const
+{
+ Q_D(const QTextDocument);
+
+ if (expr.isEmpty())
+ return QTextCursor();
+
+ int pos = from;
+ //the cursor is positioned between characters, so for a backward search
+ //do not include the character given in the position.
+ if (options & FindBackward) {
+ --pos ;
+ if(pos < 0)
+ return QTextCursor();
+ }
+
+ QTextCursor cursor;
+ QTextBlock block = d->blocksFind(pos);
+
+ if (!(options & FindBackward)) {
+ int blockOffset = qMax(0, pos - block.position());
+ while (block.isValid()) {
+ if (findInBlock(block, expr, blockOffset, options, cursor))
+ return cursor;
+ blockOffset = 0;
+ block = block.next();
+ }
+ } else {
+ int blockOffset = pos - block.position();
+ while (block.isValid()) {
+ if (findInBlock(block, expr, blockOffset, options, cursor))
+ return cursor;
+ block = block.previous();
+ blockOffset = block.length() - 1;
+ }
+ }
+
+ return QTextCursor();
+}
+
+/*!
+ \fn QTextCursor QTextDocument::find(const QRegExp &expr, const QTextCursor &cursor, FindFlags options) const
+
+ Finds the next occurrence, matching the regular expression, \a expr, in the document.
+ The search starts at the position of the given \a cursor, and proceeds
+ forwards through the document unless specified otherwise in the search
+ options. The \a options control the type of search performed. The FindCaseSensitively
+ option is ignored for this overload, use QRegExp::caseSensitivity instead.
+
+ Returns a cursor with the match selected if a match was found; otherwise
+ returns a null cursor.
+
+ If the given \a cursor has a selection, the search begins after the
+ selection; otherwise it begins at the cursor's position.
+
+ By default the search is case-sensitive, and can match text anywhere in the
+ document.
+*/
+QTextCursor QTextDocument::find(const QRegExp &expr, const QTextCursor &from, FindFlags options) const
+{
+ int pos = 0;
+ if (!from.isNull()) {
+ if (options & QTextDocument::FindBackward)
+ pos = from.selectionStart();
+ else
+ pos = from.selectionEnd();
+ }
+ return find(expr, pos, options);
+}
+
+
+/*!
+ \fn QTextObject *QTextDocument::createObject(const QTextFormat &format)
+
+ Creates and returns a new document object (a QTextObject), based
+ on the given \a format.
+
+ QTextObjects will always get created through this method, so you
+ must reimplement it if you use custom text objects inside your document.
+*/
+QTextObject *QTextDocument::createObject(const QTextFormat &f)
+{
+ QTextObject *obj = 0;
+ if (f.isListFormat())
+ obj = new QTextList(this);
+ else if (f.isTableFormat())
+ obj = new QTextTable(this);
+ else if (f.isFrameFormat())
+ obj = new QTextFrame(this);
+
+ return obj;
+}
+
+/*!
+ \internal
+
+ Returns the frame that contains the text cursor position \a pos.
+*/
+QTextFrame *QTextDocument::frameAt(int pos) const
+{
+ Q_D(const QTextDocument);
+ return d->frameAt(pos);
+}
+
+/*!
+ Returns the document's root frame.
+*/
+QTextFrame *QTextDocument::rootFrame() const
+{
+ Q_D(const QTextDocument);
+ return d->rootFrame();
+}
+
+/*!
+ Returns the text object associated with the given \a objectIndex.
+*/
+QTextObject *QTextDocument::object(int objectIndex) const
+{
+ Q_D(const QTextDocument);
+ return d->objectForIndex(objectIndex);
+}
+
+/*!
+ Returns the text object associated with the format \a f.
+*/
+QTextObject *QTextDocument::objectForFormat(const QTextFormat &f) const
+{
+ Q_D(const QTextDocument);
+ return d->objectForFormat(f);
+}
+
+
+/*!
+ Returns the text block that contains the \a{pos}-th character.
+*/
+QTextBlock QTextDocument::findBlock(int pos) const
+{
+ Q_D(const QTextDocument);
+ return QTextBlock(docHandle(), d->blockMap().findNode(pos));
+}
+
+/*!
+ \since 4.4
+ Returns the text block with the specified \a blockNumber.
+
+ \sa QTextBlock::blockNumber()
+*/
+QTextBlock QTextDocument::findBlockByNumber(int blockNumber) const
+{
+ Q_D(const QTextDocument);
+ return QTextBlock(docHandle(), d->blockMap().findNode(blockNumber, 1));
+}
+
+/*!
+ \since 4.5
+ Returns the text block that contains the specified \a lineNumber.
+
+ \sa QTextBlock::firstLineNumber()
+*/
+QTextBlock QTextDocument::findBlockByLineNumber(int lineNumber) const
+{
+ Q_D(const QTextDocument);
+ return QTextBlock(docHandle(), d->blockMap().findNode(lineNumber, 2));
+}
+
+/*!
+ Returns the document's first text block.
+
+ \sa firstBlock()
+*/
+QTextBlock QTextDocument::begin() const
+{
+ Q_D(const QTextDocument);
+ return QTextBlock(docHandle(), d->blockMap().begin().n);
+}
+
+/*!
+ This function returns a block to test for the end of the document
+ while iterating over it.
+
+ \snippet doc/src/snippets/textdocumentendsnippet.cpp 0
+
+ The block returned is invalid and represents the block after the
+ last block in the document. You can use lastBlock() to retrieve the
+ last valid block of the document.
+
+ \sa lastBlock()
+*/
+QTextBlock QTextDocument::end() const
+{
+ return QTextBlock(docHandle(), 0);
+}
+
+/*!
+ \since 4.4
+ Returns the document's first text block.
+*/
+QTextBlock QTextDocument::firstBlock() const
+{
+ Q_D(const QTextDocument);
+ return QTextBlock(docHandle(), d->blockMap().begin().n);
+}
+
+/*!
+ \since 4.4
+ Returns the document's last (valid) text block.
+*/
+QTextBlock QTextDocument::lastBlock() const
+{
+ Q_D(const QTextDocument);
+ return QTextBlock(docHandle(), d->blockMap().last().n);
+}
+
+/*!
+ \property QTextDocument::pageSize
+ \brief the page size that should be used for laying out the document
+
+ By default, for a newly-created, empty document, this property contains
+ an undefined size.
+
+ \sa modificationChanged()
+*/
+
+void QTextDocument::setPageSize(const QSizeF &size)
+{
+ Q_D(QTextDocument);
+ d->pageSize = size;
+ if (d->lout)
+ d->lout->documentChanged(0, 0, d->length());
+}
+
+QSizeF QTextDocument::pageSize() const
+{
+ Q_D(const QTextDocument);
+ return d->pageSize;
+}
+
+/*!
+ returns the number of pages in this document.
+*/
+int QTextDocument::pageCount() const
+{
+ return documentLayout()->pageCount();
+}
+
+/*!
+ Sets the default \a font to use in the document layout.
+*/
+void QTextDocument::setDefaultFont(const QFont &font)
+{
+ Q_D(QTextDocument);
+ d->setDefaultFont(font);
+ if (d->lout)
+ d->lout->documentChanged(0, 0, d->length());
+}
+
+/*!
+ Returns the default font to be used in the document layout.
+*/
+QFont QTextDocument::defaultFont() const
+{
+ Q_D(const QTextDocument);
+ return d->defaultFont();
+}
+
+/*!
+ \fn QTextDocument::modificationChanged(bool changed)
+
+ This signal is emitted whenever the content of the document
+ changes in a way that affects the modification state. If \a
+ changed is true, the document has been modified; otherwise it is
+ false.
+
+ For example, calling setModified(false) on a document and then
+ inserting text causes the signal to get emitted. If you undo that
+ operation, causing the document to return to its original
+ unmodified state, the signal will get emitted again.
+*/
+
+/*!
+ \property QTextDocument::modified
+ \brief whether the document has been modified by the user
+
+ By default, this property is false.
+
+ \sa modificationChanged()
+*/
+
+bool QTextDocument::isModified() const
+{
+ return docHandle()->isModified();
+}
+
+void QTextDocument::setModified(bool m)
+{
+ docHandle()->setModified(m);
+}
+
+#ifndef QT_NO_PRINTER
+static void printPage(int index, QPainter *painter, const QTextDocument *doc, const QRectF &body, const QPointF &pageNumberPos)
+{
+ painter->save();
+ painter->translate(body.left(), body.top() - (index - 1) * body.height());
+ QRectF view(0, (index - 1) * body.height(), body.width(), body.height());
+
+ QAbstractTextDocumentLayout *layout = doc->documentLayout();
+ QAbstractTextDocumentLayout::PaintContext ctx;
+
+ painter->setClipRect(view);
+ ctx.clip = view;
+
+ // don't use the system palette text as default text color, on HP/UX
+ // for example that's white, and white text on white paper doesn't
+ // look that nice
+ ctx.palette.setColor(QPalette::Text, Qt::black);
+
+ layout->draw(painter, ctx);
+
+ if (!pageNumberPos.isNull()) {
+ painter->setClipping(false);
+ painter->setFont(QFont(doc->defaultFont()));
+ const QString pageString = QString::number(index);
+
+ painter->drawText(qRound(pageNumberPos.x() - painter->fontMetrics().width(pageString)),
+ qRound(pageNumberPos.y() + view.top()),
+ pageString);
+ }
+
+ painter->restore();
+}
+
+extern int qt_defaultDpi();
+
+/*!
+ Prints the document to the given \a printer. The QPrinter must be
+ set up before being used with this function.
+
+ This is only a convenience method to print the whole document to the printer.
+
+ If the document is already paginated through a specified height in the pageSize()
+ property it is printed as-is.
+
+ If the document is not paginated, like for example a document used in a QTextEdit,
+ then a temporary copy of the document is created and the copy is broken into
+ multiple pages according to the size of the QPrinter's paperRect(). By default
+ a 2 cm margin is set around the document contents. In addition the current page
+ number is printed at the bottom of each page.
+
+ Note that QPrinter::Selection is not supported as print range with this function since
+ the selection is a property of QTextCursor. If you have a QTextEdit associated with
+ your QTextDocument then you can use QTextEdit's print() function because QTextEdit has
+ access to the user's selection.
+
+ \sa QTextEdit::print()
+*/
+
+void QTextDocument::print(QPrinter *printer) const
+{
+ Q_D(const QTextDocument);
+
+ if (!printer || !printer->isValid())
+ return;
+
+ if (!d->title.isEmpty())
+ printer->setDocName(d->title);
+
+ bool documentPaginated = d->pageSize.isValid() && !d->pageSize.isNull()
+ && d->pageSize.height() != INT_MAX;
+
+ if (!documentPaginated && !printer->fullPage() && !printer->d_func()->hasCustomPageMargins)
+ printer->setPageMargins(23.53, 23.53, 23.53, 23.53, QPrinter::Millimeter);
+
+ QPainter p(printer);
+
+ // Check that there is a valid device to print to.
+ if (!p.isActive())
+ return;
+
+ const QTextDocument *doc = this;
+ QTextDocument *clonedDoc = 0;
+ (void)doc->documentLayout(); // make sure that there is a layout
+
+ QRectF body = QRectF(QPointF(0, 0), d->pageSize);
+ QPointF pageNumberPos;
+
+ if (documentPaginated) {
+ qreal sourceDpiX = qt_defaultDpi();
+ qreal sourceDpiY = sourceDpiX;
+
+ QPaintDevice *dev = doc->documentLayout()->paintDevice();
+ if (dev) {
+ sourceDpiX = dev->logicalDpiX();
+ sourceDpiY = dev->logicalDpiY();
+ }
+
+ const qreal dpiScaleX = qreal(printer->logicalDpiX()) / sourceDpiX;
+ const qreal dpiScaleY = qreal(printer->logicalDpiY()) / sourceDpiY;
+
+ // scale to dpi
+ p.scale(dpiScaleX, dpiScaleY);
+
+ QSizeF scaledPageSize = d->pageSize;
+ scaledPageSize.rwidth() *= dpiScaleX;
+ scaledPageSize.rheight() *= dpiScaleY;
+
+ const QSizeF printerPageSize(printer->pageRect().size());
+
+ // scale to page
+ p.scale(printerPageSize.width() / scaledPageSize.width(),
+ printerPageSize.height() / scaledPageSize.height());
+ } else {
+ doc = clone(const_cast<QTextDocument *>(this));
+ clonedDoc = const_cast<QTextDocument *>(doc);
+
+ for (QTextBlock srcBlock = firstBlock(), dstBlock = clonedDoc->firstBlock();
+ srcBlock.isValid() && dstBlock.isValid();
+ srcBlock = srcBlock.next(), dstBlock = dstBlock.next()) {
+ dstBlock.layout()->setAdditionalFormats(srcBlock.layout()->additionalFormats());
+ }
+
+ QAbstractTextDocumentLayout *layout = doc->documentLayout();
+ layout->setPaintDevice(p.device());
+
+ int dpiy = p.device()->logicalDpiY();
+ int margin = 0;
+ if (printer->fullPage() && !printer->d_func()->hasCustomPageMargins) {
+ // for compatibility
+ margin = (int) ((2/2.54)*dpiy); // 2 cm margins
+ QTextFrameFormat fmt = doc->rootFrame()->frameFormat();
+ fmt.setMargin(margin);
+ doc->rootFrame()->setFrameFormat(fmt);
+ }
+
+ QRectF pageRect(printer->pageRect());
+ body = QRectF(0, 0, pageRect.width(), pageRect.height());
+ pageNumberPos = QPointF(body.width() - margin,
+ body.height() - margin
+ + QFontMetrics(doc->defaultFont(), p.device()).ascent()
+ + 5 * dpiy / 72.0);
+ clonedDoc->setPageSize(body.size());
+ }
+
+ int docCopies;
+ int pageCopies;
+ if (printer->collateCopies() == true){
+ docCopies = 1;
+ pageCopies = printer->numCopies();
+ } else {
+ docCopies = printer->numCopies();
+ pageCopies = 1;
+ }
+
+ int fromPage = printer->fromPage();
+ int toPage = printer->toPage();
+ bool ascending = true;
+
+ if (fromPage == 0 && toPage == 0) {
+ fromPage = 1;
+ toPage = doc->pageCount();
+ }
+ // paranoia check
+ fromPage = qMax(1, fromPage);
+ toPage = qMin(doc->pageCount(), toPage);
+
+ if (printer->pageOrder() == QPrinter::LastPageFirst) {
+ int tmp = fromPage;
+ fromPage = toPage;
+ toPage = tmp;
+ ascending = false;
+ }
+
+ for (int i = 0; i < docCopies; ++i) {
+
+ int page = fromPage;
+ while (true) {
+ for (int j = 0; j < pageCopies; ++j) {
+ if (printer->printerState() == QPrinter::Aborted
+ || printer->printerState() == QPrinter::Error)
+ goto UserCanceled;
+ printPage(page, &p, doc, body, pageNumberPos);
+ if (j < pageCopies - 1)
+ printer->newPage();
+ }
+
+ if (page == toPage)
+ break;
+
+ if (ascending)
+ ++page;
+ else
+ --page;
+
+ printer->newPage();
+ }
+
+ if ( i < docCopies - 1)
+ printer->newPage();
+ }
+
+UserCanceled:
+ delete clonedDoc;
+}
+#endif
+
+/*!
+ \enum QTextDocument::ResourceType
+
+ This enum describes the types of resources that can be loaded by
+ QTextDocument's loadResource() function.
+
+ \value HtmlResource The resource contains HTML.
+ \value ImageResource The resource contains image data.
+ Currently supported data types are QVariant::Pixmap and
+ QVariant::Image. If the corresponding variant is of type
+ QVariant::ByteArray then Qt attempts to load the image using
+ QImage::loadFromData. QVariant::Icon is currently not supported.
+ The icon needs to be converted to one of the supported types first,
+ for example using QIcon::pixmap.
+ \value StyleSheetResource The resource contains CSS.
+ \value UserResource The first available value for user defined
+ resource types.
+
+ \sa loadResource()
+*/
+
+/*!
+ Returns data of the specified \a type from the resource with the
+ given \a name.
+
+ This function is called by the rich text engine to request data that isn't
+ directly stored by QTextDocument, but still associated with it. For example,
+ images are referenced indirectly by the name attribute of a QTextImageFormat
+ object.
+
+ Resources are cached internally in the document. If a resource can
+ not be found in the cache, loadResource is called to try to load
+ the resource. loadResource should then use addResource to add the
+ resource to the cache.
+
+ \sa QTextDocument::ResourceType
+*/
+QVariant QTextDocument::resource(int type, const QUrl &name) const
+{
+ Q_D(const QTextDocument);
+ QVariant r = d->resources.value(name);
+ if (!r.isValid()) {
+ r = d->cachedResources.value(name);
+ if (!r.isValid())
+ r = const_cast<QTextDocument *>(this)->loadResource(type, name);
+ }
+ return r;
+}
+
+/*!
+ Adds the resource \a resource to the resource cache, using \a
+ type and \a name as identifiers. \a type should be a value from
+ QTextDocument::ResourceType.
+
+ For example, you can add an image as a resource in order to reference it
+ from within the document:
+
+ \snippet snippets/textdocument-resources/main.cpp Adding a resource
+
+ The image can be inserted into the document using the QTextCursor API:
+
+ \snippet snippets/textdocument-resources/main.cpp Inserting an image with a cursor
+
+ Alternatively, you can insert images using the HTML \c img tag:
+
+ \snippet snippets/textdocument-resources/main.cpp Inserting an image using HTML
+*/
+void QTextDocument::addResource(int type, const QUrl &name, const QVariant &resource)
+{
+ Q_UNUSED(type);
+ Q_D(QTextDocument);
+ d->resources.insert(name, resource);
+}
+
+/*!
+ Loads data of the specified \a type from the resource with the
+ given \a name.
+
+ This function is called by the rich text engine to request data that isn't
+ directly stored by QTextDocument, but still associated with it. For example,
+ images are referenced indirectly by the name attribute of a QTextImageFormat
+ object.
+
+ When called by Qt, \a type is one of the values of
+ QTextDocument::ResourceType.
+
+ If the QTextDocument is a child object of a QTextEdit, QTextBrowser,
+ or a QTextDocument itself then the default implementation tries
+ to retrieve the data from the parent.
+*/
+QVariant QTextDocument::loadResource(int type, const QUrl &name)
+{
+ Q_D(QTextDocument);
+ QVariant r;
+
+ QTextDocument *doc = qobject_cast<QTextDocument *>(parent());
+ if (doc) {
+ r = doc->loadResource(type, name);
+ }
+#ifndef QT_NO_TEXTEDIT
+ else if (QTextEdit *edit = qobject_cast<QTextEdit *>(parent())) {
+ QUrl resolvedName = edit->d_func()->resolveUrl(name);
+ r = edit->loadResource(type, resolvedName);
+ }
+#endif
+#ifndef QT_NO_TEXTCONTROL
+ else if (QTextControl *control = qobject_cast<QTextControl *>(parent())) {
+ r = control->loadResource(type, name);
+ }
+#endif
+
+ // if resource was not loaded try to load it here
+ if (!doc && r.isNull() && name.isRelative()) {
+ QUrl currentURL = d->url;
+ QUrl resourceUrl = name;
+
+ // For the second case QUrl can merge "#someanchor" with "foo.html"
+ // correctly to "foo.html#someanchor"
+ if (!(currentURL.isRelative()
+ || (currentURL.scheme() == QLatin1String("file")
+ && !QFileInfo(currentURL.toLocalFile()).isAbsolute()))
+ || (name.hasFragment() && name.path().isEmpty())) {
+ resourceUrl = currentURL.resolved(name);
+ } else {
+ // this is our last resort when current url and new url are both relative
+ // we try to resolve against the current working directory in the local
+ // file system.
+ QFileInfo fi(currentURL.toLocalFile());
+ if (fi.exists()) {
+ resourceUrl =
+ QUrl::fromLocalFile(fi.absolutePath() + QDir::separator()).resolved(name);
+ }
+ }
+
+ QString s = resourceUrl.toLocalFile();
+ QFile f(s);
+ if (!s.isEmpty() && f.open(QFile::ReadOnly)) {
+ r = f.readAll();
+ f.close();
+ }
+ }
+
+ if (!r.isNull()) {
+ if (type == ImageResource && r.type() == QVariant::ByteArray) {
+ if (qApp->thread() != QThread::currentThread()) {
+ // must use images in non-GUI threads
+ QImage image;
+ image.loadFromData(r.toByteArray());
+ if (!image.isNull())
+ r = image;
+ } else {
+ QPixmap pm;
+ pm.loadFromData(r.toByteArray());
+ if (!pm.isNull())
+ r = pm;
+ }
+ }
+ d->cachedResources.insert(name, r);
+ }
+ return r;
+}
+
+static QTextFormat formatDifference(const QTextFormat &from, const QTextFormat &to)
+{
+ QTextFormat diff = to;
+
+ const QMap<int, QVariant> props = to.properties();
+ for (QMap<int, QVariant>::ConstIterator it = props.begin(), end = props.end();
+ it != end; ++it)
+ if (it.value() == from.property(it.key()))
+ diff.clearProperty(it.key());
+
+ return diff;
+}
+
+QTextHtmlExporter::QTextHtmlExporter(const QTextDocument *_doc)
+ : doc(_doc), fragmentMarkers(false)
+{
+ const QFont defaultFont = doc->defaultFont();
+ defaultCharFormat.setFont(defaultFont);
+ // don't export those for the default font since we cannot turn them off with CSS
+ defaultCharFormat.clearProperty(QTextFormat::FontUnderline);
+ defaultCharFormat.clearProperty(QTextFormat::FontOverline);
+ defaultCharFormat.clearProperty(QTextFormat::FontStrikeOut);
+ defaultCharFormat.clearProperty(QTextFormat::TextUnderlineStyle);
+}
+
+/*!
+ Returns the document in HTML format. The conversion may not be
+ perfect, especially for complex documents, due to the limitations
+ of HTML.
+*/
+QString QTextHtmlExporter::toHtml(const QByteArray &encoding, ExportMode mode)
+{
+ html = QLatin1String("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0//EN\" "
+ "\"http://www.w3.org/TR/REC-html40/strict.dtd\">\n"
+ "<html><head><meta name=\"qrichtext\" content=\"1\" />");
+ html.reserve(doc->docHandle()->length());
+
+ fragmentMarkers = (mode == ExportFragment);
+
+ if (!encoding.isEmpty())
+ html += QString::fromLatin1("<meta http-equiv=\"Content-Type\" content=\"text/html; charset=%1\" />").arg(QString::fromAscii(encoding));
+
+ QString title = doc->metaInformation(QTextDocument::DocumentTitle);
+ if (!title.isEmpty())
+ html += QString::fromLatin1("<title>") + title + QString::fromLatin1("</title>");
+ html += QLatin1String("<style type=\"text/css\">\n");
+ html += QLatin1String("p, li { white-space: pre-wrap; }\n");
+ html += QLatin1String("</style>");
+ html += QLatin1String("</head><body");
+
+ if (mode == ExportEntireDocument) {
+ html += QLatin1String(" style=\"");
+
+ emitFontFamily(defaultCharFormat.fontFamily());
+
+ if (defaultCharFormat.hasProperty(QTextFormat::FontPointSize)) {
+ html += QLatin1String(" font-size:");
+ html += QString::number(defaultCharFormat.fontPointSize());
+ html += QLatin1String("pt;");
+ }
+
+ html += QLatin1String(" font-weight:");
+ html += QString::number(defaultCharFormat.fontWeight() * 8);
+ html += QLatin1Char(';');
+
+ html += QLatin1String(" font-style:");
+ html += (defaultCharFormat.fontItalic() ? QLatin1String("italic") : QLatin1String("normal"));
+ html += QLatin1Char(';');
+
+ // do not set text-decoration on the default font since those values are /always/ propagated
+ // and cannot be turned off with CSS
+
+ html += QLatin1Char('\"');
+
+ const QTextFrameFormat fmt = doc->rootFrame()->frameFormat();
+ emitBackgroundAttribute(fmt);
+
+ } else {
+ defaultCharFormat = QTextCharFormat();
+ }
+ html += QLatin1Char('>');
+
+ QTextFrameFormat rootFmt = doc->rootFrame()->frameFormat();
+ rootFmt.clearProperty(QTextFormat::BackgroundBrush);
+
+ QTextFrameFormat defaultFmt;
+ defaultFmt.setMargin(doc->documentMargin());
+
+ if (rootFmt == defaultFmt)
+ emitFrame(doc->rootFrame()->begin());
+ else
+ emitTextFrame(doc->rootFrame());
+
+ html += QLatin1String("</body></html>");
+ return html;
+}
+
+void QTextHtmlExporter::emitAttribute(const char *attribute, const QString &value)
+{
+ html += QLatin1Char(' ');
+ html += QLatin1String(attribute);
+ html += QLatin1String("=\"");
+ html += value;
+ html += QLatin1Char('"');
+}
+
+bool QTextHtmlExporter::emitCharFormatStyle(const QTextCharFormat &format)
+{
+ bool attributesEmitted = false;
+
+ {
+ const QString family = format.fontFamily();
+ if (!family.isEmpty() && family != defaultCharFormat.fontFamily()) {
+ emitFontFamily(family);
+ attributesEmitted = true;
+ }
+ }
+
+ if (format.hasProperty(QTextFormat::FontPointSize)
+ && format.fontPointSize() != defaultCharFormat.fontPointSize()) {
+ html += QLatin1String(" font-size:");
+ html += QString::number(format.fontPointSize());
+ html += QLatin1String("pt;");
+ attributesEmitted = true;
+ } else if (format.hasProperty(QTextFormat::FontSizeAdjustment)) {
+ static const char * const sizeNames[] = {
+ "small", "medium", "large", "x-large", "xx-large"
+ };
+ const char *name = 0;
+ const int idx = format.intProperty(QTextFormat::FontSizeAdjustment) + 1;
+ if (idx >= 0 && idx <= 4) {
+ name = sizeNames[idx];
+ }
+ if (name) {
+ html += QLatin1String(" font-size:");
+ html += QLatin1String(name);
+ html += QLatin1Char(';');
+ attributesEmitted = true;
+ }
+ }
+
+ if (format.hasProperty(QTextFormat::FontWeight)
+ && format.fontWeight() != defaultCharFormat.fontWeight()) {
+ html += QLatin1String(" font-weight:");
+ html += QString::number(format.fontWeight() * 8);
+ html += QLatin1Char(';');
+ attributesEmitted = true;
+ }
+
+ if (format.hasProperty(QTextFormat::FontItalic)
+ && format.fontItalic() != defaultCharFormat.fontItalic()) {
+ html += QLatin1String(" font-style:");
+ html += (format.fontItalic() ? QLatin1String("italic") : QLatin1String("normal"));
+ html += QLatin1Char(';');
+ attributesEmitted = true;
+ }
+
+ QLatin1String decorationTag(" text-decoration:");
+ html += decorationTag;
+ bool hasDecoration = false;
+ bool atLeastOneDecorationSet = false;
+
+ if ((format.hasProperty(QTextFormat::FontUnderline) || format.hasProperty(QTextFormat::TextUnderlineStyle))
+ && format.fontUnderline() != defaultCharFormat.fontUnderline()) {
+ hasDecoration = true;
+ if (format.fontUnderline()) {
+ html += QLatin1String(" underline");
+ atLeastOneDecorationSet = true;
+ }
+ }
+
+ if (format.hasProperty(QTextFormat::FontOverline)
+ && format.fontOverline() != defaultCharFormat.fontOverline()) {
+ hasDecoration = true;
+ if (format.fontOverline()) {
+ html += QLatin1String(" overline");
+ atLeastOneDecorationSet = true;
+ }
+ }
+
+ if (format.hasProperty(QTextFormat::FontStrikeOut)
+ && format.fontStrikeOut() != defaultCharFormat.fontStrikeOut()) {
+ hasDecoration = true;
+ if (format.fontStrikeOut()) {
+ html += QLatin1String(" line-through");
+ atLeastOneDecorationSet = true;
+ }
+ }
+
+ if (hasDecoration) {
+ if (!atLeastOneDecorationSet)
+ html += QLatin1String("none");
+ html += QLatin1Char(';');
+ attributesEmitted = true;
+ } else {
+ html.chop(qstrlen(decorationTag.latin1()));
+ }
+
+ if (format.foreground() != defaultCharFormat.foreground()
+ && format.foreground().style() != Qt::NoBrush) {
+ html += QLatin1String(" color:");
+ html += format.foreground().color().name();
+ html += QLatin1Char(';');
+ attributesEmitted = true;
+ }
+
+ if (format.background() != defaultCharFormat.background()
+ && format.background().style() == Qt::SolidPattern) {
+ html += QLatin1String(" background-color:");
+ html += format.background().color().name();
+ html += QLatin1Char(';');
+ attributesEmitted = true;
+ }
+
+ if (format.verticalAlignment() != defaultCharFormat.verticalAlignment()
+ && format.verticalAlignment() != QTextCharFormat::AlignNormal)
+ {
+ html += QLatin1String(" vertical-align:");
+
+ QTextCharFormat::VerticalAlignment valign = format.verticalAlignment();
+ if (valign == QTextCharFormat::AlignSubScript)
+ html += QLatin1String("sub");
+ else if (valign == QTextCharFormat::AlignSuperScript)
+ html += QLatin1String("super");
+ else if (valign == QTextCharFormat::AlignMiddle)
+ html += QLatin1String("middle");
+ else if (valign == QTextCharFormat::AlignTop)
+ html += QLatin1String("top");
+ else if (valign == QTextCharFormat::AlignBottom)
+ html += QLatin1String("bottom");
+
+ html += QLatin1Char(';');
+ attributesEmitted = true;
+ }
+
+ if (format.fontCapitalization() != QFont::MixedCase) {
+ const QFont::Capitalization caps = format.fontCapitalization();
+ if (caps == QFont::AllUppercase)
+ html += QLatin1String(" text-transform:uppercase;");
+ else if (caps == QFont::AllLowercase)
+ html += QLatin1String(" text-transform:lowercase;");
+ else if (caps == QFont::SmallCaps)
+ html += QLatin1String(" font-variant:small-caps;");
+ attributesEmitted = true;
+ }
+
+ if (format.fontWordSpacing() != 0.0) {
+ html += QLatin1String(" word-spacing:");
+ html += QString::number(format.fontWordSpacing());
+ html += QLatin1String("px;");
+ attributesEmitted = true;
+ }
+
+ return attributesEmitted;
+}
+
+void QTextHtmlExporter::emitTextLength(const char *attribute, const QTextLength &length)
+{
+ if (length.type() == QTextLength::VariableLength) // default
+ return;
+
+ html += QLatin1Char(' ');
+ html += QLatin1String(attribute);
+ html += QLatin1String("=\"");
+ html += QString::number(length.rawValue());
+
+ if (length.type() == QTextLength::PercentageLength)
+ html += QLatin1String("%\"");
+ else
+ html += QLatin1String("\"");
+}
+
+void QTextHtmlExporter::emitAlignment(Qt::Alignment align)
+{
+ if (align & Qt::AlignLeft)
+ return;
+ else if (align & Qt::AlignRight)
+ html += QLatin1String(" align=\"right\"");
+ else if (align & Qt::AlignHCenter)
+ html += QLatin1String(" align=\"center\"");
+ else if (align & Qt::AlignJustify)
+ html += QLatin1String(" align=\"justify\"");
+}
+
+void QTextHtmlExporter::emitFloatStyle(QTextFrameFormat::Position pos, StyleMode mode)
+{
+ if (pos == QTextFrameFormat::InFlow)
+ return;
+
+ if (mode == EmitStyleTag)
+ html += QLatin1String(" style=\"float:");
+ else
+ html += QLatin1String(" float:");
+
+ if (pos == QTextFrameFormat::FloatLeft)
+ html += QLatin1String(" left;");
+ else if (pos == QTextFrameFormat::FloatRight)
+ html += QLatin1String(" right;");
+ else
+ Q_ASSERT_X(0, "QTextHtmlExporter::emitFloatStyle()", "pos should be a valid enum type");
+
+ if (mode == EmitStyleTag)
+ html += QLatin1Char('\"');
+}
+
+void QTextHtmlExporter::emitBorderStyle(QTextFrameFormat::BorderStyle style)
+{
+ Q_ASSERT(style <= QTextFrameFormat::BorderStyle_Outset);
+
+ html += QLatin1String(" border-style:");
+
+ switch (style) {
+ case QTextFrameFormat::BorderStyle_None:
+ html += QLatin1String("none");
+ break;
+ case QTextFrameFormat::BorderStyle_Dotted:
+ html += QLatin1String("dotted");
+ break;
+ case QTextFrameFormat::BorderStyle_Dashed:
+ html += QLatin1String("dashed");
+ break;
+ case QTextFrameFormat::BorderStyle_Solid:
+ html += QLatin1String("solid");
+ break;
+ case QTextFrameFormat::BorderStyle_Double:
+ html += QLatin1String("double");
+ break;
+ case QTextFrameFormat::BorderStyle_DotDash:
+ html += QLatin1String("dot-dash");
+ break;
+ case QTextFrameFormat::BorderStyle_DotDotDash:
+ html += QLatin1String("dot-dot-dash");
+ break;
+ case QTextFrameFormat::BorderStyle_Groove:
+ html += QLatin1String("groove");
+ break;
+ case QTextFrameFormat::BorderStyle_Ridge:
+ html += QLatin1String("ridge");
+ break;
+ case QTextFrameFormat::BorderStyle_Inset:
+ html += QLatin1String("inset");
+ break;
+ case QTextFrameFormat::BorderStyle_Outset:
+ html += QLatin1String("outset");
+ break;
+ default:
+ Q_ASSERT(false);
+ break;
+ };
+
+ html += QLatin1Char(';');
+}
+
+void QTextHtmlExporter::emitPageBreakPolicy(QTextFormat::PageBreakFlags policy)
+{
+ if (policy & QTextFormat::PageBreak_AlwaysBefore)
+ html += QLatin1String(" page-break-before:always;");
+
+ if (policy & QTextFormat::PageBreak_AlwaysAfter)
+ html += QLatin1String(" page-break-after:always;");
+}
+
+void QTextHtmlExporter::emitFontFamily(const QString &family)
+{
+ html += QLatin1String(" font-family:");
+
+ QLatin1Char quote('\'');
+ if (family.contains(quote))
+ quote = QLatin1Char('\"');
+
+ html += quote;
+ html += family;
+ html += quote;
+ html += QLatin1Char(';');
+}
+
+void QTextHtmlExporter::emitMargins(const QString &top, const QString &bottom, const QString &left, const QString &right)
+{
+ html += QLatin1String(" margin-top:");
+ html += top;
+ html += QLatin1String("px;");
+
+ html += QLatin1String(" margin-bottom:");
+ html += bottom;
+ html += QLatin1String("px;");
+
+ html += QLatin1String(" margin-left:");
+ html += left;
+ html += QLatin1String("px;");
+
+ html += QLatin1String(" margin-right:");
+ html += right;
+ html += QLatin1String("px;");
+}
+
+void QTextHtmlExporter::emitFragment(const QTextFragment &fragment)
+{
+ const QTextCharFormat format = fragment.charFormat();
+
+ bool closeAnchor = false;
+
+ if (format.isAnchor()) {
+ const QString name = format.anchorName();
+ if (!name.isEmpty()) {
+ html += QLatin1String("<a name=\"");
+ html += name;
+ html += QLatin1String("\"></a>");
+ }
+ const QString href = format.anchorHref();
+ if (!href.isEmpty()) {
+ html += QLatin1String("<a href=\"");
+ html += href;
+ html += QLatin1String("\">");
+ closeAnchor = true;
+ }
+ }
+
+ QString txt = fragment.text();
+ const bool isObject = txt.contains(QChar::ObjectReplacementCharacter);
+ const bool isImage = isObject && format.isImageFormat();
+
+ QLatin1String styleTag("<span style=\"");
+ html += styleTag;
+
+ bool attributesEmitted = false;
+ if (!isImage)
+ attributesEmitted = emitCharFormatStyle(format);
+ if (attributesEmitted)
+ html += QLatin1String("\">");
+ else
+ html.chop(qstrlen(styleTag.latin1()));
+
+ if (isObject) {
+ for (int i = 0; isImage && i < txt.length(); ++i) {
+ QTextImageFormat imgFmt = format.toImageFormat();
+
+ html += QLatin1String("<img");
+
+ if (imgFmt.hasProperty(QTextFormat::ImageName))
+ emitAttribute("src", imgFmt.name());
+
+ if (imgFmt.hasProperty(QTextFormat::ImageWidth))
+ emitAttribute("width", QString::number(imgFmt.width()));
+
+ if (imgFmt.hasProperty(QTextFormat::ImageHeight))
+ emitAttribute("height", QString::number(imgFmt.height()));
+
+ if (imgFmt.verticalAlignment() == QTextCharFormat::AlignMiddle)
+ html += QLatin1String(" style=\"vertical-align: middle;\"");
+ else if (imgFmt.verticalAlignment() == QTextCharFormat::AlignTop)
+ html += QLatin1String(" style=\"vertical-align: top;\"");
+
+ if (QTextFrame *imageFrame = qobject_cast<QTextFrame *>(doc->objectForFormat(imgFmt)))
+ emitFloatStyle(imageFrame->frameFormat().position());
+
+ html += QLatin1String(" />");
+ }
+ } else {
+ Q_ASSERT(!txt.contains(QChar::ObjectReplacementCharacter));
+
+ txt = Qt::escape(txt);
+
+ // split for [\n{LineSeparator}]
+ QString forcedLineBreakRegExp = QString::fromLatin1("[\\na]");
+ forcedLineBreakRegExp[3] = QChar::LineSeparator;
+
+ const QStringList lines = txt.split(QRegExp(forcedLineBreakRegExp));
+ for (int i = 0; i < lines.count(); ++i) {
+ if (i > 0)
+ html += QLatin1String("<br />"); // space on purpose for compatibility with Netscape, Lynx & Co.
+ html += lines.at(i);
+ }
+ }
+
+ if (attributesEmitted)
+ html += QLatin1String("</span>");
+
+ if (closeAnchor)
+ html += QLatin1String("</a>");
+}
+
+static bool isOrderedList(int style)
+{
+ return style == QTextListFormat::ListDecimal || style == QTextListFormat::ListLowerAlpha
+ || style == QTextListFormat::ListUpperAlpha;
+}
+
+void QTextHtmlExporter::emitBlockAttributes(const QTextBlock &block)
+{
+ QTextBlockFormat format = block.blockFormat();
+ emitAlignment(format.alignment());
+
+ Qt::LayoutDirection dir = format.layoutDirection();
+ if (dir == Qt::LeftToRight) {
+ // assume default to not bloat the html too much
+ // html += QLatin1String(" dir='ltr'");
+ } else {
+ html += QLatin1String(" dir='rtl'");
+ }
+
+ QLatin1String style(" style=\"");
+ html += style;
+
+ const bool emptyBlock = block.begin().atEnd();
+ if (emptyBlock) {
+ html += QLatin1String("-qt-paragraph-type:empty;");
+ }
+
+ emitMargins(QString::number(format.topMargin()),
+ QString::number(format.bottomMargin()),
+ QString::number(format.leftMargin()),
+ QString::number(format.rightMargin()));
+
+ html += QLatin1String(" -qt-block-indent:");
+ html += QString::number(format.indent());
+ html += QLatin1Char(';');
+
+ html += QLatin1String(" text-indent:");
+ html += QString::number(format.textIndent());
+ html += QLatin1String("px;");
+
+ if (block.userState() != -1) {
+ html += QLatin1String(" -qt-user-state:");
+ html += QString::number(block.userState());
+ html += QLatin1Char(';');
+ }
+
+ emitPageBreakPolicy(format.pageBreakPolicy());
+
+ QTextCharFormat diff;
+ if (emptyBlock) { // only print character properties when we don't expect them to be repeated by actual text in the parag
+ const QTextCharFormat blockCharFmt = block.charFormat();
+ diff = formatDifference(defaultCharFormat, blockCharFmt).toCharFormat();
+ }
+
+ diff.clearProperty(QTextFormat::BackgroundBrush);
+ if (format.hasProperty(QTextFormat::BackgroundBrush)) {
+ QBrush bg = format.background();
+ if (bg.style() != Qt::NoBrush)
+ diff.setProperty(QTextFormat::BackgroundBrush, format.property(QTextFormat::BackgroundBrush));
+ }
+
+ if (!diff.properties().isEmpty())
+ emitCharFormatStyle(diff);
+
+ html += QLatin1Char('"');
+
+}
+
+void QTextHtmlExporter::emitBlock(const QTextBlock &block)
+{
+ if (block.begin().atEnd()) {
+ // ### HACK, remove once QTextFrame::Iterator is fixed
+ int p = block.position();
+ if (p > 0)
+ --p;
+ QTextDocumentPrivate::FragmentIterator frag = doc->docHandle()->find(p);
+ QChar ch = doc->docHandle()->buffer().at(frag->stringPosition);
+ if (ch == QTextBeginningOfFrame
+ || ch == QTextEndOfFrame)
+ return;
+ }
+
+ html += QLatin1Char('\n');
+
+ // save and later restore, in case we 'change' the default format by
+ // emitting block char format information
+ QTextCharFormat oldDefaultCharFormat = defaultCharFormat;
+
+ QTextList *list = block.textList();
+ if (list) {
+ if (list->itemNumber(block) == 0) { // first item? emit <ul> or appropriate
+ const QTextListFormat format = list->format();
+ const int style = format.style();
+ switch (style) {
+ case QTextListFormat::ListDecimal: html += QLatin1String("<ol"); break;
+ case QTextListFormat::ListDisc: html += QLatin1String("<ul"); break;
+ case QTextListFormat::ListCircle: html += QLatin1String("<ul type=\"circle\""); break;
+ case QTextListFormat::ListSquare: html += QLatin1String("<ul type=\"square\""); break;
+ case QTextListFormat::ListLowerAlpha: html += QLatin1String("<ol type=\"a\""); break;
+ case QTextListFormat::ListUpperAlpha: html += QLatin1String("<ol type=\"A\""); break;
+ default: html += QLatin1String("<ul"); // ### should not happen
+ }
+
+ if (format.hasProperty(QTextFormat::ListIndent)) {
+ html += QLatin1String(" style=\"-qt-list-indent: ");
+ html += QString::number(format.indent());
+ html += QLatin1String(";\"");
+ }
+
+ html += QLatin1Char('>');
+ }
+
+ html += QLatin1String("<li");
+
+ const QTextCharFormat blockFmt = formatDifference(defaultCharFormat, block.charFormat()).toCharFormat();
+ if (!blockFmt.properties().isEmpty()) {
+ html += QLatin1String(" style=\"");
+ emitCharFormatStyle(blockFmt);
+ html += QLatin1Char('\"');
+
+ defaultCharFormat.merge(block.charFormat());
+ }
+ }
+
+ const QTextBlockFormat blockFormat = block.blockFormat();
+ if (blockFormat.hasProperty(QTextFormat::BlockTrailingHorizontalRulerWidth)) {
+ html += QLatin1String("<hr");
+
+ QTextLength width = blockFormat.lengthProperty(QTextFormat::BlockTrailingHorizontalRulerWidth);
+ if (width.type() != QTextLength::VariableLength)
+ emitTextLength("width", width);
+ else
+ html += QLatin1Char(' ');
+
+ html += QLatin1String("/>");
+ return;
+ }
+
+ const bool pre = blockFormat.nonBreakableLines();
+ if (pre) {
+ if (list)
+ html += QLatin1Char('>');
+ html += QLatin1String("<pre");
+ } else if (!list) {
+ html += QLatin1String("<p");
+ }
+
+ emitBlockAttributes(block);
+
+ html += QLatin1Char('>');
+
+ QTextBlock::Iterator it = block.begin();
+ if (fragmentMarkers && !it.atEnd() && block == doc->begin())
+ html += QLatin1String("<!--StartFragment-->");
+
+ for (; !it.atEnd(); ++it)
+ emitFragment(it.fragment());
+
+ if (fragmentMarkers && block.position() + block.length() == doc->docHandle()->length())
+ html += QLatin1String("<!--EndFragment-->");
+
+ if (pre)
+ html += QLatin1String("</pre>");
+ else if (list)
+ html += QLatin1String("</li>");
+ else
+ html += QLatin1String("</p>");
+
+ if (list) {
+ if (list->itemNumber(block) == list->count() - 1) { // last item? close list
+ if (isOrderedList(list->format().style()))
+ html += QLatin1String("</ol>");
+ else
+ html += QLatin1String("</ul>");
+ }
+ }
+
+ defaultCharFormat = oldDefaultCharFormat;
+}
+
+extern bool qHasPixmapTexture(const QBrush& brush);
+
+QString QTextHtmlExporter::findUrlForImage(const QTextDocument *doc, qint64 cacheKey, bool isPixmap)
+{
+ QString url;
+ if (!doc)
+ return url;
+
+ if (QTextDocument *parent = qobject_cast<QTextDocument *>(doc->parent()))
+ return findUrlForImage(parent, cacheKey, isPixmap);
+
+ if (doc && doc->docHandle()) {
+ QTextDocumentPrivate *priv = doc->docHandle();
+ QMap<QUrl, QVariant>::const_iterator it = priv->cachedResources.constBegin();
+ for (; it != priv->cachedResources.constEnd(); ++it) {
+
+ const QVariant &v = it.value();
+ if (v.type() == QVariant::Image && !isPixmap) {
+ if (qvariant_cast<QImage>(v).cacheKey() == cacheKey)
+ break;
+ }
+
+ if (v.type() == QVariant::Pixmap && isPixmap) {
+ if (qvariant_cast<QPixmap>(v).cacheKey() == cacheKey)
+ break;
+ }
+ }
+
+ if (it != priv->cachedResources.constEnd())
+ url = it.key().toString();
+ }
+
+ return url;
+}
+
+void QTextDocumentPrivate::mergeCachedResources(const QTextDocumentPrivate *priv)
+{
+ if (!priv)
+ return;
+
+ cachedResources.unite(priv->cachedResources);
+}
+
+void QTextHtmlExporter::emitBackgroundAttribute(const QTextFormat &format)
+{
+ if (format.hasProperty(QTextFormat::BackgroundImageUrl)) {
+ QString url = format.property(QTextFormat::BackgroundImageUrl).toString();
+ emitAttribute("background", url);
+ } else {
+ const QBrush &brush = format.background();
+ if (brush.style() == Qt::SolidPattern) {
+ emitAttribute("bgcolor", brush.color().name());
+ } else if (brush.style() == Qt::TexturePattern) {
+ const bool isPixmap = qHasPixmapTexture(brush);
+ const qint64 cacheKey = isPixmap ? brush.texture().cacheKey() : brush.textureImage().cacheKey();
+
+ const QString url = findUrlForImage(doc, cacheKey, isPixmap);
+
+ if (!url.isEmpty())
+ emitAttribute("background", url);
+ }
+ }
+}
+
+void QTextHtmlExporter::emitTable(const QTextTable *table)
+{
+ QTextTableFormat format = table->format();
+
+ html += QLatin1String("\n<table");
+
+ if (format.hasProperty(QTextFormat::FrameBorder))
+ emitAttribute("border", QString::number(format.border()));
+
+ emitFrameStyle(format, TableFrame);
+
+ emitAlignment(format.alignment());
+ emitTextLength("width", format.width());
+
+ if (format.hasProperty(QTextFormat::TableCellSpacing))
+ emitAttribute("cellspacing", QString::number(format.cellSpacing()));
+ if (format.hasProperty(QTextFormat::TableCellPadding))
+ emitAttribute("cellpadding", QString::number(format.cellPadding()));
+
+ emitBackgroundAttribute(format);
+
+ html += QLatin1Char('>');
+
+ const int rows = table->rows();
+ const int columns = table->columns();
+
+ QVector<QTextLength> columnWidths = format.columnWidthConstraints();
+ if (columnWidths.isEmpty()) {
+ columnWidths.resize(columns);
+ columnWidths.fill(QTextLength());
+ }
+ Q_ASSERT(columnWidths.count() == columns);
+
+ QVarLengthArray<bool> widthEmittedForColumn(columns);
+ for (int i = 0; i < columns; ++i)
+ widthEmittedForColumn[i] = false;
+
+ const int headerRowCount = qMin(format.headerRowCount(), rows);
+ if (headerRowCount > 0)
+ html += QLatin1String("<thead>");
+
+ for (int row = 0; row < rows; ++row) {
+ html += QLatin1String("\n<tr>");
+
+ for (int col = 0; col < columns; ++col) {
+ const QTextTableCell cell = table->cellAt(row, col);
+
+ // for col/rowspans
+ if (cell.row() != row)
+ continue;
+
+ if (cell.column() != col)
+ continue;
+
+ html += QLatin1String("\n<td");
+
+ if (!widthEmittedForColumn[col] && cell.columnSpan() == 1) {
+ emitTextLength("width", columnWidths.at(col));
+ widthEmittedForColumn[col] = true;
+ }
+
+ if (cell.columnSpan() > 1)
+ emitAttribute("colspan", QString::number(cell.columnSpan()));
+
+ if (cell.rowSpan() > 1)
+ emitAttribute("rowspan", QString::number(cell.rowSpan()));
+
+ const QTextTableCellFormat cellFormat = cell.format().toTableCellFormat();
+ emitBackgroundAttribute(cellFormat);
+
+ QTextCharFormat oldDefaultCharFormat = defaultCharFormat;
+
+ QTextCharFormat::VerticalAlignment valign = cellFormat.verticalAlignment();
+
+ QString styleString;
+ if (valign >= QTextCharFormat::AlignMiddle && valign <= QTextCharFormat::AlignBottom) {
+ styleString += QLatin1String(" vertical-align:");
+ switch (valign) {
+ case QTextCharFormat::AlignMiddle:
+ styleString += QLatin1String("middle");
+ break;
+ case QTextCharFormat::AlignTop:
+ styleString += QLatin1String("top");
+ break;
+ case QTextCharFormat::AlignBottom:
+ styleString += QLatin1String("bottom");
+ break;
+ default:
+ break;
+ }
+ styleString += QLatin1Char(';');
+
+ QTextCharFormat temp;
+ temp.setVerticalAlignment(valign);
+ defaultCharFormat.merge(temp);
+ }
+
+ if (cellFormat.hasProperty(QTextFormat::TableCellLeftPadding))
+ styleString += QLatin1String(" padding-left:") + QString::number(cellFormat.leftPadding()) + QLatin1Char(';');
+ if (cellFormat.hasProperty(QTextFormat::TableCellRightPadding))
+ styleString += QLatin1String(" padding-right:") + QString::number(cellFormat.rightPadding()) + QLatin1Char(';');
+ if (cellFormat.hasProperty(QTextFormat::TableCellTopPadding))
+ styleString += QLatin1String(" padding-top:") + QString::number(cellFormat.topPadding()) + QLatin1Char(';');
+ if (cellFormat.hasProperty(QTextFormat::TableCellBottomPadding))
+ styleString += QLatin1String(" padding-bottom:") + QString::number(cellFormat.bottomPadding()) + QLatin1Char(';');
+
+ if (!styleString.isEmpty())
+ html += QLatin1String(" style=\"") + styleString + QLatin1Char('\"');
+
+ html += QLatin1Char('>');
+
+ emitFrame(cell.begin());
+
+ html += QLatin1String("</td>");
+
+ defaultCharFormat = oldDefaultCharFormat;
+ }
+
+ html += QLatin1String("</tr>");
+ if (headerRowCount > 0 && row == headerRowCount - 1)
+ html += QLatin1String("</thead>");
+ }
+
+ html += QLatin1String("</table>");
+}
+
+void QTextHtmlExporter::emitFrame(QTextFrame::Iterator frameIt)
+{
+ if (!frameIt.atEnd()) {
+ QTextFrame::Iterator next = frameIt;
+ ++next;
+ if (next.atEnd()
+ && frameIt.currentFrame() == 0
+ && frameIt.parentFrame() != doc->rootFrame()
+ && frameIt.currentBlock().begin().atEnd())
+ return;
+ }
+
+ for (QTextFrame::Iterator it = frameIt;
+ !it.atEnd(); ++it) {
+ if (QTextFrame *f = it.currentFrame()) {
+ if (QTextTable *table = qobject_cast<QTextTable *>(f)) {
+ emitTable(table);
+ } else {
+ emitTextFrame(f);
+ }
+ } else if (it.currentBlock().isValid()) {
+ emitBlock(it.currentBlock());
+ }
+ }
+}
+
+void QTextHtmlExporter::emitTextFrame(const QTextFrame *f)
+{
+ FrameType frameType = f->parentFrame() ? TextFrame : RootFrame;
+
+ html += QLatin1String("\n<table");
+ QTextFrameFormat format = f->frameFormat();
+
+ if (format.hasProperty(QTextFormat::FrameBorder))
+ emitAttribute("border", QString::number(format.border()));
+
+ emitFrameStyle(format, frameType);
+
+ emitTextLength("width", format.width());
+ emitTextLength("height", format.height());
+
+ // root frame's bcolor goes in the <body> tag
+ if (frameType != RootFrame)
+ emitBackgroundAttribute(format);
+
+ html += QLatin1Char('>');
+ html += QLatin1String("\n<tr>\n<td style=\"border: none;\">");
+ emitFrame(f->begin());
+ html += QLatin1String("</td></tr></table>");
+}
+
+void QTextHtmlExporter::emitFrameStyle(const QTextFrameFormat &format, FrameType frameType)
+{
+ QLatin1String styleAttribute(" style=\"");
+ html += styleAttribute;
+ const int originalHtmlLength = html.length();
+
+ if (frameType == TextFrame)
+ html += QLatin1String("-qt-table-type: frame;");
+ else if (frameType == RootFrame)
+ html += QLatin1String("-qt-table-type: root;");
+
+ const QTextFrameFormat defaultFormat;
+
+ emitFloatStyle(format.position(), OmitStyleTag);
+ emitPageBreakPolicy(format.pageBreakPolicy());
+
+ if (format.borderBrush() != defaultFormat.borderBrush()) {
+ html += QLatin1String(" border-color:");
+ html += format.borderBrush().color().name();
+ html += QLatin1Char(';');
+ }
+
+ if (format.borderStyle() != defaultFormat.borderStyle())
+ emitBorderStyle(format.borderStyle());
+
+ if (format.hasProperty(QTextFormat::FrameMargin)
+ || format.hasProperty(QTextFormat::FrameLeftMargin)
+ || format.hasProperty(QTextFormat::FrameRightMargin)
+ || format.hasProperty(QTextFormat::FrameTopMargin)
+ || format.hasProperty(QTextFormat::FrameBottomMargin))
+ emitMargins(QString::number(format.topMargin()),
+ QString::number(format.bottomMargin()),
+ QString::number(format.leftMargin()),
+ QString::number(format.rightMargin()));
+
+ if (html.length() == originalHtmlLength) // nothing emitted?
+ html.chop(qstrlen(styleAttribute.latin1()));
+ else
+ html += QLatin1Char('\"');
+}
+
+/*!
+ Returns a string containing an HTML representation of the document.
+
+ The \a encoding parameter specifies the value for the charset attribute
+ in the html header. For example if 'utf-8' is specified then the
+ beginning of the generated html will look like this:
+ \snippet doc/src/snippets/code/src_gui_text_qtextdocument.cpp 1
+
+ If no encoding is specified then no such meta information is generated.
+
+ If you later on convert the returned html string into a byte array for
+ transmission over a network or when saving to disk you should specify
+ the encoding you're going to use for the conversion to a byte array here.
+
+ \sa {Supported HTML Subset}
+*/
+#ifndef QT_NO_TEXTHTMLPARSER
+QString QTextDocument::toHtml(const QByteArray &encoding) const
+{
+ return QTextHtmlExporter(this).toHtml(encoding);
+}
+#endif // QT_NO_TEXTHTMLPARSER
+
+/*!
+ Returns a vector of text formats for all the formats used in the document.
+*/
+QVector<QTextFormat> QTextDocument::allFormats() const
+{
+ Q_D(const QTextDocument);
+ return d->formatCollection()->formats;
+}
+
+
+/*!
+ \internal
+
+ So that not all classes have to be friends of each other...
+*/
+QTextDocumentPrivate *QTextDocument::docHandle() const
+{
+ Q_D(const QTextDocument);
+ return const_cast<QTextDocumentPrivate *>(d);
+}
+
+/*!
+ \since 4.4
+ \fn QTextDocument::undoCommandAdded()
+
+ This signal is emitted every time a new level of undo is added to the QTextDocument.
+*/
+
+QT_END_NAMESPACE
diff --git a/src/gui/text/qtextdocument.h b/src/gui/text/qtextdocument.h
new file mode 100644
index 0000000000..c33778312e
--- /dev/null
+++ b/src/gui/text/qtextdocument.h
@@ -0,0 +1,298 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QTEXTDOCUMENT_H
+#define QTEXTDOCUMENT_H
+
+#include <QtCore/qobject.h>
+#include <QtCore/qsize.h>
+#include <QtCore/qrect.h>
+#include <QtGui/qfont.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+class QTextFormatCollection;
+class QTextListFormat;
+class QRect;
+class QPainter;
+class QPrinter;
+class QAbstractTextDocumentLayout;
+class QPoint;
+class QTextCursor;
+class QTextObject;
+class QTextFormat;
+class QTextFrame;
+class QTextBlock;
+class QTextCodec;
+class QUrl;
+class QVariant;
+class QRectF;
+class QTextOption;
+
+template<typename T> class QVector;
+
+namespace Qt
+{
+ enum HitTestAccuracy { ExactHit, FuzzyHit };
+ enum WhiteSpaceMode {
+ WhiteSpaceNormal,
+ WhiteSpacePre,
+ WhiteSpaceNoWrap,
+ WhiteSpaceModeUndefined = -1
+ };
+
+ Q_GUI_EXPORT bool mightBeRichText(const QString&);
+ Q_GUI_EXPORT QString escape(const QString& plain);
+ Q_GUI_EXPORT QString convertFromPlainText(const QString &plain, WhiteSpaceMode mode = WhiteSpacePre);
+
+#ifndef QT_NO_TEXTCODEC
+ Q_GUI_EXPORT QTextCodec *codecForHtml(const QByteArray &ba);
+#endif
+}
+
+class Q_GUI_EXPORT QAbstractUndoItem
+{
+public:
+ virtual ~QAbstractUndoItem() = 0;
+ virtual void undo() = 0;
+ virtual void redo() = 0;
+};
+
+inline QAbstractUndoItem::~QAbstractUndoItem()
+{
+}
+
+class QTextDocumentPrivate;
+
+class Q_GUI_EXPORT QTextDocument : public QObject
+{
+ Q_OBJECT
+
+ Q_PROPERTY(bool undoRedoEnabled READ isUndoRedoEnabled WRITE setUndoRedoEnabled)
+ Q_PROPERTY(bool modified READ isModified WRITE setModified DESIGNABLE false)
+ Q_PROPERTY(QSizeF pageSize READ pageSize WRITE setPageSize)
+ Q_PROPERTY(QFont defaultFont READ defaultFont WRITE setDefaultFont)
+ Q_PROPERTY(bool useDesignMetrics READ useDesignMetrics WRITE setUseDesignMetrics)
+ Q_PROPERTY(QSizeF size READ size)
+ Q_PROPERTY(qreal textWidth READ textWidth WRITE setTextWidth)
+ Q_PROPERTY(int blockCount READ blockCount)
+ Q_PROPERTY(qreal indentWidth READ indentWidth WRITE setIndentWidth)
+#ifndef QT_NO_CSSPARSER
+ Q_PROPERTY(QString defaultStyleSheet READ defaultStyleSheet WRITE setDefaultStyleSheet)
+#endif
+ Q_PROPERTY(int maximumBlockCount READ maximumBlockCount WRITE setMaximumBlockCount)
+ Q_PROPERTY(qreal documentMargin READ documentMargin WRITE setDocumentMargin)
+ QDOC_PROPERTY(QTextOption defaultTextOption READ defaultTextOption WRITE setDefaultTextOption)
+
+public:
+ explicit QTextDocument(QObject *parent = 0);
+ explicit QTextDocument(const QString &text, QObject *parent = 0);
+ ~QTextDocument();
+
+ QTextDocument *clone(QObject *parent = 0) const;
+
+ bool isEmpty() const;
+ virtual void clear();
+
+ void setUndoRedoEnabled(bool enable);
+ bool isUndoRedoEnabled() const;
+
+ bool isUndoAvailable() const;
+ bool isRedoAvailable() const;
+
+ int revision() const;
+
+ void setDocumentLayout(QAbstractTextDocumentLayout *layout);
+ QAbstractTextDocumentLayout *documentLayout() const;
+
+ enum MetaInformation {
+ DocumentTitle,
+ DocumentUrl
+ };
+ void setMetaInformation(MetaInformation info, const QString &);
+ QString metaInformation(MetaInformation info) const;
+
+#ifndef QT_NO_TEXTHTMLPARSER
+ QString toHtml(const QByteArray &encoding = QByteArray()) const;
+ void setHtml(const QString &html);
+#endif
+
+ QString toPlainText() const;
+ void setPlainText(const QString &text);
+
+ QChar characterAt(int pos) const;
+
+ enum FindFlag
+ {
+ FindBackward = 0x00001,
+ FindCaseSensitively = 0x00002,
+ FindWholeWords = 0x00004
+ };
+ Q_DECLARE_FLAGS(FindFlags, FindFlag)
+
+ QTextCursor find(const QString &subString, int from = 0, FindFlags options = 0) const;
+ QTextCursor find(const QString &subString, const QTextCursor &from, FindFlags options = 0) const;
+
+ QTextCursor find(const QRegExp &expr, int from = 0, FindFlags options = 0) const;
+ QTextCursor find(const QRegExp &expr, const QTextCursor &from, FindFlags options = 0) const;
+
+ QTextFrame *frameAt(int pos) const;
+ QTextFrame *rootFrame() const;
+
+ QTextObject *object(int objectIndex) const;
+ QTextObject *objectForFormat(const QTextFormat &) const;
+
+ QTextBlock findBlock(int pos) const;
+ QTextBlock findBlockByNumber(int blockNumber) const;
+ QTextBlock findBlockByLineNumber(int blockNumber) const;
+ QTextBlock begin() const;
+ QTextBlock end() const;
+
+ QTextBlock firstBlock() const;
+ QTextBlock lastBlock() const;
+
+ void setPageSize(const QSizeF &size);
+ QSizeF pageSize() const;
+
+ void setDefaultFont(const QFont &font);
+ QFont defaultFont() const;
+
+ int pageCount() const;
+
+ bool isModified() const;
+
+#ifndef QT_NO_PRINTER
+ void print(QPrinter *printer) const;
+#endif
+
+ enum ResourceType {
+ HtmlResource = 1,
+ ImageResource = 2,
+ StyleSheetResource = 3,
+
+ UserResource = 100
+ };
+
+ QVariant resource(int type, const QUrl &name) const;
+ void addResource(int type, const QUrl &name, const QVariant &resource);
+
+ QVector<QTextFormat> allFormats() const;
+
+ void markContentsDirty(int from, int length);
+
+ void setUseDesignMetrics(bool b);
+ bool useDesignMetrics() const;
+
+ void drawContents(QPainter *painter, const QRectF &rect = QRectF());
+
+ void setTextWidth(qreal width);
+ qreal textWidth() const;
+
+ qreal idealWidth() const;
+
+ qreal indentWidth() const;
+ void setIndentWidth(qreal width);
+
+ qreal documentMargin() const;
+ void setDocumentMargin(qreal margin);
+
+ void adjustSize();
+ QSizeF size() const;
+
+ int blockCount() const;
+ int lineCount() const;
+ int characterCount() const;
+
+#ifndef QT_NO_CSSPARSER
+ void setDefaultStyleSheet(const QString &sheet);
+ QString defaultStyleSheet() const;
+#endif
+
+ void undo(QTextCursor *cursor);
+ void redo(QTextCursor *cursor);
+
+ int maximumBlockCount() const;
+ void setMaximumBlockCount(int maximum);
+
+ QTextOption defaultTextOption() const;
+ void setDefaultTextOption(const QTextOption &option);
+
+Q_SIGNALS:
+ void contentsChange(int from, int charsRemoves, int charsAdded);
+ void contentsChanged();
+ void undoAvailable(bool);
+ void redoAvailable(bool);
+ void undoCommandAdded();
+ void modificationChanged(bool m);
+ void cursorPositionChanged(const QTextCursor &cursor);
+ void blockCountChanged(int newBlockCount);
+
+ void documentLayoutChanged();
+
+public Q_SLOTS:
+ void undo();
+ void redo();
+ void appendUndoItem(QAbstractUndoItem *);
+ void setModified(bool m = true);
+
+protected:
+ virtual QTextObject *createObject(const QTextFormat &f);
+ virtual QVariant loadResource(int type, const QUrl &name);
+
+ QTextDocument(QTextDocumentPrivate &dd, QObject *parent);
+public:
+ QTextDocumentPrivate *docHandle() const;
+private:
+ Q_DISABLE_COPY(QTextDocument)
+ Q_DECLARE_PRIVATE(QTextDocument)
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(QTextDocument::FindFlags)
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QTEXTDOCUMENT_H
diff --git a/src/gui/text/qtextdocument_p.cpp b/src/gui/text/qtextdocument_p.cpp
new file mode 100644
index 0000000000..320ecbfc4e
--- /dev/null
+++ b/src/gui/text/qtextdocument_p.cpp
@@ -0,0 +1,1600 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <private/qtools_p.h>
+#include <qdebug.h>
+
+#include "qtextdocument_p.h"
+#include "qtextdocument.h"
+#include <qtextformat.h>
+#include "qtextformat_p.h"
+#include "qtextobject_p.h"
+#include "qtextcursor.h"
+#include "qtextimagehandler_p.h"
+#include "qtextcursor_p.h"
+#include "qtextdocumentlayout_p.h"
+#include "qtexttable.h"
+#include "qtextengine_p.h"
+
+#include <stdlib.h>
+
+QT_BEGIN_NAMESPACE
+
+#define PMDEBUG if(0) qDebug
+
+/*
+ Structure of a document:
+
+ DOCUMENT :== FRAME_CONTENTS
+ FRAME :== START_OF_FRAME FRAME_CONTENTS END_OF_FRAME
+ FRAME_CONTENTS = LIST_OF_BLOCKS ((FRAME | TABLE) LIST_OF_BLOCKS)*
+ TABLE :== (START_OF_FRAME TABLE_CELL)+ END_OF_FRAME
+ TABLE_CELL = FRAME_CONTENTS
+ LIST_OF_BLOCKS :== (BLOCK END_OF_PARA)* BLOCK
+ BLOCK :== (FRAGMENT)*
+ FRAGMENT :== String of characters
+
+ END_OF_PARA :== 0x2029 # Paragraph separator in Unicode
+ START_OF_FRAME :== 0xfdd0
+ END_OF_FRAME := 0xfdd1
+
+ Note also that LIST_OF_BLOCKS can be empty. Nevertheless, there is
+ at least one valid cursor position there where you could start
+ typing. The block format is in this case determined by the last
+ END_OF_PARA/START_OF_FRAME/END_OF_FRAME (see below).
+
+ Lists are not in here, as they are treated specially. A list is just
+ a collection of (not neccessarily connected) blocks, that share the
+ same objectIndex() in the format that refers to the list format and
+ object.
+
+ The above does not clearly note where formats are. Here's
+ how it looks currently:
+
+ FRAGMENT: one charFormat associated
+
+ END_OF_PARA: one charFormat, and a blockFormat for the _next_ block.
+
+ START_OF_FRAME: one char format, and a blockFormat (for the next
+ block). The format associated with the objectIndex() of the
+ charFormat decides whether this is a frame or table and its
+ properties
+
+ END_OF_FRAME: one charFormat and a blockFormat (for the next
+ block). The object() of the charFormat is the same as for the
+ corresponding START_OF_BLOCK.
+
+
+ The document is independent of the layout with certain restrictions:
+
+ * Cursor movement (esp. up and down) depend on the layout.
+ * You cannot have more than one layout, as the layout data of QTextObjects
+ is stored in the text object itself.
+
+*/
+
+void QTextBlockData::invalidate() const
+{
+ if (layout)
+ layout->engine()->invalidate();
+}
+
+static bool isValidBlockSeparator(const QChar &ch)
+{
+ return ch == QChar::ParagraphSeparator
+ || ch == QTextBeginningOfFrame
+ || ch == QTextEndOfFrame;
+}
+
+#ifndef QT_NO_DEBUG
+static bool noBlockInString(const QString &str)
+{
+ return !str.contains(QChar::ParagraphSeparator)
+ && !str.contains(QTextBeginningOfFrame)
+ && !str.contains(QTextEndOfFrame);
+}
+#endif
+
+bool QTextUndoCommand::tryMerge(const QTextUndoCommand &other)
+{
+ if (command != other.command)
+ return false;
+
+ if (command == Inserted
+ && (pos + length == other.pos)
+ && (strPos + length == other.strPos)
+ && format == other.format) {
+
+ length += other.length;
+ return true;
+ }
+
+ // removal to the 'right' using 'Delete' key
+ if (command == Removed
+ && pos == other.pos
+ && (strPos + length == other.strPos)
+ && format == other.format) {
+
+ length += other.length;
+ return true;
+ }
+
+ // removal to the 'left' using 'Backspace'
+ if (command == Removed
+ && (other.pos + other.length == pos)
+ && (other.strPos + other.length == strPos)
+ && (format == other.format)) {
+
+ int l = length;
+ (*this) = other;
+
+ length += l;
+ return true;
+ }
+
+ return false;
+}
+
+QTextDocumentPrivate::QTextDocumentPrivate()
+ : wasUndoAvailable(false),
+ wasRedoAvailable(false),
+ docChangeOldLength(0),
+ docChangeLength(0),
+ framesDirty(true),
+ initialBlockCharFormatIndex(-1) // set correctly later in init()
+{
+ editBlock = 0;
+ docChangeFrom = -1;
+
+ undoState = 0;
+
+ lout = 0;
+
+ modified = false;
+ modifiedState = 0;
+
+ undoEnabled = true;
+ inContentsChange = false;
+ defaultTextOption.setTabStop(80); // same as in qtextengine.cpp
+ defaultTextOption.setWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere);
+
+ indentWidth = 40;
+ documentMargin = 4;
+
+ maximumBlockCount = 0;
+ needsEnsureMaximumBlockCount = false;
+ unreachableCharacterCount = 0;
+ lastBlockCount = 0;
+}
+
+void QTextDocumentPrivate::init()
+{
+ rtFrame = 0;
+ framesDirty = false;
+
+ bool undoState = undoEnabled;
+ undoEnabled = false;
+ initialBlockCharFormatIndex = formats.indexForFormat(QTextCharFormat());
+ insertBlock(0, formats.indexForFormat(QTextBlockFormat()), formats.indexForFormat(QTextCharFormat()));
+ undoEnabled = undoState;
+ modified = false;
+ modifiedState = 0;
+}
+
+void QTextDocumentPrivate::clear()
+{
+ Q_Q(QTextDocument);
+ for (int i = 0; i < cursors.count(); ++i) {
+ cursors.at(i)->setPosition(0);
+ cursors.at(i)->currentCharFormat = -1;
+ cursors.at(i)->anchor = 0;
+ cursors.at(i)->adjusted_anchor = 0;
+ }
+
+ QList<QTextCursorPrivate *>oldCursors = cursors;
+ cursors.clear();
+ changedCursors.clear();
+
+ QMap<int, QTextObject *>::Iterator objectIt = objects.begin();
+ while (objectIt != objects.end()) {
+ if (*objectIt != rtFrame) {
+ delete *objectIt;
+ objectIt = objects.erase(objectIt);
+ } else {
+ ++objectIt;
+ }
+ }
+ // also clear out the remaining root frame pointer
+ // (we're going to delete the object further down)
+ objects.clear();
+
+ title.clear();
+ undoState = 0;
+ truncateUndoStack();
+ text = QString();
+ unreachableCharacterCount = 0;
+ modifiedState = 0;
+ modified = false;
+ formats = QTextFormatCollection();
+ int len = fragments.length();
+ fragments.clear();
+ blocks.clear();
+ cachedResources.clear();
+ delete rtFrame;
+ init();
+ cursors = oldCursors;
+ inContentsChange = true;
+ q->contentsChange(0, len, 0);
+ inContentsChange = false;
+ if (lout)
+ lout->documentChanged(0, len, 0);
+}
+
+QTextDocumentPrivate::~QTextDocumentPrivate()
+{
+ for (int i = 0; i < cursors.count(); ++i)
+ cursors.at(i)->priv = 0;
+ cursors.clear();
+ undoState = 0;
+ undoEnabled = true;
+ truncateUndoStack();
+}
+
+void QTextDocumentPrivate::setLayout(QAbstractTextDocumentLayout *layout)
+{
+ Q_Q(QTextDocument);
+ if (lout == layout)
+ return;
+ const bool firstLayout = !lout;
+ delete lout;
+ lout = layout;
+
+ if (!firstLayout)
+ for (BlockMap::Iterator it = blocks.begin(); !it.atEnd(); ++it)
+ it->free();
+
+ emit q->documentLayoutChanged();
+ inContentsChange = true;
+ emit q->contentsChange(0, 0, length());
+ inContentsChange = false;
+ if (lout)
+ lout->documentChanged(0, 0, length());
+}
+
+
+void QTextDocumentPrivate::insert_string(int pos, uint strPos, uint length, int format, QTextUndoCommand::Operation op)
+{
+ // ##### optimise when only appending to the fragment!
+ Q_ASSERT(noBlockInString(text.mid(strPos, length)));
+
+ split(pos);
+ uint x = fragments.insert_single(pos, length);
+ QTextFragmentData *X = fragments.fragment(x);
+ X->format = format;
+ X->stringPosition = strPos;
+ uint w = fragments.previous(x);
+ if (w)
+ unite(w);
+
+ int b = blocks.findNode(pos);
+ blocks.setSize(b, blocks.size(b)+length);
+
+ Q_ASSERT(blocks.length() == fragments.length());
+
+ QTextFrame *frame = qobject_cast<QTextFrame *>(objectForFormat(format));
+ if (frame) {
+ frame->d_func()->fragmentAdded(text.at(strPos), x);
+ framesDirty = true;
+ }
+
+ adjustDocumentChangesAndCursors(pos, length, op);
+}
+
+int QTextDocumentPrivate::insert_block(int pos, uint strPos, int format, int blockFormat, QTextUndoCommand::Operation op, int command)
+{
+ split(pos);
+ uint x = fragments.insert_single(pos, 1);
+ QTextFragmentData *X = fragments.fragment(x);
+ X->format = format;
+ X->stringPosition = strPos;
+ // no need trying to unite, since paragraph separators are always in a fragment of their own
+
+ Q_ASSERT(isValidBlockSeparator(text.at(strPos)));
+ Q_ASSERT(blocks.length()+1 == fragments.length());
+
+ int block_pos = pos;
+ if (blocks.length() && command == QTextUndoCommand::BlockRemoved)
+ ++block_pos;
+ int size = 1;
+ int n = blocks.findNode(block_pos);
+ int key = n ? blocks.position(n) : blocks.length();
+
+ Q_ASSERT(n || (!n && block_pos == blocks.length()));
+ if (key != block_pos) {
+ Q_ASSERT(key < block_pos);
+ int oldSize = blocks.size(n);
+ blocks.setSize(n, block_pos-key);
+ size += oldSize - (block_pos-key);
+ }
+ int b = blocks.insert_single(block_pos, size);
+ QTextBlockData *B = blocks.fragment(b);
+ B->format = blockFormat;
+
+ Q_ASSERT(blocks.length() == fragments.length());
+
+ QTextBlockGroup *group = qobject_cast<QTextBlockGroup *>(objectForFormat(blockFormat));
+ if (group)
+ group->blockInserted(QTextBlock(this, b));
+
+ QTextFrame *frame = qobject_cast<QTextFrame *>(objectForFormat(formats.format(format)));
+ if (frame) {
+ frame->d_func()->fragmentAdded(text.at(strPos), x);
+ framesDirty = true;
+ }
+
+ adjustDocumentChangesAndCursors(pos, 1, op);
+ return x;
+}
+
+int QTextDocumentPrivate::insertBlock(const QChar &blockSeparator,
+ int pos, int blockFormat, int charFormat, QTextUndoCommand::Operation op)
+{
+ Q_ASSERT(formats.format(blockFormat).isBlockFormat());
+ Q_ASSERT(formats.format(charFormat).isCharFormat());
+ Q_ASSERT(pos >= 0 && (pos < fragments.length() || (pos == 0 && fragments.length() == 0)));
+ Q_ASSERT(isValidBlockSeparator(blockSeparator));
+
+ beginEditBlock();
+
+ int strPos = text.length();
+ text.append(blockSeparator);
+
+ int ob = blocks.findNode(pos);
+ bool atBlockEnd = true;
+ bool atBlockStart = true;
+ int oldRevision = 0;
+ if (ob) {
+ atBlockEnd = (pos - blocks.position(ob) == blocks.size(ob)-1);
+ atBlockStart = ((int)blocks.position(ob) == pos);
+ oldRevision = blocks.fragment(ob)->revision;
+ }
+
+ const int fragment = insert_block(pos, strPos, charFormat, blockFormat, op, QTextUndoCommand::BlockRemoved);
+
+ Q_ASSERT(blocks.length() == fragments.length());
+
+ int b = blocks.findNode(pos);
+ QTextBlockData *B = blocks.fragment(b);
+
+ QTextUndoCommand c = { QTextUndoCommand::BlockInserted, true,
+ op, charFormat, strPos, pos, { blockFormat },
+ B->revision };
+
+ appendUndoItem(c);
+ Q_ASSERT(undoState == undoStack.size());
+
+ // update revision numbers of the modified blocks.
+ B->revision = (atBlockEnd && !atBlockStart)? oldRevision : undoState;
+ b = blocks.next(b);
+ if (b) {
+ B = blocks.fragment(b);
+ B->revision = atBlockStart ? oldRevision : undoState;
+ }
+
+ if (formats.charFormat(charFormat).objectIndex() == -1)
+ needsEnsureMaximumBlockCount = true;
+
+ endEditBlock();
+ return fragment;
+}
+
+int QTextDocumentPrivate::insertBlock(int pos, int blockFormat, int charFormat, QTextUndoCommand::Operation op)
+{
+ return insertBlock(QChar::ParagraphSeparator, pos, blockFormat, charFormat, op);
+}
+
+void QTextDocumentPrivate::insert(int pos, int strPos, int strLength, int format)
+{
+ if (strLength <= 0)
+ return;
+
+ Q_ASSERT(pos >= 0 && pos < fragments.length());
+ Q_ASSERT(formats.format(format).isCharFormat());
+
+ beginEditBlock();
+ insert_string(pos, strPos, strLength, format, QTextUndoCommand::MoveCursor);
+ if (undoEnabled) {
+ int b = blocks.findNode(pos);
+ QTextBlockData *B = blocks.fragment(b);
+
+ QTextUndoCommand c = { QTextUndoCommand::Inserted, true,
+ QTextUndoCommand::MoveCursor, format, strPos, pos, { strLength },
+ B->revision };
+ appendUndoItem(c);
+ B->revision = undoState;
+ Q_ASSERT(undoState == undoStack.size());
+ }
+ endEditBlock();
+}
+
+void QTextDocumentPrivate::insert(int pos, const QString &str, int format)
+{
+ if (str.size() == 0)
+ return;
+
+ Q_ASSERT(noBlockInString(str));
+
+ int strPos = text.length();
+ text.append(str);
+ insert(pos, strPos, str.length(), format);
+}
+
+int QTextDocumentPrivate::remove_string(int pos, uint length, QTextUndoCommand::Operation op)
+{
+ Q_ASSERT(pos >= 0);
+ Q_ASSERT(blocks.length() == fragments.length());
+ Q_ASSERT(blocks.length() >= pos+(int)length);
+
+ int b = blocks.findNode(pos);
+ uint x = fragments.findNode(pos);
+
+ Q_ASSERT(blocks.size(b) > length);
+ Q_ASSERT(x && fragments.position(x) == (uint)pos && fragments.size(x) == length);
+ Q_ASSERT(noBlockInString(text.mid(fragments.fragment(x)->stringPosition, length)));
+
+ blocks.setSize(b, blocks.size(b)-length);
+
+ QTextFrame *frame = qobject_cast<QTextFrame *>(objectForFormat(fragments.fragment(x)->format));
+ if (frame) {
+ frame->d_func()->fragmentRemoved(text.at(fragments.fragment(x)->stringPosition), x);
+ framesDirty = true;
+ }
+
+ const int w = fragments.erase_single(x);
+
+ if (!undoEnabled)
+ unreachableCharacterCount += length;
+
+ adjustDocumentChangesAndCursors(pos, -int(length), op);
+
+ return w;
+}
+
+int QTextDocumentPrivate::remove_block(int pos, int *blockFormat, int command, QTextUndoCommand::Operation op)
+{
+ Q_ASSERT(pos >= 0);
+ Q_ASSERT(blocks.length() == fragments.length());
+ Q_ASSERT(blocks.length() > pos);
+
+ int b = blocks.findNode(pos);
+ uint x = fragments.findNode(pos);
+
+ Q_ASSERT(x && (int)fragments.position(x) == pos);
+ Q_ASSERT(fragments.size(x) == 1);
+ Q_ASSERT(isValidBlockSeparator(text.at(fragments.fragment(x)->stringPosition)));
+ Q_ASSERT(b);
+
+ if (blocks.size(b) == 1 && command == QTextUndoCommand::BlockAdded) {
+ Q_ASSERT((int)blocks.position(b) == pos);
+// qDebug("removing empty block");
+ // empty block remove the block itself
+ } else {
+ // non empty block, merge with next one into this block
+// qDebug("merging block with next");
+ int n = blocks.next(b);
+ Q_ASSERT((int)blocks.position(n) == pos + 1);
+ blocks.setSize(b, blocks.size(b) + blocks.size(n) - 1);
+ b = n;
+ }
+ *blockFormat = blocks.fragment(b)->format;
+
+ QTextBlockGroup *group = qobject_cast<QTextBlockGroup *>(objectForFormat(blocks.fragment(b)->format));
+ if (group)
+ group->blockRemoved(QTextBlock(this, b));
+
+ QTextFrame *frame = qobject_cast<QTextFrame *>(objectForFormat(fragments.fragment(x)->format));
+ if (frame) {
+ frame->d_func()->fragmentRemoved(text.at(fragments.fragment(x)->stringPosition), x);
+ framesDirty = true;
+ }
+
+ blocks.erase_single(b);
+ const int w = fragments.erase_single(x);
+
+ adjustDocumentChangesAndCursors(pos, -1, op);
+
+ return w;
+}
+
+#if !defined(QT_NO_DEBUG)
+static bool isAncestorFrame(QTextFrame *possibleAncestor, QTextFrame *child)
+{
+ while (child) {
+ if (child == possibleAncestor)
+ return true;
+ child = child->parentFrame();
+ }
+ return false;
+}
+#endif
+
+void QTextDocumentPrivate::move(int pos, int to, int length, QTextUndoCommand::Operation op)
+{
+ Q_ASSERT(to <= fragments.length() && to <= pos);
+ Q_ASSERT(pos >= 0 && pos+length <= fragments.length());
+ Q_ASSERT(blocks.length() == fragments.length());
+
+ if (pos == to)
+ return;
+
+ const bool needsInsert = to != -1;
+
+#if !defined(QT_NO_DEBUG)
+ const bool startAndEndInSameFrame = (frameAt(pos) == frameAt(pos + length - 1));
+
+ const bool endIsEndOfChildFrame = (isAncestorFrame(frameAt(pos), frameAt(pos + length - 1))
+ && text.at(find(pos + length - 1)->stringPosition) == QTextEndOfFrame);
+
+ const bool startIsStartOfFrameAndEndIsEndOfFrameWithCommonParent
+ = (text.at(find(pos)->stringPosition) == QTextBeginningOfFrame
+ && text.at(find(pos + length - 1)->stringPosition) == QTextEndOfFrame
+ && frameAt(pos)->parentFrame() == frameAt(pos + length - 1)->parentFrame());
+
+ const bool isFirstTableCell = (qobject_cast<QTextTable *>(frameAt(pos + length - 1))
+ && frameAt(pos + length - 1)->parentFrame() == frameAt(pos));
+
+ Q_ASSERT(startAndEndInSameFrame || endIsEndOfChildFrame || startIsStartOfFrameAndEndIsEndOfFrameWithCommonParent || isFirstTableCell);
+#endif
+
+ beginEditBlock();
+
+ split(pos);
+ split(pos+length);
+
+ uint dst = needsInsert ? fragments.findNode(to) : 0;
+ uint dstKey = needsInsert ? fragments.position(dst) : 0;
+
+ uint x = fragments.findNode(pos);
+ uint end = fragments.findNode(pos+length);
+
+ uint w = 0;
+ while (x != end) {
+ uint n = fragments.next(x);
+
+ uint key = fragments.position(x);
+ uint b = blocks.findNode(key+1);
+ QTextBlockData *B = blocks.fragment(b);
+ int blockRevision = B->revision;
+
+ QTextFragmentData *X = fragments.fragment(x);
+ QTextUndoCommand c = { QTextUndoCommand::Removed, true,
+ op, X->format, X->stringPosition, key, { X->size_array[0] },
+ blockRevision };
+ QTextUndoCommand cInsert = { QTextUndoCommand::Inserted, true,
+ op, X->format, X->stringPosition, dstKey, { X->size_array[0] },
+ blockRevision };
+
+ if (key+1 != blocks.position(b)) {
+// qDebug("remove_string from %d length %d", key, X->size_array[0]);
+ Q_ASSERT(noBlockInString(text.mid(X->stringPosition, X->size_array[0])));
+ w = remove_string(key, X->size_array[0], op);
+
+ if (needsInsert) {
+ insert_string(dstKey, X->stringPosition, X->size_array[0], X->format, op);
+ dstKey += X->size_array[0];
+ }
+ } else {
+// qDebug("remove_block at %d", key);
+ Q_ASSERT(X->size_array[0] == 1 && isValidBlockSeparator(text.at(X->stringPosition)));
+ b = blocks.previous(b);
+ B = 0;
+ c.command = blocks.size(b) == 1 ? QTextUndoCommand::BlockDeleted : QTextUndoCommand::BlockRemoved;
+ w = remove_block(key, &c.blockFormat, QTextUndoCommand::BlockAdded, op);
+
+ if (needsInsert) {
+ insert_block(dstKey++, X->stringPosition, X->format, c.blockFormat, op, QTextUndoCommand::BlockRemoved);
+ cInsert.command = blocks.size(b) == 1 ? QTextUndoCommand::BlockAdded : QTextUndoCommand::BlockInserted;
+ cInsert.blockFormat = c.blockFormat;
+ }
+ }
+ appendUndoItem(c);
+ if (B)
+ B->revision = undoState;
+ x = n;
+
+ if (needsInsert)
+ appendUndoItem(cInsert);
+ }
+ if (w)
+ unite(w);
+
+ Q_ASSERT(blocks.length() == fragments.length());
+
+ endEditBlock();
+}
+
+void QTextDocumentPrivate::remove(int pos, int length, QTextUndoCommand::Operation op)
+{
+ if (length == 0)
+ return;
+ move(pos, -1, length, op);
+}
+
+void QTextDocumentPrivate::setCharFormat(int pos, int length, const QTextCharFormat &newFormat, FormatChangeMode mode)
+{
+ beginEditBlock();
+
+ Q_ASSERT(newFormat.isValid());
+
+ int newFormatIdx = -1;
+ if (mode == SetFormatAndPreserveObjectIndices) {
+ QTextCharFormat cleanFormat = newFormat;
+ cleanFormat.clearProperty(QTextFormat::ObjectIndex);
+ newFormatIdx = formats.indexForFormat(cleanFormat);
+ } else if (mode == SetFormat) {
+ newFormatIdx = formats.indexForFormat(newFormat);
+ }
+
+ if (pos == -1) {
+ if (mode == MergeFormat) {
+ QTextFormat format = formats.format(initialBlockCharFormatIndex);
+ format.merge(newFormat);
+ initialBlockCharFormatIndex = formats.indexForFormat(format);
+ } else if (mode == SetFormatAndPreserveObjectIndices
+ && formats.format(initialBlockCharFormatIndex).objectIndex() != -1) {
+ QTextCharFormat f = newFormat;
+ f.setObjectIndex(formats.format(initialBlockCharFormatIndex).objectIndex());
+ initialBlockCharFormatIndex = formats.indexForFormat(f);
+ } else {
+ initialBlockCharFormatIndex = newFormatIdx;
+ }
+
+ ++pos;
+ --length;
+ }
+
+ const int startPos = pos;
+ const int endPos = pos + length;
+
+ split(startPos);
+ split(endPos);
+
+ while (pos < endPos) {
+ FragmentMap::Iterator it = fragments.find(pos);
+ Q_ASSERT(!it.atEnd());
+
+ QTextFragmentData *fragment = it.value();
+
+ Q_ASSERT(formats.format(fragment->format).type() == QTextFormat::CharFormat);
+
+ int offset = pos - it.position();
+ int length = qMin(endPos - pos, int(fragment->size_array[0] - offset));
+ int oldFormat = fragment->format;
+
+ if (mode == MergeFormat) {
+ QTextFormat format = formats.format(fragment->format);
+ format.merge(newFormat);
+ fragment->format = formats.indexForFormat(format);
+ } else if (mode == SetFormatAndPreserveObjectIndices
+ && formats.format(oldFormat).objectIndex() != -1) {
+ QTextCharFormat f = newFormat;
+ f.setObjectIndex(formats.format(oldFormat).objectIndex());
+ fragment->format = formats.indexForFormat(f);
+ } else {
+ fragment->format = newFormatIdx;
+ }
+
+ QTextUndoCommand c = { QTextUndoCommand::CharFormatChanged, true, QTextUndoCommand::MoveCursor, oldFormat,
+ 0, pos, { length }, 0 };
+ appendUndoItem(c);
+
+ pos += length;
+ Q_ASSERT(pos == (int)(it.position() + fragment->size_array[0]) || pos >= endPos);
+ }
+
+ int n = fragments.findNode(startPos - 1);
+ if (n)
+ unite(n);
+
+ n = fragments.findNode(endPos);
+ if (n)
+ unite(n);
+
+ QTextBlock blockIt = blocksFind(startPos);
+ QTextBlock endIt = blocksFind(endPos);
+ if (endIt.isValid())
+ endIt = endIt.next();
+ for (; blockIt.isValid() && blockIt != endIt; blockIt = blockIt.next())
+ QTextDocumentPrivate::block(blockIt)->invalidate();
+
+ documentChange(startPos, length);
+
+ endEditBlock();
+}
+
+void QTextDocumentPrivate::setBlockFormat(const QTextBlock &from, const QTextBlock &to,
+ const QTextBlockFormat &newFormat, FormatChangeMode mode)
+{
+ beginEditBlock();
+
+ Q_ASSERT(mode != SetFormatAndPreserveObjectIndices); // only implemented for setCharFormat
+
+ Q_ASSERT(newFormat.isValid());
+
+ int newFormatIdx = -1;
+ if (mode == SetFormat)
+ newFormatIdx = formats.indexForFormat(newFormat);
+ QTextBlockGroup *group = qobject_cast<QTextBlockGroup *>(objectForFormat(newFormat));
+
+ QTextBlock it = from;
+ QTextBlock end = to;
+ if (end.isValid())
+ end = end.next();
+
+ for (; it != end; it = it.next()) {
+ int oldFormat = block(it)->format;
+ QTextBlockFormat format = formats.blockFormat(oldFormat);
+ QTextBlockGroup *oldGroup = qobject_cast<QTextBlockGroup *>(objectForFormat(format));
+ if (mode == MergeFormat) {
+ format.merge(newFormat);
+ newFormatIdx = formats.indexForFormat(format);
+ group = qobject_cast<QTextBlockGroup *>(objectForFormat(format));
+ }
+ block(it)->format = newFormatIdx;
+
+ block(it)->invalidate();
+
+ QTextUndoCommand c = { QTextUndoCommand::BlockFormatChanged, true, QTextUndoCommand::MoveCursor, oldFormat,
+ 0, it.position(), { 1 }, 0 };
+ appendUndoItem(c);
+
+ if (group != oldGroup) {
+ if (oldGroup)
+ oldGroup->blockRemoved(it);
+ if (group)
+ group->blockInserted(it);
+ } else if (group) {
+ group->blockFormatChanged(it);
+ }
+ }
+
+ documentChange(from.position(), to.position() + to.length() - from.position());
+
+ endEditBlock();
+}
+
+
+bool QTextDocumentPrivate::split(int pos)
+{
+ uint x = fragments.findNode(pos);
+ if (x) {
+ int k = fragments.position(x);
+// qDebug("found fragment with key %d, size_left=%d, size=%d to split at %d",
+// k, (*it)->size_left[0], (*it)->size_array[0], pos);
+ if (k != pos) {
+ Q_ASSERT(k <= pos);
+ // need to resize the first fragment and add a new one
+ QTextFragmentData *X = fragments.fragment(x);
+ int oldsize = X->size_array[0];
+ fragments.setSize(x, pos-k);
+ uint n = fragments.insert_single(pos, oldsize-(pos-k));
+ X = fragments.fragment(x);
+ QTextFragmentData *N = fragments.fragment(n);
+ N->stringPosition = X->stringPosition + pos-k;
+ N->format = X->format;
+ return true;
+ }
+ }
+ return false;
+}
+
+bool QTextDocumentPrivate::unite(uint f)
+{
+ uint n = fragments.next(f);
+ if (!n)
+ return false;
+
+ QTextFragmentData *ff = fragments.fragment(f);
+ QTextFragmentData *nf = fragments.fragment(n);
+
+ if (nf->format == ff->format && (ff->stringPosition + (int)ff->size_array[0] == nf->stringPosition)) {
+ if (isValidBlockSeparator(text.at(ff->stringPosition))
+ || isValidBlockSeparator(text.at(nf->stringPosition)))
+ return false;
+
+ fragments.setSize(f, ff->size_array[0] + nf->size_array[0]);
+ fragments.erase_single(n);
+ return true;
+ }
+ return false;
+}
+
+
+int QTextDocumentPrivate::undoRedo(bool undo)
+{
+ PMDEBUG("%s, undoState=%d, undoStack size=%d", undo ? "undo:" : "redo:", undoState, undoStack.size());
+ if (!undoEnabled || (undo && undoState == 0) || (!undo && undoState == undoStack.size()))
+ return -1;
+
+ undoEnabled = false;
+ beginEditBlock();
+ while (1) {
+ if (undo)
+ --undoState;
+ QTextUndoCommand &c = undoStack[undoState];
+ int resetBlockRevision = c.pos;
+
+ switch(c.command) {
+ case QTextUndoCommand::Inserted:
+ remove(c.pos, c.length, (QTextUndoCommand::Operation)c.operation);
+ PMDEBUG(" erase: from %d, length %d", c.pos, c.length);
+ c.command = QTextUndoCommand::Removed;
+ break;
+ case QTextUndoCommand::Removed:
+ PMDEBUG(" insert: format %d (from %d, length %d, strpos=%d)", c.format, c.pos, c.length, c.strPos);
+ insert_string(c.pos, c.strPos, c.length, c.format, (QTextUndoCommand::Operation)c.operation);
+ c.command = QTextUndoCommand::Inserted;
+ break;
+ case QTextUndoCommand::BlockInserted:
+ case QTextUndoCommand::BlockAdded:
+ remove_block(c.pos, &c.blockFormat, c.command, (QTextUndoCommand::Operation)c.operation);
+ PMDEBUG(" blockremove: from %d", c.pos);
+ if (c.command == QTextUndoCommand::BlockInserted)
+ c.command = QTextUndoCommand::BlockRemoved;
+ else
+ c.command = QTextUndoCommand::BlockDeleted;
+ break;
+ case QTextUndoCommand::BlockRemoved:
+ case QTextUndoCommand::BlockDeleted:
+ PMDEBUG(" blockinsert: charformat %d blockformat %d (pos %d, strpos=%d)", c.format, c.blockFormat, c.pos, c.strPos);
+ insert_block(c.pos, c.strPos, c.format, c.blockFormat, (QTextUndoCommand::Operation)c.operation, c.command);
+ resetBlockRevision += 1;
+ if (c.command == QTextUndoCommand::BlockRemoved)
+ c.command = QTextUndoCommand::BlockInserted;
+ else
+ c.command = QTextUndoCommand::BlockAdded;
+ break;
+ case QTextUndoCommand::CharFormatChanged: {
+ resetBlockRevision = -1; // ## TODO
+ PMDEBUG(" charFormat: format %d (from %d, length %d)", c.format, c.pos, c.length);
+ FragmentIterator it = find(c.pos);
+ Q_ASSERT(!it.atEnd());
+
+ int oldFormat = it.value()->format;
+ setCharFormat(c.pos, c.length, formats.charFormat(c.format));
+ c.format = oldFormat;
+ break;
+ }
+ case QTextUndoCommand::BlockFormatChanged: {
+ resetBlockRevision = -1; // ## TODO
+ PMDEBUG(" blockformat: format %d pos %d", c.format, c.pos);
+ QTextBlock it = blocksFind(c.pos);
+ Q_ASSERT(it.isValid());
+
+ int oldFormat = block(it)->format;
+ block(it)->format = c.format;
+ QTextBlockGroup *oldGroup = qobject_cast<QTextBlockGroup *>(objectForFormat(formats.blockFormat(oldFormat)));
+ QTextBlockGroup *group = qobject_cast<QTextBlockGroup *>(objectForFormat(formats.blockFormat(c.format)));
+ c.format = oldFormat;
+ if (group != oldGroup) {
+ if (oldGroup)
+ oldGroup->blockRemoved(it);
+ if (group)
+ group->blockInserted(it);
+ } else if (group) {
+ group->blockFormatChanged(it);
+ }
+ documentChange(it.position(), it.length());
+ break;
+ }
+ case QTextUndoCommand::GroupFormatChange: {
+ resetBlockRevision = -1; // ## TODO
+ PMDEBUG(" group format change");
+ QTextObject *object = objectForIndex(c.objectIndex);
+ int oldFormat = formats.objectFormatIndex(c.objectIndex);
+ changeObjectFormat(object, c.format);
+ c.format = oldFormat;
+ break;
+ }
+ case QTextUndoCommand::Custom:
+ resetBlockRevision = -1; // ## TODO
+ if (undo)
+ c.custom->undo();
+ else
+ c.custom->redo();
+ break;
+ default:
+ Q_ASSERT(false);
+ }
+
+ if (resetBlockRevision >= 0) {
+ int b = blocks.findNode(resetBlockRevision);
+ QTextBlockData *B = blocks.fragment(b);
+ B->revision = c.revision;
+ }
+
+ if (undo) {
+ if (undoState == 0 || !undoStack[undoState-1].block)
+ break;
+ } else {
+ ++undoState;
+ if (undoState == undoStack.size() || !undoStack[undoState-1].block)
+ break;
+ }
+ }
+ undoEnabled = true;
+ int editPos = -1;
+ if (docChangeFrom >= 0) {
+ editPos = qMin(docChangeFrom + docChangeLength, length() - 1);
+ }
+ endEditBlock();
+ emitUndoAvailable(isUndoAvailable());
+ emitRedoAvailable(isRedoAvailable());
+ return editPos;
+}
+
+/*!
+ Appends a custom undo \a item to the undo stack.
+*/
+void QTextDocumentPrivate::appendUndoItem(QAbstractUndoItem *item)
+{
+ if (!undoEnabled) {
+ delete item;
+ return;
+ }
+
+ QTextUndoCommand c;
+ c.command = QTextUndoCommand::Custom;
+ c.block = editBlock != 0;
+ c.operation = QTextUndoCommand::MoveCursor;
+ c.format = 0;
+ c.strPos = 0;
+ c.pos = 0;
+ c.blockFormat = 0;
+
+ c.custom = item;
+ appendUndoItem(c);
+}
+
+void QTextDocumentPrivate::appendUndoItem(const QTextUndoCommand &c)
+{
+ PMDEBUG("appendUndoItem, command=%d enabled=%d", c.command, undoEnabled);
+ if (!undoEnabled)
+ return;
+ if (undoState < undoStack.size())
+ truncateUndoStack();
+
+ if (!undoStack.isEmpty() && modified) {
+ QTextUndoCommand &last = undoStack[undoState - 1];
+ if (last.tryMerge(c))
+ return;
+ }
+ if (modifiedState > undoState)
+ modifiedState = -1;
+ undoStack.append(c);
+ undoState++;
+ emitUndoAvailable(true);
+ emitRedoAvailable(false);
+}
+
+void QTextDocumentPrivate::truncateUndoStack()
+{
+ if (undoState == undoStack.size())
+ return;
+
+ for (int i = undoState; i < undoStack.size(); ++i) {
+ QTextUndoCommand c = undoStack[i];
+ if (c.command & QTextUndoCommand::Removed) {
+ // ########
+// QTextFragment *f = c.fragment_list;
+// while (f) {
+// QTextFragment *n = f->right;
+// delete f;
+// f = n;
+// }
+ } else if (c.command & QTextUndoCommand::Custom) {
+ delete c.custom;
+ }
+ }
+ undoStack.resize(undoState);
+}
+
+void QTextDocumentPrivate::emitUndoAvailable(bool available)
+{
+ if (available != wasUndoAvailable) {
+ Q_Q(QTextDocument);
+ emit q->undoAvailable(available);
+ wasUndoAvailable = available;
+ }
+}
+
+void QTextDocumentPrivate::emitRedoAvailable(bool available)
+{
+ if (available != wasRedoAvailable) {
+ Q_Q(QTextDocument);
+ emit q->redoAvailable(available);
+ wasRedoAvailable = available;
+ }
+}
+
+void QTextDocumentPrivate::enableUndoRedo(bool enable)
+{
+ if (enable && maximumBlockCount > 0)
+ return;
+
+ if (!enable) {
+ undoState = 0;
+ truncateUndoStack();
+ emitUndoAvailable(false);
+ emitRedoAvailable(false);
+ }
+ modifiedState = modified ? -1 : undoState;
+ undoEnabled = enable;
+ if (!undoEnabled)
+ compressPieceTable();
+}
+
+void QTextDocumentPrivate::joinPreviousEditBlock()
+{
+ beginEditBlock();
+
+ if (undoEnabled && undoState)
+ undoStack[undoState - 1].block = true;
+}
+
+void QTextDocumentPrivate::endEditBlock()
+{
+ Q_Q(QTextDocument);
+ if (--editBlock)
+ return;
+
+ if (undoEnabled && undoState > 0) {
+ const bool wasBlocking = undoStack[undoState - 1].block;
+ undoStack[undoState - 1].block = false;
+ if (wasBlocking)
+ emit document()->undoCommandAdded();
+ }
+
+ if (framesDirty)
+ scan_frames(docChangeFrom, docChangeOldLength, docChangeLength);
+
+ if (lout && docChangeFrom >= 0) {
+ if (!inContentsChange) {
+ inContentsChange = true;
+ emit q->contentsChange(docChangeFrom, docChangeOldLength, docChangeLength);
+ inContentsChange = false;
+ }
+ lout->documentChanged(docChangeFrom, docChangeOldLength, docChangeLength);
+ }
+
+ docChangeFrom = -1;
+
+ if (needsEnsureMaximumBlockCount) {
+ needsEnsureMaximumBlockCount = false;
+ if (ensureMaximumBlockCount()) {
+ // if ensureMaximumBlockCount() returns true
+ // it will have called endEditBlock() and
+ // compressPieceTable() itself, so we return here
+ // to prevent getting two contentsChanged emits
+ return;
+ }
+ }
+
+ while (!changedCursors.isEmpty()) {
+ QTextCursorPrivate *curs = changedCursors.takeFirst();
+ emit q->cursorPositionChanged(QTextCursor(curs));
+ }
+
+ contentsChanged();
+
+ if (blocks.numNodes() != lastBlockCount) {
+ lastBlockCount = blocks.numNodes();
+ emit q->blockCountChanged(lastBlockCount);
+ }
+
+ if (!undoEnabled && unreachableCharacterCount)
+ compressPieceTable();
+}
+
+void QTextDocumentPrivate::documentChange(int from, int length)
+{
+// qDebug("QTextDocumentPrivate::documentChange: from=%d,length=%d", from, length);
+ if (docChangeFrom < 0) {
+ docChangeFrom = from;
+ docChangeOldLength = length;
+ docChangeLength = length;
+ return;
+ }
+ int start = qMin(from, docChangeFrom);
+ int end = qMax(from + length, docChangeFrom + docChangeLength);
+ int diff = qMax(0, end - start - docChangeLength);
+ docChangeFrom = start;
+ docChangeOldLength += diff;
+ docChangeLength += diff;
+}
+
+/*
+ adjustDocumentChangesAndCursors is called whenever there is an insert or remove of characters.
+ param from is the cursor position in the document
+ param addedOrRemoved is the amount of characters added or removed. A negative number means characters are removed.
+*/
+void QTextDocumentPrivate::adjustDocumentChangesAndCursors(int from, int addedOrRemoved, QTextUndoCommand::Operation op)
+{
+ Q_Q(QTextDocument);
+ for (int i = 0; i < cursors.size(); ++i) {
+ QTextCursorPrivate *curs = cursors.at(i);
+ if (curs->adjustPosition(from, addedOrRemoved, op) == QTextCursorPrivate::CursorMoved) {
+ if (editBlock) {
+ if (!changedCursors.contains(curs))
+ changedCursors.append(curs);
+ } else {
+ emit q->cursorPositionChanged(QTextCursor(curs));
+ }
+ }
+ }
+
+// qDebug("QTextDocumentPrivate::adjustDocumentChanges: from=%d,addedOrRemoved=%d", from, addedOrRemoved);
+ if (docChangeFrom < 0) {
+ docChangeFrom = from;
+ if (addedOrRemoved > 0) {
+ docChangeOldLength = 0;
+ docChangeLength = addedOrRemoved;
+ } else {
+ docChangeOldLength = -addedOrRemoved;
+ docChangeLength = 0;
+ }
+// qDebug("adjustDocumentChanges:");
+// qDebug(" -> %d %d %d", docChangeFrom, docChangeOldLength, docChangeLength);
+ contentsChanged();
+ return;
+ }
+
+ // have to merge the new change with the already existing one.
+ int added = qMax(0, addedOrRemoved);
+ int removed = qMax(0, -addedOrRemoved);
+
+ int diff = 0;
+ if(from + removed < docChangeFrom)
+ diff = docChangeFrom - from - removed;
+ else if(from > docChangeFrom + docChangeLength)
+ diff = from - (docChangeFrom + docChangeLength);
+
+ int overlap_start = qMax(from, docChangeFrom);
+ int overlap_end = qMin(from + removed, docChangeFrom + docChangeLength);
+ int removedInside = qMax(0, overlap_end - overlap_start);
+ removed -= removedInside;
+
+// qDebug("adjustDocumentChanges: from=%d, addedOrRemoved=%d, diff=%d, removedInside=%d", from, addedOrRemoved, diff, removedInside);
+ docChangeFrom = qMin(docChangeFrom, from);
+ docChangeOldLength += removed + diff;
+ docChangeLength += added - removedInside + diff;
+// qDebug(" -> %d %d %d", docChangeFrom, docChangeOldLength, docChangeLength);
+
+ contentsChanged();
+}
+
+
+QString QTextDocumentPrivate::plainText() const
+{
+ QString result;
+ result.resize(length());
+ const QChar *text_unicode = text.unicode();
+ QChar *data = result.data();
+ for (QTextDocumentPrivate::FragmentIterator it = begin(); it != end(); ++it) {
+ const QTextFragmentData *f = *it;
+ ::memcpy(data, text_unicode + f->stringPosition, f->size_array[0] * sizeof(QChar));
+ data += f->size_array[0];
+ }
+ // remove trailing block separator
+ result.chop(1);
+ return result;
+}
+
+int QTextDocumentPrivate::blockCharFormatIndex(int node) const
+{
+ int pos = blocks.position(node);
+ if (pos == 0)
+ return initialBlockCharFormatIndex;
+
+ return fragments.find(pos - 1)->format;
+}
+
+int QTextDocumentPrivate::nextCursorPosition(int position, QTextLayout::CursorMode mode) const
+{
+ if (position == length()-1)
+ return position;
+
+ QTextBlock it = blocksFind(position);
+ int start = it.position();
+ int end = start + it.length() - 1;
+ if (position == end)
+ return end + 1;
+
+ return it.layout()->nextCursorPosition(position-start, mode) + start;
+}
+
+int QTextDocumentPrivate::previousCursorPosition(int position, QTextLayout::CursorMode mode) const
+{
+ if (position == 0)
+ return position;
+
+ QTextBlock it = blocksFind(position);
+ int start = it.position();
+ if (position == start)
+ return start - 1;
+
+ return it.layout()->previousCursorPosition(position-start, mode) + start;
+}
+
+void QTextDocumentPrivate::changeObjectFormat(QTextObject *obj, int format)
+{
+ beginEditBlock();
+ int objectIndex = obj->objectIndex();
+ int oldFormatIndex = formats.objectFormatIndex(objectIndex);
+ formats.setObjectFormatIndex(objectIndex, format);
+
+ QTextBlockGroup *b = qobject_cast<QTextBlockGroup *>(obj);
+ if (b) {
+ b->d_func()->markBlocksDirty();
+ }
+ QTextFrame *f = qobject_cast<QTextFrame *>(obj);
+ if (f)
+ documentChange(f->firstPosition(), f->lastPosition() - f->firstPosition());
+
+ QTextUndoCommand c = { QTextUndoCommand::GroupFormatChange, true, QTextUndoCommand::MoveCursor, oldFormatIndex,
+ 0, 0, { obj->d_func()->objectIndex }, 0 };
+ appendUndoItem(c);
+
+ endEditBlock();
+}
+
+static QTextFrame *findChildFrame(QTextFrame *f, int pos)
+{
+ // ##### use binary search
+ QList<QTextFrame *> children = f->childFrames();
+ for (int i = 0; i < children.size(); ++i) {
+ QTextFrame *c = children.at(i);
+ if (pos >= c->firstPosition() && pos <= c->lastPosition())
+ return c;
+ }
+ return 0;
+}
+
+QTextFrame *QTextDocumentPrivate::rootFrame() const
+{
+ if (!rtFrame) {
+ QTextFrameFormat defaultRootFrameFormat;
+ defaultRootFrameFormat.setMargin(documentMargin);
+ rtFrame = qobject_cast<QTextFrame *>(const_cast<QTextDocumentPrivate *>(this)->createObject(defaultRootFrameFormat));
+ }
+ return rtFrame;
+}
+
+QTextFrame *QTextDocumentPrivate::frameAt(int pos) const
+{
+ QTextFrame *f = rootFrame();
+
+ while (1) {
+ QTextFrame *c = findChildFrame(f, pos);
+ if (!c)
+ return f;
+ f = c;
+ }
+}
+
+void QTextDocumentPrivate::clearFrame(QTextFrame *f)
+{
+ for (int i = 0; i < f->d_func()->childFrames.count(); ++i)
+ clearFrame(f->d_func()->childFrames.at(i));
+ f->d_func()->childFrames.clear();
+ f->d_func()->parentFrame = 0;
+}
+
+void QTextDocumentPrivate::scan_frames(int pos, int charsRemoved, int charsAdded)
+{
+ // ###### optimise
+ Q_UNUSED(pos);
+ Q_UNUSED(charsRemoved);
+ Q_UNUSED(charsAdded);
+
+ QTextFrame *f = rootFrame();
+ clearFrame(f);
+
+ for (FragmentIterator it = begin(); it != end(); ++it) {
+ // QTextFormat fmt = formats.format(it->format);
+ QTextFrame *frame = qobject_cast<QTextFrame *>(objectForFormat(it->format));
+ if (!frame)
+ continue;
+
+ Q_ASSERT(it.size() == 1);
+ QChar ch = text.at(it->stringPosition);
+
+ if (ch == QTextBeginningOfFrame) {
+ if (f != frame) {
+ // f == frame happens for tables
+ Q_ASSERT(frame->d_func()->fragment_start == it.n || frame->d_func()->fragment_start == 0);
+ frame->d_func()->parentFrame = f;
+ f->d_func()->childFrames.append(frame);
+ f = frame;
+ }
+ } else if (ch == QTextEndOfFrame) {
+ Q_ASSERT(f == frame);
+ Q_ASSERT(frame->d_func()->fragment_end == it.n || frame->d_func()->fragment_end == 0);
+ f = frame->d_func()->parentFrame;
+ } else if (ch == QChar::ObjectReplacementCharacter) {
+ Q_ASSERT(f != frame);
+ Q_ASSERT(frame->d_func()->fragment_start == it.n || frame->d_func()->fragment_start == 0);
+ Q_ASSERT(frame->d_func()->fragment_end == it.n || frame->d_func()->fragment_end == 0);
+ frame->d_func()->parentFrame = f;
+ f->d_func()->childFrames.append(frame);
+ } else {
+ Q_ASSERT(false);
+ }
+ }
+ Q_ASSERT(f == rtFrame);
+ framesDirty = false;
+}
+
+void QTextDocumentPrivate::insert_frame(QTextFrame *f)
+{
+ int start = f->firstPosition();
+ int end = f->lastPosition();
+ QTextFrame *parent = frameAt(start-1);
+ Q_ASSERT(parent == frameAt(end+1));
+
+ if (start != end) {
+ // iterator over the parent and move all children contained in my frame to myself
+ for (int i = 0; i < parent->d_func()->childFrames.size(); ++i) {
+ QTextFrame *c = parent->d_func()->childFrames.at(i);
+ if (start < c->firstPosition() && end > c->lastPosition()) {
+ parent->d_func()->childFrames.removeAt(i);
+ f->d_func()->childFrames.append(c);
+ c->d_func()->parentFrame = f;
+ }
+ }
+ }
+ // insert at the correct position
+ int i = 0;
+ for (; i < parent->d_func()->childFrames.size(); ++i) {
+ QTextFrame *c = parent->d_func()->childFrames.at(i);
+ if (c->firstPosition() > end)
+ break;
+ }
+ parent->d_func()->childFrames.insert(i, f);
+ f->d_func()->parentFrame = parent;
+}
+
+QTextFrame *QTextDocumentPrivate::insertFrame(int start, int end, const QTextFrameFormat &format)
+{
+ Q_ASSERT(start >= 0 && start < length());
+ Q_ASSERT(end >= 0 && end < length());
+ Q_ASSERT(start <= end || end == -1);
+
+ if (start != end && frameAt(start) != frameAt(end))
+ return 0;
+
+ beginEditBlock();
+
+ QTextFrame *frame = qobject_cast<QTextFrame *>(createObject(format));
+ Q_ASSERT(frame);
+
+ // #### using the default block and char format below might be wrong
+ int idx = formats.indexForFormat(QTextBlockFormat());
+ QTextCharFormat cfmt;
+ cfmt.setObjectIndex(frame->objectIndex());
+ int charIdx = formats.indexForFormat(cfmt);
+
+ insertBlock(QTextBeginningOfFrame, start, idx, charIdx, QTextUndoCommand::MoveCursor);
+ insertBlock(QTextEndOfFrame, ++end, idx, charIdx, QTextUndoCommand::KeepCursor);
+
+ frame->d_func()->fragment_start = find(start).n;
+ frame->d_func()->fragment_end = find(end).n;
+
+ insert_frame(frame);
+
+ endEditBlock();
+
+ return frame;
+}
+
+void QTextDocumentPrivate::removeFrame(QTextFrame *frame)
+{
+ QTextFrame *parent = frame->d_func()->parentFrame;
+ if (!parent)
+ return;
+
+ int start = frame->firstPosition();
+ int end = frame->lastPosition();
+ Q_ASSERT(end >= start);
+
+ beginEditBlock();
+
+ // remove already removes the frames from the tree
+ remove(end, 1);
+ remove(start-1, 1);
+
+ endEditBlock();
+}
+
+QTextObject *QTextDocumentPrivate::objectForIndex(int objectIndex) const
+{
+ if (objectIndex < 0)
+ return 0;
+
+ QTextObject *object = objects.value(objectIndex, 0);
+ if (!object) {
+ QTextDocumentPrivate *that = const_cast<QTextDocumentPrivate *>(this);
+ QTextFormat fmt = formats.objectFormat(objectIndex);
+ object = that->createObject(fmt, objectIndex);
+ }
+ return object;
+}
+
+QTextObject *QTextDocumentPrivate::objectForFormat(int formatIndex) const
+{
+ int objectIndex = formats.format(formatIndex).objectIndex();
+ return objectForIndex(objectIndex);
+}
+
+QTextObject *QTextDocumentPrivate::objectForFormat(const QTextFormat &f) const
+{
+ return objectForIndex(f.objectIndex());
+}
+
+QTextObject *QTextDocumentPrivate::createObject(const QTextFormat &f, int objectIndex)
+{
+ QTextObject *obj = document()->createObject(f);
+
+ if (obj) {
+ obj->d_func()->pieceTable = this;
+ obj->d_func()->objectIndex = objectIndex == -1 ? formats.createObjectIndex(f) : objectIndex;
+ objects[obj->d_func()->objectIndex] = obj;
+ }
+
+ return obj;
+}
+
+void QTextDocumentPrivate::deleteObject(QTextObject *object)
+{
+ const int objIdx = object->d_func()->objectIndex;
+ objects.remove(objIdx);
+ delete object;
+}
+
+void QTextDocumentPrivate::contentsChanged()
+{
+ Q_Q(QTextDocument);
+ if (editBlock)
+ return;
+
+ bool m = undoEnabled ? (modifiedState != undoState) : true;
+ if (modified != m) {
+ modified = m;
+ emit q->modificationChanged(modified);
+ }
+
+ emit q->contentsChanged();
+}
+
+void QTextDocumentPrivate::compressPieceTable()
+{
+ if (undoEnabled)
+ return;
+
+ const uint garbageCollectionThreshold = 96 * 1024; // bytes
+
+ //qDebug() << "unreachable bytes:" << unreachableCharacterCount * sizeof(QChar) << " -- limit" << garbageCollectionThreshold << "text size =" << text.size() << "capacity:" << text.capacity();
+
+ bool compressTable = unreachableCharacterCount * sizeof(QChar) > garbageCollectionThreshold
+ && text.size() >= text.capacity() * 0.9;
+ if (!compressTable)
+ return;
+
+ QString newText;
+ newText.resize(text.size());
+ QChar *newTextPtr = newText.data();
+ int newLen = 0;
+
+ for (FragmentMap::Iterator it = fragments.begin(); !it.atEnd(); ++it) {
+ qMemCopy(newTextPtr, text.constData() + it->stringPosition, it->size_array[0] * sizeof(QChar));
+ it->stringPosition = newLen;
+ newTextPtr += it->size_array[0];
+ newLen += it->size_array[0];
+ }
+
+ newText.resize(newLen);
+ newText.squeeze();
+ //qDebug() << "removed" << text.size() - newText.size() << "characters";
+ text = newText;
+ unreachableCharacterCount = 0;
+}
+
+void QTextDocumentPrivate::setModified(bool m)
+{
+ Q_Q(QTextDocument);
+ if (m == modified)
+ return;
+
+ modified = m;
+ if (!modified)
+ modifiedState = undoState;
+ else
+ modifiedState = -1;
+
+ emit q->modificationChanged(modified);
+}
+
+bool QTextDocumentPrivate::ensureMaximumBlockCount()
+{
+ if (maximumBlockCount <= 0)
+ return false;
+ if (blocks.numNodes() <= maximumBlockCount)
+ return false;
+
+ beginEditBlock();
+
+ const int blocksToRemove = blocks.numNodes() - maximumBlockCount;
+ QTextCursor cursor(this, 0);
+ cursor.movePosition(QTextCursor::NextBlock, QTextCursor::KeepAnchor, blocksToRemove);
+
+ unreachableCharacterCount += cursor.selectionEnd() - cursor.selectionStart();
+
+ // preserve the char format of the paragraph that is to become the new first one
+ QTextCharFormat charFmt = cursor.blockCharFormat();
+ cursor.removeSelectedText();
+ cursor.setBlockCharFormat(charFmt);
+
+ endEditBlock();
+
+ compressPieceTable();
+
+ return true;
+}
+
+/// This method is called from QTextTable when it is about to remove a table-cell to allow cursors to update their selection.
+void QTextDocumentPrivate::aboutToRemoveCell(int from, int to)
+{
+ Q_ASSERT(from <= to);
+ for (int i = 0; i < cursors.size(); ++i)
+ cursors.at(i)->aboutToRemoveCell(from, to);
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/text/qtextdocument_p.h b/src/gui/text/qtextdocument_p.h
new file mode 100644
index 0000000000..25763e16fe
--- /dev/null
+++ b/src/gui/text/qtextdocument_p.h
@@ -0,0 +1,398 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QTEXTDOCUMENT_P_H
+#define QTEXTDOCUMENT_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 "QtCore/qglobal.h"
+#include "QtCore/qstring.h"
+#include "QtCore/qvector.h"
+#include "QtCore/qlist.h"
+#include "private/qobject_p.h"
+#include "private/qfragmentmap_p.h"
+#include "QtGui/qtextlayout.h"
+#include "QtGui/qtextoption.h"
+#include "private/qtextformat_p.h"
+#include "QtGui/qtextdocument.h"
+#include "QtGui/qtextobject.h"
+#include "QtCore/qmap.h"
+#include "QtCore/qvariant.h"
+#include "QtCore/qurl.h"
+#include "private/qcssparser_p.h"
+
+// #define QT_QMAP_DEBUG
+
+#ifdef QT_QMAP_DEBUG
+#include <iostream>
+#endif
+
+QT_BEGIN_NAMESPACE
+
+class QTextFormatCollection;
+class QTextFormat;
+class QTextBlockFormat;
+class QTextCursorPrivate;
+class QAbstractTextDocumentLayout;
+class QTextDocument;
+class QTextFrame;
+
+#define QTextBeginningOfFrame QChar(0xfdd0)
+#define QTextEndOfFrame QChar(0xfdd1)
+
+class QTextFragmentData : public QFragment<>
+{
+public:
+ inline void initialize() {}
+ inline void invalidate() const {}
+ inline void free() {}
+ int stringPosition;
+ int format;
+};
+
+class QTextBlockData : public QFragment<3>
+{
+public:
+ inline void initialize()
+ { layout = 0; userData = 0; userState = -1; revision = 0; hidden = 0; }
+ void invalidate() const;
+ inline void free()
+ { delete layout; layout = 0; delete userData; userData = 0; }
+
+ mutable int format;
+ // ##### probably store a QTextEngine * here!
+ mutable QTextLayout *layout;
+ mutable QTextBlockUserData *userData;
+ mutable int userState;
+ mutable int revision : 31;
+ mutable uint hidden : 1;
+};
+
+
+class QAbstractUndoItem;
+
+class QTextUndoCommand
+{
+public:
+ enum Command {
+ Inserted = 0,
+ Removed = 1,
+ CharFormatChanged = 2,
+ BlockFormatChanged = 3,
+ BlockInserted = 4,
+ BlockRemoved = 5,
+ BlockAdded = 6,
+ BlockDeleted = 7,
+ GroupFormatChange = 8,
+ Custom = 256
+ };
+ enum Operation {
+ KeepCursor = 0,
+ MoveCursor = 1
+ };
+ quint16 command;
+ quint8 block; ///< All undo commands that have this set to zero/false are combined with the preceding command on undo/redo.
+ quint8 operation;
+ int format;
+ quint32 strPos;
+ quint32 pos;
+ union {
+ int blockFormat;
+ quint32 length;
+ QAbstractUndoItem *custom;
+ int objectIndex;
+ };
+ quint32 revision;
+
+ bool tryMerge(const QTextUndoCommand &other);
+};
+Q_DECLARE_TYPEINFO(QTextUndoCommand, Q_PRIMITIVE_TYPE);
+
+class Q_AUTOTEST_EXPORT QTextDocumentPrivate : public QObjectPrivate
+{
+ Q_DECLARE_PUBLIC(QTextDocument)
+public:
+ typedef QFragmentMap<QTextFragmentData> FragmentMap;
+ typedef FragmentMap::ConstIterator FragmentIterator;
+ typedef QFragmentMap<QTextBlockData> BlockMap;
+
+ QTextDocumentPrivate();
+ ~QTextDocumentPrivate();
+
+ void init();
+ void clear();
+
+ void setLayout(QAbstractTextDocumentLayout *layout);
+
+ void insert(int pos, const QString &text, int format);
+ void insert(int pos, int strPos, int strLength, int format);
+ int insertBlock(int pos, int blockFormat, int charFormat, QTextUndoCommand::Operation = QTextUndoCommand::MoveCursor);
+ int insertBlock(const QChar &blockSeparator, int pos, int blockFormat, int charFormat,
+ QTextUndoCommand::Operation op = QTextUndoCommand::MoveCursor);
+
+ void move(int from, int to, int length, QTextUndoCommand::Operation = QTextUndoCommand::MoveCursor);
+ void remove(int pos, int length, QTextUndoCommand::Operation = QTextUndoCommand::MoveCursor);
+
+ void aboutToRemoveCell(int cursorFrom, int cursorEnd);
+
+ QTextFrame *insertFrame(int start, int end, const QTextFrameFormat &format);
+ void removeFrame(QTextFrame *frame);
+
+ enum FormatChangeMode { MergeFormat, SetFormat, SetFormatAndPreserveObjectIndices };
+
+ void setCharFormat(int pos, int length, const QTextCharFormat &newFormat, FormatChangeMode mode = SetFormat);
+ void setBlockFormat(const QTextBlock &from, const QTextBlock &to,
+ const QTextBlockFormat &newFormat, FormatChangeMode mode = SetFormat);
+
+ void emitUndoAvailable(bool available);
+ void emitRedoAvailable(bool available);
+
+ int undoRedo(bool undo);
+ inline void undo() { undoRedo(true); }
+ inline void redo() { undoRedo(false); }
+ void appendUndoItem(QAbstractUndoItem *);
+ inline void beginEditBlock() { editBlock++; }
+ void joinPreviousEditBlock();
+ void endEditBlock();
+ inline bool isInEditBlock() const { return editBlock; }
+ void enableUndoRedo(bool enable);
+ inline bool isUndoRedoEnabled() const { return undoEnabled; }
+
+ inline bool isUndoAvailable() const { return undoEnabled && undoState > 0; }
+ inline bool isRedoAvailable() const { return undoEnabled && undoState < undoStack.size(); }
+
+ inline QString buffer() const { return text; }
+ QString plainText() const;
+ inline int length() const { return fragments.length(); }
+
+ inline QTextFormatCollection *formatCollection() { return &formats; }
+ inline const QTextFormatCollection *formatCollection() const { return &formats; }
+ inline QAbstractTextDocumentLayout *layout() const { return lout; }
+
+ inline FragmentIterator find(int pos) const { return fragments.find(pos); }
+ inline FragmentIterator begin() const { return fragments.begin(); }
+ inline FragmentIterator end() const { return fragments.end(); }
+
+ inline QTextBlock blocksBegin() const { return QTextBlock(const_cast<QTextDocumentPrivate *>(this), blocks.firstNode()); }
+ inline QTextBlock blocksEnd() const { return QTextBlock(const_cast<QTextDocumentPrivate *>(this), 0); }
+ inline QTextBlock blocksFind(int pos) const { return QTextBlock(const_cast<QTextDocumentPrivate *>(this), blocks.findNode(pos)); }
+ int blockCharFormatIndex(int node) const;
+
+ inline int numBlocks() const { return blocks.numNodes(); }
+
+ const BlockMap &blockMap() const { return blocks; }
+ const FragmentMap &fragmentMap() const { return fragments; }
+ BlockMap &blockMap() { return blocks; }
+ FragmentMap &fragmentMap() { return fragments; }
+
+ static const QTextBlockData *block(const QTextBlock &it) { return it.p->blocks.fragment(it.n); }
+
+ int nextCursorPosition(int position, QTextLayout::CursorMode mode) const;
+ int previousCursorPosition(int position, QTextLayout::CursorMode mode) const;
+
+ void changeObjectFormat(QTextObject *group, int format);
+
+ void setModified(bool m);
+ inline bool isModified() const { return modified; }
+
+ inline QFont defaultFont() const { return formats.defaultFont(); }
+ inline void setDefaultFont(const QFont &f) { formats.setDefaultFont(f); }
+
+private:
+ bool split(int pos);
+ bool unite(uint f);
+ void truncateUndoStack();
+
+ void insert_string(int pos, uint strPos, uint length, int format, QTextUndoCommand::Operation op);
+ int insert_block(int pos, uint strPos, int format, int blockformat, QTextUndoCommand::Operation op, int command);
+ int remove_string(int pos, uint length, QTextUndoCommand::Operation op);
+ int remove_block(int pos, int *blockformat, int command, QTextUndoCommand::Operation op);
+
+ void insert_frame(QTextFrame *f);
+ void scan_frames(int pos, int charsRemoved, int charsAdded);
+ static void clearFrame(QTextFrame *f);
+
+ void adjustDocumentChangesAndCursors(int from, int addedOrRemoved, QTextUndoCommand::Operation op);
+
+ bool wasUndoAvailable;
+ bool wasRedoAvailable;
+
+public:
+ void documentChange(int from, int length);
+
+ inline void addCursor(QTextCursorPrivate *c) { cursors.append(c); }
+ inline void removeCursor(QTextCursorPrivate *c) { cursors.removeAll(c); changedCursors.removeAll(c); }
+
+ QTextFrame *frameAt(int pos) const;
+ QTextFrame *rootFrame() const;
+
+ QTextObject *objectForIndex(int objectIndex) const;
+ QTextObject *objectForFormat(int formatIndex) const;
+ QTextObject *objectForFormat(const QTextFormat &f) const;
+
+ QTextObject *createObject(const QTextFormat &newFormat, int objectIndex = -1);
+ void deleteObject(QTextObject *object);
+
+ QTextDocument *document() { return q_func(); }
+ const QTextDocument *document() const { return q_func(); }
+
+ bool ensureMaximumBlockCount();
+
+private:
+ QTextDocumentPrivate(const QTextDocumentPrivate& m);
+ QTextDocumentPrivate& operator= (const QTextDocumentPrivate& m);
+
+ void appendUndoItem(const QTextUndoCommand &c);
+
+ void contentsChanged();
+
+ void compressPieceTable();
+
+ QString text;
+ uint unreachableCharacterCount;
+
+ QVector<QTextUndoCommand> undoStack;
+ bool undoEnabled;
+ int undoState;
+ // position in undo stack of the last setModified(false) call
+ int modifiedState;
+ bool modified;
+
+ int editBlock;
+ int docChangeFrom;
+ int docChangeOldLength;
+ int docChangeLength;
+ bool framesDirty;
+
+ QTextFormatCollection formats;
+ mutable QTextFrame *rtFrame;
+ QAbstractTextDocumentLayout *lout;
+ FragmentMap fragments;
+ BlockMap blocks;
+ int initialBlockCharFormatIndex;
+
+ QList<QTextCursorPrivate*> cursors;
+ QList<QTextCursorPrivate*> changedCursors;
+ QMap<int, QTextObject *> objects;
+ QMap<QUrl, QVariant> resources;
+ QMap<QUrl, QVariant> cachedResources;
+ QString defaultStyleSheet;
+
+ int lastBlockCount;
+
+public:
+ QTextOption defaultTextOption;
+#ifndef QT_NO_CSSPARSER
+ QCss::StyleSheet parsedDefaultStyleSheet;
+#endif
+ int maximumBlockCount;
+ bool needsEnsureMaximumBlockCount;
+ bool inContentsChange;
+ QSizeF pageSize;
+ QString title;
+ QString url;
+ qreal indentWidth;
+ qreal documentMargin;
+
+ void mergeCachedResources(const QTextDocumentPrivate *priv);
+
+ friend class QTextHtmlExporter;
+ friend class QTextCursor;
+};
+
+class QTextTable;
+class QTextHtmlExporter
+{
+public:
+ QTextHtmlExporter(const QTextDocument *_doc);
+
+ enum ExportMode {
+ ExportEntireDocument,
+ ExportFragment
+ };
+
+ QString toHtml(const QByteArray &encoding, ExportMode mode = ExportEntireDocument);
+
+private:
+ enum StyleMode { EmitStyleTag, OmitStyleTag };
+ enum FrameType { TextFrame, TableFrame, RootFrame };
+
+ void emitFrame(QTextFrame::Iterator frameIt);
+ void emitTextFrame(const QTextFrame *frame);
+ void emitBlock(const QTextBlock &block);
+ void emitTable(const QTextTable *table);
+ void emitFragment(const QTextFragment &fragment);
+
+ void emitBlockAttributes(const QTextBlock &block);
+ bool emitCharFormatStyle(const QTextCharFormat &format);
+ void emitTextLength(const char *attribute, const QTextLength &length);
+ void emitAlignment(Qt::Alignment alignment);
+ void emitFloatStyle(QTextFrameFormat::Position pos, StyleMode mode = EmitStyleTag);
+ void emitMargins(const QString &top, const QString &bottom, const QString &left, const QString &right);
+ void emitAttribute(const char *attribute, const QString &value);
+ void emitFrameStyle(const QTextFrameFormat &format, FrameType frameType);
+ void emitBorderStyle(QTextFrameFormat::BorderStyle style);
+ void emitPageBreakPolicy(QTextFormat::PageBreakFlags policy);
+
+ void emitFontFamily(const QString &family);
+
+ void emitBackgroundAttribute(const QTextFormat &format);
+ QString findUrlForImage(const QTextDocument *doc, qint64 cacheKey, bool isPixmap);
+
+ QString html;
+ QTextCharFormat defaultCharFormat;
+ const QTextDocument *doc;
+ bool fragmentMarkers;
+};
+
+QT_END_NAMESPACE
+
+#endif // QTEXTDOCUMENT_P_H
diff --git a/src/gui/text/qtextdocumentfragment.cpp b/src/gui/text/qtextdocumentfragment.cpp
new file mode 100644
index 0000000000..21958a67de
--- /dev/null
+++ b/src/gui/text/qtextdocumentfragment.cpp
@@ -0,0 +1,1217 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qtextdocumentfragment.h"
+#include "qtextdocumentfragment_p.h"
+#include "qtextcursor_p.h"
+#include "qtextlist.h"
+#include "private/qunicodetables_p.h"
+
+#include <qdebug.h>
+#include <qtextcodec.h>
+#include <qbytearray.h>
+#include <qdatastream.h>
+#include <qdatetime.h>
+
+QT_BEGIN_NAMESPACE
+
+QTextCopyHelper::QTextCopyHelper(const QTextCursor &_source, const QTextCursor &_destination, bool forceCharFormat, const QTextCharFormat &fmt)
+ : formatCollection(*_destination.d->priv->formatCollection()), originalText(_source.d->priv->buffer())
+{
+ src = _source.d->priv;
+ dst = _destination.d->priv;
+ insertPos = _destination.position();
+ this->forceCharFormat = forceCharFormat;
+ primaryCharFormatIndex = convertFormatIndex(fmt);
+ cursor = _source;
+}
+
+int QTextCopyHelper::convertFormatIndex(const QTextFormat &oldFormat, int objectIndexToSet)
+{
+ QTextFormat fmt = oldFormat;
+ if (objectIndexToSet != -1) {
+ fmt.setObjectIndex(objectIndexToSet);
+ } else if (fmt.objectIndex() != -1) {
+ int newObjectIndex = objectIndexMap.value(fmt.objectIndex(), -1);
+ if (newObjectIndex == -1) {
+ QTextFormat objFormat = src->formatCollection()->objectFormat(fmt.objectIndex());
+ Q_ASSERT(objFormat.objectIndex() == -1);
+ newObjectIndex = formatCollection.createObjectIndex(objFormat);
+ objectIndexMap.insert(fmt.objectIndex(), newObjectIndex);
+ }
+ fmt.setObjectIndex(newObjectIndex);
+ }
+ int idx = formatCollection.indexForFormat(fmt);
+ Q_ASSERT(formatCollection.format(idx).type() == oldFormat.type());
+ return idx;
+}
+
+int QTextCopyHelper::appendFragment(int pos, int endPos, int objectIndex)
+{
+ QTextDocumentPrivate::FragmentIterator fragIt = src->find(pos);
+ const QTextFragmentData * const frag = fragIt.value();
+
+ Q_ASSERT(objectIndex == -1
+ || (frag->size_array[0] == 1 && src->formatCollection()->format(frag->format).objectIndex() != -1));
+
+ int charFormatIndex;
+ if (forceCharFormat)
+ charFormatIndex = primaryCharFormatIndex;
+ else
+ charFormatIndex = convertFormatIndex(frag->format, objectIndex);
+
+ const int inFragmentOffset = qMax(0, pos - fragIt.position());
+ int charsToCopy = qMin(int(frag->size_array[0] - inFragmentOffset), endPos - pos);
+
+ QTextBlock nextBlock = src->blocksFind(pos + 1);
+
+ int blockIdx = -2;
+ if (nextBlock.position() == pos + 1) {
+ blockIdx = convertFormatIndex(nextBlock.blockFormat());
+ } else if (pos == 0 && insertPos == 0) {
+ dst->setBlockFormat(dst->blocksBegin(), dst->blocksBegin(), convertFormat(src->blocksBegin().blockFormat()).toBlockFormat());
+ dst->setCharFormat(-1, 1, convertFormat(src->blocksBegin().charFormat()).toCharFormat());
+ }
+
+ QString txtToInsert(originalText.constData() + frag->stringPosition + inFragmentOffset, charsToCopy);
+ if (txtToInsert.length() == 1
+ && (txtToInsert.at(0) == QChar::ParagraphSeparator
+ || txtToInsert.at(0) == QTextBeginningOfFrame
+ || txtToInsert.at(0) == QTextEndOfFrame
+ )
+ ) {
+ dst->insertBlock(txtToInsert.at(0), insertPos, blockIdx, charFormatIndex);
+ ++insertPos;
+ } else {
+ if (nextBlock.textList()) {
+ QTextBlock dstBlock = dst->blocksFind(insertPos);
+ if (!dstBlock.textList()) {
+ // insert a new text block with the block and char format from the
+ // source block to make sure that the following text fragments
+ // end up in a list as they should
+ int listBlockFormatIndex = convertFormatIndex(nextBlock.blockFormat());
+ int listCharFormatIndex = convertFormatIndex(nextBlock.charFormat());
+ dst->insertBlock(insertPos, listBlockFormatIndex, listCharFormatIndex);
+ ++insertPos;
+ }
+ }
+ dst->insert(insertPos, txtToInsert, charFormatIndex);
+ const int userState = nextBlock.userState();
+ if (userState != -1)
+ dst->blocksFind(insertPos).setUserState(userState);
+ insertPos += txtToInsert.length();
+ }
+
+ return charsToCopy;
+}
+
+void QTextCopyHelper::appendFragments(int pos, int endPos)
+{
+ Q_ASSERT(pos < endPos);
+
+ while (pos < endPos)
+ pos += appendFragment(pos, endPos);
+}
+
+void QTextCopyHelper::copy()
+{
+ if (cursor.hasComplexSelection()) {
+ QTextTable *table = cursor.currentTable();
+ int row_start, col_start, num_rows, num_cols;
+ cursor.selectedTableCells(&row_start, &num_rows, &col_start, &num_cols);
+
+ QTextTableFormat tableFormat = table->format();
+ tableFormat.setColumns(num_cols);
+ tableFormat.clearColumnWidthConstraints();
+ const int objectIndex = dst->formatCollection()->createObjectIndex(tableFormat);
+
+ Q_ASSERT(row_start != -1);
+ for (int r = row_start; r < row_start + num_rows; ++r) {
+ for (int c = col_start; c < col_start + num_cols; ++c) {
+ QTextTableCell cell = table->cellAt(r, c);
+ const int rspan = cell.rowSpan();
+ const int cspan = cell.columnSpan();
+ if (rspan != 1) {
+ int cr = cell.row();
+ if (cr != r)
+ continue;
+ }
+ if (cspan != 1) {
+ int cc = cell.column();
+ if (cc != c)
+ continue;
+ }
+
+ // add the QTextBeginningOfFrame
+ QTextCharFormat cellFormat = cell.format();
+ if (r + rspan >= row_start + num_rows) {
+ cellFormat.setTableCellRowSpan(row_start + num_rows - r);
+ }
+ if (c + cspan >= col_start + num_cols) {
+ cellFormat.setTableCellColumnSpan(col_start + num_cols - c);
+ }
+ const int charFormatIndex = convertFormatIndex(cellFormat, objectIndex);
+
+ int blockIdx = -2;
+ const int cellPos = cell.firstPosition();
+ QTextBlock block = src->blocksFind(cellPos);
+ if (block.position() == cellPos) {
+ blockIdx = convertFormatIndex(block.blockFormat());
+ }
+
+ dst->insertBlock(QTextBeginningOfFrame, insertPos, blockIdx, charFormatIndex);
+ ++insertPos;
+
+ // nothing to add for empty cells
+ if (cell.lastPosition() > cellPos) {
+ // add the contents
+ appendFragments(cellPos, cell.lastPosition());
+ }
+ }
+ }
+
+ // add end of table
+ int end = table->lastPosition();
+ appendFragment(end, end+1, objectIndex);
+ } else {
+ appendFragments(cursor.selectionStart(), cursor.selectionEnd());
+ }
+}
+
+QTextDocumentFragmentPrivate::QTextDocumentFragmentPrivate(const QTextCursor &_cursor)
+ : ref(1), doc(new QTextDocument), importedFromPlainText(false)
+{
+ doc->setUndoRedoEnabled(false);
+
+ if (!_cursor.hasSelection())
+ return;
+
+ doc->docHandle()->beginEditBlock();
+ QTextCursor destCursor(doc);
+ QTextCopyHelper(_cursor, destCursor).copy();
+ doc->docHandle()->endEditBlock();
+
+ if (_cursor.d)
+ doc->docHandle()->mergeCachedResources(_cursor.d->priv);
+}
+
+void QTextDocumentFragmentPrivate::insert(QTextCursor &_cursor) const
+{
+ if (_cursor.isNull())
+ return;
+
+ QTextDocumentPrivate *destPieceTable = _cursor.d->priv;
+ destPieceTable->beginEditBlock();
+
+ QTextCursor sourceCursor(doc);
+ sourceCursor.movePosition(QTextCursor::End, QTextCursor::KeepAnchor);
+ QTextCopyHelper(sourceCursor, _cursor, importedFromPlainText, _cursor.charFormat()).copy();
+
+ destPieceTable->endEditBlock();
+}
+
+/*!
+ \class QTextDocumentFragment
+ \reentrant
+
+ \brief The QTextDocumentFragment class represents a piece of formatted text
+ from a QTextDocument.
+
+ \ingroup text
+ \ingroup shared
+
+ A QTextDocumentFragment is a fragment of rich text, that can be inserted into
+ a QTextDocument. A document fragment can be created from a
+ QTextDocument, from a QTextCursor's selection, or from another
+ document fragment. Document fragments can also be created by the
+ static functions, fromPlainText() and fromHtml().
+
+ The contents of a document fragment can be obtained as plain text
+ by using the toPlainText() function, or it can be obtained as HTML
+ with toHtml().
+*/
+
+
+/*!
+ Constructs an empty QTextDocumentFragment.
+
+ \sa isEmpty()
+*/
+QTextDocumentFragment::QTextDocumentFragment()
+ : d(0)
+{
+}
+
+/*!
+ Converts the given \a document into a QTextDocumentFragment.
+ Note that the QTextDocumentFragment only stores the document contents, not meta information
+ like the document's title.
+*/
+QTextDocumentFragment::QTextDocumentFragment(const QTextDocument *document)
+ : d(0)
+{
+ if (!document)
+ return;
+
+ QTextCursor cursor(const_cast<QTextDocument *>(document));
+ cursor.movePosition(QTextCursor::End, QTextCursor::KeepAnchor);
+ d = new QTextDocumentFragmentPrivate(cursor);
+}
+
+/*!
+ Creates a QTextDocumentFragment from the \a{cursor}'s selection.
+ If the cursor doesn't have a selection, the created fragment is empty.
+
+ \sa isEmpty() QTextCursor::selection()
+*/
+QTextDocumentFragment::QTextDocumentFragment(const QTextCursor &cursor)
+ : d(0)
+{
+ if (!cursor.hasSelection())
+ return;
+
+ d = new QTextDocumentFragmentPrivate(cursor);
+}
+
+/*!
+ \fn QTextDocumentFragment::QTextDocumentFragment(const QTextDocumentFragment &other)
+
+ Copy constructor. Creates a copy of the \a other fragment.
+*/
+QTextDocumentFragment::QTextDocumentFragment(const QTextDocumentFragment &rhs)
+ : d(rhs.d)
+{
+ if (d)
+ d->ref.ref();
+}
+
+/*!
+ \fn QTextDocumentFragment &QTextDocumentFragment::operator=(const QTextDocumentFragment &other)
+
+ Assigns the \a other fragment to this fragment.
+*/
+QTextDocumentFragment &QTextDocumentFragment::operator=(const QTextDocumentFragment &rhs)
+{
+ if (rhs.d)
+ rhs.d->ref.ref();
+ if (d && !d->ref.deref())
+ delete d;
+ d = rhs.d;
+ return *this;
+}
+
+/*!
+ Destroys the document fragment.
+*/
+QTextDocumentFragment::~QTextDocumentFragment()
+{
+ if (d && !d->ref.deref())
+ delete d;
+}
+
+/*!
+ Returns true if the fragment is empty; otherwise returns false.
+*/
+bool QTextDocumentFragment::isEmpty() const
+{
+ return !d || !d->doc || d->doc->docHandle()->length() <= 1;
+}
+
+/*!
+ Returns the document fragment's text as plain text (i.e. with no
+ formatting information).
+
+ \sa toHtml()
+*/
+QString QTextDocumentFragment::toPlainText() const
+{
+ if (!d)
+ return QString();
+
+ return d->doc->toPlainText();
+}
+
+// #### Qt 5: merge with other overload
+/*!
+ \overload
+*/
+
+#ifndef QT_NO_TEXTHTMLPARSER
+
+QString QTextDocumentFragment::toHtml() const
+{
+ return toHtml(QByteArray());
+}
+
+/*!
+ \since 4.2
+
+ Returns the contents of the document fragment as HTML,
+ using the specified \a encoding (e.g., "UTF-8", "ISO 8859-1").
+
+ \sa toPlainText(), QTextDocument::toHtml(), QTextCodec
+*/
+QString QTextDocumentFragment::toHtml(const QByteArray &encoding) const
+{
+ if (!d)
+ return QString();
+
+ return QTextHtmlExporter(d->doc).toHtml(encoding, QTextHtmlExporter::ExportFragment);
+}
+
+#endif // QT_NO_TEXTHTMLPARSER
+
+/*!
+ Returns a document fragment that contains the given \a plainText.
+
+ When inserting such a fragment into a QTextDocument the current char format of
+ the QTextCursor used for insertion is used as format for the text.
+*/
+QTextDocumentFragment QTextDocumentFragment::fromPlainText(const QString &plainText)
+{
+ QTextDocumentFragment res;
+
+ res.d = new QTextDocumentFragmentPrivate;
+ res.d->importedFromPlainText = true;
+ QTextCursor cursor(res.d->doc);
+ cursor.insertText(plainText);
+ return res;
+}
+
+static QTextListFormat::Style nextListStyle(QTextListFormat::Style style)
+{
+ if (style == QTextListFormat::ListDisc)
+ return QTextListFormat::ListCircle;
+ else if (style == QTextListFormat::ListCircle)
+ return QTextListFormat::ListSquare;
+ return style;
+}
+
+#ifndef QT_NO_TEXTHTMLPARSER
+
+QTextHtmlImporter::QTextHtmlImporter(QTextDocument *_doc, const QString &_html, ImportMode mode, const QTextDocument *resourceProvider)
+ : indent(0), compressNextWhitespace(PreserveWhiteSpace), doc(_doc), importMode(mode)
+{
+ cursor = QTextCursor(doc);
+ wsm = QTextHtmlParserNode::WhiteSpaceNormal;
+
+ QString html = _html;
+ const int startFragmentPos = html.indexOf(QLatin1String("<!--StartFragment-->"));
+ if (startFragmentPos != -1) {
+ QString qt3RichTextHeader(QLatin1String("<meta name=\"qrichtext\" content=\"1\" />"));
+
+ // Hack for Qt3
+ const bool hasQtRichtextMetaTag = html.contains(qt3RichTextHeader);
+
+ const int endFragmentPos = html.indexOf(QLatin1String("<!--EndFragment-->"));
+ if (startFragmentPos < endFragmentPos)
+ html = html.mid(startFragmentPos, endFragmentPos - startFragmentPos);
+ else
+ html = html.mid(startFragmentPos);
+
+ if (hasQtRichtextMetaTag)
+ html.prepend(qt3RichTextHeader);
+ }
+
+ parse(html, resourceProvider ? resourceProvider : doc);
+// dumpHtml();
+}
+
+void QTextHtmlImporter::import()
+{
+ cursor.beginEditBlock();
+ hasBlock = true;
+ forceBlockMerging = false;
+ compressNextWhitespace = RemoveWhiteSpace;
+ blockTagClosed = false;
+ for (currentNodeIdx = 0; currentNodeIdx < count(); ++currentNodeIdx) {
+ currentNode = &at(currentNodeIdx);
+ wsm = textEditMode ? QTextHtmlParserNode::WhiteSpacePreWrap : currentNode->wsm;
+
+ /*
+ * process each node in three stages:
+ * 1) check if the hierarchy changed and we therefore passed the
+ * equivalent of a closing tag -> we may need to finish off
+ * some structures like tables
+ *
+ * 2) check if the current node is a special node like a
+ * <table>, <ul> or <img> tag that requires special processing
+ *
+ * 3) if the node should result in a QTextBlock create one and
+ * finally insert text that may be attached to the node
+ */
+
+ /* emit 'closing' table blocks or adjust current indent level
+ * if we
+ * 1) are beyond the first node
+ * 2) the current node not being a child of the previous node
+ * means there was a tag closing in the input html
+ */
+ if (currentNodeIdx > 0 && (currentNode->parent != currentNodeIdx - 1)) {
+ blockTagClosed = closeTag();
+ // visually collapse subsequent block tags, but if the element after the closed block tag
+ // is for example an inline element (!isBlock) we have to make sure we start a new paragraph by setting
+ // hasBlock to false.
+ if (blockTagClosed
+ && !currentNode->isBlock()
+ && currentNode->id != Html_unknown)
+ {
+ hasBlock = false;
+ } else if (hasBlock) {
+ // when collapsing subsequent block tags we need to clear the block format
+ QTextBlockFormat blockFormat = currentNode->blockFormat;
+ blockFormat.setIndent(indent);
+
+ QTextBlockFormat oldFormat = cursor.blockFormat();
+ if (oldFormat.hasProperty(QTextFormat::PageBreakPolicy)) {
+ QTextFormat::PageBreakFlags pageBreak = oldFormat.pageBreakPolicy();
+ if (pageBreak == QTextFormat::PageBreak_AlwaysAfter)
+ /* We remove an empty paragrah that requested a page break after.
+ moving that request to the next paragraph means we also need to make
+ that a pagebreak before to keep the same visual appearance.
+ */
+ pageBreak = QTextFormat::PageBreak_AlwaysBefore;
+ blockFormat.setPageBreakPolicy(pageBreak);
+ }
+
+ cursor.setBlockFormat(blockFormat);
+ }
+ }
+
+ if (currentNode->displayMode == QTextHtmlElement::DisplayNone) {
+ if (currentNode->id == Html_title)
+ doc->setMetaInformation(QTextDocument::DocumentTitle, currentNode->text);
+ // ignore explicitly 'invisible' elements
+ continue;
+ }
+
+ if (processSpecialNodes() == ContinueWithNextNode)
+ continue;
+
+ // make sure there's a block for 'Blah' after <ul><li>foo</ul>Blah
+ if (blockTagClosed
+ && !hasBlock
+ && !currentNode->isBlock()
+ && !currentNode->text.isEmpty() && !currentNode->hasOnlyWhitespace()
+ && currentNode->displayMode == QTextHtmlElement::DisplayInline) {
+
+ QTextBlockFormat block = currentNode->blockFormat;
+ block.setIndent(indent);
+
+ appendBlock(block, currentNode->charFormat);
+
+ hasBlock = true;
+ }
+
+ if (currentNode->isBlock()) {
+ if (processBlockNode() == ContinueWithNextNode)
+ continue;
+ }
+
+ if (currentNode->charFormat.isAnchor() && !currentNode->charFormat.anchorName().isEmpty()) {
+ namedAnchors.append(currentNode->charFormat.anchorName());
+ }
+
+ if (appendNodeText())
+ hasBlock = false; // if we actually appended text then we don't
+ // have an empty block anymore
+ }
+
+ cursor.endEditBlock();
+}
+
+bool QTextHtmlImporter::appendNodeText()
+{
+ const int initialCursorPosition = cursor.position();
+ QTextCharFormat format = currentNode->charFormat;
+
+ if(wsm == QTextHtmlParserNode::WhiteSpacePre || wsm == QTextHtmlParserNode::WhiteSpacePreWrap)
+ compressNextWhitespace = PreserveWhiteSpace;
+
+ QString text = currentNode->text;
+
+ QString textToInsert;
+ textToInsert.reserve(text.size());
+
+ for (int i = 0; i < text.length(); ++i) {
+ QChar ch = text.at(i);
+
+ if (ch.isSpace()
+ && ch != QChar::Nbsp
+ && ch != QChar::ParagraphSeparator) {
+
+ if (compressNextWhitespace == CollapseWhiteSpace)
+ compressNextWhitespace = RemoveWhiteSpace; // allow this one, and remove the ones coming next.
+ else if(compressNextWhitespace == RemoveWhiteSpace)
+ continue;
+
+ if (wsm == QTextHtmlParserNode::WhiteSpacePre
+ || textEditMode
+ ) {
+ if (ch == QLatin1Char('\n')) {
+ if (textEditMode)
+ continue;
+ } else if (ch == QLatin1Char('\r')) {
+ continue;
+ }
+ } else if (wsm != QTextHtmlParserNode::WhiteSpacePreWrap) {
+ compressNextWhitespace = RemoveWhiteSpace;
+ if (wsm == QTextHtmlParserNode::WhiteSpaceNoWrap)
+ ch = QChar::Nbsp;
+ else
+ ch = QLatin1Char(' ');
+ }
+ } else {
+ compressNextWhitespace = PreserveWhiteSpace;
+ }
+
+ if (ch == QLatin1Char('\n')
+ || ch == QChar::ParagraphSeparator) {
+
+ if (!textToInsert.isEmpty()) {
+ cursor.insertText(textToInsert, format);
+ textToInsert.clear();
+ }
+
+ QTextBlockFormat fmt = cursor.blockFormat();
+
+ if (fmt.hasProperty(QTextFormat::BlockBottomMargin)) {
+ QTextBlockFormat tmp = fmt;
+ tmp.clearProperty(QTextFormat::BlockBottomMargin);
+ cursor.setBlockFormat(tmp);
+ }
+
+ fmt.clearProperty(QTextFormat::BlockTopMargin);
+ appendBlock(fmt, cursor.charFormat());
+ } else {
+ if (!namedAnchors.isEmpty()) {
+ if (!textToInsert.isEmpty()) {
+ cursor.insertText(textToInsert, format);
+ textToInsert.clear();
+ }
+
+ format.setAnchor(true);
+ format.setAnchorNames(namedAnchors);
+ cursor.insertText(ch, format);
+ namedAnchors.clear();
+ format.clearProperty(QTextFormat::IsAnchor);
+ format.clearProperty(QTextFormat::AnchorName);
+ } else {
+ textToInsert += ch;
+ }
+ }
+ }
+
+ if (!textToInsert.isEmpty()) {
+ cursor.insertText(textToInsert, format);
+ }
+
+ return cursor.position() != initialCursorPosition;
+}
+
+QTextHtmlImporter::ProcessNodeResult QTextHtmlImporter::processSpecialNodes()
+{
+ switch (currentNode->id) {
+ case Html_body:
+ if (currentNode->charFormat.background().style() != Qt::NoBrush) {
+ QTextFrameFormat fmt = doc->rootFrame()->frameFormat();
+ fmt.setBackground(currentNode->charFormat.background());
+ doc->rootFrame()->setFrameFormat(fmt);
+ const_cast<QTextHtmlParserNode *>(currentNode)->charFormat.clearProperty(QTextFormat::BackgroundBrush);
+ }
+ compressNextWhitespace = RemoveWhiteSpace;
+ break;
+
+ case Html_ol:
+ case Html_ul: {
+ QTextListFormat::Style style = currentNode->listStyle;
+
+ if (currentNode->id == Html_ul && !currentNode->hasOwnListStyle && currentNode->parent) {
+ const QTextHtmlParserNode *n = &at(currentNode->parent);
+ while (n) {
+ if (n->id == Html_ul) {
+ style = nextListStyle(currentNode->listStyle);
+ }
+ if (n->parent)
+ n = &at(n->parent);
+ else
+ n = 0;
+ }
+ }
+
+ QTextListFormat listFmt;
+ listFmt.setStyle(style);
+
+ ++indent;
+ if (currentNode->hasCssListIndent)
+ listFmt.setIndent(currentNode->cssListIndent);
+ else
+ listFmt.setIndent(indent);
+
+ List l;
+ l.format = listFmt;
+ l.listNode = currentNodeIdx;
+ lists.append(l);
+ compressNextWhitespace = RemoveWhiteSpace;
+
+ // broken html: <ul>Text here<li>Foo
+ const QString simpl = currentNode->text.simplified();
+ if (simpl.isEmpty() || simpl.at(0).isSpace())
+ return ContinueWithNextNode;
+ break;
+ }
+
+ case Html_table: {
+ Table t = scanTable(currentNodeIdx);
+ tables.append(t);
+ hasBlock = false;
+ compressNextWhitespace = RemoveWhiteSpace;
+ return ContinueWithNextNode;
+ }
+
+ case Html_tr:
+ return ContinueWithNextNode;
+
+ case Html_img: {
+ QTextImageFormat fmt;
+ fmt.setName(currentNode->imageName);
+
+ fmt.merge(currentNode->charFormat);
+
+ if (currentNode->imageWidth != -1)
+ fmt.setWidth(currentNode->imageWidth);
+ if (currentNode->imageHeight != -1)
+ fmt.setHeight(currentNode->imageHeight);
+
+ cursor.insertImage(fmt, QTextFrameFormat::Position(currentNode->cssFloat));
+
+ cursor.movePosition(QTextCursor::Left, QTextCursor::KeepAnchor);
+ cursor.mergeCharFormat(currentNode->charFormat);
+ cursor.movePosition(QTextCursor::Right);
+ compressNextWhitespace = CollapseWhiteSpace;
+
+ hasBlock = false;
+ return ContinueWithNextNode;
+ }
+
+ case Html_hr: {
+ QTextBlockFormat blockFormat = currentNode->blockFormat;
+ blockFormat.setTopMargin(topMargin(currentNodeIdx));
+ blockFormat.setBottomMargin(bottomMargin(currentNodeIdx));
+ blockFormat.setProperty(QTextFormat::BlockTrailingHorizontalRulerWidth, currentNode->width);
+ if (hasBlock && importMode == ImportToDocument)
+ cursor.mergeBlockFormat(blockFormat);
+ else
+ appendBlock(blockFormat);
+ hasBlock = false;
+ compressNextWhitespace = RemoveWhiteSpace;
+ return ContinueWithNextNode;
+ }
+
+ default: break;
+ }
+ return ContinueWithCurrentNode;
+}
+
+// returns true if a block tag was closed
+bool QTextHtmlImporter::closeTag()
+{
+ const QTextHtmlParserNode *closedNode = &at(currentNodeIdx - 1);
+ const int endDepth = depth(currentNodeIdx) - 1;
+ int depth = this->depth(currentNodeIdx - 1);
+ bool blockTagClosed = false;
+
+ while (depth > endDepth) {
+ Table *t = 0;
+ if (!tables.isEmpty())
+ t = &tables.last();
+
+ switch (closedNode->id) {
+ case Html_tr:
+ if (t && !t->isTextFrame) {
+ ++t->currentRow;
+
+ // for broken html with rowspans but missing tr tags
+ while (!t->currentCell.atEnd() && t->currentCell.row < t->currentRow)
+ ++t->currentCell;
+ }
+
+ blockTagClosed = true;
+ break;
+
+ case Html_table:
+ if (!t)
+ break;
+ indent = t->lastIndent;
+
+ tables.resize(tables.size() - 1);
+ t = 0;
+
+ if (tables.isEmpty()) {
+ cursor = doc->rootFrame()->lastCursorPosition();
+ } else {
+ t = &tables.last();
+ if (t->isTextFrame)
+ cursor = t->frame->lastCursorPosition();
+ else if (!t->currentCell.atEnd())
+ cursor = t->currentCell.cell().lastCursorPosition();
+ }
+
+ // we don't need an extra block after tables, so we don't
+ // claim to have closed one for the creation of a new one
+ // in import()
+ blockTagClosed = false;
+ compressNextWhitespace = RemoveWhiteSpace;
+ break;
+
+ case Html_th:
+ case Html_td:
+ if (t && !t->isTextFrame)
+ ++t->currentCell;
+ blockTagClosed = true;
+ compressNextWhitespace = RemoveWhiteSpace;
+ break;
+
+ case Html_ol:
+ case Html_ul:
+ if (lists.isEmpty())
+ break;
+ lists.resize(lists.size() - 1);
+ --indent;
+ blockTagClosed = true;
+ break;
+
+ case Html_br:
+ compressNextWhitespace = RemoveWhiteSpace;
+ break;
+
+ case Html_div:
+ if (closedNode->children.isEmpty())
+ break;
+ // fall through
+ default:
+ if (closedNode->isBlock())
+ blockTagClosed = true;
+ break;
+ }
+
+ closedNode = &at(closedNode->parent);
+ --depth;
+ }
+
+ return blockTagClosed;
+}
+
+QTextHtmlImporter::Table QTextHtmlImporter::scanTable(int tableNodeIdx)
+{
+ Table table;
+ table.columns = 0;
+
+ QVector<QTextLength> columnWidths;
+
+ int tableHeaderRowCount = 0;
+ QVector<int> rowNodes;
+ rowNodes.reserve(at(tableNodeIdx).children.count());
+ foreach (int row, at(tableNodeIdx).children)
+ switch (at(row).id) {
+ case Html_tr:
+ rowNodes += row;
+ break;
+ case Html_thead:
+ case Html_tbody:
+ case Html_tfoot:
+ foreach (int potentialRow, at(row).children)
+ if (at(potentialRow).id == Html_tr) {
+ rowNodes += potentialRow;
+ if (at(row).id == Html_thead)
+ ++tableHeaderRowCount;
+ }
+ break;
+ default: break;
+ }
+
+ QVector<RowColSpanInfo> rowColSpans;
+ QVector<RowColSpanInfo> rowColSpanForColumn;
+
+ int effectiveRow = 0;
+ foreach (int row, rowNodes) {
+ int colsInRow = 0;
+
+ foreach (int cell, at(row).children)
+ if (at(cell).isTableCell()) {
+ // skip all columns with spans from previous rows
+ while (colsInRow < rowColSpanForColumn.size()) {
+ const RowColSpanInfo &spanInfo = rowColSpanForColumn[colsInRow];
+
+ if (spanInfo.row + spanInfo.rowSpan > effectiveRow) {
+ Q_ASSERT(spanInfo.col == colsInRow);
+ colsInRow += spanInfo.colSpan;
+ } else
+ break;
+ }
+
+ const QTextHtmlParserNode &c = at(cell);
+ const int currentColumn = colsInRow;
+ colsInRow += c.tableCellColSpan;
+
+ RowColSpanInfo spanInfo;
+ spanInfo.row = effectiveRow;
+ spanInfo.col = currentColumn;
+ spanInfo.colSpan = c.tableCellColSpan;
+ spanInfo.rowSpan = c.tableCellRowSpan;
+ if (spanInfo.colSpan > 1 || spanInfo.rowSpan > 1)
+ rowColSpans.append(spanInfo);
+
+ columnWidths.resize(qMax(columnWidths.count(), colsInRow));
+ rowColSpanForColumn.resize(columnWidths.size());
+ for (int i = currentColumn; i < currentColumn + c.tableCellColSpan; ++i) {
+ if (columnWidths.at(i).type() == QTextLength::VariableLength) {
+ QTextLength w = c.width;
+ if (c.tableCellColSpan > 1 && w.type() != QTextLength::VariableLength)
+ w = QTextLength(w.type(), w.value(100.) / c.tableCellColSpan);
+ columnWidths[i] = w;
+ }
+ rowColSpanForColumn[i] = spanInfo;
+ }
+ }
+
+ table.columns = qMax(table.columns, colsInRow);
+
+ ++effectiveRow;
+ }
+ table.rows = effectiveRow;
+
+ table.lastIndent = indent;
+ indent = 0;
+
+ if (table.rows == 0 || table.columns == 0)
+ return table;
+
+ QTextFrameFormat fmt;
+ const QTextHtmlParserNode &node = at(tableNodeIdx);
+
+ if (!node.isTextFrame) {
+ QTextTableFormat tableFmt;
+ tableFmt.setCellSpacing(node.tableCellSpacing);
+ tableFmt.setCellPadding(node.tableCellPadding);
+ if (node.blockFormat.hasProperty(QTextFormat::BlockAlignment))
+ tableFmt.setAlignment(node.blockFormat.alignment());
+ tableFmt.setColumns(table.columns);
+ tableFmt.setColumnWidthConstraints(columnWidths);
+ tableFmt.setHeaderRowCount(tableHeaderRowCount);
+ fmt = tableFmt;
+ }
+
+ fmt.setTopMargin(topMargin(tableNodeIdx));
+ fmt.setBottomMargin(bottomMargin(tableNodeIdx));
+ fmt.setLeftMargin(leftMargin(tableNodeIdx)
+ + table.lastIndent * 40 // ##### not a good emulation
+ );
+ fmt.setRightMargin(rightMargin(tableNodeIdx));
+
+ // compatibility
+ if (qFuzzyCompare(fmt.leftMargin(), fmt.rightMargin())
+ && qFuzzyCompare(fmt.leftMargin(), fmt.topMargin())
+ && qFuzzyCompare(fmt.leftMargin(), fmt.bottomMargin()))
+ fmt.setProperty(QTextFormat::FrameMargin, fmt.leftMargin());
+
+ fmt.setBorderStyle(node.borderStyle);
+ fmt.setBorderBrush(node.borderBrush);
+ fmt.setBorder(node.tableBorder);
+ fmt.setWidth(node.width);
+ fmt.setHeight(node.height);
+ if (node.blockFormat.hasProperty(QTextFormat::PageBreakPolicy))
+ fmt.setPageBreakPolicy(node.blockFormat.pageBreakPolicy());
+
+ if (node.blockFormat.hasProperty(QTextFormat::LayoutDirection))
+ fmt.setLayoutDirection(node.blockFormat.layoutDirection());
+ if (node.charFormat.background().style() != Qt::NoBrush)
+ fmt.setBackground(node.charFormat.background());
+ fmt.setPosition(QTextFrameFormat::Position(node.cssFloat));
+
+ if (node.isTextFrame) {
+ if (node.isRootFrame) {
+ table.frame = cursor.currentFrame();
+ table.frame->setFrameFormat(fmt);
+ } else
+ table.frame = cursor.insertFrame(fmt);
+
+ table.isTextFrame = true;
+ } else {
+ const int oldPos = cursor.position();
+ QTextTable *textTable = cursor.insertTable(table.rows, table.columns, fmt.toTableFormat());
+ table.frame = textTable;
+
+ for (int i = 0; i < rowColSpans.count(); ++i) {
+ const RowColSpanInfo &nfo = rowColSpans.at(i);
+ textTable->mergeCells(nfo.row, nfo.col, nfo.rowSpan, nfo.colSpan);
+ }
+
+ table.currentCell = TableCellIterator(textTable);
+ cursor.setPosition(oldPos); // restore for caption support which needs to be inserted right before the table
+ }
+ return table;
+}
+
+QTextHtmlImporter::ProcessNodeResult QTextHtmlImporter::processBlockNode()
+{
+ QTextBlockFormat block;
+ QTextCharFormat charFmt;
+ bool modifiedBlockFormat = true;
+ bool modifiedCharFormat = true;
+
+ if (currentNode->isTableCell() && !tables.isEmpty()) {
+ Table &t = tables.last();
+ if (!t.isTextFrame && !t.currentCell.atEnd()) {
+ QTextTableCell cell = t.currentCell.cell();
+ if (cell.isValid()) {
+ QTextTableCellFormat fmt = cell.format().toTableCellFormat();
+ if (topPadding(currentNodeIdx) >= 0)
+ fmt.setTopPadding(topPadding(currentNodeIdx));
+ if (bottomPadding(currentNodeIdx) >= 0)
+ fmt.setBottomPadding(bottomPadding(currentNodeIdx));
+ if (leftPadding(currentNodeIdx) >= 0)
+ fmt.setLeftPadding(leftPadding(currentNodeIdx));
+ if (rightPadding(currentNodeIdx) >= 0)
+ fmt.setRightPadding(rightPadding(currentNodeIdx));
+ cell.setFormat(fmt);
+
+ cursor.setPosition(cell.firstPosition());
+ }
+ }
+ hasBlock = true;
+ compressNextWhitespace = RemoveWhiteSpace;
+
+ if (currentNode->charFormat.background().style() != Qt::NoBrush) {
+ charFmt.setBackground(currentNode->charFormat.background());
+ cursor.mergeBlockCharFormat(charFmt);
+ }
+ }
+
+ if (hasBlock) {
+ block = cursor.blockFormat();
+ charFmt = cursor.blockCharFormat();
+ modifiedBlockFormat = false;
+ modifiedCharFormat = false;
+ }
+
+ // collapse
+ {
+ qreal tm = qreal(topMargin(currentNodeIdx));
+ if (tm > block.topMargin()) {
+ block.setTopMargin(tm);
+ modifiedBlockFormat = true;
+ }
+ }
+
+ int bottomMargin = this->bottomMargin(currentNodeIdx);
+
+ // for list items we may want to collapse with the bottom margin of the
+ // list.
+ const QTextHtmlParserNode *parentNode = currentNode->parent ? &at(currentNode->parent) : 0;
+ if ((currentNode->id == Html_li || currentNode->id == Html_dt || currentNode->id == Html_dd)
+ && parentNode
+ && (parentNode->isListStart() || parentNode->id == Html_dl)
+ && (parentNode->children.last() == currentNodeIdx)) {
+ bottomMargin = qMax(bottomMargin, this->bottomMargin(currentNode->parent));
+ }
+
+ if (block.bottomMargin() != bottomMargin) {
+ block.setBottomMargin(bottomMargin);
+ modifiedBlockFormat = true;
+ }
+
+ {
+ const qreal lm = leftMargin(currentNodeIdx);
+ const qreal rm = rightMargin(currentNodeIdx);
+
+ if (block.leftMargin() != lm) {
+ block.setLeftMargin(lm);
+ modifiedBlockFormat = true;
+ }
+ if (block.rightMargin() != rm) {
+ block.setRightMargin(rm);
+ modifiedBlockFormat = true;
+ }
+ }
+
+ if (currentNode->id != Html_li
+ && indent != 0
+ && (lists.isEmpty()
+ || !hasBlock
+ || !lists.last().list
+ || lists.last().list->itemNumber(cursor.block()) == -1
+ )
+ ) {
+ block.setIndent(indent);
+ modifiedBlockFormat = true;
+ }
+
+ if (currentNode->blockFormat.propertyCount() > 0) {
+ modifiedBlockFormat = true;
+ block.merge(currentNode->blockFormat);
+ }
+
+ if (currentNode->charFormat.propertyCount() > 0) {
+ modifiedCharFormat = true;
+ charFmt.merge(currentNode->charFormat);
+ }
+
+ // ####################
+ // block.setFloatPosition(node->cssFloat);
+
+ if (wsm == QTextHtmlParserNode::WhiteSpacePre) {
+ block.setNonBreakableLines(true);
+ modifiedBlockFormat = true;
+ }
+
+ if (currentNode->charFormat.background().style() != Qt::NoBrush && !currentNode->isTableCell()) {
+ block.setBackground(currentNode->charFormat.background());
+ modifiedBlockFormat = true;
+ }
+
+ if (hasBlock && (!currentNode->isEmptyParagraph || forceBlockMerging)) {
+ if (modifiedBlockFormat)
+ cursor.setBlockFormat(block);
+ if (modifiedCharFormat)
+ cursor.setBlockCharFormat(charFmt);
+ } else {
+ if (currentNodeIdx == 1 && cursor.position() == 0 && currentNode->isEmptyParagraph) {
+ cursor.setBlockFormat(block);
+ cursor.setBlockCharFormat(charFmt);
+ } else {
+ appendBlock(block, charFmt);
+ }
+ }
+
+ if (currentNode->userState != -1)
+ cursor.block().setUserState(currentNode->userState);
+
+ if (currentNode->id == Html_li && !lists.isEmpty()) {
+ List &l = lists.last();
+ if (l.list) {
+ l.list->add(cursor.block());
+ } else {
+ l.list = cursor.createList(l.format);
+ const qreal listTopMargin = topMargin(l.listNode);
+ if (listTopMargin > block.topMargin()) {
+ block.setTopMargin(listTopMargin);
+ cursor.mergeBlockFormat(block);
+ }
+ }
+ if (hasBlock) {
+ QTextBlockFormat fmt;
+ fmt.setIndent(0);
+ cursor.mergeBlockFormat(fmt);
+ }
+ }
+
+ forceBlockMerging = false;
+ if (currentNode->id == Html_body || currentNode->id == Html_html)
+ forceBlockMerging = true;
+
+ if (currentNode->isEmptyParagraph) {
+ hasBlock = false;
+ return ContinueWithNextNode;
+ }
+
+ hasBlock = true;
+ blockTagClosed = false;
+ return ContinueWithCurrentNode;
+}
+
+void QTextHtmlImporter::appendBlock(const QTextBlockFormat &format, QTextCharFormat charFmt)
+{
+ if (!namedAnchors.isEmpty()) {
+ charFmt.setAnchor(true);
+ charFmt.setAnchorNames(namedAnchors);
+ namedAnchors.clear();
+ }
+
+ cursor.insertBlock(format, charFmt);
+
+ if (wsm != QTextHtmlParserNode::WhiteSpacePre && wsm != QTextHtmlParserNode::WhiteSpacePreWrap)
+ compressNextWhitespace = RemoveWhiteSpace;
+}
+
+#endif // QT_NO_TEXTHTMLPARSER
+
+/*!
+ \fn QTextDocumentFragment QTextDocumentFragment::fromHtml(const QString &text)
+
+ Returns a QTextDocumentFragment based on the arbitrary piece of
+ HTML in the given \a text. The formatting is preserved as much as
+ possible; for example, "<b>bold</b>" will become a document
+ fragment with the text "bold" with a bold character format.
+*/
+
+#ifndef QT_NO_TEXTHTMLPARSER
+
+QTextDocumentFragment QTextDocumentFragment::fromHtml(const QString &html)
+{
+ return fromHtml(html, 0);
+}
+
+/*!
+ \fn QTextDocumentFragment QTextDocumentFragment::fromHtml(const QString &text, const QTextDocument *resourceProvider)
+ \since 4.2
+
+ Returns a QTextDocumentFragment based on the arbitrary piece of
+ HTML in the given \a text. The formatting is preserved as much as
+ possible; for example, "<b>bold</b>" will become a document
+ fragment with the text "bold" with a bold character format.
+
+ If the provided HTML contains references to external resources such as imported style sheets, then
+ they will be loaded through the \a resourceProvider.
+*/
+
+QTextDocumentFragment QTextDocumentFragment::fromHtml(const QString &html, const QTextDocument *resourceProvider)
+{
+ QTextDocumentFragment res;
+ res.d = new QTextDocumentFragmentPrivate;
+
+ QTextHtmlImporter importer(res.d->doc, html, QTextHtmlImporter::ImportToFragment, resourceProvider);
+ importer.import();
+ return res;
+}
+
+QT_END_NAMESPACE
+#endif // QT_NO_TEXTHTMLPARSER
diff --git a/src/gui/text/qtextdocumentfragment.h b/src/gui/text/qtextdocumentfragment.h
new file mode 100644
index 0000000000..269aca2bcb
--- /dev/null
+++ b/src/gui/text/qtextdocumentfragment.h
@@ -0,0 +1,92 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QTEXTDOCUMENTFRAGMENT_H
+#define QTEXTDOCUMENTFRAGMENT_H
+
+#include <QtCore/qstring.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+class QTextStream;
+class QTextDocument;
+class QTextDocumentFragmentPrivate;
+class QTextCursor;
+
+class Q_GUI_EXPORT QTextDocumentFragment
+{
+public:
+ QTextDocumentFragment();
+ explicit QTextDocumentFragment(const QTextDocument *document);
+ explicit QTextDocumentFragment(const QTextCursor &range);
+ QTextDocumentFragment(const QTextDocumentFragment &rhs);
+ QTextDocumentFragment &operator=(const QTextDocumentFragment &rhs);
+ ~QTextDocumentFragment();
+
+ bool isEmpty() const;
+
+ QString toPlainText() const;
+#ifndef QT_NO_TEXTHTMLPARSER
+ QString toHtml() const;
+ QString toHtml(const QByteArray &encoding) const;
+#endif // QT_NO_TEXTHTMLPARSER
+
+ static QTextDocumentFragment fromPlainText(const QString &plainText);
+#ifndef QT_NO_TEXTHTMLPARSER
+ static QTextDocumentFragment fromHtml(const QString &html);
+ static QTextDocumentFragment fromHtml(const QString &html, const QTextDocument *resourceProvider);
+#endif // QT_NO_TEXTHTMLPARSER
+
+private:
+ QTextDocumentFragmentPrivate *d;
+ friend class QTextCursor;
+ friend class QTextDocumentWriter;
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QTEXTDOCUMENTFRAGMENT_H
diff --git a/src/gui/text/qtextdocumentfragment_p.h b/src/gui/text/qtextdocumentfragment_p.h
new file mode 100644
index 0000000000..743ed9d2b6
--- /dev/null
+++ b/src/gui/text/qtextdocumentfragment_p.h
@@ -0,0 +1,236 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QTEXTDOCUMENTFRAGMENT_P_H
+#define QTEXTDOCUMENTFRAGMENT_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 "QtGui/qtextdocument.h"
+#include "private/qtexthtmlparser_p.h"
+#include "private/qtextdocument_p.h"
+#include "QtGui/qtexttable.h"
+#include "QtCore/qatomic.h"
+#include "QtCore/qlist.h"
+#include "QtCore/qmap.h"
+#include "QtCore/qpointer.h"
+#include "QtCore/qvarlengtharray.h"
+#include "QtCore/qdatastream.h"
+
+QT_BEGIN_NAMESPACE
+
+class QTextDocumentFragmentPrivate;
+
+class QTextCopyHelper
+{
+public:
+ QTextCopyHelper(const QTextCursor &_source, const QTextCursor &_destination, bool forceCharFormat = false, const QTextCharFormat &fmt = QTextCharFormat());
+
+ void copy();
+
+private:
+ void appendFragments(int pos, int endPos);
+ int appendFragment(int pos, int endPos, int objectIndex = -1);
+ int convertFormatIndex(const QTextFormat &oldFormat, int objectIndexToSet = -1);
+ inline int convertFormatIndex(int oldFormatIndex, int objectIndexToSet = -1)
+ { return convertFormatIndex(src->formatCollection()->format(oldFormatIndex), objectIndexToSet); }
+ inline QTextFormat convertFormat(const QTextFormat &fmt)
+ { return dst->formatCollection()->format(convertFormatIndex(fmt)); }
+
+ int insertPos;
+
+ bool forceCharFormat;
+ int primaryCharFormatIndex;
+
+ QTextCursor cursor;
+ QTextDocumentPrivate *dst;
+ QTextDocumentPrivate *src;
+ QTextFormatCollection &formatCollection;
+ const QString originalText;
+ QMap<int, int> objectIndexMap;
+};
+
+class QTextDocumentFragmentPrivate
+{
+public:
+ QTextDocumentFragmentPrivate(const QTextCursor &cursor = QTextCursor());
+ inline ~QTextDocumentFragmentPrivate() { delete doc; }
+
+ void insert(QTextCursor &cursor) const;
+
+ QAtomicInt ref;
+ QTextDocument *doc;
+
+ uint importedFromPlainText : 1;
+private:
+ Q_DISABLE_COPY(QTextDocumentFragmentPrivate)
+};
+
+#ifndef QT_NO_TEXTHTMLPARSER
+
+class QTextHtmlImporter : public QTextHtmlParser
+{
+ struct Table;
+public:
+ enum ImportMode {
+ ImportToFragment,
+ ImportToDocument
+ };
+
+ QTextHtmlImporter(QTextDocument *_doc, const QString &html,
+ ImportMode mode,
+ const QTextDocument *resourceProvider = 0);
+
+ void import();
+
+private:
+ bool closeTag();
+
+ Table scanTable(int tableNodeIdx);
+
+ enum ProcessNodeResult { ContinueWithNextNode, ContinueWithCurrentNode };
+
+ void appendBlock(const QTextBlockFormat &format, QTextCharFormat charFmt = QTextCharFormat());
+ bool appendNodeText();
+
+ ProcessNodeResult processBlockNode();
+ ProcessNodeResult processSpecialNodes();
+
+ struct List
+ {
+ inline List() : listNode(0) {}
+ QTextListFormat format;
+ int listNode;
+ QPointer<QTextList> list;
+ };
+ QVector<List> lists;
+ int indent;
+
+ // insert a named anchor the next time we emit a char format,
+ // either in a block or in regular text
+ QStringList namedAnchors;
+
+#ifdef Q_CC_SUN
+ friend struct QTextHtmlImporter::Table;
+#endif
+ struct TableCellIterator
+ {
+ inline TableCellIterator(QTextTable *t = 0) : table(t), row(0), column(0) {}
+
+ inline TableCellIterator &operator++() {
+ if (atEnd())
+ return *this;
+ do {
+ const QTextTableCell cell = table->cellAt(row, column);
+ if (!cell.isValid())
+ break;
+ column += cell.columnSpan();
+ if (column >= table->columns()) {
+ column = 0;
+ ++row;
+ }
+ } while (row < table->rows() && table->cellAt(row, column).row() != row);
+
+ return *this;
+ }
+
+ inline bool atEnd() const { return table == 0 || row >= table->rows(); }
+
+ QTextTableCell cell() const { return table->cellAt(row, column); }
+
+ QTextTable *table;
+ int row;
+ int column;
+ };
+
+ friend struct Table;
+ struct Table
+ {
+ Table() : isTextFrame(false), rows(0), columns(0), currentRow(0), lastIndent(0) {}
+ QPointer<QTextFrame> frame;
+ bool isTextFrame;
+ int rows;
+ int columns;
+ int currentRow; // ... for buggy html (see html_skipCell testcase)
+ TableCellIterator currentCell;
+ int lastIndent;
+ };
+ QVector<Table> tables;
+
+ struct RowColSpanInfo
+ {
+ int row, col;
+ int rowSpan, colSpan;
+ };
+
+ enum WhiteSpace
+ {
+ RemoveWhiteSpace,
+ CollapseWhiteSpace,
+ PreserveWhiteSpace
+ };
+
+ WhiteSpace compressNextWhitespace;
+
+ QTextDocument *doc;
+ QTextCursor cursor;
+ QTextHtmlParserNode::WhiteSpaceMode wsm;
+ ImportMode importMode;
+ bool hasBlock;
+ bool forceBlockMerging;
+ bool blockTagClosed;
+ int currentNodeIdx;
+ const QTextHtmlParserNode *currentNode;
+};
+
+QT_END_NAMESPACE
+#endif // QT_NO_TEXTHTMLPARSER
+
+#endif // QTEXTDOCUMENTFRAGMENT_P_H
diff --git a/src/gui/text/qtextdocumentlayout.cpp b/src/gui/text/qtextdocumentlayout.cpp
new file mode 100644
index 0000000000..c66d0c1ed0
--- /dev/null
+++ b/src/gui/text/qtextdocumentlayout.cpp
@@ -0,0 +1,3224 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qtextdocumentlayout_p.h"
+#include "qtextdocument_p.h"
+#include "qtextimagehandler_p.h"
+#include "qtexttable.h"
+#include "qtextlist.h"
+#include "qtextengine_p.h"
+#include "private/qcssutil_p.h"
+
+#include "qabstracttextdocumentlayout_p.h"
+#include "qcssparser_p.h"
+
+#include <qpainter.h>
+#include <qmath.h>
+#include <qrect.h>
+#include <qpalette.h>
+#include <qdebug.h>
+#include <qvarlengtharray.h>
+#include <limits.h>
+#include <qstyle.h>
+#include <qbasictimer.h>
+#include "private/qfunctions_p.h"
+
+// #define LAYOUT_DEBUG
+
+#ifdef LAYOUT_DEBUG
+#define LDEBUG qDebug()
+#define INC_INDENT debug_indent += " "
+#define DEC_INDENT debug_indent = debug_indent.left(debug_indent.length()-2)
+#else
+#define LDEBUG if(0) qDebug()
+#define INC_INDENT do {} while(0)
+#define DEC_INDENT do {} while(0)
+#endif
+
+QT_BEGIN_NAMESPACE
+
+extern int qt_defaultDpi();
+
+// ################ should probably add frameFormatChange notification!
+
+struct QLayoutStruct;
+
+class QTextFrameData : public QTextFrameLayoutData
+{
+public:
+ QTextFrameData();
+
+ // relative to parent frame
+ QFixedPoint position;
+ QFixedSize size;
+
+ // contents starts at (margin+border/margin+border)
+ QFixed topMargin;
+ QFixed bottomMargin;
+ QFixed leftMargin;
+ QFixed rightMargin;
+ QFixed border;
+ QFixed padding;
+ // contents width includes padding (as we need to treat this on a per cell basis for tables)
+ QFixed contentsWidth;
+ QFixed contentsHeight;
+ QFixed oldContentsWidth;
+
+ // accumulated margins
+ QFixed effectiveTopMargin;
+ QFixed effectiveBottomMargin;
+
+ QFixed minimumWidth;
+ QFixed maximumWidth;
+
+ QLayoutStruct *currentLayoutStruct;
+
+ bool sizeDirty;
+ bool layoutDirty;
+
+ QList<QPointer<QTextFrame> > floats;
+};
+
+QTextFrameData::QTextFrameData()
+ : maximumWidth(QFIXED_MAX),
+ currentLayoutStruct(0), sizeDirty(true), layoutDirty(true)
+{
+}
+
+struct QLayoutStruct {
+ QLayoutStruct() : maximumWidth(QFIXED_MAX), fullLayout(false)
+ {}
+ QTextFrame *frame;
+ QFixed x_left;
+ QFixed x_right;
+ QFixed frameY; // absolute y position of the current frame
+ QFixed y; // always relative to the current frame
+ QFixed contentsWidth;
+ QFixed minimumWidth;
+ QFixed maximumWidth;
+ bool fullLayout;
+ QList<QTextFrame *> pendingFloats;
+ QFixed pageHeight;
+ QFixed pageBottom;
+ QFixed pageTopMargin;
+ QFixed pageBottomMargin;
+ QRectF updateRect;
+ QRectF updateRectForFloats;
+
+ inline void addUpdateRectForFloat(const QRectF &rect) {
+ if (updateRectForFloats.isValid())
+ updateRectForFloats |= rect;
+ else
+ updateRectForFloats = rect;
+ }
+
+ inline QFixed absoluteY() const
+ { return frameY + y; }
+
+ inline int currentPage() const
+ { return pageHeight == 0 ? 0 : (absoluteY() / pageHeight).truncate(); }
+
+ inline void newPage()
+ { if (pageHeight == QFIXED_MAX) return; pageBottom += pageHeight; y = pageBottom - pageHeight + pageBottomMargin + pageTopMargin - frameY; }
+};
+
+class QTextTableData : public QTextFrameData
+{
+public:
+ QFixed cellSpacing, cellPadding;
+ qreal deviceScale;
+ QVector<QFixed> minWidths;
+ QVector<QFixed> maxWidths;
+ QVector<QFixed> widths;
+ QVector<QFixed> heights;
+ QVector<QFixed> columnPositions;
+ QVector<QFixed> rowPositions;
+
+ QVector<QFixed> cellVerticalOffsets;
+
+ QFixed headerHeight;
+
+ // maps from cell index (row + col * rowCount) to child frames belonging to
+ // the specific cell
+ QMultiHash<int, QTextFrame *> childFrameMap;
+
+ inline QFixed cellWidth(int column, int colspan) const
+ { return columnPositions.at(column + colspan - 1) + widths.at(column + colspan - 1)
+ - columnPositions.at(column); }
+
+ inline void calcRowPosition(int row)
+ {
+ if (row > 0)
+ rowPositions[row] = rowPositions.at(row - 1) + heights.at(row - 1) + border + cellSpacing + border;
+ }
+
+ QRectF cellRect(const QTextTableCell &cell) const;
+
+ inline QFixed paddingProperty(const QTextFormat &format, QTextFormat::Property property) const
+ {
+ QVariant v = format.property(property);
+ if (v.isNull()) {
+ return cellPadding;
+ } else {
+ Q_ASSERT(v.type() == QVariant::Double);
+ return QFixed::fromReal(v.toDouble() * deviceScale);
+ }
+ }
+
+ inline QFixed topPadding(const QTextFormat &format) const
+ {
+ return paddingProperty(format, QTextFormat::TableCellTopPadding);
+ }
+
+ inline QFixed bottomPadding(const QTextFormat &format) const
+ {
+ return paddingProperty(format, QTextFormat::TableCellBottomPadding);
+ }
+
+ inline QFixed leftPadding(const QTextFormat &format) const
+ {
+ return paddingProperty(format, QTextFormat::TableCellLeftPadding);
+ }
+
+ inline QFixed rightPadding(const QTextFormat &format) const
+ {
+ return paddingProperty(format, QTextFormat::TableCellRightPadding);
+ }
+
+ inline QFixedPoint cellPosition(const QTextTableCell &cell) const
+ {
+ const QTextFormat fmt = cell.format();
+ return cellPosition(cell.row(), cell.column()) + QFixedPoint(leftPadding(fmt), topPadding(fmt));
+ }
+
+ void updateTableSize();
+
+private:
+ inline QFixedPoint cellPosition(int row, int col) const
+ { return QFixedPoint(columnPositions.at(col), rowPositions.at(row) + cellVerticalOffsets.at(col + row * widths.size())); }
+};
+
+static QTextFrameData *createData(QTextFrame *f)
+{
+ QTextFrameData *data;
+ if (qobject_cast<QTextTable *>(f))
+ data = new QTextTableData;
+ else
+ data = new QTextFrameData;
+ f->setLayoutData(data);
+ return data;
+}
+
+static inline QTextFrameData *data(QTextFrame *f)
+{
+ QTextFrameData *data = static_cast<QTextFrameData *>(f->layoutData());
+ if (!data)
+ data = createData(f);
+ return data;
+}
+
+static bool isFrameFromInlineObject(QTextFrame *f)
+{
+ return f->firstPosition() > f->lastPosition();
+}
+
+void QTextTableData::updateTableSize()
+{
+ const QFixed effectiveTopMargin = this->topMargin + border + padding;
+ const QFixed effectiveBottomMargin = this->bottomMargin + border + padding;
+ const QFixed effectiveLeftMargin = this->leftMargin + border + padding;
+ const QFixed effectiveRightMargin = this->rightMargin + border + padding;
+ size.height = contentsHeight == -1
+ ? rowPositions.last() + heights.last() + padding + border + cellSpacing + effectiveBottomMargin
+ : effectiveTopMargin + contentsHeight + effectiveBottomMargin;
+ size.width = effectiveLeftMargin + contentsWidth + effectiveRightMargin;
+}
+
+QRectF QTextTableData::cellRect(const QTextTableCell &cell) const
+{
+ const int row = cell.row();
+ const int rowSpan = cell.rowSpan();
+ const int column = cell.column();
+ const int colSpan = cell.columnSpan();
+
+ return QRectF(columnPositions.at(column).toReal(),
+ rowPositions.at(row).toReal(),
+ (columnPositions.at(column + colSpan - 1) + widths.at(column + colSpan - 1) - columnPositions.at(column)).toReal(),
+ (rowPositions.at(row + rowSpan - 1) + heights.at(row + rowSpan - 1) - rowPositions.at(row)).toReal());
+}
+
+static inline bool isEmptyBlockBeforeTable(const QTextBlock &block, const QTextBlockFormat &format, const QTextFrame::Iterator &nextIt)
+{
+ return !nextIt.atEnd()
+ && qobject_cast<QTextTable *>(nextIt.currentFrame())
+ && block.isValid()
+ && block.length() == 1
+ && !format.hasProperty(QTextFormat::BlockTrailingHorizontalRulerWidth)
+ && !format.hasProperty(QTextFormat::BackgroundBrush)
+ && nextIt.currentFrame()->firstPosition() == block.position() + 1
+ ;
+}
+
+static inline bool isEmptyBlockBeforeTable(QTextFrame::Iterator it)
+{
+ QTextFrame::Iterator next = it; ++next;
+ if (it.currentFrame())
+ return false;
+ QTextBlock block = it.currentBlock();
+ return isEmptyBlockBeforeTable(block, block.blockFormat(), next);
+}
+
+static inline bool isEmptyBlockAfterTable(const QTextBlock &block, const QTextFrame *previousFrame)
+{
+ return qobject_cast<const QTextTable *>(previousFrame)
+ && block.isValid()
+ && block.length() == 1
+ && previousFrame->lastPosition() == block.position() - 1
+ ;
+}
+
+static inline bool isLineSeparatorBlockAfterTable(const QTextBlock &block, const QTextFrame *previousFrame)
+{
+ return qobject_cast<const QTextTable *>(previousFrame)
+ && block.isValid()
+ && block.length() > 1
+ && block.text().at(0) == QChar::LineSeparator
+ && previousFrame->lastPosition() == block.position() - 1
+ ;
+}
+
+/*
+
+Optimisation strategies:
+
+HTML layout:
+
+* Distinguish between normal and special flow. For normal flow the condition:
+ y1 > y2 holds for all blocks with b1.key() > b2.key().
+* Special flow is: floats, table cells
+
+* Normal flow within table cells. Tables (not cells) are part of the normal flow.
+
+
+* If blocks grows/shrinks in height and extends over whole page width at the end, move following blocks.
+* If height doesn't change, no need to do anything
+
+Table cells:
+
+* If minWidth of cell changes, recalculate table width, relayout if needed.
+* What about maxWidth when doing auto layout?
+
+Floats:
+* need fixed or proportional width, otherwise don't float!
+* On width/height change relayout surrounding paragraphs.
+
+Document width change:
+* full relayout needed
+
+
+Float handling:
+
+* Floats are specified by a special format object.
+* currently only floating images are implemented.
+
+*/
+
+/*
+
+ On the table layouting:
+
+ +---[ table border ]-------------------------
+ | [ cell spacing ]
+ | +------[ cell border ]-----+ +--------
+ | | | |
+ | |
+ | |
+ | |
+ |
+
+ rowPositions[i] and columnPositions[i] point at the cell content
+ position. So for example the left border is drawn at
+ x = columnPositions[i] - fd->border and similar for y.
+
+*/
+
+struct QCheckPoint
+{
+ QFixed y;
+ QFixed frameY; // absolute y position of the current frame
+ int positionInFrame;
+ QFixed minimumWidth;
+ QFixed maximumWidth;
+ QFixed contentsWidth;
+};
+Q_DECLARE_TYPEINFO(QCheckPoint, Q_PRIMITIVE_TYPE);
+
+Q_STATIC_GLOBAL_OPERATOR bool operator<(const QCheckPoint &checkPoint, QFixed y)
+{
+ return checkPoint.y < y;
+}
+
+Q_STATIC_GLOBAL_OPERATOR bool operator<(const QCheckPoint &checkPoint, int pos)
+{
+ return checkPoint.positionInFrame < pos;
+}
+
+static void fillBackground(QPainter *p, const QRectF &rect, QBrush brush, const QPointF &origin, QRectF gradientRect = QRectF())
+{
+ p->save();
+ if (brush.style() >= Qt::LinearGradientPattern && brush.style() <= Qt::ConicalGradientPattern) {
+ if (!gradientRect.isNull()) {
+ QTransform m;
+ m.translate(gradientRect.left(), gradientRect.top());
+ m.scale(gradientRect.width(), gradientRect.height());
+ brush.setTransform(m);
+ const_cast<QGradient *>(brush.gradient())->setCoordinateMode(QGradient::LogicalMode);
+ }
+ } else {
+ p->setBrushOrigin(origin);
+ }
+ p->fillRect(rect, brush);
+ p->restore();
+}
+
+class QTextDocumentLayoutPrivate : public QAbstractTextDocumentLayoutPrivate
+{
+ Q_DECLARE_PUBLIC(QTextDocumentLayout)
+public:
+ QTextDocumentLayoutPrivate();
+
+ QTextOption::WrapMode wordWrapMode;
+#ifdef LAYOUT_DEBUG
+ mutable QString debug_indent;
+#endif
+
+ int fixedColumnWidth;
+ int cursorWidth;
+
+ QSizeF lastReportedSize;
+ QRectF viewportRect;
+ QRectF clipRect;
+
+ mutable int currentLazyLayoutPosition;
+ mutable int lazyLayoutStepSize;
+ QBasicTimer layoutTimer;
+ mutable QBasicTimer sizeChangedTimer;
+ uint showLayoutProgress : 1;
+ uint insideDocumentChange : 1;
+
+ int lastPageCount;
+ qreal idealWidth;
+ bool contentHasAlignment;
+
+ QFixed blockIndent(const QTextBlockFormat &blockFormat) const;
+
+ void drawFrame(const QPointF &offset, QPainter *painter, const QAbstractTextDocumentLayout::PaintContext &context,
+ QTextFrame *f) const;
+ void drawFlow(const QPointF &offset, QPainter *painter, const QAbstractTextDocumentLayout::PaintContext &context,
+ QTextFrame::Iterator it, const QList<QTextFrame *> &floats, QTextBlock *cursorBlockNeedingRepaint) const;
+ void drawBlock(const QPointF &offset, QPainter *painter, const QAbstractTextDocumentLayout::PaintContext &context,
+ QTextBlock bl, bool inRootFrame) const;
+ void drawListItem(const QPointF &offset, QPainter *painter, const QAbstractTextDocumentLayout::PaintContext &context,
+ QTextBlock bl, const QTextCharFormat *selectionFormat) const;
+ void drawTableCell(const QRectF &cellRect, QPainter *painter, const QAbstractTextDocumentLayout::PaintContext &cell_context,
+ QTextTable *table, QTextTableData *td, int r, int c,
+ QTextBlock *cursorBlockNeedingRepaint, QPointF *cursorBlockOffset) const;
+ void drawBorder(QPainter *painter, const QRectF &rect, qreal topMargin, qreal bottomMargin, qreal border,
+ const QBrush &brush, QTextFrameFormat::BorderStyle style) const;
+ void drawFrameDecoration(QPainter *painter, QTextFrame *frame, QTextFrameData *fd, const QRectF &clip, const QRectF &rect) const;
+
+ enum HitPoint {
+ PointBefore,
+ PointAfter,
+ PointInside,
+ PointExact
+ };
+ HitPoint hitTest(QTextFrame *frame, const QFixedPoint &point, int *position, QTextLayout **l, Qt::HitTestAccuracy accuracy) const;
+ HitPoint hitTest(QTextFrame::Iterator it, HitPoint hit, const QFixedPoint &p,
+ int *position, QTextLayout **l, Qt::HitTestAccuracy accuracy) const;
+ HitPoint hitTest(QTextTable *table, const QFixedPoint &point, int *position, QTextLayout **l, Qt::HitTestAccuracy accuracy) const;
+ HitPoint hitTest(QTextBlock bl, const QFixedPoint &point, int *position, QTextLayout **l, Qt::HitTestAccuracy accuracy) const;
+
+ QLayoutStruct layoutCell(QTextTable *t, const QTextTableCell &cell, QFixed width,
+ int layoutFrom, int layoutTo, QTextTableData *tableData, QFixed absoluteTableY,
+ bool withPageBreaks);
+ void setCellPosition(QTextTable *t, const QTextTableCell &cell, const QPointF &pos);
+ QRectF layoutTable(QTextTable *t, int layoutFrom, int layoutTo, QFixed parentY);
+
+ void positionFloat(QTextFrame *frame, QTextLine *currentLine = 0);
+
+ // calls the next one
+ QRectF layoutFrame(QTextFrame *f, int layoutFrom, int layoutTo, QFixed parentY = 0);
+ QRectF layoutFrame(QTextFrame *f, int layoutFrom, int layoutTo, QFixed frameWidth, QFixed frameHeight, QFixed parentY = 0);
+
+ void layoutBlock(const QTextBlock &bl, int blockPosition, const QTextBlockFormat &blockFormat,
+ QLayoutStruct *layoutStruct, int layoutFrom, int layoutTo, const QTextBlockFormat *previousBlockFormat);
+ void layoutFlow(QTextFrame::Iterator it, QLayoutStruct *layoutStruct, int layoutFrom, int layoutTo, QFixed width = 0);
+ void pageBreakInsideTable(QTextTable *table, QLayoutStruct *layoutStruct);
+
+
+ void floatMargins(const QFixed &y, const QLayoutStruct *layoutStruct, QFixed *left, QFixed *right) const;
+ QFixed findY(QFixed yFrom, const QLayoutStruct *layoutStruct, QFixed requiredWidth) const;
+
+ QVector<QCheckPoint> checkPoints;
+
+ QTextFrame::Iterator frameIteratorForYPosition(QFixed y) const;
+ QTextFrame::Iterator frameIteratorForTextPosition(int position) const;
+
+ void ensureLayouted(QFixed y) const;
+ void ensureLayoutedByPosition(int position) const;
+ inline void ensureLayoutFinished() const
+ { ensureLayoutedByPosition(INT_MAX); }
+ void layoutStep() const;
+
+ QRectF frameBoundingRectInternal(QTextFrame *frame) const;
+
+ qreal scaleToDevice(qreal value) const;
+ QFixed scaleToDevice(QFixed value) const;
+};
+
+QTextDocumentLayoutPrivate::QTextDocumentLayoutPrivate()
+ : fixedColumnWidth(-1),
+ cursorWidth(1),
+ currentLazyLayoutPosition(-1),
+ lazyLayoutStepSize(1000),
+ lastPageCount(-1)
+{
+ showLayoutProgress = true;
+ insideDocumentChange = false;
+ idealWidth = 0;
+ contentHasAlignment = false;
+}
+
+QTextFrame::Iterator QTextDocumentLayoutPrivate::frameIteratorForYPosition(QFixed y) const
+{
+ QTextFrame *rootFrame = document->rootFrame();
+
+ if (checkPoints.isEmpty()
+ || y < 0 || y > data(rootFrame)->size.height)
+ return rootFrame->begin();
+
+ QVector<QCheckPoint>::ConstIterator checkPoint = qLowerBound(checkPoints.begin(), checkPoints.end(), y);
+ if (checkPoint == checkPoints.end())
+ return rootFrame->begin();
+
+ if (checkPoint != checkPoints.begin())
+ --checkPoint;
+
+ const int position = rootFrame->firstPosition() + checkPoint->positionInFrame;
+ return frameIteratorForTextPosition(position);
+}
+
+QTextFrame::Iterator QTextDocumentLayoutPrivate::frameIteratorForTextPosition(int position) const
+{
+ QTextFrame *rootFrame = docPrivate->rootFrame();
+
+ const QTextDocumentPrivate::BlockMap &map = docPrivate->blockMap();
+ const int begin = map.findNode(rootFrame->firstPosition());
+ const int end = map.findNode(rootFrame->lastPosition()+1);
+
+ const int block = map.findNode(position);
+ const int blockPos = map.position(block);
+
+ QTextFrame::iterator it(rootFrame, block, begin, end);
+
+ QTextFrame *containingFrame = docPrivate->frameAt(blockPos);
+ if (containingFrame != rootFrame) {
+ while (containingFrame->parentFrame() != rootFrame) {
+ containingFrame = containingFrame->parentFrame();
+ Q_ASSERT(containingFrame);
+ }
+
+ it.cf = containingFrame;
+ it.cb = 0;
+ }
+
+ return it;
+}
+
+QTextDocumentLayoutPrivate::HitPoint
+QTextDocumentLayoutPrivate::hitTest(QTextFrame *frame, const QFixedPoint &point, int *position, QTextLayout **l, Qt::HitTestAccuracy accuracy) const
+{
+ QTextFrameData *fd = data(frame);
+ // #########
+ if (fd->layoutDirty)
+ return PointAfter;
+ Q_ASSERT(!fd->layoutDirty);
+ Q_ASSERT(!fd->sizeDirty);
+ const QFixedPoint relativePoint(point.x - fd->position.x, point.y - fd->position.y);
+
+ QTextFrame *rootFrame = docPrivate->rootFrame();
+
+// LDEBUG << "checking frame" << frame->firstPosition() << "point=" << point
+// << "position" << fd->position << "size" << fd->size;
+ if (frame != rootFrame) {
+ if (relativePoint.y < 0 || relativePoint.x < 0) {
+ *position = frame->firstPosition() - 1;
+// LDEBUG << "before pos=" << *position;
+ return PointBefore;
+ } else if (relativePoint.y > fd->size.height || relativePoint.x > fd->size.width) {
+ *position = frame->lastPosition() + 1;
+// LDEBUG << "after pos=" << *position;
+ return PointAfter;
+ }
+ }
+
+ if (isFrameFromInlineObject(frame)) {
+ *position = frame->firstPosition() - 1;
+ return PointExact;
+ }
+
+ if (QTextTable *table = qobject_cast<QTextTable *>(frame)) {
+ const int rows = table->rows();
+ const int columns = table->columns();
+ QTextTableData *td = static_cast<QTextTableData *>(data(table));
+
+ if (!td->childFrameMap.isEmpty()) {
+ for (int r = 0; r < rows; ++r) {
+ for (int c = 0; c < columns; ++c) {
+ QTextTableCell cell = table->cellAt(r, c);
+ if (cell.row() != r || cell.column() != c)
+ continue;
+
+ QRectF cellRect = td->cellRect(cell);
+ const QFixedPoint cellPos = QFixedPoint::fromPointF(cellRect.topLeft());
+ const QFixedPoint pointInCell = relativePoint - cellPos;
+
+ const QList<QTextFrame *> childFrames = td->childFrameMap.values(r + c * rows);
+ for (int i = 0; i < childFrames.size(); ++i) {
+ QTextFrame *child = childFrames.at(i);
+ if (isFrameFromInlineObject(child)
+ && child->frameFormat().position() != QTextFrameFormat::InFlow
+ && hitTest(child, pointInCell, position, l, accuracy) == PointExact)
+ {
+ return PointExact;
+ }
+ }
+ }
+ }
+ }
+
+ return hitTest(table, relativePoint, position, l, accuracy);
+ }
+
+ const QList<QTextFrame *> childFrames = frame->childFrames();
+ for (int i = 0; i < childFrames.size(); ++i) {
+ QTextFrame *child = childFrames.at(i);
+ if (isFrameFromInlineObject(child)
+ && child->frameFormat().position() != QTextFrameFormat::InFlow
+ && hitTest(child, relativePoint, position, l, accuracy) == PointExact)
+ {
+ return PointExact;
+ }
+ }
+
+ QTextFrame::Iterator it = frame->begin();
+
+ if (frame == rootFrame) {
+ it = frameIteratorForYPosition(relativePoint.y);
+
+ Q_ASSERT(it.parentFrame() == frame);
+ }
+
+ if (it.currentFrame())
+ *position = it.currentFrame()->firstPosition();
+ else
+ *position = it.currentBlock().position();
+
+ return hitTest(it, PointBefore, relativePoint, position, l, accuracy);
+}
+
+QTextDocumentLayoutPrivate::HitPoint
+QTextDocumentLayoutPrivate::hitTest(QTextFrame::Iterator it, HitPoint hit, const QFixedPoint &p,
+ int *position, QTextLayout **l, Qt::HitTestAccuracy accuracy) const
+{
+ INC_INDENT;
+
+ for (; !it.atEnd(); ++it) {
+ QTextFrame *c = it.currentFrame();
+ HitPoint hp;
+ int pos = -1;
+ if (c) {
+ hp = hitTest(c, p, &pos, l, accuracy);
+ } else {
+ hp = hitTest(it.currentBlock(), p, &pos, l, accuracy);
+ }
+ if (hp >= PointInside) {
+ if (isEmptyBlockBeforeTable(it))
+ continue;
+ hit = hp;
+ *position = pos;
+ break;
+ }
+ if (hp == PointBefore && pos < *position) {
+ *position = pos;
+ hit = hp;
+ } else if (hp == PointAfter && pos > *position) {
+ *position = pos;
+ hit = hp;
+ }
+ }
+
+ DEC_INDENT;
+// LDEBUG << "inside=" << hit << " pos=" << *position;
+ return hit;
+}
+
+QTextDocumentLayoutPrivate::HitPoint
+QTextDocumentLayoutPrivate::hitTest(QTextTable *table, const QFixedPoint &point,
+ int *position, QTextLayout **l, Qt::HitTestAccuracy accuracy) const
+{
+ QTextTableData *td = static_cast<QTextTableData *>(data(table));
+
+ QVector<QFixed>::ConstIterator rowIt = qLowerBound(td->rowPositions.constBegin(), td->rowPositions.constEnd(), point.y);
+ if (rowIt == td->rowPositions.constEnd()) {
+ rowIt = td->rowPositions.constEnd() - 1;
+ } else if (rowIt != td->rowPositions.constBegin()) {
+ --rowIt;
+ }
+
+ QVector<QFixed>::ConstIterator colIt = qLowerBound(td->columnPositions.constBegin(), td->columnPositions.constEnd(), point.x);
+ if (colIt == td->columnPositions.constEnd()) {
+ colIt = td->columnPositions.constEnd() - 1;
+ } else if (colIt != td->columnPositions.constBegin()) {
+ --colIt;
+ }
+
+ QTextTableCell cell = table->cellAt(rowIt - td->rowPositions.constBegin(),
+ colIt - td->columnPositions.constBegin());
+ if (!cell.isValid())
+ return PointBefore;
+
+ *position = cell.firstPosition();
+
+ HitPoint hp = hitTest(cell.begin(), PointInside, point - td->cellPosition(cell), position, l, accuracy);
+
+ if (hp == PointExact)
+ return hp;
+ if (hp == PointAfter)
+ *position = cell.lastPosition();
+ return PointInside;
+}
+
+QTextDocumentLayoutPrivate::HitPoint
+QTextDocumentLayoutPrivate::hitTest(QTextBlock bl, const QFixedPoint &point, int *position, QTextLayout **l,
+ Qt::HitTestAccuracy accuracy) const
+{
+ QTextLayout *tl = bl.layout();
+ QRectF textrect = tl->boundingRect();
+ textrect.translate(tl->position());
+// LDEBUG << " checking block" << bl.position() << "point=" << point
+// << " tlrect" << textrect;
+ *position = bl.position();
+ if (point.y.toReal() < textrect.top()) {
+// LDEBUG << " before pos=" << *position;
+ return PointBefore;
+ } else if (point.y.toReal() > textrect.bottom()) {
+ *position += bl.length();
+// LDEBUG << " after pos=" << *position;
+ return PointAfter;
+ }
+
+ QPointF pos = point.toPointF() - tl->position();
+
+ // ### rtl?
+
+ HitPoint hit = PointInside;
+ *l = tl;
+ int off = 0;
+ for (int i = 0; i < tl->lineCount(); ++i) {
+ QTextLine line = tl->lineAt(i);
+ const QRectF lr = line.naturalTextRect();
+ if (lr.top() > pos.y()) {
+ off = qMin(off, line.textStart());
+ } else if (lr.bottom() <= pos.y()) {
+ off = qMax(off, line.textStart() + line.textLength());
+ } else {
+ if (lr.left() <= pos.x() && lr.right() >= pos.x())
+ hit = PointExact;
+ // when trying to hit an anchor we want it to hit not only in the left
+ // half
+ if (accuracy == Qt::ExactHit)
+ off = line.xToCursor(pos.x(), QTextLine::CursorOnCharacter);
+ else
+ off = line.xToCursor(pos.x(), QTextLine::CursorBetweenCharacters);
+ break;
+ }
+ }
+ *position += off;
+
+// LDEBUG << " inside=" << hit << " pos=" << *position;
+ return hit;
+}
+
+// ### could be moved to QTextBlock
+QFixed QTextDocumentLayoutPrivate::blockIndent(const QTextBlockFormat &blockFormat) const
+{
+ qreal indent = blockFormat.indent();
+
+ QTextObject *object = document->objectForFormat(blockFormat);
+ if (object)
+ indent += object->format().toListFormat().indent();
+
+ if (qIsNull(indent))
+ return 0;
+
+ qreal scale = 1;
+ if (paintDevice) {
+ scale = qreal(paintDevice->logicalDpiY()) / qreal(qt_defaultDpi());
+ }
+
+ return QFixed::fromReal(indent * scale * document->indentWidth());
+}
+
+void QTextDocumentLayoutPrivate::drawBorder(QPainter *painter, const QRectF &rect, qreal topMargin, qreal bottomMargin,
+ qreal border, const QBrush &brush, QTextFrameFormat::BorderStyle style) const
+{
+ const qreal pageHeight = document->pageSize().height();
+ const int topPage = pageHeight > 0 ? static_cast<int>(rect.top() / pageHeight) : 0;
+ const int bottomPage = pageHeight > 0 ? static_cast<int>((rect.bottom() + border) / pageHeight) : 0;
+
+#ifndef QT_NO_CSSPARSER
+ QCss::BorderStyle cssStyle = static_cast<QCss::BorderStyle>(style + 1);
+#endif //QT_NO_CSSPARSER
+
+ bool turn_off_antialiasing = !(painter->renderHints() & QPainter::Antialiasing);
+ painter->setRenderHint(QPainter::Antialiasing);
+
+ for (int i = topPage; i <= bottomPage; ++i) {
+ QRectF clipped = rect.toRect();
+
+ if (topPage != bottomPage) {
+ clipped.setTop(qMax(clipped.top(), i * pageHeight + topMargin - border));
+ clipped.setBottom(qMin(clipped.bottom(), (i + 1) * pageHeight - bottomMargin));
+
+ if (clipped.bottom() <= clipped.top())
+ continue;
+ }
+#ifndef QT_NO_CSSPARSER
+ qDrawEdge(painter, clipped.left(), clipped.top(), clipped.left() + border, clipped.bottom() + border, 0, 0, QCss::LeftEdge, cssStyle, brush);
+ qDrawEdge(painter, clipped.left() + border, clipped.top(), clipped.right() + border, clipped.top() + border, 0, 0, QCss::TopEdge, cssStyle, brush);
+ qDrawEdge(painter, clipped.right(), clipped.top() + border, clipped.right() + border, clipped.bottom(), 0, 0, QCss::RightEdge, cssStyle, brush);
+ qDrawEdge(painter, clipped.left() + border, clipped.bottom(), clipped.right() + border, clipped.bottom() + border, 0, 0, QCss::BottomEdge, cssStyle, brush);
+#else
+ painter->save();
+ painter->setPen(Qt::NoPen);
+ painter->setBrush(brush);
+ painter->drawRect(QRectF(clipped.left(), clipped.top(), clipped.left() + border, clipped.bottom() + border));
+ painter->drawRect(QRectF(clipped.left() + border, clipped.top(), clipped.right() + border, clipped.top() + border));
+ painter->drawRect(QRectF(clipped.right(), clipped.top() + border, clipped.right() + border, clipped.bottom()));
+ painter->drawRect(QRectF(clipped.left() + border, clipped.bottom(), clipped.right() + border, clipped.bottom() + border));
+ painter->restore();
+#endif //QT_NO_CSSPARSER
+ }
+ if (turn_off_antialiasing)
+ painter->setRenderHint(QPainter::Antialiasing, false);
+}
+
+void QTextDocumentLayoutPrivate::drawFrameDecoration(QPainter *painter, QTextFrame *frame, QTextFrameData *fd, const QRectF &clip, const QRectF &rect) const
+{
+ if (fd->border != 0) {
+ painter->save();
+ painter->setBrush(Qt::lightGray);
+ painter->setPen(Qt::NoPen);
+
+ const qreal leftEdge = rect.left() + fd->leftMargin.toReal();
+ const qreal border = fd->border.toReal();
+ const qreal topMargin = fd->topMargin.toReal();
+ const qreal leftMargin = fd->leftMargin.toReal();
+ const qreal bottomMargin = fd->bottomMargin.toReal();
+ const qreal rightMargin = fd->rightMargin.toReal();
+ const qreal w = rect.width() - 2 * border - leftMargin - rightMargin;
+ const qreal h = rect.height() - 2 * border - topMargin - bottomMargin;
+
+ drawBorder(painter, QRectF(leftEdge, rect.top() + topMargin, w + border, h + border),
+ fd->effectiveTopMargin.toReal(), fd->effectiveBottomMargin.toReal(),
+ border, frame->frameFormat().borderBrush(), frame->frameFormat().borderStyle());
+
+ painter->restore();
+ }
+
+ const QBrush bg = frame->frameFormat().background();
+ if (bg != Qt::NoBrush) {
+ QRectF bgRect = rect;
+ bgRect.adjust((fd->leftMargin + fd->border).toReal(),
+ (fd->topMargin + fd->border).toReal(),
+ - (fd->rightMargin + fd->border).toReal(),
+ - (fd->bottomMargin + fd->border).toReal());
+
+ QRectF gradientRect; // invalid makes it default to bgRect
+ QPointF origin = bgRect.topLeft();
+ if (!frame->parentFrame()) {
+ bgRect = clip;
+ gradientRect.setWidth(painter->device()->width());
+ gradientRect.setHeight(painter->device()->height());
+ }
+ fillBackground(painter, bgRect, bg, origin, gradientRect);
+ }
+}
+
+static void adjustContextSelectionsForCell(QAbstractTextDocumentLayout::PaintContext &cell_context,
+ const QTextTableCell &cell,
+ int r, int c,
+ const int *selectedTableCells)
+{
+ for (int i = 0; i < cell_context.selections.size(); ++i) {
+ int row_start = selectedTableCells[i * 4];
+ int col_start = selectedTableCells[i * 4 + 1];
+ int num_rows = selectedTableCells[i * 4 + 2];
+ int num_cols = selectedTableCells[i * 4 + 3];
+
+ if (row_start != -1) {
+ if (r >= row_start && r < row_start + num_rows
+ && c >= col_start && c < col_start + num_cols)
+ {
+ int firstPosition = cell.firstPosition();
+ int lastPosition = cell.lastPosition();
+
+ // make sure empty cells are still selected
+ if (firstPosition == lastPosition)
+ ++lastPosition;
+
+ cell_context.selections[i].cursor.setPosition(firstPosition);
+ cell_context.selections[i].cursor.setPosition(lastPosition, QTextCursor::KeepAnchor);
+ } else {
+ cell_context.selections[i].cursor.clearSelection();
+ }
+ }
+
+ // FullWidthSelection is not useful for tables
+ cell_context.selections[i].format.clearProperty(QTextFormat::FullWidthSelection);
+ }
+}
+
+void QTextDocumentLayoutPrivate::drawFrame(const QPointF &offset, QPainter *painter,
+ const QAbstractTextDocumentLayout::PaintContext &context,
+ QTextFrame *frame) const
+{
+ QTextFrameData *fd = data(frame);
+ // #######
+ if (fd->layoutDirty)
+ return;
+ Q_ASSERT(!fd->sizeDirty);
+ Q_ASSERT(!fd->layoutDirty);
+
+ const QPointF off = offset + fd->position.toPointF();
+ if (context.clip.isValid()
+ && (off.y() > context.clip.bottom() || off.y() + fd->size.height.toReal() < context.clip.top()
+ || off.x() > context.clip.right() || off.x() + fd->size.width.toReal() < context.clip.left()))
+ return;
+
+// LDEBUG << debug_indent << "drawFrame" << frame->firstPosition() << "--" << frame->lastPosition() << "at" << offset;
+// INC_INDENT;
+
+ // if the cursor is /on/ a table border we may need to repaint it
+ // afterwards, as we usually draw the decoration first
+ QTextBlock cursorBlockNeedingRepaint;
+ QPointF offsetOfRepaintedCursorBlock = off;
+
+ QTextTable *table = qobject_cast<QTextTable *>(frame);
+ const QRectF frameRect(off, fd->size.toSizeF());
+
+ if (table) {
+ const int rows = table->rows();
+ const int columns = table->columns();
+ QTextTableData *td = static_cast<QTextTableData *>(data(table));
+
+ QVarLengthArray<int> selectedTableCells(context.selections.size() * 4);
+ for (int i = 0; i < context.selections.size(); ++i) {
+ const QAbstractTextDocumentLayout::Selection &s = context.selections.at(i);
+ int row_start = -1, col_start = -1, num_rows = -1, num_cols = -1;
+
+ if (s.cursor.currentTable() == table)
+ s.cursor.selectedTableCells(&row_start, &num_rows, &col_start, &num_cols);
+
+ selectedTableCells[i * 4] = row_start;
+ selectedTableCells[i * 4 + 1] = col_start;
+ selectedTableCells[i * 4 + 2] = num_rows;
+ selectedTableCells[i * 4 + 3] = num_cols;
+ }
+
+ QFixed pageHeight = QFixed::fromReal(document->pageSize().height());
+ if (pageHeight <= 0)
+ pageHeight = QFIXED_MAX;
+
+ const int tableStartPage = (td->position.y / pageHeight).truncate();
+ const int tableEndPage = ((td->position.y + td->size.height) / pageHeight).truncate();
+
+ qreal border = td->border.toReal();
+ drawFrameDecoration(painter, frame, fd, context.clip, frameRect);
+
+ // draw the table headers
+ const int headerRowCount = qMin(table->format().headerRowCount(), rows - 1);
+ int page = tableStartPage + 1;
+ while (page <= tableEndPage) {
+ const QFixed pageTop = page * pageHeight + td->effectiveTopMargin + td->cellSpacing + td->border;
+ const qreal headerOffset = (pageTop - td->rowPositions.at(0)).toReal();
+ for (int r = 0; r < headerRowCount; ++r) {
+ for (int c = 0; c < columns; ++c) {
+ QTextTableCell cell = table->cellAt(r, c);
+ QAbstractTextDocumentLayout::PaintContext cell_context = context;
+ adjustContextSelectionsForCell(cell_context, cell, r, c, selectedTableCells.data());
+ QRectF cellRect = td->cellRect(cell);
+
+ cellRect.translate(off.x(), headerOffset);
+ // we need to account for the cell border in the clipping test
+ int leftAdjust = qMin(qreal(0), 1 - border);
+ if (cell_context.clip.isValid() && !cellRect.adjusted(leftAdjust, leftAdjust, border, border).intersects(cell_context.clip))
+ continue;
+
+ drawTableCell(cellRect, painter, cell_context, table, td, r, c, &cursorBlockNeedingRepaint,
+ &offsetOfRepaintedCursorBlock);
+ }
+ }
+ ++page;
+ }
+
+ int firstRow = 0;
+ int lastRow = rows;
+
+ if (context.clip.isValid()) {
+ QVector<QFixed>::ConstIterator rowIt = qLowerBound(td->rowPositions.constBegin(), td->rowPositions.constEnd(), QFixed::fromReal(context.clip.top() - off.y()));
+ if (rowIt != td->rowPositions.constEnd() && rowIt != td->rowPositions.constBegin()) {
+ --rowIt;
+ firstRow = rowIt - td->rowPositions.constBegin();
+ }
+
+ rowIt = qUpperBound(td->rowPositions.constBegin(), td->rowPositions.constEnd(), QFixed::fromReal(context.clip.bottom() - off.y()));
+ if (rowIt != td->rowPositions.constEnd()) {
+ ++rowIt;
+ lastRow = rowIt - td->rowPositions.constBegin();
+ }
+ }
+
+ for (int c = 0; c < columns; ++c) {
+ QTextTableCell cell = table->cellAt(firstRow, c);
+ firstRow = qMin(firstRow, cell.row());
+ }
+
+ for (int r = firstRow; r < lastRow; ++r) {
+ for (int c = 0; c < columns; ++c) {
+ QTextTableCell cell = table->cellAt(r, c);
+ QAbstractTextDocumentLayout::PaintContext cell_context = context;
+ adjustContextSelectionsForCell(cell_context, cell, r, c, selectedTableCells.data());
+ QRectF cellRect = td->cellRect(cell);
+
+ cellRect.translate(off);
+ // we need to account for the cell border in the clipping test
+ int leftAdjust = qMin(qreal(0), 1 - border);
+ if (cell_context.clip.isValid() && !cellRect.adjusted(leftAdjust, leftAdjust, border, border).intersects(cell_context.clip))
+ continue;
+
+ drawTableCell(cellRect, painter, cell_context, table, td, r, c, &cursorBlockNeedingRepaint,
+ &offsetOfRepaintedCursorBlock);
+ }
+ }
+
+ } else {
+ drawFrameDecoration(painter, frame, fd, context.clip, frameRect);
+
+ QTextFrame::Iterator it = frame->begin();
+
+ if (frame == docPrivate->rootFrame())
+ it = frameIteratorForYPosition(QFixed::fromReal(context.clip.top()));
+
+ QList<QTextFrame *> floats;
+ for (int i = 0; i < fd->floats.count(); ++i)
+ floats.append(fd->floats.at(i));
+
+ drawFlow(off, painter, context, it, floats, &cursorBlockNeedingRepaint);
+ }
+
+ if (cursorBlockNeedingRepaint.isValid()) {
+ const QPen oldPen = painter->pen();
+ painter->setPen(context.palette.color(QPalette::Text));
+ const int cursorPos = context.cursorPosition - cursorBlockNeedingRepaint.position();
+ cursorBlockNeedingRepaint.layout()->drawCursor(painter, offsetOfRepaintedCursorBlock,
+ cursorPos, cursorWidth);
+ painter->setPen(oldPen);
+ }
+
+// DEC_INDENT;
+
+ return;
+}
+
+void QTextDocumentLayoutPrivate::drawTableCell(const QRectF &cellRect, QPainter *painter, const QAbstractTextDocumentLayout::PaintContext &cell_context,
+ QTextTable *table, QTextTableData *td, int r, int c,
+ QTextBlock *cursorBlockNeedingRepaint, QPointF *cursorBlockOffset) const
+{
+ QTextTableCell cell = table->cellAt(r, c);
+ int rspan = cell.rowSpan();
+ int cspan = cell.columnSpan();
+ if (rspan != 1) {
+ int cr = cell.row();
+ if (cr != r)
+ return;
+ }
+ if (cspan != 1) {
+ int cc = cell.column();
+ if (cc != c)
+ return;
+ }
+
+ QTextFormat fmt = cell.format();
+ const QFixed leftPadding = td->leftPadding(fmt);
+ const QFixed topPadding = td->topPadding(fmt);
+
+ if (td->border != 0) {
+ const QBrush oldBrush = painter->brush();
+ const QPen oldPen = painter->pen();
+
+ const qreal border = td->border.toReal();
+
+ QRectF borderRect(cellRect.left() - border, cellRect.top() - border, cellRect.width() + border, cellRect.height() + border);
+
+ // invert the border style for cells
+ QTextFrameFormat::BorderStyle cellBorder = table->format().borderStyle();
+ switch (cellBorder) {
+ case QTextFrameFormat::BorderStyle_Inset:
+ cellBorder = QTextFrameFormat::BorderStyle_Outset;
+ break;
+ case QTextFrameFormat::BorderStyle_Outset:
+ cellBorder = QTextFrameFormat::BorderStyle_Inset;
+ break;
+ case QTextFrameFormat::BorderStyle_Groove:
+ cellBorder = QTextFrameFormat::BorderStyle_Ridge;
+ break;
+ case QTextFrameFormat::BorderStyle_Ridge:
+ cellBorder = QTextFrameFormat::BorderStyle_Groove;
+ break;
+ default:
+ break;
+ }
+
+ qreal topMargin = (td->effectiveTopMargin + td->cellSpacing + td->border).toReal();
+ qreal bottomMargin = (td->effectiveBottomMargin + td->cellSpacing + td->border).toReal();
+
+ const int headerRowCount = qMin(table->format().headerRowCount(), table->rows() - 1);
+ if (r >= headerRowCount)
+ topMargin += td->headerHeight.toReal();
+
+ drawBorder(painter, borderRect, topMargin, bottomMargin,
+ border, table->format().borderBrush(), cellBorder);
+
+ painter->setBrush(oldBrush);
+ painter->setPen(oldPen);
+ }
+
+ const QBrush bg = cell.format().background();
+ const QPointF brushOrigin = painter->brushOrigin();
+ if (bg.style() != Qt::NoBrush) {
+ fillBackground(painter, cellRect, bg, cellRect.topLeft());
+
+ if (bg.style() > Qt::SolidPattern)
+ painter->setBrushOrigin(cellRect.topLeft());
+ }
+
+ const QFixed verticalOffset = td->cellVerticalOffsets.at(c + r * table->columns());
+
+ const QPointF cellPos = QPointF(cellRect.left() + leftPadding.toReal(),
+ cellRect.top() + (topPadding + verticalOffset).toReal());
+
+ QTextBlock repaintBlock;
+ drawFlow(cellPos, painter, cell_context, cell.begin(),
+ td->childFrameMap.values(r + c * table->rows()),
+ &repaintBlock);
+ if (repaintBlock.isValid()) {
+ *cursorBlockNeedingRepaint = repaintBlock;
+ *cursorBlockOffset = cellPos;
+ }
+
+ if (bg.style() > Qt::SolidPattern)
+ painter->setBrushOrigin(brushOrigin);
+}
+
+void QTextDocumentLayoutPrivate::drawFlow(const QPointF &offset, QPainter *painter, const QAbstractTextDocumentLayout::PaintContext &context,
+ QTextFrame::Iterator it, const QList<QTextFrame *> &floats, QTextBlock *cursorBlockNeedingRepaint) const
+{
+ Q_Q(const QTextDocumentLayout);
+ const bool inRootFrame = (!it.atEnd() && it.parentFrame() && it.parentFrame()->parentFrame() == 0);
+
+ QVector<QCheckPoint>::ConstIterator lastVisibleCheckPoint = checkPoints.end();
+ if (inRootFrame && context.clip.isValid()) {
+ lastVisibleCheckPoint = qLowerBound(checkPoints.begin(), checkPoints.end(), QFixed::fromReal(context.clip.bottom()));
+ }
+
+ QTextBlock previousBlock;
+ QTextFrame *previousFrame = 0;
+
+ for (; !it.atEnd(); ++it) {
+ QTextFrame *c = it.currentFrame();
+
+ if (inRootFrame && !checkPoints.isEmpty()) {
+ int currentPosInDoc;
+ if (c)
+ currentPosInDoc = c->firstPosition();
+ else
+ currentPosInDoc = it.currentBlock().position();
+
+ // if we're past what is already laid out then we're better off
+ // not trying to draw things that may not be positioned correctly yet
+ if (currentPosInDoc >= checkPoints.last().positionInFrame)
+ break;
+
+ if (lastVisibleCheckPoint != checkPoints.end()
+ && context.clip.isValid()
+ && currentPosInDoc >= lastVisibleCheckPoint->positionInFrame
+ )
+ break;
+ }
+
+ if (c)
+ drawFrame(offset, painter, context, c);
+ else {
+ QAbstractTextDocumentLayout::PaintContext pc = context;
+ if (isEmptyBlockAfterTable(it.currentBlock(), previousFrame))
+ pc.selections.clear();
+ drawBlock(offset, painter, pc, it.currentBlock(), inRootFrame);
+ }
+
+ // when entering a table and the previous block is empty
+ // then layoutFlow 'hides' the block that just causes a
+ // new line by positioning it /on/ the table border. as we
+ // draw that block before the table itself the decoration
+ // 'overpaints' the cursor and we need to paint it afterwards
+ // again
+ if (isEmptyBlockBeforeTable(previousBlock, previousBlock.blockFormat(), it)
+ && previousBlock.contains(context.cursorPosition)
+ ) {
+ *cursorBlockNeedingRepaint = previousBlock;
+ }
+
+ previousBlock = it.currentBlock();
+ previousFrame = c;
+ }
+
+ for (int i = 0; i < floats.count(); ++i) {
+ QTextFrame *frame = floats.at(i);
+ if (!isFrameFromInlineObject(frame)
+ || frame->frameFormat().position() == QTextFrameFormat::InFlow)
+ continue;
+
+ const int pos = frame->firstPosition() - 1;
+ QTextCharFormat format = const_cast<QTextDocumentLayout *>(q)->format(pos);
+ QTextObjectInterface *handler = q->handlerForObject(format.objectType());
+ if (handler) {
+ QRectF rect = frameBoundingRectInternal(frame);
+ handler->drawObject(painter, rect, document, pos, format);
+ }
+ }
+}
+
+void QTextDocumentLayoutPrivate::drawBlock(const QPointF &offset, QPainter *painter,
+ const QAbstractTextDocumentLayout::PaintContext &context,
+ QTextBlock bl, bool inRootFrame) const
+{
+ const QTextLayout *tl = bl.layout();
+ QRectF r = tl->boundingRect();
+ r.translate(offset + tl->position());
+ if (context.clip.isValid() && (r.bottom() < context.clip.y() || r.top() > context.clip.bottom()))
+ return;
+// LDEBUG << debug_indent << "drawBlock" << bl.position() << "at" << offset << "br" << tl->boundingRect();
+
+ QTextBlockFormat blockFormat = bl.blockFormat();
+
+ QBrush bg = blockFormat.background();
+ if (bg != Qt::NoBrush) {
+ QRectF rect = r;
+
+ // extend the background rectangle if we're in the root frame with NoWrap,
+ // as the rect of the text block will then be only the width of the text
+ // instead of the full page width
+ if (inRootFrame && document->pageSize().width() <= 0) {
+ const QTextFrameData *fd = data(document->rootFrame());
+ rect.setRight((fd->size.width - fd->rightMargin).toReal());
+ }
+
+ fillBackground(painter, rect, bg, r.topLeft());
+ }
+
+ QVector<QTextLayout::FormatRange> selections;
+ int blpos = bl.position();
+ int bllen = bl.length();
+ const QTextCharFormat *selFormat = 0;
+ for (int i = 0; i < context.selections.size(); ++i) {
+ const QAbstractTextDocumentLayout::Selection &range = context.selections.at(i);
+ const int selStart = range.cursor.selectionStart() - blpos;
+ const int selEnd = range.cursor.selectionEnd() - blpos;
+ if (selStart < bllen && selEnd > 0
+ && selEnd > selStart) {
+ QTextLayout::FormatRange o;
+ o.start = selStart;
+ o.length = selEnd - selStart;
+ o.format = range.format;
+ selections.append(o);
+ } else if (! range.cursor.hasSelection() && range.format.hasProperty(QTextFormat::FullWidthSelection)
+ && bl.contains(range.cursor.position())) {
+ // for full width selections we don't require an actual selection, just
+ // a position to specify the line. that's more convenience in usage.
+ QTextLayout::FormatRange o;
+ QTextLine l = tl->lineForTextPosition(range.cursor.position() - blpos);
+ o.start = l.textStart();
+ o.length = l.textLength();
+ if (o.start + o.length == bllen - 1)
+ ++o.length; // include newline
+ o.format = range.format;
+ selections.append(o);
+ }
+ if (selStart < 0 && selEnd >= 1)
+ selFormat = &range.format;
+ }
+
+ QTextObject *object = document->objectForFormat(bl.blockFormat());
+ if (object && object->format().toListFormat().style() != QTextListFormat::ListStyleUndefined)
+ drawListItem(offset, painter, context, bl, selFormat);
+
+ QPen oldPen = painter->pen();
+ painter->setPen(context.palette.color(QPalette::Text));
+
+ tl->draw(painter, offset, selections, context.clip.isValid() ? (context.clip & clipRect) : clipRect);
+
+ if ((context.cursorPosition >= blpos && context.cursorPosition < blpos + bllen)
+ || (context.cursorPosition < -1 && !tl->preeditAreaText().isEmpty())) {
+ int cpos = context.cursorPosition;
+ if (cpos < -1)
+ cpos = tl->preeditAreaPosition() - (cpos + 2);
+ else
+ cpos -= blpos;
+ tl->drawCursor(painter, offset, cpos, cursorWidth);
+ }
+
+ if (blockFormat.hasProperty(QTextFormat::BlockTrailingHorizontalRulerWidth)) {
+ const qreal width = blockFormat.lengthProperty(QTextFormat::BlockTrailingHorizontalRulerWidth).value(r.width());
+ painter->setPen(context.palette.color(QPalette::Dark));
+ qreal y = r.bottom();
+ if (bl.length() == 1)
+ y = r.top() + r.height() / 2;
+
+ const qreal middleX = r.left() + r.width() / 2;
+ painter->drawLine(QLineF(middleX - width / 2, y, middleX + width / 2, y));
+ }
+
+ painter->setPen(oldPen);
+}
+
+
+void QTextDocumentLayoutPrivate::drawListItem(const QPointF &offset, QPainter *painter,
+ const QAbstractTextDocumentLayout::PaintContext &context,
+ QTextBlock bl, const QTextCharFormat *selectionFormat) const
+{
+ Q_Q(const QTextDocumentLayout);
+ const QTextBlockFormat blockFormat = bl.blockFormat();
+ const QTextCharFormat charFormat = QTextCursor(bl).charFormat();
+ QFont font(charFormat.font());
+ if (q->paintDevice())
+ font = QFont(font, q->paintDevice());
+
+ const QFontMetrics fontMetrics(font);
+ QTextObject * const object = document->objectForFormat(blockFormat);
+ const QTextListFormat lf = object->format().toListFormat();
+ int style = lf.style();
+ QString itemText;
+ QSizeF size;
+
+ if (blockFormat.hasProperty(QTextFormat::ListStyle))
+ style = QTextListFormat::Style(blockFormat.intProperty(QTextFormat::ListStyle));
+
+ QTextLayout *layout = bl.layout();
+ if (layout->lineCount() == 0)
+ return;
+ QTextLine firstLine = layout->lineAt(0);
+ Q_ASSERT(firstLine.isValid());
+ QPointF pos = (offset + layout->position()).toPoint();
+ Qt::LayoutDirection dir = docPrivate->defaultTextOption.textDirection();
+ if (blockFormat.hasProperty(QTextFormat::LayoutDirection))
+ dir = blockFormat.layoutDirection();
+ {
+ QRectF textRect = firstLine.naturalTextRect();
+ pos += textRect.topLeft().toPoint();
+ if (dir == Qt::RightToLeft)
+ pos.rx() += textRect.width();
+ }
+
+ switch (style) {
+ case QTextListFormat::ListDecimal:
+ case QTextListFormat::ListLowerAlpha:
+ case QTextListFormat::ListUpperAlpha:
+ itemText = static_cast<QTextList *>(object)->itemText(bl);
+ size.setWidth(fontMetrics.width(itemText));
+ size.setHeight(fontMetrics.height());
+ break;
+
+ case QTextListFormat::ListSquare:
+ case QTextListFormat::ListCircle:
+ case QTextListFormat::ListDisc:
+ size.setWidth(fontMetrics.lineSpacing() / 3);
+ size.setHeight(size.width());
+ break;
+
+ case QTextListFormat::ListStyleUndefined:
+ return;
+ default: return;
+ }
+
+ QRectF r(pos, size);
+
+ qreal xoff = fontMetrics.width(QLatin1Char(' '));
+ if (dir == Qt::LeftToRight)
+ xoff = -xoff - size.width();
+ r.translate( xoff, (fontMetrics.height() / 2 - size.height() / 2));
+
+ painter->save();
+
+ painter->setRenderHint(QPainter::Antialiasing);
+
+ if (selectionFormat) {
+ painter->setPen(QPen(selectionFormat->foreground(), 0));
+ painter->fillRect(r, selectionFormat->background());
+ } else {
+ QBrush fg = charFormat.foreground();
+ if (fg == Qt::NoBrush)
+ fg = context.palette.text();
+ painter->setPen(QPen(fg, 0));
+ }
+
+ QBrush brush = context.palette.brush(QPalette::Text);
+
+ switch (style) {
+ case QTextListFormat::ListDecimal:
+ case QTextListFormat::ListLowerAlpha:
+ case QTextListFormat::ListUpperAlpha: {
+ QTextLayout layout(itemText, font, q->paintDevice());
+ layout.setCacheEnabled(true);
+ QTextOption option(Qt::AlignLeft | Qt::AlignAbsolute);
+ option.setTextDirection(dir);
+ layout.setTextOption(option);
+ layout.beginLayout();
+ layout.createLine();
+ layout.endLayout();
+ layout.draw(painter, QPointF(r.left(), pos.y()));
+ break;
+ }
+ case QTextListFormat::ListSquare:
+ painter->fillRect(r, brush);
+ break;
+ case QTextListFormat::ListCircle:
+ painter->drawEllipse(r);
+ break;
+ case QTextListFormat::ListDisc:
+ painter->setBrush(brush);
+ painter->setPen(Qt::NoPen);
+ painter->drawEllipse(r);
+ painter->setBrush(Qt::NoBrush);
+ break;
+ case QTextListFormat::ListStyleUndefined:
+ break;
+ default:
+ break;
+ }
+
+ painter->restore();
+}
+
+static QFixed flowPosition(const QTextFrame::iterator it)
+{
+ if (it.atEnd())
+ return 0;
+
+ if (it.currentFrame()) {
+ return data(it.currentFrame())->position.y;
+ } else {
+ QTextBlock block = it.currentBlock();
+ QTextLayout *layout = block.layout();
+ if (layout->lineCount() == 0)
+ return QFixed::fromReal(layout->position().y());
+ else
+ return QFixed::fromReal(layout->position().y() + layout->lineAt(0).y());
+ }
+}
+
+static QFixed firstChildPos(const QTextFrame *f)
+{
+ return flowPosition(f->begin());
+}
+
+QLayoutStruct QTextDocumentLayoutPrivate::layoutCell(QTextTable *t, const QTextTableCell &cell, QFixed width,
+ int layoutFrom, int layoutTo, QTextTableData *td,
+ QFixed absoluteTableY, bool withPageBreaks)
+{
+ LDEBUG << "layoutCell";
+ QLayoutStruct layoutStruct;
+ layoutStruct.frame = t;
+ layoutStruct.minimumWidth = 0;
+ layoutStruct.maximumWidth = QFIXED_MAX;
+ layoutStruct.y = 0;
+
+ const QTextFormat fmt = cell.format();
+ const QFixed topPadding = td->topPadding(fmt);
+ if (withPageBreaks) {
+ layoutStruct.frameY = absoluteTableY + td->rowPositions.at(cell.row()) + topPadding;
+ }
+ layoutStruct.x_left = 0;
+ layoutStruct.x_right = width;
+ // we get called with different widths all the time (for example for figuring
+ // out the min/max widths), so we always have to do the full layout ;(
+ // also when for example in a table layoutFrom/layoutTo affect only one cell,
+ // making that one cell grow the available width of the other cells may change
+ // (shrink) and therefore when layoutCell gets called for them they have to
+ // be re-laid out, even if layoutFrom/layoutTo is not in their range. Hence
+ // this line:
+
+ layoutStruct.pageHeight = QFixed::fromReal(document->pageSize().height());
+ if (layoutStruct.pageHeight < 0 || !withPageBreaks)
+ layoutStruct.pageHeight = QFIXED_MAX;
+ const int currentPage = layoutStruct.currentPage();
+ layoutStruct.pageTopMargin = td->effectiveTopMargin + td->cellSpacing + td->border + topPadding;
+ layoutStruct.pageBottomMargin = td->effectiveBottomMargin + td->cellSpacing + td->border + td->bottomPadding(fmt);
+ layoutStruct.pageBottom = (currentPage + 1) * layoutStruct.pageHeight - layoutStruct.pageBottomMargin;
+
+ layoutStruct.fullLayout = true;
+
+ QFixed pageTop = currentPage * layoutStruct.pageHeight + layoutStruct.pageTopMargin - layoutStruct.frameY;
+ layoutStruct.y = qMax(layoutStruct.y, pageTop);
+
+ const QList<QTextFrame *> childFrames = td->childFrameMap.values(cell.row() + cell.column() * t->rows());
+ for (int i = 0; i < childFrames.size(); ++i) {
+ QTextFrame *frame = childFrames.at(i);
+ QTextFrameData *cd = data(frame);
+ cd->sizeDirty = true;
+ }
+
+ layoutFlow(cell.begin(), &layoutStruct, layoutFrom, layoutTo, width);
+
+ QFixed floatMinWidth;
+
+ // floats that are located inside the text (like inline images) aren't taken into account by
+ // layoutFlow with regards to the cell height (layoutStruct->y), so for a safety measure we
+ // do that here. For example with <td><img align="right" src="..." />blah</td>
+ // when the image happens to be higher than the text
+ for (int i = 0; i < childFrames.size(); ++i) {
+ QTextFrame *frame = childFrames.at(i);
+ QTextFrameData *cd = data(frame);
+
+ if (frame->frameFormat().position() != QTextFrameFormat::InFlow)
+ layoutStruct.y = qMax(layoutStruct.y, cd->position.y + cd->size.height);
+
+ floatMinWidth = qMax(floatMinWidth, cd->minimumWidth);
+ }
+
+ // constraint the maximumWidth by the minimum width of the fixed size floats, to
+ // keep them visible
+ layoutStruct.maximumWidth = qMax(layoutStruct.maximumWidth, floatMinWidth);
+
+ // as floats in cells get added to the table's float list but must not affect
+ // floats in other cells we must clear the list here.
+ data(t)->floats.clear();
+
+// qDebug() << "layoutCell done";
+
+ return layoutStruct;
+}
+
+QRectF QTextDocumentLayoutPrivate::layoutTable(QTextTable *table, int layoutFrom, int layoutTo, QFixed parentY)
+{
+ LDEBUG << "layoutTable";
+ QTextTableData *td = static_cast<QTextTableData *>(data(table));
+ Q_ASSERT(td->sizeDirty);
+ const int rows = table->rows();
+ const int columns = table->columns();
+
+ const QTextTableFormat fmt = table->format();
+
+ td->childFrameMap.clear();
+ {
+ const QList<QTextFrame *> children = table->childFrames();
+ for (int i = 0; i < children.count(); ++i) {
+ QTextFrame *frame = children.at(i);
+ QTextTableCell cell = table->cellAt(frame->firstPosition());
+ td->childFrameMap.insertMulti(cell.row() + cell.column() * rows, frame);
+ }
+ }
+
+ QVector<QTextLength> columnWidthConstraints = fmt.columnWidthConstraints();
+ if (columnWidthConstraints.size() != columns)
+ columnWidthConstraints.resize(columns);
+ Q_ASSERT(columnWidthConstraints.count() == columns);
+
+ const QFixed cellSpacing = td->cellSpacing = QFixed::fromReal(scaleToDevice(fmt.cellSpacing()));
+ td->deviceScale = scaleToDevice(qreal(1));
+ td->cellPadding = QFixed::fromReal(scaleToDevice(fmt.cellPadding()));
+ const QFixed leftMargin = td->leftMargin + td->border + td->padding;
+ const QFixed rightMargin = td->rightMargin + td->border + td->padding;
+ const QFixed topMargin = td->topMargin + td->border + td->padding;
+
+ const QFixed absoluteTableY = parentY + td->position.y;
+
+ const QTextOption::WrapMode oldDefaultWrapMode = docPrivate->defaultTextOption.wrapMode();
+
+recalc_minmax_widths:
+
+ QFixed remainingWidth = td->contentsWidth;
+ // two (vertical) borders per cell per column
+ remainingWidth -= columns * 2 * td->border;
+ // inter-cell spacing
+ remainingWidth -= (columns - 1) * cellSpacing;
+ // cell spacing at the left and right hand side
+ remainingWidth -= 2 * cellSpacing;
+ // remember the width used to distribute to percentaged columns
+ const QFixed initialTotalWidth = remainingWidth;
+
+ td->widths.resize(columns);
+ td->widths.fill(0);
+
+ td->minWidths.resize(columns);
+ // start with a minimum width of 0. totally empty
+ // cells of default created tables are invisible otherwise
+ // and therefore hardly editable
+ td->minWidths.fill(1);
+
+ td->maxWidths.resize(columns);
+ td->maxWidths.fill(QFIXED_MAX);
+
+ // calculate minimum and maximum sizes of the columns
+ for (int i = 0; i < columns; ++i) {
+ for (int row = 0; row < rows; ++row) {
+ const QTextTableCell cell = table->cellAt(row, i);
+ const int cspan = cell.columnSpan();
+
+ if (cspan > 1 && i != cell.column())
+ continue;
+
+ const QTextFormat fmt = cell.format();
+ const QFixed leftPadding = td->leftPadding(fmt);
+ const QFixed rightPadding = td->rightPadding(fmt);
+ const QFixed widthPadding = leftPadding + rightPadding;
+
+ // to figure out the min and the max width lay out the cell at
+ // maximum width. otherwise the maxwidth calculation sometimes
+ // returns wrong values
+ QLayoutStruct layoutStruct = layoutCell(table, cell, QFIXED_MAX, layoutFrom,
+ layoutTo, td, absoluteTableY,
+ /*withPageBreaks =*/false);
+
+ // distribute the minimum width over all columns the cell spans
+ QFixed widthToDistribute = layoutStruct.minimumWidth + widthPadding;
+ for (int n = 0; n < cspan; ++n) {
+ const int col = i + n;
+ QFixed w = widthToDistribute / (cspan - n);
+ td->minWidths[col] = qMax(td->minWidths.at(col), w);
+ widthToDistribute -= td->minWidths.at(col);
+ if (widthToDistribute <= 0)
+ break;
+ }
+
+ QFixed maxW = td->maxWidths.at(i);
+ if (layoutStruct.maximumWidth != QFIXED_MAX) {
+ if (maxW == QFIXED_MAX)
+ maxW = layoutStruct.maximumWidth + widthPadding;
+ else
+ maxW = qMax(maxW, layoutStruct.maximumWidth + widthPadding);
+ }
+ if (maxW == QFIXED_MAX)
+ continue;
+
+ widthToDistribute = maxW;
+ for (int n = 0; n < cspan; ++n) {
+ const int col = i + n;
+ QFixed w = widthToDistribute / (cspan - n);
+ td->maxWidths[col] = qMax(td->minWidths.at(col), w);
+ widthToDistribute -= td->maxWidths.at(col);
+ if (widthToDistribute <= 0)
+ break;
+ }
+ }
+ }
+
+ // set fixed values, figure out total percentages used and number of
+ // variable length cells. Also assign the minimum width for variable columns.
+ QFixed totalPercentage;
+ int variableCols = 0;
+ QFixed totalMinWidth = 0;
+ for (int i = 0; i < columns; ++i) {
+ const QTextLength &length = columnWidthConstraints.at(i);
+ if (length.type() == QTextLength::FixedLength) {
+ td->minWidths[i] = td->widths[i] = qMax(scaleToDevice(QFixed::fromReal(length.rawValue())), td->minWidths.at(i));
+ remainingWidth -= td->widths.at(i);
+ } else if (length.type() == QTextLength::PercentageLength) {
+ totalPercentage += QFixed::fromReal(length.rawValue());
+ } else if (length.type() == QTextLength::VariableLength) {
+ variableCols++;
+
+ td->widths[i] = td->minWidths.at(i);
+ remainingWidth -= td->minWidths.at(i);
+ }
+ totalMinWidth += td->minWidths.at(i);
+ }
+
+ // set percentage values
+ {
+ const QFixed totalPercentagedWidth = initialTotalWidth * totalPercentage / 100;
+ QFixed remainingMinWidths = totalMinWidth;
+ for (int i = 0; i < columns; ++i) {
+ remainingMinWidths -= td->minWidths.at(i);
+ if (columnWidthConstraints.at(i).type() == QTextLength::PercentageLength) {
+ const QFixed allottedPercentage = QFixed::fromReal(columnWidthConstraints.at(i).rawValue());
+
+ const QFixed percentWidth = totalPercentagedWidth * allottedPercentage / totalPercentage;
+ if (percentWidth >= td->minWidths.at(i)) {
+ td->widths[i] = qBound(td->minWidths.at(i), percentWidth, remainingWidth - remainingMinWidths);
+ } else {
+ td->widths[i] = td->minWidths.at(i);
+ }
+ remainingWidth -= td->widths.at(i);
+ }
+ }
+ }
+
+ // for variable columns distribute the remaining space
+ if (variableCols > 0 && remainingWidth > 0) {
+ QVarLengthArray<int> columnsWithProperMaxSize;
+ for (int i = 0; i < columns; ++i)
+ if (columnWidthConstraints.at(i).type() == QTextLength::VariableLength
+ && td->maxWidths.at(i) != QFIXED_MAX)
+ columnsWithProperMaxSize.append(i);
+
+ QFixed lastRemainingWidth = remainingWidth;
+ while (remainingWidth > 0) {
+ for (int k = 0; k < columnsWithProperMaxSize.count(); ++k) {
+ const int col = columnsWithProperMaxSize[k];
+ const int colsLeft = columnsWithProperMaxSize.count() - k;
+ const QFixed w = qMin(td->maxWidths.at(col) - td->widths.at(col), remainingWidth / colsLeft);
+ td->widths[col] += w;
+ remainingWidth -= w;
+ }
+ if (remainingWidth == lastRemainingWidth)
+ break;
+ lastRemainingWidth = remainingWidth;
+ }
+
+ if (remainingWidth > 0
+ // don't unnecessarily grow variable length sized tables
+ && fmt.width().type() != QTextLength::VariableLength) {
+ const QFixed widthPerAnySizedCol = remainingWidth / variableCols;
+ for (int col = 0; col < columns; ++col) {
+ if (columnWidthConstraints.at(col).type() == QTextLength::VariableLength)
+ td->widths[col] += widthPerAnySizedCol;
+ }
+ }
+ }
+
+ td->columnPositions.resize(columns);
+ td->columnPositions[0] = leftMargin /*includes table border*/ + cellSpacing + td->border;
+
+ for (int i = 1; i < columns; ++i)
+ td->columnPositions[i] = td->columnPositions.at(i-1) + td->widths.at(i-1) + 2 * td->border + cellSpacing;
+
+ // - margin to compensate the + margin in columnPositions[0]
+ const QFixed contentsWidth = td->columnPositions.last() + td->widths.last() + td->padding + td->border + cellSpacing - leftMargin;
+
+ // if the table is too big and causes an overflow re-do the layout with WrapAnywhere as wrap
+ // mode
+ if (docPrivate->defaultTextOption.wrapMode() == QTextOption::WrapAtWordBoundaryOrAnywhere
+ && contentsWidth > td->contentsWidth) {
+ docPrivate->defaultTextOption.setWrapMode(QTextOption::WrapAnywhere);
+ // go back to the top of the function
+ goto recalc_minmax_widths;
+ }
+
+ td->contentsWidth = contentsWidth;
+
+ docPrivate->defaultTextOption.setWrapMode(oldDefaultWrapMode);
+
+ td->heights.resize(rows);
+ td->heights.fill(0);
+
+ td->rowPositions.resize(rows);
+ td->rowPositions[0] = topMargin /*includes table border*/ + cellSpacing + td->border;
+
+ bool haveRowSpannedCells = false;
+
+ // need to keep track of cell heights for vertical alignment
+ QVector<QFixed> cellHeights;
+ cellHeights.reserve(rows * columns);
+
+ QFixed pageHeight = QFixed::fromReal(document->pageSize().height());
+ if (pageHeight <= 0)
+ pageHeight = QFIXED_MAX;
+
+ QVector<QFixed> heightToDistribute;
+ heightToDistribute.resize(columns);
+
+ td->headerHeight = 0;
+ const int headerRowCount = qMin(table->format().headerRowCount(), rows - 1);
+ const QFixed originalTopMargin = td->effectiveTopMargin;
+ bool hasDroppedTable = false;
+
+ // now that we have the column widths we can lay out all cells with the right width.
+ // spanning cells are only allowed to grow the last row spanned by the cell.
+ //
+ // ### this could be made faster by iterating over the cells array of QTextTable
+ for (int r = 0; r < rows; ++r) {
+ td->calcRowPosition(r);
+
+ const int tableStartPage = (absoluteTableY / pageHeight).truncate();
+ const int currentPage = ((td->rowPositions[r] + absoluteTableY) / pageHeight).truncate();
+ const QFixed pageBottom = (currentPage + 1) * pageHeight - td->effectiveBottomMargin - absoluteTableY - cellSpacing - td->border;
+ const QFixed pageTop = currentPage * pageHeight + td->effectiveTopMargin - absoluteTableY + cellSpacing + td->border;
+ const QFixed nextPageTop = pageTop + pageHeight;
+
+ if (td->rowPositions[r] > pageBottom)
+ td->rowPositions[r] = nextPageTop;
+ else if (td->rowPositions[r] < pageTop)
+ td->rowPositions[r] = pageTop;
+
+ bool dropRowToNextPage = true;
+ int cellCountBeforeRow = cellHeights.size();
+
+ // if we drop the row to the next page we need to subtract the drop
+ // distance from any row spanning cells
+ QFixed dropDistance = 0;
+
+relayout:
+ const int rowStartPage = ((td->rowPositions[r] + absoluteTableY) / pageHeight).truncate();
+ // if any of the header rows or the first non-header row start on the next page
+ // then the entire header should be dropped
+ if (r <= headerRowCount && rowStartPage > tableStartPage && !hasDroppedTable) {
+ td->rowPositions[0] = nextPageTop;
+ cellHeights.clear();
+ td->effectiveTopMargin = originalTopMargin;
+ hasDroppedTable = true;
+ r = -1;
+ continue;
+ }
+
+ int rowCellCount = 0;
+ for (int c = 0; c < columns; ++c) {
+ QTextTableCell cell = table->cellAt(r, c);
+ const int rspan = cell.rowSpan();
+ const int cspan = cell.columnSpan();
+
+ if (cspan > 1 && cell.column() != c)
+ continue;
+
+ if (rspan > 1) {
+ haveRowSpannedCells = true;
+
+ const int cellRow = cell.row();
+ if (cellRow != r) {
+ // the last row gets all the remaining space
+ if (cellRow + rspan - 1 == r)
+ td->heights[r] = qMax(td->heights.at(r), heightToDistribute.at(c) - dropDistance);
+ continue;
+ }
+ }
+
+ const QTextFormat fmt = cell.format();
+
+ const QFixed topPadding = td->topPadding(fmt);
+ const QFixed bottomPadding = td->bottomPadding(fmt);
+ const QFixed leftPadding = td->leftPadding(fmt);
+ const QFixed rightPadding = td->rightPadding(fmt);
+ const QFixed widthPadding = leftPadding + rightPadding;
+
+ ++rowCellCount;
+
+ const QFixed width = td->cellWidth(c, cspan) - widthPadding;
+ QLayoutStruct layoutStruct = layoutCell(table, cell, width,
+ layoutFrom, layoutTo,
+ td, absoluteTableY,
+ /*withPageBreaks =*/true);
+
+ const QFixed height = layoutStruct.y + bottomPadding + topPadding;
+
+ if (rspan > 1)
+ heightToDistribute[c] = height + dropDistance;
+ else
+ td->heights[r] = qMax(td->heights.at(r), height);
+
+ cellHeights.append(layoutStruct.y);
+
+ QFixed childPos = td->rowPositions.at(r) + topPadding + flowPosition(cell.begin());
+ if (childPos < pageBottom)
+ dropRowToNextPage = false;
+ }
+
+ if (rowCellCount > 0 && dropRowToNextPage) {
+ dropDistance = nextPageTop - td->rowPositions[r];
+ td->rowPositions[r] = nextPageTop;
+ td->heights[r] = 0;
+ dropRowToNextPage = false;
+ cellHeights.resize(cellCountBeforeRow);
+ if (r > headerRowCount)
+ td->heights[r-1] = pageBottom - td->rowPositions[r-1];
+ goto relayout;
+ }
+
+ if (haveRowSpannedCells) {
+ const QFixed effectiveHeight = td->heights.at(r) + td->border + cellSpacing + td->border;
+ for (int c = 0; c < columns; ++c)
+ heightToDistribute[c] = qMax(heightToDistribute.at(c) - effectiveHeight - dropDistance, QFixed(0));
+ }
+
+ if (r == headerRowCount - 1) {
+ td->headerHeight = td->rowPositions[r] + td->heights[r] - td->rowPositions[0] + td->cellSpacing + 2 * td->border;
+ td->headerHeight -= td->headerHeight * (td->headerHeight / pageHeight).truncate();
+ td->effectiveTopMargin += td->headerHeight;
+ }
+ }
+
+ td->effectiveTopMargin = originalTopMargin;
+
+ // now that all cells have been properly laid out, we can compute the
+ // vertical offsets for vertical alignment
+ td->cellVerticalOffsets.resize(rows * columns);
+ int cellIndex = 0;
+ for (int r = 0; r < rows; ++r) {
+ for (int c = 0; c < columns; ++c) {
+ QTextTableCell cell = table->cellAt(r, c);
+ if (cell.row() != r || cell.column() != c)
+ continue;
+
+ const int rowSpan = cell.rowSpan();
+ const QFixed availableHeight = td->rowPositions.at(r + rowSpan - 1) + td->heights.at(r + rowSpan - 1) - td->rowPositions.at(r);
+
+ const QTextCharFormat cellFormat = cell.format();
+ const QFixed cellHeight = cellHeights.at(cellIndex++) + td->topPadding(cellFormat) + td->bottomPadding(cellFormat);
+
+ QFixed offset = 0;
+ switch (cellFormat.verticalAlignment()) {
+ case QTextCharFormat::AlignMiddle:
+ offset = (availableHeight - cellHeight) / 2;
+ break;
+ case QTextCharFormat::AlignBottom:
+ offset = availableHeight - cellHeight;
+ break;
+ default:
+ break;
+ };
+
+ for (int rd = 0; rd < cell.rowSpan(); ++rd) {
+ for (int cd = 0; cd < cell.columnSpan(); ++cd) {
+ const int index = (c + cd) + (r + rd) * columns;
+ td->cellVerticalOffsets[index] = offset;
+ }
+ }
+ }
+ }
+
+ td->minimumWidth = td->columnPositions.at(0);
+ for (int i = 0; i < columns; ++i) {
+ td->minimumWidth += td->minWidths.at(i) + 2 * td->border + cellSpacing;
+ }
+ td->minimumWidth += rightMargin - td->border;
+
+ td->maximumWidth = td->columnPositions.at(0);
+ for (int i = 0; i < columns; ++i)
+ if (td->maxWidths.at(i) != QFIXED_MAX)
+ td->maximumWidth += td->maxWidths.at(i) + 2 * td->border + cellSpacing;
+ td->maximumWidth += rightMargin - td->border;
+
+ td->updateTableSize();
+ td->sizeDirty = false;
+ return QRectF(); // invalid rect -> update everything
+}
+
+void QTextDocumentLayoutPrivate::positionFloat(QTextFrame *frame, QTextLine *currentLine)
+{
+ QTextFrameData *fd = data(frame);
+
+ QTextFrame *parent = frame->parentFrame();
+ Q_ASSERT(parent);
+ QTextFrameData *pd = data(parent);
+ Q_ASSERT(pd && pd->currentLayoutStruct);
+
+ QLayoutStruct *layoutStruct = pd->currentLayoutStruct;
+
+ if (!pd->floats.contains(frame))
+ pd->floats.append(frame);
+ fd->layoutDirty = true;
+ Q_ASSERT(!fd->sizeDirty);
+
+// qDebug() << "positionFloat:" << frame << "width=" << fd->size.width;
+ QFixed y = layoutStruct->y;
+ if (currentLine) {
+ QFixed left, right;
+ floatMargins(y, layoutStruct, &left, &right);
+// qDebug() << "have line: right=" << right << "left=" << left << "textWidth=" << currentLine->width();
+ if (right - left < QFixed::fromReal(currentLine->naturalTextWidth()) + fd->size.width) {
+ layoutStruct->pendingFloats.append(frame);
+// qDebug() << " adding to pending list";
+ return;
+ }
+ }
+
+ if (y + layoutStruct->frameY + fd->size.height > layoutStruct->pageBottom) {
+ layoutStruct->newPage();
+ y = layoutStruct->y;
+ }
+
+ y = findY(y, layoutStruct, fd->size.width);
+
+ QFixed left, right;
+ floatMargins(y, layoutStruct, &left, &right);
+
+ if (frame->frameFormat().position() == QTextFrameFormat::FloatLeft) {
+ fd->position.x = left;
+ fd->position.y = y;
+ } else {
+ fd->position.x = right - fd->size.width;
+ fd->position.y = y;
+ }
+
+ layoutStruct->minimumWidth = qMax(layoutStruct->minimumWidth, fd->minimumWidth);
+ layoutStruct->maximumWidth = qMin(layoutStruct->maximumWidth, fd->maximumWidth);
+
+// qDebug()<< "float positioned at " << fd->position.x << fd->position.y;
+ fd->layoutDirty = false;
+}
+
+QRectF QTextDocumentLayoutPrivate::layoutFrame(QTextFrame *f, int layoutFrom, int layoutTo, QFixed parentY)
+{
+ LDEBUG << "layoutFrame (pre)";
+ Q_ASSERT(data(f)->sizeDirty);
+// qDebug("layouting frame (%d--%d), parent=%p", f->firstPosition(), f->lastPosition(), f->parentFrame());
+
+ QTextFrameFormat fformat = f->frameFormat();
+
+ QTextFrame *parent = f->parentFrame();
+ const QTextFrameData *pd = parent ? data(parent) : 0;
+
+ const qreal maximumWidth = qMax(qreal(0), pd ? pd->contentsWidth.toReal() : document->pageSize().width());
+ QFixed width = QFixed::fromReal(fformat.width().value(maximumWidth));
+ if (fformat.width().type() == QTextLength::FixedLength)
+ width = scaleToDevice(width);
+
+ const QFixed maximumHeight = pd ? pd->contentsHeight : -1;
+ const QFixed height = (maximumHeight != -1 || fformat.height().type() != QTextLength::PercentageLength)
+ ? QFixed::fromReal(fformat.height().value(maximumHeight.toReal()))
+ : -1;
+
+ return layoutFrame(f, layoutFrom, layoutTo, width, height, parentY);
+}
+
+QRectF QTextDocumentLayoutPrivate::layoutFrame(QTextFrame *f, int layoutFrom, int layoutTo, QFixed frameWidth, QFixed frameHeight, QFixed parentY)
+{
+ LDEBUG << "layoutFrame from=" << layoutFrom << "to=" << layoutTo;
+ Q_ASSERT(data(f)->sizeDirty);
+// qDebug("layouting frame (%d--%d), parent=%p", f->firstPosition(), f->lastPosition(), f->parentFrame());
+
+ QTextFrameData *fd = data(f);
+ QFixed newContentsWidth;
+
+ {
+ QTextFrameFormat fformat = f->frameFormat();
+ // set sizes of this frame from the format
+ fd->topMargin = QFixed::fromReal(fformat.topMargin());
+ fd->bottomMargin = QFixed::fromReal(fformat.bottomMargin());
+ fd->leftMargin = QFixed::fromReal(fformat.leftMargin());
+ fd->rightMargin = QFixed::fromReal(fformat.rightMargin());
+ fd->border = QFixed::fromReal(fformat.border());
+ fd->padding = QFixed::fromReal(fformat.padding());
+
+ QTextFrame *parent = f->parentFrame();
+ const QTextFrameData *pd = parent ? data(parent) : 0;
+
+ // accumulate top and bottom margins
+ if (parent) {
+ fd->effectiveTopMargin = pd->effectiveTopMargin + fd->topMargin + fd->border + fd->padding;
+ fd->effectiveBottomMargin = pd->effectiveBottomMargin + fd->topMargin + fd->border + fd->padding;
+
+ if (qobject_cast<QTextTable *>(parent)) {
+ const QTextTableData *td = static_cast<const QTextTableData *>(pd);
+ fd->effectiveTopMargin += td->cellSpacing + td->border + td->cellPadding;
+ fd->effectiveBottomMargin += td->cellSpacing + td->border + td->cellPadding;
+ }
+ } else {
+ fd->effectiveTopMargin = fd->topMargin + fd->border + fd->padding;
+ fd->effectiveBottomMargin = fd->bottomMargin + fd->border + fd->padding;
+ }
+
+ newContentsWidth = frameWidth - 2*(fd->border + fd->padding)
+ - fd->leftMargin - fd->rightMargin;
+
+ if (frameHeight != -1) {
+ fd->contentsHeight = frameHeight - 2*(fd->border + fd->padding)
+ - fd->topMargin - fd->bottomMargin;
+ } else {
+ fd->contentsHeight = frameHeight;
+ }
+ }
+
+ if (isFrameFromInlineObject(f)) {
+ // never reached, handled in resizeInlineObject/positionFloat instead
+ return QRectF();
+ }
+
+ if (QTextTable *table = qobject_cast<QTextTable *>(f)) {
+ fd->contentsWidth = newContentsWidth;
+ return layoutTable(table, layoutFrom, layoutTo, parentY);
+ }
+
+ // set fd->contentsWidth temporarily, so that layoutFrame for the children
+ // picks the right width. We'll initialize it properly at the end of this
+ // function.
+ fd->contentsWidth = newContentsWidth;
+
+ QLayoutStruct layoutStruct;
+ layoutStruct.frame = f;
+ layoutStruct.x_left = fd->leftMargin + fd->border + fd->padding;
+ layoutStruct.x_right = layoutStruct.x_left + newContentsWidth;
+ layoutStruct.y = fd->topMargin + fd->border + fd->padding;
+ layoutStruct.frameY = parentY + fd->position.y;
+ layoutStruct.contentsWidth = 0;
+ layoutStruct.minimumWidth = 0;
+ layoutStruct.maximumWidth = QFIXED_MAX;
+ layoutStruct.fullLayout = fd->oldContentsWidth != newContentsWidth;
+ layoutStruct.updateRect = QRectF(QPointF(0, 0), QSizeF(qreal(INT_MAX), qreal(INT_MAX)));
+ LDEBUG << "layoutStruct: x_left" << layoutStruct.x_left << "x_right" << layoutStruct.x_right
+ << "fullLayout" << layoutStruct.fullLayout;
+ fd->oldContentsWidth = newContentsWidth;
+
+ layoutStruct.pageHeight = QFixed::fromReal(document->pageSize().height());
+ if (layoutStruct.pageHeight < 0)
+ layoutStruct.pageHeight = QFIXED_MAX;
+
+ const int currentPage = layoutStruct.pageHeight == 0 ? 0 : (layoutStruct.frameY / layoutStruct.pageHeight).truncate();
+ layoutStruct.pageTopMargin = fd->effectiveTopMargin;
+ layoutStruct.pageBottomMargin = fd->effectiveBottomMargin;
+ layoutStruct.pageBottom = (currentPage + 1) * layoutStruct.pageHeight - layoutStruct.pageBottomMargin;
+
+ if (!f->parentFrame())
+ idealWidth = 0; // reset
+
+ QTextFrame::Iterator it = f->begin();
+ layoutFlow(it, &layoutStruct, layoutFrom, layoutTo);
+
+ QFixed maxChildFrameWidth = 0;
+ QList<QTextFrame *> children = f->childFrames();
+ for (int i = 0; i < children.size(); ++i) {
+ QTextFrame *c = children.at(i);
+ QTextFrameData *cd = data(c);
+ maxChildFrameWidth = qMax(maxChildFrameWidth, cd->size.width);
+ }
+
+ const QFixed marginWidth = 2*(fd->border + fd->padding) + fd->leftMargin + fd->rightMargin;
+ if (!f->parentFrame()) {
+ idealWidth = qMax(maxChildFrameWidth, layoutStruct.contentsWidth).toReal();
+ idealWidth += marginWidth.toReal();
+ }
+
+ QFixed actualWidth = qMax(newContentsWidth, qMax(maxChildFrameWidth, layoutStruct.contentsWidth));
+ fd->contentsWidth = actualWidth;
+ if (newContentsWidth <= 0) { // nowrap layout?
+ fd->contentsWidth = newContentsWidth;
+ }
+
+ fd->minimumWidth = layoutStruct.minimumWidth;
+ fd->maximumWidth = layoutStruct.maximumWidth;
+
+ fd->size.height = fd->contentsHeight == -1
+ ? layoutStruct.y + fd->border + fd->padding + fd->bottomMargin
+ : fd->contentsHeight + 2*(fd->border + fd->padding) + fd->topMargin + fd->bottomMargin;
+ fd->size.width = actualWidth + marginWidth;
+ fd->sizeDirty = false;
+ if (layoutStruct.updateRectForFloats.isValid())
+ layoutStruct.updateRect |= layoutStruct.updateRectForFloats;
+ return layoutStruct.updateRect;
+}
+
+void QTextDocumentLayoutPrivate::layoutFlow(QTextFrame::Iterator it, QLayoutStruct *layoutStruct,
+ int layoutFrom, int layoutTo, QFixed width)
+{
+ LDEBUG << "layoutFlow from=" << layoutFrom << "to=" << layoutTo;
+ QTextFrameData *fd = data(layoutStruct->frame);
+
+ fd->currentLayoutStruct = layoutStruct;
+
+ QTextFrame::Iterator previousIt;
+
+ const bool inRootFrame = (it.parentFrame() == document->rootFrame());
+ if (inRootFrame) {
+ bool redoCheckPoints = layoutStruct->fullLayout || checkPoints.isEmpty();
+
+ if (!redoCheckPoints) {
+ QVector<QCheckPoint>::Iterator checkPoint = qLowerBound(checkPoints.begin(), checkPoints.end(), layoutFrom);
+ if (checkPoint != checkPoints.end()) {
+ if (checkPoint != checkPoints.begin())
+ --checkPoint;
+
+ layoutStruct->y = checkPoint->y;
+ layoutStruct->frameY = checkPoint->frameY;
+ layoutStruct->minimumWidth = checkPoint->minimumWidth;
+ layoutStruct->maximumWidth = checkPoint->maximumWidth;
+ layoutStruct->contentsWidth = checkPoint->contentsWidth;
+
+ if (layoutStruct->pageHeight > 0) {
+ int page = layoutStruct->currentPage();
+ layoutStruct->pageBottom = (page + 1) * layoutStruct->pageHeight - layoutStruct->pageBottomMargin;
+ }
+
+ it = frameIteratorForTextPosition(checkPoint->positionInFrame);
+ checkPoints.resize(checkPoint - checkPoints.begin() + 1);
+
+ if (checkPoint != checkPoints.begin()) {
+ previousIt = it;
+ --previousIt;
+ }
+ } else {
+ redoCheckPoints = true;
+ }
+ }
+
+ if (redoCheckPoints) {
+ checkPoints.clear();
+ QCheckPoint cp;
+ cp.y = layoutStruct->y;
+ cp.frameY = layoutStruct->frameY;
+ cp.positionInFrame = 0;
+ cp.minimumWidth = layoutStruct->minimumWidth;
+ cp.maximumWidth = layoutStruct->maximumWidth;
+ cp.contentsWidth = layoutStruct->contentsWidth;
+ checkPoints.append(cp);
+ }
+ }
+
+ QTextBlockFormat previousBlockFormat = previousIt.currentBlock().blockFormat();
+
+ QFixed maximumBlockWidth = 0;
+ while (!it.atEnd()) {
+ QTextFrame *c = it.currentFrame();
+
+ int docPos;
+ if (it.currentFrame())
+ docPos = it.currentFrame()->firstPosition();
+ else
+ docPos = it.currentBlock().position();
+
+ if (inRootFrame) {
+ if (qAbs(layoutStruct->y - checkPoints.last().y) > 2000) {
+ QFixed left, right;
+ floatMargins(layoutStruct->y, layoutStruct, &left, &right);
+ if (left == layoutStruct->x_left && right == layoutStruct->x_right) {
+ QCheckPoint p;
+ p.y = layoutStruct->y;
+ p.frameY = layoutStruct->frameY;
+ p.positionInFrame = docPos;
+ p.minimumWidth = layoutStruct->minimumWidth;
+ p.maximumWidth = layoutStruct->maximumWidth;
+ p.contentsWidth = layoutStruct->contentsWidth;
+ checkPoints.append(p);
+
+ if (currentLazyLayoutPosition != -1
+ && docPos > currentLazyLayoutPosition + lazyLayoutStepSize)
+ break;
+
+ }
+ }
+ }
+
+ if (c) {
+ // position child frame
+ QTextFrameData *cd = data(c);
+
+ QTextFrameFormat fformat = c->frameFormat();
+
+ if (fformat.position() == QTextFrameFormat::InFlow) {
+ if (fformat.pageBreakPolicy() & QTextFormat::PageBreak_AlwaysBefore)
+ layoutStruct->newPage();
+
+ QFixed left, right;
+ floatMargins(layoutStruct->y, layoutStruct, &left, &right);
+ left = qMax(left, layoutStruct->x_left);
+ right = qMin(right, layoutStruct->x_right);
+
+ if (right - left < cd->size.width) {
+ layoutStruct->y = findY(layoutStruct->y, layoutStruct, cd->size.width);
+ floatMargins(layoutStruct->y, layoutStruct, &left, &right);
+ }
+
+ QFixedPoint pos(left, layoutStruct->y);
+
+ Qt::Alignment align = Qt::AlignLeft;
+
+ QTextTable *table = qobject_cast<QTextTable *>(c);
+
+ if (table)
+ align = table->format().alignment() & Qt::AlignHorizontal_Mask;
+
+ // detect whether we have any alignment in the document that disallows optimizations,
+ // such as not laying out the document again in a textedit with wrapping disabled.
+ if (inRootFrame && !(align & Qt::AlignLeft))
+ contentHasAlignment = true;
+
+ cd->position = pos;
+
+ if (document->pageSize().height() > 0.0f)
+ cd->sizeDirty = true;
+
+ if (cd->sizeDirty) {
+ if (width != 0)
+ layoutFrame(c, layoutFrom, layoutTo, width, -1, layoutStruct->frameY);
+ else
+ layoutFrame(c, layoutFrom, layoutTo, layoutStruct->frameY);
+
+ QFixed absoluteChildPos = table ? pos.y + static_cast<QTextTableData *>(data(table))->rowPositions.at(0) : pos.y + firstChildPos(c);
+ absoluteChildPos += layoutStruct->frameY;
+
+ // drop entire frame to next page if first child of frame is on next page
+ if (absoluteChildPos > layoutStruct->pageBottom) {
+ layoutStruct->newPage();
+ pos.y = layoutStruct->y;
+
+ cd->position = pos;
+ cd->sizeDirty = true;
+
+ if (width != 0)
+ layoutFrame(c, layoutFrom, layoutTo, width, -1, layoutStruct->frameY);
+ else
+ layoutFrame(c, layoutFrom, layoutTo, layoutStruct->frameY);
+ }
+ }
+
+ // align only if there is space for alignment
+ if (right - left > cd->size.width) {
+ if (align & Qt::AlignRight)
+ pos.x += layoutStruct->x_right - cd->size.width;
+ else if (align & Qt::AlignHCenter)
+ pos.x += (layoutStruct->x_right - cd->size.width) / 2;
+ }
+
+ cd->position = pos;
+
+ layoutStruct->y += cd->size.height;
+ const int page = layoutStruct->currentPage();
+ layoutStruct->pageBottom = (page + 1) * layoutStruct->pageHeight - layoutStruct->pageBottomMargin;
+
+ cd->layoutDirty = false;
+
+ if (c->frameFormat().pageBreakPolicy() & QTextFormat::PageBreak_AlwaysAfter)
+ layoutStruct->newPage();
+ } else {
+ QRectF oldFrameRect(cd->position.toPointF(), cd->size.toSizeF());
+ QRectF updateRect;
+
+ if (cd->sizeDirty)
+ updateRect = layoutFrame(c, layoutFrom, layoutTo);
+
+ positionFloat(c);
+
+ QRectF frameRect(cd->position.toPointF(), cd->size.toSizeF());
+
+ if (frameRect == oldFrameRect && updateRect.isValid())
+ updateRect.translate(cd->position.toPointF());
+ else
+ updateRect = frameRect;
+
+ layoutStruct->addUpdateRectForFloat(updateRect);
+ if (oldFrameRect.isValid())
+ layoutStruct->addUpdateRectForFloat(oldFrameRect);
+ }
+
+ layoutStruct->minimumWidth = qMax(layoutStruct->minimumWidth, cd->minimumWidth);
+ layoutStruct->maximumWidth = qMin(layoutStruct->maximumWidth, cd->maximumWidth);
+
+ previousIt = it;
+ ++it;
+ } else {
+ QTextFrame::Iterator lastIt;
+ if (!previousIt.atEnd())
+ lastIt = previousIt;
+ previousIt = it;
+ QTextBlock block = it.currentBlock();
+ ++it;
+
+ const QTextBlockFormat blockFormat = block.blockFormat();
+
+ if (blockFormat.pageBreakPolicy() & QTextFormat::PageBreak_AlwaysBefore)
+ layoutStruct->newPage();
+
+ const QFixed origY = layoutStruct->y;
+ const QFixed origPageBottom = layoutStruct->pageBottom;
+ const QFixed origMaximumWidth = layoutStruct->maximumWidth;
+ layoutStruct->maximumWidth = 0;
+
+ const QTextBlockFormat *previousBlockFormatPtr = 0;
+ if (lastIt.currentBlock().isValid())
+ previousBlockFormatPtr = &previousBlockFormat;
+
+ // layout and position child block
+ layoutBlock(block, docPos, blockFormat, layoutStruct, layoutFrom, layoutTo, previousBlockFormatPtr);
+
+ // detect whether we have any alignment in the document that disallows optimizations,
+ // such as not laying out the document again in a textedit with wrapping disabled.
+ if (inRootFrame && !(block.layout()->textOption().alignment() & Qt::AlignLeft))
+ contentHasAlignment = true;
+
+ // if the block right before a table is empty 'hide' it by
+ // positioning it into the table border
+ if (isEmptyBlockBeforeTable(block, blockFormat, it)) {
+ const QTextBlock lastBlock = lastIt.currentBlock();
+ const qreal lastBlockBottomMargin = lastBlock.isValid() ? lastBlock.blockFormat().bottomMargin() : 0.0f;
+ layoutStruct->y = origY + QFixed::fromReal(qMax(lastBlockBottomMargin, block.blockFormat().topMargin()));
+ layoutStruct->pageBottom = origPageBottom;
+ } else {
+ // if the block right after a table is empty then 'hide' it, too
+ if (isEmptyBlockAfterTable(block, lastIt.currentFrame())) {
+ QTextTableData *td = static_cast<QTextTableData *>(data(lastIt.currentFrame()));
+ QTextLayout *layout = block.layout();
+
+ QPointF pos((td->position.x + td->size.width).toReal(),
+ (td->position.y + td->size.height).toReal() - layout->boundingRect().height());
+
+ layout->setPosition(pos);
+ layoutStruct->y = origY;
+ layoutStruct->pageBottom = origPageBottom;
+ }
+
+ // if the block right after a table starts with a line separator, shift it up by one line
+ if (isLineSeparatorBlockAfterTable(block, lastIt.currentFrame())) {
+ QTextTableData *td = static_cast<QTextTableData *>(data(lastIt.currentFrame()));
+ QTextLayout *layout = block.layout();
+
+ QFixed height = QFixed::fromReal(layout->lineAt(0).height());
+
+ if (layoutStruct->pageBottom == origPageBottom) {
+ layoutStruct->y -= height;
+ layout->setPosition(layout->position() - QPointF(0, height.toReal()));
+ } else {
+ // relayout block to correctly handle page breaks
+ layoutStruct->y = origY - height;
+ layoutStruct->pageBottom = origPageBottom;
+ layoutBlock(block, docPos, blockFormat, layoutStruct, layoutFrom, layoutTo, previousBlockFormatPtr);
+ }
+
+ QPointF linePos((td->position.x + td->size.width).toReal(),
+ (td->position.y + td->size.height - height).toReal());
+
+ layout->lineAt(0).setPosition(linePos - layout->position());
+ }
+
+ if (blockFormat.pageBreakPolicy() & QTextFormat::PageBreak_AlwaysAfter)
+ layoutStruct->newPage();
+ }
+
+ maximumBlockWidth = qMax(maximumBlockWidth, layoutStruct->maximumWidth);
+ layoutStruct->maximumWidth = origMaximumWidth;
+ previousBlockFormat = blockFormat;
+ }
+ }
+ if (layoutStruct->maximumWidth == QFIXED_MAX && maximumBlockWidth > 0)
+ layoutStruct->maximumWidth = maximumBlockWidth;
+ else
+ layoutStruct->maximumWidth = qMax(layoutStruct->maximumWidth, maximumBlockWidth);
+
+ // a float at the bottom of a frame may make it taller, hence the qMax() for layoutStruct->y.
+ // we don't need to do it for tables though because floats in tables are per table
+ // and not per cell and layoutCell already takes care of doing the same as we do here
+ if (!qobject_cast<QTextTable *>(layoutStruct->frame)) {
+ QList<QTextFrame *> children = layoutStruct->frame->childFrames();
+ for (int i = 0; i < children.count(); ++i) {
+ QTextFrameData *fd = data(children.at(i));
+ if (!fd->layoutDirty && children.at(i)->frameFormat().position() != QTextFrameFormat::InFlow)
+ layoutStruct->y = qMax(layoutStruct->y, fd->position.y + fd->size.height);
+ }
+ }
+
+ if (inRootFrame) {
+ // we assume that any float is aligned in a way that disallows the optimizations that rely
+ // on unaligned content.
+ if (!fd->floats.isEmpty())
+ contentHasAlignment = true;
+
+ if (it.atEnd()) {
+ //qDebug() << "layout done!";
+ currentLazyLayoutPosition = -1;
+ QCheckPoint cp;
+ cp.y = layoutStruct->y;
+ cp.positionInFrame = docPrivate->length();
+ cp.minimumWidth = layoutStruct->minimumWidth;
+ cp.maximumWidth = layoutStruct->maximumWidth;
+ cp.contentsWidth = layoutStruct->contentsWidth;
+ checkPoints.append(cp);
+ checkPoints.reserve(checkPoints.size());
+ } else {
+ currentLazyLayoutPosition = checkPoints.last().positionInFrame;
+ // #######
+ //checkPoints.last().positionInFrame = q->document()->docHandle()->length();
+ }
+ }
+
+
+ fd->currentLayoutStruct = 0;
+}
+
+void QTextDocumentLayoutPrivate::layoutBlock(const QTextBlock &bl, int blockPosition, const QTextBlockFormat &blockFormat,
+ QLayoutStruct *layoutStruct, int layoutFrom, int layoutTo, const QTextBlockFormat *previousBlockFormat)
+{
+ Q_Q(QTextDocumentLayout);
+
+ QTextLayout *tl = bl.layout();
+ const int blockLength = bl.length();
+
+ LDEBUG << "layoutBlock from=" << layoutFrom << "to=" << layoutTo;
+
+// qDebug() << "layoutBlock; width" << layoutStruct->x_right - layoutStruct->x_left << "(maxWidth is btw" << tl->maximumWidth() << ")";
+
+ if (previousBlockFormat) {
+ qreal margin = qMax(blockFormat.topMargin(), previousBlockFormat->bottomMargin());
+ if (margin > 0 && q->paintDevice()) {
+ margin *= qreal(q->paintDevice()->logicalDpiY()) / qreal(qt_defaultDpi());
+ }
+ layoutStruct->y += QFixed::fromReal(margin);
+ }
+
+ //QTextFrameData *fd = data(layoutStruct->frame);
+
+ Qt::LayoutDirection dir = docPrivate->defaultTextOption.textDirection();
+ if (blockFormat.hasProperty(QTextFormat::LayoutDirection))
+ dir = blockFormat.layoutDirection();
+
+ QFixed extraMargin;
+ if (docPrivate->defaultTextOption.flags() & QTextOption::AddSpaceForLineAndParagraphSeparators) {
+ QFontMetricsF fm(bl.charFormat().font());
+ extraMargin = QFixed::fromReal(fm.width(QChar(QChar(0x21B5))));
+ }
+
+ const QFixed indent = this->blockIndent(blockFormat);
+ const QFixed totalLeftMargin = QFixed::fromReal(blockFormat.leftMargin()) + (dir == Qt::RightToLeft ? extraMargin : indent);
+ const QFixed totalRightMargin = QFixed::fromReal(blockFormat.rightMargin()) + (dir == Qt::RightToLeft ? indent : extraMargin);
+
+ const QPointF oldPosition = tl->position();
+ tl->setPosition(QPointF(layoutStruct->x_left.toReal(), layoutStruct->y.toReal()));
+
+ if (layoutStruct->fullLayout
+ || (blockPosition + blockLength > layoutFrom && blockPosition <= layoutTo)
+ // force relayout if we cross a page boundary
+ || (layoutStruct->pageHeight != QFIXED_MAX && layoutStruct->absoluteY() + QFixed::fromReal(tl->boundingRect().height()) > layoutStruct->pageBottom)) {
+
+ LDEBUG << " do layout";
+ QTextOption option = docPrivate->defaultTextOption;
+ option.setTextDirection(dir);
+ option.setTabs( blockFormat.tabPositions() );
+
+ Qt::Alignment align = docPrivate->defaultTextOption.alignment();
+ if (blockFormat.hasProperty(QTextFormat::BlockAlignment))
+ align = blockFormat.alignment();
+ option.setAlignment(QStyle::visualAlignment(dir, align)); // for paragraph that are RTL, alignment is auto-reversed;
+
+ if (blockFormat.nonBreakableLines() || document->pageSize().width() < 0) {
+ option.setWrapMode(QTextOption::ManualWrap);
+ }
+
+ tl->setTextOption(option);
+
+ const bool haveWordOrAnyWrapMode = (option.wrapMode() == QTextOption::WrapAtWordBoundaryOrAnywhere);
+
+// qDebug() << " layouting block at" << bl.position();
+ const QFixed cy = layoutStruct->y;
+ const QFixed l = layoutStruct->x_left + totalLeftMargin;
+ const QFixed r = layoutStruct->x_right - totalRightMargin;
+
+ tl->beginLayout();
+ bool firstLine = true;
+ while (1) {
+ QTextLine line = tl->createLine();
+ if (!line.isValid())
+ break;
+
+ QFixed left, right;
+ floatMargins(layoutStruct->y, layoutStruct, &left, &right);
+ left = qMax(left, l);
+ right = qMin(right, r);
+ QFixed text_indent;
+ if (firstLine) {
+ text_indent = QFixed::fromReal(blockFormat.textIndent());
+ if (dir == Qt::LeftToRight)
+ left += text_indent;
+ else
+ right -= text_indent;
+ firstLine = false;
+ }
+// qDebug() << "layout line y=" << currentYPos << "left=" << left << "right=" <<right;
+
+ if (fixedColumnWidth != -1)
+ line.setNumColumns(fixedColumnWidth, (right - left).toReal());
+ else
+ line.setLineWidth((right - left).toReal());
+
+// qDebug() << "layoutBlock; layouting line with width" << right - left << "->textWidth" << line.textWidth();
+ floatMargins(layoutStruct->y, layoutStruct, &left, &right);
+ left = qMax(left, l);
+ right = qMin(right, r);
+ if (dir == Qt::LeftToRight)
+ left += text_indent;
+ else
+ right -= text_indent;
+
+ if (fixedColumnWidth == -1 && QFixed::fromReal(line.naturalTextWidth()) > right-left) {
+ // float has been added in the meantime, redo
+ layoutStruct->pendingFloats.clear();
+
+ if (haveWordOrAnyWrapMode) {
+ option.setWrapMode(QTextOption::WrapAnywhere);
+ tl->setTextOption(option);
+ }
+
+ line.setLineWidth((right-left).toReal());
+ if (QFixed::fromReal(line.naturalTextWidth()) > right-left) {
+ layoutStruct->pendingFloats.clear();
+ // lines min width more than what we have
+ layoutStruct->y = findY(layoutStruct->y, layoutStruct, QFixed::fromReal(line.naturalTextWidth()));
+ floatMargins(layoutStruct->y, layoutStruct, &left, &right);
+ left = qMax(left, l);
+ right = qMin(right, r);
+ if (dir == Qt::LeftToRight)
+ left += text_indent;
+ else
+ right -= text_indent;
+ line.setLineWidth(qMax<qreal>(line.naturalTextWidth(), (right-left).toReal()));
+ }
+
+ if (haveWordOrAnyWrapMode) {
+ option.setWrapMode(QTextOption::WordWrap);
+ tl->setTextOption(option);
+ }
+ }
+
+ QFixed lineHeight = QFixed::fromReal(line.height());
+ if (layoutStruct->pageHeight > 0 && layoutStruct->absoluteY() + lineHeight > layoutStruct->pageBottom) {
+ layoutStruct->newPage();
+
+ floatMargins(layoutStruct->y, layoutStruct, &left, &right);
+ left = qMax(left, l);
+ right = qMin(right, r);
+ if (dir == Qt::LeftToRight)
+ left += text_indent;
+ else
+ right -= text_indent;
+ }
+
+ line.setPosition(QPointF((left - layoutStruct->x_left).toReal(), (layoutStruct->y - cy).toReal()));
+ layoutStruct->y += lineHeight;
+ layoutStruct->contentsWidth
+ = qMax<QFixed>(layoutStruct->contentsWidth, QFixed::fromReal(line.x() + line.naturalTextWidth()) + totalRightMargin);
+
+ // position floats
+ for (int i = 0; i < layoutStruct->pendingFloats.size(); ++i) {
+ QTextFrame *f = layoutStruct->pendingFloats.at(i);
+ positionFloat(f);
+ }
+ layoutStruct->pendingFloats.clear();
+ }
+ tl->endLayout();
+ } else {
+ const int cnt = tl->lineCount();
+ for (int i = 0; i < cnt; ++i) {
+ LDEBUG << "going to move text line" << i;
+ QTextLine line = tl->lineAt(i);
+ layoutStruct->contentsWidth
+ = qMax(layoutStruct->contentsWidth, QFixed::fromReal(line.x() + tl->lineAt(i).naturalTextWidth()) + totalRightMargin);
+ const QFixed lineHeight = QFixed::fromReal(line.height());
+ if (layoutStruct->pageHeight != QFIXED_MAX) {
+ if (layoutStruct->absoluteY() + lineHeight > layoutStruct->pageBottom)
+ layoutStruct->newPage();
+ line.setPosition(QPointF(line.position().x(), layoutStruct->y.toReal() - tl->position().y()));
+ }
+ layoutStruct->y += lineHeight;
+ }
+ if (layoutStruct->updateRect.isValid()
+ && blockLength > 1) {
+ if (layoutFrom >= blockPosition + blockLength) {
+ // if our height didn't change and the change in the document is
+ // in one of the later paragraphs, then we don't need to repaint
+ // this one
+ layoutStruct->updateRect.setTop(qMax(layoutStruct->updateRect.top(), layoutStruct->y.toReal()));
+ } else if (layoutTo < blockPosition) {
+ if (oldPosition == tl->position())
+ // if the change in the document happened earlier in the document
+ // and our position did /not/ change because none of the earlier paragraphs
+ // or frames changed their height, then we don't need to repaint
+ // this one
+ layoutStruct->updateRect.setBottom(qMin(layoutStruct->updateRect.bottom(), tl->position().y()));
+ else
+ layoutStruct->updateRect.setBottom(qreal(INT_MAX)); // reset
+ }
+ }
+ }
+
+ // ### doesn't take floats into account. would need to do it per line. but how to retrieve then? (Simon)
+ const QFixed margins = totalLeftMargin + totalRightMargin;
+ layoutStruct->minimumWidth = qMax(layoutStruct->minimumWidth, QFixed::fromReal(tl->minimumWidth()) + margins);
+
+ const QFixed maxW = QFixed::fromReal(tl->maximumWidth()) + margins;
+
+ if (maxW > 0) {
+ if (layoutStruct->maximumWidth == QFIXED_MAX)
+ layoutStruct->maximumWidth = maxW;
+ else
+ layoutStruct->maximumWidth = qMax(layoutStruct->maximumWidth, maxW);
+ }
+}
+
+void QTextDocumentLayoutPrivate::floatMargins(const QFixed &y, const QLayoutStruct *layoutStruct,
+ QFixed *left, QFixed *right) const
+{
+// qDebug() << "floatMargins y=" << y;
+ *left = layoutStruct->x_left;
+ *right = layoutStruct->x_right;
+ QTextFrameData *lfd = data(layoutStruct->frame);
+ for (int i = 0; i < lfd->floats.size(); ++i) {
+ QTextFrameData *fd = data(lfd->floats.at(i));
+ if (!fd->layoutDirty) {
+ if (fd->position.y <= y && fd->position.y + fd->size.height > y) {
+// qDebug() << "adjusting with float" << f << fd->position.x()<< fd->size.width();
+ if (lfd->floats.at(i)->frameFormat().position() == QTextFrameFormat::FloatLeft)
+ *left = qMax(*left, fd->position.x + fd->size.width);
+ else
+ *right = qMin(*right, fd->position.x);
+ }
+ }
+ }
+// qDebug() << "floatMargins: left="<<*left<<"right="<<*right<<"y="<<y;
+}
+
+QFixed QTextDocumentLayoutPrivate::findY(QFixed yFrom, const QLayoutStruct *layoutStruct, QFixed requiredWidth) const
+{
+ QFixed right, left;
+ requiredWidth = qMin(requiredWidth, layoutStruct->x_right - layoutStruct->x_left);
+
+// qDebug() << "findY:" << yFrom;
+ while (1) {
+ floatMargins(yFrom, layoutStruct, &left, &right);
+// qDebug() << " yFrom=" << yFrom<<"right=" << right << "left=" << left << "requiredWidth=" << requiredWidth;
+ if (right-left >= requiredWidth)
+ break;
+
+ // move float down until we find enough space
+ QFixed newY = QFIXED_MAX;
+ QTextFrameData *lfd = data(layoutStruct->frame);
+ for (int i = 0; i < lfd->floats.size(); ++i) {
+ QTextFrameData *fd = data(lfd->floats.at(i));
+ if (!fd->layoutDirty) {
+ if (fd->position.y <= yFrom && fd->position.y + fd->size.height > yFrom)
+ newY = qMin(newY, fd->position.y + fd->size.height);
+ }
+ }
+ if (newY == QFIXED_MAX)
+ break;
+ yFrom = newY;
+ }
+ return yFrom;
+}
+
+QTextDocumentLayout::QTextDocumentLayout(QTextDocument *doc)
+ : QAbstractTextDocumentLayout(*new QTextDocumentLayoutPrivate, doc)
+{
+ registerHandler(QTextFormat::ImageObject, new QTextImageHandler(this));
+}
+
+
+void QTextDocumentLayout::draw(QPainter *painter, const PaintContext &context)
+{
+ Q_D(QTextDocumentLayout);
+ QTextFrame *frame = d->document->rootFrame();
+ QTextFrameData *fd = data(frame);
+
+ if(fd->sizeDirty)
+ return;
+
+ if (context.clip.isValid()) {
+ d->ensureLayouted(QFixed::fromReal(context.clip.bottom()));
+ } else {
+ d->ensureLayoutFinished();
+ }
+
+ QFixed width = fd->size.width;
+ if (d->document->pageSize().width() == 0 && d->viewportRect.isValid()) {
+ // we're in NoWrap mode, meaning the frame should expand to the viewport
+ // so that backgrounds are drawn correctly
+ fd->size.width = qMax(width, QFixed::fromReal(d->viewportRect.right()));
+ }
+
+ // Make sure we conform to the root frames bounds when drawing.
+ d->clipRect = QRectF(fd->position.toPointF(), fd->size.toSizeF()).adjusted(fd->leftMargin.toReal(), 0, -fd->rightMargin.toReal(), 0);
+ d->drawFrame(QPointF(), painter, context, frame);
+ fd->size.width = width;
+}
+
+void QTextDocumentLayout::setViewport(const QRectF &viewport)
+{
+ Q_D(QTextDocumentLayout);
+ d->viewportRect = viewport;
+}
+
+static void markFrames(QTextFrame *current, int from, int oldLength, int length)
+{
+ int end = qMax(oldLength, length) + from;
+
+ if (current->firstPosition() >= end || current->lastPosition() < from)
+ return;
+
+ QTextFrameData *fd = data(current);
+ for (int i = 0; i < fd->floats.size(); ++i) {
+ QTextFrame *f = fd->floats[i];
+ if (!f) {
+ // float got removed in editing operation
+ fd->floats.removeAt(i);
+ --i;
+ }
+ }
+
+ fd->layoutDirty = true;
+ fd->sizeDirty = true;
+
+// qDebug(" marking frame (%d--%d) as dirty", current->firstPosition(), current->lastPosition());
+ QList<QTextFrame *> children = current->childFrames();
+ for (int i = 0; i < children.size(); ++i)
+ markFrames(children.at(i), from, oldLength, length);
+}
+
+void QTextDocumentLayout::documentChanged(int from, int oldLength, int length)
+{
+ Q_D(QTextDocumentLayout);
+
+ QTextBlock blockIt = document()->findBlock(from);
+ QTextBlock endIt = document()->findBlock(qMax(0, from + length - 1));
+ if (endIt.isValid())
+ endIt = endIt.next();
+ for (; blockIt.isValid() && blockIt != endIt; blockIt = blockIt.next())
+ blockIt.clearLayout();
+
+ if (d->docPrivate->pageSize.isNull())
+ return;
+
+ QRectF updateRect;
+
+ d->lazyLayoutStepSize = 1000;
+ d->sizeChangedTimer.stop();
+ d->insideDocumentChange = true;
+
+ const int documentLength = d->docPrivate->length();
+ const bool fullLayout = (oldLength == 0 && length == documentLength);
+ const bool smallChange = documentLength > 0
+ && (qMax(length, oldLength) * 100 / documentLength) < 5;
+
+ // don't show incremental layout progress (avoid scroll bar flicker)
+ // if we see only a small change in the document and we're either starting
+ // a layout run or we're already in progress for that and we haven't seen
+ // any bigger change previously (showLayoutProgress already false)
+ if (smallChange
+ && (d->currentLazyLayoutPosition == -1 || d->showLayoutProgress == false))
+ d->showLayoutProgress = false;
+ else
+ d->showLayoutProgress = true;
+
+ if (fullLayout) {
+ d->contentHasAlignment = false;
+ d->currentLazyLayoutPosition = 0;
+ d->checkPoints.clear();
+ d->layoutStep();
+ } else {
+ d->ensureLayoutedByPosition(from);
+ updateRect = doLayout(from, oldLength, length);
+ }
+
+ if (!d->layoutTimer.isActive() && d->currentLazyLayoutPosition != -1)
+ d->layoutTimer.start(10, this);
+
+ d->insideDocumentChange = false;
+
+ if (d->showLayoutProgress) {
+ const QSizeF newSize = dynamicDocumentSize();
+ if (newSize != d->lastReportedSize) {
+ d->lastReportedSize = newSize;
+ emit documentSizeChanged(newSize);
+ }
+ }
+
+ if (!updateRect.isValid()) {
+ // don't use the frame size, it might have shrunken
+ updateRect = QRectF(QPointF(0, 0), QSizeF(qreal(INT_MAX), qreal(INT_MAX)));
+ }
+
+ emit update(updateRect);
+}
+
+QRectF QTextDocumentLayout::doLayout(int from, int oldLength, int length)
+{
+ Q_D(QTextDocumentLayout);
+
+// qDebug("documentChange: from=%d, oldLength=%d, length=%d", from, oldLength, length);
+
+ // mark all frames between f_start and f_end as dirty
+ markFrames(d->docPrivate->rootFrame(), from, oldLength, length);
+
+ QRectF updateRect;
+
+ QTextFrame *root = d->docPrivate->rootFrame();
+ if(data(root)->sizeDirty)
+ updateRect = d->layoutFrame(root, from, from + length);
+ data(root)->layoutDirty = false;
+
+ if (d->currentLazyLayoutPosition == -1)
+ layoutFinished();
+ else if (d->showLayoutProgress)
+ d->sizeChangedTimer.start(0, this);
+
+ return updateRect;
+}
+
+int QTextDocumentLayout::hitTest(const QPointF &point, Qt::HitTestAccuracy accuracy) const
+{
+ Q_D(const QTextDocumentLayout);
+ d->ensureLayouted(QFixed::fromReal(point.y()));
+ QTextFrame *f = d->docPrivate->rootFrame();
+ int position = 0;
+ QTextLayout *l = 0;
+ QFixedPoint pointf;
+ pointf.x = QFixed::fromReal(point.x());
+ pointf.y = QFixed::fromReal(point.y());
+ QTextDocumentLayoutPrivate::HitPoint p = d->hitTest(f, pointf, &position, &l, accuracy);
+ if (accuracy == Qt::ExactHit && p < QTextDocumentLayoutPrivate::PointExact)
+ return -1;
+
+ // ensure we stay within document bounds
+ int lastPos = f->lastPosition();
+ if (l && !l->preeditAreaText().isEmpty())
+ lastPos += l->preeditAreaText().length();
+ if (position > lastPos)
+ position = lastPos;
+ else if (position < 0)
+ position = 0;
+
+ return position;
+}
+
+void QTextDocumentLayout::resizeInlineObject(QTextInlineObject item, int posInDocument, const QTextFormat &format)
+{
+ Q_D(QTextDocumentLayout);
+ QTextCharFormat f = format.toCharFormat();
+ Q_ASSERT(f.isValid());
+ QTextObjectHandler handler = d->handlers.value(f.objectType());
+ if (!handler.component)
+ return;
+
+ QSizeF intrinsic = handler.iface->intrinsicSize(d->document, posInDocument, format);
+
+ QTextFrameFormat::Position pos = QTextFrameFormat::InFlow;
+ QTextFrame *frame = qobject_cast<QTextFrame *>(d->document->objectForFormat(f));
+ if (frame) {
+ pos = frame->frameFormat().position();
+ QTextFrameData *fd = data(frame);
+ fd->sizeDirty = false;
+ fd->size = QFixedSize::fromSizeF(intrinsic);
+ fd->minimumWidth = fd->maximumWidth = fd->size.width;
+ }
+
+ QSizeF inlineSize = (pos == QTextFrameFormat::InFlow ? intrinsic : QSizeF(0, 0));
+ item.setWidth(inlineSize.width());
+ if (f.verticalAlignment() == QTextCharFormat::AlignMiddle) {
+ item.setDescent(inlineSize.height() / 2);
+ item.setAscent(inlineSize.height() / 2 - 1);
+ } else {
+ item.setDescent(0);
+ item.setAscent(inlineSize.height() - 1);
+ }
+}
+
+void QTextDocumentLayout::positionInlineObject(QTextInlineObject item, int posInDocument, const QTextFormat &format)
+{
+ Q_D(QTextDocumentLayout);
+ Q_UNUSED(posInDocument);
+ if (item.width() != 0)
+ // inline
+ return;
+
+ QTextCharFormat f = format.toCharFormat();
+ Q_ASSERT(f.isValid());
+ QTextObjectHandler handler = d->handlers.value(f.objectType());
+ if (!handler.component)
+ return;
+
+ QTextFrame *frame = qobject_cast<QTextFrame *>(d->document->objectForFormat(f));
+ if (!frame)
+ return;
+
+ QTextBlock b = d->document->findBlock(frame->firstPosition());
+ QTextLine line;
+ if (b.position() <= frame->firstPosition() && b.position() + b.length() > frame->lastPosition())
+ line = b.layout()->lineAt(b.layout()->lineCount()-1);
+// qDebug() << "layoutObject: line.isValid" << line.isValid() << b.position() << b.length() <<
+// frame->firstPosition() << frame->lastPosition();
+ d->positionFloat(frame, line.isValid() ? &line : 0);
+}
+
+void QTextDocumentLayout::drawInlineObject(QPainter *p, const QRectF &rect, QTextInlineObject item,
+ int posInDocument, const QTextFormat &format)
+{
+ Q_D(QTextDocumentLayout);
+ QTextCharFormat f = format.toCharFormat();
+ Q_ASSERT(f.isValid());
+ QTextFrame *frame = qobject_cast<QTextFrame *>(d->document->objectForFormat(f));
+ if (frame && frame->frameFormat().position() != QTextFrameFormat::InFlow)
+ return; // don't draw floating frames from inline objects here but in drawFlow instead
+
+// qDebug() << "drawObject at" << r;
+ QAbstractTextDocumentLayout::drawInlineObject(p, rect, item, posInDocument, format);
+}
+
+int QTextDocumentLayout::dynamicPageCount() const
+{
+ Q_D(const QTextDocumentLayout);
+ const QSizeF pgSize = d->document->pageSize();
+ if (pgSize.height() < 0)
+ return 1;
+ return qCeil(dynamicDocumentSize().height() / pgSize.height());
+}
+
+QSizeF QTextDocumentLayout::dynamicDocumentSize() const
+{
+ Q_D(const QTextDocumentLayout);
+ return data(d->docPrivate->rootFrame())->size.toSizeF();
+}
+
+int QTextDocumentLayout::pageCount() const
+{
+ Q_D(const QTextDocumentLayout);
+ d->ensureLayoutFinished();
+ return dynamicPageCount();
+}
+
+QSizeF QTextDocumentLayout::documentSize() const
+{
+ Q_D(const QTextDocumentLayout);
+ d->ensureLayoutFinished();
+ return dynamicDocumentSize();
+}
+
+void QTextDocumentLayoutPrivate::ensureLayouted(QFixed y) const
+{
+ Q_Q(const QTextDocumentLayout);
+ if (currentLazyLayoutPosition == -1)
+ return;
+ const QSizeF oldSize = q->dynamicDocumentSize();
+
+ if (checkPoints.isEmpty())
+ layoutStep();
+
+ while (currentLazyLayoutPosition != -1
+ && checkPoints.last().y < y)
+ layoutStep();
+}
+
+void QTextDocumentLayoutPrivate::ensureLayoutedByPosition(int position) const
+{
+ if (currentLazyLayoutPosition == -1)
+ return;
+ if (position < currentLazyLayoutPosition)
+ return;
+ while (currentLazyLayoutPosition != -1
+ && currentLazyLayoutPosition < position) {
+ const_cast<QTextDocumentLayout *>(q_func())->doLayout(currentLazyLayoutPosition, 0, INT_MAX - currentLazyLayoutPosition);
+ }
+}
+
+void QTextDocumentLayoutPrivate::layoutStep() const
+{
+ ensureLayoutedByPosition(currentLazyLayoutPosition + lazyLayoutStepSize);
+ lazyLayoutStepSize = qMin(200000, lazyLayoutStepSize * 2);
+}
+
+void QTextDocumentLayout::setCursorWidth(int width)
+{
+ Q_D(QTextDocumentLayout);
+ d->cursorWidth = width;
+}
+
+int QTextDocumentLayout::cursorWidth() const
+{
+ Q_D(const QTextDocumentLayout);
+ return d->cursorWidth;
+}
+
+void QTextDocumentLayout::setFixedColumnWidth(int width)
+{
+ Q_D(QTextDocumentLayout);
+ d->fixedColumnWidth = width;
+}
+
+QRectF QTextDocumentLayout::frameBoundingRect(QTextFrame *frame) const
+{
+ Q_D(const QTextDocumentLayout);
+ if (d->docPrivate->pageSize.isNull())
+ return QRectF();
+ d->ensureLayoutFinished();
+ return d->frameBoundingRectInternal(frame);
+}
+
+QRectF QTextDocumentLayoutPrivate::frameBoundingRectInternal(QTextFrame *frame) const
+{
+ QPointF pos;
+ const int framePos = frame->firstPosition();
+ QTextFrame *f = frame;
+ while (f) {
+ QTextFrameData *fd = data(f);
+ pos += fd->position.toPointF();
+
+ if (QTextTable *table = qobject_cast<QTextTable *>(f)) {
+ QTextTableCell cell = table->cellAt(framePos);
+ if (cell.isValid())
+ pos += static_cast<QTextTableData *>(fd)->cellPosition(cell).toPointF();
+ }
+
+ f = f->parentFrame();
+ }
+ return QRectF(pos, data(frame)->size.toSizeF());
+}
+
+QRectF QTextDocumentLayout::blockBoundingRect(const QTextBlock &block) const
+{
+ Q_D(const QTextDocumentLayout);
+ if (d->docPrivate->pageSize.isNull())
+ return QRectF();
+ d->ensureLayoutedByPosition(block.position() + block.length());
+ QTextFrame *frame = d->document->frameAt(block.position());
+ QPointF offset;
+ const int blockPos = block.position();
+
+ while (frame) {
+ QTextFrameData *fd = data(frame);
+ offset += fd->position.toPointF();
+
+ if (QTextTable *table = qobject_cast<QTextTable *>(frame)) {
+ QTextTableCell cell = table->cellAt(blockPos);
+ if (cell.isValid())
+ offset += static_cast<QTextTableData *>(fd)->cellPosition(cell).toPointF();
+ }
+
+ frame = frame->parentFrame();
+ }
+
+ const QTextLayout *layout = block.layout();
+ QRectF rect = layout->boundingRect();
+ rect.moveTopLeft(layout->position() + offset);
+ return rect;
+}
+
+int QTextDocumentLayout::layoutStatus() const
+{
+ Q_D(const QTextDocumentLayout);
+ int pos = d->currentLazyLayoutPosition;
+ if (pos == -1)
+ return 100;
+ return pos * 100 / d->document->docHandle()->length();
+}
+
+void QTextDocumentLayout::timerEvent(QTimerEvent *e)
+{
+ Q_D(QTextDocumentLayout);
+ if (e->timerId() == d->layoutTimer.timerId()) {
+ if (d->currentLazyLayoutPosition != -1)
+ d->layoutStep();
+ } else if (e->timerId() == d->sizeChangedTimer.timerId()) {
+ d->lastReportedSize = dynamicDocumentSize();
+ emit documentSizeChanged(d->lastReportedSize);
+ d->sizeChangedTimer.stop();
+
+ if (d->currentLazyLayoutPosition == -1) {
+ const int newCount = dynamicPageCount();
+ if (newCount != d->lastPageCount) {
+ d->lastPageCount = newCount;
+ emit pageCountChanged(newCount);
+ }
+ }
+ } else {
+ QAbstractTextDocumentLayout::timerEvent(e);
+ }
+}
+
+void QTextDocumentLayout::layoutFinished()
+{
+ Q_D(QTextDocumentLayout);
+ d->layoutTimer.stop();
+ if (!d->insideDocumentChange)
+ d->sizeChangedTimer.start(0, this);
+ // reset
+ d->showLayoutProgress = true;
+}
+
+void QTextDocumentLayout::ensureLayouted(qreal y)
+{
+ d_func()->ensureLayouted(QFixed::fromReal(y));
+}
+
+qreal QTextDocumentLayout::idealWidth() const
+{
+ Q_D(const QTextDocumentLayout);
+ d->ensureLayoutFinished();
+ return d->idealWidth;
+}
+
+bool QTextDocumentLayout::contentHasAlignment() const
+{
+ Q_D(const QTextDocumentLayout);
+ return d->contentHasAlignment;
+}
+
+qreal QTextDocumentLayoutPrivate::scaleToDevice(qreal value) const
+{
+ QPaintDevice *dev = q_func()->paintDevice();
+ if (!dev)
+ return value;
+ return value * dev->logicalDpiY() / qreal(qt_defaultDpi());
+}
+
+QFixed QTextDocumentLayoutPrivate::scaleToDevice(QFixed value) const
+{
+ QPaintDevice *dev = q_func()->paintDevice();
+ if (!dev)
+ return value;
+ return value * QFixed(dev->logicalDpiY()) / QFixed(qt_defaultDpi());
+}
+
+QT_END_NAMESPACE
+
+#include "moc_qtextdocumentlayout_p.cpp"
diff --git a/src/gui/text/qtextdocumentlayout_p.h b/src/gui/text/qtextdocumentlayout_p.h
new file mode 100644
index 0000000000..d0206ab899
--- /dev/null
+++ b/src/gui/text/qtextdocumentlayout_p.h
@@ -0,0 +1,119 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QTEXTDOCUMENTLAYOUT_P_H
+#define QTEXTDOCUMENTLAYOUT_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 "QtGui/qabstracttextdocumentlayout.h"
+#include "QtGui/qtextoption.h"
+#include "QtGui/qtextobject.h"
+
+QT_BEGIN_NAMESPACE
+
+class QTextListFormat;
+
+class QTextDocumentLayoutPrivate;
+
+class Q_AUTOTEST_EXPORT QTextDocumentLayout : public QAbstractTextDocumentLayout
+{
+ Q_DECLARE_PRIVATE(QTextDocumentLayout)
+ Q_OBJECT
+ Q_PROPERTY(int cursorWidth READ cursorWidth WRITE setCursorWidth)
+ Q_PROPERTY(qreal idealWidth READ idealWidth)
+ Q_PROPERTY(bool contentHasAlignment READ contentHasAlignment)
+public:
+ explicit QTextDocumentLayout(QTextDocument *doc);
+
+ // from the abstract layout
+ void draw(QPainter *painter, const PaintContext &context);
+ int hitTest(const QPointF &point, Qt::HitTestAccuracy accuracy) const;
+
+ int pageCount() const;
+ QSizeF documentSize() const;
+
+ void setCursorWidth(int width);
+ int cursorWidth() const;
+
+ // internal, to support the ugly FixedColumnWidth wordwrap mode in QTextEdit
+ void setFixedColumnWidth(int width);
+
+ // internal for QTextEdit's NoWrap mode
+ void setViewport(const QRectF &viewport);
+
+ virtual QRectF frameBoundingRect(QTextFrame *frame) const;
+ virtual QRectF blockBoundingRect(const QTextBlock &block) const;
+
+ // ####
+ int layoutStatus() const;
+ int dynamicPageCount() const;
+ QSizeF dynamicDocumentSize() const;
+ void ensureLayouted(qreal);
+
+ qreal idealWidth() const;
+
+ bool contentHasAlignment() const;
+
+protected:
+ void documentChanged(int from, int oldLength, int length);
+ void resizeInlineObject(QTextInlineObject item, int posInDocument, const QTextFormat &format);
+ void positionInlineObject(QTextInlineObject item, int posInDocument, const QTextFormat &format);
+ void drawInlineObject(QPainter *p, const QRectF &rect, QTextInlineObject item,
+ int posInDocument, const QTextFormat &format);
+ virtual void timerEvent(QTimerEvent *e);
+private:
+ QRectF doLayout(int from, int oldLength, int length);
+ void layoutFinished();
+};
+
+QT_END_NAMESPACE
+
+#endif // QTEXTDOCUMENTLAYOUT_P_H
diff --git a/src/gui/text/qtextdocumentwriter.cpp b/src/gui/text/qtextdocumentwriter.cpp
new file mode 100644
index 0000000000..08ee14e914
--- /dev/null
+++ b/src/gui/text/qtextdocumentwriter.cpp
@@ -0,0 +1,372 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#include "qtextdocumentwriter.h"
+
+#include <QtCore/qfile.h>
+#include <QtCore/qbytearray.h>
+#include <QtCore/qfileinfo.h>
+#include <QtCore/qtextcodec.h>
+#include <QtCore/qtextstream.h>
+#include <QtCore/qdebug.h>
+#include "qtextdocument.h"
+#include "qtextdocumentfragment.h"
+
+#include "qtextdocumentfragment_p.h"
+#include "qtextodfwriter_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class QTextDocumentWriterPrivate
+{
+public:
+ QTextDocumentWriterPrivate(QTextDocumentWriter* qq);
+
+ // device
+ QByteArray format;
+ QIODevice *device;
+ bool deleteDevice;
+#ifndef QT_NO_TEXTCODEC
+ QTextCodec *codec;
+#endif
+
+ QTextDocumentWriter *q;
+};
+
+/*!
+ \since 4.5
+ \class QTextDocumentWriter
+
+ \brief The QTextDocumentWriter class provides a format-independent interface for writing a QTextDocument to files or other devices.
+
+ \ingroup text
+ \ingroup io
+
+ To write a document, construct a QTextDocumentWriter object with either a
+ file name or a device object, and specify the document format to be
+ written. You can construct a writer and set the format using setFormat()
+ later.
+
+ Call write() to write the document to the device. If the document is
+ successfully written, this function returns true. However, if an error
+ occurs when writing the document, it will return false.
+
+ Call supportedDocumentFormats() for a list of formats that
+ QTextDocumentWriter can write.
+
+ Since the capabilities of the supported output formats vary considerably,
+ the writer simply outputs the appropriate subset of objects for each format.
+ This typically includes the formatted text and images contained in a
+ document.
+*/
+
+/*!
+ \internal
+*/
+QTextDocumentWriterPrivate::QTextDocumentWriterPrivate(QTextDocumentWriter *qq)
+ : device(0),
+ deleteDevice(false),
+#ifndef QT_NO_TEXTCODEC
+ codec(QTextCodec::codecForName("utf-8")),
+#endif
+ q(qq)
+{
+}
+
+/*!
+ Constructs an empty QTextDocumentWriter object. Before writing, you must
+ call setFormat() to set a document format, then setDevice() or
+ setFileName().
+*/
+QTextDocumentWriter::QTextDocumentWriter()
+ : d(new QTextDocumentWriterPrivate(this))
+{
+}
+
+/*!
+ Constructs a QTextDocumentWriter object to write to the given \a device
+ in the document format specified by \a format.
+*/
+QTextDocumentWriter::QTextDocumentWriter(QIODevice *device, const QByteArray &format)
+ : d(new QTextDocumentWriterPrivate(this))
+{
+ d->device = device;
+ d->format = format;
+}
+
+/*!
+ Constructs an QTextDocumentWriter object that will write to a file with
+ the name \a fileName, using the document format specified by \a format.
+ If \a format is not provided, QTextDocumentWriter will detect the document
+ format by inspecting the extension of \a fileName.
+*/
+QTextDocumentWriter::QTextDocumentWriter(const QString &fileName, const QByteArray &format)
+ : d(new QTextDocumentWriterPrivate(this))
+{
+ QFile *file = new QFile(fileName);
+ d->device = file;
+ d->deleteDevice = true;
+ d->format = format;
+}
+
+/*!
+ Destroys the QTextDocumentWriter object.
+*/
+QTextDocumentWriter::~QTextDocumentWriter()
+{
+ if (d->deleteDevice)
+ delete d->device;
+ delete d;
+}
+
+/*!
+ Sets the format used to write documents to the \a format specified.
+ \a format is a case insensitive text string. For example:
+
+ \snippet doc/src/snippets/code/src.gui.text.qtextdocumentwriter.cpp 0
+
+ You can call supportedDocumentFormats() for the full list of formats
+ QTextDocumentWriter supports.
+
+ \sa format()
+*/
+void QTextDocumentWriter::setFormat (const QByteArray &format)
+{
+ d->format = format;
+}
+
+/*!
+ Returns the format used for writing documents.
+
+ \sa setFormat()
+*/
+QByteArray QTextDocumentWriter::format () const
+{
+ return d->format;
+}
+
+/*!
+ Sets the writer's device to the \a device specified. If a device has
+ already been set, the old device is removed but otherwise left
+ unchanged.
+
+ If the device is not already open, QTextDocumentWriter will attempt to
+ open the device in \l QIODevice::WriteOnly mode by calling open().
+
+ \note This will not work for certain devices, such as QProcess,
+ QTcpSocket and QUdpSocket, where some configuration is required before
+ the device can be opened.
+
+ \sa device(), setFileName()
+*/
+void QTextDocumentWriter::setDevice (QIODevice *device)
+{
+ if (d->device && d->deleteDevice)
+ delete d->device;
+
+ d->device = device;
+ d->deleteDevice = false;
+}
+
+/*!
+ Returns the device currently assigned, or 0 if no device has been
+ assigned.
+*/
+QIODevice *QTextDocumentWriter::device () const
+{
+ return d->device;
+}
+
+/*!
+ Sets the name of the file to be written to \a fileName. Internally,
+ QTextDocumentWriter will create a QFile and open it in \l
+ QIODevice::WriteOnly mode, and use this file when writing the document.
+
+ \sa fileName(), setDevice()
+*/
+void QTextDocumentWriter::setFileName (const QString &fileName)
+{
+ setDevice(new QFile(fileName));
+ d->deleteDevice = true;
+}
+
+/*!
+ If the currently assigned device is a QFile, or if setFileName()
+ has been called, this function returns the name of the file
+ to be written to. In all other cases, it returns an empty string.
+
+ \sa setFileName(), setDevice()
+*/
+QString QTextDocumentWriter::fileName () const
+{
+ QFile *file = qobject_cast<QFile *>(d->device);
+ return file ? file->fileName() : QString();
+}
+
+/*!
+ Writes the given \a document to the assigned device or file and
+ returns true if successful; otherwise returns false.
+*/
+bool QTextDocumentWriter::write(const QTextDocument *document)
+{
+ QByteArray suffix;
+
+ if (d->device && d->format.isEmpty()) {
+ // if there's no format, see if device is a file, and if so, find
+ // the file suffix
+ if (QFile *file = qobject_cast<QFile *>(d->device))
+ suffix = QFileInfo(file->fileName()).suffix().toLower().toLatin1();
+ }
+
+ QByteArray format = !d->format.isEmpty() ? d->format.toLower() : suffix;
+
+#ifndef QT_NO_TEXTODFWRITER
+ if (format == "odf" || format == "opendocumentformat" || format == "odt") {
+ QTextOdfWriter writer(*document, d->device);
+#ifndef QT_NO_TEXTCODEC
+ writer.setCodec(d->codec);
+#endif
+ return writer.writeAll();
+ }
+#endif // QT_NO_TEXTODFWRITER
+
+#ifndef QT_NO_TEXTHTMLPARSER
+ if (format == "html" || format == "htm") {
+ if (!d->device->isWritable() && ! d->device->open(QIODevice::WriteOnly)) {
+ qWarning() << "QTextDocumentWriter::write: the device can not be opened for writing";
+ return false;
+ }
+ QTextStream ts(d->device);
+#ifndef QT_NO_TEXTCODEC
+ ts.setCodec(d->codec);
+ ts << document->toHtml(d->codec->name());
+#endif
+ d->device->close();
+ return true;
+ }
+#endif
+ if (format == "txt" || format == "plaintext") {
+ if (!d->device->isWritable() && ! d->device->open(QIODevice::WriteOnly)) {
+ qWarning() << "QTextDocumentWriter::write: the device can not be opened for writing";
+ return false;
+ }
+ QTextStream ts(d->device);
+#ifndef QT_NO_TEXTCODEC
+ ts.setCodec(d->codec);
+#endif
+ ts << document->toPlainText();
+ d->device->close();
+ return true;
+ }
+
+ return false;
+}
+
+/*!
+ Writes the document fragment specified by \a fragment to the assigned device
+ or file and returns true if successful; otherwise returns false.
+*/
+bool QTextDocumentWriter::write(const QTextDocumentFragment &fragment)
+{
+ if (fragment.d == 0)
+ return false; // invalid fragment.
+ QTextDocument *doc = fragment.d->doc;
+ if (doc)
+ return write(doc);
+ return false;
+}
+
+/*!
+ Sets the codec for this stream to \a codec. The codec is used for
+ encoding any data that is written. By default, QTextDocumentWriter
+ uses UTF-8.
+*/
+
+#ifndef QT_NO_TEXTCODEC
+void QTextDocumentWriter::setCodec(QTextCodec *codec)
+{
+ if (codec == 0)
+ codec = QTextCodec::codecForName("UTF-8");
+ Q_ASSERT(codec);
+ d->codec = codec;
+}
+#endif
+
+/*!
+ Returns the codec that is currently assigned to the writer.
+*/
+#ifndef QT_NO_TEXTCODEC
+QTextCodec *QTextDocumentWriter::codec() const
+{
+ return d->codec;
+}
+#endif
+
+/*!
+ Returns the list of document formats supported by QTextDocumentWriter.
+
+ By default, Qt can write the following formats:
+
+ \table
+ \header \o Format \o Description
+ \row \o plaintext \o Plain text
+ \row \o HTML \o HyperText Markup Language
+ \row \o ODF \o OpenDocument Format
+ \endtable
+
+ \sa setFormat()
+*/
+QList<QByteArray> QTextDocumentWriter::supportedDocumentFormats()
+{
+ QList<QByteArray> answer;
+ answer << "plaintext";
+
+#ifndef QT_NO_TEXTHTMLPARSER
+ answer << "HTML";
+#endif
+#ifndef QT_NO_TEXTODFWRITER
+ answer << "ODF";
+#endif // QT_NO_TEXTODFWRITER
+
+ qSort(answer);
+ return answer;
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/text/qtextdocumentwriter.h b/src/gui/text/qtextdocumentwriter.h
new file mode 100644
index 0000000000..53d17ca2db
--- /dev/null
+++ b/src/gui/text/qtextdocumentwriter.h
@@ -0,0 +1,93 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#ifndef QTEXTDOCUMENTWRITER_H
+#define QTEXTDOCUMENTWRITER_H
+
+#include <QtCore/qstring.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+class QTextDocumentWriterPrivate;
+class QIODevice;
+class QByteArray;
+class QTextDocument;
+class QTextDocumentFragment;
+
+class Q_GUI_EXPORT QTextDocumentWriter
+{
+public:
+ QTextDocumentWriter();
+ QTextDocumentWriter(QIODevice *device, const QByteArray &format);
+ QTextDocumentWriter(const QString &fileName, const QByteArray &format = QByteArray());
+ ~QTextDocumentWriter();
+
+ void setFormat (const QByteArray &format);
+ QByteArray format () const;
+
+ void setDevice (QIODevice *device);
+ QIODevice *device () const;
+ void setFileName (const QString &fileName);
+ QString fileName () const;
+
+ bool write(const QTextDocument *document);
+ bool write(const QTextDocumentFragment &fragment);
+
+#ifndef QT_NO_TEXTCODEC
+ void setCodec(QTextCodec *codec);
+ QTextCodec *codec() const;
+#endif
+
+ static QList<QByteArray> supportedDocumentFormats();
+
+private:
+ Q_DISABLE_COPY(QTextDocumentWriter)
+ QTextDocumentWriterPrivate *d;
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif
diff --git a/src/gui/text/qtextengine.cpp b/src/gui/text/qtextengine.cpp
new file mode 100644
index 0000000000..80a5425790
--- /dev/null
+++ b/src/gui/text/qtextengine.cpp
@@ -0,0 +1,2648 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qdebug.h"
+#include "qtextformat.h"
+#include "qtextformat_p.h"
+#include "qtextengine_p.h"
+#include "qabstracttextdocumentlayout.h"
+#include "qtextlayout.h"
+#include "qtextboundaryfinder.h"
+#include "qvarlengtharray.h"
+#include "qfont.h"
+#include "qfont_p.h"
+#include "qfontengine_p.h"
+#include "qstring.h"
+#include <private/qunicodetables_p.h>
+#include "qtextdocument_p.h"
+#include <qapplication.h>
+#include <stdlib.h>
+
+
+QT_BEGIN_NAMESPACE
+
+namespace {
+// Helper class used in QTextEngine::itemize
+// keep it out here to allow us to keep supporting various compilers.
+class Itemizer {
+public:
+ Itemizer(const QString &string, const QScriptAnalysis *analysis, QScriptItemArray &items)
+ : m_string(string),
+ m_analysis(analysis),
+ m_items(items),
+ m_splitter(0)
+ {
+ }
+ ~Itemizer()
+ {
+ delete m_splitter;
+ }
+
+ /// generate the script items
+ /// The caps parameter is used to choose the algoritm of splitting text and assiging roles to the textitems
+ void generate(int start, int length, QFont::Capitalization caps)
+ {
+ if ((int)caps == (int)QFont::SmallCaps)
+ generateScriptItemsSmallCaps(m_string.utf16(), start, length);
+ else if(caps == QFont::Capitalize)
+ generateScriptItemsCapitalize(start, length);
+ else if(caps != QFont::MixedCase) {
+ generateScriptItemsAndChangeCase(start, length,
+ caps == QFont::AllLowercase ? QScriptAnalysis::Lowercase : QScriptAnalysis::Uppercase);
+ }
+ else
+ generateScriptItems(start, length);
+ }
+
+private:
+ enum { MaxItemLength = 4096 };
+
+ void generateScriptItemsAndChangeCase(int start, int length, QScriptAnalysis::Flags flags)
+ {
+ generateScriptItems(start, length);
+ if (m_items.isEmpty()) // the next loop won't work in that case
+ return;
+ QScriptItemArray::Iterator iter = m_items.end();
+ do {
+ iter--;
+ if (iter->analysis.flags < QScriptAnalysis::TabOrObject)
+ iter->analysis.flags = flags;
+ } while (iter->position > start);
+ }
+
+ void generateScriptItems(int start, int length)
+ {
+ if (!length)
+ return;
+ const int end = start + length;
+ for (int i = start + 1; i < end; ++i) {
+ if ((m_analysis[i] == m_analysis[start])
+ && m_analysis[i].flags < QScriptAnalysis::SpaceTabOrObject
+ && i - start < MaxItemLength)
+ continue;
+ m_items.append(QScriptItem(start, m_analysis[start]));
+ start = i;
+ }
+ m_items.append(QScriptItem(start, m_analysis[start]));
+ }
+
+ void generateScriptItemsCapitalize(int start, int length)
+ {
+ if (!length)
+ return;
+
+ if (!m_splitter)
+ m_splitter = new QTextBoundaryFinder(QTextBoundaryFinder::Word,
+ m_string.constData(), m_string.length(),
+ /*buffer*/0, /*buffer size*/0);
+
+ m_splitter->setPosition(start);
+ QScriptAnalysis itemAnalysis = m_analysis[start];
+
+ if (m_splitter->boundaryReasons() & QTextBoundaryFinder::StartWord) {
+ itemAnalysis.flags = QScriptAnalysis::Uppercase;
+ m_splitter->toNextBoundary();
+ }
+
+ const int end = start + length;
+ for (int i = start + 1; i < end; ++i) {
+
+ bool atWordBoundary = false;
+
+ if (i == m_splitter->position()) {
+ if (m_splitter->boundaryReasons() & QTextBoundaryFinder::StartWord
+ && m_analysis[i].flags < QScriptAnalysis::TabOrObject)
+ atWordBoundary = true;
+
+ m_splitter->toNextBoundary();
+ }
+
+ if (m_analysis[i] == itemAnalysis
+ && m_analysis[i].flags < QScriptAnalysis::TabOrObject
+ && !atWordBoundary
+ && i - start < MaxItemLength)
+ continue;
+
+ m_items.append(QScriptItem(start, itemAnalysis));
+ start = i;
+ itemAnalysis = m_analysis[start];
+
+ if (atWordBoundary)
+ itemAnalysis.flags = QScriptAnalysis::Uppercase;
+ }
+ m_items.append(QScriptItem(start, itemAnalysis));
+ }
+
+ void generateScriptItemsSmallCaps(const ushort *uc, int start, int length)
+ {
+ if (!length)
+ return;
+ bool lower = (QChar::category(uc[start]) == QChar::Letter_Lowercase);
+ const int end = start + length;
+ // split text into parts that are already uppercase and parts that are lowercase, and mark the latter to be uppercased later.
+ for (int i = start + 1; i < end; ++i) {
+ bool l = (QChar::category(uc[i]) == QChar::Letter_Lowercase);
+ if ((m_analysis[i] == m_analysis[start])
+ && m_analysis[i].flags < QScriptAnalysis::TabOrObject
+ && l == lower
+ && i - start < MaxItemLength)
+ continue;
+ m_items.append(QScriptItem(start, m_analysis[start]));
+ if (lower)
+ m_items.last().analysis.flags = QScriptAnalysis::SmallCaps;
+
+ start = i;
+ lower = l;
+ }
+ m_items.append(QScriptItem(start, m_analysis[start]));
+ if (lower)
+ m_items.last().analysis.flags = QScriptAnalysis::SmallCaps;
+ }
+
+ const QString &m_string;
+ const QScriptAnalysis * const m_analysis;
+ QScriptItemArray &m_items;
+ QTextBoundaryFinder *m_splitter;
+};
+}
+
+
+// ----------------------------------------------------------------------------
+//
+// The BiDi algorithm
+//
+// ----------------------------------------------------------------------------
+
+#define BIDI_DEBUG 0
+#if (BIDI_DEBUG >= 1)
+QT_BEGIN_INCLUDE_NAMESPACE
+#include <iostream>
+QT_END_INCLUDE_NAMESPACE
+using namespace std;
+
+static const char *directions[] = {
+ "DirL", "DirR", "DirEN", "DirES", "DirET", "DirAN", "DirCS", "DirB", "DirS", "DirWS", "DirON",
+ "DirLRE", "DirLRO", "DirAL", "DirRLE", "DirRLO", "DirPDF", "DirNSM", "DirBN"
+};
+
+#endif
+
+struct QBidiStatus {
+ QBidiStatus() {
+ eor = QChar::DirON;
+ lastStrong = QChar::DirON;
+ last = QChar:: DirON;
+ dir = QChar::DirON;
+ }
+ QChar::Direction eor;
+ QChar::Direction lastStrong;
+ QChar::Direction last;
+ QChar::Direction dir;
+};
+
+enum { MaxBidiLevel = 61 };
+
+struct QBidiControl {
+ inline QBidiControl(bool rtl)
+ : cCtx(0), base(rtl ? 1 : 0), level(rtl ? 1 : 0), override(false) {}
+
+ inline void embed(bool rtl, bool o = false) {
+ unsigned int toAdd = 1;
+ if((level%2 != 0) == rtl ) {
+ ++toAdd;
+ }
+ if (level + toAdd <= MaxBidiLevel) {
+ ctx[cCtx].level = level;
+ ctx[cCtx].override = override;
+ cCtx++;
+ override = o;
+ level += toAdd;
+ }
+ }
+ inline bool canPop() const { return cCtx != 0; }
+ inline void pdf() {
+ Q_ASSERT(cCtx);
+ --cCtx;
+ level = ctx[cCtx].level;
+ override = ctx[cCtx].override;
+ }
+
+ inline QChar::Direction basicDirection() const {
+ return (base ? QChar::DirR : QChar:: DirL);
+ }
+ inline unsigned int baseLevel() const {
+ return base;
+ }
+ inline QChar::Direction direction() const {
+ return ((level%2) ? QChar::DirR : QChar:: DirL);
+ }
+
+ struct {
+ unsigned int level;
+ bool override;
+ } ctx[MaxBidiLevel];
+ unsigned int cCtx;
+ const unsigned int base;
+ unsigned int level;
+ bool override;
+};
+
+
+static void appendItems(QScriptAnalysis *analysis, int &start, int &stop, const QBidiControl &control, QChar::Direction dir)
+{
+ if (start > stop)
+ return;
+
+ int level = control.level;
+
+ if(dir != QChar::DirON && !control.override) {
+ // add level of run (cases I1 & I2)
+ if(level % 2) {
+ if(dir == QChar::DirL || dir == QChar::DirAN || dir == QChar::DirEN)
+ level++;
+ } else {
+ if(dir == QChar::DirR)
+ level++;
+ else if(dir == QChar::DirAN || dir == QChar::DirEN)
+ level += 2;
+ }
+ }
+
+#if (BIDI_DEBUG >= 1)
+ qDebug("new run: dir=%s from %d, to %d level = %d override=%d", directions[dir], start, stop, level, control.override);
+#endif
+ QScriptAnalysis *s = analysis + start;
+ const QScriptAnalysis *e = analysis + stop;
+ while (s <= e) {
+ s->bidiLevel = level;
+ ++s;
+ }
+ ++stop;
+ start = stop;
+}
+
+
+// creates the next QScript items.
+static bool bidiItemize(QTextEngine *engine, QScriptAnalysis *analysis, QBidiControl &control)
+{
+ bool rightToLeft = (control.basicDirection() == 1);
+ bool hasBidi = rightToLeft;
+#if BIDI_DEBUG >= 2
+ qDebug() << "bidiItemize: rightToLeft=" << rightToLeft << engine->layoutData->string;
+#endif
+
+ int sor = 0;
+ int eor = -1;
+
+
+ int length = engine->layoutData->string.length();
+
+ const ushort *unicode = (const ushort *)engine->layoutData->string.unicode();
+ int current = 0;
+
+ QChar::Direction dir = rightToLeft ? QChar::DirR : QChar::DirL;
+ QBidiStatus status;
+
+ QChar::Direction sdir = QChar::direction(*unicode);
+ if (sdir != QChar::DirL && sdir != QChar::DirR && sdir != QChar::DirEN && sdir != QChar::DirAN)
+ sdir = QChar::DirON;
+ else
+ dir = QChar::DirON;
+ status.eor = sdir;
+ status.lastStrong = rightToLeft ? QChar::DirR : QChar::DirL;
+ status.last = status.lastStrong;
+ status.dir = sdir;
+
+
+ while (current <= length) {
+
+ QChar::Direction dirCurrent;
+ if (current == (int)length)
+ dirCurrent = control.basicDirection();
+ else
+ dirCurrent = QChar::direction(unicode[current]);
+
+#if (BIDI_DEBUG >= 2)
+// qDebug() << "pos=" << current << " dir=" << directions[dir]
+// << " current=" << directions[dirCurrent] << " last=" << directions[status.last]
+// << " eor=" << eor << "/" << directions[status.eor]
+// << " sor=" << sor << " lastStrong="
+// << directions[status.lastStrong]
+// << " level=" << (int)control.level << " override=" << (bool)control.override;
+#endif
+
+ switch(dirCurrent) {
+
+ // embedding and overrides (X1-X9 in the BiDi specs)
+ case QChar::DirRLE:
+ case QChar::DirRLO:
+ case QChar::DirLRE:
+ case QChar::DirLRO:
+ {
+ bool rtl = (dirCurrent == QChar::DirRLE || dirCurrent == QChar::DirRLO);
+ hasBidi |= rtl;
+ bool override = (dirCurrent == QChar::DirLRO || dirCurrent == QChar::DirRLO);
+
+ unsigned int level = control.level+1;
+ if ((level%2 != 0) == rtl) ++level;
+ if(level < MaxBidiLevel) {
+ eor = current-1;
+ appendItems(analysis, sor, eor, control, dir);
+ eor = current;
+ control.embed(rtl, override);
+ QChar::Direction edir = (rtl ? QChar::DirR : QChar::DirL);
+ dir = status.eor = edir;
+ status.lastStrong = edir;
+ }
+ break;
+ }
+ case QChar::DirPDF:
+ {
+ if (control.canPop()) {
+ if (dir != control.direction()) {
+ eor = current-1;
+ appendItems(analysis, sor, eor, control, dir);
+ dir = control.direction();
+ }
+ eor = current;
+ appendItems(analysis, sor, eor, control, dir);
+ control.pdf();
+ dir = QChar::DirON; status.eor = QChar::DirON;
+ status.last = control.direction();
+ if (control.override)
+ dir = control.direction();
+ else
+ dir = QChar::DirON;
+ status.lastStrong = control.direction();
+ }
+ break;
+ }
+
+ // strong types
+ case QChar::DirL:
+ if(dir == QChar::DirON)
+ dir = QChar::DirL;
+ switch(status.last)
+ {
+ case QChar::DirL:
+ eor = current; status.eor = QChar::DirL; break;
+ case QChar::DirR:
+ case QChar::DirAL:
+ case QChar::DirEN:
+ case QChar::DirAN:
+ if (eor >= 0) {
+ appendItems(analysis, sor, eor, control, dir);
+ dir = eor < length ? QChar::direction(unicode[eor]) : control.basicDirection();
+ status.eor = dir;
+ } else {
+ eor = current; status.eor = dir;
+ }
+ break;
+ case QChar::DirES:
+ case QChar::DirET:
+ case QChar::DirCS:
+ case QChar::DirBN:
+ case QChar::DirB:
+ case QChar::DirS:
+ case QChar::DirWS:
+ case QChar::DirON:
+ if(dir != QChar::DirL) {
+ //last stuff takes embedding dir
+ if(control.direction() == QChar::DirR) {
+ if(status.eor != QChar::DirR) {
+ // AN or EN
+ appendItems(analysis, sor, eor, control, dir);
+ status.eor = QChar::DirON;
+ dir = QChar::DirR;
+ }
+ eor = current - 1;
+ appendItems(analysis, sor, eor, control, dir);
+ dir = eor < length ? QChar::direction(unicode[eor]) : control.basicDirection();
+ status.eor = dir;
+ } else {
+ if(status.eor != QChar::DirL) {
+ appendItems(analysis, sor, eor, control, dir);
+ status.eor = QChar::DirON;
+ dir = QChar::DirL;
+ } else {
+ eor = current; status.eor = QChar::DirL; break;
+ }
+ }
+ } else {
+ eor = current; status.eor = QChar::DirL;
+ }
+ default:
+ break;
+ }
+ status.lastStrong = QChar::DirL;
+ break;
+ case QChar::DirAL:
+ case QChar::DirR:
+ hasBidi = true;
+ if(dir == QChar::DirON) dir = QChar::DirR;
+ switch(status.last)
+ {
+ case QChar::DirL:
+ case QChar::DirEN:
+ case QChar::DirAN:
+ if (eor >= 0)
+ appendItems(analysis, sor, eor, control, dir);
+ // fall through
+ case QChar::DirR:
+ case QChar::DirAL:
+ dir = QChar::DirR; eor = current; status.eor = QChar::DirR; break;
+ case QChar::DirES:
+ case QChar::DirET:
+ case QChar::DirCS:
+ case QChar::DirBN:
+ case QChar::DirB:
+ case QChar::DirS:
+ case QChar::DirWS:
+ case QChar::DirON:
+ if(status.eor != QChar::DirR && status.eor != QChar::DirAL) {
+ //last stuff takes embedding dir
+ if(control.direction() == QChar::DirR
+ || status.lastStrong == QChar::DirR || status.lastStrong == QChar::DirAL) {
+ appendItems(analysis, sor, eor, control, dir);
+ dir = QChar::DirR; status.eor = QChar::DirON;
+ eor = current;
+ } else {
+ eor = current - 1;
+ appendItems(analysis, sor, eor, control, dir);
+ dir = QChar::DirR; status.eor = QChar::DirON;
+ }
+ } else {
+ eor = current; status.eor = QChar::DirR;
+ }
+ default:
+ break;
+ }
+ status.lastStrong = dirCurrent;
+ break;
+
+ // weak types:
+
+ case QChar::DirNSM:
+ if (eor == current-1)
+ eor = current;
+ break;
+ case QChar::DirEN:
+ // if last strong was AL change EN to AN
+ if(status.lastStrong != QChar::DirAL) {
+ if(dir == QChar::DirON) {
+ if(status.lastStrong == QChar::DirL)
+ dir = QChar::DirL;
+ else
+ dir = QChar::DirEN;
+ }
+ switch(status.last)
+ {
+ case QChar::DirET:
+ if (status.lastStrong == QChar::DirR || status.lastStrong == QChar::DirAL) {
+ appendItems(analysis, sor, eor, control, dir);
+ status.eor = QChar::DirON;
+ dir = QChar::DirAN;
+ }
+ // fall through
+ case QChar::DirEN:
+ case QChar::DirL:
+ eor = current;
+ status.eor = dirCurrent;
+ break;
+ case QChar::DirR:
+ case QChar::DirAL:
+ case QChar::DirAN:
+ if (eor >= 0)
+ appendItems(analysis, sor, eor, control, dir);
+ else
+ eor = current;
+ status.eor = QChar::DirEN;
+ dir = QChar::DirAN; break;
+ case QChar::DirES:
+ case QChar::DirCS:
+ if(status.eor == QChar::DirEN || dir == QChar::DirAN) {
+ eor = current; break;
+ }
+ case QChar::DirBN:
+ case QChar::DirB:
+ case QChar::DirS:
+ case QChar::DirWS:
+ case QChar::DirON:
+ if(status.eor == QChar::DirR) {
+ // neutrals go to R
+ eor = current - 1;
+ appendItems(analysis, sor, eor, control, dir);
+ dir = QChar::DirON; status.eor = QChar::DirEN;
+ dir = QChar::DirAN;
+ }
+ else if(status.eor == QChar::DirL ||
+ (status.eor == QChar::DirEN && status.lastStrong == QChar::DirL)) {
+ eor = current; status.eor = dirCurrent;
+ } else {
+ // numbers on both sides, neutrals get right to left direction
+ if(dir != QChar::DirL) {
+ appendItems(analysis, sor, eor, control, dir);
+ dir = QChar::DirON; status.eor = QChar::DirON;
+ eor = current - 1;
+ dir = QChar::DirR;
+ appendItems(analysis, sor, eor, control, dir);
+ dir = QChar::DirON; status.eor = QChar::DirON;
+ dir = QChar::DirAN;
+ } else {
+ eor = current; status.eor = dirCurrent;
+ }
+ }
+ default:
+ break;
+ }
+ break;
+ }
+ case QChar::DirAN:
+ hasBidi = true;
+ dirCurrent = QChar::DirAN;
+ if(dir == QChar::DirON) dir = QChar::DirAN;
+ switch(status.last)
+ {
+ case QChar::DirL:
+ case QChar::DirAN:
+ eor = current; status.eor = QChar::DirAN; break;
+ case QChar::DirR:
+ case QChar::DirAL:
+ case QChar::DirEN:
+ if (eor >= 0){
+ appendItems(analysis, sor, eor, control, dir);
+ } else {
+ eor = current;
+ }
+ dir = QChar::DirON; status.eor = QChar::DirAN;
+ break;
+ case QChar::DirCS:
+ if(status.eor == QChar::DirAN) {
+ eor = current; break;
+ }
+ case QChar::DirES:
+ case QChar::DirET:
+ case QChar::DirBN:
+ case QChar::DirB:
+ case QChar::DirS:
+ case QChar::DirWS:
+ case QChar::DirON:
+ if(status.eor == QChar::DirR) {
+ // neutrals go to R
+ eor = current - 1;
+ appendItems(analysis, sor, eor, control, dir);
+ status.eor = QChar::DirAN;
+ dir = QChar::DirAN;
+ } else if(status.eor == QChar::DirL ||
+ (status.eor == QChar::DirEN && status.lastStrong == QChar::DirL)) {
+ eor = current; status.eor = dirCurrent;
+ } else {
+ // numbers on both sides, neutrals get right to left direction
+ if(dir != QChar::DirL) {
+ appendItems(analysis, sor, eor, control, dir);
+ status.eor = QChar::DirON;
+ eor = current - 1;
+ dir = QChar::DirR;
+ appendItems(analysis, sor, eor, control, dir);
+ status.eor = QChar::DirAN;
+ dir = QChar::DirAN;
+ } else {
+ eor = current; status.eor = dirCurrent;
+ }
+ }
+ default:
+ break;
+ }
+ break;
+ case QChar::DirES:
+ case QChar::DirCS:
+ break;
+ case QChar::DirET:
+ if(status.last == QChar::DirEN) {
+ dirCurrent = QChar::DirEN;
+ eor = current; status.eor = dirCurrent;
+ }
+ break;
+
+ // boundary neutrals should be ignored
+ case QChar::DirBN:
+ break;
+ // neutrals
+ case QChar::DirB:
+ // ### what do we do with newline and paragraph separators that come to here?
+ break;
+ case QChar::DirS:
+ // ### implement rule L1
+ break;
+ case QChar::DirWS:
+ case QChar::DirON:
+ break;
+ default:
+ break;
+ }
+
+ //qDebug() << " after: dir=" << // dir << " current=" << dirCurrent << " last=" << status.last << " eor=" << status.eor << " lastStrong=" << status.lastStrong << " embedding=" << control.direction();
+
+ if(current >= (int)length) break;
+
+ // set status.last as needed.
+ switch(dirCurrent) {
+ case QChar::DirET:
+ case QChar::DirES:
+ case QChar::DirCS:
+ case QChar::DirS:
+ case QChar::DirWS:
+ case QChar::DirON:
+ switch(status.last)
+ {
+ case QChar::DirL:
+ case QChar::DirR:
+ case QChar::DirAL:
+ case QChar::DirEN:
+ case QChar::DirAN:
+ status.last = dirCurrent;
+ break;
+ default:
+ status.last = QChar::DirON;
+ }
+ break;
+ case QChar::DirNSM:
+ case QChar::DirBN:
+ // ignore these
+ break;
+ case QChar::DirLRO:
+ case QChar::DirLRE:
+ status.last = QChar::DirL;
+ break;
+ case QChar::DirRLO:
+ case QChar::DirRLE:
+ status.last = QChar::DirR;
+ break;
+ case QChar::DirEN:
+ if (status.last == QChar::DirL) {
+ status.last = QChar::DirL;
+ break;
+ }
+ // fall through
+ default:
+ status.last = dirCurrent;
+ }
+
+ ++current;
+ }
+
+#if (BIDI_DEBUG >= 1)
+ qDebug() << "reached end of line current=" << current << ", eor=" << eor;
+#endif
+ eor = current - 1; // remove dummy char
+
+ if (sor <= eor)
+ appendItems(analysis, sor, eor, control, dir);
+
+ return hasBidi;
+}
+
+void QTextEngine::bidiReorder(int numItems, const quint8 *levels, int *visualOrder)
+{
+
+ // first find highest and lowest levels
+ quint8 levelLow = 128;
+ quint8 levelHigh = 0;
+ int i = 0;
+ while (i < numItems) {
+ //printf("level = %d\n", r->level);
+ if (levels[i] > levelHigh)
+ levelHigh = levels[i];
+ if (levels[i] < levelLow)
+ levelLow = levels[i];
+ i++;
+ }
+
+ // implements reordering of the line (L2 according to BiDi spec):
+ // L2. From the highest level found in the text to the lowest odd level on each line,
+ // reverse any contiguous sequence of characters that are at that level or higher.
+
+ // reversing is only done up to the lowest odd level
+ if(!(levelLow%2)) levelLow++;
+
+#if (BIDI_DEBUG >= 1)
+// qDebug() << "reorderLine: lineLow = " << (uint)levelLow << ", lineHigh = " << (uint)levelHigh;
+#endif
+
+ int count = numItems - 1;
+ for (i = 0; i < numItems; i++)
+ visualOrder[i] = i;
+
+ while(levelHigh >= levelLow) {
+ int i = 0;
+ while (i < count) {
+ while(i < count && levels[i] < levelHigh) i++;
+ int start = i;
+ while(i <= count && levels[i] >= levelHigh) i++;
+ int end = i-1;
+
+ if(start != end) {
+ //qDebug() << "reversing from " << start << " to " << end;
+ for(int j = 0; j < (end-start+1)/2; j++) {
+ int tmp = visualOrder[start+j];
+ visualOrder[start+j] = visualOrder[end-j];
+ visualOrder[end-j] = tmp;
+ }
+ }
+ i++;
+ }
+ levelHigh--;
+ }
+
+#if (BIDI_DEBUG >= 1)
+// qDebug() << "visual order is:";
+// for (i = 0; i < numItems; i++)
+// qDebug() << visualOrder[i];
+#endif
+}
+
+QT_BEGIN_INCLUDE_NAMESPACE
+
+#if defined(Q_WS_X11) || defined (Q_WS_QWS)
+# include "qfontengine_ft_p.h"
+#elif defined(Q_WS_MAC)
+# include "qtextengine_mac.cpp"
+#endif
+
+#include <private/qharfbuzz_p.h>
+
+QT_END_INCLUDE_NAMESPACE
+
+// ask the font engine to find out which glyphs (as an index in the specific font) to use for the text in one item.
+static bool stringToGlyphs(HB_ShaperItem *item, QGlyphLayout *glyphs, QFontEngine *fontEngine)
+{
+ int nGlyphs = item->num_glyphs;
+
+ QTextEngine::ShaperFlags shaperFlags(QTextEngine::GlyphIndicesOnly);
+ if (item->item.bidiLevel % 2)
+ shaperFlags |= QTextEngine::RightToLeft;
+
+ bool result = fontEngine->stringToCMap(reinterpret_cast<const QChar *>(item->string + item->item.pos), item->item.length, glyphs, &nGlyphs, shaperFlags);
+ item->num_glyphs = nGlyphs;
+ glyphs->numGlyphs = nGlyphs;
+ return result;
+}
+
+// shape all the items that intersect with the line, taking tab widths into account to find out what text actually fits in the line.
+void QTextEngine::shapeLine(const QScriptLine &line)
+{
+ QFixed x;
+ bool first = true;
+ const int end = findItem(line.from + line.length - 1);
+ int item = findItem(line.from);
+ if (item == -1)
+ return;
+ for (item = findItem(line.from); item <= end; ++item) {
+ QScriptItem &si = layoutData->items[item];
+ if (si.analysis.flags == QScriptAnalysis::Tab) {
+ ensureSpace(1);
+ si.width = calculateTabWidth(item, x);
+ } else {
+ shape(item);
+ }
+ if (first && si.position != line.from) { // that means our x position has to be offset
+ QGlyphLayout glyphs = shapedGlyphs(&si);
+ Q_ASSERT(line.from > si.position);
+ for (int i = line.from - si.position - 1; i >= 0; i--) {
+ x -= glyphs.effectiveAdvance(i);
+ }
+ }
+ first = false;
+
+ x += si.width;
+ }
+}
+
+extern int qt_defaultDpiY(); // in qfont.cpp
+
+void QTextEngine::shapeText(int item) const
+{
+ Q_ASSERT(item < layoutData->items.size());
+ QScriptItem &si = layoutData->items[item];
+
+ if (si.num_glyphs)
+ return;
+
+#if defined(Q_WS_MAC)
+ shapeTextMac(item);
+#elif defined(Q_OS_WINCE)
+ shapeTextWithCE(item);
+#else
+ shapeTextWithHarfbuzz(item);
+#endif
+
+ si.width = 0;
+
+ if (!si.num_glyphs)
+ return;
+ QGlyphLayout glyphs = shapedGlyphs(&si);
+
+ QFont font = this->font(si);
+ bool letterSpacingIsAbsolute = font.d->letterSpacingIsAbsolute;
+ QFixed letterSpacing = font.d->letterSpacing;
+ QFixed wordSpacing = font.d->wordSpacing;
+
+ if (letterSpacingIsAbsolute)
+ letterSpacing *= font.d->dpi / qt_defaultDpiY();
+
+ if (letterSpacing != 0) {
+ for (int i = 1; i < si.num_glyphs; ++i) {
+ if (glyphs.attributes[i].clusterStart) {
+ if (letterSpacingIsAbsolute)
+ glyphs.advances_x[i-1] += letterSpacing;
+ else {
+ const QFixed advance = glyphs.advances_x[i-1];
+ glyphs.advances_x[i-1] += (letterSpacing - 100) * advance / 100;
+ }
+ }
+ }
+ if (letterSpacingIsAbsolute)
+ glyphs.advances_x[si.num_glyphs-1] += letterSpacing;
+ else {
+ const QFixed advance = glyphs.advances_x[si.num_glyphs-1];
+ glyphs.advances_x[si.num_glyphs-1] += (letterSpacing - 100) * advance / 100;
+ }
+ }
+ if (wordSpacing != 0) {
+ for (int i = 0; i < si.num_glyphs; ++i) {
+ if (glyphs.attributes[i].justification == HB_Space
+ || glyphs.attributes[i].justification == HB_Arabic_Space) {
+ // word spacing only gets added once to a consecutive run of spaces (see CSS spec)
+ if (i + 1 == si.num_glyphs
+ ||(glyphs.attributes[i+1].justification != HB_Space
+ && glyphs.attributes[i+1].justification != HB_Arabic_Space))
+ glyphs.advances_x[i] += wordSpacing;
+ }
+ }
+ }
+
+ for (int i = 0; i < si.num_glyphs; ++i)
+ si.width += glyphs.advances_x[i];
+}
+
+#if defined(Q_OS_WINCE) //TODO
+// set the glyph attributes heuristically. Assumes a 1 to 1 relationship between chars and glyphs
+// and no reordering.
+// also computes logClusters heuristically
+static void heuristicSetGlyphAttributes(const QChar *uc, int length, QGlyphLayout *glyphs, unsigned short *logClusters, int num_glyphs)
+{
+ // ### zeroWidth and justification are missing here!!!!!
+
+ Q_UNUSED(num_glyphs);
+ Q_ASSERT(num_glyphs <= length);
+
+// qDebug("QScriptEngine::heuristicSetGlyphAttributes, num_glyphs=%d", item->num_glyphs);
+
+ int glyph_pos = 0;
+ for (int i = 0; i < length; i++) {
+ if (uc[i].unicode() >= 0xd800 && uc[i].unicode() < 0xdc00 && i < length-1
+ && uc[i+1].unicode() >= 0xdc00 && uc[i+1].unicode() < 0xe000) {
+ logClusters[i] = glyph_pos;
+ logClusters[++i] = glyph_pos;
+ } else {
+ logClusters[i] = glyph_pos;
+ }
+ ++glyph_pos;
+ }
+
+ // first char in a run is never (treated as) a mark
+ int cStart = 0;
+
+ const bool symbolFont = false; // ####
+ glyphs->attributes[0].mark = false;
+ glyphs->attributes[0].clusterStart = true;
+ glyphs->attributes[0].dontPrint = (!symbolFont && uc[0].unicode() == 0x00ad) || qIsControlChar(uc[0].unicode());
+
+ int pos = 0;
+ int lastCat = QChar::category(uc[0].unicode());
+ for (int i = 1; i < length; ++i) {
+ if (logClusters[i] == pos)
+ // same glyph
+ continue;
+ ++pos;
+ while (pos < logClusters[i]) {
+ glyphs[pos].attributes = glyphs[pos-1].attributes;
+ ++pos;
+ }
+ // hide soft-hyphens by default
+ if ((!symbolFont && uc[i].unicode() == 0x00ad) || qIsControlChar(uc[i].unicode()))
+ glyphs->attributes[pos].dontPrint = true;
+ const QUnicodeTables::Properties *prop = QUnicodeTables::properties(uc[i].unicode());
+ int cat = prop->category;
+ if (cat != QChar::Mark_NonSpacing) {
+ glyphs->attributes[pos].mark = false;
+ glyphs->attributes[pos].clusterStart = true;
+ glyphs->attributes[pos].combiningClass = 0;
+ cStart = logClusters[i];
+ } else {
+ int cmb = prop->combiningClass;
+
+ if (cmb == 0) {
+ // Fix 0 combining classes
+ if ((uc[pos].unicode() & 0xff00) == 0x0e00) {
+ // thai or lao
+ unsigned char col = uc[pos].cell();
+ if (col == 0x31 ||
+ col == 0x34 ||
+ col == 0x35 ||
+ col == 0x36 ||
+ col == 0x37 ||
+ col == 0x47 ||
+ col == 0x4c ||
+ col == 0x4d ||
+ col == 0x4e) {
+ cmb = QChar::Combining_AboveRight;
+ } else if (col == 0xb1 ||
+ col == 0xb4 ||
+ col == 0xb5 ||
+ col == 0xb6 ||
+ col == 0xb7 ||
+ col == 0xbb ||
+ col == 0xcc ||
+ col == 0xcd) {
+ cmb = QChar::Combining_Above;
+ } else if (col == 0xbc) {
+ cmb = QChar::Combining_Below;
+ }
+ }
+ }
+
+ glyphs->attributes[pos].mark = true;
+ glyphs->attributes[pos].clusterStart = false;
+ glyphs->attributes[pos].combiningClass = cmb;
+ logClusters[i] = cStart;
+ glyphs->advances_x[pos] = 0;
+ glyphs->advances_y[pos] = 0;
+ }
+
+ // one gets an inter character justification point if the current char is not a non spacing mark.
+ // as then the current char belongs to the last one and one gets a space justification point
+ // after the space char.
+ if (lastCat == QChar::Separator_Space)
+ glyphs->attributes[pos-1].justification = HB_Space;
+ else if (cat != QChar::Mark_NonSpacing)
+ glyphs->attributes[pos-1].justification = HB_Character;
+ else
+ glyphs->attributes[pos-1].justification = HB_NoJustification;
+
+ lastCat = cat;
+ }
+ pos = logClusters[length-1];
+ if (lastCat == QChar::Separator_Space)
+ glyphs->attributes[pos].justification = HB_Space;
+ else
+ glyphs->attributes[pos].justification = HB_Character;
+}
+
+void QTextEngine::shapeTextWithCE(int item) const
+{
+ QScriptItem &si = layoutData->items[item];
+ si.glyph_data_offset = layoutData->used;
+
+ QFontEngine *fe = fontEngine(si, &si.ascent, &si.descent);
+
+ QTextEngine::ShaperFlags flags;
+ if (si.analysis.bidiLevel % 2)
+ flags |= RightToLeft;
+ if (option.useDesignMetrics())
+ flags |= DesignMetrics;
+
+ attributes(); // pre-initialize char attributes
+
+ const int len = length(item);
+ int num_glyphs = length(item);
+ const QChar *str = layoutData->string.unicode() + si.position;
+ ushort upperCased[256];
+ if (si.analysis.flags == QScriptAnalysis::SmallCaps || si.analysis.flags == QScriptAnalysis::Uppercase
+ || si.analysis.flags == QScriptAnalysis::Lowercase) {
+ ushort *uc = upperCased;
+ if (len > 256)
+ uc = new ushort[len];
+ for (int i = 0; i < len; ++i) {
+ if(si.analysis.flags == QScriptAnalysis::Lowercase)
+ uc[i] = str[i].toLower().unicode();
+ else
+ uc[i] = str[i].toUpper().unicode();
+ }
+ str = reinterpret_cast<const QChar *>(uc);
+ }
+
+ while (true) {
+ ensureSpace(num_glyphs);
+ num_glyphs = layoutData->glyphLayout.numGlyphs - layoutData->used;
+
+ QGlyphLayout g = availableGlyphs(&si);
+ unsigned short *log_clusters = logClusters(&si);
+
+ if (fe->stringToCMap(str,
+ len,
+ &g,
+ &num_glyphs,
+ flags)) {
+ heuristicSetGlyphAttributes(str, len, &g, log_clusters, num_glyphs);
+ break;
+ }
+ }
+
+ si.num_glyphs = num_glyphs;
+
+ layoutData->used += si.num_glyphs;
+
+ const ushort *uc = reinterpret_cast<const ushort *>(str);
+ if ((si.analysis.flags == QScriptAnalysis::SmallCaps || si.analysis.flags == QScriptAnalysis::Uppercase
+ || si.analysis.flags == QScriptAnalysis::Lowercase)
+ && uc != upperCased)
+ delete [] uc;
+}
+#endif
+
+/// take the item from layoutData->items and
+void QTextEngine::shapeTextWithHarfbuzz(int item) const
+{
+ Q_ASSERT(sizeof(HB_Fixed) == sizeof(QFixed));
+ Q_ASSERT(sizeof(HB_FixedPoint) == sizeof(QFixedPoint));
+
+ QScriptItem &si = layoutData->items[item];
+
+ si.glyph_data_offset = layoutData->used;
+
+ QFontEngine *font = fontEngine(si, &si.ascent, &si.descent);
+
+ bool kerningEnabled = this->font(si).d->kerning;
+
+ HB_ShaperItem entire_shaper_item;
+ entire_shaper_item.kerning_applied = false;
+ entire_shaper_item.string = reinterpret_cast<const HB_UChar16 *>(layoutData->string.constData());
+ entire_shaper_item.stringLength = layoutData->string.length();
+ entire_shaper_item.item.script = (HB_Script)si.analysis.script;
+ entire_shaper_item.item.pos = si.position;
+ entire_shaper_item.item.length = length(item);
+ entire_shaper_item.item.bidiLevel = si.analysis.bidiLevel;
+ entire_shaper_item.glyphIndicesPresent = false;
+
+ HB_UChar16 upperCased[256]; // XXX what about making this 4096, so we don't have to extend it ever.
+ if (si.analysis.flags == QScriptAnalysis::SmallCaps || si.analysis.flags == QScriptAnalysis::Uppercase
+ || si.analysis.flags == QScriptAnalysis::Lowercase) {
+ HB_UChar16 *uc = upperCased;
+ if (entire_shaper_item.item.length > 256)
+ uc = new HB_UChar16[entire_shaper_item.item.length];
+ for (uint i = 0; i < entire_shaper_item.item.length; ++i) {
+ if(si.analysis.flags == QScriptAnalysis::Lowercase)
+ uc[i] = QChar::toLower(entire_shaper_item.string[si.position + i]);
+ else
+ uc[i] = QChar::toUpper(entire_shaper_item.string[si.position + i]);
+ }
+ entire_shaper_item.item.pos = 0;
+ entire_shaper_item.string = uc;
+ entire_shaper_item.stringLength = entire_shaper_item.item.length;
+ }
+
+ entire_shaper_item.shaperFlags = 0;
+ if (!kerningEnabled)
+ entire_shaper_item.shaperFlags |= HB_ShaperFlag_NoKerning;
+ if (option.useDesignMetrics())
+ entire_shaper_item.shaperFlags |= HB_ShaperFlag_UseDesignMetrics;
+
+ entire_shaper_item.num_glyphs = qMax(layoutData->glyphLayout.numGlyphs - layoutData->used, int(entire_shaper_item.item.length));
+ ensureSpace(entire_shaper_item.num_glyphs);
+ QGlyphLayout initialGlyphs = availableGlyphs(&si).mid(0, entire_shaper_item.num_glyphs);
+
+ if (!stringToGlyphs(&entire_shaper_item, &initialGlyphs, font)) {
+ ensureSpace(entire_shaper_item.num_glyphs);
+ initialGlyphs = availableGlyphs(&si).mid(0, entire_shaper_item.num_glyphs);
+
+ if (!stringToGlyphs(&entire_shaper_item, &initialGlyphs, font)) {
+ // ############ if this happens there's a bug in the fontengine
+ if ((si.analysis.flags == QScriptAnalysis::SmallCaps || si.analysis.flags == QScriptAnalysis::Uppercase
+ || si.analysis.flags == QScriptAnalysis::Lowercase) && entire_shaper_item.string != upperCased)
+ delete [] const_cast<HB_UChar16 *>(entire_shaper_item.string);
+ return;
+ }
+ }
+
+ // split up the item into parts that come from different font engines.
+ QVarLengthArray<int> itemBoundaries(2);
+ // k * 2 entries, array[k] == index in string, array[k + 1] == index in glyphs
+ itemBoundaries[0] = entire_shaper_item.item.pos;
+ itemBoundaries[1] = 0;
+
+ if (font->type() == QFontEngine::Multi) {
+ uint lastEngine = 0;
+ int charIdx = entire_shaper_item.item.pos;
+ const int stringEnd = charIdx + entire_shaper_item.item.length;
+ for (quint32 i = 0; i < entire_shaper_item.num_glyphs; ++i, ++charIdx) {
+ uint engineIdx = initialGlyphs.glyphs[i] >> 24;
+ if (engineIdx != lastEngine && i > 0) {
+ itemBoundaries.append(charIdx);
+ itemBoundaries.append(i);
+ }
+ lastEngine = engineIdx;
+ if (HB_IsHighSurrogate(entire_shaper_item.string[charIdx])
+ && charIdx < stringEnd - 1
+ && HB_IsLowSurrogate(entire_shaper_item.string[charIdx + 1]))
+ ++charIdx;
+ }
+ }
+
+
+
+ int initial_glyph_pos = 0;
+ int glyph_pos = 0;
+ // for each item shape using harfbuzz and store the results in our layoutData's glyphs array.
+ for (int k = 0; k < itemBoundaries.size(); k += 2) { // for the +2, see the comment at the definition of itemBoundaries
+
+ HB_ShaperItem shaper_item = entire_shaper_item;
+
+ shaper_item.item.pos = itemBoundaries[k];
+ if (k < itemBoundaries.size() - 3) {
+ shaper_item.item.length = itemBoundaries[k + 2] - shaper_item.item.pos;
+ shaper_item.num_glyphs = itemBoundaries[k + 3] - itemBoundaries[k + 1];
+ } else { // last combo in the list, avoid out of bounds access.
+ shaper_item.item.length -= shaper_item.item.pos - entire_shaper_item.item.pos;
+ shaper_item.num_glyphs -= itemBoundaries[k + 1];
+ }
+ shaper_item.initialGlyphCount = shaper_item.num_glyphs;
+
+ QFontEngine *actualFontEngine = font;
+ uint engineIdx = 0;
+ if (font->type() == QFontEngine::Multi) {
+ engineIdx = uint(initialGlyphs.glyphs[itemBoundaries[k + 1]] >> 24);
+
+ actualFontEngine = static_cast<QFontEngineMulti *>(font)->engine(engineIdx);
+ }
+
+ shaper_item.font = actualFontEngine->harfbuzzFont();
+ shaper_item.face = actualFontEngine->harfbuzzFace();
+
+ shaper_item.glyphIndicesPresent = true;
+
+ do {
+ ensureSpace(glyph_pos + shaper_item.num_glyphs);
+ initialGlyphs = availableGlyphs(&si).mid(0, entire_shaper_item.num_glyphs);
+ shaper_item.num_glyphs = layoutData->glyphLayout.numGlyphs - layoutData->used - glyph_pos;
+
+ const QGlyphLayout g = availableGlyphs(&si);
+ shaper_item.glyphs = g.glyphs + glyph_pos;
+ shaper_item.attributes = g.attributes + glyph_pos;
+ shaper_item.advances = reinterpret_cast<HB_Fixed *>(g.advances_x + glyph_pos);
+ shaper_item.offsets = reinterpret_cast<HB_FixedPoint *>(g.offsets + glyph_pos);
+
+ if (shaper_item.glyphIndicesPresent) {
+ for (hb_uint32 i = 0; i < shaper_item.initialGlyphCount; ++i)
+ shaper_item.glyphs[i] &= 0x00ffffff;
+ }
+
+ shaper_item.log_clusters = logClusters(&si) + shaper_item.item.pos - entire_shaper_item.item.pos;
+
+// qDebug(" .. num_glyphs=%d, used=%d, item.num_glyphs=%d", num_glyphs, used, shaper_item.num_glyphs);
+ } while (!qShapeItem(&shaper_item)); // this does the actual shaping via harfbuzz.
+
+ QGlyphLayout g = availableGlyphs(&si).mid(glyph_pos, shaper_item.num_glyphs);
+
+ for (hb_uint32 i = 0; i < shaper_item.item.length; ++i) {
+ g.glyphs[i] = g.glyphs[i] | (engineIdx << 24);
+ shaper_item.log_clusters[i] += glyph_pos;
+ }
+
+ if (kerningEnabled && !shaper_item.kerning_applied)
+ font->doKerning(&g, option.useDesignMetrics() ? QFlag(QTextEngine::DesignMetrics) : QFlag(0));
+
+ glyph_pos += shaper_item.num_glyphs;
+
+ initial_glyph_pos += shaper_item.initialGlyphCount;
+ }
+
+// qDebug(" -> item: script=%d num_glyphs=%d", shaper_item.script, shaper_item.num_glyphs);
+ si.num_glyphs = glyph_pos;
+
+ layoutData->used += si.num_glyphs;
+
+ if ((si.analysis.flags == QScriptAnalysis::SmallCaps || si.analysis.flags == QScriptAnalysis::Uppercase)
+ && entire_shaper_item.string != upperCased)
+ delete [] const_cast<HB_UChar16 *>(entire_shaper_item.string);
+}
+
+static void init(QTextEngine *e)
+{
+ e->ignoreBidi = false;
+ e->cacheGlyphs = false;
+ e->forceJustification = false;
+
+ e->layoutData = 0;
+
+ e->minWidth = 0;
+ e->maxWidth = 0;
+
+ e->underlinePositions = 0;
+ e->specialData = 0;
+ e->stackEngine = false;
+}
+
+QTextEngine::QTextEngine()
+{
+ init(this);
+}
+
+QTextEngine::QTextEngine(const QString &str, const QFont &f)
+ : fnt(f)
+{
+ init(this);
+ text = str;
+}
+
+QTextEngine::~QTextEngine()
+{
+ if (!stackEngine)
+ delete layoutData;
+ delete specialData;
+}
+
+const HB_CharAttributes *QTextEngine::attributes() const
+{
+ if (layoutData && layoutData->haveCharAttributes)
+ return (HB_CharAttributes *) layoutData->memory;
+
+ itemize();
+ ensureSpace(layoutData->string.length());
+
+ QVarLengthArray<HB_ScriptItem> hbScriptItems(layoutData->items.size());
+
+ for (int i = 0; i < layoutData->items.size(); ++i) {
+ const QScriptItem &si = layoutData->items[i];
+ hbScriptItems[i].pos = si.position;
+ hbScriptItems[i].length = length(i);
+ hbScriptItems[i].bidiLevel = si.analysis.bidiLevel;
+ hbScriptItems[i].script = (HB_Script)si.analysis.script;
+ }
+
+ qGetCharAttributes(reinterpret_cast<const HB_UChar16 *>(layoutData->string.constData()),
+ layoutData->string.length(),
+ hbScriptItems.data(), hbScriptItems.size(),
+ (HB_CharAttributes *)layoutData->memory);
+
+
+ layoutData->haveCharAttributes = true;
+ return (HB_CharAttributes *) layoutData->memory;
+}
+
+void QTextEngine::shape(int item) const
+{
+ if (layoutData->items[item].analysis.flags == QScriptAnalysis::Object) {
+ ensureSpace(1);
+ if (block.docHandle()) {
+ QTextFormat format = formats()->format(formatIndex(&layoutData->items[item]));
+ docLayout()->resizeInlineObject(QTextInlineObject(item, const_cast<QTextEngine *>(this)),
+ layoutData->items[item].position + block.position(), format);
+ }
+ } else if (layoutData->items[item].analysis.flags == QScriptAnalysis::Tab) {
+ // set up at least the ascent/descent of the script item for the tab
+ fontEngine(layoutData->items[item], &layoutData->items[item].ascent, &layoutData->items[item].descent);
+ } else {
+ shapeText(item);
+ }
+}
+
+void QTextEngine::invalidate()
+{
+ freeMemory();
+ minWidth = 0;
+ maxWidth = 0;
+ if (specialData)
+ specialData->resolvedFormatIndices.clear();
+}
+
+void QTextEngine::clearLineData()
+{
+ lines.clear();
+}
+
+void QTextEngine::validate() const
+{
+ if (layoutData)
+ return;
+ layoutData = new LayoutData();
+ if (block.docHandle()) {
+ layoutData->string = block.text();
+ if (option.flags() & QTextOption::ShowLineAndParagraphSeparators)
+ layoutData->string += QLatin1Char(block.next().isValid() ? 0xb6 : 0x20);
+ } else {
+ layoutData->string = text;
+ }
+ if (specialData && specialData->preeditPosition != -1)
+ layoutData->string.insert(specialData->preeditPosition, specialData->preeditText);
+}
+
+void QTextEngine::itemize() const
+{
+ validate();
+ if (layoutData->items.size())
+ return;
+
+ int length = layoutData->string.length();
+ if (!length)
+ return;
+
+ bool ignore = ignoreBidi;
+ if (!ignore && option.textDirection() == Qt::LeftToRight) {
+ ignore = true;
+ const QChar *start = layoutData->string.unicode();
+ const QChar * const end = start + length;
+ while (start < end) {
+ if (start->unicode() >= 0x590) {
+ ignore = false;
+ break;
+ }
+ ++start;
+ }
+ }
+
+ QVarLengthArray<QScriptAnalysis, 4096> scriptAnalysis(length);
+ QScriptAnalysis *analysis = scriptAnalysis.data();
+
+ QBidiControl control(option.textDirection() == Qt::RightToLeft);
+
+ if (ignore) {
+ memset(analysis, 0, length*sizeof(QScriptAnalysis));
+ if (option.textDirection() == Qt::RightToLeft) {
+ for (int i = 0; i < length; ++i)
+ analysis[i].bidiLevel = 1;
+ layoutData->hasBidi = true;
+ }
+ } else {
+ layoutData->hasBidi = bidiItemize(const_cast<QTextEngine *>(this), analysis, control);
+ }
+
+ const ushort *unicode = layoutData->string.utf16();
+ // correctly assign script, isTab and isObject to the script analysis
+ const ushort *uc = unicode;
+ const ushort *e = uc + length;
+ int lastScript = QUnicodeTables::Common;
+ while (uc < e) {
+ int script = QUnicodeTables::script(*uc);
+ if (script == QUnicodeTables::Inherited)
+ script = lastScript;
+ analysis->flags = QScriptAnalysis::None;
+ if (*uc == QChar::ObjectReplacementCharacter) {
+ if (analysis->bidiLevel % 2)
+ --analysis->bidiLevel;
+ analysis->script = QUnicodeTables::Common;
+ analysis->flags = QScriptAnalysis::Object;
+ } else if (*uc == QChar::LineSeparator) {
+ if (analysis->bidiLevel % 2)
+ --analysis->bidiLevel;
+ analysis->script = QUnicodeTables::Common;
+ analysis->flags = QScriptAnalysis::LineOrParagraphSeparator;
+ if (option.flags() & QTextOption::ShowLineAndParagraphSeparators)
+ *const_cast<ushort*>(uc) = 0x21B5; // visual line separator
+ } else if (*uc == 9) {
+ analysis->script = QUnicodeTables::Common;
+ analysis->flags = QScriptAnalysis::Tab;
+ analysis->bidiLevel = control.baseLevel();
+ } else if ((*uc == 32 || *uc == QChar::Nbsp)
+ && (option.flags() & QTextOption::ShowTabsAndSpaces)) {
+ analysis->script = QUnicodeTables::Common;
+ analysis->flags = QScriptAnalysis::Space;
+ analysis->bidiLevel = control.baseLevel();
+ } else {
+ analysis->script = script;
+ }
+ lastScript = analysis->script;
+ ++uc;
+ ++analysis;
+ }
+ if (option.flags() & QTextOption::ShowLineAndParagraphSeparators) {
+ (analysis-1)->flags = QScriptAnalysis::LineOrParagraphSeparator; // to exclude it from width
+ }
+
+ Itemizer itemizer(layoutData->string, scriptAnalysis.data(), layoutData->items);
+
+ const QTextDocumentPrivate *p = block.docHandle();
+ if (p) {
+ SpecialData *s = specialData;
+
+ QTextDocumentPrivate::FragmentIterator it = p->find(block.position());
+ QTextDocumentPrivate::FragmentIterator end = p->find(block.position() + block.length() - 1); // -1 to omit the block separator char
+ int format = it.value()->format;
+
+ int prevPosition = 0;
+ int position = prevPosition;
+ while (1) {
+ const QTextFragmentData * const frag = it.value();
+ if (it == end || format != frag->format) {
+ if (s && position >= s->preeditPosition) {
+ position += s->preeditText.length();
+ s = 0;
+ }
+ Q_ASSERT(position <= length);
+ itemizer.generate(prevPosition, position - prevPosition,
+ formats()->charFormat(format).fontCapitalization());
+ if (it == end) {
+ if (position < length)
+ itemizer.generate(position, length - position,
+ formats()->charFormat(format).fontCapitalization());
+ break;
+ }
+ format = frag->format;
+ prevPosition = position;
+ }
+ position += frag->size_array[0];
+ ++it;
+ }
+ } else {
+ itemizer.generate(0, length, static_cast<QFont::Capitalization> (fnt.d->capital));
+ }
+
+ addRequiredBoundaries();
+ resolveAdditionalFormats();
+}
+
+int QTextEngine::findItem(int strPos) const
+{
+ itemize();
+
+ // ##### use binary search
+ int item;
+ for (item = layoutData->items.size()-1; item > 0; --item) {
+ if (layoutData->items[item].position <= strPos)
+ break;
+ }
+ return item;
+}
+
+QFixed QTextEngine::width(int from, int len) const
+{
+ itemize();
+
+ QFixed w = 0;
+
+// qDebug("QTextEngine::width(from = %d, len = %d), numItems=%d, strleng=%d", from, len, items.size(), string.length());
+ for (int i = 0; i < layoutData->items.size(); i++) {
+ const QScriptItem *si = layoutData->items.constData() + i;
+ int pos = si->position;
+ int ilen = length(i);
+// qDebug("item %d: from %d len %d", i, pos, ilen);
+ if (pos >= from + len)
+ break;
+ if (pos + ilen > from) {
+ if (!si->num_glyphs)
+ shape(i);
+
+ if (si->analysis.flags == QScriptAnalysis::Object) {
+ w += si->width;
+ continue;
+ } else if (si->analysis.flags == QScriptAnalysis::Tab) {
+ w += calculateTabWidth(i, w);
+ continue;
+ }
+
+
+ QGlyphLayout glyphs = shapedGlyphs(si);
+ unsigned short *logClusters = this->logClusters(si);
+
+// fprintf(stderr, " logclusters:");
+// for (int k = 0; k < ilen; k++)
+// fprintf(stderr, " %d", logClusters[k]);
+// fprintf(stderr, "\n");
+ // do the simple thing for now and give the first glyph in a cluster the full width, all other ones 0.
+ int charFrom = from - pos;
+ if (charFrom < 0)
+ charFrom = 0;
+ int glyphStart = logClusters[charFrom];
+ if (charFrom > 0 && logClusters[charFrom-1] == glyphStart)
+ while (charFrom < ilen && logClusters[charFrom] == glyphStart)
+ charFrom++;
+ if (charFrom < ilen) {
+ glyphStart = logClusters[charFrom];
+ int charEnd = from + len - 1 - pos;
+ if (charEnd >= ilen)
+ charEnd = ilen-1;
+ int glyphEnd = logClusters[charEnd];
+ while (charEnd < ilen && logClusters[charEnd] == glyphEnd)
+ charEnd++;
+ glyphEnd = (charEnd == ilen) ? si->num_glyphs : logClusters[charEnd];
+
+// qDebug("char: start=%d end=%d / glyph: start = %d, end = %d", charFrom, charEnd, glyphStart, glyphEnd);
+ for (int i = glyphStart; i < glyphEnd; i++)
+ w += glyphs.advances_x[i] * !glyphs.attributes[i].dontPrint;
+ }
+ }
+ }
+// qDebug(" --> w= %d ", w);
+ return w;
+}
+
+glyph_metrics_t QTextEngine::boundingBox(int from, int len) const
+{
+ itemize();
+
+ glyph_metrics_t gm;
+
+ for (int i = 0; i < layoutData->items.size(); i++) {
+ const QScriptItem *si = layoutData->items.constData() + i;
+ int pos = si->position;
+ int ilen = length(i);
+ if (pos > from + len)
+ break;
+ if (pos + len > from) {
+ if (!si->num_glyphs)
+ shape(i);
+
+ if (si->analysis.flags == QScriptAnalysis::Object) {
+ gm.width += si->width;
+ continue;
+ } else if (si->analysis.flags == QScriptAnalysis::Tab) {
+ gm.width += calculateTabWidth(i, gm.width);
+ continue;
+ }
+
+ unsigned short *logClusters = this->logClusters(si);
+ QGlyphLayout glyphs = shapedGlyphs(si);
+
+ // do the simple thing for now and give the first glyph in a cluster the full width, all other ones 0.
+ int charFrom = from - pos;
+ if (charFrom < 0)
+ charFrom = 0;
+ int glyphStart = logClusters[charFrom];
+ if (charFrom > 0 && logClusters[charFrom-1] == glyphStart)
+ while (charFrom < ilen && logClusters[charFrom] == glyphStart)
+ charFrom++;
+ if (charFrom < ilen) {
+ glyphStart = logClusters[charFrom];
+ int charEnd = from + len - 1 - pos;
+ if (charEnd >= ilen)
+ charEnd = ilen-1;
+ int glyphEnd = logClusters[charEnd];
+ while (charEnd < ilen && logClusters[charEnd] == glyphEnd)
+ charEnd++;
+ glyphEnd = (charEnd == ilen) ? si->num_glyphs : logClusters[charEnd];
+ if (glyphStart <= glyphEnd ) {
+ QFontEngine *fe = fontEngine(*si);
+ glyph_metrics_t m = fe->boundingBox(glyphs.mid(glyphStart, glyphEnd - glyphStart));
+ gm.x = qMin(gm.x, m.x + gm.xoff);
+ gm.y = qMin(gm.y, m.y + gm.yoff);
+ gm.width = qMax(gm.width, m.width+gm.xoff);
+ gm.height = qMax(gm.height, m.height+gm.yoff);
+ gm.xoff += m.xoff;
+ gm.yoff += m.yoff;
+ }
+ }
+ }
+ }
+ return gm;
+}
+
+glyph_metrics_t QTextEngine::tightBoundingBox(int from, int len) const
+{
+ itemize();
+
+ glyph_metrics_t gm;
+
+ for (int i = 0; i < layoutData->items.size(); i++) {
+ const QScriptItem *si = layoutData->items.constData() + i;
+ int pos = si->position;
+ int ilen = length(i);
+ if (pos > from + len)
+ break;
+ if (pos + len > from) {
+ if (!si->num_glyphs)
+ shape(i);
+ unsigned short *logClusters = this->logClusters(si);
+ QGlyphLayout glyphs = shapedGlyphs(si);
+
+ // do the simple thing for now and give the first glyph in a cluster the full width, all other ones 0.
+ int charFrom = from - pos;
+ if (charFrom < 0)
+ charFrom = 0;
+ int glyphStart = logClusters[charFrom];
+ if (charFrom > 0 && logClusters[charFrom-1] == glyphStart)
+ while (charFrom < ilen && logClusters[charFrom] == glyphStart)
+ charFrom++;
+ if (charFrom < ilen) {
+ glyphStart = logClusters[charFrom];
+ int charEnd = from + len - 1 - pos;
+ if (charEnd >= ilen)
+ charEnd = ilen-1;
+ int glyphEnd = logClusters[charEnd];
+ while (charEnd < ilen && logClusters[charEnd] == glyphEnd)
+ charEnd++;
+ glyphEnd = (charEnd == ilen) ? si->num_glyphs : logClusters[charEnd];
+ if (glyphStart <= glyphEnd ) {
+ QFontEngine *fe = fontEngine(*si);
+ glyph_metrics_t m = fe->tightBoundingBox(glyphs.mid(glyphStart, glyphEnd - glyphStart));
+ gm.x = qMin(gm.x, m.x + gm.xoff);
+ gm.y = qMin(gm.y, m.y + gm.yoff);
+ gm.width = qMax(gm.width, m.width+gm.xoff);
+ gm.height = qMax(gm.height, m.height+gm.yoff);
+ gm.xoff += m.xoff;
+ gm.yoff += m.yoff;
+ }
+ }
+ }
+ }
+ return gm;
+}
+
+QFont QTextEngine::font(const QScriptItem &si) const
+{
+ QFont font = fnt;
+ if (hasFormats()) {
+ QTextCharFormat f = format(&si);
+ font = f.font();
+
+ if (block.docHandle() && block.docHandle()->layout()) {
+ // Make sure we get the right dpi on printers
+ QPaintDevice *pdev = block.docHandle()->layout()->paintDevice();
+ if (pdev)
+ font = QFont(font, pdev);
+ } else {
+ font = font.resolve(fnt);
+ }
+ QTextCharFormat::VerticalAlignment valign = f.verticalAlignment();
+ if (valign == QTextCharFormat::AlignSuperScript || valign == QTextCharFormat::AlignSubScript) {
+ if (font.pointSize() != -1)
+ font.setPointSize((font.pointSize() * 2) / 3);
+ else
+ font.setPixelSize((font.pixelSize() * 2) / 3);
+ }
+ }
+
+ if (si.analysis.flags == QScriptAnalysis::SmallCaps)
+ font = font.d->smallCapsFont();
+
+ return font;
+}
+
+QFontEngine *QTextEngine::fontEngine(const QScriptItem &si, QFixed *ascent, QFixed *descent) const
+{
+ QFontEngine *engine = 0;
+ QFontEngine *scaledEngine = 0;
+ int script = si.analysis.script;
+
+ QFont font = fnt;
+ if (hasFormats()) {
+ QTextCharFormat f = format(&si);
+ font = f.font();
+
+ if (block.docHandle() && block.docHandle()->layout()) {
+ // Make sure we get the right dpi on printers
+ QPaintDevice *pdev = block.docHandle()->layout()->paintDevice();
+ if (pdev)
+ font = QFont(font, pdev);
+ } else {
+ font = font.resolve(fnt);
+ }
+ engine = font.d->engineForScript(script);
+ QTextCharFormat::VerticalAlignment valign = f.verticalAlignment();
+ if (valign == QTextCharFormat::AlignSuperScript || valign == QTextCharFormat::AlignSubScript) {
+ if (font.pointSize() != -1)
+ font.setPointSize((font.pointSize() * 2) / 3);
+ else
+ font.setPixelSize((font.pixelSize() * 2) / 3);
+ scaledEngine = font.d->engineForScript(script);
+ }
+ } else {
+ engine = font.d->engineForScript(script);
+ }
+
+ if (si.analysis.flags == QScriptAnalysis::SmallCaps) {
+ QFontPrivate *p = font.d->smallCapsFontPrivate();
+ scaledEngine = p->engineForScript(script);
+ }
+
+ if (ascent) {
+ *ascent = engine->ascent();
+ *descent = engine->descent();
+ }
+
+ if (scaledEngine)
+ return scaledEngine;
+ return engine;
+}
+
+struct QJustificationPoint {
+ int type;
+ QFixed kashidaWidth;
+ QGlyphLayout glyph;
+ QFontEngine *fontEngine;
+};
+
+Q_DECLARE_TYPEINFO(QJustificationPoint, Q_PRIMITIVE_TYPE);
+
+static void set(QJustificationPoint *point, int type, const QGlyphLayout &glyph, QFontEngine *fe)
+{
+ point->type = type;
+ point->glyph = glyph;
+ point->fontEngine = fe;
+
+ if (type >= HB_Arabic_Normal) {
+ QChar ch(0x640); // Kashida character
+ QGlyphLayoutArray<8> glyphs;
+ int nglyphs = 7;
+ fe->stringToCMap(&ch, 1, &glyphs, &nglyphs, 0);
+ if (glyphs.glyphs[0] && glyphs.advances_x[0] != 0) {
+ point->kashidaWidth = glyphs.advances_x[0];
+ } else {
+ point->type = HB_NoJustification;
+ point->kashidaWidth = 0;
+ }
+ }
+}
+
+
+void QTextEngine::justify(const QScriptLine &line)
+{
+// qDebug("justify: line.gridfitted = %d, line.justified=%d", line.gridfitted, line.justified);
+ if (line.gridfitted && line.justified)
+ return;
+
+ if (!line.gridfitted) {
+ // redo layout in device metrics, then adjust
+ const_cast<QScriptLine &>(line).gridfitted = true;
+ }
+
+ if ((option.alignment() & Qt::AlignHorizontal_Mask) != Qt::AlignJustify)
+ return;
+
+ itemize();
+
+ if (!forceJustification) {
+ int end = line.from + (int)line.length;
+ if (end == layoutData->string.length())
+ return; // no justification at end of paragraph
+ if (end && layoutData->items[findItem(end-1)].analysis.flags == QScriptAnalysis::LineOrParagraphSeparator)
+ return; // no justification at the end of an explicitely separated line
+ }
+
+ // justify line
+ int maxJustify = 0;
+
+ // don't include trailing white spaces when doing justification
+ int line_length = line.length;
+ const HB_CharAttributes *a = attributes()+line.from;
+ while (line_length && a[line_length-1].whiteSpace)
+ --line_length;
+ // subtract one char more, as we can't justfy after the last character
+ --line_length;
+
+ if (!line_length)
+ return;
+
+ int firstItem = findItem(line.from);
+ int nItems = findItem(line.from + line_length - 1) - firstItem + 1;
+
+ QVarLengthArray<QJustificationPoint> justificationPoints;
+ int nPoints = 0;
+// qDebug("justifying from %d len %d, firstItem=%d, nItems=%d (%s)", line.from, line_length, firstItem, nItems, layoutData->string.mid(line.from, line_length).toUtf8().constData());
+ QFixed minKashida = 0x100000;
+
+ // we need to do all shaping before we go into the next loop, as we there
+ // store pointers to the glyph data that could get reallocated by the shaping
+ // process.
+ for (int i = 0; i < nItems; ++i) {
+ QScriptItem &si = layoutData->items[firstItem + i];
+ if (!si.num_glyphs)
+ shape(firstItem + i);
+ }
+
+ for (int i = 0; i < nItems; ++i) {
+ QScriptItem &si = layoutData->items[firstItem + i];
+
+ int kashida_type = HB_Arabic_Normal;
+ int kashida_pos = -1;
+
+ int start = qMax(line.from - si.position, 0);
+ int end = qMin(line.from + line_length - (int)si.position, length(firstItem+i));
+
+ unsigned short *log_clusters = logClusters(&si);
+
+ int gs = log_clusters[start];
+ int ge = (end == length(firstItem+i) ? si.num_glyphs : log_clusters[end]);
+
+ const QGlyphLayout g = shapedGlyphs(&si);
+
+ for (int i = gs; i < ge; ++i) {
+ g.justifications[i].type = QGlyphJustification::JustifyNone;
+ g.justifications[i].nKashidas = 0;
+ g.justifications[i].space_18d6 = 0;
+
+ justificationPoints.resize(nPoints+3);
+ int justification = g.attributes[i].justification;
+
+ switch(justification) {
+ case HB_NoJustification:
+ break;
+ case HB_Space :
+ // fall through
+ case HB_Arabic_Space :
+ if (kashida_pos >= 0) {
+// qDebug("kashida position at %d in word", kashida_pos);
+ set(&justificationPoints[nPoints], kashida_type, g.mid(kashida_pos), fontEngine(si));
+ minKashida = qMin(minKashida, justificationPoints[nPoints].kashidaWidth);
+ maxJustify = qMax(maxJustify, justificationPoints[nPoints].type);
+ ++nPoints;
+ }
+ kashida_pos = -1;
+ kashida_type = HB_Arabic_Normal;
+ // fall through
+ case HB_Character :
+ set(&justificationPoints[nPoints++], justification, g.mid(i), fontEngine(si));
+ maxJustify = qMax(maxJustify, justification);
+ break;
+ case HB_Arabic_Normal :
+ case HB_Arabic_Waw :
+ case HB_Arabic_BaRa :
+ case HB_Arabic_Alef :
+ case HB_Arabic_HaaDal :
+ case HB_Arabic_Seen :
+ case HB_Arabic_Kashida :
+ if (justification >= kashida_type) {
+ kashida_pos = i;
+ kashida_type = justification;
+ }
+ }
+ }
+ if (kashida_pos >= 0) {
+ set(&justificationPoints[nPoints], kashida_type, g.mid(kashida_pos), fontEngine(si));
+ minKashida = qMin(minKashida, justificationPoints[nPoints].kashidaWidth);
+ maxJustify = qMax(maxJustify, justificationPoints[nPoints].type);
+ ++nPoints;
+ }
+ }
+
+ QFixed need = line.width - line.textWidth;
+ if (need < 0) {
+ // line overflows already!
+ const_cast<QScriptLine &>(line).justified = true;
+ return;
+ }
+
+// qDebug("doing justification: textWidth=%x, requested=%x, maxJustify=%d", line.textWidth.value(), line.width.value(), maxJustify);
+// qDebug(" minKashida=%f, need=%f", minKashida.toReal(), need.toReal());
+
+ // distribute in priority order
+ if (maxJustify >= HB_Arabic_Normal) {
+ while (need >= minKashida) {
+ for (int type = maxJustify; need >= minKashida && type >= HB_Arabic_Normal; --type) {
+ for (int i = 0; need >= minKashida && i < nPoints; ++i) {
+ if (justificationPoints[i].type == type && justificationPoints[i].kashidaWidth <= need) {
+ justificationPoints[i].glyph.justifications->nKashidas++;
+ // ############
+ justificationPoints[i].glyph.justifications->space_18d6 += justificationPoints[i].kashidaWidth.value();
+ need -= justificationPoints[i].kashidaWidth;
+// qDebug("adding kashida type %d with width %x, neednow %x", type, justificationPoints[i].kashidaWidth, need.value());
+ }
+ }
+ }
+ }
+ }
+ Q_ASSERT(need >= 0);
+ if (!need)
+ goto end;
+
+ maxJustify = qMin(maxJustify, (int)HB_Space);
+ for (int type = maxJustify; need != 0 && type > 0; --type) {
+ int n = 0;
+ for (int i = 0; i < nPoints; ++i) {
+ if (justificationPoints[i].type == type)
+ ++n;
+ }
+// qDebug("number of points for justification type %d: %d", type, n);
+
+
+ if (!n)
+ continue;
+
+ for (int i = 0; i < nPoints; ++i) {
+ if (justificationPoints[i].type == type) {
+ QFixed add = need/n;
+// qDebug("adding %x to glyph %x", add.value(), justificationPoints[i].glyph->glyph);
+ justificationPoints[i].glyph.justifications[0].space_18d6 = add.value();
+ need -= add;
+ --n;
+ }
+ }
+
+ Q_ASSERT(!need);
+ }
+ end:
+ const_cast<QScriptLine &>(line).justified = true;
+}
+
+void QScriptLine::setDefaultHeight(QTextEngine *eng)
+{
+ QFont f;
+ QFontEngine *e;
+
+ if (eng->block.docHandle() && eng->block.docHandle()->layout()) {
+ f = eng->block.charFormat().font();
+ // Make sure we get the right dpi on printers
+ QPaintDevice *pdev = eng->block.docHandle()->layout()->paintDevice();
+ if (pdev)
+ f = QFont(f, pdev);
+ e = f.d->engineForScript(QUnicodeTables::Common);
+ } else {
+ e = eng->fnt.d->engineForScript(QUnicodeTables::Common);
+ }
+
+ ascent = qMax(ascent, e->ascent());
+ descent = qMax(descent, e->descent());
+}
+
+QTextEngine::LayoutData::LayoutData()
+{
+ memory = 0;
+ allocated = 0;
+ memory_on_stack = false;
+ used = 0;
+ hasBidi = false;
+ inLayout = false;
+ haveCharAttributes = false;
+ logClustersPtr = 0;
+ available_glyphs = 0;
+}
+
+QTextEngine::LayoutData::LayoutData(const QString &str, void **stack_memory, int _allocated)
+ : string(str)
+{
+ allocated = _allocated;
+
+ int space_charAttributes = sizeof(HB_CharAttributes)*string.length()/sizeof(void*) + 1;
+ int space_logClusters = sizeof(unsigned short)*string.length()/sizeof(void*) + 1;
+ available_glyphs = ((int)allocated - space_charAttributes - space_logClusters)*(int)sizeof(void*)/(int)QGlyphLayout::spaceNeededForGlyphLayout(1);
+
+ if (available_glyphs < str.length()) {
+ // need to allocate on the heap
+ allocated = 0;
+
+ memory_on_stack = false;
+ memory = 0;
+ logClustersPtr = 0;
+ } else {
+ memory_on_stack = true;
+ memory = stack_memory;
+ logClustersPtr = (unsigned short *)(memory + space_charAttributes);
+
+ void *m = memory + space_charAttributes + space_logClusters;
+ glyphLayout = QGlyphLayout(reinterpret_cast<char *>(m), str.length());
+ glyphLayout.clear();
+ memset(memory, 0, space_charAttributes*sizeof(void *));
+ }
+ used = 0;
+ hasBidi = false;
+ inLayout = false;
+ haveCharAttributes = false;
+}
+
+QTextEngine::LayoutData::~LayoutData()
+{
+ if (!memory_on_stack)
+ free(memory);
+ memory = 0;
+}
+
+void QTextEngine::LayoutData::reallocate(int totalGlyphs)
+{
+ Q_ASSERT(totalGlyphs >= glyphLayout.numGlyphs);
+ if (memory_on_stack && available_glyphs >= totalGlyphs) {
+ glyphLayout.grow(glyphLayout.data(), totalGlyphs);
+ return;
+ }
+
+ int space_charAttributes = sizeof(HB_CharAttributes)*string.length()/sizeof(void*) + 1;
+ int space_logClusters = sizeof(unsigned short)*string.length()/sizeof(void*) + 1;
+ int space_glyphs = QGlyphLayout::spaceNeededForGlyphLayout(totalGlyphs)/sizeof(void*) + 2;
+
+ int newAllocated = space_charAttributes + space_glyphs + space_logClusters;
+ Q_ASSERT(newAllocated >= allocated);
+ void **old_mem = memory;
+ memory = (void **)::realloc(memory_on_stack ? 0 : old_mem, newAllocated*sizeof(void *));
+ if (memory_on_stack && memory)
+ memcpy(memory, old_mem, allocated*sizeof(void *));
+ memory_on_stack = false;
+
+ void **m = memory;
+ m += space_charAttributes;
+ logClustersPtr = (unsigned short *) m;
+ m += space_logClusters;
+
+ const int space_preGlyphLayout = space_charAttributes + space_logClusters;
+ if (allocated < space_preGlyphLayout)
+ memset(memory + allocated, 0, (space_preGlyphLayout - allocated)*sizeof(void *));
+
+ glyphLayout.grow(reinterpret_cast<char *>(m), totalGlyphs);
+
+ allocated = newAllocated;
+}
+
+// grow to the new size, copying the existing data to the new layout
+void QGlyphLayout::grow(char *address, int totalGlyphs)
+{
+ QGlyphLayout oldLayout(address, numGlyphs);
+ QGlyphLayout newLayout(address, totalGlyphs);
+
+ if (numGlyphs) {
+ // move the existing data
+ memmove(newLayout.attributes, oldLayout.attributes, numGlyphs * sizeof(HB_GlyphAttributes));
+ memmove(newLayout.justifications, oldLayout.justifications, numGlyphs * sizeof(QGlyphJustification));
+ memmove(newLayout.advances_y, oldLayout.advances_y, numGlyphs * sizeof(QFixed));
+ memmove(newLayout.advances_x, oldLayout.advances_x, numGlyphs * sizeof(QFixed));
+ memmove(newLayout.glyphs, oldLayout.glyphs, numGlyphs * sizeof(HB_Glyph));
+ }
+
+ // clear the new data
+ newLayout.clear(numGlyphs);
+
+ *this = newLayout;
+}
+
+void QTextEngine::freeMemory()
+{
+ if (!stackEngine) {
+ delete layoutData;
+ layoutData = 0;
+ } else {
+ layoutData->used = 0;
+ layoutData->hasBidi = false;
+ layoutData->inLayout = false;
+ layoutData->haveCharAttributes = false;
+ }
+ for (int i = 0; i < lines.size(); ++i) {
+ lines[i].justified = 0;
+ lines[i].gridfitted = 0;
+ }
+}
+
+int QTextEngine::formatIndex(const QScriptItem *si) const
+{
+ if (specialData && !specialData->resolvedFormatIndices.isEmpty())
+ return specialData->resolvedFormatIndices.at(si - &layoutData->items[0]);
+ QTextDocumentPrivate *p = block.docHandle();
+ if (!p)
+ return -1;
+ int pos = si->position;
+ if (specialData && si->position >= specialData->preeditPosition) {
+ if (si->position < specialData->preeditPosition + specialData->preeditText.length())
+ pos = qMax(specialData->preeditPosition - 1, 0);
+ else
+ pos -= specialData->preeditText.length();
+ }
+ QTextDocumentPrivate::FragmentIterator it = p->find(block.position() + pos);
+ return it.value()->format;
+}
+
+
+QTextCharFormat QTextEngine::format(const QScriptItem *si) const
+{
+ QTextCharFormat format;
+ const QTextFormatCollection *formats = 0;
+ if (block.docHandle()) {
+ formats = this->formats();
+ format = formats->charFormat(formatIndex(si));
+ }
+ if (specialData && specialData->resolvedFormatIndices.isEmpty()) {
+ int end = si->position + length(si);
+ for (int i = 0; i < specialData->addFormats.size(); ++i) {
+ const QTextLayout::FormatRange &r = specialData->addFormats.at(i);
+ if (r.start <= si->position && r.start + r.length >= end) {
+ if (!specialData->addFormatIndices.isEmpty())
+ format.merge(formats->format(specialData->addFormatIndices.at(i)));
+ else
+ format.merge(r.format);
+ }
+ }
+ }
+ return format;
+}
+
+void QTextEngine::addRequiredBoundaries() const
+{
+ if (specialData) {
+ for (int i = 0; i < specialData->addFormats.size(); ++i) {
+ const QTextLayout::FormatRange &r = specialData->addFormats.at(i);
+ setBoundary(r.start);
+ setBoundary(r.start + r.length);
+ //qDebug("adding boundaries %d %d", r.start, r.start+r.length);
+ }
+ }
+}
+
+bool QTextEngine::atWordSeparator(int position) const
+{
+ const QChar c = layoutData->string.at(position);
+ switch (c.toLatin1()) {
+ case '.':
+ case ',':
+ case '?':
+ case '!':
+ case ':':
+ case ';':
+ case '-':
+ case '<':
+ case '>':
+ case '[':
+ case ']':
+ case '(':
+ case ')':
+ case '{':
+ case '}':
+ case '=':
+ case '/':
+ case '+':
+ case '%':
+ case '&':
+ case '^':
+ case '*':
+ case '\'':
+ case '"':
+ case '~':
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool QTextEngine::atSpace(int position) const
+{
+ const QChar c = layoutData->string.at(position);
+
+ return c == QLatin1Char(' ')
+ || c == QChar::Nbsp
+ || c == QChar::LineSeparator
+ || c == QLatin1Char('\t')
+ ;
+}
+
+
+void QTextEngine::indexAdditionalFormats()
+{
+ if (!block.docHandle())
+ return;
+
+ specialData->addFormatIndices.resize(specialData->addFormats.count());
+ QTextFormatCollection * const formats = this->formats();
+
+ for (int i = 0; i < specialData->addFormats.count(); ++i) {
+ specialData->addFormatIndices[i] = formats->indexForFormat(specialData->addFormats.at(i).format);
+ specialData->addFormats[i].format = QTextCharFormat();
+ }
+}
+
+QString QTextEngine::elidedText(Qt::TextElideMode mode, const QFixed &width, int flags) const
+{
+// qDebug() << "elidedText; available width" << width.toReal() << "text width:" << this->width(0, layoutData->string.length()).toReal();
+
+ if (flags & Qt::TextShowMnemonic) {
+ itemize();
+ for (int i = 0; i < layoutData->items.size(); ++i) {
+ QScriptItem &si = layoutData->items[i];
+ if (!si.num_glyphs)
+ shape(i);
+
+ HB_CharAttributes *attributes = const_cast<HB_CharAttributes *>(this->attributes());
+ unsigned short *logClusters = this->logClusters(&si);
+ QGlyphLayout glyphs = shapedGlyphs(&si);
+
+ const int end = si.position + length(&si);
+ for (int i = si.position; i < end - 1; ++i)
+ if (layoutData->string.at(i) == QLatin1Char('&')) {
+ const int gp = logClusters[i - si.position];
+ glyphs.attributes[gp].dontPrint = true;
+ attributes[i + 1].charStop = false;
+ attributes[i + 1].whiteSpace = false;
+ attributes[i + 1].lineBreakType = HB_NoBreak;
+ if (i < end - 1
+ && layoutData->string.at(i + 1) == QLatin1Char('&'))
+ ++i;
+ }
+ }
+ }
+
+ validate();
+
+ if (mode == Qt::ElideNone
+ || this->width(0, layoutData->string.length()) <= width
+ || layoutData->string.length() <= 1)
+ return layoutData->string;
+
+ QFixed ellipsisWidth;
+ QString ellipsisText;
+ {
+ QChar ellipsisChar(0x2026);
+
+ QFontEngine *fe = fnt.d->engineForScript(QUnicodeTables::Common);
+
+ QGlyphLayoutArray<1> ellipsisGlyph;
+ {
+ QFontEngine *feForEllipsis = (fe->type() == QFontEngine::Multi)
+ ? static_cast<QFontEngineMulti *>(fe)->engine(0)
+ : fe;
+
+ if (feForEllipsis->type() == QFontEngine::Mac)
+ feForEllipsis = fe;
+
+ // the lookup can be really slow when we use XLFD fonts
+ if (feForEllipsis->type() != QFontEngine::XLFD
+ && feForEllipsis->canRender(&ellipsisChar, 1)) {
+ int nGlyphs = 1;
+ feForEllipsis->stringToCMap(&ellipsisChar, 1, &ellipsisGlyph, &nGlyphs, 0);
+ }
+ }
+
+ if (ellipsisGlyph.glyphs[0]) {
+ ellipsisWidth = ellipsisGlyph.advances_x[0];
+ ellipsisText = ellipsisChar;
+ } else {
+ QString dotDotDot(QLatin1String("..."));
+
+ QGlyphLayoutArray<3> glyphs;
+ int nGlyphs = 3;
+ if (!fe->stringToCMap(dotDotDot.constData(), 3, &glyphs, &nGlyphs, 0))
+ // should never happen...
+ return layoutData->string;
+ for (int i = 0; i < nGlyphs; ++i)
+ ellipsisWidth += glyphs.advances_x[i];
+ ellipsisText = dotDotDot;
+ }
+ }
+
+ const QFixed availableWidth = width - ellipsisWidth;
+ if (availableWidth < 0)
+ return QString();
+
+ const HB_CharAttributes *attributes = this->attributes();
+
+ if (mode == Qt::ElideRight) {
+ QFixed currentWidth;
+ int pos = 0;
+ int nextBreak = 0;
+
+ do {
+ pos = nextBreak;
+
+ ++nextBreak;
+ while (nextBreak < layoutData->string.length() && !attributes[nextBreak].charStop)
+ ++nextBreak;
+
+ currentWidth += this->width(pos, nextBreak - pos);
+ } while (nextBreak < layoutData->string.length()
+ && currentWidth < availableWidth);
+
+ return layoutData->string.left(pos) + ellipsisText;
+ } else if (mode == Qt::ElideLeft) {
+ QFixed currentWidth;
+ int pos = layoutData->string.length();
+ int nextBreak = layoutData->string.length();
+
+ do {
+ pos = nextBreak;
+
+ --nextBreak;
+ while (nextBreak > 0 && !attributes[nextBreak].charStop)
+ --nextBreak;
+
+ currentWidth += this->width(nextBreak, pos - nextBreak);
+ } while (nextBreak > 0
+ && currentWidth < availableWidth);
+
+ return ellipsisText + layoutData->string.mid(pos);
+ } else if (mode == Qt::ElideMiddle) {
+ QFixed leftWidth;
+ QFixed rightWidth;
+
+ int leftPos = 0;
+ int nextLeftBreak = 0;
+
+ int rightPos = layoutData->string.length();
+ int nextRightBreak = layoutData->string.length();
+
+ do {
+ leftPos = nextLeftBreak;
+ rightPos = nextRightBreak;
+
+ ++nextLeftBreak;
+ while (nextLeftBreak < layoutData->string.length() && !attributes[nextLeftBreak].charStop)
+ ++nextLeftBreak;
+
+ --nextRightBreak;
+ while (nextRightBreak > 0 && !attributes[nextRightBreak].charStop)
+ --nextRightBreak;
+
+ leftWidth += this->width(leftPos, nextLeftBreak - leftPos);
+ rightWidth += this->width(nextRightBreak, rightPos - nextRightBreak);
+ } while (nextLeftBreak < layoutData->string.length()
+ && nextRightBreak > 0
+ && leftWidth + rightWidth < availableWidth);
+
+ return layoutData->string.left(leftPos) + ellipsisText + layoutData->string.mid(rightPos);
+ }
+
+ return layoutData->string;
+}
+
+void QTextEngine::setBoundary(int strPos) const
+{
+ if (strPos <= 0 || strPos >= layoutData->string.length())
+ return;
+
+ int itemToSplit = 0;
+ while (itemToSplit < layoutData->items.size() && layoutData->items[itemToSplit].position <= strPos)
+ itemToSplit++;
+ itemToSplit--;
+ if (layoutData->items[itemToSplit].position == strPos) {
+ // already a split at the requested position
+ return;
+ }
+ splitItem(itemToSplit, strPos - layoutData->items[itemToSplit].position);
+}
+
+void QTextEngine::splitItem(int item, int pos) const
+{
+ if (pos <= 0)
+ return;
+
+ layoutData->items.insert(item + 1, QScriptItem(layoutData->items[item]));
+ QScriptItem &oldItem = layoutData->items[item];
+ QScriptItem &newItem = layoutData->items[item+1];
+ newItem.position += pos;
+
+ if (oldItem.num_glyphs) {
+ // already shaped, break glyphs aswell
+ int breakGlyph = logClusters(&oldItem)[pos];
+
+ newItem.num_glyphs = oldItem.num_glyphs - breakGlyph;
+ oldItem.num_glyphs = breakGlyph;
+ newItem.glyph_data_offset = oldItem.glyph_data_offset + breakGlyph;
+
+ for (int i = 0; i < newItem.num_glyphs; i++)
+ logClusters(&newItem)[i] -= breakGlyph;
+
+ QFixed w = 0;
+ const QGlyphLayout g = shapedGlyphs(&oldItem);
+ for(int j = 0; j < breakGlyph; ++j)
+ w += g.advances_x[j];
+
+ newItem.width = oldItem.width - w;
+ oldItem.width = w;
+ }
+
+// qDebug("split at position %d itempos=%d", pos, item);
+}
+
+extern int qt_defaultDpiY();
+
+QFixed QTextEngine::calculateTabWidth(int item, QFixed x) const
+{
+ const QScriptItem &si = layoutData->items[item];
+
+ QFixed dpiScale = 1;
+ if (block.docHandle() && block.docHandle()->layout()) {
+ QPaintDevice *pdev = block.docHandle()->layout()->paintDevice();
+ if (pdev)
+ dpiScale = QFixed::fromReal(pdev->logicalDpiY() / qreal(qt_defaultDpiY()));
+ } else {
+ dpiScale = QFixed::fromReal(fnt.d->dpi / qreal(qt_defaultDpiY()));
+ }
+
+ QList<QTextOption::Tab> tabArray = option.tabs();
+ if (!tabArray.isEmpty()) {
+ if (option.textDirection() == Qt::RightToLeft) { // rebase the tabArray positions.
+ QList<QTextOption::Tab> newTabs;
+ QList<QTextOption::Tab>::Iterator iter = tabArray.begin();
+ while(iter != tabArray.end()) {
+ QTextOption::Tab tab = *iter;
+ if (tab.type == QTextOption::LeftTab)
+ tab.type = QTextOption::RightTab;
+ else if (tab.type == QTextOption::RightTab)
+ tab.type = QTextOption::LeftTab;
+ newTabs << tab;
+ ++iter;
+ }
+ tabArray = newTabs;
+ }
+ for (int i = 0; i < tabArray.size(); ++i) {
+ QFixed tab = QFixed::fromReal(tabArray[i].position) * dpiScale;
+ if (tab > x) { // this is the tab we need.
+ QTextOption::Tab tabSpec = tabArray[i];
+ int tabSectionEnd = layoutData->string.count();
+ if (tabSpec.type == QTextOption::RightTab || tabSpec.type == QTextOption::CenterTab) {
+ // find next tab to calculate the width required.
+ tab = QFixed::fromReal(tabSpec.position);
+ for (int i=item + 1; i < layoutData->items.count(); i++) {
+ const QScriptItem &item = layoutData->items[i];
+ if (item.analysis.flags == QScriptAnalysis::TabOrObject) { // found it.
+ tabSectionEnd = item.position;
+ break;
+ }
+ }
+ }
+ else if (tabSpec.type == QTextOption::DelimiterTab)
+ // find delimitor character to calculate the width required
+ tabSectionEnd = qMax(si.position, layoutData->string.indexOf(tabSpec.delimiter, si.position) + 1);
+
+ if (tabSectionEnd > si.position) {
+ QFixed length;
+ // Calculate the length of text between this tab and the tabSectionEnd
+ for (int i=item; i < layoutData->items.count(); i++) {
+ QScriptItem &item = layoutData->items[i];
+ if (item.position > tabSectionEnd || item.position <= si.position)
+ continue;
+ shape(i); // first, lets make sure relevant text is already shaped
+ QGlyphLayout glyphs = this->shapedGlyphs(&item);
+ const int end = qMin(item.position + item.num_glyphs, tabSectionEnd) - item.position;
+ for (int i=0; i < end; i++)
+ length += glyphs.advances_x[i] * !glyphs.attributes[i].dontPrint;
+ if (end + item.position == tabSectionEnd && tabSpec.type == QTextOption::DelimiterTab) // remove half of matching char
+ length -= glyphs.advances_x[end] / 2 * !glyphs.attributes[end].dontPrint;
+ }
+
+ switch (tabSpec.type) {
+ case QTextOption::CenterTab:
+ length /= 2;
+ // fall through
+ case QTextOption::DelimiterTab:
+ // fall through
+ case QTextOption::RightTab:
+ tab = QFixed::fromReal(tabSpec.position) * dpiScale - length;
+ if (tab < 0) // default to tab taking no space
+ return QFixed();
+ break;
+ case QTextOption::LeftTab:
+ break;
+ }
+ }
+ return tab - x;
+ }
+ }
+ }
+ QFixed tab = QFixed::fromReal(option.tabStop());
+ if (tab <= 0)
+ tab = 80; // default
+ tab *= dpiScale;
+ QFixed nextTabPos = ((x / tab).truncate() + 1) * tab;
+ QFixed tabWidth = nextTabPos - x;
+
+ return tabWidth;
+}
+
+void QTextEngine::resolveAdditionalFormats() const
+{
+ if (!specialData || specialData->addFormats.isEmpty()
+ || !block.docHandle()
+ || !specialData->resolvedFormatIndices.isEmpty())
+ return;
+
+ QTextFormatCollection *collection = this->formats();
+
+ specialData->resolvedFormatIndices.clear();
+ QVector<int> indices(layoutData->items.count());
+ for (int i = 0; i < layoutData->items.count(); ++i) {
+ QTextCharFormat f = format(&layoutData->items.at(i));
+ indices[i] = collection->indexForFormat(f);
+ }
+ specialData->resolvedFormatIndices = indices;
+}
+
+QStackTextEngine::QStackTextEngine(const QString &string, const QFont &f)
+ : _layoutData(string, _memory, MemSize)
+{
+ fnt = f;
+ text = string;
+ stackEngine = true;
+ layoutData = &_layoutData;
+}
+
+QTextItemInt::QTextItemInt(const QScriptItem &si, QFont *font, const QTextCharFormat &format)
+ : justified(false), underlineStyle(QTextCharFormat::NoUnderline), charFormat(format),
+ num_chars(0), chars(0), logClusters(0), f(0), fontEngine(0)
+{
+ // explicitly initialize flags so that initFontAttributes can be called
+ // multiple times on the same TextItem
+ flags = 0;
+ if (si.analysis.bidiLevel %2)
+ flags |= QTextItem::RightToLeft;
+ ascent = si.ascent;
+ descent = si.descent;
+ f = font;
+ fontEngine = f->d->engineForScript(si.analysis.script);
+ Q_ASSERT(fontEngine);
+
+ if (format.hasProperty(QTextFormat::TextUnderlineStyle)) {
+ underlineStyle = format.underlineStyle();
+ } else if (format.boolProperty(QTextFormat::FontUnderline)
+ || f->d->underline) {
+ underlineStyle = QTextCharFormat::SingleUnderline;
+ }
+
+ // compat
+ if (underlineStyle == QTextCharFormat::SingleUnderline)
+ flags |= QTextItem::Underline;
+
+ if (f->d->overline || format.fontOverline())
+ flags |= QTextItem::Overline;
+ if (f->d->strikeOut || format.fontStrikeOut())
+ flags |= QTextItem::StrikeOut;
+}
+
+QTextItemInt QTextItemInt::midItem(QFontEngine *fontEngine, int firstGlyphIndex, int numGlyphs) const
+{
+ QTextItemInt ti = *this;
+ const int end = firstGlyphIndex + numGlyphs;
+ ti.glyphs = glyphs.mid(firstGlyphIndex, numGlyphs);
+ ti.fontEngine = fontEngine;
+
+ if (logClusters && chars) {
+ const int logClusterOffset = logClusters[0];
+ while (logClusters[ti.chars - chars] - logClusterOffset < firstGlyphIndex)
+ ++ti.chars;
+
+ ti.logClusters += (ti.chars - chars);
+
+ ti.num_chars = 0;
+ int char_start = ti.chars - chars;
+ while (char_start + ti.num_chars < num_chars && ti.logClusters[ti.num_chars] - logClusterOffset < end)
+ ++ti.num_chars;
+ }
+ return ti;
+}
+
+
+QTransform qt_true_matrix(qreal w, qreal h, QTransform x)
+{
+ QRectF rect = x.mapRect(QRectF(0, 0, w, h));
+ return x * QTransform::fromTranslate(-rect.x(), -rect.y());
+}
+
+
+glyph_metrics_t glyph_metrics_t::transformed(const QTransform &matrix) const
+{
+ if (matrix.type() < QTransform::TxTranslate)
+ return *this;
+
+ glyph_metrics_t m = *this;
+
+ qreal w = width.toReal();
+ qreal h = height.toReal();
+ QTransform xform = qt_true_matrix(w, h, matrix);
+
+ QRectF rect(0, 0, w, h);
+ rect = xform.mapRect(rect);
+ m.width = QFixed::fromReal(rect.width());
+ m.height = QFixed::fromReal(rect.height());
+
+ QLineF l = xform.map(QLineF(x.toReal(), y.toReal(), xoff.toReal(), yoff.toReal()));
+
+ m.x = QFixed::fromReal(l.x1());
+ m.y = QFixed::fromReal(l.y1());
+
+ // The offset is relative to the baseline which is why we use dx/dy of the line
+ m.xoff = QFixed::fromReal(l.dx());
+ m.yoff = QFixed::fromReal(l.dy());
+
+ return m;
+}
+
+
+QT_END_NAMESPACE
diff --git a/src/gui/text/qtextengine_mac.cpp b/src/gui/text/qtextengine_mac.cpp
new file mode 100644
index 0000000000..d122317f7d
--- /dev/null
+++ b/src/gui/text/qtextengine_mac.cpp
@@ -0,0 +1,656 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qtextengine_p.h"
+
+QT_BEGIN_NAMESPACE
+
+// set the glyph attributes heuristically. Assumes a 1 to 1 relationship between chars and glyphs
+// and no reordering.
+// also computes logClusters heuristically
+static void heuristicSetGlyphAttributes(const QChar *uc, int length, QGlyphLayout *glyphs, unsigned short *logClusters, int num_glyphs)
+{
+ // ### zeroWidth and justification are missing here!!!!!
+
+ Q_ASSERT(num_glyphs <= length);
+ Q_UNUSED(num_glyphs);
+
+// qDebug("QScriptEngine::heuristicSetGlyphAttributes, num_glyphs=%d", item->num_glyphs);
+
+ const bool symbolFont = false; // ####
+ glyphs->attributes[0].mark = false;
+ glyphs->attributes[0].clusterStart = true;
+ glyphs->attributes[0].dontPrint = (!symbolFont && uc[0].unicode() == 0x00ad) || qIsControlChar(uc[0].unicode());
+
+ int pos = 0;
+ int lastCat = QChar::category(uc[0].unicode());
+ for (int i = 1; i < length; ++i) {
+ if (logClusters[i] == pos)
+ // same glyph
+ continue;
+ ++pos;
+ while (pos < logClusters[i]) {
+ ++pos;
+ }
+ // hide soft-hyphens by default
+ if ((!symbolFont && uc[i].unicode() == 0x00ad) || qIsControlChar(uc[i].unicode()))
+ glyphs->attributes[pos].dontPrint = true;
+ const QUnicodeTables::Properties *prop = QUnicodeTables::properties(uc[i].unicode());
+ int cat = prop->category;
+
+ // one gets an inter character justification point if the current char is not a non spacing mark.
+ // as then the current char belongs to the last one and one gets a space justification point
+ // after the space char.
+ if (lastCat == QChar::Separator_Space)
+ glyphs->attributes[pos-1].justification = HB_Space;
+ else if (cat != QChar::Mark_NonSpacing)
+ glyphs->attributes[pos-1].justification = HB_Character;
+ else
+ glyphs->attributes[pos-1].justification = HB_NoJustification;
+
+ lastCat = cat;
+ }
+ pos = logClusters[length-1];
+ if (lastCat == QChar::Separator_Space)
+ glyphs->attributes[pos].justification = HB_Space;
+ else
+ glyphs->attributes[pos].justification = HB_Character;
+}
+
+struct QArabicProperties {
+ unsigned char shape;
+ unsigned char justification;
+};
+Q_DECLARE_TYPEINFO(QArabicProperties, Q_PRIMITIVE_TYPE);
+
+enum QArabicShape {
+ XIsolated,
+ XFinal,
+ XInitial,
+ XMedial,
+ // intermediate state
+ XCausing
+};
+
+
+// these groups correspond to the groups defined in the Unicode standard.
+// Some of these groups are equal with regards to both joining and line breaking behaviour,
+// and thus have the same enum value
+//
+// I'm not sure the mapping of syriac to arabic enums is correct with regards to justification, but as
+// I couldn't find any better document I'll hope for the best.
+enum ArabicGroup {
+ // NonJoining
+ ArabicNone,
+ ArabicSpace,
+ // Transparent
+ Transparent,
+ // Causing
+ Center,
+ Kashida,
+
+ // Arabic
+ // Dual
+ Beh,
+ Noon,
+ Meem = Noon,
+ Heh = Noon,
+ KnottedHeh = Noon,
+ HehGoal = Noon,
+ SwashKaf = Noon,
+ Yeh,
+ Hah,
+ Seen,
+ Sad = Seen,
+ Tah,
+ Kaf = Tah,
+ Gaf = Tah,
+ Lam = Tah,
+ Ain,
+ Feh = Ain,
+ Qaf = Ain,
+ // Right
+ Alef,
+ Waw,
+ Dal,
+ TehMarbuta = Dal,
+ Reh,
+ HamzaOnHehGoal,
+ YehWithTail = HamzaOnHehGoal,
+ YehBarre = HamzaOnHehGoal,
+
+ // Syriac
+ // Dual
+ Beth = Beh,
+ Gamal = Ain,
+ Heth = Noon,
+ Teth = Hah,
+ Yudh = Noon,
+ Kaph = Noon,
+ Lamadh = Lam,
+ Mim = Noon,
+ Nun = Noon,
+ Semakh = Noon,
+ FinalSemakh = Noon,
+ SyriacE = Ain,
+ Pe = Ain,
+ ReversedPe = Hah,
+ Qaph = Noon,
+ Shin = Noon,
+ Fe = Ain,
+
+ // Right
+ Alaph = Alef,
+ Dalath = Dal,
+ He = Dal,
+ SyriacWaw = Waw,
+ Zain = Alef,
+ YudhHe = Waw,
+ Sadhe = HamzaOnHehGoal,
+ Taw = Dal,
+
+ // Compiler bug? Otherwise ArabicGroupsEnd would be equal to Dal + 1.
+ Dummy = HamzaOnHehGoal,
+ ArabicGroupsEnd
+};
+
+static const unsigned char arabic_group[0x150] = {
+ ArabicNone, ArabicNone, ArabicNone, ArabicNone,
+ ArabicNone, ArabicNone, ArabicNone, ArabicNone,
+ ArabicNone, ArabicNone, ArabicNone, ArabicNone,
+ ArabicNone, ArabicNone, ArabicNone, ArabicNone,
+
+ Transparent, Transparent, Transparent, Transparent,
+ Transparent, Transparent, ArabicNone, ArabicNone,
+ ArabicNone, ArabicNone, ArabicNone, ArabicNone,
+ ArabicNone, ArabicNone, ArabicNone, ArabicNone,
+
+ ArabicNone, ArabicNone, Alef, Alef,
+ Waw, Alef, Yeh, Alef,
+ Beh, TehMarbuta, Beh, Beh,
+ Hah, Hah, Hah, Dal,
+
+ Dal, Reh, Reh, Seen,
+ Seen, Sad, Sad, Tah,
+ Tah, Ain, Ain, ArabicNone,
+ ArabicNone, ArabicNone, ArabicNone, ArabicNone,
+
+ // 0x640
+ Kashida, Feh, Qaf, Kaf,
+ Lam, Meem, Noon, Heh,
+ Waw, Yeh, Yeh, Transparent,
+ Transparent, Transparent, Transparent, Transparent,
+
+ Transparent, Transparent, Transparent, Transparent,
+ Transparent, Transparent, Transparent, Transparent,
+ Transparent, ArabicNone, ArabicNone, ArabicNone,
+ ArabicNone, ArabicNone, ArabicNone, ArabicNone,
+
+ ArabicNone, ArabicNone, ArabicNone, ArabicNone,
+ ArabicNone, ArabicNone, ArabicNone, ArabicNone,
+ ArabicNone, ArabicNone, ArabicNone, ArabicNone,
+ ArabicNone, ArabicNone, Beh, Qaf,
+
+ Transparent, Alef, Alef, Alef,
+ ArabicNone, Alef, Waw, Waw,
+ Yeh, Beh, Beh, Beh,
+ Beh, Beh, Beh, Beh,
+
+ // 0x680
+ Beh, Hah, Hah, Hah,
+ Hah, Hah, Hah, Hah,
+ Dal, Dal, Dal, Dal,
+ Dal, Dal, Dal, Dal,
+
+ Dal, Reh, Reh, Reh,
+ Reh, Reh, Reh, Reh,
+ Reh, Reh, Seen, Seen,
+ Seen, Sad, Sad, Tah,
+
+ Ain, Feh, Feh, Feh,
+ Feh, Feh, Feh, Qaf,
+ Qaf, Gaf, SwashKaf, Gaf,
+ Kaf, Kaf, Kaf, Gaf,
+
+ Gaf, Gaf, Gaf, Gaf,
+ Gaf, Lam, Lam, Lam,
+ Lam, Noon, Noon, Noon,
+ Noon, Noon, KnottedHeh, Hah,
+
+ // 0x6c0
+ TehMarbuta, HehGoal, HamzaOnHehGoal, HamzaOnHehGoal,
+ Waw, Waw, Waw, Waw,
+ Waw, Waw, Waw, Waw,
+ Yeh, YehWithTail, Yeh, Waw,
+
+ Yeh, Yeh, YehBarre, YehBarre,
+ ArabicNone, TehMarbuta, Transparent, Transparent,
+ Transparent, Transparent, Transparent, Transparent,
+ Transparent, ArabicNone, ArabicNone, Transparent,
+
+ Transparent, Transparent, Transparent, Transparent,
+ Transparent, ArabicNone, ArabicNone, Transparent,
+ Transparent, ArabicNone, Transparent, Transparent,
+ Transparent, Transparent, Dal, Reh,
+
+ ArabicNone, ArabicNone, ArabicNone, ArabicNone,
+ ArabicNone, ArabicNone, ArabicNone, ArabicNone,
+ ArabicNone, ArabicNone, Seen, Sad,
+ Ain, ArabicNone, ArabicNone, KnottedHeh,
+
+ // 0x700
+ ArabicNone, ArabicNone, ArabicNone, ArabicNone,
+ ArabicNone, ArabicNone, ArabicNone, ArabicNone,
+ ArabicNone, ArabicNone, ArabicNone, ArabicNone,
+ ArabicNone, ArabicNone, ArabicNone, ArabicNone,
+
+ Alaph, Transparent, Beth, Gamal,
+ Gamal, Dalath, Dalath, He,
+ SyriacWaw, Zain, Heth, Teth,
+ Teth, Yudh, YudhHe, Kaph,
+
+ Lamadh, Mim, Nun, Semakh,
+ FinalSemakh, SyriacE, Pe, ReversedPe,
+ Sadhe, Qaph, Dalath, Shin,
+ Taw, Beth, Gamal, Dalath,
+
+ Transparent, Transparent, Transparent, Transparent,
+ Transparent, Transparent, Transparent, Transparent,
+ Transparent, Transparent, Transparent, Transparent,
+ Transparent, Transparent, Transparent, Transparent,
+
+ Transparent, Transparent, Transparent, Transparent,
+ Transparent, Transparent, Transparent, Transparent,
+ Transparent, Transparent, Transparent, ArabicNone,
+ ArabicNone, Zain, Kaph, Fe,
+};
+
+static inline ArabicGroup arabicGroup(unsigned short uc)
+{
+ if (uc >= 0x0600 && uc < 0x750)
+ return (ArabicGroup) arabic_group[uc-0x600];
+ else if (uc == 0x200d)
+ return Center;
+ else if (QChar::category(uc) == QChar::Separator_Space)
+ return ArabicSpace;
+ else
+ return ArabicNone;
+}
+
+
+/*
+ Arabic shaping obeys a number of rules according to the joining classes (see Unicode book, section on
+ arabic).
+
+ Each unicode char has a joining class (right, dual (left&right), center (joincausing) or transparent).
+ transparent joining is not encoded in QChar::joining(), but applies to all combining marks and format marks.
+
+ Right join-causing: dual + center
+ Left join-causing: dual + right + center
+
+ Rules are as follows (for a string already in visual order, as we have it here):
+
+ R1 Transparent characters do not affect joining behaviour.
+ R2 A right joining character, that has a right join-causing char on the right will get form XRight
+ (R3 A left joining character, that has a left join-causing char on the left will get form XLeft)
+ Note: the above rule is meaningless, as there are no pure left joining characters defined in Unicode
+ R4 A dual joining character, that has a left join-causing char on the left and a right join-causing char on
+ the right will get form XMedial
+ R5 A dual joining character, that has a right join causing char on the right, and no left join causing char on the left
+ will get form XRight
+ R6 A dual joining character, that has a left join causing char on the left, and no right join causing char on the right
+ will get form XLeft
+ R7 Otherwise the character will get form XIsolated
+
+ Additionally we have to do the minimal ligature support for lam-alef ligatures:
+
+ L1 Transparent characters do not affect ligature behaviour.
+ L2 Any sequence of Alef(XRight) + Lam(XMedial) will form the ligature Alef.Lam(XLeft)
+ L3 Any sequence of Alef(XRight) + Lam(XLeft) will form the ligature Alef.Lam(XIsolated)
+
+ The state table below handles rules R1-R7.
+*/
+
+enum Joining {
+ JNone,
+ JCausing,
+ JDual,
+ JRight,
+ JTransparent
+};
+
+static const Joining joining_for_group[ArabicGroupsEnd] = {
+ // NonJoining
+ JNone, // ArabicNone
+ JNone, // ArabicSpace
+ // Transparent
+ JTransparent, // Transparent
+ // Causing
+ JCausing, // Center
+ JCausing, // Kashida
+ // Dual
+ JDual, // Beh
+ JDual, // Noon
+ JDual, // Yeh
+ JDual, // Hah
+ JDual, // Seen
+ JDual, // Tah
+ JDual, // Ain
+ // Right
+ JRight, // Alef
+ JRight, // Waw
+ JRight, // Dal
+ JRight, // Reh
+ JRight // HamzaOnHehGoal
+};
+
+
+struct JoiningPair {
+ QArabicShape form1;
+ QArabicShape form2;
+};
+
+static const JoiningPair joining_table[5][4] =
+// None, Causing, Dual, Right
+{
+ { { XIsolated, XIsolated }, { XIsolated, XCausing }, { XIsolated, XInitial }, { XIsolated, XIsolated } }, // XIsolated
+ { { XFinal, XIsolated }, { XFinal, XCausing }, { XFinal, XInitial }, { XFinal, XIsolated } }, // XFinal
+ { { XIsolated, XIsolated }, { XInitial, XCausing }, { XInitial, XMedial }, { XInitial, XFinal } }, // XInitial
+ { { XFinal, XIsolated }, { XMedial, XCausing }, { XMedial, XMedial }, { XMedial, XFinal } }, // XMedial
+ { { XIsolated, XIsolated }, { XIsolated, XCausing }, { XIsolated, XMedial }, { XIsolated, XFinal } }, // XCausing
+};
+
+
+/*
+According to http://www.microsoft.com/middleeast/Arabicdev/IE6/KBase.asp
+
+1. Find the priority of the connecting opportunities in each word
+2. Add expansion at the highest priority connection opportunity
+3. If more than one connection opportunity have the same highest value,
+ use the opportunity closest to the end of the word.
+
+Following is a chart that provides the priority for connection
+opportunities and where expansion occurs. The character group names
+are those in table 6.6 of the UNICODE 2.0 book.
+
+
+PrioritY Glyph Condition Kashida Location
+
+Arabic_Kashida User inserted Kashida The user entered a Kashida in a position. After the user
+ (Shift+j or Shift+[E with hat]) Thus, it is the highest priority to insert an inserted kashida
+ automatic kashida.
+
+Arabic_Seen Seen, Sad Connecting to the next character. After the character.
+ (Initial or medial form).
+
+Arabic_HaaDal Teh Marbutah, Haa, Dal Connecting to previous character. Before the final form
+ of these characters.
+
+Arabic_Alef Alef, Tah, Lam, Connecting to previous character. Before the final form
+ Kaf and Gaf of these characters.
+
+Arabic_BaRa Reh, Yeh Connected to medial Beh Before preceding medial Baa
+
+Arabic_Waw Waw, Ain, Qaf, Feh Connecting to previous character. Before the final form of
+ these characters.
+
+Arabic_Normal Other connecting Connecting to previous character. Before the final form
+ characters of these characters.
+
+
+
+This seems to imply that we have at most one kashida point per arabic word.
+
+*/
+
+void qt_getArabicProperties(const unsigned short *chars, int len, QArabicProperties *properties)
+{
+// qDebug("arabicSyriacOpenTypeShape: properties:");
+ int lastPos = 0;
+ int lastGroup = ArabicNone;
+
+ ArabicGroup group = arabicGroup(chars[0]);
+ Joining j = joining_for_group[group];
+ QArabicShape shape = joining_table[XIsolated][j].form2;
+ properties[0].justification = HB_NoJustification;
+
+ for (int i = 1; i < len; ++i) {
+ // #### fix handling for spaces and punktuation
+ properties[i].justification = HB_NoJustification;
+
+ group = arabicGroup(chars[i]);
+ j = joining_for_group[group];
+
+ if (j == JTransparent) {
+ properties[i].shape = XIsolated;
+ continue;
+ }
+
+ properties[lastPos].shape = joining_table[shape][j].form1;
+ shape = joining_table[shape][j].form2;
+
+ switch(lastGroup) {
+ case Seen:
+ if (properties[lastPos].shape == XInitial || properties[lastPos].shape == XMedial)
+ properties[i-1].justification = HB_Arabic_Seen;
+ break;
+ case Hah:
+ if (properties[lastPos].shape == XFinal)
+ properties[lastPos-1].justification = HB_Arabic_HaaDal;
+ break;
+ case Alef:
+ if (properties[lastPos].shape == XFinal)
+ properties[lastPos-1].justification = HB_Arabic_Alef;
+ break;
+ case Ain:
+ if (properties[lastPos].shape == XFinal)
+ properties[lastPos-1].justification = HB_Arabic_Waw;
+ break;
+ case Noon:
+ if (properties[lastPos].shape == XFinal)
+ properties[lastPos-1].justification = HB_Arabic_Normal;
+ break;
+ case ArabicNone:
+ break;
+
+ default:
+ Q_ASSERT(false);
+ }
+
+ lastGroup = ArabicNone;
+
+ switch(group) {
+ case ArabicNone:
+ case Transparent:
+ // ### Center should probably be treated as transparent when it comes to justification.
+ case Center:
+ break;
+ case ArabicSpace:
+ properties[i].justification = HB_Arabic_Space;
+ break;
+ case Kashida:
+ properties[i].justification = HB_Arabic_Kashida;
+ break;
+ case Seen:
+ lastGroup = Seen;
+ break;
+
+ case Hah:
+ case Dal:
+ lastGroup = Hah;
+ break;
+
+ case Alef:
+ case Tah:
+ lastGroup = Alef;
+ break;
+
+ case Yeh:
+ case Reh:
+ if (properties[lastPos].shape == XMedial && arabicGroup(chars[lastPos]) == Beh)
+ properties[lastPos-1].justification = HB_Arabic_BaRa;
+ break;
+
+ case Ain:
+ case Waw:
+ lastGroup = Ain;
+ break;
+
+ case Noon:
+ case Beh:
+ case HamzaOnHehGoal:
+ lastGroup = Noon;
+ break;
+ case ArabicGroupsEnd:
+ Q_ASSERT(false);
+ }
+
+ lastPos = i;
+ }
+ properties[lastPos].shape = joining_table[shape][JNone].form1;
+
+
+// for (int i = 0; i < len; ++i)
+// qDebug("arabic properties(%d): uc=%x shape=%d, justification=%d", i, chars[i], properties[i].shape, properties[i].justification);
+}
+
+void QTextEngine::shapeTextMac(int item) const
+{
+ QScriptItem &si = layoutData->items[item];
+
+ si.glyph_data_offset = layoutData->used;
+
+ QFontEngine *font = fontEngine(si, &si.ascent, &si.descent);
+ if (font->type() != QFontEngine::Multi) {
+ shapeTextWithHarfbuzz(item);
+ return;
+ }
+
+#ifndef QT_MAC_USE_COCOA
+ QFontEngineMacMulti *fe = static_cast<QFontEngineMacMulti *>(font);
+#else
+ QCoreTextFontEngineMulti *fe = static_cast<QCoreTextFontEngineMulti *>(font);
+#endif
+ QTextEngine::ShaperFlags flags;
+ if (si.analysis.bidiLevel % 2)
+ flags |= RightToLeft;
+ if (option.useDesignMetrics())
+ flags |= DesignMetrics;
+
+ attributes(); // pre-initialize char attributes
+
+ const int len = length(item);
+ int num_glyphs = length(item);
+ const QChar *str = layoutData->string.unicode() + si.position;
+ ushort upperCased[256];
+ if (si.analysis.flags == QScriptAnalysis::SmallCaps || si.analysis.flags == QScriptAnalysis::Uppercase
+ || si.analysis.flags == QScriptAnalysis::Lowercase) {
+ ushort *uc = upperCased;
+ if (len > 256)
+ uc = new ushort[len];
+ for (int i = 0; i < len; ++i) {
+ if(si.analysis.flags == QScriptAnalysis::Lowercase)
+ uc[i] = str[i].toLower().unicode();
+ else
+ uc[i] = str[i].toUpper().unicode();
+ }
+ str = reinterpret_cast<const QChar *>(uc);
+ }
+
+ while (true) {
+ ensureSpace(num_glyphs);
+ num_glyphs = layoutData->glyphLayout.numGlyphs - layoutData->used;
+
+ QGlyphLayout g = availableGlyphs(&si);
+ g.numGlyphs = num_glyphs;
+ unsigned short *log_clusters = logClusters(&si);
+
+ if (fe->stringToCMap(str,
+ len,
+ &g,
+ &num_glyphs,
+ flags,
+ log_clusters,
+ attributes())) {
+
+ heuristicSetGlyphAttributes(str, len, &g, log_clusters, num_glyphs);
+ break;
+ }
+ }
+
+ si.num_glyphs = num_glyphs;
+
+ layoutData->used += si.num_glyphs;
+
+ QGlyphLayout g = shapedGlyphs(&si);
+
+ if (si.analysis.script == QUnicodeTables::Arabic) {
+ QVarLengthArray<QArabicProperties> props(len + 2);
+ QArabicProperties *properties = props.data();
+ int f = si.position;
+ int l = len;
+ if (f > 0) {
+ --f;
+ ++l;
+ ++properties;
+ }
+ if (f + l < layoutData->string.length()) {
+ ++l;
+ }
+ qt_getArabicProperties((const unsigned short *)(layoutData->string.unicode()+f), l, props.data());
+
+ unsigned short *log_clusters = logClusters(&si);
+
+ for (int i = 0; i < len; ++i) {
+ int gpos = log_clusters[i];
+ g.attributes[gpos].justification = properties[i].justification;
+ }
+ }
+
+ const ushort *uc = reinterpret_cast<const ushort *>(str);
+
+ if ((si.analysis.flags == QScriptAnalysis::SmallCaps || si.analysis.flags == QScriptAnalysis::Uppercase
+ || si.analysis.flags == QScriptAnalysis::Lowercase)
+ && uc != upperCased)
+ delete [] uc;
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/text/qtextengine_p.h b/src/gui/text/qtextengine_p.h
new file mode 100644
index 0000000000..cf241faf14
--- /dev/null
+++ b/src/gui/text/qtextengine_p.h
@@ -0,0 +1,608 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QTEXTENGINE_P_H
+#define QTEXTENGINE_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/qglobal.h"
+#include "QtCore/qstring.h"
+#include "QtCore/qvarlengtharray.h"
+#include "QtCore/qnamespace.h"
+#include "QtGui/qtextlayout.h"
+#include "private/qtextformat_p.h"
+#include "private/qfont_p.h"
+#include "QtCore/qvector.h"
+#include "QtGui/qpaintengine.h"
+#include "QtGui/qtextobject.h"
+#include "QtGui/qtextoption.h"
+#include "QtCore/qset.h"
+#include "QtCore/qdebug.h"
+#ifndef QT_BUILD_COMPAT_LIB
+#include "private/qtextdocument_p.h"
+#endif
+#include "private/qharfbuzz_p.h"
+#include "private/qfixed_p.h"
+
+#include <stdlib.h>
+
+QT_BEGIN_NAMESPACE
+
+class QFontPrivate;
+class QFontEngine;
+
+class QString;
+class QPainter;
+
+class QAbstractTextDocumentLayout;
+
+
+// this uses the same coordinate system as Qt, but a different one to freetype.
+// * y is usually negative, and is equal to the ascent.
+// * negative yoff means the following stuff is drawn higher up.
+// the characters bounding rect is given by QRect(x,y,width,height), its advance by
+// xoo and yoff
+struct glyph_metrics_t
+{
+ inline glyph_metrics_t()
+ : x(100000), y(100000) {}
+ inline glyph_metrics_t(QFixed _x, QFixed _y, QFixed _width, QFixed _height, QFixed _xoff, QFixed _yoff)
+ : x(_x),
+ y(_y),
+ width(_width),
+ height(_height),
+ xoff(_xoff),
+ yoff(_yoff)
+ {}
+ QFixed x;
+ QFixed y;
+ QFixed width;
+ QFixed height;
+ QFixed xoff;
+ QFixed yoff;
+
+ glyph_metrics_t transformed(const QTransform &xform) const;
+};
+Q_DECLARE_TYPEINFO(glyph_metrics_t, Q_PRIMITIVE_TYPE);
+
+struct Q_AUTOTEST_EXPORT QScriptAnalysis
+{
+ enum Flags {
+ None = 0,
+ Lowercase = 1,
+ Uppercase = 2,
+ SmallCaps = 3,
+ LineOrParagraphSeparator = 4,
+ Space = 5,
+ SpaceTabOrObject = Space,
+ Tab = 6,
+ TabOrObject = Tab,
+ Object = 7
+ };
+ unsigned short script : 8;
+ unsigned short bidiLevel : 6; // Unicode Bidi algorithm embedding level (0-61)
+ unsigned short flags : 3;
+ inline bool operator == (const QScriptAnalysis &other) const {
+ return script == other.script && bidiLevel == other.bidiLevel && flags == other.flags;
+ }
+};
+Q_DECLARE_TYPEINFO(QScriptAnalysis, Q_PRIMITIVE_TYPE);
+
+struct QGlyphJustification
+{
+ inline QGlyphJustification()
+ : type(0), nKashidas(0), space_18d6(0)
+ {}
+
+ enum JustificationType {
+ JustifyNone,
+ JustifySpace,
+ JustifyKashida
+ };
+
+ uint type :2;
+ uint nKashidas : 6; // more do not make sense...
+ uint space_18d6 : 24;
+};
+Q_DECLARE_TYPEINFO(QGlyphJustification, Q_PRIMITIVE_TYPE);
+
+struct QGlyphLayoutInstance
+{
+ QFixedPoint offset;
+ QFixedPoint advance;
+ HB_Glyph glyph;
+ QGlyphJustification justification;
+ HB_GlyphAttributes attributes;
+};
+
+struct QGlyphLayout
+{
+ // init to 0 not needed, done when shaping
+ QFixedPoint *offsets; // 8 bytes per element
+ HB_Glyph *glyphs; // 4 bytes per element
+ QFixed *advances_x; // 4 bytes per element
+ QFixed *advances_y; // 4 bytes per element
+ QGlyphJustification *justifications; // 4 bytes per element
+ HB_GlyphAttributes *attributes; // 2 bytes per element
+
+ int numGlyphs;
+
+ inline QGlyphLayout() : numGlyphs(0) {}
+
+ inline explicit QGlyphLayout(char *address, int totalGlyphs)
+ {
+ offsets = reinterpret_cast<QFixedPoint *>(address);
+ int offset = totalGlyphs * sizeof(HB_FixedPoint);
+ glyphs = reinterpret_cast<HB_Glyph *>(address + offset);
+ offset += totalGlyphs * sizeof(HB_Glyph);
+ advances_x = reinterpret_cast<QFixed *>(address + offset);
+ offset += totalGlyphs * sizeof(QFixed);
+ advances_y = reinterpret_cast<QFixed *>(address + offset);
+ offset += totalGlyphs * sizeof(QFixed);
+ justifications = reinterpret_cast<QGlyphJustification *>(address + offset);
+ offset += totalGlyphs * sizeof(QGlyphJustification);
+ attributes = reinterpret_cast<HB_GlyphAttributes *>(address + offset);
+ numGlyphs = totalGlyphs;
+ }
+
+ inline QGlyphLayout mid(int position, int n = -1) const {
+ QGlyphLayout copy = *this;
+ copy.glyphs += position;
+ copy.advances_x += position;
+ copy.advances_y += position;
+ copy.offsets += position;
+ copy.justifications += position;
+ copy.attributes += position;
+ if (n == -1)
+ copy.numGlyphs -= position;
+ else
+ copy.numGlyphs = n;
+ return copy;
+ }
+
+ static inline int spaceNeededForGlyphLayout(int totalGlyphs) {
+ return totalGlyphs * (sizeof(HB_Glyph) + sizeof(HB_GlyphAttributes)
+ + sizeof(QFixed) + sizeof(QFixed) + sizeof(QFixedPoint)
+ + sizeof(QGlyphJustification));
+ }
+
+ inline QFixed effectiveAdvance(int item) const
+ { return (advances_x[item] + QFixed::fromFixed(justifications[item].space_18d6)) * !attributes[item].dontPrint; }
+
+ inline QGlyphLayoutInstance instance(int position) const {
+ QGlyphLayoutInstance g;
+ g.offset.x = offsets[position].x;
+ g.offset.y = offsets[position].y;
+ g.glyph = glyphs[position];
+ g.advance.x = advances_x[position];
+ g.advance.y = advances_y[position];
+ g.justification = justifications[position];
+ g.attributes = attributes[position];
+ return g;
+ }
+
+ inline void setInstance(int position, const QGlyphLayoutInstance &g) {
+ offsets[position].x = g.offset.x;
+ offsets[position].y = g.offset.y;
+ glyphs[position] = g.glyph;
+ advances_x[position] = g.advance.x;
+ advances_y[position] = g.advance.y;
+ justifications[position] = g.justification;
+ attributes[position] = g.attributes;
+ }
+
+ inline void clear(int first = 0, int last = -1) {
+ if (last == -1)
+ last = numGlyphs;
+ if (first == 0 && last == numGlyphs
+ && reinterpret_cast<char *>(offsets + numGlyphs) == reinterpret_cast<char *>(glyphs)) {
+ memset(offsets, 0, spaceNeededForGlyphLayout(numGlyphs));
+ } else {
+ const int num = last - first;
+ memset(offsets + first, 0, num * sizeof(QFixedPoint));
+ memset(glyphs + first, 0, num * sizeof(HB_Glyph));
+ memset(advances_x + first, 0, num * sizeof(QFixed));
+ memset(advances_y + first, 0, num * sizeof(QFixed));
+ memset(justifications + first, 0, num * sizeof(QGlyphJustification));
+ memset(attributes + first, 0, num * sizeof(HB_GlyphAttributes));
+ }
+ }
+
+ inline char *data() {
+ return reinterpret_cast<char *>(offsets);
+ }
+
+ void grow(char *address, int totalGlyphs);
+};
+
+class QVarLengthGlyphLayoutArray : private QVarLengthArray<void *>, public QGlyphLayout
+{
+private:
+ typedef QVarLengthArray<void *> Array;
+public:
+ QVarLengthGlyphLayoutArray(int totalGlyphs)
+ : Array(spaceNeededForGlyphLayout(totalGlyphs) / sizeof(void *) + 1)
+ , QGlyphLayout(reinterpret_cast<char *>(Array::data()), totalGlyphs)
+ {
+ memset(Array::data(), 0, Array::size() * sizeof(void *));
+ }
+
+ void resize(int totalGlyphs)
+ {
+ Array::resize(spaceNeededForGlyphLayout(totalGlyphs) / sizeof(void *) + 1);
+
+ *((QGlyphLayout *)this) = QGlyphLayout(reinterpret_cast<char *>(Array::data()), totalGlyphs);
+ memset(Array::data(), 0, Array::size() * sizeof(void *));
+ }
+};
+
+template <int N> struct QGlyphLayoutArray : public QGlyphLayout
+{
+public:
+ QGlyphLayoutArray()
+ : QGlyphLayout(reinterpret_cast<char *>(buffer), N)
+ {
+ memset(buffer, 0, sizeof(buffer));
+ }
+
+private:
+ void *buffer[(N * (sizeof(HB_Glyph) + sizeof(HB_GlyphAttributes)
+ + sizeof(QFixed) + sizeof(QFixed) + sizeof(QFixedPoint)
+ + sizeof(QGlyphJustification)))
+ / sizeof(void *) + 1];
+};
+
+struct QScriptItem;
+/// Internal QTextItem
+class QTextItemInt : public QTextItem
+{
+public:
+ inline QTextItemInt()
+ : justified(false), underlineStyle(QTextCharFormat::NoUnderline), num_chars(0), chars(0),
+ logClusters(0), f(0), fontEngine(0)
+ {}
+ QTextItemInt(const QScriptItem &si, QFont *font, const QTextCharFormat &format = QTextCharFormat());
+
+ /// copy the structure items, adjusting the glyphs arrays to the right subarrays.
+ /// the width of the returned QTextItemInt is not adjusted, for speed reasons
+ QTextItemInt midItem(QFontEngine *fontEngine, int firstGlyphIndex, int numGlyphs) const;
+
+ QFixed descent;
+ QFixed ascent;
+ QFixed width;
+
+ RenderFlags flags;
+ bool justified;
+ QTextCharFormat::UnderlineStyle underlineStyle;
+ const QTextCharFormat charFormat;
+ int num_chars;
+ const QChar *chars;
+ const unsigned short *logClusters;
+ const QFont *f;
+
+ QGlyphLayout glyphs;
+ QFontEngine *fontEngine;
+};
+
+inline bool qIsControlChar(ushort uc)
+{
+ return uc >= 0x200b && uc <= 0x206f
+ && (uc <= 0x200f /* ZW Space, ZWNJ, ZWJ, LRM and RLM */
+ || (uc >= 0x2028 && uc <= 0x202f /* LS, PS, LRE, RLE, PDF, LRO, RLO, NNBSP */)
+ || uc >= 0x206a /* ISS, ASS, IAFS, AFS, NADS, NODS */);
+}
+
+struct Q_AUTOTEST_EXPORT QScriptItem
+{
+ inline QScriptItem()
+ : position(0),
+ num_glyphs(0), descent(-1), ascent(-1), width(-1),
+ glyph_data_offset(0) {}
+ inline QScriptItem(int p, const QScriptAnalysis &a)
+ : position(p), analysis(a),
+ num_glyphs(0), descent(-1), ascent(-1), width(-1),
+ glyph_data_offset(0) {}
+
+ int position;
+ QScriptAnalysis analysis;
+ unsigned short num_glyphs;
+ QFixed descent;
+ QFixed ascent;
+ QFixed width;
+ int glyph_data_offset;
+ QFixed height() const { return ascent + descent + 1; }
+};
+
+
+Q_DECLARE_TYPEINFO(QScriptItem, Q_MOVABLE_TYPE);
+
+typedef QVector<QScriptItem> QScriptItemArray;
+
+struct Q_AUTOTEST_EXPORT QScriptLine
+{
+ // created and filled in QTextLine::layout_helper
+ QScriptLine()
+ : from(0), length(0),
+ justified(0), gridfitted(0),
+ hasTrailingSpaces(0) {}
+ QFixed descent;
+ QFixed ascent;
+ QFixed x;
+ QFixed y;
+ QFixed width;
+ QFixed textWidth;
+ int from;
+ signed int length : 29;
+ mutable uint justified : 1;
+ mutable uint gridfitted : 1;
+ uint hasTrailingSpaces : 1;
+ QFixed height() const { return ascent + descent + 1; }
+ void setDefaultHeight(QTextEngine *eng);
+ void operator+=(const QScriptLine &other);
+};
+Q_DECLARE_TYPEINFO(QScriptLine, Q_PRIMITIVE_TYPE);
+
+
+inline void QScriptLine::operator+=(const QScriptLine &other)
+{
+ descent = qMax(descent, other.descent);
+ ascent = qMax(ascent, other.ascent);
+ textWidth += other.textWidth;
+ length += other.length;
+}
+
+typedef QVector<QScriptLine> QScriptLineArray;
+
+class QFontPrivate;
+class QTextFormatCollection;
+
+class Q_GUI_EXPORT QTextEngine {
+public:
+ struct LayoutData {
+ LayoutData(const QString &str, void **stack_memory, int mem_size);
+ LayoutData();
+ ~LayoutData();
+ mutable QScriptItemArray items;
+ int allocated;
+ int available_glyphs;
+ void **memory;
+ unsigned short *logClustersPtr;
+ QGlyphLayout glyphLayout;
+ mutable int used;
+ uint hasBidi : 1;
+ uint inLayout : 1;
+ uint memory_on_stack : 1;
+ bool haveCharAttributes;
+ QString string;
+ void reallocate(int totalGlyphs);
+ };
+
+ QTextEngine(LayoutData *data);
+ QTextEngine();
+ QTextEngine(const QString &str, const QFont &f);
+ ~QTextEngine();
+
+ enum Mode {
+ WidthOnly = 0x07
+ };
+
+ // keep in sync with QAbstractFontEngine::TextShapingFlag!!
+ enum ShaperFlag {
+ RightToLeft = 0x0001,
+ DesignMetrics = 0x0002,
+ GlyphIndicesOnly = 0x0004
+ };
+ Q_DECLARE_FLAGS(ShaperFlags, ShaperFlag)
+
+ void invalidate();
+ void clearLineData();
+
+ void validate() const;
+ void itemize() const;
+
+ static void bidiReorder(int numRuns, const quint8 *levels, int *visualOrder);
+
+ const HB_CharAttributes *attributes() const;
+
+ void shape(int item) const;
+
+ void justify(const QScriptLine &si);
+
+ QFixed width(int charFrom, int numChars) const;
+ glyph_metrics_t boundingBox(int from, int len) const;
+ glyph_metrics_t tightBoundingBox(int from, int len) const;
+
+ int length(int item) const {
+ const QScriptItem &si = layoutData->items[item];
+ int from = si.position;
+ item++;
+ return (item < layoutData->items.size() ? layoutData->items[item].position : layoutData->string.length()) - from;
+ }
+ int length(const QScriptItem *si) const {
+ int end;
+ if (si + 1 < layoutData->items.constData()+ layoutData->items.size())
+ end = (si+1)->position;
+ else
+ end = layoutData->string.length();
+ return end - si->position;
+ }
+
+ QFontEngine *fontEngine(const QScriptItem &si, QFixed *ascent = 0, QFixed *descent = 0) const;
+ QFont font(const QScriptItem &si) const;
+ inline QFont font() const { return fnt; }
+
+ /**
+ * Returns a pointer to an array of log clusters, offset at the script item.
+ * Each item in the array is a unsigned short. For each character in the original string there is an entry in the table
+ * so there is a one to one correlation in indexes between the original text and the index in the logcluster.
+ * The value of each item is the position in the glyphs array. Multiple similar pointers in the logclusters array imply
+ * that one glyph is used for more than one character.
+ * \sa glyphs()
+ */
+ inline unsigned short *logClusters(const QScriptItem *si) const
+ { return layoutData->logClustersPtr+si->position; }
+ /**
+ * Returns an array of QGlyphLayout items, offset at the script item.
+ * Each item in the array matches one glyph in the text, storing the advance, position etc.
+ * The returned item's length equals to the number of available glyphs. This may be more
+ * than what was actually shaped.
+ * \sa logClusters()
+ */
+ inline QGlyphLayout availableGlyphs(const QScriptItem *si) const {
+ return layoutData->glyphLayout.mid(si->glyph_data_offset);
+ }
+ /**
+ * Returns an array of QGlyphLayout items, offset at the script item.
+ * Each item in the array matches one glyph in the text, storing the advance, position etc.
+ * The returned item's length equals to the number of shaped glyphs.
+ * \sa logClusters()
+ */
+ inline QGlyphLayout shapedGlyphs(const QScriptItem *si) const {
+ return layoutData->glyphLayout.mid(si->glyph_data_offset, si->num_glyphs);
+ }
+
+ inline void ensureSpace(int nGlyphs) const {
+ if (layoutData->glyphLayout.numGlyphs - layoutData->used < nGlyphs)
+ layoutData->reallocate((((layoutData->used + nGlyphs)*3/2 + 15) >> 4) << 4);
+ }
+
+ void freeMemory();
+
+ int findItem(int strPos) const;
+ inline QTextFormatCollection *formats() const {
+#ifdef QT_BUILD_COMPAT_LIB
+ return 0; // Compat should never reference this symbol
+#else
+ return block.docHandle()->formatCollection();
+#endif
+ }
+ QTextCharFormat format(const QScriptItem *si) const;
+ inline QAbstractTextDocumentLayout *docLayout() const {
+#ifdef QT_BUILD_COMPAT_LIB
+ return 0; // Compat should never reference this symbol
+#else
+ return block.docHandle()->document()->documentLayout();
+#endif
+ }
+ int formatIndex(const QScriptItem *si) const;
+
+ /// returns the width of tab at index (in the tabs array) with the tab-start at position x
+ QFixed calculateTabWidth(int index, QFixed x) const;
+
+ mutable QScriptLineArray lines;
+
+ QString text;
+ QFont fnt;
+ QTextBlock block;
+
+ QTextOption option;
+
+ QFixed minWidth;
+ QFixed maxWidth;
+ QPointF position;
+ uint ignoreBidi : 1;
+ uint cacheGlyphs : 1;
+ uint stackEngine : 1;
+ uint forceJustification : 1;
+
+ int *underlinePositions;
+
+ mutable LayoutData *layoutData;
+
+ inline bool hasFormats() const { return (block.docHandle() || specialData); }
+
+ struct SpecialData {
+ int preeditPosition;
+ QString preeditText;
+ QList<QTextLayout::FormatRange> addFormats;
+ QVector<int> addFormatIndices;
+ QVector<int> resolvedFormatIndices;
+ };
+ SpecialData *specialData;
+
+ bool atWordSeparator(int position) const;
+ bool atSpace(int position) const;
+ void indexAdditionalFormats();
+
+ QString elidedText(Qt::TextElideMode mode, const QFixed &width, int flags = 0) const;
+
+ void shapeLine(const QScriptLine &line);
+
+private:
+ void setBoundary(int strPos) const;
+ void addRequiredBoundaries() const;
+ void shapeText(int item) const;
+ void shapeTextWithHarfbuzz(int item) const;
+#if defined(Q_OS_WINCE)
+ void shapeTextWithCE(int item) const;
+#endif
+#if defined(Q_WS_MAC)
+ void shapeTextMac(int item) const;
+#endif
+ void splitItem(int item, int pos) const;
+
+ void resolveAdditionalFormats() const;
+};
+
+class QStackTextEngine : public QTextEngine {
+public:
+ enum { MemSize = 256*40/sizeof(void *) };
+ QStackTextEngine(const QString &string, const QFont &f);
+ LayoutData _layoutData;
+ void *_memory[MemSize];
+};
+
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(QTextEngine::ShaperFlags)
+
+QT_END_NAMESPACE
+
+#endif // QTEXTENGINE_P_H
diff --git a/src/gui/text/qtextformat.cpp b/src/gui/text/qtextformat.cpp
new file mode 100644
index 0000000000..21bfc4daae
--- /dev/null
+++ b/src/gui/text/qtextformat.cpp
@@ -0,0 +1,3063 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qtextformat.h"
+#include "qtextformat_p.h"
+
+#include <qvariant.h>
+#include <qdatastream.h>
+#include <qdebug.h>
+#include <qmap.h>
+#include <qhash.h>
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QTextLength
+ \reentrant
+
+ \brief The QTextLength class encapsulates the different types of length
+ used in a QTextDocument.
+
+ \ingroup text
+
+ When we specify a value for the length of an element in a text document,
+ we often need to provide some other information so that the length is
+ used in the way we expect. For example, when we specify a table width,
+ the value can represent a fixed number of pixels, or it can be a percentage
+ value. This information changes both the meaning of the value and the way
+ it is used.
+
+ Generally, this class is used to specify table widths. These can be
+ specified either as a fixed amount of pixels, as a percentage of the
+ containing frame's width, or by a variable width that allows it to take
+ up just the space it requires.
+
+ \sa QTextTable
+*/
+
+/*!
+ \fn explicit QTextLength::QTextLength()
+
+ Constructs a new length object which represents a variable size.
+*/
+
+/*!
+ \fn QTextLength::QTextLength(Type type, qreal value)
+
+ Constructs a new length object of the given \a type and \a value.
+*/
+
+/*!
+ \fn Type QTextLength::type() const
+
+ Returns the type of length.
+
+ \sa QTextLength::Type
+*/
+
+/*!
+ \fn qreal QTextLength::value(qreal maximumLength) const
+
+ Returns the effective length, constrained by the type of the length object
+ and the specified \a maximumLength.
+
+ \sa type()
+*/
+
+/*!
+ \fn qreal QTextLength::rawValue() const
+
+ Returns the constraint value that is specific for the type of the length.
+ If the length is QTextLength::PercentageLength then the raw value is in
+ percent, in the range of 0 to 100. If the length is QTextLength::FixedLength
+ then that fixed amount is returned. For variable lengths, zero is returned.
+*/
+
+/*!
+ \fn bool QTextLength::operator==(const QTextLength &other) const
+
+ Returns true if this text length is the same as the \a other text
+ length.
+*/
+
+/*!
+ \fn bool QTextLength::operator!=(const QTextLength &other) const
+
+ Returns true if this text length is different from the \a other text
+ length.
+*/
+
+/*!
+ \enum QTextLength::Type
+
+ \value VariableLength
+ \value FixedLength
+ \value PercentageLength
+*/
+
+/*!
+ Returns the text length as a QVariant
+*/
+QTextLength::operator QVariant() const
+{
+ return QVariant(QVariant::TextLength, this);
+}
+
+QDataStream &operator<<(QDataStream &stream, const QTextLength &length)
+{
+ return stream << qint32(length.lengthType) << double(length.fixedValueOrPercentage);
+}
+
+QDataStream &operator>>(QDataStream &stream, QTextLength &length)
+{
+ qint32 type;
+ double fixedValueOrPercentage;
+ stream >> type >> fixedValueOrPercentage;
+ length.fixedValueOrPercentage = fixedValueOrPercentage;
+ length.lengthType = QTextLength::Type(type);
+ return stream;
+}
+
+class QTextFormatPrivate : public QSharedData
+{
+public:
+ QTextFormatPrivate() : hashDirty(true), fontDirty(true), hashValue(0) {}
+
+ struct Property
+ {
+ inline Property(qint32 k, const QVariant &v) : key(k), value(v) {}
+ inline Property() {}
+
+ qint32 key;
+ QVariant value;
+
+ inline bool operator==(const Property &other) const
+ { return key == other.key && value == other.value; }
+ inline bool operator!=(const Property &other) const
+ { return key != other.key || value != other.value; }
+ };
+
+ inline uint hash() const
+ {
+ if (!hashDirty)
+ return hashValue;
+ return recalcHash();
+ }
+
+ inline bool operator==(const QTextFormatPrivate &rhs) const {
+ if (hash() != rhs.hash())
+ return false;
+
+ return props == rhs.props;
+ }
+
+ inline void insertProperty(qint32 key, const QVariant &value)
+ {
+ hashDirty = true;
+ if (key >= QTextFormat::FirstFontProperty && key <= QTextFormat::LastFontProperty)
+ fontDirty = true;
+ for (int i = 0; i < props.count(); ++i)
+ if (props.at(i).key == key) {
+ props[i].value = value;
+ return;
+ }
+ props.append(Property(key, value));
+ }
+
+ inline void clearProperty(qint32 key)
+ {
+ for (int i = 0; i < props.count(); ++i)
+ if (props.at(i).key == key) {
+ hashDirty = true;
+ if (key >= QTextFormat::FirstFontProperty && key <= QTextFormat::LastFontProperty)
+ fontDirty = true;
+ props.remove(i);
+ return;
+ }
+ }
+
+ inline int propertyIndex(qint32 key) const
+ {
+ for (int i = 0; i < props.count(); ++i)
+ if (props.at(i).key == key)
+ return i;
+ return -1;
+ }
+
+ inline QVariant property(qint32 key) const
+ {
+ const int idx = propertyIndex(key);
+ if (idx < 0)
+ return QVariant();
+ return props.at(idx).value;
+ }
+
+ inline bool hasProperty(qint32 key) const
+ { return propertyIndex(key) != -1; }
+
+ void resolveFont(const QFont &defaultFont);
+
+ inline const QFont &font() const {
+ if (fontDirty)
+ recalcFont();
+ return fnt;
+ }
+
+ QVector<Property> props;
+private:
+
+ uint recalcHash() const;
+ void recalcFont() const;
+
+ mutable bool hashDirty;
+ mutable bool fontDirty;
+ mutable uint hashValue;
+ mutable QFont fnt;
+
+ friend QDataStream &operator<<(QDataStream &, const QTextFormat &);
+ friend QDataStream &operator>>(QDataStream &, QTextFormat &);
+};
+
+static uint variantHash(const QVariant &variant)
+{
+ switch (variant.type()) {
+ case QVariant::Invalid: return 0;
+ case QVariant::Bool: return variant.toBool();
+ case QVariant::Int: return variant.toInt();
+ case QVariant::Double: return static_cast<int>(variant.toDouble());
+ case QVariant::String: return qHash(variant.toString());
+ case QVariant::Color: return qHash(qvariant_cast<QColor>(variant).rgb());
+ default: break;
+ }
+ return qHash(variant.typeName());
+}
+
+uint QTextFormatPrivate::recalcHash() const
+{
+ hashValue = 0;
+ for (QVector<Property>::ConstIterator it = props.constBegin(); it != props.constEnd(); ++it)
+ hashValue += (it->key << 16) + variantHash(it->value);
+
+ hashDirty = false;
+
+ return hashValue;
+}
+
+void QTextFormatPrivate::resolveFont(const QFont &defaultFont)
+{
+ recalcFont();
+ const uint oldMask = fnt.resolve();
+ fnt = fnt.resolve(defaultFont);
+
+ if (hasProperty(QTextFormat::FontSizeAdjustment)) {
+ const qreal scaleFactors[7] = {qreal(0.7), qreal(0.8), qreal(1.0), qreal(1.2), qreal(1.5), qreal(2), qreal(2.4)};
+
+ const int htmlFontSize = qBound(0, property(QTextFormat::FontSizeAdjustment).toInt() + 3 - 1, 6);
+
+
+ if (defaultFont.pointSize() <= 0) {
+ qreal pixelSize = scaleFactors[htmlFontSize] * defaultFont.pixelSize();
+ fnt.setPixelSize(qRound(pixelSize));
+ } else {
+ qreal pointSize = scaleFactors[htmlFontSize] * defaultFont.pointSizeF();
+ fnt.setPointSizeF(pointSize);
+ }
+ }
+
+ fnt.resolve(oldMask);
+}
+
+void QTextFormatPrivate::recalcFont() const
+{
+ // update cached font as well
+ QFont f;
+
+ for (int i = 0; i < props.count(); ++i) {
+ switch (props.at(i).key) {
+ case QTextFormat::FontFamily:
+ f.setFamily(props.at(i).value.toString());
+ break;
+ case QTextFormat::FontPointSize:
+ f.setPointSizeF(props.at(i).value.toDouble());
+ break;
+ case QTextFormat::FontPixelSize:
+ f.setPixelSize(props.at(i).value.toInt());
+ break;
+ case QTextFormat::FontWeight: {
+ int weight = props.at(i).value.toInt();
+ if (weight == 0) weight = QFont::Normal;
+ f.setWeight(weight);
+ break; }
+ case QTextFormat::FontItalic:
+ f.setItalic(props.at(i).value.toBool());
+ break;
+ case QTextFormat::FontUnderline:
+ if (! hasProperty(QTextFormat::TextUnderlineStyle)) // don't use the old one if the new one is there.
+ f.setUnderline(props.at(i).value.toBool());
+ break;
+ case QTextFormat::TextUnderlineStyle:
+ f.setUnderline(static_cast<QTextCharFormat::UnderlineStyle>(props.at(i).value.toInt()) == QTextCharFormat::SingleUnderline);
+ break;
+ case QTextFormat::FontOverline:
+ f.setOverline(props.at(i).value.toBool());
+ break;
+ case QTextFormat::FontStrikeOut:
+ f.setStrikeOut(props.at(i).value.toBool());
+ break;
+ case QTextFormat::FontLetterSpacing:
+ f.setLetterSpacing(QFont::PercentageSpacing, props.at(i).value.toDouble());
+ break;
+ case QTextFormat::FontWordSpacing:
+ f.setWordSpacing(props.at(i).value.toDouble());
+ break;
+ case QTextFormat::FontCapitalization:
+ f.setCapitalization(static_cast<QFont::Capitalization> (props.at(i).value.toInt()));
+ break;
+ case QTextFormat::FontFixedPitch: {
+ const bool value = props.at(i).value.toBool();
+ if (f.fixedPitch() != value)
+ f.setFixedPitch(value);
+ break; }
+ case QTextFormat::FontStyleHint:
+ f.setStyleHint(static_cast<QFont::StyleHint>(props.at(i).value.toInt()), f.styleStrategy());
+ break;
+ case QTextFormat::FontStyleStrategy:
+ f.setStyleStrategy(static_cast<QFont::StyleStrategy>(props.at(i).value.toInt()));
+ break;
+ case QTextFormat::FontKerning:
+ f.setKerning(props.at(i).value.toBool());
+ break;
+ default:
+ break;
+ }
+ }
+ fnt = f;
+ fontDirty = false;
+}
+
+Q_GUI_EXPORT QDataStream &operator<<(QDataStream &stream, const QTextFormat &fmt)
+{
+ stream << fmt.format_type << fmt.properties();
+ return stream;
+}
+
+Q_GUI_EXPORT QDataStream &operator>>(QDataStream &stream, QTextFormat &fmt)
+{
+ QMap<qint32, QVariant> properties;
+ stream >> fmt.format_type >> properties;
+
+ // QTextFormat's default constructor doesn't allocate the private structure, so
+ // we have to do this, in case fmt is a default constructed value.
+ if(!fmt.d)
+ fmt.d = new QTextFormatPrivate();
+
+ for (QMap<qint32, QVariant>::ConstIterator it = properties.constBegin();
+ it != properties.constEnd(); ++it)
+ fmt.d->insertProperty(it.key(), it.value());
+
+ return stream;
+}
+
+/*!
+ \class QTextFormat
+ \reentrant
+
+ \brief The QTextFormat class provides formatting information for a
+ QTextDocument.
+
+ \ingroup text
+ \ingroup shared
+
+ A QTextFormat is a generic class used for describing the format of
+ parts of a QTextDocument. The derived classes QTextCharFormat,
+ QTextBlockFormat, QTextListFormat, and QTextTableFormat are usually
+ more useful, and describe the formatting that is applied to
+ specific parts of the document.
+
+ A format has a \c FormatType which specifies the kinds of thing it
+ can format; e.g. a block of text, a list, a table, etc. A format
+ also has various properties (some specific to particular format
+ types), as described by the Property enum. Every property has a
+ corresponding Property.
+
+ The format type is given by type(), and the format can be tested
+ with isCharFormat(), isBlockFormat(), isListFormat(),
+ isTableFormat(), isFrameFormat(), and isImageFormat(). If the
+ type is determined, it can be retrieved with toCharFormat(),
+ toBlockFormat(), toListFormat(), toTableFormat(), toFrameFormat(),
+ and toImageFormat().
+
+ A format's properties can be set with the setProperty() functions,
+ and retrieved with boolProperty(), intProperty(), doubleProperty(),
+ and stringProperty() as appropriate. All the property IDs used in
+ the format can be retrieved with allPropertyIds(). One format can
+ be merged into another using merge().
+
+ A format's object index can be set with setObjectIndex(), and
+ retrieved with objectIndex(). These methods can be used to
+ associate the format with a QTextObject. It is used to represent
+ lists, frames, and tables inside the document.
+
+ \sa {Text Processing Classes}
+*/
+
+/*!
+ \enum QTextFormat::FormatType
+
+ \value InvalidFormat
+ \value BlockFormat
+ \value CharFormat
+ \value ListFormat
+ \value TableFormat
+ \value FrameFormat
+
+ \value UserFormat
+*/
+
+/*!
+ \enum QTextFormat::Property
+
+ \value ObjectIndex
+
+ Paragraph and character properties
+
+ \value CssFloat
+ \value LayoutDirection The layout direction of the text in the document
+ (Qt::LayoutDirection).
+
+ \value OutlinePen
+ \value ForegroundBrush
+ \value BackgroundBrush
+ \value BackgroundImageUrl
+
+ Paragraph properties
+
+ \value BlockAlignment
+ \value BlockTopMargin
+ \value BlockBottomMargin
+ \value BlockLeftMargin
+ \value BlockRightMargin
+ \value TextIndent
+ \value TabPositions Specifies the tab positions. The tab positions are structs of QTextOption::Tab which are stored in
+ a QList (internally, in a QList<QVariant>).
+ \value BlockIndent
+ \value BlockNonBreakableLines
+ \value BlockTrailingHorizontalRulerWidth
+
+ Character properties
+
+ \value FontFamily
+ \value FontPointSize
+ \value FontSizeAdjustment Specifies the change in size given to the fontsize already set using
+ FontPointSize or FontPixelSize.
+ \omitvalue FontSizeIncrement
+ \value FontWeight
+ \value FontItalic
+ \value FontUnderline \e{This property has been deprecated.} Use QTextFormat::TextUnderlineStyle instead.
+ \value FontOverline
+ \value FontStrikeOut
+ \value FontFixedPitch
+ \value FontPixelSize
+ \value FontCapitalization Specifies the capitalization type that is to be applied to the text.
+ \value FontLetterSpacing Changes the default spacing between individual letters in the font. The value is
+ specified in percentage, with 100 as the default value.
+ \value FontWordSpacing Changes the default spacing between individual words. A positive value increases the word spacing
+ by the corresponding pixels; a negative value decreases the spacing.
+ \value FontStyleHint Corresponds to the QFont::StyleHint property
+ \value FontStyleStrategy Corresponds to the QFont::StyleStrategy property
+ \value FontKerning Specifies whether the font has kerning turned on.
+
+ \omitvalue FirstFontProperty
+ \omitvalue LastFontProperty
+
+ \value TextUnderlineColor
+ \value TextVerticalAlignment
+ \value TextOutline
+ \value TextUnderlineStyle
+ \value TextToolTip Specifies the (optional) tool tip to be displayed for a fragment of text.
+
+ \value IsAnchor
+ \value AnchorHref
+ \value AnchorName
+ \value ObjectType
+
+ List properties
+
+ \value ListStyle
+ \value ListIndent
+
+ Table and frame properties
+
+ \value FrameBorder
+ \value FrameBorderBrush
+ \value FrameBorderStyle
+ \value FrameBottomMargin
+ \value FrameHeight
+ \value FrameLeftMargin
+ \value FrameMargin
+ \value FramePadding
+ \value FrameRightMargin
+ \value FrameTopMargin
+ \value FrameWidth
+ \value TableCellSpacing
+ \value TableCellPadding
+ \value TableColumns
+ \value TableColumnWidthConstraints
+ \value TableHeaderRowCount
+
+ Table cell properties
+
+ \value TableCellRowSpan
+ \value TableCellColumnSpan
+ \value TableCellLeftPadding
+ \value TableCellRightPadding
+ \value TableCellTopPadding
+ \value TableCellBottomPadding
+
+ Image properties
+
+ \value ImageName
+ \value ImageWidth
+ \value ImageHeight
+
+ Selection properties
+
+ \value FullWidthSelection When set on the characterFormat of a selection, the whole width of the text will be shown selected
+
+ Page break properties
+
+ \value PageBreakPolicy
+
+ \value UserProperty
+*/
+
+/*!
+ \enum QTextFormat::ObjectTypes
+
+ \value NoObject
+ \value ImageObject
+ \value TableObject
+ \value TableCellObject
+ \value UserObject The first object that can be used for application-specific purposes.
+*/
+
+/*!
+ \enum QTextFormat::PageBreakFlag
+ \since 4.2
+
+ \value PageBreak_Auto The page break is determined automatically depending on the
+ available space on the current page
+ \value PageBreak_AlwaysBefore The page is always broken before the paragraph/table
+ \value PageBreak_AlwaysAfter A new page is always started after the paragraph/table
+*/
+
+/*!
+ \fn bool QTextFormat::isValid() const
+
+ Returns true if the format is valid (i.e. is not
+ InvalidFormat); otherwise returns false.
+*/
+
+/*!
+ \fn bool QTextFormat::isCharFormat() const
+
+ Returns true if this text format is a \c CharFormat; otherwise
+ returns false.
+*/
+
+
+/*!
+ \fn bool QTextFormat::isBlockFormat() const
+
+ Returns true if this text format is a \c BlockFormat; otherwise
+ returns false.
+*/
+
+
+/*!
+ \fn bool QTextFormat::isListFormat() const
+
+ Returns true if this text format is a \c ListFormat; otherwise
+ returns false.
+*/
+
+
+/*!
+ \fn bool QTextFormat::isTableFormat() const
+
+ Returns true if this text format is a \c TableFormat; otherwise
+ returns false.
+*/
+
+
+/*!
+ \fn bool QTextFormat::isFrameFormat() const
+
+ Returns true if this text format is a \c FrameFormat; otherwise
+ returns false.
+*/
+
+
+/*!
+ \fn bool QTextFormat::isImageFormat() const
+
+ Returns true if this text format is an image format; otherwise
+ returns false.
+*/
+
+
+/*!
+ \fn bool QTextFormat::isTableCellFormat() const
+ \since 4.4
+
+ Returns true if this text format is a \c TableCellFormat; otherwise
+ returns false.
+*/
+
+
+/*!
+ Creates a new text format with an \c InvalidFormat.
+
+ \sa FormatType
+*/
+QTextFormat::QTextFormat()
+ : format_type(InvalidFormat)
+{
+}
+
+/*!
+ Creates a new text format of the given \a type.
+
+ \sa FormatType
+*/
+QTextFormat::QTextFormat(int type)
+ : format_type(type)
+{
+}
+
+
+/*!
+ \fn QTextFormat::QTextFormat(const QTextFormat &other)
+
+ Creates a new text format with the same attributes as the \a other
+ text format.
+*/
+QTextFormat::QTextFormat(const QTextFormat &rhs)
+ : d(rhs.d), format_type(rhs.format_type)
+{
+}
+
+/*!
+ \fn QTextFormat &QTextFormat::operator=(const QTextFormat &other)
+
+ Assigns the \a other text format to this text format, and returns a
+ reference to this text format.
+*/
+QTextFormat &QTextFormat::operator=(const QTextFormat &rhs)
+{
+ d = rhs.d;
+ format_type = rhs.format_type;
+ return *this;
+}
+
+/*!
+ Destroys this text format.
+*/
+QTextFormat::~QTextFormat()
+{
+}
+
+
+/*!
+ Returns the text format as a QVariant
+*/
+QTextFormat::operator QVariant() const
+{
+ return QVariant(QVariant::TextFormat, this);
+}
+
+/*!
+ Merges the \a other format with this format; where there are
+ conflicts the \a other format takes precedence.
+*/
+void QTextFormat::merge(const QTextFormat &other)
+{
+ if (format_type != other.format_type)
+ return;
+
+ if (!d) {
+ d = other.d;
+ return;
+ }
+
+ if (!other.d)
+ return;
+
+ QTextFormatPrivate *d = this->d;
+
+ const QVector<QTextFormatPrivate::Property> &otherProps = other.d->props;
+ d->props.reserve(d->props.size() + otherProps.size());
+ for (int i = 0; i < otherProps.count(); ++i) {
+ const QTextFormatPrivate::Property &p = otherProps.at(i);
+ d->insertProperty(p.key, p.value);
+ }
+}
+
+/*!
+ Returns the type of this format.
+
+ \sa FormatType
+*/
+int QTextFormat::type() const
+{
+ return format_type;
+}
+
+/*!
+ Returns this format as a block format.
+*/
+QTextBlockFormat QTextFormat::toBlockFormat() const
+{
+ return QTextBlockFormat(*this);
+}
+
+/*!
+ Returns this format as a character format.
+*/
+QTextCharFormat QTextFormat::toCharFormat() const
+{
+ return QTextCharFormat(*this);
+}
+
+/*!
+ Returns this format as a list format.
+*/
+QTextListFormat QTextFormat::toListFormat() const
+{
+ return QTextListFormat(*this);
+}
+
+/*!
+ Returns this format as a table format.
+*/
+QTextTableFormat QTextFormat::toTableFormat() const
+{
+ return QTextTableFormat(*this);
+}
+
+/*!
+ Returns this format as a frame format.
+*/
+QTextFrameFormat QTextFormat::toFrameFormat() const
+{
+ return QTextFrameFormat(*this);
+}
+
+/*!
+ Returns this format as an image format.
+*/
+QTextImageFormat QTextFormat::toImageFormat() const
+{
+ return QTextImageFormat(*this);
+}
+
+/*!
+ \since 4.4
+
+ Returns this format as a table cell format.
+*/
+QTextTableCellFormat QTextFormat::toTableCellFormat() const
+{
+ return QTextTableCellFormat(*this);
+}
+
+/*!
+ Returns the value of the property specified by \a propertyId. If the
+ property isn't of QTextFormat::Bool type, false is returned instead.
+
+ \sa setProperty() intProperty() doubleProperty() stringProperty() colorProperty() lengthProperty() lengthVectorProperty() Property
+*/
+bool QTextFormat::boolProperty(int propertyId) const
+{
+ if (!d)
+ return false;
+ const QVariant prop = d->property(propertyId);
+ if (prop.type() != QVariant::Bool)
+ return false;
+ return prop.toBool();
+}
+
+/*!
+ Returns the value of the property specified by \a propertyId. If the
+ property is not of QTextFormat::Integer type, 0 is returned instead.
+
+ \sa setProperty() boolProperty() doubleProperty() stringProperty() colorProperty() lengthProperty() lengthVectorProperty() Property
+*/
+int QTextFormat::intProperty(int propertyId) const
+{
+ if (!d)
+ return 0;
+ const QVariant prop = d->property(propertyId);
+ if (prop.type() != QVariant::Int)
+ return 0;
+ return prop.toInt();
+}
+
+/*!
+ Returns the value of the property specified by \a propertyId. If the
+ property isn't of QVariant::Double type, 0 is returned instead.
+
+ \sa setProperty() boolProperty() intProperty() stringProperty() colorProperty() lengthProperty() lengthVectorProperty() Property
+*/
+qreal QTextFormat::doubleProperty(int propertyId) const
+{
+ if (!d)
+ return 0.;
+ const QVariant prop = d->property(propertyId);
+ if (prop.type() != QVariant::Double)
+ return 0.;
+ return prop.toDouble(); // ####
+}
+
+/*!
+ Returns the value of the property given by \a propertyId; if the
+ property isn't of QVariant::String type, an empty string is
+ returned instead.
+
+ \sa setProperty() boolProperty() intProperty() doubleProperty() colorProperty() lengthProperty() lengthVectorProperty() Property
+*/
+QString QTextFormat::stringProperty(int propertyId) const
+{
+ if (!d)
+ return QString();
+ const QVariant prop = d->property(propertyId);
+ if (prop.type() != QVariant::String)
+ return QString();
+ return prop.toString();
+}
+
+/*!
+ Returns the value of the property given by \a propertyId; if the
+ property isn't of QVariant::Color type, an invalid color is
+ returned instead.
+
+ \sa setProperty(), boolProperty(), intProperty(), doubleProperty(),
+ stringProperty(), lengthProperty(), lengthVectorProperty(), Property
+*/
+QColor QTextFormat::colorProperty(int propertyId) const
+{
+ if (!d)
+ return QColor();
+ const QVariant prop = d->property(propertyId);
+ if (prop.type() != QVariant::Color)
+ return QColor();
+ return qvariant_cast<QColor>(prop);
+}
+
+/*!
+ Returns the value of the property given by \a propertyId; if the
+ property isn't of QVariant::Pen type, Qt::NoPen is
+ returned instead.
+
+ \sa setProperty() boolProperty() intProperty() doubleProperty() stringProperty() lengthProperty() lengthVectorProperty() Property
+*/
+QPen QTextFormat::penProperty(int propertyId) const
+{
+ if (!d)
+ return QPen(Qt::NoPen);
+ const QVariant prop = d->property(propertyId);
+ if (prop.type() != QVariant::Pen)
+ return QPen(Qt::NoPen);
+ return qvariant_cast<QPen>(prop);
+}
+
+/*!
+ Returns the value of the property given by \a propertyId; if the
+ property isn't of QVariant::Brush type, Qt::NoBrush is
+ returned instead.
+
+ \sa setProperty() boolProperty() intProperty() doubleProperty() stringProperty() lengthProperty() lengthVectorProperty() Property
+*/
+QBrush QTextFormat::brushProperty(int propertyId) const
+{
+ if (!d)
+ return QBrush(Qt::NoBrush);
+ const QVariant prop = d->property(propertyId);
+ if (prop.type() != QVariant::Brush)
+ return QBrush(Qt::NoBrush);
+ return qvariant_cast<QBrush>(prop);
+}
+
+/*!
+ Returns the value of the property given by \a propertyId.
+
+ \sa setProperty() boolProperty() intProperty() doubleProperty() stringProperty() colorProperty() lengthVectorProperty() Property
+*/
+QTextLength QTextFormat::lengthProperty(int propertyId) const
+{
+ if (!d)
+ return QTextLength();
+ return qvariant_cast<QTextLength>(d->property(propertyId));
+}
+
+/*!
+ Returns the value of the property given by \a propertyId. If the
+ property isn't of QTextFormat::LengthVector type, an empty length
+ vector is returned instead.
+
+ \sa setProperty() boolProperty() intProperty() doubleProperty() stringProperty() colorProperty() lengthProperty() Property
+*/
+QVector<QTextLength> QTextFormat::lengthVectorProperty(int propertyId) const
+{
+ QVector<QTextLength> vector;
+ if (!d)
+ return vector;
+ const QVariant prop = d->property(propertyId);
+ if (prop.type() != QVariant::List)
+ return vector;
+
+ QList<QVariant> propertyList = prop.toList();
+ for (int i=0; i<propertyList.size(); ++i) {
+ QVariant var = propertyList.at(i);
+ if (var.type() == QVariant::TextLength)
+ vector.append(qvariant_cast<QTextLength>(var));
+ }
+
+ return vector;
+}
+
+/*!
+ Returns the property specified by the given \a propertyId.
+*/
+QVariant QTextFormat::property(int propertyId) const
+{
+ return d ? d->property(propertyId) : QVariant();
+}
+
+/*!
+ Sets the property specified by the \a propertyId to the given \a value.
+*/
+void QTextFormat::setProperty(int propertyId, const QVariant &value)
+{
+ if (!d)
+ d = new QTextFormatPrivate;
+ if (!value.isValid())
+ clearProperty(propertyId);
+ else
+ d->insertProperty(propertyId, value);
+}
+
+/*!
+ Sets the value of the property given by \a propertyId to \a value.
+
+ \sa lengthVectorProperty() Property
+*/
+void QTextFormat::setProperty(int propertyId, const QVector<QTextLength> &value)
+{
+ if (!d)
+ d = new QTextFormatPrivate;
+ QVariantList list;
+ for (int i=0; i<value.size(); ++i)
+ list << value.at(i);
+ d->insertProperty(propertyId, list);
+}
+
+/*!
+ Clears the value of the property given by \a propertyId
+ */
+void QTextFormat::clearProperty(int propertyId)
+{
+ if (!d)
+ return;
+ d->clearProperty(propertyId);
+}
+
+
+/*!
+ \fn void QTextFormat::setObjectType(int type)
+
+ Sets the text format's object \a type. See \c{ObjectTypes}.
+*/
+
+
+/*!
+ \fn int QTextFormat::objectType() const
+
+ Returns the text format's object type. See \c{ObjectTypes}.
+*/
+
+
+/*!
+ Returns the index of the format object, or -1 if the format object is invalid.
+
+ \sa setObjectIndex()
+*/
+int QTextFormat::objectIndex() const
+{
+ if (!d)
+ return -1;
+ const QVariant prop = d->property(ObjectIndex);
+ if (prop.type() != QVariant::Int) // ####
+ return -1;
+ return prop.toInt();
+}
+
+/*!
+ \fn void QTextFormat::setObjectIndex(int index)
+
+ Sets the format object's object \a index.
+
+ \sa objectIndex()
+*/
+void QTextFormat::setObjectIndex(int o)
+{
+ if (o == -1) {
+ if (d)
+ d->clearProperty(ObjectIndex);
+ } else {
+ if (!d)
+ d = new QTextFormatPrivate;
+ // ### type
+ d->insertProperty(ObjectIndex, o);
+ }
+}
+
+/*!
+ Returns true if the text format has a property with the given \a
+ propertyId; otherwise returns false.
+
+ \sa properties() Property
+*/
+bool QTextFormat::hasProperty(int propertyId) const
+{
+ return d ? d->hasProperty(propertyId) : false;
+}
+
+/*
+ Returns the property type for the given \a propertyId.
+
+ \sa hasProperty() allPropertyIds() Property
+*/
+
+/*!
+ Returns a map with all properties of this text format.
+*/
+QMap<int, QVariant> QTextFormat::properties() const
+{
+ QMap<int, QVariant> map;
+ if (d) {
+ for (int i = 0; i < d->props.count(); ++i)
+ map.insert(d->props.at(i).key, d->props.at(i).value);
+ }
+ return map;
+}
+
+/*!
+ \since 4.3
+ Returns the number of properties stored in the format.
+*/
+int QTextFormat::propertyCount() const
+{
+ return d ? d->props.count() : 0;
+}
+
+/*!
+ \fn bool QTextFormat::operator!=(const QTextFormat &other) const
+
+ Returns true if this text format is different from the \a other text
+ format.
+*/
+
+
+/*!
+ \fn bool QTextFormat::operator==(const QTextFormat &other) const
+
+ Returns true if this text format is the same as the \a other text
+ format.
+*/
+bool QTextFormat::operator==(const QTextFormat &rhs) const
+{
+ if (format_type != rhs.format_type)
+ return false;
+
+ if (d == rhs.d)
+ return true;
+
+ if (d && d->props.isEmpty() && !rhs.d)
+ return true;
+
+ if (!d && rhs.d && rhs.d->props.isEmpty())
+ return true;
+
+ if (!d || !rhs.d)
+ return false;
+
+ return *d == *rhs.d;
+}
+
+/*!
+ \class QTextCharFormat
+ \reentrant
+
+ \brief The QTextCharFormat class provides formatting information for
+ characters in a QTextDocument.
+
+ \ingroup text
+
+ The character format of text in a document specifies the visual properties
+ of the text, as well as information about its role in a hypertext document.
+
+ The font used can be set by supplying a font to the setFont() function, and
+ each aspect of its appearance can be adjusted to give the desired effect.
+ setFontFamily() and setFontPointSize() define the font's family (e.g. Times)
+ and printed size; setFontWeight() and setFontItalic() provide control over
+ the style of the font. setFontUnderline(), setFontOverline(),
+ setFontStrikeOut(), and setFontFixedPitch() provide additional effects for
+ text.
+
+ The color is set with setForeground(). If the text is intended to be used
+ as an anchor (for hyperlinks), this can be enabled with setAnchor(). The
+ setAnchorHref() and setAnchorNames() functions are used to specify the
+ information about the hyperlink's destination and the anchor's name.
+
+ \sa QTextFormat QTextBlockFormat QTextTableFormat QTextListFormat
+*/
+
+/*!
+ \enum QTextCharFormat::VerticalAlignment
+
+ This enum describes the ways that adjacent characters can be vertically
+ aligned.
+
+ \value AlignNormal Adjacent characters are positioned in the standard
+ way for text in the writing system in use.
+ \value AlignSuperScript Characters are placed above the baseline for
+ normal text.
+ \value AlignSubScript Characters are placed below the baseline for
+ normal text.
+ \value AlignMiddle The center of the object is vertically aligned with the base line.
+ Currently, this is only implemented for inline objects.
+ \value AlignBottom The bottom edge of the object is vertically aligned with
+ the base line.
+ \value AlignTop The top edge of the object is vertically aligned with
+ the base line.
+*/
+
+/*!
+ \enum QTextCharFormat::UnderlineStyle
+
+ This enum describes the different ways drawing underlined text.
+
+ \value NoUnderline Text is draw without any underlining decoration.
+ \value SingleUnderline A line is drawn using Qt::SolidLine.
+ \value DashUnderline Dashes are drawn using Qt::DashLine.
+ \value DotLine Dots are drawn using Qt::DotLine;
+ \value DashDotLine Dashs and dots are drawn using Qt::DashDotLine.
+ \value DashDotDotLine Underlines draw drawn using Qt::DashDotDotLine.
+ \value WaveUnderline The text is underlined using a wave shaped line.
+ \value SpellCheckUnderline The underline is drawn depending on the QStyle::SH_SpellCeckUnderlineStyle
+ style hint of the QApplication style. By default this is mapped to
+ WaveUnderline, on Mac OS X it is mapped to DashDotLine.
+
+ \sa Qt::PenStyle
+*/
+
+/*!
+ \fn QTextCharFormat::QTextCharFormat()
+
+ Constructs a new character format object.
+*/
+QTextCharFormat::QTextCharFormat() : QTextFormat(CharFormat) {}
+
+/*!
+ \internal
+ \fn QTextCharFormat::QTextCharFormat(const QTextFormat &other)
+
+ Creates a new character format with the same attributes as the \a given
+ text format.
+*/
+QTextCharFormat::QTextCharFormat(const QTextFormat &fmt)
+ : QTextFormat(fmt)
+{
+}
+
+/*!
+ \fn bool QTextCharFormat::isValid() const
+
+ Returns true if this character format is valid; otherwise returns
+ false.
+*/
+
+
+/*!
+ \fn void QTextCharFormat::setFontFamily(const QString &family)
+
+ Sets the text format's font \a family.
+
+ \sa setFont()
+*/
+
+
+/*!
+ \fn QString QTextCharFormat::fontFamily() const
+
+ Returns the text format's font family.
+
+ \sa font()
+*/
+
+
+/*!
+ \fn void QTextCharFormat::setFontPointSize(qreal size)
+
+ Sets the text format's font \a size.
+
+ \sa setFont()
+*/
+
+
+/*!
+ \fn qreal QTextCharFormat::fontPointSize() const
+
+ Returns the font size used to display text in this format.
+
+ \sa font()
+*/
+
+
+/*!
+ \fn void QTextCharFormat::setFontWeight(int weight)
+
+ Sets the text format's font weight to \a weight.
+
+ \sa setFont(), QFont::Weight
+*/
+
+
+/*!
+ \fn int QTextCharFormat::fontWeight() const
+
+ Returns the text format's font weight.
+
+ \sa font(), QFont::Weight
+*/
+
+
+/*!
+ \fn void QTextCharFormat::setFontItalic(bool italic)
+
+ If \a italic is true, sets the text format's font to be italic; otherwise
+ the font will be non-italic.
+
+ \sa setFont()
+*/
+
+
+/*!
+ \fn bool QTextCharFormat::fontItalic() const
+
+ Returns true if the text format's font is italic; otherwise
+ returns false.
+
+ \sa font()
+*/
+
+
+/*!
+ \fn void QTextCharFormat::setFontUnderline(bool underline)
+
+ If \a underline is true, sets the text format's font to be underlined;
+ otherwise it is displayed non-underlined.
+
+ \sa setFont()
+*/
+
+
+/*!
+ \fn bool QTextCharFormat::fontUnderline() const
+
+ Returns true if the text format's font is underlined; otherwise
+ returns false.
+
+ \sa font()
+*/
+bool QTextCharFormat::fontUnderline() const
+{
+ if (hasProperty(TextUnderlineStyle))
+ return underlineStyle() == SingleUnderline;
+ return boolProperty(FontUnderline);
+}
+
+/*!
+ \fn UnderlineStyle QTextCharFormat::underlineStyle() const
+ \since 4.2
+
+ Returns the style of underlining the text.
+*/
+
+/*!
+ \fn void QTextCharFormat::setUnderlineStyle(UnderlineStyle style)
+ \since 4.2
+
+ Sets the style of underlining the text to \a style.
+*/
+void QTextCharFormat::setUnderlineStyle(UnderlineStyle style)
+{
+ setProperty(TextUnderlineStyle, style);
+ // for compatibility
+ setProperty(FontUnderline, style == SingleUnderline);
+}
+
+/*!
+ \fn void QTextCharFormat::setFontOverline(bool overline)
+
+ If \a overline is true, sets the text format's font to be overlined;
+ otherwise the font is displayed non-overlined.
+
+ \sa setFont()
+*/
+
+
+/*!
+ \fn bool QTextCharFormat::fontOverline() const
+
+ Returns true if the text format's font is overlined; otherwise
+ returns false.
+
+ \sa font()
+*/
+
+
+/*!
+ \fn void QTextCharFormat::setFontStrikeOut(bool strikeOut)
+
+ If \a strikeOut is true, sets the text format's font with strike-out
+ enabled (with a horizontal line through it); otherwise it is displayed
+ without strikeout.
+
+ \sa setFont()
+*/
+
+
+/*!
+ \fn bool QTextCharFormat::fontStrikeOut() const
+
+ Returns true if the text format's font is struck out (has a horizontal line
+ drawn through it); otherwise returns false.
+
+ \sa font()
+*/
+
+
+/*!
+ \since 4.5
+ \fn void QTextCharFormat::setFontStyleHint(QFont::StyleHint hint, QFont::StyleStrategy strategy)
+
+ Sets the font style \a hint and \a strategy.
+
+ Qt does not support style hints on X11 since this information is not provided by the window system.
+
+ \sa setFont()
+ \sa QFont::setStyleHint()
+*/
+
+
+/*!
+ \since 4.5
+ \fn void QTextCharFormat::setFontStyleStrategy(QFont::StyleStrategy strategy)
+
+ Sets the font style \a strategy.
+
+ \sa setFont()
+ \sa QFont::setStyleStrategy()
+*/
+
+
+/*!
+ \since 4.5
+ \fn void QTextCharFormat::setFontKerning(bool enable)
+ Enables kerning for this font if \a enable is true; otherwise disables it.
+
+ When kerning is enabled, glyph metrics do not add up anymore, even for
+ Latin text. In other words, the assumption that width('a') + width('b')
+ is equal to width("ab") is not neccesairly true.
+
+ \sa setFont()
+*/
+
+
+/*!
+ \fn QTextCharFormat::StyleHint QTextCharFormat::fontStyleHint() const
+ \since 4.5
+
+ Returns the font style hint.
+
+ \sa setFontStyleHint(), font()
+*/
+
+
+/*!
+ \since 4.5
+ \fn QTextCharFormat::StyleStrategy QTextCharFormat::fontStyleStrategy() const
+
+ Returns the current font style strategy.
+
+ \sa setFontStyleStrategy()
+ \sa font()
+*/
+
+
+/*!
+ \since 4.5
+ \fn bool QTextCharFormat::fontKerning() const
+ Returns true if the the font kerning is enabled.
+
+ \sa setFontKerning()
+ \sa font()
+*/
+
+
+/*!
+ \fn void QTextCharFormat::setFontFixedPitch(bool fixedPitch)
+
+ If \a fixedPitch is true, sets the text format's font to be fixed pitch;
+ otherwise a non-fixed pitch font is used.
+
+ \sa setFont()
+*/
+
+
+/*!
+ \fn bool QTextCharFormat::fontFixedPitch() const
+
+ Returns true if the text format's font is fixed pitch; otherwise
+ returns false.
+
+ \sa font()
+*/
+
+
+/*!
+ \fn QPen QTextCharFormat::textOutline() const
+
+ Returns the pen used to draw the outlines of characters in this format.
+*/
+
+
+/*!
+ \fn void QTextCharFormat::setTextOutline(const QPen &pen)
+
+ Sets the pen used to draw the outlines of characters to the given \a pen.
+*/
+
+/*!
+ \fn void QTextCharFormat::setToolTip(const QString &text)
+ \since 4.3
+
+ Sets the tool tip for a fragment of text to the given \a text.
+*/
+
+/*!
+ \fn QString QTextCharFormat::toolTip() const
+ \since 4.3
+
+ Returns the tool tip that is displayed for a fragment of text.
+*/
+
+/*!
+ \fn void QTextFormat::setForeground(const QBrush &brush)
+
+ Sets the foreground brush to the specified \a brush. The foreground
+ brush is mostly used to render text.
+
+ \sa foreground() clearForeground() setBackground()
+*/
+
+
+/*!
+ \fn QBrush QTextFormat::foreground() const
+
+ Returns the brush used to render foreground details, such as text,
+ frame outlines, and table borders.
+
+ \sa setForeground() clearForeground() background()
+*/
+
+/*!
+ \fn void QTextFormat::clearForeground()
+
+ Clears the brush used to paint the document's foreground. The default
+ brush will be used.
+
+ \sa foreground() setForeground() clearBackground()
+*/
+
+
+/*!
+ \fn void QTextCharFormat::setAnchor(bool anchor)
+
+ If \a anchor is true, text with this format represents an anchor, and is
+ formatted in the appropriate way; otherwise the text is formatted normally.
+ (Anchors are hyperlinks which are often shown underlined and in a different
+ color from plain text.)
+
+ The way the text is rendered is independent of whether or not the format
+ has a valid anchor defined. Use setAnchorHref(), and optionally
+ setAnchorNames() to create a hypertext link.
+
+ \sa isAnchor()
+*/
+
+
+/*!
+ \fn bool QTextCharFormat::isAnchor() const
+
+ Returns true if the text is formatted as an anchor; otherwise
+ returns false.
+
+ \sa setAnchor() setAnchorHref() setAnchorNames()
+*/
+
+
+/*!
+ \fn void QTextCharFormat::setAnchorHref(const QString &value)
+
+ Sets the hypertext link for the text format to the given \a value.
+ This is typically a URL like "http://qtsoftware.com/index.html".
+
+ The anchor will be displayed with the \a value as its display text;
+ if you want to display different text call setAnchorNames().
+
+ To format the text as a hypertext link use setAnchor().
+*/
+
+
+/*!
+ \fn QString QTextCharFormat::anchorHref() const
+
+ Returns the text format's hypertext link, or an empty string if
+ none has been set.
+*/
+
+
+/*!
+ \fn void QTextCharFormat::setAnchorName(const QString &name)
+ \obsolete
+
+ This function is deprecated. Use setAnchorNames() instead.
+
+ Sets the text format's anchor \a name. For the anchor to work as a
+ hyperlink, the destination must be set with setAnchorHref() and
+ the anchor must be enabled with setAnchor().
+*/
+
+/*!
+ \fn void QTextCharFormat::setAnchorNames(const QStringList &names)
+ \since 4.3
+
+ Sets the text format's anchor \a names. For the anchor to work as a
+ hyperlink, the destination must be set with setAnchorHref() and
+ the anchor must be enabled with setAnchor().
+*/
+
+/*!
+ \fn QString QTextCharFormat::anchorName() const
+ \obsolete
+
+ This function is deprecated. Use anchorNames() instead.
+
+ Returns the anchor name associated with this text format, or an empty
+ string if none has been set. If the anchor name is set, text with this
+ format can be the destination of a hypertext link.
+*/
+QString QTextCharFormat::anchorName() const
+{
+ QVariant prop = property(AnchorName);
+ if (prop.type() == QVariant::StringList)
+ return prop.toStringList().value(0);
+ else if (prop.type() != QVariant::String)
+ return QString();
+ return prop.toString();
+}
+
+/*!
+ \fn QStringList QTextCharFormat::anchorNames() const
+ \since 4.3
+
+ Returns the anchor names associated with this text format, or an empty
+ string list if none has been set. If the anchor names are set, text with this
+ format can be the destination of a hypertext link.
+*/
+QStringList QTextCharFormat::anchorNames() const
+{
+ QVariant prop = property(AnchorName);
+ if (prop.type() == QVariant::StringList)
+ return prop.toStringList();
+ else if (prop.type() != QVariant::String)
+ return QStringList();
+ return QStringList(prop.toString());
+}
+
+
+/*!
+ \fn void QTextCharFormat::setTableCellRowSpan(int tableCellRowSpan)
+ \internal
+
+ If this character format is applied to characters in a table cell,
+ the cell will span \a tableCellRowSpan rows.
+*/
+
+
+/*!
+ \fn int QTextCharFormat::tableCellRowSpan() const
+ \internal
+
+ If this character format is applied to characters in a table cell,
+ this function returns the number of rows spanned by the text (this may
+ be 1); otherwise it returns 1.
+*/
+
+/*!
+ \fn void QTextCharFormat::setTableCellColumnSpan(int tableCellColumnSpan)
+ \internal
+
+ If this character format is applied to characters in a table cell,
+ the cell will span \a tableCellColumnSpan columns.
+*/
+
+
+/*!
+ \fn int QTextCharFormat::tableCellColumnSpan() const
+ \internal
+
+ If this character format is applied to characters in a table cell,
+ this function returns the number of columns spanned by the text (this
+ may be 1); otherwise it returns 1.
+*/
+
+/*!
+ \fn void QTextCharFormat::setUnderlineColor(const QColor &color)
+
+ Sets the underline color used for the characters with this format to
+ the \a color specified.
+
+ \sa underlineColor()
+*/
+
+/*!
+ \fn QColor QTextCharFormat::underlineColor() const
+
+ Returns the color used to underline the characters with this format.
+
+ \sa setUnderlineColor()
+*/
+
+/*!
+ \fn void QTextCharFormat::setVerticalAlignment(VerticalAlignment alignment)
+
+ Sets the vertical alignment used for the characters with this format to
+ the \a alignment specified.
+
+ \sa verticalAlignment()
+*/
+
+/*!
+ \fn VerticalAlignment QTextCharFormat::verticalAlignment() const
+
+ Returns the vertical alignment used for characters with this format.
+
+ \sa setVerticalAlignment()
+*/
+
+/*!
+ Sets the text format's \a font.
+*/
+void QTextCharFormat::setFont(const QFont &font)
+{
+ setFontFamily(font.family());
+
+ const qreal pointSize = font.pointSizeF();
+ if (pointSize > 0) {
+ setFontPointSize(pointSize);
+ } else {
+ const int pixelSize = font.pixelSize();
+ if (pixelSize > 0)
+ setProperty(QTextFormat::FontPixelSize, pixelSize);
+ }
+
+ setFontWeight(font.weight());
+ setFontItalic(font.italic());
+ setUnderlineStyle(font.underline() ? SingleUnderline : NoUnderline);
+ setFontOverline(font.overline());
+ setFontStrikeOut(font.strikeOut());
+ setFontFixedPitch(font.fixedPitch());
+ setFontCapitalization(font.capitalization());
+ setFontWordSpacing(font.wordSpacing());
+ if (font.letterSpacingType() == QFont::PercentageSpacing)
+ setFontLetterSpacing(font.letterSpacing());
+ setFontStyleHint(font.styleHint());
+ setFontStyleStrategy(font.styleStrategy());
+ setFontKerning(font.kerning());
+}
+
+/*!
+ Returns the font for this character format.
+*/
+QFont QTextCharFormat::font() const
+{
+ return d ? d->font() : QFont();
+}
+
+/*!
+ \class QTextBlockFormat
+ \reentrant
+
+ \brief The QTextBlockFormat class provides formatting information for
+ blocks of text in a QTextDocument.
+
+ \ingroup text
+
+ A document is composed of a list of blocks, represented by QTextBlock
+ objects. Each block can contain an item of some kind, such as a
+ paragraph of text, a table, a list, or an image. Every block has an
+ associated QTextBlockFormat that specifies its characteristics.
+
+ To cater for left-to-right and right-to-left languages you can set
+ a block's direction with setDirection(). Paragraph alignment is
+ set with setAlignment(). Margins are controlled by setTopMargin(),
+ setBottomMargin(), setLeftMargin(), setRightMargin(). Overall
+ indentation is set with setIndent(), the indentation of the first
+ line with setTextIndent().
+
+ Line breaking can be enabled and disabled with setNonBreakableLines().
+
+ The brush used to paint the paragraph's background
+ is set with \l{QTextFormat::setBackground()}{setBackground()}, and other
+ aspects of the text's appearance can be customized by using the
+ \l{QTextFormat::setProperty()}{setProperty()} function with the
+ \c OutlinePen, \c ForegroundBrush, and \c BackgroundBrush
+ \l{QTextFormat::Property} values.
+
+ If a text block is part of a list, it can also have a list format that
+ is accessible with the listFormat() function.
+
+ \sa QTextBlock, QTextCharFormat
+*/
+
+/*!
+ \fn QTextBlockFormat::QTextBlockFormat()
+
+ Constructs a new QTextBlockFormat.
+*/
+QTextBlockFormat::QTextBlockFormat() : QTextFormat(BlockFormat) {}
+
+/*!
+ \internal
+ \fn QTextBlockFormat::QTextBlockFormat(const QTextFormat &other)
+
+ Creates a new block format with the same attributes as the \a given
+ text format.
+*/
+QTextBlockFormat::QTextBlockFormat(const QTextFormat &fmt)
+ : QTextFormat(fmt)
+{
+}
+
+/*!
+ \since 4.4
+ Sets the tab positions for the text block to those specified by
+ \a tabs.
+
+ \sa tabPositions()
+*/
+void QTextBlockFormat::setTabPositions(const QList<QTextOption::Tab> &tabs)
+{
+ QList<QVariant> list;
+ QList<QTextOption::Tab>::ConstIterator iter = tabs.constBegin();
+ while (iter != tabs.constEnd()) {
+ QVariant v;
+ qVariantSetValue<QTextOption::Tab>(v, *iter);
+ list.append(v);
+ ++iter;
+ }
+ setProperty(TabPositions, list);
+}
+
+/*!
+ \since 4.4
+ Returns a list of tab positions defined for the text block.
+
+ \sa setTabPositions()
+*/
+QList<QTextOption::Tab> QTextBlockFormat::tabPositions() const
+{
+ QVariant variant = property(TabPositions);
+ if(variant.isNull())
+ return QList<QTextOption::Tab>();
+ QList<QTextOption::Tab> answer;
+ QList<QVariant> variantsList = qvariant_cast<QList<QVariant> >(variant);
+ QList<QVariant>::Iterator iter = variantsList.begin();
+ while(iter != variantsList.end()) {
+ answer.append( qVariantValue<QTextOption::Tab>(*iter));
+ ++iter;
+ }
+ return answer;
+}
+
+/*!
+ \fn QTextBlockFormat::isValid() const
+
+ Returns true if this block format is valid; otherwise returns
+ false.
+*/
+
+/*!
+ \fn void QTextFormat::setLayoutDirection(Qt::LayoutDirection direction)
+
+ Sets the document's layout direction to the specified \a direction.
+
+ \sa layoutDirection()
+*/
+
+
+/*!
+ \fn Qt::LayoutDirection QTextFormat::layoutDirection() const
+
+ Returns the document's layout direction.
+
+ \sa setLayoutDirection()
+*/
+
+
+/*!
+ \fn void QTextBlockFormat::setAlignment(Qt::Alignment alignment)
+
+ Sets the paragraph's \a alignment.
+
+ \sa alignment()
+*/
+
+
+/*!
+ \fn Qt::Alignment QTextBlockFormat::alignment() const
+
+ Returns the paragraph's alignment.
+
+ \sa setAlignment()
+*/
+
+
+/*!
+ \fn void QTextBlockFormat::setTopMargin(qreal margin)
+
+ Sets the paragraph's top \a margin.
+
+ \sa topMargin() setBottomMargin() setLeftMargin() setRightMargin()
+*/
+
+
+/*!
+ \fn qreal QTextBlockFormat::topMargin() const
+
+ Returns the paragraph's top margin.
+
+ \sa setTopMargin() bottomMargin()
+*/
+
+
+/*!
+ \fn void QTextBlockFormat::setBottomMargin(qreal margin)
+
+ Sets the paragraph's bottom \a margin.
+
+ \sa bottomMargin() setTopMargin() setLeftMargin() setRightMargin()
+*/
+
+
+/*!
+ \fn qreal QTextBlockFormat::bottomMargin() const
+
+ Returns the paragraph's bottom margin.
+
+ \sa setBottomMargin() topMargin()
+*/
+
+
+/*!
+ \fn void QTextBlockFormat::setLeftMargin(qreal margin)
+
+ Sets the paragraph's left \a margin. Indentation can be applied separately
+ with setIndent().
+
+ \sa leftMargin() setRightMargin() setTopMargin() setBottomMargin()
+*/
+
+
+/*!
+ \fn qreal QTextBlockFormat::leftMargin() const
+
+ Returns the paragraph's left margin.
+
+ \sa setLeftMargin() rightMargin() indent()
+*/
+
+
+/*!
+ \fn void QTextBlockFormat::setRightMargin(qreal margin)
+
+ Sets the paragraph's right \a margin.
+
+ \sa rightMargin() setLeftMargin() setTopMargin() setBottomMargin()
+*/
+
+
+/*!
+ \fn qreal QTextBlockFormat::rightMargin() const
+
+ Returns the paragraph's right margin.
+
+ \sa setRightMargin() leftMargin()
+*/
+
+
+/*!
+ \fn void QTextBlockFormat::setTextIndent(qreal indent)
+
+ Sets the \a indent for the first line in the block. This allows the first
+ line of a paragraph to be indented differently to the other lines,
+ enhancing the readability of the text.
+
+ \sa textIndent() setLeftMargin() setRightMargin() setTopMargin() setBottomMargin()
+*/
+
+
+/*!
+ \fn qreal QTextBlockFormat::textIndent() const
+
+ Returns the paragraph's text indent.
+
+ \sa setTextIndent()
+*/
+
+
+/*!
+ \fn void QTextBlockFormat::setIndent(int indentation)
+
+ Sets the paragraph's \a indentation. Margins are set independently of
+ indentation with setLeftMargin() and setTextIndent().
+ The \a indentation is an integer that is multiplied with the document-wide
+ standard indent, resulting in the actual indent of the paragraph.
+
+ \sa indent() QTextDocument::indentWidth()
+*/
+
+
+/*!
+ \fn int QTextBlockFormat::indent() const
+
+ Returns the paragraph's indent.
+
+ \sa setIndent()
+*/
+
+
+/*!
+ \fn void QTextBlockFormat::setNonBreakableLines(bool b)
+
+ If \a b is true, the lines in the paragraph are treated as
+ non-breakable; otherwise they are breakable.
+
+ \sa nonBreakableLines()
+*/
+
+
+/*!
+ \fn bool QTextBlockFormat::nonBreakableLines() const
+
+ Returns true if the lines in the paragraph are non-breakable;
+ otherwise returns false.
+
+ \sa setNonBreakableLines()
+*/
+
+/*!
+ \fn QTextFormat::PageBreakFlags QTextBlockFormat::pageBreakPolicy() const
+ \since 4.2
+
+ Returns the currently set page break policy for the paragraph. The default is
+ QTextFormat::PageBreak_Auto.
+
+ \sa setPageBreakPolicy()
+*/
+
+/*!
+ \fn void QTextBlockFormat::setPageBreakPolicy(PageBreakFlags policy)
+ \since 4.2
+
+ Sets the page break policy for the paragraph to \a policy.
+
+ \sa pageBreakPolicy()
+*/
+
+/*!
+ \class QTextListFormat
+ \reentrant
+
+ \brief The QTextListFormat class provides formatting information for
+ lists in a QTextDocument.
+
+ \ingroup text
+
+ A list is composed of one or more items, represented as text blocks.
+ The list's format specifies the appearance of items in the list.
+ In particular, it determines the indentation and the style of each item.
+
+ The indentation of the items is an integer value that causes each item to
+ be offset from the left margin by a certain amount. This value is read with
+ indent() and set with setIndent().
+
+ The style used to decorate each item is set with setStyle() and can be read
+ with the style() function. The style controls the type of bullet points and
+ numbering scheme used for items in the list. Note that lists that use the
+ decimal numbering scheme begin counting at 1 rather than 0.
+
+ \sa QTextList
+*/
+
+/*!
+ \enum QTextListFormat::Style
+
+ This enum describes the symbols used to decorate list items:
+
+ \value ListDisc a filled circle
+ \value ListCircle an empty circle
+ \value ListSquare a filled square
+ \value ListDecimal decimal values in ascending order
+ \value ListLowerAlpha lower case Latin characters in alphabetical order
+ \value ListUpperAlpha upper case Latin characters in alphabetical order
+ \omitvalue ListStyleUndefined
+*/
+
+/*!
+ \fn QTextListFormat::QTextListFormat()
+
+ Constructs a new list format object.
+*/
+QTextListFormat::QTextListFormat()
+ : QTextFormat(ListFormat)
+{
+ setIndent(1);
+}
+
+/*!
+ \internal
+ \fn QTextListFormat::QTextListFormat(const QTextFormat &other)
+
+ Creates a new list format with the same attributes as the \a given
+ text format.
+*/
+QTextListFormat::QTextListFormat(const QTextFormat &fmt)
+ : QTextFormat(fmt)
+{
+}
+
+/*!
+ \fn bool QTextListFormat::isValid() const
+
+ Returns true if this list format is valid; otherwise
+ returns false.
+*/
+
+/*!
+ \fn void QTextListFormat::setStyle(Style style)
+
+ Sets the list format's \a style. See \c{Style} for the available styles.
+
+ \sa style()
+*/
+
+/*!
+ \fn Style QTextListFormat::style() const
+
+ Returns the list format's style. See \c{Style}.
+
+ \sa setStyle()
+*/
+
+
+/*!
+ \fn void QTextListFormat::setIndent(int indentation)
+
+ Sets the list format's \a indentation.
+ The indentation is multiplied by the QTextDocument::indentWidth
+ property to get the effective indent in pixels.
+
+ \sa indent()
+*/
+
+
+/*!
+ \fn int QTextListFormat::indent() const
+
+ Returns the list format's indentation.
+ The indentation is multiplied by the QTextDocument::indentWidth
+ property to get the effective indent in pixels.
+
+ \sa setIndent()
+*/
+
+
+/*!
+ \class QTextFrameFormat
+ \reentrant
+
+ \brief The QTextFrameFormat class provides formatting information for
+ frames in a QTextDocument.
+
+ \ingroup text
+
+ A text frame groups together one or more blocks of text, providing a layer
+ of structure larger than the paragraph. The format of a frame specifies
+ how it is rendered and positioned on the screen. It does not directly
+ specify the behavior of the text formatting within, but provides
+ constraints on the layout of its children.
+
+ The frame format defines the width() and height() of the frame on the
+ screen. Each frame can have a border() that surrounds its contents with
+ a rectangular box. The border is surrounded by a margin() around the frame,
+ and the contents of the frame are kept separate from the border by the
+ frame's padding(). This scheme is similar to the box model used by Cascading
+ Style Sheets for HTML pages.
+
+ \img qtextframe-style.png
+
+ The position() of a frame is set using setPosition() and determines how it
+ is located relative to the surrounding text.
+
+ The validity of a QTextFrameFormat object can be determined with the
+ isValid() function.
+
+ \sa QTextFrame QTextBlockFormat
+*/
+
+/*!
+ \enum QTextFrameFormat::Position
+
+ \value InFlow
+ \value FloatLeft
+ \value FloatRight
+
+*/
+
+/*!
+ \enum QTextFrameFormat::BorderStyle
+ \since 4.3
+
+ \value BorderStyle_None
+ \value BorderStyle_Dotted
+ \value BorderStyle_Dashed
+ \value BorderStyle_Solid
+ \value BorderStyle_Double
+ \value BorderStyle_DotDash
+ \value BorderStyle_DotDotDash
+ \value BorderStyle_Groove
+ \value BorderStyle_Ridge
+ \value BorderStyle_Inset
+ \value BorderStyle_Outset
+
+*/
+
+/*!
+ \fn QTextFrameFormat::QTextFrameFormat()
+
+ Constructs a text frame format object with the default properties.
+*/
+QTextFrameFormat::QTextFrameFormat() : QTextFormat(FrameFormat)
+{
+ setBorderStyle(BorderStyle_Outset);
+ setBorderBrush(Qt::darkGray);
+}
+
+/*!
+ \internal
+ \fn QTextFrameFormat::QTextFrameFormat(const QTextFormat &other)
+
+ Creates a new frame format with the same attributes as the \a given
+ text format.
+*/
+QTextFrameFormat::QTextFrameFormat(const QTextFormat &fmt)
+ : QTextFormat(fmt)
+{
+}
+
+/*!
+ \fn QTextFrameFormat::isValid() const
+
+ Returns true if the format description is valid; otherwise returns false.
+*/
+
+/*!
+ \fn QTextFrameFormat::setPosition(Position policy)
+
+ Sets the \a policy for positioning frames with this frame format.
+
+*/
+
+/*!
+ \fn Position QTextFrameFormat::position() const
+
+ Returns the positioning policy for frames with this frame format.
+*/
+
+/*!
+ \fn QTextFrameFormat::setBorder(qreal width)
+
+ Sets the \a width (in pixels) of the frame's border.
+*/
+
+/*!
+ \fn qreal QTextFrameFormat::border() const
+
+ Returns the width of the border in pixels.
+*/
+
+/*!
+ \fn QTextFrameFormat::setBorderBrush(const QBrush &brush)
+ \since 4.3
+
+ Sets the \a brush used for the frame's border.
+*/
+
+/*!
+ \fn QBrush QTextFrameFormat::borderBrush() const
+ \since 4.3
+
+ Returns the brush used for the frame's border.
+*/
+
+/*!
+ \fn QTextFrameFormat::setBorderStyle(BorderStyle style)
+ \since 4.3
+
+ Sets the \a style of the frame's border.
+*/
+
+/*!
+ \fn BorderStyle QTextFrameFormat::borderStyle() const
+ \since 4.3
+
+ Returns the style of the frame's border.
+*/
+
+/*!
+ \fn QTextFrameFormat::setMargin(qreal margin)
+
+ Sets the frame's \a margin in pixels.
+ This method also sets the left, right, top and bottom margins
+ of the frame to the same value. The individual margins override
+ the general margin.
+*/
+void QTextFrameFormat::setMargin(qreal amargin)
+{
+ setProperty(FrameMargin, amargin);
+ setProperty(FrameTopMargin, amargin);
+ setProperty(FrameBottomMargin, amargin);
+ setProperty(FrameLeftMargin, amargin);
+ setProperty(FrameRightMargin, amargin);
+}
+
+
+/*!
+ \fn qreal QTextFrameFormat::margin() const
+
+ Returns the width of the frame's external margin in pixels.
+*/
+
+/*!
+ \fn QTextFrameFormat::setTopMargin(qreal margin)
+ \since 4.3
+
+ Sets the frame's top \a margin in pixels.
+*/
+
+/*!
+ \fn qreal QTextFrameFormat::topMargin() const
+ \since 4.3
+
+ Returns the width of the frame's top margin in pixels.
+*/
+qreal QTextFrameFormat::topMargin() const
+{
+ if (!hasProperty(FrameTopMargin))
+ return margin();
+ return doubleProperty(FrameTopMargin);
+}
+
+/*!
+ \fn QTextFrameFormat::setBottomMargin(qreal margin)
+ \since 4.3
+
+ Sets the frame's bottom \a margin in pixels.
+*/
+
+/*!
+ \fn qreal QTextFrameFormat::bottomMargin() const
+ \since 4.3
+
+ Returns the width of the frame's bottom margin in pixels.
+*/
+qreal QTextFrameFormat::bottomMargin() const
+{
+ if (!hasProperty(FrameBottomMargin))
+ return margin();
+ return doubleProperty(FrameBottomMargin);
+}
+
+/*!
+ \fn QTextFrameFormat::setLeftMargin(qreal margin)
+ \since 4.3
+
+ Sets the frame's left \a margin in pixels.
+*/
+
+/*!
+ \fn qreal QTextFrameFormat::leftMargin() const
+ \since 4.3
+
+ Returns the width of the frame's left margin in pixels.
+*/
+qreal QTextFrameFormat::leftMargin() const
+{
+ if (!hasProperty(FrameLeftMargin))
+ return margin();
+ return doubleProperty(FrameLeftMargin);
+}
+
+/*!
+ \fn QTextFrameFormat::setRightMargin(qreal margin)
+ \since 4.3
+
+ Sets the frame's right \a margin in pixels.
+*/
+
+/*!
+ \fn qreal QTextFrameFormat::rightMargin() const
+ \since 4.3
+
+ Returns the width of the frame's right margin in pixels.
+*/
+qreal QTextFrameFormat::rightMargin() const
+{
+ if (!hasProperty(FrameRightMargin))
+ return margin();
+ return doubleProperty(FrameRightMargin);
+}
+
+/*!
+ \fn QTextFrameFormat::setPadding(qreal width)
+
+ Sets the \a width of the frame's internal padding in pixels.
+*/
+
+/*!
+ \fn qreal QTextFrameFormat::padding() const
+
+ Returns the width of the frame's internal padding in pixels.
+*/
+
+/*!
+ \fn QTextFrameFormat::setWidth(const QTextLength &width)
+
+ Sets the frame's border rectangle's \a width.
+
+ \sa QTextLength
+*/
+
+/*!
+ \fn QTextFrameFormat::setWidth(qreal width)
+ \overload
+
+ Convenience method that sets the width of the frame's border
+ rectangle's width to the specified fixed \a width.
+*/
+
+/*!
+ \fn QTextFormat::PageBreakFlags QTextFrameFormat::pageBreakPolicy() const
+ \since 4.2
+
+ Returns the currently set page break policy for the frame/table. The default is
+ QTextFormat::PageBreak_Auto.
+
+ \sa setPageBreakPolicy()
+*/
+
+/*!
+ \fn void QTextFrameFormat::setPageBreakPolicy(PageBreakFlags policy)
+ \since 4.2
+
+ Sets the page break policy for the frame/table to \a policy.
+
+ \sa pageBreakPolicy()
+*/
+
+/*!
+ \fn QTextLength QTextFrameFormat::width() const
+
+ Returns the width of the frame's border rectangle.
+
+ \sa QTextLength
+*/
+
+/*!
+ \fn void QTextFrameFormat::setHeight(const QTextLength &height)
+
+ Sets the frame's \a height.
+*/
+
+/*!
+ \fn void QTextFrameFormat::setHeight(qreal height)
+ \overload
+
+ Sets the frame's \a height.
+*/
+
+/*!
+ \fn qreal QTextFrameFormat::height() const
+
+ Returns the height of the frame's border rectangle.
+*/
+
+/*!
+ \class QTextTableFormat
+ \reentrant
+
+ \brief The QTextTableFormat class provides formatting information for
+ tables in a QTextDocument.
+
+ \ingroup text
+
+ A table is a group of cells ordered into rows and columns. Each table
+ contains at least one row and one column. Each cell contains a block.
+ Tables in rich text documents are formatted using the properties
+ defined in this class.
+
+ Tables are horizontally justified within their parent frame according to the
+ table's alignment. This can be read with the alignment() function and set
+ with setAlignment().
+
+ Cells within the table are separated by cell spacing. The number of pixels
+ between cells is set with setCellSpacing() and read with cellSpacing().
+ The contents of each cell is surrounded by cell padding. The number of pixels
+ between each cell edge and its contents is set with setCellPadding() and read
+ with cellPadding().
+
+ \image qtexttableformat-cell.png
+
+ The table's background color can be read with the background() function,
+ and can be specified with setBackground(). The background color of each
+ cell can be set independently, and will control the color of the cell within
+ the padded area.
+
+ The table format also provides a way to constrain the widths of the columns
+ in the table. Columns can be assigned a fixed width, a variable width, or
+ a percentage of the available width (see QTextLength). The columns() function
+ returns the number of columns with constraints, and the
+ columnWidthConstraints() function returns the constraints defined for the
+ table. These quantities can also be set by calling setColumnWidthConstraints()
+ with a vector containing new constraints. If no constraints are
+ required, clearColumnWidthConstraints() can be used to remove them.
+
+ \sa QTextTable QTextTableCell QTextLength
+*/
+
+/*!
+ \fn QTextTableFormat::QTextTableFormat()
+
+ Constructs a new table format object.
+*/
+QTextTableFormat::QTextTableFormat()
+ : QTextFrameFormat()
+{
+ setObjectType(TableObject);
+ setCellSpacing(2);
+ setBorder(1);
+}
+
+/*!
+ \internal
+ \fn QTextTableFormat::QTextTableFormat(const QTextFormat &other)
+
+ Creates a new table format with the same attributes as the \a given
+ text format.
+*/
+QTextTableFormat::QTextTableFormat(const QTextFormat &fmt)
+ : QTextFrameFormat(fmt)
+{
+}
+
+/*!
+ \fn bool QTextTableFormat::isValid() const
+
+ Returns true if this table format is valid; otherwise
+ returns false.
+*/
+
+
+/*!
+ \fn int QTextTableFormat::columns() const
+
+ Returns the number of columns specified by the table format.
+*/
+
+
+/*!
+ \internal
+ \fn void QTextTableFormat::setColumns(int columns)
+
+ Sets the number of \a columns required by the table format.
+
+ \sa columns()
+*/
+
+/*!
+ \fn void QTextTableFormat::clearColumnWidthConstraints()
+
+ Clears the column width constraints for the table.
+
+ \sa columnWidthConstraints() setColumnWidthConstraints()
+*/
+
+/*!
+ \fn void QTextTableFormat::setColumnWidthConstraints(const QVector<QTextLength> &constraints)
+
+ Sets the column width \a constraints for the table.
+
+ \sa columnWidthConstraints() clearColumnWidthConstraints()
+*/
+
+/*!
+ \fn QVector<QTextLength> QTextTableFormat::columnWidthConstraints() const
+
+ Returns a list of constraints used by this table format to control the
+ appearance of columns in a table.
+
+ \sa setColumnWidthConstraints()
+*/
+
+/*!
+ \fn qreal QTextTableFormat::cellSpacing() const
+
+ Returns the table's cell spacing. This describes the distance between
+ adjacent cells.
+*/
+
+/*!
+ \fn void QTextTableFormat::setCellSpacing(qreal spacing)
+
+ Sets the cell \a spacing for the table. This determines the distance
+ between adjacent cells.
+*/
+
+/*!
+ \fn qreal QTextTableFormat::cellPadding() const
+
+ Returns the table's cell padding. This describes the distance between
+ the border of a cell and its contents.
+*/
+
+/*!
+ \fn void QTextTableFormat::setCellPadding(qreal padding)
+
+ Sets the cell \a padding for the table. This determines the distance
+ between the border of a cell and its contents.
+*/
+
+/*!
+ \fn void QTextTableFormat::setAlignment(Qt::Alignment alignment)
+
+ Sets the table's \a alignment.
+
+ \sa alignment()
+*/
+
+/*!
+ \fn Qt::Alignment QTextTableFormat::alignment() const
+
+ Returns the table's alignment.
+
+ \sa setAlignment()
+*/
+
+/*!
+ \fn void QTextTableFormat::setHeaderRowCount(int count)
+ \since 4.2
+
+ Declares the first \a count rows of the table as table header.
+ The table header rows get repeated when a table is broken
+ across a page boundary.
+*/
+
+/*!
+ \fn int QTextTableFormat::headerRowCount() const
+ \since 4.2
+
+ Returns the number of rows in the table that define the header.
+
+ \sa setHeaderRowCount()
+*/
+
+/*!
+ \fn void QTextFormat::setBackground(const QBrush &brush)
+
+ Sets the brush use to paint the document's background to the
+ \a brush specified.
+
+ \sa background() clearBackground() setForeground()
+*/
+
+/*!
+ \fn QColor QTextFormat::background() const
+
+ Returns the brush used to paint the document's background.
+
+ \sa setBackground() clearBackground() foreground()
+*/
+
+/*!
+ \fn void QTextFormat::clearBackground()
+
+ Clears the brush used to paint the document's background. The default
+ brush will be used.
+
+ \sa background() setBackground() clearForeground()
+*/
+
+
+/*!
+ \class QTextImageFormat
+ \reentrant
+
+ \brief The QTextImageFormat class provides formatting information for
+ images in a QTextDocument.
+
+ \ingroup text
+
+ Inline images are represented by an object replacement character
+ (0xFFFC in Unicode) which has an associated QTextImageFormat. The
+ image format specifies a name with setName() that is used to
+ locate the image. The size of the rectangle that the image will
+ occupy is specified using setWidth() and setHeight().
+
+ Images can be supplied in any format for which Qt has an image
+ reader, so SVG drawings can be included alongside PNG, TIFF and
+ other bitmap formats.
+
+ \sa QImage, QImageReader
+*/
+
+/*!
+ \fn QTextImageFormat::QTextImageFormat()
+
+ Creates a new image format object.
+*/
+QTextImageFormat::QTextImageFormat() : QTextCharFormat() { setObjectType(ImageObject); }
+
+/*!
+ \internal
+ \fn QTextImageFormat::QTextImageFormat(const QTextFormat &other)
+
+ Creates a new image format with the same attributes as the \a given
+ text format.
+*/
+QTextImageFormat::QTextImageFormat(const QTextFormat &fmt)
+ : QTextCharFormat(fmt)
+{
+}
+
+/*!
+ \fn bool QTextImageFormat::isValid() const
+
+ Returns true if this image format is valid; otherwise returns false.
+*/
+
+
+/*!
+ \fn void QTextImageFormat::setName(const QString &name)
+
+ Sets the \a name of the image. The \a name is used to locate the image
+ in the application's resources.
+
+ \sa name()
+*/
+
+
+/*!
+ \fn QString QTextImageFormat::name() const
+
+ Returns the name of the image. The name refers to an entry in the
+ application's resources file.
+
+ \sa setName()
+*/
+
+/*!
+ \fn void QTextImageFormat::setWidth(qreal width)
+
+ Sets the \a width of the rectangle occupied by the image.
+
+ \sa width() setHeight()
+*/
+
+
+// ### Qt5 qreal replace with a QTextLength
+/*!
+ \fn qreal QTextImageFormat::width() const
+
+ Returns the width of the rectangle occupied by the image.
+
+ \sa height() setWidth()
+*/
+
+
+/*!
+ \fn void QTextImageFormat::setHeight(qreal height)
+
+ Sets the \a height of the rectangle occupied by the image.
+
+ \sa height() setWidth()
+*/
+
+
+// ### Qt5 qreal replace with a QTextLength
+/*!
+ \fn qreal QTextImageFormat::height() const
+
+ Returns the height of the rectangle occupied by the image.
+
+ \sa width() setHeight()
+*/
+
+/*!
+ \fn void QTextCharFormat::setFontCapitalization(QFont::Capitalization capitalization)
+ \since 4.4
+
+ Sets the capitalization of the text that apppears in this font to \a capitalization.
+
+ A font's capitalization makes the text appear in the selected capitalization mode.
+
+ \sa fontCapitalization()
+*/
+
+/*!
+ \fn Capitalization QTextCharFormat::fontCapitalization() const
+ \since 4.4
+
+ Returns the current capitalization type of the font.
+*/
+
+/*!
+ \fn void QTextCharFormat::setFontLetterSpacing(qreal spacing)
+ \since 4.4
+
+ Sets the letter spacing of this format to the given \a spacing, in percent.
+ A value of 100 indicates default spacing; a value of 200 doubles the amount
+ of space a letter takes.
+
+ \sa fontLetterSpacing()
+*/
+
+/*!
+ \fn qreal QTextCharFormat::fontLetterSpacing() const
+ \since 4.4
+
+ Returns the current letter spacing percentage.
+*/
+
+/*!
+ \fn void QTextCharFormat::setFontWordSpacing(qreal spacing)
+ \since 4.4
+
+ Sets the word spacing of this format to the given \a spacing, in pixels.
+
+ \sa fontWordSpacing()
+*/
+
+/*!
+ \fn qreal QTextCharFormat::fontWordSpacing() const
+ \since 4.4
+
+ Returns the current word spacing value.
+*/
+
+/*!
+ \fn qreal QTextTableCellFormat::topPadding() const
+ \since 4.4
+
+ Gets the top padding of the table cell.
+
+ \sa setTopPadding(), leftPadding(), rightPadding(), bottomPadding()
+*/
+
+/*!
+ \fn qreal QTextTableCellFormat::bottomPadding() const
+ \since 4.4
+
+ Gets the bottom padding of the table cell.
+
+ \sa setBottomPadding(), leftPadding(), rightPadding(), topPadding()
+*/
+
+/*!
+ \fn qreal QTextTableCellFormat::leftPadding() const
+ \since 4.4
+
+ Gets the left padding of the table cell.
+
+ \sa setLeftPadding(), rightPadding(), topPadding(), bottomPadding()
+*/
+
+/*!
+ \fn qreal QTextTableCellFormat::rightPadding() const
+ \since 4.4
+
+ Gets the right padding of the table cell.
+
+ \sa setRightPadding(), leftPadding(), topPadding(), bottomPadding()
+*/
+
+/*!
+ \fn void QTextTableCellFormat::setTopPadding(qreal padding)
+ \since 4.4
+
+ Sets the top \a padding of the table cell.
+
+ \sa topPadding(), setLeftPadding(), setRightPadding(), setBottomPadding()
+*/
+
+/*!
+ \fn void QTextTableCellFormat::setBottomPadding(qreal padding)
+ \since 4.4
+
+ Sets the bottom \a padding of the table cell.
+
+ \sa bottomPadding(), setLeftPadding(), setRightPadding(), setTopPadding()
+*/
+
+/*!
+ \fn void QTextTableCellFormat::setLeftPadding(qreal padding)
+ \since 4.4
+
+ Sets the left \a padding of the table cell.
+
+ \sa leftPadding(), setRightPadding(), setTopPadding(), setBottomPadding()
+*/
+
+/*!
+ \fn void QTextTableCellFormat::setRightPadding(qreal padding)
+ \since 4.4
+
+ Sets the right \a padding of the table cell.
+
+ \sa rightPadding(), setLeftPadding(), setTopPadding(), setBottomPadding()
+*/
+
+/*!
+ \fn void QTextTableCellFormat::setPadding(qreal padding)
+ \since 4.4
+
+ Sets the left, right, top, and bottom \a padding of the table cell.
+
+ \sa setLeftPadding(), setRightPadding(), setTopPadding(), setBottomPadding()
+*/
+
+/*!
+ \fn bool QTextTableCellFormat::isValid() const
+ \since 4.4
+
+ Returns true if this table cell format is valid; otherwise returns false.
+*/
+
+/*!
+ \fn QTextTableCellFormat::QTextTableCellFormat()
+ \since 4.4
+
+ Constructs a new table cell format object.
+*/
+QTextTableCellFormat::QTextTableCellFormat()
+ : QTextCharFormat()
+{
+ setObjectType(TableCellObject);
+}
+
+/*!
+ \internal
+ \fn QTextTableCellFormat::QTextTableCellFormat(const QTextFormat &other)
+
+ Creates a new table cell format with the same attributes as the \a given
+ text format.
+*/
+QTextTableCellFormat::QTextTableCellFormat(const QTextFormat &fmt)
+ : QTextCharFormat(fmt)
+{
+}
+
+/*!
+ \class QTextTableCellFormat
+ \reentrant
+ \since 4.4
+
+ \brief The QTextTableCellFormat class provides formatting information for
+ table cells in a QTextDocument.
+
+ \ingroup text
+
+ The table cell format of a table cell in a document specifies the visual
+ properties of the table cell.
+
+ The padding properties of a table cell are controlled by setLeftPadding(),
+ setRightPadding(), setTopPadding(), and setBottomPadding(). All the paddings
+ can be set at once using setPadding().
+
+ \sa QTextFormat QTextBlockFormat QTextTableFormat QTextCharFormat
+*/
+
+// ------------------------------------------------------
+
+
+QTextFormatCollection::QTextFormatCollection(const QTextFormatCollection &rhs)
+{
+ formats = rhs.formats;
+ objFormats = rhs.objFormats;
+}
+
+QTextFormatCollection &QTextFormatCollection::operator=(const QTextFormatCollection &rhs)
+{
+ formats = rhs.formats;
+ objFormats = rhs.objFormats;
+ return *this;
+}
+
+QTextFormatCollection::~QTextFormatCollection()
+{
+}
+
+int QTextFormatCollection::indexForFormat(const QTextFormat &format)
+{
+ uint hash = format.d ? format.d->hash() : 0;
+ if (hashes.contains(hash)) {
+ for (int i = 0; i < formats.size(); ++i) {
+ if (formats.at(i) == format)
+ return i;
+ }
+ }
+ int idx = formats.size();
+ formats.append(format);
+
+ QTextFormat &f = formats.last();
+ if (!f.d)
+ f.d = new QTextFormatPrivate;
+ f.d->resolveFont(defaultFnt);
+
+ hashes.insert(hash);
+ return idx;
+}
+
+bool QTextFormatCollection::hasFormatCached(const QTextFormat &format) const
+{
+ uint hash = format.d ? format.d->hash() : 0;
+ if (hashes.contains(hash)) {
+ for (int i = 0; i < formats.size(); ++i)
+ if (formats.at(i) == format)
+ return true;
+ }
+ return false;
+}
+
+QTextFormat QTextFormatCollection::objectFormat(int objectIndex) const
+{
+ if (objectIndex == -1)
+ return QTextFormat();
+ return format(objFormats.at(objectIndex));
+}
+
+void QTextFormatCollection::setObjectFormat(int objectIndex, const QTextFormat &f)
+{
+ const int formatIndex = indexForFormat(f);
+ objFormats[objectIndex] = formatIndex;
+}
+
+int QTextFormatCollection::objectFormatIndex(int objectIndex) const
+{
+ if (objectIndex == -1)
+ return -1;
+ return objFormats.at(objectIndex);
+}
+
+void QTextFormatCollection::setObjectFormatIndex(int objectIndex, int formatIndex)
+{
+ objFormats[objectIndex] = formatIndex;
+}
+
+int QTextFormatCollection::createObjectIndex(const QTextFormat &f)
+{
+ const int objectIndex = objFormats.size();
+ objFormats.append(indexForFormat(f));
+ return objectIndex;
+}
+
+QTextFormat QTextFormatCollection::format(int idx) const
+{
+ if (idx < 0 || idx >= formats.count())
+ return QTextFormat();
+
+ return formats.at(idx);
+}
+
+void QTextFormatCollection::setDefaultFont(const QFont &f)
+{
+ defaultFnt = f;
+ for (int i = 0; i < formats.count(); ++i)
+ if (formats[i].d)
+ formats[i].d->resolveFont(defaultFnt);
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/text/qtextformat.h b/src/gui/text/qtextformat.h
new file mode 100644
index 0000000000..0571d75ef2
--- /dev/null
+++ b/src/gui/text/qtextformat.h
@@ -0,0 +1,902 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QTEXTFORMAT_H
+#define QTEXTFORMAT_H
+
+#include <QtGui/qcolor.h>
+#include <QtGui/qfont.h>
+#include <QtCore/qshareddata.h>
+#include <QtCore/qvector.h>
+#include <QtCore/qvariant.h>
+#include <QtGui/qpen.h>
+#include <QtGui/qbrush.h>
+#include <QtGui/qtextoption.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+class QString;
+class QVariant;
+class QFont;
+
+class QTextFormatCollection;
+class QTextFormatPrivate;
+class QTextBlockFormat;
+class QTextCharFormat;
+class QTextListFormat;
+class QTextTableFormat;
+class QTextFrameFormat;
+class QTextImageFormat;
+class QTextTableCellFormat;
+class QTextFormat;
+class QTextObject;
+class QTextCursor;
+class QTextDocument;
+class QTextLength;
+
+Q_GUI_EXPORT QDataStream &operator<<(QDataStream &, const QTextLength &);
+Q_GUI_EXPORT QDataStream &operator>>(QDataStream &, QTextLength &);
+
+class Q_GUI_EXPORT QTextLength
+{
+public:
+ enum Type { VariableLength = 0, FixedLength, PercentageLength };
+
+ inline QTextLength() : lengthType(VariableLength), fixedValueOrPercentage(0) {}
+
+ inline explicit QTextLength(Type type, qreal value);
+
+ inline Type type() const { return lengthType; }
+ inline qreal value(qreal maximumLength) const
+ {
+ switch (lengthType) {
+ case FixedLength: return fixedValueOrPercentage;
+ case VariableLength: return maximumLength;
+ case PercentageLength: return fixedValueOrPercentage * maximumLength / qreal(100);
+ }
+ return -1;
+ }
+
+ inline qreal rawValue() const { return fixedValueOrPercentage; }
+
+ inline bool operator==(const QTextLength &other) const
+ { return lengthType == other.lengthType
+ && qFuzzyCompare(fixedValueOrPercentage, other.fixedValueOrPercentage); }
+ inline bool operator!=(const QTextLength &other) const
+ { return lengthType != other.lengthType
+ || !qFuzzyCompare(fixedValueOrPercentage, other.fixedValueOrPercentage); }
+ operator QVariant() const;
+
+private:
+ Type lengthType;
+ qreal fixedValueOrPercentage;
+ friend Q_GUI_EXPORT QDataStream &operator<<(QDataStream &, const QTextLength &);
+ friend Q_GUI_EXPORT QDataStream &operator>>(QDataStream &, QTextLength &);
+};
+
+inline QTextLength::QTextLength(Type atype, qreal avalue)
+ : lengthType(atype), fixedValueOrPercentage(avalue) {}
+
+Q_GUI_EXPORT QDataStream &operator<<(QDataStream &, const QTextFormat &);
+Q_GUI_EXPORT QDataStream &operator>>(QDataStream &, QTextFormat &);
+
+class Q_GUI_EXPORT QTextFormat
+{
+ Q_GADGET
+ Q_ENUMS(FormatType Property ObjectTypes)
+public:
+ enum FormatType {
+ InvalidFormat = -1,
+ BlockFormat = 1,
+ CharFormat = 2,
+ ListFormat = 3,
+ TableFormat = 4,
+ FrameFormat = 5,
+
+ UserFormat = 100
+ };
+
+ enum Property {
+ ObjectIndex = 0x0,
+
+ // paragraph and char
+ CssFloat = 0x0800,
+ LayoutDirection = 0x0801,
+
+ OutlinePen = 0x810,
+ BackgroundBrush = 0x820,
+ ForegroundBrush = 0x821,
+ // Internal to qtextlayout.cpp: ObjectSelectionBrush = 0x822
+ BackgroundImageUrl = 0x823,
+
+ // paragraph
+ BlockAlignment = 0x1010,
+ BlockTopMargin = 0x1030,
+ BlockBottomMargin = 0x1031,
+ BlockLeftMargin = 0x1032,
+ BlockRightMargin = 0x1033,
+ TextIndent = 0x1034,
+ TabPositions = 0x1035,
+ BlockIndent = 0x1040,
+ BlockNonBreakableLines = 0x1050,
+ BlockTrailingHorizontalRulerWidth = 0x1060,
+
+ // character properties
+ FirstFontProperty = 0x1FE0,
+ FontCapitalization = FirstFontProperty,
+ FontLetterSpacing = 0x1FE1,
+ FontWordSpacing = 0x1FE2,
+ FontStyleHint = 0x1FE3,
+ FontStyleStrategy = 0x1FE4,
+ FontKerning = 0x1FE5,
+ FontFamily = 0x2000,
+ FontPointSize = 0x2001,
+ FontSizeAdjustment = 0x2002,
+ FontSizeIncrement = FontSizeAdjustment, // old name, compat
+ FontWeight = 0x2003,
+ FontItalic = 0x2004,
+ FontUnderline = 0x2005, // deprecated, use TextUnderlineStyle instead
+ FontOverline = 0x2006,
+ FontStrikeOut = 0x2007,
+ FontFixedPitch = 0x2008,
+ FontPixelSize = 0x2009,
+ LastFontProperty = FontPixelSize,
+
+ TextUnderlineColor = 0x2010,
+ TextVerticalAlignment = 0x2021,
+ TextOutline = 0x2022,
+ TextUnderlineStyle = 0x2023,
+ TextToolTip = 0x2024,
+
+ IsAnchor = 0x2030,
+ AnchorHref = 0x2031,
+ AnchorName = 0x2032,
+ ObjectType = 0x2f00,
+
+ // list properties
+ ListStyle = 0x3000,
+ ListIndent = 0x3001,
+
+ // table and frame properties
+ FrameBorder = 0x4000,
+ FrameMargin = 0x4001,
+ FramePadding = 0x4002,
+ FrameWidth = 0x4003,
+ FrameHeight = 0x4004,
+ FrameTopMargin = 0x4005,
+ FrameBottomMargin = 0x4006,
+ FrameLeftMargin = 0x4007,
+ FrameRightMargin = 0x4008,
+ FrameBorderBrush = 0x4009,
+ FrameBorderStyle = 0x4010,
+
+ TableColumns = 0x4100,
+ TableColumnWidthConstraints = 0x4101,
+ TableCellSpacing = 0x4102,
+ TableCellPadding = 0x4103,
+ TableHeaderRowCount = 0x4104,
+
+ // table cell properties
+ TableCellRowSpan = 0x4810,
+ TableCellColumnSpan = 0x4811,
+
+ TableCellTopPadding = 0x4812,
+ TableCellBottomPadding = 0x4813,
+ TableCellLeftPadding = 0x4814,
+ TableCellRightPadding = 0x4815,
+
+ // image properties
+ ImageName = 0x5000,
+ ImageWidth = 0x5010,
+ ImageHeight = 0x5011,
+
+ // selection properties
+ FullWidthSelection = 0x06000,
+
+ // page break properties
+ PageBreakPolicy = 0x7000,
+
+ // --
+ UserProperty = 0x100000
+ };
+
+ enum ObjectTypes {
+ NoObject,
+ ImageObject,
+ TableObject,
+ TableCellObject,
+
+ UserObject = 0x1000
+ };
+
+ enum PageBreakFlag {
+ PageBreak_Auto = 0,
+ PageBreak_AlwaysBefore = 0x001,
+ PageBreak_AlwaysAfter = 0x010
+ // PageBreak_AlwaysInside = 0x100
+ };
+ Q_DECLARE_FLAGS(PageBreakFlags, PageBreakFlag)
+
+ QTextFormat();
+
+ explicit QTextFormat(int type);
+
+ QTextFormat(const QTextFormat &rhs);
+ QTextFormat &operator=(const QTextFormat &rhs);
+ ~QTextFormat();
+
+ void merge(const QTextFormat &other);
+
+ inline bool isValid() const { return type() != InvalidFormat; }
+
+ int type() const;
+
+ int objectIndex() const;
+ void setObjectIndex(int object);
+
+ QVariant property(int propertyId) const;
+ void setProperty(int propertyId, const QVariant &value);
+ void clearProperty(int propertyId);
+ bool hasProperty(int propertyId) const;
+
+ bool boolProperty(int propertyId) const;
+ int intProperty(int propertyId) const;
+ qreal doubleProperty(int propertyId) const;
+ QString stringProperty(int propertyId) const;
+ QColor colorProperty(int propertyId) const;
+ QPen penProperty(int propertyId) const;
+ QBrush brushProperty(int propertyId) const;
+ QTextLength lengthProperty(int propertyId) const;
+ QVector<QTextLength> lengthVectorProperty(int propertyId) const;
+
+ void setProperty(int propertyId, const QVector<QTextLength> &lengths);
+
+ QMap<int, QVariant> properties() const;
+ int propertyCount() const;
+
+ inline void setObjectType(int type);
+ inline int objectType() const
+ { return intProperty(ObjectType); }
+
+ inline bool isCharFormat() const { return type() == CharFormat; }
+ inline bool isBlockFormat() const { return type() == BlockFormat; }
+ inline bool isListFormat() const { return type() == ListFormat; }
+ inline bool isFrameFormat() const { return type() == FrameFormat; }
+ inline bool isImageFormat() const { return type() == CharFormat && objectType() == ImageObject; }
+ inline bool isTableFormat() const { return type() == FrameFormat && objectType() == TableObject; }
+ inline bool isTableCellFormat() const { return type() == CharFormat && objectType() == TableCellObject; }
+
+ QTextBlockFormat toBlockFormat() const;
+ QTextCharFormat toCharFormat() const;
+ QTextListFormat toListFormat() const;
+ QTextTableFormat toTableFormat() const;
+ QTextFrameFormat toFrameFormat() const;
+ QTextImageFormat toImageFormat() const;
+ QTextTableCellFormat toTableCellFormat() const;
+
+ bool operator==(const QTextFormat &rhs) const;
+ inline bool operator!=(const QTextFormat &rhs) const { return !operator==(rhs); }
+ operator QVariant() const;
+
+ inline void setLayoutDirection(Qt::LayoutDirection direction)
+ { setProperty(QTextFormat::LayoutDirection, direction); }
+ inline Qt::LayoutDirection layoutDirection() const
+ { return Qt::LayoutDirection(intProperty(QTextFormat::LayoutDirection)); }
+
+ inline void setBackground(const QBrush &brush)
+ { setProperty(BackgroundBrush, brush); }
+ inline QBrush background() const
+ { return brushProperty(BackgroundBrush); }
+ inline void clearBackground()
+ { clearProperty(BackgroundBrush); }
+
+ inline void setForeground(const QBrush &brush)
+ { setProperty(ForegroundBrush, brush); }
+ inline QBrush foreground() const
+ { return brushProperty(ForegroundBrush); }
+ inline void clearForeground()
+ { clearProperty(ForegroundBrush); }
+
+private:
+ QSharedDataPointer<QTextFormatPrivate> d;
+ qint32 format_type;
+
+ friend class QTextFormatCollection;
+ friend class QTextCharFormat;
+ friend Q_GUI_EXPORT QDataStream &operator<<(QDataStream &, const QTextFormat &);
+ friend Q_GUI_EXPORT QDataStream &operator>>(QDataStream &, QTextFormat &);
+};
+
+inline void QTextFormat::setObjectType(int atype)
+{ setProperty(ObjectType, atype); }
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(QTextFormat::PageBreakFlags)
+
+class Q_GUI_EXPORT QTextCharFormat : public QTextFormat
+{
+public:
+ enum VerticalAlignment {
+ AlignNormal = 0,
+ AlignSuperScript,
+ AlignSubScript,
+ AlignMiddle,
+ AlignTop,
+ AlignBottom
+ };
+ enum UnderlineStyle { // keep in sync with Qt::PenStyle!
+ NoUnderline,
+ SingleUnderline,
+ DashUnderline,
+ DotLine,
+ DashDotLine,
+ DashDotDotLine,
+ WaveUnderline,
+ SpellCheckUnderline
+ };
+
+ QTextCharFormat();
+
+ bool isValid() const { return isCharFormat(); }
+ void setFont(const QFont &font);
+ QFont font() const;
+
+ inline void setFontFamily(const QString &family)
+ { setProperty(FontFamily, family); }
+ inline QString fontFamily() const
+ { return stringProperty(FontFamily); }
+
+ inline void setFontPointSize(qreal size)
+ { setProperty(FontPointSize, size); }
+ inline qreal fontPointSize() const
+ { return doubleProperty(FontPointSize); }
+
+ inline void setFontWeight(int weight)
+ { if (weight == QFont::Normal) weight = 0; setProperty(FontWeight, weight); }
+ inline int fontWeight() const
+ { int weight = intProperty(FontWeight); if (weight == 0) weight = QFont::Normal; return weight; }
+ inline void setFontItalic(bool italic)
+ { setProperty(FontItalic, italic); }
+ inline bool fontItalic() const
+ { return boolProperty(FontItalic); }
+ inline void setFontCapitalization(QFont::Capitalization capitalization)
+ { setProperty(FontCapitalization, capitalization); }
+ inline QFont::Capitalization fontCapitalization() const
+ { return static_cast<QFont::Capitalization>(intProperty(FontCapitalization)); }
+ inline void setFontLetterSpacing(qreal spacing)
+ { setProperty(FontLetterSpacing, spacing); }
+ inline qreal fontLetterSpacing() const
+ { return doubleProperty(FontLetterSpacing); }
+ inline void setFontWordSpacing(qreal spacing)
+ { setProperty(FontWordSpacing, spacing); }
+ inline qreal fontWordSpacing() const
+ { return doubleProperty(FontWordSpacing); }
+
+ inline void setFontUnderline(bool underline)
+ { setProperty(TextUnderlineStyle, underline ? SingleUnderline : NoUnderline); }
+ bool fontUnderline() const;
+
+ inline void setFontOverline(bool overline)
+ { setProperty(FontOverline, overline); }
+ inline bool fontOverline() const
+ { return boolProperty(FontOverline); }
+
+ inline void setFontStrikeOut(bool strikeOut)
+ { setProperty(FontStrikeOut, strikeOut); }
+ inline bool fontStrikeOut() const
+ { return boolProperty(FontStrikeOut); }
+
+ inline void setUnderlineColor(const QColor &color)
+ { setProperty(TextUnderlineColor, color); }
+ inline QColor underlineColor() const
+ { return colorProperty(TextUnderlineColor); }
+
+ inline void setFontFixedPitch(bool fixedPitch)
+ { setProperty(FontFixedPitch, fixedPitch); }
+ inline bool fontFixedPitch() const
+ { return boolProperty(FontFixedPitch); }
+
+ inline void setFontStyleHint(QFont::StyleHint hint, QFont::StyleStrategy strategy = QFont::PreferDefault)
+ { setProperty(FontStyleHint, hint); setProperty(FontStyleStrategy, strategy); }
+ inline void setFontStyleStrategy(QFont::StyleStrategy strategy)
+ { setProperty(FontStyleStrategy, strategy); }
+ QFont::StyleHint fontStyleHint() const
+ { return static_cast<QFont::StyleHint>(intProperty(FontStyleHint)); }
+ QFont::StyleStrategy fontStyleStrategy() const
+ { return static_cast<QFont::StyleStrategy>(intProperty(FontStyleStrategy)); }
+
+ inline void setFontKerning(bool enable)
+ { setProperty(FontKerning, enable); }
+ inline bool fontKerning() const
+ { return boolProperty(FontKerning); }
+
+ void setUnderlineStyle(UnderlineStyle style);
+ inline UnderlineStyle underlineStyle() const
+ { return static_cast<UnderlineStyle>(intProperty(TextUnderlineStyle)); }
+
+ inline void setVerticalAlignment(VerticalAlignment alignment)
+ { setProperty(TextVerticalAlignment, alignment); }
+ inline VerticalAlignment verticalAlignment() const
+ { return static_cast<VerticalAlignment>(intProperty(TextVerticalAlignment)); }
+
+ inline void setTextOutline(const QPen &pen)
+ { setProperty(TextOutline, pen); }
+ inline QPen textOutline() const
+ { return penProperty(TextOutline); }
+
+ inline void setToolTip(const QString &tip)
+ { setProperty(TextToolTip, tip); }
+ inline QString toolTip() const
+ { return stringProperty(TextToolTip); }
+
+ inline void setAnchor(bool anchor)
+ { setProperty(IsAnchor, anchor); }
+ inline bool isAnchor() const
+ { return boolProperty(IsAnchor); }
+
+ inline void setAnchorHref(const QString &value)
+ { setProperty(AnchorHref, value); }
+ inline QString anchorHref() const
+ { return stringProperty(AnchorHref); }
+
+ inline void setAnchorName(const QString &name)
+ { setAnchorNames(QStringList(name)); }
+ QString anchorName() const;
+
+ inline void setAnchorNames(const QStringList &names)
+ { setProperty(AnchorName, names); }
+ QStringList anchorNames() const;
+
+ inline void setTableCellRowSpan(int tableCellRowSpan);
+ inline int tableCellRowSpan() const
+ { int s = intProperty(TableCellRowSpan); if (s == 0) s = 1; return s; }
+ inline void setTableCellColumnSpan(int tableCellColumnSpan);
+ inline int tableCellColumnSpan() const
+ { int s = intProperty(TableCellColumnSpan); if (s == 0) s = 1; return s; }
+
+protected:
+ explicit QTextCharFormat(const QTextFormat &fmt);
+ friend class QTextFormat;
+};
+
+inline void QTextCharFormat::setTableCellRowSpan(int _tableCellRowSpan)
+{
+ if (_tableCellRowSpan <= 1)
+ clearProperty(TableCellRowSpan); // the getter will return 1 here.
+ else
+ setProperty(TableCellRowSpan, _tableCellRowSpan);
+}
+
+inline void QTextCharFormat::setTableCellColumnSpan(int _tableCellColumnSpan)
+{
+ if (_tableCellColumnSpan <= 1)
+ clearProperty(TableCellColumnSpan); // the getter will return 1 here.
+ else
+ setProperty(TableCellColumnSpan, _tableCellColumnSpan);
+}
+
+class Q_GUI_EXPORT QTextBlockFormat : public QTextFormat
+{
+public:
+ QTextBlockFormat();
+
+ bool isValid() const { return isBlockFormat(); }
+
+ inline void setAlignment(Qt::Alignment alignment);
+ inline Qt::Alignment alignment() const
+ { int a = intProperty(BlockAlignment); if (a == 0) a = Qt::AlignLeft; return QFlag(a); }
+
+ inline void setTopMargin(qreal margin)
+ { setProperty(BlockTopMargin, margin); }
+ inline qreal topMargin() const
+ { return doubleProperty(BlockTopMargin); }
+
+ inline void setBottomMargin(qreal margin)
+ { setProperty(BlockBottomMargin, margin); }
+ inline qreal bottomMargin() const
+ { return doubleProperty(BlockBottomMargin); }
+
+ inline void setLeftMargin(qreal margin)
+ { setProperty(BlockLeftMargin, margin); }
+ inline qreal leftMargin() const
+ { return doubleProperty(BlockLeftMargin); }
+
+ inline void setRightMargin(qreal margin)
+ { setProperty(BlockRightMargin, margin); }
+ inline qreal rightMargin() const
+ { return doubleProperty(BlockRightMargin); }
+
+ inline void setTextIndent(qreal aindent)
+ { setProperty(TextIndent, aindent); }
+ inline qreal textIndent() const
+ { return doubleProperty(TextIndent); }
+
+ inline void setIndent(int indent);
+ inline int indent() const
+ { return intProperty(BlockIndent); }
+
+ inline void setNonBreakableLines(bool b)
+ { setProperty(BlockNonBreakableLines, b); }
+ inline bool nonBreakableLines() const
+ { return boolProperty(BlockNonBreakableLines); }
+
+ inline void setPageBreakPolicy(PageBreakFlags flags)
+ { setProperty(PageBreakPolicy, int(flags)); }
+ inline PageBreakFlags pageBreakPolicy() const
+ { return PageBreakFlags(intProperty(PageBreakPolicy)); }
+
+ void setTabPositions(const QList<QTextOption::Tab> &tabs);
+ QList<QTextOption::Tab> tabPositions() const;
+
+protected:
+ explicit QTextBlockFormat(const QTextFormat &fmt);
+ friend class QTextFormat;
+};
+
+inline void QTextBlockFormat::setAlignment(Qt::Alignment aalignment)
+{ setProperty(BlockAlignment, int(aalignment)); }
+
+inline void QTextBlockFormat::setIndent(int aindent)
+{ setProperty(BlockIndent, aindent); }
+
+class Q_GUI_EXPORT QTextListFormat : public QTextFormat
+{
+public:
+ QTextListFormat();
+
+ bool isValid() const { return isListFormat(); }
+
+ enum Style {
+ ListDisc = -1,
+ ListCircle = -2,
+ ListSquare = -3,
+ ListDecimal = -4,
+ ListLowerAlpha = -5,
+ ListUpperAlpha = -6,
+ ListStyleUndefined = 0
+ };
+
+ inline void setStyle(Style style);
+ inline Style style() const
+ { return static_cast<Style>(intProperty(ListStyle)); }
+
+ inline void setIndent(int indent);
+ inline int indent() const
+ { return intProperty(ListIndent); }
+
+protected:
+ explicit QTextListFormat(const QTextFormat &fmt);
+ friend class QTextFormat;
+};
+
+inline void QTextListFormat::setStyle(Style astyle)
+{ setProperty(ListStyle, astyle); }
+
+inline void QTextListFormat::setIndent(int aindent)
+{ setProperty(ListIndent, aindent); }
+
+class Q_GUI_EXPORT QTextImageFormat : public QTextCharFormat
+{
+public:
+ QTextImageFormat();
+
+ bool isValid() const { return isImageFormat(); }
+
+ inline void setName(const QString &name);
+ inline QString name() const
+ { return stringProperty(ImageName); }
+
+ inline void setWidth(qreal width);
+ inline qreal width() const
+ { return doubleProperty(ImageWidth); }
+
+ inline void setHeight(qreal height);
+ inline qreal height() const
+ { return doubleProperty(ImageHeight); }
+
+protected:
+ explicit QTextImageFormat(const QTextFormat &format);
+ friend class QTextFormat;
+};
+
+inline void QTextImageFormat::setName(const QString &aname)
+{ setProperty(ImageName, aname); }
+
+inline void QTextImageFormat::setWidth(qreal awidth)
+{ setProperty(ImageWidth, awidth); }
+
+inline void QTextImageFormat::setHeight(qreal aheight)
+{ setProperty(ImageHeight, aheight); }
+
+class Q_GUI_EXPORT QTextFrameFormat : public QTextFormat
+{
+public:
+ QTextFrameFormat();
+
+ bool isValid() const { return isFrameFormat(); }
+
+ enum Position {
+ InFlow,
+ FloatLeft,
+ FloatRight
+ // ######
+// Absolute
+ };
+
+ enum BorderStyle {
+ BorderStyle_None,
+ BorderStyle_Dotted,
+ BorderStyle_Dashed,
+ BorderStyle_Solid,
+ BorderStyle_Double,
+ BorderStyle_DotDash,
+ BorderStyle_DotDotDash,
+ BorderStyle_Groove,
+ BorderStyle_Ridge,
+ BorderStyle_Inset,
+ BorderStyle_Outset
+ };
+
+ inline void setPosition(Position f)
+ { setProperty(CssFloat, f); }
+ inline Position position() const
+ { return static_cast<Position>(intProperty(CssFloat)); }
+
+ inline void setBorder(qreal border);
+ inline qreal border() const
+ { return doubleProperty(FrameBorder); }
+
+ inline void setBorderBrush(const QBrush &brush)
+ { setProperty(FrameBorderBrush, brush); }
+ inline QBrush borderBrush() const
+ { return brushProperty(FrameBorderBrush); }
+
+ inline void setBorderStyle(BorderStyle style)
+ { setProperty(FrameBorderStyle, style); }
+ inline BorderStyle borderStyle() const
+ { return static_cast<BorderStyle>(intProperty(FrameBorderStyle)); }
+
+ void setMargin(qreal margin);
+ inline qreal margin() const
+ { return doubleProperty(FrameMargin); }
+
+ inline void setTopMargin(qreal margin);
+ qreal topMargin() const;
+
+ inline void setBottomMargin(qreal margin);
+ qreal bottomMargin() const;
+
+ inline void setLeftMargin(qreal margin);
+ qreal leftMargin() const;
+
+ inline void setRightMargin(qreal margin);
+ qreal rightMargin() const;
+
+ inline void setPadding(qreal padding);
+ inline qreal padding() const
+ { return doubleProperty(FramePadding); }
+
+ inline void setWidth(qreal width);
+ inline void setWidth(const QTextLength &length)
+ { setProperty(FrameWidth, length); }
+ inline QTextLength width() const
+ { return lengthProperty(FrameWidth); }
+
+ inline void setHeight(qreal height);
+ inline void setHeight(const QTextLength &height);
+ inline QTextLength height() const
+ { return lengthProperty(FrameHeight); }
+
+ inline void setPageBreakPolicy(PageBreakFlags flags)
+ { setProperty(PageBreakPolicy, int(flags)); }
+ inline PageBreakFlags pageBreakPolicy() const
+ { return PageBreakFlags(intProperty(PageBreakPolicy)); }
+
+protected:
+ explicit QTextFrameFormat(const QTextFormat &fmt);
+ friend class QTextFormat;
+};
+
+inline void QTextFrameFormat::setBorder(qreal aborder)
+{ setProperty(FrameBorder, aborder); }
+
+inline void QTextFrameFormat::setPadding(qreal apadding)
+{ setProperty(FramePadding, apadding); }
+
+inline void QTextFrameFormat::setWidth(qreal awidth)
+{ setProperty(FrameWidth, QTextLength(QTextLength::FixedLength, awidth)); }
+
+inline void QTextFrameFormat::setHeight(qreal aheight)
+{ setProperty(FrameHeight, QTextLength(QTextLength::FixedLength, aheight)); }
+inline void QTextFrameFormat::setHeight(const QTextLength &aheight)
+{ setProperty(FrameHeight, aheight); }
+
+inline void QTextFrameFormat::setTopMargin(qreal amargin)
+{ setProperty(FrameTopMargin, amargin); }
+
+inline void QTextFrameFormat::setBottomMargin(qreal amargin)
+{ setProperty(FrameBottomMargin, amargin); }
+
+inline void QTextFrameFormat::setLeftMargin(qreal amargin)
+{ setProperty(FrameLeftMargin, amargin); }
+
+inline void QTextFrameFormat::setRightMargin(qreal amargin)
+{ setProperty(FrameRightMargin, amargin); }
+
+class Q_GUI_EXPORT QTextTableFormat : public QTextFrameFormat
+{
+public:
+ QTextTableFormat();
+
+ inline bool isValid() const { return isTableFormat(); }
+
+ inline int columns() const
+ { int cols = intProperty(TableColumns); if (cols == 0) cols = 1; return cols; }
+ inline void setColumns(int columns);
+
+ inline void setColumnWidthConstraints(const QVector<QTextLength> &constraints)
+ { setProperty(TableColumnWidthConstraints, constraints); }
+
+ inline QVector<QTextLength> columnWidthConstraints() const
+ { return lengthVectorProperty(TableColumnWidthConstraints); }
+
+ inline void clearColumnWidthConstraints()
+ { clearProperty(TableColumnWidthConstraints); }
+
+ inline qreal cellSpacing() const
+ { return doubleProperty(TableCellSpacing); }
+ inline void setCellSpacing(qreal spacing)
+ { setProperty(TableCellSpacing, spacing); }
+
+ inline qreal cellPadding() const
+ { return doubleProperty(TableCellPadding); }
+ inline void setCellPadding(qreal padding);
+
+ inline void setAlignment(Qt::Alignment alignment);
+ inline Qt::Alignment alignment() const
+ { return QFlag(intProperty(BlockAlignment)); }
+
+ inline void setHeaderRowCount(int count)
+ { setProperty(TableHeaderRowCount, count); }
+ inline int headerRowCount() const
+ { return intProperty(TableHeaderRowCount); }
+
+protected:
+ explicit QTextTableFormat(const QTextFormat &fmt);
+ friend class QTextFormat;
+};
+
+inline void QTextTableFormat::setColumns(int acolumns)
+{
+ if (acolumns == 1)
+ acolumns = 0;
+ setProperty(TableColumns, acolumns);
+}
+
+inline void QTextTableFormat::setCellPadding(qreal apadding)
+{ setProperty(TableCellPadding, apadding); }
+
+inline void QTextTableFormat::setAlignment(Qt::Alignment aalignment)
+{ setProperty(BlockAlignment, int(aalignment)); }
+
+class Q_GUI_EXPORT QTextTableCellFormat : public QTextCharFormat
+{
+public:
+ QTextTableCellFormat();
+
+ inline bool isValid() const { return isTableCellFormat(); }
+
+ inline void setTopPadding(qreal padding);
+ inline qreal topPadding() const;
+
+ inline void setBottomPadding(qreal padding);
+ inline qreal bottomPadding() const;
+
+ inline void setLeftPadding(qreal padding);
+ inline qreal leftPadding() const;
+
+ inline void setRightPadding(qreal padding);
+ inline qreal rightPadding() const;
+
+ inline void setPadding(qreal padding);
+
+protected:
+ explicit QTextTableCellFormat(const QTextFormat &fmt);
+ friend class QTextFormat;
+};
+
+inline void QTextTableCellFormat::setTopPadding(qreal padding)
+{
+ setProperty(TableCellTopPadding, padding);
+}
+
+inline qreal QTextTableCellFormat::topPadding() const
+{
+ return doubleProperty(TableCellTopPadding);
+}
+
+inline void QTextTableCellFormat::setBottomPadding(qreal padding)
+{
+ setProperty(TableCellBottomPadding, padding);
+}
+
+inline qreal QTextTableCellFormat::bottomPadding() const
+{
+ return doubleProperty(TableCellBottomPadding);
+}
+
+inline void QTextTableCellFormat::setLeftPadding(qreal padding)
+{
+ setProperty(TableCellLeftPadding, padding);
+}
+
+inline qreal QTextTableCellFormat::leftPadding() const
+{
+ return doubleProperty(TableCellLeftPadding);
+}
+
+inline void QTextTableCellFormat::setRightPadding(qreal padding)
+{
+ setProperty(TableCellRightPadding, padding);
+}
+
+inline qreal QTextTableCellFormat::rightPadding() const
+{
+ return doubleProperty(TableCellRightPadding);
+}
+
+inline void QTextTableCellFormat::setPadding(qreal padding)
+{
+ setTopPadding(padding);
+ setBottomPadding(padding);
+ setLeftPadding(padding);
+ setRightPadding(padding);
+}
+
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QTEXTFORMAT_H
diff --git a/src/gui/text/qtextformat_p.h b/src/gui/text/qtextformat_p.h
new file mode 100644
index 0000000000..115bc80a00
--- /dev/null
+++ b/src/gui/text/qtextformat_p.h
@@ -0,0 +1,111 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QTEXTFORMAT_P_H
+#define QTEXTFORMAT_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 "QtGui/qtextformat.h"
+#include "QtCore/qvector.h"
+#include "QtCore/qset.h"
+
+QT_BEGIN_NAMESPACE
+
+class Q_GUI_EXPORT QTextFormatCollection
+{
+public:
+ QTextFormatCollection() {}
+ ~QTextFormatCollection();
+
+ QTextFormatCollection(const QTextFormatCollection &rhs);
+ QTextFormatCollection &operator=(const QTextFormatCollection &rhs);
+
+ QTextFormat objectFormat(int objectIndex) const;
+ void setObjectFormat(int objectIndex, const QTextFormat &format);
+
+ int objectFormatIndex(int objectIndex) const;
+ void setObjectFormatIndex(int objectIndex, int formatIndex);
+
+ int createObjectIndex(const QTextFormat &f);
+
+ int indexForFormat(const QTextFormat &f);
+ bool hasFormatCached(const QTextFormat &format) const;
+
+ QTextFormat format(int idx) const;
+ inline QTextBlockFormat blockFormat(int index) const
+ { return format(index).toBlockFormat(); }
+ inline QTextCharFormat charFormat(int index) const
+ { return format(index).toCharFormat(); }
+ inline QTextListFormat listFormat(int index) const
+ { return format(index).toListFormat(); }
+ inline QTextTableFormat tableFormat(int index) const
+ { return format(index).toTableFormat(); }
+ inline QTextImageFormat imageFormat(int index) const
+ { return format(index).toImageFormat(); }
+
+ inline int numFormats() const { return formats.count(); }
+
+ typedef QVector<QTextFormat> FormatVector;
+
+ FormatVector formats;
+ QVector<qint32> objFormats;
+ QSet<uint> hashes;
+
+ inline QFont defaultFont() const { return defaultFnt; }
+ void setDefaultFont(const QFont &f);
+
+private:
+ QFont defaultFnt;
+};
+
+QT_END_NAMESPACE
+
+#endif // QTEXTFORMAT_P_H
diff --git a/src/gui/text/qtexthtmlparser.cpp b/src/gui/text/qtexthtmlparser.cpp
new file mode 100644
index 0000000000..b1f1b75bfa
--- /dev/null
+++ b/src/gui/text/qtexthtmlparser.cpp
@@ -0,0 +1,1881 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qtexthtmlparser_p.h"
+
+#include <qbytearray.h>
+#include <qtextcodec.h>
+#include <qapplication.h>
+#include <qstack.h>
+#include <qdebug.h>
+#include <qthread.h>
+
+#include "qtextdocument.h"
+#include "qtextformat_p.h"
+#include "qtextdocument_p.h"
+#include "qtextcursor.h"
+#include "qfont_p.h"
+#include "private/qunicodetables_p.h"
+#include "private/qfunctions_p.h"
+
+#ifndef QT_NO_TEXTHTMLPARSER
+
+QT_BEGIN_NAMESPACE
+
+// see also tst_qtextdocumentfragment.cpp
+#define MAX_ENTITY 258
+static const struct QTextHtmlEntity { const char *name; quint16 code; } entities[MAX_ENTITY]= {
+ { "AElig", 0x00c6 },
+ { "AMP", 38 },
+ { "Aacute", 0x00c1 },
+ { "Acirc", 0x00c2 },
+ { "Agrave", 0x00c0 },
+ { "Alpha", 0x0391 },
+ { "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 },
+ { "GT", 62 },
+ { "Gamma", 0x0393 },
+ { "Iacute", 0x00cd },
+ { "Icirc", 0x00ce },
+ { "Igrave", 0x00cc },
+ { "Iota", 0x0399 },
+ { "Iuml", 0x00cf },
+ { "Kappa", 0x039a },
+ { "LT", 60 },
+ { "Lambda", 0x039b },
+ { "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 },
+ { "curren", 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 },
+ { "sup", 0x2283 },
+ { "sup1", 0x00b9 },
+ { "sup2", 0x00b2 },
+ { "sup3", 0x00b3 },
+ { "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 }
+};
+
+Q_STATIC_GLOBAL_OPERATOR bool operator<(const QString &entityStr, const QTextHtmlEntity &entity)
+{
+ return entityStr < QLatin1String(entity.name);
+}
+
+Q_STATIC_GLOBAL_OPERATOR bool operator<(const QTextHtmlEntity &entity, const QString &entityStr)
+{
+ return QLatin1String(entity.name) < entityStr;
+}
+
+static QChar resolveEntity(const QString &entity)
+{
+ const QTextHtmlEntity *start = &entities[0];
+ const QTextHtmlEntity *end = &entities[MAX_ENTITY];
+ const QTextHtmlEntity *e = qBinaryFind(start, end, entity);
+ if (e == end)
+ return QChar();
+ return e->code;
+}
+
+static const uint windowsLatin1ExtendedCharacters[0xA0 - 0x80] = {
+ 0x20ac, // 0x80
+ 0x0081, // 0x81 direct mapping
+ 0x201a, // 0x82
+ 0x0192, // 0x83
+ 0x201e, // 0x84
+ 0x2026, // 0x85
+ 0x2020, // 0x86
+ 0x2021, // 0x87
+ 0x02C6, // 0x88
+ 0x2030, // 0x89
+ 0x0160, // 0x8A
+ 0x2039, // 0x8B
+ 0x0152, // 0x8C
+ 0x008D, // 0x8D direct mapping
+ 0x017D, // 0x8E
+ 0x008F, // 0x8F directmapping
+ 0x0090, // 0x90 directmapping
+ 0x2018, // 0x91
+ 0x2019, // 0x92
+ 0x201C, // 0x93
+ 0X201D, // 0x94
+ 0x2022, // 0x95
+ 0x2013, // 0x96
+ 0x2014, // 0x97
+ 0x02DC, // 0x98
+ 0x2122, // 0x99
+ 0x0161, // 0x9A
+ 0x203A, // 0x9B
+ 0x0153, // 0x9C
+ 0x009D, // 0x9D direct mapping
+ 0x017E, // 0x9E
+ 0x0178 // 0x9F
+};
+
+// the displayMode value is according to the what are blocks in the piecetable, not
+// what the w3c defines.
+static const QTextHtmlElement elements[Html_NumElements]= {
+ { "a", Html_a, QTextHtmlElement::DisplayInline },
+ { "address", Html_address, QTextHtmlElement::DisplayInline },
+ { "b", Html_b, QTextHtmlElement::DisplayInline },
+ { "big", Html_big, QTextHtmlElement::DisplayInline },
+ { "blockquote", Html_blockquote, QTextHtmlElement::DisplayBlock },
+ { "body", Html_body, QTextHtmlElement::DisplayBlock },
+ { "br", Html_br, QTextHtmlElement::DisplayInline },
+ { "caption", Html_caption, QTextHtmlElement::DisplayBlock },
+ { "center", Html_center, QTextHtmlElement::DisplayBlock },
+ { "cite", Html_cite, QTextHtmlElement::DisplayInline },
+ { "code", Html_code, QTextHtmlElement::DisplayInline },
+ { "dd", Html_dd, QTextHtmlElement::DisplayBlock },
+ { "dfn", Html_dfn, QTextHtmlElement::DisplayInline },
+ { "div", Html_div, QTextHtmlElement::DisplayBlock },
+ { "dl", Html_dl, QTextHtmlElement::DisplayBlock },
+ { "dt", Html_dt, QTextHtmlElement::DisplayBlock },
+ { "em", Html_em, QTextHtmlElement::DisplayInline },
+ { "font", Html_font, QTextHtmlElement::DisplayInline },
+ { "h1", Html_h1, QTextHtmlElement::DisplayBlock },
+ { "h2", Html_h2, QTextHtmlElement::DisplayBlock },
+ { "h3", Html_h3, QTextHtmlElement::DisplayBlock },
+ { "h4", Html_h4, QTextHtmlElement::DisplayBlock },
+ { "h5", Html_h5, QTextHtmlElement::DisplayBlock },
+ { "h6", Html_h6, QTextHtmlElement::DisplayBlock },
+ { "head", Html_head, QTextHtmlElement::DisplayNone },
+ { "hr", Html_hr, QTextHtmlElement::DisplayBlock },
+ { "html", Html_html, QTextHtmlElement::DisplayInline },
+ { "i", Html_i, QTextHtmlElement::DisplayInline },
+ { "img", Html_img, QTextHtmlElement::DisplayInline },
+ { "kbd", Html_kbd, QTextHtmlElement::DisplayInline },
+ { "li", Html_li, QTextHtmlElement::DisplayBlock },
+ { "link", Html_link, QTextHtmlElement::DisplayNone },
+ { "meta", Html_meta, QTextHtmlElement::DisplayNone },
+ { "nobr", Html_nobr, QTextHtmlElement::DisplayInline },
+ { "ol", Html_ol, QTextHtmlElement::DisplayBlock },
+ { "p", Html_p, QTextHtmlElement::DisplayBlock },
+ { "pre", Html_pre, QTextHtmlElement::DisplayBlock },
+ { "qt", Html_body /*deliberate mapping*/, QTextHtmlElement::DisplayBlock },
+ { "s", Html_s, QTextHtmlElement::DisplayInline },
+ { "samp", Html_samp, QTextHtmlElement::DisplayInline },
+ { "script", Html_script, QTextHtmlElement::DisplayNone },
+ { "small", Html_small, QTextHtmlElement::DisplayInline },
+ { "span", Html_span, QTextHtmlElement::DisplayInline },
+ { "strong", Html_strong, QTextHtmlElement::DisplayInline },
+ { "style", Html_style, QTextHtmlElement::DisplayNone },
+ { "sub", Html_sub, QTextHtmlElement::DisplayInline },
+ { "sup", Html_sup, QTextHtmlElement::DisplayInline },
+ { "table", Html_table, QTextHtmlElement::DisplayTable },
+ { "tbody", Html_tbody, QTextHtmlElement::DisplayTable },
+ { "td", Html_td, QTextHtmlElement::DisplayBlock },
+ { "tfoot", Html_tfoot, QTextHtmlElement::DisplayTable },
+ { "th", Html_th, QTextHtmlElement::DisplayBlock },
+ { "thead", Html_thead, QTextHtmlElement::DisplayTable },
+ { "title", Html_title, QTextHtmlElement::DisplayNone },
+ { "tr", Html_tr, QTextHtmlElement::DisplayTable },
+ { "tt", Html_tt, QTextHtmlElement::DisplayInline },
+ { "u", Html_u, QTextHtmlElement::DisplayInline },
+ { "ul", Html_ul, QTextHtmlElement::DisplayBlock },
+ { "var", Html_var, QTextHtmlElement::DisplayInline },
+};
+
+
+Q_STATIC_GLOBAL_OPERATOR bool operator<(const QString &str, const QTextHtmlElement &e)
+{
+ return str < QLatin1String(e.name);
+}
+
+Q_STATIC_GLOBAL_OPERATOR bool operator<(const QTextHtmlElement &e, const QString &str)
+{
+ return QLatin1String(e.name) < str;
+}
+
+static const QTextHtmlElement *lookupElementHelper(const QString &element)
+{
+ const QTextHtmlElement *start = &elements[0];
+ const QTextHtmlElement *end = &elements[Html_NumElements];
+ const QTextHtmlElement *e = qBinaryFind(start, end, element);
+ if (e == end)
+ return 0;
+ return e;
+}
+
+int QTextHtmlParser::lookupElement(const QString &element)
+{
+ const QTextHtmlElement *e = lookupElementHelper(element);
+ if (!e)
+ return -1;
+ return e->id;
+}
+
+// quotes newlines as "\\n"
+static QString quoteNewline(const QString &s)
+{
+ QString n = s;
+ n.replace(QLatin1Char('\n'), QLatin1String("\\n"));
+ return n;
+}
+
+QTextHtmlParserNode::QTextHtmlParserNode()
+ : parent(0), id(Html_unknown),
+ cssFloat(QTextFrameFormat::InFlow), hasOwnListStyle(false),
+ hasCssListIndent(false), isEmptyParagraph(false), isTextFrame(false), isRootFrame(false),
+ displayMode(QTextHtmlElement::DisplayInline), hasHref(false),
+ listStyle(QTextListFormat::ListStyleUndefined), imageWidth(-1), imageHeight(-1), tableBorder(0),
+ tableCellRowSpan(1), tableCellColSpan(1), tableCellSpacing(2), tableCellPadding(0),
+ borderBrush(Qt::darkGray), borderStyle(QTextFrameFormat::BorderStyle_Outset),
+ userState(-1), cssListIndent(0), wsm(WhiteSpaceModeUndefined)
+{
+ margin[QTextHtmlParser::MarginLeft] = 0;
+ margin[QTextHtmlParser::MarginRight] = 0;
+ margin[QTextHtmlParser::MarginTop] = 0;
+ margin[QTextHtmlParser::MarginBottom] = 0;
+}
+
+void QTextHtmlParser::dumpHtml()
+{
+ for (int i = 0; i < count(); ++i) {
+ qDebug().nospace() << qPrintable(QString(depth(i)*4, QLatin1Char(' ')))
+ << qPrintable(at(i).tag) << ":"
+ << quoteNewline(at(i).text);
+ ;
+ }
+}
+
+QTextHtmlParserNode *QTextHtmlParser::newNode(int parent)
+{
+ QTextHtmlParserNode *lastNode = &nodes.last();
+ QTextHtmlParserNode *newNode = 0;
+
+ bool reuseLastNode = true;
+
+ if (nodes.count() == 1) {
+ reuseLastNode = false;
+ } else if (lastNode->tag.isEmpty()) {
+
+ if (lastNode->text.isEmpty()) {
+ reuseLastNode = true;
+ } else { // last node is a text node (empty tag) with some text
+
+ if (lastNode->text.length() == 1 && lastNode->text.at(0).isSpace()) {
+
+ int lastSibling = count() - 2;
+ while (lastSibling
+ && at(lastSibling).parent != lastNode->parent
+ && at(lastSibling).displayMode == QTextHtmlElement::DisplayInline) {
+ lastSibling = at(lastSibling).parent;
+ }
+
+ if (at(lastSibling).displayMode == QTextHtmlElement::DisplayInline) {
+ reuseLastNode = false;
+ } else {
+ reuseLastNode = true;
+ }
+ } else {
+ // text node with real (non-whitespace) text -> nothing to re-use
+ reuseLastNode = false;
+ }
+
+ }
+
+ } else {
+ // last node had a proper tag -> nothing to re-use
+ reuseLastNode = false;
+ }
+
+ if (reuseLastNode) {
+ newNode = lastNode;
+ newNode->tag.clear();
+ newNode->text.clear();
+ newNode->id = Html_unknown;
+ } else {
+ nodes.resize(nodes.size() + 1);
+ newNode = &nodes.last();
+ }
+
+ newNode->parent = parent;
+ return newNode;
+}
+
+void QTextHtmlParser::parse(const QString &text, const QTextDocument *_resourceProvider)
+{
+ nodes.clear();
+ nodes.resize(1);
+ txt = text;
+ pos = 0;
+ len = txt.length();
+ textEditMode = false;
+ resourceProvider = _resourceProvider;
+ parse();
+ //dumpHtml();
+}
+
+int QTextHtmlParser::depth(int i) const
+{
+ int depth = 0;
+ while (i) {
+ i = at(i).parent;
+ ++depth;
+ }
+ return depth;
+}
+
+int QTextHtmlParser::margin(int i, int mar) const {
+ int m = 0;
+ const QTextHtmlParserNode *node;
+ if (mar == MarginLeft
+ || mar == MarginRight) {
+ while (i) {
+ node = &at(i);
+ if (!node->isBlock() && node->id != Html_table)
+ break;
+ if (node->isTableCell())
+ break;
+ m += node->margin[mar];
+ i = node->parent;
+ }
+ }
+ return m;
+}
+
+int QTextHtmlParser::topMargin(int i) const
+{
+ if (!i)
+ return 0;
+ return at(i).margin[MarginTop];
+}
+
+int QTextHtmlParser::bottomMargin(int i) const
+{
+ if (!i)
+ return 0;
+ return at(i).margin[MarginBottom];
+}
+
+void QTextHtmlParser::eatSpace()
+{
+ while (pos < len && txt.at(pos).isSpace() && txt.at(pos) != QChar::ParagraphSeparator)
+ pos++;
+}
+
+void QTextHtmlParser::parse()
+{
+ while (pos < len) {
+ QChar c = txt.at(pos++);
+ if (c == QLatin1Char('<')) {
+ parseTag();
+ } else if (c == QLatin1Char('&')) {
+ nodes.last().text += parseEntity();
+ } else {
+ nodes.last().text += c;
+ }
+ }
+}
+
+// parses a tag after "<"
+void QTextHtmlParser::parseTag()
+{
+ eatSpace();
+
+ // handle comments and other exclamation mark declarations
+ if (hasPrefix(QLatin1Char('!'))) {
+ parseExclamationTag();
+ if (nodes.last().wsm != QTextHtmlParserNode::WhiteSpacePre
+ && nodes.last().wsm != QTextHtmlParserNode::WhiteSpacePreWrap
+ && !textEditMode)
+ eatSpace();
+ return;
+ }
+
+ // if close tag just close
+ if (hasPrefix(QLatin1Char('/'))) {
+ if (nodes.last().id == Html_style) {
+#ifndef QT_NO_CSSPARSER
+ QCss::Parser parser(nodes.last().text);
+ QCss::StyleSheet sheet;
+ sheet.origin = QCss::StyleSheetOrigin_Author;
+ parser.parse(&sheet, Qt::CaseInsensitive);
+ inlineStyleSheets.append(sheet);
+ resolveStyleSheetImports(sheet);
+#endif
+ }
+ parseCloseTag();
+ return;
+ }
+
+ int p = last();
+ while (p && at(p).tag.size() == 0)
+ p = at(p).parent;
+
+ QTextHtmlParserNode *node = newNode(p);
+
+ // parse tag name
+ node->tag = parseWord().toLower();
+
+ const QTextHtmlElement *elem = lookupElementHelper(node->tag);
+ if (elem) {
+ node->id = elem->id;
+ node->displayMode = elem->displayMode;
+ } else {
+ node->id = Html_unknown;
+ }
+
+ node->attributes.clear();
+ // _need_ at least one space after the tag name, otherwise there can't be attributes
+ if (pos < len && txt.at(pos).isSpace())
+ node->attributes = parseAttributes();
+
+ // resolveParent() may have to change the order in the tree and
+ // insert intermediate nodes for buggy HTML, so re-initialize the 'node'
+ // pointer through the return value
+ node = resolveParent();
+ resolveNode();
+
+ const int nodeIndex = nodes.count() - 1; // this new node is always the last
+#ifndef QT_NO_CSSPARSER
+ node->applyCssDeclarations(declarationsForNode(nodeIndex), resourceProvider);
+#endif
+ applyAttributes(node->attributes);
+
+ // finish tag
+ bool tagClosed = false;
+ while (pos < len && txt.at(pos) != QLatin1Char('>')) {
+ if (txt.at(pos) == QLatin1Char('/'))
+ tagClosed = true;
+
+ pos++;
+ }
+ pos++;
+
+ // in a white-space preserving environment strip off a initial newline
+ // since the element itself already generates a newline
+ if ((node->wsm == QTextHtmlParserNode::WhiteSpacePre
+ || node->wsm == QTextHtmlParserNode::WhiteSpacePreWrap)
+ && node->isBlock()) {
+ if (pos < len - 1 && txt.at(pos) == QLatin1Char('\n'))
+ ++pos;
+ }
+
+ if (node->mayNotHaveChildren() || tagClosed) {
+ newNode(node->parent);
+ resolveNode();
+ }
+}
+
+// parses a tag beginning with "/"
+void QTextHtmlParser::parseCloseTag()
+{
+ ++pos;
+ QString tag = parseWord().toLower().trimmed();
+ while (pos < len) {
+ QChar c = txt.at(pos++);
+ if (c == QLatin1Char('>'))
+ break;
+ }
+
+ // find corresponding open node
+ int p = last();
+ if (p > 0
+ && at(p - 1).tag == tag
+ && at(p - 1).mayNotHaveChildren())
+ p--;
+
+ while (p && at(p).tag != tag)
+ p = at(p).parent;
+
+ // simply ignore the tag if we can't find
+ // a corresponding open node, for broken
+ // html such as <font>blah</font></font>
+ if (!p)
+ return;
+
+ // in a white-space preserving environment strip off a trailing newline
+ // since the closing of the opening block element will automatically result
+ // in a new block for elements following the <pre>
+ // ...foo\n</pre><p>blah -> foo</pre><p>blah
+ if ((at(p).wsm == QTextHtmlParserNode::WhiteSpacePre
+ || at(p).wsm == QTextHtmlParserNode::WhiteSpacePreWrap)
+ && at(p).isBlock()) {
+ if (at(last()).text.endsWith(QLatin1Char('\n')))
+ nodes[last()].text.chop(1);
+ }
+
+ newNode(at(p).parent);
+ resolveNode();
+}
+
+// parses a tag beginning with "!"
+void QTextHtmlParser::parseExclamationTag()
+{
+ ++pos;
+ if (hasPrefix(QLatin1Char('-'),1) && hasPrefix(QLatin1Char('-'),2)) {
+ pos += 3;
+ // eat comments
+ int end = txt.indexOf(QLatin1String("-->"), pos);
+ pos = (end >= 0 ? end + 3 : len);
+ } else {
+ // eat internal tags
+ while (pos < len) {
+ QChar c = txt.at(pos++);
+ if (c == QLatin1Char('>'))
+ break;
+ }
+ }
+}
+
+// parses an entity after "&", and returns it
+QString QTextHtmlParser::parseEntity()
+{
+ int recover = pos;
+ QString entity;
+ while (pos < len) {
+ QChar c = txt.at(pos++);
+ if (c.isSpace() || pos - recover > 9) {
+ goto error;
+ }
+ if (c == QLatin1Char(';'))
+ break;
+ entity += c;
+ }
+ {
+ QChar resolved = resolveEntity(entity);
+ if (!resolved.isNull())
+ return QString(resolved);
+ }
+ if (entity.length() > 1 && entity.at(0) == QLatin1Char('#')) {
+ entity.remove(0, 1); // removing leading #
+
+ int base = 10;
+ bool ok = false;
+
+ if (entity.at(0).toLower() == QLatin1Char('x')) { // hex entity?
+ entity.remove(0, 1);
+ base = 16;
+ }
+
+ uint uc = entity.toUInt(&ok, base);
+ if (ok) {
+ if (uc >= 0x80 && uc < 0x80 + (sizeof(windowsLatin1ExtendedCharacters)/sizeof(windowsLatin1ExtendedCharacters[0])))
+ uc = windowsLatin1ExtendedCharacters[uc - 0x80];
+ QString str;
+ if (uc > 0xffff) {
+ // surrogate pair
+ uc -= 0x10000;
+ ushort high = uc/0x400 + 0xd800;
+ ushort low = uc%0x400 + 0xdc00;
+ str.append(QChar(high));
+ str.append(QChar(low));
+ } else {
+ str.append(QChar(uc));
+ }
+ return str;
+ }
+ }
+error:
+ pos = recover;
+ return QLatin1String("&");
+}
+
+// parses one word, possibly quoted, and returns it
+QString QTextHtmlParser::parseWord()
+{
+ QString word;
+ if (hasPrefix(QLatin1Char('\"'))) { // double quotes
+ ++pos;
+ while (pos < len) {
+ QChar c = txt.at(pos++);
+ if (c == QLatin1Char('\"'))
+ break;
+ else if (c == QLatin1Char('&'))
+ word += parseEntity();
+ else
+ word += c;
+ }
+ } else if (hasPrefix(QLatin1Char('\''))) { // single quotes
+ ++pos;
+ while (pos < len) {
+ QChar c = txt.at(pos++);
+ if (c == QLatin1Char('\''))
+ break;
+ else
+ word += c;
+ }
+ } else { // normal text
+ while (pos < len) {
+ QChar c = txt.at(pos++);
+ if (c == QLatin1Char('>')
+ || (c == QLatin1Char('/') && hasPrefix(QLatin1Char('>'), 1))
+ || c == QLatin1Char('<')
+ || c == QLatin1Char('=')
+ || c.isSpace()) {
+ --pos;
+ break;
+ }
+ if (c == QLatin1Char('&'))
+ word += parseEntity();
+ else
+ word += c;
+ }
+ }
+ return word;
+}
+
+// gives the new node the right parent
+QTextHtmlParserNode *QTextHtmlParser::resolveParent()
+{
+ QTextHtmlParserNode *node = &nodes.last();
+
+ int p = node->parent;
+
+ // Excel gives us buggy HTML with just tr without surrounding table tags
+ // or with just td tags
+
+ if (node->id == Html_td) {
+ int n = p;
+ while (n && at(n).id != Html_tr)
+ n = at(n).parent;
+
+ if (!n) {
+ nodes.insert(nodes.count() - 1, QTextHtmlParserNode());
+ nodes.insert(nodes.count() - 1, QTextHtmlParserNode());
+
+ QTextHtmlParserNode *table = &nodes[nodes.count() - 3];
+ table->parent = p;
+ table->id = Html_table;
+ table->tag = QLatin1String("table");
+ table->children.append(nodes.count() - 2); // add row as child
+
+ QTextHtmlParserNode *row = &nodes[nodes.count() - 2];
+ row->parent = nodes.count() - 3; // table as parent
+ row->id = Html_tr;
+ row->tag = QLatin1String("tr");
+
+ p = nodes.count() - 2;
+ node = &nodes.last(); // re-initialize pointer
+ }
+ }
+
+ if (node->id == Html_tr) {
+ int n = p;
+ while (n && at(n).id != Html_table)
+ n = at(n).parent;
+
+ if (!n) {
+ nodes.insert(nodes.count() - 1, QTextHtmlParserNode());
+ QTextHtmlParserNode *table = &nodes[nodes.count() - 2];
+ table->parent = p;
+ table->id = Html_table;
+ table->tag = QLatin1String("table");
+ p = nodes.count() - 2;
+ node = &nodes.last(); // re-initialize pointer
+ }
+ }
+
+ // permit invalid html by letting block elements be children
+ // of inline elements with the exception of paragraphs:
+ //
+ // a new paragraph closes parent inline elements (while loop),
+ // unless they themselves are children of a non-paragraph block
+ // element (if statement)
+ //
+ // For example:
+ //
+ // <body><p><b>Foo<p>Bar <-- second <p> implicitly closes <b> that
+ // belongs to the first <p>. The self-nesting
+ // check further down prevents the second <p>
+ // from nesting into the first one then.
+ // so Bar is not bold.
+ //
+ // <body><b><p>Foo <-- Foo should be bold.
+ //
+ // <body><b><p>Foo<p>Bar <-- Foo and Bar should be bold.
+ //
+ if (node->id == Html_p) {
+ while (p && !at(p).isBlock())
+ p = at(p).parent;
+
+ if (!p || at(p).id != Html_p)
+ p = node->parent;
+ }
+
+ // some elements are not self nesting
+ if (node->id == at(p).id
+ && node->isNotSelfNesting())
+ p = at(p).parent;
+
+ // some elements are not allowed in certain contexts
+ while ((p && !node->allowedInContext(at(p).id))
+ // ### make new styles aware of empty tags
+ || at(p).mayNotHaveChildren()
+ ) {
+ p = at(p).parent;
+ }
+
+ node->parent = p;
+
+ // makes it easier to traverse the tree, later
+ nodes[p].children.append(nodes.count() - 1);
+ return node;
+}
+
+// sets all properties on the new node
+void QTextHtmlParser::resolveNode()
+{
+ QTextHtmlParserNode *node = &nodes.last();
+ const QTextHtmlParserNode *parent = &nodes.at(node->parent);
+ node->initializeProperties(parent, this);
+}
+
+bool QTextHtmlParserNode::isNestedList(const QTextHtmlParser *parser) const
+{
+ if (!isListStart())
+ return false;
+
+ int p = parent;
+ while (p) {
+ if (parser->at(p).isListStart())
+ return true;
+ p = parser->at(p).parent;
+ }
+ return false;
+}
+
+void QTextHtmlParserNode::initializeProperties(const QTextHtmlParserNode *parent, const QTextHtmlParser *parser)
+{
+ // inherit properties from parent element
+ charFormat = parent->charFormat;
+
+ if (id == Html_html)
+ blockFormat.setLayoutDirection(Qt::LeftToRight); // HTML default
+ else if (parent->blockFormat.hasProperty(QTextFormat::LayoutDirection))
+ blockFormat.setLayoutDirection(parent->blockFormat.layoutDirection());
+
+ if (parent->displayMode == QTextHtmlElement::DisplayNone)
+ displayMode = QTextHtmlElement::DisplayNone;
+
+ if (parent->id != Html_table || id == Html_caption) {
+ if (parent->blockFormat.hasProperty(QTextFormat::BlockAlignment))
+ blockFormat.setAlignment(parent->blockFormat.alignment());
+ else
+ blockFormat.clearProperty(QTextFormat::BlockAlignment);
+ }
+ // we don't paint per-row background colors, yet. so as an
+ // exception inherit the background color here
+ // we also inherit the background between inline elements
+ if ((parent->id != Html_tr || !isTableCell())
+ && (displayMode != QTextHtmlElement::DisplayInline || parent->displayMode != QTextHtmlElement::DisplayInline)) {
+ charFormat.clearProperty(QTextFormat::BackgroundBrush);
+ }
+
+ listStyle = parent->listStyle;
+ // makes no sense to inherit that property, a named anchor is a single point
+ // in the document, which is set by the DocumentFragment
+ charFormat.clearProperty(QTextFormat::AnchorName);
+ wsm = parent->wsm;
+
+ // initialize remaining properties
+ margin[QTextHtmlParser::MarginLeft] = 0;
+ margin[QTextHtmlParser::MarginRight] = 0;
+ margin[QTextHtmlParser::MarginTop] = 0;
+ margin[QTextHtmlParser::MarginBottom] = 0;
+ cssFloat = QTextFrameFormat::InFlow;
+
+ for (int i = 0; i < 4; ++i)
+ padding[i] = -1;
+
+ // set element specific attributes
+ switch (id) {
+ case Html_a:
+ charFormat.setAnchor(true);
+ for (int i = 0; i < attributes.count(); i += 2) {
+ const QString key = attributes.at(i);
+ if (key.compare(QLatin1String("href"), Qt::CaseInsensitive) == 0
+ && !attributes.at(i + 1).isEmpty()) {
+ hasHref = true;
+ charFormat.setUnderlineStyle(QTextCharFormat::SingleUnderline);
+ charFormat.setForeground(QApplication::palette().link());
+ }
+ }
+
+ break;
+ case Html_em:
+ case Html_i:
+ case Html_cite:
+ case Html_address:
+ case Html_var:
+ case Html_dfn:
+ charFormat.setFontItalic(true);
+ break;
+ case Html_big:
+ charFormat.setProperty(QTextFormat::FontSizeAdjustment, int(1));
+ break;
+ case Html_small:
+ charFormat.setProperty(QTextFormat::FontSizeAdjustment, int(-1));
+ break;
+ case Html_strong:
+ case Html_b:
+ charFormat.setFontWeight(QFont::Bold);
+ break;
+ case Html_h1:
+ charFormat.setFontWeight(QFont::Bold);
+ charFormat.setProperty(QTextFormat::FontSizeAdjustment, int(3));
+ margin[QTextHtmlParser::MarginTop] = 18;
+ margin[QTextHtmlParser::MarginBottom] = 12;
+ break;
+ case Html_h2:
+ charFormat.setFontWeight(QFont::Bold);
+ charFormat.setProperty(QTextFormat::FontSizeAdjustment, int(2));
+ margin[QTextHtmlParser::MarginTop] = 16;
+ margin[QTextHtmlParser::MarginBottom] = 12;
+ break;
+ case Html_h3:
+ charFormat.setFontWeight(QFont::Bold);
+ charFormat.setProperty(QTextFormat::FontSizeAdjustment, int(1));
+ margin[QTextHtmlParser::MarginTop] = 14;
+ margin[QTextHtmlParser::MarginBottom] = 12;
+ break;
+ case Html_h4:
+ charFormat.setFontWeight(QFont::Bold);
+ charFormat.setProperty(QTextFormat::FontSizeAdjustment, int(0));
+ margin[QTextHtmlParser::MarginTop] = 12;
+ margin[QTextHtmlParser::MarginBottom] = 12;
+ break;
+ case Html_h5:
+ charFormat.setFontWeight(QFont::Bold);
+ charFormat.setProperty(QTextFormat::FontSizeAdjustment, int(-1));
+ margin[QTextHtmlParser::MarginTop] = 12;
+ margin[QTextHtmlParser::MarginBottom] = 4;
+ break;
+ case Html_p:
+ margin[QTextHtmlParser::MarginTop] = 12;
+ margin[QTextHtmlParser::MarginBottom] = 12;
+ break;
+ case Html_center:
+ blockFormat.setAlignment(Qt::AlignCenter);
+ break;
+ case Html_ul:
+ listStyle = QTextListFormat::ListDisc;
+ // nested lists don't have margins, except for the toplevel one
+ if (!isNestedList(parser)) {
+ margin[QTextHtmlParser::MarginTop] = 12;
+ margin[QTextHtmlParser::MarginBottom] = 12;
+ }
+ // no left margin as we use indenting instead
+ break;
+ case Html_ol:
+ listStyle = QTextListFormat::ListDecimal;
+ // nested lists don't have margins, except for the toplevel one
+ if (!isNestedList(parser)) {
+ margin[QTextHtmlParser::MarginTop] = 12;
+ margin[QTextHtmlParser::MarginBottom] = 12;
+ }
+ // no left margin as we use indenting instead
+ break;
+ case Html_code:
+ case Html_tt:
+ case Html_kbd:
+ case Html_samp:
+ charFormat.setFontFamily(QString::fromLatin1("Courier New,courier"));
+ // <tt> uses a fixed font, so set the property
+ charFormat.setFontFixedPitch(true);
+ break;
+ case Html_br:
+ text = QChar(QChar::LineSeparator);
+ wsm = QTextHtmlParserNode::WhiteSpacePre;
+ break;
+ // ##### sub / sup
+ case Html_pre:
+ charFormat.setFontFamily(QString::fromLatin1("Courier New,courier"));
+ wsm = WhiteSpacePre;
+ margin[QTextHtmlParser::MarginTop] = 12;
+ margin[QTextHtmlParser::MarginBottom] = 12;
+ // <pre> uses a fixed font
+ charFormat.setFontFixedPitch(true);
+ break;
+ case Html_blockquote:
+ margin[QTextHtmlParser::MarginTop] = 12;
+ margin[QTextHtmlParser::MarginBottom] = 12;
+ margin[QTextHtmlParser::MarginLeft] = 40;
+ margin[QTextHtmlParser::MarginRight] = 40;
+ break;
+ case Html_dl:
+ margin[QTextHtmlParser::MarginTop] = 8;
+ margin[QTextHtmlParser::MarginBottom] = 8;
+ break;
+ case Html_dd:
+ margin[QTextHtmlParser::MarginLeft] = 30;
+ break;
+ case Html_u:
+ charFormat.setUnderlineStyle(QTextCharFormat::SingleUnderline);
+ break;
+ case Html_s:
+ charFormat.setFontStrikeOut(true);
+ break;
+ case Html_nobr:
+ wsm = WhiteSpaceNoWrap;
+ break;
+ case Html_th:
+ charFormat.setFontWeight(QFont::Bold);
+ blockFormat.setAlignment(Qt::AlignCenter);
+ break;
+ case Html_td:
+ blockFormat.setAlignment(Qt::AlignLeft);
+ break;
+ case Html_sub:
+ charFormat.setVerticalAlignment(QTextCharFormat::AlignSubScript);
+ break;
+ case Html_sup:
+ charFormat.setVerticalAlignment(QTextCharFormat::AlignSuperScript);
+ break;
+ default: break;
+ }
+}
+
+#ifndef QT_NO_CSSPARSER
+void QTextHtmlParserNode::setListStyle(const QVector<QCss::Value> &cssValues)
+{
+ for (int i = 0; i < cssValues.count(); ++i) {
+ if (cssValues.at(i).type == QCss::Value::KnownIdentifier) {
+ switch (static_cast<QCss::KnownValue>(cssValues.at(i).variant.toInt())) {
+ case QCss::Value_Disc: hasOwnListStyle = true; listStyle = QTextListFormat::ListDisc; break;
+ case QCss::Value_Square: hasOwnListStyle = true; listStyle = QTextListFormat::ListSquare; break;
+ case QCss::Value_Circle: hasOwnListStyle = true; listStyle = QTextListFormat::ListCircle; break;
+ case QCss::Value_Decimal: hasOwnListStyle = true; listStyle = QTextListFormat::ListDecimal; break;
+ case QCss::Value_LowerAlpha: hasOwnListStyle = true; listStyle = QTextListFormat::ListLowerAlpha; break;
+ case QCss::Value_UpperAlpha: hasOwnListStyle = true; listStyle = QTextListFormat::ListUpperAlpha; break;
+ default: break;
+ }
+ }
+ }
+ // allow individual list items to override the style
+ if (id == Html_li && hasOwnListStyle)
+ blockFormat.setProperty(QTextFormat::ListStyle, listStyle);
+}
+
+void QTextHtmlParserNode::applyCssDeclarations(const QVector<QCss::Declaration> &declarations, const QTextDocument *resourceProvider)
+{
+ QCss::ValueExtractor extractor(declarations);
+ extractor.extractBox(margin, padding);
+
+ for (int i = 0; i < declarations.count(); ++i) {
+ const QCss::Declaration &decl = declarations.at(i);
+ if (decl.d->values.isEmpty()) continue;
+
+ QCss::KnownValue identifier = QCss::UnknownValue;
+ if (decl.d->values.first().type == QCss::Value::KnownIdentifier)
+ identifier = static_cast<QCss::KnownValue>(decl.d->values.first().variant.toInt());
+
+ switch (decl.d->propertyId) {
+ case QCss::BorderColor: borderBrush = QBrush(decl.colorValue()); break;
+ case QCss::BorderStyles:
+ if (decl.styleValue() != QCss::BorderStyle_Unknown && decl.styleValue() != QCss::BorderStyle_Native)
+ borderStyle = static_cast<QTextFrameFormat::BorderStyle>(decl.styleValue() - 1);
+ break;
+ case QCss::BorderWidth:
+ tableBorder = extractor.lengthValue(decl);
+ break;
+ case QCss::Color: charFormat.setForeground(decl.colorValue()); break;
+ case QCss::Float:
+ cssFloat = QTextFrameFormat::InFlow;
+ switch (identifier) {
+ case QCss::Value_Left: cssFloat = QTextFrameFormat::FloatLeft; break;
+ case QCss::Value_Right: cssFloat = QTextFrameFormat::FloatRight; break;
+ default: break;
+ }
+ break;
+ case QCss::QtBlockIndent:
+ blockFormat.setIndent(decl.d->values.first().variant.toInt());
+ break;
+ case QCss::TextIndent: {
+ qreal indent = 0;
+ if (decl.realValue(&indent, "px"))
+ blockFormat.setTextIndent(indent);
+ break; }
+ case QCss::QtListIndent:
+ if (decl.intValue(&cssListIndent))
+ hasCssListIndent = true;
+ break;
+ case QCss::QtParagraphType:
+ if (decl.d->values.first().variant.toString().compare(QLatin1String("empty"), Qt::CaseInsensitive) == 0)
+ isEmptyParagraph = true;
+ break;
+ case QCss::QtTableType:
+ if (decl.d->values.first().variant.toString().compare(QLatin1String("frame"), Qt::CaseInsensitive) == 0)
+ isTextFrame = true;
+ else if (decl.d->values.first().variant.toString().compare(QLatin1String("root"), Qt::CaseInsensitive) == 0) {
+ isTextFrame = true;
+ isRootFrame = true;
+ }
+ break;
+ case QCss::QtUserState:
+ userState = decl.d->values.first().variant.toInt();
+ break;
+ case QCss::Whitespace:
+ switch (identifier) {
+ case QCss::Value_Normal: wsm = QTextHtmlParserNode::WhiteSpaceNormal; break;
+ case QCss::Value_Pre: wsm = QTextHtmlParserNode::WhiteSpacePre; break;
+ case QCss::Value_NoWrap: wsm = QTextHtmlParserNode::WhiteSpaceNoWrap; break;
+ case QCss::Value_PreWrap: wsm = QTextHtmlParserNode::WhiteSpacePreWrap; break;
+ default: break;
+ }
+ break;
+ case QCss::VerticalAlignment:
+ switch (identifier) {
+ case QCss::Value_Sub: charFormat.setVerticalAlignment(QTextCharFormat::AlignSubScript); break;
+ case QCss::Value_Super: charFormat.setVerticalAlignment(QTextCharFormat::AlignSuperScript); break;
+ case QCss::Value_Middle: charFormat.setVerticalAlignment(QTextCharFormat::AlignMiddle); break;
+ case QCss::Value_Top: charFormat.setVerticalAlignment(QTextCharFormat::AlignTop); break;
+ case QCss::Value_Bottom: charFormat.setVerticalAlignment(QTextCharFormat::AlignBottom); break;
+ default: charFormat.setVerticalAlignment(QTextCharFormat::AlignNormal); break;
+ }
+ break;
+ case QCss::PageBreakBefore:
+ switch (identifier) {
+ case QCss::Value_Always: blockFormat.setPageBreakPolicy(blockFormat.pageBreakPolicy() | QTextFormat::PageBreak_AlwaysBefore); break;
+ case QCss::Value_Auto: blockFormat.setPageBreakPolicy(blockFormat.pageBreakPolicy() & ~QTextFormat::PageBreak_AlwaysBefore); break;
+ default: break;
+ }
+ break;
+ case QCss::PageBreakAfter:
+ switch (identifier) {
+ case QCss::Value_Always: blockFormat.setPageBreakPolicy(blockFormat.pageBreakPolicy() | QTextFormat::PageBreak_AlwaysAfter); break;
+ case QCss::Value_Auto: blockFormat.setPageBreakPolicy(blockFormat.pageBreakPolicy() & ~QTextFormat::PageBreak_AlwaysAfter); break;
+ default: break;
+ }
+ break;
+ case QCss::TextUnderlineStyle:
+ switch (identifier) {
+ case QCss::Value_None: charFormat.setUnderlineStyle(QTextCharFormat::NoUnderline); break;
+ case QCss::Value_Solid: charFormat.setUnderlineStyle(QTextCharFormat::SingleUnderline); break;
+ case QCss::Value_Dashed: charFormat.setUnderlineStyle(QTextCharFormat::DashUnderline); break;
+ case QCss::Value_Dotted: charFormat.setUnderlineStyle(QTextCharFormat::DotLine); break;
+ case QCss::Value_DotDash: charFormat.setUnderlineStyle(QTextCharFormat::DashDotLine); break;
+ case QCss::Value_DotDotDash: charFormat.setUnderlineStyle(QTextCharFormat::DashDotDotLine); break;
+ case QCss::Value_Wave: charFormat.setUnderlineStyle(QTextCharFormat::WaveUnderline); break;
+ default: break;
+ }
+ break;
+ case QCss::ListStyleType:
+ case QCss::ListStyle:
+ setListStyle(decl.d->values);
+ break;
+ default: break;
+ }
+ }
+
+ QFont f;
+ int adjustment = -255;
+ extractor.extractFont(&f, &adjustment);
+ if (f.resolve() & QFont::SizeResolved) {
+ if (f.pointSize() > 0) {
+ charFormat.setFontPointSize(f.pointSize());
+ } else if (f.pixelSize() > 0) {
+ charFormat.setProperty(QTextFormat::FontPixelSize, f.pixelSize());
+ }
+ }
+ if (f.resolve() & QFont::StyleResolved)
+ charFormat.setFontItalic(f.style() != QFont::StyleNormal);
+
+ if (f.resolve() & QFont::WeightResolved)
+ charFormat.setFontWeight(f.weight());
+
+ if (f.resolve() & QFont::FamilyResolved)
+ charFormat.setFontFamily(f.family());
+
+ if (f.resolve() & QFont::UnderlineResolved)
+ charFormat.setUnderlineStyle(f.underline() ? QTextCharFormat::SingleUnderline : QTextCharFormat::NoUnderline);
+
+ if (f.resolve() & QFont::OverlineResolved)
+ charFormat.setFontOverline(f.overline());
+
+ if (f.resolve() & QFont::StrikeOutResolved)
+ charFormat.setFontStrikeOut(f.strikeOut());
+
+ if (f.resolve() & QFont::CapitalizationResolved)
+ charFormat.setFontCapitalization(f.capitalization());
+
+ if (adjustment >= -1)
+ charFormat.setProperty(QTextFormat::FontSizeAdjustment, adjustment);
+
+ {
+ Qt::Alignment ignoredAlignment;
+ QCss::Repeat ignoredRepeat;
+ QString bgImage;
+ QBrush bgBrush;
+ QCss::Origin ignoredOrigin, ignoredClip;
+ QCss::Attachment ignoredAttachment;
+ extractor.extractBackground(&bgBrush, &bgImage, &ignoredRepeat, &ignoredAlignment,
+ &ignoredOrigin, &ignoredAttachment, &ignoredClip);
+
+ if (!bgImage.isEmpty() && resourceProvider) {
+ applyBackgroundImage(bgImage, resourceProvider);
+ } else if (bgBrush.style() != Qt::NoBrush) {
+ charFormat.setBackground(bgBrush);
+ }
+ }
+}
+
+#endif // QT_NO_CSSPARSER
+
+void QTextHtmlParserNode::applyBackgroundImage(const QString &url, const QTextDocument *resourceProvider)
+{
+ if (!url.isEmpty() && resourceProvider) {
+ QVariant val = resourceProvider->resource(QTextDocument::ImageResource, url);
+
+ if (qApp->thread() != QThread::currentThread()) {
+ // must use images in non-GUI threads
+ if (val.type() == QVariant::Image) {
+ QImage image = qvariant_cast<QImage>(val);
+ charFormat.setBackground(image);
+ } else if (val.type() == QVariant::ByteArray) {
+ QImage image;
+ if (image.loadFromData(val.toByteArray())) {
+ charFormat.setBackground(image);
+ }
+ }
+ } else {
+ if (val.type() == QVariant::Image || val.type() == QVariant::Pixmap) {
+ charFormat.setBackground(qvariant_cast<QPixmap>(val));
+ } else if (val.type() == QVariant::ByteArray) {
+ QPixmap pm;
+ if (pm.loadFromData(val.toByteArray())) {
+ charFormat.setBackground(pm);
+ }
+ }
+ }
+ }
+ if (!url.isEmpty())
+ charFormat.setProperty(QTextFormat::BackgroundImageUrl, url);
+}
+
+bool QTextHtmlParserNode::hasOnlyWhitespace() const
+{
+ for (int i = 0; i < text.count(); ++i)
+ if (!text.at(i).isSpace() || text.at(i) == QChar::LineSeparator)
+ return false;
+ return true;
+}
+
+static bool setIntAttribute(int *destination, const QString &value)
+{
+ bool ok = false;
+ int val = value.toInt(&ok);
+ if (ok)
+ *destination = val;
+
+ return ok;
+}
+
+static bool setFloatAttribute(qreal *destination, const QString &value)
+{
+ bool ok = false;
+ qreal val = value.toDouble(&ok);
+ if (ok)
+ *destination = val;
+
+ return ok;
+}
+
+static void setWidthAttribute(QTextLength *width, QString value)
+{
+ qreal realVal;
+ bool ok = false;
+ realVal = value.toDouble(&ok);
+ if (ok) {
+ *width = QTextLength(QTextLength::FixedLength, realVal);
+ } else {
+ value = value.trimmed();
+ if (!value.isEmpty() && value.at(value.length() - 1) == QLatin1Char('%')) {
+ value.chop(1);
+ realVal = value.toDouble(&ok);
+ if (ok)
+ *width = QTextLength(QTextLength::PercentageLength, realVal);
+ }
+ }
+}
+
+#ifndef QT_NO_CSSPARSER
+void QTextHtmlParserNode::parseStyleAttribute(const QString &value, const QTextDocument *resourceProvider)
+{
+ QString css = value;
+ css.prepend(QLatin1String("* {"));
+ css.append(QLatin1Char('}'));
+ QCss::Parser parser(css);
+ QCss::StyleSheet sheet;
+ parser.parse(&sheet, Qt::CaseInsensitive);
+ if (sheet.styleRules.count() != 1) return;
+ applyCssDeclarations(sheet.styleRules.at(0).declarations, resourceProvider);
+}
+#endif
+
+QStringList QTextHtmlParser::parseAttributes()
+{
+ QStringList attrs;
+
+ while (pos < len) {
+ eatSpace();
+ if (hasPrefix(QLatin1Char('>')) || hasPrefix(QLatin1Char('/')))
+ break;
+ QString key = parseWord().toLower();
+ QString value = QLatin1String("1");
+ if (key.size() == 0)
+ break;
+ eatSpace();
+ if (hasPrefix(QLatin1Char('='))){
+ pos++;
+ eatSpace();
+ value = parseWord();
+ }
+ if (value.size() == 0)
+ continue;
+ attrs << key << value;
+ }
+
+ return attrs;
+}
+
+void QTextHtmlParser::applyAttributes(const QStringList &attributes)
+{
+ // local state variable for qt3 textedit mode
+ bool seenQt3Richtext = false;
+ QString linkHref;
+ QString linkType;
+
+ if (attributes.count() % 2 == 1)
+ return;
+
+ QTextHtmlParserNode *node = &nodes.last();
+
+ for (int i = 0; i < attributes.count(); i += 2) {
+ QString key = attributes.at(i);
+ QString value = attributes.at(i + 1);
+
+ switch (node->id) {
+ case Html_font:
+ // the infamous font tag
+ if (key == QLatin1String("size") && value.size()) {
+ int n = value.toInt();
+ if (value.at(0) != QLatin1Char('+') && value.at(0) != QLatin1Char('-'))
+ n -= 3;
+ node->charFormat.setProperty(QTextFormat::FontSizeAdjustment, n);
+ } else if (key == QLatin1String("face")) {
+ node->charFormat.setFontFamily(value);
+ } else if (key == QLatin1String("color")) {
+ QColor c; c.setNamedColor(value);
+ if (!c.isValid())
+ qWarning("QTextHtmlParser::applyAttributes: Unknown color name '%s'",value.toLatin1().constData());
+ node->charFormat.setForeground(c);
+ }
+ break;
+ case Html_ol:
+ case Html_ul:
+ if (key == QLatin1String("type")) {
+ node->hasOwnListStyle = true;
+ if (value == QLatin1String("1")) {
+ node->listStyle = QTextListFormat::ListDecimal;
+ } else if (value == QLatin1String("a")) {
+ node->listStyle = QTextListFormat::ListLowerAlpha;
+ } else if (value == QLatin1String("A")) {
+ node->listStyle = QTextListFormat::ListUpperAlpha;
+ } else {
+ value = value.toLower();
+ if (value == QLatin1String("square"))
+ node->listStyle = QTextListFormat::ListSquare;
+ else if (value == QLatin1String("disc"))
+ node->listStyle = QTextListFormat::ListDisc;
+ else if (value == QLatin1String("circle"))
+ node->listStyle = QTextListFormat::ListCircle;
+ }
+ }
+ break;
+ case Html_a:
+ if (key == QLatin1String("href"))
+ node->charFormat.setAnchorHref(value);
+ else if (key == QLatin1String("name"))
+ node->charFormat.setAnchorName(value);
+ break;
+ case Html_img:
+ if (key == QLatin1String("src") || key == QLatin1String("source")) {
+ node->imageName = value;
+ } else if (key == QLatin1String("width")) {
+ node->imageWidth = -2; // register that there is a value for it.
+ setFloatAttribute(&node->imageWidth, value);
+ } else if (key == QLatin1String("height")) {
+ node->imageHeight = -2; // register that there is a value for it.
+ setFloatAttribute(&node->imageHeight, value);
+ }
+ break;
+ case Html_tr:
+ case Html_body:
+ if (key == QLatin1String("bgcolor")) {
+ QColor c; c.setNamedColor(value);
+ if (!c.isValid())
+ qWarning("QTextHtmlParser::applyAttributes: Unknown color name '%s'",value.toLatin1().constData());
+ node->charFormat.setBackground(c);
+ } else if (key == QLatin1String("background")) {
+ node->applyBackgroundImage(value, resourceProvider);
+ }
+ break;
+ case Html_th:
+ case Html_td:
+ if (key == QLatin1String("width")) {
+ setWidthAttribute(&node->width, value);
+ } else if (key == QLatin1String("bgcolor")) {
+ QColor c; c.setNamedColor(value);
+ if (!c.isValid())
+ qWarning("QTextHtmlParser::applyAttributes: Unknown color name '%s'",value.toLatin1().constData());
+ node->charFormat.setBackground(c);
+ } else if (key == QLatin1String("background")) {
+ node->applyBackgroundImage(value, resourceProvider);
+ } else if (key == QLatin1String("rowspan")) {
+ if (setIntAttribute(&node->tableCellRowSpan, value))
+ node->tableCellRowSpan = qMax(1, node->tableCellRowSpan);
+ } else if (key == QLatin1String("colspan")) {
+ if (setIntAttribute(&node->tableCellColSpan, value))
+ node->tableCellColSpan = qMax(1, node->tableCellColSpan);
+ }
+ break;
+ case Html_table:
+ if (key == QLatin1String("border")) {
+ setFloatAttribute(&node->tableBorder, value);
+ } else if (key == QLatin1String("bgcolor")) {
+ QColor c; c.setNamedColor(value);
+ if (!c.isValid())
+ qWarning("QTextHtmlParser::applyAttributes: Unknown color name '%s'",value.toLatin1().constData());
+ node->charFormat.setBackground(c);
+ } else if (key == QLatin1String("background")) {
+ node->applyBackgroundImage(value, resourceProvider);
+ } else if (key == QLatin1String("cellspacing")) {
+ setFloatAttribute(&node->tableCellSpacing, value);
+ } else if (key == QLatin1String("cellpadding")) {
+ setFloatAttribute(&node->tableCellPadding, value);
+ } else if (key == QLatin1String("width")) {
+ setWidthAttribute(&node->width, value);
+ } else if (key == QLatin1String("height")) {
+ setWidthAttribute(&node->height, value);
+ }
+ break;
+ case Html_meta:
+ if (key == QLatin1String("name")
+ && value == QLatin1String("qrichtext")) {
+ seenQt3Richtext = true;
+ }
+
+ if (key == QLatin1String("content")
+ && value == QLatin1String("1")
+ && seenQt3Richtext) {
+
+ textEditMode = true;
+ }
+ break;
+ case Html_hr:
+ if (key == QLatin1String("width"))
+ setWidthAttribute(&node->width, value);
+ break;
+ case Html_link:
+ if (key == QLatin1String("href"))
+ linkHref = value;
+ else if (key == QLatin1String("type"))
+ linkType = value;
+ break;
+ default:
+ break;
+ }
+
+ if (key == QLatin1String("style")) {
+#ifndef QT_NO_CSSPARSER
+ node->parseStyleAttribute(value, resourceProvider);
+#endif
+ } else if (key == QLatin1String("align")) {
+ value = value.toLower();
+ bool alignmentSet = true;
+
+ if (value == QLatin1String("left"))
+ node->blockFormat.setAlignment(Qt::AlignLeft|Qt::AlignAbsolute);
+ else if (value == QLatin1String("right"))
+ node->blockFormat.setAlignment(Qt::AlignRight|Qt::AlignAbsolute);
+ else if (value == QLatin1String("center"))
+ node->blockFormat.setAlignment(Qt::AlignHCenter);
+ else if (value == QLatin1String("justify"))
+ node->blockFormat.setAlignment(Qt::AlignJustify);
+ else
+ alignmentSet = false;
+
+ if (node->id == Html_img) {
+ // HTML4 compat
+ if (alignmentSet) {
+ if (node->blockFormat.alignment() & Qt::AlignLeft)
+ node->cssFloat = QTextFrameFormat::FloatLeft;
+ else if (node->blockFormat.alignment() & Qt::AlignRight)
+ node->cssFloat = QTextFrameFormat::FloatRight;
+ } else if (value == QLatin1String("middle")) {
+ node->charFormat.setVerticalAlignment(QTextCharFormat::AlignMiddle);
+ } else if (value == QLatin1String("top")) {
+ node->charFormat.setVerticalAlignment(QTextCharFormat::AlignTop);
+ }
+ }
+ } else if (key == QLatin1String("valign")) {
+ value = value.toLower();
+ if (value == QLatin1String("top"))
+ node->charFormat.setVerticalAlignment(QTextCharFormat::AlignTop);
+ else if (value == QLatin1String("middle"))
+ node->charFormat.setVerticalAlignment(QTextCharFormat::AlignMiddle);
+ else if (value == QLatin1String("bottom"))
+ node->charFormat.setVerticalAlignment(QTextCharFormat::AlignBottom);
+ } else if (key == QLatin1String("dir")) {
+ value = value.toLower();
+ if (value == QLatin1String("ltr"))
+ node->blockFormat.setLayoutDirection(Qt::LeftToRight);
+ else if (value == QLatin1String("rtl"))
+ node->blockFormat.setLayoutDirection(Qt::RightToLeft);
+ } else if (key == QLatin1String("title")) {
+ node->charFormat.setToolTip(value);
+ } else if (key == QLatin1String("id")) {
+ node->charFormat.setAnchor(true);
+ node->charFormat.setAnchorName(value);
+ }
+ }
+
+#ifndef QT_NO_CSSPARSER
+ if (resourceProvider && !linkHref.isEmpty() && linkType == QLatin1String("text/css"))
+ importStyleSheet(linkHref);
+#endif
+}
+
+#ifndef QT_NO_CSSPARSER
+class QTextHtmlStyleSelector : public QCss::StyleSelector
+{
+public:
+ inline QTextHtmlStyleSelector(const QTextHtmlParser *parser)
+ : parser(parser) { nameCaseSensitivity = Qt::CaseInsensitive; }
+
+ virtual QStringList nodeNames(NodePtr node) const;
+ virtual QString attribute(NodePtr node, const QString &name) const;
+ virtual bool hasAttributes(NodePtr node) const;
+ virtual bool isNullNode(NodePtr node) const;
+ virtual NodePtr parentNode(NodePtr node) const;
+ virtual NodePtr previousSiblingNode(NodePtr node) const;
+ virtual NodePtr duplicateNode(NodePtr node) const;
+ virtual void freeNode(NodePtr node) const;
+
+private:
+ const QTextHtmlParser *parser;
+};
+
+QStringList QTextHtmlStyleSelector::nodeNames(NodePtr node) const
+{
+ return QStringList(parser->at(node.id).tag.toLower());
+}
+
+#endif // QT_NO_CSSPARSER
+
+static inline int findAttribute(const QStringList &attributes, const QString &name)
+{
+ int idx = -1;
+ do {
+ idx = attributes.indexOf(name, idx + 1);
+ } while (idx != -1 && (idx % 2 == 1));
+ return idx;
+}
+
+#ifndef QT_NO_CSSPARSER
+
+QString QTextHtmlStyleSelector::attribute(NodePtr node, const QString &name) const
+{
+ const QStringList &attributes = parser->at(node.id).attributes;
+ const int idx = findAttribute(attributes, name);
+ if (idx == -1)
+ return QString();
+ return attributes.at(idx + 1);
+}
+
+bool QTextHtmlStyleSelector::hasAttributes(NodePtr node) const
+{
+ const QStringList &attributes = parser->at(node.id).attributes;
+ return !attributes.isEmpty();
+}
+
+bool QTextHtmlStyleSelector::isNullNode(NodePtr node) const
+{
+ return node.id == 0;
+}
+
+QCss::StyleSelector::NodePtr QTextHtmlStyleSelector::parentNode(NodePtr node) const
+{
+ NodePtr parent;
+ parent.id = 0;
+ if (node.id) {
+ parent.id = parser->at(node.id).parent;
+ }
+ return parent;
+}
+
+QCss::StyleSelector::NodePtr QTextHtmlStyleSelector::duplicateNode(NodePtr node) const
+{
+ return node;
+}
+
+QCss::StyleSelector::NodePtr QTextHtmlStyleSelector::previousSiblingNode(NodePtr node) const
+{
+ NodePtr sibling;
+ sibling.id = 0;
+ if (!node.id)
+ return sibling;
+ int parent = parser->at(node.id).parent;
+ if (!parent)
+ return sibling;
+ const int childIdx = parser->at(parent).children.indexOf(node.id);
+ if (childIdx <= 0)
+ return sibling;
+ sibling.id = parser->at(parent).children.at(childIdx - 1);
+ return sibling;
+}
+
+void QTextHtmlStyleSelector::freeNode(NodePtr) const
+{
+}
+
+void QTextHtmlParser::resolveStyleSheetImports(const QCss::StyleSheet &sheet)
+{
+ for (int i = 0; i < sheet.importRules.count(); ++i) {
+ const QCss::ImportRule &rule = sheet.importRules.at(i);
+ if (rule.media.isEmpty()
+ || rule.media.contains(QLatin1String("screen"), Qt::CaseInsensitive))
+ importStyleSheet(rule.href);
+ }
+}
+
+void QTextHtmlParser::importStyleSheet(const QString &href)
+{
+ if (!resourceProvider)
+ return;
+ for (int i = 0; i < externalStyleSheets.count(); ++i)
+ if (externalStyleSheets.at(i).url == href)
+ return;
+
+ QVariant res = resourceProvider->resource(QTextDocument::StyleSheetResource, href);
+ QString css;
+ if (res.type() == QVariant::String) {
+ css = res.toString();
+ } else if (res.type() == QVariant::ByteArray) {
+ // #### detect @charset
+ css = QString::fromUtf8(res.toByteArray());
+ }
+ if (!css.isEmpty()) {
+ QCss::Parser parser(css);
+ QCss::StyleSheet sheet;
+ parser.parse(&sheet, Qt::CaseInsensitive);
+ externalStyleSheets.append(ExternalStyleSheet(href, sheet));
+ resolveStyleSheetImports(sheet);
+ }
+}
+
+QVector<QCss::Declaration> QTextHtmlParser::declarationsForNode(int node) const
+{
+ QVector<QCss::Declaration> decls;
+
+ QTextHtmlStyleSelector selector(this);
+
+ int idx = 0;
+ selector.styleSheets.resize((resourceProvider ? 1 : 0)
+ + externalStyleSheets.count()
+ + inlineStyleSheets.count());
+ if (resourceProvider)
+ selector.styleSheets[idx++] = resourceProvider->docHandle()->parsedDefaultStyleSheet;
+
+ for (int i = 0; i < externalStyleSheets.count(); ++i, ++idx)
+ selector.styleSheets[idx] = externalStyleSheets.at(i).sheet;
+
+ for (int i = 0; i < inlineStyleSheets.count(); ++i, ++idx)
+ selector.styleSheets[idx] = inlineStyleSheets.at(i);
+
+ selector.medium = QLatin1String("screen");
+
+ QCss::StyleSelector::NodePtr n;
+ n.id = node;
+
+ const char *extraPseudo = 0;
+ if (nodes.at(node).id == Html_a && nodes.at(node).hasHref)
+ extraPseudo = "link";
+ decls = selector.declarationsForNode(n, extraPseudo);
+
+ return decls;
+}
+
+bool QTextHtmlParser::nodeIsChildOf(int i, QTextHTMLElements id) const
+{
+ while (i) {
+ if (at(i).id == id)
+ return true;
+ i = at(i).parent;
+ }
+ return false;
+}
+
+QT_END_NAMESPACE
+#endif // QT_NO_CSSPARSER
+
+#endif // QT_NO_TEXTHTMLPARSER
diff --git a/src/gui/text/qtexthtmlparser_p.h b/src/gui/text/qtexthtmlparser_p.h
new file mode 100644
index 0000000000..a27b2f2ca1
--- /dev/null
+++ b/src/gui/text/qtexthtmlparser_p.h
@@ -0,0 +1,342 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QTEXTHTMLPARSER_P_H
+#define QTEXTHTMLPARSER_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 "QtCore/qvector.h"
+#include "QtGui/qbrush.h"
+#include "QtGui/qcolor.h"
+#include "QtGui/qfont.h"
+#include "QtGui/qtextdocument.h"
+#include "QtGui/qtextcursor.h"
+#include "private/qtextformat_p.h"
+#include "private/qtextdocument_p.h"
+#include "private/qcssparser_p.h"
+
+#ifndef QT_NO_TEXTHTMLPARSER
+
+QT_BEGIN_NAMESPACE
+
+enum QTextHTMLElements {
+ Html_unknown = -1,
+ Html_qt = 0,
+ Html_body,
+
+ Html_a,
+ Html_em,
+ Html_i,
+ Html_big,
+ Html_small,
+ Html_strong,
+ Html_b,
+ Html_cite,
+ Html_address,
+ Html_var,
+ Html_dfn,
+
+ Html_h1,
+ Html_h2,
+ Html_h3,
+ Html_h4,
+ Html_h5,
+ Html_h6,
+ Html_p,
+ Html_center,
+
+ Html_font,
+
+ Html_ul,
+ Html_ol,
+ Html_li,
+
+ Html_code,
+ Html_tt,
+ Html_kbd,
+ Html_samp,
+
+ Html_img,
+ Html_br,
+ Html_hr,
+
+ Html_sub,
+ Html_sup,
+
+ Html_pre,
+ Html_blockquote,
+ Html_head,
+ Html_div,
+ Html_span,
+ Html_dl,
+ Html_dt,
+ Html_dd,
+ Html_u,
+ Html_s,
+ Html_nobr,
+
+ // tables
+ Html_table,
+ Html_tr,
+ Html_td,
+ Html_th,
+ Html_thead,
+ Html_tbody,
+ Html_tfoot,
+ Html_caption,
+
+ // misc...
+ Html_html,
+ Html_style,
+ Html_title,
+ Html_meta,
+ Html_link,
+ Html_script,
+
+ Html_NumElements
+};
+
+struct QTextHtmlElement
+{
+ const char *name;
+ QTextHTMLElements id;
+ enum DisplayMode { DisplayBlock, DisplayInline, DisplayTable, DisplayNone } displayMode;
+};
+
+class QTextHtmlParser;
+
+struct QTextHtmlParserNode {
+ enum WhiteSpaceMode {
+ WhiteSpaceNormal,
+ WhiteSpacePre,
+ WhiteSpaceNoWrap,
+ WhiteSpacePreWrap,
+ WhiteSpaceModeUndefined = -1
+ };
+
+ QTextHtmlParserNode();
+ QString tag;
+ QString text;
+ QStringList attributes;
+ int parent;
+ QVector<int> children;
+ QTextHTMLElements id;
+ QTextCharFormat charFormat;
+ QTextBlockFormat blockFormat;
+ uint cssFloat : 2;
+ uint hasOwnListStyle : 1;
+ uint hasCssListIndent : 1;
+ uint isEmptyParagraph : 1;
+ uint isTextFrame : 1;
+ uint isRootFrame : 1;
+ uint displayMode : 3; // QTextHtmlElement::DisplayMode
+ uint hasHref : 1;
+ QTextListFormat::Style listStyle;
+ QString imageName;
+ qreal imageWidth;
+ qreal imageHeight;
+ QTextLength width;
+ QTextLength height;
+ qreal tableBorder;
+ int tableCellRowSpan;
+ int tableCellColSpan;
+ qreal tableCellSpacing;
+ qreal tableCellPadding;
+ QBrush borderBrush;
+ QTextFrameFormat::BorderStyle borderStyle;
+ int userState;
+
+ int cssListIndent;
+
+ WhiteSpaceMode wsm;
+
+ inline bool isListStart() const
+ { return id == Html_ol || id == Html_ul; }
+ inline bool isTableCell() const
+ { return id == Html_td || id == Html_th; }
+ inline bool isBlock() const
+ { return displayMode == QTextHtmlElement::DisplayBlock; }
+
+ inline bool isNotSelfNesting() const
+ { return id == Html_p || id == Html_li; }
+
+ inline bool allowedInContext(int parentId) const
+ {
+ switch (id) {
+ case Html_dd:
+ case Html_dt: return (parentId == Html_dl);
+ case Html_tr: return (parentId == Html_table
+ || parentId == Html_thead
+ || parentId == Html_tbody
+ || parentId == Html_tfoot
+ );
+ case Html_th:
+ case Html_td: return (parentId == Html_tr);
+ case Html_thead:
+ case Html_tbody:
+ case Html_tfoot: return (parentId == Html_table);
+ case Html_caption: return (parentId == Html_table);
+ case Html_body: return parentId != Html_head;
+ default: break;
+ }
+ return true;
+ }
+
+ inline bool mayNotHaveChildren() const
+ { return id == Html_img || id == Html_hr || id == Html_br || id == Html_meta; }
+
+ void initializeProperties(const QTextHtmlParserNode *parent, const QTextHtmlParser *parser);
+
+ inline int uncollapsedMargin(int mar) const { return margin[mar]; }
+
+ bool isNestedList(const QTextHtmlParser *parser) const;
+
+ void parseStyleAttribute(const QString &value, const QTextDocument *resourceProvider);
+
+#ifndef QT_NO_CSSPARSER
+ void applyCssDeclarations(const QVector<QCss::Declaration> &declarations, const QTextDocument *resourceProvider);
+
+ void setListStyle(const QVector<QCss::Value> &cssValues);
+#endif
+
+ void applyBackgroundImage(const QString &url, const QTextDocument *resourceProvider);
+
+ bool hasOnlyWhitespace() const;
+
+ int margin[4];
+ int padding[4];
+
+ friend class QTextHtmlParser;
+};
+Q_DECLARE_TYPEINFO(QTextHtmlParserNode, Q_MOVABLE_TYPE);
+
+
+class QTextHtmlParser
+{
+public:
+ enum Margin {
+ MarginTop,
+ MarginRight,
+ MarginBottom,
+ MarginLeft
+ };
+
+ inline const QTextHtmlParserNode &at(int i) const { return nodes.at(i); }
+ inline QTextHtmlParserNode &operator[](int i) { return nodes[i]; }
+ inline int count() const { return nodes.count(); }
+ inline int last() const { return nodes.count()-1; }
+ int depth(int i) const;
+ int topMargin(int i) const;
+ int bottomMargin(int i) const;
+ inline int leftMargin(int i) const { return margin(i, MarginLeft); }
+ inline int rightMargin(int i) const { return margin(i, MarginRight); }
+
+ inline int topPadding(int i) const { return at(i).padding[MarginTop]; }
+ inline int bottomPadding(int i) const { return at(i).padding[MarginBottom]; }
+ inline int leftPadding(int i) const { return at(i).padding[MarginLeft]; }
+ inline int rightPadding(int i) const { return at(i).padding[MarginRight]; }
+
+ void dumpHtml();
+
+ void parse(const QString &text, const QTextDocument *resourceProvider);
+
+ static int lookupElement(const QString &element);
+protected:
+ QTextHtmlParserNode *newNode(int parent);
+ QVector<QTextHtmlParserNode> nodes;
+ QString txt;
+ int pos, len;
+
+ bool textEditMode;
+
+ void parse();
+ void parseTag();
+ void parseCloseTag();
+ void parseExclamationTag();
+ QString parseEntity();
+ QString parseWord();
+ QTextHtmlParserNode *resolveParent();
+ void resolveNode();
+ QStringList parseAttributes();
+ void applyAttributes(const QStringList &attributes);
+ void eatSpace();
+ inline bool hasPrefix(QChar c, int lookahead = 0) const
+ {return pos + lookahead < len && txt.at(pos) == c; }
+ int margin(int i, int mar) const;
+
+ bool nodeIsChildOf(int i, QTextHTMLElements id) const;
+
+
+#ifndef QT_NO_CSSPARSER
+ QVector<QCss::Declaration> declarationsForNode(int node) const;
+ void resolveStyleSheetImports(const QCss::StyleSheet &sheet);
+ void importStyleSheet(const QString &href);
+
+ struct ExternalStyleSheet
+ {
+ inline ExternalStyleSheet() {}
+ inline ExternalStyleSheet(const QString &_url, const QCss::StyleSheet &_sheet)
+ : url(_url), sheet(_sheet) {}
+ QString url;
+ QCss::StyleSheet sheet;
+ };
+ QVector<ExternalStyleSheet> externalStyleSheets;
+ QVector<QCss::StyleSheet> inlineStyleSheets;
+#endif
+
+ const QTextDocument *resourceProvider;
+};
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_TEXTHTMLPARSER
+
+#endif // QTEXTHTMLPARSER_P_H
diff --git a/src/gui/text/qtextimagehandler.cpp b/src/gui/text/qtextimagehandler.cpp
new file mode 100644
index 0000000000..c04f2258ef
--- /dev/null
+++ b/src/gui/text/qtextimagehandler.cpp
@@ -0,0 +1,234 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+
+#include "qtextimagehandler_p.h"
+
+#include <qapplication.h>
+#include <qtextformat.h>
+#include <qpainter.h>
+#include <qdebug.h>
+#include <private/qtextengine_p.h>
+#include <qpalette.h>
+#include <qtextbrowser.h>
+#include <qthread.h>
+
+QT_BEGIN_NAMESPACE
+
+// set by the mime source factory in Qt3Compat
+QTextImageHandler::ExternalImageLoaderFunction QTextImageHandler::externalLoader = 0;
+
+static QPixmap getPixmap(QTextDocument *doc, const QTextImageFormat &format)
+{
+ QPixmap pm;
+
+ QString name = format.name();
+ if (name.startsWith(QLatin1String(":/"))) // auto-detect resources
+ name.prepend(QLatin1String("qrc"));
+ QUrl url = QUrl::fromEncoded(name.toUtf8());
+ const QVariant data = doc->resource(QTextDocument::ImageResource, url);
+ if (data.type() == QVariant::Pixmap || data.type() == QVariant::Image) {
+ pm = qvariant_cast<QPixmap>(data);
+ } else if (data.type() == QVariant::ByteArray) {
+ pm.loadFromData(data.toByteArray());
+ }
+
+ if (pm.isNull()) {
+ QString context;
+#ifndef QT_NO_TEXTBROWSER
+ QTextBrowser *browser = qobject_cast<QTextBrowser *>(doc->parent());
+ if (browser)
+ context = browser->source().toString();
+#endif
+ QImage img;
+ if (QTextImageHandler::externalLoader)
+ img = QTextImageHandler::externalLoader(name, context);
+
+ if (img.isNull()) { // try direct loading
+ name = format.name(); // remove qrc:/ prefix again
+ if (name.isEmpty() || !img.load(name))
+ return QPixmap(QLatin1String(":/trolltech/styles/commonstyle/images/file-16.png"));
+ }
+ pm = QPixmap::fromImage(img);
+ doc->addResource(QTextDocument::ImageResource, url, pm);
+ }
+
+ return pm;
+}
+
+static QSize getPixmapSize(QTextDocument *doc, const QTextImageFormat &format)
+{
+ QPixmap pm;
+
+ const bool hasWidth = format.hasProperty(QTextFormat::ImageWidth);
+ const int width = qRound(format.width());
+ const bool hasHeight = format.hasProperty(QTextFormat::ImageHeight);
+ const int height = qRound(format.height());
+
+ QSize size(width, height);
+ if (!hasWidth || !hasHeight) {
+ pm = getPixmap(doc, format);
+ if (!hasWidth) {
+ if (!hasHeight)
+ size.setWidth(pm.width());
+ else
+ size.setWidth(qRound(height * (pm.width() / (qreal) pm.height())));
+ }
+ if (!hasHeight) {
+ if (!hasWidth)
+ size.setHeight(pm.height());
+ else
+ size.setHeight(qRound(width * (pm.height() / (qreal) pm.width())));
+ }
+ }
+
+ qreal scale = 1.0;
+ QPaintDevice *pdev = doc->documentLayout()->paintDevice();
+ if (pdev) {
+ extern int qt_defaultDpi();
+ if (pm.isNull())
+ pm = getPixmap(doc, format);
+ if (!pm.isNull())
+ scale = qreal(pdev->logicalDpiY()) / qreal(qt_defaultDpi());
+ }
+ size *= scale;
+
+ return size;
+}
+
+static QImage getImage(QTextDocument *doc, const QTextImageFormat &format)
+{
+ QImage image;
+
+ QString name = format.name();
+ if (name.startsWith(QLatin1String(":/"))) // auto-detect resources
+ name.prepend(QLatin1String("qrc"));
+ QUrl url = QUrl::fromEncoded(name.toUtf8());
+ const QVariant data = doc->resource(QTextDocument::ImageResource, url);
+ if (data.type() == QVariant::Image) {
+ image = qvariant_cast<QImage>(data);
+ } else if (data.type() == QVariant::ByteArray) {
+ image.loadFromData(data.toByteArray());
+ }
+
+ if (image.isNull()) {
+ QString context;
+#ifndef QT_NO_TEXTBROWSER
+ QTextBrowser *browser = qobject_cast<QTextBrowser *>(doc->parent());
+ if (browser)
+ context = browser->source().toString();
+#endif
+ if (QTextImageHandler::externalLoader)
+ image = QTextImageHandler::externalLoader(name, context);
+
+ if (image.isNull()) { // try direct loading
+ name = format.name(); // remove qrc:/ prefix again
+ if (name.isEmpty() || !image.load(name))
+ return QImage(QLatin1String(":/trolltech/styles/commonstyle/images/file-16.png"));
+ }
+ doc->addResource(QTextDocument::ImageResource, url, image);
+ }
+
+ return image;
+}
+
+static QSize getImageSize(QTextDocument *doc, const QTextImageFormat &format)
+{
+ QImage image;
+
+ const bool hasWidth = format.hasProperty(QTextFormat::ImageWidth);
+ const int width = qRound(format.width());
+ const bool hasHeight = format.hasProperty(QTextFormat::ImageHeight);
+ const int height = qRound(format.height());
+
+ QSize size(width, height);
+ if (!hasWidth || !hasHeight) {
+ image = getImage(doc, format);
+ if (!hasWidth)
+ size.setWidth(image.width());
+ if (!hasHeight)
+ size.setHeight(image.height());
+ }
+
+ qreal scale = 1.0;
+ QPaintDevice *pdev = doc->documentLayout()->paintDevice();
+ if (pdev) {
+ extern int qt_defaultDpi();
+ if (image.isNull())
+ image = getImage(doc, format);
+ if (!image.isNull())
+ scale = qreal(pdev->logicalDpiY()) / qreal(qt_defaultDpi());
+ }
+ size *= scale;
+
+ return size;
+}
+
+QTextImageHandler::QTextImageHandler(QObject *parent)
+ : QObject(parent)
+{
+}
+
+QSizeF QTextImageHandler::intrinsicSize(QTextDocument *doc, int posInDocument, const QTextFormat &format)
+{
+ Q_UNUSED(posInDocument)
+ const QTextImageFormat imageFormat = format.toImageFormat();
+
+ if (qApp->thread() != QThread::currentThread())
+ return getImageSize(doc, imageFormat);
+ return getPixmapSize(doc, imageFormat);
+}
+
+void QTextImageHandler::drawObject(QPainter *p, const QRectF &rect, QTextDocument *doc, int posInDocument, const QTextFormat &format)
+{
+ Q_UNUSED(posInDocument)
+ const QTextImageFormat imageFormat = format.toImageFormat();
+
+ if (qApp->thread() != QThread::currentThread()) {
+ const QImage image = getImage(doc, imageFormat);
+ p->drawImage(rect, image, image.rect());
+ } else {
+ const QPixmap pixmap = getPixmap(doc, imageFormat);
+ p->drawPixmap(rect, pixmap, pixmap.rect());
+ }
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/text/qtextimagehandler_p.h b/src/gui/text/qtextimagehandler_p.h
new file mode 100644
index 0000000000..f5426b549d
--- /dev/null
+++ b/src/gui/text/qtextimagehandler_p.h
@@ -0,0 +1,80 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QTEXTIMAGEHANDLER_P_H
+#define QTEXTIMAGEHANDLER_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 "QtCore/qobject.h"
+#include "QtGui/qabstracttextdocumentlayout.h"
+
+QT_BEGIN_NAMESPACE
+
+class QTextImageFormat;
+
+class Q_GUI_EXPORT QTextImageHandler : public QObject,
+ public QTextObjectInterface
+{
+ Q_OBJECT
+ Q_INTERFACES(QTextObjectInterface)
+public:
+ explicit QTextImageHandler(QObject *parent = 0);
+
+ virtual QSizeF intrinsicSize(QTextDocument *doc, int posInDocument, const QTextFormat &format);
+ virtual void drawObject(QPainter *p, const QRectF &rect, QTextDocument *doc, int posInDocument, const QTextFormat &format);
+
+ typedef QImage (*ExternalImageLoaderFunction)(const QString &name, const QString &context);
+ static ExternalImageLoaderFunction externalLoader;
+};
+
+QT_END_NAMESPACE
+
+#endif // QTEXTIMAGEHANDLER_P_H
diff --git a/src/gui/text/qtextlayout.cpp b/src/gui/text/qtextlayout.cpp
new file mode 100644
index 0000000000..434d1cae87
--- /dev/null
+++ b/src/gui/text/qtextlayout.cpp
@@ -0,0 +1,2453 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qtextlayout.h"
+#include "qtextengine_p.h"
+
+#include <qfont.h>
+#include <qapplication.h>
+#include <qpainter.h>
+#include <qvarlengtharray.h>
+#include <qtextformat.h>
+#include <qabstracttextdocumentlayout.h>
+#include "qtextdocument_p.h"
+#include "qtextformat_p.h"
+#include "qstyleoption.h"
+#include "qpainterpath.h"
+#include <limits.h>
+
+#include <qdebug.h>
+
+#include "qfontengine_p.h"
+
+QT_BEGIN_NAMESPACE
+
+#define ObjectSelectionBrush (QTextFormat::ForegroundBrush + 1)
+
+static inline QFixed leadingSpaceWidth(QTextEngine *eng, const QScriptLine &line)
+{
+ if (!line.hasTrailingSpaces
+ || (eng->option.flags() & QTextOption::IncludeTrailingSpaces)
+ || !(eng->option.alignment() & Qt::AlignRight)
+ || (eng->option.textDirection() != Qt::RightToLeft))
+ return QFixed();
+
+ int pos = line.length;
+ const HB_CharAttributes *attributes = eng->attributes();
+ while (pos > 0 && attributes[line.from + pos - 1].whiteSpace)
+ --pos;
+ return eng->width(line.from + pos, line.length - pos);
+}
+
+static QFixed alignLine(QTextEngine *eng, const QScriptLine &line)
+{
+ QFixed x = 0;
+ eng->justify(line);
+ // if width is QFIXED_MAX that means we used setNumColumns() and that implicitly makes this line left aligned.
+ if (!line.justified && line.width != QFIXED_MAX) {
+ int align = eng->option.alignment();
+ if (align & Qt::AlignJustify && eng->option.textDirection() == Qt::RightToLeft)
+ align = Qt::AlignRight;
+ if (align & Qt::AlignRight)
+ x = line.width - (line.textWidth + leadingSpaceWidth(eng, line));
+ else if (align & Qt::AlignHCenter)
+ x = (line.width - line.textWidth)/2;
+ }
+ return x;
+}
+
+/*!
+ \class QTextLayout::FormatRange
+ \reentrant
+
+ \brief The QTextLayout::FormatRange structure is used to apply extra formatting information
+ for a specified area in the text layout's content.
+
+ \sa QTextLayout::setAdditionalFormats(), QTextLayout::draw()
+*/
+
+/*!
+ \variable QTextLayout::FormatRange::start
+ Specifies the beginning of the format range within the text layout's text.
+*/
+
+/*!
+ \variable QTextLayout::FormatRange::length
+ Specifies the numer of characters the format range spans.
+*/
+
+/*!
+ \variable QTextLayout::FormatRange::format
+ Specifies the format to apply.
+*/
+
+/*!
+ \class QTextInlineObject
+ \reentrant
+
+ \brief The QTextInlineObject class represents an inline object in
+ a QTextLayout.
+
+ \ingroup text
+
+ This class is only used if the text layout is used to lay out
+ parts of a QTextDocument.
+
+ The inline object has various attributes that can be set, for
+ example using, setWidth(), setAscent(), and setDescent(). The
+ rectangle it occupies is given by rect(), and its direction by
+ isRightToLeft(). Its position in the text layout is given by at(),
+ and its format is given by format().
+*/
+
+/*!
+ \fn QTextInlineObject::QTextInlineObject(int i, QTextEngine *e)
+
+ Creates a new inline object for the item at position \a i in the
+ text engine \a e.
+*/
+
+/*!
+ \fn QTextInlineObject::QTextInlineObject()
+
+ \internal
+*/
+
+/*!
+ \fn bool QTextInlineObject::isValid() const
+
+ Returns true if this inline object is valid; otherwise returns
+ false.
+*/
+
+/*!
+ Returns the inline object's rectangle.
+
+ \sa ascent() descent() width()
+*/
+QRectF QTextInlineObject::rect() const
+{
+ QScriptItem& si = eng->layoutData->items[itm];
+ return QRectF(0, -si.ascent.toReal(), si.width.toReal(), si.height().toReal());
+}
+
+/*!
+ Returns the inline object's width.
+
+ \sa ascent() descent() rect()
+*/
+qreal QTextInlineObject::width() const
+{
+ return eng->layoutData->items[itm].width.toReal();
+}
+
+/*!
+ Returns the inline object's ascent.
+
+ \sa descent() width() rect()
+*/
+qreal QTextInlineObject::ascent() const
+{
+ return eng->layoutData->items[itm].ascent.toReal();
+}
+
+/*!
+ Returns the inline object's descent.
+
+ \sa ascent() width() rect()
+*/
+qreal QTextInlineObject::descent() const
+{
+ return eng->layoutData->items[itm].descent.toReal();
+}
+
+/*!
+ Returns the inline object's total height. This is equal to
+ ascent() + descent() + 1.
+
+ \sa ascent() descent() width() rect()
+*/
+qreal QTextInlineObject::height() const
+{
+ return eng->layoutData->items[itm].height().toReal();
+}
+
+
+/*!
+ Sets the inline object's width to \a w.
+
+ \sa width() ascent() descent() rect()
+*/
+void QTextInlineObject::setWidth(qreal w)
+{
+ eng->layoutData->items[itm].width = QFixed::fromReal(w);
+}
+
+/*!
+ Sets the inline object's ascent to \a a.
+
+ \sa ascent() setDescent() width() rect()
+*/
+void QTextInlineObject::setAscent(qreal a)
+{
+ eng->layoutData->items[itm].ascent = QFixed::fromReal(a);
+}
+
+/*!
+ Sets the inline object's decent to \a d.
+
+ \sa descent() setAscent() width() rect()
+*/
+void QTextInlineObject::setDescent(qreal d)
+{
+ eng->layoutData->items[itm].descent = QFixed::fromReal(d);
+}
+
+/*!
+ The position of the inline object within the text layout.
+*/
+int QTextInlineObject::textPosition() const
+{
+ return eng->layoutData->items[itm].position;
+}
+
+/*!
+ Returns an integer describing the format of the inline object
+ within the text layout.
+*/
+int QTextInlineObject::formatIndex() const
+{
+ return eng->formatIndex(&eng->layoutData->items[itm]);
+}
+
+/*!
+ Returns format of the inline object within the text layout.
+*/
+QTextFormat QTextInlineObject::format() const
+{
+ if (!eng->block.docHandle())
+ return QTextFormat();
+ return eng->formats()->format(eng->formatIndex(&eng->layoutData->items[itm]));
+}
+
+/*!
+ Returns if the object should be laid out right-to-left or left-to-right.
+*/
+Qt::LayoutDirection QTextInlineObject::textDirection() const
+{
+ return (eng->layoutData->items[itm].analysis.bidiLevel % 2 ? Qt::RightToLeft : Qt::LeftToRight);
+}
+
+/*!
+ \class QTextLayout
+ \reentrant
+
+ \brief The QTextLayout class is used to lay out and paint a single
+ paragraph of text.
+
+ \ingroup text
+
+ It offers most features expected from a modern text layout
+ engine, including Unicode compliant rendering, line breaking and
+ handling of cursor positioning. It can also produce and render
+ device independent layout, something that is important for WYSIWYG
+ applications.
+
+ The class has a rather low level API and unless you intend to
+ implement your own text rendering for some specialized widget, you
+ probably won't need to use it directly.
+
+ QTextLayout can currently deal with plain text and rich text
+ paragraphs that are part of a QTextDocument.
+
+ QTextLayout can be used to create a sequence of QTextLine's with
+ given widths and can position them independently on the screen.
+ Once the layout is done, these lines can be drawn on a paint
+ device.
+
+ Here's some pseudo code that presents the layout phase:
+ \snippet doc/src/snippets/code/src_gui_text_qtextlayout.cpp 0
+
+ The text can be drawn by calling the layout's draw() function:
+ \snippet doc/src/snippets/code/src_gui_text_qtextlayout.cpp 1
+
+ The text layout's text is set in the constructor or with
+ setText(). The layout can be seen as a sequence of QTextLine
+ objects; use lineAt() or lineForTextPosition() to get a QTextLine,
+ createLine() to create one. For a given position in the text you
+ can find a valid cursor position with isValidCursorPosition(),
+ nextCursorPosition(), and previousCursorPosition(). The layout
+ itself can be positioned with setPosition(); it has a
+ boundingRect(), and a minimumWidth() and a maximumWidth(). A text
+ layout can be drawn on a painter device using draw().
+
+*/
+
+/*!
+ \enum QTextLayout::CursorMode
+
+ \value SkipCharacters
+ \value SkipWords
+*/
+
+/*!
+ \fn QTextEngine *QTextLayout::engine() const
+ \internal
+
+ Returns the text engine used to render the text layout.
+*/
+
+/*!
+ Constructs an empty text layout.
+
+ \sa setText()
+*/
+QTextLayout::QTextLayout()
+{ d = new QTextEngine(); }
+
+/*!
+ Constructs a text layout to lay out the given \a text.
+*/
+QTextLayout::QTextLayout(const QString& text)
+{
+ d = new QTextEngine();
+ d->text = text;
+}
+
+/*!
+ Constructs a text layout to lay out the given \a text with the specified
+ \a font.
+
+ All the metric and layout calculations will be done in terms of
+ the paint device, \a paintdevice. If \a paintdevice is 0 the
+ calculations will be done in screen metrics.
+*/
+QTextLayout::QTextLayout(const QString& text, const QFont &font, QPaintDevice *paintdevice)
+{
+ QFont f(font);
+ if (paintdevice)
+ f = QFont(font, paintdevice);
+ d = new QTextEngine((text.isNull() ? (const QString&)QString::fromLatin1("") : text), f.d);
+}
+
+/*!
+ \internal
+ Constructs a text layout to lay out the given \a block.
+*/
+QTextLayout::QTextLayout(const QTextBlock &block)
+{
+ d = new QTextEngine();
+ d->block = block;
+}
+
+/*!
+ Destructs the layout.
+*/
+QTextLayout::~QTextLayout()
+{
+ if (!d->stackEngine)
+ delete d;
+}
+
+/*!
+ Sets the layout's font to the given \a font. The layout is
+ invalidated and must be laid out again.
+
+ \sa text()
+*/
+void QTextLayout::setFont(const QFont &font)
+{
+ d->fnt = font;
+}
+
+/*!
+ Returns the current font that is used for the layout, or a default
+ font if none is set.
+*/
+QFont QTextLayout::font() const
+{
+ return d->font();
+}
+
+/*!
+ Sets the layout's text to the given \a string. The layout is
+ invalidated and must be laid out again.
+
+ Notice that when using this QTextLayout as part of a QTextDocument this
+ method will have no effect.
+
+ \sa text()
+*/
+void QTextLayout::setText(const QString& string)
+{
+ d->invalidate();
+ d->clearLineData();
+ d->text = string;
+}
+
+/*!
+ Returns the layout's text.
+
+ \sa setText()
+*/
+QString QTextLayout::text() const
+{
+ return d->text;
+}
+
+/*!
+ Sets the text option structure that controls the layout process to the
+ given \a option.
+
+ \sa textOption() QTextOption
+*/
+void QTextLayout::setTextOption(const QTextOption &option)
+{
+ d->option = option;
+}
+
+/*!
+ Returns the current text option used to control the layout process.
+
+ \sa setTextOption() QTextOption
+*/
+QTextOption QTextLayout::textOption() const
+{
+ return d->option;
+}
+
+/*!
+ Sets the \a position and \a text of the area in the layout that is
+ processed before editing occurs.
+*/
+void QTextLayout::setPreeditArea(int position, const QString &text)
+{
+ if (text.isEmpty()) {
+ if (!d->specialData)
+ return;
+ if (d->specialData->addFormats.isEmpty()) {
+ delete d->specialData;
+ d->specialData = 0;
+ } else {
+ d->specialData->preeditText = QString();
+ d->specialData->preeditPosition = -1;
+ }
+ } else {
+ if (!d->specialData)
+ d->specialData = new QTextEngine::SpecialData;
+ d->specialData->preeditPosition = position;
+ d->specialData->preeditText = text;
+ }
+ d->invalidate();
+ d->clearLineData();
+ if (d->block.docHandle())
+ d->block.docHandle()->documentChange(d->block.position(), d->block.length());
+}
+
+/*!
+ Returns the position of the area in the text layout that will be
+ processed before editing occurs.
+*/
+int QTextLayout::preeditAreaPosition() const
+{
+ return d->specialData ? d->specialData->preeditPosition : -1;
+}
+
+/*!
+ Returns the text that is inserted in the layout before editing occurs.
+*/
+QString QTextLayout::preeditAreaText() const
+{
+ return d->specialData ? d->specialData->preeditText : QString();
+}
+
+
+/*!
+ Sets the additional formats supported by the text layout to \a
+ formatList.
+
+ \sa additionalFormats(), clearAdditionalFormats()
+*/
+void QTextLayout::setAdditionalFormats(const QList<FormatRange> &formatList)
+{
+ if (formatList.isEmpty()) {
+ if (!d->specialData)
+ return;
+ if (d->specialData->preeditText.isEmpty()) {
+ delete d->specialData;
+ d->specialData = 0;
+ } else {
+ d->specialData->addFormats = formatList;
+ d->specialData->addFormatIndices.clear();
+ }
+ } else {
+ if (!d->specialData) {
+ d->specialData = new QTextEngine::SpecialData;
+ d->specialData->preeditPosition = -1;
+ }
+ d->specialData->addFormats = formatList;
+ d->indexAdditionalFormats();
+ }
+ if (d->block.docHandle())
+ d->block.docHandle()->documentChange(d->block.position(), d->block.length());
+}
+
+/*!
+ Returns the list of additional formats supported by the text layout.
+
+ \sa setAdditionalFormats(), clearAdditionalFormats()
+*/
+QList<QTextLayout::FormatRange> QTextLayout::additionalFormats() const
+{
+ QList<FormatRange> formats;
+ if (!d->specialData)
+ return formats;
+
+ formats = d->specialData->addFormats;
+
+ if (d->specialData->addFormatIndices.isEmpty())
+ return formats;
+
+ const QTextFormatCollection *collection = d->formats();
+
+ for (int i = 0; i < d->specialData->addFormatIndices.count(); ++i)
+ formats[i].format = collection->charFormat(d->specialData->addFormatIndices.at(i));
+
+ return formats;
+}
+
+/*!
+ Clears the list of additional formats supported by the text layout.
+
+ \sa additionalFormats(), setAdditionalFormats()
+*/
+void QTextLayout::clearAdditionalFormats()
+{
+ setAdditionalFormats(QList<FormatRange>());
+}
+
+/*!
+ Enables caching of the complete layout information if \a enable is
+ true; otherwise disables layout caching. Usually
+ QTextLayout throws most of the layouting information away after a
+ call to endLayout() to reduce memory consumption. If you however
+ want to draw the laid out text directly afterwards enabling caching
+ might speed up drawing significantly.
+
+ \sa cacheEnabled()
+*/
+void QTextLayout::setCacheEnabled(bool enable)
+{
+ d->cacheGlyphs = enable;
+}
+
+/*!
+ Returns true if the complete layout information is cached; otherwise
+ returns false.
+
+ \sa setCacheEnabled()
+*/
+bool QTextLayout::cacheEnabled() const
+{
+ return d->cacheGlyphs;
+}
+
+/*!
+ Begins the layout process.
+*/
+void QTextLayout::beginLayout()
+{
+#ifndef QT_NO_DEBUG
+ if (d->layoutData && d->layoutData->inLayout) {
+ qWarning("QTextLayout::beginLayout: Called while already doing layout");
+ return;
+ }
+#endif
+ d->invalidate();
+ d->clearLineData();
+ d->itemize();
+ d->layoutData->inLayout = true;
+}
+
+/*!
+ Ends the layout process.
+*/
+void QTextLayout::endLayout()
+{
+#ifndef QT_NO_DEBUG
+ if (!d->layoutData || !d->layoutData->inLayout) {
+ qWarning("QTextLayout::endLayout: Called without beginLayout()");
+ return;
+ }
+#endif
+ int l = d->lines.size();
+ if (l && d->lines.at(l-1).length < 0) {
+ QTextLine(l-1, d).setNumColumns(INT_MAX);
+ }
+ d->layoutData->inLayout = false;
+ if (!d->cacheGlyphs)
+ d->freeMemory();
+}
+
+/*! \since 4.4
+
+Clears the line information in the layout. After having called
+this function, lineCount() returns 0.
+ */
+void QTextLayout::clearLayout()
+{
+ d->clearLineData();
+}
+
+
+/*!
+ Returns the next valid cursor position after \a oldPos that
+ respects the given cursor \a mode.
+
+ \sa isValidCursorPosition() previousCursorPosition()
+*/
+int QTextLayout::nextCursorPosition(int oldPos, CursorMode mode) const
+{
+// qDebug("looking for next cursor pos for %d", oldPos);
+ const HB_CharAttributes *attributes = d->attributes();
+ if (!attributes)
+ return 0;
+ int len = d->block.isValid() ?
+ (d->block.length() - 1)
+ : d->layoutData->string.length();
+
+ if (oldPos >= len)
+ return oldPos;
+ if (mode == SkipCharacters) {
+ oldPos++;
+ while (oldPos < len && !attributes[oldPos].charStop)
+ oldPos++;
+ } else {
+ if (oldPos < len && d->atWordSeparator(oldPos)) {
+ oldPos++;
+ while (oldPos < len && d->atWordSeparator(oldPos))
+ oldPos++;
+ } else {
+ while (oldPos < len && !d->atSpace(oldPos) && !d->atWordSeparator(oldPos))
+ oldPos++;
+ }
+ while (oldPos < len && d->atSpace(oldPos))
+ oldPos++;
+ }
+// qDebug(" -> %d", oldPos);
+ return oldPos;
+}
+
+/*!
+ Returns the first valid cursor position before \a oldPos that
+ respects the given cursor \a mode.
+
+ \sa isValidCursorPosition() nextCursorPosition()
+*/
+int QTextLayout::previousCursorPosition(int oldPos, CursorMode mode) const
+{
+// qDebug("looking for previous cursor pos for %d", oldPos);
+ const HB_CharAttributes *attributes = d->attributes();
+ if (!attributes || oldPos <= 0)
+ return 0;
+ if (mode == SkipCharacters) {
+ oldPos--;
+ while (oldPos && !attributes[oldPos].charStop)
+ oldPos--;
+ } else {
+ while (oldPos && d->atSpace(oldPos-1))
+ oldPos--;
+
+ if (oldPos && d->atWordSeparator(oldPos-1)) {
+ oldPos--;
+ while (oldPos && d->atWordSeparator(oldPos-1))
+ oldPos--;
+ } else {
+ while (oldPos && !d->atSpace(oldPos-1) && !d->atWordSeparator(oldPos-1))
+ oldPos--;
+ }
+ }
+// qDebug(" -> %d", oldPos);
+ return oldPos;
+}
+
+/*!
+ Returns true if position \a pos is a valid cursor position.
+
+ In a Unicode context some positions in the text are not valid
+ cursor positions, because the position is inside a Unicode
+ surrogate or a grapheme cluster.
+
+ A grapheme cluster is a sequence of two or more Unicode characters
+ that form one indivisible entity on the screen. For example the
+ latin character `\Auml' can be represented in Unicode by two
+ characters, `A' (0x41), and the combining diaresis (0x308). A text
+ cursor can only validly be positioned before or after these two
+ characters, never between them since that wouldn't make sense. In
+ indic languages every syllable forms a grapheme cluster.
+*/
+bool QTextLayout::isValidCursorPosition(int pos) const
+{
+ const HB_CharAttributes *attributes = d->attributes();
+ if (!attributes || pos < 0 || pos > (int)d->layoutData->string.length())
+ return false;
+ return attributes[pos].charStop;
+}
+
+
+/*!
+ Returns a new text line to be laid out if there is text to be
+ inserted into the layout; otherwise returns an invalid text line.
+
+ The text layout creates a new line object that starts after the
+ last line in the layout, or at the beginning if the layout is empty.
+ The layout maintains an internal cursor, and each line is filled
+ with text from the cursor position onwards when the
+ QTextLine::setLineWidth() function is called.
+
+ Once QTextLine::setLineWidth() is called, a new line can be created and
+ filled with text. Repeating this process will lay out the whole block
+ of text contained in the QTextLayout. If there is no text left to be
+ inserted into the layout, the QTextLine returned will not be valid
+ (isValid() will return false).
+*/
+QTextLine QTextLayout::createLine()
+{
+#ifndef QT_NO_DEBUG
+ if (!d->layoutData || !d->layoutData->inLayout) {
+ qWarning("QTextLayout::createLine: Called without layouting");
+ return QTextLine();
+ }
+#endif
+ int l = d->lines.size();
+ if (l && d->lines.at(l-1).length < 0) {
+ QTextLine(l-1, d).setNumColumns(INT_MAX);
+ }
+ int from = l > 0 ? d->lines.at(l-1).from + d->lines.at(l-1).length : 0;
+ int strlen = d->layoutData->string.length();
+ if (l && from >= strlen) {
+ if (!d->lines.at(l-1).length || d->layoutData->string.at(strlen - 1) != QChar::LineSeparator)
+ return QTextLine();
+ }
+
+ QScriptLine line;
+ line.from = from;
+ line.length = -1;
+ line.justified = false;
+ line.gridfitted = false;
+
+ d->lines.append(line);
+ return QTextLine(l, d);
+}
+
+/*!
+ Returns the number of lines in this text layout.
+
+ \sa lineAt()
+*/
+int QTextLayout::lineCount() const
+{
+ return d->lines.size();
+}
+
+/*!
+ Returns the \a{i}-th line of text in this text layout.
+
+ \sa lineCount() lineForTextPosition()
+*/
+QTextLine QTextLayout::lineAt(int i) const
+{
+ return QTextLine(i, d);
+}
+
+/*!
+ Returns the line that contains the cursor position specified by \a pos.
+
+ \sa isValidCursorPosition() lineAt()
+*/
+QTextLine QTextLayout::lineForTextPosition(int pos) const
+{
+ for (int i = 0; i < d->lines.size(); ++i) {
+ const QScriptLine& line = d->lines[i];
+ if (line.from + (int)line.length > pos)
+ return QTextLine(i, d);
+ }
+ if (!d->layoutData)
+ d->itemize();
+ if (pos == d->layoutData->string.length() && d->lines.size())
+ return QTextLine(d->lines.size()-1, d);
+ return QTextLine();
+}
+
+/*!
+ \since 4.2
+
+ The global position of the layout. This is independent of the
+ bounding rectangle and of the layout process.
+
+ \sa setPosition()
+*/
+QPointF QTextLayout::position() const
+{
+ return d->position;
+}
+
+/*!
+ Moves the text layout to point \a p.
+
+ \sa position()
+*/
+void QTextLayout::setPosition(const QPointF &p)
+{
+ d->position = p;
+}
+
+/*!
+ The smallest rectangle that contains all the lines in the layout.
+*/
+QRectF QTextLayout::boundingRect() const
+{
+ if (d->lines.isEmpty())
+ return QRectF();
+
+ QFixed xmax, ymax;
+ QFixed xmin = d->lines.at(0).x;
+ QFixed ymin = d->lines.at(0).y;
+
+ for (int i = 0; i < d->lines.size(); ++i) {
+ const QScriptLine &si = d->lines[i];
+ xmin = qMin(xmin, si.x);
+ ymin = qMin(ymin, si.y);
+ xmax = qMax(xmax, si.x+qMax(si.width, si.textWidth));
+ // ### shouldn't the ascent be used in ymin???
+ ymax = qMax(ymax, si.y+si.ascent+si.descent+1);
+ }
+ return QRectF(xmin.toReal(), ymin.toReal(), (xmax-xmin).toReal(), (ymax-ymin).toReal());
+}
+
+/*!
+ The minimum width the layout needs. This is the width of the
+ layout's smallest non-breakable substring.
+
+ \warning This function only returns a valid value after the layout
+ has been done.
+
+ \sa maximumWidth()
+*/
+qreal QTextLayout::minimumWidth() const
+{
+ return d->minWidth.toReal();
+}
+
+/*!
+ The maximum width the layout could expand to; this is essentially
+ the width of the entire text.
+
+ \warning This function only returns a valid value after the layout
+ has been done.
+
+ \sa minimumWidth()
+*/
+qreal QTextLayout::maximumWidth() const
+{
+ return d->maxWidth.toReal();
+}
+
+/*!
+ \internal
+*/
+void QTextLayout::setFlags(int flags)
+{
+ if (flags & Qt::TextJustificationForced) {
+ d->option.setAlignment(Qt::AlignJustify);
+ d->forceJustification = true;
+ }
+
+ if (flags & (Qt::TextForceLeftToRight|Qt::TextForceRightToLeft)) {
+ d->ignoreBidi = true;
+ d->option.setTextDirection((flags & Qt::TextForceLeftToRight) ? Qt::LeftToRight : Qt::RightToLeft);
+ }
+}
+
+struct QTextLineItemIterator
+{
+ QTextLineItemIterator(QTextEngine *eng, int lineNum, const QPointF &pos = QPointF(),
+ const QTextLayout::FormatRange *_selection = 0);
+
+ inline bool atEnd() const { return logicalItem >= nItems - 1; }
+ QScriptItem &next();
+
+ bool getSelectionBounds(QFixed *selectionX, QFixed *selectionWidth) const;
+ inline bool isOutsideSelection() const {
+ QFixed tmp1, tmp2;
+ return !getSelectionBounds(&tmp1, &tmp2);
+ }
+
+ QTextEngine *eng;
+
+ QFixed x;
+ QFixed pos_x;
+ const QScriptLine &line;
+ QScriptItem *si;
+
+ int lineEnd;
+ int firstItem;
+ int lastItem;
+ int nItems;
+ int logicalItem;
+ int item;
+ int itemLength;
+
+ int glyphsStart;
+ int glyphsEnd;
+ int itemStart;
+ int itemEnd;
+
+ QFixed itemWidth;
+
+ QVarLengthArray<int> visualOrder;
+ QVarLengthArray<uchar> levels;
+
+ const QTextLayout::FormatRange *selection;
+};
+
+QTextLineItemIterator::QTextLineItemIterator(QTextEngine *_eng, int lineNum, const QPointF &pos,
+ const QTextLayout::FormatRange *_selection)
+ : eng(_eng),
+ line(eng->lines[lineNum]),
+ si(0),
+ lineEnd(line.from + line.length),
+ firstItem(eng->findItem(line.from)),
+ lastItem(eng->findItem(lineEnd - 1)),
+ nItems((firstItem >= 0 && lastItem >= firstItem)? (lastItem-firstItem+1) : 0),
+ logicalItem(-1),
+ item(-1),
+ visualOrder(nItems),
+ levels(nItems),
+ selection(_selection)
+{
+ pos_x = x = QFixed::fromReal(pos.x());
+
+ x += line.x;
+
+ x += alignLine(eng, line);
+
+ for (int i = 0; i < nItems; ++i)
+ levels[i] = eng->layoutData->items[i+firstItem].analysis.bidiLevel;
+ QTextEngine::bidiReorder(nItems, levels.data(), visualOrder.data());
+
+ eng->shapeLine(line);
+}
+
+QScriptItem &QTextLineItemIterator::next()
+{
+ x += itemWidth;
+
+ ++logicalItem;
+ item = visualOrder[logicalItem] + firstItem;
+ itemLength = eng->length(item);
+ si = &eng->layoutData->items[item];
+ if (!si->num_glyphs)
+ eng->shape(item);
+
+ if (si->analysis.flags >= QScriptAnalysis::TabOrObject) {
+ itemWidth = si->width;
+ return *si;
+ }
+
+ unsigned short *logClusters = eng->logClusters(si);
+ QGlyphLayout glyphs = eng->shapedGlyphs(si);
+
+ itemStart = qMax(line.from, si->position);
+ glyphsStart = logClusters[itemStart - si->position];
+ if (lineEnd < si->position + itemLength) {
+ itemEnd = lineEnd;
+ glyphsEnd = logClusters[itemEnd-si->position];
+ } else {
+ itemEnd = si->position + itemLength;
+ glyphsEnd = si->num_glyphs;
+ }
+ // show soft-hyphen at line-break
+ if (si->position + itemLength >= lineEnd
+ && eng->layoutData->string.at(lineEnd - 1) == 0x00ad)
+ glyphs.attributes[glyphsEnd - 1].dontPrint = false;
+
+ itemWidth = 0;
+ for (int g = glyphsStart; g < glyphsEnd; ++g)
+ itemWidth += glyphs.effectiveAdvance(g);
+
+ return *si;
+}
+
+bool QTextLineItemIterator::getSelectionBounds(QFixed *selectionX, QFixed *selectionWidth) const
+{
+ *selectionX = *selectionWidth = 0;
+
+ if (!selection)
+ return false;
+
+ if (si->analysis.flags >= QScriptAnalysis::TabOrObject) {
+ if (si->position >= selection->start + selection->length
+ || si->position + itemLength <= selection->start)
+ return false;
+
+ *selectionX = x;
+ *selectionWidth = itemWidth;
+ } else {
+ unsigned short *logClusters = eng->logClusters(si);
+ QGlyphLayout glyphs = eng->shapedGlyphs(si);
+
+ int from = qMax(itemStart, selection->start) - si->position;
+ int to = qMin(itemEnd, selection->start + selection->length) - si->position;
+ if (from >= to)
+ return false;
+
+ int start_glyph = logClusters[from];
+ int end_glyph = (to == eng->length(item)) ? si->num_glyphs : logClusters[to];
+ QFixed soff;
+ QFixed swidth;
+ if (si->analysis.bidiLevel %2) {
+ for (int g = glyphsEnd - 1; g >= end_glyph; --g)
+ soff += glyphs.effectiveAdvance(g);
+ for (int g = end_glyph - 1; g >= start_glyph; --g)
+ swidth += glyphs.effectiveAdvance(g);
+ } else {
+ for (int g = glyphsStart; g < start_glyph; ++g)
+ soff += glyphs.effectiveAdvance(g);
+ for (int g = start_glyph; g < end_glyph; ++g)
+ swidth += glyphs.effectiveAdvance(g);
+ }
+
+ *selectionX = x + soff;
+ *selectionWidth = swidth;
+ }
+ return true;
+}
+
+static void addSelectedRegionsToPath(QTextEngine *eng, int lineNumber, const QPointF &pos, QTextLayout::FormatRange *selection,
+ QPainterPath *region, QRectF boundingRect)
+{
+ const QScriptLine &line = eng->lines[lineNumber];
+
+ QTextLineItemIterator iterator(eng, lineNumber, pos, selection);
+
+ const QFixed y = QFixed::fromReal(pos.y()) + line.y + line.ascent;
+
+ const qreal lineHeight = line.height().toReal();
+ const qreal selectionY = (y - line.ascent).toReal();
+
+ QFixed lastSelectionX = iterator.x;
+ QFixed lastSelectionWidth;
+
+ while (!iterator.atEnd()) {
+ iterator.next();
+
+ QFixed selectionX, selectionWidth;
+ if (iterator.getSelectionBounds(&selectionX, &selectionWidth)) {
+ if (selectionX == lastSelectionX + lastSelectionWidth) {
+ lastSelectionWidth += selectionWidth;
+ continue;
+ }
+
+ if (lastSelectionWidth > 0)
+ region->addRect(boundingRect & QRectF(lastSelectionX.toReal(), selectionY, lastSelectionWidth.toReal(), lineHeight));
+
+ lastSelectionX = selectionX;
+ lastSelectionWidth = selectionWidth;
+ }
+ }
+ if (lastSelectionWidth > 0)
+ region->addRect(boundingRect & QRectF(lastSelectionX.toReal(), selectionY, lastSelectionWidth.toReal(), lineHeight));
+}
+
+static inline QRectF clipIfValid(const QRectF &rect, const QRectF &clip)
+{
+ return clip.isValid() ? (rect & clip) : rect;
+}
+
+/*!
+ Draws the whole layout on the painter \a p at the position specified by
+ \a pos.
+ The rendered layout includes the given \a selections and is clipped within
+ the rectangle specified by \a clip.
+*/
+void QTextLayout::draw(QPainter *p, const QPointF &pos, const QVector<FormatRange> &selections, const QRectF &clip) const
+{
+ if (d->lines.isEmpty())
+ return;
+
+ if (!d->layoutData)
+ d->itemize();
+
+ QPointF position = pos + d->position;
+
+ QFixed clipy = (INT_MIN/256);
+ QFixed clipe = (INT_MAX/256);
+ if (clip.isValid()) {
+ clipy = QFixed::fromReal(clip.y() - position.y());
+ clipe = clipy + QFixed::fromReal(clip.height());
+ }
+
+ int firstLine = 0;
+ int lastLine = d->lines.size();
+ for (int i = 0; i < d->lines.size(); ++i) {
+ QTextLine l(i, d);
+ const QScriptLine &sl = d->lines[i];
+
+ if (sl.y > clipe) {
+ lastLine = i;
+ break;
+ }
+ if ((sl.y + sl.height()) < clipy) {
+ firstLine = i;
+ continue;
+ }
+ }
+
+ QPainterPath excludedRegion;
+ for (int i = 0; i < selections.size(); ++i) {
+ FormatRange selection = selections.at(i);
+ const QBrush bg = selection.format.background();
+
+ QPainterPath region;
+ region.setFillRule(Qt::WindingFill);
+
+ for (int line = firstLine; line < lastLine; ++line) {
+ const QScriptLine &sl = d->lines[line];
+ QTextLine tl(line, d);
+
+ QRectF lineRect(tl.naturalTextRect());
+ lineRect.translate(position);
+
+ bool isLastLineInBlock = (line == d->lines.size()-1);
+ int sl_length = sl.length + (isLastLineInBlock? 1 : 0); // the infamous newline
+
+
+ if (sl.from > selection.start + selection.length || sl.from + sl_length <= selection.start)
+ continue; // no actual intersection
+
+ const bool selectionStartInLine = sl.from <= selection.start;
+ const bool selectionEndInLine = selection.start + selection.length < sl.from + sl_length;
+
+ if (sl.length && (selectionStartInLine || selectionEndInLine)) {
+ addSelectedRegionsToPath(d, line, position, &selection, &region, clipIfValid(lineRect, clip));
+ } else {
+ region.addRect(clipIfValid(lineRect, clip));
+ }
+
+ if (selection.format.boolProperty(QTextFormat::FullWidthSelection)) {
+ QRectF fullLineRect(tl.rect());
+ fullLineRect.translate(position);
+ fullLineRect.setRight(QFIXED_MAX);
+ if (!selectionEndInLine)
+ region.addRect(clipIfValid(QRectF(lineRect.topRight(), fullLineRect.bottomRight()), clip));
+ if (!selectionStartInLine)
+ region.addRect(clipIfValid(QRectF(fullLineRect.topLeft(), lineRect.bottomLeft()), clip));
+ } else if (!selectionEndInLine
+ && isLastLineInBlock
+ &&!(d->option.flags() & QTextOption::ShowLineAndParagraphSeparators)) {
+ region.addRect(clipIfValid(QRectF(lineRect.right(), lineRect.top(),
+ lineRect.height()/4, lineRect.height()), clip));
+ }
+
+ }
+ {
+ const QPen oldPen = p->pen();
+ const QBrush oldBrush = p->brush();
+
+ p->setPen(selection.format.penProperty(QTextFormat::OutlinePen));
+ p->setBrush(selection.format.brushProperty(QTextFormat::BackgroundBrush));
+ p->drawPath(region);
+
+ p->setPen(oldPen);
+ p->setBrush(oldBrush);
+ }
+
+
+ p->save();
+ p->setClipPath(region, Qt::IntersectClip);
+
+ selection.format.setProperty(ObjectSelectionBrush, selection.format.property(QTextFormat::BackgroundBrush));
+ // don't just clear the property, set an empty brush that overrides a potential
+ // background brush specified in the text
+ selection.format.setProperty(QTextFormat::BackgroundBrush, QBrush());
+ selection.format.clearProperty(QTextFormat::OutlinePen);
+
+ for (int line = firstLine; line < lastLine; ++line) {
+ QTextLine l(line, d);
+ l.draw(p, position, &selection);
+ }
+ p->restore();
+
+ if (selection.format.foreground().style() != Qt::NoBrush) // i.e. we have drawn text
+ excludedRegion += region;
+ }
+
+ if (!excludedRegion.isEmpty()) {
+ p->save();
+ QPainterPath path;
+ QRectF br = boundingRect().translated(position);
+ br.setRight(QFIXED_MAX);
+ if (!clip.isNull())
+ br = br.intersected(clip);
+ path.addRect(br);
+ path -= excludedRegion;
+ p->setClipPath(path, Qt::IntersectClip);
+ }
+
+ for (int i = firstLine; i < lastLine; ++i) {
+ QTextLine l(i, d);
+ l.draw(p, position);
+ }
+ if (!excludedRegion.isEmpty())
+ p->restore();
+
+
+ if (!d->cacheGlyphs)
+ d->freeMemory();
+}
+
+/*!
+ \fn void QTextLayout::drawCursor(QPainter *painter, const QPointF &position, int cursorPosition) const
+ \overload
+
+ Draws a text cursor with the current pen at the given \a position using the
+ \a painter specified.
+ The corresponding position within the text is specified by \a cursorPosition.
+*/
+void QTextLayout::drawCursor(QPainter *p, const QPointF &pos, int cursorPosition) const
+{
+ drawCursor(p, pos, cursorPosition, 1);
+}
+
+/*!
+ \fn void QTextLayout::drawCursor(QPainter *painter, const QPointF &position, int cursorPosition, int width) const
+
+ Draws a text cursor with the current pen and the specified \a width at the given \a position using the
+ \a painter specified.
+ The corresponding position within the text is specified by \a cursorPosition.
+*/
+void QTextLayout::drawCursor(QPainter *p, const QPointF &pos, int cursorPosition, int width) const
+{
+ if (d->lines.isEmpty())
+ return;
+
+ if (!d->layoutData)
+ d->itemize();
+
+ QPointF position = pos + d->position;
+ QFixed pos_x = QFixed::fromReal(position.x());
+ QFixed pos_y = QFixed::fromReal(position.y());
+
+ cursorPosition = qBound(0, cursorPosition, d->layoutData->string.length());
+ int line = 0;
+ if (cursorPosition == d->layoutData->string.length()) {
+ line = d->lines.size() - 1;
+ } else {
+ // ### binary search
+ for (line = 0; line < d->lines.size(); line++) {
+ const QScriptLine &sl = d->lines[line];
+ if (sl.from <= cursorPosition && sl.from + (int)sl.length > cursorPosition)
+ break;
+ }
+ }
+
+ if (line >= d->lines.size())
+ return;
+
+ QTextLine l(line, d);
+ const QScriptLine &sl = d->lines[line];
+
+ const qreal x = position.x() + l.cursorToX(cursorPosition);
+
+ int itm = d->findItem(cursorPosition - 1);
+ QFixed ascent = sl.ascent;
+ QFixed descent = sl.descent;
+ bool rightToLeft = (d->option.textDirection() == Qt::RightToLeft);
+ if (itm >= 0) {
+ const QScriptItem &si = d->layoutData->items.at(itm);
+ if (si.ascent > 0)
+ ascent = si.ascent;
+ if (si.descent > 0)
+ descent = si.descent;
+ rightToLeft = si.analysis.bidiLevel % 2;
+ }
+ qreal y = position.y() + (sl.y + sl.ascent - ascent).toReal();
+ bool toggleAntialiasing = !(p->renderHints() & QPainter::Antialiasing)
+ && (p->transform().type() > QTransform::TxTranslate);
+ if (toggleAntialiasing)
+ p->setRenderHint(QPainter::Antialiasing);
+ p->fillRect(QRectF(x, y, qreal(width), (ascent + descent).toReal()), p->pen().brush());
+ if (toggleAntialiasing)
+ p->setRenderHint(QPainter::Antialiasing, false);
+ if (d->layoutData->hasBidi) {
+ const int arrow_extent = 4;
+ int sign = rightToLeft ? -1 : 1;
+ p->drawLine(QLineF(x, y, x + (sign * arrow_extent/2), y + arrow_extent/2));
+ p->drawLine(QLineF(x, y+arrow_extent, x + (sign * arrow_extent/2), y + arrow_extent/2));
+ }
+ return;
+}
+
+/*!
+ \class QTextLine
+ \reentrant
+
+ \brief The QTextLine class represents a line of text inside a QTextLayout.
+
+ \ingroup text
+
+ A text line is usually created by QTextLayout::createLine().
+
+ After being created, the line can be filled using the setLineWidth()
+ or setNumColumns() functions. A line has a number of attributes including the
+ rectangle it occupies, rect(), its coordinates, x() and y(), its
+ textLength(), width() and naturalTextWidth(), and its ascent() and decent()
+ relative to the text. The position of the cursor in terms of the
+ line is available from cursorToX() and its inverse from
+ xToCursor(). A line can be moved with setPosition().
+*/
+
+/*!
+ \enum QTextLine::Edge
+
+ \value Leading
+ \value Trailing
+*/
+
+/*!
+ \enum QTextLine::CursorPosition
+
+ \value CursorBetweenCharacters
+ \value CursorOnCharacter
+*/
+
+/*!
+ \fn QTextLine::QTextLine(int line, QTextEngine *e)
+ \internal
+
+ Constructs a new text line using the line at position \a line in
+ the text engine \a e.
+*/
+
+/*!
+ \fn QTextLine::QTextLine()
+
+ Creates an invalid line.
+*/
+
+/*!
+ \fn bool QTextLine::isValid() const
+
+ Returns true if this text line is valid; otherwise returns false.
+*/
+
+/*!
+ \fn int QTextLine::lineNumber() const
+
+ Returns the position of the line in the text engine.
+*/
+
+
+/*!
+ Returns the line's bounding rectangle.
+
+ \sa x() y() textLength() width()
+*/
+QRectF QTextLine::rect() const
+{
+ const QScriptLine& sl = eng->lines[i];
+ return QRectF(sl.x.toReal(), sl.y.toReal(), sl.width.toReal(), sl.height().toReal());
+}
+
+/*!
+ Returns the rectangle covered by the line.
+*/
+QRectF QTextLine::naturalTextRect() const
+{
+ const QScriptLine& sl = eng->lines[i];
+ QFixed x = sl.x + alignLine(eng, sl);
+
+ QFixed width = sl.textWidth;
+ if (sl.justified)
+ width = sl.width;
+
+ return QRectF(x.toReal(), sl.y.toReal(), width.toReal(), sl.height().toReal());
+}
+
+/*!
+ Returns the line's x position.
+
+ \sa rect() y() textLength() width()
+*/
+qreal QTextLine::x() const
+{
+ return eng->lines[i].x.toReal();
+}
+
+/*!
+ Returns the line's y position.
+
+ \sa x() rect() textLength() width()
+*/
+qreal QTextLine::y() const
+{
+ return eng->lines[i].y.toReal();
+}
+
+/*!
+ Returns the line's width as specified by the layout() function.
+
+ \sa naturalTextWidth() x() y() textLength() rect()
+*/
+qreal QTextLine::width() const
+{
+ return eng->lines[i].width.toReal();
+}
+
+
+/*!
+ Returns the line's ascent.
+
+ \sa descent() height()
+*/
+qreal QTextLine::ascent() const
+{
+ return eng->lines[i].ascent.toReal();
+}
+
+/*!
+ Returns the line's descent.
+
+ \sa ascent() height()
+*/
+qreal QTextLine::descent() const
+{
+ return eng->lines[i].descent.toReal();
+}
+
+/*!
+ Returns the line's height. This is equal to ascent() + descent() + 1.
+
+ \sa ascent() descent()
+*/
+qreal QTextLine::height() const
+{
+ return eng->lines[i].height().toReal();
+}
+
+/*!
+ Returns the width of the line that is occupied by text. This is
+ always \<= to width(), and is the minimum width that could be used
+ by layout() without changing the line break position.
+*/
+qreal QTextLine::naturalTextWidth() const
+{
+ return eng->lines[i].textWidth.toReal();
+}
+
+/*!
+ Lays out the line with the given \a width. The line is filled from
+ its starting position with as many characters as will fit into
+ the line. In case the text cannot be split at the end of the line,
+ it will be filled with additional characters to the next whitespace
+ or end of the text.
+*/
+void QTextLine::setLineWidth(qreal width)
+{
+ QScriptLine &line = eng->lines[i];
+ if (!eng->layoutData) {
+ qWarning("QTextLine: Can't set a line width while not layouting.");
+ return;
+ }
+
+ if (width > QFIXED_MAX)
+ width = QFIXED_MAX;
+
+ line.width = QFixed::fromReal(width);
+ if (line.length
+ && line.textWidth <= line.width
+ && line.from + line.length == eng->layoutData->string.length())
+ // no need to do anything if the line is already layouted and the last one. This optimisation helps
+ // when using things in a single line layout.
+ return;
+ line.length = 0;
+ line.textWidth = 0;
+
+ layout_helper(INT_MAX);
+}
+
+/*!
+ Lays out the line. The line is filled from its starting position
+ with as many characters as are specified by \a numColumns. In case
+ the text cannot be split until \a numColumns characters, the line
+ will be filled with as many characters to the next whitespace or
+ end of the text.
+*/
+void QTextLine::setNumColumns(int numColumns)
+{
+ QScriptLine &line = eng->lines[i];
+ line.width = QFIXED_MAX;
+ line.length = 0;
+ line.textWidth = 0;
+ layout_helper(numColumns);
+}
+
+/*!
+ Lays out the line. The line is filled from its starting position
+ with as many characters as are specified by \a numColumns. In case
+ the text cannot be split until \a numColumns characters, the line
+ will be filled with as many characters to the next whitespace or
+ end of the text. The provided \a alignmentWidth is used as reference
+ width for alignment.
+*/
+void QTextLine::setNumColumns(int numColumns, qreal alignmentWidth)
+{
+ QScriptLine &line = eng->lines[i];
+ line.width = QFixed::fromReal(alignmentWidth);
+ line.length = 0;
+ line.textWidth = 0;
+ layout_helper(numColumns);
+}
+
+#if 0
+#define LB_DEBUG qDebug
+#else
+#define LB_DEBUG if (0) qDebug
+#endif
+
+static inline bool checkFullOtherwiseExtend(QScriptLine &line, QScriptLine &tmpData, QScriptLine &spaceData,
+ int glyphCount, int maxGlyphs, QFixed &minw, bool manualWrap,
+ QFixed softHyphenWidth = QFixed())
+{
+ LB_DEBUG("possible break width %f, spacew=%f", tmpData.textWidth.toReal(), spaceData.textWidth.toReal());
+ if (line.length && !manualWrap &&
+ (line.textWidth + tmpData.textWidth + spaceData.textWidth + softHyphenWidth > line.width || glyphCount > maxGlyphs))
+ return true;
+ minw = qMax(minw, tmpData.textWidth);
+ line += tmpData;
+ line.textWidth += spaceData.textWidth;
+ line.length += spaceData.length;
+ tmpData.textWidth = 0;
+ tmpData.length = 0;
+ spaceData.textWidth = 0;
+ spaceData.length = 0;
+ return false;
+}
+
+static inline void addNextCluster(int &pos, int end, QScriptLine &line, int &glyphCount,
+ const QScriptItem &current, const unsigned short *logClusters, const QGlyphLayout &glyphs)
+{
+ int glyphPosition = logClusters[pos];
+ do { // got to the first next cluster
+ ++pos;
+ ++line.length;
+ } while (pos < end && logClusters[pos] == glyphPosition);
+ do { // calculate the textWidth for the rest of the current cluster.
+ line.textWidth += glyphs.advances_x[glyphPosition] * !glyphs.attributes[glyphPosition].dontPrint;
+ ++glyphPosition;
+ } while (glyphPosition < current.num_glyphs && !glyphs.attributes[glyphPosition].clusterStart);
+
+ Q_ASSERT((pos == end && glyphPosition == current.num_glyphs) || logClusters[pos] == glyphPosition);
+
+ ++glyphCount;
+}
+
+
+// fill QScriptLine
+void QTextLine::layout_helper(int maxGlyphs)
+{
+ QScriptLine &line = eng->lines[i];
+ line.length = 0;
+ line.textWidth = 0;
+ line.hasTrailingSpaces = false;
+
+ if (!eng->layoutData->items.size() || line.from >= eng->layoutData->string.length()) {
+ line.setDefaultHeight(eng);
+ return;
+ }
+
+ Q_ASSERT(line.from < eng->layoutData->string.length());
+
+ QTextOption::WrapMode wrapMode = eng->option.wrapMode();
+ bool breakany = (wrapMode == QTextOption::WrapAnywhere);
+ bool manualWrap = (wrapMode == QTextOption::ManualWrap || wrapMode == QTextOption::NoWrap);
+
+ // #### binary search!
+ int item = -1;
+ int newItem;
+ for (newItem = eng->layoutData->items.size()-1; newItem > 0; --newItem) {
+ if (eng->layoutData->items[newItem].position <= line.from)
+ break;
+ }
+
+ QFixed minw = 0;
+ int glyphCount = 0;
+
+ LB_DEBUG("from: %d: item=%d, total %d, width available %f", line.from, newItem, eng->layoutData->items.size(), line.width.toReal());
+ QScriptLine tmpData;
+ QScriptLine spaceData;
+
+ Qt::Alignment alignment = eng->option.alignment();
+
+ const HB_CharAttributes *attributes = eng->attributes();
+ int pos = line.from;
+ int end = 0;
+ QGlyphLayout glyphs;
+ const unsigned short *logClusters = eng->layoutData->logClustersPtr;
+ while (newItem < eng->layoutData->items.size()) {
+ if (newItem != item) {
+ item = newItem;
+ const QScriptItem &current = eng->layoutData->items[item];
+ if (!current.num_glyphs) {
+ eng->shape(item);
+ attributes = eng->attributes();
+ logClusters = eng->layoutData->logClustersPtr;
+ }
+ pos = qMax(line.from, current.position);
+ end = current.position + eng->length(item);
+ glyphs = eng->shapedGlyphs(&current);
+ }
+ const QScriptItem &current = eng->layoutData->items[item];
+
+ tmpData.ascent = qMax(tmpData.ascent, current.ascent);
+ tmpData.descent = qMax(tmpData.descent, current.descent);
+
+ if (current.analysis.flags == QScriptAnalysis::Tab && (alignment & (Qt::AlignLeft | Qt::AlignRight | Qt::AlignCenter | Qt::AlignJustify))) {
+ if (checkFullOtherwiseExtend(line, tmpData, spaceData, glyphCount, maxGlyphs, minw, manualWrap))
+ goto found;
+
+ QFixed x = line.x + line.textWidth + tmpData.textWidth + spaceData.textWidth;
+ spaceData.textWidth += eng->calculateTabWidth(item, x);
+ spaceData.length++;
+ newItem = item + 1;
+ ++glyphCount;
+ if (checkFullOtherwiseExtend(line, tmpData, spaceData, glyphCount, maxGlyphs, minw, manualWrap))
+ goto found;
+ } else if (current.analysis.flags == QScriptAnalysis::LineOrParagraphSeparator) {
+ // if the line consists only of the line separator make sure
+ // we have a sane height
+ if (!line.length && !tmpData.length)
+ line.setDefaultHeight(eng);
+ if (eng->option.flags() & QTextOption::ShowLineAndParagraphSeparators) {
+ addNextCluster(pos, end, tmpData, glyphCount, current, logClusters, glyphs);
+ } else {
+ tmpData.length++;
+ }
+ line += tmpData;
+ goto found;
+ } else if (current.analysis.flags == QScriptAnalysis::Object) {
+ tmpData.length++;
+
+ QTextFormat format = eng->formats()->format(eng->formatIndex(&eng->layoutData->items[item]));
+ if (eng->block.docHandle())
+ eng->docLayout()->positionInlineObject(QTextInlineObject(item, eng), eng->block.position() + current.position, format);
+
+ tmpData.textWidth += current.width;
+
+ newItem = item + 1;
+ ++glyphCount;
+ if (checkFullOtherwiseExtend(line, tmpData, spaceData, glyphCount, maxGlyphs, minw, manualWrap))
+ goto found;
+ } else if (attributes[pos].whiteSpace) {
+ while (pos < end && attributes[pos].whiteSpace)
+ addNextCluster(pos, end, spaceData, glyphCount, current, logClusters, glyphs);
+
+ if (!manualWrap && spaceData.textWidth > line.width) {
+ spaceData.textWidth = line.width; // ignore spaces that fall out of the line.
+ goto found;
+ }
+ } else {
+ bool sb_or_ws = false;
+ do {
+ addNextCluster(pos, end, tmpData, glyphCount, current, logClusters, glyphs);
+
+ if (attributes[pos].whiteSpace || attributes[pos-1].lineBreakType != HB_NoBreak) {
+ sb_or_ws = true;
+ break;
+ } else if (breakany && attributes[pos].charStop) {
+ break;
+ }
+ } while (pos < end);
+ minw = qMax(tmpData.textWidth, minw);
+
+ QFixed softHyphenWidth;
+ if (pos && attributes[pos - 1].lineBreakType == HB_SoftHyphen) {
+ // if we are splitting up a word because of
+ // a soft hyphen then we ...
+ //
+ // a) have to take the width of the soft hyphen into
+ // account to see if the first syllable(s) /and/
+ // the soft hyphen fit into the line
+ //
+ // b) if we are so short of available width that the
+ // soft hyphen is the first breakable position, then
+ // we don't want to show it. However we initially
+ // have to take the width for it into accoun so that
+ // the text document layout sees the overflow and
+ // switch to break-anywhere mode, in which we
+ // want the soft-hyphen to slip into the next line
+ // and thus become invisible again.
+ //
+ if (line.length)
+ softHyphenWidth = glyphs.advances_x[logClusters[pos - 1]];
+ else if (breakany)
+ tmpData.textWidth += glyphs.advances_x[logClusters[pos - 1]];
+ }
+
+ if ((sb_or_ws|breakany)
+ && checkFullOtherwiseExtend(line, tmpData, spaceData, glyphCount, maxGlyphs, minw, manualWrap, softHyphenWidth)) {
+ if (!breakany) {
+ line.textWidth += softHyphenWidth;
+ }
+ goto found;
+ }
+ }
+ if (pos == end)
+ newItem = item + 1;
+ }
+ LB_DEBUG("reached end of line");
+ checkFullOtherwiseExtend(line, tmpData, spaceData, glyphCount, maxGlyphs, minw, manualWrap);
+found:
+ if (line.length == 0) {
+ LB_DEBUG("no break available in line, adding temp: length %d, width %f, space: length %d, width %f",
+ tmpData.length, tmpData.textWidth.toReal(), spaceData.length, spaceData.textWidth.toReal());
+ line += tmpData;
+ }
+
+ LB_DEBUG("line length = %d, ascent=%f, descent=%f, textWidth=%f (spacew=%f)", line.length, line.ascent.toReal(),
+ line.descent.toReal(), line.textWidth.toReal(), spaceData.width.toReal());
+ LB_DEBUG(" : '%s'", eng->layoutData->string.mid(line.from, line.length).toUtf8().data());
+
+ if (manualWrap) {
+ eng->minWidth = qMax(eng->minWidth, line.textWidth);
+ eng->maxWidth = qMax(eng->maxWidth, line.textWidth);
+ } else {
+ eng->minWidth = qMax(eng->minWidth, minw);
+ eng->maxWidth += line.textWidth;
+ }
+
+ if (line.textWidth > 0 && item < eng->layoutData->items.size())
+ eng->maxWidth += spaceData.textWidth;
+ if (eng->option.flags() & QTextOption::IncludeTrailingSpaces)
+ line.textWidth += spaceData.textWidth;
+ line.length += spaceData.length;
+ if (spaceData.length)
+ line.hasTrailingSpaces = true;
+
+ line.justified = false;
+ line.gridfitted = false;
+
+ if (eng->option.wrapMode() == QTextOption::WrapAtWordBoundaryOrAnywhere) {
+ if ((maxGlyphs != INT_MAX && glyphCount > maxGlyphs)
+ || (maxGlyphs == INT_MAX && line.textWidth > line.width)) {
+
+ eng->option.setWrapMode(QTextOption::WrapAnywhere);
+ line.length = 0;
+ line.textWidth = 0;
+ layout_helper(maxGlyphs);
+ eng->option.setWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere);
+ }
+ }
+}
+
+/*!
+ Moves the line to position \a pos.
+*/
+void QTextLine::setPosition(const QPointF &pos)
+{
+ eng->lines[i].x = QFixed::fromReal(pos.x());
+ eng->lines[i].y = QFixed::fromReal(pos.y());
+}
+
+/*!
+ Returns the line's position relative to the text layout's position.
+*/
+QPointF QTextLine::position() const
+{
+ return QPointF(eng->lines[i].x.toReal(), eng->lines[i].y.toReal());
+}
+
+// ### DOC: I have no idea what this means/does.
+// You create a text layout with a string of text. Once you laid
+// it out, it contains a number of QTextLines. from() returns the position
+// inside the text string where this line starts. If you e.g. has a
+// text of "This is a string", laid out into two lines (the second
+// starting at the word 'a'), layout.lineAt(0).from() == 0 and
+// layout.lineAt(1).from() == 8.
+/*!
+ Returns the start of the line from the beginning of the string
+ passed to the QTextLayout.
+*/
+int QTextLine::textStart() const
+{
+ return eng->lines[i].from;
+}
+
+/*!
+ Returns the length of the text in the line.
+
+ \sa naturalTextWidth()
+*/
+int QTextLine::textLength() const
+{
+ if (eng->option.flags() & QTextOption::ShowLineAndParagraphSeparators
+ && eng->block.isValid() && i == eng->lines.count()-1) {
+ return eng->lines[i].length - 1;
+ }
+ return eng->lines[i].length;
+}
+
+static void drawMenuText(QPainter *p, QFixed x, QFixed y, const QScriptItem &si, QTextItemInt &gf, QTextEngine *eng,
+ int start, int glyph_start)
+{
+ int ge = glyph_start + gf.glyphs.numGlyphs;
+ int gs = glyph_start;
+ int end = start + gf.num_chars;
+ unsigned short *logClusters = eng->logClusters(&si);
+ QGlyphLayout glyphs = eng->shapedGlyphs(&si);
+ QFixed orig_width = gf.width;
+
+ int *ul = eng->underlinePositions;
+ if (ul)
+ while (*ul != -1 && *ul < start)
+ ++ul;
+ bool rtl = si.analysis.bidiLevel % 2;
+ if (rtl)
+ x += si.width;
+
+ do {
+ int gtmp = ge;
+ int stmp = end;
+ if (ul && *ul != -1 && *ul < end) {
+ stmp = *ul;
+ gtmp = logClusters[*ul-si.position];
+ }
+
+ gf.glyphs = glyphs.mid(gs, gtmp - gs);
+ gf.num_chars = stmp - start;
+ gf.chars = eng->layoutData->string.unicode() + start;
+ QFixed w = 0;
+ while (gs < gtmp) {
+ w += glyphs.effectiveAdvance(gs);
+ ++gs;
+ }
+ start = stmp;
+ gf.width = w;
+ if (rtl)
+ x -= w;
+ if (gf.num_chars)
+ p->drawTextItem(QPointF(x.toReal(), y.toReal()), gf);
+ if (!rtl)
+ x += w;
+ if (ul && *ul != -1 && *ul < end) {
+ // draw underline
+ gtmp = (*ul == end-1) ? ge : logClusters[*ul+1-si.position];
+ ++stmp;
+ gf.glyphs = glyphs.mid(gs, gtmp - gs);
+ gf.num_chars = stmp - start;
+ gf.chars = eng->layoutData->string.unicode() + start;
+ gf.logClusters = logClusters + start - si.position;
+ w = 0;
+ while (gs < gtmp) {
+ w += glyphs.effectiveAdvance(gs);
+ ++gs;
+ }
+ ++start;
+ gf.width = w;
+ gf.underlineStyle = QTextCharFormat::SingleUnderline;
+ if (rtl)
+ x -= w;
+ p->drawTextItem(QPointF(x.toReal(), y.toReal()), gf);
+ if (!rtl)
+ x += w;
+ gf.underlineStyle = QTextCharFormat::NoUnderline;
+ ++gf.chars;
+ ++ul;
+ }
+ } while (gs < ge);
+
+ gf.width = orig_width;
+}
+
+
+static void setPenAndDrawBackground(QPainter *p, const QPen &defaultPen, const QTextCharFormat &chf, const QRectF &r)
+{
+ QBrush c = chf.foreground();
+ if (c.style() == Qt::NoBrush)
+ p->setPen(defaultPen);
+
+ QBrush bg = chf.background();
+ if (bg.style() != Qt::NoBrush)
+ p->fillRect(r, bg);
+ if (c.style() != Qt::NoBrush)
+ p->setPen(QPen(c, 0));
+}
+
+/*!
+ \fn void QTextLine::draw(QPainter *painter, const QPointF &position, const QTextLayout::FormatRange *selection) const
+
+ Draws a line on the given \a painter at the specified \a position.
+ The \a selection is reserved for internal use.
+*/
+void QTextLine::draw(QPainter *p, const QPointF &pos, const QTextLayout::FormatRange *selection) const
+{
+ const QScriptLine &line = eng->lines[i];
+ QPen pen = p->pen();
+
+ bool noText = (selection && selection->format.foreground().style() == Qt::NoBrush);
+
+ if (!line.length) {
+ if (selection
+ && selection->start <= line.from
+ && selection->start + selection->length > line.from) {
+
+ const qreal lineHeight = line.height().toReal();
+ QRectF r(pos.x() + line.x.toReal(), pos.y() + line.y.toReal(),
+ lineHeight / 2, QFontMetrics(eng->font()).width(QLatin1Char(' ')));
+ setPenAndDrawBackground(p, QPen(), selection->format, r);
+ p->setPen(pen);
+ }
+ return;
+ }
+
+
+ QTextLineItemIterator iterator(eng, i, pos, selection);
+ const QFixed y = QFixed::fromReal(pos.y()) + line.y + line.ascent;
+
+ bool suppressColors = (eng->option.flags() & QTextOption::SuppressColors);
+ while (!iterator.atEnd()) {
+ QScriptItem &si = iterator.next();
+
+ if (selection && selection->start >= 0 && iterator.isOutsideSelection())
+ continue;
+
+ if (si.analysis.flags == QScriptAnalysis::LineOrParagraphSeparator
+ && !(eng->option.flags() & QTextOption::ShowLineAndParagraphSeparators))
+ continue;
+
+ QFixed itemBaseLine = y;
+ QFont f = eng->font(si);
+ QTextCharFormat format;
+
+ if (eng->hasFormats() || selection) {
+ if (!suppressColors)
+ format = eng->format(&si);
+ if (selection)
+ format.merge(selection->format);
+
+ setPenAndDrawBackground(p, pen, format, QRectF(iterator.x.toReal(), (y - line.ascent).toReal(),
+ iterator.itemWidth.toReal(), line.height().toReal()));
+
+ QTextCharFormat::VerticalAlignment valign = format.verticalAlignment();
+ if (valign == QTextCharFormat::AlignSuperScript || valign == QTextCharFormat::AlignSubScript) {
+ QFontEngine *fe = f.d->engineForScript(si.analysis.script);
+ QFixed height = fe->ascent() + fe->descent();
+ if (valign == QTextCharFormat::AlignSubScript)
+ itemBaseLine += height / 6;
+ else if (valign == QTextCharFormat::AlignSuperScript)
+ itemBaseLine -= height / 2;
+ }
+ }
+
+ if (si.analysis.flags >= QScriptAnalysis::TabOrObject) {
+
+ if (eng->hasFormats()) {
+ p->save();
+ if (si.analysis.flags == QScriptAnalysis::Object && eng->block.docHandle()) {
+ QFixed itemY = y - si.ascent;
+ if (format.verticalAlignment() == QTextCharFormat::AlignTop) {
+ itemY = y - line.ascent;
+ }
+
+ QRectF itemRect(iterator.x.toReal(), itemY.toReal(), iterator.itemWidth.toReal(), si.height().toReal());
+
+ eng->docLayout()->drawInlineObject(p, itemRect,
+ QTextInlineObject(iterator.item, eng),
+ si.position + eng->block.position(),
+ format);
+ if (selection) {
+ QBrush bg = format.brushProperty(ObjectSelectionBrush);
+ if (bg.style() != Qt::NoBrush) {
+ QColor c = bg.color();
+ c.setAlpha(128);
+ p->fillRect(itemRect, c);
+ }
+ }
+ } else { // si.isTab
+ QFont f = eng->font(si);
+ QTextItemInt gf(si, &f, format);
+ gf.chars = 0;
+ gf.num_chars = 0;
+ gf.width = iterator.itemWidth;
+ p->drawTextItem(QPointF(iterator.x.toReal(), y.toReal()), gf);
+ if (eng->option.flags() & QTextOption::ShowTabsAndSpaces) {
+ QChar visualTab(0x2192);
+ int w = QFontMetrics(f).width(visualTab);
+ qreal x = iterator.itemWidth.toReal() - w; // Right-aligned
+ if (x < 0)
+ p->setClipRect(QRectF(iterator.x.toReal(), line.y.toReal(),
+ iterator.itemWidth.toReal(), line.height().toReal()),
+ Qt::IntersectClip);
+ else
+ x /= 2; // Centered
+ p->drawText(QPointF(iterator.x.toReal() + x,
+ y.toReal()), visualTab);
+ }
+
+ }
+ p->restore();
+ }
+
+ continue;
+ }
+
+ unsigned short *logClusters = eng->logClusters(&si);
+ QGlyphLayout glyphs = eng->shapedGlyphs(&si);
+
+ QTextItemInt gf(si, &f, format);
+ gf.glyphs = glyphs.mid(iterator.glyphsStart, iterator.glyphsEnd - iterator.glyphsStart);
+ gf.chars = eng->layoutData->string.unicode() + iterator.itemStart;
+ gf.logClusters = logClusters + iterator.itemStart - si.position;
+ gf.num_chars = iterator.itemEnd - iterator.itemStart;
+ gf.width = iterator.itemWidth;
+ gf.justified = line.justified;
+
+ Q_ASSERT(gf.fontEngine);
+
+ if (eng->underlinePositions) {
+ // can't have selections in this case
+ drawMenuText(p, iterator.x, itemBaseLine, si, gf, eng, iterator.itemStart, iterator.glyphsStart);
+ } else {
+ QPointF pos(iterator.x.toReal(), itemBaseLine.toReal());
+ if (format.penProperty(QTextFormat::TextOutline).style() != Qt::NoPen) {
+ QPainterPath path;
+ path.setFillRule(Qt::WindingFill);
+
+ if (gf.glyphs.numGlyphs)
+ gf.fontEngine->addOutlineToPath(pos.x(), pos.y(), gf.glyphs, &path, gf.flags);
+ if (gf.flags) {
+ const QFontEngine *fe = gf.fontEngine;
+ const qreal lw = fe->lineThickness().toReal();
+ if (gf.flags & QTextItem::Underline) {
+ qreal offs = fe->underlinePosition().toReal();
+ path.addRect(pos.x(), pos.y() + offs, gf.width.toReal(), lw);
+ }
+ if (gf.flags & QTextItem::Overline) {
+ qreal offs = fe->ascent().toReal() + 1;
+ path.addRect(pos.x(), pos.y() - offs, gf.width.toReal(), lw);
+ }
+ if (gf.flags & QTextItem::StrikeOut) {
+ qreal offs = fe->ascent().toReal() / 3;
+ path.addRect(pos.x(), pos.y() - offs, gf.width.toReal(), lw);
+ }
+ }
+
+ p->save();
+ p->setRenderHint(QPainter::Antialiasing);
+ //Currently QPen with a Qt::NoPen style still returns a default
+ //QBrush which != Qt::NoBrush so we need this specialcase to reset it
+ if (p->pen().style() == Qt::NoPen)
+ p->setBrush(Qt::NoBrush);
+ else
+ p->setBrush(p->pen().brush());
+
+ p->setPen(format.textOutline());
+ p->drawPath(path);
+ p->restore();
+ } else {
+ if (noText)
+ gf.glyphs.numGlyphs = 0; // slightly less elegant than it should be
+ p->drawTextItem(pos, gf);
+ }
+ }
+ if (si.analysis.flags == QScriptAnalysis::Space
+ && (eng->option.flags() & QTextOption::ShowTabsAndSpaces)) {
+ QBrush c = format.foreground();
+ if (c.style() != Qt::NoBrush)
+ p->setPen(c.color());
+ QChar visualSpace((ushort)0xb7);
+ p->drawText(QPointF(iterator.x.toReal(), itemBaseLine.toReal()), visualSpace);
+ p->setPen(pen);
+ }
+ }
+
+
+ if (eng->hasFormats())
+ p->setPen(pen);
+}
+
+/*!
+ \fn int QTextLine::cursorToX(int cursorPos, Edge edge) const
+
+ \overload
+*/
+
+
+/*!
+ Converts the cursor position \a cursorPos to the corresponding x position
+ inside the line, taking account of the \a edge.
+
+ If \a cursorPos is not a valid cursor position, the nearest valid
+ cursor position will be used instead, and cpos will be modified to
+ point to this valid cursor position.
+
+ \sa xToCursor()
+*/
+qreal QTextLine::cursorToX(int *cursorPos, Edge edge) const
+{
+ if (!eng->layoutData)
+ eng->itemize();
+
+ const QScriptLine &line = eng->lines[i];
+
+ QFixed x = line.x;
+ x += alignLine(eng, line);
+
+ if (!i && !eng->layoutData->items.size()) {
+ *cursorPos = 0;
+ return x.toReal();
+ }
+
+ int pos = *cursorPos;
+ int itm;
+ if (pos == line.from + (int)line.length) {
+ // end of line ensure we have the last item on the line
+ itm = eng->findItem(pos-1);
+ }
+ else
+ itm = eng->findItem(pos);
+ eng->shapeLine(line);
+
+ const QScriptItem *si = &eng->layoutData->items[itm];
+ if (!si->num_glyphs)
+ eng->shape(itm);
+ pos -= si->position;
+
+ QGlyphLayout glyphs = eng->shapedGlyphs(si);
+ unsigned short *logClusters = eng->logClusters(si);
+ Q_ASSERT(logClusters);
+
+ int l = eng->length(itm);
+ if (pos > l)
+ pos = l;
+ if (pos < 0)
+ pos = 0;
+
+ int glyph_pos = pos == l ? si->num_glyphs : logClusters[pos];
+ if (edge == Trailing) {
+ // trailing edge is leading edge of next cluster
+ while (glyph_pos < si->num_glyphs && !glyphs.attributes[glyph_pos].clusterStart)
+ glyph_pos++;
+ }
+
+ bool reverse = eng->layoutData->items[itm].analysis.bidiLevel % 2;
+
+ int lineEnd = line.from + line.length;
+
+ // add the items left of the cursor
+
+ int firstItem = eng->findItem(line.from);
+ int lastItem = eng->findItem(lineEnd - 1);
+ int nItems = (firstItem >= 0 && lastItem >= firstItem)? (lastItem-firstItem+1) : 0;
+
+ QVarLengthArray<int> visualOrder(nItems);
+ QVarLengthArray<uchar> levels(nItems);
+ for (int i = 0; i < nItems; ++i)
+ levels[i] = eng->layoutData->items[i+firstItem].analysis.bidiLevel;
+ QTextEngine::bidiReorder(nItems, levels.data(), visualOrder.data());
+
+ for (int i = 0; i < nItems; ++i) {
+ int item = visualOrder[i]+firstItem;
+ if (item == itm)
+ break;
+ QScriptItem &si = eng->layoutData->items[item];
+ if (!si.num_glyphs)
+ eng->shape(item);
+
+ if (si.analysis.flags >= QScriptAnalysis::TabOrObject) {
+ x += si.width;
+ continue;
+ }
+ int start = qMax(line.from, si.position);
+ int end = qMin(lineEnd, si.position + eng->length(item));
+
+ logClusters = eng->logClusters(&si);
+
+ int gs = logClusters[start-si.position];
+ int ge = (end == si.position + eng->length(item)) ? si.num_glyphs-1 : logClusters[end-si.position-1];
+
+ QGlyphLayout glyphs = eng->shapedGlyphs(&si);
+
+ while (gs <= ge) {
+ x += glyphs.effectiveAdvance(gs);
+ ++gs;
+ }
+ }
+
+ logClusters = eng->logClusters(si);
+ glyphs = eng->shapedGlyphs(si);
+ if (si->analysis.flags >= QScriptAnalysis::TabOrObject) {
+ if(pos == l)
+ x += si->width;
+ } else {
+ int offsetInCluster = 0;
+ for (int i=pos-1; i >= 0; i--) {
+ if (logClusters[i] == glyph_pos)
+ offsetInCluster++;
+ else
+ break;
+ }
+
+ if (reverse) {
+ int end = qMin(lineEnd, si->position + l) - si->position;
+ int glyph_end = end == l ? si->num_glyphs : logClusters[end];
+ for (int i = glyph_end - 1; i >= glyph_pos; i--)
+ x += glyphs.effectiveAdvance(i);
+ } else {
+ int start = qMax(line.from - si->position, 0);
+ int glyph_start = logClusters[start];
+ for (int i = glyph_start; i < glyph_pos; i++)
+ x += glyphs.effectiveAdvance(i);
+ }
+ if (offsetInCluster > 0) { // in the case that the offset is inside a (multi-character) glyph, interpolate the position.
+ int clusterLength = 0;
+ for (int i=pos - offsetInCluster; i < line.length; i++) {
+ if (logClusters[i] == glyph_pos)
+ clusterLength++;
+ else
+ break;
+ }
+ if (clusterLength)
+ x+= glyphs.advances_x[glyph_pos] * offsetInCluster / clusterLength;
+ }
+ }
+
+ *cursorPos = pos + si->position;
+ return x.toReal();
+}
+
+/*!
+ \fn int QTextLine::xToCursor(qreal x, CursorPosition cpos) const
+
+ Converts the x-coordinate \a x, to the nearest matching cursor
+ position, depending on the cursor position type, \a cpos.
+
+ \sa cursorToX()
+*/
+int QTextLine::xToCursor(qreal _x, CursorPosition cpos) const
+{
+ QFixed x = QFixed::fromReal(_x);
+ const QScriptLine &line = eng->lines[i];
+
+ if (!eng->layoutData)
+ eng->itemize();
+
+ int line_length = textLength();
+
+ if (!line_length)
+ return line.from;
+
+ int firstItem = eng->findItem(line.from);
+ int lastItem = eng->findItem(line.from + line_length - 1);
+ int nItems = (firstItem >= 0 && lastItem >= firstItem)? (lastItem-firstItem+1) : 0;
+
+ if (!nItems)
+ return 0;
+
+ x -= line.x;
+ x -= alignLine(eng, line);
+// qDebug("xToCursor: x=%f, cpos=%d", x.toReal(), cpos);
+
+ QVarLengthArray<int> visualOrder(nItems);
+ QVarLengthArray<unsigned char> levels(nItems);
+ for (int i = 0; i < nItems; ++i)
+ levels[i] = eng->layoutData->items[i+firstItem].analysis.bidiLevel;
+ QTextEngine::bidiReorder(nItems, levels.data(), visualOrder.data());
+
+ if (x <= 0) {
+ // left of first item
+ int item = visualOrder[0]+firstItem;
+ QScriptItem &si = eng->layoutData->items[item];
+ if (!si.num_glyphs)
+ eng->shape(item);
+ int pos = si.position;
+ if (si.analysis.bidiLevel % 2)
+ pos += eng->length(item);
+ pos = qMax(line.from, pos);
+ pos = qMin(line.from + line_length, pos);
+ return pos;
+ } else if (x < line.textWidth
+ || (line.justified && x < line.width)) {
+ // has to be in one of the runs
+ QFixed pos;
+
+ eng->shapeLine(line);
+ for (int i = 0; i < nItems; ++i) {
+ int item = visualOrder[i]+firstItem;
+ QScriptItem &si = eng->layoutData->items[item];
+ int item_length = eng->length(item);
+// qDebug(" item %d, visual %d x_remain=%f", i, item, x.toReal());
+
+ int start = qMax(line.from - si.position, 0);
+ int end = qMin(line.from + line_length - si.position, item_length);
+
+ unsigned short *logClusters = eng->logClusters(&si);
+
+ int gs = logClusters[start];
+ int ge = (end == item_length ? si.num_glyphs : logClusters[end]) - 1;
+ QGlyphLayout glyphs = eng->shapedGlyphs(&si);
+
+ QFixed item_width = 0;
+ if (si.analysis.flags >= QScriptAnalysis::TabOrObject) {
+ item_width = si.width;
+ } else {
+ int g = gs;
+ while (g <= ge) {
+ item_width += glyphs.effectiveAdvance(g);
+ ++g;
+ }
+ }
+// qDebug(" start=%d, end=%d, gs=%d, ge=%d item_width=%f", start, end, gs, ge, item_width.toReal());
+
+ if (pos + item_width < x) {
+ pos += item_width;
+ continue;
+ }
+// qDebug(" inside run");
+ if (si.analysis.flags >= QScriptAnalysis::TabOrObject) {
+ if (cpos == QTextLine::CursorOnCharacter)
+ return si.position;
+ bool left_half = (x - pos) < item_width/2;
+
+ if (bool(si.analysis.bidiLevel % 2) != left_half)
+ return si.position;
+ return si.position + 1;
+ }
+
+ int glyph_pos = -1;
+ // has to be inside run
+ if (cpos == QTextLine::CursorOnCharacter) {
+ if (si.analysis.bidiLevel % 2) {
+ pos += item_width;
+ glyph_pos = gs;
+ while (gs <= ge) {
+ if (glyphs.attributes[gs].clusterStart) {
+ if (pos < x)
+ break;
+ glyph_pos = gs;
+ break;
+ }
+ pos -= glyphs.effectiveAdvance(gs);
+ ++gs;
+ }
+ } else {
+ glyph_pos = gs;
+ while (gs <= ge) {
+ if (glyphs.attributes[gs].clusterStart) {
+ if (pos > x)
+ break;
+ glyph_pos = gs;
+ }
+ pos += glyphs.effectiveAdvance(gs);
+ ++gs;
+ }
+ }
+ } else {
+ QFixed dist = INT_MAX/256;
+ if (si.analysis.bidiLevel % 2) {
+ pos += item_width;
+ while (gs <= ge) {
+ if (glyphs.attributes[gs].clusterStart && qAbs(x-pos) < dist) {
+ glyph_pos = gs;
+ dist = qAbs(x-pos);
+ }
+ pos -= glyphs.effectiveAdvance(gs);
+ ++gs;
+ }
+ } else {
+ while (gs <= ge) {
+ if (glyphs.attributes[gs].clusterStart && qAbs(x-pos) < dist) {
+ glyph_pos = gs;
+ dist = qAbs(x-pos);
+ }
+ pos += glyphs.effectiveAdvance(gs);
+ ++gs;
+ }
+ }
+ if (qAbs(x-pos) < dist)
+ return si.position + end;
+ }
+ Q_ASSERT(glyph_pos != -1);
+ int j;
+ for (j = 0; j < eng->length(item); ++j)
+ if (logClusters[j] == glyph_pos)
+ break;
+// qDebug("at pos %d (in run: %d)", si.position + j, j);
+ return si.position + j;
+ }
+ }
+ // right of last item
+// qDebug() << "right of last";
+ int item = visualOrder[nItems-1]+firstItem;
+ QScriptItem &si = eng->layoutData->items[item];
+ if (!si.num_glyphs)
+ eng->shape(item);
+ int pos = si.position;
+ if (!(si.analysis.bidiLevel % 2))
+ pos += eng->length(item);
+ pos = qMax(line.from, pos);
+
+ int maxPos = line.from + line_length;
+
+ // except for the last line we assume that the
+ // character between lines is a space and we want
+ // to position the cursor to the left of that
+ // character.
+ // ###### breaks with japanese for example
+ if (this->i < eng->lines.count() - 1)
+ --maxPos;
+
+ pos = qMin(pos, maxPos);
+ return pos;
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/text/qtextlayout.h b/src/gui/text/qtextlayout.h
new file mode 100644
index 0000000000..e5acb8ea03
--- /dev/null
+++ b/src/gui/text/qtextlayout.h
@@ -0,0 +1,243 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#ifndef QTEXTLAYOUT_H
+#define QTEXTLAYOUT_H
+
+#include <QtCore/qstring.h>
+#include <QtCore/qnamespace.h>
+#include <QtCore/qrect.h>
+#include <QtCore/qvector.h>
+#include <QtGui/qcolor.h>
+#include <QtCore/qobject.h>
+#include <QtGui/qevent.h>
+#include <QtGui/qtextformat.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+class QTextEngine;
+class QFont;
+class QRect;
+class QRegion;
+class QTextFormat;
+class QPalette;
+class QPainter;
+
+class Q_GUI_EXPORT QTextInlineObject
+{
+public:
+ QTextInlineObject(int i, QTextEngine *e) : itm(i), eng(e) {}
+ inline QTextInlineObject() : itm(0), eng(0) {}
+ inline bool isValid() const { return eng; }
+
+ QRectF rect() const;
+ qreal width() const;
+ qreal ascent() const;
+ qreal descent() const;
+ qreal height() const;
+
+ Qt::LayoutDirection textDirection() const;
+
+ void setWidth(qreal w);
+ void setAscent(qreal a);
+ void setDescent(qreal d);
+
+ int textPosition() const;
+
+ int formatIndex() const;
+ QTextFormat format() const;
+
+private:
+ friend class QTextLayout;
+ int itm;
+ QTextEngine *eng;
+};
+
+class QPaintDevice;
+class QTextFormat;
+class QTextLine;
+class QTextBlock;
+class QTextOption;
+
+class Q_GUI_EXPORT QTextLayout
+{
+public:
+ // does itemization
+ QTextLayout();
+ QTextLayout(const QString& text);
+ QTextLayout(const QString& text, const QFont &font, QPaintDevice *paintdevice = 0);
+ QTextLayout(const QTextBlock &b);
+ ~QTextLayout();
+
+ void setFont(const QFont &f);
+ QFont font() const;
+
+ void setText(const QString& string);
+ QString text() const;
+
+ void setTextOption(const QTextOption &option);
+ QTextOption textOption() const;
+
+ void setPreeditArea(int position, const QString &text);
+ int preeditAreaPosition() const;
+ QString preeditAreaText() const;
+
+ struct FormatRange {
+ int start;
+ int length;
+ QTextCharFormat format;
+ };
+ void setAdditionalFormats(const QList<FormatRange> &overrides);
+ QList<FormatRange> additionalFormats() const;
+ void clearAdditionalFormats();
+
+ void setCacheEnabled(bool enable);
+ bool cacheEnabled() const;
+
+ void beginLayout();
+ void endLayout();
+ void clearLayout();
+
+ QTextLine createLine();
+
+ int lineCount() const;
+ QTextLine lineAt(int i) const;
+ QTextLine lineForTextPosition(int pos) const;
+
+ enum CursorMode {
+ SkipCharacters,
+ SkipWords
+ };
+ bool isValidCursorPosition(int pos) const;
+ int nextCursorPosition(int oldPos, CursorMode mode = SkipCharacters) const;
+ int previousCursorPosition(int oldPos, CursorMode mode = SkipCharacters) const;
+
+ void draw(QPainter *p, const QPointF &pos, const QVector<FormatRange> &selections = QVector<FormatRange>(),
+ const QRectF &clip = QRectF()) const;
+ void drawCursor(QPainter *p, const QPointF &pos, int cursorPosition) const;
+ void drawCursor(QPainter *p, const QPointF &pos, int cursorPosition, int width) const;
+
+ QPointF position() const;
+ void setPosition(const QPointF &p);
+
+ QRectF boundingRect() const;
+
+ qreal minimumWidth() const;
+ qreal maximumWidth() const;
+
+ QTextEngine *engine() const { return d; }
+ void setFlags(int flags);
+private:
+ QTextLayout(QTextEngine *e) : d(e) {}
+ Q_DISABLE_COPY(QTextLayout)
+
+ friend class QPainter;
+ friend class QPSPrinter;
+ friend class QGraphicsSimpleTextItemPrivate;
+ friend class QGraphicsSimpleTextItem;
+ friend void qt_format_text(const QFont &font, const QRectF &_r, int tf, const QTextOption *, const QString& str,
+ QRectF *brect, int tabstops, int* tabarray, int tabarraylen,
+ QPainter *painter);
+ QTextEngine *d;
+};
+
+
+class Q_GUI_EXPORT QTextLine
+{
+public:
+ inline QTextLine() : i(0), eng(0) {}
+ inline bool isValid() const { return eng; }
+
+ QRectF rect() const;
+ qreal x() const;
+ qreal y() const;
+ qreal width() const;
+ qreal ascent() const;
+ qreal descent() const;
+ qreal height() const;
+
+ qreal naturalTextWidth() const;
+ QRectF naturalTextRect() const;
+
+ enum Edge {
+ Leading,
+ Trailing
+ };
+ enum CursorPosition {
+ CursorBetweenCharacters,
+ CursorOnCharacter
+ };
+
+ /* cursorPos gets set to the valid position */
+ qreal cursorToX(int *cursorPos, Edge edge = Leading) const;
+ inline qreal cursorToX(int cursorPos, Edge edge = Leading) const { return cursorToX(&cursorPos, edge); }
+ int xToCursor(qreal x, CursorPosition = CursorBetweenCharacters) const;
+
+ void setLineWidth(qreal width);
+ void setNumColumns(int columns);
+ void setNumColumns(int columns, qreal alignmentWidth);
+
+ void setPosition(const QPointF &pos);
+ QPointF position() const;
+
+ int textStart() const;
+ int textLength() const;
+
+ int lineNumber() const { return i; }
+
+ void draw(QPainter *p, const QPointF &point, const QTextLayout::FormatRange *selection = 0) const;
+
+private:
+ QTextLine(int line, QTextEngine *e) : i(line), eng(e) {}
+ void layout_helper(int numGlyphs);
+ friend class QTextLayout;
+ int i;
+ QTextEngine *eng;
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QTEXTLAYOUT_H
diff --git a/src/gui/text/qtextlist.cpp b/src/gui/text/qtextlist.cpp
new file mode 100644
index 0000000000..e305027efc
--- /dev/null
+++ b/src/gui/text/qtextlist.cpp
@@ -0,0 +1,261 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+
+#include "qtextlist.h"
+#include "qtextobject_p.h"
+#include "qtextcursor.h"
+#include "qtextdocument_p.h"
+#include <qdebug.h>
+
+QT_BEGIN_NAMESPACE
+
+class QTextListPrivate : public QTextBlockGroupPrivate
+{
+};
+
+/*!
+ \class QTextList
+ \reentrant
+
+ \brief The QTextList class provides a decorated list of items in a QTextDocument.
+
+ \ingroup text
+
+ A list contains a sequence of text blocks, each of which is marked with a
+ bullet point or other symbol. Multiple levels of lists can be used, and
+ the automatic numbering feature provides support for ordered numeric and
+ alphabetical lists.
+
+ Lists are created by using a text cursor to insert an empty list at the
+ current position or by moving existing text into a new list.
+ The \l{QTextCursor::insertList()} function inserts an empty block into the
+ document at the cursor position, and makes it the first item in a list.
+
+ \snippet doc/src/snippets/textdocument-lists/mainwindow.cpp 0
+
+ The \l{QTextCursor::createList()} function takes the contents of the
+ cursor's current block and turns it into the first item of a new list.
+
+ The cursor's current list is found with \l{QTextCursor::currentList()}.
+
+ The number of items in a list is given by count(). Each item can be
+ obtained by its index in the list with the item() function. Similarly,
+ the index of a given item can be found with itemNumber(). The text of
+ each item can be found with the itemText() function.
+
+ Note that the items in the list may not be adjacent elements in the
+ document. For example, the top-level items in a multi-level list will
+ be separated by the items in lower levels of the list.
+
+ List items can be deleted by index with the removeItem() function.
+ remove() deletes the specified item in the list.
+
+ The list's format is set with setFormat() and read with format().
+ The format describes the decoration of the list itself, and not the
+ individual items.
+
+ \sa QTextBlock, QTextListFormat, QTextCursor
+*/
+
+/*!
+ \fn bool QTextList::isEmpty() const
+ \obsolete
+
+ Returns true if the list has no items; otherwise returns false.
+
+ \bold{Note:} Empty lists are automatically deleted by the QTextDocument that owns
+ them.
+
+ \sa count()
+*/
+
+/*! \internal
+ */
+QTextList::QTextList(QTextDocument *doc)
+ : QTextBlockGroup(*new QTextListPrivate, doc)
+{
+}
+
+/*!
+ \internal
+*/
+QTextList::~QTextList()
+{
+}
+
+/*!
+ Returns the number of items in the list.
+
+ \sa isEmpty()
+*/
+int QTextList::count() const
+{
+ Q_D(const QTextList);
+ return d->blocks.count();
+}
+
+/*!
+ Returns the \a{i}-th text block in the list.
+
+ \sa count() itemText()
+*/
+QTextBlock QTextList::item(int i) const
+{
+ Q_D(const QTextList);
+ if (i < 0 || i >= d->blocks.size())
+ return QTextBlock();
+ return d->blocks.at(i);
+}
+
+/*!
+ \fn void QTextList::setFormat(const QTextListFormat &format)
+
+ Sets the list's format to \a format.
+*/
+
+/*!
+ \fn QTextListFormat QTextList::format() const
+
+ Returns the list's format.
+*/
+
+/*!
+ \fn int QTextList::itemNumber(const QTextBlock &block) const
+
+ Returns the index of the list item that corresponds to the given \a block.
+ Returns -1 if the block was not present in the list.
+*/
+int QTextList::itemNumber(const QTextBlock &blockIt) const
+{
+ Q_D(const QTextList);
+ return d->blocks.indexOf(blockIt);
+}
+
+/*!
+ \fn QString QTextList::itemText(const QTextBlock &block) const
+
+ Returns the text of the list item that corresponds to the given \a block.
+*/
+QString QTextList::itemText(const QTextBlock &blockIt) const
+{
+ Q_D(const QTextList);
+ int item = d->blocks.indexOf(blockIt) + 1;
+ if (item <= 0)
+ return QString();
+
+ QTextBlock block = d->blocks.at(item-1);
+ QTextBlockFormat blockFormat = block.blockFormat();
+
+ QString result;
+
+ const int style = format().style();
+
+ switch (style) {
+ case QTextListFormat::ListDecimal:
+ result = QString::number(item);
+ break;
+ // from the old richtext
+ case QTextListFormat::ListLowerAlpha:
+ case QTextListFormat::ListUpperAlpha:
+ {
+ const char baseChar = style == QTextListFormat::ListUpperAlpha ? 'A' : 'a';
+
+ int c = item;
+ while (c > 0) {
+ c--;
+ result.prepend(QChar(baseChar + (c % 26)));
+ c /= 26;
+ }
+ }
+ break;
+ default:
+ Q_ASSERT(false);
+ }
+ if (blockFormat.layoutDirection() == Qt::RightToLeft)
+ return result.prepend(QLatin1Char('.'));
+ return result + QLatin1Char('.');
+}
+
+/*!
+ Removes the item at item position \a i from the list. When the last item in the
+ list is removed, the list is automatically deleted by the QTextDocument that owns
+ it.
+
+ \sa add(), remove()
+*/
+void QTextList::removeItem(int i)
+{
+ Q_D(QTextList);
+ if (i < 0 || i >= d->blocks.size())
+ return;
+
+ QTextBlock block = d->blocks.at(i);
+ remove(block);
+}
+
+
+/*!
+ Removes the given \a block from the list.
+
+ \sa add(), removeItem()
+*/
+void QTextList::remove(const QTextBlock &block)
+{
+ QTextBlockFormat fmt = block.blockFormat();
+ fmt.setIndent(fmt.indent() + format().indent());
+ fmt.setObjectIndex(-1);
+ block.docHandle()->setBlockFormat(block, block, fmt, QTextDocumentPrivate::SetFormat);
+}
+
+/*!
+ Makes the given \a block part of the list.
+
+ \sa remove(), removeItem()
+*/
+void QTextList::add(const QTextBlock &block)
+{
+ QTextBlockFormat fmt = block.blockFormat();
+ fmt.setObjectIndex(objectIndex());
+ block.docHandle()->setBlockFormat(block, block, fmt, QTextDocumentPrivate::SetFormat);
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/text/qtextlist.h b/src/gui/text/qtextlist.h
new file mode 100644
index 0000000000..ab8d2d9d50
--- /dev/null
+++ b/src/gui/text/qtextlist.h
@@ -0,0 +1,94 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QTEXTLIST_H
+#define QTEXTLIST_H
+
+#include <QtGui/qtextobject.h>
+#include <QtCore/qobject.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+class QTextListPrivate;
+class QTextCursor;
+
+class Q_GUI_EXPORT QTextList : public QTextBlockGroup
+{
+ Q_OBJECT
+public:
+ explicit QTextList(QTextDocument *doc);
+ ~QTextList();
+
+ int count() const;
+
+ inline bool isEmpty() const
+ { return count() == 0; }
+
+ QTextBlock item(int i) const;
+
+ int itemNumber(const QTextBlock &) const;
+ QString itemText(const QTextBlock &) const;
+
+ void removeItem(int i);
+ void remove(const QTextBlock &);
+
+ void add(const QTextBlock &block);
+
+ inline void setFormat(const QTextListFormat &format);
+ QTextListFormat format() const { return QTextObject::format().toListFormat(); }
+
+private:
+ Q_DISABLE_COPY(QTextList)
+ Q_DECLARE_PRIVATE(QTextList)
+};
+
+inline void QTextList::setFormat(const QTextListFormat &aformat)
+{ QTextObject::setFormat(aformat); }
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QTEXTLIST_H
diff --git a/src/gui/text/qtextobject.cpp b/src/gui/text/qtextobject.cpp
new file mode 100644
index 0000000000..1645a21cf4
--- /dev/null
+++ b/src/gui/text/qtextobject.cpp
@@ -0,0 +1,1711 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qtextobject.h"
+#include "qtextobject_p.h"
+#include "qtextdocument.h"
+#include "qtextformat_p.h"
+#include "qtextdocument_p.h"
+#include "qtextcursor.h"
+#include "qtextlist.h"
+#include "qabstracttextdocumentlayout.h"
+#include "qtextengine_p.h"
+#include "qdebug.h"
+
+QT_BEGIN_NAMESPACE
+
+// ### DOC: We ought to explain the CONCEPT of objectIndexes if
+// relevant to the public API
+/*!
+ \class QTextObject
+ \reentrant
+
+ \brief The QTextObject class is a base class for different kinds
+ of objects that can group parts of a QTextDocument together.
+
+ \ingroup text
+
+ The common grouping text objects are lists (QTextList), frames
+ (QTextFrame), and tables (QTextTable). A text object has an
+ associated format() and document().
+
+ There are essentially two kinds of text objects: those that are used
+ with blocks (block formats), and those that are used with characters
+ (character formats). The first kind are derived from QTextBlockGroup,
+ and the second kind from QTextFrame.
+
+ You rarely need to use this class directly. When creating custom text
+ objects, you will also need to reimplement QTextDocument::createObject()
+ which acts as a factory method for creating text objects.
+
+ \sa QTextDocument
+*/
+
+/*!
+ \fn QTextObject::QTextObject(QTextDocument *document)
+
+ Creates a new QTextObject for the given \a document.
+
+ \warning This function should never be called directly, but only
+ from QTextDocument::createObject().
+*/
+QTextObject::QTextObject(QTextDocument *doc)
+ : QObject(*new QTextObjectPrivate, doc)
+{
+}
+
+/*!
+ \fn QTextObject::QTextObject(QTextObjectPrivate &p, QTextDocument *document)
+
+ \internal
+*/
+QTextObject::QTextObject(QTextObjectPrivate &p, QTextDocument *doc)
+ :QObject(p, doc)
+{
+}
+
+/*!
+ Destroys the text object.
+
+ \warning Text objects are owned by the document, so you should
+ never destroy them yourself.
+*/
+QTextObject::~QTextObject()
+{
+}
+
+/*!
+ Returns the text object's format.
+
+ \sa setFormat() document()
+*/
+QTextFormat QTextObject::format() const
+{
+ Q_D(const QTextObject);
+ return d->pieceTable->formatCollection()->objectFormat(d->objectIndex);
+}
+
+/*!
+ Returns the index of the object's format in the document's internal
+ list of formats.
+
+ \sa QTextDocument::allFormats()
+*/
+int QTextObject::formatIndex() const
+{
+ Q_D(const QTextObject);
+ return d->pieceTable->formatCollection()->objectFormatIndex(d->objectIndex);
+}
+
+
+/*!
+ Sets the text object's \a format.
+
+ \sa format()
+*/
+void QTextObject::setFormat(const QTextFormat &format)
+{
+ Q_D(QTextObject);
+ int idx = d->pieceTable->formatCollection()->indexForFormat(format);
+ d->pieceTable->changeObjectFormat(this, idx);
+}
+
+/*!
+ Returns the object index of this object. This can be used together with
+ QTextFormat::setObjectIndex().
+*/
+int QTextObject::objectIndex() const
+{
+ Q_D(const QTextObject);
+ return d->objectIndex;
+}
+
+/*!
+ Returns the document this object belongs to.
+
+ \sa format()
+*/
+QTextDocument *QTextObject::document() const
+{
+ return static_cast<QTextDocument *>(parent());
+}
+
+/*!
+ \internal
+*/
+QTextDocumentPrivate *QTextObject::docHandle() const
+{
+ return static_cast<const QTextDocument *>(parent())->docHandle();
+}
+
+/*!
+ \class QTextBlockGroup
+ \reentrant
+
+ \brief The QTextBlockGroup class provides a container for text blocks within
+ a QTextDocument.
+
+ \ingroup text
+
+ Block groups can be used to organize blocks of text within a document.
+ They maintain an up-to-date list of the text blocks that belong to
+ them, even when text blocks are being edited.
+
+ Each group has a parent document which is specified when the group is
+ constructed.
+
+ Text blocks can be inserted into a group with blockInserted(), and removed
+ with blockRemoved(). If a block's format is changed, blockFormatChanged()
+ is called.
+
+ The list of blocks in the group is returned by blockList(). Note that the
+ blocks in the list are not necessarily adjacent elements in the document;
+ for example, the top-level items in a multi-level list will be separated
+ by the items in lower levels of the list.
+
+ \sa QTextBlock QTextDocument
+*/
+
+void QTextBlockGroupPrivate::markBlocksDirty()
+{
+ for (int i = 0; i < blocks.count(); ++i) {
+ const QTextBlock &block = blocks.at(i);
+ pieceTable->documentChange(block.position(), block.length());
+ }
+}
+
+/*!
+ \fn QTextBlockGroup::QTextBlockGroup(QTextDocument *document)
+
+ Creates a new new block group for the given \a document.
+
+ \warning This function should only be called from
+ QTextDocument::createObject().
+*/
+QTextBlockGroup::QTextBlockGroup(QTextDocument *doc)
+ : QTextObject(*new QTextBlockGroupPrivate, doc)
+{
+}
+
+/*!
+ \internal
+*/
+QTextBlockGroup::QTextBlockGroup(QTextBlockGroupPrivate &p, QTextDocument *doc)
+ : QTextObject(p, doc)
+{
+}
+
+/*!
+ Destroys this block group; the blocks are not deleted, they simply
+ don't belong to this block anymore.
+*/
+QTextBlockGroup::~QTextBlockGroup()
+{
+}
+
+// ### DOC: Shouldn't this be insertBlock()?
+/*!
+ Appends the given \a block to the end of the group.
+
+ \warning If you reimplement this function you must call the base
+ class implementation.
+*/
+void QTextBlockGroup::blockInserted(const QTextBlock &block)
+{
+ Q_D(QTextBlockGroup);
+ QTextBlockGroupPrivate::BlockList::Iterator it = qLowerBound(d->blocks.begin(), d->blocks.end(), block);
+ d->blocks.insert(it, block);
+ d->markBlocksDirty();
+}
+
+// ### DOC: Shouldn't this be removeBlock()?
+/*!
+ Removes the given \a block from the group; the block itself is not
+ deleted, it simply isn't a member of this group anymore.
+*/
+void QTextBlockGroup::blockRemoved(const QTextBlock &block)
+{
+ Q_D(QTextBlockGroup);
+ d->blocks.removeAll(block);
+ d->markBlocksDirty();
+ if (d->blocks.isEmpty()) {
+ document()->docHandle()->deleteObject(this);
+ return;
+ }
+}
+
+/*!
+ This function is called whenever the specified \a block of text is changed.
+ The text block is a member of this group.
+
+ The base class implementation does nothing.
+*/
+void QTextBlockGroup::blockFormatChanged(const QTextBlock &)
+{
+}
+
+/*!
+ Returns a (possibly empty) list of all the blocks that are part of
+ the block group.
+*/
+QList<QTextBlock> QTextBlockGroup::blockList() const
+{
+ Q_D(const QTextBlockGroup);
+ return d->blocks;
+}
+
+
+
+QTextFrameLayoutData::~QTextFrameLayoutData()
+{
+}
+
+
+/*!
+ \class QTextFrame
+ \reentrant
+
+ \brief The QTextFrame class represents a frame in a QTextDocument.
+
+ \ingroup text
+
+ Text frames provide structure for the text in a document. They are used
+ as generic containers for other document elements.
+ Frames are usually created by using QTextCursor::insertFrame().
+
+ \omit
+ Each frame in a document consists of a frame start character,
+ QChar(0xFDD0), followed by the frame's contents, followed by a
+ frame end character, QChar(0xFDD1). The character formats of the
+ start and end character contain a reference to the frame object's
+ objectIndex.
+ \endomit
+
+ Frames can be used to create hierarchical structures in rich text documents.
+ Each document has a root frame (QTextDocument::rootFrame()), and each frame
+ beneath the root frame has a parent frame and a (possibly empty) list of
+ child frames. The parent frame can be found with parentFrame(), and the
+ childFrames() function provides a list of child frames.
+
+ Each frame contains at least one text block to enable text cursors to
+ insert new document elements within. As a result, the QTextFrame::iterator
+ class is used to traverse both the blocks and child frames within a given
+ frame. The first and last child elements in the frame can be found with
+ begin() and end().
+
+ A frame also has a format (specified using QTextFrameFormat) which can be set
+ with setFormat() and read with format().
+
+ Text cursors can be obtained that point to the first and last valid cursor
+ positions within a frame; use the firstCursorPosition() and
+ lastCursorPosition() functions for this. The frame's extent in the
+ document can be found with firstPosition() and lastPosition().
+
+ You can iterate over a frame's contents using the
+ QTextFrame::iterator class: this provides read-only access to its
+ internal list of text blocks and child frames.
+
+ \sa QTextCursor QTextDocument
+*/
+
+/*!
+ \typedef QTextFrame::Iterator
+
+ Qt-style synonym for QTextFrame::iterator.
+*/
+
+/*!
+ \fn QTextFrame *QTextFrame::iterator::parentFrame() const
+
+ Returns the parent frame of the current frame.
+
+ \sa currentFrame() QTextFrame::parentFrame()
+*/
+
+/*!
+ \fn bool QTextFrame::iterator::operator==(const iterator &other) const
+
+ Retuns true if the iterator is the same as the \a other iterator;
+ otherwise returns false.
+*/
+
+/*!
+ \fn bool QTextFrame::iterator::operator!=(const iterator &other) const
+
+ Retuns true if the iterator is different from the \a other iterator;
+ otherwise returns false.
+*/
+
+/*!
+ \fn QTextFrame::iterator QTextFrame::iterator::operator++(int)
+
+ The postfix ++ operator (\c{i++}) advances the iterator to the
+ next item in the text frame, and returns an iterator to the old item.
+*/
+
+/*!
+ \fn QTextFrame::iterator QTextFrame::iterator::operator--(int)
+
+ The postfix -- operator (\c{i--}) makes the preceding item in the
+ current frame, and returns an iterator to the old item.
+*/
+
+/*!
+ \fn void QTextFrame::setFrameFormat(const QTextFrameFormat &format)
+
+ Sets the frame's \a format.
+
+ \sa frameFormat()
+*/
+
+/*!
+ \fn QTextFrameFormat QTextFrame::frameFormat() const
+
+ Returns the frame's format.
+
+ \sa setFrameFormat()
+*/
+
+/*!
+ \fn QTextFrame::QTextFrame(QTextDocument *document)
+
+ Creates a new empty frame for the text \a document.
+*/
+QTextFrame::QTextFrame(QTextDocument *doc)
+ : QTextObject(*new QTextFramePrivate, doc)
+{
+ Q_D(QTextFrame);
+ d->fragment_start = 0;
+ d->fragment_end = 0;
+ d->parentFrame = 0;
+ d->layoutData = 0;
+}
+
+// ### DOC: What does this do to child frames?
+/*!
+ Destroys the frame, and removes it from the document's layout.
+*/
+QTextFrame::~QTextFrame()
+{
+ Q_D(QTextFrame);
+ delete d->layoutData;
+}
+
+/*!
+ \internal
+*/
+QTextFrame::QTextFrame(QTextFramePrivate &p, QTextDocument *doc)
+ : QTextObject(p, doc)
+{
+ Q_D(QTextFrame);
+ d->fragment_start = 0;
+ d->fragment_end = 0;
+ d->parentFrame = 0;
+ d->layoutData = 0;
+}
+
+/*!
+ Returns a (possibly empty) list of the frame's child frames.
+
+ \sa parentFrame()
+*/
+QList<QTextFrame *> QTextFrame::childFrames() const
+{
+ Q_D(const QTextFrame);
+ return d->childFrames;
+}
+
+/*!
+ Returns the frame's parent frame. If the frame is the root frame of a
+ document, this will return 0.
+
+ \sa childFrames() QTextDocument::rootFrame()
+*/
+QTextFrame *QTextFrame::parentFrame() const
+{
+ Q_D(const QTextFrame);
+ return d->parentFrame;
+}
+
+
+/*!
+ Returns the first cursor position inside the frame.
+
+ \sa lastCursorPosition() firstPosition() lastPosition()
+*/
+QTextCursor QTextFrame::firstCursorPosition() const
+{
+ Q_D(const QTextFrame);
+ return QTextCursor(d->pieceTable, firstPosition());
+}
+
+/*!
+ Returns the last cursor position inside the frame.
+
+ \sa firstCursorPosition() firstPosition() lastPosition()
+*/
+QTextCursor QTextFrame::lastCursorPosition() const
+{
+ Q_D(const QTextFrame);
+ return QTextCursor(d->pieceTable, lastPosition());
+}
+
+/*!
+ Returns the first document position inside the frame.
+
+ \sa lastPosition() firstCursorPosition() lastCursorPosition()
+*/
+int QTextFrame::firstPosition() const
+{
+ Q_D(const QTextFrame);
+ if (!d->fragment_start)
+ return 0;
+ return d->pieceTable->fragmentMap().position(d->fragment_start) + 1;
+}
+
+/*!
+ Returns the last document position inside the frame.
+
+ \sa firstPosition() firstCursorPosition() lastCursorPosition()
+*/
+int QTextFrame::lastPosition() const
+{
+ Q_D(const QTextFrame);
+ if (!d->fragment_end)
+ return d->pieceTable->length() - 1;
+ return d->pieceTable->fragmentMap().position(d->fragment_end);
+}
+
+/*!
+ \internal
+*/
+QTextFrameLayoutData *QTextFrame::layoutData() const
+{
+ Q_D(const QTextFrame);
+ return d->layoutData;
+}
+
+/*!
+ \internal
+*/
+void QTextFrame::setLayoutData(QTextFrameLayoutData *data)
+{
+ Q_D(QTextFrame);
+ delete d->layoutData;
+ d->layoutData = data;
+}
+
+
+
+void QTextFramePrivate::fragmentAdded(const QChar &type, uint fragment)
+{
+ if (type == QTextBeginningOfFrame) {
+ Q_ASSERT(!fragment_start);
+ fragment_start = fragment;
+ } else if (type == QTextEndOfFrame) {
+ Q_ASSERT(!fragment_end);
+ fragment_end = fragment;
+ } else if (type == QChar::ObjectReplacementCharacter) {
+ Q_ASSERT(!fragment_start);
+ Q_ASSERT(!fragment_end);
+ fragment_start = fragment;
+ fragment_end = fragment;
+ } else {
+ Q_ASSERT(false);
+ }
+}
+
+void QTextFramePrivate::fragmentRemoved(const QChar &type, uint fragment)
+{
+ Q_UNUSED(fragment); // --release warning
+ if (type == QTextBeginningOfFrame) {
+ Q_ASSERT(fragment_start == fragment);
+ fragment_start = 0;
+ } else if (type == QTextEndOfFrame) {
+ Q_ASSERT(fragment_end == fragment);
+ fragment_end = 0;
+ } else if (type == QChar::ObjectReplacementCharacter) {
+ Q_ASSERT(fragment_start == fragment);
+ Q_ASSERT(fragment_end == fragment);
+ fragment_start = 0;
+ fragment_end = 0;
+ } else {
+ Q_ASSERT(false);
+ }
+ remove_me();
+}
+
+
+void QTextFramePrivate::remove_me()
+{
+ Q_Q(QTextFrame);
+ if (fragment_start == 0 && fragment_end == 0
+ && !parentFrame) {
+ q->document()->docHandle()->deleteObject(q);
+ return;
+ }
+
+ if (!parentFrame)
+ return;
+
+ int index = parentFrame->d_func()->childFrames.indexOf(q);
+
+ // iterator over all children and move them to the parent
+ for (int i = 0; i < childFrames.size(); ++i) {
+ QTextFrame *c = childFrames.at(i);
+ parentFrame->d_func()->childFrames.insert(index, c);
+ c->d_func()->parentFrame = parentFrame;
+ ++index;
+ }
+ Q_ASSERT(parentFrame->d_func()->childFrames.at(index) == q);
+ parentFrame->d_func()->childFrames.removeAt(index);
+
+ childFrames.clear();
+ parentFrame = 0;
+}
+
+/*!
+ \class QTextFrame::iterator
+ \reentrant
+
+ \brief The iterator class provides an iterator for reading
+ the contents of a QTextFrame.
+
+ \ingroup text
+
+ A frame consists of an arbitrary sequence of \l{QTextBlock}s and
+ child \l{QTextFrame}s. This class provides a way to iterate over the
+ child objects of a frame, and read their contents. It does not provide
+ a way to modify the contents of the frame.
+
+*/
+
+/*!
+ \fn bool QTextFrame::iterator::atEnd() const
+
+ Returns true if the current item is the last item in the text frame.
+*/
+
+/*!
+ Returns an iterator pointing to the first document element inside the frame.
+
+ \sa end()
+*/
+QTextFrame::iterator QTextFrame::begin() const
+{
+ const QTextDocumentPrivate *priv = docHandle();
+ int b = priv->blockMap().findNode(firstPosition());
+ int e = priv->blockMap().findNode(lastPosition()+1);
+ return iterator(const_cast<QTextFrame *>(this), b, b, e);
+}
+
+/*!
+ Returns an iterator pointing to the last document element inside the frame.
+
+ \sa begin()
+*/
+QTextFrame::iterator QTextFrame::end() const
+{
+ const QTextDocumentPrivate *priv = docHandle();
+ int b = priv->blockMap().findNode(firstPosition());
+ int e = priv->blockMap().findNode(lastPosition()+1);
+ return iterator(const_cast<QTextFrame *>(this), e, b, e);
+}
+
+/*!
+ Constructs an invalid iterator.
+*/
+QTextFrame::iterator::iterator()
+{
+ f = 0;
+ b = 0;
+ e = 0;
+ cf = 0;
+ cb = 0;
+}
+
+/*!
+ \internal
+*/
+QTextFrame::iterator::iterator(QTextFrame *frame, int block, int begin, int end)
+{
+ f = frame;
+ b = begin;
+ e = end;
+ cf = 0;
+ cb = block;
+}
+
+/*!
+ Copy constructor. Constructs a copy of the \a other iterator.
+*/
+QTextFrame::iterator::iterator(const iterator &other)
+{
+ f = other.f;
+ b = other.b;
+ e = other.e;
+ cf = other.cf;
+ cb = other.cb;
+}
+
+/*!
+ Assigns \a other to this iterator and returns a reference to
+ this iterator.
+*/
+QTextFrame::iterator &QTextFrame::iterator::operator=(const iterator &other)
+{
+ f = other.f;
+ b = other.b;
+ e = other.e;
+ cf = other.cf;
+ cb = other.cb;
+ return *this;
+}
+
+/*!
+ Returns the current frame pointed to by the iterator, or 0 if the
+ iterator currently points to a block.
+
+ \sa currentBlock()
+*/
+QTextFrame *QTextFrame::iterator::currentFrame() const
+{
+ return cf;
+}
+
+/*!
+ Returns the current block the iterator points to. If the iterator
+ points to a child frame, the returned block is invalid.
+
+ \sa currentFrame()
+*/
+QTextBlock QTextFrame::iterator::currentBlock() const
+{
+ if (!f)
+ return QTextBlock();
+ return QTextBlock(f->docHandle(), cb);
+}
+
+/*!
+ Moves the iterator to the next frame or block.
+
+ \sa currentBlock() currentFrame()
+*/
+QTextFrame::iterator &QTextFrame::iterator::operator++()
+{
+ const QTextDocumentPrivate *priv = f->docHandle();
+ const QTextDocumentPrivate::BlockMap &map = priv->blockMap();
+ if (cf) {
+ int end = cf->lastPosition() + 1;
+ cb = map.findNode(end);
+ cf = 0;
+ } else if (cb) {
+ cb = map.next(cb);
+ if (cb == e)
+ return *this;
+
+ if (!f->d_func()->childFrames.isEmpty()) {
+ int pos = map.position(cb);
+ // check if we entered a frame
+ QTextDocumentPrivate::FragmentIterator frag = priv->find(pos-1);
+ if (priv->buffer().at(frag->stringPosition) != QChar::ParagraphSeparator) {
+ QTextFrame *nf = qobject_cast<QTextFrame *>(priv->objectForFormat(frag->format));
+ if (nf) {
+ if (priv->buffer().at(frag->stringPosition) == QTextBeginningOfFrame && nf != f) {
+ cf = nf;
+ cb = 0;
+ } else {
+ Q_ASSERT(priv->buffer().at(frag->stringPosition) != QTextEndOfFrame);
+ }
+ }
+ }
+ }
+ }
+ return *this;
+}
+
+/*!
+ Moves the iterator to the previous frame or block.
+
+ \sa currentBlock() currentFrame()
+*/
+QTextFrame::iterator &QTextFrame::iterator::operator--()
+{
+ const QTextDocumentPrivate *priv = f->docHandle();
+ const QTextDocumentPrivate::BlockMap &map = priv->blockMap();
+ if (cf) {
+ int start = cf->firstPosition() - 1;
+ cb = map.findNode(start);
+ cf = 0;
+ } else {
+ if (cb == b)
+ goto end;
+ if (cb != e) {
+ int pos = map.position(cb);
+ // check if we have to enter a frame
+ QTextDocumentPrivate::FragmentIterator frag = priv->find(pos-1);
+ if (priv->buffer().at(frag->stringPosition) != QChar::ParagraphSeparator) {
+ QTextFrame *pf = qobject_cast<QTextFrame *>(priv->objectForFormat(frag->format));
+ if (pf) {
+ if (priv->buffer().at(frag->stringPosition) == QTextBeginningOfFrame) {
+ Q_ASSERT(pf == f);
+ } else if (priv->buffer().at(frag->stringPosition) == QTextEndOfFrame) {
+ Q_ASSERT(pf != f);
+ cf = pf;
+ cb = 0;
+ goto end;
+ }
+ }
+ }
+ }
+ cb = map.previous(cb);
+ }
+ end:
+ return *this;
+}
+
+/*!
+ \class QTextBlockUserData
+ \reentrant
+
+ \brief The QTextBlockUserData class is used to associate custom data with blocks of text.
+ \since 4.1
+
+ \ingroup text
+
+ QTextBlockUserData provides an abstract interface for container classes that are used
+ to associate application-specific user data with text blocks in a QTextDocument.
+
+ Generally, subclasses of this class provide functions to allow data to be stored
+ and retrieved, and instances are attached to blocks of text using
+ QTextBlock::setUserData(). This makes it possible to store additional data per text
+ block in a way that can be retrieved safely by the application.
+
+ Each subclass should provide a reimplementation of the destructor to ensure that any
+ private data is automatically cleaned up when user data objects are deleted.
+
+ \sa QTextBlock
+*/
+
+/*!
+ Destroys the user data.
+*/
+QTextBlockUserData::~QTextBlockUserData()
+{
+}
+
+/*!
+ \class QTextBlock
+ \reentrant
+
+ \brief The QTextBlock class provides a container for text fragments in a
+ QTextDocument.
+
+ \ingroup text
+
+ A text block encapsulates a block or paragraph of text in a QTextDocument.
+ QTextBlock provides read-only access to the block/paragraph structure of
+ QTextDocuments. It is mainly of use if you want to implement your own
+ layouts for the visual representation of a QTextDocument, or if you want to
+ iterate over a document and write out the contents in your own custom
+ format.
+
+ Text blocks are created by their parent documents. If you need to create
+ a new text block, or modify the contents of a document while examining its
+ contents, use the cursor-based interface provided by QTextCursor instead.
+
+ Each text block is located at a specific position() in a document().
+ The contents of the block can be obtained by using the text() function.
+ The length() function determines the block's size within the document
+ (including formatting characters).
+ The visual properties of the block are determined by its text layout(),
+ its charFormat(), and its blockFormat().
+
+ The next() and previous() functions enable iteration over consecutive
+ valid blocks in a document under the condition that the document is not
+ modified by other means during the iteration process. Note that, although
+ blocks are returned in sequence, adjacent blocks may come from different
+ places in the document structure. The validity of a block can be determined
+ by calling isValid().
+
+ QTextBlock provides comparison operators to make it easier to work with
+ blocks: \l operator==() compares two block for equality, \l operator!=()
+ compares two blocks for inequality, and \l operator<() determines whether
+ a block precedes another in the same document.
+
+ \img qtextblock-sequence.png
+
+ \sa QTextBlockFormat QTextCharFormat QTextFragment
+ */
+
+/*!
+ \fn QTextBlock::QTextBlock(QTextDocumentPrivate *priv, int b)
+
+ \internal
+*/
+
+/*!
+ \fn QTextBlock::QTextBlock()
+
+ \internal
+*/
+
+/*!
+ \fn QTextBlock::QTextBlock(const QTextBlock &other)
+
+ Copies the \a other text block's attributes to this text block.
+*/
+
+/*!
+ \fn bool QTextBlock::isValid() const
+
+ Returns true if this text block is valid; otherwise returns false.
+*/
+
+/*!
+ \fn QTextBlock &QTextBlock::operator=(const QTextBlock &other)
+
+ Assigns the \a other text block to this text block.
+*/
+
+/*!
+ \fn bool QTextBlock::operator==(const QTextBlock &other) const
+
+ Returns true if this text block is the same as the \a other text
+ block.
+*/
+
+/*!
+ \fn bool QTextBlock::operator!=(const QTextBlock &other) const
+
+ Returns true if this text block is different from the \a other
+ text block.
+*/
+
+/*!
+ \fn bool QTextBlock::operator<(const QTextBlock &other) const
+
+ Returns true if this text block occurs before the \a other text
+ block in the document.
+*/
+
+/*!
+ \class QTextBlock::iterator
+ \reentrant
+
+ \brief The QTextBlock::iterator class provides an iterator for reading
+ the contents of a QTextBlock.
+
+ \ingroup text
+
+ A block consists of a sequence of text fragments. This class provides
+ a way to iterate over these, and read their contents. It does not provide
+ a way to modify the internal structure or contents of the block.
+
+ An iterator can be constructed and used to access the fragments within
+ a text block in the following way:
+
+ \snippet doc/src/snippets/textblock-fragments/xmlwriter.cpp 4
+ \snippet doc/src/snippets/textblock-fragments/xmlwriter.cpp 7
+
+ \sa QTextFragment
+*/
+
+/*!
+ \typedef QTextBlock::Iterator
+
+ Qt-style synonym for QTextBlock::iterator.
+*/
+
+/*!
+ \fn QTextBlock::iterator::iterator()
+
+ Constructs an iterator for this text block.
+*/
+
+/*!
+ \fn QTextBlock::iterator::iterator(const iterator &other)
+
+ Copy constructor. Constructs a copy of the \a other iterator.
+*/
+
+/*!
+ \fn bool QTextBlock::iterator::atEnd() const
+
+ Returns true if the current item is the last item in the text block.
+*/
+
+/*!
+ \fn bool QTextBlock::iterator::operator==(const iterator &other) const
+
+ Retuns true if this iterator is the same as the \a other iterator;
+ otherwise returns false.
+*/
+
+/*!
+ \fn bool QTextBlock::iterator::operator!=(const iterator &other) const
+
+ Retuns true if this iterator is different from the \a other iterator;
+ otherwise returns false.
+*/
+
+/*!
+ \fn QTextBlock::iterator QTextBlock::iterator::operator++(int)
+
+ The postfix ++ operator (\c{i++}) advances the iterator to the
+ next item in the text block and returns an iterator to the old current
+ item.
+*/
+
+/*!
+ \fn QTextBlock::iterator QTextBlock::iterator::operator--(int)
+
+ The postfix -- operator (\c{i--}) makes the preceding item current and
+ returns an iterator to the old current item.
+*/
+
+/*!
+ \fn QTextDocumentPrivate *QTextBlock::docHandle() const
+
+ \internal
+*/
+
+/*!
+ \fn int QTextBlock::fragmentIndex() const
+
+ \internal
+*/
+
+/*!
+ Returns the index of the block's first character within the document.
+ */
+int QTextBlock::position() const
+{
+ if (!p || !n)
+ return 0;
+
+ return p->blockMap().position(n);
+}
+
+/*!
+ Returns the length of the block in characters.
+
+ \note The length returned includes all formatting characters,
+ for example, newline.
+
+ \sa text() charFormat() blockFormat()
+ */
+int QTextBlock::length() const
+{
+ if (!p || !n)
+ return 0;
+
+ return p->blockMap().size(n);
+}
+
+/*!
+ Returns true if the given \a position is located within the text
+ block; otherwise returns false.
+ */
+bool QTextBlock::contains(int position) const
+{
+ if (!p || !n)
+ return false;
+
+ int pos = p->blockMap().position(n);
+ int len = p->blockMap().size(n);
+ return position >= pos && position < pos + len;
+}
+
+/*!
+ Returns the QTextLayout that is used to lay out and display the
+ block's contents.
+
+ Note that the returned QTextLayout object can only be modified from the
+ documentChanged implementation of a QAbstractTextDocumentLayout subclass.
+ Any changes applied from the outside cause undefined behavior.
+
+ \sa clearLayout()
+ */
+QTextLayout *QTextBlock::layout() const
+{
+ if (!p || !n)
+ return 0;
+
+ const QTextBlockData *b = p->blockMap().fragment(n);
+ if (!b->layout)
+ b->layout = new QTextLayout(*this);
+ return b->layout;
+}
+
+/*!
+ \since 4.4
+ Clears the QTextLayout that is used to lay out and display the
+ block's contents.
+
+ \sa layout()
+ */
+void QTextBlock::clearLayout()
+{
+ if (!p || !n)
+ return;
+
+ const QTextBlockData *b = p->blockMap().fragment(n);
+ if (b->layout)
+ b->layout->clearLayout();
+}
+
+/*!
+ Returns the QTextBlockFormat that describes block-specific properties.
+
+ \sa charFormat()
+ */
+QTextBlockFormat QTextBlock::blockFormat() const
+{
+ if (!p || !n)
+ return QTextFormat().toBlockFormat();
+
+ return p->formatCollection()->blockFormat(p->blockMap().fragment(n)->format);
+}
+
+/*!
+ Returns an index into the document's internal list of block formats
+ for the text block's format.
+
+ \sa QTextDocument::allFormats()
+*/
+int QTextBlock::blockFormatIndex() const
+{
+ if (!p || !n)
+ return -1;
+
+ return p->blockMap().fragment(n)->format;
+}
+
+/*!
+ Returns the QTextCharFormat that describes the block's character
+ format. The block's character format is used when inserting text into
+ an empty block.
+
+ \sa blockFormat()
+ */
+QTextCharFormat QTextBlock::charFormat() const
+{
+ if (!p || !n)
+ return QTextFormat().toCharFormat();
+
+ return p->formatCollection()->charFormat(charFormatIndex());
+}
+
+/*!
+ Returns an index into the document's internal list of character formats
+ for the text block's character format.
+
+ \sa QTextDocument::allFormats()
+*/
+int QTextBlock::charFormatIndex() const
+{
+ if (!p || !n)
+ return -1;
+
+ return p->blockCharFormatIndex(n);
+}
+
+/*!
+ Returns the block's contents as plain text.
+
+ \sa length() charFormat() blockFormat()
+ */
+QString QTextBlock::text() const
+{
+ if (!p || !n)
+ return QString();
+
+ const QString buffer = p->buffer();
+ QString text;
+ text.reserve(length());
+
+ const int pos = position();
+ QTextDocumentPrivate::FragmentIterator it = p->find(pos);
+ QTextDocumentPrivate::FragmentIterator end = p->find(pos + length() - 1); // -1 to omit the block separator char
+ for (; it != end; ++it) {
+ const QTextFragmentData * const frag = it.value();
+ text += QString::fromRawData(buffer.constData() + frag->stringPosition, frag->size_array[0]);
+ }
+
+ return text;
+}
+
+
+/*!
+ Returns the text document this text block belongs to, or 0 if the
+ text block does not belong to any document.
+*/
+const QTextDocument *QTextBlock::document() const
+{
+ return p ? p->document() : 0;
+}
+
+/*!
+ If the block represents a list item, returns the list that the item belongs
+ to; otherwise returns 0.
+*/
+QTextList *QTextBlock::textList() const
+{
+ if (!isValid())
+ return 0;
+
+ const QTextBlockFormat fmt = blockFormat();
+ QTextObject *obj = p->document()->objectForFormat(fmt);
+ return qobject_cast<QTextList *>(obj);
+}
+
+/*!
+ \since 4.1
+
+ Returns a pointer to a QTextBlockUserData object if previously set with
+ setUserData() or a null pointer.
+*/
+QTextBlockUserData *QTextBlock::userData() const
+{
+ if (!p || !n)
+ return 0;
+
+ const QTextBlockData *b = p->blockMap().fragment(n);
+ return b->userData;
+}
+
+/*!
+ \since 4.1
+
+ Attaches the given \a data object to the text block.
+
+ QTextBlockUserData can be used to store custom settings. The
+ ownership is passed to the underlying text document, i.e. the
+ provided QTextBlockUserData object will be deleted if the
+ corresponding text block gets deleted. The user data object is
+ not stored in the undo history, so it will not be available after
+ undoing the deletion of a text block.
+
+ For example, if you write a programming editor in an IDE, you may
+ want to let your user set breakpoints visually in your code for an
+ integrated debugger. In a programming editor a line of text
+ usually corresponds to one QTextBlock. The QTextBlockUserData
+ interface allows the developer to store data for each QTextBlock,
+ like for example in which lines of the source code the user has a
+ breakpoint set. Of course this could also be stored externally,
+ but by storing it inside the QTextDocument, it will for example be
+ automatically deleted when the user deletes the associated
+ line. It's really just a way to store custom information in the
+ QTextDocument without using custom properties in QTextFormat which
+ would affect the undo/redo stack.
+*/
+void QTextBlock::setUserData(QTextBlockUserData *data)
+{
+ if (!p || !n)
+ return;
+
+ const QTextBlockData *b = p->blockMap().fragment(n);
+ if (data != b->userData)
+ delete b->userData;
+ b->userData = data;
+}
+
+/*!
+ \since 4.1
+
+ Returns the integer value previously set with setUserState() or -1.
+*/
+int QTextBlock::userState() const
+{
+ if (!p || !n)
+ return -1;
+
+ const QTextBlockData *b = p->blockMap().fragment(n);
+ return b->userState;
+}
+
+/*!
+ \since 4.1
+
+ Stores the specified \a state integer value in the text block. This may be
+ useful for example in a syntax highlighter to store a text parsing state.
+*/
+void QTextBlock::setUserState(int state)
+{
+ if (!p || !n)
+ return;
+
+ const QTextBlockData *b = p->blockMap().fragment(n);
+ b->userState = state;
+}
+
+/*!
+ \since 4.4
+
+ Returns the blocks revision.
+
+ \sa setRevision(), QTextDocument::revision()
+*/
+int QTextBlock::revision() const
+{
+ if (!p || !n)
+ return -1;
+
+ const QTextBlockData *b = p->blockMap().fragment(n);
+ return b->revision;
+}
+
+/*!
+ \since 4.4
+
+ Sets a blocks revision to \a rev.
+
+ \sa revision(), QTextDocument::revision()
+*/
+void QTextBlock::setRevision(int rev)
+{
+ if (!p || !n)
+ return;
+
+ const QTextBlockData *b = p->blockMap().fragment(n);
+ b->revision = rev;
+}
+
+/*!
+ \since 4.4
+
+ Returns true if the block is visible; otherwise returns false.
+
+ \sa setVisible()
+*/
+bool QTextBlock::isVisible() const
+{
+ if (!p || !n)
+ return true;
+
+ const QTextBlockData *b = p->blockMap().fragment(n);
+ return !b->hidden;
+}
+
+/*!
+ \since 4.4
+
+ Sets the block's visibility to \a visible.
+
+ \sa isVisible()
+*/
+void QTextBlock::setVisible(bool visible)
+{
+ if (!p || !n)
+ return;
+
+ const QTextBlockData *b = p->blockMap().fragment(n);
+ b->hidden = !visible;
+}
+
+
+/*!
+\since 4.4
+
+ Returns the number of this block, or -1 if the block is invalid.
+
+ \sa QTextCursor::blockNumber()
+
+*/
+int QTextBlock::blockNumber() const
+{
+ if (!p || !n)
+ return -1;
+ return p->blockMap().position(n, 1);
+}
+
+/*!
+\since 4.5
+
+ Returns the first line number of this block, or -1 if the block is invalid.
+ Unless the layout supports it, the line number is identical to the block number.
+
+ \sa QTextBlock::blockNumber()
+
+*/
+int QTextBlock::firstLineNumber() const
+{
+ if (!p || !n)
+ return -1;
+ return p->blockMap().position(n, 2);
+}
+
+
+/*!
+\since 4.5
+
+Sets the line count to \a count.
+
+/sa lineCount()
+*/
+void QTextBlock::setLineCount(int count)
+{
+ if (!p || !n)
+ return;
+ p->blockMap().setSize(n, count, 2);
+}
+/*!
+\since 4.5
+
+Returns the line count. Not all document layouts support this feature.
+
+\sa setLineCount()
+ */
+int QTextBlock::lineCount() const
+{
+ if (!p || !n)
+ return -1;
+ return p->blockMap().size(n, 2);
+}
+
+
+/*!
+ Returns a text block iterator pointing to the beginning of the
+ text block.
+
+ \sa end()
+*/
+QTextBlock::iterator QTextBlock::begin() const
+{
+ if (!p || !n)
+ return iterator();
+
+ int pos = position();
+ int len = length() - 1; // exclude the fragment that holds the paragraph separator
+ int b = p->fragmentMap().findNode(pos);
+ int e = p->fragmentMap().findNode(pos+len);
+ return iterator(p, b, e, b);
+}
+
+/*!
+ Returns a text block iterator pointing to the end of the text
+ block.
+
+ \sa begin() next() previous()
+*/
+QTextBlock::iterator QTextBlock::end() const
+{
+ if (!p || !n)
+ return iterator();
+
+ int pos = position();
+ int len = length() - 1; // exclude the fragment that holds the paragraph separator
+ int b = p->fragmentMap().findNode(pos);
+ int e = p->fragmentMap().findNode(pos+len);
+ return iterator(p, b, e, e);
+}
+
+
+/*!
+ Returns the text block in the document after this block, or an empty
+ text block if this is the last one.
+
+ Note that the next block may be in a different frame or table to this block.
+
+ \sa previous() begin() end()
+*/
+QTextBlock QTextBlock::next() const
+{
+ if (!p)
+ return QTextBlock();
+
+ return QTextBlock(p, p->blockMap().next(n));
+}
+
+/*!
+ Returns the text block in the document before this block, or an empty text
+ block if this is the first one.
+
+ Note that the next block may be in a different frame or table to this block.
+
+ \sa next() begin() end()
+*/
+QTextBlock QTextBlock::previous() const
+{
+ if (!p)
+ return QTextBlock();
+
+ return QTextBlock(p, p->blockMap().previous(n));
+}
+
+
+/*!
+ Returns the text fragment the iterator currently points to.
+*/
+QTextFragment QTextBlock::iterator::fragment() const
+{
+ int ne = n;
+ int formatIndex = p->fragmentMap().fragment(n)->format;
+ do {
+ ne = p->fragmentMap().next(ne);
+ } while (ne != e && p->fragmentMap().fragment(ne)->format == formatIndex);
+ return QTextFragment(p, n, ne);
+}
+
+/*!
+ The prefix ++ operator (\c{++i}) advances the iterator to the
+ next item in the hash and returns an iterator to the new current
+ item.
+*/
+
+QTextBlock::iterator &QTextBlock::iterator::operator++()
+{
+ int ne = n;
+ int formatIndex = p->fragmentMap().fragment(n)->format;
+ do {
+ ne = p->fragmentMap().next(ne);
+ } while (ne != e && p->fragmentMap().fragment(ne)->format == formatIndex);
+ n = ne;
+ return *this;
+}
+
+/*!
+ The prefix -- operator (\c{--i}) makes the preceding item
+ current and returns an iterator pointing to the new current item.
+*/
+
+QTextBlock::iterator &QTextBlock::iterator::operator--()
+{
+ n = p->fragmentMap().previous(n);
+
+ if (n == b)
+ return *this;
+
+ int formatIndex = p->fragmentMap().fragment(n)->format;
+ int last = n;
+
+ while (n != b && p->fragmentMap().fragment(n)->format != formatIndex) {
+ last = n;
+ n = p->fragmentMap().previous(n);
+ }
+
+ n = last;
+ return *this;
+}
+
+
+/*!
+ \class QTextFragment
+ \reentrant
+
+ \brief The QTextFragment class holds a piece of text in a
+ QTextDocument with a single QTextCharFormat.
+
+ \ingroup text
+
+ A text fragment describes a piece of text that is stored with a single
+ character format. Text in which the character format changes can be
+ represented by sequences of text fragments with different formats.
+
+ If the user edits the text in a fragment and introduces a different
+ character format, the fragment's text will be split at each point where
+ the format changes, and new fragments will be created.
+ For example, changing the style of some text in the middle of a
+ sentence will cause the fragment to be broken into three separate fragments:
+ the first and third with the same format as before, and the second with
+ the new style. The first fragment will contain the text from the beginning
+ of the sentence, the second will contain the text from the middle, and the
+ third takes the text from the end of the sentence.
+
+ \img qtextfragment-split.png
+
+ A fragment's text and character format can be obtained with the text()
+ and charFormat() functions. The length() function gives the length of
+ the text in the fragment. position() gives the position in the document
+ of the start of the fragment. To determine whether the fragment contains
+ a particular position within the document, use the contains() function.
+
+ \sa QTextDocument, {Rich Text Document Structure}
+*/
+
+/*!
+ \fn QTextFragment::QTextFragment(const QTextDocumentPrivate *priv, int f, int fe)
+ \internal
+*/
+
+/*!
+ \fn QTextFragment::QTextFragment()
+
+ Creates a new empty text fragment.
+*/
+
+/*!
+ \fn QTextFragment::QTextFragment(const QTextFragment &other)
+
+ Copies the content (text and format) of the \a other text fragment
+ to this text fragment.
+*/
+
+/*!
+ \fn QTextFragment &QTextFragment::operator=(const QTextFragment
+ &other)
+
+ Assigns the content (text and format) of the \a other text fragment
+ to this text fragment.
+*/
+
+/*!
+ \fn bool QTextFragment::isValid() const
+
+ Returns true if this is a valid text fragment (i.e. has a valid
+ position in a document); otherwise returns false.
+*/
+
+/*!
+ \fn bool QTextFragment::operator==(const QTextFragment &other) const
+
+ Returns true if this text fragment is the same (at the same
+ position) as the \a other text fragment; otherwise returns false.
+*/
+
+/*!
+ \fn bool QTextFragment::operator!=(const QTextFragment &other) const
+
+ Returns true if this text fragment is different (at a different
+ position) from the \a other text fragment; otherwise returns
+ false.
+*/
+
+/*!
+ \fn bool QTextFragment::operator<(const QTextFragment &other) const
+
+ Returns true if this text fragment appears earlier in the document
+ than the \a other text fragment; otherwise returns false.
+*/
+
+
+/*!
+ Returns the position of this text fragment in the document.
+*/
+int QTextFragment::position() const
+{
+ if (!p || !n)
+ return 0; // ### -1 instead?
+
+ return p->fragmentMap().position(n);
+}
+
+/*!
+ Returns the number of characters in the text fragment.
+
+ \sa text()
+*/
+int QTextFragment::length() const
+{
+ if (!p || !n)
+ return 0;
+
+ int len = 0;
+ int f = n;
+ while (f != ne) {
+ len += p->fragmentMap().size(f);
+ f = p->fragmentMap().next(f);
+ }
+ return len;
+}
+
+/*!
+ Returns true if the text fragment contains the text at the given
+ \a position in the document; otherwise returns false.
+*/
+bool QTextFragment::contains(int position) const
+{
+ if (!p || !n)
+ return false;
+ int pos = this->position();
+ return position >= pos && position < pos + length();
+}
+
+/*!
+ Returns the text fragment's character format.
+
+ \sa text()
+*/
+QTextCharFormat QTextFragment::charFormat() const
+{
+ if (!p || !n)
+ return QTextCharFormat();
+ const QTextFragmentData *data = p->fragmentMap().fragment(n);
+ return p->formatCollection()->charFormat(data->format);
+}
+
+/*!
+ Returns an index into the document's internal list of character formats
+ for the text fragment's character format.
+
+ \sa QTextDocument::allFormats()
+*/
+int QTextFragment::charFormatIndex() const
+{
+ if (!p || !n)
+ return -1;
+ const QTextFragmentData *data = p->fragmentMap().fragment(n);
+ return data->format;
+}
+
+/*!
+ Returns the text fragment's as plain text.
+
+ \sa length(), charFormat()
+*/
+QString QTextFragment::text() const
+{
+ if (!p || !n)
+ return QString();
+
+ QString result;
+ QString buffer = p->buffer();
+ int f = n;
+ while (f != ne) {
+ const QTextFragmentData * const frag = p->fragmentMap().fragment(f);
+ result += QString(buffer.constData() + frag->stringPosition, frag->size_array[0]);
+ f = p->fragmentMap().next(f);
+ }
+ return result;
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/text/qtextobject.h b/src/gui/text/qtextobject.h
new file mode 100644
index 0000000000..5175441374
--- /dev/null
+++ b/src/gui/text/qtextobject.h
@@ -0,0 +1,328 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QTEXTOBJECT_H
+#define QTEXTOBJECT_H
+
+#include <QtCore/qobject.h>
+#include <QtGui/qtextformat.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+class QTextObjectPrivate;
+class QTextDocument;
+class QTextDocumentPrivate;
+class QTextCursor;
+class QTextBlock;
+class QTextFragment;
+class QTextLayout;
+class QTextList;
+
+class Q_GUI_EXPORT QTextObject : public QObject
+{
+ Q_OBJECT
+
+protected:
+ explicit QTextObject(QTextDocument *doc);
+ ~QTextObject();
+
+ void setFormat(const QTextFormat &format);
+
+public:
+ QTextFormat format() const;
+ int formatIndex() const;
+
+ QTextDocument *document() const;
+
+ int objectIndex() const;
+
+ QTextDocumentPrivate *docHandle() const;
+
+protected:
+ QTextObject(QTextObjectPrivate &p, QTextDocument *doc);
+
+private:
+ Q_DECLARE_PRIVATE(QTextObject)
+ Q_DISABLE_COPY(QTextObject)
+ friend class QTextDocumentPrivate;
+};
+
+class QTextBlockGroupPrivate;
+class Q_GUI_EXPORT QTextBlockGroup : public QTextObject
+{
+ Q_OBJECT
+
+protected:
+ explicit QTextBlockGroup(QTextDocument *doc);
+ ~QTextBlockGroup();
+
+ virtual void blockInserted(const QTextBlock &block);
+ virtual void blockRemoved(const QTextBlock &block);
+ virtual void blockFormatChanged(const QTextBlock &block);
+
+ QList<QTextBlock> blockList() const;
+
+protected:
+ QTextBlockGroup(QTextBlockGroupPrivate &p, QTextDocument *doc);
+private:
+ Q_DECLARE_PRIVATE(QTextBlockGroup)
+ Q_DISABLE_COPY(QTextBlockGroup)
+ friend class QTextDocumentPrivate;
+};
+
+class Q_GUI_EXPORT QTextFrameLayoutData {
+public:
+ virtual ~QTextFrameLayoutData();
+};
+
+class QTextFramePrivate;
+class Q_GUI_EXPORT QTextFrame : public QTextObject
+{
+ Q_OBJECT
+
+public:
+ explicit QTextFrame(QTextDocument *doc);
+ ~QTextFrame();
+
+ inline void setFrameFormat(const QTextFrameFormat &format);
+ QTextFrameFormat frameFormat() const { return QTextObject::format().toFrameFormat(); }
+
+ QTextCursor firstCursorPosition() const;
+ QTextCursor lastCursorPosition() const;
+ int firstPosition() const;
+ int lastPosition() const;
+
+ QTextFrameLayoutData *layoutData() const;
+ void setLayoutData(QTextFrameLayoutData *data);
+
+ QList<QTextFrame *> childFrames() const;
+ QTextFrame *parentFrame() const;
+
+ class Q_GUI_EXPORT iterator {
+ QTextFrame *f;
+ int b;
+ int e;
+ QTextFrame *cf;
+ int cb;
+
+ friend class QTextFrame;
+ friend class QTextTableCell;
+ friend class QTextDocumentLayoutPrivate;
+ iterator(QTextFrame *frame, int block, int begin, int end);
+ public:
+ iterator();
+ iterator(const iterator &o);
+ iterator &operator=(const iterator &o);
+
+ QTextFrame *parentFrame() const { return f; }
+
+ QTextFrame *currentFrame() const;
+ QTextBlock currentBlock() const;
+
+ bool atEnd() const { return !cf && cb == e; }
+
+ inline bool operator==(const iterator &o) const { return f == o.f && cf == o.cf && cb == o.cb; }
+ inline bool operator!=(const iterator &o) const { return f != o.f || cf != o.cf || cb != o.cb; }
+ iterator &operator++();
+ inline iterator operator++(int) { iterator tmp = *this; operator++(); return tmp; }
+ iterator &operator--();
+ inline iterator operator--(int) { iterator tmp = *this; operator--(); return tmp; }
+ };
+
+ friend class iterator;
+ // more Qt
+ typedef iterator Iterator;
+
+ iterator begin() const;
+ iterator end() const;
+
+protected:
+ QTextFrame(QTextFramePrivate &p, QTextDocument *doc);
+private:
+ friend class QTextDocumentPrivate;
+ Q_DECLARE_PRIVATE(QTextFrame)
+ Q_DISABLE_COPY(QTextFrame)
+};
+Q_DECLARE_TYPEINFO(QTextFrame::iterator, Q_MOVABLE_TYPE);
+
+inline void QTextFrame::setFrameFormat(const QTextFrameFormat &aformat)
+{ QTextObject::setFormat(aformat); }
+
+class Q_GUI_EXPORT QTextBlockUserData {
+public:
+ virtual ~QTextBlockUserData();
+};
+
+class Q_GUI_EXPORT QTextBlock
+{
+ friend class QSyntaxHighlighter;
+public:
+ inline QTextBlock(QTextDocumentPrivate *priv, int b) : p(priv), n(b) {}
+ inline QTextBlock() : p(0), n(0) {}
+ inline QTextBlock(const QTextBlock &o) : p(o.p), n(o.n) {}
+ inline QTextBlock &operator=(const QTextBlock &o) { p = o.p; n = o.n; return *this; }
+
+ inline bool isValid() const { return p != 0 && n != 0; }
+
+ inline bool operator==(const QTextBlock &o) const { return p == o.p && n == o.n; }
+ inline bool operator!=(const QTextBlock &o) const { return p != o.p || n != o.n; }
+ inline bool operator<(const QTextBlock &o) const { return position() < o.position(); }
+
+ int position() const;
+ int length() const;
+ bool contains(int position) const;
+
+ QTextLayout *layout() const;
+ void clearLayout();
+ QTextBlockFormat blockFormat() const;
+ int blockFormatIndex() const;
+ QTextCharFormat charFormat() const;
+ int charFormatIndex() const;
+
+ QString text() const;
+
+ const QTextDocument *document() const;
+
+ QTextList *textList() const;
+
+ QTextBlockUserData *userData() const;
+ void setUserData(QTextBlockUserData *data);
+
+ int userState() const;
+ void setUserState(int state);
+
+ int revision() const;
+ void setRevision(int rev);
+
+ bool isVisible() const;
+ void setVisible(bool visible);
+
+ int blockNumber() const;
+ int firstLineNumber() const;
+
+ void setLineCount(int count);
+ int lineCount() const;
+
+ class Q_GUI_EXPORT iterator {
+ const QTextDocumentPrivate *p;
+ int b;
+ int e;
+ int n;
+ friend class QTextBlock;
+ iterator(const QTextDocumentPrivate *priv, int begin, int end, int f) : p(priv), b(begin), e(end), n(f) {}
+ public:
+ iterator() : p(0), b(0), e(0), n(0) {}
+ iterator(const iterator &o) : p(o.p), b(o.b), e(o.e), n(o.n) {}
+
+ QTextFragment fragment() const;
+
+ bool atEnd() const { return n == e; }
+
+ inline bool operator==(const iterator &o) const { return p == o.p && n == o.n; }
+ inline bool operator!=(const iterator &o) const { return p != o.p || n != o.n; }
+ iterator &operator++();
+ inline iterator operator++(int) { iterator tmp = *this; operator++(); return tmp; }
+ iterator &operator--();
+ inline iterator operator--(int) { iterator tmp = *this; operator--(); return tmp; }
+ };
+
+ // more Qt
+ typedef iterator Iterator;
+
+ iterator begin() const;
+ iterator end() const;
+
+ QTextBlock next() const;
+ QTextBlock previous() const;
+
+ inline QTextDocumentPrivate *docHandle() const { return p; }
+ inline int fragmentIndex() const { return n; }
+
+private:
+ QTextDocumentPrivate *p;
+ int n;
+ friend class QTextDocumentPrivate;
+ friend class QTextLayout;
+};
+
+Q_DECLARE_TYPEINFO(QTextBlock, Q_MOVABLE_TYPE);
+Q_DECLARE_TYPEINFO(QTextBlock::iterator, Q_MOVABLE_TYPE);
+
+
+class Q_GUI_EXPORT QTextFragment
+{
+public:
+ inline QTextFragment(const QTextDocumentPrivate *priv, int f, int fe) : p(priv), n(f), ne(fe) {}
+ inline QTextFragment() : p(0), n(0), ne(0) {}
+ inline QTextFragment(const QTextFragment &o) : p(o.p), n(o.n), ne(o.ne) {}
+ inline QTextFragment &operator=(const QTextFragment &o) { p = o.p; n = o.n; ne = o.ne; return *this; }
+
+ inline bool isValid() const { return p && n; }
+
+ inline bool operator==(const QTextFragment &o) const { return p == o.p && n == o.n; }
+ inline bool operator!=(const QTextFragment &o) const { return p != o.p || n != o.n; }
+ inline bool operator<(const QTextFragment &o) const { return position() < o.position(); }
+
+ int position() const;
+ int length() const;
+ bool contains(int position) const;
+
+ QTextCharFormat charFormat() const;
+ int charFormatIndex() const;
+ QString text() const;
+
+private:
+ const QTextDocumentPrivate *p;
+ int n;
+ int ne;
+};
+
+Q_DECLARE_TYPEINFO(QTextFragment, Q_MOVABLE_TYPE);
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QTEXTOBJECT_H
diff --git a/src/gui/text/qtextobject_p.h b/src/gui/text/qtextobject_p.h
new file mode 100644
index 0000000000..b00a16a485
--- /dev/null
+++ b/src/gui/text/qtextobject_p.h
@@ -0,0 +1,103 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QTEXTOBJECT_P_H
+#define QTEXTOBJECT_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 "QtGui/qtextobject.h"
+#include "private/qobject_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class QTextDocumentPrivate;
+
+class QTextObjectPrivate : public QObjectPrivate
+{
+ Q_DECLARE_PUBLIC(QTextObject)
+public:
+ QTextDocumentPrivate *pieceTable;
+ int objectIndex;
+};
+
+class QTextBlockGroupPrivate : public QTextObjectPrivate
+{
+ Q_DECLARE_PUBLIC(QTextBlockGroup)
+public:
+
+ typedef QList<QTextBlock> BlockList;
+ BlockList blocks;
+ void markBlocksDirty();
+};
+
+class QTextFrameLayoutData;
+
+class QTextFramePrivate : public QTextObjectPrivate
+{
+ friend class QTextDocumentPrivate;
+ Q_DECLARE_PUBLIC(QTextFrame)
+public:
+
+ virtual void fragmentAdded(const QChar &type, uint fragment);
+ virtual void fragmentRemoved(const QChar &type, uint fragment);
+ void remove_me();
+
+ uint fragment_start;
+ uint fragment_end;
+
+ QTextFrame *parentFrame;
+ QList<QTextFrame *> childFrames;
+ QTextFrameLayoutData *layoutData;
+};
+
+QT_END_NAMESPACE
+
+#endif // QTEXTOBJECT_P_H
diff --git a/src/gui/text/qtextodfwriter.cpp b/src/gui/text/qtextodfwriter.cpp
new file mode 100644
index 0000000000..1edc3b873d
--- /dev/null
+++ b/src/gui/text/qtextodfwriter.cpp
@@ -0,0 +1,818 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <qglobal.h>
+
+#ifndef QT_NO_TEXTODFWRITER
+
+#include "qtextodfwriter_p.h"
+
+#include <QImageWriter>
+#include <QTextListFormat>
+#include <QTextList>
+#include <QBuffer>
+#include <QUrl>
+
+#include "qtextdocument_p.h"
+#include "qtexttable.h"
+#include "qtextcursor.h"
+#include "qtextimagehandler_p.h"
+#include "qzipwriter_p.h"
+
+#include <QDebug>
+
+QT_BEGIN_NAMESPACE
+
+/// Convert pixels to postscript point units
+static QString pixelToPoint(qreal pixels)
+{
+ // we hardcode 96 DPI, we do the same in the ODF importer to have a perfect roundtrip.
+ return QString::number(pixels * 72 / 96) + QString::fromLatin1("pt");
+}
+
+// strategies
+class QOutputStrategy {
+public:
+ QOutputStrategy() : contentStream(0), counter(1) { }
+ virtual ~QOutputStrategy() {}
+ virtual void addFile(const QString &fileName, const QString &mimeType, const QByteArray &bytes) = 0;
+
+ QString createUniqueImageName()
+ {
+ return QString::fromLatin1("Pictures/Picture%1").arg(counter++);
+ }
+
+ QIODevice *contentStream;
+ int counter;
+};
+
+class QXmlStreamStrategy : public QOutputStrategy {
+public:
+ QXmlStreamStrategy(QIODevice *device)
+ {
+ contentStream = device;
+ }
+
+ virtual ~QXmlStreamStrategy()
+ {
+ if (contentStream)
+ contentStream->close();
+ }
+ virtual void addFile(const QString &, const QString &, const QByteArray &)
+ {
+ // we ignore this...
+ }
+};
+
+class QZipStreamStrategy : public QOutputStrategy {
+public:
+ QZipStreamStrategy(QIODevice *device)
+ : zip(device),
+ manifestWriter(&manifest)
+ {
+ QByteArray mime("application/vnd.oasis.opendocument.text");
+ zip.setCompressionPolicy(QZipWriter::NeverCompress);
+ zip.addFile(QString::fromLatin1("mimetype"), mime); // for mime-magick
+ zip.setCompressionPolicy(QZipWriter::AutoCompress);
+ contentStream = &content;
+ content.open(QIODevice::WriteOnly);
+ manifest.open(QIODevice::WriteOnly);
+
+ manifestNS = QString::fromLatin1("urn:oasis:names:tc:opendocument:xmlns:manifest:1.0");
+ // prettyfy
+ manifestWriter.setAutoFormatting(true);
+ manifestWriter.setAutoFormattingIndent(1);
+
+ manifestWriter.writeNamespace(manifestNS, QString::fromLatin1("manifest"));
+ manifestWriter.writeStartDocument();
+ manifestWriter.writeStartElement(manifestNS, QString::fromLatin1("manifest"));
+ addFile(QString::fromLatin1("/"), QString::fromLatin1("application/vnd.oasis.opendocument.text"));
+ addFile(QString::fromLatin1("content.xml"), QString::fromLatin1("text/xml"));
+ }
+
+ ~QZipStreamStrategy()
+ {
+ manifestWriter.writeEndDocument();
+ manifest.close();
+ zip.addFile(QString::fromLatin1("META-INF/manifest.xml"), &manifest);
+ content.close();
+ zip.addFile(QString::fromLatin1("content.xml"), &content);
+ zip.close();
+ }
+
+ virtual void addFile(const QString &fileName, const QString &mimeType, const QByteArray &bytes)
+ {
+ zip.addFile(fileName, bytes);
+ addFile(fileName, mimeType);
+ }
+
+private:
+ void addFile(const QString &fileName, const QString &mimeType)
+ {
+ manifestWriter.writeEmptyElement(manifestNS, QString::fromLatin1("file-entry"));
+ manifestWriter.writeAttribute(manifestNS, QString::fromLatin1("media-type"), mimeType);
+ manifestWriter.writeAttribute(manifestNS, QString::fromLatin1("full-path"), fileName);
+ }
+
+ QBuffer content;
+ QBuffer manifest;
+ QZipWriter zip;
+ QXmlStreamWriter manifestWriter;
+ QString manifestNS;
+};
+
+static QString bulletChar(QTextListFormat::Style style)
+{
+ switch(style) {
+ case QTextListFormat::ListDisc:
+ return QChar(0x25cf); // bullet character
+ case QTextListFormat::ListCircle:
+ return QChar(0x25cb); // white circle
+ case QTextListFormat::ListSquare:
+ return QChar(0x25a1); // white square
+ case QTextListFormat::ListDecimal:
+ return QString::fromLatin1("1");
+ case QTextListFormat::ListLowerAlpha:
+ return QString::fromLatin1("a");
+ case QTextListFormat::ListUpperAlpha:
+ return QString::fromLatin1("A");
+ default:
+ case QTextListFormat::ListStyleUndefined:
+ return QString();
+ }
+}
+
+void QTextOdfWriter::writeFrame(QXmlStreamWriter &writer, const QTextFrame *frame)
+{
+ Q_ASSERT(frame);
+ const QTextTable *table = qobject_cast<const QTextTable*> (frame);
+
+ if (table) { // Start a table.
+ writer.writeStartElement(tableNS, QString::fromLatin1("table"));
+ writer.writeEmptyElement(tableNS, QString::fromLatin1("table-column"));
+ writer.writeAttribute(tableNS, QString::fromLatin1("number-columns-repeated"), QString::number(table->columns()));
+ } else if (frame->document() && frame->document()->rootFrame() != frame) { // start a section
+ writer.writeStartElement(textNS, QString::fromLatin1("section"));
+ }
+
+ QTextFrame::iterator iterator = frame->begin();
+ QTextFrame *child = 0;
+
+ int tableRow = -1;
+ while (! iterator.atEnd()) {
+ if (iterator.currentFrame() && child != iterator.currentFrame())
+ writeFrame(writer, iterator.currentFrame());
+ else { // no frame, its a block
+ QTextBlock block = iterator.currentBlock();
+ if (table) {
+ QTextTableCell cell = table->cellAt(block.position());
+ if (tableRow < cell.row()) {
+ if (tableRow >= 0)
+ writer.writeEndElement(); // close table row
+ tableRow = cell.row();
+ writer.writeStartElement(tableNS, QString::fromLatin1("table-row"));
+ }
+ writer.writeStartElement(tableNS, QString::fromLatin1("table-cell"));
+ if (cell.columnSpan() > 1)
+ writer.writeAttribute(tableNS, QString::fromLatin1("number-columns-spanned"), QString::number(cell.columnSpan()));
+ if (cell.rowSpan() > 1)
+ writer.writeAttribute(tableNS, QString::fromLatin1("number-rows-spanned"), QString::number(cell.rowSpan()));
+ if (cell.format().isTableCellFormat()) {
+ writer.writeAttribute(tableNS, QString::fromLatin1("style-name"), QString::fromLatin1("T%1").arg(cell.tableCellFormatIndex()));
+ }
+ }
+ writeBlock(writer, block);
+ if (table)
+ writer.writeEndElement(); // table-cell
+ }
+ child = iterator.currentFrame();
+ ++iterator;
+ }
+ if (tableRow >= 0)
+ writer.writeEndElement(); // close table-row
+
+ if (table || (frame->document() && frame->document()->rootFrame() != frame))
+ writer.writeEndElement(); // close table or section element
+}
+
+void QTextOdfWriter::writeBlock(QXmlStreamWriter &writer, const QTextBlock &block)
+{
+ if (block.textList()) { // its a list-item
+ const int listLevel = block.textList()->format().indent();
+ if (m_listStack.isEmpty() || m_listStack.top() != block.textList()) {
+ // not the same list we were in.
+ while (m_listStack.count() >= listLevel && !m_listStack.isEmpty() && m_listStack.top() != block.textList() ) { // we need to close tags
+ m_listStack.pop();
+ writer.writeEndElement(); // list
+ if (m_listStack.count())
+ writer.writeEndElement(); // list-item
+ }
+ while (m_listStack.count() < listLevel) {
+ if (m_listStack.count())
+ writer.writeStartElement(textNS, QString::fromLatin1("list-item"));
+ writer.writeStartElement(textNS, QString::fromLatin1("list"));
+ if (m_listStack.count() == listLevel - 1) {
+ m_listStack.push(block.textList());
+ writer.writeAttribute(textNS, QString::fromLatin1("style-name"), QString::fromLatin1("L%1")
+ .arg(block.textList()->formatIndex()));
+ }
+ else {
+ m_listStack.push(0);
+ }
+ }
+ }
+ writer.writeStartElement(textNS, QString::fromLatin1("list-item"));
+ }
+ else {
+ while (! m_listStack.isEmpty()) {
+ m_listStack.pop();
+ writer.writeEndElement(); // list
+ if (m_listStack.count())
+ writer.writeEndElement(); // list-item
+ }
+ }
+
+ if (block.length() == 1) { // only a linefeed
+ writer.writeEmptyElement(textNS, QString::fromLatin1("p"));
+ writer.writeAttribute(textNS, QString::fromLatin1("style-name"), QString::fromLatin1("p%1")
+ .arg(block.blockFormatIndex()));
+ if (block.textList())
+ writer.writeEndElement(); // numbered-paragraph
+ return;
+ }
+ writer.writeStartElement(textNS, QString::fromLatin1("p"));
+ writer.writeAttribute(textNS, QString::fromLatin1("style-name"), QString::fromLatin1("p%1")
+ .arg(block.blockFormatIndex()));
+ for (QTextBlock::Iterator frag= block.begin(); !frag.atEnd(); frag++) {
+ writer.writeCharacters(QString()); // Trick to make sure that the span gets no linefeed in front of it.
+ writer.writeStartElement(textNS, QString::fromLatin1("span"));
+
+ QString fragmentText = frag.fragment().text();
+ if (fragmentText.length() == 1 && fragmentText[0] == 0xFFFC) { // its an inline character.
+ writeInlineCharacter(writer, frag.fragment());
+ writer.writeEndElement(); // span
+ continue;
+ }
+
+ writer.writeAttribute(textNS, QString::fromLatin1("style-name"), QString::fromLatin1("c%1")
+ .arg(frag.fragment().charFormatIndex()));
+ bool escapeNextSpace = true;
+ int precedingSpaces = 0, precedingTabs = 0;
+ int exportedIndex = 0;
+ for (int i=0; i <= fragmentText.count(); ++i) {
+ bool isTab = false, isSpace = false;
+ if (i < fragmentText.count()) {
+ QChar character = fragmentText[i];
+ isTab = character.unicode() == '\t';
+ isSpace = character.unicode() == ' ';
+ if (character.unicode() == 0x2028) { // soft-return
+ writer.writeCharacters(fragmentText.mid(exportedIndex, i));
+ writer.writeEmptyElement(textNS, QString::fromLatin1("line-break"));
+ exportedIndex = i+1;
+ continue;
+ }
+ if (isSpace) {
+ ++precedingSpaces;
+ escapeNextSpace = true;
+ }
+ else if (isTab) {
+ precedingTabs++;
+ }
+ }
+ // find more than one space. -> <text:s text:c="2" />
+ if (!isSpace && escapeNextSpace && precedingSpaces > 1) {
+ const bool startParag = exportedIndex == 0 && i == precedingSpaces;
+ if (!startParag)
+ writer.writeCharacters(fragmentText.mid(exportedIndex, i - precedingSpaces + 1 - exportedIndex));
+ writer.writeEmptyElement(textNS, QString::fromLatin1("s"));
+ const int count = precedingSpaces - (startParag?0:1);
+ if (count > 1)
+ writer.writeAttribute(textNS, QString::fromLatin1("c"), QString::number(count));
+ precedingSpaces = 0;
+ exportedIndex = i;
+ }
+ // find tabs. -> <text:tab text:tab-ref="3" /> or <text:tab/>
+ if (!isTab && precedingTabs) {
+ writer.writeCharacters(fragmentText.mid(exportedIndex, i - precedingTabs - exportedIndex));
+ writer.writeEmptyElement(textNS, QString::fromLatin1("tab"));
+ if (precedingTabs > 1)
+ writer.writeAttribute(textNS, QString::fromLatin1("tab-ref"), QString::number(precedingTabs));
+ precedingTabs = 0;
+ exportedIndex = i;
+ }
+ if (!isSpace && !isTab)
+ precedingSpaces = 0;
+ }
+
+ writer.writeCharacters(fragmentText.mid(exportedIndex));
+ writer.writeEndElement(); // span
+ }
+ writer.writeCharacters(QString()); // Trick to make sure that the span gets no linefeed behind it.
+ writer.writeEndElement(); // p
+ if (block.textList())
+ writer.writeEndElement(); // list-item
+}
+
+void QTextOdfWriter::writeInlineCharacter(QXmlStreamWriter &writer, const QTextFragment &fragment) const
+{
+ writer.writeStartElement(drawNS, QString::fromLatin1("frame"));
+ if (m_strategy == 0) {
+ // don't do anything.
+ }
+ else if (fragment.charFormat().isImageFormat()) {
+ QTextImageFormat imageFormat = fragment.charFormat().toImageFormat();
+ writer.writeAttribute(drawNS, QString::fromLatin1("name"), imageFormat.name());
+
+ // vvv Copy pasted mostly from Qt =================
+ QImage image;
+ QString name = imageFormat.name();
+ if (name.startsWith(QLatin1String(":/"))) // auto-detect resources
+ name.prepend(QLatin1String("qrc"));
+ QUrl url = QUrl::fromEncoded(name.toUtf8());
+ const QVariant data = m_document->resource(QTextDocument::ImageResource, url);
+ if (data.type() == QVariant::Image) {
+ image = qvariant_cast<QImage>(data);
+ } else if (data.type() == QVariant::ByteArray) {
+ image.loadFromData(data.toByteArray());
+ }
+
+ if (image.isNull()) {
+ QString context;
+ if (QTextImageHandler::externalLoader)
+ image = QTextImageHandler::externalLoader(name, context);
+
+ if (image.isNull()) { // try direct loading
+ name = imageFormat.name(); // remove qrc:/ prefix again
+ image.load(name);
+ }
+ }
+
+ // ^^^ Copy pasted mostly from Qt =================
+ if (! image.isNull()) {
+ QBuffer imageBytes;
+ QImageWriter imageWriter(&imageBytes, "png");
+ imageWriter.write(image);
+ QString filename = m_strategy->createUniqueImageName();
+ m_strategy->addFile(filename, QString::fromLatin1("image/png"), imageBytes.data());
+
+ // get the width/height from the format.
+ qreal width = (imageFormat.hasProperty(QTextFormat::ImageWidth)) ? imageFormat.width() : image.width();
+ writer.writeAttribute(svgNS, QString::fromLatin1("width"), pixelToPoint(width));
+ qreal height = (imageFormat.hasProperty(QTextFormat::ImageHeight)) ? imageFormat.height() : image.height();
+ writer.writeAttribute(svgNS, QString::fromLatin1("height"), pixelToPoint(height));
+
+ writer.writeStartElement(drawNS, QString::fromLatin1("image"));
+ writer.writeAttribute(xlinkNS, QString::fromLatin1("href"), filename);
+ writer.writeEndElement(); // image
+ }
+ }
+
+ writer.writeEndElement(); // frame
+}
+
+void QTextOdfWriter::writeFormats(QXmlStreamWriter &writer, QSet<int> formats) const
+{
+ writer.writeStartElement(officeNS, QString::fromLatin1("automatic-styles"));
+ QVector<QTextFormat> allStyles = m_document->allFormats();
+ QSetIterator<int> formatId(formats);
+ while(formatId.hasNext()) {
+ int formatIndex = formatId.next();
+ QTextFormat textFormat = allStyles.at(formatIndex);
+ switch (textFormat.type()) {
+ case QTextFormat::CharFormat:
+ if (textFormat.isTableCellFormat())
+ writeTableCellFormat(writer, textFormat.toTableCellFormat(), formatIndex);
+ else
+ writeCharacterFormat(writer, textFormat.toCharFormat(), formatIndex);
+ break;
+ case QTextFormat::BlockFormat:
+ writeBlockFormat(writer, textFormat.toBlockFormat(), formatIndex);
+ break;
+ case QTextFormat::ListFormat:
+ writeListFormat(writer, textFormat.toListFormat(), formatIndex);
+ break;
+ case QTextFormat::FrameFormat:
+ writeFrameFormat(writer, textFormat.toFrameFormat(), formatIndex);
+ break;
+ case QTextFormat::TableFormat:
+ ;break;
+ }
+ }
+
+ writer.writeEndElement(); // automatic-styles
+}
+
+void QTextOdfWriter::writeBlockFormat(QXmlStreamWriter &writer, QTextBlockFormat format, int formatIndex) const
+{
+ writer.writeStartElement(styleNS, QString::fromLatin1("style"));
+ writer.writeAttribute(styleNS, QString::fromLatin1("name"), QString::fromLatin1("p%1").arg(formatIndex));
+ writer.writeAttribute(styleNS, QString::fromLatin1("family"), QString::fromLatin1("paragraph"));
+ writer.writeStartElement(styleNS, QString::fromLatin1("paragraph-properties"));
+
+ if (format.hasProperty(QTextFormat::BlockAlignment)) {
+ QString value;
+ if (format.alignment() == Qt::AlignLeading)
+ value = QString::fromLatin1("start");
+ else if (format.alignment() == Qt::AlignTrailing)
+ value = QString::fromLatin1("end");
+ else if (format.alignment() == (Qt::AlignLeft | Qt::AlignAbsolute))
+ value = QString::fromLatin1("left");
+ else if (format.alignment() == (Qt::AlignRight | Qt::AlignAbsolute))
+ value = QString::fromLatin1("right");
+ else if (format.alignment() == Qt::AlignHCenter)
+ value = QString::fromLatin1("center");
+ else if (format.alignment() == Qt::AlignJustify)
+ value = QString::fromLatin1("justify");
+ else
+ qWarning() << "QTextOdfWriter: unsupported paragraph alignment; " << format.alignment();
+ if (! value.isNull())
+ writer.writeAttribute(foNS, QString::fromLatin1("text-align"), value);
+ }
+
+ if (format.hasProperty(QTextFormat::BlockTopMargin))
+ writer.writeAttribute(foNS, QString::fromLatin1("margin-top"), pixelToPoint(qMax(qreal(0.), format.topMargin())) );
+ if (format.hasProperty(QTextFormat::BlockBottomMargin))
+ writer.writeAttribute(foNS, QString::fromLatin1("margin-bottom"), pixelToPoint(qMax(qreal(0.), format.bottomMargin())) );
+ if (format.hasProperty(QTextFormat::BlockLeftMargin) || format.hasProperty(QTextFormat::BlockIndent))
+ writer.writeAttribute(foNS, QString::fromLatin1("margin-left"), pixelToPoint(qMax(qreal(0.),
+ format.leftMargin() + format.indent())));
+ if (format.hasProperty(QTextFormat::BlockRightMargin))
+ writer.writeAttribute(foNS, QString::fromLatin1("margin-right"), pixelToPoint(qMax(qreal(0.), format.rightMargin())) );
+ if (format.hasProperty(QTextFormat::TextIndent))
+ writer.writeAttribute(foNS, QString::fromLatin1("text-indent"), QString::number(format.textIndent()));
+ if (format.hasProperty(QTextFormat::PageBreakPolicy)) {
+ if (format.pageBreakPolicy() & QTextFormat::PageBreak_AlwaysBefore)
+ writer.writeAttribute(foNS, QString::fromLatin1("break-before"), QString::fromLatin1("page"));
+ if (format.pageBreakPolicy() & QTextFormat::PageBreak_AlwaysAfter)
+ writer.writeAttribute(foNS, QString::fromLatin1("break-after"), QString::fromLatin1("page"));
+ }
+ if (format.hasProperty(QTextFormat::BlockNonBreakableLines))
+ writer.writeAttribute(foNS, QString::fromLatin1("keep-together"),
+ format.nonBreakableLines() ? QString::fromLatin1("true") : QString::fromLatin1("false"));
+ if (format.hasProperty(QTextFormat::TabPositions)) {
+ QList<QTextOption::Tab> tabs = format.tabPositions();
+ writer.writeStartElement(styleNS, QString::fromLatin1("style-tab-stops"));
+ QList<QTextOption::Tab>::Iterator iterator = tabs.begin();
+ while(iterator != tabs.end()) {
+ writer.writeEmptyElement(styleNS, QString::fromLatin1("style-tab-stop"));
+ writer.writeAttribute(styleNS, QString::fromLatin1("position"), pixelToPoint(iterator->position) );
+ QString type;
+ switch(iterator->type) {
+ case QTextOption::DelimiterTab: type = QString::fromLatin1("char"); break;
+ case QTextOption::LeftTab: type = QString::fromLatin1("left"); break;
+ case QTextOption::RightTab: type = QString::fromLatin1("right"); break;
+ case QTextOption::CenterTab: type = QString::fromLatin1("center"); break;
+ }
+ writer.writeAttribute(styleNS, QString::fromLatin1("type"), type);
+ if (iterator->delimiter != 0)
+ writer.writeAttribute(styleNS, QString::fromLatin1("char"), iterator->delimiter);
+ ++iterator;
+ }
+
+ writer.writeEndElement(); // style-tab-stops
+ }
+
+ writer.writeEndElement(); // paragraph-properties
+ writer.writeEndElement(); // style
+}
+
+void QTextOdfWriter::writeCharacterFormat(QXmlStreamWriter &writer, QTextCharFormat format, int formatIndex) const
+{
+ writer.writeStartElement(styleNS, QString::fromLatin1("style"));
+ writer.writeAttribute(styleNS, QString::fromLatin1("name"), QString::fromLatin1("c%1").arg(formatIndex));
+ writer.writeAttribute(styleNS, QString::fromLatin1("family"), QString::fromLatin1("text"));
+ writer.writeEmptyElement(styleNS, QString::fromLatin1("text-properties"));
+ if (format.fontItalic())
+ writer.writeAttribute(foNS, QString::fromLatin1("font-style"), QString::fromLatin1("italic"));
+ if (format.hasProperty(QTextFormat::FontWeight) && format.fontWeight() != QFont::Normal) {
+ QString value;
+ if (format.fontWeight() == QFont::Bold)
+ value = QString::fromLatin1("bold");
+ else
+ value = QString::number(format.fontWeight() * 10);
+ writer.writeAttribute(foNS, QString::fromLatin1("font-weight"), value);
+ }
+ if (format.hasProperty(QTextFormat::FontFamily))
+ writer.writeAttribute(foNS, QString::fromLatin1("font-family"), format.fontFamily());
+ else
+ writer.writeAttribute(foNS, QString::fromLatin1("font-family"), QString::fromLatin1("Sans")); // Qt default
+ if (format.hasProperty(QTextFormat::FontPointSize))
+ writer.writeAttribute(foNS, QString::fromLatin1("font-size"), QString::fromLatin1("%1pt").arg(format.fontPointSize()));
+ if (format.hasProperty(QTextFormat::FontCapitalization)) {
+ switch(format.fontCapitalization()) {
+ case QFont::MixedCase:
+ writer.writeAttribute(foNS, QString::fromLatin1("text-transform"), QString::fromLatin1("none")); break;
+ case QFont::AllUppercase:
+ writer.writeAttribute(foNS, QString::fromLatin1("text-transform"), QString::fromLatin1("uppercase")); break;
+ case QFont::AllLowercase:
+ writer.writeAttribute(foNS, QString::fromLatin1("text-transform"), QString::fromLatin1("lowercase")); break;
+ case QFont::Capitalize:
+ writer.writeAttribute(foNS, QString::fromLatin1("text-transform"), QString::fromLatin1("capitalize")); break;
+ case QFont::SmallCaps:
+ writer.writeAttribute(foNS, QString::fromLatin1("font-variant"), QString::fromLatin1("small-caps")); break;
+ }
+ }
+ if (format.hasProperty(QTextFormat::FontLetterSpacing))
+ writer.writeAttribute(foNS, QString::fromLatin1("letter-spacing"), pixelToPoint(format.fontLetterSpacing()) );
+ if (format.hasProperty(QTextFormat::FontWordSpacing))
+ writer.writeAttribute(foNS, QString::fromLatin1("letter-spacing"), pixelToPoint(format.fontWordSpacing()) );
+ if (format.hasProperty(QTextFormat::FontUnderline))
+ writer.writeAttribute(styleNS, QString::fromLatin1("text-underline-type"),
+ format.fontUnderline() ? QString::fromLatin1("single") : QString::fromLatin1("none"));
+ if (format.hasProperty(QTextFormat::FontOverline)) {
+ // bool fontOverline () const TODO
+ }
+ if (format.hasProperty(QTextFormat::FontStrikeOut))
+ writer.writeAttribute(styleNS,QString::fromLatin1( "text-line-through-type"),
+ format.fontStrikeOut() ? QString::fromLatin1("single") : QString::fromLatin1("none"));
+ if (format.hasProperty(QTextFormat::TextUnderlineColor))
+ writer.writeAttribute(styleNS, QString::fromLatin1("text-underline-color"), format.underlineColor().name());
+ if (format.hasProperty(QTextFormat::FontFixedPitch)) {
+ // bool fontFixedPitch () const TODO
+ }
+ if (format.hasProperty(QTextFormat::TextUnderlineStyle)) {
+ QString value;
+ switch (format.underlineStyle()) {
+ case QTextCharFormat::NoUnderline: value = QString::fromLatin1("none"); break;
+ case QTextCharFormat::SingleUnderline: value = QString::fromLatin1("solid"); break;
+ case QTextCharFormat::DashUnderline: value = QString::fromLatin1("dash"); break;
+ case QTextCharFormat::DotLine: value = QString::fromLatin1("dotted"); break;
+ case QTextCharFormat::DashDotLine: value = QString::fromLatin1("dash-dot"); break;
+ case QTextCharFormat::DashDotDotLine: value = QString::fromLatin1("dot-dot-dash"); break;
+ case QTextCharFormat::WaveUnderline: value = QString::fromLatin1("wave"); break;
+ case QTextCharFormat::SpellCheckUnderline: value = QString::fromLatin1("none"); break;
+ }
+ writer.writeAttribute(styleNS, QString::fromLatin1("text-underline-style"), value);
+ }
+ if (format.hasProperty(QTextFormat::TextVerticalAlignment)) {
+ QString value;
+ switch (format.verticalAlignment()) {
+ case QTextCharFormat::AlignMiddle:
+ case QTextCharFormat::AlignNormal: value = QString::fromLatin1("0%"); break;
+ case QTextCharFormat::AlignSuperScript: value = QString::fromLatin1("super"); break;
+ case QTextCharFormat::AlignSubScript: value = QString::fromLatin1("sub"); break;
+ case QTextCharFormat::AlignTop: value = QString::fromLatin1("100%"); break;
+ case QTextCharFormat::AlignBottom : value = QString::fromLatin1("-100%"); break;
+ }
+ writer.writeAttribute(styleNS, QString::fromLatin1("text-position"), value);
+ }
+ if (format.hasProperty(QTextFormat::TextOutline))
+ writer.writeAttribute(styleNS, QString::fromLatin1("text-outline"), QString::fromLatin1("true"));
+ if (format.hasProperty(QTextFormat::TextToolTip)) {
+ // QString toolTip () const TODO
+ }
+ if (format.hasProperty(QTextFormat::IsAnchor)) {
+ // bool isAnchor () const TODO
+ }
+ if (format.hasProperty(QTextFormat::AnchorHref)) {
+ // QString anchorHref () const TODO
+ }
+ if (format.hasProperty(QTextFormat::AnchorName)) {
+ // QString anchorName () const TODO
+ }
+ if (format.hasProperty(QTextFormat::ForegroundBrush)) {
+ QBrush brush = format.foreground();
+ // TODO
+ writer.writeAttribute(foNS, QString::fromLatin1("color"), brush.color().name());
+ }
+
+ writer.writeEndElement(); // style
+}
+
+void QTextOdfWriter::writeListFormat(QXmlStreamWriter &writer, QTextListFormat format, int formatIndex) const
+{
+ writer.writeStartElement(textNS, QString::fromLatin1("list-style"));
+ writer.writeAttribute(styleNS, QString::fromLatin1("name"), QString::fromLatin1("L%1").arg(formatIndex));
+
+ QTextListFormat::Style style = format.style();
+ if (style == QTextListFormat::ListDecimal || style == QTextListFormat::ListLowerAlpha
+ || style == QTextListFormat::ListUpperAlpha) {
+ writer.writeStartElement(textNS, QString::fromLatin1("list-level-style-number"));
+ writer.writeAttribute(styleNS, QString::fromLatin1("num-format"), bulletChar(style));
+ writer.writeAttribute(styleNS, QString::fromLatin1("num-suffix"), QString::fromLatin1("."));
+ } else {
+ writer.writeStartElement(textNS, QString::fromLatin1("list-level-style-bullet"));
+ writer.writeAttribute(textNS, QString::fromLatin1("bullet-char"), bulletChar(style));
+ }
+
+ writer.writeAttribute(textNS, QString::fromLatin1("level"), QString::number(format.indent()));
+ writer.writeEmptyElement(styleNS, QString::fromLatin1("list-level-properties"));
+ writer.writeAttribute(foNS, QString::fromLatin1("text-align"), QString::fromLatin1("start"));
+ QString spacing = QString::fromLatin1("%1mm").arg(format.indent() * 8);
+ writer.writeAttribute(textNS, QString::fromLatin1("space-before"), spacing);
+ //writer.writeAttribute(textNS, QString::fromLatin1("min-label-width"), spacing);
+
+ writer.writeEndElement(); // list-level-style-*
+ writer.writeEndElement(); // list-style
+}
+
+void QTextOdfWriter::writeFrameFormat(QXmlStreamWriter &writer, QTextFrameFormat format, int formatIndex) const
+{
+ writer.writeStartElement(styleNS, QString::fromLatin1("style"));
+ writer.writeAttribute(styleNS, QString::fromLatin1("name"), QString::fromLatin1("s%1").arg(formatIndex));
+ writer.writeAttribute(styleNS, QString::fromLatin1("family"), QString::fromLatin1("section"));
+ writer.writeEmptyElement(styleNS, QString::fromLatin1("section-properties"));
+ if (format.hasProperty(QTextFormat::BlockTopMargin))
+ writer.writeAttribute(foNS, QString::fromLatin1("margin-top"), pixelToPoint(qMax(qreal(0.), format.topMargin())) );
+ if (format.hasProperty(QTextFormat::BlockBottomMargin))
+ writer.writeAttribute(foNS, QString::fromLatin1("margin-bottom"), pixelToPoint(qMax(qreal(0.), format.bottomMargin())) );
+ if (format.hasProperty(QTextFormat::BlockLeftMargin))
+ writer.writeAttribute(foNS, QString::fromLatin1("margin-left"), pixelToPoint(qMax(qreal(0.), format.leftMargin())) );
+ if (format.hasProperty(QTextFormat::BlockRightMargin))
+ writer.writeAttribute(foNS, QString::fromLatin1("margin-right"), pixelToPoint(qMax(qreal(0.), format.rightMargin())) );
+
+ writer.writeEndElement(); // style
+
+// TODO consider putting the following properties in a qt-namespace.
+// Position position () const
+// qreal border () const
+// QBrush borderBrush () const
+// BorderStyle borderStyle () const
+// qreal padding () const
+// QTextLength width () const
+// QTextLength height () const
+// PageBreakFlags pageBreakPolicy () const
+}
+
+void QTextOdfWriter::writeTableCellFormat(QXmlStreamWriter &writer, QTextTableCellFormat format, int formatIndex) const
+{
+ writer.writeStartElement(styleNS, QString::fromLatin1("style"));
+ writer.writeAttribute(styleNS, QString::fromLatin1("name"), QString::fromLatin1("T%1").arg(formatIndex));
+ writer.writeAttribute(styleNS, QString::fromLatin1("family"), QString::fromLatin1("table"));
+ writer.writeEmptyElement(styleNS, QString::fromLatin1("table-properties"));
+
+
+ qreal padding = format.topPadding();
+ if (padding > 0 && padding == format.bottomPadding()
+ && padding == format.leftPadding() && padding == format.rightPadding()) {
+ writer.writeAttribute(foNS, QString::fromLatin1("padding"), pixelToPoint(padding));
+ }
+ else {
+ if (padding > 0)
+ writer.writeAttribute(foNS, QString::fromLatin1("padding-top"), pixelToPoint(padding));
+ if (format.bottomPadding() > 0)
+ writer.writeAttribute(foNS, QString::fromLatin1("padding-top"), pixelToPoint(format.bottomPadding()));
+ if (format.leftPadding() > 0)
+ writer.writeAttribute(foNS, QString::fromLatin1("padding-top"), pixelToPoint(format.leftPadding()));
+ if (format.rightPadding() > 0)
+ writer.writeAttribute(foNS, QString::fromLatin1("padding-top"), pixelToPoint(format.rightPadding()));
+ }
+
+ if (format.hasProperty(QTextFormat::TextVerticalAlignment)) {
+ QString pos;
+ switch (format.verticalAlignment()) {
+ case QTextCharFormat::AlignMiddle:
+ pos = QString::fromLatin1("middle"); break;
+ case QTextCharFormat::AlignTop:
+ pos = QString::fromLatin1("top"); break;
+ case QTextCharFormat::AlignBottom:
+ pos = QString::fromLatin1("bottom"); break;
+ default:
+ pos = QString::fromLatin1("automatic"); break;
+ }
+ writer.writeAttribute(styleNS, QString::fromLatin1("vertical-align"), pos);
+ }
+
+ // TODO
+ // ODF just search for style-table-cell-properties-attlist)
+ // QTextFormat::BackgroundImageUrl
+ // format.background
+ // QTextFormat::FrameBorder
+
+ writer.writeEndElement(); // style
+}
+
+///////////////////////
+
+QTextOdfWriter::QTextOdfWriter(const QTextDocument &document, QIODevice *device)
+ : officeNS (QLatin1String("urn:oasis:names:tc:opendocument:xmlns:office:1.0")),
+ textNS (QLatin1String("urn:oasis:names:tc:opendocument:xmlns:text:1.0")),
+ styleNS (QLatin1String("urn:oasis:names:tc:opendocument:xmlns:style:1.0")),
+ foNS (QLatin1String("urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0")),
+ tableNS (QLatin1String("urn:oasis:names:tc:opendocument:xmlns:table:1.0")),
+ drawNS (QLatin1String("urn:oasis:names:tc:opendocument:xmlns:drawing:1.0")),
+ xlinkNS (QLatin1String("http://www.w3.org/1999/xlink")),
+ svgNS (QLatin1String("urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0")),
+ m_document(&document),
+ m_device(device),
+ m_strategy(0),
+ m_codec(0),
+ m_createArchive(true)
+{
+}
+
+bool QTextOdfWriter::writeAll()
+{
+ if (m_createArchive)
+ m_strategy = new QZipStreamStrategy(m_device);
+ else
+ m_strategy = new QXmlStreamStrategy(m_device);
+
+ if (!m_device->isWritable() && ! m_device->open(QIODevice::WriteOnly)) {
+ qWarning() << "QTextOdfWriter::writeAll: the device can not be opened for writing";
+ return false;
+ }
+ QXmlStreamWriter writer(m_strategy->contentStream);
+#ifndef QT_NO_TEXTCODEC
+ if (m_codec)
+ writer.setCodec(m_codec);
+#endif
+ // prettyfy
+ writer.setAutoFormatting(true);
+ writer.setAutoFormattingIndent(2);
+
+ writer.writeNamespace(officeNS, QString::fromLatin1("office"));
+ writer.writeNamespace(textNS, QString::fromLatin1("text"));
+ writer.writeNamespace(styleNS, QString::fromLatin1("style"));
+ writer.writeNamespace(foNS, QString::fromLatin1("fo"));
+ writer.writeNamespace(tableNS, QString::fromLatin1("table"));
+ writer.writeNamespace(drawNS, QString::fromLatin1("draw"));
+ writer.writeNamespace(xlinkNS, QString::fromLatin1("xlink"));
+ writer.writeNamespace(svgNS, QString::fromLatin1("svg"));
+ writer.writeStartDocument();
+ writer.writeStartElement(officeNS, QString::fromLatin1("document-content"));
+
+ // add fragments. (for character formats)
+ QTextDocumentPrivate::FragmentIterator fragIt = m_document->docHandle()->begin();
+ QSet<int> formats;
+ while (fragIt != m_document->docHandle()->end()) {
+ const QTextFragmentData * const frag = fragIt.value();
+ formats << frag->format;
+ ++fragIt;
+ }
+
+ // add blocks (for blockFormats)
+ QTextDocumentPrivate::BlockMap &blocks = m_document->docHandle()->blockMap();
+ QTextDocumentPrivate::BlockMap::Iterator blockIt = blocks.begin();
+ while (blockIt != blocks.end()) {
+ const QTextBlockData * const block = blockIt.value();
+ formats << block->format;
+ ++blockIt;
+ }
+
+ // add objects for lists, frames and tables
+ QVector<QTextFormat> allFormats = m_document->allFormats();
+ QList<int> copy = formats.toList();
+ for (QList<int>::Iterator iter = copy.begin(); iter != copy.end(); ++iter) {
+ QTextObject *object = m_document->objectForFormat(allFormats[*iter]);
+ if (object)
+ formats << object->formatIndex();
+ }
+
+ writeFormats(writer, formats);
+
+ writer.writeStartElement(officeNS, QString::fromLatin1("body"));
+ writer.writeStartElement(officeNS, QString::fromLatin1("text"));
+ QTextFrame *rootFrame = m_document->rootFrame();
+ writeFrame(writer, rootFrame);
+ writer.writeEndElement(); // text
+ writer.writeEndElement(); // body
+ writer.writeEndElement(); // document-content
+ writer.writeEndDocument();
+ delete m_strategy;
+ m_strategy = 0;
+
+ return true;
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_TEXTODFWRITER
diff --git a/src/gui/text/qtextodfwriter_p.h b/src/gui/text/qtextodfwriter_p.h
new file mode 100644
index 0000000000..88e6b460d4
--- /dev/null
+++ b/src/gui/text/qtextodfwriter_p.h
@@ -0,0 +1,115 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QTEXTODFWRITER_H
+#define QTEXTODFWRITER_H
+#ifndef QT_NO_TEXTODFWRITER
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of the QLibrary class. This header file may change from
+// version to version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtCore/QXmlStreamWriter>
+#include <QtCore/qset.h>
+#include <QtCore/qstack.h>
+
+#include "qtextdocument_p.h"
+#include "qtextdocumentwriter.h"
+
+QT_BEGIN_NAMESPACE
+
+class QTextDocumentPrivate;
+class QTextCursor;
+class QTextBlock;
+class QIODevice;
+class QXmlStreamWriter;
+class QTextOdfWriterPrivate;
+class QTextBlockFormat;
+class QTextCharFormat;
+class QTextListFormat;
+class QTextFrameFormat;
+class QTextTableCellFormat;
+class QTextFrame;
+class QTextFragment;
+class QOutputStrategy;
+
+class Q_AUTOTEST_EXPORT QTextOdfWriter {
+public:
+ QTextOdfWriter(const QTextDocument &document, QIODevice *device);
+ bool writeAll();
+
+ void setCodec(QTextCodec *codec) { m_codec = codec; }
+ void setCreateArchive(bool on) { m_createArchive = on; }
+ bool createArchive() const { return m_createArchive; }
+
+ void writeBlock(QXmlStreamWriter &writer, const QTextBlock &block);
+ void writeFormats(QXmlStreamWriter &writer, QSet<int> formatIds) const;
+ void writeBlockFormat(QXmlStreamWriter &writer, QTextBlockFormat format, int formatIndex) const;
+ void writeCharacterFormat(QXmlStreamWriter &writer, QTextCharFormat format, int formatIndex) const;
+ void writeListFormat(QXmlStreamWriter &writer, QTextListFormat format, int formatIndex) const;
+ void writeFrameFormat(QXmlStreamWriter &writer, QTextFrameFormat format, int formatIndex) const;
+ void writeTableCellFormat(QXmlStreamWriter &writer, QTextTableCellFormat format, int formatIndex) const;
+ void writeFrame(QXmlStreamWriter &writer, const QTextFrame *frame);
+ void writeInlineCharacter(QXmlStreamWriter &writer, const QTextFragment &fragment) const;
+
+ const QString officeNS, textNS, styleNS, foNS, tableNS, drawNS, xlinkNS, svgNS;
+private:
+ const QTextDocument *m_document;
+ QIODevice *m_device;
+
+ QOutputStrategy *m_strategy;
+ QTextCodec *m_codec;
+ bool m_createArchive;
+
+ QStack<QTextList *> m_listStack;
+};
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_TEXTODFWRITER
+#endif // QTEXTODFWRITER_H
diff --git a/src/gui/text/qtextoption.cpp b/src/gui/text/qtextoption.cpp
new file mode 100644
index 0000000000..e1b98445a3
--- /dev/null
+++ b/src/gui/text/qtextoption.cpp
@@ -0,0 +1,414 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qtextoption.h"
+#include "qapplication.h"
+#include "qlist.h"
+
+QT_BEGIN_NAMESPACE
+
+struct QTextOptionPrivate
+{
+ QList<QTextOption::Tab> tabStops;
+};
+
+/*!
+ Constructs a text option with default properties for text.
+*/
+QTextOption::QTextOption()
+ : align(Qt::AlignLeft),
+ wordWrap(QTextOption::WordWrap),
+ design(false),
+ unused(0),
+ f(0),
+ tab(-1),
+ d(0)
+{
+ direction = QApplication::layoutDirection();
+}
+
+/*!
+ Constructs a text option with the given \a alignment for text.
+*/
+QTextOption::QTextOption(Qt::Alignment alignment)
+ : align(alignment),
+ wordWrap(QTextOption::WordWrap),
+ design(false),
+ unused(0),
+ f(0),
+ tab(-1),
+ d(0)
+{
+ direction = QApplication::layoutDirection();
+}
+
+/*!
+ Destroys the text option.
+*/
+QTextOption::~QTextOption()
+{
+ delete d;
+}
+
+/*!
+ \fn QTextOption::QTextOption(const QTextOption &other)
+
+ Construct a copy of the \a other text option.
+*/
+QTextOption::QTextOption(const QTextOption &o)
+ : align(o.align),
+ wordWrap(o.wordWrap),
+ design(o.design),
+ direction(o.direction),
+ unused(o.unused),
+ f(o.f),
+ tab(o.tab),
+ d(0)
+{
+ if (o.d)
+ d = new QTextOptionPrivate(*o.d);
+}
+
+/*!
+ \fn QTextOption &QTextOption::operator=(const QTextOption &other)
+
+ Returns true if the text option is the same as the \a other text option;
+ otherwise returns false.
+*/
+QTextOption &QTextOption::operator=(const QTextOption &o)
+{
+ if (this == &o)
+ return *this;
+ delete d; d = 0;
+ align = o.align;
+ wordWrap = o.wordWrap;
+ design = o.design;
+ direction = o.direction;
+ unused = o.unused;
+ f = o.f;
+ tab = o.tab;
+ if (o.d)
+ d = new QTextOptionPrivate(*o.d);
+ return *this;
+}
+
+/*!
+ Sets the tab positions for the text layout to those specified by
+ \a tabStops.
+
+ \sa tabArray(), setTabStop(), setTabs()
+*/
+void QTextOption::setTabArray(QList<qreal> tabStops)
+{
+ if (!d)
+ d = new QTextOptionPrivate;
+ QList<QTextOption::Tab> tabs;
+ QTextOption::Tab tab;
+ foreach (qreal pos, tabStops) {
+ tab.position = pos;
+ tabs.append(tab);
+ }
+ d->tabStops = tabs;
+}
+
+/*!
+ \since 4.4
+ Sets the tab positions for the text layout to those specified by
+ \a tabStops.
+
+ \sa tabStops()
+*/
+void QTextOption::setTabs(QList<QTextOption::Tab> tabStops)
+{
+ if (!d)
+ d = new QTextOptionPrivate;
+ d->tabStops = tabStops;
+}
+
+/*!
+ Returns a list of tab positions defined for the text layout.
+
+ \sa setTabArray(), tabStop()
+*/
+QList<qreal> QTextOption::tabArray() const
+{
+ if (!d)
+ return QList<qreal>();
+
+ QList<qreal> answer;
+ QList<QTextOption::Tab>::ConstIterator iter = d->tabStops.constBegin();
+ while(iter != d->tabStops.constEnd()) {
+ answer.append( (*iter).position);
+ ++iter;
+ }
+ return answer;
+}
+
+
+QList<QTextOption::Tab> QTextOption::tabs() const
+{
+ if (!d)
+ return QList<QTextOption::Tab>();
+ return d->tabStops;
+}
+
+/*!
+ \class QTextOption
+ \reentrant
+
+ \brief The QTextOption class provides a description of general rich text
+ properties.
+
+ \ingroup text
+
+ QTextOption is used to encapsulate common rich text properties in a single
+ object. It contains information about text alignment, layout direction,
+ word wrapping, and other standard properties associated with text rendering
+ and layout.
+
+ \sa QTextEdit, QTextDocument, QTextCursor
+*/
+
+/*!
+ \enum QTextOption::WrapMode
+
+ This enum describes how text is wrapped in a document.
+
+ \value NoWrap Text is not wrapped at all.
+ \value WordWrap Text is wrapped at word boundaries.
+ \value ManualWrap Same as QTextOption::NoWrap
+ \value WrapAnywhere Text can be wrapped at any point on a line, even if
+ it occurs in the middle of a word.
+ \value WrapAtWordBoundaryOrAnywhere If possible, wrapping occurs at a word
+ boundary; otherwise it will occur at the appropriate
+ point on the line, even in the middle of a word.
+*/
+
+/*!
+ \fn void QTextOption::setUseDesignMetrics(bool enable)
+
+ If \a enable is true then the layout will use design metrics;
+ otherwise it will use the metrics of the paint device (which is
+ the default behavior).
+
+ \sa useDesignMetrics()
+*/
+
+/*!
+ \fn bool QTextOption::useDesignMetrics() const
+
+ Returns true if the layout uses design rather than device metrics;
+ otherwise returns false.
+
+ \sa setUseDesignMetrics()
+*/
+
+/*!
+ \fn Qt::Alignment QTextOption::alignment() const
+
+ Returns the text alignment defined by the option.
+
+ \sa setAlignment()
+*/
+
+/*!
+ \fn void QTextOption::setAlignment(Qt::Alignment alignment);
+
+ Sets the option's text alignment to the specified \a alignment.
+
+ \sa alignment()
+*/
+
+/*!
+ \fn Qt::LayoutDirection QTextOption::textDirection() const
+
+ Returns the direction of the text layout defined by the option.
+
+ \sa setTextDirection()
+*/
+
+/*!
+ \fn void QTextOption::setTextDirection(Qt::LayoutDirection direction)
+
+ Sets the direction of the text layout defined by the option to the
+ given \a direction.
+
+ \sa textDirection()
+*/
+
+/*!
+ \fn WrapMode QTextOption::wrapMode() const
+
+ Returns the text wrap mode defined by the option.
+
+ \sa setWrapMode()
+*/
+
+/*!
+ \fn void QTextOption::setWrapMode(WrapMode mode)
+
+ Sets the option's text wrap mode to the given \a mode.
+*/
+
+/*!
+ \enum QTextOption::Flag
+
+ \value IncludeTrailingSpaces When this option is set, QTextLine::naturalTextWidth() and naturalTextRect() will
+ return a value that includes the width of trailing spaces in the text; otherwise
+ this width is excluded.
+ \value ShowTabsAndSpaces Visualize spaces with little dots, and tabs with little arrows.
+ \value ShowLineAndParagraphSeparators Visualize line and paragraph separators with appropriate symbol characters.
+ \value AddSpaceForLineAndParagraphSeparators While determining the line-break positions take into account the
+ space added for drawing a separator character.
+ \value SuppressColors Suppress all color changes in the character formats (except the main selection).
+*/
+
+/*!
+ \fn Flags QTextOption::flags() const
+
+ Returns the flags associated with the option.
+
+ \sa setFlags()
+*/
+
+/*!
+ \fn void QTextOption::setFlags(Flags flags)
+
+ Sets the flags associated with the option to the given \a flags.
+
+ \sa flags()
+*/
+
+/*!
+ \fn qreal QTextOption::tabStop() const
+
+ Returns the distance in device units between tab stops.
+ Convenient function for the above method
+
+ \sa setTabStop(), tabArray(), setTabs(), tabs()
+*/
+
+/*!
+ \fn void QTextOption::setTabStop(qreal tabStop)
+
+ Sets the default distance in device units between tab stops to the value specified
+ by \a tabStop.
+
+ \sa tabStop(), setTabArray(), setTabs(), tabs()
+*/
+
+/*!
+ \enum QTextOption::TabType
+ \since 4.4
+
+ This enum holds the different types of tabulator
+
+ \value LeftTab, A left-tab
+ \value RightTab, A right-tab
+ \value CenterTab, A centered-tab
+ \value DelimiterTab A tab stopping at a certain delimiter-character
+*/
+
+/*!
+ \class QTextOption::Tab
+ \since 4.4
+ Each tab definition is represented by this struct.
+*/
+
+/*!
+ \variable Tab::position
+ Distance from the start of the paragraph.
+ The position of a tab is from the start of the paragraph which implies that when
+ the alignment of the paragraph is set to centered, the tab is interpreted to be
+ moved the same distance as the left ege of the paragraph does.
+ In case the paragraph is set to have a layoutDirection() RightToLeft the position
+ is interpreted to be from the right side of the paragraph with higher numbers moving
+ the tab to the left.
+*/
+
+/*!
+ \variable Tab::type
+ Determine which type is used.
+ In a paragraph that has layoutDirection() RightToLeft the type LeftTab will
+ be interpreted to be a RightTab and vice versa.
+*/
+
+/*!
+ \variable Tab::delimiter
+ If type is DelimitorTab; tab until this char is found in the text.
+*/
+
+/*!
+ \fn Tab::Tab()
+ Creates a default left tab with position 80.
+*/
+
+/*!
+ \fn bool Tab::operator==(const Tab &other) const
+
+ Returns true if tab \a other is equal to this tab;
+ otherwise returns false.
+*/
+
+/*!
+ \fn bool Tab::operator!=(const Tab &other) const
+
+ Returns true if tab \a other is not equal to this tab;
+ otherwise returns false.
+*/
+
+/*!
+ \fn void setTabs(QList<Tab> tabStops)
+ Set the Tab properties to \a tabStops.
+
+ \sa tabStop(), tabs()
+*/
+
+/*!
+ \since 4.4
+ \fn QList<QTextOption::Tab> QTextOption::tabs() const
+ Returns a list of tab positions defined for the text layout.
+
+ \sa tabStop(), setTabs(), setTabStop()
+*/
+
+
+QT_END_NAMESPACE
diff --git a/src/gui/text/qtextoption.h b/src/gui/text/qtextoption.h
new file mode 100644
index 0000000000..1c637a30d9
--- /dev/null
+++ b/src/gui/text/qtextoption.h
@@ -0,0 +1,161 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QTEXTOPTION_H
+#define QTEXTOPTION_H
+
+#include <QtCore/qnamespace.h>
+#include <QtCore/qchar.h>
+#include <QtCore/qmetatype.h>
+
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+template <typename T> class QList;
+struct QTextOptionPrivate;
+
+class Q_GUI_EXPORT QTextOption
+{
+public:
+ enum TabType {
+ LeftTab,
+ RightTab,
+ CenterTab,
+ DelimiterTab
+ };
+
+ struct Q_GUI_EXPORT Tab {
+ inline Tab() : position(80), type(QTextOption::LeftTab) { }
+
+ inline bool operator==(const Tab &other) const {
+ return type == other.type
+ && qFuzzyCompare(position, other.position)
+ && delimiter == other.delimiter;
+ }
+
+ inline bool operator!=(const Tab &other) const {
+ return !operator==(other);
+ }
+
+ qreal position;
+ TabType type;
+ QChar delimiter;
+ };
+
+ QTextOption();
+ QTextOption(Qt::Alignment alignment);
+ ~QTextOption();
+
+ QTextOption(const QTextOption &o);
+ QTextOption &operator=(const QTextOption &o);
+
+ inline void setAlignment(Qt::Alignment alignment);
+ inline Qt::Alignment alignment() const { return Qt::Alignment(align); }
+
+ inline void setTextDirection(Qt::LayoutDirection aDirection) { this->direction = aDirection; }
+ inline Qt::LayoutDirection textDirection() const { return Qt::LayoutDirection(direction); }
+
+ enum WrapMode {
+ NoWrap,
+ WordWrap,
+ ManualWrap,
+ WrapAnywhere,
+ WrapAtWordBoundaryOrAnywhere
+ };
+ inline void setWrapMode(WrapMode wrap) { wordWrap = wrap; }
+ inline WrapMode wrapMode() const { return static_cast<WrapMode>(wordWrap); }
+
+ enum Flag {
+ ShowTabsAndSpaces = 0x1,
+ ShowLineAndParagraphSeparators = 0x2,
+ AddSpaceForLineAndParagraphSeparators = 0x4,
+ SuppressColors = 0x8,
+ IncludeTrailingSpaces = 0x80000000
+ };
+ Q_DECLARE_FLAGS(Flags, Flag)
+ inline void setFlags(Flags flags);
+ inline Flags flags() const { return Flags(f); }
+
+ inline void setTabStop(qreal tabStop);
+ inline qreal tabStop() const { return tab; }
+
+ void setTabArray(QList<qreal> tabStops);
+ QList<qreal> tabArray() const;
+
+ void setTabs(QList<Tab> tabStops);
+ QList<Tab> tabs() const;
+
+ void setUseDesignMetrics(bool b) { design = b; }
+ bool useDesignMetrics() const { return design; }
+
+private:
+ uint align : 8;
+ uint wordWrap : 4;
+ uint design : 1;
+ uint direction : 1;
+ uint unused : 19;
+ uint f;
+ qreal tab;
+ QTextOptionPrivate *d;
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(QTextOption::Flags)
+
+inline void QTextOption::setAlignment(Qt::Alignment aalignment)
+{ align = aalignment; }
+
+inline void QTextOption::setFlags(Flags aflags)
+{ f = aflags; }
+
+inline void QTextOption::setTabStop(qreal atabStop)
+{ tab = atabStop; }
+
+QT_END_NAMESPACE
+
+Q_DECLARE_METATYPE( QTextOption::Tab )
+
+QT_END_HEADER
+
+#endif // QTEXTOPTION_H
diff --git a/src/gui/text/qtexttable.cpp b/src/gui/text/qtexttable.cpp
new file mode 100644
index 0000000000..375bb0974f
--- /dev/null
+++ b/src/gui/text/qtexttable.cpp
@@ -0,0 +1,1290 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qtexttable.h"
+#include "qtextcursor.h"
+#include "qtextformat.h"
+#include <qdebug.h>
+#include "qtexttable_p.h"
+#include "qvarlengtharray.h"
+#include "private/qfunctions_p.h"
+
+#include <stdlib.h>
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QTextTableCell
+ \reentrant
+
+ \brief The QTextTableCell class represents the properties of a
+ cell in a QTextTable.
+
+ \ingroup text
+
+ Table cells are pieces of document structure that belong to a table.
+ The table orders cells into particular rows and columns; cells can
+ also span multiple columns and rows.
+
+ Cells are usually created when a table is inserted into a document with
+ QTextCursor::insertTable(), but they are also created and destroyed when
+ a table is resized.
+
+ Cells contain information about their location in a table; you can
+ obtain the row() and column() numbers of a cell, and its rowSpan()
+ and columnSpan().
+
+ The format() of a cell describes the default character format of its
+ contents. The firstCursorPosition() and lastCursorPosition() functions
+ are used to obtain the extent of the cell in the document.
+
+ \sa QTextTable QTextTableFormat
+*/
+
+/*!
+ \fn QTextTableCell::QTextTableCell()
+
+ Constructs an invalid table cell.
+
+ \sa isValid()
+*/
+
+/*!
+ \fn QTextTableCell::QTextTableCell(const QTextTableCell &other)
+
+ Copy constructor. Creates a new QTextTableCell object based on the
+ \a other cell.
+*/
+
+/*!
+ \fn QTextTableCell& QTextTableCell::operator=(const QTextTableCell &other)
+
+ Assigns the \a other table cell to this table cell.
+*/
+
+/*!
+ \since 4.2
+
+ Sets the cell's character format to \a format. This can for example be used to change
+ the background color of the entire cell:
+
+ QTextTableCell cell = table->cellAt(2, 3);
+ QTextCharFormat format = cell.format();
+ format.setBackground(Qt::blue);
+ cell.setFormat(format);
+
+ Note that the cell's row or column span cannot be changed through this function. You have
+ to use QTextTable::mergeCells and QTextTable::splitCell instead.
+
+ \sa format()
+*/
+void QTextTableCell::setFormat(const QTextCharFormat &format)
+{
+ QTextCharFormat fmt = format;
+ fmt.clearProperty(QTextFormat::ObjectIndex);
+ fmt.setObjectType(QTextFormat::TableCellObject);
+ QTextDocumentPrivate *p = table->docHandle();
+ QTextDocumentPrivate::FragmentIterator frag(&p->fragmentMap(), fragment);
+
+ QTextFormatCollection *c = p->formatCollection();
+ QTextCharFormat oldFormat = c->charFormat(frag->format);
+ fmt.setTableCellRowSpan(oldFormat.tableCellRowSpan());
+ fmt.setTableCellColumnSpan(oldFormat.tableCellColumnSpan());
+
+ p->setCharFormat(frag.position(), 1, fmt, QTextDocumentPrivate::SetFormatAndPreserveObjectIndices);
+}
+
+/*!
+ Returns the cell's character format.
+*/
+QTextCharFormat QTextTableCell::format() const
+{
+ QTextDocumentPrivate *p = table->docHandle();
+ QTextFormatCollection *c = p->formatCollection();
+
+ QTextCharFormat fmt = c->charFormat(tableCellFormatIndex());
+ fmt.setObjectType(QTextFormat::TableCellObject);
+ return fmt;
+}
+
+/*!
+ \since 4.5
+
+ Returns the index of the tableCell's format in the document's internal list of formats.
+
+ \sa QTextDocument::allFormats()
+*/
+int QTextTableCell::tableCellFormatIndex() const
+{
+ QTextDocumentPrivate *p = table->docHandle();
+ return QTextDocumentPrivate::FragmentIterator(&p->fragmentMap(), fragment)->format;
+}
+
+/*!
+ Returns the number of the row in the table that contains this cell.
+
+ \sa column()
+*/
+int QTextTableCell::row() const
+{
+ const QTextTablePrivate *tp = table->d_func();
+ if (tp->dirty)
+ tp->update();
+
+ int idx = tp->findCellIndex(fragment);
+ if (idx == -1)
+ return idx;
+ return tp->cellIndices.at(idx) / tp->nCols;
+}
+
+/*!
+ Returns the number of the column in the table that contains this cell.
+
+ \sa row()
+*/
+int QTextTableCell::column() const
+{
+ const QTextTablePrivate *tp = table->d_func();
+ if (tp->dirty)
+ tp->update();
+
+ int idx = tp->findCellIndex(fragment);
+ if (idx == -1)
+ return idx;
+ return tp->cellIndices.at(idx) % tp->nCols;
+}
+
+/*!
+ Returns the number of rows this cell spans. The default is 1.
+
+ \sa columnSpan()
+*/
+int QTextTableCell::rowSpan() const
+{
+ return format().tableCellRowSpan();
+}
+
+/*!
+ Returns the number of columns this cell spans. The default is 1.
+
+ \sa rowSpan()
+*/
+int QTextTableCell::columnSpan() const
+{
+ return format().tableCellColumnSpan();
+}
+
+/*!
+ \fn bool QTextTableCell::isValid() const
+
+ Returns true if this is a valid table cell; otherwise returns
+ false.
+*/
+
+
+/*!
+ Returns the first valid cursor position in this cell.
+
+ \sa lastCursorPosition()
+*/
+QTextCursor QTextTableCell::firstCursorPosition() const
+{
+ return QTextCursor(table->d_func()->pieceTable, firstPosition());
+}
+
+/*!
+ Returns the last valid cursor position in this cell.
+
+ \sa firstCursorPosition()
+*/
+QTextCursor QTextTableCell::lastCursorPosition() const
+{
+ return QTextCursor(table->d_func()->pieceTable, lastPosition());
+}
+
+
+/*!
+ \internal
+
+ Returns the first valid position in the document occupied by this cell.
+*/
+int QTextTableCell::firstPosition() const
+{
+ QTextDocumentPrivate *p = table->docHandle();
+ return p->fragmentMap().position(fragment) + 1;
+}
+
+/*!
+ \internal
+
+ Returns the last valid position in the document occupied by this cell.
+*/
+int QTextTableCell::lastPosition() const
+{
+ QTextDocumentPrivate *p = table->docHandle();
+ const QTextTablePrivate *td = table->d_func();
+ int index = table->d_func()->findCellIndex(fragment);
+ int f;
+ if (index != -1)
+ f = td->cells.value(index + 1, td->fragment_end);
+ else
+ f = td->fragment_end;
+ return p->fragmentMap().position(f);
+}
+
+
+/*!
+ Returns a frame iterator pointing to the beginning of the table's cell.
+
+ \sa end()
+*/
+QTextFrame::iterator QTextTableCell::begin() const
+{
+ QTextDocumentPrivate *p = table->docHandle();
+ int b = p->blockMap().findNode(firstPosition());
+ int e = p->blockMap().findNode(lastPosition()+1);
+ return QTextFrame::iterator(const_cast<QTextTable *>(table), b, b, e);
+}
+
+/*!
+ Returns a frame iterator pointing to the end of the table's cell.
+
+ \sa begin()
+*/
+QTextFrame::iterator QTextTableCell::end() const
+{
+ QTextDocumentPrivate *p = table->docHandle();
+ int b = p->blockMap().findNode(firstPosition());
+ int e = p->blockMap().findNode(lastPosition()+1);
+ return QTextFrame::iterator(const_cast<QTextTable *>(table), e, b, e);
+}
+
+
+/*!
+ \fn QTextCursor QTextTableCell::operator==(const QTextTableCell &other) const
+
+ Returns true if this cell object and the \a other cell object
+ describe the same cell; otherwise returns false.
+*/
+
+/*!
+ \fn QTextCursor QTextTableCell::operator!=(const QTextTableCell &other) const
+
+ Returns true if this cell object and the \a other cell object
+ describe different cells; otherwise returns false.
+*/
+
+/*!
+ \fn QTextTableCell::~QTextTableCell()
+
+ Destroys the table cell.
+*/
+
+QTextTablePrivate::~QTextTablePrivate()
+{
+ if (grid)
+ free(grid);
+}
+
+
+QTextTable *QTextTablePrivate::createTable(QTextDocumentPrivate *pieceTable, int pos, int rows, int cols, const QTextTableFormat &tableFormat)
+{
+ QTextTableFormat fmt = tableFormat;
+ fmt.setColumns(cols);
+ QTextTable *table = qobject_cast<QTextTable *>(pieceTable->createObject(fmt));
+ Q_ASSERT(table);
+
+ pieceTable->beginEditBlock();
+
+// qDebug("---> createTable: rows=%d, cols=%d at %d", rows, cols, pos);
+ // add block after table
+ QTextCharFormat charFmt;
+ charFmt.setObjectIndex(table->objectIndex());
+ charFmt.setObjectType(QTextFormat::TableCellObject);
+
+
+ int charIdx = pieceTable->formatCollection()->indexForFormat(charFmt);
+ int cellIdx = pieceTable->formatCollection()->indexForFormat(QTextBlockFormat());
+
+ QTextTablePrivate *d = table->d_func();
+ d->blockFragmentUpdates = true;
+
+ d->fragment_start = pieceTable->insertBlock(QTextBeginningOfFrame, pos, cellIdx, charIdx);
+ d->cells.append(d->fragment_start);
+ ++pos;
+
+ for (int i = 1; i < rows*cols; ++i) {
+ d->cells.append(pieceTable->insertBlock(QTextBeginningOfFrame, pos, cellIdx, charIdx));
+// qDebug(" addCell at %d", pos);
+ ++pos;
+ }
+
+ d->fragment_end = pieceTable->insertBlock(QTextEndOfFrame, pos, cellIdx, charIdx);
+// qDebug(" addEOR at %d", pos);
+ ++pos;
+
+ d->blockFragmentUpdates = false;
+ d->dirty = true;
+
+ pieceTable->endEditBlock();
+
+ return table;
+}
+
+struct QFragmentFindHelper
+{
+ inline QFragmentFindHelper(int _pos, const QTextDocumentPrivate::FragmentMap &map)
+ : pos(_pos), fragmentMap(map) {}
+ uint pos;
+ const QTextDocumentPrivate::FragmentMap &fragmentMap;
+};
+
+Q_STATIC_GLOBAL_INLINE_OPERATOR bool operator<(int fragment, const QFragmentFindHelper &helper)
+{
+ return helper.fragmentMap.position(fragment) < helper.pos;
+}
+
+Q_STATIC_GLOBAL_INLINE_OPERATOR bool operator<(const QFragmentFindHelper &helper, int fragment)
+{
+ return helper.pos < helper.fragmentMap.position(fragment);
+}
+
+int QTextTablePrivate::findCellIndex(int fragment) const
+{
+ QFragmentFindHelper helper(pieceTable->fragmentMap().position(fragment),
+ pieceTable->fragmentMap());
+ QList<int>::ConstIterator it = qBinaryFind(cells.begin(), cells.end(), helper);
+ if (it == cells.end())
+ return -1;
+ return it - cells.begin();
+}
+
+void QTextTablePrivate::fragmentAdded(const QChar &type, uint fragment)
+{
+ dirty = true;
+ if (blockFragmentUpdates)
+ return;
+ if (type == QTextBeginningOfFrame) {
+ Q_ASSERT(cells.indexOf(fragment) == -1);
+ const uint pos = pieceTable->fragmentMap().position(fragment);
+ QFragmentFindHelper helper(pos, pieceTable->fragmentMap());
+ QList<int>::Iterator it = qLowerBound(cells.begin(), cells.end(), helper);
+ cells.insert(it, fragment);
+ if (!fragment_start || pos < pieceTable->fragmentMap().position(fragment_start))
+ fragment_start = fragment;
+ return;
+ }
+ QTextFramePrivate::fragmentAdded(type, fragment);
+}
+
+void QTextTablePrivate::fragmentRemoved(const QChar &type, uint fragment)
+{
+ dirty = true;
+ if (blockFragmentUpdates)
+ return;
+ if (type == QTextBeginningOfFrame) {
+ Q_ASSERT(cells.indexOf(fragment) != -1);
+ cells.removeAll(fragment);
+ if (fragment_start == fragment && cells.size()) {
+ fragment_start = cells.at(0);
+ }
+ if (fragment_start != fragment)
+ return;
+ }
+ QTextFramePrivate::fragmentRemoved(type, fragment);
+}
+
+void QTextTablePrivate::update() const
+{
+ Q_Q(const QTextTable);
+ nCols = q->format().columns();
+ nRows = (cells.size() + nCols-1)/nCols;
+// qDebug(">>>> QTextTablePrivate::update, nRows=%d, nCols=%d", nRows, nCols);
+
+ grid = (int *)realloc(grid, nRows*nCols*sizeof(int));
+ memset(grid, 0, nRows*nCols*sizeof(int));
+
+ QTextDocumentPrivate *p = pieceTable;
+ QTextFormatCollection *c = p->formatCollection();
+
+ cellIndices.resize(cells.size());
+
+ int cell = 0;
+ for (int i = 0; i < cells.size(); ++i) {
+ int fragment = cells.at(i);
+ QTextCharFormat fmt = c->charFormat(QTextDocumentPrivate::FragmentIterator(&p->fragmentMap(), fragment)->format);
+ int rowspan = fmt.tableCellRowSpan();
+ int colspan = fmt.tableCellColumnSpan();
+
+ // skip taken cells
+ while (cell < nRows*nCols && grid[cell])
+ ++cell;
+
+ int r = cell/nCols;
+ int c = cell%nCols;
+ cellIndices[i] = cell;
+
+ if (r + rowspan > nRows) {
+ grid = (int *)realloc(grid, sizeof(int)*(r + rowspan)*nCols);
+ memset(grid + (nRows*nCols), 0, sizeof(int)*(r+rowspan-nRows)*nCols);
+ nRows = r + rowspan;
+ }
+
+ Q_ASSERT(c + colspan <= nCols);
+ for (int ii = 0; ii < rowspan; ++ii) {
+ for (int jj = 0; jj < colspan; ++jj) {
+ Q_ASSERT(grid[(r+ii)*nCols + c+jj] == 0);
+ grid[(r+ii)*nCols + c+jj] = fragment;
+// qDebug(" setting cell %d span=%d/%d at %d/%d", fragment, rowspan, colspan, r+ii, c+jj);
+ }
+ }
+ }
+// qDebug("<<<< end: nRows=%d, nCols=%d", nRows, nCols);
+
+ dirty = false;
+}
+
+
+
+
+
+/*!
+ \class QTextTable
+ \reentrant
+
+ \brief The QTextTable class represents a table in a QTextDocument.
+
+ \ingroup text
+
+ A table is a group of cells ordered into rows and columns. Each table
+ contains at least one row and one column. Each cell contains a block, and
+ is surrounded by a frame.
+
+ Tables are usually created and inserted into a document with the
+ QTextCursor::insertTable() function.
+ For example, we can insert a table with three rows and two columns at the
+ current cursor position in an editor using the following lines of code:
+
+ \snippet doc/src/snippets/textdocument-tables/mainwindow.cpp 1
+ \codeline
+ \snippet doc/src/snippets/textdocument-tables/mainwindow.cpp 3
+
+ The table format is either defined when the table is created or changed
+ later with setFormat().
+
+ The table currently being edited by the cursor is found with
+ QTextCursor::currentTable(). This allows its format or dimensions to be
+ changed after it has been inserted into a document.
+
+ A table's size can be changed with resize(), or by using
+ insertRows(), insertColumns(), removeRows(), or removeColumns().
+ Use cellAt() to retrieve table cells.
+
+ The starting and ending positions of table rows can be found by moving
+ a cursor within a table, and using the rowStart() and rowEnd() functions
+ to obtain cursors at the start and end of each row.
+
+ Rows and columns within a QTextTable can be merged and split using
+ the mergeCells() and splitCell() functions. However, only cells that span multiple
+ rows or columns can be split. (Merging or splitting does not increase or decrease
+ the number of rows and columns.)
+
+ \table 80%
+ \row
+ \o \inlineimage texttable-split.png Original Table
+ \o Suppose we have a 2x3 table of names and addresses. To merge both
+ columns in the first row we invoke mergeCells() with \a row = 0,
+ \a column = 0, \a numRows = 1 and \a numColumns = 2.
+ \snippet doc/src/snippets/textdocument-texttable/main.cpp 0
+
+ \row
+ \o \inlineimage texttable-merge.png
+ \o This gives us the following table. To split the first row of the table
+ back into two cells, we invoke the splitCell() function with \a numRows
+ and \a numCols = 1.
+ \snippet doc/src/snippets/textdocument-texttable/main.cpp 1
+
+ \row
+ \o \inlineimage texttable-split.png Split Table
+ \o This results in the original table.
+ \endtable
+
+ \sa QTextTableFormat
+*/
+
+/*! \internal
+ */
+QTextTable::QTextTable(QTextDocument *doc)
+ : QTextFrame(*new QTextTablePrivate, doc)
+{
+}
+
+/*! \internal
+
+Destroys the table.
+ */
+QTextTable::~QTextTable()
+{
+}
+
+
+/*!
+ \fn QTextTableCell QTextTable::cellAt(int row, int column) const
+
+ Returns the table cell at the given \a row and \a column in the table.
+
+ \sa columns() rows()
+*/
+QTextTableCell QTextTable::cellAt(int row, int col) const
+{
+ Q_D(const QTextTable);
+ if (d->dirty)
+ d->update();
+
+ if (row < 0 || row >= d->nRows || col < 0 || col >= d->nCols)
+ return QTextTableCell();
+
+ return QTextTableCell(this, d->grid[row*d->nCols + col]);
+}
+
+/*!
+ \overload
+
+ Returns the table cell that contains the character at the given \a position
+ in the document.
+*/
+QTextTableCell QTextTable::cellAt(int position) const
+{
+ Q_D(const QTextTable);
+ if (d->dirty)
+ d->update();
+
+ uint pos = (uint)position;
+ const QTextDocumentPrivate::FragmentMap &map = d->pieceTable->fragmentMap();
+ if (position < 0 || map.position(d->fragment_start) >= pos || map.position(d->fragment_end) < pos)
+ return QTextTableCell();
+
+ QFragmentFindHelper helper(position, map);
+ QList<int>::ConstIterator it = qLowerBound(d->cells.begin(), d->cells.end(), helper);
+ if (it != d->cells.begin())
+ --it;
+
+ return QTextTableCell(this, *it);
+}
+
+/*!
+ \fn QTextTableCell QTextTable::cellAt(const QTextCursor &cursor) const
+
+ \overload
+
+ Returns the table cell containing the given \a cursor.
+*/
+QTextTableCell QTextTable::cellAt(const QTextCursor &c) const
+{
+ return cellAt(c.position());
+}
+
+/*!
+ \fn void QTextTable::resize(int rows, int columns)
+
+ Resizes the table to contain the required number of \a rows and \a columns.
+
+ \sa insertRows() insertColumns() removeRows() removeColumns()
+*/
+void QTextTable::resize(int rows, int cols)
+{
+ Q_D(QTextTable);
+ if (d->dirty)
+ d->update();
+
+ int nRows = this->rows();
+ int nCols = this->columns();
+
+ if (rows == nRows && cols == nCols)
+ return;
+
+ d->pieceTable->beginEditBlock();
+
+ if (nCols < cols)
+ insertColumns(nCols, cols - nCols);
+ else if (nCols > cols)
+ removeColumns(cols, nCols - cols);
+
+ if (nRows < rows)
+ insertRows(nRows, rows-nRows);
+ else if (nRows > rows)
+ removeRows(rows, nRows-rows);
+
+ d->pieceTable->endEditBlock();
+}
+
+/*!
+ \fn void QTextTable::insertRows(int index, int rows)
+
+ Inserts a number of \a rows before the row with the specified \a index.
+
+ \sa resize() insertColumns() removeRows() removeColumns() appendRows() appendColumns()
+*/
+void QTextTable::insertRows(int pos, int num)
+{
+ Q_D(QTextTable);
+ if (num <= 0)
+ return;
+
+ if (d->dirty)
+ d->update();
+
+ if (pos > d->nRows || pos < 0)
+ pos = d->nRows;
+
+// qDebug() << "-------- insertRows" << pos << num;
+ QTextDocumentPrivate *p = d->pieceTable;
+ QTextFormatCollection *c = p->formatCollection();
+ p->beginEditBlock();
+
+ int extended = 0;
+ int insert_before = 0;
+ if (pos > 0 && pos < d->nRows) {
+ for (int i = 0; i < d->nCols; ++i) {
+ int cell = d->grid[pos*d->nCols + i];
+ if (cell == d->grid[(pos-1)*d->nCols+i]) {
+ // cell spans the insertion place, extend it
+ QTextDocumentPrivate::FragmentIterator it(&p->fragmentMap(), cell);
+ QTextCharFormat fmt = c->charFormat(it->format);
+ fmt.setTableCellRowSpan(fmt.tableCellRowSpan() + num);
+ p->setCharFormat(it.position(), 1, fmt);
+ extended++;
+ } else if (!insert_before) {
+ insert_before = cell;
+ }
+ }
+ } else {
+ insert_before = (pos == 0 ? d->grid[0] : d->fragment_end);
+ }
+ if (extended < d->nCols) {
+ Q_ASSERT(insert_before);
+ QTextDocumentPrivate::FragmentIterator it(&p->fragmentMap(), insert_before);
+ QTextCharFormat fmt = c->charFormat(it->format);
+ fmt.setTableCellRowSpan(1);
+ fmt.setTableCellColumnSpan(1);
+ Q_ASSERT(fmt.objectIndex() == objectIndex());
+ int pos = it.position();
+ int cfmt = p->formatCollection()->indexForFormat(fmt);
+ int bfmt = p->formatCollection()->indexForFormat(QTextBlockFormat());
+// qDebug("inserting %d cells, nCols=%d extended=%d", num*(d->nCols-extended), d->nCols, extended);
+ for (int i = 0; i < num*(d->nCols-extended); ++i)
+ p->insertBlock(QTextBeginningOfFrame, pos, bfmt, cfmt, QTextUndoCommand::MoveCursor);
+ }
+
+// qDebug() << "-------- end insertRows" << pos << num;
+ p->endEditBlock();
+}
+
+/*!
+ \fn void QTextTable::insertColumns(int index, int columns)
+
+ Inserts a number of \a columns before the column with the specified \a index.
+
+ \sa insertRows() resize() removeRows() removeColumns() appendRows() appendColumns()
+*/
+void QTextTable::insertColumns(int pos, int num)
+{
+ Q_D(QTextTable);
+ if (num <= 0)
+ return;
+
+ if (d->dirty)
+ d->update();
+
+ if (pos > d->nCols || pos < 0)
+ pos = d->nCols;
+
+// qDebug() << "-------- insertCols" << pos << num;
+ QTextDocumentPrivate *p = d->pieceTable;
+ QTextFormatCollection *c = p->formatCollection();
+ p->beginEditBlock();
+
+ for (int i = 0; i < d->nRows; ++i) {
+ int cell;
+ if (i == d->nRows - 1 && pos == d->nCols)
+ cell = d->fragment_end;
+ else
+ cell = d->grid[i*d->nCols + pos];
+ QTextDocumentPrivate::FragmentIterator it(&p->fragmentMap(), cell);
+ QTextCharFormat fmt = c->charFormat(it->format);
+ if (pos > 0 && pos < d->nCols && cell == d->grid[i*d->nCols + pos - 1]) {
+ // cell spans the insertion place, extend it
+ fmt.setTableCellColumnSpan(fmt.tableCellColumnSpan() + num);
+ p->setCharFormat(it.position(), 1, fmt);
+ } else {
+ fmt.setTableCellRowSpan(1);
+ fmt.setTableCellColumnSpan(1);
+ Q_ASSERT(fmt.objectIndex() == objectIndex());
+ int position = it.position();
+ int cfmt = p->formatCollection()->indexForFormat(fmt);
+ int bfmt = p->formatCollection()->indexForFormat(QTextBlockFormat());
+ for (int i = 0; i < num; ++i)
+ p->insertBlock(QTextBeginningOfFrame, position, bfmt, cfmt, QTextUndoCommand::MoveCursor);
+ }
+ }
+
+ QTextTableFormat tfmt = format();
+ tfmt.setColumns(tfmt.columns()+num);
+ QVector<QTextLength> columnWidths = tfmt.columnWidthConstraints();
+ if (! columnWidths.isEmpty()) {
+ for (int i = num; i > 0; --i)
+ columnWidths.insert(pos, columnWidths[qMax(0, pos-1)]);
+ }
+ tfmt.setColumnWidthConstraints (columnWidths);
+ QTextObject::setFormat(tfmt);
+
+// qDebug() << "-------- end insertCols" << pos << num;
+ p->endEditBlock();
+}
+
+/*!
+ \since 4.5
+ Appends \a count rows at the bottom of the table.
+
+ \sa insertColumns() insertRows() resize() removeRows() removeColumns() appendColumns()
+*/
+void QTextTable::appendRows(int count)
+{
+ insertRows(rows(), count);
+}
+
+/*!
+ \since 4.5
+ Appends \a count columns at the right side of the table.
+
+ \sa insertColumns() insertRows() resize() removeRows() removeColumns() appendRows()
+*/
+void QTextTable::appendColumns(int count)
+{
+ insertColumns(columns(), count);
+}
+
+/*!
+ \fn void QTextTable::removeRows(int index, int rows)
+
+ Removes a number of \a rows starting with the row at the specified \a index.
+
+ \sa insertRows(), insertColumns(), resize(), removeColumns() appendRows() appendColumns()
+*/
+void QTextTable::removeRows(int pos, int num)
+{
+ Q_D(QTextTable);
+// qDebug() << "-------- removeRows" << pos << num;
+
+ if (num <= 0 || pos < 0)
+ return;
+ if (d->dirty)
+ d->update();
+ if (pos >= d->nRows)
+ return;
+ if (pos+num > d->nRows)
+ num = d->nRows - pos;
+
+ QTextDocumentPrivate *p = d->pieceTable;
+ QTextFormatCollection *collection = p->formatCollection();
+ p->beginEditBlock();
+
+ // delete whole table?
+ if (pos == 0 && num == d->nRows) {
+ const int pos = p->fragmentMap().position(d->fragment_start);
+ p->remove(pos, p->fragmentMap().position(d->fragment_end) - pos + 1);
+ p->endEditBlock();
+ return;
+ }
+
+ p->aboutToRemoveCell(cellAt(pos, 0).firstPosition(), cellAt(pos + num - 1, d->nCols - 1).lastPosition());
+
+ QList<int> touchedCells;
+ for (int r = pos; r < pos + num; ++r) {
+ for (int c = 0; c < d->nCols; ++c) {
+ int cell = d->grid[r*d->nCols + c];
+ if (touchedCells.contains(cell))
+ continue;
+ touchedCells << cell;
+ QTextDocumentPrivate::FragmentIterator it(&p->fragmentMap(), cell);
+ QTextCharFormat fmt = collection->charFormat(it->format);
+ int span = fmt.tableCellRowSpan();
+ if (span > 1) {
+ fmt.setTableCellRowSpan(span - 1);
+ p->setCharFormat(it.position(), 1, fmt);
+ } else {
+ // remove cell
+ int index = d->cells.indexOf(cell) + 1;
+ int f_end = index < d->cells.size() ? d->cells.at(index) : d->fragment_end;
+ p->remove(it.position(), p->fragmentMap().position(f_end) - it.position());
+ }
+ }
+ }
+
+ p->endEditBlock();
+// qDebug() << "-------- end removeRows" << pos << num;
+}
+
+/*!
+ \fn void QTextTable::removeColumns(int index, int columns)
+
+ Removes a number of \a columns starting with the column at the specified
+ \a index.
+
+ \sa insertRows() insertColumns() removeRows() resize() appendRows() appendColumns()
+*/
+void QTextTable::removeColumns(int pos, int num)
+{
+ Q_D(QTextTable);
+// qDebug() << "-------- removeCols" << pos << num;
+
+ if (num <= 0 || pos < 0)
+ return;
+ if (d->dirty)
+ d->update();
+ if (pos >= d->nCols)
+ return;
+ if (pos + num > d->nCols)
+ pos = d->nCols - num;
+
+ QTextDocumentPrivate *p = d->pieceTable;
+ QTextFormatCollection *collection = p->formatCollection();
+ p->beginEditBlock();
+
+ // delete whole table?
+ if (pos == 0 && num == d->nCols) {
+ const int pos = p->fragmentMap().position(d->fragment_start);
+ p->remove(pos, p->fragmentMap().position(d->fragment_end) - pos + 1);
+ p->endEditBlock();
+ return;
+ }
+
+ p->aboutToRemoveCell(cellAt(0, pos).firstPosition(), cellAt(d->nRows - 1, pos + num - 1).lastPosition());
+
+ QList<int> touchedCells;
+ for (int r = 0; r < d->nRows; ++r) {
+ for (int c = pos; c < pos + num; ++c) {
+ int cell = d->grid[r*d->nCols + c];
+ if (touchedCells.contains(cell))
+ continue;
+ touchedCells << cell;
+ QTextDocumentPrivate::FragmentIterator it(&p->fragmentMap(), cell);
+ QTextCharFormat fmt = collection->charFormat(it->format);
+ int span = fmt.tableCellColumnSpan();
+ if (span > 1) {
+ fmt.setTableCellColumnSpan(span - 1);
+ p->setCharFormat(it.position(), 1, fmt);
+ } else {
+ // remove cell
+ int index = d->cells.indexOf(cell) + 1;
+ int f_end = index < d->cells.size() ? d->cells.at(index) : d->fragment_end;
+ p->remove(it.position(), p->fragmentMap().position(f_end) - it.position());
+ }
+ }
+ }
+
+ QTextTableFormat tfmt = format();
+ tfmt.setColumns(tfmt.columns()-num);
+ QVector<QTextLength> columnWidths = tfmt.columnWidthConstraints();
+ if (columnWidths.count() > pos) {
+ columnWidths.remove(pos, num);
+ tfmt.setColumnWidthConstraints (columnWidths);
+ }
+ QTextObject::setFormat(tfmt);
+
+ p->endEditBlock();
+// qDebug() << "-------- end removeCols" << pos << num;
+}
+
+/*!
+ \since 4.1
+
+ Merges the cell at the specified \a row and \a column with the adjacent cells
+ into one cell. The new cell will span \a numRows rows and \a numCols columns.
+ If \a numRows or \a numCols is less than the current number of rows or columns
+ the cell spans then this method does nothing.
+
+ \sa splitCell()
+*/
+void QTextTable::mergeCells(int row, int column, int numRows, int numCols)
+{
+ Q_D(QTextTable);
+
+ if (d->dirty)
+ d->update();
+
+ QTextDocumentPrivate *p = d->pieceTable;
+ QTextFormatCollection *fc = p->formatCollection();
+
+ const QTextTableCell cell = cellAt(row, column);
+ if (!cell.isValid() || row != cell.row() || column != cell.column())
+ return;
+
+ QTextCharFormat fmt = cell.format();
+ const int rowSpan = fmt.tableCellRowSpan();
+ const int colSpan = fmt.tableCellColumnSpan();
+
+ numRows = qMin(numRows, rows() - cell.row());
+ numCols = qMin(numCols, columns() - cell.column());
+
+ // nothing to merge?
+ if (numRows < rowSpan || numCols < colSpan)
+ return;
+
+ // check the edges of the merge rect to make sure no cell spans the edge
+ for (int r = row; r < row + numRows; ++r) {
+ if (cellAt(r, column) == cellAt(r, column - 1))
+ return;
+ if (cellAt(r, column + numCols) == cellAt(r, column + numCols - 1))
+ return;
+ }
+
+ for (int c = column; c < column + numCols; ++c) {
+ if (cellAt(row, c) == cellAt(row - 1, c))
+ return;
+ if (cellAt(row + numRows, c) == cellAt(row + numRows - 1, c))
+ return;
+ }
+
+ p->beginEditBlock();
+
+ const int origCellPosition = cell.firstPosition() - 1;
+
+ const int cellFragment = d->grid[row * d->nCols + column];
+
+ // find the position at which to insert the contents of the merged cells
+ QFragmentFindHelper helper(origCellPosition, p->fragmentMap());
+ QList<int>::Iterator it = qBinaryFind(d->cells.begin(), d->cells.end(), helper);
+ Q_ASSERT(it != d->cells.end());
+ Q_ASSERT(*it == cellFragment);
+ const int insertCellIndex = it - d->cells.begin();
+ int insertFragment = d->cells.value(insertCellIndex + 1, d->fragment_end);
+ uint insertPos = p->fragmentMap().position(insertFragment);
+
+ d->blockFragmentUpdates = true;
+
+ bool rowHasText = cell.firstCursorPosition().block().length();
+ bool needsParagraph = rowHasText && colSpan == numCols;
+
+ // find all cells that will be erased by the merge
+ for (int r = row; r < row + numRows; ++r) {
+ int firstColumn = r < row + rowSpan ? column + colSpan : column;
+
+ // don't recompute the cell index for the first row
+ int firstCellIndex = r == row ? insertCellIndex + 1 : -1;
+ int cellIndex = firstCellIndex;
+
+ for (int c = firstColumn; c < column + numCols; ++c) {
+ const int fragment = d->grid[r * d->nCols + c];
+
+ // already handled?
+ if (fragment == cellFragment)
+ continue;
+
+ QTextDocumentPrivate::FragmentIterator it(&p->fragmentMap(), fragment);
+ uint pos = it.position();
+
+ if (firstCellIndex == -1) {
+ QFragmentFindHelper helper(pos, p->fragmentMap());
+ QList<int>::Iterator it = qBinaryFind(d->cells.begin(), d->cells.end(), helper);
+ Q_ASSERT(it != d->cells.end());
+ Q_ASSERT(*it == fragment);
+ firstCellIndex = cellIndex = it - d->cells.begin();
+ }
+
+ ++cellIndex;
+
+ QTextCharFormat fmt = fc->charFormat(it->format);
+
+ const int cellRowSpan = fmt.tableCellRowSpan();
+ const int cellColSpan = fmt.tableCellColumnSpan();
+
+ // update the grid for this cell
+ for (int i = r; i < r + cellRowSpan; ++i)
+ for (int j = c; j < c + cellColSpan; ++j)
+ d->grid[i * d->nCols + j] = cellFragment;
+
+ // erase the cell marker
+ p->remove(pos, 1);
+
+ const int nextFragment = d->cells.value(cellIndex, d->fragment_end);
+ const uint nextPos = p->fragmentMap().position(nextFragment);
+
+ Q_ASSERT(nextPos >= pos);
+
+ // merge the contents of the cell (if not empty)
+ if (nextPos > pos) {
+ if (needsParagraph) {
+ needsParagraph = false;
+ QTextCursor(p, insertPos++).insertBlock();
+ p->move(pos + 1, insertPos, nextPos - pos);
+ } else if (rowHasText) {
+ QTextCursor(p, insertPos++).insertText(QLatin1String(" "));
+ p->move(pos + 1, insertPos, nextPos - pos);
+ } else {
+ p->move(pos, insertPos, nextPos - pos);
+ }
+
+ insertPos += nextPos - pos;
+ rowHasText = true;
+ }
+ }
+
+ if (rowHasText) {
+ needsParagraph = true;
+ rowHasText = false;
+ }
+
+ // erase cells from last row
+ if (firstCellIndex >= 0) {
+ d->cellIndices.remove(firstCellIndex, cellIndex - firstCellIndex);
+ d->cells.erase(d->cells.begin() + firstCellIndex, d->cells.begin() + cellIndex);
+ }
+ }
+
+ d->fragment_start = d->cells.first();
+
+ fmt.setTableCellRowSpan(numRows);
+ fmt.setTableCellColumnSpan(numCols);
+ p->setCharFormat(origCellPosition, 1, fmt);
+
+ d->blockFragmentUpdates = false;
+ d->dirty = false;
+
+ p->endEditBlock();
+}
+
+/*!
+ \overload
+ \since 4.1
+
+ Merges the cells selected by the provided \a cursor.
+
+ \sa splitCell()
+*/
+void QTextTable::mergeCells(const QTextCursor &cursor)
+{
+ if (!cursor.hasComplexSelection())
+ return;
+
+ int firstRow, numRows, firstColumn, numColumns;
+ cursor.selectedTableCells(&firstRow, &numRows, &firstColumn, &numColumns);
+ mergeCells(firstRow, firstColumn, numRows, numColumns);
+}
+
+/*!
+ \since 4.1
+
+ Splits the specified cell at \a row and \a column into an array of multiple
+ cells with dimensions specified by \a numRows and \a numCols.
+
+ \note It is only possible to split cells that span multiple rows or columns, such as rows
+ that have been merged using mergeCells().
+
+ \sa mergeCells()
+*/
+void QTextTable::splitCell(int row, int column, int numRows, int numCols)
+{
+ Q_D(QTextTable);
+
+ if (d->dirty)
+ d->update();
+
+ QTextDocumentPrivate *p = d->pieceTable;
+ QTextFormatCollection *c = p->formatCollection();
+
+ const QTextTableCell cell = cellAt(row, column);
+ if (!cell.isValid())
+ return;
+ row = cell.row();
+ column = cell.column();
+
+ QTextCharFormat fmt = cell.format();
+ const int rowSpan = fmt.tableCellRowSpan();
+ const int colSpan = fmt.tableCellColumnSpan();
+
+ // nothing to split?
+ if (numRows > rowSpan || numCols > colSpan)
+ return;
+
+ p->beginEditBlock();
+
+ const int origCellPosition = cell.firstPosition() - 1;
+
+ QVarLengthArray<int> rowPositions(rowSpan);
+
+ rowPositions[0] = cell.lastPosition();
+
+ for (int r = row + 1; r < row + rowSpan; ++r) {
+ // find the cell before which to insert the new cell markers
+ int gridIndex = r * d->nCols + column;
+ QVector<int>::iterator it = qUpperBound(d->cellIndices.begin(), d->cellIndices.end(), gridIndex);
+ int cellIndex = it - d->cellIndices.begin();
+ int fragment = d->cells.value(cellIndex, d->fragment_end);
+ rowPositions[r - row] = p->fragmentMap().position(fragment);
+ }
+
+ fmt.setTableCellColumnSpan(1);
+ fmt.setTableCellRowSpan(1);
+ const int fmtIndex = c->indexForFormat(fmt);
+ const int blockIndex = p->blockMap().find(cell.lastPosition())->format;
+
+ int insertAdjustement = 0;
+ for (int i = 0; i < numRows; ++i) {
+ for (int c = 0; c < colSpan - numCols; ++c)
+ p->insertBlock(QTextBeginningOfFrame, rowPositions[i] + insertAdjustement + c, blockIndex, fmtIndex);
+ insertAdjustement += colSpan - numCols;
+ }
+
+ for (int i = numRows; i < rowSpan; ++i) {
+ for (int c = 0; c < colSpan; ++c)
+ p->insertBlock(QTextBeginningOfFrame, rowPositions[i] + insertAdjustement + c, blockIndex, fmtIndex);
+ insertAdjustement += colSpan;
+ }
+
+ fmt.setTableCellRowSpan(numRows);
+ fmt.setTableCellColumnSpan(numCols);
+ p->setCharFormat(origCellPosition, 1, fmt);
+
+ p->endEditBlock();
+}
+
+/*!
+ Returns the number of rows in the table.
+
+ \sa columns()
+*/
+int QTextTable::rows() const
+{
+ Q_D(const QTextTable);
+ if (d->dirty)
+ d->update();
+
+ return d->nRows;
+}
+
+/*!
+ Returns the number of columns in the table.
+
+ \sa rows()
+*/
+int QTextTable::columns() const
+{
+ Q_D(const QTextTable);
+ if (d->dirty)
+ d->update();
+
+ return d->nCols;
+}
+
+#if 0
+void QTextTable::mergeCells(const QTextCursor &selection)
+{
+}
+#endif
+
+/*!
+ \fn QTextCursor QTextTable::rowStart(const QTextCursor &cursor) const
+
+ Returns a cursor pointing to the start of the row that contains the
+ given \a cursor.
+
+ \sa rowEnd()
+*/
+QTextCursor QTextTable::rowStart(const QTextCursor &c) const
+{
+ Q_D(const QTextTable);
+ QTextTableCell cell = cellAt(c);
+ if (!cell.isValid())
+ return QTextCursor();
+
+ int row = cell.row();
+ QTextDocumentPrivate *p = d->pieceTable;
+ QTextDocumentPrivate::FragmentIterator it(&p->fragmentMap(), d->grid[row*d->nCols]);
+ return QTextCursor(p, it.position());
+}
+
+/*!
+ \fn QTextCursor QTextTable::rowEnd(const QTextCursor &cursor) const
+
+ Returns a cursor pointing to the end of the row that contains the given
+ \a cursor.
+
+ \sa rowStart()
+*/
+QTextCursor QTextTable::rowEnd(const QTextCursor &c) const
+{
+ Q_D(const QTextTable);
+ QTextTableCell cell = cellAt(c);
+ if (!cell.isValid())
+ return QTextCursor();
+
+ int row = cell.row() + 1;
+ int fragment = row < d->nRows ? d->grid[row*d->nCols] : d->fragment_end;
+ QTextDocumentPrivate *p = d->pieceTable;
+ QTextDocumentPrivate::FragmentIterator it(&p->fragmentMap(), fragment);
+ return QTextCursor(p, it.position() - 1);
+}
+
+/*!
+ \fn void QTextTable::setFormat(const QTextTableFormat &format)
+
+ Sets the table's \a format.
+
+ \sa format()
+*/
+void QTextTable::setFormat(const QTextTableFormat &format)
+{
+ QTextTableFormat fmt = format;
+ // don't try to change the number of table columns from here
+ fmt.setColumns(columns());
+ QTextObject::setFormat(fmt);
+}
+
+/*!
+ \fn QTextTableFormat QTextTable::format() const
+
+ Returns the table's format.
+
+ \sa setFormat()
+*/
+
+QT_END_NAMESPACE
diff --git a/src/gui/text/qtexttable.h b/src/gui/text/qtexttable.h
new file mode 100644
index 0000000000..76cd4275f3
--- /dev/null
+++ b/src/gui/text/qtexttable.h
@@ -0,0 +1,145 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QTEXTTABLE_H
+#define QTEXTTABLE_H
+
+#include <QtCore/qglobal.h>
+#include <QtCore/qobject.h>
+#include <QtGui/qtextobject.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+class QTextCursor;
+class QTextTable;
+class QTextTablePrivate;
+
+class Q_GUI_EXPORT QTextTableCell
+{
+public:
+ QTextTableCell() : table(0) {}
+ ~QTextTableCell() {}
+ QTextTableCell(const QTextTableCell &o) : table(o.table), fragment(o.fragment) {}
+ QTextTableCell &operator=(const QTextTableCell &o)
+ { table = o.table; fragment = o.fragment; return *this; }
+
+ void setFormat(const QTextCharFormat &format);
+ QTextCharFormat format() const;
+
+ int row() const;
+ int column() const;
+
+ int rowSpan() const;
+ int columnSpan() const;
+
+ inline bool isValid() const { return table != 0; }
+
+ QTextCursor firstCursorPosition() const;
+ QTextCursor lastCursorPosition() const;
+ int firstPosition() const;
+ int lastPosition() const;
+
+ inline bool operator==(const QTextTableCell &other) const
+ { return table == other.table && fragment == other.fragment; }
+ inline bool operator!=(const QTextTableCell &other) const
+ { return !operator==(other); }
+
+ QTextFrame::iterator begin() const;
+ QTextFrame::iterator end() const;
+
+ int tableCellFormatIndex() const;
+
+private:
+ friend class QTextTable;
+ QTextTableCell(const QTextTable *t, int f)
+ : table(t), fragment(f) {}
+
+ const QTextTable *table;
+ int fragment;
+};
+
+class Q_GUI_EXPORT QTextTable : public QTextFrame
+{
+ Q_OBJECT
+public:
+ explicit QTextTable(QTextDocument *doc);
+ ~QTextTable();
+
+ void resize(int rows, int cols);
+ void insertRows(int pos, int num);
+ void insertColumns(int pos, int num);
+ void appendRows(int count);
+ void appendColumns(int count);
+ void removeRows(int pos, int num);
+ void removeColumns(int pos, int num);
+
+ void mergeCells(int row, int col, int numRows, int numCols);
+ void mergeCells(const QTextCursor &cursor);
+ void splitCell(int row, int col, int numRows, int numCols);
+
+ int rows() const;
+ int columns() const;
+
+ QTextTableCell cellAt(int row, int col) const;
+ QTextTableCell cellAt(int position) const;
+ QTextTableCell cellAt(const QTextCursor &c) const;
+
+ QTextCursor rowStart(const QTextCursor &c) const;
+ QTextCursor rowEnd(const QTextCursor &c) const;
+
+ void setFormat(const QTextTableFormat &format);
+ QTextTableFormat format() const { return QTextObject::format().toTableFormat(); }
+
+private:
+ Q_DISABLE_COPY(QTextTable)
+ Q_DECLARE_PRIVATE(QTextTable)
+ friend class QTextTableCell;
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QTEXTTABLE_H
diff --git a/src/gui/text/qtexttable_p.h b/src/gui/text/qtexttable_p.h
new file mode 100644
index 0000000000..1ba3a3f129
--- /dev/null
+++ b/src/gui/text/qtexttable_p.h
@@ -0,0 +1,89 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QTEXTTABLE_P_H
+#define QTEXTTABLE_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/qtextobject_p.h"
+#include "private/qtextdocument_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class QTextTablePrivate : public QTextFramePrivate
+{
+ Q_DECLARE_PUBLIC(QTextTable)
+public:
+ QTextTablePrivate() : grid(0), nRows(0), dirty(true), blockFragmentUpdates(false) {}
+ ~QTextTablePrivate();
+
+ static QTextTable *createTable(QTextDocumentPrivate *, int pos, int rows, int cols, const QTextTableFormat &tableFormat);
+ void fragmentAdded(const QChar &type, uint fragment);
+ void fragmentRemoved(const QChar &type, uint fragment);
+
+ void update() const;
+
+ int findCellIndex(int fragment) const;
+
+ QList<int> cells;
+ // symmetric to cells array and maps to indecs in grid,
+ // used for fast-lookup for row/column by fragment
+ mutable QVector<int> cellIndices;
+ mutable int *grid;
+ mutable int nRows;
+ mutable int nCols;
+ mutable bool dirty;
+ bool blockFragmentUpdates;
+};
+
+QT_END_NAMESPACE
+
+#endif // QTEXTTABLE_P_H
diff --git a/src/gui/text/qzip.cpp b/src/gui/text/qzip.cpp
new file mode 100644
index 0000000000..c6c2e6913f
--- /dev/null
+++ b/src/gui/text/qzip.cpp
@@ -0,0 +1,1208 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <qglobal.h>
+
+#ifndef QT_NO_TEXTODFWRITER
+
+#include "qzipreader_p.h"
+#include "qzipwriter_p.h"
+#include <qdatetime.h>
+#include <qplatformdefs.h>
+#include <qendian.h>
+#include <qdebug.h>
+#include <qdir.h>
+
+#include <zlib.h>
+
+#if defined(Q_OS_WIN)
+#undef S_IFREG
+#define S_IFREG 0100000
+# define S_ISDIR(x) ((x) & 0040000) > 0
+# define S_ISREG(x) ((x) & 0170000) == S_IFREG
+# define S_IFLNK 020000
+# define S_ISLNK(x) ((x) & S_IFLNK) > 0
+# define S_IRUSR 0400
+# define S_IWUSR 0200
+# define S_IXUSR 0100
+# define S_IRGRP 0040
+# define S_IWGRP 0020
+# define S_IXGRP 0010
+# define S_IROTH 0004
+# define S_IWOTH 0002
+# define S_IXOTH 0001
+#endif
+
+#if 0
+#define ZDEBUG qDebug
+#else
+#define ZDEBUG if (0) qDebug
+#endif
+
+QT_BEGIN_NAMESPACE
+
+static inline uint readUInt(const uchar *data)
+{
+ return (data[0]) + (data[1]<<8) + (data[2]<<16) + (data[3]<<24);
+}
+
+static inline ushort readUShort(const uchar *data)
+{
+ return (data[0]) + (data[1]<<8);
+}
+
+static inline void writeUInt(uchar *data, uint i)
+{
+ data[0] = i & 0xff;
+ data[1] = (i>>8) & 0xff;
+ data[2] = (i>>16) & 0xff;
+ data[3] = (i>>24) & 0xff;
+}
+
+static inline void writeUShort(uchar *data, ushort i)
+{
+ data[0] = i & 0xff;
+ data[1] = (i>>8) & 0xff;
+}
+
+static inline void copyUInt(uchar *dest, const uchar *src)
+{
+ dest[0] = src[0];
+ dest[1] = src[1];
+ dest[2] = src[2];
+ dest[3] = src[3];
+}
+
+static inline void copyUShort(uchar *dest, const uchar *src)
+{
+ dest[0] = src[0];
+ dest[1] = src[1];
+}
+
+static void writeMSDosDate(uchar *dest, const QDateTime& dt)
+{
+ if (dt.isValid()) {
+ quint16 time =
+ (dt.time().hour() << 11) // 5 bit hour
+ | (dt.time().minute() << 5) // 6 bit minute
+ | (dt.time().second() >> 1); // 5 bit double seconds
+
+ dest[0] = time & 0xff;
+ dest[1] = time >> 8;
+
+ quint16 date =
+ ((dt.date().year() - 1980) << 9) // 7 bit year 1980-based
+ | (dt.date().month() << 5) // 4 bit month
+ | (dt.date().day()); // 5 bit day
+
+ dest[2] = char(date);
+ dest[3] = char(date >> 8);
+ } else {
+ dest[0] = 0;
+ dest[1] = 0;
+ dest[2] = 0;
+ dest[3] = 0;
+ }
+}
+
+static quint32 permissionsToMode(QFile::Permissions perms)
+{
+ quint32 mode = 0;
+ if (perms & QFile::ReadOwner)
+ mode |= S_IRUSR;
+ if (perms & QFile::WriteOwner)
+ mode |= S_IWUSR;
+ if (perms & QFile::ExeOwner)
+ mode |= S_IXUSR;
+ if (perms & QFile::ReadUser)
+ mode |= S_IRUSR;
+ if (perms & QFile::WriteUser)
+ mode |= S_IWUSR;
+ if (perms & QFile::ExeUser)
+ mode |= S_IXUSR;
+ if (perms & QFile::ReadGroup)
+ mode |= S_IRGRP;
+ if (perms & QFile::WriteGroup)
+ mode |= S_IWGRP;
+ if (perms & QFile::ExeGroup)
+ mode |= S_IXGRP;
+ if (perms & QFile::ReadOther)
+ mode |= S_IROTH;
+ if (perms & QFile::WriteOther)
+ mode |= S_IWOTH;
+ if (perms & QFile::ExeOther)
+ mode |= S_IXOTH;
+ return mode;
+}
+
+static int inflate(Bytef *dest, ulong *destLen, const Bytef *source, ulong sourceLen)
+{
+ z_stream stream;
+ int err;
+
+ stream.next_in = (Bytef*)source;
+ stream.avail_in = (uInt)sourceLen;
+ if ((uLong)stream.avail_in != sourceLen)
+ return Z_BUF_ERROR;
+
+ stream.next_out = dest;
+ stream.avail_out = (uInt)*destLen;
+ if ((uLong)stream.avail_out != *destLen)
+ return Z_BUF_ERROR;
+
+ stream.zalloc = (alloc_func)0;
+ stream.zfree = (free_func)0;
+
+ err = inflateInit2(&stream, -MAX_WBITS);
+ if (err != Z_OK)
+ return err;
+
+ err = inflate(&stream, Z_FINISH);
+ if (err != Z_STREAM_END) {
+ inflateEnd(&stream);
+ if (err == Z_NEED_DICT || (err == Z_BUF_ERROR && stream.avail_in == 0))
+ return Z_DATA_ERROR;
+ return err;
+ }
+ *destLen = stream.total_out;
+
+ err = inflateEnd(&stream);
+ return err;
+}
+
+static int deflate (Bytef *dest, ulong *destLen, const Bytef *source, ulong sourceLen)
+{
+ z_stream stream;
+ int err;
+
+ stream.next_in = (Bytef*)source;
+ stream.avail_in = (uInt)sourceLen;
+ stream.next_out = dest;
+ stream.avail_out = (uInt)*destLen;
+ if ((uLong)stream.avail_out != *destLen) return Z_BUF_ERROR;
+
+ stream.zalloc = (alloc_func)0;
+ stream.zfree = (free_func)0;
+ stream.opaque = (voidpf)0;
+
+ err = deflateInit2(&stream, Z_DEFAULT_COMPRESSION, Z_DEFLATED, -MAX_WBITS, 8, Z_DEFAULT_STRATEGY);
+ if (err != Z_OK) return err;
+
+ err = deflate(&stream, Z_FINISH);
+ if (err != Z_STREAM_END) {
+ deflateEnd(&stream);
+ return err == Z_OK ? Z_BUF_ERROR : err;
+ }
+ *destLen = stream.total_out;
+
+ err = deflateEnd(&stream);
+ return err;
+}
+
+static QFile::Permissions modeToPermissions(quint32 mode)
+{
+ QFile::Permissions ret;
+ if (mode & S_IRUSR)
+ ret |= QFile::ReadOwner;
+ if (mode & S_IWUSR)
+ ret |= QFile::WriteOwner;
+ if (mode & S_IXUSR)
+ ret |= QFile::ExeOwner;
+ if (mode & S_IRUSR)
+ ret |= QFile::ReadUser;
+ if (mode & S_IWUSR)
+ ret |= QFile::WriteUser;
+ if (mode & S_IXUSR)
+ ret |= QFile::ExeUser;
+ if (mode & S_IRGRP)
+ ret |= QFile::ReadGroup;
+ if (mode & S_IWGRP)
+ ret |= QFile::WriteGroup;
+ if (mode & S_IXGRP)
+ ret |= QFile::ExeGroup;
+ if (mode & S_IROTH)
+ ret |= QFile::ReadOther;
+ if (mode & S_IWOTH)
+ ret |= QFile::WriteOther;
+ if (mode & S_IXOTH)
+ ret |= QFile::ExeOther;
+ return ret;
+}
+
+struct LocalFileHeader
+{
+ uchar signature[4]; // 0x04034b50
+ uchar version_needed[2];
+ uchar general_purpose_bits[2];
+ uchar compression_method[2];
+ uchar last_mod_file[4];
+ uchar crc_32[4];
+ uchar compressed_size[4];
+ uchar uncompressed_size[4];
+ uchar file_name_length[2];
+ uchar extra_field_length[2];
+};
+
+struct DataDescriptor
+{
+ uchar crc_32[4];
+ uchar compressed_size[4];
+ uchar uncompressed_size[4];
+};
+
+struct CentralFileHeader
+{
+ uchar signature[4]; // 0x02014b50
+ uchar version_made[2];
+ uchar version_needed[2];
+ uchar general_purpose_bits[2];
+ uchar compression_method[2];
+ uchar last_mod_file[4];
+ uchar crc_32[4];
+ uchar compressed_size[4];
+ uchar uncompressed_size[4];
+ uchar file_name_length[2];
+ uchar extra_field_length[2];
+ uchar file_comment_length[2];
+ uchar disk_start[2];
+ uchar internal_file_attributes[2];
+ uchar external_file_attributes[4];
+ uchar offset_local_header[4];
+ LocalFileHeader toLocalHeader() const;
+};
+
+struct EndOfDirectory
+{
+ uchar signature[4]; // 0x06054b50
+ uchar this_disk[2];
+ uchar start_of_directory_disk[2];
+ uchar num_dir_entries_this_disk[2];
+ uchar num_dir_entries[2];
+ uchar directory_size[4];
+ uchar dir_start_offset[4];
+ uchar comment_length[2];
+};
+
+struct FileHeader
+{
+ CentralFileHeader h;
+ QByteArray file_name;
+ QByteArray extra_field;
+ QByteArray file_comment;
+};
+
+QZipReader::FileInfo::FileInfo()
+ : isDir(false), isFile(true), isSymLink(false), crc32(0), size(0)
+{
+}
+
+QZipReader::FileInfo::~FileInfo()
+{
+}
+
+QZipReader::FileInfo::FileInfo(const FileInfo &other)
+{
+ operator=(other);
+}
+
+QZipReader::FileInfo& QZipReader::FileInfo::operator=(const FileInfo &other)
+{
+ filePath = other.filePath;
+ isDir = other.isDir;
+ isFile = other.isFile;
+ isSymLink = other.isSymLink;
+ permissions = other.permissions;
+ crc32 = other.crc32;
+ size = other.size;
+ return *this;
+}
+
+class QZipPrivate
+{
+public:
+ QZipPrivate(QIODevice *device, bool ownDev)
+ : device(device), ownDevice(ownDev), dirtyFileTree(true), start_of_directory(0)
+ {
+ }
+
+ ~QZipPrivate()
+ {
+ if (ownDevice)
+ delete device;
+ }
+
+ void fillFileInfo(int index, QZipReader::FileInfo &fileInfo) const;
+
+ QIODevice *device;
+ bool ownDevice;
+ bool dirtyFileTree;
+ QList<FileHeader> fileHeaders;
+ QByteArray comment;
+ uint start_of_directory;
+};
+
+void QZipPrivate::fillFileInfo(int index, QZipReader::FileInfo &fileInfo) const
+{
+ FileHeader header = fileHeaders.at(index);
+ fileInfo.filePath = QString::fromLocal8Bit(header.file_name);
+ const quint32 mode = (qFromLittleEndian<quint32>(&header.h.external_file_attributes[0]) >> 16) & 0xFFFF;
+ fileInfo.isDir = S_ISDIR(mode);
+ fileInfo.isFile = S_ISREG(mode);
+ fileInfo.isSymLink = S_ISLNK(mode);
+ fileInfo.permissions = modeToPermissions(mode);
+ fileInfo.crc32 = readUInt(header.h.crc_32);
+ fileInfo.size = readUInt(header.h.uncompressed_size);
+}
+
+class QZipReaderPrivate : public QZipPrivate
+{
+public:
+ QZipReaderPrivate(QIODevice *device, bool ownDev)
+ : QZipPrivate(device, ownDev), status(QZipReader::NoError)
+ {
+ }
+
+ void scanFiles();
+
+ QZipReader::Status status;
+};
+
+class QZipWriterPrivate : public QZipPrivate
+{
+public:
+ QZipWriterPrivate(QIODevice *device, bool ownDev)
+ : QZipPrivate(device, ownDev),
+ status(QZipWriter::NoError),
+ permissions(QFile::ReadOwner | QFile::WriteOwner),
+ compressionPolicy(QZipWriter::AlwaysCompress)
+ {
+ }
+
+ QZipWriter::Status status;
+ QFile::Permissions permissions;
+ QZipWriter::CompressionPolicy compressionPolicy;
+
+ enum EntryType { Directory, File, Symlink };
+
+ void addEntry(EntryType type, const QString &fileName, const QByteArray &contents);
+};
+
+LocalFileHeader CentralFileHeader::toLocalHeader() const
+{
+ LocalFileHeader h;
+ writeUInt(h.signature, 0x04034b50);
+ copyUShort(h.version_needed, version_needed);
+ copyUShort(h.general_purpose_bits, general_purpose_bits);
+ copyUShort(h.compression_method, compression_method);
+ copyUInt(h.last_mod_file, last_mod_file);
+ copyUInt(h.crc_32, crc_32);
+ copyUInt(h.compressed_size, compressed_size);
+ copyUInt(h.uncompressed_size, uncompressed_size);
+ copyUShort(h.file_name_length, file_name_length);
+ copyUShort(h.extra_field_length, extra_field_length);
+ return h;
+}
+
+void QZipReaderPrivate::scanFiles()
+{
+ if (!dirtyFileTree)
+ return;
+
+ if (! (device->isOpen() || device->open(QIODevice::ReadOnly))) {
+ status = QZipReader::FileOpenError;
+ return;
+ }
+
+ if ((device->openMode() & QIODevice::ReadOnly) == 0) { // only read the index from readable files.
+ status = QZipReader::FileReadError;
+ return;
+ }
+
+ dirtyFileTree = false;
+ uchar tmp[4];
+ device->read((char *)tmp, 4);
+ if (readUInt(tmp) != 0x04034b50) {
+ qWarning() << "QZip: not a zip file!";
+ return;
+ }
+
+ // find EndOfDirectory header
+ int i = 0;
+ int start_of_directory = -1;
+ int num_dir_entries = 0;
+ EndOfDirectory eod;
+ while (start_of_directory == -1) {
+ int pos = device->size() - sizeof(EndOfDirectory) - i;
+ if (pos < 0 || i > 65535) {
+ qWarning() << "QZip: EndOfDirectory not found";
+ return;
+ }
+
+ device->seek(pos);
+ device->read((char *)&eod, sizeof(EndOfDirectory));
+ if (readUInt(eod.signature) == 0x06054b50)
+ break;
+ ++i;
+ }
+
+ // have the eod
+ start_of_directory = readUInt(eod.dir_start_offset);
+ num_dir_entries = readUShort(eod.num_dir_entries);
+ ZDEBUG("start_of_directory at %d, num_dir_entries=%d", start_of_directory, num_dir_entries);
+ int comment_length = readUShort(eod.comment_length);
+ if (comment_length != i)
+ qWarning() << "QZip: failed to parse zip file.";
+ comment = device->read(qMin(comment_length, i));
+
+
+ device->seek(start_of_directory);
+ for (i = 0; i < num_dir_entries; ++i) {
+ FileHeader header;
+ int read = device->read((char *) &header.h, sizeof(CentralFileHeader));
+ if (read < (int)sizeof(CentralFileHeader)) {
+ qWarning() << "QZip: Failed to read complete header, index may be incomplete";
+ break;
+ }
+ if (readUInt(header.h.signature) != 0x02014b50) {
+ qWarning() << "QZip: invalid header signature, index may be incomplete";
+ break;
+ }
+
+ int l = readUShort(header.h.file_name_length);
+ header.file_name = device->read(l);
+ if (header.file_name.length() != l) {
+ qWarning() << "QZip: Failed to read filename from zip index, index may be incomplete";
+ break;
+ }
+ l = readUShort(header.h.extra_field_length);
+ header.extra_field = device->read(l);
+ if (header.extra_field.length() != l) {
+ qWarning() << "QZip: Failed to read extra field in zip file, skipping file, index may be incomplete";
+ break;
+ }
+ l = readUShort(header.h.file_comment_length);
+ header.file_comment = device->read(l);
+ if (header.file_comment.length() != l) {
+ qWarning() << "QZip: Failed to read read file comment, index may be incomplete";
+ break;
+ }
+
+ ZDEBUG("found file '%s'", header.file_name.data());
+ fileHeaders.append(header);
+ }
+}
+
+void QZipWriterPrivate::addEntry(EntryType type, const QString &fileName, const QByteArray &contents/*, QFile::Permissions permissions, QZip::Method m*/)
+{
+#ifndef NDEBUG
+ static const char *entryTypes[] = {
+ "directory",
+ "file ",
+ "symlink " };
+ ZDEBUG() << "adding" << entryTypes[type] <<":" << fileName.toUtf8().data() << (type == 2 ? (" -> " + contents).constData() : "");
+#endif
+
+ if (! (device->isOpen() || device->open(QIODevice::WriteOnly))) {
+ status = QZipWriter::FileOpenError;
+ return;
+ }
+ device->seek(start_of_directory);
+
+ // don't compress small files
+ QZipWriter::CompressionPolicy compression = compressionPolicy;
+ if (compressionPolicy == QZipWriter::AutoCompress) {
+ if (contents.length() < 64)
+ compression = QZipWriter::NeverCompress;
+ else
+ compression = QZipWriter::AlwaysCompress;
+ }
+
+ FileHeader header;
+ memset(&header.h, 0, sizeof(CentralFileHeader));
+ writeUInt(header.h.signature, 0x02014b50);
+
+ writeUShort(header.h.version_needed, 0x14);
+ writeUInt(header.h.uncompressed_size, contents.length());
+ writeMSDosDate(header.h.last_mod_file, QDateTime::currentDateTime());
+ QByteArray data = contents;
+ if (compression == QZipWriter::AlwaysCompress) {
+ writeUShort(header.h.compression_method, 8);
+
+ ulong len = contents.length();
+ // shamelessly copied form zlib
+ len += (len >> 12) + (len >> 14) + 11;
+ int res;
+ do {
+ data.resize(len);
+ res = deflate((uchar*)data.data(), &len, (const uchar*)contents.constData(), contents.length());
+
+ switch (res) {
+ case Z_OK:
+ data.resize(len);
+ break;
+ case Z_MEM_ERROR:
+ qWarning("QZip: Z_MEM_ERROR: Not enough memory to compress file, skipping");
+ data.resize(0);
+ break;
+ case Z_BUF_ERROR:
+ len *= 2;
+ break;
+ }
+ } while (res == Z_BUF_ERROR);
+ }
+// TODO add a check if data.length() > contents.length(). Then try to store the original and revert the compression method to be uncompressed
+ writeUInt(header.h.compressed_size, data.length());
+ uint crc_32 = ::crc32(0, 0, 0);
+ crc_32 = ::crc32(crc_32, (const uchar *)contents.constData(), contents.length());
+ writeUInt(header.h.crc_32, crc_32);
+
+ header.file_name = fileName.toLocal8Bit();
+ if (header.file_name.size() > 0xffff) {
+ qWarning("QZip: Filename too long, chopping it to 65535 characters");
+ header.file_name = header.file_name.left(0xffff);
+ }
+ writeUShort(header.h.file_name_length, header.file_name.length());
+ //h.extra_field_length[2];
+
+ writeUShort(header.h.version_made, 3 << 8);
+ //uchar internal_file_attributes[2];
+ //uchar external_file_attributes[4];
+ quint32 mode = permissionsToMode(permissions);
+ switch (type) {
+ case File: mode |= S_IFREG; break;
+ case Directory: mode |= S_IFDIR; break;
+ case Symlink: mode |= S_IFLNK; break;
+ }
+ writeUInt(header.h.external_file_attributes, mode << 16);
+ writeUInt(header.h.offset_local_header, start_of_directory);
+
+
+ fileHeaders.append(header);
+
+ LocalFileHeader h = header.h.toLocalHeader();
+ device->write((const char *)&h, sizeof(LocalFileHeader));
+ device->write(header.file_name);
+ device->write(data);
+ start_of_directory = device->pos();
+ dirtyFileTree = true;
+}
+
+////////////////////////////// Reader
+
+/*!
+ \class QZipReader::FileInfo
+ \internal
+ Represents one entry in the zip table of contents.
+*/
+
+/*!
+ \variable FileInfo::filePath
+ The full filepath inside the archive.
+*/
+
+/*!
+ \variable FileInfo::isDir
+ A boolean type indicating if the entry is a directory.
+*/
+
+/*!
+ \variable FileInfo::isFile
+ A boolean type, if it is one this entry is a file.
+*/
+
+/*!
+ \variable FileInfo::isSymLink
+ A boolean type, if it is one this entry is symbolic link.
+*/
+
+/*!
+ \variable FileInfo::permissions
+ A list of flags for the permissions of this entry.
+*/
+
+/*!
+ \variable FileInfo::crc32
+ The calculated checksum as a crc32 type.
+*/
+
+/*!
+ \variable FileInfo::size
+ The total size of the unpacked content.
+*/
+
+/*!
+ \variable FileInfo::d
+ \internal
+ private pointer.
+*/
+
+/*!
+ \class QZipReader
+ \internal
+ \since 4.5
+
+ \brief the QZipReader class provides a way to inspect the contents of a zip
+ archive and extract individual files from it.
+
+ QZipReader can be used to read a zip archive either from a file or from any
+ device. An in-memory QBuffer for instance. The reader can be used to read
+ which files are in the archive using fileInfoList() and entryInfoAt() but
+ also to extract individual files using fileData() or even to extract all
+ files in the archive using extractAll()
+*/
+
+/*!
+ Create a new zip archive that operates on the \a fileName. The file will be
+ opened with the \a mode.
+*/
+QZipReader::QZipReader(const QString &archive, QIODevice::OpenMode mode)
+{
+ QFile *f = new QFile(archive);
+ f->open(mode);
+ QZipReader::Status status;
+ if (f->error() == QFile::NoError)
+ status = NoError;
+ else {
+ if (f->error() == QFile::ReadError)
+ status = FileReadError;
+ else if (f->error() == QFile::OpenError)
+ status = FileOpenError;
+ else if (f->error() == QFile::PermissionsError)
+ status = FilePermissionsError;
+ else
+ status = FileError;
+ }
+
+ d = new QZipReaderPrivate(f, /*ownDevice=*/true);
+ d->status = status;
+}
+
+/*!
+ Create a new zip archive that operates on the archive found in \a device.
+ You have to open the device previous to calling the constructor and only a
+ device that is readable will be scanned for zip filecontent.
+ */
+QZipReader::QZipReader(QIODevice *device)
+ : d(new QZipReaderPrivate(device, /*ownDevice=*/false))
+{
+ Q_ASSERT(device);
+}
+
+/*!
+ Desctructor
+*/
+QZipReader::~QZipReader()
+{
+ close();
+ delete d;
+}
+
+/*!
+ Returns true if the user can read the file; otherwise returns false.
+*/
+bool QZipReader::isReadable() const
+{
+ return d->device->isReadable();
+}
+
+/*!
+ Returns true if the file exists; otherwise returns false.
+*/
+bool QZipReader::exists() const
+{
+ QFile *f = qobject_cast<QFile*> (d->device);
+ if (f == 0)
+ return true;
+ return f->exists();
+}
+
+/*!
+ Returns the list of files the archive contains.
+*/
+QList<QZipReader::FileInfo> QZipReader::fileInfoList() const
+{
+ d->scanFiles();
+ QList<QZipReader::FileInfo> files;
+ for (int i = 0; d && i < d->fileHeaders.size(); ++i) {
+ QZipReader::FileInfo fi;
+ d->fillFileInfo(i, fi);
+ files.append(fi);
+ }
+ return files;
+
+}
+
+/*!
+ Return the number of items in the zip archive.
+*/
+int QZipReader::count() const
+{
+ d->scanFiles();
+ return d->fileHeaders.count();
+}
+
+/*!
+ Returns a FileInfo of an entry in the zipfile.
+ The \a index is the index into the directoy listing of the zipfile.
+
+ \sa fileInfoList()
+*/
+QZipReader::FileInfo QZipReader::entryInfoAt(int index) const
+{
+ d->scanFiles();
+ QZipReader::FileInfo fi;
+ d->fillFileInfo(index, fi);
+ return fi;
+}
+
+/*!
+ Fetch the file contents from the zip archive and return the uncompressed bytes.
+*/
+QByteArray QZipReader::fileData(const QString &fileName) const
+{
+ d->scanFiles();
+ int i;
+ for (i = 0; i < d->fileHeaders.size(); ++i) {
+ if (QString::fromLocal8Bit(d->fileHeaders.at(i).file_name) == fileName)
+ break;
+ }
+ if (i == d->fileHeaders.size())
+ return QByteArray();
+
+ FileHeader header = d->fileHeaders.at(i);
+
+ int compressed_size = readUInt(header.h.compressed_size);
+ int uncompressed_size = readUInt(header.h.uncompressed_size);
+ int start = readUInt(header.h.offset_local_header);
+ //qDebug("uncompressing file %d: local header at %d", i, start);
+
+ d->device->seek(start);
+ LocalFileHeader lh;
+ d->device->read((char *)&lh, sizeof(LocalFileHeader));
+ uint skip = readUShort(lh.file_name_length) + readUShort(lh.extra_field_length);
+ d->device->seek(d->device->pos() + skip);
+
+ int compression_method = readUShort(lh.compression_method);
+ //qDebug("file=%s: compressed_size=%d, uncompressed_size=%d", fileName.toLocal8Bit().data(), compressed_size, uncompressed_size);
+
+ //qDebug("file at %lld", d->device->pos());
+ QByteArray compressed = d->device->read(compressed_size);
+ if (compression_method == 0) {
+ // no compression
+ compressed.truncate(uncompressed_size);
+ return compressed;
+ } else if (compression_method == 8) {
+ // Deflate
+ //qDebug("compressed=%d", compressed.size());
+ compressed.truncate(compressed_size);
+ QByteArray baunzip;
+ ulong len = qMax(uncompressed_size, 1);
+ int res;
+ do {
+ baunzip.resize(len);
+ res = inflate((uchar*)baunzip.data(), &len,
+ (uchar*)compressed.constData(), compressed_size);
+
+ switch (res) {
+ case Z_OK:
+ if ((int)len != baunzip.size())
+ baunzip.resize(len);
+ break;
+ case Z_MEM_ERROR:
+ qWarning("QZip: Z_MEM_ERROR: Not enough memory");
+ break;
+ case Z_BUF_ERROR:
+ len *= 2;
+ break;
+ case Z_DATA_ERROR:
+ qWarning("QZip: Z_DATA_ERROR: Input data is corrupted");
+ break;
+ }
+ } while (res == Z_BUF_ERROR);
+ return baunzip;
+ }
+ qWarning() << "QZip: Unknown compression method";
+ return QByteArray();
+}
+
+/*!
+ Extracts the full contents of the zip file into \a destinationDir on
+ the local filesystem.
+ In case writing or linking a file fails, the extraction will be aborted.
+*/
+bool QZipReader::extractAll(const QString &destinationDir) const
+{
+ QDir baseDir(destinationDir);
+
+ // create directories first
+ QList<FileInfo> allFiles = fileInfoList();
+ foreach (FileInfo fi, allFiles) {
+ const QString absPath = destinationDir + QDir::separator() + fi.filePath;
+ if (fi.isDir) {
+ if (!baseDir.mkpath(fi.filePath))
+ return false;
+ if (!QFile::setPermissions(absPath, fi.permissions))
+ return false;
+ }
+ }
+
+ // set up symlinks
+ foreach (FileInfo fi, allFiles) {
+ const QString absPath = destinationDir + QDir::separator() + fi.filePath;
+ if (fi.isSymLink) {
+ QString destination = QFile::decodeName(fileData(fi.filePath));
+ if (destination.isEmpty())
+ return false;
+ QFileInfo linkFi(absPath);
+ if (!QFile::exists(linkFi.absolutePath()))
+ QDir::root().mkpath(linkFi.absolutePath());
+ if (!QFile::link(destination, absPath))
+ return false;
+ /* cannot change permission of links
+ if (!QFile::setPermissions(absPath, fi.permissions))
+ return false;
+ */
+ }
+ }
+
+ foreach (FileInfo fi, allFiles) {
+ const QString absPath = destinationDir + QDir::separator() + fi.filePath;
+ if (fi.isFile) {
+ QFile f(absPath);
+ if (!f.open(QIODevice::WriteOnly))
+ return false;
+ f.write(fileData(fi.filePath));
+ f.setPermissions(fi.permissions);
+ f.close();
+ }
+ }
+
+ return true;
+}
+
+/*!
+ \enum QZipReader::Status
+
+ The following status values are possible:
+
+ \value NoError No error occurred.
+ \value FileReadError An error occurred when reading from the file.
+ \value FileOpenError The file could not be opened.
+ \value FilePermissionsError The file could not be accessed.
+ \value FileError Another file error occurred.
+*/
+
+/*!
+ Returns a status code indicating the first error that was met by QZipReader,
+ or QZipReader::NoError if no error occurred.
+*/
+QZipReader::Status QZipReader::status() const
+{
+ return d->status;
+}
+
+/*!
+ Close the zip file.
+*/
+void QZipReader::close()
+{
+ d->device->close();
+}
+
+////////////////////////////// Writer
+
+/*!
+ \class QZipWriter
+ \internal
+ \since 4.5
+
+ \brief the QZipWriter class provides a way to create a new zip archive.
+
+ QZipWriter can be used to create a zip archive containing any number of files
+ and directories. The files in the archive will be compressed in a way that is
+ compatible with common zip reader applications.
+*/
+
+
+/*!
+ Create a new zip archive that operates on the \a archive filename. The file will
+ be opened with the \a mode.
+ \sa isValid()
+*/
+QZipWriter::QZipWriter(const QString &fileName, QIODevice::OpenMode mode)
+{
+ QFile *f = new QFile(fileName);
+ f->open(mode);
+ QZipWriter::Status status;
+ if (f->error() == QFile::NoError)
+ status = QZipWriter::NoError;
+ else {
+ if (f->error() == QFile::WriteError)
+ status = QZipWriter::FileWriteError;
+ else if (f->error() == QFile::OpenError)
+ status = QZipWriter::FileOpenError;
+ else if (f->error() == QFile::PermissionsError)
+ status = QZipWriter::FilePermissionsError;
+ else
+ status = QZipWriter::FileError;
+ }
+
+ d = new QZipWriterPrivate(f, /*ownDevice=*/true);
+ d->status = status;
+}
+
+/*!
+ Create a new zip archive that operates on the archive found in \a device.
+ You have to open the device previous to calling the constructor and
+ only a device that is readable will be scanned for zip filecontent.
+ */
+QZipWriter::QZipWriter(QIODevice *device)
+ : d(new QZipWriterPrivate(device, /*ownDevice=*/false))
+{
+ Q_ASSERT(device);
+}
+
+QZipWriter::~QZipWriter()
+{
+ close();
+ delete d;
+}
+
+/*!
+ Returns true if the user can write to the archive; otherwise returns false.
+*/
+bool QZipWriter::isWritable() const
+{
+ return d->device->isWritable();
+}
+
+/*!
+ Returns true if the file exists; otherwise returns false.
+*/
+bool QZipWriter::exists() const
+{
+ QFile *f = qobject_cast<QFile*> (d->device);
+ if (f == 0)
+ return true;
+ return f->exists();
+}
+
+/*!
+ \enum QZipWriter::Status
+
+ The following status values are possible:
+
+ \value NoError No error occurred.
+ \value FileWriteError An error occurred when writing to the device.
+ \value FileOpenError The file could not be opened.
+ \value FilePermissionsError The file could not be accessed.
+ \value FileError Another file error occurred.
+*/
+
+/*!
+ Returns a status code indicating the first error that was met by QZipWriter,
+ or QZipWriter::NoError if no error occurred.
+*/
+QZipWriter::Status QZipWriter::status() const
+{
+ return d->status;
+}
+
+/*!
+ \enum QZipWriter::CompressionPolicy
+
+ \value AlwaysCompress A file that is added is compressed.
+ \value NeverCompress A file that is added will be stored without changes.
+ \value AutoCompress A file that is added will be compressed only if that will give a smaller file.
+*/
+
+/*!
+ Sets the policy for compressing newly added files to the new \a policy.
+
+ \note the default policy is AlwaysCompress
+
+ \sa compressionPolicy()
+ \sa addFile()
+*/
+void QZipWriter::setCompressionPolicy(CompressionPolicy policy)
+{
+ d->compressionPolicy = policy;
+}
+
+/*!
+ Returns the currently set compression policy.
+ \sa setCompressionPolicy()
+ \sa addFile()
+*/
+QZipWriter::CompressionPolicy QZipWriter::compressionPolicy() const
+{
+ return d->compressionPolicy;
+}
+
+/*!
+ Sets the permissions that will be used for newly added files.
+
+ \note the default permissions are QFile::ReadOwner | QFile::WriteOwner.
+
+ \sa creationPermissions()
+ \sa addFile()
+*/
+void QZipWriter::setCreationPermissions(QFile::Permissions permissions)
+{
+ d->permissions = permissions;
+}
+
+/*!
+ Returns the currently set creation permissions.
+
+ \sa setCreationPermissions()
+ \sa addFile()
+*/
+QFile::Permissions QZipWriter::creationPermissions() const
+{
+ return d->permissions;
+}
+
+/*!
+ Add a file to the archive with \a data as the file contents.
+ The file will be stored in the archive using the \a fileName which
+ includes the full path in the archive.
+
+ The new file will get the file permissions based on the current
+ creationPermissions and it will be compressed using the zip compression
+ based on the current compression policy.
+
+ \sa setCreationPermissions()
+ \sa setCompressionPolicy()
+*/
+void QZipWriter::addFile(const QString &fileName, const QByteArray &data)
+{
+ d->addEntry(QZipWriterPrivate::File, fileName, data);
+}
+
+/*!
+ Add a file to the archive with \a device as the source of the contents.
+ The contents returned from QIODevice::readAll() will be used as the
+ filedata.
+ The file will be stored in the archive using the \a fileName which
+ includes the full path in the archive.
+*/
+void QZipWriter::addFile(const QString &fileName, QIODevice *device)
+{
+ Q_ASSERT(device);
+ QIODevice::OpenMode mode = device->openMode();
+ bool opened = false;
+ if ((mode & QIODevice::ReadOnly) == 0) {
+ opened = true;
+ if (! device->open(QIODevice::ReadOnly)) {
+ d->status = FileOpenError;
+ return;
+ }
+ }
+ d->addEntry(QZipWriterPrivate::File, fileName, device->readAll());
+ if (opened)
+ device->close();
+}
+
+/*!
+ Create a new directory in the archive with the specified \a dirName and
+ the \a permissions;
+*/
+void QZipWriter::addDirectory(const QString &dirName)
+{
+ QString name = dirName;
+ // separator is mandatory
+ if (!name.endsWith(QDir::separator()))
+ name.append(QDir::separator());
+ d->addEntry(QZipWriterPrivate::Directory, name, QByteArray());
+}
+
+/*!
+ Create a new symbolic link in the archive with the specified \a dirName
+ and the \a permissions;
+ A symbolic link contains the destination (relative) path and name.
+*/
+void QZipWriter::addSymLink(const QString &fileName, const QString &destination)
+{
+ d->addEntry(QZipWriterPrivate::Symlink, fileName, QFile::encodeName(destination));
+}
+
+/*!
+ Closes the zip file.
+*/
+void QZipWriter::close()
+{
+ if (!(d->device->openMode() & QIODevice::WriteOnly)) {
+ d->device->close();
+ return;
+ }
+
+ //qDebug("QZip::close writing directory, %d entries", d->fileHeaders.size());
+ d->device->seek(d->start_of_directory);
+ // write new directory
+ for (int i = 0; i < d->fileHeaders.size(); ++i) {
+ const FileHeader &header = d->fileHeaders.at(i);
+ d->device->write((const char *)&header.h, sizeof(CentralFileHeader));
+ d->device->write(header.file_name);
+ d->device->write(header.extra_field);
+ d->device->write(header.file_comment);
+ }
+ int dir_size = d->device->pos() - d->start_of_directory;
+ // write end of directory
+ EndOfDirectory eod;
+ memset(&eod, 0, sizeof(EndOfDirectory));
+ writeUInt(eod.signature, 0x06054b50);
+ //uchar this_disk[2];
+ //uchar start_of_directory_disk[2];
+ writeUShort(eod.num_dir_entries_this_disk, d->fileHeaders.size());
+ writeUShort(eod.num_dir_entries, d->fileHeaders.size());
+ writeUInt(eod.directory_size, dir_size);
+ writeUInt(eod.dir_start_offset, d->start_of_directory);
+ writeUShort(eod.comment_length, d->comment.length());
+
+ d->device->write((const char *)&eod, sizeof(EndOfDirectory));
+ d->device->write(d->comment);
+ d->device->close();
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_TEXTODFWRITER
diff --git a/src/gui/text/qzipreader_p.h b/src/gui/text/qzipreader_p.h
new file mode 100644
index 0000000000..c2974a1a09
--- /dev/null
+++ b/src/gui/text/qzipreader_p.h
@@ -0,0 +1,119 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QZIPREADER_H
+#define QZIPREADER_H
+
+#ifndef QT_NO_TEXTODFWRITER
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of the QLibrary class. This header file may change from
+// version to version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtCore/qfile.h>
+#include <QtCore/qstring.h>
+
+QT_BEGIN_NAMESPACE
+
+class QZipReaderPrivate;
+
+class Q_AUTOTEST_EXPORT QZipReader
+{
+public:
+ QZipReader(const QString &fileName, QIODevice::OpenMode mode = QIODevice::ReadOnly );
+
+ explicit QZipReader(QIODevice *device);
+ ~QZipReader();
+
+ bool isReadable() const;
+ bool exists() const;
+
+ struct Q_AUTOTEST_EXPORT FileInfo
+ {
+ FileInfo();
+ FileInfo(const FileInfo &other);
+ ~FileInfo();
+ FileInfo &operator=(const FileInfo &other);
+ QString filePath;
+ uint isDir : 1;
+ uint isFile : 1;
+ uint isSymLink : 1;
+ QFile::Permissions permissions;
+ uint crc32;
+ qint64 size;
+ void *d;
+ };
+
+ QList<FileInfo> fileInfoList() const;
+ int count() const;
+
+ FileInfo entryInfoAt(int index) const;
+ QByteArray fileData(const QString &fileName) const;
+ bool extractAll(const QString &destinationDir) const;
+
+ enum Status {
+ NoError,
+ FileReadError,
+ FileOpenError,
+ FilePermissionsError,
+ FileError
+ };
+
+ Status status() const;
+
+ void close();
+
+private:
+ QZipReaderPrivate *d;
+ Q_DISABLE_COPY(QZipReader)
+};
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_TEXTODFWRITER
+#endif // QZIPREADER_H
diff --git a/src/gui/text/qzipwriter_p.h b/src/gui/text/qzipwriter_p.h
new file mode 100644
index 0000000000..b5072b7286
--- /dev/null
+++ b/src/gui/text/qzipwriter_p.h
@@ -0,0 +1,114 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#ifndef QZIPWRITER_H
+#define QZIPWRITER_H
+#ifndef QT_NO_TEXTODFWRITER
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of the QLibrary class. This header file may change from
+// version to version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtCore/qstring.h>
+#include <QtCore/qfile.h>
+
+QT_BEGIN_NAMESPACE
+
+class QZipWriterPrivate;
+
+
+class Q_AUTOTEST_EXPORT QZipWriter
+{
+public:
+ QZipWriter(const QString &fileName, QIODevice::OpenMode mode = (QIODevice::WriteOnly | QIODevice::Truncate) );
+
+ explicit QZipWriter(QIODevice *device);
+ ~QZipWriter();
+
+ bool isWritable() const;
+ bool exists() const;
+
+ enum Status {
+ NoError,
+ FileWriteError,
+ FileOpenError,
+ FilePermissionsError,
+ FileError
+ };
+
+ Status status() const;
+
+ enum CompressionPolicy {
+ AlwaysCompress,
+ NeverCompress,
+ AutoCompress
+ };
+
+ void setCompressionPolicy(CompressionPolicy policy);
+ CompressionPolicy compressionPolicy() const;
+
+ void setCreationPermissions(QFile::Permissions permissions);
+ QFile::Permissions creationPermissions() const;
+
+ void addFile(const QString &fileName, const QByteArray &data);
+
+ void addFile(const QString &fileName, QIODevice *device);
+
+ void addDirectory(const QString &dirName);
+
+ void addSymLink(const QString &fileName, const QString &destination);
+
+ void close();
+private:
+ QZipWriterPrivate *d;
+ Q_DISABLE_COPY(QZipWriter)
+};
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_TEXTODFWRITER
+#endif // QZIPWRITER_H
diff --git a/src/gui/text/text.pri b/src/gui/text/text.pri
new file mode 100644
index 0000000000..fc33d4358f
--- /dev/null
+++ b/src/gui/text/text.pri
@@ -0,0 +1,177 @@
+# Qt kernel module
+
+HEADERS += \
+ text/qfont.h \
+ text/qfontdatabase.h \
+ text/qfontengine_p.h \
+ text/qfontengineglyphcache_p.h \
+ text/qfontinfo.h \
+ text/qfontmetrics.h \
+ text/qfont_p.h \
+ text/qfontsubset_p.h \
+ text/qtextcontrol_p.h \
+ text/qtextcontrol_p_p.h \
+ text/qtextengine_p.h \
+ text/qtextlayout.h \
+ text/qtextformat.h \
+ text/qtextformat_p.h \
+ text/qtextobject.h \
+ text/qtextobject_p.h \
+ text/qtextoption.h \
+ text/qfragmentmap_p.h \
+ text/qtextdocument.h \
+ text/qtextdocument_p.h \
+ text/qtexthtmlparser_p.h \
+ text/qabstracttextdocumentlayout.h \
+ text/qtextdocumentlayout_p.h \
+ text/qtextcursor.h \
+ text/qtextcursor_p.h \
+ text/qtextdocumentfragment.h \
+ text/qtextdocumentfragment_p.h \
+ text/qtextimagehandler_p.h \
+ text/qtexttable.h \
+ text/qtextlist.h \
+ text/qsyntaxhighlighter.h \
+ text/qtextdocumentwriter.h \
+ text/qcssparser_p.h \
+ text/qtexttable_p.h \
+ text/qzipreader_p.h \
+ text/qzipwriter_p.h \
+ text/qtextodfwriter_p.h
+
+SOURCES += \
+ text/qfont.cpp \
+ text/qfontengine.cpp \
+ text/qfontsubset.cpp \
+ text/qfontmetrics.cpp \
+ text/qfontdatabase.cpp \
+ text/qtextcontrol.cpp \
+ text/qtextengine.cpp \
+ text/qtextlayout.cpp \
+ text/qtextformat.cpp \
+ text/qtextobject.cpp \
+ text/qtextoption.cpp \
+ text/qfragmentmap.cpp \
+ text/qtextdocument.cpp \
+ text/qtextdocument_p.cpp \
+ text/qtexthtmlparser.cpp \
+ text/qabstracttextdocumentlayout.cpp \
+ text/qtextdocumentlayout.cpp \
+ text/qtextcursor.cpp \
+ text/qtextdocumentfragment.cpp \
+ text/qtextimagehandler.cpp \
+ text/qtexttable.cpp \
+ text/qtextlist.cpp \
+ text/qtextdocumentwriter.cpp \
+ text/qsyntaxhighlighter.cpp \
+ text/qcssparser.cpp \
+ text/qzip.cpp \
+ text/qtextodfwriter.cpp
+
+win32 {
+ SOURCES += \
+ text/qfont_win.cpp \
+ text/qfontengine_win.cpp
+ HEADERS += text/qfontengine_win_p.h
+}
+
+unix:x11 {
+ HEADERS += \
+ text/qfontengine_x11_p.h \
+ text/qfontengine_ft_p.h
+ SOURCES += \
+ text/qfont_x11.cpp \
+ text/qfontengine_x11.cpp \
+ text/qfontengine_ft.cpp
+}
+
+!embedded:!x11:mac {
+ SOURCES += \
+ text/qfont_mac.cpp
+ OBJECTIVE_SOURCES += text/qfontengine_mac.mm
+}
+
+embedded {
+ SOURCES += \
+ text/qfont_qws.cpp \
+ text/qfontengine_qws.cpp \
+ text/qfontengine_ft.cpp \
+ text/qfontengine_qpf.cpp \
+ text/qabstractfontengine_qws.cpp
+ HEADERS += \
+ text/qfontengine_ft_p.h \
+ text/qfontengine_qpf_p.h \
+ text/qabstractfontengine_qws.h \
+ text/qabstractfontengine_p.h
+ DEFINES += QT_NO_FONTCONFIG
+}
+
+contains(QT_CONFIG, freetype) {
+ SOURCES += \
+ ../3rdparty/freetype/builds/unix/ftsystem.c \
+ ../3rdparty/freetype/src/base/ftbase.c \
+ ../3rdparty/freetype/src/base/ftbbox.c \
+ ../3rdparty/freetype/src/base/ftdebug.c \
+ ../3rdparty/freetype/src/base/ftglyph.c \
+ ../3rdparty/freetype/src/base/ftinit.c \
+ ../3rdparty/freetype/src/base/ftmm.c \
+ ../3rdparty/freetype/src/base/fttype1.c \
+ ../3rdparty/freetype/src/base/ftbitmap.c\
+ ../3rdparty/freetype/src/bdf/bdf.c \
+ ../3rdparty/freetype/src/cache/ftcache.c \
+ ../3rdparty/freetype/src/cff/cff.c \
+ ../3rdparty/freetype/src/cid/type1cid.c \
+ ../3rdparty/freetype/src/gzip/ftgzip.c \
+ ../3rdparty/freetype/src/pcf/pcf.c \
+ ../3rdparty/freetype/src/pfr/pfr.c \
+ ../3rdparty/freetype/src/psaux/psaux.c \
+ ../3rdparty/freetype/src/pshinter/pshinter.c \
+ ../3rdparty/freetype/src/psnames/psmodule.c \
+ ../3rdparty/freetype/src/raster/raster.c \
+ ../3rdparty/freetype/src/sfnt/sfnt.c \
+ ../3rdparty/freetype/src/smooth/smooth.c \
+ ../3rdparty/freetype/src/truetype/truetype.c \
+ ../3rdparty/freetype/src/type1/type1.c \
+ ../3rdparty/freetype/src/type42/type42.c \
+ ../3rdparty/freetype/src/winfonts/winfnt.c \
+ ../3rdparty/freetype/src/lzw/ftlzw.c\
+ ../3rdparty/freetype/src/otvalid/otvalid.c\
+ ../3rdparty/freetype/src/otvalid/otvbase.c\
+ ../3rdparty/freetype/src/otvalid/otvgdef.c\
+ ../3rdparty/freetype/src/otvalid/otvjstf.c\
+ ../3rdparty/freetype/src/otvalid/otvcommn.c\
+ ../3rdparty/freetype/src/otvalid/otvgpos.c\
+ ../3rdparty/freetype/src/otvalid/otvgsub.c\
+ ../3rdparty/freetype/src/otvalid/otvmod.c\
+ ../3rdparty/freetype/src/autofit/afangles.c\
+ ../3rdparty/freetype/src/autofit/afglobal.c\
+ ../3rdparty/freetype/src/autofit/aflatin.c\
+ ../3rdparty/freetype/src/autofit/afmodule.c\
+ ../3rdparty/freetype/src/autofit/afdummy.c\
+ ../3rdparty/freetype/src/autofit/afhints.c\
+ ../3rdparty/freetype/src/autofit/afloader.c\
+ ../3rdparty/freetype/src/autofit/autofit.c
+
+ INCLUDEPATH += \
+ ../3rdparty/freetype/src \
+ ../3rdparty/freetype/include \
+ ../3rdparty/freetype/builds/unix
+
+ DEFINES += FT2_BUILD_LIBRARY FT_CONFIG_OPTION_SYSTEM_ZLIB
+
+ embedded:CONFIG += opentype
+} else:contains(QT_CONFIG, system-freetype) {
+ embedded:CONFIG += opentype
+ # pull in the proper freetype2 include directory
+ include($$QT_SOURCE_TREE/config.tests/unix/freetype/freetype.pri)
+ LIBS += -lfreetype
+} else {
+ DEFINES *= QT_NO_FREETYPE
+}
+
+contains(QT_CONFIG, fontconfig) {
+ CONFIG += opentype
+}
+
+DEFINES += QT_NO_OPENTYPE
+INCLUDEPATH += ../3rdparty/harfbuzz/src
diff --git a/src/gui/util/qcompleter.cpp b/src/gui/util/qcompleter.cpp
new file mode 100644
index 0000000000..8b071634da
--- /dev/null
+++ b/src/gui/util/qcompleter.cpp
@@ -0,0 +1,1712 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*!
+ \class QCompleter
+ \brief The QCompleter class provides completions based on an item model.
+ \since 4.2
+
+ You can use QCompleter to provide auto completions in any Qt
+ widget, such as QLineEdit and QComboBox.
+ When the user starts typing a word, QCompleter suggests possible ways of
+ completing the word, based on a word list. The word list is
+ provided as a QAbstractItemModel. (For simple applications, where
+ the word list is static, you can pass a QStringList to
+ QCompleter's constructor.)
+
+ \tableofcontents
+
+ \section1 Basic Usage
+
+ A QCompleter is used typically with a QLineEdit or QComboBox.
+ For example, here's how to provide auto completions from a simple
+ word list in a QLineEdit:
+
+ \snippet doc/src/snippets/code/src_gui_util_qcompleter.cpp 0
+
+ A QDirModel can be used to provide auto completion of file names.
+ For example:
+
+ \snippet doc/src/snippets/code/src_gui_util_qcompleter.cpp 1
+
+ To set the model on which QCompleter should operate, call
+ setModel(). By default, QCompleter will attempt to match the \l
+ {completionPrefix}{completion prefix} (i.e., the word that the
+ user has started typing) against the Qt::EditRole data stored in
+ column 0 in the model case sensitively. This can be changed
+ using setCompletionRole(), setCompletionColumn(), and
+ setCaseSensitivity().
+
+ If the model is sorted on the column and role that are used for completion,
+ you can call setModelSorting() with either
+ QCompleter::CaseSensitivelySortedModel or
+ QCompleter::CaseInsensitivelySortedModel as the argument. On large models,
+ this can lead to significant performance improvements, because QCompleter
+ can then use binary search instead of linear search.
+
+ The model can be a \l{QAbstractListModel}{list model},
+ a \l{QAbstractTableModel}{table model}, or a
+ \l{QAbstractItemModel}{tree model}. Completion on tree models
+ is slightly more involved and is covered in the \l{Handling
+ Tree Models} section below.
+
+ The completionMode() determines the mode used to provide completions to
+ the user.
+
+ \section1 Iterating Through Completions
+
+ To retrieve a single candidate string, call setCompletionPrefix()
+ with the text that needs to be completed and call
+ currentCompletion(). You can iterate through the list of
+ completions as below:
+
+ \snippet doc/src/snippets/code/src_gui_util_qcompleter.cpp 2
+
+ completionCount() returns the total number of completions for the
+ current prefix. completionCount() should be avoided when possible,
+ since it requires a scan of the entire model.
+
+ \section1 The Completion Model
+
+ completionModel() return a list model that contains all possible
+ completions for the current completion prefix, in the order in which
+ they appear in the model. This model can be used to display the current
+ completions in a custom view. Calling setCompletionPrefix() automatically
+ refreshes the completion model.
+
+ \section1 Handling Tree Models
+
+ QCompleter can look for completions in tree models, assuming
+ that any item (or sub-item or sub-sub-item) can be unambiguously
+ represented as a string by specifying the path to the item. The
+ completion is then performed one level at a time.
+
+ Let's take the example of a user typing in a file system path.
+ The model is a (hierarchical) QDirModel. The completion
+ occurs for every element in the path. For example, if the current
+ text is \c C:\Wind, QCompleter might suggest \c Windows to
+ complete the current path element. Similarly, if the current text
+ is \c C:\Windows\Sy, QCompleter might suggest \c System.
+
+ For this kind of completion to work, QCompleter needs to be able to
+ split the path into a list of strings that are matched at each level.
+ For \c C:\Windows\Sy, it needs to be split as "C:", "Windows" and "Sy".
+ The default implementation of splitPath(), splits the completionPrefix
+ using QDir::separator() if the model is a QDirModel.
+
+ To provide completions, QCompleter needs to know the path from an index.
+ This is provided by pathFromIndex(). The default implementation of
+ pathFromIndex(), returns the data for the \l{Qt::EditRole}{edit role}
+ for list models and the absolute file path if the mode is a QDirModel.
+
+ \sa QAbstractItemModel, QLineEdit, QComboBox, {Completer Example}
+*/
+
+#include "qcompleter_p.h"
+
+#ifndef QT_NO_COMPLETER
+
+#include "QtGui/qscrollbar.h"
+#include "QtGui/qstringlistmodel.h"
+#include "QtGui/qdirmodel.h"
+#include "QtGui/qheaderview.h"
+#include "QtGui/qlistview.h"
+#include "QtGui/qapplication.h"
+#include "QtGui/qevent.h"
+#include "QtGui/qheaderview.h"
+#include "QtGui/qdesktopwidget.h"
+
+QT_BEGIN_NAMESPACE
+
+QCompletionModel::QCompletionModel(QCompleterPrivate *c, QObject *parent)
+ : QAbstractProxyModel(*new QCompletionModelPrivate, parent),
+ c(c), engine(0), showAll(false)
+{
+ createEngine();
+}
+
+int QCompletionModel::columnCount(const QModelIndex &) const
+{
+ Q_D(const QCompletionModel);
+ return d->model->columnCount();
+}
+
+void QCompletionModel::setSourceModel(QAbstractItemModel *source)
+{
+ bool hadModel = (sourceModel() != 0);
+
+ if (hadModel)
+ QObject::disconnect(sourceModel(), 0, this, 0);
+
+ QAbstractProxyModel::setSourceModel(source);
+
+ if (source) {
+ // TODO: Optimize updates in the source model
+ connect(source, SIGNAL(modelReset()), this, SLOT(invalidate()));
+ connect(source, SIGNAL(destroyed()), this, SLOT(modelDestroyed()));
+ connect(source, SIGNAL(layoutChanged()), this, SLOT(invalidate()));
+ connect(source, SIGNAL(rowsInserted(QModelIndex,int,int)), this, SLOT(rowsInserted()));
+ connect(source, SIGNAL(rowsRemoved(QModelIndex,int,int)), this, SLOT(invalidate()));
+ connect(source, SIGNAL(columnsInserted(QModelIndex,int,int)), this, SLOT(invalidate()));
+ connect(source, SIGNAL(columnsRemoved(QModelIndex,int,int)), this, SLOT(invalidate()));
+ connect(source, SIGNAL(dataChanged(QModelIndex,QModelIndex)), this, SLOT(invalidate()));
+ }
+
+ invalidate();
+}
+
+void QCompletionModel::createEngine()
+{
+ bool sortedEngine = false;
+ switch (c->sorting) {
+ case QCompleter::UnsortedModel:
+ sortedEngine = false;
+ break;
+ case QCompleter::CaseSensitivelySortedModel:
+ sortedEngine = c->cs == Qt::CaseSensitive;
+ break;
+ case QCompleter::CaseInsensitivelySortedModel:
+ sortedEngine = c->cs == Qt::CaseInsensitive;
+ break;
+ }
+
+ delete engine;
+ if (sortedEngine)
+ engine = new QSortedModelEngine(c);
+ else
+ engine = new QUnsortedModelEngine(c);
+}
+
+QModelIndex QCompletionModel::mapToSource(const QModelIndex& index) const
+{
+ Q_D(const QCompletionModel);
+ if (!index.isValid())
+ return QModelIndex();
+
+ int row;
+ QModelIndex parent = engine->curParent;
+ if (!showAll) {
+ if (!engine->matchCount())
+ return QModelIndex();
+ Q_ASSERT(index.row() < engine->matchCount());
+ QIndexMapper& rootIndices = engine->historyMatch.indices;
+ if (index.row() < rootIndices.count()) {
+ row = rootIndices[index.row()];
+ parent = QModelIndex();
+ } else {
+ row = engine->curMatch.indices[index.row() - rootIndices.count()];
+ }
+ } else {
+ row = index.row();
+ }
+
+ return d->model->index(row, index.column(), parent);
+}
+
+QModelIndex QCompletionModel::mapFromSource(const QModelIndex& idx) const
+{
+ if (!idx.isValid())
+ return QModelIndex();
+
+ int row = -1;
+ if (!showAll) {
+ if (!engine->matchCount())
+ return QModelIndex();
+
+ QIndexMapper& rootIndices = engine->historyMatch.indices;
+ if (idx.parent().isValid()) {
+ if (idx.parent() != engine->curParent)
+ return QModelIndex();
+ } else {
+ row = rootIndices.indexOf(idx.row());
+ if (row == -1 && engine->curParent.isValid())
+ return QModelIndex(); // source parent and our parent don't match
+ }
+
+ if (row == -1) {
+ QIndexMapper& indices = engine->curMatch.indices;
+ engine->filterOnDemand(idx.row() - indices.last());
+ row = indices.indexOf(idx.row()) + rootIndices.count();
+ }
+
+ if (row == -1)
+ return QModelIndex();
+ } else {
+ if (idx.parent() != engine->curParent)
+ return QModelIndex();
+ row = idx.row();
+ }
+
+ return createIndex(row, idx.column());
+}
+
+bool QCompletionModel::setCurrentRow(int row)
+{
+ if (row < 0 || !engine->matchCount())
+ return false;
+
+ if (row >= engine->matchCount())
+ engine->filterOnDemand(row + 1 - engine->matchCount());
+
+ if (row >= engine->matchCount()) // invalid row
+ return false;
+
+ engine->curRow = row;
+ return true;
+}
+
+QModelIndex QCompletionModel::currentIndex(bool sourceIndex) const
+{
+ if (!engine->matchCount())
+ return QModelIndex();
+
+ int row = engine->curRow;
+ if (showAll)
+ row = engine->curMatch.indices[engine->curRow];
+
+ QModelIndex idx = createIndex(row, c->column);
+ if (!sourceIndex)
+ return idx;
+ return mapToSource(idx);
+}
+
+QModelIndex QCompletionModel::index(int row, int column, const QModelIndex& parent) const
+{
+ Q_D(const QCompletionModel);
+ if (row < 0 || column < 0 || column >= columnCount(parent) || parent.isValid())
+ return QModelIndex();
+
+ if (!showAll) {
+ if (!engine->matchCount())
+ return QModelIndex();
+ if (row >= engine->historyMatch.indices.count()) {
+ int want = row + 1 - engine->matchCount();
+ if (want > 0)
+ engine->filterOnDemand(want);
+ if (row >= engine->matchCount())
+ return QModelIndex();
+ }
+ } else {
+ if (row >= d->model->rowCount(engine->curParent))
+ return QModelIndex();
+ }
+
+ return createIndex(row, column);
+}
+
+int QCompletionModel::completionCount() const
+{
+ if (!engine->matchCount())
+ return 0;
+
+ engine->filterOnDemand(INT_MAX);
+ return engine->matchCount();
+}
+
+int QCompletionModel::rowCount(const QModelIndex &parent) const
+{
+ Q_D(const QCompletionModel);
+ if (parent.isValid())
+ return 0;
+
+ if (showAll) {
+ // Show all items below current parent, even if we have no valid matches
+ if (engine->curParts.count() != 1 && !engine->matchCount()
+ && !engine->curParent.isValid())
+ return 0;
+ return d->model->rowCount(engine->curParent);
+ }
+
+ return completionCount();
+}
+
+void QCompletionModel::setFiltered(bool filtered)
+{
+ if (showAll == !filtered)
+ return;
+ showAll = !filtered;
+ resetModel();
+}
+
+bool QCompletionModel::hasChildren(const QModelIndex &parent) const
+{
+ Q_D(const QCompletionModel);
+ if (parent.isValid())
+ return false;
+
+ if (showAll)
+ return d->model->hasChildren(mapToSource(parent));
+
+ if (!engine->matchCount())
+ return false;
+
+ return true;
+}
+
+QVariant QCompletionModel::data(const QModelIndex& index, int role) const
+{
+ Q_D(const QCompletionModel);
+ return d->model->data(mapToSource(index), role);
+}
+
+void QCompletionModel::modelDestroyed()
+{
+ QAbstractProxyModel::setSourceModel(0); // switch to static empty model
+ invalidate();
+}
+
+void QCompletionModel::rowsInserted()
+{
+ invalidate();
+ emit rowsAdded();
+}
+
+void QCompletionModel::invalidate()
+{
+ engine->cache.clear();
+ filter(engine->curParts);
+}
+
+void QCompletionModel::filter(const QStringList& parts)
+{
+ Q_D(QCompletionModel);
+ engine->filter(parts);
+ resetModel();
+
+ if (d->model->canFetchMore(engine->curParent))
+ d->model->fetchMore(engine->curParent);
+}
+
+void QCompletionModel::resetModel()
+{
+ if (rowCount() == 0) {
+ reset();
+ return;
+ }
+
+ emit layoutAboutToBeChanged();
+ QModelIndexList piList = persistentIndexList();
+ QModelIndexList empty;
+ for (int i = 0; i < piList.size(); i++)
+ empty.append(QModelIndex());
+ changePersistentIndexList(piList, empty);
+ emit layoutChanged();
+}
+
+//////////////////////////////////////////////////////////////////////////////
+void QCompletionEngine::filter(const QStringList& parts)
+{
+ const QAbstractItemModel *model = c->proxy->sourceModel();
+ curParts = parts;
+ if (curParts.isEmpty())
+ curParts.append(QString());
+
+ curRow = -1;
+ curParent = QModelIndex();
+ curMatch = QMatchData();
+ historyMatch = filterHistory();
+
+ if (!model)
+ return;
+
+ QModelIndex parent;
+ for (int i = 0; i < curParts.count() - 1; i++) {
+ QString part = curParts[i];
+ int emi = filter(part, parent, -1).exactMatchIndex;
+ if (emi == -1)
+ return;
+ parent = model->index(emi, c->column, parent);
+ }
+
+ // Note that we set the curParent to a valid parent, even if we have no matches
+ // When filtering is disabled, we show all the items under this parent
+ curParent = parent;
+ if (curParts.last().isEmpty())
+ curMatch = QMatchData(QIndexMapper(0, model->rowCount(curParent) - 1), -1, false);
+ else
+ curMatch = filter(curParts.last(), curParent, 1); // build at least one
+ curRow = curMatch.isValid() ? 0 : -1;
+}
+
+QMatchData QCompletionEngine::filterHistory()
+{
+ QAbstractItemModel *source = c->proxy->sourceModel();
+ if (curParts.count() <= 1 || c->proxy->showAll || !source)
+ return QMatchData();
+ bool dirModel = false;
+#ifndef QT_NO_DIRMODEL
+ dirModel = (qobject_cast<QDirModel *>(source) != 0);
+#endif
+ QVector<int> v;
+ QIndexMapper im(v);
+ QMatchData m(im, -1, true);
+
+ for (int i = 0; i < source->rowCount(); i++) {
+ QString str = source->index(i, c->column).data().toString();
+ if (str.startsWith(c->prefix, c->cs)
+#if !defined(Q_OS_WIN) || defined(Q_OS_WINCE)
+ && (!dirModel || QDir::toNativeSeparators(str) != QDir::separator())
+#endif
+ )
+ m.indices.append(i);
+ }
+ return m;
+}
+
+// Returns a match hint from the cache by chopping the search string
+bool QCompletionEngine::matchHint(QString part, const QModelIndex& parent, QMatchData *hint)
+{
+ if (c->cs == Qt::CaseInsensitive)
+ part = part.toLower();
+
+ const CacheItem& map = cache[parent];
+
+ QString key = part;
+ while (!key.isEmpty()) {
+ key.chop(1);
+ if (map.contains(key)) {
+ *hint = map[key];
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool QCompletionEngine::lookupCache(QString part, const QModelIndex& parent, QMatchData *m)
+{
+ if (c->cs == Qt::CaseInsensitive)
+ part = part.toLower();
+ const CacheItem& map = cache[parent];
+ if (!map.contains(part))
+ return false;
+ *m = map[part];
+ return true;
+}
+
+// When the cache size exceeds 1MB, it clears out about 1/2 of the cache.
+void QCompletionEngine::saveInCache(QString part, const QModelIndex& parent, const QMatchData& m)
+{
+ QMatchData old = cache[parent].take(part);
+ cost = cost + m.indices.cost() - old.indices.cost();
+ if (cost * sizeof(int) > 1024 * 1024) {
+ QMap<QModelIndex, CacheItem>::iterator it1 ;
+ for (it1 = cache.begin(); it1 != cache.end(); ++it1) {
+ CacheItem& ci = it1.value();
+ int sz = ci.count()/2;
+ QMap<QString, QMatchData>::iterator it2 = ci.begin();
+ for (int i = 0; it2 != ci.end() && i < sz; i++, ++it2) {
+ cost -= it2.value().indices.cost();
+ ci.erase(it2);
+ }
+ if (ci.count() == 0)
+ cache.erase(it1);
+ }
+ }
+
+ if (c->cs == Qt::CaseInsensitive)
+ part = part.toLower();
+ cache[parent][part] = m;
+}
+
+///////////////////////////////////////////////////////////////////////////////////
+QIndexMapper QSortedModelEngine::indexHint(QString part, const QModelIndex& parent, Qt::SortOrder order)
+{
+ const QAbstractItemModel *model = c->proxy->sourceModel();
+
+ if (c->cs == Qt::CaseInsensitive)
+ part = part.toLower();
+
+ const CacheItem& map = cache[parent];
+
+ // Try to find a lower and upper bound for the search from previous results
+ int to = model->rowCount(parent) - 1;
+ int from = 0;
+ const CacheItem::const_iterator it = map.lowerBound(part);
+
+ // look backward for first valid hint
+ for(CacheItem::const_iterator it1 = it; it1-- != map.constBegin();) {
+ const QMatchData& value = it1.value();
+ if (value.isValid()) {
+ if (order == Qt::AscendingOrder) {
+ from = value.indices.last() + 1;
+ } else {
+ to = value.indices.first() - 1;
+ }
+ break;
+ }
+ }
+
+ // look forward for first valid hint
+ for(CacheItem::const_iterator it2 = it; it2 != map.constEnd(); ++it2) {
+ const QMatchData& value = it2.value();
+ if (value.isValid() && !it2.key().startsWith(part)) {
+ if (order == Qt::AscendingOrder) {
+ to = value.indices.first() - 1;
+ } else {
+ from = value.indices.first() + 1;
+ }
+ break;
+ }
+ }
+
+ return QIndexMapper(from, to);
+}
+
+Qt::SortOrder QSortedModelEngine::sortOrder(const QModelIndex &parent) const
+{
+ const QAbstractItemModel *model = c->proxy->sourceModel();
+
+ int rowCount = model->rowCount(parent);
+ if (rowCount < 2)
+ return Qt::AscendingOrder;
+ QString first = model->data(model->index(0, c->column, parent), c->role).toString();
+ QString last = model->data(model->index(rowCount - 1, c->column, parent), c->role).toString();
+ return QString::compare(first, last, c->cs) <= 0 ? Qt::AscendingOrder : Qt::DescendingOrder;
+}
+
+QMatchData QSortedModelEngine::filter(const QString& part, const QModelIndex& parent, int)
+{
+ const QAbstractItemModel *model = c->proxy->sourceModel();
+
+ QMatchData hint;
+ if (lookupCache(part, parent, &hint))
+ return hint;
+
+ QIndexMapper indices;
+ Qt::SortOrder order = sortOrder(parent);
+
+ if (matchHint(part, parent, &hint)) {
+ if (!hint.isValid())
+ return QMatchData();
+ indices = hint.indices;
+ } else {
+ indices = indexHint(part, parent, order);
+ }
+
+ // binary search the model within 'indices' for 'part' under 'parent'
+ int high = indices.to() + 1;
+ int low = indices.from() - 1;
+ int probe;
+ QModelIndex probeIndex;
+ QString probeData;
+
+ while (high - low > 1)
+ {
+ probe = (high + low) / 2;
+ probeIndex = model->index(probe, c->column, parent);
+ probeData = model->data(probeIndex, c->role).toString();
+ const int cmp = QString::compare(probeData, part, c->cs);
+ if ((order == Qt::AscendingOrder && cmp >= 0)
+ || (order == Qt::DescendingOrder && cmp < 0)) {
+ high = probe;
+ } else {
+ low = probe;
+ }
+ }
+
+ if ((order == Qt::AscendingOrder && low == indices.to())
+ || (order == Qt::DescendingOrder && high == indices.from())) { // not found
+ saveInCache(part, parent, QMatchData());
+ return QMatchData();
+ }
+
+ probeIndex = model->index(order == Qt::AscendingOrder ? low+1 : high-1, c->column, parent);
+ probeData = model->data(probeIndex, c->role).toString();
+ if (!probeData.startsWith(part, c->cs)) {
+ saveInCache(part, parent, QMatchData());
+ return QMatchData();
+ }
+
+ const bool exactMatch = QString::compare(probeData, part, c->cs) == 0;
+ int emi = exactMatch ? (order == Qt::AscendingOrder ? low+1 : high-1) : -1;
+
+ int from = 0;
+ int to = 0;
+ if (order == Qt::AscendingOrder) {
+ from = low + 1;
+ high = indices.to() + 1;
+ low = from;
+ } else {
+ to = high - 1;
+ low = indices.from() - 1;
+ high = to;
+ }
+
+ while (high - low > 1)
+ {
+ probe = (high + low) / 2;
+ probeIndex = model->index(probe, c->column, parent);
+ probeData = model->data(probeIndex, c->role).toString();
+ const bool startsWith = probeData.startsWith(part, c->cs);
+ if ((order == Qt::AscendingOrder && startsWith)
+ || (order == Qt::DescendingOrder && !startsWith)) {
+ low = probe;
+ } else {
+ high = probe;
+ }
+ }
+
+ QMatchData m(order == Qt::AscendingOrder ? QIndexMapper(from, high - 1) : QIndexMapper(low+1, to), emi, false);
+ saveInCache(part, parent, m);
+ return m;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+int QUnsortedModelEngine::buildIndices(const QString& str, const QModelIndex& parent, int n,
+ const QIndexMapper& indices, QMatchData* m)
+{
+ Q_ASSERT(m->partial);
+ Q_ASSERT(n != -1 || m->exactMatchIndex == -1);
+ const QAbstractItemModel *model = c->proxy->sourceModel();
+ int i, count = 0;
+
+ for (i = 0; i < indices.count() && count != n; ++i) {
+ QModelIndex idx = model->index(indices[i], c->column, parent);
+ QString data = model->data(idx, c->role).toString();
+ if (!data.startsWith(str, c->cs) || !(model->flags(idx) & Qt::ItemIsSelectable))
+ continue;
+ m->indices.append(indices[i]);
+ ++count;
+ if (m->exactMatchIndex == -1 && QString::compare(data, str, c->cs) == 0) {
+ m->exactMatchIndex = indices[i];
+ if (n == -1)
+ return indices[i];
+ }
+ }
+ return indices[i-1];
+}
+
+void QUnsortedModelEngine::filterOnDemand(int n)
+{
+ Q_ASSERT(matchCount());
+ if (!curMatch.partial)
+ return;
+ Q_ASSERT(n >= -1);
+ const QAbstractItemModel *model = c->proxy->sourceModel();
+ int lastRow = model->rowCount(curParent) - 1;
+ QIndexMapper im(curMatch.indices.last() + 1, lastRow);
+ int lastIndex = buildIndices(curParts.last(), curParent, n, im, &curMatch);
+ curMatch.partial = (lastRow != lastIndex);
+ saveInCache(curParts.last(), curParent, curMatch);
+}
+
+QMatchData QUnsortedModelEngine::filter(const QString& part, const QModelIndex& parent, int n)
+{
+ QMatchData hint;
+
+ QVector<int> v;
+ QIndexMapper im(v);
+ QMatchData m(im, -1, true);
+
+ const QAbstractItemModel *model = c->proxy->sourceModel();
+ bool foundInCache = lookupCache(part, parent, &m);
+
+ if (!foundInCache) {
+ if (matchHint(part, parent, &hint) && !hint.isValid())
+ return QMatchData();
+ }
+
+ if (!foundInCache && !hint.isValid()) {
+ const int lastRow = model->rowCount(parent) - 1;
+ QIndexMapper all(0, lastRow);
+ int lastIndex = buildIndices(part, parent, n, all, &m);
+ m.partial = (lastIndex != lastRow);
+ } else {
+ if (!foundInCache) { // build from hint as much as we can
+ buildIndices(part, parent, INT_MAX, hint.indices, &m);
+ m.partial = hint.partial;
+ }
+ if (m.partial && ((n == -1 && m.exactMatchIndex == -1) || (m.indices.count() < n))) {
+ // need more and have more
+ const int lastRow = model->rowCount(parent) - 1;
+ QIndexMapper rest(hint.indices.last() + 1, lastRow);
+ int want = n == -1 ? -1 : n - m.indices.count();
+ int lastIndex = buildIndices(part, parent, want, rest, &m);
+ m.partial = (lastRow != lastIndex);
+ }
+ }
+
+ saveInCache(part, parent, m);
+ return m;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+QCompleterPrivate::QCompleterPrivate()
+: widget(0), proxy(0), popup(0), cs(Qt::CaseSensitive), role(Qt::EditRole), column(0),
+ sorting(QCompleter::UnsortedModel), wrap(true), eatFocusOut(true)
+{
+}
+
+void QCompleterPrivate::init(QAbstractItemModel *m)
+{
+ Q_Q(QCompleter);
+ proxy = new QCompletionModel(this, q);
+ QObject::connect(proxy, SIGNAL(rowsAdded()), q, SLOT(_q_autoResizePopup()));
+ q->setModel(m);
+#ifdef QT_NO_LISTVIEW
+ q->setCompletionMode(QCompleter::InlineCompletion);
+#else
+ q->setCompletionMode(QCompleter::PopupCompletion);
+#endif // QT_NO_LISTVIEW
+}
+
+void QCompleterPrivate::setCurrentIndex(QModelIndex index, bool select)
+{
+ Q_Q(QCompleter);
+ if (!q->popup())
+ return;
+ if (!select) {
+ popup->selectionModel()->setCurrentIndex(index, QItemSelectionModel::NoUpdate);
+ } else {
+ if (!index.isValid())
+ popup->selectionModel()->clear();
+ else
+ popup->selectionModel()->setCurrentIndex(index, QItemSelectionModel::Select
+ | QItemSelectionModel::Rows);
+ }
+ index = popup->selectionModel()->currentIndex();
+ if (!index.isValid())
+ popup->scrollToTop();
+ else
+ popup->scrollTo(index, QAbstractItemView::PositionAtTop);
+}
+
+void QCompleterPrivate::_q_completionSelected(const QItemSelection& selection)
+{
+ QModelIndex index;
+ if (!selection.indexes().isEmpty())
+ index = selection.indexes().first();
+
+ _q_complete(index, true);
+}
+
+void QCompleterPrivate::_q_complete(QModelIndex index, bool highlighted)
+{
+ Q_Q(QCompleter);
+ QString completion;
+
+ if (!index.isValid())
+ completion = prefix;
+ else {
+ QModelIndex si = proxy->mapToSource(index);
+ si = si.sibling(si.row(), column); // for clicked()
+ completion = q->pathFromIndex(si);
+#ifndef QT_NO_DIRMODEL
+ // add a trailing separator in inline
+ if (mode == QCompleter::InlineCompletion) {
+ if (qobject_cast<QDirModel *>(proxy->sourceModel()) && QFileInfo(completion).isDir())
+ completion += QDir::separator();
+ }
+#endif
+ }
+
+ if (highlighted) {
+ emit q->highlighted(index);
+ emit q->highlighted(completion);
+ } else {
+ emit q->activated(index);
+ emit q->activated(completion);
+ }
+}
+
+void QCompleterPrivate::_q_autoResizePopup()
+{
+ if (!popup || !popup->isVisible())
+ return;
+ showPopup(popupRect);
+}
+
+void QCompleterPrivate::showPopup(const QRect& rect)
+{
+ const QRect screen = QApplication::desktop()->availableGeometry(widget);
+ Qt::LayoutDirection dir = widget->layoutDirection();
+ QPoint pos;
+ int rw, rh, w;
+ int h = (popup->sizeHintForRow(0) * qMin(7, popup->model()->rowCount()) + 3) + 3;
+ QScrollBar *hsb = popup->horizontalScrollBar();
+ if (hsb && hsb->isVisible())
+ h += popup->horizontalScrollBar()->sizeHint().height();
+
+ if (rect.isValid()) {
+ rh = rect.height();
+ w = rw = rect.width();
+ pos = widget->mapToGlobal(dir == Qt::RightToLeft ? rect.bottomRight() : rect.bottomLeft());
+ } else {
+ rh = widget->height();
+ rw = widget->width();
+ pos = widget->mapToGlobal(QPoint(0, widget->height() - 2));
+ w = widget->width();
+ }
+
+ if ((pos.x() + rw) > (screen.x() + screen.width()))
+ pos.setX(screen.x() + screen.width() - w);
+ if (pos.x() < screen.x())
+ pos.setX(screen.x());
+ if (((pos.y() + rh) > (screen.y() + screen.height())) && ((pos.y() - h - rh) >= 0))
+ pos.setY(pos.y() - qMax(h, popup->minimumHeight()) - rh + 2);
+
+ popup->setGeometry(pos.x(), pos.y(), w, h);
+
+ if (!popup->isVisible())
+ popup->show();
+}
+
+/*!
+ Constructs a completer object with the given \a parent.
+*/
+QCompleter::QCompleter(QObject *parent)
+: QObject(*new QCompleterPrivate(), parent)
+{
+ Q_D(QCompleter);
+ d->init();
+}
+
+/*!
+ Constructs a completer object with the given \a parent that provides completions
+ from the specified \a model.
+*/
+QCompleter::QCompleter(QAbstractItemModel *model, QObject *parent)
+ : QObject(*new QCompleterPrivate(), parent)
+{
+ Q_D(QCompleter);
+ d->init(model);
+}
+
+#ifndef QT_NO_STRINGLISTMODEL
+/*!
+ Constructs a QCompleter object with the given \a parent that uses the specified
+ \a list as a source of possible completions.
+*/
+QCompleter::QCompleter(const QStringList& list, QObject *parent)
+: QObject(*new QCompleterPrivate(), parent)
+{
+ Q_D(QCompleter);
+ d->init(new QStringListModel(list, this));
+}
+#endif // QT_NO_STRINGLISTMODEL
+
+/*!
+ Destroys the completer object.
+*/
+QCompleter::~QCompleter()
+{
+}
+
+/*!
+ Sets the widget for which completion are provided for to \a widget. This
+ function is automatically called when a QCompleter is set on a QLineEdit
+ using QLineEdit::setCompleter() or on a QComboBox using
+ QComboBox::setCompleter(). The widget needs to be set explicitly when
+ providing completions for custom widgets.
+
+ \sa widget(), setModel(), setPopup()
+ */
+void QCompleter::setWidget(QWidget *widget)
+{
+ Q_D(QCompleter);
+ if (d->widget)
+ d->widget->removeEventFilter(this);
+ d->widget = widget;
+ if (d->widget)
+ d->widget->installEventFilter(this);
+ if (d->popup) {
+ d->popup->hide();
+ d->popup->setFocusProxy(d->widget);
+ }
+}
+
+/*!
+ Returns the widget for which the completer object is providing completions.
+
+ \sa setWidget()
+ */
+QWidget *QCompleter::widget() const
+{
+ Q_D(const QCompleter);
+ return d->widget;
+}
+
+/*!
+ Sets the model which provides completions to \a model. The \a model can
+ be list model or a tree model. If a model has been already previously set
+ and it has the QCompleter as its parent, it is deleted.
+
+ For convenience, if \a model is a QDirModel, QCompleter switches its
+ caseSensitivity to Qt::CaseInsensitive on Windows and Qt::CaseSensitive
+ on other platforms.
+
+ \sa completionModel(), modelSorting, {Handling Tree Models}
+*/
+void QCompleter::setModel(QAbstractItemModel *model)
+{
+ Q_D(QCompleter);
+ QAbstractItemModel *oldModel = d->proxy->sourceModel();
+ d->proxy->setSourceModel(model);
+ if (d->popup)
+ setPopup(d->popup); // set the model and make new connections
+ if (oldModel && oldModel->QObject::parent() == this)
+ delete oldModel;
+#ifndef QT_NO_DIRMODEL
+ if (qobject_cast<QDirModel *>(model)) {
+#if defined(Q_OS_WIN) && !defined(Q_OS_WINCE)
+ setCaseSensitivity(Qt::CaseInsensitive);
+#else
+ setCaseSensitivity(Qt::CaseSensitive);
+#endif
+ }
+#endif // QT_NO_DIRMODEL
+}
+
+/*!
+ Returns the model that provides completion strings.
+
+ \sa completionModel()
+*/
+QAbstractItemModel *QCompleter::model() const
+{
+ Q_D(const QCompleter);
+ return d->proxy->sourceModel();
+}
+
+/*!
+ \enum QCompleter::CompletionMode
+
+ This enum specifies how completions are provided to the user.
+
+ \value PopupCompletion Current completions are displayed in a popup window.
+ \value InlineCompletion Completions appear inline (as selected text).
+ \value UnfilteredPopupCompletion All possible completions are displayed in a popup window with the most likely suggestion indicated as current.
+
+ \sa setCompletionMode()
+*/
+
+/*!
+ \property QCompleter::completionMode
+ \brief how the completions are provided to the user
+
+ The default value is QCompleter::PopupCompletion.
+*/
+void QCompleter::setCompletionMode(QCompleter::CompletionMode mode)
+{
+ Q_D(QCompleter);
+ d->mode = mode;
+ d->proxy->setFiltered(mode != QCompleter::UnfilteredPopupCompletion);
+
+ if (mode == QCompleter::InlineCompletion) {
+ if (d->widget)
+ d->widget->removeEventFilter(this);
+ if (d->popup) {
+ d->popup->deleteLater();
+ d->popup = 0;
+ }
+ } else {
+ if (d->widget)
+ d->widget->installEventFilter(this);
+ }
+}
+
+QCompleter::CompletionMode QCompleter::completionMode() const
+{
+ Q_D(const QCompleter);
+ return d->mode;
+}
+
+/*!
+ Sets the popup used to display completions to \a popup. QCompleter takes
+ ownership of the view.
+
+ A QListView is automatically created when the completionMode() is set to
+ QCompleter::PopupCompletion or QCompleter::UnfilteredPopupCompletion. The
+ default popup displays the completionColumn().
+
+ Ensure that this function is called before the view settings are modified.
+ This is required since view's properties may require that a model has been
+ set on the view (for example, hiding columns in the view requires a model
+ to be set on the view).
+
+ \sa popup()
+*/
+void QCompleter::setPopup(QAbstractItemView *popup)
+{
+ Q_D(QCompleter);
+ Q_ASSERT(popup != 0);
+ if (d->popup) {
+ QObject::disconnect(d->popup->selectionModel(), 0, this, 0);
+ QObject::disconnect(d->popup, 0, this, 0);
+ }
+ if (d->popup != popup)
+ delete d->popup;
+ if (popup->model() != d->proxy)
+ popup->setModel(d->proxy);
+ popup->hide();
+ popup->setParent(0, Qt::Popup);
+ popup->setFocusPolicy(Qt::NoFocus);
+ popup->setFocusProxy(d->widget);
+ popup->installEventFilter(this);
+ popup->setItemDelegate(new QCompleterItemDelegate(popup));
+#ifndef QT_NO_LISTVIEW
+ if (QListView *listView = qobject_cast<QListView *>(popup)) {
+ listView->setModelColumn(d->column);
+ }
+#endif
+
+ QObject::connect(popup, SIGNAL(clicked(QModelIndex)),
+ this, SLOT(_q_complete(QModelIndex)));
+ QObject::connect(popup, SIGNAL(clicked(QModelIndex)), popup, SLOT(hide()));
+
+ QObject::connect(popup->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)),
+ this, SLOT(_q_completionSelected(QItemSelection)));
+ d->popup = popup;
+}
+
+/*!
+ Returns the popup used to display completions.
+
+ \sa setPopup()
+*/
+QAbstractItemView *QCompleter::popup() const
+{
+ Q_D(const QCompleter);
+#ifndef QT_NO_LISTVIEW
+ if (!d->popup && completionMode() != QCompleter::InlineCompletion) {
+ QListView *listView = new QListView;
+ listView->setEditTriggers(QAbstractItemView::NoEditTriggers);
+ listView->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
+ listView->setSelectionBehavior(QAbstractItemView::SelectRows);
+ listView->setSelectionMode(QAbstractItemView::SingleSelection);
+ listView->setModelColumn(d->column);
+ QCompleter *that = const_cast<QCompleter*>(this);
+ that->setPopup(listView);
+ }
+#endif // QT_NO_LISTVIEW
+ return d->popup;
+}
+
+/*!
+ \reimp
+*/
+bool QCompleter::event(QEvent *ev)
+{
+ return QObject::event(ev);
+}
+
+/*!
+ \reimp
+*/
+bool QCompleter::eventFilter(QObject *o, QEvent *e)
+{
+ Q_D(QCompleter);
+
+ if (d->eatFocusOut && o == d->widget && e->type() == QEvent::FocusOut) {
+ if (d->popup && d->popup->isVisible())
+ return true;
+ }
+
+ if (o != d->popup)
+ return QObject::eventFilter(o, e);
+
+ switch (e->type()) {
+ case QEvent::KeyPress: {
+ QKeyEvent *ke = static_cast<QKeyEvent *>(e);
+
+ QModelIndex curIndex = d->popup->currentIndex();
+ QModelIndexList selList = d->popup->selectionModel()->selectedIndexes();
+
+ const int key = ke->key();
+ // In UnFilteredPopup mode, select the current item
+ if ((key == Qt::Key_Up || key == Qt::Key_Down) && selList.isEmpty() && curIndex.isValid()
+ && d->mode == QCompleter::UnfilteredPopupCompletion) {
+ d->setCurrentIndex(curIndex);
+ return true;
+ }
+
+ // Handle popup navigation keys. These are hardcoded because up/down might make the
+ // widget do something else (lineedit cursor moves to home/end on mac, for instance)
+ switch (key) {
+ case Qt::Key_End:
+ case Qt::Key_Home:
+ if (ke->modifiers() & Qt::ControlModifier)
+ return false;
+ break;
+
+ case Qt::Key_Up:
+ if (!curIndex.isValid()) {
+ int rowCount = d->proxy->rowCount();
+ QModelIndex lastIndex = d->proxy->index(rowCount - 1, 0);
+ d->setCurrentIndex(lastIndex);
+ return true;
+ } else if (curIndex.row() == 0) {
+ if (d->wrap)
+ d->setCurrentIndex(QModelIndex());
+ return true;
+ }
+ return false;
+
+ case Qt::Key_Down:
+ if (!curIndex.isValid()) {
+ QModelIndex firstIndex = d->proxy->index(0, 0);
+ d->setCurrentIndex(firstIndex);
+ return true;
+ } else if (curIndex.row() == d->proxy->rowCount() - 1) {
+ if (d->wrap)
+ d->setCurrentIndex(QModelIndex());
+ return true;
+ }
+ return false;
+
+ case Qt::Key_PageUp:
+ case Qt::Key_PageDown:
+ return false;
+ }
+
+ // Send the event to the widget. If the widget accepted the event, do nothing
+ // If the widget did not accept the event, provide a default implementation
+ d->eatFocusOut = false;
+ (static_cast<QObject *>(d->widget))->event(ke);
+ d->eatFocusOut = true;
+ if (!d->widget || e->isAccepted() || !d->popup->isVisible()) {
+ // widget lost focus, hide the popup
+ if (d->widget && (!d->widget->hasFocus()
+#ifdef QT_KEYPAD_NAVIGATION
+ || (QApplication::keypadNavigationEnabled() && !d->widget->hasEditFocus())
+#endif
+ ))
+ d->popup->hide();
+ if (e->isAccepted())
+ return true;
+ }
+
+ // default implementation for keys not handled by the widget when popup is open
+ switch (key) {
+#ifdef QT_KEYPAD_NAVIGATION
+ case Qt::Key_Select:
+ if (!QApplication::keypadNavigationEnabled())
+ break;
+#endif
+ case Qt::Key_Return:
+ case Qt::Key_Enter:
+ case Qt::Key_Tab:
+ d->popup->hide();
+ if (curIndex.isValid())
+ d->_q_complete(curIndex);
+ break;
+
+ case Qt::Key_F4:
+ if (ke->modifiers() & Qt::AltModifier)
+ d->popup->hide();
+ break;
+
+ case Qt::Key_Backtab:
+ case Qt::Key_Escape:
+ d->popup->hide();
+ break;
+
+ default:
+ break;
+ }
+
+ return true;
+ }
+
+#ifdef QT_KEYPAD_NAVIGATION
+ case QEvent::KeyRelease: {
+ QKeyEvent *ke = static_cast<QKeyEvent *>(e);
+ if (QApplication::keypadNavigationEnabled() && ke->key() == Qt::Key_Back) {
+ // Send the event to the 'widget'. This is what we did for KeyPress, so we need
+ // to do the same for KeyRelease, in case the widget's KeyPress event set
+ // up something (such as a timer) that is relying on also receiving the
+ // key release. I see this as a bug in Qt, and should really set it up for all
+ // the affected keys. However, it is difficult to tell how this will affect
+ // existing code, and I can't test for every combination!
+ d->eatFocusOut = false;
+ static_cast<QObject *>(d->widget)->event(ke);
+ d->eatFocusOut = true;
+ }
+ break;
+ }
+#endif
+
+ case QEvent::MouseButtonPress: {
+#ifdef QT_KEYPAD_NAVIGATION
+ if (QApplication::keypadNavigationEnabled()) {
+ // if we've clicked in the widget (or its descendant), let it handle the click
+ QWidget *source = qobject_cast<QWidget *>(o);
+ if (source) {
+ QPoint pos = source->mapToGlobal((static_cast<QMouseEvent *>(e))->pos());
+ QWidget *target = QApplication::widgetAt(pos);
+ if (target && (d->widget->isAncestorOf(target) ||
+ target == d->widget)) {
+ d->eatFocusOut = false;
+ static_cast<QObject *>(target)->event(e);
+ d->eatFocusOut = true;
+ return true;
+ }
+ }
+ }
+#endif
+ if (!d->popup->underMouse()) {
+ d->popup->hide();
+ return true;
+ }
+ }
+ return false;
+
+ case QEvent::InputMethod:
+ case QEvent::ShortcutOverride:
+ QApplication::sendEvent(d->widget, e);
+ break;
+
+ default:
+ return false;
+ }
+ return false;
+}
+
+/*!
+ For QCompleter::PopupCompletion and QCompletion::UnfilteredPopupCompletion
+ modes, calling this function displays the popup displaying the current
+ completions. By default, if \a rect is not specified, the popup is displayed
+ on the bottom of the widget(). If \a rect is specified the popup is
+ displayed on the left edge of the rectangle.
+
+ For QCompleter::InlineCompletion mode, the highlighted() signal is fired
+ with the current completion.
+*/
+void QCompleter::complete(const QRect& rect)
+{
+ Q_D(QCompleter);
+ QModelIndex idx = d->proxy->currentIndex(false);
+ if (d->mode == QCompleter::InlineCompletion) {
+ if (idx.isValid())
+ d->_q_complete(idx, true);
+ return;
+ }
+
+ Q_ASSERT(d->widget != 0);
+ if ((d->mode == QCompleter::PopupCompletion && !idx.isValid())
+ || (d->mode == QCompleter::UnfilteredPopupCompletion && d->proxy->rowCount() == 0)) {
+ if (d->popup)
+ d->popup->hide(); // no suggestion, hide
+ return;
+ }
+
+ popup();
+ if (d->mode == QCompleter::UnfilteredPopupCompletion)
+ d->setCurrentIndex(idx, false);
+
+ d->showPopup(rect);
+ d->popupRect = rect;
+}
+
+/*!
+ Sets the current row to the \a row specified. Returns true if successful;
+ otherwise returns false.
+
+ This function may be used along with currentCompletion() to iterate
+ through all the possible completions.
+
+ \sa currentCompletion(), completionCount()
+*/
+bool QCompleter::setCurrentRow(int row)
+{
+ Q_D(QCompleter);
+ return d->proxy->setCurrentRow(row);
+}
+
+/*!
+ Returns the current row.
+
+ \sa setCurrentRow()
+*/
+int QCompleter::currentRow() const
+{
+ Q_D(const QCompleter);
+ return d->proxy->currentRow();
+}
+
+/*!
+ Returns the number of completions for the current prefix. For an unsorted
+ model with a large number of items this can be expensive. Use setCurrentRow()
+ and currentCompletion() to iterate through all the completions.
+*/
+int QCompleter::completionCount() const
+{
+ Q_D(const QCompleter);
+ return d->proxy->completionCount();
+}
+
+/*!
+ \enum QCompleter::ModelSorting
+
+ This enum specifies how the items in the model are sorted.
+
+ \value UnsortedModel The model is unsorted.
+ \value CaseSensitivelySortedModel The model is sorted case sensitively.
+ \value CaseInsensitivelySortedModel The model is sorted case insensitively.
+
+ \sa setModelSorting()
+*/
+
+/*!
+ \property QCompleter::modelSorting
+ \brief the way the model is sorted
+
+ By default, no assumptions are made about the order of the items
+ in the model that provides the completions.
+
+ If the model's data for the completionColumn() and completionRole() is sorted in
+ ascending order, you can set this property to \l CaseSensitivelySortedModel
+ or \l CaseInsensitivelySortedModel. On large models, this can lead to
+ significant performance improvements because the completer object can
+ then use a binary search algorithm instead of linear search algorithm.
+
+ The sort order (i.e ascending or descending order) of the model is determined
+ dynamically by inspecting the contents of the model.
+
+ \bold{Note:} The performance improvements described above cannot take place
+ when the completer's \l caseSensitivity is different to the case sensitivity
+ used by the model's when sorting.
+
+ \sa setCaseSensitivity(), QCompleter::ModelSorting
+*/
+void QCompleter::setModelSorting(QCompleter::ModelSorting sorting)
+{
+ Q_D(QCompleter);
+ if (d->sorting == sorting)
+ return;
+ d->sorting = sorting;
+ d->proxy->createEngine();
+ d->proxy->invalidate();
+}
+
+QCompleter::ModelSorting QCompleter::modelSorting() const
+{
+ Q_D(const QCompleter);
+ return d->sorting;
+}
+
+/*!
+ \property QCompleter::completionColumn
+ \brief the column in the model in which completions are searched for.
+
+ If the popup() is a QListView, it is automatically setup to display
+ this column.
+
+ By default, the match column is 0.
+
+ \sa completionRole, caseSensitivity
+*/
+void QCompleter::setCompletionColumn(int column)
+{
+ Q_D(QCompleter);
+ if (d->column == column)
+ return;
+#ifndef QT_NO_LISTVIEW
+ if (QListView *listView = qobject_cast<QListView *>(d->popup))
+ listView->setModelColumn(column);
+#endif
+ d->column = column;
+ d->proxy->invalidate();
+}
+
+int QCompleter::completionColumn() const
+{
+ Q_D(const QCompleter);
+ return d->column;
+}
+
+/*!
+ \property QCompleter::completionRole
+ \brief the item role to be used to query the contents of items for matching.
+
+ The default role is Qt::EditRole.
+
+ \sa completionColumn, caseSensitivity
+*/
+void QCompleter::setCompletionRole(int role)
+{
+ Q_D(QCompleter);
+ if (d->role == role)
+ return;
+ d->role = role;
+ d->proxy->invalidate();
+}
+
+int QCompleter::completionRole() const
+{
+ Q_D(const QCompleter);
+ return d->role;
+}
+
+/*!
+ \property QCompleter::wrapAround
+ \brief the completions wrap around when navigating through items
+ \since 4.3
+
+ The default is true.
+*/
+void QCompleter::setWrapAround(bool wrap)
+{
+ Q_D(QCompleter);
+ if (d->wrap == wrap)
+ return;
+ d->wrap = wrap;
+}
+
+bool QCompleter::wrapAround() const
+{
+ Q_D(const QCompleter);
+ return d->wrap;
+}
+
+/*!
+ \property QCompleter::caseSensitivity
+ \brief the case sensitivity of the matching
+
+ The default is Qt::CaseSensitive.
+
+ \sa completionColumn, completionRole, modelSorting
+*/
+void QCompleter::setCaseSensitivity(Qt::CaseSensitivity cs)
+{
+ Q_D(QCompleter);
+ if (d->cs == cs)
+ return;
+ d->cs = cs;
+ d->proxy->createEngine();
+ d->proxy->invalidate();
+}
+
+Qt::CaseSensitivity QCompleter::caseSensitivity() const
+{
+ Q_D(const QCompleter);
+ return d->cs;
+}
+
+/*!
+ \property QCompleter::completionPrefix
+ \brief the completion prefix used to provide completions.
+
+ The completionModel() is updated to reflect the list of possible
+ matches for \a prefix.
+*/
+void QCompleter::setCompletionPrefix(const QString &prefix)
+{
+ Q_D(QCompleter);
+ d->prefix = prefix;
+ d->proxy->filter(splitPath(prefix));
+}
+
+QString QCompleter::completionPrefix() const
+{
+ Q_D(const QCompleter);
+ return d->prefix;
+}
+
+/*!
+ Returns the model index of the current completion in the completionModel().
+
+ \sa setCurrentRow(), currentCompletion(), model()
+*/
+QModelIndex QCompleter::currentIndex() const
+{
+ Q_D(const QCompleter);
+ return d->proxy->currentIndex(false);
+}
+
+/*!
+ Returns the current completion string. This includes the \l completionPrefix.
+ When used alongside setCurrentRow(), it can be used to iterate through
+ all the matches.
+
+ \sa setCurrentRow(), currentIndex()
+*/
+QString QCompleter::currentCompletion() const
+{
+ Q_D(const QCompleter);
+ return pathFromIndex(d->proxy->currentIndex(true));
+}
+
+/*!
+ Returns the completion model. The completion model is a read-only list model
+ that contains all the possible matches for the current completion prefix.
+ The completion model is auto-updated to reflect the current completions.
+
+ \sa completionPrefix, model()
+*/
+QAbstractItemModel *QCompleter::completionModel() const
+{
+ Q_D(const QCompleter);
+ return d->proxy;
+}
+
+/*!
+ Returns the path for the given \a index. The completer object uses this to
+ obtain the completion text from the underlying model.
+
+ The default implementation returns the \l{Qt::EditRole}{edit role} of the
+ item for list models. It returns the absolute file path if the model is a
+ QDirModel.
+
+ \sa splitPath()
+*/
+QString QCompleter::pathFromIndex(const QModelIndex& index) const
+{
+ Q_D(const QCompleter);
+ if (!index.isValid())
+ return QString();
+
+ QAbstractItemModel *sourceModel = d->proxy->sourceModel();
+ if (!sourceModel)
+ return QString();
+#ifndef QT_NO_DIRMODEL
+ QDirModel *dirModel = qobject_cast<QDirModel *>(sourceModel);
+ if (!dirModel)
+#endif
+ return sourceModel->data(index, d->role).toString();
+
+ QModelIndex idx = index;
+ QStringList list;
+ do {
+ QString t = sourceModel->data(idx, Qt::EditRole).toString();
+ list.prepend(t);
+ QModelIndex parent = idx.parent();
+ idx = parent.sibling(parent.row(), index.column());
+ } while (idx.isValid());
+
+#if !defined(Q_OS_WIN) || defined(Q_OS_WINCE)
+ if (list.count() == 1) // only the separator or some other text
+ return list[0];
+ list[0].clear() ; // the join below will provide the separator
+#endif
+
+ return list.join(QDir::separator());
+}
+
+/*!
+ Splits the given \a path into strings that are used to match at each level
+ in the model().
+
+ The default implementation of splitPath() splits a file system path based on
+ QDir::separator() when the sourceModel() is a QDirModel.
+
+ When used with list models, the first item in the returned list is used for
+ matching.
+
+ \sa pathFromIndex(), {Handling Tree Models}
+*/
+QStringList QCompleter::splitPath(const QString& path) const
+{
+ bool isDirModel = false;
+#ifndef QT_NO_DIRMODEL
+ Q_D(const QCompleter);
+ isDirModel = qobject_cast<QDirModel *>(d->proxy->sourceModel()) != 0;
+#endif
+
+ if (!isDirModel || path.isEmpty())
+ return QStringList(completionPrefix());
+
+ QString pathCopy = QDir::toNativeSeparators(path);
+ QString sep = QDir::separator();
+#if defined(Q_OS_WIN) && !defined(Q_OS_WINCE)
+ if (pathCopy == QLatin1String("\\") || pathCopy == QLatin1String("\\\\"))
+ return QStringList(pathCopy);
+ QString doubleSlash(QLatin1String("\\\\"));
+ if (pathCopy.startsWith(doubleSlash))
+ pathCopy = pathCopy.mid(2);
+ else
+ doubleSlash.clear();
+#endif
+
+ QRegExp re(QLatin1String("[") + QRegExp::escape(sep) + QLatin1String("]"));
+ QStringList parts = pathCopy.split(re);
+
+#if defined(Q_OS_WIN) && !defined(Q_OS_WINCE)
+ if (!doubleSlash.isEmpty())
+ parts[0].prepend(doubleSlash);
+#else
+ if (pathCopy[0] == sep[0]) // readd the "/" at the beginning as the split removed it
+ parts[0] = QDir::fromNativeSeparators(QString(sep[0]));
+#endif
+
+ return parts;
+}
+
+/*!
+ \fn void QCompleter::activated(const QModelIndex& index)
+
+ This signal is sent when an item in the popup() is activated by the user.
+ (by clicking or pressing return). The item's \a index in the completionModel()
+ is given.
+
+*/
+
+/*!
+ \fn void QCompleter::activated(const QString &text)
+
+ This signal is sent when an item in the popup() is activated by the user (by
+ clicking or pressing return). The item's \a text is given.
+
+*/
+
+/*!
+ \fn void QCompleter::highlighted(const QModelIndex& index)
+
+ This signal is sent when an item in the popup() is highlighted by
+ the user. It is also sent if complete() is called with the completionMode()
+ set to QCompleter::InlineCompletion. The item's \a index in the completionModel()
+ is given.
+*/
+
+/*!
+ \fn void QCompleter::highlighted(const QString &text)
+
+ This signal is sent when an item in the popup() is highlighted by
+ the user. It is also sent if complete() is called with the completionMode()
+ set to QCOmpleter::InlineCompletion. The item's \a text is given.
+*/
+
+QT_END_NAMESPACE
+
+#include "moc_qcompleter.cpp"
+
+#endif // QT_NO_COMPLETER
diff --git a/src/gui/util/qcompleter.h b/src/gui/util/qcompleter.h
new file mode 100644
index 0000000000..15df2b65e4
--- /dev/null
+++ b/src/gui/util/qcompleter.h
@@ -0,0 +1,166 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QCOMPLETER_H
+#define QCOMPLETER_H
+
+#include <QtCore/qobject.h>
+#include <QtCore/qpoint.h>
+#include <QtCore/qstring.h>
+#include <QtCore/qabstractitemmodel.h>
+#include <QtCore/qrect.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+#ifndef QT_NO_COMPLETER
+
+class QCompleterPrivate;
+class QAbstractItemView;
+class QAbstractProxyModel;
+class QWidget;
+
+class Q_GUI_EXPORT QCompleter : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(QString completionPrefix READ completionPrefix WRITE setCompletionPrefix)
+ Q_PROPERTY(ModelSorting modelSorting READ modelSorting WRITE setModelSorting)
+ Q_PROPERTY(CompletionMode completionMode READ completionMode WRITE setCompletionMode)
+ Q_PROPERTY(int completionColumn READ completionColumn WRITE setCompletionColumn)
+ Q_PROPERTY(int completionRole READ completionRole WRITE setCompletionRole)
+ Q_PROPERTY(Qt::CaseSensitivity caseSensitivity READ caseSensitivity WRITE setCaseSensitivity)
+ Q_PROPERTY(bool wrapAround READ wrapAround WRITE setWrapAround)
+
+public:
+ enum CompletionMode {
+ PopupCompletion,
+ UnfilteredPopupCompletion,
+ InlineCompletion
+ };
+
+ enum ModelSorting {
+ UnsortedModel = 0,
+ CaseSensitivelySortedModel,
+ CaseInsensitivelySortedModel
+ };
+
+ QCompleter(QObject *parent = 0);
+ QCompleter(QAbstractItemModel *model, QObject *parent = 0);
+#ifndef QT_NO_STRINGLISTMODEL
+ QCompleter(const QStringList& completions, QObject *parent = 0);
+#endif
+ ~QCompleter();
+
+ void setWidget(QWidget *widget);
+ QWidget *widget() const;
+
+ void setModel(QAbstractItemModel *c);
+ QAbstractItemModel *model() const;
+
+ void setCompletionMode(CompletionMode mode);
+ CompletionMode completionMode() const;
+
+ QAbstractItemView *popup() const;
+ void setPopup(QAbstractItemView *popup);
+
+ void setCaseSensitivity(Qt::CaseSensitivity caseSensitivity);
+ Qt::CaseSensitivity caseSensitivity() const;
+
+ void setModelSorting(ModelSorting sorting);
+ ModelSorting modelSorting() const;
+
+ void setCompletionColumn(int column);
+ int completionColumn() const;
+
+ void setCompletionRole(int role);
+ int completionRole() const;
+
+ bool wrapAround() const;
+
+ int completionCount() const;
+ bool setCurrentRow(int row);
+ int currentRow() const;
+
+ QModelIndex currentIndex() const;
+ QString currentCompletion() const;
+
+ QAbstractItemModel *completionModel() const;
+
+ QString completionPrefix() const;
+
+public Q_SLOTS:
+ void setCompletionPrefix(const QString &prefix);
+ void complete(const QRect& rect = QRect());
+ void setWrapAround(bool wrap);
+
+public:
+ virtual QString pathFromIndex(const QModelIndex &index) const;
+ virtual QStringList splitPath(const QString &path) const;
+
+protected:
+ bool eventFilter(QObject *o, QEvent *e);
+ bool event(QEvent *);
+
+Q_SIGNALS:
+ void activated(const QString &text);
+ void activated(const QModelIndex &index);
+ void highlighted(const QString &text);
+ void highlighted(const QModelIndex &index);
+
+private:
+ Q_DISABLE_COPY(QCompleter)
+ Q_DECLARE_PRIVATE(QCompleter)
+
+ Q_PRIVATE_SLOT(d_func(), void _q_complete(QModelIndex))
+ Q_PRIVATE_SLOT(d_func(), void _q_completionSelected(const QItemSelection&))
+ Q_PRIVATE_SLOT(d_func(), void _q_autoResizePopup())
+};
+
+#endif // QT_NO_COMPLETER
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QCOMPLETER_H
diff --git a/src/gui/util/qcompleter_p.h b/src/gui/util/qcompleter_p.h
new file mode 100644
index 0000000000..88dc2c04aa
--- /dev/null
+++ b/src/gui/util/qcompleter_p.h
@@ -0,0 +1,262 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QCOMPLETER_P_H
+#define QCOMPLETER_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/qobject_p.h"
+
+#ifndef QT_NO_COMPLETER
+
+#include "QtGui/qtreeview.h"
+#include "QtGui/qabstractproxymodel.h"
+#include "qcompleter.h"
+#include "QtGui/qitemdelegate.h"
+#include "QtGui/qpainter.h"
+#include "private/qabstractproxymodel_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class QCompletionModel;
+
+class QCompleterPrivate : public QObjectPrivate
+{
+ Q_DECLARE_PUBLIC(QCompleter)
+
+public:
+ QCompleterPrivate();
+ ~QCompleterPrivate() { delete popup; }
+ void init(QAbstractItemModel *model = 0);
+
+ QPointer<QWidget> widget;
+ QCompletionModel *proxy;
+ QAbstractItemView *popup;
+ QCompleter::CompletionMode mode;
+
+ QString prefix;
+ Qt::CaseSensitivity cs;
+ int role;
+ int column;
+ QCompleter::ModelSorting sorting;
+ bool wrap;
+
+ bool eatFocusOut;
+ QRect popupRect;
+
+ void showPopup(const QRect&);
+ void _q_complete(QModelIndex, bool = false);
+ void _q_completionSelected(const QItemSelection&);
+ void _q_autoResizePopup();
+ void setCurrentIndex(QModelIndex, bool = true);
+};
+
+class QIndexMapper
+{
+public:
+ QIndexMapper() : v(false), f(0), t(-1) { }
+ QIndexMapper(int f, int t) : v(false), f(f), t(t) { }
+ QIndexMapper(QVector<int> vec) : v(true), vector(vec), f(-1), t(-1) { }
+
+ inline int count() const { return v ? vector.count() : t - f + 1; }
+ inline int operator[] (int index) const { return v ? vector[index] : f + index; }
+ inline int indexOf(int x) const { return v ? vector.indexOf(x) : ((t < f) ? -1 : x - f); }
+ inline bool isValid() const { return !isEmpty(); }
+ inline bool isEmpty() const { return v ? vector.isEmpty() : (t < f); }
+ inline void append(int x) { Q_ASSERT(v); vector.append(x); }
+ inline int first() const { return v ? vector.first() : f; }
+ inline int last() const { return v ? vector.last() : t; }
+ inline int from() const { Q_ASSERT(!v); return f; }
+ inline int to() const { Q_ASSERT(!v); return t; }
+ inline int cost() const { return vector.count()+2; }
+
+private:
+ bool v;
+ QVector<int> vector;
+ int f, t;
+};
+
+struct QMatchData {
+ QMatchData() : exactMatchIndex(-1) { }
+ QMatchData(const QIndexMapper& indices, int em, bool p) :
+ indices(indices), exactMatchIndex(em), partial(p) { }
+ QIndexMapper indices;
+ inline bool isValid() const { return indices.isValid(); }
+ int exactMatchIndex;
+ bool partial;
+};
+
+class QCompletionEngine
+{
+public:
+ typedef QMap<QString, QMatchData> CacheItem;
+ typedef QMap<QModelIndex, CacheItem> Cache;
+
+ QCompletionEngine(QCompleterPrivate *c) : c(c), curRow(-1), cost(0) { }
+ virtual ~QCompletionEngine() { }
+
+ void filter(const QStringList &parts);
+
+ QMatchData filterHistory();
+ bool matchHint(QString, const QModelIndex&, QMatchData*);
+
+ void saveInCache(QString, const QModelIndex&, const QMatchData&);
+ bool lookupCache(QString part, const QModelIndex& parent, QMatchData *m);
+
+ virtual void filterOnDemand(int) { }
+ virtual QMatchData filter(const QString&, const QModelIndex&, int) = 0;
+
+ int matchCount() const { return curMatch.indices.count() + historyMatch.indices.count(); }
+
+ QMatchData curMatch, historyMatch;
+ QCompleterPrivate *c;
+ QStringList curParts;
+ QModelIndex curParent;
+ int curRow;
+
+ Cache cache;
+ int cost;
+};
+
+class QSortedModelEngine : public QCompletionEngine
+{
+public:
+ QSortedModelEngine(QCompleterPrivate *c) : QCompletionEngine(c) { }
+ QMatchData filter(const QString&, const QModelIndex&, int);
+ QIndexMapper indexHint(QString, const QModelIndex&, Qt::SortOrder);
+ Qt::SortOrder sortOrder(const QModelIndex&) const;
+};
+
+class QUnsortedModelEngine : public QCompletionEngine
+{
+public:
+ QUnsortedModelEngine(QCompleterPrivate *c) : QCompletionEngine(c) { }
+
+ void filterOnDemand(int);
+ QMatchData filter(const QString&, const QModelIndex&, int);
+private:
+ int buildIndices(const QString& str, const QModelIndex& parent, int n,
+ const QIndexMapper& iv, QMatchData* m);
+};
+
+class QCompleterItemDelegate : public QItemDelegate
+{
+public:
+ QCompleterItemDelegate(QAbstractItemView *view)
+ : QItemDelegate(view), view(view) { }
+ void paint(QPainter *p, const QStyleOptionViewItem& opt, const QModelIndex& idx) const {
+ QStyleOptionViewItem optCopy = opt;
+ optCopy.showDecorationSelected = true;
+ if (view->currentIndex() == idx)
+ optCopy.state |= QStyle::State_HasFocus;
+ QItemDelegate::paint(p, optCopy, idx);
+ }
+
+private:
+ QAbstractItemView *view;
+};
+
+class QCompletionModelPrivate;
+
+class QCompletionModel : public QAbstractProxyModel
+{
+ Q_OBJECT
+
+public:
+ QCompletionModel(QCompleterPrivate *c, QObject *parent);
+ ~QCompletionModel() { delete engine; }
+
+ void createEngine();
+ void setFiltered(bool);
+ void filter(const QStringList& parts);
+ int completionCount() const;
+ int currentRow() const { return engine->curRow; }
+ bool setCurrentRow(int row);
+ QModelIndex currentIndex(bool) const;
+ void resetModel();
+
+ QModelIndex index(int row, int column, const QModelIndex & = QModelIndex()) const;
+ int rowCount(const QModelIndex &index = QModelIndex()) const;
+ int columnCount(const QModelIndex &index = QModelIndex()) const;
+ bool hasChildren(const QModelIndex &parent = QModelIndex()) const;
+ QModelIndex parent(const QModelIndex & = QModelIndex()) const { return QModelIndex(); }
+ QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const;
+
+ void setSourceModel(QAbstractItemModel *sourceModel);
+ QModelIndex mapToSource(const QModelIndex& proxyIndex) const;
+ QModelIndex mapFromSource(const QModelIndex& sourceIndex) const;
+
+ QCompleterPrivate *c;
+ QCompletionEngine *engine;
+ bool showAll;
+
+ Q_DECLARE_PRIVATE(QCompletionModel)
+
+signals:
+ void rowsAdded();
+
+public Q_SLOTS:
+ void invalidate();
+ void rowsInserted();
+ void modelDestroyed();
+};
+
+class QCompletionModelPrivate : public QAbstractProxyModelPrivate
+{
+ Q_DECLARE_PUBLIC(QCompletionModel)
+};
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_COMPLETER
+
+#endif // QCOMPLETER_P_H
diff --git a/src/gui/util/qdesktopservices.cpp b/src/gui/util/qdesktopservices.cpp
new file mode 100644
index 0000000000..0fe1d69fde
--- /dev/null
+++ b/src/gui/util/qdesktopservices.cpp
@@ -0,0 +1,307 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qdesktopservices.h"
+
+#ifndef QT_NO_DESKTOPSERVICES
+
+#include <qdebug.h>
+
+#if defined(Q_WS_QWS)
+#include "qdesktopservices_qws.cpp"
+#elif defined(Q_WS_X11)
+#include "qdesktopservices_x11.cpp"
+#elif defined(Q_WS_WIN)
+#include "qdesktopservices_win.cpp"
+#elif defined(Q_WS_MAC)
+#include "qdesktopservices_mac.cpp"
+#endif
+
+#include <qhash.h>
+#include <qobject.h>
+#include <qcoreapplication.h>
+#include <qurl.h>
+#include <qmutex.h>
+
+QT_BEGIN_NAMESPACE
+
+class QOpenUrlHandlerRegistry : public QObject
+{
+ Q_OBJECT
+public:
+ inline QOpenUrlHandlerRegistry() : mutex(QMutex::Recursive) {}
+
+ QMutex mutex;
+
+ struct Handler
+ {
+ QObject *receiver;
+ QByteArray name;
+ };
+ typedef QHash<QString, Handler> HandlerHash;
+ HandlerHash handlers;
+
+public Q_SLOTS:
+ void handlerDestroyed(QObject *handler);
+
+};
+
+Q_GLOBAL_STATIC(QOpenUrlHandlerRegistry, handlerRegistry)
+
+void QOpenUrlHandlerRegistry::handlerDestroyed(QObject *handler)
+{
+ HandlerHash::Iterator it = handlers.begin();
+ while (it != handlers.end()) {
+ if (it->receiver == handler) {
+ it = handlers.erase(it);
+ } else {
+ ++it;
+ }
+ }
+}
+
+/*!
+ \class QDesktopServices
+ \brief The QDesktopServices class provides methods for accessing common desktop services.
+ \since 4.2
+ \ingroup desktop
+
+ Many desktop environments provide services that can be used by applications to
+ perform common tasks, such as opening a web page, in a way that is both consistent
+ and takes into account the user's application preferences.
+
+ This class contains functions that provide simple interfaces to these services
+ that indicate whether they succeeded or failed.
+
+ The openUrl() function is used to open files located at arbitrary URLs in external
+ applications. For URLs that correspond to resources on the local filing system
+ (where the URL scheme is "file"), a suitable application will be used to open the
+ file; otherwise, a web browser will be used to fetch and display the file.
+
+ The user's desktop settings control whether certain executable file types are
+ opened for browsing, or if they are executed instead. Some desktop environments
+ are configured to prevent users from executing files obtained from non-local URLs,
+ or to ask the user's permission before doing so.
+
+ \section1 URL Handlers
+
+ The behavior of the openUrl() function can be customized for individual URL
+ schemes to allow applications to override the default handling behavior for
+ certain types of URLs.
+
+ The dispatch mechanism allows only one custom handler to be used for each URL
+ scheme; this is set using the setUrlHandler() function. Each handler is
+ implemented as a slot which accepts only a single QUrl argument.
+
+ The existing handlers for each scheme can be removed with the
+ unsetUrlHandler() function. This returns the handling behavior for the given
+ scheme to the default behavior.
+
+ This system makes it easy to implement a help system, for example. Help could be
+ provided in labels and text browsers using \gui{help://myapplication/mytopic}
+ URLs, and by registering a handler it becomes possible to display the help text
+ inside the application:
+
+ \snippet doc/src/snippets/code/src_gui_util_qdesktopservices.cpp 0
+
+ If inside the handler you decide that you can't open the requested
+ URL, you can just call QDesktopServices::openUrl() again with the
+ same argument, and it will try to open the URL using the
+ appropriate mechanism for the user's desktop environment.
+
+ \sa QSystemTrayIcon, QProcess
+*/
+
+/*!
+ Opens the given \a url in the appropriate Web browser for the user's desktop
+ environment, and returns true if successful; otherwise returns false.
+
+ If the URL is a reference to a local file (i.e., the URL scheme is "file") then
+ it will be opened with a suitable application instead of a Web browser.
+
+ If a \c mailto URL is specified, the user's e-mail client will be used to open a
+ composer window containing the options specified in the URL, similar to the way
+ \c mailto links are handled by a Web browser.
+
+ For example, the following URL contains a recipient (\c{user@foo.com}), a
+ subject (\c{Test}), and a message body (\c{Just a test}):
+
+ \snippet doc/src/snippets/code/src_gui_util_qdesktopservices.cpp 1
+
+ \warning Although many e-mail clients can send attachments and are
+ Unicode-aware, the user may have configured their client without these features.
+ Also, certain e-mail clients (e.g., Lotus Notes) have problems with long URLs.
+
+ \sa setUrlHandler()
+*/
+bool QDesktopServices::openUrl(const QUrl &url)
+{
+ QOpenUrlHandlerRegistry *registry = handlerRegistry();
+ QMutexLocker locker(&registry->mutex);
+ static bool insideOpenUrlHandler = false;
+
+ if (!insideOpenUrlHandler) {
+ QOpenUrlHandlerRegistry::HandlerHash::ConstIterator handler = registry->handlers.constFind(url.scheme());
+ if (handler != registry->handlers.constEnd()) {
+ insideOpenUrlHandler = true;
+ bool result = QMetaObject::invokeMethod(handler->receiver, handler->name.constData(), Qt::DirectConnection, Q_ARG(QUrl, url));
+ insideOpenUrlHandler = false;
+ return result; // ### support bool slot return type
+ }
+ }
+
+ bool result;
+ if (url.scheme() == QLatin1String("file"))
+ result = openDocument(url);
+ else
+ result = launchWebBrowser(url);
+
+ return result;
+}
+
+/*!
+ Sets the handler for the given \a scheme to be the handler \a method provided by
+ the \a receiver object.
+
+ This function provides a way to customize the behavior of openUrl(). If openUrl()
+ is called with a URL with the specified \a scheme then the given \a method on the
+ \a receiver object is called instead of QDesktopServices launching an external
+ application.
+
+ The provided method must be implemented as a slot that only accepts a single QUrl
+ argument.
+
+ If setUrlHandler() is used to set a new handler for a scheme which already
+ has a handler, the existing handler is simply replaced with the new one.
+ Since QDesktopServices does not take ownership of handlers, no objects are
+ deleted when a handler is replaced.
+
+ Note that the handler will always be called from within the same thread that
+ calls QDesktopServices::openUrl().
+
+ \sa openUrl(), unsetUrlHandler()
+*/
+void QDesktopServices::setUrlHandler(const QString &scheme, QObject *receiver, const char *method)
+{
+ QOpenUrlHandlerRegistry *registry = handlerRegistry();
+ QMutexLocker locker(&registry->mutex);
+ if (!receiver) {
+ registry->handlers.remove(scheme);
+ return;
+ }
+ QOpenUrlHandlerRegistry::Handler h;
+ h.receiver = receiver;
+ h.name = method;
+ registry->handlers.insert(scheme, h);
+ QObject::connect(receiver, SIGNAL(destroyed(QObject*)),
+ registry, SLOT(handlerDestroyed(QObject*)));
+}
+
+/*!
+ Removes a previously set URL handler for the specified \a scheme.
+
+ \sa setUrlHandler()
+*/
+void QDesktopServices::unsetUrlHandler(const QString &scheme)
+{
+ setUrlHandler(scheme, 0, 0);
+}
+
+/*!
+ \enum QDesktopServices::StandardLocation
+ \since 4.4
+
+ This enum describes the different locations that can be queried by
+ QDesktopServices::storageLocation and QDesktopServices::displayName.
+
+ \value DesktopLocation Returns the user's desktop directory.
+ \value DocumentsLocation Returns the user's document.
+ \value FontsLocation Returns the user's fonts.
+ \value ApplicationsLocation Returns the user's applications.
+ \value MusicLocation Returns the users music.
+ \value MoviesLocation Returns the user's movies.
+ \value PicturesLocation Returns the user's pictures.
+ \value TempLocation Returns the system's temporary directory.
+ \value HomeLocation Returns the user's home directory.
+ \value DataLocation Returns a directory location where persistent
+ application data can be stored. QCoreApplication::applicationName
+ and QCoreApplication::organizationName should work on all
+ platforms.
+ \value CacheLocation Returns a directory location where user-specific
+ non-essential (cached) data should be written.
+
+ \sa storageLocation() displayName()
+*/
+
+/*!
+ \fn QString QDesktopServices::storageLocation(StandardLocation type)
+ \since 4.4
+
+ Returns the default system directory where files of \a type belong, or an empty string
+ if the location cannot be determined.
+
+ \note The storage location returned can be a directory that does not exist; i.e., it
+ may need to be created by the system or the user.
+
+ \note On Mac OS X, DataLocation does not include QCoreApplication::organizationName.
+ Use code like this to add it:
+
+ \code
+ QString location = QDesktopServices::storageLocation(QDesktopServices::DataLocation);
+ #ifdef Q_WS_MAC
+ location.insert(location.count() - QCoreApplication::applicationName().count(),
+ QCoreApplication::organizationName() + "/");
+ #endif
+ \endcode
+*/
+
+/*!
+ \fn QString QDesktopServices::displayName(StandardLocation type)
+
+ Returns a localized display name for the given location \a type or
+ an empty QString if no relevant location can be found.
+*/
+
+QT_END_NAMESPACE
+
+#include "qdesktopservices.moc"
+
+#endif // QT_NO_DESKTOPSERVICES
diff --git a/src/gui/util/qdesktopservices.h b/src/gui/util/qdesktopservices.h
new file mode 100644
index 0000000000..15774b47ff
--- /dev/null
+++ b/src/gui/util/qdesktopservices.h
@@ -0,0 +1,91 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QDESKTOPSERVICES_H
+#define QDESKTOPSERVICES_H
+
+#include <QtCore/qstring.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+#ifndef QT_NO_DESKTOPSERVICES
+
+class QStringList;
+class QUrl;
+class QObject;
+
+class Q_GUI_EXPORT QDesktopServices
+{
+public:
+ static bool openUrl(const QUrl &url);
+ static void setUrlHandler(const QString &scheme, QObject *receiver, const char *method);
+ static void unsetUrlHandler(const QString &scheme);
+
+ enum StandardLocation {
+ DesktopLocation,
+ DocumentsLocation,
+ FontsLocation,
+ ApplicationsLocation,
+ MusicLocation,
+ MoviesLocation,
+ PicturesLocation,
+ TempLocation,
+ HomeLocation,
+ DataLocation,
+ CacheLocation
+ };
+
+ static QString storageLocation(StandardLocation type);
+ static QString displayName(StandardLocation type);
+};
+
+#endif // QT_NO_DESKTOPSERVICES
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QDESKTOPSERVICES_H
+
diff --git a/src/gui/util/qdesktopservices_mac.cpp b/src/gui/util/qdesktopservices_mac.cpp
new file mode 100644
index 0000000000..5124068e44
--- /dev/null
+++ b/src/gui/util/qdesktopservices_mac.cpp
@@ -0,0 +1,182 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QT_NO_DESKTOPSERVICES
+
+#include <qprocess.h>
+#include <qstringlist.h>
+#include <qdir.h>
+#include <qurl.h>
+#include <qstringlist.h>
+#include <private/qcore_mac_p.h>
+#include <qcoreapplication.h>
+
+QT_BEGIN_NAMESPACE
+
+/*
+ Translates a QDesktopServices::StandardLocation into the mac equivalent.
+*/
+OSType translateLocation(QDesktopServices::StandardLocation type)
+{
+ switch (type) {
+ case QDesktopServices::DesktopLocation:
+ return kDesktopFolderType; break;
+
+ case QDesktopServices::DocumentsLocation:
+ return kDocumentsFolderType; break;
+
+ case QDesktopServices::FontsLocation:
+ // There are at least two different font directories on the mac: /Library/Fonts and ~/Library/Fonts.
+ // To select a specific one we have to specify a different first parameter when calling FSFindFolder.
+ return kFontsFolderType; break;
+
+ case QDesktopServices::ApplicationsLocation:
+ return kApplicationsFolderType; break;
+
+ case QDesktopServices::MusicLocation:
+ return kMusicDocumentsFolderType; break;
+
+ case QDesktopServices::MoviesLocation:
+ return kMovieDocumentsFolderType; break;
+
+ case QDesktopServices::PicturesLocation:
+ return kPictureDocumentsFolderType; break;
+
+ case QDesktopServices::TempLocation:
+ return kTemporaryFolderType; break;
+
+ case QDesktopServices::DataLocation:
+ return kApplicationSupportFolderType; break;
+
+ case QDesktopServices::CacheLocation:
+ return kCachedDataFolderType; break;
+
+ default:
+ return kDesktopFolderType; break;
+ }
+}
+
+static bool lsOpen(const QUrl &url)
+{
+ if (!url.isValid())
+ return false;
+
+ QCFType<CFURLRef> cfUrl = CFURLCreateWithString(0, QCFString(QString::fromLatin1(url.toEncoded())), 0);
+ if (cfUrl == 0)
+ return false;
+
+ const OSStatus err = LSOpenCFURLRef(cfUrl, 0);
+ return (err == noErr);
+}
+
+static bool launchWebBrowser(const QUrl &url)
+{
+ return lsOpen(url);
+}
+
+static bool openDocument(const QUrl &file)
+{
+ if (!file.isValid())
+ return false;
+
+ // LSOpen does not work in this case, use QProcess open instead.
+ return QProcess::startDetached(QLatin1String("open"), QStringList() << file.toLocalFile());
+}
+
+/*
+ Constructs a full unicode path from a FSRef.
+*/
+static QString getFullPath(const FSRef &ref)
+{
+ QByteArray ba(2048, 0);
+ if (FSRefMakePath(&ref, reinterpret_cast<UInt8 *>(ba.data()), ba.size()) == noErr)
+ return QString::fromUtf8(ba).normalized(QString::NormalizationForm_C);
+ return QString();
+}
+
+QString QDesktopServices::storageLocation(StandardLocation type)
+{
+ if (QDesktopServices::HomeLocation == type)
+ return QDir::homePath();
+
+ short domain = kOnAppropriateDisk;
+
+ if (QDesktopServices::DataLocation == type
+ || QDesktopServices::CacheLocation == type)
+ domain = kUserDomain;
+
+ // http://developer.apple.com/documentation/Carbon/Reference/Folder_Manager/Reference/reference.html
+ FSRef ref;
+ OSErr err = FSFindFolder(domain, translateLocation(type), false, &ref);
+ if (err)
+ return QString();
+
+ QString path = getFullPath(ref);
+
+ QString appName = QCoreApplication::applicationName();
+ if (!appName.isEmpty() &&
+ (QDesktopServices::DataLocation == type || QDesktopServices::CacheLocation == type))
+ path += QLatin1String("/") + appName;
+
+ return path;
+}
+
+QString QDesktopServices::displayName(StandardLocation type)
+{
+ if (QDesktopServices::HomeLocation == type)
+ return QObject::tr("Home");
+
+ FSRef ref;
+ OSErr err = FSFindFolder(kOnAppropriateDisk, translateLocation(type), false, &ref);
+ if (err)
+ return QString();
+
+ QCFString displayName;
+ err = LSCopyDisplayNameForRef(&ref, &displayName);
+ if (err)
+ return QString();
+
+ return static_cast<QString>(displayName);
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_DESKTOPSERVICES
diff --git a/src/gui/util/qdesktopservices_qws.cpp b/src/gui/util/qdesktopservices_qws.cpp
new file mode 100644
index 0000000000..f40010a902
--- /dev/null
+++ b/src/gui/util/qdesktopservices_qws.cpp
@@ -0,0 +1,93 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <qcoreapplication.h>
+#include <qdir.h>
+
+QT_BEGIN_NAMESPACE
+
+static bool launchWebBrowser(const QUrl &url)
+{
+ Q_UNUSED(url);
+ qWarning("QDesktopServices::launchWebBrowser not implemented");
+ return false;
+}
+
+static bool openDocument(const QUrl &file)
+{
+ Q_UNUSED(file);
+ qWarning("QDesktopServices::openDocument not implemented");
+ return false;
+}
+
+
+QString QDesktopServices::storageLocation(StandardLocation type)
+{
+ if (type == DataLocation) {
+ QString qwsDataHome = QLatin1String(qgetenv("QWS_DATA_HOME"));
+ if (qwsDataHome.isEmpty())
+ qwsDataHome = QDir::homePath() + QLatin1String("/.qws/share");
+ qwsDataHome += QLatin1String("/data/")
+ + QCoreApplication::organizationName() + QLatin1Char('/')
+ + QCoreApplication::applicationName();
+ return qwsDataHome;
+ }
+ if (type == QDesktopServices::CacheLocation) {
+ QString qwsCacheHome = QLatin1String(qgetenv("QWS_CACHE_HOME"));
+ if (qwsCacheHome.isEmpty())
+ qwsCacheHome = QDir::homePath() + QLatin1String("/.qws/cache/");
+ qwsCacheHome += QCoreApplication::organizationName() + QLatin1Char('/')
+ + QCoreApplication::applicationName();
+ return qwsCacheHome;
+ }
+
+ qWarning("QDesktopServices::storageLocation %d not implemented", type);
+ return QString();
+}
+
+QString QDesktopServices::displayName(StandardLocation type)
+{
+ Q_UNUSED(type);
+ qWarning("QDesktopServices::displayName not implemented");
+ return QString();
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/util/qdesktopservices_win.cpp b/src/gui/util/qdesktopservices_win.cpp
new file mode 100644
index 0000000000..0449cba7d6
--- /dev/null
+++ b/src/gui/util/qdesktopservices_win.cpp
@@ -0,0 +1,249 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <qsettings.h>
+#include <qdir.h>
+#include <qurl.h>
+#include <qstringlist.h>
+#include <qprocess.h>
+#include <qtemporaryfile.h>
+#include <qcoreapplication.h>
+
+#include <windows.h>
+#include <shlobj.h>
+#if !defined(Q_OS_WINCE)
+# include <intshcut.h>
+#else
+# include <qguifunctions_wince.h>
+# if !defined(STANDARDSHELL_UI_MODEL)
+# include <winx.h>
+# endif
+#endif
+
+#ifndef QT_NO_DESKTOPSERVICES
+
+QT_BEGIN_NAMESPACE
+
+//#undef UNICODE
+
+static bool openDocument(const QUrl &file)
+{
+ if (!file.isValid())
+ return false;
+
+ quintptr returnValue;
+ QT_WA({
+ returnValue = (quintptr)ShellExecute(0, 0, (TCHAR *)file.toString().utf16(), 0, 0, SW_SHOWNORMAL);
+ } , {
+ returnValue = (quintptr)ShellExecuteA(0, 0, file.toString().toLocal8Bit().constData(), 0, 0, SW_SHOWNORMAL);
+ });
+ return (returnValue > 32); //ShellExecute returns a value greater than 32 if successful
+}
+
+static QString expandEnvStrings(const QString &command)
+{
+
+#if defined(Q_OS_WINCE)
+ return command;
+#else
+ QByteArray path = command.toLocal8Bit();
+ char commandValue[2 * MAX_PATH] = {0};
+ DWORD returnValue = ExpandEnvironmentStringsA(path.data(), commandValue, MAX_PATH);
+ if (returnValue)
+ return QString::fromLocal8Bit(commandValue);
+ else
+ return command;
+#endif
+}
+
+static bool launchWebBrowser(const QUrl &url)
+{
+ if (url.scheme() == QLatin1String("mailto")) {
+ //Retrieve the commandline for the default mail client
+ //the key used below is the command line for the mailto: shell command
+ DWORD bufferSize = 2 * MAX_PATH;
+ long returnValue = -1;
+ QString command;
+
+ HKEY handle;
+ LONG res;
+ QT_WA ({
+ res = RegOpenKeyExW(HKEY_CLASSES_ROOT, L"mailto\\Shell\\Open\\Command", 0, KEY_READ, &handle);
+ if (res != ERROR_SUCCESS)
+ return false;
+
+ wchar_t keyValue[2 * MAX_PATH] = {0};
+ returnValue = RegQueryValueExW(handle, L"", 0, 0, reinterpret_cast<unsigned char*>(keyValue), &bufferSize);
+ if (!returnValue)
+ command = QString::fromRawData((QChar*)keyValue, bufferSize);
+ }, {
+ res = RegOpenKeyExA(HKEY_CLASSES_ROOT, "mailto\\Shell\\Open\\Command", 0, KEY_READ, &handle);
+ if (res != ERROR_SUCCESS)
+ return false;
+
+ char keyValue[2 * MAX_PATH] = {0};
+ returnValue = RegQueryValueExA(handle, "", 0, 0, reinterpret_cast<unsigned char*>(keyValue), &bufferSize);
+ if (!returnValue)
+ command = QString::fromLocal8Bit(keyValue);
+ });
+ RegCloseKey(handle);
+
+ if(returnValue)
+ return false;
+ command = expandEnvStrings(command);
+ command = command.trimmed();
+ //Make sure the path for the process is in quotes
+ int index = -1 ;
+ if (command[0]!= QLatin1Char('\"')) {
+ index = command.indexOf(QLatin1String(".exe "), 0, Qt::CaseInsensitive);
+ command.insert(index+4, QLatin1Char('\"'));
+ command.insert(0, QLatin1Char('\"'));
+ }
+ //pass the url as the parameter
+ index = command.lastIndexOf(QLatin1String("%1"));
+ if (index != -1){
+ command.replace(index, 2, url.toString());
+ }
+ //start the process
+ PROCESS_INFORMATION pi;
+ ZeroMemory(&pi, sizeof(pi));
+ QT_WA ({
+ STARTUPINFO si;
+ ZeroMemory(&si, sizeof(si));
+ si.cb = sizeof(si);
+
+ returnValue = CreateProcess(NULL, (TCHAR*)command.utf16(), NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi);
+ }, {
+ STARTUPINFOA si;
+ ZeroMemory(&si, sizeof(si));
+ si.cb = sizeof(si);
+
+ returnValue = CreateProcessA(NULL, command.toLocal8Bit().data(), NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi);
+ });
+
+ if (!returnValue)
+ return false;
+
+ CloseHandle(pi.hProcess);
+ CloseHandle(pi.hThread);
+ return true;
+ }
+
+ if (!url.isValid())
+ return false;
+
+ quintptr returnValue;
+ QT_WA ({
+ returnValue = (quintptr)ShellExecute(0, 0, (TCHAR *) QString::fromUtf8(url.toEncoded().constData()).utf16(), 0, 0, SW_SHOWNORMAL);
+ } , {
+ returnValue = (quintptr)ShellExecuteA(0, 0, url.toEncoded().constData(), 0, 0, SW_SHOWNORMAL);
+ });
+ return (returnValue > 32);
+}
+
+QString QDesktopServices::storageLocation(StandardLocation type)
+{
+#if !defined(QT_NO_SETTINGS)
+ QSettings settings(QSettings::UserScope, QLatin1String("Microsoft"), QLatin1String("Windows"));
+ settings.beginGroup(QLatin1String("CurrentVersion/Explorer/Shell Folders"));
+ switch (type) {
+ case CacheLocation:
+ // Although Microsoft has a Cache key it is a pointer to IE's cache, not a cache
+ // location for everyone. Most applications seem to be using a
+ // cache directory located in their AppData directory
+ return storageLocation(DataLocation) + QLatin1String("\\cache");
+ case DataLocation:
+ if (!settings.contains(QLatin1String("Local AppData")))
+ break;
+ return settings.value(QLatin1String("Local AppData")).toString()
+ + QLatin1String("\\") + QCoreApplication::organizationName()
+ + QLatin1String("\\") + QCoreApplication::applicationName();
+ break;
+ case DesktopLocation:
+ return settings.value(QLatin1String("Desktop")).toString();
+ break;
+
+ case DocumentsLocation:
+ return settings.value(QLatin1String("Personal")).toString();
+ break;
+
+ case FontsLocation:
+ return settings.value(QLatin1String("Fonts")).toString();
+ break;
+
+ case ApplicationsLocation:
+ return settings.value(QLatin1String("Programs")).toString();
+ break;
+
+ case MusicLocation:
+ return settings.value(QLatin1String("My Music")).toString();
+ break;
+
+ case MoviesLocation:
+ return settings.value(QLatin1String("My Video")).toString();
+ break;
+
+ case PicturesLocation:
+ return settings.value(QLatin1String("My Pictures")).toString();
+ break;
+
+ case QDesktopServices::HomeLocation:
+ return QDir::homePath(); break;
+
+ case QDesktopServices::TempLocation:
+ return QDir::tempPath(); break;
+
+ default:
+ break;
+ }
+#endif
+ return QString();
+}
+
+QString QDesktopServices::displayName(StandardLocation type)
+{
+ Q_UNUSED(type);
+ return QString();
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_DESKTOPSERVICES
diff --git a/src/gui/util/qdesktopservices_x11.cpp b/src/gui/util/qdesktopservices_x11.cpp
new file mode 100644
index 0000000000..b3486e8cf2
--- /dev/null
+++ b/src/gui/util/qdesktopservices_x11.cpp
@@ -0,0 +1,234 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qdesktopservices.h"
+
+#ifndef QT_NO_DESKTOPSERVICES
+
+#include <qprocess.h>
+#include <qurl.h>
+#include <qdir.h>
+#include <qfile.h>
+#include <qtextstream.h>
+#include <private/qt_x11_p.h>
+#include <qcoreapplication.h>
+#include <stdlib.h>
+
+QT_BEGIN_NAMESPACE
+
+inline static bool launch(const QUrl &url, const QString &client)
+{
+ return (QProcess::startDetached(client + QLatin1Char(' ') + QString::fromLatin1(url.toEncoded().constData())));
+}
+
+static bool openDocument(const QUrl &url)
+{
+ if (!url.isValid())
+ return false;
+
+ if (launch(url, QLatin1String("xdg-open")))
+ return true;
+
+ if (X11->desktopEnvironment == DE_GNOME && launch(url, QLatin1String("gnome-open"))) {
+ return true;
+ } else {
+ if (X11->desktopEnvironment == DE_KDE && launch(url, QLatin1String("kfmclient exec")))
+ return true;
+ }
+
+ if (launch(url, QLatin1String("firefox")))
+ return true;
+ if (launch(url, QLatin1String("mozilla")))
+ return true;
+ if (launch(url, QLatin1String("netscape")))
+ return true;
+ if (launch(url, QLatin1String("opera")))
+ return true;
+
+ return false;
+}
+
+static bool launchWebBrowser(const QUrl &url)
+{
+ if (!url.isValid())
+ return false;
+ if (url.scheme() == QLatin1String("mailto"))
+ return openDocument(url);
+
+ if (launch(url, QLatin1String("xdg-open")))
+ return true;
+ if (launch(url, QString::fromLocal8Bit(getenv("DEFAULT_BROWSER"))))
+ return true;
+ if (launch(url, QString::fromLocal8Bit(getenv("BROWSER"))))
+ return true;
+
+ if (X11->desktopEnvironment == DE_GNOME && launch(url, QLatin1String("gnome-open"))) {
+ return true;
+ } else {
+ if (X11->desktopEnvironment == DE_KDE && launch(url, QLatin1String("kfmclient openURL")))
+ return true;
+ }
+
+ if (launch(url, QLatin1String("firefox")))
+ return true;
+ if (launch(url, QLatin1String("mozilla")))
+ return true;
+ if (launch(url, QLatin1String("netscape")))
+ return true;
+ if (launch(url, QLatin1String("opera")))
+ return true;
+ return false;
+}
+
+
+
+QString QDesktopServices::storageLocation(StandardLocation type)
+{
+ if (type == QDesktopServices::HomeLocation)
+ return QDir::homePath();
+ if (type == QDesktopServices::TempLocation)
+ return QDir::tempPath();
+
+ // http://standards.freedesktop.org/basedir-spec/basedir-spec-0.6.html
+ if (type == QDesktopServices::CacheLocation) {
+ QString xdgCacheHome = QLatin1String(qgetenv("XDG_CACHE_HOME"));
+ if (xdgCacheHome.isEmpty())
+ xdgCacheHome = QDir::homePath() + QLatin1String("/.cache");
+ xdgCacheHome += QLatin1Char('/') + QCoreApplication::organizationName()
+ + QLatin1Char('/') + QCoreApplication::applicationName();
+ return xdgCacheHome;
+ }
+
+ if (type == QDesktopServices::DataLocation) {
+ QString xdgDataHome = QLatin1String(qgetenv("XDG_DATA_HOME"));
+ if (xdgDataHome.isEmpty())
+ xdgDataHome = QDir::homePath() + QLatin1String("/.local/share");
+ xdgDataHome += QLatin1String("/data/")
+ + QCoreApplication::organizationName() + QLatin1Char('/')
+ + QCoreApplication::applicationName();
+ return xdgDataHome;
+ }
+
+ // http://www.freedesktop.org/wiki/Software/xdg-user-dirs
+ QString xdgConfigHome = QLatin1String(qgetenv("XDG_CONFIG_HOME"));
+ if (xdgConfigHome.isEmpty())
+ xdgConfigHome = QDir::homePath() + QLatin1String("/.config");
+ QFile file(xdgConfigHome + QLatin1String("/user-dirs.dirs"));
+ if (file.exists() && file.open(QIODevice::ReadOnly)) {
+ QHash<QString, QString> lines;
+ QTextStream stream(&file);
+ // Only look for lines like: XDG_DESKTOP_DIR="$HOME/Desktop"
+ QRegExp exp(QLatin1String("^XDG_(.*)_DIR=(.*)$"));
+ while (!stream.atEnd()) {
+ QString line = stream.readLine();
+ if (exp.indexIn(line) != -1) {
+ QStringList lst = exp.capturedTexts();
+ QString key = lst.at(1);
+ QString value = lst.at(2);
+ if (value.length() > 2
+ && value.startsWith(QLatin1String("\""))
+ && value.endsWith(QLatin1String("\"")))
+ value = value.mid(1, value.length() - 2);
+ // Store the key and value: "DESKTOP", "$HOME/Desktop"
+ lines[key] = value;
+ }
+ }
+
+ QString key;
+ switch (type) {
+ case DesktopLocation: key = QLatin1String("DESKTOP"); break;
+ case DocumentsLocation: key = QLatin1String("DOCUMENTS"); break;
+ case PicturesLocation: key = QLatin1String("PICTURES"); break;
+ case MusicLocation: key = QLatin1String("MUSIC"); break;
+ case MoviesLocation: key = QLatin1String("VIDEOS"); break;
+ default: break;
+ }
+ if (!key.isEmpty() && lines.contains(key)) {
+ QString value = lines[key];
+ // value can start with $HOME
+ if (value.startsWith(QLatin1String("$HOME")))
+ value = QDir::homePath() + value.mid(5);
+ return value;
+ }
+ }
+
+ QDir emptyDir;
+ QString path;
+ switch (type) {
+ case DesktopLocation:
+ path = QDir::homePath() + QLatin1String("/Desktop");
+ break;
+ case DocumentsLocation:
+ path = QDir::homePath() + QLatin1String("/Documents");
+ break;
+ case PicturesLocation:
+ path = QDir::homePath() + QLatin1String("/Pictures");
+ break;
+
+ case FontsLocation:
+ path = QDir::homePath() + QLatin1String("/.fonts");
+ break;
+
+ case MusicLocation:
+ path = QDir::homePath() + QLatin1String("/Music");
+ break;
+
+ case MoviesLocation:
+ path = QDir::homePath() + QLatin1String("/Videos");
+ break;
+
+ case ApplicationsLocation:
+ default:
+ break;
+ }
+
+ return path;
+}
+
+QString QDesktopServices::displayName(StandardLocation type)
+{
+ Q_UNUSED(type);
+ return QString();
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_DESKTOPSERVICES
diff --git a/src/gui/util/qsystemtrayicon.cpp b/src/gui/util/qsystemtrayicon.cpp
new file mode 100644
index 0000000000..2e072c587d
--- /dev/null
+++ b/src/gui/util/qsystemtrayicon.cpp
@@ -0,0 +1,675 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qsystemtrayicon.h"
+#include "qsystemtrayicon_p.h"
+
+#ifndef QT_NO_SYSTEMTRAYICON
+
+#include "qmenu.h"
+#include "qevent.h"
+#include "qpoint.h"
+#include "qlabel.h"
+#include "qpushbutton.h"
+#include "qpainterpath.h"
+#include "qpainter.h"
+#include "qstyle.h"
+#include "qgridlayout.h"
+#include "qapplication.h"
+#include "qdesktopwidget.h"
+#include "qbitmap.h"
+#include "private/qlabel_p.h"
+#include "qapplication.h"
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QSystemTrayIcon
+ \brief The QSystemTrayIcon class provides an icon for an application in the system tray.
+ \since 4.2
+ \ingroup application
+ \ingroup desktop
+
+ Modern operating systems usually provide a special area on the desktop,
+ called the \e{system tray} or \e{notification area}, where long-running
+ applications can display icons and short messages.
+
+ \image system-tray.png The system tray on Windows XP.
+
+ The QSystemTrayIcon class can be used on the following platforms:
+
+ \list
+ \o All supported versions of Windows.
+ \o All window managers for X11 that implement the \l{freedesktop.org} system
+ tray specification, including recent versions of KDE and GNOME.
+ \o All supported versions of Mac OS X. Note that the Growl
+ notification system must be installed for
+ QSystemTrayIcon::showMessage() to display messages.
+ \endlist
+
+ To check whether a system tray is present on the user's desktop,
+ call the QSystemTrayIcon::isSystemTrayAvailable() static function.
+
+ To add a system tray entry, create a QSystemTrayIcon object, call setContextMenu()
+ to provide a context menu for the icon, and call show() to make it visible in the
+ system tray. Status notification messages ("balloon messages") can be displayed at
+ any time using showMessage().
+
+ If the system tray is unavailable when a system tray icon is constructed, but
+ becomes available later, QSystemTrayIcon will automatically add an entry for the
+ application in the system tray if the icon is \l visible.
+
+ The activated() signal is emitted when the user activates the icon.
+
+ Only on X11, when a tooltip is requested, the QSystemTrayIcon receives a QHelpEvent
+ of type QEvent::ToolTip. Additionally, the QSystemTrayIcon receives wheel events of
+ type QEvent::Wheel. These are not supported on any other platform.
+
+ \sa QDesktopServices, QDesktopWidget, {Desktop Integration}, {System Tray Icon Example}
+*/
+
+/*!
+ \enum QSystemTrayIcon::MessageIcon
+
+ This enum describes the icon that is shown when a balloon message is displayed.
+
+ \value NoIcon No icon is shown.
+ \value Information An information icon is shown.
+ \value Warning A standard warning icon is shown.
+ \value Critical A critical warning icon is shown.
+
+ \sa QMessageBox
+*/
+
+/*!
+ Constructs a QSystemTrayIcon object with the given \a parent.
+
+ The icon is initially invisible.
+
+ \sa visible
+*/
+QSystemTrayIcon::QSystemTrayIcon(QObject *parent)
+: QObject(*new QSystemTrayIconPrivate(), parent)
+{
+}
+
+/*!
+ Constructs a QSystemTrayIcon object with the given \a icon and \a parent.
+
+ The icon is initially invisible.
+
+ \sa visible
+*/
+QSystemTrayIcon::QSystemTrayIcon(const QIcon &icon, QObject *parent)
+: QObject(*new QSystemTrayIconPrivate(), parent)
+{
+ setIcon(icon);
+}
+
+/*!
+ Removes the icon from the system tray and frees all allocated resources.
+*/
+QSystemTrayIcon::~QSystemTrayIcon()
+{
+ Q_D(QSystemTrayIcon);
+ d->remove_sys();
+}
+
+#ifndef QT_NO_MENU
+
+/*!
+ Sets the specified \a menu to be the context menu for the system tray icon.
+
+ The menu will pop up when the user requests the context menu for the system
+ tray icon by clicking the mouse button.
+
+ On Mac OS X, this is currenly converted to a NSMenu, so the
+ aboutToHide() signal is not emitted.
+
+ \note The system tray icon does not take ownership of the menu. You must
+ ensure that it is deleted at the appropriate time by, for example, creating
+ the menu with a suitable parent object.
+*/
+void QSystemTrayIcon::setContextMenu(QMenu *menu)
+{
+ Q_D(QSystemTrayIcon);
+ d->menu = menu;
+ d->updateMenu_sys();
+}
+
+/*!
+ Returns the current context menu for the system tray entry.
+*/
+QMenu* QSystemTrayIcon::contextMenu() const
+{
+ Q_D(const QSystemTrayIcon);
+ return d->menu;
+}
+
+#endif // QT_NO_MENU
+
+/*!
+ \property QSystemTrayIcon::icon
+ \brief the system tray icon
+
+ On Windows, the system tray icon size is 16x16; on X11, the preferred size is
+ 22x22. The icon will be scaled to the appropriate size as necessary.
+*/
+void QSystemTrayIcon::setIcon(const QIcon &icon)
+{
+ Q_D(QSystemTrayIcon);
+ d->icon = icon;
+ d->updateIcon_sys();
+}
+
+QIcon QSystemTrayIcon::icon() const
+{
+ Q_D(const QSystemTrayIcon);
+ return d->icon;
+}
+
+/*!
+ \property QSystemTrayIcon::toolTip
+ \brief the tooltip for the system tray entry
+
+ On some systems, the tooltip's length is limited. The tooltip will be truncated
+ if necessary.
+*/
+void QSystemTrayIcon::setToolTip(const QString &tooltip)
+{
+ Q_D(QSystemTrayIcon);
+ d->toolTip = tooltip;
+ d->updateToolTip_sys();
+}
+
+QString QSystemTrayIcon::toolTip() const
+{
+ Q_D(const QSystemTrayIcon);
+ return d->toolTip;
+}
+
+/*!
+ \fn void QSystemTrayIcon::show()
+
+ Shows the icon in the system tray.
+
+ \sa hide(), visible
+*/
+
+/*!
+ \fn void QSystemTrayIcon::hide()
+
+ Hides the system tray entry.
+
+ \sa show(), visible
+*/
+
+/*!
+ \since 4.3
+ Returns the geometry of the system tray icon in screen coordinates.
+
+ \sa visible
+*/
+QRect QSystemTrayIcon::geometry() const
+{
+ Q_D(const QSystemTrayIcon);
+ if (!d->visible)
+ return QRect();
+ return d->geometry_sys();
+}
+
+/*!
+ \property QSystemTrayIcon::visible
+ \brief whether the system tray entry is visible
+
+ Setting this property to true or calling show() makes the system tray icon
+ visible; setting this property to false or calling hide() hides it.
+*/
+void QSystemTrayIcon::setVisible(bool visible)
+{
+ Q_D(QSystemTrayIcon);
+ if (visible == d->visible)
+ return;
+ if (d->icon.isNull() && visible)
+ qWarning("QSystemTrayIcon::setVisible: No Icon set");
+ d->visible = visible;
+ if (d->visible)
+ d->install_sys();
+ else
+ d->remove_sys();
+}
+
+bool QSystemTrayIcon::isVisible() const
+{
+ Q_D(const QSystemTrayIcon);
+ return d->visible;
+}
+
+/*!
+ \reimp
+*/
+bool QSystemTrayIcon::event(QEvent *e)
+{
+#if defined(Q_WS_X11)
+ if (e->type() == QEvent::ToolTip) {
+ Q_D(QSystemTrayIcon);
+ return d->sys->deliverToolTipEvent(e);
+ }
+#endif
+ return QObject::event(e);
+}
+
+/*!
+ \enum QSystemTrayIcon::ActivationReason
+
+ This enum describes the reason the system tray was activated.
+
+ \value Unknown Unknown reason
+ \value Context The context menu for the system tray entry was requested
+ \value DoubleClick The system tray entry was double clicked
+ \value Trigger The system tray entry was clicked
+ \value MiddleClick The system tray entry was clicked with the middle mouse button
+
+ \sa activated()
+*/
+
+/*!
+ \fn void QSystemTrayIcon::activated(QSystemTrayIcon::ActivationReason reason)
+
+ This signal is emitted when the user activates the system tray icon. \a reason
+ specifies the reason for activation. QSystemTrayIcon::ActivationReason enumerates
+ the various reasons.
+
+ \sa QSystemTrayIcon::ActivationReason
+*/
+
+/*!
+ \fn void QSystemTrayIcon::messageClicked()
+
+ This signal is emitted when the message displayed using showMessage()
+ was clicked by the user.
+
+ Currently this signal is not sent on Mac OS X.
+
+ \note We follow Microsoft Windows XP/Vista behavior, so the
+ signal is also emitted when the user clicks on a tray icon with
+ a balloon message displayed.
+
+ \sa activated()
+*/
+
+
+/*!
+ Returns true if the system tray is available; otherwise returns false.
+
+ If the system tray is currently unavailable but becomes available later,
+ QSystemTrayIcon will automatically add an entry in the system tray if it
+ is \l visible.
+*/
+
+bool QSystemTrayIcon::isSystemTrayAvailable()
+{
+ return QSystemTrayIconPrivate::isSystemTrayAvailable_sys();
+}
+
+/*!
+ Returns true if the system tray supports balloon messages; otherwise returns false.
+
+ \sa showMessage()
+*/
+bool QSystemTrayIcon::supportsMessages()
+{
+#if defined(Q_WS_QWS)
+ return false;
+#endif
+ return true;
+}
+
+/*!
+ \fn void QSystemTrayIcon::showMessage(const QString &title, const QString &message, MessageIcon icon, int millisecondsTimeoutHint)
+ \since 4.3
+
+ Shows a balloon message for the entry with the given \a title, \a message and
+ \a icon for the time specified in \a millisecondsTimeoutHint. \a title and \a message
+ must be plain text strings.
+
+ Message can be clicked by the user; the messageClicked() signal will emitted when
+ this occurs.
+
+ Note that display of messages are dependent on the system configuration and user
+ preferences, and that messages may not appear at all. Hence, it should not be
+ relied upon as the sole means for providing critical information.
+
+ On Windows, the \a millisecondsTimeoutHint is usually ignored by the system
+ when the application has focus.
+
+ \sa show() supportsMessages()
+ */
+void QSystemTrayIcon::showMessage(const QString& title, const QString& msg,
+ QSystemTrayIcon::MessageIcon icon, int msecs)
+{
+ Q_D(QSystemTrayIcon);
+ if (d->visible)
+ d->showMessage_sys(title, msg, icon, msecs);
+}
+
+//////////////////////////////////////////////////////////////////////
+static QBalloonTip *theSolitaryBalloonTip = 0;
+
+void QBalloonTip::showBalloon(QSystemTrayIcon::MessageIcon icon, const QString& title,
+ const QString& message, QSystemTrayIcon *trayIcon,
+ const QPoint& pos, int timeout, bool showArrow)
+{
+ hideBalloon();
+ if (message.isEmpty() && title.isEmpty())
+ return;
+
+ theSolitaryBalloonTip = new QBalloonTip(icon, title, message, trayIcon);
+ if (timeout < 0)
+ timeout = 10000; //10 s default
+ theSolitaryBalloonTip->balloon(pos, timeout, showArrow);
+}
+
+void QBalloonTip::hideBalloon()
+{
+ if (!theSolitaryBalloonTip)
+ return;
+ theSolitaryBalloonTip->hide();
+ delete theSolitaryBalloonTip;
+ theSolitaryBalloonTip = 0;
+}
+
+bool QBalloonTip::isBalloonVisible()
+{
+ return theSolitaryBalloonTip;
+}
+
+QBalloonTip::QBalloonTip(QSystemTrayIcon::MessageIcon icon, const QString& title,
+ const QString& message, QSystemTrayIcon *ti)
+ : QWidget(0, Qt::ToolTip), trayIcon(ti), timerId(-1)
+{
+ setAttribute(Qt::WA_DeleteOnClose);
+ QObject::connect(ti, SIGNAL(destroyed()), this, SLOT(close()));
+
+ QLabel *titleLabel = new QLabel;
+ titleLabel->installEventFilter(this);
+ titleLabel->setText(title);
+ QFont f = titleLabel->font();
+ f.setBold(true);
+#ifdef Q_OS_WINCE
+ f.setPointSize(f.pointSize() - 2);
+#endif
+ titleLabel->setFont(f);
+ titleLabel->setTextFormat(Qt::PlainText); // to maintain compat with windows
+
+#ifdef Q_OS_WINCE
+ const int iconSize = style()->pixelMetric(QStyle::PM_SmallIconSize);
+ const int closeButtonSize = style()->pixelMetric(QStyle::PM_SmallIconSize) - 2;
+#else
+ const int iconSize = 18;
+ const int closeButtonSize = 15;
+#endif
+
+ QPushButton *closeButton = new QPushButton;
+ closeButton->setIcon(style()->standardIcon(QStyle::SP_TitleBarCloseButton));
+ closeButton->setIconSize(QSize(closeButtonSize, closeButtonSize));
+ closeButton->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
+ closeButton->setFixedSize(closeButtonSize, closeButtonSize);
+ QObject::connect(closeButton, SIGNAL(clicked()), this, SLOT(close()));
+
+ QLabel *msgLabel = new QLabel;
+#ifdef Q_OS_WINCE
+ f.setBold(false);
+ msgLabel->setFont(f);
+#endif
+ msgLabel->installEventFilter(this);
+ msgLabel->setText(message);
+ msgLabel->setTextFormat(Qt::PlainText);
+ msgLabel->setAlignment(Qt::AlignTop | Qt::AlignLeft);
+
+ // smart size for the message label
+#ifdef Q_OS_WINCE
+ int limit = QApplication::desktop()->availableGeometry(msgLabel).size().width() / 2;
+#else
+ int limit = QApplication::desktop()->availableGeometry(msgLabel).size().width() / 3;
+#endif
+ if (msgLabel->sizeHint().width() > limit) {
+ msgLabel->setWordWrap(true);
+ if (msgLabel->sizeHint().width() > limit) {
+ msgLabel->d_func()->ensureTextControl();
+ if (QTextControl *control = msgLabel->d_func()->control) {
+ QTextOption opt = control->document()->defaultTextOption();
+ opt.setWrapMode(QTextOption::WrapAnywhere);
+ control->document()->setDefaultTextOption(opt);
+ }
+ }
+#ifdef Q_OS_WINCE
+ // Make sure that the text isn't wrapped "somewhere" in the balloon widget
+ // in the case that we have a long title label.
+ setMaximumWidth(limit);
+#else
+ // Here we allow the text being much smaller than the balloon widget
+ // to emulate the weird standard windows behavior.
+ msgLabel->setFixedSize(limit, msgLabel->heightForWidth(limit));
+#endif
+ }
+
+ QIcon si;
+ switch (icon) {
+ case QSystemTrayIcon::Warning:
+ si = style()->standardIcon(QStyle::SP_MessageBoxWarning);
+ break;
+ case QSystemTrayIcon::Critical:
+ si = style()->standardIcon(QStyle::SP_MessageBoxCritical);
+ break;
+ case QSystemTrayIcon::Information:
+ si = style()->standardIcon(QStyle::SP_MessageBoxInformation);
+ break;
+ case QSystemTrayIcon::NoIcon:
+ default:
+ break;
+ }
+
+ QGridLayout *layout = new QGridLayout;
+ if (!si.isNull()) {
+ QLabel *iconLabel = new QLabel;
+ iconLabel->setPixmap(si.pixmap(iconSize, iconSize));
+ iconLabel->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
+ iconLabel->setMargin(2);
+ layout->addWidget(iconLabel, 0, 0);
+ layout->addWidget(titleLabel, 0, 1);
+ } else {
+ layout->addWidget(titleLabel, 0, 0, 1, 2);
+ }
+
+ layout->addWidget(closeButton, 0, 2);
+ layout->addWidget(msgLabel, 1, 0, 1, 3);
+ layout->setSizeConstraint(QLayout::SetFixedSize);
+ layout->setMargin(3);
+ setLayout(layout);
+
+ QPalette pal = palette();
+ pal.setColor(QPalette::Window, QColor(0xff, 0xff, 0xe1));
+ pal.setColor(QPalette::WindowText, Qt::black);
+ setPalette(pal);
+}
+
+QBalloonTip::~QBalloonTip()
+{
+ theSolitaryBalloonTip = 0;
+}
+
+void QBalloonTip::paintEvent(QPaintEvent *)
+{
+ QPainter painter(this);
+ painter.drawPixmap(rect(), pixmap);
+}
+
+void QBalloonTip::resizeEvent(QResizeEvent *ev)
+{
+ QWidget::resizeEvent(ev);
+}
+
+void QBalloonTip::balloon(const QPoint& pos, int msecs, bool showArrow)
+{
+ QRect scr = QApplication::desktop()->screenGeometry(pos);
+ QSize sh = sizeHint();
+ const int border = 1;
+ const int ah = 18, ao = 18, aw = 18, rc = 7;
+ bool arrowAtTop = (pos.y() + sh.height() + ah < scr.height());
+ bool arrowAtLeft = (pos.x() + sh.width() - ao < scr.width());
+ setContentsMargins(border + 3, border + (arrowAtTop ? ah : 0) + 2, border + 3, border + (arrowAtTop ? 0 : ah) + 2);
+ updateGeometry();
+ sh = sizeHint();
+
+ int ml, mr, mt, mb;
+ QSize sz = sizeHint();
+ if (!arrowAtTop) {
+ ml = mt = 0;
+ mr = sz.width() - 1;
+ mb = sz.height() - ah - 1;
+ } else {
+ ml = 0;
+ mt = ah;
+ mr = sz.width() - 1;
+ mb = sz.height() - 1;
+ }
+
+ QPainterPath path;
+#if defined(QT_NO_XSHAPE) && defined(Q_WS_X11)
+ // XShape is required for setting the mask, so we just
+ // draw an ugly square when its not available
+ path.moveTo(0, 0);
+ path.lineTo(sz.width() - 1, 0);
+ path.lineTo(sz.width() - 1, sz.height() - 1);
+ path.lineTo(0, sz.height() - 1);
+ path.lineTo(0, 0);
+ move(qMax(pos.x() - sz.width(), scr.left()), pos.y());
+#else
+ path.moveTo(ml + rc, mt);
+ if (arrowAtTop && arrowAtLeft) {
+ if (showArrow) {
+ path.lineTo(ml + ao, mt);
+ path.lineTo(ml + ao, mt - ah);
+ path.lineTo(ml + ao + aw, mt);
+ }
+ move(qMax(pos.x() - ao, scr.left() + 2), pos.y());
+ } else if (arrowAtTop && !arrowAtLeft) {
+ if (showArrow) {
+ path.lineTo(mr - ao - aw, mt);
+ path.lineTo(mr - ao, mt - ah);
+ path.lineTo(mr - ao, mt);
+ }
+ move(qMin(pos.x() - sh.width() + ao, scr.right() - sh.width() - 2), pos.y());
+ }
+ path.lineTo(mr - rc, mt);
+ path.arcTo(QRect(mr - rc*2, mt, rc*2, rc*2), 90, -90);
+ path.lineTo(mr, mb - rc);
+ path.arcTo(QRect(mr - rc*2, mb - rc*2, rc*2, rc*2), 0, -90);
+ if (!arrowAtTop && !arrowAtLeft) {
+ if (showArrow) {
+ path.lineTo(mr - ao, mb);
+ path.lineTo(mr - ao, mb + ah);
+ path.lineTo(mr - ao - aw, mb);
+ }
+ move(qMin(pos.x() - sh.width() + ao, scr.right() - sh.width() - 2),
+ pos.y() - sh.height());
+ } else if (!arrowAtTop && arrowAtLeft) {
+ if (showArrow) {
+ path.lineTo(ao + aw, mb);
+ path.lineTo(ao, mb + ah);
+ path.lineTo(ao, mb);
+ }
+ move(qMax(pos.x() - ao, scr.x() + 2), pos.y() - sh.height());
+ }
+ path.lineTo(ml + rc, mb);
+ path.arcTo(QRect(ml, mb - rc*2, rc*2, rc*2), -90, -90);
+ path.lineTo(ml, mt + rc);
+ path.arcTo(QRect(ml, mt, rc*2, rc*2), 180, -90);
+
+ // Set the mask
+ QBitmap bitmap = QBitmap(sizeHint());
+ bitmap.fill(Qt::color0);
+ QPainter painter1(&bitmap);
+ painter1.setPen(QPen(Qt::color1, border));
+ painter1.setBrush(QBrush(Qt::color1));
+ painter1.drawPath(path);
+ setMask(bitmap);
+#endif
+
+ // Draw the border
+ pixmap = QPixmap(sz);
+ QPainter painter2(&pixmap);
+ painter2.setPen(QPen(palette().color(QPalette::Window).darker(160), border));
+ painter2.setBrush(palette().color(QPalette::Window));
+ painter2.drawPath(path);
+
+ if (msecs > 0)
+ timerId = startTimer(msecs);
+ show();
+}
+
+void QBalloonTip::mousePressEvent(QMouseEvent *e)
+{
+ close();
+ if(e->button() == Qt::LeftButton)
+ emit trayIcon->messageClicked();
+}
+
+void QBalloonTip::timerEvent(QTimerEvent *e)
+{
+ if (e->timerId() == timerId) {
+ killTimer(timerId);
+ if (!underMouse())
+ close();
+ return;
+ }
+ QWidget::timerEvent(e);
+}
+
+void qtsystray_sendActivated(QSystemTrayIcon *i, int r)
+{
+ emit i->activated((QSystemTrayIcon::ActivationReason)r);
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_SYSTEMTRAYICON
diff --git a/src/gui/util/qsystemtrayicon.h b/src/gui/util/qsystemtrayicon.h
new file mode 100644
index 0000000000..2845717e26
--- /dev/null
+++ b/src/gui/util/qsystemtrayicon.h
@@ -0,0 +1,132 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSYSTEMTRAYICON_H
+#define QSYSTEMTRAYICON_H
+
+#include <QtCore/qobject.h>
+
+#ifndef QT_NO_SYSTEMTRAYICON
+
+#include <QtGui/qicon.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+class QSystemTrayIconPrivate;
+
+class QMenu;
+class QEvent;
+class QWheelEvent;
+class QMouseEvent;
+class QPoint;
+
+class Q_GUI_EXPORT QSystemTrayIcon : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(QString toolTip READ toolTip WRITE setToolTip)
+ Q_PROPERTY(QIcon icon READ icon WRITE setIcon)
+ Q_PROPERTY(bool visible READ isVisible WRITE setVisible DESIGNABLE false)
+
+public:
+ QSystemTrayIcon(QObject *parent = 0);
+ QSystemTrayIcon(const QIcon &icon, QObject *parent = 0);
+ ~QSystemTrayIcon();
+
+ enum ActivationReason {
+ Unknown,
+ Context,
+ DoubleClick,
+ Trigger,
+ MiddleClick
+ };
+
+#ifndef QT_NO_MENU
+ void setContextMenu(QMenu *menu);
+ QMenu *contextMenu() const;
+#endif
+
+ QIcon icon() const;
+ void setIcon(const QIcon &icon);
+
+ QString toolTip() const;
+ void setToolTip(const QString &tip);
+
+ static bool isSystemTrayAvailable();
+ static bool supportsMessages();
+
+ enum MessageIcon { NoIcon, Information, Warning, Critical };
+ void showMessage(const QString &title, const QString &msg,
+ MessageIcon icon = Information, int msecs = 10000);
+
+ QRect geometry() const;
+ bool isVisible() const;
+
+public Q_SLOTS:
+ void setVisible(bool visible);
+ inline void show() { setVisible(true); }
+ inline void hide() { setVisible(false); }
+
+Q_SIGNALS:
+ void activated(QSystemTrayIcon::ActivationReason reason);
+ void messageClicked();
+
+protected:
+ bool event(QEvent *event);
+
+private:
+ Q_DISABLE_COPY(QSystemTrayIcon)
+ Q_DECLARE_PRIVATE(QSystemTrayIcon)
+
+ friend class QSystemTrayIconSys;
+ friend class QBalloonTip;
+ friend void qtsystray_sendActivated(QSystemTrayIcon *, int);
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QT_NO_SYSTEMTRAYICON
+#endif // QSYSTEMTRAYICON_H
diff --git a/src/gui/util/qsystemtrayicon_mac.mm b/src/gui/util/qsystemtrayicon_mac.mm
new file mode 100644
index 0000000000..f6e858a534
--- /dev/null
+++ b/src/gui/util/qsystemtrayicon_mac.mm
@@ -0,0 +1,547 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/****************************************************************************
+**
+** Copyright (c) 2007-2008, Apple, Inc.
+**
+** All rights reserved.
+**
+** Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are met:
+**
+** * Redistributions of source code must retain the above copyright notice,
+** this list of conditions and the following disclaimer.
+**
+** * Redistributions in binary form must reproduce the above copyright notice,
+** this list of conditions and the following disclaimer in the documentation
+** and/or other materials provided with the distribution.
+**
+** * Neither the name of Apple, Inc. nor the names of its contributors
+** may be used to endorse or promote products derived from this software
+** without specific prior written permission.
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+** CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+** EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+** PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+** PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+** LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+** NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+** SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+**
+****************************************************************************/
+
+#define QT_MAC_SYSTEMTRAY_USE_GROWL
+
+@class QNSMenu;
+
+#include <private/qt_cocoa_helpers_mac_p.h>
+#include <private/qsystemtrayicon_p.h>
+#include <qtemporaryfile.h>
+#include <qimagewriter.h>
+#include <qapplication.h>
+#include <qdebug.h>
+#include <qstyle.h>
+
+#include <private/qt_mac_p.h>
+#import <AppKit/AppKit.h>
+
+QT_BEGIN_NAMESPACE
+extern bool qt_mac_execute_apple_script(const QString &script, AEDesc *ret); //qapplication_mac.cpp
+extern void qtsystray_sendActivated(QSystemTrayIcon *i, int r); //qsystemtrayicon.cpp
+extern NSString *keySequenceToKeyEqivalent(const QKeySequence &accel); // qmenu_mac.mm
+extern NSUInteger keySequenceModifierMask(const QKeySequence &accel); // qmenu_mac.mm
+QT_END_NAMESPACE
+
+QT_USE_NAMESPACE
+
+@class QNSImageView;
+
+@interface QNSStatusItem : NSObject {
+ NSStatusItem *item;
+ QSystemTrayIcon *icon;
+ QSystemTrayIconPrivate *iconPrivate;
+ QNSImageView *imageCell;
+}
+-(id)initWithIcon:(QSystemTrayIcon*)icon iconPrivate:(QSystemTrayIconPrivate *)iprivate;
+-(void)dealloc;
+-(QSystemTrayIcon*)icon;
+-(NSStatusItem*)item;
+-(QRectF)geometry;
+- (void)triggerSelector:(id)sender;
+- (void)doubleClickSelector:(id)sender;
+@end
+
+@interface QNSImageView : NSImageView {
+ BOOL down;
+ QNSStatusItem *parent;
+}
+-(id)initWithParent:(QNSStatusItem*)myParent;
+-(QSystemTrayIcon*)icon;
+-(void)menuTrackingDone:(NSNotification*)notification;
+-(void)mousePressed:(NSEvent *)mouseEvent;
+@end
+
+@interface QNSMenu : NSMenu {
+ QMenu *qmenu;
+}
+-(QMenu*)menu;
+-(id)initWithQMenu:(QMenu*)qmenu;
+-(void)menuNeedsUpdate:(QNSMenu*)menu;
+-(void)selectedAction:(id)item;
+@end
+
+QT_BEGIN_NAMESPACE
+class QSystemTrayIconSys
+{
+public:
+ QSystemTrayIconSys(QSystemTrayIcon *icon, QSystemTrayIconPrivate *d) {
+ QMacCocoaAutoReleasePool pool;
+ item = [[QNSStatusItem alloc] initWithIcon:icon iconPrivate:d];
+ }
+ ~QSystemTrayIconSys() {
+ QMacCocoaAutoReleasePool pool;
+ [[[item item] view] setHidden: YES];
+ [item release];
+ }
+ QNSStatusItem *item;
+};
+
+void QSystemTrayIconPrivate::install_sys()
+{
+ Q_Q(QSystemTrayIcon);
+ if (!sys) {
+ sys = new QSystemTrayIconSys(q, this);
+ updateIcon_sys();
+ updateMenu_sys();
+ updateToolTip_sys();
+ }
+}
+
+QRect QSystemTrayIconPrivate::geometry_sys() const
+{
+ if(sys) {
+ const QRectF geom = [sys->item geometry];
+ if(!geom.isNull())
+ return geom.toRect();
+ }
+ return QRect();
+}
+
+void QSystemTrayIconPrivate::remove_sys()
+{
+ delete sys;
+ sys = 0;
+}
+
+void QSystemTrayIconPrivate::updateIcon_sys()
+{
+ if(sys && !icon.isNull()) {
+ QMacCocoaAutoReleasePool pool;
+#ifndef QT_MAC_USE_COCOA
+ const short scale = GetMBarHeight()-4;
+#else
+ CGFloat hgt = [[[NSApplication sharedApplication] mainMenu] menuBarHeight];
+ const short scale = hgt - 4;
+#endif
+ NSImage *nsimage = static_cast<NSImage *>(qt_mac_create_nsimage(icon.pixmap(QSize(scale, scale))));
+ [(NSImageView*)[[sys->item item] view] setImage: nsimage];
+ [nsimage release];
+ }
+}
+
+void QSystemTrayIconPrivate::updateMenu_sys()
+{
+ if(sys) {
+ QMacCocoaAutoReleasePool pool;
+ if(menu && !menu->isEmpty()) {
+ [[sys->item item] setHighlightMode:YES];
+ } else {
+ [[sys->item item] setHighlightMode:NO];
+ }
+ }
+}
+
+void QSystemTrayIconPrivate::updateToolTip_sys()
+{
+ if(sys) {
+ QMacCocoaAutoReleasePool pool;
+ QCFString string(toolTip);
+ [[[sys->item item] view] setToolTip:(NSString*)static_cast<CFStringRef>(string)];
+ }
+}
+
+bool QSystemTrayIconPrivate::isSystemTrayAvailable_sys()
+{
+ return true;
+}
+
+void QSystemTrayIconPrivate::showMessage_sys(const QString &title, const QString &message, QSystemTrayIcon::MessageIcon icon, int)
+{
+
+ if(sys) {
+#ifdef QT_MAC_SYSTEMTRAY_USE_GROWL
+ // Make sure that we have Growl installed on the machine we are running on.
+ QCFType<CFURLRef> cfurl;
+ OSStatus status = LSGetApplicationForInfo(kLSUnknownType, kLSUnknownCreator,
+ CFSTR("growlTicket"), kLSRolesAll, 0, &cfurl);
+ if (status == kLSApplicationNotFoundErr)
+ return;
+ QCFType<CFBundleRef> bundle = CFBundleCreate(0, cfurl);
+
+ if (CFStringCompare(CFBundleGetIdentifier(bundle), CFSTR("com.Growl.GrowlHelperApp"),
+ kCFCompareCaseInsensitive | kCFCompareBackwards) != kCFCompareEqualTo)
+ return;
+ QPixmap notificationIconPixmap;
+ if(icon == QSystemTrayIcon::Information)
+ notificationIconPixmap = QApplication::style()->standardPixmap(QStyle::SP_MessageBoxInformation);
+ else if(icon == QSystemTrayIcon::Warning)
+ notificationIconPixmap = QApplication::style()->standardPixmap(QStyle::SP_MessageBoxWarning);
+ else if(icon == QSystemTrayIcon::Critical)
+ notificationIconPixmap = QApplication::style()->standardPixmap(QStyle::SP_MessageBoxCritical);
+ QTemporaryFile notificationIconFile;
+ QString notificationType(QLatin1String("Notification")), notificationIcon, notificationApp(QApplication::applicationName());
+ if(notificationApp.isEmpty())
+ notificationApp = QLatin1String("Application");
+ if(!notificationIconPixmap.isNull() && notificationIconFile.open()) {
+ QImageWriter writer(&notificationIconFile, "PNG");
+ if(writer.write(notificationIconPixmap.toImage()))
+ notificationIcon = QLatin1String("image from location \"file://") + notificationIconFile.fileName() + QLatin1String("\"");
+ }
+ const QString script(QLatin1String(
+ "tell application \"GrowlHelperApp\"\n"
+ "-- Make a list of all the notification types (all)\n"
+ "set the allNotificationsList to {\"") + notificationType + QLatin1String("\"}\n"
+
+ "-- Make a list of the notifications (enabled)\n"
+ "set the enabledNotificationsList to {\"") + notificationType + QLatin1String("\"}\n"
+
+ "-- Register our script with growl.\n"
+ "register as application \"") + notificationApp + QLatin1String("\" all notifications allNotificationsList default notifications enabledNotificationsList\n"
+
+ "-- Send a Notification...\n") +
+ QLatin1String("notify with name \"") + notificationType +
+ QLatin1String("\" title \"") + title +
+ QLatin1String("\" description \"") + message +
+ QLatin1String("\" application name \"") + notificationApp +
+ QLatin1String("\" ") + notificationIcon +
+ QLatin1String("\nend tell"));
+ qt_mac_execute_apple_script(script, 0);
+#elif 0
+ Q_Q(QSystemTrayIcon);
+ NSView *v = [[sys->item item] view];
+ NSWindow *w = [v window];
+ w = [[sys->item item] window];
+ qDebug() << w << v;
+ QPoint p(qRound([w frame].origin.x), qRound([w frame].origin.y));
+ qDebug() << p;
+ QBalloonTip::showBalloon(icon, message, title, q, QPoint(0, 0), msecs);
+#else
+ Q_UNUSED(icon);
+ Q_UNUSED(title);
+ Q_UNUSED(message);
+#endif
+ }
+}
+QT_END_NAMESPACE
+
+@implementation NSStatusItem (Qt)
+@end
+
+@implementation QNSImageView
+-(id)initWithParent:(QNSStatusItem*)myParent {
+ self = [super init];
+ parent = myParent;
+ down = NO;
+ return self;
+}
+
+-(QSystemTrayIcon*)icon {
+ return [parent icon];
+}
+
+-(void)menuTrackingDone:(NSNotification*)notification
+{
+ Q_UNUSED(notification);
+ down = NO;
+ if([self icon]->contextMenu())
+ [self icon]->contextMenu()->hide();
+ [self setNeedsDisplay:YES];
+}
+
+-(void)mousePressed:(NSEvent *)mouseEvent
+{
+ int clickCount = [mouseEvent clickCount];
+ down = !down;
+ if(!down && [self icon]->contextMenu())
+ [self icon]->contextMenu()->hide();
+ [self setNeedsDisplay:YES];
+
+ if (down)
+ [parent triggerSelector:self];
+ else if ((clickCount%2))
+ [parent doubleClickSelector:self];
+ while (down) {
+ mouseEvent = [[self window] nextEventMatchingMask:NSLeftMouseDownMask | NSLeftMouseUpMask
+ | NSLeftMouseDraggedMask | NSRightMouseDownMask | NSRightMouseUpMask
+ | NSRightMouseDraggedMask];
+ switch ([mouseEvent type]) {
+ case NSRightMouseDown:
+ case NSRightMouseUp:
+ case NSLeftMouseDown:
+ case NSLeftMouseUp:
+ [self menuTrackingDone:nil];
+ break;
+ case NSRightMouseDragged:
+ case NSLeftMouseDragged:
+ default:
+ /* Ignore any other kind of event. */
+ break;
+ }
+ };
+}
+
+-(void)mouseDown:(NSEvent *)mouseEvent
+{
+ [self mousePressed:mouseEvent];
+}
+
+- (void)rightMouseDown:(NSEvent *)mouseEvent
+{
+ [self mousePressed:mouseEvent];
+}
+
+
+-(void)drawRect:(NSRect)rect {
+ [[parent item] drawStatusBarBackgroundInRect:rect withHighlight:down];
+ [super drawRect:rect];
+}
+@end
+
+@implementation QNSStatusItem
+
+-(id)initWithIcon:(QSystemTrayIcon*)i iconPrivate:(QSystemTrayIconPrivate *)iPrivate
+{
+ self = [super init];
+ if(self) {
+ icon = i;
+ iconPrivate = iPrivate;
+ item = [[[NSStatusBar systemStatusBar] statusItemWithLength:NSSquareStatusItemLength] retain];
+ imageCell = [[QNSImageView alloc] initWithParent:self];
+ [item setView: imageCell];
+ }
+ return self;
+}
+-(void)dealloc {
+ [[NSStatusBar systemStatusBar] removeStatusItem:item];
+ [imageCell release];
+ [item release];
+ [super dealloc];
+
+}
+
+-(QSystemTrayIcon*)icon {
+ return icon;
+}
+
+-(NSStatusItem*)item {
+ return item;
+}
+-(QRectF)geometry {
+ if(NSWindow *window = [[item view] window]) {
+ NSRect screenRect = [[window screen] frame];
+ NSRect windowRect = [window frame];
+ return QRectF(windowRect.origin.x, screenRect.size.height-windowRect.origin.y-windowRect.size.height, windowRect.size.width, windowRect.size.height);
+ }
+ return QRectF();
+}
+- (void)triggerSelector:(id)sender {
+ Q_UNUSED(sender);
+ if(!icon)
+ return;
+ qtsystray_sendActivated(icon, QSystemTrayIcon::Trigger);
+ if (icon->contextMenu()) {
+#if 0
+ const QRectF geom = [self geometry];
+ if(!geom.isNull()) {
+ [[NSNotificationCenter defaultCenter] addObserver:imageCell
+ selector:@selector(menuTrackingDone:)
+ name:nil
+ object:self];
+ icon->contextMenu()->exec(geom.topLeft().toPoint(), 0);
+ [imageCell menuTrackingDone:nil];
+ } else
+#endif
+ {
+#ifndef QT_MAC_USE_COCOA
+ [[[self item] view] removeAllToolTips];
+ iconPrivate->updateToolTip_sys();
+#endif
+ NSMenu *m = [[QNSMenu alloc] initWithQMenu:icon->contextMenu()];
+ [m setAutoenablesItems: NO];
+ [[NSNotificationCenter defaultCenter] addObserver:imageCell
+ selector:@selector(menuTrackingDone:)
+ name:NSMenuDidEndTrackingNotification
+ object:m];
+ [item popUpStatusItemMenu: m];
+ [m release];
+ }
+ }
+}
+- (void)doubleClickSelector:(id)sender {
+ Q_UNUSED(sender);
+ if(!icon)
+ return;
+ qtsystray_sendActivated(icon, QSystemTrayIcon::DoubleClick);
+}
+@end
+
+class QSystemTrayIconQMenu : public QMenu
+{
+public:
+ void doAboutToShow() { emit aboutToShow(); }
+private:
+ QSystemTrayIconQMenu();
+};
+
+@implementation QNSMenu
+-(id)initWithQMenu:(QMenu*)qm {
+ self = [super init];
+ if(self) {
+ self->qmenu = qm;
+ [self setDelegate:self];
+ }
+ return self;
+}
+-(QMenu*)menu {
+ return qmenu;
+}
+-(void)menuNeedsUpdate:(QNSMenu*)menu {
+ emit static_cast<QSystemTrayIconQMenu*>(menu->qmenu)->doAboutToShow();
+ for(int i = [menu numberOfItems]-1; i >= 0; --i)
+ [menu removeItemAtIndex:i];
+ QList<QAction*> actions = menu->qmenu->actions();;
+ for(int i = 0; i < actions.size(); ++i) {
+ const QAction *action = actions[i];
+ if(!action->isVisible())
+ continue;
+
+ NSMenuItem *item = 0;
+ bool needRelease = false;
+ if(action->isSeparator()) {
+ item = [NSMenuItem separatorItem];
+ } else {
+ item = [[NSMenuItem alloc] init];
+ needRelease = true;
+ QString text = action->text();
+ QKeySequence accel = action->shortcut();
+ {
+ int st = text.lastIndexOf(QLatin1Char('\t'));
+ if(st != -1) {
+ accel = QKeySequence(text.right(text.length()-(st+1)));
+ text.remove(st, text.length()-st);
+ }
+ }
+ if(accel.count() > 1)
+ text += QLatin1String(" (****)"); //just to denote a multi stroke shortcut
+
+ [item setTitle:(NSString*)QCFString::toCFStringRef(qt_mac_removeMnemonics(text))];
+ [item setEnabled:menu->qmenu->isEnabled() && action->isEnabled()];
+ [item setState:action->isChecked() ? NSOnState : NSOffState];
+ [item setToolTip:(NSString*)QCFString::toCFStringRef(action->toolTip())];
+ const QIcon icon = action->icon();
+ if(!icon.isNull()) {
+ const short scale = [[NSApp mainMenu] menuBarHeight];
+ NSImage *nsimage = static_cast<NSImage *>(qt_mac_create_nsimage(icon.pixmap(QSize(scale, scale))));
+ [item setImage: nsimage];
+ [nsimage release];
+ }
+ if(action->menu()) {
+ QNSMenu *sub = [[QNSMenu alloc] initWithQMenu:action->menu()];
+ [item setSubmenu:sub];
+ } else {
+ [item setAction:@selector(selectedAction:)];
+ [item setTarget:self];
+ }
+ if(!accel.isEmpty()) {
+ [item setKeyEquivalent:keySequenceToKeyEqivalent(accel)];
+ [item setKeyEquivalentModifierMask:keySequenceModifierMask(accel)];
+ }
+ }
+ if(item)
+ [menu addItem:item];
+ if (needRelease)
+ [item release];
+ }
+}
+-(void)selectedAction:(id)a {
+ const int activated = [self indexOfItem:a];
+ QAction *action = 0;
+ QList<QAction*> actions = qmenu->actions();
+ for(int i = 0, cnt = 0; i < actions.size(); ++i) {
+ if(actions.at(i)->isVisible() && (cnt++) == activated) {
+ action = actions.at(i);
+ break;
+ }
+ }
+ if(action) {
+ action->activate(QAction::Trigger);
+ }
+}
+@end
+
+
+/* Done here because this is the only .mm for now! -Sam */
+QMacCocoaAutoReleasePool::QMacCocoaAutoReleasePool()
+{
+ NSApplicationLoad();
+ pool = (void*)[[NSAutoreleasePool alloc] init];
+}
+
+QMacCocoaAutoReleasePool::~QMacCocoaAutoReleasePool()
+{
+ [(NSAutoreleasePool*)pool release];
+}
+
diff --git a/src/gui/util/qsystemtrayicon_p.h b/src/gui/util/qsystemtrayicon_p.h
new file mode 100644
index 0000000000..8c0732dcea
--- /dev/null
+++ b/src/gui/util/qsystemtrayicon_p.h
@@ -0,0 +1,181 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSYSTEMTRAYICON_P_H
+#define QSYSTEMTRAYICON_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 "qsystemtrayicon.h"
+#include "private/qobject_p.h"
+
+#ifndef QT_NO_SYSTEMTRAYICON
+
+#include "QtGui/qmenu.h"
+#include "QtGui/qpixmap.h"
+#include "QtCore/qstring.h"
+#include "QtCore/qpointer.h"
+
+QT_BEGIN_NAMESPACE
+
+class QSystemTrayIconSys;
+class QToolButton;
+class QLabel;
+
+class QSystemTrayIconPrivate : public QObjectPrivate
+{
+ Q_DECLARE_PUBLIC(QSystemTrayIcon)
+
+public:
+ QSystemTrayIconPrivate() : sys(0), visible(false) { }
+
+ void install_sys();
+ void remove_sys();
+ void updateIcon_sys();
+ void updateToolTip_sys();
+ void updateMenu_sys();
+ QRect geometry_sys() const;
+ void showMessage_sys(const QString &msg, const QString &title, QSystemTrayIcon::MessageIcon icon, int secs);
+ static bool isSystemTrayAvailable_sys();
+
+ QPointer<QMenu> menu;
+ QIcon icon;
+ QString toolTip;
+ QSystemTrayIconSys *sys;
+ bool visible;
+};
+
+class QBalloonTip : public QWidget
+{
+public:
+ static void showBalloon(QSystemTrayIcon::MessageIcon icon, const QString& title,
+ const QString& msg, QSystemTrayIcon *trayIcon,
+ const QPoint& pos, int timeout, bool showArrow = true);
+ static void hideBalloon();
+ static bool isBalloonVisible();
+
+private:
+ QBalloonTip(QSystemTrayIcon::MessageIcon icon, const QString& title,
+ const QString& msg, QSystemTrayIcon *trayIcon);
+ ~QBalloonTip();
+ void balloon(const QPoint&, int, bool);
+
+protected:
+ void paintEvent(QPaintEvent *);
+ void resizeEvent(QResizeEvent *);
+ void mousePressEvent(QMouseEvent *e);
+ void timerEvent(QTimerEvent *e);
+
+private:
+ QSystemTrayIcon *trayIcon;
+ QPixmap pixmap;
+ int timerId;
+};
+
+#if defined(Q_WS_X11)
+QT_BEGIN_INCLUDE_NAMESPACE
+#include <QtCore/qcoreapplication.h>
+#include <X11/Xlib.h>
+#include <X11/Xatom.h>
+#include <X11/Xutil.h>
+QT_END_INCLUDE_NAMESPACE
+
+class QSystemTrayIconSys : public QWidget
+{
+ friend class QSystemTrayIconPrivate;
+
+public:
+ QSystemTrayIconSys(QSystemTrayIcon *q);
+ ~QSystemTrayIconSys();
+ enum {
+ SYSTEM_TRAY_REQUEST_DOCK = 0,
+ SYSTEM_TRAY_BEGIN_MESSAGE = 1,
+ SYSTEM_TRAY_CANCEL_MESSAGE =2
+ };
+
+ void addToTray();
+ void updateIcon();
+ XVisualInfo* getSysTrayVisualInfo();
+
+ // QObject::event is public but QWidget's ::event() re-implementation
+ // is protected ;(
+ inline bool deliverToolTipEvent(QEvent *e)
+ { return QWidget::event(e); }
+
+ static Window sysTrayWindow;
+ static QList<QSystemTrayIconSys *> trayIcons;
+ static QCoreApplication::EventFilter oldEventFilter;
+ static bool sysTrayTracker(void *message, long *result);
+ static Window locateSystemTray();
+ static Atom sysTraySelection;
+ static XVisualInfo sysTrayVisual;
+
+protected:
+ void paintEvent(QPaintEvent *pe);
+ void resizeEvent(QResizeEvent *re);
+ bool x11Event(XEvent *event);
+ void mousePressEvent(QMouseEvent *event);
+ void mouseDoubleClickEvent(QMouseEvent *event);
+ void wheelEvent(QWheelEvent *event);
+ bool event(QEvent *e);
+
+private:
+ QPixmap background;
+ QSystemTrayIcon *q;
+ Colormap colormap;
+};
+#endif // Q_WS_X11
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_SYSTEMTRAYICON
+
+#endif // QSYSTEMTRAYICON_P_H
+
diff --git a/src/gui/util/qsystemtrayicon_qws.cpp b/src/gui/util/qsystemtrayicon_qws.cpp
new file mode 100644
index 0000000000..fc5fdbe182
--- /dev/null
+++ b/src/gui/util/qsystemtrayicon_qws.cpp
@@ -0,0 +1,91 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qsystemtrayicon_p.h"
+
+#ifndef QT_NO_SYSTEMTRAYICON
+
+QT_BEGIN_NAMESPACE
+
+void QSystemTrayIconPrivate::install_sys()
+{
+}
+
+void QSystemTrayIconPrivate::remove_sys()
+{
+}
+
+QRect QSystemTrayIconPrivate::geometry_sys() const
+{
+ return QRect();
+}
+
+void QSystemTrayIconPrivate::updateIcon_sys()
+{
+}
+
+void QSystemTrayIconPrivate::updateMenu_sys()
+{
+}
+
+void QSystemTrayIconPrivate::updateToolTip_sys()
+{
+}
+
+bool QSystemTrayIconPrivate::isSystemTrayAvailable_sys()
+{
+ return false;
+}
+
+void QSystemTrayIconPrivate::showMessage_sys(const QString &message,
+ const QString &title,
+ QSystemTrayIcon::MessageIcon icon,
+ int msecs)
+{
+ Q_UNUSED(message);
+ Q_UNUSED(title);
+ Q_UNUSED(icon);
+ Q_UNUSED(msecs);
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_SYSTEMTRAYICON
diff --git a/src/gui/util/qsystemtrayicon_win.cpp b/src/gui/util/qsystemtrayicon_win.cpp
new file mode 100644
index 0000000000..84f9de4c10
--- /dev/null
+++ b/src/gui/util/qsystemtrayicon_win.cpp
@@ -0,0 +1,748 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qsystemtrayicon_p.h"
+#ifndef QT_NO_SYSTEMTRAYICON
+//#define _WIN32_IE 0x0500
+#define _WIN32_IE 0x0600 //required for NOTIFYICONDATAW_V2_SIZE
+
+//missing defines for MINGW :
+#ifndef NIN_BALLOONTIMEOUT
+#define NIN_BALLOONTIMEOUT (WM_USER + 4)
+#endif
+#ifndef NIN_BALLOONUSERCLICK
+#define NIN_BALLOONUSERCLICK (WM_USER + 5)
+#endif
+
+#include <qt_windows.h>
+#include <commctrl.h>
+#include <shlwapi.h>
+#include <QBitmap>
+#include <QLibrary>
+#include <QApplication>
+#include <QToolTip>
+#include <QDesktopWidget>
+#include <QSettings>
+
+#if defined(Q_OS_WINCE) && !defined(STANDARDSHELL_UI_MODEL)
+# include <streams.h>
+#endif
+
+QT_BEGIN_NAMESPACE
+
+#if defined(Q_OS_WINCE)
+static const UINT q_uNOTIFYICONID = 13; // IDs from 0 to 12 are reserved on WinCE.
+#else
+static const UINT q_uNOTIFYICONID = 0;
+#endif
+
+static uint MYWM_TASKBARCREATED = 0;
+#define MYWM_NOTIFYICON (WM_APP+101)
+
+typedef BOOL (WINAPI *PtrShell_NotifyIcon)(DWORD,PNOTIFYICONDATA);
+static PtrShell_NotifyIcon ptrShell_NotifyIcon = 0;
+
+static void resolveLibs()
+{
+ static bool triedResolve = false;
+#if defined Q_OS_WINCE
+ QString libName(QLatin1String("coredll"));
+ const char* funcName = "Shell_NotifyIcon";
+#else
+ QString libName(QLatin1String("shell32"));
+ const char* funcName = "Shell_NotifyIconW";
+#endif
+ if (!triedResolve) {
+ QLibrary lib(libName);
+ triedResolve = true;
+ ptrShell_NotifyIcon = (PtrShell_NotifyIcon) lib.resolve(funcName);
+ }
+}
+
+class QSystemTrayIconSys : QWidget
+{
+public:
+ QSystemTrayIconSys(QSystemTrayIcon *object);
+ ~QSystemTrayIconSys();
+ bool winEvent( MSG *m, long *result );
+ bool trayMessageA(DWORD msg);
+ bool trayMessageW(DWORD msg);
+ bool trayMessage(DWORD msg);
+ bool iconDrawItem(LPDRAWITEMSTRUCT lpdi);
+ void setIconContentsW(NOTIFYICONDATAW &data);
+ void setIconContentsA(NOTIFYICONDATAA &data);
+ bool showMessageW(const QString &title, const QString &message, QSystemTrayIcon::MessageIcon type, uint uSecs);
+ bool showMessageA(const QString &title, const QString &message, QSystemTrayIcon::MessageIcon type, uint uSecs);
+ bool allowsMessages();
+ bool supportsMessages();
+ QRect findIconGeometry(const int a_iButtonID);
+ QRect findTrayGeometry();
+ HBITMAP createIconMask(const QBitmap &bitmap);
+ void createIcon();
+ int detectShellVersion() const;
+ HICON hIcon;
+ QPoint globalPos;
+ QSystemTrayIcon *q;
+private:
+ uint notifyIconSizeW;
+ uint notifyIconSizeA;
+ int currentShellVersion;
+ int maxTipLength;
+};
+
+// Checks for the shell32 dll version number, since only version
+// 5 or later of supports ballon messages
+bool QSystemTrayIconSys::allowsMessages()
+{
+#ifndef QT_NO_SETTINGS
+
+ QSettings settings(QLatin1String("HKEY_CURRENT_USER\\Software\\Microsoft"
+ "\\Windows\\CurrentVersion\\Explorer\\Advanced"), QSettings::NativeFormat);
+ return settings.value(QLatin1String("EnableBalloonTips"), true).toBool();
+#else
+ return false;
+#endif
+}
+
+// Checks for the shell32 dll version number, since only version
+// 5 or later of supports ballon messages
+bool QSystemTrayIconSys::supportsMessages()
+{
+#if NOTIFYICON_VERSION >= 3
+ if (currentShellVersion >= 5)
+ return allowsMessages();
+ else
+#endif
+ return false;
+}
+
+//Returns the runtime major version of the shell32 dll
+int QSystemTrayIconSys::detectShellVersion() const
+{
+#ifndef Q_OS_WINCE
+ int shellVersion = 4; //NT 4.0 and W95
+ DLLGETVERSIONPROC pDllGetVersion = (DLLGETVERSIONPROC)QLibrary::resolve(
+ QLatin1String("shell32"), "DllGetVersion");
+ if (pDllGetVersion)
+ {
+ DLLVERSIONINFO dvi;
+ HRESULT hr;
+ ZeroMemory(&dvi, sizeof(dvi));
+ dvi.cbSize = sizeof(dvi);
+ hr = (*pDllGetVersion)(&dvi);
+ if (SUCCEEDED(hr)) {
+ if (dvi.dwMajorVersion >= 5)
+ {
+ shellVersion = dvi.dwMajorVersion;
+ }
+ }
+ }
+ return shellVersion;
+#endif
+ return 4; //No ballonMessages and MaxTipLength = 64 for WindowsCE
+}
+
+QSystemTrayIconSys::QSystemTrayIconSys(QSystemTrayIcon *object)
+ : hIcon(0), q(object)
+{
+ currentShellVersion = detectShellVersion();
+ notifyIconSizeA = FIELD_OFFSET(NOTIFYICONDATAA, szTip[64]); // NOTIFYICONDATAA_V1_SIZE
+ notifyIconSizeW = FIELD_OFFSET(NOTIFYICONDATAW, szTip[64]); // NOTIFYICONDATAW_V1_SIZE;
+ maxTipLength = 64;
+
+#if NOTIFYICON_VERSION >= 3
+ if (currentShellVersion >=5) {
+ notifyIconSizeA = FIELD_OFFSET(NOTIFYICONDATAA, guidItem); // NOTIFYICONDATAA_V2_SIZE
+ notifyIconSizeW = FIELD_OFFSET(NOTIFYICONDATAW, guidItem); // NOTIFYICONDATAW_V2_SIZE;
+ maxTipLength = 128;
+ }
+#endif
+
+ // For restoring the tray icon after explorer crashes
+ if (!MYWM_TASKBARCREATED) {
+ MYWM_TASKBARCREATED = QT_WA_INLINE(RegisterWindowMessageW(L"TaskbarCreated"),RegisterWindowMessageA("TaskbarCreated"));
+ }
+}
+
+QSystemTrayIconSys::~QSystemTrayIconSys()
+{
+ if (hIcon)
+ DestroyIcon(hIcon);
+}
+
+void QSystemTrayIconSys::setIconContentsW(NOTIFYICONDATAW &tnd)
+{
+ tnd.uFlags = NIF_MESSAGE|NIF_ICON|NIF_TIP;
+ tnd.uCallbackMessage = MYWM_NOTIFYICON;
+ tnd.hIcon = hIcon;
+ QString tip = q->toolTip();
+
+ if (!tip.isNull()) {
+ // Tip is limited to maxTipLength - NULL; lstrcpyn appends a NULL terminator.
+ tip = tip.left(maxTipLength - 1) + QChar();
+#if defined(Q_OS_WINCE)
+ wcsncpy(tnd.szTip, reinterpret_cast<const wchar_t *> (tip.utf16()), qMin(tip.length()+1, maxTipLength));
+#else
+ lstrcpynW(tnd.szTip, (TCHAR*)tip.utf16(), qMin(tip.length()+1, maxTipLength));
+#endif
+ }
+}
+
+void QSystemTrayIconSys::setIconContentsA(NOTIFYICONDATAA &tnd)
+{
+ tnd.uFlags = NIF_MESSAGE|NIF_ICON|NIF_TIP;
+ tnd.uCallbackMessage = MYWM_NOTIFYICON;
+ tnd.hIcon = hIcon;
+ QString tip = q->toolTip();
+
+ if (!tip.isNull()) {
+ // Tip is limited to maxTipLength - NULL; lstrcpyn appends a NULL terminator.
+ tip = tip.left(maxTipLength - 1) + QChar();
+#if defined(Q_OS_WINCE)
+ strncpy(tnd.szTip, tip.toLocal8Bit().constData(), qMin(tip.length()+1, maxTipLength));
+#else
+ lstrcpynA(tnd.szTip, tip.toLocal8Bit().constData(), qMin(tip.length()+1, maxTipLength));
+#endif
+ }
+}
+
+int iconFlag( QSystemTrayIcon::MessageIcon icon )
+{
+ int flag = 0;
+#if NOTIFYICON_VERSION >= 3
+ switch (icon) {
+ case QSystemTrayIcon::NoIcon:
+ break;
+ case QSystemTrayIcon::Critical:
+ flag = NIIF_ERROR;
+ break;
+ case QSystemTrayIcon::Warning:
+ flag = NIIF_WARNING;
+ break;
+ case QSystemTrayIcon::Information:
+ default : // fall through
+ flag = NIIF_INFO;
+ }
+#else
+ Q_UNUSED(icon);
+#endif
+ return flag;
+}
+
+bool QSystemTrayIconSys::showMessageW(const QString &title, const QString &message, QSystemTrayIcon::MessageIcon type, uint uSecs)
+{
+#if NOTIFYICON_VERSION>=3
+ NOTIFYICONDATA tnd;
+ memset(&tnd, 0, notifyIconSizeW);
+ Q_ASSERT(testAttribute(Qt::WA_WState_Created));
+
+ setIconContentsW(tnd);
+#if defined(Q_OS_WINCE)
+ wcsncpy(tnd.szInfo, message.utf16(), qMin(message.length() + 1, 256));
+ wcsncpy(tnd.szInfoTitle, title.utf16(), qMin(title.length()+1, 64));
+#else
+ lstrcpynW(tnd.szInfo, (TCHAR*)message.utf16(), qMin(message.length() + 1, 256));
+ lstrcpynW(tnd.szInfoTitle, (TCHAR*)title.utf16(), qMin(title.length() + 1, 64));
+#endif
+ tnd.uID = q_uNOTIFYICONID;
+ tnd.dwInfoFlags = iconFlag(type);
+ tnd.cbSize = notifyIconSizeW;
+ tnd.hWnd = winId();
+ tnd.uTimeout = uSecs;
+ tnd.uFlags = NIF_INFO;
+ return ptrShell_NotifyIcon(NIM_MODIFY, &tnd);
+#else
+ Q_UNUSED(title);
+ Q_UNUSED(message);
+ Q_UNUSED(type);
+ Q_UNUSED(uSecs);
+ return false;
+#endif
+}
+
+bool QSystemTrayIconSys::showMessageA(const QString &title, const QString &message, QSystemTrayIcon::MessageIcon type, uint uSecs)
+{
+#if NOTIFYICON_VERSION>=3
+ NOTIFYICONDATAA tnd;
+ memset(&tnd, 0, notifyIconSizeA);
+ Q_ASSERT(testAttribute(Qt::WA_WState_Created));
+
+ setIconContentsA(tnd);
+#if defined(Q_OS_WINCE)
+ strncpy(tnd.szInfo, message.toLocal8Bit().constData(), qMin(message.length() + 1, 256));
+ strncpy(tnd.szInfoTitle, title.toLocal8Bit().constData(), qMin(title.length()+1, 64));
+#else
+ lstrcpynA(tnd.szInfo, message.toLocal8Bit().constData(), qMin(message.length() + 1, 256));
+ lstrcpynA(tnd.szInfoTitle, title.toLocal8Bit().constData(), qMin(title.length() + 1, 64));
+#endif
+ tnd.uID = q_uNOTIFYICONID;
+ tnd.dwInfoFlags = iconFlag(type);
+ tnd.cbSize = notifyIconSizeA;
+ tnd.hWnd = winId();
+ tnd.uTimeout = uSecs;
+ tnd.uFlags = NIF_INFO;
+ return Shell_NotifyIconA(NIM_MODIFY, &tnd);
+#else
+ Q_UNUSED(title);
+ Q_UNUSED(message);
+ Q_UNUSED(type);
+ Q_UNUSED(uSecs);
+ return false;
+#endif
+}
+
+bool QSystemTrayIconSys::trayMessageA(DWORD msg)
+{
+#if !defined(Q_OS_WINCE)
+ NOTIFYICONDATAA tnd;
+ memset(&tnd, 0, notifyIconSizeA);
+ tnd.uID = q_uNOTIFYICONID;
+ tnd.cbSize = notifyIconSizeA;
+ tnd.hWnd = winId();
+ Q_ASSERT(testAttribute(Qt::WA_WState_Created));
+
+ if (msg != NIM_DELETE) {
+ setIconContentsA(tnd);
+ }
+ return Shell_NotifyIconA(msg, &tnd);
+#else
+ Q_UNUSED(msg);
+ return false;
+#endif
+}
+
+bool QSystemTrayIconSys::trayMessageW(DWORD msg)
+{
+ NOTIFYICONDATAW tnd;
+ memset(&tnd, 0, notifyIconSizeW);
+ tnd.uID = q_uNOTIFYICONID;
+ tnd.cbSize = notifyIconSizeW;
+ tnd.hWnd = winId();
+ Q_ASSERT(testAttribute(Qt::WA_WState_Created));
+
+ if (msg != NIM_DELETE) {
+ setIconContentsW(tnd);
+ }
+ return ptrShell_NotifyIcon(msg, &tnd);
+}
+
+bool QSystemTrayIconSys::trayMessage(DWORD msg)
+{
+ resolveLibs();
+ if (!(ptrShell_NotifyIcon))
+ return false;
+
+ QT_WA({
+ return trayMessageW(msg);
+ },
+ {
+ return trayMessageA(msg);
+ });
+}
+
+bool QSystemTrayIconSys::iconDrawItem(LPDRAWITEMSTRUCT lpdi)
+{
+ if (!hIcon)
+ return false;
+
+ DrawIconEx(lpdi->hDC, lpdi->rcItem.left, lpdi->rcItem.top, hIcon, 0, 0, 0, 0, DI_NORMAL);
+ return true;
+}
+
+HBITMAP QSystemTrayIconSys::createIconMask(const QBitmap &bitmap)
+{
+ QImage bm = bitmap.toImage().convertToFormat(QImage::Format_Mono);
+ int w = bm.width();
+ int h = bm.height();
+ int bpl = ((w+15)/16)*2; // bpl, 16 bit alignment
+ uchar *bits = new uchar[bpl*h];
+ bm.invertPixels();
+ for (int y=0; y<h; y++)
+ memcpy(bits+y*bpl, bm.scanLine(y), bpl);
+ HBITMAP hbm = CreateBitmap(w, h, 1, 1, bits);
+ delete [] bits;
+ return hbm;
+}
+
+void QSystemTrayIconSys::createIcon()
+{
+ hIcon = 0;
+ QIcon icon = q->icon();
+ if (icon.isNull())
+ return;
+
+ const int iconSizeX = GetSystemMetrics(SM_CXSMICON);
+ const int iconSizeY = GetSystemMetrics(SM_CYSMICON);
+ QSize size = icon.actualSize(QSize(iconSizeX, iconSizeY));
+ QPixmap pm = icon.pixmap(size);
+ if (pm.isNull())
+ return;
+
+ QBitmap mask = pm.mask();
+ if (mask.isNull()) {
+ mask = QBitmap(pm.size());
+ mask.fill(Qt::color1);
+ }
+
+ HBITMAP im = createIconMask(mask);
+ ICONINFO ii;
+ ii.fIcon = true;
+ ii.hbmMask = im;
+ ii.hbmColor = pm.toWinHBITMAP(QPixmap::Alpha);
+ ii.xHotspot = 0;
+ ii.yHotspot = 0;
+ hIcon = CreateIconIndirect(&ii);
+
+ DeleteObject(ii.hbmColor);
+ DeleteObject(im);
+}
+
+bool QSystemTrayIconSys::winEvent( MSG *m, long *result )
+{
+ switch(m->message) {
+ case WM_CREATE:
+#ifdef GWLP_USERDATA
+ SetWindowLongPtr(winId(), GWLP_USERDATA, (LONG_PTR)((CREATESTRUCTW*)m->lParam)->lpCreateParams);
+#else
+ SetWindowLong(winId(), GWL_USERDATA, (LONG)((CREATESTRUCTW*)m->lParam)->lpCreateParams);
+#endif
+ break;
+
+ case WM_DRAWITEM:
+ return iconDrawItem((LPDRAWITEMSTRUCT)m->lParam);
+
+ case MYWM_NOTIFYICON:
+ {
+ RECT r;
+ GetWindowRect(winId(), &r);
+ QEvent *e = 0;
+ Qt::KeyboardModifiers keys = QApplication::keyboardModifiers();
+ QPoint gpos = QCursor::pos();
+
+ switch (m->lParam) {
+ case WM_LBUTTONUP:
+ emit q->activated(QSystemTrayIcon::Trigger);
+ break;
+
+#if !defined(Q_OS_WINCE)
+ case WM_LBUTTONDBLCLK:
+ emit q->activated(QSystemTrayIcon::DoubleClick);
+ break;
+
+ case WM_RBUTTONUP:
+ if (q->contextMenu()) {
+ q->contextMenu()->popup(gpos);
+ q->contextMenu()->activateWindow();
+ //Must be activated for proper keyboardfocus and menu closing on windows:
+ }
+ emit q->activated(QSystemTrayIcon::Context);
+ break;
+
+ case NIN_BALLOONUSERCLICK:
+ emit q->messageClicked();
+ break;
+
+ case WM_MBUTTONUP:
+ emit q->activated(QSystemTrayIcon::MiddleClick);
+ break;
+#endif
+ default:
+ break;
+ }
+ if (e) {
+ bool res = QApplication::sendEvent(q, e);
+ delete e;
+ return res;
+ }
+ break;
+ }
+ default:
+ if (m->message == MYWM_TASKBARCREATED)
+ trayMessage(NIM_ADD);
+ else
+ return QWidget::winEvent(m, result);
+ break;
+ }
+ return 0;
+}
+
+void QSystemTrayIconPrivate::install_sys()
+{
+ Q_Q(QSystemTrayIcon);
+ if (!sys) {
+ sys = new QSystemTrayIconSys(q);
+ sys->createIcon();
+ sys->trayMessage(NIM_ADD);
+ }
+}
+
+//fallback on win 95/98
+QRect QSystemTrayIconSys::findTrayGeometry()
+{
+ //Use lower right corner as fallback
+ QPoint brCorner = qApp->desktop()->screenGeometry().bottomRight();
+ QRect ret(brCorner.x() - 10, brCorner.y() - 10, 10, 10);
+#if defined(Q_OS_WINCE)
+ HWND trayHandle = FindWindowW(L"Shell_TrayWnd", NULL);
+#else
+ HWND trayHandle = FindWindowA("Shell_TrayWnd", NULL);
+#endif
+ if (trayHandle) {
+#if defined(Q_OS_WINCE)
+ trayHandle = FindWindowW(L"TrayNotifyWnd", NULL);
+#else
+ trayHandle = FindWindowExA(trayHandle, NULL, "TrayNotifyWnd", NULL);
+#endif
+ if (trayHandle) {
+ RECT r;
+ if (GetWindowRect(trayHandle, &r)) {
+ ret = QRect(r.left, r.top, r.right- r.left, r.bottom - r.top);
+ }
+ }
+ }
+ return ret;
+}
+
+/*
+* This function tries to determine the icon geometry from the tray
+*
+* If it fails an invalid rect is returned.
+*/
+QRect QSystemTrayIconSys::findIconGeometry(const int iconId)
+{
+ QRect ret;
+
+ TBBUTTON buttonData;
+ DWORD processID = 0;
+#if defined(Q_OS_WINCE)
+ HWND trayHandle = FindWindowW(L"Shell_TrayWnd", NULL);
+#else
+ HWND trayHandle = FindWindowA("Shell_TrayWnd", NULL);
+#endif
+
+ //find the toolbar used in the notification area
+ if (trayHandle) {
+#if defined(Q_OS_WINCE)
+ trayHandle = FindWindowW(L"TrayNotifyWnd", NULL);
+#else
+ trayHandle = FindWindowExA(trayHandle, NULL, "TrayNotifyWnd", NULL);
+#endif
+ if (trayHandle) {
+#if defined(Q_OS_WINCE)
+ HWND hwnd = FindWindowW(L"SysPager", NULL);
+#else
+ HWND hwnd = FindWindowEx(trayHandle, NULL, L"SysPager", NULL);
+#endif
+ if (hwnd) {
+#if defined(Q_OS_WINCE)
+ hwnd = FindWindow(L"ToolbarWindow32", NULL);
+#else
+ hwnd = FindWindowEx(hwnd, NULL, L"ToolbarWindow32", NULL);
+#endif
+ if (hwnd)
+ trayHandle = hwnd;
+ }
+ }
+ }
+
+ if (!trayHandle)
+ return ret;
+
+ GetWindowThreadProcessId(trayHandle, &processID);
+ if (processID <= 0)
+ return ret;
+
+ HANDLE trayProcess = OpenProcess(PROCESS_VM_OPERATION | PROCESS_VM_READ, 0, processID);
+ if (!trayProcess)
+ return ret;
+
+ int buttonCount = SendMessage(trayHandle, TB_BUTTONCOUNT, 0, 0);
+#if defined(Q_OS_WINCE)
+ LPVOID data = VirtualAlloc(NULL, sizeof(TBBUTTON), MEM_COMMIT, PAGE_READWRITE);
+#else
+ LPVOID data = VirtualAllocEx(trayProcess, NULL, sizeof(TBBUTTON), MEM_COMMIT, PAGE_READWRITE);
+#endif
+
+ if ( buttonCount < 1 || !data ) {
+ CloseHandle(trayProcess);
+ return ret;
+ }
+
+ //search for our icon among all toolbar buttons
+ for (int toolbarButton = 0; toolbarButton < buttonCount; ++toolbarButton ) {
+ SIZE_T numBytes = 0;
+ DWORD appData[2] = { 0, 0 };
+ SendMessage(trayHandle, TB_GETBUTTON, toolbarButton , (LPARAM)data);
+
+ if(!ReadProcessMemory(trayProcess, data, &buttonData, sizeof(TBBUTTON), &numBytes))
+ continue;
+
+ if(!ReadProcessMemory(trayProcess, (LPVOID) buttonData.dwData, appData, sizeof(appData), &numBytes))
+ continue;
+
+ int currentIconId = appData[1];
+ HWND currentIconHandle = (HWND) appData[0];
+ bool isHidden = buttonData.fsState & TBSTATE_HIDDEN;
+
+ if (currentIconHandle == winId() &&
+ currentIconId == iconId && !isHidden) {
+ SendMessage(trayHandle, TB_GETITEMRECT, toolbarButton , (LPARAM)data);
+ RECT iconRect = {0, 0};
+ if(ReadProcessMemory(trayProcess, data, &iconRect, sizeof(RECT), &numBytes)) {
+ MapWindowPoints(trayHandle, NULL, (LPPOINT)&iconRect, 2);
+ QRect geometry(iconRect.left + 1, iconRect.top + 1,
+ iconRect.right - iconRect.left - 2,
+ iconRect.bottom - iconRect.top - 2);
+ if (geometry.isValid())
+ ret = geometry;
+ break;
+ }
+ }
+ }
+#if defined(Q_OS_WINCE)
+ VirtualFree(data, 0, MEM_RELEASE);
+#else
+ VirtualFreeEx(trayProcess, data, 0, MEM_RELEASE);
+#endif
+ CloseHandle(trayProcess);
+ return ret;
+}
+
+
+void QSystemTrayIconPrivate::showMessage_sys(const QString &title, const QString &message, QSystemTrayIcon::MessageIcon type, int timeOut)
+{
+ if (!sys || !sys->allowsMessages())
+ return;
+
+ uint uSecs = 0;
+ if ( timeOut < 0)
+ uSecs = 10000; //10 sec default
+ else uSecs = (int)timeOut;
+
+ resolveLibs();
+
+ //message is limited to 255 chars + NULL
+ QString messageString;
+ if (message.isEmpty() && !title.isEmpty())
+ messageString = QLatin1String(" "); //ensures that the message shows when only title is set
+ else
+ messageString = message.left(255) + QChar();
+
+ //title is limited to 63 chars + NULL
+ QString titleString = title.left(63) + QChar();
+
+ if (sys->supportsMessages()) {
+ QT_WA({
+ sys->showMessageW(titleString, messageString, type, (unsigned int)uSecs);
+ }, {
+ sys->showMessageA(titleString, messageString, type, (unsigned int)uSecs);
+ });
+ } else {
+ //use fallbacks
+ QRect iconPos = sys->findIconGeometry(0);
+ if (iconPos.isValid()) {
+ QBalloonTip::showBalloon(type, title, message, sys->q, iconPos.center(), uSecs, true);
+ } else {
+ QRect trayRect = sys->findTrayGeometry();
+ QBalloonTip::showBalloon(type, title, message, sys->q, QPoint(trayRect.left(),
+ trayRect.center().y()), uSecs, false);
+ }
+ }
+}
+
+QRect QSystemTrayIconPrivate::geometry_sys() const
+{
+ if (!sys)
+ return QRect();
+ return sys->findIconGeometry(0);
+}
+
+void QSystemTrayIconPrivate::remove_sys()
+{
+ if (!sys)
+ return;
+
+ sys->trayMessage(NIM_DELETE);
+ delete sys;
+ sys = 0;
+}
+
+void QSystemTrayIconPrivate::updateIcon_sys()
+{
+ if (!sys)
+ return;
+
+ HICON hIconToDestroy = sys->hIcon;
+
+ sys->createIcon();
+ sys->trayMessage(NIM_MODIFY);
+
+ if (hIconToDestroy)
+ DestroyIcon(hIconToDestroy);
+}
+
+void QSystemTrayIconPrivate::updateMenu_sys()
+{
+
+}
+
+void QSystemTrayIconPrivate::updateToolTip_sys()
+{
+#ifdef Q_OS_WINCE
+ // Calling sys->trayMessage(NIM_MODIFY) on an existing icon is broken on Windows CE.
+ // So we need to call updateIcon_sys() which creates a new icon handle.
+ updateIcon_sys();
+#else
+ if (!sys)
+ return;
+
+ sys->trayMessage(NIM_MODIFY);
+#endif
+}
+
+bool QSystemTrayIconPrivate::isSystemTrayAvailable_sys()
+{
+ return true;
+}
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/gui/util/qsystemtrayicon_x11.cpp b/src/gui/util/qsystemtrayicon_x11.cpp
new file mode 100644
index 0000000000..52c258a9df
--- /dev/null
+++ b/src/gui/util/qsystemtrayicon_x11.cpp
@@ -0,0 +1,394 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "private/qt_x11_p.h"
+#include "qlabel.h"
+#include "qx11info_x11.h"
+#include "qpainter.h"
+#include "qpixmap.h"
+#include "qbitmap.h"
+#include "qevent.h"
+#include "qapplication.h"
+#include "qlist.h"
+#include "qmenu.h"
+#include "qtimer.h"
+#include "qsystemtrayicon_p.h"
+#include "qpaintengine.h"
+
+#ifndef QT_NO_SYSTEMTRAYICON
+QT_BEGIN_NAMESPACE
+
+Window QSystemTrayIconSys::sysTrayWindow = XNone;
+QList<QSystemTrayIconSys *> QSystemTrayIconSys::trayIcons;
+QCoreApplication::EventFilter QSystemTrayIconSys::oldEventFilter = 0;
+Atom QSystemTrayIconSys::sysTraySelection = XNone;
+XVisualInfo QSystemTrayIconSys::sysTrayVisual = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+
+// Locate the system tray
+Window QSystemTrayIconSys::locateSystemTray()
+{
+ Display *display = QX11Info::display();
+ if (sysTraySelection == XNone) {
+ int screen = QX11Info::appScreen();
+ QString net_sys_tray = QString::fromLatin1("_NET_SYSTEM_TRAY_S%1").arg(screen);
+ sysTraySelection = XInternAtom(display, net_sys_tray.toLatin1(), False);
+ }
+
+ return XGetSelectionOwner(QX11Info::display(), sysTraySelection);
+}
+
+XVisualInfo* QSystemTrayIconSys::getSysTrayVisualInfo()
+{
+ Display *display = QX11Info::display();
+
+ if (!sysTrayVisual.visual) {
+ Window win = locateSystemTray();
+ if (win != XNone) {
+ Atom actual_type;
+ int actual_format;
+ ulong nitems, bytes_remaining;
+ uchar *data = 0;
+ int result = XGetWindowProperty(display, win, ATOM(_NET_SYSTEM_TRAY_VISUAL), 0, 1,
+ False, XA_VISUALID, &actual_type,
+ &actual_format, &nitems, &bytes_remaining, &data);
+ VisualID vid = 0;
+ if (result == Success && data && actual_type == XA_VISUALID && actual_format == 32 &&
+ nitems == 1 && bytes_remaining == 0)
+ vid = *(VisualID*)data;
+ if (data)
+ XFree(data);
+ if (vid == 0)
+ return 0;
+
+ uint mask = VisualIDMask;
+ XVisualInfo *vi, rvi;
+ int count;
+ rvi.visualid = vid;
+ vi = XGetVisualInfo(display, mask, &rvi, &count);
+ if (vi) {
+ sysTrayVisual = vi[0];
+ XFree((char*)vi);
+ }
+ if (sysTrayVisual.depth != 32)
+ memset(&sysTrayVisual, 0, sizeof(sysTrayVisual));
+ }
+ }
+
+ return sysTrayVisual.visual ? &sysTrayVisual : 0;
+}
+
+bool QSystemTrayIconSys::sysTrayTracker(void *message, long *result)
+{
+ bool retval = false;
+ if (QSystemTrayIconSys::oldEventFilter)
+ retval = QSystemTrayIconSys::oldEventFilter(message, result);
+
+ if (trayIcons.isEmpty())
+ return retval;
+
+ Display *display = QX11Info::display();
+ XEvent *ev = (XEvent *)message;
+ if (ev->type == DestroyNotify && ev->xany.window == sysTrayWindow) {
+ sysTrayWindow = locateSystemTray();
+ memset(&sysTrayVisual, 0, sizeof(sysTrayVisual));
+ for (int i = 0; i < trayIcons.count(); i++) {
+ if (sysTrayWindow == XNone) {
+ QBalloonTip::hideBalloon();
+ trayIcons[i]->hide(); // still no luck
+ trayIcons[i]->destroy();
+ trayIcons[i]->create();
+ } else
+ trayIcons[i]->addToTray(); // add it to the new tray
+ }
+ retval = true;
+ } else if (ev->type == ClientMessage && sysTrayWindow == XNone) {
+ static Atom manager_atom = XInternAtom(display, "MANAGER", False);
+ XClientMessageEvent *cm = (XClientMessageEvent *)message;
+ if ((cm->message_type == manager_atom) && ((Atom)cm->data.l[1] == sysTraySelection)) {
+ sysTrayWindow = cm->data.l[2];
+ memset(&sysTrayVisual, 0, sizeof(sysTrayVisual));
+ XSelectInput(display, sysTrayWindow, StructureNotifyMask);
+ for (int i = 0; i < trayIcons.count(); i++) {
+ trayIcons[i]->addToTray();
+ }
+ retval = true;
+ }
+ } else if (ev->type == PropertyNotify && ev->xproperty.atom == ATOM(_NET_SYSTEM_TRAY_VISUAL) &&
+ ev->xproperty.window == sysTrayWindow) {
+ memset(&sysTrayVisual, 0, sizeof(sysTrayVisual));
+ for (int i = 0; i < trayIcons.count(); i++) {
+ trayIcons[i]->addToTray();
+ }
+ }
+
+ return retval;
+}
+
+QSystemTrayIconSys::QSystemTrayIconSys(QSystemTrayIcon *q)
+ : QWidget(0, Qt::FramelessWindowHint | Qt::X11BypassWindowManagerHint),
+ q(q), colormap(0)
+{
+ setAttribute(Qt::WA_AlwaysShowToolTips);
+ setAttribute(Qt::WA_QuitOnClose, false);
+ setAttribute(Qt::WA_NoSystemBackground, true);
+ setAttribute(Qt::WA_PaintOnScreen);
+
+ static bool eventFilterAdded = false;
+ Display *display = QX11Info::display();
+ if (!eventFilterAdded) {
+ oldEventFilter = qApp->setEventFilter(sysTrayTracker);
+ eventFilterAdded = true;
+ Window root = QX11Info::appRootWindow();
+ XWindowAttributes attr;
+ XGetWindowAttributes(display, root, &attr);
+ if ((attr.your_event_mask & StructureNotifyMask) != StructureNotifyMask) {
+ (void) QApplication::desktop(); // lame trick to ensure our event mask is not overridden
+ XSelectInput(display, root, attr.your_event_mask | StructureNotifyMask); // for MANAGER selection
+ }
+ }
+ if (trayIcons.isEmpty()) {
+ sysTrayWindow = locateSystemTray();
+ if (sysTrayWindow != XNone)
+ XSelectInput(display, sysTrayWindow, StructureNotifyMask); // track tray events
+ }
+ trayIcons.append(this);
+ setMouseTracking(true);
+#ifndef QT_NO_TOOLTIP
+ setToolTip(q->toolTip());
+#endif
+ if (sysTrayWindow != XNone)
+ addToTray();
+}
+
+QSystemTrayIconSys::~QSystemTrayIconSys()
+{
+ trayIcons.removeAt(trayIcons.indexOf(this));
+ Display *display = QX11Info::display();
+ if (trayIcons.isEmpty()) {
+ if (sysTrayWindow == XNone)
+ return;
+ if (display)
+ XSelectInput(display, sysTrayWindow, 0); // stop tracking the tray
+ sysTrayWindow = XNone;
+ }
+ if (colormap)
+ XFreeColormap(display, colormap);
+}
+
+void QSystemTrayIconSys::addToTray()
+{
+ Q_ASSERT(sysTrayWindow != XNone);
+ Display *display = QX11Info::display();
+
+ XVisualInfo *vi = getSysTrayVisualInfo();
+ if (vi && vi->visual) {
+ Window root = RootWindow(display, vi->screen);
+ Window p = root;
+ if (QWidget *pw = parentWidget())
+ p = pw->effectiveWinId();
+ colormap = XCreateColormap(display, root, vi->visual, AllocNone);
+ XSetWindowAttributes wsa;
+ wsa.background_pixmap = 0;
+ wsa.colormap = colormap;
+ wsa.background_pixel = 0;
+ wsa.border_pixel = 0;
+ Window wid = XCreateWindow(display, p, -1, -1, 1, 1,
+ 0, vi->depth, InputOutput, vi->visual,
+ CWBackPixmap|CWBackPixel|CWBorderPixel|CWColormap, &wsa);
+ create(wid);
+ } else {
+ XSetWindowBackgroundPixmap(display, winId(), ParentRelative);
+ }
+
+ // GNOME, NET WM Specification
+ static Atom netwm_tray_atom = XInternAtom(display, "_NET_SYSTEM_TRAY_OPCODE", False);
+ long l[5] = { CurrentTime, SYSTEM_TRAY_REQUEST_DOCK, winId(), 0, 0 };
+ XEvent ev;
+ memset(&ev, 0, sizeof(ev));
+ ev.xclient.type = ClientMessage;
+ ev.xclient.window = sysTrayWindow;
+ ev.xclient.message_type = netwm_tray_atom;
+ ev.xclient.format = 32;
+ memcpy((char *)&ev.xclient.data, (const char *) l, sizeof(l));
+ XSendEvent(display, sysTrayWindow, False, 0, &ev);
+ setMinimumSize(22, 22); // required at least on GNOME
+}
+
+void QSystemTrayIconSys::updateIcon()
+{
+ update();
+}
+
+void QSystemTrayIconSys::resizeEvent(QResizeEvent *re)
+{
+ QWidget::resizeEvent(re);
+ updateIcon();
+}
+
+void QSystemTrayIconSys::paintEvent(QPaintEvent*)
+{
+ QPainter p(this);
+ if (!getSysTrayVisualInfo()) {
+ const QRegion oldSystemClip = p.paintEngine()->systemClip();
+ const QRect clearedRect = oldSystemClip.boundingRect();
+ XClearArea(QX11Info::display(), winId(), clearedRect.x(), clearedRect.y(),
+ clearedRect.width(), clearedRect.height(), False);
+ QPaintEngine *pe = p.paintEngine();
+ pe->setSystemClip(clearedRect);
+ q->icon().paint(&p, rect());
+ pe->setSystemClip(oldSystemClip);
+ } else {
+ p.setCompositionMode(QPainter::CompositionMode_Source);
+ p.fillRect(rect(), Qt::transparent);
+ p.setCompositionMode(QPainter::CompositionMode_SourceOver);
+ q->icon().paint(&p, rect());
+ }
+}
+
+void QSystemTrayIconSys::mousePressEvent(QMouseEvent *ev)
+{
+ QPoint globalPos = ev->globalPos();
+ if (ev->button() == Qt::RightButton && q->contextMenu())
+ q->contextMenu()->popup(globalPos);
+
+ if (QBalloonTip::isBalloonVisible()) {
+ emit q->messageClicked();
+ QBalloonTip::hideBalloon();
+ }
+
+ if (ev->button() == Qt::LeftButton)
+ emit q->activated(QSystemTrayIcon::Trigger);
+ else if (ev->button() == Qt::RightButton)
+ emit q->activated(QSystemTrayIcon::Context);
+ else if (ev->button() == Qt::MidButton)
+ emit q->activated(QSystemTrayIcon::MiddleClick);
+}
+
+void QSystemTrayIconSys::mouseDoubleClickEvent(QMouseEvent *ev)
+{
+ if (ev->button() == Qt::LeftButton)
+ emit q->activated(QSystemTrayIcon::DoubleClick);
+}
+
+void QSystemTrayIconSys::wheelEvent(QWheelEvent *e)
+{
+ QApplication::sendEvent(q, e);
+}
+
+bool QSystemTrayIconSys::event(QEvent *e)
+{
+ if (e->type() == QEvent::ToolTip) {
+ return QApplication::sendEvent(q, e);
+ }
+ return QWidget::event(e);
+}
+
+bool QSystemTrayIconSys::x11Event(XEvent *event)
+{
+ if (event->type == ReparentNotify)
+ show();
+ return QWidget::x11Event(event);
+}
+
+////////////////////////////////////////////////////////////////////////////
+void QSystemTrayIconPrivate::install_sys()
+{
+ Q_Q(QSystemTrayIcon);
+ if (!sys)
+ sys = new QSystemTrayIconSys(q);
+}
+
+QRect QSystemTrayIconPrivate::geometry_sys() const
+{
+ if (!sys)
+ return QRect();
+ return QRect(sys->mapToGlobal(QPoint(0, 0)), sys->size());
+}
+
+void QSystemTrayIconPrivate::remove_sys()
+{
+ if (!sys)
+ return;
+ QBalloonTip::hideBalloon();
+ sys->hide(); // this should do the trick, but...
+ delete sys; // wm may resize system tray only for DestroyEvents
+ sys = 0;
+}
+
+void QSystemTrayIconPrivate::updateIcon_sys()
+{
+ if (!sys)
+ return;
+ sys->updateIcon();
+}
+
+void QSystemTrayIconPrivate::updateMenu_sys()
+{
+
+}
+
+void QSystemTrayIconPrivate::updateToolTip_sys()
+{
+ if (!sys)
+ return;
+#ifndef QT_NO_TOOLTIP
+ sys->setToolTip(toolTip);
+#endif
+}
+
+bool QSystemTrayIconPrivate::isSystemTrayAvailable_sys()
+{
+ return QSystemTrayIconSys::locateSystemTray() != XNone;
+}
+
+void QSystemTrayIconPrivate::showMessage_sys(const QString &message, const QString &title,
+ QSystemTrayIcon::MessageIcon icon, int msecs)
+{
+ if (!sys)
+ return;
+ QPoint g = sys->mapToGlobal(QPoint(0, 0));
+ QBalloonTip::showBalloon(icon, message, title, sys->q,
+ QPoint(g.x() + sys->width()/2, g.y() + sys->height()/2),
+ msecs);
+}
+
+QT_END_NAMESPACE
+#endif //QT_NO_SYSTEMTRAYICON
diff --git a/src/gui/util/qundogroup.cpp b/src/gui/util/qundogroup.cpp
new file mode 100644
index 0000000000..6fc05fe2db
--- /dev/null
+++ b/src/gui/util/qundogroup.cpp
@@ -0,0 +1,500 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qundogroup.h"
+#include "qundostack.h"
+#include "qundostack_p.h"
+
+#ifndef QT_NO_UNDOGROUP
+
+QT_BEGIN_NAMESPACE
+
+class QUndoGroupPrivate : public QObjectPrivate
+{
+ Q_DECLARE_PUBLIC(QUndoGroup)
+public:
+ QUndoGroupPrivate() : active(0) {}
+
+ QUndoStack *active;
+ QList<QUndoStack*> stack_list;
+};
+
+/*!
+ \class QUndoGroup
+ \brief The QUndoGroup class is a group of QUndoStack objects.
+ \since 4.2
+ \ingroup misc
+
+ For an overview of the Qt's undo framework, see the
+ \link qundo.html overview\endlink.
+
+ An application often has multiple undo stacks, one for each opened document. At the
+ same time, an application usually has one undo action and one redo action, which
+ triggers undo or redo in the active document.
+
+ QUndoGroup is a group of QUndoStack objects, one of which may be active. It has
+ an undo() and redo() slot, which calls QUndoStack::undo() and QUndoStack::redo()
+ for the active stack. It also has the functions createUndoAction() and createRedoAction().
+ The actions returned by these functions behave in the same way as those returned by
+ QUndoStack::createUndoAction() and QUndoStack::createRedoAction() of the active
+ stack.
+
+ Stacks are added to a group with addStack() and removed with removeStack(). A stack
+ is implicitly added to a group when it is created with the group as its parent
+ QObject.
+
+ It is the programmer's responsibility to specify which stack is active by
+ calling QUndoStack::setActive(), usually when the associated document window receives focus.
+ The active stack may also be set with setActiveStack(), and is returned by activeStack().
+
+ When a stack is added to a group using addStack(), the group does not take ownership
+ of the stack. This means the stack has to be deleted separately from the group. When
+ a stack is deleted, it is automatically removed from a group. A stack may belong to
+ only one group. Adding it to another group will cause it to be removed from the previous
+ group.
+
+ A QUndoGroup is also useful in conjunction with QUndoView. If a QUndoView is
+ set to watch a group using QUndoView::setGroup(), it will update itself to display
+ the active stack.
+*/
+
+/*!
+ Creates an empty QUndoGroup object with parent \a parent.
+
+ \sa addStack()
+*/
+
+QUndoGroup::QUndoGroup(QObject *parent)
+ : QObject(*new QUndoGroupPrivate(), parent)
+{
+}
+
+/*!
+ Destroys the QUndoGroup.
+*/
+QUndoGroup::~QUndoGroup()
+{
+ // Ensure all QUndoStacks no longer refer to this group.
+ Q_D(QUndoGroup);
+ QList<QUndoStack *>::iterator it = d->stack_list.begin();
+ QList<QUndoStack *>::iterator end = d->stack_list.end();
+ while (it != end) {
+ (*it)->d_func()->group = 0;
+ ++it;
+ }
+}
+
+/*!
+ Adds \a stack to this group. The group does not take ownership of the stack. Another
+ way of adding a stack to a group is by specifying the group as the stack's parent
+ QObject in QUndoStack::QUndoStack(). In this case, the stack is deleted when the
+ group is deleted, in the usual manner of QObjects.
+
+ \sa removeStack() stacks() QUndoStack::QUndoStack()
+*/
+
+void QUndoGroup::addStack(QUndoStack *stack)
+{
+ Q_D(QUndoGroup);
+
+ if (d->stack_list.contains(stack))
+ return;
+ d->stack_list.append(stack);
+
+ if (QUndoGroup *other = stack->d_func()->group)
+ other->removeStack(stack);
+ stack->d_func()->group = this;
+}
+
+/*!
+ Removes \a stack from this group. If the stack was the active stack in the group,
+ the active stack becomes 0.
+
+ \sa addStack() stacks() QUndoStack::~QUndoStack()
+*/
+
+void QUndoGroup::removeStack(QUndoStack *stack)
+{
+ Q_D(QUndoGroup);
+
+ if (d->stack_list.removeAll(stack) == 0)
+ return;
+ if (stack == d->active)
+ setActiveStack(0);
+ stack->d_func()->group = 0;
+}
+
+/*!
+ Returns a list of stacks in this group.
+
+ \sa addStack() removeStack()
+*/
+
+QList<QUndoStack*> QUndoGroup::stacks() const
+{
+ Q_D(const QUndoGroup);
+ return d->stack_list;
+}
+
+/*!
+ Sets the active stack of this group to \a stack.
+
+ If the stack is not a member of this group, this function does nothing.
+
+ Synonymous with calling QUndoStack::setActive() on \a stack.
+
+ The actions returned by createUndoAction() and createRedoAction() will now behave
+ in the same way as those returned by \a stack's QUndoStack::createUndoAction()
+ and QUndoStack::createRedoAction().
+
+ \sa QUndoStack::setActive() activeStack()
+*/
+
+void QUndoGroup::setActiveStack(QUndoStack *stack)
+{
+ Q_D(QUndoGroup);
+ if (d->active == stack)
+ return;
+
+ if (d->active != 0) {
+ disconnect(d->active, SIGNAL(canUndoChanged(bool)),
+ this, SIGNAL(canUndoChanged(bool)));
+ disconnect(d->active, SIGNAL(undoTextChanged(QString)),
+ this, SIGNAL(undoTextChanged(QString)));
+ disconnect(d->active, SIGNAL(canRedoChanged(bool)),
+ this, SIGNAL(canRedoChanged(bool)));
+ disconnect(d->active, SIGNAL(redoTextChanged(QString)),
+ this, SIGNAL(redoTextChanged(QString)));
+ disconnect(d->active, SIGNAL(indexChanged(int)),
+ this, SIGNAL(indexChanged(int)));
+ disconnect(d->active, SIGNAL(cleanChanged(bool)),
+ this, SIGNAL(cleanChanged(bool)));
+ }
+
+ d->active = stack;
+
+ if (d->active == 0) {
+ emit canUndoChanged(false);
+ emit undoTextChanged(QString());
+ emit canRedoChanged(false);
+ emit redoTextChanged(QString());
+ emit cleanChanged(true);
+ emit indexChanged(0);
+ } else {
+ connect(d->active, SIGNAL(canUndoChanged(bool)),
+ this, SIGNAL(canUndoChanged(bool)));
+ connect(d->active, SIGNAL(undoTextChanged(QString)),
+ this, SIGNAL(undoTextChanged(QString)));
+ connect(d->active, SIGNAL(canRedoChanged(bool)),
+ this, SIGNAL(canRedoChanged(bool)));
+ connect(d->active, SIGNAL(redoTextChanged(QString)),
+ this, SIGNAL(redoTextChanged(QString)));
+ connect(d->active, SIGNAL(indexChanged(int)),
+ this, SIGNAL(indexChanged(int)));
+ connect(d->active, SIGNAL(cleanChanged(bool)),
+ this, SIGNAL(cleanChanged(bool)));
+ emit canUndoChanged(d->active->canUndo());
+ emit undoTextChanged(d->active->undoText());
+ emit canRedoChanged(d->active->canRedo());
+ emit redoTextChanged(d->active->redoText());
+ emit cleanChanged(d->active->isClean());
+ emit indexChanged(d->active->index());
+ }
+
+ emit activeStackChanged(d->active);
+}
+
+/*!
+ Returns the active stack of this group.
+
+ If none of the stacks are active, or if the group is empty, this function
+ returns 0.
+
+ \sa setActiveStack() QUndoStack::setActive()
+*/
+
+QUndoStack *QUndoGroup::activeStack() const
+{
+ Q_D(const QUndoGroup);
+ return d->active;
+}
+
+/*!
+ Calls QUndoStack::undo() on the active stack.
+
+ If none of the stacks are active, or if the group is empty, this function
+ does nothing.
+
+ \sa redo() canUndo() setActiveStack()
+*/
+
+void QUndoGroup::undo()
+{
+ Q_D(QUndoGroup);
+ if (d->active != 0)
+ d->active->undo();
+}
+
+/*!
+ Calls QUndoStack::redo() on the active stack.
+
+ If none of the stacks are active, or if the group is empty, this function
+ does nothing.
+
+ \sa undo() canRedo() setActiveStack()
+*/
+
+
+void QUndoGroup::redo()
+{
+ Q_D(QUndoGroup);
+ if (d->active != 0)
+ d->active->redo();
+}
+
+/*!
+ Returns the value of the active stack's QUndoStack::canUndo().
+
+ If none of the stacks are active, or if the group is empty, this function
+ returns false.
+
+ \sa canRedo() setActiveStack()
+*/
+
+bool QUndoGroup::canUndo() const
+{
+ Q_D(const QUndoGroup);
+ return d->active != 0 && d->active->canUndo();
+}
+
+/*!
+ Returns the value of the active stack's QUndoStack::canRedo().
+
+ If none of the stacks are active, or if the group is empty, this function
+ returns false.
+
+ \sa canUndo() setActiveStack()
+*/
+
+bool QUndoGroup::canRedo() const
+{
+ Q_D(const QUndoGroup);
+ return d->active != 0 && d->active->canRedo();
+}
+
+/*!
+ Returns the value of the active stack's QUndoStack::undoText().
+
+ If none of the stacks are active, or if the group is empty, this function
+ returns an empty string.
+
+ \sa redoText() setActiveStack()
+*/
+
+QString QUndoGroup::undoText() const
+{
+ Q_D(const QUndoGroup);
+ return d->active == 0 ? QString() : d->active->undoText();
+}
+
+/*!
+ Returns the value of the active stack's QUndoStack::redoText().
+
+ If none of the stacks are active, or if the group is empty, this function
+ returns an empty string.
+
+ \sa undoText() setActiveStack()
+*/
+
+QString QUndoGroup::redoText() const
+{
+ Q_D(const QUndoGroup);
+ return d->active == 0 ? QString() : d->active->redoText();
+}
+
+/*!
+ Returns the value of the active stack's QUndoStack::isClean().
+
+ If none of the stacks are active, or if the group is empty, this function
+ returns true.
+
+ \sa setActiveStack()
+*/
+
+bool QUndoGroup::isClean() const
+{
+ Q_D(const QUndoGroup);
+ return d->active == 0 || d->active->isClean();
+}
+
+#ifndef QT_NO_ACTION
+
+/*!
+ Creates an undo QAction object with parent \a parent.
+
+ Triggering this action will cause a call to QUndoStack::undo() on the active stack.
+ The text of this action will always be the text of the command which will be undone
+ in the next call to undo(), prefixed by \a prefix. If there is no command available
+ for undo, if the group is empty or if none of the stacks are active, this action will
+ be disabled.
+
+ If \a prefix is empty, the default prefix "Undo" is used.
+
+ \sa createRedoAction() canUndo() QUndoCommand::text()
+*/
+
+QAction *QUndoGroup::createUndoAction(QObject *parent, const QString &prefix) const
+{
+ QString pref = prefix.isEmpty() ? tr("Undo") : prefix;
+ QUndoAction *result = new QUndoAction(pref, parent);
+ result->setEnabled(canUndo());
+ result->setPrefixedText(undoText());
+ connect(this, SIGNAL(canUndoChanged(bool)),
+ result, SLOT(setEnabled(bool)));
+ connect(this, SIGNAL(undoTextChanged(QString)),
+ result, SLOT(setPrefixedText(QString)));
+ connect(result, SIGNAL(triggered()), this, SLOT(undo()));
+ return result;
+}
+
+/*!
+ Creates an redo QAction object with parent \a parent.
+
+ Triggering this action will cause a call to QUndoStack::redo() on the active stack.
+ The text of this action will always be the text of the command which will be redone
+ in the next call to redo(), prefixed by \a prefix. If there is no command available
+ for redo, if the group is empty or if none of the stacks are active, this action will
+ be disabled.
+
+ If \a prefix is empty, the default prefix "Undo" is used.
+
+ \sa createUndoAction() canRedo() QUndoCommand::text()
+*/
+
+QAction *QUndoGroup::createRedoAction(QObject *parent, const QString &prefix) const
+{
+ QString pref = prefix.isEmpty() ? tr("Redo") : prefix;
+ QUndoAction *result = new QUndoAction(pref, parent);
+ result->setEnabled(canRedo());
+ result->setPrefixedText(redoText());
+ connect(this, SIGNAL(canRedoChanged(bool)),
+ result, SLOT(setEnabled(bool)));
+ connect(this, SIGNAL(redoTextChanged(QString)),
+ result, SLOT(setPrefixedText(QString)));
+ connect(result, SIGNAL(triggered()), this, SLOT(redo()));
+ return result;
+}
+
+#endif // QT_NO_ACTION
+
+/*! \fn void QUndoGroup::activeStackChanged(QUndoStack *stack)
+
+ This signal is emitted whenever the active stack of the group changes. This can happen
+ when setActiveStack() or QUndoStack::setActive() is called, or when the active stack
+ is removed form the group. \a stack is the new active stack. If no stack is active,
+ \a stack is 0.
+
+ \sa setActiveStack() QUndoStack::setActive()
+*/
+
+/*! \fn void QUndoGroup::indexChanged(int idx)
+
+ This signal is emitted whenever the active stack emits QUndoStack::indexChanged()
+ or the active stack changes.
+
+ \a idx is the new current index, or 0 if the active stack is 0.
+
+ \sa QUndoStack::indexChanged() setActiveStack()
+*/
+
+/*! \fn void QUndoGroup::cleanChanged(bool clean)
+
+ This signal is emitted whenever the active stack emits QUndoStack::cleanChanged()
+ or the active stack changes.
+
+ \a clean is the new state, or true if the active stack is 0.
+
+ \sa QUndoStack::cleanChanged() setActiveStack()
+*/
+
+/*! \fn void QUndoGroup::canUndoChanged(bool canUndo)
+
+ This signal is emitted whenever the active stack emits QUndoStack::canUndoChanged()
+ or the active stack changes.
+
+ \a canUndo is the new state, or false if the active stack is 0.
+
+ \sa QUndoStack::canUndoChanged() setActiveStack()
+*/
+
+/*! \fn void QUndoGroup::canRedoChanged(bool canRedo)
+
+ This signal is emitted whenever the active stack emits QUndoStack::canRedoChanged()
+ or the active stack changes.
+
+ \a canRedo is the new state, or false if the active stack is 0.
+
+ \sa QUndoStack::canRedoChanged() setActiveStack()
+*/
+
+/*! \fn void QUndoGroup::undoTextChanged(const QString &undoText)
+
+ This signal is emitted whenever the active stack emits QUndoStack::undoTextChanged()
+ or the active stack changes.
+
+ \a undoText is the new state, or an empty string if the active stack is 0.
+
+ \sa QUndoStack::undoTextChanged() setActiveStack()
+*/
+
+/*! \fn void QUndoGroup::redoTextChanged(const QString &redoText)
+
+ This signal is emitted whenever the active stack emits QUndoStack::redoTextChanged()
+ or the active stack changes.
+
+ \a redoText is the new state, or an empty string if the active stack is 0.
+
+ \sa QUndoStack::redoTextChanged() setActiveStack()
+*/
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_UNDOGROUP
diff --git a/src/gui/util/qundogroup.h b/src/gui/util/qundogroup.h
new file mode 100644
index 0000000000..b2adb84b3f
--- /dev/null
+++ b/src/gui/util/qundogroup.h
@@ -0,0 +1,110 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QUNDOGROUP_H
+#define QUNDOGROUP_H
+
+#include <QtCore/qobject.h>
+#include <QtCore/qstring.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+class QUndoGroupPrivate;
+class QUndoStack;
+class QAction;
+
+QT_MODULE(Gui)
+
+#ifndef QT_NO_UNDOGROUP
+
+class Q_GUI_EXPORT QUndoGroup : public QObject
+{
+ Q_OBJECT
+ Q_DECLARE_PRIVATE(QUndoGroup)
+
+public:
+ explicit QUndoGroup(QObject *parent = 0);
+ ~QUndoGroup();
+
+ void addStack(QUndoStack *stack);
+ void removeStack(QUndoStack *stack);
+ QList<QUndoStack*> stacks() const;
+ QUndoStack *activeStack() const;
+
+#ifndef QT_NO_ACTION
+ QAction *createUndoAction(QObject *parent,
+ const QString &prefix = QString()) const;
+ QAction *createRedoAction(QObject *parent,
+ const QString &prefix = QString()) const;
+#endif // QT_NO_ACTION
+ bool canUndo() const;
+ bool canRedo() const;
+ QString undoText() const;
+ QString redoText() const;
+ bool isClean() const;
+
+public Q_SLOTS:
+ void undo();
+ void redo();
+ void setActiveStack(QUndoStack *stack);
+
+Q_SIGNALS:
+ void activeStackChanged(QUndoStack *stack);
+ void indexChanged(int idx);
+ void cleanChanged(bool clean);
+ void canUndoChanged(bool canUndo);
+ void canRedoChanged(bool canRedo);
+ void undoTextChanged(const QString &undoText);
+ void redoTextChanged(const QString &redoText);
+
+private:
+ Q_DISABLE_COPY(QUndoGroup)
+};
+
+#endif // QT_NO_UNDOGROUP
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QUNDOGROUP_H
diff --git a/src/gui/util/qundostack.cpp b/src/gui/util/qundostack.cpp
new file mode 100644
index 0000000000..11f65e385b
--- /dev/null
+++ b/src/gui/util/qundostack.cpp
@@ -0,0 +1,1129 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtCore/qdebug.h>
+#include "qundostack.h"
+#include "qundogroup.h"
+#include "qundostack_p.h"
+
+#ifndef QT_NO_UNDOCOMMAND
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QUndoCommand
+ \brief The QUndoCommand class is the base class of all commands stored on a QUndoStack.
+ \since 4.2
+ \ingroup misc
+
+ For an overview of Qt's Undo Framework, see the
+ \l{Overview of Qt's Undo Framework}{overview document}.
+
+ A QUndoCommand represents a single editing action on a document; for example,
+ inserting or deleting a block of text in a text editor. QUndoCommand can apply
+ a change to the document with redo() and undo the change with undo(). The
+ implementations for these functions must be provided in a derived class.
+
+ \snippet doc/src/snippets/code/src_gui_util_qundostack.cpp 0
+
+ A QUndoCommand has an associated text(). This is a short string
+ describing what the command does. It is used to update the text
+ properties of the stack's undo and redo actions; see
+ QUndoStack::createUndoAction() and QUndoStack::createRedoAction().
+
+ QUndoCommand objects are owned by the stack they were pushed on.
+ QUndoStack deletes a command if it has been undone and a new command is pushed. For example:
+
+\snippet doc/src/snippets/code/src_gui_util_qundostack.cpp 1
+
+ In effect, when a command is pushed, it becomes the top-most command
+ on the stack.
+
+ To support command compression, QUndoCommand has an id() and the virtual function
+ mergeWith(). These functions are used by QUndoStack::push().
+
+ To support command macros, a QUndoCommand object can have any number of child
+ commands. Undoing or redoing the parent command will cause the child
+ commands to be undone or redone. A command can be assigned
+ to a parent explicitly in the constructor. In this case, the command
+ will be owned by the parent.
+
+ The parent in this case is usually an empty command, in that it doesn't
+ provide its own implementation of undo() and redo(). Instead, it uses
+ the base implementations of these functions, which simply call undo() or
+ redo() on all its children. The parent should, however, have a meaningful
+ text().
+
+ \snippet doc/src/snippets/code/src_gui_util_qundostack.cpp 2
+
+ Another way to create macros is to use the convenience functions
+ QUndoStack::beginMacro() and QUndoStack::endMacro().
+
+ \sa QUndoStack
+*/
+
+/*!
+ Constructs a QUndoCommand object with the given \a parent and \a text.
+
+ If \a parent is not 0, this command is appended to parent's child list.
+ The parent command then owns this command and will delete it in its
+ destructor.
+
+ \sa ~QUndoCommand()
+*/
+
+QUndoCommand::QUndoCommand(const QString &text, QUndoCommand *parent)
+{
+ d = new QUndoCommandPrivate;
+ if (parent != 0)
+ parent->d->child_list.append(this);
+ d->text = text;
+}
+
+/*!
+ Constructs a QUndoCommand object with parent \a parent.
+
+ If \a parent is not 0, this command is appended to parent's child list.
+ The parent command then owns this command and will delete it in its
+ destructor.
+
+ \sa ~QUndoCommand()
+*/
+
+QUndoCommand::QUndoCommand(QUndoCommand *parent)
+{
+ d = new QUndoCommandPrivate;
+ if (parent != 0)
+ parent->d->child_list.append(this);
+}
+
+/*!
+ Destroys the QUndoCommand object and all child commands.
+
+ \sa QUndoCommand()
+*/
+
+QUndoCommand::~QUndoCommand()
+{
+ qDeleteAll(d->child_list);
+ delete d;
+}
+
+/*!
+ Returns the ID of this command.
+
+ A command ID is used in command compression. It must be an integer unique to
+ this command's class, or -1 if the command doesn't support compression.
+
+ If the command supports compression this function must be overridden in the
+ derived class to return the correct ID. The base implementation returns -1.
+
+ QUndoStack::push() will only try to merge two commands if they have the
+ same ID, and the ID is not -1.
+
+ \sa mergeWith(), QUndoStack::push()
+*/
+
+int QUndoCommand::id() const
+{
+ return -1;
+}
+
+/*!
+ Attempts to merge this command with \a command. Returns true on
+ success; otherwise returns false.
+
+ If this function returns true, calling this command's redo() must have the same
+ effect as redoing both this command and \a command.
+ Similarly, calling this command's undo() must have the same effect as undoing
+ \a command and this command.
+
+ QUndoStack will only try to merge two commands if they have the same id, and
+ the id is not -1.
+
+ The default implementation returns false.
+
+ \snippet doc/src/snippets/code/src_gui_util_qundostack.cpp 3
+
+ \sa id() QUndoStack::push()
+*/
+
+bool QUndoCommand::mergeWith(const QUndoCommand *command)
+{
+ Q_UNUSED(command);
+ return false;
+}
+
+/*!
+ Applies a change to the document. This function must be implemented in
+ the derived class. Calling QUndoStack::push(),
+ QUndoStack::undo() or QUndoStack::redo() from this function leads to
+ undefined beahavior.
+
+ The default implementation calls redo() on all child commands.
+
+ \sa undo()
+*/
+
+void QUndoCommand::redo()
+{
+ for (int i = 0; i < d->child_list.size(); ++i)
+ d->child_list.at(i)->redo();
+}
+
+/*!
+ Reverts a change to the document. After undo() is called, the state of
+ the document should be the same as before redo() was called. This function must
+ be implemented in the derived class. Calling QUndoStack::push(),
+ QUndoStack::undo() or QUndoStack::redo() from this function leads to
+ undefined beahavior.
+
+ The default implementation calls undo() on all child commands in reverse order.
+
+ \sa redo()
+*/
+
+void QUndoCommand::undo()
+{
+ for (int i = d->child_list.size() - 1; i >= 0; --i)
+ d->child_list.at(i)->undo();
+}
+
+/*!
+ Returns a short text string describing what this command does; for example,
+ "insert text".
+
+ The text is used when the text properties of the stack's undo and redo
+ actions are updated.
+
+ \sa setText(), QUndoStack::createUndoAction(), QUndoStack::createRedoAction()
+*/
+
+QString QUndoCommand::text() const
+{
+ return d->text;
+}
+
+/*!
+ Sets the command's text to be the \a text specified.
+
+ The specified text should be a short user-readable string describing what this
+ command does.
+
+ \sa text() QUndoStack::createUndoAction() QUndoStack::createRedoAction()
+*/
+
+void QUndoCommand::setText(const QString &text)
+{
+ d->text = text;
+}
+
+/*!
+ \since 4.4
+
+ Returns the number of child commands in this command.
+
+ \sa child()
+*/
+
+int QUndoCommand::childCount() const
+{
+ return d->child_list.count();
+}
+
+/*!
+ \since 4.4
+
+ Returns the child command at \a index.
+
+ \sa childCount(), QUndoStack::command()
+*/
+
+const QUndoCommand *QUndoCommand::child(int index) const
+{
+ if (index < 0 || index >= d->child_list.count())
+ return 0;
+ return d->child_list.at(index);
+}
+
+#endif // QT_NO_UNDOCOMMAND
+
+#ifndef QT_NO_UNDOSTACK
+
+/*!
+ \class QUndoStack
+ \brief The QUndoStack class is a stack of QUndoCommand objects.
+ \since 4.2
+ \ingroup misc
+
+ For an overview of Qt's Undo Framework, see the
+ \l{Overview of Qt's Undo Framework}{overview document}.
+
+ An undo stack maintains a stack of commands that have been applied to a
+ document.
+
+ New commands are pushed on the stack using push(). Commands can be
+ undone and redone using undo() and redo(), or by triggering the
+ actions returned by createUndoAction() and createRedoAction().
+
+ QUndoStack keeps track of the \a current command. This is the command
+ which will be executed by the next call to redo(). The index of this
+ command is returned by index(). The state of the edited object can be
+ rolled forward or back using setIndex(). If the top-most command on the
+ stack has already been redone, index() is equal to count().
+
+ QUndoStack provides support for undo and redo actions, command
+ compression, command macros, and supports the concept of a
+ \e{clean state}.
+
+ \section1 Undo and Redo Actions
+
+ QUndoStack provides convenient undo and redo QAction objects, which
+ can be inserted into a menu or a toolbar. When commands are undone or
+ redone, QUndoStack updates the text properties of these actions
+ to reflect what change they will trigger. The actions are also disabled
+ when no command is available for undo or redo. These actions
+ are returned by QUndoStack::createUndoAction() and QUndoStack::createRedoAction().
+
+ \section1 Command Compression and Macros
+
+ Command compression is useful when several commands can be compressed
+ into a single command that can be undone and redone in a single operation.
+ For example, when a user types a character in a text editor, a new command
+ is created. This command inserts the character into the document at the
+ cursor position. However, it is more convenient for the user to be able
+ to undo or redo typing of whole words, sentences, or paragraphs.
+ Command compression allows these single-character commands to be merged
+ into a single command which inserts or deletes sections of text.
+ For more information, see QUndoCommand::mergeWith() and push().
+
+ A command macro is a sequence of commands, all of which are undone and
+ redone in one go. Command macros are created by giving a command a list
+ of child commands.
+ Undoing or redoing the parent command will cause the child commands to
+ be undone or redone. Command macros may be created explicitly
+ by specifying a parent in the QUndoCommand constructor, or by using the
+ convenience functions beginMacro() and endMacro().
+
+ Although command compression and macros appear to have the same effect to the
+ user, they often have different uses in an application. Commands that
+ perform small changes to a document may be usefully compressed if there is
+ no need to individually record them, and if only larger changes are relevant
+ to the user.
+ However, for commands that need to be recorded individually, or those that
+ cannot be compressed, it is useful to use macros to provide a more convenient
+ user experience while maintaining a record of each command.
+
+ \section1 Clean State
+
+ QUndoStack supports the concept of a clean state. When the
+ document is saved to disk, the stack can be marked as clean using
+ setClean(). Whenever the stack returns to this state through undoing and
+ redoing commands, it emits the signal cleanChanged(). This signal
+ is also emitted when the stack leaves the clean state. This signal is
+ usually used to enable and disable the save actions in the application,
+ and to update the document's title to reflect that it contains unsaved
+ changes.
+
+ \sa QUndoCommand, QUndoView
+*/
+
+#ifndef QT_NO_ACTION
+
+QUndoAction::QUndoAction(const QString &prefix, QObject *parent)
+ : QAction(parent)
+{
+ m_prefix = prefix;
+}
+
+void QUndoAction::setPrefixedText(const QString &text)
+{
+ QString s = m_prefix;
+ if (!m_prefix.isEmpty() && !text.isEmpty())
+ s.append(QLatin1Char(' '));
+ s.append(text);
+ setText(s);
+}
+
+#endif // QT_NO_ACTION
+
+/*! \internal
+ Sets the current index to \a idx, emitting appropriate signals. If \a clean is true,
+ makes \a idx the clean index as well.
+*/
+
+void QUndoStackPrivate::setIndex(int idx, bool clean)
+{
+ Q_Q(QUndoStack);
+
+ bool was_clean = index == clean_index;
+
+ if (idx != index) {
+ index = idx;
+ emit q->indexChanged(index);
+ emit q->canUndoChanged(q->canUndo());
+ emit q->undoTextChanged(q->undoText());
+ emit q->canRedoChanged(q->canRedo());
+ emit q->redoTextChanged(q->redoText());
+ }
+
+ if (clean)
+ clean_index = index;
+
+ bool is_clean = index == clean_index;
+ if (is_clean != was_clean)
+ emit q->cleanChanged(is_clean);
+}
+
+/*! \internal
+ If the number of commands on the stack exceedes the undo limit, deletes commands from
+ the bottom of the stack.
+
+ Returns true if commands were deleted.
+*/
+
+bool QUndoStackPrivate::checkUndoLimit()
+{
+ if (undo_limit <= 0 || !macro_stack.isEmpty() || undo_limit >= command_list.count())
+ return false;
+
+ int del_count = command_list.count() - undo_limit;
+
+ for (int i = 0; i < del_count; ++i)
+ delete command_list.takeFirst();
+
+ index -= del_count;
+ if (clean_index != -1) {
+ if (clean_index < del_count)
+ clean_index = -1; // we've deleted the clean command
+ else
+ clean_index -= del_count;
+ }
+
+ return true;
+}
+
+/*!
+ Constructs an empty undo stack with the parent \a parent. The
+ stack will initally be in the clean state. If \a parent is a
+ QUndoGroup object, the stack is automatically added to the group.
+
+ \sa push()
+*/
+
+QUndoStack::QUndoStack(QObject *parent)
+ : QObject(*(new QUndoStackPrivate), parent)
+{
+#ifndef QT_NO_UNDOGROUP
+ if (QUndoGroup *group = qobject_cast<QUndoGroup*>(parent))
+ group->addStack(this);
+#endif
+}
+
+/*!
+ Destroys the undo stack, deleting any commands that are on it. If the
+ stack is in a QUndoGroup, the stack is automatically removed from the group.
+
+ \sa QUndoStack()
+*/
+
+QUndoStack::~QUndoStack()
+{
+#ifndef QT_NO_UNDOGROUP
+ Q_D(QUndoStack);
+ if (d->group != 0)
+ d->group->removeStack(this);
+#endif
+ clear();
+}
+
+/*!
+ Clears the command stack by deleting all commands on it, and returns the stack
+ to the clean state.
+
+ Commands are not undone or redone; the state of the edited object remains
+ unchanged.
+
+ This function is usually used when the contents of the document are
+ abandoned.
+
+ \sa QUndoStack()
+*/
+
+void QUndoStack::clear()
+{
+ Q_D(QUndoStack);
+
+ if (d->command_list.isEmpty())
+ return;
+
+ bool was_clean = isClean();
+
+ d->macro_stack.clear();
+ qDeleteAll(d->command_list);
+ d->command_list.clear();
+
+ d->index = 0;
+ d->clean_index = 0;
+
+ emit indexChanged(0);
+ emit canUndoChanged(false);
+ emit undoTextChanged(QString());
+ emit canRedoChanged(false);
+ emit redoTextChanged(QString());
+
+ if (!was_clean)
+ emit cleanChanged(true);
+}
+
+/*!
+ Pushes \a cmd on the stack or merges it with the most recently executed command.
+ In either case, executes \a cmd by calling its redo() function.
+
+ If \a cmd's id is not -1, and if the id is the same as that of the
+ most recently executed command, QUndoStack will attempt to merge the two
+ commands by calling QUndoCommand::mergeWith() on the most recently executed
+ command. If QUndoCommand::mergeWith() returns true, \a cmd is deleted.
+
+ In all other cases \a cmd is simply pushed on the stack.
+
+ If commands were undone before \a cmd was pushed, the current command and
+ all commands above it are deleted. Hence \a cmd always ends up being the
+ top-most on the stack.
+
+ Once a command is pushed, the stack takes ownership of it. There
+ are no getters to return the command, since modifying it after it has
+ been executed will almost always lead to corruption of the document's
+ state.
+
+ \sa QUndoCommand::id() QUndoCommand::mergeWith()
+*/
+
+void QUndoStack::push(QUndoCommand *cmd)
+{
+ Q_D(QUndoStack);
+ cmd->redo();
+
+ bool macro = !d->macro_stack.isEmpty();
+
+ QUndoCommand *cur = 0;
+ if (macro) {
+ QUndoCommand *macro_cmd = d->macro_stack.last();
+ if (!macro_cmd->d->child_list.isEmpty())
+ cur = macro_cmd->d->child_list.last();
+ } else {
+ if (d->index > 0)
+ cur = d->command_list.at(d->index - 1);
+ while (d->index < d->command_list.size())
+ delete d->command_list.takeLast();
+ if (d->clean_index > d->index)
+ d->clean_index = -1; // we've deleted the clean state
+ }
+
+ bool try_merge = cur != 0
+ && cur->id() != -1
+ && cur->id() == cmd->id()
+ && (macro || d->index != d->clean_index);
+
+ if (try_merge && cur->mergeWith(cmd)) {
+ delete cmd;
+ if (!macro) {
+ emit indexChanged(d->index);
+ emit canUndoChanged(canUndo());
+ emit undoTextChanged(undoText());
+ emit canRedoChanged(canRedo());
+ emit redoTextChanged(redoText());
+ }
+ } else {
+ if (macro) {
+ d->macro_stack.last()->d->child_list.append(cmd);
+ } else {
+ d->command_list.append(cmd);
+ d->checkUndoLimit();
+ d->setIndex(d->index + 1, false);
+ }
+ }
+}
+
+/*!
+ Marks the stack as clean and emits cleanChanged() if the stack was
+ not already clean.
+
+ Whenever the stack returns to this state through the use of undo/redo
+ commands, it emits the signal cleanChanged(). This signal is also
+ emitted when the stack leaves the clean state.
+
+ \sa isClean(), cleanIndex()
+*/
+
+void QUndoStack::setClean()
+{
+ Q_D(QUndoStack);
+ if (!d->macro_stack.isEmpty()) {
+ qWarning("QUndoStack::setClean(): cannot set clean in the middle of a macro");
+ return;
+ }
+
+ d->setIndex(d->index, true);
+}
+
+/*!
+ If the stack is in the clean state, returns true; otherwise returns false.
+
+ \sa setClean() cleanIndex()
+*/
+
+bool QUndoStack::isClean() const
+{
+ Q_D(const QUndoStack);
+ if (!d->macro_stack.isEmpty())
+ return false;
+ return d->clean_index == d->index;
+}
+
+/*!
+ Returns the clean index. This is the index at which setClean() was called.
+
+ A stack may not have a clean index. This happens if a document is saved,
+ some commands are undone, then a new command is pushed. Since
+ push() deletes all the undone commands before pushing the new command, the stack
+ can't return to the clean state again. In this case, this function returns -1.
+
+ \sa isClean() setClean()
+*/
+
+int QUndoStack::cleanIndex() const
+{
+ Q_D(const QUndoStack);
+ return d->clean_index;
+}
+
+/*!
+ Undoes the command below the current command by calling QUndoCommand::undo().
+ Decrements the current command index.
+
+ If the stack is empty, or if the bottom command on the stack has already been
+ undone, this function does nothing.
+
+ \sa redo() index()
+*/
+
+void QUndoStack::undo()
+{
+ Q_D(QUndoStack);
+ if (d->index == 0)
+ return;
+
+ if (!d->macro_stack.isEmpty()) {
+ qWarning("QUndoStack::undo(): cannot undo in the middle of a macro");
+ return;
+ }
+
+ int idx = d->index - 1;
+ d->command_list.at(idx)->undo();
+ d->setIndex(idx, false);
+}
+
+/*!
+ Redoes the current command by calling QUndoCommand::redo(). Increments the current
+ command index.
+
+ If the stack is empty, or if the top command on the stack has already been
+ redone, this function does nothing.
+
+ \sa undo() index()
+*/
+
+void QUndoStack::redo()
+{
+ Q_D(QUndoStack);
+ if (d->index == d->command_list.size())
+ return;
+
+ if (!d->macro_stack.isEmpty()) {
+ qWarning("QUndoStack::redo(): cannot redo in the middle of a macro");
+ return;
+ }
+
+ d->command_list.at(d->index)->redo();
+ d->setIndex(d->index + 1, false);
+}
+
+/*!
+ Returns the number of commands on the stack. Macro commands are counted as
+ one command.
+
+ \sa index() setIndex() command()
+*/
+
+int QUndoStack::count() const
+{
+ Q_D(const QUndoStack);
+ return d->command_list.size();
+}
+
+/*!
+ Returns the index of the current command. This is the command that will be
+ executed on the next call to redo(). It is not always the top-most command
+ on the stack, since a number of commands may have been undone.
+
+ \sa undo() redo() count()
+*/
+
+int QUndoStack::index() const
+{
+ Q_D(const QUndoStack);
+ return d->index;
+}
+
+/*!
+ Repeatedly calls undo() or redo() until the the current command index reaches
+ \a idx. This function can be used to roll the state of the document forwards
+ of backwards. indexChanged() is emitted only once.
+
+ \sa index() count() undo() redo()
+*/
+
+void QUndoStack::setIndex(int idx)
+{
+ Q_D(QUndoStack);
+ if (!d->macro_stack.isEmpty()) {
+ qWarning("QUndoStack::setIndex(): cannot set index in the middle of a macro");
+ return;
+ }
+
+ if (idx < 0)
+ idx = 0;
+ else if (idx > d->command_list.size())
+ idx = d->command_list.size();
+
+ int i = d->index;
+ while (i < idx)
+ d->command_list.at(i++)->redo();
+ while (i > idx)
+ d->command_list.at(--i)->undo();
+
+ d->setIndex(idx, false);
+}
+
+/*!
+ Returns true if there is a command available for undo; otherwise returns false.
+
+ This function returns false if the stack is empty, or if the bottom command
+ on the stack has already been undone.
+
+ Synonymous with index() == 0.
+
+ \sa index() canRedo()
+*/
+
+bool QUndoStack::canUndo() const
+{
+ Q_D(const QUndoStack);
+ if (!d->macro_stack.isEmpty())
+ return false;
+ return d->index > 0;
+}
+
+/*!
+ Returns true if there is a command available for redo; otherwise returns false.
+
+ This function returns false if the stack is empty or if the top command
+ on the stack has already been redone.
+
+ Synonymous with index() == count().
+
+ \sa index() canUndo()
+*/
+
+bool QUndoStack::canRedo() const
+{
+ Q_D(const QUndoStack);
+ if (!d->macro_stack.isEmpty())
+ return false;
+ return d->index < d->command_list.size();
+}
+
+/*!
+ Returns the text of the command which will be undone in the next call to undo().
+
+ \sa QUndoCommand::text() redoText()
+*/
+
+QString QUndoStack::undoText() const
+{
+ Q_D(const QUndoStack);
+ if (!d->macro_stack.isEmpty())
+ return QString();
+ if (d->index > 0)
+ return d->command_list.at(d->index - 1)->text();
+ return QString();
+}
+
+/*!
+ Returns the text of the command which will be redone in the next call to redo().
+
+ \sa QUndoCommand::text() undoText()
+*/
+
+QString QUndoStack::redoText() const
+{
+ Q_D(const QUndoStack);
+ if (!d->macro_stack.isEmpty())
+ return QString();
+ if (d->index < d->command_list.size())
+ return d->command_list.at(d->index)->text();
+ return QString();
+}
+
+#ifndef QT_NO_ACTION
+
+/*!
+ Creates an undo QAction object with the given \a parent.
+
+ Triggering this action will cause a call to undo(). The text of this action
+ is the text of the command which will be undone in the next call to undo(),
+ prefixed by the specified \a prefix. If there is no command available for undo,
+ this action will be disabled.
+
+ If \a prefix is empty, the default prefix "Undo" is used.
+
+ \sa createRedoAction(), canUndo(), QUndoCommand::text()
+*/
+
+QAction *QUndoStack::createUndoAction(QObject *parent, const QString &prefix) const
+{
+ QString pref = prefix.isEmpty() ? tr("Undo") : prefix;
+ QUndoAction *result = new QUndoAction(pref, parent);
+ result->setEnabled(canUndo());
+ result->setPrefixedText(undoText());
+ connect(this, SIGNAL(canUndoChanged(bool)),
+ result, SLOT(setEnabled(bool)));
+ connect(this, SIGNAL(undoTextChanged(QString)),
+ result, SLOT(setPrefixedText(QString)));
+ connect(result, SIGNAL(triggered()), this, SLOT(undo()));
+ return result;
+}
+
+/*!
+ Creates an redo QAction object with the given \a parent.
+
+ Triggering this action will cause a call to redo(). The text of this action
+ is the text of the command which will be redone in the next call to redo(),
+ prefixed by the specified \a prefix. If there is no command available for redo,
+ this action will be disabled.
+
+ If \a prefix is empty, the default prefix "Redo" is used.
+
+ \sa createUndoAction(), canRedo(), QUndoCommand::text()
+*/
+
+QAction *QUndoStack::createRedoAction(QObject *parent, const QString &prefix) const
+{
+ QString pref = prefix.isEmpty() ? tr("Redo") : prefix;
+ QUndoAction *result = new QUndoAction(pref, parent);
+ result->setEnabled(canRedo());
+ result->setPrefixedText(redoText());
+ connect(this, SIGNAL(canRedoChanged(bool)),
+ result, SLOT(setEnabled(bool)));
+ connect(this, SIGNAL(redoTextChanged(QString)),
+ result, SLOT(setPrefixedText(QString)));
+ connect(result, SIGNAL(triggered()), this, SLOT(redo()));
+ return result;
+}
+
+#endif // QT_NO_ACTION
+
+/*!
+ Begins composition of a macro command with the given \a text description.
+
+ An empty command described by the specified \a text is pushed on the stack.
+ Any subsequent commands pushed on the stack will be appended to the empty
+ command's children until endMacro() is called.
+
+ Calls to beginMacro() and endMacro() may be nested, but every call to
+ beginMacro() must have a matching call to endMacro().
+
+ While a macro is composed, the stack is disabled. This means that:
+ \list
+ \i indexChanged() and cleanChanged() are not emitted,
+ \i canUndo() and canRedo() return false,
+ \i calling undo() or redo() has no effect,
+ \i the undo/redo actions are disabled.
+ \endlist
+
+ The stack becomes enabled and appropriate signals are emitted when endMacro()
+ is called for the outermost macro.
+
+ \snippet doc/src/snippets/code/src_gui_util_qundostack.cpp 4
+
+ This code is equivalent to:
+
+ \snippet doc/src/snippets/code/src_gui_util_qundostack.cpp 5
+
+ \sa endMacro()
+*/
+
+void QUndoStack::beginMacro(const QString &text)
+{
+ Q_D(QUndoStack);
+ QUndoCommand *cmd = new QUndoCommand();
+ cmd->setText(text);
+
+ if (d->macro_stack.isEmpty()) {
+ while (d->index < d->command_list.size())
+ delete d->command_list.takeLast();
+ if (d->clean_index > d->index)
+ d->clean_index = -1; // we've deleted the clean state
+ d->command_list.append(cmd);
+ } else {
+ d->macro_stack.last()->d->child_list.append(cmd);
+ }
+ d->macro_stack.append(cmd);
+
+ if (d->macro_stack.count() == 1) {
+ emit canUndoChanged(false);
+ emit undoTextChanged(QString());
+ emit canRedoChanged(false);
+ emit redoTextChanged(QString());
+ }
+}
+
+/*!
+ Ends composition of a macro command.
+
+ If this is the outermost macro in a set nested macros, this function emits
+ indexChanged() once for the entire macro command.
+
+ \sa beginMacro()
+*/
+
+void QUndoStack::endMacro()
+{
+ Q_D(QUndoStack);
+ if (d->macro_stack.isEmpty()) {
+ qWarning("QUndoStack::endMacro(): no matching beginMacro()");
+ return;
+ }
+
+ d->macro_stack.removeLast();
+
+ if (d->macro_stack.isEmpty()) {
+ d->checkUndoLimit();
+ d->setIndex(d->index + 1, false);
+ }
+}
+
+/*!
+ \since 4.4
+
+ Returns a const pointer to the command at \a index.
+
+ This function returns a const pointer, because modifying a command,
+ once it has been pushed onto the stack and executed, almost always
+ causes corruption of the state of the document, if the command is
+ later undone or redone.
+
+ \sa QUndoCommand::child()
+*/
+const QUndoCommand *QUndoStack::command(int index) const
+{
+ Q_D(const QUndoStack);
+
+ if (index < 0 || index >= d->command_list.count())
+ return 0;
+ return d->command_list.at(index);
+}
+
+/*!
+ Returns the text of the command at index \a idx.
+
+ \sa beginMacro()
+*/
+
+QString QUndoStack::text(int idx) const
+{
+ Q_D(const QUndoStack);
+
+ if (idx < 0 || idx >= d->command_list.size())
+ return QString();
+ return d->command_list.at(idx)->text();
+}
+
+/*!
+ \property QUndoStack::undoLimit
+ \brief the maximum number of commands on this stack.
+ \since 4.3
+
+ When the number of commands on a stack exceedes the stack's undoLimit, commands are
+ deleted from the bottom of the stack. Macro commands (commands with child commands)
+ are treated as one command. The default value is 0, which means that there is no
+ limit.
+
+ This property may only be set when the undo stack is empty, since setting it on a
+ non-empty stack might delete the command at the current index. Calling setUndoLimit()
+ on a non-empty stack prints a warning and does nothing.
+*/
+
+void QUndoStack::setUndoLimit(int limit)
+{
+ Q_D(QUndoStack);
+
+ if (!d->command_list.isEmpty()) {
+ qWarning("QUndoStack::setUndoLimit(): an undo limit can only be set when the stack is empty");
+ return;
+ }
+
+ if (limit == d->undo_limit)
+ return;
+ d->undo_limit = limit;
+ d->checkUndoLimit();
+}
+
+int QUndoStack::undoLimit() const
+{
+ Q_D(const QUndoStack);
+
+ return d->undo_limit;
+}
+
+/*!
+ \property QUndoStack::active
+ \brief the active status of this stack.
+
+ An application often has multiple undo stacks, one for each opened document. The active
+ stack is the one associated with the currently active document. If the stack belongs
+ to a QUndoGroup, calls to QUndoGroup::undo() or QUndoGroup::redo() will be forwarded
+ to this stack when it is active. If the QUndoGroup is watched by a QUndoView, the view
+ will display the contents of this stack when it is active. If the stack does not belong to
+ a QUndoGroup, making it active has no effect.
+
+ It is the programmer's responsibility to specify which stack is active by
+ calling setActive(), usually when the associated document window receives focus.
+
+ \sa QUndoGroup
+*/
+
+void QUndoStack::setActive(bool active)
+{
+#ifdef QT_NO_UNDOGROUP
+ Q_UNUSED(active);
+#else
+ Q_D(QUndoStack);
+
+ if (d->group != 0) {
+ if (active)
+ d->group->setActiveStack(this);
+ else if (d->group->activeStack() == this)
+ d->group->setActiveStack(0);
+ }
+#endif
+}
+
+bool QUndoStack::isActive() const
+{
+#ifdef QT_NO_UNDOGROUP
+ return true;
+#else
+ Q_D(const QUndoStack);
+ return d->group == 0 || d->group->activeStack() == this;
+#endif
+}
+
+/*!
+ \fn void QUndoStack::indexChanged(int idx)
+
+ This signal is emitted whenever a command modifies the state of the document.
+ This happens when a command is undone or redone. When a macro
+ command is undone or redone, or setIndex() is called, this signal
+ is emitted only once.
+
+ \a idx specifies the index of the current command, ie. the command which will be
+ executed on the next call to redo().
+
+ \sa index() setIndex()
+*/
+
+/*!
+ \fn void QUndoStack::cleanChanged(bool clean)
+
+ This signal is emitted whenever the stack enters or leaves the clean state.
+ If \a clean is true, the stack is in a clean state; otherwise this signal
+ indicates that it has left the clean state.
+
+ \sa isClean() setClean()
+*/
+
+/*!
+ \fn void QUndoStack::undoTextChanged(const QString &undoText)
+
+ This signal is emitted whenever the value of undoText() changes. It is
+ used to update the text property of the undo action returned by createUndoAction().
+ \a undoText specifies the new text.
+*/
+
+/*!
+ \fn void QUndoStack::canUndoChanged(bool canUndo)
+
+ This signal is emitted whenever the value of canUndo() changes. It is
+ used to enable or disable the undo action returned by createUndoAction().
+ \a canUndo specifies the new value.
+*/
+
+/*!
+ \fn void QUndoStack::redoTextChanged(const QString &redoText)
+
+ This signal is emitted whenever the value of redoText() changes. It is
+ used to update the text property of the redo action returned by createRedoAction().
+ \a redoText specifies the new text.
+*/
+
+/*!
+ \fn void QUndoStack::canRedoChanged(bool canRedo)
+
+ This signal is emitted whenever the value of canRedo() changes. It is
+ used to enable or disable the redo action returned by createRedoAction().
+ \a canRedo specifies the new value.
+*/
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_UNDOSTACK
diff --git a/src/gui/util/qundostack.h b/src/gui/util/qundostack.h
new file mode 100644
index 0000000000..9fec136821
--- /dev/null
+++ b/src/gui/util/qundostack.h
@@ -0,0 +1,158 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QUNDOSTACK_H
+#define QUNDOSTACK_H
+
+#include <QtCore/qobject.h>
+#include <QtCore/qstring.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+class QAction;
+class QUndoCommandPrivate;
+class QUndoStackPrivate;
+
+#ifndef QT_NO_UNDOCOMMAND
+
+class Q_GUI_EXPORT QUndoCommand
+{
+ QUndoCommandPrivate *d;
+
+public:
+ explicit QUndoCommand(QUndoCommand *parent = 0);
+ explicit QUndoCommand(const QString &text, QUndoCommand *parent = 0);
+ virtual ~QUndoCommand();
+
+ virtual void undo();
+ virtual void redo();
+
+ QString text() const;
+ void setText(const QString &text);
+
+ virtual int id() const;
+ virtual bool mergeWith(const QUndoCommand *other);
+
+ int childCount() const;
+ const QUndoCommand *child(int index) const;
+
+private:
+ Q_DISABLE_COPY(QUndoCommand)
+ friend class QUndoStack;
+};
+
+#endif // QT_NO_UNDOCOMMAND
+
+#ifndef QT_NO_UNDOSTACK
+
+class Q_GUI_EXPORT QUndoStack : public QObject
+{
+ Q_OBJECT
+ Q_DECLARE_PRIVATE(QUndoStack)
+ Q_PROPERTY(bool active READ isActive WRITE setActive)
+ Q_PROPERTY(int undoLimit READ undoLimit WRITE setUndoLimit)
+
+public:
+ explicit QUndoStack(QObject *parent = 0);
+ ~QUndoStack();
+ void clear();
+
+ void push(QUndoCommand *cmd);
+
+ bool canUndo() const;
+ bool canRedo() const;
+ QString undoText() const;
+ QString redoText() const;
+
+ int count() const;
+ int index() const;
+ QString text(int idx) const;
+
+#ifndef QT_NO_ACTION
+ QAction *createUndoAction(QObject *parent,
+ const QString &prefix = QString()) const;
+ QAction *createRedoAction(QObject *parent,
+ const QString &prefix = QString()) const;
+#endif // QT_NO_ACTION
+
+ bool isActive() const;
+ bool isClean() const;
+ int cleanIndex() const;
+
+ void beginMacro(const QString &text);
+ void endMacro();
+
+ void setUndoLimit(int limit);
+ int undoLimit() const;
+
+ const QUndoCommand *command(int index) const;
+
+public Q_SLOTS:
+ void setClean();
+ void setIndex(int idx);
+ void undo();
+ void redo();
+ void setActive(bool active = true);
+
+Q_SIGNALS:
+ void indexChanged(int idx);
+ void cleanChanged(bool clean);
+ void canUndoChanged(bool canUndo);
+ void canRedoChanged(bool canRedo);
+ void undoTextChanged(const QString &undoText);
+ void redoTextChanged(const QString &redoText);
+
+private:
+ Q_DISABLE_COPY(QUndoStack)
+ friend class QUndoGroup;
+};
+
+#endif // QT_NO_UNDOSTACK
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QUNDOSTACK_H
diff --git a/src/gui/util/qundostack_p.h b/src/gui/util/qundostack_p.h
new file mode 100644
index 0000000000..f1e11958d5
--- /dev/null
+++ b/src/gui/util/qundostack_p.h
@@ -0,0 +1,111 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QUNDOSTACK_P_H
+#define QUNDOSTACK_P_H
+
+#include <private/qobject_p.h>
+#include <QtCore/qlist.h>
+#include <QtCore/qstring.h>
+#include <QtGui/qaction.h>
+
+#include "qundostack.h"
+
+QT_BEGIN_NAMESPACE
+class QUndoCommand;
+class QUndoGroup;
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of qapplication_*.cpp, qwidget*.cpp and qfiledialog.cpp. This header
+// file may change from version to version without notice, or even be removed.
+//
+// We mean it.
+//
+
+class QUndoCommandPrivate
+{
+public:
+ QUndoCommandPrivate() : id(-1) {}
+ QList<QUndoCommand*> child_list;
+ QString text;
+ int id;
+};
+
+#ifndef QT_NO_UNDOSTACK
+
+class QUndoStackPrivate : public QObjectPrivate
+{
+ Q_DECLARE_PUBLIC(QUndoStack)
+public:
+ QUndoStackPrivate() : index(0), clean_index(0), group(0), undo_limit(0) {}
+
+ QList<QUndoCommand*> command_list;
+ QList<QUndoCommand*> macro_stack;
+ int index;
+ int clean_index;
+ QUndoGroup *group;
+ int undo_limit;
+
+ void setIndex(int idx, bool clean);
+ bool checkUndoLimit();
+};
+
+#ifndef QT_NO_ACTION
+class QUndoAction : public QAction
+{
+ Q_OBJECT
+public:
+ QUndoAction(const QString &prefix, QObject *parent = 0);
+public Q_SLOTS:
+ void setPrefixedText(const QString &text);
+private:
+ QString m_prefix;
+};
+#endif // QT_NO_ACTION
+
+
+QT_END_NAMESPACE
+#endif // QT_NO_UNDOSTACK
+#endif // QUNDOSTACK_P_H
diff --git a/src/gui/util/qundoview.cpp b/src/gui/util/qundoview.cpp
new file mode 100644
index 0000000000..6dbb2d48fd
--- /dev/null
+++ b/src/gui/util/qundoview.cpp
@@ -0,0 +1,476 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qundostack.h"
+#include "qundoview.h"
+
+#ifndef QT_NO_UNDOVIEW
+
+#include "qundogroup.h"
+#include <QtCore/qabstractitemmodel.h>
+#include <QtCore/qpointer.h>
+#include <QtGui/qicon.h>
+#include <private/qlistview_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QUndoModel : public QAbstractItemModel
+{
+ Q_OBJECT
+public:
+ QUndoModel(QObject *parent = 0);
+
+ QUndoStack *stack() const;
+
+ virtual QModelIndex index(int row, int column,
+ const QModelIndex &parent = QModelIndex()) const;
+ virtual QModelIndex parent(const QModelIndex &child) const;
+ virtual int rowCount(const QModelIndex &parent = QModelIndex()) const;
+ virtual int columnCount(const QModelIndex &parent = QModelIndex()) const;
+ virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
+
+ QModelIndex selectedIndex() const;
+ QItemSelectionModel *selectionModel() const;
+
+ QString emptyLabel() const;
+ void setEmptyLabel(const QString &label);
+
+ void setCleanIcon(const QIcon &icon);
+ QIcon cleanIcon() const;
+
+public slots:
+ void setStack(QUndoStack *stack);
+
+private slots:
+ void stackChanged();
+ void stackDestroyed(QObject *obj);
+ void setStackCurrentIndex(const QModelIndex &index);
+
+private:
+ QUndoStack *m_stack;
+ QItemSelectionModel *m_sel_model;
+ QString m_emty_label;
+ QIcon m_clean_icon;
+};
+
+QUndoModel::QUndoModel(QObject *parent)
+ : QAbstractItemModel(parent)
+{
+ m_stack = 0;
+ m_sel_model = new QItemSelectionModel(this, this);
+ connect(m_sel_model, SIGNAL(currentChanged(QModelIndex,QModelIndex)),
+ this, SLOT(setStackCurrentIndex(QModelIndex)));
+ m_emty_label = tr("<empty>");
+}
+
+QItemSelectionModel *QUndoModel::selectionModel() const
+{
+ return m_sel_model;
+}
+
+QUndoStack *QUndoModel::stack() const
+{
+ return m_stack;
+}
+
+void QUndoModel::setStack(QUndoStack *stack)
+{
+ if (m_stack == stack)
+ return;
+
+ if (m_stack != 0) {
+ disconnect(m_stack, SIGNAL(cleanChanged(bool)), this, SLOT(stackChanged()));
+ disconnect(m_stack, SIGNAL(indexChanged(int)), this, SLOT(stackChanged()));
+ disconnect(m_stack, SIGNAL(destroyed(QObject*)), this, SLOT(stackDestroyed(QObject*)));
+ }
+ m_stack = stack;
+ if (m_stack != 0) {
+ connect(m_stack, SIGNAL(cleanChanged(bool)), this, SLOT(stackChanged()));
+ connect(m_stack, SIGNAL(indexChanged(int)), this, SLOT(stackChanged()));
+ connect(m_stack, SIGNAL(destroyed(QObject*)), this, SLOT(stackDestroyed(QObject*)));
+ }
+
+ stackChanged();
+}
+
+void QUndoModel::stackDestroyed(QObject *obj)
+{
+ if (obj != m_stack)
+ return;
+ m_stack = 0;
+
+ stackChanged();
+}
+
+void QUndoModel::stackChanged()
+{
+ reset();
+ m_sel_model->setCurrentIndex(selectedIndex(), QItemSelectionModel::ClearAndSelect);
+}
+
+void QUndoModel::setStackCurrentIndex(const QModelIndex &index)
+{
+ if (m_stack == 0)
+ return;
+
+ if (index == selectedIndex())
+ return;
+
+ if (index.column() != 0)
+ return;
+
+ m_stack->setIndex(index.row());
+}
+
+QModelIndex QUndoModel::selectedIndex() const
+{
+ return m_stack == 0 ? QModelIndex() : createIndex(m_stack->index(), 0);
+}
+
+QModelIndex QUndoModel::index(int row, int column, const QModelIndex &parent) const
+{
+ if (m_stack == 0)
+ return QModelIndex();
+
+ if (parent.isValid())
+ return QModelIndex();
+
+ if (column != 0)
+ return QModelIndex();
+
+ if (row < 0 || row > m_stack->count())
+ return QModelIndex();
+
+ return createIndex(row, column);
+}
+
+QModelIndex QUndoModel::parent(const QModelIndex&) const
+{
+ return QModelIndex();
+}
+
+int QUndoModel::rowCount(const QModelIndex &parent) const
+{
+ if (m_stack == 0)
+ return 0;
+
+ if (parent.isValid())
+ return 0;
+
+ return m_stack->count() + 1;
+}
+
+int QUndoModel::columnCount(const QModelIndex&) const
+{
+ return 1;
+}
+
+QVariant QUndoModel::data(const QModelIndex &index, int role) const
+{
+ if (m_stack == 0)
+ return QVariant();
+
+ if (index.column() != 0)
+ return QVariant();
+
+ if (index.row() < 0 || index.row() > m_stack->count())
+ return QVariant();
+
+ if (role == Qt::DisplayRole) {
+ if (index.row() == 0)
+ return m_emty_label;
+ return m_stack->text(index.row() - 1);
+ } else if (role == Qt::DecorationRole) {
+ if (index.row() == m_stack->cleanIndex() && !m_clean_icon.isNull())
+ return m_clean_icon;
+ return QVariant();
+ }
+
+ return QVariant();
+}
+
+QString QUndoModel::emptyLabel() const
+{
+ return m_emty_label;
+}
+
+void QUndoModel::setEmptyLabel(const QString &label)
+{
+ m_emty_label = label;
+ stackChanged();
+}
+
+void QUndoModel::setCleanIcon(const QIcon &icon)
+{
+ m_clean_icon = icon;
+ stackChanged();
+}
+
+QIcon QUndoModel::cleanIcon() const
+{
+ return m_clean_icon;
+}
+
+/*!
+ \class QUndoView
+ \brief The QUndoView class displays the contents of a QUndoStack.
+ \since 4.2
+ \ingroup misc
+ \ingroup advanced
+
+ QUndoView is a QListView which displays the list of commands pushed on an undo stack.
+ The most recently executed command is always selected. Selecting a different command
+ results in a call to QUndoStack::setIndex(), rolling the state of the document
+ backwards or forward to the new command.
+
+ The stack can be set explicitly with setStack(). Alternatively, a QUndoGroup object can
+ be set with setGroup(). The view will then update itself automatically whenever the
+ active stack of the group changes.
+
+ \image qundoview.png
+*/
+
+class QUndoViewPrivate : public QListViewPrivate
+{
+ Q_DECLARE_PUBLIC(QUndoView)
+public:
+ QUndoViewPrivate() :
+#ifndef QT_NO_UNDOGROUP
+ group(0),
+#endif
+ model(0) {}
+
+#ifndef QT_NO_UNDOGROUP
+ QPointer<QUndoGroup> group;
+#endif
+ QUndoModel *model;
+
+ void init();
+};
+
+void QUndoViewPrivate::init()
+{
+ Q_Q(QUndoView);
+
+ model = new QUndoModel(q);
+ q->setModel(model);
+ q->setSelectionModel(model->selectionModel());
+}
+
+/*!
+ Constructs a new view with parent \a parent.
+*/
+
+QUndoView::QUndoView(QWidget *parent)
+ : QListView(*new QUndoViewPrivate(), parent)
+{
+ Q_D(QUndoView);
+ d->init();
+}
+
+/*!
+ Constructs a new view with parent \a parent and sets the observed stack to \a stack.
+*/
+
+QUndoView::QUndoView(QUndoStack *stack, QWidget *parent)
+ : QListView(*new QUndoViewPrivate(), parent)
+{
+ Q_D(QUndoView);
+ d->init();
+ setStack(stack);
+}
+
+#ifndef QT_NO_UNDOGROUP
+
+/*!
+ Constructs a new view with parent \a parent and sets the observed group to \a group.
+
+ The view will update itself autmiatically whenever the active stack of the group changes.
+*/
+
+QUndoView::QUndoView(QUndoGroup *group, QWidget *parent)
+ : QListView(*new QUndoViewPrivate(), parent)
+{
+ Q_D(QUndoView);
+ d->init();
+ setGroup(group);
+}
+
+#endif // QT_NO_UNDOGROUP
+
+/*!
+ Destroys this view.
+*/
+
+QUndoView::~QUndoView()
+{
+}
+
+/*!
+ Returns the stack currently displayed by this view. If the view is looking at a
+ QUndoGroup, this the group's active stack.
+
+ \sa setStack() setGroup()
+*/
+
+QUndoStack *QUndoView::stack() const
+{
+ Q_D(const QUndoView);
+ return d->model->stack();
+}
+
+/*!
+ Sets the stack displayed by this view to \a stack. If \a stack is 0, the view
+ will be empty.
+
+ If the view was previously looking at a QUndoGroup, the group is set to 0.
+
+ \sa stack() setGroup()
+*/
+
+void QUndoView::setStack(QUndoStack *stack)
+{
+ Q_D(QUndoView);
+#ifndef QT_NO_UNDOGROUP
+ setGroup(0);
+#endif
+ d->model->setStack(stack);
+}
+
+#ifndef QT_NO_UNDOGROUP
+
+/*!
+ Sets the group displayed by this view to \a group. If \a group is 0, the view will
+ be empty.
+
+ The view will update itself autmiatically whenever the active stack of the group changes.
+
+ \sa group() setStack()
+*/
+
+void QUndoView::setGroup(QUndoGroup *group)
+{
+ Q_D(QUndoView);
+
+ if (d->group == group)
+ return;
+
+ if (d->group != 0) {
+ disconnect(d->group, SIGNAL(activeStackChanged(QUndoStack*)),
+ d->model, SLOT(setStack(QUndoStack*)));
+ }
+
+ d->group = group;
+
+ if (d->group != 0) {
+ connect(d->group, SIGNAL(activeStackChanged(QUndoStack*)),
+ d->model, SLOT(setStack(QUndoStack*)));
+ d->model->setStack(d->group->activeStack());
+ } else {
+ d->model->setStack(0);
+ }
+}
+
+/*!
+ Returns the group displayed by this view.
+
+ If the view is not looking at group, this function returns 0.
+
+ \sa setGroup() setStack()
+*/
+
+QUndoGroup *QUndoView::group() const
+{
+ Q_D(const QUndoView);
+ return d->group;
+}
+
+#endif // QT_NO_UNDOGROUP
+
+/*!
+ \property QUndoView::emptyLabel
+ \brief the label used for the empty state.
+
+ The empty label is the topmost element in the list of commands, which represents
+ the state of the document before any commands were pushed on the stack. The default
+ is the string "<empty>".
+*/
+
+void QUndoView::setEmptyLabel(const QString &label)
+{
+ Q_D(QUndoView);
+ d->model->setEmptyLabel(label);
+}
+
+QString QUndoView::emptyLabel() const
+{
+ Q_D(const QUndoView);
+ return d->model->emptyLabel();
+}
+
+/*!
+ \property QUndoView::cleanIcon
+ \brief the icon used to represent the clean state.
+
+ A stack may have a clean state set with QUndoStack::setClean(). This is usually
+ the state of the document at the point it was saved. QUndoView can display an
+ icon in the list of commands to show the clean state. If this property is
+ a null icon, no icon is shown. The default value is the null icon.
+*/
+
+void QUndoView::setCleanIcon(const QIcon &icon)
+{
+ Q_D(const QUndoView);
+ d->model->setCleanIcon(icon);
+
+}
+
+QIcon QUndoView::cleanIcon() const
+{
+ Q_D(const QUndoView);
+ return d->model->cleanIcon();
+}
+
+QT_END_NAMESPACE
+
+#include "qundoview.moc"
+
+#endif // QT_NO_UNDOVIEW
diff --git a/src/gui/util/qundoview.h b/src/gui/util/qundoview.h
new file mode 100644
index 0000000000..b776ad356a
--- /dev/null
+++ b/src/gui/util/qundoview.h
@@ -0,0 +1,102 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QUNDOVIEW_H
+#define QUNDOVIEW_H
+
+#include <QtGui/qlistview.h>
+#include <QtCore/qstring.h>
+
+#ifndef QT_NO_UNDOVIEW
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+class QUndoViewPrivate;
+class QUndoStack;
+class QUndoGroup;
+class QIcon;
+
+QT_MODULE(Gui)
+
+class Q_GUI_EXPORT QUndoView : public QListView
+{
+ Q_OBJECT
+ Q_DECLARE_PRIVATE(QUndoView)
+ Q_PROPERTY(QString emptyLabel READ emptyLabel WRITE setEmptyLabel)
+ Q_PROPERTY(QIcon cleanIcon READ cleanIcon WRITE setCleanIcon)
+
+public:
+ explicit QUndoView(QWidget *parent = 0);
+ explicit QUndoView(QUndoStack *stack, QWidget *parent = 0);
+#ifndef QT_NO_UNDOGROUP
+ explicit QUndoView(QUndoGroup *group, QWidget *parent = 0);
+#endif
+ ~QUndoView();
+
+ QUndoStack *stack() const;
+#ifndef QT_NO_UNDOGROUP
+ QUndoGroup *group() const;
+#endif
+
+ void setEmptyLabel(const QString &label);
+ QString emptyLabel() const;
+
+ void setCleanIcon(const QIcon &icon);
+ QIcon cleanIcon() const;
+
+public Q_SLOTS:
+ void setStack(QUndoStack *stack);
+#ifndef QT_NO_UNDOGROUP
+ void setGroup(QUndoGroup *group);
+#endif
+
+private:
+ Q_DISABLE_COPY(QUndoView)
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QT_NO_UNDOVIEW
+#endif // QUNDOVIEW_H
diff --git a/src/gui/util/util.pri b/src/gui/util/util.pri
new file mode 100644
index 0000000000..e628229f0c
--- /dev/null
+++ b/src/gui/util/util.pri
@@ -0,0 +1,40 @@
+# Qt util module
+
+HEADERS += \
+ util/qsystemtrayicon.h \
+ util/qcompleter.h \
+ util/qcompleter_p.h \
+ util/qdesktopservices.h \
+ util/qsystemtrayicon_p.h \
+ util/qundogroup.h \
+ util/qundostack.h \
+ util/qundostack_p.h \
+ util/qundoview.h
+
+SOURCES += \
+ util/qsystemtrayicon.cpp \
+ util/qcompleter.cpp \
+ util/qdesktopservices.cpp \
+ util/qundogroup.cpp \
+ util/qundostack.cpp \
+ util/qundoview.cpp
+
+
+win32 {
+ SOURCES += \
+ util/qsystemtrayicon_win.cpp
+}
+
+unix:x11 {
+ SOURCES += \
+ util/qsystemtrayicon_x11.cpp
+}
+
+embedded {
+ SOURCES += \
+ util/qsystemtrayicon_qws.cpp
+}
+
+!embedded:!x11:mac {
+ OBJECTIVE_SOURCES += util/qsystemtrayicon_mac.mm
+}
diff --git a/src/gui/widgets/qabstractbutton.cpp b/src/gui/widgets/qabstractbutton.cpp
new file mode 100644
index 0000000000..330a7f8d79
--- /dev/null
+++ b/src/gui/widgets/qabstractbutton.cpp
@@ -0,0 +1,1468 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qabstractbutton.h"
+#include "qabstractitemview.h"
+#include "qbuttongroup.h"
+#include "qabstractbutton_p.h"
+#include "qevent.h"
+#include "qpainter.h"
+#include "qapplication.h"
+#include "qstyle.h"
+#include "qaction.h"
+#ifndef QT_NO_ACCESSIBILITY
+#include "qaccessible.h"
+#endif
+
+QT_BEGIN_NAMESPACE
+
+#define AUTO_REPEAT_DELAY 300
+#define AUTO_REPEAT_INTERVAL 100
+
+extern bool qt_tab_all_widgets;
+
+/*!
+ \class QAbstractButton
+
+ \brief The QAbstractButton class is the abstract base class of
+ button widgets, providing functionality common to buttons.
+
+ \ingroup abstractwidgets
+
+ This class implements an \e abstract button.
+ Subclasses of this class handle user actions, and specify how the button
+ is drawn.
+
+ QAbstractButton provides support for both push buttons and checkable
+ (toggle) buttons. Checkable buttons are implemented in the QRadioButton
+ and QCheckBox classes. Push buttons are implemented in the
+ QPushButton and QToolButton classes; these also provide toggle
+ behavior if required.
+
+ Any button can display a label containing text and an icon. setText()
+ sets the text; setIcon() sets the icon. If a button is disabled, its label
+ is changed to give the button a "disabled" appearance.
+
+ If the button is a text button with a string containing an
+ ampersand ('&'), QAbstractButton automatically creates a shortcut
+ key. For example:
+
+ \snippet doc/src/snippets/code/src_gui_widgets_qabstractbutton.cpp 0
+
+ The \key Alt+C shortcut is assigned to the button, i.e., when the
+ user presses \key Alt+C the button will call animateClick(). See
+ the \l {QShortcut#mnemonic}{QShortcut} documentation for details
+ (to display an actual ampersand, use '&&').
+
+ You can also set a custom shortcut key using the setShortcut()
+ function. This is useful mostly for buttons that do not have any
+ text, because they have no automatic shortcut.
+
+ \snippet doc/src/snippets/code/src_gui_widgets_qabstractbutton.cpp 1
+
+ All of the buttons provided by Qt (QPushButton, QToolButton,
+ QCheckBox, and QRadioButton) can display both \l text and \l{icon}{icons}.
+
+ A button can be made the default button in a dialog are provided by
+ QPushButton::setDefault() and QPushButton::setAutoDefault().
+
+ QAbstractButton provides most of the states used for buttons:
+
+ \list
+
+ \o isDown() indicates whether the button is \e pressed down.
+
+ \o isChecked() indicates whether the button is \e checked. Only
+ checkable buttons can be checked and unchecked (see below).
+
+ \o isEnabled() indicates whether the button can be pressed by the
+ user.
+
+ \o setAutoRepeat() sets whether the button will auto-repeat if the
+ user holds it down. \l autoRepeatDelay and \l autoRepeatInterval
+ define how auto-repetition is done.
+
+ \o setCheckable() sets whether the button is a toggle button or not.
+
+ \endlist
+
+ The difference between isDown() and isChecked() is as follows.
+ When the user clicks a toggle button to check it, the button is first
+ \e pressed then released into the \e checked state. When the user
+ clicks it again (to uncheck it), the button moves first to the
+ \e pressed state, then to the \e unchecked state (isChecked() and
+ isDown() are both false).
+
+ QAbstractButton provides four signals:
+
+ \list 1
+
+ \o pressed() is emitted when the left mouse button is pressed while
+ the mouse cursor is inside the button.
+
+ \o released() is emitted when the left mouse button is released.
+
+ \o clicked() is emitted when the button is first pressed and then
+ released, when the shortcut key is typed, or when click() or
+ animateClick() is called.
+
+ \o toggled() is emitted when the state of a toggle button changes.
+
+ \endlist
+
+ To subclass QAbstractButton, you must reimplement at least
+ paintEvent() to draw the button's outline and 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). For buttons with more than two states (like tri-state
+ buttons), you will also have to reimplement checkStateSet() and
+ nextCheckState().
+
+ \sa QButtonGroup
+*/
+
+QAbstractButtonPrivate::QAbstractButtonPrivate(QSizePolicy::ControlType type)
+ :
+#ifndef QT_NO_SHORTCUT
+ shortcutId(0),
+#endif
+ checkable(false), checked(false), autoRepeat(false), autoExclusive(false),
+ down(false), blockRefresh(false),
+#ifndef QT_NO_BUTTONGROUP
+ group(0),
+#endif
+ autoRepeatDelay(AUTO_REPEAT_DELAY),
+ autoRepeatInterval(AUTO_REPEAT_INTERVAL),
+ controlType(type)
+{}
+
+#ifndef QT_NO_BUTTONGROUP
+
+class QButtonGroupPrivate: public QObjectPrivate
+{
+ Q_DECLARE_PUBLIC(QButtonGroup)
+
+public:
+ QButtonGroupPrivate():exclusive(true){}
+ QList<QAbstractButton *> buttonList;
+ QPointer<QAbstractButton> checkedButton;
+ void detectCheckedButton();
+ void notifyChecked(QAbstractButton *button);
+ bool exclusive;
+ QMap<QAbstractButton*, int> mapping;
+};
+
+QButtonGroup::QButtonGroup(QObject *parent)
+ : QObject(*new QButtonGroupPrivate, parent)
+{
+}
+
+QButtonGroup::~QButtonGroup()
+{
+ Q_D(QButtonGroup);
+ for (int i = 0; i < d->buttonList.count(); ++i)
+ d->buttonList.at(i)->d_func()->group = 0;
+}
+
+
+bool QButtonGroup::exclusive() const
+{
+ Q_D(const QButtonGroup);
+ return d->exclusive;
+}
+
+void QButtonGroup::setExclusive(bool exclusive)
+{
+ Q_D(QButtonGroup);
+ d->exclusive = exclusive;
+}
+
+/*!
+ Adds the given \a button to the end of the group's internal list of buttons.
+
+ \sa removeButton()
+*/
+void QButtonGroup::addButton(QAbstractButton *button)
+{
+ addButton(button, -1);
+}
+
+void QButtonGroup::addButton(QAbstractButton *button, int id)
+{
+ Q_D(QButtonGroup);
+ if (QButtonGroup *previous = button->d_func()->group)
+ previous->removeButton(button);
+ button->d_func()->group = this;
+ d->buttonList.append(button);
+ if (id != -1)
+ d->mapping[button] = id;
+ if (d->exclusive && button->isChecked())
+ button->d_func()->notifyChecked();
+}
+
+void QButtonGroup::removeButton(QAbstractButton *button)
+{
+ Q_D(QButtonGroup);
+ if (d->checkedButton == button) {
+ d->detectCheckedButton();
+ }
+ if (button->d_func()->group == this) {
+ button->d_func()->group = 0;
+ d->buttonList.removeAll(button);
+ d->mapping.remove(button);
+ }
+}
+
+QList<QAbstractButton*> QButtonGroup::buttons() const
+{
+ Q_D(const QButtonGroup);
+ return d->buttonList;
+}
+
+QAbstractButton *QButtonGroup::checkedButton() const
+{
+ Q_D(const QButtonGroup);
+ return d->checkedButton;
+}
+
+QAbstractButton *QButtonGroup::button(int id) const
+{
+ Q_D(const QButtonGroup);
+ return d->mapping.key(id);
+}
+
+void QButtonGroup::setId(QAbstractButton *button, int id)
+{
+ Q_D(QButtonGroup);
+ if (button && id != -1)
+ d->mapping[button] = id;
+}
+
+int QButtonGroup::id(QAbstractButton *button) const
+{
+ Q_D(const QButtonGroup);
+ return d->mapping.value(button, -1);
+}
+
+int QButtonGroup::checkedId() const
+{
+ Q_D(const QButtonGroup);
+ return d->mapping.value(d->checkedButton, -1);
+}
+
+// detect a checked button other than the current one
+void QButtonGroupPrivate::detectCheckedButton()
+{
+ QAbstractButton *previous = checkedButton;
+ checkedButton = 0;
+ if (exclusive)
+ return;
+ for (int i = 0; i < buttonList.count(); i++) {
+ if (buttonList.at(i) != previous && buttonList.at(i)->isChecked()) {
+ checkedButton = buttonList.at(i);
+ return;
+ }
+ }
+}
+
+#endif // QT_NO_BUTTONGROUP
+
+QList<QAbstractButton *>QAbstractButtonPrivate::queryButtonList() const
+{
+#ifndef QT_NO_BUTTONGROUP
+ if (group)
+ return group->d_func()->buttonList;
+#endif
+
+ Q_Q(const QAbstractButton);
+ QList<QAbstractButton*>candidates;
+ if (q->parentWidget()) {
+ candidates = qFindChildren<QAbstractButton *>(q->parentWidget());
+ if (autoExclusive) {
+ for (int i = candidates.count() - 1; i >= 0; --i) {
+ QAbstractButton *candidate = candidates.at(i);
+ if (!candidate->autoExclusive()
+#ifndef QT_NO_BUTTONGROUP
+ || candidate->group()
+#endif
+ )
+ candidates.removeAt(i);
+ }
+ }
+ }
+ return candidates;
+}
+
+QAbstractButton *QAbstractButtonPrivate::queryCheckedButton() const
+{
+#ifndef QT_NO_BUTTONGROUP
+ if (group)
+ return group->d_func()->checkedButton;
+#endif
+
+ Q_Q(const QAbstractButton);
+ QList<QAbstractButton *> buttonList = queryButtonList();
+ if (!autoExclusive || buttonList.count() == 1) // no group
+ return 0;
+
+ for (int i = 0; i < buttonList.count(); ++i) {
+ QAbstractButton *b = buttonList.at(i);
+ if (b->d_func()->checked && b != q)
+ return b;
+ }
+ return checked ? const_cast<QAbstractButton *>(q) : 0;
+}
+
+void QAbstractButtonPrivate::notifyChecked()
+{
+#ifndef QT_NO_BUTTONGROUP
+ Q_Q(QAbstractButton);
+ if (group) {
+ QAbstractButton *previous = group->d_func()->checkedButton;
+ group->d_func()->checkedButton = q;
+ if (group->d_func()->exclusive && previous && previous != q)
+ previous->nextCheckState();
+ } else
+#endif
+ if (autoExclusive) {
+ if (QAbstractButton *b = queryCheckedButton())
+ b->setChecked(false);
+ }
+}
+
+void QAbstractButtonPrivate::moveFocus(int key)
+{
+ QList<QAbstractButton *> buttonList = queryButtonList();;
+#ifndef QT_NO_BUTTONGROUP
+ bool exclusive = group ? group->d_func()->exclusive : autoExclusive;
+#else
+ bool exclusive = autoExclusive;
+#endif
+ QWidget *f = qApp->focusWidget();
+ QAbstractButton *fb = qobject_cast<QAbstractButton *>(f);
+ if (!fb || !buttonList.contains(fb))
+ return;
+
+ QAbstractButton *candidate = 0;
+ int bestScore = -1;
+ QRect target = f->rect().translated(f->mapToGlobal(QPoint(0,0)));
+ QPoint goal = target.center();
+ uint focus_flag = qt_tab_all_widgets ? Qt::TabFocus : Qt::StrongFocus;
+
+ for (int i = 0; i < buttonList.count(); ++i) {
+ QAbstractButton *button = buttonList.at(i);
+ if (button != f && button->window() == f->window() && button->isEnabled() && !button->isHidden() &&
+ (autoExclusive || (button->focusPolicy() & focus_flag) == focus_flag)) {
+ QRect buttonRect = button->rect().translated(button->mapToGlobal(QPoint(0,0)));
+ QPoint p = buttonRect.center();
+
+ //Priority to widgets that overlap on the same coordinate.
+ //In that case, the distance in the direction will be used as significant score,
+ //take also in account orthogonal distance in case two widget are in the same distance.
+ int score;
+ if ((buttonRect.x() < target.right() && target.x() < buttonRect.right())
+ && (key == Qt::Key_Up || key == Qt::Key_Down)) {
+ //one item's is at the vertical of the other
+ score = (qAbs(p.y() - goal.y()) << 16) + qAbs(p.x() - goal.x());
+ } else if ((buttonRect.y() < target.bottom() && target.y() < buttonRect.bottom())
+ && (key == Qt::Key_Left || key == Qt::Key_Right) ) {
+ //one item's is at the horizontal of the other
+ score = (qAbs(p.x() - goal.x()) << 16) + qAbs(p.y() - goal.y());
+ } else {
+ score = (1 << 30) + (p.y() - goal.y()) * (p.y() - goal.y()) + (p.x() - goal.x()) * (p.x() - goal.x());
+ }
+
+ if (score > bestScore && candidate)
+ continue;
+
+ switch(key) {
+ case Qt::Key_Up:
+ if (p.y() < goal.y()) {
+ candidate = button;
+ bestScore = score;
+ }
+ break;
+ case Qt::Key_Down:
+ if (p.y() > goal.y()) {
+ candidate = button;
+ bestScore = score;
+ }
+ break;
+ case Qt::Key_Left:
+ if (p.x() < goal.x()) {
+ candidate = button;
+ bestScore = score;
+ }
+ break;
+ case Qt::Key_Right:
+ if (p.x() > goal.x()) {
+ candidate = button;
+ bestScore = score;
+ }
+ break;
+ }
+ }
+ }
+
+ if (exclusive
+#ifdef QT_KEYPAD_NAVIGATION
+ && !QApplication::keypadNavigationEnabled()
+#endif
+ && candidate
+ && fb->d_func()->checked
+ && candidate->d_func()->checkable)
+ candidate->click();
+
+ if (candidate) {
+ if (key == Qt::Key_Up || key == Qt::Key_Left)
+ candidate->setFocus(Qt::BacktabFocusReason);
+ else
+ candidate->setFocus(Qt::TabFocusReason);
+ }
+}
+
+void QAbstractButtonPrivate::fixFocusPolicy()
+{
+ Q_Q(QAbstractButton);
+#ifndef QT_NO_BUTTONGROUP
+ if (!group && !autoExclusive)
+#else
+ if (!autoExclusive)
+#endif
+ return;
+
+ QList<QAbstractButton *> buttonList = queryButtonList();
+ for (int i = 0; i < buttonList.count(); ++i) {
+ QAbstractButton *b = buttonList.at(i);
+ if (!b->isCheckable())
+ continue;
+ b->setFocusPolicy((Qt::FocusPolicy) ((b == q || !q->isCheckable())
+ ? (b->focusPolicy() | Qt::TabFocus)
+ : (b->focusPolicy() & ~Qt::TabFocus)));
+ }
+}
+
+void QAbstractButtonPrivate::init()
+{
+ Q_Q(QAbstractButton);
+
+ q->setFocusPolicy(Qt::FocusPolicy(q->style()->styleHint(QStyle::SH_Button_FocusPolicy)));
+ q->setSizePolicy(QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed, controlType));
+ q->setAttribute(Qt::WA_WState_OwnSizePolicy, false);
+ q->setForegroundRole(QPalette::ButtonText);
+ q->setBackgroundRole(QPalette::Button);
+}
+
+void QAbstractButtonPrivate::refresh()
+{
+ Q_Q(QAbstractButton);
+
+ if (blockRefresh)
+ return;
+ q->update();
+#ifndef QT_NO_ACCESSIBILITY
+ QAccessible::updateAccessibility(q, 0, QAccessible::StateChanged);
+#endif
+}
+
+void QAbstractButtonPrivate::click()
+{
+ Q_Q(QAbstractButton);
+
+ down = false;
+ blockRefresh = true;
+ bool changeState = true;
+ if (checked && queryCheckedButton() == q) {
+ // the checked button of an exclusive or autoexclusive group cannot be unchecked
+#ifndef QT_NO_BUTTONGROUP
+ if (group ? group->d_func()->exclusive : autoExclusive)
+#else
+ if (autoExclusive)
+#endif
+ changeState = false;
+ }
+
+ QPointer<QAbstractButton> guard(q);
+ if (changeState) {
+ q->nextCheckState();
+ if (!guard)
+ return;
+ }
+ blockRefresh = false;
+ refresh();
+ q->repaint(); //flush paint event before invoking potentially expensive operation
+ QApplication::flush();
+ if (guard)
+ emitReleased();
+ if (guard)
+ emitClicked();
+}
+
+void QAbstractButtonPrivate::emitClicked()
+{
+ Q_Q(QAbstractButton);
+ QPointer<QAbstractButton> guard(q);
+ emit q->clicked(checked);
+#ifndef QT_NO_BUTTONGROUP
+ if (guard && group) {
+ emit group->buttonClicked(group->id(q));
+ if (guard && group)
+ emit group->buttonClicked(q);
+ }
+#endif
+}
+
+void QAbstractButtonPrivate::emitPressed()
+{
+ Q_Q(QAbstractButton);
+ QPointer<QAbstractButton> guard(q);
+ emit q->pressed();
+#ifndef QT_NO_BUTTONGROUP
+ if (guard && group) {
+ emit group->buttonPressed(group->id(q));
+ if (guard && group)
+ emit group->buttonPressed(q);
+ }
+#endif
+}
+
+void QAbstractButtonPrivate::emitReleased()
+{
+ Q_Q(QAbstractButton);
+ QPointer<QAbstractButton> guard(q);
+ emit q->released();
+#ifndef QT_NO_BUTTONGROUP
+ if (guard && group) {
+ emit group->buttonReleased(group->id(q));
+ if (guard && group)
+ emit group->buttonReleased(q);
+ }
+#endif
+}
+
+/*!
+ Constructs an abstract button with a \a parent.
+*/
+QAbstractButton::QAbstractButton(QWidget *parent)
+ : QWidget(*new QAbstractButtonPrivate, parent, 0)
+{
+ Q_D(QAbstractButton);
+ d->init();
+}
+
+/*!
+ Destroys the button.
+ */
+ QAbstractButton::~QAbstractButton()
+{
+#ifndef QT_NO_BUTTONGROUP
+ Q_D(QAbstractButton);
+ if (d->group)
+ d->group->removeButton(this);
+#endif
+}
+
+
+/*! \internal
+ */
+QAbstractButton::QAbstractButton(QAbstractButtonPrivate &dd, QWidget *parent)
+ : QWidget(dd, parent, 0)
+{
+ Q_D(QAbstractButton);
+ d->init();
+}
+
+/*!
+\property QAbstractButton::text
+\brief the text shown on the button
+
+If the button has no text, the text() function will return a an empty
+string.
+
+If the text contains an ampersand character ('&'), a shortcut is
+automatically created for it. The character that follows the '&' will
+be used as the shortcut key. Any previous shortcut will be
+overwritten, or cleared if no shortcut is defined by the text. See the
+\l {QShortcut#mnemonic}{QShortcut} documentation for details (to
+display an actual ampersand, use '&&').
+
+There is no default text.
+*/
+
+void QAbstractButton::setText(const QString &text)
+{
+ Q_D(QAbstractButton);
+ if (d->text == text)
+ return;
+ d->text = text;
+#ifndef QT_NO_SHORTCUT
+ QKeySequence newMnemonic = QKeySequence::mnemonic(text);
+ setShortcut(newMnemonic);
+#endif
+ d->sizeHint = QSize();
+ update();
+ updateGeometry();
+#ifndef QT_NO_ACCESSIBILITY
+ QAccessible::updateAccessibility(this, 0, QAccessible::NameChanged);
+#endif
+}
+
+QString QAbstractButton::text() const
+{
+ Q_D(const QAbstractButton);
+ return d->text;
+}
+
+
+/*!
+ \property QAbstractButton::icon
+ \brief the icon shown on the button
+
+ The icon's default size is defined by the GUI style, but can be
+ adjusted by setting the \l iconSize property.
+*/
+void QAbstractButton::setIcon(const QIcon &icon)
+{
+ Q_D(QAbstractButton);
+ d->icon = icon;
+ d->sizeHint = QSize();
+ update();
+ updateGeometry();
+}
+
+QIcon QAbstractButton::icon() const
+{
+ Q_D(const QAbstractButton);
+ return d->icon;
+}
+
+#ifndef QT_NO_SHORTCUT
+/*!
+\property QAbstractButton::shortcut
+\brief the mnemonic associated with the button
+*/
+
+void QAbstractButton::setShortcut(const QKeySequence &key)
+{
+ Q_D(QAbstractButton);
+ if (d->shortcutId != 0)
+ releaseShortcut(d->shortcutId);
+ d->shortcut = key;
+ d->shortcutId = grabShortcut(key);
+}
+
+QKeySequence QAbstractButton::shortcut() const
+{
+ Q_D(const QAbstractButton);
+ return d->shortcut;
+}
+#endif // QT_NO_SHORTCUT
+
+/*!
+\property QAbstractButton::checkable
+\brief whether the button is checkable
+
+By default, the button is not checkable.
+
+\sa checked
+*/
+void QAbstractButton::setCheckable(bool checkable)
+{
+ Q_D(QAbstractButton);
+ if (d->checkable == checkable)
+ return;
+
+ d->checkable = checkable;
+ d->checked = false;
+}
+
+bool QAbstractButton::isCheckable() const
+{
+ Q_D(const QAbstractButton);
+ return d->checkable;
+}
+
+/*!
+\property QAbstractButton::checked
+\brief whether the button is checked
+
+Only checkable buttons can be checked. By default, the button is unchecked.
+
+\sa checkable
+*/
+void QAbstractButton::setChecked(bool checked)
+{
+ Q_D(QAbstractButton);
+ if (!d->checkable || d->checked == checked) {
+ if (!d->blockRefresh)
+ checkStateSet();
+ return;
+ }
+
+ if (!checked && d->queryCheckedButton() == this) {
+ // the checked button of an exclusive or autoexclusive group cannot be unchecked
+#ifndef QT_NO_BUTTONGROUP
+ if (d->group ? d->group->d_func()->exclusive : d->autoExclusive)
+ return;
+ if (d->group)
+ d->group->d_func()->detectCheckedButton();
+#else
+ if (d->autoExclusive)
+ return;
+#endif
+ }
+
+ QPointer<QAbstractButton> guard(this);
+
+ d->checked = checked;
+ if (!d->blockRefresh)
+ checkStateSet();
+ d->refresh();
+
+ if (guard && checked)
+ d->notifyChecked();
+ if (guard)
+ emit toggled(checked);
+}
+
+bool QAbstractButton::isChecked() const
+{
+ Q_D(const QAbstractButton);
+ return d->checked;
+}
+
+/*!
+ \property QAbstractButton::down
+ \brief whether the button is pressed down
+
+ If this property is true, the button is pressed down. The signals
+ pressed() and clicked() are not emitted if you set this property
+ to true. The default is false.
+*/
+
+void QAbstractButton::setDown(bool down)
+{
+ Q_D(QAbstractButton);
+ if (d->down == down)
+ return;
+ d->down = down;
+ d->refresh();
+ if (d->autoRepeat && d->down)
+ d->repeatTimer.start(d->autoRepeatDelay, this);
+ else
+ d->repeatTimer.stop();
+}
+
+bool QAbstractButton::isDown() const
+{
+ Q_D(const QAbstractButton);
+ return d->down;
+}
+
+/*!
+\property QAbstractButton::autoRepeat
+\brief whether autoRepeat is enabled
+
+If autoRepeat is enabled, then the pressed(), released(), and clicked() signals are emitted at
+regular intervals when the button is down. autoRepeat is off by default.
+The initial delay and the repetition interval are defined in milliseconds by \l
+autoRepeatDelay and \l autoRepeatInterval.
+
+Note: If a button is pressed down by a shortcut key, then auto-repeat is enabled and timed by the
+system and not by this class. The pressed(), released(), and clicked() signals will be emitted
+like in the normal case.
+*/
+
+void QAbstractButton::setAutoRepeat(bool autoRepeat)
+{
+ Q_D(QAbstractButton);
+ if (d->autoRepeat == autoRepeat)
+ return;
+ d->autoRepeat = autoRepeat;
+ if (d->autoRepeat && d->down)
+ d->repeatTimer.start(d->autoRepeatDelay, this);
+ else
+ d->repeatTimer.stop();
+}
+
+bool QAbstractButton::autoRepeat() const
+{
+ Q_D(const QAbstractButton);
+ return d->autoRepeat;
+}
+
+/*!
+ \property QAbstractButton::autoRepeatDelay
+ \brief the initial delay of auto-repetition
+ \since 4.2
+
+ If \l autoRepeat is enabled, then autoRepeatDelay defines the initial
+ delay in milliseconds before auto-repetition kicks in.
+
+ \sa autoRepeat, autoRepeatInterval
+*/
+
+void QAbstractButton::setAutoRepeatDelay(int autoRepeatDelay)
+{
+ Q_D(QAbstractButton);
+ d->autoRepeatDelay = autoRepeatDelay;
+}
+
+int QAbstractButton::autoRepeatDelay() const
+{
+ Q_D(const QAbstractButton);
+ return d->autoRepeatDelay;
+}
+
+/*!
+ \property QAbstractButton::autoRepeatInterval
+ \brief the interval of auto-repetition
+ \since 4.2
+
+ If \l autoRepeat is enabled, then autoRepeatInterval defines the
+ length of the auto-repetition interval in millisecons.
+
+ \sa autoRepeat, autoRepeatDelay
+*/
+
+void QAbstractButton::setAutoRepeatInterval(int autoRepeatInterval)
+{
+ Q_D(QAbstractButton);
+ d->autoRepeatInterval = autoRepeatInterval;
+}
+
+int QAbstractButton::autoRepeatInterval() const
+{
+ Q_D(const QAbstractButton);
+ return d->autoRepeatInterval;
+}
+
+
+
+/*!
+\property QAbstractButton::autoExclusive
+\brief whether auto-exclusivity is enabled
+
+If auto-exclusivity is enabled, checkable buttons that belong to the
+same parent widget behave as if they were part of the same
+exclusive button group. In an exclusive button group, only one button
+can be checked at any time; checking another button automatically
+unchecks the previously checked one.
+
+The property has no effect on buttons that belong to a button
+group.
+
+autoExclusive is off by default, except for radio buttons.
+
+\sa QRadioButton
+*/
+void QAbstractButton::setAutoExclusive(bool autoExclusive)
+{
+ Q_D(QAbstractButton);
+ d->autoExclusive = autoExclusive;
+}
+
+bool QAbstractButton::autoExclusive() const
+{
+ Q_D(const QAbstractButton);
+ return d->autoExclusive;
+}
+
+#ifndef QT_NO_BUTTONGROUP
+/*!
+ Returns the group that this button belongs to.
+
+ If the button is not a member of any QButtonGroup, this function
+ returns 0.
+
+ \sa QButtonGroup
+*/
+QButtonGroup *QAbstractButton::group() const
+{
+ Q_D(const QAbstractButton);
+ return d->group;
+}
+#endif // QT_NO_BUTTONGROUP
+
+/*!
+Performs an animated click: the button is pressed immediately, and
+released \a msec milliseconds later (the default is 100 ms).
+
+Calling this function again before the button was released will reset
+the release timer.
+
+All signals associated with a click are emitted as appropriate.
+
+This function does nothing if the button is \link setEnabled()
+disabled. \endlink
+
+\sa click()
+*/
+void QAbstractButton::animateClick(int msec)
+{
+ if (!isEnabled())
+ return;
+ Q_D(QAbstractButton);
+ if (d->checkable && focusPolicy() & Qt::ClickFocus)
+ setFocus();
+ setDown(true);
+ repaint(); //flush paint event before invoking potentially expensive operation
+ QApplication::flush();
+ if (!d->animateTimer.isActive())
+ d->emitPressed();
+ d->animateTimer.start(msec, this);
+}
+
+/*!
+Performs a click.
+
+All the usual signals associated with a click are emitted as
+appropriate. If the button is checkable, the state of the button is
+toggled.
+
+This function does nothing if the button is \link setEnabled()
+disabled. \endlink
+
+\sa animateClick()
+ */
+void QAbstractButton::click()
+{
+ if (!isEnabled())
+ return;
+ Q_D(QAbstractButton);
+ QPointer<QAbstractButton> guard(this);
+ d->down = true;
+ d->emitPressed();
+ if (guard) {
+ d->down = false;
+ nextCheckState();
+ if (guard)
+ d->emitReleased();
+ if (guard)
+ d->emitClicked();
+ }
+}
+
+/*! \fn void QAbstractButton::toggle()
+
+ Toggles the state of a checkable button.
+
+ \sa checked
+*/
+void QAbstractButton::toggle()
+{
+ Q_D(QAbstractButton);
+ setChecked(!d->checked);
+}
+
+
+/*! This virtual handler is called when setChecked() was called,
+unless it was called from within nextCheckState(). It allows
+subclasses to reset their intermediate button states.
+
+\sa nextCheckState()
+ */
+void QAbstractButton::checkStateSet()
+{
+}
+
+/*! This virtual handler is called when a button is clicked. The
+default implementation calls setChecked(!isChecked()) if the button
+isCheckable(). It allows subclasses to implement intermediate button
+states.
+
+\sa checkStateSet()
+*/
+void QAbstractButton::nextCheckState()
+{
+ if (isCheckable())
+ setChecked(!isChecked());
+}
+
+/*!
+Returns true if \a pos is inside the clickable button rectangle;
+otherwise returns false.
+
+By default, the clickable area is the entire widget. Subclasses
+may reimplement this function to provide support for clickable
+areas of different shapes and sizes.
+*/
+bool QAbstractButton::hitButton(const QPoint &pos) const
+{
+ return rect().contains(pos);
+}
+
+/*! \reimp */
+bool QAbstractButton::event(QEvent *e)
+{
+ // as opposed to other widgets, disabled buttons accept mouse
+ // events. This avoids surprising click-through scenarios
+ if (!isEnabled()) {
+ switch(e->type()) {
+ case QEvent::TabletPress:
+ case QEvent::TabletRelease:
+ case QEvent::TabletMove:
+ case QEvent::MouseButtonPress:
+ case QEvent::MouseButtonRelease:
+ case QEvent::MouseButtonDblClick:
+ case QEvent::MouseMove:
+ case QEvent::HoverMove:
+ case QEvent::HoverEnter:
+ case QEvent::HoverLeave:
+ case QEvent::ContextMenu:
+#ifndef QT_NO_WHEELEVENT
+ case QEvent::Wheel:
+#endif
+ return true;
+ default:
+ break;
+ }
+ }
+
+#ifndef QT_NO_SHORTCUT
+ if (e->type() == QEvent::Shortcut) {
+ Q_D(QAbstractButton);
+ QShortcutEvent *se = static_cast<QShortcutEvent *>(e);
+ if (d->shortcutId != se->shortcutId())
+ return false;
+ if (!se->isAmbiguous()) {
+ if (!d->animateTimer.isActive())
+ animateClick();
+ } else {
+ if (focusPolicy() != Qt::NoFocus)
+ setFocus(Qt::ShortcutFocusReason);
+ window()->setAttribute(Qt::WA_KeyboardFocusChange);
+ }
+ return true;
+ }
+#endif
+ return QWidget::event(e);
+}
+
+/*! \reimp */
+void QAbstractButton::mousePressEvent(QMouseEvent *e)
+{
+ Q_D(QAbstractButton);
+ if (e->button() != Qt::LeftButton) {
+ e->ignore();
+ return;
+ }
+ if (hitButton(e->pos())) {
+ setDown(true);
+ repaint(); //flush paint event before invoking potentially expensive operation
+ QApplication::flush();
+ d->emitPressed();
+ e->accept();
+ } else {
+ e->ignore();
+ }
+}
+
+/*! \reimp */
+void QAbstractButton::mouseReleaseEvent(QMouseEvent *e)
+{
+ Q_D(QAbstractButton);
+ if (e->button() != Qt::LeftButton) {
+ e->ignore();
+ return;
+ }
+
+ if (!d->down) {
+ e->ignore();
+ return;
+ }
+
+ if (hitButton(e->pos())) {
+ d->repeatTimer.stop();
+ d->click();
+ e->accept();
+ } else {
+ setDown(false);
+ e->ignore();
+ }
+}
+
+/*! \reimp */
+void QAbstractButton::mouseMoveEvent(QMouseEvent *e)
+{
+ Q_D(QAbstractButton);
+ if (!(e->buttons() & Qt::LeftButton)) {
+ e->ignore();
+ return;
+ }
+
+ if (hitButton(e->pos()) != d->down) {
+ setDown(!d->down);
+ repaint(); //flush paint event before invoking potentially expensive operation
+ QApplication::flush();
+ if (d->down)
+ d->emitPressed();
+ else
+ d->emitReleased();
+ e->accept();
+ } else if (!hitButton(e->pos())) {
+ e->ignore();
+ }
+}
+
+/*! \reimp */
+void QAbstractButton::keyPressEvent(QKeyEvent *e)
+{
+ Q_D(QAbstractButton);
+ bool next = true;
+ switch (e->key()) {
+ case Qt::Key_Enter:
+ case Qt::Key_Return:
+ e->ignore();
+ break;
+ case Qt::Key_Select:
+ case Qt::Key_Space:
+ if (!e->isAutoRepeat()) {
+ setDown(true);
+ repaint(); //flush paint event before invoking potentially expensive operation
+ QApplication::flush();
+ d->emitPressed();
+ }
+ break;
+ case Qt::Key_Up:
+ case Qt::Key_Left:
+ next = false;
+ // fall through
+ case Qt::Key_Right:
+ case Qt::Key_Down:
+#ifdef QT_KEYPAD_NAVIGATION
+ if (QApplication::keypadNavigationEnabled() && (e->key() == Qt::Key_Left || e->key() == Qt::Key_Right)) {
+ e->ignore();
+ return;
+ }
+#endif
+ QWidget *pw;
+ if (d->autoExclusive
+#ifndef QT_NO_BUTTONGROUP
+ || d->group
+#endif
+#ifndef QT_NO_ITEMVIEWS
+ || ((pw = parentWidget()) && qobject_cast<QAbstractItemView *>(pw->parentWidget()))
+#endif
+ ) {
+ // ### Using qobject_cast to check if the parent is a viewport of
+ // QAbstractItemView is a crude hack, and should be revisited and
+ // cleaned up when fixing task 194373. It's here to ensure that we
+ // keep compatibility outside QAbstractItemView.
+ d->moveFocus(e->key());
+ if (hasFocus()) // nothing happend, propagate
+ e->ignore();
+ } else {
+ focusNextPrevChild(next);
+ }
+ break;
+ case Qt::Key_Escape:
+ if (d->down) {
+ setDown(false);
+ repaint(); //flush paint event before invoking potentially expensive operation
+ QApplication::flush();
+ d->emitReleased();
+ break;
+ }
+ // fall through
+ default:
+ e->ignore();
+ }
+}
+
+/*! \reimp */
+void QAbstractButton::keyReleaseEvent(QKeyEvent *e)
+{
+ Q_D(QAbstractButton);
+
+ if (!e->isAutoRepeat())
+ d->repeatTimer.stop();
+
+ switch (e->key()) {
+ case Qt::Key_Select:
+ case Qt::Key_Space:
+ if (!e->isAutoRepeat() && d->down)
+ d->click();
+ break;
+ default:
+ e->ignore();
+ }
+}
+
+/*!\reimp
+ */
+void QAbstractButton::timerEvent(QTimerEvent *e)
+{
+ Q_D(QAbstractButton);
+ if (e->timerId() == d->repeatTimer.timerId()) {
+ d->repeatTimer.start(d->autoRepeatInterval, this);
+ if (d->down) {
+ QPointer<QAbstractButton> guard(this);
+ d->emitReleased();
+ if (guard)
+ d->emitClicked();
+ if (guard)
+ d->emitPressed();
+ }
+ } else if (e->timerId() == d->animateTimer.timerId()) {
+ d->animateTimer.stop();
+ d->click();
+ }
+}
+
+#if defined(Q_OS_WINCE) && !defined(QT_NO_CONTEXTMENU)
+/*! \reimp */
+void QAbstractButton::contextMenuEvent(QContextMenuEvent *e)
+{
+ e->ignore();
+ setDown(false);
+}
+#endif
+
+/*! \reimp */
+void QAbstractButton::focusInEvent(QFocusEvent *e)
+{
+ Q_D(QAbstractButton);
+#ifdef QT_KEYPAD_NAVIGATION
+ if (!QApplication::keypadNavigationEnabled())
+#endif
+ d->fixFocusPolicy();
+ QWidget::focusInEvent(e);
+}
+
+/*! \reimp */
+void QAbstractButton::focusOutEvent(QFocusEvent *e)
+{
+ Q_D(QAbstractButton);
+ if (e->reason() != Qt::PopupFocusReason)
+ d->down = false;
+ QWidget::focusOutEvent(e);
+}
+
+/*! \reimp */
+void QAbstractButton::changeEvent(QEvent *e)
+{
+ Q_D(QAbstractButton);
+ switch (e->type()) {
+ case QEvent::EnabledChange:
+ if (!isEnabled())
+ setDown(false);
+ break;
+ default:
+ d->sizeHint = QSize();
+ break;
+ }
+ QWidget::changeEvent(e);
+}
+
+/*!
+ \fn void QAbstractButton::paintEvent(QPaintEvent *e)
+ \reimp
+*/
+
+/*!
+ \fn void QAbstractButton::pressed()
+
+ This signal is emitted when the button is pressed down.
+
+ \sa released(), clicked()
+*/
+
+/*!
+ \fn void QAbstractButton::released()
+
+ This signal is emitted when the button is released.
+
+ \sa pressed(), clicked(), toggled()
+*/
+
+/*!
+\fn void QAbstractButton::clicked(bool checked)
+
+This signal is emitted when the button is activated (i.e. pressed down
+then released while the mouse cursor is inside the button), when the
+shortcut key is typed, or when click() or animateClick() is called.
+Notably, this signal is \e not emitted if you call setDown(),
+setChecked() or toggle().
+
+If the button is checkable, \a checked is true if the button is
+checked, or false if the button is unchecked.
+
+\sa pressed(), released(), toggled()
+*/
+
+/*!
+\fn void QAbstractButton::toggled(bool checked)
+
+This signal is emitted whenever a checkable button changes its state.
+\a checked is true if the button is checked, or false if the button is
+unchecked.
+
+This may be the result of a user action, click() slot activation,
+or because setChecked() was called.
+
+The states of buttons in exclusive button groups are updated before this
+signal is emitted. This means that slots can act on either the "off"
+signal or the "on" signal emitted by the buttons in the group whose
+states have changed.
+
+For example, a slot that reacts to signals emitted by newly checked
+buttons but which ignores signals from buttons that have been unchecked
+can be implemented using the following pattern:
+
+\snippet doc/src/snippets/code/src_gui_widgets_qabstractbutton.cpp 2
+
+Button groups can be created using the QButtonGroup class, and
+updates to the button states monitored with the
+\l{QButtonGroup::buttonClicked()} signal.
+
+\sa checked, clicked()
+*/
+
+/*!
+ \property QAbstractButton::iconSize
+ \brief the icon size used for this button.
+
+ The default size is defined by the GUI style. This is a maximum
+ size for the icons. Smaller icons will not be scaled up.
+*/
+
+QSize QAbstractButton::iconSize() const
+{
+ Q_D(const QAbstractButton);
+ if (d->iconSize.isValid())
+ return d->iconSize;
+ int e = style()->pixelMetric(QStyle::PM_ButtonIconSize, 0, this);
+ return QSize(e, e);
+}
+
+void QAbstractButton::setIconSize(const QSize &size)
+{
+ Q_D(QAbstractButton);
+ if (d->iconSize == size)
+ return;
+
+ d->iconSize = size;
+ d->sizeHint = QSize();
+ updateGeometry();
+ if (isVisible()) {
+ update();
+ }
+}
+
+
+#ifdef QT3_SUPPORT
+/*!
+ Use icon() instead.
+*/
+QIcon *QAbstractButton::iconSet() const
+{
+ Q_D(const QAbstractButton);
+ if (!d->icon.isNull())
+ return const_cast<QIcon *>(&d->icon);
+ return 0;
+}
+
+/*!
+ Use QAbstractButton(QWidget *) instead.
+
+ Call setObjectName() if you want to specify an object name, and
+ setParent() if you want to set the window flags.
+*/
+QAbstractButton::QAbstractButton(QWidget *parent, const char *name, Qt::WindowFlags f)
+ : QWidget(*new QAbstractButtonPrivate, parent, f)
+{
+ Q_D(QAbstractButton);
+ setObjectName(QString::fromAscii(name));
+ d->init();
+}
+
+/*! \fn bool QAbstractButton::isOn() const
+
+ Use isChecked() instead.
+*/
+
+/*!
+ \fn QPixmap *QAbstractButton::pixmap() const
+
+ This compatibility function always returns 0.
+
+ Use icon() instead.
+*/
+
+/*! \fn void QAbstractButton::setPixmap(const QPixmap &p)
+
+ Use setIcon() instead.
+*/
+
+/*! \fn void QAbstractButton::setIconSet(const QIcon &icon)
+
+ Use setIcon() instead.
+*/
+
+/*! \fn void QAbstractButton::setOn(bool b)
+
+ Use setChecked() instead.
+*/
+
+/*! \fn bool QAbstractButton::isToggleButton() const
+
+ Use isCheckable() instead.
+*/
+
+/*!
+ \fn void QAbstractButton::setToggleButton(bool b)
+
+ Use setCheckable() instead.
+*/
+
+/*! \fn void QAbstractButton::setAccel(const QKeySequence &key)
+
+ Use setShortcut() instead.
+*/
+
+/*! \fn QKeySequence QAbstractButton::accel() const
+
+ Use shortcut() instead.
+*/
+#endif
+
+QT_END_NAMESPACE
diff --git a/src/gui/widgets/qabstractbutton.h b/src/gui/widgets/qabstractbutton.h
new file mode 100644
index 0000000000..6503a562a8
--- /dev/null
+++ b/src/gui/widgets/qabstractbutton.h
@@ -0,0 +1,183 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QABSTRACTBUTTON_H
+#define QABSTRACTBUTTON_H
+
+#include <QtGui/qicon.h>
+#include <QtGui/qkeysequence.h>
+#include <QtGui/qwidget.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+class QButtonGroup;
+class QAbstractButtonPrivate;
+
+class Q_GUI_EXPORT QAbstractButton : public QWidget
+{
+ Q_OBJECT
+
+ Q_PROPERTY(QString text READ text WRITE setText)
+ Q_PROPERTY(QIcon icon READ icon WRITE setIcon)
+ Q_PROPERTY(QSize iconSize READ iconSize WRITE setIconSize)
+#ifndef QT_NO_SHORTCUT
+ Q_PROPERTY(QKeySequence shortcut READ shortcut WRITE setShortcut)
+#endif
+ Q_PROPERTY(bool checkable READ isCheckable WRITE setCheckable)
+ Q_PROPERTY(bool checked READ isChecked WRITE setChecked DESIGNABLE isCheckable NOTIFY toggled USER true)
+ Q_PROPERTY(bool autoRepeat READ autoRepeat WRITE setAutoRepeat)
+ Q_PROPERTY(bool autoExclusive READ autoExclusive WRITE setAutoExclusive)
+ Q_PROPERTY(int autoRepeatDelay READ autoRepeatDelay WRITE setAutoRepeatDelay)
+ Q_PROPERTY(int autoRepeatInterval READ autoRepeatInterval WRITE setAutoRepeatInterval)
+ Q_PROPERTY(bool down READ isDown WRITE setDown DESIGNABLE false)
+
+public:
+ explicit QAbstractButton(QWidget* parent=0);
+ ~QAbstractButton();
+
+ void setText(const QString &text);
+ QString text() const;
+
+ void setIcon(const QIcon &icon);
+ QIcon icon() const;
+
+ QSize iconSize() const;
+
+#ifndef QT_NO_SHORTCUT
+ void setShortcut(const QKeySequence &key);
+ QKeySequence shortcut() const;
+#endif
+
+ void setCheckable(bool);
+ bool isCheckable() const;
+
+ bool isChecked() const;
+
+ void setDown(bool);
+ bool isDown() const;
+
+ void setAutoRepeat(bool);
+ bool autoRepeat() const;
+
+ void setAutoRepeatDelay(int);
+ int autoRepeatDelay() const;
+
+ void setAutoRepeatInterval(int);
+ int autoRepeatInterval() const;
+
+ void setAutoExclusive(bool);
+ bool autoExclusive() const;
+
+#ifndef QT_NO_BUTTONGROUP
+ QButtonGroup *group() const;
+#endif
+
+public Q_SLOTS:
+ void setIconSize(const QSize &size);
+ void animateClick(int msec = 100);
+ void click();
+ void toggle();
+ void setChecked(bool);
+
+Q_SIGNALS:
+ void pressed();
+ void released();
+ void clicked(bool checked = false);
+ void toggled(bool checked);
+
+protected:
+ virtual void paintEvent(QPaintEvent *e) = 0;
+ virtual bool hitButton(const QPoint &pos) const;
+ virtual void checkStateSet();
+ virtual void nextCheckState();
+
+ bool event(QEvent *e);
+ void keyPressEvent(QKeyEvent *e);
+ void keyReleaseEvent(QKeyEvent *e);
+ void mousePressEvent(QMouseEvent *e);
+ void mouseReleaseEvent(QMouseEvent *e);
+ void mouseMoveEvent(QMouseEvent *e);
+ void focusInEvent(QFocusEvent *e);
+ void focusOutEvent(QFocusEvent *e);
+ void changeEvent(QEvent *e);
+ void timerEvent(QTimerEvent *e);
+#ifdef Q_OS_WINCE
+ void contextMenuEvent(QContextMenuEvent *e);
+#endif
+
+#ifdef QT3_SUPPORT
+public:
+ QT3_SUPPORT_CONSTRUCTOR QAbstractButton(QWidget *parent, const char *name, Qt::WindowFlags f=0);
+ inline QT3_SUPPORT bool isOn() const { return isChecked(); }
+ inline QT3_SUPPORT const QPixmap *pixmap() const { return 0; } // help styles compile
+ inline QT3_SUPPORT void setPixmap( const QPixmap &p ) {
+ setIcon(QIcon(p));
+ setIconSize(p.size());
+ }
+ QT3_SUPPORT QIcon *iconSet() const;
+ inline QT3_SUPPORT void setIconSet(const QIcon &icon) { setIcon(icon); }
+ inline QT3_SUPPORT bool isToggleButton() const { return isCheckable(); }
+ inline QT3_SUPPORT void setToggleButton(bool b) { setCheckable(b); }
+ inline QT3_SUPPORT void setAccel(const QKeySequence &key) { setShortcut(key); }
+ inline QT3_SUPPORT QKeySequence accel() const { return shortcut(); }
+
+public Q_SLOTS:
+ inline QT_MOC_COMPAT void setOn(bool b) { setChecked(b); }
+#endif
+
+protected:
+ QAbstractButton(QAbstractButtonPrivate &dd, QWidget* parent = 0);
+
+private:
+ Q_DECLARE_PRIVATE(QAbstractButton)
+ Q_DISABLE_COPY(QAbstractButton)
+ friend class QButtonGroup;
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QABSTRACTBUTTON_H
diff --git a/src/gui/widgets/qabstractbutton_p.h b/src/gui/widgets/qabstractbutton_p.h
new file mode 100644
index 0000000000..d250952b4e
--- /dev/null
+++ b/src/gui/widgets/qabstractbutton_p.h
@@ -0,0 +1,109 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QABSTRACTBUTTON_P_H
+#define QABSTRACTBUTTON_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 "QtCore/qbasictimer.h"
+#include "private/qwidget_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class QAbstractButtonPrivate : public QWidgetPrivate
+{
+ Q_DECLARE_PUBLIC(QAbstractButton)
+public:
+ QAbstractButtonPrivate(QSizePolicy::ControlType type = QSizePolicy::DefaultType);
+
+ QString text;
+ QIcon icon;
+ QSize iconSize;
+#ifndef QT_NO_SHORTCUT
+ QKeySequence shortcut;
+ int shortcutId;
+#endif
+ uint checkable :1;
+ uint checked :1;
+ uint autoRepeat :1;
+ uint autoExclusive :1;
+ uint down :1;
+ uint blockRefresh :1;
+
+#ifndef QT_NO_BUTTONGROUP
+ QButtonGroup* group;
+#endif
+ QBasicTimer repeatTimer;
+ QBasicTimer animateTimer;
+
+ int autoRepeatDelay, autoRepeatInterval;
+
+ QSizePolicy::ControlType controlType;
+ mutable QSize sizeHint;
+
+ void init();
+ void click();
+ void refresh();
+
+ QList<QAbstractButton *>queryButtonList() const;
+ QAbstractButton *queryCheckedButton() const;
+ void notifyChecked();
+ void moveFocus(int key);
+ void fixFocusPolicy();
+
+ void emitPressed();
+ void emitReleased();
+ void emitClicked();
+};
+
+QT_END_NAMESPACE
+
+#endif // QABSTRACTBUTTON_P_H
diff --git a/src/gui/widgets/qabstractscrollarea.cpp b/src/gui/widgets/qabstractscrollarea.cpp
new file mode 100644
index 0000000000..9886969153
--- /dev/null
+++ b/src/gui/widgets/qabstractscrollarea.cpp
@@ -0,0 +1,1303 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qabstractscrollarea.h"
+
+#ifndef QT_NO_SCROLLAREA
+
+#include "qscrollbar.h"
+#include "qapplication.h"
+#include "qstyle.h"
+#include "qstyleoption.h"
+#include "qevent.h"
+#include "qdebug.h"
+#include "qboxlayout.h"
+#include "qpainter.h"
+
+#include "qabstractscrollarea_p.h"
+#include <qwidget.h>
+
+#ifdef Q_WS_MAC
+#include <private/qt_mac_p.h>
+#include <private/qt_cocoa_helpers_mac_p.h>
+#endif
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QAbstractScrollArea
+ \brief The QAbstractScrollArea widget provides a scrolling area with
+ on-demand scroll bars.
+
+ \ingroup abstractwidgets
+
+ QAbstractScrollArea is a low-level abstraction of a scrolling
+ area. The area provides a central widget called the viewport, in
+ which the contents of the area is to be scrolled (i.e, the
+ visible parts of the contents are rendered in the viewport).
+
+ Next to the viewport is a vertical scroll bar, and below is a
+ horizontal scroll bar. When all of the area contents fits in the
+ viewport, each scroll bar can be either visible or hidden
+ depending on the scroll bar's Qt::ScrollBarPolicy. When a scroll
+ bar is hidden, the viewport expands in order to cover all
+ available space. When a scroll bar becomes visible again, the
+ viewport shrinks in order to make room for the scroll bar.
+
+ It is possible to reserve a margin area around the viewport, see
+ setViewportMargins(). The feature is mostly used to place a
+ QHeaderView widget above or beside the scrolling area. Subclasses
+ of QAbstractScrollArea should implement margins.
+
+ When inheriting QAbstractScrollArea, you need to do the
+ following:
+
+ \list
+ \o Control the scroll bars by setting their
+ range, value, page step, and tracking their
+ movements.
+ \o Draw the contents of the area in the viewport according
+ to the values of the scroll bars.
+ \o Handle events received by the viewport in
+ viewportEvent() - notably resize events.
+ \o Use \c{viewport->update()} to update the contents of the
+ viewport instead of \l{QWidget::update()}{update()}
+ as all painting operations take place on the viewport.
+ \endlist
+
+ With a scroll bar policy of Qt::ScrollBarAsNeeded (the default),
+ QAbstractScrollArea shows scroll bars when they provide a non-zero
+ scrolling range, and hides them otherwise.
+
+ The scroll bars and viewport should be updated whenever the viewport
+ receives a resize event or the size of the contents changes.
+ The viewport also needs to be updated when the scroll bars
+ values change. The initial values of the scroll bars are often
+ set when the area receives new contents.
+
+ We give a simple example, in which we have implemented a scroll area
+ that can scroll any QWidget. We make the widget a child of the
+ viewport; this way, we do not have to calculate which part of
+ the widget to draw but can simply move the widget with
+ QWidget::move(). When the area contents or the viewport size
+ changes, we do the following:
+
+ \snippet doc/src/snippets/myscrollarea.cpp 1
+
+ When the scroll bars change value, we need to update the widget
+ position, i.e., find the part of the widget that is to be drawn in
+ the viewport:
+
+ \snippet doc/src/snippets/myscrollarea.cpp 0
+
+ In order to track scroll bar movements, reimplement the virtual
+ function scrollContentsBy(). In order to fine-tune scrolling
+ behavior, connect to a scroll bar's
+ QAbstractSlider::actionTriggered() signal and adjust the \l
+ QAbstractSlider::sliderPosition as you wish.
+
+ For convenience, QAbstractScrollArea makes all viewport events
+ available in the virtual viewportEvent() handler. QWidget's
+ specialized handlers are remapped to viewport events in the cases
+ where this makes sense. The remapped specialized handlers are:
+ paintEvent(), mousePressEvent(), mouseReleaseEvent(),
+ mouseDoubleClickEvent(), mouseMoveEvent(), wheelEvent(),
+ dragEnterEvent(), dragMoveEvent(), dragLeaveEvent(), dropEvent(),
+ contextMenuEvent(), and resizeEvent().
+
+ QScrollArea, which inherits QAbstractScrollArea, provides smooth
+ scrolling for any QWidget (i.e., the widget is scrolled pixel by
+ pixel). You only need to subclass QAbstractScrollArea if you need
+ more specialized behavior. This is, for instance, true if the
+ entire contents of the area is not suitable for being drawn on a
+ QWidget or if you do not want smooth scrolling.
+
+ \sa QScrollArea
+*/
+
+QAbstractScrollAreaPrivate::QAbstractScrollAreaPrivate()
+ :hbar(0), vbar(0), vbarpolicy(Qt::ScrollBarAsNeeded), hbarpolicy(Qt::ScrollBarAsNeeded),
+ viewport(0), cornerWidget(0), left(0), top(0), right(0), bottom(0),
+ xoffset(0), yoffset(0), viewportFilter(0)
+{
+}
+
+QAbstractScrollAreaScrollBarContainer::QAbstractScrollAreaScrollBarContainer(Qt::Orientation orientation, QWidget *parent)
+ :QWidget(parent), scrollBar(new QScrollBar(orientation, this)),
+ layout(new QBoxLayout(orientation == Qt::Horizontal ? QBoxLayout::LeftToRight : QBoxLayout::TopToBottom)),
+ orientation(orientation)
+{
+ setLayout(layout);
+ layout->setMargin(0);
+ layout->setSpacing(0);
+ layout->addWidget(scrollBar);
+}
+
+/*! \internal
+ Adds a widget to the scroll bar container.
+*/
+void QAbstractScrollAreaScrollBarContainer::addWidget(QWidget *widget, LogicalPosition position)
+{
+ QSizePolicy policy = widget->sizePolicy();
+ if (orientation == Qt::Vertical)
+ policy.setHorizontalPolicy(QSizePolicy::Ignored);
+ else
+ policy.setVerticalPolicy(QSizePolicy::Ignored);
+ widget->setSizePolicy(policy);
+ widget->setParent(this);
+
+ const int insertIndex = (position & LogicalLeft) ? 0 : scrollBarLayoutIndex() + 1;
+ layout->insertWidget(insertIndex, widget);
+}
+
+/*! \internal
+ Retuns a list of scroll bar widgets for the given position. The scroll bar
+ itself is not returned.
+*/
+QWidgetList QAbstractScrollAreaScrollBarContainer::widgets(LogicalPosition position)
+{
+ QWidgetList list;
+ const int scrollBarIndex = scrollBarLayoutIndex();
+ if (position == LogicalLeft) {
+ for (int i = 0; i < scrollBarIndex; ++i)
+ list.append(layout->itemAt(i)->widget());
+ } else if (position == LogicalRight) {
+ const int layoutItemCount = layout->count();
+ for (int i = scrollBarIndex + 1; i < layoutItemCount; ++i)
+ list.append(layout->itemAt(i)->widget());
+ }
+ return list;
+}
+
+/*! \internal
+ Returns the layout index for the scroll bar. This needs to be
+ recalculated by a linear search for each use, since items in
+ the layout can be removed at any time (i.e. when a widget is
+ deleted or re-parented).
+*/
+int QAbstractScrollAreaScrollBarContainer::scrollBarLayoutIndex() const
+{
+ const int layoutItemCount = layout->count();
+ for (int i = 0; i < layoutItemCount; ++i) {
+ if (qobject_cast<QScrollBar *>(layout->itemAt(i)->widget()))
+ return i;
+ }
+ return -1;
+}
+
+/*! \internal
+*/
+void QAbstractScrollAreaPrivate::replaceScrollBar(QScrollBar *scrollBar,
+ Qt::Orientation orientation)
+{
+ Q_Q(QAbstractScrollArea);
+
+ QAbstractScrollAreaScrollBarContainer *container = scrollBarContainers[orientation];
+ bool horizontal = (orientation == Qt::Horizontal);
+ QScrollBar *oldBar = horizontal ? hbar : vbar;
+ if (horizontal)
+ hbar = scrollBar;
+ else
+ vbar = scrollBar;
+ scrollBar->setParent(container);
+ container->scrollBar = scrollBar;
+ container->layout->removeWidget(oldBar);
+ container->layout->insertWidget(0, scrollBar);
+ scrollBar->setVisible(oldBar->isVisibleTo(container));
+ scrollBar->setInvertedAppearance(oldBar->invertedAppearance());
+ scrollBar->setInvertedControls(oldBar->invertedControls());
+ scrollBar->setRange(oldBar->minimum(), oldBar->maximum());
+ scrollBar->setOrientation(oldBar->orientation());
+ scrollBar->setPageStep(oldBar->pageStep());
+ scrollBar->setSingleStep(oldBar->singleStep());
+ scrollBar->setSliderDown(oldBar->isSliderDown());
+ scrollBar->setSliderPosition(oldBar->sliderPosition());
+ scrollBar->setTracking(oldBar->hasTracking());
+ scrollBar->setValue(oldBar->value());
+ delete oldBar;
+
+ QObject::connect(scrollBar, SIGNAL(valueChanged(int)),
+ q, horizontal ? SLOT(_q_hslide(int)) : SLOT(_q_vslide(int)));
+ QObject::connect(scrollBar, SIGNAL(rangeChanged(int,int)),
+ q, SLOT(_q_showOrHideScrollBars()), Qt::QueuedConnection);
+}
+
+void QAbstractScrollAreaPrivate::init()
+{
+ Q_Q(QAbstractScrollArea);
+ viewport = new QWidget(q);
+ viewport->setObjectName(QLatin1String("qt_scrollarea_viewport"));
+ viewport->setBackgroundRole(QPalette::Base);
+ viewport->setAutoFillBackground(true);
+ scrollBarContainers[Qt::Horizontal] = new QAbstractScrollAreaScrollBarContainer(Qt::Horizontal, q);
+ scrollBarContainers[Qt::Horizontal]->setObjectName(QLatin1String("qt_scrollarea_hcontainer"));
+ hbar = scrollBarContainers[Qt::Horizontal]->scrollBar;
+ hbar->setRange(0,0);
+ scrollBarContainers[Qt::Horizontal]->setVisible(false);
+ QObject::connect(hbar, SIGNAL(valueChanged(int)), q, SLOT(_q_hslide(int)));
+ QObject::connect(hbar, SIGNAL(rangeChanged(int,int)), q, SLOT(_q_showOrHideScrollBars()), Qt::QueuedConnection);
+ scrollBarContainers[Qt::Vertical] = new QAbstractScrollAreaScrollBarContainer(Qt::Vertical, q);
+ scrollBarContainers[Qt::Vertical]->setObjectName(QLatin1String("qt_scrollarea_vcontainer"));
+ vbar = scrollBarContainers[Qt::Vertical]->scrollBar;
+ vbar->setRange(0,0);
+ scrollBarContainers[Qt::Vertical]->setVisible(false);
+ QObject::connect(vbar, SIGNAL(valueChanged(int)), q, SLOT(_q_vslide(int)));
+ QObject::connect(vbar, SIGNAL(rangeChanged(int,int)), q, SLOT(_q_showOrHideScrollBars()), Qt::QueuedConnection);
+ viewportFilter = new QAbstractScrollAreaFilter(this);
+ viewport->installEventFilter(viewportFilter);
+ viewport->setFocusProxy(q);
+ q->setFocusPolicy(Qt::WheelFocus);
+ q->setFrameStyle(QFrame::StyledPanel | QFrame::Sunken);
+ q->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
+ layoutChildren();
+}
+
+void QAbstractScrollAreaPrivate::layoutChildren()
+{
+ Q_Q(QAbstractScrollArea);
+ bool needh = (hbarpolicy == Qt::ScrollBarAlwaysOn
+ || (hbarpolicy == Qt::ScrollBarAsNeeded && hbar->minimum() < hbar->maximum()));
+
+ bool needv = (vbarpolicy == Qt::ScrollBarAlwaysOn
+ || (vbarpolicy == Qt::ScrollBarAsNeeded && vbar->minimum() < vbar->maximum()));
+
+#ifdef Q_WS_MAC
+ QWidget * const window = q->window();
+
+ // Use small scroll bars for tool windows, to match the native size grip.
+ bool hbarIsSmall = hbar->testAttribute(Qt::WA_MacSmallSize);
+ bool vbarIsSmall = vbar->testAttribute(Qt::WA_MacSmallSize);
+ const Qt::WindowType windowType = window->windowType();
+ if (windowType == Qt::Tool) {
+ if (!hbarIsSmall) {
+ hbar->setAttribute(Qt::WA_MacMiniSize, false);
+ hbar->setAttribute(Qt::WA_MacNormalSize, false);
+ hbar->setAttribute(Qt::WA_MacSmallSize, true);
+ }
+ if (!vbarIsSmall) {
+ vbar->setAttribute(Qt::WA_MacMiniSize, false);
+ vbar->setAttribute(Qt::WA_MacNormalSize, false);
+ vbar->setAttribute(Qt::WA_MacSmallSize, true);
+ }
+ } else {
+ if (hbarIsSmall) {
+ hbar->setAttribute(Qt::WA_MacMiniSize, false);
+ hbar->setAttribute(Qt::WA_MacNormalSize, false);
+ hbar->setAttribute(Qt::WA_MacSmallSize, false);
+ }
+ if (vbarIsSmall) {
+ vbar->setAttribute(Qt::WA_MacMiniSize, false);
+ vbar->setAttribute(Qt::WA_MacNormalSize, false);
+ vbar->setAttribute(Qt::WA_MacSmallSize, false);
+ }
+ }
+#endif
+
+ const int hsbExt = hbar->sizeHint().height();
+ const int vsbExt = vbar->sizeHint().width();
+ const QPoint extPoint(vsbExt, hsbExt);
+ const QSize extSize(vsbExt, hsbExt);
+
+ const QRect widgetRect = q->rect();
+ QStyleOption opt(0);
+ opt.init(q);
+
+ const bool hasCornerWidget = (cornerWidget != 0);
+
+// If the scroll bars are at the very right and bottom of the window we
+// move their positions to be aligned with the size grip.
+#ifdef Q_WS_MAC
+ // Check if a native sizegrip is present.
+ bool hasMacReverseSizeGrip = false;
+ bool hasMacSizeGrip = false;
+ bool nativeGripPresent = false;
+ if (q->testAttribute(Qt::WA_WState_Created))
+ nativeGripPresent = qt_mac_checkForNativeSizeGrip(q);
+
+ if (nativeGripPresent) {
+ // Look for a native size grip at the visual window bottom right and at the
+ // absolute window bottom right. In reverse mode, the native size grip does not
+ // swich side, so we need to check if it is on the "wrong side".
+ const QPoint scrollAreaBottomRight = q->mapTo(window, widgetRect.bottomRight() - QPoint(frameWidth, frameWidth));
+ const QPoint windowBottomRight = window->rect().bottomRight();
+ const QPoint visualWindowBottomRight = QStyle::visualPos(opt.direction, opt.rect, windowBottomRight);
+ const QPoint offset = windowBottomRight - scrollAreaBottomRight;
+ const QPoint visualOffset = visualWindowBottomRight - scrollAreaBottomRight;
+ hasMacSizeGrip = (visualOffset.manhattanLength() < vsbExt);
+ hasMacReverseSizeGrip = (hasMacSizeGrip == false && (offset.manhattanLength() < hsbExt));
+ }
+#endif
+
+ QPoint cornerOffset(needv ? vsbExt : 0, needh ? hsbExt : 0);
+ QRect controlsRect;
+ QRect viewportRect;
+
+ // In FrameOnlyAroundContents mode the frame is drawn between the controls and
+ // the viewport, else the frame rect is equal to the widget rect.
+ if ((frameStyle != QFrame::NoFrame) &&
+ q->style()->styleHint(QStyle::SH_ScrollView_FrameOnlyAroundContents, &opt, q)) {
+ controlsRect = widgetRect;
+ const int extra = q->style()->pixelMetric(QStyle::PM_ScrollView_ScrollBarSpacing);
+ const QPoint cornerExtra(needv ? extra : 0, needh ? extra : 0);
+ QRect frameRect = widgetRect;
+ frameRect.adjust(0, 0, -cornerOffset.x() - cornerExtra.x(), -cornerOffset.y() - cornerExtra.y());
+ q->setFrameRect(QStyle::visualRect(opt.direction, opt.rect, frameRect));
+ // The frame rect needs to be in logical coords, however we need to flip
+ // the contentsRect back before passing it on to the viewportRect
+ // since the viewportRect has its logical coords calculated later.
+ viewportRect = QStyle::visualRect(opt.direction, opt.rect, q->contentsRect());
+ } else {
+ q->setFrameRect(QStyle::visualRect(opt.direction, opt.rect, widgetRect));
+ controlsRect = q->contentsRect();
+ viewportRect = QRect(controlsRect.topLeft(), controlsRect.bottomRight() - cornerOffset);
+ }
+
+ // If we have a corner widget and are only showing one scroll bar, we need to move it
+ // to make room for the corner widget.
+ if (hasCornerWidget && (needv || needh))
+ cornerOffset = extPoint;
+
+#ifdef Q_WS_MAC
+ // Also move the scroll bars if they are covered by the native Mac size grip.
+ if (hasMacSizeGrip)
+ cornerOffset = extPoint;
+#endif
+
+ // The corner point is where the scroll bar rects, the corner widget rect and the
+ // viewport rect meets.
+ const QPoint cornerPoint(controlsRect.bottomRight() + QPoint(1, 1) - cornerOffset);
+
+ // Some styles paints the corner if both scorllbars are showing and there is
+ // no corner widget. Also, on the Mac we paint if there is a native
+ // (transparent) sizegrip in the area where a corner widget would be.
+ if ((needv && needh && hasCornerWidget == false)
+ || ((needv || needh)
+#ifdef Q_WS_MAC
+ && hasMacSizeGrip
+#endif
+ )
+ ) {
+ cornerPaintingRect = QStyle::visualRect(opt.direction, opt.rect, QRect(cornerPoint, extSize));
+ } else {
+ cornerPaintingRect = QRect();
+ }
+
+#ifdef Q_WS_MAC
+ if (hasMacReverseSizeGrip)
+ reverseCornerPaintingRect = QRect(controlsRect.bottomRight() + QPoint(1, 1) - extPoint, extSize);
+ else
+ reverseCornerPaintingRect = QRect();
+#endif
+
+ if (needh) {
+ QRect horizontalScrollBarRect(QPoint(controlsRect.left(), cornerPoint.y()), QPoint(cornerPoint.x() - 1, controlsRect.bottom()));
+#ifdef Q_WS_MAC
+ if (hasMacReverseSizeGrip)
+ horizontalScrollBarRect.adjust(vsbExt, 0, 0, 0);
+#endif
+ scrollBarContainers[Qt::Horizontal]->setGeometry(QStyle::visualRect(opt.direction, opt.rect, horizontalScrollBarRect));
+ scrollBarContainers[Qt::Horizontal]->raise();
+ }
+
+ if (needv) {
+ const QRect verticalScrollBarRect (QPoint(cornerPoint.x(), controlsRect.top()), QPoint(controlsRect.right(), cornerPoint.y() - 1));
+ scrollBarContainers[Qt::Vertical]->setGeometry(QStyle::visualRect(opt.direction, opt.rect, verticalScrollBarRect));
+ scrollBarContainers[Qt::Vertical]->raise();
+ }
+
+ if (cornerWidget) {
+ const QRect cornerWidgetRect(cornerPoint, controlsRect.bottomRight());
+ cornerWidget->setGeometry(QStyle::visualRect(opt.direction, opt.rect, cornerWidgetRect));
+ }
+
+ scrollBarContainers[Qt::Horizontal]->setVisible(needh);
+ scrollBarContainers[Qt::Vertical]->setVisible(needv);
+
+ if (q->isRightToLeft())
+ viewportRect.adjust(right, top, -left, -bottom);
+ else
+ viewportRect.adjust(left, top, -right, -bottom);
+
+ viewport->setGeometry(QStyle::visualRect(opt.direction, opt.rect, viewportRect)); // resize the viewport last
+}
+
+// ### Fix for 4.4, talk to Bjoern E or Girish.
+void QAbstractScrollAreaPrivate::scrollBarPolicyChanged(Qt::Orientation, Qt::ScrollBarPolicy) {}
+
+/*!
+ \internal
+
+ Creates a new QAbstractScrollAreaPrivate, \a dd with the given \a parent.
+*/
+QAbstractScrollArea::QAbstractScrollArea(QAbstractScrollAreaPrivate &dd, QWidget *parent)
+ :QFrame(dd, parent)
+{
+ Q_D(QAbstractScrollArea);
+ d->init();
+}
+
+/*!
+ Constructs a viewport.
+
+ The \a parent arguments is sent to the QWidget constructor.
+*/
+QAbstractScrollArea::QAbstractScrollArea(QWidget *parent)
+ :QFrame(*new QAbstractScrollAreaPrivate, parent)
+{
+ Q_D(QAbstractScrollArea);
+ d->init();
+}
+
+
+/*!
+ Destroys the viewport.
+ */
+QAbstractScrollArea::~QAbstractScrollArea()
+{
+ Q_D(QAbstractScrollArea);
+ delete d->viewportFilter;
+}
+
+
+/*!
+ \since 4.2
+ Sets the viewport to be the given \a widget.
+ The QAbstractScrollArea will take ownership of the given \a widget.
+
+ If \a widget is 0, QAbstractScrollArea will assign a new QWidget instance
+ for the viewport.
+
+ \sa viewport()
+*/
+void QAbstractScrollArea::setViewport(QWidget *widget)
+{
+ Q_D(QAbstractScrollArea);
+ if (widget != d->viewport) {
+ QWidget *oldViewport = d->viewport;
+ if (!widget)
+ widget = new QWidget;
+ d->viewport = widget;
+ d->viewport->setParent(this);
+ d->viewport->setFocusProxy(this);
+ d->viewport->installEventFilter(d->viewportFilter);
+ d->layoutChildren();
+ if (isVisible())
+ d->viewport->show();
+ QMetaObject::invokeMethod(this, "setupViewport", Q_ARG(QWidget *, widget));
+ delete oldViewport;
+ }
+}
+
+/*!
+ Returns the viewport widget.
+
+ Use the QScrollArea::widget() function to retrieve the contents of
+ the viewport widget.
+
+ \sa QScrollArea::widget()
+*/
+QWidget *QAbstractScrollArea::viewport() const
+{
+ Q_D(const QAbstractScrollArea);
+ return d->viewport;
+}
+
+
+/*!
+Returns the size of the viewport as if the scroll bars had no valid
+scrolling range.
+*/
+// ### still thinking about the name
+QSize QAbstractScrollArea::maximumViewportSize() const
+{
+ Q_D(const QAbstractScrollArea);
+ int hsbExt = d->hbar->sizeHint().height();
+ int vsbExt = d->vbar->sizeHint().width();
+
+ int f = 2 * d->frameWidth;
+ QSize max = size() - QSize(f + d->left + d->right, f + d->top + d->bottom);
+ if (d->vbarpolicy == Qt::ScrollBarAlwaysOn)
+ max.rwidth() -= vsbExt;
+ if (d->hbarpolicy == Qt::ScrollBarAlwaysOn)
+ max.rheight() -= hsbExt;
+ return max;
+}
+
+/*!
+ \property QAbstractScrollArea::verticalScrollBarPolicy
+ \brief the policy for the vertical scroll bar
+
+ The default policy is Qt::ScrollBarAsNeeded.
+
+ \sa horizontalScrollBarPolicy
+*/
+
+Qt::ScrollBarPolicy QAbstractScrollArea::verticalScrollBarPolicy() const
+{
+ Q_D(const QAbstractScrollArea);
+ return d->vbarpolicy;
+}
+
+void QAbstractScrollArea::setVerticalScrollBarPolicy(Qt::ScrollBarPolicy policy)
+{
+ Q_D(QAbstractScrollArea);
+ const Qt::ScrollBarPolicy oldPolicy = d->vbarpolicy;
+ d->vbarpolicy = policy;
+ if (isVisible())
+ d->layoutChildren();
+ if (oldPolicy != d->vbarpolicy)
+ d->scrollBarPolicyChanged(Qt::Vertical, d->vbarpolicy);
+}
+
+
+/*!
+ Returns the vertical scroll bar.
+
+ \sa verticalScrollBarPolicy, horizontalScrollBar()
+ */
+QScrollBar *QAbstractScrollArea::verticalScrollBar() const
+{
+ Q_D(const QAbstractScrollArea);
+ return d->vbar;
+}
+
+/*!
+ \since 4.2
+ Replaces the existing vertical scroll bar with \a scrollBar, and sets all
+ the former scroll bar's slider properties on the new scroll bar. The former
+ scroll bar is then deleted.
+
+ QAbstractScrollArea already provides vertical and horizontal scroll bars by
+ default. You can call this function to replace the default vertical
+ scroll bar with your own custom scroll bar.
+
+ \sa verticalScrollBar(), setHorizontalScrollBar()
+*/
+void QAbstractScrollArea::setVerticalScrollBar(QScrollBar *scrollBar)
+{
+ Q_D(QAbstractScrollArea);
+ if (!scrollBar) {
+ qWarning("QAbstractScrollArea::setVerticalScrollBar: Cannot set a null scroll bar");
+ return;
+ }
+
+ d->replaceScrollBar(scrollBar, Qt::Vertical);
+}
+
+/*!
+ \property QAbstractScrollArea::horizontalScrollBarPolicy
+ \brief the policy for the horizontal scroll bar
+
+ The default policy is Qt::ScrollBarAsNeeded.
+
+ \sa verticalScrollBarPolicy
+*/
+
+Qt::ScrollBarPolicy QAbstractScrollArea::horizontalScrollBarPolicy() const
+{
+ Q_D(const QAbstractScrollArea);
+ return d->hbarpolicy;
+}
+
+void QAbstractScrollArea::setHorizontalScrollBarPolicy(Qt::ScrollBarPolicy policy)
+{
+ Q_D(QAbstractScrollArea);
+ const Qt::ScrollBarPolicy oldPolicy = d->hbarpolicy;
+ d->hbarpolicy = policy;
+ if (isVisible())
+ d->layoutChildren();
+ if (oldPolicy != d->hbarpolicy)
+ d->scrollBarPolicyChanged(Qt::Horizontal, d->hbarpolicy);
+}
+
+/*!
+ Returns the horizontal scroll bar.
+
+ \sa horizontalScrollBarPolicy, verticalScrollBar()
+ */
+QScrollBar *QAbstractScrollArea::horizontalScrollBar() const
+{
+ Q_D(const QAbstractScrollArea);
+ return d->hbar;
+}
+
+/*!
+ \since 4.2
+
+ Replaces the existing horizontal scroll bar with \a scrollBar, and sets all
+ the former scroll bar's slider properties on the new scroll bar. The former
+ scroll bar is then deleted.
+
+ QAbstractScrollArea already provides horizontal and vertical scroll bars by
+ default. You can call this function to replace the default horizontal
+ scroll bar with your own custom scroll bar.
+
+ \sa horizontalScrollBar(), setVerticalScrollBar()
+*/
+void QAbstractScrollArea::setHorizontalScrollBar(QScrollBar *scrollBar)
+{
+ Q_D(QAbstractScrollArea);
+ if (!scrollBar) {
+ qWarning("QAbstractScrollArea::setHorizontalScrollBar: Cannot set a null scroll bar");
+ return;
+ }
+
+ d->replaceScrollBar(scrollBar, Qt::Horizontal);
+}
+
+/*!
+ \since 4.2
+
+ Returns the widget in the corner between the two scroll bars.
+
+ By default, no corner widget is present.
+*/
+QWidget *QAbstractScrollArea::cornerWidget() const
+{
+ Q_D(const QAbstractScrollArea);
+ return d->cornerWidget;
+}
+
+/*!
+ \since 4.2
+
+ Sets the widget in the corner between the two scroll bars to be
+ \a widget.
+
+ 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 corner widget is hidden.
+
+ You may call setCornerWidget() with the same widget at different
+ times.
+
+ All widgets set here will be deleted by the scroll area 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 horizontalScrollBarPolicy, horizontalScrollBarPolicy
+*/
+void QAbstractScrollArea::setCornerWidget(QWidget *widget)
+{
+ Q_D(QAbstractScrollArea);
+ QWidget* oldWidget = d->cornerWidget;
+ if (oldWidget != widget) {
+ if (oldWidget)
+ oldWidget->hide();
+ d->cornerWidget = widget;
+
+ if (widget && widget->parentWidget() != this)
+ widget->setParent(this);
+
+ d->layoutChildren();
+ if (widget)
+ widget->show();
+ } else {
+ d->cornerWidget = widget;
+ d->layoutChildren();
+ }
+}
+
+/*!
+ \since 4.2
+ Adds \a widget as a scroll bar widget in the location specified
+ by \a alignment.
+
+ Scroll bar widgets are shown next to the horizontal or vertical
+ scroll bar, and can be placed on either side of it. If you want
+ the scroll bar widgets to be always visible, set the
+ scrollBarPolicy for the corresponding scroll bar to \c AlwaysOn.
+
+ \a alignment must be one of Qt::Alignleft and Qt::AlignRight,
+ which maps to the horizontal scroll bar, or Qt::AlignTop and
+ Qt::AlignBottom, which maps to the vertical scroll bar.
+
+ A scroll bar widget can be removed by either re-parenting the
+ widget or deleting it. It's also possible to hide a widget with
+ QWidget::hide()
+
+ The scroll bar widget will be resized to fit the scroll bar
+ geometry for the current style. The following describes the case
+ for scroll bar widgets on the horizontal scroll bar:
+
+ The height of the widget will be set to match the height of the
+ scroll bar. To control the width of the widget, use
+ QWidget::setMinimumWidth and QWidget::setMaximumWidth, or
+ implement QWidget::sizeHint() and set a horizontal size policy.
+ If you want a square widget, call
+ QStyle::pixelMetric(QStyle::PM_ScrollBarExtent) and set the
+ width to this value.
+
+ \sa scrollBarWidgets()
+*/
+void QAbstractScrollArea::addScrollBarWidget(QWidget *widget, Qt::Alignment alignment)
+{
+ Q_D(QAbstractScrollArea);
+
+ if (widget == 0)
+ return;
+
+ const Qt::Orientation scrollBarOrientation
+ = ((alignment & Qt::AlignLeft) || (alignment & Qt::AlignRight)) ? Qt::Horizontal : Qt::Vertical;
+ const QAbstractScrollAreaScrollBarContainer::LogicalPosition position
+ = ((alignment & Qt::AlignRight) || (alignment & Qt::AlignBottom))
+ ? QAbstractScrollAreaScrollBarContainer::LogicalRight : QAbstractScrollAreaScrollBarContainer::LogicalLeft;
+ d->scrollBarContainers[scrollBarOrientation]->addWidget(widget, position);
+ d->layoutChildren();
+ if (isHidden() == false)
+ widget->show();
+}
+
+/*!
+ \since 4.2
+ Returns a list of the currently set scroll bar widgets. \a alignment
+ can be any combination of the four location flags.
+
+ \sa addScrollBarWidget()
+*/
+QWidgetList QAbstractScrollArea::scrollBarWidgets(Qt::Alignment alignment)
+{
+ Q_D(QAbstractScrollArea);
+
+ QWidgetList list;
+
+ if (alignment & Qt::AlignLeft)
+ list += d->scrollBarContainers[Qt::Horizontal]->widgets(QAbstractScrollAreaScrollBarContainer::LogicalLeft);
+ if (alignment & Qt::AlignRight)
+ list += d->scrollBarContainers[Qt::Horizontal]->widgets(QAbstractScrollAreaScrollBarContainer::LogicalRight);
+ if (alignment & Qt::AlignTop)
+ list += d->scrollBarContainers[Qt::Vertical]->widgets(QAbstractScrollAreaScrollBarContainer::LogicalLeft);
+ if (alignment & Qt::AlignBottom)
+ list += d->scrollBarContainers[Qt::Vertical]->widgets(QAbstractScrollAreaScrollBarContainer::LogicalRight);
+
+ return list;
+}
+
+/*!
+ 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
+ is left blank; put widgets in the unused area.
+
+ Note that this function is frequently called by QTreeView and
+ QTableView, so margins must be implemented by QAbstractScrollArea
+ subclasses. Also, if the subclasses are to be used in item views,
+ they should not call this function.
+
+ By default all margins are zero.
+
+*/
+void QAbstractScrollArea::setViewportMargins(int left, int top, int right, int bottom)
+{
+ Q_D(QAbstractScrollArea);
+ d->left = left;
+ d->top = top;
+ d->right = right;
+ d->bottom = bottom;
+ d->layoutChildren();
+}
+
+/*!
+ \fn bool QAbstractScrollArea::event(QEvent *event)
+
+ \reimp
+
+ This is the main event handler for the QAbstractScrollArea widget (\e not
+ the scrolling area viewport()). The specified \a event is a general event
+ object that may need to be cast to the appropriate class depending on its
+ type.
+
+ \sa QEvent::type()
+*/
+bool QAbstractScrollArea::event(QEvent *e)
+{
+ Q_D(QAbstractScrollArea);
+ switch (e->type()) {
+ case QEvent::AcceptDropsChange:
+ // There was a chance that with accessibility client we get an
+ // event before the viewport was created.
+ // Also, in some cases we might get here from QWidget::event() virtual function which is (indirectly) called
+ // from the viewport constructor at the time when the d->viewport is not yet initialized even without any
+ // accessibility client. See qabstractscrollarea autotest for a test case.
+ if (d->viewport)
+ d->viewport->setAcceptDrops(acceptDrops());
+ break;
+ case QEvent::MouseTrackingChange:
+ d->viewport->setMouseTracking(hasMouseTracking());
+ break;
+ case QEvent::Resize:
+ d->layoutChildren();
+ break;
+ case QEvent::Paint:
+ if (d->cornerPaintingRect.isValid()) {
+ QStyleOption option;
+ option.rect = d->cornerPaintingRect;
+ QPainter p(this);
+ style()->drawPrimitive(QStyle::PE_PanelScrollAreaCorner, &option, &p, this);
+ }
+#ifdef Q_WS_MAC
+ if (d->reverseCornerPaintingRect.isValid()) {
+ QStyleOption option;
+ option.rect = d->reverseCornerPaintingRect;
+ QPainter p(this);
+ style()->drawPrimitive(QStyle::PE_PanelScrollAreaCorner, &option, &p, this);
+ }
+#endif
+ QFrame::paintEvent((QPaintEvent*)e);
+ break;
+#ifndef QT_NO_CONTEXTMENU
+ case QEvent::ContextMenu:
+ if (static_cast<QContextMenuEvent *>(e)->reason() == QContextMenuEvent::Keyboard)
+ return QFrame::event(e);
+ e->ignore();
+ break;
+#endif // QT_NO_CONTEXTMENU
+ case QEvent::MouseButtonPress:
+ case QEvent::MouseButtonRelease:
+ case QEvent::MouseButtonDblClick:
+ case QEvent::MouseMove:
+ case QEvent::Wheel:
+#ifndef QT_NO_DRAGANDDROP
+ case QEvent::Drop:
+ case QEvent::DragEnter:
+ case QEvent::DragMove:
+ case QEvent::DragLeave:
+#endif
+ return false;
+ case QEvent::StyleChange:
+ case QEvent::LayoutDirectionChange:
+ case QEvent::ApplicationLayoutDirectionChange:
+ d->layoutChildren();
+ // fall through
+ default:
+ return QFrame::event(e);
+ }
+ return true;
+}
+
+/*!
+ \fn bool QAbstractScrollArea::viewportEvent(QEvent *event)
+
+ The main event handler for the scrolling area (the viewport() widget).
+ It handles the \a event specified, and can be called by subclasses to
+ provide reasonable default behavior.
+
+ Returns true to indicate to the event system that the event has been
+ handled, and needs no further processing; otherwise returns false to
+ indicate that the event should be propagated further.
+
+ You can reimplement this function in a subclass, but we recommend
+ using one of the specialized event handlers instead.
+
+ Specialised handlers for viewport events are: paintEvent(),
+ mousePressEvent(), mouseReleaseEvent(), mouseDoubleClickEvent(),
+ mouseMoveEvent(), wheelEvent(), dragEnterEvent(), dragMoveEvent(),
+ dragLeaveEvent(), dropEvent(), contextMenuEvent(), and
+ resizeEvent().
+*/
+bool QAbstractScrollArea::viewportEvent(QEvent *e)
+{
+ switch (e->type()) {
+ case QEvent::Resize:
+ case QEvent::Paint:
+ case QEvent::MouseButtonPress:
+ case QEvent::MouseButtonRelease:
+ case QEvent::MouseButtonDblClick:
+ case QEvent::MouseMove:
+ case QEvent::ContextMenu:
+#ifndef QT_NO_WHEELEVENT
+ case QEvent::Wheel:
+#endif
+#ifndef QT_NO_DRAGANDDROP
+ case QEvent::Drop:
+ case QEvent::DragEnter:
+ case QEvent::DragMove:
+ case QEvent::DragLeave:
+#endif
+ return QFrame::event(e);
+ case QEvent::LayoutRequest:
+ return event(e);
+ default:
+ break;
+ }
+ return false; // let the viewport widget handle the event
+}
+
+/*!
+ \fn void QAbstractScrollArea::resizeEvent(QResizeEvent *event)
+
+ This event handler can be reimplemented in a subclass to receive
+ resize events (passed in \a event), for the viewport() widget.
+
+ When resizeEvent() is called, the viewport already has its new
+ geometry: Its new size is accessible through the
+ QResizeEvent::size() function, and the old size through
+ QResizeEvent::oldSize().
+
+ \sa QWidget::resizeEvent()
+ */
+void QAbstractScrollArea::resizeEvent(QResizeEvent *)
+{
+}
+
+/*!
+ \fn void QAbstractScrollArea::paintEvent(QPaintEvent *event)
+
+ This event handler can be reimplemented in a subclass to receive
+ paint events (passed in \a event), for the viewport() widget.
+
+ \note If you open a painter, make sure to open it on the viewport().
+
+ \sa QWidget::paintEvent()
+*/
+void QAbstractScrollArea::paintEvent(QPaintEvent*)
+{
+}
+
+/*!
+ This event handler can be reimplemented in a subclass to receive
+ mouse press events for the viewport() widget. The event is passed
+ in \a e.
+
+ \sa QWidget::mousePressEvent()
+*/
+void QAbstractScrollArea::mousePressEvent(QMouseEvent *e)
+{
+ e->ignore();
+}
+
+/*!
+ This event handler can be reimplemented in a subclass to receive
+ mouse release events for the viewport() widget. The event is
+ passed in \a e.
+
+ \sa QWidget::mouseReleaseEvent()
+*/
+void QAbstractScrollArea::mouseReleaseEvent(QMouseEvent *e)
+{
+ e->ignore();
+}
+
+/*!
+ This event handler can be reimplemented in a subclass to receive
+ mouse double click events for the viewport() widget. The event is
+ passed in \a e.
+
+ \sa QWidget::mouseDoubleClickEvent()
+*/
+void QAbstractScrollArea::mouseDoubleClickEvent(QMouseEvent *e)
+{
+ e->ignore();
+}
+
+/*!
+ This event handler can be reimplemented in a subclass to receive
+ mouse move events for the viewport() widget. The event is passed
+ in \a e.
+
+ \sa QWidget::mouseMoveEvent()
+*/
+void QAbstractScrollArea::mouseMoveEvent(QMouseEvent *e)
+{
+ e->ignore();
+}
+
+/*!
+ This event handler can be reimplemented in a subclass to receive
+ wheel events for the viewport() widget. The event is passed in \a
+ e.
+
+ \sa QWidget::wheelEvent()
+*/
+#ifndef QT_NO_WHEELEVENT
+void QAbstractScrollArea::wheelEvent(QWheelEvent *e)
+{
+ Q_D(QAbstractScrollArea);
+ if (static_cast<QWheelEvent*>(e)->orientation() == Qt::Horizontal)
+ QApplication::sendEvent(d->hbar, e);
+ else
+ QApplication::sendEvent(d->vbar, e);
+}
+#endif
+
+#ifndef QT_NO_CONTEXTMENU
+/*!
+ This event handler can be reimplemented in a subclass to receive
+ context menu events for the viewport() widget. The event is passed
+ in \a e.
+
+ \sa QWidget::contextMenuEvent()
+*/
+void QAbstractScrollArea::contextMenuEvent(QContextMenuEvent *e)
+{
+ e->ignore();
+}
+#endif // QT_NO_CONTEXTMENU
+
+/*!
+ This function is called with key event \a e when key presses
+ occur. It handles PageUp, PageDown, Up, Down, Left, and Right, and
+ ignores all other key presses.
+*/
+void QAbstractScrollArea::keyPressEvent(QKeyEvent * e)
+{
+ Q_D(QAbstractScrollArea);
+ if (false){
+#ifndef QT_NO_SHORTCUT
+ } else if (e == QKeySequence::MoveToPreviousPage) {
+ d->vbar->triggerAction(QScrollBar::SliderPageStepSub);
+ } else if (e == QKeySequence::MoveToNextPage) {
+ d->vbar->triggerAction(QScrollBar::SliderPageStepAdd);
+#endif
+ } else {
+#ifdef QT_KEYPAD_NAVIGATION
+ if (QApplication::keypadNavigationEnabled() && !hasEditFocus()) {
+ e->ignore();
+ return;
+ }
+#endif
+ switch (e->key()) {
+ case Qt::Key_Up:
+ d->vbar->triggerAction(QScrollBar::SliderSingleStepSub);
+ break;
+ case Qt::Key_Down:
+ d->vbar->triggerAction(QScrollBar::SliderSingleStepAdd);
+ break;
+ case Qt::Key_Left:
+#ifdef QT_KEYPAD_NAVIGATION
+ if (QApplication::keypadNavigationEnabled() && hasEditFocus()
+ && (!d->hbar->isVisible() || d->hbar->value() == d->hbar->minimum())) {
+ //if we aren't using the hbar or we are already at the leftmost point ignore
+ e->ignore();
+ return;
+ }
+#endif
+ d->hbar->triggerAction(
+ layoutDirection() == Qt::LeftToRight
+ ? QScrollBar::SliderSingleStepSub : QScrollBar::SliderSingleStepAdd);
+ break;
+ case Qt::Key_Right:
+#ifdef QT_KEYPAD_NAVIGATION
+ if (QApplication::keypadNavigationEnabled() && hasEditFocus()
+ && (!d->hbar->isVisible() || d->hbar->value() == d->hbar->maximum())) {
+ //if we aren't using the hbar or we are already at the rightmost point ignore
+ e->ignore();
+ return;
+ }
+#endif
+ d->hbar->triggerAction(
+ layoutDirection() == Qt::LeftToRight
+ ? QScrollBar::SliderSingleStepAdd : QScrollBar::SliderSingleStepSub);
+ break;
+ default:
+ e->ignore();
+ return;
+ }
+ }
+ e->accept();
+}
+
+
+#ifndef QT_NO_DRAGANDDROP
+/*!
+ \fn void QAbstractScrollArea::dragEnterEvent(QDragEnterEvent *event)
+
+ This event handler can be reimplemented in a subclass to receive
+ drag enter events (passed in \a event), for the viewport() widget.
+
+ \sa QWidget::dragEnterEvent()
+*/
+void QAbstractScrollArea::dragEnterEvent(QDragEnterEvent *)
+{
+}
+
+/*!
+ \fn void QAbstractScrollArea::dragMoveEvent(QDragMoveEvent *event)
+
+ This event handler can be reimplemented in a subclass to receive
+ drag move events (passed in \a event), for the viewport() widget.
+
+ \sa QWidget::dragMoveEvent()
+*/
+void QAbstractScrollArea::dragMoveEvent(QDragMoveEvent *)
+{
+}
+
+/*!
+ \fn void QAbstractScrollArea::dragLeaveEvent(QDragLeaveEvent *event)
+
+ This event handler can be reimplemented in a subclass to receive
+ drag leave events (passed in \a event), for the viewport() widget.
+
+ \sa QWidget::dragLeaveEvent()
+*/
+void QAbstractScrollArea::dragLeaveEvent(QDragLeaveEvent *)
+{
+}
+
+/*!
+ \fn void QAbstractScrollArea::dropEvent(QDropEvent *event)
+
+ This event handler can be reimplemented in a subclass to receive
+ drop events (passed in \a event), for the viewport() widget.
+
+ \sa QWidget::dropEvent()
+*/
+void QAbstractScrollArea::dropEvent(QDropEvent *)
+{
+}
+
+
+#endif
+
+/*!
+ This virtual handler is called when the scroll bars are moved by
+ \a dx, \a dy, and consequently the viewport's contents should be
+ scrolled accordingly.
+
+ The default implementation simply calls update() on the entire
+ viewport(), subclasses can reimplement this handler for
+ optimization purposes, or - like QScrollArea - to move a contents
+ widget. The parameters \a dx and \a dy are there for convenience,
+ so that the class knows how much should be scrolled (useful
+ e.g. when doing pixel-shifts). You may just as well ignore these
+ values and scroll directly to the position the scroll bars
+ indicate.
+
+ Calling this function in order to scroll programmatically is an
+ error, use the scroll bars instead (e.g. by calling
+ QScrollBar::setValue() directly).
+*/
+void QAbstractScrollArea::scrollContentsBy(int, int)
+{
+ viewport()->update();
+}
+
+void QAbstractScrollAreaPrivate::_q_hslide(int x)
+{
+ Q_Q(QAbstractScrollArea);
+ int dx = xoffset - x;
+ xoffset = x;
+ q->scrollContentsBy(dx, 0);
+}
+
+void QAbstractScrollAreaPrivate::_q_vslide(int y)
+{
+ Q_Q(QAbstractScrollArea);
+ int dy = yoffset - y;
+ yoffset = y;
+ q->scrollContentsBy(0, dy);
+}
+
+void QAbstractScrollAreaPrivate::_q_showOrHideScrollBars()
+{
+ layoutChildren();
+}
+
+QPoint QAbstractScrollAreaPrivate::contentsOffset() const
+{
+ Q_Q(const QAbstractScrollArea);
+ QPoint offset;
+ if (vbar->isVisible())
+ offset.setY(vbar->value());
+ if (hbar->isVisible()) {
+ if (q->isRightToLeft())
+ offset.setX(hbar->maximum() - hbar->value());
+ else
+ offset.setX(hbar->value());
+ }
+ return offset;
+}
+
+/*!
+ \reimp
+
+*/
+QSize QAbstractScrollArea::minimumSizeHint() const
+{
+ Q_D(const QAbstractScrollArea);
+ int hsbExt = d->hbar->sizeHint().height();
+ int vsbExt = d->vbar->sizeHint().width();
+ int extra = 2 * d->frameWidth;
+ return QSize(d->scrollBarContainers[Qt::Horizontal]->sizeHint().width() + vsbExt + extra,
+ d->scrollBarContainers[Qt::Vertical]->sizeHint().height() + hsbExt + extra);
+}
+
+/*!
+ \reimp
+*/
+QSize QAbstractScrollArea::sizeHint() const
+{
+ return QSize(256, 192);
+#if 0
+ Q_D(const QAbstractScrollArea);
+ int h = qMax(10, fontMetrics().height());
+ int f = 2 * d->frameWidth;
+ return QSize((6 * h) + f, (4 * h) + f);
+#endif
+}
+
+/*!
+ This slot is called by QAbstractScrollArea after setViewport(\a
+ viewport) has been called. Reimplement this function in a
+ subclass of QAbstractScrollArea to initialize the new \a viewport
+ before it is used.
+
+ \sa setViewport()
+*/
+void QAbstractScrollArea::setupViewport(QWidget *viewport)
+{
+ Q_UNUSED(viewport);
+}
+
+QT_END_NAMESPACE
+
+#include "moc_qabstractscrollarea.cpp"
+#include "moc_qabstractscrollarea_p.cpp"
+
+#endif // QT_NO_SCROLLAREA
diff --git a/src/gui/widgets/qabstractscrollarea.h b/src/gui/widgets/qabstractscrollarea.h
new file mode 100644
index 0000000000..eac32f3769
--- /dev/null
+++ b/src/gui/widgets/qabstractscrollarea.h
@@ -0,0 +1,141 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QABSTRACTSCROLLAREA_H
+#define QABSTRACTSCROLLAREA_H
+
+#include <QtGui/qframe.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+#ifndef QT_NO_SCROLLAREA
+
+class QScrollBar;
+class QAbstractScrollAreaPrivate;
+
+class Q_GUI_EXPORT QAbstractScrollArea : public QFrame
+{
+ Q_OBJECT
+ Q_PROPERTY(Qt::ScrollBarPolicy verticalScrollBarPolicy READ verticalScrollBarPolicy WRITE setVerticalScrollBarPolicy)
+ Q_PROPERTY(Qt::ScrollBarPolicy horizontalScrollBarPolicy READ horizontalScrollBarPolicy WRITE setHorizontalScrollBarPolicy)
+
+public:
+ explicit QAbstractScrollArea(QWidget* parent=0);
+ ~QAbstractScrollArea();
+
+ Qt::ScrollBarPolicy verticalScrollBarPolicy() const;
+ void setVerticalScrollBarPolicy(Qt::ScrollBarPolicy);
+ QScrollBar *verticalScrollBar() const;
+ void setVerticalScrollBar(QScrollBar *scrollbar);
+
+ Qt::ScrollBarPolicy horizontalScrollBarPolicy() const;
+ void setHorizontalScrollBarPolicy(Qt::ScrollBarPolicy);
+ QScrollBar *horizontalScrollBar() const;
+ void setHorizontalScrollBar(QScrollBar *scrollbar);
+
+ QWidget *cornerWidget() const;
+ void setCornerWidget(QWidget *widget);
+
+ void addScrollBarWidget(QWidget *widget, Qt::Alignment alignment);
+ QWidgetList scrollBarWidgets(Qt::Alignment alignment);
+
+ QWidget *viewport() const;
+ void setViewport(QWidget *widget);
+ QSize maximumViewportSize() const;
+
+ QSize minimumSizeHint() const;
+
+ QSize sizeHint() const;
+
+protected Q_SLOTS:
+ void setupViewport(QWidget *viewport);
+
+protected:
+ QAbstractScrollArea(QAbstractScrollAreaPrivate &dd, QWidget *parent = 0);
+ void setViewportMargins(int left, int top, int right, int bottom);
+
+ bool event(QEvent *);
+ virtual bool viewportEvent(QEvent *);
+
+ void resizeEvent(QResizeEvent *);
+ void paintEvent(QPaintEvent *);
+ void mousePressEvent(QMouseEvent *);
+ void mouseReleaseEvent(QMouseEvent *);
+ void mouseDoubleClickEvent(QMouseEvent *);
+ void mouseMoveEvent(QMouseEvent *);
+#ifndef QT_NO_WHEELEVENT
+ void wheelEvent(QWheelEvent *);
+#endif
+#ifndef QT_NO_CONTEXTMENU
+ void contextMenuEvent(QContextMenuEvent *);
+#endif
+#ifndef QT_NO_DRAGANDDROP
+ void dragEnterEvent(QDragEnterEvent *);
+ void dragMoveEvent(QDragMoveEvent *);
+ void dragLeaveEvent(QDragLeaveEvent *);
+ void dropEvent(QDropEvent *);
+#endif
+
+ void keyPressEvent(QKeyEvent *);
+
+ virtual void scrollContentsBy(int dx, int dy);
+
+private:
+ Q_DECLARE_PRIVATE(QAbstractScrollArea)
+ Q_DISABLE_COPY(QAbstractScrollArea)
+ Q_PRIVATE_SLOT(d_func(), void _q_hslide(int))
+ Q_PRIVATE_SLOT(d_func(), void _q_vslide(int))
+ Q_PRIVATE_SLOT(d_func(), void _q_showOrHideScrollBars())
+
+ friend class QStyleSheetStyle;
+};
+
+#endif // QT_NO_SCROLLAREA
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QABSTRACTSCROLLAREA_H
diff --git a/src/gui/widgets/qabstractscrollarea_p.h b/src/gui/widgets/qabstractscrollarea_p.h
new file mode 100644
index 0000000000..e4c47e9905
--- /dev/null
+++ b/src/gui/widgets/qabstractscrollarea_p.h
@@ -0,0 +1,139 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QABSTRACTSCROLLAREA_P_H
+#define QABSTRACTSCROLLAREA_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/qframe_p.h"
+#include "qabstractscrollarea.h"
+
+QT_BEGIN_NAMESPACE
+
+#ifndef QT_NO_SCROLLAREA
+
+class QScrollBar;
+class QAbstractScrollAreaScrollBarContainer;
+class Q_GUI_EXPORT QAbstractScrollAreaPrivate: public QFramePrivate
+{
+ Q_DECLARE_PUBLIC(QAbstractScrollArea)
+
+public:
+ QAbstractScrollAreaPrivate();
+
+ void replaceScrollBar(QScrollBar *scrollBar, Qt::Orientation orientation);
+
+ QAbstractScrollAreaScrollBarContainer *scrollBarContainers[Qt::Vertical + 1];
+ QScrollBar *hbar, *vbar;
+ Qt::ScrollBarPolicy vbarpolicy, hbarpolicy;
+
+ QWidget *viewport;
+ QWidget *cornerWidget;
+ QRect cornerPaintingRect;
+#ifdef Q_WS_MAC
+ QRect reverseCornerPaintingRect;
+#endif
+ int left, top, right, bottom; // viewport margin
+
+ int xoffset, yoffset;
+
+ void init();
+ void layoutChildren();
+ // ### Fix for 4.4, talk to Bjoern E or Girish.
+ virtual void scrollBarPolicyChanged(Qt::Orientation, Qt::ScrollBarPolicy);
+
+ void _q_hslide(int);
+ void _q_vslide(int);
+ void _q_showOrHideScrollBars();
+
+ virtual QPoint contentsOffset() const;
+
+ inline bool viewportEvent(QEvent *event)
+ { return q_func()->viewportEvent(event); }
+ QObject *viewportFilter;
+};
+
+class QAbstractScrollAreaFilter : public QObject
+{
+ Q_OBJECT
+public:
+ QAbstractScrollAreaFilter(QAbstractScrollAreaPrivate *p) : d(p)
+ { setObjectName(QLatin1String("qt_abstractscrollarea_filter")); }
+ bool eventFilter(QObject *o, QEvent *e)
+ { return (o == d->viewport ? d->viewportEvent(e) : false); }
+private:
+ QAbstractScrollAreaPrivate *d;
+};
+
+class QBoxLayout;
+class QAbstractScrollAreaScrollBarContainer : public QWidget
+{
+public:
+ enum LogicalPosition { LogicalLeft = 1, LogicalRight = 2 };
+
+ QAbstractScrollAreaScrollBarContainer(Qt::Orientation orientation, QWidget *parent);
+ void addWidget(QWidget *widget, LogicalPosition position);
+ QWidgetList widgets(LogicalPosition position);
+ void removeWidget(QWidget *widget);
+
+ QScrollBar *scrollBar;
+ QBoxLayout *layout;
+private:
+ int scrollBarLayoutIndex() const;
+
+ Qt::Orientation orientation;
+};
+
+#endif // QT_NO_SCROLLAREA
+
+QT_END_NAMESPACE
+
+#endif // QABSTRACTSCROLLAREA_P_H
diff --git a/src/gui/widgets/qabstractslider.cpp b/src/gui/widgets/qabstractslider.cpp
new file mode 100644
index 0000000000..8028fdceb6
--- /dev/null
+++ b/src/gui/widgets/qabstractslider.cpp
@@ -0,0 +1,914 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <qapplication.h>
+#include "qabstractslider.h"
+#include "qevent.h"
+#include "qabstractslider_p.h"
+#include "qdebug.h"
+#ifndef QT_NO_ACCESSIBILITY
+#include "qaccessible.h"
+#endif
+#include <limits.h>
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QAbstractSlider
+ \brief The QAbstractSlider class provides an integer value within a range.
+
+ \ingroup abstractwidgets
+
+ The class is designed as a common super class for widgets like
+ QScrollBar, QSlider and QDial.
+
+ Here are the main properties of the class:
+
+ \list 1
+
+ \i \l value: The bounded integer that QAbstractSlider maintains.
+
+ \i \l minimum: The lowest possible value.
+
+ \i \l maximum: The highest possible value.
+
+ \i \l singleStep: The smaller of two natural steps that an
+ abstract sliders provides and typically corresponds to the user
+ pressing an arrow key.
+
+ \i \l pageStep: The larger of two natural steps that an abstract
+ slider provides and typically corresponds to the user pressing
+ PageUp or PageDown.
+
+ \i \l tracking: Whether slider tracking is enabled.
+
+ \i \l sliderPosition: The current position of the slider. If \l
+ tracking is enabled (the default), this is identical to \l value.
+
+ \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 minimum() + \e n * singleStep() for integer values of \e n.
+ Some widgets may allow the user to set any value at all; others
+ may just provide multiples of singleStep() or pageStep().
+
+ QAbstractSlider emits a comprehensive set of signals:
+
+ \table
+ \header \i Signal \i Emitted when
+ \row \i \l valueChanged()
+ \i the value has changed. The \l tracking
+ determines whether this signal is emitted during user
+ interaction.
+ \row \i \l sliderPressed()
+ \i the user starts to drag the slider.
+ \row \i \l sliderMoved()
+ \i the user drags the slider.
+ \row \i \l sliderReleased()
+ \i the user releases the slider.
+ \row \i \l actionTriggered()
+ \i a slider action was triggerd.
+ \row \i \l rangeChanged()
+ \i a the range has changed.
+ \endtable
+
+ QAbstractSlider provides a virtual sliderChange() function that is
+ well suited for updating the on-screen representation of
+ sliders. By calling triggerAction(), subclasses trigger slider
+ actions. Two helper functions QStyle::sliderPositionFromValue() and
+ QStyle::sliderValueFromPosition() help subclasses and styles to map
+ screen coordinates to logical range values.
+
+ \sa QAbstractSpinBox, QSlider, QDial, QScrollBar, {Sliders Example}
+*/
+
+/*!
+ \enum QAbstractSlider::SliderAction
+
+ \value SliderNoAction
+ \value SliderSingleStepAdd
+ \value SliderSingleStepSub
+ \value SliderPageStepAdd
+ \value SliderPageStepSub
+ \value SliderToMinimum
+ \value SliderToMaximum
+ \value SliderMove
+
+*/
+
+/*!
+ \fn void QAbstractSlider::valueChanged(int value)
+
+ This signal is emitted when the slider value has changed, with the
+ new slider \a value as argument.
+*/
+
+/*!
+ \fn void QAbstractSlider::sliderPressed()
+
+ This signal is emitted when the user presses the slider with the
+ mouse, or programmatically when setSliderDown(true) is called.
+
+ \sa sliderReleased(), sliderMoved(), isSliderDown()
+*/
+
+/*!
+ \fn void QAbstractSlider::sliderMoved(int value)
+
+ This signal is emitted when sliderDown is true and the slider moves. This
+ usually happens when the user is dragging the slider. The \a value
+ is the new slider position.
+
+ This signal is emitted even when tracking is turned off.
+
+ \sa setTracking(), valueChanged(), isSliderDown(),
+ sliderPressed(), sliderReleased()
+*/
+
+/*!
+ \fn void QAbstractSlider::sliderReleased()
+
+ This signal is emitted when the user releases the slider with the
+ mouse, or programmatically when setSliderDown(false) is called.
+
+ \sa sliderPressed() sliderMoved() sliderDown
+*/
+
+/*!
+ \fn void QAbstractSlider::rangeChanged(int min, int max)
+
+ This signal is emitted when the slider range has changed, with \a
+ min being the new minimum, and \a max being the new maximum.
+
+ \sa minimum, maximum
+*/
+
+/*!
+ \fn void QAbstractSlider::actionTriggered(int action)
+
+ This signal is emitted when the slider action \a action is
+ triggered. Actions are \l SliderSingleStepAdd, \l
+ SliderSingleStepSub, \l SliderPageStepAdd, \l SliderPageStepSub,
+ \l SliderToMinimum, \l SliderToMaximum, and \l SliderMove.
+
+ When the signal is emitted, the \l sliderPosition has been
+ adjusted according to the action, but the \l value has not yet
+ been propagated (meaning the valueChanged() signal was not yet
+ emitted), and the visual display has not been updated. In slots
+ connected to this signal you can thus safely adjust any action by
+ calling setSliderPosition() yourself, based on both the action and
+ the slider's value.
+
+ \sa triggerAction()
+*/
+
+/*!
+ \enum QAbstractSlider::SliderChange
+
+ \value SliderRangeChange
+ \value SliderOrientationChange
+ \value SliderStepsChange
+ \value SliderValueChange
+*/
+
+QAbstractSliderPrivate::QAbstractSliderPrivate()
+ : minimum(0), maximum(99), singleStep(1), pageStep(10),
+ value(0), position(0), pressValue(-1), tracking(true), blocktracking(false), pressed(false),
+ invertedAppearance(false), invertedControls(false),
+ orientation(Qt::Horizontal), repeatAction(QAbstractSlider::SliderNoAction)
+{
+}
+
+QAbstractSliderPrivate::~QAbstractSliderPrivate()
+{
+}
+
+/*!
+ Sets the slider's minimum to \a min and its maximum to \a max.
+
+ If \a max is smaller than \a min, \a min becomes the only legal
+ value.
+
+ \sa minimum maximum
+*/
+void QAbstractSlider::setRange(int min, int max)
+{
+ Q_D(QAbstractSlider);
+ int oldMin = d->minimum;
+ int oldMax = d->maximum;
+ d->minimum = min;
+ d->maximum = qMax(min, max);
+ if (oldMin != d->minimum || oldMax != d->maximum) {
+ sliderChange(SliderRangeChange);
+ emit rangeChanged(d->minimum, d->maximum);
+ setValue(d->value); // re-bound
+ }
+}
+
+
+void QAbstractSliderPrivate::setSteps(int single, int page)
+{
+ Q_Q(QAbstractSlider);
+ singleStep = qAbs(single);
+ pageStep = qAbs(page);
+ q->sliderChange(QAbstractSlider::SliderStepsChange);
+}
+
+/*!
+ Constructs an abstract slider.
+
+ The \a parent arguments is sent to the QWidget constructor.
+
+ The \l minimum defaults to 0, the \l maximum to 99, with a \l
+ singleStep size of 1 and a \l pageStep size of 10, and an initial
+ \l value of 0.
+*/
+QAbstractSlider::QAbstractSlider(QWidget *parent)
+ :QWidget(*new QAbstractSliderPrivate, parent, 0)
+{
+}
+
+/*! \internal */
+QAbstractSlider::QAbstractSlider(QAbstractSliderPrivate &dd, QWidget *parent)
+ :QWidget(dd, parent, 0)
+{
+}
+
+/*!
+ Destroys the slider.
+*/
+QAbstractSlider::~QAbstractSlider()
+{
+}
+
+/*!
+ \property QAbstractSlider::orientation
+ \brief the orientation of the slider
+
+ The orientation must be \l Qt::Vertical (the default) or \l
+ Qt::Horizontal.
+*/
+void QAbstractSlider::setOrientation(Qt::Orientation orientation)
+{
+ Q_D(QAbstractSlider);
+ if (d->orientation == orientation)
+ return;
+
+ d->orientation = orientation;
+ if (!testAttribute(Qt::WA_WState_OwnSizePolicy)) {
+ QSizePolicy sp = sizePolicy();
+ sp.transpose();
+ setSizePolicy(sp);
+ setAttribute(Qt::WA_WState_OwnSizePolicy, false);
+ }
+ update();
+ updateGeometry();
+}
+
+Qt::Orientation QAbstractSlider::orientation() const
+{
+ Q_D(const QAbstractSlider);
+ return d->orientation;
+}
+
+
+/*!
+ \property QAbstractSlider::minimum
+ \brief the sliders's minimum value
+
+ When setting this property, the \l maximum is adjusted if
+ necessary to ensure that the range remains valid. Also the
+ slider's current value is adjusted to be within the new range.
+
+*/
+
+void QAbstractSlider::setMinimum(int min)
+{
+ Q_D(QAbstractSlider);
+ setRange(min, qMax(d->maximum, min));
+}
+
+int QAbstractSlider::minimum() const
+{
+ Q_D(const QAbstractSlider);
+ return d->minimum;
+}
+
+
+/*!
+ \property QAbstractSlider::maximum
+ \brief the slider's maximum value
+
+ When setting this property, the \l minimum is adjusted if
+ necessary to ensure that the range remains valid. Also the
+ slider's current value is adjusted to be within the new range.
+
+
+*/
+
+void QAbstractSlider::setMaximum(int max)
+{
+ Q_D(QAbstractSlider);
+ setRange(qMin(d->minimum, max), max);
+}
+
+int QAbstractSlider::maximum() const
+{
+ Q_D(const QAbstractSlider);
+ return d->maximum;
+}
+
+
+
+/*!
+ \property QAbstractSlider::singleStep
+ \brief the single step.
+
+ The smaller of two natural steps that an
+ abstract sliders provides and typically corresponds to the user
+ pressing an arrow key.
+
+ \sa pageStep
+*/
+
+void QAbstractSlider::setSingleStep(int step)
+{
+ Q_D(QAbstractSlider);
+ if (step != d->singleStep)
+ d->setSteps(step, d->pageStep);
+}
+
+int QAbstractSlider::singleStep() const
+{
+ Q_D(const QAbstractSlider);
+ return d->singleStep;
+}
+
+
+/*!
+ \property QAbstractSlider::pageStep
+ \brief the page step.
+
+ The larger of two natural steps that an abstract slider provides
+ and typically corresponds to the user pressing PageUp or PageDown.
+
+ \sa singleStep
+*/
+
+void QAbstractSlider::setPageStep(int step)
+{
+ Q_D(QAbstractSlider);
+ if (step != d->pageStep)
+ d->setSteps(d->singleStep, step);
+}
+
+int QAbstractSlider::pageStep() const
+{
+ Q_D(const QAbstractSlider);
+ return d->pageStep;
+}
+
+/*!
+ \property QAbstractSlider::tracking
+ \brief whether slider tracking is enabled
+
+ If tracking is enabled (the default), the slider emits the
+ valueChanged() signal while the slider is being dragged. If
+ tracking is disabled, the slider emits the valueChanged() signal
+ only when the user releases the slider.
+
+ \sa sliderDown
+*/
+void QAbstractSlider::setTracking(bool enable)
+{
+ Q_D(QAbstractSlider);
+ d->tracking = enable;
+}
+
+bool QAbstractSlider::hasTracking() const
+{
+ Q_D(const QAbstractSlider);
+ return d->tracking;
+}
+
+
+/*!
+ \property QAbstractSlider::sliderDown
+ \brief whether the slider is pressed down.
+
+ The property is set by subclasses in order to let the abstract
+ slider know whether or not \l tracking has any effect.
+
+ Changing the slider down property emits the sliderPressed() and
+ sliderReleased() signals.
+
+*/
+void QAbstractSlider::setSliderDown(bool down)
+{
+ Q_D(QAbstractSlider);
+ bool doEmit = d->pressed != down;
+
+ d->pressed = down;
+
+ if (doEmit) {
+ if (down)
+ emit sliderPressed();
+ else
+ emit sliderReleased();
+ }
+
+ if (!down && d->position != d->value)
+ triggerAction(SliderMove);
+}
+
+bool QAbstractSlider::isSliderDown() const
+{
+ Q_D(const QAbstractSlider);
+ return d->pressed;
+}
+
+
+/*!
+ \property QAbstractSlider::sliderPosition
+ \brief the current slider position
+
+ If \l tracking is enabled (the default), this is identical to \l value.
+*/
+void QAbstractSlider::setSliderPosition(int position)
+{
+ Q_D(QAbstractSlider);
+ position = d->bound(position);
+ if (position == d->position)
+ return;
+ d->position = position;
+ if (!d->tracking)
+ update();
+ if (d->pressed)
+ emit sliderMoved(position);
+ if (d->tracking && !d->blocktracking)
+ triggerAction(SliderMove);
+}
+
+int QAbstractSlider::sliderPosition() const
+{
+ Q_D(const QAbstractSlider);
+ return d->position;
+}
+
+
+/*!
+ \property QAbstractSlider::value
+ \brief the slider's current value
+
+ The slider forces the value to be within the legal range: \l
+ minimum <= \c value <= \l maximum.
+
+ Changing the value also changes the \l sliderPosition.
+*/
+
+
+int QAbstractSlider::value() const
+{
+ Q_D(const QAbstractSlider);
+ return d->value;
+}
+
+void QAbstractSlider::setValue(int value)
+{
+ Q_D(QAbstractSlider);
+ value = d->bound(value);
+ if (d->value == value && d->position == value)
+ return;
+ d->value = value;
+ if (d->position != value) {
+ d->position = value;
+ if (d->pressed)
+ emit sliderMoved((d->position = value));
+ }
+#ifndef QT_NO_ACCESSIBILITY
+ QAccessible::updateAccessibility(this, 0, QAccessible::ValueChanged);
+#endif
+ sliderChange(SliderValueChange);
+ emit valueChanged(value);
+}
+
+/*!
+ \property QAbstractSlider::invertedAppearance
+ \brief whether or not a slider shows its values inverted.
+
+ If this property is false (the default), the minimum and maximum will
+ be shown in its classic position for the inherited widget. If the
+ value is true, the minimum and maximum appear at their opposite location.
+
+ Note: This property makes most sense for sliders and dials. For
+ scroll bars, the visual effect of the scroll bar subcontrols depends on
+ whether or not the styles understand inverted appearance; most styles
+ ignore this property for scroll bars.
+*/
+
+bool QAbstractSlider::invertedAppearance() const
+{
+ Q_D(const QAbstractSlider);
+ return d->invertedAppearance;
+}
+
+void QAbstractSlider::setInvertedAppearance(bool invert)
+{
+ Q_D(QAbstractSlider);
+ d->invertedAppearance = invert;
+ update();
+}
+
+
+/*!
+ \property QAbstractSlider::invertedControls
+ \brief whether or not the slider inverts its wheel and key events.
+
+ If this property is false, scrolling the mouse wheel "up" and using keys
+ like page up will increase the slider's value towards its maximum. Otherwise
+ pressing page up will move value towards the slider's minimum.
+*/
+
+
+bool QAbstractSlider::invertedControls() const
+{
+ Q_D(const QAbstractSlider);
+ return d->invertedControls;
+}
+
+void QAbstractSlider::setInvertedControls(bool invert)
+{
+ Q_D(QAbstractSlider);
+ d->invertedControls = invert;
+}
+
+/*! Triggers a slider \a action. Possible actions are \l
+ SliderSingleStepAdd, \l SliderSingleStepSub, \l SliderPageStepAdd,
+ \l SliderPageStepSub, \l SliderToMinimum, \l SliderToMaximum, and \l
+ SliderMove.
+
+ \sa actionTriggered()
+ */
+void QAbstractSlider::triggerAction(SliderAction action)
+{
+ Q_D(QAbstractSlider);
+ d->blocktracking = true;
+ switch (action) {
+ case SliderSingleStepAdd:
+ setSliderPosition(d->overflowSafeAdd(d->singleStep));
+ break;
+ case SliderSingleStepSub:
+ setSliderPosition(d->overflowSafeAdd(-d->singleStep));
+ break;
+ case SliderPageStepAdd:
+ setSliderPosition(d->overflowSafeAdd(d->pageStep));
+ break;
+ case SliderPageStepSub:
+ setSliderPosition(d->overflowSafeAdd(-d->pageStep));
+ break;
+ case SliderToMinimum:
+ setSliderPosition(d->minimum);
+ break;
+ case SliderToMaximum:
+ setSliderPosition(d->maximum);
+ break;
+ case SliderMove:
+ case SliderNoAction:
+ break;
+ };
+ emit actionTriggered(action);
+ d->blocktracking = false;
+ setValue(d->position);
+}
+
+/*! Sets action \a action to be triggered repetitively in intervals
+of \a repeatTime, after an initial delay of \a thresholdTime.
+
+\sa triggerAction() repeatAction()
+ */
+void QAbstractSlider::setRepeatAction(SliderAction action, int thresholdTime, int repeatTime)
+{
+ Q_D(QAbstractSlider);
+ if ((d->repeatAction = action) == SliderNoAction) {
+ d->repeatActionTimer.stop();
+ } else {
+ d->repeatActionTime = repeatTime;
+ d->repeatActionTimer.start(thresholdTime, this);
+ }
+}
+
+/*!
+ Returns the current repeat action.
+ \sa setRepeatAction()
+ */
+QAbstractSlider::SliderAction QAbstractSlider::repeatAction() const
+{
+ Q_D(const QAbstractSlider);
+ return d->repeatAction;
+}
+
+/*!\reimp
+ */
+void QAbstractSlider::timerEvent(QTimerEvent *e)
+{
+ Q_D(QAbstractSlider);
+ if (e->timerId() == d->repeatActionTimer.timerId()) {
+ if (d->repeatActionTime) { // was threshold time, use repeat time next time
+ d->repeatActionTimer.start(d->repeatActionTime, this);
+ d->repeatActionTime = 0;
+ }
+ if (d->repeatAction == SliderPageStepAdd)
+ d->setAdjustedSliderPosition(d->overflowSafeAdd(d->pageStep));
+ else if (d->repeatAction == SliderPageStepSub)
+ d->setAdjustedSliderPosition(d->overflowSafeAdd(-d->pageStep));
+ else
+ triggerAction(d->repeatAction);
+ }
+}
+
+/*!
+ Reimplement this virtual function to track slider changes such as
+ \l SliderRangeChange, \l SliderOrientationChange, \l
+ SliderStepsChange, or \l SliderValueChange. The default
+ implementation only updates the display and ignores the \a change
+ parameter.
+ */
+void QAbstractSlider::sliderChange(SliderChange)
+{
+ update();
+}
+
+
+/*!
+ \reimp
+*/
+#ifndef QT_NO_WHEELEVENT
+void QAbstractSlider::wheelEvent(QWheelEvent * e)
+{
+ Q_D(QAbstractSlider);
+ e->ignore();
+ if (e->orientation() != d->orientation && !rect().contains(e->pos()))
+ return;
+
+ static qreal offset = 0;
+ static QAbstractSlider *offset_owner = 0;
+ if (offset_owner != this){
+ offset_owner = this;
+ offset = 0;
+ }
+
+ // On Mac/Cocoa, always scroll one step. The mouse wheel acceleration
+ // is higher than on other systems, so this works well in practice.
+#ifdef QT_MAC_USE_COCOA
+ int step = 1;
+#else
+ int step = qMin(QApplication::wheelScrollLines() * d->singleStep, d->pageStep);
+#endif
+ if ((e->modifiers() & Qt::ControlModifier) || (e->modifiers() & Qt::ShiftModifier))
+ step = d->pageStep;
+ int currentOffset = qRound(qreal(e->delta()) * step / 120);
+ if (currentOffset == 0)
+ currentOffset = (e->delta() < 0 ? -1 : 1);
+ offset += currentOffset;
+
+ if (d->invertedControls)
+ offset = -offset;
+
+ int prevValue = d->value;
+ d->position = d->overflowSafeAdd(int(offset)); // value will be updated by triggerAction()
+
+ triggerAction(SliderMove);
+ if (prevValue == d->value) {
+ offset = 0;
+ } else {
+ offset -= int(offset);
+ e->accept();
+ }
+}
+#endif
+/*!
+ \reimp
+*/
+void QAbstractSlider::keyPressEvent(QKeyEvent *ev)
+{
+ Q_D(QAbstractSlider);
+ SliderAction action = SliderNoAction;
+ switch (ev->key()) {
+#ifdef QT_KEYPAD_NAVIGATION
+ case Qt::Key_Select:
+ if (QApplication::keypadNavigationEnabled())
+ setEditFocus(!hasEditFocus());
+ else
+ ev->ignore();
+ break;
+ case Qt::Key_Back:
+ if (QApplication::keypadNavigationEnabled() && hasEditFocus()) {
+ setValue(d->origValue);
+ setEditFocus(false);
+ } else
+ ev->ignore();
+ break;
+#endif
+
+ // It seems we need to use invertedAppearance for Left and right, otherwise, things look weird.
+ case Qt::Key_Left:
+#ifdef QT_KEYPAD_NAVIGATION
+ if (QApplication::keypadNavigationEnabled() && !hasEditFocus()) {
+ ev->ignore();
+ return;
+ }
+ if (QApplication::keypadNavigationEnabled() && d->orientation == Qt::Vertical)
+ action = d->invertedControls ? SliderSingleStepSub : SliderSingleStepAdd;
+ else
+#endif
+ if (isRightToLeft())
+ action = d->invertedAppearance ? SliderSingleStepSub : SliderSingleStepAdd;
+ else
+ action = !d->invertedAppearance ? SliderSingleStepSub : SliderSingleStepAdd;
+ break;
+ case Qt::Key_Right:
+#ifdef QT_KEYPAD_NAVIGATION
+ if (QApplication::keypadNavigationEnabled() && !hasEditFocus()) {
+ ev->ignore();
+ return;
+ }
+ if (QApplication::keypadNavigationEnabled() && d->orientation == Qt::Vertical)
+ action = d->invertedControls ? SliderSingleStepAdd : SliderSingleStepSub;
+ else
+#endif
+ if (isRightToLeft())
+ action = d->invertedAppearance ? SliderSingleStepAdd : SliderSingleStepSub;
+ else
+ action = !d->invertedAppearance ? SliderSingleStepAdd : SliderSingleStepSub;
+ break;
+ case Qt::Key_Up:
+#ifdef QT_KEYPAD_NAVIGATION
+ if (QApplication::keypadNavigationEnabled()) {
+ ev->ignore();
+ break;
+ }
+#endif
+ action = d->invertedControls ? SliderSingleStepSub : SliderSingleStepAdd;
+ break;
+ case Qt::Key_Down:
+#ifdef QT_KEYPAD_NAVIGATION
+ if (QApplication::keypadNavigationEnabled()) {
+ ev->ignore();
+ break;
+ }
+#endif
+ action = d->invertedControls ? SliderSingleStepAdd : SliderSingleStepSub;
+ break;
+ case Qt::Key_PageUp:
+ action = d->invertedControls ? SliderPageStepSub : SliderPageStepAdd;
+ break;
+ case Qt::Key_PageDown:
+ action = d->invertedControls ? SliderPageStepAdd : SliderPageStepSub;
+ break;
+ case Qt::Key_Home:
+ action = SliderToMinimum;
+ break;
+ case Qt::Key_End:
+ action = SliderToMaximum;
+ break;
+ default:
+ ev->ignore();
+ break;
+ }
+ if (action)
+ triggerAction(action);
+}
+
+/*!
+ \reimp
+*/
+void QAbstractSlider::changeEvent(QEvent *ev)
+{
+ Q_D(QAbstractSlider);
+ switch (ev->type()) {
+ case QEvent::EnabledChange:
+ if (!isEnabled()) {
+ d->repeatActionTimer.stop();
+ setSliderDown(false);
+ }
+ // fall through...
+ default:
+ QWidget::changeEvent(ev);
+ }
+}
+
+/*!
+ \reimp
+*/
+bool QAbstractSlider::event(QEvent *e)
+{
+#ifdef QT_KEYPAD_NAVIGATION
+ Q_D(QAbstractSlider);
+ switch (e->type()) {
+ case QEvent::FocusIn:
+ d->origValue = d->value;
+ break;
+ default:
+ break;
+ }
+#endif
+
+ return QWidget::event(e);
+}
+
+/*! \fn int QAbstractSlider::minValue() const
+
+ Use minimum() instead.
+*/
+
+/*! \fn int QAbstractSlider::maxValue() const
+
+ Use maximum() instead.
+*/
+
+/*! \fn int QAbstractSlider::lineStep() const
+
+ Use singleStep() instead.
+*/
+
+/*! \fn void QAbstractSlider::setMinValue(int v)
+
+ Use setMinimum() instead.
+*/
+
+/*! \fn void QAbstractSlider::setMaxValue(int v)
+
+ Use setMaximum() instead.
+*/
+
+/*! \fn void QAbstractSlider::setLineStep(int v)
+
+ Use setSingleStep() instead.
+*/
+
+/*! \fn void QAbstractSlider::addPage()
+
+ Use triggerAction(QAbstractSlider::SliderPageStepAdd) instead.
+*/
+
+/*! \fn void QAbstractSlider::subtractPage()
+
+ Use triggerAction(QAbstractSlider::SliderPageStepSub) instead.
+*/
+
+/*! \fn void QAbstractSlider::addLine()
+
+ Use triggerAction(QAbstractSlider::SliderSingleStepAdd) instead.
+*/
+
+/*! \fn void QAbstractSlider::subtractLine()
+
+ Use triggerAction(QAbstractSlider::SliderSingleStepSub) instead.
+*/
+
+/*! \fn void QAbstractSlider::setSteps(int single, int page)
+
+ Use setSingleStep(\a single) followed by setPageStep(\a page)
+ instead.
+*/
+
+QT_END_NAMESPACE
diff --git a/src/gui/widgets/qabstractslider.h b/src/gui/widgets/qabstractslider.h
new file mode 100644
index 0000000000..e94d047a83
--- /dev/null
+++ b/src/gui/widgets/qabstractslider.h
@@ -0,0 +1,184 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QABSTRACTSLIDER_H
+#define QABSTRACTSLIDER_H
+
+#include <QtGui/qwidget.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+class QAbstractSliderPrivate;
+
+class Q_GUI_EXPORT QAbstractSlider : public QWidget
+{
+ Q_OBJECT
+
+ Q_PROPERTY(int minimum READ minimum WRITE setMinimum)
+ Q_PROPERTY(int maximum READ maximum WRITE setMaximum)
+ Q_PROPERTY(int singleStep READ singleStep WRITE setSingleStep)
+ Q_PROPERTY(int pageStep READ pageStep WRITE setPageStep)
+ Q_PROPERTY(int value READ value WRITE setValue NOTIFY valueChanged USER true)
+ Q_PROPERTY(int sliderPosition READ sliderPosition WRITE setSliderPosition NOTIFY sliderMoved)
+ Q_PROPERTY(bool tracking READ hasTracking WRITE setTracking)
+ Q_PROPERTY(Qt::Orientation orientation READ orientation WRITE setOrientation)
+ Q_PROPERTY(bool invertedAppearance READ invertedAppearance WRITE setInvertedAppearance)
+ Q_PROPERTY(bool invertedControls READ invertedControls WRITE setInvertedControls)
+ Q_PROPERTY(bool sliderDown READ isSliderDown WRITE setSliderDown DESIGNABLE false)
+
+public:
+ explicit QAbstractSlider(QWidget *parent=0);
+ ~QAbstractSlider();
+
+ Qt::Orientation orientation() const;
+
+ void setMinimum(int);
+ int minimum() const;
+
+ void setMaximum(int);
+ int maximum() const;
+
+ void setRange(int min, int max);
+
+ void setSingleStep(int);
+ int singleStep() const;
+
+ void setPageStep(int);
+ int pageStep() const;
+
+ void setTracking(bool enable);
+ bool hasTracking() const;
+
+ void setSliderDown(bool);
+ bool isSliderDown() const;
+
+ void setSliderPosition(int);
+ int sliderPosition() const;
+
+ void setInvertedAppearance(bool);
+ bool invertedAppearance() const;
+
+ void setInvertedControls(bool);
+ bool invertedControls() const;
+
+ enum SliderAction {
+ SliderNoAction,
+ SliderSingleStepAdd,
+ SliderSingleStepSub,
+ SliderPageStepAdd,
+ SliderPageStepSub,
+ SliderToMinimum,
+ SliderToMaximum,
+ SliderMove
+ };
+
+ int value() const;
+
+ void triggerAction(SliderAction action);
+
+public Q_SLOTS:
+ void setValue(int);
+ void setOrientation(Qt::Orientation);
+
+Q_SIGNALS:
+ void valueChanged(int value);
+
+ void sliderPressed();
+ void sliderMoved(int position);
+ void sliderReleased();
+
+ void rangeChanged(int min, int max);
+
+ void actionTriggered(int action);
+
+protected:
+ bool event(QEvent *e);
+
+ void setRepeatAction(SliderAction action, int thresholdTime = 500, int repeatTime = 50);
+ SliderAction repeatAction() const;
+
+ enum SliderChange {
+ SliderRangeChange,
+ SliderOrientationChange,
+ SliderStepsChange,
+ SliderValueChange
+ };
+ virtual void sliderChange(SliderChange change);
+
+ void keyPressEvent(QKeyEvent *ev);
+ void timerEvent(QTimerEvent *);
+#ifndef QT_NO_WHEELEVENT
+ void wheelEvent(QWheelEvent *e);
+#endif
+ void changeEvent(QEvent *e);
+
+#ifdef QT3_SUPPORT
+public:
+ inline QT3_SUPPORT int minValue() const { return minimum(); }
+ inline QT3_SUPPORT int maxValue() const { return maximum(); }
+ inline QT3_SUPPORT int lineStep() const { return singleStep(); }
+ inline QT3_SUPPORT void setMinValue(int v) { setMinimum(v); }
+ inline QT3_SUPPORT void setMaxValue(int v) { setMaximum(v); }
+ inline QT3_SUPPORT void setLineStep(int v) { setSingleStep(v); }
+ inline QT3_SUPPORT void setSteps(int single, int page) { setSingleStep(single); setPageStep(page); }
+ inline QT3_SUPPORT void addPage() { triggerAction(SliderPageStepAdd); }
+ inline QT3_SUPPORT void subtractPage() { triggerAction(SliderPageStepSub); }
+ inline QT3_SUPPORT void addLine() { triggerAction(SliderSingleStepAdd); }
+ inline QT3_SUPPORT void subtractLine() { triggerAction(SliderSingleStepSub); }
+#endif
+
+protected:
+ QAbstractSlider(QAbstractSliderPrivate &dd, QWidget *parent=0);
+
+private:
+ Q_DISABLE_COPY(QAbstractSlider)
+ Q_DECLARE_PRIVATE(QAbstractSlider)
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QABSTRACTSLIDER_H
diff --git a/src/gui/widgets/qabstractslider_p.h b/src/gui/widgets/qabstractslider_p.h
new file mode 100644
index 0000000000..6438d30439
--- /dev/null
+++ b/src/gui/widgets/qabstractslider_p.h
@@ -0,0 +1,113 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QABSTRACTSLIDER_P_H
+#define QABSTRACTSLIDER_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 "QtCore/qbasictimer.h"
+#include "private/qwidget_p.h"
+#include "qstyle.h"
+
+QT_BEGIN_NAMESPACE
+
+class QAbstractSliderPrivate : public QWidgetPrivate
+{
+ Q_DECLARE_PUBLIC(QAbstractSlider)
+public:
+ QAbstractSliderPrivate();
+ ~QAbstractSliderPrivate();
+
+ void setSteps(int single, int page);
+
+ int minimum, maximum, singleStep, pageStep, value, position, pressValue;
+ uint tracking : 1;
+ uint blocktracking :1;
+ uint pressed : 1;
+ uint invertedAppearance : 1;
+ uint invertedControls : 1;
+ Qt::Orientation orientation;
+
+ QBasicTimer repeatActionTimer;
+ int repeatActionTime;
+ QAbstractSlider::SliderAction repeatAction;
+
+#ifdef QT_KEYPAD_NAVIGATION
+ int origValue;
+#endif
+
+ inline int bound(int val) const { return qMax(minimum, qMin(maximum, val)); }
+ inline int overflowSafeAdd(int add) const
+ {
+ int newValue = value + add;
+ if (add > 0 && newValue < value)
+ newValue = maximum;
+ else if (add < 0 && newValue > value)
+ newValue = minimum;
+ return newValue;
+ }
+ inline void setAdjustedSliderPosition(int position)
+ {
+ Q_Q(QAbstractSlider);
+ if (q->style()->styleHint(QStyle::SH_Slider_StopMouseOverSlider, 0, q)) {
+ if ((position > pressValue - 2 * pageStep) && (position < pressValue + 2 * pageStep)) {
+ repeatAction = QAbstractSlider::SliderNoAction;
+ q->setSliderPosition(pressValue);
+ return;
+ }
+ }
+ q->triggerAction(repeatAction);
+ }
+};
+
+QT_END_NAMESPACE
+
+#endif // QABSTRACTSLIDER_P_H
diff --git a/src/gui/widgets/qabstractspinbox.cpp b/src/gui/widgets/qabstractspinbox.cpp
new file mode 100644
index 0000000000..347f89a145
--- /dev/null
+++ b/src/gui/widgets/qabstractspinbox.cpp
@@ -0,0 +1,2049 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <qplatformdefs.h>
+#include <private/qabstractspinbox_p.h>
+#include <private/qdatetime_p.h>
+#include <private/qlineedit_p.h>
+#include <qabstractspinbox.h>
+
+#ifndef QT_NO_SPINBOX
+
+#include <qapplication.h>
+#include <qclipboard.h>
+#include <qdatetime.h>
+#include <qdatetimeedit.h>
+#include <qevent.h>
+#include <qmenu.h>
+#include <qpainter.h>
+#include <qpalette.h>
+#include <qstylepainter.h>
+#include <qdebug.h>
+
+#if defined(Q_WS_X11)
+#include <limits.h>
+#endif
+
+//#define QABSTRACTSPINBOX_QSBDEBUG
+#ifdef QABSTRACTSPINBOX_QSBDEBUG
+# define QASBDEBUG qDebug
+#else
+# define QASBDEBUG if (false) qDebug
+#endif
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QAbstractSpinBox
+ \brief The QAbstractSpinBox class provides a spinbox and a line edit to
+ display values.
+
+ \ingroup abstractwidgets
+
+ The class is designed as a common super class for widgets like
+ QSpinBox, QDoubleSpinBox and QDateTimeEdit
+
+ Here are the main properties of the class:
+
+ \list 1
+
+ \i \l text: The text that is displayed in the QAbstractSpinBox.
+
+ \i \l alignment: The alignment of the text in the QAbstractSpinBox.
+
+ \i \l wrapping: Whether the QAbstractSpinBox wraps from the
+ minimum value to the maximum value and vica versa.
+
+ \endlist
+
+ QAbstractSpinBox provides a virtual stepBy() function that is
+ called whenever the user triggers a step. This function takes an
+ integer value to signify how many steps were taken. E.g. Pressing
+ Qt::Key_Down will trigger a call to stepBy(-1).
+
+ QAbstractSpinBox also provide a virtual function stepEnabled() to
+ determine whether stepping up/down is allowed at any point. This
+ function returns a bitset of StepEnabled.
+
+ \sa QAbstractSlider, QSpinBox, QDoubleSpinBox, QDateTimeEdit,
+ {Spin Boxes Example}
+*/
+
+/*!
+ \enum QAbstractSpinBox::StepEnabledFlag
+
+ \value StepNone
+ \value StepUpEnabled
+ \value StepDownEnabled
+*/
+
+/*!
+ \fn void QAbstractSpinBox::editingFinished()
+
+ This signal is emitted editing is finished. This happens when the
+ spinbox loses focus and when enter is pressed.
+*/
+
+/*!
+ Constructs an abstract spinbox with the given \a parent with default
+ \l wrapping, and \l alignment properties.
+*/
+
+QAbstractSpinBox::QAbstractSpinBox(QWidget *parent)
+ : QWidget(*new QAbstractSpinBoxPrivate, parent, 0)
+{
+ Q_D(QAbstractSpinBox);
+ d->init();
+}
+
+/*!
+ \internal
+*/
+QAbstractSpinBox::QAbstractSpinBox(QAbstractSpinBoxPrivate &dd, QWidget *parent)
+ : QWidget(dd, parent, 0)
+{
+ Q_D(QAbstractSpinBox);
+ d->init();
+}
+
+/*!
+ Called when the QAbstractSpinBox is destroyed.
+*/
+
+QAbstractSpinBox::~QAbstractSpinBox()
+{
+}
+
+/*!
+ \enum QAbstractSpinBox::ButtonSymbols
+
+ This enum type describes the symbols that can be displayed on the buttons
+ in a spin box.
+
+ \inlineimage qspinbox-updown.png
+ \inlineimage qspinbox-plusminus.png
+
+ \value UpDownArrows Little arrows in the classic style.
+ \value PlusMinus \bold{+} and \bold{-} symbols.
+ \value NoButtons Don't display buttons.
+
+ \sa QAbstractSpinBox::buttonSymbols
+*/
+
+/*!
+ \property QAbstractSpinBox::buttonSymbols
+
+ \brief the current button symbol mode
+
+ The possible values can be either \c UpDownArrows or \c PlusMinus.
+ The default is \c UpDownArrows.
+
+ Note that some styles might render PlusMinus and UpDownArrows
+ identically.
+
+ \sa ButtonSymbols
+*/
+
+QAbstractSpinBox::ButtonSymbols QAbstractSpinBox::buttonSymbols() const
+{
+ Q_D(const QAbstractSpinBox);
+ return d->buttonSymbols;
+}
+
+void QAbstractSpinBox::setButtonSymbols(ButtonSymbols buttonSymbols)
+{
+ Q_D(QAbstractSpinBox);
+ if (d->buttonSymbols != buttonSymbols) {
+ d->buttonSymbols = buttonSymbols;
+ update();
+ }
+}
+
+/*!
+ \property QAbstractSpinBox::text
+
+ \brief the spin box's text, including any prefix and suffix
+
+ There is no default text.
+*/
+
+QString QAbstractSpinBox::text() const
+{
+ return lineEdit()->displayText();
+}
+
+
+/*!
+ \property QAbstractSpinBox::specialValueText
+ \brief the special-value text
+
+ If set, the spin box will display this text instead of a numeric
+ value whenever the current value is equal to minimum(). Typical use
+ is to indicate that this choice has a special (default) meaning.
+
+ For example, if your spin box allows the user to choose a scale factor
+ (or zoom level) for displaying an image, and your application is able
+ to automatically choose one that will enable the image to fit completely
+ within the display window, you can set up the spin box like this:
+
+ \snippet examples/widgets/spinboxes/window.cpp 3
+
+ The user will then be able to choose a scale from 1% to 1000%
+ or select "Auto" to leave it up to the application to choose. Your code
+ must then interpret the spin box value of 0 as a request from the user
+ to scale the image to fit inside the window.
+
+ All values are displayed with the prefix and suffix (if set), \e
+ except for the special value, which only shows the special value
+ text. This special text is passed in the QSpinBox::valueChanged()
+ signal that passes a QString.
+
+ To turn off the special-value text display, call this function
+ with an empty string. The default is no special-value text, i.e.
+ the numeric value is shown as usual.
+
+ If no special-value text is set, specialValueText() returns an
+ empty string.
+*/
+
+QString QAbstractSpinBox::specialValueText() const
+{
+ Q_D(const QAbstractSpinBox);
+ return d->specialValueText;
+}
+
+void QAbstractSpinBox::setSpecialValueText(const QString &specialValueText)
+{
+ Q_D(QAbstractSpinBox);
+
+ d->specialValueText = specialValueText;
+ d->cachedSizeHint = QSize(); // minimumSizeHint doesn't care about specialValueText
+ d->clearCache();
+ d->updateEdit();
+}
+
+/*!
+ \property QAbstractSpinBox::wrapping
+
+ \brief whether the spin box is circular.
+
+ If wrapping is true stepping up from maximum() value will take you
+ to the minimum() value and vica versa. Wrapping only make sense if
+ you have minimum() and maximum() values set.
+
+ \snippet doc/src/snippets/code/src_gui_widgets_qabstractspinbox.cpp 0
+
+ \sa QSpinBox::minimum(), QSpinBox::maximum()
+*/
+
+bool QAbstractSpinBox::wrapping() const
+{
+ Q_D(const QAbstractSpinBox);
+ return d->wrapping;
+}
+
+void QAbstractSpinBox::setWrapping(bool wrapping)
+{
+ Q_D(QAbstractSpinBox);
+ d->wrapping = wrapping;
+}
+
+
+/*!
+ \property QAbstractSpinBox::readOnly
+ \brief whether the spin box is read only.
+
+ In read-only mode, the user can still copy the text to the
+ clipboard, or drag and drop the text;
+ but cannot edit it.
+
+ The QLineEdit in the QAbstractSpinBox does not show a cursor in
+ read-only mode.
+
+ \sa QLineEdit::readOnly
+*/
+
+bool QAbstractSpinBox::isReadOnly() const
+{
+ Q_D(const QAbstractSpinBox);
+ return d->readOnly;
+}
+
+void QAbstractSpinBox::setReadOnly(bool enable)
+{
+ Q_D(QAbstractSpinBox);
+ d->readOnly = enable;
+ d->edit->setReadOnly(enable);
+ update();
+}
+
+/*!
+ \property QAbstractSpinBox::keyboardTracking
+ \brief whether keyboard tracking is enabled for the spinbox.
+ \since 4.3
+
+ If keyboard tracking is enabled (the default), the spinbox
+ emits the valueChanged() signal while the new value is being
+ entered from the keyboard.
+
+ E.g. when the user enters the value 600 by typing 6, 0, and 0,
+ the spinbox emits 3 signals with the values 6, 60, and 600
+ respectively.
+
+ If keyboard tracking is disabled, the spinbox doesn't emit the
+ valueChanged() signal while typing. It emits the signal later,
+ when the return key is pressed, when keyboard focus is lost, or
+ when other spinbox functionality is used, e.g. pressing an arrow
+ key.
+*/
+
+bool QAbstractSpinBox::keyboardTracking() const
+{
+ Q_D(const QAbstractSpinBox);
+ return d->keyboardTracking;
+}
+
+void QAbstractSpinBox::setKeyboardTracking(bool enable)
+{
+ Q_D(QAbstractSpinBox);
+ d->keyboardTracking = enable;
+}
+
+/*!
+ \property QAbstractSpinBox::frame
+ \brief whether the spin box draws itself with a frame
+
+ If enabled (the default) the spin box draws itself inside a frame,
+ otherwise the spin box draws itself without any frame.
+*/
+
+bool QAbstractSpinBox::hasFrame() const
+{
+ Q_D(const QAbstractSpinBox);
+ return d->frame;
+}
+
+
+void QAbstractSpinBox::setFrame(bool enable)
+{
+ Q_D(QAbstractSpinBox);
+ d->frame = enable;
+ update();
+ d->updateEditFieldGeometry();
+}
+
+/*!
+ \property QAbstractSpinBox::accelerated
+ \brief whether the spin box will accelerate the frequency of the steps when
+ pressing the step Up/Down buttons.
+ \since 4.2
+
+ If enabled the spin box will increase/decrease the value faster
+ the longer you hold the button down.
+*/
+
+void QAbstractSpinBox::setAccelerated(bool accelerate)
+{
+ Q_D(QAbstractSpinBox);
+ d->accelerate = accelerate;
+
+}
+bool QAbstractSpinBox::isAccelerated() const
+{
+ Q_D(const QAbstractSpinBox);
+ return d->accelerate;
+}
+
+/*!
+ \enum QAbstractSpinBox::CorrectionMode
+
+ This enum type describes the mode the spinbox will use to correct
+ an \l{QValidator::}{Intermediate} value if editing finishes.
+
+ \value CorrectToPreviousValue The spinbox will revert to the last
+ valid value.
+
+ \value CorrectToNearestValue The spinbox will revert to the nearest
+ valid value.
+
+ \sa correctionMode
+*/
+
+/*!
+ \property QAbstractSpinBox::correctionMode
+ \brief the mode to correct an \l{QValidator::}{Intermediate}
+ value if editing finishes
+ \since 4.2
+
+ The default mode is QAbstractSpinBox::CorrectToPreviousValue.
+
+ \sa acceptableInput, validate(), fixup()
+*/
+void QAbstractSpinBox::setCorrectionMode(CorrectionMode correctionMode)
+{
+ Q_D(QAbstractSpinBox);
+ d->correctionMode = correctionMode;
+
+}
+QAbstractSpinBox::CorrectionMode QAbstractSpinBox::correctionMode() const
+{
+ Q_D(const QAbstractSpinBox);
+ return d->correctionMode;
+}
+
+
+/*!
+ \property QAbstractSpinBox::acceptableInput
+ \brief whether the input satisfies the current validation
+ \since 4.2
+
+ \sa validate(), fixup(), correctionMode
+*/
+
+bool QAbstractSpinBox::hasAcceptableInput() const
+{
+ Q_D(const QAbstractSpinBox);
+ return d->edit->hasAcceptableInput();
+}
+
+/*!
+ \property QAbstractSpinBox::alignment
+ \brief the alignment of the spin box
+
+ Possible Values are Qt::AlignLeft, Qt::AlignRight, and Qt::AlignHCenter.
+
+ By default, the alignment is Qt::AlignLeft
+
+ Attempting to set the alignment to an illegal flag combination
+ does nothing.
+
+ \sa Qt::Alignment
+*/
+
+Qt::Alignment QAbstractSpinBox::alignment() const
+{
+ Q_D(const QAbstractSpinBox);
+
+ return (Qt::Alignment)d->edit->alignment();
+}
+
+void QAbstractSpinBox::setAlignment(Qt::Alignment flag)
+{
+ Q_D(QAbstractSpinBox);
+
+ d->edit->setAlignment(flag);
+}
+
+/*!
+ Selects all the text in the spinbox except the prefix and suffix.
+*/
+
+void QAbstractSpinBox::selectAll()
+{
+ Q_D(QAbstractSpinBox);
+
+
+ if (!d->specialValue()) {
+ const int tmp = d->edit->displayText().size() - d->suffix.size();
+ d->edit->setSelection(tmp, -(tmp - d->prefix.size()));
+ } else {
+ d->edit->selectAll();
+ }
+}
+
+/*!
+ Clears the lineedit of all text but prefix and suffix.
+*/
+
+void QAbstractSpinBox::clear()
+{
+ Q_D(QAbstractSpinBox);
+
+ d->edit->setText(d->prefix + d->suffix);
+ d->edit->setCursorPosition(d->prefix.size());
+ d->cleared = true;
+}
+
+/*!
+ Virtual function that determines whether stepping up and down is
+ legal at any given time.
+
+ The up arrow will be painted as disabled unless (stepEnabled() &
+ StepUpEnabled) != 0.
+
+ The default implementation will return (StepUpEnabled|
+ StepDownEnabled) if wrapping is turned on. Else it will return
+ StepDownEnabled if value is > minimum() or'ed with StepUpEnabled if
+ value < maximum().
+
+ If you subclass QAbstractSpinBox you will need to reimplement this function.
+
+ \sa QSpinBox::minimum(), QSpinBox::maximum(), wrapping()
+*/
+
+
+QAbstractSpinBox::StepEnabled QAbstractSpinBox::stepEnabled() const
+{
+ Q_D(const QAbstractSpinBox);
+ if (d->readOnly || d->type == QVariant::Invalid)
+ return StepNone;
+ if (d->wrapping)
+ return StepEnabled(StepUpEnabled | StepDownEnabled);
+ StepEnabled ret = StepNone;
+ if (d->variantCompare(d->value, d->maximum) < 0) {
+ ret |= StepUpEnabled;
+ }
+ if (d->variantCompare(d->value, d->minimum) > 0) {
+ ret |= StepDownEnabled;
+ }
+ return ret;
+}
+
+/*!
+ This virtual function is called by the QAbstractSpinBox to
+ determine whether \a input is valid. The \a pos parameter indicates
+ the position in the string. Reimplemented in the various
+ subclasses.
+*/
+
+QValidator::State QAbstractSpinBox::validate(QString & /* input */, int & /* pos */) const
+{
+ return QValidator::Acceptable;
+}
+
+/*!
+ This virtual function is called by the QAbstractSpinBox if the
+ \a input is not validated to QValidator::Acceptable when Return is
+ pressed or interpretText() is called. It will try to change the
+ text so it is valid. Reimplemented in the various subclasses.
+*/
+
+void QAbstractSpinBox::fixup(QString & /* input */) const
+{
+}
+
+/*!
+ Steps up by one linestep
+ Calling this slot is analogous to calling stepBy(1);
+ \sa stepBy(), stepDown()
+*/
+
+void QAbstractSpinBox::stepUp()
+{
+ stepBy(1);
+}
+
+/*!
+ Steps down by one linestep
+ Calling this slot is analogous to calling stepBy(-1);
+ \sa stepBy(), stepUp()
+*/
+
+void QAbstractSpinBox::stepDown()
+{
+ stepBy(-1);
+}
+/*!
+ Virtual function that is called whenever the user triggers a step.
+ The \a steps parameter indicates how many steps were taken, e.g.
+ Pressing Qt::Key_Down will trigger a call to stepBy(-1),
+ whereas pressing Qt::Key_Prior will trigger a call to
+ stepBy(10).
+
+ If you subclass QAbstractSpinBox you must reimplement this
+ function. Note that this function is called even if the resulting
+ value will be outside the bounds of minimum and maximum. It's this
+ function's job to handle these situations.
+*/
+
+void QAbstractSpinBox::stepBy(int steps)
+{
+ Q_D(QAbstractSpinBox);
+
+ const QVariant old = d->value;
+ QString tmp = d->edit->displayText();
+ int cursorPos = d->edit->cursorPosition();
+ bool dontstep = false;
+ EmitPolicy e = EmitIfChanged;
+ if (d->pendingEmit) {
+ dontstep = validate(tmp, cursorPos) != QValidator::Acceptable;
+ d->cleared = false;
+ d->interpret(NeverEmit);
+ if (d->value != old)
+ e = AlwaysEmit;
+ }
+ if (!dontstep) {
+ d->setValue(d->bound(d->value + (d->singleStep * steps), old, steps), e);
+ } else if (e == AlwaysEmit) {
+ d->emitSignals(e, old);
+ }
+ selectAll();
+}
+
+/*!
+ This function returns a pointer to the line edit of the spin box.
+*/
+
+QLineEdit *QAbstractSpinBox::lineEdit() const
+{
+ Q_D(const QAbstractSpinBox);
+
+ return d->edit;
+}
+
+
+/*!
+ \fn void QAbstractSpinBox::setLineEdit(QLineEdit *lineEdit)
+
+ Sets the line edit of the spinbox to be \a lineEdit instead of the
+ current line edit widget. \a lineEdit can not be 0.
+
+ QAbstractSpinBox takes ownership of the new lineEdit
+
+ If QLineEdit::validator() for the \a lineEdit returns 0, the internal
+ validator of the spinbox will be set on the line edit.
+*/
+
+void QAbstractSpinBox::setLineEdit(QLineEdit *lineEdit)
+{
+ Q_D(QAbstractSpinBox);
+
+ if (!lineEdit) {
+ Q_ASSERT(lineEdit);
+ return;
+ }
+ delete d->edit;
+ d->edit = lineEdit;
+ if (!d->edit->validator())
+ d->edit->setValidator(d->validator);
+
+ if (d->edit->parent() != this)
+ d->edit->setParent(this);
+
+ d->edit->setFrame(false);
+ d->edit->setAttribute(Qt::WA_InputMethodEnabled, false);
+ d->edit->setFocusProxy(this);
+ d->edit->setAcceptDrops(false);
+
+ if (d->type != QVariant::Invalid) {
+ connect(d->edit, SIGNAL(textChanged(QString)),
+ this, SLOT(_q_editorTextChanged(QString)));
+ connect(d->edit, SIGNAL(cursorPositionChanged(int,int)),
+ this, SLOT(_q_editorCursorPositionChanged(int,int)));
+ }
+ d->updateEditFieldGeometry();
+ d->edit->setContextMenuPolicy(Qt::NoContextMenu);
+
+ if (isVisible())
+ d->edit->show();
+ if (isVisible())
+ d->updateEdit();
+}
+
+
+/*!
+ This function interprets the text of the spin box. If the value
+ has changed since last interpretation it will emit signals.
+*/
+
+void QAbstractSpinBox::interpretText()
+{
+ Q_D(QAbstractSpinBox);
+ d->interpret(EmitIfChanged);
+}
+
+/*!
+ \reimp
+*/
+
+bool QAbstractSpinBox::event(QEvent *event)
+{
+ Q_D(QAbstractSpinBox);
+ switch (event->type()) {
+ case QEvent::FontChange:
+ case QEvent::StyleChange:
+ d->cachedSizeHint = d->cachedMinimumSizeHint = QSize();
+ break;
+ case QEvent::ApplicationLayoutDirectionChange:
+ case QEvent::LayoutDirectionChange:
+ d->updateEditFieldGeometry();
+ break;
+ case QEvent::HoverEnter:
+ case QEvent::HoverLeave:
+ case QEvent::HoverMove:
+ if (const QHoverEvent *he = static_cast<const QHoverEvent *>(event))
+ d->updateHoverControl(he->pos());
+ break;
+ case QEvent::ShortcutOverride:
+ if (d->edit->event(event))
+ return true;
+ break;
+#ifdef QT_KEYPAD_NAVIGATION
+ case QEvent::EnterEditFocus:
+ case QEvent::LeaveEditFocus:
+ if (QApplication::keypadNavigationEnabled()) {
+ const bool b = d->edit->event(event);
+ d->edit->setSelection(d->edit->displayText().size() - d->suffix.size(),0);
+ if (event->type() == QEvent::LeaveEditFocus)
+ emit editingFinished();
+ if (b)
+ return true;
+ }
+ break;
+#endif
+ case QEvent::InputMethod:
+ return d->edit->event(event);
+ default:
+ break;
+ }
+ return QWidget::event(event);
+}
+
+/*!
+ \reimp
+*/
+
+void QAbstractSpinBox::showEvent(QShowEvent *)
+{
+ Q_D(QAbstractSpinBox);
+ d->reset();
+
+ if (d->ignoreUpdateEdit) {
+ d->ignoreUpdateEdit = false;
+ } else {
+ d->updateEdit();
+ }
+}
+
+/*!
+ \reimp
+*/
+
+void QAbstractSpinBox::changeEvent(QEvent *event)
+{
+ Q_D(QAbstractSpinBox);
+
+ switch (event->type()) {
+ case QEvent::StyleChange:
+ d->spinClickTimerInterval = style()->styleHint(QStyle::SH_SpinBox_ClickAutoRepeatRate, 0, this);
+ d->spinClickThresholdTimerInterval =
+ style()->styleHint(QStyle::SH_SpinBox_ClickAutoRepeatThreshold, 0, this);
+ d->reset();
+ d->updateEditFieldGeometry();
+ break;
+ case QEvent::EnabledChange:
+ if (!isEnabled()) {
+ d->reset();
+ }
+ break;
+ case QEvent::ActivationChange:
+ if (!isActiveWindow()){
+ d->reset();
+ if (d->pendingEmit) // pendingEmit can be true even if it hasn't changed.
+ d->interpret(EmitIfChanged); // E.g. 10 to 10.0
+ }
+ break;
+ default:
+ break;
+ }
+ QWidget::changeEvent(event);
+}
+
+/*!
+ \reimp
+*/
+
+void QAbstractSpinBox::resizeEvent(QResizeEvent *event)
+{
+ Q_D(QAbstractSpinBox);
+ QWidget::resizeEvent(event);
+
+ d->updateEditFieldGeometry();
+ update();
+}
+
+/*!
+ \reimp
+*/
+
+QSize QAbstractSpinBox::sizeHint() const
+{
+ Q_D(const QAbstractSpinBox);
+ if (d->cachedSizeHint.isEmpty()) {
+ ensurePolished();
+
+ const QFontMetrics fm(fontMetrics());
+ int h = d->edit->sizeHint().height();
+ int w = 0;
+ QString s;
+ s = d->prefix + d->textFromValue(d->minimum) + d->suffix + QLatin1Char(' ');
+ s.truncate(18);
+ w = qMax(w, fm.width(s));
+ s = d->prefix + d->textFromValue(d->maximum) + d->suffix + QLatin1Char(' ');
+ s.truncate(18);
+ w = qMax(w, fm.width(s));
+ if (d->specialValueText.size()) {
+ s = d->specialValueText;
+ w = qMax(w, fm.width(s));
+ }
+ w += 2; // cursor blinking space
+
+ QStyleOptionSpinBox opt;
+ initStyleOption(&opt);
+ QSize hint(w, h);
+ QSize extra(35, 6);
+ opt.rect.setSize(hint + extra);
+ extra += hint - style()->subControlRect(QStyle::CC_SpinBox, &opt,
+ QStyle::SC_SpinBoxEditField, this).size();
+ // get closer to final result by repeating the calculation
+ opt.rect.setSize(hint + extra);
+ extra += hint - style()->subControlRect(QStyle::CC_SpinBox, &opt,
+ QStyle::SC_SpinBoxEditField, this).size();
+ hint += extra;
+
+ opt.rect = rect();
+ d->cachedSizeHint = style()->sizeFromContents(QStyle::CT_SpinBox, &opt, hint, this)
+ .expandedTo(QApplication::globalStrut());
+ }
+ return d->cachedSizeHint;
+}
+
+/*!
+ \reimp
+*/
+
+QSize QAbstractSpinBox::minimumSizeHint() const
+{
+ Q_D(const QAbstractSpinBox);
+ if (d->cachedMinimumSizeHint.isEmpty()) {
+ ensurePolished();
+
+ const QFontMetrics fm(fontMetrics());
+ int h = d->edit->minimumSizeHint().height();
+ int w = fm.width(QLatin1String("1000"));
+ w += 2; // cursor blinking space
+
+ QStyleOptionSpinBox opt;
+ initStyleOption(&opt);
+ QSize hint(w, h);
+ QSize extra(35, 6);
+ opt.rect.setSize(hint + extra);
+ extra += hint - style()->subControlRect(QStyle::CC_SpinBox, &opt,
+ QStyle::SC_SpinBoxEditField, this).size();
+ // get closer to final result by repeating the calculation
+ opt.rect.setSize(hint + extra);
+ extra += hint - style()->subControlRect(QStyle::CC_SpinBox, &opt,
+ QStyle::SC_SpinBoxEditField, this).size();
+ hint += extra;
+
+ opt.rect = rect();
+
+ d->cachedMinimumSizeHint = style()->sizeFromContents(QStyle::CT_SpinBox, &opt, hint, this)
+ .expandedTo(QApplication::globalStrut());
+ }
+ return d->cachedMinimumSizeHint;
+}
+
+/*!
+ \reimp
+*/
+
+void QAbstractSpinBox::paintEvent(QPaintEvent *)
+{
+ QStyleOptionSpinBox opt;
+ initStyleOption(&opt);
+ QStylePainter p(this);
+ p.drawComplexControl(QStyle::CC_SpinBox, opt);
+}
+
+/*!
+ \reimp
+
+ This function handles keyboard input.
+
+ The following keys are handled specifically:
+ \table
+ \row \i Enter/Return
+ \i This will reinterpret the text and emit a signal even if the value has not changed
+ since last time a signal was emitted.
+ \row \i Up
+ \i This will invoke stepBy(1)
+ \row \i Down
+ \i This will invoke stepBy(-1)
+ \row \i Page up
+ \i This will invoke stepBy(10)
+ \row \i Page down
+ \i This will invoke stepBy(-10)
+ \endtable
+*/
+
+
+void QAbstractSpinBox::keyPressEvent(QKeyEvent *event)
+{
+ Q_D(QAbstractSpinBox);
+
+ if (!event->text().isEmpty() && d->edit->cursorPosition() < d->prefix.size())
+ d->edit->setCursorPosition(d->prefix.size());
+
+ int steps = 1;
+ switch (event->key()) {
+ case Qt::Key_PageUp:
+ case Qt::Key_PageDown:
+ steps *= 10;
+ case Qt::Key_Up:
+ case Qt::Key_Down: {
+#ifdef QT_KEYPAD_NAVIGATION
+ if (QApplication::keypadNavigationEnabled()) {
+ // Reserve up/down for nav - use left/right for edit.
+ if (!hasEditFocus() && (event->key() == Qt::Key_Up
+ || event->key() == Qt::Key_Down)) {
+ event->ignore();
+ return;
+ }
+ }
+#endif
+ event->accept();
+ const bool up = (event->key() == Qt::Key_PageUp || event->key() == Qt::Key_Up);
+ if (!(stepEnabled() & (up ? StepUpEnabled : StepDownEnabled)))
+ return;
+ if (!up)
+ steps *= -1;
+ if (style()->styleHint(QStyle::SH_SpinBox_AnimateButton, 0, this)) {
+ d->buttonState = (Keyboard | (up ? Up : Down));
+ }
+ stepBy(steps);
+ return;
+ }
+#ifdef QT_KEYPAD_NAVIGATION
+ case Qt::Key_Left:
+ case Qt::Key_Right:
+ if (QApplication::keypadNavigationEnabled() && !hasEditFocus()) {
+ event->ignore();
+ return;
+ }
+ break;
+ case Qt::Key_Back:
+ if (QApplication::keypadNavigationEnabled() && !hasEditFocus()) {
+ event->ignore();
+ return;
+ }
+ break;
+#endif
+ case Qt::Key_Enter:
+ case Qt::Key_Return:
+ d->edit->d_func()->modifiedState = d->edit->d_func()->undoState = 0;
+ d->interpret(d->keyboardTracking ? AlwaysEmit : EmitIfChanged);
+ selectAll();
+ event->ignore();
+ emit editingFinished();
+ return;
+
+#ifdef QT_KEYPAD_NAVIGATION
+ case Qt::Key_Select:
+ if (QApplication::keypadNavigationEnabled()) {
+ // Toggles between left/right moving cursor and inc/dec.
+ setEditFocus(!hasEditFocus());
+ }
+ return;
+#endif
+
+#ifdef Q_WS_X11 // only X11
+ case Qt::Key_U:
+ if (event->modifiers() & Qt::ControlModifier) {
+ event->accept();
+ if (!isReadOnly())
+ clear();
+ return;
+ }
+ break;
+#endif
+
+ case Qt::Key_End:
+ case Qt::Key_Home:
+ if (event->modifiers() & Qt::ShiftModifier) {
+ int currentPos = d->edit->cursorPosition();
+ const QString text = d->edit->displayText();
+ if (event->key() == Qt::Key_End) {
+ if ((currentPos == 0 && !d->prefix.isEmpty()) || text.size() - d->suffix.size() <= currentPos) {
+ break; // let lineedit handle this
+ } else {
+ d->edit->setSelection(currentPos, text.size() - d->suffix.size() - currentPos);
+ }
+ } else {
+ if ((currentPos == text.size() && !d->suffix.isEmpty()) || currentPos <= d->prefix.size()) {
+ break; // let lineedit handle this
+ } else {
+ d->edit->setSelection(currentPos, d->prefix.size() - currentPos);
+ }
+ }
+ event->accept();
+ return;
+ }
+ break;
+
+ default:
+#ifndef QT_NO_SHORTCUT
+ if (event == QKeySequence::SelectAll) {
+ selectAll();
+ event->accept();
+ return;
+ }
+#endif
+ break;
+ }
+
+ d->edit->event(event);
+ if (!isVisible())
+ d->ignoreUpdateEdit = true;
+}
+
+/*!
+ \reimp
+*/
+
+void QAbstractSpinBox::keyReleaseEvent(QKeyEvent *event)
+{
+ Q_D(QAbstractSpinBox);
+
+ if (d->buttonState & Keyboard && !event->isAutoRepeat()
+ && style()->styleHint(QStyle::SH_SpinBox_AnimateButton, 0, this)) {
+ d->reset();
+ } else {
+ d->edit->event(event);
+ }
+}
+
+/*!
+ \reimp
+*/
+
+#ifndef QT_NO_WHEELEVENT
+void QAbstractSpinBox::wheelEvent(QWheelEvent *event)
+{
+ const int steps = (event->delta() > 0 ? 1 : -1);
+ if (stepEnabled() & (steps > 0 ? StepUpEnabled : StepDownEnabled))
+ stepBy(event->modifiers() & Qt::ControlModifier ? steps * 10 : steps);
+ event->accept();
+}
+#endif
+
+
+/*!
+ \reimp
+*/
+void QAbstractSpinBox::focusInEvent(QFocusEvent *event)
+{
+ Q_D(QAbstractSpinBox);
+
+ d->edit->event(event);
+ if (event->reason() == Qt::TabFocusReason || event->reason() == Qt::BacktabFocusReason) {
+ selectAll();
+ }
+ QWidget::focusInEvent(event);
+}
+
+/*!
+ \reimp
+*/
+
+void QAbstractSpinBox::focusOutEvent(QFocusEvent *event)
+{
+ Q_D(QAbstractSpinBox);
+
+ if (d->pendingEmit)
+ d->interpret(EmitIfChanged);
+
+ d->reset();
+ d->edit->event(event);
+ d->updateEdit();
+ QWidget::focusOutEvent(event);
+
+#ifdef QT_KEYPAD_NAVIGATION
+ // editingFinished() is already emitted on LeaveEditFocus
+ if (!QApplication::keypadNavigationEnabled())
+#endif
+ emit editingFinished();
+}
+
+/*!
+ \reimp
+*/
+
+void QAbstractSpinBox::closeEvent(QCloseEvent *event)
+{
+ Q_D(QAbstractSpinBox);
+
+ d->reset();
+ if (d->pendingEmit)
+ d->interpret(EmitIfChanged);
+ QWidget::closeEvent(event);
+}
+
+/*!
+ \reimp
+*/
+
+void QAbstractSpinBox::hideEvent(QHideEvent *event)
+{
+ Q_D(QAbstractSpinBox);
+ d->reset();
+ if (d->pendingEmit)
+ d->interpret(EmitIfChanged);
+ QWidget::hideEvent(event);
+}
+
+/*!
+ \reimp
+*/
+
+void QAbstractSpinBox::timerEvent(QTimerEvent *event)
+{
+ Q_D(QAbstractSpinBox);
+
+ bool doStep = false;
+ if (event->timerId() == d->spinClickThresholdTimerId) {
+ killTimer(d->spinClickThresholdTimerId);
+ d->spinClickThresholdTimerId = -1;
+ d->spinClickTimerId = startTimer(d->spinClickTimerInterval);
+ doStep = true;
+ } else if (event->timerId() == d->spinClickTimerId) {
+ if (d->accelerate) {
+ d->acceleration = d->acceleration + (int)(d->spinClickTimerInterval * 0.05);
+ if (d->spinClickTimerInterval - d->acceleration >= 10) {
+ killTimer(d->spinClickTimerId);
+ d->spinClickTimerId = startTimer(d->spinClickTimerInterval - d->acceleration);
+ }
+ }
+ doStep = true;
+ }
+
+ if (doStep) {
+ const StepEnabled st = stepEnabled();
+ if (d->buttonState & Up) {
+ if (!(st & StepUpEnabled)) {
+ d->reset();
+ } else {
+ stepBy(1);
+ }
+ } else if (d->buttonState & Down) {
+ if (!(st & StepDownEnabled)) {
+ d->reset();
+ } else {
+ stepBy(-1);
+ }
+ }
+ return;
+ }
+ QWidget::timerEvent(event);
+ return;
+}
+
+/*!
+ \reimp
+*/
+
+void QAbstractSpinBox::contextMenuEvent(QContextMenuEvent *event)
+{
+#ifdef QT_NO_CONTEXTMENU
+ Q_UNUSED(event);
+#else
+ Q_D(QAbstractSpinBox);
+
+ d->reset();
+ QPointer<QMenu> menu = d->edit->createStandardContextMenu();
+
+ QAction *selAll = new QAction(tr("&Select All"), menu);
+ menu->insertAction(d->edit->d_func()->selectAllAction,
+ selAll);
+ menu->removeAction(d->edit->d_func()->selectAllAction);
+ menu->addSeparator();
+ const uint se = stepEnabled();
+ QAction *up = menu->addAction(tr("&Step up"));
+ up->setEnabled(se & StepUpEnabled);
+ QAction *down = menu->addAction(tr("Step &down"));
+ down->setEnabled(se & StepDownEnabled);
+ menu->addSeparator();
+
+ const QPointer<QAbstractSpinBox> that = this;
+ const QPoint pos = (event->reason() == QContextMenuEvent::Mouse)
+ ? event->globalPos() : mapToGlobal(QPoint(event->pos().x(), 0)) + QPoint(width() / 2, height() / 2);
+ const QAction *action = menu->exec(pos);
+ delete static_cast<QMenu *>(menu);
+ if (that && action) {
+ if (action == up) {
+ stepBy(1);
+ } else if (action == down) {
+ stepBy(-1);
+ } else if (action == selAll) {
+ selectAll();
+ }
+ }
+ event->accept();
+#endif // QT_NO_CONTEXTMENU
+}
+
+/*!
+ \reimp
+*/
+
+void QAbstractSpinBox::mouseMoveEvent(QMouseEvent *event)
+{
+ Q_D(QAbstractSpinBox);
+
+ d->updateHoverControl(event->pos());
+
+ // If we have a timer ID, update the state
+ if (d->spinClickTimerId != -1 && d->buttonSymbols != NoButtons) {
+ const StepEnabled se = stepEnabled();
+ if ((se & StepUpEnabled) && d->hoverControl == QStyle::SC_SpinBoxUp)
+ d->updateState(true);
+ else if ((se & StepDownEnabled) && d->hoverControl == QStyle::SC_SpinBoxDown)
+ d->updateState(false);
+ else
+ d->reset();
+ event->accept();
+ }
+}
+
+/*!
+ \reimp
+*/
+
+void QAbstractSpinBox::mousePressEvent(QMouseEvent *event)
+{
+ Q_D(QAbstractSpinBox);
+
+ if (event->button() != Qt::LeftButton || d->buttonState != None) {
+ return;
+ }
+
+ d->updateHoverControl(event->pos());
+ event->accept();
+
+ const StepEnabled se = (d->buttonSymbols == NoButtons) ? StepEnabled(StepNone) : stepEnabled();
+ if ((se & StepUpEnabled) && d->hoverControl == QStyle::SC_SpinBoxUp) {
+ d->updateState(true);
+ } else if ((se & StepDownEnabled) && d->hoverControl == QStyle::SC_SpinBoxDown) {
+ d->updateState(false);
+ } else {
+ event->ignore();
+ }
+}
+
+/*!
+ \reimp
+*/
+void QAbstractSpinBox::mouseReleaseEvent(QMouseEvent *event)
+{
+ Q_D(QAbstractSpinBox);
+
+ if ((d->buttonState & Mouse) != 0)
+ d->reset();
+ event->accept();
+}
+
+// --- QAbstractSpinBoxPrivate ---
+
+/*!
+ \internal
+ Constructs a QAbstractSpinBoxPrivate object
+*/
+
+QAbstractSpinBoxPrivate::QAbstractSpinBoxPrivate()
+ : edit(0), type(QVariant::Invalid), spinClickTimerId(-1),
+ spinClickTimerInterval(100), spinClickThresholdTimerId(-1), spinClickThresholdTimerInterval(-1),
+ buttonState(None), cachedText(QLatin1String("\x01")), cachedState(QValidator::Invalid),
+ pendingEmit(false), readOnly(false), wrapping(false),
+ ignoreCursorPositionChanged(false), frame(true), accelerate(false), keyboardTracking(true),
+ cleared(false), ignoreUpdateEdit(false), correctionMode(QAbstractSpinBox::CorrectToPreviousValue),
+ acceleration(0), hoverControl(QStyle::SC_None), buttonSymbols(QAbstractSpinBox::UpDownArrows), validator(0)
+{
+}
+
+/*
+ \internal
+ Called when the QAbstractSpinBoxPrivate is destroyed
+*/
+QAbstractSpinBoxPrivate::~QAbstractSpinBoxPrivate()
+{
+}
+
+/*!
+ \internal
+ Updates the old and new hover control. Does nothing if the hover
+ control has not changed.
+*/
+bool QAbstractSpinBoxPrivate::updateHoverControl(const QPoint &pos)
+{
+ Q_Q(QAbstractSpinBox);
+ QRect lastHoverRect = hoverRect;
+ QStyle::SubControl lastHoverControl = hoverControl;
+ bool doesHover = q->testAttribute(Qt::WA_Hover);
+ if (lastHoverControl != newHoverControl(pos) && doesHover) {
+ q->update(lastHoverRect);
+ q->update(hoverRect);
+ return true;
+ }
+ return !doesHover;
+}
+
+/*!
+ \internal
+ Returns the hover control at \a pos.
+ This will update the hoverRect and hoverControl.
+*/
+QStyle::SubControl QAbstractSpinBoxPrivate::newHoverControl(const QPoint &pos)
+{
+ Q_Q(QAbstractSpinBox);
+
+ QStyleOptionSpinBox opt;
+ q->initStyleOption(&opt);
+ opt.subControls = QStyle::SC_All;
+ hoverControl = q->style()->hitTestComplexControl(QStyle::CC_SpinBox, &opt, pos, q);
+ hoverRect = q->style()->subControlRect(QStyle::CC_SpinBox, &opt, hoverControl, q);
+ return hoverControl;
+}
+
+/*!
+ \internal
+ Strips any prefix/suffix from \a text.
+*/
+
+QString QAbstractSpinBoxPrivate::stripped(const QString &t, int *pos) const
+{
+ QString text = t;
+ if (specialValueText.size() == 0 || text != specialValueText) {
+ int from = 0;
+ int size = text.size();
+ bool changed = false;
+ if (prefix.size() && text.startsWith(prefix)) {
+ from += prefix.size();
+ size -= from;
+ changed = true;
+ }
+ if (suffix.size() && text.endsWith(suffix)) {
+ size -= suffix.size();
+ changed = true;
+ }
+ if (changed)
+ text = text.mid(from, size);
+ }
+
+ const int s = text.size();
+ text = text.trimmed();
+ if (pos)
+ (*pos) -= (s - text.size());
+ return text;
+
+}
+
+void QAbstractSpinBoxPrivate::updateEditFieldGeometry()
+{
+ Q_Q(QAbstractSpinBox);
+ QStyleOptionSpinBox opt;
+ q->initStyleOption(&opt);
+ opt.subControls = QStyle::SC_SpinBoxEditField;
+ edit->setGeometry(q->style()->subControlRect(QStyle::CC_SpinBox, &opt,
+ QStyle::SC_SpinBoxEditField, q));
+}
+/*!
+ \internal
+ Returns true if a specialValueText has been set and the current value is minimum.
+*/
+
+bool QAbstractSpinBoxPrivate::specialValue() const
+{
+ return (value == minimum && !specialValueText.isEmpty());
+}
+
+/*!
+ \internal Virtual function that emits signals when the value
+ changes. Reimplemented in the different subclasses.
+*/
+
+void QAbstractSpinBoxPrivate::emitSignals(EmitPolicy, const QVariant &)
+{
+}
+
+/*!
+ \internal
+
+ Slot connected to the line edit's textChanged(const QString &)
+ signal.
+*/
+
+void QAbstractSpinBoxPrivate::_q_editorTextChanged(const QString &t)
+{
+ Q_Q(QAbstractSpinBox);
+
+ if (keyboardTracking) {
+ QString tmp = t;
+ int pos = edit->cursorPosition();
+ QValidator::State state = q->validate(tmp, pos);
+ if (state == QValidator::Acceptable) {
+ const QVariant v = valueFromText(tmp);
+ setValue(v, EmitIfChanged, tmp != t);
+ pendingEmit = false;
+ } else {
+ pendingEmit = true;
+ }
+ } else {
+ pendingEmit = true;
+ }
+}
+
+/*!
+ \internal
+
+ Virtual slot connected to the line edit's
+ cursorPositionChanged(int, int) signal. Will move the cursor to a
+ valid position if the new one is invalid. E.g. inside the prefix.
+ Reimplemented in Q[Date|Time|DateTime]EditPrivate to account for
+ the different sections etc.
+*/
+
+void QAbstractSpinBoxPrivate::_q_editorCursorPositionChanged(int oldpos, int newpos)
+{
+ if (!edit->hasSelectedText() && !ignoreCursorPositionChanged && !specialValue()) {
+ ignoreCursorPositionChanged = true;
+
+ bool allowSelection = true;
+ int pos = -1;
+ if (newpos < prefix.size() && newpos != 0) {
+ if (oldpos == 0) {
+ allowSelection = false;
+ pos = prefix.size();
+ } else {
+ pos = oldpos;
+ }
+ } else if (newpos > edit->text().size() - suffix.size()
+ && newpos != edit->text().size()) {
+ if (oldpos == edit->text().size()) {
+ pos = edit->text().size() - suffix.size();
+ allowSelection = false;
+ } else {
+ pos = edit->text().size();
+ }
+ }
+ if (pos != -1) {
+ const int selSize = edit->selectionStart() >= 0 && allowSelection
+ ? (edit->selectedText().size()
+ * (newpos < pos ? -1 : 1)) - newpos + pos
+ : 0;
+
+ const bool wasBlocked = edit->blockSignals(true);
+ if (selSize != 0) {
+ edit->setSelection(pos - selSize, selSize);
+ } else {
+ edit->setCursorPosition(pos);
+ }
+ edit->blockSignals(wasBlocked);
+ }
+ ignoreCursorPositionChanged = false;
+ }
+}
+
+/*!
+ \internal
+
+ Initialises the QAbstractSpinBoxPrivate object.
+*/
+
+void QAbstractSpinBoxPrivate::init()
+{
+ Q_Q(QAbstractSpinBox);
+
+ q->setLineEdit(new QLineEdit(q));
+ edit->setObjectName(QLatin1String("qt_spinbox_lineedit"));
+ validator = new QSpinBoxValidator(q, this);
+ edit->setValidator(validator);
+
+ QStyleOptionSpinBox opt;
+ q->initStyleOption(&opt);
+ spinClickTimerInterval = q->style()->styleHint(QStyle::SH_SpinBox_ClickAutoRepeatRate, &opt, q);
+ spinClickThresholdTimerInterval = q->style()->styleHint(QStyle::SH_SpinBox_ClickAutoRepeatThreshold, &opt, q);
+ q->setFocusPolicy(Qt::WheelFocus);
+ q->setSizePolicy(QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed, QSizePolicy::SpinBox));
+ q->setAttribute(Qt::WA_InputMethodEnabled);
+
+ q->setAttribute(Qt::WA_MacShowFocusRect);
+}
+
+/*!
+ \internal
+
+ Resets the state of the spinbox. E.g. the state is set to
+ (Keyboard|Up) if Key up is currently pressed.
+*/
+
+void QAbstractSpinBoxPrivate::reset()
+{
+ Q_Q(QAbstractSpinBox);
+
+ buttonState = None;
+ if (q) {
+ if (spinClickTimerId != -1)
+ q->killTimer(spinClickTimerId);
+ if (spinClickThresholdTimerId != -1)
+ q->killTimer(spinClickThresholdTimerId);
+ spinClickTimerId = spinClickThresholdTimerId = -1;
+ acceleration = 0;
+ q->update();
+ }
+}
+
+/*!
+ \internal
+
+ Updates the state of the spinbox.
+*/
+
+void QAbstractSpinBoxPrivate::updateState(bool up)
+{
+ Q_Q(QAbstractSpinBox);
+ if ((up && (buttonState & Up)) || (!up && (buttonState & Down)))
+ return;
+ reset();
+ if (q && (q->stepEnabled() & (up ? QAbstractSpinBox::StepUpEnabled
+ : QAbstractSpinBox::StepDownEnabled))) {
+ spinClickThresholdTimerId = q->startTimer(spinClickThresholdTimerInterval);
+ buttonState = (up ? (Mouse | Up) : (Mouse | Down));
+ q->stepBy(up ? 1 : -1);
+ }
+}
+
+
+/*!
+ Initialize \a option with the values from this QSpinBox. This method
+ is useful for subclasses when they need a QStyleOptionSpinBox, but don't want
+ to fill in all the information themselves.
+
+ \sa QStyleOption::initFrom()
+*/
+void QAbstractSpinBox::initStyleOption(QStyleOptionSpinBox *option) const
+{
+ if (!option)
+ return;
+
+ Q_D(const QAbstractSpinBox);
+ option->initFrom(this);
+ option->activeSubControls = QStyle::SC_None;
+ option->buttonSymbols = d->buttonSymbols;
+ option->subControls = QStyle::SC_SpinBoxFrame;
+ if (d->buttonSymbols != QAbstractSpinBox::NoButtons) {
+ option->subControls |= QStyle::SC_SpinBoxUp | QStyle::SC_SpinBoxDown;
+ if (d->buttonState & Up) {
+ option->activeSubControls = QStyle::SC_SpinBoxUp;
+ } else if (d->buttonState & Down) {
+ option->activeSubControls = QStyle::SC_SpinBoxDown;
+ }
+ }
+
+ if (d->buttonState) {
+ option->state |= QStyle::State_Sunken;
+ } else {
+ option->activeSubControls = d->hoverControl;
+ }
+
+ option->stepEnabled = style()->styleHint(QStyle::SH_SpinControls_DisableOnBounds)
+ ? stepEnabled()
+ : (QAbstractSpinBox::StepDownEnabled|QAbstractSpinBox::StepUpEnabled);
+
+ option->frame = d->frame;
+}
+
+/*!
+ \internal
+
+ Bounds \a val to be within minimum and maximum. Also tries to be
+ clever about setting it at min and max depending on what it was
+ and what direction it was changed etc.
+*/
+
+QVariant QAbstractSpinBoxPrivate::bound(const QVariant &val, const QVariant &old, int steps) const
+{
+ QVariant v = val;
+ if (!wrapping || steps == 0 || old.isNull()) {
+ if (variantCompare(v, minimum) < 0) {
+ v = wrapping ? maximum : minimum;
+ }
+ if (variantCompare(v, maximum) > 0) {
+ v = wrapping ? minimum : maximum;
+ }
+ } else {
+ const bool wasMin = old == minimum;
+ const bool wasMax = old == maximum;
+ const int oldcmp = variantCompare(v, old);
+ const int maxcmp = variantCompare(v, maximum);
+ const int mincmp = variantCompare(v, minimum);
+ const bool wrapped = (oldcmp > 0 && steps < 0) || (oldcmp < 0 && steps > 0);
+ if (maxcmp > 0) {
+ v = ((wasMax && !wrapped && steps > 0) || (steps < 0 && !wasMin && wrapped))
+ ? minimum : maximum;
+ } else if (wrapped && (maxcmp > 0 || mincmp < 0)) {
+ v = ((wasMax && steps > 0) || (!wasMin && steps < 0)) ? minimum : maximum;
+ } else if (mincmp < 0) {
+ v = (!wasMax && !wasMin ? minimum : maximum);
+ }
+ }
+
+ return v;
+}
+
+/*!
+ \internal
+
+ Sets the value of the spin box to \a val. Depending on the value
+ of \a ep it will also emit signals.
+*/
+
+void QAbstractSpinBoxPrivate::setValue(const QVariant &val, EmitPolicy ep,
+ bool doUpdate)
+{
+ Q_Q(QAbstractSpinBox);
+ const QVariant old = value;
+ value = bound(val);
+ pendingEmit = false;
+ cleared = false;
+ if (doUpdate) {
+ updateEdit();
+ }
+ q->update();
+
+ if (ep == AlwaysEmit || (ep == EmitIfChanged && old != value)) {
+ emitSignals(ep, old);
+ }
+}
+
+/*!
+ \internal
+
+ Updates the line edit to reflect the current value of the spin box.
+*/
+
+void QAbstractSpinBoxPrivate::updateEdit()
+{
+ Q_Q(QAbstractSpinBox);
+ if (type == QVariant::Invalid)
+ return;
+ const QString newText = specialValue() ? specialValueText : prefix + textFromValue(value) + suffix;
+ if (newText == edit->displayText() || cleared)
+ return;
+
+ const bool empty = edit->text().isEmpty();
+ int cursor = edit->cursorPosition();
+ int selsize = edit->selectedText().size();
+ const bool sb = edit->blockSignals(true);
+ edit->setText(newText);
+
+ if (!specialValue()) {
+ cursor = qBound(prefix.size(), cursor, edit->displayText().size() - suffix.size());
+
+ if (selsize > 0) {
+ edit->setSelection(cursor, selsize);
+ } else {
+ edit->setCursorPosition(empty ? prefix.size() : cursor);
+ }
+ }
+ edit->blockSignals(sb);
+ q->update();
+}
+
+/*!
+ \internal
+
+ Convenience function to set min/max values.
+*/
+
+void QAbstractSpinBoxPrivate::setRange(const QVariant &min, const QVariant &max)
+{
+ clearCache();
+ minimum = min;
+ maximum = (variantCompare(min, max) < 0 ? max : min);
+ cachedSizeHint = QSize(); // minimumSizeHint doesn't care about min/max
+
+ reset();
+ if (!(bound(value) == value)) {
+ setValue(bound(value), EmitIfChanged);
+ } else if (value == minimum && !specialValueText.isEmpty()) {
+ updateEdit();
+ }
+}
+
+/*!
+ \internal
+
+ Convenience function to get a variant of the right type.
+*/
+
+QVariant QAbstractSpinBoxPrivate::getZeroVariant() const
+{
+ QVariant ret;
+ switch (type) {
+ case QVariant::Int: ret = QVariant((int)0); break;
+ case QVariant::Double: ret = QVariant((double)0.0); break;
+ default: break;
+ }
+ return ret;
+}
+
+/*!
+ \internal
+
+ Virtual method called that calls the public textFromValue()
+ functions in the subclasses. Needed to change signature from
+ QVariant to int/double/QDateTime etc. Used when needing to display
+ a value textually.
+
+ This method is reimeplemented in the various subclasses.
+*/
+
+QString QAbstractSpinBoxPrivate::textFromValue(const QVariant &) const
+{
+ return QString();
+}
+
+/*!
+ \internal
+
+ Virtual method called that calls the public valueFromText()
+ functions in the subclasses. Needed to change signature from
+ QVariant to int/double/QDateTime etc. Used when needing to
+ interpret a string as another type.
+
+ This method is reimeplemented in the various subclasses.
+*/
+
+QVariant QAbstractSpinBoxPrivate::valueFromText(const QString &) const
+{
+ return QVariant();
+}
+/*!
+ \internal
+
+ Interprets text and emits signals. Called when the spinbox needs
+ to interpret the text on the lineedit.
+*/
+
+void QAbstractSpinBoxPrivate::interpret(EmitPolicy ep)
+{
+ Q_Q(QAbstractSpinBox);
+ if (type == QVariant::Invalid || cleared)
+ return;
+
+ QVariant v = getZeroVariant();
+ bool doInterpret = true;
+ QString tmp = edit->displayText();
+ int pos = edit->cursorPosition();
+ const int oldpos = pos;
+
+ if (q->validate(tmp, pos) != QValidator::Acceptable) {
+ const QString copy = tmp;
+ q->fixup(tmp);
+ QASBDEBUG() << "QAbstractSpinBoxPrivate::interpret() text '"
+ << edit->displayText()
+ << "' >> '" << copy << "'"
+ << "' >> '" << tmp << "'";
+
+ doInterpret = tmp != copy && (q->validate(tmp, pos) == QValidator::Acceptable);
+ if (!doInterpret) {
+ v = (correctionMode == QAbstractSpinBox::CorrectToNearestValue
+ ? variantBound(minimum, v, maximum) : value);
+ }
+ }
+ if (doInterpret) {
+ v = valueFromText(tmp);
+ }
+ clearCache();
+ setValue(v, ep, true);
+ if (oldpos != pos)
+ edit->setCursorPosition(pos);
+}
+
+void QAbstractSpinBoxPrivate::clearCache() const
+{
+ cachedText.clear();
+ cachedValue.clear();
+ cachedState = QValidator::Acceptable;
+}
+
+
+// --- QSpinBoxValidator ---
+
+/*!
+ \internal
+ Constructs a QSpinBoxValidator object
+*/
+
+QSpinBoxValidator::QSpinBoxValidator(QAbstractSpinBox *qp, QAbstractSpinBoxPrivate *dp)
+ : QValidator(qp), qptr(qp), dptr(dp)
+{
+ setObjectName(QLatin1String("qt_spinboxvalidator"));
+}
+
+/*!
+ \internal
+
+ Checks for specialValueText, prefix, suffix and calls
+ the virtual QAbstractSpinBox::validate function.
+*/
+
+QValidator::State QSpinBoxValidator::validate(QString &input, int &pos) const
+{
+ if (dptr->specialValueText.size() > 0 && input == dptr->specialValueText)
+ return QValidator::Acceptable;
+
+ if (!dptr->prefix.isEmpty() && !input.startsWith(dptr->prefix))
+ input.prepend(dptr->prefix);
+
+ if (!dptr->suffix.isEmpty() && !input.endsWith(dptr->suffix))
+ input.append(dptr->suffix);
+
+ return qptr->validate(input, pos);
+}
+/*!
+ \internal
+ Calls the virtual QAbstractSpinBox::fixup function.
+*/
+
+void QSpinBoxValidator::fixup(QString &input) const
+{
+ qptr->fixup(input);
+}
+
+// --- global ---
+
+/*!
+ \internal
+ Adds two variants together and returns the result.
+*/
+
+QVariant operator+(const QVariant &arg1, const QVariant &arg2)
+{
+ QVariant ret;
+ if (arg1.type() != arg2.type())
+ qWarning("QAbstractSpinBox: Internal error: Different types (%s vs %s) (%s:%d)",
+ arg1.typeName(), arg2.typeName(), __FILE__, __LINE__);
+ switch (arg1.type()) {
+ case QVariant::Int: ret = QVariant(arg1.toInt() + arg2.toInt()); break;
+ case QVariant::Double: ret = QVariant(arg1.toDouble() + arg2.toDouble()); break;
+ case QVariant::DateTime: {
+ QDateTime a2 = arg2.toDateTime();
+ QDateTime a1 = arg1.toDateTime().addDays(QDATETIMEEDIT_DATETIME_MIN.daysTo(a2));
+ a1.setTime(a1.time().addMSecs(QTime().msecsTo(a2.time())));
+ ret = QVariant(a1);
+ }
+ default: break;
+ }
+ return ret;
+}
+
+
+/*!
+ \internal
+ Subtracts two variants and returns the result.
+*/
+
+QVariant operator-(const QVariant &arg1, const QVariant &arg2)
+{
+ QVariant ret;
+ if (arg1.type() != arg2.type())
+ qWarning("QAbstractSpinBox: Internal error: Different types (%s vs %s) (%s:%d)",
+ arg1.typeName(), arg2.typeName(), __FILE__, __LINE__);
+ switch (arg1.type()) {
+ case QVariant::Int: ret = QVariant(arg1.toInt() - arg2.toInt()); break;
+ case QVariant::Double: ret = QVariant(arg1.toDouble() - arg2.toDouble()); break;
+ case QVariant::DateTime: {
+ QDateTime a1 = arg1.toDateTime();
+ QDateTime a2 = arg2.toDateTime();
+ int days = a2.daysTo(a1);
+ int secs = a2.secsTo(a1);
+ int msecs = qMax(0, a1.time().msec() - a2.time().msec());
+ if (days < 0 || secs < 0 || msecs < 0) {
+ ret = arg1;
+ } else {
+ QDateTime dt = a2.addDays(days).addSecs(secs);
+ if (msecs > 0)
+ dt.setTime(dt.time().addMSecs(msecs));
+ ret = QVariant(dt);
+ }
+ }
+ default: break;
+ }
+ return ret;
+}
+
+/*!
+ \internal
+ Multiplies \a arg1 by \a multiplier and returns the result.
+*/
+
+QVariant operator*(const QVariant &arg1, double multiplier)
+{
+ QVariant ret;
+
+ switch (arg1.type()) {
+ case QVariant::Int: ret = QVariant((int)(arg1.toInt() * multiplier)); break;
+ case QVariant::Double: ret = QVariant(arg1.toDouble() * multiplier); break;
+ case QVariant::DateTime: {
+ double days = QDATETIMEEDIT_DATE_MIN.daysTo(arg1.toDateTime().date()) * multiplier;
+ int daysInt = (int)days;
+ days -= daysInt;
+ long msecs = (long)((QDATETIMEEDIT_TIME_MIN.msecsTo(arg1.toDateTime().time()) * multiplier)
+ + (days * (24 * 3600 * 1000)));
+ ret = QDateTime(QDate().addDays(int(days)), QTime().addMSecs(msecs));
+ break;
+ }
+ default: ret = arg1; break;
+ }
+
+ return ret;
+}
+
+
+
+double operator/(const QVariant &arg1, const QVariant &arg2)
+{
+ double a1 = 0;
+ double a2 = 0;
+
+ switch (arg1.type()) {
+ case QVariant::Int:
+ a1 = (double)arg1.toInt();
+ a2 = (double)arg2.toInt();
+ break;
+ case QVariant::Double:
+ a1 = arg1.toDouble();
+ a2 = arg2.toDouble();
+ break;
+ case QVariant::DateTime:
+ a1 = QDATETIMEEDIT_DATE_MIN.daysTo(arg1.toDate());
+ a2 = QDATETIMEEDIT_DATE_MIN.daysTo(arg2.toDate());
+ a1 += (double)QDATETIMEEDIT_TIME_MIN.msecsTo(arg1.toDateTime().time()) / (long)(3600 * 24 * 1000);
+ a2 += (double)QDATETIMEEDIT_TIME_MIN.msecsTo(arg2.toDateTime().time()) / (long)(3600 * 24 * 1000);
+ default: break;
+ }
+
+ return (a1 != 0 && a2 != 0) ? (a1 / a2) : 0.0;
+}
+
+int QAbstractSpinBoxPrivate::variantCompare(const QVariant &arg1, const QVariant &arg2)
+{
+ switch (arg2.type()) {
+ case QVariant::Date:
+ Q_ASSERT_X(arg1.type() == QVariant::Date, "QAbstractSpinBoxPrivate::variantCompare",
+ qPrintable(QString::fromAscii("Internal error 1 (%1)").
+ arg(QString::fromAscii(arg1.typeName()))));
+ if (arg1.toDate() == arg2.toDate()) {
+ return 0;
+ } else if (arg1.toDate() < arg2.toDate()) {
+ return -1;
+ } else {
+ return 1;
+ }
+ case QVariant::Time:
+ Q_ASSERT_X(arg1.type() == QVariant::Time, "QAbstractSpinBoxPrivate::variantCompare",
+ qPrintable(QString::fromAscii("Internal error 2 (%1)").
+ arg(QString::fromAscii(arg1.typeName()))));
+ if (arg1.toTime() == arg2.toTime()) {
+ return 0;
+ } else if (arg1.toTime() < arg2.toTime()) {
+ return -1;
+ } else {
+ return 1;
+ }
+
+
+ case QVariant::DateTime:
+ if (arg1.toDateTime() == arg2.toDateTime()) {
+ return 0;
+ } else if (arg1.toDateTime() < arg2.toDateTime()) {
+ return -1;
+ } else {
+ return 1;
+ }
+ case QVariant::Int:
+ if (arg1.toInt() == arg2.toInt()) {
+ return 0;
+ } else if (arg1.toInt() < arg2.toInt()) {
+ return -1;
+ } else {
+ return 1;
+ }
+ case QVariant::Double:
+ if (arg1.toDouble() == arg2.toDouble()) {
+ return 0;
+ } else if (arg1.toDouble() < arg2.toDouble()) {
+ return -1;
+ } else {
+ return 1;
+ }
+ case QVariant::Invalid:
+ if (arg2.type() == QVariant::Invalid)
+ return 0;
+ default:
+ Q_ASSERT_X(0, "QAbstractSpinBoxPrivate::variantCompare",
+ qPrintable(QString::fromAscii("Internal error 3 (%1 %2)").
+ arg(QString::fromAscii(arg1.typeName())).
+ arg(QString::fromAscii(arg2.typeName()))));
+ }
+ return -2;
+}
+
+QVariant QAbstractSpinBoxPrivate::variantBound(const QVariant &min,
+ const QVariant &value,
+ const QVariant &max)
+{
+ Q_ASSERT(variantCompare(min, max) <= 0);
+ if (variantCompare(min, value) < 0) {
+ const int compMax = variantCompare(value, max);
+ return (compMax < 0 ? value : max);
+ } else {
+ return min;
+ }
+}
+
+
+QT_END_NAMESPACE
+
+#include "moc_qabstractspinbox.cpp"
+
+#endif // QT_NO_SPINBOX
diff --git a/src/gui/widgets/qabstractspinbox.h b/src/gui/widgets/qabstractspinbox.h
new file mode 100644
index 0000000000..4e7fc3ff35
--- /dev/null
+++ b/src/gui/widgets/qabstractspinbox.h
@@ -0,0 +1,177 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QABSTRACTSPINBOX_H
+#define QABSTRACTSPINBOX_H
+
+#include <QtGui/qwidget.h>
+#include <QtGui/qvalidator.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+#ifndef QT_NO_SPINBOX
+
+class QLineEdit;
+
+class QAbstractSpinBoxPrivate;
+class QStyleOptionSpinBox;
+
+class Q_GUI_EXPORT QAbstractSpinBox : public QWidget
+{
+ Q_OBJECT
+
+ Q_ENUMS(ButtonSymbols)
+ Q_ENUMS(CorrectionMode)
+ Q_PROPERTY(bool wrapping READ wrapping WRITE setWrapping)
+ Q_PROPERTY(bool frame READ hasFrame WRITE setFrame)
+ Q_PROPERTY(Qt::Alignment alignment READ alignment WRITE setAlignment)
+ Q_PROPERTY(bool readOnly READ isReadOnly WRITE setReadOnly)
+ Q_PROPERTY(ButtonSymbols buttonSymbols READ buttonSymbols WRITE setButtonSymbols)
+ Q_PROPERTY(QString specialValueText READ specialValueText WRITE setSpecialValueText)
+ Q_PROPERTY(QString text READ text)
+ Q_PROPERTY(bool accelerated READ isAccelerated WRITE setAccelerated)
+ Q_PROPERTY(CorrectionMode correctionMode READ correctionMode WRITE setCorrectionMode)
+ Q_PROPERTY(bool acceptableInput READ hasAcceptableInput)
+ Q_PROPERTY(bool keyboardTracking READ keyboardTracking WRITE setKeyboardTracking)
+public:
+ explicit QAbstractSpinBox(QWidget *parent = 0);
+ ~QAbstractSpinBox();
+
+ enum StepEnabledFlag { StepNone = 0x00, StepUpEnabled = 0x01,
+ StepDownEnabled = 0x02 };
+ Q_DECLARE_FLAGS(StepEnabled, StepEnabledFlag)
+
+ enum ButtonSymbols { UpDownArrows, PlusMinus, NoButtons };
+
+ ButtonSymbols buttonSymbols() const;
+ void setButtonSymbols(ButtonSymbols bs);
+
+ enum CorrectionMode { CorrectToPreviousValue, CorrectToNearestValue };
+
+ void setCorrectionMode(CorrectionMode cm);
+ CorrectionMode correctionMode() const;
+
+ bool hasAcceptableInput() const;
+ QString text() const;
+
+ QString specialValueText() const;
+ void setSpecialValueText(const QString &txt);
+
+ bool wrapping() const;
+ void setWrapping(bool w);
+
+ void setReadOnly(bool r);
+ bool isReadOnly() const;
+
+ void setKeyboardTracking(bool kt);
+ bool keyboardTracking() const;
+
+ void setAlignment(Qt::Alignment flag);
+ Qt::Alignment alignment() const;
+
+ void setFrame(bool);
+ bool hasFrame() const;
+
+ void setAccelerated(bool on);
+ bool isAccelerated() const;
+
+ QSize sizeHint() const;
+ QSize minimumSizeHint() const;
+ void interpretText();
+ bool event(QEvent *event);
+
+ virtual QValidator::State validate(QString &input, int &pos) const;
+ virtual void fixup(QString &input) const;
+
+ virtual void stepBy(int steps);
+public Q_SLOTS:
+ void stepUp();
+ void stepDown();
+ void selectAll();
+ virtual void clear();
+protected:
+ void resizeEvent(QResizeEvent *event);
+ void keyPressEvent(QKeyEvent *event);
+ void keyReleaseEvent(QKeyEvent *event);
+ void wheelEvent(QWheelEvent *event);
+ void focusInEvent(QFocusEvent *event);
+ void focusOutEvent(QFocusEvent *event);
+ void contextMenuEvent(QContextMenuEvent *event);
+ void changeEvent(QEvent *event);
+ void closeEvent(QCloseEvent *event);
+ void hideEvent(QHideEvent *event);
+ void mousePressEvent(QMouseEvent *event);
+ void mouseReleaseEvent(QMouseEvent *event);
+ void mouseMoveEvent(QMouseEvent *event);
+ void timerEvent(QTimerEvent *event);
+ void paintEvent(QPaintEvent *event);
+ void showEvent(QShowEvent *event);
+ void initStyleOption(QStyleOptionSpinBox *option) const;
+
+ QLineEdit *lineEdit() const;
+ void setLineEdit(QLineEdit *edit);
+
+ virtual StepEnabled stepEnabled() const;
+Q_SIGNALS:
+ void editingFinished();
+protected:
+ QAbstractSpinBox(QAbstractSpinBoxPrivate &dd, QWidget *parent = 0);
+
+private:
+ Q_PRIVATE_SLOT(d_func(), void _q_editorTextChanged(const QString &))
+ Q_PRIVATE_SLOT(d_func(), void _q_editorCursorPositionChanged(int, int))
+
+ Q_DECLARE_PRIVATE(QAbstractSpinBox)
+ Q_DISABLE_COPY(QAbstractSpinBox)
+};
+Q_DECLARE_OPERATORS_FOR_FLAGS(QAbstractSpinBox::StepEnabled)
+
+#endif // QT_NO_SPINBOX
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QABSTRACTSPINBOX_H
diff --git a/src/gui/widgets/qabstractspinbox_p.h b/src/gui/widgets/qabstractspinbox_p.h
new file mode 100644
index 0000000000..5f7f896a2e
--- /dev/null
+++ b/src/gui/widgets/qabstractspinbox_p.h
@@ -0,0 +1,171 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QABSTRACTSPINBOX_P_H
+#define QABSTRACTSPINBOX_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 "QtGui/qabstractspinbox.h"
+
+#ifndef QT_NO_SPINBOX
+
+#include "QtGui/qlineedit.h"
+#include "QtGui/qstyleoption.h"
+#include "QtGui/qvalidator.h"
+#include "QtCore/qdatetime.h"
+#include "QtCore/qvariant.h"
+#include "private/qwidget_p.h"
+#include "private/qdatetime_p.h"
+
+QT_BEGIN_NAMESPACE
+
+QVariant operator+(const QVariant &arg1, const QVariant &arg2);
+QVariant operator-(const QVariant &arg1, const QVariant &arg2);
+QVariant operator*(const QVariant &arg1, double multiplier);
+double operator/(const QVariant &arg1, const QVariant &arg2);
+
+enum EmitPolicy {
+ EmitIfChanged,
+ AlwaysEmit,
+ NeverEmit
+};
+
+enum Button {
+ None = 0x000,
+ Keyboard = 0x001,
+ Mouse = 0x002,
+ Wheel = 0x004,
+ ButtonMask = 0x008,
+ Up = 0x010,
+ Down = 0x020,
+ DirectionMask = 0x040
+};
+class QSpinBoxValidator;
+class QAbstractSpinBoxPrivate : public QWidgetPrivate
+{
+ Q_DECLARE_PUBLIC(QAbstractSpinBox)
+public:
+ QAbstractSpinBoxPrivate();
+ ~QAbstractSpinBoxPrivate();
+
+ void init();
+ void reset();
+ void updateState(bool up);
+ QString stripped(const QString &text, int *pos = 0) const;
+ bool specialValue() const;
+ virtual QVariant getZeroVariant() const;
+ virtual void setRange(const QVariant &min, const QVariant &max);
+ void setValue(const QVariant &val, EmitPolicy ep, bool updateEdit = true);
+ virtual QVariant bound(const QVariant &val, const QVariant &old = QVariant(), int steps = 0) const;
+ virtual void updateEdit();
+
+ virtual void emitSignals(EmitPolicy ep, const QVariant &old);
+ virtual void interpret(EmitPolicy ep);
+ virtual QString textFromValue(const QVariant &n) const;
+ virtual QVariant valueFromText(const QString &input) const;
+
+ void _q_editorTextChanged(const QString &);
+ virtual void _q_editorCursorPositionChanged(int oldpos, int newpos);
+
+ virtual QStyle::SubControl newHoverControl(const QPoint &pos);
+ bool updateHoverControl(const QPoint &pos);
+
+ virtual void clearCache() const;
+ virtual void updateEditFieldGeometry();
+
+ static int variantCompare(const QVariant &arg1, const QVariant &arg2);
+ static QVariant variantBound(const QVariant &min, const QVariant &value, const QVariant &max);
+
+ QLineEdit *edit;
+ QString prefix, suffix, specialValueText;
+ QVariant value, minimum, maximum, singleStep;
+ QVariant::Type type;
+ int spinClickTimerId, spinClickTimerInterval, spinClickThresholdTimerId, spinClickThresholdTimerInterval;
+ uint buttonState;
+ mutable QString cachedText;
+ mutable QVariant cachedValue;
+ mutable QValidator::State cachedState;
+ mutable QSize cachedSizeHint, cachedMinimumSizeHint;
+ uint pendingEmit : 1;
+ uint spindownEnabled : 1;
+ uint spinupEnabled : 1;
+ uint readOnly : 1;
+ uint wrapping : 1;
+ uint ignoreCursorPositionChanged : 1;
+ uint frame : 1;
+ uint accelerate : 1;
+ uint keyboardTracking : 1;
+ uint cleared : 1;
+ uint ignoreUpdateEdit : 1;
+ QAbstractSpinBox::CorrectionMode correctionMode;
+ int acceleration;
+ QStyle::SubControl hoverControl;
+ QRect hoverRect;
+ QAbstractSpinBox::ButtonSymbols buttonSymbols;
+ QSpinBoxValidator *validator;
+};
+
+class QSpinBoxValidator : public QValidator
+{
+public:
+ QSpinBoxValidator(QAbstractSpinBox *qptr, QAbstractSpinBoxPrivate *dptr);
+ QValidator::State validate(QString &input, int &) const;
+ void fixup(QString &) const;
+private:
+ QAbstractSpinBox *qptr;
+ QAbstractSpinBoxPrivate *dptr;
+};
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_SPINBOX
+
+#endif // QABSTRACTSPINBOX_P_H
diff --git a/src/gui/widgets/qbuttongroup.cpp b/src/gui/widgets/qbuttongroup.cpp
new file mode 100644
index 0000000000..06bcf1e6cc
--- /dev/null
+++ b/src/gui/widgets/qbuttongroup.cpp
@@ -0,0 +1,260 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+
+
+/*!
+ \class QButtonGroup
+ \brief The QButtonGroup class provides a container to organize groups of
+ button widgets.
+
+ \ingroup organizers
+ \ingroup geomanagement
+ \ingroup appearance
+ \mainclass
+
+ QButtonGroup provides an abstract container into which button widgets can
+ be placed. It does not provide a visual representation of this container
+ (see QGroupBox for a container widget), but instead manages the states of
+ each of the buttons in the group.
+
+ An \l {QButtonGroup::exclusive} {exclusive} button group switches
+ off all checkable (toggle) buttons except the one that was
+ clicked. By default, a button group is exclusive. The buttons in a
+ button group are usually checkable QPushButton's, \l{QCheckBox}es
+ (normally for non-exclusive button groups), or \l{QRadioButton}s.
+ If you create an exclusive button group, you should ensure that
+ one of the buttons in the group is initially checked; otherwise,
+ the group will initially be in a state where no buttons are
+ checked.
+
+ A button is added to the group with addButton(). It can be removed
+ from the group with removeButton(). If the group is exclusive, the
+ currently checked button is available as checkedButton(). If a
+ button is clicked the buttonClicked() signal is emitted. For a
+ checkable button in an exclusive group this means that the button
+ was checked. The list of buttons in the group is returned by
+ buttons().
+
+ In addition, QButtonGroup can map between integers and buttons.
+ You can assign an integer id to a button with setId(), and
+ retrieve it with id(). The id of the currently checked button is
+ available with checkedId(), and there is an overloaded signal
+ buttonClicked() which emits the id of the button. The id \c {-1}
+ is reserved by QButtonGroup to mean "no such button". The purpose
+ of the mapping mechanism is to simplify the representation of enum
+ values in a user interface.
+
+ \sa QGroupBox QPushButton, QCheckBox, QRadioButton
+*/
+
+/*!
+ \fn QButtonGroup::QButtonGroup(QObject *parent)
+
+ Constructs a new, empty button group with the given \a parent.
+
+ \sa addButton() setExclusive()
+*/
+
+/*!
+ \fn QButtonGroup::~QButtonGroup()
+
+ Destroys the button group.
+*/
+
+/*!
+ \property QButtonGroup::exclusive
+ \brief whether the button group is exclusive
+
+ If this property is true then only one button in the group can be checked
+ at any given time. The user can click on any button to check it, and that
+ button will replace the existing one as the checked button in the group.
+
+ In an exclusive group, the user cannot uncheck the currently checked button
+ by clicking on it; instead, another button in the group must be clicked
+ to set the new checked button for that group.
+
+ By default, this property is true.
+*/
+
+/*!
+ \fn void QButtonGroup::buttonClicked(QAbstractButton *button)
+
+ This signal is emitted when the given \a button is clicked. A
+ button is clicked when it is first pressed and then released, when
+ its shortcut key is typed, or programmatically when
+ QAbstractButton::click() or QAbstractButton::animateClick() is
+ called.
+
+
+ \sa checkedButton(), QAbstractButton::clicked()
+*/
+
+/*!
+ \fn void QButtonGroup::buttonClicked(int id)
+
+ This signal is emitted when a button with the given \a id is
+ clicked.
+
+ \sa checkedButton(), QAbstractButton::clicked()
+*/
+
+/*!
+ \fn void QButtonGroup::buttonPressed(QAbstractButton *button)
+ \since 4.2
+
+ This signal is emitted when the given \a button is pressed down.
+
+ \sa QAbstractButton::pressed()
+*/
+
+/*!
+ \fn void QButtonGroup::buttonPressed(int id)
+ \since 4.2
+
+ This signal is emitted when a button with the given \a id is
+ pressed down.
+
+ \sa QAbstractButton::pressed()
+*/
+
+/*!
+ \fn void QButtonGroup::buttonReleased(QAbstractButton *button)
+ \since 4.2
+
+ This signal is emitted when the given \a button is released.
+
+ \sa QAbstractButton::released()
+*/
+
+/*!
+ \fn void QButtonGroup::buttonReleased(int id)
+ \since 4.2
+
+ This signal is emitted when a button with the given \a id is
+ released.
+
+ \sa QAbstractButton::released()
+*/
+
+/*!
+ \fn void QButtonGroup::addButton(QAbstractButton *button, int id = -1);
+
+ Adds the given \a button to the button group, with the given \a
+ id. If \a id is -1 (the default), an id will be assigned to the
+ button by this QButtonGroup.
+
+ \sa removeButton() buttons()
+*/
+
+/*!
+ \fn void QButtonGroup::removeButton(QAbstractButton *button);
+
+ Removes the given \a button from the button group.
+
+ \sa addButton() buttons()
+*/
+
+/*!
+ \fn QList<QAbstractButton*> QButtonGroup::buttons() const
+
+ Returns the list of this groups's buttons. This may be empty.
+
+ \sa addButton(), removeButton()
+*/
+
+/*!
+ \fn QAbstractButton *QButtonGroup::checkedButton() const;
+
+ Returns the button group's checked button, or 0 if no buttons are
+ checked.
+
+ \sa buttonClicked()
+*/
+
+/*!
+ \fn QAbstractButton *QButtonGroup::button(int id) const;
+ \since 4.1
+
+ Returns the button with the specified \a id, or 0 if no such button
+ exists.
+*/
+
+/*!
+ \fn void QButtonGroup::setId(QAbstractButton *button, int id)
+ \since 4.1
+
+ Sets the \a id for the specified \a button. Note that \a id can
+ not be -1.
+
+ \sa id()
+*/
+
+/*!
+ \fn int QButtonGroup::id(QAbstractButton *button) const;
+ \since 4.1
+
+ Returns the id for the specified \a button, or -1 if no such button
+ exists.
+
+
+ \sa setId()
+*/
+
+/*!
+ \fn int QButtonGroup::checkedId() const;
+ \since 4.1
+
+ Returns the id of the checkedButton(), or -1 if no button is checked.
+
+ \sa setId()
+*/
+
+
+/*! \fn void QButtonGroup::insert(QAbstractButton *b)
+
+ Use addButton() instead.
+*/
+
+/*! \fn void QButtonGroup::remove(QAbstractButton *b)
+
+ Use removeButton() instead.
+*/
diff --git a/src/gui/widgets/qbuttongroup.h b/src/gui/widgets/qbuttongroup.h
new file mode 100644
index 0000000000..e0b01eec8d
--- /dev/null
+++ b/src/gui/widgets/qbuttongroup.h
@@ -0,0 +1,112 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QBUTTONGROUP_H
+#define QBUTTONGROUP_H
+
+#include <QtCore/qobject.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+#ifndef QT_NO_BUTTONGROUP
+
+class QAbstractButton;
+class QAbstractButtonPrivate;
+class QButtonGroupPrivate;
+
+class Q_GUI_EXPORT QButtonGroup : public QObject
+{
+ Q_OBJECT
+
+ Q_PROPERTY(bool exclusive READ exclusive WRITE setExclusive)
+public:
+ explicit QButtonGroup(QObject *parent = 0);
+ ~QButtonGroup();
+
+ void setExclusive(bool);
+ bool exclusive() const;
+
+ void addButton(QAbstractButton *);
+ void addButton(QAbstractButton *, int id);
+ void removeButton(QAbstractButton *);
+
+ QList<QAbstractButton*> buttons() const;
+
+ QAbstractButton * checkedButton() const;
+ // no setter on purpose!
+
+ QAbstractButton *button(int id) const;
+ void setId(QAbstractButton *button, int id);
+ int id(QAbstractButton *button) const;
+ int checkedId() const;
+
+Q_SIGNALS:
+ void buttonClicked(QAbstractButton *);
+ void buttonClicked(int);
+ void buttonPressed(QAbstractButton *);
+ void buttonPressed(int);
+ void buttonReleased(QAbstractButton *);
+ void buttonReleased(int);
+
+#ifdef QT3_SUPPORT
+public:
+ inline QT3_SUPPORT void insert(QAbstractButton *b) { addButton(b); }
+ inline QT3_SUPPORT void remove(QAbstractButton *b) { removeButton(b); }
+#endif
+
+private:
+ Q_DISABLE_COPY(QButtonGroup)
+ Q_DECLARE_PRIVATE(QButtonGroup)
+ friend class QAbstractButton;
+ friend class QAbstractButtonPrivate;
+};
+
+#endif // QT_NO_BUTTONGROUP
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QBUTTONGROUP_H
diff --git a/src/gui/widgets/qcalendartextnavigator_p.h b/src/gui/widgets/qcalendartextnavigator_p.h
new file mode 100644
index 0000000000..0aaeffcc88
--- /dev/null
+++ b/src/gui/widgets/qcalendartextnavigator_p.h
@@ -0,0 +1,112 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QCALENDARTEXTNAVIGATOR_P_H
+#define QCALENDARTEXTNAVIGATOR_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 <QtCore/qobject.h>
+#include <QtCore/qdatetime.h>
+#include <QtCore/qbasictimer.h>
+
+#ifndef QT_NO_CALENDARWIDGET
+
+QT_BEGIN_NAMESPACE
+
+class QLabel;
+class QCalendarDateValidator;
+class QFrame;
+
+class QCalendarTextNavigator: public QObject
+{
+ Q_OBJECT
+public:
+ QCalendarTextNavigator(QObject *parent = 0)
+ : QObject(parent), m_dateText(0), m_dateFrame(0), m_dateValidator(0), m_widget(0), m_editDelay(1500), m_date(QDate::currentDate()) { }
+
+ QWidget *widget() const;
+ void setWidget(QWidget *widget);
+
+ int dateEditAcceptDelay() const;
+ void setDateEditAcceptDelay(int delay);
+
+ QDate date() const;
+ void setDate(const QDate &date);
+
+ bool eventFilter(QObject *o, QEvent *e);
+ void timerEvent(QTimerEvent *e);
+
+signals:
+ void dateChanged(const QDate &date);
+ void editingFinished();
+
+private:
+ void applyDate();
+ void updateDateLabel();
+ void createDateLabel();
+ void removeDateLabel();
+
+ QLabel *m_dateText;
+ QFrame *m_dateFrame;
+ QBasicTimer m_acceptTimer;
+ QCalendarDateValidator *m_dateValidator;
+ QWidget *m_widget;
+ int m_editDelay;
+
+ QDate m_date;
+};
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_CALENDARWIDGET
+
+#endif
+
diff --git a/src/gui/widgets/qcalendarwidget.cpp b/src/gui/widgets/qcalendarwidget.cpp
new file mode 100644
index 0000000000..92c12a5a4b
--- /dev/null
+++ b/src/gui/widgets/qcalendarwidget.cpp
@@ -0,0 +1,3091 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qcalendarwidget.h"
+
+#ifndef QT_NO_CALENDARWIDGET
+
+#include <qabstractitemmodel.h>
+#include <qitemdelegate.h>
+#include <qdatetime.h>
+#include <qtableview.h>
+#include <qlayout.h>
+#include <qevent.h>
+#include <qtextformat.h>
+#include <qheaderview.h>
+#include <private/qwidget_p.h>
+#include <qpushbutton.h>
+#include <qtoolbutton.h>
+#include <qlabel.h>
+#include <qspinbox.h>
+#include <qmenu.h>
+#include <qapplication.h>
+#include <qbasictimer.h>
+#include <qstylepainter.h>
+#include <private/qcalendartextnavigator_p.h>
+
+QT_BEGIN_NAMESPACE
+
+enum {
+ RowCount = 6,
+ ColumnCount = 7,
+ HeaderColumn = 0,
+ HeaderRow = 0,
+ MinimumDayOffset = 1
+};
+
+class QCalendarDateSectionValidator
+{
+public:
+
+ enum Section {
+ NextSection,
+ ThisSection,
+ PrevSection
+ };
+
+ QCalendarDateSectionValidator() {}
+ virtual ~QCalendarDateSectionValidator() {}
+ virtual Section handleKey(int key) = 0;
+ virtual QDate applyToDate(const QDate &date) const = 0;
+ virtual void setDate(const QDate &date) = 0;
+ virtual QString text() const = 0;
+ virtual QString text(const QDate &date, int repeat) const = 0;
+
+ QLocale m_locale;
+
+protected:
+ QString highlightString(const QString &str, int pos) const;
+private:
+};
+
+QString QCalendarDateSectionValidator::highlightString(const QString &str, int pos) const
+{
+ if (pos == 0)
+ return QLatin1String("<b>") + str + QLatin1String("</b>");
+ int startPos = str.length() - pos;
+ return str.mid(0, startPos) + QLatin1String("<b>") + str.mid(startPos, pos) + QLatin1String("</b>");
+
+}
+
+class QCalendarDayValidator : public QCalendarDateSectionValidator
+{
+
+public:
+ QCalendarDayValidator();
+ virtual Section handleKey(int key);
+ virtual QDate applyToDate(const QDate &date) const;
+ virtual void setDate(const QDate &date);
+ virtual QString text() const;
+ virtual QString text(const QDate &date, int repeat) const;
+private:
+ int m_pos;
+ int m_day;
+ int m_oldDay;
+};
+
+QCalendarDayValidator::QCalendarDayValidator()
+ : QCalendarDateSectionValidator(), m_pos(0), m_day(1), m_oldDay(1)
+{
+}
+
+QCalendarDateSectionValidator::Section QCalendarDayValidator::handleKey(int key)
+{
+ if (key == Qt::Key_Right || key == Qt::Key_Left) {
+ m_pos = 0;
+ return QCalendarDateSectionValidator::ThisSection;
+ } else if (key == Qt::Key_Up) {
+ m_pos = 0;
+ ++m_day;
+ if (m_day > 31)
+ m_day = 1;
+ return QCalendarDateSectionValidator::ThisSection;
+ } else if (key == Qt::Key_Down) {
+ m_pos = 0;
+ --m_day;
+ if (m_day < 1)
+ m_day = 31;
+ return QCalendarDateSectionValidator::ThisSection;
+ } else if (key == Qt::Key_Back || key == Qt::Key_Backspace) {
+ --m_pos;
+ if (m_pos < 0)
+ m_pos = 1;
+
+ if (m_pos == 0)
+ m_day = m_oldDay;
+ else
+ m_day = m_day / 10;
+ //m_day = m_oldDay / 10 * 10 + m_day / 10;
+
+ if (m_pos == 0)
+ return QCalendarDateSectionValidator::PrevSection;
+ return QCalendarDateSectionValidator::ThisSection;
+ }
+ if (key < Qt::Key_0 || key > Qt::Key_9)
+ return QCalendarDateSectionValidator::ThisSection;
+ int pressedKey = key - Qt::Key_0;
+ if (m_pos == 0)
+ m_day = pressedKey;
+ else
+ m_day = m_day % 10 * 10 + pressedKey;
+ if (m_day > 31)
+ m_day = 31;
+ ++m_pos;
+ if (m_pos > 1) {
+ m_pos = 0;
+ return QCalendarDateSectionValidator::NextSection;
+ }
+ return QCalendarDateSectionValidator::ThisSection;
+}
+
+QDate QCalendarDayValidator::applyToDate(const QDate &date) const
+{
+ int day = m_day;
+ if (day < 1)
+ day = 1;
+ else if (day > 31)
+ day = 31;
+ if (day > date.daysInMonth())
+ day = date.daysInMonth();
+ return QDate(date.year(), date.month(), day);
+}
+
+void QCalendarDayValidator::setDate(const QDate &date)
+{
+ m_day = m_oldDay = date.day();
+ m_pos = 0;
+}
+
+QString QCalendarDayValidator::text() const
+{
+ QString str;
+ if (m_day / 10 == 0)
+ str += QLatin1String("0");
+ str += QString::number(m_day);
+ return highlightString(str, m_pos);
+}
+
+QString QCalendarDayValidator::text(const QDate &date, int repeat) const
+{
+ if (repeat <= 1) {
+ return QString::number(date.day());
+ } else if (repeat == 2) {
+ QString str;
+ if (date.day() / 10 == 0)
+ str += QLatin1String("0");
+ return str + QString::number(date.day());
+ } else if (repeat == 3) {
+ return m_locale.dayName(date.dayOfWeek(), QLocale::ShortFormat);
+ } else if (repeat >= 4) {
+ return m_locale.dayName(date.dayOfWeek(), QLocale::LongFormat);
+ }
+ return QString();
+}
+
+//////////////////////////////////
+
+class QCalendarMonthValidator : public QCalendarDateSectionValidator
+{
+
+public:
+ QCalendarMonthValidator();
+ virtual Section handleKey(int key);
+ virtual QDate applyToDate(const QDate &date) const;
+ virtual void setDate(const QDate &date);
+ virtual QString text() const;
+ virtual QString text(const QDate &date, int repeat) const;
+private:
+ int m_pos;
+ int m_month;
+ int m_oldMonth;
+};
+
+QCalendarMonthValidator::QCalendarMonthValidator()
+ : QCalendarDateSectionValidator(), m_pos(0), m_month(1), m_oldMonth(1)
+{
+}
+
+QCalendarDateSectionValidator::Section QCalendarMonthValidator::handleKey(int key)
+{
+ if (key == Qt::Key_Right || key == Qt::Key_Left) {
+ m_pos = 0;
+ return QCalendarDateSectionValidator::ThisSection;
+ } else if (key == Qt::Key_Up) {
+ m_pos = 0;
+ ++m_month;
+ if (m_month > 12)
+ m_month = 1;
+ return QCalendarDateSectionValidator::ThisSection;
+ } else if (key == Qt::Key_Down) {
+ m_pos = 0;
+ --m_month;
+ if (m_month < 1)
+ m_month = 12;
+ return QCalendarDateSectionValidator::ThisSection;
+ } else if (key == Qt::Key_Back || key == Qt::Key_Backspace) {
+ --m_pos;
+ if (m_pos < 0)
+ m_pos = 1;
+
+ if (m_pos == 0)
+ m_month = m_oldMonth;
+ else
+ m_month = m_month / 10;
+ //m_month = m_oldMonth / 10 * 10 + m_month / 10;
+
+ if (m_pos == 0)
+ return QCalendarDateSectionValidator::PrevSection;
+ return QCalendarDateSectionValidator::ThisSection;
+ }
+ if (key < Qt::Key_0 || key > Qt::Key_9)
+ return QCalendarDateSectionValidator::ThisSection;
+ int pressedKey = key - Qt::Key_0;
+ if (m_pos == 0)
+ m_month = pressedKey;
+ else
+ m_month = m_month % 10 * 10 + pressedKey;
+ if (m_month > 12)
+ m_month = 12;
+ ++m_pos;
+ if (m_pos > 1) {
+ m_pos = 0;
+ return QCalendarDateSectionValidator::NextSection;
+ }
+ return QCalendarDateSectionValidator::ThisSection;
+}
+
+QDate QCalendarMonthValidator::applyToDate(const QDate &date) const
+{
+ int month = m_month;
+ if (month < 1)
+ month = 1;
+ else if (month > 12)
+ month = 12;
+ QDate newDate(date.year(), m_month, 1);
+ int day = date.day();
+ if (day > newDate.daysInMonth())
+ day = newDate.daysInMonth();
+ return QDate(date.year(), month, day);
+}
+
+void QCalendarMonthValidator::setDate(const QDate &date)
+{
+ m_month = m_oldMonth = date.month();
+ m_pos = 0;
+}
+
+QString QCalendarMonthValidator::text() const
+{
+ QString str;
+ if (m_month / 10 == 0)
+ str += QLatin1String("0");
+ str += QString::number(m_month);
+ return highlightString(str, m_pos);
+}
+
+QString QCalendarMonthValidator::text(const QDate &date, int repeat) const
+{
+ if (repeat <= 1) {
+ return QString::number(date.month());
+ } else if (repeat == 2) {
+ QString str;
+ if (date.month() / 10 == 0)
+ str += QLatin1String("0");
+ return str + QString::number(date.month());
+ } else if (repeat == 3) {
+ return m_locale.standaloneMonthName(date.month(), QLocale::ShortFormat);
+ } else if (repeat >= 4) {
+ return m_locale.standaloneMonthName(date.month(), QLocale::LongFormat);
+ }
+ return QString();
+}
+
+//////////////////////////////////
+
+class QCalendarYearValidator : public QCalendarDateSectionValidator
+{
+
+public:
+ QCalendarYearValidator();
+ virtual Section handleKey(int key);
+ virtual QDate applyToDate(const QDate &date) const;
+ virtual void setDate(const QDate &date);
+ virtual QString text() const;
+ virtual QString text(const QDate &date, int repeat) const;
+private:
+ int pow10(int n);
+ int m_pos;
+ int m_year;
+ int m_oldYear;
+};
+
+QCalendarYearValidator::QCalendarYearValidator()
+ : QCalendarDateSectionValidator(), m_pos(0), m_year(2000), m_oldYear(2000)
+{
+}
+
+int QCalendarYearValidator::pow10(int n)
+{
+ int power = 1;
+ for (int i = 0; i < n; i++)
+ power *= 10;
+ return power;
+}
+
+QCalendarDateSectionValidator::Section QCalendarYearValidator::handleKey(int key)
+{
+ if (key == Qt::Key_Right || key == Qt::Key_Left) {
+ m_pos = 0;
+ return QCalendarDateSectionValidator::ThisSection;
+ } else if (key == Qt::Key_Up) {
+ m_pos = 0;
+ ++m_year;
+ return QCalendarDateSectionValidator::ThisSection;
+ } else if (key == Qt::Key_Down) {
+ m_pos = 0;
+ --m_year;
+ return QCalendarDateSectionValidator::ThisSection;
+ } else if (key == Qt::Key_Back || key == Qt::Key_Backspace) {
+ --m_pos;
+ if (m_pos < 0)
+ m_pos = 3;
+
+ int pow = pow10(m_pos);
+ m_year = m_oldYear / pow * pow + m_year % (pow * 10) / 10;
+
+ if (m_pos == 0)
+ return QCalendarDateSectionValidator::PrevSection;
+ return QCalendarDateSectionValidator::ThisSection;
+ }
+ if (key < Qt::Key_0 || key > Qt::Key_9)
+ return QCalendarDateSectionValidator::ThisSection;
+ int pressedKey = key - Qt::Key_0;
+ int pow = pow10(m_pos);
+ m_year = m_year / (pow * 10) * (pow * 10) + m_year % pow * 10 + pressedKey;
+ ++m_pos;
+ if (m_pos > 3) {
+ m_pos = 0;
+ return QCalendarDateSectionValidator::NextSection;
+ }
+ return QCalendarDateSectionValidator::ThisSection;
+}
+
+QDate QCalendarYearValidator::applyToDate(const QDate &date) const
+{
+ int year = m_year;
+ if (year < 1)
+ year = 1;
+ QDate newDate(year, date.month(), 1);
+ int day = date.day();
+ if (day > newDate.daysInMonth())
+ day = newDate.daysInMonth();
+ return QDate(year, date.month(), day);
+}
+
+void QCalendarYearValidator::setDate(const QDate &date)
+{
+ m_year = m_oldYear = date.year();
+ m_pos = 0;
+}
+
+QString QCalendarYearValidator::text() const
+{
+ QString str;
+ int pow = 10;
+ for (int i = 0; i < 3; i++) {
+ if (m_year / pow == 0)
+ str += QLatin1String("0");
+ pow *= 10;
+ }
+ str += QString::number(m_year);
+ return highlightString(str, m_pos);
+}
+
+QString QCalendarYearValidator::text(const QDate &date, int repeat) const
+{
+ if (repeat < 4) {
+ QString str;
+ int year = date.year() % 100;
+ if (year / 10 == 0)
+ str = QLatin1String("0");
+ return str + QString::number(year);
+ }
+ return QString::number(date.year());
+}
+
+///////////////////////////////////
+
+class QCalendarDateValidator
+{
+public:
+ QCalendarDateValidator();
+ ~QCalendarDateValidator();
+
+ void handleKeyEvent(QKeyEvent *keyEvent);
+ QString currentText() const;
+ QDate currentDate() const { return m_currentDate; }
+ void setFormat(const QString &format);
+ void setInitialDate(const QDate &date);
+
+ void setLocale(const QLocale &locale);
+
+private:
+
+ struct SectionToken {
+ SectionToken(QCalendarDateSectionValidator *val, int rep) : validator(val), repeat(rep) {}
+ QCalendarDateSectionValidator *validator;
+ int repeat;
+ };
+
+ void toNextToken();
+ void toPreviousToken();
+ void applyToDate();
+
+ int countRepeat(const QString &str, int index) const;
+ void clear();
+
+ QStringList m_separators;
+ QList<SectionToken *> m_tokens;
+ QCalendarDateSectionValidator *m_yearValidator;
+ QCalendarDateSectionValidator *m_monthValidator;
+ QCalendarDateSectionValidator *m_dayValidator;
+
+ SectionToken *m_currentToken;
+
+ QDate m_initialDate;
+ QDate m_currentDate;
+
+ QCalendarDateSectionValidator::Section m_lastSectionMove;
+};
+
+QCalendarDateValidator::QCalendarDateValidator()
+ : m_currentToken(0), m_lastSectionMove(QCalendarDateSectionValidator::ThisSection)
+{
+ m_initialDate = m_currentDate = QDate::currentDate();
+ m_yearValidator = new QCalendarYearValidator();
+ m_monthValidator = new QCalendarMonthValidator();
+ m_dayValidator = new QCalendarDayValidator();
+}
+
+void QCalendarDateValidator::setLocale(const QLocale &locale)
+{
+ m_yearValidator->m_locale = locale;
+ m_monthValidator->m_locale = locale;
+ m_dayValidator->m_locale = locale;
+}
+
+QCalendarDateValidator::~QCalendarDateValidator()
+{
+ delete m_yearValidator;
+ delete m_monthValidator;
+ delete m_dayValidator;
+ clear();
+}
+
+// from qdatetime.cpp
+int QCalendarDateValidator::countRepeat(const QString &str, int index) const
+{
+ Q_ASSERT(index >= 0 && index < str.size());
+ int count = 1;
+ const QChar ch = str.at(index);
+ while (index + count < str.size() && str.at(index + count) == ch)
+ ++count;
+ return count;
+}
+
+void QCalendarDateValidator::setInitialDate(const QDate &date)
+{
+ m_yearValidator->setDate(date);
+ m_monthValidator->setDate(date);
+ m_dayValidator->setDate(date);
+ m_initialDate = date;
+ m_currentDate = date;
+ m_lastSectionMove = QCalendarDateSectionValidator::ThisSection;
+}
+
+QString QCalendarDateValidator::currentText() const
+{
+ QString str;
+ QStringListIterator itSep(m_separators);
+ QListIterator<SectionToken *> itTok(m_tokens);
+ while (itSep.hasNext()) {
+ str += itSep.next();
+ if (itTok.hasNext()) {
+ SectionToken *token = itTok.next();
+ QCalendarDateSectionValidator *validator = token->validator;
+ if (m_currentToken == token)
+ str += validator->text();
+ else
+ str += validator->text(m_currentDate, token->repeat);
+ }
+ }
+ return str;
+}
+
+void QCalendarDateValidator::clear()
+{
+ QListIterator<SectionToken *> it(m_tokens);
+ while (it.hasNext())
+ delete it.next();
+
+ m_tokens.clear();
+ m_separators.clear();
+
+ m_currentToken = 0;
+}
+
+void QCalendarDateValidator::setFormat(const QString &format)
+{
+ clear();
+
+ int pos = 0;
+ const QLatin1String quote("'");
+ bool quoting = false;
+ QString separator;
+ while (pos < format.size()) {
+ QString mid = format.mid(pos);
+ int offset = 1;
+
+ if (mid.startsWith(quote)) {
+ quoting = !quoting;
+ } else {
+ const QChar nextChar = format.at(pos);
+ if (quoting) {
+ separator += nextChar;
+ } else {
+ SectionToken *token = 0;
+ if (nextChar == QLatin1Char('d')) {
+ offset = qMin(4, countRepeat(format, pos));
+ token = new SectionToken(m_dayValidator, offset);
+ } else if (nextChar == QLatin1Char('M')) {
+ offset = qMin(4, countRepeat(format, pos));
+ token = new SectionToken(m_monthValidator, offset);
+ } else if (nextChar == QLatin1Char('y')) {
+ offset = qMin(4, countRepeat(format, pos));
+ token = new SectionToken(m_yearValidator, offset);
+ } else {
+ separator += nextChar;
+ }
+ if (token) {
+ m_tokens.append(token);
+ m_separators.append(separator);
+ separator = QString();
+ if (!m_currentToken)
+ m_currentToken = token;
+
+ }
+ }
+ }
+ pos += offset;
+ }
+ m_separators += separator;
+}
+
+void QCalendarDateValidator::applyToDate()
+{
+ m_currentDate = m_yearValidator->applyToDate(m_currentDate);
+ m_currentDate = m_monthValidator->applyToDate(m_currentDate);
+ m_currentDate = m_dayValidator->applyToDate(m_currentDate);
+}
+
+void QCalendarDateValidator::toNextToken()
+{
+ const int idx = m_tokens.indexOf(m_currentToken);
+ if (idx == -1)
+ return;
+ if (idx + 1 >= m_tokens.count())
+ m_currentToken = m_tokens.first();
+ else
+ m_currentToken = m_tokens.at(idx + 1);
+}
+
+void QCalendarDateValidator::toPreviousToken()
+{
+ const int idx = m_tokens.indexOf(m_currentToken);
+ if (idx == -1)
+ return;
+ if (idx - 1 < 0)
+ m_currentToken = m_tokens.last();
+ else
+ m_currentToken = m_tokens.at(idx - 1);
+}
+
+void QCalendarDateValidator::handleKeyEvent(QKeyEvent *keyEvent)
+{
+ if (!m_currentToken)
+ return;
+
+ int key = keyEvent->key();
+ if (m_lastSectionMove == QCalendarDateSectionValidator::NextSection) {
+ if (key == Qt::Key_Back || key == Qt::Key_Backspace)
+ toPreviousToken();
+ }
+ if (key == Qt::Key_Right)
+ toNextToken();
+ else if (key == Qt::Key_Left)
+ toPreviousToken();
+
+ m_lastSectionMove = m_currentToken->validator->handleKey(key);
+
+ applyToDate();
+ if (m_lastSectionMove == QCalendarDateSectionValidator::NextSection)
+ toNextToken();
+ else if (m_lastSectionMove == QCalendarDateSectionValidator::PrevSection)
+ toPreviousToken();
+}
+
+QWidget *QCalendarTextNavigator::widget() const
+{
+ return m_widget;
+}
+
+void QCalendarTextNavigator::setWidget(QWidget *widget)
+{
+ m_widget = widget;
+}
+
+QDate QCalendarTextNavigator::date() const
+{
+ return m_date;
+}
+
+void QCalendarTextNavigator::setDate(const QDate &date)
+{
+ m_date = date;
+}
+
+void QCalendarTextNavigator::updateDateLabel()
+{
+ if (!m_widget)
+ return;
+
+ m_acceptTimer.start(m_editDelay, this);
+
+ m_dateText->setText(m_dateValidator->currentText());
+
+ QSize s = m_dateFrame->sizeHint();
+ QRect r = m_widget->geometry(); // later, just the table section
+ QRect newRect((r.width() - s.width()) / 2, (r.height() - s.height()) / 2, s.width(), s.height());
+ m_dateFrame->setGeometry(newRect);
+ // need to set palette after geometry update as phonestyle sets transparency
+ // effect in move event.
+ QPalette p = m_dateFrame->palette();
+ p.setBrush(QPalette::Window, m_dateFrame->window()->palette().brush(QPalette::Window));
+ m_dateFrame->setPalette(p);
+
+ m_dateFrame->raise();
+ m_dateFrame->show();
+}
+
+void QCalendarTextNavigator::applyDate()
+{
+ QDate date = m_dateValidator->currentDate();
+ if (m_date == date)
+ return;
+
+ m_date = date;
+ emit dateChanged(date);
+}
+
+void QCalendarTextNavigator::createDateLabel()
+{
+ if (m_dateFrame)
+ return;
+ m_dateFrame = new QFrame(m_widget);
+ QVBoxLayout *vl = new QVBoxLayout;
+ m_dateText = new QLabel;
+ vl->addWidget(m_dateText);
+ m_dateFrame->setLayout(vl);
+ m_dateFrame->setFrameShadow(QFrame::Plain);
+ m_dateFrame->setFrameShape(QFrame::Box);
+ m_dateValidator = new QCalendarDateValidator();
+ m_dateValidator->setLocale(m_widget->locale());
+ m_dateValidator->setFormat(m_widget->locale().dateFormat(QLocale::ShortFormat));
+ m_dateValidator->setInitialDate(m_date);
+
+ m_dateFrame->setAutoFillBackground(true);
+ m_dateFrame->setBackgroundRole(QPalette::Window);
+}
+
+void QCalendarTextNavigator::removeDateLabel()
+{
+ if (!m_dateFrame)
+ return;
+ m_acceptTimer.stop();
+ m_dateFrame->hide();
+ m_dateFrame->deleteLater();
+ delete m_dateValidator;
+ m_dateFrame = 0;
+ m_dateText = 0;
+ m_dateValidator = 0;
+}
+
+bool QCalendarTextNavigator::eventFilter(QObject *o, QEvent *e)
+{
+ if (m_widget) {
+ if (e->type() == QEvent::KeyPress || e->type() == QEvent::KeyRelease) {
+ QKeyEvent* ke = (QKeyEvent*)e;
+ if ((ke->text().length() > 0 && ke->text()[0].isPrint()) || m_dateFrame) {
+ if (ke->key() == Qt::Key_Return || ke->key() == Qt::Key_Enter || ke->key() == Qt::Key_Select) {
+ applyDate();
+ emit editingFinished();
+ removeDateLabel();
+ } else if (ke->key() == Qt::Key_Escape) {
+ removeDateLabel();
+ } else if (e->type() == QEvent::KeyPress) {
+ createDateLabel();
+ m_dateValidator->handleKeyEvent(ke);
+ updateDateLabel();
+ }
+ ke->accept();
+ return true;
+ }
+ // If we are navigating let the user finish his date in old locate.
+ // If we change our mind and want it to update immediately simply uncomment below
+ /*
+ } else if (e->type() == QEvent::LocaleChange) {
+ if (m_dateValidator) {
+ m_dateValidator->setLocale(m_widget->locale());
+ m_dateValidator->setFormat(m_widget->locale().dateFormat(QLocale::ShortFormat));
+ updateDateLabel();
+ }
+ */
+ }
+ }
+ return QObject::eventFilter(o,e);
+}
+
+void QCalendarTextNavigator::timerEvent(QTimerEvent *e)
+{
+ if (e->timerId() == m_acceptTimer.timerId()) {
+ applyDate();
+ removeDateLabel();
+ }
+}
+
+int QCalendarTextNavigator::dateEditAcceptDelay() const
+{
+ return m_editDelay;
+}
+
+void QCalendarTextNavigator::setDateEditAcceptDelay(int delay)
+{
+ m_editDelay = delay;
+}
+
+class QCalendarView;
+
+class QCalendarModel : public QAbstractTableModel
+{
+ Q_OBJECT
+public:
+ QCalendarModel(QObject *parent = 0);
+
+ int rowCount(const QModelIndex &) const
+ { return RowCount + m_firstRow; }
+ int columnCount(const QModelIndex &) const
+ { return ColumnCount + m_firstColumn; }
+ QVariant data(const QModelIndex &index, int role) const;
+ Qt::ItemFlags flags(const QModelIndex &index) const;
+
+ bool insertRows(int row, int count, const QModelIndex &parent = QModelIndex())
+ {
+ beginInsertRows(parent, row, row + count - 1);
+ endInsertRows();
+ return true;
+ }
+ bool insertColumns(int column, int count, const QModelIndex &parent = QModelIndex())
+ {
+ beginInsertColumns(parent, column, column + count - 1);
+ endInsertColumns();
+ return true;
+ }
+ bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex())
+ {
+ beginRemoveRows(parent, row, row + count - 1);
+ endRemoveRows();
+ return true;
+ }
+ bool removeColumns(int column, int count, const QModelIndex &parent = QModelIndex())
+ {
+ beginRemoveColumns(parent, column, column + count - 1);
+ endRemoveColumns();
+ return true;
+ }
+
+ void showMonth(int year, int month);
+ void setDate(const QDate &d);
+
+ void setMinimumDate(const QDate &date);
+ void setMaximumDate(const QDate &date);
+
+ void setRange(const QDate &min, const QDate &max);
+
+ void setHorizontalHeaderFormat(QCalendarWidget::HorizontalHeaderFormat format);
+
+ void setFirstColumnDay(Qt::DayOfWeek dayOfWeek);
+ Qt::DayOfWeek firstColumnDay() const;
+
+ bool weekNumbersShown() const;
+ void setWeekNumbersShown(bool show);
+
+ QTextCharFormat formatForCell(int row, int col) const;
+ Qt::DayOfWeek dayOfWeekForColumn(int section) const;
+ int columnForDayOfWeek(Qt::DayOfWeek day) const;
+ QDate dateForCell(int row, int column) const;
+ void cellForDate(const QDate &date, int *row, int *column) const;
+ QString dayName(Qt::DayOfWeek day) const;
+
+ void setView(QCalendarView *view)
+ { m_view = view; }
+
+ void internalUpdate();
+ QDate referenceDate() const;
+ int columnForFirstOfMonth(const QDate &date) const;
+
+ int m_firstColumn;
+ int m_firstRow;
+ QDate m_date;
+ QDate m_minimumDate;
+ QDate m_maximumDate;
+ int m_shownYear;
+ int m_shownMonth;
+ Qt::DayOfWeek m_firstDay;
+ QCalendarWidget::HorizontalHeaderFormat m_horizontalHeaderFormat;
+ bool m_weekNumbersShown;
+ QMap<Qt::DayOfWeek, QTextCharFormat> m_dayFormats;
+ QMap<QDate, QTextCharFormat> m_dateFormats;
+ QTextCharFormat m_headerFormat;
+ QCalendarView *m_view;
+};
+
+class QCalendarView : public QTableView
+{
+ Q_OBJECT
+public:
+ QCalendarView(QWidget *parent = 0);
+
+ void internalUpdate() { updateGeometries(); }
+ void setReadOnly(bool enable);
+ virtual void keyboardSearch(const QString & search) { Q_UNUSED(search) }
+
+signals:
+ void showDate(const QDate &date);
+ void changeDate(const QDate &date, bool changeMonth);
+ void clicked(const QDate &date);
+ void editingFinished();
+protected:
+ QModelIndex moveCursor(CursorAction cursorAction, Qt::KeyboardModifiers modifiers);
+ void mouseDoubleClickEvent(QMouseEvent *event);
+ void mousePressEvent(QMouseEvent *event);
+ void mouseMoveEvent(QMouseEvent *event);
+ void mouseReleaseEvent(QMouseEvent *event);
+#ifndef QT_NO_WHEELEVENT
+ void wheelEvent(QWheelEvent *event);
+#endif
+ void keyPressEvent(QKeyEvent *event);
+ bool event(QEvent *event);
+
+ QDate handleMouseEvent(QMouseEvent *event);
+public:
+ bool readOnly;
+private:
+ bool validDateClicked;
+#ifdef QT_KEYPAD_NAVIGATION
+ QDate origDate;
+#endif
+};
+
+QCalendarModel::QCalendarModel(QObject *parent)
+ : QAbstractTableModel(parent)
+{
+ m_date = QDate::currentDate();
+ m_minimumDate = QDate::fromJulianDay(1);
+ m_maximumDate = QDate(7999, 12, 31);
+ m_shownYear = m_date.year();
+ m_shownMonth = m_date.month();
+ m_firstDay = Qt::Sunday;
+ m_horizontalHeaderFormat = QCalendarWidget::ShortDayNames;
+ m_weekNumbersShown = true;
+ m_firstColumn = 1;
+ m_firstRow = 1;
+ m_view = 0;
+}
+
+Qt::DayOfWeek QCalendarModel::dayOfWeekForColumn(int column) const
+{
+ int col = column - m_firstColumn;
+ if (col < 0 || col > 6)
+ return Qt::Sunday;
+ int day = m_firstDay + col;
+ if (day > 7)
+ day -= 7;
+ return Qt::DayOfWeek(day);
+}
+
+int QCalendarModel::columnForDayOfWeek(Qt::DayOfWeek day) const
+{
+ if (day < 1 || day > 7)
+ return -1;
+ int column = (int)day - (int)m_firstDay;
+ if (column < 0)
+ column += 7;
+ return column + m_firstColumn;
+}
+
+/*
+This simple algorithm tries to generate a valid date from the month shown.
+Some months don't contain a first day (e.g. Jan of -4713 year,
+so QDate (-4713, 1, 1) would be invalid). In that case we try to generate
+another valid date for that month. Later, returned date's day is the number of cells
+calendar widget will reserve for days before referenceDate. (E.g. if returned date's
+day is 16, that day will be placed in 3rd or 4th row, not in the 1st or 2nd row).
+Depending on referenceData we can change behaviour of Oct 1582. If referenceDate is 1st
+of Oct we render 1 Oct in 1st or 2nd row. If referenceDate is 17 of Oct we show always 16
+dates before 17 of Oct, and since this month contains the hole 5-14 Oct, the first of Oct
+will be rendered in 2nd or 3rd row, showing more dates from previous month.
+*/
+QDate QCalendarModel::referenceDate() const
+{
+ int refDay = 1;
+ while (refDay <= 31) {
+ QDate refDate(m_shownYear, m_shownMonth, refDay);
+ if (refDate.isValid())
+ return refDate;
+ refDay += 1;
+ }
+ return QDate();
+}
+
+int QCalendarModel::columnForFirstOfMonth(const QDate &date) const
+{
+ return (columnForDayOfWeek(static_cast<Qt::DayOfWeek>(date.dayOfWeek())) - (date.day() % 7) + 8) % 7;
+}
+
+QDate QCalendarModel::dateForCell(int row, int column) const
+{
+ if (row < m_firstRow || row > m_firstRow + RowCount - 1 ||
+ column < m_firstColumn || column > m_firstColumn + ColumnCount - 1)
+ return QDate();
+ const QDate refDate = referenceDate();
+ if (!refDate.isValid())
+ return QDate();
+
+ const int columnForFirstOfShownMonth = columnForFirstOfMonth(refDate);
+ if (columnForFirstOfShownMonth - m_firstColumn < MinimumDayOffset)
+ row -= 1;
+
+ const int requestedDay = 7 * (row - m_firstRow) + column - columnForFirstOfShownMonth - refDate.day() + 1;
+ return refDate.addDays(requestedDay);
+}
+
+void QCalendarModel::cellForDate(const QDate &date, int *row, int *column) const
+{
+ if (!row && !column)
+ return;
+
+ if (row)
+ *row = -1;
+ if (column)
+ *column = -1;
+
+ const QDate refDate = referenceDate();
+ if (!refDate.isValid())
+ return;
+
+ const int columnForFirstOfShownMonth = columnForFirstOfMonth(refDate);
+ const int requestedPosition = refDate.daysTo(date) - m_firstColumn + columnForFirstOfShownMonth + refDate.day() - 1;
+
+ int c = requestedPosition % 7;
+ int r = requestedPosition / 7;
+ if (c < 0) {
+ c += 7;
+ r -= 1;
+ }
+
+ if (columnForFirstOfShownMonth - m_firstColumn < MinimumDayOffset)
+ r += 1;
+
+ if (r < 0 || r > RowCount - 1 || c < 0 || c > ColumnCount - 1)
+ return;
+
+ if (row)
+ *row = r + m_firstRow;
+ if (column)
+ *column = c + m_firstColumn;
+}
+
+QString QCalendarModel::dayName(Qt::DayOfWeek day) const
+{
+ switch (m_horizontalHeaderFormat) {
+ case QCalendarWidget::SingleLetterDayNames: {
+ QString standaloneDayName = m_view->locale().standaloneDayName(day, QLocale::NarrowFormat);
+ if (standaloneDayName == m_view->locale().dayName(day, QLocale::NarrowFormat))
+ return standaloneDayName.left(1);
+ return standaloneDayName;
+ }
+ case QCalendarWidget::ShortDayNames:
+ return m_view->locale().dayName(day, QLocale::ShortFormat);
+ case QCalendarWidget::LongDayNames:
+ return m_view->locale().dayName(day, QLocale::LongFormat);
+ default:
+ break;
+ }
+ return QString();
+}
+
+QTextCharFormat QCalendarModel::formatForCell(int row, int col) const
+{
+ QPalette pal;
+ QPalette::ColorGroup cg = QPalette::Active;
+ if (m_view) {
+ pal = m_view->palette();
+ if (!m_view->isEnabled())
+ cg = QPalette::Disabled;
+ else if (!m_view->isActiveWindow())
+ cg = QPalette::Inactive;
+ }
+
+ QTextCharFormat format;
+ format.setFont(m_view->font());
+ bool header = (m_weekNumbersShown && col == HeaderColumn)
+ || (m_horizontalHeaderFormat != QCalendarWidget::NoHorizontalHeader && row == HeaderRow);
+ format.setBackground(pal.brush(cg, header ? QPalette::AlternateBase : QPalette::Base));
+ format.setForeground(pal.brush(cg, QPalette::Text));
+ if (header) {
+ format.merge(m_headerFormat);
+ }
+
+ if (col >= m_firstColumn && col < m_firstColumn + ColumnCount) {
+ Qt::DayOfWeek dayOfWeek = dayOfWeekForColumn(col);
+ if (m_dayFormats.contains(dayOfWeek))
+ format.merge(m_dayFormats.value(dayOfWeek));
+ }
+
+ if (!header) {
+ QDate date = dateForCell(row, col);
+ format.merge(m_dateFormats.value(date));
+ if(date < m_minimumDate || date > m_maximumDate)
+ format.setBackground(pal.brush(cg, QPalette::Window));
+ if (m_shownMonth != date.month())
+ format.setForeground(pal.brush(QPalette::Disabled, QPalette::Text));
+ }
+ return format;
+}
+
+QVariant QCalendarModel::data(const QModelIndex &index, int role) const
+{
+ if (role == Qt::TextAlignmentRole)
+ return (int) Qt::AlignCenter;
+
+ int row = index.row();
+ int column = index.column();
+
+ if(role == Qt::DisplayRole) {
+ if (m_weekNumbersShown && column == HeaderColumn
+ && row >= m_firstRow && row < m_firstRow + RowCount) {
+ QDate date = dateForCell(row, columnForDayOfWeek(Qt::Monday));
+ if (date.isValid())
+ return date.weekNumber();
+ }
+ if (m_horizontalHeaderFormat != QCalendarWidget::NoHorizontalHeader && row == HeaderRow
+ && column >= m_firstColumn && column < m_firstColumn + ColumnCount)
+ return dayName(dayOfWeekForColumn(column));
+ QDate date = dateForCell(row, column);
+ if (date.isValid())
+ return date.day();
+ return QString();
+ }
+
+ QTextCharFormat fmt = formatForCell(row, column);
+ if (role == Qt::BackgroundColorRole)
+ return fmt.background().color();
+ if (role == Qt::TextColorRole)
+ return fmt.foreground().color();
+ if (role == Qt::FontRole)
+ return fmt.font();
+ if (role == Qt::ToolTipRole)
+ return fmt.toolTip();
+ return QVariant();
+}
+
+Qt::ItemFlags QCalendarModel::flags(const QModelIndex &index) const
+{
+ QDate date = dateForCell(index.row(), index.column());
+ if (!date.isValid())
+ return QAbstractTableModel::flags(index);
+ if (date < m_minimumDate)
+ return 0;
+ if (date > m_maximumDate)
+ return 0;
+ return QAbstractTableModel::flags(index);
+}
+
+void QCalendarModel::setDate(const QDate &d)
+{
+ m_date = d;
+ if (m_date < m_minimumDate)
+ m_date = m_minimumDate;
+ else if (m_date > m_maximumDate)
+ m_date = m_maximumDate;
+}
+
+void QCalendarModel::showMonth(int year, int month)
+{
+ if (m_shownYear == year && m_shownMonth == month)
+ return;
+
+ m_shownYear = year;
+ m_shownMonth = month;
+
+ internalUpdate();
+}
+
+void QCalendarModel::setMinimumDate(const QDate &d)
+{
+ if (!d.isValid() || d == m_minimumDate)
+ return;
+
+ m_minimumDate = d;
+ if (m_maximumDate < m_minimumDate)
+ m_maximumDate = m_minimumDate;
+ if (m_date < m_minimumDate)
+ m_date = m_minimumDate;
+ internalUpdate();
+}
+
+void QCalendarModel::setMaximumDate(const QDate &d)
+{
+ if (!d.isValid() || d == m_maximumDate)
+ return;
+
+ m_maximumDate = d;
+ if (m_minimumDate > m_maximumDate)
+ m_minimumDate = m_maximumDate;
+ if (m_date > m_maximumDate)
+ m_date = m_maximumDate;
+ internalUpdate();
+}
+
+void QCalendarModel::setRange(const QDate &min, const QDate &max)
+{
+ m_minimumDate = min;
+ m_maximumDate = max;
+ if (m_minimumDate > m_maximumDate)
+ qSwap(m_minimumDate, m_maximumDate);
+ if (m_date < m_minimumDate)
+ m_date = m_minimumDate;
+ if (m_date > m_maximumDate)
+ m_date = m_maximumDate;
+ internalUpdate();
+}
+
+void QCalendarModel::internalUpdate()
+{
+ QModelIndex begin = index(0, 0);
+ QModelIndex end = index(m_firstRow + RowCount - 1, m_firstColumn + ColumnCount - 1);
+ emit dataChanged(begin, end);
+ emit headerDataChanged(Qt::Vertical, 0, m_firstRow + RowCount - 1);
+ emit headerDataChanged(Qt::Horizontal, 0, m_firstColumn + ColumnCount - 1);
+}
+
+void QCalendarModel::setHorizontalHeaderFormat(QCalendarWidget::HorizontalHeaderFormat format)
+{
+ if (m_horizontalHeaderFormat == format)
+ return;
+
+ int oldFormat = m_horizontalHeaderFormat;
+ m_horizontalHeaderFormat = format;
+ if (oldFormat == QCalendarWidget::NoHorizontalHeader) {
+ m_firstRow = 1;
+ insertRow(0);
+ } else if (m_horizontalHeaderFormat == QCalendarWidget::NoHorizontalHeader) {
+ m_firstRow = 0;
+ removeRow(0);
+ }
+ internalUpdate();
+}
+
+void QCalendarModel::setFirstColumnDay(Qt::DayOfWeek dayOfWeek)
+{
+ if (m_firstDay == dayOfWeek)
+ return;
+
+ m_firstDay = dayOfWeek;
+ internalUpdate();
+}
+
+Qt::DayOfWeek QCalendarModel::firstColumnDay() const
+{
+ return m_firstDay;
+}
+
+bool QCalendarModel::weekNumbersShown() const
+{
+ return m_weekNumbersShown;
+}
+
+void QCalendarModel::setWeekNumbersShown(bool show)
+{
+ if (m_weekNumbersShown == show)
+ return;
+
+ m_weekNumbersShown = show;
+ if (show) {
+ m_firstColumn = 1;
+ insertColumn(0);
+ } else {
+ m_firstColumn = 0;
+ removeColumn(0);
+ }
+ internalUpdate();
+}
+
+QCalendarView::QCalendarView(QWidget *parent)
+ : QTableView(parent),
+ readOnly(false),
+ validDateClicked(false)
+{
+ setTabKeyNavigation(false);
+ setShowGrid(false);
+ verticalHeader()->setVisible(false);
+ horizontalHeader()->setVisible(false);
+ setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
+ setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
+}
+
+QModelIndex QCalendarView::moveCursor(CursorAction cursorAction, Qt::KeyboardModifiers modifiers)
+{
+ QCalendarModel *calendarModel = qobject_cast<QCalendarModel *>(model());
+ if (!calendarModel)
+ return QTableView::moveCursor(cursorAction, modifiers);
+
+ if (readOnly)
+ return currentIndex();
+
+ QModelIndex index = currentIndex();
+ QDate currentDate = static_cast<QCalendarModel*>(model())->dateForCell(index.row(), index.column());
+ switch (cursorAction) {
+ case QAbstractItemView::MoveUp:
+ currentDate = currentDate.addDays(-7);
+ break;
+ case QAbstractItemView::MoveDown:
+ currentDate = currentDate.addDays(7);
+ break;
+ case QAbstractItemView::MoveLeft:
+ currentDate = currentDate.addDays(isRightToLeft() ? 1 : -1);
+ break;
+ case QAbstractItemView::MoveRight:
+ currentDate = currentDate.addDays(isRightToLeft() ? -1 : 1);
+ break;
+ case QAbstractItemView::MoveHome:
+ currentDate = QDate(currentDate.year(), currentDate.month(), 1);
+ break;
+ case QAbstractItemView::MoveEnd:
+ currentDate = QDate(currentDate.year(), currentDate.month(), currentDate.daysInMonth());
+ break;
+ case QAbstractItemView::MovePageUp:
+ currentDate = currentDate.addMonths(-1);
+ break;
+ case QAbstractItemView::MovePageDown:
+ currentDate = currentDate.addMonths(1);
+ break;
+ case QAbstractItemView::MoveNext:
+ case QAbstractItemView::MovePrevious:
+ return currentIndex();
+ default:
+ break;
+ }
+ emit changeDate(currentDate, true);
+ return currentIndex();
+}
+
+void QCalendarView::keyPressEvent(QKeyEvent *event)
+{
+#ifdef QT_KEYPAD_NAVIGATION
+ if (event->key() == Qt::Key_Select) {
+ if (QApplication::keypadNavigationEnabled()) {
+ if (!hasEditFocus()) {
+ setEditFocus(true);
+ return;
+ }
+ }
+ } else if (event->key() == Qt::Key_Back) {
+ if (QApplication::keypadNavigationEnabled() && hasEditFocus()) {
+ if (qobject_cast<QCalendarModel *>(model())) {
+ emit changeDate(origDate, true); //changes selection back to origDate, but doesn't activate
+ setEditFocus(false);
+ return;
+ }
+ }
+ }
+#endif
+
+ if (!readOnly) {
+ switch (event->key()) {
+ case Qt::Key_Return:
+ case Qt::Key_Enter:
+ case Qt::Key_Select:
+ emit editingFinished();
+ return;
+ default:
+ break;
+ }
+ }
+ QTableView::keyPressEvent(event);
+}
+
+#ifndef QT_NO_WHEELEVENT
+void QCalendarView::wheelEvent(QWheelEvent *event)
+{
+ const int numDegrees = event->delta() / 8;
+ const int numSteps = numDegrees / 15;
+ const QModelIndex index = currentIndex();
+ QDate currentDate = static_cast<QCalendarModel*>(model())->dateForCell(index.row(), index.column());
+ currentDate = currentDate.addMonths(-numSteps);
+ emit showDate(currentDate);
+}
+#endif
+
+bool QCalendarView::event(QEvent *event)
+{
+#ifdef QT_KEYPAD_NAVIGATION
+ if (event->type() == QEvent::FocusIn) {
+ if (QCalendarModel *calendarModel = qobject_cast<QCalendarModel *>(model())) {
+ origDate = calendarModel->m_date;
+ }
+ }
+#endif
+
+ return QTableView::event(event);
+}
+
+QDate QCalendarView::handleMouseEvent(QMouseEvent *event)
+{
+ QCalendarModel *calendarModel = qobject_cast<QCalendarModel *>(model());
+ if (!calendarModel)
+ return QDate();
+
+ QPoint pos = event->pos();
+ QModelIndex index = indexAt(pos);
+ QDate date = calendarModel->dateForCell(index.row(), index.column());
+ if (date.isValid() && date >= calendarModel->m_minimumDate
+ && date <= calendarModel->m_maximumDate) {
+ return date;
+ }
+ return QDate();
+}
+
+void QCalendarView::mouseDoubleClickEvent(QMouseEvent *event)
+{
+ QCalendarModel *calendarModel = qobject_cast<QCalendarModel *>(model());
+ if (!calendarModel) {
+ QTableView::mouseDoubleClickEvent(event);
+ return;
+ }
+
+ if (readOnly)
+ return;
+
+ QDate date = handleMouseEvent(event);
+ validDateClicked = false;
+ if (date == calendarModel->m_date && !style()->styleHint(QStyle::SH_ItemView_ActivateItemOnSingleClick)) {
+ emit editingFinished();
+ }
+}
+
+void QCalendarView::mousePressEvent(QMouseEvent *event)
+{
+ QCalendarModel *calendarModel = qobject_cast<QCalendarModel *>(model());
+ if (!calendarModel) {
+ QTableView::mousePressEvent(event);
+ return;
+ }
+
+ if (readOnly)
+ return;
+
+ if (event->button() != Qt::LeftButton)
+ return;
+
+ QDate date = handleMouseEvent(event);
+ if (date.isValid()) {
+ validDateClicked = true;
+ int row = -1, col = -1;
+ static_cast<QCalendarModel *>(model())->cellForDate(date, &row, &col);
+ if (row != -1 && col != -1) {
+ selectionModel()->setCurrentIndex(model()->index(row, col), QItemSelectionModel::NoUpdate);
+ }
+ } else {
+ validDateClicked = false;
+ event->ignore();
+ }
+}
+
+void QCalendarView::mouseMoveEvent(QMouseEvent *event)
+{
+ QCalendarModel *calendarModel = qobject_cast<QCalendarModel *>(model());
+ if (!calendarModel) {
+ QTableView::mouseMoveEvent(event);
+ return;
+ }
+
+ if (readOnly)
+ return;
+
+ if (validDateClicked) {
+ QDate date = handleMouseEvent(event);
+ if (date.isValid()) {
+ int row = -1, col = -1;
+ static_cast<QCalendarModel *>(model())->cellForDate(date, &row, &col);
+ if (row != -1 && col != -1) {
+ selectionModel()->setCurrentIndex(model()->index(row, col), QItemSelectionModel::NoUpdate);
+ }
+ }
+ } else {
+ event->ignore();
+ }
+}
+
+void QCalendarView::mouseReleaseEvent(QMouseEvent *event)
+{
+ QCalendarModel *calendarModel = qobject_cast<QCalendarModel *>(model());
+ if (!calendarModel) {
+ QTableView::mouseReleaseEvent(event);
+ return;
+ }
+
+ if (event->button() != Qt::LeftButton)
+ return;
+
+ if (readOnly)
+ return;
+
+ if (validDateClicked) {
+ QDate date = handleMouseEvent(event);
+ if (date.isValid()) {
+ emit changeDate(date, true);
+ emit clicked(date);
+ if (style()->styleHint(QStyle::SH_ItemView_ActivateItemOnSingleClick))
+ emit editingFinished();
+ }
+ validDateClicked = false;
+ } else {
+ event->ignore();
+ }
+}
+
+class QCalendarDelegate : public QItemDelegate
+{
+ Q_OBJECT
+public:
+ QCalendarDelegate(QCalendarWidgetPrivate *w, QObject *parent = 0)
+ : QItemDelegate(parent), calendarWidgetPrivate(w)
+ { }
+ virtual void paint(QPainter *painter, const QStyleOptionViewItem &option,
+ const QModelIndex &index) const;
+ void paintCell(QPainter *painter, const QRect &rect, const QDate &date) const;
+
+private:
+ QCalendarWidgetPrivate *calendarWidgetPrivate;
+ mutable QStyleOptionViewItemV4 storedOption;
+};
+
+//Private tool button class
+class QCalToolButton: public QToolButton
+{
+public:
+ QCalToolButton(QWidget * parent)
+ : QToolButton(parent)
+ { }
+protected:
+ void paintEvent(QPaintEvent *e)
+ {
+ Q_UNUSED(e)
+
+#ifndef Q_WS_MAC
+ QStyleOptionToolButton opt;
+ initStyleOption(&opt);
+
+ if (opt.state & QStyle::State_MouseOver || isDown()) {
+ //act as normal button
+ setPalette(QPalette());
+ } else {
+ //set the highlight color for button text
+ QPalette toolPalette = palette();
+ toolPalette.setColor(QPalette::ButtonText, toolPalette.color(QPalette::HighlightedText));
+ setPalette(toolPalette);
+ }
+#endif
+ QToolButton::paintEvent(e);
+ }
+};
+
+class QPrevNextCalButton : public QToolButton
+{
+ Q_OBJECT
+public:
+ QPrevNextCalButton(QWidget *parent) : QToolButton(parent) {}
+protected:
+ void paintEvent(QPaintEvent *) {
+ QStylePainter painter(this);
+ QStyleOptionToolButton opt;
+ initStyleOption(&opt);
+ opt.state &= ~QStyle::State_HasFocus;
+ painter.drawComplexControl(QStyle::CC_ToolButton, opt);
+ }
+};
+
+class QCalendarWidgetPrivate : public QWidgetPrivate
+{
+ Q_DECLARE_PUBLIC(QCalendarWidget)
+public:
+ QCalendarWidgetPrivate();
+
+ void showMonth(int year, int month);
+ void update();
+ void paintCell(QPainter *painter, const QRect &rect, const QDate &date) const;
+
+ void _q_slotShowDate(const QDate &date);
+ void _q_slotChangeDate(const QDate &date);
+ void _q_slotChangeDate(const QDate &date, bool changeMonth);
+ void _q_editingFinished();
+ void _q_monthChanged(QAction*);
+ void _q_prevMonthClicked();
+ void _q_nextMonthClicked();
+ void _q_yearEditingFinished();
+ void _q_yearClicked();
+
+ void createNavigationBar(QWidget *widget);
+ void updateButtonIcons();
+ void updateMonthMenu();
+ void updateMonthMenuNames();
+ void updateNavigationBar();
+ void updateCurrentPage(const QDate &newDate);
+ inline QDate getCurrentDate();
+ void setNavigatorEnabled(bool enable);
+
+ QCalendarModel *m_model;
+ QCalendarView *m_view;
+ QCalendarDelegate *m_delegate;
+ QItemSelectionModel *m_selection;
+ QCalendarTextNavigator *m_navigator;
+ bool m_dateEditEnabled;
+
+ QToolButton *nextMonth;
+ QToolButton *prevMonth;
+ QCalToolButton *monthButton;
+ QMenu *monthMenu;
+ QMap<int, QAction *> monthToAction;
+ QCalToolButton *yearButton;
+ QSpinBox *yearEdit;
+ QWidget *navBarBackground;
+ QSpacerItem *spaceHolder;
+
+ bool navBarVisible;
+ mutable QSize cachedSizeHint;
+ Qt::FocusPolicy oldFocusPolicy;
+};
+
+void QCalendarDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option,
+ const QModelIndex &index) const
+{
+ QDate date = calendarWidgetPrivate->m_model->dateForCell(index.row(), index.column());
+ if (date.isValid()) {
+ storedOption = option;
+ QRect rect = option.rect;
+ calendarWidgetPrivate->paintCell(painter, rect, date);
+ } else {
+ QItemDelegate::paint(painter, option, index);
+ }
+}
+
+void QCalendarDelegate::paintCell(QPainter *painter, const QRect &rect, const QDate &date) const
+{
+ storedOption.rect = rect;
+ int row = -1;
+ int col = -1;
+ calendarWidgetPrivate->m_model->cellForDate(date, &row, &col);
+ QModelIndex idx = calendarWidgetPrivate->m_model->index(row, col);
+ QItemDelegate::paint(painter, storedOption, idx);
+}
+
+QCalendarWidgetPrivate::QCalendarWidgetPrivate()
+ : QWidgetPrivate()
+{
+ m_model = 0;
+ m_view = 0;
+ m_delegate = 0;
+ m_selection = 0;
+ m_navigator = 0;
+ m_dateEditEnabled = false;
+ navBarVisible = true;
+ oldFocusPolicy = Qt::StrongFocus;
+}
+
+void QCalendarWidgetPrivate::setNavigatorEnabled(bool enable)
+{
+ Q_Q(QCalendarWidget);
+
+ bool navigatorEnabled = (m_navigator->widget() != 0);
+ if (enable == navigatorEnabled)
+ return;
+
+ if (enable) {
+ m_navigator->setWidget(q);
+ q->connect(m_navigator, SIGNAL(dateChanged(QDate)),
+ q, SLOT(_q_slotChangeDate(QDate)));
+ q->connect(m_navigator, SIGNAL(editingFinished()),
+ q, SLOT(_q_editingFinished()));
+ m_view->installEventFilter(m_navigator);
+ } else {
+ m_navigator->setWidget(0);
+ q->disconnect(m_navigator, SIGNAL(dateChanged(QDate)),
+ q, SLOT(_q_slotChangeDate(QDate)));
+ q->disconnect(m_navigator, SIGNAL(editingFinished()),
+ q, SLOT(_q_editingFinished()));
+ m_view->removeEventFilter(m_navigator);
+ }
+}
+
+void QCalendarWidgetPrivate::createNavigationBar(QWidget *widget)
+{
+ Q_Q(QCalendarWidget);
+ navBarBackground = new QWidget(widget);
+ navBarBackground->setObjectName(QLatin1String("qt_calendar_navigationbar"));
+ navBarBackground->setAutoFillBackground(true);
+ navBarBackground->setBackgroundRole(QPalette::Highlight);
+
+ prevMonth = new QPrevNextCalButton(navBarBackground);
+ nextMonth = new QPrevNextCalButton(navBarBackground);
+ prevMonth->setAutoRaise(true);
+ nextMonth->setAutoRaise(true);
+ prevMonth->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Minimum);
+ nextMonth->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Minimum);
+ nextMonth->setAutoRaise(true);
+ updateButtonIcons();
+ prevMonth->setAutoRepeat(true);
+ nextMonth->setAutoRepeat(true);
+
+ monthButton = new QCalToolButton(navBarBackground);
+ monthButton->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Minimum);
+ monthButton->setAutoRaise(true);
+ monthButton->setPopupMode(QToolButton::InstantPopup);
+ monthMenu = new QMenu(monthButton);
+ for (int i = 1; i <= 12; i++) {
+ QString monthName(q->locale().standaloneMonthName(i, QLocale::LongFormat));
+ QAction *act = monthMenu->addAction(monthName);
+ act->setData(i);
+ monthToAction[i] = act;
+ }
+ monthButton->setMenu(monthMenu);
+ yearButton = new QCalToolButton(navBarBackground);
+ yearButton->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Minimum);
+ yearButton->setAutoRaise(true);
+ yearEdit = new QSpinBox(navBarBackground);
+
+ QFont font = q->font();
+ font.setBold(true);
+ monthButton->setFont(font);
+ yearButton->setFont(font);
+ yearEdit->setFrame(false);
+ yearEdit->setMinimum(m_model->m_minimumDate.year());
+ yearEdit->setMaximum(m_model->m_maximumDate.year());
+ yearEdit->hide();
+ spaceHolder = new QSpacerItem(0,0);
+
+ QHBoxLayout *headerLayout = new QHBoxLayout;
+ headerLayout->setMargin(0);
+ headerLayout->setSpacing(0);
+ headerLayout->addWidget(prevMonth);
+ headerLayout->insertStretch(headerLayout->count());
+ headerLayout->addWidget(monthButton);
+ headerLayout->addItem(spaceHolder);
+ headerLayout->addWidget(yearButton);
+ headerLayout->insertStretch(headerLayout->count());
+ headerLayout->addWidget(nextMonth);
+ navBarBackground->setLayout(headerLayout);
+
+ yearEdit->setFocusPolicy(Qt::StrongFocus);
+ prevMonth->setFocusPolicy(Qt::NoFocus);
+ nextMonth->setFocusPolicy(Qt::NoFocus);
+ yearButton->setFocusPolicy(Qt::NoFocus);
+ monthButton->setFocusPolicy(Qt::NoFocus);
+
+ //set names for the header controls.
+ prevMonth->setObjectName(QLatin1String("qt_calendar_prevmonth"));
+ nextMonth->setObjectName(QLatin1String("qt_calendar_nextmonth"));
+ monthButton->setObjectName(QLatin1String("qt_calendar_monthbutton"));
+ yearButton->setObjectName(QLatin1String("qt_calendar_yearbutton"));
+ yearEdit->setObjectName(QLatin1String("qt_calendar_yearedit"));
+
+ updateMonthMenu();
+ showMonth(m_model->m_date.year(), m_model->m_date.month());
+}
+
+void QCalendarWidgetPrivate::updateButtonIcons()
+{
+ Q_Q(QCalendarWidget);
+ prevMonth->setIcon(q->style()->standardIcon(q->isRightToLeft() ? QStyle::SP_ArrowRight : QStyle::SP_ArrowLeft, 0, q));
+ nextMonth->setIcon(q->style()->standardIcon(q->isRightToLeft() ? QStyle::SP_ArrowLeft : QStyle::SP_ArrowRight, 0, q));
+}
+
+void QCalendarWidgetPrivate::updateMonthMenu()
+{
+ int beg = 1, end = 12;
+ bool prevEnabled = true;
+ bool nextEnabled = true;
+ if (m_model->m_shownYear == m_model->m_minimumDate.year()) {
+ beg = m_model->m_minimumDate.month();
+ if (m_model->m_shownMonth == m_model->m_minimumDate.month())
+ prevEnabled = false;
+ }
+ if (m_model->m_shownYear == m_model->m_maximumDate.year()) {
+ end = m_model->m_maximumDate.month();
+ if (m_model->m_shownMonth == m_model->m_maximumDate.month())
+ nextEnabled = false;
+ }
+ prevMonth->setEnabled(prevEnabled);
+ nextMonth->setEnabled(nextEnabled);
+ for (int i = 1; i <= 12; i++) {
+ bool monthEnabled = true;
+ if (i < beg || i > end)
+ monthEnabled = false;
+ monthToAction[i]->setEnabled(monthEnabled);
+ }
+}
+
+void QCalendarWidgetPrivate::updateMonthMenuNames()
+{
+ Q_Q(QCalendarWidget);
+
+ for (int i = 1; i <= 12; i++) {
+ QString monthName(q->locale().standaloneMonthName(i, QLocale::LongFormat));
+ monthToAction[i]->setText(monthName);
+ }
+}
+
+void QCalendarWidgetPrivate::updateCurrentPage(const QDate &date)
+{
+ Q_Q(QCalendarWidget);
+
+ QDate newDate = date;
+ QDate minDate = q->minimumDate();
+ QDate maxDate = q->maximumDate();
+ if (minDate.isValid()&& minDate.daysTo(newDate) < 0)
+ newDate = minDate;
+ if (maxDate.isValid()&& maxDate.daysTo(newDate) > 0)
+ newDate = maxDate;
+ showMonth(newDate.year(), newDate.month());
+ int row = -1, col = -1;
+ m_model->cellForDate(newDate, &row, &col);
+ if (row != -1 && col != -1)
+ {
+ m_view->selectionModel()->setCurrentIndex(m_model->index(row, col),
+ QItemSelectionModel::NoUpdate);
+ }
+}
+
+void QCalendarWidgetPrivate::_q_monthChanged(QAction *act)
+{
+ monthButton->setText(act->text());
+ QDate currentDate = getCurrentDate();
+ QDate newDate = currentDate.addMonths(act->data().toInt()-currentDate.month());
+ updateCurrentPage(newDate);
+}
+
+QDate QCalendarWidgetPrivate::getCurrentDate()
+{
+ QModelIndex index = m_view->currentIndex();
+ return m_model->dateForCell(index.row(), index.column());
+}
+
+void QCalendarWidgetPrivate::_q_prevMonthClicked()
+{
+ QDate currentDate = getCurrentDate().addMonths(-1);
+ updateCurrentPage(currentDate);
+}
+
+void QCalendarWidgetPrivate::_q_nextMonthClicked()
+{
+ QDate currentDate = getCurrentDate().addMonths(1);
+ updateCurrentPage(currentDate);
+}
+
+void QCalendarWidgetPrivate::_q_yearEditingFinished()
+{
+ Q_Q(QCalendarWidget);
+ yearButton->setText(yearEdit->text());
+ yearEdit->hide();
+ q->setFocusPolicy(oldFocusPolicy);
+ qApp->removeEventFilter(q);
+ spaceHolder->changeSize(0, 0);
+ yearButton->show();
+ QDate currentDate = getCurrentDate();
+ currentDate = currentDate.addYears(yearEdit->text().toInt() - currentDate.year());
+ updateCurrentPage(currentDate);
+}
+
+void QCalendarWidgetPrivate::_q_yearClicked()
+{
+ Q_Q(QCalendarWidget);
+ //show the spinbox on top of the button
+ yearEdit->setGeometry(yearButton->x(), yearButton->y(),
+ yearEdit->sizeHint().width(), yearButton->height());
+ spaceHolder->changeSize(yearButton->width(), 0);
+ yearButton->hide();
+ oldFocusPolicy = q->focusPolicy();
+ q->setFocusPolicy(Qt::NoFocus);
+ yearEdit->show();
+ qApp->installEventFilter(q);
+ yearEdit->raise();
+ yearEdit->selectAll();
+ yearEdit->setFocus(Qt::MouseFocusReason);
+}
+
+void QCalendarWidgetPrivate::showMonth(int year, int month)
+{
+ if (m_model->m_shownYear == year && m_model->m_shownMonth == month)
+ return;
+ Q_Q(QCalendarWidget);
+ m_model->showMonth(year, month);
+ updateNavigationBar();
+ emit q->currentPageChanged(year, month);
+ m_view->internalUpdate();
+ cachedSizeHint = QSize();
+ update();
+ updateMonthMenu();
+}
+
+void QCalendarWidgetPrivate::updateNavigationBar()
+{
+ Q_Q(QCalendarWidget);
+
+ QString monthName = q->locale().standaloneMonthName(m_model->m_shownMonth, QLocale::LongFormat);
+
+ monthButton->setText(monthName);
+ yearButton->setText(QString::number(m_model->m_shownYear));
+ yearEdit->setValue(m_model->m_shownYear);
+}
+
+void QCalendarWidgetPrivate::update()
+{
+ QDate currentDate = m_model->m_date;
+ int row, column;
+ m_model->cellForDate(currentDate, &row, &column);
+ QModelIndex idx;
+ m_selection->clear();
+ if (row != -1 && column != -1) {
+ idx = m_model->index(row, column);
+ m_selection->setCurrentIndex(idx, QItemSelectionModel::SelectCurrent);
+ }
+}
+
+void QCalendarWidgetPrivate::paintCell(QPainter *painter, const QRect &rect, const QDate &date) const
+{
+ Q_Q(const QCalendarWidget);
+ q->paintCell(painter, rect, date);
+}
+
+void QCalendarWidgetPrivate::_q_slotShowDate(const QDate &date)
+{
+ updateCurrentPage(date);
+}
+
+void QCalendarWidgetPrivate::_q_slotChangeDate(const QDate &date)
+{
+ _q_slotChangeDate(date, true);
+}
+
+void QCalendarWidgetPrivate::_q_slotChangeDate(const QDate &date, bool changeMonth)
+{
+ QDate oldDate = m_model->m_date;
+ m_model->setDate(date);
+ QDate newDate = m_model->m_date;
+ if (changeMonth)
+ showMonth(newDate.year(), newDate.month());
+ if (oldDate != newDate) {
+ update();
+ Q_Q(QCalendarWidget);
+ m_navigator->setDate(newDate);
+ emit q->selectionChanged();
+ }
+}
+
+void QCalendarWidgetPrivate::_q_editingFinished()
+{
+ Q_Q(QCalendarWidget);
+ emit q->activated(m_model->m_date);
+}
+
+/*!
+ \class QCalendarWidget
+ \brief The QCalendarWidget class provides a monthly based
+ calendar widget allowing the user to select a date.
+ \since 4.2
+ \mainclass
+ \ingroup advanced
+
+ \image cleanlooks-calendarwidget.png
+
+ The widget is initialized with the current month and year, but
+ QCalendarWidget provides several public slots to change the year
+ and month that is shown. The currently displayed month and year
+ can be retrieved using the currentPageMonth() and currentPageYear()
+ functions, respectively.
+
+ By default, today's date is selected, and the user can select a
+ date using both mouse and keyboard. The currently selected date
+ can be retrieved using the selectedDate() function. It is
+ possible to constrain the user selection to a given date range by
+ setting the minimumDate and maximumDate properties.
+ Alternatively, both properties can be set in one go using the
+ setDateRange() convenience slot. Set the \l selectionMode
+ property to NoSelection to prohibit the user from selecting at
+ all. Note that a date also can be selected programmatically using
+ the setSelectedDate() slot.
+
+ A newly created calendar widget uses abbreviated day names, and
+ both Saturdays and Sundays are marked in red. The calendar grid is
+ not visible. The week numbers are displayed, and the first column
+ day is Sunday.
+
+ The notation of the days can be altered to a single letter
+ abbreviations ("M" for "Monday") by setting the
+ horizontalHeaderFormat property to
+ QCalendarWidget::SingleLetterDayNames. Setting the same property
+ to QCalendarWidget::LongDayNames makes the header display the
+ complete day names. The week numbers can be removed by setting
+ the verticalHeaderFormat property to
+ QCalendarWidget::NoVerticalHeader. The calendar grid can be
+ turned on by setting the gridVisible property to true using the
+ setGridVisible() function:
+
+ \table
+ \row \o
+ \image qcalendarwidget-grid.png
+ \row \o
+ \snippet doc/src/snippets/code/src_gui_widgets_qcalendarwidget.cpp 0
+ \endtable
+
+ Finally, the day in the first column can be altered using the
+ setFirstDayOfWeek() function.
+
+ The QCalendarWidget class also provides three signals,
+ selectionChanged(), activated() and currentPageChanged() making it
+ possible to respond to user interaction.
+
+ The rendering of the headers, weekdays or single days can be
+ largely customized by setting QTextCharFormat's for some special
+ weekday, a special date or for the rendering of the headers.
+
+ Only a subset of the properties in QTextCharFormat are used by the
+ calendar widget. Currently, the foreground, background and font
+ properties are used to determine the rendering of individual cells
+ in the widget.
+
+ \sa QDate, QDateEdit, QTextCharFormat
+*/
+
+/*!
+ \enum QCalendarWidget::SelectionMode
+
+ This enum describes the types of selection offered to the user for
+ selecting dates in the calendar.
+
+ \value NoSelection Dates cannot be selected.
+ \value SingleSelection Single dates can be selected.
+
+ \sa selectionMode
+*/
+
+/*!
+ Constructs a calendar widget with the given \a parent.
+
+ The widget is initialized with the current month and year, and the
+ currently selected date is today.
+
+ \sa setCurrentPage()
+*/
+QCalendarWidget::QCalendarWidget(QWidget *parent)
+ : QWidget(*new QCalendarWidgetPrivate, parent, 0)
+{
+ Q_D(QCalendarWidget);
+
+ setAutoFillBackground(true);
+ setBackgroundRole(QPalette::Window);
+
+ QVBoxLayout *layoutV = new QVBoxLayout(this);
+ layoutV->setMargin(0);
+ d->m_model = new QCalendarModel(this);
+ QTextCharFormat fmt;
+ fmt.setForeground(QBrush(Qt::red));
+ d->m_model->m_dayFormats.insert(Qt::Saturday, fmt);
+ d->m_model->m_dayFormats.insert(Qt::Sunday, fmt);
+ d->m_view = new QCalendarView(this);
+ d->m_view->setObjectName(QLatin1String("qt_calendar_calendarview"));
+ d->m_view->setModel(d->m_model);
+ d->m_model->setView(d->m_view);
+ d->m_view->setSelectionBehavior(QAbstractItemView::SelectItems);
+ d->m_view->setSelectionMode(QAbstractItemView::SingleSelection);
+ d->m_view->horizontalHeader()->setResizeMode(QHeaderView::Stretch);
+ d->m_view->horizontalHeader()->setClickable(false);
+ d->m_view->verticalHeader()->setResizeMode(QHeaderView::Stretch);
+ d->m_view->verticalHeader()->setClickable(false);
+ d->m_selection = d->m_view->selectionModel();
+ d->createNavigationBar(this);
+ d->m_view->setFrameStyle(QFrame::NoFrame);
+ d->m_delegate = new QCalendarDelegate(d, this);
+ d->m_view->setItemDelegate(d->m_delegate);
+ d->update();
+ d->updateNavigationBar();
+ setFocusPolicy(Qt::StrongFocus);
+ setFocusProxy(d->m_view);
+ setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
+
+ connect(d->m_view, SIGNAL(showDate(QDate)),
+ this, SLOT(_q_slotShowDate(QDate)));
+ connect(d->m_view, SIGNAL(changeDate(QDate,bool)),
+ this, SLOT(_q_slotChangeDate(QDate,bool)));
+ connect(d->m_view, SIGNAL(clicked(QDate)),
+ this, SIGNAL(clicked(QDate)));
+ connect(d->m_view, SIGNAL(editingFinished()),
+ this, SLOT(_q_editingFinished()));
+
+ connect(d->prevMonth, SIGNAL(clicked(bool)),
+ this, SLOT(_q_prevMonthClicked()));
+ connect(d->nextMonth, SIGNAL(clicked(bool)),
+ this, SLOT(_q_nextMonthClicked()));
+ connect(d->yearButton, SIGNAL(clicked(bool)),
+ this, SLOT(_q_yearClicked()));
+ connect(d->monthMenu, SIGNAL(triggered(QAction*)),
+ this, SLOT(_q_monthChanged(QAction*)));
+ connect(d->yearEdit, SIGNAL(editingFinished()),
+ this, SLOT(_q_yearEditingFinished()));
+
+ layoutV->setMargin(0);
+ layoutV->setSpacing(0);
+ layoutV->addWidget(d->navBarBackground);
+ layoutV->addWidget(d->m_view);
+
+ d->m_navigator = new QCalendarTextNavigator(this);
+ setDateEditEnabled(true);
+}
+
+/*!
+ Destroys the calendar widget.
+*/
+QCalendarWidget::~QCalendarWidget()
+{
+}
+
+/*!
+ \reimp
+*/
+QSize QCalendarWidget::sizeHint() const
+{
+ return minimumSizeHint();
+}
+
+/*!
+ \reimp
+*/
+QSize QCalendarWidget::minimumSizeHint() const
+{
+ Q_D(const QCalendarWidget);
+ if (d->cachedSizeHint.isValid())
+ return d->cachedSizeHint;
+
+ ensurePolished();
+
+ int w = 0;
+ int h = 0;
+
+ int end = 53;
+ int rows = 7;
+ int cols = 8;
+ int startRow = 0;
+ int startCol = 0;
+
+ const int marginH = (style()->pixelMetric(QStyle::PM_FocusFrameHMargin) + 1) * 2;
+
+ if (horizontalHeaderFormat() == QCalendarWidget::NoHorizontalHeader) {
+ rows = 6;
+ startRow = 1;
+ } else {
+ for (int i = 1; i <= 7; i++) {
+ QFontMetrics fm(d->m_model->formatForCell(0, i).font());
+ w = qMax(w, fm.width(d->m_model->dayName(d->m_model->dayOfWeekForColumn(i))) + marginH);
+ h = qMax(h, fm.height());
+ }
+ }
+
+ if (verticalHeaderFormat() == QCalendarWidget::NoVerticalHeader) {
+ cols = 7;
+ startCol = 1;
+ } else {
+ for (int i = 1; i <= 6; i++) {
+ QFontMetrics fm(d->m_model->formatForCell(i, 0).font());
+ for (int j = 1; j < end; j++)
+ w = qMax(w, fm.width(QString::number(j)) + marginH);
+ h = qMax(h, fm.height());
+ }
+ }
+
+ QFontMetrics fm(d->m_model->formatForCell(1, 1).font());
+ for (int i = 1; i <= end; i++) {
+ w = qMax(w, fm.width(QString::number(i)) + marginH);
+ h = qMax(h, fm.height());
+ }
+
+ if (d->m_view->showGrid()) {
+ // hardcoded in tableview
+ w += 1;
+ h += 1;
+ }
+
+ w += 1; // default column span
+
+ h = qMax(h, d->m_view->verticalHeader()->minimumSectionSize());
+ w = qMax(w, d->m_view->horizontalHeader()->minimumSectionSize());
+
+ //add the size of the header.
+ QSize headerSize(0, 0);
+ if (d->navBarVisible) {
+ int headerH = d->navBarBackground->sizeHint().height();
+ int headerW = 0;
+
+ headerW += d->prevMonth->sizeHint().width();
+ headerW += d->nextMonth->sizeHint().width();
+
+ QFontMetrics fm = d->monthButton->fontMetrics();
+ int monthW = 0;
+ for (int i = 1; i < 12; i++) {
+ QString monthName = locale().standaloneMonthName(i, QLocale::LongFormat);
+ monthW = qMax(monthW, fm.boundingRect(monthName).width());
+ }
+ const int buttonDecoMargin = d->monthButton->sizeHint().width() - fm.boundingRect(d->monthButton->text()).width();
+ headerW += monthW + buttonDecoMargin;
+
+ fm = d->yearButton->fontMetrics();
+ headerW += fm.boundingRect(QLatin1String("5555")).width() + buttonDecoMargin;
+
+ headerSize = QSize(headerW, headerH);
+ }
+ w *= cols;
+ w = qMax(headerSize.width(), w);
+ h = (h * rows) + headerSize.height();
+ d->cachedSizeHint = QSize(w, h);
+ return d->cachedSizeHint;
+}
+
+/*!
+ Paints the cell specified by the given \a date, using the given \a painter and \a rect.
+*/
+
+void QCalendarWidget::paintCell(QPainter *painter, const QRect &rect, const QDate &date) const
+{
+ Q_D(const QCalendarWidget);
+ d->m_delegate->paintCell(painter, rect, date);
+}
+
+/*!
+ \property QCalendarWidget::selectedDate
+ \brief the currently selected date.
+
+ The selected date must be within the date range specified by the
+ minimumDate and maximumDate properties. By default, the selected
+ date is the current date.
+
+ \sa setDateRange()
+*/
+
+QDate QCalendarWidget::selectedDate() const
+{
+ Q_D(const QCalendarWidget);
+ return d->m_model->m_date;
+}
+
+void QCalendarWidget::setSelectedDate(const QDate &date)
+{
+ Q_D(QCalendarWidget);
+ if (d->m_model->m_date == date && date == d->getCurrentDate())
+ return;
+
+ if (!date.isValid())
+ return;
+
+ d->m_model->setDate(date);
+ d->update();
+ QDate newDate = d->m_model->m_date;
+ d->showMonth(newDate.year(), newDate.month());
+ emit selectionChanged();
+}
+
+/*!
+ Returns the year of the currently displayed month. Months are
+ numbered from 1 to 12.
+
+ \sa monthShown(), setCurrentPage()
+*/
+
+int QCalendarWidget::yearShown() const
+{
+ Q_D(const QCalendarWidget);
+ return d->m_model->m_shownYear;
+}
+
+/*!
+ Returns the currently displayed month. Months are numbered from 1 to
+ 12.
+
+ \sa yearShown(), setCurrentPage()
+*/
+
+int QCalendarWidget::monthShown() const
+{
+ Q_D(const QCalendarWidget);
+ return d->m_model->m_shownMonth;
+}
+
+/*!
+ Displays the given \a month of the given \a year without changing
+ the selected date. Use the setSelectedDate() function to alter the
+ selected date.
+
+ The currently displayed month and year can be retrieved using the
+ currentPageMonth() and currentPageYear() functions respectively.
+
+ \sa yearShown(), monthShown(), showPreviousMonth(), showNextMonth(),
+ showPreviousYear(), showNextYear()
+*/
+
+void QCalendarWidget::setCurrentPage(int year, int month)
+{
+ Q_D(QCalendarWidget);
+ d->showMonth(year, month);
+}
+
+/*!
+ Shows the next month relative to the currently displayed
+ month. Note that the selected date is not changed.
+
+ \sa showPreviousMonth(), setCurrentPage(), setSelectedDate()
+*/
+
+void QCalendarWidget::showNextMonth()
+{
+ int year = yearShown();
+ int month = monthShown();
+ if (month == 12) {
+ ++year;
+ month = 1;
+ } else {
+ ++month;
+ }
+ setCurrentPage(year, month);
+}
+
+/*!
+ Shows the previous month relative to the currently displayed
+ month. Note that the selected date is not changed.
+
+ \sa showNextMonth(), setCurrentPage(), setSelectedDate()
+*/
+
+void QCalendarWidget::showPreviousMonth()
+{
+ int year = yearShown();
+ int month = monthShown();
+ if (month == 1) {
+ --year;
+ month = 12;
+ } else {
+ --month;
+ }
+ setCurrentPage(year, month);
+}
+
+/*!
+ Shows the currently displayed month in the \e next year relative
+ to the currently displayed year. Note that the selected date is
+ not changed.
+
+ \sa showPreviousYear(), setCurrentPage(), setSelectedDate()
+*/
+
+void QCalendarWidget::showNextYear()
+{
+ int year = yearShown();
+ int month = monthShown();
+ ++year;
+ setCurrentPage(year, month);
+}
+
+/*!
+ Shows the currently displayed month in the \e previous year
+ relative to the currently displayed year. Note that the selected
+ date is not changed.
+
+ \sa showNextYear(), setCurrentPage(), setSelectedDate()
+*/
+
+void QCalendarWidget::showPreviousYear()
+{
+ int year = yearShown();
+ int month = monthShown();
+ --year;
+ setCurrentPage(year, month);
+}
+
+/*!
+ Shows the month of the selected date.
+
+ \sa selectedDate(), setCurrentPage()
+*/
+void QCalendarWidget::showSelectedDate()
+{
+ QDate currentDate = selectedDate();
+ setCurrentPage(currentDate.year(), currentDate.month());
+}
+
+/*!
+ Shows the month of the today's date.
+
+ \sa selectedDate(), setCurrentPage()
+*/
+void QCalendarWidget::showToday()
+{
+ QDate currentDate = QDate::currentDate();
+ setCurrentPage(currentDate.year(), currentDate.month());
+}
+
+/*!
+ \property QCalendarWidget::minimumDate
+ \brief the minimum date of the currently specified date range.
+
+ The user will not be able to select a date that is before the
+ currently set minimum date.
+
+ \table
+ \row
+ \o \image qcalendarwidget-minimum.png
+ \row
+ \o
+ \snippet doc/src/snippets/code/src_gui_widgets_qcalendarwidget.cpp 1
+ \endtable
+
+ By default, the minimum date is the earliest date that the QDate
+ class can handle.
+
+ When setting a minimum date, the maximumDate and selectedDate
+ properties are adjusted if the selection range becomes invalid. If
+ the provided date is not a valid QDate object, the
+ setMinimumDate() function does nothing.
+
+ \sa setDateRange()
+*/
+
+QDate QCalendarWidget::minimumDate() const
+{
+ Q_D(const QCalendarWidget);
+ return d->m_model->m_minimumDate;
+}
+
+void QCalendarWidget::setMinimumDate(const QDate &date)
+{
+ Q_D(QCalendarWidget);
+ if (!date.isValid() || d->m_model->m_minimumDate == date)
+ return;
+
+ QDate oldDate = d->m_model->m_date;
+ d->m_model->setMinimumDate(date);
+ d->yearEdit->setMinimum(d->m_model->m_minimumDate.year());
+ d->updateMonthMenu();
+ QDate newDate = d->m_model->m_date;
+ if (oldDate != newDate) {
+ d->update();
+ d->showMonth(newDate.year(), newDate.month());
+ d->m_navigator->setDate(newDate);
+ emit selectionChanged();
+ }
+}
+
+/*!
+ \property QCalendarWidget::maximumDate
+ \brief the maximum date of the currently specified date range.
+
+ The user will not be able to select a date which is after the
+ currently set maximum date.
+
+ \table
+ \row
+ \o \image qcalendarwidget-maximum.png
+ \row
+ \o
+ \snippet doc/src/snippets/code/src_gui_widgets_qcalendarwidget.cpp 2
+ \endtable
+
+ By default, the maximum date is the last day the QDate class can
+ handle.
+
+ When setting a maximum date, the minimumDate and selectedDate
+ properties are adjusted if the selection range becomes invalid. If
+ the provided date is not a valid QDate object, the
+ setMaximumDate() function does nothing.
+
+ \sa setDateRange()
+*/
+
+QDate QCalendarWidget::maximumDate() const
+{
+ Q_D(const QCalendarWidget);
+ return d->m_model->m_maximumDate;
+}
+
+void QCalendarWidget::setMaximumDate(const QDate &date)
+{
+ Q_D(QCalendarWidget);
+ if (!date.isValid() || d->m_model->m_maximumDate == date)
+ return;
+
+ QDate oldDate = d->m_model->m_date;
+ d->m_model->setMaximumDate(date);
+ d->yearEdit->setMaximum(d->m_model->m_maximumDate.year());
+ d->updateMonthMenu();
+ QDate newDate = d->m_model->m_date;
+ if (oldDate != newDate) {
+ d->update();
+ d->showMonth(newDate.year(), newDate.month());
+ d->m_navigator->setDate(newDate);
+ emit selectionChanged();
+ }
+}
+
+/*!
+ Defines a date range by setting the minimumDate and maximumDate
+ properties.
+
+ The date range restricts the user selection, i.e. the user can
+ only select dates within the specified date range. Note that
+
+ \snippet doc/src/snippets/code/src_gui_widgets_qcalendarwidget.cpp 3
+
+ is analogous to
+
+ \snippet doc/src/snippets/code/src_gui_widgets_qcalendarwidget.cpp 4
+
+ If either the \a min or \a max parameters are not valid QDate
+ objects, this function does nothing.
+
+ \sa setMinimumDate(), setMaximumDate()
+*/
+
+void QCalendarWidget::setDateRange(const QDate &min, const QDate &max)
+{
+ Q_D(QCalendarWidget);
+ if (d->m_model->m_minimumDate == min && d->m_model->m_maximumDate == max)
+ return;
+ if (!min.isValid() || !max.isValid())
+ return;
+
+ QDate minimum = min;
+ QDate maximum = max;
+ if (min > max) {
+ minimum = max;
+ maximum = min;
+ }
+
+ QDate oldDate = d->m_model->m_date;
+ d->m_model->setRange(min, max);
+ d->yearEdit->setMinimum(d->m_model->m_minimumDate.year());
+ d->yearEdit->setMaximum(d->m_model->m_maximumDate.year());
+ d->updateMonthMenu();
+ QDate newDate = d->m_model->m_date;
+ if (oldDate != newDate) {
+ d->update();
+ d->showMonth(newDate.year(), newDate.month());
+ d->m_navigator->setDate(newDate);
+ emit selectionChanged();
+ }
+}
+
+
+/*! \enum QCalendarWidget::HorizontalHeaderFormat
+
+ This enum type defines the various formats the horizontal header can display.
+
+ \value SingleLetterDayNames The header displays a single letter abbreviation for day names (e.g. M for Monday).
+ \value ShortDayNames The header displays a short abbreviation for day names (e.g. Mon for Monday).
+ \value LongDayNames The header displays complete day names (e.g. Monday).
+ \value NoHorizontalHeader The header is hidden.
+
+ \sa horizontalHeaderFormat(), VerticalHeaderFormat
+*/
+
+/*!
+ \property QCalendarWidget::horizontalHeaderFormat
+ \brief the format of the horizontal header.
+
+ The default value is QCalendarWidget::ShortDayNames.
+*/
+
+void QCalendarWidget::setHorizontalHeaderFormat(QCalendarWidget::HorizontalHeaderFormat format)
+{
+ Q_D(QCalendarWidget);
+ if (d->m_model->m_horizontalHeaderFormat == format)
+ return;
+
+ d->m_model->setHorizontalHeaderFormat(format);
+ d->cachedSizeHint = QSize();
+ d->m_view->viewport()->update();
+ d->m_view->updateGeometry();
+}
+
+QCalendarWidget::HorizontalHeaderFormat QCalendarWidget::horizontalHeaderFormat() const
+{
+ Q_D(const QCalendarWidget);
+ return d->m_model->m_horizontalHeaderFormat;
+}
+
+
+/*!
+ \enum QCalendarWidget::VerticalHeaderFormat
+
+ This enum type defines the various formats the vertical header can display.
+
+ \value ISOWeekNumbers The header displays ISO week numbers as described by \l QDate::weekNumber().
+ \value NoVerticalHeader The header is hidden.
+
+ \sa verticalHeaderFormat(), HorizontalHeaderFormat
+*/
+
+/*!
+ \property QCalendarWidget::verticalHeaderFormat
+ \brief the format of the vertical header.
+
+ The default value is QCalendarWidget::ISOWeekNumber.
+*/
+
+QCalendarWidget::VerticalHeaderFormat QCalendarWidget::verticalHeaderFormat() const
+{
+ Q_D(const QCalendarWidget);
+ bool shown = d->m_model->weekNumbersShown();
+ if (shown)
+ return QCalendarWidget::ISOWeekNumbers;
+ return QCalendarWidget::NoVerticalHeader;
+}
+
+void QCalendarWidget::setVerticalHeaderFormat(QCalendarWidget::VerticalHeaderFormat format)
+{
+ Q_D(QCalendarWidget);
+ bool show = false;
+ if (format == QCalendarWidget::ISOWeekNumbers)
+ show = true;
+ if (d->m_model->weekNumbersShown() == show)
+ return;
+ d->m_model->setWeekNumbersShown(show);
+ d->cachedSizeHint = QSize();
+ d->m_view->viewport()->update();
+ d->m_view->updateGeometry();
+}
+
+/*!
+ \property QCalendarWidget::gridVisible
+ \brief whether the table grid is displayed.
+
+ \table
+ \row
+ \o \inlineimage qcalendarwidget-grid.png
+ \row
+ \o
+ \snippet doc/src/snippets/code/src_gui_widgets_qcalendarwidget.cpp 5
+ \endtable
+
+ The default value is false.
+*/
+
+bool QCalendarWidget::isGridVisible() const
+{
+ Q_D(const QCalendarWidget);
+ return d->m_view->showGrid();
+}
+
+void QCalendarWidget::setGridVisible(bool show)
+{
+ Q_D(QCalendarWidget);
+ d->m_view->setShowGrid(show);
+ d->cachedSizeHint = QSize();
+ d->m_view->viewport()->update();
+ d->m_view->updateGeometry();
+}
+
+/*!
+ \property QCalendarWidget::selectionMode
+ \brief the type of selection the user can make in the calendar
+
+ When this property is set to SingleSelection, the user can select a date
+ within the minimum and maximum allowed dates, using either the mouse or
+ the keyboard.
+
+ When the property is set to NoSelection, the user will be unable to select
+ dates, but they can still be selected programmatically. Note that the date
+ that is selected when the property is set to NoSelection will still be
+ the selected date of the calendar.
+
+ The default value is SingleSelection.
+*/
+
+QCalendarWidget::SelectionMode QCalendarWidget::selectionMode() const
+{
+ Q_D(const QCalendarWidget);
+ return d->m_view->readOnly ? QCalendarWidget::NoSelection : QCalendarWidget::SingleSelection;
+}
+
+void QCalendarWidget::setSelectionMode(SelectionMode mode)
+{
+ Q_D(QCalendarWidget);
+ d->m_view->readOnly = (mode == QCalendarWidget::NoSelection);
+ d->setNavigatorEnabled(isDateEditEnabled() && (selectionMode() != QCalendarWidget::NoSelection));
+ d->update();
+}
+
+/*!
+ \property QCalendarWidget::firstDayOfWeek
+ \brief a value identifying the day displayed in the first column.
+
+ By default, the day displayed in the first column is Sunday
+*/
+
+void QCalendarWidget::setFirstDayOfWeek(Qt::DayOfWeek dayOfWeek)
+{
+ Q_D(QCalendarWidget);
+ if ((Qt::DayOfWeek)d->m_model->firstColumnDay() == dayOfWeek)
+ return;
+
+ d->m_model->setFirstColumnDay(dayOfWeek);
+ d->update();
+}
+
+Qt::DayOfWeek QCalendarWidget::firstDayOfWeek() const
+{
+ Q_D(const QCalendarWidget);
+ return (Qt::DayOfWeek)d->m_model->firstColumnDay();
+}
+
+/*!
+ Returns the text char format for rendering the header.
+*/
+QTextCharFormat QCalendarWidget::headerTextFormat() const
+{
+ Q_D(const QCalendarWidget);
+ return d->m_model->m_headerFormat;
+}
+
+/*!
+ Sets the text char format for rendering the header to \a format.
+ If you also set a weekday text format, this format's foreground and
+ background color will take precedence over the header's format.
+ The other formatting information will still be decided by
+ the header's format.
+*/
+void QCalendarWidget::setHeaderTextFormat(const QTextCharFormat &format)
+{
+ Q_D(QCalendarWidget);
+ d->m_model->m_headerFormat = format;
+ d->cachedSizeHint = QSize();
+ d->m_view->viewport()->update();
+ d->m_view->updateGeometry();
+}
+
+/*!
+ Returns the text char format for rendering of day in the week \a dayOfWeek.
+
+ \sa headerTextFormat()
+*/
+QTextCharFormat QCalendarWidget::weekdayTextFormat(Qt::DayOfWeek dayOfWeek) const
+{
+ Q_D(const QCalendarWidget);
+ return d->m_model->m_dayFormats.value(dayOfWeek);
+}
+
+/*!
+ Sets the text char format for rendering of day in the week \a dayOfWeek to \a format.
+ The format will take precedence over the header format in case of foreground
+ and background color. Other text formatting information is taken from the headers format.
+
+ \sa setHeaderTextFormat()
+*/
+void QCalendarWidget::setWeekdayTextFormat(Qt::DayOfWeek dayOfWeek, const QTextCharFormat &format)
+{
+ Q_D(QCalendarWidget);
+ d->m_model->m_dayFormats[dayOfWeek] = format;
+ d->cachedSizeHint = QSize();
+ d->m_view->viewport()->update();
+ d->m_view->updateGeometry();
+}
+
+/*!
+ Returns a QMap from QDate to QTextCharFormat showing all dates
+ that use a special format that alters their rendering.
+*/
+QMap<QDate, QTextCharFormat> QCalendarWidget::dateTextFormat() const
+{
+ Q_D(const QCalendarWidget);
+ return d->m_model->m_dateFormats;
+}
+
+/*!
+ Returns a QTextCharFormat for \a date. The char format can be be
+ empty if the date is not renderd specially.
+*/
+QTextCharFormat QCalendarWidget::dateTextFormat(const QDate &date) const
+{
+ Q_D(const QCalendarWidget);
+ return d->m_model->m_dateFormats.value(date);
+}
+
+/*!
+ Sets the format used to render the given \a date to that specified by \a format.
+
+ If \a date is null, all date formats are cleared.
+*/
+void QCalendarWidget::setDateTextFormat(const QDate &date, const QTextCharFormat &format)
+{
+ Q_D(QCalendarWidget);
+ if ( date.isNull() && !format.isValid() )
+ d->m_model->m_dateFormats.clear();
+ else
+ d->m_model->m_dateFormats[date] = format;
+ d->m_view->viewport()->update();
+ d->m_view->updateGeometry();
+}
+
+/*!
+ \property QCalendarWidget::dateEditEnabled
+ \brief whether the date edit popup is enabled
+ \since 4.3
+
+ If this property is enabled, pressing a non-modifier key will cause a
+ date edit to popup if the calendar widget has focus, allowing the user
+ to specify a date in the form specified by the current locale.
+
+ By default, this property is enabled.
+
+ The date edit is simpler in appearance than QDateEdit, but allows the
+ user to navigate between fields using the left and right cursor keys,
+ increment and decrement individual fields using the up and down cursor
+ keys, and enter values directly using the number keys.
+
+ \sa QCalendarWidget::dateEditAcceptDelay
+*/
+bool QCalendarWidget::isDateEditEnabled() const
+{
+ Q_D(const QCalendarWidget);
+ return d->m_dateEditEnabled;
+}
+
+void QCalendarWidget::setDateEditEnabled(bool enable)
+{
+ Q_D(QCalendarWidget);
+ if (isDateEditEnabled() == enable)
+ return;
+
+ d->m_dateEditEnabled = enable;
+
+ d->setNavigatorEnabled(enable && (selectionMode() != QCalendarWidget::NoSelection));
+}
+
+/*!
+ \property QCalendarWidget::dateEditAcceptDelay
+ \brief the time an inactive date edit is shown before its contents are accepted
+ \since 4.3
+
+ If the calendar widget's \l{dateEditEnabled}{date edit is enabled}, this
+ property specifies the amount of time (in millseconds) that the date edit
+ remains open after the most recent user input. Once this time has elapsed,
+ the date specified in the date edit is accepted and the popup is closed.
+
+ By default, the delay is defined to be 1500 milliseconds (1.5 seconds).
+*/
+int QCalendarWidget::dateEditAcceptDelay() const
+{
+ Q_D(const QCalendarWidget);
+ return d->m_navigator->dateEditAcceptDelay();
+}
+
+void QCalendarWidget::setDateEditAcceptDelay(int delay)
+{
+ Q_D(QCalendarWidget);
+ d->m_navigator->setDateEditAcceptDelay(delay);
+}
+
+/*!
+ \since 4.4
+
+ Updates the cell specified by the given \a date unless updates
+ are disabled or the cell is hidden.
+
+ \sa updateCells(), yearShown(), monthShown()
+*/
+void QCalendarWidget::updateCell(const QDate &date)
+{
+ if (!date.isValid()) {
+ qWarning("QCalendarWidget::updateCell: Invalid date");
+ return;
+ }
+
+ if (!isVisible())
+ return;
+
+ Q_D(QCalendarWidget);
+ int row, column;
+ d->m_model->cellForDate(date, &row, &column);
+ if (row == -1 || column == -1)
+ return;
+
+ QModelIndex modelIndex = d->m_model->index(row, column);
+ if (!modelIndex.isValid())
+ return;
+
+ d->m_view->viewport()->update(d->m_view->visualRect(modelIndex));
+}
+
+/*!
+ \since 4.4
+
+ Updates all visible cells unless updates are disabled.
+
+ \sa updateCell()
+*/
+void QCalendarWidget::updateCells()
+{
+ Q_D(QCalendarWidget);
+ if (isVisible())
+ d->m_view->viewport()->update();
+}
+
+/*!
+ \fn void QCalendarWidget::selectionChanged()
+
+ This signal is emitted when the currently selected date is
+ changed.
+
+ The currently selected date can be changed by the user using the
+ mouse or keyboard, or by the programmer using setSelectedDate().
+
+ \sa selectedDate()
+*/
+
+/*!
+ \fn void QCalendarWidget::currentPageChanged(int year, int month)
+
+ This signal is emitted when the currently shown month is changed.
+ The new \a year and \a month are passed as parameters.
+
+ \sa setCurrentPage()
+*/
+
+/*!
+ \fn void QCalendarWidget::activated(const QDate &date)
+
+ This signal is emitted whenever the user presses the Return or
+ Enter key or double-clicks a \a date in the calendar
+ widget.
+*/
+
+/*!
+ \fn void QCalendarWidget::clicked(const QDate &date)
+
+ This signal is emitted when a mouse button is clicked. The date
+ the mouse was clicked on is specified by \a date. The signal is
+ only emitted when clicked on a valid date, e.g., dates are not
+ outside the minimumDate() and maximumDate(). If the selection mode
+ is NoSelection, this signal will not be emitted.
+
+*/
+
+/*!
+ \property QCalendarWidget::headerVisible
+ \brief whether the navigation bar is shown or not
+
+ \obsolete
+
+ Use navigationBarVisible() instead.
+
+ By default, this property is true.
+*/
+
+/*!
+ \obsolete
+
+ Use setNavigationBarVisible() instead.
+*/
+bool QCalendarWidget::isHeaderVisible() const
+{
+ Q_D(const QCalendarWidget);
+ return d->navBarVisible;
+}
+
+/*!
+ \obsolete
+
+ Use setNavigationBarVisible() instead.
+
+*/
+void QCalendarWidget::setHeaderVisible(bool visible)
+{
+ setNavigationBarVisible(visible);
+}
+
+/*!
+ \property QCalendarWidget::navigationBarVisible
+ \brief whether the navigation bar is shown or not
+
+ \since 4.3
+
+ When this property is true (the default), the next month,
+ previous month, month selection, year selection controls are
+ shown on top.
+
+ When the property is set to false, these controls are hidden.
+*/
+
+void QCalendarWidget::setNavigationBarVisible(bool visible)
+{
+ Q_D(QCalendarWidget);
+ d->navBarVisible = visible;
+ d->cachedSizeHint = QSize();
+ d->navBarBackground->setVisible(visible);
+ updateGeometry();
+}
+
+/*!
+ \reimp
+*/
+bool QCalendarWidget::event(QEvent *event)
+{
+ Q_D(QCalendarWidget);
+ switch (event->type()) {
+ case QEvent::LayoutDirectionChange:
+ d->updateButtonIcons();
+ case QEvent::LocaleChange:
+ d->cachedSizeHint = QSize();
+ d->updateMonthMenuNames();
+ d->updateNavigationBar();
+ d->m_view->updateGeometry();
+ break;
+ case QEvent::FontChange:
+ case QEvent::ApplicationFontChange:
+ d->cachedSizeHint = QSize();
+ d->m_view->updateGeometry();
+ break;
+ case QEvent::StyleChange:
+ d->cachedSizeHint = QSize();
+ d->m_view->updateGeometry();
+ default:
+ break;
+ }
+ return QWidget::event(event);
+}
+
+/*!
+ \reimp
+*/
+bool QCalendarWidget::eventFilter(QObject *watched, QEvent *event)
+{
+ Q_D(QCalendarWidget);
+ if (event->type() == QEvent::MouseButtonPress && d->yearEdit->hasFocus() && !QRect(d->yearEdit->mapToGlobal(QPoint(0, 0)), d->yearEdit->size()).contains(static_cast<QMouseEvent *>(event)->globalPos())) {
+ event->accept();
+ d->_q_yearEditingFinished();
+ setFocus();
+ return true;
+ }
+ return QWidget::eventFilter(watched, event);
+}
+
+/*!
+ \reimp
+*/
+void QCalendarWidget::mousePressEvent(QMouseEvent *event)
+{
+ setAttribute(Qt::WA_NoMouseReplay);
+ QWidget::mousePressEvent(event);
+ setFocus();
+}
+
+/*!
+ \reimp
+*/
+void QCalendarWidget::resizeEvent(QResizeEvent * event)
+{
+ Q_D(QCalendarWidget);
+
+ // XXX Should really use a QWidgetStack for yearEdit and yearButton,
+ // XXX here we hide the year edit when the layout is likely to break
+ // XXX the manual positioning of the yearEdit over the yearButton.
+ if(d->yearEdit->isVisible() && event->size().width() != event->oldSize().width())
+ d->_q_yearEditingFinished();
+
+ QWidget::resizeEvent(event);
+}
+
+/*!
+ \reimp
+*/
+void QCalendarWidget::keyPressEvent(QKeyEvent * event)
+{
+ Q_D(QCalendarWidget);
+ if(d->yearEdit->isVisible()&& event->key() == Qt::Key_Escape)
+ {
+ d->yearEdit->setValue(yearShown());
+ d->_q_yearEditingFinished();
+ return;
+ }
+ QWidget::keyPressEvent(event);
+}
+
+QT_END_NAMESPACE
+
+#include "qcalendarwidget.moc"
+#include "moc_qcalendarwidget.cpp"
+
+#endif //QT_NO_CALENDARWIDGET
diff --git a/src/gui/widgets/qcalendarwidget.h b/src/gui/widgets/qcalendarwidget.h
new file mode 100644
index 0000000000..05c2344475
--- /dev/null
+++ b/src/gui/widgets/qcalendarwidget.h
@@ -0,0 +1,204 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QCALENDARWIDGET_H
+#define QCALENDARWIDGET_H
+
+#include <QtGui/qwidget.h>
+#include <QtCore/qdatetime.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+#ifndef QT_NO_CALENDARWIDGET
+
+class QDate;
+class QTextCharFormat;
+class QCalendarWidgetPrivate;
+
+class Q_GUI_EXPORT QCalendarWidget : public QWidget
+{
+ Q_OBJECT
+ Q_ENUMS(Qt::DayOfWeek)
+ Q_ENUMS(HorizontalHeaderFormat)
+ Q_ENUMS(VerticalHeaderFormat)
+ Q_ENUMS(SelectionMode)
+ Q_PROPERTY(QDate selectedDate READ selectedDate WRITE setSelectedDate)
+ Q_PROPERTY(QDate minimumDate READ minimumDate WRITE setMinimumDate)
+ Q_PROPERTY(QDate maximumDate READ maximumDate WRITE setMaximumDate)
+ Q_PROPERTY(Qt::DayOfWeek firstDayOfWeek READ firstDayOfWeek WRITE setFirstDayOfWeek)
+ Q_PROPERTY(bool gridVisible READ isGridVisible WRITE setGridVisible)
+ Q_PROPERTY(SelectionMode selectionMode READ selectionMode WRITE setSelectionMode)
+ Q_PROPERTY(HorizontalHeaderFormat horizontalHeaderFormat READ horizontalHeaderFormat WRITE setHorizontalHeaderFormat)
+ Q_PROPERTY(VerticalHeaderFormat verticalHeaderFormat READ verticalHeaderFormat WRITE setVerticalHeaderFormat)
+ Q_PROPERTY(bool headerVisible READ isHeaderVisible WRITE setHeaderVisible STORED false DESIGNABLE false) // obsolete
+ Q_PROPERTY(bool navigationBarVisible READ isNavigationBarVisible WRITE setNavigationBarVisible)
+ Q_PROPERTY(bool dateEditEnabled READ isDateEditEnabled WRITE setDateEditEnabled)
+ Q_PROPERTY(int dateEditAcceptDelay READ dateEditAcceptDelay WRITE setDateEditAcceptDelay)
+
+public:
+ enum HorizontalHeaderFormat {
+ NoHorizontalHeader,
+ SingleLetterDayNames,
+ ShortDayNames,
+ LongDayNames
+ };
+
+ enum VerticalHeaderFormat {
+ NoVerticalHeader,
+ ISOWeekNumbers
+ };
+
+ enum SelectionMode {
+ NoSelection,
+ SingleSelection
+ };
+
+ explicit QCalendarWidget(QWidget *parent = 0);
+ ~QCalendarWidget();
+
+ virtual QSize sizeHint() const;
+ virtual QSize minimumSizeHint() const;
+
+ QDate selectedDate() const;
+
+ int yearShown() const;
+ int monthShown() const;
+
+ QDate minimumDate() const;
+ void setMinimumDate(const QDate &date);
+
+ QDate maximumDate() const;
+ void setMaximumDate(const QDate &date);
+
+ Qt::DayOfWeek firstDayOfWeek() const;
+ void setFirstDayOfWeek(Qt::DayOfWeek dayOfWeek);
+
+ // ### Qt 5: eliminate these two
+ bool isHeaderVisible() const;
+ void setHeaderVisible(bool show);
+
+ inline bool isNavigationBarVisible() const { return isHeaderVisible(); }
+
+ bool isGridVisible() const;
+
+ SelectionMode selectionMode() const;
+ void setSelectionMode(SelectionMode mode);
+
+ HorizontalHeaderFormat horizontalHeaderFormat() const;
+ void setHorizontalHeaderFormat(HorizontalHeaderFormat format);
+
+ VerticalHeaderFormat verticalHeaderFormat() const;
+ void setVerticalHeaderFormat(VerticalHeaderFormat format);
+
+ QTextCharFormat headerTextFormat() const;
+ void setHeaderTextFormat(const QTextCharFormat &format);
+
+ QTextCharFormat weekdayTextFormat(Qt::DayOfWeek dayOfWeek) const;
+ void setWeekdayTextFormat(Qt::DayOfWeek dayOfWeek, const QTextCharFormat &format);
+
+ QMap<QDate, QTextCharFormat> dateTextFormat() const;
+ QTextCharFormat dateTextFormat(const QDate &date) const;
+ void setDateTextFormat(const QDate &date, const QTextCharFormat &format);
+
+ bool isDateEditEnabled() const;
+ void setDateEditEnabled(bool enable);
+
+ int dateEditAcceptDelay() const;
+ void setDateEditAcceptDelay(int delay);
+
+protected:
+ bool event(QEvent *event);
+ bool eventFilter(QObject *watched, QEvent *event);
+ void mousePressEvent(QMouseEvent *event);
+ void resizeEvent(QResizeEvent * event);
+ void keyPressEvent(QKeyEvent * event);
+
+ virtual void paintCell(QPainter *painter, const QRect &rect, const QDate &date) const;
+ void updateCell(const QDate &date);
+ void updateCells();
+
+public Q_SLOTS:
+ void setSelectedDate(const QDate &date);
+ void setDateRange(const QDate &min, const QDate &max);
+ void setCurrentPage(int year, int month);
+ void setGridVisible(bool show);
+ void setNavigationBarVisible(bool visible);
+ void showNextMonth();
+ void showPreviousMonth();
+ void showNextYear();
+ void showPreviousYear();
+ void showSelectedDate();
+ void showToday();
+
+Q_SIGNALS:
+ void selectionChanged();
+ void clicked(const QDate &date);
+ void activated(const QDate &date);
+ void currentPageChanged(int year, int month);
+
+private:
+ Q_DECLARE_PRIVATE(QCalendarWidget)
+ Q_DISABLE_COPY(QCalendarWidget)
+
+ Q_PRIVATE_SLOT(d_func(), void _q_slotShowDate(const QDate &date))
+ Q_PRIVATE_SLOT(d_func(), void _q_slotChangeDate(const QDate &date))
+ Q_PRIVATE_SLOT(d_func(), void _q_slotChangeDate(const QDate &date, bool changeMonth))
+ Q_PRIVATE_SLOT(d_func(), void _q_editingFinished())
+ Q_PRIVATE_SLOT(d_func(), void _q_prevMonthClicked())
+ Q_PRIVATE_SLOT(d_func(), void _q_nextMonthClicked())
+ Q_PRIVATE_SLOT(d_func(), void _q_yearEditingFinished())
+ Q_PRIVATE_SLOT(d_func(), void _q_yearClicked())
+ Q_PRIVATE_SLOT(d_func(), void _q_monthChanged(QAction *act))
+
+};
+
+#endif // QT_NO_CALENDARWIDGET
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QCALENDARWIDGET_H
+
diff --git a/src/gui/widgets/qcheckbox.cpp b/src/gui/widgets/qcheckbox.cpp
new file mode 100644
index 0000000000..1998f9fa23
--- /dev/null
+++ b/src/gui/widgets/qcheckbox.cpp
@@ -0,0 +1,425 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qcheckbox.h"
+#include "qapplication.h"
+#include "qbitmap.h"
+#include "qicon.h"
+#include "qstylepainter.h"
+#include "qstyle.h"
+#include "qstyleoption.h"
+#include "qevent.h"
+
+#include "private/qabstractbutton_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class QCheckBoxPrivate : public QAbstractButtonPrivate
+{
+ Q_DECLARE_PUBLIC(QCheckBox)
+public:
+ QCheckBoxPrivate()
+ : QAbstractButtonPrivate(QSizePolicy::CheckBox), tristate(false), noChange(false),
+ hovering(true), publishedState(Qt::Unchecked) {}
+
+ uint tristate : 1;
+ uint noChange : 1;
+ uint hovering : 1;
+ uint publishedState : 2;
+
+ void init();
+};
+
+/*!
+ \class QCheckBox
+ \brief The QCheckBox widget provides a checkbox with a text label.
+
+ \ingroup basicwidgets
+ \mainclass
+
+ A QCheckBox is an option button that can be switched on (checked)
+ or off (unchecked). Checkboxes are typically used to represent
+ features in an application that can be enabled or disabled without
+ affecting others, but different types of behavior can be
+ implemented.
+
+ A QButtonGroup can be used to group check buttons visually.
+
+ Whenever a checkbox is checked or cleared it emits the signal
+ stateChanged(). Connect to this signal if you want to trigger an
+ action each time the checkbox changes state. You can use
+ isChecked() to query whether or not a checkbox is checked.
+
+ In addition to the usual checked and unchecked states, QCheckBox
+ optionally provides a third state to indicate "no change". This
+ is useful whenever you need to give the user the option of neither
+ checking nor unchecking a checkbox. If you need this third state,
+ enable it with setTristate(), and use checkState() to query the current
+ toggle state.
+
+ Just like QPushButton, a checkbox displays text, and optionally a
+ small icon. The icon is set with setIcon(). The text can be set in
+ the constructor or with setText(). A shortcut key can be specified
+ by preceding the preferred character with an ampersand. For
+ example:
+
+ \snippet doc/src/snippets/code/src_gui_widgets_qcheckbox.cpp 0
+
+ In this example the shortcut is \e{Alt+A}. See the \l
+ {QShortcut#mnemonic}{QShortcut} documentation for details (to
+ display an actual ampersand, use '&&').
+
+ Important inherited functions: text(), setText(), text(),
+ pixmap(), setPixmap(), accel(), setAccel(), isToggleButton(),
+ setDown(), isDown(), isOn(), checkState(), autoRepeat(),
+ isExclusiveToggle(), group(), setAutoRepeat(), toggle(),
+ pressed(), released(), clicked(), toggled(), checkState(), and
+ stateChanged().
+
+ \table 100%
+ \row \o \inlineimage macintosh-checkbox.png Screenshot of a Macintosh style checkbox
+ \o A checkbox shown in the \l{Macintosh Style Widget Gallery}{Macintosh widget style}.
+ \row \o \inlineimage windows-checkbox.png Screenshot of a Windows XP style checkbox
+ \o A checkbox shown in the \l{Windows XP Style Widget Gallery}{Windows XP widget style}.
+ \row \o \inlineimage plastique-checkbox.png Screenshot of a Plastique style checkbox
+ \o A checkbox shown in the \l{Plastique Style Widget Gallery}{Plastique widget style}.
+ \endtable
+
+ \sa QAbstractButton, QRadioButton, {fowler}{GUI Design Handbook: Check Box}
+*/
+
+/*!
+ \enum QCheckBox::ToggleState
+ \compat
+
+ \value Off Use Qt::Unchecked instead.
+ \value NoChange Use Qt::PartiallyChecked instead.
+ \value On Use Qt::Checked instead.
+*/
+
+/*!
+ \fn void QCheckBox::stateChanged(int state)
+
+ This signal is emitted whenever the check box's state changes,
+ i.e. whenever the user checks or unchecks it.
+
+ \a state contains the check box's new Qt::CheckState.
+*/
+
+/*!
+ \property QCheckBox::tristate
+ \brief whether the checkbox is a tri-state checkbox
+
+ The default is false; i.e. the checkbox has only two states.
+*/
+
+void QCheckBoxPrivate::init()
+{
+ Q_Q(QCheckBox);
+ q->setCheckable(true);
+ q->setMouseTracking(true);
+ q->setForegroundRole(QPalette::WindowText);
+ setLayoutItemMargins(QStyle::SE_CheckBoxLayoutItem);
+}
+
+/*!
+ Initialize \a option with the values from this QCheckBox. This method is useful
+ for subclasses when they need a QStyleOptionButton, but don't want to fill
+ in all the information themselves.
+
+ \sa QStyleOption::initFrom()
+*/
+void QCheckBox::initStyleOption(QStyleOptionButton *option) const
+{
+ if (!option)
+ return;
+ Q_D(const QCheckBox);
+ option->initFrom(this);
+ if (d->down)
+ option->state |= QStyle::State_Sunken;
+ if (d->tristate && d->noChange)
+ option->state |= QStyle::State_NoChange;
+ else
+ option->state |= d->checked ? QStyle::State_On : QStyle::State_Off;
+ if (testAttribute(Qt::WA_Hover) && underMouse()) {
+ if (d->hovering)
+ option->state |= QStyle::State_MouseOver;
+ else
+ option->state &= ~QStyle::State_MouseOver;
+ }
+ option->text = d->text;
+ option->icon = d->icon;
+ option->iconSize = iconSize();
+}
+
+/*!
+ Constructs a checkbox with the given \a parent, but with no text.
+
+ The \a parent argument is passed on to the QAbstractButton constructor.
+*/
+
+QCheckBox::QCheckBox(QWidget *parent)
+ : QAbstractButton (*new QCheckBoxPrivate, parent)
+{
+ Q_D(QCheckBox);
+ d->init();
+}
+
+/*!
+ Constructs a checkbox with the given \a parent and \a text.
+
+ The \a parent argument is passed on to the QAbstractButton constructor.
+*/
+
+QCheckBox::QCheckBox(const QString &text, QWidget *parent)
+ : QAbstractButton (*new QCheckBoxPrivate, parent)
+{
+ Q_D(QCheckBox);
+ d->init();
+ setText(text);
+}
+
+void QCheckBox::setTristate(bool y)
+{
+ Q_D(QCheckBox);
+ d->tristate = y;
+}
+
+bool QCheckBox::isTristate() const
+{
+ Q_D(const QCheckBox);
+ return d->tristate;
+}
+
+
+/*!
+ Returns the check box's check state.
+ If you do not need tristate support, you can also
+ use \l QAbstractButton::isChecked() which returns
+ a boolean.
+
+ \sa setCheckState() Qt::CheckState
+*/
+Qt::CheckState QCheckBox::checkState() const
+{
+ Q_D(const QCheckBox);
+ if (d->tristate && d->noChange)
+ return Qt::PartiallyChecked;
+ return d->checked ? Qt::Checked : Qt::Unchecked;
+}
+
+/*!
+ Sets the check box's check state to \a state.
+ If you do not need tristate support, you can also
+ use \l QAbstractButton::setChecked() which takes
+ a boolean.
+
+ \sa checkState() Qt::CheckState
+*/
+void QCheckBox::setCheckState(Qt::CheckState state)
+{
+ Q_D(QCheckBox);
+ if (state == Qt::PartiallyChecked) {
+ d->tristate = true;
+ d->noChange = true;
+ } else {
+ d->noChange = false;
+ }
+ d->blockRefresh = true;
+ setChecked(state != Qt::Unchecked);
+ d->blockRefresh = false;
+ d->refresh();
+ if ((uint)state != d->publishedState) {
+ d->publishedState = state;
+ emit stateChanged(state);
+ }
+}
+
+
+/*!\reimp
+*/
+QSize QCheckBox::sizeHint() const
+{
+ Q_D(const QCheckBox);
+ if (d->sizeHint.isValid())
+ return d->sizeHint;
+ ensurePolished();
+ QFontMetrics fm = fontMetrics();
+ QStyleOptionButton opt;
+ initStyleOption(&opt);
+ QSize sz = style()->itemTextRect(fm, QRect(0, 0, 1, 1), Qt::TextShowMnemonic, false,
+ text()).size();
+ if (!opt.icon.isNull())
+ sz = QSize(sz.width() + opt.iconSize.width() + 4, qMax(sz.height(), opt.iconSize.height()));
+ d->sizeHint = (style()->sizeFromContents(QStyle::CT_CheckBox, &opt, sz, this)
+ .expandedTo(QApplication::globalStrut()));
+ return d->sizeHint;
+}
+
+/*!\reimp
+*/
+void QCheckBox::paintEvent(QPaintEvent *)
+{
+ QStylePainter p(this);
+ QStyleOptionButton opt;
+ initStyleOption(&opt);
+ p.drawControl(QStyle::CE_CheckBox, opt);
+}
+
+/*!
+ \reimp
+*/
+void QCheckBox::mouseMoveEvent(QMouseEvent *e)
+{
+ Q_D(QCheckBox);
+ if (testAttribute(Qt::WA_Hover)) {
+ bool hit = false;
+ if (underMouse())
+ hit = hitButton(e->pos());
+
+ if (hit != d->hovering) {
+ update(rect());
+ d->hovering = hit;
+ }
+ }
+
+ QAbstractButton::mouseMoveEvent(e);
+}
+
+
+/*!\reimp*/
+bool QCheckBox::hitButton(const QPoint &pos) const
+{
+ QStyleOptionButton opt;
+ initStyleOption(&opt);
+ return style()->subElementRect(QStyle::SE_CheckBoxClickRect, &opt, this).contains(pos);
+}
+
+/*!\reimp*/
+void QCheckBox::checkStateSet()
+{
+ Q_D(QCheckBox);
+ d->noChange = false;
+ Qt::CheckState state = checkState();
+ if ((uint)state != d->publishedState) {
+ d->publishedState = state;
+ emit stateChanged(state);
+ }
+}
+
+/*!\reimp*/
+void QCheckBox::nextCheckState()
+{
+ Q_D(QCheckBox);
+ if (d->tristate)
+ setCheckState((Qt::CheckState)((checkState() + 1) % 3));
+ else {
+ QAbstractButton::nextCheckState();
+ QCheckBox::checkStateSet();
+ }
+}
+
+/*!
+ \reimp
+*/
+bool QCheckBox::event(QEvent *e)
+{
+ Q_D(QCheckBox);
+ if (e->type() == QEvent::StyleChange
+#ifdef Q_WS_MAC
+ || e->type() == QEvent::MacSizeChange
+#endif
+ )
+ d->setLayoutItemMargins(QStyle::SE_CheckBoxLayoutItem);
+ return QAbstractButton::event(e);
+}
+
+#ifdef QT3_SUPPORT
+/*!
+ Use one of the constructors that doesn't take the \a name
+ argument and then use setObjectName() instead.
+*/
+QCheckBox::QCheckBox(QWidget *parent, const char* name)
+ : QAbstractButton (*new QCheckBoxPrivate, parent)
+{
+ Q_D(QCheckBox);
+ setObjectName(QString::fromAscii(name));
+ d->init();
+}
+
+/*!
+ Use one of the constructors that doesn't take the \a name
+ argument and then use setObjectName() instead.
+*/
+QCheckBox::QCheckBox(const QString &text, QWidget *parent, const char* name)
+ : QAbstractButton (*new QCheckBoxPrivate, parent)
+{
+ Q_D(QCheckBox);
+ setObjectName(QString::fromAscii(name));
+ d->init();
+ setText(text);
+}
+
+#endif
+
+
+/*!
+ \fn void QCheckBox::setNoChange()
+ \compat
+
+ Use setCheckState() instead.
+*/
+
+/*!
+ \fn void QCheckBox::setState(ToggleState state)
+ \compat
+
+ Use setCheckState() instead.
+*/
+
+/*!
+ \fn QCheckBox::ToggleState QCheckBox::state() const
+ \compat
+
+ Use checkState() instead.
+*/
+
+QT_END_NAMESPACE
diff --git a/src/gui/widgets/qcheckbox.h b/src/gui/widgets/qcheckbox.h
new file mode 100644
index 0000000000..fcab7a7c3f
--- /dev/null
+++ b/src/gui/widgets/qcheckbox.h
@@ -0,0 +1,113 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QCHECKBOX_H
+#define QCHECKBOX_H
+
+#include <QtGui/qabstractbutton.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+class QCheckBoxPrivate;
+class QStyleOptionButton;
+
+class Q_GUI_EXPORT QCheckBox : public QAbstractButton
+{
+ Q_OBJECT
+
+ Q_PROPERTY(bool tristate READ isTristate WRITE setTristate)
+
+public:
+ explicit QCheckBox(QWidget *parent=0);
+ explicit QCheckBox(const QString &text, QWidget *parent=0);
+
+
+ QSize sizeHint() const;
+
+ void setTristate(bool y = true);
+ bool isTristate() const;
+
+ Qt::CheckState checkState() const;
+ void setCheckState(Qt::CheckState state);
+
+Q_SIGNALS:
+ void stateChanged(int);
+
+protected:
+ bool event(QEvent *e);
+ bool hitButton(const QPoint &pos) const;
+ void checkStateSet();
+ void nextCheckState();
+ void paintEvent(QPaintEvent *);
+ void mouseMoveEvent(QMouseEvent *);
+ void initStyleOption(QStyleOptionButton *option) const;
+
+#ifdef QT3_SUPPORT
+public:
+ enum ToggleState {
+ Off = Qt::Unchecked,
+ NoChange = Qt::PartiallyChecked,
+ On = Qt::Checked
+ };
+ inline QT3_SUPPORT ToggleState state() const
+ { return static_cast<QCheckBox::ToggleState>(static_cast<int>(checkState())); }
+ inline QT3_SUPPORT void setState(ToggleState state)
+ { setCheckState(static_cast<Qt::CheckState>(static_cast<int>(state))); }
+ inline QT3_SUPPORT void setNoChange()
+ { setCheckState(Qt::PartiallyChecked); }
+ QT3_SUPPORT_CONSTRUCTOR QCheckBox(QWidget *parent, const char* name);
+ QT3_SUPPORT_CONSTRUCTOR QCheckBox(const QString &text, QWidget *parent, const char* name);
+#endif
+
+private:
+ Q_DECLARE_PRIVATE(QCheckBox)
+ Q_DISABLE_COPY(QCheckBox)
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QCHECKBOX_H
diff --git a/src/gui/widgets/qcocoamenu_mac.mm b/src/gui/widgets/qcocoamenu_mac.mm
new file mode 100644
index 0000000000..c5977e4626
--- /dev/null
+++ b/src/gui/widgets/qcocoamenu_mac.mm
@@ -0,0 +1,187 @@
+/****************************************************************************
+ **
+ ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+ **
+ ** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+ **
+ ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
+ ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ **
+ ****************************************************************************/
+
+#include "qmacdefines_mac.h"
+#include "qapplication.h"
+#ifdef QT_MAC_USE_COCOA
+#import <private/qcocoamenu_mac_p.h>
+#import <private/qcocoamenuloader_mac_p.h>
+#include <private/qt_cocoa_helpers_mac_p.h>
+#include <private/qapplication_p.h>
+
+#include <QtGui/QMenu>
+
+QT_FORWARD_DECLARE_CLASS(QAction)
+QT_FORWARD_DECLARE_CLASS(QWidget)
+QT_FORWARD_DECLARE_CLASS(QApplication)
+
+@implementation QT_MANGLE_NAMESPACE(QCocoaMenu)
+
+- (id)initWithQMenu:(QMenu*)menu
+{
+ self = [super init];
+ if (self) {
+ qmenu = menu;
+ [self setAutoenablesItems:NO];
+ [self setDelegate:self];
+ }
+ return self;
+}
+
+- (void)menu:(NSMenu*)menu willHighlightItem:(NSMenuItem*)item;
+{
+ Q_UNUSED(menu);
+
+ if (!item) {
+ // ### According to the docs everything will be highlighted. Not sure what we should do in
+ // Qt, so just return.
+ return;
+ }
+
+ if (QAction *action = reinterpret_cast<QAction *>([item tag]))
+ action->activate(QAction::Hover);
+}
+
+- (void)menuWillOpen:(NSMenu*)menu;
+{
+ while (QWidget *popup
+ = QApplication::activePopupWidget())
+ popup->close();
+ qt_mac_emit_menuSignals(((QT_MANGLE_NAMESPACE(QCocoaMenu) *)menu)->qmenu, true);
+}
+
+- (void)menuWillClose:(NSMenu*)menu;
+{
+ qt_mac_emit_menuSignals(((QT_MANGLE_NAMESPACE(QCocoaMenu) *)menu)->qmenu, false);
+}
+
+- (BOOL)hasShortcut:(NSMenu *)menu forKey:(NSString *)key forModifiers:(NSUInteger)modifier
+ whichItem:(NSMenuItem**)outItem
+{
+ for (NSMenuItem *item in [menu itemArray]) {
+ if (![item isEnabled] || [item isHidden] || [item isSeparatorItem])
+ continue;
+ if ([item hasSubmenu]) {
+ if ([self hasShortcut:[item submenu]
+ forKey:key
+ forModifiers:modifier whichItem:outItem]) {
+ if (outItem)
+ *outItem = item;
+ return YES;
+ }
+ }
+ NSString *menuKey = [item keyEquivalent];
+ if (menuKey && NSOrderedSame == [menuKey compare:key]
+ && (modifier == [item keyEquivalentModifierMask])) {
+ if (outItem)
+ *outItem = item;
+ return YES;
+ }
+ }
+ if (outItem)
+ *outItem = 0;
+ return NO;
+}
+
+- (BOOL)menuHasKeyEquivalent:(NSMenu *)menu forEvent:(NSEvent *)event target:(id *)target action:(SEL *)action
+{
+ // Check if the menu actually has a keysequence defined for this key event.
+ // If it does, then we will first send the key sequence to the QWidget that has focus
+ // since (in Qt's eyes) it needs to a chance at the key event first. If the widget
+ // accepts the key event, we then return YES, but set the target and action to be nil,
+ // which means that the action should not be triggered. In every other case we return
+ // NO, which means that Cocoa can do as it pleases (i.e., fire the menu action).
+ NSMenuItem *whichItem;
+ if ([self hasShortcut:menu
+ forKey:[event characters]
+ forModifiers:([event modifierFlags] & NSDeviceIndependentModifierFlagsMask)
+ whichItem:&whichItem]) {
+ QWidget *widget = 0;
+ QAction *qaction = 0;
+ if (whichItem && [whichItem tag]) {
+ qaction = reinterpret_cast<QAction *>([whichItem tag]);
+ }
+ if (qApp->activePopupWidget())
+ widget = (qApp->activePopupWidget()->focusWidget() ?
+ qApp->activePopupWidget()->focusWidget() : qApp->activePopupWidget());
+ else if (QApplicationPrivate::focus_widget)
+ widget = QApplicationPrivate::focus_widget;
+ if (qaction && widget) {
+ int key = qaction->shortcut();
+ QKeyEvent accel_ev(QEvent::ShortcutOverride, (key & (~Qt::KeyboardModifierMask)),
+ Qt::KeyboardModifiers(key & Qt::KeyboardModifierMask));
+ accel_ev.ignore();
+ qt_sendSpontaneousEvent(widget, &accel_ev);
+ if (accel_ev.isAccepted()) {
+ *target = nil;
+ *action = nil;
+ return YES;
+ }
+ }
+ }
+ return NO;
+}
+
+@end
+
+QT_BEGIN_NAMESPACE
+extern int qt_mac_menus_open_count; // qmenu_mac.mm
+
+void qt_mac_emit_menuSignals(QMenu *menu, bool show)
+{
+ if (!menu)
+ return;
+ int delta;
+ if (show) {
+ emit menu->aboutToShow();
+ delta = 1;
+ } else {
+ emit menu->aboutToHide();
+ delta = -1;
+ }
+ qt_mac_menus_open_count += delta;
+}
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/gui/widgets/qcocoamenu_mac_p.h b/src/gui/widgets/qcocoamenu_mac_p.h
new file mode 100644
index 0000000000..1372f0910d
--- /dev/null
+++ b/src/gui/widgets/qcocoamenu_mac_p.h
@@ -0,0 +1,72 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//
+// 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 "qmacdefines_mac.h"
+#ifdef QT_MAC_USE_COCOA
+#import <Cocoa/Cocoa.h>
+
+QT_FORWARD_DECLARE_CLASS(QMenu)
+
+@interface QT_MANGLE_NAMESPACE(QCocoaMenu) : NSMenu
+{
+ QMenu *qmenu;
+}
+- (id)initWithQMenu:(QMenu*)menu;
+- (void)menu:(NSMenu*)menu willHighlightItem:(NSMenuItem*)item;
+- (void)menuWillOpen:(NSMenu*)menu;
+- (void)menuWillClose:(NSMenu*)menu;
+- (BOOL)hasShortcut:(NSMenu *)menu forKey:(NSString *)key forModifiers:(NSUInteger)modifier
+ whichItem:(NSMenuItem**)outItem;
+- (BOOL)menuHasKeyEquivalent:(NSMenu *)menu forEvent:(NSEvent *)event target:(id *)target action:(SEL *)action;
+@end
+#endif
+
diff --git a/src/gui/widgets/qcocoatoolbardelegate_mac.mm b/src/gui/widgets/qcocoatoolbardelegate_mac.mm
new file mode 100644
index 0000000000..a0ccaf3939
--- /dev/null
+++ b/src/gui/widgets/qcocoatoolbardelegate_mac.mm
@@ -0,0 +1,153 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#import <private/qcocoatoolbardelegate_mac_p.h>
+#ifdef QT_MAC_USE_COCOA
+#include <private/qmainwindowlayout_p.h>
+#include <private/qt_mac_p.h>
+#include <private/qcocoaview_mac_p.h>
+#include <private/qwidget_p.h>
+#include <qtoolbar.h>
+#include <qlayout.h>
+#include <qdebug.h>
+
+QT_BEGIN_NAMESPACE
+extern QWidgetPrivate *qt_widget_private(QWidget *widget);
+QT_END_NAMESPACE
+
+QT_FORWARD_DECLARE_CLASS(QMainWindowLayout);
+QT_FORWARD_DECLARE_CLASS(QToolBar);
+QT_FORWARD_DECLARE_CLASS(QCFString);
+
+@implementation QCocoaToolBarDelegate
+
+- (id)initWithMainWindowLayout:(QMainWindowLayout *)layout
+{
+ self = [super init];
+ if (self) {
+ mainWindowLayout = layout;
+ }
+ return self;
+}
+
+- (NSArray *)toolbarAllowedItemIdentifiers:(NSToolbar *)toolbar
+{
+ Q_UNUSED(toolbar);
+ return [NSArray arrayWithObject:@"com.trolltech.qt.nstoolbar-qtoolbar"];
+}
+
+- (NSArray *)toolbarDefaultItemIdentifiers:(NSToolbar *)toolbar
+{
+ return [self toolbarAllowedItemIdentifiers:toolbar];
+}
+
+- (void)toolbarDidRemoveItem:(NSNotification *)notification
+{
+ NSToolbarItem *item = [[notification userInfo] valueForKey:@"item"];
+ mainWindowLayout->unifiedToolbarHash.remove(item);
+ for (int i = 0; i < mainWindowLayout->toolbarItemsCopy.size(); ++i) {
+ if (mainWindowLayout->toolbarItemsCopy.at(i) == item) {
+ // I know about it, so release it.
+ mainWindowLayout->toolbarItemsCopy.removeAt(i);
+ mainWindowLayout->qtoolbarsInUnifiedToolbarList.removeAt(i);
+ [item release];
+ break;
+ }
+ }
+}
+
+- (NSToolbarItem *)toolbar:(NSToolbar *)nstoolbar itemForItemIdentifier:(NSString *)itemIdentifier
+ willBeInsertedIntoToolbar:(BOOL)flag
+{
+ Q_UNUSED(flag);
+ Q_UNUSED(nstoolbar);
+ QToolBar *tb = mainWindowLayout->cocoaItemIDToToolbarHash.value(QCFString::toQString(CFStringRef(itemIdentifier)));
+ NSToolbarItem *item = nil;
+ if (tb) {
+ item = [[NSToolbarItem alloc] initWithItemIdentifier:itemIdentifier];
+ mainWindowLayout->unifiedToolbarHash.insert(item, tb);
+ }
+ return item;
+}
+
+- (void)toolbarWillAddItem:(NSNotification *)notification
+{
+ NSToolbarItem *item = [[notification userInfo] valueForKey:@"item"];
+ QToolBar *tb = mainWindowLayout->cocoaItemIDToToolbarHash.value(QCFString::toQString(CFStringRef([item itemIdentifier])));
+ if (!tb)
+ return; // I can't really do anything about this.
+ [item retain];
+ [item setView:QT_PREPEND_NAMESPACE(qt_mac_nativeview_for)(tb)];
+
+ NSArray *items = [[qt_mac_window_for(mainWindowLayout->layoutState.mainWindow->window()) toolbar] items];
+ int someIndex = 0;
+ bool foundItem = false;
+ for (NSToolbarItem *i in items) {
+ if (i == item) {
+ foundItem = true;
+ break;
+ }
+ ++someIndex;
+ }
+ mainWindowLayout->toolbarItemsCopy.insert(someIndex, item);
+
+ // This is synchronization code that was needed in Carbon, but may not be needed anymore here.
+ QToolBar *toolbar = mainWindowLayout->unifiedToolbarHash.value(item);
+ if (toolbar) {
+ int toolbarIndex = mainWindowLayout->qtoolbarsInUnifiedToolbarList.indexOf(toolbar);
+ if (someIndex != toolbarIndex) {
+ // Dang, we must be out of sync, rebuild it from the "toolbarItemsCopy"
+ mainWindowLayout->qtoolbarsInUnifiedToolbarList.clear();
+ for (int i = 0; i < mainWindowLayout->toolbarItemsCopy.size(); ++i) {
+ // This will either append the correct toolbar or an
+ // null toolbar. This is fine because this list
+ // is really only kept to make sure that things are but in the right order.
+ mainWindowLayout->qtoolbarsInUnifiedToolbarList.append(
+ mainWindowLayout->unifiedToolbarHash.value(mainWindowLayout->
+ toolbarItemsCopy.at(i)));
+ }
+ }
+ toolbar->update();
+ }
+}
+
+@end
+#endif // QT_MAC_USE_COCOA
diff --git a/src/gui/widgets/qcocoatoolbardelegate_mac_p.h b/src/gui/widgets/qcocoatoolbardelegate_mac_p.h
new file mode 100644
index 0000000000..a32e936262
--- /dev/null
+++ b/src/gui/widgets/qcocoatoolbardelegate_mac_p.h
@@ -0,0 +1,71 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//
+// 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 "qmacdefines_mac.h"
+#ifdef QT_MAC_USE_COCOA
+#import <Cocoa/Cocoa.h>
+
+QT_BEGIN_NAMESPACE
+class QMainWindowLayout;
+class QToolBar;
+QT_END_NAMESPACE
+
+@class NSToolbarItem;
+
+@interface QCocoaToolBarDelegate : NSObject {
+ QT_PREPEND_NAMESPACE(QMainWindowLayout) *mainWindowLayout;
+ NSToolbarItem *toolbarItem;
+}
+
+- (id)initWithMainWindowLayout:(QT_PREPEND_NAMESPACE(QMainWindowLayout) *)layout;
+@end
+#endif
diff --git a/src/gui/widgets/qcombobox.cpp b/src/gui/widgets/qcombobox.cpp
new file mode 100644
index 0000000000..9a0c40448f
--- /dev/null
+++ b/src/gui/widgets/qcombobox.cpp
@@ -0,0 +1,3186 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qcombobox.h"
+
+#ifndef QT_NO_COMBOBOX
+#include <qstylepainter.h>
+#include <qlineedit.h>
+#include <qapplication.h>
+#include <qdesktopwidget.h>
+#include <qlistview.h>
+#include <qtableview.h>
+#include <qitemdelegate.h>
+#include <qmap.h>
+#include <qmenu.h>
+#include <qevent.h>
+#include <qlayout.h>
+#include <qscrollbar.h>
+#include <qtreeview.h>
+#include <qheaderview.h>
+#ifndef QT_NO_IM
+#include "qinputcontext.h"
+#endif
+#include <private/qcombobox_p.h>
+#include <private/qabstractitemmodel_p.h>
+#include <qdebug.h>
+
+#ifdef Q_WS_X11
+#include <private/qt_x11_p.h>
+#endif
+#if defined(Q_WS_MAC) && !defined(QT_NO_EFFECTS) && !defined(QT_NO_STYLE_MAC)
+#include <private/qcore_mac_p.h>
+#include <QMacStyle>
+#include <private/qt_cocoa_helpers_mac_p.h>
+#endif
+#ifndef QT_NO_EFFECTS
+# include <private/qeffects_p.h>
+#endif
+QT_BEGIN_NAMESPACE
+
+extern QHash<QByteArray, QFont> *qt_app_fonts_hash();
+
+QComboBoxPrivate::QComboBoxPrivate()
+ : QWidgetPrivate(),
+ model(0),
+ lineEdit(0),
+ container(0),
+ insertPolicy(QComboBox::InsertAtBottom),
+ sizeAdjustPolicy(QComboBox::AdjustToContentsOnFirstShow),
+ minimumContentsLength(0),
+ shownOnce(false),
+ autoCompletion(true),
+ duplicatesEnabled(false),
+ frame(true),
+ maxVisibleItems(10),
+ maxCount(INT_MAX),
+ modelColumn(0),
+ inserting(false),
+ arrowState(QStyle::State_None),
+ hoverControl(QStyle::SC_None),
+ autoCompletionCaseSensitivity(Qt::CaseInsensitive),
+ indexBeforeChange(-1)
+#ifndef QT_NO_COMPLETER
+ , completer(0)
+#endif
+{
+}
+
+QStyleOptionMenuItem QComboMenuDelegate::getStyleOption(const QStyleOptionViewItem &option,
+ const QModelIndex &index) const
+{
+ QStyleOptionMenuItem menuOption;
+ menuOption.palette = QComboBoxPrivate::viewContainerPalette(mCombo).resolve(QApplication::palette("QMenu"));
+ menuOption.state = QStyle::State_None;
+ if (mCombo->window()->isActiveWindow())
+ menuOption.state = QStyle::State_Active;
+ if ((option.state & QStyle::State_Enabled) && (index.model()->flags(index) & Qt::ItemIsEnabled))
+ menuOption.state |= QStyle::State_Enabled;
+ else
+ menuOption.palette.setCurrentColorGroup(QPalette::Disabled);
+ if (option.state & QStyle::State_Selected)
+ menuOption.state |= QStyle::State_Selected;
+ menuOption.checkType = QStyleOptionMenuItem::NonExclusive;
+ menuOption.checked = mCombo->currentIndex() == index.row();
+ if (QComboBoxDelegate::isSeparator(index))
+ menuOption.menuItemType = QStyleOptionMenuItem::Separator;
+ else
+ menuOption.menuItemType = QStyleOptionMenuItem::Normal;
+
+ QVariant variant = index.model()->data(index, Qt::DecorationRole);
+ switch (variant.type()) {
+ case QVariant::Icon:
+ menuOption.icon = qvariant_cast<QIcon>(variant);
+ break;
+ case QVariant::Color: {
+ static QPixmap pixmap(option.decorationSize);
+ pixmap.fill(qvariant_cast<QColor>(variant));
+ menuOption.icon = pixmap;
+ break; }
+ default:
+ menuOption.icon = qvariant_cast<QPixmap>(variant);
+ break;
+ }
+
+ menuOption.text = index.model()->data(index, Qt::DisplayRole).toString()
+ .replace(QLatin1Char('&'), QLatin1String("&&"));
+ menuOption.tabWidth = 0;
+ menuOption.maxIconWidth = option.decorationSize.width() + 4;
+ menuOption.menuRect = option.rect;
+ menuOption.rect = option.rect;
+
+ // Make sure fonts set on the combo box also overrides the font for the popup menu.
+ if (mCombo->testAttribute(Qt::WA_SetFont) || mCombo->testAttribute(Qt::WA_MacSmallSize)
+ || mCombo->testAttribute(Qt::WA_MacMiniSize))
+ menuOption.font = mCombo->font();
+ else
+ menuOption.font = qt_app_fonts_hash()->value("QComboMenuItem", mCombo->font());
+
+ menuOption.fontMetrics = QFontMetrics(menuOption.font);
+
+ return menuOption;
+}
+
+#ifdef QT_KEYPAD_NAVIGATION
+void QComboBoxPrivate::_q_completerActivated()
+{
+ Q_Q(QComboBox);
+ if ( QApplication::keypadNavigationEnabled()
+ && q->isEditable()
+ && q->completer()
+ && q->completer()->completionMode() == QCompleter::UnfilteredPopupCompletion ) {
+ q->setEditFocus(false);
+ }
+}
+#endif
+
+void QComboBoxPrivate::updateArrow(QStyle::StateFlag state)
+{
+ Q_Q(QComboBox);
+ if (arrowState == state)
+ return;
+ arrowState = state;
+ QStyleOptionComboBox opt;
+ q->initStyleOption(&opt);
+ q->update(q->style()->subControlRect(QStyle::CC_ComboBox, &opt, QStyle::SC_ComboBoxArrow, q));
+}
+
+void QComboBoxPrivate::_q_modelReset()
+{
+ Q_Q(QComboBox);
+ if (lineEdit) {
+ lineEdit->setText(QString());
+ updateLineEditGeometry();
+ }
+ q->update();
+}
+
+void QComboBoxPrivate::_q_modelDestroyed()
+{
+ model = QAbstractItemModelPrivate::staticEmptyModel();
+}
+
+
+//Windows and KDE allows menus to cover the taskbar, while GNOME and Mac don't
+QRect QComboBoxPrivate::popupGeometry(int screen) const
+{
+#ifdef Q_WS_WIN
+ return QApplication::desktop()->screenGeometry(screen);
+#elif defined Q_WS_X11
+ if (X11->desktopEnvironment == DE_KDE)
+ return QApplication::desktop()->screenGeometry(screen);
+ else
+ return QApplication::desktop()->availableGeometry(screen);
+#else
+ return QApplication::desktop()->availableGeometry(screen);
+#endif
+}
+
+bool QComboBoxPrivate::updateHoverControl(const QPoint &pos)
+{
+
+ Q_Q(QComboBox);
+ QRect lastHoverRect = hoverRect;
+ QStyle::SubControl lastHoverControl = hoverControl;
+ bool doesHover = q->testAttribute(Qt::WA_Hover);
+ if (lastHoverControl != newHoverControl(pos) && doesHover) {
+ q->update(lastHoverRect);
+ q->update(hoverRect);
+ return true;
+ }
+ return !doesHover;
+}
+
+QStyle::SubControl QComboBoxPrivate::newHoverControl(const QPoint &pos)
+{
+ Q_Q(QComboBox);
+ QStyleOptionComboBox opt;
+ q->initStyleOption(&opt);
+ opt.subControls = QStyle::SC_All;
+ hoverControl = q->style()->hitTestComplexControl(QStyle::CC_ComboBox, &opt, pos, q);
+ hoverRect = (hoverControl != QStyle::SC_None)
+ ? q->style()->subControlRect(QStyle::CC_ComboBox, &opt, hoverControl, q)
+ : QRect();
+ return hoverControl;
+}
+
+/*
+ Computes a size hint based on the maximum width
+ for the items in the combobox.
+*/
+int QComboBoxPrivate::computeWidthHint() const
+{
+ Q_Q(const QComboBox);
+
+ int width = 0;
+ const int count = q->count();
+ const int iconWidth = q->iconSize().width() + 4;
+ const QFontMetrics &fontMetrics = q->fontMetrics();
+
+ for (int i = 0; i < count; ++i) {
+ const int textWidth = fontMetrics.width(q->itemText(i));
+ if (q->itemIcon(i).isNull())
+ width = (qMax(width, textWidth));
+ else
+ width = (qMax(width, textWidth + iconWidth));
+ }
+
+ QStyleOptionComboBox opt;
+ q->initStyleOption(&opt);
+ QSize tmp(width, 0);
+ tmp = q->style()->sizeFromContents(QStyle::CT_ComboBox, &opt, tmp, q);
+ return tmp.width();
+}
+
+QSize QComboBoxPrivate::recomputeSizeHint(QSize &sh) const
+{
+ Q_Q(const QComboBox);
+ if (!sh.isValid()) {
+ bool hasIcon = sizeAdjustPolicy == QComboBox::AdjustToMinimumContentsLengthWithIcon ? true : false;
+ int count = q->count();
+ QSize iconSize = q->iconSize();
+ const QFontMetrics &fm = q->fontMetrics();
+
+ // text width
+ if (&sh == &sizeHint || minimumContentsLength == 0) {
+ switch (sizeAdjustPolicy) {
+ case QComboBox::AdjustToContents:
+ case QComboBox::AdjustToContentsOnFirstShow:
+ if (count == 0) {
+ sh.rwidth() = 7 * fm.width(QLatin1Char('x'));
+ } else {
+ for (int i = 0; i < count; ++i) {
+ if (!q->itemIcon(i).isNull()) {
+ hasIcon = true;
+ sh.setWidth(qMax(sh.width(), fm.boundingRect(q->itemText(i)).width() + iconSize.width() + 4));
+ } else {
+ sh.setWidth(qMax(sh.width(), fm.boundingRect(q->itemText(i)).width()));
+ }
+ }
+ }
+ break;
+ case QComboBox::AdjustToMinimumContentsLength:
+ for (int i = 0; i < count && !hasIcon; ++i)
+ hasIcon = !q->itemIcon(i).isNull();
+ default:
+ ;
+ }
+ } else {
+ for (int i = 0; i < count && !hasIcon; ++i)
+ hasIcon = !q->itemIcon(i).isNull();
+ }
+ if (minimumContentsLength > 0)
+ sh.setWidth(qMax(sh.width(), minimumContentsLength * fm.width(QLatin1Char('X')) + (hasIcon ? iconSize.width() + 4 : 0)));
+
+
+ // height
+ sh.setHeight(qMax(fm.lineSpacing(), 14) + 2);
+ if (hasIcon) {
+ sh.setHeight(qMax(sh.height(), iconSize.height() + 2));
+ }
+
+ // add style and strut values
+ QStyleOptionComboBox opt;
+ q->initStyleOption(&opt);
+ sh = q->style()->sizeFromContents(QStyle::CT_ComboBox, &opt, sh, q);
+ }
+ return sh.expandedTo(QApplication::globalStrut());
+}
+
+void QComboBoxPrivate::adjustComboBoxSize()
+{
+ viewContainer()->adjustSizeTimer.start(20, container);
+}
+
+void QComboBoxPrivate::updateLayoutDirection()
+{
+ Q_Q(const QComboBox);
+ QStyleOptionComboBox opt;
+ q->initStyleOption(&opt);
+ Qt::LayoutDirection dir = Qt::LayoutDirection(
+ q->style()->styleHint(QStyle::SH_ComboBox_LayoutDirection, &opt, q));
+ if (lineEdit)
+ lineEdit->setLayoutDirection(dir);
+ if (container)
+ container->setLayoutDirection(dir);
+}
+
+
+void QComboBoxPrivateContainer::timerEvent(QTimerEvent *timerEvent)
+{
+ if (timerEvent->timerId() == adjustSizeTimer.timerId()) {
+ adjustSizeTimer.stop();
+ if (combo->sizeAdjustPolicy() == QComboBox::AdjustToContents) {
+ combo->adjustSize();
+ combo->update();
+ }
+ }
+}
+
+void QComboBoxPrivateContainer::resizeEvent(QResizeEvent *e)
+{
+ QStyleOptionComboBox opt = comboStyleOption();
+ if (combo->style()->styleHint(QStyle::SH_ComboBox_Popup, &opt, combo)) {
+ QStyleOption myOpt;
+ myOpt.initFrom(this);
+ QStyleHintReturnMask mask;
+ if (combo->style()->styleHint(QStyle::SH_Menu_Mask, &myOpt, this, &mask)) {
+ setMask(mask.region);
+ }
+ } else {
+ clearMask();
+ }
+ QFrame::resizeEvent(e);
+}
+
+void QComboBoxPrivateContainer::leaveEvent(QEvent *)
+{
+// On Mac using the Mac style we want to clear the selection
+// when the mouse moves outside the popup.
+#ifdef Q_WS_MAC
+ QStyleOptionComboBox opt = comboStyleOption();
+ if (combo->style()->styleHint(QStyle::SH_ComboBox_Popup, &opt, combo))
+ view->clearSelection();
+#endif
+}
+
+QComboBoxPrivateContainer::QComboBoxPrivateContainer(QAbstractItemView *itemView, QComboBox *parent)
+ : QFrame(parent, Qt::Popup), combo(parent), view(0), top(0), bottom(0)
+{
+ // we need the combobox and itemview
+ Q_ASSERT(parent);
+ Q_ASSERT(itemView);
+
+ setAttribute(Qt::WA_WindowPropagation);
+ setAttribute(Qt::WA_X11NetWmWindowTypeCombo);
+
+ // setup container
+ blockMouseReleaseTimer.setSingleShot(true);
+
+ // we need a vertical layout
+ QBoxLayout *layout = new QBoxLayout(QBoxLayout::TopToBottom, this);
+ layout->setSpacing(0);
+ layout->setMargin(0);
+
+ // set item view
+ setItemView(itemView);
+
+ // add scroller arrows if style needs them
+ QStyleOptionComboBox opt = comboStyleOption();
+ const bool usePopup = combo->style()->styleHint(QStyle::SH_ComboBox_Popup, &opt, combo);
+ if (usePopup) {
+ top = new QComboBoxPrivateScroller(QAbstractSlider::SliderSingleStepSub, this);
+ bottom = new QComboBoxPrivateScroller(QAbstractSlider::SliderSingleStepAdd, this);
+ top->hide();
+ bottom->hide();
+ } else {
+ setLineWidth(1);
+ }
+
+ setFrameStyle(combo->style()->styleHint(QStyle::SH_ComboBox_PopupFrameStyle, &opt, combo));
+
+ if (top) {
+ layout->insertWidget(0, top);
+ connect(top, SIGNAL(doScroll(int)), this, SLOT(scrollItemView(int)));
+ }
+ if (bottom) {
+ layout->addWidget(bottom);
+ connect(bottom, SIGNAL(doScroll(int)), this, SLOT(scrollItemView(int)));
+ }
+
+ // Some styles (Mac) have a margin at the top and bottom of the popup.
+ layout->insertSpacing(0, 0);
+ layout->addSpacing(0);
+ updateTopBottomMargin();
+}
+
+void QComboBoxPrivateContainer::scrollItemView(int action)
+{
+#ifndef QT_NO_SCROLLBAR
+ if (view->verticalScrollBar())
+ view->verticalScrollBar()->triggerAction(static_cast<QAbstractSlider::SliderAction>(action));
+#endif
+}
+
+/*
+ Hides or shows the scrollers when we emulate a popupmenu
+*/
+void QComboBoxPrivateContainer::updateScrollers()
+{
+#ifndef QT_NO_SCROLLBAR
+ if (!top || !bottom)
+ return;
+
+ if (isVisible() == false)
+ return;
+
+ QStyleOptionComboBox opt = comboStyleOption();
+ if (combo->style()->styleHint(QStyle::SH_ComboBox_Popup, &opt, combo) &&
+ view->verticalScrollBar()->minimum() < view->verticalScrollBar()->maximum()) {
+
+ bool needTop = view->verticalScrollBar()->value()
+ > (view->verticalScrollBar()->minimum() + spacing());
+ bool needBottom = view->verticalScrollBar()->value()
+ < (view->verticalScrollBar()->maximum() - spacing()*2);
+ if (needTop)
+ top->show();
+ else
+ top->hide();
+ if (needBottom)
+ bottom->show();
+ else
+ bottom->hide();
+ } else {
+ top->hide();
+ bottom->hide();
+ }
+#endif // QT_NO_SCROLLBAR
+}
+
+/*
+ Cleans up when the view is destroyed.
+*/
+void QComboBoxPrivateContainer::viewDestroyed()
+{
+ view = 0;
+ setItemView(new QComboBoxListView());
+}
+
+/*
+ Sets currentIndex on entered if the LeftButton is not pressed. This
+ means that if mouseTracking(...) is on, we setCurrentIndex and select
+ even when LeftButton is not pressed.
+*/
+void QComboBoxPrivateContainer::setCurrentIndex(const QModelIndex &index)
+{
+ if (QComboBoxDelegate::isSeparator(index))
+ return;
+ view->setCurrentIndex(index);
+}
+
+/*
+ Returns the item view used for the combobox popup.
+*/
+QAbstractItemView *QComboBoxPrivateContainer::itemView() const
+{
+ return view;
+}
+
+/*!
+ Sets the item view to be used for the combobox popup.
+*/
+void QComboBoxPrivateContainer::setItemView(QAbstractItemView *itemView)
+{
+ Q_ASSERT(itemView);
+
+ // clean up old one
+ if (view) {
+ view->removeEventFilter(this);
+ view->viewport()->removeEventFilter(this);
+#ifndef QT_NO_SCROLLBAR
+ disconnect(view->verticalScrollBar(), SIGNAL(valueChanged(int)),
+ this, SLOT(updateScrollers()));
+ disconnect(view->verticalScrollBar(), SIGNAL(rangeChanged(int,int)),
+ this, SLOT(updateScrollers()));
+#endif
+ disconnect(view, SIGNAL(entered(QModelIndex)),
+ this, SLOT(setCurrentIndex(QModelIndex)));
+ disconnect(view, SIGNAL(destroyed()),
+ this, SLOT(viewDestroyed()));
+ delete view;
+ view = 0;
+ }
+
+ // setup the item view
+ view = itemView;
+ view->setParent(this);
+ view->setAttribute(Qt::WA_MacShowFocusRect, false);
+ qobject_cast<QBoxLayout*>(layout())->insertWidget(top ? 2 : 0, view);
+ view->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored);
+ view->installEventFilter(this);
+ view->viewport()->installEventFilter(this);
+ view->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
+ QStyleOptionComboBox opt = comboStyleOption();
+ const bool usePopup = combo->style()->styleHint(QStyle::SH_ComboBox_Popup, &opt, combo);
+#ifndef QT_NO_SCROLLBAR
+ if (usePopup)
+ view->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
+#endif
+ if (combo->style()->styleHint(QStyle::SH_ComboBox_ListMouseTracking, &opt, combo) ||
+ usePopup) {
+ view->setMouseTracking(true);
+ }
+ view->setSelectionMode(QAbstractItemView::SingleSelection);
+ view->setFrameStyle(QFrame::NoFrame);
+ view->setLineWidth(0);
+ view->setEditTriggers(QAbstractItemView::NoEditTriggers);
+#ifndef QT_NO_SCROLLBAR
+ connect(view->verticalScrollBar(), SIGNAL(valueChanged(int)),
+ this, SLOT(updateScrollers()));
+ connect(view->verticalScrollBar(), SIGNAL(rangeChanged(int,int)),
+ this, SLOT(updateScrollers()));
+#endif
+ connect(view, SIGNAL(entered(QModelIndex)),
+ this, SLOT(setCurrentIndex(QModelIndex)));
+ connect(view, SIGNAL(destroyed()),
+ this, SLOT(viewDestroyed()));
+}
+
+/*!
+ Returns the spacing between the items in the view.
+*/
+int QComboBoxPrivateContainer::spacing() const
+{
+ QListView *lview = qobject_cast<QListView*>(view);
+ if (lview)
+ return lview->spacing();
+#ifndef QT_NO_TABLEVIEW
+ QTableView *tview = qobject_cast<QTableView*>(view);
+ if (tview)
+ return tview->showGrid() ? 1 : 0;
+#endif
+ return 0;
+}
+
+void QComboBoxPrivateContainer::updateTopBottomMargin()
+{
+ if (!layout() || layout()->count() < 1)
+ return;
+
+ QBoxLayout *boxLayout = qobject_cast<QBoxLayout *>(layout());
+ if (!boxLayout)
+ return;
+
+ const QStyleOptionComboBox opt = comboStyleOption();
+ const bool usePopup = combo->style()->styleHint(QStyle::SH_ComboBox_Popup, &opt, combo);
+ const int margin = usePopup ? combo->style()->pixelMetric(QStyle::PM_MenuVMargin, &opt, combo) : 0;
+
+ QSpacerItem *topSpacer = boxLayout->itemAt(0)->spacerItem();
+ if (topSpacer)
+ topSpacer->changeSize(0, margin, QSizePolicy::Minimum, QSizePolicy::Fixed);
+
+ QSpacerItem *bottomSpacer = boxLayout->itemAt(boxLayout->count() - 1)->spacerItem();
+ if (bottomSpacer && bottomSpacer != topSpacer)
+ bottomSpacer->changeSize(0, margin, QSizePolicy::Minimum, QSizePolicy::Fixed);
+
+ boxLayout->invalidate();
+}
+
+void QComboBoxPrivateContainer::changeEvent(QEvent *e)
+{
+ if (e->type() == QEvent::StyleChange) {
+ QStyleOptionComboBox opt = comboStyleOption();
+ view->setMouseTracking(combo->style()->styleHint(QStyle::SH_ComboBox_ListMouseTracking, &opt, combo) ||
+ combo->style()->styleHint(QStyle::SH_ComboBox_Popup, &opt, combo));
+ setFrameStyle(combo->style()->styleHint(QStyle::SH_ComboBox_PopupFrameStyle, &opt, combo));
+ }
+ QWidget::changeEvent(e);
+}
+
+
+bool QComboBoxPrivateContainer::eventFilter(QObject *o, QEvent *e)
+{
+ switch (e->type()) {
+ case QEvent::ShortcutOverride:
+ switch (static_cast<QKeyEvent*>(e)->key()) {
+ case Qt::Key_Enter:
+ case Qt::Key_Return:
+#ifdef QT_KEYPAD_NAVIGATION
+ case Qt::Key_Select:
+#endif
+ if (view->currentIndex().isValid() && (view->currentIndex().flags() & Qt::ItemIsEnabled) ) {
+ combo->hidePopup();
+ emit itemSelected(view->currentIndex());
+ }
+ return true;
+ case Qt::Key_Down:
+ if (!(static_cast<QKeyEvent*>(e)->modifiers() & Qt::AltModifier))
+ break;
+ // fall through
+ case Qt::Key_F4:
+ case Qt::Key_Escape:
+#ifdef QT_KEYPAD_NAVIGATION
+ case Qt::Key_Back:
+#endif
+ combo->hidePopup();
+ return true;
+ default:
+ break;
+ }
+ break;
+ case QEvent::MouseMove: {
+ if (isVisible()) {
+ QMouseEvent *m = static_cast<QMouseEvent *>(e);
+ QWidget *widget = static_cast<QWidget *>(o);
+ QPoint vector = widget->mapToGlobal(m->pos()) - initialClickPosition;
+ if (vector.manhattanLength() > 9 && blockMouseReleaseTimer.isActive())
+ blockMouseReleaseTimer.stop();
+ }
+ break;
+ }
+ case QEvent::MouseButtonRelease: {
+ QMouseEvent *m = static_cast<QMouseEvent *>(e);
+ if (isVisible() && view->rect().contains(m->pos()) && view->currentIndex().isValid()
+ && !blockMouseReleaseTimer.isActive()
+ && (view->currentIndex().flags() & Qt::ItemIsEnabled)
+ && (view->currentIndex().flags() & Qt::ItemIsSelectable)) {
+ combo->hidePopup();
+ emit itemSelected(view->currentIndex());
+ return true;
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ return QFrame::eventFilter(o, e);
+}
+
+void QComboBoxPrivateContainer::showEvent(QShowEvent *)
+{
+ combo->update();
+}
+
+void QComboBoxPrivateContainer::hideEvent(QHideEvent *)
+{
+ emit resetButton();
+ combo->update();
+}
+
+void QComboBoxPrivateContainer::mousePressEvent(QMouseEvent *e)
+{
+
+ QStyleOptionComboBox opt = comboStyleOption();
+ opt.subControls = QStyle::SC_All;
+ opt.activeSubControls = QStyle::SC_ComboBoxArrow;
+ QStyle::SubControl sc = combo->style()->hitTestComplexControl(QStyle::CC_ComboBox, &opt,
+ combo->mapFromGlobal(e->globalPos()),
+ combo);
+ if ((combo->isEditable() && sc == QStyle::SC_ComboBoxArrow)
+ || (!combo->isEditable() && sc != QStyle::SC_None))
+ setAttribute(Qt::WA_NoMouseReplay);
+ combo->hidePopup();
+}
+
+void QComboBoxPrivateContainer::mouseReleaseEvent(QMouseEvent *e)
+{
+ Q_UNUSED(e);
+ if (!blockMouseReleaseTimer.isActive()){
+ combo->hidePopup();
+ emit resetButton();
+ }
+}
+
+QStyleOptionComboBox QComboBoxPrivateContainer::comboStyleOption() const
+{
+ // ### This should use QComboBox's initStyleOption(), but it's protected
+ // perhaps, we could cheat by having the QCombo private instead?
+ QStyleOptionComboBox opt;
+ opt.initFrom(combo);
+ opt.subControls = QStyle::SC_All;
+ opt.activeSubControls = QStyle::SC_None;
+ opt.editable = combo->isEditable();
+ return opt;
+}
+
+/*!
+ \enum QComboBox::InsertPolicy
+
+ This enum specifies what the QComboBox should do when a new string is
+ entered by the user.
+
+ \value NoInsert The string will not be inserted into the combobox.
+ \value InsertAtTop The string will be inserted as the first item in the combobox.
+ \value InsertAtCurrent The current item will be \e replaced by the string.
+ \value InsertAtBottom The string will be inserted after the last item in the combobox.
+ \value InsertAfterCurrent The string is inserted after the current item in the combobox.
+ \value InsertBeforeCurrent The string is inserted before the current item in the combobox.
+ \value InsertAlphabetically The string is inserted in the alphabetic order in the combobox.
+ \omitvalue NoInsertion
+ \omitvalue AtTop
+ \omitvalue AtCurrent
+ \omitvalue AtBottom
+ \omitvalue AfterCurrent
+ \omitvalue BeforeCurrent
+*/
+
+/*!
+ \enum QComboBox::SizeAdjustPolicy
+
+ This enum specifies how the size hint of the QComboBox should
+ adjust when new content is added or content changes.
+
+ \value AdjustToContents The combobox will always adjust to the contents
+ \value AdjustToContentsOnFirstShow The combobox will adjust to its contents the first time it is shown.
+ \value AdjustToMinimumContentsLength Use AdjustToContents or AdjustToContentsOnFirstShow instead.
+ \value AdjustToMinimumContentsLengthWithIcon The combobox will adjust to \l minimumContentsLength plus space for an icon. For performance reasons use this policy on large models.
+*/
+
+/*!
+ \fn void QComboBox::activated(int index)
+
+ This signal is sent when the user chooses an item in the combobox.
+ The item's \a index is passed. Note that this signal is sent even
+ when the choice is not changed. If you need to know when the
+ choice actually changes, use signal currentIndexChanged().
+
+*/
+
+/*!
+ \fn void QComboBox::activated(const QString &text)
+
+ This signal is sent when the user chooses an item in the combobox.
+ The item's \a text is passed. Note that this signal is sent even
+ when the choice is not changed. If you need to know when the
+ choice actually changes, use signal currentIndexChanged().
+
+*/
+
+/*!
+ \fn void QComboBox::highlighted(int index)
+
+ This signal is sent when an item in the combobox popup list is
+ highlighted by the user. The item's \a index is passed.
+*/
+
+/*!
+ \fn void QComboBox::highlighted(const QString &text)
+
+ This signal is sent when an item in the combobox popup list is
+ highlighted by the user. The item's \a text is passed.
+*/
+
+/*!
+ \fn void QComboBox::currentIndexChanged(int index)
+ \since 4.1
+
+ This signal is sent whenever the currentIndex in the combobox
+ changes either through user interaction or programmatically. The
+ item's \a index is passed or -1 if the combobox becomes empty or the
+ currentIndex was reset.
+*/
+
+/*!
+ \fn void QComboBox::currentIndexChanged(const QString &text)
+ \since 4.1
+
+ This signal is sent whenever the currentIndex in the combobox
+ changes either through user interaction or programmatically. The
+ item's \a text is passed.
+*/
+
+/*!
+ Constructs a combobox with the given \a parent, using the default
+ model QStandardItemModel.
+*/
+QComboBox::QComboBox(QWidget *parent)
+ : QWidget(*new QComboBoxPrivate(), parent, 0)
+{
+ Q_D(QComboBox);
+ d->init();
+}
+
+/*!
+ \internal
+*/
+QComboBox::QComboBox(QComboBoxPrivate &dd, QWidget *parent)
+ : QWidget(dd, parent, 0)
+{
+ Q_D(QComboBox);
+ d->init();
+}
+
+#ifdef QT3_SUPPORT
+/*!
+ Use one of the constructors that doesn't take the \a name
+ argument and then use setObjectName() instead.
+*/
+QComboBox::QComboBox(QWidget *parent, const char *name)
+ : QWidget(*new QComboBoxPrivate(), parent, 0)
+{
+ Q_D(QComboBox);
+ d->init();
+ setObjectName(QString::fromAscii(name));
+}
+
+/*!
+ Use one of the constructors that doesn't take the \a name
+ argument and then use setObjectName() instead.
+*/
+QComboBox::QComboBox(bool rw, QWidget *parent, const char *name)
+ : QWidget(*new QComboBoxPrivate(), parent, 0)
+{
+ Q_D(QComboBox);
+ d->init();
+ setEditable(rw);
+ setObjectName(QString::fromAscii(name));
+}
+
+#endif //QT3_SUPPORT
+
+/*!
+ \class QComboBox
+ \brief The QComboBox widget is a combined button and popup list.
+
+ \ingroup basicwidgets
+ \mainclass
+
+ A QComboBox provides a means of presenting a list of options to the user
+ in a way that takes up the minimum amount of screen space.
+
+ A combobox is a selection widget that displays the current item,
+ and can pop up a list of selectable items. A combobox may be editable,
+ allowing the user to modify each item in the list.
+
+ Comboboxes can contain pixmaps as well as strings; the
+ insertItem() and setItemText() functions are suitably overloaded.
+ For editable comboboxes, the function clearEditText() is provided,
+ to clear the displayed string without changing the combobox's
+ contents.
+
+ There are two signals emitted if the current item of a combobox
+ changes, currentIndexChanged() and activated().
+ currentIndexChanged() is always emitted regardless if the change
+ was done programmatically or by user interaction, while
+ activated() is only emitted when the change is caused by user
+ interaction. The highlighted() signal is emitted when the user
+ highlights an item in the combobox popup list. All three signals
+ exist in two versions, one with a QString argument and one with an
+ \c int argument. If the user selectes or highlights a pixmap, only
+ the \c int signals are emitted. Whenever the text of an editable
+ combobox is changed the editTextChanged() 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 \l AtBottom but you can change
+ this using setInsertPolicy().
+
+ It is possible to constrain the input to an editable combobox
+ using QValidator; see setValidator(). By default, any input is
+ accepted.
+
+ A combobox can be populated using the insert functions,
+ insertItem() and insertItems() for example. Items can be
+ changed with setItemText(). 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 setCurrentIndex(). 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
+ setCompleter() and whether or not the user can add duplicates
+ is set with setDuplicatesEnabled().
+
+ QComboBox uses the \l{Model/View Programming}{model/view
+ framework} for its popup list and to store its items. By default
+ a QStandardItemModel stores the items and a QListView subclass
+ displays the popuplist. You can access the model and view directly
+ (with model() and view()), but QComboBox also provides functions
+ to set and get item data (e.g., setItemData() and itemText()). You
+ can also set a new model and view (with setModel() and setView()).
+ For the text and icon in the combobox label, the data in the model
+ that has the Qt::DisplayRole and Qt::DecorationRole is used.
+
+ \image qstyle-comboboxes.png Comboboxes in the different built-in styles.
+
+ \sa QLineEdit, QSpinBox, QRadioButton, QButtonGroup,
+ {fowler}{GUI Design Handbook: Combo Box, Drop-Down List Box}
+*/
+
+void QComboBoxPrivate::init()
+{
+ Q_Q(QComboBox);
+ q->setFocusPolicy(Qt::WheelFocus);
+ q->setSizePolicy(QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed,
+ QSizePolicy::ComboBox));
+ setLayoutItemMargins(QStyle::SE_ComboBoxLayoutItem);
+ q->setModel(new QStandardItemModel(0, 1, q));
+ q->setAttribute(Qt::WA_InputMethodEnabled);
+}
+
+QComboBoxPrivateContainer* QComboBoxPrivate::viewContainer()
+{
+ if (container)
+ return container;
+
+ Q_Q(QComboBox);
+ container = new QComboBoxPrivateContainer(new QComboBoxListView(q), q);
+ container->itemView()->setModel(model);
+ container->itemView()->setTextElideMode(Qt::ElideMiddle);
+ updateDelegate();
+ updateLayoutDirection();
+ QObject::connect(container, SIGNAL(itemSelected(QModelIndex)),
+ q, SLOT(_q_itemSelected(QModelIndex)));
+ QObject::connect(container->itemView()->selectionModel(),
+ SIGNAL(currentChanged(QModelIndex,QModelIndex)),
+ q, SLOT(_q_emitHighlighted(QModelIndex)));
+ QObject::connect(container, SIGNAL(resetButton()), q, SLOT(_q_resetButton()));
+ return container;
+}
+
+
+void QComboBoxPrivate::_q_resetButton()
+{
+ updateArrow(QStyle::State_None);
+}
+
+void QComboBoxPrivate::_q_dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight)
+{
+ Q_Q(QComboBox);
+ if (inserting || topLeft.parent() != root)
+ return;
+
+ if (sizeAdjustPolicy == QComboBox::AdjustToContents) {
+ sizeHint = QSize();
+ adjustComboBoxSize();
+ q->updateGeometry();
+ }
+
+ if (currentIndex.row() >= topLeft.row() && currentIndex.row() <= bottomRight.row()) {
+ if (lineEdit) {
+ lineEdit->setText(q->itemText(currentIndex.row()));
+ updateLineEditGeometry();
+ }
+ q->update();
+ }
+}
+
+void QComboBoxPrivate::_q_rowsAboutToBeInserted(const QModelIndex & parent,
+ int /*start*/, int /*end*/)
+{
+ if (parent != root)
+ return;
+ indexBeforeChange = currentIndex.row();
+}
+
+void QComboBoxPrivate::_q_rowsInserted(const QModelIndex &parent, int start, int end)
+{
+ Q_Q(QComboBox);
+ if (inserting || parent != root)
+ return;
+
+ if (sizeAdjustPolicy == QComboBox::AdjustToContents) {
+ sizeHint = QSize();
+ adjustComboBoxSize();
+ q->updateGeometry();
+ }
+
+ // set current index if combo was previously empty
+ if (start == 0 && (end - start + 1) == q->count() && !currentIndex.isValid()) {
+ q->setCurrentIndex(0);
+ // need to emit changed if model updated index "silently"
+ } else if (currentIndex.row() != indexBeforeChange) {
+ q->update();
+ _q_emitCurrentIndexChanged(currentIndex);
+ }
+}
+
+void QComboBoxPrivate::_q_rowsAboutToBeRemoved(const QModelIndex &parent, int /*start*/, int /*end*/)
+{
+ if (parent != root)
+ return;
+
+ indexBeforeChange = currentIndex.row();
+}
+
+void QComboBoxPrivate::_q_rowsRemoved(const QModelIndex &parent, int /*start*/, int /*end*/)
+{
+ Q_Q(QComboBox);
+ if (parent != root)
+ return;
+
+ if (sizeAdjustPolicy == QComboBox::AdjustToContents) {
+ sizeHint = QSize();
+ adjustComboBoxSize();
+ q->updateGeometry();
+ }
+
+ // model has changed the currentIndex
+ if (currentIndex.row() != indexBeforeChange) {
+ if (!currentIndex.isValid() && q->count()) {
+ q->setCurrentIndex(qMin(q->count() - 1, qMax(indexBeforeChange, 0)));
+ return;
+ }
+ if (lineEdit) {
+ lineEdit->setText(q->itemText(currentIndex.row()));
+ updateLineEditGeometry();
+ }
+ q->update();
+ _q_emitCurrentIndexChanged(currentIndex);
+ }
+}
+
+
+/*!
+ Initialize \a option with the values from this QComboBox. This method
+ is useful for subclasses when they need a QStyleOptionComboBox, but don't want
+ to fill in all the information themselves.
+
+ \sa QStyleOption::initFrom()
+*/
+void QComboBox::initStyleOption(QStyleOptionComboBox *option) const
+{
+ if (!option)
+ return;
+
+ Q_D(const QComboBox);
+ option->initFrom(this);
+ option->editable = isEditable();
+ option->frame = d->frame;
+ if (hasFocus() && !option->editable)
+ option->state |= QStyle::State_Selected;
+ option->subControls = QStyle::SC_All;
+ if (d->arrowState == QStyle::State_Sunken) {
+ option->activeSubControls = QStyle::SC_ComboBoxArrow;
+ option->state |= d->arrowState;
+ } else {
+ option->activeSubControls = d->hoverControl;
+ }
+ if (d->currentIndex.isValid()) {
+ option->currentText = currentText();
+ option->currentIcon = d->itemIcon(d->currentIndex);
+ }
+ option->iconSize = iconSize();
+ if (d->container && d->container->isVisible())
+ option->state |= QStyle::State_On;
+}
+
+void QComboBoxPrivate::updateLineEditGeometry()
+{
+ if (!lineEdit)
+ return;
+
+ Q_Q(QComboBox);
+ QStyleOptionComboBox opt;
+ q->initStyleOption(&opt);
+ QRect editRect = q->style()->subControlRect(QStyle::CC_ComboBox, &opt,
+ QStyle::SC_ComboBoxEditField, q);
+ if (!q->itemIcon(q->currentIndex()).isNull()) {
+ QRect comboRect(editRect);
+ editRect.setWidth(editRect.width() - q->iconSize().width() - 4);
+ editRect = QStyle::alignedRect(q->layoutDirection(), Qt::AlignRight,
+ editRect.size(), comboRect);
+ }
+ lineEdit->setGeometry(editRect);
+}
+
+void QComboBoxPrivate::_q_returnPressed()
+{
+ Q_Q(QComboBox);
+ if (lineEdit && !lineEdit->text().isEmpty()) {
+ if (q->count() >= maxCount && !(this->insertPolicy == QComboBox::InsertAtCurrent))
+ return;
+ lineEdit->deselect();
+ lineEdit->end(false);
+ QString text = lineEdit->text();
+ // check for duplicates (if not enabled) and quit
+ int index = -1;
+ if (!duplicatesEnabled) {
+ // Base how duplicates are determined on the autocompletion case sensitivity
+ Qt::MatchFlags flags = Qt::MatchFixedString;
+#ifndef QT_NO_COMPLETER
+ if (!lineEdit->completer() || lineEdit->completer()->caseSensitivity() == Qt::CaseSensitive)
+#endif
+ flags |= Qt::MatchCaseSensitive;
+ index = q->findText(text, flags);
+ if (index != -1) {
+ q->setCurrentIndex(index);
+ emitActivated(currentIndex);
+ return;
+ }
+ }
+ switch (insertPolicy) {
+ case QComboBox::InsertAtTop:
+ index = 0;
+ break;
+ case QComboBox::InsertAtBottom:
+ index = q->count();
+ break;
+ case QComboBox::InsertAtCurrent:
+ case QComboBox::InsertAfterCurrent:
+ case QComboBox::InsertBeforeCurrent:
+ if (!q->count() || !currentIndex.isValid())
+ index = 0;
+ else if (insertPolicy == QComboBox::InsertAtCurrent)
+ q->setItemText(q->currentIndex(), text);
+ else if (insertPolicy == QComboBox::InsertAfterCurrent)
+ index = q->currentIndex() + 1;
+ else if (insertPolicy == QComboBox::InsertBeforeCurrent)
+ index = q->currentIndex();
+ break;
+ case QComboBox::InsertAlphabetically:
+ index = 0;
+ for (int i=0; i< q->count(); i++, index++ ) {
+ if (text.toLower() < q->itemText(i).toLower())
+ break;
+ }
+ break;
+ case QComboBox::NoInsert:
+ default:
+ break;
+ }
+ if (index >= 0) {
+ q->insertItem(index, text);
+ q->setCurrentIndex(index);
+ emitActivated(currentIndex);
+ }
+ }
+}
+
+void QComboBoxPrivate::_q_itemSelected(const QModelIndex &item)
+{
+ Q_Q(QComboBox);
+ if (item != currentIndex) {
+ setCurrentIndex(item);
+ } else if (lineEdit) {
+ lineEdit->selectAll();
+ lineEdit->setText(q->itemText(currentIndex.row()));
+ }
+ emitActivated(currentIndex);
+}
+
+void QComboBoxPrivate::emitActivated(const QModelIndex &index)
+{
+ Q_Q(QComboBox);
+ if (!index.isValid())
+ return;
+ QString text(itemText(index));
+ emit q->activated(index.row());
+ emit q->activated(text);
+}
+
+void QComboBoxPrivate::_q_emitHighlighted(const QModelIndex &index)
+{
+ Q_Q(QComboBox);
+ if (!index.isValid())
+ return;
+ QString text(itemText(index));
+ emit q->highlighted(index.row());
+ emit q->highlighted(text);
+}
+
+void QComboBoxPrivate::_q_emitCurrentIndexChanged(const QModelIndex &index)
+{
+ Q_Q(QComboBox);
+ emit q->currentIndexChanged(index.row());
+ emit q->currentIndexChanged(itemText(index));
+}
+
+QString QComboBoxPrivate::itemText(const QModelIndex &index) const
+{
+ return index.isValid() ? model->data(index, itemRole()).toString() : QString();
+}
+
+int QComboBoxPrivate::itemRole() const
+{
+ return q_func()->isEditable() ? Qt::EditRole : Qt::DisplayRole;
+}
+
+/*!
+ Destroys the combobox.
+*/
+QComboBox::~QComboBox()
+{
+ // ### check delegateparent and delete delegate if us?
+ Q_D(QComboBox);
+
+ disconnect(d->model, SIGNAL(destroyed()),
+ this, SLOT(_q_modelDestroyed()));
+}
+
+/*!
+ \property QComboBox::maxVisibleItems
+ \brief the maximum allowed size on screen of the combo box, measured in items
+
+ By default, this property has a value of 10.
+
+ \note This property is ignored for non-editable comboboxes in Mac style.
+*/
+int QComboBox::maxVisibleItems() const
+{
+ Q_D(const QComboBox);
+ return d->maxVisibleItems;
+}
+
+void QComboBox::setMaxVisibleItems(int maxItems)
+{
+ Q_D(QComboBox);
+ if (maxItems < 0) {
+ qWarning("QComboBox::setMaxVisibleItems: "
+ "Invalid max visible items (%d) must be >= 0", maxItems);
+ return;
+ }
+ d->maxVisibleItems = maxItems;
+}
+
+/*!
+ \property QComboBox::count
+ \brief the number of items in the combobox
+
+ By default, for an empty combo box, this property has a value of 0.
+*/
+int QComboBox::count() const
+{
+ Q_D(const QComboBox);
+ return d->model->rowCount(d->root);
+}
+
+/*!
+ \property QComboBox::maxCount
+ \brief the maximum number of items allowed in the combobox
+
+ \note If you set the maximum number to be less then the current
+ amount of items in the combobox, the extra items will be
+ truncated. This also applies if you have set an external model on
+ the combobox.
+
+ By default, this property's value is derived from the highest
+ signed integer available (typically 2147483647).
+*/
+void QComboBox::setMaxCount(int max)
+{
+ Q_D(QComboBox);
+ if (max < 0) {
+ qWarning("QComboBox::setMaxCount: Invalid count (%d) must be >= 0", max);
+ return;
+ }
+
+ if (max < count())
+ d->model->removeRows(max, count() - max, d->root);
+
+ d->maxCount = max;
+}
+
+int QComboBox::maxCount() const
+{
+ Q_D(const QComboBox);
+ return d->maxCount;
+}
+
+#ifndef QT_NO_COMPLETER
+
+/*!
+ \property QComboBox::autoCompletion
+ \brief whether the combobox provides auto-completion for editable items
+ \since 4.1
+ \obsolete
+
+ Use setCompleter() instead.
+
+ By default, this property is true.
+
+ \sa editable
+*/
+
+/*!
+ \obsolete
+
+ Use setCompleter() instead.
+*/
+bool QComboBox::autoCompletion() const
+{
+ Q_D(const QComboBox);
+ return d->autoCompletion;
+}
+
+/*!
+ \obsolete
+
+ Use setCompleter() instead.
+*/
+void QComboBox::setAutoCompletion(bool enable)
+{
+ Q_D(QComboBox);
+
+#ifdef QT_KEYPAD_NAVIGATION
+ if (QApplication::keypadNavigationEnabled() && !enable && isEditable())
+ qWarning("QComboBox::setAutoCompletion: auto completion is mandatory when combo box editable");
+#endif
+
+ d->autoCompletion = enable;
+ if (!d->lineEdit)
+ return;
+ if (enable) {
+ if (d->lineEdit->completer())
+ return;
+ d->completer = new QCompleter(d->model, d->lineEdit);
+ d->completer->setCaseSensitivity(d->autoCompletionCaseSensitivity);
+ d->completer->setCompletionMode(QCompleter::InlineCompletion);
+ d->completer->setCompletionColumn(d->modelColumn);
+ d->lineEdit->setCompleter(d->completer);
+ d->completer->setWidget(this);
+ } else {
+ d->lineEdit->setCompleter(0);
+ }
+}
+
+/*!
+ \property QComboBox::autoCompletionCaseSensitivity
+ \brief whether string comparisons are case-sensitive or case-insensitive for auto-completion
+ \obsolete
+
+ By default, this property is Qt::CaseInsensitive.
+
+ Use setCompleter() instead. Case sensitivity of the auto completion can be
+ changed using QCompleter::setCaseSensitivity().
+
+ \sa autoCompletion
+*/
+
+/*!
+ \obsolete
+
+ Use setCompleter() and QCompleter::setCaseSensitivity() instead.
+*/
+Qt::CaseSensitivity QComboBox::autoCompletionCaseSensitivity() const
+{
+ Q_D(const QComboBox);
+ return d->autoCompletionCaseSensitivity;
+}
+
+/*!
+ \obsolete
+
+ Use setCompleter() and QCompleter::setCaseSensitivity() instead.
+*/
+void QComboBox::setAutoCompletionCaseSensitivity(Qt::CaseSensitivity sensitivity)
+{
+ Q_D(QComboBox);
+ d->autoCompletionCaseSensitivity = sensitivity;
+ if (d->lineEdit && d->lineEdit->completer())
+ d->lineEdit->completer()->setCaseSensitivity(sensitivity);
+}
+
+#endif // QT_NO_COMPLETER
+
+/*!
+ \property QComboBox::duplicatesEnabled
+ \brief whether the user can enter duplicate items into the combobox
+
+ Note that it is always possible to programmatically insert duplicate items into the
+ combobox.
+
+ By default, this property is false (duplicates are not allowed).
+*/
+bool QComboBox::duplicatesEnabled() const
+{
+ Q_D(const QComboBox);
+ return d->duplicatesEnabled;
+}
+
+void QComboBox::setDuplicatesEnabled(bool enable)
+{
+ Q_D(QComboBox);
+ d->duplicatesEnabled = enable;
+}
+
+/*! \fn int QComboBox::findText(const QString &text, Qt::MatchFlags flags = Qt::MatchExactly|Qt::MatchCaseSensitive) const
+
+ Returns the index of the item containing the given \a text; otherwise
+ returns -1.
+
+ The \a flags specify how the items in the combobox are searched.
+*/
+
+/*!
+ Returns the index of the item containing the given \a data for the
+ given \a role; otherwise returns -1.
+
+ The \a flags specify how the items in the combobox are searched.
+*/
+int QComboBox::findData(const QVariant &data, int role, Qt::MatchFlags flags) const
+{
+ Q_D(const QComboBox);
+ QModelIndexList result;
+ QModelIndex start = d->model->index(0, d->modelColumn, d->root);
+ result = d->model->match(start, role, data, 1, flags);
+ if (result.isEmpty())
+ return -1;
+ return result.first().row();
+}
+
+/*!
+ \property QComboBox::insertPolicy
+ \brief the policy used to determine where user-inserted items should
+ appear in the combobox
+
+ The default value is \l AtBottom, indicating that new items will appear
+ at the bottom of the list of items.
+
+ \sa InsertPolicy
+*/
+
+QComboBox::InsertPolicy QComboBox::insertPolicy() const
+{
+ Q_D(const QComboBox);
+ return d->insertPolicy;
+}
+
+void QComboBox::setInsertPolicy(InsertPolicy policy)
+{
+ Q_D(QComboBox);
+ d->insertPolicy = policy;
+}
+
+/*!
+ \property QComboBox::sizeAdjustPolicy
+ \brief the policy describing how the size of the combobox changes
+ when the content changes
+
+ The default value is \l AdjustToContentsOnFirstShow.
+
+ \sa SizeAdjustPolicy
+*/
+
+QComboBox::SizeAdjustPolicy QComboBox::sizeAdjustPolicy() const
+{
+ Q_D(const QComboBox);
+ return d->sizeAdjustPolicy;
+}
+
+void QComboBox::setSizeAdjustPolicy(QComboBox::SizeAdjustPolicy policy)
+{
+ Q_D(QComboBox);
+ if (policy == d->sizeAdjustPolicy)
+ return;
+
+ d->sizeAdjustPolicy = policy;
+ d->sizeHint = QSize();
+ d->adjustComboBoxSize();
+ updateGeometry();
+}
+
+/*!
+ \property QComboBox::minimumContentsLength
+ \brief the minimum number of characters that should fit into the combobox.
+
+ The default value is 0.
+
+ If this property is set to a positive value, the
+ minimumSizeHint() and sizeHint() take it into account.
+
+ \sa sizeAdjustPolicy
+*/
+int QComboBox::minimumContentsLength() const
+{
+ Q_D(const QComboBox);
+ return d->minimumContentsLength;
+}
+
+void QComboBox::setMinimumContentsLength(int characters)
+{
+ Q_D(QComboBox);
+ if (characters == d->minimumContentsLength || characters < 0)
+ return;
+
+ d->minimumContentsLength = characters;
+
+ if (d->sizeAdjustPolicy == AdjustToContents
+ || d->sizeAdjustPolicy == AdjustToMinimumContentsLength
+ || d->sizeAdjustPolicy == AdjustToMinimumContentsLengthWithIcon) {
+ d->sizeHint = QSize();
+ d->adjustComboBoxSize();
+ updateGeometry();
+ }
+}
+
+/*!
+ \property QComboBox::iconSize
+ \brief the size of the icons shown in the combobox.
+
+ Unless explicitly set this returns the default value of the
+ current style. This size is the maximum size that icons can have;
+ icons of smaller size are not scaled up.
+*/
+
+QSize QComboBox::iconSize() const
+{
+ Q_D(const QComboBox);
+ if (d->iconSize.isValid())
+ return d->iconSize;
+
+ int iconWidth = style()->pixelMetric(QStyle::PM_SmallIconSize, 0, this);
+ return QSize(iconWidth, iconWidth);
+}
+
+void QComboBox::setIconSize(const QSize &size)
+{
+ Q_D(QComboBox);
+ if (size == d->iconSize)
+ return;
+
+ view()->setIconSize(size);
+ d->iconSize = size;
+ d->sizeHint = QSize();
+ updateGeometry();
+}
+
+/*!
+ \property QComboBox::editable
+ \brief whether the combo box can be edited by the user
+
+ By default, this property is false.
+*/
+bool QComboBox::isEditable() const
+{
+ Q_D(const QComboBox);
+ return d->lineEdit != 0;
+}
+
+void QComboBoxPrivate::updateDelegate()
+{
+ Q_Q(QComboBox);
+ QStyleOptionComboBox opt;
+ q->initStyleOption(&opt);
+ if (q->style()->styleHint(QStyle::SH_ComboBox_Popup, &opt, q))
+ q->setItemDelegate(new QComboMenuDelegate(q->view(), q));
+ else
+ q->setItemDelegate(new QComboBoxDelegate(q->view(), q));
+}
+
+QIcon QComboBoxPrivate::itemIcon(const QModelIndex &index) const
+{
+ QVariant decoration = model->data(index, Qt::DecorationRole);
+ if (decoration.type() == QVariant::Pixmap)
+ return QIcon(qvariant_cast<QPixmap>(decoration));
+ else
+ return qvariant_cast<QIcon>(decoration);
+}
+
+void QComboBox::setEditable(bool editable)
+{
+ Q_D(QComboBox);
+ if (isEditable() == editable)
+ return;
+
+ d->updateDelegate();
+
+ QStyleOptionComboBox opt;
+ initStyleOption(&opt);
+ if (editable) {
+ if (style()->styleHint(QStyle::SH_ComboBox_Popup, &opt, this)) {
+ d->viewContainer()->updateScrollers();
+ view()->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
+ }
+ QLineEdit *le = new QLineEdit(this);
+ setLineEdit(le);
+ } else {
+ if (style()->styleHint(QStyle::SH_ComboBox_Popup, &opt, this)) {
+ d->viewContainer()->updateScrollers();
+ view()->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
+ }
+ setAttribute(Qt::WA_InputMethodEnabled, false);
+ d->lineEdit->hide();
+ d->lineEdit->deleteLater();
+ d->lineEdit = 0;
+ }
+
+ d->viewContainer()->updateTopBottomMargin();
+ if (!testAttribute(Qt::WA_Resized))
+ adjustSize();
+}
+
+/*!
+ Sets the line \a edit to use instead of the current line edit widget.
+
+ The combo box takes ownership of the line edit.
+*/
+void QComboBox::setLineEdit(QLineEdit *edit)
+{
+ Q_D(QComboBox);
+ if (!edit) {
+ qWarning("QComboBox::setLineEdit: cannot set a 0 line edit");
+ return;
+ }
+
+ if (edit == d->lineEdit)
+ return;
+
+ edit->setText(currentText());
+ delete d->lineEdit;
+
+ d->lineEdit = edit;
+ if (d->lineEdit->parent() != this)
+ d->lineEdit->setParent(this);
+ connect(d->lineEdit, SIGNAL(returnPressed()), this, SLOT(_q_returnPressed()));
+ connect(d->lineEdit, SIGNAL(textChanged(QString)), this, SIGNAL(editTextChanged(QString)));
+#ifdef QT3_SUPPORT
+ connect(d->lineEdit, SIGNAL(textChanged(QString)), this, SIGNAL(textChanged(QString)));
+#endif
+ d->lineEdit->setFrame(false);
+ d->lineEdit->setContextMenuPolicy(Qt::NoContextMenu);
+ d->lineEdit->setFocusProxy(this);
+ d->lineEdit->setAttribute(Qt::WA_MacShowFocusRect, false);
+#ifndef QT_NO_COMPLETER
+ setAutoCompletion(d->autoCompletion);
+#endif
+
+#ifdef QT_KEYPAD_NAVIGATION
+#ifndef QT_NO_COMPLETER
+ if (QApplication::keypadNavigationEnabled()) {
+ // Editable combo boxes will have a completer that is set to UnfilteredPopupCompletion.
+ // This means that when the user enters edit mode they are immediately presented with a
+ // list of possible completions.
+ setAutoCompletion(true);
+ if (d->completer) {
+ d->completer->setCompletionMode(QCompleter::UnfilteredPopupCompletion);
+ connect(d->completer, SIGNAL(activated(QModelIndex)), this, SLOT(_q_completerActivated()));
+ }
+ }
+#endif
+#endif
+
+ setAttribute(Qt::WA_InputMethodEnabled);
+ d->updateLayoutDirection();
+ d->updateLineEditGeometry();
+ if (isVisible())
+ d->lineEdit->show();
+
+ update();
+}
+
+/*!
+ Returns the line edit used to edit items in the combobox, or 0 if there
+ is no line edit.
+
+ Only editable combo boxes have a line edit.
+*/
+QLineEdit *QComboBox::lineEdit() const
+{
+ Q_D(const QComboBox);
+ return d->lineEdit;
+}
+
+#ifndef QT_NO_VALIDATOR
+/*!
+ \fn void QComboBox::setValidator(const QValidator *validator)
+
+ Sets the \a validator to use instead of the current validator.
+*/
+
+void QComboBox::setValidator(const QValidator *v)
+{
+ Q_D(QComboBox);
+ if (d->lineEdit)
+ d->lineEdit->setValidator(v);
+}
+
+/*!
+ Returns the validator that is used to constrain text input for the
+ combobox.
+
+ \sa editable
+*/
+const QValidator *QComboBox::validator() const
+{
+ Q_D(const QComboBox);
+ return d->lineEdit ? d->lineEdit->validator() : 0;
+}
+#endif // QT_NO_VALIDATOR
+
+#ifndef QT_NO_COMPLETER
+
+/*!
+ \fn void QComboBox::setCompleter(QCompleter *completer)
+ \since 4.2
+
+ Sets the \a completer to use instead of the current completer.
+ If \a completer is 0, auto completion is disabled.
+
+ By default, for an editable combo box, a QCompleter that
+ performs case insensitive inline completion is automatically created.
+*/
+void QComboBox::setCompleter(QCompleter *c)
+{
+ Q_D(QComboBox);
+ if (!d->lineEdit)
+ return;
+ d->lineEdit->setCompleter(c);
+ if (c)
+ c->setWidget(this);
+}
+
+/*!
+ \since 4.2
+
+ Returns the completer that is used to auto complete text input for the
+ combobox.
+
+ \sa editable
+*/
+QCompleter *QComboBox::completer() const
+{
+ Q_D(const QComboBox);
+ return d->lineEdit ? d->lineEdit->completer() : 0;
+}
+
+#endif // QT_NO_COMPLETER
+
+/*!
+ Returns the item delegate used by the popup list view.
+
+ \sa setItemDelegate()
+*/
+QAbstractItemDelegate *QComboBox::itemDelegate() const
+{
+ return view()->itemDelegate();
+}
+
+/*!
+ Sets the item \a delegate for the popup list view.
+ The combobox takes ownership of the delegate.
+
+ \warning You should not share the same instance of a delegate between comboboxes,
+ widget mappers or views. Doing so can cause incorrect or unintuitive editing behavior
+ since each view connected to a given delegate may receive the
+ \l{QAbstractItemDelegate::}{closeEditor()} signal, and attempt to access, modify or
+ close an editor that has already been closed.
+
+ \sa itemDelegate()
+*/
+void QComboBox::setItemDelegate(QAbstractItemDelegate *delegate)
+{
+ if (!delegate) {
+ qWarning("QComboBox::setItemDelegate: cannot set a 0 delegate");
+ return;
+ }
+ delete view()->itemDelegate();
+ view()->setItemDelegate(delegate);
+}
+
+/*!
+ Returns the model used by the combobox.
+*/
+
+QAbstractItemModel *QComboBox::model() const
+{
+ Q_D(const QComboBox);
+ if (d->model == QAbstractItemModelPrivate::staticEmptyModel()) {
+ QComboBox *that = const_cast<QComboBox*>(this);
+ that->setModel(new QStandardItemModel(0, 1, that));
+ }
+ return d->model;
+}
+
+/*!
+ Sets the model to be \a model. \a model must not be 0.
+ If you want to clear the contents of a model, call clear().
+
+ \sa clear()
+*/
+void QComboBox::setModel(QAbstractItemModel *model)
+{
+ Q_D(QComboBox);
+
+ if (!model) {
+ qWarning("QComboBox::setModel: cannot set a 0 model");
+ return;
+ }
+
+#ifndef QT_NO_COMPLETER
+ if (d->lineEdit && d->lineEdit->completer()
+ && d->lineEdit->completer() == d->completer)
+ d->lineEdit->completer()->setModel(model);
+#endif
+ if (d->model) {
+ disconnect(d->model, SIGNAL(dataChanged(QModelIndex,QModelIndex)),
+ this, SLOT(_q_dataChanged(QModelIndex,QModelIndex)));
+ disconnect(d->model, SIGNAL(rowsAboutToBeInserted(QModelIndex,int,int)),
+ this, SLOT(_q_rowsAboutToBeInserted(QModelIndex,int,int)));
+ disconnect(d->model, SIGNAL(rowsInserted(QModelIndex,int,int)),
+ this, SLOT(_q_rowsInserted(QModelIndex,int,int)));
+ disconnect(d->model, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)),
+ this, SLOT(_q_rowsAboutToBeRemoved(QModelIndex,int,int)));
+ disconnect(d->model, SIGNAL(rowsRemoved(QModelIndex,int,int)),
+ this, SLOT(_q_rowsRemoved(QModelIndex,int,int)));
+ disconnect(d->model, SIGNAL(destroyed()),
+ this, SLOT(_q_modelDestroyed()));
+ disconnect(d->model, SIGNAL(modelReset()),
+ this, SLOT(_q_modelReset()));
+ if (d->model->QObject::parent() == this)
+ delete d->model;
+ }
+
+ d->model = model;
+
+ connect(model, SIGNAL(dataChanged(QModelIndex,QModelIndex)),
+ this, SLOT(_q_dataChanged(QModelIndex,QModelIndex)));
+ connect(model, SIGNAL(rowsAboutToBeInserted(QModelIndex,int,int)),
+ this, SLOT(_q_rowsAboutToBeInserted(QModelIndex,int,int)));
+ connect(model, SIGNAL(rowsInserted(QModelIndex,int,int)),
+ this, SLOT(_q_rowsInserted(QModelIndex,int,int)));
+ connect(model, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)),
+ this, SLOT(_q_rowsAboutToBeRemoved(QModelIndex,int,int)));
+ connect(model, SIGNAL(rowsRemoved(QModelIndex,int,int)),
+ this, SLOT(_q_rowsRemoved(QModelIndex,int,int)));
+ connect(model, SIGNAL(destroyed()),
+ this, SLOT(_q_modelDestroyed()));
+ connect(model, SIGNAL(modelReset()),
+ this, SLOT(_q_modelReset()));
+
+ if (d->container)
+ d->container->itemView()->setModel(model);
+
+ bool currentReset = false;
+
+ if (count()) {
+ for (int pos=0; pos < count(); pos++) {
+ if (d->model->index(pos, d->modelColumn, d->root).flags() & Qt::ItemIsEnabled) {
+ setCurrentIndex(pos);
+ currentReset = true;
+ break;
+ }
+ }
+ }
+
+ if (!currentReset)
+ setCurrentIndex(-1);
+
+ d->modelChanged();
+}
+
+/*!
+ Returns the root model item index for the items in the combobox.
+
+ \sa setRootModelIndex()
+*/
+
+QModelIndex QComboBox::rootModelIndex() const
+{
+ Q_D(const QComboBox);
+ return QModelIndex(d->root);
+}
+
+/*!
+ Sets the root model item \a index for the items in the combobox.
+
+ \sa rootModelIndex()
+*/
+void QComboBox::setRootModelIndex(const QModelIndex &index)
+{
+ Q_D(QComboBox);
+ d->root = QPersistentModelIndex(index);
+ view()->setRootIndex(index);
+ update();
+}
+
+/*!
+ \property QComboBox::currentIndex
+ \brief the index of the current item in the combobox.
+
+ The current index can change when inserting or removing items.
+
+ By default, for an empty combo box or a combo box in which no current
+ item is set, this property has a value of -1.
+*/
+int QComboBox::currentIndex() const
+{
+ Q_D(const QComboBox);
+ return d->currentIndex.row();
+}
+
+void QComboBox::setCurrentIndex(int index)
+{
+ Q_D(QComboBox);
+ QModelIndex mi = d->model->index(index, d->modelColumn, d->root);
+ d->setCurrentIndex(mi);
+}
+
+void QComboBoxPrivate::setCurrentIndex(const QModelIndex &mi)
+{
+ Q_Q(QComboBox);
+ bool indexChanged = (mi != currentIndex);
+ if (indexChanged)
+ currentIndex = QPersistentModelIndex(mi);
+ if (lineEdit) {
+ QString newText = q->itemText(currentIndex.row());
+ if (lineEdit->text() != newText)
+ lineEdit->setText(q->itemText(currentIndex.row()));
+ updateLineEditGeometry();
+ }
+ if (indexChanged) {
+ q->update();
+ _q_emitCurrentIndexChanged(currentIndex);
+ }
+}
+
+/*!
+ \property QComboBox::currentText
+ \brief the text of the current item
+
+ By default, for an empty combo box or a combo box in which no current
+ item is set, this property contains an empty string.
+*/
+QString QComboBox::currentText() const
+{
+ Q_D(const QComboBox);
+ if (d->lineEdit)
+ return d->lineEdit->text();
+ else if (d->currentIndex.isValid())
+ return d->itemText(d->currentIndex);
+ else
+ return QString();
+}
+
+/*!
+ Returns the text for the given \a index in the combobox.
+*/
+QString QComboBox::itemText(int index) const
+{
+ Q_D(const QComboBox);
+ QModelIndex mi = d->model->index(index, d->modelColumn, d->root);
+ return d->itemText(mi);
+}
+
+/*!
+ Returns the icon for the given \a index in the combobox.
+*/
+QIcon QComboBox::itemIcon(int index) const
+{
+ Q_D(const QComboBox);
+ QModelIndex mi = d->model->index(index, d->modelColumn, d->root);
+ return d->itemIcon(mi);
+}
+
+/*!
+ Returns the data for the given \a role in the given \a index in the
+ combobox, or QVariant::Invalid if there is no data for this role.
+*/
+QVariant QComboBox::itemData(int index, int role) const
+{
+ Q_D(const QComboBox);
+ QModelIndex mi = d->model->index(index, d->modelColumn, d->root);
+ return d->model->data(mi, role);
+}
+
+/*!
+ \fn void QComboBox::insertItem(int index, const QString &text, const QVariant &userData)
+
+ Inserts the \a text and \a userData (stored in the Qt::UserRole)
+ into the combobox at the given \a index.
+
+ If the index is equal to or higher than the total number of items,
+ the new item is appended to the list of existing items. If the
+ index is zero or negative, the new item is prepended to the list
+ of existing items.
+
+ \sa insertItems()
+*/
+
+/*!
+
+ Inserts the \a icon, \a text and \a userData (stored in the
+ Qt::UserRole) into the combobox at the given \a index.
+
+ If the index is equal to or higher than the total number of items,
+ the new item is appended to the list of existing items. If the
+ index is zero or negative, the new item is prepended to the list
+ of existing items.
+
+ \sa insertItems()
+*/
+void QComboBox::insertItem(int index, const QIcon &icon, const QString &text, const QVariant &userData)
+{
+ Q_D(QComboBox);
+ int itemCount = count();
+ index = qBound(0, index, itemCount);
+ if (index >= d->maxCount)
+ return;
+
+ // For the common case where we are using the built in QStandardItemModel
+ // construct a QStandardItem, reducing the number of expensive signals from the model
+ if (QStandardItemModel *m = qobject_cast<QStandardItemModel*>(d->model)) {
+ QStandardItem *item = new QStandardItem(text);
+ if (!icon.isNull()) item->setData(icon, Qt::DecorationRole);
+ if (userData.isValid()) item->setData(userData, Qt::UserRole);
+ m->insertRow(index, item);
+ ++itemCount;
+ } else {
+ d->inserting = true;
+ if (d->model->insertRows(index, 1, d->root)) {
+ QModelIndex item = d->model->index(index, d->modelColumn, d->root);
+ if (icon.isNull() && !userData.isValid()) {
+ d->model->setData(item, text, Qt::EditRole);
+ } else {
+ QMap<int, QVariant> values;
+ if (!text.isNull()) values.insert(Qt::EditRole, text);
+ if (!icon.isNull()) values.insert(Qt::DecorationRole, icon);
+ if (userData.isValid()) values.insert(Qt::UserRole, userData);
+ if (!values.isEmpty()) d->model->setItemData(item, values);
+ }
+ d->inserting = false;
+ d->_q_rowsInserted(d->root, index, index);
+ ++itemCount;
+ } else {
+ d->inserting = false;
+ }
+ }
+
+ if (itemCount > d->maxCount)
+ d->model->removeRows(itemCount - 1, itemCount - d->maxCount, d->root);
+}
+
+/*!
+ Inserts the strings from the \a list into the combobox as separate items,
+ starting at the \a index specified.
+
+ If the index is equal to or higher than the total number of items, the new items
+ are appended to the list of existing items. If the index is zero or negative, the
+ new items are prepended to the list of existing items.
+
+ \sa insertItem()
+ */
+void QComboBox::insertItems(int index, const QStringList &list)
+{
+ Q_D(QComboBox);
+ if (list.isEmpty())
+ return;
+ index = qBound(0, index, count());
+ int insertCount = qMin(d->maxCount - index, list.count());
+ if (insertCount <= 0)
+ return;
+ // For the common case where we are using the built in QStandardItemModel
+ // construct a QStandardItem, reducing the number of expensive signals from the model
+ if (QStandardItemModel *m = qobject_cast<QStandardItemModel*>(d->model)) {
+ QList<QStandardItem *> items;
+ QStandardItem *hiddenRoot = m->invisibleRootItem();
+ for (int i = 0; i < insertCount; ++i)
+ items.append(new QStandardItem(list.at(i)));
+ hiddenRoot->insertRows(index, items);
+ } else {
+ d->inserting = true;
+ if (d->model->insertRows(index, insertCount, d->root)) {
+ QModelIndex item;
+ for (int i = 0; i < insertCount; ++i) {
+ item = d->model->index(i+index, d->modelColumn, d->root);
+ d->model->setData(item, list.at(i), Qt::EditRole);
+ }
+ d->inserting = false;
+ d->_q_rowsInserted(d->root, index, index + insertCount - 1);
+ } else {
+ d->inserting = false;
+ }
+ }
+
+ int mc = count();
+ if (mc > d->maxCount)
+ d->model->removeRows(d->maxCount, mc - d->maxCount, d->root);
+}
+
+/*!
+ \since 4.4
+
+ Inserts a separator item into the combobox at the given \a index.
+
+ If the index is equal to or higher than the total number of items, the new item
+ is appended to the list of existing items. If the index is zero or negative, the
+ new item is prepended to the list of existing items.
+
+ \sa insertItem()
+*/
+void QComboBox::insertSeparator(int index)
+{
+ Q_D(QComboBox);
+ int itemCount = count();
+ index = qBound(0, index, itemCount);
+ if (index >= d->maxCount)
+ return;
+ insertItem(index, QIcon(), QString());
+ QComboBoxDelegate::setSeparator(d->model, d->model->index(index, 0, d->root));
+}
+
+/*!
+ Removes the item at the given \a index from the combobox.
+ This will update the current index if the index is removed.
+*/
+void QComboBox::removeItem(int index)
+{
+ Q_ASSERT(index >= 0 && index < count());
+ Q_D(QComboBox);
+ d->model->removeRows(index, 1, d->root);
+}
+
+/*!
+ Sets the \a text for the item on the given \a index in the combobox.
+*/
+void QComboBox::setItemText(int index, const QString &text)
+{
+ Q_D(const QComboBox);
+ QModelIndex item = d->model->index(index, d->modelColumn, d->root);
+ if (item.isValid()) {
+ d->model->setData(item, text, Qt::EditRole);
+ }
+}
+
+/*!
+ Sets the \a icon for the item on the given \a index in the combobox.
+*/
+void QComboBox::setItemIcon(int index, const QIcon &icon)
+{
+ Q_D(const QComboBox);
+ QModelIndex item = d->model->index(index, d->modelColumn, d->root);
+ if (item.isValid()) {
+ d->model->setData(item, icon, Qt::DecorationRole);
+ }
+}
+
+/*!
+ Sets the data \a role for the item on the given \a index in the combobox
+ to the specified \a value.
+*/
+void QComboBox::setItemData(int index, const QVariant &value, int role)
+{
+ Q_D(const QComboBox);
+ QModelIndex item = d->model->index(index, d->modelColumn, d->root);
+ if (item.isValid()) {
+ d->model->setData(item, value, role);
+ }
+}
+
+/*!
+ Returns the list view used for the combobox popup.
+*/
+QAbstractItemView *QComboBox::view() const
+{
+ Q_D(const QComboBox);
+ return const_cast<QComboBoxPrivate*>(d)->viewContainer()->itemView();
+}
+
+/*!
+ Sets the view to be used in the combobox popup to the given \a
+ itemView. The combobox takes ownership of the view.
+
+ Note: If you want to use the convenience views (like QListWidget,
+ QTableWidget or QTreeWidget), make sure to call setModel() on the
+ combobox with the convenience widgets model before calling this
+ function.
+*/
+void QComboBox::setView(QAbstractItemView *itemView)
+{
+ Q_D(QComboBox);
+ if (!itemView) {
+ qWarning("QComboBox::setView: cannot set a 0 view");
+ return;
+ }
+
+ if (itemView->model() != d->model)
+ itemView->setModel(d->model);
+ d->viewContainer()->setItemView(itemView);
+}
+
+/*!
+ \reimp
+*/
+QSize QComboBox::minimumSizeHint() const
+{
+ Q_D(const QComboBox);
+ return d->recomputeSizeHint(d->minimumSizeHint);
+}
+
+/*!
+ \reimp
+
+ This implementation caches the size hint to avoid resizing when
+ the contents change dynamically. To invalidate the cached value
+ change the \l sizeAdjustPolicy.
+*/
+QSize QComboBox::sizeHint() const
+{
+ Q_D(const QComboBox);
+ return d->recomputeSizeHint(d->sizeHint);
+}
+
+/*!
+ Displays the list of items in the combobox. If the list is empty
+ then the no items will be shown.
+
+ If you reimplement this function to show a custom pop-up, make
+ sure you call hidePopup() to reset the internal state.
+
+ \sa hidePopup()
+*/
+void QComboBox::showPopup()
+{
+ Q_D(QComboBox);
+ if (count() <= 0)
+ return;
+
+#ifdef QT_KEYPAD_NAVIGATION
+#ifndef QT_NO_COMPLETER
+ if (QApplication::keypadNavigationEnabled() && d->completer) {
+ // editable combo box is line edit plus completer
+ setEditFocus(true);
+ d->completer->complete(); // show popup
+ return;
+ }
+#endif
+#endif
+
+ QStyle * const style = this->style();
+
+ // set current item and select it
+ view()->selectionModel()->setCurrentIndex(d->currentIndex,
+ QItemSelectionModel::ClearAndSelect);
+ QComboBoxPrivateContainer* container = d->viewContainer();
+ QStyleOptionComboBox opt;
+ initStyleOption(&opt);
+ QRect listRect(style->subControlRect(QStyle::CC_ComboBox, &opt,
+ QStyle::SC_ComboBoxListBoxPopup, this));
+ QRect screen = d->popupGeometry(QApplication::desktop()->screenNumber(this));
+ QPoint below = mapToGlobal(listRect.bottomLeft());
+ int belowHeight = screen.bottom() - below.y();
+ QPoint above = mapToGlobal(listRect.topLeft());
+ int aboveHeight = above.y() - screen.y();
+ bool boundToScreen = !window()->testAttribute(Qt::WA_DontShowOnScreen);
+
+ const bool usePopup = style->styleHint(QStyle::SH_ComboBox_Popup, &opt, this);
+
+ {
+ int listHeight = 0;
+ int count = 0;
+ QStack<QModelIndex> toCheck;
+ toCheck.push(view()->rootIndex());
+#ifndef QT_NO_TREEVIEW
+ QTreeView *treeView = qobject_cast<QTreeView*>(view());
+ if (treeView && treeView->header() && !treeView->header()->isHidden())
+ listHeight += treeView->header()->height();
+#endif
+ while (!toCheck.isEmpty()) {
+ QModelIndex parent = toCheck.pop();
+ for (int i = 0; i < d->model->rowCount(parent); ++i) {
+ QModelIndex idx = d->model->index(i, d->modelColumn, parent);
+ if (!idx.isValid())
+ continue;
+ listHeight += view()->visualRect(idx).height() + container->spacing();
+#ifndef QT_NO_TREEVIEW
+ if (d->model->hasChildren(idx) && treeView && treeView->isExpanded(idx))
+ toCheck.push(idx);
+#endif
+ ++count;
+ if (!usePopup && count > d->maxVisibleItems) {
+ toCheck.clear();
+ break;
+ }
+ }
+ }
+ listRect.setHeight(listHeight);
+ }
+
+ // add the frame size to the height. (+the spacing for the top and the bottom item)
+ int marginTop, marginBottom;
+ view()->getContentsMargins(0, &marginTop, 0, &marginBottom);
+ listRect.setHeight(listRect.height() + 2*container->spacing()
+ + marginTop + marginBottom);
+
+ // Add space for margin at top and bottom if the style wants it.
+ if (usePopup)
+ listRect.setHeight(listRect.height() + style->pixelMetric(QStyle::PM_MenuVMargin, &opt, this) * 2);
+
+ // Make sure the popup is wide enough to display its contents.
+ if (usePopup) {
+ const int diff = d->computeWidthHint() - width();
+ if (diff > 0)
+ listRect.setWidth(listRect.width() + diff);
+ }
+
+ //we need to activate the layout to make sure the min/maximum size are set when the widget was not yet show
+ container->layout()->activate();
+ //takes account of the minimum/maximum size of the container
+ listRect.setSize( listRect.size().expandedTo(container->minimumSize())
+ .boundedTo(container->maximumSize()));
+
+ // make sure the widget fits on screen
+ if (boundToScreen) {
+ if (listRect.width() > screen.width() )
+ listRect.setWidth(screen.width());
+ if (mapToGlobal(listRect.bottomRight()).x() > screen.right()) {
+ below.setX(screen.x() + screen.width() - listRect.width());
+ above.setX(screen.x() + screen.width() - listRect.width());
+ }
+ if (mapToGlobal(listRect.topLeft()).x() < screen.x() ) {
+ below.setX(screen.x());
+ above.setX(screen.x());
+ }
+ }
+
+ if (usePopup) {
+ // Position horizontally.
+ listRect.moveLeft(above.x());
+
+ // Position vertically so the curently selected item lines up
+ // with the combo box.
+ const QRect currentItemRect = view()->visualRect(view()->currentIndex());
+ const int offset = listRect.top() - currentItemRect.top();
+ listRect.moveTop(above.y() + offset - listRect.top());
+
+
+ // Clamp the listRect height and vertical position so we don't expand outside the
+ // available screen geometry.This may override the vertical position, but it is more
+ // important to show as much as possible of the popup.
+ const int height = !boundToScreen ? listRect.height() : qMin(listRect.height(), screen.height());
+ listRect.setHeight(height);
+ if (boundToScreen) {
+ if (listRect.top() < screen.top())
+ listRect.moveTop(screen.top());
+ if (listRect.bottom() > screen.bottom())
+ listRect.moveBottom(screen.bottom());
+ }
+ } else if (!boundToScreen || listRect.height() <= belowHeight) {
+ listRect.moveTopLeft(below);
+ } else if (listRect.height() <= aboveHeight) {
+ listRect.moveBottomLeft(above);
+ } else if (belowHeight >= aboveHeight) {
+ listRect.setHeight(belowHeight);
+ listRect.moveTopLeft(below);
+ } else {
+ listRect.setHeight(aboveHeight);
+ listRect.moveBottomLeft(above);
+ }
+
+#ifndef QT_NO_IM
+ if (QInputContext *qic = inputContext())
+ qic->reset();
+#endif
+ QScrollBar *sb = view()->horizontalScrollBar();
+ Qt::ScrollBarPolicy policy = view()->horizontalScrollBarPolicy();
+ bool needHorizontalScrollBar = (policy == Qt::ScrollBarAsNeeded || policy == Qt::ScrollBarAlwaysOn)
+ && sb->minimum() < sb->maximum();
+ if (needHorizontalScrollBar) {
+ listRect.adjust(0, 0, 0, sb->height());
+ }
+ container->setGeometry(listRect);
+
+ bool updatesEnabled = container->updatesEnabled();
+#if defined(Q_WS_WIN) && !defined(QT_NO_EFFECTS)
+ bool scrollDown = (listRect.topLeft() == below);
+ if (QApplication::isEffectEnabled(Qt::UI_AnimateCombo)
+ && !style->styleHint(QStyle::SH_ComboBox_Popup, &opt, this) && !window()->testAttribute(Qt::WA_DontShowOnScreen))
+ qScrollEffect(container, scrollDown ? QEffects::DownScroll : QEffects::UpScroll, 150);
+#endif
+ container->setUpdatesEnabled(false);
+ container->raise();
+ container->show();
+ container->updateScrollers();
+ view()->setFocus();
+
+ view()->scrollTo(view()->currentIndex(),
+ style->styleHint(QStyle::SH_ComboBox_Popup, &opt, this)
+ ? QAbstractItemView::PositionAtCenter
+ : QAbstractItemView::EnsureVisible);
+
+ container->setUpdatesEnabled(updatesEnabled);
+ container->update();
+}
+
+/*!
+ Hides the list of items in the combobox if it is currently visible
+ and resets the internal state, so that if the custom pop-up was
+ shown inside the reimplemented showPopup(), then you also need to
+ reimplement the hidePopup() function to hide your custom pop-up
+ and call the base class implementation to reset the internal state
+ whenever your custom pop-up widget is hidden.
+
+ \sa showPopup()
+*/
+void QComboBox::hidePopup()
+{
+ Q_D(QComboBox);
+ if (d->container && d->container->isVisible()) {
+#if !defined(QT_NO_EFFECTS)
+ d->model->blockSignals(true);
+ d->container->itemView()->blockSignals(true);
+ d->container->blockSignals(true);
+ // Flash selected/triggered item (if any).
+ if (style()->styleHint(QStyle::SH_Menu_FlashTriggeredItem)) {
+ QItemSelectionModel *selectionModel = view() ? view()->selectionModel() : 0;
+ if (selectionModel && selectionModel->hasSelection()) {
+ QEventLoop eventLoop;
+ const QItemSelection selection = selectionModel->selection();
+
+ // Deselect item and wait 60 ms.
+ selectionModel->select(selection, QItemSelectionModel::Toggle);
+ QTimer::singleShot(60, &eventLoop, SLOT(quit()));
+ eventLoop.exec();
+
+ // Select item and wait 20 ms.
+ selectionModel->select(selection, QItemSelectionModel::Toggle);
+ QTimer::singleShot(20, &eventLoop, SLOT(quit()));
+ eventLoop.exec();
+ }
+ }
+
+ // Fade out.
+ bool needFade = style()->styleHint(QStyle::SH_Menu_FadeOutOnHide);
+ if (needFade) {
+#if defined(Q_WS_MAC)
+ macWindowFade(qt_mac_window_for(d->container));
+#endif // Q_WS_MAC
+ // Other platform implementations welcome :-)
+ }
+ d->model->blockSignals(false);
+ d->container->itemView()->blockSignals(false);
+ d->container->blockSignals(false);
+
+ if (!needFade)
+#endif // QT_NO_EFFECTS
+ // Fade should implicitly hide as well ;-)
+ d->container->hide();
+ }
+#ifdef QT_KEYPAD_NAVIGATION
+ if (QApplication::keypadNavigationEnabled() && isEditable() && hasFocus())
+ setEditFocus(true);
+#endif
+ d->_q_resetButton();
+}
+
+/*!
+ Clears the combobox, removing all items.
+
+ Note: If you have set an external model on the combobox this model
+ will still be cleared when calling this function.
+*/
+void QComboBox::clear()
+{
+ Q_D(QComboBox);
+ d->model->removeRows(0, d->model->rowCount(d->root), d->root);
+}
+
+/*!
+ \fn void QComboBox::clearValidator()
+
+ Use setValidator(0) instead.
+*/
+
+/*!
+ Clears the contents of the line edit used for editing in the combobox.
+*/
+void QComboBox::clearEditText()
+{
+ Q_D(QComboBox);
+ if (d->lineEdit)
+ d->lineEdit->clear();
+}
+
+/*!
+ Sets the \a text in the combobox's text edit.
+*/
+void QComboBox::setEditText(const QString &text)
+{
+ Q_D(QComboBox);
+ if (d->lineEdit)
+ d->lineEdit->setText(text);
+}
+
+/*!
+ \reimp
+*/
+void QComboBox::focusInEvent(QFocusEvent *e)
+{
+ Q_D(QComboBox);
+ update();
+ if (d->lineEdit) {
+ d->lineEdit->event(e);
+#ifndef QT_NO_COMPLETER
+ if (d->lineEdit->completer())
+ d->lineEdit->completer()->setWidget(this);
+#endif
+ }
+}
+
+/*!
+ \reimp
+*/
+void QComboBox::focusOutEvent(QFocusEvent *e)
+{
+ Q_D(QComboBox);
+ update();
+ if (d->lineEdit)
+ d->lineEdit->event(e);
+}
+
+/*! \reimp */
+void QComboBox::changeEvent(QEvent *e)
+{
+ Q_D(QComboBox);
+ switch (e->type()) {
+ case QEvent::StyleChange:
+ d->updateDelegate();
+#ifdef Q_WS_MAC
+ case QEvent::MacSizeChange:
+#endif
+ d->sizeHint = QSize(); // invalidate size hint
+ d->minimumSizeHint = QSize();
+ d->updateLayoutDirection();
+ if (d->lineEdit)
+ d->updateLineEditGeometry();
+ d->setLayoutItemMargins(QStyle::SE_ComboBoxLayoutItem);
+ // ### need to update scrollers etc. as well here
+ break;
+ case QEvent::EnabledChange:
+ if (!isEnabled())
+ hidePopup();
+ break;
+ case QEvent::PaletteChange: {
+ QStyleOptionComboBox opt;
+ initStyleOption(&opt);
+#ifndef QT_NO_MENU
+ if (style()->styleHint(QStyle::SH_ComboBox_Popup, &opt, this)) {
+ QMenu menu;
+ menu.ensurePolished();
+ d->viewContainer()->setPalette(menu.palette());
+ d->viewContainer()->setWindowOpacity(menu.windowOpacity());
+ } else
+#endif
+ {
+ d->viewContainer()->setPalette(palette());
+ d->viewContainer()->setWindowOpacity(1.0);
+ }
+ break;
+ }
+ case QEvent::FontChange:
+ d->sizeHint = QSize(); // invalidate size hint
+ d->viewContainer()->setFont(font());
+ if (d->lineEdit)
+ d->updateLineEditGeometry();
+ break;
+ default:
+ break;
+ }
+ QWidget::changeEvent(e);
+}
+
+/*!
+ \reimp
+*/
+void QComboBox::resizeEvent(QResizeEvent *)
+{
+ Q_D(QComboBox);
+ d->updateLineEditGeometry();
+}
+
+/*!
+ \reimp
+*/
+void QComboBox::paintEvent(QPaintEvent *)
+{
+ QStylePainter painter(this);
+ painter.setPen(palette().color(QPalette::Text));
+
+ // draw the combobox frame, focusrect and selected etc.
+ QStyleOptionComboBox opt;
+ initStyleOption(&opt);
+ painter.drawComplexControl(QStyle::CC_ComboBox, opt);
+
+ // draw the icon and text
+ painter.drawControl(QStyle::CE_ComboBoxLabel, opt);
+}
+
+/*!
+ \reimp
+*/
+void QComboBox::showEvent(QShowEvent *e)
+{
+ Q_D(QComboBox);
+ if (!d->shownOnce && d->sizeAdjustPolicy == QComboBox::AdjustToContentsOnFirstShow) {
+ d->sizeHint = QSize();
+ updateGeometry();
+ }
+ d->shownOnce = true;
+ QWidget::showEvent(e);
+}
+
+/*!
+ \reimp
+*/
+void QComboBox::hideEvent(QHideEvent *)
+{
+ hidePopup();
+}
+
+/*!
+ \reimp
+*/
+bool QComboBox::event(QEvent *event)
+{
+ Q_D(QComboBox);
+ switch(event->type()) {
+ case QEvent::LayoutDirectionChange:
+ case QEvent::ApplicationLayoutDirectionChange:
+ d->updateLayoutDirection();
+ d->updateLineEditGeometry();
+ break;
+ case QEvent::HoverEnter:
+ case QEvent::HoverLeave:
+ case QEvent::HoverMove:
+ if (const QHoverEvent *he = static_cast<const QHoverEvent *>(event))
+ d->updateHoverControl(he->pos());
+ break;
+ case QEvent::ShortcutOverride:
+ if (d->lineEdit)
+ return d->lineEdit->event(event);
+ break;
+#ifdef QT_KEYPAD_NAVIGATION
+ case QEvent::EnterEditFocus:
+ if (!d->lineEdit)
+ setEditFocus(false); // We never want edit focus if we are not editable
+ else
+ d->lineEdit->event(event); //so cursor starts
+ break;
+ case QEvent::LeaveEditFocus:
+ if (d->lineEdit)
+ d->lineEdit->event(event); //so cursor stops
+ break;
+#endif
+ default:
+ break;
+ }
+ return QWidget::event(event);
+}
+
+/*!
+ \reimp
+*/
+void QComboBox::mousePressEvent(QMouseEvent *e)
+{
+ Q_D(QComboBox);
+ QStyleOptionComboBox opt;
+ initStyleOption(&opt);
+ QStyle::SubControl sc = style()->hitTestComplexControl(QStyle::CC_ComboBox, &opt, e->pos(),
+ this);
+ if (e->button() == Qt::LeftButton && (sc == QStyle::SC_ComboBoxArrow || !isEditable())
+ && !d->viewContainer()->isVisible()) {
+ if (sc == QStyle::SC_ComboBoxArrow)
+ d->updateArrow(QStyle::State_Sunken);
+#ifdef QT_KEYPAD_NAVIGATION
+ if (!d->lineEdit) {
+#endif
+ // We've restricted the next couple of lines, because by not calling
+ // viewContainer(), we avoid creating the QComboBoxPrivateContainer.
+ d->viewContainer()->blockMouseReleaseTimer.start(QApplication::doubleClickInterval());
+ d->viewContainer()->initialClickPosition = mapToGlobal(e->pos());
+#ifdef QT_KEYPAD_NAVIGATION
+ }
+#endif
+ showPopup();
+ } else {
+#ifdef QT_KEYPAD_NAVIGATION
+ if (QApplication::keypadNavigationEnabled() && sc == QStyle::SC_ComboBoxEditField && d->lineEdit) {
+ d->lineEdit->event(e); //so lineedit can move cursor, etc
+ return;
+ }
+#endif
+ QWidget::mousePressEvent(e);
+ }
+}
+
+/*!
+ \reimp
+*/
+void QComboBox::mouseReleaseEvent(QMouseEvent *e)
+{
+ Q_D(QComboBox);
+ Q_UNUSED(e);
+ d->updateArrow(QStyle::State_None);
+}
+
+/*!
+ \reimp
+*/
+void QComboBox::keyPressEvent(QKeyEvent *e)
+{
+ Q_D(QComboBox);
+
+#ifndef QT_NO_COMPLETER
+ if (d->lineEdit
+ && d->lineEdit->completer()
+ && d->lineEdit->completer()->popup()
+ && d->lineEdit->completer()->popup()->isVisible()) {
+ // provide same autocompletion support as line edit
+ d->lineEdit->event(e);
+ return;
+ }
+#endif
+
+ enum Move { NoMove=0 , MoveUp , MoveDown , MoveFirst , MoveLast};
+
+ Move move = NoMove;
+ int newIndex = currentIndex();
+ switch (e->key()) {
+ case Qt::Key_Up:
+ if (e->modifiers() & Qt::ControlModifier)
+ break; // pass to line edit for auto completion
+ case Qt::Key_PageUp:
+#ifdef QT_KEYPAD_NAVIGATION
+ if (QApplication::keypadNavigationEnabled())
+ e->ignore();
+ else
+#endif
+ move = MoveUp;
+ break;
+ case Qt::Key_Down:
+ if (e->modifiers() & Qt::AltModifier) {
+ showPopup();
+ return;
+ } else if (e->modifiers() & Qt::ControlModifier)
+ break; // pass to line edit for auto completion
+ // fall through
+ case Qt::Key_PageDown:
+#ifdef QT_KEYPAD_NAVIGATION
+ if (QApplication::keypadNavigationEnabled())
+ e->ignore();
+ else
+#endif
+ move = MoveDown;
+ break;
+ case Qt::Key_Home:
+ if (!d->lineEdit)
+ move = MoveFirst;
+ break;
+ case Qt::Key_End:
+ if (!d->lineEdit)
+ move = MoveLast;
+ break;
+ case Qt::Key_F4:
+ if (!e->modifiers()) {
+ showPopup();
+ return;
+ }
+ break;
+ case Qt::Key_Space:
+ if (!d->lineEdit) {
+ showPopup();
+ return;
+ }
+ case Qt::Key_Enter:
+ case Qt::Key_Return:
+ case Qt::Key_Escape:
+ if (!d->lineEdit)
+ e->ignore();
+ break;
+#ifdef QT_KEYPAD_NAVIGATION
+ case Qt::Key_Select:
+ if (QApplication::keypadNavigationEnabled()
+ && (!hasEditFocus() || !d->lineEdit)) {
+ showPopup();
+ return;
+ }
+ break;
+ case Qt::Key_Left:
+ case Qt::Key_Right:
+ if (QApplication::keypadNavigationEnabled() && !hasEditFocus())
+ e->ignore();
+ break;
+ case Qt::Key_Back:
+ if (QApplication::keypadNavigationEnabled()) {
+ if (!hasEditFocus() || !d->lineEdit)
+ e->ignore();
+ } else {
+ e->ignore(); // let the surounding dialog have it
+ }
+ break;
+#endif
+ default:
+ if (!d->lineEdit) {
+ if (!e->text().isEmpty())
+ d->keyboardSearchString(e->text());
+ else
+ e->ignore();
+ }
+ }
+
+ if (move != NoMove) {
+ e->accept();
+ switch (move) {
+ case MoveFirst:
+ newIndex = -1;
+ case MoveDown:
+ newIndex++;
+ while ((newIndex < count()) && !(d->model->flags(d->model->index(newIndex,d->modelColumn,d->root)) & Qt::ItemIsEnabled))
+ newIndex++;
+ break;
+ case MoveLast:
+ newIndex = count();
+ case MoveUp:
+ newIndex--;
+ while ((newIndex >= 0) && !(d->model->flags(d->model->index(newIndex,d->modelColumn,d->root)) & Qt::ItemIsEnabled))
+ newIndex--;
+ break;
+ default:
+ e->ignore();
+ break;
+ }
+
+ if (newIndex >= 0 && newIndex < count() && newIndex != currentIndex()) {
+ setCurrentIndex(newIndex);
+ d->emitActivated(d->currentIndex);
+ }
+ } else if (d->lineEdit) {
+ d->lineEdit->event(e);
+ }
+}
+
+
+/*!
+ \reimp
+*/
+void QComboBox::keyReleaseEvent(QKeyEvent *e)
+{
+ Q_D(QComboBox);
+ if (d->lineEdit)
+ d->lineEdit->event(e);
+}
+
+/*!
+ \reimp
+*/
+#ifndef QT_NO_WHEELEVENT
+void QComboBox::wheelEvent(QWheelEvent *e)
+{
+ Q_D(QComboBox);
+ if (!d->viewContainer()->isVisible()) {
+ int newIndex = currentIndex();
+
+ if (e->delta() > 0) {
+ newIndex--;
+ while ((newIndex >= 0) && !(d->model->flags(d->model->index(newIndex,d->modelColumn,d->root)) & Qt::ItemIsEnabled))
+ newIndex--;
+ } else {
+ newIndex++;
+ while ((newIndex < count()) && !(d->model->flags(d->model->index(newIndex,d->modelColumn,d->root)) & Qt::ItemIsEnabled))
+ newIndex++;
+ }
+
+ if (newIndex >= 0 && newIndex < count() && newIndex != currentIndex()) {
+ setCurrentIndex(newIndex);
+ d->emitActivated(d->currentIndex);
+ }
+ e->accept();
+ }
+}
+#endif
+
+#ifndef QT_NO_CONTEXTMENU
+/*!
+ \reimp
+*/
+void QComboBox::contextMenuEvent(QContextMenuEvent *e)
+{
+ Q_D(QComboBox);
+ if (d->lineEdit) {
+ Qt::ContextMenuPolicy p = d->lineEdit->contextMenuPolicy();
+ d->lineEdit->setContextMenuPolicy(Qt::DefaultContextMenu);
+ d->lineEdit->event(e);
+ d->lineEdit->setContextMenuPolicy(p);
+ }
+}
+#endif // QT_NO_CONTEXTMENU
+
+void QComboBoxPrivate::keyboardSearchString(const QString &text)
+{
+ // use keyboardSearch from the listView so we do not duplicate code
+ QAbstractItemView *view = viewContainer()->itemView();
+ view->setCurrentIndex(currentIndex);
+ int currentRow = view->currentIndex().row();
+ view->keyboardSearch(text);
+ if (currentRow != view->currentIndex().row()) {
+ setCurrentIndex(view->currentIndex());
+ emitActivated(currentIndex);
+ }
+}
+
+void QComboBoxPrivate::modelChanged()
+{
+ Q_Q(QComboBox);
+
+ if (sizeAdjustPolicy == QComboBox::AdjustToContents) {
+ sizeHint = QSize();
+ adjustComboBoxSize();
+ q->updateGeometry();
+ }
+}
+
+/*!
+ \reimp
+*/
+void QComboBox::inputMethodEvent(QInputMethodEvent *e)
+{
+ Q_D(QComboBox);
+ if (d->lineEdit) {
+ d->lineEdit->event(e);
+ } else {
+ if (!e->commitString().isEmpty())
+ d->keyboardSearchString(e->commitString());
+ else
+ e->ignore();
+ }
+}
+
+/*!
+ \reimp
+*/
+QVariant QComboBox::inputMethodQuery(Qt::InputMethodQuery query) const
+{
+ Q_D(const QComboBox);
+ if (d->lineEdit)
+ return d->lineEdit->inputMethodQuery(query);
+ return QWidget::inputMethodQuery(query);
+}
+
+/*!
+ \fn bool QComboBox::editable() const
+
+ Use isEditable() instead.
+*/
+
+/*!
+ \fn void QComboBox::insertItem(const QPixmap &pixmap, int index)
+
+ Use an insertItem() function that takes a QIcon instead, for
+ example, insertItem(index, QIcon(pixmap)).
+*/
+
+/*!
+ \fn void QComboBox::insertItem(const QPixmap &pixmap, const QString &text, int index)
+
+ Use an insertItem() function that takes a QIcon instead, for
+ example, insertItem(index, QIcon(pixmap), text).
+
+ \sa insertItems()
+*/
+
+/*!
+ \fn void QComboBox::changeItem(const QString &text, int index)
+
+ Use setItemText() instead.
+*/
+
+/*!
+ \fn void QComboBox::changeItem(const QPixmap &pixmap, int index)
+
+ Use setItemIcon() instead, for example,
+ setItemIcon(index, QIcon(pixmap)).
+*/
+
+/*!
+ \fn void QComboBox::changeItem(const QPixmap &pixmap, const QString &text, int index)
+
+ Use setItem() instead, for example, setItem(index, QIcon(pixmap),text).
+*/
+
+/*!
+ \fn void QComboBox::addItem(const QString &text, const QVariant &userData)
+
+ Adds an item to the combobox with the given \a text, and
+ containing the specified \a userData (stored in the Qt::UserRole).
+ The item is appended to the list of existing items.
+*/
+
+/*!
+ \fn void QComboBox::addItem(const QIcon &icon, const QString &text,
+ const QVariant &userData)
+
+ Adds an item to the combobox with the given \a icon and \a text,
+ and containing the specified \a userData (stored in the
+ Qt::UserRole). The item is appended to the list of existing items.
+*/
+
+/*!
+ \fn void QComboBox::addItems(const QStringList &texts)
+
+ Adds each of the strings in the given \a texts to the combobox. Each item
+ is appended to the list of existing items in turn.
+*/
+
+/*!
+ \fn void QComboBox::editTextChanged(const QString &text)
+
+ This signal is emitted when the text in the combobox's line edit
+ widget is changed. The new text is specified by \a text.
+*/
+
+/*!
+ \fn QComboBox::InsertPolicy QComboBox::insertionPolicy() const
+ \compat
+
+ Use QComboBox::insertPolicy instead.
+*/
+
+/*!
+ \fn void QComboBox::setInsertionPolicy(InsertPolicy policy)
+ \compat
+
+ Use QComboBox::insertPolicy instead.
+*/
+
+/*!
+ \fn void QComboBox::setCurrentText(const QString &text)
+ \compat
+
+ Use setItemText() instead.
+
+ \sa currentIndex()
+*/
+
+/*!
+ \fn QString QComboBox::text(int index) const
+ \compat
+
+ Use itemText() instead.
+*/
+
+/*!
+ \fn QPixmap QComboBox::pixmap(int index) const
+ \compat
+
+ Use itemIcon() instead.
+*/
+
+/*!
+ \fn void QComboBox::insertStringList(const QStringList &list, int index)
+ \compat
+
+ Use insertItems() instead.
+*/
+
+/*!
+ \fn void QComboBox::insertItem(const QString &text, int index)
+ \compat
+*/
+
+/*!
+ \fn void QComboBox::clearEdit()
+ \compat
+
+ Use clearEditText() instead.
+*/
+
+
+/*!
+ \property QComboBox::frame
+ \brief whether the combo box draws itself with a frame
+
+
+ If enabled (the default) the combo box draws itself inside a
+ frame, otherwise the combo box draws itself without any frame.
+*/
+bool QComboBox::hasFrame() const
+{
+ Q_D(const QComboBox);
+ return d->frame;
+}
+
+
+void QComboBox::setFrame(bool enable)
+{
+ Q_D(QComboBox);
+ d->frame = enable;
+ update();
+ updateGeometry();
+}
+
+/*!
+ \property QComboBox::modelColumn
+ \brief the column in the model that is visible.
+
+ If set prior to populating the combo box, the pop-up view will
+ not be affected and will show the first column (using this property's
+ default value).
+
+ By default, this property has a value of 0.
+*/
+int QComboBox::modelColumn() const
+{
+ Q_D(const QComboBox);
+ return d->modelColumn;
+}
+
+void QComboBox::setModelColumn(int visibleColumn)
+{
+ Q_D(QComboBox);
+ d->modelColumn = visibleColumn;
+ QListView *lv = qobject_cast<QListView *>(d->viewContainer()->itemView());
+ if (lv)
+ lv->setModelColumn(visibleColumn);
+#ifndef QT_NO_COMPLETER
+ if (d->lineEdit && d->lineEdit->completer()
+ && d->lineEdit->completer() == d->completer)
+ d->lineEdit->completer()->setCompletionColumn(visibleColumn);
+#endif
+ setCurrentIndex(currentIndex()); //update the text to the text of the new column;
+}
+
+/*!
+ \fn int QComboBox::currentItem() const
+
+ Use currentIndex() instead.
+*/
+
+/*!
+ \fn void QComboBox::setCurrentItem(int)
+
+ Use setCurrentIndex(int) instead.
+*/
+
+/*!
+ \fn void QComboBox::popup()
+
+ Use showPopup() instead.
+*/
+
+/*!
+ \fn void QComboBox::textChanged(const QString &text)
+
+ Use the editTextChanged(const QString &text) signal instead.
+*/
+
+/*!
+ \typedef QComboBox::Policy
+ \compat
+
+ Use QComboBox::InsertPolicy instead.
+*/
+
+QT_END_NAMESPACE
+
+#include "moc_qcombobox.cpp"
+
+#endif // QT_NO_COMBOBOX
diff --git a/src/gui/widgets/qcombobox.h b/src/gui/widgets/qcombobox.h
new file mode 100644
index 0000000000..2cb2e8f389
--- /dev/null
+++ b/src/gui/widgets/qcombobox.h
@@ -0,0 +1,338 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QCOMBOBOX_H
+#define QCOMBOBOX_H
+
+#include <QtGui/qwidget.h>
+#include <QtGui/qabstractitemdelegate.h>
+#include <QtCore/qabstractitemmodel.h>
+#include <QtCore/qvariant.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+#ifndef QT_NO_COMBOBOX
+
+class QAbstractItemView;
+class QLineEdit;
+class QComboBoxPrivate;
+class QCompleter;
+
+class Q_GUI_EXPORT QComboBox : public QWidget
+{
+ Q_OBJECT
+
+ Q_ENUMS(InsertPolicy)
+ Q_ENUMS(SizeAdjustPolicy)
+ Q_PROPERTY(bool editable READ isEditable WRITE setEditable)
+ Q_PROPERTY(int count READ count)
+ Q_PROPERTY(QString currentText READ currentText)
+ Q_PROPERTY(int currentIndex READ currentIndex WRITE setCurrentIndex NOTIFY currentIndexChanged)
+ Q_PROPERTY(int maxVisibleItems READ maxVisibleItems WRITE setMaxVisibleItems)
+ Q_PROPERTY(int maxCount READ maxCount WRITE setMaxCount)
+ Q_PROPERTY(InsertPolicy insertPolicy READ insertPolicy WRITE setInsertPolicy)
+ Q_PROPERTY(SizeAdjustPolicy sizeAdjustPolicy READ sizeAdjustPolicy WRITE setSizeAdjustPolicy)
+ Q_PROPERTY(int minimumContentsLength READ minimumContentsLength WRITE setMinimumContentsLength)
+ Q_PROPERTY(QSize iconSize READ iconSize WRITE setIconSize)
+
+#ifndef QT_NO_COMPLETER
+ Q_PROPERTY(bool autoCompletion READ autoCompletion WRITE setAutoCompletion DESIGNABLE false)
+ Q_PROPERTY(Qt::CaseSensitivity autoCompletionCaseSensitivity READ autoCompletionCaseSensitivity WRITE setAutoCompletionCaseSensitivity DESIGNABLE false)
+#endif // QT_NO_COMPLETER
+
+ Q_PROPERTY(bool duplicatesEnabled READ duplicatesEnabled WRITE setDuplicatesEnabled)
+ Q_PROPERTY(bool frame READ hasFrame WRITE setFrame)
+ Q_PROPERTY(int modelColumn READ modelColumn WRITE setModelColumn)
+
+public:
+ explicit QComboBox(QWidget *parent = 0);
+ ~QComboBox();
+
+ int maxVisibleItems() const;
+ void setMaxVisibleItems(int maxItems);
+
+ int count() const;
+ void setMaxCount(int max);
+ int maxCount() const;
+
+#ifndef QT_NO_COMPLETER
+ bool autoCompletion() const;
+ void setAutoCompletion(bool enable);
+
+ Qt::CaseSensitivity autoCompletionCaseSensitivity() const;
+ void setAutoCompletionCaseSensitivity(Qt::CaseSensitivity sensitivity);
+#endif
+
+ bool duplicatesEnabled() const;
+ void setDuplicatesEnabled(bool enable);
+
+ void setFrame(bool);
+ bool hasFrame() const;
+
+ inline int findText(const QString &text,
+ Qt::MatchFlags flags = Qt::MatchExactly|Qt::MatchCaseSensitive) const
+ { return findData(text, Qt::DisplayRole, flags); }
+ int findData(const QVariant &data, int role = Qt::UserRole,
+ Qt::MatchFlags flags = Qt::MatchExactly|Qt::MatchCaseSensitive) const;
+
+ enum InsertPolicy {
+ NoInsert,
+ InsertAtTop,
+ InsertAtCurrent,
+ InsertAtBottom,
+ InsertAfterCurrent,
+ InsertBeforeCurrent,
+ InsertAlphabetically
+#if defined(QT3_SUPPORT) && !defined(Q_MOC_RUN)
+ ,
+ NoInsertion = NoInsert,
+ AtTop = InsertAtTop,
+ AtCurrent = InsertAtCurrent,
+ AtBottom = InsertAtBottom,
+ AfterCurrent = InsertAfterCurrent,
+ BeforeCurrent = InsertBeforeCurrent
+#endif
+ };
+#ifdef QT3_SUPPORT
+ typedef InsertPolicy Policy;
+#endif
+
+ InsertPolicy insertPolicy() const;
+ void setInsertPolicy(InsertPolicy policy);
+
+ enum SizeAdjustPolicy {
+ AdjustToContents,
+ AdjustToContentsOnFirstShow,
+ AdjustToMinimumContentsLength, // ### Qt 5: remove
+ AdjustToMinimumContentsLengthWithIcon
+ };
+
+ SizeAdjustPolicy sizeAdjustPolicy() const;
+ void setSizeAdjustPolicy(SizeAdjustPolicy policy);
+ int minimumContentsLength() const;
+ void setMinimumContentsLength(int characters);
+ QSize iconSize() const;
+ void setIconSize(const QSize &size);
+
+ bool isEditable() const;
+ void setEditable(bool editable);
+ void setLineEdit(QLineEdit *edit);
+ QLineEdit *lineEdit() const;
+#ifndef QT_NO_VALIDATOR
+ void setValidator(const QValidator *v);
+ const QValidator *validator() const;
+#endif
+
+#ifndef QT_NO_COMPLETER
+ void setCompleter(QCompleter *c);
+ QCompleter *completer() const;
+#endif
+
+ QAbstractItemDelegate *itemDelegate() const;
+ void setItemDelegate(QAbstractItemDelegate *delegate);
+
+ QAbstractItemModel *model() const;
+ void setModel(QAbstractItemModel *model);
+
+ QModelIndex rootModelIndex() const;
+ void setRootModelIndex(const QModelIndex &index);
+
+ int modelColumn() const;
+ void setModelColumn(int visibleColumn);
+
+ int currentIndex() const;
+
+ QString currentText() const;
+
+ QString itemText(int index) const;
+ QIcon itemIcon(int index) const;
+ QVariant itemData(int index, int role = Qt::UserRole) const;
+
+ inline void addItem(const QString &text, const QVariant &userData = QVariant());
+ inline void addItem(const QIcon &icon, const QString &text,
+ const QVariant &userData = QVariant());
+ inline void addItems(const QStringList &texts)
+ { insertItems(count(), texts); }
+
+ inline void insertItem(int index, const QString &text, const QVariant &userData = QVariant());
+ void insertItem(int index, const QIcon &icon, const QString &text,
+ const QVariant &userData = QVariant());
+ void insertItems(int index, const QStringList &texts);
+ void insertSeparator(int index);
+
+ void removeItem(int index);
+
+ void setItemText(int index, const QString &text);
+ void setItemIcon(int index, const QIcon &icon);
+ void setItemData(int index, const QVariant &value, int role = Qt::UserRole);
+
+ QAbstractItemView *view() const;
+ void setView(QAbstractItemView *itemView);
+
+ QSize sizeHint() const;
+ QSize minimumSizeHint() const;
+
+ virtual void showPopup();
+ virtual void hidePopup();
+
+ bool event(QEvent *event);
+
+public Q_SLOTS:
+ void clear();
+ void clearEditText();
+ void setEditText(const QString &text);
+ void setCurrentIndex(int index);
+
+Q_SIGNALS:
+ void editTextChanged(const QString &);
+ void activated(int index);
+ void activated(const QString &);
+ void highlighted(int index);
+ void highlighted(const QString &);
+ void currentIndexChanged(int index);
+ void currentIndexChanged(const QString &);
+
+protected:
+ void focusInEvent(QFocusEvent *e);
+ void focusOutEvent(QFocusEvent *e);
+ void changeEvent(QEvent *e);
+ void resizeEvent(QResizeEvent *e);
+ void paintEvent(QPaintEvent *e);
+ void showEvent(QShowEvent *e);
+ void hideEvent(QHideEvent *e);
+ void mousePressEvent(QMouseEvent *e);
+ void mouseReleaseEvent(QMouseEvent *e);
+ void keyPressEvent(QKeyEvent *e);
+ void keyReleaseEvent(QKeyEvent *e);
+ void wheelEvent(QWheelEvent *e);
+ void contextMenuEvent(QContextMenuEvent *e);
+ void inputMethodEvent(QInputMethodEvent *);
+ QVariant inputMethodQuery(Qt::InputMethodQuery) const;
+ void initStyleOption(QStyleOptionComboBox *option) const;
+
+#ifdef QT3_SUPPORT
+public:
+ QT3_SUPPORT_CONSTRUCTOR QComboBox(QWidget *parent, const char *name);
+ QT3_SUPPORT_CONSTRUCTOR QComboBox(bool rw, QWidget *parent, const char *name = 0);
+ inline QT3_SUPPORT int currentItem() const { return currentIndex(); }
+ inline QT3_SUPPORT void setCurrentItem(int index) { setCurrentIndex(index); }
+ inline QT3_SUPPORT InsertPolicy insertionPolicy() const { return insertPolicy(); }
+ inline QT3_SUPPORT void setInsertionPolicy(InsertPolicy policy) { setInsertPolicy(policy); }
+ inline QT3_SUPPORT bool editable() const { return isEditable(); }
+ inline QT3_SUPPORT void popup() { showPopup(); }
+ inline QT3_SUPPORT void setCurrentText(const QString& text) {
+ int i = findText(text);
+ if (i != -1)
+ setCurrentIndex(i);
+ else if (isEditable())
+ setEditText(text);
+ else
+ setItemText(currentIndex(), text);
+ }
+ inline QT3_SUPPORT QString text(int index) const { return itemText(index); }
+
+ inline QT3_SUPPORT QPixmap pixmap(int index) const
+ { return itemIcon(index).pixmap(iconSize(), isEnabled() ? QIcon::Normal : QIcon::Disabled); }
+ inline QT3_SUPPORT void insertStringList(const QStringList &list, int index = -1)
+ { insertItems((index < 0 ? count() : index), list); }
+ inline QT3_SUPPORT void insertItem(const QString &text, int index = -1)
+ { insertItem((index < 0 ? count() : index), text); }
+ inline QT3_SUPPORT void insertItem(const QPixmap &pix, int index = -1)
+ { insertItem((index < 0 ? count() : index), QIcon(pix), QString()); }
+ inline QT3_SUPPORT void insertItem(const QPixmap &pix, const QString &text, int index = -1)
+ { insertItem((index < 0 ? count() : index), QIcon(pix), text); }
+ inline QT3_SUPPORT void changeItem(const QString &text, int index)
+ { setItemText(index, text); }
+ inline QT3_SUPPORT void changeItem(const QPixmap &pix, int index)
+ { setItemIcon(index, QIcon(pix)); }
+ inline QT3_SUPPORT void changeItem(const QPixmap &pix, const QString &text, int index)
+ { setItemIcon(index, QIcon(pix)); setItemText(index, text); }
+ inline QT3_SUPPORT void clearValidator() { setValidator(0); }
+ inline QT3_SUPPORT void clearEdit() { clearEditText(); }
+
+Q_SIGNALS:
+ QT_MOC_COMPAT void textChanged(const QString &);
+#endif
+
+protected:
+ QComboBox(QComboBoxPrivate &, QWidget *);
+
+private:
+ Q_DECLARE_PRIVATE(QComboBox)
+ Q_DISABLE_COPY(QComboBox)
+ Q_PRIVATE_SLOT(d_func(), void _q_itemSelected(const QModelIndex &item))
+ Q_PRIVATE_SLOT(d_func(), void _q_emitHighlighted(const QModelIndex &))
+ Q_PRIVATE_SLOT(d_func(), void _q_emitCurrentIndexChanged(const QModelIndex &index))
+ Q_PRIVATE_SLOT(d_func(), void _q_returnPressed())
+ Q_PRIVATE_SLOT(d_func(), void _q_resetButton())
+ Q_PRIVATE_SLOT(d_func(), void _q_dataChanged(const QModelIndex &, const QModelIndex &))
+ Q_PRIVATE_SLOT(d_func(), void _q_rowsAboutToBeInserted(const QModelIndex & parent, int start, int end))
+ Q_PRIVATE_SLOT(d_func(), void _q_rowsInserted(const QModelIndex & parent, int start, int end))
+ Q_PRIVATE_SLOT(d_func(), void _q_rowsAboutToBeRemoved(const QModelIndex & parent, int start, int end))
+ Q_PRIVATE_SLOT(d_func(), void _q_rowsRemoved(const QModelIndex & parent, int start, int end))
+ Q_PRIVATE_SLOT(d_func(), void _q_modelDestroyed())
+ Q_PRIVATE_SLOT(d_func(), void _q_modelReset())
+#ifdef QT_KEYPAD_NAVIGATION
+ Q_PRIVATE_SLOT(d_func(), void _q_completerActivated())
+#endif
+};
+
+inline void QComboBox::addItem(const QString &atext, const QVariant &auserData)
+{ insertItem(count(), atext, auserData); }
+inline void QComboBox::addItem(const QIcon &aicon, const QString &atext,
+ const QVariant &auserData)
+{ insertItem(count(), aicon, atext, auserData); }
+
+inline void QComboBox::insertItem(int aindex, const QString &atext,
+ const QVariant &auserData)
+{ insertItem(aindex, QIcon(), atext, auserData); }
+
+#endif // QT_NO_COMBOBOX
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QCOMBOBOX_H
diff --git a/src/gui/widgets/qcombobox_p.h b/src/gui/widgets/qcombobox_p.h
new file mode 100644
index 0000000000..f1203dcd75
--- /dev/null
+++ b/src/gui/widgets/qcombobox_p.h
@@ -0,0 +1,410 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QCOMBOBOX_P_H
+#define QCOMBOBOX_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 "QtGui/qcombobox.h"
+
+#ifndef QT_NO_COMBOBOX
+#include "QtGui/qabstractslider.h"
+#include "QtGui/qapplication.h"
+#include "QtGui/qitemdelegate.h"
+#include "QtGui/qstandarditemmodel.h"
+#include "QtGui/qlineedit.h"
+#include "QtGui/qlistview.h"
+#include "QtGui/qpainter.h"
+#include "QtGui/qstyle.h"
+#include "QtGui/qstyleoption.h"
+#include "QtCore/qhash.h"
+#include "QtCore/qpair.h"
+#include "QtCore/qtimer.h"
+#include "private/qwidget_p.h"
+#include "QtCore/qpointer.h"
+#include "QtGui/qcompleter.h"
+#include "QtGui/qevent.h"
+#include "QtCore/qdebug.h"
+
+#include <limits.h>
+
+QT_BEGIN_NAMESPACE
+
+class QComboBoxListView : public QListView
+{
+ Q_OBJECT
+public:
+ QComboBoxListView(QComboBox *cmb = 0) : combo(cmb) {}
+
+protected:
+ void resizeEvent(QResizeEvent *event)
+ {
+ resizeContents(viewport()->width(), contentsSize().height());
+ QListView::resizeEvent(event);
+ }
+
+ QStyleOptionViewItem viewOptions() const
+ {
+ QStyleOptionViewItem option = QListView::viewOptions();
+ option.showDecorationSelected = true;
+ if (combo)
+ option.font = combo->font();
+ return option;
+ }
+
+ void paintEvent(QPaintEvent *e)
+ {
+ if (combo) {
+ QStyleOptionComboBox opt;
+ opt.initFrom(combo);
+ opt.editable = combo->isEditable();
+ if (combo->style()->styleHint(QStyle::SH_ComboBox_Popup, &opt, combo)) {
+ //we paint the empty menu area to avoid having blank space that can happen when scrolling
+ QStyleOptionMenuItem menuOpt;
+ menuOpt.initFrom(this);
+ menuOpt.palette = palette();
+ menuOpt.state = QStyle::State_None;
+ menuOpt.checkType = QStyleOptionMenuItem::NotCheckable;
+ menuOpt.menuRect = e->rect();
+ menuOpt.maxIconWidth = 0;
+ menuOpt.tabWidth = 0;
+ QPainter p(viewport());
+ combo->style()->drawControl(QStyle::CE_MenuEmptyArea, &menuOpt, &p, this);
+ }
+ }
+ QListView::paintEvent(e);
+ }
+
+private:
+ QComboBox *combo;
+};
+
+
+class QStandardItemModel;
+
+class Q_AUTOTEST_EXPORT QComboBoxPrivateScroller : public QWidget
+{
+ Q_OBJECT
+
+public:
+ QComboBoxPrivateScroller(QAbstractSlider::SliderAction action, QWidget *parent)
+ : QWidget(parent), sliderAction(action)
+ {
+ setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed);
+ setAttribute(Qt::WA_NoMousePropagation);
+ }
+ QSize sizeHint() const {
+ return QSize(20, style()->pixelMetric(QStyle::PM_MenuScrollerHeight));
+ }
+
+protected:
+ inline void stopTimer() {
+ timer.stop();
+ }
+
+ inline void startTimer() {
+ timer.start(100, this);
+ fast = false;
+ }
+
+ void enterEvent(QEvent *) {
+ startTimer();
+ }
+
+ void leaveEvent(QEvent *) {
+ stopTimer();
+ }
+ void timerEvent(QTimerEvent *e) {
+ if (e->timerId() == timer.timerId()) {
+ emit doScroll(sliderAction);
+ if (fast) {
+ emit doScroll(sliderAction);
+ emit doScroll(sliderAction);
+ }
+ }
+ }
+ void hideEvent(QHideEvent *) {
+ stopTimer();
+ }
+
+ void mouseMoveEvent(QMouseEvent *e)
+ {
+ // Enable fast scrolling if the cursor is directly above or below the popup.
+ const int mouseX = e->pos().x();
+ const int mouseY = e->pos().y();
+ const bool horizontallyInside = pos().x() < mouseX && mouseX < rect().right() + 1;
+ const bool verticallyOutside = (sliderAction == QAbstractSlider::SliderSingleStepAdd) ?
+ rect().bottom() + 1 < mouseY : mouseY < pos().y();
+
+ fast = horizontallyInside && verticallyOutside;
+ }
+
+ void paintEvent(QPaintEvent *) {
+ QPainter p(this);
+ QStyleOptionMenuItem menuOpt;
+ menuOpt.init(this);
+ menuOpt.checkType = QStyleOptionMenuItem::NotCheckable;
+ menuOpt.menuRect = rect();
+ menuOpt.maxIconWidth = 0;
+ menuOpt.tabWidth = 0;
+ menuOpt.menuItemType = QStyleOptionMenuItem::Scroller;
+ if (sliderAction == QAbstractSlider::SliderSingleStepAdd)
+ menuOpt.state |= QStyle::State_DownArrow;
+ p.eraseRect(rect());
+ style()->drawControl(QStyle::CE_MenuScroller, &menuOpt, &p);
+ }
+
+Q_SIGNALS:
+ void doScroll(int action);
+
+private:
+ QAbstractSlider::SliderAction sliderAction;
+ QBasicTimer timer;
+ bool fast;
+};
+
+class Q_AUTOTEST_EXPORT QComboBoxPrivateContainer : public QFrame
+{
+ Q_OBJECT
+
+public:
+ QComboBoxPrivateContainer(QAbstractItemView *itemView, QComboBox *parent);
+ QAbstractItemView *itemView() const;
+ void setItemView(QAbstractItemView *itemView);
+ int spacing() const;
+ void updateTopBottomMargin();
+
+ QTimer blockMouseReleaseTimer;
+ QBasicTimer adjustSizeTimer;
+ QPoint initialClickPosition;
+
+public Q_SLOTS:
+ void scrollItemView(int action);
+ void updateScrollers();
+ void setCurrentIndex(const QModelIndex &index);
+ void viewDestroyed();
+
+protected:
+ void changeEvent(QEvent *e);
+ bool eventFilter(QObject *o, QEvent *e);
+ void mousePressEvent(QMouseEvent *e);
+ void mouseReleaseEvent(QMouseEvent *e);
+ void showEvent(QShowEvent *e);
+ void hideEvent(QHideEvent *e);
+ void timerEvent(QTimerEvent *timerEvent);
+ void leaveEvent(QEvent *e);
+ void resizeEvent(QResizeEvent *e);
+ QStyleOptionComboBox comboStyleOption() const;
+
+Q_SIGNALS:
+ void itemSelected(const QModelIndex &);
+ void resetButton();
+
+private:
+ QComboBox *combo;
+ QAbstractItemView *view;
+ QComboBoxPrivateScroller *top;
+ QComboBoxPrivateScroller *bottom;
+};
+
+class QComboMenuDelegate : public QAbstractItemDelegate
+{
+public:
+ QComboMenuDelegate(QObject *parent, QComboBox *cmb) : QAbstractItemDelegate(parent), mCombo(cmb) {}
+
+protected:
+ void paint(QPainter *painter,
+ const QStyleOptionViewItem &option,
+ const QModelIndex &index) const {
+ QStyleOptionMenuItem opt = getStyleOption(option, index);
+ painter->eraseRect(option.rect);
+ mCombo->style()->drawControl(QStyle::CE_MenuItem, &opt, painter, mCombo);
+ }
+ QSize sizeHint(const QStyleOptionViewItem &option,
+ const QModelIndex &index) const {
+ QStyleOptionMenuItem opt = getStyleOption(option, index);
+ return mCombo->style()->sizeFromContents(
+ QStyle::CT_MenuItem, &opt, option.rect.size(), mCombo);
+ }
+
+private:
+ QStyleOptionMenuItem getStyleOption(const QStyleOptionViewItem &option,
+ const QModelIndex &index) const;
+ QComboBox *mCombo;
+};
+
+// Note that this class is intentionally not using QStyledItemDelegate
+// Vista does not use the new theme for combo boxes and there might
+// be other side effects from using the new class
+class QComboBoxDelegate : public QItemDelegate
+{
+public:
+ QComboBoxDelegate(QObject *parent, QComboBox *cmb) : QItemDelegate(parent), mCombo(cmb) {}
+
+ static bool isSeparator(const QModelIndex &index) {
+ return index.data(Qt::AccessibleDescriptionRole).toString() == QString::fromLatin1("separator");
+ }
+ static void setSeparator(QAbstractItemModel *model, const QModelIndex &index) {
+ model->setData(index, QString::fromLatin1("separator"), Qt::AccessibleDescriptionRole);
+ if (QStandardItemModel *m = qobject_cast<QStandardItemModel*>(model))
+ if (QStandardItem *item = m->itemFromIndex(index))
+ item->setFlags(item->flags() & ~(Qt::ItemIsSelectable|Qt::ItemIsEnabled));
+ }
+
+protected:
+ void paint(QPainter *painter,
+ const QStyleOptionViewItem &option,
+ const QModelIndex &index) const {
+ if (isSeparator(index)) {
+ QRect rect = option.rect;
+ if (const QStyleOptionViewItemV3 *v3 = qstyleoption_cast<const QStyleOptionViewItemV3*>(&option))
+ if (const QAbstractItemView *view = qobject_cast<const QAbstractItemView*>(v3->widget))
+ rect.setWidth(view->viewport()->width());
+ QStyleOption opt;
+ opt.rect = rect;
+ mCombo->style()->drawPrimitive(QStyle::PE_IndicatorToolBarSeparator, &opt, painter, mCombo);
+ } else {
+ QItemDelegate::paint(painter, option, index);
+ }
+ }
+
+ QSize sizeHint(const QStyleOptionViewItem &option,
+ const QModelIndex &index) const {
+ if (isSeparator(index)) {
+ int pm = mCombo->style()->pixelMetric(QStyle::PM_DefaultFrameWidth, 0, mCombo);
+ return QSize(pm, pm);
+ }
+ return QItemDelegate::sizeHint(option, index);
+ }
+private:
+ QComboBox *mCombo;
+};
+
+class QComboBoxPrivate : public QWidgetPrivate
+{
+ Q_DECLARE_PUBLIC(QComboBox)
+public:
+ QComboBoxPrivate();
+ ~QComboBoxPrivate() {}
+ void init();
+ QComboBoxPrivateContainer* viewContainer();
+ void updateLineEditGeometry();
+ void _q_returnPressed();
+ void _q_complete();
+ void _q_itemSelected(const QModelIndex &item);
+ bool contains(const QString &text, int role);
+ void emitActivated(const QModelIndex&);
+ void _q_emitHighlighted(const QModelIndex&);
+ void _q_emitCurrentIndexChanged(const QModelIndex &index);
+ void _q_modelDestroyed();
+ void _q_modelReset();
+#ifdef QT_KEYPAD_NAVIGATION
+ void _q_completerActivated();
+#endif
+ void _q_resetButton();
+ void _q_dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight);
+ void _q_rowsAboutToBeInserted(const QModelIndex & parent, int start, int end);
+ void _q_rowsInserted(const QModelIndex & parent, int start, int end);
+ void _q_rowsAboutToBeRemoved(const QModelIndex & parent, int start, int end);
+ void _q_rowsRemoved(const QModelIndex & parent, int start, int end);
+ void updateArrow(QStyle::StateFlag state);
+ bool updateHoverControl(const QPoint &pos);
+ QRect popupGeometry(int screen = -1) const;
+ QStyle::SubControl newHoverControl(const QPoint &pos);
+ int computeWidthHint() const;
+ QSize recomputeSizeHint(QSize &sh) const;
+ void adjustComboBoxSize();
+ QString itemText(const QModelIndex &index) const;
+ QIcon itemIcon(const QModelIndex &index) const;
+ int itemRole() const;
+ void updateLayoutDirection();
+ void setCurrentIndex(const QModelIndex &index);
+ void updateDelegate();
+ void keyboardSearchString(const QString &text);
+ void modelChanged();
+
+ QAbstractItemModel *model;
+ QLineEdit *lineEdit;
+ QComboBoxPrivateContainer *container;
+ QComboBox::InsertPolicy insertPolicy;
+ QComboBox::SizeAdjustPolicy sizeAdjustPolicy;
+ int minimumContentsLength;
+ QSize iconSize;
+ uint shownOnce : 1;
+ uint autoCompletion : 1;
+ uint duplicatesEnabled : 1;
+ uint frame : 1;
+ uint padding : 26;
+ int maxVisibleItems;
+ int maxCount;
+ int modelColumn;
+ bool inserting;
+ mutable QSize minimumSizeHint;
+ mutable QSize sizeHint;
+ QStyle::StateFlag arrowState;
+ QStyle::SubControl hoverControl;
+ QRect hoverRect;
+ QPersistentModelIndex currentIndex;
+ QPersistentModelIndex root;
+ Qt::CaseSensitivity autoCompletionCaseSensitivity;
+ int indexBeforeChange;
+#ifndef QT_NO_COMPLETER
+ QPointer<QCompleter> completer;
+#endif
+ static QPalette viewContainerPalette(QComboBox *cmb)
+ { return cmb->d_func()->viewContainer()->palette(); }
+};
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_COMBOBOX
+
+#endif // QCOMBOBOX_P_H
diff --git a/src/gui/widgets/qcommandlinkbutton.cpp b/src/gui/widgets/qcommandlinkbutton.cpp
new file mode 100644
index 0000000000..13ee6af4bf
--- /dev/null
+++ b/src/gui/widgets/qcommandlinkbutton.cpp
@@ -0,0 +1,384 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qcommandlinkbutton.h"
+#include "qstylepainter.h"
+#include "qstyleoption.h"
+#include "qtextdocument.h"
+#include "qtextlayout.h"
+#include "qcolor.h"
+#include "qfont.h"
+
+#include "private/qpushbutton_p.h"
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QCommandLinkButton
+ \since 4.4
+ \brief The QCommandLinkButton widget provides a Vista style command link button.
+
+ \ingroup basicwidgets
+ \mainclass
+
+ The command link is a new control that was introduced by Windows Vista. It's
+ intended use is similar to that of a radio button in that it is used to choose
+ between a set of mutually exclusive options. Command link buttons should not
+ be used by themselves but rather as an alternative to radio buttons in
+ Wizards and dialogs and makes pressing the "next" button redundant.
+ The appearance is generally similar to that of a flat pushbutton, but
+ it allows for a descriptive text in addition to the normal button text.
+ By default it will also carry an arrow icon, indicating that pressing the
+ control will open another window or page.
+
+ \sa QPushButton QRadioButton
+*/
+
+/*!
+ \property QCommandLinkButton::description
+ \brief A descriptive label to complement the button text
+
+ Setting this property will set a descriptive text on the
+ button, complementing the text label. This will usually
+ be displayed in a smaller font than the primary text.
+*/
+
+/*!
+ \property QCommandLinkButton::flat
+ \brief This property determines whether the button is displayed as a flat
+ panel or with a border.
+
+ By default, this property is set to false.
+
+ \sa QPushButton::flat
+*/
+
+class QCommandLinkButtonPrivate : public QPushButtonPrivate
+{
+ Q_DECLARE_PUBLIC(QCommandLinkButton)
+
+public:
+ QCommandLinkButtonPrivate()
+ : QPushButtonPrivate(){}
+
+ void init();
+ qreal titleSize() const;
+ bool usingVistaStyle() const;
+
+ QFont titleFont() const;
+ QFont descriptionFont() const;
+
+ QRect titleRect() const;
+ QRect descriptionRect() const;
+
+ int textOffset() const;
+ int descriptionOffset() const;
+ int descriptionHeight(int width) const;
+ QColor mergedColors(const QColor &a, const QColor &b, int value) const;
+
+ int topMargin() const { return 10; }
+ int leftMargin() const { return 7; }
+ int rightMargin() const { return 4; }
+ int bottomMargin() const { return 4; }
+
+ QString description;
+ QColor currentColor;
+};
+
+// Mix colors a and b with a ratio in the range [0-255]
+QColor QCommandLinkButtonPrivate::mergedColors(const QColor &a, const QColor &b, int value = 50) const
+{
+ Q_ASSERT(value >= 0);
+ Q_ASSERT(value <= 255);
+ QColor tmp = a;
+ tmp.setRed((tmp.red() * value) / 255 + (b.red() * (255 - value)) / 255);
+ tmp.setGreen((tmp.green() * value) / 255 + (b.green() * (255 - value)) / 255);
+ tmp.setBlue((tmp.blue() * value) / 255 + (b.blue() * (255 - value)) / 255);
+ return tmp;
+}
+
+QFont QCommandLinkButtonPrivate::titleFont() const
+{
+ Q_Q(const QCommandLinkButton);
+ QFont font = q->font();
+ if (usingVistaStyle()) {
+ font.setPointSizeF(12.0);
+ } else {
+ font.setBold(true);
+ font.setPointSizeF(9.0);
+ }
+ return font;
+}
+
+QFont QCommandLinkButtonPrivate::descriptionFont() const
+{
+ Q_Q(const QCommandLinkButton);
+ QFont font = q->font();
+ font.setPointSizeF(9.0);
+ return font;
+}
+
+QRect QCommandLinkButtonPrivate::titleRect() const
+{
+ Q_Q(const QCommandLinkButton);
+ return q->rect().adjusted(textOffset(), topMargin(),
+ -rightMargin(), 0);
+}
+
+QRect QCommandLinkButtonPrivate::descriptionRect() const
+{
+ Q_Q(const QCommandLinkButton);
+ return q->rect().adjusted(textOffset(), descriptionOffset(),
+ -rightMargin(), -bottomMargin());
+}
+
+int QCommandLinkButtonPrivate::textOffset() const
+{
+ Q_Q(const QCommandLinkButton);
+ return q->icon().actualSize(q->iconSize()).width() + leftMargin() + 6;
+}
+
+int QCommandLinkButtonPrivate::descriptionOffset() const
+{
+ QFontMetrics fm(titleFont());
+ return topMargin() + fm.height();
+}
+
+bool QCommandLinkButtonPrivate::usingVistaStyle() const
+{
+ Q_Q(const QCommandLinkButton);
+ //### This is a hack to detect if we are indeed running Vista style themed and not in classic
+ // When we add api to query for this, we should change this implementation to use it.
+ return q->style()->inherits("QWindowsVistaStyle")
+ && !q->style()->pixelMetric(QStyle::PM_ButtonShiftHorizontal);
+}
+
+void QCommandLinkButtonPrivate::init()
+{
+ Q_Q(QCommandLinkButton);
+ QPushButtonPrivate::init();
+ q->setAttribute(Qt::WA_Hover);
+
+ QSizePolicy policy(QSizePolicy::Preferred, QSizePolicy::Preferred, QSizePolicy::PushButton);
+ policy.setHeightForWidth(true);
+ q->setSizePolicy(policy);
+
+ q->setIconSize(QSize(20, 20));
+ q->setIcon(q->style()->standardIcon(QStyle::SP_CommandLink));
+}
+
+// Calculates the height of the description text based on widget width
+int QCommandLinkButtonPrivate::descriptionHeight(int widgetWidth) const
+{
+ // Calc width of actual paragraph
+ int lineWidth = widgetWidth - textOffset() - rightMargin();
+
+ qreal descriptionheight = 0;
+ if (!description.isEmpty()) {
+ QTextLayout layout(description);
+ layout.setFont(descriptionFont());
+ layout.beginLayout();
+ while (true) {
+ QTextLine line = layout.createLine();
+ if (!line.isValid())
+ break;
+ line.setLineWidth(lineWidth);
+ line.setPosition(QPointF(0, descriptionheight));
+ descriptionheight += line.height();
+ }
+ layout.endLayout();
+ }
+ return qRound(descriptionheight);
+}
+
+/*!
+ \reimp
+ */
+QSize QCommandLinkButton::minimumSizeHint() const
+{
+ Q_D(const QCommandLinkButton);
+ QSize size = sizeHint();
+ int minimumHeight = qMax(d->descriptionOffset() + d->bottomMargin(),
+ iconSize().height() + d->topMargin());
+ size.setHeight(minimumHeight);
+ return size;
+}
+
+/*!
+ Constructs a command link with no text and a \a parent.
+*/
+
+QCommandLinkButton::QCommandLinkButton(QWidget *parent)
+: QPushButton(*new QCommandLinkButtonPrivate, parent)
+{
+ Q_D(QCommandLinkButton);
+ d->init();
+}
+
+/*!
+ Constructs a command link with the parent \a parent and the text \a
+ text.
+*/
+
+QCommandLinkButton::QCommandLinkButton(const QString &text, QWidget *parent)
+ : QPushButton(*new QCommandLinkButtonPrivate, parent)
+{
+ Q_D(QCommandLinkButton);
+ setText(text);
+ d->init();
+}
+
+/*!
+ Constructs a command link with a \a text, a \a description, and a \a parent.
+*/
+QCommandLinkButton::QCommandLinkButton(const QString &text, const QString &description, QWidget *parent)
+ : QPushButton(*new QCommandLinkButtonPrivate, parent)
+{
+ Q_D(QCommandLinkButton);
+ setText(text);
+ setDescription(description);
+ d->init();
+}
+
+/*! \reimp */
+bool QCommandLinkButton::event(QEvent *e)
+{
+ return QPushButton::event(e);
+}
+
+/*! \reimp */
+QSize QCommandLinkButton::sizeHint() const
+{
+// Standard size hints from UI specs
+// Without note: 135, 41
+// With note: 135, 60
+ Q_D(const QCommandLinkButton);
+
+ QSize size = QPushButton::sizeHint();
+ QFontMetrics fm(d->titleFont());
+ int textWidth = qMax(fm.width(text()), 135);
+ int buttonWidth = textWidth + d->textOffset() + d->rightMargin();
+ int heightWithoutDescription = d->descriptionOffset() + d->bottomMargin();
+
+ size.setWidth(qMax(size.width(), buttonWidth));
+ size.setHeight(qMax(d->description.isEmpty() ? 41 : 60,
+ heightWithoutDescription + d->descriptionHeight(buttonWidth)));
+ return size;
+}
+
+/*! \reimp */
+int QCommandLinkButton::heightForWidth(int width) const
+{
+ Q_D(const QCommandLinkButton);
+ int heightWithoutDescription = d->descriptionOffset() + d->bottomMargin();
+ // find the width available for the description area
+ return heightWithoutDescription + d->descriptionHeight(width);
+}
+
+/*! \reimp */
+void QCommandLinkButton::paintEvent(QPaintEvent *)
+{
+ Q_D(QCommandLinkButton);
+ QStylePainter p(this);
+ p.save();
+
+ QStyleOptionButton option;
+ initStyleOption(&option);
+
+ //Enable command link appearence on Vista
+ option.features |= QStyleOptionButton::CommandLinkButton;
+ option.text = QString();
+ option.icon = QIcon(); //we draw this ourselves
+ QSize pixmapSize = icon().actualSize(iconSize());
+
+ int vOffset = isDown() ? style()->pixelMetric(QStyle::PM_ButtonShiftVertical) : 0;
+ int hOffset = isDown() ? style()->pixelMetric(QStyle::PM_ButtonShiftHorizontal) : 0;
+
+ //Draw icon
+ p.drawControl(QStyle::CE_PushButton, option);
+ if (!icon().isNull())
+ p.drawPixmap(d->leftMargin() + hOffset, d->topMargin() + vOffset,
+ icon().pixmap(pixmapSize, isEnabled() ? QIcon::Normal : QIcon::Disabled,
+ isChecked() ? QIcon::On : QIcon::Off));
+
+ //Draw title
+ QColor textColor = palette().buttonText().color();
+ if (isEnabled() && d->usingVistaStyle()) {
+ textColor = QColor(21, 28, 85);
+ if (underMouse() && !isDown())
+ textColor = QColor(7, 64, 229);
+ //A simple text color transition
+ d->currentColor = d->mergedColors(textColor, d->currentColor, 60);
+ option.palette.setColor(QPalette::ButtonText, d->currentColor);
+ }
+
+ int textflags = Qt::TextShowMnemonic;
+ if (!style()->styleHint(QStyle::SH_UnderlineShortcut, &option, this))
+ textflags |= Qt::TextHideMnemonic;
+
+ p.setFont(d->titleFont());
+ p.drawItemText(d->titleRect().translated(hOffset, vOffset),
+ textflags, option.palette, isEnabled(), text(), QPalette::ButtonText);
+
+ //Draw description
+ textflags |= Qt::TextWordWrap | Qt::ElideRight;
+ p.setFont(d->descriptionFont());
+ p.drawItemText(d->descriptionRect().translated(hOffset, vOffset), textflags,
+ option.palette, isEnabled(), description(), QPalette::ButtonText);
+ p.restore();
+}
+
+void QCommandLinkButton::setDescription(const QString &description)
+{
+ Q_D(QCommandLinkButton);
+ d->description = description;
+ updateGeometry();
+ update();
+}
+
+QString QCommandLinkButton::description() const
+{
+ Q_D(const QCommandLinkButton);
+ return d->description;
+}
+
+QT_END_NAMESPACE
+
diff --git a/src/gui/widgets/qcommandlinkbutton.h b/src/gui/widgets/qcommandlinkbutton.h
new file mode 100644
index 0000000000..93d91cbf0d
--- /dev/null
+++ b/src/gui/widgets/qcommandlinkbutton.h
@@ -0,0 +1,85 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QCOMMANDLINKBUTTON_H
+#define QCOMMANDLINKBUTTON_H
+
+#include <QtGui/qpushbutton.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+class QCommandLinkButtonPrivate;
+
+class Q_GUI_EXPORT QCommandLinkButton: public QPushButton
+{
+ Q_OBJECT
+
+ Q_PROPERTY(QString description READ description WRITE setDescription)
+ Q_PROPERTY(bool flat READ isFlat WRITE setFlat DESIGNABLE false)
+
+public:
+ explicit QCommandLinkButton(QWidget *parent=0);
+ explicit QCommandLinkButton(const QString &text, QWidget *parent=0);
+ QCommandLinkButton(const QString &text, const QString &description, QWidget *parent=0);
+ QString description() const;
+ void setDescription(const QString &description);
+
+protected:
+ QSize sizeHint() const;
+ int heightForWidth(int) const;
+ QSize minimumSizeHint() const;
+ bool event(QEvent *e);
+ void paintEvent(QPaintEvent *);
+
+private:
+ Q_DISABLE_COPY(QCommandLinkButton)
+ Q_DECLARE_PRIVATE(QCommandLinkButton)
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QCOMMANDLINKBUTTON
diff --git a/src/gui/widgets/qdatetimeedit.cpp b/src/gui/widgets/qdatetimeedit.cpp
new file mode 100644
index 0000000000..83bec68f1b
--- /dev/null
+++ b/src/gui/widgets/qdatetimeedit.cpp
@@ -0,0 +1,2647 @@
+/****************************************************************************)
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <math.h>
+#include <private/qdatetimeedit_p.h>
+#include <qabstractspinbox.h>
+#include <qapplication.h>
+#include <qdatetimeedit.h>
+#include <qdesktopwidget.h>
+#include <qdebug.h>
+#include <qevent.h>
+#include <qlineedit.h>
+#include <private/qlineedit_p.h>
+#include <qlocale.h>
+#include <qpainter.h>
+#include <qlayout.h>
+#include <qset.h>
+#include <qstyle.h>
+
+#ifndef QT_NO_DATETIMEEDIT
+
+//#define QDATETIMEEDIT_QDTEDEBUG
+#ifdef QDATETIMEEDIT_QDTEDEBUG
+# define QDTEDEBUG qDebug() << QString::fromLatin1("%1:%2").arg(__FILE__).arg(__LINE__)
+# define QDTEDEBUGN qDebug
+#else
+# define QDTEDEBUG if (false) qDebug()
+# define QDTEDEBUGN if (false) qDebug
+#endif
+
+QT_BEGIN_NAMESPACE
+
+// --- QDateTimeEdit ---
+
+/*!
+ \class QDateTimeEdit
+ \brief The QDateTimeEdit class provides a widget for editing dates and times.
+
+ \ingroup basicwidgets
+ \mainclass
+
+ QDateTimeEdit allows the user to edit dates by using the keyboard or
+ the arrow keys to increase and decrease date and time values. The
+ arrow keys can be used to move from section to section within the
+ QDateTimeEdit box. Dates and times appear in accordance with the
+ format set; see setDisplayFormat().
+
+ \snippet doc/src/snippets/code/src_gui_widgets_qdatetimeedit.cpp 0
+
+ Here we've created a new QDateTimeEdit object initialized 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.
+
+ The minimum value for QDateTimeEdit is 14 September 1752,
+ and 2 January 4713BC for QDate. You can change this by calling
+ setMinimumDate(), setMaximumDate(), setMinimumTime(),
+ and setMaximumTime().
+
+ \section1 Using a Pop-up Calendar Widget
+
+ QDateTimeEdit can be configured to allow a QCalendarWidget to be used
+ to select dates. This is enabled by setting the calendarPopup property.
+ Additionally, you can supply a custom calendar widget for use as the
+ calendar pop-up by calling the setCalendarWidget() function. The existing
+ calendar widget can be retrieved with calendarWidget().
+
+ \table 100%
+ \row \o \inlineimage windowsxp-datetimeedit.png Screenshot of a Windows XP style date time editing widget
+ \o A date time editing widget shown in the \l{Windows XP Style Widget Gallery}{Windows XP widget style}.
+ \row \o \inlineimage macintosh-datetimeedit.png Screenshot of a Macintosh style date time editing widget
+ \o A date time editing widget shown in the \l{Macintosh Style Widget Gallery}{Macintosh widget style}.
+ \row \o \inlineimage plastique-datetimeedit.png Screenshot of a Plastique style date time editing widget
+ \o A date time editing widget shown in the \l{Plastique Style Widget Gallery}{Plastique widget style}.
+ \endtable
+
+ \sa QDateEdit, QTimeEdit, QDate, QTime
+*/
+
+/*!
+ \enum QDateTimeEdit::Section
+
+ \value NoSection
+ \value AmPmSection
+ \value MSecSection
+ \value SecondSection
+ \value MinuteSection
+ \value HourSection
+ \value DaySection
+ \value MonthSection
+ \value YearSection
+ \omitvalue DateSections_Mask
+ \omitvalue TimeSections_Mask
+*/
+
+/*!
+ \fn void QDateTimeEdit::dateTimeChanged(const QDateTime &datetime)
+
+ This signal is emitted whenever the date or time is changed. The
+ new date and time is passed in \a datetime.
+*/
+
+/*!
+ \fn void QDateTimeEdit::timeChanged(const QTime &time)
+
+ This signal is emitted whenever the time is changed. The new time
+ is passed in \a time.
+*/
+
+/*!
+ \fn void QDateTimeEdit::dateChanged(const QDate &date)
+
+ This signal is emitted whenever the date is changed. The new date
+ is passed in \a date.
+*/
+
+
+/*!
+ Constructs an empty date time editor with a \a parent.
+*/
+
+QDateTimeEdit::QDateTimeEdit(QWidget *parent)
+ : QAbstractSpinBox(*new QDateTimeEditPrivate, parent)
+{
+ Q_D(QDateTimeEdit);
+ d->init(QDateTime(QDATETIMEEDIT_DATE_INITIAL, QDATETIMEEDIT_TIME_MIN));
+}
+
+/*!
+ Constructs an empty date time editor with a \a parent. The value
+ is set to \a datetime.
+*/
+
+QDateTimeEdit::QDateTimeEdit(const QDateTime &datetime, QWidget *parent)
+ : QAbstractSpinBox(*new QDateTimeEditPrivate, parent)
+{
+ Q_D(QDateTimeEdit);
+ d->init(datetime.isValid() ? datetime : QDateTime(QDATETIMEEDIT_DATE_INITIAL,
+ QDATETIMEEDIT_TIME_MIN));
+}
+
+/*!
+ \fn QDateTimeEdit::QDateTimeEdit(const QDate &date, QWidget *parent)
+
+ Constructs an empty date time editor with a \a parent.
+ The value is set to \a date.
+*/
+
+QDateTimeEdit::QDateTimeEdit(const QDate &date, QWidget *parent)
+ : QAbstractSpinBox(*new QDateTimeEditPrivate, parent)
+{
+ Q_D(QDateTimeEdit);
+ d->init(date.isValid() ? date : QDATETIMEEDIT_DATE_INITIAL);
+}
+
+/*!
+ \fn QDateTimeEdit::QDateTimeEdit(const QTime &time, QWidget *parent)
+
+ Constructs an empty date time editor with a \a parent.
+ The value is set to \a time.
+*/
+
+QDateTimeEdit::QDateTimeEdit(const QTime &time, QWidget *parent)
+ : QAbstractSpinBox(*new QDateTimeEditPrivate, parent)
+{
+ Q_D(QDateTimeEdit);
+ d->init(time.isValid() ? time : QDATETIMEEDIT_TIME_MIN);
+}
+
+/*!
+ \internal
+*/
+
+QDateTimeEdit::QDateTimeEdit(const QVariant &var, QVariant::Type parserType, QWidget *parent)
+ : QAbstractSpinBox(*new QDateTimeEditPrivate, parent)
+{
+ Q_D(QDateTimeEdit);
+ d->parserType = parserType;
+ d->init(var);
+}
+
+/*!
+ \property QDateTimeEdit::dateTime
+ \brief the QDateTime that is set in the QDateTimeEdit
+
+ By default, this property contains a date that refers to January 1,
+ 2000 and a time of 00:00:00 and 0 milliseconds.
+
+ \sa date, time
+*/
+
+QDateTime QDateTimeEdit::dateTime() const
+{
+ Q_D(const QDateTimeEdit);
+ return d->value.toDateTime();
+}
+
+void QDateTimeEdit::setDateTime(const QDateTime &datetime)
+{
+ Q_D(QDateTimeEdit);
+ if (datetime.isValid()) {
+ d->clearCache();
+ if (!(d->sections & DateSections_Mask))
+ setDateRange(datetime.date(), datetime.date());
+ d->setValue(QVariant(datetime), EmitIfChanged);
+ }
+}
+
+/*!
+ \property QDateTimeEdit::date
+ \brief the QDate that is set in the QDateTimeEdit
+
+ By default, this property contains a date that refers to January 1, 2000.
+
+ \sa time, dateTime
+*/
+
+/*!
+ Returns the date of the date time edit.
+*/
+QDate QDateTimeEdit::date() const
+{
+ Q_D(const QDateTimeEdit);
+ return d->value.toDate();
+}
+
+void QDateTimeEdit::setDate(const QDate &date)
+{
+ Q_D(QDateTimeEdit);
+ if (date.isValid()) {
+ if (!(d->sections & DateSections_Mask))
+ setDateRange(date, date);
+
+ d->clearCache();
+ d->setValue(QDateTime(date, d->value.toTime(), d->spec), EmitIfChanged);
+ d->updateTimeSpec();
+ }
+}
+
+/*!
+ \property QDateTimeEdit::time
+ \brief the QTime that is set in the QDateTimeEdit
+
+ By default, this property contains a time of 00:00:00 and 0 milliseconds.
+
+ \sa date, dateTime
+*/
+
+/*!
+ Returns the time of the date time edit.
+*/
+QTime QDateTimeEdit::time() const
+{
+ Q_D(const QDateTimeEdit);
+ return d->value.toTime();
+}
+
+void QDateTimeEdit::setTime(const QTime &time)
+{
+ Q_D(QDateTimeEdit);
+ if (time.isValid()) {
+ d->clearCache();
+ d->setValue(QDateTime(d->value.toDate(), time, d->spec), EmitIfChanged);
+ }
+}
+
+
+/*!
+ \property QDateTimeEdit::minimumDateTime
+ \since 4.4
+
+ \brief the minimum datetime of the date time edit
+
+ When setting this property the \l maximumDateTime() is adjusted if
+ necessary to ensure that the range remains valid. If the datetime is
+ not a valid QDateTime object, this function does nothing.
+
+ The default minimumDateTime can be restored with
+ clearMinimumDateTime()
+
+ By default, this property contains a date that refers to September 14,
+ 1752 and a time of 00:00:00 and 0 milliseconds.
+
+ \sa maximumDateTime(), minimumTime(), maximumTime(), minimumDate(),
+ maximumDate(), setDateTimeRange(), setDateRange(), setTimeRange(),
+ clearMaximumDateTime(), clearMinimumDate(),
+ clearMaximumDate(), clearMinimumTime(), clearMaximumTime()
+*/
+
+QDateTime QDateTimeEdit::minimumDateTime() const
+{
+ Q_D(const QDateTimeEdit);
+ return d->minimum.toDateTime();
+}
+
+void QDateTimeEdit::clearMinimumDateTime()
+{
+ setMinimumDateTime(QDateTime(QDATETIMEEDIT_COMPAT_DATE_MIN, QDATETIMEEDIT_TIME_MIN));
+}
+
+void QDateTimeEdit::setMinimumDateTime(const QDateTime &dt)
+{
+ Q_D(QDateTimeEdit);
+ if (dt.isValid() && dt.date() >= QDATETIMEEDIT_DATE_MIN) {
+ const QDateTime m = dt.toTimeSpec(d->spec);
+ const QDateTime max = d->maximum.toDateTime();
+ d->setRange(m, (max > m ? max : m));
+ }
+}
+
+/*!
+ \property QDateTimeEdit::maximumDateTime
+ \since 4.4
+
+ \brief the maximum datetime of the date time edit
+
+ When setting this property the \l minimumDateTime() is adjusted if
+ necessary to ensure that the range remains valid. If the datetime is
+ not a valid QDateTime object, this function does nothing.
+
+ The default maximumDateTime can be restored with
+ clearMaximumDateTime().
+
+ By default, this property contains a date that refers to 31 December,
+ 7999 and a time of 23:59:59 and 999 milliseconds.
+
+ \sa minimumDateTime(), minimumTime(), maximumTime(), minimumDate(),
+ maximumDate(), setDateTimeRange(), setDateRange(), setTimeRange(),
+ clearMinimumDateTime(), clearMinimumDate(),
+ clearMaximumDate(), clearMinimumTime(), clearMaximumTime()
+*/
+
+QDateTime QDateTimeEdit::maximumDateTime() const
+{
+ Q_D(const QDateTimeEdit);
+ return d->maximum.toDateTime();
+}
+
+void QDateTimeEdit::clearMaximumDateTime()
+{
+ setMaximumDateTime(QDATETIMEEDIT_DATETIME_MAX);
+}
+
+void QDateTimeEdit::setMaximumDateTime(const QDateTime &dt)
+{
+ Q_D(QDateTimeEdit);
+ if (dt.isValid() && dt.date() <= QDATETIMEEDIT_DATE_MAX) {
+ const QDateTime m = dt.toTimeSpec(d->spec);
+ const QDateTime min = d->minimum.toDateTime();
+ d->setRange((min < m ? min : m), m);
+ }
+}
+
+
+/*!
+ Convenience function to set minimum and maximum date time with one
+ function call.
+ \since 4.4
+
+ \snippet doc/src/snippets/code/src_gui_widgets_qdatetimeedit.cpp 1
+
+ is analogous to:
+
+ \snippet doc/src/snippets/code/src_gui_widgets_qdatetimeedit.cpp 2
+
+ If either \a min or \a max are not valid, this function does
+ nothing.
+
+ \sa setMinimumDate(), maximumDate(), setMaximumDate(),
+ clearMinimumDate(), setMinimumTime(), maximumTime(),
+ setMaximumTime(), clearMinimumTime(), QDateTime::isValid()
+*/
+
+void QDateTimeEdit::setDateTimeRange(const QDateTime &min, const QDateTime &max)
+{
+ Q_D(QDateTimeEdit);
+ const QDateTime minimum = min.toTimeSpec(d->spec);
+ QDateTime maximum = max.toTimeSpec(d->spec);
+ if (min > max)
+ maximum = minimum;
+ d->setRange(minimum, maximum);
+}
+
+/*!
+ \property QDateTimeEdit::minimumDate
+
+ \brief the minimum date of the date time edit
+
+ When setting this property the \l maximumDate is adjusted if
+ necessary, to ensure that the range remains valid. If the date is
+ not a valid QDate object, this function does nothing.
+
+ By default, this property contains a date that refers to September 14, 1752.
+ The minimum date must be at least the first day in year 100, otherwise
+ setMinimumDate() has no effect.
+
+ \sa minimumTime(), maximumTime(), setDateRange()
+*/
+
+QDate QDateTimeEdit::minimumDate() const
+{
+ Q_D(const QDateTimeEdit);
+ return d->minimum.toDate();
+}
+
+void QDateTimeEdit::setMinimumDate(const QDate &min)
+{
+ Q_D(QDateTimeEdit);
+ if (min.isValid() && min >= QDATETIMEEDIT_DATE_MIN) {
+ setMinimumDateTime(QDateTime(min, d->minimum.toTime(), d->spec));
+ }
+}
+
+void QDateTimeEdit::clearMinimumDate()
+{
+ setMinimumDate(QDATETIMEEDIT_COMPAT_DATE_MIN);
+}
+
+/*!
+ \property QDateTimeEdit::maximumDate
+
+ \brief the maximum date of the date time edit
+
+ When setting this property the \l minimumDate is adjusted if
+ necessary to ensure that the range remains valid. If the date is
+ not a valid QDate object, this function does nothing.
+
+ By default, this property contains a date that refers to December 31, 7999.
+
+ \sa minimumDate, minimumTime, maximumTime, setDateRange()
+*/
+
+QDate QDateTimeEdit::maximumDate() const
+{
+ Q_D(const QDateTimeEdit);
+ return d->maximum.toDate();
+}
+
+void QDateTimeEdit::setMaximumDate(const QDate &max)
+{
+ Q_D(QDateTimeEdit);
+ if (max.isValid()) {
+ setMaximumDateTime(QDateTime(max, d->maximum.toTime(), d->spec));
+ }
+}
+
+void QDateTimeEdit::clearMaximumDate()
+{
+ setMaximumDate(QDATETIMEEDIT_DATE_MAX);
+}
+
+/*!
+ \property QDateTimeEdit::minimumTime
+
+ \brief the minimum time of the date time edit
+
+ When setting this property the \l maximumTime is adjusted if
+ necessary, to ensure that the range remains valid. If the time is
+ not a valid QTime object, this function does nothing.
+
+ By default, this property contains a time of 00:00:00 and 0 milliseconds.
+
+ \sa maximumTime, minimumDate, maximumDate, setTimeRange()
+*/
+
+QTime QDateTimeEdit::minimumTime() const
+{
+ Q_D(const QDateTimeEdit);
+ return d->minimum.toTime();
+}
+
+void QDateTimeEdit::setMinimumTime(const QTime &min)
+{
+ Q_D(QDateTimeEdit);
+ if (min.isValid()) {
+ const QDateTime m(d->minimum.toDate(), min, d->spec);
+ setMinimumDateTime(m);
+ }
+}
+
+void QDateTimeEdit::clearMinimumTime()
+{
+ setMinimumTime(QDATETIMEEDIT_TIME_MIN);
+}
+
+/*!
+ \property QDateTimeEdit::maximumTime
+
+ \brief the maximum time of the date time edit
+
+ When setting this property, the \l minimumTime is adjusted if
+ necessary to ensure that the range remains valid. If the time is
+ not a valid QTime object, this function does nothing.
+
+ By default, this property contains a time of 23:59:59 and 999 milliseconds.
+
+ \sa minimumTime, minimumDate, maximumDate, setTimeRange()
+*/
+QTime QDateTimeEdit::maximumTime() const
+{
+ Q_D(const QDateTimeEdit);
+ return d->maximum.toTime();
+}
+
+void QDateTimeEdit::setMaximumTime(const QTime &max)
+{
+ Q_D(QDateTimeEdit);
+ if (max.isValid()) {
+ const QDateTime m(d->maximum.toDate(), max);
+ setMaximumDateTime(m);
+ }
+}
+
+void QDateTimeEdit::clearMaximumTime()
+{
+ setMaximumTime(QDATETIMEEDIT_TIME_MAX);
+}
+
+/*!
+ Convenience function to set minimum and maximum date with one
+ function call.
+
+ \snippet doc/src/snippets/code/src_gui_widgets_qdatetimeedit.cpp 3
+
+ is analogous to:
+
+ \snippet doc/src/snippets/code/src_gui_widgets_qdatetimeedit.cpp 4
+
+ If either \a min or \a max are not valid, this function does
+ nothing.
+
+ \sa setMinimumDate(), maximumDate(), setMaximumDate(),
+ clearMinimumDate(), setMinimumTime(), maximumTime(),
+ setMaximumTime(), clearMinimumTime(), QDate::isValid()
+*/
+
+void QDateTimeEdit::setDateRange(const QDate &min, const QDate &max)
+{
+ Q_D(QDateTimeEdit);
+ if (min.isValid() && max.isValid()) {
+ setDateTimeRange(QDateTime(min, d->minimum.toTime(), d->spec),
+ QDateTime(max, d->maximum.toTime(), d->spec));
+ }
+}
+
+/*!
+ Convenience function to set minimum and maximum time with one
+ function call.
+
+ \snippet doc/src/snippets/code/src_gui_widgets_qdatetimeedit.cpp 5
+
+ is analogous to:
+
+ \snippet doc/src/snippets/code/src_gui_widgets_qdatetimeedit.cpp 6
+
+ If either \a min or \a max are not valid, this function does
+ nothing.
+
+ \sa setMinimumDate(), maximumDate(), setMaximumDate(),
+ clearMinimumDate(), setMinimumTime(), maximumTime(),
+ setMaximumTime(), clearMinimumTime(), QTime::isValid()
+*/
+
+void QDateTimeEdit::setTimeRange(const QTime &min, const QTime &max)
+{
+ Q_D(QDateTimeEdit);
+ if (min.isValid() && max.isValid()) {
+ setDateTimeRange(QDateTime(d->minimum.toDate(), min, d->spec),
+ QDateTime(d->maximum.toDate(), max, d->spec));
+ }
+}
+
+/*!
+ \property QDateTimeEdit::displayedSections
+
+ \brief the currently displayed fields of the date time edit
+
+ Returns a bit set of the displayed sections for this format.
+ \a setDisplayFormat(), displayFormat()
+*/
+
+QDateTimeEdit::Sections QDateTimeEdit::displayedSections() const
+{
+ Q_D(const QDateTimeEdit);
+ return d->sections;
+}
+
+/*!
+ \property QDateTimeEdit::currentSection
+
+ \brief the current section of the spinbox
+ \a setCurrentSection()
+*/
+
+QDateTimeEdit::Section QDateTimeEdit::currentSection() const
+{
+ Q_D(const QDateTimeEdit);
+#ifdef QT_KEYPAD_NAVIGATION
+ if (QApplication::keypadNavigationEnabled() && d->focusOnButton)
+ return NoSection;
+#endif
+ return d->convertToPublic(d->sectionType(d->currentSectionIndex));
+}
+
+void QDateTimeEdit::setCurrentSection(Section section)
+{
+ Q_D(QDateTimeEdit);
+ if (section == NoSection || !(section & d->sections))
+ return;
+
+ d->updateCache(d->value, d->displayText());
+ const int size = d->sectionNodes.size();
+ int index = d->currentSectionIndex + 1;
+ for (int i=0; i<2; ++i) {
+ while (index < size) {
+ if (d->convertToPublic(d->sectionType(index)) == section) {
+ d->edit->setCursorPosition(d->sectionPos(index));
+ QDTEDEBUG << d->sectionPos(index);
+ return;
+ }
+ ++index;
+ }
+ index = 0;
+ }
+}
+
+/*!
+ \since 4.3
+
+ Returns the Section at \a index.
+
+ If the format is 'yyyy/MM/dd', sectionAt(0) returns YearSection,
+ sectionAt(1) returns MonthSection, and sectionAt(2) returns
+ YearSection,
+*/
+
+QDateTimeEdit::Section QDateTimeEdit::sectionAt(int index) const
+{
+ Q_D(const QDateTimeEdit);
+ if (index < 0 || index >= d->sectionNodes.size())
+ return NoSection;
+ return d->convertToPublic(d->sectionType(index));
+}
+
+/*!
+ \since 4.3
+
+ \property QDateTimeEdit::sectionCount
+
+ \brief the number of sections displayed.
+ If the format is 'yyyy/yy/yyyy', sectionCount returns 3
+*/
+
+int QDateTimeEdit::sectionCount() const
+{
+ Q_D(const QDateTimeEdit);
+ return d->sectionNodes.size();
+}
+
+
+/*!
+ \since 4.3
+
+ \property QDateTimeEdit::currentSectionIndex
+
+ \brief the current section index of the spinbox
+
+ If the format is 'yyyy/MM/dd', the displayText is '2001/05/21' and
+ the cursorPosition is 5 currentSectionIndex returns 1. If the
+ cursorPosition is 3 currentSectionIndex is 0 etc.
+
+ \a setCurrentSection()
+ \sa currentSection()
+*/
+
+int QDateTimeEdit::currentSectionIndex() const
+{
+ Q_D(const QDateTimeEdit);
+ return d->currentSectionIndex;
+}
+
+void QDateTimeEdit::setCurrentSectionIndex(int index)
+{
+ Q_D(QDateTimeEdit);
+ if (index < 0 || index >= d->sectionNodes.size())
+ return;
+ d->edit->setCursorPosition(d->sectionPos(index));
+}
+
+/*!
+ \since 4.4
+
+ \brief Returns the calendar widget for the editor if calendarPopup is
+ set to true and (sections() & DateSections_Mask) != 0.
+
+ This function creates and returns a calendar widget if none has been set.
+*/
+
+
+QCalendarWidget *QDateTimeEdit::calendarWidget() const
+{
+ Q_D(const QDateTimeEdit);
+ if (!d->calendarPopup || !(d->sections & QDateTimeParser::DateSectionMask))
+ return 0;
+ if (!d->monthCalendar) {
+ const_cast<QDateTimeEditPrivate*>(d)->initCalendarPopup();
+ }
+ return d->monthCalendar->calendarWidget();
+}
+
+/*!
+ \since 4.4
+
+ Sets the given \a calendarWidget as the widget to be used for the calendar
+ pop-up. The editor does not automatically take ownership of the calendar widget.
+
+ \sa calendarPopup
+*/
+void QDateTimeEdit::setCalendarWidget(QCalendarWidget *calendarWidget)
+{
+ Q_D(QDateTimeEdit);
+ if (!calendarWidget) {
+ qWarning("QDateTimeEdit::setCalendarWidget: Cannot set a null calendar widget");
+ return;
+ }
+
+ if (!d->calendarPopup) {
+ qWarning("QDateTimeEdit::setCalendarWidget: calendarPopup is set to false");
+ return;
+ }
+
+ if (!(d->display & QDateTimeParser::DateSectionMask)) {
+ qWarning("QDateTimeEdit::setCalendarWidget: no date sections specified");
+ return;
+ }
+ d->initCalendarPopup(calendarWidget);
+}
+
+
+/*!
+ \since 4.2
+
+ Selects \a section. If \a section doesn't exist in the currently
+ displayed sections this function does nothing. If \a section is
+ NoSection this function will unselect all text in the editor.
+ Otherwise this function will move the cursor and the current section
+ to the selected section.
+
+ \sa currentSection()
+*/
+
+void QDateTimeEdit::setSelectedSection(Section section)
+{
+ Q_D(QDateTimeEdit);
+ if (section == NoSection) {
+ d->edit->setSelection(d->edit->cursorPosition(), 0);
+ } else if (section & d->sections) {
+ if (currentSection() != section)
+ setCurrentSection(section);
+ d->setSelected(d->currentSectionIndex);
+ }
+}
+
+
+
+/*!
+ \fn QString QDateTimeEdit::sectionText(Section section) const
+
+ Returns the text from the given \a section.
+
+ \sa currentSection()
+*/
+
+QString QDateTimeEdit::sectionText(Section section) const
+{
+ Q_D(const QDateTimeEdit);
+ if (section == QDateTimeEdit::NoSection || !(section & d->sections)) {
+ return QString();
+ }
+
+ d->updateCache(d->value, d->displayText());
+ const int sectionIndex = d->absoluteIndex(section, 0);
+ return d->sectionText(sectionIndex);
+}
+
+/*!
+ \property QDateTimeEdit::displayFormat
+
+ \brief the format used to display the time/date of the date time edit
+
+ This format is the same as the one used described in QDateTime::toString()
+ and QDateTime::fromString()
+
+ Example format strings(assuming that the date is 2nd of July 1969):
+
+ \table
+ \header \i Format \i Result
+ \row \i dd.MM.yyyy \i 02.07.1969
+ \row \i MMM d yy \i Jul 2 69
+ \row \i MMMM d yy \i July 2 69
+ \endtable
+
+ Note that if you specify a two digit year, it will be interpreted
+ to be in the century in which the date time edit was initialized.
+ The default century is the 21 (2000-2099).
+
+ If you specify an invalid format the format will not be set.
+
+ \sa QDateTime::toString(), displayedSections()
+*/
+
+QString QDateTimeEdit::displayFormat() const
+{
+ Q_D(const QDateTimeEdit);
+ return isRightToLeft() ? d->unreversedFormat : d->displayFormat;
+}
+
+template<typename C> static inline C reverse(const C &l)
+{
+ C ret;
+ for (int i=l.size() - 1; i>=0; --i)
+ ret.append(l.at(i));
+ return ret;
+}
+
+void QDateTimeEdit::setDisplayFormat(const QString &format)
+{
+ Q_D(QDateTimeEdit);
+ if (d->parseFormat(format)) {
+ d->unreversedFormat.clear();
+ if (isRightToLeft()) {
+ d->unreversedFormat = format;
+ d->displayFormat.clear();
+ for (int i=d->sectionNodes.size() - 1; i>=0; --i) {
+ d->displayFormat += d->separators.at(i + 1);
+ d->displayFormat += d->sectionFormat(i);
+ }
+ d->displayFormat += d->separators.at(0);
+ d->separators = reverse(d->separators);
+ d->sectionNodes = reverse(d->sectionNodes);
+ }
+
+ d->formatExplicitlySet = true;
+ d->sections = d->convertSections(d->display);
+ d->clearCache();
+
+ d->currentSectionIndex = qMin(d->currentSectionIndex, d->sectionNodes.size() - 1);
+ const bool timeShown = (d->sections & TimeSections_Mask);
+ const bool dateShown = (d->sections & DateSections_Mask);
+ Q_ASSERT(dateShown || timeShown);
+ if (timeShown && !dateShown) {
+ setDateRange(d->value.toDate(), d->value.toDate());
+ } else if (dateShown && !timeShown) {
+ setTimeRange(QDATETIMEEDIT_TIME_MIN, QDATETIMEEDIT_TIME_MAX);
+ d->value = QDateTime(d->value.toDate(), QTime(), d->spec);
+ }
+ d->updateEdit();
+ d->_q_editorCursorPositionChanged(-1, 0);
+ }
+}
+
+/*!
+ \property QDateTimeEdit::calendarPopup
+ \brief the current calender pop-up showing mode.
+ \since 4.2
+
+ The calendar pop-up will be shown upon clicking the arrow button.
+ This property is valid only if there is a valid date display format.
+
+ \sa setDisplayFormat()
+*/
+
+bool QDateTimeEdit::calendarPopup() const
+{
+ Q_D(const QDateTimeEdit);
+ return d->calendarPopup;
+}
+
+void QDateTimeEdit::setCalendarPopup(bool enable)
+{
+ Q_D(QDateTimeEdit);
+ if (enable == d->calendarPopup)
+ return;
+ setAttribute(Qt::WA_MacShowFocusRect, !enable);
+ d->calendarPopup = enable;
+#ifdef QT_KEYPAD_NAVIGATION
+ if (!enable)
+ d->focusOnButton = false;
+#endif
+ d->updateEditFieldGeometry();
+ update();
+}
+
+/*!
+ \property QDateTimeEdit::timeSpec
+ \brief the current timespec used by the date time edit.
+ \since 4.4
+
+ All dates/passed to the date time edit will be converted to this
+ timespec.
+*/
+
+Qt::TimeSpec QDateTimeEdit::timeSpec() const
+{
+ Q_D(const QDateTimeEdit);
+ return d->spec;
+}
+
+void QDateTimeEdit::setTimeSpec(Qt::TimeSpec spec)
+{
+ Q_D(QDateTimeEdit);
+ if (spec != d->spec) {
+ d->spec = spec;
+ d->updateTimeSpec();
+ }
+}
+
+/*!
+ \reimp
+*/
+
+QSize QDateTimeEdit::sizeHint() const
+{
+ Q_D(const QDateTimeEdit);
+ if (d->cachedSizeHint.isEmpty()) {
+ ensurePolished();
+
+ const QFontMetrics fm(fontMetrics());
+ int h = d->edit->sizeHint().height();
+ int w = 0;
+ QString s;
+ s = d->textFromValue(d->minimum) + QLatin1String(" ");
+ w = qMax<int>(w, fm.width(s));
+ s = d->textFromValue(d->maximum) + QLatin1String(" ");
+ w = qMax<int>(w, fm.width(s));
+ if (d->specialValueText.size()) {
+ s = d->specialValueText;
+ w = qMax<int>(w, fm.width(s));
+ }
+ w += 2; // cursor blinking space
+
+ QSize hint(w, h);
+
+#ifdef Q_WS_MAC
+ if (d->calendarPopupEnabled()) {
+ QStyleOptionComboBox opt;
+ d->cachedSizeHint = style()->sizeFromContents(QStyle::CT_ComboBox, &opt, hint, this);
+ } else {
+#else
+ {
+#endif
+ QSize extra(35, 6);
+ QStyleOptionSpinBox opt;
+ initStyleOption(&opt);
+ opt.rect.setSize(hint + extra);
+ extra += hint - style()->subControlRect(QStyle::CC_SpinBox, &opt,
+ QStyle::SC_SpinBoxEditField, this).size();
+ // get closer to final result by repeating the calculation
+ opt.rect.setSize(hint + extra);
+ extra += hint - style()->subControlRect(QStyle::CC_SpinBox, &opt,
+ QStyle::SC_SpinBoxEditField, this).size();
+ hint += extra;
+
+ opt.rect = rect();
+ d->cachedSizeHint = style()->sizeFromContents(QStyle::CT_SpinBox, &opt, hint, this)
+ .expandedTo(QApplication::globalStrut());
+ }
+
+ d->cachedMinimumSizeHint = d->cachedSizeHint;
+ // essentially make minimumSizeHint return the same as sizeHint for datetimeedits
+ }
+ return d->cachedSizeHint;
+}
+
+/*!
+ \reimp
+*/
+
+bool QDateTimeEdit::event(QEvent *event)
+{
+ Q_D(QDateTimeEdit);
+ switch (event->type()) {
+ case QEvent::ApplicationLayoutDirectionChange: {
+ const bool was = d->formatExplicitlySet;
+ const QString oldFormat = d->displayFormat;
+ d->displayFormat.clear();
+ setDisplayFormat(oldFormat);
+ d->formatExplicitlySet = was;
+ break; }
+ case QEvent::LocaleChange:
+ d->updateEdit();
+ break;
+ case QEvent::StyleChange:
+#ifdef Q_WS_MAC
+ case QEvent::MacSizeChange:
+#endif
+ d->setLayoutItemMargins(QStyle::SE_DateTimeEditLayoutItem);
+ break;
+ default:
+ break;
+ }
+ return QAbstractSpinBox::event(event);
+}
+
+/*!
+ \reimp
+*/
+
+void QDateTimeEdit::clear()
+{
+ Q_D(QDateTimeEdit);
+ d->clearSection(d->currentSectionIndex);
+}
+/*!
+ \reimp
+*/
+
+void QDateTimeEdit::keyPressEvent(QKeyEvent *event)
+{
+ Q_D(QDateTimeEdit);
+ int oldCurrent = d->currentSectionIndex;
+ bool select = true;
+ bool inserted = false;
+
+ switch (event->key()) {
+#ifdef QT_KEYPAD_NAVIGATION
+ case Qt::Key_NumberSign: //shortcut to popup calendar
+ if (QApplication::keypadNavigationEnabled() && d->calendarPopupEnabled()) {
+ d->initCalendarPopup();
+ d->positionCalendarPopup();
+ d->monthCalendar->show();
+ return;
+ }
+ break;
+ case Qt::Key_Select:
+ if (QApplication::keypadNavigationEnabled()) {
+ if (hasEditFocus()) {
+ if (d->focusOnButton) {
+ d->initCalendarPopup();
+ d->positionCalendarPopup();
+ d->monthCalendar->show();
+ d->focusOnButton = false;
+ return;
+ }
+ setEditFocus(false);
+ selectAll();
+ } else {
+ setEditFocus(true);
+
+ //hide cursor
+ d->edit->d_func()->setCursorVisible(false);
+ if (d->edit->d_func()->cursorTimer > 0)
+ killTimer(d->edit->d_func()->cursorTimer);
+ d->edit->d_func()->cursorTimer = 0;
+
+ d->setSelected(0);
+ }
+ }
+ return;
+#endif
+ case Qt::Key_Enter:
+ case Qt::Key_Return:
+ d->interpret(AlwaysEmit);
+ d->setSelected(d->currentSectionIndex, true);
+ event->ignore();
+ emit editingFinished();
+ return;
+ default:
+#ifdef QT_KEYPAD_NAVIGATION
+ if (QApplication::keypadNavigationEnabled() && !hasEditFocus()
+ && !event->text().isEmpty() && event->text().at(0).isLetterOrNumber()) {
+ setEditFocus(true);
+
+ //hide cursor
+ d->edit->d_func()->setCursorVisible(false);
+ if (d->edit->d_func()->cursorTimer > 0)
+ killTimer(d->edit->d_func()->cursorTimer);
+ d->edit->d_func()->cursorTimer = 0;
+
+ d->setSelected(0);
+ oldCurrent = 0;
+ }
+#endif
+ if (!d->isSeparatorKey(event)) {
+ inserted = select = !event->text().isEmpty() && event->text().at(0).isPrint()
+ && !(event->modifiers() & ~(Qt::ShiftModifier|Qt::KeypadModifier));
+ break;
+ }
+ case Qt::Key_Left:
+ case Qt::Key_Right:
+ if (event->key() == Qt::Key_Left || event->key() == Qt::Key_Right) {
+#ifdef QT_KEYPAD_NAVIGATION
+ if (!QApplication::keypadNavigationEnabled() || !hasEditFocus()) {
+ select = false;
+ break;
+ }
+#else
+ if (!(event->modifiers() & Qt::ControlModifier)) {
+ select = false;
+ break;
+ }
+#ifdef Q_WS_MAC
+ else {
+ select = (event->modifiers() & Qt::ShiftModifier);
+ break;
+ }
+#endif
+#endif // QT_KEYPAD_NAVIGATION
+ }
+ // else fall through
+ case Qt::Key_Backtab:
+ case Qt::Key_Tab: {
+ event->accept();
+ if (d->specialValue()) {
+ d->edit->setSelection(d->edit->cursorPosition(), 0);
+ return;
+ }
+ const bool forward = event->key() != Qt::Key_Left && event->key() != Qt::Key_Backtab
+ && (event->key() != Qt::Key_Tab || !(event->modifiers() & Qt::ShiftModifier));
+#ifdef QT_KEYPAD_NAVIGATION
+ int newSection = d->nextPrevSection(d->currentSectionIndex, forward);
+ if (QApplication::keypadNavigationEnabled()) {
+ if (d->focusOnButton) {
+ newSection = forward ? 0 : d->sectionNodes.size() - 1;
+ d->focusOnButton = false;
+ update();
+ } else if (newSection < 0 && select && d->calendarPopupEnabled()) {
+ setSelectedSection(NoSection);
+ d->focusOnButton = true;
+ update();
+ return;
+ }
+ }
+ // only allow date/time sections to be selected.
+ if (newSection & ~(QDateTimeParser::TimeSectionMask | QDateTimeParser::DateSectionMask))
+ return;
+#endif
+ //key tab and backtab will be managed thrgout QWidget::event
+ if (event->key() != Qt::Key_Backtab && event->key() != Qt::Key_Tab)
+ focusNextPrevChild(forward);
+
+ return; }
+ }
+ QAbstractSpinBox::keyPressEvent(event);
+ if (select && !(event->modifiers() & Qt::ShiftModifier) && !d->edit->hasSelectedText()) {
+ if (inserted && d->sectionAt(d->edit->cursorPosition()) == QDateTimeParser::NoSectionIndex) {
+ QString str = d->displayText();
+ int pos = d->edit->cursorPosition();
+ if (validate(str, pos) == QValidator::Acceptable
+ && (d->sectionNodes.at(oldCurrent).count != 1
+ || d->sectionMaxSize(oldCurrent) == d->sectionSize(oldCurrent)
+ || d->skipToNextSection(oldCurrent, d->value.toDateTime(), d->sectionText(oldCurrent)))) {
+ QDTEDEBUG << "Setting currentsection to"
+ << d->closestSection(d->edit->cursorPosition(), true) << event->key()
+ << oldCurrent << str;
+ const int tmp = d->closestSection(d->edit->cursorPosition(), true);
+ if (tmp >= 0)
+ d->currentSectionIndex = tmp;
+ }
+ }
+ if (d->currentSectionIndex != oldCurrent) {
+ d->setSelected(d->currentSectionIndex);
+ }
+ }
+ if (d->specialValue()) {
+ d->edit->setSelection(d->edit->cursorPosition(), 0);
+ }
+}
+
+/*!
+ \reimp
+*/
+
+#ifndef QT_NO_WHEELEVENT
+void QDateTimeEdit::wheelEvent(QWheelEvent *event)
+{
+ QAbstractSpinBox::wheelEvent(event);
+}
+#endif
+
+/*!
+ \reimp
+*/
+
+void QDateTimeEdit::focusInEvent(QFocusEvent *event)
+{
+ Q_D(QDateTimeEdit);
+ QAbstractSpinBox::focusInEvent(event);
+ QString *frm = 0;
+ const int oldPos = d->edit->cursorPosition();
+ if (!d->formatExplicitlySet) {
+ if (d->displayFormat == d->defaultTimeFormat) {
+ frm = &d->defaultTimeFormat;
+ } else if (d->displayFormat == d->defaultDateFormat) {
+ frm = &d->defaultDateFormat;
+ } else if (d->displayFormat == d->defaultDateTimeFormat) {
+ frm = &d->defaultDateTimeFormat;
+ }
+
+ if (frm) {
+ d->readLocaleSettings();
+ if (d->displayFormat != *frm) {
+ setDisplayFormat(*frm);
+ d->formatExplicitlySet = false;
+ d->edit->setCursorPosition(oldPos);
+ }
+ }
+ }
+ const bool oldHasHadFocus = d->hasHadFocus;
+ d->hasHadFocus = true;
+ bool first = true;
+ switch (event->reason()) {
+ case Qt::BacktabFocusReason:
+ first = false;
+ break;
+ case Qt::MouseFocusReason:
+ case Qt::PopupFocusReason:
+ return;
+ case Qt::ActiveWindowFocusReason:
+ if (oldHasHadFocus)
+ return;
+ case Qt::ShortcutFocusReason:
+ case Qt::TabFocusReason:
+ default:
+ break;
+ }
+ if (isRightToLeft())
+ first = !first;
+ d->updateEdit(); // needed to make it update specialValueText
+
+ d->setSelected(first ? 0 : d->sectionNodes.size() - 1);
+}
+
+/*!
+ \reimp
+*/
+
+bool QDateTimeEdit::focusNextPrevChild(bool next)
+{
+ Q_D(QDateTimeEdit);
+ const int newSection = d->nextPrevSection(d->currentSectionIndex, next);
+ switch (d->sectionType(newSection)) {
+ case QDateTimeParser::NoSection:
+ case QDateTimeParser::FirstSection:
+ case QDateTimeParser::LastSection:
+ return QAbstractSpinBox::focusNextPrevChild(next);
+ default:
+ d->edit->deselect();
+ d->edit->setCursorPosition(d->sectionPos(newSection));
+ QDTEDEBUG << d->sectionPos(newSection);
+ d->setSelected(newSection, true);
+ return false;
+ }
+}
+
+/*!
+ \reimp
+*/
+
+void QDateTimeEdit::stepBy(int steps)
+{
+ Q_D(QDateTimeEdit);
+#ifdef QT_KEYPAD_NAVIGATION
+ // with keypad navigation and not editFocus, left right change the date/time by a fixed amount.
+ if (QApplication::keypadNavigationEnabled() && !hasEditFocus()) {
+ // if date based, shift by day. else shift by 15min
+ if (d->sections & DateSections_Mask) {
+ setDateTime(dateTime().addDays(steps));
+ } else {
+ int minutes = time().hour()*60 + time().minute();
+ int blocks = minutes/15;
+ blocks += steps;
+ /* rounding involved */
+ if (minutes % 15) {
+ if (steps < 0) {
+ blocks += 1; // do one less step;
+ }
+ }
+
+ minutes = blocks * 15;
+
+ /* need to take wrapping into account */
+ if (!d->wrapping) {
+ int max_minutes = d->maximum.toTime().hour()*60 + d->maximum.toTime().minute();
+ int min_minutes = d->minimum.toTime().hour()*60 + d->minimum.toTime().minute();
+
+ if (minutes >= max_minutes) {
+ setTime(maximumTime());
+ return;
+ } else if (minutes <= min_minutes) {
+ setTime(minimumTime());
+ return;
+ }
+ }
+ setTime(QTime(minutes/60, minutes%60));
+ }
+ return;
+ }
+#endif
+ // don't optimize away steps == 0. This is the only way to select
+ // the currentSection in Qt 4.1.x
+ if (d->specialValue() && displayedSections() != AmPmSection) {
+ for (int i=0; i<d->sectionNodes.size(); ++i) {
+ if (d->sectionType(i) != QDateTimeParser::AmPmSection) {
+ d->currentSectionIndex = i;
+ break;
+ }
+ }
+ }
+ d->setValue(d->stepBy(d->currentSectionIndex, steps, false), EmitIfChanged);
+ d->updateCache(d->value, d->displayText());
+
+ d->setSelected(d->currentSectionIndex);
+ d->updateTimeSpec();
+}
+
+/*!
+ This virtual function is used by the date time edit whenever it
+ needs to display \a dateTime.
+
+ If you reimplement this, you may also need to reimplement validate().
+
+ \sa dateTimeFromText(), validate()
+*/
+QString QDateTimeEdit::textFromDateTime(const QDateTime &dateTime) const
+{
+ Q_D(const QDateTimeEdit);
+ return locale().toString(dateTime, d->displayFormat);
+}
+
+
+/*!
+ Returns an appropriate datetime for the given \a text.
+
+ This virtual function is used by the datetime edit whenever it
+ needs to interpret text entered by the user as a value.
+
+ \sa textFromDateTime(), validate()
+*/
+QDateTime QDateTimeEdit::dateTimeFromText(const QString &text) const
+{
+ Q_D(const QDateTimeEdit);
+ QString copy = text;
+ int pos = d->edit->cursorPosition();
+ QValidator::State state = QValidator::Acceptable;
+ return d->validateAndInterpret(copy, pos, state);
+}
+
+/*!
+ \reimp
+*/
+
+QValidator::State QDateTimeEdit::validate(QString &text, int &pos) const
+{
+ Q_D(const QDateTimeEdit);
+ QValidator::State state;
+ d->validateAndInterpret(text, pos, state);
+ return state;
+}
+
+/*!
+ \reimp
+*/
+
+
+void QDateTimeEdit::fixup(QString &input) const
+{
+ Q_D(const QDateTimeEdit);
+ QValidator::State state;
+ int copy = d->edit->cursorPosition();
+
+ d->validateAndInterpret(input, copy, state, true);
+}
+
+
+/*!
+ \reimp
+*/
+
+QDateTimeEdit::StepEnabled QDateTimeEdit::stepEnabled() const
+{
+ Q_D(const QDateTimeEdit);
+ if (d->readOnly)
+ return StepEnabled(0);
+ if (d->specialValue()) {
+ return (d->minimum == d->maximum ? StepEnabled(0) : StepEnabled(StepUpEnabled));
+ }
+
+ QAbstractSpinBox::StepEnabled ret = 0;
+
+#ifdef QT_KEYPAD_NAVIGATION
+ if (QApplication::keypadNavigationEnabled() && !hasEditFocus()) {
+ if (d->wrapping)
+ return StepEnabled(StepUpEnabled | StepDownEnabled);
+ // 3 cases. date, time, datetime. each case look
+ // at just the relavant component.
+ QVariant max, min, val;
+ if (!(d->sections & DateSections_Mask)) {
+ // time only, no date
+ max = d->maximum.toTime();
+ min = d->minimum.toTime();
+ val = d->value.toTime();
+ } else if (!(d->sections & TimeSections_Mask)) {
+ // date only, no time
+ max = d->maximum.toDate();
+ min = d->minimum.toDate();
+ val = d->value.toDate();
+ } else {
+ // both
+ max = d->maximum;
+ min = d->minimum;
+ val = d->value;
+ }
+ if (val != min)
+ ret |= QAbstractSpinBox::StepDownEnabled;
+ if (val != max)
+ ret |= QAbstractSpinBox::StepUpEnabled;
+ return ret;
+ }
+#endif
+ switch (d->sectionType(d->currentSectionIndex)) {
+ case QDateTimeParser::NoSection:
+ case QDateTimeParser::FirstSection:
+ case QDateTimeParser::LastSection: return 0;
+ default: break;
+ }
+ if (d->wrapping)
+ return StepEnabled(StepDownEnabled|StepUpEnabled);
+
+ QVariant v = d->stepBy(d->currentSectionIndex, 1, true);
+ if (v != d->value) {
+ ret |= QAbstractSpinBox::StepUpEnabled;
+ }
+ v = d->stepBy(d->currentSectionIndex, -1, true);
+ if (v != d->value) {
+ ret |= QAbstractSpinBox::StepDownEnabled;
+ }
+
+ return ret;
+}
+
+
+/*!
+ \reimp
+*/
+
+void QDateTimeEdit::mousePressEvent(QMouseEvent *event)
+{
+ Q_D(QDateTimeEdit);
+ if (!d->calendarPopupEnabled()) {
+ QAbstractSpinBox::mousePressEvent(event);
+ return;
+ }
+ d->updateHoverControl(event->pos());
+ if (d->hoverControl == QStyle::SC_ComboBoxArrow) {
+ event->accept();
+ if (d->readOnly) {
+ return;
+ }
+ d->updateArrow(QStyle::State_Sunken);
+ d->initCalendarPopup();
+ d->positionCalendarPopup();
+ //Show the calendar
+ d->monthCalendar->show();
+ } else {
+ QAbstractSpinBox::mousePressEvent(event);
+ }
+}
+
+/*!
+ \class QTimeEdit
+ \brief The QTimeEdit class provides a widget for editing times based on
+ the QDateTimeEdit widget.
+
+ \ingroup basicwidgets
+ \mainclass
+
+ Many of the properties and functions provided by QTimeEdit are implemented in
+ QDateTimeEdit. The following properties are most relevant to users of this
+ class:
+
+ \list
+ \o \l{QDateTimeEdit::time}{time} holds the date displayed by the widget.
+ \o \l{QDateTimeEdit::minimumTime}{minimumTime} defines the minimum (earliest) time
+ that can be set by the user.
+ \o \l{QDateTimeEdit::maximumTime}{maximumTime} defines the maximum (latest) time
+ that can be set by the user.
+ \o \l{QDateTimeEdit::displayFormat}{displayFormat} contains a string that is used
+ to format the time displayed in the widget.
+ \endlist
+
+ \table 100%
+ \row \o \inlineimage windowsxp-timeedit.png Screenshot of a Windows XP style time editing widget
+ \o A time editing widget shown in the \l{Windows XP Style Widget Gallery}{Windows XP widget style}.
+ \row \o \inlineimage macintosh-timeedit.png Screenshot of a Macintosh style time editing widget
+ \o A time editing widget shown in the \l{Macintosh Style Widget Gallery}{Macintosh widget style}.
+ \row \o \inlineimage plastique-timeedit.png Screenshot of a Plastique style time editing widget
+ \o A time editing widget shown in the \l{Plastique Style Widget Gallery}{Plastique widget style}.
+ \endtable
+
+ \sa QDateEdit, QDateTimeEdit
+*/
+
+/*!
+ Constructs an empty time editor with a \a parent.
+*/
+
+
+QTimeEdit::QTimeEdit(QWidget *parent)
+ : QDateTimeEdit(QDATETIMEEDIT_TIME_MIN, QVariant::Time, parent)
+{
+}
+
+/*!
+ Constructs an empty time editor with a \a parent. The time is set
+ to \a time.
+*/
+
+QTimeEdit::QTimeEdit(const QTime &time, QWidget *parent)
+ : QDateTimeEdit(time, QVariant::Time, parent)
+{
+}
+
+/*!
+ \property QTimeEdit::time
+ \brief the QTime that is shown in the widget
+
+ By default, this property contains a time of 00:00:00 and 0 milliseconds.
+*/
+
+
+/*!
+ \class QDateEdit
+ \brief The QDateEdit class provides a widget for editing dates based on
+ the QDateTimeEdit widget.
+
+ \ingroup basicwidgets
+ \mainclass
+
+ Many of the properties and functions provided by QDateEdit are implemented in
+ QDateTimeEdit. The following properties are most relevant to users of this
+ class:
+
+ \list
+ \o \l{QDateTimeEdit::date}{date} holds the date displayed by the widget.
+ \o \l{QDateTimeEdit::minimumDate}{minimumDate} defines the minimum (earliest)
+ date that can be set by the user.
+ \o \l{QDateTimeEdit::maximumDate}{maximumDate} defines the maximum (latest) date
+ that can be set by the user.
+ \o \l{QDateTimeEdit::displayFormat}{displayFormat} contains a string that is used
+ to format the date displayed in the widget.
+ \endlist
+
+ \table 100%
+ \row \o \inlineimage windowsxp-dateedit.png Screenshot of a Windows XP style date editing widget
+ \o A date editing widget shown in the \l{Windows XP Style Widget Gallery}{Windows XP widget style}.
+ \row \o \inlineimage macintosh-dateedit.png Screenshot of a Macintosh style date editing widget
+ \o A date editing widget shown in the \l{Macintosh Style Widget Gallery}{Macintosh widget style}.
+ \row \o \inlineimage plastique-dateedit.png Screenshot of a Plastique style date editing widget
+ \o A date editing widget shown in the \l{Plastique Style Widget Gallery}{Plastique widget style}.
+ \endtable
+
+ \sa QTimeEdit, QDateTimeEdit
+*/
+
+/*!
+ Constructs an empty date editor with a \a parent.
+*/
+
+QDateEdit::QDateEdit(QWidget *parent)
+ : QDateTimeEdit(QDATETIMEEDIT_DATE_INITIAL, QVariant::Date, parent)
+{
+}
+
+/*!
+ Constructs an empty date editor with a \a parent. The date is set
+ to \a date.
+*/
+
+QDateEdit::QDateEdit(const QDate &date, QWidget *parent)
+ : QDateTimeEdit(date, QVariant::Date, parent)
+{
+}
+
+/*!
+ \property QDateEdit::date
+ \brief the QDate that is shown in the widget
+
+ By default, this property contains a date referring to January 1, 2000.
+*/
+
+
+// --- QDateTimeEditPrivate ---
+
+/*!
+ \internal
+ Constructs a QDateTimeEditPrivate object
+*/
+
+
+QDateTimeEditPrivate::QDateTimeEditPrivate()
+ : QDateTimeParser(QVariant::DateTime, QDateTimeParser::DateTimeEdit)
+{
+ hasHadFocus = false;
+ formatExplicitlySet = false;
+ cacheGuard = false;
+ fixday = true;
+ type = QVariant::DateTime;
+ sections = 0;
+ cachedDay = -1;
+ currentSectionIndex = FirstSectionIndex;
+
+ layoutDirection = QApplication::layoutDirection();
+ first.type = FirstSection;
+ last.type = LastSection;
+ none.type = NoSection;
+ first.pos = 0;
+ last.pos = -1;
+ none.pos = -1;
+ sections = 0;
+ calendarPopup = false;
+ minimum = QDATETIMEEDIT_COMPAT_DATETIME_MIN;
+ maximum = QDATETIMEEDIT_DATETIME_MAX;
+ arrowState = QStyle::State_None;
+ monthCalendar = 0;
+ readLocaleSettings();
+
+#ifdef QT_KEYPAD_NAVIGATION
+ focusOnButton = false;
+#endif
+}
+
+void QDateTimeEditPrivate::updateTimeSpec()
+{
+ minimum = minimum.toDateTime().toTimeSpec(spec);
+ maximum = maximum.toDateTime().toTimeSpec(spec);
+ value = value.toDateTime().toTimeSpec(spec);
+}
+
+void QDateTimeEditPrivate::updateEdit()
+{
+ const QString newText = (specialValue() ? specialValueText : textFromValue(value));
+ if (newText == displayText())
+ return;
+ int selsize = edit->selectedText().size();
+ const bool sb = edit->blockSignals(true);
+
+ edit->setText(newText);
+
+ if (!specialValue()
+#ifdef QT_KEYPAD_NAVIGATION
+ && !(QApplication::keypadNavigationEnabled() && !edit->hasEditFocus())
+#endif
+ ) {
+ int cursor = sectionPos(currentSectionIndex);
+ QDTEDEBUG << "cursor is " << cursor << currentSectionIndex;
+ cursor = qBound(0, cursor, displayText().size());
+ QDTEDEBUG << cursor;
+ if (selsize > 0) {
+ edit->setSelection(cursor, selsize);
+ QDTEDEBUG << cursor << selsize;
+ } else {
+ edit->setCursorPosition(cursor);
+ QDTEDEBUG << cursor;
+
+ }
+ }
+ edit->blockSignals(sb);
+}
+
+
+/*!
+ \internal
+
+ Selects the section \a s. If \a forward is false selects backwards.
+*/
+
+void QDateTimeEditPrivate::setSelected(int sectionIndex, bool forward)
+{
+ if (specialValue()
+#ifdef QT_KEYPAD_NAVIGATION
+ || (QApplication::keypadNavigationEnabled() && !edit->hasEditFocus())
+#endif
+ ) {
+ edit->selectAll();
+ } else {
+ const SectionNode &node = sectionNode(sectionIndex);
+ if (node.type == NoSection || node.type == LastSection || node.type == FirstSection)
+ return;
+
+ updateCache(value, displayText());
+ const int size = sectionSize(sectionIndex);
+ if (forward) {
+ edit->setSelection(sectionPos(node), size);
+ } else {
+ edit->setSelection(sectionPos(node) + size, -size);
+ }
+ }
+}
+
+/*!
+ \internal
+
+ Returns the section at index \a index or NoSection if there are no sections there.
+*/
+
+int QDateTimeEditPrivate::sectionAt(int pos) const
+{
+ if (pos < separators.first().size()) {
+ return (pos == 0 ? FirstSectionIndex : NoSectionIndex);
+ } else if (displayText().size() - pos < separators.last().size() + 1) {
+ if (separators.last().size() == 0) {
+ return sectionNodes.count() - 1;
+ }
+ return (pos == displayText().size() ? LastSectionIndex : NoSectionIndex);
+ }
+ updateCache(value, displayText());
+
+ for (int i=0; i<sectionNodes.size(); ++i) {
+ const int tmp = sectionPos(i);
+ if (pos < tmp + sectionSize(i)) {
+ return (pos < tmp ? -1 : i);
+ }
+ }
+ return -1;
+}
+
+/*!
+ \internal
+
+ Returns the closest section of index \a index. Searches forward
+ for a section if \a forward is true. Otherwise searches backwards.
+*/
+
+int QDateTimeEditPrivate::closestSection(int pos, bool forward) const
+{
+ Q_ASSERT(pos >= 0);
+ if (pos < separators.first().size()) {
+ return forward ? 0 : FirstSectionIndex;
+ } else if (displayText().size() - pos < separators.last().size() + 1) {
+ return forward ? LastSectionIndex : sectionNodes.size() - 1;
+ }
+ updateCache(value, displayText());
+ for (int i=0; i<sectionNodes.size(); ++i) {
+ const int tmp = sectionPos(sectionNodes.at(i));
+ if (pos < tmp + sectionSize(i)) {
+ if (pos < tmp && !forward) {
+ return i-1;
+ }
+ return i;
+ } else if (i == sectionNodes.size() - 1 && pos > tmp) {
+ return i;
+ }
+ }
+ qWarning("QDateTimeEdit: Internal Error: closestSection returned NoSection");
+ return NoSectionIndex;
+}
+
+/*!
+ \internal
+
+ Returns a copy of the section that is before or after \a current, depending on \a forward.
+*/
+
+int QDateTimeEditPrivate::nextPrevSection(int current, bool forward) const
+{
+ Q_Q(const QDateTimeEdit);
+ if (q->isRightToLeft())
+ forward = !forward;
+
+ switch (current) {
+ case FirstSectionIndex: return forward ? 0 : FirstSectionIndex;
+ case LastSectionIndex: return (forward ? LastSectionIndex : sectionNodes.size() - 1);
+ case NoSectionIndex: return FirstSectionIndex;
+ default: break;
+ }
+ Q_ASSERT(current >= 0 && current < sectionNodes.size());
+
+ current += (forward ? 1 : -1);
+ if (current >= sectionNodes.size()) {
+ return LastSectionIndex;
+ } else if (current < 0) {
+ return FirstSectionIndex;
+ }
+
+ return current;
+}
+
+/*!
+ \internal
+
+ Clears the text of section \a s.
+*/
+
+void QDateTimeEditPrivate::clearSection(int index)
+{
+ const QLatin1Char space(' ');
+ int cursorPos = edit->cursorPosition();
+ bool blocked = edit->blockSignals(true);
+ QString t = edit->text();
+ const int pos = sectionPos(index);
+ if (pos == -1) {
+ qWarning("QDateTimeEdit: Internal error (%s:%d)", __FILE__, __LINE__);
+ return;
+ }
+ const int size = sectionSize(index);
+ t.replace(pos, size, QString().fill(space, size));
+ edit->setText(t);
+ edit->setCursorPosition(cursorPos);
+ QDTEDEBUG << cursorPos;
+
+ edit->blockSignals(blocked);
+}
+
+
+/*!
+ \internal
+
+ updates the cached values
+*/
+
+void QDateTimeEditPrivate::updateCache(const QVariant &val, const QString &str) const
+{
+ if (val != cachedValue || str != cachedText || cacheGuard) {
+ cacheGuard = true;
+ QString copy = str;
+ int unused = edit->cursorPosition();
+ QValidator::State unusedState;
+ validateAndInterpret(copy, unused, unusedState);
+ cacheGuard = false;
+ }
+}
+
+/*!
+ \internal
+
+ parses and validates \a input
+*/
+
+QDateTime QDateTimeEditPrivate::validateAndInterpret(QString &input, int &position,
+ QValidator::State &state, bool fixup) const
+{
+ if (input.isEmpty()) {
+ if (sectionNodes.size() == 1 || !specialValueText.isEmpty()) {
+ state = QValidator::Intermediate;
+ } else {
+ state = QValidator::Invalid;
+ }
+ return getZeroVariant().toDateTime();
+ } else if (cachedText == input && !fixup) {
+ state = cachedState;
+ return cachedValue.toDateTime();
+ } else if (!specialValueText.isEmpty()) {
+ bool changeCase = false;
+ const int max = qMin(specialValueText.size(), input.size());
+ int i;
+ for (i=0; i<max; ++i) {
+ const QChar ic = input.at(i);
+ const QChar sc = specialValueText.at(i);
+ if (ic != sc) {
+ if (sc.toLower() == ic.toLower()) {
+ changeCase = true;
+ } else {
+ break;
+ }
+ }
+ }
+ if (i == max) {
+ state = specialValueText.size() == input.size() ? QValidator::Acceptable : QValidator::Intermediate;
+ if (changeCase) {
+ input = specialValueText.left(max);
+ }
+ return minimum.toDateTime();
+ }
+ }
+ StateNode tmp = parse(input, position, value.toDateTime(), fixup);
+ input = tmp.input;
+ state = QValidator::State(int(tmp.state));
+ if (state == QValidator::Acceptable) {
+ if (tmp.conflicts && conflictGuard != tmp.value) {
+ conflictGuard = tmp.value;
+ clearCache();
+ input = textFromValue(tmp.value);
+ updateCache(tmp.value, input);
+ conflictGuard.clear();
+ } else {
+ cachedText = input;
+ cachedState = state;
+ cachedValue = tmp.value;
+ }
+ } else {
+ clearCache();
+ }
+ return (tmp.value.isNull() ? getZeroVariant().toDateTime() : tmp.value);
+}
+
+
+/*!
+ \internal
+ \reimp
+*/
+
+QString QDateTimeEditPrivate::textFromValue(const QVariant &f) const
+{
+ Q_Q(const QDateTimeEdit);
+ return q->textFromDateTime(f.toDateTime());
+}
+
+/*!
+ \internal
+ \reimp
+
+ This function's name is slightly confusing; it is not to be confused
+ with QAbstractSpinBox::valueFromText().
+*/
+
+QVariant QDateTimeEditPrivate::valueFromText(const QString &f) const
+{
+ Q_Q(const QDateTimeEdit);
+ return q->dateTimeFromText(f).toTimeSpec(spec);
+}
+
+
+/*!
+ \internal
+
+ Internal function called by QDateTimeEdit::stepBy(). Also takes a
+ Section for which section to step on and a bool \a test for
+ whether or not to modify the internal cachedDay variable. This is
+ necessary because the function is called from the const function
+ QDateTimeEdit::stepEnabled() as well as QDateTimeEdit::stepBy().
+*/
+
+QDateTime QDateTimeEditPrivate::stepBy(int sectionIndex, int steps, bool test) const
+{
+ Q_Q(const QDateTimeEdit);
+ QDateTime v = value.toDateTime();
+ QString str = displayText();
+ int pos = edit->cursorPosition();
+ const SectionNode sn = sectionNode(sectionIndex);
+
+ int val;
+ // to make sure it behaves reasonably when typing something and then stepping in non-tracking mode
+ if (!test && pendingEmit) {
+ if (q->validate(str, pos) != QValidator::Acceptable) {
+ v = value.toDateTime();
+ } else {
+ v = q->dateTimeFromText(str);
+ }
+ val = getDigit(v, sectionIndex);
+ } else {
+ val = getDigit(v, sectionIndex);
+ }
+
+ val += steps;
+
+ const int min = absoluteMin(sectionIndex);
+ const int max = absoluteMax(sectionIndex, value.toDateTime());
+
+ if (val < min) {
+ val = (wrapping ? max - (min - val) + 1 : min);
+ } else if (val > max) {
+ val = (wrapping ? min + val - max - 1 : max);
+ }
+
+
+ const int oldDay = v.date().day();
+
+ setDigit(v, sectionIndex, val);
+ // if this sets year or month it will make
+ // sure that days are lowered if needed.
+
+ const QDateTime minimumDateTime = minimum.toDateTime();
+ const QDateTime maximumDateTime = maximum.toDateTime();
+ // changing one section should only modify that section, if possible
+ if (sn.type != AmPmSection && (v < minimumDateTime || v > maximumDateTime)) {
+ const int localmin = getDigit(minimumDateTime, sectionIndex);
+ const int localmax = getDigit(maximumDateTime, sectionIndex);
+
+ if (wrapping) {
+ // just because we hit the roof in one direction, it
+ // doesn't mean that we hit the floor in the other
+ if (steps > 0) {
+ setDigit(v, sectionIndex, min);
+ if (!(sn.type & (DaySection|DayOfWeekSection)) && sections & DateSectionMask) {
+ const int daysInMonth = v.date().daysInMonth();
+ if (v.date().day() < oldDay && v.date().day() < daysInMonth) {
+ const int adds = qMin(oldDay, daysInMonth);
+ v = v.addDays(adds - v.date().day());
+ }
+ }
+
+ if (v < minimumDateTime) {
+ setDigit(v, sectionIndex, localmin);
+ if (v < minimumDateTime)
+ setDigit(v, sectionIndex, localmin + 1);
+ }
+ } else {
+ setDigit(v, sectionIndex, max);
+ if (!(sn.type & (DaySection|DayOfWeekSection)) && sections & DateSectionMask) {
+ const int daysInMonth = v.date().daysInMonth();
+ if (v.date().day() < oldDay && v.date().day() < daysInMonth) {
+ const int adds = qMin(oldDay, daysInMonth);
+ v = v.addDays(adds - v.date().day());
+ }
+ }
+
+ if (v > maximumDateTime) {
+ setDigit(v, sectionIndex, localmax);
+ if (v > maximumDateTime)
+ setDigit(v, sectionIndex, localmax - 1);
+ }
+ }
+ } else {
+ setDigit(v, sectionIndex, (steps > 0 ? localmax : localmin));
+ }
+ }
+ if (!test && oldDay != v.date().day() && !(sn.type & (DaySection|DayOfWeekSection))) {
+ // this should not happen when called from stepEnabled
+ cachedDay = qMax<int>(oldDay, cachedDay);
+ }
+
+ if (v < minimumDateTime) {
+ if (wrapping) {
+ QDateTime t = v;
+ setDigit(t, sectionIndex, steps < 0 ? max : min);
+ bool mincmp = (t >= minimumDateTime);
+ bool maxcmp = (t <= maximumDateTime);
+ if (!mincmp || !maxcmp) {
+ setDigit(t, sectionIndex, getDigit(steps < 0
+ ? maximumDateTime
+ : minimumDateTime, sectionIndex));
+ mincmp = (t >= minimumDateTime);
+ maxcmp = (t <= maximumDateTime);
+ }
+ if (mincmp && maxcmp) {
+ v = t;
+ }
+ } else {
+ v = value.toDateTime();
+ }
+ } else if (v > maximumDateTime) {
+ if (wrapping) {
+ QDateTime t = v;
+ setDigit(t, sectionIndex, steps > 0 ? min : max);
+ bool mincmp = (t >= minimumDateTime);
+ bool maxcmp = (t <= maximumDateTime);
+ if (!mincmp || !maxcmp) {
+ setDigit(t, sectionIndex, getDigit(steps > 0 ?
+ minimumDateTime :
+ maximumDateTime, sectionIndex));
+ mincmp = (t >= minimumDateTime);
+ maxcmp = (t <= maximumDateTime);
+ }
+ if (mincmp && maxcmp) {
+ v = t;
+ }
+ } else {
+ v = value.toDateTime();
+ }
+ }
+
+ const QDateTime ret = bound(v, value, steps).toDateTime().toTimeSpec(spec);
+ return ret;
+}
+
+/*!
+ \internal
+ \reimp
+*/
+
+void QDateTimeEditPrivate::emitSignals(EmitPolicy ep, const QVariant &old)
+{
+ Q_Q(QDateTimeEdit);
+ if (ep == NeverEmit) {
+ return;
+ }
+ pendingEmit = false;
+
+ const bool dodate = value.toDate().isValid() && (sections & DateSectionMask);
+ const bool datechanged = (ep == AlwaysEmit || old.toDate() != value.toDate());
+ const bool dotime = value.toTime().isValid() && (sections & TimeSectionMask);
+ const bool timechanged = (ep == AlwaysEmit || old.toTime() != value.toTime());
+
+ updateCache(value, displayText());
+
+ syncCalendarWidget();
+ if (datechanged || timechanged)
+ emit q->dateTimeChanged(value.toDateTime());
+ if (dodate && datechanged)
+ emit q->dateChanged(value.toDate());
+ if (dotime && timechanged)
+ emit q->timeChanged(value.toTime());
+
+}
+
+/*!
+ \internal
+ \reimp
+*/
+
+void QDateTimeEditPrivate::_q_editorCursorPositionChanged(int oldpos, int newpos)
+{
+ if (ignoreCursorPositionChanged || specialValue())
+ return;
+ const QString oldText = displayText();
+ updateCache(value, oldText);
+
+ const bool allowChange = !edit->hasSelectedText();
+ const bool forward = oldpos <= newpos;
+ ignoreCursorPositionChanged = true;
+ int s = sectionAt(newpos);
+ if (s == NoSectionIndex && forward && newpos > 0) {
+ s = sectionAt(newpos - 1);
+ }
+
+ int c = newpos;
+
+ const int selstart = edit->selectionStart();
+ const int selSection = sectionAt(selstart);
+ const int l = selSection != -1 ? sectionSize(selSection) : 0;
+
+ if (s == NoSectionIndex) {
+ if (l > 0 && selstart == sectionPos(selSection) && edit->selectedText().size() == l) {
+ s = selSection;
+ if (allowChange)
+ setSelected(selSection, true);
+ c = -1;
+ } else {
+ int closest = closestSection(newpos, forward);
+ c = sectionPos(closest) + (forward ? 0 : qMax<int>(0, sectionSize(closest)));
+
+ if (allowChange) {
+ edit->setCursorPosition(c);
+ QDTEDEBUG << c;
+ }
+ s = closest;
+ }
+ }
+
+ if (allowChange && currentSectionIndex != s) {
+ interpret(EmitIfChanged);
+ }
+ if (c == -1) {
+ setSelected(s, true);
+ } else if (!edit->hasSelectedText()) {
+ if (oldpos < newpos) {
+ edit->setCursorPosition(displayText().size() - (oldText.size() - c));
+ } else {
+ edit->setCursorPosition(c);
+ }
+ }
+
+ QDTEDEBUG << "currentSectionIndex is set to" << sectionName(sectionType(s))
+ << oldpos << newpos
+ << "was" << sectionName(sectionType(currentSectionIndex));
+
+ currentSectionIndex = s;
+ Q_ASSERT_X(currentSectionIndex < sectionNodes.size(),
+ "QDateTimeEditPrivate::_q_editorCursorPositionChanged()",
+ qPrintable(QString::fromAscii("Internal error (%1 %2)").
+ arg(currentSectionIndex).
+ arg(sectionNodes.size())));
+
+ ignoreCursorPositionChanged = false;
+}
+
+/*!
+ \internal
+
+ Try to get the format from the local settings
+*/
+void QDateTimeEditPrivate::readLocaleSettings()
+{
+ const QLocale loc;
+ defaultTimeFormat = loc.timeFormat(QLocale::ShortFormat);
+ defaultDateFormat = loc.dateFormat(QLocale::ShortFormat);
+ defaultDateTimeFormat = loc.dateTimeFormat(QLocale::ShortFormat);
+}
+
+QDateTimeEdit::Section QDateTimeEditPrivate::convertToPublic(QDateTimeParser::Section s)
+{
+ switch (s & ~Internal) {
+ case AmPmSection: return QDateTimeEdit::AmPmSection;
+ case MSecSection: return QDateTimeEdit::MSecSection;
+ case SecondSection: return QDateTimeEdit::SecondSection;
+ case MinuteSection: return QDateTimeEdit::MinuteSection;
+ case DayOfWeekSection:
+ case DaySection: return QDateTimeEdit::DaySection;
+ case MonthSection: return QDateTimeEdit::MonthSection;
+ case YearSection2Digits:
+ case YearSection: return QDateTimeEdit::YearSection;
+ case Hour12Section:
+ case Hour24Section: return QDateTimeEdit::HourSection;
+ case FirstSection:
+ case NoSection:
+ case LastSection: break;
+ }
+ return QDateTimeEdit::NoSection;
+}
+
+QDateTimeEdit::Sections QDateTimeEditPrivate::convertSections(QDateTimeParser::Sections s)
+{
+ QDateTimeEdit::Sections ret = 0;
+ if (s & QDateTimeParser::MSecSection)
+ ret |= QDateTimeEdit::MSecSection;
+ if (s & QDateTimeParser::SecondSection)
+ ret |= QDateTimeEdit::SecondSection;
+ if (s & QDateTimeParser::MinuteSection)
+ ret |= QDateTimeEdit::MinuteSection;
+ if (s & (QDateTimeParser::Hour24Section|QDateTimeParser::Hour12Section))
+ ret |= QDateTimeEdit::HourSection;
+ if (s & QDateTimeParser::AmPmSection)
+ ret |= QDateTimeEdit::AmPmSection;
+ if (s & (QDateTimeParser::DaySection|QDateTimeParser::DayOfWeekSection))
+ ret |= QDateTimeEdit::DaySection;
+ if (s & QDateTimeParser::MonthSection)
+ ret |= QDateTimeEdit::MonthSection;
+ if (s & (QDateTimeParser::YearSection|QDateTimeParser::YearSection2Digits))
+ ret |= QDateTimeEdit::YearSection;
+
+ return ret;
+}
+
+/*!
+ \reimp
+*/
+
+void QDateTimeEdit::paintEvent(QPaintEvent *event)
+{
+ Q_D(QDateTimeEdit);
+ if (!d->calendarPopupEnabled()) {
+ QAbstractSpinBox::paintEvent(event);
+ return;
+ }
+
+ QStyleOptionSpinBox opt;
+ initStyleOption(&opt);
+
+ QStyleOptionComboBox optCombo;
+
+ optCombo.init(this);
+ optCombo.editable = true;
+ optCombo.subControls = opt.subControls;
+ optCombo.activeSubControls = opt.activeSubControls;
+ optCombo.state = opt.state;
+ if (d->readOnly) {
+ optCombo.state &= ~QStyle::State_Enabled;
+ }
+
+ QPainter p(this);
+ style()->drawComplexControl(QStyle::CC_ComboBox, &optCombo, &p, this);
+}
+
+QString QDateTimeEditPrivate::getAmPmText(AmPm ap, Case cs) const
+{
+ if (ap == AmText) {
+ return (cs == UpperCase ? QDateTimeEdit::tr("AM") : QDateTimeEdit::tr("am"));
+ } else {
+ return (cs == UpperCase ? QDateTimeEdit::tr("PM") : QDateTimeEdit::tr("pm"));
+ }
+}
+
+int QDateTimeEditPrivate::absoluteIndex(QDateTimeEdit::Section s, int index) const
+{
+ for (int i=0; i<sectionNodes.size(); ++i) {
+ if (convertToPublic(sectionNodes.at(i).type) == s && index-- == 0) {
+ return i;
+ }
+ }
+ return NoSectionIndex;
+}
+
+int QDateTimeEditPrivate::absoluteIndex(const SectionNode &s) const
+{
+ return sectionNodes.indexOf(s);
+}
+
+void QDateTimeEditPrivate::interpret(EmitPolicy ep)
+{
+ Q_Q(QDateTimeEdit);
+ QString tmp = displayText();
+ int pos = edit->cursorPosition();
+ const QValidator::State state = q->validate(tmp, pos);
+ if (state != QValidator::Acceptable
+ && correctionMode == QAbstractSpinBox::CorrectToPreviousValue
+ && (state == QValidator::Invalid || !(fieldInfo(currentSectionIndex) & AllowPartial))) {
+ setValue(value, ep);
+ updateTimeSpec();
+ } else {
+ QAbstractSpinBoxPrivate::interpret(ep);
+ }
+}
+
+void QDateTimeEditPrivate::clearCache() const
+{
+ QAbstractSpinBoxPrivate::clearCache();
+ cachedDay = -1;
+}
+
+/*!
+ Initialize \a option with the values from this QDataTimeEdit. This method
+ is useful for subclasses when they need a QStyleOptionSpinBox, but don't want
+ to fill in all the information themselves.
+
+ \sa QStyleOption::initFrom()
+*/
+void QDateTimeEdit::initStyleOption(QStyleOptionSpinBox *option) const
+{
+ if (!option)
+ return;
+
+ Q_D(const QDateTimeEdit);
+ QAbstractSpinBox::initStyleOption(option);
+ if (d->calendarPopupEnabled()) {
+ option->subControls = QStyle::SC_ComboBoxFrame | QStyle::SC_ComboBoxEditField
+ | QStyle::SC_ComboBoxArrow;
+ if (d->arrowState == QStyle::State_Sunken)
+ option->state |= QStyle::State_Sunken;
+ else
+ option->state &= ~QStyle::State_Sunken;
+ }
+}
+
+void QDateTimeEditPrivate::init(const QVariant &var)
+{
+ Q_Q(QDateTimeEdit);
+ switch (var.type()) {
+ case QVariant::Date:
+ value = QDateTime(var.toDate(), QDATETIMEEDIT_TIME_MIN);
+ q->setDisplayFormat(defaultDateFormat);
+ if (sectionNodes.isEmpty()) // ### safeguard for broken locale
+ q->setDisplayFormat(QLatin1String("dd/MM/yyyy"));
+ break;
+ case QVariant::DateTime:
+ value = var;
+ q->setDisplayFormat(defaultDateTimeFormat);
+ if (sectionNodes.isEmpty()) // ### safeguard for broken locale
+ q->setDisplayFormat(QLatin1String("dd/MM/yyyy hh:mm:ss"));
+ break;
+ case QVariant::Time:
+ value = QDateTime(QDATETIMEEDIT_DATE_INITIAL, var.toTime());
+ q->setDisplayFormat(defaultTimeFormat);
+ if (sectionNodes.isEmpty()) // ### safeguard for broken locale
+ q->setDisplayFormat(QLatin1String("hh:mm:ss"));
+ break;
+ default:
+ Q_ASSERT_X(0, "QDateTimeEditPrivate::init", "Internal error");
+ break;
+ }
+#ifdef QT_KEYPAD_NAVIGATION
+ if (QApplication::keypadNavigationEnabled())
+ q->setCalendarPopup(true);
+#endif
+ updateTimeSpec();
+ setLayoutItemMargins(QStyle::SE_DateTimeEditLayoutItem);
+}
+
+void QDateTimeEditPrivate::_q_resetButton()
+{
+ updateArrow(QStyle::State_None);
+}
+
+void QDateTimeEditPrivate::updateArrow(QStyle::StateFlag state)
+{
+ Q_Q(QDateTimeEdit);
+
+ if (arrowState == state)
+ return;
+ arrowState = state;
+ if (arrowState != QStyle::State_None)
+ buttonState |= Mouse;
+ else {
+ buttonState = 0;
+ hoverControl = QStyle::SC_ComboBoxFrame;
+ }
+ q->update();
+}
+
+/*!
+ \internal
+ Returns the hover control at \a pos.
+ This will update the hoverRect and hoverControl.
+*/
+QStyle::SubControl QDateTimeEditPrivate::newHoverControl(const QPoint &pos)
+{
+ if (!calendarPopupEnabled())
+ return QAbstractSpinBoxPrivate::newHoverControl(pos);
+
+ Q_Q(QDateTimeEdit);
+
+ QStyleOptionComboBox optCombo;
+ optCombo.init(q);
+ optCombo.editable = true;
+ optCombo.subControls = QStyle::SC_All;
+ hoverControl = q->style()->hitTestComplexControl(QStyle::CC_ComboBox, &optCombo, pos, q);
+ return hoverControl;
+}
+
+void QDateTimeEditPrivate::updateEditFieldGeometry()
+{
+ if (!calendarPopupEnabled()) {
+ QAbstractSpinBoxPrivate::updateEditFieldGeometry();
+ return;
+ }
+
+ Q_Q(QDateTimeEdit);
+
+ QStyleOptionComboBox optCombo;
+ optCombo.init(q);
+ optCombo.editable = true;
+ optCombo.subControls = QStyle::SC_ComboBoxEditField;
+ edit->setGeometry(q->style()->subControlRect(QStyle::CC_ComboBox, &optCombo,
+ QStyle::SC_ComboBoxEditField, q));
+}
+
+QVariant QDateTimeEditPrivate::getZeroVariant() const
+{
+ Q_ASSERT(type == QVariant::DateTime);
+ return QDateTime(QDATETIMEEDIT_DATE_INITIAL, QTime(), spec);
+}
+
+void QDateTimeEditPrivate::setRange(const QVariant &min, const QVariant &max)
+{
+ QAbstractSpinBoxPrivate::setRange(min, max);
+ syncCalendarWidget();
+}
+
+
+bool QDateTimeEditPrivate::isSeparatorKey(const QKeyEvent *ke) const
+{
+ if (!ke->text().isEmpty() && currentSectionIndex + 1 < sectionNodes.size() && currentSectionIndex >= 0) {
+ if (fieldInfo(currentSectionIndex) & Numeric) {
+ if (ke->text().at(0).isNumber())
+ return false;
+ } else if (ke->text().at(0).isLetterOrNumber()) {
+ return false;
+ }
+ return separators.at(currentSectionIndex + 1).contains(ke->text());
+ }
+ return false;
+}
+
+void QDateTimeEditPrivate::initCalendarPopup(QCalendarWidget *cw)
+{
+ Q_Q(QDateTimeEdit);
+ if (!monthCalendar) {
+ monthCalendar = new QCalendarPopup(q, cw);
+ monthCalendar->setObjectName(QLatin1String("qt_datetimedit_calendar"));
+ QObject::connect(monthCalendar, SIGNAL(newDateSelected(QDate)), q, SLOT(setDate(QDate)));
+ QObject::connect(monthCalendar, SIGNAL(hidingCalendar(QDate)), q, SLOT(setDate(QDate)));
+ QObject::connect(monthCalendar, SIGNAL(activated(QDate)), q, SLOT(setDate(QDate)));
+ QObject::connect(monthCalendar, SIGNAL(activated(QDate)), monthCalendar, SLOT(close()));
+ QObject::connect(monthCalendar, SIGNAL(resetButton()), q, SLOT(_q_resetButton()));
+ } else if (cw) {
+ monthCalendar->setCalendarWidget(cw);
+ }
+ syncCalendarWidget();
+}
+
+void QDateTimeEditPrivate::positionCalendarPopup()
+{
+ Q_Q(QDateTimeEdit);
+ QPoint pos = (q->layoutDirection() == Qt::RightToLeft) ? q->rect().bottomRight() : q->rect().bottomLeft();
+ QPoint pos2 = (q->layoutDirection() == Qt::RightToLeft) ? q->rect().topRight() : q->rect().topLeft();
+ pos = q->mapToGlobal(pos);
+ pos2 = q->mapToGlobal(pos2);
+ QSize size = monthCalendar->sizeHint();
+ QRect screen = QApplication::desktop()->availableGeometry(pos);
+ //handle popup falling "off screen"
+ if (q->layoutDirection() == Qt::RightToLeft) {
+ pos.setX(pos.x()-size.width());
+ pos2.setX(pos2.x()-size.width());
+ if (pos.x() < screen.left())
+ pos.setX(qMax(pos.x(), screen.left()));
+ else if (pos.x()+size.width() > screen.right())
+ pos.setX(qMax(pos.x()-size.width(), screen.right()-size.width()));
+ } else {
+ if (pos.x()+size.width() > screen.right())
+ pos.setX(screen.right()-size.width());
+ pos.setX(qMax(pos.x(), screen.left()));
+ }
+ if (pos.y() + size.height() > screen.bottom())
+ pos.setY(pos2.y() - size.height());
+ else if (pos.y() < screen.top())
+ pos.setY(screen.top());
+ if (pos.y() < screen.top())
+ pos.setY(screen.top());
+ if (pos.y()+size.height() > screen.bottom())
+ pos.setY(screen.bottom()-size.height());
+ monthCalendar->move(pos);
+}
+
+bool QDateTimeEditPrivate::calendarPopupEnabled() const
+{
+ return (calendarPopup && (sections & (DateSectionMask)));
+}
+
+void QDateTimeEditPrivate::syncCalendarWidget()
+{
+ Q_Q(QDateTimeEdit);
+ if (monthCalendar) {
+ monthCalendar->setDateRange(q->minimumDate(), q->maximumDate());
+ monthCalendar->setDate(q->date());
+ }
+}
+
+QCalendarPopup::QCalendarPopup(QWidget * parent, QCalendarWidget *cw)
+ : QWidget(parent, Qt::Popup), calendar(0)
+{
+ setAttribute(Qt::WA_WindowPropagation);
+
+ dateChanged = false;
+ if (!cw) {
+ cw = new QCalendarWidget(this);
+ cw->setVerticalHeaderFormat(QCalendarWidget::NoVerticalHeader);
+#ifdef QT_KEYPAD_NAVIGATION
+ if (QApplication::keypadNavigationEnabled())
+ cw->setHorizontalHeaderFormat(QCalendarWidget::SingleLetterDayNames);
+#endif
+ }
+ setCalendarWidget(cw);
+}
+
+void QCalendarPopup::setCalendarWidget(QCalendarWidget *cw)
+{
+ Q_ASSERT(cw);
+ QVBoxLayout *widgetLayout = qobject_cast<QVBoxLayout*>(layout());
+ if (!widgetLayout) {
+ widgetLayout = new QVBoxLayout(this);
+ widgetLayout->setMargin(0);
+ widgetLayout->setSpacing(0);
+ }
+ delete calendar;
+ calendar = cw;
+ widgetLayout->addWidget(calendar);
+
+ connect(calendar, SIGNAL(activated(QDate)), this, SLOT(dateSelected(QDate)));
+ connect(calendar, SIGNAL(clicked(QDate)), this, SLOT(dateSelected(QDate)));
+ connect(calendar, SIGNAL(selectionChanged()), this, SLOT(dateSelectionChanged()));
+
+ calendar->setFocus();
+}
+
+
+void QCalendarPopup::setDate(const QDate &date)
+{
+ oldDate = date;
+ calendar->setSelectedDate(date);
+}
+
+void QCalendarPopup::setDateRange(const QDate &min, const QDate &max)
+{
+ calendar->setMinimumDate(min);
+ calendar->setMaximumDate(max);
+}
+
+void QCalendarPopup::mousePressEvent(QMouseEvent *event)
+{
+ QDateTimeEdit *dateTime = qobject_cast<QDateTimeEdit *>(parentWidget());
+ if (dateTime) {
+ QStyleOptionComboBox opt;
+ opt.init(dateTime);
+ QRect arrowRect = dateTime->style()->subControlRect(QStyle::CC_ComboBox, &opt,
+ QStyle::SC_ComboBoxArrow, dateTime);
+ arrowRect.moveTo(dateTime->mapToGlobal(arrowRect .topLeft()));
+ if (arrowRect.contains(event->globalPos()) || rect().contains(event->pos()))
+ setAttribute(Qt::WA_NoMouseReplay);
+ }
+ QWidget::mousePressEvent(event);
+}
+
+void QCalendarPopup::mouseReleaseEvent(QMouseEvent*)
+{
+ emit resetButton();
+}
+
+bool QCalendarPopup::event(QEvent *event)
+{
+ if (event->type() == QEvent::KeyPress) {
+ QKeyEvent *keyEvent = static_cast<QKeyEvent*>(event);
+ if (keyEvent->key()== Qt::Key_Escape)
+ dateChanged = false;
+ }
+ return QWidget::event(event);
+}
+
+void QCalendarPopup::dateSelectionChanged()
+{
+ dateChanged = true;
+ emit newDateSelected(calendar->selectedDate());
+}
+void QCalendarPopup::dateSelected(const QDate &date)
+{
+ dateChanged = true;
+ emit activated(date);
+ close();
+}
+
+void QCalendarPopup::hideEvent(QHideEvent *)
+{
+ emit resetButton();
+ if (!dateChanged)
+ emit hidingCalendar(oldDate);
+}
+
+QT_END_NAMESPACE
+#include "moc_qdatetimeedit.cpp"
+
+#endif // QT_NO_DATETIMEEDIT
diff --git a/src/gui/widgets/qdatetimeedit.h b/src/gui/widgets/qdatetimeedit.h
new file mode 100644
index 0000000000..26c0e66c6e
--- /dev/null
+++ b/src/gui/widgets/qdatetimeedit.h
@@ -0,0 +1,232 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QDATETIMEEDIT_H
+#define QDATETIMEEDIT_H
+
+#include <QtCore/qdatetime.h>
+#include <QtCore/qvariant.h>
+#include <QtGui/qabstractspinbox.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+#ifndef QT_NO_DATETIMEEDIT
+
+class QDateTimeEditPrivate;
+class QStyleOptionSpinBox;
+class QCalendarWidget;
+
+class Q_GUI_EXPORT QDateTimeEdit : public QAbstractSpinBox
+{
+ Q_OBJECT
+
+ Q_ENUMS(Section)
+ Q_FLAGS(Sections)
+ Q_PROPERTY(QDateTime dateTime READ dateTime WRITE setDateTime NOTIFY dateTimeChanged USER true)
+ Q_PROPERTY(QDate date READ date WRITE setDate NOTIFY dateChanged)
+ Q_PROPERTY(QTime time READ time WRITE setTime NOTIFY timeChanged)
+ Q_PROPERTY(QDateTime maximumDateTime READ maximumDateTime WRITE setMaximumDateTime RESET clearMaximumDateTime)
+ Q_PROPERTY(QDateTime minimumDateTime READ minimumDateTime WRITE setMinimumDateTime RESET clearMinimumDateTime)
+ Q_PROPERTY(QDate maximumDate READ maximumDate WRITE setMaximumDate RESET clearMaximumDate)
+ Q_PROPERTY(QDate minimumDate READ minimumDate WRITE setMinimumDate RESET clearMinimumDate)
+ Q_PROPERTY(QTime maximumTime READ maximumTime WRITE setMaximumTime RESET clearMaximumTime)
+ Q_PROPERTY(QTime minimumTime READ minimumTime WRITE setMinimumTime RESET clearMinimumTime)
+ Q_PROPERTY(Section currentSection READ currentSection WRITE setCurrentSection)
+ Q_PROPERTY(Sections displayedSections READ displayedSections)
+ Q_PROPERTY(QString displayFormat READ displayFormat WRITE setDisplayFormat)
+ Q_PROPERTY(bool calendarPopup READ calendarPopup WRITE setCalendarPopup)
+ Q_PROPERTY(int currentSectionIndex READ currentSectionIndex WRITE setCurrentSectionIndex)
+ Q_PROPERTY(int sectionCount READ sectionCount)
+ Q_PROPERTY(Qt::TimeSpec timeSpec READ timeSpec WRITE setTimeSpec)
+public:
+ enum Section {
+ NoSection = 0x0000,
+ AmPmSection = 0x0001,
+ MSecSection = 0x0002,
+ SecondSection = 0x0004,
+ MinuteSection = 0x0008,
+ HourSection = 0x0010,
+ DaySection = 0x0100,
+ MonthSection = 0x0200,
+ YearSection = 0x0400,
+ TimeSections_Mask = AmPmSection|MSecSection|SecondSection|MinuteSection|HourSection,
+ DateSections_Mask = DaySection|MonthSection|YearSection
+ };
+
+ Q_DECLARE_FLAGS(Sections, Section)
+
+ explicit QDateTimeEdit(QWidget *parent = 0);
+ explicit QDateTimeEdit(const QDateTime &dt, QWidget *parent = 0);
+ explicit QDateTimeEdit(const QDate &d, QWidget *parent = 0);
+ explicit QDateTimeEdit(const QTime &t, QWidget *parent = 0);
+
+ QDateTime dateTime() const;
+ QDate date() const;
+ QTime time() const;
+
+ QDateTime minimumDateTime() const;
+ void clearMinimumDateTime();
+ void setMinimumDateTime(const QDateTime &dt);
+
+ QDateTime maximumDateTime() const;
+ void clearMaximumDateTime();
+ void setMaximumDateTime(const QDateTime &dt);
+
+ void setDateTimeRange(const QDateTime &min, const QDateTime &max);
+
+ QDate minimumDate() const;
+ void setMinimumDate(const QDate &min);
+ void clearMinimumDate();
+
+ QDate maximumDate() const;
+ void setMaximumDate(const QDate &max);
+ void clearMaximumDate();
+
+ void setDateRange(const QDate &min, const QDate &max);
+
+ QTime minimumTime() const;
+ void setMinimumTime(const QTime &min);
+ void clearMinimumTime();
+
+ QTime maximumTime() const;
+ void setMaximumTime(const QTime &max);
+ void clearMaximumTime();
+
+ void setTimeRange(const QTime &min, const QTime &max);
+
+ Sections displayedSections() const;
+ Section currentSection() const;
+ Section sectionAt(int index) const;
+ void setCurrentSection(Section section);
+
+ int currentSectionIndex() const;
+ void setCurrentSectionIndex(int index);
+
+ QCalendarWidget *calendarWidget() const;
+ void setCalendarWidget(QCalendarWidget *calendarWidget);
+
+ int sectionCount() const;
+
+ void setSelectedSection(Section section);
+
+ QString sectionText(Section section) const;
+
+ QString displayFormat() const;
+ void setDisplayFormat(const QString &format);
+
+ bool calendarPopup() const;
+ void setCalendarPopup(bool enable);
+
+ Qt::TimeSpec timeSpec() const;
+ void setTimeSpec(Qt::TimeSpec spec);
+
+ QSize sizeHint() const;
+
+ virtual void clear();
+ virtual void stepBy(int steps);
+
+ bool event(QEvent *event);
+Q_SIGNALS:
+ void dateTimeChanged(const QDateTime &date);
+ void timeChanged(const QTime &date);
+ void dateChanged(const QDate &date);
+
+public Q_SLOTS:
+ void setDateTime(const QDateTime &dateTime);
+ void setDate(const QDate &date);
+ void setTime(const QTime &time);
+
+protected:
+ virtual void keyPressEvent(QKeyEvent *event);
+#ifndef QT_NO_WHEELEVENT
+ virtual void wheelEvent(QWheelEvent *event);
+#endif
+ virtual void focusInEvent(QFocusEvent *event);
+ virtual bool focusNextPrevChild(bool next);
+ virtual QValidator::State validate(QString &input, int &pos) const;
+ virtual void fixup(QString &input) const;
+
+ virtual QDateTime dateTimeFromText(const QString &text) const;
+ virtual QString textFromDateTime(const QDateTime &dt) const;
+ virtual StepEnabled stepEnabled() const;
+ virtual void mousePressEvent(QMouseEvent *event);
+ virtual void paintEvent(QPaintEvent *event);
+ void initStyleOption(QStyleOptionSpinBox *option) const;
+
+ QDateTimeEdit(const QVariant &val, QVariant::Type parserType, QWidget *parent = 0);
+private:
+ Q_DECLARE_PRIVATE(QDateTimeEdit)
+ Q_DISABLE_COPY(QDateTimeEdit)
+
+ Q_PRIVATE_SLOT(d_func(), void _q_resetButton())
+};
+
+class Q_GUI_EXPORT QTimeEdit : public QDateTimeEdit
+{
+ Q_OBJECT
+ Q_PROPERTY(QTime time READ time WRITE setTime NOTIFY timeChanged USER true)
+public:
+ QTimeEdit(QWidget *parent = 0);
+ QTimeEdit(const QTime &time, QWidget *parent = 0);
+};
+
+class Q_GUI_EXPORT QDateEdit : public QDateTimeEdit
+{
+ Q_OBJECT
+ Q_PROPERTY(QDate date READ date WRITE setDate NOTIFY dateChanged USER true)
+public:
+ QDateEdit(QWidget *parent = 0);
+ QDateEdit(const QDate &date, QWidget *parent = 0);
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(QDateTimeEdit::Sections)
+
+#endif // QT_NO_DATETIMEEDIT
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QDATETIMEEDIT_H
diff --git a/src/gui/widgets/qdatetimeedit_p.h b/src/gui/widgets/qdatetimeedit_p.h
new file mode 100644
index 0000000000..5710935307
--- /dev/null
+++ b/src/gui/widgets/qdatetimeedit_p.h
@@ -0,0 +1,184 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QDATETIMEEDIT_P_H
+#define QDATETIMEEDIT_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 "QtGui/qcombobox.h"
+#include "QtGui/qcalendarwidget.h"
+#include "QtGui/qspinbox.h"
+#include "QtGui/qtoolbutton.h"
+#include "QtGui/qmenu.h"
+#include "QtGui/qlabel.h"
+#include "QtGui/qdatetimeedit.h"
+#include "QtGui/private/qabstractspinbox_p.h"
+#include "QtCore/private/qdatetime_p.h"
+
+#include "qdebug.h"
+
+#ifndef QT_NO_DATETIMEEDIT
+
+QT_BEGIN_NAMESPACE
+
+class QCalendarPopup;
+class QDateTimeEditPrivate : public QAbstractSpinBoxPrivate, public QDateTimeParser
+{
+ Q_DECLARE_PUBLIC(QDateTimeEdit)
+public:
+ QDateTimeEditPrivate();
+
+ void init(const QVariant &var);
+ void readLocaleSettings();
+
+ void emitSignals(EmitPolicy ep, const QVariant &old);
+ QString textFromValue(const QVariant &f) const;
+ QVariant valueFromText(const QString &f) const;
+ virtual void _q_editorCursorPositionChanged(int oldpos, int newpos);
+ virtual void interpret(EmitPolicy ep);
+ virtual void clearCache() const;
+
+ QDateTime validateAndInterpret(QString &input, int &, QValidator::State &state,
+ bool fixup = false) const;
+ void clearSection(int index);
+ virtual QString displayText() const { return edit->displayText(); } // this is from QDateTimeParser
+
+ int absoluteIndex(QDateTimeEdit::Section s, int index) const;
+ int absoluteIndex(const SectionNode &s) const;
+ void updateEdit();
+ QDateTime stepBy(int index, int steps, bool test = false) const;
+ int sectionAt(int pos) const;
+ int closestSection(int index, bool forward) const;
+ int nextPrevSection(int index, bool forward) const;
+ void setSelected(int index, bool forward = false);
+
+ void updateCache(const QVariant &val, const QString &str) const;
+
+ void updateTimeSpec();
+ virtual QDateTime getMinimum() const { return minimum.toDateTime(); }
+ virtual QDateTime getMaximum() const { return maximum.toDateTime(); }
+ virtual QLocale locale() const { return q_func()->locale(); }
+ QString valueToText(const QVariant &var) const { return textFromValue(var); }
+ QString getAmPmText(AmPm ap, Case cs) const;
+ int cursorPosition() const { return edit ? edit->cursorPosition() : -1; }
+
+ virtual QStyle::SubControl newHoverControl(const QPoint &pos);
+ virtual void updateEditFieldGeometry();
+ virtual QVariant getZeroVariant() const;
+ virtual void setRange(const QVariant &min, const QVariant &max);
+
+ void _q_resetButton();
+ void updateArrow(QStyle::StateFlag state);
+ bool calendarPopupEnabled() const;
+ void syncCalendarWidget();
+
+ bool isSeparatorKey(const QKeyEvent *k) const;
+
+ static QDateTimeEdit::Sections convertSections(QDateTimeParser::Sections s);
+ static QDateTimeEdit::Section convertToPublic(QDateTimeParser::Section s);
+
+ void initCalendarPopup(QCalendarWidget *cw = 0);
+ void positionCalendarPopup();
+
+ QDateTimeEdit::Sections sections;
+ mutable bool cacheGuard;
+
+ QString defaultDateFormat, defaultTimeFormat, defaultDateTimeFormat, unreversedFormat;
+ Qt::LayoutDirection layoutDirection;
+ mutable QVariant conflictGuard;
+ bool hasHadFocus, formatExplicitlySet, calendarPopup;
+ QStyle::StateFlag arrowState;
+ QCalendarPopup *monthCalendar;
+
+#ifdef QT_KEYPAD_NAVIGATION
+ bool focusOnButton;
+#endif
+};
+
+
+class QCalendarPopup : public QWidget
+{
+ Q_OBJECT
+public:
+ QCalendarPopup(QWidget *parent = 0, QCalendarWidget *cw = 0);
+ QDate selectedDate() { return calendar->selectedDate(); }
+ void setDate(const QDate &date);
+ void setDateRange(const QDate &min, const QDate &max);
+ void setFirstDayOfWeek(Qt::DayOfWeek dow) { calendar->setFirstDayOfWeek(dow); }
+ QCalendarWidget *calendarWidget() const { return calendar; }
+ void setCalendarWidget(QCalendarWidget *cw);
+Q_SIGNALS:
+ void activated(const QDate &date);
+ void newDateSelected(const QDate &newDate);
+ void hidingCalendar(const QDate &oldDate);
+ void resetButton();
+
+private Q_SLOTS:
+ void dateSelected(const QDate &date);
+ void dateSelectionChanged();
+
+protected:
+ void hideEvent(QHideEvent *);
+ void mousePressEvent(QMouseEvent *e);
+ void mouseReleaseEvent(QMouseEvent *);
+ bool event(QEvent *e);
+
+private:
+ QCalendarWidget *calendar;
+ QDate oldDate;
+ bool dateChanged;
+};
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_DATETIMEEDIT
+
+#endif // QDATETIMEEDIT_P_H
diff --git a/src/gui/widgets/qdial.cpp b/src/gui/widgets/qdial.cpp
new file mode 100644
index 0000000000..e19ce6a434
--- /dev/null
+++ b/src/gui/widgets/qdial.cpp
@@ -0,0 +1,530 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qdial.h"
+
+#ifndef QT_NO_DIAL
+
+#include <qapplication.h>
+#include <qbitmap.h>
+#include <qcolor.h>
+#include <qevent.h>
+#include <qpainter.h>
+#include <qpolygon.h>
+#include <qregion.h>
+#include <qstyle.h>
+#include <qstylepainter.h>
+#include <qstyleoption.h>
+#include <qslider.h>
+#include <private/qabstractslider_p.h>
+#include <private/qmath_p.h>
+#ifndef QT_NO_ACCESSIBILITY
+#include "qaccessible.h"
+#endif
+
+QT_BEGIN_NAMESPACE
+
+class QDialPrivate : public QAbstractSliderPrivate
+{
+ Q_DECLARE_PUBLIC(QDial)
+public:
+ QDialPrivate()
+ {
+ wrapping = false;
+ tracking = true;
+ doNotEmit = false;
+ target = qreal(3.7);
+ }
+
+ qreal target;
+ uint showNotches : 1;
+ uint wrapping : 1;
+ uint doNotEmit : 1;
+
+ int valueFromPoint(const QPoint &) const;
+ double angle(const QPoint &, const QPoint &) const;
+ void init();
+};
+
+void QDialPrivate::init()
+{
+ Q_Q(QDial);
+ showNotches = false;
+ q->setFocusPolicy(Qt::WheelFocus);
+#ifdef QT3_SUPPORT
+ QObject::connect(q, SIGNAL(sliderPressed()), q, SIGNAL(dialPressed()));
+ QObject::connect(q, SIGNAL(sliderMoved(int)), q, SIGNAL(dialMoved(int)));
+ QObject::connect(q, SIGNAL(sliderReleased()), q, SIGNAL(dialReleased()));
+#endif
+}
+
+/*!
+ Initialize \a option with the values from this QDial. This method
+ is useful for subclasses when they need a QStyleOptionSlider, but don't want
+ to fill in all the information themselves.
+
+ \sa QStyleOption::initFrom()
+*/
+void QDial::initStyleOption(QStyleOptionSlider *option) const
+{
+ if (!option)
+ return;
+
+ Q_D(const QDial);
+ option->initFrom(this);
+ option->minimum = d->minimum;
+ option->maximum = d->maximum;
+ option->sliderPosition = d->position;
+ option->sliderValue = d->value;
+ option->singleStep = d->singleStep;
+ option->pageStep = d->pageStep;
+ option->upsideDown = !d->invertedAppearance;
+ option->notchTarget = d->target;
+ option->dialWrapping = d->wrapping;
+ option->subControls = QStyle::SC_All;
+ option->activeSubControls = QStyle::SC_None;
+ if (!d->showNotches) {
+ option->subControls &= ~QStyle::SC_DialTickmarks;
+ option->tickPosition = QSlider::TicksAbove;
+ } else {
+ option->tickPosition = QSlider::NoTicks;
+ }
+ option->tickInterval = notchSize();
+}
+
+int QDialPrivate::valueFromPoint(const QPoint &p) const
+{
+ Q_Q(const QDial);
+ double yy = (double)q->height()/2.0 - p.y();
+ double xx = (double)p.x() - q->width()/2.0;
+ double a = (xx || yy) ? atan2(yy, xx) : 0;
+
+ if (a < Q_PI / -2)
+ a = a + Q_PI * 2;
+
+ int dist = 0;
+ int minv = minimum, maxv = maximum;
+
+ if (minimum < 0) {
+ dist = -minimum;
+ minv = 0;
+ maxv = maximum + dist;
+ }
+
+ int r = maxv - minv;
+ int v;
+ if (wrapping)
+ v = (int)(0.5 + minv + r * (Q_PI * 3 / 2 - a) / (2 * Q_PI));
+ else
+ v = (int)(0.5 + minv + r* (Q_PI * 4 / 3 - a) / (Q_PI * 10 / 6));
+
+ if (dist > 0)
+ v -= dist;
+
+ return !invertedAppearance ? bound(v) : maximum - bound(v);
+}
+
+/*!
+ \class QDial
+
+ \brief The QDial class provides a rounded range control (like a speedometer or potentiometer).
+
+ \ingroup basicwidgets
+ \mainclass
+
+ QDial is used when the user needs to control a value within a
+ program-definable range, and the range either wraps around
+ (for example, with angles measured from 0 to 359 degrees) or the
+ dialog layout needs a square widget.
+
+ Since QDial inherits from QAbstractSlider, the dial behaves in
+ a similar way to a \l{QSlider}{slider}. When wrapping() is false
+ (the default setting) there is no real difference between a slider
+ and a dial. They both share the same signals, slots and member
+ functions. Which one you use depends on the expectations of
+ your users and on the type of application.
+
+ The dial initially emits valueChanged() signals continuously while
+ the slider is being moved; you can make it emit the signal less
+ often by disabling the \l{QAbstractSlider::tracking} {tracking}
+ property. The sliderMoved() signal is emitted continuously even
+ when tracking is disabled.
+
+ The dial also emits sliderPressed() and sliderReleased() signals
+ when the mouse button is pressed and released. Note that the
+ dial's value can change without these signals being emitted since
+ the keyboard and wheel can also be used to change the value.
+
+ Unlike the slider, QDial attempts to draw a "nice" number of
+ notches rather than one per line step. If possible, the number of
+ notches drawn is one per line step, but if there aren't enough pixels
+ to draw every one, QDial will skip notches to try and draw a uniform
+ set (e.g. by drawing every second or third notch).
+
+ Like the slider, the dial makes the QAbstractSlider functions
+ setValue(), addLine(), subtractLine(), addPage() and
+ subtractPage() available as slots.
+
+ The dial's keyboard interface is fairly simple: The
+ \key{left}/\key{up} and \key{right}/\key{down} arrow keys adjust
+ the dial's \l {QAbstractSlider::value} {value} by the defined
+ \l {QAbstractSlider::singleStep} {singleStep}, \key{Page Up} and
+ \key{Page Down} by the defined \l {QAbstractSlider::pageStep}
+ {pageStep}, and the \key Home and \key End keys set the value to
+ the defined \l {QAbstractSlider::minimum} {minimum} and
+ \l {QAbstractSlider::maximum} {maximum} values.
+
+ If you are using the mouse wheel to adjust the dial, the increment
+ value is determined by the lesser value of
+ \l{QApplication::wheelScrollLines()} {wheelScrollLines} multipled
+ by \l {QAbstractSlider::singleStep} {singleStep}, and
+ \l {QAbstractSlider::pageStep} {pageStep}.
+
+ \table
+ \row \o \inlineimage plastique-dial.png Screenshot of a dial in the Plastique widget style
+ \o \inlineimage windowsxp-dial.png Screenshot of a dial in the Windows XP widget style
+ \o \inlineimage macintosh-dial.png Screenshot of a dial in the Macintosh widget style
+ \row \o {3,1} Dials shown in various widget styles (from left to right):
+ \l{Plastique Style Widget Gallery}{Plastique},
+ \l{Windows XP Style Widget Gallery}{Windows XP},
+ \l{Macintosh Style Widget Gallery}{Macintosh}.
+ \endtable
+
+ \sa QScrollBar, QSpinBox, QSlider, {fowler}{GUI Design Handbook: Slider}, {Sliders Example}
+*/
+
+/*!
+ Constructs a dial.
+
+ The \a parent argument is sent to the QAbstractSlider constructor.
+*/
+QDial::QDial(QWidget *parent)
+ : QAbstractSlider(*new QDialPrivate, parent)
+{
+ Q_D(QDial);
+ d->init();
+}
+
+#ifdef QT3_SUPPORT
+/*!
+ Use one of the constructors that doesn't take the \a name
+ argument and then use setObjectName() instead.
+*/
+QDial::QDial(QWidget *parent, const char *name)
+ : QAbstractSlider(*new QDialPrivate, parent)
+{
+ Q_D(QDial);
+ setObjectName(QString::fromAscii(name));
+ d->init();
+}
+
+/*!
+ Use one of the constructors that doesn't take the \a name
+ argument and then use setObjectName() instead.
+*/
+QDial::QDial(int minValue, int maxValue, int pageStep, int value,
+ QWidget *parent, const char *name)
+ : QAbstractSlider(*new QDialPrivate, parent)
+{
+ Q_D(QDial);
+ setObjectName(QString::fromAscii(name));
+ d->minimum = minValue;
+ d->maximum = maxValue;
+ d->pageStep = pageStep;
+ d->position = d->value = value;
+ d->init();
+}
+#endif
+/*!
+ Destroys the dial.
+*/
+QDial::~QDial()
+{
+}
+
+/*! \reimp */
+void QDial::resizeEvent(QResizeEvent *e)
+{
+ QWidget::resizeEvent(e);
+}
+
+/*!
+ \reimp
+*/
+
+void QDial::paintEvent(QPaintEvent *)
+{
+ QStylePainter p(this);
+ QStyleOptionSlider option;
+ initStyleOption(&option);
+ p.drawComplexControl(QStyle::CC_Dial, option);
+}
+
+/*!
+ \reimp
+*/
+
+void QDial::mousePressEvent(QMouseEvent *e)
+{
+ Q_D(QDial);
+ if (d->maximum == d->minimum ||
+ (e->button() != Qt::LeftButton) ||
+ (e->buttons() ^ e->button())) {
+ e->ignore();
+ return;
+ }
+ e->accept();
+ setSliderPosition(d->valueFromPoint(e->pos()));
+ // ### This isn't quite right,
+ // we should be doing a hit test and only setting this if it's
+ // the actual dial thingie (similar to what QSlider does), but we have no
+ // subControls for QDial.
+ setSliderDown(true);
+}
+
+
+/*!
+ \reimp
+*/
+
+void QDial::mouseReleaseEvent(QMouseEvent * e)
+{
+ Q_D(QDial);
+ if (e->buttons() & (~e->button()) ||
+ (e->button() != Qt::LeftButton)) {
+ e->ignore();
+ return;
+ }
+ e->accept();
+ setValue(d->valueFromPoint(e->pos()));
+ setSliderDown(false);
+}
+
+
+/*!
+ \reimp
+*/
+
+void QDial::mouseMoveEvent(QMouseEvent * e)
+{
+ Q_D(QDial);
+ if (!(e->buttons() & Qt::LeftButton)) {
+ e->ignore();
+ return;
+ }
+ e->accept();
+ d->doNotEmit = true;
+ setSliderPosition(d->valueFromPoint(e->pos()));
+ d->doNotEmit = false;
+}
+
+
+/*!
+ \reimp
+*/
+
+void QDial::sliderChange(SliderChange change)
+{
+ QAbstractSlider::sliderChange(change);
+}
+
+void QDial::setWrapping(bool enable)
+{
+ Q_D(QDial);
+ if (d->wrapping == enable)
+ return;
+ d->wrapping = enable;
+ update();
+}
+
+
+/*!
+ \property QDial::wrapping
+ \brief whether wrapping is enabled
+
+ If true, wrapping is enabled; otherwise some space is inserted at the bottom
+ of the dial to separate the ends of the range of valid values.
+
+ If enabled, the arrow can be oriented at any angle on the dial. If disabled,
+ the arrow will be restricted to the upper part of the dial; if it is rotated
+ into the space at the bottom of the dial, it will be clamped to the closest
+ end of the valid range of values.
+
+ By default this property is false.
+*/
+
+bool QDial::wrapping() const
+{
+ Q_D(const QDial);
+ return d->wrapping;
+}
+
+
+/*!
+ \property QDial::notchSize
+ \brief the current notch size
+
+ The notch size is in range control units, not pixels, and if
+ possible it is a multiple of singleStep() that results in an
+ on-screen notch size near notchTarget().
+
+ By default, this property has a value of 1.
+
+ \sa notchTarget(), singleStep()
+*/
+
+int QDial::notchSize() const
+{
+ Q_D(const QDial);
+ // radius of the arc
+ int r = qMin(width(), height())/2;
+ // length of the whole arc
+ int l = (int)(r * (d->wrapping ? 6 : 5) * Q_PI / 6);
+ // length of the arc from minValue() to minValue()+pageStep()
+ if (d->maximum > d->minimum + d->pageStep)
+ l = (int)(0.5 + l * d->pageStep / (d->maximum - d->minimum));
+ // length of a singleStep arc
+ l = l * d->singleStep / (d->pageStep ? d->pageStep : 1);
+ if (l < 1)
+ l = 1;
+ // how many times singleStep can be draw in d->target pixels
+ l = (int)(0.5 + d->target / l);
+ // we want notchSize() to be a non-zero multiple of lineStep()
+ if (!l)
+ l = 1;
+ return d->singleStep * l;
+}
+
+void QDial::setNotchTarget(double target)
+{
+ Q_D(QDial);
+ d->target = target;
+ update();
+}
+
+/*!
+ \property QDial::notchTarget
+ \brief the target number of pixels between notches
+
+ The notch target is the number of pixels QDial attempts to put
+ between each notch.
+
+ The actual size may differ from the target size.
+
+ The default notch target is 3.7 pixels.
+*/
+qreal QDial::notchTarget() const
+{
+ Q_D(const QDial);
+ return d->target;
+}
+
+
+void QDial::setNotchesVisible(bool visible)
+{
+ Q_D(QDial);
+ d->showNotches = visible;
+ update();
+}
+
+/*!
+ \property QDial::notchesVisible
+ \brief whether the notches are shown
+
+ If the property is true, a series of notches are drawn around the dial
+ to indicate the range of values available; otherwise no notches are
+ shown.
+
+ By default, this property is disabled.
+*/
+bool QDial::notchesVisible() const
+{
+ Q_D(const QDial);
+ return d->showNotches;
+}
+
+/*!
+ \reimp
+*/
+
+QSize QDial::minimumSizeHint() const
+{
+ return QSize(50, 50);
+}
+
+/*!
+ \reimp
+*/
+
+QSize QDial::sizeHint() const
+{
+ return QSize(100, 100).expandedTo(QApplication::globalStrut());
+}
+
+/*!
+ \reimp
+*/
+bool QDial::event(QEvent *e)
+{
+ return QAbstractSlider::event(e);
+}
+
+/*!
+ \fn void QDial::dialPressed();
+
+ Use QAbstractSlider::sliderPressed() instead.
+*/
+
+/*!
+ \fn void QDial::dialMoved(int value);
+
+ Use QAbstractSlider::sliderMoved() instead.
+*/
+
+/*!
+ \fn void QDial::dialReleased();
+
+ Use QAbstractSlider::sliderReleased() instead.
+*/
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_DIAL
diff --git a/src/gui/widgets/qdial.h b/src/gui/widgets/qdial.h
new file mode 100644
index 0000000000..1aa3d11031
--- /dev/null
+++ b/src/gui/widgets/qdial.h
@@ -0,0 +1,122 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+
+#ifndef QDIAL_H
+#define QDIAL_H
+
+#include <QtGui/qabstractslider.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+#ifndef QT_NO_DIAL
+
+class QDialPrivate;
+class QStyleOptionSlider;
+
+class Q_GUI_EXPORT QDial: public QAbstractSlider
+{
+ Q_OBJECT
+
+ Q_PROPERTY(bool wrapping READ wrapping WRITE setWrapping)
+ Q_PROPERTY(int notchSize READ notchSize)
+ Q_PROPERTY(qreal notchTarget READ notchTarget WRITE setNotchTarget)
+ Q_PROPERTY(bool notchesVisible READ notchesVisible WRITE setNotchesVisible)
+public:
+ explicit QDial(QWidget *parent = 0);
+
+ ~QDial();
+
+ bool wrapping() const;
+
+ int notchSize() const;
+
+ void setNotchTarget(double target);
+ qreal notchTarget() const;
+ bool notchesVisible() const;
+
+ QSize sizeHint() const;
+ QSize minimumSizeHint() const;
+
+public Q_SLOTS:
+ void setNotchesVisible(bool visible);
+ void setWrapping(bool on);
+
+protected:
+ bool event(QEvent *e);
+ void resizeEvent(QResizeEvent *re);
+ void paintEvent(QPaintEvent *pe);
+
+ void mousePressEvent(QMouseEvent *me);
+ void mouseReleaseEvent(QMouseEvent *me);
+ void mouseMoveEvent(QMouseEvent *me);
+
+ void sliderChange(SliderChange change);
+ void initStyleOption(QStyleOptionSlider *option) const;
+
+#ifdef QT3_SUPPORT
+public:
+ QT3_SUPPORT_CONSTRUCTOR QDial(int minValue, int maxValue, int pageStep, int value,
+ QWidget* parent = 0, const char* name = 0);
+ QT3_SUPPORT_CONSTRUCTOR QDial(QWidget *parent, const char *name);
+
+Q_SIGNALS:
+ QT_MOC_COMPAT void dialPressed();
+ QT_MOC_COMPAT void dialMoved(int value);
+ QT_MOC_COMPAT void dialReleased();
+#endif
+
+private:
+ Q_DECLARE_PRIVATE(QDial)
+ Q_DISABLE_COPY(QDial)
+};
+
+#endif // QT_NO_DIAL
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QDIAL_H
diff --git a/src/gui/widgets/qdialogbuttonbox.cpp b/src/gui/widgets/qdialogbuttonbox.cpp
new file mode 100644
index 0000000000..246da95736
--- /dev/null
+++ b/src/gui/widgets/qdialogbuttonbox.cpp
@@ -0,0 +1,1136 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtCore/qhash.h>
+#include <QtGui/qpushbutton.h>
+#include <QtGui/qstyle.h>
+#include <QtGui/qlayout.h>
+#include <QtGui/qdialog.h>
+#include <QtGui/qapplication.h>
+#include <QtGui/private/qwidget_p.h>
+
+#include "qdialogbuttonbox.h"
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QDialogButtonBox
+ \since 4.2
+ \brief The QDialogButtonBox class is a widget that presents buttons in a
+ layout that is appropriate to the current widget style.
+
+ \ingroup application
+ \mainclass
+
+ Dialogs and message boxes typically present buttons in a layout that
+ conforms to the interface guidelines for that platform. Invariably,
+ different platforms have different layouts for their dialogs.
+ QDialogButtonBox allows a developer to add buttons to it and will
+ automatically use the appropriate layout for the user's desktop
+ environment.
+
+ Most buttons for a dialog follow certain roles. Such roles include:
+
+ \list
+ \o Accepting or rejecting the dialog.
+ \o Asking for help.
+ \o Performing actions on the dialog itself (such as resetting fields or
+ applying changes).
+ \endlist
+
+ There can also be alternate ways of dismissing the dialog which may cause
+ destructive results.
+
+ Most dialogs have buttons that can almost be considered standard (e.g.
+ \gui OK and \gui Cancel buttons). It is sometimes convenient to create these
+ buttons in a standard way.
+
+ There are a couple ways of using QDialogButtonBox. One ways is to create
+ the buttons (or button texts) yourself and add them to the button box,
+ specifying their role.
+
+ \snippet examples/dialogs/extension/finddialog.cpp 1
+
+ Alternatively, QDialogButtonBox provides several standard buttons (e.g. OK, Cancel, Save)
+ that you can use. They exist as flags so you can OR them together in the constructor.
+
+ \snippet examples/dialogs/tabdialog/tabdialog.cpp 2
+
+ You can mix and match normal buttons and standard buttons.
+
+ Currently the buttons are laid out in the following way if the button box is horizontal:
+ \table 100%
+ \row \o \inlineimage buttonbox-gnomelayout-horizontal.png GnomeLayout Horizontal
+ \o Button box laid out in horizontal GnomeLayout
+ \row \o \inlineimage buttonbox-kdelayout-horizontal.png KdeLayout Horizontal
+ \o Button box laid out in horizontal KdeLayout
+ \row \o \inlineimage buttonbox-maclayout-horizontal.png MacLayout Horizontal
+ \o Button box laid out in horizontal MacLayout
+ \row \o \inlineimage buttonbox-winlayout-horizontal.png WinLayout Horizontal
+ \o Button box laid out in horizontal WinLayout
+ \endtable
+
+ The buttons are laid out the following way if the button box is vertical:
+
+ \table 100%
+ \row \o \inlineimage buttonbox-gnomelayout-vertical.png GnomeLayout Vertical
+ \o Button box laid out in vertical GnomeLayout
+ \row \o \inlineimage buttonbox-kdelayout-vertical.png KdeLayout Vertical
+ \o Button box laid out in vertical KdeLayout
+ \row \o \inlineimage buttonbox-maclayout-vertical.png MacLayout Vertical
+ \o Button box laid out in vertical MacLayout
+ \row \o \inlineimage buttonbox-winlayout-vertical.png WinLayout Vertical
+ \o Button box laid out in vertical WinLayout
+ \endtable
+
+ Additionally, button boxes that contain only buttons with ActionRole or
+ HelpRole can be considered modeless and have an alternate look on the mac:
+
+ \table 100%
+ \row \o \inlineimage buttonbox-mac-modeless-horizontal.png Screenshot of modeless horizontal MacLayout
+ \o modeless horizontal MacLayout
+ \row \o \inlineimage buttonbox-mac-modeless-vertical.png Screenshot of modeless vertical MacLayout
+ \o modeless vertical MacLayout
+ \endtable
+
+ When a button is clicked in the button box, the clicked() signal is emitted
+ for the actual button is that is pressed. For convenience, if the button
+ has an AcceptRole, RejectRole, or HelpRole, the accepted(), rejected(), or
+ helpRequested() signals are emitted respectively.
+
+ If you want a specific button to be default you need to call
+ QPushButton::setDefault() on it yourself. However, if there is no default
+ button set and to preserve which button is the default button across
+ platforms when using the QPushButton::autoDefault property, the first push
+ button with the accept role is made the default button when the
+ QDialogButtonBox is shown,
+
+ \sa QMessageBox, QPushButton, QDialog
+*/
+
+enum {
+ AcceptRole = QDialogButtonBox::AcceptRole,
+ RejectRole = QDialogButtonBox::RejectRole,
+ DestructiveRole = QDialogButtonBox::DestructiveRole,
+ ActionRole = QDialogButtonBox::ActionRole,
+ HelpRole = QDialogButtonBox::HelpRole,
+ YesRole = QDialogButtonBox::YesRole,
+ NoRole = QDialogButtonBox::NoRole,
+ ApplyRole = QDialogButtonBox::ApplyRole,
+ ResetRole = QDialogButtonBox::ResetRole,
+
+ AlternateRole = 0x10000000,
+ Stretch = 0x20000000,
+ EOL = 0x40000000,
+ Reverse = 0x80000000
+};
+
+static QDialogButtonBox::ButtonRole roleFor(QDialogButtonBox::StandardButton button)
+{
+ switch (button) {
+ case QDialogButtonBox::Ok:
+ case QDialogButtonBox::Save:
+ case QDialogButtonBox::Open:
+ case QDialogButtonBox::SaveAll:
+ case QDialogButtonBox::Retry:
+ case QDialogButtonBox::Ignore:
+ return QDialogButtonBox::AcceptRole;
+
+ case QDialogButtonBox::Cancel:
+ case QDialogButtonBox::Close:
+ case QDialogButtonBox::Abort:
+ return QDialogButtonBox::RejectRole;
+
+ case QDialogButtonBox::Discard:
+ return QDialogButtonBox::DestructiveRole;
+
+ case QDialogButtonBox::Help:
+ return QDialogButtonBox::HelpRole;
+
+ case QDialogButtonBox::Apply:
+ return QDialogButtonBox::ApplyRole;
+
+ case QDialogButtonBox::Yes:
+ case QDialogButtonBox::YesToAll:
+ return QDialogButtonBox::YesRole;
+
+ case QDialogButtonBox::No:
+ case QDialogButtonBox::NoToAll:
+ return QDialogButtonBox::NoRole;
+
+ case QDialogButtonBox::RestoreDefaults:
+ case QDialogButtonBox::Reset:
+ return QDialogButtonBox::ResetRole;
+
+ case QDialogButtonBox::NoButton: // NoButton means zero buttons, not "No" button
+ ;
+ }
+
+ return QDialogButtonBox::InvalidRole;
+}
+
+static const int layouts[2][5][14] =
+{
+ // Qt::Horizontal
+ {
+ // WinLayout
+ { ResetRole, Stretch, YesRole, AcceptRole, AlternateRole, DestructiveRole, NoRole, ActionRole, RejectRole, ApplyRole,
+ HelpRole, EOL, EOL, EOL },
+
+ // MacLayout
+ { HelpRole, ResetRole, ApplyRole, ActionRole, Stretch, DestructiveRole | Reverse,
+ AlternateRole | Reverse, RejectRole | Reverse, AcceptRole | Reverse, NoRole | Reverse, YesRole | Reverse, EOL, EOL },
+
+ // KdeLayout
+ { HelpRole, ResetRole, Stretch, YesRole, NoRole, ActionRole, AcceptRole, AlternateRole,
+ ApplyRole, DestructiveRole, RejectRole, EOL },
+
+ // GnomeLayout
+ { HelpRole, ResetRole, Stretch, ActionRole, ApplyRole | Reverse, DestructiveRole | Reverse,
+ AlternateRole | Reverse, RejectRole | Reverse, AcceptRole | Reverse, NoRole | Reverse, YesRole | Reverse, EOL },
+
+ // Mac modeless
+ { ResetRole, ApplyRole, ActionRole, Stretch, HelpRole, EOL, EOL, EOL, EOL, EOL, EOL, EOL, EOL, EOL }
+ },
+
+ // Qt::Vertical
+ {
+ // WinLayout
+ { ActionRole, YesRole, AcceptRole, AlternateRole, DestructiveRole, NoRole, RejectRole, ApplyRole, ResetRole,
+ HelpRole, Stretch, EOL, EOL, EOL },
+
+ // MacLayout
+ { YesRole, NoRole, AcceptRole, RejectRole, AlternateRole, DestructiveRole, Stretch, ActionRole, ApplyRole,
+ ResetRole, HelpRole, EOL, EOL },
+
+ // KdeLayout
+ { AcceptRole, AlternateRole, ApplyRole, ActionRole, YesRole, NoRole, Stretch, ResetRole,
+ DestructiveRole, RejectRole, HelpRole, EOL },
+
+ // GnomeLayout
+ { YesRole, NoRole, AcceptRole, RejectRole, AlternateRole, DestructiveRole, ApplyRole, ActionRole, Stretch,
+ ResetRole, HelpRole, EOL, EOL, EOL },
+
+ // Mac modeless
+ { ActionRole, ApplyRole, ResetRole, Stretch, HelpRole, EOL, EOL, EOL, EOL, EOL, EOL, EOL, EOL, EOL }
+ }
+};
+
+class QDialogButtonBoxPrivate : public QWidgetPrivate
+{
+ Q_DECLARE_PUBLIC(QDialogButtonBox)
+
+public:
+ QDialogButtonBoxPrivate(Qt::Orientation orient);
+
+ QList<QAbstractButton *> buttonLists[QDialogButtonBox::NRoles];
+ QHash<QPushButton *, QDialogButtonBox::StandardButton> standardButtonHash;
+
+ Qt::Orientation orientation;
+ QDialogButtonBox::ButtonLayout layoutPolicy;
+ QBoxLayout *buttonLayout;
+ bool internalRemove;
+ bool center;
+
+ void createStandardButtons(QDialogButtonBox::StandardButtons buttons);
+
+ void layoutButtons();
+ void initLayout();
+ void resetLayout();
+ QPushButton *createButton(QDialogButtonBox::StandardButton button, bool doLayout = true);
+ void addButton(QAbstractButton *button, QDialogButtonBox::ButtonRole role, bool doLayout = true);
+ void _q_handleButtonDestroyed();
+ void _q_handleButtonClicked();
+ void addButtonsToLayout(const QList<QAbstractButton *> &buttonList, bool reverse);
+ void retranslateStrings();
+ const char *standardButtonText(QDialogButtonBox::StandardButton sbutton) const;
+};
+
+QDialogButtonBoxPrivate::QDialogButtonBoxPrivate(Qt::Orientation orient)
+ : orientation(orient), buttonLayout(0), internalRemove(false), center(false)
+{
+}
+
+void QDialogButtonBoxPrivate::initLayout()
+{
+ Q_Q(QDialogButtonBox);
+ layoutPolicy = QDialogButtonBox::ButtonLayout(q->style()->styleHint(QStyle::SH_DialogButtonLayout, 0, q));
+ bool createNewLayout = buttonLayout == 0
+ || (orientation == Qt::Horizontal && qobject_cast<QVBoxLayout *>(buttonLayout) != 0)
+ || (orientation == Qt::Vertical && qobject_cast<QHBoxLayout *>(buttonLayout) != 0);
+ if (createNewLayout) {
+ delete buttonLayout;
+ if (orientation == Qt::Horizontal)
+ buttonLayout = new QHBoxLayout(q);
+ else
+ buttonLayout = new QVBoxLayout(q);
+ }
+
+ int left, top, right, bottom;
+ setLayoutItemMargins(QStyle::SE_PushButtonLayoutItem);
+ getLayoutItemMargins(&left, &top, &right, &bottom);
+ buttonLayout->setContentsMargins(-left, -top, -right, -bottom);
+
+ if (!q->testAttribute(Qt::WA_WState_OwnSizePolicy)) {
+ QSizePolicy sp(QSizePolicy::Expanding, QSizePolicy::Fixed, QSizePolicy::ButtonBox);
+ if (orientation == Qt::Vertical)
+ sp.transpose();
+ q->setSizePolicy(sp);
+ q->setAttribute(Qt::WA_WState_OwnSizePolicy, false);
+ }
+
+ // ### move to a real init() function
+ q->setFocusPolicy(Qt::TabFocus);
+}
+
+void QDialogButtonBoxPrivate::resetLayout()
+{
+ //delete buttonLayout;
+ initLayout();
+ layoutButtons();
+}
+
+void QDialogButtonBoxPrivate::addButtonsToLayout(const QList<QAbstractButton *> &buttonList,
+ bool reverse)
+{
+ int start = reverse ? buttonList.count() - 1 : 0;
+ int end = reverse ? -1 : buttonList.count();
+ int step = reverse ? -1 : 1;
+
+ for (int i = start; i != end; i += step) {
+ QAbstractButton *button = buttonList.at(i);
+ buttonLayout->addWidget(button);
+ button->show();
+ }
+}
+
+void QDialogButtonBoxPrivate::layoutButtons()
+{
+ Q_Q(QDialogButtonBox);
+ const int MacGap = 36 - 8; // 8 is the default gap between a widget and a spacer item
+
+ for (int i = buttonLayout->count() - 1; i >= 0; --i) {
+ QLayoutItem *item = buttonLayout->takeAt(i);
+ if (QWidget *widget = item->widget())
+ widget->hide();
+ delete item;
+ }
+
+ int tmpPolicy = layoutPolicy;
+
+ static const int M = 5;
+ static const int ModalRoles[M] = { AcceptRole, RejectRole, DestructiveRole, YesRole, NoRole };
+ if (tmpPolicy == QDialogButtonBox::MacLayout) {
+ bool hasModalButton = false;
+ for (int i = 0; i < M; ++i) {
+ if (!buttonLists[ModalRoles[i]].isEmpty()) {
+ hasModalButton = true;
+ break;
+ }
+ }
+ if (!hasModalButton)
+ tmpPolicy = 4; // Mac modeless
+ }
+
+ const int *currentLayout = layouts[orientation == Qt::Vertical][tmpPolicy];
+
+ if (center)
+ buttonLayout->addStretch();
+
+ QList<QAbstractButton *> acceptRoleList = buttonLists[AcceptRole];
+
+ while (*currentLayout != EOL) {
+ int role = (*currentLayout & ~Reverse);
+ bool reverse = (*currentLayout & Reverse);
+
+ switch (role) {
+ case Stretch:
+ if (!center)
+ buttonLayout->addStretch();
+ break;
+ case AcceptRole: {
+ if (acceptRoleList.isEmpty())
+ break;
+ // Only the first one
+ QAbstractButton *button = acceptRoleList.first();
+ buttonLayout->addWidget(button);
+ button->show();
+ }
+ break;
+ case AlternateRole:
+ {
+ if (acceptRoleList.size() < 2)
+ break;
+ QList<QAbstractButton *> list = acceptRoleList;
+ list.removeFirst();
+ addButtonsToLayout(list, reverse);
+ }
+ break;
+ case DestructiveRole:
+ {
+ const QList<QAbstractButton *> &list = buttonLists[role];
+
+ /*
+ Mac: Insert a gap on the left of the destructive
+ buttons to ensure that they don't get too close to
+ the help and action buttons (but only if there are
+ some buttons to the left of the destructive buttons
+ (and the stretch, whence buttonLayout->count() > 1
+ and not 0)).
+ */
+ if (tmpPolicy == QDialogButtonBox::MacLayout
+ && !list.isEmpty() && buttonLayout->count() > 1)
+ buttonLayout->addSpacing(MacGap);
+
+ addButtonsToLayout(list, reverse);
+
+ /*
+ Insert a gap between the destructive buttons and the
+ accept and reject buttons.
+ */
+ if (tmpPolicy == QDialogButtonBox::MacLayout && !list.isEmpty())
+ buttonLayout->addSpacing(MacGap);
+ }
+ break;
+ case RejectRole:
+ case ActionRole:
+ case HelpRole:
+ case YesRole:
+ case NoRole:
+ case ApplyRole:
+ case ResetRole:
+ addButtonsToLayout(buttonLists[role], reverse);
+ }
+ ++currentLayout;
+ }
+
+ QWidget *lastWidget = 0;
+ q->setFocusProxy(0);
+ for (int i = 0; i < buttonLayout->count(); ++i) {
+ QLayoutItem *item = buttonLayout->itemAt(i);
+ if (QWidget *widget = item->widget()) {
+ if (lastWidget)
+ QWidget::setTabOrder(lastWidget, widget);
+ else
+ q->setFocusProxy(widget);
+ lastWidget = widget;
+ }
+ }
+
+ if (center)
+ buttonLayout->addStretch();
+}
+
+QPushButton *QDialogButtonBoxPrivate::createButton(QDialogButtonBox::StandardButton sbutton,
+ bool doLayout)
+{
+ Q_Q(QDialogButtonBox);
+ const char *buttonText = 0;
+ int icon = 0;
+
+ switch (sbutton) {
+ case QDialogButtonBox::Ok:
+ icon = QStyle::SP_DialogOkButton;
+ break;
+ case QDialogButtonBox::Save:
+ icon = QStyle::SP_DialogSaveButton;
+ break;
+ case QDialogButtonBox::Open:
+ icon = QStyle::SP_DialogOpenButton;
+ break;
+ case QDialogButtonBox::Cancel:
+ icon = QStyle::SP_DialogCancelButton;
+ break;
+ case QDialogButtonBox::Close:
+ icon = QStyle::SP_DialogCloseButton;
+ break;
+ case QDialogButtonBox::Apply:
+ icon = QStyle::SP_DialogApplyButton;
+ break;
+ case QDialogButtonBox::Reset:
+ icon = QStyle::SP_DialogResetButton;
+ break;
+ case QDialogButtonBox::Help:
+ icon = QStyle::SP_DialogHelpButton;
+ break;
+ case QDialogButtonBox::Discard:
+ icon = QStyle::SP_DialogDiscardButton;
+ break;
+ case QDialogButtonBox::Yes:
+ icon = QStyle::SP_DialogYesButton;
+ break;
+ case QDialogButtonBox::No:
+ icon = QStyle::SP_DialogNoButton;
+ break;
+ case QDialogButtonBox::YesToAll:
+ case QDialogButtonBox::NoToAll:
+ case QDialogButtonBox::SaveAll:
+ case QDialogButtonBox::Abort:
+ case QDialogButtonBox::Retry:
+ case QDialogButtonBox::Ignore:
+ case QDialogButtonBox::RestoreDefaults:
+ break;
+ case QDialogButtonBox::NoButton:
+ return 0;
+ ;
+ }
+ buttonText = standardButtonText(sbutton);
+
+ QPushButton *button = new QPushButton(QDialogButtonBox::tr(buttonText), q);
+ QStyle *style = q->style();
+ if (style->styleHint(QStyle::SH_DialogButtonBox_ButtonsHaveIcons, 0, q) && icon != 0)
+ button->setIcon(style->standardIcon(QStyle::StandardPixmap(icon), 0, q));
+ if (style != QApplication::style()) // Propagate style
+ button->setStyle(style);
+ standardButtonHash.insert(button, sbutton);
+ if (roleFor(sbutton) != QDialogButtonBox::InvalidRole) {
+ addButton(button, roleFor(sbutton), doLayout);
+ } else {
+ qWarning("QDialogButtonBox::createButton: Invalid ButtonRole, button not added");
+ }
+ return button;
+}
+
+void QDialogButtonBoxPrivate::addButton(QAbstractButton *button, QDialogButtonBox::ButtonRole role,
+ bool doLayout)
+{
+ Q_Q(QDialogButtonBox);
+ QObject::connect(button, SIGNAL(clicked()), q, SLOT(_q_handleButtonClicked()));
+ QObject::connect(button, SIGNAL(destroyed()), q, SLOT(_q_handleButtonDestroyed()));
+ buttonLists[role].append(button);
+ if (doLayout)
+ layoutButtons();
+}
+
+void QDialogButtonBoxPrivate::createStandardButtons(QDialogButtonBox::StandardButtons buttons)
+{
+ uint i = QDialogButtonBox::FirstButton;
+ while (i <= QDialogButtonBox::LastButton) {
+ if (i & buttons) {
+ createButton(QDialogButtonBox::StandardButton(i), false);
+ }
+ i = i << 1;
+ }
+ layoutButtons();
+}
+
+const char *QDialogButtonBoxPrivate::standardButtonText(QDialogButtonBox::StandardButton sbutton) const
+{
+ const char *buttonText = 0;
+ bool gnomeLayout = (layoutPolicy == QDialogButtonBox::GnomeLayout);
+ switch (sbutton) {
+ case QDialogButtonBox::Ok:
+ buttonText = gnomeLayout ? QT_TRANSLATE_NOOP("QDialogButtonBox", "&OK") : QT_TRANSLATE_NOOP("QDialogButtonBox", "OK");
+ break;
+ case QDialogButtonBox::Save:
+ buttonText = gnomeLayout ? QT_TRANSLATE_NOOP("QDialogButtonBox", "&Save") : QT_TRANSLATE_NOOP("QDialogButtonBox", "Save");
+ break;
+ case QDialogButtonBox::Open:
+ buttonText = QT_TRANSLATE_NOOP("QDialogButtonBox", "Open");
+ break;
+ case QDialogButtonBox::Cancel:
+ buttonText = gnomeLayout ? QT_TRANSLATE_NOOP("QDialogButtonBox", "&Cancel") : QT_TRANSLATE_NOOP("QDialogButtonBox", "Cancel");
+ break;
+ case QDialogButtonBox::Close:
+ buttonText = gnomeLayout ? QT_TRANSLATE_NOOP("QDialogButtonBox", "&Close") : QT_TRANSLATE_NOOP("QDialogButtonBox", "Close");
+ break;
+ case QDialogButtonBox::Apply:
+ buttonText = QT_TRANSLATE_NOOP("QDialogButtonBox", "Apply");
+ break;
+ case QDialogButtonBox::Reset:
+ buttonText = QT_TRANSLATE_NOOP("QDialogButtonBox", "Reset");
+ break;
+ case QDialogButtonBox::Help:
+ buttonText = QT_TRANSLATE_NOOP("QDialogButtonBox", "Help");
+ break;
+ case QDialogButtonBox::Discard:
+ if (layoutPolicy == QDialogButtonBox::MacLayout)
+ buttonText = QT_TRANSLATE_NOOP("QDialogButtonBox", "Don't Save");
+ else if (layoutPolicy == QDialogButtonBox::GnomeLayout)
+ buttonText = QT_TRANSLATE_NOOP("QDialogButtonBox", "Close without Saving");
+ else
+ buttonText = QT_TRANSLATE_NOOP("QDialogButtonBox", "Discard");
+ break;
+ case QDialogButtonBox::Yes:
+ buttonText = QT_TRANSLATE_NOOP("QDialogButtonBox", "&Yes");
+ break;
+ case QDialogButtonBox::YesToAll:
+ buttonText = QT_TRANSLATE_NOOP("QDialogButtonBox", "Yes to &All");
+ break;
+ case QDialogButtonBox::No:
+ buttonText = QT_TRANSLATE_NOOP("QDialogButtonBox", "&No");
+ break;
+ case QDialogButtonBox::NoToAll:
+ buttonText = QT_TRANSLATE_NOOP("QDialogButtonBox", "N&o to All");
+ break;
+ case QDialogButtonBox::SaveAll:
+ buttonText = QT_TRANSLATE_NOOP("QDialogButtonBox", "Save All");
+ break;
+ case QDialogButtonBox::Abort:
+ buttonText = QT_TRANSLATE_NOOP("QDialogButtonBox", "Abort");
+ break;
+ case QDialogButtonBox::Retry:
+ buttonText = QT_TRANSLATE_NOOP("QDialogButtonBox", "Retry");
+ break;
+ case QDialogButtonBox::Ignore:
+ buttonText = QT_TRANSLATE_NOOP("QDialogButtonBox", "Ignore");
+ break;
+ case QDialogButtonBox::RestoreDefaults:
+ buttonText = QT_TRANSLATE_NOOP("QDialogButtonBox", "Restore Defaults");
+ break;
+ case QDialogButtonBox::NoButton:
+ ;
+ } // switch
+ return buttonText;
+}
+
+void QDialogButtonBoxPrivate::retranslateStrings()
+{
+ const char *buttonText = 0;
+ QHash<QPushButton *, QDialogButtonBox::StandardButton>::iterator it = standardButtonHash.begin();
+ while (it != standardButtonHash.end()) {
+ buttonText = standardButtonText(it.value());
+ if (buttonText) {
+ QPushButton *button = it.key();
+ button->setText(QDialogButtonBox::tr(buttonText));
+ }
+ ++it;
+ }
+}
+
+/*!
+ Constructs an empty, horizontal button box with the given \a parent.
+
+ \sa orientation, addButton()
+*/
+QDialogButtonBox::QDialogButtonBox(QWidget *parent)
+ : QWidget(*new QDialogButtonBoxPrivate(Qt::Horizontal), parent, 0)
+{
+ d_func()->initLayout();
+}
+
+/*!
+ Constructs an empty button box with the given \a orientation and \a parent.
+
+ \sa orientation, addButton()
+*/
+QDialogButtonBox::QDialogButtonBox(Qt::Orientation orientation, QWidget *parent)
+ : QWidget(*new QDialogButtonBoxPrivate(orientation), parent, 0)
+{
+ d_func()->initLayout();
+}
+
+/*!
+ Constructs a button box with the given \a orientation and \a parent, containing
+ the standard buttons specified by \a buttons.
+
+ \sa orientation, addButton()
+*/
+QDialogButtonBox::QDialogButtonBox(StandardButtons buttons, Qt::Orientation orientation,
+ QWidget *parent)
+ : QWidget(*new QDialogButtonBoxPrivate(orientation), parent, 0)
+{
+ d_func()->initLayout();
+ d_func()->createStandardButtons(buttons);
+}
+
+/*!
+ Destroys the button box.
+*/
+QDialogButtonBox::~QDialogButtonBox()
+{
+}
+
+/*!
+ \enum QDialogButtonBox::ButtonRole
+ \enum QMessageBox::ButtonRole
+
+ This enum describes the roles that can be used to describe buttons in
+ the button box. Combinations of these roles are as flags used to
+ describe different aspects of their behavior.
+
+ \value InvalidRole The button is invalid.
+ \value AcceptRole Clicking the button causes the dialog to be accepted
+ (e.g. OK).
+ \value RejectRole Clicking the button causes the dialog to be rejected
+ (e.g. Cancel).
+ \value DestructiveRole Clicking the button causes a destructive change
+ (e.g. for Discarding Changes) and closes the dialog.
+ \value ActionRole Clicking the button causes changes to the elements within
+ the dialog.
+ \value HelpRole The button can be clicked to request help.
+ \value YesRole The button is a "Yes"-like button.
+ \value NoRole The button is a "No"-like button.
+ \value ApplyRole The button applies current changes.
+ \value ResetRole The button resets the dialog's fields to default values.
+
+ \omitvalue NRoles
+
+ \sa StandardButton
+*/
+
+/*!
+ \enum QDialogButtonBox::StandardButton
+
+ These enums describe flags for standard buttons. Each button has a
+ defined \l ButtonRole.
+
+ \value Ok An "OK" button defined with the \l AcceptRole.
+ \value Open A "Open" button defined with the \l AcceptRole.
+ \value Save A "Save" button defined with the \l AcceptRole.
+ \value Cancel A "Cancel" button defined with the \l RejectRole.
+ \value Close A "Close" button defined with the \l RejectRole.
+ \value Discard A "Discard" or "Don't Save" button, depending on the platform,
+ defined with the \l DestructiveRole.
+ \value Apply An "Apply" button defined with the \l ApplyRole.
+ \value Reset A "Reset" button defined with the \l ResetRole.
+ \value RestoreDefaults A "Restore Defaults" button defined with the \l ResetRole.
+ \value Help A "Help" button defined with the \l HelpRole.
+ \value SaveAll A "Save All" button defined with the \l AcceptRole.
+ \value Yes A "Yes" button defined with the \l YesRole.
+ \value YesToAll A "Yes to All" button defined with the \l YesRole.
+ \value No A "No" button defined with the \l NoRole.
+ \value NoToAll A "No to All" button defined with the \l NoRole.
+ \value Abort An "Abort" button defined with the \l RejectRole.
+ \value Retry A "Retry" button defined with the \l AcceptRole.
+ \value Ignore An "Ignore" button defined with the \l AcceptRole.
+
+ \value NoButton An invalid button.
+
+ \omitvalue FirstButton
+ \omitvalue LastButton
+
+ \sa ButtonRole, standardButtons
+*/
+
+/*!
+ \enum QDialogButtonBox::ButtonLayout
+
+ This enum describes the layout policy to be used when arranging the buttons
+ contained in the button box.
+
+ \value WinLayout Use a policy appropriate for applications on Windows.
+ \value MacLayout Use a policy appropriate for applications on Mac OS X.
+ \value KdeLayout Use a policy appropriate for applications on KDE.
+ \value GnomeLayout Use a policy appropriate for applications on GNOME.
+
+ The button layout is specified by the \l{style()}{current style}.
+*/
+
+/*!
+ \fn void QDialogButtonBox::clicked(QAbstractButton *button)
+
+ This signal is emitted when a button inside the button box is clicked. The
+ specific button that was pressed is specified by \a button.
+
+ \sa accepted(), rejected(), helpRequested()
+*/
+
+/*!
+ \fn void QDialogButtonBox::accepted()
+
+ This signal is emitted when a button inside the button box is clicked, as long
+ as it was defined with the \l AcceptRole or \l YesRole.
+
+ \sa rejected(), clicked() helpRequested()
+*/
+
+/*!
+ \fn void QDialogButtonBox::rejected()
+
+ This signal is emitted when a button inside the button box is clicked, as long
+ as it was defined with the \l RejectRole or \l NoRole.
+
+ \sa accepted() helpRequested() clicked()
+*/
+
+/*!
+ \fn void QDialogButtonBox::helpRequested()
+
+ This signal is emitted when a button inside the button box is clicked, as long
+ as it was defined with the \l HelpRole.
+
+ \sa accepted() rejected() clicked()
+*/
+
+/*!
+ \property QDialogButtonBox::orientation
+ \brief the orientation of the button box
+
+ By default, the orientation is horizontal (i.e. the buttons are laid out
+ side by side). The possible orientations are Qt::Horizontal and
+ Qt::Vertical.
+*/
+Qt::Orientation QDialogButtonBox::orientation() const
+{
+ return d_func()->orientation;
+}
+
+void QDialogButtonBox::setOrientation(Qt::Orientation orientation)
+{
+ Q_D(QDialogButtonBox);
+ if (orientation == d->orientation)
+ return;
+
+ d->orientation = orientation;
+ d->resetLayout();
+}
+
+/*!
+ Clears the button box, deleting all buttons within it.
+
+ \sa removeButton(), addButton()
+*/
+void QDialogButtonBox::clear()
+{
+ Q_D(QDialogButtonBox);
+ // Remove the created standard buttons, they should be in the other lists, which will
+ // do the deletion
+ d->standardButtonHash.clear();
+ for (int i = 0; i < NRoles; ++i) {
+ QList<QAbstractButton *> &list = d->buttonLists[i];
+ while (list.count()) {
+ QAbstractButton *button = list.takeAt(0);
+ QObject::disconnect(button, SIGNAL(destroyed()), this, SLOT(_q_handleButtonDestroyed()));
+ delete button;
+ }
+ }
+}
+
+/*!
+ Returns a list of all the buttons that have been added to the button box.
+
+ \sa buttonRole(), addButton(), removeButton()
+*/
+QList<QAbstractButton *> QDialogButtonBox::buttons() const
+{
+ Q_D(const QDialogButtonBox);
+ QList<QAbstractButton *> finalList;
+ for (int i = 0; i < NRoles; ++i) {
+ const QList<QAbstractButton *> &list = d->buttonLists[i];
+ for (int j = 0; j < list.count(); ++j)
+ finalList.append(list.at(j));
+ }
+ return finalList;
+}
+
+/*!
+ Returns the button role for the specified \a button. This function returns
+ \l InvalidRole if \a button is 0 or has not been added to the button box.
+
+ \sa buttons(), addButton()
+*/
+QDialogButtonBox::ButtonRole QDialogButtonBox::buttonRole(QAbstractButton *button) const
+{
+ Q_D(const QDialogButtonBox);
+ for (int i = 0; i < NRoles; ++i) {
+ const QList<QAbstractButton *> &list = d->buttonLists[i];
+ for (int j = 0; j < list.count(); ++j) {
+ if (list.at(j) == button)
+ return ButtonRole(i);
+ }
+ }
+ return InvalidRole;
+}
+
+/*!
+ Removes \a button from the button box without deleting it and sets its parent to zero.
+
+ \sa clear(), buttons(), addButton()
+*/
+void QDialogButtonBox::removeButton(QAbstractButton *button)
+{
+ Q_D(QDialogButtonBox);
+
+ if (!button)
+ return;
+
+ // Remove it from the standard button hash first and then from the roles
+ if (QPushButton *pushButton = qobject_cast<QPushButton *>(button))
+ d->standardButtonHash.remove(pushButton);
+ for (int i = 0; i < NRoles; ++i) {
+ QList<QAbstractButton *> &list = d->buttonLists[i];
+ for (int j = 0; j < list.count(); ++j) {
+ if (list.at(j) == button) {
+ list.takeAt(j);
+ if (!d->internalRemove) {
+ disconnect(button, SIGNAL(clicked()), this, SLOT(_q_handleButtonClicked()));
+ disconnect(button, SIGNAL(destroyed()), this, SLOT(_q_handleButtonDestroyed()));
+ }
+ break;
+ }
+ }
+ }
+ if (!d->internalRemove)
+ button->setParent(0);
+}
+
+/*!
+ Adds the given \a button to the button box with the specified \a role.
+ If the role is invalid, the button is not added.
+
+ If the button has already been added, it is removed and added again with the
+ new role.
+
+ \sa removeButton(), clear()
+*/
+void QDialogButtonBox::addButton(QAbstractButton *button, ButtonRole role)
+{
+ Q_D(QDialogButtonBox);
+ if (role <= InvalidRole || role >= NRoles) {
+ qWarning("QDialogButtonBox::addButton: Invalid ButtonRole, button not added");
+ return;
+ }
+ removeButton(button);
+ button->setParent(this);
+ d->addButton(button, role);
+}
+
+/*!
+ Creates a push button with the given \a text, adds it to the button box for the
+ specified \a role, and returns the corresponding push button. If \a role is
+ invalid, no button is created, and zero is returned.
+
+ \sa removeButton(), clear()
+*/
+QPushButton *QDialogButtonBox::addButton(const QString &text, ButtonRole role)
+{
+ Q_D(QDialogButtonBox);
+ if (role <= InvalidRole || role >= NRoles) {
+ qWarning("QDialogButtonBox::addButton: Invalid ButtonRole, button not added");
+ return 0;
+ }
+ QPushButton *button = new QPushButton(text, this);
+ d->addButton(button, role);
+ return button;
+}
+
+/*!
+ Adds a standard \a button to the button box if it is valid to do so, and returns
+ a push button. If \a button is invalid, it is not added to the button box, and
+ zero is returned.
+
+ \sa removeButton(), clear()
+*/
+QPushButton *QDialogButtonBox::addButton(StandardButton button)
+{
+ Q_D(QDialogButtonBox);
+ return d->createButton(button);
+}
+
+/*!
+ \property QDialogButtonBox::standardButtons
+ \brief collection of standard buttons in the button box
+
+ This property controls which standard buttons are used by the button box.
+
+ \sa addButton()
+*/
+void QDialogButtonBox::setStandardButtons(StandardButtons buttons)
+{
+ Q_D(QDialogButtonBox);
+ // Clear out all the old standard buttons, then recreate them.
+ qDeleteAll(d->standardButtonHash.keys());
+ d->standardButtonHash.clear();
+
+ d->createStandardButtons(buttons);
+}
+
+QDialogButtonBox::StandardButtons QDialogButtonBox::standardButtons() const
+{
+ Q_D(const QDialogButtonBox);
+ StandardButtons standardButtons = NoButton;
+ QHash<QPushButton *, StandardButton>::const_iterator it = d->standardButtonHash.constBegin();
+ while (it != d->standardButtonHash.constEnd()) {
+ standardButtons |= it.value();
+ ++it;
+ }
+ return standardButtons;
+}
+
+/*!
+ Returns the QPushButton corresponding to the standard button \a which,
+ or 0 if the standard button doesn't exist in this button box.
+
+ \sa standardButton(), standardButtons(), buttons()
+*/
+QPushButton *QDialogButtonBox::button(StandardButton which) const
+{
+ Q_D(const QDialogButtonBox);
+ return d->standardButtonHash.key(which);
+}
+
+/*!
+ Returns the standard button enum value corresponding to the given \a button,
+ or NoButton if the given \a button isn't a standard button.
+
+ \sa button(), buttons(), standardButtons()
+*/
+QDialogButtonBox::StandardButton QDialogButtonBox::standardButton(QAbstractButton *button) const
+{
+ Q_D(const QDialogButtonBox);
+ return d->standardButtonHash.value(static_cast<QPushButton *>(button));
+}
+
+void QDialogButtonBoxPrivate::_q_handleButtonClicked()
+{
+ Q_Q(QDialogButtonBox);
+ if (QAbstractButton *button = qobject_cast<QAbstractButton *>(q->sender())) {
+ emit q->clicked(button);
+
+ switch (q->buttonRole(button)) {
+ case AcceptRole:
+ case YesRole:
+ emit q->accepted();
+ break;
+ case RejectRole:
+ case NoRole:
+ emit q->rejected();
+ break;
+ case HelpRole:
+ emit q->helpRequested();
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+void QDialogButtonBoxPrivate::_q_handleButtonDestroyed()
+{
+ Q_Q(QDialogButtonBox);
+ if (QObject *object = q->sender()) {
+ QBoolBlocker skippy(internalRemove);
+ q->removeButton(static_cast<QAbstractButton *>(object));
+ }
+}
+
+/*!
+ \property QDialogButtonBox::centerButtons
+ \brief whether the buttons in the button box are centered
+
+ By default, this property is false. This behavior is appopriate
+ for most types of dialogs. A notable exception is message boxes
+ on most platforms (e.g. Windows), where the button box is
+ centered horizontally.
+
+ \sa QMessageBox
+*/
+void QDialogButtonBox::setCenterButtons(bool center)
+{
+ Q_D(QDialogButtonBox);
+ if (d->center != center) {
+ d->center = center;
+ d->resetLayout();
+ }
+}
+
+bool QDialogButtonBox::centerButtons() const
+{
+ Q_D(const QDialogButtonBox);
+ return d->center;
+}
+
+/*!
+ \reimp
+*/
+void QDialogButtonBox::changeEvent(QEvent *event)
+{
+ typedef QHash<QPushButton *, QDialogButtonBox::StandardButton> StandardButtonHash;
+
+ Q_D(QDialogButtonBox);
+ switch (event->type()) {
+ case QEvent::StyleChange: // Propagate style
+ if (!d->standardButtonHash.empty()) {
+ QStyle *newStyle = style();
+ const StandardButtonHash::iterator end = d->standardButtonHash.end();
+ for (StandardButtonHash::iterator it = d->standardButtonHash.begin(); it != end; ++it)
+ it.key()->setStyle(newStyle);
+ }
+ // fallthrough intended
+#ifdef Q_WS_MAC
+ case QEvent::MacSizeChange:
+#endif
+ d->resetLayout();
+ QWidget::changeEvent(event);
+ break;
+ default:
+ QWidget::changeEvent(event);
+ break;
+ }
+}
+
+/*!
+ \reimp
+*/
+bool QDialogButtonBox::event(QEvent *event)
+{
+ Q_D(QDialogButtonBox);
+ if (event->type() == QEvent::Show) {
+ QList<QAbstractButton *> acceptRoleList = d->buttonLists[AcceptRole];
+ QPushButton *firstAcceptButton = acceptRoleList.isEmpty() ? 0 : qobject_cast<QPushButton *>(acceptRoleList.at(0));
+ bool hasDefault = false;
+ QWidget *dialog = 0;
+ QWidget *p = this;
+ while (p && !p->isWindow()) {
+ p = p->parentWidget();
+ if ((dialog = qobject_cast<QDialog *>(p)))
+ break;
+ }
+
+ foreach (QPushButton *pb, qFindChildren<QPushButton *>(dialog ? dialog : this)) {
+ if (pb->isDefault() && pb != firstAcceptButton) {
+ hasDefault = true;
+ break;
+ }
+ }
+ if (!hasDefault && firstAcceptButton)
+ firstAcceptButton->setDefault(true);
+ }else if (event->type() == QEvent::LanguageChange) {
+ d->retranslateStrings();
+ }
+
+ return QWidget::event(event);
+}
+
+QT_END_NAMESPACE
+
+#include "moc_qdialogbuttonbox.cpp"
diff --git a/src/gui/widgets/qdialogbuttonbox.h b/src/gui/widgets/qdialogbuttonbox.h
new file mode 100644
index 0000000000..c4f3cf5c4c
--- /dev/null
+++ b/src/gui/widgets/qdialogbuttonbox.h
@@ -0,0 +1,168 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QDIALOGBUTTONBOX_H
+#define QDIALOGBUTTONBOX_H
+
+#include <QtGui/qwidget.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+class QAbstractButton;
+class QPushButton;
+class QDialogButtonBoxPrivate;
+
+class Q_GUI_EXPORT QDialogButtonBox : public QWidget
+{
+ Q_OBJECT
+ Q_FLAGS(StandardButtons)
+ Q_PROPERTY(Qt::Orientation orientation READ orientation WRITE setOrientation)
+ Q_PROPERTY(StandardButtons standardButtons READ standardButtons WRITE setStandardButtons)
+ Q_PROPERTY(bool centerButtons READ centerButtons WRITE setCenterButtons)
+
+public:
+ enum ButtonRole {
+ // keep this in sync with QMessageBox::ButtonRole
+ InvalidRole = -1,
+ AcceptRole,
+ RejectRole,
+ DestructiveRole,
+ ActionRole,
+ HelpRole,
+ YesRole,
+ NoRole,
+ ResetRole,
+ ApplyRole,
+
+ NRoles
+ };
+
+ enum StandardButton {
+ // keep this in sync with QMessageBox::StandardButton
+ NoButton = 0x00000000,
+ Ok = 0x00000400,
+ Save = 0x00000800,
+ SaveAll = 0x00001000,
+ Open = 0x00002000,
+ Yes = 0x00004000,
+ YesToAll = 0x00008000,
+ No = 0x00010000,
+ NoToAll = 0x00020000,
+ Abort = 0x00040000,
+ Retry = 0x00080000,
+ Ignore = 0x00100000,
+ Close = 0x00200000,
+ Cancel = 0x00400000,
+ Discard = 0x00800000,
+ Help = 0x01000000,
+ Apply = 0x02000000,
+ Reset = 0x04000000,
+ RestoreDefaults = 0x08000000,
+
+#ifndef Q_MOC_RUN
+ FirstButton = Ok,
+ LastButton = RestoreDefaults
+#endif
+ };
+
+ Q_DECLARE_FLAGS(StandardButtons, StandardButton)
+
+ enum ButtonLayout {
+ WinLayout,
+ MacLayout,
+ KdeLayout,
+ GnomeLayout
+ };
+
+ QDialogButtonBox(QWidget *parent = 0);
+ QDialogButtonBox(Qt::Orientation orientation, QWidget *parent = 0);
+ QDialogButtonBox(StandardButtons buttons, Qt::Orientation orientation = Qt::Horizontal,
+ QWidget *parent = 0);
+ ~QDialogButtonBox();
+
+ void setOrientation(Qt::Orientation orientation);
+ Qt::Orientation orientation() const;
+
+ void addButton(QAbstractButton *button, ButtonRole role);
+ QPushButton *addButton(const QString &text, ButtonRole role);
+ QPushButton *addButton(StandardButton button);
+ void removeButton(QAbstractButton *button);
+ void clear();
+
+ QList<QAbstractButton *> buttons() const;
+ ButtonRole buttonRole(QAbstractButton *button) const;
+
+ void setStandardButtons(StandardButtons buttons);
+ StandardButtons standardButtons() const;
+ StandardButton standardButton(QAbstractButton *button) const;
+ QPushButton *button(StandardButton which) const;
+
+ void setCenterButtons(bool center);
+ bool centerButtons() const;
+
+Q_SIGNALS:
+ void clicked(QAbstractButton *button);
+ void accepted();
+ void helpRequested();
+ void rejected();
+
+protected:
+ void changeEvent(QEvent *event);
+ bool event(QEvent *event);
+
+private:
+ Q_DISABLE_COPY(QDialogButtonBox)
+ Q_DECLARE_PRIVATE(QDialogButtonBox)
+ Q_PRIVATE_SLOT(d_func(), void _q_handleButtonClicked())
+ Q_PRIVATE_SLOT(d_func(), void _q_handleButtonDestroyed())
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(QDialogButtonBox::StandardButtons)
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QDIALOGBUTTONBOX_H
diff --git a/src/gui/widgets/qdockarealayout.cpp b/src/gui/widgets/qdockarealayout.cpp
new file mode 100644
index 0000000000..9261c63054
--- /dev/null
+++ b/src/gui/widgets/qdockarealayout.cpp
@@ -0,0 +1,3316 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "QtGui/qapplication.h"
+#include "QtGui/qwidget.h"
+#include "QtGui/qtabbar.h"
+#include "QtGui/qstyle.h"
+#include "QtGui/qdesktopwidget.h"
+#include "QtCore/qvariant.h"
+#include "qdockarealayout_p.h"
+#include "qdockwidget.h"
+#include "qmainwindow.h"
+#include "qwidgetanimator_p.h"
+#include "qmainwindowlayout_p.h"
+#include "qdockwidget_p.h"
+#include <private/qlayoutengine_p.h>
+
+#include <qpainter.h>
+#include <qstyleoption.h>
+
+#ifndef QT_NO_DOCKWIDGET
+
+QT_BEGIN_NAMESPACE
+
+enum { StateFlagVisible = 1, StateFlagFloating = 2 };
+
+/******************************************************************************
+** QPlaceHolderItem
+*/
+
+QPlaceHolderItem::QPlaceHolderItem(QWidget *w)
+{
+ objectName = w->objectName();
+ hidden = w->isHidden();
+ window = w->isWindow();
+ if (window)
+ topLevelRect = w->geometry();
+}
+
+/******************************************************************************
+** QDockAreaLayoutItem
+*/
+
+QDockAreaLayoutItem::QDockAreaLayoutItem(QLayoutItem *_widgetItem)
+ : widgetItem(_widgetItem), subinfo(0), placeHolderItem(0), pos(0), size(-1), flags(NoFlags)
+{
+}
+
+QDockAreaLayoutItem::QDockAreaLayoutItem(QDockAreaLayoutInfo *_subinfo)
+ : widgetItem(0), subinfo(_subinfo), placeHolderItem(0), pos(0), size(-1), flags(NoFlags)
+{
+}
+
+QDockAreaLayoutItem::QDockAreaLayoutItem(QPlaceHolderItem *_placeHolderItem)
+ : widgetItem(0), subinfo(0), placeHolderItem(_placeHolderItem), pos(0), size(-1), flags(NoFlags)
+{
+}
+
+QDockAreaLayoutItem::QDockAreaLayoutItem(const QDockAreaLayoutItem &other)
+ : widgetItem(other.widgetItem), subinfo(0), placeHolderItem(0), pos(other.pos),
+ size(other.size), flags(other.flags)
+{
+ if (other.subinfo != 0)
+ subinfo = new QDockAreaLayoutInfo(*other.subinfo);
+ else if (other.placeHolderItem != 0)
+ placeHolderItem = new QPlaceHolderItem(*other.placeHolderItem);
+}
+
+QDockAreaLayoutItem::~QDockAreaLayoutItem()
+{
+ delete subinfo;
+ delete placeHolderItem;
+}
+
+bool QDockAreaLayoutItem::skip() const
+{
+ if (placeHolderItem != 0)
+ return true;
+
+ if (flags & GapItem)
+ return false;
+
+ if (widgetItem != 0)
+ return widgetItem->isEmpty();
+
+ if (subinfo != 0) {
+ for (int i = 0; i < subinfo->item_list.count(); ++i) {
+ if (!subinfo->item_list.at(i).skip())
+ return false;
+ }
+ }
+
+ return true;
+}
+
+QSize QDockAreaLayoutItem::minimumSize() const
+{
+ if (widgetItem != 0) {
+ int left, top, right, bottom;
+ widgetItem->widget()->getContentsMargins(&left, &top, &right, &bottom);
+ return widgetItem->minimumSize() + QSize(left+right, top+bottom);
+ }
+ if (subinfo != 0)
+ return subinfo->minimumSize();
+ return QSize(0, 0);
+}
+
+QSize QDockAreaLayoutItem::maximumSize() const
+{
+ if (widgetItem != 0) {
+ int left, top, right, bottom;
+ widgetItem->widget()->getContentsMargins(&left, &top, &right, &bottom);
+ return widgetItem->maximumSize()+ QSize(left+right, top+bottom);
+ }
+ if (subinfo != 0)
+ return subinfo->maximumSize();
+ return QSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX);
+}
+
+bool QDockAreaLayoutItem::expansive(Qt::Orientation o) const
+{
+ if ((flags & GapItem) || placeHolderItem != 0)
+ return false;
+ if (widgetItem != 0)
+ return ((widgetItem->expandingDirections() & o) == o);
+ if (subinfo != 0)
+ return subinfo->expansive(o);
+ return false;
+}
+
+QSize QDockAreaLayoutItem::sizeHint() const
+{
+ if (placeHolderItem != 0)
+ return QSize(0, 0);
+ if (widgetItem != 0) {
+ int left, top, right, bottom;
+ widgetItem->widget()->getContentsMargins(&left, &top, &right, &bottom);
+ return widgetItem->sizeHint() + QSize(left+right, top+bottom);
+ }
+ if (subinfo != 0)
+ return subinfo->sizeHint();
+ return QSize(-1, -1);
+}
+
+QDockAreaLayoutItem
+ &QDockAreaLayoutItem::operator = (const QDockAreaLayoutItem &other)
+{
+ widgetItem = other.widgetItem;
+ if (other.subinfo == 0)
+ subinfo = 0;
+ else
+ subinfo = new QDockAreaLayoutInfo(*other.subinfo);
+
+ delete placeHolderItem;
+ if (other.placeHolderItem == 0)
+ placeHolderItem = 0;
+ else
+ placeHolderItem = new QPlaceHolderItem(*other.placeHolderItem);
+
+ pos = other.pos;
+ size = other.size;
+ flags = other.flags;
+
+ return *this;
+}
+
+/******************************************************************************
+** QDockAreaLayoutInfo
+*/
+
+#ifndef QT_NO_TABBAR
+static quintptr tabId(const QDockAreaLayoutItem &item)
+{
+ if (item.widgetItem == 0)
+ return 0;
+ return reinterpret_cast<quintptr>(item.widgetItem->widget());
+}
+#endif
+
+QDockAreaLayoutInfo::QDockAreaLayoutInfo()
+ : sep(0), dockPos(QInternal::LeftDock), o(Qt::Horizontal), rect(0, 0, -1, -1), mainWindow(0)
+#ifndef QT_NO_TABBAR
+ , tabbed(false), tabBar(0), tabBarShape(QTabBar::RoundedSouth)
+#endif
+{
+}
+
+QDockAreaLayoutInfo::QDockAreaLayoutInfo(int _sep, QInternal::DockPosition _dockPos,
+ Qt::Orientation _o, int tbshape,
+ QMainWindow *window)
+ : sep(_sep), dockPos(_dockPos), o(_o), rect(0, 0, -1, -1), mainWindow(window)
+#ifndef QT_NO_TABBAR
+ , tabbed(false), tabBar(0), tabBarShape(static_cast<QTabBar::Shape>(tbshape))
+#endif
+{
+#ifdef QT_NO_TABBAR
+ Q_UNUSED(tbshape);
+#endif
+}
+
+QSize QDockAreaLayoutInfo::size() const
+{
+ return isEmpty() ? QSize(0, 0) : rect.size();
+}
+
+void QDockAreaLayoutInfo::clear()
+{
+ item_list.clear();
+ rect = QRect(0, 0, -1, -1);
+#ifndef QT_NO_TABBAR
+ tabbed = false;
+ tabBar = 0;
+#endif
+}
+
+bool QDockAreaLayoutInfo::isEmpty() const
+{
+ return next(-1) == -1;
+}
+
+QSize QDockAreaLayoutInfo::minimumSize() const
+{
+ if (isEmpty())
+ return QSize(0, 0);
+
+ int a = 0, b = 0;
+ bool first = true;
+ for (int i = 0; i < item_list.size(); ++i) {
+ const QDockAreaLayoutItem &item = item_list.at(i);
+ if (item.skip())
+ continue;
+
+ QSize min_size = item.minimumSize();
+#ifndef QT_NO_TABBAR
+ if (tabbed) {
+ a = qMax(a, pick(o, min_size));
+ } else
+#endif
+ {
+ if (!first)
+ a += sep;
+ a += pick(o, min_size);
+ }
+ b = qMax(b, perp(o, min_size));
+
+ first = false;
+ }
+
+ QSize result;
+ rpick(o, result) = a;
+ rperp(o, result) = b;
+
+#ifndef QT_NO_TABBAR
+ if (tabbed) {
+ QSize tbm = tabBarMinimumSize();
+ switch (tabBarShape) {
+ case QTabBar::RoundedNorth:
+ case QTabBar::RoundedSouth:
+ case QTabBar::TriangularNorth:
+ case QTabBar::TriangularSouth:
+ result.rheight() += tbm.height();
+ result.rwidth() = qMax(tbm.width(), result.width());
+ break;
+ case QTabBar::RoundedEast:
+ case QTabBar::RoundedWest:
+ case QTabBar::TriangularEast:
+ case QTabBar::TriangularWest:
+ result.rheight() = qMax(tbm.height(), result.height());
+ result.rwidth() += tbm.width();
+ break;
+ default:
+ break;
+ }
+ }
+#endif // QT_NO_TABBAR
+
+ return result;
+}
+
+QSize QDockAreaLayoutInfo::maximumSize() const
+{
+ if (isEmpty())
+ return QSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX);
+
+ int a = 0, b = QWIDGETSIZE_MAX;
+#ifndef QT_NO_TABBAR
+ if (tabbed)
+ a = QWIDGETSIZE_MAX;
+#endif
+
+ int min_perp = 0;
+
+ bool first = true;
+ for (int i = 0; i < item_list.size(); ++i) {
+ const QDockAreaLayoutItem &item = item_list.at(i);
+ if (item.skip())
+ continue;
+
+ QSize max_size = item.maximumSize();
+ min_perp = qMax(min_perp, perp(o, item.minimumSize()));
+
+#ifndef QT_NO_TABBAR
+ if (tabbed) {
+ a = qMin(a, pick(o, max_size));
+ } else
+#endif
+ {
+ if (!first)
+ a += sep;
+ a += pick(o, max_size);
+ }
+ b = qMin(b, perp(o, max_size));
+
+ a = qMin(a, int(QWIDGETSIZE_MAX));
+ b = qMin(b, int(QWIDGETSIZE_MAX));
+
+ first = false;
+ }
+
+ b = qMax(b, min_perp);
+
+ QSize result;
+ rpick(o, result) = a;
+ rperp(o, result) = b;
+
+#ifndef QT_NO_TABBAR
+ if (tabbed) {
+ QSize tbh = tabBarSizeHint();
+ switch (tabBarShape) {
+ case QTabBar::RoundedNorth:
+ case QTabBar::RoundedSouth:
+ result.rheight() += tbh.height();
+ break;
+ case QTabBar::RoundedEast:
+ case QTabBar::RoundedWest:
+ result.rwidth() += tbh.width();
+ break;
+ default:
+ break;
+ }
+ }
+#endif // QT_NO_TABBAR
+
+ return result;
+}
+
+QSize QDockAreaLayoutInfo::sizeHint() const
+{
+ if (isEmpty())
+ return QSize(0, 0);
+
+ int a = 0, b = 0;
+ bool prev_gap = false;
+ bool first = true;
+ int min_perp = 0;
+ int max_perp = QWIDGETSIZE_MAX;
+ for (int i = 0; i < item_list.size(); ++i) {
+ const QDockAreaLayoutItem &item = item_list.at(i);
+ if (item.skip())
+ continue;
+
+ bool gap = item.flags & QDockAreaLayoutItem::GapItem;
+
+ QSize size_hint = item.sizeHint();
+ min_perp = qMax(min_perp, perp(o, item.minimumSize()));
+ max_perp = qMin(max_perp, perp(o, item.maximumSize()));
+
+#ifndef QT_NO_TABBAR
+ if (tabbed) {
+ a = qMax(a, gap ? item.size : pick(o, size_hint));
+ } else
+#endif
+ {
+ if (!first && !gap && !prev_gap)
+ a += sep;
+ a += gap ? item.size : pick(o, size_hint);
+ }
+ b = qMax(b, perp(o, size_hint));
+
+ prev_gap = gap;
+ first = false;
+ }
+
+ max_perp = qMax(max_perp, min_perp);
+ b = qMax(b, min_perp);
+ b = qMin(b, max_perp);
+
+ QSize result;
+ rpick(o, result) = a;
+ rperp(o, result) = b;
+
+#ifndef QT_NO_TABBAR
+ if (tabbed) {
+ QSize tbh = tabBarSizeHint();
+ switch (tabBarShape) {
+ case QTabBar::RoundedNorth:
+ case QTabBar::RoundedSouth:
+ case QTabBar::TriangularNorth:
+ case QTabBar::TriangularSouth:
+ result.rheight() += tbh.height();
+ result.rwidth() = qMax(tbh.width(), result.width());
+ break;
+ case QTabBar::RoundedEast:
+ case QTabBar::RoundedWest:
+ case QTabBar::TriangularEast:
+ case QTabBar::TriangularWest:
+ result.rheight() = qMax(tbh.height(), result.height());
+ result.rwidth() += tbh.width();
+ break;
+ default:
+ break;
+ }
+ }
+#endif // QT_NO_TABBAR
+
+ return result;
+}
+
+bool QDockAreaLayoutInfo::expansive(Qt::Orientation o) const
+{
+ for (int i = 0; i < item_list.size(); ++i) {
+ if (item_list.at(i).expansive(o))
+ return true;
+ }
+ return false;
+}
+
+/* QDockAreaLayoutInfo::maximumSize() doesn't return the real max size. For example,
+ if the layout is empty, it returns QWIDGETSIZE_MAX. This is so that empty dock areas
+ don't constrain the size of the QMainWindow, but sometimes we really need to know the
+ maximum size. Also, these functions take into account widgets that want to keep their
+ size (f.ex. when they are hidden and then shown, they should not change size).
+*/
+
+static int realMinSize(const QDockAreaLayoutInfo &info)
+{
+ int result = 0;
+ bool first = true;
+ for (int i = 0; i < info.item_list.size(); ++i) {
+ const QDockAreaLayoutItem &item = info.item_list.at(i);
+ if (item.skip())
+ continue;
+
+ int min = 0;
+ if ((item.flags & QDockAreaLayoutItem::KeepSize) && item.size != -1)
+ min = item.size;
+ else
+ min = pick(info.o, item.minimumSize());
+
+ if (!first)
+ result += info.sep;
+ result += min;
+
+ first = false;
+ }
+
+ return result;
+}
+
+static int realMaxSize(const QDockAreaLayoutInfo &info)
+{
+ int result = 0;
+ bool first = true;
+ for (int i = 0; i < info.item_list.size(); ++i) {
+ const QDockAreaLayoutItem &item = info.item_list.at(i);
+ if (item.skip())
+ continue;
+
+ int max = 0;
+ if ((item.flags & QDockAreaLayoutItem::KeepSize) && item.size != -1)
+ max = item.size;
+ else
+ max = pick(info.o, item.maximumSize());
+
+ if (!first)
+ result += info.sep;
+ result += max;
+
+ if (result >= QWIDGETSIZE_MAX)
+ return QWIDGETSIZE_MAX;
+
+ first = false;
+ }
+
+ return result;
+}
+
+void QDockAreaLayoutInfo::fitItems()
+{
+#ifndef QT_NO_TABBAR
+ if (tabbed) {
+ return;
+ }
+#endif
+
+ QVector<QLayoutStruct> layout_struct_list(item_list.size()*2);
+ int j = 0;
+
+ int size = pick(o, rect.size());
+ int min_size = realMinSize(*this);
+ int max_size = realMaxSize(*this);
+ int last_index = -1;
+
+ bool prev_gap = false;
+ bool first = true;
+ for (int i = 0; i < item_list.size(); ++i) {
+ QDockAreaLayoutItem &item = item_list[i];
+ if (item.skip())
+ continue;
+
+ bool gap = item.flags & QDockAreaLayoutItem::GapItem;
+ if (!first && !gap && !prev_gap) {
+ QLayoutStruct &ls = layout_struct_list[j++];
+ ls.init();
+ ls.minimumSize = sep;
+ ls.maximumSize = sep;
+ ls.sizeHint = sep;
+ ls.empty = false;
+ }
+
+ if (item.flags & QDockAreaLayoutItem::KeepSize) {
+ // Check if the item can keep its size, without violating size constraints
+ // of other items.
+
+ if (size < min_size) {
+ // There is too little space to keep this widget's size
+ item.flags &= ~QDockAreaLayoutItem::KeepSize;
+ min_size -= item.size;
+ min_size += pick(o, item.minimumSize());
+ min_size = qMax(0, min_size);
+ } else if (size > max_size) {
+ // There is too much space to keep this widget's size
+ item.flags &= ~QDockAreaLayoutItem::KeepSize;
+ max_size -= item.size;
+ max_size += pick(o, item.maximumSize());
+ max_size = qMin<int>(QWIDGETSIZE_MAX, max_size);
+ }
+ }
+
+ last_index = j;
+ QLayoutStruct &ls = layout_struct_list[j++];
+ ls.init();
+ ls.empty = false;
+ if (gap || (item.flags & QDockAreaLayoutItem::KeepSize)) {
+ ls.minimumSize = ls.maximumSize = ls.sizeHint = item.size;
+ ls.expansive = false;
+ ls.stretch = 0;
+ } else {
+ ls.maximumSize = pick(o, item.maximumSize());
+ ls.expansive = item.expansive(o);
+ ls.minimumSize = pick(o, item.minimumSize());
+ ls.sizeHint = item.size == -1 ? pick(o, item.sizeHint()) : item.size;
+ ls.stretch = ls.expansive ? ls.sizeHint : 0;
+ }
+
+ item.flags &= ~QDockAreaLayoutItem::KeepSize;
+ prev_gap = gap;
+ first = false;
+ }
+ layout_struct_list.resize(j);
+
+ // If there is more space than the widgets can take (due to maximum size constraints),
+ // we detect it here and stretch the last widget to take up the rest of the space.
+ if (size > max_size && last_index != -1) {
+ layout_struct_list[last_index].maximumSize = QWIDGETSIZE_MAX;
+ layout_struct_list[last_index].expansive = true;
+ }
+
+ qGeomCalc(layout_struct_list, 0, j, pick(o, rect.topLeft()), size, 0);
+
+ j = 0;
+ prev_gap = false;
+ first = true;
+ for (int i = 0; i < item_list.size(); ++i) {
+ QDockAreaLayoutItem &item = item_list[i];
+ if (item.skip())
+ continue;
+
+ bool gap = item.flags & QDockAreaLayoutItem::GapItem;
+ if (!first && !gap && !prev_gap)
+ ++j;
+
+ const QLayoutStruct &ls = layout_struct_list.at(j++);
+ item.size = ls.size;
+ item.pos = ls.pos;
+
+ if (item.subinfo != 0) {
+ item.subinfo->rect = itemRect(i);
+ item.subinfo->fitItems();
+ }
+
+ prev_gap = gap;
+ first = false;
+ }
+}
+
+static QInternal::DockPosition dockPosHelper(const QRect &rect, const QPoint &_pos,
+ Qt::Orientation o,
+ bool nestingEnabled,
+ QDockAreaLayoutInfo::TabMode tabMode)
+{
+ if (tabMode == QDockAreaLayoutInfo::ForceTabs)
+ return QInternal::DockCount;
+
+ QPoint pos = _pos - rect.topLeft();
+
+ int x = pos.x();
+ int y = pos.y();
+ int w = rect.width();
+ int h = rect.height();
+
+ if (tabMode != QDockAreaLayoutInfo::NoTabs) {
+ // is it in the center?
+ if (nestingEnabled) {
+ /* 2/3
+ +--------------+
+ | |
+ | CCCCCCCC |
+ 2/3 | CCCCCCCC |
+ | CCCCCCCC |
+ | |
+ +--------------+ */
+
+ QRect center(w/6, h/6, 2*w/3, 2*h/3);
+ if (center.contains(pos))
+ return QInternal::DockCount;
+ } else if (o == Qt::Horizontal) {
+ /* 2/3
+ +--------------+
+ | CCCCCCCC |
+ | CCCCCCCC |
+ | CCCCCCCC |
+ | CCCCCCCC |
+ | CCCCCCCC |
+ +--------------+ */
+
+ if (x > w/6 && x < w*5/6)
+ return QInternal::DockCount;
+ } else {
+ /*
+ +--------------+
+ | |
+ 2/3 |CCCCCCCCCCCCCC|
+ |CCCCCCCCCCCCCC|
+ | |
+ +--------------+ */
+ if (y > h/6 && y < 5*h/6)
+ return QInternal::DockCount;
+ }
+ }
+
+ // not in the center. which edge?
+ if (nestingEnabled) {
+ if (o == Qt::Horizontal) {
+ /* 1/3 1/3 1/3
+ +------------+ (we've already ruled out the center)
+ |LLLLTTTTRRRR|
+ |LLLLTTTTRRRR|
+ |LLLLBBBBRRRR|
+ |LLLLBBBBRRRR|
+ +------------+ */
+
+ if (x < w/3)
+ return QInternal::LeftDock;
+ if (x > 2*w/3)
+ return QInternal::RightDock;
+ if (y < h/2)
+ return QInternal::TopDock;
+ return QInternal::BottomDock;
+ } else {
+ /* +------------+ (we've already ruled out the center)
+ 1/3 |TTTTTTTTTTTT|
+ |LLLLLLRRRRRR|
+ 1/3 |LLLLLLRRRRRR|
+ 1/3 |BBBBBBBBBBBB|
+ +------------+ */
+
+ if (y < h/3)
+ return QInternal::TopDock;
+ if (y > 2*h/3)
+ return QInternal::BottomDock;
+ if (x < w/2)
+ return QInternal::LeftDock;
+ return QInternal::RightDock;
+ }
+ } else {
+ if (o == Qt::Horizontal) {
+ return x < w/2
+ ? QInternal::LeftDock
+ : QInternal::RightDock;
+ } else {
+ return y < h/2
+ ? QInternal::TopDock
+ : QInternal::BottomDock;
+ }
+ }
+}
+
+QList<int> QDockAreaLayoutInfo::gapIndex(const QPoint& _pos,
+ bool nestingEnabled, TabMode tabMode) const
+{
+ QList<int> result;
+ QRect item_rect;
+ int item_index = 0;
+
+#ifndef QT_NO_TABBAR
+ if (tabbed) {
+ item_rect = tabContentRect();
+ } else
+#endif
+ {
+ int pos = pick(o, _pos);
+
+ int last = -1;
+ for (int i = 0; i < item_list.size(); ++i) {
+ const QDockAreaLayoutItem &item = item_list.at(i);
+ if (item.skip())
+ continue;
+
+ last = i;
+
+ if (item.pos + item.size < pos)
+ continue;
+
+ if (item.subinfo != 0
+#ifndef QT_NO_TABBAR
+ && !item.subinfo->tabbed
+#endif
+ ) {
+ result = item.subinfo->gapIndex(_pos, nestingEnabled,
+ tabMode);
+ result.prepend(i);
+ return result;
+ }
+
+ item_rect = itemRect(i);
+ item_index = i;
+ break;
+ }
+
+ if (item_rect.isNull()) {
+ result.append(last + 1);
+ return result;
+ }
+ }
+
+ Q_ASSERT(!item_rect.isNull());
+
+ QInternal::DockPosition dock_pos
+ = dockPosHelper(item_rect, _pos, o, nestingEnabled, tabMode);
+
+ switch (dock_pos) {
+ case QInternal::LeftDock:
+ if (o == Qt::Horizontal)
+ result << item_index;
+ else
+ result << item_index << 0; // this subinfo doesn't exist yet, but insertGap()
+ // handles this by inserting it
+ break;
+ case QInternal::RightDock:
+ if (o == Qt::Horizontal)
+ result << item_index + 1;
+ else
+ result << item_index << 1;
+ break;
+ case QInternal::TopDock:
+ if (o == Qt::Horizontal)
+ result << item_index << 0;
+ else
+ result << item_index;
+ break;
+ case QInternal::BottomDock:
+ if (o == Qt::Horizontal)
+ result << item_index << 1;
+ else
+ result << item_index + 1;
+ break;
+ case QInternal::DockCount:
+ result << (-item_index - 1) << 0; // negative item_index means "on top of"
+ // -item_index - 1, insertGap()
+ // will insert a tabbed subinfo
+ break;
+ default:
+ break;
+ }
+
+ return result;
+}
+
+static inline int shrink(QLayoutStruct &ls, int delta)
+{
+ if (ls.empty)
+ return 0;
+ int old_size = ls.size;
+ ls.size = qMax(ls.size - delta, ls.minimumSize);
+ return old_size - ls.size;
+}
+
+static inline int grow(QLayoutStruct &ls, int delta)
+{
+ if (ls.empty)
+ return 0;
+ int old_size = ls.size;
+ ls.size = qMin(ls.size + delta, ls.maximumSize);
+ return ls.size - old_size;
+}
+
+static int separatorMoveHelper(QVector<QLayoutStruct> &list, int index, int delta, int sep)
+{
+ // adjust sizes
+ int pos = -1;
+ for (int i = 0; i < list.size(); ++i) {
+ const QLayoutStruct &ls = list.at(i);
+ if (!ls.empty) {
+ pos = ls.pos;
+ break;
+ }
+ }
+ if (pos == -1)
+ return 0;
+
+ if (delta > 0) {
+ int growlimit = 0;
+ for (int i = 0; i<=index; ++i) {
+ const QLayoutStruct &ls = list.at(i);
+ if (ls.empty)
+ continue;
+ if (ls.maximumSize == QLAYOUTSIZE_MAX) {
+ growlimit = QLAYOUTSIZE_MAX;
+ break;
+ }
+ growlimit += ls.maximumSize - ls.size;
+ }
+ if (delta > growlimit)
+ delta = growlimit;
+
+ int d = 0;
+ for (int i = index + 1; d < delta && i < list.count(); ++i)
+ d += shrink(list[i], delta - d);
+ delta = d;
+ d = 0;
+ for (int i = index; d < delta && i >= 0; --i)
+ d += grow(list[i], delta - d);
+ } else if (delta < 0) {
+ int growlimit = 0;
+ for (int i = index + 1; i < list.count(); ++i) {
+ const QLayoutStruct &ls = list.at(i);
+ if (ls.empty)
+ continue;
+ if (ls.maximumSize == QLAYOUTSIZE_MAX) {
+ growlimit = QLAYOUTSIZE_MAX;
+ break;
+ }
+ growlimit += ls.maximumSize - ls.size;
+ }
+ if (-delta > growlimit)
+ delta = -growlimit;
+
+ int d = 0;
+ for (int i = index; d < -delta && i >= 0; --i)
+ d += shrink(list[i], -delta - d);
+ delta = -d;
+ d = 0;
+ for (int i = index + 1; d < -delta && i < list.count(); ++i)
+ d += grow(list[i], -delta - d);
+ }
+
+ // adjust positions
+ bool first = true;
+ for (int i = 0; i < list.size(); ++i) {
+ QLayoutStruct &ls = list[i];
+ if (ls.empty) {
+ ls.pos = pos + (first ? 0 : sep);
+ continue;
+ }
+ if (!first)
+ pos += sep;
+ ls.pos = pos;
+ pos += ls.size;
+ first = false;
+ }
+
+ return delta;
+}
+
+int QDockAreaLayoutInfo::separatorMove(int index, int delta, QVector<QLayoutStruct> *cache)
+{
+#ifndef QT_NO_TABBAR
+ Q_ASSERT(!tabbed);
+#endif
+
+ if (cache->isEmpty()) {
+ QVector<QLayoutStruct> &list = *cache;
+ list.resize(item_list.size());
+ for (int i = 0; i < item_list.size(); ++i) {
+ const QDockAreaLayoutItem &item = item_list.at(i);
+ QLayoutStruct &ls = list[i];
+ Q_ASSERT(!(item.flags & QDockAreaLayoutItem::GapItem));
+ if (item.skip()) {
+ ls.empty = true;
+ } else {
+ ls.empty = false;
+ ls.pos = item.pos;
+ ls.size = item.size;
+ ls.minimumSize = pick(o, item.minimumSize());
+ ls.maximumSize = pick(o, item.maximumSize());
+ }
+ }
+ }
+
+ QVector<QLayoutStruct> list = *cache;
+
+ delta = separatorMoveHelper(list, index, delta, sep);
+
+ for (int i = 0; i < item_list.size(); ++i) {
+ QDockAreaLayoutItem &item = item_list[i];
+ if (item.skip())
+ continue;
+ QLayoutStruct &ls = list[i];
+ item.size = ls.size;
+ item.pos = ls.pos;
+
+ if (item.subinfo != 0) {
+ item.subinfo->rect = itemRect(i);
+ item.subinfo->fitItems();
+ }
+ }
+
+ return delta;
+}
+
+void QDockAreaLayoutInfo::unnest(int index)
+{
+ QDockAreaLayoutItem &item = item_list[index];
+ if (item.subinfo == 0)
+ return;
+ if (item.subinfo->item_list.count() > 1)
+ return;
+
+ if (item.subinfo->item_list.count() == 0) {
+ item_list.removeAt(index);
+ } else if (item.subinfo->item_list.count() == 1) {
+ QDockAreaLayoutItem &child = item.subinfo->item_list.first();
+ if (child.widgetItem != 0) {
+ item.widgetItem = child.widgetItem;
+ delete item.subinfo;
+ item.subinfo = 0;
+ } else if (child.subinfo != 0) {
+ QDockAreaLayoutInfo *tmp = item.subinfo;
+ item.subinfo = child.subinfo;
+ child.subinfo = 0;
+ tmp->item_list.clear();
+ delete tmp;
+ }
+ }
+}
+
+void QDockAreaLayoutInfo::remove(QList<int> path)
+{
+ Q_ASSERT(!path.isEmpty());
+
+ if (path.count() > 1) {
+ int index = path.takeFirst();
+ QDockAreaLayoutItem &item = item_list[index];
+ Q_ASSERT(item.subinfo != 0);
+ item.subinfo->remove(path);
+ unnest(index);
+ } else {
+ int index = path.first();
+ item_list.removeAt(index);
+ }
+}
+
+QLayoutItem *QDockAreaLayoutInfo::plug(QList<int> path)
+{
+ Q_ASSERT(!path.isEmpty());
+
+ int index = path.takeFirst();
+ if (index < 0)
+ index = -index - 1;
+
+ if (!path.isEmpty()) {
+ const QDockAreaLayoutItem &item = item_list.at(index);
+ Q_ASSERT(item.subinfo != 0);
+ return item.subinfo->plug(path);
+ }
+
+ QDockAreaLayoutItem &item = item_list[index];
+
+ Q_ASSERT(item.widgetItem != 0);
+ Q_ASSERT(item.flags & QDockAreaLayoutItem::GapItem);
+ item.flags &= ~QDockAreaLayoutItem::GapItem;
+
+ QRect result;
+
+#ifndef QT_NO_TABBAR
+ if (tabbed) {
+ } else
+#endif
+ {
+ int prev = this->prev(index);
+ int next = this->next(index);
+
+ if (prev != -1 && !(item_list.at(prev).flags & QDockAreaLayoutItem::GapItem)) {
+ item.pos += sep;
+ item.size -= sep;
+ }
+ if (next != -1 && !(item_list.at(next).flags & QDockAreaLayoutItem::GapItem))
+ item.size -= sep;
+
+ QPoint pos;
+ rpick(o, pos) = item.pos;
+ rperp(o, pos) = perp(o, rect.topLeft());
+ QSize s;
+ rpick(o, s) = item.size;
+ rperp(o, s) = perp(o, rect.size());
+ result = QRect(pos, s);
+ }
+
+ return item.widgetItem;
+}
+
+QLayoutItem *QDockAreaLayoutInfo::unplug(QList<int> path)
+{
+ Q_ASSERT(!path.isEmpty());
+
+ if (path.count() > 1) {
+ int index = path.takeFirst();
+ const QDockAreaLayoutItem &item = item_list.at(index);
+ Q_ASSERT(item.subinfo != 0);
+ return item.subinfo->unplug(path);
+ }
+
+ int index = path.first();
+ QDockAreaLayoutItem &item = item_list[index];
+ int prev = this->prev(index);
+ int next = this->next(index);
+
+ Q_ASSERT(!(item.flags & QDockAreaLayoutItem::GapItem));
+ item.flags |= QDockAreaLayoutItem::GapItem;
+
+#ifndef QT_NO_TABBAR
+ if (tabbed) {
+ } else
+#endif
+ {
+ if (prev != -1 && !(item_list.at(prev).flags & QDockAreaLayoutItem::GapItem)) {
+ item.pos -= sep;
+ item.size += sep;
+ }
+ if (next != -1 && !(item_list.at(next).flags & QDockAreaLayoutItem::GapItem))
+ item.size += sep;
+ }
+
+ return item.widgetItem;
+}
+
+#ifndef QT_NO_TABBAR
+
+quintptr QDockAreaLayoutInfo::currentTabId() const
+{
+ if (!tabbed || tabBar == 0)
+ return 0;
+
+ int index = tabBar->currentIndex();
+ if (index == -1)
+ return 0;
+
+ return qvariant_cast<quintptr>(tabBar->tabData(index));
+}
+
+void QDockAreaLayoutInfo::setCurrentTab(QWidget *widget)
+{
+ setCurrentTabId(reinterpret_cast<quintptr>(widget));
+}
+
+void QDockAreaLayoutInfo::setCurrentTabId(quintptr id)
+{
+ if (!tabbed || tabBar == 0)
+ return;
+
+ for (int i = 0; i < tabBar->count(); ++i) {
+ if (qvariant_cast<quintptr>(tabBar->tabData(i)) == id) {
+ tabBar->setCurrentIndex(i);
+ return;
+ }
+ }
+}
+
+#endif // QT_NO_TABBAR
+
+static QRect dockedGeometry(QWidget *widget)
+{
+ int titleHeight = 0;
+
+ QDockWidgetLayout *layout
+ = qobject_cast<QDockWidgetLayout*>(widget->layout());
+ if(layout != 0 && layout->nativeWindowDeco())
+ titleHeight = layout->titleHeight();
+
+ QRect result = widget->geometry();
+ result.adjust(0, -titleHeight, 0, 0);
+ return result;
+}
+
+bool QDockAreaLayoutInfo::insertGap(QList<int> path, QLayoutItem *dockWidgetItem)
+{
+ Q_ASSERT(!path.isEmpty());
+
+ bool insert_tabbed = false;
+ int index = path.takeFirst();
+ if (index < 0) {
+ insert_tabbed = true;
+ index = -index - 1;
+ }
+
+// dump(qDebug() << "insertGap() before:" << index << tabIndex, *this, QString());
+
+ if (!path.isEmpty()) {
+ QDockAreaLayoutItem &item = item_list[index];
+
+ if (item.subinfo == 0
+#ifndef QT_NO_TABBAR
+ || (item.subinfo->tabbed && !insert_tabbed)
+#endif
+ ) {
+
+ // this is not yet a nested layout - make it
+
+ QDockAreaLayoutInfo *subinfo = item.subinfo;
+ QLayoutItem *widgetItem = item.widgetItem;
+ QRect r = subinfo == 0 ? dockedGeometry(widgetItem->widget()) : subinfo->rect;
+
+ Qt::Orientation opposite = o == Qt::Horizontal ? Qt::Vertical : Qt::Horizontal;
+#ifdef QT_NO_TABBAR
+ const int tabBarShape = 0;
+#endif
+ QDockAreaLayoutInfo *new_info
+ = new QDockAreaLayoutInfo(sep, dockPos, opposite, tabBarShape, mainWindow);
+
+ item.subinfo = new_info;
+ item.widgetItem = 0;
+
+ QDockAreaLayoutItem new_item
+ = widgetItem == 0
+ ? QDockAreaLayoutItem(subinfo)
+ : QDockAreaLayoutItem(widgetItem);
+ new_item.size = pick(opposite, r.size());
+ new_item.pos = pick(opposite, r.topLeft());
+ new_info->item_list.append(new_item);
+#ifndef QT_NO_TABBAR
+ if (insert_tabbed) {
+ new_info->tabbed = true;
+ }
+#endif
+ }
+
+ bool result = item.subinfo->insertGap(path, dockWidgetItem);
+ return result;
+ }
+
+ // create the gap item
+ QDockAreaLayoutItem gap_item;
+ gap_item.flags |= QDockAreaLayoutItem::GapItem;
+ gap_item.widgetItem = dockWidgetItem; // so minimumSize(), maximumSize() and
+ // sizeHint() will work
+#ifndef QT_NO_TABBAR
+ if (!tabbed)
+#endif
+ {
+ int prev = this->prev(index);
+ int next = this->next(index - 1);
+ // find out how much space we have in the layout
+ int space = 0;
+ if (isEmpty()) {
+ // I am an empty dock area, therefore I am a top-level dock area.
+ switch (dockPos) {
+ case QInternal::LeftDock:
+ case QInternal::RightDock:
+ if (o == Qt::Vertical) {
+ // the "size" is the height of the dock area (remember we are empty)
+ space = pick(Qt::Vertical, rect.size());
+ } else {
+ space = pick(Qt::Horizontal, dockWidgetItem->widget()->size());
+ }
+ break;
+ case QInternal::TopDock:
+ case QInternal::BottomDock:
+ default:
+ if (o == Qt::Horizontal) {
+ // the "size" is width of the dock area
+ space = pick(Qt::Horizontal, rect.size());
+ } else {
+ space = pick(Qt::Vertical, dockWidgetItem->widget()->size());
+ }
+ break;
+ }
+ } else {
+ for (int i = 0; i < item_list.count(); ++i) {
+ const QDockAreaLayoutItem &item = item_list.at(i);
+ if (item.skip())
+ continue;
+ Q_ASSERT(!(item.flags & QDockAreaLayoutItem::GapItem));
+ space += item.size - pick(o, item.minimumSize());
+ }
+ }
+
+ // find the actual size of the gap
+ int gap_size = 0;
+ int sep_size = 0;
+ if (isEmpty()) {
+ gap_size = space;
+ sep_size = 0;
+ } else {
+ QRect r = dockedGeometry(dockWidgetItem->widget());
+ gap_size = pick(o, r.size());
+ if (prev != -1 && !(item_list.at(prev).flags & QDockAreaLayoutItem::GapItem))
+ sep_size += sep;
+ if (next != -1 && !(item_list.at(next).flags & QDockAreaLayoutItem::GapItem))
+ sep_size += sep;
+ }
+ if (gap_size + sep_size > space)
+ gap_size = pick(o, gap_item.minimumSize());
+ gap_item.size = gap_size + sep_size;
+ }
+
+ // finally, insert the gap
+ item_list.insert(index, gap_item);
+
+// dump(qDebug() << "insertGap() after:" << index << tabIndex, *this, QString());
+
+ return true;
+}
+
+QDockAreaLayoutInfo *QDockAreaLayoutInfo::info(QWidget *widget)
+{
+ for (int i = 0; i < item_list.count(); ++i) {
+ const QDockAreaLayoutItem &item = item_list.at(i);
+ if (item.skip())
+ continue;
+
+#ifndef QT_NO_TABBAR
+ if (tabbed && widget == tabBar)
+ return this;
+#endif
+
+ if (item.widgetItem != 0 && item.widgetItem->widget() == widget)
+ return this;
+
+ if (item.subinfo != 0) {
+ if (QDockAreaLayoutInfo *result = item.subinfo->info(widget))
+ return result;
+ }
+ }
+
+ return 0;
+}
+
+QDockAreaLayoutInfo *QDockAreaLayoutInfo::info(QList<int> path)
+{
+ int index = path.takeFirst();
+ if (index < 0)
+ index = -index - 1;
+ if (index >= item_list.count())
+ return this;
+ if (path.isEmpty() || item_list.at(index).subinfo == 0)
+ return this;
+ return item_list.at(index).subinfo->info(path);
+}
+
+QRect QDockAreaLayoutInfo::itemRect(int index) const
+{
+ const QDockAreaLayoutItem &item = item_list.at(index);
+
+ if (item.skip())
+ return QRect();
+
+ QRect result;
+
+#ifndef QT_NO_TABBAR
+ if (tabbed) {
+ if (tabId(item) == currentTabId())
+ result = tabContentRect();
+ } else
+#endif
+ {
+ QPoint pos;
+ rpick(o, pos) = item.pos;
+ rperp(o, pos) = perp(o, rect.topLeft());
+ QSize s;
+ rpick(o, s) = item.size;
+ rperp(o, s) = perp(o, rect.size());
+ result = QRect(pos, s);
+ }
+
+ return result;
+}
+
+QRect QDockAreaLayoutInfo::itemRect(QList<int> path) const
+{
+ Q_ASSERT(!path.isEmpty());
+
+ if (path.count() > 1) {
+ const QDockAreaLayoutItem &item = item_list.at(path.takeFirst());
+ Q_ASSERT(item.subinfo != 0);
+ return item.subinfo->itemRect(path);
+ }
+
+ return itemRect(path.first());
+}
+
+QRect QDockAreaLayoutInfo::separatorRect(int index) const
+{
+#ifndef QT_NO_TABBAR
+ if (tabbed)
+ return QRect();
+#endif
+
+ const QDockAreaLayoutItem &item = item_list.at(index);
+ if (item.skip())
+ return QRect();
+
+ QPoint pos = rect.topLeft();
+ rpick(o, pos) = item.pos + item.size;
+ QSize s = rect.size();
+ rpick(o, s) = sep;
+
+ return QRect(pos, s);
+}
+
+QRect QDockAreaLayoutInfo::separatorRect(QList<int> path) const
+{
+ Q_ASSERT(!path.isEmpty());
+
+ if (path.count() > 1) {
+ const QDockAreaLayoutItem &item = item_list.at(path.takeFirst());
+ Q_ASSERT(item.subinfo != 0);
+ return item.subinfo->separatorRect(path);
+ }
+ return separatorRect(path.first());
+}
+
+QList<int> QDockAreaLayoutInfo::findSeparator(const QPoint &_pos) const
+{
+#ifndef QT_NO_TABBAR
+ if (tabbed)
+ return QList<int>();
+#endif
+
+ int pos = pick(o, _pos);
+
+ for (int i = 0; i < item_list.size(); ++i) {
+ const QDockAreaLayoutItem &item = item_list.at(i);
+ if (item.skip() || (item.flags & QDockAreaLayoutItem::GapItem))
+ continue;
+
+ if (item.pos + item.size > pos) {
+ if (item.subinfo != 0) {
+ QList<int> result = item.subinfo->findSeparator(_pos);
+ if (!result.isEmpty()) {
+ result.prepend(i);
+ return result;
+ } else {
+ return QList<int>();
+ }
+ }
+ }
+
+ int next = this->next(i);
+ if (next == -1 || (item_list.at(next).flags & QDockAreaLayoutItem::GapItem))
+ continue;
+
+ int margin = (sep == 1? 2 : 0);
+ if (pos >= item.pos + item.size - margin && item.pos + item.size + sep + margin > pos) {
+ QList<int> result;
+ result.append(i);
+ return result;
+ }
+
+ }
+
+ return QList<int>();
+}
+
+QList<int> QDockAreaLayoutInfo::indexOfPlaceHolder(const QString &objectName) const
+{
+ for (int i = 0; i < item_list.size(); ++i) {
+ const QDockAreaLayoutItem &item = item_list.at(i);
+
+ if (item.subinfo != 0) {
+ QList<int> result = item.subinfo->indexOfPlaceHolder(objectName);
+ if (!result.isEmpty()) {
+ result.prepend(i);
+ return result;
+ }
+ continue;
+ }
+
+ if (item.placeHolderItem != 0 && item.placeHolderItem->objectName == objectName) {
+ QList<int> result;
+ result << i;
+ return result;
+ }
+ }
+
+ return QList<int>();
+}
+
+QList<int> QDockAreaLayoutInfo::indexOf(QWidget *widget) const
+{
+ for (int i = 0; i < item_list.size(); ++i) {
+ const QDockAreaLayoutItem &item = item_list.at(i);
+
+ if (item.placeHolderItem != 0)
+ continue;
+
+ if (item.subinfo != 0) {
+ QList<int> result = item.subinfo->indexOf(widget);
+ if (!result.isEmpty()) {
+ result.prepend(i);
+ return result;
+ }
+ continue;
+ }
+
+ if (!(item.flags & QDockAreaLayoutItem::GapItem) && item.widgetItem->widget() == widget) {
+ QList<int> result;
+ result << i;
+ return result;
+ }
+ }
+
+ return QList<int>();
+}
+
+QMainWindowLayout *QDockAreaLayoutInfo::mainWindowLayout() const
+{
+ QMainWindowLayout *result = qobject_cast<QMainWindowLayout*>(mainWindow->layout());
+ Q_ASSERT(result != 0);
+ return result;
+}
+
+void QDockAreaLayoutInfo::apply(bool animate)
+{
+ QWidgetAnimator *widgetAnimator = mainWindowLayout()->widgetAnimator;
+
+#ifndef QT_NO_TABBAR
+ if (tabbed) {
+ QRect tab_rect;
+ QSize tbh = tabBarSizeHint();
+
+ if (tabBarVisible) {
+ switch (tabBarShape) {
+ case QTabBar::RoundedNorth:
+ case QTabBar::TriangularNorth:
+ tab_rect = QRect(rect.left(), rect.top(), rect.width(), tbh.height());
+ break;
+ case QTabBar::RoundedSouth:
+ case QTabBar::TriangularSouth:
+ tab_rect = QRect(rect.left(), rect.bottom() - tbh.height() + 1,
+ rect.width(), tbh.height());
+ break;
+ case QTabBar::RoundedEast:
+ case QTabBar::TriangularEast:
+ tab_rect = QRect(rect.right() - tbh.width() + 1, rect.top(),
+ tbh.width(), rect.height());
+ break;
+ case QTabBar::RoundedWest:
+ case QTabBar::TriangularWest:
+ tab_rect = QRect(rect.left(), rect.top(),
+ tbh.width(), rect.height());
+ break;
+ default:
+ break;
+ }
+ }
+
+ widgetAnimator->animate(tabBar, tab_rect, animate);
+ }
+#endif // QT_NO_TABBAR
+
+ for (int i = 0; i < item_list.size(); ++i) {
+ QDockAreaLayoutItem &item = item_list[i];
+
+ if (item.flags & QDockAreaLayoutItem::GapItem)
+ continue;
+
+ if (item.subinfo != 0) {
+ item.subinfo->apply(animate);
+ continue;
+ }
+
+ if (item.skip())
+ continue;
+
+ Q_ASSERT(item.widgetItem);
+ QRect r = itemRect(i);
+ QWidget *w = item.widgetItem->widget();
+
+ QRect geo = w->geometry();
+ widgetAnimator->animate(w, r, animate);
+ if (!w->isHidden()) {
+ QDockWidget *dw = qobject_cast<QDockWidget*>(w);
+ if (!r.isValid() && geo.right() >= 0 && geo.bottom() >= 0) {
+ dw->lower();
+ emit dw->visibilityChanged(false);
+ } else if (r.isValid()
+ && (geo.right() < 0 || geo.bottom() < 0)) {
+ emit dw->visibilityChanged(true);
+ }
+ }
+ }
+
+ if (sep == 1)
+ updateSeparatorWidgets();
+}
+
+static void paintSep(QPainter *p, QWidget *w, const QRect &r, Qt::Orientation o, bool mouse_over)
+{
+ QStyleOption opt(0);
+ opt.state = QStyle::State_None;
+ if (w->isEnabled())
+ opt.state |= QStyle::State_Enabled;
+ if (o != Qt::Horizontal)
+ opt.state |= QStyle::State_Horizontal;
+ if (mouse_over)
+ opt.state |= QStyle::State_MouseOver;
+ opt.rect = r;
+ opt.palette = w->palette();
+
+ w->style()->drawPrimitive(QStyle::PE_IndicatorDockWidgetResizeHandle, &opt, p, w);
+}
+
+QRegion QDockAreaLayoutInfo::separatorRegion() const
+{
+ QRegion result;
+
+ if (isEmpty())
+ return result;
+#ifndef QT_NO_TABBAR
+ if (tabbed)
+ return result;
+#endif
+
+ for (int i = 0; i < item_list.count(); ++i) {
+ const QDockAreaLayoutItem &item = item_list.at(i);
+
+ if (item.skip())
+ continue;
+
+ int next = this->next(i);
+
+ if (item.subinfo)
+ result |= item.subinfo->separatorRegion();
+
+ if (next == -1)
+ break;
+ result |= separatorRect(i);
+ }
+
+ return result;
+}
+
+void QDockAreaLayoutInfo::paintSeparators(QPainter *p, QWidget *widget,
+ const QRegion &clip,
+ const QPoint &mouse) const
+{
+ if (isEmpty())
+ return;
+#ifndef QT_NO_TABBAR
+ if (tabbed)
+ return;
+#endif
+
+ for (int i = 0; i < item_list.count(); ++i) {
+ const QDockAreaLayoutItem &item = item_list.at(i);
+
+ if (item.skip())
+ continue;
+
+ int next = this->next(i);
+ if ((item.flags & QDockAreaLayoutItem::GapItem)
+ || (next != -1 && (item_list.at(next).flags & QDockAreaLayoutItem::GapItem)))
+ continue;
+
+ if (item.subinfo) {
+ if (clip.contains(item.subinfo->rect))
+ item.subinfo->paintSeparators(p, widget, clip, mouse);
+ }
+
+ if (next == -1)
+ break;
+ QRect r = separatorRect(i);
+ if (clip.contains(r))
+ paintSep(p, widget, r, o, r.contains(mouse));
+ }
+}
+
+int QDockAreaLayoutInfo::next(int index) const
+{
+ for (int i = index + 1; i < item_list.size(); ++i) {
+ if (!item_list.at(i).skip())
+ return i;
+ }
+ return -1;
+}
+
+int QDockAreaLayoutInfo::prev(int index) const
+{
+ for (int i = index - 1; i >= 0; --i) {
+ if (!item_list.at(i).skip())
+ return i;
+ }
+ return -1;
+}
+
+void QDockAreaLayoutInfo::tab(int index, QLayoutItem *dockWidgetItem)
+{
+#ifdef QT_NO_TABBAR
+ Q_UNUSED(index);
+ Q_UNUSED(dockWidgetItem);
+#else
+ if (tabbed) {
+ item_list.append(QDockAreaLayoutItem(dockWidgetItem));
+ updateTabBar();
+ setCurrentTab(dockWidgetItem->widget());
+ } else {
+ QDockAreaLayoutInfo *new_info
+ = new QDockAreaLayoutInfo(sep, dockPos, o, tabBarShape, mainWindow);
+ item_list[index].subinfo = new_info;
+ new_info->item_list.append(item_list.at(index).widgetItem);
+ item_list[index].widgetItem = 0;
+ new_info->item_list.append(dockWidgetItem);
+ new_info->tabbed = true;
+ new_info->updateTabBar();
+ new_info->setCurrentTab(dockWidgetItem->widget());
+ }
+#endif // QT_NO_TABBAR
+}
+
+void QDockAreaLayoutInfo::split(int index, Qt::Orientation orientation,
+ QLayoutItem *dockWidgetItem)
+{
+ if (orientation == o) {
+ item_list.insert(index + 1, QDockAreaLayoutItem(dockWidgetItem));
+ } else {
+#ifdef QT_NO_TABBAR
+ const int tabBarShape = 0;
+#endif
+ QDockAreaLayoutInfo *new_info
+ = new QDockAreaLayoutInfo(sep, dockPos, orientation, tabBarShape, mainWindow);
+ item_list[index].subinfo = new_info;
+ new_info->item_list.append(item_list.at(index).widgetItem);
+ item_list[index].widgetItem = 0;
+ new_info->item_list.append(dockWidgetItem);
+ }
+}
+
+QDockAreaLayoutItem &QDockAreaLayoutInfo::item(QList<int> path)
+{
+ Q_ASSERT(!path.isEmpty());
+ if (path.count() > 1) {
+ QDockAreaLayoutItem &item = item_list[path.takeFirst()];
+ Q_ASSERT(item.subinfo != 0);
+ return item.subinfo->item(path);
+ }
+ return item_list[path.first()];
+}
+
+QLayoutItem *QDockAreaLayoutInfo::itemAt(int *x, int index) const
+{
+ for (int i = 0; i < item_list.count(); ++i) {
+ const QDockAreaLayoutItem &item = item_list.at(i);
+ if (item.placeHolderItem != 0)
+ continue;
+ if (item.subinfo) {
+ if (QLayoutItem *ret = item.subinfo->itemAt(x, index))
+ return ret;
+ } else if (item.widgetItem) {
+ if ((*x)++ == index)
+ return item.widgetItem;
+ }
+ }
+ return 0;
+}
+
+QLayoutItem *QDockAreaLayoutInfo::takeAt(int *x, int index)
+{
+ for (int i = 0; i < item_list.count(); ++i) {
+ QDockAreaLayoutItem &item = item_list[i];
+ if (item.placeHolderItem != 0)
+ continue;
+ else if (item.subinfo) {
+ if (QLayoutItem *ret = item.subinfo->takeAt(x, index)) {
+ unnest(i);
+ return ret;
+ }
+ } else if (item.widgetItem) {
+ if ((*x)++ == index) {
+ item.placeHolderItem = new QPlaceHolderItem(item.widgetItem->widget());
+ QLayoutItem *ret = item.widgetItem;
+ item.widgetItem = 0;
+ if (item.size != -1)
+ item.flags |= QDockAreaLayoutItem::KeepSize;
+ return ret;
+ }
+ }
+ }
+ return 0;
+}
+
+void QDockAreaLayoutInfo::deleteAllLayoutItems()
+{
+ for (int i = 0; i < item_list.count(); ++i) {
+ QDockAreaLayoutItem &item= item_list[i];
+ if (item.subinfo) {
+ item.subinfo->deleteAllLayoutItems();
+ } else {
+ delete item.widgetItem;
+ item.widgetItem = 0;
+ }
+ }
+}
+
+void QDockAreaLayoutInfo::saveState(QDataStream &stream) const
+{
+#ifndef QT_NO_TABBAR
+ if (tabbed) {
+ stream << (uchar) TabMarker;
+
+ // write the index in item_list of the widget that's currently on top.
+ quintptr id = currentTabId();
+ int index = -1;
+ for (int i = 0; i < item_list.count(); ++i) {
+ if (tabId(item_list.at(i)) == id) {
+ index = i;
+ break;
+ }
+ }
+ stream << index;
+ } else
+#endif // QT_NO_TABBAR
+ {
+ stream << (uchar) SequenceMarker;
+ }
+
+ stream << (uchar) o << item_list.count();
+
+ for (int i = 0; i < item_list.count(); ++i) {
+ const QDockAreaLayoutItem &item = item_list.at(i);
+ if (item.widgetItem != 0) {
+ stream << (uchar) WidgetMarker;
+ QWidget *w = item.widgetItem->widget();
+ QString name = w->objectName();
+ if (name.isEmpty()) {
+ qWarning("QMainWindow::saveState(): 'objectName' not set for QDockWidget %p '%s;",
+ w, qPrintable(w->windowTitle()));
+ }
+ stream << name;
+
+ uchar flags = 0;
+ if (!w->isHidden())
+ flags |= StateFlagVisible;
+ if (w->isWindow())
+ flags |= StateFlagFloating;
+ stream << flags;
+
+ if (w->isWindow()) {
+ stream << w->x() << w->y() << w->width() << w->height();
+ } else {
+ stream << item.pos << item.size << pick(o, item.minimumSize())
+ << pick(o, item.maximumSize());
+ }
+ } else if (item.placeHolderItem != 0) {
+ stream << (uchar) WidgetMarker;
+ stream << item.placeHolderItem->objectName;
+ uchar flags = 0;
+ if (!item.placeHolderItem->hidden)
+ flags |= StateFlagVisible;
+ if (item.placeHolderItem->window)
+ flags |= StateFlagFloating;
+ stream << flags;
+ if (item.placeHolderItem->window) {
+ QRect r = item.placeHolderItem->topLevelRect;
+ stream << r.x() << r.y() << r.width() << r.height();
+ } else {
+ stream << item.pos << item.size << (int)0 << (int)0;
+ }
+ } else if (item.subinfo != 0) {
+ stream << (uchar) SequenceMarker << item.pos << item.size << pick(o, item.minimumSize()) << pick(o, item.maximumSize());
+ item.subinfo->saveState(stream);
+ }
+ }
+}
+
+#ifdef Q_WS_MAC
+static Qt::DockWidgetArea toDockWidgetArea(QInternal::DockPosition pos)
+{
+ switch (pos) {
+ case QInternal::LeftDock: return Qt::LeftDockWidgetArea;
+ case QInternal::RightDock: return Qt::RightDockWidgetArea;
+ case QInternal::TopDock: return Qt::TopDockWidgetArea;
+ case QInternal::BottomDock: return Qt::BottomDockWidgetArea;
+ default: break;
+ }
+ return Qt::NoDockWidgetArea;
+}
+#endif
+
+static QRect constrainedRect(QRect rect, const QRect &desktop)
+{
+ if (desktop.isValid()) {
+ rect.setWidth(qMin(rect.width(), desktop.width()));
+ rect.setHeight(qMin(rect.height(), desktop.height()));
+ rect.moveLeft(qMax(rect.left(), desktop.left()));
+ rect.moveTop(qMax(rect.top(), desktop.top()));
+ rect.moveRight(qMin(rect.right(), desktop.right()));
+ rect.moveBottom(qMin(rect.bottom(), desktop.bottom()));
+ }
+
+ return rect;
+}
+
+bool QDockAreaLayoutInfo::restoreState(QDataStream &stream, QList<QDockWidget*> &widgets, bool testing)
+{
+ uchar marker;
+ stream >> marker;
+ if (marker != TabMarker && marker != SequenceMarker)
+ return false;
+
+#ifndef QT_NO_TABBAR
+ tabbed = marker == TabMarker;
+
+ int index = -1;
+ if (tabbed)
+ stream >> index;
+#endif
+
+ uchar orientation;
+ stream >> orientation;
+ o = static_cast<Qt::Orientation>(orientation);
+
+ int cnt;
+ stream >> cnt;
+
+ for (int i = 0; i < cnt; ++i) {
+ uchar nextMarker;
+ stream >> nextMarker;
+ if (nextMarker == WidgetMarker) {
+ QString name;
+ uchar flags;
+ stream >> name >> flags;
+ if (name.isEmpty()) {
+ int dummy;
+ stream >> dummy >> dummy >> dummy >> dummy;
+ continue;
+ }
+
+ QDockWidget *widget = 0;
+ for (int j = 0; j < widgets.count(); ++j) {
+ if (widgets.at(j)->objectName() == name) {
+ widget = widgets.takeAt(j);
+ break;
+ }
+ }
+
+ if (widget == 0) {
+ QPlaceHolderItem *placeHolder = new QPlaceHolderItem;
+ QDockAreaLayoutItem item(placeHolder);
+
+ placeHolder->objectName = name;
+ placeHolder->window = flags & StateFlagFloating;
+ placeHolder->hidden = !(flags & StateFlagVisible);
+ if (placeHolder->window) {
+ int x, y, w, h;
+ stream >> x >> y >> w >> h;
+ placeHolder->topLevelRect = QRect(x, y, w, h);
+ } else {
+ int dummy;
+ stream >> item.pos >> item.size >> dummy >> dummy;
+ }
+ if (item.size != -1)
+ item.flags |= QDockAreaLayoutItem::KeepSize;
+ if (!testing)
+ item_list.append(item);
+ } else {
+ QDockAreaLayoutItem item(new QDockWidgetItem(widget));
+ if (flags & StateFlagFloating) {
+ bool drawer = false;
+#ifdef Q_WS_MAC // drawer support
+ extern bool qt_mac_is_macdrawer(const QWidget *); //qwidget_mac.cpp
+ extern bool qt_mac_set_drawer_preferred_edge(QWidget *, Qt::DockWidgetArea); //qwidget_mac.cpp
+ drawer = qt_mac_is_macdrawer(widget);
+#endif
+
+ if (!testing) {
+ widget->hide();
+ if (!drawer)
+ widget->setFloating(true);
+ }
+
+ int x, y, w, h;
+ stream >> x >> y >> w >> h;
+
+#ifdef Q_WS_MAC // drawer support
+ if (drawer) {
+ mainWindow->window()->createWinId();
+ widget->window()->createWinId();
+ qt_mac_set_drawer_preferred_edge(widget, toDockWidgetArea(dockPos));
+ } else
+#endif
+ if (!testing) {
+ QRect r(x, y, w, h);
+ QDesktopWidget *desktop = QApplication::desktop();
+ if (desktop->isVirtualDesktop())
+ r = constrainedRect(r, desktop->screenGeometry(desktop->screenNumber(r.topLeft())));
+ else
+ r = constrainedRect(r, desktop->screenGeometry(widget));
+ widget->move(r.topLeft());
+ widget->resize(r.size());
+ }
+
+ if (!testing) {
+ widget->setVisible(flags & StateFlagVisible);
+ }
+ } else {
+ int dummy;
+ stream >> item.pos >> item.size >> dummy >> dummy;
+ // qDebug() << widget << item.pos << item.size;
+ if (!testing) {
+ widget->setFloating(false);
+ widget->setVisible(flags & StateFlagVisible);
+ }
+ }
+
+ if (!testing) {
+ item_list.append(item);
+ }
+ }
+ } else if (nextMarker == SequenceMarker) {
+ int dummy;
+#ifdef QT_NO_TABBAR
+ const int tabBarShape = 0;
+#endif
+ QDockAreaLayoutInfo *info = new QDockAreaLayoutInfo(sep, dockPos, o,
+ tabBarShape, mainWindow);
+ QDockAreaLayoutItem item(info);
+ stream >> item.pos >> item.size >> dummy >> dummy;
+ if (!info->restoreState(stream, widgets, testing))
+ return false;
+
+ if (!testing) {
+ item_list.append(item);
+ }
+ } else {
+ return false;
+ }
+ }
+
+#ifndef QT_NO_TABBAR
+ if (!testing && tabbed && index >= 0 && index < item_list.count()) {
+ updateTabBar();
+ setCurrentTabId(tabId(item_list.at(index)));
+ }
+#endif
+ if (!testing && sep == 1)
+ updateSeparatorWidgets();
+
+ return true;
+}
+
+void QDockAreaLayoutInfo::updateSeparatorWidgets() const
+{
+ QDockAreaLayoutInfo *that = const_cast<QDockAreaLayoutInfo*>(this);
+
+ if (tabbed) {
+ that->separatorWidgets.clear();
+ return;
+ }
+
+ int j = 0;
+ for (int i = 0; i < item_list.count(); ++i) {
+ const QDockAreaLayoutItem &item = item_list.at(i);
+
+ if (item.skip())
+ continue;
+
+ int next = this->next(i);
+ if ((item.flags & QDockAreaLayoutItem::GapItem)
+ || (next != -1 && (item_list.at(next).flags & QDockAreaLayoutItem::GapItem)))
+ continue;
+
+ if (item.subinfo) {
+ item.subinfo->updateSeparatorWidgets();
+ }
+
+ if (next == -1)
+ break;
+
+ QWidget *sepWidget;
+ if (j < separatorWidgets.size() && separatorWidgets.at(j)) {
+ sepWidget = separatorWidgets.at(j);
+ } else {
+ sepWidget = mainWindowLayout()->getSeparatorWidget();
+ that->separatorWidgets.append(sepWidget);
+ }
+ j++;
+
+#ifndef QT_MAC_USE_COCOA
+ sepWidget->raise();
+#endif
+ QRect sepRect = separatorRect(i).adjusted(-2, -2, 2, 2);
+ sepWidget->setGeometry(sepRect);
+ sepWidget->setMask( QRegion(separatorRect(i).translated( - sepRect.topLeft())));
+ sepWidget->show();
+ }
+
+ for (int k = j; k < that->separatorWidgets.size(); ++k) {
+ that->separatorWidgets[k]->hide();
+ }
+ that->separatorWidgets.resize(j);
+ Q_ASSERT(separatorWidgets.size() == j);
+}
+
+#ifndef QT_NO_TABBAR
+void QDockAreaLayoutInfo::updateTabBar() const
+{
+ if (!tabbed)
+ return;
+
+ QDockAreaLayoutInfo *that = const_cast<QDockAreaLayoutInfo*>(this);
+
+ if (tabBar == 0) {
+ that->tabBar = mainWindowLayout()->getTabBar();
+ that->tabBar->setShape(static_cast<QTabBar::Shape>(tabBarShape));
+ that->tabBar->setDrawBase(true);
+ }
+
+ bool blocked = tabBar->blockSignals(true);
+ bool gap = false;
+
+ int tab_idx = 0;
+ bool changed = false;
+ for (int i = 0; i < item_list.count(); ++i) {
+ const QDockAreaLayoutItem &item = item_list.at(i);
+ if (item.skip())
+ continue;
+ if (item.flags & QDockAreaLayoutItem::GapItem) {
+ gap = true;
+ continue;
+ }
+ if (item.widgetItem == 0)
+ continue;
+
+ QDockWidget *dw = qobject_cast<QDockWidget*>(item.widgetItem->widget());
+ QString title = dw->d_func()->fixedWindowTitle;
+ quintptr id = tabId(item);
+ if (tab_idx == tabBar->count()) {
+ tabBar->insertTab(tab_idx, title);
+#ifndef QT_NO_TOOLTIP
+ tabBar->setTabToolTip(tab_idx, title);
+#endif
+ tabBar->setTabData(tab_idx, id);
+ changed = true;
+ } else if (qvariant_cast<quintptr>(tabBar->tabData(tab_idx)) != id) {
+ if (tab_idx + 1 < tabBar->count()
+ && qvariant_cast<quintptr>(tabBar->tabData(tab_idx + 1)) == id)
+ tabBar->removeTab(tab_idx);
+ else {
+ tabBar->insertTab(tab_idx, title);
+#ifndef QT_NO_TOOLTIP
+ tabBar->setTabToolTip(tab_idx, title);
+#endif
+ tabBar->setTabData(tab_idx, id);
+ }
+ changed = true;
+ }
+
+ if (title != tabBar->tabText(tab_idx)) {
+ tabBar->setTabText(tab_idx, title);
+#ifndef QT_NO_TOOLTIP
+ tabBar->setTabToolTip(tab_idx, title);
+#endif
+ changed = true;
+ }
+
+ ++tab_idx;
+ }
+
+ while (tab_idx < tabBar->count()) {
+ tabBar->removeTab(tab_idx);
+ changed = true;
+ }
+
+ tabBar->blockSignals(blocked);
+
+ that->tabBarVisible = ( (gap ? 1 : 0) + tabBar->count()) > 1;
+
+ if (changed || !tabBarMin.isValid() | !tabBarHint.isValid()) {
+ that->tabBarMin = tabBar->minimumSizeHint();
+ that->tabBarHint = tabBar->sizeHint();
+ }
+}
+
+void QDockAreaLayoutInfo::setTabBarShape(int shape)
+{
+ if (shape == tabBarShape)
+ return;
+ tabBarShape = shape;
+ if (tabBar != 0) {
+ tabBar->setShape(static_cast<QTabBar::Shape>(shape));
+ tabBarMin = QSize();
+ tabBarHint = QSize();
+ }
+
+ for (int i = 0; i < item_list.count(); ++i) {
+ QDockAreaLayoutItem &item = item_list[i];
+ if (item.subinfo != 0)
+ item.subinfo->setTabBarShape(shape);
+ }
+}
+
+QSize QDockAreaLayoutInfo::tabBarMinimumSize() const
+{
+ if (!tabbed)
+ return QSize(0, 0);
+
+ updateTabBar();
+
+ return tabBarMin;
+}
+
+QSize QDockAreaLayoutInfo::tabBarSizeHint() const
+{
+ if (!tabbed)
+ return QSize(0, 0);
+
+ updateTabBar();
+
+ return tabBarHint;
+}
+
+QSet<QTabBar*> QDockAreaLayoutInfo::usedTabBars() const
+{
+ QSet<QTabBar*> result;
+
+ if (tabbed) {
+ updateTabBar();
+ result.insert(tabBar);
+ }
+
+ for (int i = 0; i < item_list.count(); ++i) {
+ const QDockAreaLayoutItem &item = item_list.at(i);
+ if (item.subinfo != 0)
+ result += item.subinfo->usedTabBars();
+ }
+
+ return result;
+}
+
+// returns a set of all used separator widgets for this dockarelayout info
+// and all subinfos
+QSet<QWidget*> QDockAreaLayoutInfo::usedSeparatorWidgets() const
+{
+ QSet<QWidget*> result;
+
+ foreach (QWidget *sepWidget, separatorWidgets)
+ result << sepWidget;
+
+ for (int i = 0; i < item_list.count(); ++i) {
+ const QDockAreaLayoutItem &item = item_list.at(i);
+ if (item.subinfo != 0)
+ result += item.subinfo->usedSeparatorWidgets();
+ }
+
+ return result;
+}
+
+QRect QDockAreaLayoutInfo::tabContentRect() const
+{
+ if (!tabbed)
+ return QRect();
+
+ QRect result = rect;
+ QSize tbh = tabBarSizeHint();
+
+ if (tabBarVisible) {
+ switch (tabBarShape) {
+ case QTabBar::RoundedNorth:
+ case QTabBar::TriangularNorth:
+ result.adjust(0, tbh.height(), 0, 0);
+ break;
+ case QTabBar::RoundedSouth:
+ case QTabBar::TriangularSouth:
+ result.adjust(0, 0, 0, -tbh.height());
+ break;
+ case QTabBar::RoundedEast:
+ case QTabBar::TriangularEast:
+ result.adjust(0, 0, -tbh.width(), 0);
+ break;
+ case QTabBar::RoundedWest:
+ case QTabBar::TriangularWest:
+ result.adjust(tbh.width(), 0, 0, 0);
+ break;
+ default:
+ break;
+ }
+ }
+
+ return result;
+}
+#endif // QT_NO_TABBAR
+
+/******************************************************************************
+** QDockAreaLayout
+*/
+
+QDockAreaLayout::QDockAreaLayout(QMainWindow *win)
+{
+ mainWindow = win;
+ sep = win->style()->pixelMetric(QStyle::PM_DockWidgetSeparatorExtent, 0, win);
+#ifndef QT_NO_TABBAR
+ const int tabShape = QTabBar::RoundedSouth;
+#else
+ const int tabShape = 0;
+#endif
+ docks[QInternal::LeftDock]
+ = QDockAreaLayoutInfo(sep, QInternal::LeftDock, Qt::Vertical, tabShape, win);
+ docks[QInternal::RightDock]
+ = QDockAreaLayoutInfo(sep, QInternal::RightDock, Qt::Vertical, tabShape, win);
+ docks[QInternal::TopDock]
+ = QDockAreaLayoutInfo(sep, QInternal::TopDock, Qt::Horizontal, tabShape, win);
+ docks[QInternal::BottomDock]
+ = QDockAreaLayoutInfo(sep, QInternal::BottomDock, Qt::Horizontal, tabShape, win);
+ centralWidgetItem = 0;
+ centralWidgetRect = QRect(0, 0, -1, -1);
+
+ corners[Qt::TopLeftCorner] = Qt::TopDockWidgetArea;
+ corners[Qt::TopRightCorner] = Qt::TopDockWidgetArea;
+ corners[Qt::BottomLeftCorner] = Qt::BottomDockWidgetArea;
+ corners[Qt::BottomRightCorner] = Qt::BottomDockWidgetArea;
+}
+
+bool QDockAreaLayout::isValid() const
+{
+ return rect.isValid();
+}
+
+void QDockAreaLayout::saveState(QDataStream &stream) const
+{
+ stream << (uchar) DockWidgetStateMarker;
+ int cnt = 0;
+ for (int i = 0; i < QInternal::DockCount; ++i) {
+ if (!docks[i].item_list.isEmpty())
+ ++cnt;
+ }
+ stream << cnt;
+ for (int i = 0; i < QInternal::DockCount; ++i) {
+ if (docks[i].item_list.isEmpty())
+ continue;
+ stream << i << docks[i].rect.size();
+ docks[i].saveState(stream);
+ }
+
+ stream << centralWidgetRect.size();
+
+ for (int i = 0; i < 4; ++i)
+ stream << static_cast<int>(corners[i]);
+}
+
+bool QDockAreaLayout::restoreState(QDataStream &stream, const QList<QDockWidget*> &_dockwidgets, bool testing)
+{
+ QList<QDockWidget*> dockwidgets = _dockwidgets;
+
+ int cnt;
+ stream >> cnt;
+ for (int i = 0; i < cnt; ++i) {
+ int pos;
+ stream >> pos;
+ QSize size;
+ stream >> size;
+ if (!testing) {
+ docks[pos].rect = QRect(QPoint(0, 0), size);
+ }
+ if (!docks[pos].restoreState(stream, dockwidgets, testing)) {
+ stream.setStatus(QDataStream::ReadCorruptData);
+ return false;
+ }
+ }
+
+ QSize size;
+ stream >> size;
+ centralWidgetRect = QRect(QPoint(0, 0), size);
+
+ bool ok = stream.status() == QDataStream::Ok;
+
+ if (ok) {
+ int cornerData[4];
+ for (int i = 0; i < 4; ++i)
+ stream >> cornerData[i];
+ if (stream.status() == QDataStream::Ok) {
+ for (int i = 0; i < 4; ++i)
+ corners[i] = static_cast<Qt::DockWidgetArea>(cornerData[i]);
+ }
+ }
+
+ return ok;
+}
+
+QList<int> QDockAreaLayout::indexOfPlaceHolder(const QString &objectName) const
+{
+ for (int i = 0; i < QInternal::DockCount; ++i) {
+ QList<int> result = docks[i].indexOfPlaceHolder(objectName);
+ if (!result.isEmpty()) {
+ result.prepend(i);
+ return result;
+ }
+ }
+ return QList<int>();
+}
+
+QList<int> QDockAreaLayout::indexOf(QWidget *dockWidget) const
+{
+ for (int i = 0; i < QInternal::DockCount; ++i) {
+ QList<int> result = docks[i].indexOf(dockWidget);
+ if (!result.isEmpty()) {
+ result.prepend(i);
+ return result;
+ }
+ }
+ return QList<int>();
+}
+
+QList<int> QDockAreaLayout::gapIndex(const QPoint &pos) const
+{
+ QMainWindow::DockOptions opts = mainWindow->dockOptions();
+ bool nestingEnabled = opts & QMainWindow::AllowNestedDocks;
+ QDockAreaLayoutInfo::TabMode tabMode = QDockAreaLayoutInfo::NoTabs;
+#ifndef QT_NO_TABBAR
+ if (opts & QMainWindow::AllowTabbedDocks
+ || opts & QMainWindow::VerticalTabs)
+ tabMode = QDockAreaLayoutInfo::AllowTabs;
+ if (opts & QMainWindow::ForceTabbedDocks)
+ tabMode = QDockAreaLayoutInfo::ForceTabs;
+
+ if (tabMode == QDockAreaLayoutInfo::ForceTabs)
+ nestingEnabled = false;
+#endif
+
+
+ for (int i = 0; i < QInternal::DockCount; ++i) {
+ const QDockAreaLayoutInfo &info = docks[i];
+
+ if (!info.isEmpty() && info.rect.contains(pos)) {
+ QList<int> result
+ = docks[i].gapIndex(pos, nestingEnabled, tabMode);
+ if (!result.isEmpty())
+ result.prepend(i);
+ return result;
+ }
+ }
+
+ for (int i = 0; i < QInternal::DockCount; ++i) {
+ const QDockAreaLayoutInfo &info = docks[i];
+
+ if (info.isEmpty()) {
+ QRect r;
+ switch (i) {
+ case QInternal::LeftDock:
+ r = QRect(rect.left(), rect.top(), EmptyDropAreaSize, rect.height());
+ break;
+ case QInternal::RightDock:
+ r = QRect(rect.right() - EmptyDropAreaSize, rect.top(),
+ EmptyDropAreaSize, rect.height());
+ break;
+ case QInternal::TopDock:
+ r = QRect(rect.left(), rect.top(), rect.width(), EmptyDropAreaSize);
+ break;
+ case QInternal::BottomDock:
+ r = QRect(rect.left(), rect.bottom() - EmptyDropAreaSize,
+ rect.width(), EmptyDropAreaSize);
+ break;
+ }
+ if (r.contains(pos)) {
+ if (opts & QMainWindow::ForceTabbedDocks && !info.item_list.isEmpty()) {
+ //in case of ForceTabbedDocks, we pass -1 in order to force the gap to be tabbed
+ //it mustn't be completely empty otherwise it won't work
+ return QList<int>() << i << -1 << 0;
+ } else {
+ return QList<int>() << i << 0;
+ }
+ }
+ }
+ }
+
+ return QList<int>();
+}
+
+QList<int> QDockAreaLayout::findSeparator(const QPoint &pos) const
+{
+ QList<int> result;
+ for (int i = 0; i < QInternal::DockCount; ++i) {
+ const QDockAreaLayoutInfo &info = docks[i];
+ if (info.isEmpty())
+ continue;
+ QRect rect = separatorRect(i);
+ if (sep == 1)
+ rect.adjust(-2, -2, 2, 2);
+ if (rect.contains(pos)) {
+ result << i;
+ break;
+ } else if (info.rect.contains(pos)) {
+ result = docks[i].findSeparator(pos);
+ if (!result.isEmpty()) {
+ result.prepend(i);
+ break;
+ }
+ }
+ }
+
+ return result;
+}
+
+QDockAreaLayoutInfo *QDockAreaLayout::info(QWidget *widget)
+{
+ for (int i = 0; i < QInternal::DockCount; ++i) {
+ if (QDockAreaLayoutInfo *result = docks[i].info(widget))
+ return result;
+ }
+
+ return 0;
+}
+
+QDockAreaLayoutInfo *QDockAreaLayout::info(QList<int> path)
+{
+ Q_ASSERT(!path.isEmpty());
+ int index = path.takeFirst();
+ Q_ASSERT(index >= 0 && index < QInternal::DockCount);
+
+ if (path.isEmpty())
+ return &docks[index];
+
+ return docks[index].info(path);
+}
+
+const QDockAreaLayoutInfo *QDockAreaLayout::info(QList<int> path) const
+{
+ return const_cast<QDockAreaLayout*>(this)->info(path);
+}
+
+QDockAreaLayoutItem &QDockAreaLayout::item(QList<int> path)
+{
+ Q_ASSERT(!path.isEmpty());
+ int index = path.takeFirst();
+ Q_ASSERT(index >= 0 && index < QInternal::DockCount);
+ return docks[index].item(path);
+}
+
+QRect QDockAreaLayout::itemRect(QList<int> path) const
+{
+ Q_ASSERT(!path.isEmpty());
+ int index = path.takeFirst();
+ Q_ASSERT(index >= 0 && index < QInternal::DockCount);
+ return docks[index].itemRect(path);
+}
+
+QRect QDockAreaLayout::separatorRect(int index) const
+{
+ if (docks[index].isEmpty())
+ return QRect();
+ QRect r = docks[index].rect;
+ switch (index) {
+ case QInternal::LeftDock:
+ return QRect(r.right() + 1, r.top(), sep, r.height());
+ case QInternal::RightDock:
+ return QRect(r.left() - sep, r.top(), sep, r.height());
+ case QInternal::TopDock:
+ return QRect(r.left(), r.bottom() + 1, r.width(), sep);
+ case QInternal::BottomDock:
+ return QRect(r.left(), r.top() - sep, r.width(), sep);
+ default:
+ break;
+ }
+ return QRect();
+}
+
+QRect QDockAreaLayout::separatorRect(QList<int> path) const
+{
+ Q_ASSERT(!path.isEmpty());
+
+ int index = path.takeFirst();
+ Q_ASSERT(index >= 0 && index < QInternal::DockCount);
+
+ if (path.isEmpty())
+ return separatorRect(index);
+ else
+ return docks[index].separatorRect(path);
+}
+
+bool QDockAreaLayout::insertGap(QList<int> path, QLayoutItem *dockWidgetItem)
+{
+ Q_ASSERT(!path.isEmpty());
+ int index = path.takeFirst();
+ Q_ASSERT(index >= 0 && index < QInternal::DockCount);
+ return docks[index].insertGap(path, dockWidgetItem);
+}
+
+QLayoutItem *QDockAreaLayout::plug(QList<int> path)
+{
+ Q_ASSERT(!path.isEmpty());
+ int index = path.takeFirst();
+ Q_ASSERT(index >= 0 && index < QInternal::DockCount);
+ return docks[index].plug(path);
+}
+
+QLayoutItem *QDockAreaLayout::unplug(QList<int> path)
+{
+ Q_ASSERT(!path.isEmpty());
+ int index = path.takeFirst();
+ Q_ASSERT(index >= 0 && index < QInternal::DockCount);
+ return docks[index].unplug(path);
+}
+
+void QDockAreaLayout::remove(QList<int> path)
+{
+ Q_ASSERT(!path.isEmpty());
+ int index = path.takeFirst();
+ Q_ASSERT(index >= 0 && index < QInternal::DockCount);
+ docks[index].remove(path);
+}
+
+static inline int qMin(int i1, int i2, int i3) { return qMin(i1, qMin(i2, i3)); }
+static inline int qMax(int i1, int i2, int i3) { return qMax(i1, qMax(i2, i3)); }
+
+void QDockAreaLayout::getGrid(QVector<QLayoutStruct> *_ver_struct_list,
+ QVector<QLayoutStruct> *_hor_struct_list)
+{
+ QSize center_hint(0, 0);
+ QSize center_min(0, 0);
+ bool have_central = centralWidgetItem != 0 && !centralWidgetItem->isEmpty();
+ if (have_central) {
+ center_hint = centralWidgetRect.size();
+ if (!center_hint.isValid())
+ center_hint = centralWidgetItem->sizeHint();
+ center_min = centralWidgetItem->minimumSize();
+ }
+
+ QRect center_rect = rect;
+ if (!docks[QInternal::LeftDock].isEmpty())
+ center_rect.setLeft(rect.left() + docks[QInternal::LeftDock].rect.width() + sep);
+ if (!docks[QInternal::TopDock].isEmpty())
+ center_rect.setTop(rect.top() + docks[QInternal::TopDock].rect.height() + sep);
+ if (!docks[QInternal::RightDock].isEmpty())
+ center_rect.setRight(rect.right() - docks[QInternal::RightDock].rect.width() - sep);
+ if (!docks[QInternal::BottomDock].isEmpty())
+ center_rect.setBottom(rect.bottom() - docks[QInternal::BottomDock].rect.height() - sep);
+
+ QSize left_hint = docks[QInternal::LeftDock].size();
+ if (!left_hint.isValid())
+ left_hint = docks[QInternal::LeftDock].sizeHint();
+ QSize left_min = docks[QInternal::LeftDock].minimumSize();
+ QSize left_max = docks[QInternal::LeftDock].maximumSize();
+ left_hint = left_hint.boundedTo(left_max).expandedTo(left_min);
+
+ QSize right_hint = docks[QInternal::RightDock].size();
+ if (!right_hint.isValid())
+ right_hint = docks[QInternal::RightDock].sizeHint();
+ QSize right_min = docks[QInternal::RightDock].minimumSize();
+ QSize right_max = docks[QInternal::RightDock].maximumSize();
+ right_hint = right_hint.boundedTo(right_max).expandedTo(right_min);
+
+ QSize top_hint = docks[QInternal::TopDock].size();
+ if (!top_hint.isValid())
+ top_hint = docks[QInternal::TopDock].sizeHint();
+ QSize top_min = docks[QInternal::TopDock].minimumSize();
+ QSize top_max = docks[QInternal::TopDock].maximumSize();
+ top_hint = top_hint.boundedTo(top_max).expandedTo(top_min);
+
+ QSize bottom_hint = docks[QInternal::BottomDock].size();
+ if (!bottom_hint.isValid())
+ bottom_hint = docks[QInternal::BottomDock].sizeHint();
+ QSize bottom_min = docks[QInternal::BottomDock].minimumSize();
+ QSize bottom_max = docks[QInternal::BottomDock].maximumSize();
+ bottom_hint = bottom_hint.boundedTo(bottom_max).expandedTo(bottom_min);
+
+ if (_ver_struct_list != 0) {
+ QVector<QLayoutStruct> &ver_struct_list = *_ver_struct_list;
+ ver_struct_list.resize(3);
+
+ // top --------------------------------------------------
+ ver_struct_list[0].init();
+ ver_struct_list[0].stretch = 0;
+ ver_struct_list[0].sizeHint = top_hint.height();
+ ver_struct_list[0].minimumSize = top_min.height();
+ ver_struct_list[0].maximumSize = top_max.height();
+ ver_struct_list[0].expansive = false;
+ ver_struct_list[0].empty = docks[QInternal::TopDock].isEmpty();
+ ver_struct_list[0].pos = docks[QInternal::TopDock].rect.top();
+ ver_struct_list[0].size = docks[QInternal::TopDock].rect.height();
+
+ // center --------------------------------------------------
+ ver_struct_list[1].init();
+ ver_struct_list[1].stretch = center_hint.height();
+
+ bool tl_significant = corners[Qt::TopLeftCorner] == Qt::TopDockWidgetArea
+ || docks[QInternal::TopDock].isEmpty();
+ bool bl_significant = corners[Qt::BottomLeftCorner] == Qt::BottomDockWidgetArea
+ || docks[QInternal::BottomDock].isEmpty();
+ bool tr_significant = corners[Qt::TopRightCorner] == Qt::TopDockWidgetArea
+ || docks[QInternal::TopDock].isEmpty();
+ bool br_significant = corners[Qt::BottomRightCorner] == Qt::BottomDockWidgetArea
+ || docks[QInternal::BottomDock].isEmpty();
+
+ int left = (tl_significant && bl_significant) ? left_hint.height() : 0;
+ int right = (tr_significant && br_significant) ? right_hint.height() : 0;
+ ver_struct_list[1].sizeHint = qMax(left, center_hint.height(), right);
+
+ left = (tl_significant && bl_significant) ? left_min.height() : 0;
+ right = (tr_significant && br_significant) ? right_min.height() : 0;
+ ver_struct_list[1].minimumSize = qMax(left, center_min.height(), right);
+ ver_struct_list[1].maximumSize = have_central ? QWIDGETSIZE_MAX : 0;
+ ver_struct_list[1].expansive = have_central;
+ ver_struct_list[1].empty = docks[QInternal::LeftDock].isEmpty()
+ && !have_central
+ && docks[QInternal::RightDock].isEmpty();
+ ver_struct_list[1].pos = center_rect.top();
+ ver_struct_list[1].size = center_rect.height();
+
+ // bottom --------------------------------------------------
+ ver_struct_list[2].init();
+ ver_struct_list[2].stretch = 0;
+ ver_struct_list[2].sizeHint = bottom_hint.height();
+ ver_struct_list[2].minimumSize = bottom_min.height();
+ ver_struct_list[2].maximumSize = bottom_max.height();
+ ver_struct_list[2].expansive = false;
+ ver_struct_list[2].empty = docks[QInternal::BottomDock].isEmpty();
+ ver_struct_list[2].pos = docks[QInternal::BottomDock].rect.top();
+ ver_struct_list[2].size = docks[QInternal::BottomDock].rect.height();
+
+ for (int i = 0; i < 3; ++i) {
+ ver_struct_list[i].sizeHint
+ = qMax(ver_struct_list[i].sizeHint, ver_struct_list[i].minimumSize);
+ }
+ }
+
+ if (_hor_struct_list != 0) {
+ QVector<QLayoutStruct> &hor_struct_list = *_hor_struct_list;
+ hor_struct_list.resize(3);
+
+ // left --------------------------------------------------
+ hor_struct_list[0].init();
+ hor_struct_list[0].stretch = 0;
+ hor_struct_list[0].sizeHint = left_hint.width();
+ hor_struct_list[0].minimumSize = left_min.width();
+ hor_struct_list[0].maximumSize = left_max.width();
+ hor_struct_list[0].expansive = false;
+ hor_struct_list[0].empty = docks[QInternal::LeftDock].isEmpty();
+ hor_struct_list[0].pos = docks[QInternal::LeftDock].rect.left();
+ hor_struct_list[0].size = docks[QInternal::LeftDock].rect.width();
+
+ // center --------------------------------------------------
+ hor_struct_list[1].init();
+ hor_struct_list[1].stretch = center_hint.width();
+
+ bool tl_significant = corners[Qt::TopLeftCorner] == Qt::LeftDockWidgetArea
+ || docks[QInternal::LeftDock].isEmpty();
+ bool tr_significant = corners[Qt::TopRightCorner] == Qt::RightDockWidgetArea
+ || docks[QInternal::RightDock].isEmpty();
+ bool bl_significant = corners[Qt::BottomLeftCorner] == Qt::LeftDockWidgetArea
+ || docks[QInternal::LeftDock].isEmpty();
+ bool br_significant = corners[Qt::BottomRightCorner] == Qt::RightDockWidgetArea
+ || docks[QInternal::RightDock].isEmpty();
+
+ int top = (tl_significant && tr_significant) ? top_hint.width() : 0;
+ int bottom = (bl_significant && br_significant) ? bottom_hint.width() : 0;
+ hor_struct_list[1].sizeHint = qMax(top, center_hint.width(), bottom);
+
+ top = (tl_significant && tr_significant) ? top_min.width() : 0;
+ bottom = (bl_significant && br_significant) ? bottom_min.width() : 0;
+ hor_struct_list[1].minimumSize = qMax(top, center_min.width(), bottom);
+
+ hor_struct_list[1].maximumSize = have_central ? QWIDGETSIZE_MAX : 0;
+ hor_struct_list[1].expansive = have_central;
+ hor_struct_list[1].empty = !have_central;
+ hor_struct_list[1].pos = center_rect.left();
+ hor_struct_list[1].size = center_rect.width();
+
+ // right --------------------------------------------------
+ hor_struct_list[2].init();
+ hor_struct_list[2].stretch = 0;
+ hor_struct_list[2].sizeHint = right_hint.width();
+ hor_struct_list[2].minimumSize = right_min.width();
+ hor_struct_list[2].maximumSize = right_max.width();
+ hor_struct_list[2].expansive = false;
+ hor_struct_list[2].empty = docks[QInternal::RightDock].isEmpty();
+ hor_struct_list[2].pos = docks[QInternal::RightDock].rect.left();
+ hor_struct_list[2].size = docks[QInternal::RightDock].rect.width();
+
+ for (int i = 0; i < 3; ++i) {
+ hor_struct_list[i].sizeHint
+ = qMax(hor_struct_list[i].sizeHint, hor_struct_list[i].minimumSize);
+ }
+ }
+}
+
+void QDockAreaLayout::setGrid(QVector<QLayoutStruct> *ver_struct_list,
+ QVector<QLayoutStruct> *hor_struct_list)
+{
+
+ // top ---------------------------------------------------
+
+ if (!docks[QInternal::TopDock].isEmpty()) {
+ QRect r = docks[QInternal::TopDock].rect;
+ if (hor_struct_list != 0) {
+ r.setLeft(corners[Qt::TopLeftCorner] == Qt::TopDockWidgetArea
+ || docks[QInternal::LeftDock].isEmpty()
+ ? rect.left() : hor_struct_list->at(1).pos);
+ r.setRight(corners[Qt::TopRightCorner] == Qt::TopDockWidgetArea
+ || docks[QInternal::RightDock].isEmpty()
+ ? rect.right() : hor_struct_list->at(2).pos - sep - 1);
+ }
+ if (ver_struct_list != 0) {
+ r.setTop(rect.top());
+ r.setBottom(ver_struct_list->at(1).pos - sep - 1);
+ }
+ docks[QInternal::TopDock].rect = r;
+ docks[QInternal::TopDock].fitItems();
+ }
+
+ // bottom ---------------------------------------------------
+
+ if (!docks[QInternal::BottomDock].isEmpty()) {
+ QRect r = docks[QInternal::BottomDock].rect;
+ if (hor_struct_list != 0) {
+ r.setLeft(corners[Qt::BottomLeftCorner] == Qt::BottomDockWidgetArea
+ || docks[QInternal::LeftDock].isEmpty()
+ ? rect.left() : hor_struct_list->at(1).pos);
+ r.setRight(corners[Qt::BottomRightCorner] == Qt::BottomDockWidgetArea
+ || docks[QInternal::RightDock].isEmpty()
+ ? rect.right() : hor_struct_list->at(2).pos - sep - 1);
+ }
+ if (ver_struct_list != 0) {
+ r.setTop(ver_struct_list->at(2).pos);
+ r.setBottom(rect.bottom());
+ }
+ docks[QInternal::BottomDock].rect = r;
+ docks[QInternal::BottomDock].fitItems();
+ }
+
+ // left ---------------------------------------------------
+
+ if (!docks[QInternal::LeftDock].isEmpty()) {
+ QRect r = docks[QInternal::LeftDock].rect;
+ if (hor_struct_list != 0) {
+ r.setLeft(rect.left());
+ r.setRight(hor_struct_list->at(1).pos - sep - 1);
+ }
+ if (ver_struct_list != 0) {
+ r.setTop(corners[Qt::TopLeftCorner] == Qt::LeftDockWidgetArea
+ || docks[QInternal::TopDock].isEmpty()
+ ? rect.top() : ver_struct_list->at(1).pos);
+ r.setBottom(corners[Qt::BottomLeftCorner] == Qt::LeftDockWidgetArea
+ || docks[QInternal::BottomDock].isEmpty()
+ ? rect.bottom() : ver_struct_list->at(2).pos - sep - 1);
+ }
+ docks[QInternal::LeftDock].rect = r;
+ docks[QInternal::LeftDock].fitItems();
+ }
+
+ // right ---------------------------------------------------
+
+ if (!docks[QInternal::RightDock].isEmpty()) {
+ QRect r = docks[QInternal::RightDock].rect;
+ if (hor_struct_list != 0) {
+ r.setLeft(hor_struct_list->at(2).pos);
+ r.setRight(rect.right());
+ }
+ if (ver_struct_list != 0) {
+ r.setTop(corners[Qt::TopRightCorner] == Qt::RightDockWidgetArea
+ || docks[QInternal::TopDock].isEmpty()
+ ? rect.top() : ver_struct_list->at(1).pos);
+ r.setBottom(corners[Qt::BottomRightCorner] == Qt::RightDockWidgetArea
+ || docks[QInternal::BottomDock].isEmpty()
+ ? rect.bottom() : ver_struct_list->at(2).pos - sep - 1);
+ }
+ docks[QInternal::RightDock].rect = r;
+ docks[QInternal::RightDock].fitItems();
+ }
+
+ // center ---------------------------------------------------
+
+ if (hor_struct_list != 0) {
+ centralWidgetRect.setLeft(hor_struct_list->at(1).pos);
+ centralWidgetRect.setWidth(hor_struct_list->at(1).size);
+ }
+ if (ver_struct_list != 0) {
+ centralWidgetRect.setTop(ver_struct_list->at(1).pos);
+ centralWidgetRect.setHeight(ver_struct_list->at(1).size);
+ }
+}
+
+void QDockAreaLayout::fitLayout()
+{
+ QVector<QLayoutStruct> ver_struct_list(3);
+ QVector<QLayoutStruct> hor_struct_list(3);
+ getGrid(&ver_struct_list, &hor_struct_list);
+
+ qGeomCalc(ver_struct_list, 0, 3, rect.top(), rect.height(), sep);
+ qGeomCalc(hor_struct_list, 0, 3, rect.left(), rect.width(), sep);
+
+ setGrid(&ver_struct_list, &hor_struct_list);
+}
+
+void QDockAreaLayout::clear()
+{
+ for (int i = 0; i < QInternal::DockCount; ++i)
+ docks[i].clear();
+
+ rect = QRect(0, 0, -1, -1);
+ centralWidgetRect = QRect(0, 0, -1, -1);
+}
+
+QSize QDockAreaLayout::sizeHint() const
+{
+ int left_sep = 0;
+ int right_sep = 0;
+ int top_sep = 0;
+ int bottom_sep = 0;
+
+ if (centralWidgetItem != 0) {
+ left_sep = docks[QInternal::LeftDock].isEmpty() ? 0 : sep;
+ right_sep = docks[QInternal::RightDock].isEmpty() ? 0 : sep;
+ top_sep = docks[QInternal::TopDock].isEmpty() ? 0 : sep;
+ bottom_sep = docks[QInternal::BottomDock].isEmpty() ? 0 : sep;
+ }
+
+ QSize left = docks[QInternal::LeftDock].sizeHint() + QSize(left_sep, 0);
+ QSize right = docks[QInternal::RightDock].sizeHint() + QSize(right_sep, 0);
+ QSize top = docks[QInternal::TopDock].sizeHint() + QSize(0, top_sep);
+ QSize bottom = docks[QInternal::BottomDock].sizeHint() + QSize(0, bottom_sep);
+ QSize center = centralWidgetItem == 0 ? QSize(0, 0) : centralWidgetItem->sizeHint();
+
+ int row1 = top.width();
+ int row2 = left.width() + center.width() + right.width();
+ int row3 = bottom.width();
+ int col1 = left.height();
+ int col2 = top.height() + center.height() + bottom.height();
+ int col3 = right.height();
+
+ if (corners[Qt::TopLeftCorner] == Qt::LeftDockWidgetArea)
+ row1 += left.width();
+ else
+ col1 += top.height();
+
+ if (corners[Qt::TopRightCorner] == Qt::RightDockWidgetArea)
+ row1 += right.width();
+ else
+ col3 += top.height();
+
+ if (corners[Qt::BottomLeftCorner] == Qt::LeftDockWidgetArea)
+ row3 += left.width();
+ else
+ col1 += bottom.height();
+
+ if (corners[Qt::BottomRightCorner] == Qt::RightDockWidgetArea)
+ row3 += right.width();
+ else
+ col3 += bottom.height();
+
+ return QSize(qMax(row1, row2, row3), qMax(col1, col2, col3));
+}
+
+QSize QDockAreaLayout::minimumSize() const
+{
+ int left_sep = 0;
+ int right_sep = 0;
+ int top_sep = 0;
+ int bottom_sep = 0;
+
+ if (centralWidgetItem != 0) {
+ left_sep = docks[QInternal::LeftDock].isEmpty() ? 0 : sep;
+ right_sep = docks[QInternal::RightDock].isEmpty() ? 0 : sep;
+ top_sep = docks[QInternal::TopDock].isEmpty() ? 0 : sep;
+ bottom_sep = docks[QInternal::BottomDock].isEmpty() ? 0 : sep;
+ }
+
+ QSize left = docks[QInternal::LeftDock].minimumSize() + QSize(left_sep, 0);
+ QSize right = docks[QInternal::RightDock].minimumSize() + QSize(right_sep, 0);
+ QSize top = docks[QInternal::TopDock].minimumSize() + QSize(0, top_sep);
+ QSize bottom = docks[QInternal::BottomDock].minimumSize() + QSize(0, bottom_sep);
+ QSize center = centralWidgetItem == 0 ? QSize(0, 0) : centralWidgetItem->minimumSize();
+
+ int row1 = top.width();
+ int row2 = left.width() + center.width() + right.width();
+ int row3 = bottom.width();
+ int col1 = left.height();
+ int col2 = top.height() + center.height() + bottom.height();
+ int col3 = right.height();
+
+ if (corners[Qt::TopLeftCorner] == Qt::LeftDockWidgetArea)
+ row1 += left.width();
+ else
+ col1 += top.height();
+
+ if (corners[Qt::TopRightCorner] == Qt::RightDockWidgetArea)
+ row1 += right.width();
+ else
+ col3 += top.height();
+
+ if (corners[Qt::BottomLeftCorner] == Qt::LeftDockWidgetArea)
+ row3 += left.width();
+ else
+ col1 += bottom.height();
+
+ if (corners[Qt::BottomRightCorner] == Qt::RightDockWidgetArea)
+ row3 += right.width();
+ else
+ col3 += bottom.height();
+
+ return QSize(qMax(row1, row2, row3), qMax(col1, col2, col3));
+}
+
+bool QDockAreaLayout::restoreDockWidget(QDockWidget *dockWidget)
+{
+ QList<int> index = indexOfPlaceHolder(dockWidget->objectName());
+ if (index.isEmpty())
+ return false;
+
+ QDockAreaLayoutItem &item = this->item(index);
+ QPlaceHolderItem *placeHolder = item.placeHolderItem;
+ Q_ASSERT(placeHolder != 0);
+
+ item.widgetItem = new QDockWidgetItem(dockWidget);
+
+ if (placeHolder->window) {
+ QDesktopWidget desktop;
+ QRect r = constrainedRect(placeHolder->topLevelRect, desktop.screenGeometry(dockWidget));
+ dockWidget->d_func()->setWindowState(true, true, r);
+ }
+ dockWidget->show();
+// dockWidget->setVisible(!placeHolder->hidden);
+#ifdef Q_WS_X11
+ if (placeHolder->window) // gets rid of the X11BypassWindowManager window flag
+ dockWidget->d_func()->setWindowState(true);
+#endif
+
+ item.placeHolderItem = 0;
+ delete placeHolder;
+
+ return true;
+}
+
+void QDockAreaLayout::addDockWidget(QInternal::DockPosition pos, QDockWidget *dockWidget,
+ Qt::Orientation orientation)
+{
+ QLayoutItem *dockWidgetItem = new QDockWidgetItem(dockWidget);
+ QDockAreaLayoutInfo &info = docks[pos];
+ if (orientation == info.o || info.item_list.count() <= 1) {
+ // empty dock areas, or dock areas containing exactly one widget can have their orientation
+ // switched.
+ info.o = orientation;
+
+ QDockAreaLayoutItem new_item(dockWidgetItem);
+ info.item_list.append(new_item);
+#ifndef QT_NO_TABBAR
+ if (info.tabbed && !new_item.skip()) {
+ info.updateTabBar();
+ info.setCurrentTabId(tabId(new_item));
+ }
+#endif
+ } else {
+#ifndef QT_NO_TABBAR
+ int tbshape = info.tabBarShape;
+#else
+ int tbshape = 0;
+#endif
+ QDockAreaLayoutInfo new_info(sep, pos, orientation, tbshape, mainWindow);
+ new_info.item_list.append(new QDockAreaLayoutInfo(info));
+ new_info.item_list.append(dockWidgetItem);
+ info = new_info;
+ }
+
+ QList<int> index = indexOfPlaceHolder(dockWidget->objectName());
+ if (!index.isEmpty())
+ remove(index);
+}
+
+void QDockAreaLayout::tabifyDockWidget(QDockWidget *first, QDockWidget *second)
+{
+ QList<int> path = indexOf(first);
+ if (path.isEmpty())
+ return;
+
+ QDockAreaLayoutInfo *info = this->info(path);
+ Q_ASSERT(info != 0);
+ info->tab(path.last(), new QDockWidgetItem(second));
+
+ QList<int> index = indexOfPlaceHolder(second->objectName());
+ if (!index.isEmpty())
+ remove(index);
+}
+
+void QDockAreaLayout::splitDockWidget(QDockWidget *after,
+ QDockWidget *dockWidget,
+ Qt::Orientation orientation)
+{
+ QList<int> path = indexOf(after);
+ if (path.isEmpty())
+ return;
+
+ QDockAreaLayoutInfo *info = this->info(path);
+ Q_ASSERT(info != 0);
+ info->split(path.last(), orientation, new QDockWidgetItem(dockWidget));
+
+ QList<int> index = indexOfPlaceHolder(dockWidget->objectName());
+ if (!index.isEmpty())
+ remove(index);
+}
+
+void QDockAreaLayout::apply(bool animate)
+{
+ QWidgetAnimator *widgetAnimator
+ = qobject_cast<QMainWindowLayout*>(mainWindow->layout())->widgetAnimator;
+
+ for (int i = 0; i < QInternal::DockCount; ++i)
+ docks[i].apply(animate);
+ if (centralWidgetItem != 0 && !centralWidgetItem->isEmpty()) {
+ widgetAnimator->animate(centralWidgetItem->widget(), centralWidgetRect,
+ animate);
+ }
+
+ if (sep == 1)
+ updateSeparatorWidgets();
+}
+
+void QDockAreaLayout::paintSeparators(QPainter *p, QWidget *widget,
+ const QRegion &clip,
+ const QPoint &mouse) const
+{
+ for (int i = 0; i < QInternal::DockCount; ++i) {
+ const QDockAreaLayoutInfo &dock = docks[i];
+ if (dock.isEmpty())
+ continue;
+ QRect r = separatorRect(i);
+ if (clip.contains(r)) {
+ Qt::Orientation opposite = dock.o == Qt::Horizontal
+ ? Qt::Vertical : Qt::Horizontal;
+ paintSep(p, widget, r, opposite, r.contains(mouse));
+ }
+ if (clip.contains(dock.rect))
+ dock.paintSeparators(p, widget, clip, mouse);
+ }
+}
+
+QRegion QDockAreaLayout::separatorRegion() const
+{
+ QRegion result;
+
+ for (int i = 0; i < QInternal::DockCount; ++i) {
+ const QDockAreaLayoutInfo &dock = docks[i];
+ if (dock.isEmpty())
+ continue;
+ result |= separatorRect(i);
+ result |= dock.separatorRegion();
+ }
+
+ return result;
+}
+
+int QDockAreaLayout::separatorMove(QList<int> separator, const QPoint &origin,
+ const QPoint &dest,
+ QVector<QLayoutStruct> *cache)
+{
+ int delta = 0;
+ int index = separator.last();
+
+ if (separator.count() > 1) {
+ QDockAreaLayoutInfo *info = this->info(separator);
+ delta = pick(info->o, dest - origin);
+ if (delta != 0)
+ delta = info->separatorMove(index, delta, cache);
+ info->apply(false);
+ return delta;
+ }
+
+ if (cache->isEmpty()) {
+ QVector<QLayoutStruct> &list = *cache;
+
+ if (index == QInternal::LeftDock || index == QInternal::RightDock)
+ getGrid(0, &list);
+ else
+ getGrid(&list, 0);
+ }
+
+ QVector<QLayoutStruct> list = *cache;
+ int sep_index = index == QInternal::LeftDock || index == QInternal::TopDock
+ ? 0 : 1;
+ Qt::Orientation o = index == QInternal::LeftDock || index == QInternal::RightDock
+ ? Qt::Horizontal
+ : Qt::Vertical;
+
+ delta = pick(o, dest - origin);
+ delta = separatorMoveHelper(list, sep_index, delta, sep);
+
+ if (index == QInternal::LeftDock || index == QInternal::RightDock)
+ setGrid(0, &list);
+ else
+ setGrid(&list, 0);
+
+ apply(false);
+
+ return delta;
+}
+
+// Sets the correct positions for the seperator widgets
+// Allocates new sepearator widgets with getSeparatorWidget
+void QDockAreaLayout::updateSeparatorWidgets() const
+{
+ QDockAreaLayout *that = const_cast<QDockAreaLayout*>(this);
+
+ int j = 0;
+
+ for (int i = 0; i < QInternal::DockCount; ++i) {
+ const QDockAreaLayoutInfo &dock = docks[i];
+ if (dock.isEmpty())
+ continue;
+
+ QWidget *sepWidget;
+ if (j < separatorWidgets.size()) {
+ sepWidget = separatorWidgets.at(j);
+ } else {
+ sepWidget = qobject_cast<QMainWindowLayout*>(mainWindow->layout())->getSeparatorWidget();
+ that->separatorWidgets.append(sepWidget);
+ }
+ j++;
+
+#ifndef QT_MAC_USE_COCOA
+ sepWidget->raise();
+#endif
+ QRect sepRect = separatorRect(i).adjusted(-2, -2, 2, 2);
+ sepWidget->setGeometry(sepRect);
+ sepWidget->setMask( QRegion(separatorRect(i).translated( - sepRect.topLeft())));
+ sepWidget->show();
+ }
+ for (int i = j; i < separatorWidgets.size(); ++i)
+ separatorWidgets.at(i)->hide();
+
+ that->separatorWidgets.resize(j);
+}
+
+QLayoutItem *QDockAreaLayout::itemAt(int *x, int index) const
+{
+ Q_ASSERT(x != 0);
+
+ for (int i = 0; i < QInternal::DockCount; ++i) {
+ const QDockAreaLayoutInfo &dock = docks[i];
+ if (QLayoutItem *ret = dock.itemAt(x, index))
+ return ret;
+ }
+
+ if (centralWidgetItem && (*x)++ == index)
+ return centralWidgetItem;
+
+ return 0;
+}
+
+QLayoutItem *QDockAreaLayout::takeAt(int *x, int index)
+{
+ Q_ASSERT(x != 0);
+
+ for (int i = 0; i < QInternal::DockCount; ++i) {
+ QDockAreaLayoutInfo &dock = docks[i];
+ if (QLayoutItem *ret = dock.takeAt(x, index))
+ return ret;
+ }
+
+ if (centralWidgetItem && (*x)++ == index) {
+ QLayoutItem *ret = centralWidgetItem;
+ centralWidgetItem = 0;
+ return ret;
+ }
+
+ return 0;
+}
+
+void QDockAreaLayout::deleteAllLayoutItems()
+{
+ for (int i = 0; i < QInternal::DockCount; ++i)
+ docks[i].deleteAllLayoutItems();
+}
+
+#ifndef QT_NO_TABBAR
+QSet<QTabBar*> QDockAreaLayout::usedTabBars() const
+{
+ QSet<QTabBar*> result;
+ for (int i = 0; i < QInternal::DockCount; ++i) {
+ const QDockAreaLayoutInfo &dock = docks[i];
+ result += dock.usedTabBars();
+ }
+ return result;
+}
+#endif
+
+// Returns the set of all used separator widgets
+QSet<QWidget*> QDockAreaLayout::usedSeparatorWidgets() const
+{
+ QSet<QWidget*> result;
+
+ foreach (QWidget *sepWidget, separatorWidgets)
+ result << sepWidget;
+ for (int i = 0; i < QInternal::DockCount; ++i) {
+ const QDockAreaLayoutInfo &dock = docks[i];
+ result += dock.usedSeparatorWidgets();
+ }
+ return result;
+}
+
+QRect QDockAreaLayout::gapRect(QList<int> path) const
+{
+ const QDockAreaLayoutInfo *info = this->info(path);
+ if (info == 0)
+ return QRect();
+ const QList<QDockAreaLayoutItem> &item_list = info->item_list;
+ Qt::Orientation o = info->o;
+ int index = path.last();
+ if (index < 0 || index >= item_list.count())
+ return QRect();
+ const QDockAreaLayoutItem &item = item_list.at(index);
+ if (!(item.flags & QDockAreaLayoutItem::GapItem))
+ return QRect();
+
+ QRect result;
+
+#ifndef QT_NO_TABBAR
+ if (info->tabbed) {
+ result = info->tabContentRect();
+ } else
+#endif
+ {
+ int pos = item.pos;
+ int size = item.size;
+
+ int prev = info->prev(index);
+ int next = info->next(index);
+
+ if (prev != -1 && !(item_list.at(prev).flags & QDockAreaLayoutItem::GapItem)) {
+ pos += sep;
+ size -= sep;
+ }
+ if (next != -1 && !(item_list.at(next).flags & QDockAreaLayoutItem::GapItem))
+ size -= sep;
+
+ QPoint p;
+ rpick(o, p) = pos;
+ rperp(o, p) = perp(o, info->rect.topLeft());
+ QSize s;
+ rpick(o, s) = size;
+ rperp(o, s) = perp(o, info->rect.size());
+
+ result = QRect(p, s);
+ }
+
+ return result;
+}
+
+void QDockAreaLayout::keepSize(QDockWidget *w)
+{
+ QList<int> path = indexOf(w);
+ if (path.isEmpty())
+ return;
+ QDockAreaLayoutItem &item = this->item(path);
+ if (item.size != -1)
+ item.flags |= QDockAreaLayoutItem::KeepSize;
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_DOCKWIDGET
diff --git a/src/gui/widgets/qdockarealayout_p.h b/src/gui/widgets/qdockarealayout_p.h
new file mode 100644
index 0000000000..7c5bd184a1
--- /dev/null
+++ b/src/gui/widgets/qdockarealayout_p.h
@@ -0,0 +1,303 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QDOCKAREALAYOUT_P_H
+#define QDOCKAREALAYOUT_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 "QtCore/qrect.h"
+#include "QtCore/qpair.h"
+#include "QtCore/qlist.h"
+#include "QtCore/qvector.h"
+#include "QtGui/qlayout.h"
+
+#ifndef QT_NO_DOCKWIDGET
+
+QT_BEGIN_NAMESPACE
+
+class QLayoutItem;
+class QWidget;
+class QLayoutItem;
+class QDockAreaLayoutInfo;
+class QPlaceHolderItem;
+class QDockWidget;
+class QMainWindow;
+class QWidgetAnimator;
+class QMainWindowLayout;
+struct QLayoutStruct;
+class QTabBar;
+
+// The classes in this file represent the tree structure that represents all the docks
+// Also see the wiki internal documentation
+// At the root of the tree is: QDockAreaLayout, which handles all 4 sides, so there is only one.
+// For each side it has one QDockAreaLayoutInfo child. (See QDockAreaLayout::docks.)
+// The QDockAreaLayoutInfo have QDockAreaLayoutItems as children (See QDockAreaLayoutInfo::item_list),
+// which then has one QDockAreaLayoutInfo as a child. (QDockAreaLayoutItem::subInfo) or
+// a widgetItem if this is a node of the tree (QDockAreaLayoutItem::widgetItem)
+//
+// A path indetifies uniquely one object in this tree, the first number beeing the side and all the following
+// indexes into the QDockAreaLayoutInfo::item_list.
+
+struct QDockAreaLayoutItem
+{
+ enum ItemFlags { NoFlags = 0, GapItem = 1, KeepSize = 2 };
+
+ QDockAreaLayoutItem(QLayoutItem *_widgetItem = 0);
+ QDockAreaLayoutItem(QDockAreaLayoutInfo *_subinfo);
+ QDockAreaLayoutItem(QPlaceHolderItem *_placeHolderItem);
+ QDockAreaLayoutItem(const QDockAreaLayoutItem &other);
+ ~QDockAreaLayoutItem();
+
+ QDockAreaLayoutItem &operator = (const QDockAreaLayoutItem &other);
+
+ bool skip() const;
+ QSize minimumSize() const;
+ QSize maximumSize() const;
+ QSize sizeHint() const;
+ bool expansive(Qt::Orientation o) const;
+
+ QLayoutItem *widgetItem;
+ QDockAreaLayoutInfo *subinfo;
+ QPlaceHolderItem *placeHolderItem;
+ int pos;
+ int size;
+ uint flags;
+};
+
+class Q_AUTOTEST_EXPORT QPlaceHolderItem
+{
+public:
+ QPlaceHolderItem() : hidden(false), window(false) {}
+ QPlaceHolderItem(QWidget *w);
+
+ QString objectName;
+ bool hidden, window;
+ QRect topLevelRect;
+};
+
+class Q_AUTOTEST_EXPORT QDockAreaLayoutInfo
+{
+public:
+ QDockAreaLayoutInfo();
+ QDockAreaLayoutInfo(int _sep, QInternal::DockPosition _dockPos, Qt::Orientation _o,
+ int tbhape, QMainWindow *window);
+
+ QSize minimumSize() const;
+ QSize maximumSize() const;
+ QSize sizeHint() const;
+ QSize size() const;
+
+ bool insertGap(QList<int> path, QLayoutItem *dockWidgetItem);
+ QLayoutItem *plug(QList<int> path);
+ QLayoutItem *unplug(QList<int> path);
+ enum TabMode { NoTabs, AllowTabs, ForceTabs };
+ QList<int> gapIndex(const QPoint &pos, bool nestingEnabled,
+ TabMode tabMode) const;
+ void remove(QList<int> path);
+ void unnest(int index);
+ void split(int index, Qt::Orientation orientation, QLayoutItem *dockWidgetItem);
+ void tab(int index, QLayoutItem *dockWidgetItem);
+ QDockAreaLayoutItem &item(QList<int> path);
+ QDockAreaLayoutInfo *info(QList<int> path);
+ QDockAreaLayoutInfo *info(QWidget *widget);
+
+ enum { // sentinel values used to validate state data
+ SequenceMarker = 0xfc,
+ TabMarker = 0xfa,
+ WidgetMarker = 0xfb
+ };
+ void saveState(QDataStream &stream) const;
+ bool restoreState(QDataStream &stream, QList<QDockWidget*> &widgets, bool testing);
+
+ void fitItems();
+ bool expansive(Qt::Orientation o) const;
+ int changeSize(int index, int size, bool below);
+ QRect itemRect(int index) const;
+ QRect itemRect(QList<int> path) const;
+ QRect separatorRect(int index) const;
+ QRect separatorRect(QList<int> path) const;
+
+ void clear();
+ bool isEmpty() const;
+ QList<int> findSeparator(const QPoint &pos) const;
+ int next(int idx) const;
+ int prev(int idx) const;
+
+ QList<int> indexOf(QWidget *widget) const;
+ QList<int> indexOfPlaceHolder(const QString &objectName) const;
+
+ void apply(bool animate);
+
+ void paintSeparators(QPainter *p, QWidget *widget, const QRegion &clip,
+ const QPoint &mouse) const;
+ QRegion separatorRegion() const;
+ int separatorMove(int index, int delta, QVector<QLayoutStruct> *cache);
+
+ QLayoutItem *itemAt(int *x, int index) const;
+ QLayoutItem *takeAt(int *x, int index);
+ void deleteAllLayoutItems();
+
+ QMainWindowLayout *mainWindowLayout() const;
+
+ int sep;
+ QVector<QWidget*> separatorWidgets;
+ QInternal::DockPosition dockPos;
+ Qt::Orientation o;
+ QRect rect;
+ QMainWindow *mainWindow;
+ QList<QDockAreaLayoutItem> item_list;
+
+ void updateSeparatorWidgets() const;
+ QSet<QWidget*> usedSeparatorWidgets() const;
+
+#ifndef QT_NO_TABBAR
+ quintptr currentTabId() const;
+ void setCurrentTab(QWidget *widget);
+ void setCurrentTabId(quintptr id);
+ QRect tabContentRect() const;
+ bool tabbed;
+ QTabBar *tabBar;
+ QSize tabBarMin, tabBarHint;
+ int tabBarShape;
+ bool tabBarVisible;
+
+ void updateTabBar() const;
+ void setTabBarShape(int shape);
+ QSize tabBarMinimumSize() const;
+ QSize tabBarSizeHint() const;
+
+ QSet<QTabBar*> usedTabBars() const;
+#endif // QT_NO_TABBAR
+};
+
+class Q_AUTOTEST_EXPORT QDockAreaLayout
+{
+public:
+ enum { EmptyDropAreaSize = 80 }; // when a dock area is empty, how "wide" is it?
+
+ Qt::DockWidgetArea corners[4]; // use a Qt::Corner for indexing
+ QRect rect;
+ QLayoutItem *centralWidgetItem;
+ QMainWindow *mainWindow;
+ QRect centralWidgetRect;
+ QDockAreaLayout(QMainWindow *win);
+ QDockAreaLayoutInfo docks[4];
+ int sep; // separator extent
+ QVector<QWidget*> separatorWidgets;
+
+ bool isValid() const;
+
+ enum { DockWidgetStateMarker = 0xfd };
+ void saveState(QDataStream &stream) const;
+ bool restoreState(QDataStream &stream, const QList<QDockWidget*> &widgets, bool testing = false);
+
+ QList<int> indexOfPlaceHolder(const QString &objectName) const;
+ QList<int> indexOf(QWidget *dockWidget) const;
+ QList<int> gapIndex(const QPoint &pos) const;
+ QList<int> findSeparator(const QPoint &pos) const;
+
+ QDockAreaLayoutItem &item(QList<int> path);
+ QDockAreaLayoutInfo *info(QList<int> path);
+ const QDockAreaLayoutInfo *info(QList<int> path) const;
+ QDockAreaLayoutInfo *info(QWidget *widget);
+ QRect itemRect(QList<int> path) const;
+ QRect separatorRect(int index) const;
+ QRect separatorRect(QList<int> path) const;
+
+ bool insertGap(QList<int> path, QLayoutItem *dockWidgetItem);
+ QLayoutItem *plug(QList<int> path);
+ QLayoutItem *unplug(QList<int> path);
+ void remove(QList<int> path);
+
+ void fitLayout();
+
+ void clear();
+
+ QSize sizeHint() const;
+ QSize minimumSize() const;
+
+ void addDockWidget(QInternal::DockPosition pos, QDockWidget *dockWidget, Qt::Orientation orientation);
+ bool restoreDockWidget(QDockWidget *dockWidget);
+ void splitDockWidget(QDockWidget *after, QDockWidget *dockWidget,
+ Qt::Orientation orientation);
+ void tabifyDockWidget(QDockWidget *first, QDockWidget *second);
+
+ void apply(bool animate);
+
+ void paintSeparators(QPainter *p, QWidget *widget, const QRegion &clip,
+ const QPoint &mouse) const;
+ QRegion separatorRegion() const;
+ int separatorMove(QList<int> separator, const QPoint &origin, const QPoint &dest,
+ QVector<QLayoutStruct> *cache);
+ void updateSeparatorWidgets() const;
+
+ QLayoutItem *itemAt(int *x, int index) const;
+ QLayoutItem *takeAt(int *x, int index);
+ void deleteAllLayoutItems();
+
+ void getGrid(QVector<QLayoutStruct> *ver_struct_list,
+ QVector<QLayoutStruct> *hor_struct_list);
+ void setGrid(QVector<QLayoutStruct> *ver_struct_list,
+ QVector<QLayoutStruct> *hor_struct_list);
+
+ QRect gapRect(QList<int> path) const;
+
+ void keepSize(QDockWidget *w);
+
+ QSet<QTabBar*> usedTabBars() const;
+ QSet<QWidget*> usedSeparatorWidgets() const;
+};
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_QDOCKWIDGET
+
+#endif // QDOCKAREALAYOUT_P_H
diff --git a/src/gui/widgets/qdockwidget.cpp b/src/gui/widgets/qdockwidget.cpp
new file mode 100644
index 0000000000..865b19cb4d
--- /dev/null
+++ b/src/gui/widgets/qdockwidget.cpp
@@ -0,0 +1,1594 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qdockwidget.h"
+
+#ifndef QT_NO_DOCKWIDGET
+#include <qaction.h>
+#include <qapplication.h>
+#include <qdesktopwidget.h>
+#include <qdrawutil.h>
+#include <qevent.h>
+#include <qfontmetrics.h>
+#include <qmainwindow.h>
+#include <qrubberband.h>
+#include <qstylepainter.h>
+#include <qtoolbutton.h>
+#include <qdebug.h>
+
+#include <private/qwidgetresizehandler_p.h>
+
+#include "qdockwidget_p.h"
+#include "qmainwindowlayout_p.h"
+#ifdef Q_WS_MAC
+#include <private/qt_mac_p.h>
+#include <qmacstyle_mac.h>
+#endif
+
+QT_BEGIN_NAMESPACE
+
+extern QString qt_setWindowTitle_helperHelper(const QString&, const QWidget*); // qwidget.cpp
+
+extern QHash<QByteArray, QFont> *qt_app_fonts_hash(); // qapplication.cpp
+
+static inline bool hasFeature(const QDockWidget *dockwidget, QDockWidget::DockWidgetFeature feature)
+{ return (dockwidget->features() & feature) == feature; }
+
+
+/*
+ A Dock Window:
+
+ [+] is the float button
+ [X] is the close button
+
+ +-------------------------------+
+ | Dock Window Title [+][X]|
+ +-------------------------------+
+ | |
+ | place to put the single |
+ | QDockWidget child (this space |
+ | does not yet have a name) |
+ | |
+ | |
+ | |
+ | |
+ | |
+ | |
+ | |
+ | |
+ | |
+ +-------------------------------+
+
+*/
+
+/******************************************************************************
+** QDockWidgetTitleButton
+*/
+
+class QDockWidgetTitleButton : public QAbstractButton
+{
+ Q_OBJECT
+
+public:
+ QDockWidgetTitleButton(QDockWidget *dockWidget);
+
+ QSize sizeHint() const;
+ inline QSize minimumSizeHint() const
+ { return sizeHint(); }
+
+ void enterEvent(QEvent *event);
+ void leaveEvent(QEvent *event);
+ void paintEvent(QPaintEvent *event);
+};
+
+
+QDockWidgetTitleButton::QDockWidgetTitleButton(QDockWidget *dockWidget)
+ : QAbstractButton(dockWidget)
+{
+ setFocusPolicy(Qt::NoFocus);
+}
+
+QSize QDockWidgetTitleButton::sizeHint() const
+{
+ ensurePolished();
+
+ int size = 2*style()->pixelMetric(QStyle::PM_DockWidgetTitleBarButtonMargin, 0, this);
+ if (!icon().isNull()) {
+ int iconSize = style()->pixelMetric(QStyle::PM_SmallIconSize, 0, this);
+ QSize sz = icon().actualSize(QSize(iconSize, iconSize));
+ size += qMax(sz.width(), sz.height());
+ }
+
+ return QSize(size, size);
+}
+
+void QDockWidgetTitleButton::enterEvent(QEvent *event)
+{
+ if (isEnabled()) update();
+ QAbstractButton::enterEvent(event);
+}
+
+void QDockWidgetTitleButton::leaveEvent(QEvent *event)
+{
+ if (isEnabled()) update();
+ QAbstractButton::leaveEvent(event);
+}
+
+void QDockWidgetTitleButton::paintEvent(QPaintEvent *)
+{
+ QPainter p(this);
+
+ QRect r = rect();
+ QStyleOptionToolButton opt;
+ opt.init(this);
+ opt.state |= QStyle::State_AutoRaise;
+
+ if (style()->styleHint(QStyle::SH_DockWidget_ButtonsHaveFrame, 0, this))
+ {
+ if (isEnabled() && underMouse() && !isChecked() && !isDown())
+ opt.state |= QStyle::State_Raised;
+ if (isChecked())
+ opt.state |= QStyle::State_On;
+ if (isDown())
+ opt.state |= QStyle::State_Sunken;
+ style()->drawPrimitive(QStyle::PE_PanelButtonTool, &opt, &p, this);
+ }
+
+ opt.icon = icon();
+ opt.subControls = 0;
+ opt.activeSubControls = 0;
+ opt.features = QStyleOptionToolButton::None;
+ opt.arrowType = Qt::NoArrow;
+ int size = style()->pixelMetric(QStyle::PM_SmallIconSize, 0, this);
+ opt.iconSize = QSize(size, size);
+ style()->drawComplexControl(QStyle::CC_ToolButton, &opt, &p, this);
+}
+
+/******************************************************************************
+** QDockWidgetLayout
+*/
+
+QDockWidgetLayout::QDockWidgetLayout(QWidget *parent)
+ : QLayout(parent), verticalTitleBar(false), item_list(RoleCount, 0)
+{
+}
+
+QDockWidgetLayout::~QDockWidgetLayout()
+{
+ qDeleteAll(item_list);
+}
+
+bool QDockWidgetLayout::nativeWindowDeco() const
+{
+ return nativeWindowDeco(parentWidget()->isWindow());
+}
+
+bool QDockWidgetLayout::nativeWindowDeco(bool floating) const
+{
+#if defined(Q_WS_X11) || defined(Q_WS_QWS) || defined(Q_OS_WINCE)
+ Q_UNUSED(floating);
+ return false;
+#else
+ return floating && item_list[QDockWidgetLayout::TitleBar] == 0;
+#endif
+}
+
+
+void QDockWidgetLayout::addItem(QLayoutItem*)
+{
+ qWarning() << "QDockWidgetLayout::addItem(): please use QDockWidgetLayout::setWidget()";
+ return;
+}
+
+QLayoutItem *QDockWidgetLayout::itemAt(int index) const
+{
+ int cnt = 0;
+ foreach (QLayoutItem *item, item_list) {
+ if (item == 0)
+ continue;
+ if (index == cnt++)
+ return item;
+ }
+ return 0;
+}
+
+QLayoutItem *QDockWidgetLayout::takeAt(int index)
+{
+ int j = 0;
+ for (int i = 0; i < item_list.count(); ++i) {
+ QLayoutItem *item = item_list.at(i);
+ if (item == 0)
+ continue;
+ if (index == j) {
+ item_list[i] = 0;
+ invalidate();
+ return item;
+ }
+ ++j;
+ }
+ return 0;
+}
+
+int QDockWidgetLayout::count() const
+{
+ int result = 0;
+ foreach (QLayoutItem *item, item_list) {
+ if (item != 0)
+ ++result;
+ }
+ return result;
+}
+
+QSize QDockWidgetLayout::sizeFromContent(const QSize &content, bool floating) const
+{
+ QSize result = content;
+
+ if (verticalTitleBar) {
+ result.setHeight(qMax(result.height(), minimumTitleWidth()));
+ result.setWidth(qMax(content.width(), 0));
+ } else {
+ result.setHeight(qMax(result.height(), 0));
+ result.setWidth(qMax(content.width(), minimumTitleWidth()));
+ }
+
+ QDockWidget *w = qobject_cast<QDockWidget*>(parentWidget());
+ const bool nativeDeco = nativeWindowDeco(floating);
+
+ int fw = floating && !nativeDeco
+ ? w->style()->pixelMetric(QStyle::PM_DockWidgetFrameWidth, 0, w)
+ : 0;
+
+ const int th = titleHeight();
+ if (!nativeDeco) {
+ if (verticalTitleBar)
+ result += QSize(th + 2*fw, 2*fw);
+ else
+ result += QSize(2*fw, th + 2*fw);
+ }
+
+ result.setHeight(qMin(result.height(), (int) QWIDGETSIZE_MAX));
+ result.setWidth(qMin(result.width(), (int) QWIDGETSIZE_MAX));
+
+ if (content.width() < 0)
+ result.setWidth(-1);
+ if (content.height() < 0)
+ result.setHeight(-1);
+
+ int left, top, right, bottom;
+ w->getContentsMargins(&left, &top, &right, &bottom);
+ //we need to substract the contents margin (it will be added by the caller)
+ QSize min = w->minimumSize() - QSize(left + right, top + bottom);
+ QSize max = w->maximumSize() - QSize(left + right, top + bottom);
+
+ /* A floating dockwidget will automatically get its minimumSize set to the layout's
+ minimum size + deco. We're *not* interested in this, we only take minimumSize()
+ into account if the user set it herself. Otherwise we end up expanding the result
+ of a calculation for a non-floating dock widget to a floating dock widget's
+ minimum size + window decorations. */
+
+ uint explicitMin = 0;
+ uint explicitMax = 0;
+ if (w->d_func()->extra != 0) {
+ explicitMin = w->d_func()->extra->explicitMinSize;
+ explicitMax = w->d_func()->extra->explicitMaxSize;
+ }
+
+ if (!(explicitMin & Qt::Horizontal) || min.width() == 0)
+ min.setWidth(-1);
+ if (!(explicitMin & Qt::Vertical) || min.height() == 0)
+ min.setHeight(-1);
+
+ if (!(explicitMax & Qt::Horizontal))
+ max.setWidth(QWIDGETSIZE_MAX);
+ if (!(explicitMax & Qt::Vertical))
+ max.setHeight(QWIDGETSIZE_MAX);
+
+ return result.boundedTo(max).expandedTo(min);
+}
+
+QSize QDockWidgetLayout::sizeHint() const
+{
+ QDockWidget *w = qobject_cast<QDockWidget*>(parentWidget());
+
+ QSize content(-1, -1);
+ if (item_list[Content] != 0)
+ content = item_list[Content]->sizeHint();
+
+ return sizeFromContent(content, w->isFloating());
+}
+
+QSize QDockWidgetLayout::maximumSize() const
+{
+ if (item_list[Content] != 0) {
+ const QSize content = item_list[Content]->maximumSize();
+ return sizeFromContent(content, parentWidget()->isWindow());
+ } else {
+ return parentWidget()->maximumSize();
+ }
+
+}
+
+QSize QDockWidgetLayout::minimumSize() const
+{
+ QDockWidget *w = qobject_cast<QDockWidget*>(parentWidget());
+
+ QSize content(0, 0);
+ if (item_list[Content] != 0)
+ content = item_list[Content]->minimumSize();
+
+ return sizeFromContent(content, w->isFloating());
+}
+
+QWidget *QDockWidgetLayout::widgetForRole(Role r) const
+{
+ QLayoutItem *item = item_list.at(r);
+ return item == 0 ? 0 : item->widget();
+}
+
+QLayoutItem *QDockWidgetLayout::itemForRole(Role r) const
+{
+ return item_list.at(r);
+}
+
+void QDockWidgetLayout::setWidgetForRole(Role r, QWidget *w)
+{
+ QWidget *old = widgetForRole(r);
+ if (old != 0) {
+ old->hide();
+ removeWidget(old);
+ }
+
+ if (w != 0) {
+ addChildWidget(w);
+ item_list[r] = new QWidgetItemV2(w);
+ w->show();
+ } else {
+ item_list[r] = 0;
+ }
+
+ invalidate();
+}
+
+static inline int pick(bool vertical, const QSize &size)
+{
+ return vertical ? size.height() : size.width();
+}
+
+static inline int perp(bool vertical, const QSize &size)
+{
+ return vertical ? size.width() : size.height();
+}
+
+int QDockWidgetLayout::minimumTitleWidth() const
+{
+ QDockWidget *q = qobject_cast<QDockWidget*>(parentWidget());
+
+ if (QWidget *title = widgetForRole(TitleBar))
+ return pick(verticalTitleBar, title->minimumSizeHint());
+
+ QSize closeSize(0, 0);
+ QSize floatSize(0, 0);
+ if (QLayoutItem *item = item_list[CloseButton])
+ closeSize = item->sizeHint();
+ if (QLayoutItem *item = item_list[FloatButton])
+ floatSize = item->sizeHint();
+
+ int titleHeight = this->titleHeight();
+
+ int mw = q->style()->pixelMetric(QStyle::PM_DockWidgetTitleMargin, 0, q);
+ int fw = q->style()->pixelMetric(QStyle::PM_DockWidgetFrameWidth, 0, q);
+
+ return pick(verticalTitleBar, closeSize)
+ + pick(verticalTitleBar, floatSize)
+ + titleHeight + 2*fw + 3*mw;
+}
+
+int QDockWidgetLayout::titleHeight() const
+{
+ QDockWidget *q = qobject_cast<QDockWidget*>(parentWidget());
+
+ if (QWidget *title = widgetForRole(TitleBar))
+ return perp(verticalTitleBar, title->sizeHint());
+
+ QSize closeSize(0, 0);
+ QSize floatSize(0, 0);
+ if (QLayoutItem *item = item_list[CloseButton])
+ closeSize = item->widget()->sizeHint();
+ if (QLayoutItem *item = item_list[FloatButton])
+ floatSize = item->widget()->sizeHint();
+
+ int buttonHeight = qMax(perp(verticalTitleBar, closeSize),
+ perp(verticalTitleBar, floatSize));
+
+ QFontMetrics titleFontMetrics = q->fontMetrics();
+#ifdef Q_WS_MAC
+ if (qobject_cast<QMacStyle *>(q->style())) {
+ //### this breaks on proxy styles. (But is this code still called?)
+ QFont font = qt_app_fonts_hash()->value("QToolButton", q->font());
+ titleFontMetrics = QFontMetrics(font);
+ }
+#endif
+
+ int mw = q->style()->pixelMetric(QStyle::PM_DockWidgetTitleMargin, 0, q);
+
+ return qMax(buttonHeight + 2, titleFontMetrics.lineSpacing() + 2*mw);
+}
+
+void QDockWidgetLayout::setGeometry(const QRect &geometry)
+{
+ QDockWidget *q = qobject_cast<QDockWidget*>(parentWidget());
+
+ bool nativeDeco = nativeWindowDeco();
+
+ int fw = q->isFloating() && !nativeDeco
+ ? q->style()->pixelMetric(QStyle::PM_DockWidgetFrameWidth, 0, q)
+ : 0;
+
+ if (nativeDeco) {
+ if (QLayoutItem *item = item_list[Content])
+ item->setGeometry(geometry);
+ } else {
+ int titleHeight = this->titleHeight();
+
+ if (verticalTitleBar) {
+ _titleArea = QRect(QPoint(fw, fw),
+ QSize(titleHeight, geometry.height() - (fw * 2)));
+ } else {
+ _titleArea = QRect(QPoint(fw, fw),
+ QSize(geometry.width() - (fw * 2), titleHeight));
+ }
+
+ if (QLayoutItem *item = item_list[TitleBar]) {
+ item->setGeometry(_titleArea);
+ } else {
+ QStyleOptionDockWidgetV2 opt;
+ q->initStyleOption(&opt);
+
+ if (QLayoutItem *item = item_list[CloseButton]) {
+ if (!item->isEmpty()) {
+ QRect r = q->style()
+ ->subElementRect(QStyle::SE_DockWidgetCloseButton,
+ &opt, q);
+ if (!r.isNull())
+ item->setGeometry(r);
+ }
+ }
+
+ if (QLayoutItem *item = item_list[FloatButton]) {
+ if (!item->isEmpty()) {
+ QRect r = q->style()
+ ->subElementRect(QStyle::SE_DockWidgetFloatButton,
+ &opt, q);
+ if (!r.isNull())
+ item->setGeometry(r);
+ }
+ }
+ }
+
+ if (QLayoutItem *item = item_list[Content]) {
+ QRect r = geometry;
+ if (verticalTitleBar) {
+ r.setLeft(_titleArea.right() + 1);
+ r.adjust(0, fw, -fw, -fw);
+ } else {
+ r.setTop(_titleArea.bottom() + 1);
+ r.adjust(fw, 0, -fw, -fw);
+ }
+ item->setGeometry(r);
+ }
+ }
+}
+
+void QDockWidgetLayout::setVerticalTitleBar(bool b)
+{
+ if (b == verticalTitleBar)
+ return;
+ verticalTitleBar = b;
+ invalidate();
+ parentWidget()->update();
+}
+
+/******************************************************************************
+** QDockWidgetItem
+*/
+
+QDockWidgetItem::QDockWidgetItem(QDockWidget *dockWidget)
+ : QWidgetItem(dockWidget)
+{
+}
+
+QSize QDockWidgetItem::minimumSize() const
+{
+ QSize widgetMin(0, 0);
+ if (QLayoutItem *item = dockWidgetChildItem())
+ widgetMin = item->minimumSize();
+ return dockWidgetLayout()->sizeFromContent(widgetMin, false);
+}
+
+QSize QDockWidgetItem::maximumSize() const
+{
+ if (QLayoutItem *item = dockWidgetChildItem()) {
+ return dockWidgetLayout()->sizeFromContent(item->maximumSize(), false);
+ } else {
+ return QSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX);
+ }
+}
+
+
+QSize QDockWidgetItem::sizeHint() const
+{
+ if (QLayoutItem *item = dockWidgetChildItem()) {
+ return dockWidgetLayout()->sizeFromContent(item->sizeHint(), false);
+ } else {
+ return QWidgetItem::sizeHint();
+ }
+}
+
+/******************************************************************************
+** QDockWidgetPrivate
+*/
+
+void QDockWidgetPrivate::init()
+{
+ Q_Q(QDockWidget);
+
+ QDockWidgetLayout *layout = new QDockWidgetLayout(q);
+ layout->setSizeConstraint(QLayout::SetMinAndMaxSize);
+
+ QAbstractButton *button = new QDockWidgetTitleButton(q);
+ button->setObjectName(QLatin1String("qt_dockwidget_floatbutton"));
+ QObject::connect(button, SIGNAL(clicked()), q, SLOT(_q_toggleTopLevel()));
+ layout->setWidgetForRole(QDockWidgetLayout::FloatButton, button);
+
+ button = new QDockWidgetTitleButton(q);
+ button->setObjectName(QLatin1String("qt_dockwidget_closebutton"));
+ QObject::connect(button, SIGNAL(clicked()), q, SLOT(close()));
+ layout->setWidgetForRole(QDockWidgetLayout::CloseButton, button);
+
+ resizer = new QWidgetResizeHandler(q);
+ resizer->setMovingEnabled(false);
+ resizer->setActive(false);
+
+#ifndef QT_NO_ACTION
+ toggleViewAction = new QAction(q);
+ toggleViewAction->setCheckable(true);
+ fixedWindowTitle = qt_setWindowTitle_helperHelper(q->windowTitle(), q);
+ toggleViewAction->setText(fixedWindowTitle);
+ QObject::connect(toggleViewAction, SIGNAL(triggered(bool)),
+ q, SLOT(_q_toggleView(bool)));
+#endif
+
+ updateButtons();
+}
+
+/*!
+ Initialize \a option with the values from this QDockWidget. This method
+ is useful for subclasses when they need a QStyleOptionDockWidget, but don't want
+ to fill in all the information themselves.
+
+ \sa QStyleOption::initFrom()
+*/
+void QDockWidget::initStyleOption(QStyleOptionDockWidget *option) const
+{
+ Q_D(const QDockWidget);
+
+ if (!option)
+ return;
+ QDockWidgetLayout *dwlayout = qobject_cast<QDockWidgetLayout*>(layout());
+
+ option->initFrom(this);
+ option->rect = dwlayout->titleArea();
+ option->title = d->fixedWindowTitle;
+ option->closable = hasFeature(this, QDockWidget::DockWidgetClosable);
+ option->movable = hasFeature(this, QDockWidget::DockWidgetMovable);
+ option->floatable = hasFeature(this, QDockWidget::DockWidgetFloatable);
+
+ QDockWidgetLayout *l = qobject_cast<QDockWidgetLayout*>(layout());
+ QStyleOptionDockWidgetV2 *v2
+ = qstyleoption_cast<QStyleOptionDockWidgetV2*>(option);
+ if (v2 != 0)
+ v2->verticalTitleBar = l->verticalTitleBar;
+}
+
+void QDockWidgetPrivate::_q_toggleView(bool b)
+{
+ Q_Q(QDockWidget);
+ if (b == q->isHidden()) {
+ if (b)
+ q->show();
+ else
+ q->close();
+ }
+}
+
+void QDockWidgetPrivate::updateButtons()
+{
+ Q_Q(QDockWidget);
+ QDockWidgetLayout *layout = qobject_cast<QDockWidgetLayout*>(q->layout());
+
+ QStyleOptionDockWidget opt;
+ q->initStyleOption(&opt);
+
+ bool customTitleBar = layout->widgetForRole(QDockWidgetLayout::TitleBar) != 0;
+ bool nativeDeco = layout->nativeWindowDeco();
+ bool hideButtons = nativeDeco || customTitleBar;
+
+ bool canClose = hasFeature(q, QDockWidget::DockWidgetClosable);
+ bool canFloat = hasFeature(q, QDockWidget::DockWidgetFloatable);
+
+ QAbstractButton *button
+ = qobject_cast<QAbstractButton*>(layout->widgetForRole(QDockWidgetLayout::FloatButton));
+ button->setIcon(q->style()->standardIcon(QStyle::SP_TitleBarNormalButton, &opt, q));
+ button->setVisible(canFloat && !hideButtons);
+
+ button
+ = qobject_cast <QAbstractButton*>(layout->widgetForRole(QDockWidgetLayout::CloseButton));
+ button->setIcon(q->style()->standardIcon(QStyle::SP_TitleBarCloseButton, &opt, q));
+ button->setVisible(canClose && !hideButtons);
+
+ q->setAttribute(Qt::WA_ContentsPropagated,
+ (canFloat || canClose) && !hideButtons);
+
+ layout->invalidate();
+}
+
+void QDockWidgetPrivate::_q_toggleTopLevel()
+{
+ Q_Q(QDockWidget);
+ q->setFloating(!q->isFloating());
+}
+
+void QDockWidgetPrivate::initDrag(const QPoint &pos, bool nca)
+{
+ Q_Q(QDockWidget);
+
+ if (state != 0)
+ return;
+
+ QMainWindow *win = qobject_cast<QMainWindow*>(q->parentWidget());
+ Q_ASSERT(win != 0);
+ QMainWindowLayout *layout = qobject_cast<QMainWindowLayout*>(win->layout());
+ Q_ASSERT(layout != 0);
+ if (layout->layoutState.indexOf(q).isEmpty()) //The dock widget has not been added into the main window
+ return;
+ if (layout->pluggingWidget != 0) // the main window is animating a docking operation
+ return;
+
+ state = new QDockWidgetPrivate::DragState;
+ state->pressPos = pos;
+ state->dragging = false;
+ state->widgetItem = 0;
+ state->ownWidgetItem = false;
+ state->nca = nca;
+ state->ctrlDrag = false;
+}
+
+void QDockWidgetPrivate::startDrag()
+{
+ Q_Q(QDockWidget);
+
+ if (state == 0 || state->dragging)
+ return;
+
+ QMainWindowLayout *layout
+ = qobject_cast<QMainWindowLayout *>(q->parentWidget()->layout());
+ Q_ASSERT(layout != 0);
+
+ state->widgetItem = layout->unplug(q);
+ if (state->widgetItem == 0) {
+ /* I have a QMainWindow parent, but I was never inserted with
+ QMainWindow::addDockWidget, so the QMainWindowLayout has no
+ widget item for me. :( I have to create it myself, and then
+ delete it if I don't get dropped into a dock area. */
+ state->widgetItem = new QDockWidgetItem(q);
+ state->ownWidgetItem = true;
+ }
+
+ if (state->ctrlDrag)
+ layout->restore();
+
+ state->dragging = true;
+}
+
+void QDockWidgetPrivate::endDrag(bool abort)
+{
+ Q_Q(QDockWidget);
+ Q_ASSERT(state != 0);
+
+ q->releaseMouse();
+
+ if (state->dragging) {
+ QMainWindowLayout *layout =
+ qobject_cast<QMainWindowLayout *>(q->parentWidget()->layout());
+ Q_ASSERT(layout != 0);
+
+ if (abort || !layout->plug(state->widgetItem)) {
+ if (hasFeature(q, QDockWidget::DockWidgetFloatable)) {
+ if (state->ownWidgetItem)
+ delete state->widgetItem;
+ layout->restore();
+#ifdef Q_WS_X11
+ // get rid of the X11BypassWindowManager window flag and activate the resizer
+ Qt::WindowFlags flags = q->windowFlags();
+ flags &= ~Qt::X11BypassWindowManagerHint;
+ q->setWindowFlags(flags);
+ resizer->setActive(QWidgetResizeHandler::Resize, true);
+ q->show();
+#else
+ QDockWidgetLayout *myLayout
+ = qobject_cast<QDockWidgetLayout*>(q->layout());
+ resizer->setActive(QWidgetResizeHandler::Resize,
+ myLayout->widgetForRole(QDockWidgetLayout::TitleBar) != 0);
+#endif
+ undockedGeometry = q->geometry();
+ q->activateWindow();
+ } else {
+ layout->revert(state->widgetItem);
+ }
+ }
+ }
+ delete state;
+ state = 0;
+}
+
+bool QDockWidgetPrivate::isAnimating() const
+{
+ Q_Q(const QDockWidget);
+
+ QMainWindow *mainWin = qobject_cast<QMainWindow*>(q->parentWidget());
+ if (mainWin == 0)
+ return false;
+
+ QMainWindowLayout *mainWinLayout
+ = qobject_cast<QMainWindowLayout*>(mainWin->layout());
+ if (mainWinLayout == 0)
+ return false;
+
+ return (void*)mainWinLayout->pluggingWidget == (void*)q;
+}
+
+bool QDockWidgetPrivate::mousePressEvent(QMouseEvent *event)
+{
+#if !defined(QT_NO_MAINWINDOW)
+ Q_Q(QDockWidget);
+
+ QDockWidgetLayout *layout
+ = qobject_cast<QDockWidgetLayout*>(q->layout());
+
+ if (!layout->nativeWindowDeco()) {
+ QRect titleArea = layout->titleArea();
+
+ if (event->button() != Qt::LeftButton ||
+ !titleArea.contains(event->pos()) ||
+ // check if the tool window is movable... do nothing if it
+ // is not (but allow moving if the window is floating)
+ (!hasFeature(q, QDockWidget::DockWidgetMovable) && !q->isFloating()) ||
+ qobject_cast<QMainWindow*>(q->parentWidget()) == 0 ||
+ isAnimating() || state != 0) {
+ return false;
+ }
+
+ initDrag(event->pos(), false);
+
+ if (state)
+ state->ctrlDrag = hasFeature(q, QDockWidget::DockWidgetFloatable) && event->modifiers() & Qt::ControlModifier;
+
+ return true;
+ }
+
+#endif // !defined(QT_NO_MAINWINDOW)
+ return false;
+}
+
+bool QDockWidgetPrivate::mouseDoubleClickEvent(QMouseEvent *event)
+{
+ Q_Q(QDockWidget);
+
+ QDockWidgetLayout *layout = qobject_cast<QDockWidgetLayout*>(q->layout());
+
+ if (!layout->nativeWindowDeco()) {
+ QRect titleArea = layout->titleArea();
+
+ if (event->button() == Qt::LeftButton && titleArea.contains(event->pos()) &&
+ hasFeature(q, QDockWidget::DockWidgetFloatable)) {
+ _q_toggleTopLevel();
+ return true;
+ }
+ }
+ return false;
+}
+
+bool QDockWidgetPrivate::mouseMoveEvent(QMouseEvent *event)
+{
+ bool ret = false;
+#if !defined(QT_NO_MAINWINDOW)
+ Q_Q(QDockWidget);
+
+ if (!state)
+ return ret;
+
+ QDockWidgetLayout *dwlayout
+ = qobject_cast<QDockWidgetLayout*>(q->layout());
+ QMainWindowLayout *mwlayout
+ = qobject_cast<QMainWindowLayout*>(q->parentWidget()->layout());
+ if (!dwlayout->nativeWindowDeco()) {
+ if (!state->dragging
+ && mwlayout->pluggingWidget == 0
+ && (event->pos() - state->pressPos).manhattanLength()
+ > QApplication::startDragDistance()) {
+ startDrag();
+#ifdef Q_OS_WIN
+ grabMouseWhileInWindow();
+#else
+ q->grabMouse();
+#endif
+ ret = true;
+ }
+ }
+
+ if (state->dragging && !state->nca) {
+ QPoint pos = event->globalPos() - state->pressPos;
+ q->move(pos);
+
+ if (!state->ctrlDrag)
+ mwlayout->hover(state->widgetItem, event->globalPos());
+
+ ret = true;
+ }
+
+#endif // !defined(QT_NO_MAINWINDOW)
+ return ret;
+}
+
+bool QDockWidgetPrivate::mouseReleaseEvent(QMouseEvent *event)
+{
+#if !defined(QT_NO_MAINWINDOW)
+
+ if (event->button() == Qt::LeftButton && state && !state->nca) {
+ endDrag();
+ return true; //filter out the event
+ }
+
+#endif // !defined(QT_NO_MAINWINDOW)
+ return false;
+}
+
+void QDockWidgetPrivate::nonClientAreaMouseEvent(QMouseEvent *event)
+{
+ Q_Q(QDockWidget);
+
+ int fw = q->style()->pixelMetric(QStyle::PM_DockWidgetFrameWidth, 0, q);
+
+ QRect geo = q->geometry();
+ QRect titleRect = q->frameGeometry();
+#ifdef Q_WS_MAC
+ if ((features & QDockWidget::DockWidgetVerticalTitleBar)) {
+ titleRect.setTop(geo.top());
+ titleRect.setBottom(geo.bottom());
+ titleRect.setRight(geo.left() - 1);
+ } else
+#endif
+ {
+ titleRect.setLeft(geo.left());
+ titleRect.setRight(geo.right());
+ titleRect.setBottom(geo.top() - 1);
+ titleRect.adjust(0, fw, 0, 0);
+ }
+
+ switch (event->type()) {
+ case QEvent::NonClientAreaMouseButtonPress:
+ if (!titleRect.contains(event->globalPos()))
+ break;
+ if (state != 0)
+ break;
+ if (qobject_cast<QMainWindow*>(q->parentWidget()) == 0)
+ break;
+ if (isAnimating())
+ break;
+ initDrag(event->pos(), true);
+ if (state == 0)
+ break;
+#ifdef Q_OS_WIN
+ // On Windows, NCA mouse events don't contain modifier info
+ state->ctrlDrag = GetKeyState(VK_CONTROL) & 0x8000;
+#else
+ state->ctrlDrag = event->modifiers() & Qt::ControlModifier;
+#endif
+ startDrag();
+ break;
+ case QEvent::NonClientAreaMouseMove:
+ if (state == 0 || !state->dragging)
+ break;
+ if (state->nca) {
+ endDrag();
+ }
+#ifdef Q_OS_MAC
+ else { // workaround for lack of mouse-grab on Mac
+ QMainWindowLayout *layout
+ = qobject_cast<QMainWindowLayout *>(q->parentWidget()->layout());
+ Q_ASSERT(layout != 0);
+
+ q->move(event->globalPos() - state->pressPos);
+ if (!state->ctrlDrag)
+ layout->hover(state->widgetItem, event->globalPos());
+ }
+#endif
+ break;
+ case QEvent::NonClientAreaMouseButtonRelease:
+#ifdef Q_OS_MAC
+ if (state)
+ endDrag();
+#endif
+ break;
+ case QEvent::NonClientAreaMouseButtonDblClick:
+ _q_toggleTopLevel();
+ break;
+ default:
+ break;
+ }
+}
+
+void QDockWidgetPrivate::moveEvent(QMoveEvent *event)
+{
+ Q_Q(QDockWidget);
+
+ if (state == 0 || !state->dragging || !state->nca || !q->isWindow())
+ return;
+
+ // When the native window frame is being dragged, all we get is these mouse
+ // move events.
+
+ if (state->ctrlDrag)
+ return;
+
+ QMainWindowLayout *layout
+ = qobject_cast<QMainWindowLayout *>(q->parentWidget()->layout());
+ Q_ASSERT(layout != 0);
+
+ QPoint globalMousePos = event->pos() + state->pressPos;
+ layout->hover(state->widgetItem, globalMousePos);
+}
+
+void QDockWidgetPrivate::unplug(const QRect &rect)
+{
+ Q_Q(QDockWidget);
+ QRect r = rect;
+ r.moveTopLeft(q->mapToGlobal(QPoint(0, 0)));
+ QDockWidgetLayout *layout = qobject_cast<QDockWidgetLayout*>(q->layout());
+ if (layout->nativeWindowDeco(true))
+ r.adjust(0, layout->titleHeight(), 0, 0);
+ setWindowState(true, true, r);
+}
+
+void QDockWidgetPrivate::plug(const QRect &rect)
+{
+ setWindowState(false, false, rect);
+}
+
+void QDockWidgetPrivate::setWindowState(bool floating, bool unplug, const QRect &rect)
+{
+ Q_Q(QDockWidget);
+
+ bool wasFloating = q->isFloating();
+ bool hidden = q->isHidden();
+
+ if (q->isVisible())
+ q->hide();
+
+ Qt::WindowFlags flags = floating ? Qt::Tool : Qt::Widget;
+
+ QDockWidgetLayout *layout = qobject_cast<QDockWidgetLayout*>(q->layout());
+ const bool nativeDeco = layout->nativeWindowDeco(floating);
+
+ if (nativeDeco) {
+ flags |= Qt::CustomizeWindowHint | Qt::WindowTitleHint;
+ if (hasFeature(q, QDockWidget::DockWidgetClosable))
+ flags |= Qt::WindowCloseButtonHint;
+ } else {
+ flags |= Qt::FramelessWindowHint;
+ }
+
+ if (unplug)
+ flags |= Qt::X11BypassWindowManagerHint;
+
+ q->setWindowFlags(flags);
+
+#if defined(Q_WS_MAC) && !defined(QT_MAC_USE_COCOA)
+ if (floating && nativeDeco && (q->features() & QDockWidget::DockWidgetVerticalTitleBar)) {
+ ChangeWindowAttributes(HIViewGetWindow(HIViewRef(q->winId())), kWindowSideTitlebarAttribute, 0);
+ }
+#endif
+
+ if (!rect.isNull())
+ q->setGeometry(rect);
+
+ updateButtons();
+
+ if (!hidden)
+ q->show();
+
+ if (floating != wasFloating) {
+ emit q->topLevelChanged(floating);
+ if (!floating && q->parentWidget()) {
+ QMainWindowLayout *mwlayout = qobject_cast<QMainWindowLayout *>(q->parentWidget()->layout());
+ if (mwlayout)
+ emit q->dockLocationChanged(mwlayout->dockWidgetArea(q));
+ }
+ }
+
+ resizer->setActive(QWidgetResizeHandler::Resize, !unplug && floating && !nativeDeco);
+}
+
+/*!
+ \class QDockWidget
+
+ \brief The QDockWidget class provides a widget that can be docked
+ inside a QMainWindow or floated as a top-level window on the
+ desktop.
+
+ \ingroup application
+
+ QDockWidget provides the concept of dock widgets, also know as
+ tool palettes or utility windows. Dock windows are secondary
+ windows placed in the \e {dock widget area} around the
+ \l{QMainWindow::centralWidget()}{central widget} in a
+ QMainWindow.
+
+ \image mainwindow-docks.png
+
+ Dock windows can be moved inside their current area, moved into
+ new areas and floated (e.g., undocked) by the end-user. The
+ QDockWidget API allows the programmer to restrict the dock widgets
+ ability to move, float and close, as well as the areas in which
+ they can be placed.
+
+ \section1 Appearance
+
+ A QDockWidget consists of a title bar and the content area. The
+ title bar displays the dock widgets \link QWidget::windowTitle()
+ window title\endlink, a \e float button and a \e close button.
+ Depending on the state of the QDockWidget, the \e float and \e
+ close buttons may be either disabled or not shown at all.
+
+ The visual appearance of the title bar and buttons is dependent
+ on the \l{QStyle}{style} in use.
+
+ A QDockWidget acts as a wrapper for its child widget, set with setWidget().
+ Custom size hints, minimum and maximum sizes and size policies should be
+ implemented in the child widget. QDockWidget will respect them, adjusting
+ its own constraints to include the frame and title. Size constraints
+ should not be set on the QDockWidget itself, because they change depending
+ on whether it is docked; a docked QDockWidget has no frame and a smaller title
+ bar.
+
+ \sa QMainWindow, {Dock Widgets Example}
+*/
+
+/*!
+ \enum QDockWidget::DockWidgetFeature
+
+ \value DockWidgetClosable The dock widget can be closed. On some systems the dock
+ widget always has a close button when it's floating
+ (for example on MacOS 10.5).
+ \value DockWidgetMovable The dock widget can be moved between docks
+ by the user.
+ \value DockWidgetFloatable The dock widget can be detached from the
+ main window, and floated as an independent
+ window.
+ \value DockWidgetVerticalTitleBar The dock widget displays a vertical title
+ bar on its left side. This can be used to
+ increase the amount of vertical space in
+ a QMainWindow.
+ \value AllDockWidgetFeatures (Deprecated) The dock widget can be closed, moved,
+ and floated. Since new features might be added in future
+ releases, the look and behavior of dock widgets might
+ change if you use this flag. Please specify individual
+ flags instead.
+ \value NoDockWidgetFeatures The dock widget cannot be closed, moved,
+ or floated.
+
+ \omitvalue DockWidgetFeatureMask
+ \omitvalue Reserved
+*/
+
+/*!
+ \property QDockWidget::windowTitle
+ \internal
+
+ By default, this property contains an empty string.
+*/
+
+/*!
+ Constructs a QDockWidget with parent \a parent and window flags \a
+ flags. The dock widget will be placed in the left dock widget
+ area.
+*/
+QDockWidget::QDockWidget(QWidget *parent, Qt::WindowFlags flags)
+ : QWidget(*new QDockWidgetPrivate, parent, flags)
+{
+ Q_D(QDockWidget);
+ d->init();
+}
+
+/*!
+ Constructs a QDockWidget with parent \a parent and window flags \a
+ flags. The dock widget will be placed in the left dock widget
+ area.
+
+ The window title is set to \a title. This title is used when the
+ QDockWidget is docked and undocked. It is also used in the context
+ menu provided by QMainWindow.
+
+ \sa setWindowTitle()
+*/
+QDockWidget::QDockWidget(const QString &title, QWidget *parent, Qt::WindowFlags flags)
+ : QWidget(*new QDockWidgetPrivate, parent, flags)
+{
+ Q_D(QDockWidget);
+ d->init();
+ setWindowTitle(title);
+}
+
+/*!
+ Destroys the dock widget.
+*/
+QDockWidget::~QDockWidget()
+{ }
+
+/*!
+ Returns the widget for the dock widget. This function returns zero
+ if the widget has not been set.
+
+ \sa setWidget()
+*/
+QWidget *QDockWidget::widget() const
+{
+ QDockWidgetLayout *layout = qobject_cast<QDockWidgetLayout*>(this->layout());
+ return layout->widgetForRole(QDockWidgetLayout::Content);
+}
+
+/*!
+ Sets the widget for the dock widget to \a widget.
+
+ If the dock widget is visible when \a widget is added, you must
+ \l{QWidget::}{show()} it explicitly.
+
+ Note that you must add the layout of the \a widget before you call
+ this function; if not, the \a widget will not be visible.
+
+ \sa widget()
+*/
+void QDockWidget::setWidget(QWidget *widget)
+{
+ QDockWidgetLayout *layout = qobject_cast<QDockWidgetLayout*>(this->layout());
+ layout->setWidgetForRole(QDockWidgetLayout::Content, widget);
+}
+
+/*!
+ \property QDockWidget::features
+ \brief whether the dock widget is movable, closable, and floatable
+
+ By default, this property is set to a combination of DockWidgetClosable,
+ DockWidgetMovable and DockWidgetFloatable.
+
+ \sa DockWidgetFeature
+*/
+
+void QDockWidget::setFeatures(QDockWidget::DockWidgetFeatures features)
+{
+ Q_D(QDockWidget);
+ features &= DockWidgetFeatureMask;
+ if (d->features == features)
+ return;
+ d->features = features;
+ QDockWidgetLayout *layout
+ = qobject_cast<QDockWidgetLayout*>(this->layout());
+ layout->setVerticalTitleBar(features & DockWidgetVerticalTitleBar);
+ d->updateButtons();
+ d->toggleViewAction->setEnabled((d->features & DockWidgetClosable) == DockWidgetClosable);
+ emit featuresChanged(d->features);
+ update();
+}
+
+QDockWidget::DockWidgetFeatures QDockWidget::features() const
+{
+ Q_D(const QDockWidget);
+ return d->features;
+}
+
+/*!
+ \property QDockWidget::floating
+ \brief whether the dock widget is floating
+
+ A floating dock widget is presented to the user as an independent
+ window "on top" of its parent QMainWindow, instead of being
+ docked in the QMainWindow.
+
+ By default, this property is true.
+
+ \sa isWindow()
+*/
+void QDockWidget::setFloating(bool floating)
+{
+ Q_D(QDockWidget);
+
+ // the initial click of a double-click may have started a drag...
+ if (d->state != 0)
+ d->endDrag(true);
+
+ QRect r = d->undockedGeometry;
+
+ d->setWindowState(floating, false, floating ? r : QRect());
+ if (floating && r.isNull()) {
+ QDockWidgetLayout *layout = qobject_cast<QDockWidgetLayout*>(this->layout());
+ QRect titleArea = layout->titleArea();
+ int h = layout->verticalTitleBar ? titleArea.width() : titleArea.height();
+ QPoint p = mapToGlobal(QPoint(h, h));
+ move(p);
+ }
+}
+
+/*!
+ \property QDockWidget::allowedAreas
+ \brief areas where the dock widget may be placed
+
+ The default is Qt::AllDockWidgetAreas.
+
+ \sa Qt::DockWidgetArea
+*/
+
+void QDockWidget::setAllowedAreas(Qt::DockWidgetAreas areas)
+{
+ Q_D(QDockWidget);
+ areas &= Qt::DockWidgetArea_Mask;
+ if (areas == d->allowedAreas)
+ return;
+ d->allowedAreas = areas;
+ emit allowedAreasChanged(d->allowedAreas);
+}
+
+Qt::DockWidgetAreas QDockWidget::allowedAreas() const
+{
+ Q_D(const QDockWidget);
+ return d->allowedAreas;
+}
+
+/*!
+ \fn bool QDockWidget::isAreaAllowed(Qt::DockWidgetArea area) const
+
+ Returns true if this dock widget can be placed in the given \a area;
+ otherwise returns false.
+*/
+
+/*! \reimp */
+void QDockWidget::changeEvent(QEvent *event)
+{
+ Q_D(QDockWidget);
+ QDockWidgetLayout *layout = qobject_cast<QDockWidgetLayout*>(this->layout());
+
+ switch (event->type()) {
+ case QEvent::ModifiedChange:
+ case QEvent::WindowTitleChange:
+ update(layout->titleArea());
+#ifndef QT_NO_ACTION
+ d->fixedWindowTitle = qt_setWindowTitle_helperHelper(windowTitle(), this);
+ d->toggleViewAction->setText(d->fixedWindowTitle);
+#endif
+#ifndef QT_NO_TABBAR
+ {
+ QMainWindow *win = qobject_cast<QMainWindow*>(parentWidget());
+ if (QMainWindowLayout *winLayout =
+ (win ? qobject_cast<QMainWindowLayout*>(win->layout()) : 0))
+ if (QDockAreaLayoutInfo *info =
+ (winLayout ? winLayout->layoutState.dockAreaLayout.info(this) : 0))
+ info->updateTabBar();
+ }
+#endif // QT_NO_TABBAR
+ break;
+ default:
+ break;
+ }
+ QWidget::changeEvent(event);
+}
+
+/*! \reimp */
+void QDockWidget::closeEvent(QCloseEvent *event)
+{
+ Q_D(QDockWidget);
+ if (d->state)
+ d->endDrag(true);
+ QWidget::closeEvent(event);
+}
+
+/*! \reimp */
+void QDockWidget::paintEvent(QPaintEvent *event)
+{
+ Q_UNUSED(event)
+
+ QDockWidgetLayout *layout
+ = qobject_cast<QDockWidgetLayout*>(this->layout());
+ bool customTitleBar = layout->widgetForRole(QDockWidgetLayout::TitleBar) != 0;
+ bool nativeDeco = layout->nativeWindowDeco();
+
+ if (!nativeDeco && !customTitleBar) {
+ QStylePainter p(this);
+ // ### Add PixelMetric to change spacers, so style may show border
+ // when not floating.
+ if (isFloating()) {
+ QStyleOptionFrame framOpt;
+ framOpt.init(this);
+ p.drawPrimitive(QStyle::PE_FrameDockWidget, framOpt);
+ }
+
+ // Title must be painted after the frame, since the areas overlap, and
+ // the title may wish to extend out to all sides (eg. XP style)
+ QStyleOptionDockWidgetV2 titleOpt;
+ initStyleOption(&titleOpt);
+ p.drawControl(QStyle::CE_DockWidgetTitle, titleOpt);
+ }
+}
+
+/*! \reimp */
+bool QDockWidget::event(QEvent *event)
+{
+ Q_D(QDockWidget);
+
+ QMainWindow *win = qobject_cast<QMainWindow*>(parentWidget());
+ QMainWindowLayout *layout = 0;
+ if (win != 0)
+ layout = qobject_cast<QMainWindowLayout*>(win->layout());
+
+ switch (event->type()) {
+#ifndef QT_NO_ACTION
+ case QEvent::Hide:
+ if (layout != 0)
+ layout->keepSize(this);
+ d->toggleViewAction->setChecked(false);
+ emit visibilityChanged(false);
+ break;
+ case QEvent::Show:
+ d->toggleViewAction->setChecked(true);
+ emit visibilityChanged(true);
+ break;
+#endif
+ case QEvent::ApplicationLayoutDirectionChange:
+ case QEvent::LayoutDirectionChange:
+ case QEvent::StyleChange:
+ case QEvent::ParentChange:
+ d->updateButtons();
+ break;
+ case QEvent::ZOrderChange: {
+ bool onTop = false;
+ if (win != 0) {
+ const QObjectList &siblings = win->children();
+ onTop = siblings.count() > 0 && siblings.last() == (QObject*)this;
+ }
+ if (!isFloating() && layout != 0 && onTop)
+ layout->raise(this);
+ break;
+ }
+ case QEvent::WindowActivate:
+ case QEvent::WindowDeactivate:
+ update(qobject_cast<QDockWidgetLayout *>(this->layout())->titleArea());
+ break;
+ case QEvent::ContextMenu:
+ if (d->state) {
+ event->accept();
+ return true;
+ }
+ break;
+ // return true after calling the handler since we don't want
+ // them to be passed onto the default handlers
+ case QEvent::MouseButtonPress:
+ if (d->mousePressEvent(static_cast<QMouseEvent *>(event)))
+ return true;
+ break;
+ case QEvent::MouseButtonDblClick:
+ if (d->mouseDoubleClickEvent(static_cast<QMouseEvent *>(event)))
+ return true;
+ break;
+ case QEvent::MouseMove:
+ if (d->mouseMoveEvent(static_cast<QMouseEvent *>(event)))
+ return true;
+ break;
+#ifdef Q_OS_WIN
+ case QEvent::Leave:
+ if (d->state != 0 && d->state->dragging && !d->state->nca) {
+ // This is a workaround for loosing the mouse on Vista.
+ QPoint pos = QCursor::pos();
+ QMouseEvent fake(QEvent::MouseMove, mapFromGlobal(pos), pos, Qt::NoButton,
+ QApplication::mouseButtons(), QApplication::keyboardModifiers());
+ d->mouseMoveEvent(&fake);
+ }
+ break;
+#endif
+ case QEvent::MouseButtonRelease:
+ if (d->mouseReleaseEvent(static_cast<QMouseEvent *>(event)))
+ return true;
+ break;
+ case QEvent::NonClientAreaMouseMove:
+ case QEvent::NonClientAreaMouseButtonPress:
+ case QEvent::NonClientAreaMouseButtonRelease:
+ case QEvent::NonClientAreaMouseButtonDblClick:
+ d->nonClientAreaMouseEvent(static_cast<QMouseEvent*>(event));
+ return true;
+ case QEvent::Move:
+ d->moveEvent(static_cast<QMoveEvent*>(event));
+ break;
+ case QEvent::Resize:
+ // if the mainwindow is plugging us, we don't want to update undocked geometry
+ if (isFloating() && layout != 0 && layout->pluggingWidget != this)
+ d->undockedGeometry = geometry();
+ break;
+ default:
+ break;
+ }
+ return QWidget::event(event);
+}
+
+#ifndef QT_NO_ACTION
+/*!
+ Returns a checkable action that can be used to show or close this
+ dock widget.
+
+ The action's text is set to the dock widget's window title.
+
+ \sa QAction::text QWidget::windowTitle
+ */
+QAction * QDockWidget::toggleViewAction() const
+{
+ Q_D(const QDockWidget);
+ return d->toggleViewAction;
+}
+#endif // QT_NO_ACTION
+
+/*!
+ \fn void QDockWidget::featuresChanged(QDockWidget::DockWidgetFeatures features)
+
+ This signal is emitted when the \l features property changes. The
+ \a features parameter gives the new value of the property.
+*/
+
+/*!
+ \fn void QDockWidget::topLevelChanged(bool topLevel)
+
+ This signal is emitted when the \l floating property changes.
+ The \a topLevel parameter is true if the dock widget is now floating;
+ otherwise it is false.
+
+ \sa isWindow()
+*/
+
+/*!
+ \fn void QDockWidget::allowedAreasChanged(Qt::DockWidgetAreas allowedAreas)
+
+ This signal is emitted when the \l allowedAreas property changes. The
+ \a allowedAreas parameter gives the new value of the property.
+*/
+
+/*!
+ \fn void QDockWidget::visibilityChanged(bool visible)
+ \since 4.3
+
+ This signal is emitted when the dock widget becomes \a visible (or
+ invisible). This happens when the widget is hidden or shown, as
+ well as when it is docked in a tabbed dock area and its tab
+ becomes selected or unselected.
+*/
+
+/*!
+ \fn void QDockWidget::dockLocationChanged(Qt::DockWidgetArea area)
+ \since 4.3
+
+ This signal is emitted when the dock widget is moved to another
+ dock \a area, or is moved to a different location in its current
+ dock area. This happens when the dock widget is moved
+ programmatically or is dragged to a new location by the user.
+*/
+
+/*!
+ \since 4.3
+ Sets an arbitrary \a widget as the dock widget's title bar. If \a widget
+ is 0, the title bar widget is removed, but not deleted.
+
+ If a title bar widget is set, QDockWidget will not use native window
+ decorations when it is floated.
+
+ Here are some tips for implementing custom title bars:
+
+ \list
+ \i Mouse events that are not explicitly handled by the title bar widget
+ must be ignored by calling QMouseEvent::ignore(). These events then
+ propagate to the QDockWidget parent, which handles them in the usual
+ manner, moving when the title bar is dragged, docking and undocking
+ when it is double-clicked, etc.
+
+ \i When DockWidgetVerticalTitleBar is set on QDockWidget, the title
+ bar widget is repositioned accordingly. In resizeEvent(), the title
+ bar should check what orientation it should assume:
+ \snippet doc/src/snippets/code/src_gui_widgets_qdockwidget.cpp 0
+
+ \i The title bar widget must have a valid QWidget::sizeHint() and
+ QWidget::minimumSizeHint(). These functions should take into account
+ the current orientation of the title bar.
+ \endlist
+
+ Using qobject_cast as shown above, the title bar widget has full access
+ to its parent QDockWidget. Hence it can perform such operations as docking
+ and hiding in response to user actions.
+
+ \sa titleBarWidget() DockWidgetVerticalTitleBar
+*/
+
+void QDockWidget::setTitleBarWidget(QWidget *widget)
+{
+ Q_D(QDockWidget);
+ QDockWidgetLayout *layout
+ = qobject_cast<QDockWidgetLayout*>(this->layout());
+ layout->setWidgetForRole(QDockWidgetLayout::TitleBar, widget);
+ d->updateButtons();
+ if (isWindow()) {
+ //this ensures the native decoration is drawn
+ d->setWindowState(true /*floating*/, true /*unplug*/);
+ }
+}
+
+/*!
+ \since 4.3
+ Returns the custom title bar widget set on the QDockWidget, or 0 if no
+ custom title bar has been set.
+
+ \sa setTitleBarWidget()
+*/
+
+QWidget *QDockWidget::titleBarWidget() const
+{
+ QDockWidgetLayout *layout
+ = qobject_cast<QDockWidgetLayout*>(this->layout());
+ return layout->widgetForRole(QDockWidgetLayout::TitleBar);
+}
+
+QT_END_NAMESPACE
+
+#include "qdockwidget.moc"
+#include "moc_qdockwidget.cpp"
+
+#endif // QT_NO_DOCKWIDGET
diff --git a/src/gui/widgets/qdockwidget.h b/src/gui/widgets/qdockwidget.h
new file mode 100644
index 0000000000..938a2fd504
--- /dev/null
+++ b/src/gui/widgets/qdockwidget.h
@@ -0,0 +1,146 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QDYNAMICDOCKWIDGET_H
+#define QDYNAMICDOCKWIDGET_H
+
+#include <QtGui/qwidget.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+#ifndef QT_NO_DOCKWIDGET
+
+class QDockAreaLayout;
+class QDockWidgetPrivate;
+class QMainWindow;
+class QStyleOptionDockWidget;
+
+class Q_GUI_EXPORT QDockWidget : public QWidget
+{
+ Q_OBJECT
+
+ Q_FLAGS(DockWidgetFeatures)
+ Q_PROPERTY(bool floating READ isFloating WRITE setFloating)
+ Q_PROPERTY(DockWidgetFeatures features READ features WRITE setFeatures NOTIFY featuresChanged)
+ Q_PROPERTY(Qt::DockWidgetAreas allowedAreas READ allowedAreas
+ WRITE setAllowedAreas NOTIFY allowedAreasChanged)
+ Q_PROPERTY(QString windowTitle READ windowTitle WRITE setWindowTitle DESIGNABLE true)
+
+public:
+ explicit QDockWidget(const QString &title, QWidget *parent = 0, Qt::WindowFlags flags = 0);
+ explicit QDockWidget(QWidget *parent = 0, Qt::WindowFlags flags = 0);
+ ~QDockWidget();
+
+ QWidget *widget() const;
+ void setWidget(QWidget *widget);
+
+ enum DockWidgetFeature {
+ DockWidgetClosable = 0x01,
+ DockWidgetMovable = 0x02,
+ DockWidgetFloatable = 0x04,
+ DockWidgetVerticalTitleBar = 0x08,
+
+ DockWidgetFeatureMask = 0x0f,
+ AllDockWidgetFeatures = DockWidgetClosable|DockWidgetMovable|DockWidgetFloatable, // ### remove in 5.0
+ NoDockWidgetFeatures = 0x00,
+
+ Reserved = 0xff
+ };
+ Q_DECLARE_FLAGS(DockWidgetFeatures, DockWidgetFeature)
+
+ void setFeatures(DockWidgetFeatures features);
+ DockWidgetFeatures features() const;
+
+ void setFloating(bool floating);
+ inline bool isFloating() const { return isWindow(); }
+
+ void setAllowedAreas(Qt::DockWidgetAreas areas);
+ Qt::DockWidgetAreas allowedAreas() const;
+
+ void setTitleBarWidget(QWidget *widget);
+ QWidget *titleBarWidget() const;
+
+ inline bool isAreaAllowed(Qt::DockWidgetArea area) const
+ { return (allowedAreas() & area) == area; }
+
+#ifndef QT_NO_ACTION
+ QAction *toggleViewAction() const;
+#endif
+
+Q_SIGNALS:
+ void featuresChanged(QDockWidget::DockWidgetFeatures features);
+ void topLevelChanged(bool topLevel);
+ void allowedAreasChanged(Qt::DockWidgetAreas allowedAreas);
+ void visibilityChanged(bool visible);
+ void dockLocationChanged(Qt::DockWidgetArea area);
+
+protected:
+ void changeEvent(QEvent *event);
+ void closeEvent(QCloseEvent *event);
+ void paintEvent(QPaintEvent *event);
+ bool event(QEvent *event);
+ void initStyleOption(QStyleOptionDockWidget *option) const;
+
+private:
+ Q_DECLARE_PRIVATE(QDockWidget)
+ Q_DISABLE_COPY(QDockWidget)
+ Q_PRIVATE_SLOT(d_func(), void _q_toggleView(bool))
+ Q_PRIVATE_SLOT(d_func(), void _q_toggleTopLevel())
+ friend class QDockAreaLayout;
+ friend class QDockWidgetItem;
+ friend class QMainWindowLayout;
+ friend class QDockWidgetLayout;
+ friend class QDockAreaLayoutInfo;
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(QDockWidget::DockWidgetFeatures)
+
+#endif // QT_NO_DOCKWIDGET
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QDYNAMICDOCKWIDGET_H
diff --git a/src/gui/widgets/qdockwidget_p.h b/src/gui/widgets/qdockwidget_p.h
new file mode 100644
index 0000000000..0bc619c847
--- /dev/null
+++ b/src/gui/widgets/qdockwidget_p.h
@@ -0,0 +1,207 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QDYNAMICDOCKWIDGET_P_H
+#define QDYNAMICDOCKWIDGET_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 "QtGui/qstyleoption.h"
+#include "private/qwidget_p.h"
+#include "QtGui/qboxlayout.h"
+#include "QtGui/qdockwidget.h"
+
+#ifndef QT_NO_DOCKWIDGET
+
+QT_BEGIN_NAMESPACE
+
+class QGridLayout;
+class QWidgetResizeHandler;
+class QRubberBand;
+class QDockWidgetTitleButton;
+class QSpacerItem;
+class QDockWidgetItem;
+
+class QDockWidgetPrivate : public QWidgetPrivate
+{
+ Q_DECLARE_PUBLIC(QDockWidget)
+
+ struct DragState {
+ QPoint pressPos;
+ bool dragging;
+ QLayoutItem *widgetItem;
+ bool ownWidgetItem;
+ bool nca;
+ bool ctrlDrag;
+ };
+
+public:
+ inline QDockWidgetPrivate()
+ : QWidgetPrivate(), state(0),
+ features(QDockWidget::DockWidgetClosable
+ | QDockWidget::DockWidgetMovable
+ | QDockWidget::DockWidgetFloatable),
+ allowedAreas(Qt::AllDockWidgetAreas)
+ { }
+
+ void init();
+ void _q_toggleView(bool); // private slot
+ void _q_toggleTopLevel(); // private slot
+
+ void updateButtons();
+ DragState *state;
+
+ QDockWidget::DockWidgetFeatures features;
+ Qt::DockWidgetAreas allowedAreas;
+
+ QWidgetResizeHandler *resizer;
+
+#ifndef QT_NO_ACTION
+ QAction *toggleViewAction;
+#endif
+
+// QMainWindow *findMainWindow(QWidget *widget) const;
+ QRect undockedGeometry;
+ QString fixedWindowTitle;
+
+ bool mousePressEvent(QMouseEvent *event);
+ bool mouseDoubleClickEvent(QMouseEvent *event);
+ bool mouseMoveEvent(QMouseEvent *event);
+ bool mouseReleaseEvent(QMouseEvent *event);
+ void setWindowState(bool floating, bool unplug = false, const QRect &rect = QRect());
+ void nonClientAreaMouseEvent(QMouseEvent *event);
+ void initDrag(const QPoint &pos, bool nca);
+ void startDrag();
+ void endDrag(bool abort = false);
+ void moveEvent(QMoveEvent *event);
+
+ void unplug(const QRect &rect);
+ void plug(const QRect &rect);
+
+ bool isAnimating() const;
+};
+
+class Q_GUI_EXPORT QDockWidgetLayout : public QLayout
+{
+ Q_OBJECT
+public:
+ QDockWidgetLayout(QWidget *parent = 0);
+ ~QDockWidgetLayout();
+ void addItem(QLayoutItem *item);
+ QLayoutItem *itemAt(int index) const;
+ QLayoutItem *takeAt(int index);
+ int count() const;
+
+ QSize maximumSize() const;
+ QSize minimumSize() const;
+ QSize sizeHint() const;
+
+ QSize sizeFromContent(const QSize &content, bool floating) const;
+
+ void setGeometry(const QRect &r);
+
+ enum Role { Content, CloseButton, FloatButton, TitleBar, RoleCount };
+ QWidget *widgetForRole(Role r) const;
+ void setWidgetForRole(Role r, QWidget *w);
+ QLayoutItem *itemForRole(Role r) const;
+
+ QRect titleArea() const { return _titleArea; }
+
+ int minimumTitleWidth() const;
+ int titleHeight() const;
+ void updateMaxSize();
+ bool nativeWindowDeco() const;
+ bool nativeWindowDeco(bool floating) const;
+
+ void setVerticalTitleBar(bool b);
+
+ bool verticalTitleBar;
+
+private:
+ QVector<QLayoutItem*> item_list;
+ QRect _titleArea;
+};
+
+/* The size hints of a QDockWidget will depend on wether it is docked or not.
+ This layout item always returns the size hints as if the dock widget was docked. */
+
+class QDockWidgetItem : public QWidgetItem
+{
+public:
+ QDockWidgetItem(QDockWidget *dockWidget);
+ QSize minimumSize() const;
+ QSize maximumSize() const;
+ QSize sizeHint() const;
+
+private:
+ inline QLayoutItem *dockWidgetChildItem() const;
+ inline QDockWidgetLayout *dockWidgetLayout() const;
+};
+
+inline QLayoutItem *QDockWidgetItem::dockWidgetChildItem() const
+{
+ if (QDockWidgetLayout *layout = dockWidgetLayout())
+ return layout->itemForRole(QDockWidgetLayout::Content);
+ return 0;
+}
+
+inline QDockWidgetLayout *QDockWidgetItem::dockWidgetLayout() const
+{
+ QWidget *w = const_cast<QDockWidgetItem*>(this)->widget();
+ if (w != 0)
+ return qobject_cast<QDockWidgetLayout*>(w->layout());
+ return 0;
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_DOCKWIDGET
+
+#endif // QDYNAMICDOCKWIDGET_P_H
diff --git a/src/gui/widgets/qeffects.cpp b/src/gui/widgets/qeffects.cpp
new file mode 100644
index 0000000000..140953dbe1
--- /dev/null
+++ b/src/gui/widgets/qeffects.cpp
@@ -0,0 +1,632 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qapplication.h"
+#ifndef QT_NO_EFFECTS
+#include "qdatetime.h"
+#include "qdesktopwidget.h"
+#include "qeffects_p.h"
+#include "qevent.h"
+#include "qimage.h"
+#include "qpainter.h"
+#include "qpixmap.h"
+#include "qpointer.h"
+#include "qtimer.h"
+#include "qdebug.h"
+
+QT_BEGIN_NAMESPACE
+
+/*
+ Internal class to get access to protected QWidget-members
+*/
+
+class QAccessWidget : public QWidget
+{
+ friend class QAlphaWidget;
+ friend class QRollEffect;
+public:
+ QAccessWidget(QWidget* parent=0, Qt::WindowFlags f = 0)
+ : QWidget(parent, f) {}
+};
+
+/*
+ Internal class QAlphaWidget.
+
+ The QAlphaWidget is shown while the animation lasts
+ and displays the pixmap resulting from the alpha blending.
+*/
+
+class QAlphaWidget: public QWidget, private QEffects
+{
+ Q_OBJECT
+public:
+ QAlphaWidget(QWidget* w, Qt::WindowFlags f = 0);
+ ~QAlphaWidget();
+
+ void run(int time);
+
+protected:
+ void paintEvent(QPaintEvent* e);
+ void closeEvent(QCloseEvent*);
+ void alphaBlend();
+ bool eventFilter(QObject *, QEvent *);
+
+protected slots:
+ void render();
+
+private:
+ QPixmap pm;
+ double alpha;
+ QImage backImage;
+ QImage frontImage;
+ QImage mixedImage;
+ QPointer<QAccessWidget> widget;
+ int duration;
+ int elapsed;
+ bool showWidget;
+ QTimer anim;
+ QTime checkTime;
+ double windowOpacity;
+};
+
+static QAlphaWidget* q_blend = 0;
+
+/*
+ Constructs a QAlphaWidget.
+*/
+QAlphaWidget::QAlphaWidget(QWidget* w, Qt::WindowFlags f)
+ : QWidget(QApplication::desktop()->screen(QApplication::desktop()->screenNumber(w)), f)
+{
+#ifndef Q_WS_WIN
+ setEnabled(false);
+#endif
+ setAttribute(Qt::WA_NoSystemBackground, true);
+ widget = (QAccessWidget*)w;
+ windowOpacity = w->windowOpacity();
+ alpha = 0;
+}
+
+QAlphaWidget::~QAlphaWidget()
+{
+#ifdef Q_WS_WIN
+ // Restore user-defined opacity value
+ if (widget && QSysInfo::WindowsVersion >= QSysInfo::WV_2000 && QSysInfo::WindowsVersion < QSysInfo::WV_NT_based)
+ widget->setWindowOpacity(windowOpacity);
+#endif
+}
+
+/*
+ \reimp
+*/
+void QAlphaWidget::paintEvent(QPaintEvent*)
+{
+ QPainter p(this);
+ p.drawPixmap(0, 0, pm);
+}
+
+/*
+ Starts the alphablending animation.
+ The animation will take about \a time ms
+*/
+void QAlphaWidget::run(int time)
+{
+ duration = time;
+
+ if (duration < 0)
+ duration = 150;
+
+ if (!widget)
+ return;
+
+ elapsed = 0;
+ checkTime.start();
+
+ showWidget = true;
+#if defined(Q_OS_WIN)
+ if (QSysInfo::WindowsVersion >= QSysInfo::WV_2000 && QSysInfo::WindowsVersion < QSysInfo::WV_NT_based) {
+ qApp->installEventFilter(this);
+ widget->setWindowOpacity(0.0);
+ widget->show();
+ connect(&anim, SIGNAL(timeout()), this, SLOT(render()));
+ anim.start(1);
+ } else
+#endif
+ {
+ //This is roughly equivalent to calling setVisible(true) without actually showing the widget
+ widget->setAttribute(Qt::WA_WState_ExplicitShowHide, true);
+ widget->setAttribute(Qt::WA_WState_Hidden, false);
+
+ qApp->installEventFilter(this);
+
+ move(widget->geometry().x(),widget->geometry().y());
+ resize(widget->size().width(), widget->size().height());
+
+ frontImage = QPixmap::grabWidget(widget).toImage();
+ backImage = QPixmap::grabWindow(QApplication::desktop()->winId(),
+ widget->geometry().x(), widget->geometry().y(),
+ widget->geometry().width(), widget->geometry().height()).toImage();
+
+ if (!backImage.isNull() && checkTime.elapsed() < duration / 2) {
+ mixedImage = backImage.copy();
+ pm = QPixmap::fromImage(mixedImage);
+ show();
+ setEnabled(false);
+
+ connect(&anim, SIGNAL(timeout()), this, SLOT(render()));
+ anim.start(1);
+ } else {
+ duration = 0;
+ render();
+ }
+ }
+}
+
+/*
+ \reimp
+*/
+bool QAlphaWidget::eventFilter(QObject *o, QEvent *e)
+{
+ switch (e->type()) {
+ case QEvent::Move:
+ if (o != widget)
+ break;
+ move(widget->geometry().x(),widget->geometry().y());
+ update();
+ break;
+ case QEvent::Hide:
+ case QEvent::Close:
+ if (o != widget)
+ break;
+ case QEvent::MouseButtonPress:
+ case QEvent::MouseButtonDblClick:
+ showWidget = false;
+ render();
+ break;
+ case QEvent::KeyPress: {
+ QKeyEvent *ke = (QKeyEvent*)e;
+ if (ke->key() == Qt::Key_Escape) {
+ showWidget = false;
+ } else {
+ duration = 0;
+ }
+ render();
+ break;
+ }
+ default:
+ break;
+ }
+ return QWidget::eventFilter(o, e);
+}
+
+/*
+ \reimp
+*/
+void QAlphaWidget::closeEvent(QCloseEvent *e)
+{
+ e->accept();
+ if (!q_blend)
+ return;
+
+ showWidget = false;
+ render();
+
+ QWidget::closeEvent(e);
+}
+
+/*
+ Render alphablending for the time elapsed.
+
+ Show the blended widget and free all allocated source
+ if the blending is finished.
+*/
+void QAlphaWidget::render()
+{
+ int tempel = checkTime.elapsed();
+ if (elapsed >= tempel)
+ elapsed++;
+ else
+ elapsed = tempel;
+
+ if (duration != 0)
+ alpha = tempel / double(duration);
+ else
+ alpha = 1;
+
+#if defined(Q_OS_WIN)
+ if (QSysInfo::WindowsVersion >= QSysInfo::WV_2000 && QSysInfo::WindowsVersion < QSysInfo::WV_NT_based) {
+ if (alpha >= windowOpacity || !showWidget) {
+ anim.stop();
+ qApp->removeEventFilter(this);
+ widget->setWindowOpacity(windowOpacity);
+ q_blend = 0;
+ deleteLater();
+ } else {
+ widget->setWindowOpacity(alpha);
+ }
+ } else
+#endif
+ if (alpha >= 1 || !showWidget) {
+ anim.stop();
+ qApp->removeEventFilter(this);
+
+ if (widget) {
+ if (!showWidget) {
+#ifdef Q_WS_WIN
+ setEnabled(true);
+ setFocus();
+#endif
+ widget->hide();
+ } else {
+ //Since we are faking the visibility of the widget
+ //we need to unset the hidden state on it before calling show
+ widget->setAttribute(Qt::WA_WState_Hidden, true);
+ widget->show();
+ lower();
+ }
+ }
+ q_blend = 0;
+ deleteLater();
+ } else {
+ alphaBlend();
+ pm = QPixmap::fromImage(mixedImage);
+ repaint();
+ }
+}
+
+/*
+ Calculate an alphablended image.
+*/
+void QAlphaWidget::alphaBlend()
+{
+ const int a = qRound(alpha*256);
+ const int ia = 256 - a;
+
+ const int sw = frontImage.width();
+ const int sh = frontImage.height();
+ const int bpl = frontImage.bytesPerLine();
+ switch(frontImage.depth()) {
+ case 32:
+ {
+ uchar *mixed_data = mixedImage.bits();
+ const uchar *back_data = backImage.bits();
+ const uchar *front_data = frontImage.bits();
+
+ for (int sy = 0; sy < sh; sy++) {
+ quint32* mixed = (quint32*)mixed_data;
+ const quint32* back = (const quint32*)back_data;
+ const quint32* front = (const quint32*)front_data;
+ for (int sx = 0; sx < sw; sx++) {
+ quint32 bp = back[sx];
+ quint32 fp = front[sx];
+
+ mixed[sx] = qRgb((qRed(bp)*ia + qRed(fp)*a)>>8,
+ (qGreen(bp)*ia + qGreen(fp)*a)>>8,
+ (qBlue(bp)*ia + qBlue(fp)*a)>>8);
+ }
+ mixed_data += bpl;
+ back_data += bpl;
+ front_data += bpl;
+ }
+ }
+ default:
+ break;
+ }
+}
+
+/*
+ Internal class QRollEffect
+
+ The QRollEffect widget is shown while the animation lasts
+ and displays a scrolling pixmap.
+*/
+
+class QRollEffect : public QWidget, private QEffects
+{
+ Q_OBJECT
+public:
+ QRollEffect(QWidget* w, Qt::WindowFlags f, DirFlags orient);
+
+ void run(int time);
+
+protected:
+ void paintEvent(QPaintEvent*);
+ void closeEvent(QCloseEvent*);
+
+private slots:
+ void scroll();
+
+private:
+ QPointer<QAccessWidget> widget;
+
+ int currentHeight;
+ int currentWidth;
+ int totalHeight;
+ int totalWidth;
+
+ int duration;
+ int elapsed;
+ bool done;
+ bool showWidget;
+ int orientation;
+
+ QTimer anim;
+ QTime checkTime;
+
+ QPixmap pm;
+};
+
+static QRollEffect* q_roll = 0;
+
+/*
+ Construct a QRollEffect widget.
+*/
+QRollEffect::QRollEffect(QWidget* w, Qt::WindowFlags f, DirFlags orient)
+ : QWidget(0, f), orientation(orient)
+{
+#ifndef Q_WS_WIN
+ setEnabled(false);
+#endif
+
+ widget = (QAccessWidget*) w;
+ Q_ASSERT(widget);
+
+ setAttribute(Qt::WA_NoSystemBackground, true);
+
+ if (widget->testAttribute(Qt::WA_Resized)) {
+ totalWidth = widget->width();
+ totalHeight = widget->height();
+ } else {
+ totalWidth = widget->sizeHint().width();
+ totalHeight = widget->sizeHint().height();
+ }
+
+ currentHeight = totalHeight;
+ currentWidth = totalWidth;
+
+ if (orientation & (RightScroll|LeftScroll))
+ currentWidth = 0;
+ if (orientation & (DownScroll|UpScroll))
+ currentHeight = 0;
+
+ pm = QPixmap::grabWidget(widget);
+}
+
+/*
+ \reimp
+*/
+void QRollEffect::paintEvent(QPaintEvent*)
+{
+ int x = orientation & RightScroll ? qMin(0, currentWidth - totalWidth) : 0;
+ int y = orientation & DownScroll ? qMin(0, currentHeight - totalHeight) : 0;
+
+ QPainter p(this);
+ p.drawPixmap(x, y, pm);
+}
+
+/*
+ \reimp
+*/
+void QRollEffect::closeEvent(QCloseEvent *e)
+{
+ e->accept();
+ if (done)
+ return;
+
+ showWidget = false;
+ done = true;
+ scroll();
+
+ QWidget::closeEvent(e);
+}
+
+/*
+ Start the animation.
+
+ The animation will take about \a time ms, or is
+ calculated if \a time is negative
+*/
+void QRollEffect::run(int time)
+{
+ if (!widget)
+ return;
+
+ duration = time;
+ elapsed = 0;
+
+ if (duration < 0) {
+ int dist = 0;
+ if (orientation & (RightScroll|LeftScroll))
+ dist += totalWidth - currentWidth;
+ if (orientation & (DownScroll|UpScroll))
+ dist += totalHeight - currentHeight;
+ duration = qMin(qMax(dist/3, 50), 120);
+ }
+
+ connect(&anim, SIGNAL(timeout()), this, SLOT(scroll()));
+
+ move(widget->geometry().x(),widget->geometry().y());
+ resize(qMin(currentWidth, totalWidth), qMin(currentHeight, totalHeight));
+
+ //This is roughly equivalent to calling setVisible(true) without actually showing the widget
+ widget->setAttribute(Qt::WA_WState_ExplicitShowHide, true);
+ widget->setAttribute(Qt::WA_WState_Hidden, false);
+
+ show();
+ setEnabled(false);
+
+ qApp->installEventFilter(this);
+
+ showWidget = true;
+ done = false;
+ anim.start(1);
+ checkTime.start();
+}
+
+/*
+ Roll according to the time elapsed.
+*/
+void QRollEffect::scroll()
+{
+ if (!done && widget) {
+ int tempel = checkTime.elapsed();
+ if (elapsed >= tempel)
+ elapsed++;
+ else
+ elapsed = tempel;
+
+ if (currentWidth != totalWidth) {
+ currentWidth = totalWidth * (elapsed/duration)
+ + (2 * totalWidth * (elapsed%duration) + duration)
+ / (2 * duration);
+ // equiv. to int((totalWidth*elapsed) / duration + 0.5)
+ done = (currentWidth >= totalWidth);
+ }
+ if (currentHeight != totalHeight) {
+ currentHeight = totalHeight * (elapsed/duration)
+ + (2 * totalHeight * (elapsed%duration) + duration)
+ / (2 * duration);
+ // equiv. to int((totalHeight*elapsed) / duration + 0.5)
+ done = (currentHeight >= totalHeight);
+ }
+ done = (currentHeight >= totalHeight) &&
+ (currentWidth >= totalWidth);
+
+ int w = totalWidth;
+ int h = totalHeight;
+ int x = widget->geometry().x();
+ int y = widget->geometry().y();
+
+ if (orientation & RightScroll || orientation & LeftScroll)
+ w = qMin(currentWidth, totalWidth);
+ if (orientation & DownScroll || orientation & UpScroll)
+ h = qMin(currentHeight, totalHeight);
+
+ setUpdatesEnabled(false);
+ if (orientation & UpScroll)
+ y = widget->geometry().y() + qMax(0, totalHeight - currentHeight);
+ if (orientation & LeftScroll)
+ x = widget->geometry().x() + qMax(0, totalWidth - currentWidth);
+ if (orientation & UpScroll || orientation & LeftScroll)
+ move(x, y);
+
+ resize(w, h);
+ setUpdatesEnabled(true);
+ repaint();
+ }
+ if (done) {
+ anim.stop();
+ qApp->removeEventFilter(this);
+ if (widget) {
+ if (!showWidget) {
+#ifdef Q_WS_WIN
+ setEnabled(true);
+ setFocus();
+#endif
+ widget->hide();
+ } else {
+ //Since we are faking the visibility of the widget
+ //we need to unset the hidden state on it before calling show
+ widget->setAttribute(Qt::WA_WState_Hidden, true);
+ widget->show();
+ lower();
+ }
+ }
+ q_roll = 0;
+ deleteLater();
+ }
+}
+
+/*!
+ Scroll widget \a w in \a time ms. \a orient may be 1 (vertical), 2
+ (horizontal) or 3 (diagonal).
+*/
+void qScrollEffect(QWidget* w, QEffects::DirFlags orient, int time)
+{
+ if (q_roll) {
+ q_roll->deleteLater();
+ q_roll = 0;
+ }
+
+ if (!w)
+ return;
+
+ qApp->sendPostedEvents(w, QEvent::Move);
+ qApp->sendPostedEvents(w, QEvent::Resize);
+ Qt::WindowFlags flags = Qt::ToolTip;
+
+ // those can be popups - they would steal the focus, but are disabled
+ q_roll = new QRollEffect(w, flags, orient);
+ q_roll->run(time);
+}
+
+/*!
+ Fade in widget \a w in \a time ms.
+*/
+void qFadeEffect(QWidget* w, int time)
+{
+ if (q_blend) {
+ q_blend->deleteLater();
+ q_blend = 0;
+ }
+
+ if (!w)
+ return;
+
+ qApp->sendPostedEvents(w, QEvent::Move);
+ qApp->sendPostedEvents(w, QEvent::Resize);
+
+ Qt::WindowFlags flags = Qt::ToolTip;
+
+ // those can be popups - they would steal the focus, but are disabled
+ q_blend = new QAlphaWidget(w, flags);
+
+ q_blend->run(time);
+}
+
+QT_END_NAMESPACE
+
+/*
+ Delete this after timeout
+*/
+
+#include "qeffects.moc"
+
+#endif //QT_NO_EFFECTS
diff --git a/src/gui/widgets/qeffects_p.h b/src/gui/widgets/qeffects_p.h
new file mode 100644
index 0000000000..edff5a9d0f
--- /dev/null
+++ b/src/gui/widgets/qeffects_p.h
@@ -0,0 +1,84 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QEFFECTS_P_H
+#define QEFFECTS_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of qeffects.cpp, qcombobox.cpp, qpopupmenu.cpp and qtooltip.cpp.
+// This header file may change from version to version without notice,
+// or even be removed.
+//
+// We mean it.
+//
+
+#include "QtCore/qnamespace.h"
+
+#ifndef QT_NO_EFFECTS
+
+QT_BEGIN_NAMESPACE
+
+class QWidget;
+
+struct QEffects
+{
+ enum Direction {
+ LeftScroll = 0x0001,
+ RightScroll = 0x0002,
+ UpScroll = 0x0004,
+ DownScroll = 0x0008
+ };
+
+ typedef uint DirFlags;
+};
+
+extern void Q_GUI_EXPORT qScrollEffect(QWidget*, QEffects::DirFlags dir = QEffects::DownScroll, int time = -1);
+extern void Q_GUI_EXPORT qFadeEffect(QWidget*, int time = -1);
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_EFFECTS
+
+#endif // QEFFECTS_P_H
diff --git a/src/gui/widgets/qfocusframe.cpp b/src/gui/widgets/qfocusframe.cpp
new file mode 100644
index 0000000000..4e5b630ddb
--- /dev/null
+++ b/src/gui/widgets/qfocusframe.cpp
@@ -0,0 +1,267 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qfocusframe.h"
+#include "qstyle.h"
+#include "qbitmap.h"
+#include "qstylepainter.h"
+#include "qstyleoption.h"
+#include "qdebug.h"
+#include <private/qwidget_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QFocusFramePrivate : public QWidgetPrivate
+{
+ Q_DECLARE_PUBLIC(QFocusFrame)
+ QWidget *widget;
+
+public:
+ QFocusFramePrivate() {
+ widget = 0;
+ sendChildEvents = false;
+ }
+ void updateSize();
+ void update();
+};
+
+void QFocusFramePrivate::update()
+{
+ Q_Q(QFocusFrame);
+ q->setParent(widget->parentWidget());
+ updateSize();
+ if (q->parentWidget()->rect().intersects(q->geometry())) {
+ if (q->style()->styleHint(QStyle::SH_FocusFrame_AboveWidget, 0, q))
+ q->raise();
+ else
+ q->stackUnder(widget);
+ q->show();
+ } else {
+ q->hide();
+ }
+}
+
+void QFocusFramePrivate::updateSize()
+{
+ Q_Q(QFocusFrame);
+ int vmargin = q->style()->pixelMetric(QStyle::PM_FocusFrameVMargin),
+ hmargin = q->style()->pixelMetric(QStyle::PM_FocusFrameHMargin);
+ QRect geom(widget->x()-hmargin, widget->y()-vmargin,
+ widget->width()+(hmargin*2), widget->height()+(vmargin*2));
+ if(q->geometry() == geom)
+ return;
+
+ q->setGeometry(geom);
+ QStyleHintReturnMask mask;
+ QStyleOption opt;
+ q->initStyleOption(&opt);
+ if (q->style()->styleHint(QStyle::SH_FocusFrame_Mask, &opt, q, &mask))
+ q->setMask(mask.region);
+}
+
+/*!
+ Initialize \a option with the values from this QFocusFrame. This method is useful
+ for subclasses when they need a QStyleOption, but don't want to fill
+ in all the information themselves.
+
+ \sa QStyleOption::initFrom()
+*/
+void QFocusFrame::initStyleOption(QStyleOption *option) const
+{
+ if (!option)
+ return;
+
+ option->initFrom(this);
+}
+
+/*!
+ \class QFocusFrame
+ \brief The QFocusFrame widget provides a focus frame which can be
+ outside of a widget's normal paintable area.
+
+ \ingroup basicwidgets
+ \mainclass
+
+ Normally an application will not need to create its own
+ QFocusFrame as QStyle will handle this detail for
+ you. A style writer can optionally use a QFocusFrame to have a
+ focus area outside of the widget's paintable geometry. In this way
+ space need not be reserved for the widget to have focus but only
+ set on a QWidget with QFocusFrame::setWidget. It is, however,
+ legal to create your own QFocusFrame on a custom widget and set
+ its geometry manually via QWidget::setGeometry however you will
+ not get auto-placement when the focused widget changes size or
+ placement.
+*/
+
+/*!
+ Constructs a QFocusFrame.
+
+ The focus frame will not monitor \a parent for updates but rather
+ can be placed manually or by using QFocusFrame::setWidget. A
+ QFocusFrame sets Qt::WA_NoChildEventsForParent attribute; as a
+ result the parent will not receive a QEvent::ChildInserted event,
+ this will make it possible to manually set the geometry of the
+ QFocusFrame inside of a QSplitter or other child event monitoring
+ widget.
+
+ \sa QFocusFrame::setWidget()
+*/
+
+QFocusFrame::QFocusFrame(QWidget *parent)
+ : QWidget(*new QFocusFramePrivate, parent, 0)
+{
+ setAttribute(Qt::WA_TransparentForMouseEvents);
+ setFocusPolicy(Qt::NoFocus);
+ setAttribute(Qt::WA_NoChildEventsForParent, true);
+ setAttribute(Qt::WA_AcceptDrops, style()->styleHint(QStyle::SH_FocusFrame_AboveWidget, 0, this));
+}
+
+/*!
+ Destructor.
+*/
+
+QFocusFrame::~QFocusFrame()
+{
+}
+
+/*!
+ QFocusFrame will track changes to \a widget and resize itself automatically.
+ If the monitored widget's parent changes, QFocusFrame will follow the widget
+ and place itself around the widget automatically. If the monitored widget is deleted,
+ QFocusFrame will set it to zero.
+
+ \sa QFocusFrame::widget()
+*/
+
+void
+QFocusFrame::setWidget(QWidget *widget)
+{
+ Q_D(QFocusFrame);
+ if(widget == d->widget)
+ return;
+
+ if(d->widget)
+ d->widget->removeEventFilter(this);
+ if(widget && !widget->isWindow() && widget->parentWidget()->windowType() != Qt::SubWindow) {
+ d->widget = widget;
+ widget->installEventFilter(this);
+ d->update();
+ } else {
+ d->widget = 0;
+ hide();
+ }
+}
+
+/*!
+ Returns the currently monitored widget for automatically resize and
+ update.
+
+ \sa QFocusFrame::setWidget()
+*/
+
+QWidget *
+QFocusFrame::widget() const
+{
+ Q_D(const QFocusFrame);
+ return d->widget;
+}
+
+
+/*! \reimp */
+void
+QFocusFrame::paintEvent(QPaintEvent *)
+{
+ QStylePainter p(this);
+ QStyleOption option;
+ initStyleOption(&option);
+ p.drawControl(QStyle::CE_FocusFrame, option);
+}
+
+
+/*! \reimp */
+bool
+QFocusFrame::eventFilter(QObject *o, QEvent *e)
+{
+ Q_D(QFocusFrame);
+ if(o == d->widget) {
+ switch(e->type()) {
+ case QEvent::Move:
+ case QEvent::Resize:
+ d->updateSize();
+ break;
+ case QEvent::Hide:
+ case QEvent::StyleChange:
+ hide();
+ break;
+ case QEvent::ParentChange:
+ d->update();
+ break;
+ case QEvent::Show:
+ d->update();
+ show();
+ break;
+ case QEvent::PaletteChange:
+ setPalette(d->widget->palette());
+ break;
+ case QEvent::ZOrderChange:
+ if (style()->styleHint(QStyle::SH_FocusFrame_AboveWidget, 0, this))
+ raise();
+ else
+ stackUnder(d->widget);
+ break;
+ case QEvent::Destroy:
+ setWidget(0);
+ break;
+ default:
+ break;
+ }
+ }
+ return false;
+}
+
+/*! \reimp */
+bool QFocusFrame::event(QEvent *e)
+{
+ return QWidget::event(e);
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/widgets/qfocusframe.h b/src/gui/widgets/qfocusframe.h
new file mode 100644
index 0000000000..d886e09c20
--- /dev/null
+++ b/src/gui/widgets/qfocusframe.h
@@ -0,0 +1,82 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QFOCUSFRAME_H
+#define QFOCUSFRAME_H
+
+#include <QtGui/qwidget.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+class QFocusFramePrivate;
+class QStyleOption;
+
+class Q_GUI_EXPORT QFocusFrame : public QWidget
+{
+ Q_OBJECT
+public:
+ QFocusFrame(QWidget *parent=0);
+ ~QFocusFrame();
+
+ void setWidget(QWidget *widget);
+ QWidget *widget() const;
+
+protected:
+ bool event(QEvent *e);
+
+ bool eventFilter(QObject *, QEvent *);
+ void paintEvent(QPaintEvent *);
+ void initStyleOption(QStyleOption *option) const;
+
+private:
+ Q_DECLARE_PRIVATE(QFocusFrame)
+ Q_DISABLE_COPY(QFocusFrame)
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QFOCUSFRAME_H
diff --git a/src/gui/widgets/qfontcombobox.cpp b/src/gui/widgets/qfontcombobox.cpp
new file mode 100644
index 0000000000..3c7e691f33
--- /dev/null
+++ b/src/gui/widgets/qfontcombobox.cpp
@@ -0,0 +1,467 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qfontcombobox.h"
+
+#ifndef QT_NO_FONTCOMBOBOX
+
+#include <qstringlistmodel.h>
+#include <qitemdelegate.h>
+#include <qlistview.h>
+#include <qpainter.h>
+#include <qevent.h>
+#include <qapplication.h>
+#include <private/qcombobox_p.h>
+#include <qdebug.h>
+
+QT_BEGIN_NAMESPACE
+
+static QFontDatabase::WritingSystem writingSystemForFont(const QFont &font, bool *hasLatin)
+{
+ *hasLatin = true;
+
+ QList<QFontDatabase::WritingSystem> writingSystems = QFontDatabase().writingSystems(font.family());
+// qDebug() << font.family() << writingSystems;
+
+ // this just confuses the algorithm below. Vietnamese is Latin with lots of special chars
+ writingSystems.removeAll(QFontDatabase::Vietnamese);
+
+ QFontDatabase::WritingSystem system = QFontDatabase::Any;
+
+ if (!writingSystems.contains(QFontDatabase::Latin)) {
+ *hasLatin = false;
+ // we need to show something
+ if (writingSystems.count())
+ system = writingSystems.last();
+ } else {
+ writingSystems.removeAll(QFontDatabase::Latin);
+ }
+
+ if (writingSystems.isEmpty())
+ return system;
+
+ if (writingSystems.count() == 1 && writingSystems.at(0) > QFontDatabase::Cyrillic) {
+ system = writingSystems.at(0);
+ return system;
+ }
+
+ if (writingSystems.count() <= 2
+ && writingSystems.last() > QFontDatabase::Armenian
+ && writingSystems.last() < QFontDatabase::Vietnamese) {
+ system = writingSystems.last();
+ return system;
+ }
+
+ if (writingSystems.count() <= 5
+ && writingSystems.last() >= QFontDatabase::SimplifiedChinese
+ && writingSystems.last() <= QFontDatabase::Korean)
+ system = writingSystems.last();
+
+ return system;
+}
+
+class QFontFamilyDelegate : public QAbstractItemDelegate
+{
+ Q_OBJECT
+public:
+ explicit QFontFamilyDelegate(QObject *parent);
+
+ // painting
+ void paint(QPainter *painter,
+ const QStyleOptionViewItem &option,
+ const QModelIndex &index) const;
+
+ QSize sizeHint(const QStyleOptionViewItem &option,
+ const QModelIndex &index) const;
+
+ QIcon truetype;
+ QIcon bitmap;
+ QFontDatabase::WritingSystem writingSystem;
+};
+
+QFontFamilyDelegate::QFontFamilyDelegate(QObject *parent)
+ : QAbstractItemDelegate(parent)
+{
+ truetype = QIcon(QLatin1String(":/trolltech/styles/commonstyle/images/fonttruetype-16.png"));
+ bitmap = QIcon(QLatin1String(":/trolltech/styles/commonstyle/images/fontbitmap-16.png"));
+ writingSystem = QFontDatabase::Any;
+}
+
+void QFontFamilyDelegate::paint(QPainter *painter,
+ const QStyleOptionViewItem &option,
+ const QModelIndex &index) const
+{
+ QString text = index.data(Qt::DisplayRole).toString();
+ QFont font(option.font);
+ font.setPointSize(QFontInfo(font).pointSize() * 3 / 2);
+ QFont font2 = font;
+ font2.setFamily(text);
+
+ bool hasLatin;
+ QFontDatabase::WritingSystem system = writingSystemForFont(font2, &hasLatin);
+ if (hasLatin)
+ font = font2;
+
+ QRect r = option.rect;
+
+ if (option.state & QStyle::State_Selected) {
+ painter->save();
+ painter->setBrush(option.palette.highlight());
+ painter->setPen(Qt::NoPen);
+ painter->drawRect(option.rect);
+ painter->setPen(QPen(option.palette.highlightedText(), 0));
+ }
+
+ const QIcon *icon = &bitmap;
+ if (QFontDatabase().isSmoothlyScalable(text)) {
+ icon = &truetype;
+ }
+ QSize actualSize = icon->actualSize(r.size());
+
+ icon->paint(painter, r, Qt::AlignLeft|Qt::AlignVCenter);
+ if (option.direction == Qt::RightToLeft)
+ r.setRight(r.right() - actualSize.width() - 4);
+ else
+ r.setLeft(r.left() + actualSize.width() + 4);
+
+ QFont old = painter->font();
+ painter->setFont(font);
+ painter->drawText(r, Qt::AlignVCenter|Qt::AlignLeading|Qt::TextSingleLine, text);
+
+ if (writingSystem != QFontDatabase::Any)
+ system = writingSystem;
+
+ if (system != QFontDatabase::Any) {
+ int w = painter->fontMetrics().width(text + QLatin1String(" "));
+ painter->setFont(font2);
+ QString sample = QFontDatabase().writingSystemSample(system);
+ if (option.direction == Qt::RightToLeft)
+ r.setRight(r.right() - w);
+ else
+ r.setLeft(r.left() + w);
+ painter->drawText(r, Qt::AlignVCenter|Qt::AlignLeading|Qt::TextSingleLine, sample);
+ }
+ painter->setFont(old);
+
+ if (option.state & QStyle::State_Selected)
+ painter->restore();
+
+}
+
+QSize QFontFamilyDelegate::sizeHint(const QStyleOptionViewItem &option,
+ const QModelIndex &index) const
+{
+ QString text = index.data(Qt::DisplayRole).toString();
+ QFont font(option.font);
+// font.setFamily(text);
+ font.setPointSize(QFontInfo(font).pointSize() * 3/2);
+ QFontMetrics fontMetrics(font);
+ return QSize(fontMetrics.width(text), fontMetrics.lineSpacing());
+}
+
+
+class QFontComboBoxPrivate : public QComboBoxPrivate
+{
+public:
+ inline QFontComboBoxPrivate() { filters = QFontComboBox::AllFonts; }
+
+ QFontComboBox::FontFilters filters;
+ QFont currentFont;
+
+ void _q_updateModel();
+ void _q_currentChanged(const QString &);
+
+ Q_DECLARE_PUBLIC(QFontComboBox)
+};
+
+
+void QFontComboBoxPrivate::_q_updateModel()
+{
+ Q_Q(QFontComboBox);
+ const int scalableMask = (QFontComboBox::ScalableFonts | QFontComboBox::NonScalableFonts);
+ const int spacingMask = (QFontComboBox::ProportionalFonts | QFontComboBox::MonospacedFonts);
+
+ QStringListModel *m = qobject_cast<QStringListModel *>(q->model());
+ if (!m)
+ return;
+ QFontFamilyDelegate *delegate = qobject_cast<QFontFamilyDelegate *>(q->view()->itemDelegate());
+ QFontDatabase::WritingSystem system = delegate ? delegate->writingSystem : QFontDatabase::Any;
+
+ QFontDatabase fdb;
+ QStringList list = fdb.families(system);
+ QStringList result;
+
+ int offset = 0;
+ QFontInfo fi(currentFont);
+
+ for (int i = 0; i < list.size(); ++i) {
+ if ((filters & scalableMask) && (filters & scalableMask) != scalableMask) {
+ if (bool(filters & QFontComboBox::ScalableFonts) != fdb.isSmoothlyScalable(list.at(i)))
+ continue;
+ }
+ if ((filters & spacingMask) && (filters & spacingMask) != spacingMask) {
+ if (bool(filters & QFontComboBox::MonospacedFonts) != fdb.isFixedPitch(list.at(i)))
+ continue;
+ }
+ result += list.at(i);
+ if (list.at(i) == fi.family() || list.at(i).startsWith(fi.family() + QLatin1String(" [")))
+ offset = result.count() - 1;
+ }
+ list = result;
+
+ m->setStringList(list);
+ if (list.isEmpty()) {
+ if (currentFont != QFont()) {
+ currentFont = QFont();
+ emit q->currentFontChanged(currentFont);
+ }
+ } else {
+ q->setCurrentIndex(offset);
+ }
+}
+
+
+void QFontComboBoxPrivate::_q_currentChanged(const QString &text)
+{
+ Q_Q(QFontComboBox);
+ QFont newFont(text);
+ if (currentFont != newFont) {
+ currentFont = newFont;
+ emit q->currentFontChanged(currentFont);
+ }
+}
+
+/*!
+ \class QFontComboBox
+ \brief The QFontComboBox widget is a combobox that lets the user
+ select a font family.
+
+ \since 4.2
+ \ingroup basicwidgets
+ \ingroup text
+
+ The combobox is populated with an alphabetized list of font
+ family names, such as Arial, Helvetica, and Times New Roman.
+ Family names are displayed using the actual font when possible.
+ For fonts such as Symbol, where the name is not representable in
+ the font itself, a sample of the font is displayed next to the
+ family name.
+
+ QFontComboBox is often used in toolbars, in conjunction with a
+ QComboBox for controlling the font size and two \l{QToolButton}s
+ for bold and italic.
+
+ When the user selects a new font, the currentFontChanged() signal
+ is emitted in addition to currentIndexChanged().
+
+ Call setWritingSystem() to tell QFontComboBox to show only fonts
+ that support a given writing system, and setFontFilters() to
+ filter out certain types of fonts as e.g. non scalable fonts or
+ monospaced fonts.
+
+ \image windowsxp-fontcombobox.png Screenshot of QFontComboBox on Windows XP
+
+ \sa QComboBox, QFont, QFontInfo, QFontMetrics, QFontDatabase, {Character Map Example}
+*/
+
+/*!
+ \fn void QFontComboBox::setWritingSystem(QFontDatabase::WritingSystem script)
+*/
+
+/*!
+ \fn void QFontComboBox::setCurrentFont(const QFont &font);
+*/
+
+/*!
+ Constructs a font combobox with the given \a parent.
+*/
+QFontComboBox::QFontComboBox(QWidget *parent)
+ : QComboBox(*new QFontComboBoxPrivate, parent)
+{
+ Q_D(QFontComboBox);
+ d->currentFont = font();
+ setEditable(true);
+
+ QStringListModel *m = new QStringListModel(this);
+ setModel(m);
+ setItemDelegate(new QFontFamilyDelegate(this));
+ QListView *lview = qobject_cast<QListView*>(view());
+ if (lview)
+ lview->setUniformItemSizes(true);
+ setWritingSystem(QFontDatabase::Any);
+
+ connect(this, SIGNAL(currentIndexChanged(QString)),
+ this, SLOT(_q_currentChanged(QString)));
+
+ connect(qApp, SIGNAL(fontDatabaseChanged()),
+ this, SLOT(_q_updateModel()));
+}
+
+
+/*!
+ Destroys the combobox.
+*/
+QFontComboBox::~QFontComboBox()
+{
+}
+
+/*!
+ \property QFontComboBox::writingSystem
+ \brief the writing system that serves as a filter for the combobox
+
+ If \a script is QFontDatabase::Any (the default), all fonts are
+ listed.
+
+ \sa fontFilters
+*/
+
+void QFontComboBox::setWritingSystem(QFontDatabase::WritingSystem script)
+{
+ Q_D(QFontComboBox);
+ QFontFamilyDelegate *delegate = qobject_cast<QFontFamilyDelegate *>(view()->itemDelegate());
+ if (delegate)
+ delegate->writingSystem = script;
+ d->_q_updateModel();
+}
+
+QFontDatabase::WritingSystem QFontComboBox::writingSystem() const
+{
+ QFontFamilyDelegate *delegate = qobject_cast<QFontFamilyDelegate *>(view()->itemDelegate());
+ if (delegate)
+ return delegate->writingSystem;
+ return QFontDatabase::Any;
+}
+
+
+/*!
+ \enum QFontComboBox::FontFilter
+
+ This enum can be used to only show certain types of fonts in the font combo box.
+
+ \value AllFonts Show all fonts
+ \value ScalableFonts Show scalable fonts
+ \value NonScalableFonts Show non scalable fonts
+ \value MonospacedFonts Show monospaced fonts
+ \value ProportionalFonts Show proportional fonts
+*/
+
+/*!
+ \property QFontComboBox::fontFilters
+ \brief the filter for the combobox
+
+ By default, all fonts are listed.
+
+ \sa writingSystem
+*/
+void QFontComboBox::setFontFilters(FontFilters filters)
+{
+ Q_D(QFontComboBox);
+ d->filters = filters;
+ d->_q_updateModel();
+}
+
+QFontComboBox::FontFilters QFontComboBox::fontFilters() const
+{
+ Q_D(const QFontComboBox);
+ return d->filters;
+}
+
+/*!
+ \property QFontComboBox::currentFont
+ \brief the currently selected font
+
+ \sa currentFontChanged(), currentIndex, currentText
+*/
+QFont QFontComboBox::currentFont() const
+{
+ Q_D(const QFontComboBox);
+ return d->currentFont;
+}
+
+void QFontComboBox::setCurrentFont(const QFont &font)
+{
+ Q_D(QFontComboBox);
+ if (font != d->currentFont) {
+ d->currentFont = font;
+ emit currentFontChanged(d->currentFont);
+ d->_q_updateModel();
+ }
+}
+
+/*!
+ \fn QFontComboBox::currentFontChanged(const QFont &font)
+
+ This signal is emitted whenever the current font changes, with
+ the new \a font.
+
+ \sa currentFont
+*/
+
+/*!
+ \reimp
+*/
+bool QFontComboBox::event(QEvent *e)
+{
+ if (e->type() == QEvent::Resize) {
+ QListView *lview = qobject_cast<QListView*>(view());
+ if (lview)
+ lview->window()->setFixedWidth(width() * 5 / 3);
+ }
+ return QComboBox::event(e);
+}
+
+/*!
+ \reimp
+*/
+QSize QFontComboBox::sizeHint() const
+{
+ QSize sz = QComboBox::sizeHint();
+ QFontMetrics fm(font());
+ sz.setWidth(fm.width(QLatin1Char('m'))*14);
+ return sz;
+}
+
+QT_END_NAMESPACE
+
+#include "qfontcombobox.moc"
+#include "moc_qfontcombobox.cpp"
+
+#endif // QT_NO_FONTCOMBOBOX
diff --git a/src/gui/widgets/qfontcombobox.h b/src/gui/widgets/qfontcombobox.h
new file mode 100644
index 0000000000..4929ff3f4b
--- /dev/null
+++ b/src/gui/widgets/qfontcombobox.h
@@ -0,0 +1,112 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QFONTCOMBOBOX_H
+#define QFONTCOMBOBOX_H
+
+#include <QtGui/qcombobox.h>
+#include <QtGui/qfontdatabase.h>
+
+#ifndef QT_NO_FONTCOMBOBOX
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+class QFontComboBoxPrivate;
+
+class Q_GUI_EXPORT QFontComboBox : public QComboBox
+{
+ Q_OBJECT
+ Q_FLAGS(FontFilters)
+ Q_PROPERTY(QFontDatabase::WritingSystem writingSystem READ writingSystem WRITE setWritingSystem)
+ Q_PROPERTY(FontFilters fontFilters READ fontFilters WRITE setFontFilters)
+ Q_PROPERTY(QFont currentFont READ currentFont WRITE setCurrentFont NOTIFY currentFontChanged)
+ Q_ENUMS(FontSelection)
+
+public:
+ explicit QFontComboBox(QWidget *parent = 0);
+ ~QFontComboBox();
+
+ void setWritingSystem(QFontDatabase::WritingSystem);
+ QFontDatabase::WritingSystem writingSystem() const;
+
+ enum FontFilter {
+ AllFonts = 0,
+ ScalableFonts = 0x1,
+ NonScalableFonts = 0x2,
+ MonospacedFonts = 0x4,
+ ProportionalFonts = 0x8
+ };
+ Q_DECLARE_FLAGS(FontFilters, FontFilter)
+
+ void setFontFilters(FontFilters filters);
+ FontFilters fontFilters() const;
+
+ QFont currentFont() const;
+ QSize sizeHint() const;
+
+public Q_SLOTS:
+ void setCurrentFont(const QFont &f);
+
+Q_SIGNALS:
+ void currentFontChanged(const QFont &f);
+
+protected:
+ bool event(QEvent *e);
+
+private:
+ Q_DISABLE_COPY(QFontComboBox)
+ Q_DECLARE_PRIVATE(QFontComboBox)
+ Q_PRIVATE_SLOT(d_func(), void _q_currentChanged(const QString &))
+ Q_PRIVATE_SLOT(d_func(), void _q_updateModel())
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(QFontComboBox::FontFilters)
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QT_NO_FONTCOMBOBOX
+#endif
diff --git a/src/gui/widgets/qframe.cpp b/src/gui/widgets/qframe.cpp
new file mode 100644
index 0000000000..6f813310fe
--- /dev/null
+++ b/src/gui/widgets/qframe.cpp
@@ -0,0 +1,566 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qframe.h"
+#include "qbitmap.h"
+#include "qdrawutil.h"
+#include "qevent.h"
+#include "qpainter.h"
+#include "qstyle.h"
+#include "qstyleoption.h"
+#include "qapplication.h"
+
+#include "qframe_p.h"
+
+QT_BEGIN_NAMESPACE
+
+QFramePrivate::QFramePrivate()
+ : frect(QRect(0, 0, 0, 0)),
+ frameStyle(QFrame::NoFrame | QFrame::Plain),
+ lineWidth(1),
+ midLineWidth(0),
+ frameWidth(0),
+ leftFrameWidth(0), rightFrameWidth(0),
+ topFrameWidth(0), bottomFrameWidth(0),
+ oldFrameStyle(QFrame::NoFrame | QFrame::Plain)
+{
+}
+
+inline void QFramePrivate::init()
+{
+ setLayoutItemMargins(QStyle::SE_FrameLayoutItem);
+}
+
+/*!
+ \class QFrame
+ \brief The QFrame class is the base class of widgets that can have a frame.
+
+ \ingroup abstractwidgets
+ \mainclass
+
+ QMenu uses this to "raise" the menu above the surrounding
+ screen. QProgressBar has a "sunken" look. QLabel has a flat look.
+ The frames of widgets like these can be changed.
+
+ \snippet doc/src/snippets/code/src_gui_widgets_qframe.cpp 0
+
+ The QFrame class can also be used directly for creating simple
+ placeholder frames without any contents.
+
+ The frame style is specified by a \l{QFrame::Shape}{frame shape} and
+ a \l{QFrame::Shadow}{shadow style} that is used to visually separate
+ the frame from surrounding widgets. These properties can be set
+ together using the setFrameStyle() function and read with frameStyle().
+
+ The frame shapes are \l NoFrame, \l Box, \l Panel, \l StyledPanel,
+ HLine and \l VLine; the shadow styles are \l Plain, \l Raised and
+ \l Sunken.
+
+ A frame widget has three attributes that describe the thickness of the
+ border: \l lineWidth, \l midLineWidth, and \l frameWidth.
+
+ \list
+ \o The line width is the width of the frame border. It can be modified
+ to customize the frame's appearance.
+
+ \o The mid-line width specifies the width of an extra line in the
+ middle of the frame, which uses a third color to obtain a special
+ 3D effect. Notice that a mid-line is only drawn for \l Box, \l
+ HLine and \l VLine frames that are raised or sunken.
+
+ \o The frame width is determined by the frame style, and the frameWidth()
+ function is used to obtain the value defined for the style used.
+ \endlist
+
+ The margin between the frame and the contents of the frame can be
+ customized with the QWidget::setContentsMargins() function.
+
+ \target picture
+ This table shows some of the combinations of styles and line widths:
+
+ \image frames.png Table of frame styles
+*/
+
+
+/*!
+ \enum QFrame::Shape
+
+ This enum type defines the shapes of frame available.
+
+ \value NoFrame QFrame draws nothing
+ \value Box QFrame draws a box around its contents
+ \value Panel QFrame draws a panel to make the contents appear
+ raised or sunken
+ \value StyledPanel draws a rectangular panel with a look that
+ depends on the current GUI style. It can be raised or sunken.
+ \value HLine QFrame draws a horizontal line that frames nothing
+ (useful as separator)
+ \value VLine QFrame draws a vertical line that frames nothing
+ (useful as separator)
+ \value WinPanel draws a rectangular panel that can be raised or
+ sunken like those in Windows 95. Specifying this shape sets
+ the line width to 2 pixels. WinPanel is provided for compatibility.
+ For GUI style independence we recommend using StyledPanel instead.
+
+ \omitvalue GroupBoxPanel
+ \omitvalue ToolBarPanel
+ \omitvalue MenuBarPanel
+ \omitvalue PopupPanel
+ \omitvalue LineEditPanel
+ \omitvalue TabWidgetPanel
+
+ When it does not call QStyle, Shape interacts with QFrame::Shadow,
+ the lineWidth() and the midLineWidth() to create the total result.
+ See the picture of the frames in the main class documentation.
+
+ \sa QFrame::Shadow QFrame::style() QStyle::drawPrimitive()
+*/
+
+
+/*!
+ \enum QFrame::Shadow
+
+ This enum type defines the types of shadow that are used to give
+ a 3D effect to frames.
+
+ \value Plain the frame and contents appear level with the
+ surroundings; draws using the palette QPalette::WindowText color
+ (without any 3D effect)
+
+ \value Raised the frame and contents appear raised; draws a 3D
+ raised line using the light and dark colors of the current color
+ group
+ \value Sunken the frame and contents appear sunken; draws a 3D
+ sunken line using the light and dark colors of the current color
+ group
+
+ Shadow interacts with QFrame::Shape, the lineWidth() and the
+ midLineWidth(). See the picture of the frames in the main class
+ documentation.
+
+ \sa QFrame::Shape lineWidth() midLineWidth()
+*/
+
+/*!
+ \enum QFrame::StyleMask
+
+ This enum defines two constants that can be used to extract the
+ two components of frameStyle():
+
+ \value Shadow_Mask The \l Shadow part of frameStyle()
+ \value Shape_Mask The \l Shape part of frameStyle()
+
+ \omitvalue MShadow
+ \omitvalue MShape
+
+ Normally, you don't need to use these, since frameShadow() and
+ frameShape() already extract the \l Shadow and the \l Shape parts
+ of frameStyle().
+
+ \sa frameStyle(), setFrameStyle()
+*/
+
+/*!
+ Constructs a frame widget with frame style \l NoFrame and a
+ 1-pixel frame width.
+
+ The \a parent and \a f arguments are passed to the QWidget
+ constructor.
+*/
+
+QFrame::QFrame(QWidget* parent, Qt::WindowFlags f)
+ : QWidget(*new QFramePrivate, parent, f)
+{
+ Q_D(QFrame);
+ d->init();
+}
+
+/*! \internal */
+QFrame::QFrame(QFramePrivate &dd, QWidget* parent, Qt::WindowFlags f)
+ : QWidget(dd, parent, f)
+{
+ Q_D(QFrame);
+ d->init();
+}
+
+#ifdef QT3_SUPPORT
+/*!
+ Use one of the constructors that doesn't take the \a name
+ argument and then use setObjectName() instead.
+*/
+QFrame::QFrame(QWidget *parent, const char *name, Qt::WindowFlags f)
+ : QWidget(*new QFramePrivate, parent, f)
+{
+ Q_D(QFrame);
+ setObjectName(QString::fromAscii(name));
+ d->init();
+}
+#endif
+
+/*!
+ Destroys the frame.
+ */
+QFrame::~QFrame()
+{
+}
+
+/*!
+ Returns the frame style.
+
+ The default value is QFrame::NoFrame.
+
+ \sa setFrameStyle(), frameShape(), frameShadow()
+*/
+int QFrame::frameStyle() const
+{
+ Q_D(const QFrame);
+ return d->frameStyle;
+}
+
+/*!
+ \property QFrame::frameShape
+ \brief the frame shape value from the frame style
+
+ \sa frameStyle(), frameShadow()
+*/
+
+QFrame::Shape QFrame::frameShape() const
+{
+ Q_D(const QFrame);
+ return (Shape) (d->frameStyle & Shape_Mask);
+}
+
+void QFrame::setFrameShape(QFrame::Shape s)
+{
+ Q_D(QFrame);
+ setFrameStyle((d->frameStyle & Shadow_Mask) | s);
+}
+
+
+/*!
+ \property QFrame::frameShadow
+ \brief the frame shadow value from the frame style
+
+ \sa frameStyle(), frameShape()
+*/
+QFrame::Shadow QFrame::frameShadow() const
+{
+ Q_D(const QFrame);
+ return (Shadow) (d->frameStyle & Shadow_Mask);
+}
+
+void QFrame::setFrameShadow(QFrame::Shadow s)
+{
+ Q_D(QFrame);
+ setFrameStyle((d->frameStyle & Shape_Mask) | s);
+}
+
+/*!
+ Sets the frame style to \a style.
+
+ The \a style is the bitwise OR between a frame shape and a frame
+ shadow style. See the picture of the frames in the main class
+ documentation.
+
+ The frame shapes are given in \l{QFrame::Shape} and the shadow
+ styles in \l{QFrame::Shadow}.
+
+ If a mid-line width greater than 0 is specified, an additional
+ line is drawn for \l Raised or \l Sunken \l Box, \l HLine, and \l
+ VLine frames. The mid-color of the current color group is used for
+ drawing middle lines.
+
+ \sa frameStyle()
+*/
+
+void QFrame::setFrameStyle(int style)
+{
+ Q_D(QFrame);
+ if (!testAttribute(Qt::WA_WState_OwnSizePolicy)) {
+ QSizePolicy sp;
+
+ switch (style & Shape_Mask) {
+ case HLine:
+ sp = QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed, QSizePolicy::Line);
+ break;
+ case VLine:
+ sp = QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Minimum, QSizePolicy::Line);
+ break;
+ default:
+ sp = QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred, QSizePolicy::Frame);
+ }
+ setSizePolicy(sp);
+ setAttribute(Qt::WA_WState_OwnSizePolicy, false);
+ }
+ d->frameStyle = (short)style;
+ update();
+ d->updateFrameWidth();
+ d->oldFrameStyle = (short)style;
+}
+
+/*!
+ \property QFrame::lineWidth
+ \brief the line width
+
+ Note that the \e total line width for frames used as separators
+ (\l HLine and \l VLine) is specified by \l frameWidth.
+
+ The default value is 1.
+
+ \sa midLineWidth, frameWidth
+*/
+
+void QFrame::setLineWidth(int w)
+{
+ Q_D(QFrame);
+ if (short(w) == d->lineWidth)
+ return;
+ d->lineWidth = short(w);
+ d->updateFrameWidth();
+}
+
+int QFrame::lineWidth() const
+{
+ Q_D(const QFrame);
+ return d->lineWidth;
+}
+
+/*!
+ \property QFrame::midLineWidth
+ \brief the width of the mid-line
+
+ The default value is 0.
+
+ \sa lineWidth, frameWidth
+*/
+
+void QFrame::setMidLineWidth(int w)
+{
+ Q_D(QFrame);
+ if (short(w) == d->midLineWidth)
+ return;
+ d->midLineWidth = short(w);
+ d->updateFrameWidth();
+}
+
+int QFrame::midLineWidth() const
+{
+ Q_D(const QFrame);
+ return d->midLineWidth;
+}
+
+/*!
+ \internal
+ Updates the frame widths from the style.
+*/
+void QFramePrivate::updateStyledFrameWidths()
+{
+ Q_Q(const QFrame);
+ QStyleOptionFrameV3 opt;
+ opt.initFrom(q);
+ opt.lineWidth = lineWidth;
+ opt.midLineWidth = midLineWidth;
+ opt.frameShape = QFrame::Shape(frameStyle & QFrame::Shape_Mask);
+
+ QRect cr = q->style()->subElementRect(QStyle::SE_ShapedFrameContents, &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 QFramePrivate::updateFrameWidth()
+{
+ Q_Q(QFrame);
+ QRect fr = q->frameRect();
+ updateStyledFrameWidths();
+ q->setFrameRect(fr);
+ setLayoutItemMargins(QStyle::SE_FrameLayoutItem);
+}
+
+/*!
+ \property QFrame::frameWidth
+ \brief the width of the frame that is drawn.
+
+ Note that the frame width depends on the \l{QFrame::setFrameStyle()}{frame style},
+ not only the line width and the mid-line width. For example, the style specified
+ by \l NoFrame always has a frame width of 0, whereas the style \l Panel has a
+ frame width equivalent to the line width.
+
+ \sa lineWidth(), midLineWidth(), frameStyle()
+*/
+int QFrame::frameWidth() const
+{
+ Q_D(const QFrame);
+ return d->frameWidth;
+}
+
+
+/*!
+ \property QFrame::frameRect
+ \brief the frame's rectangle
+
+ The frame's rectangle is the rectangle the frame is drawn in. By
+ default, this is the entire widget. Setting the rectangle does
+ does \e not cause a widget update. The frame rectangle is
+ automatically adjusted when the widget changes size.
+
+ If you set the rectangle to a null rectangle (for example,
+ QRect(0, 0, 0, 0)), then the resulting frame rectangle is
+ equivalent to the \link QWidget::rect() widget rectangle\endlink.
+*/
+
+QRect QFrame::frameRect() const
+{
+ Q_D(const QFrame);
+ QRect fr = contentsRect();
+ fr.adjust(-d->leftFrameWidth, -d->topFrameWidth, d->rightFrameWidth, d->bottomFrameWidth);
+ return fr;
+}
+
+void QFrame::setFrameRect(const QRect &r)
+{
+ Q_D(QFrame);
+ QRect cr = r.isValid() ? r : rect();
+ cr.adjust(d->leftFrameWidth, d->topFrameWidth, -d->rightFrameWidth, -d->bottomFrameWidth);
+ setContentsMargins(cr.left(), cr.top(), rect().right() - cr.right(), rect().bottom() - cr.bottom());
+}
+
+/*!\reimp
+*/
+QSize QFrame::sizeHint() const
+{
+ Q_D(const QFrame);
+ // Returns a size hint for the frame - for HLine and VLine
+ // shapes, this is stretchable one way and 3 pixels wide the
+ // other. For other shapes, QWidget::sizeHint() is used.
+ switch (d->frameStyle & Shape_Mask) {
+ case HLine:
+ return QSize(-1,3);
+ case VLine:
+ return QSize(3,-1);
+ default:
+ return QWidget::sizeHint();
+ }
+}
+
+/*!\reimp
+*/
+
+void QFrame::paintEvent(QPaintEvent *)
+{
+ QPainter paint(this);
+ drawFrame(&paint);
+}
+
+/*!
+ \internal
+
+ Mostly for the sake of Q3Frame
+ */
+void QFrame::drawFrame(QPainter *p)
+{
+ Q_D(QFrame);
+ QStyleOptionFrameV3 opt;
+ opt.init(this);
+ int frameShape = d->frameStyle & QFrame::Shape_Mask;
+ int frameShadow = d->frameStyle & QFrame::Shadow_Mask;
+ opt.frameShape = Shape(int(opt.frameShape) | frameShape);
+ opt.rect = frameRect();
+ switch (frameShape) {
+ case QFrame::Box:
+ case QFrame::HLine:
+ case QFrame::VLine:
+ case QFrame::StyledPanel:
+ case QFrame::Panel:
+ opt.lineWidth = d->lineWidth;
+ opt.midLineWidth = d->midLineWidth;
+ break;
+ default:
+ // most frame styles do not handle customized line and midline widths
+ // (see updateFrameWidth()).
+ opt.lineWidth = d->frameWidth;
+ break;
+ }
+
+ if (frameShadow == Sunken)
+ opt.state |= QStyle::State_Sunken;
+ else if (frameShadow == Raised)
+ opt.state |= QStyle::State_Raised;
+
+ style()->drawControl(QStyle::CE_ShapedFrame, &opt, p, this);
+}
+
+
+/*!\reimp
+ */
+void QFrame::changeEvent(QEvent *ev)
+{
+ Q_D(QFrame);
+ if (ev->type() == QEvent::StyleChange
+#ifdef Q_WS_MAC
+ || ev->type() == QEvent::MacSizeChange
+#endif
+ )
+ d->updateFrameWidth();
+ QWidget::changeEvent(ev);
+}
+
+/*! \reimp */
+bool QFrame::event(QEvent *e)
+{
+ if (e->type() == QEvent::ParentChange)
+ d_func()->updateFrameWidth();
+ bool result = QWidget::event(e);
+ //this has to be done after the widget has been polished
+ if (e->type() == QEvent::Polish)
+ d_func()->updateFrameWidth();
+ return result;
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/widgets/qframe.h b/src/gui/widgets/qframe.h
new file mode 100644
index 0000000000..7bffb59e09
--- /dev/null
+++ b/src/gui/widgets/qframe.h
@@ -0,0 +1,148 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QFRAME_H
+#define QFRAME_H
+
+#include <QtGui/qwidget.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+class QFramePrivate;
+
+class Q_GUI_EXPORT QFrame : public QWidget
+{
+ Q_OBJECT
+
+ Q_ENUMS(Shape Shadow)
+ Q_PROPERTY(Shape frameShape READ frameShape WRITE setFrameShape)
+ Q_PROPERTY(Shadow frameShadow READ frameShadow WRITE setFrameShadow)
+ Q_PROPERTY(int lineWidth READ lineWidth WRITE setLineWidth)
+ Q_PROPERTY(int midLineWidth READ midLineWidth WRITE setMidLineWidth)
+ Q_PROPERTY(int frameWidth READ frameWidth)
+ Q_PROPERTY(QRect frameRect READ frameRect WRITE setFrameRect DESIGNABLE false)
+
+public:
+ explicit QFrame(QWidget* parent = 0, Qt::WindowFlags f = 0);
+ ~QFrame();
+
+ int frameStyle() const;
+ void setFrameStyle(int);
+
+ int frameWidth() const;
+
+ QSize sizeHint() const;
+
+ enum Shape {
+ NoFrame = 0, // no frame
+ Box = 0x0001, // rectangular box
+ Panel = 0x0002, // rectangular panel
+ WinPanel = 0x0003, // rectangular panel (Windows)
+ HLine = 0x0004, // horizontal line
+ VLine = 0x0005, // vertical line
+ StyledPanel = 0x0006 // rectangular panel depending on the GUI style
+
+#if defined(QT3_SUPPORT) && !defined(Q_MOC_RUN)
+ ,PopupPanel = StyledPanel, // rectangular panel depending on the GUI style
+ MenuBarPanel = StyledPanel,
+ ToolBarPanel = StyledPanel,
+ LineEditPanel = StyledPanel,
+ TabWidgetPanel = StyledPanel,
+ GroupBoxPanel = StyledPanel
+#endif
+ };
+ enum Shadow {
+ Plain = 0x0010, // plain line
+ Raised = 0x0020, // raised shadow effect
+ Sunken = 0x0030 // sunken shadow effect
+ };
+
+ enum StyleMask {
+ Shadow_Mask = 0x00f0, // mask for the shadow
+ Shape_Mask = 0x000f // mask for the shape
+#if defined(QT3_SUPPORT)
+ ,MShadow = Shadow_Mask,
+ MShape = Shape_Mask
+#endif
+ };
+
+ Shape frameShape() const;
+ void setFrameShape(Shape);
+ Shadow frameShadow() const;
+ void setFrameShadow(Shadow);
+
+ int lineWidth() const;
+ void setLineWidth(int);
+
+ int midLineWidth() const;
+ void setMidLineWidth(int);
+
+ QRect frameRect() const;
+ void setFrameRect(const QRect &);
+
+protected:
+ bool event(QEvent *e);
+ void paintEvent(QPaintEvent *);
+ void changeEvent(QEvent *);
+ void drawFrame(QPainter *);
+
+#ifdef QT3_SUPPORT
+public:
+ QT3_SUPPORT_CONSTRUCTOR QFrame(QWidget* parent, const char* name, Qt::WindowFlags f = 0);
+#endif
+
+protected:
+ QFrame(QFramePrivate &dd, QWidget* parent = 0, Qt::WindowFlags f = 0);
+
+private:
+ Q_DISABLE_COPY(QFrame)
+ Q_DECLARE_PRIVATE(QFrame)
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QFRAME_H
diff --git a/src/gui/widgets/qframe_p.h b/src/gui/widgets/qframe_p.h
new file mode 100644
index 0000000000..4fd341dfc3
--- /dev/null
+++ b/src/gui/widgets/qframe_p.h
@@ -0,0 +1,85 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QFRAME_P_H
+#define QFRAME_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"
+#include "qframe.h"
+
+QT_BEGIN_NAMESPACE
+
+class Q_GUI_EXPORT QFramePrivate : public QWidgetPrivate
+{
+ Q_DECLARE_PUBLIC(QFrame)
+public:
+ QFramePrivate();
+
+ void updateFrameWidth();
+ void updateStyledFrameWidths();
+
+ QRect frect;
+ int frameStyle;
+ short lineWidth;
+ short midLineWidth;
+ short frameWidth;
+ short leftFrameWidth, rightFrameWidth;
+ short topFrameWidth, bottomFrameWidth;
+ short oldFrameStyle;
+
+ inline void init();
+
+};
+
+QT_END_NAMESPACE
+
+#endif // QFRAME_P_H
diff --git a/src/gui/widgets/qgroupbox.cpp b/src/gui/widgets/qgroupbox.cpp
new file mode 100644
index 0000000000..6a82483eee
--- /dev/null
+++ b/src/gui/widgets/qgroupbox.cpp
@@ -0,0 +1,792 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qgroupbox.h"
+#ifndef QT_NO_GROUPBOX
+#include "qapplication.h"
+#include "qbitmap.h"
+#include "qdrawutil.h"
+#include "qevent.h"
+#include "qlayout.h"
+#include "qradiobutton.h"
+#include "qstyle.h"
+#include "qstyleoption.h"
+#include "qstylepainter.h"
+#ifndef QT_NO_ACCESSIBILITY
+#include "qaccessible.h"
+#endif
+#include <private/qwidget_p.h>
+
+#include "qdebug.h"
+
+QT_BEGIN_NAMESPACE
+
+class QGroupBoxPrivate : public QWidgetPrivate
+{
+ Q_DECLARE_PUBLIC(QGroupBox)
+
+public:
+ void skip();
+ void init();
+ void calculateFrame();
+ QString title;
+ int align;
+#ifndef QT_NO_SHORTCUT
+ int shortcutId;
+#endif
+
+ void _q_fixFocus(Qt::FocusReason reason);
+ void _q_setChildrenEnabled(bool b);
+ void click();
+ bool flat;
+ bool checkable;
+ bool checked;
+ bool hover;
+ bool overCheckBox;
+ QStyle::SubControl pressedControl;
+};
+
+/*!
+ Initialize \a option with the values from this QGroupBox. This method
+ is useful for subclasses when they need a QStyleOptionGroupBox, but don't want
+ to fill in all the information themselves.
+
+ \sa QStyleOption::initFrom()
+*/
+void QGroupBox::initStyleOption(QStyleOptionGroupBox *option) const
+{
+ if (!option)
+ return;
+
+ Q_D(const QGroupBox);
+ option->initFrom(this);
+ option->text = d->title;
+ option->lineWidth = 1;
+ option->midLineWidth = 0;
+ option->textAlignment = Qt::Alignment(d->align);
+ option->activeSubControls |= d->pressedControl;
+ option->subControls = QStyle::SC_GroupBoxFrame;
+
+ if (d->hover)
+ option->state |= QStyle::State_MouseOver;
+ else
+ option->state &= ~QStyle::State_MouseOver;
+
+ if (d->flat)
+ option->features |= QStyleOptionFrameV2::Flat;
+
+ if (d->checkable) {
+ option->subControls |= QStyle::SC_GroupBoxCheckBox;
+ option->state |= (d->checked ? QStyle::State_On : QStyle::State_Off);
+ if ((d->pressedControl == QStyle::SC_GroupBoxCheckBox
+ || d->pressedControl == QStyle::SC_GroupBoxLabel) && (d->hover || d->overCheckBox))
+ option->state |= QStyle::State_Sunken;
+ }
+
+ if (!option->palette.isBrushSet(isEnabled() ? QPalette::Active :
+ QPalette::Disabled, QPalette::WindowText))
+ option->textColor = QColor(style()->styleHint(QStyle::SH_GroupBox_TextLabelColor,
+ option, this));
+
+ if (!d->title.isEmpty())
+ option->subControls |= QStyle::SC_GroupBoxLabel;
+}
+
+void QGroupBoxPrivate::click()
+{
+ Q_Q(QGroupBox);
+
+ QPointer<QGroupBox> guard(q);
+ q->setChecked(!checked);
+ if (!guard)
+ return;
+ emit q->clicked(checked);
+}
+
+/*!
+ \class QGroupBox
+ \brief The QGroupBox widget provides a group box frame with a title.
+
+ \ingroup organizers
+ \ingroup geomanagement
+ \ingroup appearance
+ \mainclass
+
+ 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.
+
+ QGroupBox also lets you set the \l title (normally set in the
+ constructor) and the title's \l alignment. Group boxes can be
+ \l checkable; child widgets in checkable group boxes are enabled or
+ disabled depending on whether or not the group box is \l checked.
+
+ You can minimize the space consumption of a group box by enabling
+ the \l flat property. In most \l{QStyle}{styles}, enabling this
+ property results in the removal of the left, right and bottom
+ edges of the frame.
+
+ QGroupBox doesn't automatically lay out the child widgets (which
+ are often \l{QCheckBox}es or \l{QRadioButton}s but can be any
+ widgets). The following example shows how we can set up a
+ QGroupBox with a layout:
+
+ \snippet examples/widgets/groupbox/window.cpp 2
+
+ \table 100%
+ \row \o \inlineimage windowsxp-groupbox.png Screenshot of a Windows XP style group box
+ \o \inlineimage macintosh-groupbox.png Screenshot of a Macintosh style group box
+ \o \inlineimage plastique-groupbox.png Screenshot of a Plastique style group box
+ \row \o A \l{Windows XP Style Widget Gallery}{Windows XP style} group box.
+ \o A \l{Macintosh Style Widget Gallery}{Macintosh style} group box.
+ \o A \l{Plastique Style Widget Gallery}{Plastique style} group box.
+ \endtable
+
+ \sa QButtonGroup, {Group Box Example}
+*/
+
+
+
+/*!
+ Constructs a group box widget with the given \a parent but with no title.
+*/
+
+QGroupBox::QGroupBox(QWidget *parent)
+ : QWidget(*new QGroupBoxPrivate, parent, 0)
+{
+ Q_D(QGroupBox);
+ d->init();
+}
+
+/*!
+ Constructs a group box with the given \a title and \a parent.
+*/
+
+QGroupBox::QGroupBox(const QString &title, QWidget *parent)
+ : QWidget(*new QGroupBoxPrivate, parent, 0)
+{
+ Q_D(QGroupBox);
+ d->init();
+ setTitle(title);
+}
+
+
+/*!
+ Destroys the group box.
+*/
+QGroupBox::~QGroupBox()
+{
+}
+
+void QGroupBoxPrivate::init()
+{
+ Q_Q(QGroupBox);
+ align = Qt::AlignLeft;
+#ifndef QT_NO_SHORTCUT
+ shortcutId = 0;
+#endif
+ flat = false;
+ checkable = false;
+ checked = true;
+ hover = false;
+ overCheckBox = false;
+ pressedControl = QStyle::SC_None;
+ calculateFrame();
+ q->setSizePolicy(QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred,
+ QSizePolicy::GroupBox));
+}
+
+void QGroupBox::setTitle(const QString &title)
+{
+ Q_D(QGroupBox);
+ if (d->title == title) // no change
+ return;
+ d->title = title;
+#ifndef QT_NO_SHORTCUT
+ releaseShortcut(d->shortcutId);
+ d->shortcutId = grabShortcut(QKeySequence::mnemonic(title));
+#endif
+ d->calculateFrame();
+
+ update();
+ updateGeometry();
+#ifndef QT_NO_ACCESSIBILITY
+ QAccessible::updateAccessibility(this, 0, QAccessible::NameChanged);
+#endif
+}
+
+/*!
+ \property QGroupBox::title
+ \brief the group box title text
+
+ The group box title text will have a keyboard shortcut if the title
+ contains an ampersand ('&') followed by a letter.
+
+ \snippet doc/src/snippets/code/src_gui_widgets_qgroupbox.cpp 0
+
+ In the example above, \key Alt+U moves the keyboard focus to the
+ group box. See the \l {QShortcut#mnemonic}{QShortcut}
+ documentation for details (to display an actual ampersand, use
+ '&&').
+
+ There is no default title text.
+
+ \sa alignment
+*/
+
+QString QGroupBox::title() const
+{
+ Q_D(const QGroupBox);
+ return d->title;
+}
+
+/*!
+ \property QGroupBox::alignment
+ \brief the alignment of the group box title.
+
+ Most styles place the title at the top of the frame. The horizontal
+ alignment of the title can be specified using single values from
+ the following list:
+
+ \list
+ \i Qt::AlignLeft aligns the title text with the left-hand side of the group box.
+ \i Qt::AlignRight aligns the title text with the right-hand side of the group box.
+ \i Qt::AlignHCenter aligns the title text with the horizontal center of the group box.
+ \endlist
+
+ The default alignment is Qt::AlignLeft.
+
+ \sa Qt::Alignment
+*/
+Qt::Alignment QGroupBox::alignment() const
+{
+ Q_D(const QGroupBox);
+ return QFlag(d->align);
+}
+
+void QGroupBox::setAlignment(int alignment)
+{
+ Q_D(QGroupBox);
+ d->align = alignment;
+ updateGeometry();
+ update();
+}
+
+/*! \reimp
+*/
+void QGroupBox::resizeEvent(QResizeEvent *e)
+{
+ QWidget::resizeEvent(e);
+}
+
+/*! \reimp
+*/
+
+void QGroupBox::paintEvent(QPaintEvent *)
+{
+ QStylePainter paint(this);
+ QStyleOptionGroupBox option;
+ initStyleOption(&option);
+ paint.drawComplexControl(QStyle::CC_GroupBox, option);
+}
+
+/*! \reimp */
+bool QGroupBox::event(QEvent *e)
+{
+ Q_D(QGroupBox);
+#ifndef QT_NO_SHORTCUT
+ if (e->type() == QEvent::Shortcut) {
+ QShortcutEvent *se = static_cast<QShortcutEvent *>(e);
+ if (se->shortcutId() == d->shortcutId) {
+ if (!isCheckable()) {
+ d->_q_fixFocus(Qt::ShortcutFocusReason);
+ } else {
+ d->click();
+ setFocus(Qt::ShortcutFocusReason);
+ }
+ return true;
+ }
+ }
+#endif
+ QStyleOptionGroupBox box;
+ initStyleOption(&box);
+ switch (e->type()) {
+ case QEvent::HoverEnter:
+ case QEvent::HoverMove: {
+ QStyle::SubControl control = style()->hitTestComplexControl(QStyle::CC_GroupBox, &box,
+ static_cast<QHoverEvent *>(e)->pos(),
+ this);
+ bool oldHover = d->hover;
+ d->hover = d->checkable && (control == QStyle::SC_GroupBoxLabel || control == QStyle::SC_GroupBoxCheckBox);
+ if (oldHover != d->hover) {
+ QRect rect = style()->subControlRect(QStyle::CC_GroupBox, &box, QStyle::SC_GroupBoxCheckBox, this)
+ | style()->subControlRect(QStyle::CC_GroupBox, &box, QStyle::SC_GroupBoxLabel, this);
+ update(rect);
+ }
+ return true;
+ }
+ case QEvent::HoverLeave:
+ d->hover = false;
+ if (d->checkable) {
+ QRect rect = style()->subControlRect(QStyle::CC_GroupBox, &box, QStyle::SC_GroupBoxCheckBox, this)
+ | style()->subControlRect(QStyle::CC_GroupBox, &box, QStyle::SC_GroupBoxLabel, this);
+ update(rect);
+ }
+ return true;
+ case QEvent::KeyPress: {
+ QKeyEvent *k = static_cast<QKeyEvent*>(e);
+ if (!k->isAutoRepeat() && (k->key() == Qt::Key_Select || k->key() == Qt::Key_Space)) {
+ d->pressedControl = QStyle::SC_GroupBoxCheckBox;
+ update(style()->subControlRect(QStyle::CC_GroupBox, &box, QStyle::SC_GroupBoxCheckBox, this));
+ return true;
+ }
+ break;
+ }
+ case QEvent::KeyRelease: {
+ QKeyEvent *k = static_cast<QKeyEvent*>(e);
+ if (!k->isAutoRepeat() && (k->key() == Qt::Key_Select || k->key() == Qt::Key_Space)) {
+ bool toggle = (d->pressedControl == QStyle::SC_GroupBoxLabel
+ || d->pressedControl == QStyle::SC_GroupBoxCheckBox);
+ d->pressedControl = QStyle::SC_None;
+ if (toggle)
+ d->click();
+ return true;
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ return QWidget::event(e);
+}
+
+/*!\reimp */
+void QGroupBox::childEvent(QChildEvent *c)
+{
+ Q_D(QGroupBox);
+ if (c->type() != QEvent::ChildAdded || !c->child()->isWidgetType())
+ return;
+ QWidget *w = (QWidget*)c->child();
+ if (d->checkable) {
+ if (d->checked) {
+ if (!w->testAttribute(Qt::WA_ForceDisabled))
+ w->setEnabled(true);
+ } else {
+ if (w->isEnabled()) {
+ w->setEnabled(false);
+ w->setAttribute(Qt::WA_ForceDisabled, false);
+ }
+ }
+ }
+}
+
+
+/*!
+ \internal
+
+ This private slot finds a widget in this group box that can accept
+ focus, and gives the focus to that widget.
+*/
+
+void QGroupBoxPrivate::_q_fixFocus(Qt::FocusReason reason)
+{
+ Q_Q(QGroupBox);
+ QWidget *fw = q->focusWidget();
+ if (!fw) {
+ QWidget * best = 0;
+ QWidget * candidate = 0;
+ QWidget * w = q;
+ while ((w = w->nextInFocusChain()) != q) {
+ if (q->isAncestorOf(w) && (w->focusPolicy() & Qt::TabFocus) == Qt::TabFocus && w->isVisibleTo(q)) {
+ if (!best && qobject_cast<QRadioButton*>(w) && ((QRadioButton*)w)->isChecked())
+ // we prefer a checked radio button or a widget that
+ // already has focus, if there is one
+ best = w;
+ else
+ if (!candidate)
+ // but we'll accept anything that takes focus
+ candidate = w;
+ }
+ }
+ if (best)
+ fw = best;
+ else
+ if (candidate)
+ fw = candidate;
+ }
+ if (fw)
+ fw->setFocus(reason);
+}
+
+/*
+ Sets the right frame rect depending on the title.
+*/
+void QGroupBoxPrivate::calculateFrame()
+{
+ Q_Q(QGroupBox);
+ QStyleOptionGroupBox box;
+ q->initStyleOption(&box);
+ QRect contentsRect = q->style()->subControlRect(QStyle::CC_GroupBox, &box, QStyle::SC_GroupBoxContents, q);
+ q->setContentsMargins(contentsRect.left() - box.rect.left(), contentsRect.top() - box.rect.top(),
+ box.rect.right() - contentsRect.right(), box.rect.bottom() - contentsRect.bottom());
+ setLayoutItemMargins(QStyle::SE_GroupBoxLayoutItem, &box);
+}
+
+/*! \reimp
+ */
+void QGroupBox::focusInEvent(QFocusEvent *fe)
+{ // note no call to super
+ Q_D(QGroupBox);
+ if (focusPolicy() == Qt::NoFocus) {
+ d->_q_fixFocus(fe->reason());
+ } else {
+ QStyleOptionGroupBox box;
+ initStyleOption(&box);
+ QRect rect = style()->subControlRect(QStyle::CC_GroupBox, &box, QStyle::SC_GroupBoxCheckBox, this)
+ | style()->subControlRect(QStyle::CC_GroupBox, &box, QStyle::SC_GroupBoxLabel, this);
+ update(rect);
+ }
+}
+
+
+/*!
+ \reimp
+*/
+QSize QGroupBox::minimumSizeHint() const
+{
+ Q_D(const QGroupBox);
+ QStyleOptionGroupBox option;
+ initStyleOption(&option);
+
+ QFontMetrics metrics(fontMetrics());
+
+ int baseWidth = metrics.width(d->title) + metrics.width(QLatin1Char(' '));
+ int baseHeight = metrics.height();
+ if (d->checkable) {
+ baseWidth += style()->pixelMetric(QStyle::PM_IndicatorWidth);
+ baseWidth += style()->pixelMetric(QStyle::PM_CheckBoxLabelSpacing);
+ baseHeight = qMax(baseHeight, style()->pixelMetric(QStyle::PM_IndicatorHeight));
+ }
+
+ QSize size = style()->sizeFromContents(QStyle::CT_GroupBox, &option, QSize(baseWidth, baseHeight), this);
+ return size.expandedTo(QWidget::minimumSizeHint());
+}
+
+/*!
+ \property QGroupBox::flat
+ \brief whether the group box is painted flat or has a frame
+
+ A group box usually consists of a surrounding frame with a title
+ at the top. If this property is enabled, only the top part of the frame is
+ drawn in most styles; otherwise the whole frame is drawn.
+
+ By default, this property is disabled; i.e. group boxes are not flat unless
+ explicitly specified.
+
+ \bold{Note:} In some styles, flat and non-flat group boxes have similar
+ representations and may not be as distinguishable as they are in other
+ styles.
+
+ \sa title
+*/
+bool QGroupBox::isFlat() const
+{
+ Q_D(const QGroupBox);
+ return d->flat;
+}
+
+void QGroupBox::setFlat(bool b)
+{
+ Q_D(QGroupBox);
+ if (d->flat == b)
+ return;
+ d->flat = b;
+ updateGeometry();
+ update();
+}
+
+
+/*!
+ \property QGroupBox::checkable
+ \brief whether the group box has a checkbox in its title
+
+ If this property is true, the group box displays its title using
+ a checkbox in place of an ordinary label. If the checkbox is checked,
+ the group box's children are enabled; otherwise they are disabled and
+ inaccessible.
+
+ By default, group boxes are not checkable.
+
+ If this property is enabled for a group box, it will also be initially
+ checked to ensure that its contents are enabled.
+
+ \sa checked
+*/
+void QGroupBox::setCheckable(bool checkable)
+{
+ Q_D(QGroupBox);
+
+ bool wasCheckable = d->checkable;
+ d->checkable = checkable;
+
+ if (checkable) {
+ setChecked(true);
+ if (!wasCheckable) {
+ setFocusPolicy(Qt::StrongFocus);
+ d->_q_setChildrenEnabled(true);
+ updateGeometry();
+ }
+ } else {
+ if (wasCheckable) {
+ setFocusPolicy(Qt::NoFocus);
+ d->_q_setChildrenEnabled(true);
+ updateGeometry();
+ }
+ d->_q_setChildrenEnabled(true);
+ }
+
+ if (wasCheckable != checkable) {
+ d->calculateFrame();
+ update();
+ }
+}
+
+bool QGroupBox::isCheckable() const
+{
+ Q_D(const QGroupBox);
+ return d->checkable;
+}
+
+
+bool QGroupBox::isChecked() const
+{
+ Q_D(const QGroupBox);
+ return d->checkable && d->checked;
+}
+
+
+/*!
+ \fn void QGroupBox::toggled(bool on)
+
+ If the group box is checkable, this signal is emitted when the check box
+ is toggled. \a on is true if the check box is checked; otherwise it is false.
+
+ \sa checkable
+*/
+
+
+/*!
+ \fn void QGroupBox::clicked(bool checked)
+ \since 4.2
+
+ This signal is emitted when the check box is activated (i.e. pressed down
+ then released while the mouse cursor is inside the button), or when the
+ shortcut key is typed, Notably, this signal is \e not emitted if you call
+ setChecked().
+
+ If the check box is checked \a checked is true; it is false if the check
+ box is unchecked.
+
+ \sa checkable, toggled(), checked
+*/
+
+/*!
+ \property QGroupBox::checked
+ \brief whether the group box is checked
+
+ If the group box is checkable, it is displayed with a check box.
+ If the check box is checked, the group box's children are enabled;
+ otherwise the children are disabled and are inaccessible to the user.
+
+ By default, checkable group boxes are also checked.
+
+ \sa checkable
+*/
+void QGroupBox::setChecked(bool b)
+{
+ Q_D(QGroupBox);
+ if (d->checkable) {
+ if (d->checked != b)
+ update();
+ bool wasToggled = (b != d->checked);
+ d->checked = b;
+ if (wasToggled) {
+ d->_q_setChildrenEnabled(b);
+ emit toggled(b);
+ }
+ }
+}
+
+/*
+ sets all children of the group box except the qt_groupbox_checkbox
+ to either disabled/enabled
+*/
+void QGroupBoxPrivate::_q_setChildrenEnabled(bool b)
+{
+ Q_Q(QGroupBox);
+ QObjectList childList = q->children();
+ for (int i = 0; i < childList.size(); ++i) {
+ QObject *o = childList.at(i);
+ if (o->isWidgetType()) {
+ QWidget *w = static_cast<QWidget *>(o);
+ if (b) {
+ if (!w->testAttribute(Qt::WA_ForceDisabled))
+ w->setEnabled(true);
+ } else {
+ if (w->isEnabled()) {
+ w->setEnabled(false);
+ w->setAttribute(Qt::WA_ForceDisabled, false);
+ }
+ }
+ }
+ }
+}
+
+/*! \reimp */
+void QGroupBox::changeEvent(QEvent *ev)
+{
+ Q_D(QGroupBox);
+ if (ev->type() == QEvent::EnabledChange) {
+ if (d->checkable && isEnabled()) {
+ // we are being enabled - disable children
+ if (!d->checked)
+ d->_q_setChildrenEnabled(false);
+ }
+ } else if (ev->type() == QEvent::FontChange
+#ifdef Q_WS_MAC
+ || ev->type() == QEvent::MacSizeChange
+#endif
+ || ev->type() == QEvent::StyleChange) {
+ d->calculateFrame();
+ }
+ QWidget::changeEvent(ev);
+}
+
+/*! \reimp */
+void QGroupBox::mousePressEvent(QMouseEvent *event)
+{
+ if (event->button() != Qt::LeftButton) {
+ event->ignore();
+ return;
+ }
+
+ Q_D(QGroupBox);
+ QStyleOptionGroupBox box;
+ initStyleOption(&box);
+ d->pressedControl = style()->hitTestComplexControl(QStyle::CC_GroupBox, &box,
+ event->pos(), this);
+ if (d->checkable && (d->pressedControl & (QStyle::SC_GroupBoxCheckBox | QStyle::SC_GroupBoxLabel))) {
+ d->overCheckBox = true;
+ update(style()->subControlRect(QStyle::CC_GroupBox, &box, QStyle::SC_GroupBoxCheckBox, this));
+ }
+}
+
+/*! \reimp */
+void QGroupBox::mouseMoveEvent(QMouseEvent *event)
+{
+ Q_D(QGroupBox);
+ QStyleOptionGroupBox box;
+ initStyleOption(&box);
+ QStyle::SubControl pressed = style()->hitTestComplexControl(QStyle::CC_GroupBox, &box,
+ event->pos(), this);
+ bool oldOverCheckBox = d->overCheckBox;
+ d->overCheckBox = (pressed == QStyle::SC_GroupBoxCheckBox || pressed == QStyle::SC_GroupBoxLabel);
+ if (d->checkable && (d->pressedControl == QStyle::SC_GroupBoxCheckBox || d->pressedControl == QStyle::SC_GroupBoxLabel)
+ && (d->overCheckBox != oldOverCheckBox))
+ update(style()->subControlRect(QStyle::CC_GroupBox, &box, QStyle::SC_GroupBoxCheckBox, this));
+}
+
+/*! \reimp */
+void QGroupBox::mouseReleaseEvent(QMouseEvent *event)
+{
+ if (event->button() != Qt::LeftButton) {
+ event->ignore();
+ return;
+ }
+
+ Q_D(QGroupBox);
+ QStyleOptionGroupBox box;
+ initStyleOption(&box);
+ QStyle::SubControl released = style()->hitTestComplexControl(QStyle::CC_GroupBox, &box,
+ event->pos(), this);
+ bool toggle = d->checkable && (released == QStyle::SC_GroupBoxLabel
+ || released == QStyle::SC_GroupBoxCheckBox);
+ d->pressedControl = QStyle::SC_None;
+ d->overCheckBox = false;
+ if (toggle)
+ d->click();
+ else if (d->checkable)
+ update(style()->subControlRect(QStyle::CC_GroupBox, &box, QStyle::SC_GroupBoxCheckBox, this));
+}
+
+#ifdef QT3_SUPPORT
+/*!
+ Use one of the constructors that doesn't take the \a name
+ argument and then use setObjectName() instead.
+*/
+QGroupBox::QGroupBox(QWidget *parent, const char *name)
+ : QWidget(*new QGroupBoxPrivate, parent, 0)
+{
+ Q_D(QGroupBox);
+ setObjectName(QString::fromAscii(name));
+ d->init();
+}
+
+/*!
+ Use one of the constructors that doesn't take the \a name
+ argument and then use setObjectName() instead.
+*/
+QGroupBox::QGroupBox(const QString &title, QWidget *parent, const char *name)
+ : QWidget(*new QGroupBoxPrivate, parent, 0)
+{
+ Q_D(QGroupBox);
+ setObjectName(QString::fromAscii(name));
+ d->init();
+ setTitle(title);
+}
+#endif // QT3_SUPPORT
+
+QT_END_NAMESPACE
+
+#include "moc_qgroupbox.cpp"
+
+#endif //QT_NO_GROUPBOX
diff --git a/src/gui/widgets/qgroupbox.h b/src/gui/widgets/qgroupbox.h
new file mode 100644
index 0000000000..85f1a0d7a1
--- /dev/null
+++ b/src/gui/widgets/qgroupbox.h
@@ -0,0 +1,122 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QGROUPBOX_H
+#define QGROUPBOX_H
+
+#include <QtGui/qframe.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+#ifndef QT_NO_GROUPBOX
+
+class QGroupBoxPrivate;
+class QStyleOptionGroupBox;
+class Q_GUI_EXPORT QGroupBox : public QWidget
+{
+ Q_OBJECT
+
+ Q_PROPERTY(QString title READ title WRITE setTitle)
+ Q_PROPERTY(Qt::Alignment alignment READ alignment WRITE setAlignment)
+ Q_PROPERTY(bool flat READ isFlat WRITE setFlat)
+ Q_PROPERTY(bool checkable READ isCheckable WRITE setCheckable)
+ Q_PROPERTY(bool checked READ isChecked WRITE setChecked DESIGNABLE isCheckable NOTIFY toggled USER true)
+public:
+ explicit QGroupBox(QWidget* parent=0);
+ explicit QGroupBox(const QString &title, QWidget* parent=0);
+ ~QGroupBox();
+
+ QString title() const;
+ void setTitle(const QString &title);
+
+ Qt::Alignment alignment() const;
+ void setAlignment(int alignment);
+
+ QSize minimumSizeHint() const;
+
+ bool isFlat() const;
+ void setFlat(bool flat);
+ bool isCheckable() const;
+ void setCheckable(bool checkable);
+ bool isChecked() const;
+
+public Q_SLOTS:
+ void setChecked(bool checked);
+
+Q_SIGNALS:
+ void clicked(bool checked = false);
+ void toggled(bool);
+
+protected:
+ bool event(QEvent *event);
+ void childEvent(QChildEvent *event);
+ void resizeEvent(QResizeEvent *event);
+ void paintEvent(QPaintEvent *event);
+ void focusInEvent(QFocusEvent *event);
+ void changeEvent(QEvent *event);
+ void mousePressEvent(QMouseEvent *event);
+ void mouseMoveEvent(QMouseEvent *event);
+ void mouseReleaseEvent(QMouseEvent *event);
+ void initStyleOption(QStyleOptionGroupBox *option) const;
+
+#ifdef QT3_SUPPORT
+public:
+ QT3_SUPPORT_CONSTRUCTOR QGroupBox(QWidget* parent, const char* name);
+ QT3_SUPPORT_CONSTRUCTOR QGroupBox(const QString &title, QWidget* parent, const char* name);
+#endif
+
+private:
+ Q_DISABLE_COPY(QGroupBox)
+ Q_DECLARE_PRIVATE(QGroupBox)
+ Q_PRIVATE_SLOT(d_func(), void _q_setChildrenEnabled(bool b))
+};
+
+#endif // QT_NO_GROUPBOX
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QGROUPBOX_H
diff --git a/src/gui/widgets/qlabel.cpp b/src/gui/widgets/qlabel.cpp
new file mode 100644
index 0000000000..63c1315c37
--- /dev/null
+++ b/src/gui/widgets/qlabel.cpp
@@ -0,0 +1,1606 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qpainter.h"
+#include "qevent.h"
+#include "qdrawutil.h"
+#include "qapplication.h"
+#include "qabstractbutton.h"
+#include "qstyle.h"
+#include "qstyleoption.h"
+#include <limits.h>
+#include "qaction.h"
+#include "qclipboard.h"
+#include <qdebug.h>
+#include <qurl.h>
+#include "qlabel_p.h"
+#include "private/qstylesheetstyle_p.h"
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QLabel
+ \brief The QLabel widget provides a text or image display.
+
+ \ingroup basicwidgets
+ \ingroup text
+ \mainclass
+
+ QLabel is used for displaying text or an image. No user
+ interaction functionality is provided. The visual appearance of
+ the label can be configured in various ways, and it can be used
+ for specifying a focus mnemonic key for another widget.
+
+ A QLabel can contain any of the following content types:
+
+ \table
+ \header \o Content \o Setting
+ \row \o Plain text
+ \o Pass a QString to setText().
+ \row \o Rich text
+ \o Pass a QString that contains rich text to setText().
+ \row \o A pixmap
+ \o Pass a QPixmap to setPixmap().
+ \row \o A movie
+ \o Pass a QMovie to setMovie().
+ \row \o A number
+ \o Pass an \e int or a \e double to setNum(), which converts
+ the number to plain text.
+ \row \o Nothing
+ \o The same as an empty plain text. This is the default. Set
+ by clear().
+ \endtable
+
+ When the content is changed using any of these functions, any
+ previous content is cleared.
+
+ By default, labels display \l{alignment}{left-aligned, vertically-centered}
+ text and images, where any tabs in the text to be displayed are
+ \l{Qt::TextExpandTabs}{automatically expanded}. However, the look
+ of a QLabel can be adjusted and fine-tuned in several ways.
+
+ The positioning of the content within the QLabel widget area can
+ be tuned with setAlignment() and setIndent(). Text content can
+ also wrap lines along word boundaries with setWordWrap(). For
+ example, this code sets up a sunken panel with a two-line text in
+ the bottom right corner (both lines being flush with the right
+ side of the label):
+
+ \snippet doc/src/snippets/code/src_gui_widgets_qlabel.cpp 0
+
+ The properties and functions QLabel inherits from QFrame can also
+ be used to specify the widget frame to be used for any given label.
+
+ A QLabel is often used as a label for an interactive widget. For
+ this use QLabel provides a useful mechanism for adding an
+ mnemonic (see QKeySequence) that will set the keyboard focus to
+ the other widget (called the QLabel's "buddy"). For example:
+
+ \snippet doc/src/snippets/code/src_gui_widgets_qlabel.cpp 1
+
+ In this example, keyboard focus is transferred to the label's
+ buddy (the QLineEdit) when the user presses Alt+P. If the buddy
+ was a button (inheriting from QAbstractButton), triggering the
+ mnemonic would emulate a button click.
+
+ \table 100%
+ \row
+ \o \inlineimage macintosh-label.png Screenshot of a Macintosh style label
+ \o A label shown in the \l{Macintosh Style Widget Gallery}{Macintosh widget style}.
+ \row
+ \o \inlineimage plastique-label.png Screenshot of a Plastique style label
+ \o A label shown in the \l{Plastique Style Widget Gallery}{Plastique widget style}.
+ \row
+ \o \inlineimage windowsxp-label.png Screenshot of a Windows XP style label
+ \o A label shown in the \l{Windows XP Style Widget Gallery}{Windows XP widget style}.
+ \endtable
+
+ \sa QLineEdit, QTextEdit, QPixmap, QMovie,
+ {fowler}{GUI Design Handbook: Label}
+*/
+
+#ifndef QT_NO_PICTURE
+/*!
+ Returns the label's picture or 0 if the label doesn't have a
+ picture.
+*/
+
+const QPicture *QLabel::picture() const
+{
+ Q_D(const QLabel);
+ return d->picture;
+}
+#endif
+
+
+/*!
+ Constructs an empty label.
+
+ The \a parent and widget flag \a f, arguments are passed
+ to the QFrame constructor.
+
+ \sa setAlignment(), setFrameStyle(), setIndent()
+*/
+QLabel::QLabel(QWidget *parent, Qt::WindowFlags f)
+ : QFrame(*new QLabelPrivate(), parent, f)
+{
+ Q_D(QLabel);
+ d->init();
+}
+
+/*!
+ Constructs a label that displays the text, \a text.
+
+ The \a parent and widget flag \a f, arguments are passed
+ to the QFrame constructor.
+
+ \sa setText(), setAlignment(), setFrameStyle(), setIndent()
+*/
+QLabel::QLabel(const QString &text, QWidget *parent, Qt::WindowFlags f)
+ : QFrame(*new QLabelPrivate(), parent, f)
+{
+ Q_D(QLabel);
+ d->init();
+ setText(text);
+}
+
+
+#ifdef QT3_SUPPORT
+/*! \obsolete
+ Constructs an empty label.
+
+ The \a parent, \a name and widget flag \a f, arguments are passed
+ to the QFrame constructor.
+
+ \sa setAlignment(), setFrameStyle(), setIndent()
+*/
+
+QLabel::QLabel(QWidget *parent, const char *name, Qt::WindowFlags f)
+ : QFrame(*new QLabelPrivate(), parent, f)
+{
+ Q_D(QLabel);
+ if (name)
+ setObjectName(QString::fromAscii(name));
+ d->init();
+}
+
+
+/*! \obsolete
+ Constructs a label that displays the text, \a text.
+
+ The \a parent, \a name and widget flag \a f, arguments are passed
+ to the QFrame constructor.
+
+ \sa setText(), setAlignment(), setFrameStyle(), setIndent()
+*/
+
+QLabel::QLabel(const QString &text, QWidget *parent, const char *name,
+ Qt::WindowFlags f)
+ : QFrame(*new QLabelPrivate(), parent, f)
+{
+ Q_D(QLabel);
+ if (name)
+ setObjectName(QString::fromAscii(name));
+ d->init();
+ setText(text);
+}
+
+
+/*! \obsolete
+ Constructs a label that displays the text \a text. The label has a
+ buddy widget, \a buddy.
+
+ If the \a text contains an underlined letter (a letter preceded by
+ an ampersand, \&), when the user presses Alt+ the underlined letter,
+ focus is passed to the buddy widget.
+
+ The \a parent, \a name and widget flag, \a f, arguments are passed
+ to the QFrame constructor.
+
+ \sa setText(), setBuddy(), setAlignment(), setFrameStyle(),
+ setIndent()
+*/
+QLabel::QLabel(QWidget *buddy, const QString &text,
+ QWidget *parent, const char *name, Qt::WindowFlags f)
+ : QFrame(*new QLabelPrivate(), parent, f)
+{
+ Q_D(QLabel);
+ if (name)
+ setObjectName(QString::fromAscii(name));
+ d->init();
+#ifndef QT_NO_SHORTCUT
+ setBuddy(buddy);
+#endif
+ setText(text);
+}
+#endif //QT3_SUPPORT
+
+/*!
+ Destroys the label.
+*/
+
+QLabel::~QLabel()
+{
+ Q_D(QLabel);
+ d->clearContents();
+}
+
+void QLabelPrivate::init()
+{
+ Q_Q(QLabel);
+
+ valid_hints = false;
+ margin = 0;
+#ifndef QT_NO_MOVIE
+ movie = 0;
+#endif
+#ifndef QT_NO_SHORTCUT
+ shortcutId = 0;
+#endif
+ pixmap = 0;
+ scaledpixmap = 0;
+ cachedimage = 0;
+#ifndef QT_NO_PICTURE
+ picture = 0;
+#endif
+ align = Qt::AlignLeft | Qt::AlignVCenter | Qt::TextExpandTabs;
+ indent = -1;
+ scaledcontents = false;
+ textLayoutDirty = false;
+ textDirty = false;
+ textformat = Qt::AutoText;
+ control = 0;
+ textInteractionFlags = Qt::LinksAccessibleByMouse;
+ isRichText = false;
+ isTextLabel = false;
+
+ q->setSizePolicy(QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred,
+ QSizePolicy::Label));
+
+#ifndef QT_NO_CURSOR
+ validCursor = false;
+ onAnchor = false;
+#endif
+
+ openExternalLinks = false;
+
+ setLayoutItemMargins(QStyle::SE_LabelLayoutItem);
+}
+
+
+/*!
+ \property QLabel::text
+ \brief the label's text
+
+ If no text has been set this will return an empty string. Setting
+ the text clears any previous content.
+
+ The text will be interpreted either as plain text or as rich
+ text, depending on the text format setting; see setTextFormat().
+ The default setting is Qt::AutoText; i.e. QLabel will try to
+ auto-detect the format of the text set.
+
+ If a buddy has been set, the buddy mnemonic key is updated
+ from the new text.
+
+ Note that QLabel is well-suited to display small rich text
+ documents, such as small documents that get their document
+ specific settings (font, text color, link color) from the label's
+ palette and font properties. For large documents, use QTextEdit
+ in read-only mode instead. QTextEdit can also provide a scroll bar
+ when necessary.
+
+ \note This function enables mouse tracking if \a text contains rich
+ text.
+
+ \sa setTextFormat(), setBuddy(), alignment
+*/
+
+void QLabel::setText(const QString &text)
+{
+ Q_D(QLabel);
+ if (d->text == text)
+ return;
+
+ QTextControl *oldControl = d->control;
+ d->control = 0;
+
+ d->clearContents();
+ d->text = text;
+ d->isTextLabel = true;
+ d->textDirty = true;
+ d->isRichText = d->textformat == Qt::RichText
+ || (d->textformat == Qt::AutoText && Qt::mightBeRichText(d->text));
+
+ d->control = oldControl;
+
+ if (d->needTextControl()) {
+ d->ensureTextControl();
+ } else {
+ delete d->control;
+ d->control = 0;
+ }
+
+ if (d->isRichText) {
+ setMouseTracking(true);
+ } else {
+ // Note: mouse tracking not disabled intentionally
+ }
+
+#ifndef QT_NO_SHORTCUT
+ if (d->buddy)
+ d->updateShortcut();
+#endif
+
+ d->updateLabel();
+}
+
+QString QLabel::text() const
+{
+ Q_D(const QLabel);
+ return d->text;
+}
+
+/*!
+ Clears any label contents.
+*/
+
+void QLabel::clear()
+{
+ Q_D(QLabel);
+ d->clearContents();
+ d->updateLabel();
+}
+
+/*!
+ \property QLabel::pixmap
+ \brief the label's pixmap
+
+ If no pixmap has been set this will return 0.
+
+ Setting the pixmap clears any previous content. The buddy
+ shortcut, if any, is disabled.
+*/
+void QLabel::setPixmap(const QPixmap &pixmap)
+{
+ Q_D(QLabel);
+ if (!d->pixmap || d->pixmap->cacheKey() != pixmap.cacheKey()) {
+ d->clearContents();
+ d->pixmap = new QPixmap(pixmap);
+ }
+
+ if (d->pixmap->depth() == 1 && !d->pixmap->mask())
+ d->pixmap->setMask(*((QBitmap *)d->pixmap));
+
+ d->updateLabel();
+}
+
+const QPixmap *QLabel::pixmap() const
+{
+ Q_D(const QLabel);
+ return d->pixmap;
+}
+
+#ifndef QT_NO_PICTURE
+/*!
+ Sets the label contents to \a picture. Any previous content is
+ cleared.
+
+ The buddy shortcut, if any, is disabled.
+
+ \sa picture(), setBuddy()
+*/
+
+void QLabel::setPicture(const QPicture &picture)
+{
+ Q_D(QLabel);
+ d->clearContents();
+ d->picture = new QPicture(picture);
+
+ d->updateLabel();
+}
+#endif // QT_NO_PICTURE
+
+/*!
+ Sets the label contents to plain text containing the textual
+ representation of integer \a num. Any previous content is cleared.
+ Does nothing if the integer's string representation is the same as
+ the current contents of the label.
+
+ The buddy shortcut, if any, is disabled.
+
+ \sa setText(), QString::setNum(), setBuddy()
+*/
+
+void QLabel::setNum(int num)
+{
+ QString str;
+ str.setNum(num);
+ setText(str);
+}
+
+/*!
+ \overload
+
+ Sets the label contents to plain text containing the textual
+ representation of double \a num. Any previous content is cleared.
+ Does nothing if the double's string representation is the same as
+ the current contents of the label.
+
+ The buddy shortcut, if any, is disabled.
+
+ \sa setText(), QString::setNum(), setBuddy()
+*/
+
+void QLabel::setNum(double num)
+{
+ QString str;
+ str.setNum(num);
+ setText(str);
+}
+
+/*!
+ \property QLabel::alignment
+ \brief the alignment of the label's contents
+
+ By default, the contents of the label are left-aligned and vertically-centered.
+
+ \sa text
+*/
+
+void QLabel::setAlignment(Qt::Alignment alignment)
+{
+ Q_D(QLabel);
+ if (alignment == (d->align & (Qt::AlignVertical_Mask|Qt::AlignHorizontal_Mask)))
+ return;
+ d->align = (d->align & ~(Qt::AlignVertical_Mask|Qt::AlignHorizontal_Mask))
+ | (alignment & (Qt::AlignVertical_Mask|Qt::AlignHorizontal_Mask));
+
+ d->updateLabel();
+}
+
+#ifdef QT3_SUPPORT
+/*!
+ Use setAlignment(Qt::Alignment) instead.
+
+ If \a alignment specifies text flags as well, use setTextFormat()
+ to set those.
+*/
+void QLabel::setAlignment(int alignment)
+{
+ Q_D(QLabel);
+ d->align = alignment & ~(Qt::AlignVertical_Mask|Qt::AlignHorizontal_Mask|Qt::TextWordWrap);
+ setAlignment(Qt::Alignment(QFlag(alignment)));
+}
+#endif
+
+Qt::Alignment QLabel::alignment() const
+{
+ Q_D(const QLabel);
+ return QFlag(d->align & (Qt::AlignVertical_Mask|Qt::AlignHorizontal_Mask));
+}
+
+
+/*!
+ \property QLabel::wordWrap
+ \brief the label's word-wrapping policy
+
+ If this property is true then label text is wrapped where
+ necessary at word-breaks; otherwise it is not wrapped at all.
+
+ By default, word wrap is disabled.
+
+ \sa text
+*/
+void QLabel::setWordWrap(bool on)
+{
+ Q_D(QLabel);
+ if (on)
+ d->align |= Qt::TextWordWrap;
+ else
+ d->align &= ~Qt::TextWordWrap;
+
+ d->updateLabel();
+}
+
+bool QLabel::wordWrap() const
+{
+ Q_D(const QLabel);
+ return d->align & Qt::TextWordWrap;
+}
+
+/*!
+ \property QLabel::indent
+ \brief the label's text indent in pixels
+
+ If a label displays text, the indent applies to the left edge if
+ alignment() is Qt::AlignLeft, to the right edge if alignment() is
+ Qt::AlignRight, to the top edge if alignment() is Qt::AlignTop, and
+ to to the bottom edge if alignment() is Qt::AlignBottom.
+
+ If indent is negative, or if no indent has been set, the label
+ computes the effective indent as follows: If frameWidth() is 0,
+ the effective indent becomes 0. If frameWidth() is greater than 0,
+ the effective indent becomes half the width of the "x" character
+ of the widget's current font().
+
+ By default, the indent is -1, meaning that an effective indent is
+ calculating in the manner described above.
+
+ \sa alignment, margin, frameWidth(), font()
+*/
+
+void QLabel::setIndent(int indent)
+{
+ Q_D(QLabel);
+ d->indent = indent;
+ d->updateLabel();
+}
+
+int QLabel::indent() const
+{
+ Q_D(const QLabel);
+ return d->indent;
+}
+
+
+/*!
+ \property QLabel::margin
+ \brief the width of the margin
+
+ The margin is the distance between the innermost pixel of the
+ frame and the outermost pixel of contents.
+
+ The default margin is 0.
+
+ \sa indent
+*/
+int QLabel::margin() const
+{
+ Q_D(const QLabel);
+ return d->margin;
+}
+
+void QLabel::setMargin(int margin)
+{
+ Q_D(QLabel);
+ if (d->margin == margin)
+ return;
+ d->margin = margin;
+ d->updateLabel();
+}
+
+/*!
+ Returns the size that will be used if the width of the label is \a
+ w. If \a w is -1, the sizeHint() is returned. If \a w is 0 minimumSizeHint() is returned
+*/
+QSize QLabelPrivate::sizeForWidth(int w) const
+{
+ Q_Q(const QLabel);
+ if(q->minimumWidth() > 0)
+ w = qMax(w, q->minimumWidth());
+ QSize contentsMargin(leftmargin + rightmargin, topmargin + bottommargin);
+
+ QRect br;
+
+ int hextra = 2 * margin;
+ int vextra = hextra;
+ QFontMetrics fm = q->fontMetrics();
+
+ if (pixmap && !pixmap->isNull())
+ br = pixmap->rect();
+#ifndef QT_NO_PICTURE
+ else if (picture && !picture->isNull())
+ br = picture->boundingRect();
+#endif
+#ifndef QT_NO_MOVIE
+ else if (movie && !movie->currentPixmap().isNull())
+ br = movie->currentPixmap().rect();
+#endif
+ else if (isTextLabel) {
+ int align = QStyle::visualAlignment(q->layoutDirection(), QFlag(this->align));
+ // Add indentation
+ int m = indent;
+
+ if (m < 0 && q->frameWidth()) // no indent, but we do have a frame
+ m = fm.width(QLatin1Char('x')) - margin*2;
+ if (m > 0) {
+ if ((align & Qt::AlignLeft) || (align & Qt::AlignRight))
+ hextra += m;
+ if ((align & Qt::AlignTop) || (align & Qt::AlignBottom))
+ vextra += m;
+ }
+
+ if (control) {
+ ensureTextLayouted();
+ const qreal oldTextWidth = control->textWidth();
+ // Calculate the length of document if w is the width
+ if (align & Qt::TextWordWrap) {
+ if (w >= 0) {
+ w = qMax(w-hextra-contentsMargin.width(), 0); // strip margin and indent
+ control->setTextWidth(w);
+ } else {
+ control->adjustSize();
+ }
+ } else {
+ control->setTextWidth(-1);
+ }
+ br = QRect(QPoint(0, 0), control->size().toSize());
+
+ // restore state
+ control->setTextWidth(oldTextWidth);
+ } else {
+ // Turn off center alignment in order to avoid rounding errors for centering,
+ // since centering involves a division by 2. At the end, all we want is the size.
+ int flags = align & ~(Qt::AlignVCenter | Qt::AlignHCenter);
+ if (hasShortcut) {
+ flags |= Qt::TextShowMnemonic;
+ QStyleOption opt;
+ opt.initFrom(q);
+ if (!q->style()->styleHint(QStyle::SH_UnderlineShortcut, &opt, q))
+ flags |= Qt::TextHideMnemonic;
+ }
+
+ bool tryWidth = (w < 0) && (align & Qt::TextWordWrap);
+ if (tryWidth)
+ w = fm.averageCharWidth() * 80;
+ else if (w < 0)
+ w = 2000;
+ w -= (hextra + contentsMargin.width());
+ br = fm.boundingRect(0, 0, w ,2000, flags, text);
+ if (tryWidth && br.height() < 4*fm.lineSpacing() && br.width() > w/2)
+ br = fm.boundingRect(0, 0, w/2, 2000, flags, text);
+ if (tryWidth && br.height() < 2*fm.lineSpacing() && br.width() > w/4)
+ br = fm.boundingRect(0, 0, w/4, 2000, flags, text);
+ }
+ } else {
+ br = QRect(QPoint(0, 0), QSize(fm.averageCharWidth(), fm.lineSpacing()));
+ }
+
+ const QSize contentsSize(br.width() + hextra, br.height() + vextra);
+ return (contentsSize + contentsMargin).expandedTo(q->minimumSize());
+}
+
+
+/*!
+ \reimp
+*/
+
+int QLabel::heightForWidth(int w) const
+{
+ Q_D(const QLabel);
+ if (d->isTextLabel)
+ return d->sizeForWidth(w).height();
+ return QWidget::heightForWidth(w);
+}
+
+/*!
+ \property QLabel::openExternalLinks
+ \since 4.2
+
+ Specifies whether QLabel should automatically open links using
+ QDesktopServices::openUrl() instead of emitting the
+ linkActivated() signal.
+
+ \bold{Note:} The textInteractionFlags set on the label need to include
+ either LinksAccessibleByMouse or LinksAccessibleByKeyboard.
+
+ The default value is false.
+
+ \sa textInteractionFlags()
+*/
+bool QLabel::openExternalLinks() const
+{
+ Q_D(const QLabel);
+ return d->openExternalLinks;
+}
+
+void QLabel::setOpenExternalLinks(bool open)
+{
+ Q_D(QLabel);
+ d->openExternalLinks = open;
+ if (d->control)
+ d->control->setOpenExternalLinks(open);
+}
+
+/*!
+ \property QLabel::textInteractionFlags
+ \since 4.2
+
+ Specifies how the label should interact with user input if it displays text.
+
+ If the flags contain Qt::LinksAccessibleByKeyboard the focus policy is also
+ automatically set to Qt::StrongFocus. If Qt::TextSelectableByKeyboard is set
+ then the focus policy is set to Qt::ClickFocus.
+
+ The default value is Qt::LinksAccessibleByMouse.
+*/
+void QLabel::setTextInteractionFlags(Qt::TextInteractionFlags flags)
+{
+ Q_D(QLabel);
+ if (d->textInteractionFlags == flags)
+ return;
+ d->textInteractionFlags = flags;
+ if (flags & Qt::LinksAccessibleByKeyboard)
+ setFocusPolicy(Qt::StrongFocus);
+ else if (flags & (Qt::TextSelectableByKeyboard | Qt::TextSelectableByMouse))
+ setFocusPolicy(Qt::ClickFocus);
+ else
+ setFocusPolicy(Qt::NoFocus);
+
+ if (d->needTextControl()) {
+ d->ensureTextControl();
+ } else {
+ delete d->control;
+ d->control = 0;
+ }
+
+ if (d->control)
+ d->control->setTextInteractionFlags(d->textInteractionFlags);
+}
+
+Qt::TextInteractionFlags QLabel::textInteractionFlags() const
+{
+ Q_D(const QLabel);
+ return d->textInteractionFlags;
+}
+
+/*!\reimp
+*/
+QSize QLabel::sizeHint() const
+{
+ Q_D(const QLabel);
+ if (!d->valid_hints)
+ (void) QLabel::minimumSizeHint();
+ return d->sh;
+}
+
+/*!
+ \reimp
+*/
+QSize QLabel::minimumSizeHint() const
+{
+ Q_D(const QLabel);
+ if (d->valid_hints) {
+ if (d->sizePolicy == sizePolicy())
+ return d->msh;
+ }
+
+ ensurePolished();
+ d->valid_hints = true;
+ d->sh = d->sizeForWidth(-1); // wrap ? golden ratio : min doc size
+ QSize msh(-1, -1);
+
+ if (!d->isTextLabel) {
+ msh = d->sh;
+ } else {
+ msh.rheight() = d->sizeForWidth(QWIDGETSIZE_MAX).height(); // height for one line
+ msh.rwidth() = d->sizeForWidth(0).width(); // wrap ? size of biggest word : min doc size
+ if (d->sh.height() < msh.height())
+ msh.rheight() = d->sh.height();
+ }
+ d->msh = msh;
+ d->sizePolicy = sizePolicy();
+ return msh;
+}
+
+/*!\reimp
+*/
+void QLabel::mousePressEvent(QMouseEvent *ev)
+{
+ Q_D(QLabel);
+ d->sendControlEvent(ev);
+}
+
+/*!\reimp
+*/
+void QLabel::mouseMoveEvent(QMouseEvent *ev)
+{
+ Q_D(QLabel);
+ d->sendControlEvent(ev);
+}
+
+/*!\reimp
+*/
+void QLabel::mouseReleaseEvent(QMouseEvent *ev)
+{
+ Q_D(QLabel);
+ d->sendControlEvent(ev);
+}
+
+/*!\reimp
+*/
+void QLabel::contextMenuEvent(QContextMenuEvent *ev)
+{
+#ifdef QT_NO_CONTEXTMENU
+ Q_UNUSED(ev);
+#else
+ Q_D(QLabel);
+ if (!d->isTextLabel) {
+ ev->ignore();
+ return;
+ }
+ QMenu *menu = d->createStandardContextMenu(ev->pos());
+ if (!menu) {
+ ev->ignore();
+ return;
+ }
+ ev->accept();
+ menu->exec(ev->globalPos());
+ delete menu;
+#endif
+}
+
+/*!
+ \reimp
+*/
+void QLabel::focusInEvent(QFocusEvent *ev)
+{
+ Q_D(QLabel);
+ if (d->isTextLabel) {
+ d->ensureTextControl();
+ d->sendControlEvent(ev);
+ }
+ QFrame::focusInEvent(ev);
+}
+
+/*!
+ \reimp
+*/
+void QLabel::focusOutEvent(QFocusEvent *ev)
+{
+ Q_D(QLabel);
+ d->sendControlEvent(ev);
+ QFrame::focusOutEvent(ev);
+}
+
+/*!\reimp
+*/
+bool QLabel::focusNextPrevChild(bool next)
+{
+ Q_D(QLabel);
+ if (d->control && d->control->setFocusToNextOrPreviousAnchor(next))
+ return true;
+ return QFrame::focusNextPrevChild(next);
+}
+
+/*!\reimp
+*/
+void QLabel::keyPressEvent(QKeyEvent *ev)
+{
+ Q_D(QLabel);
+ d->sendControlEvent(ev);
+}
+
+/*!\reimp
+*/
+bool QLabel::event(QEvent *e)
+{
+ Q_D(QLabel);
+ QEvent::Type type = e->type();
+
+#ifndef QT_NO_SHORTCUT
+ if (type == QEvent::Shortcut) {
+ QShortcutEvent *se = static_cast<QShortcutEvent *>(e);
+ if (se->shortcutId() == d->shortcutId) {
+ QWidget * w = d->buddy;
+ QAbstractButton *button = qobject_cast<QAbstractButton *>(w);
+ if (w->focusPolicy() != Qt::NoFocus)
+ w->setFocus(Qt::ShortcutFocusReason);
+ if (button && !se->isAmbiguous())
+ button->animateClick();
+ else
+ window()->setAttribute(Qt::WA_KeyboardFocusChange);
+ return true;
+ }
+ } else
+#endif
+ if (type == QEvent::Resize) {
+ if (d->control)
+ d->textLayoutDirty = true;
+ } else if (e->type() == QEvent::StyleChange
+#ifdef Q_WS_MAC
+ || e->type() == QEvent::MacSizeChange
+#endif
+ ) {
+ d->setLayoutItemMargins(QStyle::SE_LabelLayoutItem);
+ d->updateLabel();
+ }
+
+ return QFrame::event(e);
+}
+
+/*!\reimp
+*/
+void QLabel::paintEvent(QPaintEvent *)
+{
+ Q_D(QLabel);
+ QStyle *style = QWidget::style();
+ QPainter painter(this);
+ drawFrame(&painter);
+ QRect cr = contentsRect();
+ cr.adjust(d->margin, d->margin, -d->margin, -d->margin);
+ int align = QStyle::visualAlignment(layoutDirection(), QFlag(d->align));
+
+#ifndef QT_NO_MOVIE
+ if (d->movie) {
+ if (d->scaledcontents)
+ style->drawItemPixmap(&painter, cr, align, d->movie->currentPixmap().scaled(cr.size()));
+ else
+ style->drawItemPixmap(&painter, cr, align, d->movie->currentPixmap());
+ }
+ else
+#endif
+ if (d->isTextLabel) {
+ QRectF lr = d->layoutRect();
+ if (d->control) {
+#ifndef QT_NO_SHORTCUT
+ const bool underline = (bool)style->styleHint(QStyle::SH_UnderlineShortcut, 0, this, 0);
+ if (d->shortcutId != 0
+ && underline != d->shortcutCursor.charFormat().fontUnderline()) {
+ QTextCharFormat fmt;
+ fmt.setFontUnderline(underline);
+ d->shortcutCursor.mergeCharFormat(fmt);
+ }
+#endif
+ d->ensureTextLayouted();
+
+ QAbstractTextDocumentLayout::PaintContext context;
+ QStyleOption opt(0);
+ opt.init(this);
+
+ if (!isEnabled() && style->styleHint(QStyle::SH_EtchDisabledText, &opt, this)) {
+ context.palette = palette();
+ context.palette.setColor(QPalette::Text, context.palette.light().color());
+ painter.save();
+ painter.translate(lr.x() + 1, lr.y() + 1);
+ painter.setClipRect(lr.translated(-lr.x() - 1, -lr.y() - 1));
+ QAbstractTextDocumentLayout *layout = d->control->document()->documentLayout();
+ layout->draw(&painter, context);
+ painter.restore();
+ }
+
+ // Adjust the palette
+ context.palette = palette();
+#ifndef QT_NO_STYLE_STYLESHEET
+ if (QStyleSheetStyle* cssStyle = qobject_cast<QStyleSheetStyle*>(style)) {
+ cssStyle->focusPalette(this, &opt, &context.palette);
+ }
+#endif
+
+ if (foregroundRole() != QPalette::Text && isEnabled())
+ context.palette.setColor(QPalette::Text, context.palette.color(foregroundRole()));
+
+ painter.save();
+ painter.translate(lr.topLeft());
+ painter.setClipRect(lr.translated(-lr.x(), -lr.y()));
+ d->control->setPalette(context.palette);
+ d->control->drawContents(&painter, QRectF(), this);
+ painter.restore();
+ } else {
+ int flags = align;
+ if (d->hasShortcut) {
+ flags |= Qt::TextShowMnemonic;
+ QStyleOption opt;
+ opt.initFrom(this);
+ if (!style->styleHint(QStyle::SH_UnderlineShortcut, &opt, this))
+ flags |= Qt::TextHideMnemonic;
+ }
+ style->drawItemText(&painter, lr.toRect(), flags, palette(), isEnabled(), d->text, foregroundRole());
+ }
+ } else
+#ifndef QT_NO_PICTURE
+ if (d->picture) {
+ QRect br = d->picture->boundingRect();
+ int rw = br.width();
+ int rh = br.height();
+ if (d->scaledcontents) {
+ painter.save();
+ painter.translate(cr.x(), cr.y());
+ painter.scale((double)cr.width()/rw, (double)cr.height()/rh);
+ painter.drawPicture(-br.x(), -br.y(), *d->picture);
+ painter.restore();
+ } else {
+ int xo = 0;
+ int yo = 0;
+ if (align & Qt::AlignVCenter)
+ yo = (cr.height()-rh)/2;
+ else if (align & Qt::AlignBottom)
+ yo = cr.height()-rh;
+ if (align & Qt::AlignRight)
+ xo = cr.width()-rw;
+ else if (align & Qt::AlignHCenter)
+ xo = (cr.width()-rw)/2;
+ painter.drawPicture(cr.x()+xo-br.x(), cr.y()+yo-br.y(), *d->picture);
+ }
+ } else
+#endif
+ if (d->pixmap && !d->pixmap->isNull()) {
+ QPixmap pix;
+ if (d->scaledcontents) {
+ if (!d->scaledpixmap || d->scaledpixmap->size() != cr.size()) {
+ if (!d->cachedimage)
+ d->cachedimage = new QImage(d->pixmap->toImage());
+ delete d->scaledpixmap;
+ d->scaledpixmap = new QPixmap(QPixmap::fromImage(d->cachedimage->scaled(cr.size(),Qt::IgnoreAspectRatio,Qt::SmoothTransformation)));
+ }
+ pix = *d->scaledpixmap;
+ } else
+ pix = *d->pixmap;
+ QStyleOption opt;
+ opt.initFrom(this);
+ if (!isEnabled())
+ pix = style->generatedIconPixmap(QIcon::Disabled, pix, &opt);
+ style->drawItemPixmap(&painter, cr, align, pix);
+ }
+}
+
+
+/*!
+ Updates the label, but not the frame.
+*/
+
+void QLabelPrivate::updateLabel()
+{
+ Q_Q(QLabel);
+ valid_hints = false;
+
+ if (isTextLabel) {
+ QSizePolicy policy = q->sizePolicy();
+ const bool wrap = align & Qt::TextWordWrap;
+ policy.setHeightForWidth(wrap);
+ if (policy != q->sizePolicy()) // ### should be replaced by WA_WState_OwnSizePolicy idiom
+ q->setSizePolicy(policy);
+ textLayoutDirty = true;
+ }
+ q->updateGeometry();
+ q->update(q->contentsRect());
+}
+
+#ifndef QT_NO_SHORTCUT
+/*!
+ Sets this label's buddy to \a buddy.
+
+ When the user presses the shortcut key indicated by this label,
+ the keyboard focus is transferred to the label's buddy widget.
+
+ The buddy mechanism is only available for QLabels that contain
+ text in which one character is prefixed with an ampersand, '&'.
+ This character is set as the shortcut key. See the \l
+ QKeySequence::mnemonic() documentation for details (to display an
+ actual ampersand, use '&&').
+
+ In a dialog, you might create two data entry widgets and a label
+ for each, and set up the geometry layout so each label is just to
+ the left of its data entry widget (its "buddy"), for example:
+ \snippet doc/src/snippets/code/src_gui_widgets_qlabel.cpp 2
+
+ With the code above, the focus jumps to the Name field when the
+ user presses Alt+N, and to the Phone field when the user presses
+ Alt+P.
+
+ To unset a previously set buddy, call this function with \a buddy
+ set to 0.
+
+ \sa buddy(), setText(), QShortcut, setAlignment()
+*/
+
+void QLabel::setBuddy(QWidget *buddy)
+{
+ Q_D(QLabel);
+ d->buddy = buddy;
+ if (d->isTextLabel) {
+ if (d->shortcutId)
+ releaseShortcut(d->shortcutId);
+ d->shortcutId = 0;
+ d->textDirty = true;
+ if (buddy)
+ d->updateShortcut(); // grab new shortcut
+ d->updateLabel();
+ }
+}
+
+
+/*!
+ Returns this label's buddy, or 0 if no buddy is currently set.
+
+ \sa setBuddy()
+*/
+
+QWidget * QLabel::buddy() const
+{
+ Q_D(const QLabel);
+ return d->buddy;
+}
+
+void QLabelPrivate::updateShortcut()
+{
+ Q_Q(QLabel);
+ Q_ASSERT(shortcutId == 0);
+ // Introduce an extra boolean to indicate the presence of a shortcut in the
+ // text. We cannot use the shortcutId itself because on the mac mnemonics are
+ // off by default, so QKeySequence::mnemonic always returns an empty sequence.
+ // But then we do want to hide the ampersands, so we can't use shortcutId.
+ hasShortcut = false;
+
+ if (control) {
+ ensureTextPopulated();
+ // Underline the first character that follows an ampersand
+ shortcutCursor = control->document()->find(QLatin1String("&"));
+ if (shortcutCursor.isNull())
+ return;
+ hasShortcut = true;
+ shortcutId = q->grabShortcut(QKeySequence::mnemonic(text));
+ shortcutCursor.deleteChar(); // remove the ampersand
+ shortcutCursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor);
+ } else {
+ if (!text.contains(QLatin1String("&")))
+ return;
+ hasShortcut = true;
+ shortcutId = q->grabShortcut(QKeySequence::mnemonic(text));
+ }
+}
+
+#endif // QT_NO_SHORTCUT
+
+#ifndef QT_NO_MOVIE
+void QLabelPrivate::_q_movieUpdated(const QRect& rect)
+{
+ Q_Q(QLabel);
+ if (movie && movie->isValid()) {
+ QRect r;
+ if (scaledcontents) {
+ QRect cr = q->contentsRect();
+ QRect pixmapRect(cr.topLeft(), movie->currentPixmap().size());
+ if (pixmapRect.isEmpty())
+ return;
+ r.setRect(cr.left(), cr.top(),
+ (rect.width() * cr.width()) / pixmapRect.width(),
+ (rect.height() * cr.height()) / pixmapRect.height());
+ } else {
+ r = q->style()->itemPixmapRect(q->contentsRect(), align, movie->currentPixmap());
+ r.translate(rect.x(), rect.y());
+ r.setWidth(qMin(r.width(), rect.width()));
+ r.setHeight(qMin(r.height(), rect.height()));
+ }
+ q->update(r);
+ }
+}
+
+void QLabelPrivate::_q_movieResized(const QSize& size)
+{
+ Q_Q(QLabel);
+ q->update(); //we need to refresh the whole background in case the new size is smaler
+ valid_hints = false;
+ _q_movieUpdated(QRect(QPoint(0,0), size));
+ q->updateGeometry();
+}
+
+/*!
+ Sets the label contents to \a movie. Any previous content is
+ cleared. The label does NOT take ownership of the movie.
+
+ The buddy shortcut, if any, is disabled.
+
+ \sa movie(), setBuddy()
+*/
+
+void QLabel::setMovie(QMovie *movie)
+{
+ Q_D(QLabel);
+ d->clearContents();
+
+ if (!movie)
+ return;
+
+ d->movie = movie;
+ connect(movie, SIGNAL(resized(QSize)), this, SLOT(_q_movieResized(QSize)));
+ connect(movie, SIGNAL(updated(QRect)), this, SLOT(_q_movieUpdated(QRect)));
+
+ // Assume that if the movie is running,
+ // resize/update signals will come soon enough
+ if (movie->state() != QMovie::Running)
+ d->updateLabel();
+}
+
+#endif // QT_NO_MOVIE
+
+/*!
+ \internal
+
+ Clears any contents, without updating/repainting the label.
+*/
+
+void QLabelPrivate::clearContents()
+{
+ delete control;
+ control = 0;
+ isTextLabel = false;
+ hasShortcut = false;
+
+#ifndef QT_NO_PICTURE
+ delete picture;
+ picture = 0;
+#endif
+ delete scaledpixmap;
+ scaledpixmap = 0;
+ delete cachedimage;
+ cachedimage = 0;
+ delete pixmap;
+ pixmap = 0;
+
+ text.clear();
+ Q_Q(QLabel);
+#ifndef QT_NO_SHORTCUT
+ if (shortcutId)
+ q->releaseShortcut(shortcutId);
+ shortcutId = 0;
+#endif
+#ifndef QT_NO_MOVIE
+ if (movie) {
+ QObject::disconnect(movie, SIGNAL(resized(QSize)), q, SLOT(_q_movieResized(QSize)));
+ QObject::disconnect(movie, SIGNAL(updated(QRect)), q, SLOT(_q_movieUpdated(QRect)));
+ }
+ movie = 0;
+#endif
+#ifndef QT_NO_CURSOR
+ if (onAnchor) {
+ if (validCursor)
+ q->setCursor(cursor);
+ else
+ q->unsetCursor();
+ }
+ validCursor = false;
+ onAnchor = false;
+#endif
+}
+
+
+#ifndef QT_NO_MOVIE
+
+/*!
+ Returns a pointer to the label's movie, or 0 if no movie has been
+ set.
+
+ \sa setMovie()
+*/
+
+QMovie *QLabel::movie() const
+{
+ Q_D(const QLabel);
+ return d->movie;
+}
+
+#endif // QT_NO_MOVIE
+
+/*!
+ \property QLabel::textFormat
+ \brief the label's text format
+
+ See the Qt::TextFormat enum for an explanation of the possible
+ options.
+
+ The default format is Qt::AutoText.
+
+ \sa text()
+*/
+
+Qt::TextFormat QLabel::textFormat() const
+{
+ Q_D(const QLabel);
+ return d->textformat;
+}
+
+void QLabel::setTextFormat(Qt::TextFormat format)
+{
+ Q_D(QLabel);
+ if (format != d->textformat) {
+ d->textformat = format;
+ QString t = d->text;
+ if (!t.isNull()) {
+ d->text.clear();
+ setText(t);
+ }
+ }
+}
+
+/*!
+ \reimp
+*/
+void QLabel::changeEvent(QEvent *ev)
+{
+ Q_D(QLabel);
+ if(ev->type() == QEvent::FontChange || ev->type() == QEvent::ApplicationFontChange) {
+ if (d->isTextLabel) {
+ if (d->control)
+ d->control->document()->setDefaultFont(font());
+ d->updateLabel();
+ }
+ } else if (ev->type() == QEvent::PaletteChange && d->control) {
+ d->control->setPalette(palette());
+ } else if (ev->type() == QEvent::ContentsRectChange) {
+ d->updateLabel();
+ } else if (ev->type() == QEvent::LayoutDirectionChange) {
+ if (d->isTextLabel && d->control) {
+ d->sendControlEvent(ev);
+ }
+ }
+ QFrame::changeEvent(ev);
+}
+
+/*!
+ \property QLabel::scaledContents
+ \brief whether the label will scale its contents to fill all
+ available space.
+
+ When enabled and the label shows a pixmap, it will scale the
+ pixmap to fill the available space.
+
+ This property's default is false.
+*/
+bool QLabel::hasScaledContents() const
+{
+ Q_D(const QLabel);
+ return d->scaledcontents;
+}
+
+void QLabel::setScaledContents(bool enable)
+{
+ Q_D(QLabel);
+ if ((bool)d->scaledcontents == enable)
+ return;
+ d->scaledcontents = enable;
+ if (!enable) {
+ delete d->scaledpixmap;
+ d->scaledpixmap = 0;
+ delete d->cachedimage;
+ d->cachedimage = 0;
+ }
+ update(contentsRect());
+}
+
+
+/*!
+ \fn void QLabel::setAlignment(Qt::AlignmentFlag flag)
+ \internal
+
+ Without this function, a call to e.g. setAlignment(Qt::AlignTop)
+ results in the \c QT3_SUPPORT function setAlignment(int) being called,
+ rather than setAlignment(Qt::Alignment).
+*/
+
+// Returns the rect that is available for us to draw the document
+QRect QLabelPrivate::documentRect() const
+{
+ Q_Q(const QLabel);
+ Q_ASSERT_X(isTextLabel, "documentRect", "document rect called for label that is not a text label!");
+ QRect cr = q->contentsRect();
+ cr.adjust(margin, margin, -margin, -margin);
+ const int align = QStyle::visualAlignment(q->layoutDirection(), QFlag(this->align));
+ int m = indent;
+ if (m < 0 && q->frameWidth()) // no indent, but we do have a frame
+ m = q->fontMetrics().width(QLatin1Char('x')) / 2 - margin;
+ if (m > 0) {
+ if (align & Qt::AlignLeft)
+ cr.setLeft(cr.left() + m);
+ if (align & Qt::AlignRight)
+ cr.setRight(cr.right() - m);
+ if (align & Qt::AlignTop)
+ cr.setTop(cr.top() + m);
+ if (align & Qt::AlignBottom)
+ cr.setBottom(cr.bottom() - m);
+ }
+ return cr;
+}
+
+void QLabelPrivate::ensureTextPopulated() const
+{
+ if (!textDirty)
+ return;
+ if (control) {
+ QTextDocument *doc = control->document();
+ if (textDirty) {
+#ifndef QT_NO_TEXTHTMLPARSER
+ if (isRichText)
+ doc->setHtml(text);
+ else
+ doc->setPlainText(text);
+#else
+ doc->setPlainText(text);
+#endif
+ doc->setUndoRedoEnabled(false);
+ }
+ }
+ textDirty = false;
+}
+
+void QLabelPrivate::ensureTextLayouted() const
+{
+ if (!textLayoutDirty)
+ return;
+ ensureTextPopulated();
+ Q_Q(const QLabel);
+ if (control) {
+ QTextDocument *doc = control->document();
+ QTextOption opt = doc->defaultTextOption();
+
+ opt.setAlignment(QFlag(this->align));
+
+ if (this->align & Qt::TextWordWrap)
+ opt.setWrapMode(QTextOption::WordWrap);
+ else
+ opt.setWrapMode(QTextOption::ManualWrap);
+
+ opt.setTextDirection(q->layoutDirection());
+
+ doc->setDefaultTextOption(opt);
+
+ QTextFrameFormat fmt = doc->rootFrame()->frameFormat();
+ fmt.setMargin(0);
+ doc->rootFrame()->setFrameFormat(fmt);
+ doc->setTextWidth(documentRect().width());
+ }
+ textLayoutDirty = false;
+}
+
+void QLabelPrivate::ensureTextControl() const
+{
+ Q_Q(const QLabel);
+ if (!isTextLabel)
+ return;
+ if (!control) {
+ control = new QTextControl(const_cast<QLabel *>(q));
+ control->document()->setUndoRedoEnabled(false);
+ control->document()->setDefaultFont(q->font());
+ control->setTextInteractionFlags(textInteractionFlags);
+ control->setOpenExternalLinks(openExternalLinks);
+ control->setPalette(q->palette());
+ control->setFocus(q->hasFocus());
+ QObject::connect(control, SIGNAL(updateRequest(QRectF)),
+ q, SLOT(update()));
+ QObject::connect(control, SIGNAL(linkHovered(QString)),
+ q, SLOT(_q_linkHovered(QString)));
+ QObject::connect(control, SIGNAL(linkActivated(QString)),
+ q, SIGNAL(linkActivated(QString)));
+ textLayoutDirty = true;
+ textDirty = true;
+ }
+}
+
+void QLabelPrivate::sendControlEvent(QEvent *e)
+{
+ Q_Q(QLabel);
+ if (!isTextLabel || !control || textInteractionFlags == Qt::NoTextInteraction) {
+ e->ignore();
+ return;
+ }
+ control->processEvent(e, -layoutRect().topLeft(), q);
+}
+
+void QLabelPrivate::_q_linkHovered(const QString &anchor)
+{
+ Q_Q(QLabel);
+#ifndef QT_NO_CURSOR
+ if (anchor.isEmpty()) { // restore cursor
+ if (validCursor)
+ q->setCursor(cursor);
+ else
+ q->unsetCursor();
+ onAnchor = false;
+ } else if (!onAnchor) {
+ validCursor = q->testAttribute(Qt::WA_SetCursor);
+ if (validCursor) {
+ cursor = q->cursor();
+ }
+ q->setCursor(Qt::PointingHandCursor);
+ onAnchor = true;
+ }
+#endif
+ emit q->linkHovered(anchor);
+}
+
+// Return the layout rect - this is the rect that is given to the layout painting code
+// This may be different from the document rect since vertical alignment is not
+// done by the text layout code
+QRectF QLabelPrivate::layoutRect() const
+{
+ QRectF cr = documentRect();
+ if (!control)
+ return cr;
+ ensureTextLayouted();
+ // Caculate y position manually
+ qreal rh = control->document()->documentLayout()->documentSize().height();
+ qreal yo = 0;
+ if (align & Qt::AlignVCenter)
+ yo = qMax((cr.height()-rh)/2, qreal(0));
+ else if (align & Qt::AlignBottom)
+ yo = qMax(cr.height()-rh, qreal(0));
+ return QRectF(cr.x(), yo + cr.y(), cr.width(), cr.height());
+}
+
+// Returns the point in the document rect adjusted with p
+QPoint QLabelPrivate::layoutPoint(const QPoint& p) const
+{
+ QRect lr = layoutRect().toRect();
+ return p - lr.topLeft();
+}
+
+#ifndef QT_NO_CONTEXTMENU
+QMenu *QLabelPrivate::createStandardContextMenu(const QPoint &pos)
+{
+ QString linkToCopy;
+ QPoint p;
+ if (control && isRichText) {
+ p = layoutPoint(pos);
+ linkToCopy = control->document()->documentLayout()->anchorAt(p);
+ }
+
+ if (linkToCopy.isEmpty() && !control)
+ return 0;
+
+ return control->createStandardContextMenu(p, q_func());
+}
+#endif
+
+/*!
+ \fn void QLabel::linkHovered(const QString &link)
+ \since 4.2
+
+ This signal is emitted when the user hovers over a link. The URL
+ referred to by the anchor is passed in \a link.
+
+ \sa linkActivated()
+*/
+
+
+/*!
+ \fn void QLabel::linkActivated(const QString &link)
+ \since 4.2
+
+ This signal is emitted when the user clicks a link. The URL
+ referred to by the anchor is passed in \a link.
+
+ \sa linkHovered()
+*/
+
+QT_END_NAMESPACE
+
+#include "moc_qlabel.cpp"
diff --git a/src/gui/widgets/qlabel.h b/src/gui/widgets/qlabel.h
new file mode 100644
index 0000000000..34f397fdab
--- /dev/null
+++ b/src/gui/widgets/qlabel.h
@@ -0,0 +1,175 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QLABEL_H
+#define QLABEL_H
+
+#include <QtGui/qframe.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+class QLabelPrivate;
+
+class Q_GUI_EXPORT QLabel : public QFrame
+{
+ Q_OBJECT
+ Q_PROPERTY(QString text READ text WRITE setText)
+ Q_PROPERTY(Qt::TextFormat textFormat READ textFormat WRITE setTextFormat)
+ Q_PROPERTY(QPixmap pixmap READ pixmap WRITE setPixmap)
+ Q_PROPERTY(bool scaledContents READ hasScaledContents WRITE setScaledContents)
+ Q_PROPERTY(Qt::Alignment alignment READ alignment WRITE setAlignment)
+ Q_PROPERTY(bool wordWrap READ wordWrap WRITE setWordWrap)
+ Q_PROPERTY(int margin READ margin WRITE setMargin)
+ Q_PROPERTY(int indent READ indent WRITE setIndent)
+ Q_PROPERTY(bool openExternalLinks READ openExternalLinks WRITE setOpenExternalLinks)
+ Q_PROPERTY(Qt::TextInteractionFlags textInteractionFlags READ textInteractionFlags WRITE setTextInteractionFlags)
+
+public:
+ explicit QLabel(QWidget *parent=0, Qt::WindowFlags f=0);
+ explicit QLabel(const QString &text, QWidget *parent=0, Qt::WindowFlags f=0);
+ ~QLabel();
+
+ QString text() const;
+ const QPixmap *pixmap() const;
+#ifndef QT_NO_PICTURE
+ const QPicture *picture() const;
+#endif
+#ifndef QT_NO_MOVIE
+ QMovie *movie() const;
+#endif
+
+ Qt::TextFormat textFormat() const;
+ void setTextFormat(Qt::TextFormat);
+
+ Qt::Alignment alignment() const;
+ void setAlignment(Qt::Alignment);
+
+ void setWordWrap(bool on);
+ bool wordWrap() const;
+
+ int indent() const;
+ void setIndent(int);
+
+ int margin() const;
+ void setMargin(int);
+
+ bool hasScaledContents() const;
+ void setScaledContents(bool);
+ QSize sizeHint() const;
+ QSize minimumSizeHint() const;
+#ifndef QT_NO_SHORTCUT
+ void setBuddy(QWidget *);
+ QWidget *buddy() const;
+#endif
+ int heightForWidth(int) const;
+
+ bool openExternalLinks() const;
+ void setOpenExternalLinks(bool open);
+
+ void setTextInteractionFlags(Qt::TextInteractionFlags flags);
+ Qt::TextInteractionFlags textInteractionFlags() const;
+
+public Q_SLOTS:
+ void setText(const QString &);
+ void setPixmap(const QPixmap &);
+#ifndef QT_NO_PICTURE
+ void setPicture(const QPicture &);
+#endif
+#ifndef QT_NO_MOVIE
+ void setMovie(QMovie *movie);
+#endif
+ void setNum(int);
+ void setNum(double);
+ void clear();
+
+Q_SIGNALS:
+ void linkActivated(const QString& link);
+ void linkHovered(const QString& link);
+
+protected:
+ bool event(QEvent *e);
+ void keyPressEvent(QKeyEvent *ev);
+ void paintEvent(QPaintEvent *);
+ void changeEvent(QEvent *);
+ void mousePressEvent(QMouseEvent *ev);
+ void mouseMoveEvent(QMouseEvent *ev);
+ void mouseReleaseEvent(QMouseEvent *ev);
+ void contextMenuEvent(QContextMenuEvent *ev);
+ void focusInEvent(QFocusEvent *ev);
+ void focusOutEvent(QFocusEvent *ev);
+ bool focusNextPrevChild(bool next);
+
+#ifdef QT3_SUPPORT
+public:
+ QT3_SUPPORT_CONSTRUCTOR QLabel(QWidget *parent, const char* name, Qt::WindowFlags f=0);
+ QT3_SUPPORT_CONSTRUCTOR QLabel(const QString &text, QWidget *parent, const char* name,
+ Qt::WindowFlags f=0);
+ QT3_SUPPORT_CONSTRUCTOR QLabel(QWidget *buddy, const QString &,
+ QWidget *parent=0, const char* name=0, Qt::WindowFlags f=0);
+ QT3_SUPPORT void setAlignment(int alignment);
+
+ // don't mark the next function with QT3_SUPPORT
+ inline void setAlignment(Qt::AlignmentFlag flag) { setAlignment((Qt::Alignment)flag); }
+#endif
+
+private:
+ Q_DISABLE_COPY(QLabel)
+ Q_DECLARE_PRIVATE(QLabel)
+#ifndef QT_NO_MOVIE
+ Q_PRIVATE_SLOT(d_func(), void _q_movieUpdated(const QRect&))
+ Q_PRIVATE_SLOT(d_func(), void _q_movieResized(const QSize&))
+#endif
+ Q_PRIVATE_SLOT(d_func(), void _q_linkHovered(const QString &))
+
+ friend class QTipLabel;
+ friend class QMessageBoxPrivate;
+ friend class QBalloonTip;
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QLABEL_H
diff --git a/src/gui/widgets/qlabel_p.h b/src/gui/widgets/qlabel_p.h
new file mode 100644
index 0000000000..4d83f3568c
--- /dev/null
+++ b/src/gui/widgets/qlabel_p.h
@@ -0,0 +1,152 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QLABEL_P_H
+#define QLABEL_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 "qlabel.h"
+
+#include "../text/qtextdocumentlayout_p.h"
+#include "private/qtextcontrol_p.h"
+#include "qtextdocumentfragment.h"
+#include "qframe_p.h"
+#include "qtextdocument.h"
+#include "qmovie.h"
+#include "qimage.h"
+#include "qbitmap.h"
+#include "qpicture.h"
+#include "qmenu.h"
+
+QT_BEGIN_NAMESPACE
+
+class QLabelPrivate : public QFramePrivate
+{
+ Q_DECLARE_PUBLIC(QLabel)
+public:
+ QLabelPrivate() {}
+
+ void init();
+ void clearContents();
+ void updateLabel();
+ QSize sizeForWidth(int w) const;
+
+ mutable QSize sh;
+ mutable QSize msh;
+ mutable bool valid_hints;
+ mutable QSizePolicy sizePolicy;
+ int margin;
+ QString text;
+ QPixmap *pixmap;
+ QPixmap *scaledpixmap;
+ QImage *cachedimage;
+#ifndef QT_NO_PICTURE
+ QPicture *picture;
+#endif
+#ifndef QT_NO_MOVIE
+ QPointer<QMovie> movie;
+ void _q_movieUpdated(const QRect&);
+ void _q_movieResized(const QSize&);
+#endif
+#ifndef QT_NO_SHORTCUT
+ void updateShortcut();
+#endif
+#ifndef QT_NO_SHORTCUT
+ QPointer<QWidget> buddy;
+ int shortcutId;
+#endif
+ ushort align;
+ short indent;
+ uint scaledcontents :1;
+ mutable uint textLayoutDirty : 1;
+ mutable uint textDirty : 1;
+ mutable uint isRichText : 1;
+ mutable uint isTextLabel : 1;
+ mutable uint hasShortcut : 1;
+ Qt::TextFormat textformat;
+ mutable QTextControl *control;
+ QTextCursor shortcutCursor;
+ Qt::TextInteractionFlags textInteractionFlags;
+
+ inline bool needTextControl() const {
+ return isTextLabel
+ && (isRichText
+ || (!isRichText && (textInteractionFlags & (Qt::TextSelectableByMouse | Qt::TextSelectableByKeyboard))));
+ }
+
+ void ensureTextPopulated() const;
+ void ensureTextLayouted() const;
+ void ensureTextControl() const;
+ void sendControlEvent(QEvent *e);
+
+ void _q_linkHovered(const QString &link);
+
+ QRectF layoutRect() const;
+ QRect documentRect() const;
+ QPoint layoutPoint(const QPoint& p) const;
+#ifndef QT_NO_CONTEXTMENU
+ QMenu *createStandardContextMenu(const QPoint &pos);
+#endif
+
+ bool openExternalLinks;
+
+#ifndef QT_NO_CURSOR
+ uint validCursor : 1;
+ uint onAnchor : 1;
+ QCursor cursor;
+#endif
+
+ friend class QMessageBoxPrivate;
+};
+
+QT_END_NAMESPACE
+
+#endif // QLABEL_P_H
diff --git a/src/gui/widgets/qlcdnumber.cpp b/src/gui/widgets/qlcdnumber.cpp
new file mode 100644
index 0000000000..0136f1a203
--- /dev/null
+++ b/src/gui/widgets/qlcdnumber.cpp
@@ -0,0 +1,1282 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qlcdnumber.h"
+#ifndef QT_NO_LCDNUMBER
+#include "qbitarray.h"
+#include "qpainter.h"
+#include "private/qframe_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class QLCDNumberPrivate : public QFramePrivate
+{
+ Q_DECLARE_PUBLIC(QLCDNumber)
+public:
+ void init();
+ void internalSetString(const QString& s);
+ void drawString(const QString& s, QPainter &, QBitArray * = 0, bool = true);
+ //void drawString(const QString &, QPainter &, QBitArray * = 0) const;
+ void drawDigit(const QPoint &, QPainter &, int, char, char = ' ');
+ void drawSegment(const QPoint &, char, QPainter &, int, bool = false);
+
+ int ndigits;
+ double val;
+ uint base : 2;
+ uint smallPoint : 1;
+ uint fill : 1;
+ uint shadow : 1;
+ QString digitStr;
+ QBitArray points;
+};
+
+/*!
+ \class QLCDNumber
+
+ \brief The QLCDNumber widget displays a number with LCD-like digits.
+
+ \ingroup basicwidgets
+ \mainclass
+
+ It can display a number in just about any size. It can display
+ decimal, hexadecimal, octal or binary numbers. It is easy to
+ connect to data sources using the display() slot, which is
+ overloaded to take any of five argument types.
+
+ There are also slots to change the base with setMode() and the
+ decimal point with setSmallDecimalPoint().
+
+ QLCDNumber emits the overflow() signal when it is asked to display
+ something beyond its range. The range is set by setNumDigits(),
+ but setSmallDecimalPoint() also influences it. If the display is
+ set to hexadecimal, octal or binary, the integer equivalent of the
+ value is displayed.
+
+ These digits and other symbols can be shown: 0/O, 1, 2, 3, 4, 5/S,
+ 6, 7, 8, 9/g, minus, decimal point, A, B, C, D, E, F, h, H, L, o,
+ P, r, u, U, Y, colon, degree sign (which is specified as single
+ quote in the string) and space. QLCDNumber substitutes spaces for
+ illegal characters.
+
+ It is not possible to retrieve the contents of a QLCDNumber
+ object, although you can retrieve the numeric value with value().
+ If you really need the text, we recommend that you connect the
+ signals that feed the display() slot to another slot as well and
+ store the value there.
+
+ Incidentally, QLCDNumber is the very oldest part of Qt, tracing
+ its roots back to a BASIC program on the \link
+ http://www.nvg.ntnu.no/sinclair/computers/zxspectrum/zxspectrum.htm
+ Sinclair Spectrum\endlink.
+
+ \table
+ \row \o \inlineimage motif-lcdnumber.png Screenshot of a Motif style LCD number widget
+ \inlineimage cde-lcdnumber.png Screenshot of a CDE style LCD number widget
+ \inlineimage windows-lcdnumber.png Screenshot of a Windows style LCD number widget
+ \inlineimage windowsxp-lcdnumber.png Screenshot of a Windows XP style LCD number widget
+ \inlineimage macintosh-lcdnumber.png Screenshot of a Macintosh style LCD number widget
+ \inlineimage plastique-lcdnumber.png Screenshot of a Plastique style LCD number widget
+ \row \o LCD number widgets shown in various widget styles (from left to right):
+ \l{Motif Style Widget Gallery}{Motif}, \l{CDE Style Widget Gallery}{CDE},
+ \l{Windows Style Widget Gallery}{Windows}, \l{Windows XP Style Widget Gallery}{Windows XP},
+ \l{Macintosh Style Widget Gallery}{Macintosh}, \l{Plastique Style Widget Gallery}{Plastique}.
+ \endtable
+
+ \sa QLabel, QFrame, {Digital Clock Example}, {Tetrix Example}
+*/
+
+/*!
+ \enum QLCDNumber::Mode
+
+ This type determines how numbers are shown.
+
+ \value Hex Hexadecimal
+ \value Dec Decimal
+ \value Oct Octal
+ \value Bin Binary
+ \omitvalue HEX
+ \omitvalue DEC
+ \omitvalue OCT
+ \omitvalue BIN
+
+ If the display is set to hexadecimal, octal or binary, the integer
+ equivalent of the value is displayed.
+*/
+
+/*!
+ \enum QLCDNumber::SegmentStyle
+
+ This type determines the visual appearance of the QLCDNumber
+ widget.
+
+ \value Outline gives raised segments filled with the background color.
+ \value Filled gives raised segments filled with the windowText color.
+ \value Flat gives flat segments filled with the windowText color.
+*/
+
+
+
+/*!
+ \fn void QLCDNumber::overflow()
+
+ This signal is emitted whenever the QLCDNumber is asked to display
+ a too-large number or a too-long string.
+
+ It is never emitted by setNumDigits().
+*/
+
+
+static QString int2string(int num, int base, int ndigits, bool *oflow)
+{
+ QString s;
+ bool negative;
+ if (num < 0) {
+ negative = true;
+ num = -num;
+ } else {
+ negative = false;
+ }
+ switch(base) {
+ case QLCDNumber::Hex:
+ s.sprintf("%*x", ndigits, num);
+ break;
+ case QLCDNumber::Dec:
+ s.sprintf("%*i", ndigits, num);
+ break;
+ case QLCDNumber::Oct:
+ s.sprintf("%*o", ndigits, num);
+ break;
+ case QLCDNumber::Bin:
+ {
+ char buf[42];
+ char *p = &buf[41];
+ uint n = num;
+ int len = 0;
+ *p = '\0';
+ do {
+ *--p = (char)((n&1)+'0');
+ n >>= 1;
+ len++;
+ } while (n != 0);
+ len = ndigits - len;
+ if (len > 0)
+ s.fill(QLatin1Char(' '), len);
+ s += QString::fromLatin1(p);
+ }
+ break;
+ }
+ if (negative) {
+ for (int i=0; i<(int)s.length(); i++) {
+ if (s[i] != QLatin1Char(' ')) {
+ if (i != 0) {
+ s[i-1] = QLatin1Char('-');
+ } else {
+ s.insert(0, QLatin1Char('-'));
+ }
+ break;
+ }
+ }
+ }
+ if (oflow)
+ *oflow = (int)s.length() > ndigits;
+ return s;
+}
+
+
+static QString double2string(double num, int base, int ndigits, bool *oflow)
+{
+ QString s;
+ if (base != QLCDNumber::Dec) {
+ bool of = num >= 2147483648.0 || num < -2147483648.0;
+ if (of) { // oops, integer overflow
+ if (oflow)
+ *oflow = true;
+ return s;
+ }
+ s = int2string((int)num, base, ndigits, 0);
+ } else { // decimal base
+ int nd = ndigits;
+ do {
+ s.sprintf("%*.*g", ndigits, nd, num);
+ int i = s.indexOf(QLatin1Char('e'));
+ if (i > 0 && s[i+1]==QLatin1Char('+')) {
+ s[i] = QLatin1Char(' ');
+ s[i+1] = QLatin1Char('e');
+ }
+ } while (nd-- && (int)s.length() > ndigits);
+ }
+ if (oflow)
+ *oflow = (int)s.length() > ndigits;
+ return s;
+}
+
+
+static const char *getSegments(char ch) // gets list of segments for ch
+{
+ static const char segments[30][8] =
+ { { 0, 1, 2, 4, 5, 6,99, 0}, // 0 0 / O
+ { 2, 5,99, 0, 0, 0, 0, 0}, // 1 1
+ { 0, 2, 3, 4, 6,99, 0, 0}, // 2 2
+ { 0, 2, 3, 5, 6,99, 0, 0}, // 3 3
+ { 1, 2, 3, 5,99, 0, 0, 0}, // 4 4
+ { 0, 1, 3, 5, 6,99, 0, 0}, // 5 5 / S
+ { 0, 1, 3, 4, 5, 6,99, 0}, // 6 6
+ { 0, 2, 5,99, 0, 0, 0, 0}, // 7 7
+ { 0, 1, 2, 3, 4, 5, 6,99}, // 8 8
+ { 0, 1, 2, 3, 5, 6,99, 0}, // 9 9 / g
+ { 3,99, 0, 0, 0, 0, 0, 0}, // 10 -
+ { 7,99, 0, 0, 0, 0, 0, 0}, // 11 .
+ { 0, 1, 2, 3, 4, 5,99, 0}, // 12 A
+ { 1, 3, 4, 5, 6,99, 0, 0}, // 13 B
+ { 0, 1, 4, 6,99, 0, 0, 0}, // 14 C
+ { 2, 3, 4, 5, 6,99, 0, 0}, // 15 D
+ { 0, 1, 3, 4, 6,99, 0, 0}, // 16 E
+ { 0, 1, 3, 4,99, 0, 0, 0}, // 17 F
+ { 1, 3, 4, 5,99, 0, 0, 0}, // 18 h
+ { 1, 2, 3, 4, 5,99, 0, 0}, // 19 H
+ { 1, 4, 6,99, 0, 0, 0, 0}, // 20 L
+ { 3, 4, 5, 6,99, 0, 0, 0}, // 21 o
+ { 0, 1, 2, 3, 4,99, 0, 0}, // 22 P
+ { 3, 4,99, 0, 0, 0, 0, 0}, // 23 r
+ { 4, 5, 6,99, 0, 0, 0, 0}, // 24 u
+ { 1, 2, 4, 5, 6,99, 0, 0}, // 25 U
+ { 1, 2, 3, 5, 6,99, 0, 0}, // 26 Y
+ { 8, 9,99, 0, 0, 0, 0, 0}, // 27 :
+ { 0, 1, 2, 3,99, 0, 0, 0}, // 28 '
+ {99, 0, 0, 0, 0, 0, 0, 0} }; // 29 empty
+
+ if (ch >= '0' && ch <= '9')
+ return segments[ch - '0'];
+ if (ch >= 'A' && ch <= 'F')
+ return segments[ch - 'A' + 12];
+ if (ch >= 'a' && ch <= 'f')
+ return segments[ch - 'a' + 12];
+
+ int n;
+ switch (ch) {
+ case '-':
+ n = 10; break;
+ case 'O':
+ n = 0; break;
+ case 'g':
+ n = 9; break;
+ case '.':
+ n = 11; break;
+ case 'h':
+ n = 18; break;
+ case 'H':
+ n = 19; break;
+ case 'l':
+ case 'L':
+ n = 20; break;
+ case 'o':
+ n = 21; break;
+ case 'p':
+ case 'P':
+ n = 22; break;
+ case 'r':
+ case 'R':
+ n = 23; break;
+ case 's':
+ case 'S':
+ n = 5; break;
+ case 'u':
+ n = 24; break;
+ case 'U':
+ n = 25; break;
+ case 'y':
+ case 'Y':
+ n = 26; break;
+ case ':':
+ n = 27; break;
+ case '\'':
+ n = 28; break;
+ default:
+ n = 29; break;
+ }
+ return segments[n];
+}
+
+
+#ifdef QT3_SUPPORT
+/*! \obsolete
+ Constructs an LCD number, sets the number of digits to 5, the base
+ to decimal, the decimal point mode to 'small' and the frame style
+ to a raised box. The segmentStyle() is set to \c Outline.
+
+ The \a parent and \a name arguments are passed to the QFrame
+ constructor.
+
+ \sa setNumDigits(), setSmallDecimalPoint()
+*/
+
+QLCDNumber::QLCDNumber(QWidget *parent, const char *name)
+ : QFrame(*new QLCDNumberPrivate, parent)
+{
+ setObjectName(QString::fromAscii(name));
+ Q_D(QLCDNumber);
+ d->ndigits = 5;
+ d->init();
+}
+
+
+/*! \obsolete
+ Constructs an LCD number, sets the number of digits to \a
+ numDigits, the base to decimal, the decimal point mode to 'small'
+ and the frame style to a raised box. The segmentStyle() is set to
+ \c Outline.
+
+ The \a parent and \a name arguments are passed to the QFrame
+ constructor.
+
+ \sa setNumDigits(), setSmallDecimalPoint()
+*/
+
+QLCDNumber::QLCDNumber(uint numDigits, QWidget *parent, const char *name)
+ : QFrame(*new QLCDNumberPrivate, parent)
+{
+ setObjectName(QString::fromAscii(name));
+ Q_D(QLCDNumber);
+ d->ndigits = numDigits;
+ d->init();
+}
+#endif //QT3_SUPPORT
+
+/*!
+ Constructs an LCD number, sets the number of digits to 5, the base
+ to decimal, the decimal point mode to 'small' and the frame style
+ to a raised box. The segmentStyle() is set to \c Outline.
+
+ The \a parent argument is passed to the QFrame constructor.
+
+ \sa setNumDigits(), setSmallDecimalPoint()
+*/
+
+QLCDNumber::QLCDNumber(QWidget *parent)
+ : QFrame(*new QLCDNumberPrivate, parent)
+{
+ Q_D(QLCDNumber);
+ d->ndigits = 5;
+ d->init();
+}
+
+
+/*!
+ Constructs an LCD number, sets the number of digits to \a
+ numDigits, the base to decimal, the decimal point mode to 'small'
+ and the frame style to a raised box. The segmentStyle() is set to
+ \c Outline.
+
+ The \a parent argument is passed to the QFrame constructor.
+
+ \sa setNumDigits(), setSmallDecimalPoint()
+*/
+
+QLCDNumber::QLCDNumber(uint numDigits, QWidget *parent)
+ : QFrame(*new QLCDNumberPrivate, parent)
+{
+ Q_D(QLCDNumber);
+ d->ndigits = numDigits;
+ d->init();
+}
+
+void QLCDNumberPrivate::init()
+{
+ Q_Q(QLCDNumber);
+
+ q->setFrameStyle(QFrame::Box | QFrame::Raised);
+ val = 0;
+ base = QLCDNumber::Dec;
+ smallPoint = false;
+ q->setNumDigits(ndigits);
+ q->setSegmentStyle(QLCDNumber::Outline);
+ q->setSizePolicy(QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum));
+}
+
+/*!
+ Destroys the LCD number.
+*/
+
+QLCDNumber::~QLCDNumber()
+{
+}
+
+
+/*!
+ \property QLCDNumber::numDigits
+ \brief the current number of digits displayed
+
+ Corresponds to the current number of digits. If \l
+ QLCDNumber::smallDecimalPoint is false, the decimal point occupies
+ one digit position.
+
+ By default, this property contains a value of 5.
+
+ \sa smallDecimalPoint
+*/
+
+void QLCDNumber::setNumDigits(int numDigits)
+{
+ Q_D(QLCDNumber);
+ if (numDigits > 99) {
+ qWarning("QLCDNumber::setNumDigits: (%s) Max 99 digits allowed",
+ objectName().toLocal8Bit().constData());
+ numDigits = 99;
+ }
+ if (numDigits < 0) {
+ qWarning("QLCDNumber::setNumDigits: (%s) Min 0 digits allowed",
+ objectName().toLocal8Bit().constData());
+ numDigits = 0;
+ }
+ if (d->digitStr.isNull()) { // from constructor
+ d->ndigits = numDigits;
+ d->digitStr.fill(QLatin1Char(' '), d->ndigits);
+ d->points.fill(0, d->ndigits);
+ d->digitStr[d->ndigits - 1] = QLatin1Char('0'); // "0" is the default number
+ } else {
+ bool doDisplay = d->ndigits == 0;
+ if (numDigits == d->ndigits) // no change
+ return;
+ register int i;
+ int dif;
+ if (numDigits > d->ndigits) { // expand
+ dif = numDigits - d->ndigits;
+ QString buf;
+ buf.fill(QLatin1Char(' '), dif);
+ d->digitStr.insert(0, buf);
+ d->points.resize(numDigits);
+ for (i=numDigits-1; i>=dif; i--)
+ d->points.setBit(i, d->points.testBit(i-dif));
+ for (i=0; i<dif; i++)
+ d->points.clearBit(i);
+ } else { // shrink
+ dif = d->ndigits - numDigits;
+ d->digitStr = d->digitStr.right(numDigits);
+ QBitArray tmpPoints = d->points;
+ d->points.resize(numDigits);
+ for (i=0; i<(int)numDigits; i++)
+ d->points.setBit(i, tmpPoints.testBit(i+dif));
+ }
+ d->ndigits = numDigits;
+ if (doDisplay)
+ display(value());
+ update();
+ }
+}
+
+int QLCDNumber::numDigits() const
+{
+ Q_D(const QLCDNumber);
+ return d->ndigits;
+}
+
+/*!
+ \overload
+
+ Returns true if \a num is too big to be displayed in its entirety;
+ otherwise returns false.
+
+ \sa display(), numDigits(), smallDecimalPoint()
+*/
+
+bool QLCDNumber::checkOverflow(int num) const
+{
+ Q_D(const QLCDNumber);
+ bool of;
+ int2string(num, d->base, d->ndigits, &of);
+ return of;
+}
+
+
+/*!
+ Returns true if \a num is too big to be displayed in its entirety;
+ otherwise returns false.
+
+ \sa display(), numDigits(), smallDecimalPoint()
+*/
+
+bool QLCDNumber::checkOverflow(double num) const
+{
+ Q_D(const QLCDNumber);
+ bool of;
+ double2string(num, d->base, d->ndigits, &of);
+ return of;
+}
+
+
+/*!
+ \property QLCDNumber::mode
+ \brief the current display mode (number base)
+
+ Corresponds to the current display mode, which is one of \c Bin,
+ \c Oct, \c Dec (the default) and \c Hex. \c Dec mode can display
+ floating point values, the other modes display the integer
+ equivalent.
+
+ \sa smallDecimalPoint(), setHexMode(), setDecMode(), setOctMode(), setBinMode()
+*/
+
+QLCDNumber::Mode QLCDNumber::mode() const
+{
+ Q_D(const QLCDNumber);
+ return (QLCDNumber::Mode) d->base;
+}
+
+void QLCDNumber::setMode(Mode m)
+{
+ Q_D(QLCDNumber);
+ d->base = m;
+ display(d->val);
+}
+
+
+/*!
+ \property QLCDNumber::value
+ \brief the displayed value
+
+ This property corresponds to the current value displayed by the
+ LCDNumber.
+
+ If the displayed value is not a number, the property has a value
+ of 0.
+
+ By default, this property contains a value of 0.
+*/
+
+double QLCDNumber::value() const
+{
+ Q_D(const QLCDNumber);
+ return d->val;
+}
+
+/*!
+ \overload
+
+ Displays the number \a num.
+*/
+void QLCDNumber::display(double num)
+{
+ Q_D(QLCDNumber);
+ d->val = num;
+ bool of;
+ QString s = double2string(d->val, d->base, d->ndigits, &of);
+ if (of)
+ emit overflow();
+ else
+ d->internalSetString(s);
+}
+
+/*!
+ \property QLCDNumber::intValue
+ \brief the displayed value rounded to the nearest integer
+
+ This property corresponds to the nearest integer to the current
+ value displayed by the LCDNumber. This is the value used for
+ hexadecimal, octal and binary modes.
+
+ If the displayed value is not a number, the property has a value
+ of 0.
+
+ By default, this property contains a value of 0.
+*/
+int QLCDNumber::intValue() const
+{
+ Q_D(const QLCDNumber);
+ return qRound(d->val);
+}
+
+
+/*!
+ \overload
+
+ Displays the number \a num.
+*/
+void QLCDNumber::display(int num)
+{
+ Q_D(QLCDNumber);
+ d->val = (double)num;
+ bool of;
+ QString s = int2string(num, d->base, d->ndigits, &of);
+ if (of)
+ emit overflow();
+ else
+ d->internalSetString(s);
+}
+
+
+/*!
+ Displays the number represented by the string \a s.
+
+ This version of the function disregards mode() and
+ smallDecimalPoint().
+
+ These digits and other symbols can be shown: 0/O, 1, 2, 3, 4, 5/S,
+ 6, 7, 8, 9/g, minus, decimal point, A, B, C, D, E, F, h, H, L, o,
+ P, r, u, U, Y, colon, degree sign (which is specified as single
+ quote in the string) and space. QLCDNumber substitutes spaces for
+ illegal characters.
+*/
+
+void QLCDNumber::display(const QString &s)
+{
+ Q_D(QLCDNumber);
+ d->val = 0;
+ bool ok = false;
+ double v = s.toDouble(&ok);
+ if (ok)
+ d->val = v;
+ d->internalSetString(s);
+}
+
+/*!
+ Calls setMode(Hex). Provided for convenience (e.g. for
+ connecting buttons to it).
+
+ \sa setMode(), setDecMode(), setOctMode(), setBinMode(), mode()
+*/
+
+void QLCDNumber::setHexMode()
+{
+ setMode(Hex);
+}
+
+
+/*!
+ Calls setMode(Dec). Provided for convenience (e.g. for
+ connecting buttons to it).
+
+ \sa setMode(), setHexMode(), setOctMode(), setBinMode(), mode()
+*/
+
+void QLCDNumber::setDecMode()
+{
+ setMode(Dec);
+}
+
+
+/*!
+ Calls setMode(Oct). Provided for convenience (e.g. for
+ connecting buttons to it).
+
+ \sa setMode(), setHexMode(), setDecMode(), setBinMode(), mode()
+*/
+
+void QLCDNumber::setOctMode()
+{
+ setMode(Oct);
+}
+
+
+/*!
+ Calls setMode(Bin). Provided for convenience (e.g. for
+ connecting buttons to it).
+
+ \sa setMode(), setHexMode(), setDecMode(), setOctMode(), mode()
+*/
+
+void QLCDNumber::setBinMode()
+{
+ setMode(Bin);
+}
+
+
+/*!
+ \property QLCDNumber::smallDecimalPoint
+ \brief the style of the decimal point
+
+ If true the decimal point is drawn between two digit positions.
+ Otherwise it occupies a digit position of its own, i.e. is drawn
+ in a digit position. The default is false.
+
+ The inter-digit space is made slightly wider when the decimal
+ point is drawn between the digits.
+
+ \sa mode
+*/
+
+void QLCDNumber::setSmallDecimalPoint(bool b)
+{
+ Q_D(QLCDNumber);
+ d->smallPoint = b;
+ update();
+}
+
+bool QLCDNumber::smallDecimalPoint() const
+{
+ Q_D(const QLCDNumber);
+ return d->smallPoint;
+}
+
+
+
+/*!\reimp
+*/
+
+
+void QLCDNumber::paintEvent(QPaintEvent *)
+{
+ Q_D(QLCDNumber);
+ QPainter p(this);
+ drawFrame(&p);
+ if (d->smallPoint)
+ d->drawString(d->digitStr, p, &d->points, false);
+ else
+ d->drawString(d->digitStr, p, 0, false);
+}
+
+
+void QLCDNumberPrivate::internalSetString(const QString& s)
+{
+ Q_Q(QLCDNumber);
+ QString buffer;
+ int i;
+ int len = s.length();
+ QBitArray newPoints(ndigits);
+
+ if (!smallPoint) {
+ if (len == ndigits)
+ buffer = s;
+ else
+ buffer = s.right(ndigits).rightJustified(ndigits, QLatin1Char(' '));
+ } else {
+ int index = -1;
+ bool lastWasPoint = true;
+ newPoints.clearBit(0);
+ for (i=0; i<len; i++) {
+ if (s[i] == QLatin1Char('.')) {
+ if (lastWasPoint) { // point already set for digit?
+ if (index == ndigits - 1) // no more digits
+ break;
+ index++;
+ buffer[index] = QLatin1Char(' '); // 2 points in a row, add space
+ }
+ newPoints.setBit(index); // set decimal point
+ lastWasPoint = true;
+ } else {
+ if (index == ndigits - 1)
+ break;
+ index++;
+ buffer[index] = s[i];
+ newPoints.clearBit(index); // decimal point default off
+ lastWasPoint = false;
+ }
+ }
+ if (index < ((int) ndigits) - 1) {
+ for(i=index; i>=0; i--) {
+ buffer[ndigits - 1 - index + i] = buffer[i];
+ newPoints.setBit(ndigits - 1 - index + i,
+ newPoints.testBit(i));
+ }
+ for(i=0; i<ndigits-index-1; i++) {
+ buffer[i] = QLatin1Char(' ');
+ newPoints.clearBit(i);
+ }
+ }
+ }
+
+ if (buffer == digitStr)
+ return;
+
+ digitStr = buffer;
+ if (smallPoint)
+ points = newPoints;
+ q->update();
+}
+
+/*!
+ \internal
+*/
+
+void QLCDNumberPrivate::drawString(const QString &s, QPainter &p,
+ QBitArray *newPoints, bool newString)
+{
+ Q_Q(QLCDNumber);
+ QPoint pos;
+
+ int digitSpace = smallPoint ? 2 : 1;
+ int xSegLen = q->width()*5/(ndigits*(5 + digitSpace) + digitSpace);
+ int ySegLen = q->height()*5/12;
+ int segLen = ySegLen > xSegLen ? xSegLen : ySegLen;
+ int xAdvance = segLen*(5 + digitSpace)/5;
+ int xOffset = (q->width() - ndigits*xAdvance + segLen/5)/2;
+ int yOffset = (q->height() - segLen*2)/2;
+
+ for (int i=0; i<ndigits; i++) {
+ pos = QPoint(xOffset + xAdvance*i, yOffset);
+ if (newString)
+ drawDigit(pos, p, segLen, s[i].toLatin1(), digitStr[i].toLatin1());
+ else
+ drawDigit(pos, p, segLen, s[i].toLatin1());
+ if (newPoints) {
+ char newPoint = newPoints->testBit(i) ? '.' : ' ';
+ if (newString) {
+ char oldPoint = points.testBit(i) ? '.' : ' ';
+ drawDigit(pos, p, segLen, newPoint, oldPoint);
+ } else {
+ drawDigit(pos, p, segLen, newPoint);
+ }
+ }
+ }
+ if (newString) {
+ digitStr = s;
+ digitStr.truncate(ndigits);
+ if (newPoints)
+ points = *newPoints;
+ }
+}
+
+
+/*!
+ \internal
+*/
+
+void QLCDNumberPrivate::drawDigit(const QPoint &pos, QPainter &p, int segLen,
+ char newCh, char oldCh)
+{
+// Draws and/or erases segments to change display of a single digit
+// from oldCh to newCh
+
+ char updates[18][2]; // can hold 2 times number of segments, only
+ // first 9 used if segment table is correct
+ int nErases;
+ int nUpdates;
+ const char *segs;
+ int i,j;
+
+ const char erase = 0;
+ const char draw = 1;
+ const char leaveAlone = 2;
+
+ segs = getSegments(oldCh);
+ for (nErases=0; segs[nErases] != 99; nErases++) {
+ updates[nErases][0] = erase; // get segments to erase to
+ updates[nErases][1] = segs[nErases]; // remove old char
+ }
+ nUpdates = nErases;
+ segs = getSegments(newCh);
+ for(i = 0 ; segs[i] != 99 ; i++) {
+ for (j=0; j<nErases; j++)
+ if (segs[i] == updates[j][1]) { // same segment ?
+ updates[j][0] = leaveAlone; // yes, already on screen
+ break;
+ }
+ if (j == nErases) { // if not already on screen
+ updates[nUpdates][0] = draw;
+ updates[nUpdates][1] = segs[i];
+ nUpdates++;
+ }
+ }
+ for (i=0; i<nUpdates; i++) {
+ if (updates[i][0] == draw)
+ drawSegment(pos, updates[i][1], p, segLen);
+ if (updates[i][0] == erase)
+ drawSegment(pos, updates[i][1], p, segLen, true);
+ }
+}
+
+
+static void addPoint(QPolygon &a, const QPoint &p)
+{
+ uint n = a.size();
+ a.resize(n + 1);
+ a.setPoint(n, p);
+}
+
+/*!
+ \internal
+*/
+
+void QLCDNumberPrivate::drawSegment(const QPoint &pos, char segmentNo, QPainter &p,
+ int segLen, bool erase)
+{
+ Q_Q(QLCDNumber);
+ QPoint ppt;
+ QPoint pt = pos;
+ int width = segLen/5;
+
+ const QPalette &pal = q->palette();
+ QColor lightColor,darkColor,fgColor;
+ if (erase){
+ lightColor = pal.color(q->backgroundRole());
+ darkColor = lightColor;
+ fgColor = lightColor;
+ } else {
+ lightColor = pal.light().color();
+ darkColor = pal.dark().color();
+ fgColor = pal.color(q->foregroundRole());
+ }
+
+
+#define LINETO(X,Y) addPoint(a, QPoint(pt.x() + (X),pt.y() + (Y)))
+#define LIGHT
+#define DARK
+
+ if (fill) {
+ QPolygon a(0);
+ //The following is an exact copy of the switch below.
+ //don't make any changes here
+ switch (segmentNo) {
+ case 0 :
+ ppt = pt;
+ LIGHT;
+ LINETO(segLen - 1,0);
+ DARK;
+ LINETO(segLen - width - 1,width);
+ LINETO(width,width);
+ LINETO(0,0);
+ break;
+ case 1 :
+ pt += QPoint(0 , 1);
+ ppt = pt;
+ LIGHT;
+ LINETO(width,width);
+ DARK;
+ LINETO(width,segLen - width/2 - 2);
+ LINETO(0,segLen - 2);
+ LIGHT;
+ LINETO(0,0);
+ break;
+ case 2 :
+ pt += QPoint(segLen - 1 , 1);
+ ppt = pt;
+ DARK;
+ LINETO(0,segLen - 2);
+ LINETO(-width,segLen - width/2 - 2);
+ LIGHT;
+ LINETO(-width,width);
+ LINETO(0,0);
+ break;
+ case 3 :
+ pt += QPoint(0 , segLen);
+ ppt = pt;
+ LIGHT;
+ LINETO(width,-width/2);
+ LINETO(segLen - width - 1,-width/2);
+ LINETO(segLen - 1,0);
+ DARK;
+ if (width & 1) { // adjust for integer division error
+ LINETO(segLen - width - 3,width/2 + 1);
+ LINETO(width + 2,width/2 + 1);
+ } else {
+ LINETO(segLen - width - 1,width/2);
+ LINETO(width,width/2);
+ }
+ LINETO(0,0);
+ break;
+ case 4 :
+ pt += QPoint(0 , segLen + 1);
+ ppt = pt;
+ LIGHT;
+ LINETO(width,width/2);
+ DARK;
+ LINETO(width,segLen - width - 2);
+ LINETO(0,segLen - 2);
+ LIGHT;
+ LINETO(0,0);
+ break;
+ case 5 :
+ pt += QPoint(segLen - 1 , segLen + 1);
+ ppt = pt;
+ DARK;
+ LINETO(0,segLen - 2);
+ LINETO(-width,segLen - width - 2);
+ LIGHT;
+ LINETO(-width,width/2);
+ LINETO(0,0);
+ break;
+ case 6 :
+ pt += QPoint(0 , segLen*2);
+ ppt = pt;
+ LIGHT;
+ LINETO(width,-width);
+ LINETO(segLen - width - 1,-width);
+ LINETO(segLen - 1,0);
+ DARK;
+ LINETO(0,0);
+ break;
+ case 7 :
+ if (smallPoint) // if smallpoint place'.' between other digits
+ pt += QPoint(segLen + width/2 , segLen*2);
+ else
+ pt += QPoint(segLen/2 , segLen*2);
+ ppt = pt;
+ DARK;
+ LINETO(width,0);
+ LINETO(width,-width);
+ LIGHT;
+ LINETO(0,-width);
+ LINETO(0,0);
+ break;
+ case 8 :
+ pt += QPoint(segLen/2 - width/2 + 1 , segLen/2 + width);
+ ppt = pt;
+ DARK;
+ LINETO(width,0);
+ LINETO(width,-width);
+ LIGHT;
+ LINETO(0,-width);
+ LINETO(0,0);
+ break;
+ case 9 :
+ pt += QPoint(segLen/2 - width/2 + 1 , 3*segLen/2 + width);
+ ppt = pt;
+ DARK;
+ LINETO(width,0);
+ LINETO(width,-width);
+ LIGHT;
+ LINETO(0,-width);
+ LINETO(0,0);
+ break;
+ default :
+ qWarning("QLCDNumber::drawSegment: (%s) Illegal segment id: %d\n",
+ q->objectName().toLocal8Bit().constData(), segmentNo);
+ }
+ // End exact copy
+ p.setPen(fgColor);
+ p.setBrush(fgColor);
+ p.drawPolygon(a);
+ p.setBrush(Qt::NoBrush);
+
+ pt = pos;
+ }
+#undef LINETO
+#undef LIGHT
+#undef DARK
+
+#define LINETO(X,Y) p.drawLine(ppt.x(), ppt.y(), pt.x()+(X), pt.y()+(Y)); \
+ ppt = QPoint(pt.x()+(X), pt.y()+(Y))
+#define LIGHT p.setPen(lightColor)
+#define DARK p.setPen(darkColor)
+ if (shadow)
+ switch (segmentNo) {
+ case 0 :
+ ppt = pt;
+ LIGHT;
+ LINETO(segLen - 1,0);
+ DARK;
+ LINETO(segLen - width - 1,width);
+ LINETO(width,width);
+ LINETO(0,0);
+ break;
+ case 1 :
+ pt += QPoint(0,1);
+ ppt = pt;
+ LIGHT;
+ LINETO(width,width);
+ DARK;
+ LINETO(width,segLen - width/2 - 2);
+ LINETO(0,segLen - 2);
+ LIGHT;
+ LINETO(0,0);
+ break;
+ case 2 :
+ pt += QPoint(segLen - 1 , 1);
+ ppt = pt;
+ DARK;
+ LINETO(0,segLen - 2);
+ LINETO(-width,segLen - width/2 - 2);
+ LIGHT;
+ LINETO(-width,width);
+ LINETO(0,0);
+ break;
+ case 3 :
+ pt += QPoint(0 , segLen);
+ ppt = pt;
+ LIGHT;
+ LINETO(width,-width/2);
+ LINETO(segLen - width - 1,-width/2);
+ LINETO(segLen - 1,0);
+ DARK;
+ if (width & 1) { // adjust for integer division error
+ LINETO(segLen - width - 3,width/2 + 1);
+ LINETO(width + 2,width/2 + 1);
+ } else {
+ LINETO(segLen - width - 1,width/2);
+ LINETO(width,width/2);
+ }
+ LINETO(0,0);
+ break;
+ case 4 :
+ pt += QPoint(0 , segLen + 1);
+ ppt = pt;
+ LIGHT;
+ LINETO(width,width/2);
+ DARK;
+ LINETO(width,segLen - width - 2);
+ LINETO(0,segLen - 2);
+ LIGHT;
+ LINETO(0,0);
+ break;
+ case 5 :
+ pt += QPoint(segLen - 1 , segLen + 1);
+ ppt = pt;
+ DARK;
+ LINETO(0,segLen - 2);
+ LINETO(-width,segLen - width - 2);
+ LIGHT;
+ LINETO(-width,width/2);
+ LINETO(0,0);
+ break;
+ case 6 :
+ pt += QPoint(0 , segLen*2);
+ ppt = pt;
+ LIGHT;
+ LINETO(width,-width);
+ LINETO(segLen - width - 1,-width);
+ LINETO(segLen - 1,0);
+ DARK;
+ LINETO(0,0);
+ break;
+ case 7 :
+ if (smallPoint) // if smallpoint place'.' between other digits
+ pt += QPoint(segLen + width/2 , segLen*2);
+ else
+ pt += QPoint(segLen/2 , segLen*2);
+ ppt = pt;
+ DARK;
+ LINETO(width,0);
+ LINETO(width,-width);
+ LIGHT;
+ LINETO(0,-width);
+ LINETO(0,0);
+ break;
+ case 8 :
+ pt += QPoint(segLen/2 - width/2 + 1 , segLen/2 + width);
+ ppt = pt;
+ DARK;
+ LINETO(width,0);
+ LINETO(width,-width);
+ LIGHT;
+ LINETO(0,-width);
+ LINETO(0,0);
+ break;
+ case 9 :
+ pt += QPoint(segLen/2 - width/2 + 1 , 3*segLen/2 + width);
+ ppt = pt;
+ DARK;
+ LINETO(width,0);
+ LINETO(width,-width);
+ LIGHT;
+ LINETO(0,-width);
+ LINETO(0,0);
+ break;
+ default :
+ qWarning("QLCDNumber::drawSegment: (%s) Illegal segment id: %d\n",
+ q->objectName().toLocal8Bit().constData(), segmentNo);
+ }
+
+#undef LINETO
+#undef LIGHT
+#undef DARK
+}
+
+
+
+/*!
+ \property QLCDNumber::segmentStyle
+ \brief the style of the LCDNumber
+
+ \table
+ \header \i Style \i Result
+ \row \i \c Outline
+ \i Produces raised segments filled with the background color
+ (this is the default).
+ \row \i \c Filled
+ \i Produces raised segments filled with the foreground color.
+ \row \i \c Flat
+ \i Produces flat segments filled with the foreground color.
+ \endtable
+
+ \c Outline and \c Filled will additionally use
+ QPalette::light() and QPalette::dark() for shadow effects.
+*/
+void QLCDNumber::setSegmentStyle(SegmentStyle s)
+{
+ Q_D(QLCDNumber);
+ d->fill = (s == Flat || s == Filled);
+ d->shadow = (s == Outline || s == Filled);
+ update();
+}
+
+QLCDNumber::SegmentStyle QLCDNumber::segmentStyle() const
+{
+ Q_D(const QLCDNumber);
+ Q_ASSERT(d->fill || d->shadow);
+ if (!d->fill && d->shadow)
+ return Outline;
+ if (d->fill && d->shadow)
+ return Filled;
+ return Flat;
+}
+
+
+/*!\reimp
+*/
+QSize QLCDNumber::sizeHint() const
+{
+ return QSize(10 + 9 * (numDigits() + (smallDecimalPoint() ? 0 : 1)), 23);
+}
+
+/*! \reimp */
+bool QLCDNumber::event(QEvent *e)
+{
+ return QFrame::event(e);
+}
+
+/*!
+ \fn void QLCDNumber::setMargin(int margin)
+ Sets the width of the margin around the contents of the widget to \a margin.
+
+ Use QWidget::setContentsMargins() instead.
+ \sa margin(), QWidget::setContentsMargins()
+*/
+
+/*!
+ \fn int QLCDNumber::margin() const
+ Returns the with of the the margin around the contents of the widget.
+
+ Use QWidget::getContentsMargins() instead.
+ \sa setMargin(), QWidget::getContentsMargins()
+*/
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_LCDNUMBER
diff --git a/src/gui/widgets/qlcdnumber.h b/src/gui/widgets/qlcdnumber.h
new file mode 100644
index 0000000000..626c85d271
--- /dev/null
+++ b/src/gui/widgets/qlcdnumber.h
@@ -0,0 +1,140 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QLCDNUMBER_H
+#define QLCDNUMBER_H
+
+#include <QtGui/qframe.h>
+#include <QtCore/qbitarray.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+#ifndef QT_NO_LCDNUMBER
+
+class QLCDNumberPrivate;
+class Q_GUI_EXPORT QLCDNumber : public QFrame // LCD number widget
+{
+ Q_OBJECT
+ Q_ENUMS(Mode SegmentStyle)
+ Q_PROPERTY(bool smallDecimalPoint READ smallDecimalPoint WRITE setSmallDecimalPoint)
+ Q_PROPERTY(int numDigits READ numDigits WRITE setNumDigits)
+ Q_PROPERTY(Mode mode READ mode WRITE setMode)
+ Q_PROPERTY(SegmentStyle segmentStyle READ segmentStyle WRITE setSegmentStyle)
+ Q_PROPERTY(double value READ value WRITE display)
+ Q_PROPERTY(int intValue READ intValue WRITE display)
+
+public:
+ explicit QLCDNumber(QWidget* parent = 0);
+ explicit QLCDNumber(uint numDigits, QWidget* parent = 0);
+ ~QLCDNumber();
+
+ enum Mode {
+ Hex, Dec, Oct, Bin
+#if defined(QT3_SUPPORT) && !defined(Q_MOC_RUN)
+ , HEX = Hex, DEC = Dec, OCT = Oct, BIN = Bin
+#endif
+ };
+ enum SegmentStyle {
+ Outline, Filled, Flat
+ };
+
+ bool smallDecimalPoint() const;
+
+ int numDigits() const;
+ void setNumDigits(int nDigits);
+
+ bool checkOverflow(double num) const;
+ bool checkOverflow(int num) const;
+
+ Mode mode() const;
+ void setMode(Mode);
+
+ SegmentStyle segmentStyle() const;
+ void setSegmentStyle(SegmentStyle);
+
+ double value() const;
+ int intValue() const;
+
+ QSize sizeHint() const;
+
+public Q_SLOTS:
+ void display(const QString &str);
+ void display(int num);
+ void display(double num);
+ void setHexMode();
+ void setDecMode();
+ void setOctMode();
+ void setBinMode();
+ void setSmallDecimalPoint(bool);
+
+Q_SIGNALS:
+ void overflow();
+
+protected:
+ bool event(QEvent *e);
+ void paintEvent(QPaintEvent *);
+
+public:
+#ifdef QT3_SUPPORT
+ QT3_SUPPORT_CONSTRUCTOR QLCDNumber(QWidget* parent, const char* name);
+ QT3_SUPPORT_CONSTRUCTOR QLCDNumber(uint numDigits, QWidget* parent, const char* name);
+
+ QT3_SUPPORT void setMargin(int margin) { setContentsMargins(margin, margin, margin, margin); }
+ QT3_SUPPORT int margin() const
+ { int margin; int dummy; getContentsMargins(&margin, &dummy, &dummy, &dummy); return margin; }
+#endif
+
+private:
+ Q_DISABLE_COPY(QLCDNumber)
+ Q_DECLARE_PRIVATE(QLCDNumber)
+};
+
+#endif // QT_NO_LCDNUMBER
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QLCDNUMBER_H
diff --git a/src/gui/widgets/qlineedit.cpp b/src/gui/widgets/qlineedit.cpp
new file mode 100644
index 0000000000..b03df9ecff
--- /dev/null
+++ b/src/gui/widgets/qlineedit.cpp
@@ -0,0 +1,3696 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qlineedit.h"
+#include "qlineedit_p.h"
+
+#ifndef QT_NO_LINEEDIT
+#include "qaction.h"
+#include "qapplication.h"
+#include "qclipboard.h"
+#include "qdrag.h"
+#include "qdrawutil.h"
+#include "qevent.h"
+#include "qfontmetrics.h"
+#include "qmenu.h"
+#include "qpainter.h"
+#include "qpixmap.h"
+#include "qpointer.h"
+#include "qstringlist.h"
+#include "qstyle.h"
+#include "qstyleoption.h"
+#include "qtimer.h"
+#include "qvalidator.h"
+#include "qvariant.h"
+#include "qvector.h"
+#include "qwhatsthis.h"
+#include "qdebug.h"
+#include "qtextedit.h"
+#include <private/qtextedit_p.h>
+#ifndef QT_NO_ACCESSIBILITY
+#include "qaccessible.h"
+#endif
+#ifndef QT_NO_IM
+#include "qinputcontext.h"
+#include "qlist.h"
+#endif
+#include "qabstractitemview.h"
+#include "private/qstylesheetstyle_p.h"
+
+#ifndef QT_NO_SHORTCUT
+#include "private/qapplication_p.h"
+#include "private/qshortcutmap_p.h"
+#include "qkeysequence.h"
+#define ACCEL_KEY(k) (!qApp->d_func()->shortcutMap.hasShortcutForKeySequence(k) ? QLatin1String("\t") + QString(QKeySequence(k)) : QString())
+#else
+#define ACCEL_KEY(k) QString()
+#endif
+
+#include <limits.h>
+
+#define verticalMargin 1
+#define horizontalMargin 2
+
+QT_BEGIN_NAMESPACE
+
+#ifdef Q_WS_MAC
+extern void qt_mac_secure_keyboard(bool); //qapplication_mac.cpp
+#endif
+
+static inline bool shouldEnableInputMethod(QLineEdit *lineedit)
+{
+ const QLineEdit::EchoMode mode = lineedit->echoMode();
+ return !lineedit->isReadOnly() && (mode == QLineEdit::Normal || mode == QLineEdit::PasswordEchoOnEdit);
+}
+
+/*!
+ Initialize \a option with the values from this QLineEdit. This method
+ is useful for subclasses when they need a QStyleOptionFrame or QStyleOptionFrameV2, but don't want
+ to fill in all the information themselves. This function will check the version
+ of the QStyleOptionFrame and fill in the additional values for a
+ QStyleOptionFrameV2.
+
+ \sa QStyleOption::initFrom()
+*/
+void QLineEdit::initStyleOption(QStyleOptionFrame *option) const
+{
+ if (!option)
+ return;
+
+ Q_D(const QLineEdit);
+ option->initFrom(this);
+ option->rect = contentsRect();
+ option->lineWidth = d->frame ? style()->pixelMetric(QStyle::PM_DefaultFrameWidth, option, this)
+ : 0;
+ option->midLineWidth = 0;
+ option->state |= QStyle::State_Sunken;
+ if (d->readOnly)
+ option->state |= QStyle::State_ReadOnly;
+#ifdef QT_KEYPAD_NAVIGATION
+ if (hasEditFocus())
+ option->state |= QStyle::State_HasEditFocus;
+#endif
+ if (QStyleOptionFrameV2 *optionV2 = qstyleoption_cast<QStyleOptionFrameV2 *>(option))
+ optionV2->features = QStyleOptionFrameV2::None;
+}
+
+/*!
+ \class QLineEdit
+ \brief The QLineEdit widget is a one-line text editor.
+
+ \ingroup basicwidgets
+ \mainclass
+
+ A line edit allows the user to enter and edit a single line of
+ plain text with a useful collection of editing functions,
+ including undo and redo, cut and paste, and drag and drop.
+
+ By changing the echoMode() of a line edit, it can also be used as
+ a "write-only" field, for inputs such as passwords.
+
+ The length of the text can be constrained to maxLength(). The text
+ can be arbitrarily constrained using a validator() or an
+ inputMask(), or both.
+
+ A related class is QTextEdit which allows multi-line, rich text
+ editing.
+
+ You can change the text with setText() or insert(). The text is
+ retrieved with text(); the displayed text (which may be different,
+ see \l{EchoMode}) is retrieved with displayText(). Text can be
+ selected with setSelection() or selectAll(), and the selection can
+ be cut(), copy()ied and paste()d. The text can be aligned with
+ setAlignment().
+
+ When the text changes the textChanged() signal is emitted; when
+ the text changes other than by calling setText() the textEdited()
+ signal is emitted; when the cursor is moved the
+ cursorPositionChanged() signal is emitted; and when the Return or
+ Enter key is pressed the returnPressed() signal is emitted.
+
+ When editing is finished, either because the line edit lost focus
+ or Return/Enter is pressed the editingFinished() signal is
+ emitted.
+
+ Note that if there is a validator set on the line edit, the
+ returnPressed()/editingFinished() signals will only be emitted if
+ the validator returns QValidator::Acceptable.
+
+ By default, QLineEdits have a frame as specified by the Windows
+ and Motif style guides; you can turn it off by calling
+ setFrame(false).
+
+ The default key bindings are described below. The line edit also
+ provides a context menu (usually invoked by a right mouse click)
+ that presents some of these editing options.
+ \target desc
+ \table
+ \header \i Keypress \i Action
+ \row \i Left Arrow \i Moves the cursor one character to the left.
+ \row \i Shift+Left Arrow \i Moves and selects text one character to the left.
+ \row \i Right Arrow \i Moves the cursor one character to the right.
+ \row \i Shift+Right Arrow \i Moves and selects text one character to the right.
+ \row \i Home \i Moves the cursor to the beginning of the line.
+ \row \i End \i Moves the cursor to the end of the line.
+ \row \i Backspace \i Deletes the character to the left of the cursor.
+ \row \i Ctrl+Backspace \i Deletes the word to the left of the cursor.
+ \row \i Delete \i Deletes the character to the right of the cursor.
+ \row \i Ctrl+Delete \i Deletes the word to the right of the cursor.
+ \row \i Ctrl+A \i Select all.
+ \row \i Ctrl+C \i Copies the selected text to the clipboard.
+ \row \i Ctrl+Insert \i Copies the selected text to the clipboard.
+ \row \i Ctrl+K \i Deletes to the end of the line.
+ \row \i Ctrl+V \i Pastes the clipboard text into line edit.
+ \row \i Shift+Insert \i Pastes the clipboard text into line edit.
+ \row \i Ctrl+X \i Deletes the selected text and copies it to the clipboard.
+ \row \i Shift+Delete \i Deletes the selected text and copies it to the clipboard.
+ \row \i Ctrl+Z \i Undoes the last operation.
+ \row \i Ctrl+Y \i Redoes the last undone operation.
+ \endtable
+
+ Any other key sequence that represents a valid character, will
+ cause the character to be inserted into the line edit.
+
+ \table 100%
+ \row \o \inlineimage macintosh-lineedit.png Screenshot of a Macintosh style line edit
+ \o A line edit shown in the \l{Macintosh Style Widget Gallery}{Macintosh widget style}.
+ \row \o \inlineimage windows-lineedit.png Screenshot of a Windows XP style line edit
+ \o A line edit shown in the \l{Windows XP Style Widget Gallery}{Windows XP widget style}.
+ \row \o \inlineimage plastique-lineedit.png Screenshot of a Plastique style line edit
+ \o A line edit shown in the \l{Plastique Style Widget Gallery}{Plastique widget style}.
+ \endtable
+
+ \sa QTextEdit, QLabel, QComboBox, {fowler}{GUI Design Handbook: Field, Entry}, {Line Edits Example}
+*/
+
+
+/*!
+ \fn void QLineEdit::textChanged(const QString &text)
+
+ This signal is emitted whenever the text changes. The \a text
+ argument is the new text.
+
+ Unlike textEdited(), this signal is also emitted when the text is
+ changed programmatically, for example, by calling setText().
+*/
+
+/*!
+ \fn void QLineEdit::textEdited(const QString &text)
+
+ This signal is emitted whenever the text is edited. The \a text
+ argument is the next text.
+
+ Unlike textChanged(), this signal is not emitted when the text is
+ changed programmatically, for example, by calling setText().
+*/
+
+/*!
+ \fn void QLineEdit::cursorPositionChanged(int old, int new)
+
+ This signal is emitted whenever the cursor moves. The previous
+ position is given by \a old, and the new position by \a new.
+
+ \sa setCursorPosition(), cursorPosition()
+*/
+
+/*!
+ \fn void QLineEdit::selectionChanged()
+
+ This signal is emitted whenever the selection changes.
+
+ \sa hasSelectedText(), selectedText()
+*/
+
+/*!
+ Constructs a line edit with no text.
+
+ The maximum text length is set to 32767 characters.
+
+ The \a parent argument is sent to the QWidget constructor.
+
+ \sa setText(), setMaxLength()
+*/
+QLineEdit::QLineEdit(QWidget* parent)
+ : QWidget(*new QLineEditPrivate, parent,0)
+{
+ Q_D(QLineEdit);
+ d->init(QString());
+}
+
+/*!
+ Constructs a line edit containing the text \a contents.
+
+ The cursor position is set to the end of the line and the maximum
+ text length to 32767 characters.
+
+ The \a parent and argument is sent to the QWidget
+ constructor.
+
+ \sa text(), setMaxLength()
+*/
+QLineEdit::QLineEdit(const QString& contents, QWidget* parent)
+ : QWidget(*new QLineEditPrivate, parent, 0)
+{
+ Q_D(QLineEdit);
+ d->init(contents);
+}
+
+
+#ifdef QT3_SUPPORT
+/*!
+ Constructs a line edit with no text.
+
+ The maximum text length is set to 32767 characters.
+
+ The \a parent and \a name arguments are sent to the QWidget constructor.
+
+ \sa setText(), setMaxLength()
+*/
+QLineEdit::QLineEdit(QWidget* parent, const char* name)
+ : QWidget(*new QLineEditPrivate, parent,0)
+{
+ Q_D(QLineEdit);
+ setObjectName(QString::fromAscii(name));
+ d->init(QString());
+}
+
+/*!
+ Constructs a line edit containing the text \a contents.
+
+ The cursor position is set to the end of the line and the maximum
+ text length to 32767 characters.
+
+ The \a parent and \a name arguments are sent to the QWidget
+ constructor.
+
+ \sa text(), setMaxLength()
+*/
+
+QLineEdit::QLineEdit(const QString& contents, QWidget* parent, const char* name)
+ : QWidget(*new QLineEditPrivate, parent, 0)
+{
+ Q_D(QLineEdit);
+ setObjectName(QString::fromAscii(name));
+ d->init(contents);
+}
+
+/*!
+ Constructs a line edit with an input \a inputMask and the text \a
+ contents.
+
+ The cursor position is set to the end of the line and the maximum
+ text length is set to the length of the mask (the number of mask
+ characters and separators).
+
+ The \a parent and \a name arguments are sent to the QWidget
+ constructor.
+
+ \sa setMask() text()
+*/
+QLineEdit::QLineEdit(const QString& contents, const QString &inputMask, QWidget* parent, const char* name)
+ : QWidget(*new QLineEditPrivate, parent, 0)
+{
+ Q_D(QLineEdit);
+ setObjectName(QString::fromAscii(name));
+ d->parseInputMask(inputMask);
+ if (d->maskData) {
+ QString ms = d->maskString(0, contents);
+ d->init(ms + d->clearString(ms.length(), d->maxLength - ms.length()));
+ d->cursor = d->nextMaskBlank(ms.length());
+ } else {
+ d->init(contents);
+ }
+}
+#endif
+
+/*!
+ Destroys the line edit.
+*/
+
+QLineEdit::~QLineEdit()
+{
+}
+
+
+/*!
+ \property QLineEdit::text
+ \brief the line edit's text
+
+ Setting this property clears the selection, clears the undo/redo
+ history, moves the cursor to the end of the line and resets the
+ \l modified property to false. The text is not validated when
+ inserted with setText().
+
+ The text is truncated to maxLength() length.
+
+ By default, this property contains an empty string.
+
+ \sa insert(), clear()
+*/
+QString QLineEdit::text() const
+{
+ Q_D(const QLineEdit);
+ QString res = d->text;
+ if (d->maskData)
+ res = d->stripString(d->text);
+ return (res.isNull() ? QString::fromLatin1("") : res);
+}
+
+void QLineEdit::setText(const QString& text)
+{
+ Q_D(QLineEdit);
+ d->setText(text, -1, false);
+#ifdef QT_KEYPAD_NAVIGATION
+ d->origText = d->text;
+#endif
+}
+
+
+/*!
+ \property QLineEdit::displayText
+ \brief the displayed text
+
+ If \l echoMode is \l Normal this returns the same as text(); if
+ \l EchoMode is \l Password or \l PasswordEchoOnEdit it returns a string of asterisks
+ text().length() characters long, e.g. "******"; if \l EchoMode is
+ \l NoEcho returns an empty string, "".
+
+ By default, this property contains an empty string.
+
+ \sa setEchoMode() text() EchoMode
+*/
+
+QString QLineEdit::displayText() const
+{
+ Q_D(const QLineEdit);
+ if (d->echoMode == NoEcho)
+ return QString::fromLatin1("");
+ QString res = d->text;
+
+ if (d->echoMode == Password || (d->echoMode == PasswordEchoOnEdit
+ && !d->passwordEchoEditing)) {
+ QStyleOptionFrameV2 opt;
+ initStyleOption(&opt);
+ res.fill(style()->styleHint(QStyle::SH_LineEdit_PasswordCharacter, &opt, this));
+ }
+ return (res.isNull() ? QString::fromLatin1("") : res);
+}
+
+
+/*!
+ \property QLineEdit::maxLength
+ \brief the maximum permitted length of the text
+
+ If the text is too long, it is truncated at the limit.
+
+ If truncation occurs any selected text will be unselected, the
+ cursor position is set to 0 and the first part of the string is
+ shown.
+
+ If the line edit has an input mask, the mask defines the maximum
+ string length.
+
+ By default, this property contains a value of 32767.
+
+ \sa inputMask
+*/
+
+int QLineEdit::maxLength() const
+{
+ Q_D(const QLineEdit);
+ return d->maxLength;
+}
+
+void QLineEdit::setMaxLength(int maxLength)
+{
+ Q_D(QLineEdit);
+ if (d->maskData)
+ return;
+ d->maxLength = maxLength;
+ setText(d->text);
+}
+
+
+
+/*!
+ \property QLineEdit::frame
+ \brief whether the line edit draws itself with a frame
+
+ If enabled (the default) the line edit draws itself inside a
+ frame, otherwise the line edit draws itself without any frame.
+*/
+bool QLineEdit::hasFrame() const
+{
+ Q_D(const QLineEdit);
+ return d->frame;
+}
+
+
+void QLineEdit::setFrame(bool enable)
+{
+ Q_D(QLineEdit);
+ d->frame = enable;
+ update();
+ updateGeometry();
+}
+
+
+/*!
+ \enum QLineEdit::EchoMode
+
+ This enum type describes how a line edit should display its
+ contents.
+
+ \value Normal Display characters as they are entered. This is the
+ default.
+ \value NoEcho Do not display anything. This may be appropriate
+ for passwords where even the length of the
+ password should be kept secret.
+ \value Password Display asterisks instead of the characters
+ actually entered.
+ \value PasswordEchoOnEdit Display characters as they are entered
+ while editing otherwise display asterisks.
+
+ \sa setEchoMode() echoMode()
+*/
+
+
+/*!
+ \property QLineEdit::echoMode
+ \brief the line edit's echo mode
+
+ The echo mode determines how the text entered in the line edit is
+ displayed (or echoed) to the user.
+
+ The most common setting is \l Normal, in which the text entered by the
+ user is displayed verbatim, but QLineEdit also supports modes that allow
+ the entered text to be suppressed or obscured: these include \l NoEcho,
+ \l Password and \l PasswordEchoOnEdit.
+
+ The widget's display and the ability to copy or drag the text is
+ affected by this setting.
+
+ By default, this property is set to \l Normal.
+
+ \sa EchoMode displayText()
+*/
+
+QLineEdit::EchoMode QLineEdit::echoMode() const
+{
+ Q_D(const QLineEdit);
+ return (EchoMode) d->echoMode;
+}
+
+void QLineEdit::setEchoMode(EchoMode mode)
+{
+ Q_D(QLineEdit);
+ if (mode == (EchoMode)d->echoMode)
+ return;
+ setAttribute(Qt::WA_InputMethodEnabled, shouldEnableInputMethod(this));
+ d->echoMode = mode;
+ d->passwordEchoEditing = false;
+ d->updateTextLayout();
+ update();
+#ifdef Q_WS_MAC
+ if (hasFocus())
+ qt_mac_secure_keyboard(d->echoMode == Password || d->echoMode == NoEcho);
+#endif
+}
+
+
+#ifndef QT_NO_VALIDATOR
+/*!
+ Returns a pointer to the current input validator, or 0 if no
+ validator has been set.
+
+ \sa setValidator()
+*/
+
+const QValidator * QLineEdit::validator() const
+{
+ Q_D(const QLineEdit);
+ return d->validator;
+}
+
+/*!
+ Sets this line edit to only accept input that the validator, \a v,
+ will accept. This allows you to place any arbitrary constraints on
+ the text which may be entered.
+
+ If \a v == 0, setValidator() removes the current input validator.
+ The initial setting is to have no input validator (i.e. any input
+ is accepted up to maxLength()).
+
+ \sa validator() QIntValidator QDoubleValidator QRegExpValidator
+*/
+
+void QLineEdit::setValidator(const QValidator *v)
+{
+ Q_D(QLineEdit);
+ d->validator = const_cast<QValidator*>(v);
+}
+#endif // QT_NO_VALIDATOR
+
+#ifndef QT_NO_COMPLETER
+/*!
+ \since 4.2
+
+ Sets this line edit to provide auto completions from the completer, \a c.
+ The completion mode is set using QCompleter::setCompletionMode().
+
+ To use a QCompleter with a QValidator or QLineEdit::inputMask, you need to
+ ensure that the model provided to QCompleter contains valid entries. You can
+ use the QSortFilterProxyModel to ensure that the QCompleter's model contains
+ only valid entries.
+
+ If \a c == 0, setCompleter() removes the current completer, effectively
+ disabling auto completion.
+
+ \sa QCompleter
+*/
+void QLineEdit::setCompleter(QCompleter *c)
+{
+ Q_D(QLineEdit);
+ if (c == d->completer)
+ return;
+ if (d->completer) {
+ disconnect(d->completer, 0, this, 0);
+ d->completer->setWidget(0);
+ if (d->completer->parent() == this)
+ delete d->completer;
+ }
+ d->completer = c;
+ if (!c)
+ return;
+ if (c->widget() == 0)
+ c->setWidget(this);
+ if (hasFocus()) {
+ QObject::connect(d->completer, SIGNAL(activated(QString)),
+ this, SLOT(setText(QString)));
+ QObject::connect(d->completer, SIGNAL(highlighted(QString)),
+ this, SLOT(_q_completionHighlighted(QString)));
+ }
+}
+
+/*!
+ \since 4.2
+
+ Returns the current QCompleter that provides completions.
+*/
+QCompleter *QLineEdit::completer() const
+{
+ Q_D(const QLineEdit);
+ return d->completer;
+}
+
+// looks for an enabled item iterating forward(dir=1)/backward(dir=-1) from the
+// current row based. dir=0 indicates a new completion prefix was set.
+bool QLineEditPrivate::advanceToEnabledItem(int dir)
+{
+ int start = completer->currentRow();
+ if (start == -1)
+ return false;
+ int i = start + dir;
+ if (dir == 0) dir = 1;
+ do {
+ if (!completer->setCurrentRow(i)) {
+ if (!completer->wrapAround())
+ break;
+ i = i > 0 ? 0 : completer->completionCount() - 1;
+ } else {
+ QModelIndex currentIndex = completer->currentIndex();
+ if (completer->completionModel()->flags(currentIndex) & Qt::ItemIsEnabled)
+ return true;
+ i += dir;
+ }
+ } while (i != start);
+
+ completer->setCurrentRow(start); // restore
+ return false;
+}
+
+void QLineEditPrivate::complete(int key)
+{
+ if (!completer || readOnly || echoMode != QLineEdit::Normal)
+ return;
+
+ if (completer->completionMode() == QCompleter::InlineCompletion) {
+ if (key == Qt::Key_Backspace)
+ return;
+ int n = 0;
+ if (key == Qt::Key_Up || key == Qt::Key_Down) {
+ if (selend != 0 && selend != text.length())
+ return;
+ QString prefix = hasSelectedText() ? text.left(selstart) : text;
+ if (text.compare(completer->currentCompletion(), completer->caseSensitivity()) != 0
+ || prefix.compare(completer->completionPrefix(), completer->caseSensitivity()) != 0) {
+ completer->setCompletionPrefix(prefix);
+ } else {
+ n = (key == Qt::Key_Up) ? -1 : +1;
+ }
+ } else {
+ completer->setCompletionPrefix(text);
+ }
+ if (!advanceToEnabledItem(n))
+ return;
+ } else {
+#ifndef QT_KEYPAD_NAVIGATION
+ if (text.isEmpty()) {
+ completer->popup()->hide();
+ return;
+ }
+#endif
+ completer->setCompletionPrefix(text);
+ }
+
+ completer->complete();
+}
+
+void QLineEditPrivate::_q_completionHighlighted(QString newText)
+{
+ Q_Q(QLineEdit);
+ if (completer->completionMode() != QCompleter::InlineCompletion)
+ q->setText(newText);
+ else {
+ int c = cursor;
+ q->setText(text.left(c) + newText.mid(c));
+ q->setSelection(text.length(), c - newText.length());
+ }
+}
+#endif // QT_NO_COMPLETER
+
+/*!
+ Returns a recommended size for the widget.
+
+ The width returned, in pixels, is usually enough for about 15 to
+ 20 characters.
+*/
+
+QSize QLineEdit::sizeHint() const
+{
+ Q_D(const QLineEdit);
+ ensurePolished();
+ QFontMetrics fm(font());
+ int h = qMax(fm.lineSpacing(), 14) + 2*verticalMargin
+ + d->topTextMargin + d->bottomTextMargin
+ + d->topmargin + d->bottommargin;
+ int w = fm.width(QLatin1Char('x')) * 17 + 2*horizontalMargin
+ + d->leftTextMargin + d->rightTextMargin
+ + d->leftmargin + d->rightmargin; // "some"
+ QStyleOptionFrameV2 opt;
+ initStyleOption(&opt);
+ return (style()->sizeFromContents(QStyle::CT_LineEdit, &opt, QSize(w, h).
+ expandedTo(QApplication::globalStrut()), this));
+}
+
+
+/*!
+ Returns a minimum size for the line edit.
+
+ The width returned is enough for at least one character.
+*/
+
+QSize QLineEdit::minimumSizeHint() const
+{
+ Q_D(const QLineEdit);
+ ensurePolished();
+ QFontMetrics fm = fontMetrics();
+ int h = fm.height() + qMax(2*verticalMargin, fm.leading())
+ + d->topmargin + d->bottommargin;
+ int w = fm.maxWidth() + d->leftmargin + d->rightmargin;
+ QStyleOptionFrameV2 opt;
+ initStyleOption(&opt);
+ return (style()->sizeFromContents(QStyle::CT_LineEdit, &opt, QSize(w, h).
+ expandedTo(QApplication::globalStrut()), this));
+}
+
+
+/*!
+ \property QLineEdit::cursorPosition
+ \brief the current cursor position for this line edit
+
+ Setting the cursor position causes a repaint when appropriate.
+
+ By default, this property contains a value of 0.
+*/
+
+int QLineEdit::cursorPosition() const
+{
+ Q_D(const QLineEdit);
+ return d->cursor;
+}
+
+void QLineEdit::setCursorPosition(int pos)
+{
+ Q_D(QLineEdit);
+ if (pos < 0)
+ pos = 0;
+
+ if (pos <= d->text.length())
+ d->moveCursor(pos);
+}
+
+/*!
+ Returns the cursor position under the point \a pos.
+*/
+// ### What should this do if the point is outside of contentsRect? Currently returns 0.
+int QLineEdit::cursorPositionAt(const QPoint &pos)
+{
+ Q_D(QLineEdit);
+ return d->xToPos(pos.x());
+}
+
+
+#ifdef QT3_SUPPORT
+/*! \obsolete
+
+ Use setText(), setCursorPosition() and setSelection() instead.
+*/
+bool QLineEdit::validateAndSet(const QString &newText, int newPos,
+ int newMarkAnchor, int newMarkDrag)
+{
+ Q_D(QLineEdit);
+ int priorState = d->undoState;
+ d->selstart = 0;
+ d->selend = d->text.length();
+ d->removeSelectedText();
+ d->insert(newText);
+ d->finishChange(priorState);
+ if (d->undoState > priorState) {
+ d->cursor = newPos;
+ d->selstart = qMin(newMarkAnchor, newMarkDrag);
+ d->selend = qMax(newMarkAnchor, newMarkDrag);
+ update();
+ d->emitCursorPositionChanged();
+ return true;
+ }
+ return false;
+}
+#endif //QT3_SUPPORT
+
+/*!
+ \property QLineEdit::alignment
+ \brief the alignment of the line edit
+
+ Both horizontal and vertical alignment is allowed here, Qt::AlignJustify
+ will map to Qt::AlignLeft.
+
+ By default, this property contains a combination of Qt::AlignLeft and Qt::AlignVCenter.
+
+ \sa Qt::Alignment
+*/
+
+Qt::Alignment QLineEdit::alignment() const
+{
+ Q_D(const QLineEdit);
+ return QFlag(d->alignment);
+}
+
+void QLineEdit::setAlignment(Qt::Alignment alignment)
+{
+ Q_D(QLineEdit);
+ d->alignment = alignment;
+ update();
+}
+
+
+/*!
+ Moves the cursor forward \a steps characters. If \a mark is true
+ each character moved over is added to the selection; if \a mark is
+ false the selection is cleared.
+
+ \sa cursorBackward()
+*/
+
+void QLineEdit::cursorForward(bool mark, int steps)
+{
+ Q_D(QLineEdit);
+ int cursor = d->cursor;
+ if (steps > 0) {
+ while(steps--)
+ cursor = d->textLayout.nextCursorPosition(cursor);
+ } else if (steps < 0) {
+ while (steps++)
+ cursor = d->textLayout.previousCursorPosition(cursor);
+ }
+ d->moveCursor(cursor, mark);
+}
+
+
+/*!
+ Moves the cursor back \a steps characters. If \a mark is true each
+ character moved over is added to the selection; if \a mark is
+ false the selection is cleared.
+
+ \sa cursorForward()
+*/
+void QLineEdit::cursorBackward(bool mark, int steps)
+{
+ cursorForward(mark, -steps);
+}
+
+/*!
+ Moves the cursor one word forward. If \a mark is true, the word is
+ also selected.
+
+ \sa cursorWordBackward()
+*/
+void QLineEdit::cursorWordForward(bool mark)
+{
+ Q_D(QLineEdit);
+ d->moveCursor(d->textLayout.nextCursorPosition(d->cursor, QTextLayout::SkipWords), mark);
+}
+
+/*!
+ Moves the cursor one word backward. If \a mark is true, the word
+ is also selected.
+
+ \sa cursorWordForward()
+*/
+
+void QLineEdit::cursorWordBackward(bool mark)
+{
+ Q_D(QLineEdit);
+ d->moveCursor(d->textLayout.previousCursorPosition(d->cursor, QTextLayout::SkipWords), mark);
+}
+
+
+/*!
+ If no text is selected, deletes the character to the left of the
+ text cursor and moves the cursor one position to the left. If any
+ text is selected, the cursor is moved to the beginning of the
+ selected text and the selected text is deleted.
+
+ \sa del()
+*/
+void QLineEdit::backspace()
+{
+ Q_D(QLineEdit);
+ int priorState = d->undoState;
+ if (d->hasSelectedText()) {
+ d->removeSelectedText();
+ } else if (d->cursor) {
+ --d->cursor;
+ if (d->maskData)
+ d->cursor = d->prevMaskBlank(d->cursor);
+ QChar uc = d->text.at(d->cursor);
+ if (d->cursor > 0 && uc.unicode() >= 0xdc00 && uc.unicode() < 0xe000) {
+ // second half of a surrogate, check if we have the first half as well,
+ // if yes delete both at once
+ uc = d->text.at(d->cursor - 1);
+ if (uc.unicode() >= 0xd800 && uc.unicode() < 0xdc00) {
+ d->del(true);
+ --d->cursor;
+ }
+ }
+ d->del(true);
+ }
+ d->finishChange(priorState);
+}
+
+/*!
+ If no text is selected, deletes the character to the right of the
+ text cursor. If any text is selected, the cursor is moved to the
+ beginning of the selected text and the selected text is deleted.
+
+ \sa backspace()
+*/
+
+void QLineEdit::del()
+{
+ Q_D(QLineEdit);
+ int priorState = d->undoState;
+ if (d->hasSelectedText()) {
+ d->removeSelectedText();
+ } else {
+ int n = d->textLayout.nextCursorPosition(d->cursor) - d->cursor;
+ while (n--)
+ d->del();
+ }
+ d->finishChange(priorState);
+}
+
+/*!
+ Moves the text cursor to the beginning of the line unless it is
+ already there. If \a mark is true, text is selected towards the
+ first position; otherwise, any selected text is unselected if the
+ cursor is moved.
+
+ \sa end()
+*/
+
+void QLineEdit::home(bool mark)
+{
+ Q_D(QLineEdit);
+ d->moveCursor(0, mark);
+}
+
+/*!
+ Moves the text cursor to the end of the line unless it is already
+ there. If \a mark is true, text is selected towards the last
+ position; otherwise, any selected text is unselected if the cursor
+ is moved.
+
+ \sa home()
+*/
+
+void QLineEdit::end(bool mark)
+{
+ Q_D(QLineEdit);
+ d->moveCursor(d->text.length(), mark);
+}
+
+
+/*!
+ \property QLineEdit::modified
+ \brief whether the line edit's contents has been modified by the user
+
+ The modified flag is never read by QLineEdit; it has a default value
+ of false and is changed to true whenever the user changes the line
+ edit's contents.
+
+ This is useful for things that need to provide a default value but
+ do not start out knowing what the default should be (perhaps it
+ depends on other fields on the form). Start the line edit without
+ the best default, and when the default is known, if modified()
+ returns false (the user hasn't entered any text), insert the
+ default value.
+
+ Calling setText() resets the modified flag to false.
+*/
+
+bool QLineEdit::isModified() const
+{
+ Q_D(const QLineEdit);
+ return d->modifiedState != d->undoState;
+}
+
+void QLineEdit::setModified(bool modified)
+{
+ Q_D(QLineEdit);
+ if (modified)
+ d->modifiedState = -1;
+ else
+ d->modifiedState = d->undoState;
+}
+
+
+/*!\fn QLineEdit::clearModified()
+
+Use setModified(false) instead.
+
+ \sa isModified()
+*/
+
+
+/*!
+ \property QLineEdit::hasSelectedText
+ \brief whether there is any text selected
+
+ hasSelectedText() returns true if some or all of the text has been
+ selected by the user; otherwise returns false.
+
+ By default, this property is false.
+
+ \sa selectedText()
+*/
+
+
+bool QLineEdit::hasSelectedText() const
+{
+ Q_D(const QLineEdit);
+ return d->hasSelectedText();
+}
+
+/*!
+ \property QLineEdit::selectedText
+ \brief the selected text
+
+ If there is no selected text this property's value is
+ an empty string.
+
+ By default, this property contains an empty string.
+
+ \sa hasSelectedText()
+*/
+
+QString QLineEdit::selectedText() const
+{
+ Q_D(const QLineEdit);
+ if (d->hasSelectedText())
+ return d->text.mid(d->selstart, d->selend - d->selstart);
+ return QString();
+}
+
+/*!
+ selectionStart() returns the index of the first selected character in the
+ line edit or -1 if no text is selected.
+
+ \sa selectedText()
+*/
+
+int QLineEdit::selectionStart() const
+{
+ Q_D(const QLineEdit);
+ return d->hasSelectedText() ? d->selstart : -1;
+}
+
+
+#ifdef QT3_SUPPORT
+
+/*!
+ \fn void QLineEdit::lostFocus()
+
+ This signal is emitted when the line edit has lost focus.
+
+ Use editingFinished() instead
+ \sa editingFinished(), returnPressed()
+*/
+
+/*!
+ Use isModified() instead.
+*/
+bool QLineEdit::edited() const { return isModified(); }
+/*!
+ Use setModified() or setText().
+*/
+void QLineEdit::setEdited(bool on) { setModified(on); }
+
+/*!
+ There exists no equivalent functionality in Qt 4.
+*/
+int QLineEdit::characterAt(int xpos, QChar *chr) const
+{
+ Q_D(const QLineEdit);
+ int pos = d->xToPos(xpos + contentsRect().x() - d->hscroll + horizontalMargin);
+ if (chr && pos < (int) d->text.length())
+ *chr = d->text.at(pos);
+ return pos;
+
+}
+
+/*!
+ Use selectedText() and selectionStart() instead.
+*/
+bool QLineEdit::getSelection(int *start, int *end)
+{
+ Q_D(QLineEdit);
+ if (d->hasSelectedText() && start && end) {
+ *start = d->selstart;
+ *end = d->selend;
+ return true;
+ }
+ return false;
+}
+#endif
+
+
+/*!
+ Selects text from position \a start and for \a length characters.
+ Negative lengths are allowed.
+
+ \sa deselect() selectAll() selectedText()
+*/
+
+void QLineEdit::setSelection(int start, int length)
+{
+ Q_D(QLineEdit);
+ if (start < 0 || start > (int)d->text.length()) {
+ qWarning("QLineEdit::setSelection: Invalid start position (%d)", start);
+ return;
+ } else {
+ if (length > 0) {
+ d->selstart = start;
+ d->selend = qMin(start + length, (int)d->text.length());
+ d->cursor = d->selend;
+ } else {
+ d->selstart = qMax(start + length, 0);
+ d->selend = start;
+ d->cursor = d->selstart;
+ }
+ }
+
+ if (d->hasSelectedText()){
+ QStyleOptionFrameV2 opt;
+ initStyleOption(&opt);
+ if (!style()->styleHint(QStyle::SH_BlinkCursorWhenTextSelected, &opt, this))
+ d->setCursorVisible(false);
+ }
+
+ update();
+ d->emitCursorPositionChanged();
+}
+
+
+/*!
+ \property QLineEdit::undoAvailable
+ \brief whether undo is available
+
+ Undo becomes available once the user has modified the text in the line edit.
+
+ By default, this property is false.
+*/
+
+bool QLineEdit::isUndoAvailable() const
+{
+ Q_D(const QLineEdit);
+ return d->isUndoAvailable();
+}
+
+/*!
+ \property QLineEdit::redoAvailable
+ \brief whether redo is available
+
+ Redo becomes available once the user has performed one or more undo operations
+ on text in the line edit.
+
+ By default, this property is false.
+*/
+
+bool QLineEdit::isRedoAvailable() const
+{
+ Q_D(const QLineEdit);
+ return d->isRedoAvailable();
+}
+
+/*!
+ \property QLineEdit::dragEnabled
+ \brief whether the lineedit starts a drag if the user presses and
+ moves the mouse on some selected text
+
+ Dragging is disabled by default.
+*/
+
+bool QLineEdit::dragEnabled() const
+{
+ Q_D(const QLineEdit);
+ return d->dragEnabled;
+}
+
+void QLineEdit::setDragEnabled(bool b)
+{
+ Q_D(QLineEdit);
+ d->dragEnabled = b;
+}
+
+
+/*!
+ \property QLineEdit::acceptableInput
+ \brief whether the input satisfies the inputMask and the
+ validator.
+
+ By default, this property is true.
+
+ \sa setInputMask(), setValidator()
+*/
+bool QLineEdit::hasAcceptableInput() const
+{
+ Q_D(const QLineEdit);
+ return d->hasAcceptableInput(d->text);
+}
+
+/*!
+ Sets the margins around the text inside the frame to have the
+ sizes \a left, \a top, \a right, and \a bottom.
+ \since 4.5
+
+ See also getTextMargins().
+*/
+void QLineEdit::setTextMargins(int left, int top, int right, int bottom)
+{
+ Q_D(QLineEdit);
+ d->leftTextMargin = left;
+ d->topTextMargin = top;
+ d->rightTextMargin = right;
+ d->bottomTextMargin = bottom;
+ updateGeometry();
+ update();
+}
+
+/*!
+ Returns the widget's text margins for \a left, \a top, \a right, and \a bottom.
+ \since 4.5
+
+ \sa setTextMargins()
+*/
+void QLineEdit::getTextMargins(int *left, int *top, int *right, int *bottom) const
+{
+ Q_D(const QLineEdit);
+ if (left)
+ *left = d->leftTextMargin;
+ if (top)
+ *top = d->topTextMargin;
+ if (right)
+ *right = d->rightTextMargin;
+ if (bottom)
+ *bottom = d->bottomTextMargin;
+}
+
+/*!
+ \property QLineEdit::inputMask
+ \brief The validation input mask
+
+ If no mask is set, inputMask() returns an empty string.
+
+ Sets the QLineEdit's validation mask. Validators can be used
+ instead of, or in conjunction with masks; see setValidator().
+
+ Unset the mask and return to normal QLineEdit operation by passing
+ an empty string ("") or just calling setInputMask() with no
+ arguments.
+
+ The table below shows the characters that can be used in an input mask.
+ A space character, the default character for a blank, is needed for cases
+ where a character is \e{permitted but not required}.
+
+ \table
+ \header \i Character \i Meaning
+ \row \i \c A \i ASCII alphabetic character required. A-Z, a-z.
+ \row \i \c a \i ASCII alphabetic character permitted but not required.
+ \row \i \c N \i ASCII alphanumeric character required. A-Z, a-z, 0-9.
+ \row \i \c n \i ASCII alphanumeric character permitted but not required.
+ \row \i \c X \i Any character required.
+ \row \i \c x \i Any character permitted but not required.
+ \row \i \c 9 \i ASCII digit required. 0-9.
+ \row \i \c 0 \i ASCII digit permitted but not required.
+ \row \i \c D \i ASCII digit required. 1-9.
+ \row \i \c d \i ASCII digit permitted but not required (1-9).
+ \row \i \c # \i ASCII digit or plus/minus sign permitted but not required.
+ \row \i \c H \i Hexadecimal character required. A-F, a-f, 0-9.
+ \row \i \c h \i Hexadecimal character permitted but not required.
+ \row \i \c B \i Binary character required. 0-1.
+ \row \i \c b \i Binary character permitted but not required.
+ \row \i \c > \i All following alphabetic characters are uppercased.
+ \row \i \c < \i All following alphabetic characters are lowercased.
+ \row \i \c ! \i Switch off case conversion.
+ \row \i \tt{\\} \i Use \tt{\\} to escape the special
+ characters listed above to use them as
+ separators.
+ \endtable
+
+ The mask consists of a string of mask characters and separators,
+ optionally followed by a semicolon and the character used for
+ blanks. The blank characters are always removed from the text
+ after editing.
+
+ Examples:
+ \table
+ \header \i Mask \i Notes
+ \row \i \c 000.000.000.000;_ \i IP address; blanks are \c{_}.
+ \row \i \c HH:HH:HH:HH:HH:HH;_ \i MAC address
+ \row \i \c 0000-00-00 \i ISO Date; blanks are \c space
+ \row \i \c >AAAAA-AAAAA-AAAAA-AAAAA-AAAAA;# \i License number;
+ blanks are \c - and all (alphabetic) characters are converted to
+ uppercase.
+ \endtable
+
+ To get range control (e.g., for an IP address) use masks together
+ with \link setValidator() validators\endlink.
+
+ \sa maxLength
+*/
+QString QLineEdit::inputMask() const
+{
+ Q_D(const QLineEdit);
+ return (d->maskData ? d->inputMask + QLatin1Char(';') + d->blank : QString());
+}
+
+void QLineEdit::setInputMask(const QString &inputMask)
+{
+ Q_D(QLineEdit);
+ d->parseInputMask(inputMask);
+ if (d->maskData)
+ d->moveCursor(d->nextMaskBlank(0));
+}
+
+/*!
+ Selects all the text (i.e. highlights it) and moves the cursor to
+ the end. This is useful when a default value has been inserted
+ because if the user types before clicking on the widget, the
+ selected text will be deleted.
+
+ \sa setSelection() deselect()
+*/
+
+void QLineEdit::selectAll()
+{
+ Q_D(QLineEdit);
+ d->selstart = d->selend = d->cursor = 0;
+ d->moveCursor(d->text.length(), true);
+}
+
+/*!
+ Deselects any selected text.
+
+ \sa setSelection() selectAll()
+*/
+
+void QLineEdit::deselect()
+{
+ Q_D(QLineEdit);
+ d->deselect();
+ d->finishChange();
+}
+
+
+/*!
+ Deletes any selected text, inserts \a newText, and validates the
+ result. If it is valid, it sets it as the new contents of the line
+ edit.
+
+ \sa setText(), clear()
+*/
+void QLineEdit::insert(const QString &newText)
+{
+// q->resetInputContext(); //#### FIX ME IN QT
+ Q_D(QLineEdit);
+ int priorState = d->undoState;
+ d->removeSelectedText();
+ d->insert(newText);
+ d->finishChange(priorState);
+}
+
+/*!
+ Clears the contents of the line edit.
+
+ \sa setText(), insert()
+*/
+void QLineEdit::clear()
+{
+ Q_D(QLineEdit);
+ int priorState = d->undoState;
+ resetInputContext();
+ d->selstart = 0;
+ d->selend = d->text.length();
+ d->removeSelectedText();
+ d->separate();
+ d->finishChange(priorState, /*update*/false, /*edited*/false);
+}
+
+/*!
+ Undoes the last operation if undo is \link
+ QLineEdit::undoAvailable available\endlink. Deselects any current
+ selection, and updates the selection start to the current cursor
+ position.
+*/
+void QLineEdit::undo()
+{
+ Q_D(QLineEdit);
+ resetInputContext();
+ d->undo();
+ d->finishChange(-1, true);
+}
+
+/*!
+ Redoes the last operation if redo is \link
+ QLineEdit::redoAvailable available\endlink.
+*/
+void QLineEdit::redo()
+{
+ Q_D(QLineEdit);
+ resetInputContext();
+ d->redo();
+ d->finishChange();
+}
+
+
+/*!
+ \property QLineEdit::readOnly
+ \brief whether the line edit is read only.
+
+ In read-only mode, the user can still copy the text to the
+ clipboard, or drag and drop the text (if echoMode() is \l Normal),
+ but cannot edit it.
+
+ QLineEdit does not show a cursor in read-only mode.
+
+ By default, this property is false.
+
+ \sa setEnabled()
+*/
+
+bool QLineEdit::isReadOnly() const
+{
+ Q_D(const QLineEdit);
+ return d->readOnly;
+}
+
+void QLineEdit::setReadOnly(bool enable)
+{
+ Q_D(QLineEdit);
+ if (d->readOnly != enable) {
+ d->readOnly = enable;
+ setAttribute(Qt::WA_MacShowFocusRect, !d->readOnly);
+ setAttribute(Qt::WA_InputMethodEnabled, shouldEnableInputMethod(this));
+#ifndef QT_NO_CURSOR
+ setCursor(enable ? Qt::ArrowCursor : Qt::IBeamCursor);
+#endif
+ update();
+ }
+}
+
+
+#ifndef QT_NO_CLIPBOARD
+/*!
+ Copies the selected text to the clipboard and deletes it, if there
+ is any, and if echoMode() is \l Normal.
+
+ If the current validator disallows deleting the selected text,
+ cut() will copy without deleting.
+
+ \sa copy() paste() setValidator()
+*/
+
+void QLineEdit::cut()
+{
+ if (hasSelectedText()) {
+ copy();
+ del();
+ }
+}
+
+
+/*!
+ Copies the selected text to the clipboard, if there is any, and if
+ echoMode() is \l Normal.
+
+ \sa cut() paste()
+*/
+
+void QLineEdit::copy() const
+{
+ Q_D(const QLineEdit);
+ d->copy();
+}
+
+/*!
+ Inserts the clipboard's text at the cursor position, deleting any
+ selected text, providing the line edit is not \link
+ QLineEdit::readOnly read-only\endlink.
+
+ If the end result would not be acceptable to the current
+ \link setValidator() validator\endlink, nothing happens.
+
+ \sa copy() cut()
+*/
+
+void QLineEdit::paste()
+{
+ Q_D(QLineEdit);
+ if (echoMode() == PasswordEchoOnEdit && !d->passwordEchoEditing) {
+ // Clear the edit and reset to normal echo mode when pasting; the echo
+ // mode switches back when the edit loses focus. ### changes a public
+ // property, resets current content
+ d->updatePasswordEchoEditing(true);
+ clear();
+ }
+ insert(QApplication::clipboard()->text(QClipboard::Clipboard));
+}
+
+void QLineEditPrivate::copy(bool clipboard) const
+{
+ Q_Q(const QLineEdit);
+ QString t = q->selectedText();
+ if (!t.isEmpty() && echoMode == QLineEdit::Normal) {
+ q->disconnect(QApplication::clipboard(), SIGNAL(selectionChanged()), q, 0);
+ QApplication::clipboard()->setText(t, clipboard ? QClipboard::Clipboard : QClipboard::Selection);
+ q->connect(QApplication::clipboard(), SIGNAL(selectionChanged()),
+ q, SLOT(_q_clipboardChanged()));
+ }
+}
+
+#endif // !QT_NO_CLIPBOARD
+
+/*! \reimp
+*/
+bool QLineEdit::event(QEvent * e)
+{
+ Q_D(QLineEdit);
+#ifndef QT_NO_SHORTCUT
+ if (e->type() == QEvent::ShortcutOverride && !d->readOnly) {
+ QKeyEvent* ke = (QKeyEvent*) e;
+ if (ke == QKeySequence::Copy
+ || ke == QKeySequence::Paste
+ || ke == QKeySequence::Cut
+ || ke == QKeySequence::Redo
+ || ke == QKeySequence::Undo
+ || ke == QKeySequence::MoveToNextWord
+ || ke == QKeySequence::MoveToPreviousWord
+ || ke == QKeySequence::MoveToStartOfDocument
+ || ke == QKeySequence::MoveToEndOfDocument
+ || ke == QKeySequence::SelectNextWord
+ || ke == QKeySequence::SelectPreviousWord
+ || ke == QKeySequence::SelectStartOfLine
+ || ke == QKeySequence::SelectEndOfLine
+ || ke == QKeySequence::SelectStartOfBlock
+ || ke == QKeySequence::SelectEndOfBlock
+ || ke == QKeySequence::SelectStartOfDocument
+ || ke == QKeySequence::SelectAll
+ || ke == QKeySequence::SelectEndOfDocument) {
+ ke->accept();
+ } else if (ke->modifiers() == Qt::NoModifier || ke->modifiers() == Qt::ShiftModifier
+ || ke->modifiers() == Qt::KeypadModifier) {
+ if (ke->key() < Qt::Key_Escape) {
+ ke->accept();
+ } else {
+ switch (ke->key()) {
+ 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;
+ }
+ }
+ }
+ } else
+#endif
+ if (e->type() == QEvent::Timer) {
+ // should be timerEvent, is here for binary compatibility
+ int timerId = ((QTimerEvent*)e)->timerId();
+ if (timerId == d->cursorTimer) {
+ QStyleOptionFrameV2 opt;
+ initStyleOption(&opt);
+ if(!hasSelectedText()
+ || style()->styleHint(QStyle::SH_BlinkCursorWhenTextSelected, &opt, this))
+ d->setCursorVisible(!d->cursorVisible);
+#ifndef QT_NO_DRAGANDDROP
+ } else if (timerId == d->dndTimer.timerId()) {
+ d->drag();
+#endif
+ }
+ else if (timerId == d->tripleClickTimer.timerId())
+ d->tripleClickTimer.stop();
+#ifdef QT_KEYPAD_NAVIGATION
+ else if (timerId == d->deleteAllTimer.timerId()) {
+ d->deleteAllTimer.stop();
+ clear();
+ }
+#endif
+ } else if (e->type() == QEvent::ContextMenu) {
+#ifndef QT_NO_IM
+ if (d->composeMode())
+ return true;
+#endif
+ d->separate();
+ } else if (e->type() == QEvent::WindowActivate) {
+ QTimer::singleShot(0, this, SLOT(_q_handleWindowActivate()));
+ }
+#ifdef QT_KEYPAD_NAVIGATION
+ if (QApplication::keypadNavigationEnabled()) {
+ if ((e->type() == QEvent::KeyPress) || (e->type() == QEvent::KeyRelease)) {
+ QKeyEvent *ke = (QKeyEvent *)e;
+ if (ke->key() == Qt::Key_Back) {
+ if (ke->isAutoRepeat()) {
+ // Swallow it. We don't want back keys running amok.
+ ke->accept();
+ return true;
+ }
+ if ((e->type() == QEvent::KeyRelease)
+ && !isReadOnly()
+ && d->deleteAllTimer.isActive()) {
+ d->deleteAllTimer.stop();
+ backspace();
+ ke->accept();
+ return true;
+ }
+ }
+ } else if (e->type() == QEvent::EnterEditFocus) {
+ end(false);
+ if (!d->cursorTimer) {
+ int cft = QApplication::cursorFlashTime();
+ d->cursorTimer = cft ? startTimer(cft/2) : -1;
+ }
+ } else if (e->type() == QEvent::LeaveEditFocus) {
+ d->setCursorVisible(false);
+ if (d->cursorTimer > 0)
+ killTimer(d->cursorTimer);
+ d->cursorTimer = 0;
+
+ if (!d->emitingEditingFinished) {
+ if (hasAcceptableInput() || d->fixup()) {
+ d->emitingEditingFinished = true;
+ emit editingFinished();
+ d->emitingEditingFinished = false;
+ }
+ }
+ }
+ }
+#endif
+ return QWidget::event(e);
+}
+
+/*! \reimp
+*/
+void QLineEdit::mousePressEvent(QMouseEvent* e)
+{
+ Q_D(QLineEdit);
+ if (d->sendMouseEventToInputContext(e))
+ return;
+ if (e->button() == Qt::RightButton)
+ return;
+#ifdef QT_KEYPAD_NAVIGATION
+ if (QApplication::keypadNavigationEnabled() && !hasEditFocus()) {
+ setEditFocus(true);
+ // Get the completion list to pop up.
+ if (d->completer)
+ d->completer->complete();
+ }
+#endif
+ if (d->tripleClickTimer.isActive() && (e->pos() - d->tripleClick).manhattanLength() <
+ QApplication::startDragDistance()) {
+ selectAll();
+ return;
+ }
+ bool mark = e->modifiers() & Qt::ShiftModifier;
+ int cursor = d->xToPos(e->pos().x());
+#ifndef QT_NO_DRAGANDDROP
+ if (!mark && d->dragEnabled && d->echoMode == Normal &&
+ e->button() == Qt::LeftButton && d->inSelection(e->pos().x())) {
+ d->cursor = cursor;
+ update();
+ d->dndPos = e->pos();
+ if (!d->dndTimer.isActive())
+ d->dndTimer.start(QApplication::startDragTime(), this);
+ d->emitCursorPositionChanged();
+ } else
+#endif
+ {
+ d->moveCursor(cursor, mark);
+ }
+}
+
+/*! \reimp
+*/
+void QLineEdit::mouseMoveEvent(QMouseEvent * e)
+{
+ Q_D(QLineEdit);
+ if (d->sendMouseEventToInputContext(e))
+ return;
+
+ if (e->buttons() & Qt::LeftButton) {
+#ifndef QT_NO_DRAGANDDROP
+ if (d->dndTimer.isActive()) {
+ if ((d->dndPos - e->pos()).manhattanLength() > QApplication::startDragDistance())
+ d->drag();
+ } else
+#endif
+ {
+ d->moveCursor(d->xToPos(e->pos().x()), true);
+ }
+ }
+}
+
+/*! \reimp
+*/
+void QLineEdit::mouseReleaseEvent(QMouseEvent* e)
+{
+ Q_D(QLineEdit);
+ if (d->sendMouseEventToInputContext(e))
+ return;
+#ifndef QT_NO_DRAGANDDROP
+ if (e->button() == Qt::LeftButton) {
+ if (d->dndTimer.isActive()) {
+ d->dndTimer.stop();
+ deselect();
+ return;
+ }
+ }
+#endif
+#ifndef QT_NO_CLIPBOARD
+ if (QApplication::clipboard()->supportsSelection()) {
+ if (e->button() == Qt::LeftButton) {
+ d->copy(false);
+ } else if (!d->readOnly && e->button() == Qt::MidButton) {
+ d->deselect();
+ insert(QApplication::clipboard()->text(QClipboard::Selection));
+ }
+ }
+#endif
+}
+
+/*! \reimp
+*/
+void QLineEdit::mouseDoubleClickEvent(QMouseEvent* e)
+{
+ Q_D(QLineEdit);
+ if (d->sendMouseEventToInputContext(e))
+ return;
+ if (e->button() == Qt::LeftButton) {
+ deselect();
+ d->cursor = d->xToPos(e->pos().x());
+ d->cursor = d->textLayout.previousCursorPosition(d->cursor, QTextLayout::SkipWords);
+ // ## text layout should support end of words.
+ int end = d->textLayout.nextCursorPosition(d->cursor, QTextLayout::SkipWords);
+ while (end > d->cursor && d->text[end-1].isSpace())
+ --end;
+ d->moveCursor(end, true);
+ d->tripleClickTimer.start(QApplication::doubleClickInterval(), this);
+ d->tripleClick = e->pos();
+ }
+}
+
+/*!
+ \fn void QLineEdit::returnPressed()
+
+ This signal is emitted when the Return or Enter key is pressed.
+ Note that if there is a validator() or inputMask() set on the line
+ edit, the returnPressed() signal will only be emitted if the input
+ follows the inputMask() and the validator() returns
+ QValidator::Acceptable.
+*/
+
+/*!
+ \fn void QLineEdit::editingFinished()
+
+ This signal is emitted when the Return or Enter key is pressed or
+ the line edit loses focus. Note that if there is a validator() or
+ inputMask() set on the line edit and enter/return is pressed, the
+ editingFinished() signal will only be emitted if the input follows
+ the inputMask() and the validator() returns QValidator::Acceptable.
+*/
+
+/*!
+ Converts the given key press \a event into a line edit action.
+
+ If Return or Enter is pressed and the current text is valid (or
+ can be \link QValidator::fixup() made valid\endlink by the
+ validator), the signal returnPressed() is emitted.
+
+ The default key bindings are listed in the class's detailed
+ description.
+*/
+
+void QLineEdit::keyPressEvent(QKeyEvent *event)
+{
+ Q_D(QLineEdit);
+
+ bool inlineCompletionAccepted = false;
+
+#ifndef QT_NO_COMPLETER
+ if (d->completer) {
+ QCompleter::CompletionMode completionMode = d->completer->completionMode();
+ if ((completionMode == QCompleter::PopupCompletion
+ || completionMode == QCompleter::UnfilteredPopupCompletion)
+ &&d->completer->popup()
+ && d->completer->popup()->isVisible()) {
+ // The following keys are forwarded by the completer to the widget
+ // Ignoring the events lets the completer provide suitable default behavior
+ switch (event->key()) {
+ case Qt::Key_Escape:
+ event->ignore();
+ return;
+ case Qt::Key_Enter:
+ case Qt::Key_Return:
+ case Qt::Key_F4:
+#ifdef QT_KEYPAD_NAVIGATION
+ case Qt::Key_Select:
+ if (!QApplication::keypadNavigationEnabled())
+ break;
+#endif
+ d->completer->popup()->hide(); // just hide. will end up propagating to parent
+ default:
+ break; // normal key processing
+ }
+ } else if (completionMode == QCompleter::InlineCompletion) {
+ switch (event->key()) {
+ case Qt::Key_Enter:
+ case Qt::Key_Return:
+ case Qt::Key_F4:
+#ifdef QT_KEYPAD_NAVIGATION
+ case Qt::Key_Select:
+ if (!QApplication::keypadNavigationEnabled())
+ break;
+#endif
+ if (!d->completer->currentCompletion().isEmpty() && d->selend > d->selstart
+ && d->selend == d->text.length()) {
+ setText(d->completer->currentCompletion());
+ inlineCompletionAccepted = true;
+ }
+ default:
+ break; // normal key processing
+ }
+ }
+ }
+#endif // QT_NO_COMPLETER
+
+#ifdef QT_KEYPAD_NAVIGATION
+ bool select = false;
+ switch (event->key()) {
+ case Qt::Key_Select:
+ if (QApplication::keypadNavigationEnabled()) {
+ if (hasEditFocus()) {
+ setEditFocus(false);
+ if (d->completer && d->completer->popup()->isVisible())
+ d->completer->popup()->hide();
+ select = true;
+ }
+ }
+ break;
+ case Qt::Key_Back:
+ case Qt::Key_No:
+ if (!QApplication::keypadNavigationEnabled() || !hasEditFocus()) {
+ event->ignore();
+ return;
+ }
+ break;
+ default:
+ if (QApplication::keypadNavigationEnabled()) {
+ if (!hasEditFocus() && !(event->modifiers() & Qt::ControlModifier)) {
+ if (!event->text().isEmpty() && event->text().at(0).isPrint()
+ && !isReadOnly())
+ {
+ setEditFocus(true);
+ clear();
+ } else {
+ event->ignore();
+ return;
+ }
+ }
+ }
+ }
+
+
+
+ if (QApplication::keypadNavigationEnabled() && !select && !hasEditFocus()) {
+ setEditFocus(true);
+ if (event->key() == Qt::Key_Select)
+ return; // Just start. No action.
+ }
+#endif
+
+ if (echoMode() == PasswordEchoOnEdit
+ && !d->passwordEchoEditing
+ && !isReadOnly()
+ && !event->text().isEmpty()
+#ifdef QT_KEYPAD_NAVIGATION
+ && event->key() != Qt::Key_Select
+ && event->key() != Qt::Key_Up
+ && event->key() != Qt::Key_Down
+ && event->key() != Qt::Key_Back
+#endif
+ && !(event->modifiers() & Qt::ControlModifier)) {
+ // Clear the edit and reset to normal echo mode while editing; the
+ // echo mode switches back when the edit loses focus. ### changes a
+ // public property, resets current content. dubious code; you can
+ // navigate with keys up, down, back, and select(?), but if you press
+ // "left" or "right" it clears?
+ d->updatePasswordEchoEditing(true);
+ clear();
+ }
+
+ d->setCursorVisible(true);
+ if (event->key() == Qt::Key_Enter || event->key() == Qt::Key_Return) {
+ if (hasAcceptableInput() || d->fixup()) {
+ emit returnPressed();
+ d->emitingEditingFinished = true;
+ emit editingFinished();
+ d->emitingEditingFinished = false;
+ }
+ if (inlineCompletionAccepted)
+ event->accept();
+ else
+ event->ignore();
+ return;
+ }
+ bool unknown = false;
+
+ if (false) {
+ }
+#ifndef QT_NO_SHORTCUT
+ else if (event == QKeySequence::Undo) {
+ if (!d->readOnly)
+ undo();
+ }
+ else if (event == QKeySequence::Redo) {
+ if (!d->readOnly)
+ redo();
+ }
+ else if (event == QKeySequence::SelectAll) {
+ selectAll();
+ }
+#ifndef QT_NO_CLIPBOARD
+ else if (event == QKeySequence::Copy) {
+ copy();
+ }
+ else if (event == QKeySequence::Paste) {
+ if (!d->readOnly)
+ paste();
+ }
+ else if (event == QKeySequence::Cut) {
+ if (!d->readOnly) {
+ cut();
+ }
+ }
+ else if (event == QKeySequence::DeleteEndOfLine) {
+ if (!d->readOnly) {
+ setSelection(d->cursor, d->text.size());
+ copy();
+ del();
+ }
+ }
+#endif //QT_NO_CLIPBOARD
+ else if (event == QKeySequence::MoveToStartOfLine) {
+ home(0);
+ }
+ else if (event == QKeySequence::MoveToEndOfLine) {
+ end(0);
+ }
+ else if (event == QKeySequence::SelectStartOfLine) {
+ home(1);
+ }
+ else if (event == QKeySequence::SelectEndOfLine) {
+ end(1);
+ }
+ else if (event == QKeySequence::MoveToNextChar) {
+#if !defined(Q_WS_WIN) || defined(QT_NO_COMPLETER)
+ if (d->hasSelectedText()) {
+#else
+ if (d->hasSelectedText() && d->completer
+ && d->completer->completionMode() == QCompleter::InlineCompletion) {
+#endif
+ d->moveCursor(d->selend, false);
+ } else {
+ cursorForward(0, layoutDirection() == Qt::LeftToRight ? 1 : -1);
+ }
+ }
+ else if (event == QKeySequence::SelectNextChar) {
+ cursorForward(1, layoutDirection() == Qt::LeftToRight ? 1 : -1);
+ }
+ else if (event == QKeySequence::MoveToPreviousChar) {
+#if !defined(Q_WS_WIN) || defined(QT_NO_COMPLETER)
+ if (d->hasSelectedText()) {
+#else
+ if (d->hasSelectedText() && d->completer
+ && d->completer->completionMode() == QCompleter::InlineCompletion) {
+#endif
+ d->moveCursor(d->selstart, false);
+ } else {
+ cursorBackward(0, layoutDirection() == Qt::LeftToRight ? 1 : -1);
+ }
+ }
+ else if (event == QKeySequence::SelectPreviousChar) {
+ cursorBackward(1, layoutDirection() == Qt::LeftToRight ? 1 : -1);
+ }
+ else if (event == QKeySequence::MoveToNextWord) {
+ if (echoMode() == Normal)
+ layoutDirection() == Qt::LeftToRight ? cursorWordForward(0) : cursorWordBackward(0);
+ else
+ layoutDirection() == Qt::LeftToRight ? end(0) : home(0);
+ }
+ else if (event == QKeySequence::MoveToPreviousWord) {
+ if (echoMode() == Normal)
+ layoutDirection() == Qt::LeftToRight ? cursorWordBackward(0) : cursorWordForward(0);
+ else if (!d->readOnly) {
+ layoutDirection() == Qt::LeftToRight ? home(0) : end(0);
+ }
+ }
+ else if (event == QKeySequence::SelectNextWord) {
+ if (echoMode() == Normal)
+ layoutDirection() == Qt::LeftToRight ? cursorWordForward(1) : cursorWordBackward(1);
+ else
+ layoutDirection() == Qt::LeftToRight ? end(1) : home(1);
+ }
+ else if (event == QKeySequence::SelectPreviousWord) {
+ if (echoMode() == Normal)
+ layoutDirection() == Qt::LeftToRight ? cursorWordBackward(1) : cursorWordForward(1);
+ else
+ layoutDirection() == Qt::LeftToRight ? home(1) : end(1);
+ }
+ else if (event == QKeySequence::Delete) {
+ if (!d->readOnly)
+ del();
+ }
+ else if (event == QKeySequence::DeleteEndOfWord) {
+ if (!d->readOnly) {
+ cursorWordForward(true);
+ del();
+ }
+ }
+ else if (event == QKeySequence::DeleteStartOfWord) {
+ if (!d->readOnly) {
+ cursorWordBackward(true);
+ del();
+ }
+ }
+#endif // QT_NO_SHORTCUT
+ else {
+#ifdef Q_WS_MAC
+ if (event->key() == Qt::Key_Up || event->key() == Qt::Key_Down) {
+ Qt::KeyboardModifiers myModifiers = (event->modifiers() & ~Qt::KeypadModifier);
+ if (myModifiers & Qt::ShiftModifier) {
+ if (myModifiers == (Qt::ControlModifier|Qt::ShiftModifier)
+ || myModifiers == (Qt::AltModifier|Qt::ShiftModifier)
+ || myModifiers == Qt::ShiftModifier) {
+
+ event->key() == Qt::Key_Up ? home(1) : end(1);
+ }
+ } else {
+ if ((myModifiers == Qt::ControlModifier
+ || myModifiers == Qt::AltModifier
+ || myModifiers == Qt::NoModifier)) {
+ event->key() == Qt::Key_Up ? home(0) : end(0);
+ }
+ }
+ }
+#endif
+ if (event->modifiers() & Qt::ControlModifier) {
+ switch (event->key()) {
+ case Qt::Key_Backspace:
+ if (!d->readOnly) {
+ cursorWordBackward(true);
+ del();
+ }
+ break;
+#ifndef QT_NO_COMPLETER
+ case Qt::Key_Up:
+ case Qt::Key_Down:
+ d->complete(event->key());
+ break;
+#endif
+#if defined(Q_WS_X11)
+ case Qt::Key_E:
+ end(0);
+ break;
+
+ case Qt::Key_U:
+ if (!d->readOnly) {
+ setSelection(0, d->text.size());
+#ifndef QT_NO_CLIPBOARD
+ copy();
+#endif
+ del();
+ }
+ break;
+#endif
+ default:
+ unknown = true;
+ }
+ } else { // ### check for *no* modifier
+ switch (event->key()) {
+ case Qt::Key_Backspace:
+ if (!d->readOnly) {
+ backspace();
+#ifndef QT_NO_COMPLETER
+ d->complete(Qt::Key_Backspace);
+#endif
+ }
+ break;
+#ifdef QT_KEYPAD_NAVIGATION
+ case Qt::Key_Back:
+ if (QApplication::keypadNavigationEnabled() && !event->isAutoRepeat()
+ && !isReadOnly()) {
+ if (text().length() == 0) {
+ setText(d->origText);
+
+ if (d->passwordEchoEditing)
+ d->updatePasswordEchoEditing(false);
+
+ setEditFocus(false);
+ } else if (!d->deleteAllTimer.isActive()) {
+ d->deleteAllTimer.start(750, this);
+ }
+ } else {
+ unknown = true;
+ }
+ break;
+#endif
+
+ default:
+ unknown = true;
+ }
+ }
+ }
+
+ if (event->key() == Qt::Key_Direction_L || event->key() == Qt::Key_Direction_R) {
+ setLayoutDirection((event->key() == Qt::Key_Direction_L) ? Qt::LeftToRight : Qt::RightToLeft);
+ d->updateTextLayout();
+ update();
+ unknown = false;
+ }
+
+ if (unknown && !d->readOnly) {
+ QString t = event->text();
+ if (!t.isEmpty() && t.at(0).isPrint()) {
+ insert(t);
+#ifndef QT_NO_COMPLETER
+ d->complete(event->key());
+#endif
+ event->accept();
+ return;
+ }
+ }
+
+ if (unknown)
+ event->ignore();
+ else
+ event->accept();
+}
+
+/*!
+ \since 4.4
+
+ Returns a rectangle that includes the lineedit cursor.
+*/
+QRect QLineEdit::cursorRect() const
+{
+ Q_D(const QLineEdit);
+ return d->cursorRect();
+}
+
+/*!
+ This function is not intended as polymorphic usage. Just a shared code
+ fragment that calls QInputContext::mouseHandler for this
+ class.
+*/
+bool QLineEditPrivate::sendMouseEventToInputContext( QMouseEvent *e )
+{
+#if !defined QT_NO_IM
+ Q_Q(QLineEdit);
+ if ( composeMode() ) {
+ int tmp_cursor = xToPos(e->pos().x());
+ int mousePos = tmp_cursor - cursor;
+ if ( mousePos < 0 || mousePos > textLayout.preeditAreaText().length() ) {
+ mousePos = -1;
+ // don't send move events outside the preedit area
+ if ( e->type() == QEvent::MouseMove )
+ return true;
+ }
+
+ QInputContext *qic = q->inputContext();
+ if ( qic )
+ // may be causing reset() in some input methods
+ qic->mouseHandler(mousePos, e);
+ if (!textLayout.preeditAreaText().isEmpty())
+ return true;
+ }
+#else
+ Q_UNUSED(e);
+#endif
+
+ return false;
+}
+
+/*! \reimp
+ */
+void QLineEdit::inputMethodEvent(QInputMethodEvent *e)
+{
+ Q_D(QLineEdit);
+ if (d->readOnly) {
+ e->ignore();
+ return;
+ }
+
+ if (echoMode() == PasswordEchoOnEdit && !d->passwordEchoEditing) {
+ // Clear the edit and reset to normal echo mode while entering input
+ // method data; the echo mode switches back when the edit loses focus.
+ // ### changes a public property, resets current content.
+ d->updatePasswordEchoEditing(true);
+ clear();
+ }
+
+#ifdef QT_KEYPAD_NAVIGATION
+ // Focus in if currently in navigation focus on the widget
+ // Only focus in on preedits, to allow input methods to
+ // commit text as they focus out without interfering with focus
+ if (QApplication::keypadNavigationEnabled()
+ && hasFocus() && !hasEditFocus()
+ && !e->preeditString().isEmpty()) {
+ setEditFocus(true);
+ selectAll(); // so text is replaced rather than appended to
+ }
+#endif
+
+ int priorState = d->undoState;
+ d->removeSelectedText();
+
+ int c = d->cursor; // cursor position after insertion of commit string
+ if (e->replacementStart() <= 0)
+ c += e->commitString().length() + qMin(-e->replacementStart(), e->replacementLength());
+
+ d->cursor += e->replacementStart();
+
+ // insert commit string
+ if (e->replacementLength()) {
+ d->selstart = d->cursor;
+ d->selend = d->selstart + e->replacementLength();
+ d->removeSelectedText();
+ }
+ if (!e->commitString().isEmpty())
+ d->insert(e->commitString());
+
+ d->cursor = qMin(c, d->text.length());
+
+ d->textLayout.setPreeditArea(d->cursor, e->preeditString());
+ d->preeditCursor = e->preeditString().length();
+ d->hideCursor = false;
+ QList<QTextLayout::FormatRange> formats;
+ for (int i = 0; i < e->attributes().size(); ++i) {
+ const QInputMethodEvent::Attribute &a = e->attributes().at(i);
+ if (a.type == QInputMethodEvent::Cursor) {
+ d->preeditCursor = a.start;
+ d->hideCursor = !a.length;
+ } else if (a.type == QInputMethodEvent::TextFormat) {
+ QTextCharFormat f = qvariant_cast<QTextFormat>(a.value).toCharFormat();
+ if (f.isValid()) {
+ QTextLayout::FormatRange o;
+ o.start = a.start + d->cursor;
+ o.length = a.length;
+ o.format = f;
+ formats.append(o);
+ }
+ }
+ }
+ d->textLayout.setAdditionalFormats(formats);
+ d->updateTextLayout();
+ update();
+ if (!e->commitString().isEmpty())
+ d->emitCursorPositionChanged();
+ d->finishChange(priorState);
+#ifndef QT_NO_COMPLETER
+ if (!e->commitString().isEmpty())
+ d->complete(Qt::Key_unknown);
+#endif
+}
+
+/*!\reimp
+*/
+QVariant QLineEdit::inputMethodQuery(Qt::InputMethodQuery property) const
+{
+ Q_D(const QLineEdit);
+ switch(property) {
+ case Qt::ImMicroFocus:
+ return d->cursorRect();
+ case Qt::ImFont:
+ return font();
+ case Qt::ImCursorPosition:
+ return QVariant((d->selend - d->selstart == 0) ? d->cursor : d->selend);
+ case Qt::ImSurroundingText:
+ return QVariant(d->text);
+ case Qt::ImCurrentSelection:
+ return QVariant(selectedText());
+ default:
+ return QVariant();
+ }
+}
+
+/*!\reimp
+*/
+
+void QLineEdit::focusInEvent(QFocusEvent *e)
+{
+ Q_D(QLineEdit);
+ if (e->reason() == Qt::TabFocusReason ||
+ e->reason() == Qt::BacktabFocusReason ||
+ e->reason() == Qt::ShortcutFocusReason) {
+ if (d->maskData)
+ d->moveCursor(d->nextMaskBlank(0));
+ else if (!d->hasSelectedText())
+ selectAll();
+ }
+#ifdef QT_KEYPAD_NAVIGATION
+ if (!QApplication::keypadNavigationEnabled() || (hasEditFocus() && e->reason() == Qt::PopupFocusReason))
+#endif
+ if (!d->cursorTimer) {
+ int cft = QApplication::cursorFlashTime();
+ d->cursorTimer = cft ? startTimer(cft/2) : -1;
+ }
+ QStyleOptionFrameV2 opt;
+ initStyleOption(&opt);
+ if((!hasSelectedText() && d->textLayout.preeditAreaText().isEmpty())
+ || style()->styleHint(QStyle::SH_BlinkCursorWhenTextSelected, &opt, this))
+ d->setCursorVisible(true);
+#ifdef Q_WS_MAC
+ if (d->echoMode == Password || d->echoMode == NoEcho)
+ qt_mac_secure_keyboard(true);
+#endif
+#ifdef QT_KEYPAD_NAVIGATION
+ d->origText = d->text;
+#endif
+#ifndef QT_NO_COMPLETER
+ if (d->completer) {
+ d->completer->setWidget(this);
+ QObject::connect(d->completer, SIGNAL(activated(QString)),
+ this, SLOT(setText(QString)));
+ QObject::connect(d->completer, SIGNAL(highlighted(QString)),
+ this, SLOT(_q_completionHighlighted(QString)));
+ }
+#endif
+ update();
+}
+
+/*!\reimp
+*/
+
+void QLineEdit::focusOutEvent(QFocusEvent *e)
+{
+ Q_D(QLineEdit);
+ if (d->passwordEchoEditing) {
+ // Reset the echomode back to PasswordEchoOnEdit when the widget loses
+ // focus.
+ d->updatePasswordEchoEditing(false);
+ }
+
+ Qt::FocusReason reason = e->reason();
+ if (reason != Qt::ActiveWindowFocusReason &&
+ reason != Qt::PopupFocusReason)
+ deselect();
+
+ d->setCursorVisible(false);
+ if (d->cursorTimer > 0)
+ killTimer(d->cursorTimer);
+ d->cursorTimer = 0;
+
+#ifdef QT_KEYPAD_NAVIGATION
+ // editingFinished() is already emitted on LeaveEditFocus
+ if (!QApplication::keypadNavigationEnabled())
+#endif
+ if (reason != Qt::PopupFocusReason
+ || !(QApplication::activePopupWidget() && QApplication::activePopupWidget()->parentWidget() == this)) {
+ if (!d->emitingEditingFinished) {
+ if (hasAcceptableInput() || d->fixup()) {
+ d->emitingEditingFinished = true;
+ emit editingFinished();
+ d->emitingEditingFinished = false;
+ }
+ }
+#ifdef QT3_SUPPORT
+ emit lostFocus();
+#endif
+ }
+#ifdef Q_WS_MAC
+ if (d->echoMode == Password || d->echoMode == NoEcho)
+ qt_mac_secure_keyboard(false);
+#endif
+#ifdef QT_KEYPAD_NAVIGATION
+ d->origText = QString();
+#endif
+#ifndef QT_NO_COMPLETER
+ if (d->completer) {
+ QObject::disconnect(d->completer, 0, this, 0);
+ }
+#endif
+ update();
+}
+
+/*!\reimp
+*/
+void QLineEdit::paintEvent(QPaintEvent *)
+{
+ Q_D(QLineEdit);
+ QPainter p(this);
+
+ QRect r = rect();
+ QPalette pal = palette();
+
+ QStyleOptionFrameV2 panel;
+ initStyleOption(&panel);
+ style()->drawPrimitive(QStyle::PE_PanelLineEdit, &panel, &p, this);
+ r = style()->subElementRect(QStyle::SE_LineEditContents, &panel, this);
+ r.setX(r.x() + d->leftTextMargin);
+ r.setY(r.y() + d->topTextMargin);
+ r.setRight(r.right() - d->rightTextMargin);
+ r.setBottom(r.bottom() - d->bottomTextMargin);
+ p.setClipRect(r);
+
+ QFontMetrics fm = fontMetrics();
+ Qt::Alignment va = QStyle::visualAlignment(layoutDirection(), QFlag(d->alignment));
+ switch (va & Qt::AlignVertical_Mask) {
+ case Qt::AlignBottom:
+ d->vscroll = r.y() + r.height() - fm.height() - verticalMargin;
+ break;
+ case Qt::AlignTop:
+ d->vscroll = r.y() + verticalMargin;
+ break;
+ default:
+ //center
+ d->vscroll = r.y() + (r.height() - fm.height() + 1) / 2;
+ break;
+ }
+ QRect lineRect(r.x() + horizontalMargin, d->vscroll, r.width() - 2*horizontalMargin, fm.height());
+ QTextLine line = d->textLayout.lineAt(0);
+
+ int cursor = d->cursor;
+ if (d->preeditCursor != -1)
+ cursor += d->preeditCursor;
+ // locate cursor position
+ int cix = qRound(line.cursorToX(cursor));
+
+ // horizontal scrolling. d->hscroll is the left indent from the beginning
+ // of the text line to the left edge of lineRect. we update this value
+ // depending on the delta from the last paint event; in effect this means
+ // the below code handles all scrolling based on the textline (widthUsed,
+ // minLB, minRB), the line edit rect (lineRect) and the cursor position
+ // (cix).
+ int minLB = qMax(0, -fm.minLeftBearing());
+ int minRB = qMax(0, -fm.minRightBearing());
+ int widthUsed = qRound(line.naturalTextWidth()) + 1 + minRB;
+ if ((minLB + widthUsed) <= lineRect.width()) {
+ // text fits in lineRect; use hscroll for alignment
+ switch (va & ~(Qt::AlignAbsolute|Qt::AlignVertical_Mask)) {
+ case Qt::AlignRight:
+ d->hscroll = widthUsed - lineRect.width() + 1;
+ break;
+ case Qt::AlignHCenter:
+ d->hscroll = (widthUsed - lineRect.width()) / 2;
+ break;
+ default:
+ // Left
+ d->hscroll = 0;
+ break;
+ }
+ d->hscroll -= minLB;
+ } else if (cix - d->hscroll >= lineRect.width()) {
+ // text doesn't fit, cursor is to the right of lineRect (scroll right)
+ d->hscroll = cix - lineRect.width() + 1;
+ } else if (cix - d->hscroll < 0 && d->hscroll < widthUsed) {
+ // text doesn't fit, cursor is to the left of lineRect (scroll left)
+ d->hscroll = cix;
+ } else if (widthUsed - d->hscroll < lineRect.width()) {
+ // text doesn't fit, text document is to the left of lineRect; align
+ // right
+ d->hscroll = widthUsed - lineRect.width() + 1;
+ }
+ // the y offset is there to keep the baseline constant in case we have script changes in the text.
+ QPoint topLeft = lineRect.topLeft() - QPoint(d->hscroll, d->ascent - fm.ascent());
+
+ // draw text, selections and cursors
+#ifndef QT_NO_STYLE_STYLESHEET
+ if (QStyleSheetStyle* cssStyle = qobject_cast<QStyleSheetStyle*>(style())) {
+ cssStyle->focusPalette(this, &panel, &pal);
+ }
+#endif
+ p.setPen(pal.text().color());
+
+ QVector<QTextLayout::FormatRange> selections;
+#ifdef QT_KEYPAD_NAVIGATION
+ if (!QApplication::keypadNavigationEnabled() || hasEditFocus())
+#endif
+ if (d->selstart < d->selend || (d->cursorVisible && d->maskData && !d->readOnly)) {
+ QTextLayout::FormatRange o;
+ if (d->selstart < d->selend) {
+ o.start = d->selstart;
+ o.length = d->selend - d->selstart;
+ o.format.setBackground(pal.brush(QPalette::Highlight));
+ o.format.setForeground(pal.brush(QPalette::HighlightedText));
+ } else {
+ // mask selection
+ o.start = d->cursor;
+ o.length = 1;
+ o.format.setBackground(pal.brush(QPalette::Text));
+ o.format.setForeground(pal.brush(QPalette::Window));
+ }
+ selections.append(o);
+ }
+
+ // Asian users see an IM selection text as cursor on candidate
+ // selection phase of input method, so the ordinary cursor should be
+ // invisible if we have a preedit string.
+ d->textLayout.draw(&p, topLeft, selections, r);
+ if (d->cursorVisible && !d->readOnly && !d->hideCursor)
+ d->textLayout.drawCursor(&p, topLeft, cursor, style()->pixelMetric(QStyle::PM_TextCursorWidth));
+}
+
+
+#ifndef QT_NO_DRAGANDDROP
+/*!\reimp
+*/
+void QLineEdit::dragMoveEvent(QDragMoveEvent *e)
+{
+ Q_D(QLineEdit);
+ if (!d->readOnly && e->mimeData()->hasFormat(QLatin1String("text/plain"))) {
+ e->acceptProposedAction();
+ d->cursor = d->xToPos(e->pos().x());
+ d->cursorVisible = true;
+ update();
+ d->emitCursorPositionChanged();
+ }
+}
+
+/*!\reimp */
+void QLineEdit::dragEnterEvent(QDragEnterEvent * e)
+{
+ QLineEdit::dragMoveEvent(e);
+}
+
+/*!\reimp */
+void QLineEdit::dragLeaveEvent(QDragLeaveEvent *)
+{
+ Q_D(QLineEdit);
+ if (d->cursorVisible) {
+ d->cursorVisible = false;
+ update();
+ }
+}
+
+/*!\reimp */
+void QLineEdit::dropEvent(QDropEvent* e)
+{
+ Q_D(QLineEdit);
+ QString str = e->mimeData()->text();
+
+ if (!str.isNull() && !d->readOnly) {
+ if (e->source() == this && e->dropAction() == Qt::CopyAction)
+ deselect();
+ d->cursor =d->xToPos(e->pos().x());
+ int selStart = d->cursor;
+ int oldSelStart = d->selstart;
+ int oldSelEnd = d->selend;
+ d->cursorVisible = false;
+ e->acceptProposedAction();
+ insert(str);
+ if (e->source() == this) {
+ if (e->dropAction() == Qt::MoveAction) {
+ if (selStart > oldSelStart && selStart <= oldSelEnd)
+ setSelection(oldSelStart, str.length());
+ else if (selStart > oldSelEnd)
+ setSelection(selStart - str.length(), str.length());
+ else
+ setSelection(selStart, str.length());
+ } else {
+ setSelection(selStart, str.length());
+ }
+ }
+ } else {
+ e->ignore();
+ update();
+ }
+}
+
+void QLineEditPrivate::drag()
+{
+ Q_Q(QLineEdit);
+ dndTimer.stop();
+ QMimeData *data = new QMimeData;
+ data->setText(q->selectedText());
+ QDrag *drag = new QDrag(q);
+ drag->setMimeData(data);
+ Qt::DropAction action = drag->start();
+ if (action == Qt::MoveAction && !readOnly && drag->target() != q) {
+ int priorState = undoState;
+ removeSelectedText();
+ finishChange(priorState);
+ }
+}
+
+#endif // QT_NO_DRAGANDDROP
+
+#ifndef QT_NO_CONTEXTMENU
+/*!
+ Shows the standard context menu created with
+ createStandardContextMenu().
+
+ If you do not want the line edit to have a context menu, you can set
+ its \l contextMenuPolicy to Qt::NoContextMenu. If you want to
+ customize the context menu, reimplement this function. If you want
+ to extend the standard context menu, reimplement this function, call
+ createStandardContextMenu() and extend the menu returned.
+
+ \snippet doc/src/snippets/code/src_gui_widgets_qlineedit.cpp 0
+
+ The \a event parameter is used to obtain the position where
+ the mouse cursor was when the event was generated.
+
+ \sa setContextMenuPolicy()
+*/
+void QLineEdit::contextMenuEvent(QContextMenuEvent *event)
+{
+ QPointer<QMenu> menu = createStandardContextMenu();
+ menu->exec(event->globalPos());
+ delete menu;
+}
+
+#if defined(Q_WS_WIN)
+ extern bool qt_use_rtl_extensions;
+#endif
+
+/*! This function creates the standard context menu which is shown
+ when the user clicks on the line edit with the right mouse
+ button. It is called from the default contextMenuEvent() handler.
+ The popup menu's ownership is transferred to the caller.
+*/
+
+QMenu *QLineEdit::createStandardContextMenu()
+{
+ Q_D(QLineEdit);
+ QMenu *popup = new QMenu(this);
+ popup->setObjectName(QLatin1String("qt_edit_menu"));
+
+ QAction *action = popup->addAction(QLineEdit::tr("&Undo") + ACCEL_KEY(QKeySequence::Undo));
+ action->setEnabled(d->isUndoAvailable());
+ connect(action, SIGNAL(triggered()), SLOT(undo()));
+
+ action = popup->addAction(QLineEdit::tr("&Redo") + ACCEL_KEY(QKeySequence::Redo));
+ action->setEnabled(d->isRedoAvailable());
+ connect(action, SIGNAL(triggered()), SLOT(redo()));
+
+ popup->addSeparator();
+
+#ifndef QT_NO_CLIPBOARD
+ action = popup->addAction(QLineEdit::tr("Cu&t") + ACCEL_KEY(QKeySequence::Cut));
+ action->setEnabled(!d->readOnly && d->hasSelectedText());
+ connect(action, SIGNAL(triggered()), SLOT(cut()));
+
+ action = popup->addAction(QLineEdit::tr("&Copy") + ACCEL_KEY(QKeySequence::Copy));
+ action->setEnabled(d->hasSelectedText());
+ connect(action, SIGNAL(triggered()), SLOT(copy()));
+
+ action = popup->addAction(QLineEdit::tr("&Paste") + ACCEL_KEY(QKeySequence::Paste));
+ action->setEnabled(!d->readOnly && !QApplication::clipboard()->text().isEmpty());
+ connect(action, SIGNAL(triggered()), SLOT(paste()));
+#endif
+
+ action = popup->addAction(QLineEdit::tr("Delete"));
+ action->setEnabled(!d->readOnly && !d->text.isEmpty() && d->hasSelectedText());
+ connect(action, SIGNAL(triggered()), SLOT(_q_deleteSelected()));
+
+ popup->addSeparator();
+
+ action = popup->addAction(QLineEdit::tr("Select All") + ACCEL_KEY(QKeySequence::SelectAll));
+ action->setEnabled(!d->text.isEmpty() && !d->allSelected());
+ d->selectAllAction = action;
+ connect(action, SIGNAL(triggered()), SLOT(selectAll()));
+
+#if !defined(QT_NO_IM)
+ QInputContext *qic = inputContext();
+ if (qic) {
+ QList<QAction *> imActions = qic->actions();
+ for (int i = 0; i < imActions.size(); ++i)
+ popup->addAction(imActions.at(i));
+ }
+#endif
+
+#if defined(Q_WS_WIN)
+ if (!d->readOnly && qt_use_rtl_extensions) {
+#else
+ if (!d->readOnly) {
+#endif
+ popup->addSeparator();
+ QUnicodeControlCharacterMenu *ctrlCharacterMenu = new QUnicodeControlCharacterMenu(this, popup);
+ popup->addMenu(ctrlCharacterMenu);
+ }
+ return popup;
+}
+#endif // QT_NO_CONTEXTMENU
+
+/*! \reimp */
+void QLineEdit::changeEvent(QEvent *ev)
+{
+ Q_D(QLineEdit);
+ if(ev->type() == QEvent::ActivationChange) {
+ if (!palette().isEqual(QPalette::Active, QPalette::Inactive))
+ update();
+ } else if (ev->type() == QEvent::FontChange
+ || ev->type() == QEvent::StyleChange
+ || ev->type() == QEvent::LayoutDirectionChange) {
+ d->updateTextLayout();
+ }
+ QWidget::changeEvent(ev);
+}
+
+void QLineEditPrivate::_q_clipboardChanged()
+{
+}
+
+void QLineEditPrivate::_q_handleWindowActivate()
+{
+ Q_Q(QLineEdit);
+ if (!q->hasFocus() && q->hasSelectedText())
+ q->deselect();
+}
+
+void QLineEditPrivate::_q_deleteSelected()
+{
+ Q_Q(QLineEdit);
+ if (!hasSelectedText())
+ return;
+
+ int priorState = undoState;
+ q->resetInputContext();
+ removeSelectedText();
+ separate();
+ finishChange(priorState);
+}
+
+void QLineEditPrivate::init(const QString& txt)
+{
+ Q_Q(QLineEdit);
+#ifndef QT_NO_CURSOR
+ q->setCursor(Qt::IBeamCursor);
+#endif
+ q->setFocusPolicy(Qt::StrongFocus);
+ q->setAttribute(Qt::WA_InputMethodEnabled);
+ // Specifies that this widget can use more, but is able to survive on
+ // less, horizontal space; and is fixed vertically.
+ q->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed, QSizePolicy::LineEdit));
+ q->setBackgroundRole(QPalette::Base);
+ q->setAttribute(Qt::WA_KeyCompression);
+ q->setMouseTracking(true);
+ q->setAcceptDrops(true);
+ text = txt;
+ updateTextLayout();
+ cursor = text.length();
+
+ q->setAttribute(Qt::WA_MacShowFocusRect);
+}
+
+void QLineEditPrivate::updatePasswordEchoEditing(bool editing)
+{
+ Q_Q(QLineEdit);
+ passwordEchoEditing = editing;
+ q->setAttribute(Qt::WA_InputMethodEnabled, shouldEnableInputMethod(q));
+ updateTextLayout();
+ q->update();
+}
+
+void QLineEditPrivate::updateTextLayout()
+{
+ // replace certain non-printable characters with spaces (to avoid
+ // drawing boxes when using fonts that don't have glyphs for such
+ // characters)
+ Q_Q(QLineEdit);
+ QString str = q->displayText();
+ QChar* uc = str.data();
+ for (int i = 0; i < (int)str.length(); ++i) {
+ if ((uc[i] < 0x20 && uc[i] != 0x09)
+ || uc[i] == QChar::LineSeparator
+ || uc[i] == QChar::ParagraphSeparator
+ || uc[i] == QChar::ObjectReplacementCharacter)
+ uc[i] = QChar(0x0020);
+ }
+ textLayout.setFont(q->font());
+ textLayout.setText(str);
+ QTextOption option;
+ option.setTextDirection(q->layoutDirection());
+ option.setFlags(QTextOption::IncludeTrailingSpaces);
+ textLayout.setTextOption(option);
+
+ textLayout.beginLayout();
+ QTextLine l = textLayout.createLine();
+ textLayout.endLayout();
+ ascent = qRound(l.ascent());
+}
+
+int QLineEditPrivate::xToPos(int x, QTextLine::CursorPosition betweenOrOn) const
+{
+ QRect cr = adjustedContentsRect();
+ x-= cr.x() - hscroll + horizontalMargin;
+ QTextLine l = textLayout.lineAt(0);
+ return l.xToCursor(x, betweenOrOn);
+}
+
+QRect QLineEditPrivate::cursorRect() const
+{
+ Q_Q(const QLineEdit);
+ QRect cr = adjustedContentsRect();
+ int cix = cr.x() - hscroll + horizontalMargin;
+ QTextLine l = textLayout.lineAt(0);
+ int c = cursor;
+ if (preeditCursor != -1)
+ c += preeditCursor;
+ cix += qRound(l.cursorToX(c));
+ int ch = qMin(cr.height(), q->fontMetrics().height() + 1);
+ int w = q->style()->pixelMetric(QStyle::PM_TextCursorWidth);
+ return QRect(cix-5, vscroll, w + 9, ch);
+}
+
+QRect QLineEditPrivate::adjustedContentsRect() const
+{
+ Q_Q(const QLineEdit);
+ QStyleOptionFrameV2 opt;
+ q->initStyleOption(&opt);
+ QRect r = q->style()->subElementRect(QStyle::SE_LineEditContents, &opt, q);
+ r.setX(r.x() + leftTextMargin);
+ r.setY(r.y() + topTextMargin);
+ r.setRight(r.right() - rightTextMargin);
+ r.setBottom(r.bottom() - bottomTextMargin);
+ return r;
+}
+
+bool QLineEditPrivate::fixup() // this function assumes that validate currently returns != Acceptable
+{
+#ifndef QT_NO_VALIDATOR
+ if (validator) {
+ QString textCopy = text;
+ int cursorCopy = cursor;
+ validator->fixup(textCopy);
+ if (validator->validate(textCopy, cursorCopy) == QValidator::Acceptable) {
+ if (textCopy != text || cursorCopy != cursor)
+ setText(textCopy, cursorCopy);
+ return true;
+ }
+ }
+#endif
+ return false;
+}
+
+void QLineEditPrivate::moveCursor(int pos, bool mark)
+{
+ Q_Q(QLineEdit);
+ if (pos != cursor) {
+ separate();
+ if (maskData)
+ pos = pos > cursor ? nextMaskBlank(pos) : prevMaskBlank(pos);
+ }
+ bool fullUpdate = mark || hasSelectedText();
+ if (mark) {
+ int anchor;
+ if (selend > selstart && cursor == selstart)
+ anchor = selend;
+ else if (selend > selstart && cursor == selend)
+ anchor = selstart;
+ else
+ anchor = cursor;
+ selstart = qMin(anchor, pos);
+ selend = qMax(anchor, pos);
+ updateTextLayout();
+ } else {
+ deselect();
+ }
+ if (fullUpdate) {
+ cursor = pos;
+ q->update();
+ } else {
+ setCursorVisible(false);
+ cursor = pos;
+ setCursorVisible(true);
+ if (!adjustedContentsRect().contains(cursorRect()))
+ q->update();
+ }
+ QStyleOptionFrameV2 opt;
+ q->initStyleOption(&opt);
+ if (mark && !q->style()->styleHint(QStyle::SH_BlinkCursorWhenTextSelected, &opt, q))
+ setCursorVisible(false);
+ if (mark || selDirty) {
+ selDirty = false;
+ emit q->selectionChanged();
+ }
+ emitCursorPositionChanged();
+}
+
+void QLineEditPrivate::finishChange(int validateFromState, bool update, bool edited)
+{
+ Q_Q(QLineEdit);
+ bool lineDirty = selDirty;
+ if (textDirty) {
+ // do validation
+ bool wasValidInput = validInput;
+ validInput = true;
+#ifndef QT_NO_VALIDATOR
+ if (validator) {
+ validInput = false;
+ QString textCopy = text;
+ int cursorCopy = cursor;
+ validInput = (validator->validate(textCopy, cursorCopy) != QValidator::Invalid);
+ if (validInput) {
+ if (text != textCopy) {
+ setText(textCopy, cursorCopy);
+ return;
+ }
+ cursor = cursorCopy;
+ }
+ }
+#endif
+ if (validateFromState >= 0 && wasValidInput && !validInput) {
+ undo(validateFromState);
+ history.resize(undoState);
+ if (modifiedState > undoState)
+ modifiedState = -1;
+ validInput = true;
+ textDirty = false;
+ }
+ updateTextLayout();
+ lineDirty |= textDirty;
+ if (textDirty) {
+ textDirty = false;
+ QString actualText = maskData ? stripString(text) : text;
+ if (edited)
+ emit q->textEdited(actualText);
+ q->updateMicroFocus();
+#ifndef QT_NO_COMPLETER
+ if (edited && completer && completer->completionMode() != QCompleter::InlineCompletion)
+ complete(-1); // update the popup on cut/paste/del
+#endif
+ emit q->textChanged(actualText);
+ }
+#ifndef QT_NO_ACCESSIBILITY
+ QAccessible::updateAccessibility(q, 0, QAccessible::ValueChanged);
+#endif
+ }
+ if (selDirty) {
+ selDirty = false;
+ emit q->selectionChanged();
+ }
+ if (lineDirty || update)
+ q->update();
+ emitCursorPositionChanged();
+}
+
+void QLineEditPrivate::emitCursorPositionChanged()
+{
+ Q_Q(QLineEdit);
+ if (cursor != lastCursorPos) {
+ const int oldLast = lastCursorPos;
+ lastCursorPos = cursor;
+ emit q->cursorPositionChanged(oldLast, cursor);
+ }
+}
+
+void QLineEditPrivate::setText(const QString& txt, int pos, bool edited)
+{
+ Q_Q(QLineEdit);
+ q->resetInputContext();
+ deselect();
+ QString oldText = text;
+ if (maskData) {
+ text = maskString(0, txt, true);
+ text += clearString(text.length(), maxLength - text.length());
+ } else {
+ text = txt.isEmpty() ? txt : txt.left(maxLength);
+ }
+ history.clear();
+ modifiedState = undoState = 0;
+ cursor = (pos < 0 || pos > text.length()) ? text.length() : pos;
+ textDirty = (oldText != text);
+ finishChange(-1, true, edited);
+}
+
+
+void QLineEditPrivate::setCursorVisible(bool visible)
+{
+ Q_Q(QLineEdit);
+ if ((bool)cursorVisible == visible)
+ return;
+ if (cursorTimer)
+ cursorVisible = visible;
+ QRect r = cursorRect();
+ if (maskData)
+ q->update();
+ else
+ q->update(r);
+}
+
+void QLineEditPrivate::addCommand(const Command& cmd)
+{
+ if (separator && undoState && history[undoState-1].type != Separator) {
+ history.resize(undoState + 2);
+ history[undoState++] = Command(Separator, cursor, 0, selstart, selend);
+ } else {
+ history.resize(undoState + 1);
+ }
+ separator = false;
+ history[undoState++] = cmd;
+}
+
+void QLineEditPrivate::insert(const QString& s)
+{
+ if (hasSelectedText())
+ addCommand(Command(SetSelection, cursor, 0, selstart, selend));
+ if (maskData) {
+ QString ms = maskString(cursor, s);
+ for (int i = 0; i < (int) ms.length(); ++i) {
+ addCommand (Command(DeleteSelection, cursor+i, text.at(cursor+i), -1, -1));
+ addCommand(Command(Insert, cursor+i, ms.at(i), -1, -1));
+ }
+ text.replace(cursor, ms.length(), ms);
+ cursor += ms.length();
+ cursor = nextMaskBlank(cursor);
+ textDirty = true;
+ } else {
+ int remaining = maxLength - text.length();
+ if (remaining != 0) {
+ text.insert(cursor, s.left(remaining));
+ for (int i = 0; i < (int) s.left(remaining).length(); ++i)
+ addCommand(Command(Insert, cursor++, s.at(i), -1, -1));
+ textDirty = true;
+ }
+ }
+}
+
+void QLineEditPrivate::del(bool wasBackspace)
+{
+ if (cursor < (int) text.length()) {
+ if (hasSelectedText())
+ addCommand(Command(SetSelection, cursor, 0, selstart, selend));
+ addCommand (Command((CommandType)((maskData?2:0)+(wasBackspace?Remove:Delete)), cursor, text.at(cursor), -1, -1));
+ if (maskData) {
+ text.replace(cursor, 1, clearString(cursor, 1));
+ addCommand(Command(Insert, cursor, text.at(cursor), -1, -1));
+ } else {
+ text.remove(cursor, 1);
+ }
+ textDirty = true;
+ }
+}
+
+void QLineEditPrivate::removeSelectedText()
+{
+ if (selstart < selend && selend <= (int) text.length()) {
+ separate();
+ int i ;
+ addCommand(Command(SetSelection, cursor, 0, selstart, selend));
+ if (selstart <= cursor && cursor < selend) {
+ // cursor is within the selection. Split up the commands
+ // to be able to restore the correct cursor position
+ for (i = cursor; i >= selstart; --i)
+ addCommand (Command(DeleteSelection, i, text.at(i), -1, 1));
+ for (i = selend - 1; i > cursor; --i)
+ addCommand (Command(DeleteSelection, i - cursor + selstart - 1, text.at(i), -1, -1));
+ } else {
+ for (i = selend-1; i >= selstart; --i)
+ addCommand (Command(RemoveSelection, i, text.at(i), -1, -1));
+ }
+ if (maskData) {
+ text.replace(selstart, selend - selstart, clearString(selstart, selend - selstart));
+ for (int i = 0; i < selend - selstart; ++i)
+ addCommand(Command(Insert, selstart + i, text.at(selstart + i), -1, -1));
+ } else {
+ text.remove(selstart, selend - selstart);
+ }
+ if (cursor > selstart)
+ cursor -= qMin(cursor, selend) - selstart;
+ deselect();
+ textDirty = true;
+
+ // adjust hscroll to avoid gap
+ const int minRB = qMax(0, -q_func()->fontMetrics().minRightBearing());
+ updateTextLayout();
+ const QTextLine line = textLayout.lineAt(0);
+ const int widthUsed = qRound(line.naturalTextWidth()) + 1 + minRB;
+ hscroll = qMin(hscroll, widthUsed);
+ }
+}
+
+void QLineEditPrivate::parseInputMask(const QString &maskFields)
+{
+ int delimiter = maskFields.indexOf(QLatin1Char(';'));
+ if (maskFields.isEmpty() || delimiter == 0) {
+ if (maskData) {
+ delete [] maskData;
+ maskData = 0;
+ maxLength = 32767;
+ setText(QString());
+ }
+ return;
+ }
+
+ if (delimiter == -1) {
+ blank = QLatin1Char(' ');
+ inputMask = maskFields;
+ } else {
+ inputMask = maskFields.left(delimiter);
+ blank = (delimiter + 1 < maskFields.length()) ? maskFields[delimiter + 1] : QLatin1Char(' ');
+ }
+
+ // calculate maxLength / maskData length
+ maxLength = 0;
+ QChar c = 0;
+ for (int i=0; i<inputMask.length(); i++) {
+ c = inputMask.at(i);
+ if (i > 0 && inputMask.at(i-1) == QLatin1Char('\\')) {
+ maxLength++;
+ continue;
+ }
+ if (c != QLatin1Char('\\') && c != QLatin1Char('!') &&
+ c != QLatin1Char('<') && c != QLatin1Char('>') &&
+ c != QLatin1Char('{') && c != QLatin1Char('}') &&
+ c != QLatin1Char('[') && c != QLatin1Char(']'))
+ maxLength++;
+ }
+
+ delete [] maskData;
+ maskData = new MaskInputData[maxLength];
+
+ MaskInputData::Casemode m = MaskInputData::NoCaseMode;
+ c = 0;
+ bool s;
+ bool escape = false;
+ int index = 0;
+ for (int i = 0; i < inputMask.length(); i++) {
+ c = inputMask.at(i);
+ if (escape) {
+ s = true;
+ maskData[index].maskChar = c;
+ maskData[index].separator = s;
+ maskData[index].caseMode = m;
+ index++;
+ escape = false;
+ } else if (c == QLatin1Char('<')) {
+ m = MaskInputData::Lower;
+ } else if (c == QLatin1Char('>')) {
+ m = MaskInputData::Upper;
+ } else if (c == QLatin1Char('!')) {
+ m = MaskInputData::NoCaseMode;
+ } else if (c != QLatin1Char('{') && c != QLatin1Char('}') && c != QLatin1Char('[') && c != QLatin1Char(']')) {
+ switch (c.unicode()) {
+ case 'A':
+ case 'a':
+ case 'N':
+ case 'n':
+ case 'X':
+ case 'x':
+ case '9':
+ case '0':
+ case 'D':
+ case 'd':
+ case '#':
+ case 'H':
+ case 'h':
+ case 'B':
+ case 'b':
+ s = false;
+ break;
+ case '\\':
+ escape = true;
+ default:
+ s = true;
+ break;
+ }
+
+ if (!escape) {
+ maskData[index].maskChar = c;
+ maskData[index].separator = s;
+ maskData[index].caseMode = m;
+ index++;
+ }
+ }
+ }
+ setText(text);
+}
+
+
+/* checks if the key is valid compared to the inputMask */
+bool QLineEditPrivate::isValidInput(QChar key, QChar mask) const
+{
+ switch (mask.unicode()) {
+ case 'A':
+ if (key.isLetter())
+ return true;
+ break;
+ case 'a':
+ if (key.isLetter() || key == blank)
+ return true;
+ break;
+ case 'N':
+ if (key.isLetterOrNumber())
+ return true;
+ break;
+ case 'n':
+ if (key.isLetterOrNumber() || key == blank)
+ return true;
+ break;
+ case 'X':
+ if (key.isPrint())
+ return true;
+ break;
+ case 'x':
+ if (key.isPrint() || key == blank)
+ return true;
+ break;
+ case '9':
+ if (key.isNumber())
+ return true;
+ break;
+ case '0':
+ if (key.isNumber() || key == blank)
+ return true;
+ break;
+ case 'D':
+ if (key.isNumber() && key.digitValue() > 0)
+ return true;
+ break;
+ case 'd':
+ if ((key.isNumber() && key.digitValue() > 0) || key == blank)
+ return true;
+ break;
+ case '#':
+ if (key.isNumber() || key == QLatin1Char('+') || key == QLatin1Char('-') || key == blank)
+ return true;
+ break;
+ case 'B':
+ if (key == QLatin1Char('0') || key == QLatin1Char('1'))
+ return true;
+ break;
+ case 'b':
+ if (key == QLatin1Char('0') || key == QLatin1Char('1') || key == blank)
+ return true;
+ break;
+ case 'H':
+ if (key.isNumber() || (key >= QLatin1Char('a') && key <= QLatin1Char('f')) || (key >= QLatin1Char('A') && key <= QLatin1Char('F')))
+ return true;
+ break;
+ case 'h':
+ if (key.isNumber() || (key >= QLatin1Char('a') && key <= QLatin1Char('f')) || (key >= QLatin1Char('A') && key <= QLatin1Char('F')) || key == blank)
+ return true;
+ break;
+ default:
+ break;
+ }
+ return false;
+}
+
+bool QLineEditPrivate::hasAcceptableInput(const QString &str) const
+{
+#ifndef QT_NO_VALIDATOR
+ QString textCopy = str;
+ int cursorCopy = cursor;
+ if (validator && validator->validate(textCopy, cursorCopy)
+ != QValidator::Acceptable)
+ return false;
+#endif
+
+ if (!maskData)
+ return true;
+
+ if (str.length() != maxLength)
+ return false;
+
+ for (int i=0; i < maxLength; ++i) {
+ if (maskData[i].separator) {
+ if (str.at(i) != maskData[i].maskChar)
+ return false;
+ } else {
+ if (!isValidInput(str.at(i), maskData[i].maskChar))
+ return false;
+ }
+ }
+ return true;
+}
+
+/*
+ Applies the inputMask on \a str starting from position \a pos in the mask. \a clear
+ specifies from where characters should be gotten when a separator is met in \a str - true means
+ that blanks will be used, false that previous input is used.
+ Calling this when no inputMask is set is undefined.
+*/
+QString QLineEditPrivate::maskString(uint pos, const QString &str, bool clear) const
+{
+ if (pos >= (uint)maxLength)
+ return QString::fromLatin1("");
+
+ QString fill;
+ fill = clear ? clearString(0, maxLength) : text;
+
+ int strIndex = 0;
+ QString s = QString::fromLatin1("");
+ int i = pos;
+ while (i < maxLength) {
+ if (strIndex < str.length()) {
+ if (maskData[i].separator) {
+ s += maskData[i].maskChar;
+ if (str[(int)strIndex] == maskData[i].maskChar)
+ strIndex++;
+ ++i;
+ } else {
+ if (isValidInput(str[(int)strIndex], maskData[i].maskChar)) {
+ switch (maskData[i].caseMode) {
+ case MaskInputData::Upper:
+ s += str[(int)strIndex].toUpper();
+ break;
+ case MaskInputData::Lower:
+ s += str[(int)strIndex].toLower();
+ break;
+ default:
+ s += str[(int)strIndex];
+ }
+ ++i;
+ } else {
+ // search for separator first
+ int n = findInMask(i, true, true, str[(int)strIndex]);
+ if (n != -1) {
+ if (str.length() != 1 || i == 0 || (i > 0 && (!maskData[i-1].separator || maskData[i-1].maskChar != str[(int)strIndex]))) {
+ s += fill.mid(i, n-i+1);
+ i = n + 1; // update i to find + 1
+ }
+ } else {
+ // search for valid blank if not
+ n = findInMask(i, true, false, str[(int)strIndex]);
+ if (n != -1) {
+ s += fill.mid(i, n-i);
+ switch (maskData[n].caseMode) {
+ case MaskInputData::Upper:
+ s += str[(int)strIndex].toUpper();
+ break;
+ case MaskInputData::Lower:
+ s += str[(int)strIndex].toLower();
+ break;
+ default:
+ s += str[(int)strIndex];
+ }
+ i = n + 1; // updates i to find + 1
+ }
+ }
+ }
+ strIndex++;
+ }
+ } else
+ break;
+ }
+
+ return s;
+}
+
+
+
+/*
+ Returns a "cleared" string with only separators and blank chars.
+ Calling this when no inputMask is set is undefined.
+*/
+QString QLineEditPrivate::clearString(uint pos, uint len) const
+{
+ if (pos >= (uint)maxLength)
+ return QString();
+
+ QString s;
+ int end = qMin((uint)maxLength, pos + len);
+ for (int i=pos; i<end; i++)
+ if (maskData[i].separator)
+ s += maskData[i].maskChar;
+ else
+ s += blank;
+
+ return s;
+}
+
+/*
+ Strips blank parts of the input in a QLineEdit when an inputMask is set,
+ separators are still included. Typically "127.0__.0__.1__" becomes "127.0.0.1".
+*/
+QString QLineEditPrivate::stripString(const QString &str) const
+{
+ if (!maskData)
+ return str;
+
+ QString s;
+ int end = qMin(maxLength, (int)str.length());
+ for (int i=0; i < end; i++)
+ if (maskData[i].separator)
+ s += maskData[i].maskChar;
+ else
+ if (str[i] != blank)
+ s += str[i];
+
+ return s;
+}
+
+/* searches forward/backward in maskData for either a separator or a blank */
+int QLineEditPrivate::findInMask(int pos, bool forward, bool findSeparator, QChar searchChar) const
+{
+ if (pos >= maxLength || pos < 0)
+ return -1;
+
+ int end = forward ? maxLength : -1;
+ int step = forward ? 1 : -1;
+ int i = pos;
+
+ while (i != end) {
+ if (findSeparator) {
+ if (maskData[i].separator && maskData[i].maskChar == searchChar)
+ return i;
+ } else {
+ if (!maskData[i].separator) {
+ if (searchChar.isNull())
+ return i;
+ else if (isValidInput(searchChar, maskData[i].maskChar))
+ return i;
+ }
+ }
+ i += step;
+ }
+ return -1;
+}
+
+void QLineEditPrivate::undo(int until)
+{
+ if (!isUndoAvailable())
+ return;
+ deselect();
+ while (undoState && undoState > until) {
+ Command& cmd = history[--undoState];
+ switch (cmd.type) {
+ case Insert:
+ text.remove(cmd.pos, 1);
+ cursor = cmd.pos;
+ break;
+ case SetSelection:
+ selstart = cmd.selStart;
+ selend = cmd.selEnd;
+ cursor = cmd.pos;
+ break;
+ case Remove:
+ case RemoveSelection:
+ text.insert(cmd.pos, cmd.uc);
+ cursor = cmd.pos + 1;
+ break;
+ case Delete:
+ case DeleteSelection:
+ text.insert(cmd.pos, cmd.uc);
+ cursor = cmd.pos;
+ break;
+ case Separator:
+ continue;
+ }
+ if (until < 0 && undoState) {
+ Command& next = history[undoState-1];
+ if (next.type != cmd.type && next.type < RemoveSelection
+ && (cmd.type < RemoveSelection || next.type == Separator))
+ break;
+ }
+ }
+ textDirty = true;
+ emitCursorPositionChanged();
+}
+
+void QLineEditPrivate::redo() {
+ if (!isRedoAvailable())
+ return;
+ deselect();
+ while (undoState < (int)history.size()) {
+ Command& cmd = history[undoState++];
+ switch (cmd.type) {
+ case Insert:
+ text.insert(cmd.pos, cmd.uc);
+ cursor = cmd.pos + 1;
+ break;
+ case SetSelection:
+ selstart = cmd.selStart;
+ selend = cmd.selEnd;
+ cursor = cmd.pos;
+ break;
+ case Remove:
+ case Delete:
+ case RemoveSelection:
+ case DeleteSelection:
+ text.remove(cmd.pos, 1);
+ cursor = cmd.pos;
+ break;
+ case Separator:
+ selstart = cmd.selStart;
+ selend = cmd.selEnd;
+ cursor = cmd.pos;
+ break;
+ }
+ if (undoState < (int)history.size()) {
+ Command& next = history[undoState];
+ if (next.type != cmd.type && cmd.type < RemoveSelection && next.type != Separator
+ && (next.type < RemoveSelection || cmd.type == Separator))
+ break;
+ }
+ }
+ textDirty = true;
+ emitCursorPositionChanged();
+}
+
+/*!
+ \fn void QLineEdit::repaintArea(int a, int b)
+
+ Use update() instead.
+*/
+
+/*!
+ \fn void QLineEdit::cursorLeft(bool mark, int steps)
+
+ Use cursorForward() with a negative number of steps instead. For
+ example, cursorForward(mark, -steps).
+*/
+
+/*!
+ \fn void QLineEdit::cursorRight(bool mark, int steps)
+
+ Use cursorForward() instead.
+*/
+
+/*!
+ \fn bool QLineEdit::frame() const
+
+ Use hasFrame() instead.
+*/
+
+/*!
+ \fn void QLineEdit::clearValidator()
+
+ Use setValidator(0) instead.
+*/
+
+/*!
+ \fn bool QLineEdit::hasMarkedText() const
+
+ Use hasSelectedText() instead.
+*/
+
+/*!
+ \fn QString QLineEdit::markedText() const
+
+ Use selectedText() instead.
+*/
+
+/*!
+ \fn void QLineEdit::setFrameRect(QRect)
+ \internal
+*/
+
+/*!
+ \fn QRect QLineEdit::frameRect() const
+ \internal
+*/
+/*!
+ \enum QLineEdit::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 QLineEdit::setFrameShadow(DummyFrame)
+ \internal
+*/
+
+/*!
+ \fn DummyFrame QLineEdit::frameShadow() const
+ \internal
+*/
+
+/*!
+ \fn void QLineEdit::setFrameShape(DummyFrame)
+ \internal
+*/
+
+/*!
+ \fn DummyFrame QLineEdit::frameShape() const
+ \internal
+*/
+
+/*!
+ \fn void QLineEdit::setFrameStyle(int)
+ \internal
+*/
+
+/*!
+ \fn int QLineEdit::frameStyle() const
+ \internal
+*/
+
+/*!
+ \fn int QLineEdit::frameWidth() const
+ \internal
+*/
+
+/*!
+ \fn void QLineEdit::setLineWidth(int)
+ \internal
+*/
+
+/*!
+ \fn int QLineEdit::lineWidth() const
+ \internal
+*/
+
+/*!
+ \fn void QLineEdit::setMargin(int margin)
+ Sets the width of the margin around the contents of the widget to \a margin.
+
+ Use QWidget::setContentsMargins() instead.
+ \sa margin(), QWidget::setContentsMargins()
+*/
+
+/*!
+ \fn int QLineEdit::margin() const
+ Returns the with of the the margin around the contents of the widget.
+
+ Use QWidget::getContentsMargins() instead.
+ \sa setMargin(), QWidget::getContentsMargins()
+*/
+
+/*!
+ \fn void QLineEdit::setMidLineWidth(int)
+ \internal
+*/
+
+/*!
+ \fn int QLineEdit::midLineWidth() const
+ \internal
+*/
+
+QT_END_NAMESPACE
+
+#include "moc_qlineedit.cpp"
+
+#endif // QT_NO_LINEEDIT
diff --git a/src/gui/widgets/qlineedit.h b/src/gui/widgets/qlineedit.h
new file mode 100644
index 0000000000..c0d98928f2
--- /dev/null
+++ b/src/gui/widgets/qlineedit.h
@@ -0,0 +1,283 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QLINEEDIT_H
+#define QLINEEDIT_H
+
+#include <QtGui/qframe.h>
+#include <QtCore/qstring.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+#ifndef QT_NO_LINEEDIT
+
+class QValidator;
+class QMenu;
+class QLineEditPrivate;
+class QCompleter;
+class QStyleOptionFrame;
+class QAbstractSpinBox;
+class QDateTimeEdit;
+
+class Q_GUI_EXPORT QLineEdit : public QWidget
+{
+ Q_OBJECT
+
+ Q_ENUMS(EchoMode)
+ Q_PROPERTY(QString inputMask READ inputMask WRITE setInputMask)
+ Q_PROPERTY(QString text READ text WRITE setText NOTIFY textChanged USER true)
+ Q_PROPERTY(int maxLength READ maxLength WRITE setMaxLength)
+ Q_PROPERTY(bool frame READ hasFrame WRITE setFrame)
+ Q_PROPERTY(EchoMode echoMode READ echoMode WRITE setEchoMode)
+ Q_PROPERTY(QString displayText READ displayText)
+ Q_PROPERTY(int cursorPosition READ cursorPosition WRITE setCursorPosition)
+ Q_PROPERTY(Qt::Alignment alignment READ alignment WRITE setAlignment)
+ Q_PROPERTY(bool modified READ isModified WRITE setModified DESIGNABLE false)
+ Q_PROPERTY(bool hasSelectedText READ hasSelectedText)
+ Q_PROPERTY(QString selectedText READ selectedText)
+ Q_PROPERTY(bool dragEnabled READ dragEnabled WRITE setDragEnabled)
+ Q_PROPERTY(bool readOnly READ isReadOnly WRITE setReadOnly)
+ Q_PROPERTY(bool undoAvailable READ isUndoAvailable)
+ Q_PROPERTY(bool redoAvailable READ isRedoAvailable)
+ Q_PROPERTY(bool acceptableInput READ hasAcceptableInput)
+
+public:
+ explicit QLineEdit(QWidget* parent=0);
+ explicit QLineEdit(const QString &, QWidget* parent=0);
+#ifdef QT3_SUPPORT
+ QT3_SUPPORT_CONSTRUCTOR QLineEdit(QWidget* parent, const char* name);
+ QT3_SUPPORT_CONSTRUCTOR QLineEdit(const QString &, QWidget* parent, const char* name);
+ QT3_SUPPORT_CONSTRUCTOR QLineEdit(const QString &, const QString &, QWidget* parent=0, const char* name=0);
+#endif
+ ~QLineEdit();
+
+ QString text() const;
+
+ QString displayText() const;
+
+ int maxLength() const;
+ void setMaxLength(int);
+
+ void setFrame(bool);
+ bool hasFrame() const;
+
+ enum EchoMode { Normal, NoEcho, Password, PasswordEchoOnEdit };
+ EchoMode echoMode() const;
+ void setEchoMode(EchoMode);
+
+ bool isReadOnly() const;
+ void setReadOnly(bool);
+
+#ifndef QT_NO_VALIDATOR
+ void setValidator(const QValidator *);
+ const QValidator * validator() const;
+#endif
+
+#ifndef QT_NO_COMPLETER
+ void setCompleter(QCompleter *completer);
+ QCompleter *completer() const;
+#endif
+
+ QSize sizeHint() const;
+ QSize minimumSizeHint() const;
+
+ int cursorPosition() const;
+ void setCursorPosition(int);
+ int cursorPositionAt(const QPoint &pos);
+
+ void setAlignment(Qt::Alignment flag);
+ Qt::Alignment alignment() const;
+
+ void cursorForward(bool mark, int steps = 1);
+ void cursorBackward(bool mark, int steps = 1);
+ void cursorWordForward(bool mark);
+ void cursorWordBackward(bool mark);
+ void backspace();
+ void del();
+ void home(bool mark);
+ void end(bool mark);
+
+ bool isModified() const;
+ void setModified(bool);
+
+ void setSelection(int, int);
+ bool hasSelectedText() const;
+ QString selectedText() const;
+ int selectionStart() const;
+
+ bool isUndoAvailable() const;
+ bool isRedoAvailable() const;
+
+ void setDragEnabled(bool b);
+ bool dragEnabled() const;
+
+ QString inputMask() const;
+ void setInputMask(const QString &inputMask);
+ bool hasAcceptableInput() const;
+
+ void setTextMargins(int left, int top, int right, int bottom);
+ void getTextMargins(int *left, int *top, int *right, int *bottom) const;
+
+public Q_SLOTS:
+ void setText(const QString &);
+ void clear();
+ void selectAll();
+ void undo();
+ void redo();
+#ifndef QT_NO_CLIPBOARD
+ void cut();
+ void copy() const;
+ void paste();
+#endif
+
+public:
+ void deselect();
+ void insert(const QString &);
+#ifndef QT_NO_CONTEXTMENU
+ QMenu *createStandardContextMenu();
+#endif
+
+Q_SIGNALS:
+ void textChanged(const QString &);
+ void textEdited(const QString &);
+ void cursorPositionChanged(int, int);
+ void returnPressed();
+ void editingFinished();
+ void selectionChanged();
+
+protected:
+ void mousePressEvent(QMouseEvent *);
+ void mouseMoveEvent(QMouseEvent *);
+ void mouseReleaseEvent(QMouseEvent *);
+ void mouseDoubleClickEvent(QMouseEvent *);
+ void keyPressEvent(QKeyEvent *);
+ void focusInEvent(QFocusEvent *);
+ void focusOutEvent(QFocusEvent *);
+ void paintEvent(QPaintEvent *);
+#ifndef QT_NO_DRAGANDDROP
+ void dragEnterEvent(QDragEnterEvent *);
+ void dragMoveEvent(QDragMoveEvent *e);
+ void dragLeaveEvent(QDragLeaveEvent *e);
+ void dropEvent(QDropEvent *);
+#endif
+ void changeEvent(QEvent *);
+#ifndef QT_NO_CONTEXTMENU
+ void contextMenuEvent(QContextMenuEvent *);
+#endif
+#ifdef QT3_SUPPORT
+ inline QT3_SUPPORT void repaintArea(int, int) { update(); }
+#endif
+
+ void inputMethodEvent(QInputMethodEvent *);
+ void initStyleOption(QStyleOptionFrame *option) const;
+public:
+ QVariant inputMethodQuery(Qt::InputMethodQuery) const;
+ bool event(QEvent *);
+protected:
+ QRect cursorRect() const;
+
+public:
+#ifdef QT3_SUPPORT
+ inline QT3_SUPPORT void clearModified() { setModified(false); }
+ inline QT3_SUPPORT void cursorLeft(bool mark, int steps = 1) { cursorForward(mark, -steps); }
+ inline QT3_SUPPORT void cursorRight(bool mark, int steps = 1) { cursorForward(mark, steps); }
+ QT3_SUPPORT bool validateAndSet(const QString &, int, int, int);
+ inline QT3_SUPPORT bool frame() const { return hasFrame(); }
+#ifndef QT_NO_VALIDATOR
+ inline QT3_SUPPORT void clearValidator() { setValidator(0); }
+#endif
+ inline QT3_SUPPORT bool hasMarkedText() const { return hasSelectedText(); }
+ inline QT3_SUPPORT QString markedText() const { return selectedText(); }
+ QT3_SUPPORT bool edited() const;
+ QT3_SUPPORT void setEdited(bool);
+ QT3_SUPPORT int characterAt(int, QChar*) const;
+ QT3_SUPPORT bool getSelection(int *, int *);
+
+ QT3_SUPPORT void setFrameRect(QRect) {}
+ QT3_SUPPORT 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 };
+ QT3_SUPPORT void setFrameShadow(DummyFrame) {}
+ QT3_SUPPORT DummyFrame frameShadow() const { return Plain; }
+ QT3_SUPPORT void setFrameShape(DummyFrame) {}
+ QT3_SUPPORT DummyFrame frameShape() const { return NoFrame; }
+ QT3_SUPPORT void setFrameStyle(int) {}
+ QT3_SUPPORT int frameStyle() const { return 0; }
+ QT3_SUPPORT int frameWidth() const { return 0; }
+ QT3_SUPPORT void setLineWidth(int) {}
+ QT3_SUPPORT int lineWidth() const { return 0; }
+ QT3_SUPPORT void setMargin(int margin) { setContentsMargins(margin, margin, margin, margin); }
+ QT3_SUPPORT int margin() const
+ { int margin; int dummy; getContentsMargins(&margin, &dummy, &dummy, &dummy); return margin; }
+ QT3_SUPPORT void setMidLineWidth(int) {}
+ QT3_SUPPORT int midLineWidth() const { return 0; }
+
+Q_SIGNALS:
+ QT_MOC_COMPAT void lostFocus();
+#endif
+
+private:
+ friend class QAbstractSpinBox;
+#ifdef QT_KEYPAD_NAVIGATION
+ friend class QDateTimeEdit;
+#endif
+ Q_DISABLE_COPY(QLineEdit)
+ Q_DECLARE_PRIVATE(QLineEdit)
+ Q_PRIVATE_SLOT(d_func(), void _q_clipboardChanged())
+ Q_PRIVATE_SLOT(d_func(), void _q_handleWindowActivate())
+ Q_PRIVATE_SLOT(d_func(), void _q_deleteSelected())
+#ifndef QT_NO_COMPLETER
+ Q_PRIVATE_SLOT(d_func(), void _q_completionHighlighted(QString))
+#endif
+};
+
+#endif // QT_NO_LINEEDIT
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QLINEEDIT_H
diff --git a/src/gui/widgets/qlineedit_p.h b/src/gui/widgets/qlineedit_p.h
new file mode 100644
index 0000000000..532528bbbf
--- /dev/null
+++ b/src/gui/widgets/qlineedit_p.h
@@ -0,0 +1,250 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QLINEEDIT_P_H
+#define QLINEEDIT_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 "QtCore/qglobal.h"
+
+#ifndef QT_NO_LINEEDIT
+#include "private/qwidget_p.h"
+#include "QtGui/qlineedit.h"
+#include "QtGui/qtextlayout.h"
+#include "QtGui/qstyleoption.h"
+#include "QtCore/qbasictimer.h"
+#include "QtGui/qcompleter.h"
+#include "QtCore/qpointer.h"
+#include "QtGui/qlineedit.h"
+
+QT_BEGIN_NAMESPACE
+
+class QLineEditPrivate : public QWidgetPrivate
+{
+ Q_DECLARE_PUBLIC(QLineEdit)
+public:
+
+ QLineEditPrivate()
+ : cursor(0), preeditCursor(0), cursorTimer(0), frame(1),
+ cursorVisible(0), hideCursor(false), separator(0), readOnly(0),
+ dragEnabled(0), contextMenuEnabled(1), echoMode(0), textDirty(0),
+ selDirty(0), validInput(1), alignment(Qt::AlignLeading | Qt::AlignVCenter), ascent(0),
+ maxLength(32767), hscroll(0), vscroll(0), lastCursorPos(-1), maskData(0),
+ modifiedState(0), undoState(0), selstart(0), selend(0), userInput(false),
+ emitingEditingFinished(false), passwordEchoEditing(false)
+#ifndef QT_NO_COMPLETER
+ , completer(0)
+#endif
+ , leftTextMargin(0), topTextMargin(0), rightTextMargin(0), bottomTextMargin(0)
+ {
+ }
+
+ ~QLineEditPrivate()
+ {
+ delete [] maskData;
+ }
+ void init(const QString&);
+
+ QString text;
+ int cursor;
+ int preeditCursor;
+ int cursorTimer; // -1 for non blinking cursor.
+ QPoint tripleClick;
+ QBasicTimer tripleClickTimer;
+ uint frame : 1;
+ uint cursorVisible : 1;
+ uint hideCursor : 1; // used to hide the cursor inside preedit areas
+ uint separator : 1;
+ uint readOnly : 1;
+ uint dragEnabled : 1;
+ uint contextMenuEnabled : 1;
+ uint echoMode : 2;
+ uint textDirty : 1;
+ uint selDirty : 1;
+ uint validInput : 1;
+ uint alignment;
+ int ascent;
+ int maxLength;
+ int hscroll;
+ int vscroll;
+ int lastCursorPos;
+
+#ifndef QT_NO_CONTEXTMENU
+ QPointer<QAction> selectAllAction;
+#endif
+
+ inline void emitCursorPositionChanged();
+ bool sendMouseEventToInputContext(QMouseEvent *e);
+
+ void finishChange(int validateFromState = -1, bool update = false, bool edited = true);
+
+ QPointer<QValidator> validator;
+ struct MaskInputData {
+ enum Casemode { NoCaseMode, Upper, Lower };
+ QChar maskChar; // either the separator char or the inputmask
+ bool separator;
+ Casemode caseMode;
+ };
+ QString inputMask;
+ QChar blank;
+ MaskInputData *maskData;
+ inline int nextMaskBlank(int pos) {
+ int c = findInMask(pos, true, false);
+ separator |= (c != pos);
+ return (c != -1 ? c : maxLength);
+ }
+ inline int prevMaskBlank(int pos) {
+ int c = findInMask(pos, false, false);
+ separator |= (c != pos);
+ return (c != -1 ? c : 0);
+ }
+
+ void setCursorVisible(bool visible);
+
+
+ // undo/redo handling
+ enum CommandType { Separator, Insert, Remove, Delete, RemoveSelection, DeleteSelection, SetSelection };
+ struct Command {
+ inline Command() {}
+ inline Command(CommandType t, int p, QChar c, int ss, int se) : type(t),uc(c),pos(p),selStart(ss),selEnd(se) {}
+ uint type : 4;
+ QChar uc;
+ int pos, selStart, selEnd;
+ };
+ int modifiedState;
+ int undoState;
+ QVector<Command> history;
+ void addCommand(const Command& cmd);
+ void insert(const QString& s);
+ void del(bool wasBackspace = false);
+ void remove(int pos);
+
+ inline void separate() { separator = true; }
+ void undo(int until = -1);
+ void redo();
+ inline bool isUndoAvailable() const { return !readOnly && undoState; }
+ inline bool isRedoAvailable() const { return !readOnly && undoState < (int)history.size(); }
+
+ // selection
+ int selstart, selend;
+ inline bool allSelected() const { return !text.isEmpty() && selstart == 0 && selend == (int)text.length(); }
+ inline bool hasSelectedText() const { return !text.isEmpty() && selend > selstart; }
+ inline void deselect() { selDirty |= (selend > selstart); selstart = selend = 0; }
+ void removeSelectedText();
+#ifndef QT_NO_CLIPBOARD
+ void copy(bool clipboard = true) const;
+#endif
+ inline bool inSelection(int x) const
+ { if (selstart >= selend) return false;
+ int pos = xToPos(x, QTextLine::CursorOnCharacter); return pos >= selstart && pos < selend; }
+
+ // masking
+ void parseInputMask(const QString &maskFields);
+ bool isValidInput(QChar key, QChar mask) const;
+ bool hasAcceptableInput(const QString &text) const;
+ QString maskString(uint pos, const QString &str, bool clear = false) const;
+ QString clearString(uint pos, uint len) const;
+ QString stripString(const QString &str) const;
+ int findInMask(int pos, bool forward, bool findSeparator, QChar searchChar = QChar()) const;
+
+ // input methods
+ bool composeMode() const { return !textLayout.preeditAreaText().isEmpty(); }
+
+ // complex text layout
+ QTextLayout textLayout;
+ void updateTextLayout();
+ void moveCursor(int pos, bool mark = false);
+ void setText(const QString& txt, int pos = -1, bool edited = true);
+ int xToPos(int x, QTextLine::CursorPosition = QTextLine::CursorBetweenCharacters) const;
+ QRect cursorRect() const;
+ bool fixup();
+
+ QRect adjustedContentsRect() const;
+
+#ifndef QT_NO_DRAGANDDROP
+ // drag and drop
+ QPoint dndPos;
+ QBasicTimer dndTimer;
+ void drag();
+#endif
+
+ void _q_clipboardChanged();
+ void _q_handleWindowActivate();
+ void _q_deleteSelected();
+ bool userInput;
+ bool emitingEditingFinished;
+
+#ifdef QT_KEYPAD_NAVIGATION
+ QBasicTimer deleteAllTimer; // keypad navigation
+ QString origText;
+#endif
+
+ bool passwordEchoEditing;
+ void updatePasswordEchoEditing(bool editing);
+
+#ifndef QT_NO_COMPLETER
+ QPointer<QCompleter> completer;
+ void complete(int key = -1);
+ void _q_completionHighlighted(QString);
+ bool advanceToEnabledItem(int n);
+#endif
+
+ int leftTextMargin;
+ int topTextMargin;
+ int rightTextMargin;
+ int bottomTextMargin;
+};
+
+#endif // QT_NO_LINEEDIT
+
+QT_END_NAMESPACE
+
+#endif // QLINEEDIT_P_H
diff --git a/src/gui/widgets/qmaccocoaviewcontainer_mac.h b/src/gui/widgets/qmaccocoaviewcontainer_mac.h
new file mode 100644
index 0000000000..19763ba877
--- /dev/null
+++ b/src/gui/widgets/qmaccocoaviewcontainer_mac.h
@@ -0,0 +1,73 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QCOCOAVIEWCONTAINER_H
+#define QCOCOAVIEWCONTAINER_H
+
+#include <QtGui/QWidget>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+class QMacCocoaViewContainerPrivate;
+
+class Q_GUI_EXPORT QMacCocoaViewContainer : public QWidget
+{
+ Q_OBJECT
+public:
+ QMacCocoaViewContainer(void *cocoaViewToWrap, QWidget *parent = 0);
+ virtual ~QMacCocoaViewContainer();
+
+ void setCocoaView(void *cocoaViewToWrap);
+ void *cocoaView() const;
+
+private:
+ Q_DECLARE_PRIVATE(QMacCocoaViewContainer)
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QCOCOAVIEWCONTAINER_H
diff --git a/src/gui/widgets/qmaccocoaviewcontainer_mac.mm b/src/gui/widgets/qmaccocoaviewcontainer_mac.mm
new file mode 100644
index 0000000000..710af6af14
--- /dev/null
+++ b/src/gui/widgets/qmaccocoaviewcontainer_mac.mm
@@ -0,0 +1,190 @@
+/****************************************************************************
+ **
+ ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+ **
+ ** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+ **
+ ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
+ ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ **
+ ****************************************************************************/
+
+#import <Cocoa/Cocoa.h>
+#include <private/qwidget_p.h>
+#include "qmaccocoaviewcontainer_mac.h"
+#include <private/qt_mac_p.h>
+
+/*!
+ \class QMacCocoaViewContainer
+ \since 4.5
+
+ \brief The QMacCocoaViewContainer class provides a widget for Mac OS X that can be used to wrap arbitrary
+ Cocoa views (i.e., NSView subclasses) and insert them into Qt hierarchies.
+
+ \ingroup advanced
+
+ While Qt offers a lot of classes for writing your application, Apple's
+ Cocoa framework offers lots of functionality that is not currently in Qt or
+ may never end up in Qt. Using QMacCocoaViewContainer, it is possible to put an
+ arbitrary NSView-derived class from Cocoa and put it in a Qt hierarchy.
+ Depending on how comfortable you are with using objective-C, you can use
+ QMacCocoaViewContainer directly, or subclass it to wrap further functionality
+ of the underlying NSView.
+
+ QMacCocoaViewContainer works regardless if Qt is built against Carbon or
+ Cocoa. However, QCocoaContainerView requires Mac OS X 10.5 or better to be
+ used with Carbon.
+
+ It should be also noted that at the low level on Mac OS X, there is a
+ difference between windows (top-levels) and view (widgets that are inside a
+ window). For this reason, make sure that the NSView that you are wrapping
+ doesn't end up as a top-level. The best way to ensure this is to make sure
+ you always have a parent and not set the parent to 0.
+
+ If you are using QMacCocoaViewContainer as a sub-class and are mixing and
+ matching objective-C with C++ (a.k.a. objective-C++). It is probably
+ simpler to have your file end with \tt{.mm} than \tt{.cpp}. Most Apple tools will
+ correctly identify the source as objective-C++.
+
+ QMacCocoaViewContainer requires knowledge of how Cocoa works, especially in
+ regard to its reference counting (retain/release) nature. It is noted in
+ the functions below if there is any change in the reference count. Cocoa
+ views often generate temporary objects that are released by an autorelease
+ pool. If this is done outside of a running event loop, it is up to the
+ developer to provide the autorelease pool.
+
+ The following is a snippet of subclassing QMacCocoaViewContainer to wrap a NSSearchField.
+ \snippet demos/macmainwindow/macmainwindow.mm 0
+
+*/
+
+QT_BEGIN_NAMESPACE
+
+class QMacCocoaViewContainerPrivate : public QWidgetPrivate
+{
+ Q_DECLARE_PUBLIC(QMacCocoaViewContainer)
+public:
+ NSView *nsview;
+#ifndef QT_MAC_USE_COCOA
+ HIViewRef wrapperView;
+#endif
+ QMacCocoaViewContainerPrivate();
+ ~QMacCocoaViewContainerPrivate();
+};
+
+QMacCocoaViewContainerPrivate::QMacCocoaViewContainerPrivate()
+ : nsview(0)
+#ifndef QT_MAC_USE_COCOA
+ , wrapperView(0)
+#endif
+{
+}
+
+QMacCocoaViewContainerPrivate::~QMacCocoaViewContainerPrivate()
+{
+ [nsview release];
+#ifndef QT_MAC_USE_COCOA
+ if (wrapperView)
+ CFRelease(wrapperView);
+#endif
+}
+
+/*!
+ \fn QMacCocoaViewContainer::QMacCocoaViewContainer(void *cocoaViewToWrap, QWidget *parent)
+
+ Create a new QMacCocoaViewContainer using the NSView pointer in \a
+ cocoaViewToWrap with parent, \a parent. QMacCocoaViewContainer will
+ retain \a cocoaViewToWrap.
+
+ \a cocoaViewToWrap is a void pointer that allows the header to be included
+ with C++ source.
+*/
+QMacCocoaViewContainer::QMacCocoaViewContainer(void *cocoaViewToWrap, QWidget *parent)
+ : QWidget(*new QMacCocoaViewContainerPrivate, parent, 0)
+{
+ if (cocoaViewToWrap)
+ setCocoaView(cocoaViewToWrap);
+}
+
+/*!
+ Destroy the QMacCocoaViewContainer and release the wrapped view.
+*/
+QMacCocoaViewContainer::~QMacCocoaViewContainer()
+{
+}
+
+/*!
+ Returns the NSView that has been set on this container. The returned view
+ has been autoreleased, so you will need to retain it if you want to make
+ use of it.
+*/
+void *QMacCocoaViewContainer::cocoaView() const
+{
+ Q_D(const QMacCocoaViewContainer);
+ return [[d->nsview retain] autorelease];
+}
+
+/*!
+ Sets the NSView to contain to be \a cocoaViewToWrap and retains it. If this
+ container already had a view set, it will release the previously set view.
+*/
+void QMacCocoaViewContainer::setCocoaView(void *cocoaViewToWrap)
+{
+ Q_D(QMacCocoaViewContainer);
+ QMacCocoaAutoReleasePool pool;
+ NSView *view = static_cast<NSView *>(cocoaViewToWrap);
+ NSView *oldView = d->nsview;
+ destroy(true, true);
+ [view retain];
+ d->nsview = view;
+#ifndef QT_MAC_USE_COCOA
+ if (QSysInfo::MacintoshVersion < QSysInfo::MV_10_5) {
+ qWarning("QMacCocoaViewContainer::setCocoaView: You cannot use this class with Carbon on versions of Mac OS X less than 10.5.");
+ return;
+ }
+#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5
+ if (d->wrapperView)
+ CFRelease(d->wrapperView);
+ HICocoaViewCreate(d->nsview, 0, &d->wrapperView);
+ create(WId(d->wrapperView), false, true);
+#endif
+#else
+ create(WId(d->nsview), false, true);
+#endif
+ [oldView release];
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/widgets/qmacnativewidget_mac.h b/src/gui/widgets/qmacnativewidget_mac.h
new file mode 100644
index 0000000000..4db65e0d61
--- /dev/null
+++ b/src/gui/widgets/qmacnativewidget_mac.h
@@ -0,0 +1,74 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QMACNATIVEWIDGET_H
+#define QMACNATIVEWIDGET_H
+
+#include <QtGui/QWidget>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+class QMacNativeWidgetPrivate;
+class Q_GUI_EXPORT QMacNativeWidget : public QWidget
+{
+ Q_OBJECT
+public:
+ QMacNativeWidget(void *parentRef = 0);
+ ~QMacNativeWidget();
+
+ QSize sizeHint() const;
+
+protected:
+ bool event(QEvent *ev);
+
+private:
+ Q_DECLARE_PRIVATE_D(QWidget::d_ptr, QMacNativeWidget)
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QMACNATIVEWIDGET_H
diff --git a/src/gui/widgets/qmacnativewidget_mac.mm b/src/gui/widgets/qmacnativewidget_mac.mm
new file mode 100644
index 0000000000..1bc0430a47
--- /dev/null
+++ b/src/gui/widgets/qmacnativewidget_mac.mm
@@ -0,0 +1,136 @@
+/****************************************************************************
+ **
+ ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+ **
+ ** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+ **
+ ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
+ ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ **
+ ****************************************************************************/
+
+#import <Cocoa/Cocoa.h>
+#import <private/qcocoaview_mac_p.h>
+#include "qmacnativewidget_mac.h"
+#include <private/qwidget_p.h>
+
+/*!
+ \class QMacNativeWidget
+ \since 4.5
+ \brief The QMacNativeWidget class provides a widget for Mac OS X that provides a way to put Qt widgets into Carbon
+ or Cocoa hierarchies depending on how Qt was configured.
+
+ \ingroup advanced
+
+ On Mac OS X, there is a difference between a window and view;
+ normally expressed as widgets in Qt. Qt makes assumptions about its
+ parent-child hierarchy that make it complex to put an arbitrary Qt widget
+ into a hierarchy of "normal" views from Apple frameworks. QMacNativeWidget
+ bridges the gap between views and windows and makes it possible to put a
+ hierarchy of Qt widgets into a non-Qt window or view.
+
+ QMacNativeWidget pretends it is a window (i.e. isWindow() will return true),
+ but it cannot be shown on its own. It needs to be put into a window
+ when it is created or later through a native call.
+
+ QMacNativeWidget works for either Carbon or Cocoa depending on how Qt was configured. If Qt is
+ using Carbon, QMacNativeWidget will embed into Carbon hierarchies. If Qt is
+ using Cocoa, QMacNativeWidget embeds into Cocoa hierarchies.
+
+ Here is an example of putting a QPushButton into a NSWindow:
+
+ \snippet doc/src/snippets/qmacnativewidget/main.mm 0
+
+ On Carbon, this would do the equivalent:
+
+ \snippet doc/src/snippets/qmacnativewidget/main.mm 1
+
+ Note that QMacNativeWidget requires knowledge of Carbon or Cocoa. All it
+ does is get the Qt hierarchy into a window not owned by Qt. It is then up
+ to the programmer to ensure it is placed correctly in the window and
+ responds correctly to events.
+*/
+
+QT_BEGIN_NAMESPACE
+
+class QMacNativeWidgetPrivate : public QWidgetPrivate
+{
+};
+
+extern OSViewRef qt_mac_create_widget(QWidget *widget, QWidgetPrivate *widgetPrivate, OSViewRef parent);
+
+
+/*!
+ Create a QMacNativeWidget with \a parentView as its "superview" (i.e.,
+ parent). The \a parentView is either an HIViewRef if Qt is using Carbon or
+ a NSView pointer if Qt is using Cocoa.
+*/
+QMacNativeWidget::QMacNativeWidget(void *parentView)
+ : QWidget(*new QMacNativeWidgetPrivate, 0, Qt::Window)
+{
+ Q_D(QMacNativeWidget);
+ OSViewRef myView = qt_mac_create_widget(this, d, OSViewRef(parentView));
+
+ d->topData()->embedded = true;
+ create(WId(myView), false, false);
+ setPalette(QPalette(Qt::transparent));
+ setAttribute(Qt::WA_SetPalette, false);
+ setAttribute(Qt::WA_LayoutUsesWidgetRect);
+}
+
+/*!
+ Destroy the QMacNativeWidget.
+*/
+QMacNativeWidget::~QMacNativeWidget()
+{
+}
+
+/*!
+ \reimp
+*/
+QSize QMacNativeWidget::sizeHint() const
+{
+ return QSize(200, 200);
+}
+/*!
+ \reimp
+*/
+bool QMacNativeWidget::event(QEvent *ev)
+{
+ return QWidget::event(ev);
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/widgets/qmainwindow.cpp b/src/gui/widgets/qmainwindow.cpp
new file mode 100644
index 0000000000..46d64718c5
--- /dev/null
+++ b/src/gui/widgets/qmainwindow.cpp
@@ -0,0 +1,1591 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qmainwindow.h"
+#include "qmainwindowlayout_p.h"
+
+#ifndef QT_NO_MAINWINDOW
+
+#include "qdockwidget.h"
+#include "qtoolbar.h"
+
+#include <qapplication.h>
+#include <qmenubar.h>
+#include <qstatusbar.h>
+#include <qevent.h>
+#include <qstyle.h>
+#include <qdebug.h>
+#include <qpainter.h>
+
+#include <private/qwidget_p.h>
+#include "qtoolbar_p.h"
+#include "qwidgetanimator_p.h"
+#ifdef Q_WS_MAC
+#include <private/qt_mac_p.h>
+#include <private/qt_cocoa_helpers_mac_p.h>
+QT_BEGIN_NAMESPACE
+extern OSWindowRef qt_mac_window_for(const QWidget *); // qwidget_mac.cpp
+QT_END_NAMESPACE
+#endif
+
+QT_BEGIN_NAMESPACE
+
+class QMainWindowPrivate : public QWidgetPrivate
+{
+ Q_DECLARE_PUBLIC(QMainWindow)
+public:
+ inline QMainWindowPrivate()
+ : layout(0), toolButtonStyle(Qt::ToolButtonIconOnly)
+#ifdef Q_WS_MAC
+ , useHIToolBar(false)
+#endif
+#if !defined(QT_NO_DOCKWIDGET) && !defined(QT_NO_CURSOR)
+ , hasOldCursor(false) , cursorAdjusted(false)
+#endif
+ { }
+ QMainWindowLayout *layout;
+ QSize iconSize;
+ bool explicitIconSize;
+ Qt::ToolButtonStyle toolButtonStyle;
+#ifdef Q_WS_MAC
+ bool useHIToolBar;
+#endif
+ void init();
+ QList<int> hoverSeparator;
+ QPoint hoverPos;
+
+#if !defined(QT_NO_DOCKWIDGET) && !defined(QT_NO_CURSOR)
+ QCursor separatorCursor(const QList<int> &path) const;
+ void adjustCursor(const QPoint &pos);
+ QCursor oldCursor;
+ uint hasOldCursor : 1;
+ uint cursorAdjusted : 1;
+#endif
+};
+
+void QMainWindowPrivate::init()
+{
+ Q_Q(QMainWindow);
+ layout = new QMainWindowLayout(q);
+ const int metric = q->style()->pixelMetric(QStyle::PM_ToolBarIconSize, 0, q);
+ iconSize = QSize(metric, metric);
+ explicitIconSize = false;
+
+ q->setAttribute(Qt::WA_Hover);
+}
+
+/*
+ The Main Window:
+
+ +----------------------------------------------------------+
+ | Menu Bar |
+ +----------------------------------------------------------+
+ | Tool Bar Area |
+ | +--------------------------------------------------+ |
+ | | Dock Window Area | |
+ | | +------------------------------------------+ | |
+ | | | | | |
+ | | | Central Widget | | |
+ | | | | | |
+ | | | | | |
+ | | | | | |
+ | | | | | |
+ | | | | | |
+ | | | | | |
+ | | | | | |
+ | | | | | |
+ | | | | | |
+ | | | | | |
+ | | +------------------------------------------+ | |
+ | | | |
+ | +--------------------------------------------------+ |
+ | |
+ +----------------------------------------------------------+
+ | Status Bar |
+ +----------------------------------------------------------+
+
+*/
+
+/*!
+ \class QMainWindow
+ \brief The QMainWindow class provides a main application
+ window.
+ \ingroup application
+ \mainclass
+
+ \tableofcontents
+
+ \section1 Qt Main Window Framework
+
+ A main window provides a framework for building an
+ application's user interface. Qt has QMainWindow and its \l{Main
+ Window and Related Classes}{related classes} for main window
+ management. QMainWindow has its own layout to which you can add
+ \l{QToolBar}s, \l{QDockWidget}s, a
+ QMenuBar, and a QStatusBar. The layout has a center area that can
+ be occupied by any kind of widget. You can see an image of the
+ layout below.
+
+ \image mainwindowlayout.png
+
+ \note Creating a main window without a central widget is not supported.
+ You must have a central widget even if it is just a placeholder.
+
+ \section1 Creating Main Window Components
+
+ A central widget will typically be a standard Qt widget such
+ as a QTextEdit or a QGraphicsView. Custom widgets can also be
+ used for advanced applications. You set the central widget with \c
+ setCentralWidget().
+
+ Main windows have either a single (SDI) or multiple (MDI)
+ document interface. You create MDI applications in Qt by using a
+ QMdiArea as the central widget.
+
+ We will now examine each of the other widgets that can be
+ added to a main window. We give examples on how to create and add
+ them.
+
+ \section2 Creating Menus
+
+ Qt implements menus in QMenu and QMainWindow keeps them in a
+ QMenuBar. \l{QAction}{QAction}s are added to the menus, which
+ display them as menu items.
+
+ You can add new menus to the main window's menu bar by calling
+ \c menuBar(), which returns the QMenuBar for the window, and then
+ add a menu with QMenuBar::addMenu().
+
+ QMainWindow comes with a default menu bar, but you can also
+ set one yourself with \c setMenuBar(). If you wish to implement a
+ custom menu bar (i.e., not use the QMenuBar widget), you can set it
+ with \c setMenuWidget().
+
+ An example of how to create menus follows:
+
+ \snippet examples/mainwindows/application/mainwindow.cpp 26
+
+ The \c createPopupMenu() function creates popup menus when the
+ main window receives context menu events. The default
+ implementation generates a menu with the checkable actions from
+ the dock widgets and toolbars. You can reimplement \c
+ createPopupMenu() for a custom menu.
+
+ \section2 Creating Toolbars
+
+ Toolbars are implemented in the QToolBar class. You add a
+ toolbar to a main window with \c addToolBar().
+
+ You control the initial position of toolbars by assigning them
+ to a specific Qt::ToolBarArea. You can split an area by inserting
+ a toolbar break - think of this as a line break in text editing -
+ with \c addToolBarBreak() or \c insertToolBarBreak(). You can also
+ restrict placement by the user with QToolBar::setAllowedAreas()
+ and QToolBar::setMovable().
+
+ The size of toolbar icons can be retrieved with \c iconSize().
+ The sizes are platform dependent; you can set a fixed size with \c
+ setIconSize(). You can alter the appearance of all tool buttons in
+ the toolbars with \c setToolButtonStyle().
+
+ An example of toolbar creation follows:
+
+ \snippet examples/mainwindows/application/mainwindow.cpp 29
+
+ \section2 Creating Dock Widgets
+
+ Dock widgets are implemented in the QDockWidget class. A dock
+ widget is a window that can be docked into the main window. You
+ add dock widgets to a main window with \c addDockWidget().
+
+ There are four dock widget areas as given by the
+ Qt::DockWidgetArea enum: left, right, top, and bottom. You can
+ specify which dock widget area that should occupy the corners
+ where the areas overlap with \c setCorner(). By default
+ each area can only contain one row (vertical or horizontal) of
+ dock widgets, but if you enable nesting with \c
+ setDockNestingEnabled(), dock widgets can be added in either
+ direction.
+
+ Two dock widgets may also be stacked on top of each other. A
+ QTabBar is then used to select which of the widgets that should be
+ displayed.
+
+ We give an example of how to create and add dock widgets to a
+ main window:
+
+ \snippet doc/src/snippets/mainwindowsnippet.cpp 0
+
+ \section2 The Status Bar
+
+ You can set a status bar with \c setStatusBar(), but one is
+ created the first time \c statusBar() (which returns the main
+ window's status bar) is called. See QStatusBar for information on
+ how to use it.
+
+ \section1 Storing State
+
+ QMainWindow can store the state of its layout with \c
+ saveState(); it can later be retrieved with \c restoreState(). It
+ is the position and size (relative to the size of the main window)
+ of the toolbars and dock widgets that are stored.
+
+ \sa QMenuBar, QToolBar, QStatusBar, QDockWidget, {Application
+ Example}, {Dock Widgets Example}, {MDI Example}, {SDI Example},
+ {Menus Example}
+*/
+
+/*!
+ \fn void QMainWindow::iconSizeChanged(const QSize &iconSize)
+
+ This signal is emitted when the size of the icons used in the
+ window is changed. The new icon size is passed in \a iconSize.
+
+ You can connect this signal to other components to help maintain
+ a consistent appearance for your application.
+
+ \sa setIconSize()
+*/
+
+/*!
+ \fn void QMainWindow::toolButtonStyleChanged(Qt::ToolButtonStyle toolButtonStyle)
+
+ This signal is emitted when the style used for tool buttons in the
+ window is changed. The new style is passed in \a toolButtonStyle.
+
+ You can connect this signal to other components to help maintain
+ a consistent appearance for your application.
+
+ \sa setToolButtonStyle()
+*/
+
+/*!
+ Constructs a QMainWindow with the given \a parent and the specified
+ widget \a flags.
+
+ QMainWindow sets the Qt::Window flag itself, and will hence
+ always be created as a top-level widget.
+ */
+QMainWindow::QMainWindow(QWidget *parent, Qt::WindowFlags flags)
+ : QWidget(*(new QMainWindowPrivate()), parent, flags | Qt::Window)
+{
+ d_func()->init();
+}
+
+#ifdef QT3_SUPPORT
+/*!
+ \obsolete
+ Constructs a QMainWindow with the given \a parent, \a name, and
+ with the specified widget \a flags.
+ */
+QMainWindow::QMainWindow(QWidget *parent, const char *name, Qt::WindowFlags flags)
+ : QWidget(*(new QMainWindowPrivate()), parent, flags | Qt::WType_TopLevel)
+{
+ setObjectName(QString::fromAscii(name));
+ d_func()->init();
+}
+#endif
+
+/*!
+ Destroys the main window.
+ */
+QMainWindow::~QMainWindow()
+{ }
+
+/*! \property QMainWindow::iconSize
+ \brief size of toolbar icons in this mainwindow.
+
+ The default is the default tool bar icon size of the GUI style.
+ Note that the icons used must be at least of this size as the
+ icons are only scaled down.
+*/
+
+/*!
+ \property QMainWindow::dockOptions
+ \brief the docking behavior of QMainWindow
+ \since 4.3
+
+ The default value is AnimatedDocks | AllowTabbedDocks.
+*/
+
+/*!
+ \enum QMainWindow::DockOption
+ \since 4.3
+
+ This enum contains flags that specify the docking behavior of QMainWindow.
+
+ \value AnimatedDocks Identical to the \l animated property.
+
+ \value AllowNestedDocks Identical to the \l dockNestingEnabled property.
+
+ \value AllowTabbedDocks The user can drop one dock widget "on top" of
+ another. The two widgets are stacked and a tab
+ bar appears for selecting which one is visible.
+
+ \value ForceTabbedDocks Each dock area contains a single stack of tabbed
+ dock widgets. In other words, dock widgets cannot
+ be placed next to each other in a dock area. If
+ this option is set, AllowNestedDocks has no effect.
+
+ \value VerticalTabs The two vertical dock areas on the sides of the
+ main window show their tabs vertically. If this
+ option is not set, all dock areas show their tabs
+ at the bottom. Implies AllowTabbedDocks. See also
+ \l setTabPosition().
+
+ These options only control how dock widgets may be dropped in a QMainWindow.
+ They do not re-arrange the dock widgets to conform with the specified
+ options. For this reason they should be set before any dock widgets
+ are added to the main window. Exceptions to this are the AnimatedDocks and
+ VerticalTabs options, which may be set at any time.
+*/
+
+void QMainWindow::setDockOptions(DockOptions opt)
+{
+ Q_D(QMainWindow);
+ d->layout->setDockOptions(opt);
+}
+
+QMainWindow::DockOptions QMainWindow::dockOptions() const
+{
+ Q_D(const QMainWindow);
+ return d->layout->dockOptions;
+}
+
+QSize QMainWindow::iconSize() const
+{ return d_func()->iconSize; }
+
+void QMainWindow::setIconSize(const QSize &iconSize)
+{
+ Q_D(QMainWindow);
+ QSize sz = iconSize;
+ if (!sz.isValid()) {
+ const int metric = style()->pixelMetric(QStyle::PM_ToolBarIconSize, 0, this);
+ sz = QSize(metric, metric);
+ }
+ if (d->iconSize != sz) {
+ d->iconSize = sz;
+ emit iconSizeChanged(d->iconSize);
+ }
+ d->explicitIconSize = iconSize.isValid();
+}
+
+/*! \property QMainWindow::toolButtonStyle
+ \brief style of toolbar buttons in this mainwindow.
+
+ The default is Qt::ToolButtonIconOnly.
+*/
+
+Qt::ToolButtonStyle QMainWindow::toolButtonStyle() const
+{ return d_func()->toolButtonStyle; }
+
+void QMainWindow::setToolButtonStyle(Qt::ToolButtonStyle toolButtonStyle)
+{
+ Q_D(QMainWindow);
+ if (d->toolButtonStyle == toolButtonStyle)
+ return;
+ d->toolButtonStyle = toolButtonStyle;
+ emit toolButtonStyleChanged(d->toolButtonStyle);
+}
+
+#ifndef QT_NO_MENUBAR
+/*!
+ Returns the menu bar for the main window. This function creates
+ and returns an empty menu bar if the menu bar does not exist.
+
+ If you want all windows in a Mac application to share one menu
+ bar, don't use this function to create it, because the menu bar
+ created here will have this QMainWindow as its parent. Instead,
+ you must create a menu bar that does not have a parent, which you
+ can then share among all the Mac windows. Create a parent-less
+ menu bar this way:
+
+ \snippet doc/src/snippets/code/src_gui_widgets_qmenubar.cpp 1
+
+ \sa setMenuBar()
+*/
+QMenuBar *QMainWindow::menuBar() const
+{
+ QMenuBar *menuBar = qobject_cast<QMenuBar *>(d_func()->layout->menuBar());
+ if (!menuBar) {
+ QMainWindow *self = const_cast<QMainWindow *>(this);
+ menuBar = new QMenuBar(self);
+ self->setMenuBar(menuBar);
+ }
+ return menuBar;
+}
+
+/*!
+ Sets the menu bar for the main window to \a menuBar.
+
+ Note: QMainWindow takes ownership of the \a menuBar pointer and
+ deletes it at the appropriate time.
+
+ \sa menuBar()
+*/
+void QMainWindow::setMenuBar(QMenuBar *menuBar)
+{
+ Q_D(QMainWindow);
+ if (d->layout->menuBar() && d->layout->menuBar() != menuBar) {
+ // Reparent corner widgets before we delete the old menu bar.
+ QMenuBar *oldMenuBar = qobject_cast<QMenuBar *>(d->layout->menuBar());
+ if (menuBar) {
+ // TopLeftCorner widget.
+ QWidget *cornerWidget = oldMenuBar->cornerWidget(Qt::TopLeftCorner);
+ if (cornerWidget)
+ menuBar->setCornerWidget(cornerWidget, Qt::TopLeftCorner);
+ // TopRightCorner widget.
+ cornerWidget = oldMenuBar->cornerWidget(Qt::TopRightCorner);
+ if (cornerWidget)
+ menuBar->setCornerWidget(cornerWidget, Qt::TopRightCorner);
+ }
+ oldMenuBar->hide();
+ oldMenuBar->deleteLater();
+ }
+#ifdef Q_OS_WINCE
+ if (menuBar->size().height() > 0)
+#endif
+ d->layout->setMenuBar(menuBar);
+}
+
+/*!
+ \since 4.2
+
+ Returns the menu bar for the main window. This function returns
+ null if a menu bar hasn't been constructed yet.
+*/
+QWidget *QMainWindow::menuWidget() const
+{
+ QWidget *menuBar = d_func()->layout->menuBar();
+ return menuBar;
+}
+
+/*!
+ \since 4.2
+
+ Sets the menu bar for the main window to \a menuBar.
+
+ QMainWindow takes ownership of the \a menuBar pointer and
+ deletes it at the appropriate time.
+*/
+void QMainWindow::setMenuWidget(QWidget *menuBar)
+{
+ Q_D(QMainWindow);
+ if (d->layout->menuBar() && d->layout->menuBar() != menuBar) {
+ d->layout->menuBar()->hide();
+ d->layout->menuBar()->deleteLater();
+ }
+ d->layout->setMenuBar(menuBar);
+}
+#endif // QT_NO_MENUBAR
+
+#ifndef QT_NO_STATUSBAR
+/*!
+ Returns the status bar for the main window. This function creates
+ and returns an empty status bar if the status bar does not exist.
+
+ \sa setStatusBar()
+*/
+QStatusBar *QMainWindow::statusBar() const
+{
+ QStatusBar *statusbar = d_func()->layout->statusBar();
+ if (!statusbar) {
+ QMainWindow *self = const_cast<QMainWindow *>(this);
+ statusbar = new QStatusBar(self);
+ statusbar->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Fixed);
+ self->setStatusBar(statusbar);
+ }
+ return statusbar;
+}
+
+/*!
+ Sets the status bar for the main window to \a statusbar.
+
+ Setting the status bar to 0 will remove it from the main window.
+ Note that QMainWindow takes ownership of the \a statusbar pointer
+ and deletes it at the appropriate time.
+
+ \sa statusBar()
+*/
+void QMainWindow::setStatusBar(QStatusBar *statusbar)
+{
+ Q_D(QMainWindow);
+ if (d->layout->statusBar() && d->layout->statusBar() != statusbar) {
+ d->layout->statusBar()->hide();
+ d->layout->statusBar()->deleteLater();
+ }
+ d->layout->setStatusBar(statusbar);
+}
+#endif // QT_NO_STATUSBAR
+
+/*!
+ Returns the central widget for the main window. This function
+ returns zero if the central widget has not been set.
+
+ \sa setCentralWidget()
+*/
+QWidget *QMainWindow::centralWidget() const
+{ return d_func()->layout->centralWidget(); }
+
+/*!
+ Sets the given \a widget to be the main window's central widget.
+
+ Note: QMainWindow takes ownership of the \a widget pointer and
+ deletes it at the appropriate time.
+
+ \sa centralWidget()
+*/
+void QMainWindow::setCentralWidget(QWidget *widget)
+{
+ Q_D(QMainWindow);
+ if (d->layout->centralWidget() && d->layout->centralWidget() != widget) {
+ d->layout->centralWidget()->hide();
+ d->layout->centralWidget()->deleteLater();
+ }
+ d->layout->setCentralWidget(widget);
+}
+
+#ifndef QT_NO_DOCKWIDGET
+/*!
+ Sets the given dock widget \a area to occupy the specified \a
+ corner.
+
+ \sa corner()
+*/
+void QMainWindow::setCorner(Qt::Corner corner, Qt::DockWidgetArea area)
+{
+ bool valid = false;
+ switch (corner) {
+ case Qt::TopLeftCorner:
+ valid = (area == Qt::TopDockWidgetArea || area == Qt::LeftDockWidgetArea);
+ break;
+ case Qt::TopRightCorner:
+ valid = (area == Qt::TopDockWidgetArea || area == Qt::RightDockWidgetArea);
+ break;
+ case Qt::BottomLeftCorner:
+ valid = (area == Qt::BottomDockWidgetArea || area == Qt::LeftDockWidgetArea);
+ break;
+ case Qt::BottomRightCorner:
+ valid = (area == Qt::BottomDockWidgetArea || area == Qt::RightDockWidgetArea);
+ break;
+ }
+ if (!valid)
+ qWarning("QMainWindow::setCorner(): 'area' is not valid for 'corner'");
+ else
+ d_func()->layout->setCorner(corner, area);
+}
+
+/*!
+ Returns the dock widget area that occupies the specified \a
+ corner.
+
+ \sa setCorner()
+*/
+Qt::DockWidgetArea QMainWindow::corner(Qt::Corner corner) const
+{ return d_func()->layout->corner(corner); }
+#endif
+
+#ifndef QT_NO_TOOLBAR
+
+static bool checkToolBarArea(Qt::ToolBarArea area, const char *where)
+{
+ switch (area) {
+ case Qt::LeftToolBarArea:
+ case Qt::RightToolBarArea:
+ case Qt::TopToolBarArea:
+ case Qt::BottomToolBarArea:
+ return true;
+ default:
+ break;
+ }
+ qWarning("%s: invalid 'area' argument", where);
+ return false;
+}
+
+/*!
+ Adds a toolbar break to the given \a area after all the other
+ objects that are present.
+*/
+void QMainWindow::addToolBarBreak(Qt::ToolBarArea area)
+{
+ if (!checkToolBarArea(area, "QMainWindow::addToolBarBreak"))
+ return;
+ d_func()->layout->addToolBarBreak(area);
+}
+
+/*!
+ Inserts a toolbar break before the toolbar specified by \a before.
+*/
+void QMainWindow::insertToolBarBreak(QToolBar *before)
+{ d_func()->layout->insertToolBarBreak(before); }
+
+/*!
+ Removes a toolbar break previously inserted before the toolbar specified by \a before.
+*/
+
+void QMainWindow::removeToolBarBreak(QToolBar *before)
+{
+ Q_D(QMainWindow);
+ d->layout->removeToolBarBreak(before);
+}
+
+/*!
+ Adds the \a toolbar into the specified \a area in this main
+ window. The \a toolbar is placed at the end of the current tool
+ bar block (i.e. line). If the main window already manages \a toolbar
+ then it will only move the toolbar to \a area.
+
+ \sa insertToolBar() addToolBarBreak() insertToolBarBreak()
+*/
+void QMainWindow::addToolBar(Qt::ToolBarArea area, QToolBar *toolbar)
+{
+ if (!checkToolBarArea(area, "QMainWindow::addToolBar"))
+ return;
+
+ Q_D(QMainWindow);
+
+ disconnect(this, SIGNAL(iconSizeChanged(QSize)),
+ toolbar, SLOT(_q_updateIconSize(QSize)));
+ disconnect(this, SIGNAL(toolButtonStyleChanged(Qt::ToolButtonStyle)),
+ toolbar, SLOT(_q_updateToolButtonStyle(Qt::ToolButtonStyle)));
+
+ if(toolbar->d_func()->state && toolbar->d_func()->state->dragging) {
+ //removing a toolbar which is dragging will cause crash
+#ifndef QT_NO_DOCKWIDGET
+ bool animated = isAnimated();
+ setAnimated(false);
+#endif
+ toolbar->d_func()->endDrag();
+#ifndef QT_NO_DOCKWIDGET
+ setAnimated(animated);
+#endif
+ }
+
+ if (!d->layout->usesHIToolBar(toolbar)) {
+ d->layout->removeWidget(toolbar);
+ } else {
+ d->layout->removeToolBar(toolbar);
+ }
+
+ toolbar->d_func()->_q_updateIconSize(d->iconSize);
+ toolbar->d_func()->_q_updateToolButtonStyle(d->toolButtonStyle);
+ connect(this, SIGNAL(iconSizeChanged(QSize)),
+ toolbar, SLOT(_q_updateIconSize(QSize)));
+ connect(this, SIGNAL(toolButtonStyleChanged(Qt::ToolButtonStyle)),
+ toolbar, SLOT(_q_updateToolButtonStyle(Qt::ToolButtonStyle)));
+
+ d->layout->addToolBar(area, toolbar);
+}
+
+/*! \overload
+ Equivalent of calling addToolBar(Qt::TopToolBarArea, \a toolbar)
+*/
+void QMainWindow::addToolBar(QToolBar *toolbar)
+{ addToolBar(Qt::TopToolBarArea, toolbar); }
+
+/*!
+ \overload
+
+ Creates a QToolBar object, setting its window title to \a title,
+ and inserts it into the top toolbar area.
+
+ \sa setWindowTitle()
+*/
+QToolBar *QMainWindow::addToolBar(const QString &title)
+{
+ QToolBar *toolBar = new QToolBar(this);
+ toolBar->setWindowTitle(title);
+ addToolBar(toolBar);
+ return toolBar;
+}
+
+/*!
+ Inserts the \a toolbar into the area occupied by the \a before toolbar
+ so that it appears before it. For example, in normal left-to-right
+ layout operation, this means that \a toolbar will appear to the left
+ of the toolbar specified by \a before in a horizontal toolbar area.
+
+ \sa insertToolBarBreak() addToolBar() addToolBarBreak()
+*/
+void QMainWindow::insertToolBar(QToolBar *before, QToolBar *toolbar)
+{
+ Q_D(QMainWindow);
+
+ d->layout->removeToolBar(toolbar);
+
+ toolbar->d_func()->_q_updateIconSize(d->iconSize);
+ toolbar->d_func()->_q_updateToolButtonStyle(d->toolButtonStyle);
+ connect(this, SIGNAL(iconSizeChanged(QSize)),
+ toolbar, SLOT(_q_updateIconSize(QSize)));
+ connect(this, SIGNAL(toolButtonStyleChanged(Qt::ToolButtonStyle)),
+ toolbar, SLOT(_q_updateToolButtonStyle(Qt::ToolButtonStyle)));
+
+ d->layout->insertToolBar(before, toolbar);
+}
+
+/*!
+ Removes the \a toolbar from the main window layout and hides
+ it. Note that the \a toolbar is \e not deleted.
+*/
+void QMainWindow::removeToolBar(QToolBar *toolbar)
+{
+ if (toolbar) {
+ d_func()->layout->removeToolBar(toolbar);
+ toolbar->hide();
+ }
+}
+
+/*!
+ Returns the Qt::ToolBarArea for \a toolbar. If \a toolbar has not
+ been added to the main window, this function returns \c
+ Qt::NoToolBarArea.
+
+ \sa addToolBar() addToolBarBreak() Qt::ToolBarArea
+*/
+Qt::ToolBarArea QMainWindow::toolBarArea(QToolBar *toolbar) const
+{ return d_func()->layout->toolBarArea(toolbar); }
+
+/*!
+
+ Returns whether there is a toolbar
+ break before the \a toolbar.
+
+ \sa addToolBarBreak(), insertToolBarBreak()
+*/
+bool QMainWindow::toolBarBreak(QToolBar *toolbar) const
+{
+ return d_func()->layout->toolBarBreak(toolbar);
+}
+
+#endif // QT_NO_TOOLBAR
+
+#ifndef QT_NO_DOCKWIDGET
+
+/*! \property QMainWindow::animated
+ \brief whether manipulating dock widgets and tool bars is animated
+ \since 4.2
+
+ When a dock widget or tool bar is dragged over the
+ main window, the main window adjusts its contents
+ to indicate where the dock widget or tool bar will
+ be docked if it is dropped. Setting this property
+ causes QMainWindow to move its contents in a smooth
+ animation. Clearing this property causes the contents
+ to snap into their new positions.
+
+ By default, this property is set. It may be cleared if
+ the main window contains widgets which are slow at resizing
+ or repainting themselves.
+
+ Setting this property is identical to setting the AnimatedDocks
+ option using setDockOptions().
+*/
+
+bool QMainWindow::isAnimated() const
+{
+ Q_D(const QMainWindow);
+ return d->layout->dockOptions & AnimatedDocks;
+}
+
+void QMainWindow::setAnimated(bool enabled)
+{
+ Q_D(QMainWindow);
+
+ DockOptions opts = d->layout->dockOptions;
+ if (enabled)
+ opts |= AnimatedDocks;
+ else
+ opts &= ~AnimatedDocks;
+
+ d->layout->setDockOptions(opts);
+}
+
+/*! \property QMainWindow::dockNestingEnabled
+ \brief whether docks can be nested
+ \since 4.2
+
+ If this property is false, dock areas can only contain a single row
+ (horizontal or vertical) of dock widgets. If this property is true,
+ the area occupied by a dock widget can be split in either direction to contain
+ more dock widgets.
+
+ Dock nesting is only necessary in applications that contain a lot of
+ dock widgets. It gives the user greater freedom in organizing their
+ main window. However, dock nesting leads to more complex
+ (and less intuitive) behavior when a dock widget is dragged over the
+ main window, since there are more ways in which a dropped dock widget
+ may be placed in the dock area.
+
+ Setting this property is identical to setting the AllowNestedDocks option
+ using setDockOptions().
+*/
+
+bool QMainWindow::isDockNestingEnabled() const
+{
+ Q_D(const QMainWindow);
+ return d->layout->dockOptions & AllowNestedDocks;
+}
+
+void QMainWindow::setDockNestingEnabled(bool enabled)
+{
+ Q_D(QMainWindow);
+
+ DockOptions opts = d->layout->dockOptions;
+ if (enabled)
+ opts |= AllowNestedDocks;
+ else
+ opts &= ~AllowNestedDocks;
+
+ d->layout->setDockOptions(opts);
+}
+
+#if 0
+/*! \property QMainWindow::verticalTabsEnabled
+ \brief whether left and right dock areas use vertical tabs
+ \since 4.2
+
+ If this property is set to false, dock areas containing tabbed dock widgets
+ display horizontal tabs, simmilar to Visual Studio.
+
+ If this property is set to true, then the right and left dock areas display vertical
+ tabs, simmilar to KDevelop.
+
+ This property should be set before any dock widgets are added to the main window.
+*/
+
+bool QMainWindow::verticalTabsEnabled() const
+{
+ return d_func()->layout->verticalTabsEnabled();
+}
+
+void QMainWindow::setVerticalTabsEnabled(bool enabled)
+{
+ d_func()->layout->setVerticalTabsEnabled(enabled);
+}
+#endif
+
+static bool checkDockWidgetArea(Qt::DockWidgetArea area, const char *where)
+{
+ switch (area) {
+ case Qt::LeftDockWidgetArea:
+ case Qt::RightDockWidgetArea:
+ case Qt::TopDockWidgetArea:
+ case Qt::BottomDockWidgetArea:
+ return true;
+ default:
+ break;
+ }
+ qWarning("%s: invalid 'area' argument", where);
+ return false;
+}
+
+#ifndef QT_NO_TABBAR
+/*!
+ \property QMainWindow::documentMode
+ \brief whether the tab bar for tabbed dockwidgets is set to document mode.
+ \since 4.5
+
+ The default is false.
+
+ \sa QTabBar::documentMode
+*/
+bool QMainWindow::documentMode() const
+{
+ return d_func()->layout->documentMode();
+}
+
+void QMainWindow::setDocumentMode(bool enabled)
+{
+ d_func()->layout->setDocumentMode(enabled);
+}
+#endif // QT_NO_TABBAR
+
+#ifndef QT_NO_TABWIDGET
+/*!
+ \property QMainWindow::tabShape
+ \brief the tab shape used for tabbed dock widgets.
+ \since 4.5
+
+ The default is \l QTabWidget::Rounded.
+
+ \sa setTabPosition()
+*/
+QTabWidget::TabShape QMainWindow::tabShape() const
+{
+ return d_func()->layout->tabShape();
+}
+
+void QMainWindow::setTabShape(QTabWidget::TabShape tabShape)
+{
+ d_func()->layout->setTabShape(tabShape);
+}
+
+/*!
+ \since 4.5
+
+ Returns the tab position for \a area.
+
+ \note The \l VerticalTabs dock option overrides the tab positions returned
+ by this function.
+
+ \sa setTabPosition(), tabShape()
+*/
+QTabWidget::TabPosition QMainWindow::tabPosition(Qt::DockWidgetArea area) const
+{
+ if (!checkDockWidgetArea(area, "QMainWindow::tabPosition"))
+ return QTabWidget::South;
+ return d_func()->layout->tabPosition(area);
+}
+
+/*!
+ \since 4.5
+
+ Sets the tab position for the given dock widget \a areas to the specified
+ \a tabPosition. By default, all dock areas show their tabs at the bottom.
+
+ \note The \l VerticalTabs dock option overrides the tab positions set by
+ this method.
+
+ \sa tabPosition(), setTabShape()
+*/
+void QMainWindow::setTabPosition(Qt::DockWidgetAreas areas, QTabWidget::TabPosition tabPosition)
+{
+ d_func()->layout->setTabPosition(areas, tabPosition);
+}
+#endif // QT_NO_TABWIDGET
+
+/*!
+ Adds the given \a dockwidget to the specified \a area.
+*/
+void QMainWindow::addDockWidget(Qt::DockWidgetArea area, QDockWidget *dockwidget)
+{
+ if (!checkDockWidgetArea(area, "QMainWindow::addDockWidget"))
+ return;
+
+ Qt::Orientation orientation = Qt::Vertical;
+ switch (area) {
+ case Qt::TopDockWidgetArea:
+ case Qt::BottomDockWidgetArea:
+ orientation = Qt::Horizontal;
+ break;
+ default:
+ break;
+ }
+ d_func()->layout->removeWidget(dockwidget); // in case it was already in here
+ addDockWidget(area, dockwidget, orientation);
+
+#ifdef Q_WS_MAC //drawer support
+ QMacCocoaAutoReleasePool pool;
+ extern bool qt_mac_is_macdrawer(const QWidget *); //qwidget_mac.cpp
+ if (qt_mac_is_macdrawer(dockwidget)) {
+ extern bool qt_mac_set_drawer_preferred_edge(QWidget *, Qt::DockWidgetArea); //qwidget_mac.cpp
+ window()->createWinId();
+ dockwidget->window()->createWinId();
+ qt_mac_set_drawer_preferred_edge(dockwidget, area);
+ if (dockwidget->isVisible()) {
+ dockwidget->hide();
+ dockwidget->show();
+ }
+ }
+#endif
+}
+
+/*!
+ Restores the state of \a dockwidget if it is created after the call
+ to restoreState(). Returns true if the state was restored; otherwise
+ returns false.
+*/
+
+bool QMainWindow::restoreDockWidget(QDockWidget *dockwidget)
+{
+ return d_func()->layout->restoreDockWidget(dockwidget);
+}
+
+/*!
+ Adds \a dockwidget into the given \a area in the direction
+ specified by the \a orientation.
+*/
+void QMainWindow::addDockWidget(Qt::DockWidgetArea area, QDockWidget *dockwidget,
+ Qt::Orientation orientation)
+{
+ if (!checkDockWidgetArea(area, "QMainWindow::addDockWidget"))
+ return;
+
+ // add a window to an area, placing done relative to the previous
+ d_func()->layout->addDockWidget(area, dockwidget, orientation);
+}
+
+/*!
+ \fn void QMainWindow::splitDockWidget(QDockWidget *first, QDockWidget *second, Qt::Orientation orientation)
+
+ Splits the space covered by the \a first dock widget into two parts,
+ moves the \a first dock widget into the first part, and moves the
+ \a second dock widget into the second part.
+
+ The \a orientation specifies how the space is divided: A Qt::Horizontal
+ split places the second dock widget to the right of the first; a
+ Qt::Vertical split places the second dock widget below the first.
+
+ \e Note: if \a first is currently in a tabbed docked area, \a second will
+ be added as a new tab, not as a neighbor of \a first. This is because a
+ single tab can contain only one dock widget.
+
+ \e Note: The Qt::LayoutDirection influences the order of the dock widgets
+ in the two parts of the divided area. When right-to-left layout direction
+ is enabled, the placing of the dock widgets will be reversed.
+
+ \sa tabifyDockWidget(), addDockWidget(), removeDockWidget()
+*/
+void QMainWindow::splitDockWidget(QDockWidget *after, QDockWidget *dockwidget,
+ Qt::Orientation orientation)
+{
+ d_func()->layout->splitDockWidget(after, dockwidget, orientation);
+}
+
+/*!
+ \fn void QMainWindow::tabifyDockWidget(QDockWidget *first, QDockWidget *second)
+
+ Moves \a second dock widget on top of \a first dock widget, creating a tabbed
+ docked area in the main window.
+
+ \sa tabifiedDockWidgets()
+*/
+void QMainWindow::tabifyDockWidget(QDockWidget *first, QDockWidget *second)
+{
+ d_func()->layout->tabifyDockWidget(first, second);
+}
+
+
+/*!
+ \fn QList<QDockWidget*> QMainWindow::tabifiedDockWidgets(QDockWidget *dockwidget) const
+
+ Returns the dock widgets that are tabified together with \a dockwidget.
+
+ \since 4.5
+ \sa tabifyDockWidget()
+*/
+
+QList<QDockWidget*> QMainWindow::tabifiedDockWidgets(QDockWidget *dockwidget) const
+{
+ QList<QDockWidget*> ret;
+#if defined(QT_NO_TABBAR)
+ Q_UNUSED(dockwidget);
+#else
+ const QDockAreaLayoutInfo *info = d_func()->layout->layoutState.dockAreaLayout.info(dockwidget);
+ if (info && info->tabbed && info->tabBar) {
+ for(int i = 0; i < info->item_list.count(); ++i) {
+ const QDockAreaLayoutItem &item = info->item_list.at(i);
+ if (item.widgetItem) {
+ if (QDockWidget *dock = qobject_cast<QDockWidget*>(item.widgetItem->widget())) {
+ if (dock != dockwidget) {
+ ret += dock;
+ }
+ }
+ }
+ }
+ }
+#endif
+ return ret;
+}
+
+
+/*!
+ Removes the \a dockwidget from the main window layout and hides
+ it. Note that the \a dockwidget is \e not deleted.
+*/
+void QMainWindow::removeDockWidget(QDockWidget *dockwidget)
+{
+ if (dockwidget) {
+ d_func()->layout->removeWidget(dockwidget);
+ dockwidget->hide();
+ }
+}
+
+/*!
+ Returns the Qt::DockWidgetArea for \a dockwidget. If \a dockwidget
+ has not been added to the main window, this function returns \c
+ Qt::NoDockWidgetArea.
+
+ \sa addDockWidget() splitDockWidget() Qt::DockWidgetArea
+*/
+Qt::DockWidgetArea QMainWindow::dockWidgetArea(QDockWidget *dockwidget) const
+{ return d_func()->layout->dockWidgetArea(dockwidget); }
+
+#endif // QT_NO_DOCKWIDGET
+
+/*!
+ Saves the current state of this mainwindow's toolbars and
+ dockwidgets. The \a version number is stored as part of the data.
+
+ The \link QObject::objectName objectName\endlink property is used
+ to identify each QToolBar and QDockWidget. You should make sure
+ that this property is unique for each QToolBar and QDockWidget you
+ add to the QMainWindow
+
+ To restore the saved state, pass the return value and \a version
+ number to restoreState().
+
+ \sa restoreState(), QWidget::saveGeometry(), QWidget::restoreGeometry()
+*/
+QByteArray QMainWindow::saveState(int version) const
+{
+ QByteArray data;
+ QDataStream stream(&data, QIODevice::WriteOnly);
+ stream << QMainWindowLayout::VersionMarker;
+ stream << version;
+ d_func()->layout->saveState(stream);
+ return data;
+}
+
+/*!
+ Restores the \a state of this mainwindow's toolbars and
+ dockwidgets. The \a version number is compared with that stored
+ in \a state. If they do not match, the mainwindow's state is left
+ unchanged, and this function returns \c false; otherwise, the state
+ is restored, and this function returns \c true.
+
+ \sa saveState(), QWidget::saveGeometry(), QWidget::restoreGeometry()
+*/
+bool QMainWindow::restoreState(const QByteArray &state, int version)
+{
+ if (state.isEmpty())
+ return false;
+ QByteArray sd = state;
+ QDataStream stream(&sd, QIODevice::ReadOnly);
+ int marker, v;
+ stream >> marker;
+ stream >> v;
+ if (stream.status() != QDataStream::Ok || marker != QMainWindowLayout::VersionMarker || v != version)
+ return false;
+ bool restored = d_func()->layout->restoreState(stream);
+ return restored;
+}
+
+#if !defined(QT_NO_DOCKWIDGET) && !defined(QT_NO_CURSOR)
+QCursor QMainWindowPrivate::separatorCursor(const QList<int> &path) const
+{
+ QDockAreaLayoutInfo *info = layout->layoutState.dockAreaLayout.info(path);
+ Q_ASSERT(info != 0);
+ if (path.size() == 1) { // is this the "top-level" separator which separates a dock area
+ // from the central widget?
+ switch (path.first()) {
+ case QInternal::LeftDock:
+ case QInternal::RightDock:
+ return Qt::SplitHCursor;
+ case QInternal::TopDock:
+ case QInternal::BottomDock:
+ return Qt::SplitVCursor;
+ default:
+ break;
+ }
+ }
+
+ // no, it's a splitter inside a dock area, separating two dock widgets
+
+ return info->o == Qt::Horizontal
+ ? Qt::SplitHCursor : Qt::SplitVCursor;
+}
+
+void QMainWindowPrivate::adjustCursor(const QPoint &pos)
+{
+ Q_Q(QMainWindow);
+
+ hoverPos = pos;
+
+ if (pos == QPoint(0, 0)) {
+ if (!hoverSeparator.isEmpty())
+ q->update(layout->layoutState.dockAreaLayout.separatorRect(hoverSeparator));
+ hoverSeparator.clear();
+
+ if (cursorAdjusted) {
+ cursorAdjusted = false;
+ if (hasOldCursor)
+ q->setCursor(oldCursor);
+ else
+ q->unsetCursor();
+ }
+ } else {
+ QList<int> pathToSeparator
+ = layout->layoutState.dockAreaLayout.findSeparator(pos);
+
+ if (pathToSeparator != hoverSeparator) {
+ if (!hoverSeparator.isEmpty())
+ q->update(layout->layoutState.dockAreaLayout.separatorRect(hoverSeparator));
+
+ hoverSeparator = pathToSeparator;
+
+ if (hoverSeparator.isEmpty()) {
+ if (cursorAdjusted) {
+ cursorAdjusted = false;
+ if (hasOldCursor)
+ q->setCursor(oldCursor);
+ else
+ q->unsetCursor();
+ }
+ } else {
+ q->update(layout->layoutState.dockAreaLayout.separatorRect(hoverSeparator));
+ if (!cursorAdjusted) {
+ oldCursor = q->cursor();
+ hasOldCursor = q->testAttribute(Qt::WA_SetCursor);
+ }
+ QCursor cursor = separatorCursor(hoverSeparator);
+ cursorAdjusted = false; //to not reset the oldCursor in event(CursorChange)
+ q->setCursor(cursor);
+ cursorAdjusted = true;
+ }
+ }
+ }
+}
+#endif
+
+/*! \reimp */
+bool QMainWindow::event(QEvent *event)
+{
+ Q_D(QMainWindow);
+ switch (event->type()) {
+
+#ifndef QT_NO_DOCKWIDGET
+ case QEvent::Paint: {
+ QPainter p(this);
+ QRegion r = static_cast<QPaintEvent*>(event)->region();
+ d->layout->layoutState.dockAreaLayout.paintSeparators(&p, this, r, d->hoverPos);
+ break;
+ }
+
+#ifndef QT_NO_CURSOR
+ case QEvent::HoverMove: {
+ d->adjustCursor(static_cast<QHoverEvent*>(event)->pos());
+ break;
+ }
+
+ // We don't want QWidget to call update() on the entire QMainWindow
+ // on HoverEnter and HoverLeave, hence accept the event (return true).
+ case QEvent::HoverEnter:
+ return true;
+ case QEvent::HoverLeave:
+ d->adjustCursor(QPoint(0, 0));
+ return true;
+ case QEvent::ShortcutOverride: // when a menu pops up
+ d->adjustCursor(QPoint(0, 0));
+ break;
+#endif // QT_NO_CURSOR
+
+ case QEvent::MouseButtonPress: {
+ QMouseEvent *e = static_cast<QMouseEvent*>(event);
+ if (e->button() == Qt::LeftButton && d->layout->startSeparatorMove(e->pos())) {
+ // The click was on a separator, eat this event
+ e->accept();
+ return true;
+ }
+ break;
+ }
+
+ case QEvent::MouseMove: {
+ QMouseEvent *e = static_cast<QMouseEvent*>(event);
+
+#ifndef QT_NO_CURSOR
+ d->adjustCursor(e->pos());
+#endif
+ if (e->buttons() & Qt::LeftButton) {
+ if (d->layout->separatorMove(e->pos())) {
+ // We're moving a separator, eat this event
+ e->accept();
+ return true;
+ }
+ }
+
+ break;
+ }
+
+ case QEvent::MouseButtonRelease: {
+ QMouseEvent *e = static_cast<QMouseEvent*>(event);
+ if (d->layout->endSeparatorMove(e->pos())) {
+ // We've released a separator, eat this event
+ e->accept();
+ return true;
+ }
+ break;
+ }
+
+#endif
+
+#ifndef QT_NO_TOOLBAR
+ case QEvent::ToolBarChange: {
+ d->layout->toggleToolBarsVisible();
+ return true;
+ }
+#endif
+
+#ifndef QT_NO_STATUSTIP
+ case QEvent::StatusTip:
+#ifndef QT_NO_STATUSBAR
+ if (QStatusBar *sb = d->layout->statusBar())
+ sb->showMessage(static_cast<QStatusTipEvent*>(event)->tip());
+ else
+#endif
+ static_cast<QStatusTipEvent*>(event)->ignore();
+ return true;
+#endif // QT_NO_STATUSTIP
+
+ case QEvent::StyleChange:
+ if (!d->explicitIconSize)
+ setIconSize(QSize());
+ break;
+#ifdef Q_WS_MAC
+ case QEvent::Show:
+ if (unifiedTitleAndToolBarOnMac())
+ macWindowToolbarShow(this, true);
+ break;
+# ifdef QT_MAC_USE_COCOA
+ case QEvent::WindowStateChange:
+ {
+ // We need to update the HIToolbar status when we go out of or into fullscreen.
+ QWindowStateChangeEvent *wce = static_cast<QWindowStateChangeEvent *>(event);
+ if ((windowState() & Qt::WindowFullScreen) || (wce->oldState() & Qt::WindowFullScreen)) {
+ d->layout->updateHIToolBarStatus();
+ }
+ }
+ break;
+# endif // Cocoa
+#endif
+#if !defined(QT_NO_DOCKWIDGET) && !defined(QT_NO_CURSOR)
+ case QEvent::CursorChange:
+ if (d->cursorAdjusted) {
+ d->oldCursor = cursor();
+ d->hasOldCursor = testAttribute(Qt::WA_SetCursor);
+ }
+ break;
+#endif
+
+ default:
+ break;
+ }
+
+ return QWidget::event(event);
+}
+
+#ifndef QT_NO_TOOLBAR
+
+/*!
+ \property QMainWindow::unifiedTitleAndToolBarOnMac
+ \brief whether the window uses the unified title and toolbar look on Mac OS X
+ \since 4.3
+
+ This property is false by default and only has any effect on Mac OS X 10.4 or higher.
+
+ If set to true, then the top toolbar area is replaced with a Carbon
+ HIToolbar and all toolbars in the top toolbar area are moved to that. Any
+ toolbars added afterwards will also be added to the Carbon HIToolbar. This
+ means a couple of things.
+
+ \list
+ \i QToolBars in this toolbar area are not movable and you cannot drag other
+ toolbars to it
+ \i Toolbar breaks are not respected or preserved
+ \i Any custom widgets in the toolbar will not be shown if the toolbar
+ becomes too small (only actions will be shown)
+ \i If you call showFullScreen() on the main window, the QToolbar will
+ disappear since it is considered to be part of the title bar. You can
+ work around this by turning off the unified toolbar before you call
+ showFullScreen() and restoring it after you call showNormal().
+ \endlist
+
+ Setting this back to false will remove these restrictions.
+
+ The Qt::WA_MacBrushedMetal attribute takes precedence over this property.
+*/
+void QMainWindow::setUnifiedTitleAndToolBarOnMac(bool set)
+{
+#ifdef Q_WS_MAC
+ Q_D(QMainWindow);
+ if (!isWindow() || d->useHIToolBar == set || QSysInfo::MacintoshVersion < QSysInfo::MV_10_3)
+ return;
+
+ // ### Disable the unified toolbar when using anything but the native graphics system.
+ if (windowSurface())
+ return;
+
+ d->useHIToolBar = set;
+ createWinId(); // We need the hiview for down below.
+
+ d->layout->updateHIToolBarStatus();
+ // Enabling the unified toolbar clears the opaque size grip setting, update it.
+ d->macUpdateOpaqueSizeGrip();
+#else
+ Q_UNUSED(set)
+#endif
+}
+
+bool QMainWindow::unifiedTitleAndToolBarOnMac() const
+{
+#ifdef Q_WS_MAC
+ return d_func()->useHIToolBar && !testAttribute(Qt::WA_MacBrushedMetal) && !(windowFlags() & Qt::FramelessWindowHint);
+#endif
+ return false;
+}
+
+#endif // QT_NO_TOOLBAR
+
+/*!
+ \internal
+*/
+bool QMainWindow::isSeparator(const QPoint &pos) const
+{
+#ifndef QT_NO_DOCKWIDGET
+ Q_D(const QMainWindow);
+ return !d->layout->layoutState.dockAreaLayout.findSeparator(pos).isEmpty();
+#else
+ Q_UNUSED(pos);
+ return false;
+#endif
+}
+
+#ifndef QT_NO_CONTEXTMENU
+/*!
+ \reimp
+*/
+void QMainWindow::contextMenuEvent(QContextMenuEvent *event)
+{
+ event->ignore();
+ // only show the context menu for direct QDockWidget and QToolBar
+ // children and for the menu bar as well
+ QWidget *child = childAt(event->pos());
+ while (child && child != this) {
+#ifndef QT_NO_MENUBAR
+ if (QMenuBar *mb = qobject_cast<QMenuBar *>(child)) {
+ if (mb->parentWidget() != this)
+ return;
+ break;
+ }
+#endif
+#ifndef QT_NO_DOCKWIDGET
+ if (QDockWidget *dw = qobject_cast<QDockWidget *>(child)) {
+ if (dw->parentWidget() != this)
+ return;
+ if (dw->widget()
+ && dw->widget()->geometry().contains(child->mapFrom(this, event->pos()))) {
+ // ignore the event if the mouse is over the QDockWidget contents
+ return;
+ }
+ break;
+ }
+#endif // QT_NO_DOCKWIDGET
+#ifndef QT_NO_TOOLBAR
+ if (QToolBar *tb = qobject_cast<QToolBar *>(child)) {
+ if (tb->parentWidget() != this)
+ return;
+ break;
+ }
+#endif
+ child = child->parentWidget();
+ }
+ if (child == this)
+ return;
+
+#ifndef QT_NO_MENU
+ QMenu *popup = createPopupMenu();
+ if (popup && !popup->isEmpty()) {
+ popup->exec(event->globalPos());
+ event->accept();
+ }
+ delete popup;
+#endif
+}
+#endif // QT_NO_CONTEXTMENU
+
+#ifndef QT_NO_MENU
+/*!
+ Returns a popup menu containing checkable entries for the toolbars and
+ dock widgets present in the main window. If there are no toolbars and
+ dock widgets present, this function returns a null pointer.
+
+ By default, this function is called by the main window when the user
+ activates a context menu, typically by right-clicking on a toolbar or a dock
+ widget.
+
+ If you want to create a custom popup menu, reimplement this function and
+ return a newly-created popup menu. Ownership of the popup menu is transferred
+ to the caller.
+
+ \sa addDockWidget(), addToolBar(), menuBar()
+*/
+QMenu *QMainWindow::createPopupMenu()
+{
+ Q_D(QMainWindow);
+ QMenu *menu = 0;
+#ifndef QT_NO_DOCKWIDGET
+ QList<QDockWidget *> dockwidgets = qFindChildren<QDockWidget *>(this);
+ if (dockwidgets.size()) {
+ menu = new QMenu(this);
+ for (int i = 0; i < dockwidgets.size(); ++i) {
+ QDockWidget *dockWidget = dockwidgets.at(i);
+ if (dockWidget->parentWidget() == this
+ && !d->layout->layoutState.dockAreaLayout.indexOf(dockWidget).isEmpty()) {
+ menu->addAction(dockwidgets.at(i)->toggleViewAction());
+ }
+ }
+ menu->addSeparator();
+ }
+#endif // QT_NO_DOCKWIDGET
+#ifndef QT_NO_TOOLBAR
+ QList<QToolBar *> toolbars = qFindChildren<QToolBar *>(this);
+ if (toolbars.size()) {
+ if (!menu)
+ menu = new QMenu(this);
+ for (int i = 0; i < toolbars.size(); ++i) {
+ QToolBar *toolBar = toolbars.at(i);
+ if (toolBar->parentWidget() == this
+ && (!d->layout->layoutState.toolBarAreaLayout.indexOf(toolBar).isEmpty()
+ || (unifiedTitleAndToolBarOnMac()
+ && toolBarArea(toolBar) == Qt::TopToolBarArea))) {
+ menu->addAction(toolbars.at(i)->toggleViewAction());
+ }
+ }
+ }
+#endif
+ Q_UNUSED(d);
+ return menu;
+}
+#endif // QT_NO_MENU
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_MAINWINDOW
diff --git a/src/gui/widgets/qmainwindow.h b/src/gui/widgets/qmainwindow.h
new file mode 100644
index 0000000000..9983c7a3c2
--- /dev/null
+++ b/src/gui/widgets/qmainwindow.h
@@ -0,0 +1,217 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QDYNAMICMAINWINDOW_H
+#define QDYNAMICMAINWINDOW_H
+
+#include <QtGui/qwidget.h>
+#include <QtGui/qtabwidget.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+#ifndef QT_NO_MAINWINDOW
+
+class QDockWidget;
+class QMainWindowPrivate;
+class QMenuBar;
+class QStatusBar;
+class QToolBar;
+class QMenu;
+
+class Q_GUI_EXPORT QMainWindow : public QWidget
+{
+ Q_OBJECT
+
+ Q_ENUMS(DockOption)
+ Q_FLAGS(DockOptions)
+ Q_PROPERTY(QSize iconSize READ iconSize WRITE setIconSize)
+ Q_PROPERTY(Qt::ToolButtonStyle toolButtonStyle READ toolButtonStyle WRITE setToolButtonStyle)
+#ifndef QT_NO_DOCKWIDGET
+ Q_PROPERTY(bool animated READ isAnimated WRITE setAnimated)
+#ifndef QT_NO_TABBAR
+ Q_PROPERTY(bool documentMode READ documentMode WRITE setDocumentMode)
+#endif // QT_NO_TABBAR
+#ifndef QT_NO_TABWIDGET
+ Q_PROPERTY(QTabWidget::TabShape tabShape READ tabShape WRITE setTabShape)
+#endif // QT_NO_TABWIDGET
+ Q_PROPERTY(bool dockNestingEnabled READ isDockNestingEnabled WRITE setDockNestingEnabled)
+#endif // QT_NO_DOCKWIDGET
+ Q_PROPERTY(DockOptions dockOptions READ dockOptions WRITE setDockOptions)
+#ifndef QT_NO_TOOLBAR
+ Q_PROPERTY(bool unifiedTitleAndToolBarOnMac READ unifiedTitleAndToolBarOnMac WRITE setUnifiedTitleAndToolBarOnMac)
+#endif
+
+public:
+ enum DockOption {
+ AnimatedDocks = 0x01,
+ AllowNestedDocks = 0x02,
+ AllowTabbedDocks = 0x04,
+ ForceTabbedDocks = 0x08, // implies AllowTabbedDocks, !AllowNestedDocks
+ VerticalTabs = 0x10 // implies AllowTabbedDocks
+ };
+ Q_DECLARE_FLAGS(DockOptions, DockOption)
+
+ explicit QMainWindow(QWidget *parent = 0, Qt::WindowFlags flags = 0);
+ ~QMainWindow();
+
+ QSize iconSize() const;
+ void setIconSize(const QSize &iconSize);
+
+ Qt::ToolButtonStyle toolButtonStyle() const;
+ void setToolButtonStyle(Qt::ToolButtonStyle toolButtonStyle);
+
+ bool isAnimated() const;
+ bool isDockNestingEnabled() const;
+
+#ifndef QT_NO_TABBAR
+ bool documentMode() const;
+ void setDocumentMode(bool enabled);
+#endif
+
+#ifndef QT_NO_TABWIDGET
+ QTabWidget::TabShape tabShape() const;
+ void setTabShape(QTabWidget::TabShape tabShape);
+ QTabWidget::TabPosition tabPosition(Qt::DockWidgetArea area) const;
+ void setTabPosition(Qt::DockWidgetAreas areas, QTabWidget::TabPosition tabPosition);
+#endif // QT_NO_TABWIDGET
+
+ void setDockOptions(DockOptions options);
+ DockOptions dockOptions() const;
+
+ bool isSeparator(const QPoint &pos) const;
+
+#ifndef QT_NO_MENUBAR
+ QMenuBar *menuBar() const;
+ void setMenuBar(QMenuBar *menubar);
+
+ QWidget *menuWidget() const;
+ void setMenuWidget(QWidget *menubar);
+#endif
+
+#ifndef QT_NO_STATUSBAR
+ QStatusBar *statusBar() const;
+ void setStatusBar(QStatusBar *statusbar);
+#endif
+
+ QWidget *centralWidget() const;
+ void setCentralWidget(QWidget *widget);
+
+#ifndef QT_NO_DOCKWIDGET
+ void setCorner(Qt::Corner corner, Qt::DockWidgetArea area);
+ Qt::DockWidgetArea corner(Qt::Corner corner) const;
+#endif
+
+#ifndef QT_NO_TOOLBAR
+ void addToolBarBreak(Qt::ToolBarArea area = Qt::TopToolBarArea);
+ void insertToolBarBreak(QToolBar *before);
+
+ void addToolBar(Qt::ToolBarArea area, QToolBar *toolbar);
+ void addToolBar(QToolBar *toolbar);
+ QToolBar *addToolBar(const QString &title);
+ void insertToolBar(QToolBar *before, QToolBar *toolbar);
+ void removeToolBar(QToolBar *toolbar);
+ void removeToolBarBreak(QToolBar *before);
+
+ void setUnifiedTitleAndToolBarOnMac(bool set);
+ bool unifiedTitleAndToolBarOnMac() const;
+
+ Qt::ToolBarArea toolBarArea(QToolBar *toolbar) const;
+ bool toolBarBreak(QToolBar *toolbar) const;
+#endif
+#ifndef QT_NO_DOCKWIDGET
+ void addDockWidget(Qt::DockWidgetArea area, QDockWidget *dockwidget);
+ void addDockWidget(Qt::DockWidgetArea area, QDockWidget *dockwidget,
+ Qt::Orientation orientation);
+ void splitDockWidget(QDockWidget *after, QDockWidget *dockwidget,
+ Qt::Orientation orientation);
+ void tabifyDockWidget(QDockWidget *first, QDockWidget *second);
+ QList<QDockWidget*> tabifiedDockWidgets(QDockWidget *dockwidget) const;
+ void removeDockWidget(QDockWidget *dockwidget);
+ bool restoreDockWidget(QDockWidget *dockwidget);
+
+ Qt::DockWidgetArea dockWidgetArea(QDockWidget *dockwidget) const;
+#endif // QT_NO_DOCKWIDGET
+
+ QByteArray saveState(int version = 0) const;
+ bool restoreState(const QByteArray &state, int version = 0);
+
+#ifndef QT_NO_MENU
+ virtual QMenu *createPopupMenu();
+#endif
+
+#ifdef QT3_SUPPORT
+ QT3_SUPPORT_CONSTRUCTOR QMainWindow(QWidget *parent, const char *name, Qt::WindowFlags flags = 0);
+#endif
+
+#ifndef QT_NO_DOCKWIDGET
+public Q_SLOTS:
+ void setAnimated(bool enabled);
+ void setDockNestingEnabled(bool enabled);
+#endif
+
+Q_SIGNALS:
+ void iconSizeChanged(const QSize &iconSize);
+ void toolButtonStyleChanged(Qt::ToolButtonStyle toolButtonStyle);
+
+protected:
+#ifndef QT_NO_CONTEXTMENU
+ void contextMenuEvent(QContextMenuEvent *event);
+#endif
+ bool event(QEvent *event);
+
+private:
+ Q_DECLARE_PRIVATE(QMainWindow)
+ Q_DISABLE_COPY(QMainWindow)
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(QMainWindow::DockOptions)
+
+#endif // QT_NO_MAINWINDOW
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QDYNAMICMAINWINDOW_H
diff --git a/src/gui/widgets/qmainwindowlayout.cpp b/src/gui/widgets/qmainwindowlayout.cpp
new file mode 100644
index 0000000000..768446e30e
--- /dev/null
+++ b/src/gui/widgets/qmainwindowlayout.cpp
@@ -0,0 +1,1986 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qmainwindowlayout_p.h"
+#include "qdockarealayout_p.h"
+
+#ifndef QT_NO_MAINWINDOW
+#include "qdockwidget.h"
+#include "qdockwidget_p.h"
+#include "qtoolbar_p.h"
+#include "qmainwindow.h"
+#include "qmainwindowlayout_p.h"
+#include "qtoolbar.h"
+#include "qtoolbarlayout_p.h"
+#include "qwidgetanimator_p.h"
+#include "qrubberband.h"
+#include "qdockwidget_p.h"
+#include "qtabbar_p.h"
+
+#include <qapplication.h>
+#include <qstatusbar.h>
+#include <qstring.h>
+#include <qstyle.h>
+#include <qvarlengtharray.h>
+#include <qstack.h>
+#include <qmap.h>
+#include <qtimer.h>
+
+#include <qdebug.h>
+
+#include <private/qapplication_p.h>
+#include <private/qlayoutengine_p.h>
+#ifdef Q_WS_MAC
+# include <private/qcore_mac_p.h>
+# include <private/qt_cocoa_helpers_mac_p.h>
+#endif
+
+#ifdef Q_DEBUG_MAINWINDOW_LAYOUT
+# include <QTextStream>
+#endif
+
+QT_BEGIN_NAMESPACE
+
+/******************************************************************************
+** debug
+*/
+
+#if defined(Q_DEBUG_MAINWINDOW_LAYOUT) && !defined(QT_NO_DOCKWIDGET)
+
+static QTextStream qout(stderr, QIODevice::WriteOnly);
+
+static void dumpLayout(QTextStream &qout, const QDockAreaLayoutInfo &layout, QString indent);
+
+static void dumpLayout(QTextStream &qout, const QDockAreaLayoutItem &item, QString indent)
+{
+ qout << indent << "QDockAreaLayoutItem: "
+ << "pos: " << item.pos << " size:" << item.size
+ << " gap:" << (item.flags & QDockAreaLayoutItem::GapItem)
+ << " keepSize:" << (item.flags & QDockAreaLayoutItem::KeepSize) << '\n';
+ indent += QLatin1String(" ");
+ if (item.widgetItem != 0) {
+ qout << indent << "widget: "
+ << item.widgetItem->widget()->metaObject()->className()
+ << ' ' << item.widgetItem->widget()->windowTitle() << '\n';
+ } else if (item.subinfo != 0) {
+ qout << indent << "subinfo:\n";
+ dumpLayout(qout, *item.subinfo, indent + QLatin1String(" "));
+ } else if (item.placeHolderItem != 0) {
+ QRect r = item.placeHolderItem->topLevelRect;
+ qout << indent << "placeHolder: "
+ << "pos: " << item.pos << " size:" << item.size
+ << " gap:" << (item.flags & QDockAreaLayoutItem::GapItem)
+ << " keepSize:" << (item.flags & QDockAreaLayoutItem::KeepSize)
+ << " objectName:" << item.placeHolderItem->objectName
+ << " hidden:" << item.placeHolderItem->hidden
+ << " window:" << item.placeHolderItem->window
+ << " rect:" << r.x() << ',' << r.y() << ' '
+ << r.width() << 'x' << r.height() << '\n';
+ }
+ qout.flush();
+}
+
+static void dumpLayout(QTextStream &qout, const QDockAreaLayoutInfo &layout, QString indent)
+{
+ qout << indent << "QDockAreaLayoutInfo: "
+ << layout.rect.left() << ','
+ << layout.rect.top() << ' '
+ << layout.rect.width() << 'x'
+ << layout.rect.height()
+ << " orient:" << layout.o
+ << " tabbed:" << layout.tabbed
+ << " tbshape:" << layout.tabBarShape << '\n';
+
+ indent += QLatin1String(" ");
+
+ for (int i = 0; i < layout.item_list.count(); ++i) {
+ qout << indent << "Item: " << i << '\n';
+ dumpLayout(qout, layout.item_list.at(i), indent + QLatin1String(" "));
+ }
+ qout.flush();
+};
+
+static void dumpLayout(QTextStream &qout, const QDockAreaLayout &layout, QString indent)
+{
+ qout << indent << "QDockAreaLayout: "
+ << layout.rect.left() << ','
+ << layout.rect.top() << ' '
+ << layout.rect.width() << 'x'
+ << layout.rect.height() << '\n';
+
+ qout << indent << "TopDockArea:\n";
+ dumpLayout(qout, layout.docks[QInternal::TopDock], indent + QLatin1String(" "));
+ qout << indent << "LeftDockArea:\n";
+ dumpLayout(qout, layout.docks[QInternal::LeftDock], indent + QLatin1String(" "));
+ qout << indent << "RightDockArea:\n";
+ dumpLayout(qout, layout.docks[QInternal::RightDock], indent + QLatin1String(" "));
+ qout << indent << "BottomDockArea:\n";
+ dumpLayout(qout, layout.docks[QInternal::BottomDock], indent + QLatin1String(" "));
+
+ qout.flush();
+};
+
+void qt_dumpLayout(QTextStream &qout, QMainWindow *window)
+{
+ QMainWindowLayout *layout = qobject_cast<QMainWindowLayout*>(window->layout());
+ dumpLayout(qout, layout->layoutState.dockAreaLayout, QString());
+}
+
+#endif // Q_DEBUG_MAINWINDOW_LAYOUT && !QT_NO_DOCKWIDGET
+
+/******************************************************************************
+** QMainWindowLayoutState
+*/
+
+// we deal with all the #ifndefferry here so QMainWindowLayout code is clean
+
+QMainWindowLayoutState::QMainWindowLayoutState(QMainWindow *win)
+ :
+#ifndef QT_NO_TOOLBAR
+ toolBarAreaLayout(win),
+#endif
+#ifndef QT_NO_DOCKWIDGET
+ dockAreaLayout(win)
+#else
+ centralWidgetItem(0)
+#endif
+
+{
+ mainWindow = win;
+}
+
+QSize QMainWindowLayoutState::sizeHint() const
+{
+
+ QSize result(0, 0);
+
+#ifndef QT_NO_DOCKWIDGET
+ result = dockAreaLayout.sizeHint();
+#else
+ if (centralWidgetItem != 0)
+ result = centralWidgetItem->sizeHint();
+#endif
+
+#ifndef QT_NO_TOOLBAR
+ result = toolBarAreaLayout.sizeHint(result);
+#endif // QT_NO_TOOLBAR
+
+ return result;
+}
+
+QSize QMainWindowLayoutState::minimumSize() const
+{
+ QSize result(0, 0);
+
+#ifndef QT_NO_DOCKWIDGET
+ result = dockAreaLayout.minimumSize();
+#else
+ if (centralWidgetItem != 0)
+ result = centralWidgetItem->minimumSize();
+#endif
+
+#ifndef QT_NO_TOOLBAR
+ result = toolBarAreaLayout.minimumSize(result);
+#endif // QT_NO_TOOLBAR
+
+ return result;
+}
+
+void QMainWindowLayoutState::apply(bool animated)
+{
+#ifndef QT_NO_TOOLBAR
+ toolBarAreaLayout.apply(animated);
+#endif
+
+#ifndef QT_NO_DOCKWIDGET
+// dumpLayout(dockAreaLayout, QString());
+ dockAreaLayout.apply(animated);
+#else
+ if (centralWidgetItem != 0) {
+ QMainWindowLayout *layout = qobject_cast<QMainWindowLayout*>(mainWindow->layout());
+ Q_ASSERT(layout != 0);
+ layout->widgetAnimator->animate(centralWidgetItem->widget(), centralWidgetRect, animated);
+ }
+#endif
+}
+
+void QMainWindowLayoutState::fitLayout()
+{
+ QRect r;
+#ifdef QT_NO_TOOLBAR
+ r = rect;
+#else
+ toolBarAreaLayout.rect = rect;
+ r = toolBarAreaLayout.fitLayout();
+#endif // QT_NO_TOOLBAR
+
+#ifndef QT_NO_DOCKWIDGET
+ dockAreaLayout.rect = r;
+ dockAreaLayout.fitLayout();
+#else
+ centralWidgetRect = r;
+#endif
+}
+
+void QMainWindowLayoutState::deleteAllLayoutItems()
+{
+#ifndef QT_NO_TOOLBAR
+ toolBarAreaLayout.deleteAllLayoutItems();
+#endif
+
+#ifndef QT_NO_DOCKWIDGET
+ dockAreaLayout.deleteAllLayoutItems();
+#endif
+}
+
+void QMainWindowLayoutState::deleteCentralWidgetItem()
+{
+#ifndef QT_NO_DOCKWIDGET
+ delete dockAreaLayout.centralWidgetItem;
+ dockAreaLayout.centralWidgetItem = 0;
+#else
+ delete centralWidgetItem;
+ centralWidgetItem = 0;
+#endif
+}
+
+QLayoutItem *QMainWindowLayoutState::itemAt(int index, int *x) const
+{
+#ifndef QT_NO_TOOLBAR
+ if (QLayoutItem *ret = toolBarAreaLayout.itemAt(x, index))
+ return ret;
+#endif
+
+#ifndef QT_NO_DOCKWIDGET
+ if (QLayoutItem *ret = dockAreaLayout.itemAt(x, index))
+ return ret;
+#else
+ if (centralWidgetItem != 0 && (*x)++ == index)
+ return centralWidgetItem;
+#endif
+
+ return 0;
+}
+
+QLayoutItem *QMainWindowLayoutState::takeAt(int index, int *x)
+{
+#ifndef QT_NO_TOOLBAR
+ if (QLayoutItem *ret = toolBarAreaLayout.takeAt(x, index))
+ return ret;
+#endif
+
+#ifndef QT_NO_DOCKWIDGET
+ if (QLayoutItem *ret = dockAreaLayout.takeAt(x, index))
+ return ret;
+#else
+ if (centralWidgetItem != 0 && (*x)++ == index) {
+ QLayoutItem *ret = centralWidgetItem;
+ centralWidgetItem = 0;
+ return ret;
+ }
+#endif
+
+ return 0;
+}
+
+QList<int> QMainWindowLayoutState::indexOf(QWidget *widget) const
+{
+ QList<int> result;
+
+#ifndef QT_NO_TOOLBAR
+ // is it a toolbar?
+ if (QToolBar *toolBar = qobject_cast<QToolBar*>(widget)) {
+ result = toolBarAreaLayout.indexOf(toolBar);
+ if (!result.isEmpty())
+ result.prepend(0);
+ return result;
+ }
+#endif
+
+#ifndef QT_NO_DOCKWIDGET
+ // is it a dock widget?
+ if (QDockWidget *dockWidget = qobject_cast<QDockWidget *>(widget)) {
+ result = dockAreaLayout.indexOf(dockWidget);
+ if (!result.isEmpty())
+ result.prepend(1);
+ return result;
+ }
+#endif //QT_NO_DOCKWIDGET
+
+ return result;
+}
+
+bool QMainWindowLayoutState::contains(QWidget *widget) const
+{
+#ifndef QT_NO_DOCKWIDGET
+ if (dockAreaLayout.centralWidgetItem != 0 && dockAreaLayout.centralWidgetItem->widget() == widget)
+ return true;
+ if (!dockAreaLayout.indexOf(widget).isEmpty())
+ return true;
+#else
+ if (centralWidgetItem != 0 && centralWidgetItem->widget() == widget)
+ return true;
+#endif
+
+#ifndef QT_NO_TOOLBAR
+ if (!toolBarAreaLayout.indexOf(widget).isEmpty())
+ return true;
+#endif
+ return false;
+}
+
+void QMainWindowLayoutState::setCentralWidget(QWidget *widget)
+{
+ QLayoutItem *item = 0;
+ //make sure we remove the widget
+ deleteCentralWidgetItem();
+
+ if (widget != 0)
+ item = new QWidgetItemV2(widget);
+
+#ifndef QT_NO_DOCKWIDGET
+ dockAreaLayout.centralWidgetItem = item;
+#else
+ centralWidgetItem = item;
+#endif
+}
+
+QWidget *QMainWindowLayoutState::centralWidget() const
+{
+ QLayoutItem *item = 0;
+
+#ifndef QT_NO_DOCKWIDGET
+ item = dockAreaLayout.centralWidgetItem;
+#else
+ item = centralWidgetItem;
+#endif
+
+ if (item != 0)
+ return item->widget();
+ return 0;
+}
+
+QList<int> QMainWindowLayoutState::gapIndex(QWidget *widget,
+ const QPoint &pos) const
+{
+ QList<int> result;
+
+#ifndef QT_NO_TOOLBAR
+ // is it a toolbar?
+ if (qobject_cast<QToolBar*>(widget) != 0) {
+ result = toolBarAreaLayout.gapIndex(pos);
+ if (!result.isEmpty())
+ result.prepend(0);
+ return result;
+ }
+#endif
+
+#ifndef QT_NO_DOCKWIDGET
+ // is it a dock widget?
+ if (qobject_cast<QDockWidget *>(widget) != 0) {
+ result = dockAreaLayout.gapIndex(pos);
+ if (!result.isEmpty())
+ result.prepend(1);
+ return result;
+ }
+#endif //QT_NO_DOCKWIDGET
+
+ return result;
+}
+
+bool QMainWindowLayoutState::insertGap(QList<int> path, QLayoutItem *item)
+{
+ if (path.isEmpty())
+ return false;
+
+ int i = path.takeFirst();
+
+#ifndef QT_NO_TOOLBAR
+ if (i == 0) {
+ Q_ASSERT(qobject_cast<QToolBar*>(item->widget()) != 0);
+ return toolBarAreaLayout.insertGap(path, item);
+ }
+#endif
+
+#ifndef QT_NO_DOCKWIDGET
+ if (i == 1) {
+ Q_ASSERT(qobject_cast<QDockWidget*>(item->widget()) != 0);
+ return dockAreaLayout.insertGap(path, item);
+ }
+#endif //QT_NO_DOCKWIDGET
+
+ return false;
+}
+
+void QMainWindowLayoutState::remove(QList<int> path)
+{
+ int i = path.takeFirst();
+
+#ifndef QT_NO_TOOLBAR
+ if (i == 0)
+ toolBarAreaLayout.remove(path);
+#endif
+
+#ifndef QT_NO_DOCKWIDGET
+ if (i == 1)
+ dockAreaLayout.remove(path);
+#endif //QT_NO_DOCKWIDGET
+}
+
+void QMainWindowLayoutState::remove(QLayoutItem *item)
+{
+#ifndef QT_NO_TOOLBAR
+ toolBarAreaLayout.remove(item);
+#endif
+
+#ifndef QT_NO_DOCKWIDGET
+ // is it a dock widget?
+ if (QDockWidget *dockWidget = qobject_cast<QDockWidget *>(item->widget())) {
+ QList<int> path = dockAreaLayout.indexOf(dockWidget);
+ if (!path.isEmpty())
+ dockAreaLayout.remove(path);
+ }
+#endif //QT_NO_DOCKWIDGET
+}
+
+void QMainWindowLayoutState::clear()
+{
+#ifndef QT_NO_TOOLBAR
+ toolBarAreaLayout.clear();
+#endif
+
+#ifndef QT_NO_DOCKWIDGET
+ dockAreaLayout.clear();
+#else
+ centralWidgetRect = QRect(0, 0, -1, -1);
+#endif
+
+ rect = QRect(0, 0, -1, -1);
+}
+
+bool QMainWindowLayoutState::isValid() const
+{
+ return rect.isValid();
+}
+
+QLayoutItem *QMainWindowLayoutState::item(QList<int> path)
+{
+ int i = path.takeFirst();
+
+#ifndef QT_NO_TOOLBAR
+ if (i == 0)
+ return toolBarAreaLayout.item(path).widgetItem;
+#endif
+
+#ifndef QT_NO_DOCKWIDGET
+ if (i == 1)
+ return dockAreaLayout.item(path).widgetItem;
+#endif //QT_NO_DOCKWIDGET
+
+ return 0;
+}
+
+QRect QMainWindowLayoutState::itemRect(QList<int> path) const
+{
+ int i = path.takeFirst();
+
+#ifndef QT_NO_TOOLBAR
+ if (i == 0)
+ return toolBarAreaLayout.itemRect(path);
+#endif
+
+#ifndef QT_NO_DOCKWIDGET
+ if (i == 1)
+ return dockAreaLayout.itemRect(path);
+#endif //QT_NO_DOCKWIDGET
+
+ return QRect();
+}
+
+QRect QMainWindowLayoutState::gapRect(QList<int> path) const
+{
+ int i = path.takeFirst();
+
+#ifndef QT_NO_TOOLBAR
+ if (i == 0)
+ return toolBarAreaLayout.itemRect(path);
+#endif
+
+#ifndef QT_NO_DOCKWIDGET
+ if (i == 1)
+ return dockAreaLayout.gapRect(path);
+#endif //QT_NO_DOCKWIDGET
+
+ return QRect();
+}
+
+QLayoutItem *QMainWindowLayoutState::plug(QList<int> path)
+{
+ int i = path.takeFirst();
+
+#ifndef QT_NO_TOOLBAR
+ if (i == 0)
+ return toolBarAreaLayout.plug(path);
+#endif
+
+#ifndef QT_NO_DOCKWIDGET
+ if (i == 1)
+ return dockAreaLayout.plug(path);
+#endif //QT_NO_DOCKWIDGET
+
+ return 0;
+}
+
+QLayoutItem *QMainWindowLayoutState::unplug(QList<int> path, QMainWindowLayoutState *other)
+{
+ int i = path.takeFirst();
+
+#ifdef QT_NO_TOOLBAR
+ Q_UNUSED(other);
+#else
+ if (i == 0)
+ return toolBarAreaLayout.unplug(path, other ? &other->toolBarAreaLayout : 0);
+#endif
+
+#ifndef QT_NO_DOCKWIDGET
+ if (i == 1)
+ return dockAreaLayout.unplug(path);
+#endif //QT_NO_DOCKWIDGET
+
+ return 0;
+}
+
+void QMainWindowLayoutState::saveState(QDataStream &stream) const
+{
+#ifndef QT_NO_DOCKWIDGET
+ dockAreaLayout.saveState(stream);
+#endif
+#ifndef QT_NO_TOOLBAR
+ toolBarAreaLayout.saveState(stream);
+#endif
+}
+
+template <typename T>
+static QList<T> findChildrenHelper(const QObject *o)
+{
+ const QObjectList &list = o->children();
+ QList<T> result;
+
+ for (int i=0; i < list.size(); ++i) {
+ if (T t = qobject_cast<T>(list[i])) {
+ result.append(t);
+ }
+ }
+
+ return result;
+}
+
+//pre4.3 tests the format that was used before 4.3
+bool QMainWindowLayoutState::checkFormat(QDataStream &stream, bool pre43)
+{
+#ifdef QT_NO_TOOLBAR
+ Q_UNUSED(pre43);
+#endif
+ while (!stream.atEnd()) {
+ uchar marker;
+ stream >> marker;
+ switch(marker)
+ {
+#ifndef QT_NO_TOOLBAR
+ case QToolBarAreaLayout::ToolBarStateMarker:
+ case QToolBarAreaLayout::ToolBarStateMarkerEx:
+ {
+ QList<QToolBar *> toolBars = findChildrenHelper<QToolBar*>(mainWindow);
+ if (!toolBarAreaLayout.restoreState(stream, toolBars, marker,
+ pre43 /*testing 4.3 format*/, true /*testing*/)) {
+ return false;
+ }
+ }
+ break;
+#endif // QT_NO_TOOLBAR
+
+#ifndef QT_NO_DOCKWIDGET
+ case QDockAreaLayout::DockWidgetStateMarker:
+ {
+ QList<QDockWidget *> dockWidgets = findChildrenHelper<QDockWidget*>(mainWindow);
+ if (!dockAreaLayout.restoreState(stream, dockWidgets, true /*testing*/)) {
+ return false;
+ }
+ }
+ break;
+#endif
+ default:
+ //there was an error during the parsing
+ return false;
+ }// switch
+ } //while
+
+ //everything went fine: it must be a pre-4.3 saved state
+ return true;
+}
+
+bool QMainWindowLayoutState::restoreState(QDataStream &_stream,
+ const QMainWindowLayoutState &oldState)
+{
+ //make a copy of the data so that we can read it more than once
+ QByteArray copy;
+ while(!_stream.atEnd()) {
+ int length = 1024;
+ QByteArray ba(length, '\0');
+ length = _stream.readRawData(ba.data(), ba.size());
+ ba.resize(length);
+ copy += ba;
+ }
+
+ QDataStream ds(copy);
+ const bool oldFormat = !checkFormat(ds, false);
+ if (oldFormat) {
+ //we should try with the old format
+ QDataStream ds2(copy);
+ if (!checkFormat(ds2, true)) {
+ return false; //format unknown
+ }
+ }
+
+ QDataStream stream(copy);
+
+ while (!stream.atEnd()) {
+ uchar marker;
+ stream >> marker;
+ switch(marker)
+ {
+#ifndef QT_NO_DOCKWIDGET
+ case QDockAreaLayout::DockWidgetStateMarker:
+ {
+ QList<QDockWidget *> dockWidgets = findChildrenHelper<QDockWidget*>(mainWindow);
+ if (!dockAreaLayout.restoreState(stream, dockWidgets))
+ return false;
+
+ for (int i = 0; i < dockWidgets.size(); ++i) {
+ QDockWidget *w = dockWidgets.at(i);
+ QList<int> path = dockAreaLayout.indexOf(w);
+ if (path.isEmpty()) {
+ QList<int> oldPath = oldState.dockAreaLayout.indexOf(w);
+ if (oldPath.isEmpty()) {
+ continue;
+ }
+ QDockAreaLayoutInfo *info = dockAreaLayout.info(oldPath);
+ if (info == 0) {
+ continue;
+ }
+ info->item_list.append(QDockAreaLayoutItem(new QDockWidgetItem(w)));
+ }
+ }
+ }
+ break;
+#endif // QT_NO_DOCKWIDGET
+
+#ifndef QT_NO_TOOLBAR
+ case QToolBarAreaLayout::ToolBarStateMarker:
+ case QToolBarAreaLayout::ToolBarStateMarkerEx:
+ {
+ QList<QToolBar *> toolBars = findChildrenHelper<QToolBar*>(mainWindow);
+ if (!toolBarAreaLayout.restoreState(stream, toolBars, marker, oldFormat))
+ return false;
+
+ for (int i = 0; i < toolBars.size(); ++i) {
+ QToolBar *w = toolBars.at(i);
+ QList<int> path = toolBarAreaLayout.indexOf(w);
+ if (path.isEmpty()) {
+ QList<int> oldPath = oldState.toolBarAreaLayout.indexOf(w);
+ if (oldPath.isEmpty()) {
+ continue;
+ }
+ toolBarAreaLayout.docks[oldPath.at(0)].insertToolBar(0, w);
+ }
+ }
+ }
+ break;
+#endif //QT_NO_TOOLBAR
+ default:
+ return false;
+ }// switch
+ } //while
+
+
+ return true;
+}
+
+/******************************************************************************
+** QMainWindowLayoutState - toolbars
+*/
+
+#ifndef QT_NO_TOOLBAR
+
+static inline void validateToolBarArea(Qt::ToolBarArea &area)
+{
+ switch (area) {
+ case Qt::LeftToolBarArea:
+ case Qt::RightToolBarArea:
+ case Qt::TopToolBarArea:
+ case Qt::BottomToolBarArea:
+ break;
+ default:
+ area = Qt::TopToolBarArea;
+ }
+}
+
+static QInternal::DockPosition toDockPos(Qt::ToolBarArea area)
+{
+ switch (area) {
+ case Qt::LeftToolBarArea: return QInternal::LeftDock;
+ case Qt::RightToolBarArea: return QInternal::RightDock;
+ case Qt::TopToolBarArea: return QInternal::TopDock;
+ case Qt::BottomToolBarArea: return QInternal::BottomDock;
+ default:
+ break;
+ }
+
+ return QInternal::DockCount;
+}
+
+static Qt::ToolBarArea toToolBarArea(QInternal::DockPosition pos)
+{
+ switch (pos) {
+ case QInternal::LeftDock: return Qt::LeftToolBarArea;
+ case QInternal::RightDock: return Qt::RightToolBarArea;
+ case QInternal::TopDock: return Qt::TopToolBarArea;
+ case QInternal::BottomDock: return Qt::BottomToolBarArea;
+ default: break;
+ }
+ return Qt::NoToolBarArea;
+}
+
+static inline Qt::ToolBarArea toToolBarArea(int pos)
+{
+ return toToolBarArea(static_cast<QInternal::DockPosition>(pos));
+}
+
+void QMainWindowLayout::addToolBarBreak(Qt::ToolBarArea area)
+{
+ validateToolBarArea(area);
+
+ layoutState.toolBarAreaLayout.addToolBarBreak(toDockPos(area));
+ if (savedState.isValid())
+ savedState.toolBarAreaLayout.addToolBarBreak(toDockPos(area));
+
+ invalidate();
+}
+
+void QMainWindowLayout::insertToolBarBreak(QToolBar *before)
+{
+ layoutState.toolBarAreaLayout.insertToolBarBreak(before);
+ if (savedState.isValid())
+ savedState.toolBarAreaLayout.insertToolBarBreak(before);
+ invalidate();
+}
+
+void QMainWindowLayout::removeToolBarBreak(QToolBar *before)
+{
+ layoutState.toolBarAreaLayout.removeToolBarBreak(before);
+ if (savedState.isValid())
+ savedState.toolBarAreaLayout.removeToolBarBreak(before);
+ invalidate();
+}
+
+void QMainWindowLayout::moveToolBar(QToolBar *toolbar, int pos)
+{
+ layoutState.toolBarAreaLayout.moveToolBar(toolbar, pos);
+ if (savedState.isValid())
+ savedState.toolBarAreaLayout.moveToolBar(toolbar, pos);
+ invalidate();
+}
+
+/* Removes the toolbar from the mainwindow so that it can be added again. Does not
+ explicitly hide the toolbar. */
+void QMainWindowLayout::removeToolBar(QToolBar *toolbar)
+{
+ if (toolbar) {
+ QObject::disconnect(parentWidget(), SIGNAL(iconSizeChanged(QSize)),
+ toolbar, SLOT(_q_updateIconSize(QSize)));
+ QObject::disconnect(parentWidget(), SIGNAL(toolButtonStyleChanged(Qt::ToolButtonStyle)),
+ toolbar, SLOT(_q_updateToolButtonStyle(Qt::ToolButtonStyle)));
+
+#ifdef Q_WS_MAC
+ if (usesHIToolBar(toolbar)) {
+ removeFromMacToolbar(toolbar);
+ } else
+#endif // Q_WS_MAC
+ {
+ removeWidget(toolbar);
+ }
+ }
+}
+
+/*!
+ Adds \a toolbar to \a area, continuing the current line.
+*/
+void QMainWindowLayout::addToolBar(Qt::ToolBarArea area,
+ QToolBar *toolbar,
+ bool)
+{
+ validateToolBarArea(area);
+#ifdef Q_WS_MAC
+ if ((area == Qt::TopToolBarArea)
+ && layoutState.mainWindow->unifiedTitleAndToolBarOnMac()) {
+ insertIntoMacToolbar(0, toolbar);
+ } else
+#endif
+ {
+ //let's add the toolbar to the layout
+ addChildWidget(toolbar);
+ QLayoutItem * item = layoutState.toolBarAreaLayout.addToolBar(toDockPos(area), toolbar);
+ if (savedState.isValid() && item) {
+ // copy the toolbar also in the saved state
+ savedState.toolBarAreaLayout.insertItem(toDockPos(area), item);
+ }
+ invalidate();
+
+ //this ensures that the toolbar has the right window flags (not floating any more)
+ toolbar->d_func()->updateWindowFlags(false /*floating*/);
+ }
+}
+
+/*!
+ Adds \a toolbar before \a before
+*/
+void QMainWindowLayout::insertToolBar(QToolBar *before, QToolBar *toolbar)
+{
+#ifdef Q_WS_MAC
+ if (usesHIToolBar(before)) {
+ insertIntoMacToolbar(before, toolbar);
+ } else
+#endif // Q_WS_MAC
+ {
+ addChildWidget(toolbar);
+ QLayoutItem * item = layoutState.toolBarAreaLayout.insertToolBar(before, toolbar);
+ if (savedState.isValid() && item) {
+ // copy the toolbar also in the saved state
+ savedState.toolBarAreaLayout.insertItem(before, item);
+ }
+ if (!currentGapPos.isEmpty() && currentGapPos.first() == 0) {
+ currentGapPos = layoutState.toolBarAreaLayout.currentGapIndex();
+ if (!currentGapPos.isEmpty()) {
+ currentGapPos.prepend(0);
+ currentGapRect = layoutState.itemRect(currentGapPos);
+ }
+ }
+ invalidate();
+ }
+}
+
+Qt::ToolBarArea QMainWindowLayout::toolBarArea(QToolBar *toolbar) const
+{
+ QInternal::DockPosition pos = layoutState.toolBarAreaLayout.findToolBar(toolbar);
+ switch (pos) {
+ case QInternal::LeftDock: return Qt::LeftToolBarArea;
+ case QInternal::RightDock: return Qt::RightToolBarArea;
+ case QInternal::TopDock: return Qt::TopToolBarArea;
+ case QInternal::BottomDock: return Qt::BottomToolBarArea;
+ default: break;
+ }
+#ifdef Q_WS_MAC
+ if (pos == QInternal::DockCount) {
+ if (qtoolbarsInUnifiedToolbarList.contains(toolbar))
+ return Qt::TopToolBarArea;
+ }
+#endif
+ return Qt::NoToolBarArea;
+}
+
+bool QMainWindowLayout::toolBarBreak(QToolBar *toolBar) const
+{
+ return layoutState.toolBarAreaLayout.toolBarBreak(toolBar);
+}
+
+void QMainWindowLayout::getStyleOptionInfo(QStyleOptionToolBar *option, QToolBar *toolBar) const
+{
+ option->toolBarArea = toolBarArea(toolBar);
+ layoutState.toolBarAreaLayout.getStyleOptionInfo(option, toolBar);
+}
+
+void QMainWindowLayout::toggleToolBarsVisible()
+{
+ layoutState.toolBarAreaLayout.visible = !layoutState.toolBarAreaLayout.visible;
+ if (!layoutState.mainWindow->isMaximized()){
+ QPoint topLeft = parentWidget()->geometry().topLeft();
+ QRect r = parentWidget()->geometry();
+ r = layoutState.toolBarAreaLayout.rectHint(r);
+ r.moveTo(topLeft);
+ parentWidget()->setGeometry(r);
+// widgetAnimator->animate(parentWidget(), r, true);
+ } else{
+ update();
+ }
+}
+
+#endif // QT_NO_TOOLBAR
+
+/******************************************************************************
+** QMainWindowLayoutState - dock areas
+*/
+
+#ifndef QT_NO_DOCKWIDGET
+
+static inline void validateDockWidgetArea(Qt::DockWidgetArea &area)
+{
+ switch (area) {
+ case Qt::LeftDockWidgetArea:
+ case Qt::RightDockWidgetArea:
+ case Qt::TopDockWidgetArea:
+ case Qt::BottomDockWidgetArea:
+ break;
+ default:
+ area = Qt::LeftDockWidgetArea;
+ }
+}
+
+static QInternal::DockPosition toDockPos(Qt::DockWidgetArea area)
+{
+ switch (area) {
+ case Qt::LeftDockWidgetArea: return QInternal::LeftDock;
+ case Qt::RightDockWidgetArea: return QInternal::RightDock;
+ case Qt::TopDockWidgetArea: return QInternal::TopDock;
+ case Qt::BottomDockWidgetArea: return QInternal::BottomDock;
+ default:
+ break;
+ }
+
+ return QInternal::DockCount;
+}
+
+static Qt::DockWidgetArea toDockWidgetArea(QInternal::DockPosition pos)
+{
+ switch (pos) {
+ case QInternal::LeftDock : return Qt::LeftDockWidgetArea;
+ case QInternal::RightDock : return Qt::RightDockWidgetArea;
+ case QInternal::TopDock : return Qt::TopDockWidgetArea;
+ case QInternal::BottomDock : return Qt::BottomDockWidgetArea;
+ default:
+ break;
+ }
+
+ return Qt::NoDockWidgetArea;
+}
+
+inline static Qt::DockWidgetArea toDockWidgetArea(int pos)
+{
+ return toDockWidgetArea(static_cast<QInternal::DockPosition>(pos));
+}
+
+void QMainWindowLayout::setCorner(Qt::Corner corner, Qt::DockWidgetArea area)
+{
+ if (layoutState.dockAreaLayout.corners[corner] == area)
+ return;
+ layoutState.dockAreaLayout.corners[corner] = area;
+ if (savedState.isValid())
+ savedState.dockAreaLayout.corners[corner] = area;
+ invalidate();
+}
+
+Qt::DockWidgetArea QMainWindowLayout::corner(Qt::Corner corner) const
+{
+ return layoutState.dockAreaLayout.corners[corner];
+}
+
+void QMainWindowLayout::addDockWidget(Qt::DockWidgetArea area,
+ QDockWidget *dockwidget,
+ Qt::Orientation orientation)
+{
+ addChildWidget(dockwidget);
+
+ // If we are currently moving a separator, then we need to abort the move, since each
+ // time we move the mouse layoutState is replaced by savedState modified by the move.
+ if (!movingSeparator.isEmpty())
+ endSeparatorMove(movingSeparatorPos);
+
+ layoutState.dockAreaLayout.addDockWidget(toDockPos(area), dockwidget, orientation);
+ emit dockwidget->dockLocationChanged(area);
+ invalidate();
+}
+
+void QMainWindowLayout::tabifyDockWidget(QDockWidget *first, QDockWidget *second)
+{
+ addChildWidget(second);
+ layoutState.dockAreaLayout.tabifyDockWidget(first, second);
+ emit second->dockLocationChanged(dockWidgetArea(first));
+ invalidate();
+}
+
+bool QMainWindowLayout::restoreDockWidget(QDockWidget *dockwidget)
+{
+ addChildWidget(dockwidget);
+ if (!layoutState.dockAreaLayout.restoreDockWidget(dockwidget))
+ return false;
+ emit dockwidget->dockLocationChanged(dockWidgetArea(dockwidget));
+ invalidate();
+ return true;
+}
+
+#ifndef QT_NO_TABBAR
+bool QMainWindowLayout::documentMode() const
+{
+ return _documentMode;
+}
+
+void QMainWindowLayout::setDocumentMode(bool enabled)
+{
+ if (_documentMode == enabled)
+ return;
+
+ _documentMode = enabled;
+
+ // Update the document mode for all tab bars
+ foreach (QTabBar *bar, usedTabBars)
+ bar->setDocumentMode(_documentMode);
+ foreach (QTabBar *bar, unusedTabBars)
+ bar->setDocumentMode(_documentMode);
+}
+#endif // QT_NO_TABBAR
+
+void QMainWindowLayout::setVerticalTabsEnabled(bool enabled)
+{
+#ifdef QT_NO_TABBAR
+ Q_UNUSED(enabled);
+#else
+ if (verticalTabsEnabled == enabled)
+ return;
+
+ verticalTabsEnabled = enabled;
+
+ updateTabBarShapes();
+#endif // QT_NO_TABBAR
+}
+
+#ifndef QT_NO_TABWIDGET
+QTabWidget::TabShape QMainWindowLayout::tabShape() const
+{
+ return _tabShape;
+}
+
+void QMainWindowLayout::setTabShape(QTabWidget::TabShape tabShape)
+{
+ if (_tabShape == tabShape)
+ return;
+
+ _tabShape = tabShape;
+
+ updateTabBarShapes();
+}
+
+QTabWidget::TabPosition QMainWindowLayout::tabPosition(Qt::DockWidgetArea area) const
+{
+ return tabPositions[toDockPos(area)];
+}
+
+void QMainWindowLayout::setTabPosition(Qt::DockWidgetAreas areas, QTabWidget::TabPosition tabPosition)
+{
+ const Qt::DockWidgetArea dockWidgetAreas[] = {
+ Qt::TopDockWidgetArea,
+ Qt::LeftDockWidgetArea,
+ Qt::BottomDockWidgetArea,
+ Qt::RightDockWidgetArea
+ };
+ const QInternal::DockPosition dockPositions[] = {
+ QInternal::TopDock,
+ QInternal::LeftDock,
+ QInternal::BottomDock,
+ QInternal::RightDock
+ };
+
+ for (int i = 0; i < QInternal::DockCount; ++i)
+ if (areas & dockWidgetAreas[i])
+ tabPositions[dockPositions[i]] = tabPosition;
+
+ updateTabBarShapes();
+}
+
+static inline QTabBar::Shape tabBarShapeFrom(QTabWidget::TabShape shape, QTabWidget::TabPosition position)
+{
+ const bool rounded = (shape == QTabWidget::Rounded);
+ if (position == QTabWidget::North)
+ return rounded ? QTabBar::RoundedNorth : QTabBar::TriangularNorth;
+ if (position == QTabWidget::South)
+ return rounded ? QTabBar::RoundedSouth : QTabBar::TriangularSouth;
+ if (position == QTabWidget::East)
+ return rounded ? QTabBar::RoundedEast : QTabBar::TriangularEast;
+ if (position == QTabWidget::West)
+ return rounded ? QTabBar::RoundedWest : QTabBar::TriangularWest;
+ return QTabBar::RoundedNorth;
+}
+#endif // QT_NO_TABWIDGET
+
+#ifndef QT_NO_TABBAR
+void QMainWindowLayout::updateTabBarShapes()
+{
+#ifndef QT_NO_TABWIDGET
+ const QTabWidget::TabPosition vertical[] = {
+ QTabWidget::West,
+ QTabWidget::East,
+ QTabWidget::North,
+ QTabWidget::South
+ };
+#else
+ const QTabBar::Shape vertical[] = {
+ QTabBar::RoundedWest,
+ QTabBar::RoundedEast,
+ QTabBar::RoundedNorth,
+ QTabBar::RoundedSouth
+ };
+#endif
+
+ QDockAreaLayout &layout = layoutState.dockAreaLayout;
+
+ for (int i = 0; i < QInternal::DockCount; ++i) {
+#ifndef QT_NO_TABWIDGET
+ QTabWidget::TabPosition pos = verticalTabsEnabled ? vertical[i] : tabPositions[i];
+ QTabBar::Shape shape = tabBarShapeFrom(_tabShape, pos);
+#else
+ QTabBar::Shape shape = verticalTabsEnabled ? vertical[i] : QTabBar::RoundedSouth;
+#endif
+ layout.docks[i].setTabBarShape(shape);
+ }
+}
+#endif // QT_NO_TABBAR
+
+void QMainWindowLayout::splitDockWidget(QDockWidget *after,
+ QDockWidget *dockwidget,
+ Qt::Orientation orientation)
+{
+ addChildWidget(dockwidget);
+ layoutState.dockAreaLayout.splitDockWidget(after, dockwidget, orientation);
+ emit dockwidget->dockLocationChanged(dockWidgetArea(after));
+ invalidate();
+}
+
+Qt::DockWidgetArea QMainWindowLayout::dockWidgetArea(QDockWidget *widget) const
+{
+ QList<int> pathToWidget = layoutState.dockAreaLayout.indexOf(widget);
+ if (pathToWidget.isEmpty())
+ return Qt::NoDockWidgetArea;
+ return toDockWidgetArea(pathToWidget.first());
+}
+
+void QMainWindowLayout::keepSize(QDockWidget *w)
+{
+ layoutState.dockAreaLayout.keepSize(w);
+}
+
+#ifndef QT_NO_TABBAR
+
+class QMainWindowTabBar : public QTabBar
+{
+public:
+ QMainWindowTabBar(QWidget *parent);
+protected:
+ bool event(QEvent *e);
+};
+
+QMainWindowTabBar::QMainWindowTabBar(QWidget *parent)
+ : QTabBar(parent)
+{
+ setExpanding(false);
+}
+
+bool QMainWindowTabBar::event(QEvent *e)
+{
+ // show the tooltip if tab is too small to fit label
+
+ if (e->type() != QEvent::ToolTip)
+ return QTabBar::event(e);
+ QSize size = this->size();
+ QSize hint = sizeHint();
+ if (shape() == QTabBar::RoundedWest || shape() == QTabBar::RoundedEast) {
+ size.transpose();
+ hint.transpose();
+ }
+ if (size.width() < hint.width())
+ return QTabBar::event(e);
+ e->accept();
+ return true;
+}
+
+QTabBar *QMainWindowLayout::getTabBar()
+{
+ QTabBar *result = 0;
+ if (!unusedTabBars.isEmpty()) {
+ result = unusedTabBars.takeLast();
+ } else {
+ result = new QMainWindowTabBar(parentWidget());
+ result->setDrawBase(true);
+ result->setElideMode(Qt::ElideRight);
+ result->setDocumentMode(_documentMode);
+ connect(result, SIGNAL(currentChanged(int)), this, SLOT(tabChanged()));
+ }
+
+ usedTabBars.insert(result);
+ return result;
+}
+
+// Allocates a new separator widget if needed
+QWidget *QMainWindowLayout::getSeparatorWidget()
+{
+ QWidget *result = 0;
+ if (!unusedSeparatorWidgets.isEmpty()) {
+ result = unusedSeparatorWidgets.takeLast();
+ } else {
+ result = new QWidget(parentWidget());
+ result->setAttribute(Qt::WA_MouseNoMask, true);
+ result->setAutoFillBackground(false);
+ result->setObjectName(QLatin1String("qt_qmainwindow_extended_splitter"));
+ }
+ usedSeparatorWidgets.insert(result);
+ return result;
+}
+
+void QMainWindowLayout::tabChanged()
+{
+ QTabBar *tb = qobject_cast<QTabBar*>(sender());
+ if (tb == 0)
+ return;
+ QDockAreaLayoutInfo *info = layoutState.dockAreaLayout.info(tb);
+ if (info == 0)
+ return;
+ info->apply(false);
+
+ if (QWidget *w = centralWidget())
+ w->raise();
+}
+#endif // QT_NO_TABBAR
+
+bool QMainWindowLayout::startSeparatorMove(const QPoint &pos)
+{
+ movingSeparator = layoutState.dockAreaLayout.findSeparator(pos);
+
+ if (movingSeparator.isEmpty())
+ return false;
+
+ savedState = layoutState;
+ movingSeparatorPos = movingSeparatorOrigin = pos;
+
+ return true;
+}
+
+bool QMainWindowLayout::separatorMove(const QPoint &pos)
+{
+ if (movingSeparator.isEmpty())
+ return false;
+ movingSeparatorPos = pos;
+ separatorMoveTimer->start();
+ return true;
+}
+
+void QMainWindowLayout::doSeparatorMove()
+{
+ if (movingSeparator.isEmpty())
+ return;
+ if (movingSeparatorOrigin == movingSeparatorPos)
+ return;
+
+ layoutState = savedState;
+ layoutState.dockAreaLayout.separatorMove(movingSeparator, movingSeparatorOrigin,
+ movingSeparatorPos,
+ &separatorMoveCache);
+ movingSeparatorPos = movingSeparatorOrigin;
+}
+
+bool QMainWindowLayout::endSeparatorMove(const QPoint&)
+{
+ bool result = !movingSeparator.isEmpty();
+ movingSeparator.clear();
+ savedState.clear();
+ separatorMoveCache.clear();
+ return result;
+}
+
+void QMainWindowLayout::raise(QDockWidget *widget)
+{
+ QDockAreaLayoutInfo *info = layoutState.dockAreaLayout.info(widget);
+ if (info == 0)
+ return;
+#ifndef QT_NO_TABBAR
+ if (!info->tabbed)
+ return;
+ info->setCurrentTab(widget);
+#endif
+}
+
+#endif // QT_NO_DOCKWIDGET
+
+
+/******************************************************************************
+** QMainWindowLayoutState - layout interface
+*/
+
+int QMainWindowLayout::count() const
+{
+ qWarning("QMainWindowLayout::count: ?");
+ return 0; //#################################################
+}
+
+QLayoutItem *QMainWindowLayout::itemAt(int index) const
+{
+ int x = 0;
+
+ if (QLayoutItem *ret = layoutState.itemAt(index, &x))
+ return ret;
+
+ if (statusbar && x++ == index)
+ return statusbar;
+
+ return 0;
+}
+
+QLayoutItem *QMainWindowLayout::takeAt(int index)
+{
+ int x = 0;
+
+ if (QLayoutItem *ret = layoutState.takeAt(index, &x)) {
+ // the widget might in fact have been destroyed by now
+ if (QWidget *w = ret->widget()) {
+ widgetAnimator->abort(w);
+ if (w == pluggingWidget)
+ pluggingWidget = 0;
+ }
+
+ if (savedState.isValid() ) {
+ //we need to remove the item also from the saved state to prevent crash
+ savedState.remove(ret);
+ //Also, the item may be contained several times as a gap item.
+ layoutState.remove(ret);
+ }
+
+#ifndef QT_NO_TOOLBAR
+ if (!currentGapPos.isEmpty() && currentGapPos.first() == 0) {
+ currentGapPos = layoutState.toolBarAreaLayout.currentGapIndex();
+ if (!currentGapPos.isEmpty()) {
+ currentGapPos.prepend(0);
+ currentGapRect = layoutState.itemRect(currentGapPos);
+ }
+ }
+#endif
+
+ return ret;
+ }
+
+ if (statusbar && x++ == index) {
+ QLayoutItem *ret = statusbar;
+ statusbar = 0;
+ return ret;
+ }
+
+ return 0;
+}
+
+void QMainWindowLayout::setGeometry(const QRect &_r)
+{
+ if (savedState.isValid())
+ return;
+
+ QRect r = _r;
+
+ QLayout::setGeometry(r);
+
+ if (statusbar) {
+ QRect sbr(QPoint(0, 0),
+ QSize(r.width(), statusbar->heightForWidth(r.width()))
+ .expandedTo(statusbar->minimumSize()));
+ sbr.moveBottom(r.bottom());
+ QRect vr = QStyle::visualRect(QApplication::layoutDirection(), _r, sbr);
+ statusbar->setGeometry(vr);
+ r.setBottom(sbr.top() - 1);
+ }
+
+ layoutState.rect = r;
+ layoutState.fitLayout();
+ applyState(layoutState, false);
+}
+
+void QMainWindowLayout::addItem(QLayoutItem *)
+{ qWarning("QMainWindowLayout::addItem: Please use the public QMainWindow API instead"); }
+
+QSize QMainWindowLayout::sizeHint() const
+{
+ if (!szHint.isValid()) {
+ szHint = layoutState.sizeHint();
+ const QSize sbHint = statusbar ? statusbar->sizeHint() : QSize(0, 0);
+ szHint = QSize(qMax(sbHint.width(), szHint.width()),
+ sbHint.height() + szHint.height());
+ }
+ return szHint;
+}
+
+QSize QMainWindowLayout::minimumSize() const
+{
+ if (!minSize.isValid()) {
+ minSize = layoutState.minimumSize();
+ const QSize sbMin = statusbar ? statusbar->minimumSize() : QSize(0, 0);
+ minSize = QSize(qMax(sbMin.width(), minSize.width()),
+ sbMin.height() + minSize.height());
+#ifdef Q_WS_MAC
+ const QSize storedSize = minSize;
+ int minWidth = 0;
+ foreach (QToolBar *toolbar, qtoolbarsInUnifiedToolbarList) {
+ minWidth += toolbar->sizeHint().width() + 20;
+ }
+ minSize = QSize(qMax(minWidth, storedSize.width()), storedSize.height());
+#endif
+ }
+ return minSize;
+}
+
+void QMainWindowLayout::invalidate()
+{
+ QLayout::invalidate();
+ minSize = szHint = QSize();
+}
+
+/******************************************************************************
+** QMainWindowLayout - remaining stuff
+*/
+
+static void fixToolBarOrientation(QLayoutItem *item, int dockPos)
+{
+#ifndef QT_NO_TOOLBAR
+ QToolBar *toolBar = qobject_cast<QToolBar*>(item->widget());
+ if (toolBar == 0)
+ return;
+
+ QRect oldGeo = toolBar->geometry();
+
+ QInternal::DockPosition pos
+ = static_cast<QInternal::DockPosition>(dockPos);
+ Qt::Orientation o = pos == QInternal::TopDock || pos == QInternal::BottomDock
+ ? Qt::Horizontal : Qt::Vertical;
+ if (o != toolBar->orientation())
+ toolBar->setOrientation(o);
+
+ QSize hint = toolBar->sizeHint().boundedTo(toolBar->maximumSize())
+ .expandedTo(toolBar->minimumSize());
+
+ if (toolBar->size() != hint) {
+ QRect newGeo(oldGeo.topLeft(), hint);
+ if (toolBar->layoutDirection() == Qt::RightToLeft)
+ newGeo.moveRight(oldGeo.right());
+ toolBar->setGeometry(newGeo);
+ }
+
+#else
+ Q_UNUSED(item);
+ Q_UNUSED(dockPos);
+#endif
+}
+
+void QMainWindowLayout::revert(QLayoutItem *widgetItem)
+{
+ if (!savedState.isValid())
+ return;
+
+ QWidget *widget = widgetItem->widget();
+ layoutState = savedState;
+ currentGapPos = layoutState.indexOf(widget);
+ fixToolBarOrientation(widgetItem, currentGapPos.at(1));
+ layoutState.unplug(currentGapPos);
+ layoutState.fitLayout();
+ currentGapRect = layoutState.itemRect(currentGapPos);
+
+ plug(widgetItem);
+}
+
+bool QMainWindowLayout::plug(QLayoutItem *widgetItem)
+{
+ if (!parentWidget()->isVisible() || parentWidget()->isMinimized() || currentGapPos.isEmpty())
+ return false;
+
+ fixToolBarOrientation(widgetItem, currentGapPos.at(1));
+
+ QWidget *widget = widgetItem->widget();
+
+ QList<int> previousPath = layoutState.indexOf(widget);
+
+ QLayoutItem *it = layoutState.plug(currentGapPos);
+ Q_ASSERT(it == widgetItem);
+ Q_UNUSED(it);
+ if (!previousPath.isEmpty())
+ layoutState.remove(previousPath);
+
+ if (dockOptions & QMainWindow::AnimatedDocks) {
+ pluggingWidget = widget;
+ QRect globalRect = currentGapRect;
+ globalRect.moveTopLeft(parentWidget()->mapToGlobal(globalRect.topLeft()));
+#ifndef QT_NO_DOCKWIDGET
+ if (qobject_cast<QDockWidget*>(widget) != 0) {
+ QDockWidgetLayout *layout = qobject_cast<QDockWidgetLayout*>(widget->layout());
+ if (layout->nativeWindowDeco()) {
+ globalRect.adjust(0, layout->titleHeight(), 0, 0);
+ } else {
+ int fw = widget->style()->pixelMetric(QStyle::PM_DockWidgetFrameWidth, 0, widget);
+ globalRect.adjust(-fw, -fw, fw, fw);
+ }
+ }
+#endif
+ widgetAnimator->animate(widget, globalRect,
+ dockOptions & QMainWindow::AnimatedDocks);
+ } else {
+#ifndef QT_NO_DOCKWIDGET
+ if (QDockWidget *dw = qobject_cast<QDockWidget*>(widget))
+ dw->d_func()->plug(currentGapRect);
+#endif
+#ifndef QT_NO_TOOLBAR
+ if (QToolBar *tb = qobject_cast<QToolBar*>(widget))
+ tb->d_func()->plug(currentGapRect);
+#endif
+ applyState(layoutState);
+ savedState.clear();
+#ifndef QT_NO_DOCKWIDGET
+ parentWidget()->update(layoutState.dockAreaLayout.separatorRegion());
+#endif
+ currentGapPos.clear();
+ updateGapIndicator();
+ }
+
+ return true;
+}
+
+void QMainWindowLayout::allAnimationsFinished()
+{
+#ifndef QT_NO_DOCKWIDGET
+ parentWidget()->update(layoutState.dockAreaLayout.separatorRegion());
+
+#ifndef QT_NO_TABBAR
+ foreach (QTabBar *tab_bar, usedTabBars)
+ tab_bar->show();
+#endif // QT_NO_TABBAR
+#endif // QT_NO_DOCKWIDGET
+
+ updateGapIndicator();
+}
+
+void QMainWindowLayout::animationFinished(QWidget *widget)
+{
+
+ /* This signal is delivered from QWidgetAnimator over a qeued connection. The problem is that
+ the widget can be deleted. This is handled as follows:
+
+ The animator only ever animates widgets that have been added to this layout. If a widget
+ is deleted during animation, the widget's destructor removes the widget form this layout.
+ This in turn aborts the animation (see takeAt()) and this signal will never be delivered.
+
+ If the widget is deleted after the animation is finished but before this qeued signal
+ is delivered, the widget is no longer in the layout and we catch it here. The key is that
+ QMainWindowLayoutState::contains() never dereferences the pointer. */
+
+ if (!layoutState.contains(widget))
+ return;
+
+#ifndef QT_NO_TOOLBAR
+ if (QToolBar *tb = qobject_cast<QToolBar*>(widget)) {
+ QToolBarLayout *tbl = qobject_cast<QToolBarLayout*>(tb->layout());
+ if (tbl->animating) {
+ tbl->animating = false;
+ if (tbl->expanded)
+ tbl->layoutActions(tb->size());
+ tb->update();
+ }
+ }
+#endif
+
+ if (widget != pluggingWidget)
+ return;
+
+#ifndef QT_NO_DOCKWIDGET
+ if (QDockWidget *dw = qobject_cast<QDockWidget*>(widget))
+ dw->d_func()->plug(currentGapRect);
+#endif
+#ifndef QT_NO_TOOLBAR
+ if (QToolBar *tb = qobject_cast<QToolBar*>(widget))
+ tb->d_func()->plug(currentGapRect);
+#endif
+
+ applyState(layoutState, false);
+#ifndef QT_NO_DOCKWIDGET
+#ifndef QT_NO_TABBAR
+ if (qobject_cast<QDockWidget*>(widget) != 0) {
+ // info() might return null if the widget is destroyed while
+ // animating but before the animationFinished signal is received.
+ if (QDockAreaLayoutInfo *info = layoutState.dockAreaLayout.info(widget))
+ info->setCurrentTab(widget);
+ }
+#endif
+#endif
+ savedState.clear();
+ currentGapPos.clear();
+ pluggingWidget = 0;
+ updateGapIndicator();
+}
+
+void QMainWindowLayout::restore(bool keepSavedState)
+{
+ if (!savedState.isValid())
+ return;
+
+ layoutState = savedState;
+ applyState(layoutState);
+ if (!keepSavedState)
+ savedState.clear();
+ currentGapPos.clear();
+ pluggingWidget = 0;
+ updateGapIndicator();
+}
+
+QMainWindowLayout::QMainWindowLayout(QMainWindow *mainwindow)
+ : QLayout(mainwindow)
+ , layoutState(mainwindow)
+ , savedState(mainwindow)
+ , dockOptions(QMainWindow::AnimatedDocks | QMainWindow::AllowTabbedDocks)
+ , statusbar(0)
+#ifndef QT_NO_DOCKWIDGET
+#ifndef QT_NO_TABBAR
+ , _documentMode(false)
+ , verticalTabsEnabled(false)
+#ifndef QT_NO_TABWIDGET
+ , _tabShape(QTabWidget::Rounded)
+#endif
+#endif
+#endif // QT_NO_DOCKWIDGET
+{
+#ifndef QT_NO_DOCKWIDGET
+#ifndef QT_NO_TABBAR
+ sep = mainwindow->style()->pixelMetric(QStyle::PM_DockWidgetSeparatorExtent, 0, mainwindow);
+#endif
+ separatorMoveTimer = new QTimer(this);
+ separatorMoveTimer->setSingleShot(true);
+ separatorMoveTimer->setInterval(0);
+ connect(separatorMoveTimer, SIGNAL(timeout()), this, SLOT(doSeparatorMove()));
+
+#ifndef QT_NO_TABWIDGET
+ for (int i = 0; i < QInternal::DockCount; ++i)
+ tabPositions[i] = QTabWidget::South;
+#endif
+#endif // QT_NO_DOCKWIDGET
+
+#ifndef QT_NO_RUBBERBAND
+ gapIndicator = new QRubberBand(QRubberBand::Rectangle, mainwindow);
+ // For accessibility to identify this special widget.
+ gapIndicator->setObjectName(QLatin1String("qt_rubberband"));
+
+ gapIndicator->hide();
+#endif
+ pluggingWidget = 0;
+
+ setObjectName(mainwindow->objectName() + QLatin1String("_layout"));
+ widgetAnimator = new QWidgetAnimator(this);
+ connect(widgetAnimator, SIGNAL(finished(QWidget*)),
+ this, SLOT(animationFinished(QWidget*)), Qt::QueuedConnection);
+ connect(widgetAnimator, SIGNAL(finishedAll()),
+ this, SLOT(allAnimationsFinished()));
+}
+
+QMainWindowLayout::~QMainWindowLayout()
+{
+ layoutState.deleteAllLayoutItems();
+ layoutState.deleteCentralWidgetItem();
+
+#ifdef Q_WS_MAC
+ cleanUpMacToolbarItems();
+#endif
+
+ delete statusbar;
+}
+
+void QMainWindowLayout::setDockOptions(QMainWindow::DockOptions opts)
+{
+ if (opts == dockOptions)
+ return;
+
+ dockOptions = opts;
+
+#ifndef QT_NO_DOCKWIDGET
+ setVerticalTabsEnabled(opts & QMainWindow::VerticalTabs);
+#endif
+
+ invalidate();
+}
+
+#ifndef QT_NO_STATUSBAR
+QStatusBar *QMainWindowLayout::statusBar() const
+{ return statusbar ? qobject_cast<QStatusBar *>(statusbar->widget()) : 0; }
+
+void QMainWindowLayout::setStatusBar(QStatusBar *sb)
+{
+ if (sb)
+ addChildWidget(sb);
+ delete statusbar;
+ statusbar = sb ? new QWidgetItemV2(sb) : 0;
+ invalidate();
+}
+#endif // QT_NO_STATUSBAR
+
+QWidget *QMainWindowLayout::centralWidget() const
+{
+ return layoutState.centralWidget();
+}
+
+void QMainWindowLayout::setCentralWidget(QWidget *widget)
+{
+ if (widget != 0)
+ addChildWidget(widget);
+ layoutState.setCentralWidget(widget);
+ if (savedState.isValid()) {
+#ifndef QT_NO_DOCKWIDGET
+ savedState.dockAreaLayout.centralWidgetItem = layoutState.dockAreaLayout.centralWidgetItem;
+#else
+ savedState.centralWidgetItem = layoutState.centralWidgetItem;
+#endif
+ }
+ invalidate();
+}
+
+QLayoutItem *QMainWindowLayout::unplug(QWidget *widget)
+{
+ QList<int> path = layoutState.indexOf(widget);
+ if (path.isEmpty())
+ return 0;
+
+ QLayoutItem *item = layoutState.item(path);
+ if (widget->isWindow())
+ return item;
+
+ QRect r = layoutState.itemRect(path);
+ savedState = layoutState;
+
+#ifndef QT_NO_DOCKWIDGET
+ if (QDockWidget *dw = qobject_cast<QDockWidget*>(widget)) {
+ dw->d_func()->unplug(r);
+ }
+#endif
+#ifndef QT_NO_TOOLBAR
+ if (QToolBar *tb = qobject_cast<QToolBar*>(widget)) {
+ tb->d_func()->unplug(r);
+ }
+#endif
+
+
+ layoutState.unplug(path ,&savedState);
+ savedState.fitLayout();
+ currentGapPos = path;
+ currentGapRect = r;
+ updateGapIndicator();
+
+ fixToolBarOrientation(item, currentGapPos.at(1));
+
+ return item;
+}
+
+void QMainWindowLayout::updateGapIndicator()
+{
+#ifndef QT_NO_RUBBERBAND
+ if (widgetAnimator->animating() || currentGapPos.isEmpty()) {
+ gapIndicator->hide();
+ } else {
+ if (gapIndicator->geometry() != currentGapRect)
+ gapIndicator->setGeometry(currentGapRect);
+ if (!gapIndicator->isVisible())
+ gapIndicator->show();
+ }
+#endif
+}
+
+QList<int> QMainWindowLayout::hover(QLayoutItem *widgetItem, const QPoint &mousePos)
+{
+ if (!parentWidget()->isVisible() || parentWidget()->isMinimized()
+ || pluggingWidget != 0 || widgetItem == 0)
+ return QList<int>();
+
+ QWidget *widget = widgetItem->widget();
+ QPoint pos = parentWidget()->mapFromGlobal(mousePos);
+
+ if (!savedState.isValid())
+ savedState = layoutState;
+
+ QList<int> path = savedState.gapIndex(widget, pos);
+
+ if (!path.isEmpty()) {
+ bool allowed = false;
+
+#ifndef QT_NO_DOCKWIDGET
+ if (QDockWidget *dw = qobject_cast<QDockWidget*>(widget))
+ allowed = dw->isAreaAllowed(toDockWidgetArea(path.at(1)));
+#endif
+#ifndef QT_NO_TOOLBAR
+ if (QToolBar *tb = qobject_cast<QToolBar*>(widget))
+ allowed = tb->isAreaAllowed(toToolBarArea(path.at(1)));
+#endif
+
+ if (!allowed)
+ path.clear();
+ }
+
+ if (path == currentGapPos)
+ return currentGapPos; // the gap is already there
+
+ currentGapPos = path;
+ if (path.isEmpty()) {
+ fixToolBarOrientation(widgetItem, 2); // 2 = top dock, ie. horizontal
+ restore(true);
+ return QList<int>();
+ }
+
+ fixToolBarOrientation(widgetItem, currentGapPos.at(1));
+
+ QMainWindowLayoutState newState = savedState;
+
+ if (!newState.insertGap(path, widgetItem)) {
+ restore(true); // not enough space
+ return QList<int>();
+ }
+
+ QSize min = newState.minimumSize();
+ QSize size = newState.rect.size();
+
+ if (min.width() > size.width() || min.height() > size.height()) {
+ restore(true);
+ return QList<int>();
+ }
+
+ newState.fitLayout();
+
+ currentGapRect = newState.gapRect(currentGapPos);
+
+#ifndef QT_NO_DOCKWIDGET
+ parentWidget()->update(layoutState.dockAreaLayout.separatorRegion());
+#endif
+ layoutState = newState;
+ applyState(layoutState);
+
+ updateGapIndicator();
+
+ return path;
+}
+
+void QMainWindowLayout::applyState(QMainWindowLayoutState &newState, bool animate)
+{
+#ifndef QT_NO_DOCKWIDGET
+#ifndef QT_NO_TABBAR
+ QSet<QTabBar*> used = newState.dockAreaLayout.usedTabBars();
+ QSet<QTabBar*> retired = usedTabBars - used;
+ usedTabBars = used;
+ foreach (QTabBar *tab_bar, retired) {
+ tab_bar->hide();
+ while (tab_bar->count() > 0)
+ tab_bar->removeTab(0);
+ unusedTabBars.append(tab_bar);
+ }
+
+ if (sep == 1) {
+ QSet<QWidget*> usedSeps = newState.dockAreaLayout.usedSeparatorWidgets();
+ QSet<QWidget*> retiredSeps = usedSeparatorWidgets - usedSeps;
+ usedSeparatorWidgets = usedSeps;
+ foreach (QWidget *sepWidget, retiredSeps) {
+ unusedSeparatorWidgets.append(sepWidget);
+ }
+ }
+
+
+#endif // QT_NO_TABBAR
+#endif // QT_NO_DOCKWIDGET
+ newState.apply(dockOptions & QMainWindow::AnimatedDocks && animate);
+}
+
+void QMainWindowLayout::saveState(QDataStream &stream) const
+{
+ layoutState.saveState(stream);
+}
+
+bool QMainWindowLayout::restoreState(QDataStream &stream)
+{
+ savedState = layoutState;
+ layoutState.clear();
+ layoutState.rect = savedState.rect;
+
+ if (!layoutState.restoreState(stream, savedState)) {
+ layoutState.deleteAllLayoutItems();
+ layoutState = savedState;
+ if (parentWidget()->isVisible())
+ applyState(layoutState, false); // hides tabBars allocated by newState
+ return false;
+ }
+
+ if (parentWidget()->isVisible()) {
+ layoutState.fitLayout();
+ applyState(layoutState, false);
+ }
+
+ savedState.deleteAllLayoutItems();
+ savedState.clear();
+
+#ifndef QT_NO_DOCKWIDGET
+ if (parentWidget()->isVisible()) {
+#ifndef QT_NO_TABBAR
+ foreach (QTabBar *tab_bar, usedTabBars)
+ tab_bar->show();
+
+#endif
+ }
+#endif // QT_NO_DOCKWIDGET
+
+ return true;
+}
+
+
+// Returns if this toolbar *should* be using HIToolbar. Won't work for all in between cases
+// for example, you have a toolbar in the top area and then you suddenly turn on
+// HIToolbar.
+bool QMainWindowLayout::usesHIToolBar(QToolBar *toolbar) const
+{
+#ifndef Q_WS_MAC
+ Q_UNUSED(toolbar);
+ return false;
+#else
+ return qtoolbarsInUnifiedToolbarList.contains(toolbar)
+ || ((toolBarArea(toolbar) == Qt::TopToolBarArea)
+ && layoutState.mainWindow->unifiedTitleAndToolBarOnMac());
+#endif
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_MAINWINDOW
diff --git a/src/gui/widgets/qmainwindowlayout_mac.mm b/src/gui/widgets/qmainwindowlayout_mac.mm
new file mode 100644
index 0000000000..950f758103
--- /dev/null
+++ b/src/gui/widgets/qmainwindowlayout_mac.mm
@@ -0,0 +1,469 @@
+#include <private/qmainwindowlayout_p.h>
+#include <qtoolbar.h>
+#include <private/qtoolbarlayout_p.h>
+#include <private/qt_cocoa_helpers_mac_p.h>
+
+#ifndef QT_MAC_USE_COCOA
+#include <Carbon/Carbon.h>
+#else
+#include <private/qcocoatoolbardelegate_mac_p.h>
+#endif
+
+QT_BEGIN_NAMESPACE
+#ifdef QT_NAMESPACE
+
+// namespace up the stuff
+#define SS(x) #x
+#define S0(x) SS(x)
+#define S "com.trolltech.qt-" S0(QT_NAMESPACE) ".qmainwindow.qtoolbarInHIToolbar"
+#define SToolbar "com.trolltech.qt-" S0(QT_NAMESPACE) ".hitoolbar-qtoolbar"
+#define SNSToolbar "com.trolltech.qt-" S0(QT_NAMESPACE) ".qtoolbarInNSToolbar"
+#define MacToolbar "com.trolltech.qt-" S0(QT_NAMESPACE) ".qmainwindow.mactoolbar"
+
+#ifndef QT_MAC_USE_COCOA
+static CFStringRef kQToolBarHIToolbarItemClassID = CFSTR(S);
+static CFStringRef kQToolBarHIToolbarIdentifier = CFSTR(SToolbar);
+#else
+static NSString *kQToolBarNSToolbarIdentifier = @SNSToolbar;
+#endif
+static CFStringRef kQMainWindowMacToolbarID = CFSTR(MacToolbar);
+#undef SS
+#undef S0
+#undef S
+#undef SToolbar
+#undef SNSToolbar
+#undef MacToolbar
+
+#else
+#ifndef QT_MAC_USE_COCOA
+static CFStringRef kQToolBarHIToolbarItemClassID = CFSTR("com.trolltech.qt.qmainwindow.qtoolbarInHIToolbar");
+static CFStringRef kQToolBarHIToolbarIdentifier = CFSTR("com.trolltech.qt.hitoolbar-qtoolbar");
+#else
+static NSString *kQToolBarNSToolbarIdentifier = @"com.trolltech.qt.qmainwindow.qtoolbarInNSToolbar";
+#endif
+static CFStringRef kQMainWindowMacToolbarID = CFSTR("com.trolltech.qt.qmainwindow.mactoolbar");
+#endif // QT_NAMESPACE
+
+#ifndef QT_MAC_USE_COCOA
+
+static const int kEventParamQToolBar = 'QTBR';
+static const int kEventParamQMainWindowLayout = 'QMWL';
+
+const EventTypeSpec qtoolbarEvents[] =
+{
+ { kEventClassHIObject, kEventHIObjectConstruct },
+ { kEventClassHIObject, kEventHIObjectDestruct },
+ { kEventClassHIObject, kEventHIObjectInitialize },
+ { kEventClassToolbarItem, kEventToolbarItemCreateCustomView }
+};
+
+struct QToolBarInHIToolbarInfo
+{
+ QToolBarInHIToolbarInfo(HIToolbarItemRef item)
+ : toolbarItem(item), mainWindowLayout(0)
+ {}
+ HIToolbarItemRef toolbarItem;
+ QMainWindowLayout *mainWindowLayout;
+};
+
+OSStatus QMainWindowLayout::qtoolbarInHIToolbarHandler(EventHandlerCallRef inCallRef,
+ EventRef event, void *data)
+{
+ OSStatus result = eventNotHandledErr;
+ QToolBarInHIToolbarInfo *object = static_cast<QToolBarInHIToolbarInfo *>(data);
+
+ switch (GetEventClass(event)) {
+ case kEventClassHIObject:
+ switch (GetEventKind(event)) {
+ case kEventHIObjectConstruct:
+ {
+ HIObjectRef toolbarItem;
+ GetEventParameter(event, kEventParamHIObjectInstance, typeHIObjectRef,
+ 0, sizeof( HIObjectRef ), 0, &toolbarItem);
+
+ QToolBarInHIToolbarInfo *item = new QToolBarInHIToolbarInfo(toolbarItem);
+ SetEventParameter(event, kEventParamHIObjectInstance, typeVoidPtr,
+ sizeof(void *), &item);
+ result = noErr;
+ }
+ break;
+ case kEventHIObjectInitialize:
+ result = CallNextEventHandler(inCallRef, event);
+ if (result == noErr) {
+ QToolBar *toolbar = 0;
+ QMainWindowLayout *layout = 0;
+ GetEventParameter(event, kEventParamQToolBar, typeVoidPtr,
+ 0, sizeof(void *), 0, &toolbar);
+ GetEventParameter(event, kEventParamQMainWindowLayout, typeVoidPtr,
+ 0, sizeof(void *), 0, &layout);
+ object->mainWindowLayout = layout;
+ object->mainWindowLayout->unifiedToolbarHash.insert(object->toolbarItem, toolbar);
+ HIToolbarItemChangeAttributes(object->toolbarItem,
+ kHIToolbarItemLabelDisabled, 0);
+ }
+ break;
+
+ case kEventHIObjectDestruct:
+ delete object;
+ result = noErr;
+ break;
+ }
+ break;
+
+ case kEventClassToolbarItem:
+ switch (GetEventKind(event))
+ {
+ case kEventToolbarItemCreateCustomView:
+ {
+ QToolBar *toolbar
+ = object->mainWindowLayout->unifiedToolbarHash.value(object->toolbarItem);
+ if (toolbar) {
+ HIViewRef hiview = HIViewRef(toolbar->winId());
+ SetEventParameter(event, kEventParamControlRef, typeControlRef,
+ sizeof(HIViewRef), &hiview);
+ result = noErr;
+ }
+ }
+ break;
+ }
+ break;
+ }
+ return result;
+}
+
+void QMainWindowLayout::qtMacHIToolbarRegisterQToolBarInHIToolborItemClass()
+{
+ static bool registered = false;
+
+ if (!registered) {
+ HIObjectRegisterSubclass( kQToolBarHIToolbarItemClassID,
+ kHIToolbarItemClassID, 0, QMainWindowLayout::qtoolbarInHIToolbarHandler,
+ GetEventTypeCount(qtoolbarEvents), qtoolbarEvents, 0, 0 );
+ registered = true;
+ }
+}
+
+static void GetToolbarAllowedItems(CFMutableArrayRef array)
+{
+ CFArrayAppendValue(array, kQToolBarHIToolbarIdentifier);
+}
+
+HIToolbarItemRef QMainWindowLayout::createQToolBarInHIToolbarItem(QToolBar *toolbar,
+ QMainWindowLayout *layout)
+{
+ QMainWindowLayout::qtMacHIToolbarRegisterQToolBarInHIToolborItemClass();
+
+ EventRef event;
+ HIToolbarItemRef result = 0;
+
+ CFStringRef identifier = kQToolBarHIToolbarIdentifier;
+ UInt32 options = kHIToolbarItemAllowDuplicates;
+
+ CreateEvent(0, kEventClassHIObject, kEventHIObjectInitialize,
+ GetCurrentEventTime(), 0, &event);
+ SetEventParameter(event, kEventParamToolbarItemIdentifier, typeCFStringRef,
+ sizeof(CFStringRef), &identifier);
+ SetEventParameter(event, kEventParamAttributes, typeUInt32, sizeof(UInt32), &options);
+ SetEventParameter(event, kEventParamQToolBar, typeVoidPtr, sizeof(void *), &toolbar);
+ SetEventParameter(event, kEventParamQMainWindowLayout, typeVoidPtr, sizeof(void *), &layout);
+
+ HIObjectCreate(kQToolBarHIToolbarItemClassID, event,
+ static_cast<HIObjectRef *>(&result));
+
+ ReleaseEvent(event);
+ return result;
+
+}
+
+HIToolbarItemRef QMainWindowLayout::CreateToolbarItemForIdentifier(CFStringRef identifier,
+ CFTypeRef data)
+{
+ HIToolbarItemRef item = 0;
+ if (CFStringCompare(kQToolBarHIToolbarIdentifier, identifier,
+ kCFCompareBackwards) == kCFCompareEqualTo) {
+ if (data && CFGetTypeID(data) == CFArrayGetTypeID()) {
+ CFArrayRef array = static_cast<CFArrayRef>(data);
+ QToolBar *toolbar = static_cast<QToolBar *>(const_cast<void *>(CFArrayGetValueAtIndex(array, 0)));
+ QMainWindowLayout *layout = static_cast<QMainWindowLayout *>(const_cast<void *>(CFArrayGetValueAtIndex(array, 1)));
+ item = createQToolBarInHIToolbarItem(toolbar, layout);
+ }
+ }
+ return item;
+}
+
+static const EventTypeSpec kToolbarEvents[] = {
+{ kEventClassToolbar, kEventToolbarGetDefaultIdentifiers },
+{ kEventClassToolbar, kEventToolbarGetAllowedIdentifiers },
+{ kEventClassToolbar, kEventToolbarCreateItemWithIdentifier },
+{ kEventClassToolbar, kEventToolbarItemAdded },
+{ kEventClassToolbar, kEventToolbarItemRemoved }
+};
+
+OSStatus QMainWindowLayout::qtmacToolbarDelegate(EventHandlerCallRef, EventRef event, void *data)
+{
+ QMainWindowLayout *mainWindowLayout = static_cast<QMainWindowLayout *>(data);
+ OSStatus result = eventNotHandledErr;
+ CFMutableArrayRef array;
+ CFStringRef identifier;
+ switch (GetEventKind(event)) {
+ case kEventToolbarGetDefaultIdentifiers:
+ case kEventToolbarGetAllowedIdentifiers:
+ GetEventParameter(event, kEventParamMutableArray, typeCFMutableArrayRef, 0,
+ sizeof(CFMutableArrayRef), 0, &array);
+ GetToolbarAllowedItems(array);
+ result = noErr;
+ break;
+ case kEventToolbarCreateItemWithIdentifier: {
+ HIToolbarItemRef item;
+ CFTypeRef data = 0;
+ OSStatus err = GetEventParameter(event, kEventParamToolbarItemIdentifier, typeCFStringRef,
+ 0, sizeof(CFStringRef), 0, &identifier);
+ err = GetEventParameter(event, kEventParamToolbarItemConfigData, typeCFTypeRef,
+ 0, sizeof(CFTypeRef), 0, &data);
+ item = CreateToolbarItemForIdentifier(identifier, data);
+ if (item) {
+ result = SetEventParameter(event, kEventParamToolbarItem, typeHIToolbarItemRef,
+ sizeof(HIToolbarItemRef), &item );
+ }
+ break;
+ }
+ case kEventToolbarItemAdded: {
+ // Double check that our "view" of the toolbar is similar.
+ HIToolbarItemRef item;
+ CFIndex index;
+ if (GetEventParameter(event, kEventParamToolbarItem, typeHIToolbarItemRef,
+ 0, sizeof(HIToolbarItemRef), 0, &item) == noErr
+ && GetEventParameter(event, kEventParamIndex, typeCFIndex, 0,
+ sizeof(CFIndex), 0, &index) == noErr) {
+ CFRetain(item); // We will watch this until it's removed from the list (or bust).
+ mainWindowLayout->toolbarItemsCopy.insert(index, item);
+ QToolBar *toolbar = mainWindowLayout->unifiedToolbarHash.value(item);
+ if (toolbar) {
+ int toolbarIndex = mainWindowLayout->qtoolbarsInUnifiedToolbarList.indexOf(toolbar);
+ if (index != toolbarIndex) {
+ // Dang, we must be out of sync, rebuild it from the "toolbarItemsCopy"
+ mainWindowLayout->qtoolbarsInUnifiedToolbarList.clear();
+ for (int i = 0; i < mainWindowLayout->toolbarItemsCopy.size(); ++i) {
+ // This will either append the correct toolbar or an
+ // null toolbar. This is fine because this list
+ // is really only kept to make sure that things are but in the right order.
+ mainWindowLayout->qtoolbarsInUnifiedToolbarList.append(
+ mainWindowLayout->unifiedToolbarHash.value(mainWindowLayout->
+ toolbarItemsCopy.at(i)));
+ }
+ }
+ }
+ }
+ break;
+ }
+ case kEventToolbarItemRemoved: {
+ HIToolbarItemRef item;
+ if (GetEventParameter(event, kEventParamToolbarItem, typeHIToolbarItemRef,
+ 0, sizeof(HIToolbarItemRef), 0, &item) == noErr) {
+ mainWindowLayout->unifiedToolbarHash.remove(item);
+ for (int i = 0; i < mainWindowLayout->toolbarItemsCopy.size(); ++i) {
+ if (mainWindowLayout->toolbarItemsCopy.at(i) == item) {
+ // I know about it, so release it.
+ mainWindowLayout->toolbarItemsCopy.removeAt(i);
+ mainWindowLayout->qtoolbarsInUnifiedToolbarList.removeAt(i);
+ CFRelease(item);
+ break;
+ }
+ }
+ }
+ break;
+ }
+ }
+ return result;
+}
+#endif // ! QT_MAC_USE_COCOA
+
+#ifndef kWindowUnifiedTitleAndToolbarAttribute
+#define kWindowUnifiedTitleAndToolbarAttribute (1 << 7)
+#endif
+
+void QMainWindowLayout::updateHIToolBarStatus()
+{
+ bool useMacToolbar = layoutState.mainWindow->unifiedTitleAndToolBarOnMac();
+ if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_4) {
+#ifndef QT_MAC_USE_COCOA
+ if (useMacToolbar) {
+ ChangeWindowAttributes(qt_mac_window_for(layoutState.mainWindow),
+ kWindowUnifiedTitleAndToolbarAttribute, 0);
+ } else {
+ ChangeWindowAttributes(qt_mac_window_for(layoutState.mainWindow),
+ 0, kWindowUnifiedTitleAndToolbarAttribute);
+ }
+#endif
+ macWindowToolbarShow(layoutState.mainWindow, useMacToolbar);
+ }
+
+ layoutState.mainWindow->setUpdatesEnabled(false); // reduces a little bit of flicker, not all though
+ if (!useMacToolbar) {
+ OSWindowRef windowRef = qt_mac_window_for(parentWidget());
+ macWindowToolbarShow(parentWidget(), false);
+ // Move everything out of the HIToolbar into the main toolbar.
+ while (!qtoolbarsInUnifiedToolbarList.isEmpty()) {
+ // Should shrink the list by one every time.
+ layoutState.mainWindow->addToolBar(Qt::TopToolBarArea, qtoolbarsInUnifiedToolbarList.first());
+ }
+ macWindowToolbarSet(windowRef, NULL);
+ } else {
+ QList<QToolBar *> toolbars = layoutState.mainWindow->findChildren<QToolBar *>();
+ for (int i = 0; i < toolbars.size(); ++i) {
+ QToolBar *toolbar = toolbars.at(i);
+ if (toolBarArea(toolbar) == Qt::TopToolBarArea) {
+ removeWidget(toolbar); // Do this here, because we are in an in-between state.
+ layoutState.mainWindow->addToolBar(Qt::TopToolBarArea, toolbar);
+ }
+ }
+ }
+ layoutState.mainWindow->setUpdatesEnabled(true);
+}
+
+void QMainWindowLayout::insertIntoMacToolbar(QToolBar *before, QToolBar *toolbar)
+{
+ // This layering could go on to one more level, but I decided to stop here.
+ // The HIToolbar and NSToolbar APIs are fairly similar as you will see.
+ if (toolbar == 0)
+ return;
+
+
+ QToolBarLayout *toolbarLayout = static_cast<QToolBarLayout *>(toolbar->layout());
+ toolbarSaveState.insert(toolbar, ToolBarSaveState(toolbar->isMovable(),
+ toolbar->maximumSize()));
+
+ if (toolbarLayout->hasExpandFlag() == false)
+ toolbar->setMaximumSize(toolbar->sizeHint());
+
+ toolbar->setMovable(false);
+ toolbarLayout->setUsePopupMenu(true);
+ // Make the toolbar a child of the mainwindow to avoid creating a window.
+ toolbar->setParent(layoutState.mainWindow);
+ toolbar->createWinId(); // Now create the OSViewRef.
+
+ layoutState.mainWindow->createWinId();
+
+ OSWindowRef window = qt_mac_window_for(layoutState.mainWindow);
+ int beforeIndex = qtoolbarsInUnifiedToolbarList.indexOf(before);
+ if (beforeIndex == -1)
+ beforeIndex = qtoolbarsInUnifiedToolbarList.size();
+
+ int toolbarIndex = qtoolbarsInUnifiedToolbarList.indexOf(toolbar);
+#ifndef QT_MAC_USE_COCOA
+ HIToolbarRef macToolbar = NULL;
+ if ((GetWindowToolbar(window, &macToolbar) == noErr) && !macToolbar) {
+ HIToolbarCreate(kQMainWindowMacToolbarID,
+ kHIToolbarItemAllowDuplicates, &macToolbar);
+ InstallEventHandler(HIObjectGetEventTarget(static_cast<HIToolbarRef>(macToolbar)),
+ QMainWindowLayout::qtmacToolbarDelegate, GetEventTypeCount(kToolbarEvents),
+ kToolbarEvents, this, 0);
+ HIToolbarSetDisplaySize(macToolbar, kHIToolbarDisplaySizeNormal);
+ HIToolbarSetDisplayMode(macToolbar, kHIToolbarDisplayModeIconOnly);
+ macWindowToolbarSet(window, macToolbar);
+ if (layoutState.mainWindow->isVisible())
+ macWindowToolbarShow(layoutState.mainWindow, true);
+ CFRelease(macToolbar);
+ }
+#else
+ QMacCocoaAutoReleasePool pool;
+ NSToolbar *macToolbar = [window toolbar];
+ if (macToolbar == nil) {
+ macToolbar = [[NSToolbar alloc] initWithIdentifier:(NSString *)kQMainWindowMacToolbarID];
+ [macToolbar setDisplayMode:NSToolbarDisplayModeIconOnly];
+ [macToolbar setSizeMode:NSToolbarSizeModeRegular];
+ [macToolbar setDelegate:[[QCocoaToolBarDelegate alloc] initWithMainWindowLayout:this]];
+ [window setToolbar:macToolbar];
+ [macToolbar release];
+ }
+#endif
+ if (toolbarIndex != -1) {
+ qtoolbarsInUnifiedToolbarList.removeAt(toolbarIndex);
+#ifndef QT_MAC_USE_COCOA
+ HIToolbarRemoveItemAtIndex(macToolbar, toolbarIndex);
+#else
+ [macToolbar removeItemAtIndex:toolbarIndex];
+#endif
+ }
+ qtoolbarsInUnifiedToolbarList.insert(beforeIndex, toolbar);
+#ifndef QT_MAC_USE_COCOA
+ QCFType<HIToolbarItemRef> outItem;
+ const QObject *stupidArray[] = { toolbar, this };
+ QCFType<CFArrayRef> array = CFArrayCreate(0, reinterpret_cast<const void **>(&stupidArray),
+ 2, 0);
+ HIToolbarCreateItemWithIdentifier(macToolbar, kQToolBarHIToolbarIdentifier,
+ array, &outItem);
+ HIToolbarInsertItemAtIndex(macToolbar, outItem, beforeIndex);
+#else
+ NSString *toolbarID = kQToolBarNSToolbarIdentifier;
+ toolbarID = [toolbarID stringByAppendingFormat:@"%p", toolbar];
+ cocoaItemIDToToolbarHash.insert(QCFString::toQString(CFStringRef(toolbarID)), toolbar);
+ [macToolbar insertItemWithItemIdentifier:toolbarID atIndex:beforeIndex];
+#endif
+}
+
+void QMainWindowLayout::removeFromMacToolbar(QToolBar *toolbar)
+{
+ QHash<void *, QToolBar *>::iterator it = unifiedToolbarHash.begin();
+ while (it != unifiedToolbarHash.end()) {
+ if (it.value() == toolbar) {
+ // Rescue our HIView and set it on the mainWindow again.
+ bool saveVisible = !toolbar->isHidden();
+ toolbar->setParent(0);
+ toolbar->setParent(parentWidget());
+ toolbar->setVisible(saveVisible);
+ ToolBarSaveState saveState = toolbarSaveState.value(toolbar);
+ static_cast<QToolBarLayout *>(toolbar->layout())->setUsePopupMenu(false);
+ toolbar->setMovable(saveState.movable);
+ toolbar->setMaximumSize(saveState.maximumSize);
+ toolbarSaveState.remove(toolbar);
+#ifndef QT_MAC_USE_COCOA
+ HIToolbarItemRef item = static_cast<HIToolbarItemRef>(it.key());
+ HIToolbarRemoveItemAtIndex(HIToolbarItemGetToolbar(item),
+ toolbarItemsCopy.indexOf(item));
+#else
+ NSToolbarItem *item = static_cast<NSToolbarItem *>(it.key());
+ [[qt_mac_window_for(layoutState.mainWindow->window()) toolbar]
+ removeItemAtIndex:toolbarItemsCopy.indexOf(item)];
+ // In Carbon this hash and list gets emptied via events. In Cocoa, we have to do it ourselves here.
+ it = unifiedToolbarHash.erase(it);
+ qtoolbarsInUnifiedToolbarList.removeAll(toolbar);
+#endif
+ break;
+ }
+ ++it;
+ }
+}
+
+void QMainWindowLayout::cleanUpMacToolbarItems()
+{
+ for (int i = 0; i < toolbarItemsCopy.size(); ++i)
+ CFRelease(toolbarItemsCopy.at(i));
+ toolbarItemsCopy.clear();
+ unifiedToolbarHash.clear();
+}
+
+void QMainWindowLayout::fixSizeInUnifiedToolbar(QToolBar *tb) const
+{
+ QHash<void *, QToolBar *>::const_iterator it = unifiedToolbarHash.constBegin();
+ NSToolbarItem *item = nil;
+ while (it != unifiedToolbarHash.constEnd()) {
+ if (tb == it.value()) {
+ item = static_cast<NSToolbarItem *>(it.key());
+ break;
+ }
+ ++it;
+ }
+ if (item) {
+ QMacCocoaAutoReleasePool pool;
+ QWidgetItem layoutItem(tb);
+ QSize size = layoutItem.maximumSize();
+ NSSize nssize = NSMakeSize(size.width(), size.height());
+ [item setMaxSize:nssize];
+ size = layoutItem.minimumSize();
+ nssize.width = size.width();
+ nssize.height = size.height();
+ [item setMinSize:nssize];
+ }
+}
+QT_END_NAMESPACE
diff --git a/src/gui/widgets/qmainwindowlayout_p.h b/src/gui/widgets/qmainwindowlayout_p.h
new file mode 100644
index 0000000000..1159aac6cc
--- /dev/null
+++ b/src/gui/widgets/qmainwindowlayout_p.h
@@ -0,0 +1,374 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QDYNAMICMAINWINDOWLAYOUT_P_H
+#define QDYNAMICMAINWINDOWLAYOUT_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 "qmainwindow.h"
+
+#ifndef QT_NO_MAINWINDOW
+
+#include "QtGui/qlayout.h"
+#include "QtGui/qtabbar.h"
+#include "QtCore/qvector.h"
+#include "QtCore/qset.h"
+#include "private/qlayoutengine_p.h"
+
+#include "qdockarealayout_p.h"
+#include "qtoolbararealayout_p.h"
+
+//#define Q_DEBUG_MAINWINDOW_LAYOUT
+
+#ifdef Q_DEBUG_MAINWINDOW_LAYOUT
+QT_BEGIN_NAMESPACE
+class QTextStream;
+Q_GUI_EXPORT void qt_dumpLayout(QTextStream &qout, QMainWindow *window);
+QT_END_NAMESPACE
+#endif // Q_DEBUG_MAINWINDOW_LAYOUT
+
+#ifdef Q_WS_MAC
+// Forward defs to make avoid including Carbon.h (faster compile you know ;).
+struct OpaqueHIObjectRef;
+typedef struct OpaqueHIObjectRef* HIObjectRef;
+typedef HIObjectRef HIToolbarItemRef;
+typedef const void * CFTypeRef;
+typedef const struct __CFString * CFStringRef;
+
+#endif
+
+QT_BEGIN_NAMESPACE
+
+class QToolBar;
+class QWidgetAnimator;
+class QRubberBand;
+
+/* This data structure represents the state of all the tool-bars and dock-widgets. It's value based
+ so it can be easilly copied into a temporary variable. All operations are performed without moving
+ any widgets. Only when we are sure we have the desired state, we call apply(), which moves the
+ widgets.
+*/
+
+class QMainWindowLayoutState
+{
+public:
+ QRect rect;
+ QMainWindow *mainWindow;
+
+ QMainWindowLayoutState(QMainWindow *win);
+
+#ifndef QT_NO_TOOLBAR
+ QToolBarAreaLayout toolBarAreaLayout;
+#endif
+
+#ifndef QT_NO_DOCKWIDGET
+ QDockAreaLayout dockAreaLayout;
+#else
+ QLayoutItem *centralWidgetItem;
+ QRect centralWidgetRect;
+#endif
+
+ void apply(bool animated);
+ void deleteAllLayoutItems();
+ void deleteCentralWidgetItem();
+
+ QSize sizeHint() const;
+ QSize minimumSize() const;
+ void fitLayout();
+
+ QLayoutItem *itemAt(int index, int *x) const;
+ QLayoutItem *takeAt(int index, int *x);
+ QList<int> indexOf(QWidget *widget) const;
+ QLayoutItem *item(QList<int> path);
+ QRect itemRect(QList<int> path) const;
+ QRect gapRect(QList<int> path) const; // ### get rid of this, use itemRect() instead
+
+ bool contains(QWidget *widget) const;
+
+ void setCentralWidget(QWidget *widget);
+ QWidget *centralWidget() const;
+
+ QList<int> gapIndex(QWidget *widget, const QPoint &pos) const;
+ bool insertGap(QList<int> path, QLayoutItem *item);
+ void remove(QList<int> path);
+ void remove(QLayoutItem *item);
+ void clear();
+ bool isValid() const;
+
+ QLayoutItem *plug(QList<int> path);
+ QLayoutItem *unplug(QList<int> path, QMainWindowLayoutState *savedState = 0);
+
+ void saveState(QDataStream &stream) const;
+ bool checkFormat(QDataStream &stream, bool pre43);
+ bool restoreState(QDataStream &stream, const QMainWindowLayoutState &oldState);
+};
+
+class Q_AUTOTEST_EXPORT QMainWindowLayout : public QLayout
+{
+ Q_OBJECT
+
+public:
+ QMainWindowLayoutState layoutState, savedState;
+
+ explicit QMainWindowLayout(QMainWindow *mainwindow);
+ ~QMainWindowLayout();
+
+ QMainWindow::DockOptions dockOptions;
+ void setDockOptions(QMainWindow::DockOptions opts);
+ bool usesHIToolBar(QToolBar *toolbar) const;
+
+ // status bar
+
+ QLayoutItem *statusbar;
+
+#ifndef QT_NO_STATUSBAR
+ QStatusBar *statusBar() const;
+ void setStatusBar(QStatusBar *sb);
+#endif
+
+ // central widget
+
+ QWidget *centralWidget() const;
+ void setCentralWidget(QWidget *cw);
+
+ // toolbars
+
+#ifndef QT_NO_TOOLBAR
+ void addToolBarBreak(Qt::ToolBarArea area);
+ void insertToolBarBreak(QToolBar *before);
+ void removeToolBarBreak(QToolBar *before);
+
+ void addToolBar(Qt::ToolBarArea area, QToolBar *toolbar, bool needAddChildWidget = true);
+ void insertToolBar(QToolBar *before, QToolBar *toolbar);
+ Qt::ToolBarArea toolBarArea(QToolBar *toolbar) const;
+ bool toolBarBreak(QToolBar *toolBar) const;
+ void getStyleOptionInfo(QStyleOptionToolBar *option, QToolBar *toolBar) const;
+ void removeToolBar(QToolBar *toolbar);
+ void toggleToolBarsVisible();
+ void moveToolBar(QToolBar *toolbar, int pos);
+#endif
+
+ // dock widgets
+
+#ifndef QT_NO_DOCKWIDGET
+ void setCorner(Qt::Corner corner, Qt::DockWidgetArea area);
+ Qt::DockWidgetArea corner(Qt::Corner corner) const;
+ void addDockWidget(Qt::DockWidgetArea area,
+ QDockWidget *dockwidget,
+ Qt::Orientation orientation);
+ void splitDockWidget(QDockWidget *after,
+ QDockWidget *dockwidget,
+ Qt::Orientation orientation);
+ void tabifyDockWidget(QDockWidget *first, QDockWidget *second);
+ Qt::DockWidgetArea dockWidgetArea(QDockWidget *dockwidget) const;
+ void raise(QDockWidget *widget);
+ void setVerticalTabsEnabled(bool enabled);
+ bool restoreDockWidget(QDockWidget *dockwidget);
+
+#ifndef QT_NO_TABBAR
+ bool _documentMode;
+ bool documentMode() const;
+ void setDocumentMode(bool enabled);
+
+ QTabBar *getTabBar();
+ QSet<QTabBar*> usedTabBars;
+ QList<QTabBar*> unusedTabBars;
+ bool verticalTabsEnabled;
+
+ QWidget *getSeparatorWidget();
+ QSet<QWidget*> usedSeparatorWidgets;
+ QList<QWidget*> unusedSeparatorWidgets;
+ int sep; // separator extent
+
+#ifndef QT_NO_TABWIDGET
+ QTabWidget::TabPosition tabPositions[4];
+ QTabWidget::TabShape _tabShape;
+
+ QTabWidget::TabShape tabShape() const;
+ void setTabShape(QTabWidget::TabShape tabShape);
+ QTabWidget::TabPosition tabPosition(Qt::DockWidgetArea area) const;
+ void setTabPosition(Qt::DockWidgetAreas areas, QTabWidget::TabPosition tabPosition);
+#endif // QT_NO_TABWIDGET
+#endif // QT_NO_TABBAR
+
+ // separators
+
+ QList<int> movingSeparator;
+ QPoint movingSeparatorOrigin, movingSeparatorPos;
+ QTimer *separatorMoveTimer;
+ QVector<QLayoutStruct> separatorMoveCache;
+
+ bool startSeparatorMove(const QPoint &pos);
+ bool separatorMove(const QPoint &pos);
+ bool endSeparatorMove(const QPoint &pos);
+ void keepSize(QDockWidget *w);
+#endif // QT_NO_DOCKWIDGET
+
+ // save/restore
+
+ enum { // sentinel values used to validate state data
+ VersionMarker = 0xff
+ };
+ void saveState(QDataStream &stream) const;
+ bool restoreState(QDataStream &stream);
+
+ // QLayout interface
+
+ void addItem(QLayoutItem *item);
+ void setGeometry(const QRect &r);
+ QLayoutItem *itemAt(int index) const;
+ QLayoutItem *takeAt(int index);
+ int count() const;
+
+ QSize sizeHint() const;
+ QSize minimumSize() const;
+ mutable QSize szHint;
+ mutable QSize minSize;
+ void invalidate();
+
+ // animations
+
+ QWidgetAnimator *widgetAnimator;
+ QList<int> currentGapPos;
+ QRect currentGapRect;
+ QWidget *pluggingWidget;
+#ifndef QT_NO_RUBBERBAND
+ QRubberBand *gapIndicator;
+#endif
+
+ QList<int> hover(QLayoutItem *widgetItem, const QPoint &mousePos);
+ bool plug(QLayoutItem *widgetItem);
+ QLayoutItem *unplug(QWidget *widget);
+ void revert(QLayoutItem *widgetItem);
+ void updateGapIndicator();
+ void paintDropIndicator(QPainter *p, QWidget *widget, const QRegion &clip);
+ void applyState(QMainWindowLayoutState &newState, bool animate = true);
+ void restore(bool keepSavedState = false);
+ void updateHIToolBarStatus();
+
+private slots:
+ void animationFinished(QWidget *widget);
+ void allAnimationsFinished();
+#ifndef QT_NO_DOCKWIDGET
+ void doSeparatorMove();
+#ifndef QT_NO_TABBAR
+ void tabChanged();
+#endif
+#endif
+private:
+#ifndef QT_NO_TABBAR
+ void updateTabBarShapes();
+#endif
+#ifdef Q_WS_MAC
+# ifndef QT_MAC_USE_COCOA
+ static OSStatus qtmacToolbarDelegate(EventHandlerCallRef, EventRef , void *);
+ static OSStatus qtoolbarInHIToolbarHandler(EventHandlerCallRef inCallRef, EventRef event,
+ void *data);
+ static void qtMacHIToolbarRegisterQToolBarInHIToolborItemClass();
+ static HIToolbarItemRef CreateToolbarItemForIdentifier(CFStringRef identifier, CFTypeRef data);
+ static HIToolbarItemRef createQToolBarInHIToolbarItem(QToolBar *toolbar,
+ QMainWindowLayout *layout);
+# endif
+public:
+ struct ToolBarSaveState {
+ ToolBarSaveState() : movable(false) { }
+ ToolBarSaveState(bool newMovable, const QSize &newMax)
+ : movable(newMovable), maximumSize(newMax) { }
+ bool movable;
+ QSize maximumSize;
+ };
+ QList<QToolBar *> qtoolbarsInUnifiedToolbarList;
+ QList<void *> toolbarItemsCopy;
+ QHash<void *, QToolBar *> unifiedToolbarHash;
+ QHash<QToolBar *, ToolBarSaveState> toolbarSaveState;
+ QHash<QString, QToolBar *> cocoaItemIDToToolbarHash;
+ void insertIntoMacToolbar(QToolBar *before, QToolBar *after);
+ void removeFromMacToolbar(QToolBar *toolbar);
+ void cleanUpMacToolbarItems();
+ void fixSizeInUnifiedToolbar(QToolBar *tb) const;
+ bool useHIToolBar;
+#endif
+};
+QT_END_NAMESPACE
+
+#endif // QT_NO_MAINWINDOW
+
+QT_BEGIN_NAMESPACE
+static inline int pick(Qt::Orientation o, const QPoint &pos)
+{ return o == Qt::Horizontal ? pos.x() : pos.y(); }
+
+static inline int pick(Qt::Orientation o, const QSize &size)
+{ return o == Qt::Horizontal ? size.width() : size.height(); }
+
+static inline int &rpick(Qt::Orientation o, QPoint &pos)
+{ return o == Qt::Horizontal ? pos.rx() : pos.ry(); }
+
+static inline int &rpick(Qt::Orientation o, QSize &size)
+{ return o == Qt::Horizontal ? size.rwidth() : size.rheight(); }
+
+static inline QSizePolicy::Policy pick(Qt::Orientation o, const QSizePolicy &policy)
+{ return o == Qt::Horizontal ? policy.horizontalPolicy() : policy.verticalPolicy(); }
+
+static inline int perp(Qt::Orientation o, const QPoint &pos)
+{ return o == Qt::Vertical ? pos.x() : pos.y(); }
+
+static inline int perp(Qt::Orientation o, const QSize &size)
+{ return o == Qt::Vertical ? size.width() : size.height(); }
+
+static inline int &rperp(Qt::Orientation o, QPoint &pos)
+{ return o == Qt::Vertical ? pos.rx() : pos.ry(); }
+
+static inline int &rperp(Qt::Orientation o, QSize &size)
+{ return o == Qt::Vertical ? size.rwidth() : size.rheight(); }
+
+QT_END_NAMESPACE
+
+#endif // QDYNAMICMAINWINDOWLAYOUT_P_H
diff --git a/src/gui/widgets/qmdiarea.cpp b/src/gui/widgets/qmdiarea.cpp
new file mode 100644
index 0000000000..598d3b537e
--- /dev/null
+++ b/src/gui/widgets/qmdiarea.cpp
@@ -0,0 +1,2597 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*!
+ \class QMdiArea
+ \brief The QMdiArea widget provides an area in which MDI windows are displayed.
+ \since 4.3
+ \ingroup application
+ \mainclass
+
+ QMdiArea functions, essentially, like a window manager for MDI
+ windows. For instance, it draws the windows it manages on itself
+ and arranges them in a cascading or tile pattern. QMdiArea is
+ commonly used as the center widget in a QMainWindow to create MDI
+ applications, but can also be placed in any layout. The following
+ code adds an area to a main window:
+
+ \snippet doc/src/snippets/mdiareasnippets.cpp 0
+
+ Unlike the window managers for top-level windows, all window flags
+ (Qt::WindowFlags) are supported by QMdiArea as long as the flags
+ are supported by the current widget style. If a specific flag is
+ not supported by the style (e.g., the
+ \l{Qt::}{WindowShadeButtonHint}), you can still shade the window
+ with showShaded().
+
+ Subwindows in QMdiArea are instances of QMdiSubWindow. They
+ are added to an MDI area with addSubWindow(). It is common to pass
+ a QWidget, which is set as the internal widget, to this function,
+ but it is also possible to pass a QMdiSubWindow directly.The class
+ inherits QWidget, and you can use the same API as with a normal
+ top-level window when programming. QMdiSubWindow also has behavior
+ that is specific to MDI windows. See the QMdiSubWindow class
+ description for more details.
+
+ A subwindow becomes active when it gets the keyboard focus, or
+ when setFocus() is called. The user activates a window by moving
+ focus in the usual ways. The MDI area emits the
+ subWindowActivated() signal when the active window changes, and
+ the activeSubWindow() function returns the active subwindow.
+
+ The convenience function subWindowList() returns a list of all
+ subwindows. This information could be used in a popup menu
+ containing a list of windows, for example.
+
+ The subwindows are sorted by the the current
+ \l{QMdiArea::}{WindowOrder}. This is used for the subWindowList()
+ and for activateNextSubWindow() and acivatePreviousSubWindow().
+ Also, it is used when cascading or tiling the windows with
+ cascadeSubWindows() and tileSubWindows().
+
+ QMdiArea provides two built-in layout strategies for
+ subwindows: cascadeSubWindows() and tileSubWindows(). Both are
+ slots and are easily connected to menu entries.
+
+ \table
+ \row \o \inlineimage mdi-cascade.png
+ \o \inlineimage mdi-tile.png
+ \endtable
+
+ \note The default scroll bar property for QMdiArea is Qt::ScrollBarAlwaysOff.
+
+ \sa QMdiSubWindow
+*/
+
+/*!
+ \fn QMdiArea::subWindowActivated(QMdiSubWindow *window)
+
+ QMdiArea emits this signal after \a window has been activated. When \a
+ window is 0, QMdiArea has just deactivated its last active window, and
+ there are no active windows on the workspace.
+
+ \sa QMdiArea::activeSubWindow()
+*/
+
+/*!
+ \enum QMdiArea::AreaOption
+
+ This enum describes options that customize the behavior of the
+ QMdiArea.
+
+ \value DontMaximizeSubWindowOnActivation When the active subwindow
+ is maximized, the default behavior is to maximize the next
+ subwindow that is activated. Set this option if you do not want
+ this behavior.
+*/
+
+/*!
+ \enum QMdiArea::WindowOrder
+
+ Specifies the criteria to use for ordering the list of child windows
+ returned by subWindowList(). The functions cascadeSubWindows() and
+ tileSubWindows() follow this order when arranging the windows.
+
+ \value CreationOrder The windows are returned in the order of
+ their creation.
+
+ \value StackingOrder The windows are returned in the order in
+ which they are stacked, with the top-most window being last in
+ the list.
+
+ \value ActivationHistoryOrder The windows are returned in the order in
+ which they were activated.
+
+ \sa subWindowList()
+*/
+
+/*!
+ \enum QMdiArea::ViewMode
+ \since 4.4
+
+ This enum describes the view mode of the area; i.e. how sub-windows
+ will be displayed.
+
+ \value SubWindowView Display sub-windows with window frames (default).
+ \value TabbedView Display sub-windows with tabs in a tab bar.
+
+ \sa setViewMode()
+*/
+
+#include "qmdiarea_p.h"
+
+#ifndef QT_NO_MDIAREA
+
+#include <QApplication>
+#include <QStyle>
+#if defined(Q_WS_MAC) && !defined(QT_NO_STYLE_MAC)
+#include <QMacStyle>
+#endif
+#include <QChildEvent>
+#include <QResizeEvent>
+#include <QScrollBar>
+#include <QtAlgorithms>
+#include <QMutableListIterator>
+#include <QPainter>
+#include <QFontMetrics>
+#include <QStyleOption>
+#include <QDesktopWidget>
+#include <QDebug>
+#include <qmath.h>
+#include <private/qlayoutengine_p.h>
+
+QT_BEGIN_NAMESPACE
+
+using namespace QMdi;
+
+// Asserts in debug mode, gives warning otherwise.
+static bool sanityCheck(const QMdiSubWindow * const child, const char *where)
+{
+ if (!child) {
+ const char error[] = "null pointer";
+ Q_ASSERT_X(false, where, error);
+ qWarning("%s:%s", where, error);
+ return false;
+ }
+ return true;
+}
+
+static bool sanityCheck(const QList<QWidget *> &widgets, const int index, const char *where)
+{
+ if (index < 0 || index >= widgets.size()) {
+ const char error[] = "index out of range";
+ Q_ASSERT_X(false, where, error);
+ qWarning("%s:%s", where, error);
+ return false;
+ }
+ if (!widgets.at(index)) {
+ const char error[] = "null pointer";
+ Q_ASSERT_X(false, where, error);
+ qWarning("%s:%s", where, error);
+ return false;
+ }
+ return true;
+}
+
+static void setIndex(int *index, int candidate, int min, int max, bool isIncreasing)
+{
+ if (!index)
+ return;
+
+ if (isIncreasing) {
+ if (candidate > max)
+ *index = min;
+ else
+ *index = qMax(candidate, min);
+ } else {
+ if (candidate < min)
+ *index = max;
+ else
+ *index = qMin(candidate, max);
+ }
+ Q_ASSERT(*index >= min && *index <= max);
+}
+
+static inline bool useScrollBar(const QRect &childrenRect, const QSize &maxViewportSize,
+ Qt::Orientation orientation)
+{
+ if (orientation == Qt::Horizontal)
+ return childrenRect.width() > maxViewportSize.width()
+ || childrenRect.left() < 0
+ || childrenRect.right() >= maxViewportSize.width();
+ else
+ return childrenRect.height() > maxViewportSize.height()
+ || childrenRect.top() < 0
+ || childrenRect.bottom() >= maxViewportSize.height();
+}
+
+// Returns the closest mdi area containing the widget (if any).
+static inline QMdiArea *mdiAreaParent(QWidget *widget)
+{
+ if (!widget)
+ return 0;
+
+ QWidget *parent = widget->parentWidget();
+ while (parent) {
+ if (QMdiArea *area = qobject_cast<QMdiArea *>(parent))
+ return area;
+ parent = parent->parentWidget();
+ }
+ return 0;
+}
+
+#ifndef QT_NO_TABWIDGET
+static inline QTabBar::Shape tabBarShapeFrom(QTabWidget::TabShape shape, QTabWidget::TabPosition position)
+{
+ const bool rounded = (shape == QTabWidget::Rounded);
+ if (position == QTabWidget::North)
+ return rounded ? QTabBar::RoundedNorth : QTabBar::TriangularNorth;
+ if (position == QTabWidget::South)
+ return rounded ? QTabBar::RoundedSouth : QTabBar::TriangularSouth;
+ if (position == QTabWidget::East)
+ return rounded ? QTabBar::RoundedEast : QTabBar::TriangularEast;
+ if (position == QTabWidget::West)
+ return rounded ? QTabBar::RoundedWest : QTabBar::TriangularWest;
+ return QTabBar::RoundedNorth;
+}
+#endif // QT_NO_TABWIDGET
+
+static inline QString tabTextFor(QMdiSubWindow *subWindow)
+{
+ if (!subWindow)
+ return QString();
+
+ QString title = subWindow->windowTitle();
+ if (subWindow->isWindowModified()) {
+ title.replace(QLatin1String("[*]"), QLatin1String("*"));
+ } else {
+ extern QString qt_setWindowTitle_helperHelper(const QString&, const QWidget*);
+ title = qt_setWindowTitle_helperHelper(title, subWindow);
+ }
+
+ return title.isEmpty() ? QMdiArea::tr("(Untitled)") : title;
+}
+
+/*!
+ \internal
+*/
+void RegularTiler::rearrange(QList<QWidget *> &widgets, const QRect &domain) const
+{
+ if (widgets.isEmpty())
+ return;
+
+ const int n = widgets.size();
+ const int ncols = qMax(qCeil(qSqrt(qreal(n))), 1);
+ const int nrows = qMax((n % ncols) ? (n / ncols + 1) : (n / ncols), 1);
+ const int nspecial = (n % ncols) ? (ncols - n % ncols) : 0;
+ const int dx = domain.width() / ncols;
+ const int dy = domain.height() / nrows;
+
+ int i = 0;
+ for (int row = 0; row < nrows; ++row) {
+ const int y1 = int(row * (dy + 1));
+ for (int col = 0; col < ncols; ++col) {
+ if (row == 1 && col < nspecial)
+ continue;
+ const int x1 = int(col * (dx + 1));
+ int x2 = int(x1 + dx);
+ int y2 = int(y1 + dy);
+ if (row == 0 && col < nspecial) {
+ y2 *= 2;
+ if (nrows != 2)
+ y2 += 1;
+ else
+ y2 = domain.bottom();
+ }
+ if (col == ncols - 1 && x2 != domain.right())
+ x2 = domain.right();
+ if (row == nrows - 1 && y2 != domain.bottom())
+ y2 = domain.bottom();
+ if (!sanityCheck(widgets, i, "RegularTiler"))
+ continue;
+ QWidget *widget = widgets.at(i++);
+ QRect newGeometry = QRect(QPoint(x1, y1), QPoint(x2, y2));
+ widget->setGeometry(QStyle::visualRect(widget->layoutDirection(), domain, newGeometry));
+ }
+ }
+}
+
+/*!
+ \internal
+*/
+void SimpleCascader::rearrange(QList<QWidget *> &widgets, const QRect &domain) const
+{
+ if (widgets.isEmpty())
+ return;
+
+ // Tunables:
+ const int topOffset = 0;
+ const int bottomOffset = 50;
+ const int leftOffset = 0;
+ const int rightOffset = 100;
+ const int dx = 10;
+
+ QStyleOptionTitleBar options;
+ options.initFrom(widgets.at(0));
+ int titleBarHeight = widgets.at(0)->style()->pixelMetric(QStyle::PM_TitleBarHeight, &options, widgets.at(0));
+#if defined(Q_WS_MAC) && !defined(QT_NO_STYLE_MAC)
+ // ### Remove this after the mac style has been fixed
+ if (qobject_cast<QMacStyle *>(widgets.at(0)->style()))
+ titleBarHeight -= 4;
+#endif
+ const QFontMetrics fontMetrics = QFontMetrics(QApplication::font("QWorkspaceTitleBar"));
+ const int dy = qMax(titleBarHeight - (titleBarHeight - fontMetrics.height()) / 2, 1);
+
+ const int n = widgets.size();
+ const int nrows = qMax((domain.height() - (topOffset + bottomOffset)) / dy, 1);
+ const int ncols = qMax(n / nrows + ((n % nrows) ? 1 : 0), 1);
+ const int dcol = (domain.width() - (leftOffset + rightOffset)) / ncols;
+
+ int i = 0;
+ for (int row = 0; row < nrows; ++row) {
+ for (int col = 0; col < ncols; ++col) {
+ const int x = leftOffset + row * dx + col * dcol;
+ const int y = topOffset + row * dy;
+ if (!sanityCheck(widgets, i, "SimpleCascader"))
+ continue;
+ QWidget *widget = widgets.at(i++);
+ QRect newGeometry = QRect(QPoint(x, y), widget->sizeHint());
+ widget->setGeometry(QStyle::visualRect(widget->layoutDirection(), domain, newGeometry));
+ if (i == n)
+ return;
+ }
+ }
+}
+
+/*!
+ \internal
+*/
+void IconTiler::rearrange(QList<QWidget *> &widgets, const QRect &domain) const
+{
+ if (widgets.isEmpty() || !sanityCheck(widgets, 0, "IconTiler"))
+ return;
+
+ const int n = widgets.size();
+ const int width = widgets.at(0)->width();
+ const int height = widgets.at(0)->height();
+ const int ncols = qMax(domain.width() / width, 1);
+ const int nrows = n / ncols + ((n % ncols) ? 1 : 0);
+
+ int i = 0;
+ for (int row = 0; row < nrows; ++row) {
+ for (int col = 0; col < ncols; ++col) {
+ const int x = col * width;
+ const int y = domain.height() - height - row * height;
+ if (!sanityCheck(widgets, i, "IconTiler"))
+ continue;
+ QWidget *widget = widgets.at(i++);
+ QPoint newPos(x, y);
+ QRect newGeometry = QRect(newPos.x(), newPos.y(), widget->width(), widget->height());
+ widget->setGeometry(QStyle::visualRect(widget->layoutDirection(), domain, newGeometry));
+ if (i == n)
+ return;
+ }
+ }
+}
+
+/*!
+ \internal
+ Calculates the accumulated overlap (intersection area) between 'source' and 'rects'.
+*/
+int MinOverlapPlacer::accumulatedOverlap(const QRect &source, const QList<QRect> &rects)
+{
+ int accOverlap = 0;
+ foreach (const QRect &rect, rects) {
+ QRect intersection = source.intersected(rect);
+ accOverlap += intersection.width() * intersection.height();
+ }
+ return accOverlap;
+}
+
+
+/*!
+ \internal
+ Finds among 'source' the rectangle with the minimum accumulated overlap with the
+ rectangles in 'rects'.
+*/
+QRect MinOverlapPlacer::findMinOverlapRect(const QList<QRect> &source, const QList<QRect> &rects)
+{
+ int minAccOverlap = -1;
+ QRect minAccOverlapRect;
+ foreach (const QRect &srcRect, source) {
+ const int accOverlap = accumulatedOverlap(srcRect, rects);
+ if (accOverlap < minAccOverlap || minAccOverlap == -1) {
+ minAccOverlap = accOverlap;
+ minAccOverlapRect = srcRect;
+ }
+ }
+ return minAccOverlapRect;
+}
+
+/*!
+ \internal
+ Gets candidates for the final placement.
+*/
+void MinOverlapPlacer::getCandidatePlacements(const QSize &size, const QList<QRect> &rects,
+ const QRect &domain,QList<QRect> &candidates)
+{
+ QSet<int> xset;
+ QSet<int> yset;
+ xset << domain.left() << domain.right() - size.width() + 1;
+ yset << domain.top();
+ if (domain.bottom() - size.height() + 1 >= 0)
+ yset << domain.bottom() - size.height() + 1;
+ foreach (const QRect &rect, rects) {
+ xset << rect.right() + 1;
+ yset << rect.bottom() + 1;
+ }
+
+ QList<int> xlist = xset.values();
+ qSort(xlist.begin(), xlist.end());
+ QList<int> ylist = yset.values();
+ qSort(ylist.begin(), ylist.end());
+
+ foreach (int y, ylist)
+ foreach (int x, xlist)
+ candidates << QRect(QPoint(x, y), size);
+}
+
+/*!
+ \internal
+ Finds all rectangles in 'source' not completely inside 'domain'. The result is stored
+ in 'result' and also removed from 'source'.
+*/
+void MinOverlapPlacer::findNonInsiders(const QRect &domain, QList<QRect> &source,
+ QList<QRect> &result)
+{
+ QMutableListIterator<QRect> it(source);
+ while (it.hasNext()) {
+ const QRect srcRect = it.next();
+ if (!domain.contains(srcRect)) {
+ result << srcRect;
+ it.remove();
+ }
+ }
+}
+
+/*!
+ \internal
+ Finds all rectangles in 'source' that overlaps 'domain' by the maximum overlap area
+ between 'domain' and any rectangle in 'source'. The result is stored in 'result'.
+*/
+void MinOverlapPlacer::findMaxOverlappers(const QRect &domain, const QList<QRect> &source,
+ QList<QRect> &result)
+{
+ int maxOverlap = -1;
+ foreach (const QRect &srcRect, source) {
+ QRect intersection = domain.intersected(srcRect);
+ const int overlap = intersection.width() * intersection.height();
+ if (overlap >= maxOverlap || maxOverlap == -1) {
+ if (overlap > maxOverlap) {
+ maxOverlap = overlap;
+ result.clear();
+ }
+ result << srcRect;
+ }
+ }
+}
+
+/*!
+ \internal
+ Finds among the rectangles in 'source' the best placement. Here, 'best' means the
+ placement that overlaps the rectangles in 'rects' as little as possible while at the
+ same time being as much as possible inside 'domain'.
+*/
+QPoint MinOverlapPlacer::findBestPlacement(const QRect &domain, const QList<QRect> &rects,
+ QList<QRect> &source)
+{
+ QList<QRect> nonInsiders;
+ findNonInsiders(domain, source, nonInsiders);
+
+ if (!source.empty())
+ return findMinOverlapRect(source, rects).topLeft();
+
+ QList<QRect> maxOverlappers;
+ findMaxOverlappers(domain, nonInsiders, maxOverlappers);
+ return findMinOverlapRect(maxOverlappers, rects).topLeft();
+}
+
+
+/*!
+ \internal
+ Places the rectangle defined by 'size' relative to 'rects' and 'domain' so that it
+ overlaps 'rects' as little as possible and 'domain' as much as possible.
+ Returns the position of the resulting rectangle.
+*/
+QPoint MinOverlapPlacer::place(const QSize &size, const QList<QRect> &rects,
+ const QRect &domain) const
+{
+ if (size.isEmpty() || !domain.isValid())
+ return QPoint();
+ foreach (const QRect &rect, rects) {
+ if (!rect.isValid())
+ return QPoint();
+ }
+
+ QList<QRect> candidates;
+ getCandidatePlacements(size, rects, domain, candidates);
+ return findBestPlacement(domain, rects, candidates);
+}
+
+#ifndef QT_NO_TABBAR
+class QMdiAreaTabBar : public QTabBar
+{
+public:
+ QMdiAreaTabBar(QWidget *parent) : QTabBar(parent) {}
+
+protected:
+ void mousePressEvent(QMouseEvent *event);
+#ifndef QT_NO_CONTEXTMENU
+ void contextMenuEvent(QContextMenuEvent *event);
+#endif
+
+private:
+ QMdiSubWindow *subWindowFromIndex(int index) const;
+};
+
+/*!
+ \internal
+*/
+void QMdiAreaTabBar::mousePressEvent(QMouseEvent *event)
+{
+ if (event->button() != Qt::MidButton) {
+ QTabBar::mousePressEvent(event);
+ return;
+ }
+
+ QMdiSubWindow *subWindow = subWindowFromIndex(tabAt(event->pos()));
+ if (!subWindow) {
+ event->ignore();
+ return;
+ }
+
+ subWindow->close();
+}
+
+#ifndef QT_NO_CONTEXTMENU
+/*!
+ \internal
+*/
+void QMdiAreaTabBar::contextMenuEvent(QContextMenuEvent *event)
+{
+ QPointer<QMdiSubWindow> subWindow = subWindowFromIndex(tabAt(event->pos()));
+ if (!subWindow || subWindow->isHidden()) {
+ event->ignore();
+ return;
+ }
+
+#ifndef QT_NO_MENU
+ QMdiSubWindowPrivate *subWindowPrivate = subWindow->d_func();
+ if (!subWindowPrivate->systemMenu) {
+ event->ignore();
+ return;
+ }
+
+ QMdiSubWindow *currentSubWindow = subWindowFromIndex(currentIndex());
+ Q_ASSERT(currentSubWindow);
+
+ // We don't want these actions to show up in the system menu when the
+ // current sub-window is maximized, i.e. covers the entire viewport.
+ if (currentSubWindow->isMaximized()) {
+ subWindowPrivate->setVisible(QMdiSubWindowPrivate::MoveAction, false);
+ subWindowPrivate->setVisible(QMdiSubWindowPrivate::ResizeAction, false);
+ subWindowPrivate->setVisible(QMdiSubWindowPrivate::MinimizeAction, false);
+ subWindowPrivate->setVisible(QMdiSubWindowPrivate::MaximizeAction, false);
+ subWindowPrivate->setVisible(QMdiSubWindowPrivate::MaximizeAction, false);
+ subWindowPrivate->setVisible(QMdiSubWindowPrivate::StayOnTopAction, false);
+ }
+
+ // Show system menu.
+ subWindowPrivate->systemMenu->exec(event->globalPos());
+ if (!subWindow)
+ return;
+
+ // Restore action visibility.
+ subWindowPrivate->updateActions();
+#endif // QT_NO_MENU
+}
+#endif // QT_NO_CONTEXTMENU
+
+/*!
+ \internal
+*/
+QMdiSubWindow *QMdiAreaTabBar::subWindowFromIndex(int index) const
+{
+ if (index < 0 || index >= count())
+ return 0;
+
+ QMdiArea *mdiArea = qobject_cast<QMdiArea *>(parentWidget());
+ Q_ASSERT(mdiArea);
+
+ const QList<QMdiSubWindow *> subWindows = mdiArea->subWindowList();
+ Q_ASSERT(index < subWindows.size());
+
+ QMdiSubWindow *subWindow = mdiArea->subWindowList().at(index);
+ Q_ASSERT(subWindow);
+
+ return subWindow;
+}
+#endif // QT_NO_TABBAR
+
+/*!
+ \internal
+*/
+QMdiAreaPrivate::QMdiAreaPrivate()
+ : cascader(0),
+ regularTiler(0),
+ iconTiler(0),
+ placer(0),
+#ifndef QT_NO_RUBBERBAND
+ rubberBand(0),
+#endif
+#ifndef QT_NO_TABBAR
+ tabBar(0),
+#endif
+ activationOrder(QMdiArea::CreationOrder),
+ viewMode(QMdiArea::SubWindowView),
+#ifndef QT_NO_TABBAR
+ documentMode(false),
+#endif
+#ifndef QT_NO_TABWIDGET
+ tabShape(QTabWidget::Rounded),
+ tabPosition(QTabWidget::North),
+#endif
+ ignoreGeometryChange(false),
+ ignoreWindowStateChange(false),
+ isActivated(false),
+ isSubWindowsTiled(false),
+ showActiveWindowMaximized(false),
+ tileCalledFromResizeEvent(false),
+ updatesDisabledByUs(false),
+ inViewModeChange(false),
+ indexToNextWindow(-1),
+ indexToPreviousWindow(-1),
+ indexToHighlighted(-1),
+ indexToLastActiveTab(-1),
+ resizeTimerId(-1),
+ tabToPreviousTimerId(-1)
+{
+}
+
+/*!
+ \internal
+*/
+void QMdiAreaPrivate::_q_deactivateAllWindows(QMdiSubWindow *aboutToActivate)
+{
+ if (ignoreWindowStateChange)
+ return;
+
+ Q_Q(QMdiArea);
+ if (!aboutToActivate)
+ aboutToBecomeActive = qobject_cast<QMdiSubWindow *>(q->sender());
+ else
+ aboutToBecomeActive = aboutToActivate;
+ Q_ASSERT(aboutToBecomeActive);
+
+ foreach (QMdiSubWindow *child, childWindows) {
+ if (!sanityCheck(child, "QMdiArea::deactivateAllWindows") || aboutToBecomeActive == child)
+ continue;
+ // We don't want to handle signals caused by child->showNormal().
+ ignoreWindowStateChange = true;
+ if(!(options & QMdiArea::DontMaximizeSubWindowOnActivation) && !showActiveWindowMaximized)
+ showActiveWindowMaximized = child->isMaximized() && child->isVisible();
+ if (showActiveWindowMaximized && child->isMaximized()) {
+ if (q->updatesEnabled()) {
+ updatesDisabledByUs = true;
+ q->setUpdatesEnabled(false);
+ }
+ child->showNormal();
+ }
+ if (child->isMinimized() && !child->isShaded() && !windowStaysOnTop(child))
+ child->lower();
+ ignoreWindowStateChange = false;
+ child->d_func()->setActive(false);
+ }
+}
+
+/*!
+ \internal
+*/
+void QMdiAreaPrivate::_q_processWindowStateChanged(Qt::WindowStates oldState,
+ Qt::WindowStates newState)
+{
+ if (ignoreWindowStateChange)
+ return;
+
+ Q_Q(QMdiArea);
+ QMdiSubWindow *child = qobject_cast<QMdiSubWindow *>(q->sender());
+ if (!child)
+ return;
+
+ // windowActivated
+ if (!(oldState & Qt::WindowActive) && (newState & Qt::WindowActive))
+ emitWindowActivated(child);
+ // windowDeactivated
+ else if ((oldState & Qt::WindowActive) && !(newState & Qt::WindowActive))
+ resetActiveWindow(child);
+
+ // windowMinimized
+ if (!(oldState & Qt::WindowMinimized) && (newState & Qt::WindowMinimized)) {
+ isSubWindowsTiled = false;
+ arrangeMinimizedSubWindows();
+ // windowMaximized
+ } else if (!(oldState & Qt::WindowMaximized) && (newState & Qt::WindowMaximized)) {
+ internalRaise(child);
+ // windowRestored
+ } else if (!(newState & (Qt::WindowMaximized | Qt::WindowMinimized))) {
+ internalRaise(child);
+ if (oldState & Qt::WindowMinimized)
+ arrangeMinimizedSubWindows();
+ }
+}
+
+void QMdiAreaPrivate::_q_currentTabChanged(int index)
+{
+#ifdef QT_NO_TABBAR
+ Q_UNUSED(index);
+#else
+ if (!tabBar || index < 0)
+ return;
+
+ // If the previous active sub-window was hidden, disable the tab.
+ if (indexToLastActiveTab >= 0 && indexToLastActiveTab < tabBar->count()
+ && indexToLastActiveTab < childWindows.count()) {
+ QMdiSubWindow *lastActive = childWindows.at(indexToLastActiveTab);
+ if (lastActive && lastActive->isHidden())
+ tabBar->setTabEnabled(indexToLastActiveTab, false);
+ }
+
+ indexToLastActiveTab = index;
+ Q_ASSERT(childWindows.size() > index);
+ QMdiSubWindow *subWindow = childWindows.at(index);
+ Q_ASSERT(subWindow);
+ activateWindow(subWindow);
+#endif // QT_NO_TABBAR
+}
+
+/*!
+ \internal
+*/
+void QMdiAreaPrivate::appendChild(QMdiSubWindow *child)
+{
+ Q_Q(QMdiArea);
+ Q_ASSERT(child && childWindows.indexOf(child) == -1);
+
+ if (child->parent() != q->viewport())
+ child->setParent(q->viewport(), child->windowFlags());
+ childWindows.append(QPointer<QMdiSubWindow>(child));
+
+ if (!child->testAttribute(Qt::WA_Resized) && q->isVisible()) {
+ QSize newSize(child->sizeHint().boundedTo(q->viewport()->size()));
+ child->resize(newSize.expandedTo(qSmartMinSize(child)));
+ }
+
+ if (!placer)
+ placer = new MinOverlapPlacer;
+ place(placer, child);
+
+ if (hbarpolicy != Qt::ScrollBarAlwaysOff)
+ child->setOption(QMdiSubWindow::AllowOutsideAreaHorizontally, true);
+ else
+ child->setOption(QMdiSubWindow::AllowOutsideAreaHorizontally, false);
+
+ if (vbarpolicy != Qt::ScrollBarAlwaysOff)
+ child->setOption(QMdiSubWindow::AllowOutsideAreaVertically, true);
+ else
+ child->setOption(QMdiSubWindow::AllowOutsideAreaVertically, false);
+
+ internalRaise(child);
+ indicesToActivatedChildren.prepend(childWindows.size() - 1);
+ Q_ASSERT(indicesToActivatedChildren.size() == childWindows.size());
+
+#ifndef QT_NO_TABBAR
+ if (tabBar) {
+ tabBar->addTab(child->windowIcon(), tabTextFor(child));
+ updateTabBarGeometry();
+ if (childWindows.count() == 1 && !(options & QMdiArea::DontMaximizeSubWindowOnActivation))
+ showActiveWindowMaximized = true;
+ }
+#endif
+
+ if (!(child->windowFlags() & Qt::SubWindow))
+ child->setWindowFlags(Qt::SubWindow);
+ child->installEventFilter(q);
+
+ QObject::connect(child, SIGNAL(aboutToActivate()), q, SLOT(_q_deactivateAllWindows()));
+ QObject::connect(child, SIGNAL(windowStateChanged(Qt::WindowStates, Qt::WindowStates)),
+ q, SLOT(_q_processWindowStateChanged(Qt::WindowStates, Qt::WindowStates)));
+}
+
+/*!
+ \internal
+*/
+void QMdiAreaPrivate::place(Placer *placer, QMdiSubWindow *child)
+{
+ if (!placer || !child)
+ return;
+
+ Q_Q(QMdiArea);
+ if (!q->isVisible()) {
+ // The window is only laid out when it's added to QMdiArea,
+ // so there's no need to check that we don't have it in the
+ // list already. appendChild() ensures that.
+ pendingPlacements.append(child);
+ return;
+ }
+
+ QList<QRect> rects;
+ QRect parentRect = q->rect();
+ foreach (QMdiSubWindow *window, childWindows) {
+ if (!sanityCheck(window, "QMdiArea::place") || window == child || !window->isVisibleTo(q)
+ || !window->testAttribute(Qt::WA_Moved)) {
+ continue;
+ }
+ QRect occupiedGeometry;
+ if (window->isMaximized()) {
+ occupiedGeometry = QRect(window->d_func()->oldGeometry.topLeft(),
+ window->d_func()->restoreSize);
+ } else {
+ occupiedGeometry = window->geometry();
+ }
+ rects.append(QStyle::visualRect(child->layoutDirection(), parentRect, occupiedGeometry));
+ }
+ QPoint newPos = placer->place(child->size(), rects, parentRect);
+ QRect newGeometry = QRect(newPos.x(), newPos.y(), child->width(), child->height());
+ child->setGeometry(QStyle::visualRect(child->layoutDirection(), parentRect, newGeometry));
+}
+
+/*!
+ \internal
+*/
+void QMdiAreaPrivate::rearrange(Rearranger *rearranger)
+{
+ if (!rearranger)
+ return;
+
+ Q_Q(QMdiArea);
+ if (!q->isVisible()) {
+ // Compress if we already have the rearranger in the list.
+ int index = pendingRearrangements.indexOf(rearranger);
+ if (index != -1)
+ pendingRearrangements.move(index, pendingRearrangements.size() - 1);
+ else
+ pendingRearrangements.append(rearranger);
+ return;
+ }
+
+ QList<QWidget *> widgets;
+ const bool reverseList = rearranger->type() == Rearranger::RegularTiler;
+ const QList<QMdiSubWindow *> subWindows = subWindowList(activationOrder, reverseList);
+ QSize minSubWindowSize;
+ foreach (QMdiSubWindow *child, subWindows) {
+ if (!sanityCheck(child, "QMdiArea::rearrange") || !child->isVisible())
+ continue;
+ if (rearranger->type() == Rearranger::IconTiler) {
+ if (child->isMinimized() && !child->isShaded() && !(child->windowFlags() & Qt::FramelessWindowHint))
+ widgets.append(child);
+ } else {
+ if (child->isMinimized() && !child->isShaded())
+ continue;
+ if (child->isMaximized() || child->isShaded())
+ child->showNormal();
+ minSubWindowSize = minSubWindowSize.expandedTo(child->minimumSize())
+ .expandedTo(child->d_func()->internalMinimumSize);
+ widgets.append(child);
+ }
+ }
+
+ if (active && rearranger->type() == Rearranger::RegularTiler) {
+ // Move active window in front if necessary. That's the case if we
+ // have any windows with staysOnTopHint set.
+ int indexToActive = widgets.indexOf((QWidget *)active);
+ if (indexToActive > 0)
+ widgets.move(indexToActive, 0);
+ }
+
+ QRect domain = q->viewport()->rect();
+ if (rearranger->type() == Rearranger::RegularTiler && !widgets.isEmpty())
+ domain = resizeToMinimumTileSize(minSubWindowSize, widgets.count());
+
+ rearranger->rearrange(widgets, domain);
+
+ if (rearranger->type() == Rearranger::RegularTiler && !widgets.isEmpty()) {
+ isSubWindowsTiled = true;
+ updateScrollBars();
+ } else if (rearranger->type() == Rearranger::SimpleCascader) {
+ isSubWindowsTiled = false;
+ }
+}
+
+/*!
+ \internal
+
+ Arranges all minimized windows at the bottom of the workspace.
+*/
+void QMdiAreaPrivate::arrangeMinimizedSubWindows()
+{
+ if (!iconTiler)
+ iconTiler = new IconTiler;
+ rearrange(iconTiler);
+}
+
+/*!
+ \internal
+*/
+void QMdiAreaPrivate::activateWindow(QMdiSubWindow *child)
+{
+ if (childWindows.isEmpty()) {
+ Q_ASSERT(!child);
+ Q_ASSERT(!active);
+ return;
+ }
+
+ if (!child) {
+ if (active) {
+ Q_ASSERT(active->d_func()->isActive);
+ active->d_func()->setActive(false);
+ resetActiveWindow();
+ }
+ return;
+ }
+
+ if (child->isHidden() || child == active)
+ return;
+ child->d_func()->setActive(true);
+}
+
+/*!
+ \internal
+*/
+void QMdiAreaPrivate::activateCurrentWindow()
+{
+ QMdiSubWindow *current = q_func()->currentSubWindow();
+ if (current && !isExplicitlyDeactivated(current)) {
+ current->d_func()->activationEnabled = true;
+ current->d_func()->setActive(true, /*changeFocus=*/false);
+ }
+}
+
+void QMdiAreaPrivate::activateHighlightedWindow()
+{
+ if (indexToHighlighted < 0)
+ return;
+
+ Q_ASSERT(indexToHighlighted < childWindows.size());
+ if (tabToPreviousTimerId != -1)
+ activateWindow(nextVisibleSubWindow(-1, QMdiArea::ActivationHistoryOrder));
+ else
+ activateWindow(childWindows.at(indexToHighlighted));
+#ifndef QT_NO_RUBBERBAND
+ hideRubberBand();
+#endif
+}
+
+/*!
+ \internal
+*/
+void QMdiAreaPrivate::emitWindowActivated(QMdiSubWindow *activeWindow)
+{
+ Q_Q(QMdiArea);
+ Q_ASSERT(activeWindow);
+ if (activeWindow == active)
+ return;
+ Q_ASSERT(activeWindow->d_func()->isActive);
+
+ if (!aboutToBecomeActive)
+ _q_deactivateAllWindows(activeWindow);
+ Q_ASSERT(aboutToBecomeActive);
+
+ // This is true only if 'DontMaximizeSubWindowOnActivation' is disabled
+ // and the previous active window was maximized.
+ if (showActiveWindowMaximized) {
+ if (!activeWindow->isMaximized())
+ activeWindow->showMaximized();
+ showActiveWindowMaximized = false;
+ }
+
+ // Put in front to update activation order.
+ const int indexToActiveWindow = childWindows.indexOf(activeWindow);
+ Q_ASSERT(indexToActiveWindow != -1);
+ const int index = indicesToActivatedChildren.indexOf(indexToActiveWindow);
+ Q_ASSERT(index != -1);
+ indicesToActivatedChildren.move(index, 0);
+ internalRaise(activeWindow);
+
+ if (updatesDisabledByUs) {
+ q->setUpdatesEnabled(true);
+ updatesDisabledByUs = false;
+ }
+
+ Q_ASSERT(aboutToBecomeActive == activeWindow);
+ active = activeWindow;
+ aboutToBecomeActive = 0;
+ Q_ASSERT(active->d_func()->isActive);
+
+#ifndef QT_NO_TABBAR
+ if (tabBar && tabBar->currentIndex() != indexToActiveWindow)
+ tabBar->setCurrentIndex(indexToActiveWindow);
+#endif
+
+ if (active->isMaximized() && scrollBarsEnabled())
+ updateScrollBars();
+
+ emit q->subWindowActivated(active);
+}
+
+/*!
+ \internal
+*/
+void QMdiAreaPrivate::resetActiveWindow(QMdiSubWindow *deactivatedWindow)
+{
+ Q_Q(QMdiArea);
+ if (deactivatedWindow) {
+ if (deactivatedWindow != active)
+ return;
+ active = 0;
+ if ((aboutToBecomeActive || isActivated || lastWindowAboutToBeDestroyed())
+ && !isExplicitlyDeactivated(deactivatedWindow) && !q->window()->isMinimized()) {
+ return;
+ }
+ emit q->subWindowActivated(0);
+ return;
+ }
+
+ if (aboutToBecomeActive)
+ return;
+
+ active = 0;
+ emit q->subWindowActivated(0);
+}
+
+/*!
+ \internal
+*/
+void QMdiAreaPrivate::updateActiveWindow(int removedIndex, bool activeRemoved)
+{
+ Q_ASSERT(indicesToActivatedChildren.size() == childWindows.size());
+
+#ifndef QT_NO_TABBAR
+ if (tabBar && removedIndex >= 0) {
+ tabBar->blockSignals(true);
+ tabBar->removeTab(removedIndex);
+ updateTabBarGeometry();
+ tabBar->blockSignals(false);
+ }
+#endif
+
+ if (childWindows.isEmpty()) {
+ showActiveWindowMaximized = false;
+ resetActiveWindow();
+ return;
+ }
+
+ if (indexToHighlighted >= 0) {
+#ifndef QT_NO_RUBBERBAND
+ // Hide rubber band if highlighted window is removed.
+ if (indexToHighlighted == removedIndex)
+ hideRubberBand();
+ else
+#endif
+ // or update index if necessary.
+ if (indexToHighlighted > removedIndex)
+ --indexToHighlighted;
+ }
+
+ // Update indices list
+ for (int i = 0; i < indicesToActivatedChildren.size(); ++i) {
+ int *index = &indicesToActivatedChildren[i];
+ if (*index > removedIndex)
+ --*index;
+ }
+
+ if (!activeRemoved)
+ return;
+
+ // Activate next window.
+ QMdiSubWindow *next = nextVisibleSubWindow(0, activationOrder, removedIndex);
+ if (next)
+ activateWindow(next);
+}
+
+/*!
+ \internal
+*/
+void QMdiAreaPrivate::updateScrollBars()
+{
+ if (ignoreGeometryChange || !scrollBarsEnabled())
+ return;
+
+ Q_Q(QMdiArea);
+ QSize maxSize = q->maximumViewportSize();
+ QSize hbarExtent = hbar->sizeHint();
+ QSize vbarExtent = vbar->sizeHint();
+
+ if (q->style()->styleHint(QStyle::SH_ScrollView_FrameOnlyAroundContents, 0, q)) {
+ const int doubleFrameWidth = frameWidth * 2;
+ if (hbarpolicy == Qt::ScrollBarAlwaysOn)
+ maxSize.rheight() -= doubleFrameWidth;
+ if (vbarpolicy == Qt::ScrollBarAlwaysOn)
+ maxSize.rwidth() -= doubleFrameWidth;
+ hbarExtent.rheight() += doubleFrameWidth;
+ vbarExtent.rwidth() += doubleFrameWidth;
+ }
+
+ const QRect childrenRect = active && active->isMaximized()
+ ? active->geometry() : viewport->childrenRect();
+ bool useHorizontalScrollBar = useScrollBar(childrenRect, maxSize, Qt::Horizontal);
+ bool useVerticalScrollBar = useScrollBar(childrenRect, maxSize, Qt::Vertical);
+
+ if (useHorizontalScrollBar && !useVerticalScrollBar) {
+ const QSize max = maxSize - QSize(0, hbarExtent.height());
+ useVerticalScrollBar = useScrollBar(childrenRect, max, Qt::Vertical);
+ }
+
+ if (useVerticalScrollBar && !useHorizontalScrollBar) {
+ const QSize max = maxSize - QSize(vbarExtent.width(), 0);
+ useHorizontalScrollBar = useScrollBar(childrenRect, max, Qt::Horizontal);
+ }
+
+ if (useHorizontalScrollBar && hbarpolicy != Qt::ScrollBarAlwaysOn)
+ maxSize.rheight() -= hbarExtent.height();
+ if (useVerticalScrollBar && vbarpolicy != Qt::ScrollBarAlwaysOn)
+ maxSize.rwidth() -= vbarExtent.width();
+
+ QRect viewportRect(QPoint(0, 0), maxSize);
+ const int startX = q->isLeftToRight() ? childrenRect.left() : viewportRect.right()
+ - childrenRect.right();
+
+ // Horizontal scroll bar.
+ if (isSubWindowsTiled && hbar->value() != 0)
+ hbar->setValue(0);
+ const int xOffset = startX + hbar->value();
+ hbar->setRange(qMin(0, xOffset),
+ qMax(0, xOffset + childrenRect.width() - viewportRect.width()));
+ hbar->setPageStep(childrenRect.width());
+ hbar->setSingleStep(childrenRect.width() / 20);
+
+ // Vertical scroll bar.
+ if (isSubWindowsTiled && vbar->value() != 0)
+ vbar->setValue(0);
+ const int yOffset = childrenRect.top() + vbar->value();
+ vbar->setRange(qMin(0, yOffset),
+ qMax(0, yOffset + childrenRect.height() - viewportRect.height()));
+ vbar->setPageStep(childrenRect.height());
+ vbar->setSingleStep(childrenRect.height() / 20);
+}
+
+/*!
+ \internal
+*/
+void QMdiAreaPrivate::internalRaise(QMdiSubWindow *mdiChild) const
+{
+ if (!sanityCheck(mdiChild, "QMdiArea::internalRaise") || childWindows.size() < 2)
+ return;
+
+ QMdiSubWindow *stackUnderChild = 0;
+ if (!windowStaysOnTop(mdiChild)) {
+ foreach (QObject *object, q_func()->viewport()->children()) {
+ QMdiSubWindow *child = qobject_cast<QMdiSubWindow *>(object);
+ if (!child || !childWindows.contains(child))
+ continue;
+ if (!child->isHidden() && windowStaysOnTop(child)) {
+ if (stackUnderChild)
+ child->stackUnder(stackUnderChild);
+ else
+ child->raise();
+ stackUnderChild = child;
+ }
+ }
+ }
+
+ if (stackUnderChild)
+ mdiChild->stackUnder(stackUnderChild);
+ else
+ mdiChild->raise();
+}
+
+QRect QMdiAreaPrivate::resizeToMinimumTileSize(const QSize &minSubWindowSize, int subWindowCount)
+{
+ Q_Q(QMdiArea);
+ if (!minSubWindowSize.isValid() || subWindowCount <= 0)
+ return q->viewport()->rect();
+
+ // Calculate minimum size.
+ const int columns = qMax(qCeil(qSqrt(qreal(subWindowCount))), 1);
+ const int rows = qMax((subWindowCount % columns) ? (subWindowCount / columns + 1)
+ : (subWindowCount / columns), 1);
+ const int minWidth = minSubWindowSize.width() * columns;
+ const int minHeight = minSubWindowSize.height() * rows;
+
+ // Increase area size if necessary. Scroll bars are provided if we're not able
+ // to resize to the minimum size.
+ if (!tileCalledFromResizeEvent) {
+ QWidget *topLevel = q;
+ // Find the topLevel for this area, either a real top-level or a sub-window.
+ while (topLevel && !topLevel->isWindow() && topLevel->windowType() != Qt::SubWindow)
+ topLevel = topLevel->parentWidget();
+ // We don't want sub-subwindows to be placed at the edge, thus add 2 pixels.
+ int minAreaWidth = minWidth + left + right + 2;
+ int minAreaHeight = minHeight + top + bottom + 2;
+ if (q->horizontalScrollBar()->isVisible())
+ minAreaHeight += q->horizontalScrollBar()->height();
+ if (q->verticalScrollBar()->isVisible())
+ minAreaWidth += q->verticalScrollBar()->width();
+ if (q->style()->styleHint(QStyle::SH_ScrollView_FrameOnlyAroundContents, 0, q)) {
+ const int frame = q->style()->pixelMetric(QStyle::PM_DefaultFrameWidth, 0, q);
+ minAreaWidth += 2 * frame;
+ minAreaHeight += 2 * frame;
+ }
+ const QSize diff = QSize(minAreaWidth, minAreaHeight).expandedTo(q->size()) - q->size();
+ topLevel->resize(topLevel->size() + diff);
+ }
+
+ QRect domain = q->viewport()->rect();
+
+ // Adjust domain width and provide horizontal scroll bar.
+ if (domain.width() < minWidth) {
+ domain.setWidth(minWidth);
+ if (q->horizontalScrollBarPolicy() == Qt::ScrollBarAlwaysOff)
+ q->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded);
+ else if (q->horizontalScrollBar()->value() != 0)
+ q->horizontalScrollBar()->setValue(0);
+ }
+ // Adjust domain height and provide vertical scroll bar.
+ if (domain.height() < minHeight) {
+ domain.setHeight(minHeight);
+ if (q->verticalScrollBarPolicy() == Qt::ScrollBarAlwaysOff)
+ q->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
+ else if (q->verticalScrollBar()->value() != 0)
+ q->verticalScrollBar()->setValue(0);
+ }
+ return domain;
+}
+
+/*!
+ \internal
+*/
+bool QMdiAreaPrivate::scrollBarsEnabled() const
+{
+ return hbarpolicy != Qt::ScrollBarAlwaysOff || vbarpolicy != Qt::ScrollBarAlwaysOff;
+}
+
+/*!
+ \internal
+*/
+bool QMdiAreaPrivate::lastWindowAboutToBeDestroyed() const
+{
+ if (childWindows.count() != 1)
+ return false;
+
+ QMdiSubWindow *last = childWindows.at(0);
+ if (!last)
+ return true;
+
+ if (!last->testAttribute(Qt::WA_DeleteOnClose))
+ return false;
+
+ return last->d_func()->data.is_closing;
+}
+
+/*!
+ \internal
+*/
+void QMdiAreaPrivate::setChildActivationEnabled(bool enable, bool onlyNextActivationEvent) const
+{
+ foreach (QMdiSubWindow *subWindow, childWindows) {
+ if (!subWindow || !subWindow->isVisible())
+ continue;
+ if (onlyNextActivationEvent)
+ subWindow->d_func()->ignoreNextActivationEvent = !enable;
+ else
+ subWindow->d_func()->activationEnabled = enable;
+ }
+}
+
+/*!
+ \internal
+ \reimp
+*/
+void QMdiAreaPrivate::scrollBarPolicyChanged(Qt::Orientation orientation, Qt::ScrollBarPolicy policy)
+{
+ if (childWindows.isEmpty())
+ return;
+
+ const QMdiSubWindow::SubWindowOption option = orientation == Qt::Horizontal ?
+ QMdiSubWindow::AllowOutsideAreaHorizontally : QMdiSubWindow::AllowOutsideAreaVertically;
+ const bool enable = policy != Qt::ScrollBarAlwaysOff;
+ foreach (QMdiSubWindow *child, childWindows) {
+ if (!sanityCheck(child, "QMdiArea::scrollBarPolicyChanged"))
+ continue;
+ child->setOption(option, enable);
+ }
+ updateScrollBars();
+}
+
+QList<QMdiSubWindow*>
+QMdiAreaPrivate::subWindowList(QMdiArea::WindowOrder order, bool reversed) const
+{
+ QList<QMdiSubWindow *> list;
+ if (childWindows.isEmpty())
+ return list;
+
+ if (order == QMdiArea::CreationOrder) {
+ foreach (QMdiSubWindow *child, childWindows) {
+ if (!child)
+ continue;
+ if (!reversed)
+ list.append(child);
+ else
+ list.prepend(child);
+ }
+ } else if (order == QMdiArea::StackingOrder) {
+ foreach (QObject *object, viewport->children()) {
+ QMdiSubWindow *child = qobject_cast<QMdiSubWindow *>(object);
+ if (!child || !childWindows.contains(child))
+ continue;
+ if (!reversed)
+ list.append(child);
+ else
+ list.prepend(child);
+ }
+ } else { // ActivationHistoryOrder
+ Q_ASSERT(indicesToActivatedChildren.size() == childWindows.size());
+ for (int i = indicesToActivatedChildren.count() - 1; i >= 0; --i) {
+ QMdiSubWindow *child = childWindows.at(indicesToActivatedChildren.at(i));
+ if (!child)
+ continue;
+ if (!reversed)
+ list.append(child);
+ else
+ list.prepend(child);
+ }
+ }
+ return list;
+}
+
+/*!
+ \internal
+*/
+void QMdiAreaPrivate::disconnectSubWindow(QObject *subWindow)
+{
+ if (!subWindow)
+ return;
+
+ Q_Q(QMdiArea);
+ QObject::disconnect(subWindow, 0, q, 0);
+ subWindow->removeEventFilter(q);
+}
+
+/*!
+ \internal
+*/
+QMdiSubWindow *QMdiAreaPrivate::nextVisibleSubWindow(int increaseFactor, QMdiArea::WindowOrder order,
+ int removedIndex, int fromIndex) const
+{
+ if (childWindows.isEmpty())
+ return 0;
+
+ Q_Q(const QMdiArea);
+ const QList<QMdiSubWindow *> subWindows = q->subWindowList(order);
+ QMdiSubWindow *current = 0;
+
+ if (removedIndex < 0) {
+ if (fromIndex >= 0 && fromIndex < subWindows.size())
+ current = childWindows.at(fromIndex);
+ else
+ current = q->currentSubWindow();
+ }
+
+ // There's no current sub-window (removed or deactivated),
+ // so we have to pick the last active or the next in creation order.
+ if (!current) {
+ if (removedIndex >= 0 && order == QMdiArea::CreationOrder) {
+ int candidateIndex = -1;
+ setIndex(&candidateIndex, removedIndex, 0, subWindows.size() - 1, true);
+ current = childWindows.at(candidateIndex);
+ } else {
+ current = subWindows.back();
+ }
+ }
+ Q_ASSERT(current);
+
+ // Find the index for the current sub-window in the given activation order
+ const int indexToCurrent = subWindows.indexOf(current);
+ const bool increasing = increaseFactor > 0 ? true : false;
+
+ // and use that index + increseFactor as a candidate.
+ int index = -1;
+ setIndex(&index, indexToCurrent + increaseFactor, 0, subWindows.size() - 1, increasing);
+ Q_ASSERT(index != -1);
+
+ // Try to find another window if the candidate is hidden.
+ while (subWindows.at(index)->isHidden()) {
+ setIndex(&index, index + increaseFactor, 0, subWindows.size() - 1, increasing);
+ if (index == indexToCurrent)
+ break;
+ }
+
+ if (!subWindows.at(index)->isHidden())
+ return subWindows.at(index);
+ return 0;
+}
+
+/*!
+ \internal
+*/
+void QMdiAreaPrivate::highlightNextSubWindow(int increaseFactor)
+{
+ if (childWindows.size() == 1)
+ return;
+
+ Q_Q(QMdiArea);
+ // There's no highlighted sub-window atm, use current.
+ if (indexToHighlighted < 0) {
+ QMdiSubWindow *current = q->currentSubWindow();
+ if (!current)
+ return;
+ indexToHighlighted = childWindows.indexOf(current);
+ }
+
+ Q_ASSERT(indexToHighlighted >= 0);
+ Q_ASSERT(indexToHighlighted < childWindows.size());
+
+ QMdiSubWindow *highlight = nextVisibleSubWindow(increaseFactor, activationOrder, -1, indexToHighlighted);
+ if (!highlight)
+ return;
+
+#ifndef QT_NO_RUBBERBAND
+ if (!rubberBand) {
+ rubberBand = new QRubberBand(QRubberBand::Rectangle, viewport);
+ // For accessibility to identify this special widget.
+ rubberBand->setObjectName(QLatin1String("qt_rubberband"));
+ rubberBand->setWindowFlags(rubberBand->windowFlags() | Qt::WindowStaysOnTopHint);
+ }
+#endif
+
+ // Only highlight if we're not switching back to the previously active window (Ctrl-Tab once).
+#ifndef QT_NO_RUBBERBAND
+ if (tabToPreviousTimerId == -1)
+ showRubberBandFor(highlight);
+#endif
+
+ indexToHighlighted = childWindows.indexOf(highlight);
+ Q_ASSERT(indexToHighlighted >= 0);
+}
+
+/*!
+ \internal
+ \since 4.4
+*/
+void QMdiAreaPrivate::setViewMode(QMdiArea::ViewMode mode)
+{
+ Q_Q(QMdiArea);
+ if (viewMode == mode || inViewModeChange)
+ return;
+
+ // Just a guard since we cannot set viewMode = mode here.
+ inViewModeChange = true;
+
+#ifndef QT_NO_TABBAR
+ if (mode == QMdiArea::TabbedView) {
+ Q_ASSERT(!tabBar);
+ tabBar = new QMdiAreaTabBar(q);
+ tabBar->setDocumentMode(documentMode);
+#ifndef QT_NO_TABWIDGET
+ tabBar->setShape(tabBarShapeFrom(tabShape, tabPosition));
+#endif
+
+ isSubWindowsTiled = false;
+
+ foreach (QMdiSubWindow *subWindow, childWindows)
+ tabBar->addTab(subWindow->windowIcon(), tabTextFor(subWindow));
+
+ QMdiSubWindow *current = q->currentSubWindow();
+ if (current) {
+ tabBar->setCurrentIndex(childWindows.indexOf(current));
+ // Restore sub-window (i.e. cleanup buttons in menu bar and window title).
+ if (current->isMaximized())
+ current->showNormal();
+
+ viewMode = mode;
+
+ // Now, maximize it.
+ if (!q->testOption(QMdiArea::DontMaximizeSubWindowOnActivation)) {
+ current->showMaximized();
+ }
+ } else {
+ viewMode = mode;
+ }
+
+ if (q->isVisible())
+ tabBar->show();
+ updateTabBarGeometry();
+
+ QObject::connect(tabBar, SIGNAL(currentChanged(int)), q, SLOT(_q_currentTabChanged(int)));
+ } else
+#endif // QT_NO_TABBAR
+ { // SubWindowView
+#ifndef QT_NO_TABBAR
+ delete tabBar;
+ tabBar = 0;
+#endif // QT_NO_TABBAR
+
+ viewMode = mode;
+ q->setViewportMargins(0, 0, 0, 0);
+ indexToLastActiveTab = -1;
+
+ QMdiSubWindow *current = q->currentSubWindow();
+ if (current && current->isMaximized())
+ current->showNormal();
+ }
+
+ Q_ASSERT(viewMode == mode);
+ inViewModeChange = false;
+}
+
+#ifndef QT_NO_TABBAR
+/*!
+ \internal
+*/
+void QMdiAreaPrivate::updateTabBarGeometry()
+{
+ if (!tabBar)
+ return;
+
+ Q_Q(QMdiArea);
+#ifndef QT_NO_TABWIDGET
+ Q_ASSERT(tabBarShapeFrom(tabShape, tabPosition) == tabBar->shape());
+#endif
+ const QSize tabBarSizeHint = tabBar->sizeHint();
+
+ int areaHeight = q->height();
+ if (hbar && hbar->isVisible())
+ areaHeight -= hbar->height();
+
+ int areaWidth = q->width();
+ if (vbar && vbar->isVisible())
+ areaWidth -= vbar->width();
+
+ QRect tabBarRect;
+#ifndef QT_NO_TABWIDGET
+ switch (tabPosition) {
+ case QTabWidget::North:
+ q->setViewportMargins(0, tabBarSizeHint.height(), 0, 0);
+ tabBarRect = QRect(0, 0, areaWidth, tabBarSizeHint.height());
+ break;
+ case QTabWidget::South:
+ q->setViewportMargins(0, 0, 0, tabBarSizeHint.height());
+ tabBarRect = QRect(0, areaHeight - tabBarSizeHint.height(), areaWidth, tabBarSizeHint.height());
+ break;
+ case QTabWidget::East:
+ if (q->layoutDirection() == Qt::LeftToRight)
+ q->setViewportMargins(0, 0, tabBarSizeHint.width(), 0);
+ else
+ q->setViewportMargins(tabBarSizeHint.width(), 0, 0, 0);
+ tabBarRect = QRect(areaWidth - tabBarSizeHint.width(), 0, tabBarSizeHint.width(), areaHeight);
+ break;
+ case QTabWidget::West:
+ if (q->layoutDirection() == Qt::LeftToRight)
+ q->setViewportMargins(tabBarSizeHint.width(), 0, 0, 0);
+ else
+ q->setViewportMargins(0, 0, tabBarSizeHint.width(), 0);
+ tabBarRect = QRect(0, 0, tabBarSizeHint.width(), areaHeight);
+ break;
+ default:
+ break;
+ }
+#endif // QT_NO_TABWIDGET
+
+ tabBar->setGeometry(QStyle::visualRect(q->layoutDirection(), q->contentsRect(), tabBarRect));
+}
+
+/*!
+ \internal
+*/
+void QMdiAreaPrivate::refreshTabBar()
+{
+ if (!tabBar)
+ return;
+
+ tabBar->setDocumentMode(documentMode);
+#ifndef QT_NO_TABWIDGET
+ tabBar->setShape(tabBarShapeFrom(tabShape, tabPosition));
+#endif
+ updateTabBarGeometry();
+}
+#endif // QT_NO_TABBAR
+
+/*!
+ Constructs an empty mdi area. \a parent is passed to QWidget's
+ constructor.
+*/
+QMdiArea::QMdiArea(QWidget *parent)
+ : QAbstractScrollArea(*new QMdiAreaPrivate, parent)
+{
+ setBackground(palette().brush(QPalette::Dark));
+ setFrameStyle(QFrame::NoFrame);
+ setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
+ setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
+ setViewport(0);
+ setFocusPolicy(Qt::NoFocus);
+ QApplication::instance()->installEventFilter(this);
+}
+
+/*!
+ Destroys the MDI area.
+*/
+QMdiArea::~QMdiArea()
+{
+ Q_D(QMdiArea);
+ delete d->cascader;
+ d->cascader = 0;
+
+ delete d->regularTiler;
+ d->regularTiler = 0;
+
+ delete d->iconTiler;
+ d->iconTiler = 0;
+
+ delete d->placer;
+ d->placer = 0;
+}
+
+/*!
+ \reimp
+*/
+QSize QMdiArea::sizeHint() const
+{
+ // Calculate a proper scale factor for QDesktopWidget::size().
+ // This also takes into account that we can have nested workspaces.
+ int nestedCount = 0;
+ QWidget *widget = this->parentWidget();
+ while (widget) {
+ if (qobject_cast<QMdiArea *>(widget))
+ ++nestedCount;
+ widget = widget->parentWidget();
+ }
+ const int scaleFactor = 3 * (nestedCount + 1);
+
+ QSize desktopSize = QApplication::desktop()->size();
+ QSize size(desktopSize.width() * 2 / scaleFactor, desktopSize.height() * 2 / scaleFactor);
+ foreach (QMdiSubWindow *child, d_func()->childWindows) {
+ if (!sanityCheck(child, "QMdiArea::sizeHint"))
+ continue;
+ size = size.expandedTo(child->sizeHint());
+ }
+ return size.expandedTo(QApplication::globalStrut());
+}
+
+/*!
+ \reimp
+*/
+QSize QMdiArea::minimumSizeHint() const
+{
+ Q_D(const QMdiArea);
+ QSize size(style()->pixelMetric(QStyle::PM_MdiSubWindowMinimizedWidth, 0, this),
+ style()->pixelMetric(QStyle::PM_TitleBarHeight, 0, this));
+ size = size.expandedTo(QAbstractScrollArea::minimumSizeHint());
+ if (!d->scrollBarsEnabled()) {
+ foreach (QMdiSubWindow *child, d->childWindows) {
+ if (!sanityCheck(child, "QMdiArea::sizeHint"))
+ continue;
+ size = size.expandedTo(child->minimumSizeHint());
+ }
+ }
+ return size.expandedTo(QApplication::globalStrut());
+}
+
+/*!
+ Returns a pointer to the current subwindow, or 0 if there is
+ no current subwindow.
+
+ This function will return the same as activeSubWindow() if
+ the QApplication containing QMdiArea is active.
+
+ \sa activeSubWindow(), QApplication::activeWindow()
+*/
+QMdiSubWindow *QMdiArea::currentSubWindow() const
+{
+ Q_D(const QMdiArea);
+ if (d->childWindows.isEmpty())
+ return 0;
+
+ if (d->active)
+ return d->active;
+
+ if (d->isActivated && !window()->isMinimized())
+ return 0;
+
+ Q_ASSERT(d->indicesToActivatedChildren.count() > 0);
+ int index = d->indicesToActivatedChildren.at(0);
+ Q_ASSERT(index >= 0 && index < d->childWindows.size());
+ QMdiSubWindow *current = d->childWindows.at(index);
+ Q_ASSERT(current);
+ return current;
+}
+
+/*!
+ Returns a pointer to the current active subwindow. If no
+ window is currently active, 0 is returned.
+
+ Subwindows are treated as top-level windows with respect to
+ window state, i.e., if a widget outside the MDI area is the active
+ window, no subwindow will be active. Note that if a widget in the
+ window in which the MDI area lives gains focus, the window will be
+ activated.
+
+ \sa setActiveSubWindow(), Qt::WindowState
+*/
+QMdiSubWindow *QMdiArea::activeSubWindow() const
+{
+ Q_D(const QMdiArea);
+ return d->active;
+}
+
+/*!
+ Activates the subwindow \a window. If \a window is 0, any
+ current active window is deactivated.
+
+ \sa activeSubWindow()
+*/
+void QMdiArea::setActiveSubWindow(QMdiSubWindow *window)
+{
+ Q_D(QMdiArea);
+ if (!window) {
+ d->activateWindow(0);
+ return;
+ }
+
+ if (d->childWindows.isEmpty()) {
+ qWarning("QMdiArea::setActiveSubWindow: workspace is empty");
+ return;
+ }
+
+ if (d->childWindows.indexOf(window) == -1) {
+ qWarning("QMdiArea::setActiveSubWindow: window is not inside workspace");
+ return;
+ }
+
+ d->activateWindow(window);
+}
+
+/*!
+ Closes the active subwindow.
+
+ \sa closeAllSubWindows()
+*/
+void QMdiArea::closeActiveSubWindow()
+{
+ Q_D(QMdiArea);
+ if (d->active)
+ d->active->close();
+}
+
+/*!
+ Returns a list of all subwindows in the MDI area. If \a order is
+ CreationOrder (the default), the windows are sorted in the order
+ in which they were inserted into the workspace. If \a order is
+ StackingOrder, the windows are listed in their stacking order,
+ with the topmost window as the last item in the list. If \a order
+ is ActivationHistoryOrder, the windows are listed according to
+ their recent activation history.
+
+ \sa WindowOrder
+*/
+QList<QMdiSubWindow *> QMdiArea::subWindowList(WindowOrder order) const
+{
+ Q_D(const QMdiArea);
+ return d->subWindowList(order, false);
+}
+
+/*!
+ Closes all subwindows by sending a QCloseEvent to each window.
+ You may receive subWindowActivated() signals from subwindows
+ before they are closed (if the MDI area activates the subwindow
+ when another is closing).
+
+ Subwindows that ignore the close event will remain open.
+
+ \sa closeActiveSubWindow()
+*/
+void QMdiArea::closeAllSubWindows()
+{
+ Q_D(QMdiArea);
+ if (d->childWindows.isEmpty())
+ return;
+
+ d->isSubWindowsTiled = false;
+ foreach (QMdiSubWindow *child, d->childWindows) {
+ if (!sanityCheck(child, "QMdiArea::closeAllSubWindows"))
+ continue;
+ child->close();
+ }
+
+ d->updateScrollBars();
+}
+
+/*!
+ Gives the keyboard focus to the next window in the list of child
+ windows. The windows are activated in the order in which they are
+ created (CreationOrder).
+
+ \sa activatePreviousSubWindow()
+*/
+void QMdiArea::activateNextSubWindow()
+{
+ Q_D(QMdiArea);
+ if (d->childWindows.isEmpty())
+ return;
+
+ QMdiSubWindow *next = d->nextVisibleSubWindow(1, d->activationOrder);
+ if (next)
+ d->activateWindow(next);
+}
+
+/*!
+ Gives the keyboard focus to the previous window in the list of
+ child windows. The windows are activated in the order in which
+ they are created (CreationOrder).
+
+ \sa activateNextSubWindow()
+*/
+void QMdiArea::activatePreviousSubWindow()
+{
+ Q_D(QMdiArea);
+ if (d->childWindows.isEmpty())
+ return;
+
+ QMdiSubWindow *previous = d->nextVisibleSubWindow(-1, d->activationOrder);
+ if (previous)
+ d->activateWindow(previous);
+}
+
+/*!
+ Adds \a widget as a new subwindow to the MDI area. If \a
+ windowFlags are non-zero, they will override the flags set on the
+ widget.
+
+ The \a widget can be either a QMdiSubWindow or another QWidget
+ (in which case the MDI area will create a subwindow and set the \a
+ widget as the internal widget).
+
+ \note Once the subwindow has been added, its parent will be the
+ \e{viewport widget} of the QMdiArea.
+
+ \snippet doc/src/snippets/mdiareasnippets.cpp 1
+
+ When you create your own subwindow, you must set the
+ Qt::WA_DeleteOnClose widget attribute if you want the window to be
+ deleted when closed in the MDI area. If not, the window will be
+ hidden and the MDI area will not activate the next subwindow.
+
+ Returns the QMdiSubWindow that is added to the MDI area.
+
+ \sa removeSubWindow()
+*/
+QMdiSubWindow *QMdiArea::addSubWindow(QWidget *widget, Qt::WindowFlags windowFlags)
+{
+ if (!widget) {
+ qWarning("QMdiArea::addSubWindow: null pointer to widget");
+ return 0;
+ }
+
+ Q_D(QMdiArea);
+ // QWidget::setParent clears focusWidget so store it
+ QWidget *childFocus = widget->focusWidget();
+ QMdiSubWindow *child = qobject_cast<QMdiSubWindow *>(widget);
+
+ // Widget is already a QMdiSubWindow
+ if (child) {
+ if (d->childWindows.indexOf(child) != -1) {
+ qWarning("QMdiArea::addSubWindow: window is already added");
+ return child;
+ }
+ child->setParent(viewport(), windowFlags ? windowFlags : child->windowFlags());
+ // Create a QMdiSubWindow
+ } else {
+ child = new QMdiSubWindow(viewport(), windowFlags);
+ child->setAttribute(Qt::WA_DeleteOnClose);
+ child->setWidget(widget);
+ Q_ASSERT(child->testAttribute(Qt::WA_DeleteOnClose));
+ }
+
+ if (childFocus)
+ childFocus->setFocus();
+ d->appendChild(child);
+ return child;
+}
+
+/*!
+ Removes \a widget from the MDI area. The \a widget must be
+ either a QMdiSubWindow or a widget that is the internal widget of
+ a subwindow. Note that the subwindow is not deleted by QMdiArea
+ and that its parent is set to 0.
+
+ \sa addSubWindow()
+*/
+void QMdiArea::removeSubWindow(QWidget *widget)
+{
+ if (!widget) {
+ qWarning("QMdiArea::removeSubWindow: null pointer to widget");
+ return;
+ }
+
+ Q_D(QMdiArea);
+ if (d->childWindows.isEmpty())
+ return;
+
+ if (QMdiSubWindow *child = qobject_cast<QMdiSubWindow *>(widget)) {
+ int index = d->childWindows.indexOf(child);
+ if (index == -1) {
+ qWarning("QMdiArea::removeSubWindow: window is not inside workspace");
+ return;
+ }
+ d->disconnectSubWindow(child);
+ d->childWindows.removeAll(child);
+ d->indicesToActivatedChildren.removeAll(index);
+ d->updateActiveWindow(index, d->active == child);
+ child->setParent(0);
+ return;
+ }
+
+ bool found = false;
+ foreach (QMdiSubWindow *child, d->childWindows) {
+ if (!sanityCheck(child, "QMdiArea::removeSubWindow"))
+ continue;
+ if (child->widget() == widget) {
+ child->setWidget(0);
+ Q_ASSERT(!child->widget());
+ found = true;
+ break;
+ }
+ }
+
+ if (!found)
+ qWarning("QMdiArea::removeSubWindow: widget is not child of any window inside QMdiArea");
+}
+
+/*!
+ \property QMdiArea::background
+ \brief the background brush for the workspace
+
+ This property sets the background brush for the workspace area
+ itself. By default, it is a gray color, but can be any brush
+ (e.g., colors, gradients or pixmaps).
+*/
+QBrush QMdiArea::background() const
+{
+ return d_func()->background;
+}
+
+void QMdiArea::setBackground(const QBrush &brush)
+{
+ Q_D(QMdiArea);
+ if (d->background != brush) {
+ d->background = brush;
+ d->viewport->setAttribute(Qt::WA_OpaquePaintEvent, brush.isOpaque());
+ update();
+ }
+}
+
+
+/*!
+ \property QMdiArea::activationOrder
+ \brief the ordering criteria for subwindow lists
+ \since 4.4
+
+ This property specifies the ordering criteria for the list of
+ subwindows returned by subWindowList(). By default, it is the window
+ creation order.
+
+ \sa subWindowList()
+*/
+QMdiArea::WindowOrder QMdiArea::activationOrder() const
+{
+ Q_D(const QMdiArea);
+ return d->activationOrder;
+}
+
+void QMdiArea::setActivationOrder(WindowOrder order)
+{
+ Q_D(QMdiArea);
+ if (order != d->activationOrder)
+ d->activationOrder = order;
+}
+
+/*!
+ If \a on is true, \a option is enabled on the MDI area; otherwise
+ it is disabled. See AreaOption for the effect of each option.
+
+ \sa AreaOption, testOption()
+*/
+void QMdiArea::setOption(AreaOption option, bool on)
+{
+ Q_D(QMdiArea);
+ if (on && !(d->options & option))
+ d->options |= option;
+ else if (!on && (d->options & option))
+ d->options &= ~option;
+}
+
+/*!
+ Returns true if \a option is enabled; otherwise returns false.
+
+ \sa AreaOption, setOption()
+*/
+bool QMdiArea::testOption(AreaOption option) const
+{
+ return d_func()->options & option;
+}
+
+/*!
+ \property QMdiArea::viewMode
+ \brief the way sub-windows are displayed in the QMdiArea.
+ \since 4.4
+
+ By default, the SubWindowView is used to display sub-windows.
+
+ \sa ViewMode, setTabShape(), setTabPosition()
+*/
+QMdiArea::ViewMode QMdiArea::viewMode() const
+{
+ Q_D(const QMdiArea);
+ return d->viewMode;
+}
+
+void QMdiArea::setViewMode(ViewMode mode)
+{
+ Q_D(QMdiArea);
+ d->setViewMode(mode);
+}
+
+#ifndef QT_NO_TABBAR
+/*!
+ \property QMdiArea::documentMode
+ \brief whether the tab bar is set to document mode in tabbed view mode.
+ \since 4.5
+
+ Document mode is disabled by default.
+
+ \sa QTabBar::documentMode, setViewMode()
+*/
+bool QMdiArea::documentMode() const
+{
+ Q_D(const QMdiArea);
+ return d->documentMode;
+}
+
+void QMdiArea::setDocumentMode(bool enabled)
+{
+ Q_D(QMdiArea);
+ if (d->documentMode == enabled)
+ return;
+
+ d->documentMode = enabled;
+ d->refreshTabBar();
+}
+#endif // QT_NO_TABBAR
+
+#ifndef QT_NO_TABWIDGET
+/*!
+ \property QMdiArea::tabShape
+ \brief the shape of the tabs in tabbed view mode.
+ \since 4.4
+
+ Possible values for this property are QTabWidget::Rounded
+ (default) or QTabWidget::Triangular.
+
+ \sa QTabWidget::TabShape, setViewMode()
+*/
+QTabWidget::TabShape QMdiArea::tabShape() const
+{
+ Q_D(const QMdiArea);
+ return d->tabShape;
+}
+
+void QMdiArea::setTabShape(QTabWidget::TabShape shape)
+{
+ Q_D(QMdiArea);
+ if (d->tabShape == shape)
+ return;
+
+ d->tabShape = shape;
+ d->refreshTabBar();
+}
+
+/*!
+ \property QMdiArea::tabPosition
+ \brief the position of the tabs in tabbed view mode.
+ \since 4.4
+
+ Possible values for this property are described by the
+ QTabWidget::TabPosition enum.
+
+ \sa QTabWidget::TabPosition, setViewMode()
+*/
+QTabWidget::TabPosition QMdiArea::tabPosition() const
+{
+ Q_D(const QMdiArea);
+ return d->tabPosition;
+}
+
+void QMdiArea::setTabPosition(QTabWidget::TabPosition position)
+{
+ Q_D(QMdiArea);
+ if (d->tabPosition == position)
+ return;
+
+ d->tabPosition = position;
+ d->refreshTabBar();
+}
+#endif // QT_NO_TABWIDGET
+
+/*!
+ \reimp
+*/
+void QMdiArea::childEvent(QChildEvent *childEvent)
+{
+ Q_D(QMdiArea);
+ if (childEvent->type() == QEvent::ChildPolished) {
+ if (QMdiSubWindow *mdiChild = qobject_cast<QMdiSubWindow *>(childEvent->child())) {
+ if (d->childWindows.indexOf(mdiChild) == -1)
+ d->appendChild(mdiChild);
+ }
+ }
+}
+
+/*!
+ \reimp
+*/
+void QMdiArea::resizeEvent(QResizeEvent *resizeEvent)
+{
+ Q_D(QMdiArea);
+ if (d->childWindows.isEmpty()) {
+ resizeEvent->ignore();
+ return;
+ }
+
+#ifndef QT_NO_TABBAR
+ d->updateTabBarGeometry();
+#endif
+
+ // Re-tile the views if we're in tiled mode. Re-tile means we will change
+ // the geometry of the children, which in turn means 'isSubWindowsTiled'
+ // is set to false, so we have to update the state at the end.
+ if (d->isSubWindowsTiled) {
+ d->tileCalledFromResizeEvent = true;
+ tileSubWindows();
+ d->tileCalledFromResizeEvent = false;
+ d->isSubWindowsTiled = true;
+ d->startResizeTimer();
+ // We don't have scroll bars or any maximized views.
+ return;
+ }
+
+ // Resize maximized views.
+ bool hasMaximizedSubWindow = false;
+ foreach (QMdiSubWindow *child, d->childWindows) {
+ if (sanityCheck(child, "QMdiArea::resizeEvent") && child->isMaximized()
+ && child->size() != resizeEvent->size()) {
+ child->resize(resizeEvent->size());
+ if (!hasMaximizedSubWindow)
+ hasMaximizedSubWindow = true;
+ }
+ }
+
+ d->updateScrollBars();
+
+ // Minimized views are stacked under maximized views so there's
+ // no need to re-arrange minimized views on-demand. Start a timer
+ // just to make things faster with subsequent resize events.
+ if (hasMaximizedSubWindow)
+ d->startResizeTimer();
+ else
+ d->arrangeMinimizedSubWindows();
+}
+
+/*!
+ \reimp
+*/
+void QMdiArea::timerEvent(QTimerEvent *timerEvent)
+{
+ Q_D(QMdiArea);
+ if (timerEvent->timerId() == d->resizeTimerId) {
+ killTimer(d->resizeTimerId);
+ d->resizeTimerId = -1;
+ d->arrangeMinimizedSubWindows();
+ } else if (timerEvent->timerId() == d->tabToPreviousTimerId) {
+ killTimer(d->tabToPreviousTimerId);
+ d->tabToPreviousTimerId = -1;
+ if (d->indexToHighlighted < 0)
+ return;
+#ifndef QT_NO_RUBBERBAND
+ // We're not doing a "quick switch" ... show rubber band.
+ Q_ASSERT(d->indexToHighlighted < d->childWindows.size());
+ Q_ASSERT(d->rubberBand);
+ d->showRubberBandFor(d->childWindows.at(d->indexToHighlighted));
+#endif
+ }
+}
+
+/*!
+ \reimp
+*/
+void QMdiArea::showEvent(QShowEvent *showEvent)
+{
+ Q_D(QMdiArea);
+ if (!d->pendingRearrangements.isEmpty()) {
+ bool skipPlacement = false;
+ foreach (Rearranger *rearranger, d->pendingRearrangements) {
+ // If this is the case, we don't have to lay out pending child windows
+ // since the rearranger will find a placement for them.
+ if (rearranger->type() != Rearranger::IconTiler && !skipPlacement)
+ skipPlacement = true;
+ d->rearrange(rearranger);
+ }
+ d->pendingRearrangements.clear();
+
+ if (skipPlacement && !d->pendingPlacements.isEmpty())
+ d->pendingPlacements.clear();
+ }
+
+ if (!d->pendingPlacements.isEmpty()) {
+ foreach (QMdiSubWindow *window, d->pendingPlacements) {
+ if (!window)
+ continue;
+ if (!window->testAttribute(Qt::WA_Resized)) {
+ QSize newSize(window->sizeHint().boundedTo(viewport()->size()));
+ window->resize(newSize.expandedTo(qSmartMinSize(window)));
+ }
+ if (!window->testAttribute(Qt::WA_Moved) && !window->isMinimized()
+ && !window->isMaximized()) {
+ d->place(d->placer, window);
+ }
+ }
+ d->pendingPlacements.clear();
+ }
+
+ d->setChildActivationEnabled(true);
+ d->activateCurrentWindow();
+
+ QAbstractScrollArea::showEvent(showEvent);
+}
+
+/*!
+ \reimp
+*/
+bool QMdiArea::viewportEvent(QEvent *event)
+{
+ Q_D(QMdiArea);
+ switch (event->type()) {
+ case QEvent::ChildRemoved: {
+ d->isSubWindowsTiled = false;
+ QObject *removedChild = static_cast<QChildEvent *>(event)->child();
+ for (int i = 0; i < d->childWindows.size(); ++i) {
+ QObject *child = d->childWindows.at(i);
+ if (!child || child == removedChild || !child->parent()
+ || child->parent() != viewport()) {
+ if (!testOption(DontMaximizeSubWindowOnActivation)) {
+ // In this case we can only rely on the child being a QObject
+ // (or 0), but let's try and see if we can get more information.
+ QWidget *mdiChild = qobject_cast<QWidget *>(removedChild);
+ if (mdiChild && mdiChild->isMaximized())
+ d->showActiveWindowMaximized = true;
+ }
+ d->disconnectSubWindow(child);
+ const bool activeRemoved = i == d->indicesToActivatedChildren.at(0);
+ d->childWindows.removeAt(i);
+ d->indicesToActivatedChildren.removeAll(i);
+ d->updateActiveWindow(i, activeRemoved);
+ d->arrangeMinimizedSubWindows();
+ break;
+ }
+ }
+ d->updateScrollBars();
+ break;
+ }
+ case QEvent::Destroy:
+ d->isSubWindowsTiled = false;
+ d->resetActiveWindow();
+ d->childWindows.clear();
+ qWarning("QMdiArea: Deleting the view port is undefined, use setViewport instead.");
+ break;
+ default:
+ break;
+ }
+ return QAbstractScrollArea::viewportEvent(event);
+}
+
+/*!
+ \reimp
+*/
+void QMdiArea::scrollContentsBy(int dx, int dy)
+{
+ Q_D(QMdiArea);
+ const bool wasSubWindowsTiled = d->isSubWindowsTiled;
+ d->ignoreGeometryChange = true;
+ viewport()->scroll(isLeftToRight() ? dx : -dx, dy);
+ d->arrangeMinimizedSubWindows();
+ d->ignoreGeometryChange = false;
+ if (wasSubWindowsTiled)
+ d->isSubWindowsTiled = true;
+}
+
+/*!
+ Arranges all child windows in a tile pattern.
+
+ \sa cascadeSubWindows()
+*/
+void QMdiArea::tileSubWindows()
+{
+ Q_D(QMdiArea);
+ if (!d->regularTiler)
+ d->regularTiler = new RegularTiler;
+ d->rearrange(d->regularTiler);
+}
+
+/*!
+ Arranges all the child windows in a cascade pattern.
+
+ \sa tileSubWindows()
+*/
+void QMdiArea::cascadeSubWindows()
+{
+ Q_D(QMdiArea);
+ if (!d->cascader)
+ d->cascader = new SimpleCascader;
+ d->rearrange(d->cascader);
+}
+
+/*!
+ \reimp
+*/
+bool QMdiArea::event(QEvent *event)
+{
+ Q_D(QMdiArea);
+ switch (event->type()) {
+#ifdef Q_WS_WIN
+ // QWidgetPrivate::hide_helper activates another sub-window when closing a
+ // modal dialog on Windows (see activateWindow() inside the the ifdef).
+ case QEvent::WindowUnblocked:
+ d->activateCurrentWindow();
+ break;
+#endif
+ case QEvent::WindowActivate: {
+ d->isActivated = true;
+ if (d->childWindows.isEmpty())
+ break;
+ if (!d->active)
+ d->activateCurrentWindow();
+ d->setChildActivationEnabled(false, true);
+ break;
+ }
+ case QEvent::WindowDeactivate:
+ d->isActivated = false;
+ d->setChildActivationEnabled(false, true);
+ break;
+ case QEvent::StyleChange:
+ // Re-tile the views if we're in tiled mode. Re-tile means we will change
+ // the geometry of the children, which in turn means 'isSubWindowsTiled'
+ // is set to false, so we have to update the state at the end.
+ if (d->isSubWindowsTiled) {
+ tileSubWindows();
+ d->isSubWindowsTiled = true;
+ }
+ break;
+ case QEvent::WindowIconChange:
+ foreach (QMdiSubWindow *window, d->childWindows) {
+ if (sanityCheck(window, "QMdiArea::WindowIconChange"))
+ QApplication::sendEvent(window, event);
+ }
+ break;
+ case QEvent::Hide:
+ d->setActive(d->active, false, false);
+ d->setChildActivationEnabled(false);
+ break;
+#ifndef QT_NO_TABBAR
+ case QEvent::LayoutDirectionChange:
+ d->updateTabBarGeometry();
+ break;
+#endif
+ default:
+ break;
+ }
+ return QAbstractScrollArea::event(event);
+}
+
+/*!
+ \reimp
+*/
+bool QMdiArea::eventFilter(QObject *object, QEvent *event)
+{
+ if (!object)
+ return QAbstractScrollArea::eventFilter(object, event);
+
+ Q_D(QMdiArea);
+ // Global key events with Ctrl modifier.
+ if (event->type() == QEvent::KeyPress || event->type() == QEvent::KeyRelease) {
+
+ QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
+ // Ingore key events without a Ctrl modifier (except for press/release on the modifier itself).
+#ifdef Q_WS_MAC
+ if (!(keyEvent->modifiers() & Qt::MetaModifier) && keyEvent->key() != Qt::Key_Meta)
+#else
+ if (!(keyEvent->modifiers() & Qt::ControlModifier) && keyEvent->key() != Qt::Key_Control)
+#endif
+ return QAbstractScrollArea::eventFilter(object, event);
+
+ // Find closest mdi area (in case we have a nested workspace).
+ QMdiArea *area = mdiAreaParent(static_cast<QWidget *>(object));
+ if (!area)
+ return QAbstractScrollArea::eventFilter(object, event);
+
+ const bool keyPress = (event->type() == QEvent::KeyPress) ? true : false;
+
+ // 1) Ctrl-Tab once -> activate the previously active window.
+ // 2) Ctrl-Tab (Tab, Tab, ...) -> iterate through all windows (activateNextSubWindow()).
+ // 3) Ctrl-Shift-Tab (Tab, Tab, ...) -> iterate through all windows in the opposite
+ // direction (activatePreviousSubWindow())
+ switch (keyEvent->key()) {
+#ifdef Q_WS_MAC
+ case Qt::Key_Meta:
+#else
+ case Qt::Key_Control:
+#endif
+ if (keyPress)
+ area->d_func()->startTabToPreviousTimer();
+ else
+ area->d_func()->activateHighlightedWindow();
+ break;
+ case Qt::Key_Tab:
+ case Qt::Key_Backtab:
+ if (keyPress)
+ area->d_func()->highlightNextSubWindow(keyEvent->key() == Qt::Key_Tab ? 1 : -1);
+ return true;
+#ifndef QT_NO_RUBBERBAND
+ case Qt::Key_Escape:
+ area->d_func()->hideRubberBand();
+ break;
+#endif
+ default:
+ break;
+ }
+ return QAbstractScrollArea::eventFilter(object, event);
+ }
+
+ QMdiSubWindow *subWindow = qobject_cast<QMdiSubWindow *>(object);
+
+ if (!subWindow) {
+ // QApplication events:
+ if (event->type() == QEvent::ApplicationActivate && !d->active
+ && isVisible() && !window()->isMinimized()) {
+ d->activateCurrentWindow();
+ } else if (event->type() == QEvent::ApplicationDeactivate && d->active) {
+ d->setActive(d->active, false, false);
+ }
+ return QAbstractScrollArea::eventFilter(object, event);
+ }
+
+ // QMdiSubWindow events:
+ switch (event->type()) {
+ case QEvent::Move:
+ case QEvent::Resize:
+ if (d->tileCalledFromResizeEvent)
+ break;
+ d->updateScrollBars();
+ if (!subWindow->isMinimized())
+ d->isSubWindowsTiled = false;
+ break;
+ case QEvent::Show:
+#ifndef QT_NO_TABBAR
+ if (d->tabBar) {
+ const int tabIndex = d->childWindows.indexOf(subWindow);
+ if (!d->tabBar->isTabEnabled(tabIndex))
+ d->tabBar->setTabEnabled(tabIndex, true);
+ }
+#endif // QT_NO_TABBAR
+ // fall through
+ case QEvent::Hide:
+ d->isSubWindowsTiled = false;
+ break;
+#ifndef QT_NO_RUBBERBAND
+ case QEvent::Close:
+ if (d->childWindows.indexOf(subWindow) == d->indexToHighlighted)
+ d->hideRubberBand();
+ break;
+#endif
+#ifndef QT_NO_TABBAR
+ case QEvent::WindowTitleChange:
+ case QEvent::ModifiedChange:
+ if (d->tabBar)
+ d->tabBar->setTabText(d->childWindows.indexOf(subWindow), tabTextFor(subWindow));
+ break;
+ case QEvent::WindowIconChange:
+ if (d->tabBar)
+ d->tabBar->setTabIcon(d->childWindows.indexOf(subWindow), subWindow->windowIcon());
+ break;
+#endif // QT_NO_TABBAR
+ default:
+ break;
+ }
+ return QAbstractScrollArea::eventFilter(object, event);
+}
+
+/*!
+ \reimp
+*/
+void QMdiArea::paintEvent(QPaintEvent *paintEvent)
+{
+ Q_D(QMdiArea);
+ QPainter painter(d->viewport);
+ const QVector<QRect> &exposedRects = paintEvent->region().rects();
+ for (int i = 0; i < exposedRects.size(); ++i)
+ painter.fillRect(exposedRects.at(i), d->background);
+}
+
+/*!
+ This slot is called by QAbstractScrollArea after setViewport() has been
+ called. Reimplement this function in a subclass of QMdiArea to
+ initialize the new \a viewport before it is used.
+
+ \sa setViewport()
+*/
+void QMdiArea::setupViewport(QWidget *viewport)
+{
+ Q_D(QMdiArea);
+ if (viewport)
+ viewport->setAttribute(Qt::WA_OpaquePaintEvent, d->background.isOpaque());
+ foreach (QMdiSubWindow *child, d->childWindows) {
+ if (!sanityCheck(child, "QMdiArea::setupViewport"))
+ continue;
+ child->setParent(viewport, child->windowFlags());
+ }
+}
+
+QT_END_NAMESPACE
+
+#include "moc_qmdiarea.cpp"
+
+#endif // QT_NO_MDIAREA
diff --git a/src/gui/widgets/qmdiarea.h b/src/gui/widgets/qmdiarea.h
new file mode 100644
index 0000000000..8448c8194c
--- /dev/null
+++ b/src/gui/widgets/qmdiarea.h
@@ -0,0 +1,169 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QMDIAREA_H
+#define QMDIAREA_H
+
+#include <QtGui/qabstractscrollarea.h>
+#include <QtGui/qtabwidget.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+#ifndef QT_NO_MDIAREA
+
+class QMdiSubWindow;
+
+class QMdiAreaPrivate;
+class Q_GUI_EXPORT QMdiArea : public QAbstractScrollArea
+{
+ Q_OBJECT
+ Q_ENUMS(ViewMode)
+ Q_PROPERTY(QBrush background READ background WRITE setBackground)
+ Q_PROPERTY(WindowOrder activationOrder READ activationOrder WRITE setActivationOrder)
+ Q_PROPERTY(ViewMode viewMode READ viewMode WRITE setViewMode)
+#ifndef QT_NO_TABBAR
+ Q_PROPERTY(bool documentMode READ documentMode WRITE setDocumentMode)
+#endif
+#ifndef QT_NO_TABWIDGET
+ Q_PROPERTY(QTabWidget::TabShape tabShape READ tabShape WRITE setTabShape)
+ Q_PROPERTY(QTabWidget::TabPosition tabPosition READ tabPosition WRITE setTabPosition)
+#endif
+ Q_ENUMS(WindowOrder)
+public:
+ enum AreaOption {
+ DontMaximizeSubWindowOnActivation = 0x1
+ };
+ Q_DECLARE_FLAGS(AreaOptions, AreaOption)
+
+ enum WindowOrder {
+ CreationOrder,
+ StackingOrder,
+ ActivationHistoryOrder
+ };
+
+ enum ViewMode {
+ SubWindowView,
+ TabbedView
+ };
+
+ QMdiArea(QWidget *parent = 0);
+ ~QMdiArea();
+
+ QSize sizeHint() const;
+ QSize minimumSizeHint() const;
+
+ QMdiSubWindow *currentSubWindow() const;
+ QMdiSubWindow *activeSubWindow() const;
+ QList<QMdiSubWindow *> subWindowList(WindowOrder order = CreationOrder) const;
+
+ QMdiSubWindow *addSubWindow(QWidget *widget, Qt::WindowFlags flags = 0);
+ void removeSubWindow(QWidget *widget);
+
+ QBrush background() const;
+ void setBackground(const QBrush &background);
+
+ WindowOrder activationOrder() const;
+ void setActivationOrder(WindowOrder order);
+
+ void setOption(AreaOption option, bool on = true);
+ bool testOption(AreaOption opton) const;
+
+ void setViewMode(ViewMode mode);
+ ViewMode viewMode() const;
+
+#ifndef QT_NO_TABBAR
+ bool documentMode() const;
+ void setDocumentMode(bool enabled);
+#endif
+#ifndef QT_NO_TABWIDGET
+ void setTabShape(QTabWidget::TabShape shape);
+ QTabWidget::TabShape tabShape() const;
+
+ void setTabPosition(QTabWidget::TabPosition position);
+ QTabWidget::TabPosition tabPosition() const;
+#endif
+
+Q_SIGNALS:
+ void subWindowActivated(QMdiSubWindow *);
+
+public Q_SLOTS:
+ void setActiveSubWindow(QMdiSubWindow *window);
+ void tileSubWindows();
+ void cascadeSubWindows();
+ void closeActiveSubWindow();
+ void closeAllSubWindows();
+ void activateNextSubWindow();
+ void activatePreviousSubWindow();
+
+protected Q_SLOTS:
+ void setupViewport(QWidget *viewport);
+
+protected:
+ bool event(QEvent *event);
+ bool eventFilter(QObject *object, QEvent *event);
+ void paintEvent(QPaintEvent *paintEvent);
+ void childEvent(QChildEvent *childEvent);
+ void resizeEvent(QResizeEvent *resizeEvent);
+ void timerEvent(QTimerEvent *timerEvent);
+ void showEvent(QShowEvent *showEvent);
+ bool viewportEvent(QEvent *event);
+ void scrollContentsBy(int dx, int dy);
+
+private:
+ Q_DISABLE_COPY(QMdiArea)
+ Q_DECLARE_PRIVATE(QMdiArea)
+ Q_PRIVATE_SLOT(d_func(), void _q_deactivateAllWindows())
+ Q_PRIVATE_SLOT(d_func(), void _q_processWindowStateChanged(Qt::WindowStates, Qt::WindowStates))
+ Q_PRIVATE_SLOT(d_func(), void _q_currentTabChanged(int index))
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(QMdiArea::AreaOptions)
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QT_NO_MDIAREA
+#endif // QMDIAREA_H
diff --git a/src/gui/widgets/qmdiarea_p.h b/src/gui/widgets/qmdiarea_p.h
new file mode 100644
index 0000000000..645f0cc758
--- /dev/null
+++ b/src/gui/widgets/qmdiarea_p.h
@@ -0,0 +1,281 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QMDIAREA_P_H
+#define QMDIAREA_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 "qmdiarea.h"
+#include "qmdisubwindow.h"
+
+#ifndef QT_NO_MDIAREA
+
+#include <QList>
+#include <QRect>
+#include <QPoint>
+#include <QtGui/qapplication.h>
+#include <private/qmdisubwindow_p.h>
+#include <private/qabstractscrollarea_p.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace QMdi {
+class Rearranger
+{
+public:
+ enum Type {
+ RegularTiler,
+ SimpleCascader,
+ IconTiler
+ };
+
+ // Rearranges widgets relative to domain.
+ virtual void rearrange(QList<QWidget *> &widgets, const QRect &domain) const = 0;
+ virtual Type type() const = 0;
+ virtual ~Rearranger() {}
+};
+
+class RegularTiler : public Rearranger
+{
+ // Rearranges widgets according to a regular tiling pattern
+ // covering the entire domain.
+ // Both positions and sizes may change.
+ void rearrange(QList<QWidget *> &widgets, const QRect &domain) const;
+ inline Type type() const { return Rearranger::RegularTiler; }
+};
+
+class SimpleCascader : public Rearranger
+{
+ // Rearranges widgets according to a simple, regular cascading pattern.
+ // Widgets are resized to minimumSize.
+ // Both positions and sizes may change.
+ void rearrange(QList<QWidget *> &widgets, const QRect &domain) const;
+ inline Type type() const { return Rearranger::SimpleCascader; }
+};
+
+class IconTiler : public Rearranger
+{
+ // Rearranges icons (assumed to be the same size) according to a regular
+ // tiling pattern filling up the domain from the bottom.
+ // Only positions may change.
+ void rearrange(QList<QWidget *> &widgets, const QRect &domain) const;
+ inline Type type() const { return Rearranger::IconTiler; }
+};
+
+class Placer
+{
+public:
+ // Places the rectangle defined by 'size' relative to 'rects' and 'domain'.
+ // Returns the position of the resulting rectangle.
+ virtual QPoint place(
+ const QSize &size, const QList<QRect> &rects, const QRect &domain) const = 0;
+ virtual ~Placer() {}
+};
+
+class MinOverlapPlacer : public Placer
+{
+ QPoint place(const QSize &size, const QList<QRect> &rects, const QRect &domain) const;
+ static int accumulatedOverlap(const QRect &source, const QList<QRect> &rects);
+ static QRect findMinOverlapRect(const QList<QRect> &source, const QList<QRect> &rects);
+ static void getCandidatePlacements(
+ const QSize &size, const QList<QRect> &rects, const QRect &domain,
+ QList<QRect> &candidates);
+ static QPoint findBestPlacement(
+ const QRect &domain, const QList<QRect> &rects, QList<QRect> &source);
+ static void findNonInsiders(
+ const QRect &domain, QList<QRect> &source, QList<QRect> &result);
+ static void findMaxOverlappers(
+ const QRect &domain, const QList<QRect> &source, QList<QRect> &result);
+};
+} // namespace QMdi
+
+class QMdiAreaTabBar;
+class QMdiAreaPrivate : public QAbstractScrollAreaPrivate
+{
+ Q_DECLARE_PUBLIC(QMdiArea)
+public:
+ QMdiAreaPrivate();
+
+ // Variables.
+ QMdi::Rearranger *cascader;
+ QMdi::Rearranger *regularTiler;
+ QMdi::Rearranger *iconTiler;
+ QMdi::Placer *placer;
+#ifndef QT_NO_RUBBERBAND
+ QRubberBand *rubberBand;
+#endif
+ QMdiAreaTabBar *tabBar;
+ QList<QMdi::Rearranger *> pendingRearrangements;
+ QList< QPointer<QMdiSubWindow> > pendingPlacements;
+ QList< QPointer<QMdiSubWindow> > childWindows;
+ QList<int> indicesToActivatedChildren;
+ QPointer<QMdiSubWindow> active;
+ QPointer<QMdiSubWindow> aboutToBecomeActive;
+ QBrush background;
+ QMdiArea::WindowOrder activationOrder;
+ QMdiArea::AreaOptions options;
+ QMdiArea::ViewMode viewMode;
+#ifndef QT_NO_TABBAR
+ bool documentMode;
+#endif
+#ifndef QT_NO_TABWIDGET
+ QTabWidget::TabShape tabShape;
+ QTabWidget::TabPosition tabPosition;
+#endif
+ bool ignoreGeometryChange;
+ bool ignoreWindowStateChange;
+ bool isActivated;
+ bool isSubWindowsTiled;
+ bool showActiveWindowMaximized;
+ bool tileCalledFromResizeEvent;
+ bool updatesDisabledByUs;
+ bool inViewModeChange;
+ int indexToNextWindow;
+ int indexToPreviousWindow;
+ int indexToHighlighted;
+ int indexToLastActiveTab;
+ int resizeTimerId;
+ int tabToPreviousTimerId;
+
+ // Slots.
+ void _q_deactivateAllWindows(QMdiSubWindow *aboutToActivate = 0);
+ void _q_processWindowStateChanged(Qt::WindowStates oldState, Qt::WindowStates newState);
+ void _q_currentTabChanged(int index);
+
+ // Functions.
+ void appendChild(QMdiSubWindow *child);
+ void place(QMdi::Placer *placer, QMdiSubWindow *child);
+ void rearrange(QMdi::Rearranger *rearranger);
+ void arrangeMinimizedSubWindows();
+ void activateWindow(QMdiSubWindow *child);
+ void activateCurrentWindow();
+ void activateHighlightedWindow();
+ void emitWindowActivated(QMdiSubWindow *child);
+ void resetActiveWindow(QMdiSubWindow *child = 0);
+ void updateActiveWindow(int removedIndex, bool activeRemoved);
+ void updateScrollBars();
+ void internalRaise(QMdiSubWindow *child) const;
+ bool scrollBarsEnabled() const;
+ bool lastWindowAboutToBeDestroyed() const;
+ void setChildActivationEnabled(bool enable = true, bool onlyNextActivationEvent = false) const;
+ QRect resizeToMinimumTileSize(const QSize &minSubWindowSize, int subWindowCount);
+ void scrollBarPolicyChanged(Qt::Orientation, Qt::ScrollBarPolicy); // reimp
+ QMdiSubWindow *nextVisibleSubWindow(int increaseFactor, QMdiArea::WindowOrder,
+ int removed = -1, int fromIndex = -1) const;
+ void highlightNextSubWindow(int increaseFactor);
+ QList<QMdiSubWindow *> subWindowList(QMdiArea::WindowOrder, bool reversed = false) const;
+ void disconnectSubWindow(QObject *subWindow);
+ void setViewMode(QMdiArea::ViewMode mode);
+#ifndef QT_NO_TABBAR
+ void updateTabBarGeometry();
+ void refreshTabBar();
+#endif
+
+ inline void startResizeTimer()
+ {
+ Q_Q(QMdiArea);
+ if (resizeTimerId > 0)
+ q->killTimer(resizeTimerId);
+ resizeTimerId = q->startTimer(200);
+ }
+
+ inline void startTabToPreviousTimer()
+ {
+ Q_Q(QMdiArea);
+ if (tabToPreviousTimerId > 0)
+ q->killTimer(tabToPreviousTimerId);
+ tabToPreviousTimerId = q->startTimer(QApplication::keyboardInputInterval());
+ }
+
+ inline bool windowStaysOnTop(QMdiSubWindow *subWindow) const
+ {
+ if (!subWindow)
+ return false;
+ return subWindow->windowFlags() & Qt::WindowStaysOnTopHint;
+ }
+
+ inline bool isExplicitlyDeactivated(QMdiSubWindow *subWindow) const
+ {
+ if (!subWindow)
+ return true;
+ return subWindow->d_func()->isExplicitlyDeactivated;
+ }
+
+ inline void setActive(QMdiSubWindow *subWindow, bool active = true, bool changeFocus = true) const
+ {
+ if (subWindow)
+ subWindow->d_func()->setActive(active, changeFocus);
+ }
+
+#ifndef QT_NO_RUBBERBAND
+ inline void showRubberBandFor(QMdiSubWindow *subWindow)
+ {
+ if (!subWindow || !rubberBand)
+ return;
+ rubberBand->setGeometry(subWindow->geometry());
+ rubberBand->raise();
+ rubberBand->show();
+ }
+
+ inline void hideRubberBand()
+ {
+ if (rubberBand && rubberBand->isVisible())
+ rubberBand->hide();
+ indexToHighlighted = -1;
+ }
+#endif // QT_NO_RUBBERBAND
+};
+
+#endif // QT_NO_MDIAREA
+
+QT_END_NAMESPACE
+
+#endif // QMDIAREA_P_H
diff --git a/src/gui/widgets/qmdisubwindow.cpp b/src/gui/widgets/qmdisubwindow.cpp
new file mode 100644
index 0000000000..6bf763379c
--- /dev/null
+++ b/src/gui/widgets/qmdisubwindow.cpp
@@ -0,0 +1,3552 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*!
+ \class QMdiSubWindow
+ \brief The QMdiSubWindow class provides a subwindow class for
+ QMdiArea.
+ \since 4.3
+ \ingroup application
+ \mainclass
+
+ QMdiSubWindow represents a top-level window in a QMdiArea, and consists
+ of a title bar with window decorations, an internal widget, and
+ (depending on the current style) a window frame and a size
+ grip. QMdiSubWindow has its own layout, which consists of the
+ title bar and a center area for the internal widget.
+
+ \image qmdisubwindowlayout.png
+
+ The most common way to construct a QMdiSubWindow is to call
+ QMdiArea::addSubWindow() with the internal widget as the argument.
+ You can also create a subwindow yourself, and set an internal
+ widget by calling setWidget().
+
+ You use the same API when programming with subwindows as with
+ regular top-level windows (e.g., you can call functions such as
+ show(), hide(), showMaximized(), and setWindowTitle()).
+
+ \section1 Subwindow Handling
+
+ QMdiSubWindow also supports behavior specific to subwindows in
+ an MDI area.
+
+ By default, each QMdiSubWindow is visible inside the MDI area
+ viewport when moved around, but it is also possible to specify
+ transparent window movement and resizing behavior, where only
+ the outline of a subwindow is updated during these operations.
+ The setOption() function is used to enable this behavior.
+
+ The isShaded() function detects whether the subwindow is
+ currently shaded (i.e., the window is collapsed so that only the
+ title bar is visible). To enter shaded mode, call showShaded().
+ QMdiSubWindow emits the windowStateChanged() signal whenever the
+ window state has changed (e.g., when the window becomes minimized,
+ or is restored). It also emits aboutToActivate() before it is
+ activated.
+
+ In keyboard-interactive mode, the windows are moved and resized
+ with the keyboard. You can enter this mode through the system menu
+ of the window. The keyboardSingleStep and keyboardPageStep
+ properties control the distance the widget is moved or resized for
+ each keypress event. When shift is pressed down page step is used;
+ otherwise single step is used.
+
+ You can also change the active window with the keyboard. By
+ pressing the control and tab keys at the same time, the next
+ (using the current \l{QMdiArea::}{WindowOrder}) subwindow will be
+ activated. By pressing control, shift, and tab, you will activate
+ the previous window. This is equivalent to calling
+ \l{QMdiArea::}{activateNextSubWindow()} and
+ \l{QMdiArea::}{activatePreviousSubWindow()}. Note that these
+ shortcuts overrides global shortcuts, but not the \l{QMdiArea}s
+ shortcuts.
+
+ \sa QMdiArea
+*/
+
+/*!
+ \enum QMdiSubWindow::SubWindowOption
+
+ This enum describes options that customize the behavior
+ of QMdiSubWindow.
+
+ \omitvalue AllowOutsideAreaHorizontally
+ \omitvalue AllowOutsideAreaVertically
+
+ \value RubberBandResize If you enable this option, a rubber band
+ control is used to represent the subwindow's outline, and the user
+ resizes this instead of the subwindow itself.
+ As a result, the subwindow maintains its original position and size
+ until the resize operation has been completed, at which time it will
+ receive a single QResizeEvent.
+ By default, this option is disabled.
+
+ \value RubberBandMove If you enable this option, a rubber band
+ control is used to represent the subwindow's outline, and the user
+ moves this instead of the subwindow itself.
+ As a result, the subwindow remains in its original position until
+ the move operation has completed, at which time a QMoveEvent is
+ sent to the window. By default, this option is disabled.
+*/
+
+/*!
+ \fn QMdiSubWindow::windowStateChanged(Qt::WindowStates oldState, Qt::WindowStates newState)
+
+ QMdiSubWindow emits this signal after the window state changes. \a
+ oldState is the window state before it changed, and \a newState is the
+ new, current state.
+*/
+
+/*!
+ \fn QMdiSubWindow::aboutToActivate()
+
+ QMdiSubWindow emits this signal immediately before it is
+ activated. After the subwindow has been activated, the QMdiArea that
+ manages the subwindow will also emit the
+ \l{QMdiArea::}{subWindowActivated()} signal.
+
+ \sa QMdiArea::subWindowActivated()
+*/
+
+#include "qmdisubwindow_p.h"
+
+#ifndef QT_NO_MDIAREA
+
+#include <QApplication>
+#include <QStylePainter>
+#include <QVBoxLayout>
+#include <QMouseEvent>
+#include <QWhatsThis>
+#include <QToolTip>
+#include <QMainWindow>
+#include <QScrollBar>
+#include <QDebug>
+#if defined(Q_WS_MAC) && !defined(QT_NO_STYLE_MAC)
+#include <QMacStyle>
+#endif
+#include <QMdiArea>
+
+QT_BEGIN_NAMESPACE
+
+using namespace QMdi;
+
+static const QStyle::SubControl SubControls[] =
+{
+ QStyle::SC_TitleBarLabel, // 1
+ QStyle::SC_TitleBarSysMenu, // 2
+ QStyle::SC_TitleBarMinButton, // 3
+ QStyle::SC_TitleBarMaxButton, // 4
+ QStyle::SC_TitleBarShadeButton, // 5
+ QStyle::SC_TitleBarCloseButton, // 6
+ QStyle::SC_TitleBarNormalButton, // 7
+ QStyle::SC_TitleBarUnshadeButton, // 8
+ QStyle::SC_TitleBarContextHelpButton // 9
+};
+static const int NumSubControls = sizeof(SubControls) / sizeof(SubControls[0]);
+
+static const QStyle::StandardPixmap ButtonPixmaps[] =
+{
+ QStyle::SP_TitleBarMinButton,
+ QStyle::SP_TitleBarNormalButton,
+ QStyle::SP_TitleBarCloseButton
+};
+static const int NumButtonPixmaps = sizeof(ButtonPixmaps) / sizeof(ButtonPixmaps[0]);
+
+static const Qt::WindowFlags CustomizeWindowFlags =
+ Qt::FramelessWindowHint
+ | Qt::CustomizeWindowHint
+ | Qt::WindowTitleHint
+ | Qt::WindowSystemMenuHint
+ | Qt::WindowMinimizeButtonHint
+ | Qt::WindowMaximizeButtonHint
+ | Qt::WindowMinMaxButtonsHint;
+
+
+static const int BoundaryMargin = 5;
+
+static inline int getMoveDeltaComponent(uint cflags, uint moveFlag, uint resizeFlag,
+ int delta, int maxDelta, int minDelta)
+{
+ if (cflags & moveFlag) {
+ if (delta > 0)
+ return (cflags & resizeFlag) ? qMin(delta, maxDelta) : delta;
+ return (cflags & resizeFlag) ? qMax(delta, minDelta) : delta;
+ }
+ return 0;
+}
+
+static inline int getResizeDeltaComponent(uint cflags, uint resizeFlag,
+ uint resizeReverseFlag, int delta)
+{
+ if (cflags & resizeFlag) {
+ if (cflags & resizeReverseFlag)
+ return -delta;
+ return delta;
+ }
+ return 0;
+}
+
+static inline bool isChildOfQMdiSubWindow(const QWidget *child)
+{
+ Q_ASSERT(child);
+ QWidget *parent = child->parentWidget();
+ while (parent) {
+ if (qobject_cast<QMdiSubWindow *>(parent))
+ return true;
+ parent = parent->parentWidget();
+ }
+ return false;
+}
+
+static inline bool isChildOfTabbedQMdiArea(const QMdiSubWindow *child)
+{
+ Q_ASSERT(child);
+ if (QMdiArea *mdiArea = child->mdiArea()) {
+ if (mdiArea->viewMode() == QMdiArea::TabbedView)
+ return true;
+ }
+ return false;
+}
+
+template<typename T>
+static inline ControlElement<T> *ptr(QWidget *widget)
+{
+ if (widget && widget->qt_metacast("ControlElement")
+ && strcmp(widget->metaObject()->className(), T::staticMetaObject.className()) == 0) {
+ return static_cast<ControlElement<T> *>(widget);
+ }
+ return 0;
+}
+
+QString QMdiSubWindowPrivate::originalWindowTitle()
+{
+ Q_Q(QMdiSubWindow);
+ if (originalTitle.isNull()) {
+ originalTitle = q->window()->windowTitle();
+ if (originalTitle.isNull())
+ originalTitle = QLatin1String("");
+ }
+ return originalTitle;
+}
+
+void QMdiSubWindowPrivate::setNewWindowTitle()
+{
+ Q_Q(QMdiSubWindow);
+ QString childTitle = q->windowTitle();
+ if (childTitle.isEmpty())
+ return;
+ QString original = originalWindowTitle();
+ if (!original.isEmpty()) {
+ if (!original.contains(QMdiSubWindow::tr("- [%1]").arg(childTitle)))
+ q->window()->setWindowTitle(QMdiSubWindow::tr("%1 - [%2]").arg(original, childTitle));
+
+ } else {
+ q->window()->setWindowTitle(childTitle);
+ }
+}
+
+static inline bool isHoverControl(QStyle::SubControl control)
+{
+ return control != QStyle::SC_None && control != QStyle::SC_TitleBarLabel;
+}
+
+#if defined(Q_WS_WIN)
+static inline QRgb colorref2qrgb(COLORREF col)
+{
+ return qRgb(GetRValue(col),GetGValue(col),GetBValue(col));
+}
+#endif
+
+#ifndef QT_NO_TOOLTIP
+static void showToolTip(QHelpEvent *helpEvent, QWidget *widget, const QStyleOptionComplex &opt,
+ QStyle::ComplexControl complexControl, QStyle::SubControl subControl)
+{
+ Q_ASSERT(helpEvent);
+ Q_ASSERT(helpEvent->type() == QEvent::ToolTip);
+ Q_ASSERT(widget);
+
+#if defined(Q_WS_MAC) && !defined(QT_NO_STYLE_MAC)
+ // Native Mac windows don't show tool tip.
+ if (qobject_cast<QMacStyle *>(widget->style()))
+ return;
+#endif
+
+ // Convert CC_MdiControls to CC_TitleBar. Sub controls of different complex
+ // controls cannot be in the same switch as they might have the same value.
+ if (complexControl == QStyle::CC_MdiControls) {
+ if (subControl == QStyle::SC_MdiMinButton)
+ subControl = QStyle::SC_TitleBarMinButton;
+ else if (subControl == QStyle::SC_MdiCloseButton)
+ subControl = QStyle::SC_TitleBarCloseButton;
+ else if (subControl == QStyle::SC_MdiNormalButton)
+ subControl = QStyle::SC_TitleBarNormalButton;
+ else
+ subControl = QStyle::SC_None;
+ }
+
+ // Don't change the tooltip for the base widget itself.
+ if (subControl == QStyle::SC_None)
+ return;
+
+ QString toolTip;
+
+ switch (subControl) {
+ case QStyle::SC_TitleBarMinButton:
+ toolTip = QMdiSubWindow::tr("Minimize");
+ break;
+ case QStyle::SC_TitleBarMaxButton:
+ toolTip = QMdiSubWindow::tr("Maximize");
+ break;
+ case QStyle::SC_TitleBarUnshadeButton:
+ toolTip = QMdiSubWindow::tr("Unshade");
+ break;
+ case QStyle::SC_TitleBarShadeButton:
+ toolTip = QMdiSubWindow::tr("Shade");
+ break;
+ case QStyle::SC_TitleBarNormalButton:
+ if (widget->isMaximized() || !qobject_cast<QMdiSubWindow *>(widget))
+ toolTip = QMdiSubWindow::tr("Restore Down");
+ else
+ toolTip = QMdiSubWindow::tr("Restore");
+ break;
+ case QStyle::SC_TitleBarCloseButton:
+ toolTip = QMdiSubWindow::tr("Close");
+ break;
+ case QStyle::SC_TitleBarContextHelpButton:
+ toolTip = QMdiSubWindow::tr("Help");
+ break;
+ case QStyle::SC_TitleBarSysMenu:
+ toolTip = QMdiSubWindow::tr("Menu");
+ break;
+ default:
+ break;
+ }
+
+ const QRect rect = widget->style()->subControlRect(complexControl, &opt, subControl, widget);
+ QToolTip::showText(helpEvent->globalPos(), toolTip, widget, rect);
+}
+#endif // QT_NO_TOOLTIP
+
+namespace QMdi {
+/*
+ \class ControlLabel
+ \internal
+*/
+class ControlLabel : public QWidget
+{
+ Q_OBJECT
+public:
+ ControlLabel(QMdiSubWindow *subWindow, QWidget *parent = 0);
+
+ QSize sizeHint() const;
+
+signals:
+ void _q_clicked();
+ void _q_doubleClicked();
+
+protected:
+ bool event(QEvent *event);
+ void paintEvent(QPaintEvent *paintEvent);
+ void mousePressEvent(QMouseEvent *mouseEvent);
+ void mouseDoubleClickEvent(QMouseEvent *mouseEvent);
+ void mouseReleaseEvent(QMouseEvent *mouseEvent);
+
+private:
+ QPixmap label;
+ bool isPressed;
+ void updateWindowIcon();
+};
+} // namespace QMdi
+
+ControlLabel::ControlLabel(QMdiSubWindow *subWindow, QWidget *parent)
+ : QWidget(parent), isPressed(false)
+{
+ Q_UNUSED(subWindow);
+ setFocusPolicy(Qt::NoFocus);
+ updateWindowIcon();
+ setFixedSize(label.size());
+}
+
+/*
+ \internal
+*/
+QSize ControlLabel::sizeHint() const
+{
+ return label.size();
+}
+
+/*
+ \internal
+*/
+bool ControlLabel::event(QEvent *event)
+{
+ if (event->type() == QEvent::WindowIconChange)
+ updateWindowIcon();
+#ifndef QT_NO_TOOLTIP
+ else if (event->type() == QEvent::ToolTip) {
+ QStyleOptionTitleBar options;
+ options.initFrom(this);
+ showToolTip(static_cast<QHelpEvent *>(event), this, options,
+ QStyle::CC_TitleBar, QStyle::SC_TitleBarSysMenu);
+ }
+#endif
+ return QWidget::event(event);
+}
+
+/*
+ \internal
+*/
+void ControlLabel::paintEvent(QPaintEvent * /*paintEvent*/)
+{
+ QPainter painter(this);
+ painter.drawPixmap(0, 0, label);
+}
+
+/*
+ \internal
+*/
+void ControlLabel::mousePressEvent(QMouseEvent *mouseEvent)
+{
+ if (mouseEvent->button() != Qt::LeftButton) {
+ mouseEvent->ignore();
+ return;
+ }
+ isPressed = true;
+}
+
+/*
+ \internal
+*/
+void ControlLabel::mouseDoubleClickEvent(QMouseEvent *mouseEvent)
+{
+ if (mouseEvent->button() != Qt::LeftButton) {
+ mouseEvent->ignore();
+ return;
+ }
+ isPressed = false;
+ emit _q_doubleClicked();
+}
+
+/*
+ \internal
+*/
+void ControlLabel::mouseReleaseEvent(QMouseEvent *mouseEvent)
+{
+ if (mouseEvent->button() != Qt::LeftButton) {
+ mouseEvent->ignore();
+ return;
+ }
+ if (isPressed) {
+ isPressed = false;
+ emit _q_clicked();
+ }
+}
+
+/*
+ \internal
+*/
+void ControlLabel::updateWindowIcon()
+{
+ QIcon menuIcon = windowIcon();
+ if (menuIcon.isNull())
+ menuIcon = style()->standardIcon(QStyle::SP_TitleBarMenuButton, 0, parentWidget());
+ label = menuIcon.pixmap(16, 16);
+ update();
+}
+
+namespace QMdi {
+/*
+ \class ControllerWidget
+ \internal
+*/
+class ControllerWidget : public QWidget
+{
+ Q_OBJECT
+public:
+ ControllerWidget(QMdiSubWindow *subWindow, QWidget *parent = 0);
+ QSize sizeHint() const;
+ void setControlVisible(QMdiSubWindowPrivate::WindowStateAction action, bool visible);
+ inline bool hasVisibleControls() const
+ {
+ return (visibleControls & QStyle::SC_MdiMinButton)
+ || (visibleControls & QStyle::SC_MdiNormalButton)
+ || (visibleControls & QStyle::SC_MdiCloseButton);
+ }
+
+signals:
+ void _q_minimize();
+ void _q_restore();
+ void _q_close();
+
+protected:
+ void paintEvent(QPaintEvent *event);
+ void mousePressEvent(QMouseEvent *event);
+ void mouseReleaseEvent(QMouseEvent *event);
+ void mouseMoveEvent(QMouseEvent *event);
+ void leaveEvent(QEvent *event);
+ bool event(QEvent *event);
+
+private:
+ QStyle::SubControl activeControl;
+ QStyle::SubControl hoverControl;
+ QStyle::SubControls visibleControls;
+ void initStyleOption(QStyleOptionComplex *option) const;
+ QMdiArea *mdiArea;
+ inline QStyle::SubControl getSubControl(const QPoint &pos) const
+ {
+ QStyleOptionComplex opt;
+ initStyleOption(&opt);
+ return style()->hitTestComplexControl(QStyle::CC_MdiControls, &opt, pos, mdiArea);
+ }
+};
+} // namespace QMdi
+
+/*
+ \internal
+*/
+ControllerWidget::ControllerWidget(QMdiSubWindow *subWindow, QWidget *parent)
+ : QWidget(parent),
+ activeControl(QStyle::SC_None),
+ hoverControl(QStyle::SC_None),
+ visibleControls(QStyle::SC_None),
+ mdiArea(0)
+{
+ if (subWindow->parentWidget())
+ mdiArea = qobject_cast<QMdiArea *>(subWindow->parentWidget()->parentWidget());
+ setFocusPolicy(Qt::NoFocus);
+ setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
+ setMouseTracking(true);
+}
+
+/*
+ \internal
+*/
+QSize ControllerWidget::sizeHint() const
+{
+ ensurePolished();
+ QStyleOptionComplex opt;
+ initStyleOption(&opt);
+ QSize size(48, 16);
+ return style()->sizeFromContents(QStyle::CT_MdiControls, &opt, size, mdiArea);
+}
+
+void ControllerWidget::setControlVisible(QMdiSubWindowPrivate::WindowStateAction action, bool visible)
+{
+ QStyle::SubControl subControl = QStyle::SC_None;
+
+ // Map action from QMdiSubWindowPrivate::WindowStateAction to QStyle::SubControl.
+ if (action == QMdiSubWindowPrivate::MaximizeAction)
+ subControl = QStyle::SC_MdiNormalButton;
+ else if (action == QMdiSubWindowPrivate::CloseAction)
+ subControl = QStyle::SC_MdiCloseButton;
+ else if (action == QMdiSubWindowPrivate::MinimizeAction)
+ subControl = QStyle::SC_MdiMinButton;
+
+ if (subControl == QStyle::SC_None)
+ return;
+
+ if (visible && !(visibleControls & subControl))
+ visibleControls |= subControl;
+ else if (!visible && (visibleControls & subControl))
+ visibleControls &= ~subControl;
+}
+
+/*
+ \internal
+*/
+void ControllerWidget::paintEvent(QPaintEvent * /*paintEvent*/)
+{
+ QStyleOptionComplex opt;
+ initStyleOption(&opt);
+ if (activeControl == hoverControl) {
+ opt.activeSubControls = activeControl;
+ opt.state |= QStyle::State_Sunken;
+ } else if (hoverControl != QStyle::SC_None && (activeControl == QStyle::SC_None)) {
+ opt.activeSubControls = hoverControl;
+ opt.state |= QStyle::State_MouseOver;
+ }
+ QPainter painter(this);
+ style()->drawComplexControl(QStyle::CC_MdiControls, &opt, &painter, mdiArea);
+}
+
+/*
+ \internal
+*/
+void ControllerWidget::mousePressEvent(QMouseEvent *event)
+{
+ if (event->button() != Qt::LeftButton) {
+ event->ignore();
+ return;
+ }
+ activeControl = getSubControl(event->pos());
+ update();
+}
+
+/*
+ \internal
+*/
+void ControllerWidget::mouseReleaseEvent(QMouseEvent *event)
+{
+ if (event->button() != Qt::LeftButton) {
+ event->ignore();
+ return;
+ }
+
+ QStyle::SubControl under_mouse = getSubControl(event->pos());
+ if (under_mouse == activeControl) {
+ switch (activeControl) {
+ case QStyle::SC_MdiCloseButton:
+ emit _q_close();
+ break;
+ case QStyle::SC_MdiNormalButton:
+ emit _q_restore();
+ break;
+ case QStyle::SC_MdiMinButton:
+ emit _q_minimize();
+ break;
+ default:
+ break;
+ }
+ }
+
+ activeControl = QStyle::SC_None;
+ update();
+}
+
+/*
+ \internal
+*/
+void ControllerWidget::mouseMoveEvent(QMouseEvent *event)
+{
+ QStyle::SubControl under_mouse = getSubControl(event->pos());
+ //test if hover state changes
+ if (hoverControl != under_mouse) {
+ hoverControl = under_mouse;
+ update();
+ }
+}
+
+/*
+ \internal
+*/
+void ControllerWidget::leaveEvent(QEvent * /*event*/)
+{
+ hoverControl = QStyle::SC_None;
+ update();
+}
+
+/*
+ \internal
+*/
+bool ControllerWidget::event(QEvent *event)
+{
+#ifndef QT_NO_TOOLTIP
+ if (event->type() == QEvent::ToolTip) {
+ QStyleOptionComplex opt;
+ initStyleOption(&opt);
+ QHelpEvent *helpEvent = static_cast<QHelpEvent *>(event);
+ showToolTip(helpEvent, this, opt, QStyle::CC_MdiControls, getSubControl(helpEvent->pos()));
+ }
+#endif // QT_NO_TOOLTIP
+ return QWidget::event(event);
+}
+
+/*
+ \internal
+*/
+void ControllerWidget::initStyleOption(QStyleOptionComplex *option) const
+{
+ option->initFrom(this);
+ option->subControls = visibleControls;
+ option->activeSubControls = QStyle::SC_None;
+}
+
+/*
+ \internal
+*/
+ControlContainer::ControlContainer(QMdiSubWindow *mdiChild)
+ : QObject(mdiChild),
+ previousLeft(0),
+ previousRight(0),
+#ifndef QT_NO_MENUBAR
+ m_menuBar(0),
+#endif
+ mdiChild(mdiChild)
+{
+ Q_ASSERT(mdiChild);
+
+ m_controllerWidget = new ControlElement<ControllerWidget>(mdiChild);
+ connect(m_controllerWidget, SIGNAL(_q_close()), mdiChild, SLOT(close()));
+ connect(m_controllerWidget, SIGNAL(_q_restore()), mdiChild, SLOT(showNormal()));
+ connect(m_controllerWidget, SIGNAL(_q_minimize()), mdiChild, SLOT(showMinimized()));
+
+ m_menuLabel = new ControlElement<ControlLabel>(mdiChild);
+ m_menuLabel->setWindowIcon(mdiChild->windowIcon());
+#ifndef QT_NO_MENU
+ connect(m_menuLabel, SIGNAL(_q_clicked()), mdiChild, SLOT(showSystemMenu()));
+#endif
+ connect(m_menuLabel, SIGNAL(_q_doubleClicked()), mdiChild, SLOT(close()));
+}
+
+ControlContainer::~ControlContainer()
+{
+#ifndef QT_NO_MENUBAR
+ removeButtonsFromMenuBar();
+#endif
+ delete m_menuLabel;
+ m_menuLabel = 0;
+ delete m_controllerWidget;
+ m_controllerWidget = 0;
+}
+
+#ifndef QT_NO_MENUBAR
+/*
+ \internal
+*/
+QMenuBar *QMdiSubWindowPrivate::menuBar() const
+{
+#if defined(QT_NO_MAINWINDOW)
+ return 0;
+#else
+ Q_Q(const QMdiSubWindow);
+ if (!q->isMaximized() || drawTitleBarWhenMaximized() || isChildOfTabbedQMdiArea(q))
+ return 0;
+
+ if (QMainWindow *mainWindow = qobject_cast<QMainWindow *>(q->window()))
+ return mainWindow->menuBar();
+
+ return 0;
+#endif
+}
+
+/*
+ \internal
+*/
+void ControlContainer::showButtonsInMenuBar(QMenuBar *menuBar)
+{
+ if (!menuBar || !mdiChild || mdiChild->windowFlags() & Qt::FramelessWindowHint)
+ return;
+ m_menuBar = menuBar;
+
+ if (m_menuLabel && mdiChild->windowFlags() & Qt::WindowSystemMenuHint) {
+ QWidget *currentLeft = menuBar->cornerWidget(Qt::TopLeftCorner);
+ if (currentLeft)
+ currentLeft->hide();
+ if (currentLeft != m_menuLabel) {
+ menuBar->setCornerWidget(m_menuLabel, Qt::TopLeftCorner);
+ previousLeft = currentLeft;
+ }
+ m_menuLabel->show();
+ }
+ ControllerWidget *controllerWidget = qobject_cast<ControllerWidget *>(m_controllerWidget);
+ if (controllerWidget && controllerWidget->hasVisibleControls()) {
+ QWidget *currentRight = menuBar->cornerWidget(Qt::TopRightCorner);
+ if (currentRight)
+ currentRight->hide();
+ if (currentRight != m_controllerWidget) {
+ menuBar->setCornerWidget(m_controllerWidget, Qt::TopRightCorner);
+ previousRight = currentRight;
+ }
+ m_controllerWidget->show();
+ }
+ mdiChild->d_func()->setNewWindowTitle();
+}
+
+/*
+ \internal
+*/
+void ControlContainer::removeButtonsFromMenuBar(QMenuBar *menuBar)
+{
+ if (menuBar && menuBar != m_menuBar) {
+ // m_menubar was deleted while sub-window was maximized
+ previousRight = 0;
+ previousLeft = 0;
+ m_menuBar = menuBar;
+ }
+
+ if (!m_menuBar || !mdiChild || qt_widget_private(mdiChild->window())->data.in_destructor)
+ return;
+
+ QMdiSubWindow *child = 0;
+ if (m_controllerWidget) {
+ QWidget *currentRight = m_menuBar->cornerWidget(Qt::TopRightCorner);
+ if (currentRight == m_controllerWidget) {
+ if (ControlElement<ControllerWidget> *ce = ptr<ControllerWidget>(previousRight)) {
+ if (!ce->mdiChild || !ce->mdiChild->isMaximized())
+ previousRight = 0;
+ else
+ child = ce->mdiChild;
+ }
+ m_menuBar->setCornerWidget(previousRight, Qt::TopRightCorner);
+ if (previousRight) {
+ previousRight->show();
+ previousRight = 0;
+ }
+ }
+ m_controllerWidget->hide();
+ m_controllerWidget->setParent(0);
+ }
+ if (m_menuLabel) {
+ QWidget *currentLeft = m_menuBar->cornerWidget(Qt::TopLeftCorner);
+ if (currentLeft == m_menuLabel) {
+ if (ControlElement<ControlLabel> *ce = ptr<ControlLabel>(previousLeft)) {
+ if (!ce->mdiChild || !ce->mdiChild->isMaximized())
+ previousLeft = 0;
+ else if (!child)
+ child = mdiChild;
+ }
+ m_menuBar->setCornerWidget(previousLeft, Qt::TopLeftCorner);
+ if (previousLeft) {
+ previousLeft->show();
+ previousLeft = 0;
+ }
+ }
+ m_menuLabel->hide();
+ m_menuLabel->setParent(0);
+ }
+ m_menuBar->update();
+ if (child)
+ child->d_func()->setNewWindowTitle();
+ else if (mdiChild)
+ mdiChild->window()->setWindowTitle(mdiChild->d_func()->originalWindowTitle());
+}
+
+#endif // QT_NO_MENUBAR
+
+void ControlContainer::updateWindowIcon(const QIcon &windowIcon)
+{
+ if (m_menuLabel)
+ m_menuLabel->setWindowIcon(windowIcon);
+}
+
+/*!
+ \internal
+*/
+QMdiSubWindowPrivate::QMdiSubWindowPrivate()
+ : baseWidget(0),
+ restoreFocusWidget(0),
+ controlContainer(0),
+#ifndef QT_NO_SIZEGRIP
+ sizeGrip(0),
+#endif
+#ifndef QT_NO_RUBBERBAND
+ rubberBand(0),
+#endif
+ userMinimumSize(0,0),
+ resizeEnabled(true),
+ moveEnabled(true),
+ isInInteractiveMode(false),
+#ifndef QT_NO_RUBBERBAND
+ isInRubberBandMode(false),
+#endif
+ isShadeMode(false),
+ ignoreWindowTitleChange(false),
+ ignoreNextActivationEvent(false),
+ activationEnabled(true),
+ isShadeRequestFromMinimizeMode(false),
+ isMaximizeMode(false),
+ isWidgetHiddenByUs(false),
+ isActive(false),
+ isExplicitlyDeactivated(false),
+ keyboardSingleStep(5),
+ keyboardPageStep(20),
+ resizeTimerId(-1),
+ currentOperation(None),
+ hoveredSubControl(QStyle::SC_None),
+ activeSubControl(QStyle::SC_None),
+ focusInReason(Qt::ActiveWindowFocusReason)
+{
+ initOperationMap();
+}
+
+/*!
+ \internal
+*/
+void QMdiSubWindowPrivate::_q_updateStaysOnTopHint()
+{
+#ifndef QT_NO_ACTION
+ Q_Q(QMdiSubWindow);
+ if (QAction *senderAction = qobject_cast<QAction *>(q->sender())) {
+ if (senderAction->isChecked()) {
+ q->setWindowFlags(q->windowFlags() | Qt::WindowStaysOnTopHint);
+ q->raise();
+ } else {
+ q->setWindowFlags(q->windowFlags() & ~Qt::WindowStaysOnTopHint);
+ q->lower();
+ }
+ }
+#endif // QT_NO_ACTION
+}
+
+/*!
+ \internal
+*/
+void QMdiSubWindowPrivate::_q_enterInteractiveMode()
+{
+#ifndef QT_NO_ACTION
+ Q_Q(QMdiSubWindow);
+ QAction *action = qobject_cast<QAction *>(q->sender());
+ if (!action)
+ return;
+
+ QPoint pressPos;
+ if (actions[MoveAction] && actions[MoveAction] == action) {
+ currentOperation = Move;
+ pressPos = QPoint(q->width() / 2, titleBarHeight() - 1);
+ } else if (actions[ResizeAction] && actions[ResizeAction] == action) {
+ currentOperation = q->isLeftToRight() ? BottomRightResize : BottomLeftResize;
+ int offset = q->style()->pixelMetric(QStyle::PM_MdiSubWindowFrameWidth, 0, q) / 2;
+ int x = q->isLeftToRight() ? q->width() - offset : offset;
+ pressPos = QPoint(x, q->height() - offset);
+ } else {
+ return;
+ }
+
+ updateCursor();
+#ifndef QT_NO_CURSOR
+ q->cursor().setPos(q->mapToGlobal(pressPos));
+#endif
+ mousePressPosition = q->mapToParent(pressPos);
+ oldGeometry = q->geometry();
+ isInInteractiveMode = true;
+ q->setFocus();
+#ifndef QT_NO_RUBBERBAND
+ if ((q->testOption(QMdiSubWindow::RubberBandResize)
+ && (currentOperation == BottomRightResize || currentOperation == BottomLeftResize))
+ || (q->testOption(QMdiSubWindow::RubberBandMove) && currentOperation == Move)) {
+ enterRubberBandMode();
+ } else
+#endif // QT_NO_RUBBERBAND
+ {
+ q->grabMouse();
+ }
+#endif // QT_NO_ACTION
+}
+
+/*!
+ \internal
+*/
+void QMdiSubWindowPrivate::_q_processFocusChanged(QWidget *old, QWidget *now)
+{
+ Q_UNUSED(old);
+ Q_Q(QMdiSubWindow);
+ if (now && (now == q || q->isAncestorOf(now))) {
+ if (now == q && !isInInteractiveMode)
+ setFocusWidget();
+ setActive(true);
+ }
+}
+
+/*!
+ \internal
+*/
+void QMdiSubWindowPrivate::leaveInteractiveMode()
+{
+ Q_Q(QMdiSubWindow);
+#ifndef QT_NO_RUBBERBAND
+ if (isInRubberBandMode)
+ leaveRubberBandMode();
+ else
+#endif
+ q->releaseMouse();
+ isInInteractiveMode = false;
+ currentOperation = None;
+ updateDirtyRegions();
+ updateCursor();
+ if (baseWidget && baseWidget->focusWidget())
+ baseWidget->focusWidget()->setFocus();
+}
+
+/*!
+ \internal
+*/
+void QMdiSubWindowPrivate::removeBaseWidget()
+{
+ if (!baseWidget)
+ return;
+
+ Q_Q(QMdiSubWindow);
+ baseWidget->removeEventFilter(q);
+ if (QLayout *layout = q->layout())
+ layout->removeWidget(baseWidget);
+ if (baseWidget->windowTitle() == q->windowTitle()) {
+ ignoreWindowTitleChange = true;
+ q->setWindowTitle(QString());
+ ignoreWindowTitleChange = false;
+ q->setWindowModified(false);
+ }
+ lastChildWindowTitle.clear();
+ baseWidget->setParent(0);
+ baseWidget = 0;
+ isWidgetHiddenByUs = false;
+}
+
+/*!
+ \internal
+*/
+void QMdiSubWindowPrivate::initOperationMap()
+{
+ operationMap.insert(Move, OperationInfo(HMove | VMove, Qt::ArrowCursor, false));
+ operationMap.insert(TopResize, OperationInfo(VMove | VResize | VResizeReverse, Qt::SizeVerCursor));
+ operationMap.insert(BottomResize, OperationInfo(VResize, Qt::SizeVerCursor));
+ operationMap.insert(LeftResize, OperationInfo(HMove | HResize | HResizeReverse, Qt::SizeHorCursor));
+ operationMap.insert(RightResize, OperationInfo(HResize, Qt::SizeHorCursor));
+ operationMap.insert(TopLeftResize, OperationInfo(HMove | VMove | HResize | VResize | VResizeReverse
+ | HResizeReverse, Qt::SizeFDiagCursor));
+ operationMap.insert(TopRightResize, OperationInfo(VMove | HResize | VResize
+ | VResizeReverse, Qt::SizeBDiagCursor));
+ operationMap.insert(BottomLeftResize, OperationInfo(HMove | HResize | VResize | HResizeReverse,
+ Qt::SizeBDiagCursor));
+ operationMap.insert(BottomRightResize, OperationInfo(HResize | VResize, Qt::SizeFDiagCursor));
+}
+
+#ifndef QT_NO_MENU
+
+/*!
+ \internal
+*/
+void QMdiSubWindowPrivate::createSystemMenu()
+{
+ Q_Q(QMdiSubWindow);
+ Q_ASSERT_X(q, "QMdiSubWindowPrivate::createSystemMenu",
+ "You can NOT call this function before QMdiSubWindow's ctor");
+ systemMenu = new QMenu(q);
+ const QStyle *style = q->style();
+ addToSystemMenu(RestoreAction, QMdiSubWindow::tr("&Restore"), SLOT(showNormal()));
+ actions[RestoreAction]->setIcon(style->standardIcon(QStyle::SP_TitleBarNormalButton, 0, q));
+ actions[RestoreAction]->setEnabled(false);
+ addToSystemMenu(MoveAction, QMdiSubWindow::tr("&Move"), SLOT(_q_enterInteractiveMode()));
+ addToSystemMenu(ResizeAction, QMdiSubWindow::tr("&Size"), SLOT(_q_enterInteractiveMode()));
+ addToSystemMenu(MinimizeAction, QMdiSubWindow::tr("Mi&nimize"), SLOT(showMinimized()));
+ actions[MinimizeAction]->setIcon(style->standardIcon(QStyle::SP_TitleBarMinButton, 0, q));
+ addToSystemMenu(MaximizeAction, QMdiSubWindow::tr("Ma&ximize"), SLOT(showMaximized()));
+ actions[MaximizeAction]->setIcon(style->standardIcon(QStyle::SP_TitleBarMaxButton, 0, q));
+ addToSystemMenu(StayOnTopAction, QMdiSubWindow::tr("Stay on &Top"), SLOT(_q_updateStaysOnTopHint()));
+ actions[StayOnTopAction]->setCheckable(true);
+ systemMenu->addSeparator();
+ addToSystemMenu(CloseAction, QMdiSubWindow::tr("&Close"), SLOT(close()));
+ actions[CloseAction]->setIcon(style->standardIcon(QStyle::SP_TitleBarCloseButton, 0, q));
+#if !defined(QT_NO_SHORTCUT)
+ actions[CloseAction]->setShortcut(QKeySequence::Close);
+#endif
+ updateActions();
+}
+#endif
+
+/*!
+ \internal
+*/
+void QMdiSubWindowPrivate::updateCursor()
+{
+#ifndef QT_NO_CURSOR
+ Q_Q(QMdiSubWindow);
+#if defined(Q_WS_MAC) && !defined(QT_NO_STYLE_MAC)
+ if (qobject_cast<QMacStyle *>(q->style()))
+ return;
+#endif
+
+ if (currentOperation == None) {
+ q->unsetCursor();
+ return;
+ }
+
+ if (currentOperation == Move || operationMap.find(currentOperation).value().hover) {
+ q->setCursor(operationMap.find(currentOperation).value().cursorShape);
+ return;
+ }
+#endif
+}
+
+/*!
+ \internal
+*/
+void QMdiSubWindowPrivate::updateDirtyRegions()
+{
+ // No update necessary
+ if (!q_func()->parent())
+ return;
+
+ foreach (Operation operation, operationMap.keys())
+ operationMap.find(operation).value().region = getRegion(operation);
+}
+
+/*!
+ \internal
+*/
+void QMdiSubWindowPrivate::updateGeometryConstraints()
+{
+ Q_Q(QMdiSubWindow);
+ if (!q->parent())
+ return;
+
+ internalMinimumSize = (!q->isMinimized() && !q->minimumSize().isNull())
+ ? q->minimumSize() : q->minimumSizeHint();
+ int margin, minWidth;
+ sizeParameters(&margin, &minWidth);
+ q->setContentsMargins(margin, titleBarHeight(), margin, margin);
+ if (q->isMaximized() || (q->isMinimized() && !q->isShaded())) {
+ moveEnabled = false;
+ resizeEnabled = false;
+ } else {
+ moveEnabled = true;
+ if ((q->windowFlags() & Qt::MSWindowsFixedSizeDialogHint) || q->isShaded())
+ resizeEnabled = false;
+ else
+ resizeEnabled = true;
+ }
+ updateDirtyRegions();
+}
+
+/*!
+ \internal
+*/
+void QMdiSubWindowPrivate::updateMask()
+{
+ Q_Q(QMdiSubWindow);
+ if (!q->mask().isEmpty())
+ q->clearMask();
+
+ if (!q->parent())
+ return;
+
+ if ((q->isMaximized() && !drawTitleBarWhenMaximized())
+ || q->windowFlags() & Qt::FramelessWindowHint)
+ return;
+
+ if (resizeTimerId == -1)
+ cachedStyleOptions = titleBarOptions();
+ cachedStyleOptions.rect = q->rect();
+ QStyleHintReturnMask frameMask;
+ q->style()->styleHint(QStyle::SH_WindowFrame_Mask, &cachedStyleOptions, q, &frameMask);
+ if (!frameMask.region.isEmpty())
+ q->setMask(frameMask.region);
+}
+
+/*!
+ \internal
+*/
+void QMdiSubWindowPrivate::setNewGeometry(const QPoint &pos)
+{
+ Q_Q(QMdiSubWindow);
+ Q_ASSERT(currentOperation != None);
+ Q_ASSERT(q->parent());
+
+ uint cflags = operationMap.find(currentOperation).value().changeFlags;
+ int posX = pos.x();
+ int posY = pos.y();
+
+ const bool restrictHorizontal = !q->testOption(QMdiSubWindow::AllowOutsideAreaHorizontally);
+ const bool restrictVertical = !q->testOption(QMdiSubWindow::AllowOutsideAreaVertically);
+
+ if (restrictHorizontal || restrictVertical) {
+ QRect parentRect = q->parentWidget()->rect();
+ if (restrictVertical && (cflags & VResizeReverse || currentOperation == Move)) {
+ posY = qMin(qMax(mousePressPosition.y() - oldGeometry.y(), posY),
+ parentRect.height() - BoundaryMargin);
+ }
+ if (currentOperation == Move) {
+ if (restrictHorizontal)
+ posX = qMin(qMax(BoundaryMargin, posX), parentRect.width() - BoundaryMargin);
+ if (restrictVertical)
+ posY = qMin(posY, parentRect.height() - BoundaryMargin);
+ } else {
+ if (restrictHorizontal) {
+ if (cflags & HResizeReverse)
+ posX = qMax(mousePressPosition.x() - oldGeometry.x(), posX);
+ else
+ posX = qMin(parentRect.width() - (oldGeometry.x() + oldGeometry.width()
+ - mousePressPosition.x()), posX);
+ }
+ if (restrictVertical && !(cflags & VResizeReverse)) {
+ posY = qMin(parentRect.height() - (oldGeometry.y() + oldGeometry.height()
+ - mousePressPosition.y()), posY);
+ }
+ }
+ }
+
+ QRect geometry;
+ if (cflags & (HMove | VMove)) {
+ int dx = getMoveDeltaComponent(cflags, HMove, HResize, posX - mousePressPosition.x(),
+ oldGeometry.width() - internalMinimumSize.width(),
+ oldGeometry.width() - q->maximumWidth());
+ int dy = getMoveDeltaComponent(cflags, VMove, VResize, posY - mousePressPosition.y(),
+ oldGeometry.height() - internalMinimumSize.height(),
+ oldGeometry.height() - q->maximumHeight());
+ geometry.setTopLeft(oldGeometry.topLeft() + QPoint(dx, dy));
+ } else {
+ geometry.setTopLeft(q->geometry().topLeft());
+ }
+
+ if (cflags & (HResize | VResize)) {
+ int dx = getResizeDeltaComponent(cflags, HResize, HResizeReverse,
+ posX - mousePressPosition.x());
+ int dy = getResizeDeltaComponent(cflags, VResize, VResizeReverse,
+ posY - mousePressPosition.y());
+ geometry.setSize(oldGeometry.size() + QSize(dx, dy));
+ } else {
+ geometry.setSize(q->geometry().size());
+ }
+
+ setNewGeometry(&geometry);
+}
+
+/*!
+ \internal
+*/
+void QMdiSubWindowPrivate::setMinimizeMode()
+{
+ Q_Q(QMdiSubWindow);
+ Q_ASSERT(q->parent());
+
+ ensureWindowState(Qt::WindowMinimized);
+ isShadeRequestFromMinimizeMode = true;
+ q->showShaded();
+ isShadeRequestFromMinimizeMode = false;
+
+ moveEnabled = false;
+#ifndef QT_NO_ACTION
+ setEnabled(MoveAction, moveEnabled);
+#endif
+
+ Q_ASSERT(q->windowState() & Qt::WindowMinimized);
+ Q_ASSERT(!(q->windowState() & Qt::WindowMaximized));
+ // This should be a valid assert, but people can actually re-implement
+ // setVisible and do crazy stuff, so we're not guaranteed that
+ // the widget is hidden after calling hide().
+ // Q_ASSERT(baseWidget ? baseWidget->isHidden() : true);
+
+ setActive(true);
+}
+
+/*!
+ \internal
+*/
+void QMdiSubWindowPrivate::setNormalMode()
+{
+ Q_Q(QMdiSubWindow);
+ Q_ASSERT(q->parent());
+
+ isShadeMode = false;
+ isMaximizeMode = false;
+
+ ensureWindowState(Qt::WindowNoState);
+#ifndef QT_NO_MENUBAR
+ removeButtonsFromMenuBar();
+#endif
+
+ // Hide the window before we change the geometry to avoid multiple resize
+ // events and wrong window state.
+ const bool wasVisible = q->isVisible();
+ if (wasVisible)
+ q->setVisible(false);
+
+ // Restore minimum size if set by user.
+ if (!userMinimumSize.isNull()) {
+ q->setMinimumSize(userMinimumSize);
+ userMinimumSize = QSize(0, 0);
+ }
+
+ // Show the internal widget if it was hidden by us,
+ if (baseWidget && isWidgetHiddenByUs) {
+ baseWidget->show();
+ isWidgetHiddenByUs = false;
+ }
+
+ updateGeometryConstraints();
+ QRect newGeometry = oldGeometry;
+ newGeometry.setSize(restoreSize.expandedTo(internalMinimumSize));
+ q->setGeometry(newGeometry);
+
+ if (wasVisible)
+ q->setVisible(true);
+
+ // Invalidate the restore size.
+ restoreSize.setWidth(-1);
+ restoreSize.setHeight(-1);
+
+#ifndef QT_NO_SIZEGRIP
+ setSizeGripVisible(true);
+#endif
+
+#ifndef QT_NO_ACTION
+ setEnabled(MoveAction, true);
+ setEnabled(MaximizeAction, true);
+ setEnabled(MinimizeAction, true);
+ setEnabled(RestoreAction, false);
+ setEnabled(ResizeAction, resizeEnabled);
+#endif // QT_NO_ACTION
+
+ Q_ASSERT(!(q_func()->windowState() & Qt::WindowMinimized));
+ // This sub-window can be maximized when shown above if not the
+ // QMdiArea::DontMaximizeSubWindowOnActionvation is set. Make sure
+ // the Qt::WindowMaximized flag is set accordingly.
+ Q_ASSERT((isMaximizeMode && q_func()->windowState() & Qt::WindowMaximized)
+ || (!isMaximizeMode && !(q_func()->windowState() & Qt::WindowMaximized)));
+ Q_ASSERT(!isShadeMode);
+
+ setActive(true);
+ restoreFocus();
+ updateMask();
+}
+
+/*!
+ \internal
+*/
+void QMdiSubWindowPrivate::setMaximizeMode()
+{
+ Q_Q(QMdiSubWindow);
+ Q_ASSERT(q->parent());
+
+ ensureWindowState(Qt::WindowMaximized);
+ isShadeMode = false;
+ isMaximizeMode = true;
+
+ if (!restoreFocusWidget && q->isAncestorOf(QApplication::focusWidget()))
+ restoreFocusWidget = QApplication::focusWidget();
+
+#ifndef QT_NO_SIZEGRIP
+ setSizeGripVisible(false);
+#endif
+
+ // Store old geometry and set restore size if not already set.
+ if (!restoreSize.isValid()) {
+ oldGeometry = q->geometry();
+ restoreSize.setWidth(oldGeometry.width());
+ restoreSize.setHeight(oldGeometry.height());
+ }
+
+ // Hide the window before we change the geometry to avoid multiple resize
+ // events and wrong window state.
+ const bool wasVisible = q->isVisible();
+ if (wasVisible)
+ q->setVisible(false);
+
+ // Show the internal widget if it was hidden by us.
+ if (baseWidget && isWidgetHiddenByUs) {
+ baseWidget->show();
+ isWidgetHiddenByUs = false;
+ }
+
+ updateGeometryConstraints();
+
+ if (wasVisible) {
+#ifndef QT_NO_MENUBAR
+ if (QMenuBar *mBar = menuBar())
+ showButtonsInMenuBar(mBar);
+ else
+#endif
+ if (!controlContainer)
+ controlContainer = new ControlContainer(q);
+ }
+
+ QWidget *parent = q->parentWidget();
+ QRect availableRect = parent->contentsRect();
+
+ // Adjust geometry if the sub-window is inside a scroll area.
+ QAbstractScrollArea *scrollArea = qobject_cast<QAbstractScrollArea *>(parent->parentWidget());
+ if (scrollArea && scrollArea->viewport() == parent) {
+ QScrollBar *hbar = scrollArea->horizontalScrollBar();
+ QScrollBar *vbar = scrollArea->verticalScrollBar();
+ const int xOffset = hbar ? hbar->value() : 0;
+ const int yOffset = vbar ? vbar->value() : 0;
+ availableRect.adjust(-xOffset, -yOffset, -xOffset, -yOffset);
+ oldGeometry.adjust(xOffset, yOffset, xOffset, yOffset);
+ }
+
+ setNewGeometry(&availableRect);
+ // QWidget::setGeometry will reset Qt::WindowMaximized so we have to update it here.
+ ensureWindowState(Qt::WindowMaximized);
+
+ if (wasVisible)
+ q->setVisible(true);
+
+ resizeEnabled = false;
+ moveEnabled = false;
+
+#ifndef QT_NO_ACTION
+ setEnabled(MoveAction, moveEnabled);
+ setEnabled(MaximizeAction, false);
+ setEnabled(MinimizeAction, true);
+ setEnabled(RestoreAction, true);
+ setEnabled(ResizeAction, resizeEnabled);
+#endif // QT_NO_ACTION
+
+ Q_ASSERT(q->windowState() & Qt::WindowMaximized);
+ Q_ASSERT(!(q->windowState() & Qt::WindowMinimized));
+
+ restoreFocus();
+ updateMask();
+}
+
+/*!
+ \internal
+*/
+void QMdiSubWindowPrivate::setActive(bool activate, bool changeFocus)
+{
+ Q_Q(QMdiSubWindow);
+ if (!q->parent() || !activationEnabled)
+ return;
+
+ if (activate && !isActive && q->isEnabled()) {
+ isActive = true;
+ isExplicitlyDeactivated = false;
+ Qt::WindowStates oldWindowState = q->windowState();
+ ensureWindowState(Qt::WindowActive);
+ emit q->aboutToActivate();
+#ifndef QT_NO_MENUBAR
+ if (QMenuBar *mBar = menuBar())
+ showButtonsInMenuBar(mBar);
+#endif
+ Q_ASSERT(isActive);
+ emit q->windowStateChanged(oldWindowState, q->windowState());
+ } else if (!activate && isActive) {
+ isActive = false;
+ Qt::WindowStates oldWindowState = q->windowState();
+ q->overrideWindowState(q->windowState() & ~Qt::WindowActive);
+ if (changeFocus) {
+ QWidget *focusWidget = QApplication::focusWidget();
+ if (focusWidget && (focusWidget == q || q->isAncestorOf(focusWidget)))
+ focusWidget->clearFocus();
+ }
+ if (baseWidget)
+ baseWidget->overrideWindowState(baseWidget->windowState() & ~Qt::WindowActive);
+ Q_ASSERT(!isActive);
+ emit q->windowStateChanged(oldWindowState, q->windowState());
+ }
+
+ if (activate && isActive && q->isEnabled() && !q->hasFocus()
+ && !q->isAncestorOf(QApplication::focusWidget())) {
+ if (changeFocus)
+ setFocusWidget();
+ ensureWindowState(Qt::WindowActive);
+ }
+
+ int frameWidth = q->style()->pixelMetric(QStyle::PM_MdiSubWindowFrameWidth, 0, q);
+ int titleBarHeight = this->titleBarHeight();
+ QRegion windowDecoration = QRegion(0, 0, q->width(), q->height());
+ windowDecoration -= QRegion(frameWidth, titleBarHeight, q->width() - 2 * frameWidth,
+ q->height() - titleBarHeight - frameWidth);
+
+ // Make sure we don't use cached style options if we get
+ // resize events right before activation/deactivation.
+ if (resizeTimerId != -1) {
+ q->killTimer(resizeTimerId);
+ resizeTimerId = -1;
+ updateDirtyRegions();
+ }
+
+ q->update(windowDecoration);
+}
+
+/*!
+ \internal
+*/
+void QMdiSubWindowPrivate::processClickedSubControl()
+{
+ Q_Q(QMdiSubWindow);
+ switch (activeSubControl) {
+ case QStyle::SC_TitleBarContextHelpButton:
+#ifndef QT_NO_WHATSTHIS
+ QWhatsThis::enterWhatsThisMode();
+#endif
+ break;
+ case QStyle::SC_TitleBarShadeButton:
+ q->showShaded();
+ hoveredSubControl = QStyle::SC_TitleBarUnshadeButton;
+ break;
+ case QStyle::SC_TitleBarUnshadeButton:
+ if (q->isShaded())
+ hoveredSubControl = QStyle::SC_TitleBarShadeButton;
+ q->showNormal();
+ break;
+ case QStyle::SC_TitleBarMinButton:
+#if defined(Q_WS_MAC) && !defined(QT_NO_STYLE_MAC)
+ if (qobject_cast<QMacStyle *>(q->style())) {
+ if (q->isMinimized())
+ q->showNormal();
+ else
+ q->showMinimized();
+ break;
+ }
+#endif
+ q->showMinimized();
+ break;
+ case QStyle::SC_TitleBarNormalButton:
+ if (q->isShaded())
+ hoveredSubControl = QStyle::SC_TitleBarMinButton;
+ q->showNormal();
+ break;
+ case QStyle::SC_TitleBarMaxButton:
+#if defined(Q_WS_MAC) && !defined(QT_NO_STYLE_MAC)
+ if (qobject_cast<QMacStyle *>(q->style())) {
+ if (q->isMaximized())
+ q->showNormal();
+ else
+ q->showMaximized();
+ break;
+ }
+#endif
+ q->showMaximized();
+ break;
+ case QStyle::SC_TitleBarCloseButton:
+ q->close();
+ break;
+ default:
+ break;
+ }
+}
+
+/*!
+ \internal
+*/
+QRegion QMdiSubWindowPrivate::getRegion(Operation operation) const
+{
+ Q_Q(const QMdiSubWindow);
+ int width = q->width();
+ int height = q->height();
+ int titleBarHeight = this->titleBarHeight();
+ int frameWidth = q->style()->pixelMetric(QStyle::PM_MdiSubWindowFrameWidth, 0, q);
+ int cornerConst = titleBarHeight - frameWidth;
+ int titleBarConst = 2 * titleBarHeight;
+
+ if (operation == Move) {
+ QStyleOptionTitleBar titleBarOptions = this->titleBarOptions();
+ QRegion move(frameWidth, frameWidth, width - 2 * frameWidth, cornerConst);
+ // Depending on which window flags are set, activated sub controllers will
+ // be subtracted from the 'move' region.
+ for (int i = 0; i < NumSubControls; ++i) {
+ if (SubControls[i] == QStyle::SC_TitleBarLabel)
+ continue;
+ move -= QRegion(q->style()->subControlRect(QStyle::CC_TitleBar, &titleBarOptions,
+ SubControls[i]));
+ }
+ return move;
+ }
+
+ QRegion region;
+#if defined(Q_WS_MAC) && !defined(QT_NO_STYLE_MAC)
+ if (qobject_cast<QMacStyle *>(q->style()))
+ return region;
+#endif
+
+ switch (operation) {
+ case TopResize:
+ region = QRegion(titleBarHeight, 0, width - titleBarConst, frameWidth);
+ break;
+ case BottomResize:
+ region = QRegion(titleBarHeight, height - frameWidth, width - titleBarConst, frameWidth);
+ break;
+ case LeftResize:
+ region = QRegion(0, titleBarHeight, frameWidth, height - titleBarConst);
+ break;
+ case RightResize:
+ region = QRegion(width - frameWidth, titleBarHeight, frameWidth, height - titleBarConst);
+ break;
+ case TopLeftResize:
+ region = QRegion(0, 0, titleBarHeight, titleBarHeight)
+ - QRegion(frameWidth, frameWidth, cornerConst, cornerConst);
+ break;
+ case TopRightResize:
+ region = QRegion(width - titleBarHeight, 0, titleBarHeight, titleBarHeight)
+ - QRegion(width - titleBarHeight, frameWidth, cornerConst, cornerConst);
+ break;
+ case BottomLeftResize:
+ region = QRegion(0, height - titleBarHeight, titleBarHeight, titleBarHeight)
+ - QRegion(frameWidth, height - titleBarHeight, cornerConst, cornerConst);
+ break;
+ case BottomRightResize:
+ region = QRegion(width - titleBarHeight, height - titleBarHeight, titleBarHeight, titleBarHeight)
+ - QRegion(width - titleBarHeight, height - titleBarHeight, cornerConst, cornerConst);
+ break;
+ default:
+ break;
+ }
+
+ return region;
+}
+
+/*!
+ \internal
+*/
+QMdiSubWindowPrivate::Operation QMdiSubWindowPrivate::getOperation(const QPoint &pos) const
+{
+ OperationInfoMap::const_iterator it;
+ for (it = operationMap.constBegin(); it != operationMap.constEnd(); ++it)
+ if (it.value().region.contains(pos))
+ return it.key();
+ return None;
+}
+
+extern QString qt_setWindowTitle_helperHelper(const QString&, const QWidget*);
+
+/*!
+ \internal
+*/
+QStyleOptionTitleBar QMdiSubWindowPrivate::titleBarOptions() const
+{
+ Q_Q(const QMdiSubWindow);
+ QStyleOptionTitleBar titleBarOptions;
+ titleBarOptions.initFrom(q);
+ if (activeSubControl != QStyle::SC_None) {
+ if (hoveredSubControl == activeSubControl) {
+ titleBarOptions.state |= QStyle::State_Sunken;
+ titleBarOptions.activeSubControls = activeSubControl;
+ }
+ } else if (autoRaise() && hoveredSubControl != QStyle::SC_None
+ && hoveredSubControl != QStyle::SC_TitleBarLabel) {
+ titleBarOptions.state |= QStyle::State_MouseOver;
+ titleBarOptions.activeSubControls = hoveredSubControl;
+ } else {
+ titleBarOptions.state &= ~QStyle::State_MouseOver;
+ titleBarOptions.activeSubControls = QStyle::SC_None;
+ }
+
+ titleBarOptions.subControls = QStyle::SC_All;
+ titleBarOptions.titleBarFlags = q->windowFlags();
+ titleBarOptions.titleBarState = q->windowState();
+ titleBarOptions.palette = titleBarPalette;
+ titleBarOptions.icon = menuIcon;
+
+ if (isActive) {
+ titleBarOptions.state |= QStyle::State_Active;
+ titleBarOptions.titleBarState |= QStyle::State_Active;
+ titleBarOptions.palette.setCurrentColorGroup(QPalette::Active);
+ } else {
+ titleBarOptions.state &= ~QStyle::State_Active;
+ titleBarOptions.palette.setCurrentColorGroup(QPalette::Inactive);
+ }
+
+ int border = hasBorder(titleBarOptions) ? 4 : 0;
+ int paintHeight = titleBarHeight(titleBarOptions);
+ paintHeight -= q->isMinimized() ? 2 * border : border;
+ titleBarOptions.rect = QRect(border, border, q->width() - 2 * border, paintHeight);
+
+ if (!windowTitle.isEmpty()) {
+ // Set the text here before asking for the width of the title bar label
+ // in case people uses the actual text to calculate the width.
+ titleBarOptions.text = windowTitle;
+ titleBarOptions.fontMetrics = QFontMetrics(font);
+ int width = q->style()->subControlRect(QStyle::CC_TitleBar, &titleBarOptions,
+ QStyle::SC_TitleBarLabel, q).width();
+ // Set elided text if we don't have enough space for the entire title.
+ titleBarOptions.text = titleBarOptions.fontMetrics.elidedText(windowTitle, Qt::ElideRight, width);
+ }
+ return titleBarOptions;
+}
+
+/*!
+ \internal
+*/
+void QMdiSubWindowPrivate::ensureWindowState(Qt::WindowState state)
+{
+ Q_Q(QMdiSubWindow);
+ Qt::WindowStates windowStates = q->windowState() | state;
+ switch (state) {
+ case Qt::WindowMinimized:
+ windowStates &= ~Qt::WindowMaximized;
+ windowStates &= ~Qt::WindowNoState;
+ break;
+ case Qt::WindowMaximized:
+ windowStates &= ~Qt::WindowMinimized;
+ windowStates &= ~Qt::WindowNoState;
+ break;
+ case Qt::WindowNoState:
+ windowStates &= ~Qt::WindowMinimized;
+ windowStates &= ~Qt::WindowMaximized;
+ break;
+ default:
+ break;
+ }
+ if (baseWidget) {
+ if (!(baseWidget->windowState() & Qt::WindowActive) && windowStates & Qt::WindowActive)
+ baseWidget->overrideWindowState(windowStates & ~Qt::WindowActive);
+ else
+ baseWidget->overrideWindowState(windowStates);
+ }
+ q->overrideWindowState(windowStates);
+}
+
+/*!
+ \internal
+*/
+int QMdiSubWindowPrivate::titleBarHeight(const QStyleOptionTitleBar &options) const
+{
+ Q_Q(const QMdiSubWindow);
+ if (!q->parent() || q->windowFlags() & Qt::FramelessWindowHint
+ || (q->isMaximized() && !drawTitleBarWhenMaximized())) {
+ return 0;
+ }
+
+ int height = q->style()->pixelMetric(QStyle::PM_TitleBarHeight, &options, q);
+#if defined(Q_WS_MAC) && !defined(QT_NO_STYLE_MAC)
+ // ### Fix mac style, the +4 pixels hack is not necessary anymore
+ if (qobject_cast<QMacStyle *>(q->style()))
+ height -= 4;
+#endif
+ if (hasBorder(options))
+ height += q->isMinimized() ? 8 : 4;
+ return height;
+}
+
+/*!
+ \internal
+*/
+void QMdiSubWindowPrivate::sizeParameters(int *margin, int *minWidth) const
+{
+ Q_Q(const QMdiSubWindow);
+ Qt::WindowFlags flags = q->windowFlags();
+ if (!q->parent() || flags & Qt::FramelessWindowHint) {
+ *margin = 0;
+ *minWidth = 0;
+ return;
+ }
+
+ if (q->isMaximized() && !drawTitleBarWhenMaximized())
+ *margin = 0;
+ else
+ *margin = q->style()->pixelMetric(QStyle::PM_MdiSubWindowFrameWidth, 0, q);
+
+ QStyleOptionTitleBar opt = this->titleBarOptions();
+ int tempWidth = 0;
+ for (int i = 0; i < NumSubControls; ++i) {
+ if (SubControls[i] == QStyle::SC_TitleBarLabel) {
+ tempWidth += 30;
+ continue;
+ }
+ QRect rect = q->style()->subControlRect(QStyle::CC_TitleBar, &opt, SubControls[i], q);
+ if (!rect.isValid())
+ continue;
+ tempWidth += rect.width();
+ }
+ *minWidth = tempWidth;
+}
+
+/*!
+ \internal
+*/
+bool QMdiSubWindowPrivate::drawTitleBarWhenMaximized() const
+{
+ Q_Q(const QMdiSubWindow);
+ if (q->window()->testAttribute(Qt::WA_CanHostQMdiSubWindowTitleBar))
+ return false;
+
+ if (isChildOfTabbedQMdiArea(q))
+ return false;
+
+#if defined(Q_WS_MAC) && !defined(QT_NO_STYLE_MAC) || defined(Q_OS_WINCE_WM)
+ return true;
+#else
+ if (q->style()->styleHint(QStyle::SH_Workspace_FillSpaceOnMaximize, 0, q))
+ return true;
+#if defined(QT_NO_MENUBAR) || defined(QT_NO_MAINWINDOW)
+ return true;
+#else
+ QMainWindow *mainWindow = qobject_cast<QMainWindow *>(q->window());
+ if (!mainWindow || !qobject_cast<QMenuBar *>(mainWindow->menuWidget())
+ || mainWindow->menuWidget()->isHidden())
+ return true;
+
+ return isChildOfQMdiSubWindow(q);
+#endif
+#endif
+}
+
+#ifndef QT_NO_MENUBAR
+
+/*!
+ \internal
+*/
+void QMdiSubWindowPrivate::showButtonsInMenuBar(QMenuBar *menuBar)
+{
+ Q_Q(QMdiSubWindow);
+ Q_ASSERT(q->isMaximized() && !drawTitleBarWhenMaximized());
+
+ if (isChildOfTabbedQMdiArea(q))
+ return;
+
+ removeButtonsFromMenuBar();
+ if (!controlContainer)
+ controlContainer = new ControlContainer(q);
+
+ ignoreWindowTitleChange = true;
+ controlContainer->showButtonsInMenuBar(menuBar);
+ ignoreWindowTitleChange = false;
+
+ QWidget *topLevelWindow = q->window();
+ topLevelWindow->setWindowModified(q->isWindowModified());
+ topLevelWindow->installEventFilter(q);
+
+ int buttonHeight = 0;
+ if (controlContainer->controllerWidget())
+ buttonHeight = controlContainer->controllerWidget()->height();
+ else if (controlContainer->systemMenuLabel())
+ buttonHeight = controlContainer->systemMenuLabel()->height();
+
+ // This will rarely happen.
+ if (menuBar && menuBar->height() < buttonHeight
+ && topLevelWindow->layout()) {
+ // Make sure topLevelWindow->contentsRect returns correct geometry.
+ // topLevelWidget->updateGeoemtry will not do the trick here since it will post the event.
+ QEvent event(QEvent::LayoutRequest);
+ QApplication::sendEvent(topLevelWindow, &event);
+ }
+}
+
+/*!
+ \internal
+*/
+void QMdiSubWindowPrivate::removeButtonsFromMenuBar()
+{
+ Q_Q(QMdiSubWindow);
+
+ if (!controlContainer || isChildOfTabbedQMdiArea(q))
+ return;
+
+ QMenuBar *currentMenuBar = 0;
+#ifndef QT_NO_MAINWINDOW
+ if (QMainWindow *mainWindow = qobject_cast<QMainWindow *>(q->window())) {
+ // NB! We can't use menuBar() here because that one will actually create
+ // a menubar for us if not set. That's not what we want :-)
+ currentMenuBar = qobject_cast<QMenuBar *>(mainWindow->menuWidget());
+ }
+#endif
+
+ ignoreWindowTitleChange = true;
+ controlContainer->removeButtonsFromMenuBar(currentMenuBar);
+ ignoreWindowTitleChange = false;
+
+ QWidget *topLevelWindow = q->window();
+ topLevelWindow->removeEventFilter(q);
+ if (baseWidget && !drawTitleBarWhenMaximized())
+ topLevelWindow->setWindowModified(false);
+ originalTitle = QString::null;
+}
+
+#endif // QT_NO_MENUBAR
+
+void QMdiSubWindowPrivate::updateWindowTitle(bool isRequestFromChild)
+{
+ Q_Q(QMdiSubWindow);
+ if (isRequestFromChild && !q->windowTitle().isEmpty() && !lastChildWindowTitle.isEmpty()
+ && lastChildWindowTitle != q->windowTitle()) {
+ return;
+ }
+
+ QWidget *titleWidget = 0;
+ if (isRequestFromChild)
+ titleWidget = baseWidget;
+ else
+ titleWidget = q;
+ if (!titleWidget || titleWidget->windowTitle().isEmpty())
+ return;
+
+ ignoreWindowTitleChange = true;
+ q->setWindowTitle(titleWidget->windowTitle());
+ if (q->maximizedButtonsWidget())
+ setNewWindowTitle();
+ ignoreWindowTitleChange = false;
+}
+
+#ifndef QT_NO_RUBBERBAND
+void QMdiSubWindowPrivate::enterRubberBandMode()
+{
+ Q_Q(QMdiSubWindow);
+ if (q->isMaximized())
+ return;
+ Q_ASSERT(oldGeometry.isValid());
+ Q_ASSERT(q->parent());
+ if (!rubberBand) {
+ rubberBand = new QRubberBand(QRubberBand::Rectangle, q->parentWidget());
+ // For accessibility to identify this special widget.
+ rubberBand->setObjectName(QLatin1String("qt_rubberband"));
+ }
+ QPoint rubberBandPos = q->mapToParent(QPoint(0, 0));
+ rubberBand->setGeometry(rubberBandPos.x(), rubberBandPos.y(),
+ oldGeometry.width(), oldGeometry.height());
+ rubberBand->show();
+ isInRubberBandMode = true;
+ q->grabMouse();
+}
+
+void QMdiSubWindowPrivate::leaveRubberBandMode()
+{
+ Q_Q(QMdiSubWindow);
+ Q_ASSERT(rubberBand);
+ Q_ASSERT(isInRubberBandMode);
+ q->releaseMouse();
+ isInRubberBandMode = false;
+ q->setGeometry(rubberBand->geometry());
+ rubberBand->hide();
+ currentOperation = None;
+}
+#endif // QT_NO_RUBBERBAND
+
+// Taken from the old QWorkspace (::readColors())
+QPalette QMdiSubWindowPrivate::desktopPalette() const
+{
+ Q_Q(const QMdiSubWindow);
+ QPalette newPalette = q->palette();
+
+ bool colorsInitialized = false;
+#ifdef Q_WS_WIN // ask system properties on windows
+#ifndef SPI_GETGRADIENTCAPTIONS
+#define SPI_GETGRADIENTCAPTIONS 0x1008
+#endif
+#ifndef COLOR_GRADIENTACTIVECAPTION
+#define COLOR_GRADIENTACTIVECAPTION 27
+#endif
+#ifndef COLOR_GRADIENTINACTIVECAPTION
+#define COLOR_GRADIENTINACTIVECAPTION 28
+#endif
+ if (QApplication::desktopSettingsAware()) {
+ newPalette.setColor(QPalette::Active, QPalette::Highlight,
+ colorref2qrgb(GetSysColor(COLOR_ACTIVECAPTION)));
+ newPalette.setColor(QPalette::Inactive, QPalette::Highlight,
+ colorref2qrgb(GetSysColor(COLOR_INACTIVECAPTION)));
+ newPalette.setColor(QPalette::Active, QPalette::HighlightedText,
+ colorref2qrgb(GetSysColor(COLOR_CAPTIONTEXT)));
+ newPalette.setColor(QPalette::Inactive, QPalette::HighlightedText,
+ colorref2qrgb(GetSysColor(COLOR_INACTIVECAPTIONTEXT)));
+ if (QSysInfo::WindowsVersion != QSysInfo::WV_95
+ && QSysInfo::WindowsVersion != QSysInfo::WV_NT) {
+ colorsInitialized = true;
+ BOOL hasGradient;
+ QT_WA({
+ SystemParametersInfo(SPI_GETGRADIENTCAPTIONS, 0, &hasGradient, 0);
+ } , {
+ SystemParametersInfoA(SPI_GETGRADIENTCAPTIONS, 0, &hasGradient, 0);
+ });
+ if (hasGradient) {
+ newPalette.setColor(QPalette::Active, QPalette::Base,
+ colorref2qrgb(GetSysColor(COLOR_GRADIENTACTIVECAPTION)));
+ newPalette.setColor(QPalette::Inactive, QPalette::Base,
+ colorref2qrgb(GetSysColor(COLOR_GRADIENTINACTIVECAPTION)));
+ } else {
+ newPalette.setColor(QPalette::Active, QPalette::Base,
+ newPalette.color(QPalette::Active, QPalette::Highlight));
+ newPalette.setColor(QPalette::Inactive, QPalette::Base,
+ newPalette.color(QPalette::Inactive, QPalette::Highlight));
+ }
+ }
+ }
+#endif // Q_WS_WIN
+ if (!colorsInitialized) {
+ newPalette.setColor(QPalette::Active, QPalette::Highlight,
+ newPalette.color(QPalette::Active, QPalette::Highlight));
+ newPalette.setColor(QPalette::Active, QPalette::Base,
+ newPalette.color(QPalette::Active, QPalette::Highlight));
+ newPalette.setColor(QPalette::Inactive, QPalette::Highlight,
+ newPalette.color(QPalette::Inactive, QPalette::Dark));
+ newPalette.setColor(QPalette::Inactive, QPalette::Base,
+ newPalette.color(QPalette::Inactive, QPalette::Dark));
+ newPalette.setColor(QPalette::Inactive, QPalette::HighlightedText,
+ newPalette.color(QPalette::Inactive, QPalette::Window));
+ }
+
+ return newPalette;
+}
+
+void QMdiSubWindowPrivate::updateActions()
+{
+ Qt::WindowFlags windowFlags = q_func()->windowFlags();
+ // Hide all
+ for (int i = 0; i < NumWindowStateActions; ++i)
+ setVisible(WindowStateAction(i), false);
+
+ if (windowFlags & Qt::FramelessWindowHint)
+ return;
+
+ setVisible(StayOnTopAction, true);
+ setVisible(MoveAction, moveEnabled);
+ setVisible(ResizeAction, resizeEnabled);
+
+ // CloseAction
+ if (windowFlags & Qt::WindowSystemMenuHint)
+ setVisible(CloseAction, true);
+
+ // RestoreAction
+ if (windowFlags & (Qt::WindowMinimizeButtonHint | Qt::WindowMaximizeButtonHint))
+ setVisible(RestoreAction, true);
+
+ // MinimizeAction
+ if (windowFlags & Qt::WindowMinimizeButtonHint)
+ setVisible(MinimizeAction, true);
+
+ // MaximizeAction
+ if (windowFlags & Qt::WindowMaximizeButtonHint)
+ setVisible(MaximizeAction, true);
+}
+
+void QMdiSubWindowPrivate::setFocusWidget()
+{
+ Q_Q(QMdiSubWindow);
+ if (!baseWidget) {
+ q->setFocus();
+ return;
+ }
+
+ // This will give focus to the next child if possible, otherwise
+ // do nothing, hence it's not possible to tab between windows with
+ // just hitting tab (unless Qt::TabFocus is removed from the focus policy).
+ if (focusInReason == Qt::TabFocusReason) {
+ q->focusNextChild();
+ return;
+ }
+
+ // Same as above, but gives focus to the previous child.
+ if (focusInReason == Qt::BacktabFocusReason) {
+ q->focusPreviousChild();
+ return;
+ }
+
+ if (QWidget *focusWidget = baseWidget->focusWidget()) {
+ if (!focusWidget->hasFocus() && q->isAncestorOf(focusWidget)
+ && focusWidget->isVisible() && !q->isMinimized()
+ && focusWidget->focusPolicy() != Qt::NoFocus) {
+ focusWidget->setFocus();
+ } else {
+ q->setFocus();
+ }
+ return;
+ }
+
+ QWidget *focusWidget = q->nextInFocusChain();
+ while (focusWidget && focusWidget != q && focusWidget->focusPolicy() == Qt::NoFocus)
+ focusWidget = focusWidget->nextInFocusChain();
+ if (focusWidget && q->isAncestorOf(focusWidget))
+ focusWidget->setFocus();
+ else if (baseWidget->focusPolicy() != Qt::NoFocus)
+ baseWidget->setFocus();
+ else if (!q->hasFocus())
+ q->setFocus();
+}
+
+void QMdiSubWindowPrivate::restoreFocus()
+{
+ if (!restoreFocusWidget)
+ return;
+ if (!restoreFocusWidget->hasFocus() && q_func()->isAncestorOf(restoreFocusWidget)
+ && restoreFocusWidget->isVisible()
+ && restoreFocusWidget->focusPolicy() != Qt::NoFocus) {
+ restoreFocusWidget->setFocus();
+ }
+ restoreFocusWidget = 0;
+}
+
+/*!
+ \internal
+ ### Please add QEvent::WindowFlagsChange event
+*/
+void QMdiSubWindowPrivate::setWindowFlags(Qt::WindowFlags windowFlags)
+{
+ Q_Q(QMdiSubWindow);
+ if (!q->parent()) {
+ q->setWindowFlags(windowFlags);
+ return;
+ }
+
+ Qt::WindowFlags windowType = windowFlags & Qt::WindowType_Mask;
+ if (windowType == Qt::Dialog || windowFlags & Qt::MSWindowsFixedSizeDialogHint)
+ windowFlags |= Qt::WindowTitleHint | Qt::WindowSystemMenuHint;
+
+ // Set standard flags if none of the customize flags are set
+ if (!(windowFlags & CustomizeWindowFlags))
+ windowFlags |= Qt::WindowTitleHint | Qt::WindowSystemMenuHint | Qt::WindowMinMaxButtonsHint | Qt::WindowCloseButtonHint;
+ else if (windowFlags & Qt::FramelessWindowHint && windowFlags & Qt::WindowStaysOnTopHint)
+ windowFlags = Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint;
+ else if (windowFlags & Qt::FramelessWindowHint)
+ windowFlags = Qt::FramelessWindowHint;
+
+ windowFlags &= ~windowType;
+ windowFlags |= Qt::SubWindow;
+
+#ifndef QT_NO_ACTION
+ if (QAction *stayOnTopAction = actions[QMdiSubWindowPrivate::StayOnTopAction]) {
+ if (windowFlags & Qt::WindowStaysOnTopHint)
+ stayOnTopAction->setChecked(true);
+ else
+ stayOnTopAction->setChecked(false);
+ }
+#endif
+
+#ifndef QT_NO_SIZEGRIP
+ if ((windowFlags & Qt::FramelessWindowHint) && sizeGrip)
+ delete sizeGrip;
+#endif
+
+ q->setWindowFlags(windowFlags);
+ updateGeometryConstraints();
+ updateActions();
+ QSize currentSize = q->size();
+ if (q->isVisible() && (currentSize.width() < internalMinimumSize.width()
+ || currentSize.height() < internalMinimumSize.height())) {
+ q->resize(currentSize.expandedTo(internalMinimumSize));
+ }
+}
+
+void QMdiSubWindowPrivate::setVisible(WindowStateAction action, bool visible)
+{
+#ifndef QT_NO_ACTION
+ if (actions[action])
+ actions[action]->setVisible(visible);
+#endif
+
+ Q_Q(QMdiSubWindow);
+ if (!controlContainer)
+ controlContainer = new ControlContainer(q);
+
+ if (ControllerWidget *ctrlWidget = qobject_cast<ControllerWidget *>
+ (controlContainer->controllerWidget())) {
+ ctrlWidget->setControlVisible(action, visible);
+ }
+}
+
+#ifndef QT_NO_ACTION
+void QMdiSubWindowPrivate::setEnabled(WindowStateAction action, bool enable)
+{
+ if (actions[action])
+ actions[action]->setEnabled(enable);
+}
+
+#ifndef QT_NO_MENU
+void QMdiSubWindowPrivate::addToSystemMenu(WindowStateAction action, const QString &text,
+ const char *slot)
+{
+ if (!systemMenu)
+ return;
+ actions[action] = systemMenu->addAction(text, q_func(), slot);
+}
+#endif
+#endif // QT_NO_ACTION
+
+/*!
+ \internal
+*/
+QSize QMdiSubWindowPrivate::iconSize() const
+{
+ Q_Q(const QMdiSubWindow);
+ if (!q->parent() || q->windowFlags() & Qt::FramelessWindowHint)
+ return QSize(-1, -1);
+ return QSize(q->style()->pixelMetric(QStyle::PM_MdiSubWindowMinimizedWidth, 0, q), titleBarHeight());
+}
+
+#ifndef QT_NO_SIZEGRIP
+
+/*!
+ \internal
+*/
+void QMdiSubWindowPrivate::setSizeGrip(QSizeGrip *newSizeGrip)
+{
+ Q_Q(QMdiSubWindow);
+ if (!newSizeGrip || sizeGrip || q->windowFlags() & Qt::FramelessWindowHint)
+ return;
+
+ if (q->layout() && q->layout()->indexOf(newSizeGrip) != -1)
+ return;
+ newSizeGrip->setFixedSize(newSizeGrip->sizeHint());
+ bool putSizeGripInLayout = q->layout() ? true : false;
+#if defined(Q_WS_MAC) && !defined(QT_NO_STYLE_MAC)
+ if (qobject_cast<QMacStyle *>(q->style()))
+ putSizeGripInLayout = false;
+#endif
+ if (putSizeGripInLayout) {
+ q->layout()->addWidget(newSizeGrip);
+ q->layout()->setAlignment(newSizeGrip, Qt::AlignBottom | Qt::AlignRight);
+ } else {
+ newSizeGrip->setParent(q);
+ newSizeGrip->move(q->isLeftToRight() ? q->width() - newSizeGrip->width() : 0,
+ q->height() - newSizeGrip->height());
+ sizeGrip = newSizeGrip;
+ }
+ newSizeGrip->raise();
+ updateGeometryConstraints();
+ newSizeGrip->installEventFilter(q);
+}
+
+/*!
+ \internal
+*/
+void QMdiSubWindowPrivate::setSizeGripVisible(bool visible) const
+{
+ // See if we can find any size grips
+ QList<QSizeGrip *> sizeGrips = qFindChildren<QSizeGrip *>(q_func());
+ foreach (QSizeGrip *grip, sizeGrips)
+ grip->setVisible(visible);
+}
+
+#endif // QT_NO_SIZEGRIP
+
+/*!
+ \internal
+*/
+void QMdiSubWindowPrivate::updateInternalWindowTitle()
+{
+ Q_Q(QMdiSubWindow);
+ if (q->isWindowModified()) {
+ windowTitle = q->windowTitle();
+ windowTitle.replace(QLatin1String("[*]"), QLatin1String("*"));
+ } else {
+ windowTitle = qt_setWindowTitle_helperHelper(q->windowTitle(), q);
+ }
+ q->update(0, 0, q->width(), titleBarHeight());
+}
+
+/*!
+ Constructs a new QMdiSubWindow widget. The \a parent and \a
+ flags arguments are passed to QWidget's constructor.
+
+ Instead of using addSubWindow(), it is also simply possible to
+ use setParent() when you add the subwindow to a QMdiArea.
+
+ Note that only \l{QMdiSubWindow}s can be set as children of
+ QMdiArea; you cannot, for instance, write:
+
+ \badcode
+ QMdiArea mdiArea;
+ QTextEdit editor(&mdiArea); // invalid child widget
+ \endcode
+
+ \sa QMdiArea::addSubWindow()
+*/
+QMdiSubWindow::QMdiSubWindow(QWidget *parent, Qt::WindowFlags flags)
+ : QWidget(*new QMdiSubWindowPrivate, parent, 0)
+{
+ Q_D(QMdiSubWindow);
+#ifndef QT_NO_MENU
+ d->createSystemMenu();
+ addActions(d->systemMenu->actions());
+#endif
+ d->setWindowFlags(flags);
+ setBackgroundRole(QPalette::Window);
+ setAutoFillBackground(true);
+ setMouseTracking(true);
+ setLayout(new QVBoxLayout);
+ setFocusPolicy(Qt::StrongFocus);
+ layout()->setMargin(0);
+ d->updateGeometryConstraints();
+ setAttribute(Qt::WA_Resized, false);
+ d->titleBarPalette = d->desktopPalette();
+ d->font = QApplication::font("QWorkspaceTitleBar");
+ // We don't want the menu icon by default on mac.
+#ifndef Q_WS_MAC
+ if (windowIcon().isNull())
+ d->menuIcon = style()->standardIcon(QStyle::SP_TitleBarMenuButton, 0, this);
+ else
+ d->menuIcon = windowIcon();
+#endif
+ connect(qApp, SIGNAL(focusChanged(QWidget *, QWidget *)),
+ this, SLOT(_q_processFocusChanged(QWidget *, QWidget *)));
+}
+
+/*!
+ Destroys the subwindow.
+
+ \sa QMdiArea::removeSubWindow()
+*/
+QMdiSubWindow::~QMdiSubWindow()
+{
+ Q_D(QMdiSubWindow);
+#ifndef QT_NO_MENUBAR
+ d->removeButtonsFromMenuBar();
+#endif
+ d->setActive(false);
+}
+
+/*!
+ Sets \a widget as the internal widget of this subwindow. The
+ internal widget is displayed in the center of the subwindow
+ beneath the title bar.
+
+ QMdiSubWindow takes temporary ownership of \a widget; you do
+ not have to delete it. Any existing internal widget will be
+ removed and reparented to the root window.
+
+ \sa widget()
+*/
+void QMdiSubWindow::setWidget(QWidget *widget)
+{
+ Q_D(QMdiSubWindow);
+ if (!widget) {
+ d->removeBaseWidget();
+ return;
+ }
+
+ if (widget == d->baseWidget) {
+ qWarning("QMdiSubWindow::setWidget: widget is already set");
+ return;
+ }
+
+ bool wasResized = testAttribute(Qt::WA_Resized);
+ d->removeBaseWidget();
+
+ if (QLayout *layout = this->layout())
+ layout->addWidget(widget);
+ else
+ widget->setParent(this);
+
+#ifndef QT_NO_SIZEGRIP
+ QSizeGrip *sizeGrip = qFindChild<QSizeGrip *>(widget);
+ if (sizeGrip)
+ sizeGrip->installEventFilter(this);
+ if (d->sizeGrip)
+ d->sizeGrip->raise();
+#endif
+
+ d->baseWidget = widget;
+ d->baseWidget->installEventFilter(this);
+
+ d->ignoreWindowTitleChange = true;
+ bool isWindowModified = this->isWindowModified();
+ if (windowTitle().isEmpty()) {
+ d->updateWindowTitle(true);
+ isWindowModified = d->baseWidget->isWindowModified();
+ }
+ if (!this->isWindowModified() && isWindowModified
+ && windowTitle().contains(QLatin1String("[*]"))) {
+ setWindowModified(isWindowModified);
+ }
+ d->lastChildWindowTitle = d->baseWidget->windowTitle();
+ d->ignoreWindowTitleChange = false;
+
+ if (windowIcon().isNull() && !d->baseWidget->windowIcon().isNull())
+ setWindowIcon(d->baseWidget->windowIcon());
+
+ d->updateGeometryConstraints();
+ if (!wasResized && testAttribute(Qt::WA_Resized))
+ setAttribute(Qt::WA_Resized, false);
+}
+
+/*!
+ Returns the current internal widget.
+
+ \sa setWidget()
+*/
+QWidget *QMdiSubWindow::widget() const
+{
+ return d_func()->baseWidget;
+}
+
+
+/*!
+ \internal
+*/
+QWidget *QMdiSubWindow::maximizedButtonsWidget() const
+{
+ Q_D(const QMdiSubWindow);
+ if (isVisible() && d->controlContainer && isMaximized() && !d->drawTitleBarWhenMaximized()
+ && !isChildOfTabbedQMdiArea(this)) {
+ return d->controlContainer->controllerWidget();
+ }
+ return 0;
+}
+
+/*!
+ \internal
+*/
+QWidget *QMdiSubWindow::maximizedSystemMenuIconWidget() const
+{
+ Q_D(const QMdiSubWindow);
+ if (isVisible() && d->controlContainer && isMaximized() && !d->drawTitleBarWhenMaximized()
+ && !isChildOfTabbedQMdiArea(this)) {
+ return d->controlContainer->systemMenuLabel();
+ }
+ return 0;
+}
+
+/*!
+ Returns true if this window is shaded; otherwise returns false.
+
+ A window is shaded if it is collapsed so that only the title bar is
+ visible.
+*/
+bool QMdiSubWindow::isShaded() const
+{
+ return d_func()->isShadeMode;
+}
+
+/*!
+ If \a on is true, \a option is enabled on the subwindow; otherwise it is
+ disabled. See SubWindowOption for the effect of each option.
+
+ \sa SubWindowOption, testOption()
+*/
+void QMdiSubWindow::setOption(SubWindowOption option, bool on)
+{
+ Q_D(QMdiSubWindow);
+ if (on && !(d->options & option))
+ d->options |= option;
+ else if (!on && (d->options & option))
+ d->options &= ~option;
+
+#ifndef QT_NO_RUBBERBAND
+ if ((option & (RubberBandResize | RubberBandMove)) && !on && d->isInRubberBandMode)
+ d->leaveRubberBandMode();
+#endif
+}
+
+/*!
+ Returns true if \a option is enabled; otherwise returns false.
+
+ \sa SubWindowOption, setOption()
+*/
+bool QMdiSubWindow::testOption(SubWindowOption option) const
+{
+ return d_func()->options & option;
+}
+
+/*!
+ \property QMdiSubWindow::keyboardSingleStep
+ \brief sets how far a widget should move or resize when using the
+ keyboard arrow keys.
+
+ When in keyboard-interactive mode, you can use the arrow and page keys to
+ either move or resize the window. This property controls the arrow keys.
+ The common way to enter keyboard interactive mode is to enter the
+ subwindow menu, and select either "resize" or "move".
+
+ The default keyboard single step value is 5 pixels.
+
+ \sa keyboardPageStep
+*/
+int QMdiSubWindow::keyboardSingleStep() const
+{
+ return d_func()->keyboardSingleStep;
+}
+
+void QMdiSubWindow::setKeyboardSingleStep(int step)
+{
+ // Haven't done any boundary check here since negative step only
+ // means inverted behavior, which is OK if the user want it.
+ // A step equal to zero means "do nothing".
+ d_func()->keyboardSingleStep = step;
+}
+
+/*!
+ \property QMdiSubWindow::keyboardPageStep
+ \brief sets how far a widget should move or resize when using the
+ keyboard page keys.
+
+ When in keyboard-interactive mode, you can use the arrow and page keys to
+ either move or resize the window. This property controls the page
+ keys. The common way to enter keyboard interactive mode is to enter the
+ subwindow menu, and select either "resize" or "move".
+
+ The default keyboard page step value is 20 pixels.
+
+ \sa keyboardSingleStep
+*/
+int QMdiSubWindow::keyboardPageStep() const
+{
+ return d_func()->keyboardPageStep;
+}
+
+void QMdiSubWindow::setKeyboardPageStep(int step)
+{
+ // Haven't done any boundary check here since negative step only
+ // means inverted behavior, which is OK if the user want it.
+ // A step equal to zero means "do nothing".
+ d_func()->keyboardPageStep = step;
+}
+
+#ifndef QT_NO_MENU
+/*!
+ Sets \a systemMenu as the current system menu for this subwindow.
+
+ By default, each QMdiSubWindow has a standard system menu.
+
+ QActions for the system menu created by QMdiSubWindow will
+ automatically be updated depending on the current window state;
+ e.g., the minimize action will be disabled after the window is
+ minimized.
+
+ QActions added by the user are not updated by QMdiSubWindow.
+
+ QMdiSubWindow takes ownership of \a systemMenu; you do not have to
+ delete it. Any existing menus will be deleted.
+
+ \sa systemMenu(), showSystemMenu()
+*/
+void QMdiSubWindow::setSystemMenu(QMenu *systemMenu)
+{
+ Q_D(QMdiSubWindow);
+ if (systemMenu && systemMenu == d->systemMenu) {
+ qWarning("QMdiSubWindow::setSystemMenu: system menu is already set");
+ return;
+ }
+
+ if (d->systemMenu) {
+ delete d->systemMenu;
+ d->systemMenu = 0;
+ }
+
+ if (!systemMenu)
+ return;
+
+ if (systemMenu->parent() != this)
+ systemMenu->setParent(this);
+ d->systemMenu = systemMenu;
+}
+
+/*!
+ Returns a pointer to the current system menu, or zero if no system
+ menu is set. QMdiSubWindow provides a default system menu, but you can
+ also set the menu with setSystemMenu().
+
+ \sa setSystemMenu(), showSystemMenu()
+*/
+QMenu *QMdiSubWindow::systemMenu() const
+{
+ return d_func()->systemMenu;
+}
+
+/*!
+ Shows the system menu below the system menu icon in the title bar.
+
+ \sa setSystemMenu(), systemMenu()
+*/
+void QMdiSubWindow::showSystemMenu()
+{
+ Q_D(QMdiSubWindow);
+ if (!d->systemMenu)
+ return;
+
+ QPoint globalPopupPos;
+ if (QWidget *icon = maximizedSystemMenuIconWidget()) {
+ if (isLeftToRight())
+ globalPopupPos = icon->mapToGlobal(QPoint(0, icon->y() + icon->height()));
+ else
+ globalPopupPos = icon->mapToGlobal(QPoint(icon->width(), icon->y() + icon->height()));
+ } else {
+ if (isLeftToRight())
+ globalPopupPos = mapToGlobal(contentsRect().topLeft());
+ else // + QPoint(1, 0) because topRight() == QPoint(left() + width() -1, top())
+ globalPopupPos = mapToGlobal(contentsRect().topRight()) + QPoint(1, 0);
+ }
+
+ // Adjust x() with -menuwidth in reverse mode.
+ if (isRightToLeft())
+ globalPopupPos -= QPoint(d->systemMenu->sizeHint().width(), 0);
+ d->systemMenu->installEventFilter(this);
+ d->systemMenu->popup(globalPopupPos);
+}
+#endif // QT_NO_MENU
+
+/*!
+ \since 4.4
+
+ Returns the area containing this sub-window, or 0 if there is none.
+
+ \sa QMdiArea::addSubWindow()
+*/
+QMdiArea *QMdiSubWindow::mdiArea() const
+{
+ QWidget *parent = parentWidget();
+ while (parent) {
+ if (QMdiArea *area = qobject_cast<QMdiArea *>(parent)) {
+ if (area->viewport() == parentWidget())
+ return area;
+ }
+ parent = parent->parentWidget();
+ }
+ return 0;
+}
+
+/*!
+ Calling this function makes the subwindow enter the shaded mode.
+ When the subwindow is shaded, only the title bar is visible.
+
+ Although shading is not supported by all styles, this function will
+ still show the subwindow as shaded, regardless of whether support
+ for shading is available. However, when used with styles without
+ shading support, the user will be unable to return from shaded mode
+ through the user interface (e.g., through a shade button in the title
+ bar).
+
+ \sa isShaded()
+*/
+void QMdiSubWindow::showShaded()
+{
+ if (!parent())
+ return;
+
+ Q_D(QMdiSubWindow);
+ // setMinimizeMode uses this function.
+ if (!d->isShadeRequestFromMinimizeMode && isShaded())
+ return;
+
+ d->isMaximizeMode = false;
+
+ QWidget *currentFocusWidget = QApplication::focusWidget();
+ if (!d->restoreFocusWidget && isAncestorOf(currentFocusWidget))
+ d->restoreFocusWidget = currentFocusWidget;
+
+ if (!d->isShadeRequestFromMinimizeMode) {
+ d->isShadeMode = true;
+ d->ensureWindowState(Qt::WindowMinimized);
+ }
+
+#ifndef QT_NO_MENUBAR
+ d->removeButtonsFromMenuBar();
+#endif
+
+ // showMinimized() will reset Qt::WindowActive, which makes sense
+ // for top level widgets, but in MDI it makes sense to have an
+ // active window which is minimized.
+ if (hasFocus() || isAncestorOf(currentFocusWidget))
+ d->ensureWindowState(Qt::WindowActive);
+
+#ifndef QT_NO_SIZEGRIP
+ d->setSizeGripVisible(false);
+#endif
+
+ if (!d->restoreSize.isValid() || d->isShadeMode) {
+ d->oldGeometry = geometry();
+ d->restoreSize.setWidth(d->oldGeometry.width());
+ d->restoreSize.setHeight(d->oldGeometry.height());
+ }
+
+ // Hide the window before we change the geometry to avoid multiple resize
+ // events and wrong window state.
+ const bool wasVisible = isVisible();
+ if (wasVisible)
+ setVisible(false);
+
+ d->updateGeometryConstraints();
+ // Update minimum size to internalMinimumSize if set by user.
+ if (!minimumSize().isNull()) {
+ d->userMinimumSize = minimumSize();
+ setMinimumSize(d->internalMinimumSize);
+ }
+ resize(d->internalMinimumSize);
+
+ // Hide the internal widget if not already hidden by the user.
+ if (d->baseWidget && !d->baseWidget->isHidden()) {
+ d->baseWidget->hide();
+ d->isWidgetHiddenByUs = true;
+ }
+
+ if (wasVisible)
+ setVisible(true);
+
+ d->setFocusWidget();
+ d->resizeEnabled = false;
+ d->moveEnabled = true;
+ d->updateDirtyRegions();
+ d->updateMask();
+
+#ifndef QT_NO_ACTION
+ d->setEnabled(QMdiSubWindowPrivate::MinimizeAction, false);
+ d->setEnabled(QMdiSubWindowPrivate::ResizeAction, d->resizeEnabled);
+ d->setEnabled(QMdiSubWindowPrivate::MaximizeAction, true);
+ d->setEnabled(QMdiSubWindowPrivate::RestoreAction, true);
+ d->setEnabled(QMdiSubWindowPrivate::MoveAction, d->moveEnabled);
+#endif
+}
+
+/*!
+ \reimp
+*/
+bool QMdiSubWindow::eventFilter(QObject *object, QEvent *event)
+{
+ Q_D(QMdiSubWindow);
+ if (!object)
+ return QWidget::eventFilter(object, event);
+
+#ifndef QT_NO_MENU
+ // System menu events.
+ if (d->systemMenu && d->systemMenu == object) {
+ if (event->type() == QEvent::MouseButtonDblClick) {
+ close();
+ } else if (event->type() == QEvent::MouseMove) {
+ QMouseEvent *mouseEvent = static_cast<QMouseEvent *>(event);
+ d->hoveredSubControl = d->getSubControl(mapFromGlobal(mouseEvent->globalPos()));
+ } else if (event->type() == QEvent::Hide) {
+ d->systemMenu->removeEventFilter(this);
+ d->activeSubControl = QStyle::SC_None;
+ update(QRegion(0, 0, width(), d->titleBarHeight()));
+ }
+ return QWidget::eventFilter(object, event);
+ }
+#endif
+
+#ifndef QT_NO_SIZEGRIP
+ if (object != d->baseWidget && parent() && qobject_cast<QSizeGrip *>(object)) {
+ if (event->type() != QEvent::MouseButtonPress || !testOption(QMdiSubWindow::RubberBandResize))
+ return QWidget::eventFilter(object, event);
+ const QMouseEvent *mouseEvent = static_cast<QMouseEvent *>(event);
+ d->mousePressPosition = parentWidget()->mapFromGlobal(mouseEvent->globalPos());
+ d->oldGeometry = geometry();
+ d->currentOperation = isLeftToRight() ? QMdiSubWindowPrivate::BottomRightResize
+ : QMdiSubWindowPrivate::BottomLeftResize;
+#ifndef QT_NO_RUBBERBAND
+ d->enterRubberBandMode();
+#endif
+ return true;
+ }
+#endif
+
+ if (object != d->baseWidget && event->type() != QEvent::WindowTitleChange)
+ return QWidget::eventFilter(object, event);
+
+ switch (event->type()) {
+ case QEvent::Show:
+ d->setActive(true);
+ break;
+ case QEvent::ShowToParent:
+ if (!d->isWidgetHiddenByUs)
+ show();
+ break;
+ case QEvent::WindowStateChange: {
+ QWindowStateChangeEvent *changeEvent = static_cast<QWindowStateChangeEvent*>(event);
+ if (changeEvent->isOverride())
+ break;
+ Qt::WindowStates oldState = changeEvent->oldState();
+ Qt::WindowStates newState = d->baseWidget->windowState();
+ if (!(oldState & Qt::WindowMinimized) && (newState & Qt::WindowMinimized))
+ showMinimized();
+ else if (!(oldState & Qt::WindowMaximized) && (newState & Qt::WindowMaximized))
+ showMaximized();
+ else if (!(newState & (Qt::WindowMaximized | Qt::WindowMinimized)))
+ showNormal();
+ break;
+ }
+ case QEvent::Enter:
+ d->currentOperation = QMdiSubWindowPrivate::None;
+ d->updateCursor();
+ break;
+ case QEvent::LayoutRequest:
+ d->updateGeometryConstraints();
+ break;
+ case QEvent::WindowTitleChange:
+ if (d->ignoreWindowTitleChange)
+ break;
+ if (object == d->baseWidget) {
+ d->updateWindowTitle(true);
+ d->lastChildWindowTitle = d->baseWidget->windowTitle();
+#ifndef QT_NO_MENUBAR
+ } else if (maximizedButtonsWidget() && d->controlContainer->menuBar() && d->controlContainer->menuBar()
+ ->cornerWidget(Qt::TopRightCorner) == maximizedButtonsWidget()) {
+ d->originalTitle = QString::null;
+ if (d->baseWidget && d->baseWidget->windowTitle() == windowTitle())
+ d->updateWindowTitle(true);
+ else
+ d->updateWindowTitle(false);
+#endif
+ }
+ break;
+ case QEvent::ModifiedChange: {
+ if (object != d->baseWidget)
+ break;
+ bool windowModified = d->baseWidget->isWindowModified();
+ if (!windowModified && d->baseWidget->windowTitle() != windowTitle())
+ break;
+ if (windowTitle().contains(QLatin1String("[*]")))
+ setWindowModified(windowModified);
+ break;
+ }
+ default:
+ break;
+ }
+ return QWidget::eventFilter(object, event);
+}
+
+/*!
+ \reimp
+*/
+bool QMdiSubWindow::event(QEvent *event)
+{
+ Q_D(QMdiSubWindow);
+ switch (event->type()) {
+ case QEvent::StyleChange: {
+ bool wasShaded = isShaded();
+ bool wasMinimized = isMinimized();
+ bool wasMaximized = isMaximized();
+ ensurePolished();
+ setContentsMargins(0, 0, 0, 0);
+ if (wasMinimized || wasMaximized || wasShaded)
+ showNormal();
+ d->updateGeometryConstraints();
+ resize(d->internalMinimumSize.expandedTo(size()));
+ d->updateMask();
+ d->updateDirtyRegions();
+ if (wasShaded)
+ showShaded();
+ else if (wasMinimized)
+ showMinimized();
+ else if (wasMaximized)
+ showMaximized();
+ break;
+ }
+ case QEvent::ParentAboutToChange:
+ d->setActive(false);
+ break;
+ case QEvent::ParentChange: {
+ bool wasResized = testAttribute(Qt::WA_Resized);
+#ifndef QT_NO_MENUBAR
+ d->removeButtonsFromMenuBar();
+#endif
+ d->currentOperation = QMdiSubWindowPrivate::None;
+ d->activeSubControl = QStyle::SC_None;
+ d->hoveredSubControl = QStyle::SC_None;
+#ifndef QT_NO_RUBBERBAND
+ if (d->isInRubberBandMode)
+ d->leaveRubberBandMode();
+#endif
+ d->isShadeMode = false;
+ d->isMaximizeMode = false;
+ d->isWidgetHiddenByUs = false;
+ if (!parent()) {
+#if !defined(QT_NO_SIZEGRIP) && defined(Q_WS_MAC) && !defined(QT_NO_STYLE_MAC)
+ if (qobject_cast<QMacStyle *>(style()))
+ delete d->sizeGrip;
+#endif
+ setOption(RubberBandResize, false);
+ setOption(RubberBandMove, false);
+ } else {
+ d->setWindowFlags(windowFlags());
+ }
+ setContentsMargins(0, 0, 0, 0);
+ d->updateGeometryConstraints();
+ d->updateCursor();
+ d->updateMask();
+ d->updateDirtyRegions();
+ d->updateActions();
+ if (!wasResized && testAttribute(Qt::WA_Resized))
+ setAttribute(Qt::WA_Resized, false);
+ break;
+ }
+ case QEvent::WindowActivate:
+ if (d->ignoreNextActivationEvent) {
+ d->ignoreNextActivationEvent = false;
+ break;
+ }
+ d->isExplicitlyDeactivated = false;
+ d->setActive(true);
+ break;
+ case QEvent::WindowDeactivate:
+ if (d->ignoreNextActivationEvent) {
+ d->ignoreNextActivationEvent = false;
+ break;
+ }
+ d->isExplicitlyDeactivated = true;
+ d->setActive(false);
+ break;
+ case QEvent::WindowTitleChange:
+ if (!d->ignoreWindowTitleChange)
+ d->updateWindowTitle(false);
+ d->updateInternalWindowTitle();
+ break;
+ case QEvent::ModifiedChange:
+ if (!windowTitle().contains(QLatin1String("[*]")))
+ break;
+#ifndef QT_NO_MENUBAR
+ if (maximizedButtonsWidget() && d->controlContainer->menuBar() && d->controlContainer->menuBar()
+ ->cornerWidget(Qt::TopRightCorner) == maximizedButtonsWidget()) {
+ window()->setWindowModified(isWindowModified());
+ }
+#endif // QT_NO_MENUBAR
+ d->updateInternalWindowTitle();
+ break;
+ case QEvent::LayoutDirectionChange:
+ d->updateDirtyRegions();
+ break;
+ case QEvent::LayoutRequest:
+ d->updateGeometryConstraints();
+ break;
+ case QEvent::WindowIconChange:
+ d->menuIcon = windowIcon();
+ if (d->menuIcon.isNull())
+ d->menuIcon = style()->standardIcon(QStyle::SP_TitleBarMenuButton, 0, this);
+ if (d->controlContainer)
+ d->controlContainer->updateWindowIcon(d->menuIcon);
+ if (!maximizedSystemMenuIconWidget())
+ update(0, 0, width(), d->titleBarHeight());
+ break;
+ case QEvent::PaletteChange:
+ d->titleBarPalette = d->desktopPalette();
+ break;
+ case QEvent::FontChange:
+ d->font = font();
+ break;
+#ifndef QT_NO_TOOLTIP
+ case QEvent::ToolTip:
+ showToolTip(static_cast<QHelpEvent *>(event), this, d->titleBarOptions(),
+ QStyle::CC_TitleBar, d->hoveredSubControl);
+ break;
+#endif
+ default:
+ break;
+ }
+ return QWidget::event(event);
+}
+
+/*!
+ \reimp
+*/
+void QMdiSubWindow::showEvent(QShowEvent *showEvent)
+{
+ Q_D(QMdiSubWindow);
+ if (!parent()) {
+ QWidget::showEvent(showEvent);
+ return;
+ }
+
+#if !defined(QT_NO_SIZEGRIP) && defined(Q_WS_MAC) && !defined(QT_NO_STYLE_MAC)
+ if (qobject_cast<QMacStyle *>(style()) && !d->sizeGrip
+ && !(windowFlags() & Qt::FramelessWindowHint)) {
+ d->setSizeGrip(new QSizeGrip(0));
+ Q_ASSERT(d->sizeGrip);
+ if (isMinimized())
+ d->setSizeGripVisible(false);
+ else
+ d->setSizeGripVisible(true);
+ resize(size().expandedTo(d->internalMinimumSize));
+ }
+#endif
+
+ d->updateDirtyRegions();
+ // Show buttons in the menu bar if they're already not there.
+ // We want to do this when QMdiSubWindow becomes visible after being hidden.
+#ifndef QT_NO_MENUBAR
+ if (d->controlContainer) {
+ if (QMenuBar *menuBar = d->menuBar()) {
+ if (menuBar->cornerWidget(Qt::TopRightCorner) != maximizedButtonsWidget())
+ d->showButtonsInMenuBar(menuBar);
+ }
+ }
+#endif
+ d->setActive(true);
+}
+
+/*!
+ \reimp
+*/
+void QMdiSubWindow::hideEvent(QHideEvent * /*hideEvent*/)
+{
+#ifndef QT_NO_MENUBAR
+ d_func()->removeButtonsFromMenuBar();
+#endif
+}
+
+/*!
+ \reimp
+*/
+void QMdiSubWindow::changeEvent(QEvent *changeEvent)
+{
+ if (!parent()) {
+ QWidget::changeEvent(changeEvent);
+ return;
+ }
+
+ if (changeEvent->type() != QEvent::WindowStateChange) {
+ QWidget::changeEvent(changeEvent);
+ return;
+ }
+
+ QWindowStateChangeEvent *event = static_cast<QWindowStateChangeEvent *>(changeEvent);
+ if (event->isOverride()) {
+ event->ignore();
+ return;
+ }
+
+ Qt::WindowStates oldState = event->oldState();
+ Qt::WindowStates newState = windowState();
+ if (oldState == newState) {
+ changeEvent->ignore();
+ return;
+ }
+
+ // QWidget ensures that the widget is visible _after_ setWindowState(),
+ // but we need to ensure that the widget is visible _before_
+ // setWindowState() returns.
+ Q_D(QMdiSubWindow);
+ if (!isVisible()) {
+ d->ensureWindowState(Qt::WindowNoState);
+ setVisible(true);
+ }
+
+ if (!d->oldGeometry.isValid())
+ d->oldGeometry = geometry();
+
+ if ((oldState & Qt::WindowActive) && (newState & Qt::WindowActive))
+ d->currentOperation = QMdiSubWindowPrivate::None;
+
+ if (!(oldState & Qt::WindowMinimized) && (newState & Qt::WindowMinimized))
+ d->setMinimizeMode();
+ else if (!(oldState & Qt::WindowMaximized) && (newState & Qt::WindowMaximized))
+ d->setMaximizeMode();
+ else if (!(newState & (Qt::WindowMaximized | Qt::WindowMinimized)))
+ d->setNormalMode();
+
+ if (d->isActive)
+ d->ensureWindowState(Qt::WindowActive);
+ emit windowStateChanged(oldState, windowState());
+}
+
+/*!
+ \reimp
+*/
+void QMdiSubWindow::closeEvent(QCloseEvent *closeEvent)
+{
+ Q_D(QMdiSubWindow);
+ bool acceptClose = true;
+ if (d->baseWidget)
+ acceptClose = d->baseWidget->close();
+ if (!acceptClose) {
+ closeEvent->ignore();
+ return;
+ }
+#ifndef QT_NO_MENUBAR
+ d->removeButtonsFromMenuBar();
+#endif
+ d->setActive(false);
+ if (parentWidget() && testAttribute(Qt::WA_DeleteOnClose)) {
+ QChildEvent childRemoved(QEvent::ChildRemoved, this);
+ QApplication::sendEvent(parentWidget(), &childRemoved);
+ }
+ closeEvent->accept();
+}
+
+/*!
+ \reimp
+*/
+void QMdiSubWindow::leaveEvent(QEvent * /*leaveEvent*/)
+{
+ Q_D(QMdiSubWindow);
+ if (d->hoveredSubControl != QStyle::SC_None) {
+ d->hoveredSubControl = QStyle::SC_None;
+ update(QRegion(0, 0, width(), d->titleBarHeight()));
+ }
+}
+
+/*!
+ \reimp
+*/
+void QMdiSubWindow::resizeEvent(QResizeEvent *resizeEvent)
+{
+ Q_D(QMdiSubWindow);
+#ifndef QT_NO_SIZEGRIP
+ if (d->sizeGrip) {
+ d->sizeGrip->move(isLeftToRight() ? width() - d->sizeGrip->width() : 0,
+ height() - d->sizeGrip->height());
+ }
+#endif
+
+ if (!parent()) {
+ QWidget::resizeEvent(resizeEvent);
+ return;
+ }
+
+ if (d->isMaximizeMode)
+ d->ensureWindowState(Qt::WindowMaximized);
+
+ d->updateMask();
+ if (!isVisible())
+ return;
+
+ if (d->resizeTimerId <= 0)
+ d->cachedStyleOptions = d->titleBarOptions();
+ else
+ killTimer(d->resizeTimerId);
+ d->resizeTimerId = startTimer(200);
+}
+
+/*!
+ \reimp
+*/
+void QMdiSubWindow::timerEvent(QTimerEvent *timerEvent)
+{
+ Q_D(QMdiSubWindow);
+ if (timerEvent->timerId() == d->resizeTimerId) {
+ killTimer(d->resizeTimerId);
+ d->resizeTimerId = -1;
+ d->updateDirtyRegions();
+ }
+}
+
+/*!
+ \reimp
+*/
+void QMdiSubWindow::moveEvent(QMoveEvent *moveEvent)
+{
+ if (!parent()) {
+ QWidget::moveEvent(moveEvent);
+ return;
+ }
+
+ Q_D(QMdiSubWindow);
+ if (d->isMaximizeMode)
+ d->ensureWindowState(Qt::WindowMaximized);
+}
+
+/*!
+ \reimp
+*/
+void QMdiSubWindow::paintEvent(QPaintEvent *paintEvent)
+{
+ if (!parent() || (windowFlags() & Qt::FramelessWindowHint)) {
+ QWidget::paintEvent(paintEvent);
+ return;
+ }
+
+ Q_D(QMdiSubWindow);
+ if (isMaximized() && !d->drawTitleBarWhenMaximized())
+ return;
+
+ if (d->resizeTimerId != -1) {
+ // Only update the style option rect and the window title.
+ int border = d->hasBorder(d->cachedStyleOptions) ? 4 : 0;
+ int titleBarHeight = d->titleBarHeight(d->cachedStyleOptions);
+ titleBarHeight -= isMinimized() ? 2 * border : border;
+ d->cachedStyleOptions.rect = QRect(border, border, width() - 2 * border, titleBarHeight);
+ if (!d->windowTitle.isEmpty()) {
+ int width = style()->subControlRect(QStyle::CC_TitleBar, &d->cachedStyleOptions,
+ QStyle::SC_TitleBarLabel, this).width();
+ d->cachedStyleOptions.text = d->cachedStyleOptions.fontMetrics
+ .elidedText(d->windowTitle, Qt::ElideRight, width);
+ }
+ } else {
+ // Force full update.
+ d->cachedStyleOptions = d->titleBarOptions();
+ }
+
+ QStylePainter painter(this);
+ if (!d->windowTitle.isEmpty())
+ painter.setFont(d->font);
+ painter.drawComplexControl(QStyle::CC_TitleBar, d->cachedStyleOptions);
+
+ if (isMinimized() && !d->hasBorder(d->cachedStyleOptions))
+ return;
+
+ QStyleOptionFrame frameOptions;
+ frameOptions.initFrom(this);
+ frameOptions.lineWidth = style()->pixelMetric(QStyle::PM_MdiSubWindowFrameWidth, 0, this);
+ if (d->isActive)
+ frameOptions.state |= QStyle::State_Active;
+ else
+ frameOptions.state &= ~QStyle::State_Active;
+
+ // ### Ensure that we do not require setting the cliprect for 4.4
+ if (!isMinimized() && !d->hasBorder(d->cachedStyleOptions))
+ painter.setClipRect(rect().adjusted(0, d->titleBarHeight(d->cachedStyleOptions), 0, 0));
+ if (!isMinimized() || d->hasBorder(d->cachedStyleOptions))
+ painter.drawPrimitive(QStyle::PE_FrameWindow, frameOptions);
+}
+
+/*!
+ \reimp
+*/
+void QMdiSubWindow::mousePressEvent(QMouseEvent *mouseEvent)
+{
+ if (!parent()) {
+ QWidget::mousePressEvent(mouseEvent);
+ return;
+ }
+
+ Q_D(QMdiSubWindow);
+ if (d->isInInteractiveMode)
+ d->leaveInteractiveMode();
+#ifndef QT_NO_RUBBERBAND
+ if (d->isInRubberBandMode)
+ d->leaveRubberBandMode();
+#endif
+
+ if (mouseEvent->button() != Qt::LeftButton) {
+ mouseEvent->ignore();
+ return;
+ }
+
+ if (d->currentOperation != QMdiSubWindowPrivate::None) {
+ d->updateCursor();
+ d->mousePressPosition = mapToParent(mouseEvent->pos());
+ if (d->resizeEnabled || d->moveEnabled)
+ d->oldGeometry = geometry();
+#ifndef QT_NO_RUBBERBAND
+ if ((testOption(QMdiSubWindow::RubberBandResize) && d->isResizeOperation())
+ || (testOption(QMdiSubWindow::RubberBandMove) && d->isMoveOperation())) {
+ d->enterRubberBandMode();
+ }
+#endif
+ return;
+ }
+
+ d->activeSubControl = d->hoveredSubControl;
+#ifndef QT_NO_MENU
+ if (d->activeSubControl == QStyle::SC_TitleBarSysMenu)
+ showSystemMenu();
+ else
+#endif
+ update(QRegion(0, 0, width(), d->titleBarHeight()));
+}
+
+/*!
+ \reimp
+*/
+void QMdiSubWindow::mouseDoubleClickEvent(QMouseEvent *mouseEvent)
+{
+ if (!parent()) {
+ QWidget::mouseDoubleClickEvent(mouseEvent);
+ return;
+ }
+
+ if (mouseEvent->button() != Qt::LeftButton) {
+ mouseEvent->ignore();
+ return;
+ }
+
+ Q_D(QMdiSubWindow);
+ if (!d->isMoveOperation()) {
+#ifndef QT_NO_MENU
+ if (d->hoveredSubControl == QStyle::SC_TitleBarSysMenu)
+ close();
+#endif
+ return;
+ }
+
+ Qt::WindowFlags flags = windowFlags();
+ if (isMinimized()) {
+ if ((isShaded() && (flags & Qt::WindowShadeButtonHint))
+ || (flags & Qt::WindowMinimizeButtonHint)) {
+ showNormal();
+ }
+ return;
+ }
+
+ if (isMaximized()) {
+ if (flags & Qt::WindowMaximizeButtonHint)
+ showNormal();
+ return;
+ }
+
+ if (flags & Qt::WindowShadeButtonHint)
+ showShaded();
+ else if (flags & Qt::WindowMaximizeButtonHint)
+ showMaximized();
+}
+
+/*!
+ \reimp
+*/
+void QMdiSubWindow::mouseReleaseEvent(QMouseEvent *mouseEvent)
+{
+ if (!parent()) {
+ QWidget::mouseReleaseEvent(mouseEvent);
+ return;
+ }
+
+ if (mouseEvent->button() != Qt::LeftButton) {
+ mouseEvent->ignore();
+ return;
+ }
+
+ Q_D(QMdiSubWindow);
+ if (d->currentOperation != QMdiSubWindowPrivate::None) {
+#ifndef QT_NO_RUBBERBAND
+ if (d->isInRubberBandMode && !d->isInInteractiveMode)
+ d->leaveRubberBandMode();
+#endif
+ if (d->resizeEnabled || d->moveEnabled)
+ d->oldGeometry = geometry();
+ }
+
+ d->currentOperation = d->getOperation(mouseEvent->pos());
+ d->updateCursor();
+
+ d->hoveredSubControl = d->getSubControl(mouseEvent->pos());
+ if (d->activeSubControl != QStyle::SC_None
+ && d->activeSubControl == d->hoveredSubControl) {
+ d->processClickedSubControl();
+ }
+ d->activeSubControl = QStyle::SC_None;
+ update(QRegion(0, 0, width(), d->titleBarHeight()));
+}
+
+/*!
+ \reimp
+*/
+void QMdiSubWindow::mouseMoveEvent(QMouseEvent *mouseEvent)
+{
+ if (!parent()) {
+ QWidget::mouseMoveEvent(mouseEvent);
+ return;
+ }
+
+ Q_D(QMdiSubWindow);
+ // No update needed if we're in a move/resize operation.
+ if (!d->isMoveOperation() && !d->isResizeOperation()) {
+ // Find previous and current hover region.
+ const QStyleOptionTitleBar options = d->titleBarOptions();
+ QStyle::SubControl oldHover = d->hoveredSubControl;
+ d->hoveredSubControl = d->getSubControl(mouseEvent->pos());
+ QRegion hoverRegion;
+ if (isHoverControl(oldHover) && oldHover != d->hoveredSubControl)
+ hoverRegion += style()->subControlRect(QStyle::CC_TitleBar, &options, oldHover, this);
+ if (isHoverControl(d->hoveredSubControl) && d->hoveredSubControl != oldHover) {
+ hoverRegion += style()->subControlRect(QStyle::CC_TitleBar, &options,
+ d->hoveredSubControl, this);
+ }
+#if defined(Q_WS_MAC) && !defined(QT_NO_STYLE_MAC)
+ if (qobject_cast<QMacStyle *>(style()) && !hoverRegion.isEmpty())
+ hoverRegion += QRegion(0, 0, width(), d->titleBarHeight(options));
+#endif
+ if (!hoverRegion.isEmpty())
+ update(hoverRegion);
+ }
+
+ if ((mouseEvent->buttons() & Qt::LeftButton) || d->isInInteractiveMode) {
+ if ((d->isResizeOperation() && d->resizeEnabled) || (d->isMoveOperation() && d->moveEnabled))
+ d->setNewGeometry(mapToParent(mouseEvent->pos()));
+ return;
+ }
+
+ // Do not resize/move if not allowed.
+ d->currentOperation = d->getOperation(mouseEvent->pos());
+ if ((d->isResizeOperation() && !d->resizeEnabled) || (d->isMoveOperation() && !d->moveEnabled))
+ d->currentOperation = QMdiSubWindowPrivate::None;
+ d->updateCursor();
+}
+
+/*!
+ \reimp
+*/
+void QMdiSubWindow::keyPressEvent(QKeyEvent *keyEvent)
+{
+ Q_D(QMdiSubWindow);
+ if (!d->isInInteractiveMode || !parent()) {
+ keyEvent->ignore();
+ return;
+ }
+
+ QPoint delta;
+ switch (keyEvent->key()) {
+ case Qt::Key_Right:
+ if (keyEvent->modifiers() & Qt::ShiftModifier)
+ delta = QPoint(d->keyboardPageStep, 0);
+ else
+ delta = QPoint(d->keyboardSingleStep, 0);
+ break;
+ case Qt::Key_Up:
+ if (keyEvent->modifiers() & Qt::ShiftModifier)
+ delta = QPoint(0, -d->keyboardPageStep);
+ else
+ delta = QPoint(0, -d->keyboardSingleStep);
+ break;
+ case Qt::Key_Left:
+ if (keyEvent->modifiers() & Qt::ShiftModifier)
+ delta = QPoint(-d->keyboardPageStep, 0);
+ else
+ delta = QPoint(-d->keyboardSingleStep, 0);
+ break;
+ case Qt::Key_Down:
+ if (keyEvent->modifiers() & Qt::ShiftModifier)
+ delta = QPoint(0, d->keyboardPageStep);
+ else
+ delta = QPoint(0, d->keyboardSingleStep);
+ break;
+ case Qt::Key_Escape:
+ case Qt::Key_Return:
+ case Qt::Key_Enter:
+ d->leaveInteractiveMode();
+ return;
+ default:
+ keyEvent->ignore();
+ return;
+ }
+
+#ifndef QT_NO_CURSOR
+ QPoint newPosition = parentWidget()->mapFromGlobal(cursor().pos() + delta);
+ QRect oldGeometry =
+#ifndef QT_NO_RUBBERBAND
+ d->isInRubberBandMode ? d->rubberBand->geometry() :
+#endif
+ geometry();
+ d->setNewGeometry(newPosition);
+ QRect currentGeometry =
+#ifndef QT_NO_RUBBERBAND
+ d->isInRubberBandMode ? d->rubberBand->geometry() :
+#endif
+ geometry();
+ if (currentGeometry == oldGeometry)
+ return;
+
+ // Update cursor position
+
+ QPoint actualDelta;
+ if (d->isMoveOperation()) {
+ actualDelta = QPoint(currentGeometry.x() - oldGeometry.x(),
+ currentGeometry.y() - oldGeometry.y());
+ } else {
+ int dx = isLeftToRight() ? currentGeometry.width() - oldGeometry.width()
+ : currentGeometry.x() - oldGeometry.x();
+ actualDelta = QPoint(dx, currentGeometry.height() - oldGeometry.height());
+ }
+
+ // Adjust in case we weren't able to move as long as wanted.
+ if (actualDelta != delta)
+ newPosition += (actualDelta - delta);
+ cursor().setPos(parentWidget()->mapToGlobal(newPosition));
+#endif
+}
+
+#ifndef QT_NO_CONTEXTMENU
+/*!
+ \reimp
+*/
+void QMdiSubWindow::contextMenuEvent(QContextMenuEvent *contextMenuEvent)
+{
+ Q_D(QMdiSubWindow);
+ if (!d->systemMenu) {
+ contextMenuEvent->ignore();
+ return;
+ }
+
+ if (d->hoveredSubControl == QStyle::SC_TitleBarSysMenu
+ || d->getRegion(QMdiSubWindowPrivate::Move).contains(contextMenuEvent->pos())) {
+ d->systemMenu->exec(contextMenuEvent->globalPos());
+ } else {
+ contextMenuEvent->ignore();
+ }
+}
+#endif // QT_NO_CONTEXTMENU
+
+/*!
+ \reimp
+*/
+void QMdiSubWindow::focusInEvent(QFocusEvent *focusInEvent)
+{
+ d_func()->focusInReason = focusInEvent->reason();
+}
+
+/*!
+ \reimp
+*/
+void QMdiSubWindow::focusOutEvent(QFocusEvent * /*focusOutEvent*/)
+{
+ // To avoid update() in QWidget::focusOutEvent.
+}
+
+/*!
+ \reimp
+*/
+void QMdiSubWindow::childEvent(QChildEvent *childEvent)
+{
+ if (childEvent->type() != QEvent::ChildPolished)
+ return;
+#ifndef QT_NO_SIZEGRIP
+ if (QSizeGrip *sizeGrip = qobject_cast<QSizeGrip *>(childEvent->child()))
+ d_func()->setSizeGrip(sizeGrip);
+#endif
+}
+
+/*!
+ \reimp
+*/
+QSize QMdiSubWindow::sizeHint() const
+{
+ Q_D(const QMdiSubWindow);
+ int margin, minWidth;
+ d->sizeParameters(&margin, &minWidth);
+ QSize size(2 * margin, d->titleBarHeight() + margin);
+ if (d->baseWidget && d->baseWidget->sizeHint().isValid())
+ size += d->baseWidget->sizeHint();
+ return size.expandedTo(minimumSizeHint());
+}
+
+/*!
+ \reimp
+*/
+QSize QMdiSubWindow::minimumSizeHint() const
+{
+ Q_D(const QMdiSubWindow);
+ if (isVisible())
+ ensurePolished();
+
+ // Minimized window.
+ if (parent() && isMinimized() && !isShaded())
+ return d->iconSize();
+
+ // Calculate window decoration.
+ int margin, minWidth;
+ d->sizeParameters(&margin, &minWidth);
+ int decorationHeight = margin + d->titleBarHeight();
+ int minHeight = decorationHeight;
+
+ // Shaded window.
+ if (parent() && isShaded())
+ return QSize(qMax(minWidth, width()), d->titleBarHeight());
+
+ // Content
+ if (layout()) {
+ QSize minLayoutSize = layout()->minimumSize();
+ if (minLayoutSize.isValid()) {
+ minWidth = qMax(minWidth, minLayoutSize.width() + 2 * margin);
+ minHeight += minLayoutSize.height();
+ }
+ } else if (d->baseWidget && d->baseWidget->isVisible()) {
+ QSize minBaseWidgetSize = d->baseWidget->minimumSizeHint();
+ if (minBaseWidgetSize.isValid()) {
+ minWidth = qMax(minWidth, minBaseWidgetSize.width() + 2 * margin);
+ minHeight += minBaseWidgetSize.height();
+ }
+ }
+
+#ifndef QT_NO_SIZEGRIP
+ // SizeGrip
+ int sizeGripHeight = 0;
+ if (d->sizeGrip && d->sizeGrip->isVisibleTo(const_cast<QMdiSubWindow *>(this)))
+ sizeGripHeight = d->sizeGrip->height();
+#if defined(Q_WS_MAC) && !defined(QT_NO_STYLE_MAC)
+ else if (parent() && qobject_cast<QMacStyle *>(style()) && !d->sizeGrip)
+ sizeGripHeight = style()->pixelMetric(QStyle::PM_SizeGripSize, 0, this);
+#endif
+ minHeight = qMax(minHeight, decorationHeight + sizeGripHeight);
+#endif
+
+ return QSize(minWidth, minHeight).expandedTo(QApplication::globalStrut());
+}
+
+QT_END_NAMESPACE
+
+#include "moc_qmdisubwindow.cpp"
+#include "qmdisubwindow.moc"
+
+#endif //QT_NO_MDIAREA
diff --git a/src/gui/widgets/qmdisubwindow.h b/src/gui/widgets/qmdisubwindow.h
new file mode 100644
index 0000000000..3dcb1bba42
--- /dev/null
+++ b/src/gui/widgets/qmdisubwindow.h
@@ -0,0 +1,159 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QMDISUBWINDOW_H
+#define QMDISUBWINDOW_H
+
+#include <QtGui/qwidget.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+#ifndef QT_NO_MDIAREA
+
+class QMenu;
+class QMdiArea;
+
+namespace QMdi { class ControlContainer; }
+class QMdiSubWindowPrivate;
+class Q_GUI_EXPORT QMdiSubWindow : public QWidget
+{
+ Q_OBJECT
+ Q_PROPERTY(int keyboardSingleStep READ keyboardSingleStep WRITE setKeyboardSingleStep)
+ Q_PROPERTY(int keyboardPageStep READ keyboardPageStep WRITE setKeyboardPageStep)
+public:
+ enum SubWindowOption {
+ AllowOutsideAreaHorizontally = 0x1, // internal
+ AllowOutsideAreaVertically = 0x2, // internal
+ RubberBandResize = 0x4,
+ RubberBandMove = 0x8
+ };
+ Q_DECLARE_FLAGS(SubWindowOptions, SubWindowOption)
+
+ QMdiSubWindow(QWidget *parent = 0, Qt::WindowFlags flags = 0);
+ ~QMdiSubWindow();
+
+ QSize sizeHint() const;
+ QSize minimumSizeHint() const;
+
+ void setWidget(QWidget *widget);
+ QWidget *widget() const;
+
+ QWidget *maximizedButtonsWidget() const; // internal
+ QWidget *maximizedSystemMenuIconWidget() const; // internal
+
+ bool isShaded() const;
+
+ void setOption(SubWindowOption option, bool on = true);
+ bool testOption(SubWindowOption) const;
+
+ void setKeyboardSingleStep(int step);
+ int keyboardSingleStep() const;
+
+ void setKeyboardPageStep(int step);
+ int keyboardPageStep() const;
+
+#ifndef QT_NO_MENU
+ void setSystemMenu(QMenu *systemMenu);
+ QMenu *systemMenu() const;
+#endif
+
+ QMdiArea *mdiArea() const;
+
+Q_SIGNALS:
+ void windowStateChanged(Qt::WindowStates oldState, Qt::WindowStates newState);
+ void aboutToActivate();
+
+public Q_SLOTS:
+#ifndef QT_NO_MENU
+ void showSystemMenu();
+#endif
+ void showShaded();
+
+protected:
+ bool eventFilter(QObject *object, QEvent *event);
+ bool event(QEvent *event);
+ void showEvent(QShowEvent *showEvent);
+ void hideEvent(QHideEvent *hideEvent);
+ void changeEvent(QEvent *changeEvent);
+ void closeEvent(QCloseEvent *closeEvent);
+ void leaveEvent(QEvent *leaveEvent);
+ void resizeEvent(QResizeEvent *resizeEvent);
+ void timerEvent(QTimerEvent *timerEvent);
+ void moveEvent(QMoveEvent *moveEvent);
+ void paintEvent(QPaintEvent *paintEvent);
+ void mousePressEvent(QMouseEvent *mouseEvent);
+ void mouseDoubleClickEvent(QMouseEvent *mouseEvent);
+ void mouseReleaseEvent(QMouseEvent *mouseEvent);
+ void mouseMoveEvent(QMouseEvent *mouseEvent);
+ void keyPressEvent(QKeyEvent *keyEvent);
+#ifndef QT_NO_CONTEXTMENU
+ void contextMenuEvent(QContextMenuEvent *contextMenuEvent);
+#endif
+ void focusInEvent(QFocusEvent *focusInEvent);
+ void focusOutEvent(QFocusEvent *focusOutEvent);
+ void childEvent(QChildEvent *childEvent);
+
+private:
+ Q_DISABLE_COPY(QMdiSubWindow)
+ Q_DECLARE_PRIVATE(QMdiSubWindow)
+ Q_PRIVATE_SLOT(d_func(), void _q_updateStaysOnTopHint())
+ Q_PRIVATE_SLOT(d_func(), void _q_enterInteractiveMode())
+ Q_PRIVATE_SLOT(d_func(), void _q_processFocusChanged(QWidget *, QWidget *))
+ friend class QMdiAreaPrivate;
+#ifndef QT_NO_TABBAR
+ friend class QMdiAreaTabBar;
+#endif
+ friend class QMdi::ControlContainer;
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(QMdiSubWindow::SubWindowOptions)
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QT_NO_MDIAREA
+
+#endif // QMDISUBWINDOW_H
diff --git a/src/gui/widgets/qmdisubwindow_p.h b/src/gui/widgets/qmdisubwindow_p.h
new file mode 100644
index 0000000000..2e672d6a1e
--- /dev/null
+++ b/src/gui/widgets/qmdisubwindow_p.h
@@ -0,0 +1,348 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QMDISUBWINDOW_P_H
+#define QMDISUBWINDOW_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 "qmdisubwindow.h"
+
+#ifndef QT_NO_MDIAREA
+
+#include <QStyle>
+#include <QStyleOptionTitleBar>
+#include <QMenuBar>
+#include <QSizeGrip>
+#include <QPointer>
+#include <QDebug>
+#include <private/qwidget_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QVBoxLayout;
+class QMouseEvent;
+
+namespace QMdi {
+template<typename T>
+class ControlElement : public T
+{
+public:
+ ControlElement(QMdiSubWindow *child) : T(child, 0)
+ {
+ Q_ASSERT(child);
+ mdiChild = child;
+ }
+
+ void *qt_metacast(const char *classname)
+ {
+ if (classname && strcmp(classname, "ControlElement") == 0)
+ return this;
+ return 0;
+ }
+
+ QPointer<QMdiSubWindow> mdiChild;
+};
+
+class ControlContainer : public QObject
+{
+public:
+ ControlContainer(QMdiSubWindow *mdiChild);
+ ~ControlContainer();
+
+#ifndef QT_NO_MENUBAR
+ void showButtonsInMenuBar(QMenuBar *menuBar);
+ void removeButtonsFromMenuBar(QMenuBar *menuBar = 0);
+ QMenuBar *menuBar() const { return m_menuBar; }
+#endif
+ void updateWindowIcon(const QIcon &windowIcon);
+ QWidget *controllerWidget() const { return m_controllerWidget; }
+ QWidget *systemMenuLabel() const { return m_menuLabel; }
+
+private:
+ QPointer<QWidget> previousLeft;
+ QPointer<QWidget> previousRight;
+#ifndef QT_NO_MENUBAR
+ QPointer<QMenuBar> m_menuBar;
+#endif
+ QPointer<QWidget> m_controllerWidget;
+ QPointer<QWidget> m_menuLabel;
+ QPointer<QMdiSubWindow> mdiChild;
+};
+} // namespace QMdi
+
+class QMdiSubWindowPrivate : public QWidgetPrivate
+{
+ Q_DECLARE_PUBLIC(QMdiSubWindow)
+public:
+ // Enums and typedefs.
+ enum Operation {
+ None,
+ Move,
+ TopResize,
+ BottomResize,
+ LeftResize,
+ RightResize,
+ TopLeftResize,
+ TopRightResize,
+ BottomLeftResize,
+ BottomRightResize
+ };
+
+ enum ChangeFlag {
+ HMove = 0x01,
+ VMove = 0x02,
+ HResize = 0x04,
+ VResize = 0x08,
+ HResizeReverse = 0x10,
+ VResizeReverse = 0x20
+ };
+
+ enum WindowStateAction {
+ RestoreAction,
+ MoveAction,
+ ResizeAction,
+ MinimizeAction,
+ MaximizeAction,
+ StayOnTopAction,
+ CloseAction,
+ /* Add new states _above_ this line! */
+ NumWindowStateActions
+ };
+
+ struct OperationInfo {
+ uint changeFlags;
+ Qt::CursorShape cursorShape;
+ QRegion region;
+ bool hover;
+ OperationInfo(uint changeFlags, Qt::CursorShape cursorShape, bool hover = true)
+ : changeFlags(changeFlags),
+ cursorShape(cursorShape),
+ hover(hover)
+ {}
+ };
+
+ typedef QMap<Operation, OperationInfo> OperationInfoMap;
+
+ QMdiSubWindowPrivate();
+
+ // Variables.
+ QPointer<QWidget> baseWidget;
+ QPointer<QWidget> restoreFocusWidget;
+ QPointer<QMdi::ControlContainer> controlContainer;
+#ifndef QT_NO_SIZEGRIP
+ QPointer<QSizeGrip> sizeGrip;
+#endif
+#ifndef QT_NO_RUBBERBAND
+ QRubberBand *rubberBand;
+#endif
+ QPoint mousePressPosition;
+ QRect oldGeometry;
+ QSize internalMinimumSize;
+ QSize userMinimumSize;
+ QSize restoreSize;
+ bool resizeEnabled;
+ bool moveEnabled;
+ bool isInInteractiveMode;
+#ifndef QT_NO_RUBBERBAND
+ bool isInRubberBandMode;
+#endif
+ bool isShadeMode;
+ bool ignoreWindowTitleChange;
+ bool ignoreNextActivationEvent;
+ bool activationEnabled;
+ bool isShadeRequestFromMinimizeMode;
+ bool isMaximizeMode;
+ bool isWidgetHiddenByUs;
+ bool isActive;
+ bool isExplicitlyDeactivated;
+ int keyboardSingleStep;
+ int keyboardPageStep;
+ int resizeTimerId;
+ Operation currentOperation;
+ QStyle::SubControl hoveredSubControl;
+ QStyle::SubControl activeSubControl;
+ Qt::FocusReason focusInReason;
+ OperationInfoMap operationMap;
+ QPointer<QMenu> systemMenu;
+#ifndef QT_NO_ACTIONS
+ QPointer<QAction> actions[NumWindowStateActions];
+#endif
+ QMdiSubWindow::SubWindowOptions options;
+ QString lastChildWindowTitle;
+ QPalette titleBarPalette;
+ QString windowTitle;
+ QFont font;
+ QIcon menuIcon;
+ QStyleOptionTitleBar cachedStyleOptions;
+ QString originalTitle;
+
+ // Slots.
+ void _q_updateStaysOnTopHint();
+ void _q_enterInteractiveMode();
+ void _q_processFocusChanged(QWidget *old, QWidget *now);
+
+ // Functions.
+ void leaveInteractiveMode();
+ void removeBaseWidget();
+ void initOperationMap();
+#ifndef QT_NO_MENU
+ void createSystemMenu();
+#endif
+ void updateCursor();
+ void updateDirtyRegions();
+ void updateGeometryConstraints();
+ void updateMask();
+ void setNewGeometry(const QPoint &pos);
+ void setMinimizeMode();
+ void setNormalMode();
+ void setMaximizeMode();
+ void setActive(bool activate, bool changeFocus = true);
+ void processClickedSubControl();
+ QRegion getRegion(Operation operation) const;
+ Operation getOperation(const QPoint &pos) const;
+ QStyleOptionTitleBar titleBarOptions() const;
+ void ensureWindowState(Qt::WindowState state);
+ int titleBarHeight(const QStyleOptionTitleBar &options) const;
+ void sizeParameters(int *margin, int *minWidth) const;
+ bool drawTitleBarWhenMaximized() const;
+#ifndef QT_NO_MENUBAR
+ QMenuBar *menuBar() const;
+ void showButtonsInMenuBar(QMenuBar *menuBar);
+ void removeButtonsFromMenuBar();
+#endif
+ void updateWindowTitle(bool requestFromChild);
+#ifndef QT_NO_RUBBERBAND
+ void enterRubberBandMode();
+ void leaveRubberBandMode();
+#endif
+ QPalette desktopPalette() const;
+ void updateActions();
+ void setFocusWidget();
+ void restoreFocus();
+ void setWindowFlags(Qt::WindowFlags windowFlags);
+ void setVisible(WindowStateAction, bool visible = true);
+#ifndef QT_NO_ACTION
+ void setEnabled(WindowStateAction, bool enable = true);
+#ifndef QT_NO_MENU
+ void addToSystemMenu(WindowStateAction, const QString &text, const char *slot);
+#endif
+#endif // QT_NO_ACTION
+ QSize iconSize() const;
+#ifndef QT_NO_SIZEGRIP
+ void setSizeGrip(QSizeGrip *sizeGrip);
+ void setSizeGripVisible(bool visible = true) const;
+#endif
+ void updateInternalWindowTitle();
+ QString originalWindowTitle();
+ void setNewWindowTitle();
+
+ inline int titleBarHeight() const
+ {
+ Q_Q(const QMdiSubWindow);
+ if (!q->parent() || q->windowFlags() & Qt::FramelessWindowHint
+ || (q->isMaximized() && !drawTitleBarWhenMaximized())) {
+ return 0;
+ }
+ QStyleOptionTitleBar options = titleBarOptions();
+ int height = options.rect.height();
+ if (hasBorder(options))
+ height += q->isMinimized() ? 8 : 4;
+ return height;
+ }
+
+ inline QStyle::SubControl getSubControl(const QPoint &pos) const
+ {
+ Q_Q(const QMdiSubWindow);
+ QStyleOptionTitleBar titleBarOptions = this->titleBarOptions();
+ return q->style()->hitTestComplexControl(QStyle::CC_TitleBar, &titleBarOptions, pos, q);
+ }
+
+ inline void setNewGeometry(QRect *geometry)
+ {
+ Q_Q(QMdiSubWindow);
+ Q_ASSERT(q->parent());
+ geometry->setSize(geometry->size().expandedTo(internalMinimumSize));
+#ifndef QT_NO_RUBBERBAND
+ if (isInRubberBandMode)
+ rubberBand->setGeometry(*geometry);
+ else
+#endif
+ q->setGeometry(*geometry);
+ }
+
+ inline bool hasBorder(const QStyleOptionTitleBar &options) const
+ {
+ Q_Q(const QMdiSubWindow);
+ return !q->style()->styleHint(QStyle::SH_TitleBar_NoBorder, &options, q);
+ }
+
+ inline bool autoRaise() const
+ {
+ Q_Q(const QMdiSubWindow);
+ return q->style()->styleHint(QStyle::SH_TitleBar_AutoRaise, 0, q);
+ }
+
+ inline bool isResizeOperation() const
+ {
+ return currentOperation != None && currentOperation != Move;
+ }
+
+ inline bool isMoveOperation() const
+ {
+ return currentOperation == Move;
+ }
+};
+
+#endif // QT_NO_MDIAREA
+
+QT_END_NAMESPACE
+
+#endif // QMDISUBWINDOW_P_H
diff --git a/src/gui/widgets/qmenu.cpp b/src/gui/widgets/qmenu.cpp
new file mode 100644
index 0000000000..ed3e338b32
--- /dev/null
+++ b/src/gui/widgets/qmenu.cpp
@@ -0,0 +1,3467 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qmenu.h"
+
+#ifndef QT_NO_MENU
+
+#include "qdebug.h"
+#include "qstyle.h"
+#include "qevent.h"
+#include "qtimer.h"
+#include "qlayout.h"
+#include "qpainter.h"
+#include "qapplication.h"
+#include "qdesktopwidget.h"
+#ifndef QT_NO_ACCESSIBILITY
+# include "qaccessible.h"
+#endif
+#ifndef QT_NO_EFFECTS
+# include <private/qeffects_p.h>
+#endif
+#ifndef QT_NO_WHATSTHIS
+# include <qwhatsthis.h>
+#endif
+
+#include "qmenu_p.h"
+#include "qmenubar_p.h"
+#include "qwidgetaction.h"
+#include "qtoolbutton.h"
+#include <private/qaction_p.h>
+#ifdef QT3_SUPPORT
+#include <qmenudata.h>
+#endif // QT3_SUPPORT
+
+#ifdef Q_WS_X11
+# include <private/qt_x11_p.h>
+#endif
+
+#if defined(Q_WS_MAC) && !defined(QT_NO_EFFECTS)
+# include <private/qcore_mac_p.h>
+# include <private/qt_cocoa_helpers_mac_p.h>
+#endif
+
+
+QT_BEGIN_NAMESPACE
+
+QPointer<QMenu> QMenuPrivate::mouseDown;
+QBasicTimer QMenuPrivate::menuDelayTimer;
+QBasicTimer QMenuPrivate::sloppyDelayTimer;
+
+/* QMenu code */
+// internal class used for the torn off popup
+class QTornOffMenu : public QMenu
+{
+ Q_OBJECT
+ class QTornOffMenuPrivate : public QMenuPrivate
+ {
+ Q_DECLARE_PUBLIC(QMenu)
+ public:
+ QTornOffMenuPrivate(QMenu *p) : causedMenu(p) {
+ tornoff = 1;
+ causedPopup.widget = 0;
+ causedPopup.action = ((QTornOffMenu*)p)->d_func()->causedPopup.action;
+ causedStack = ((QTornOffMenu*)p)->d_func()->calcCausedStack();
+ }
+ QList<QPointer<QWidget> > calcCausedStack() const { return causedStack; }
+ QPointer<QMenu> causedMenu;
+ QList<QPointer<QWidget> > causedStack;
+ };
+public:
+ QTornOffMenu(QMenu *p) : QMenu(*(new QTornOffMenuPrivate(p)))
+ {
+ Q_D(QTornOffMenu);
+ // make the torn-off menu a sibling of p (instead of a child)
+ QWidget *parentWidget = d->causedStack.isEmpty() ? p : d->causedStack.last();
+ if (parentWidget->parentWidget())
+ parentWidget = parentWidget->parentWidget();
+ setParent(parentWidget, Qt::Window | Qt::Tool);
+ setAttribute(Qt::WA_DeleteOnClose, true);
+ setAttribute(Qt::WA_X11NetWmWindowTypeMenu, true);
+ setWindowTitle(p->windowTitle());
+ setEnabled(p->isEnabled());
+ //QObject::connect(this, SIGNAL(triggered(QAction*)), this, SLOT(onTrigger(QAction*)));
+ //QObject::connect(this, SIGNAL(hovered(QAction*)), this, SLOT(onHovered(QAction*)));
+ QList<QAction*> items = p->actions();
+ for(int i = 0; i < items.count(); i++)
+ addAction(items.at(i));
+ }
+ void syncWithMenu(QMenu *menu, QActionEvent *act)
+ {
+ Q_D(QTornOffMenu);
+ if(menu != d->causedMenu)
+ return;
+ if (act->type() == QEvent::ActionAdded) {
+ insertAction(act->before(), act->action());
+ } else if (act->type() == QEvent::ActionRemoved)
+ removeAction(act->action());
+ }
+ void actionEvent(QActionEvent *e)
+ {
+ QMenu::actionEvent(e);
+ setFixedSize(sizeHint());
+ }
+public slots:
+ void onTrigger(QAction *action) { d_func()->activateAction(action, QAction::Trigger, false); }
+ void onHovered(QAction *action) { d_func()->activateAction(action, QAction::Hover, false); }
+private:
+ Q_DECLARE_PRIVATE(QTornOffMenu)
+ friend class QMenuPrivate;
+};
+
+void QMenuPrivate::init()
+{
+ Q_Q(QMenu);
+ activationRecursionGuard = false;
+#ifndef QT_NO_WHATSTHIS
+ q->setAttribute(Qt::WA_CustomWhatsThis);
+#endif
+ q->setAttribute(Qt::WA_X11NetWmWindowTypePopupMenu);
+ defaultMenuAction = menuAction = new QAction(q);
+ menuAction->d_func()->menu = q;
+ q->setMouseTracking(q->style()->styleHint(QStyle::SH_Menu_MouseTracking, 0, q));
+ if (q->style()->styleHint(QStyle::SH_Menu_Scrollable, 0, q)) {
+ scroll = new QMenuPrivate::QMenuScroller;
+ scroll->scrollFlags = QMenuPrivate::QMenuScroller::ScrollNone;
+ }
+}
+
+//Windows and KDE allows menus to cover the taskbar, while GNOME and Mac don't
+QRect QMenuPrivate::popupGeometry(int screen) const
+{
+#ifdef Q_WS_WIN
+ return QApplication::desktop()->screenGeometry(screen);
+#elif defined Q_WS_X11
+ if (X11->desktopEnvironment == DE_KDE)
+ return QApplication::desktop()->screenGeometry(screen);
+ else
+ return QApplication::desktop()->availableGeometry(screen);
+#else
+ return QApplication::desktop()->availableGeometry(screen);
+#endif
+}
+
+QList<QPointer<QWidget> > QMenuPrivate::calcCausedStack() const
+{
+ QList<QPointer<QWidget> > ret;
+ for(QWidget *widget = causedPopup.widget; widget; ) {
+ ret.append(widget);
+ if (QTornOffMenu *qtmenu = qobject_cast<QTornOffMenu*>(widget))
+ ret += qtmenu->d_func()->causedStack;
+ if (QMenu *qmenu = qobject_cast<QMenu*>(widget))
+ widget = qmenu->d_func()->causedPopup.widget;
+ else
+ break;
+ }
+ return ret;
+}
+
+void QMenuPrivate::calcActionRects(QMap<QAction*, QRect> &actionRects, QList<QAction*> &actionList) const
+{
+ Q_Q(const QMenu);
+ if (!itemsDirty) {
+ actionRects = this->actionRects;
+ actionList = this->actionList;
+ return;
+ }
+
+ actionRects.clear();
+ actionList.clear();
+ QList<QAction*> items = filterActions(q->actions());
+ int max_column_width = 0,
+ dh = popupGeometry(QApplication::desktop()->screenNumber(q)).height(),
+ ncols = 1,
+ y = 0;
+ const int hmargin = q->style()->pixelMetric(QStyle::PM_MenuHMargin, 0, q),
+ vmargin = q->style()->pixelMetric(QStyle::PM_MenuVMargin, 0, q),
+ icone = q->style()->pixelMetric(QStyle::PM_SmallIconSize, 0, q);
+
+ //for compatability now - will have to refactor this away..
+ tabWidth = 0;
+ maxIconWidth = 0;
+ hasCheckableItems = false;
+ for(int i = 0; i < items.count(); i++) {
+ QAction *action = items.at(i);
+ if (widgetItems.value(action))
+ continue;
+ hasCheckableItems |= action->isCheckable();
+ QIcon is = action->icon();
+ if (!is.isNull()) {
+ uint miw = maxIconWidth;
+ maxIconWidth = qMax<uint>(miw, icone + 4);
+ }
+ }
+
+ //calculate size
+ QFontMetrics qfm = q->fontMetrics();
+ for(int i = 0; i < items.count(); i++) {
+ QAction *action = items.at(i);
+
+ QFontMetrics fm(action->font().resolve(q->font()));
+ QSize sz;
+
+ //let the style modify the above size..
+ QStyleOptionMenuItem opt;
+ q->initStyleOption(&opt, action);
+ opt.rect = q->rect();
+
+ if (QWidget *w = widgetItems.value(action)) {
+ sz=w->sizeHint().expandedTo(w->minimumSize()).expandedTo(w->minimumSizeHint()).boundedTo(w->maximumSize());
+ } else {
+ //calc what I think the size is..
+ if (action->isSeparator()) {
+ sz = QSize(2, 2);
+ } else {
+ QString s = action->text();
+ int t = s.indexOf(QLatin1Char('\t'));
+ if (t != -1) {
+ tabWidth = qMax(int(tabWidth), qfm.width(s.mid(t+1)));
+ s = s.left(t);
+ #ifndef QT_NO_SHORTCUT
+ } else {
+ QKeySequence seq = action->shortcut();
+ if (!seq.isEmpty())
+ tabWidth = qMax(int(tabWidth), qfm.width(seq));
+ #endif
+ }
+ int w = fm.boundingRect(QRect(), Qt::TextSingleLine, s).width();
+ w -= s.count(QLatin1Char('&')) * fm.width(QLatin1Char('&'));
+ w += s.count(QLatin1String("&&")) * fm.width(QLatin1Char('&'));
+ sz.setWidth(w);
+ sz.setHeight(qMax(fm.height(), qfm.height()));
+
+ QIcon is = action->icon();
+ if (!is.isNull()) {
+ QSize is_sz = QSize(icone, icone);
+ if (is_sz.height() > sz.height())
+ sz.setHeight(is_sz.height());
+ }
+ }
+ sz = q->style()->sizeFromContents(QStyle::CT_MenuItem, &opt, sz, q);
+ }
+
+
+ if (!sz.isEmpty()) {
+ max_column_width = qMax(max_column_width, sz.width());
+ //wrapping
+ if (!scroll &&
+ y+sz.height()+vmargin > dh - (q->style()->pixelMetric(QStyle::PM_MenuDesktopFrameWidth, 0, q) * 2)) {
+ ncols++;
+ y = vmargin;
+ }
+ y += sz.height();
+ //append item
+ actionRects.insert(action, QRect(0, 0, sz.width(), sz.height()));
+ actionList.append(action);
+ }
+ }
+
+ if (tabWidth)
+ max_column_width += tabWidth; //finally add in the tab width
+
+ //calculate position
+ int x = hmargin;
+ y = vmargin;
+
+ for(int i = 0; i < actionList.count(); i++) {
+ QAction *action = actionList.at(i);
+ QRect &rect = actionRects[action];
+ if (rect.isNull())
+ continue;
+ if (!scroll &&
+ y+rect.height() > dh - (q->style()->pixelMetric(QStyle::PM_MenuDesktopFrameWidth, 0, q) * 2)) {
+ ncols--;
+ if (ncols < 0)
+ qWarning("QMenu: Column calculation mismatch (%d)", ncols);
+ x += max_column_width + hmargin;
+ y = vmargin;
+ }
+ rect.translate(x, y); //move
+ rect.setWidth(max_column_width); //uniform width
+ y += rect.height();
+ }
+}
+
+void QMenuPrivate::updateActions()
+{
+ Q_Q(const QMenu);
+ if (!itemsDirty)
+ return;
+ sloppyAction = 0;
+ calcActionRects(actionRects, actionList);
+ for (QHash<QAction *, QWidget *>::ConstIterator item = widgetItems.constBegin(),
+ end = widgetItems.constEnd(); item != end; ++item) {
+ QAction *action = item.key();
+ QWidget *widget = item.value();
+ widget->setGeometry(actionRect(action));
+ widget->setVisible(action->isVisible());
+ }
+ ncols = 1;
+ int last_left = q->style()->pixelMetric(QStyle::PM_MenuVMargin, 0, q);
+ if (!scroll) {
+ for(int i = 0; i < actionList.count(); i++) {
+ int left = actionRects.value(actionList.at(i)).left();
+ if (left > last_left) {
+ last_left = left;
+ ncols++;
+ }
+ }
+ }
+ itemsDirty = 0;
+}
+
+QList<QAction *> QMenuPrivate::filterActions(const QList<QAction *> &actions) const
+{
+ QList<QAction *> visibleActions;
+ int i = 0;
+ while (i < actions.count()) {
+ QAction *action = actions.at(i);
+ if (!action->isVisible()) {
+ ++i;
+ continue;
+ }
+ if (!action->isSeparator() || !collapsibleSeparators) {
+ visibleActions.append(action);
+ ++i;
+ continue;
+ }
+
+ // no leading separators
+ if (!visibleActions.isEmpty())
+ visibleActions.append(action);
+
+ // skip double/tripple/etc. separators
+ while (i < actions.count()
+ && (!actions.at(i)->isVisible() || actions.at(i)->isSeparator()))
+ ++i;
+ }
+
+ if (collapsibleSeparators) {
+ // remove trailing separators
+ while (!visibleActions.isEmpty() && visibleActions.last()->isSeparator())
+ visibleActions.removeLast();
+ }
+
+ return visibleActions;
+}
+
+QRect QMenuPrivate::actionRect(QAction *act) const
+{
+ Q_Q(const QMenu);
+ QRect ret = actionRects.value(act);
+ if (ret.isNull())
+ return ret;
+ if (scroll)
+ ret.translate(0, scroll->scrollOffset);
+ if (tearoff)
+ ret.translate(0, q->style()->pixelMetric(QStyle::PM_MenuTearoffHeight, 0, q));
+ const int fw = q->style()->pixelMetric(QStyle::PM_MenuPanelWidth, 0, q);
+ ret.translate(fw+leftmargin, fw+topmargin);
+ return ret;
+}
+
+void QMenuPrivate::hideUpToMenuBar()
+{
+ Q_Q(QMenu);
+ if (!tornoff) {
+ QWidget *caused = causedPopup.widget;
+ hideMenu(q); //hide after getting causedPopup
+ while(caused) {
+#ifndef QT_NO_MENUBAR
+ if (QMenuBar *mb = qobject_cast<QMenuBar*>(caused)) {
+ mb->d_func()->setCurrentAction(0);
+ mb->d_func()->setKeyboardMode(false);
+ caused = 0;
+ } else
+#endif
+ if (QMenu *m = qobject_cast<QMenu*>(caused)) {
+ caused = m->d_func()->causedPopup.widget;
+ if (!m->d_func()->tornoff)
+ hideMenu(m);
+ m->d_func()->setCurrentAction(0);
+ } else {
+#ifndef QT_NO_TOOLBUTTON
+ if (qobject_cast<QToolButton*>(caused) == 0)
+#endif
+ qWarning("QMenu: Internal error");
+ caused = 0;
+ }
+ }
+ }
+ setCurrentAction(0);
+}
+
+void QMenuPrivate::hideMenu(QMenu *menu)
+{
+ if (!menu)
+ return;
+
+#if !defined(QT_NO_EFFECTS)
+ menu->blockSignals(true);
+ aboutToHide = true;
+ // Flash item which is about to trigger (if any).
+ if (menu->style()->styleHint(QStyle::SH_Menu_FlashTriggeredItem)
+ && currentAction && currentAction == actionAboutToTrigger) {
+
+ QEventLoop eventLoop;
+ QAction *activeAction = currentAction;
+
+ // Deselect and wait 60 ms.
+ menu->setActiveAction(0);
+ QTimer::singleShot(60, &eventLoop, SLOT(quit()));
+ eventLoop.exec();
+
+ // Select and wait 20 ms.
+ menu->setActiveAction(activeAction);
+ QTimer::singleShot(20, &eventLoop, SLOT(quit()));
+ eventLoop.exec();
+ }
+
+ // Fade out.
+ if (menu->style()->styleHint(QStyle::SH_Menu_FadeOutOnHide)) {
+ // ### Qt 4.4:
+ // Should be something like: q->transitionWindow(Qt::FadeOutTransition, 150);
+ // Hopefully we'll integrate qt/research/windowtransitions into main before 4.4.
+ // Talk to Richard, Trenton or Bjoern.
+#if defined(Q_WS_MAC)
+ macWindowFade(qt_mac_window_for(menu)); // FIXME - what is the default duration for view animations
+
+ // Wait for the transition to complete.
+ QEventLoop eventLoop;
+ QTimer::singleShot(150, &eventLoop, SLOT(quit()));
+ eventLoop.exec();
+#endif // Q_WS_MAC
+ }
+ aboutToHide = false;
+ menu->blockSignals(false);
+#endif // QT_NO_EFFECTS
+ menu->hide();
+}
+
+void QMenuPrivate::popupAction(QAction *action, int delay, bool activateFirst)
+{
+ Q_Q(QMenu);
+ if (action && action->isEnabled()) {
+ if (!delay)
+ q->internalDelayedPopup();
+ else
+ QMenuPrivate::menuDelayTimer.start(delay, q);
+ if (activateFirst && action->menu())
+ action->menu()->d_func()->setFirstActionActive();
+ } else if (QMenu *menu = activeMenu) { //hide the current item
+ activeMenu = 0;
+ hideMenu(menu);
+ }
+}
+
+void QMenuPrivate::setSyncAction()
+{
+ Q_Q(QMenu);
+ QAction *current = currentAction;
+ if(current && (!current->isEnabled() || current->menu() || current->isSeparator()))
+ current = 0;
+ for(QWidget *caused = q; caused;) {
+ if (QMenu *m = qobject_cast<QMenu*>(caused)) {
+ caused = m->d_func()->causedPopup.widget;
+ if (m->d_func()->eventLoop)
+ m->d_func()->syncAction = current; // synchronous operation
+ } else {
+ break;
+ }
+ }
+}
+
+
+void QMenuPrivate::setFirstActionActive()
+{
+ Q_Q(QMenu);
+ const int scrollerHeight = q->style()->pixelMetric(QStyle::PM_MenuScrollerHeight, 0, q);
+ for(int i = 0, saccum = 0; i < actionList.count(); i++) {
+ QAction *act = actionList[i];
+ if (scroll && scroll->scrollFlags & QMenuScroller::ScrollUp) {
+ saccum -= actionRects.value(act).height();
+ if (saccum > scroll->scrollOffset-scrollerHeight)
+ continue;
+ }
+ if (!act->isSeparator() &&
+ (q->style()->styleHint(QStyle::SH_Menu_AllowActiveAndDisabled, 0, q)
+ || act->isEnabled())) {
+ setCurrentAction(act);
+ break;
+ }
+ }
+}
+
+// popup == -1 means do not popup, 0 means immediately, others mean use a timer
+void QMenuPrivate::setCurrentAction(QAction *action, int popup, SelectionReason reason, bool activateFirst)
+{
+ Q_Q(QMenu);
+ tearoffHighlighted = 0;
+ if (action == currentAction && !(action && action->menu() && action->menu() != activeMenu)) {
+ if(QMenu *menu = qobject_cast<QMenu*>(causedPopup.widget)) {
+ if(causedPopup.action && menu->d_func()->activeMenu == q)
+ menu->d_func()->setCurrentAction(causedPopup.action, 0, reason, false);
+ }
+ return;
+ }
+ if (currentAction)
+ q->update(actionRect(currentAction));
+
+ sloppyAction = 0;
+ if (!sloppyRegion.isEmpty())
+ sloppyRegion = QRegion();
+ QMenu *hideActiveMenu = activeMenu;
+#ifndef QT_NO_STATUSTIP
+ QAction *previousAction = currentAction;
+#endif
+#ifdef QT3_SUPPORT
+ emitHighlighted = (action && action != currentAction);
+#endif
+ currentAction = action;
+ if (action) {
+ if (!action->isSeparator()) {
+ activateAction(action, QAction::Hover);
+ if (popup != -1) {
+ hideActiveMenu = 0; //will be done "later"
+ // if the menu is visible then activate the required action,
+ // otherwise we just mark the action as currentAction
+ // and activate it when the menu will be popuped.
+ if (q->isVisible())
+ popupAction(currentAction, popup, activateFirst);
+ }
+ q->update(actionRect(action));
+ QWidget *widget = widgetItems.value(action);
+
+ if (reason == SelectedFromKeyboard) {
+ if (widget) {
+ if (widget->focusPolicy() != Qt::NoFocus)
+ widget->setFocus(Qt::TabFocusReason);
+ } else {
+ //when the action has no QWidget, the QMenu itself should
+ // get the focus
+ // Since the menu is a pop-up, it uses the popup reason.
+ if (!q->hasFocus())
+ q->setFocus(Qt::PopupFocusReason);
+ }
+ }
+ } else { //action is a separator
+ if (popup != -1)
+ hideActiveMenu = 0; //will be done "later"
+ }
+#ifndef QT_NO_STATUSTIP
+ } else if (previousAction) {
+ QWidget *w = causedPopup.widget;
+ while (QMenu *m = qobject_cast<QMenu*>(w))
+ w = m->d_func()->causedPopup.widget;
+ if (w) {
+ QString empty;
+ QStatusTipEvent tip(empty);
+ QApplication::sendEvent(w, &tip);
+ }
+#endif
+ }
+ if (hideActiveMenu) {
+ activeMenu = 0;
+#ifndef QT_NO_EFFECTS
+ // kill any running effect
+ qFadeEffect(0);
+ qScrollEffect(0);
+#endif
+ hideMenu(hideActiveMenu);
+ }
+}
+
+QAction *QMenuPrivate::actionAt(QPoint p) const
+{
+ if (!q_func()->rect().contains(p)) //sanity check
+ return 0;
+
+ for(int i = 0; i < actionList.count(); i++) {
+ QAction *act = actionList[i];
+ if (actionRect(act).contains(p))
+ return act;
+ }
+ return 0;
+}
+
+void QMenuPrivate::setOverrideMenuAction(QAction *a)
+{
+ Q_Q(QMenu);
+ QObject::disconnect(menuAction, SIGNAL(destroyed()), q, SLOT(_q_overrideMenuActionDestroyed()));
+ if (a) {
+ menuAction = a;
+ QObject::connect(a, SIGNAL(destroyed()), q, SLOT(_q_overrideMenuActionDestroyed()));
+ } else { //we revert back to the default action created by the QMenu itself
+ menuAction = defaultMenuAction;
+ }
+}
+
+void QMenuPrivate::_q_overrideMenuActionDestroyed()
+{
+ menuAction=defaultMenuAction;
+}
+
+/*!
+ Returns the action associated with this menu.
+*/
+QAction *QMenu::menuAction() const
+{
+ return d_func()->menuAction;
+}
+
+/*!
+ \property QMenu::title
+ \brief The title of the menu
+
+ This is equivalent to the QAction::text property of the menuAction().
+
+ By default, this property contains an empty string.
+*/
+QString QMenu::title() const
+{
+ return d_func()->menuAction->text();
+}
+
+void QMenu::setTitle(const QString &text)
+{
+ d_func()->menuAction->setText(text);
+}
+
+/*!
+ \property QMenu::icon
+
+ \brief The icon of the menu
+
+ This is equivalent to the QAction::icon property of the menuAction().
+
+ By default, if no icon is explicitly set, this property contains a null icon.
+*/
+QIcon QMenu::icon() const
+{
+ return d_func()->menuAction->icon();
+}
+
+void QMenu::setIcon(const QIcon &icon)
+{
+ d_func()->menuAction->setIcon(icon);
+}
+
+
+//actually performs the scrolling
+void QMenuPrivate::scrollMenu(QAction *action, QMenuScroller::ScrollLocation location, bool active)
+{
+ Q_Q(QMenu);
+ if (!scroll || !scroll->scrollFlags)
+ return;
+ int newOffset = 0;
+ const int scrollHeight = q->style()->pixelMetric(QStyle::PM_MenuScrollerHeight, 0, q);
+ const int topScroll = (scroll->scrollFlags & QMenuScroller::ScrollUp) ? scrollHeight : 0;
+ const int botScroll = (scroll->scrollFlags & QMenuScroller::ScrollDown) ? scrollHeight : 0;
+ const int vmargin = q->style()->pixelMetric(QStyle::PM_MenuVMargin, 0, q);
+ const int fw = q->style()->pixelMetric(QStyle::PM_MenuPanelWidth, 0, q);
+
+ if (location == QMenuScroller::ScrollTop) {
+ for(int i = 0, saccum = 0; i < actionList.count(); i++) {
+ QAction *act = actionList.at(i);
+ if (act == action) {
+ newOffset = topScroll - saccum;
+ break;
+ }
+ saccum += actionRects.value(act).height();
+ }
+ } else {
+ for(int i = 0, saccum = 0; i < actionList.count(); i++) {
+ QAction *act = actionList.at(i);
+ saccum += actionRects.value(act).height();
+ if (act == action) {
+ if (location == QMenuScroller::ScrollCenter)
+ newOffset = ((q->height() / 2) - botScroll) - (saccum - topScroll);
+ else
+ newOffset = (q->height() - botScroll) - saccum;
+ break;
+ }
+ }
+ if(newOffset)
+ newOffset -= fw*2;
+ }
+
+ //figure out which scroll flags
+ uint newScrollFlags = QMenuScroller::ScrollNone;
+ if (newOffset < 0) //easy and cheap one
+ newScrollFlags |= QMenuScroller::ScrollUp;
+ int saccum = newOffset;
+ for(int i = 0; i < actionList.count(); i++) {
+ saccum += actionRects.value(actionList.at(i)).height();
+ if (saccum > q->height()) {
+ newScrollFlags |= QMenuScroller::ScrollDown;
+ break;
+ }
+ }
+
+ if (!(newScrollFlags & QMenuScroller::ScrollDown) && (scroll->scrollFlags & QMenuScroller::ScrollDown)) {
+ newOffset = q->height() - (saccum - newOffset) - fw*2 - vmargin; //last item at bottom
+ }
+
+ if (!(newScrollFlags & QMenuScroller::ScrollUp) && (scroll->scrollFlags & QMenuScroller::ScrollUp)) {
+ newOffset = 0; //first item at top
+ }
+
+ if (newScrollFlags & QMenuScroller::ScrollUp)
+ newOffset -= vmargin;
+
+ QRect screen = popupGeometry(QApplication::desktop()->screenNumber(q));
+ const int desktopFrame = q->style()->pixelMetric(QStyle::PM_MenuDesktopFrameWidth, 0, q);
+ if (q->height() < screen.height()-(desktopFrame*2)-1) {
+ QRect geom = q->geometry();
+ if (newOffset > scroll->scrollOffset && (scroll->scrollFlags & newScrollFlags & QMenuScroller::ScrollUp)) { //scroll up
+ const int newHeight = geom.height()-(newOffset-scroll->scrollOffset);
+ if(newHeight > geom.height())
+ geom.setHeight(newHeight);
+ } else if(scroll->scrollFlags & newScrollFlags & QMenuScroller::ScrollDown) {
+ int newTop = geom.top() + (newOffset-scroll->scrollOffset);
+ if (newTop < desktopFrame+screen.top())
+ newTop = desktopFrame+screen.top();
+ if (newTop < geom.top()) {
+ geom.setTop(newTop);
+ newOffset = 0;
+ newScrollFlags &= ~QMenuScroller::ScrollUp;
+ }
+ }
+ if (geom.bottom() > screen.bottom() - desktopFrame)
+ geom.setBottom(screen.bottom() - desktopFrame);
+ if (geom.top() < desktopFrame+screen.top())
+ geom.setTop(desktopFrame+screen.top());
+ if (geom != q->geometry()) {
+#if 0
+ if (newScrollFlags & QMenuScroller::ScrollDown &&
+ q->geometry().top() - geom.top() >= -newOffset)
+ newScrollFlags &= ~QMenuScroller::ScrollDown;
+#endif
+ q->setGeometry(geom);
+ }
+ }
+
+ //actually update flags
+ scroll->scrollOffset = newOffset;
+ if (scroll->scrollOffset > 0)
+ scroll->scrollOffset = 0;
+ scroll->scrollFlags = newScrollFlags;
+ if (active)
+ setCurrentAction(action);
+
+ q->update(); //issue an update so we see all the new state..
+}
+
+void QMenuPrivate::scrollMenu(QMenuScroller::ScrollLocation location, bool active)
+{
+ Q_Q(QMenu);
+ if(location == QMenuScroller::ScrollBottom) {
+ for(int i = actionList.size()-1; i >= 0; --i) {
+ QAction *act = actionList.at(i);
+ if (!act->isSeparator() &&
+ (q->style()->styleHint(QStyle::SH_Menu_AllowActiveAndDisabled, 0, q)
+ || act->isEnabled())) {
+ if(scroll->scrollFlags & QMenuPrivate::QMenuScroller::ScrollDown)
+ scrollMenu(act, QMenuPrivate::QMenuScroller::ScrollBottom, active);
+ else if(active)
+ setCurrentAction(act, /*popup*/-1, QMenuPrivate::SelectedFromKeyboard);
+ break;
+ }
+ }
+ } else if(location == QMenuScroller::ScrollTop) {
+ for(int i = 0; i < actionList.size(); ++i) {
+ QAction *act = actionList.at(i);
+ if (!act->isSeparator() &&
+ (q->style()->styleHint(QStyle::SH_Menu_AllowActiveAndDisabled, 0, q)
+ || act->isEnabled())) {
+ if(scroll->scrollFlags & QMenuPrivate::QMenuScroller::ScrollUp)
+ scrollMenu(act, QMenuPrivate::QMenuScroller::ScrollTop, active);
+ else if(active)
+ setCurrentAction(act, /*popup*/-1, QMenuPrivate::SelectedFromKeyboard);
+ break;
+ }
+ }
+ }
+}
+
+//only directional
+void QMenuPrivate::scrollMenu(QMenuScroller::ScrollDirection direction, bool page, bool active)
+{
+ Q_Q(QMenu);
+ if (!scroll || !(scroll->scrollFlags & direction)) //not really possible...
+ return;
+ const int scrollHeight = q->style()->pixelMetric(QStyle::PM_MenuScrollerHeight, 0, q);
+ const int topScroll = (scroll->scrollFlags & QMenuScroller::ScrollUp) ? scrollHeight : 0;
+ const int botScroll = (scroll->scrollFlags & QMenuScroller::ScrollDown) ? scrollHeight : 0;
+ const int vmargin = q->style()->pixelMetric(QStyle::PM_MenuVMargin, 0, q);
+ const int fw = q->style()->pixelMetric(QStyle::PM_MenuPanelWidth, 0, q);
+ const int offset = topScroll ? topScroll-vmargin : 0;
+ if (direction == QMenuScroller::ScrollUp) {
+ for(int i = 0, saccum = 0; i < actionList.count(); i++) {
+ QAction *act = actionList.at(i);
+ const int iHeight = actionRects.value(act).height();
+ saccum -= iHeight;
+ if (saccum <= scroll->scrollOffset-offset) {
+ scrollMenu(act, page ? QMenuScroller::ScrollBottom : QMenuScroller::ScrollTop, active);
+ break;
+ }
+ }
+ } else if (direction == QMenuScroller::ScrollDown) {
+ bool scrolled = false;
+ for(int i = 0, saccum = 0; i < actionList.count(); i++) {
+ QAction *act = actionList.at(i);
+ const int iHeight = actionRects.value(act).height();
+ saccum -= iHeight;
+ if (saccum <= scroll->scrollOffset-offset) {
+ const int scrollerArea = q->height() - botScroll - fw*2;
+ int visible = (scroll->scrollOffset-offset) - saccum;
+ for(i++ ; i < actionList.count(); i++) {
+ act = actionList.at(i);
+ const int iHeight = actionRects.value(act).height();
+ visible += iHeight;
+ if (visible > scrollerArea - topScroll) {
+ scrolled = true;
+ scrollMenu(act, page ? QMenuScroller::ScrollTop : QMenuScroller::ScrollBottom, active);
+ break;
+ }
+ }
+ break;
+ }
+ }
+ if(!scrolled) {
+ scroll->scrollFlags &= ~QMenuScroller::ScrollDown;
+ q->update();
+ }
+ }
+}
+
+/* This is poor-mans eventfilters. This avoids the use of
+ eventFilter (which can be nasty for users of QMenuBar's). */
+bool QMenuPrivate::mouseEventTaken(QMouseEvent *e)
+{
+ Q_Q(QMenu);
+ QPoint pos = q->mapFromGlobal(e->globalPos());
+ if (scroll && !activeMenu) { //let the scroller "steal" the event
+ bool isScroll = false;
+ if (pos.x() >= 0 && pos.x() < q->width()) {
+ const int scrollerHeight = q->style()->pixelMetric(QStyle::PM_MenuScrollerHeight, 0, q);
+ for(int dir = QMenuScroller::ScrollUp; dir <= QMenuScroller::ScrollDown; dir = dir << 1) {
+ if (scroll->scrollFlags & dir) {
+ if (dir == QMenuScroller::ScrollUp)
+ isScroll = (pos.y() <= scrollerHeight);
+ else if (dir == QMenuScroller::ScrollDown)
+ isScroll = (pos.y() >= q->height()-scrollerHeight);
+ if (isScroll) {
+ scroll->scrollDirection = dir;
+ break;
+ }
+ }
+ }
+ }
+ if (isScroll) {
+ if (!scroll->scrollTimer)
+ scroll->scrollTimer = new QBasicTimer;
+ scroll->scrollTimer->start(50, q);
+ return true;
+ } else if (scroll->scrollTimer && scroll->scrollTimer->isActive()) {
+ scroll->scrollTimer->stop();
+ }
+ }
+
+ if (tearoff) { //let the tear off thingie "steal" the event..
+ QRect tearRect(0, 0, q->width(), q->style()->pixelMetric(QStyle::PM_MenuTearoffHeight, 0, q));
+ if (scroll && scroll->scrollFlags & QMenuPrivate::QMenuScroller::ScrollUp)
+ tearRect.translate(0, q->style()->pixelMetric(QStyle::PM_MenuScrollerHeight, 0, q));
+ q->update(tearRect);
+ if (tearRect.contains(pos) && hasMouseMoved(e->globalPos())) {
+ setCurrentAction(0);
+ tearoffHighlighted = 1;
+ if (e->type() == QEvent::MouseButtonRelease) {
+ if (!tornPopup)
+ tornPopup = new QTornOffMenu(q);
+ tornPopup->setGeometry(q->geometry());
+ tornPopup->show();
+ hideUpToMenuBar();
+ }
+ return true;
+ }
+ tearoffHighlighted = 0;
+ }
+
+ if (q->frameGeometry().contains(e->globalPos())) //otherwise if the event is in our rect we want it..
+ return false;
+
+ for(QWidget *caused = causedPopup.widget; caused;) {
+ bool passOnEvent = false;
+ QWidget *next_widget = 0;
+ QPoint cpos = caused->mapFromGlobal(e->globalPos());
+#ifndef QT_NO_MENUBAR
+ if (QMenuBar *mb = qobject_cast<QMenuBar*>(caused)) {
+ passOnEvent = mb->rect().contains(cpos);
+ } else
+#endif
+ if (QMenu *m = qobject_cast<QMenu*>(caused)) {
+ passOnEvent = m->rect().contains(cpos);
+ next_widget = m->d_func()->causedPopup.widget;
+ }
+ if (passOnEvent) {
+ if(e->type() != QEvent::MouseButtonRelease || mouseDown == caused) {
+ QMouseEvent new_e(e->type(), cpos, e->button(), e->buttons(), e->modifiers());
+ QApplication::sendEvent(caused, &new_e);
+ return true;
+ }
+ }
+ if (!next_widget)
+ break;
+ caused = next_widget;
+ }
+ return false;
+}
+
+void QMenuPrivate::activateCausedStack(const QList<QPointer<QWidget> > &causedStack, QAction *action, QAction::ActionEvent action_e, bool self)
+{
+ Q_ASSERT(!activationRecursionGuard);
+ activationRecursionGuard = true;
+#ifdef QT3_SUPPORT
+ const int actionId = q_func()->findIdForAction(action);
+#endif
+ if(self)
+ action->activate(action_e);
+
+ for(int i = 0; i < causedStack.size(); ++i) {
+ QPointer<QWidget> widget = causedStack.at(i);
+ if (!widget)
+ continue;
+ //fire
+ if (QMenu *qmenu = qobject_cast<QMenu*>(widget)) {
+ widget = qmenu->d_func()->causedPopup.widget;
+ if (action_e == QAction::Trigger) {
+ emit qmenu->triggered(action);
+ } else if (action_e == QAction::Hover) {
+ emit qmenu->hovered(action);
+#ifdef QT3_SUPPORT
+ if (emitHighlighted) {
+ emit qmenu->highlighted(actionId);
+ emitHighlighted = false;
+ }
+#endif
+ }
+#ifndef QT_NO_MENUBAR
+ } else if (QMenuBar *qmenubar = qobject_cast<QMenuBar*>(widget)) {
+ if (action_e == QAction::Trigger) {
+ emit qmenubar->triggered(action);
+#ifdef QT3_SUPPORT
+ emit qmenubar->activated(actionId);
+#endif
+ } else if (action_e == QAction::Hover) {
+ emit qmenubar->hovered(action);
+#ifdef QT3_SUPPORT
+ if (emitHighlighted) {
+ emit qmenubar->highlighted(actionId);
+ emitHighlighted = false;
+ }
+#endif
+ }
+ break; //nothing more..
+#endif
+ }
+ }
+ activationRecursionGuard = false;
+}
+
+void QMenuPrivate::activateAction(QAction *action, QAction::ActionEvent action_e, bool self)
+{
+ Q_Q(QMenu);
+#ifndef QT_NO_WHATSTHIS
+ bool inWhatsThisMode = QWhatsThis::inWhatsThisMode();
+#endif
+ if (!action || !q->isEnabled()
+ || (action_e == QAction::Trigger
+#ifndef QT_NO_WHATSTHIS
+ && !inWhatsThisMode
+#endif
+ && (action->isSeparator() ||!action->isEnabled())))
+ return;
+
+ /* I have to save the caused stack here because it will be undone after popup execution (ie in the hide).
+ Then I iterate over the list to actually send the events. --Sam
+ */
+ const QList<QPointer<QWidget> > causedStack = calcCausedStack();
+ if (action_e == QAction::Trigger) {
+#ifndef QT_NO_WHATSTHIS
+ if (!inWhatsThisMode)
+ actionAboutToTrigger = action;
+#endif
+
+ if (q->testAttribute(Qt::WA_DontShowOnScreen)) {
+ hideUpToMenuBar();
+ } else {
+ for(QWidget *widget = qApp->activePopupWidget(); widget; ) {
+ if (QMenu *qmenu = qobject_cast<QMenu*>(widget)) {
+ if(qmenu == q)
+ hideUpToMenuBar();
+ widget = qmenu->d_func()->causedPopup.widget;
+ } else {
+ break;
+ }
+ }
+ }
+
+#ifndef QT_NO_WHATSTHIS
+ if (inWhatsThisMode) {
+ QString s = action->whatsThis();
+ if (s.isEmpty())
+ s = whatsThis;
+ QWhatsThis::showText(q->mapToGlobal(actionRect(action).center()), s, q);
+ return;
+ }
+#endif
+ }
+
+
+ activateCausedStack(causedStack, action, action_e, self);
+
+
+ if (action_e == QAction::Hover) {
+#ifndef QT_NO_ACCESSIBILITY
+ if (QAccessible::isActive()) {
+ int actionIndex = indexOf(action) + 1;
+ QAccessible::updateAccessibility(q, actionIndex, QAccessible::Focus);
+ QAccessible::updateAccessibility(q, actionIndex, QAccessible::Selection);
+ }
+#endif
+ QWidget *w = causedPopup.widget;
+ while (QMenu *m = qobject_cast<QMenu*>(w))
+ w = m->d_func()->causedPopup.widget;
+ action->showStatusText(w);
+ } else {
+ actionAboutToTrigger = 0;
+ }
+}
+
+void QMenuPrivate::_q_actionTriggered()
+{
+ Q_Q(QMenu);
+ if (QAction *action = qobject_cast<QAction *>(q->sender())) {
+#ifdef QT3_SUPPORT
+ //we store it here because the action might be deleted/changed by connected slots
+ const int id = q->findIdForAction(action);
+#endif
+ emit q->triggered(action);
+#ifdef QT3_SUPPORT
+ emit q->activated(id);
+#endif
+
+ if (!activationRecursionGuard) {
+ //in case the action has not been activated by the mouse
+ //we check the parent hierarchy
+ QList< QPointer<QWidget> > list;
+ for(QWidget *widget = q->parentWidget(); widget; ) {
+ if (qobject_cast<QMenu*>(widget)
+#ifndef QT_NO_MENUBAR
+ || qobject_cast<QMenuBar*>(widget)
+#endif
+ ) {
+ list.append(widget);
+ widget = widget->parentWidget();
+ } else {
+ break;
+ }
+ }
+ activateCausedStack(list, action, QAction::Trigger, false);
+ }
+ }
+}
+
+void QMenuPrivate::_q_actionHovered()
+{
+ Q_Q(QMenu);
+ if (QAction * action = qobject_cast<QAction *>(q->sender())) {
+#ifdef QT3_SUPPORT
+ //we store it here because the action might be deleted/changed by connected slots
+ const int id = q->findIdForAction(action);
+#endif
+ emit q->hovered(action);
+#ifdef QT3_SUPPORT
+ if (emitHighlighted) {
+ emit q->highlighted(id);
+ emitHighlighted = false;
+ }
+#endif
+ }
+}
+
+bool QMenuPrivate::hasMouseMoved(const QPoint &globalPos)
+{
+ //determines if the mouse has moved (ie its intial position has
+ //changed by more than QApplication::startDragDistance()
+ //or if there were at least 6 mouse motions)
+ return motions > 6 ||
+ QApplication::startDragDistance() < (mousePopupPos - globalPos).manhattanLength();
+}
+
+
+/*!
+ Initialize \a option with the values from this menu and information from \a action. This method
+ is useful for subclasses when they need a QStyleOptionMenuItem, but don't want
+ to fill in all the information themselves.
+
+ \sa QStyleOption::initFrom() QMenuBar::initStyleOption()
+*/
+void QMenu::initStyleOption(QStyleOptionMenuItem *option, const QAction *action) const
+{
+ if (!option || !action)
+ return;
+
+ Q_D(const QMenu);
+ option->initFrom(this);
+ option->palette = palette();
+ option->state = QStyle::State_None;
+
+ if (window()->isActiveWindow())
+ option->state |= QStyle::State_Active;
+ if (isEnabled() && action->isEnabled()
+ && (!action->menu() || action->menu()->isEnabled()))
+ option->state |= QStyle::State_Enabled;
+ else
+ option->palette.setCurrentColorGroup(QPalette::Disabled);
+
+ option->font = action->font();
+
+ if (d->currentAction && d->currentAction == action && !d->currentAction->isSeparator()) {
+ option->state |= QStyle::State_Selected
+ | (d->mouseDown ? QStyle::State_Sunken : QStyle::State_None);
+ }
+
+ option->menuHasCheckableItems = d->hasCheckableItems;
+ if (!action->isCheckable()) {
+ option->checkType = QStyleOptionMenuItem::NotCheckable;
+ } else {
+ option->checkType = (action->actionGroup() && action->actionGroup()->isExclusive())
+ ? QStyleOptionMenuItem::Exclusive : QStyleOptionMenuItem::NonExclusive;
+ option->checked = action->isChecked();
+ }
+ if (action->menu())
+ option->menuItemType = QStyleOptionMenuItem::SubMenu;
+ else if (action->isSeparator())
+ option->menuItemType = QStyleOptionMenuItem::Separator;
+ else if (d->defaultAction == action)
+ option->menuItemType = QStyleOptionMenuItem::DefaultItem;
+ else
+ option->menuItemType = QStyleOptionMenuItem::Normal;
+ if (action->isIconVisibleInMenu())
+ option->icon = action->icon();
+ QString textAndAccel = action->text();
+#ifndef QT_NO_SHORTCUT
+ if (textAndAccel.indexOf(QLatin1Char('\t')) == -1) {
+ QKeySequence seq = action->shortcut();
+ if (!seq.isEmpty())
+ textAndAccel += QLatin1Char('\t') + QString(seq);
+ }
+#endif
+ option->text = textAndAccel;
+ option->tabWidth = d->tabWidth;
+ option->maxIconWidth = d->maxIconWidth;
+ option->menuRect = rect();
+}
+
+/*!
+ \class QMenu
+ \brief The QMenu class provides a menu widget for use in menu
+ bars, context menus, and other popup menus.
+
+ \ingroup application
+ \ingroup basicwidgets
+ \mainclass
+
+ A menu widget is a selection menu. It can be either a pull-down
+ menu in a menu bar or a standalone context menu. Pull-down menus
+ are shown by the menu bar when the user clicks on the respective
+ item or presses the specified shortcut key. Use
+ QMenuBar::addMenu() to insert a menu into a menu bar. Context
+ menus are usually invoked by some special keyboard key or by
+ right-clicking. They can be executed either asynchronously with
+ popup() or synchronously with exec(). Menus can also be invoked in
+ response to button presses; these are just like context menus
+ except for how they are invoked.
+
+ \raw HTML
+ <table align="center" cellpadding="0">
+ <tr>
+ <td>
+ \endraw
+ \inlineimage plastique-menu.png
+ \raw HTML
+ </td>
+ <td>
+ \endraw
+ \inlineimage windowsxp-menu.png
+ \raw HTML
+ </td>
+ <td>
+ \endraw
+ \inlineimage macintosh-menu.png
+ \raw HTML
+ </td>
+
+ </tr>
+ <tr>
+ <td colspan="3">
+ \endraw
+ A menu shown in \l{Plastique Style Widget Gallery}{Plastique widget style},
+ \l{Windows XP Style Widget Gallery}{Windows XP widget style},
+ and \l{Macintosh Style Widget Gallery}{Macintosh widget style}.
+ \raw HTML
+ </td>
+ </tr>
+ </table>
+ \endraw
+
+ \section1 Actions
+
+ A menu consists of a list of action items. Actions are added with
+ the addAction(), addActions() and insertAction() functions. An action
+ is represented vertically and rendered by QStyle. In addition, actions
+ can have a text label, an optional icon drawn on the very left side,
+ and shortcut key sequence such as "Ctrl+X".
+
+ The existing actions held by a menu can be found with actions().
+
+ There are four kinds of action items: separators, actions that
+ show a submenu, widgets, and actions that perform an action.
+ Separators are inserted with addSeparator(), submenus with addMenu(),
+ and all other items are considered action items.
+
+ When inserting action items you usually specify a receiver and a
+ slot. The receiver will be notifed whenever the item is
+ \l{QAction::triggered()}{triggered()}. In addition, QMenu provides
+ two signals, activated() and highlighted(), which signal the
+ QAction that was triggered from the menu.
+
+ You clear a menu with clear() and remove individual action items
+ with removeAction().
+
+ A QMenu can also provide a tear-off menu. A tear-off menu is a
+ top-level window that contains a copy of the menu. This makes it
+ possible for the user to "tear off" frequently used menus and
+ position them in a convenient place on the screen. If you want
+ this functionality for a particular menu, insert a tear-off handle
+ with setTearOffEnabled(). When using tear-off menus, bear in mind
+ that the concept isn't typically used on Microsoft Windows so
+ some users may not be familiar with it. Consider using a QToolBar
+ instead.
+
+ Widgets can be inserted into menus with the QWidgetAction class.
+ Instances of this class are used to hold widgets, and are inserted
+ into menus with the addAction() overload that takes a QAction.
+
+ Conversely, actions can be added to widgets with the addAction(),
+ addActions() and insertAction() functions.
+
+ \section1 QMenu on Qt for Windows CE
+
+ If a menu is integrated into the native menubar on Windows Mobile we
+ do not support the signals: aboutToHide (), aboutToShow () and hovered ().
+ It is not possible to display an icon in a native menu on Windows Mobile.
+
+ See the \l{mainwindows/menus}{Menus} example for an example of how
+ to use QMenuBar and QMenu in your application.
+
+ \bold{Important inherited functions:} addAction(), removeAction(), clear(),
+ addSeparator(), and addMenu().
+
+ \sa QMenuBar, {fowler}{GUI Design Handbook: Menu, Drop-Down and Pop-Up},
+ {Application Example}, {Menus Example}, {Recent Files Example}
+*/
+
+
+/*!
+ Constructs a menu with parent \a parent.
+
+ Although a popup menu is always a top-level widget, if a parent is
+ passed the popup menu will be deleted when that parent is
+ destroyed (as with any other QObject).
+*/
+QMenu::QMenu(QWidget *parent)
+ : QWidget(*new QMenuPrivate, parent, Qt::Popup)
+{
+ Q_D(QMenu);
+ d->init();
+}
+
+/*!
+ Constructs a menu with a \a title and a \a parent.
+
+ Although a popup menu is always a top-level widget, if a parent is
+ passed the popup menu will be deleted when that parent is
+ destroyed (as with any other QObject).
+
+ \sa title
+*/
+QMenu::QMenu(const QString &title, QWidget *parent)
+ : QWidget(*new QMenuPrivate, parent, Qt::Popup)
+{
+ Q_D(QMenu);
+ d->init();
+ d->menuAction->setText(title);
+}
+
+/*! \internal
+ */
+QMenu::QMenu(QMenuPrivate &dd, QWidget *parent)
+ : QWidget(dd, parent, Qt::Popup)
+{
+ Q_D(QMenu);
+ d->init();
+}
+
+/*!
+ Destroys the menu.
+*/
+QMenu::~QMenu()
+{
+ Q_D(QMenu);
+ for (QHash<QAction *, QWidget *>::ConstIterator item = d->widgetItems.constBegin(),
+ end = d->widgetItems.constEnd(); item != end; ++item) {
+ QWidgetAction *action = static_cast<QWidgetAction *>(item.key());
+ QWidget *widget = item.value();
+ if (action && widget)
+ action->releaseWidget(widget);
+ }
+ d->widgetItems.clear();
+
+ if (d->eventLoop)
+ d->eventLoop->exit();
+ if (d->tornPopup)
+ d->tornPopup->close();
+}
+
+/*!
+ \overload
+
+ This convenience function creates a new action with \a text.
+ The function adds the newly created action to the menu's
+ list of actions, and returns it.
+
+ \sa QWidget::addAction()
+*/
+QAction *QMenu::addAction(const QString &text)
+{
+ QAction *ret = new QAction(text, this);
+ addAction(ret);
+ return ret;
+}
+
+/*!
+ \overload
+
+ This convenience function creates a new action with an \a icon
+ and some \a text. The function adds the newly created action to
+ the menu's list of actions, and returns it.
+
+ \sa QWidget::addAction()
+*/
+QAction *QMenu::addAction(const QIcon &icon, const QString &text)
+{
+ QAction *ret = new QAction(icon, text, this);
+ addAction(ret);
+ return ret;
+}
+
+/*!
+ \overload
+
+ This convenience function creates a new action with the text \a
+ text and an optional shortcut \a shortcut. The action's
+ \l{QAction::triggered()}{triggered()} signal is connected to the
+ \a receiver's \a member slot. The function adds the newly created
+ action to the menu's list of actions and returns it.
+
+ \sa QWidget::addAction()
+*/
+QAction *QMenu::addAction(const QString &text, const QObject *receiver, const char* member, const QKeySequence &shortcut)
+{
+ QAction *action = new QAction(text, this);
+#ifdef QT_NO_SHORTCUT
+ Q_UNUSED(shortcut);
+#else
+ action->setShortcut(shortcut);
+#endif
+ QObject::connect(action, SIGNAL(triggered(bool)), receiver, member);
+ addAction(action);
+ return action;
+}
+
+/*!
+ \overload
+
+ This convenience function creates a new action with an \a icon and
+ some \a text and an optional shortcut \a shortcut. The action's
+ \l{QAction::triggered()}{triggered()} signal is connected to the
+ \a member slot of the \a receiver object. The function adds the
+ newly created action to the menu's list of actions, and returns it.
+
+ \sa QWidget::addAction()
+*/
+QAction *QMenu::addAction(const QIcon &icon, const QString &text, const QObject *receiver,
+ const char* member, const QKeySequence &shortcut)
+{
+ QAction *action = new QAction(icon, text, this);
+#ifdef QT_NO_SHORTCUT
+ Q_UNUSED(shortcut);
+#else
+ action->setShortcut(shortcut);
+#endif
+ QObject::connect(action, SIGNAL(triggered(bool)), receiver, member);
+ addAction(action);
+ return action;
+}
+
+/*!
+ This convenience function adds \a menu as a submenu to this menu.
+ It returns \a menu's menuAction(). This menu does not take
+ ownership of \a menu.
+
+ \sa QWidget::addAction() QMenu::menuAction()
+*/
+QAction *QMenu::addMenu(QMenu *menu)
+{
+ QAction *action = menu->menuAction();
+ addAction(action);
+ return action;
+}
+
+/*!
+ Appends a new QMenu with \a title to the menu. The menu
+ takes ownership of the menu. Returns the new menu.
+
+ \sa QWidget::addAction() QMenu::menuAction()
+*/
+QMenu *QMenu::addMenu(const QString &title)
+{
+ QMenu *menu = new QMenu(title, this);
+ addAction(menu->menuAction());
+ return menu;
+}
+
+/*!
+ Appends a new QMenu with \a icon and \a title to the menu. The menu
+ takes ownership of the menu. Returns the new menu.
+
+ \sa QWidget::addAction() QMenu::menuAction()
+*/
+QMenu *QMenu::addMenu(const QIcon &icon, const QString &title)
+{
+ QMenu *menu = new QMenu(title, this);
+ menu->setIcon(icon);
+ addAction(menu->menuAction());
+ return menu;
+}
+
+/*!
+ This convenience function creates a new separator action, i.e. an
+ action with QAction::isSeparator() returning true, and adds the new
+ action to this menu's list of actions. It returns the newly
+ created action.
+
+ \sa QWidget::addAction()
+*/
+QAction *QMenu::addSeparator()
+{
+ QAction *action = new QAction(this);
+ action->setSeparator(true);
+ addAction(action);
+ return action;
+}
+
+/*!
+ This convenience function inserts \a menu before action \a before
+ and returns the menus menuAction().
+
+ \sa QWidget::insertAction(), addMenu()
+*/
+QAction *QMenu::insertMenu(QAction *before, QMenu *menu)
+{
+ QAction *action = menu->menuAction();
+ insertAction(before, action);
+ return action;
+}
+
+/*!
+ This convenience function creates a new separator action, i.e. an
+ action with QAction::isSeparator() returning true. The function inserts
+ the newly created action into this menu's list of actions before
+ action \a before and returns it.
+
+ \sa QWidget::insertAction(), addSeparator()
+*/
+QAction *QMenu::insertSeparator(QAction *before)
+{
+ QAction *action = new QAction(this);
+ action->setSeparator(true);
+ insertAction(before, action);
+ return action;
+}
+
+/*!
+ This will set the default action to \a act. The default action may
+ have a visual queue depending on the current QStyle. A default
+ action is usually meant to indicate what will defaultly happen on a
+ drop, as shown in a context menu.
+
+ \sa defaultAction()
+*/
+void QMenu::setDefaultAction(QAction *act)
+{
+ d_func()->defaultAction = act;
+}
+
+/*!
+ Returns the current default action.
+
+ \sa setDefaultAction()
+*/
+QAction *QMenu::defaultAction() const
+{
+ return d_func()->defaultAction;
+}
+
+/*!
+ \property QMenu::tearOffEnabled
+ \brief whether the menu supports being torn off
+
+ When true, the menu contains a special tear-off item (often shown as a dashed
+ line at the top of the menu) that creates a copy of the menu when it is
+ triggered.
+
+ This "torn-off" copy lives in a separate window. It contains the same menu
+ items as the original menu, with the exception of the tear-off handle.
+
+ By default, this property is false.
+*/
+void QMenu::setTearOffEnabled(bool b)
+{
+ Q_D(QMenu);
+ if (d->tearoff == b)
+ return;
+ if (!b && d->tornPopup)
+ d->tornPopup->close();
+ d->tearoff = b;
+
+ d->itemsDirty = true;
+ if (isVisible())
+ resize(sizeHint());
+}
+
+bool QMenu::isTearOffEnabled() const
+{
+ return d_func()->tearoff;
+}
+
+/*!
+ When a menu is torn off a second menu is shown to display the menu
+ contents in a new window. When the menu is in this mode and the menu
+ is visible returns true; otherwise false.
+
+ \sa hideTearOffMenu() isTearOffEnabled()
+*/
+bool QMenu::isTearOffMenuVisible() const
+{
+ if (d_func()->tornPopup)
+ return d_func()->tornPopup->isVisible();
+ return false;
+}
+
+/*!
+ This function will forcibly hide the torn off menu making it
+ disappear from the users desktop.
+
+ \sa isTearOffMenuVisible() isTearOffEnabled()
+*/
+void QMenu::hideTearOffMenu()
+{
+ if (d_func()->tornPopup)
+ d_func()->tornPopup->close();
+}
+
+
+/*!
+ Sets the currently highlighted action to \a act.
+*/
+void QMenu::setActiveAction(QAction *act)
+{
+ Q_D(QMenu);
+ d->setCurrentAction(act, 0);
+ if (d->scroll)
+ d->scrollMenu(act, QMenuPrivate::QMenuScroller::ScrollCenter);
+}
+
+
+/*!
+ Returns the currently highlighted action, or 0 if no
+ action is currently highlighted.
+*/
+QAction *QMenu::activeAction() const
+{
+ return d_func()->currentAction;
+}
+
+/*!
+ \since 4.2
+
+ Returns true if there are no visible actions inserted into the menu, false
+ otherwise.
+
+ \sa QWidget::actions()
+*/
+
+bool QMenu::isEmpty() const
+{
+ bool ret = true;
+ for(int i = 0; ret && i < actions().count(); ++i) {
+ const QAction *action = actions().at(i);
+ if (!action->isSeparator() && action->isVisible()) {
+ ret = false;
+ }
+ }
+ return ret;
+}
+
+/*!
+ Removes all the menu's actions. Actions owned by the menu and not
+ shown in any other widget are deleted.
+
+ \sa removeAction()
+*/
+void QMenu::clear()
+{
+ QList<QAction*> acts = actions();
+ for(int i = 0; i < acts.size(); i++) {
+ removeAction(acts[i]);
+ if (acts[i]->parent() == this && acts[i]->d_func()->widgets.isEmpty())
+ delete acts[i];
+ }
+}
+
+/*!
+ If a menu does not fit on the screen it lays itself out so that it
+ does fit. It is style dependent what layout means (for example, on
+ Windows it will use multiple columns).
+
+ This functions returns the number of columns necessary.
+*/
+int QMenu::columnCount() const
+{
+ return d_func()->ncols;
+}
+
+/*!
+ Returns the item at \a pt; returns 0 if there is no item there.
+*/
+QAction *QMenu::actionAt(const QPoint &pt) const
+{
+ if (QAction *ret = d_func()->actionAt(pt))
+ return ret;
+ return 0;
+}
+
+/*!
+ Returns the geometry of action \a act.
+*/
+QRect QMenu::actionGeometry(QAction *act) const
+{
+ return d_func()->actionRect(act);
+}
+
+/*!
+ \reimp
+*/
+QSize QMenu::sizeHint() const
+{
+ Q_D(const QMenu);
+ ensurePolished();
+ QMap<QAction*, QRect> actionRects;
+ QList<QAction*> actionList;
+ d->calcActionRects(actionRects, actionList);
+
+ QSize s;
+ QStyleOption opt(0);
+ opt.rect = rect();
+ opt.palette = palette();
+ opt.state = QStyle::State_None;
+ for (QMap<QAction*, QRect>::const_iterator i = actionRects.constBegin();
+ i != actionRects.constEnd(); ++i) {
+ if (i.value().bottom() > s.height())
+ s.setHeight(i.value().y()+i.value().height());
+ if (i.value().right() > s.width())
+ s.setWidth(i.value().right());
+ }
+ if (d->tearoff)
+ s.rheight() += style()->pixelMetric(QStyle::PM_MenuTearoffHeight, &opt, this);
+ if (const int fw = style()->pixelMetric(QStyle::PM_MenuPanelWidth, &opt, this)) {
+ s.rwidth() += fw*2;
+ s.rheight() += fw*2;
+ }
+ // Note that the action rects calculated above already include
+ // the top and left margins, so we only need to add margins for
+ // the bottom and right.
+ s.rwidth() += style()->pixelMetric(QStyle::PM_MenuHMargin, &opt, this);
+ s.rheight() += style()->pixelMetric(QStyle::PM_MenuVMargin, &opt, this);
+
+ s += QSize(d->leftmargin + d->rightmargin, d->topmargin + d->bottommargin);
+
+ return style()->sizeFromContents(QStyle::CT_Menu, &opt,
+ s.expandedTo(QApplication::globalStrut()), this);
+}
+
+/*!
+ Displays the menu so that the action \a atAction will be at the
+ specified \e global position \a p. To translate a widget's local
+ coordinates into global coordinates, use QWidget::mapToGlobal().
+
+ When positioning a menu with exec() or popup(), bear in mind that
+ you cannot rely on the menu's current size(). For performance
+ reasons, the menu adapts its size only when necessary, so in many
+ cases, the size before and after the show is different. Instead,
+ use sizeHint() which calculates the proper size depending on the
+ menu's current contents.
+
+ \sa QWidget::mapToGlobal(), exec()
+*/
+void QMenu::popup(const QPoint &p, QAction *atAction)
+{
+ Q_D(QMenu);
+ if (d->scroll) { //reset scroll state from last popup
+ d->scroll->scrollOffset = 0;
+ d->scroll->scrollFlags = QMenuPrivate::QMenuScroller::ScrollNone;
+ }
+ d->tearoffHighlighted = 0;
+ d->motions = 0;
+ d->doChildEffects = true;
+
+#ifndef QT_NO_MENUBAR
+ // if this menu is part of a chain attached to a QMenuBar, set the
+ // _NET_WM_WINDOW_TYPE_DROPDOWN_MENU X11 window type
+ QWidget* top = this;
+ while (QMenu* m = qobject_cast<QMenu *>(top))
+ top = m->d_func()->causedPopup.widget;
+ setAttribute(Qt::WA_X11NetWmWindowTypeDropDownMenu, qobject_cast<QMenuBar *>(top) != 0);
+#endif
+
+ ensurePolished(); // Get the right font
+ emit aboutToShow();
+ d->updateActions();
+ QPoint pos = p;
+ QSize size = sizeHint();
+ QRect screen = d->popupGeometry(QApplication::desktop()->screenNumber(p));
+ const int desktopFrame = style()->pixelMetric(QStyle::PM_MenuDesktopFrameWidth, 0, this);
+ bool adjustToDesktop = !window()->testAttribute(Qt::WA_DontShowOnScreen);
+ if (d->ncols > 1) {
+ pos.setY(screen.top()+desktopFrame);
+ } else if (atAction) {
+ for(int i=0, above_height=0; i<(int)d->actionList.count(); i++) {
+ QAction *action = d->actionList.at(i);
+ if (action == atAction) {
+ int newY = pos.y()-above_height;
+ if (d->scroll && newY < desktopFrame) {
+ d->scroll->scrollFlags = d->scroll->scrollFlags
+ | QMenuPrivate::QMenuScroller::ScrollUp;
+ d->scroll->scrollOffset = newY;
+ newY = desktopFrame;
+ }
+ pos.setY(newY);
+
+ if (d->scroll && d->scroll->scrollFlags != QMenuPrivate::QMenuScroller::ScrollNone
+ && !style()->styleHint(QStyle::SH_Menu_FillScreenWithScroll, 0, this)) {
+ int below_height = above_height + d->scroll->scrollOffset;
+ for(int i2 = i; i2 < (int)d->actionList.count(); i2++)
+ below_height += d->actionRects.value(d->actionList.at(i2)).height();
+ size.setHeight(below_height);
+ }
+ break;
+ } else {
+ above_height += d->actionRects.value(action).height();
+ }
+ }
+ }
+
+ QPoint mouse = QCursor::pos();
+ d->mousePopupPos = mouse;
+ const bool snapToMouse = (QRect(p.x()-3, p.y()-3, 6, 6).contains(mouse));
+
+ if (adjustToDesktop) {
+ //handle popup falling "off screen"
+ if (qApp->layoutDirection() == Qt::RightToLeft) {
+ if(snapToMouse) //position flowing left from the mouse
+ pos.setX(mouse.x()-size.width());
+
+ if (pos.x() < screen.left()+desktopFrame)
+ pos.setX(qMax(p.x(), screen.left()+desktopFrame));
+ if (pos.x()+size.width()-1 > screen.right()-desktopFrame)
+ pos.setX(qMax(p.x()-size.width(), screen.right()-desktopFrame-size.width()+1));
+ } else {
+ if (pos.x()+size.width()-1 > screen.right()-desktopFrame)
+ pos.setX(qMin(p.x()+size.width(), screen.right()-desktopFrame-size.width()+1));
+ if (pos.x() < screen.left()+desktopFrame)
+ pos.setX(qMax(p.x(), screen.left() + desktopFrame));
+ }
+ if (pos.y() + size.height() - 1 > screen.bottom() - desktopFrame) {
+ if(snapToMouse)
+ pos.setY(qMin(mouse.y() - (size.height() + desktopFrame), screen.bottom()-desktopFrame-size.height()+1));
+ else
+ pos.setY(qMax(p.y() - (size.height() + desktopFrame), screen.bottom()-desktopFrame-size.height()+1));
+ } else if (pos.y() < screen.top() + desktopFrame) {
+ pos.setY(screen.top() + desktopFrame);
+ }
+
+ if (pos.y() < screen.top() + desktopFrame)
+ pos.setY(screen.top() + desktopFrame);
+ if (pos.y()+size.height()-1 > screen.bottom() - desktopFrame) {
+ if (d->scroll) {
+ d->scroll->scrollFlags |= uint(QMenuPrivate::QMenuScroller::ScrollDown);
+ int y = qMax(screen.y(),pos.y());
+ size.setHeight(screen.bottom()-(desktopFrame*2)-y);
+ } else {
+ // Too big for screen, bias to see bottom of menu (for some reason)
+ pos.setY(screen.bottom()-size.height()+1);
+ }
+ }
+ }
+ setGeometry(QRect(pos, size));
+#ifndef QT_NO_EFFECTS
+ int hGuess = qApp->layoutDirection() == Qt::RightToLeft ? QEffects::LeftScroll : QEffects::RightScroll;
+ int vGuess = QEffects::DownScroll;
+ if (qApp->layoutDirection() == Qt::RightToLeft) {
+ if ((snapToMouse && (pos.x() + size.width()/2 > mouse.x())) ||
+ (qobject_cast<QMenu*>(d->causedPopup.widget) && pos.x() + size.width()/2 > d->causedPopup.widget->x()))
+ hGuess = QEffects::RightScroll;
+ } else {
+ if ((snapToMouse && (pos.x() + size.width()/2 < mouse.x())) ||
+ (qobject_cast<QMenu*>(d->causedPopup.widget) && pos.x() + size.width()/2 < d->causedPopup.widget->x()))
+ hGuess = QEffects::LeftScroll;
+ }
+
+#ifndef QT_NO_MENUBAR
+ if ((snapToMouse && (pos.y() + size.height()/2 < mouse.y())) ||
+ (qobject_cast<QMenuBar*>(d->causedPopup.widget) &&
+ pos.y() + size.width()/2 < d->causedPopup.widget->mapToGlobal(d->causedPopup.widget->pos()).y()))
+ vGuess = QEffects::UpScroll;
+#endif
+ if (QApplication::isEffectEnabled(Qt::UI_AnimateMenu)) {
+ bool doChildEffects = true;
+#ifndef QT_NO_MENUBAR
+ if (QMenuBar *mb = qobject_cast<QMenuBar*>(d->causedPopup.widget)) {
+ doChildEffects = mb->d_func()->doChildEffects;
+ mb->d_func()->doChildEffects = false;
+ } else
+#endif
+ if (QMenu *m = qobject_cast<QMenu*>(d->causedPopup.widget)) {
+ doChildEffects = m->d_func()->doChildEffects;
+ m->d_func()->doChildEffects = false;
+ }
+
+ if (doChildEffects) {
+ if (QApplication::isEffectEnabled(Qt::UI_FadeMenu))
+ qFadeEffect(this);
+ else if (d->causedPopup.widget)
+ qScrollEffect(this, qobject_cast<QMenu*>(d->causedPopup.widget) ? hGuess : vGuess);
+ else
+ qScrollEffect(this, hGuess | vGuess);
+ } else {
+ // kill any running effect
+ qFadeEffect(0);
+ qScrollEffect(0);
+
+ show();
+ }
+ } else
+#endif
+ {
+ show();
+ }
+
+#ifndef QT_NO_ACCESSIBILITY
+ QAccessible::updateAccessibility(this, 0, QAccessible::PopupMenuStart);
+#endif
+}
+
+/*!
+ Executes this menu synchronously.
+
+ This is equivalent to \c{exec(pos())}.
+
+ This returns the triggered QAction in either the popup menu or one
+ of its submenus, or 0 if no item was triggered (normally because
+ the user pressed Esc).
+
+ In most situations you'll want to specify the position yourself,
+ for example, the current mouse position:
+ \snippet doc/src/snippets/code/src_gui_widgets_qmenu.cpp 0
+ or aligned to a widget:
+ \snippet doc/src/snippets/code/src_gui_widgets_qmenu.cpp 1
+ or in reaction to a QMouseEvent *e:
+ \snippet doc/src/snippets/code/src_gui_widgets_qmenu.cpp 2
+*/
+QAction *QMenu::exec()
+{
+ return exec(pos());
+}
+
+
+/*!
+ \overload
+
+ Executes this menu synchronously.
+
+ Pops up the menu so that the action \a action will be at the
+ specified \e global position \a p. To translate a widget's local
+ coordinates into global coordinates, use QWidget::mapToGlobal().
+
+ This returns the triggered QAction in either the popup menu or one
+ of its submenus, or 0 if no item was triggered (normally because
+ the user pressed Esc).
+
+ Note that all signals are emitted as usual. If you connect a
+ QAction to a slot and call the menu's exec(), you get the result
+ both via the signal-slot connection and in the return value of
+ exec().
+
+ Common usage is to position the menu at the current mouse
+ position:
+ \snippet doc/src/snippets/code/src_gui_widgets_qmenu.cpp 3
+ or aligned to a widget:
+ \snippet doc/src/snippets/code/src_gui_widgets_qmenu.cpp 4
+ or in reaction to a QMouseEvent *e:
+ \snippet doc/src/snippets/code/src_gui_widgets_qmenu.cpp 5
+
+ When positioning a menu with exec() or popup(), bear in mind that
+ you cannot rely on the menu's current size(). For performance
+ reasons, the menu adapts its size only when necessary. So in many
+ cases, the size before and after the show is different. Instead,
+ use sizeHint() which calculates the proper size depending on the
+ menu's current contents.
+
+ \sa popup(), QWidget::mapToGlobal()
+*/
+QAction *QMenu::exec(const QPoint &p, QAction *action)
+{
+ Q_D(QMenu);
+ createWinId();
+ QEventLoop eventLoop;
+ d->eventLoop = &eventLoop;
+ popup(p, action);
+
+ QPointer<QObject> guard = this;
+ (void) eventLoop.exec();
+ if (guard.isNull())
+ return 0;
+
+ action = d->syncAction;
+ d->syncAction = 0;
+ d->eventLoop = 0;
+ return action;
+}
+
+/*!
+ \overload
+
+ Executes a menu synchronously.
+
+ The menu's actions are specified by the list of \a actions. The menu will
+ pop up so that the specified action, \a at, appears at global position \a
+ pos. If \a at is not specified then the menu appears at position \a
+ pos. \a parent is the menu's parent widget; specifying the parent will
+ provide context when \a pos alone is not enough to decide where the menu
+ should go (e.g., with multiple desktops or when the parent is embedded in
+ QGraphicsView).
+
+ The function returns the triggered QAction in either the popup
+ menu or one of its submenus, or 0 if no item was triggered
+ (normally because the user pressed Esc).
+
+ This is equivalent to:
+ \snippet doc/src/snippets/code/src_gui_widgets_qmenu.cpp 6
+
+ \sa popup(), QWidget::mapToGlobal()
+*/
+QAction *QMenu::exec(QList<QAction*> actions, const QPoint &pos, QAction *at, QWidget *parent)
+{
+ QMenu menu(parent);
+ for(QList<QAction*>::ConstIterator it = actions.constBegin(); it != actions.constEnd(); ++it)
+ menu.addAction((*it));
+ return menu.exec(pos, at);
+}
+
+/*!
+ \overload
+
+ Executes a menu synchronously.
+
+ The menu's actions are specified by the list of \a actions. The menu
+ will pop up so that the specified action, \a at, appears at global
+ position \a pos. If \a at is not specified then the menu appears
+ at position \a pos.
+
+ The function returns the triggered QAction in either the popup
+ menu or one of its submenus, or 0 if no item was triggered
+ (normally because the user pressed Esc).
+
+ This is equivalent to:
+ \snippet doc/src/snippets/code/src_gui_widgets_qmenu.cpp 6
+
+ \sa popup(), QWidget::mapToGlobal()
+*/
+QAction *QMenu::exec(QList<QAction*> actions, const QPoint &pos, QAction *at)
+{
+ // ### Qt 5: merge
+ return exec(actions, pos, at, 0);
+}
+
+/*!
+ \reimp
+*/
+void QMenu::hideEvent(QHideEvent *)
+{
+ Q_D(QMenu);
+ emit aboutToHide();
+ if (d->eventLoop)
+ d->eventLoop->exit();
+ d->setCurrentAction(0);
+#ifndef QT_NO_ACCESSIBILITY
+ QAccessible::updateAccessibility(this, 0, QAccessible::PopupMenuEnd);
+#endif
+#ifndef QT_NO_MENUBAR
+ if (QMenuBar *mb = qobject_cast<QMenuBar*>(d->causedPopup.widget))
+ mb->d_func()->setCurrentAction(0);
+#endif
+ d->mouseDown = 0;
+ d->hasHadMouse = false;
+ d->causedPopup.widget = 0;
+ d->causedPopup.action = 0;
+}
+
+/*!
+ \reimp
+*/
+void QMenu::paintEvent(QPaintEvent *e)
+{
+ Q_D(QMenu);
+ QPainter p(this);
+ QRegion emptyArea = QRegion(rect());
+
+ QStyleOptionMenuItem menuOpt;
+ menuOpt.initFrom(this);
+ menuOpt.state = QStyle::State_None;
+ menuOpt.checkType = QStyleOptionMenuItem::NotCheckable;
+ menuOpt.maxIconWidth = 0;
+ menuOpt.tabWidth = 0;
+ style()->drawPrimitive(QStyle::PE_PanelMenu, &menuOpt, &p, this);
+
+ //draw the items that need updating..
+ for (int i = 0; i < d->actionList.count(); ++i) {
+ QAction *action = d->actionList.at(i);
+ QRect adjustedActionRect = d->actionRect(action);
+ if (!e->rect().intersects(adjustedActionRect)
+ || d->widgetItems.value(action))
+ continue;
+ //set the clip region to be extra safe (and adjust for the scrollers)
+ QRegion adjustedActionReg(adjustedActionRect);
+ emptyArea -= adjustedActionReg;
+ p.setClipRegion(adjustedActionReg);
+
+ QStyleOptionMenuItem opt;
+ initStyleOption(&opt, action);
+ opt.rect = adjustedActionRect;
+ style()->drawControl(QStyle::CE_MenuItem, &opt, &p, this);
+ }
+
+ const int fw = style()->pixelMetric(QStyle::PM_MenuPanelWidth, 0, this);
+ //draw the scroller regions..
+ if (d->scroll) {
+ const int scrollerHeight = style()->pixelMetric(QStyle::PM_MenuScrollerHeight, 0, this);
+ menuOpt.menuItemType = QStyleOptionMenuItem::Scroller;
+ menuOpt.state |= QStyle::State_Enabled;
+ if (d->scroll->scrollFlags & QMenuPrivate::QMenuScroller::ScrollUp) {
+ menuOpt.rect.setRect(fw, fw, width() - (fw * 2), scrollerHeight);
+ emptyArea -= QRegion(menuOpt.rect);
+ p.setClipRect(menuOpt.rect);
+ style()->drawControl(QStyle::CE_MenuScroller, &menuOpt, &p, this);
+ }
+ if (d->scroll->scrollFlags & QMenuPrivate::QMenuScroller::ScrollDown) {
+ menuOpt.rect.setRect(fw, height() - scrollerHeight - fw, width() - (fw * 2),
+ scrollerHeight);
+ emptyArea -= QRegion(menuOpt.rect);
+ menuOpt.state |= QStyle::State_DownArrow;
+ p.setClipRect(menuOpt.rect);
+ style()->drawControl(QStyle::CE_MenuScroller, &menuOpt, &p, this);
+ }
+ }
+ //paint the tear off..
+ if (d->tearoff) {
+ menuOpt.menuItemType = QStyleOptionMenuItem::TearOff;
+ menuOpt.rect.setRect(fw, fw, width() - (fw * 2),
+ style()->pixelMetric(QStyle::PM_MenuTearoffHeight, 0, this));
+ if (d->scroll && d->scroll->scrollFlags & QMenuPrivate::QMenuScroller::ScrollUp)
+ menuOpt.rect.translate(0, style()->pixelMetric(QStyle::PM_MenuScrollerHeight, 0, this));
+ emptyArea -= QRegion(menuOpt.rect);
+ p.setClipRect(menuOpt.rect);
+ menuOpt.state = QStyle::State_None;
+ if (d->tearoffHighlighted)
+ menuOpt.state |= QStyle::State_Selected;
+ style()->drawControl(QStyle::CE_MenuTearoff, &menuOpt, &p, this);
+ }
+ //draw border
+ if (fw) {
+ QRegion borderReg;
+ borderReg += QRect(0, 0, fw, height()); //left
+ borderReg += QRect(width()-fw, 0, fw, height()); //right
+ borderReg += QRect(0, 0, width(), fw); //top
+ borderReg += QRect(0, height()-fw, width(), fw); //bottom
+ p.setClipRegion(borderReg);
+ emptyArea -= borderReg;
+ QStyleOptionFrame frame;
+ frame.rect = rect();
+ frame.palette = palette();
+ frame.state = QStyle::State_None;
+ frame.lineWidth = style()->pixelMetric(QStyle::PM_MenuPanelWidth);
+ frame.midLineWidth = 0;
+ style()->drawPrimitive(QStyle::PE_FrameMenu, &frame, &p, this);
+ }
+
+ //finally the rest of the space
+ p.setClipRegion(emptyArea);
+ menuOpt.state = QStyle::State_None;
+ menuOpt.menuItemType = QStyleOptionMenuItem::EmptyArea;
+ menuOpt.checkType = QStyleOptionMenuItem::NotCheckable;
+ menuOpt.rect = rect();
+ menuOpt.menuRect = rect();
+ style()->drawControl(QStyle::CE_MenuEmptyArea, &menuOpt, &p, this);
+}
+
+#ifndef QT_NO_WHEELEVENT
+/*!
+ \reimp
+*/
+void QMenu::wheelEvent(QWheelEvent *e)
+{
+ Q_D(QMenu);
+ if (d->scroll && rect().contains(e->pos()))
+ d->scrollMenu(e->delta() > 0 ?
+ QMenuPrivate::QMenuScroller::ScrollUp : QMenuPrivate::QMenuScroller::ScrollDown);
+}
+#endif
+
+/*!
+ \reimp
+*/
+void QMenu::mousePressEvent(QMouseEvent *e)
+{
+ Q_D(QMenu);
+ if (d->aboutToHide || d->mouseEventTaken(e))
+ return;
+ if (!rect().contains(e->pos())) {
+ if (d->noReplayFor
+ && QRect(d->noReplayFor->mapToGlobal(QPoint()), d->noReplayFor->size()).contains(e->globalPos()))
+ setAttribute(Qt::WA_NoMouseReplay);
+ if (d->eventLoop) // synchronous operation
+ d->syncAction = 0;
+ d->hideUpToMenuBar();
+ return;
+ }
+ d->mouseDown = this;
+
+ QAction *action = d->actionAt(e->pos());
+ d->setCurrentAction(action, 20);
+ update();
+}
+
+/*!
+ \reimp
+*/
+void QMenu::mouseReleaseEvent(QMouseEvent *e)
+{
+ Q_D(QMenu);
+ if (d->aboutToHide || d->mouseEventTaken(e))
+ return;
+ if(d->mouseDown != this) {
+ d->mouseDown = 0;
+ return;
+ }
+
+ d->mouseDown = 0;
+ d->setSyncAction();
+ QAction *action = d->actionAt(e->pos());
+
+ if (action && action == d->currentAction) {
+ if (action->menu())
+ action->menu()->d_func()->setFirstActionActive();
+ else {
+#if defined(Q_WS_WIN) && !defined(QT_NO_MENUBAR)
+ //On Windows only context menus can be activated with the right button
+ bool isContextMenu = true;
+ const QWidget *cause = d->causedPopup.widget;
+ while (cause) {
+ //if the popup was caused by either QMenuBar or a QToolButton, it is not a context menu
+ if (qobject_cast<const QMenuBar *>(cause) || qobject_cast<const QToolButton *>(cause)) {
+ isContextMenu = false;
+ break;
+ } else if (const QMenu *menu = qobject_cast<const QMenu *>(cause)) {
+ cause = menu->d_func()->causedPopup.widget;
+ } else {
+ break;
+ }
+ }
+ if (e->button() == Qt::LeftButton || (e->button() == Qt::RightButton && isContextMenu))
+#endif
+ d->activateAction(action, QAction::Trigger);
+ }
+ } else if (d->hasMouseMoved(e->globalPos())) {
+ d->hideUpToMenuBar();
+ }
+}
+
+/*!
+ \reimp
+*/
+void QMenu::changeEvent(QEvent *e)
+{
+ Q_D(QMenu);
+ if (e->type() == QEvent::StyleChange || e->type() == QEvent::FontChange ||
+ e->type() == QEvent::LayoutDirectionChange) {
+ d->itemsDirty = 1;
+ setMouseTracking(style()->styleHint(QStyle::SH_Menu_MouseTracking, 0, this));
+ if (isVisible())
+ resize(sizeHint());
+ if (!style()->styleHint(QStyle::SH_Menu_Scrollable, 0, this)) {
+ delete d->scroll;
+ d->scroll = 0;
+ } else if (!d->scroll) {
+ d->scroll = new QMenuPrivate::QMenuScroller;
+ d->scroll->scrollFlags = QMenuPrivate::QMenuScroller::ScrollNone;
+ }
+ } else if (e->type() == QEvent::EnabledChange) {
+ if (d->tornPopup) // torn-off menu
+ d->tornPopup->setEnabled(isEnabled());
+ d->menuAction->setEnabled(isEnabled());
+#ifdef Q_WS_MAC
+ if (d->mac_menu)
+ d->setMacMenuEnabled(isEnabled());
+#endif
+ }
+ QWidget::changeEvent(e);
+}
+
+
+/*!
+ \reimp
+*/
+bool
+QMenu::event(QEvent *e)
+{
+ Q_D(QMenu);
+ switch (e->type()) {
+ case QEvent::ShortcutOverride: {
+ QKeyEvent *kev = static_cast<QKeyEvent*>(e);
+ if (kev->key() == Qt::Key_Up || kev->key() == Qt::Key_Down
+ || kev->key() == Qt::Key_Left || kev->key() == Qt::Key_Right
+ || kev->key() == Qt::Key_Enter || kev->key() == Qt::Key_Return
+ || kev->key() == Qt::Key_Escape) {
+ e->accept();
+ return true;
+ }
+ }
+ break;
+ case QEvent::KeyPress: {
+ QKeyEvent *ke = (QKeyEvent*)e;
+ if (ke->key() == Qt::Key_Tab || ke->key() == Qt::Key_Backtab) {
+ keyPressEvent(ke);
+ return true;
+ }
+ } break;
+ case QEvent::ContextMenu:
+ if(QMenuPrivate::menuDelayTimer.isActive()) {
+ QMenuPrivate::menuDelayTimer.stop();
+ internalDelayedPopup();
+ }
+ break;
+ case QEvent::Resize: {
+ QStyleHintReturnMask menuMask;
+ QStyleOption option;
+ option.initFrom(this);
+ if (style()->styleHint(QStyle::SH_Menu_Mask, &option, this, &menuMask)) {
+ setMask(menuMask.region);
+ }
+ d->itemsDirty = 1;
+ d->updateActions();
+ break; }
+ case QEvent::Show:
+ d->mouseDown = 0;
+ d->updateActions();
+ if (d->currentAction)
+ d->popupAction(d->currentAction, 0, false);
+ break;
+#ifndef QT_NO_WHATSTHIS
+ case QEvent::QueryWhatsThis:
+ e->setAccepted(d->whatsThis.size());
+ if (QAction *action = d->actionAt(static_cast<QHelpEvent*>(e)->pos())) {
+ if (action->whatsThis().size() || action->menu())
+ e->accept();
+ }
+ return true;
+#endif
+ default:
+ break;
+ }
+ return QWidget::event(e);
+}
+
+/*!
+ \reimp
+*/
+bool QMenu::focusNextPrevChild(bool next)
+{
+ setFocus();
+ QKeyEvent ev(QEvent::KeyPress, next ? Qt::Key_Tab : Qt::Key_Backtab, Qt::NoModifier);
+ keyPressEvent(&ev);
+ return true;
+}
+
+/*!
+ \reimp
+*/
+void QMenu::keyPressEvent(QKeyEvent *e)
+{
+ Q_D(QMenu);
+ int key = e->key();
+ if (isRightToLeft()) { // in reverse mode open/close key for submenues are reversed
+ if (key == Qt::Key_Left)
+ key = Qt::Key_Right;
+ else if (key == Qt::Key_Right)
+ key = Qt::Key_Left;
+ }
+#ifndef Q_WS_MAC
+ if (key == Qt::Key_Tab) //means down
+ key = Qt::Key_Down;
+ if (key == Qt::Key_Backtab) //means up
+ key = Qt::Key_Up;
+#endif
+
+ bool key_consumed = false;
+ switch(key) {
+ case Qt::Key_Home:
+ key_consumed = true;
+ if (d->scroll)
+ d->scrollMenu(QMenuPrivate::QMenuScroller::ScrollTop, true);
+ break;
+ case Qt::Key_End:
+ key_consumed = true;
+ if (d->scroll)
+ d->scrollMenu(QMenuPrivate::QMenuScroller::ScrollBottom, true);
+ break;
+ case Qt::Key_PageUp:
+ key_consumed = true;
+ if (d->currentAction && d->scroll) {
+ if(d->scroll->scrollFlags & QMenuPrivate::QMenuScroller::ScrollUp)
+ d->scrollMenu(QMenuPrivate::QMenuScroller::ScrollUp, true, true);
+ else
+ d->scrollMenu(QMenuPrivate::QMenuScroller::ScrollTop, true);
+ }
+ break;
+ case Qt::Key_PageDown:
+ key_consumed = true;
+ if (d->currentAction && d->scroll) {
+ if(d->scroll->scrollFlags & QMenuPrivate::QMenuScroller::ScrollDown)
+ d->scrollMenu(QMenuPrivate::QMenuScroller::ScrollDown, true, true);
+ else
+ d->scrollMenu(QMenuPrivate::QMenuScroller::ScrollBottom, true);
+ }
+ break;
+ case Qt::Key_Up:
+ case Qt::Key_Down: {
+ key_consumed = true;
+ QAction *nextAction = 0;
+ QMenuPrivate::QMenuScroller::ScrollLocation scroll_loc = QMenuPrivate::QMenuScroller::ScrollStay;
+ if (!d->currentAction) {
+ if(key == Qt::Key_Down) {
+ for(int i = 0; i < d->actionList.size(); ++i) {
+ QAction *act = d->actionList.at(i);
+ if (!act->isSeparator() &&
+ (style()->styleHint(QStyle::SH_Menu_AllowActiveAndDisabled, 0, this)
+ || act->isEnabled())) {
+ nextAction = act;
+ break;
+ }
+ }
+ } else {
+ for(int i = d->actionList.size()-1; i >= 0; --i) {
+ QAction *act = d->actionList.at(i);
+ if (!act->isSeparator() &&
+ (style()->styleHint(QStyle::SH_Menu_AllowActiveAndDisabled, 0, this)
+ || act->isEnabled())) {
+ nextAction = act;
+ break;
+ }
+ }
+ }
+ } else {
+ for(int i=0, y=0; !nextAction && i < (int)d->actionList.count(); i++) {
+ QAction *act = d->actionList.at(i);
+ if (act == d->currentAction) {
+ if (key == Qt::Key_Up) {
+ for(int next_i = i-1; true; next_i--) {
+ if (next_i == -1) {
+ if(!style()->styleHint(QStyle::SH_Menu_SelectionWrap, 0, this))
+ break;
+ if (d->scroll)
+ scroll_loc = QMenuPrivate::QMenuScroller::ScrollBottom;
+ next_i = d->actionList.count()-1;
+ }
+ QAction *next = d->actionList.at(next_i);
+ if (next == d->currentAction)
+ break;
+ if (next->isSeparator() ||
+ (!next->isEnabled() &&
+ !style()->styleHint(QStyle::SH_Menu_AllowActiveAndDisabled, 0, this)))
+ continue;
+ nextAction = next;
+ if (d->scroll && (d->scroll->scrollFlags & QMenuPrivate::QMenuScroller::ScrollUp)) {
+ int topVisible = style()->pixelMetric(QStyle::PM_MenuScrollerHeight, 0, this);
+ if (d->tearoff)
+ topVisible += style()->pixelMetric(QStyle::PM_MenuTearoffHeight, 0, this);
+ if (((y + d->scroll->scrollOffset) - topVisible) <= d->actionRects.value(nextAction).height())
+ scroll_loc = QMenuPrivate::QMenuScroller::ScrollTop;
+ }
+ break;
+ }
+ if (!nextAction && d->tearoff)
+ d->tearoffHighlighted = 1;
+ } else {
+ y += d->actionRects.value(act).height();
+ for(int next_i = i+1; true; next_i++) {
+ if (next_i == d->actionList.count()) {
+ if(!style()->styleHint(QStyle::SH_Menu_SelectionWrap, 0, this))
+ break;
+ if (d->scroll)
+ scroll_loc = QMenuPrivate::QMenuScroller::ScrollTop;
+ next_i = 0;
+ }
+ QAction *next = d->actionList.at(next_i);
+ if (next == d->currentAction)
+ break;
+ if (next->isSeparator() ||
+ (!next->isEnabled() &&
+ !style()->styleHint(QStyle::SH_Menu_AllowActiveAndDisabled, 0, this)))
+ continue;
+ nextAction = next;
+ if (d->scroll && (d->scroll->scrollFlags & QMenuPrivate::QMenuScroller::ScrollDown)) {
+ const int scrollerHeight = style()->pixelMetric(QStyle::PM_MenuScrollerHeight, 0, this);
+ int bottomVisible = height()-scrollerHeight;
+ if (d->scroll->scrollFlags & QMenuPrivate::QMenuScroller::ScrollUp)
+ bottomVisible -= scrollerHeight;
+ if (d->tearoff)
+ bottomVisible -= style()->pixelMetric(QStyle::PM_MenuTearoffHeight, 0, this);
+ if ((y + d->scroll->scrollOffset + d->actionRects.value(nextAction).height()) > bottomVisible)
+ scroll_loc = QMenuPrivate::QMenuScroller::ScrollBottom;
+ }
+ break;
+ }
+ }
+ break;
+ }
+ y += d->actionRects.value(act).height();
+ }
+ }
+ if (nextAction) {
+ if (d->scroll && scroll_loc != QMenuPrivate::QMenuScroller::ScrollStay) {
+ if (d->scroll->scrollTimer)
+ d->scroll->scrollTimer->stop();
+ d->scrollMenu(nextAction, scroll_loc);
+ }
+ d->setCurrentAction(nextAction, /*popup*/-1, QMenuPrivate::SelectedFromKeyboard);
+ }
+ break; }
+
+ case Qt::Key_Right:
+ if (d->currentAction && d->currentAction->isEnabled() && d->currentAction->menu()) {
+ d->popupAction(d->currentAction, 0, true);
+ key_consumed = true;
+ break;
+ }
+ //FALL THROUGH
+ case Qt::Key_Left: {
+ if (d->currentAction && !d->scroll) {
+ QAction *nextAction = 0;
+ if (key == Qt::Key_Left) {
+ QRect actionR = d->actionRect(d->currentAction);
+ for(int x = actionR.left()-1; !nextAction && x >= 0; x--)
+ nextAction = d->actionAt(QPoint(x, actionR.center().y()));
+ } else {
+ QRect actionR = d->actionRect(d->currentAction);
+ for(int x = actionR.right()+1; !nextAction && x < width(); x++)
+ nextAction = d->actionAt(QPoint(x, actionR.center().y()));
+ }
+ if (nextAction) {
+ d->setCurrentAction(nextAction, /*popup*/-1, QMenuPrivate::SelectedFromKeyboard);
+ key_consumed = true;
+ }
+ }
+ if (!key_consumed && key == Qt::Key_Left && qobject_cast<QMenu*>(d->causedPopup.widget)) {
+ QPointer<QWidget> caused = d->causedPopup.widget;
+ d->hideMenu(this);
+ if (caused)
+ caused->setFocus();
+ key_consumed = true;
+ }
+ break; }
+
+ case Qt::Key_Alt:
+ if (d->tornoff)
+ break;
+
+ key_consumed = true;
+ if (style()->styleHint(QStyle::SH_MenuBar_AltKeyNavigation, 0, this))
+ {
+ d->hideMenu(this);
+#ifndef QT_NO_MENUBAR
+ if (QMenuBar *mb = qobject_cast<QMenuBar*>(qApp->focusWidget())) {
+ mb->d_func()->setKeyboardMode(false);
+ }
+#endif
+ }
+ break;
+
+ case Qt::Key_Escape:
+#ifdef QT_KEYPAD_NAVIGATION
+ case Qt::Key_Back:
+#endif
+ key_consumed = true;
+ if (d->tornoff) {
+ close();
+ return;
+ }
+ {
+ QPointer<QWidget> caused = d->causedPopup.widget;
+ d->hideMenu(this); // hide after getting causedPopup
+#ifndef QT_NO_MENUBAR
+ if (QMenuBar *mb = qobject_cast<QMenuBar*>(caused)) {
+ mb->d_func()->setCurrentAction(d->menuAction);
+ mb->d_func()->setKeyboardMode(true);
+ }
+#endif
+ }
+ break;
+
+ case Qt::Key_Space:
+ if (!style()->styleHint(QStyle::SH_Menu_SpaceActivatesItem, 0, this))
+ break;
+ // for motif, fall through
+#ifdef QT_KEYPAD_NAVIGATION
+ case Qt::Key_Select:
+#endif
+ case Qt::Key_Return:
+ case Qt::Key_Enter: {
+ if (!d->currentAction) {
+ d->setFirstActionActive();
+ key_consumed = true;
+ break;
+ }
+
+ d->setSyncAction();
+
+ if (d->currentAction->menu())
+ d->popupAction(d->currentAction, 0, true);
+ else
+ d->activateAction(d->currentAction, QAction::Trigger);
+ key_consumed = true;
+ break; }
+
+#ifndef QT_NO_WHATSTHIS
+ case Qt::Key_F1:
+ if (!d->currentAction || d->currentAction->whatsThis().isNull())
+ break;
+ QWhatsThis::enterWhatsThisMode();
+ d->activateAction(d->currentAction, QAction::Trigger);
+ return;
+#endif
+ default:
+ key_consumed = false;
+ }
+
+ if (!key_consumed) { // send to menu bar
+ if ((!e->modifiers() || e->modifiers() == Qt::AltModifier || e->modifiers() == Qt::ShiftModifier) &&
+ e->text().length()==1) {
+ bool activateAction = false;
+ QAction *nextAction = 0;
+ if (style()->styleHint(QStyle::SH_Menu_KeyboardSearch, 0, this) && !e->modifiers()) {
+ int best_match_count = 0;
+ d->searchBufferTimer.start(2000, this);
+ d->searchBuffer += e->text();
+ for(int i = 0; i < d->actionList.size(); ++i) {
+ int match_count = 0;
+ register QAction *act = d->actionList.at(i);
+ const QString act_text = act->text();
+ for(int c = 0; c < d->searchBuffer.size(); ++c) {
+ if(act_text.indexOf(d->searchBuffer.at(c), 0, Qt::CaseInsensitive) != -1)
+ ++match_count;
+ }
+ if(match_count > best_match_count) {
+ best_match_count = match_count;
+ nextAction = act;
+ }
+ }
+ }
+#ifndef QT_NO_SHORTCUT
+ else {
+ int clashCount = 0;
+ QAction *first = 0, *currentSelected = 0, *firstAfterCurrent = 0;
+ QChar c = e->text().at(0).toUpper();
+ for(int i = 0; i < d->actionList.size(); ++i) {
+ register QAction *act = d->actionList.at(i);
+ QKeySequence sequence = QKeySequence::mnemonic(act->text());
+ int key = sequence[0] & 0xffff;
+ if (key == c.unicode()) {
+ clashCount++;
+ if (!first)
+ first = act;
+ if (act == d->currentAction)
+ currentSelected = act;
+ else if (!firstAfterCurrent && currentSelected)
+ firstAfterCurrent = act;
+ }
+ }
+ if (clashCount == 1)
+ activateAction = true;
+ if (clashCount >= 1) {
+ if (clashCount == 1 || !currentSelected || !firstAfterCurrent)
+ nextAction = first;
+ else
+ nextAction = firstAfterCurrent;
+ }
+ }
+#endif
+ if (nextAction) {
+ key_consumed = true;
+ if(d->scroll)
+ d->scrollMenu(nextAction, QMenuPrivate::QMenuScroller::ScrollCenter, false);
+ d->setCurrentAction(nextAction, 20, QMenuPrivate::SelectedFromElsewhere, true);
+ if (!nextAction->menu() && activateAction) {
+ d->setSyncAction();
+ d->activateAction(nextAction, QAction::Trigger);
+ }
+ }
+ }
+ if (!key_consumed) {
+ if (QWidget *caused = d->causedPopup.widget) {
+ while(QMenu *m = qobject_cast<QMenu*>(caused))
+ caused = m->d_func()->causedPopup.widget;
+#ifndef QT_NO_MENUBAR
+ if (QMenuBar *mb = qobject_cast<QMenuBar*>(caused)) {
+ QAction *oldAct = mb->d_func()->currentAction;
+ QApplication::sendEvent(mb, e);
+ if (mb->d_func()->currentAction != oldAct)
+ key_consumed = true;
+ }
+#endif
+ }
+ }
+
+#ifdef Q_OS_WIN32
+ if (key_consumed && (e->key() == Qt::Key_Control || e->key() == Qt::Key_Shift || e->key() == Qt::Key_Meta))
+ qApp->beep();
+#endif // Q_OS_WIN32
+ }
+ if (key_consumed)
+ e->accept();
+ else
+ e->ignore();
+}
+
+/*!
+ \reimp
+*/
+void QMenu::mouseMoveEvent(QMouseEvent *e)
+{
+ Q_D(QMenu);
+ if (!isVisible() || d->aboutToHide || d->mouseEventTaken(e))
+ return;
+ d->motions++;
+ if (d->motions == 0) // ignore first mouse move event (see enterEvent())
+ return;
+ d->hasHadMouse |= rect().contains(e->pos());
+
+ QAction *action = d->actionAt(e->pos());
+ if (!action) {
+ if (d->hasHadMouse && !rect().contains(e->pos()))
+ d->setCurrentAction(0);
+ return;
+ } else if(e->buttons() & (Qt::LeftButton | Qt::RightButton)) {
+ d->mouseDown = this;
+ }
+ if (d->sloppyRegion.contains(e->pos())) {
+ d->sloppyAction = action;
+ QMenuPrivate::sloppyDelayTimer.start(style()->styleHint(QStyle::SH_Menu_SubMenuPopupDelay, 0, this)*6, this);
+ } else {
+ d->setCurrentAction(action, style()->styleHint(QStyle::SH_Menu_SubMenuPopupDelay, 0, this));
+ }
+}
+
+/*!
+ \reimp
+*/
+void QMenu::enterEvent(QEvent *)
+{
+ d_func()->motions = -1; // force us to ignore the generate mouse move in mouseMoveEvent()
+}
+
+/*!
+ \reimp
+*/
+void QMenu::leaveEvent(QEvent *)
+{
+ Q_D(QMenu);
+ d->sloppyAction = 0;
+ if (!d->sloppyRegion.isEmpty())
+ d->sloppyRegion = QRegion();
+}
+
+/*!
+ \reimp
+*/
+void
+QMenu::timerEvent(QTimerEvent *e)
+{
+ Q_D(QMenu);
+ if (d->scroll && d->scroll->scrollTimer && d->scroll->scrollTimer->timerId() == e->timerId()) {
+ d->scrollMenu((QMenuPrivate::QMenuScroller::ScrollDirection)d->scroll->scrollDirection);
+ if (d->scroll->scrollFlags == QMenuPrivate::QMenuScroller::ScrollNone)
+ d->scroll->scrollTimer->stop();
+ } else if(QMenuPrivate::menuDelayTimer.timerId() == e->timerId()) {
+ QMenuPrivate::menuDelayTimer.stop();
+ internalDelayedPopup();
+ } else if(QMenuPrivate::sloppyDelayTimer.timerId() == e->timerId()) {
+ QMenuPrivate::sloppyDelayTimer.stop();
+ internalSetSloppyAction();
+ } else if(d->searchBufferTimer.timerId() == e->timerId()) {
+ d->searchBuffer.clear();
+ }
+}
+
+/*!
+ \reimp
+*/
+void QMenu::actionEvent(QActionEvent *e)
+{
+ Q_D(QMenu);
+ d->itemsDirty = 1;
+ setAttribute(Qt::WA_Resized, false);
+ if (d->tornPopup)
+ d->tornPopup->syncWithMenu(this, e);
+ if (e->type() == QEvent::ActionAdded) {
+ if(!d->tornoff) {
+ connect(e->action(), SIGNAL(triggered()), this, SLOT(_q_actionTriggered()));
+ connect(e->action(), SIGNAL(hovered()), this, SLOT(_q_actionHovered()));
+ }
+
+ if (QWidgetAction *wa = qobject_cast<QWidgetAction *>(e->action())) {
+ QWidget *widget = wa->requestWidget(this);
+ if (widget)
+ d->widgetItems.insert(wa, widget);
+ }
+ } else if (e->type() == QEvent::ActionRemoved) {
+ d->actionRects.clear();
+ d->actionList.clear();
+ e->action()->disconnect(this);
+ if (e->action() == d->currentAction)
+ d->currentAction = 0;
+ if (QWidgetAction *wa = qobject_cast<QWidgetAction *>(e->action())) {
+ QWidget *widget = d->widgetItems.take(wa);
+ if (widget)
+ wa->releaseWidget(widget);
+ } else {
+ // If this is called from the QAction destructor, the
+ // previous call to qobject_cast will fail because the
+ // QWidgetAction has been destroyed already. We need to
+ // remove it from the hash anyway or it might crash later
+ // the widget itself has been already destroyed in
+ // ~QWidgetAction
+ d->widgetItems.remove(e->action());
+ }
+ }
+
+#ifdef Q_WS_MAC
+ if (d->mac_menu) {
+ if (e->type() == QEvent::ActionAdded)
+ d->mac_menu->addAction(e->action(), d->mac_menu->findAction(e->before()), d);
+ else if (e->type() == QEvent::ActionRemoved)
+ d->mac_menu->removeAction(e->action());
+ else if (e->type() == QEvent::ActionChanged)
+ d->mac_menu->syncAction(e->action());
+ }
+#endif
+
+#if defined(Q_OS_WINCE) && !defined(QT_NO_MENUBAR)
+ if (!d->wce_menu)
+ d->wce_menu = new QMenuPrivate::QWceMenuPrivate;
+ if (e->type() == QEvent::ActionAdded)
+ d->wce_menu->addAction(e->action(), d->wce_menu->findAction(e->before()));
+ else if (e->type() == QEvent::ActionRemoved)
+ d->wce_menu->removeAction(e->action());
+ else if (e->type() == QEvent::ActionChanged)
+ d->wce_menu->syncAction(e->action());
+#endif
+
+ if (isVisible()) {
+ d->updateActions();
+ resize(sizeHint());
+ update();
+ }
+}
+
+/*!
+ \internal
+*/
+void QMenu::internalSetSloppyAction()
+{
+ if (d_func()->sloppyAction)
+ d_func()->setCurrentAction(d_func()->sloppyAction, 0);
+}
+
+/*!
+ \internal
+*/
+void QMenu::internalDelayedPopup()
+{
+ Q_D(QMenu);
+
+ //hide the current item
+ if (QMenu *menu = d->activeMenu) {
+ d->activeMenu = 0;
+ d->hideMenu(menu);
+ }
+
+ if (!d->currentAction || !d->currentAction->isEnabled() || !d->currentAction->menu() ||
+ !d->currentAction->menu()->isEnabled() || d->currentAction->menu()->isVisible())
+ return;
+
+ //setup
+ d->activeMenu = d->currentAction->menu();
+ d->activeMenu->d_func()->causedPopup.widget = this;
+ d->activeMenu->d_func()->causedPopup.action = d->currentAction;
+
+ int subMenuOffset = style()->pixelMetric(QStyle::PM_SubMenuOverlap, 0, this);
+ const QRect actionRect(d->actionRect(d->currentAction));
+ const QSize menuSize(d->activeMenu->sizeHint());
+ const QPoint rightPos(mapToGlobal(QPoint(rect().right() + subMenuOffset + 1, actionRect.top())));
+ const QPoint leftPos(mapToGlobal(QPoint(rect().left() - subMenuOffset - menuSize.width(), actionRect.top())));
+
+ QPoint pos(rightPos);
+ QMenu *caused = qobject_cast<QMenu*>(d->activeMenu->d_func()->causedPopup.widget);
+
+ const QRect availGeometry(d->popupGeometry(QApplication::desktop()->screenNumber(caused)));
+ if (isRightToLeft()) {
+ pos = leftPos;
+ if ((caused && caused->x() < x()) || pos.x() < availGeometry.left()) {
+ if(rightPos.x() + menuSize.width() < availGeometry.right())
+ pos = rightPos;
+ else
+ pos.rx() = availGeometry.left();
+ }
+ } else {
+ if ((caused && caused->x() > x()) || pos.x() + menuSize.width() > availGeometry.right()) {
+ if(leftPos.x() < availGeometry.left())
+ pos.rx() = availGeometry.right() - menuSize.width();
+ else
+ pos = leftPos;
+ }
+ }
+
+ //calc sloppy focus buffer
+ if (style()->styleHint(QStyle::SH_Menu_SloppySubMenus, 0, this)) {
+ QPoint cur = QCursor::pos();
+ if (actionRect.contains(mapFromGlobal(cur))) {
+ QPoint pts[4];
+ pts[0] = QPoint(cur.x(), cur.y() - 2);
+ pts[3] = QPoint(cur.x(), cur.y() + 2);
+ if (pos.x() >= cur.x()) {
+ pts[1] = QPoint(geometry().right(), pos.y());
+ pts[2] = QPoint(geometry().right(), pos.y() + menuSize.height());
+ } else {
+ pts[1] = QPoint(pos.x() + menuSize.width(), pos.y());
+ pts[2] = QPoint(pos.x() + menuSize.width(), pos.y() + menuSize.height());
+ }
+ QPolygon points(4);
+ for(int i = 0; i < 4; i++)
+ points.setPoint(i, mapFromGlobal(pts[i]));
+ d->sloppyRegion = QRegion(points);
+ }
+ }
+
+ //do the popup
+ d->activeMenu->popup(pos);
+}
+
+/*!
+ \fn void QMenu::addAction(QAction *action)
+ \overload
+
+ Appends the action \a action to the menu's list of actions.
+
+ \sa QMenuBar::addAction(), QWidget::addAction()
+*/
+
+/*!
+ \fn void QMenu::aboutToHide()
+ \since 4.2
+
+ This signal is emitted just before the menu is hidden from the user.
+
+ \sa aboutToShow(), hide()
+*/
+
+/*!
+ \fn void QMenu::aboutToShow()
+
+ This signal is emitted just before the menu is shown to the user.
+
+ \sa aboutToHide(), show()
+*/
+
+/*!
+ \fn void QMenu::triggered(QAction *action)
+
+ This signal is emitted when an action in this menu is triggered.
+
+ \a action is the action that caused the signal to be emitted.
+
+ Normally, you connect each menu action's \l{QAction::}{triggered()} signal
+ to its own custom slot, but sometimes you will want to connect several
+ actions to a single slot, for example, when you have a group of closely
+ related actions, such as "left justify", "center", "right justify".
+
+ \note This signal is emitted for the main parent menu in a hierarchy.
+ Hence, only the parent menu needs to be connected to a slot; sub-menus need
+ not be connected.
+
+ \sa hovered(), QAction::triggered()
+*/
+
+/*!
+ \fn void QMenu::hovered(QAction *action)
+
+ This signal is emitted when a menu action is highlighted; \a action
+ is the action that caused the signal to be emitted.
+
+ Often this is used to update status information.
+
+ \sa triggered(), QAction::hovered()
+*/
+
+
+/*!\internal
+*/
+void QMenu::setNoReplayFor(QWidget *noReplayFor)
+{
+#ifdef Q_WS_WIN
+ d_func()->noReplayFor = noReplayFor;
+#else
+ Q_UNUSED(noReplayFor);
+#endif
+}
+
+/*!
+ \property QMenu::separatorsCollapsible
+ \since 4.2
+
+ \brief whether consecutive separators should be collapsed
+
+ This property specifies whether consecutive separators in the menu
+ should be visually collapsed to a single one. Separators at the
+ beginning or the end of the menu are also hidden.
+
+ By default, this property is true.
+*/
+bool QMenu::separatorsCollapsible() const
+{
+ Q_D(const QMenu);
+ return d->collapsibleSeparators;
+}
+
+void QMenu::setSeparatorsCollapsible(bool collapse)
+{
+ Q_D(QMenu);
+ d->collapsibleSeparators = collapse;
+ d->itemsDirty = 1;
+ if (isVisible()) {
+ d->updateActions();
+ update();
+ }
+}
+
+#ifdef QT3_SUPPORT
+
+int QMenu::insertAny(const QIcon *icon, const QString *text, const QObject *receiver, const char *member,
+ const QKeySequence *shortcut, const QMenu *popup, int id, int index)
+{
+ QAction *act = popup ? popup->menuAction() : new QAction(this);
+ if (id != -1)
+ static_cast<QMenuItem*>(act)->setId(id);
+ if (icon)
+ act->setIcon(*icon);
+ if (text)
+ act->setText(*text);
+ if (shortcut)
+ act->setShortcut(*shortcut);
+ if (receiver && member)
+ QObject::connect(act, SIGNAL(activated(int)), receiver, member);
+ if (index == -1 || index >= actions().count())
+ addAction(act);
+ else
+ insertAction(actions().value(index), act);
+ return findIdForAction(act);
+}
+
+/*!
+ Use insertAction() or one of the addAction() overloads instead.
+*/
+int QMenu::insertItem(QMenuItem *item, int id, int index)
+{
+ if (index == -1 || index >= actions().count())
+ addAction(item);
+ else
+ insertAction(actions().value(index), item);
+ if (id > -1)
+ item->d_func()->id = id;
+ return findIdForAction(item);
+}
+
+/*!
+ Use the insertSeparator() overload that takes a QAction *
+ parameter instead.
+*/
+int QMenu::insertSeparator(int index)
+{
+ QAction *act = new QAction(this);
+ act->setSeparator(true);
+ if (index == -1 || index >= actions().count())
+ addAction(act);
+ else
+ insertAction(actions().value(index), act);
+ return findIdForAction(act);
+}
+
+QAction *QMenu::findActionForId(int id) const
+{
+ QList<QAction *> list = actions();
+ for (int i = 0; i < list.size(); ++i) {
+ QAction *act = list.at(i);
+ if (findIdForAction(act)== id)
+ return act;
+ }
+ return 0;
+}
+
+/*!
+ Use QAction and actions() instead.
+*/
+QMenuItem *QMenu::findPopup( QMenu *popup, int *index )
+{
+ QList<QAction *> list = actions();
+ for (int i = 0; i < list.size(); ++i) {
+ QAction *act = list.at(i);
+ if (act->menu() == popup) {
+ QMenuItem *item = static_cast<QMenuItem *>(act);
+ if (index)
+ *index = act->d_func()->id;
+ return item;
+ }
+ }
+ return 0;
+}
+
+
+/*!
+ Use QAction::setData() instead.
+*/
+bool QMenu::setItemParameter(int id, int param)
+{
+ if (QAction *act = findActionForId(id)) {
+ act->d_func()->param = param;
+ return true;
+ }
+ return false;
+}
+
+/*!
+ Use QAction::data() instead.
+*/
+int QMenu::itemParameter(int id) const
+{
+ if (QAction *act = findActionForId(id))
+ return act->d_func()->param;
+ return id;
+}
+
+/*!
+ Use actions instead.
+*/
+void QMenu::setId(int index, int id)
+{
+ if(QAction *act = actions().value(index))
+ act->d_func()->id = id;
+}
+
+/*!
+ Use style()->pixelMetric(QStyle::PM_MenuPanelWidth, this) instead.
+*/
+int QMenu::frameWidth() const
+{
+ return style()->pixelMetric(QStyle::PM_MenuPanelWidth, 0, this);
+}
+
+int QMenu::findIdForAction(QAction *act) const
+{
+ if (!act)
+ return -1;
+ return act->d_func()->id;
+}
+#endif // QT3_SUPPORT
+
+/*!
+ \fn uint QMenu::count() const
+
+ Use actions().count() instead.
+*/
+
+/*!
+ \fn int QMenu::insertItem(const QString &text, const QObject *receiver, const char* member, const QKeySequence& shortcut, int id, int index)
+
+ Use insertAction() or one of the addAction() overloads instead.
+*/
+
+/*!
+ \fn int QMenu::insertItem(const QIcon& icon, const QString &text, const QObject *receiver, const char* member, const QKeySequence& shortcut, int id, int index)
+
+ Use insertAction() or one of the addAction() overloads instead.
+*/
+
+/*!
+ \fn int QMenu::insertItem(const QPixmap &pixmap, const QObject *receiver, const char* member, const QKeySequence& shortcut, int id, int index)
+
+ Use insertAction() or one of the addAction() overloads instead.
+*/
+
+/*!
+ \fn int QMenu::insertItem(const QString &text, int id, int index)
+
+ Use insertAction() or one of the addAction() overloads instead.
+*/
+
+/*!
+ \fn int QMenu::insertItem(const QIcon& icon, const QString &text, int id, int index)
+
+ Use insertAction() or one of the addAction() overloads instead.
+*/
+
+/*!
+ \fn int QMenu::insertItem(const QString &text, QMenu *popup, int id, int index)
+
+ Use insertMenu() or one of the addMenu() overloads instead.
+*/
+
+/*!
+ \fn int QMenu::insertItem(const QIcon& icon, const QString &text, QMenu *popup, int id, int index)
+
+ Use insertMenu() or one of the addMenu() overloads instead.
+*/
+
+/*!
+ \fn int QMenu::insertItem(const QPixmap &pixmap, int id, int index)
+
+ Use insertAction() or one of the addAction() overloads instead.
+*/
+
+/*!
+ \fn int QMenu::insertItem(const QPixmap &pixmap, QMenu *popup, int id, int index)
+
+ Use insertMenu() or one of the addMenu() overloads instead.
+*/
+
+/*!
+ \fn void QMenu::removeItem(int id)
+
+ Use removeAction() instead.
+*/
+
+/*!
+ \fn void QMenu::removeItemAt(int index)
+
+ Use removeAction() instead.
+*/
+
+/*!
+ \fn QKeySequence QMenu::accel(int id) const
+
+ Use shortcut() on the relevant QAction instead.
+*/
+
+/*!
+ \fn void QMenu::setAccel(const QKeySequence& key, int id)
+
+ Use setShortcut() on the relevant QAction instead.
+*/
+
+/*!
+ \fn QIcon QMenu::iconSet(int id) const
+
+ Use icon() on the relevant QAction instead.
+*/
+
+/*!
+ \fn QString QMenu::text(int id) const
+
+ Use text() on the relevant QAction instead.
+*/
+
+/*!
+ \fn QPixmap QMenu::pixmap(int id) const
+
+ Use QPixmap(icon()) on the relevant QAction instead.
+*/
+
+/*!
+ \fn void QMenu::setWhatsThis(int id, const QString &w)
+
+ Use setWhatsThis() on the relevant QAction instead.
+*/
+
+/*!
+ \fn QString QMenu::whatsThis(int id) const
+
+ Use whatsThis() on the relevant QAction instead.
+*/
+
+/*!
+ \fn void QMenu::changeItem(int id, const QString &text)
+
+ Use setText() on the relevant QAction instead.
+*/
+
+/*!
+ \fn void QMenu::changeItem(int id, const QPixmap &pixmap)
+
+ Use setText() on the relevant QAction instead.
+*/
+
+/*!
+ \fn void QMenu::changeItem(int id, const QIcon &icon, const QString &text)
+
+ Use setIcon() and setText() on the relevant QAction instead.
+*/
+
+/*!
+ \fn bool QMenu::isItemActive(int id) const
+
+ Use activeAction() instead.
+*/
+
+/*!
+ \fn bool QMenu::isItemEnabled(int id) const
+
+ Use isEnabled() on the relevant QAction instead.
+*/
+
+/*!
+ \fn void QMenu::setItemEnabled(int id, bool enable)
+
+ Use setEnabled() on the relevant QAction instead.
+*/
+
+/*!
+ \fn bool QMenu::isItemChecked(int id) const
+
+ Use isChecked() on the relevant QAction instead.
+*/
+
+/*!
+ \fn void QMenu::setItemChecked(int id, bool check)
+
+ Use setChecked() on the relevant QAction instead.
+*/
+
+/*!
+ \fn bool QMenu::isItemVisible(int id) const
+
+ Use isVisible() on the relevant QAction instead.
+*/
+
+/*!
+ \fn void QMenu::setItemVisible(int id, bool visible)
+
+ Use setVisible() on the relevant QAction instead.
+*/
+
+/*!
+ \fn QRect QMenu::itemGeometry(int index)
+
+ Use actionGeometry() on the relevant QAction instead.
+*/
+
+/*!
+ \fn QFont QMenu::itemFont(int id) const
+
+ Use font() on the relevant QAction instead.
+*/
+
+/*!
+ \fn void QMenu::setItemFont(int id, const QFont &font)
+
+ Use setFont() on the relevant QAction instead.
+*/
+
+/*!
+ \fn int QMenu::indexOf(int id) const
+
+ Use actions().indexOf(action) on the relevant QAction instead.
+*/
+
+/*!
+ \fn int QMenu::idAt(int index) const
+
+ Use actions instead.
+*/
+
+/*!
+ \fn void QMenu::activateItemAt(int index)
+
+ Use activate() on the relevant QAction instead.
+*/
+
+/*!
+ \fn bool QMenu::connectItem(int id, const QObject *receiver, const char* member)
+
+ Use connect() on the relevant QAction instead.
+*/
+
+/*!
+ \fn bool QMenu::disconnectItem(int id,const QObject *receiver, const char* member)
+ Use disconnect() on the relevant QAction instead.
+
+*/
+
+/*!
+ \fn QMenuItem *QMenu::findItem(int id) const
+
+ Use actions instead.
+*/
+
+/*!
+ \fn void QMenu::popup(const QPoint & pos, int indexAtPoint)
+
+ Use popup() on the relevant QAction instead.
+*/
+
+/*!
+ \fn int QMenu::insertTearOffHandle(int a, int b)
+
+ Use setTearOffEnabled() instead.
+*/
+
+/*!
+ \fn int QMenu::itemAtPos(const QPoint &p, bool ignoreSeparator)
+
+ Use actions instead.
+*/
+
+/*!
+ \fn int QMenu::columns() const
+
+ Use columnCount() instead.
+*/
+
+/*!
+ \fn int QMenu::itemHeight(int index)
+
+ Use actionGeometry(actions().value(index)).height() instead.
+*/
+
+/*!
+ \fn int QMenu::itemHeight(QMenuItem *mi)
+
+ Use actionGeometry() instead.
+*/
+
+/*!
+ \fn void QMenu::activated(int itemId);
+
+ Use triggered() instead.
+*/
+
+/*!
+ \fn void QMenu::highlighted(int itemId);
+
+ Use hovered() instead.
+*/
+
+/*!
+ \fn void QMenu::setCheckable(bool checkable)
+
+ Not necessary anymore. The \a checkable parameter is ignored.
+*/
+
+/*!
+ \fn bool QMenu::isCheckable() const
+
+ Not necessary anymore. Always returns true.
+*/
+
+/*!
+ \fn void QMenu::setActiveItem(int id)
+
+ Use setActiveAction() instead.
+*/
+
+QT_END_NAMESPACE
+
+// for private slots
+#include "moc_qmenu.cpp"
+#include "qmenu.moc"
+
+#endif // QT_NO_MENU
diff --git a/src/gui/widgets/qmenu.h b/src/gui/widgets/qmenu.h
new file mode 100644
index 0000000000..867baee1c8
--- /dev/null
+++ b/src/gui/widgets/qmenu.h
@@ -0,0 +1,428 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QMENU_H
+#define QMENU_H
+
+#include <QtGui/qwidget.h>
+#include <QtCore/qstring.h>
+#include <QtGui/qicon.h>
+#include <QtGui/qaction.h>
+
+#ifdef QT3_SUPPORT
+#include <QtGui/qpixmap.h>
+#endif
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+#ifndef QT_NO_MENU
+
+class QMenuPrivate;
+class QStyleOptionMenuItem;
+#ifdef QT3_SUPPORT
+class QMenuItem;
+#endif
+
+class Q_GUI_EXPORT QMenu : public QWidget
+{
+private:
+ Q_OBJECT
+ Q_DECLARE_PRIVATE(QMenu)
+
+ Q_PROPERTY(bool tearOffEnabled READ isTearOffEnabled WRITE setTearOffEnabled)
+ Q_PROPERTY(QString title READ title WRITE setTitle)
+ Q_PROPERTY(QIcon icon READ icon WRITE setIcon)
+ Q_PROPERTY(bool separatorsCollapsible READ separatorsCollapsible WRITE setSeparatorsCollapsible)
+
+public:
+ explicit QMenu(QWidget *parent = 0);
+ explicit QMenu(const QString &title, QWidget *parent = 0);
+ ~QMenu();
+
+#ifdef Q_NO_USING_KEYWORD
+ inline void addAction(QAction *action) { QWidget::addAction(action); }
+#else
+ using QWidget::addAction;
+#endif
+ QAction *addAction(const QString &text);
+ QAction *addAction(const QIcon &icon, const QString &text);
+ QAction *addAction(const QString &text, const QObject *receiver, const char* member, const QKeySequence &shortcut = 0);
+ QAction *addAction(const QIcon &icon, const QString &text, const QObject *receiver, const char* member, const QKeySequence &shortcut = 0);
+
+ QAction *addMenu(QMenu *menu);
+ QMenu *addMenu(const QString &title);
+ QMenu *addMenu(const QIcon &icon, const QString &title);
+
+ QAction *addSeparator();
+
+ QAction *insertMenu(QAction *before, QMenu *menu);
+ QAction *insertSeparator(QAction *before);
+
+ bool isEmpty() const;
+ void clear();
+
+ void setTearOffEnabled(bool);
+ bool isTearOffEnabled() const;
+
+ bool isTearOffMenuVisible() const;
+ void hideTearOffMenu();
+
+ void setDefaultAction(QAction *);
+ QAction *defaultAction() const;
+
+ void setActiveAction(QAction *act);
+ QAction *activeAction() const;
+
+ void popup(const QPoint &pos, QAction *at=0);
+ QAction *exec();
+ QAction *exec(const QPoint &pos, QAction *at=0);
+
+ // ### Qt 5: merge
+ static QAction *exec(QList<QAction*> actions, const QPoint &pos, QAction *at=0);
+ static QAction *exec(QList<QAction*> actions, const QPoint &pos, QAction *at, QWidget *parent);
+
+ QSize sizeHint() const;
+
+ QRect actionGeometry(QAction *) const;
+ QAction *actionAt(const QPoint &) const;
+
+ QAction *menuAction() const;
+
+ QString title() const;
+ void setTitle(const QString &title);
+
+ QIcon icon() const;
+ void setIcon(const QIcon &icon);
+
+ void setNoReplayFor(QWidget *widget);
+#ifdef Q_WS_MAC
+ OSMenuRef macMenu(OSMenuRef merge=0);
+#endif
+
+#ifdef Q_OS_WINCE
+ HMENU wceMenu(bool create = false);
+#endif
+
+
+ bool separatorsCollapsible() const;
+ void setSeparatorsCollapsible(bool collapse);
+
+Q_SIGNALS:
+ void aboutToShow();
+ void aboutToHide();
+ void triggered(QAction *action);
+ void hovered(QAction *action);
+
+protected:
+ int columnCount() const;
+
+ void changeEvent(QEvent *);
+ void keyPressEvent(QKeyEvent *);
+ void mouseReleaseEvent(QMouseEvent *);
+ void mousePressEvent(QMouseEvent *);
+ void mouseMoveEvent(QMouseEvent *);
+ void wheelEvent(QWheelEvent *);
+ void enterEvent(QEvent *);
+ void leaveEvent(QEvent *);
+ void hideEvent(QHideEvent *);
+ void paintEvent(QPaintEvent *);
+ void actionEvent(QActionEvent *);
+ void timerEvent(QTimerEvent *);
+ bool event(QEvent *);
+ bool focusNextPrevChild(bool next);
+ void initStyleOption(QStyleOptionMenuItem *option, const QAction *action) const;
+
+#ifdef Q_OS_WINCE
+ QAction* wceCommands(uint command);
+#endif
+
+private Q_SLOTS:
+ void internalSetSloppyAction();
+ void internalDelayedPopup();
+
+private:
+ Q_PRIVATE_SLOT(d_func(), void _q_actionTriggered())
+ Q_PRIVATE_SLOT(d_func(), void _q_actionHovered())
+ Q_PRIVATE_SLOT(d_func(), void _q_overrideMenuActionDestroyed())
+
+#ifdef QT3_SUPPORT
+public:
+ //menudata
+ inline QT3_SUPPORT uint count() const { return actions().count(); }
+ inline QT3_SUPPORT int insertItem(const QString &text, const QObject *receiver, const char* member,
+ const QKeySequence& shortcut = 0, int id = -1, int index = -1) {
+ return insertAny(0, &text, receiver, member, &shortcut, 0, id, index);
+ }
+ inline QT3_SUPPORT int insertItem(const QIcon& icon, const QString &text,
+ const QObject *receiver, const char* member,
+ const QKeySequence& shortcut = 0, int id = -1, int index = -1) {
+ return insertAny(&icon, &text, receiver, member, &shortcut, 0, id, index);
+ }
+ inline QT3_SUPPORT int insertItem(const QPixmap &pixmap, const QObject *receiver, const char* member,
+ const QKeySequence& shortcut = 0, int id = -1, int index = -1) {
+ QIcon icon(pixmap);
+ return insertAny(&icon, 0, receiver, member, &shortcut, 0, id, index);
+ }
+ inline QT3_SUPPORT int insertItem(const QString &text, int id=-1, int index=-1) {
+ return insertAny(0, &text, 0, 0, 0, 0, id, index);
+ }
+ inline QT3_SUPPORT int insertItem(const QIcon& icon, const QString &text, int id=-1, int index=-1) {
+ return insertAny(&icon, &text, 0, 0, 0, 0, id, index);
+ }
+ inline QT3_SUPPORT int insertItem(const QString &text, QMenu *popup, int id=-1, int index=-1) {
+ return insertAny(0, &text, 0, 0, 0, popup, id, index);
+ }
+ inline QT3_SUPPORT int insertItem(const QIcon& icon, const QString &text, QMenu *popup, int id=-1, int index=-1) {
+ return insertAny(&icon, &text, 0, 0, 0, popup, id, index);
+ }
+ inline QT3_SUPPORT int insertItem(const QPixmap &pixmap, int id=-1, int index=-1) {
+ QIcon icon(pixmap);
+ return insertAny(&icon, 0, 0, 0, 0, 0, id, index);
+ }
+ inline QT3_SUPPORT int insertItem(const QPixmap &pixmap, QMenu *popup, int id=-1, int index=-1) {
+ QIcon icon(pixmap);
+ return insertAny(&icon, 0, 0, 0, 0, popup, id, index);
+ }
+ QT3_SUPPORT int insertItem(QMenuItem *item, int id=-1, int index=-1);
+ QT3_SUPPORT int insertSeparator(int index=-1);
+ inline QT3_SUPPORT void removeItem(int id) {
+ if(QAction *act = findActionForId(id))
+ removeAction(act); }
+ inline QT3_SUPPORT void removeItemAt(int index) {
+ if(QAction *act = actions().value(index))
+ removeAction(act); }
+#ifndef QT_NO_SHORTCUT
+ inline QT3_SUPPORT QKeySequence accel(int id) const {
+ if(QAction *act = findActionForId(id))
+ return act->shortcut();
+ return QKeySequence(); }
+ inline QT3_SUPPORT void setAccel(const QKeySequence& key, int id) {
+ if(QAction *act = findActionForId(id))
+ act->setShortcut(key);
+ }
+#endif
+ inline QT3_SUPPORT QIcon iconSet(int id) const {
+ if(QAction *act = findActionForId(id))
+ return act->icon();
+ return QIcon(); }
+ inline QT3_SUPPORT QString text(int id) const {
+ if(QAction *act = findActionForId(id))
+ return act->text();
+ return QString(); }
+ inline QT3_SUPPORT QPixmap pixmap(int id) const {
+ if(QAction *act = findActionForId(id))
+ return act->icon().pixmap(QSize(22, 22));
+ return QPixmap(); }
+ inline QT3_SUPPORT void setWhatsThis(int id, const QString &w) {
+ if(QAction *act = findActionForId(id))
+ act->setWhatsThis(w); }
+ inline QT3_SUPPORT QString whatsThis(int id) const {
+ if(QAction *act = findActionForId(id))
+ return act->whatsThis();
+ return QString(); }
+
+ inline QT3_SUPPORT void changeItem(int id, const QString &text) {
+ if(QAction *act = findActionForId(id))
+ act->setText(text); }
+ inline QT3_SUPPORT void changeItem(int id, const QPixmap &pixmap) {
+ if(QAction *act = findActionForId(id))
+ act->setIcon(QIcon(pixmap)); }
+ inline QT3_SUPPORT void changeItem(int id, const QIcon &icon, const QString &text) {
+ if(QAction *act = findActionForId(id)) {
+ act->setIcon(icon);
+ act->setText(text);
+ }
+ }
+ inline QT3_SUPPORT void setActiveItem(int id) {
+ setActiveAction(findActionForId(id));
+ }
+ inline QT3_SUPPORT bool isItemActive(int id) const {
+ return findActionForId(id) == activeAction();
+ }
+ inline QT3_SUPPORT bool isItemEnabled(int id) const {
+ if(QAction *act = findActionForId(id))
+ return act->isEnabled();
+ return false; }
+ inline QT3_SUPPORT void setItemEnabled(int id, bool enable) {
+ if(QAction *act = findActionForId(id))
+ act->setEnabled(enable);
+ }
+ inline QT3_SUPPORT bool isItemChecked(int id) const {
+ if(QAction *act = findActionForId(id))
+ return act->isChecked();
+ return false;
+ }
+ inline QT3_SUPPORT void setItemChecked(int id, bool check) {
+ if(QAction *act = findActionForId(id)) {
+ act->setCheckable(true);
+ act->setChecked(check);
+ }
+ }
+ inline QT3_SUPPORT bool isItemVisible(int id) const {
+ if(QAction *act = findActionForId(id))
+ return act->isVisible();
+ return false;
+ }
+ inline QT3_SUPPORT void setItemVisible(int id, bool visible) {
+ if(QAction *act = findActionForId(id))
+ act->setVisible(visible);
+ }
+ inline QT3_SUPPORT QRect itemGeometry(int index) {
+ if(QAction *act = actions().value(index))
+ return actionGeometry(act);
+ return QRect();
+ }
+ inline QT3_SUPPORT QFont itemFont(int id) const {
+ if(QAction *act = findActionForId(id))
+ return act->font();
+ return QFont();
+ }
+ inline QT3_SUPPORT void setItemFont(int id, const QFont &font) {
+ if(QAction *act = findActionForId(id))
+ act->setFont(font);
+ }
+ inline QT3_SUPPORT int indexOf(int id) const {
+ return actions().indexOf(findActionForId(id));
+ }
+ inline QT3_SUPPORT int idAt(int index) const {
+ return findIdForAction(actions().value(index));
+ }
+ QT3_SUPPORT void setId (int index, int id);
+ inline QT3_SUPPORT void activateItemAt(int index) {
+ if(QAction *ret = actions().value(index))
+ ret->activate(QAction::Trigger);
+ }
+ inline QT3_SUPPORT bool connectItem(int id, const QObject *receiver, const char* member) {
+ if(QAction *act = findActionForId(id)) {
+ QObject::connect(act, SIGNAL(activated(int)), receiver, member);
+ return true;
+ }
+ return false;
+ }
+ inline QT3_SUPPORT bool disconnectItem(int id,const QObject *receiver, const char* member) {
+ if(QAction *act = findActionForId(id)) {
+ QObject::disconnect(act, SIGNAL(triggered()), receiver, member);
+ return true;
+ }
+ return false;
+ }
+ inline QT3_SUPPORT QMenuItem *findItem(int id) const {
+ return reinterpret_cast<QMenuItem*>(findActionForId(id));
+ }
+
+ inline QT3_SUPPORT void setCheckable(bool){}
+ inline QT3_SUPPORT bool isCheckable() const {return true;}
+
+ QT3_SUPPORT QMenuItem *findPopup( QMenu *popup, int *index );
+
+ QT3_SUPPORT bool setItemParameter(int id, int param);
+ QT3_SUPPORT int itemParameter(int id) const;
+
+ //frame
+ QT3_SUPPORT int frameWidth() const;
+
+ //popupmenu
+ inline QT3_SUPPORT void popup(const QPoint & pos, int indexAtPoint) { popup(pos, actions().value(indexAtPoint)); }
+ inline QT3_SUPPORT int insertTearOffHandle(int = 0, int = 0) {
+ setTearOffEnabled(true);
+ return -1;
+ }
+
+protected:
+ inline QT3_SUPPORT int itemAtPos(const QPoint &p, bool ignoreSeparator = true) {
+ QAction *ret = actionAt(p);
+ if(ignoreSeparator && ret && ret->isSeparator())
+ return -1;
+ return findIdForAction(ret);
+ }
+ inline QT3_SUPPORT int columns() const { return columnCount(); }
+ inline QT3_SUPPORT int itemHeight(int index) {
+ return actionGeometry(actions().value(index)).height();
+ }
+ inline QT3_SUPPORT int itemHeight(QMenuItem *mi) {
+ return actionGeometry(reinterpret_cast<QAction *>(mi)).height();
+ }
+
+Q_SIGNALS:
+ QT_MOC_COMPAT void activated(int itemId);
+ QT_MOC_COMPAT void highlighted(int itemId);
+
+private:
+ int insertAny(const QIcon *icon, const QString *text, const QObject *receiver, const char *member,
+ const QKeySequence *shorcut, const QMenu *popup, int id, int index);
+ QAction *findActionForId(int id) const;
+ int findIdForAction(QAction*) const;
+#endif
+
+protected:
+ QMenu(QMenuPrivate &dd, QWidget* parent = 0);
+
+private:
+ Q_DISABLE_COPY(QMenu)
+
+ friend class QMenuBar;
+ friend class QMenuBarPrivate;
+ friend class QTornOffMenu;
+ friend class Q3PopupMenu;
+ friend class QComboBox;
+ friend class QAction;
+ friend class QToolButtonPrivate;
+
+#ifdef Q_WS_MAC
+ friend void qt_mac_trayicon_activate_action(QMenu *, QAction *action);
+ friend bool qt_mac_watchingAboutToShow(QMenu *);
+ friend OSStatus qt_mac_menu_event(EventHandlerCallRef, EventRef, void *);
+ friend bool qt_mac_activate_action(OSMenuRef, uint, QAction::ActionEvent, bool);
+ friend void qt_mac_emit_menuSignals(QMenu *, bool);
+#endif
+};
+
+#endif // QT_NO_MENU
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QMENU_H
diff --git a/src/gui/widgets/qmenu_mac.mm b/src/gui/widgets/qmenu_mac.mm
new file mode 100644
index 0000000000..ad848c92f9
--- /dev/null
+++ b/src/gui/widgets/qmenu_mac.mm
@@ -0,0 +1,2038 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qmenu.h"
+#include "qhash.h"
+#include <qdebug.h>
+#include "qapplication.h"
+#include <private/qt_mac_p.h>
+#include "qregexp.h"
+#include "qmainwindow.h"
+#include "qdockwidget.h"
+#include "qtoolbar.h"
+#include "qevent.h"
+#include "qstyle.h"
+#include "qwidgetaction.h"
+#include "qmacnativewidget_mac.h"
+
+#include <private/qapplication_p.h>
+#include <private/qcocoaapplication_mac_p.h>
+#include <private/qmenu_p.h>
+#include <private/qmenubar_p.h>
+#include <private/qcocoamenuloader_mac_p.h>
+#include <private/qcocoamenu_mac_p.h>
+#include <private/qt_cocoa_helpers_mac_p.h>
+#include <Cocoa/Cocoa.h>
+
+QT_BEGIN_NAMESPACE
+
+/*****************************************************************************
+ QMenu debug facilities
+ *****************************************************************************/
+
+/*****************************************************************************
+ QMenu globals
+ *****************************************************************************/
+bool qt_mac_no_native_menubar = false;
+bool qt_mac_no_menubar_merge = false;
+bool qt_mac_quit_menu_item_enabled = true;
+int qt_mac_menus_open_count = 0;
+
+static OSMenuRef qt_mac_create_menu(QWidget *w);
+
+#ifndef QT_MAC_USE_COCOA
+static uint qt_mac_menu_static_cmd_id = 'QT00';
+const UInt32 kMenuCreatorQt = 'cute';
+enum {
+ kMenuPropertyQAction = 'QAcT',
+ kMenuPropertyQWidget = 'QWId',
+ kMenuPropertyCausedQWidget = 'QCAU',
+ kMenuPropertyMergeMenu = 'QApP',
+ kMenuPropertyMergeList = 'QAmL',
+ kMenuPropertyWidgetActionWidget = 'QWid',
+ kMenuPropertyWidgetMenu = 'QWMe',
+
+ kHICommandAboutQt = 'AOQT',
+ kHICommandCustomMerge = 'AQt0'
+};
+#endif
+
+static struct {
+ QPointer<QMenuBar> qmenubar;
+ bool modal;
+} qt_mac_current_menubar = { 0, false };
+
+
+
+
+/*****************************************************************************
+ Externals
+ *****************************************************************************/
+extern OSViewRef qt_mac_hiview_for(const QWidget *w); //qwidget_mac.cpp
+extern HIViewRef qt_mac_hiview_for(OSWindowRef w); //qwidget_mac.cpp
+extern IconRef qt_mac_create_iconref(const QPixmap &px); //qpixmap_mac.cpp
+extern QWidget * mac_keyboard_grabber; //qwidget_mac.cpp
+extern bool qt_sendSpontaneousEvent(QObject*, QEvent*); //qapplication_xxx.cpp
+RgnHandle qt_mac_get_rgn(); //qregion_mac.cpp
+void qt_mac_dispose_rgn(RgnHandle r); //qregion_mac.cpp
+
+/*****************************************************************************
+ QMenu utility functions
+ *****************************************************************************/
+bool qt_mac_watchingAboutToShow(QMenu *menu)
+{
+ return menu && menu->receivers(SIGNAL(aboutToShow()));
+}
+
+static int qt_mac_CountMenuItems(OSMenuRef menu)
+{
+ if (menu) {
+#ifndef QT_MAC_USE_COCOA
+ int ret = 0;
+ const int items = CountMenuItems(menu);
+ for(int i = 0; i < items; i++) {
+ MenuItemAttributes attr;
+ if (GetMenuItemAttributes(menu, i+1, &attr) == noErr &&
+ attr & kMenuItemAttrHidden)
+ continue;
+ ++ret;
+ }
+ return ret;
+#else
+ return [menu numberOfItems];
+#endif
+ }
+ return 0;
+}
+
+static bool actualMenuItemVisibility(const QMenuBarPrivate::QMacMenuBarPrivate *mbp,
+ const QMacMenuAction *action)
+{
+ bool visible = action->action->isVisible();
+ if (visible && action->action->text() == QString(QChar(0x14)))
+ return false;
+ if (visible && action->action->menu() && !action->action->menu()->actions().isEmpty() &&
+ !qt_mac_CountMenuItems(action->action->menu()->macMenu(mbp->apple_menu)) &&
+ !qt_mac_watchingAboutToShow(action->action->menu())) {
+ return false;
+ }
+ return visible;
+}
+
+#ifndef QT_MAC_USE_COCOA
+bool qt_mac_activate_action(MenuRef menu, uint command, QAction::ActionEvent action_e, bool by_accel)
+{
+ //fire event
+ QMacMenuAction *action = 0;
+ if (GetMenuCommandProperty(menu, command, kMenuCreatorQt, kMenuPropertyQAction, sizeof(action), 0, &action) != noErr) {
+ QMenuMergeList *list = 0;
+ GetMenuItemProperty(menu, 0, kMenuCreatorQt, kMenuPropertyMergeList,
+ sizeof(list), 0, &list);
+ if (!list && qt_mac_current_menubar.qmenubar) {
+ MenuRef apple_menu = qt_mac_current_menubar.qmenubar->d_func()->mac_menubar->apple_menu;
+ GetMenuItemProperty(apple_menu, 0, kMenuCreatorQt, kMenuPropertyMergeList, sizeof(list), 0, &list);
+ if (list)
+ menu = apple_menu;
+ }
+ if (list) {
+ for(int i = 0; i < list->size(); ++i) {
+ QMenuMergeItem item = list->at(i);
+ if (item.command == command && item.action) {
+ action = item.action;
+ break;
+ }
+ }
+ }
+ if (!action)
+ return false;
+ }
+
+ if (action_e == QAction::Trigger && by_accel && action->ignore_accel) //no, not a real accel (ie tab)
+ return false;
+
+ // Unhighlight the highlighted menu item before triggering the action to
+ // prevent items from staying highlighted while a modal dialog is shown.
+ // This also fixed the problem that parentless modal dialogs leave
+ // the menu item highlighted (since the menu bar is cleared for these types of dialogs).
+ if (action_e == QAction::Trigger)
+ HiliteMenu(0);
+
+ action->action->activate(action_e);
+
+ //now walk up firing for each "caused" widget (like in the platform independent menu)
+ QWidget *caused = 0;
+ if (GetMenuItemProperty(menu, 0, kMenuCreatorQt, kMenuPropertyCausedQWidget, sizeof(caused), 0, &caused) == noErr) {
+ MenuRef caused_menu = 0;
+ if (QMenu *qmenu2 = qobject_cast<QMenu*>(caused))
+ caused_menu = qmenu2->macMenu();
+ else if (QMenuBar *qmenubar2 = qobject_cast<QMenuBar*>(caused))
+ caused_menu = qmenubar2->macMenu();
+ else
+ caused_menu = 0;
+ while(caused_menu) {
+ //fire
+ QWidget *widget = 0;
+ GetMenuItemProperty(caused_menu, 0, kMenuCreatorQt, kMenuPropertyQWidget, sizeof(widget), 0, &widget);
+ if (QMenu *qmenu = qobject_cast<QMenu*>(widget)) {
+ if (action_e == QAction::Trigger) {
+ emit qmenu->triggered(action->action);
+ } else if (action_e == QAction::Hover) {
+ action->action->showStatusText(widget);
+ emit qmenu->hovered(action->action);
+ }
+ } else if (QMenuBar *qmenubar = qobject_cast<QMenuBar*>(widget)) {
+ if (action_e == QAction::Trigger) {
+ emit qmenubar->triggered(action->action);
+ } else if (action_e == QAction::Hover) {
+ action->action->showStatusText(widget);
+ emit qmenubar->hovered(action->action);
+ }
+ break; //nothing more..
+ }
+
+ //walk up
+ if (GetMenuItemProperty(caused_menu, 0, kMenuCreatorQt, kMenuPropertyCausedQWidget,
+ sizeof(caused), 0, &caused) != noErr)
+ break;
+ if (QMenu *qmenu2 = qobject_cast<QMenu*>(caused))
+ caused_menu = qmenu2->macMenu();
+ else if (QMenuBar *qmenubar2 = qobject_cast<QMenuBar*>(caused))
+ caused_menu = qmenubar2->macMenu();
+ else
+ caused_menu = 0;
+ }
+ }
+ return true;
+}
+
+//lookup a QMacMenuAction in a menu
+static int qt_mac_menu_find_action(MenuRef menu, MenuCommand cmd)
+{
+ MenuItemIndex ret_idx;
+ MenuRef ret_menu;
+ if (GetIndMenuItemWithCommandID(menu, cmd, 1, &ret_menu, &ret_idx) == noErr) {
+ if (ret_menu == menu)
+ return (int)ret_idx;
+ }
+ return -1;
+}
+static int qt_mac_menu_find_action(MenuRef menu, QMacMenuAction *action)
+{
+ return qt_mac_menu_find_action(menu, action->command);
+}
+
+typedef QMultiHash<OSMenuRef, EventHandlerRef> EventHandlerHash;
+Q_GLOBAL_STATIC(EventHandlerHash, menu_eventHandlers_hash)
+
+static EventTypeSpec widget_in_menu_events[] = {
+ { kEventClassMenu, kEventMenuMeasureItemWidth },
+ { kEventClassMenu, kEventMenuMeasureItemHeight },
+ { kEventClassMenu, kEventMenuDrawItem },
+ { kEventClassMenu, kEventMenuCalculateSize }
+};
+
+static OSStatus qt_mac_widget_in_menu_eventHandler(EventHandlerCallRef er, EventRef event, void *)
+{
+ UInt32 ekind = GetEventKind(event);
+ UInt32 eclass = GetEventClass(event);
+ OSStatus result = eventNotHandledErr;
+ switch (eclass) {
+ case kEventClassMenu:
+ switch (ekind) {
+ default:
+ break;
+ case kEventMenuMeasureItemWidth: {
+ MenuItemIndex item;
+ GetEventParameter(event, kEventParamMenuItemIndex, typeMenuItemIndex,
+ 0, sizeof(item), 0, &item);
+ OSMenuRef menu;
+ GetEventParameter(event, kEventParamDirectObject, typeMenuRef, 0, sizeof(menu), 0, &menu);
+ QWidget *widget;
+ if (GetMenuItemProperty(menu, item, kMenuCreatorQt, kMenuPropertyWidgetActionWidget,
+ sizeof(widget), 0, &widget) == noErr) {
+ short width = short(widget->sizeHint().width());
+ SetEventParameter(event, kEventParamMenuItemWidth, typeSInt16,
+ sizeof(short), &width);
+ result = noErr;
+ }
+ break; }
+ case kEventMenuMeasureItemHeight: {
+ MenuItemIndex item;
+ GetEventParameter(event, kEventParamMenuItemIndex, typeMenuItemIndex,
+ 0, sizeof(item), 0, &item);
+ OSMenuRef menu;
+ GetEventParameter(event, kEventParamDirectObject, typeMenuRef, 0, sizeof(menu), 0, &menu);
+ QWidget *widget;
+ if (GetMenuItemProperty(menu, item, kMenuCreatorQt, kMenuPropertyWidgetActionWidget,
+ sizeof(widget), 0, &widget) == noErr && widget) {
+ short height = short(widget->sizeHint().height());
+ SetEventParameter(event, kEventParamMenuItemHeight, typeSInt16,
+ sizeof(short), &height);
+ result = noErr;
+ }
+ break; }
+ case kEventMenuDrawItem:
+ result = noErr;
+ break;
+ case kEventMenuCalculateSize: {
+ result = CallNextEventHandler(er, event);
+ if (result == noErr) {
+ OSMenuRef menu;
+ GetEventParameter(event, kEventParamDirectObject, typeMenuRef, 0, sizeof(menu), 0, &menu);
+ HIViewRef content;
+ HIMenuGetContentView(menu, kThemeMenuTypePullDown, &content);
+ UInt16 count = CountMenuItems(menu);
+ for (MenuItemIndex i = 1; i <= count; ++i) {
+ QWidget *widget;
+ if (GetMenuItemProperty(menu, i, kMenuCreatorQt, kMenuPropertyWidgetActionWidget,
+ sizeof(widget), 0, &widget) == noErr && widget) {
+ RgnHandle itemRgn = qt_mac_get_rgn();
+ GetControlRegion(content, i, itemRgn);
+
+ Rect bounds;
+ GetRegionBounds( itemRgn, &bounds );
+ qt_mac_dispose_rgn(itemRgn);
+ widget->setGeometry(bounds.left, bounds.top,
+ bounds.right - bounds.left, bounds.bottom - bounds.top);
+ }
+ }
+ }
+ break; }
+ }
+ }
+ return result;
+}
+
+//handling of events for menurefs created by Qt..
+static EventTypeSpec menu_events[] = {
+ { kEventClassCommand, kEventCommandProcess },
+ { kEventClassMenu, kEventMenuTargetItem },
+ { kEventClassMenu, kEventMenuOpening },
+ { kEventClassMenu, kEventMenuClosed }
+};
+
+// Special case for kEventMenuMatchKey, see qt_mac_create_menu below.
+static EventTypeSpec menu_menu_events[] = {
+ { kEventClassMenu, kEventMenuMatchKey }
+};
+
+OSStatus qt_mac_menu_event(EventHandlerCallRef er, EventRef event, void *)
+{
+ QScopedLoopLevelCounter loopLevelCounter(QApplicationPrivate::instance()->threadData);
+
+ bool handled_event = true;
+ UInt32 ekind = GetEventKind(event), eclass = GetEventClass(event);
+ switch(eclass) {
+ case kEventClassCommand:
+ if (ekind == kEventCommandProcess) {
+ UInt32 context;
+ GetEventParameter(event, kEventParamMenuContext, typeUInt32,
+ 0, sizeof(context), 0, &context);
+ HICommand cmd;
+ GetEventParameter(event, kEventParamDirectObject, typeHICommand,
+ 0, sizeof(cmd), 0, &cmd);
+ if (!mac_keyboard_grabber && (context & kMenuContextKeyMatching)) {
+ QMacMenuAction *action = 0;
+ if (GetMenuCommandProperty(cmd.menu.menuRef, cmd.commandID, kMenuCreatorQt,
+ kMenuPropertyQAction, sizeof(action), 0, &action) == noErr) {
+ QWidget *widget = 0;
+ if (qApp->activePopupWidget())
+ widget = (qApp->activePopupWidget()->focusWidget() ?
+ qApp->activePopupWidget()->focusWidget() : qApp->activePopupWidget());
+ else if (QApplicationPrivate::focus_widget)
+ widget = QApplicationPrivate::focus_widget;
+ if (widget) {
+ int key = action->action->shortcut();
+ QKeyEvent accel_ev(QEvent::ShortcutOverride, (key & (~Qt::KeyboardModifierMask)),
+ Qt::KeyboardModifiers(key & Qt::KeyboardModifierMask));
+ accel_ev.ignore();
+ qt_sendSpontaneousEvent(widget, &accel_ev);
+ if (accel_ev.isAccepted()) {
+ handled_event = false;
+ break;
+ }
+ }
+ }
+ }
+ handled_event = qt_mac_activate_action(cmd.menu.menuRef, cmd.commandID,
+ QAction::Trigger, context & kMenuContextKeyMatching);
+ }
+ break;
+ case kEventClassMenu: {
+ MenuRef menu;
+ GetEventParameter(event, kEventParamDirectObject, typeMenuRef, NULL, sizeof(menu), NULL, &menu);
+ if (ekind == kEventMenuMatchKey) {
+ // Don't activate any actions if we are showing a native modal dialog,
+ // the key events should go to the dialog in this case.
+ if (QApplicationPrivate::native_modal_dialog_active)
+ return menuItemNotFoundErr;
+
+ handled_event = false;
+ } else if (ekind == kEventMenuTargetItem) {
+ MenuCommand command;
+ GetEventParameter(event, kEventParamMenuCommand, typeMenuCommand,
+ 0, sizeof(command), 0, &command);
+ handled_event = qt_mac_activate_action(menu, command, QAction::Hover, false);
+ } else if (ekind == kEventMenuOpening || ekind == kEventMenuClosed) {
+ qt_mac_menus_open_count += (ekind == kEventMenuOpening) ? 1 : -1;
+ MenuRef mr;
+ GetEventParameter(event, kEventParamDirectObject, typeMenuRef,
+ 0, sizeof(mr), 0, &mr);
+
+ QWidget *widget = 0;
+ if (GetMenuItemProperty(mr, 0, kMenuCreatorQt, kMenuPropertyQWidget, sizeof(widget), 0, &widget) == noErr) {
+ if (QMenu *qmenu = qobject_cast<QMenu*>(widget)) {
+ handled_event = true;
+ if (ekind == kEventMenuOpening) {
+ emit qmenu->aboutToShow();
+
+ int merged = 0;
+ const QMenuPrivate::QMacMenuPrivate *mac_menu = qmenu->d_func()->mac_menu;
+ for(int i = 0; i < mac_menu->actionItems.size(); ++i) {
+ QMacMenuAction *action = mac_menu->actionItems.at(i);
+ if (action->action->isSeparator()) {
+ bool hide = false;
+ if(!action->action->isVisible()) {
+ hide = true;
+ } else if (merged && merged == i) {
+ hide = true;
+ } else {
+ for(int l = i+1; l < mac_menu->actionItems.size(); ++l) {
+ QMacMenuAction *action = mac_menu->actionItems.at(l);
+ if (action->merged) {
+ hide = true;
+ } else if (action->action->isSeparator()) {
+ if (hide)
+ break;
+ } else if (!action->merged) {
+ hide = false;
+ break;
+ }
+ }
+ }
+
+ const int index = qt_mac_menu_find_action(mr, action);
+ if (hide) {
+ ++merged;
+ ChangeMenuItemAttributes(mr, index, kMenuItemAttrHidden, 0);
+ } else {
+ ChangeMenuItemAttributes(mr, index, 0, kMenuItemAttrHidden);
+ }
+ } else if (action->merged) {
+ ++merged;
+ }
+ }
+ } else {
+ emit qmenu->aboutToHide();
+ }
+ }
+ }
+ } else {
+ handled_event = false;
+ }
+ break; }
+ default:
+ handled_event = false;
+ break;
+ }
+ if (!handled_event) //let the event go through
+ return CallNextEventHandler(er, event);
+ return noErr; //we eat the event
+}
+static EventHandlerRef mac_menu_event_handler = 0;
+static EventHandlerUPP mac_menu_eventUPP = 0;
+static void qt_mac_cleanup_menu_event()
+{
+ if (mac_menu_event_handler) {
+ RemoveEventHandler(mac_menu_event_handler);
+ mac_menu_event_handler = 0;
+ }
+ if (mac_menu_eventUPP) {
+ DisposeEventHandlerUPP(mac_menu_eventUPP);
+ mac_menu_eventUPP = 0;
+ }
+}
+static inline void qt_mac_create_menu_event_handler()
+{
+ if (!mac_menu_event_handler) {
+ mac_menu_eventUPP = NewEventHandlerUPP(qt_mac_menu_event);
+ InstallEventHandler(GetApplicationEventTarget(), mac_menu_eventUPP,
+ GetEventTypeCount(menu_events), menu_events, 0,
+ &mac_menu_event_handler);
+ qAddPostRoutine(qt_mac_cleanup_menu_event);
+ }
+}
+
+
+//enabling of commands
+static void qt_mac_command_set_enabled(MenuRef menu, UInt32 cmd, bool b)
+{
+ if (cmd == kHICommandQuit)
+ qt_mac_quit_menu_item_enabled = b;
+
+ if (b) {
+ EnableMenuCommand(menu, cmd);
+ if (MenuRef dock_menu = GetApplicationDockTileMenu())
+ EnableMenuCommand(dock_menu, cmd);
+ } else {
+ DisableMenuCommand(menu, cmd);
+ if (MenuRef dock_menu = GetApplicationDockTileMenu())
+ DisableMenuCommand(dock_menu, cmd);
+ }
+}
+
+static bool qt_mac_auto_apple_menu(MenuCommand cmd)
+{
+ return (cmd == kHICommandPreferences || cmd == kHICommandQuit);
+}
+
+static void qt_mac_get_accel(quint32 accel_key, quint32 *modif, quint32 *key) {
+ if (modif) {
+ *modif = 0;
+ if ((accel_key & Qt::CTRL) != Qt::CTRL)
+ *modif |= kMenuNoCommandModifier;
+ if ((accel_key & Qt::META) == Qt::META)
+ *modif |= kMenuControlModifier;
+ if ((accel_key & Qt::ALT) == Qt::ALT)
+ *modif |= kMenuOptionModifier;
+ if ((accel_key & Qt::SHIFT) == Qt::SHIFT)
+ *modif |= kMenuShiftModifier;
+ }
+
+ accel_key &= ~(Qt::MODIFIER_MASK | Qt::UNICODE_ACCEL);
+ if (key) {
+ *key = 0;
+ if (accel_key == Qt::Key_Return)
+ *key = kMenuReturnGlyph;
+ else if (accel_key == Qt::Key_Enter)
+ *key = kMenuEnterGlyph;
+ else if (accel_key == Qt::Key_Tab)
+ *key = kMenuTabRightGlyph;
+ else if (accel_key == Qt::Key_Backspace)
+ *key = kMenuDeleteLeftGlyph;
+ else if (accel_key == Qt::Key_Delete)
+ *key = kMenuDeleteRightGlyph;
+ else if (accel_key == Qt::Key_Escape)
+ *key = kMenuEscapeGlyph;
+ else if (accel_key == Qt::Key_PageUp)
+ *key = kMenuPageUpGlyph;
+ else if (accel_key == Qt::Key_PageDown)
+ *key = kMenuPageDownGlyph;
+ else if (accel_key == Qt::Key_Up)
+ *key = kMenuUpArrowGlyph;
+ else if (accel_key == Qt::Key_Down)
+ *key = kMenuDownArrowGlyph;
+ else if (accel_key == Qt::Key_Left)
+ *key = kMenuLeftArrowGlyph;
+ else if (accel_key == Qt::Key_Right)
+ *key = kMenuRightArrowGlyph;
+ else if (accel_key == Qt::Key_CapsLock)
+ *key = kMenuCapsLockGlyph;
+ else if (accel_key >= Qt::Key_F1 && accel_key <= Qt::Key_F15)
+ *key = (accel_key - Qt::Key_F1) + kMenuF1Glyph;
+ else if (accel_key == Qt::Key_Home)
+ *key = kMenuNorthwestArrowGlyph;
+ else if (accel_key == Qt::Key_End)
+ *key = kMenuSoutheastArrowGlyph;
+ }
+}
+#else // Cocoa
+static inline void syncNSMenuItemVisiblity(NSMenuItem *menuItem, bool actionVisibility)
+{
+ [menuItem setHidden:NO];
+ [menuItem setHidden:YES];
+ [menuItem setHidden:!actionVisibility];
+}
+
+static inline void syncMenuBarItemsVisiblity(const QMenuBarPrivate::QMacMenuBarPrivate *mac_menubar)
+{
+ const QList<QMacMenuAction *> &menubarActions = mac_menubar->actionItems;
+ for (int i = 0; i < menubarActions.size(); ++i) {
+ const QMacMenuAction *action = menubarActions.at(i);
+ syncNSMenuItemVisiblity(action->menuItem, actualMenuItemVisibility(mac_menubar, action));
+ }
+}
+
+static inline QT_MANGLE_NAMESPACE(QCocoaMenuLoader) *getMenuLoader()
+{
+ return [NSApp QT_MANGLE_NAMESPACE(qt_qcocoamenuLoader)];
+}
+
+static NSMenuItem *createNSMenuItem(const QString &title)
+{
+ NSMenuItem *item = [[NSMenuItem alloc]
+ initWithTitle:reinterpret_cast<const NSString *>(static_cast<CFStringRef>(QCFString(title)))
+ action:@selector(qtDispatcherToQAction:) keyEquivalent:@""];
+ [item setTarget:getMenuLoader()];
+ return item;
+}
+#endif
+
+
+
+// helper that recurses into a menu structure and en/dis-ables them
+void qt_mac_set_modal_state_helper_recursive(OSMenuRef menu, OSMenuRef merge, bool on)
+{
+#ifndef QT_MAC_USE_COCOA
+ for (int i = 0; i < CountMenuItems(menu); i++) {
+ OSMenuRef submenu;
+ GetMenuItemHierarchicalMenu(menu, i+1, &submenu);
+ if (submenu != merge) {
+ if (submenu)
+ qt_mac_set_modal_state_helper_recursive(submenu, merge, on);
+ if (on)
+ DisableMenuItem(submenu, 0);
+ else
+ EnableMenuItem(submenu, 0);
+ }
+ }
+#else
+ for (NSMenuItem *item in [menu itemArray]) {
+ OSMenuRef submenu = [item submenu];
+ if (submenu != merge) {
+ if (submenu)
+ qt_mac_set_modal_state_helper_recursive(submenu, merge, on);
+ if (!on) {
+ // The item should follow what the QAction has.
+ if ([item tag]) {
+ QAction *action = reinterpret_cast<QAction *>([item tag]);
+ [item setEnabled:action->isEnabled()];
+ } else {
+ [item setEnabled:YES];
+ }
+ } else {
+ [item setEnabled:NO];
+ }
+ }
+ }
+#endif
+}
+
+//toggling of modal state
+static void qt_mac_set_modal_state(OSMenuRef menu, bool on)
+{
+#ifndef QT_MAC_USE_COCOA
+ OSMenuRef merge = 0;
+ GetMenuItemProperty(menu, 0, kMenuCreatorQt, kMenuPropertyMergeMenu,
+ sizeof(merge), 0, &merge);
+
+ qt_mac_set_modal_state_helper_recursive(menu, merge, on);
+
+ UInt32 commands[] = { kHICommandQuit, kHICommandPreferences, kHICommandAbout, kHICommandAboutQt, 0 };
+ for(int c = 0; commands[c]; c++) {
+ bool enabled = !on;
+ if (enabled) {
+ QMacMenuAction *action = 0;
+ GetMenuCommandProperty(menu, commands[c], kMenuCreatorQt, kMenuPropertyQAction,
+ sizeof(action), 0, &action);
+ if (!action && merge) {
+ GetMenuCommandProperty(merge, commands[c], kMenuCreatorQt, kMenuPropertyQAction,
+ sizeof(action), 0, &action);
+ if (!action) {
+ QMenuMergeList *list = 0;
+ GetMenuItemProperty(merge, 0, kMenuCreatorQt, kMenuPropertyMergeList,
+ sizeof(list), 0, &list);
+ for(int i = 0; list && i < list->size(); ++i) {
+ QMenuMergeItem item = list->at(i);
+ if (item.command == commands[c] && item.action) {
+ action = item.action;
+ break;
+ }
+ }
+ }
+ }
+
+ if (!action) {
+ if (commands[c] != kHICommandQuit)
+ enabled = false;
+ } else {
+ enabled = action->action ? action->action->isEnabled() : 0;
+ }
+ }
+ qt_mac_command_set_enabled(menu, commands[c], enabled);
+ }
+#else
+ OSMenuRef merge = QMenuPrivate::mergeMenuHash.value(menu);
+ qt_mac_set_modal_state_helper_recursive(menu, merge, on);
+ // I'm ignoring the special items now, since they should get handled via a syncAction()
+#endif
+}
+
+bool qt_mac_menubar_is_open()
+{
+ return qt_mac_menus_open_count > 0;
+}
+
+void qt_mac_clear_menubar()
+{
+#ifndef QT_MAC_USE_COCOA
+ MenuRef clear_menu = 0;
+ if (CreateNewMenu(0, 0, &clear_menu) == noErr) {
+ SetRootMenu(clear_menu);
+ ReleaseMenu(clear_menu);
+ } else {
+ qWarning("QMenu: Internal error at %s:%d", __FILE__, __LINE__);
+ }
+ ClearMenuBar();
+ qt_mac_command_set_enabled(0, kHICommandPreferences, false);
+ InvalMenuBar();
+#else
+ QMacCocoaAutoReleasePool pool;
+ QT_MANGLE_NAMESPACE(QCocoaMenuLoader) *loader = getMenuLoader();
+ NSMenu *menu = [loader menu];
+ [loader ensureAppMenuInMenu:menu];
+ [NSApp setMainMenu:menu];
+#endif
+}
+
+
+QMacMenuAction::~QMacMenuAction()
+{
+#ifdef QT_MAC_USE_COCOA
+ [menu release];
+ [menuItem setTag:nil];
+ [menuItem release];
+#endif
+}
+
+#ifndef QT_MAC_USE_COCOA
+static MenuCommand qt_mac_menu_merge_action(MenuRef merge, QMacMenuAction *action)
+#else
+static NSMenuItem *qt_mac_menu_merge_action(OSMenuRef merge, QMacMenuAction *action)
+#endif
+{
+ if (qt_mac_no_menubar_merge || action->action->menu() || action->action->isSeparator()
+ || action->action->menuRole() == QAction::NoRole)
+ return 0;
+
+ QString t = qt_mac_removeMnemonics(action->action->text().toLower());
+ int st = t.lastIndexOf(QLatin1Char('\t'));
+ if (st != -1)
+ t.remove(st, t.length()-st);
+ t.replace(QRegExp(QString::fromLatin1("\\.*$")), QLatin1String("")); //no ellipses
+ //now the fun part
+#ifndef QT_MAC_USE_COCOA
+ MenuCommand ret = 0;
+#else
+ NSMenuItem *ret = 0;
+ QT_MANGLE_NAMESPACE(QCocoaMenuLoader) *loader = getMenuLoader();
+#endif
+ switch (action->action->menuRole()) {
+ case QAction::NoRole:
+ ret = 0;
+ break;
+ case QAction::ApplicationSpecificRole:
+#ifndef QT_MAC_USE_COCOA
+ {
+ QMenuMergeList *list = 0;
+ if (GetMenuItemProperty(merge, 0, kMenuCreatorQt, kMenuPropertyMergeList,
+ sizeof(list), 0, &list) == noErr && list) {
+ MenuCommand lastCustom = kHICommandCustomMerge;
+ for(int i = 0; i < list->size(); ++i) {
+ QMenuMergeItem item = list->at(i);
+ if (item.command == lastCustom)
+ ++lastCustom;
+ }
+ ret = lastCustom;
+ } else {
+ // The list hasn't been created, so, must be the first one.
+ ret = kHICommandCustomMerge;
+ }
+ }
+#else
+ ret = [loader appSpecificMenuItem];
+#endif
+ break;
+ case QAction::AboutRole:
+#ifndef QT_MAC_USE_COCOA
+ ret = kHICommandAbout;
+#else
+ ret = [loader aboutMenuItem];
+#endif
+ break;
+ case QAction::AboutQtRole:
+#ifndef QT_MAC_USE_COCOA
+ ret = kHICommandAboutQt;
+#else
+ ret = [loader aboutQtMenuItem];
+#endif
+ break;
+ case QAction::QuitRole:
+#ifndef QT_MAC_USE_COCOA
+ ret = kHICommandQuit;
+#else
+ ret = [loader quitMenuItem];
+#endif
+ break;
+ case QAction::PreferencesRole:
+#ifndef QT_MAC_USE_COCOA
+ ret = kHICommandPreferences;
+#else
+ ret = [loader preferencesMenuItem];
+#endif
+ break;
+ case QAction::TextHeuristicRole: {
+ QString aboutString = QMenuBar::tr("About").toLower();
+ if (t.startsWith(aboutString) || t.endsWith(aboutString)) {
+ if (t.indexOf(QRegExp(QString::fromLatin1("qt$"), Qt::CaseInsensitive)) == -1) {
+#ifndef QT_MAC_USE_COCOA
+ ret = kHICommandAbout;
+#else
+ ret = [loader aboutMenuItem];
+#endif
+ } else {
+#ifndef QT_MAC_USE_COCOA
+ ret = kHICommandAboutQt;
+#else
+ ret = [loader aboutQtMenuItem];
+#endif
+ }
+ } else if (t.startsWith(QMenuBar::tr("Config").toLower())
+ || t.startsWith(QMenuBar::tr("Preference").toLower())
+ || t.startsWith(QMenuBar::tr("Options").toLower())
+ || t.startsWith(QMenuBar::tr("Setting").toLower())
+ || t.startsWith(QMenuBar::tr("Setup").toLower())) {
+#ifndef QT_MAC_USE_COCOA
+ ret = kHICommandPreferences;
+#else
+ ret = [loader preferencesMenuItem];
+#endif
+ } else if (t.startsWith(QMenuBar::tr("Quit").toLower())
+ || t.startsWith(QMenuBar::tr("Exit").toLower())) {
+#ifndef QT_MAC_USE_COCOA
+ ret = kHICommandQuit;
+#else
+ ret = [loader quitMenuItem];
+#endif
+ }
+ }
+ break;
+ }
+
+#ifndef QT_MAC_USE_COCOA
+ QMenuMergeList *list = 0;
+ if (GetMenuItemProperty(merge, 0, kMenuCreatorQt, kMenuPropertyMergeList,
+ sizeof(list), 0, &list) == noErr && list) {
+ for(int i = 0; i < list->size(); ++i) {
+ QMenuMergeItem item = list->at(i);
+ if (item.command == ret && item.action)
+ return 0;
+ }
+ }
+
+ QAction *cmd_action = 0;
+ if (GetMenuCommandProperty(merge, ret, kMenuCreatorQt, kMenuPropertyQAction,
+ sizeof(cmd_action), 0, &cmd_action) == noErr && cmd_action)
+ return 0; //already taken
+#else
+ if (QMenuMergeList *list = QMenuPrivate::mergeMenuItemsHash.value(merge)) {
+ for(int i = 0; i < list->size(); ++i) {
+ const QMenuMergeItem &item = list->at(i);
+ if (item.menuItem == ret && item.action)
+ return 0;
+ }
+ }
+
+ if ([ret tag] != 0)
+ ret = 0; // already taken
+#endif
+ return ret;
+}
+
+static QString qt_mac_menu_merge_text(QMacMenuAction *action)
+{
+ QString ret;
+#ifdef QT_MAC_USE_COCOA
+ QT_MANGLE_NAMESPACE(QCocoaMenuLoader) *loader = getMenuLoader();
+#endif
+ if (action->action->menuRole() == QAction::ApplicationSpecificRole)
+ ret = action->action->text();
+#ifndef QT_MAC_USE_COCOA
+ else if (action->command == kHICommandAbout)
+ ret = QMenuBar::tr("About %1").arg(qAppName());
+ else if (action->command == kHICommandAboutQt)
+ ret = QMenuBar::tr("About Qt");
+ else if (action->command == kHICommandPreferences)
+ ret = QMenuBar::tr("Preferences");
+ else if (action->command == kHICommandQuit)
+ ret = QMenuBar::tr("Quit %1").arg(qAppName());
+#else
+ else if (action->menuItem == [loader aboutMenuItem])
+ ret = QMenuBar::tr("About %1").arg(qAppName());
+ else if (action->menuItem == [loader aboutQtMenuItem])
+ ret = QMenuBar::tr("About Qt");
+ else if (action->menuItem == [loader preferencesMenuItem])
+ ret = QMenuBar::tr("Preferences");
+ else if (action->menuItem == [loader quitMenuItem])
+ ret = QMenuBar::tr("Quit %1").arg(qAppName());
+#endif
+ return ret;
+}
+
+static QKeySequence qt_mac_menu_merge_accel(QMacMenuAction *action)
+{
+ QKeySequence ret;
+#ifdef QT_MAC_USE_COCOA
+ QT_MANGLE_NAMESPACE(QCocoaMenuLoader) *loader = getMenuLoader();
+#endif
+ if (action->action->menuRole() == QAction::ApplicationSpecificRole)
+ ret = action->action->shortcut();
+#ifndef QT_MAC_USE_COCOA
+ else if (action->command == kHICommandPreferences)
+ ret = QKeySequence(Qt::CTRL+Qt::Key_Comma);
+ else if (action->command == kHICommandQuit)
+ ret = QKeySequence(Qt::CTRL+Qt::Key_Q);
+#else
+ else if (action->menuItem == [loader preferencesMenuItem])
+ ret = QKeySequence(Qt::CTRL+Qt::Key_Comma);
+ else if (action->menuItem == [loader quitMenuItem])
+ ret = QKeySequence(Qt::CTRL+Qt::Key_Q);
+#endif
+ return ret;
+}
+
+void Q_GUI_EXPORT qt_mac_set_menubar_icons(bool b)
+{ QApplication::instance()->setAttribute(Qt::AA_DontShowIconsInMenus, !b); }
+void Q_GUI_EXPORT qt_mac_set_native_menubar(bool b) { qt_mac_no_native_menubar = !b; }
+void Q_GUI_EXPORT qt_mac_set_menubar_merge(bool b) { qt_mac_no_menubar_merge = !b; }
+
+/*****************************************************************************
+ QMenu bindings
+ *****************************************************************************/
+QMenuPrivate::QMacMenuPrivate::QMacMenuPrivate() : menu(0)
+{
+}
+
+QMenuPrivate::QMacMenuPrivate::~QMacMenuPrivate()
+{
+#ifndef QT_MAC_USE_COCOA
+ for(QList<QMacMenuAction*>::Iterator it = actionItems.begin(); it != actionItems.end(); ++it) {
+ QMacMenuAction *action = (*it);
+ RemoveMenuCommandProperty(action->menu, action->command, kMenuCreatorQt, kMenuPropertyQAction);
+ if (action->merged) {
+ QMenuMergeList *list = 0;
+ GetMenuItemProperty(action->menu, 0, kMenuCreatorQt, kMenuPropertyMergeList,
+ sizeof(list), 0, &list);
+ for(int i = 0; list && i < list->size(); ) {
+ QMenuMergeItem item = list->at(i);
+ if (item.action == action)
+ list->removeAt(i);
+ else
+ ++i;
+ }
+ }
+ delete action;
+ }
+ if (menu) {
+ EventHandlerHash::iterator it = menu_eventHandlers_hash()->find(menu);
+ while (it != menu_eventHandlers_hash()->end() && it.key() == menu) {
+ RemoveEventHandler(it.value());
+ ++it;
+ }
+ menu_eventHandlers_hash()->remove(menu);
+ ReleaseMenu(menu);
+ }
+#else
+ QMacCocoaAutoReleasePool pool;
+ while (actionItems.size()) {
+ QMacMenuAction *action = actionItems.takeFirst();
+ if (QMenuMergeList *list = mergeMenuItemsHash.value(action->menu)) {
+ int i = 0;
+ while (i < list->size()) {
+ const QMenuMergeItem &item = list->at(i);
+ if (item.action == action)
+ list->removeAt(i);
+ else
+ ++i;
+ }
+ }
+ delete action;
+ }
+ mergeMenuHash.remove(menu);
+ mergeMenuItemsHash.remove(menu);
+ [menu release];
+#endif
+}
+
+void
+QMenuPrivate::QMacMenuPrivate::addAction(QAction *a, QMacMenuAction *before, QMenuPrivate *qmenu)
+{
+ QMacMenuAction *action = new QMacMenuAction;
+ action->action = a;
+ action->ignore_accel = 0;
+ action->merged = 0;
+ action->menu = 0;
+#ifndef QT_MAC_USE_COCOA
+ action->command = qt_mac_menu_static_cmd_id++;
+#endif
+ addAction(action, before, qmenu);
+}
+
+void
+QMenuPrivate::QMacMenuPrivate::addAction(QMacMenuAction *action, QMacMenuAction *before, QMenuPrivate *qmenu)
+{
+#ifdef QT_MAC_USE_COCOA
+ QMacCocoaAutoReleasePool pool;
+ Q_UNUSED(qmenu);
+#endif
+ if (!action)
+ return;
+ int before_index = actionItems.indexOf(before);
+ if (before_index < 0) {
+ before = 0;
+ before_index = actionItems.size();
+ }
+ actionItems.insert(before_index, action);
+
+#ifndef QT_MAC_USE_COCOA
+ int index = qt_mac_menu_find_action(menu, action);
+#else
+ [menu retain];
+ [action->menu release];
+#endif
+ action->menu = menu;
+
+ /* When the action is considered a mergable action it
+ will stay that way, until removed.. */
+ if (!qt_mac_no_menubar_merge) {
+#ifndef QT_MAC_USE_COCOA
+ MenuRef merge = 0;
+ GetMenuItemProperty(menu, 0, kMenuCreatorQt, kMenuPropertyMergeMenu,
+ sizeof(merge), 0, &merge);
+#else
+ OSMenuRef merge = QMenuPrivate::mergeMenuHash.value(menu);
+#endif
+ if (merge) {
+#ifndef QT_MAC_USE_COCOA
+ if (MenuCommand cmd = qt_mac_menu_merge_action(merge, action)) {
+ action->merged = 1;
+ action->menu = merge;
+ action->command = cmd;
+ if (qt_mac_auto_apple_menu(cmd))
+ index = 0; //no need
+
+ QMenuMergeList *list = 0;
+ if (GetMenuItemProperty(merge, 0, kMenuCreatorQt, kMenuPropertyMergeList,
+ sizeof(list), 0, &list) != noErr || !list) {
+ list = new QMenuMergeList;
+ SetMenuItemProperty(merge, 0, kMenuCreatorQt, kMenuPropertyMergeList,
+ sizeof(list), &list);
+ }
+ list->append(QMenuMergeItem(cmd, action));
+ }
+#else
+ if (NSMenuItem *cmd = qt_mac_menu_merge_action(merge, action)) {
+ action->merged = 1;
+ [merge retain];
+ [action->menu release];
+ action->menu = merge;
+ [cmd retain];
+ [cmd setAction:@selector(qtDispatcherToQAction:)];
+ [cmd setTarget:getMenuLoader()];
+ [action->menuItem release];
+ action->menuItem = cmd;
+ QMenuMergeList *list = QMenuPrivate::mergeMenuItemsHash.value(merge);
+ if (!list) {
+ list = new QMenuMergeList;
+ QMenuPrivate::mergeMenuItemsHash.insert(merge, list);
+ }
+ list->append(QMenuMergeItem(cmd, action));
+ }
+#endif
+ }
+ }
+
+#ifdef QT_MAC_USE_COCOA
+ NSMenuItem *newItem = action->menuItem;
+#endif
+ if (
+#ifndef QT_MAC_USE_COCOA
+ index == -1
+#else
+ newItem == 0
+#endif
+ ) {
+#ifndef QT_MAC_USE_COCOA
+ index = before_index;
+ MenuItemAttributes attr = kMenuItemAttrAutoRepeat;
+#else
+ newItem = createNSMenuItem(action->action->text());
+ action->menuItem = newItem;
+#endif
+ if (before) {
+#ifndef QT_MAC_USE_COCOA
+ InsertMenuItemTextWithCFString(action->menu, 0, qMax(before_index, 0), attr, action->command);
+#else
+ [menu insertItem:newItem atIndex:qMax(before_index, 0)];
+#endif
+ } else {
+#ifndef QT_MAC_USE_COCOA
+ // Append the menu item to the menu. If it is a kHICommandAbout or a kHICommandAboutQt append
+ // a separator also (to get a separator above "Preferences"), but make sure that we don't
+ // add separators between two "about" items.
+
+ // Build a set of all commands that could possibly be before the separator.
+ QSet<MenuCommand> mergedItems;
+ mergedItems.insert(kHICommandAbout);
+ mergedItems.insert(kHICommandAboutQt);
+ mergedItems.insert(kHICommandCustomMerge);
+
+ QMenuMergeList *list = 0;
+ if (GetMenuItemProperty(action->menu, 0, kMenuCreatorQt, kMenuPropertyMergeList,
+ sizeof(list), 0, &list) == noErr && list) {
+ for (int i = 0; i < list->size(); ++i) {
+ MenuCommand command = list->at(i).command;
+ if (command > kHICommandCustomMerge) {
+ mergedItems.insert(command);
+ }
+ }
+ }
+
+ const int itemCount = CountMenuItems(action->menu);
+ MenuItemAttributes testattr;
+ GetMenuItemAttributes(action->menu, itemCount , &testattr);
+ if (mergedItems.contains(action->command)
+ && (testattr & kMenuItemAttrSeparator)) {
+ InsertMenuItemTextWithCFString(action->menu, 0, qMax(itemCount - 1, 0), attr, action->command);
+ index = itemCount;
+ } else {
+ MenuItemIndex tmpIndex;
+ AppendMenuItemTextWithCFString(action->menu, 0, attr, action->command, &tmpIndex);
+ index = tmpIndex;
+ if (mergedItems.contains(action->command))
+ AppendMenuItemTextWithCFString(action->menu, 0, kMenuItemAttrSeparator, 0, &tmpIndex);
+ }
+#else
+ [menu addItem:newItem];
+#endif
+ }
+
+ QWidget *widget = qmenu ? qmenu->widgetItems.value(action->action) : 0;
+ if (widget) {
+#ifndef QT_MAC_USE_COCOA
+ ChangeMenuAttributes(action->menu, kMenuAttrDoNotCacheImage, 0);
+ attr = kMenuItemAttrCustomDraw;
+ SetMenuItemProperty(action->menu, index, kMenuCreatorQt, kMenuPropertyWidgetActionWidget,
+ sizeof(QWidget *), &widget);
+ HIViewRef content;
+ HIMenuGetContentView(action->menu, kThemeMenuTypePullDown, &content);
+
+ EventHandlerRef eventHandlerRef;
+ InstallMenuEventHandler(action->menu, qt_mac_widget_in_menu_eventHandler,
+ GetEventTypeCount(widget_in_menu_events),
+ widget_in_menu_events, 0, &eventHandlerRef);
+ menu_eventHandlers_hash()->insert(action->menu, eventHandlerRef);
+
+ QWidget *menuWidget = 0;
+ GetMenuItemProperty(action->menu, 0, kMenuCreatorQt, kMenuPropertyWidgetMenu,
+ sizeof(menuWidget), 0, &menuWidget);
+ if(!menuWidget) {
+ menuWidget = new QMacNativeWidget(content);
+ SetMenuItemProperty(menu, 0, kMenuCreatorQt, kMenuPropertyWidgetMenu,
+ sizeof(menuWidget), &menuWidget);
+ menuWidget->show();
+ }
+ widget->setParent(menuWidget);
+#else
+ QMacNativeWidget *container = new QMacNativeWidget(0);
+ container->resize(widget->sizeHint());
+ widget->setAttribute(Qt::WA_LayoutUsesWidgetRect);
+ widget->setParent(container);
+
+ NSView *containerView = qt_mac_nativeview_for(container);
+ [containerView setAutoresizesSubviews:YES];
+ [containerView setAutoresizingMask:NSViewWidthSizable];
+ [qt_mac_nativeview_for(widget) setAutoresizingMask:NSViewWidthSizable];
+
+ [newItem setView:containerView];
+ container->show();
+#endif
+ widget->show();
+ }
+
+ } else {
+#ifndef QT_MAC_USE_COCOA
+ qt_mac_command_set_enabled(action->menu, action->command, !QApplicationPrivate::modalState());
+#else
+ [newItem setEnabled:!QApplicationPrivate::modalState()];
+#endif
+ }
+#ifndef QT_MAC_USE_COCOA
+ SetMenuCommandProperty(action->menu, action->command, kMenuCreatorQt, kMenuPropertyQAction,
+ sizeof(action), &action);
+#else
+ [newItem setTag:long(static_cast<QAction *>(action->action))];
+#endif
+ syncAction(action);
+}
+
+// return an autoreleased string given a QKeySequence (currently only looks at the first one).
+NSString *keySequenceToKeyEqivalent(const QKeySequence &accel)
+{
+ quint32 accel_key = (accel[0] & ~(Qt::MODIFIER_MASK | Qt::UNICODE_ACCEL));
+ unichar keyEquiv[1] = { 0 };
+ if (accel_key == Qt::Key_Return)
+ keyEquiv[0] = kReturnCharCode;
+ else if (accel_key == Qt::Key_Enter)
+ keyEquiv[0] = kEnterCharCode;
+ else if (accel_key == Qt::Key_Tab)
+ keyEquiv[0] = kTabCharCode;
+ else if (accel_key == Qt::Key_Backspace)
+ keyEquiv[0] = kBackspaceCharCode;
+ else if (accel_key == Qt::Key_Delete)
+ keyEquiv[0] = NSDeleteFunctionKey;
+ else if (accel_key == Qt::Key_Escape)
+ keyEquiv[0] = kEscapeCharCode;
+ else if (accel_key == Qt::Key_PageUp)
+ keyEquiv[0] = NSPageUpFunctionKey;
+ else if (accel_key == Qt::Key_PageDown)
+ keyEquiv[0] = NSPageDownFunctionKey;
+ else if (accel_key == Qt::Key_Up)
+ keyEquiv[0] = NSUpArrowFunctionKey;
+ else if (accel_key == Qt::Key_Down)
+ keyEquiv[0] = NSDownArrowFunctionKey;
+ else if (accel_key == Qt::Key_Left)
+ keyEquiv[0] = NSLeftArrowFunctionKey;
+ else if (accel_key == Qt::Key_Right)
+ keyEquiv[0] = NSRightArrowFunctionKey;
+ else if (accel_key == Qt::Key_CapsLock)
+ keyEquiv[0] = kMenuCapsLockGlyph; // ### Cocoa has no equivalent
+ else if (accel_key >= Qt::Key_F1 && accel_key <= Qt::Key_F15)
+ keyEquiv[0] = (accel_key - Qt::Key_F1) + NSF1FunctionKey;
+ else if (accel_key == Qt::Key_Home)
+ keyEquiv[0] = NSHomeFunctionKey;
+ else if (accel_key == Qt::Key_End)
+ keyEquiv[0] = NSEndFunctionKey;
+ else
+ keyEquiv[0] = unichar(QChar(accel_key).toLower().unicode());
+ return [NSString stringWithCharacters:keyEquiv length:1];
+}
+
+// return the cocoa modifier mask for the QKeySequence (currently only looks at the first one).
+NSUInteger keySequenceModifierMask(const QKeySequence &accel)
+{
+ NSUInteger ret = 0;
+ quint32 accel_key = accel[0];
+ if ((accel_key & Qt::CTRL) == Qt::CTRL)
+ ret |= NSCommandKeyMask;
+ if ((accel_key & Qt::META) == Qt::META)
+ ret |= NSControlKeyMask;
+ if ((accel_key & Qt::ALT) == Qt::ALT)
+ ret |= NSAlternateKeyMask;
+ if ((accel_key & Qt::SHIFT) == Qt::SHIFT)
+ ret |= NSShiftKeyMask;
+ return ret;
+}
+
+void
+QMenuPrivate::QMacMenuPrivate::syncAction(QMacMenuAction *action)
+{
+ if (!action)
+ return;
+
+#ifndef QT_MAC_USE_COCOA
+ const int index = qt_mac_menu_find_action(action->menu, action);
+ if (index == -1)
+ return;
+#else
+ NSMenuItem *item = action->menuItem;
+ if (!item)
+ return;
+#endif
+
+#ifndef QT_MAC_USE_COCOA
+ if (!action->action->isVisible()) {
+ ChangeMenuItemAttributes(action->menu, index, kMenuItemAttrHidden, 0);
+ return;
+ }
+ ChangeMenuItemAttributes(action->menu, index, 0, kMenuItemAttrHidden);
+#else
+ QMacCocoaAutoReleasePool pool;
+ NSMenu *menu = [item menu];
+ bool actionVisible = action->action->isVisible();
+ [item setHidden:!actionVisible];
+ if (!actionVisible)
+ return;
+#endif
+
+#ifndef QT_MAC_USE_COCOA
+ if (action->action->isSeparator()) {
+ ChangeMenuItemAttributes(action->menu, index, kMenuItemAttrSeparator, 0);
+ return;
+ }
+ ChangeMenuItemAttributes(action->menu, index, 0, kMenuItemAttrSeparator);
+#else
+ int itemIndex = [menu indexOfItem:item];
+ Q_ASSERT(itemIndex != -1);
+ if (action->action->isSeparator()) {
+ action->menuItem = [NSMenuItem separatorItem];
+ [action->menuItem retain];
+ [menu insertItem: action->menuItem atIndex:itemIndex];
+ [menu removeItem:item];
+ [item release];
+ item = action->menuItem;
+ return;
+ } else if ([item isSeparatorItem]) {
+ // I'm no longer a separator...
+ action->menuItem = createNSMenuItem(action->action->text());
+ [menu insertItem:action->menuItem atIndex:itemIndex];
+ [menu removeItem:item];
+ [item release];
+ item = action->menuItem;
+ }
+#endif
+
+ //find text (and accel)
+ action->ignore_accel = 0;
+ QString text = action->action->text();
+ QKeySequence accel = action->action->shortcut();
+ {
+ int st = text.lastIndexOf(QLatin1Char('\t'));
+ if (st != -1) {
+ action->ignore_accel = 1;
+ accel = QKeySequence(text.right(text.length()-(st+1)));
+ text.remove(st, text.length()-st);
+ }
+ }
+ {
+ QString cmd_text = qt_mac_menu_merge_text(action);
+ if (!cmd_text.isEmpty()) {
+ text = cmd_text;
+ accel = qt_mac_menu_merge_accel(action);
+ }
+ }
+ if (accel.count() > 1)
+ text += QLatin1String(" (****)"); //just to denote a multi stroke shortcut
+
+ QString finalString = qt_mac_removeMnemonics(text);
+
+#ifndef QT_MAC_USE_COCOA
+ MenuItemDataRec data;
+ memset(&data, '\0', sizeof(data));
+
+ //Carbon text
+ data.whichData |= kMenuItemDataCFString;
+ QCFString cfstring(finalString); // Hold the reference to the end of the function.
+ data.cfText = cfstring;
+
+ // Carbon enabled
+ data.whichData |= kMenuItemDataEnabled;
+ data.enabled = action->action->isEnabled();
+ // Carbon icon
+ data.whichData |= kMenuItemDataIconHandle;
+ if (!action->action->icon().isNull()
+ && action->action->isIconVisibleInMenu()) {
+ data.iconType = kMenuIconRefType;
+ data.iconHandle = (Handle)qt_mac_create_iconref(action->action->icon().pixmap(22, QIcon::Normal));
+ } else {
+ data.iconType = kMenuNoIcon;
+ }
+ if (action->action->font().resolve()) { // Carbon font
+ if (action->action->font().bold())
+ data.style |= bold;
+ if (action->action->font().underline())
+ data.style |= underline;
+ if (action->action->font().italic())
+ data.style |= italic;
+ if (data.style)
+ data.whichData |= kMenuItemDataStyle;
+ data.whichData |= kMenuItemDataFontID;
+ data.fontID = action->action->font().macFontID();
+ }
+#else
+ // Cocoa Font and title
+ if (action->action->font().resolve()) {
+ const QFont &actionFont = action->action->font();
+ NSFont *customMenuFont = [NSFont fontWithName:reinterpret_cast<const NSString *>(static_cast<CFStringRef>(QCFString(actionFont.family())))
+ size:actionFont.pointSize()];
+ NSArray *keys = [NSArray arrayWithObjects:NSFontAttributeName, nil];
+ NSArray *objects = [NSArray arrayWithObjects:customMenuFont, nil];
+ NSDictionary *attributes = [NSDictionary dictionaryWithObjects:objects forKeys:keys];
+ NSAttributedString *str = [[[NSAttributedString alloc] initWithString:reinterpret_cast<const NSString *>(static_cast<CFStringRef>(QCFString(finalString)))
+ attributes:attributes] autorelease];
+ [item setAttributedTitle: str];
+ } else {
+ [item setTitle: reinterpret_cast<const NSString *>(static_cast<CFStringRef>(QCFString(finalString)))];
+ }
+ [item setTitle:reinterpret_cast<const NSString *>(static_cast<CFStringRef>(QCFString(qt_mac_removeMnemonics(text))))];
+
+ // Cocoa Enabled
+ [item setEnabled: action->action->isEnabled()];
+
+ // Cocoa icon
+ NSImage *nsimage = 0;
+ if (!action->action->icon().isNull() && action->action->isIconVisibleInMenu()) {
+ nsimage = static_cast<NSImage *>(qt_mac_create_nsimage(action->action->icon().pixmap(22, QIcon::Normal)));
+ }
+ [item setImage:nsimage];
+ [nsimage release];
+#endif
+
+ if (action->action->menu()) { //submenu
+#ifndef QT_MAC_USE_COCOA
+ data.whichData |= kMenuItemDataSubmenuHandle;
+ data.submenuHandle = action->action->menu()->macMenu();
+ QWidget *caused = 0;
+ GetMenuItemProperty(action->menu, 0, kMenuCreatorQt, kMenuPropertyQWidget, sizeof(caused), 0, &caused);
+ SetMenuItemProperty(data.submenuHandle, 0, kMenuCreatorQt, kMenuPropertyCausedQWidget, sizeof(caused), &caused);
+#else
+ [item setSubmenu:static_cast<NSMenu *>(action->action->menu()->macMenu())];
+#endif
+ } else { //respect some other items
+#ifndef QT_MAC_USE_COCOA
+ //shortcuts (say we are setting them all so that we can also clear them).
+ data.whichData |= kMenuItemDataCmdKey;
+ data.whichData |= kMenuItemDataCmdKeyModifiers;
+ data.whichData |= kMenuItemDataCmdKeyGlyph;
+ if (!accel.isEmpty()) {
+ qt_mac_get_accel(accel[0], (quint32*)&data.cmdKeyModifiers, (quint32*)&data.cmdKeyGlyph);
+ if (data.cmdKeyGlyph == 0)
+ data.cmdKey = (UniChar)accel[0];
+ }
+#else
+ [item setSubmenu:0];
+ if (!accel.isEmpty()) {
+ [item setKeyEquivalent:keySequenceToKeyEqivalent(accel)];
+ [item setKeyEquivalentModifierMask:keySequenceModifierMask(accel)];
+ } else {
+ [item setKeyEquivalent:@""];
+ [item setKeyEquivalentModifierMask:NSCommandKeyMask];
+ }
+#endif
+ }
+#ifndef QT_MAC_USE_COCOA
+ //mark glyph
+ data.whichData |= kMenuItemDataMark;
+ if (action->action->isChecked()) {
+#if 0
+ if (action->action->actionGroup() &&
+ action->action->actionGroup()->isExclusive())
+ data.mark = diamondMark;
+ else
+#endif
+ data.mark = checkMark;
+ } else {
+ data.mark = noMark;
+ }
+
+ //actually set it
+ SetMenuItemData(action->menu, action->command, true, &data);
+
+ // Free up memory
+ if (data.iconHandle)
+ ReleaseIconRef(IconRef(data.iconHandle));
+#else
+ //mark glyph
+ [item setState:action->action->isChecked() ? NSOnState : NSOffState];
+#endif
+}
+
+void
+QMenuPrivate::QMacMenuPrivate::removeAction(QMacMenuAction *action)
+{
+ if (!action)
+ return;
+#ifndef QT_MAC_USE_COCOA
+ if (action->command == kHICommandQuit || action->command == kHICommandPreferences)
+ qt_mac_command_set_enabled(action->menu, action->command, false);
+ else
+ DeleteMenuItem(action->menu, qt_mac_menu_find_action(action->menu, action));
+#else
+ QMacCocoaAutoReleasePool pool;
+ QT_MANGLE_NAMESPACE(QCocoaMenuLoader) *loader = getMenuLoader();
+ if (action->menuItem == [loader quitMenuItem] || action->menuItem == [loader preferencesMenuItem])
+ [action->menuItem setEnabled:false];
+ else
+ [[action->menuItem menu] removeItem:action->menuItem];
+#endif
+ actionItems.removeAll(action);
+}
+
+OSMenuRef
+QMenuPrivate::macMenu(OSMenuRef merge)
+{
+ Q_UNUSED(merge);
+ Q_Q(QMenu);
+ if (mac_menu && mac_menu->menu)
+ return mac_menu->menu;
+ if (!mac_menu)
+ mac_menu = new QMacMenuPrivate;
+ mac_menu->menu = qt_mac_create_menu(q);
+ if (merge) {
+#ifndef QT_MAC_USE_COCOA
+ SetMenuItemProperty(mac_menu->menu, 0, kMenuCreatorQt, kMenuPropertyMergeMenu, sizeof(merge), &merge);
+#else
+ mergeMenuHash.insert(mac_menu->menu, merge);
+#endif
+ }
+ QList<QAction*> items = q->actions();
+ for(int i = 0; i < items.count(); i++)
+ mac_menu->addAction(items[i], 0, this);
+ return mac_menu->menu;
+}
+
+/*!
+ \internal
+*/
+void QMenuPrivate::setMacMenuEnabled(bool enable)
+{
+ if (!macMenu(0))
+ return;
+
+ QMacCocoaAutoReleasePool pool;
+ if (enable) {
+ for (int i = 0; i < mac_menu->actionItems.count(); ++i) {
+ QMacMenuAction *menuItem = mac_menu->actionItems.at(i);
+ if (menuItem && menuItem->action && menuItem->action->isEnabled()) {
+#ifndef QT_MAC_USE_COCOA
+ // Only enable those items which contains an enabled QAction.
+ // i == 0 -> the menu itself, hence i + 1 for items.
+ EnableMenuItem(mac_menu->menu, i + 1);
+#else
+ [menuItem->menuItem setEnabled:true];
+#endif
+ }
+ }
+ } else {
+#ifndef QT_MAC_USE_COCOA
+ DisableAllMenuItems(mac_menu->menu);
+#else
+ NSMenu *menu = mac_menu->menu;
+ for (NSMenuItem *item in [menu itemArray]) {
+ [item setEnabled:false];
+ }
+#endif
+ }
+}
+
+/*!
+ \internal
+
+ This function will return the OSMenuRef used to create the native menu bar
+ bindings.
+
+ If Qt is built against Carbon, the OSMenuRef is a MenuRef that can be used
+ with Carbon's Menu Manager API.
+
+ If Qt is built against Cocoa, the OSMenuRef is a NSMenu pointer.
+
+ \warning This function is not portable.
+
+ \sa QMenuBar::macMenu()
+*/
+OSMenuRef QMenu::macMenu(OSMenuRef merge) { return d_func()->macMenu(merge); }
+
+/*****************************************************************************
+ QMenuBar bindings
+ *****************************************************************************/
+typedef QHash<QWidget *, QMenuBar *> MenuBarHash;
+Q_GLOBAL_STATIC(MenuBarHash, menubars)
+static QMenuBar *fallback = 0;
+QMenuBarPrivate::QMacMenuBarPrivate::QMacMenuBarPrivate() : menu(0), apple_menu(0)
+{
+}
+
+QMenuBarPrivate::QMacMenuBarPrivate::~QMacMenuBarPrivate()
+{
+ for(QList<QMacMenuAction*>::Iterator it = actionItems.begin(); it != actionItems.end(); ++it)
+ delete (*it);
+#ifndef QT_MAC_USE_COCOA
+ if (apple_menu) {
+ QMenuMergeList *list = 0;
+ GetMenuItemProperty(apple_menu, 0, kMenuCreatorQt, kMenuPropertyMergeList,
+ sizeof(list), 0, &list);
+ if (list) {
+ RemoveMenuItemProperty(apple_menu, 0, kMenuCreatorQt, kMenuPropertyMergeList);
+ delete list;
+ }
+ ReleaseMenu(apple_menu);
+ }
+ if (menu)
+ ReleaseMenu(menu);
+#else
+ [apple_menu release];
+ [menu release];
+#endif
+}
+
+void
+QMenuBarPrivate::QMacMenuBarPrivate::addAction(QAction *a, QMacMenuAction *before)
+{
+ if (a->isSeparator() || !menu)
+ return;
+ QMacMenuAction *action = new QMacMenuAction;
+ action->action = a;
+ action->ignore_accel = 1;
+#ifndef QT_MAC_USE_COCOA
+ action->command = qt_mac_menu_static_cmd_id++;
+#endif
+ addAction(action, before);
+}
+
+void
+QMenuBarPrivate::QMacMenuBarPrivate::addAction(QMacMenuAction *action, QMacMenuAction *before)
+{
+ if (!action || !menu)
+ return;
+
+ int before_index = actionItems.indexOf(before);
+ if (before_index < 0) {
+ before = 0;
+ before_index = actionItems.size();
+ }
+ actionItems.insert(before_index, action);
+
+ MenuItemIndex index = actionItems.size()-1;
+
+ action->menu = menu;
+#ifdef QT_MAC_USE_COCOA
+ QMacCocoaAutoReleasePool pool;
+ [action->menu retain];
+ NSMenuItem *newItem = createNSMenuItem(action->action->text());
+ action->menuItem = newItem;
+#endif
+ if (before) {
+#ifndef QT_MAC_USE_COCOA
+ InsertMenuItemTextWithCFString(action->menu, 0, qMax(1, before_index+1), 0, action->command);
+#else
+ [menu insertItem:newItem atIndex:qMax(1, before_index + 1)];
+#endif
+ index = before_index;
+ } else {
+#ifndef QT_MAC_USE_COCOA
+ AppendMenuItemTextWithCFString(action->menu, 0, 0, action->command, &index);
+#else
+ [menu addItem:newItem];
+#endif
+ }
+#ifndef QT_MAC_USE_COCOA
+ SetMenuItemProperty(action->menu, index, kMenuCreatorQt, kMenuPropertyQAction, sizeof(action),
+ &action);
+#else
+ [newItem setTag:long(static_cast<QAction *>(action->action))];
+#endif
+ syncAction(action);
+}
+
+void
+QMenuBarPrivate::QMacMenuBarPrivate::syncAction(QMacMenuAction *action)
+{
+ if (!action || !menu)
+ return;
+#ifndef QT_MAC_USE_COCOA
+ const int index = qt_mac_menu_find_action(action->menu, action);
+#else
+ QMacCocoaAutoReleasePool pool;
+ NSMenuItem *item = action->menuItem;
+#endif
+
+ OSMenuRef submenu = 0;
+ bool release_submenu = false;
+ if (action->action->menu()) {
+ if ((submenu = action->action->menu()->macMenu(apple_menu))) {
+#ifndef QT_MAC_USE_COCOA
+ QWidget *caused = 0;
+ GetMenuItemProperty(action->menu, 0, kMenuCreatorQt, kMenuPropertyQWidget, sizeof(caused), 0, &caused);
+ SetMenuItemProperty(submenu, 0, kMenuCreatorQt, kMenuPropertyCausedQWidget, sizeof(caused), &caused);
+#else
+ [item setSubmenu:submenu];
+#endif
+ }
+#ifndef QT_MAC_USE_COCOA
+ } else { // create a submenu to act as menu
+ release_submenu = true;
+ CreateNewMenu(0, 0, &submenu);
+#endif
+ }
+
+ if (submenu) {
+ bool visible = actualMenuItemVisibility(this, action);
+#ifndef QT_MAC_USE_COCOA
+ SetMenuItemHierarchicalMenu(action->menu, index, submenu);
+ SetMenuTitleWithCFString(submenu, QCFString(qt_mac_removeMnemonics(action->action->text())));
+ if (visible)
+ ChangeMenuAttributes(submenu, 0, kMenuAttrHidden);
+ else
+ ChangeMenuAttributes(submenu, kMenuAttrHidden, 0);
+#else
+ [item setSubmenu: submenu];
+ [submenu setTitle:reinterpret_cast<const NSString *>(static_cast<CFStringRef>(QCFString(qt_mac_removeMnemonics(action->action->text()))))];
+ syncNSMenuItemVisiblity(item, visible);
+#endif
+ if (release_submenu) { //no pointers to it
+#ifndef QT_MAC_USE_COCOA
+ ReleaseMenu(submenu);
+#else
+ [submenu release];
+#endif
+ }
+ } else {
+ qWarning("QMenu: No OSMenuRef created for popup menu");
+ }
+}
+
+void
+QMenuBarPrivate::QMacMenuBarPrivate::removeAction(QMacMenuAction *action)
+{
+ if (!action || !menu)
+ return;
+#ifndef QT_MAC_USE_COCOA
+ DeleteMenuItem(action->menu, qt_mac_menu_find_action(action->menu, action));
+#else
+ QMacCocoaAutoReleasePool pool;
+ [action->menu removeItem:action->menuItem];
+#endif
+ actionItems.removeAll(action);
+}
+
+void
+QMenuBarPrivate::macCreateMenuBar(QWidget *parent)
+{
+ Q_Q(QMenuBar);
+ static int checkEnv = -1;
+ if (qt_mac_no_native_menubar == false && checkEnv < 0) {
+ checkEnv = !qgetenv("QT_MAC_NO_NATIVE_MENUBAR").isEmpty();
+ qt_mac_no_native_menubar = checkEnv;
+ }
+ if (!qt_mac_no_native_menubar) {
+ extern void qt_event_request_menubarupdate(); //qapplication_mac.cpp
+ qt_event_request_menubarupdate();
+ if (!parent && !fallback) {
+ fallback = q;
+ mac_menubar = new QMacMenuBarPrivate;
+ } else if (parent && parent->isWindow()) {
+ menubars()->insert(q->window(), q);
+ mac_menubar = new QMacMenuBarPrivate;
+ }
+ }
+}
+
+void QMenuBarPrivate::macDestroyMenuBar()
+{
+ Q_Q(QMenuBar);
+ QMacCocoaAutoReleasePool pool;
+ if (fallback == q)
+ fallback = 0;
+ delete mac_menubar;
+ QWidget *tlw = q->window();
+ menubars()->remove(tlw);
+ mac_menubar = 0;
+
+ if (qt_mac_current_menubar.qmenubar == q) {
+ extern void qt_event_request_menubarupdate(); //qapplication_mac.cpp
+ qt_event_request_menubarupdate();
+ }
+}
+
+OSMenuRef QMenuBarPrivate::macMenu()
+{
+ Q_Q(QMenuBar);
+ if (!mac_menubar) {
+ return 0;
+ } else if (!mac_menubar->menu) {
+ mac_menubar->menu = qt_mac_create_menu(q);
+ ProcessSerialNumber mine, front;
+ if (GetCurrentProcess(&mine) == noErr && GetFrontProcess(&front) == noErr) {
+ if (!qt_mac_no_menubar_merge && !mac_menubar->apple_menu) {
+ mac_menubar->apple_menu = qt_mac_create_menu(q);
+#ifndef QT_MAC_USE_COCOA
+ MenuItemIndex index;
+ AppendMenuItemTextWithCFString(mac_menubar->menu, 0, 0, 0, &index);
+
+ SetMenuTitleWithCFString(mac_menubar->apple_menu, QCFString(QString(QChar(0x14))));
+ SetMenuItemHierarchicalMenu(mac_menubar->menu, index, mac_menubar->apple_menu);
+ SetMenuItemProperty(mac_menubar->apple_menu, 0, kMenuCreatorQt, kMenuPropertyQWidget, sizeof(q), &q);
+#else
+ [mac_menubar->apple_menu setTitle:reinterpret_cast<const NSString *>(static_cast<CFStringRef>(QCFString(QString(QChar(0x14)))))];
+ NSMenuItem *apple_menuItem = [[NSMenuItem alloc] init];
+ [apple_menuItem setSubmenu:mac_menubar->menu];
+ [mac_menubar->apple_menu addItem:apple_menuItem];
+ [apple_menuItem release];
+#endif
+ }
+ if (mac_menubar->apple_menu) {
+#ifndef QT_MAC_USE_COCOA
+ SetMenuItemProperty(mac_menubar->menu, 0, kMenuCreatorQt, kMenuPropertyMergeMenu,
+ sizeof(mac_menubar->apple_menu), &mac_menubar->apple_menu);
+#else
+ QMenuPrivate::mergeMenuHash.insert(mac_menubar->menu, mac_menubar->apple_menu);
+#endif
+ }
+ QList<QAction*> items = q->actions();
+ for(int i = 0; i < items.count(); i++)
+ mac_menubar->addAction(items[i]);
+ }
+ }
+ return mac_menubar->menu;
+}
+
+/*!
+ \internal
+
+ This function will return the OSMenuRef used to create the native menu bar
+ bindings. This OSMenuRef is then set as the root menu for the Menu
+ Manager.
+
+ \warning This function is not portable.
+
+ \sa QMenu::macMenu()
+*/
+OSMenuRef QMenuBar::macMenu() { return d_func()->macMenu(); }
+
+/* !
+ \internal
+ Ancestor function that crosses windows (QWidget::isAncestorOf
+ only considers widgets within the same window).
+*/
+static bool qt_mac_is_ancestor(QWidget* possibleAncestor, QWidget *child)
+{
+ QWidget * current = child->parentWidget();
+ while (current != 0) {
+ if (current == possibleAncestor)
+ return true;
+ current = current->parentWidget();
+ }
+ return false;
+}
+
+/* !
+ \internal
+ Returns true if the entries of menuBar should be disabled,
+ based on the modality type of modalWidget.
+*/
+static bool qt_mac_should_disable_menu(QMenuBar *menuBar, QWidget *modalWidget)
+{
+ if (modalWidget == 0 || menuBar == 0)
+ return false;
+ const Qt::WindowModality modality = modalWidget->windowModality();
+ if (modality == Qt::ApplicationModal) {
+ return true;
+ } else if (modality == Qt::WindowModal) {
+ QWidget * parent = menuBar->parentWidget();
+
+ // Special case for the global menu bar: It's not associated
+ // with a window so don't disable it.
+ if (parent == 0)
+ return false;
+
+ // Disable menu entries in menu bars that belong to ancestors of
+ // the modal widget, leave entries in unrelated menu bars enabled.
+ return qt_mac_is_ancestor(parent, modalWidget);
+ }
+ return false; // modality == NonModal
+}
+
+static void cancelAllMenuTracking()
+{
+#ifdef QT_MAC_USE_COCOA
+ QMacCocoaAutoReleasePool pool;
+ NSMenu *mainMenu = [NSApp mainMenu];
+ [mainMenu cancelTracking];
+ for (NSMenuItem *item in [mainMenu itemArray]) {
+ if ([item submenu]) {
+ [[item submenu] cancelTracking];
+ }
+ }
+#endif
+}
+
+/*!
+ \internal
+
+ This function will update the current menu bar and set it as the
+ active menu bar in the Menu Manager.
+
+ \warning This function is not portable.
+
+ \sa QMenu::macMenu(), QMenuBar::macMenu()
+*/
+bool QMenuBar::macUpdateMenuBar()
+{
+ if (qt_mac_no_native_menubar) //nothing to be done..
+ return true;
+
+ cancelAllMenuTracking();
+ QMenuBar *mb = 0;
+ //find a menu bar
+ QWidget *w = qApp->activeWindow();
+
+ if (!w) {
+ QWidgetList tlws = QApplication::topLevelWidgets();
+ for(int i = 0; i < tlws.size(); ++i) {
+ QWidget *tlw = tlws.at(i);
+ if ((tlw->isVisible() && tlw->windowType() != Qt::Tool &&
+ tlw->windowType() != Qt::Popup)) {
+ w = tlw;
+ break;
+ }
+ }
+ }
+ if (w) {
+ mb = menubars()->value(w);
+#ifndef QT_NO_MAINWINDOW
+ QDockWidget *dw = qobject_cast<QDockWidget *>(w);
+ if (!mb && dw) {
+ QMainWindow *mw = qobject_cast<QMainWindow *>(dw->parentWidget());
+ if (mw && (mb = menubars()->value(mw)))
+ w = mw;
+ }
+#endif
+ while(w && !mb)
+ mb = menubars()->value((w = w->parentWidget()));
+ }
+ if (!mb)
+ mb = fallback;
+ //now set it
+ bool ret = false;
+ if (mb) {
+#ifdef QT_MAC_USE_COCOA
+ QMacCocoaAutoReleasePool pool;
+#endif
+ if (OSMenuRef menu = mb->macMenu()) {
+#ifndef QT_MAC_USE_COCOA
+ SetRootMenu(menu);
+#else
+ QT_MANGLE_NAMESPACE(QCocoaMenuLoader) *loader = getMenuLoader();
+ [loader ensureAppMenuInMenu:menu];
+ [NSApp setMainMenu:menu];
+ syncMenuBarItemsVisiblity(mb->d_func()->mac_menubar);
+#endif
+ QWidget *modalWidget = qApp->activeModalWidget();
+ if (mb != menubars()->value(modalWidget)) {
+ qt_mac_set_modal_state(menu, qt_mac_should_disable_menu(mb, modalWidget));
+ }
+ }
+ qt_mac_current_menubar.qmenubar = mb;
+ qt_mac_current_menubar.modal = QApplicationPrivate::modalState();
+ ret = true;
+ } else if (qt_mac_current_menubar.qmenubar) {
+ const bool modal = QApplicationPrivate::modalState();
+ if (modal != qt_mac_current_menubar.modal) {
+ ret = true;
+ if (OSMenuRef menu = qt_mac_current_menubar.qmenubar->macMenu()) {
+#ifndef QT_MAC_USE_COCOA
+ SetRootMenu(menu);
+#else
+ QT_MANGLE_NAMESPACE(QCocoaMenuLoader) *loader = getMenuLoader();
+ [loader ensureAppMenuInMenu:menu];
+ [NSApp setMainMenu:menu];
+ syncMenuBarItemsVisiblity(qt_mac_current_menubar.qmenubar->d_func()->mac_menubar);
+#endif
+ QWidget *modalWidget = qApp->activeModalWidget();
+ if (qt_mac_current_menubar.qmenubar != menubars()->value(modalWidget)) {
+ qt_mac_set_modal_state(menu, qt_mac_should_disable_menu(mb, modalWidget));
+ }
+ }
+ qt_mac_current_menubar.modal = modal;
+ }
+ }
+ if(!ret)
+ qt_mac_clear_menubar();
+ return ret;
+}
+
+QHash<OSMenuRef, OSMenuRef> QMenuPrivate::mergeMenuHash;
+QHash<OSMenuRef, QMenuMergeList*> QMenuPrivate::mergeMenuItemsHash;
+
+bool QMenuPrivate::QMacMenuPrivate::merged(const QAction *action) const
+{
+#ifndef QT_MAC_USE_COCOA
+ MenuRef merge = 0;
+ GetMenuItemProperty(menu, 0, kMenuCreatorQt, kMenuPropertyMergeMenu,
+ sizeof(merge), 0, &merge);
+ if (merge) {
+ QMenuMergeList *list = 0;
+ if (GetMenuItemProperty(merge, 0, kMenuCreatorQt, kMenuPropertyMergeList,
+ sizeof(list), 0, &list) == noErr && list) {
+ for(int i = 0; i < list->size(); ++i) {
+ QMenuMergeItem item = list->at(i);
+ if (item.action->action == action)
+ return true;
+ }
+ }
+ }
+#else
+ if (OSMenuRef merge = mergeMenuHash.value(menu)) {
+ if (QMenuMergeList *list = mergeMenuItemsHash.value(merge)) {
+ for(int i = 0; i < list->size(); ++i) {
+ const QMenuMergeItem &item = list->at(i);
+ if (item.action->action == action)
+ return true;
+ }
+ }
+ }
+#endif
+ return false;
+}
+
+//creation of the OSMenuRef
+static OSMenuRef qt_mac_create_menu(QWidget *w)
+{
+ OSMenuRef ret;
+#ifndef QT_MAC_USE_COCOA
+ ret = 0;
+ if (CreateNewMenu(0, 0, &ret) == noErr) {
+ qt_mac_create_menu_event_handler();
+ SetMenuItemProperty(ret, 0, kMenuCreatorQt, kMenuPropertyQWidget, sizeof(w), &w);
+
+ // kEventMenuMatchKey is only sent to the menu itself and not to
+ // the application, install a separate handler for that event.
+ EventHandlerRef eventHandlerRef;
+ InstallMenuEventHandler(ret, qt_mac_menu_event,
+ GetEventTypeCount(menu_menu_events),
+ menu_menu_events, 0, &eventHandlerRef);
+ menu_eventHandlers_hash()->insert(ret, eventHandlerRef);
+ } else {
+ qWarning("QMenu: Internal error");
+ }
+#else
+ if (QMenu *qmenu = qobject_cast<QMenu *>(w)){
+ ret = [[QT_MANGLE_NAMESPACE(QCocoaMenu) alloc] initWithQMenu:qmenu];
+ } else {
+ ret = [[NSMenu alloc] init];
+ }
+#endif
+ return ret;
+}
+
+
+
+QT_END_NAMESPACE
diff --git a/src/gui/widgets/qmenu_p.h b/src/gui/widgets/qmenu_p.h
new file mode 100644
index 0000000000..e3c489024a
--- /dev/null
+++ b/src/gui/widgets/qmenu_p.h
@@ -0,0 +1,329 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QMENU_P_H
+#define QMENU_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 "QtGui/qmenubar.h"
+#include "QtGui/qstyleoption.h"
+#include "QtCore/qdatetime.h"
+#include "QtCore/qmap.h"
+#include "QtCore/qhash.h"
+#include "QtCore/qbasictimer.h"
+#include "private/qwidget_p.h"
+
+QT_BEGIN_NAMESPACE
+
+#ifndef QT_NO_MENU
+
+class QTornOffMenu;
+class QEventLoop;
+
+#ifdef Q_WS_MAC
+# ifdef __OBJC__
+QT_END_NAMESPACE
+@class NSMenuItem;
+QT_BEGIN_NAMESPACE
+# else
+typedef void NSMenuItem;
+# endif //__OBJC__
+struct QMacMenuAction {
+ QMacMenuAction()
+#ifndef QT_MAC_USE_COCOA
+ : command(0)
+#else
+ : menuItem(0)
+#endif
+ , ignore_accel(0), merged(0), menu(0)
+ {
+ }
+ ~QMacMenuAction();
+#ifndef QT_MAC_USE_COCOA
+ uint command;
+#else
+ NSMenuItem *menuItem;
+#endif
+ uchar ignore_accel : 1;
+ uchar merged : 1;
+ QPointer<QAction> action;
+ OSMenuRef menu;
+};
+
+struct QMenuMergeItem
+{
+#ifndef QT_MAC_USE_COCOA
+ inline QMenuMergeItem(MenuCommand c, QMacMenuAction *a) : command(c), action(a) { }
+ MenuCommand command;
+#else
+ inline QMenuMergeItem(NSMenuItem *c, QMacMenuAction *a) : menuItem(c), action(a) { }
+ NSMenuItem *menuItem;
+#endif
+ QMacMenuAction *action;
+};
+typedef QList<QMenuMergeItem> QMenuMergeList;
+#endif
+
+#ifdef Q_OS_WINCE
+struct QWceMenuAction {
+ uint command;
+ QPointer<QAction> action;
+ HMENU menuHandle;
+ QWceMenuAction() : menuHandle(0), command(0) {}
+};
+#endif
+
+class QMenuPrivate : public QWidgetPrivate
+{
+ Q_DECLARE_PUBLIC(QMenu)
+public:
+ QMenuPrivate() : itemsDirty(0), maxIconWidth(0), tabWidth(0), ncols(0),
+ collapsibleSeparators(true), hasHadMouse(0), aboutToHide(0), motions(0),
+ currentAction(0), scroll(0), eventLoop(0), tearoff(0), tornoff(0), tearoffHighlighted(0),
+ hasCheckableItems(0), sloppyAction(0)
+#ifdef Q_WS_MAC
+ ,mac_menu(0)
+#endif
+#if defined(Q_OS_WINCE) && !defined(QT_NO_MENUBAR)
+ ,wce_menu(0)
+#endif
+#ifdef QT3_SUPPORT
+ ,emitHighlighted(false)
+#endif
+ { }
+ ~QMenuPrivate()
+ {
+ delete scroll;
+#ifdef Q_WS_MAC
+ delete mac_menu;
+#endif
+#if defined(Q_OS_WINCE) && !defined(QT_NO_MENUBAR)
+ delete wce_menu;
+#endif
+ }
+ void init();
+
+ //item calculations
+ mutable uint itemsDirty : 1;
+ mutable uint maxIconWidth, tabWidth;
+ QRect actionRect(QAction *) const;
+ mutable QMap<QAction*, QRect> actionRects;
+ mutable QList<QAction*> actionList;
+ mutable QHash<QAction *, QWidget *> widgetItems;
+ void calcActionRects(QMap<QAction*, QRect> &actionRects, QList<QAction*> &actionList) const;
+ void updateActions();
+ QRect popupGeometry(int screen=-1) const;
+ QList<QAction *> filterActions(const QList<QAction *> &actions) const;
+ uint ncols : 4; //4 bits is probably plenty
+ uint collapsibleSeparators : 1;
+
+ uint activationRecursionGuard : 1;
+
+ //selection
+ static QPointer<QMenu> mouseDown;
+ QPoint mousePopupPos;
+ uint hasHadMouse : 1;
+ uint aboutToHide : 1;
+ int motions;
+ QAction *currentAction;
+ static QBasicTimer menuDelayTimer;
+ enum SelectionReason {
+ SelectedFromKeyboard,
+ SelectedFromElsewhere
+ };
+ QAction *actionAt(QPoint p) const;
+ void setFirstActionActive();
+ void setCurrentAction(QAction *, int popup = -1, SelectionReason reason = SelectedFromElsewhere, bool activateFirst = false);
+ void popupAction(QAction *, int, bool);
+ void setSyncAction();
+
+ //scrolling support
+ struct QMenuScroller {
+ enum ScrollLocation { ScrollStay, ScrollBottom, ScrollTop, ScrollCenter };
+ enum ScrollDirection { ScrollNone=0, ScrollUp=0x01, ScrollDown=0x02 };
+ uint scrollFlags : 2, scrollDirection : 2;
+ int scrollOffset;
+ QBasicTimer *scrollTimer;
+
+ QMenuScroller() : scrollFlags(ScrollNone), scrollDirection(ScrollNone), scrollOffset(0), scrollTimer(0) { }
+ ~QMenuScroller() { delete scrollTimer; }
+ } *scroll;
+ void scrollMenu(QMenuScroller::ScrollLocation location, bool active=false);
+ void scrollMenu(QMenuScroller::ScrollDirection direction, bool page=false, bool active=false);
+ void scrollMenu(QAction *action, QMenuScroller::ScrollLocation location, bool active=false);
+
+ //synchronous operation (ie exec())
+ QEventLoop *eventLoop;
+ QPointer<QAction> syncAction;
+
+ //search buffer
+ QString searchBuffer;
+ QBasicTimer searchBufferTimer;
+
+ //passing of mouse events up the parent heirarchy
+ QPointer<QMenu> activeMenu;
+ bool mouseEventTaken(QMouseEvent *);
+
+ //used to walk up the popup list
+ struct QMenuCaused {
+ QPointer<QWidget> widget;
+ QPointer<QAction> action;
+ };
+ virtual QList<QPointer<QWidget> > calcCausedStack() const;
+ QMenuCaused causedPopup;
+ void hideUpToMenuBar();
+ void hideMenu(QMenu *menu);
+
+ //index mappings
+ inline QAction *actionAt(int i) const { return q_func()->actions().at(i); }
+ inline int indexOf(QAction *act) const { return q_func()->actions().indexOf(act); }
+
+ //tear off support
+ uint tearoff : 1, tornoff : 1, tearoffHighlighted : 1;
+ QPointer<QTornOffMenu> tornPopup;
+
+ mutable bool hasCheckableItems;
+
+ //sloppy selection
+ static QBasicTimer sloppyDelayTimer;
+ QAction *sloppyAction;
+ QRegion sloppyRegion;
+
+ //default action
+ QPointer<QAction> defaultAction;
+
+ QAction *menuAction;
+ QAction *defaultMenuAction;
+
+ void setOverrideMenuAction(QAction *);
+ void _q_overrideMenuActionDestroyed();
+
+ //firing of events
+ void activateAction(QAction *, QAction::ActionEvent, bool self=true);
+ void activateCausedStack(const QList<QPointer<QWidget> > &, QAction *, QAction::ActionEvent, bool);
+
+ void _q_actionTriggered();
+ void _q_actionHovered();
+
+ bool hasMouseMoved(const QPoint &globalPos);
+
+ //menu fading/scrolling effects
+ bool doChildEffects;
+
+#ifdef Q_WS_MAC
+ //mac menu binding
+ struct QMacMenuPrivate {
+ QList<QMacMenuAction*> actionItems;
+ OSMenuRef menu;
+ QMacMenuPrivate();
+ ~QMacMenuPrivate();
+
+ bool merged(const QAction *action) const;
+ void addAction(QAction *, QMacMenuAction* =0, QMenuPrivate *qmenu = 0);
+ void addAction(QMacMenuAction *, QMacMenuAction* =0, QMenuPrivate *qmenu = 0);
+ void syncAction(QMacMenuAction *);
+ inline void syncAction(QAction *a) { syncAction(findAction(a)); }
+ void removeAction(QMacMenuAction *);
+ inline void removeAction(QAction *a) { removeAction(findAction(a)); }
+ inline QMacMenuAction *findAction(QAction *a) {
+ for(int i = 0; i < actionItems.size(); i++) {
+ QMacMenuAction *act = actionItems[i];
+ if(a == act->action)
+ return act;
+ }
+ return 0;
+ }
+ } *mac_menu;
+ OSMenuRef macMenu(OSMenuRef merge);
+ void setMacMenuEnabled(bool enable = true);
+ static QHash<OSMenuRef, OSMenuRef> mergeMenuHash;
+ static QHash<OSMenuRef, QMenuMergeList*> mergeMenuItemsHash;
+#endif
+
+ QPointer<QAction> actionAboutToTrigger;
+#ifdef QT3_SUPPORT
+ bool emitHighlighted;
+#endif
+
+#if defined(Q_OS_WINCE) && !defined(QT_NO_MENUBAR)
+ struct QWceMenuPrivate {
+ QList<QWceMenuAction*> actionItems;
+ HMENU menuHandle;
+ QWceMenuPrivate();
+ ~QWceMenuPrivate();
+ void addAction(QAction *, QWceMenuAction* =0);
+ void addAction(QWceMenuAction *, QWceMenuAction* =0);
+ void syncAction(QWceMenuAction *);
+ inline void syncAction(QAction *a) { syncAction(findAction(a)); }
+ void removeAction(QWceMenuAction *);
+ void rebuild(bool reCreate = false);
+ inline void removeAction(QAction *a) { removeAction(findAction(a)); }
+ inline QWceMenuAction *findAction(QAction *a) {
+ for(int i = 0; i < actionItems.size(); i++) {
+ QWceMenuAction *act = actionItems[i];
+ if(a == act->action)
+ return act;
+ }
+ return 0;
+ }
+ } *wce_menu;
+ HMENU wceMenu(bool create = false);
+ QAction* wceCommands(uint command);
+#endif
+
+ QPointer<QWidget> noReplayFor;
+};
+
+#endif // QT_NO_MENU
+
+QT_END_NAMESPACE
+
+#endif // QMENU_P_H
diff --git a/src/gui/widgets/qmenu_wince.cpp b/src/gui/widgets/qmenu_wince.cpp
new file mode 100644
index 0000000000..ea58d468ed
--- /dev/null
+++ b/src/gui/widgets/qmenu_wince.cpp
@@ -0,0 +1,608 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//Native menubars are only supported for Windows Mobile not the standard SDK/generic WinCE
+#ifdef Q_OS_WINCE
+#include "qmenu.h"
+#include "qt_windows.h"
+#include "qapplication.h"
+#include "qmainwindow.h"
+#include "qtoolbar.h"
+#include "qevent.h"
+#include "qstyle.h"
+#include "qdebug.h"
+#include "qwidgetaction.h"
+#include <private/qapplication_p.h>
+#include <private/qmenu_p.h>
+#include <private/qmenubar_p.h>
+
+#include "qmenu_wince_resource_p.h"
+
+#include <QtCore/qlibrary.h>
+#include <commctrl.h>
+
+#include "qguifunctions_wince.h"
+
+#ifndef QT_NO_MENUBAR
+
+#ifndef SHCMBF_EMPTYBAR
+#define SHCMBF_EMPTYBAR 0x0001
+#endif
+
+#ifndef SHCMBM_GETSUBMENU
+#define SHCMBM_GETSUBMENU (WM_USER + 401)
+#endif
+
+extern bool qt_wince_is_smartphone();//defined in qguifunctions_wce.cpp
+extern bool qt_wince_is_pocket_pc(); //defined in qguifunctions_wce.cpp
+
+QT_BEGIN_NAMESPACE
+
+static uint qt_wce_menu_static_cmd_id = 200;
+static QList<QMenuBar*> nativeMenuBars;
+
+struct qt_SHMENUBARINFO
+{
+ DWORD cbSize;
+ HWND hwndParent;
+ DWORD dwFlags;
+ UINT nToolBarId;
+ HINSTANCE hInstRes;
+ int nBmpId;
+ int cBmpImages;
+ HWND hwndMB;
+ COLORREF clrBk;
+};
+
+typedef int (WINAPI *superfunc)(int, int);
+typedef BOOL (WINAPI *AygCreateMenuBar)(qt_SHMENUBARINFO*);
+typedef HRESULT (WINAPI *AygEnableSoftKey)(HWND,UINT,BOOL,BOOL);
+
+static bool aygResolved = false;
+static AygCreateMenuBar ptrCreateMenuBar = 0;
+static AygEnableSoftKey ptrEnableSoftKey = 0;
+
+static void resolveAygLibs()
+{
+ if (!aygResolved) {
+ aygResolved = true;
+ QLibrary aygLib(QLatin1String("aygshell"));
+ if (!aygLib.load())
+ return;
+ ptrCreateMenuBar = (AygCreateMenuBar) aygLib.resolve("SHCreateMenuBar");
+ ptrEnableSoftKey = (AygEnableSoftKey) aygLib.resolve("SHEnableSoftkey");
+ }
+}
+
+static void qt_wce_enable_soft_key(HWND handle, uint command)
+{
+ resolveAygLibs();
+ if (ptrEnableSoftKey)
+ ptrEnableSoftKey(handle, command, false, true);
+}
+static void qt_wce_disable_soft_key(HWND handle, uint command)
+{
+ resolveAygLibs();
+ if (ptrEnableSoftKey)
+ ptrEnableSoftKey(handle, command, false, false);
+}
+
+static void qt_wce_delete_action_list(QList<QWceMenuAction*> *list) {
+ for(QList<QWceMenuAction*>::Iterator it = list->begin(); it != list->end(); ++it) {
+ QWceMenuAction *action = (*it);
+ delete action;
+ action = 0;
+ }
+ list->clear();
+}
+
+//search for first QuitRole in QMenuBar
+static QAction* qt_wce_get_quit_action(QList<QAction *> actionItems) {
+ QAction *returnAction = 0;
+ for (int i = 0; i < actionItems.size(); ++i) {
+ QAction *action = actionItems.at(i);
+ if (action->menuRole() == QAction::QuitRole)
+ returnAction = action;
+ else
+ if (action->menu())
+ returnAction = qt_wce_get_quit_action(action->menu()->actions());
+ if (returnAction)
+ return returnAction; //return first action found
+ }
+ return 0; //nothing found;
+}
+
+static QAction* qt_wce_get_quit_action(QList<QWceMenuAction*> actionItems) {
+ for (int i = 0; i < actionItems.size(); ++i) {
+ if (actionItems.at(i)->action->menuRole() == QAction::QuitRole)
+ return actionItems.at(i)->action;
+ else if (actionItems.at(i)->action->menu()) {
+ QAction *returnAction = qt_wce_get_quit_action(actionItems.at(i)->action->menu()->actions());
+ if (returnAction)
+ return returnAction;
+ }
+ }
+ return 0;
+}
+
+static HMODULE qt_wce_get_module_handle() {
+ HMODULE module = 0; //handle to resources
+ if (!(module = GetModuleHandle(L"QtGui4"))) //release dynamic
+ if (!(module = GetModuleHandle(L"QtGuid4"))) //debug dynamic
+ module = (HINSTANCE)qWinAppInst(); //static
+ Q_ASSERT_X(module, "qt_wce_get_module_handle()", "cannot get handle to module?");
+ return module;
+}
+
+static void qt_wce_change_command(HWND menuHandle, int item, int command) {
+TBBUTTONINFOA tbbi;
+ memset(&tbbi,0,sizeof(tbbi));
+ tbbi.cbSize = sizeof(tbbi);
+ tbbi.dwMask = TBIF_COMMAND;
+ tbbi.idCommand = command;
+ SendMessage(menuHandle, TB_SETBUTTONINFO, item, (LPARAM)&tbbi);
+}
+
+static void qt_wce_rename_menu_item(HWND menuHandle, int item, const QString &newText) {
+ TBBUTTONINFOA tbbi;
+ memset(&tbbi,0,sizeof(tbbi));
+ tbbi.cbSize = sizeof(tbbi);
+ tbbi.dwMask = TBIF_TEXT;
+ QString text = newText;
+ text.remove(QChar::fromLatin1('&'));
+ tbbi.pszText = (LPSTR) text.utf16();
+ SendMessage(menuHandle, TB_SETBUTTONINFO, item, (LPARAM)&tbbi);
+}
+
+static HWND qt_wce_create_menubar(HWND parentHandle, HINSTANCE resourceHandle, int toolbarID, int flags = 0) {
+ resolveAygLibs();
+
+ if (ptrCreateMenuBar) {
+ qt_SHMENUBARINFO mbi;
+ memset(&mbi, 0, sizeof(qt_SHMENUBARINFO));
+ mbi.cbSize = sizeof(qt_SHMENUBARINFO);
+ mbi.hwndParent = parentHandle;
+ mbi.hInstRes = resourceHandle;
+ mbi.dwFlags = flags;
+ mbi.nToolBarId = toolbarID;
+
+ if (ptrCreateMenuBar(&mbi))
+ return mbi.hwndMB;
+ }
+ return 0;
+}
+
+static void qt_wce_insert_action(HMENU menu, QWceMenuAction *action, bool created) {
+
+ Q_ASSERT_X(menu, "AppendMenu", "menu is 0");
+ if (action->action->isVisible()) {
+ int flags;
+ action->action->isEnabled() ? flags = MF_ENABLED : flags = MF_GRAYED;
+
+ QString text = action->action->iconText();
+ text.remove(QChar::fromLatin1('&'));
+ if (action->action->isSeparator()) {
+ AppendMenu (menu, MF_SEPARATOR , 0, 0);
+ }
+ else if (action->action->menu()) {
+ text.remove(QChar::fromLatin1('&'));
+ AppendMenu (menu, MF_STRING | flags | MF_POPUP,
+ (UINT) action->action->menu()->wceMenu(created), reinterpret_cast<const wchar_t *> (text.utf16()));
+ }
+ else {
+ AppendMenu (menu, MF_STRING | flags, action->command, reinterpret_cast<const wchar_t *> (text.utf16()));
+ }
+ if (action->action->isCheckable())
+ if (action->action->isChecked())
+ CheckMenuItem(menu, action->command, MF_BYCOMMAND | MF_CHECKED);
+ else
+ CheckMenuItem(menu, action->command, MF_BYCOMMAND | MF_UNCHECKED);
+ }
+}
+
+/*!
+ \internal
+
+ This function refreshes the native Windows CE menu.
+*/
+
+void QMenuBar::wceRefresh() {
+ for (int i = 0; i < nativeMenuBars.size(); ++i)
+ nativeMenuBars.at(i)->d_func()->wceRefresh();
+}
+
+void QMenuBarPrivate::wceRefresh() {
+ DrawMenuBar(wce_menubar->menubarHandle);
+}
+
+/*!
+ \internal
+
+ This function sends native Windows CE commands to Qt menus.
+*/
+
+QAction* QMenu::wceCommands(uint command) {
+ Q_D(QMenu);
+ return d->wceCommands(command);
+}
+
+/*!
+ \internal
+
+ This function sends native Windows CE commands to Qt menu bars
+ and all their child menus.
+*/
+
+void QMenuBar::wceCommands(uint command, HWND) {
+ for (int i = 0; i < nativeMenuBars.size(); ++i)
+ nativeMenuBars.at(i)->d_func()->wceCommands(command);
+}
+
+bool QMenuBarPrivate::wceEmitSignals(QList<QWceMenuAction*> actions, uint command) {
+ QAction *foundAction = 0;
+ for (int i = 0; i < actions.size(); ++i) {
+ if (foundAction)
+ break;
+ QWceMenuAction *action = actions.at(i);
+ if (action->action->menu()) {
+ foundAction = action->action->menu()->wceCommands(command);
+ }
+ else if (action->command == command) {
+ emit q_func()->triggered(action->action);
+ action->action->activate(QAction::Trigger);
+ return true;
+ }
+ }
+ if (foundAction) {
+ emit q_func()->triggered(foundAction);
+ return true;
+ }
+ return false;
+}
+
+void QMenuBarPrivate::wceCommands(uint command) {
+ if (wceClassicMenu) {
+ for (int i = 0; i < wce_menubar->actionItemsClassic.size(); ++i)
+ wceEmitSignals(wce_menubar->actionItemsClassic.at(i), command);
+ } else {
+ if (wceEmitSignals(wce_menubar->actionItems, command)) {
+ return ;
+ }
+ else if (wce_menubar->leftButtonIsMenu) {//check if command is on the left quick button
+ wceEmitSignals(wce_menubar->actionItemsLeftButton, command);
+ }
+ else if ((wce_menubar->leftButtonAction) && (command == wce_menubar->leftButtonCommand)) {
+ emit q_func()->triggered(wce_menubar->leftButtonAction);
+ wce_menubar->leftButtonAction->activate(QAction::Trigger);
+ }
+ }
+}
+
+QAction *QMenuPrivate::wceCommands(uint command) {
+ QAction *foundAction = 0;
+ for (int i = 0; i < wce_menu->actionItems.size(); ++i) {
+ if (foundAction)
+ break;
+ QWceMenuAction *action = wce_menu->actionItems.at(i);
+ if (action->action->menu()) {
+ foundAction = action->action->menu()->d_func()->wceCommands(command);
+ }
+ else if (action->command == command) {
+ action->action->activate(QAction::Trigger);
+ return action->action;
+ }
+ }
+ if (foundAction)
+ emit q_func()->triggered(foundAction);
+ return foundAction;
+}
+
+void QMenuBarPrivate::wceCreateMenuBar(QWidget *parent) {
+
+ Q_Q(QMenuBar);
+ wce_menubar = new QWceMenuBarPrivate(this);
+
+ wce_menubar->parentWindowHandle = parent ? parent->winId() : q->winId();
+ wce_menubar->leftButtonAction = defaultAction;
+
+ wce_menubar->menubarHandle = qt_wce_create_menubar(wce_menubar->parentWindowHandle, (HINSTANCE)qWinAppInst(), 0, SHCMBF_EMPTYBAR);
+ Q_ASSERT_X(wce_menubar->menubarHandle, "wceCreateMenuBar", "cannot create empty menu bar");
+ DrawMenuBar(wce_menubar->menubarHandle);
+ nativeMenuBars.append(q);
+ wceClassicMenu = (!qt_wince_is_smartphone() && !qt_wince_is_pocket_pc());
+}
+
+void QMenuBarPrivate::wceDestroyMenuBar() {
+ Q_Q(QMenuBar);
+ int index = nativeMenuBars.indexOf(q);
+ nativeMenuBars.removeAt(index);
+ if (wce_menubar)
+ delete wce_menubar;
+ wce_menubar = 0;
+}
+
+QMenuBarPrivate::QWceMenuBarPrivate::QWceMenuBarPrivate(QMenuBarPrivate *menubar) :
+ menubarHandle(0), menuHandle(0),leftButtonMenuHandle(0) ,
+ leftButtonAction(0), leftButtonIsMenu(false), d(menubar) {
+}
+
+QMenuBarPrivate::QWceMenuBarPrivate::~QWceMenuBarPrivate() {
+ if (menubarHandle)
+ DestroyWindow(menubarHandle);
+ qt_wce_delete_action_list(&actionItems);
+ qt_wce_delete_action_list(&actionItemsLeftButton);
+
+ for (int i=0; i<actionItemsClassic.size(); ++i)
+ if (!actionItemsClassic.value(i).empty())
+ qt_wce_delete_action_list(&actionItemsClassic[i]);
+ actionItemsClassic.clear();
+
+ menubarHandle = 0;
+ menuHandle = 0;
+ leftButtonMenuHandle = 0;
+ leftButtonCommand = 0;
+ QMenuBar::wceRefresh();
+}
+
+QMenuPrivate::QWceMenuPrivate::QWceMenuPrivate() {
+ menuHandle = 0;
+}
+
+QMenuPrivate::QWceMenuPrivate::~QWceMenuPrivate() {
+ qt_wce_delete_action_list(&actionItems);
+ menuHandle = 0;
+}
+
+void QMenuPrivate::QWceMenuPrivate::addAction(QAction *a, QWceMenuAction *before) {
+ QWceMenuAction *action = new QWceMenuAction;
+ action->action = a;
+ action->command = qt_wce_menu_static_cmd_id++;
+ addAction(action, before);
+}
+
+void QMenuPrivate::QWceMenuPrivate::addAction(QWceMenuAction *action, QWceMenuAction *before) {
+ if (!action)
+ return;
+ int before_index = actionItems.indexOf(before);
+ if (before_index < 0) {
+ before = 0;
+ before_index = actionItems.size();
+ }
+ actionItems.insert(before_index, action);
+ rebuild();
+}
+
+/*!
+ \internal
+
+ This function will return the HMENU used to create the native
+ Windows CE menu bar bindings.
+*/
+
+HMENU QMenu::wceMenu(bool create) { return d_func()->wceMenu(create); }
+
+HMENU QMenuPrivate::wceMenu(bool create) {
+ if (!wce_menu)
+ wce_menu = new QWceMenuPrivate;
+ if (!wce_menu->menuHandle || create)
+ wce_menu->rebuild(create);
+ return wce_menu->menuHandle;
+}
+
+void QMenuPrivate::QWceMenuPrivate::rebuild(bool reCreate) {
+ if (menuHandle && !reCreate)
+ DestroyMenu(menuHandle);
+ menuHandle = CreatePopupMenu();
+ for (int i = 0; i < actionItems.size(); ++i) {
+ QWceMenuAction *action = actionItems.at(i);
+ action->menuHandle = menuHandle;
+ qt_wce_insert_action(menuHandle, action, true);
+ }
+ QMenuBar::wceRefresh();
+}
+
+void QMenuPrivate::QWceMenuPrivate::syncAction(QWceMenuAction *) {
+ rebuild();
+}
+
+void QMenuPrivate::QWceMenuPrivate::removeAction(QWceMenuAction *action) {
+ actionItems.removeAll(action);
+ delete action;
+ action = 0;
+ rebuild();
+}
+
+void QMenuBarPrivate::QWceMenuBarPrivate::addAction(QAction *a, QWceMenuAction *before) {
+ QWceMenuAction *action = new QWceMenuAction;
+ action->action = a;
+ action->command = qt_wce_menu_static_cmd_id++;
+ addAction(action, before);
+}
+
+void QMenuBarPrivate::QWceMenuBarPrivate::addAction(QWceMenuAction *action, QWceMenuAction *before) {
+ if (!action)
+ return;
+ int before_index = actionItems.indexOf(before);
+ if (before_index < 0) {
+ before = 0;
+ before_index = actionItems.size();
+ }
+ actionItems.insert(before_index, action);
+ rebuild();
+}
+
+void QMenuBarPrivate::QWceMenuBarPrivate::syncAction(QWceMenuAction*) {
+ QMenuBar::wceRefresh();
+ rebuild();
+}
+
+void QMenuBarPrivate::QWceMenuBarPrivate::removeAction(QWceMenuAction *action) {
+ actionItems.removeAll(action);
+ delete action;
+ action = 0;
+ rebuild();
+}
+
+void QMenuBarPrivate::_q_updateDefaultAction() {
+ if (wce_menubar)
+ wce_menubar->rebuild();
+}
+
+void QMenuBarPrivate::QWceMenuBarPrivate::rebuild() {
+
+ d->q_func()->resize(0,0);
+ parentWindowHandle = d->q_func()->parentWidget() ? d->q_func()->parentWidget()->winId() : d->q_func()->winId();
+ if (d->wceClassicMenu) {
+ QList<QAction*> actions = d->actions;
+ int maxEntries;
+ int resourceHandle;
+ if (actions.size() < 5) {
+ maxEntries = 4;
+ resourceHandle = IDR_MAIN_MENU3;
+ } else if (actions.size() < 7) {
+ maxEntries = 6;
+ resourceHandle = IDR_MAIN_MENU4;
+ }
+ else {
+ maxEntries = 8;
+ resourceHandle = IDR_MAIN_MENU5;
+ }
+ Q_ASSERT_X(menubarHandle, "rebuild !created", "menubar already deleted");
+ DestroyWindow(menubarHandle);
+ menubarHandle = qt_wce_create_menubar(parentWindowHandle, qt_wce_get_module_handle(), resourceHandle);
+ Q_ASSERT_X(menubarHandle, "rebuild classic menu", "cannot create menubar from resource");
+ DrawMenuBar(menubarHandle);
+ QList<int> menu_ids;
+ QList<int> item_ids;
+ menu_ids << IDM_MENU1 << IDM_MENU2 << IDM_MENU3 << IDM_MENU4 << IDM_MENU5 << IDM_MENU6 << IDM_MENU7 << IDM_MENU8;
+ item_ids << IDM_ITEM1 << IDM_ITEM2 << IDM_ITEM3 << IDM_ITEM4 << IDM_ITEM5 << IDM_ITEM6 << IDM_ITEM7 << IDM_ITEM8;
+
+ for (int i = 0; i < actionItemsClassic.size(); ++i)
+ if (!actionItemsClassic.value(i).empty())
+ qt_wce_delete_action_list(&actionItemsClassic[i]);
+ actionItemsClassic.clear();
+
+ for (int i = 0; i < actions.size(); ++i) {
+ qt_wce_rename_menu_item(menubarHandle, menu_ids.at(i), actions.at(i)->text());
+ QList<QAction *> subActions = actions.at(i)->menu()->actions();
+ HMENU subMenuHandle = (HMENU) SendMessage(menubarHandle, SHCMBM_GETSUBMENU,0 , menu_ids.at(i));
+ DeleteMenu(subMenuHandle, item_ids.at(i), MF_BYCOMMAND);
+ for (int c = 0; c < subActions.size(); ++c) {
+ QList<QWceMenuAction*> list;
+ actionItemsClassic.append(list);
+ QWceMenuAction *action = new QWceMenuAction;
+ action->action = subActions.at(c);
+ action->command = qt_wce_menu_static_cmd_id++;
+ action->menuHandle = subMenuHandle;
+ actionItemsClassic.last().append(action);
+ qt_wce_insert_action(subMenuHandle, action, true);
+ }
+ }
+ for (int i = actions.size();i<maxEntries;++i) {
+ qt_wce_rename_menu_item(menubarHandle, menu_ids.at(i), QString());
+ qt_wce_disable_soft_key(menubarHandle, menu_ids.at(i));
+ }
+ } else {
+ leftButtonAction = d->defaultAction;
+ if (!leftButtonAction)
+ leftButtonAction = qt_wce_get_quit_action(actionItems);
+
+ leftButtonIsMenu = (leftButtonAction && leftButtonAction->menu());
+ Q_ASSERT_X(menubarHandle, "rebuild !created", "menubar already deleted");
+ DestroyWindow(menubarHandle);
+ if (leftButtonIsMenu) {
+ menubarHandle = qt_wce_create_menubar(parentWindowHandle, qt_wce_get_module_handle(), IDR_MAIN_MENU2);
+ Q_ASSERT_X(menubarHandle, "rebuild !created left menubar", "cannot create menubar from resource");
+ menuHandle = (HMENU) SendMessage(menubarHandle, SHCMBM_GETSUBMENU,0,IDM_MENU);
+ Q_ASSERT_X(menuHandle, "rebuild !created", "IDM_MENU not found - invalid resource?");
+ DeleteMenu(menuHandle, IDM_ABOUT, MF_BYCOMMAND);
+ leftButtonMenuHandle = (HMENU) SendMessage(menubarHandle, SHCMBM_GETSUBMENU,0,IDM_LEFTMENU);
+ Q_ASSERT_X(leftButtonMenuHandle, "rebuild !created", "IDM_LEFTMENU not found - invalid resource?");
+ DeleteMenu(leftButtonMenuHandle, IDM_VIEW, MF_BYCOMMAND);
+ } else {
+ menubarHandle = qt_wce_create_menubar(parentWindowHandle, qt_wce_get_module_handle(), IDR_MAIN_MENU);
+ Q_ASSERT_X(menubarHandle, "rebuild !created no left menubar", "cannot create menubar from resource");
+ menuHandle = (HMENU) SendMessage(menubarHandle, SHCMBM_GETSUBMENU,0,IDM_MENU);
+ Q_ASSERT_X(menuHandle, "rebuild !created", "IDM_MENU not found - invalid resource?");
+ DeleteMenu(menuHandle, IDM_ABOUT, MF_BYCOMMAND);
+ leftButtonMenuHandle = 0;
+ leftButtonCommand = qt_wce_menu_static_cmd_id++;
+ qt_wce_change_command(menubarHandle, IDM_EXIT, leftButtonCommand);
+ }
+
+ if (actionItems.size() == 0) {
+ qt_wce_rename_menu_item(menubarHandle, IDM_MENU, QLatin1String(""));
+ qt_wce_disable_soft_key(menubarHandle, IDM_MENU);
+ }
+ for (int i = 0; i < actionItems.size(); ++i) {
+ QWceMenuAction *action = actionItems.at(i);
+ action->menuHandle = menuHandle;
+ qt_wce_insert_action(menuHandle, action, true);
+ }
+ if (!leftButtonIsMenu) {
+ if (leftButtonAction) {
+ qt_wce_rename_menu_item(menubarHandle, leftButtonCommand, leftButtonAction->text());
+ qt_wce_enable_soft_key(menubarHandle, leftButtonCommand);
+ } else {
+ qt_wce_rename_menu_item(menubarHandle, leftButtonCommand, QLatin1String(""));
+ qt_wce_disable_soft_key(menubarHandle, leftButtonCommand);
+ }
+ } else {
+ qt_wce_rename_menu_item(menubarHandle, IDM_LEFTMENU, leftButtonAction->text());
+ QList<QAction *> actions = leftButtonAction->menu()->actions();
+ qt_wce_delete_action_list(&actionItemsLeftButton);
+ for (int i=0; i<actions.size(); ++i) {
+ QWceMenuAction *action = new QWceMenuAction;
+ action->action = actions.at(i);
+ action->command = qt_wce_menu_static_cmd_id++;
+ action->menuHandle = leftButtonMenuHandle;
+ actionItemsLeftButton.append(action);
+ qt_wce_insert_action(leftButtonMenuHandle, action, true);
+ }
+ }
+ }
+ DrawMenuBar(menubarHandle);
+}
+
+QT_END_NAMESPACE
+
+#endif //QT_NO_MENUBAR
+#endif //Q_OS_WINCE
diff --git a/src/gui/widgets/qmenu_wince.rc b/src/gui/widgets/qmenu_wince.rc
new file mode 100644
index 0000000000..2540d9f43a
--- /dev/null
+++ b/src/gui/widgets/qmenu_wince.rc
@@ -0,0 +1,231 @@
+#include "qmenu_wince_resource_p.h"
+
+#include <commctrl.h>
+#include "winuser.h"
+
+#if defined (_DEBUG) && defined(QT_DLL)
+#include "QtGuid_resource.rc"
+#elif defined(QT_DLL)
+#include "QtGui_resource.rc"
+#endif
+
+#define DIALOGEX DIALOG DISCARDABLE
+#define SHMENUBAR RCDATA
+#define I_IMAGENONE (-2)
+#define NOMENU 0xFFFF
+
+IDR_MAIN_MENU MENU DISCARDABLE
+BEGIN
+ POPUP "Menu"
+ BEGIN
+ MENUITEM "About", IDM_ABOUT
+ END
+END
+
+IDR_MAIN_MENU2 MENU DISCARDABLE
+BEGIN
+ POPUP "Menu"
+ BEGIN
+ MENUITEM "About", IDM_ABOUT
+ END
+ POPUP "Display"
+ BEGIN
+ MENUITEM "View", IDM_VIEW
+ END
+END
+
+
+IDR_MAIN_MENU3 MENU DISCARDABLE
+BEGIN
+ POPUP "Menu1"
+ BEGIN
+ MENUITEM "Item1", IDM_ITEM1
+ END
+ POPUP "Menu2"
+ BEGIN
+ MENUITEM "Item2", IDM_ITEM2
+ END
+ POPUP "Menu3"
+ BEGIN
+ MENUITEM "Item3", IDM_ITEM3
+ END
+ POPUP "Menu4"
+ BEGIN
+ MENUITEM "Item4", IDM_ITEM4
+ END
+END
+
+IDR_MAIN_MENU4 MENU DISCARDABLE
+BEGIN
+ POPUP "Menu1"
+ BEGIN
+ MENUITEM "Item1", IDM_ITEM1
+ END
+ POPUP "Menu2"
+ BEGIN
+ MENUITEM "Item2", IDM_ITEM2
+ END
+ POPUP "Menu3"
+ BEGIN
+ MENUITEM "Item3", IDM_ITEM3
+ END
+ POPUP "Menu4"
+ BEGIN
+ MENUITEM "Item4", IDM_ITEM4
+ END
+ POPUP "Menu5"
+ BEGIN
+ MENUITEM "Item5", IDM_ITEM5
+ END
+ POPUP "Menu6"
+ BEGIN
+ MENUITEM "Item6", IDM_ITEM6
+ END
+END
+
+IDR_MAIN_MENU5 MENU DISCARDABLE
+BEGIN
+ POPUP "Menu1"
+ BEGIN
+ MENUITEM "Item1", IDM_ITEM1
+ END
+ POPUP "Menu2"
+ BEGIN
+ MENUITEM "Item2", IDM_ITEM2
+ END
+ POPUP "Menu3"
+ BEGIN
+ MENUITEM "Item3", IDM_ITEM3
+ END
+ POPUP "Menu4"
+ BEGIN
+ MENUITEM "Item4", IDM_ITEM4
+ END
+ POPUP "Menu5"
+ BEGIN
+ MENUITEM "Item5", IDM_ITEM5
+ END
+ POPUP "Menu6"
+ BEGIN
+ MENUITEM "Item6", IDM_ITEM6
+ END
+ POPUP "Menu7"
+ BEGIN
+ MENUITEM "Item7", IDM_ITEM7
+ END
+ POPUP "Menu8"
+ BEGIN
+ MENUITEM "Item8", IDM_ITEM8
+ END
+END
+
+STRINGTABLE
+BEGIN
+ IDS_EXIT "Exit"
+ IDS_MENU "Menu"
+ IDS_LEFTMENU "Display"
+ IDS_MENU1 "Menu__1"
+ IDS_MENU2 "Menu__2"
+ IDS_MENU3 "Menu__3"
+ IDS_MENU4 "Menu__4"
+ IDS_MENU5 "Menu__5"
+ IDS_MENU6 "Menu__6"
+ IDS_MENU7 "Menu__7"
+ IDS_MENU8 "Menu__8"
+END
+
+IDR_MAIN_MENU SHMENUBAR DISCARDABLE
+BEGIN
+ IDR_MAIN_MENU,
+ 2,
+
+ I_IMAGENONE, IDM_EXIT, TBSTATE_ENABLED, TBSTYLE_BUTTON | TBSTYLE_AUTOSIZE,
+ IDS_EXIT, 0, NOMENU,
+
+ I_IMAGENONE, IDM_MENU, TBSTATE_ENABLED, TBSTYLE_DROPDOWN | TBSTYLE_AUTOSIZE,
+ IDS_MENU, 0, 0,
+END
+
+IDR_MAIN_MENU2 SHMENUBAR DISCARDABLE
+BEGIN
+ IDR_MAIN_MENU2,
+ 2,
+
+ I_IMAGENONE, IDM_LEFTMENU, TBSTATE_ENABLED, TBSTYLE_DROPDOWN | TBSTYLE_AUTOSIZE,
+ IDS_LEFTMENU, 0, 1,
+
+ I_IMAGENONE, IDM_MENU, TBSTATE_ENABLED, TBSTYLE_DROPDOWN | TBSTYLE_AUTOSIZE,
+ IDS_MENU, 0, 0,
+END
+
+IDR_MAIN_MENU3 SHMENUBAR DISCARDABLE
+BEGIN
+ IDR_MAIN_MENU3,
+ 4,
+
+ I_IMAGENONE, IDM_MENU1, TBSTATE_ENABLED, TBSTYLE_DROPDOWN | TBSTYLE_AUTOSIZE,
+ IDS_MENU1, 0, 0,
+
+ I_IMAGENONE, IDM_MENU2, TBSTATE_ENABLED, TBSTYLE_DROPDOWN | TBSTYLE_AUTOSIZE,
+ IDS_MENU2, 0, 1,
+
+ I_IMAGENONE, IDM_MENU3, TBSTATE_ENABLED, TBSTYLE_DROPDOWN | TBSTYLE_AUTOSIZE,
+ IDS_MENU3, 0, 2,
+
+ I_IMAGENONE, IDM_MENU4, TBSTATE_ENABLED, TBSTYLE_DROPDOWN | TBSTYLE_AUTOSIZE,
+ IDS_MENU4, 0, 3,
+END
+
+IDR_MAIN_MENU4 SHMENUBAR DISCARDABLE
+BEGIN
+ IDR_MAIN_MENU4,
+ 6,
+
+ I_IMAGENONE, IDM_MENU1, TBSTATE_ENABLED, TBSTYLE_DROPDOWN | TBSTYLE_AUTOSIZE,
+ IDS_MENU1, 0, 0,
+
+ I_IMAGENONE, IDM_MENU2, TBSTATE_ENABLED, TBSTYLE_DROPDOWN | TBSTYLE_AUTOSIZE,
+ IDS_MENU2, 0, 1,
+
+ I_IMAGENONE, IDM_MENU3, TBSTATE_ENABLED, TBSTYLE_DROPDOWN | TBSTYLE_AUTOSIZE,
+ IDS_MENU3, 0, 2,
+
+ I_IMAGENONE, IDM_MENU4, TBSTATE_ENABLED, TBSTYLE_DROPDOWN | TBSTYLE_AUTOSIZE,
+ IDS_MENU4, 0, 3,
+
+ I_IMAGENONE, IDM_MENU5, TBSTATE_ENABLED, TBSTYLE_DROPDOWN | TBSTYLE_AUTOSIZE,
+ IDS_MENU5, 0, 4,
+
+ I_IMAGENONE, IDM_MENU6, TBSTATE_ENABLED, TBSTYLE_DROPDOWN | TBSTYLE_AUTOSIZE,
+ IDS_MENU6, 0, 5,
+END
+
+IDR_MAIN_MENU5 SHMENUBAR DISCARDABLE
+BEGIN
+ IDR_MAIN_MENU5,
+ 8,
+
+ I_IMAGENONE, IDM_MENU1, TBSTATE_ENABLED, TBSTYLE_DROPDOWN | TBSTYLE_AUTOSIZE,
+ IDS_MENU1, 0, 0,
+
+ I_IMAGENONE, IDM_MENU2, TBSTATE_ENABLED, TBSTYLE_DROPDOWN | TBSTYLE_AUTOSIZE,
+ IDS_MENU2, 0, 1,
+
+ I_IMAGENONE, IDM_MENU3, TBSTATE_ENABLED, TBSTYLE_DROPDOWN | TBSTYLE_AUTOSIZE,
+ IDS_MENU3, 0, 2,
+
+ I_IMAGENONE, IDM_MENU4, TBSTATE_ENABLED, TBSTYLE_DROPDOWN | TBSTYLE_AUTOSIZE,
+ IDS_MENU4, 0, 3,
+
+ I_IMAGENONE, IDM_MENU5, TBSTATE_ENABLED, TBSTYLE_DROPDOWN | TBSTYLE_AUTOSIZE,
+ IDS_MENU5, 0, 4,
+
+ I_IMAGENONE, IDM_MENU6, TBSTATE_ENABLED, TBSTYLE_DROPDOWN | TBSTYLE_AUTOSIZE,
+ IDS_MENU6, 0, 5,
+
+ I_IMAGENONE, IDM_MENU7, TBSTATE_ENABLED, TBSTYLE_DROPDOWN | TBSTYLE_AUTOSIZE,
+ IDS_MENU7, 0, 6,
+
+ I_IMAGENONE, IDM_MENU8, TBSTATE_ENABLED, TBSTYLE_DROPDOWN | TBSTYLE_AUTOSIZE,
+ IDS_MENU8, 0, 7,
+END
diff --git a/src/gui/widgets/qmenu_wince_resource_p.h b/src/gui/widgets/qmenu_wince_resource_p.h
new file mode 100644
index 0000000000..cc944f4f94
--- /dev/null
+++ b/src/gui/widgets/qmenu_wince_resource_p.h
@@ -0,0 +1,94 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//
+// 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.
+//
+
+QT_BEGIN_HEADER
+
+#define IDR_MAIN_MENU 102
+#define IDR_MAIN_MENU2 103
+#define IDR_MAIN_MENU3 104
+#define IDS_EXIT 105
+#define IDS_MENU 106
+#define IDS_LEFTMENU 107
+#define IDM_ABOUT 108
+#define IDM_VIEW 109
+#define IDM_ITEM1 108
+#define IDM_ITEM2 109
+#define IDM_ITEM3 110
+#define IDM_ITEM4 111
+#define IDM_ITEM5 112
+#define IDM_ITEM6 113
+#define IDM_ITEM7 114
+#define IDM_ITEM8 115
+#define IDS_MENU1 116
+#define IDS_MENU2 117
+#define IDS_MENU3 118
+#define IDS_MENU4 119
+#define IDS_MENU5 120
+#define IDS_MENU6 121
+#define IDS_MENU7 122
+#define IDS_MENU8 123
+#define IDR_MAIN_MENU4 124
+#define IDR_MAIN_MENU5 125
+#define IDM_EXIT 40000
+#define IDM_MENU 40001
+#define IDM_LEFTMENU 40002
+#define IDM_MENU1 40003
+#define IDM_MENU2 40004
+#define IDM_MENU3 40005
+#define IDM_MENU4 40006
+#define IDM_MENU5 40007
+#define IDM_MENU6 40008
+#define IDM_MENU7 40009
+#define IDM_MENU8 40010
+
+QT_END_HEADER
+
diff --git a/src/gui/widgets/qmenubar.cpp b/src/gui/widgets/qmenubar.cpp
new file mode 100644
index 0000000000..ccf37db220
--- /dev/null
+++ b/src/gui/widgets/qmenubar.cpp
@@ -0,0 +1,2405 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <qmenubar.h>
+
+#include <qstyle.h>
+#include <qlayout.h>
+#include <qapplication.h>
+#include <qdesktopwidget.h>
+#ifndef QT_NO_ACCESSIBILITY
+# include <qaccessible.h>
+#endif
+#include <qpainter.h>
+#include <qstylepainter.h>
+#include <qevent.h>
+#include <qmainwindow.h>
+#include <qtoolbar.h>
+#include <qtoolbutton.h>
+#include <qwhatsthis.h>
+
+#ifndef QT_NO_MENUBAR
+
+#ifdef QT3_SUPPORT
+#include <private/qaction_p.h>
+#include <qmenudata.h>
+#endif
+
+#include "qmenu_p.h"
+#include "qmenubar_p.h"
+#include "qdebug.h"
+
+#ifdef Q_OS_WINCE
+extern bool qt_wince_is_mobile(); //defined in qguifunctions_wce.cpp
+#endif
+
+QT_BEGIN_NAMESPACE
+
+class QMenuBarExtension : public QToolButton
+{
+public:
+ explicit QMenuBarExtension(QWidget *parent);
+
+ QSize sizeHint() const;
+ void paintEvent(QPaintEvent *);
+};
+
+QMenuBarExtension::QMenuBarExtension(QWidget *parent)
+ : QToolButton(parent)
+{
+ setObjectName(QLatin1String("qt_menubar_ext_button"));
+ setAutoRaise(true);
+#ifndef QT_NO_MENU
+ setPopupMode(QToolButton::InstantPopup);
+#endif
+ setIcon(style()->standardIcon(QStyle::SP_ToolBarHorizontalExtensionButton, 0, parentWidget()));
+}
+
+void QMenuBarExtension::paintEvent(QPaintEvent *)
+{
+ QStylePainter p(this);
+ QStyleOptionToolButton opt;
+ initStyleOption(&opt);
+ // We do not need to draw both extention arrows
+ opt.features &= ~QStyleOptionToolButton::HasMenu;
+ p.drawComplexControl(QStyle::CC_ToolButton, opt);
+}
+
+
+QSize QMenuBarExtension::sizeHint() const
+{
+ int ext = style()->pixelMetric(QStyle::PM_ToolBarExtensionExtent, 0, parentWidget());
+ return QSize(ext, ext);
+}
+
+
+/*!
+ \internal
+*/
+QAction *QMenuBarPrivate::actionAt(QPoint p) const
+{
+ Q_Q(const QMenuBar);
+ QList<QAction*> items = q->actions();
+ for(int i = 0; i < items.size(); ++i) {
+ if(actionRect(items.at(i)).contains(p))
+ return items.at(i);
+ }
+ return 0;
+}
+
+QRect QMenuBarPrivate::menuRect(bool extVisible) const
+{
+ Q_Q(const QMenuBar);
+
+ int hmargin = q->style()->pixelMetric(QStyle::PM_MenuBarPanelWidth, 0, q);
+ QRect result = q->rect();
+ result.adjust(hmargin, 0, -hmargin, 0);
+
+ if (extVisible) {
+ if (q->layoutDirection() == Qt::RightToLeft)
+ result.setLeft(result.left() + extension->sizeHint().width());
+ else
+ result.setWidth(result.width() - extension->sizeHint().width());
+ }
+
+ if (leftWidget && leftWidget->isVisible()) {
+ QSize sz = leftWidget->sizeHint();
+ if (q->layoutDirection() == Qt::RightToLeft)
+ result.setRight(result.right() - sz.width());
+ else
+ result.setLeft(result.left() + sz.width());
+ }
+
+ if (rightWidget && rightWidget->isVisible()) {
+ QSize sz = rightWidget->sizeHint();
+ if (q->layoutDirection() == Qt::RightToLeft)
+ result.setLeft(result.left() + sz.width());
+ else
+ result.setRight(result.right() - sz.width());
+ }
+
+ return result;
+}
+
+bool QMenuBarPrivate::isVisible(QAction *action)
+{
+ return !hiddenActions.contains(action);
+}
+
+void QMenuBarPrivate::updateGeometries()
+{
+ Q_Q(QMenuBar);
+ if(!itemsDirty)
+ return;
+ int q_width = q->width()-(q->style()->pixelMetric(QStyle::PM_MenuBarPanelWidth, 0, q)*2);
+ int q_start = -1;
+ if(leftWidget || rightWidget) {
+ int vmargin = q->style()->pixelMetric(QStyle::PM_MenuBarVMargin, 0, q)
+ + q->style()->pixelMetric(QStyle::PM_MenuBarPanelWidth, 0, q);
+ int hmargin = q->style()->pixelMetric(QStyle::PM_MenuBarHMargin, 0, q)
+ + q->style()->pixelMetric(QStyle::PM_MenuBarPanelWidth, 0, q);
+ if (leftWidget && leftWidget->isVisible()) {
+ QSize sz = leftWidget->sizeHint();
+ q_width -= sz.width();
+ q_start = sz.width();
+ QPoint pos(hmargin, (q->height() - leftWidget->height()) / 2);
+ QRect vRect = QStyle::visualRect(q->layoutDirection(), q->rect(), QRect(pos, sz));
+ leftWidget->setGeometry(vRect);
+ }
+ if (rightWidget && rightWidget->isVisible()) {
+ QSize sz = rightWidget->sizeHint();
+ q_width -= sz.width();
+ QPoint pos(q->width() - sz.width() - hmargin, vmargin);
+ QRect vRect = QStyle::visualRect(q->layoutDirection(), q->rect(), QRect(pos, sz));
+ rightWidget->setGeometry(vRect);
+ }
+ }
+
+#ifdef Q_WS_MAC
+ if(mac_menubar) {//nothing to see here folks, move along..
+ itemsDirty = false;
+ return;
+ }
+#endif
+ calcActionRects(q_width, q_start, actionRects, actionList);
+ itemsWidth = q_width;
+ itemsStart = q_start;
+ currentAction = 0;
+#ifndef QT_NO_SHORTCUT
+ if(itemsDirty) {
+ for(int j = 0; j < shortcutIndexMap.size(); ++j)
+ q->releaseShortcut(shortcutIndexMap.value(j));
+ shortcutIndexMap.resize(0); // faster than clear
+ for(int i = 0; i < actionList.count(); i++)
+ shortcutIndexMap.append(q->grabShortcut(QKeySequence::mnemonic(actionList.at(i)->text())));
+ }
+#endif
+ itemsDirty = false;
+
+ hiddenActions.clear();
+ //this is the menu rectangle without any extension
+ QRect menuRect = this->menuRect(false);
+
+ //we try to see if the actions will fit there
+ bool hasHiddenActions = false;
+ foreach(QAction *action, actionList) {
+ if (!menuRect.contains(actionRect(action))) {
+ hasHiddenActions = true;
+ break;
+ }
+ }
+
+ //...and if not, determine the ones that fit on the menu with the extension visible
+ if (hasHiddenActions) {
+ menuRect = this->menuRect(true);
+ foreach(QAction *action, actionList) {
+ if (!menuRect.contains(actionRect(action))) {
+ hiddenActions.append(action);
+ }
+ }
+ }
+
+ if (hiddenActions.count() > 0) {
+ QMenu *pop = extension->menu();
+ if (!pop) {
+ pop = new QMenu(q);
+ extension->setMenu(pop);
+ }
+ pop->clear();
+ pop->addActions(hiddenActions);
+
+ int vmargin = q->style()->pixelMetric(QStyle::PM_MenuBarVMargin, 0, q);
+ int x = q->layoutDirection() == Qt::RightToLeft
+ ? menuRect.left() - extension->sizeHint().width() + 1
+ : menuRect.right();
+ extension->setGeometry(x, vmargin, extension->sizeHint().width(), menuRect.height() - vmargin*2);
+ extension->show();
+ } else {
+ extension->hide();
+ }
+ q->updateGeometry();
+#ifdef QT3_SUPPORT
+ if (q->parentWidget() != 0) {
+ QMenubarUpdatedEvent menubarUpdated(q);
+ QApplication::sendEvent(q->parentWidget(), &menubarUpdated);
+ }
+#endif
+}
+
+QRect QMenuBarPrivate::actionRect(QAction *act) const
+{
+ Q_Q(const QMenuBar);
+ QRect ret = actionRects.value(act);
+ const int fw = q->style()->pixelMetric(QStyle::PM_MenuBarPanelWidth, 0, q);
+ ret.translate(fw, fw);
+ return QStyle::visualRect(q->layoutDirection(), q->rect(), ret);
+}
+
+void QMenuBarPrivate::setKeyboardMode(bool b)
+{
+ Q_Q(QMenuBar);
+ if (b && !q->style()->styleHint(QStyle::SH_MenuBar_AltKeyNavigation, 0, q)) {
+ setCurrentAction(0);
+ return;
+ }
+ keyboardState = b;
+ if(b) {
+ QWidget *fw = qApp->focusWidget();
+ if (fw != q)
+ keyboardFocusWidget = fw;
+ if(!currentAction && !actionList.isEmpty())
+ setCurrentAction(actionList.first());
+ q->setFocus(Qt::MenuBarFocusReason);
+ } else {
+ if(!popupState)
+ setCurrentAction(0);
+ if(keyboardFocusWidget) {
+ if (qApp->focusWidget() == q)
+ keyboardFocusWidget->setFocus(Qt::MenuBarFocusReason);
+ keyboardFocusWidget = 0;
+ }
+ }
+ q->update();
+}
+
+void QMenuBarPrivate::popupAction(QAction *action, bool activateFirst)
+{
+ Q_Q(QMenuBar);
+ if(!action || !action->menu() || closePopupMode)
+ return;
+ popupState = true;
+ if (action->isEnabled() && action->menu()->isEnabled()) {
+ closePopupMode = 0;
+ activeMenu = action->menu();
+ activeMenu->d_func()->causedPopup.widget = q;
+ activeMenu->d_func()->causedPopup.action = action;
+
+ QRect adjustedActionRect = actionRect(action);
+ QPoint pos(q->mapToGlobal(QPoint(adjustedActionRect.left(), adjustedActionRect.bottom() + 1)));
+ QSize popup_size = activeMenu->sizeHint();
+
+ QRect screenRect = QApplication::desktop()->screenGeometry(pos);
+
+ const bool fitUp = (q->mapToGlobal(adjustedActionRect.topLeft()).y() >= popup_size.height());
+ const bool fitDown = (pos.y() + popup_size.height() <= screenRect.bottom());
+ const int actionWidth = adjustedActionRect.width();
+
+ if (!fitUp && !fitDown) { //we should shift the menu
+ bool shouldShiftToRight = !q->isRightToLeft();
+ if (q->isRightToLeft() && popup_size.width() > pos.x())
+ shouldShiftToRight = true;
+ else if (actionWidth + popup_size.width() + pos.x() > screenRect.right())
+ shouldShiftToRight = false;
+
+ if (shouldShiftToRight)
+ pos.rx() += actionWidth;
+ else
+ pos.rx() -= popup_size.width();
+ } else if (q->isRightToLeft()) {
+ pos.setX(pos.x()-(popup_size.width() - actionWidth));
+ }
+
+ if(pos.x() < screenRect.x()) {
+ pos.setX(screenRect.x());
+ } else {
+ const int off = pos.x()+popup_size.width() - screenRect.right();
+ if(off > 0)
+ pos.setX(qMax(screenRect.x(), pos.x()-off));
+
+ }
+
+ if(!defaultPopDown || (fitUp && !fitDown))
+ pos.setY(qMax(screenRect.y(), q->mapToGlobal(QPoint(0, adjustedActionRect.top()-popup_size.height())).y()));
+ activeMenu->popup(pos);
+ if(activateFirst)
+ activeMenu->d_func()->setFirstActionActive();
+ }
+ q->update(actionRect(action));
+}
+
+void QMenuBarPrivate::setCurrentAction(QAction *action, bool popup, bool activateFirst)
+{
+ if(currentAction == action && popup == popupState)
+ return;
+
+ autoReleaseTimer.stop();
+
+ doChildEffects = (popup && !activeMenu);
+ Q_Q(QMenuBar);
+ QWidget *fw = 0;
+ if(QMenu *menu = activeMenu) {
+ activeMenu = 0;
+ if (popup) {
+ fw = q->window()->focusWidget();
+ q->setFocus(Qt::NoFocusReason);
+ }
+ menu->hide();
+ }
+
+ if(currentAction)
+ q->update(actionRect(currentAction));
+
+ popupState = popup;
+#ifndef QT_NO_STATUSTIP
+ QAction *previousAction = currentAction;
+#endif
+ currentAction = action;
+ if (action) {
+ activateAction(action, QAction::Hover);
+ if(popup)
+ popupAction(action, activateFirst);
+ q->update(actionRect(action));
+#ifndef QT_NO_STATUSTIP
+ } else if (previousAction) {
+ QString empty;
+ QStatusTipEvent tip(empty);
+ QApplication::sendEvent(q, &tip);
+#endif
+ }
+ if (fw)
+ fw->setFocus(Qt::NoFocusReason);
+}
+
+void QMenuBarPrivate::calcActionRects(int max_width, int start, QMap<QAction*, QRect> &actionRects, QList<QAction*> &actionList) const
+{
+ Q_Q(const QMenuBar);
+
+ if(!itemsDirty && itemsWidth == max_width && itemsStart == start) {
+ actionRects = actionRects;
+ actionList = actionList;
+ return;
+ }
+ actionRects.clear();
+ actionList.clear();
+ const int itemSpacing = q->style()->pixelMetric(QStyle::PM_MenuBarItemSpacing, 0, q);
+ int max_item_height = 0, separator = -1, separator_start = 0, separator_len = 0;
+ QList<QAction*> items = q->actions();
+
+ //calculate size
+ const QFontMetrics fm = q->fontMetrics();
+ const int hmargin = q->style()->pixelMetric(QStyle::PM_MenuBarHMargin, 0, q),
+ vmargin = q->style()->pixelMetric(QStyle::PM_MenuBarVMargin, 0, q),
+ icone = q->style()->pixelMetric(QStyle::PM_SmallIconSize, 0, q);
+ for(int i = 0; i < items.count(); i++) {
+ QAction *action = items.at(i);
+ if(!action->isVisible())
+ continue;
+
+ QSize sz;
+
+ //calc what I think the size is..
+ if(action->isSeparator()) {
+ if (q->style()->styleHint(QStyle::SH_DrawMenuBarSeparator, 0, q))
+ separator = actionRects.count();
+ continue; //we don't really position these!
+ } else {
+ QString s = action->text();
+ if(!s.isEmpty()) {
+ int w = fm.width(s);
+ w -= s.count(QLatin1Char('&')) * fm.width(QLatin1Char('&'));
+ w += s.count(QLatin1String("&&")) * fm.width(QLatin1Char('&'));
+ sz = QSize(w, fm.height());
+ }
+
+ QIcon is = action->icon();
+ if (!is.isNull()) {
+ QSize is_sz = QSize(icone, icone);
+ if (is_sz.height() > sz.height())
+ sz.setHeight(is_sz.height());
+ if (is_sz.width() > sz.width())
+ sz.setWidth(is_sz.width());
+ }
+ }
+
+ //let the style modify the above size..
+ QStyleOptionMenuItem opt;
+ q->initStyleOption(&opt, action);
+ sz = q->style()->sizeFromContents(QStyle::CT_MenuBarItem, &opt, sz, q);
+
+ if(!sz.isEmpty()) {
+ { //update the separator state
+ int iWidth = sz.width();
+ iWidth += itemSpacing;
+ if(separator == -1)
+ separator_start += iWidth;
+ else
+ separator_len += iWidth;
+ }
+ //maximum height
+ max_item_height = qMax(max_item_height, sz.height());
+ //append
+ actionRects.insert(action, QRect(0, 0, sz.width(), sz.height()));
+ actionList.append(action);
+ }
+ }
+
+ //calculate position
+ int x = ((start == -1) ? hmargin : start) + itemSpacing;
+ int y = vmargin;
+ for(int i = 0; i < actionList.count(); i++) {
+ QAction *action = actionList.at(i);
+ QRect &rect = actionRects[action];
+ //resize
+ rect.setHeight(max_item_height);
+
+ //move
+ if(separator != -1 && i >= separator) { //after the separator
+ int left = (max_width - separator_len - hmargin - itemSpacing) + (x - separator_start - hmargin);
+ if(left < separator_start) { //wrap
+ separator_start = x = hmargin;
+ y += max_item_height;
+ }
+ rect.moveLeft(left);
+ } else {
+ rect.moveLeft(x);
+ }
+ rect.moveTop(y);
+
+ //keep moving along..
+ x += rect.width() + itemSpacing;
+ }
+}
+
+void QMenuBarPrivate::activateAction(QAction *action, QAction::ActionEvent action_e)
+{
+ Q_Q(QMenuBar);
+ if (!action || !action->isEnabled())
+ return;
+ action->activate(action_e);
+ if (action_e == QAction::Hover)
+ action->showStatusText(q);
+
+// if(action_e == QAction::Trigger)
+// emit q->activated(action);
+// else if(action_e == QAction::Hover)
+// emit q->highlighted(action);
+}
+
+
+void QMenuBarPrivate::_q_actionTriggered()
+{
+ Q_Q(QMenuBar);
+ if (QAction *action = qobject_cast<QAction *>(q->sender())) {
+ emit q->triggered(action);
+#ifdef QT3_SUPPORT
+ emit q->activated(q->findIdForAction(action));
+#endif
+ }
+}
+
+void QMenuBarPrivate::_q_actionHovered()
+{
+ Q_Q(QMenuBar);
+ if (QAction *action = qobject_cast<QAction *>(q->sender())) {
+ emit q->hovered(action);
+#ifndef QT_NO_ACCESSIBILITY
+ if (QAccessible::isActive()) {
+ QList<QAction*> actions = q->actions();
+ int actionIndex = actions.indexOf(action);
+ ++actionIndex;
+ QAccessible::updateAccessibility(q, actionIndex, QAccessible::Focus);
+ QAccessible::updateAccessibility(q, actionIndex, QAccessible::Selection);
+ }
+#endif //QT_NO_ACCESSIBILITY
+#ifdef QT3_SUPPORT
+ emit q->highlighted(q->findIdForAction(action));
+#endif
+ }
+}
+
+/*!
+ Initialize \a option with the values from the menu bar and information from \a action. This method
+ is useful for subclasses when they need a QStyleOptionMenuItem, but don't want
+ to fill in all the information themselves.
+
+ \sa QStyleOption::initFrom() QMenu::initStyleOption()
+*/
+void QMenuBar::initStyleOption(QStyleOptionMenuItem *option, const QAction *action) const
+{
+ if (!option || !action)
+ return;
+ Q_D(const QMenuBar);
+ option->palette = palette();
+ option->state = QStyle::State_None;
+ if (isEnabled() && action->isEnabled())
+ option->state |= QStyle::State_Enabled;
+ else
+ option->palette.setCurrentColorGroup(QPalette::Disabled);
+ option->fontMetrics = fontMetrics();
+ if (d->currentAction && d->currentAction == action) {
+ option->state |= QStyle::State_Selected;
+ if (d->popupState && !d->closePopupMode)
+ option->state |= QStyle::State_Sunken;
+ }
+ if (hasFocus() || d->currentAction)
+ option->state |= QStyle::State_HasFocus;
+ option->menuRect = rect();
+ option->menuItemType = QStyleOptionMenuItem::Normal;
+ option->checkType = QStyleOptionMenuItem::NotCheckable;
+ option->text = action->text();
+ option->icon = action->icon();
+}
+
+/*!
+ \class QMenuBar
+ \brief The QMenuBar class provides a horizontal menu bar.
+
+ \ingroup application
+ \mainclass
+
+ A menu bar consists of a list of pull-down menu items. You add
+ menu items with addMenu(). For example, asuming that \c menubar
+ is a pointer to a QMenuBar and \c fileMenu is a pointer to a
+ QMenu, the following statement inserts the menu into the menu bar:
+ \snippet doc/src/snippets/code/src_gui_widgets_qmenubar.cpp 0
+
+ The ampersand in the menu item's text sets Alt+F as a shortcut for
+ this menu. (You can use "\&\&" to get a real ampersand in the menu
+ bar.)
+
+ There is no need to lay out a menu bar. It automatically sets its
+ own geometry to the top of the parent widget and changes it
+ appropriately whenever the parent is resized.
+
+ \section1 Usage
+
+ In most main window style applications you would use the
+ \l{QMainWindow::}{menuBar()} function provided in QMainWindow,
+ adding \l{QMenu}s to the menu bar and adding \l{QAction}s to the
+ pop-up menus.
+
+ Example (from the \l{mainwindows/menus}{Menus} example):
+
+ \snippet examples/mainwindows/menus/mainwindow.cpp 9
+
+ Menu items may be removed with removeAction().
+
+ Widgets can be added to menus by using instances of the QWidgetAction
+ class to hold them. These actions can then be inserted into menus
+ in the usual way; see the QMenu documentation for more details.
+
+ \section1 Platform Dependent Look and Feel
+
+ Different platforms have different requirements for the appearance
+ of menu bars and their behavior when the user interacts with them.
+ For example, Windows systems are often configured so that the
+ underlined character mnemonics that indicate keyboard shortcuts
+ for items in the menu bar are only shown when the \gui{Alt} key is
+ pressed.
+
+ \table
+
+ \row \o \inlineimage plastique-menubar.png A menu bar shown in the
+ Plastique widget style.
+
+ \o The \l{QPlastiqueStyle}{Plastique widget style}, like most
+ other styles, handles the \gui{Help} menu in the same way as it
+ handles any other menu.
+
+ \row \o \inlineimage motif-menubar.png A menu bar shown in the
+ Motif widget style.
+
+ \o The \l{QMotifStyle}{Motif widget style} treats \gui{Help} menus
+ in a special way, placing them at right-hand end of the menu bar.
+
+ \endtable
+
+ \section1 QMenuBar on Mac OS X
+
+ QMenuBar on Mac OS X is a wrapper for using the system-wide menu bar.
+ If you have multiple menu bars in one dialog the outermost menu bar
+ (normally inside a widget with widget flag Qt::Window) will
+ be used for the system-wide menu bar.
+
+ Qt for Mac OS X also provides a menu bar merging feature to make
+ QMenuBar conform more closely to accepted Mac OS X menu bar layout.
+ The merging functionality is based on string matching the title of
+ a QMenu entry. These strings are translated (using QObject::tr())
+ in the "QMenuBar" context. If an entry is moved its slots will still
+ fire as if it was in the original place. The table below outlines
+ the strings looked for and where the entry is placed if matched:
+
+ \table
+ \header \i String matches \i Placement \i Notes
+ \row \i about.*
+ \i Application Menu | About <application name>
+ \i If this entry is not found no About item will appear in
+ the Application Menu
+ \row \i config, options, setup, settings or preferences
+ \i Application Menu | Preferences
+ \i If this entry is not found the Settings item will be disabled
+ \row \i quit or exit
+ \i Application Menu | Quit <application name>
+ \i If this entry is not found a default Quit item will be
+ created to call QApplication::quit()
+ \endtable
+
+ You can override this behavior by using the QAction::menuRole()
+ property.
+
+ If you want all windows in a Mac application to share one menu
+ bar, you must create a menu bar that does not have a parent.
+ Create a parent-less menu bar this way:
+
+ \snippet doc/src/snippets/code/src_gui_widgets_qmenubar.cpp 1
+
+ \bold{Note:} Do \e{not} call QMainWindow::menuBar() to create the
+ shared menu bar, because that menu bar will have the QMainWindow
+ as its parent. That menu bar would only be displayed for the
+ parent QMainWindow.
+
+ \bold{Note:} The text used for the application name in the menu
+ bar is obtained from the value set in the \c{Info.plist} file in
+ the application's bundle. See \l{Deploying an Application on
+ Mac OS X} for more information.
+
+ \section1 QMenuBar on Windows CE
+
+ QMenuBar on Windows CE is a wrapper for using the system-wide menu bar,
+ similar to the Mac. This feature is activated for Windows Mobile
+ and integrates QMenuBar with the native soft keys. The left soft
+ key can be controlled with QMenuBar::setDefaultAction() and the
+ right soft key can be used to access the menu bar.
+
+ The hovered() signal is not supported for the native menu
+ integration. Also, it is not possible to display an icon in a
+ native menu on Windows Mobile.
+
+ \section1 Examples
+
+ The \l{mainwindows/menus}{Menus} example shows how to use QMenuBar
+ and QMenu. The other \l{Qt Examples#Main Windows}{main window
+ application examples} also provide menus using these classes.
+
+ \sa QMenu, QShortcut, QAction,
+ {http://developer.apple.com/documentation/UserExperience/Conceptual/OSXHIGuidelines/index.html}{Introduction to Apple Human Interface Guidelines},
+ {fowler}{GUI Design Handbook: Menu Bar}, {Menus Example}
+*/
+
+
+void QMenuBarPrivate::init()
+{
+ Q_Q(QMenuBar);
+ q->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Minimum);
+ q->setAttribute(Qt::WA_CustomWhatsThis);
+#ifdef Q_WS_MAC
+ macCreateMenuBar(q->parentWidget());
+ if(mac_menubar)
+ q->hide();
+#endif
+#ifdef Q_OS_WINCE
+ if (qt_wince_is_mobile()) {
+ wceCreateMenuBar(q->parentWidget());
+ if(wce_menubar)
+ q->hide();
+ }
+#endif
+ q->setBackgroundRole(QPalette::Button);
+ oldWindow = oldParent = 0;
+#ifdef QT3_SUPPORT
+ doAutoResize = false;
+#endif
+ handleReparent();
+ q->setMouseTracking(q->style()->styleHint(QStyle::SH_MenuBar_MouseTracking, 0, q));
+
+ extension = new QMenuBarExtension(q);
+ extension->setFocusPolicy(Qt::NoFocus);
+ extension->hide();
+}
+
+/*!
+ Constructs a menu bar with parent \a parent.
+*/
+QMenuBar::QMenuBar(QWidget *parent) : QWidget(*new QMenuBarPrivate, parent, 0)
+{
+ Q_D(QMenuBar);
+ d->init();
+}
+
+#ifdef QT3_SUPPORT
+/*!
+ Use one of the constructors that doesn't take the \a name
+ argument and then use setObjectName() instead.
+*/
+QMenuBar::QMenuBar(QWidget *parent, const char *name) : QWidget(*new QMenuBarPrivate, parent, 0)
+{
+ Q_D(QMenuBar);
+ d->init();
+ setObjectName(QString::fromAscii(name));
+}
+#endif
+
+/*!
+ Destroys the menu bar.
+*/
+QMenuBar::~QMenuBar()
+{
+#ifdef Q_WS_MAC
+ Q_D(QMenuBar);
+ d->macDestroyMenuBar();
+#endif
+#ifdef Q_OS_WINCE
+ Q_D(QMenuBar);
+ if (qt_wince_is_mobile())
+ d->wceDestroyMenuBar();
+#endif
+}
+
+/*!
+ \overload
+
+ This convenience function creates a new action with \a text.
+ The function adds the newly created action to the menu's
+ list of actions, and returns it.
+
+ \sa QWidget::addAction(), QWidget::actions()
+*/
+QAction *QMenuBar::addAction(const QString &text)
+{
+ QAction *ret = new QAction(text, this);
+ addAction(ret);
+ return ret;
+}
+
+/*!
+ \overload
+
+ This convenience function creates a new action with the given \a
+ text. The action's triggered() signal is connected to the \a
+ receiver's \a member slot. The function adds the newly created
+ action to the menu's list of actions and returns it.
+
+ \sa QWidget::addAction(), QWidget::actions()
+*/
+QAction *QMenuBar::addAction(const QString &text, const QObject *receiver, const char* member)
+{
+ QAction *ret = new QAction(text, this);
+ QObject::connect(ret, SIGNAL(triggered(bool)), receiver, member);
+ addAction(ret);
+ return ret;
+}
+
+/*!
+ Appends a new QMenu with \a title to the menu bar. The menu bar
+ takes ownership of the menu. Returns the new menu.
+
+ \sa QWidget::addAction() QMenu::menuAction()
+*/
+QMenu *QMenuBar::addMenu(const QString &title)
+{
+ QMenu *menu = new QMenu(title, this);
+ addAction(menu->menuAction());
+ return menu;
+}
+
+/*!
+ Appends a new QMenu with \a icon and \a title to the menu bar. The menu bar
+ takes ownership of the menu. Returns the new menu.
+
+ \sa QWidget::addAction() QMenu::menuAction()
+*/
+QMenu *QMenuBar::addMenu(const QIcon &icon, const QString &title)
+{
+ QMenu *menu = new QMenu(title, this);
+ menu->setIcon(icon);
+ addAction(menu->menuAction());
+ return menu;
+}
+
+/*!
+ Appends \a menu to the menu bar. Returns the menu's menuAction().
+
+ \note The returned QAction object can be used to hide the corresponding
+ menu.
+
+ \sa QWidget::addAction() QMenu::menuAction()
+*/
+QAction *QMenuBar::addMenu(QMenu *menu)
+{
+ QAction *action = menu->menuAction();
+ addAction(action);
+ return action;
+}
+
+/*!
+ Appends a separator to the menu.
+*/
+QAction *QMenuBar::addSeparator()
+{
+ QAction *ret = new QAction(this);
+ ret->setSeparator(true);
+ addAction(ret);
+ return ret;
+}
+
+/*!
+ This convenience function creates a new separator action, i.e. an
+ action with QAction::isSeparator() returning true. The function inserts
+ the newly created action into this menu bar's list of actions before
+ action \a before and returns it.
+
+ \sa QWidget::insertAction(), addSeparator()
+*/
+QAction *QMenuBar::insertSeparator(QAction *before)
+{
+ QAction *action = new QAction(this);
+ action->setSeparator(true);
+ insertAction(before, action);
+ return action;
+}
+
+/*!
+ This convenience function inserts \a menu before action \a before
+ and returns the menus menuAction().
+
+ \sa QWidget::insertAction() addMenu()
+*/
+QAction *QMenuBar::insertMenu(QAction *before, QMenu *menu)
+{
+ QAction *action = menu->menuAction();
+ insertAction(before, action);
+ return action;
+}
+
+/*!
+ Returns the QAction that is currently highlighted. A null pointer
+ will be returned if no action is currently selected.
+*/
+QAction *QMenuBar::activeAction() const
+{
+ Q_D(const QMenuBar);
+ return d->currentAction;
+}
+
+/*!
+ \since 4.1
+
+ Sets the currently highlighted action to \a act.
+*/
+void QMenuBar::setActiveAction(QAction *act)
+{
+ Q_D(QMenuBar);
+ d->setCurrentAction(act, true, false);
+}
+
+
+/*!
+ Removes all the actions from the menu bar.
+
+ \sa removeAction()
+*/
+void QMenuBar::clear()
+{
+ QList<QAction*> acts = actions();
+ for(int i = 0; i < acts.size(); i++)
+ removeAction(acts[i]);
+}
+
+/*!
+ \property QMenuBar::defaultUp
+ \brief the popup orientation
+
+ The default popup orientation. By default, menus pop "down" the
+ screen. By setting the property to true, the menu will pop "up".
+ You might call this for menus that are \e below the document to
+ which they refer.
+
+ If the menu would not fit on the screen, the other direction is
+ used automatically.
+*/
+void QMenuBar::setDefaultUp(bool b)
+{
+ Q_D(QMenuBar);
+ d->defaultPopDown = !b;
+}
+
+bool QMenuBar::isDefaultUp() const
+{
+ Q_D(const QMenuBar);
+ return !d->defaultPopDown;
+}
+
+/*!
+ \reimp
+*/
+void QMenuBar::resizeEvent(QResizeEvent *)
+{
+ Q_D(QMenuBar);
+ d->itemsDirty = true;
+ d->updateGeometries();
+}
+
+/*!
+ \reimp
+*/
+void QMenuBar::paintEvent(QPaintEvent *e)
+{
+ Q_D(QMenuBar);
+ QPainter p(this);
+ QRegion emptyArea(rect());
+
+ //draw the items
+ for (int i = 0; i < d->actionList.count(); ++i) {
+ QAction *action = d->actionList.at(i);
+ QRect adjustedActionRect = d->actionRect(action);
+ if (adjustedActionRect.isEmpty() || !d->isVisible(action))
+ continue;
+ if(!e->rect().intersects(adjustedActionRect))
+ continue;
+
+ emptyArea -= adjustedActionRect;
+ QStyleOptionMenuItem opt;
+ initStyleOption(&opt, action);
+ opt.rect = adjustedActionRect;
+ p.setClipRect(adjustedActionRect);
+ style()->drawControl(QStyle::CE_MenuBarItem, &opt, &p, this);
+ }
+ //draw border
+ if(int fw = style()->pixelMetric(QStyle::PM_MenuBarPanelWidth, 0, this)) {
+ QRegion borderReg;
+ borderReg += QRect(0, 0, fw, height()); //left
+ borderReg += QRect(width()-fw, 0, fw, height()); //right
+ borderReg += QRect(0, 0, width(), fw); //top
+ borderReg += QRect(0, height()-fw, width(), fw); //bottom
+ p.setClipRegion(borderReg);
+ emptyArea -= borderReg;
+ QStyleOptionFrame frame;
+ frame.rect = rect();
+ frame.palette = palette();
+ frame.state = QStyle::State_None;
+ frame.lineWidth = style()->pixelMetric(QStyle::PM_MenuBarPanelWidth);
+ frame.midLineWidth = 0;
+ style()->drawPrimitive(QStyle::PE_PanelMenuBar, &frame, &p, this);
+ }
+ p.setClipRegion(emptyArea);
+ QStyleOptionMenuItem menuOpt;
+ menuOpt.palette = palette();
+ menuOpt.state = QStyle::State_None;
+ menuOpt.menuItemType = QStyleOptionMenuItem::EmptyArea;
+ menuOpt.checkType = QStyleOptionMenuItem::NotCheckable;
+ menuOpt.rect = rect();
+ menuOpt.menuRect = rect();
+ style()->drawControl(QStyle::CE_MenuBarEmptyArea, &menuOpt, &p, this);
+}
+
+/*!
+ \reimp
+*/
+void QMenuBar::setVisible(bool visible)
+{
+#ifdef Q_WS_MAC
+ Q_D(QMenuBar);
+ if(d->mac_menubar)
+ return;
+#endif
+#ifdef Q_OS_WINCE
+ Q_D(QMenuBar);
+ if(d->wce_menubar)
+ return;
+#endif
+ QWidget::setVisible(visible);
+}
+
+/*!
+ \reimp
+*/
+void QMenuBar::mousePressEvent(QMouseEvent *e)
+{
+ Q_D(QMenuBar);
+ if(e->button() != Qt::LeftButton)
+ return;
+
+ QAction *action = d->actionAt(e->pos());
+ if (!action || !d->isVisible(action)) {
+ d->setCurrentAction(0);
+#ifndef QT_NO_WHATSTHIS
+ if (QWhatsThis::inWhatsThisMode())
+ QWhatsThis::showText(e->globalPos(), d->whatsThis, this);
+#endif
+ return;
+ }
+
+ d->mouseDown = true;
+
+ if(d->currentAction == action && d->popupState) {
+ if(QMenu *menu = d->activeMenu) {
+ d->activeMenu = 0;
+ menu->hide();
+ }
+#ifdef Q_WS_WIN
+ if((d->closePopupMode = style()->styleHint(QStyle::SH_MenuBar_DismissOnSecondClick)))
+ update(d->actionRect(action));
+#endif
+ } else {
+ d->setCurrentAction(action, true);
+ }
+}
+
+/*!
+ \reimp
+*/
+void QMenuBar::mouseReleaseEvent(QMouseEvent *e)
+{
+ Q_D(QMenuBar);
+ if(e->button() != Qt::LeftButton || !d->mouseDown)
+ return;
+
+ d->mouseDown = false;
+ QAction *action = d->actionAt(e->pos());
+ if((d->closePopupMode && action == d->currentAction) || !action || !action->menu()) {
+ //we set the current action before activating
+ //so that we let the leave event set the current back to 0
+ d->setCurrentAction(action, false);
+ if(action)
+ d->activateAction(action, QAction::Trigger);
+ }
+ d->closePopupMode = 0;
+}
+
+/*!
+ \reimp
+*/
+void QMenuBar::keyPressEvent(QKeyEvent *e)
+{
+ Q_D(QMenuBar);
+ int key = e->key();
+ if(isRightToLeft()) { // in reverse mode open/close key for submenues are reversed
+ if(key == Qt::Key_Left)
+ key = Qt::Key_Right;
+ else if(key == Qt::Key_Right)
+ key = Qt::Key_Left;
+ }
+ if(key == Qt::Key_Tab) //means right
+ key = Qt::Key_Right;
+ else if(key == Qt::Key_Backtab) //means left
+ key = Qt::Key_Left;
+
+ bool key_consumed = false;
+ switch(key) {
+ case Qt::Key_Up:
+ case Qt::Key_Down:
+ case Qt::Key_Enter:
+ case Qt::Key_Space:
+ case Qt::Key_Return: {
+ if(!style()->styleHint(QStyle::SH_MenuBar_AltKeyNavigation, 0, this) || !d->currentAction)
+ break;
+ if(d->currentAction->menu()) {
+ d->popupAction(d->currentAction, true);
+ } else if(key == Qt::Key_Enter || key == Qt::Key_Return || key == Qt::Key_Space) {
+ d->activateAction(d->currentAction, QAction::Trigger);
+ d->setCurrentAction(d->currentAction, false);
+ d->setKeyboardMode(false);
+ }
+ key_consumed = true;
+ break; }
+
+ case Qt::Key_Right:
+ case Qt::Key_Left: {
+ if(d->currentAction) {
+ QAction *nextAction = 0;
+ bool allowActiveAndDisabled =
+ style()->styleHint(QStyle::SH_Menu_AllowActiveAndDisabled, 0, this);
+
+ for(int i=0; i<(int)d->actionList.count(); i++) {
+ if(d->actionList.at(i) == (QAction*)d->currentAction) {
+ if (key == Qt::Key_Left) {
+ while (i > 0) {
+ i--;
+ if (allowActiveAndDisabled || d->actionList[i]->isEnabled()) {
+ nextAction = d->actionList.at(i);
+ break;
+ }
+ }
+ } else {
+ while (i < d->actionList.count()-1) {
+ i++;
+ if (allowActiveAndDisabled || d->actionList[i]->isEnabled()) {
+ nextAction = d->actionList.at(i);
+ break;
+ }
+ }
+ }
+ break;
+
+ }
+ }
+
+ if(!nextAction) {
+ if (key == Qt::Key_Left) {
+ for (int i = d->actionList.size() - 1 ; i >= 0 ; --i) {
+ if (allowActiveAndDisabled || d->actionList[i]->isEnabled()) {
+ nextAction = d->actionList.at(i);
+ i--;
+ break;
+ }
+ }
+ } else {
+ for (int i = 0 ; i < d->actionList.count() ; ++i) {
+ if (allowActiveAndDisabled || d->actionList[i]->isEnabled()) {
+ nextAction = d->actionList.at(i);
+ i++;
+ break;
+ }
+ }
+ }
+ }
+ if(nextAction) {
+ d->setCurrentAction(nextAction, d->popupState, true);
+ key_consumed = true;
+ }
+ }
+ break; }
+
+ case Qt::Key_Escape:
+ d->setCurrentAction(0);
+ d->setKeyboardMode(false);
+ key_consumed = true;
+ break;
+
+ default:
+ key_consumed = false;
+ }
+
+ if(!key_consumed &&
+ (!e->modifiers() ||
+ (e->modifiers()&(Qt::MetaModifier|Qt::AltModifier))) && e->text().length()==1 && !d->popupState) {
+ int clashCount = 0;
+ QAction *first = 0, *currentSelected = 0, *firstAfterCurrent = 0;
+ {
+ QChar c = e->text()[0].toUpper();
+ for(int i = 0; i < d->actionList.size(); ++i) {
+ register QAction *act = d->actionList.at(i);
+ QString s = act->text();
+ if(!s.isEmpty()) {
+ int ampersand = s.indexOf(QLatin1Char('&'));
+ if(ampersand >= 0) {
+ if(s[ampersand+1].toUpper() == c) {
+ clashCount++;
+ if(!first)
+ first = act;
+ if(act == d->currentAction)
+ currentSelected = act;
+ else if (!firstAfterCurrent && currentSelected)
+ firstAfterCurrent = act;
+ }
+ }
+ }
+ }
+ }
+ QAction *next_action = 0;
+ if(clashCount >= 1) {
+ if(clashCount == 1 || !d->currentAction || (currentSelected && !firstAfterCurrent))
+ next_action = first;
+ else
+ next_action = firstAfterCurrent;
+ }
+ if(next_action) {
+ key_consumed = true;
+ d->setCurrentAction(next_action, true, true);
+ }
+ }
+ if(key_consumed)
+ e->accept();
+ else
+ e->ignore();
+}
+
+/*!
+ \reimp
+*/
+void QMenuBar::mouseMoveEvent(QMouseEvent *e)
+{
+ Q_D(QMenuBar);
+ d->mouseDown = e->buttons() & Qt::LeftButton;
+ QAction *action = d->actionAt(e->pos());
+ bool popupState = d->popupState || d->mouseDown;
+ if ((action && d->isVisible(action)) || !popupState)
+ d->setCurrentAction(action, popupState);
+}
+
+/*!
+ \reimp
+*/
+void QMenuBar::leaveEvent(QEvent *)
+{
+ Q_D(QMenuBar);
+ if(!hasFocus() && !d->popupState)
+ d->setCurrentAction(0);
+}
+
+/*!
+ \reimp
+*/
+void QMenuBar::actionEvent(QActionEvent *e)
+{
+ Q_D(QMenuBar);
+ d->itemsDirty = true;
+#ifdef Q_WS_MAC
+ if(d->mac_menubar) {
+ if(e->type() == QEvent::ActionAdded)
+ d->mac_menubar->addAction(e->action(), d->mac_menubar->findAction(e->before()));
+ else if(e->type() == QEvent::ActionRemoved)
+ d->mac_menubar->removeAction(e->action());
+ else if(e->type() == QEvent::ActionChanged)
+ d->mac_menubar->syncAction(e->action());
+ }
+#endif
+#ifdef Q_OS_WINCE
+ if(d->wce_menubar) {
+ if(e->type() == QEvent::ActionAdded)
+ d->wce_menubar->addAction(e->action(), d->wce_menubar->findAction(e->before()));
+ else if(e->type() == QEvent::ActionRemoved)
+ d->wce_menubar->removeAction(e->action());
+ else if(e->type() == QEvent::ActionChanged)
+ d->wce_menubar->syncAction(e->action());
+ }
+#endif
+ if(e->type() == QEvent::ActionAdded) {
+ connect(e->action(), SIGNAL(triggered()), this, SLOT(_q_actionTriggered()));
+ connect(e->action(), SIGNAL(hovered()), this, SLOT(_q_actionHovered()));
+ } else if(e->type() == QEvent::ActionRemoved) {
+ e->action()->disconnect(this);
+ }
+ if (isVisible()) {
+ d->updateGeometries();
+ update();
+ }
+}
+
+/*!
+ \reimp
+*/
+void QMenuBar::focusInEvent(QFocusEvent *)
+{
+ Q_D(QMenuBar);
+ if(d->keyboardState && !d->currentAction && !d->actionList.isEmpty())
+ d->setCurrentAction(d->actionList.first());
+}
+
+/*!
+ \reimp
+*/
+void QMenuBar::focusOutEvent(QFocusEvent *)
+{
+ Q_D(QMenuBar);
+ if(!d->popupState) {
+ d->setCurrentAction(0);
+ d->setKeyboardMode(false);
+ }
+}
+
+/*!
+ \reimp
+ */
+void QMenuBar::timerEvent (QTimerEvent *e)
+{
+ Q_D(QMenuBar);
+ if (e->timerId() == d->autoReleaseTimer.timerId()) {
+ d->autoReleaseTimer.stop();
+ d->setCurrentAction(0);
+ }
+ QWidget::timerEvent(e);
+}
+
+
+void QMenuBarPrivate::handleReparent()
+{
+ Q_Q(QMenuBar);
+ QWidget *newParent = q->parentWidget();
+ //Note: if parent is reparented, then window may change even if parent doesn't
+
+ // we need to install an event filter on parent, and remove the old one
+
+ if (oldParent != newParent) {
+ if (oldParent)
+ oldParent->removeEventFilter(q);
+ if (newParent)
+ newParent->installEventFilter(q);
+ }
+
+ //we also need event filter on top-level (for shortcuts)
+ QWidget *newWindow = newParent ? newParent->window() : 0;
+
+ if (oldWindow != newWindow) {
+ if (oldParent && oldParent != oldWindow)
+ oldWindow->removeEventFilter(q);
+
+ if (newParent && newParent != newWindow)
+ newWindow->installEventFilter(q);
+ }
+
+ oldParent = newParent;
+ oldWindow = newWindow;
+
+#ifdef Q_WS_MAC
+ macDestroyMenuBar();
+ macCreateMenuBar(newParent);
+#endif
+
+#ifdef Q_OS_WINCE
+ if (qt_wince_is_mobile() && wce_menubar)
+ wce_menubar->rebuild();
+#endif
+}
+
+#ifdef QT3_SUPPORT
+/*!
+ Sets whether the menu bar should automatically resize itself
+ when its parent widget is resized.
+
+ This feature is provided to help porting to Qt 4. We recommend
+ against using it in new code.
+
+ \sa autoGeometry()
+*/
+void QMenuBar::setAutoGeometry(bool b)
+{
+ Q_D(QMenuBar);
+ d->doAutoResize = b;
+}
+
+/*!
+ Returns true if the menu bar automatically resizes itself
+ when its parent widget is resized; otherwise returns false.
+
+ This feature is provided to help porting to Qt 4. We recommend
+ against using it in new code.
+
+ \sa setAutoGeometry()
+*/
+bool QMenuBar::autoGeometry() const
+{
+ Q_D(const QMenuBar);
+ return d->doAutoResize;
+}
+#endif
+
+/*!
+ \reimp
+*/
+void QMenuBar::changeEvent(QEvent *e)
+{
+ Q_D(QMenuBar);
+ if(e->type() == QEvent::StyleChange) {
+ d->itemsDirty = true;
+ setMouseTracking(style()->styleHint(QStyle::SH_MenuBar_MouseTracking, 0, this));
+ if(parentWidget())
+ resize(parentWidget()->width(), heightForWidth(parentWidget()->width()));
+ d->updateGeometries();
+ } else if (e->type() == QEvent::ParentChange) {
+ d->handleReparent();
+ } else if (e->type() == QEvent::FontChange
+ || e->type() == QEvent::ApplicationFontChange) {
+ d->itemsDirty = true;
+ d->updateGeometries();
+ }
+ QWidget::changeEvent(e);
+}
+
+/*!
+ \reimp
+*/
+bool QMenuBar::event(QEvent *e)
+{
+ Q_D(QMenuBar);
+ switch (e->type()) {
+ case QEvent::KeyPress: {
+ QKeyEvent *ke = (QKeyEvent*)e;
+#if 0
+ if(!d->keyboardState) { //all keypresses..
+ d->setCurrentAction(0);
+ return ;
+ }
+#endif
+ if(ke->key() == Qt::Key_Tab || ke->key() == Qt::Key_Backtab) {
+ keyPressEvent(ke);
+ return true;
+ }
+
+ } break;
+#ifndef QT_NO_SHORTCUT
+ case QEvent::Shortcut: {
+ QShortcutEvent *se = static_cast<QShortcutEvent *>(e);
+ int shortcutId = se->shortcutId();
+ for(int j = 0; j < d->shortcutIndexMap.size(); ++j) {
+ if (shortcutId == d->shortcutIndexMap.value(j))
+ d->_q_internalShortcutActivated(j);
+ }
+ } break;
+#endif
+ case QEvent::Show:
+#ifdef QT3_SUPPORT
+ if(QWidget *p = parentWidget()) {
+ // If itemsDirty == true, updateGeometries sends the MenubarUpdated event.
+ if (!d->itemsDirty) {
+ QMenubarUpdatedEvent menubarUpdated(this);
+ QApplication::sendEvent(p, &menubarUpdated);
+ }
+ }
+#endif
+ d->_q_updateLayout();
+ break;
+ case QEvent::ShortcutOverride: {
+ QKeyEvent *kev = static_cast<QKeyEvent*>(e);
+ if (kev->key() == Qt::Key_Escape) {
+ e->accept();
+ return true;
+ }
+ }
+ break;
+
+#ifdef QT3_SUPPORT
+ case QEvent::Hide: {
+ if(QWidget *p = parentWidget()) {
+ QMenubarUpdatedEvent menubarUpdated(this);
+ QApplication::sendEvent(p, &menubarUpdated);
+ }
+ } break;
+#endif
+
+#ifndef QT_NO_WHATSTHIS
+ case QEvent::QueryWhatsThis:
+ e->setAccepted(d->whatsThis.size());
+ if (QAction *action = d->actionAt(static_cast<QHelpEvent*>(e)->pos())) {
+ if (action->whatsThis().size() || action->menu())
+ e->accept();
+ }
+ return true;
+#endif
+ case QEvent::LayoutDirectionChange:
+ d->_q_updateLayout();
+ break;
+ default:
+ break;
+ }
+ return QWidget::event(e);
+}
+
+/*!
+ \reimp
+*/
+bool QMenuBar::eventFilter(QObject *object, QEvent *event)
+{
+ Q_D(QMenuBar);
+ if (object == parent() && object) {
+#ifdef QT3_SUPPORT
+ if (d->doAutoResize && event->type() == QEvent::Resize) {
+ QResizeEvent *e = (QResizeEvent *)event;
+ int w = e->size().width();
+ setGeometry(0, y(), w, heightForWidth(w));
+ return false;
+ }
+#endif
+ if (event->type() == QEvent::ParentChange) //GrandparentChange
+ d->handleReparent();
+ }
+ if (object == d->leftWidget || object == d->rightWidget) {
+ switch (event->type()) {
+ case QEvent::ShowToParent:
+ case QEvent::HideToParent:
+ d->_q_updateLayout();
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (style()->styleHint(QStyle::SH_MenuBar_AltKeyNavigation, 0, this)) {
+ if (d->altPressed) {
+ switch (event->type()) {
+ case QEvent::KeyPress:
+ case QEvent::KeyRelease:
+ {
+ QKeyEvent *kev = static_cast<QKeyEvent*>(event);
+ if (kev->key() == Qt::Key_Alt || kev->key() == Qt::Key_Meta) {
+ if (event->type() == QEvent::KeyPress) // Alt-press does not interest us, we have the shortcut-override event
+ break;
+ d->setKeyboardMode(!d->keyboardState);
+ }
+ }
+ // fall through
+ case QEvent::MouseButtonPress:
+ case QEvent::MouseButtonRelease:
+ case QEvent::MouseMove:
+ case QEvent::FocusIn:
+ case QEvent::FocusOut:
+ case QEvent::ActivationChange:
+ d->altPressed = false;
+ qApp->removeEventFilter(this);
+ break;
+ default:
+ break;
+ }
+ } else if (isVisible()) {
+ if (event->type() == QEvent::ShortcutOverride) {
+ QKeyEvent *kev = static_cast<QKeyEvent*>(event);
+ if ((kev->key() == Qt::Key_Alt || kev->key() == Qt::Key_Meta)
+ && kev->modifiers() == Qt::AltModifier) {
+ d->altPressed = true;
+ qApp->installEventFilter(this);
+ }
+ }
+ }
+ }
+
+ return false;
+}
+
+/*!
+ \internal
+
+ Return the item at \a pt, or 0 if there is no item there or if it is
+ a separator item.
+*/
+QAction *QMenuBar::actionAt(const QPoint &pt) const
+{
+ Q_D(const QMenuBar);
+ return d->actionAt(pt);
+}
+
+/*!
+ \internal
+
+ Returns the geometry of action \a act.
+*/
+QRect QMenuBar::actionGeometry(QAction *act) const
+{
+ Q_D(const QMenuBar);
+ return d->actionRect(act);
+}
+
+/*!
+ \reimp
+*/
+QSize QMenuBar::minimumSizeHint() const
+{
+ Q_D(const QMenuBar);
+#ifdef Q_WS_MAC
+ const bool as_gui_menubar = !d->mac_menubar;
+#elif defined (Q_OS_WINCE)
+ const bool as_gui_menubar = !d->wce_menubar;
+#else
+ const bool as_gui_menubar = true;
+#endif
+
+ ensurePolished();
+ QSize ret(0, 0);
+ const int hmargin = style()->pixelMetric(QStyle::PM_MenuBarHMargin, 0, this);
+ const int vmargin = style()->pixelMetric(QStyle::PM_MenuBarVMargin, 0, this);
+ int fw = style()->pixelMetric(QStyle::PM_MenuBarPanelWidth, 0, this);
+ int spaceBelowMenuBar = style()->styleHint(QStyle::SH_MainWindow_SpaceBelowMenuBar, 0, this);
+ if(as_gui_menubar) {
+ QMap<QAction*, QRect> actionRects;
+ QList<QAction*> actionList;
+ int w = parentWidget() ? parentWidget()->width() : QApplication::desktop()->width();
+ d->calcActionRects(w - (2 * fw), 0, actionRects, actionList);
+ if (d->actionList.count() > 0) {
+ ret = d->actionRect(d->actionList.at(0)).size();
+ if (!d->extension->isHidden())
+ ret += QSize(d->extension->sizeHint().width(), 0);
+ }
+ ret += QSize(2*fw + hmargin, 2*fw + vmargin);
+ }
+ int margin = 2*vmargin + 2*fw + spaceBelowMenuBar;
+ if(d->leftWidget) {
+ QSize sz = d->leftWidget->minimumSizeHint();
+ ret.setWidth(ret.width() + sz.width());
+ if(sz.height() + margin > ret.height())
+ ret.setHeight(sz.height() + margin);
+ }
+ if(d->rightWidget) {
+ QSize sz = d->rightWidget->minimumSizeHint();
+ ret.setWidth(ret.width() + sz.width());
+ if(sz.height() + margin > ret.height())
+ ret.setHeight(sz.height() + margin);
+ }
+ if(as_gui_menubar) {
+ QStyleOptionMenuItem opt;
+ opt.rect = rect();
+ opt.menuRect = rect();
+ opt.state = QStyle::State_None;
+ opt.menuItemType = QStyleOptionMenuItem::Normal;
+ opt.checkType = QStyleOptionMenuItem::NotCheckable;
+ opt.palette = palette();
+ return (style()->sizeFromContents(QStyle::CT_MenuBar, &opt,
+ ret.expandedTo(QApplication::globalStrut()),
+ this));
+ }
+ return ret;
+}
+
+/*!
+ \reimp
+*/
+QSize QMenuBar::sizeHint() const
+{
+ Q_D(const QMenuBar);
+#ifdef Q_WS_MAC
+ const bool as_gui_menubar = !d->mac_menubar;
+#elif defined (Q_OS_WINCE)
+ const bool as_gui_menubar = !d->wce_menubar;
+#else
+ const bool as_gui_menubar = true;
+#endif
+
+ ensurePolished();
+ QSize ret(0, 0);
+ const int hmargin = style()->pixelMetric(QStyle::PM_MenuBarHMargin, 0, this);
+ const int vmargin = style()->pixelMetric(QStyle::PM_MenuBarVMargin, 0, this);
+ int fw = style()->pixelMetric(QStyle::PM_MenuBarPanelWidth, 0, this);
+ int spaceBelowMenuBar = style()->styleHint(QStyle::SH_MainWindow_SpaceBelowMenuBar, 0, this);
+ if(as_gui_menubar) {
+ QMap<QAction*, QRect> actionRects;
+ QList<QAction*> actionList;
+ const int w = parentWidget() ? parentWidget()->width() : QApplication::desktop()->width();
+ d->calcActionRects(w - (2 * fw), 0, actionRects, actionList);
+ for (QMap<QAction*, QRect>::const_iterator i = actionRects.constBegin();
+ i != actionRects.constEnd(); ++i) {
+ QRect actionRect(i.value());
+ if(actionRect.x() + actionRect.width() > ret.width())
+ ret.setWidth(actionRect.x() + actionRect.width());
+ if(actionRect.y() + actionRect.height() > ret.height())
+ ret.setHeight(actionRect.y() + actionRect.height());
+ }
+ ret += QSize(2*fw + 2*hmargin, 2*fw + 2*vmargin);
+ }
+ int margin = 2*vmargin + 2*fw + spaceBelowMenuBar;
+ if(d->leftWidget) {
+ QSize sz = d->leftWidget->sizeHint();
+ ret.setWidth(ret.width() + sz.width());
+ if(sz.height() + margin > ret.height())
+ ret.setHeight(sz.height() + margin);
+ }
+ if(d->rightWidget) {
+ QSize sz = d->rightWidget->sizeHint();
+ ret.setWidth(ret.width() + sz.width());
+ if(sz.height() + margin > ret.height())
+ ret.setHeight(sz.height() + margin);
+ }
+ if(as_gui_menubar) {
+ QStyleOptionMenuItem opt;
+ opt.rect = rect();
+ opt.menuRect = rect();
+ opt.state = QStyle::State_None;
+ opt.menuItemType = QStyleOptionMenuItem::Normal;
+ opt.checkType = QStyleOptionMenuItem::NotCheckable;
+ opt.palette = palette();
+ return (style()->sizeFromContents(QStyle::CT_MenuBar, &opt,
+ ret.expandedTo(QApplication::globalStrut()),
+ this));
+ }
+ return ret;
+}
+
+/*!
+ \reimp
+*/
+int QMenuBar::heightForWidth(int) const
+{
+ Q_D(const QMenuBar);
+#ifdef Q_WS_MAC
+ const bool as_gui_menubar = !d->mac_menubar;
+#elif defined (Q_OS_WINCE)
+ const bool as_gui_menubar = !d->wce_menubar;
+#else
+ const bool as_gui_menubar = true;
+#endif
+ int height = 0;
+ const int vmargin = style()->pixelMetric(QStyle::PM_MenuBarVMargin, 0, this);
+ int fw = style()->pixelMetric(QStyle::PM_MenuBarPanelWidth, 0, this);
+ int spaceBelowMenuBar = style()->styleHint(QStyle::SH_MainWindow_SpaceBelowMenuBar, 0, this);
+ if(as_gui_menubar) {
+ if (d->actionList.count()) {
+ // assume all actionrects have the same height
+ height = d->actionRect(d->actionList.first()).height();
+ height += spaceBelowMenuBar;
+ }
+ height += 2*fw;
+ height += 2*vmargin;
+ }
+ int margin = 2*vmargin + 2*fw + spaceBelowMenuBar;
+ if(d->leftWidget)
+ height = qMax(d->leftWidget->sizeHint().height() + margin, height);
+ if(d->rightWidget)
+ height = qMax(d->rightWidget->sizeHint().height() + margin, height);
+ if(as_gui_menubar) {
+ QStyleOptionMenuItem opt;
+ opt.init(this);
+ opt.menuRect = rect();
+ opt.state = QStyle::State_None;
+ opt.menuItemType = QStyleOptionMenuItem::Normal;
+ opt.checkType = QStyleOptionMenuItem::NotCheckable;
+ return style()->sizeFromContents(QStyle::CT_MenuBar, &opt, QSize(0, height), this).height(); //not pretty..
+ }
+ return height;
+}
+
+/*!
+ \internal
+*/
+void QMenuBarPrivate::_q_internalShortcutActivated(int id)
+{
+ Q_Q(QMenuBar);
+ QAction *act = actionList.at(id);
+ setCurrentAction(act, true, true);
+ if (act && !act->menu()) {
+ activateAction(act, QAction::Trigger);
+ //100 is the same as the default value in QPushButton::animateClick
+ autoReleaseTimer.start(100, q);
+ }
+}
+
+void QMenuBarPrivate::_q_updateLayout()
+{
+ Q_Q(QMenuBar);
+ itemsDirty = true;
+ if (q->isVisible()) {
+ updateGeometries();
+ q->update();
+ }
+}
+
+/*!
+ \internal
+
+ This sets widget \a w to be shown directly on the left of the first or
+ the right of the last menu item, depending on \a corner.
+*/
+void QMenuBar::setCornerWidget(QWidget *w, Qt::Corner corner)
+{
+ Q_D(QMenuBar);
+ switch (corner) {
+ case Qt::TopLeftCorner:
+ if (d->leftWidget)
+ d->leftWidget->removeEventFilter(this);
+ d->leftWidget = w;
+ break;
+ case Qt::TopRightCorner:
+ if (d->rightWidget)
+ d->rightWidget->removeEventFilter(this);
+ d->rightWidget = w;
+ break;
+ default:
+ qWarning("QMenuBar::setCornerWidget: Only TopLeftCorner and TopRightCorner are supported");
+ return;
+ }
+
+ if (w) {
+ w->setParent(this);
+ w->installEventFilter(this);
+ }
+
+ d->_q_updateLayout();
+}
+
+/*!
+ \internal
+
+ Returns the widget in the left of the first or the right of the last menu
+ item, depending on \a corner.
+*/
+QWidget *QMenuBar::cornerWidget(Qt::Corner corner) const
+{
+ Q_D(const QMenuBar);
+ QWidget *w = 0;
+ switch(corner) {
+ case Qt::TopLeftCorner:
+ w = d->leftWidget;
+ break;
+ case Qt::TopRightCorner:
+ w = d->rightWidget;
+ break;
+ default:
+ qWarning("QMenuBar::cornerWidget: Only TopLeftCorner and TopRightCorner are supported");
+ break;
+ }
+
+ return w;
+}
+
+/*!
+ \since 4.4
+
+ Sets the default action to \a act.
+
+ The default action is assigned to the left soft key. The menu is assigned
+ to the right soft key.
+
+ Currently there is only support for the default action on Windows
+ Mobile. All other platforms ignore the default action.
+
+ \sa defaultAction()
+*/
+
+#ifdef Q_OS_WINCE
+void QMenuBar::setDefaultAction(QAction *act)
+{
+ Q_D(QMenuBar);
+ if (d->defaultAction == act)
+ return;
+#ifdef Q_OS_WINCE
+ if (qt_wince_is_mobile())
+ if (d->defaultAction) {
+ disconnect(d->defaultAction, SIGNAL(changed()), this, SLOT(_q_updateDefaultAction()));
+ disconnect(d->defaultAction, SIGNAL(destroyed ()), this, SLOT(_q_updateDefaultAction()));
+ }
+#endif
+ d->defaultAction = act;
+#ifdef Q_OS_WINCE
+ if (qt_wince_is_mobile())
+ if (d->defaultAction) {
+ connect(d->defaultAction, SIGNAL(changed()), this, SLOT(_q_updateDefaultAction()));
+ connect(d->defaultAction, SIGNAL(destroyed()), this, SLOT(_q_updateDefaultAction()));
+ }
+ if (d->wce_menubar) {
+ d->wce_menubar->rebuild();
+ }
+#endif
+}
+
+/*!
+ \since 4.4
+
+ Returns the current default action.
+
+ \sa setDefaultAction()
+*/
+QAction *QMenuBar::defaultAction() const
+{
+ return d_func()->defaultAction;
+}
+#endif
+
+/*!
+ \fn void QMenuBar::triggered(QAction *action)
+
+ This signal is emitted when an action in a menu belonging to this menubar
+ is triggered as a result of a mouse click; \a action is the action that
+ caused the signal to be emitted.
+
+ Normally, you connect each menu action to a single slot using
+ QAction::triggered(), but sometimes you will want to connect
+ several items to a single slot (most often if the user selects
+ from an array). This signal is useful in such cases.
+
+ \sa hovered(), QAction::triggered()
+*/
+
+/*!
+ \fn void QMenuBar::hovered(QAction *action)
+
+ This signal is emitted when a menu action is highlighted; \a action
+ is the action that caused the event to be sent.
+
+ Often this is used to update status information.
+
+ \sa triggered(), QAction::hovered()
+*/
+
+
+#ifdef QT3_SUPPORT
+/*!
+ Use style()->pixelMetric(QStyle::PM_MenuBarPanelWidth, this)
+ instead.
+*/
+int QMenuBar::frameWidth() const
+{
+ return style()->pixelMetric(QStyle::PM_MenuBarPanelWidth, 0, this);
+}
+
+int QMenuBar::insertAny(const QIcon *icon, const QString *text, const QObject *receiver, const char *member,
+ const QKeySequence *shortcut, const QMenu *popup, int id, int index)
+{
+ QAction *act = popup ? popup->menuAction() : new QAction(this);
+ if(id != -1)
+ static_cast<QMenuItem*>(act)->setId(id);
+ if(icon)
+ act->setIcon(*icon);
+ if(text)
+ act->setText(*text);
+ if(shortcut)
+ act->setShortcut(*shortcut);
+ if(receiver && member)
+ QObject::connect(act, SIGNAL(triggered(bool)), receiver, member);
+ if(index == -1 || index >= actions().count())
+ addAction(act);
+ else
+ insertAction(actions().value(index), act);
+ return findIdForAction(act);
+}
+
+/*!
+ \since 4.2
+
+ Use addSeparator() or insertAction() instead.
+
+ \oldcode
+ menuBar->insertSeparator();
+ \newcode
+ menuBar->addSeparator();
+ \endcode
+*/
+int QMenuBar::insertSeparator(int index)
+{
+ QAction *act = new QAction(this);
+ act->setSeparator(true);
+ if(index == -1 || index >= actions().count())
+ addAction(act);
+ else
+ insertAction(actions().value(index), act);
+ return findIdForAction(act);
+}
+
+/*!
+ Use QAction::setData() instead.
+*/
+bool QMenuBar::setItemParameter(int id, int param)
+{
+ if(QAction *act = findActionForId(id)) {
+ act->d_func()->param = param;
+ return true;
+ }
+ return false;
+}
+
+/*!
+ Use QAction::data() instead.
+*/
+int QMenuBar::itemParameter(int id) const
+{
+ if(QAction *act = findActionForId(id))
+ return act->d_func()->param;
+ return id;
+}
+
+QAction *QMenuBar::findActionForId(int id) const
+{
+ QList<QAction *> list = actions();
+ for (int i = 0; i < list.size(); ++i) {
+ QAction *act = list.at(i);
+ if (findIdForAction(act) == id)
+ return act;
+ }
+ return 0;
+}
+
+int QMenuBar::findIdForAction(QAction *act) const
+{
+ Q_ASSERT(act);
+ return act->d_func()->id;
+}
+#endif
+
+/*!
+ \enum QMenuBar::Separator
+
+ \compat
+
+ \value Never
+ \value InWindowsStyle
+
+*/
+
+/*!
+ \fn void QMenuBar::addAction(QAction *action)
+ \overload
+
+ Appends the action \a action to the menu bar's list of actions.
+
+ \sa QMenu::addAction(), QWidget::addAction(), QWidget::actions()
+*/
+
+/*!
+ \fn uint QMenuBar::count() const
+
+ Use actions().count() instead.
+*/
+
+/*!
+ \fn int QMenuBar::insertItem(const QString &text, const QObject *receiver, const char* member, const QKeySequence& shortcut, int id, int index)
+
+ Use one of the insertAction() or addAction() overloads instead.
+*/
+
+/*!
+ \fn int QMenuBar::insertItem(const QIcon& icon, const QString &text, const QObject *receiver, const char* member, const QKeySequence& shortcut, int id, int index)
+
+ Use one of the insertAction() or addAction() overloads instead.
+*/
+
+/*!
+ \fn int QMenuBar::insertItem(const QPixmap &pixmap, const QObject *receiver, const char* member, const QKeySequence& shortcut, int id, int index)
+
+ Use one of the insertAction(), addAction(), insertMenu(), or
+ addMenu() overloads instead.
+*/
+
+/*!
+ \fn int QMenuBar::insertItem(const QString &text, int id, int index)
+
+ Use one of the insertAction() or addAction() overloads instead.
+*/
+
+/*!
+ \fn int QMenuBar::insertItem(const QIcon& icon, const QString &text, int id, int index)
+
+ Use one of the insertAction(), addAction(), insertMenu(), or
+ addMenu() overloads instead.
+*/
+
+/*!
+ \fn int QMenuBar::insertItem(const QString &text, QMenu *popup, int id, int index)
+
+ Use one of the insertMenu(), or addMenu() overloads instead.
+*/
+
+/*!
+ \fn int QMenuBar::insertItem(const QIcon& icon, const QString &text, QMenu *popup, int id, int index)
+
+ Use one of the insertMenu(), or addMenu() overloads instead.
+*/
+
+/*!
+ \fn int QMenuBar::insertItem(const QPixmap &pixmap, int id, int index)
+
+ Use one of the insertAction(), addAction(), insertMenu(), or
+ addMenu() overloads instead.
+*/
+
+/*!
+ \fn int QMenuBar::insertItem(const QPixmap &pixmap, QMenu *popup, int id, int index)
+
+ Use one of the insertMenu(), or addMenu() overloads instead.
+*/
+
+/*!
+ \fn void QMenuBar::removeItem(int id)
+
+ Use removeAction() instead.
+*/
+
+/*!
+ \fn void QMenuBar::removeItemAt(int index)
+
+ Use removeAction() instead.
+*/
+
+/*!
+ \fn QKeySequence QMenuBar::accel(int id) const
+
+ Use shortcut() on the relevant QAction instead.
+*/
+
+/*!
+ \fn void QMenuBar::setAccel(const QKeySequence& key, int id)
+
+ Use setShortcut() on the relevant QAction instead.
+*/
+
+/*!
+ \fn QIcon QMenuBar::iconSet(int id) const
+
+ Use icon() on the relevant QAction instead.
+*/
+
+/*!
+ \fn QString QMenuBar::text(int id) const
+
+ Use text() on the relevant QAction instead.
+*/
+
+/*!
+ \fn QPixmap QMenuBar::pixmap(int id) const
+
+ Use QPixmap(icon()) on the relevant QAction instead.
+*/
+
+/*!
+ \fn void QMenuBar::setWhatsThis(int id, const QString &w)
+
+ Use setWhatsThis() on the relevant QAction instead.
+*/
+
+/*!
+ \fn QString QMenuBar::whatsThis(int id) const
+
+ Use whatsThis() on the relevant QAction instead.
+*/
+
+/*!
+ \fn void QMenuBar::changeItem(int id, const QString &text)
+
+ Use setText() on the relevant QAction instead.
+*/
+
+/*!
+ \fn void QMenuBar::changeItem(int id, const QPixmap &pixmap)
+
+ Use setText() on the relevant QAction instead.
+*/
+
+/*!
+ \fn void QMenuBar::changeItem(int id, const QIcon &icon, const QString &text)
+
+ Use setIcon() and setText() on the relevant QAction instead.
+*/
+
+/*!
+ \fn bool QMenuBar::isItemActive(int id) const
+
+ Use activeAction() instead.
+*/
+
+/*!
+ \fn bool QMenuBar::isItemEnabled(int id) const
+
+ Use isEnabled() on the relevant QAction instead.
+*/
+
+/*!
+ \fn void QMenuBar::setItemEnabled(int id, bool enable)
+
+ Use setEnabled() on the relevant QAction instead.
+*/
+
+/*!
+ \fn bool QMenuBar::isItemChecked(int id) const
+
+ Use isChecked() on the relevant QAction instead.
+*/
+
+/*!
+ \fn void QMenuBar::setItemChecked(int id, bool check)
+
+ Use setChecked() on the relevant QAction instead.
+*/
+
+/*!
+ \fn bool QMenuBar::isItemVisible(int id) const
+
+ Use isVisible() on the relevant QAction instead.
+*/
+
+/*!
+ \fn void QMenuBar::setItemVisible(int id, bool visible)
+
+ Use setVisible() on the relevant QAction instead.
+*/
+
+/*!
+ \fn int QMenuBar::indexOf(int id) const
+
+ Use actions().indexOf(action) on the relevant QAction instead.
+*/
+
+/*!
+ \fn int QMenuBar::idAt(int index) const
+
+ Use actions instead.
+*/
+
+/*!
+ \fn void QMenuBar::activateItemAt(int index)
+
+ Use activate() on the relevant QAction instead.
+*/
+
+/*!
+ \fn bool QMenuBar::connectItem(int id, const QObject *receiver, const char* member)
+
+ Use connect() on the relevant QAction instead.
+*/
+
+/*!
+ \fn bool QMenuBar::disconnectItem(int id,const QObject *receiver, const char* member)
+
+ Use disconnect() on the relevant QAction instead.
+*/
+
+/*!
+ \fn QMenuItem *QMenuBar::findItem(int id) const
+
+ Use actions instead.
+*/
+
+/*!
+ \fn Separator QMenuBar::separator() const
+
+ This function is provided only to make old code compile.
+*/
+
+/*!
+ \fn void QMenuBar::setSeparator(Separator sep)
+
+ This function is provided only to make old code compile.
+*/
+
+/*!
+ \fn QRect QMenuBar::itemRect(int index)
+
+ Use actionGeometry() on the relevant QAction instead.
+*/
+
+/*!
+ \fn int QMenuBar::itemAtPos(const QPoint &p)
+
+ There is no equivalent way to achieve this in Qt 4.
+*/
+
+/*!
+ \fn void QMenuBar::activated(int itemId);
+
+ Use triggered() instead.
+*/
+
+/*!
+ \fn void QMenuBar::highlighted(int itemId);
+
+ Use hovered() instead.
+*/
+
+/*!
+ \fn void QMenuBar::setFrameRect(QRect)
+ \internal
+*/
+
+/*!
+ \fn QRect QMenuBar::frameRect() const
+ \internal
+*/
+/*!
+ \enum QMenuBar::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 QMenuBar::setFrameShadow(DummyFrame)
+ \internal
+*/
+
+/*!
+ \fn DummyFrame QMenuBar::frameShadow() const
+ \internal
+*/
+
+/*!
+ \fn void QMenuBar::setFrameShape(DummyFrame)
+ \internal
+*/
+
+/*!
+ \fn DummyFrame QMenuBar::frameShape() const
+ \internal
+*/
+
+/*!
+ \fn void QMenuBar::setFrameStyle(int)
+ \internal
+*/
+
+/*!
+ \fn int QMenuBar::frameStyle() const
+ \internal
+*/
+
+/*!
+ \fn void QMenuBar::setLineWidth(int)
+ \internal
+*/
+
+/*!
+ \fn int QMenuBar::lineWidth() const
+ \internal
+*/
+
+/*!
+ \fn void QMenuBar::setMargin(int margin)
+ Sets the width of the margin around the contents of the widget to \a margin.
+
+ Use QWidget::setContentsMargins() instead.
+ \sa margin(), QWidget::setContentsMargins()
+*/
+
+/*!
+ \fn int QMenuBar::margin() const
+ Returns the with of the the margin around the contents of the widget.
+
+ Use QWidget::getContentsMargins() instead.
+ \sa setMargin(), QWidget::getContentsMargins()
+*/
+
+/*!
+ \fn void QMenuBar::setMidLineWidth(int)
+ \internal
+*/
+
+/*!
+ \fn int QMenuBar::midLineWidth() const
+ \internal
+*/
+
+// for private slots
+
+
+QT_END_NAMESPACE
+
+#include <moc_qmenubar.cpp>
+
+#endif // QT_NO_MENUBAR
diff --git a/src/gui/widgets/qmenubar.h b/src/gui/widgets/qmenubar.h
new file mode 100644
index 0000000000..42f0c0c952
--- /dev/null
+++ b/src/gui/widgets/qmenubar.h
@@ -0,0 +1,363 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QMENUBAR_H
+#define QMENUBAR_H
+
+#include <QtGui/qmenu.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+#ifndef QT_NO_MENUBAR
+
+class QMenuBarPrivate;
+class QStyleOptionMenuItem;
+class QWindowsStyle;
+#ifdef QT3_SUPPORT
+class QMenuItem;
+#endif
+
+class Q_GUI_EXPORT QMenuBar : public QWidget
+{
+ Q_OBJECT
+
+ Q_PROPERTY(bool defaultUp READ isDefaultUp WRITE setDefaultUp)
+
+public:
+ explicit QMenuBar(QWidget *parent = 0);
+ ~QMenuBar();
+
+#ifdef Q_NO_USING_KEYWORD
+ void addAction(QAction *action) { QWidget::addAction(action); }
+#else
+ using QWidget::addAction;
+#endif
+ QAction *addAction(const QString &text);
+ QAction *addAction(const QString &text, const QObject *receiver, const char* member);
+
+ QAction *addMenu(QMenu *menu);
+ QMenu *addMenu(const QString &title);
+ QMenu *addMenu(const QIcon &icon, const QString &title);
+
+
+ QAction *addSeparator();
+ QAction *insertSeparator(QAction *before);
+
+ QAction *insertMenu(QAction *before, QMenu *menu);
+
+ void clear();
+
+ QAction *activeAction() const;
+ void setActiveAction(QAction *action);
+
+ void setDefaultUp(bool);
+ bool isDefaultUp() const;
+
+ QSize sizeHint() const;
+ QSize minimumSizeHint() const;
+ int heightForWidth(int) const;
+
+ QRect actionGeometry(QAction *) const;
+ QAction *actionAt(const QPoint &) const;
+
+ void setCornerWidget(QWidget *w, Qt::Corner corner = Qt::TopRightCorner);
+ QWidget *cornerWidget(Qt::Corner corner = Qt::TopRightCorner) const;
+
+#ifdef Q_WS_MAC
+ OSMenuRef macMenu();
+ static bool macUpdateMenuBar();
+#endif
+
+#ifdef Q_OS_WINCE
+ void setDefaultAction(QAction *);
+ QAction *defaultAction() const;
+
+ static void wceCommands(uint command, HWND controlHandle);
+ static void wceRefresh();
+#endif
+
+public Q_SLOTS:
+ virtual void setVisible(bool visible);
+
+Q_SIGNALS:
+ void triggered(QAction *action);
+ void hovered(QAction *action);
+
+protected:
+ void changeEvent(QEvent *);
+ void keyPressEvent(QKeyEvent *);
+ void mouseReleaseEvent(QMouseEvent *);
+ void mousePressEvent(QMouseEvent *);
+ void mouseMoveEvent(QMouseEvent *);
+ void leaveEvent(QEvent *);
+ void paintEvent(QPaintEvent *);
+ void resizeEvent(QResizeEvent *);
+ void actionEvent(QActionEvent *);
+ void focusOutEvent(QFocusEvent *);
+ void focusInEvent(QFocusEvent *);
+ void timerEvent(QTimerEvent *);
+ bool eventFilter(QObject *, QEvent *);
+ bool event(QEvent *);
+ void initStyleOption(QStyleOptionMenuItem *option, const QAction *action) const;
+
+#ifdef QT3_SUPPORT
+public:
+ QT3_SUPPORT_CONSTRUCTOR QMenuBar(QWidget *parent, const char *name);
+ inline QT3_SUPPORT uint count() const { return actions().count(); }
+ inline QT3_SUPPORT int insertItem(const QString &text, const QObject *receiver, const char* member,
+ const QKeySequence& shortcut = 0, int id = -1, int index = -1) {
+ return insertAny(0, &text, receiver, member, &shortcut, 0, id, index);
+ }
+ inline QT3_SUPPORT int insertItem(const QIcon& icon, const QString &text,
+ const QObject *receiver, const char* member,
+ const QKeySequence& shortcut = 0, int id = -1, int index = -1) {
+ return insertAny(&icon, &text, receiver, member, &shortcut, 0, id, index);
+ }
+ inline QT3_SUPPORT int insertItem(const QPixmap &pixmap, const QObject *receiver, const char* member,
+ const QKeySequence& shortcut = 0, int id = -1, int index = -1) {
+ QIcon icon(pixmap);
+ return insertAny(&icon, 0, receiver, member, &shortcut, 0, id, index);
+ }
+ inline QT3_SUPPORT int insertItem(const QString &text, int id=-1, int index=-1) {
+ return insertAny(0, &text, 0, 0, 0, 0, id, index);
+ }
+ inline QT3_SUPPORT int insertItem(const QIcon& icon, const QString &text, int id=-1, int index=-1) {
+ return insertAny(&icon, &text, 0, 0, 0, 0, id, index);
+ }
+ inline QT3_SUPPORT int insertItem(const QString &text, QMenu *popup, int id=-1, int index=-1) {
+ return insertAny(0, &text, 0, 0, 0, popup, id, index);
+ }
+ inline QT3_SUPPORT int insertItem(const QIcon& icon, const QString &text, QMenu *popup, int id=-1, int index=-1) {
+ return insertAny(&icon, &text, 0, 0, 0, popup, id, index);
+ }
+ inline QT3_SUPPORT int insertItem(const QPixmap &pixmap, int id=-1, int index=-1) {
+ QIcon icon(pixmap);
+ return insertAny(&icon, 0, 0, 0, 0, 0, id, index);
+ }
+ inline QT3_SUPPORT int insertItem(const QPixmap &pixmap, QMenu *popup, int id=-1, int index=-1) {
+ QIcon icon(pixmap);
+ return insertAny(&icon, 0, 0, 0, 0, popup, id, index);
+ }
+ QT3_SUPPORT int insertSeparator(int index=-1);
+ inline QT3_SUPPORT void removeItem(int id) {
+ if(QAction *act = findActionForId(id))
+ removeAction(act); }
+ inline QT3_SUPPORT void removeItemAt(int index) {
+ if(QAction *act = actions().value(index))
+ removeAction(act); }
+#ifndef QT_NO_SHORTCUT
+ inline QT3_SUPPORT QKeySequence accel(int id) const {
+ if(QAction *act = findActionForId(id))
+ return act->shortcut();
+ return QKeySequence(); }
+ inline QT3_SUPPORT void setAccel(const QKeySequence& key, int id) {
+ if(QAction *act = findActionForId(id))
+ act->setShortcut(key);
+ }
+#endif
+ inline QT3_SUPPORT QIcon iconSet(int id) const {
+ if(QAction *act = findActionForId(id))
+ return act->icon();
+ return QIcon(); }
+ inline QT3_SUPPORT QString text(int id) const {
+ if(QAction *act = findActionForId(id))
+ return act->text();
+ return QString(); }
+ inline QT3_SUPPORT QPixmap pixmap(int id) const {
+ if(QAction *act = findActionForId(id))
+ return act->icon().pixmap(QSize(22,22));
+ return QPixmap(); }
+ inline QT3_SUPPORT void setWhatsThis(int id, const QString &w) {
+ if(QAction *act = findActionForId(id))
+ act->setWhatsThis(w); }
+ inline QT3_SUPPORT QString whatsThis(int id) const {
+ if(QAction *act = findActionForId(id))
+ return act->whatsThis();
+ return QString(); }
+
+ inline QT3_SUPPORT void changeItem(int id, const QString &text) {
+ if(QAction *act = findActionForId(id))
+ act->setText(text); }
+ inline QT3_SUPPORT void changeItem(int id, const QPixmap &pixmap) {
+ if(QAction *act = findActionForId(id))
+ act->setIcon(QIcon(pixmap)); }
+ inline QT3_SUPPORT void changeItem(int id, const QIcon &icon, const QString &text) {
+ if(QAction *act = findActionForId(id)) {
+ act->setIcon(icon);
+ act->setText(text);
+ }
+ }
+ inline QT3_SUPPORT bool isItemActive(int id) const { return findActionForId(id) == activeAction(); }
+ inline QT3_SUPPORT bool isItemEnabled(int id) const {
+ if(QAction *act = findActionForId(id))
+ return act->isEnabled();
+ return false; }
+ inline QT3_SUPPORT void setItemEnabled(int id, bool enable) {
+ if(QAction *act = findActionForId(id))
+ act->setEnabled(enable); }
+ inline QT3_SUPPORT bool isItemChecked(int id) const {
+ if(QAction *act = findActionForId(id))
+ return act->isChecked();
+ return false; }
+ inline QT3_SUPPORT void setItemChecked(int id, bool check) {
+ if(QAction *act = findActionForId(id))
+ act->setChecked(check); }
+ inline QT3_SUPPORT bool isItemVisible(int id) const {
+ if(QAction *act = findActionForId(id))
+ return act->isVisible();
+ return false; }
+ inline QT3_SUPPORT void setItemVisible(int id, bool visible) {
+ if(QAction *act = findActionForId(id))
+ act->setVisible(visible); }
+ inline QT3_SUPPORT int indexOf(int id) const { return actions().indexOf(findActionForId(id)); }
+ inline QT3_SUPPORT int idAt(int index) const {
+ return index >= 0 && index < actions().size()
+ ? findIdForAction(actions().at(index))
+ : -1;
+ }
+ inline QT3_SUPPORT void activateItemAt(int index) {
+ if(QAction *ret = actions().value(index))
+ setActiveAction(ret);
+ }
+ inline QT3_SUPPORT bool connectItem(int id, const QObject *receiver, const char* member) {
+ if(QAction *act = findActionForId(id)) {
+ QObject::connect(act, SIGNAL(triggered()), receiver, member);
+ return true;
+ }
+ return false;
+ }
+ inline QT3_SUPPORT bool disconnectItem(int id,const QObject *receiver, const char* member) {
+ if(QAction *act = findActionForId(id)) {
+ QObject::disconnect(act, SIGNAL(triggered()), receiver, member);
+ return true;
+ }
+ return false;
+ }
+ inline QT3_SUPPORT QMenuItem *findItem(int id) const {
+ return (QMenuItem*)findActionForId(id);
+ }
+ QT3_SUPPORT bool setItemParameter(int id, int param);
+ QT3_SUPPORT int itemParameter(int id) const;
+
+ //frame
+ QT3_SUPPORT int frameWidth() const;
+
+ QT3_SUPPORT void setFrameRect(QRect) {}
+ QT3_SUPPORT 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 };
+ QT3_SUPPORT void setFrameShadow(DummyFrame) {}
+ QT3_SUPPORT DummyFrame frameShadow() const { return Plain; }
+ QT3_SUPPORT void setFrameShape(DummyFrame) {}
+ QT3_SUPPORT DummyFrame frameShape() const { return NoFrame; }
+ QT3_SUPPORT void setFrameStyle(int) {}
+ QT3_SUPPORT int frameStyle() const { return 0; }
+ QT3_SUPPORT void setLineWidth(int) {}
+ QT3_SUPPORT int lineWidth() const { return 0; }
+ QT3_SUPPORT void setMargin(int margin) { setContentsMargins(margin, margin, margin, margin); }
+ QT3_SUPPORT int margin() const
+ { int margin; int dummy; getContentsMargins(&margin, &dummy, &dummy, &dummy); return margin; }
+ QT3_SUPPORT void setMidLineWidth(int) {}
+ QT3_SUPPORT int midLineWidth() const { return 0; }
+
+ //menubar
+ enum Separator { Never=0, InWindowsStyle=1 };
+ inline QT3_SUPPORT Separator separator() const { return InWindowsStyle; }
+ inline QT3_SUPPORT void setSeparator(Separator) { }
+
+ QT3_SUPPORT void setAutoGeometry(bool);
+ QT3_SUPPORT bool autoGeometry() const;
+
+Q_SIGNALS:
+ QT_MOC_COMPAT void activated(int itemId);
+ QT_MOC_COMPAT void highlighted(int itemId);
+
+protected:
+ inline QT3_SUPPORT QRect itemRect(int index) {
+ if(QAction *act = actions().value(index))
+ return actionGeometry(act);
+ return QRect();
+ }
+ inline QT3_SUPPORT int itemAtPos(const QPoint &p) {
+ return findIdForAction(actionAt(p));
+ }
+private:
+ QAction *findActionForId(int id) const;
+ int insertAny(const QIcon *icon, const QString *text, const QObject *receiver, const char *member,
+ const QKeySequence *shorcut, const QMenu *popup, int id, int index);
+ int findIdForAction(QAction*) const;
+#endif
+
+private:
+ Q_DECLARE_PRIVATE(QMenuBar)
+ Q_DISABLE_COPY(QMenuBar)
+ Q_PRIVATE_SLOT(d_func(), void _q_actionTriggered())
+ Q_PRIVATE_SLOT(d_func(), void _q_actionHovered())
+ Q_PRIVATE_SLOT(d_func(), void _q_internalShortcutActivated(int))
+ Q_PRIVATE_SLOT(d_func(), void _q_updateLayout())
+
+#ifdef Q_OS_WINCE
+ Q_PRIVATE_SLOT(d_func(), void _q_updateDefaultAction())
+#endif
+
+ friend class QMenu;
+ friend class QMenuPrivate;
+ friend class QWindowsStyle;
+
+#ifdef Q_WS_MAC
+ friend class QApplicationPrivate;
+ friend class QWidgetPrivate;
+ friend bool qt_mac_activate_action(MenuRef, uint, QAction::ActionEvent, bool);
+#endif
+};
+
+#endif // QT_NO_MENUBAR
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QMENUBAR_H
diff --git a/src/gui/widgets/qmenubar_p.h b/src/gui/widgets/qmenubar_p.h
new file mode 100644
index 0000000000..223346bd4a
--- /dev/null
+++ b/src/gui/widgets/qmenubar_p.h
@@ -0,0 +1,230 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QMENUBAR_P_H
+#define QMENUBAR_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 QMAC_Q3MENUBAR_CPP_FILE
+#include "QtGui/qstyleoption.h"
+#include <private/qmenu_p.h> // Mac needs what in this file!
+
+#ifdef Q_OS_WINCE
+#include "qguifunctions_wince.h"
+#endif
+
+QT_BEGIN_NAMESPACE
+
+#ifndef QT_NO_MENUBAR
+class QMenuBarExtension;
+class QMenuBarPrivate : public QWidgetPrivate
+{
+ Q_DECLARE_PUBLIC(QMenuBar)
+public:
+ QMenuBarPrivate() : itemsDirty(0), itemsWidth(0), itemsStart(-1), currentAction(0), mouseDown(0),
+ closePopupMode(0), defaultPopDown(1), popupState(0), keyboardState(0), altPressed(0)
+#ifdef Q_WS_MAC
+ , mac_menubar(0)
+#endif
+
+#ifdef Q_OS_WINCE
+ , wce_menubar(0), wceClassicMenu(false)
+#endif
+ { }
+ ~QMenuBarPrivate()
+ {
+#ifdef Q_WS_MAC
+ delete mac_menubar;
+#endif
+#ifdef Q_OS_WINCE
+ delete wce_menubar;
+#endif
+ }
+
+ void init();
+ QStyleOptionMenuItem getStyleOption(const QAction *action) const;
+
+ //item calculations
+ uint itemsDirty : 1;
+ int itemsWidth, itemsStart;
+
+ QVector<int> shortcutIndexMap;
+ mutable QMap<QAction*, QRect> actionRects;
+ mutable QList<QAction*> actionList;
+ void calcActionRects(int max_width, int start, QMap<QAction*, QRect> &actionRects, QList<QAction*> &actionList) const;
+ QRect actionRect(QAction *) const;
+ void updateGeometries();
+
+ //selection
+ QPointer<QAction>currentAction;
+ uint mouseDown : 1, closePopupMode : 1, defaultPopDown;
+ QAction *actionAt(QPoint p) const;
+ void setCurrentAction(QAction *, bool =false, bool =false);
+ void popupAction(QAction *, bool);
+
+ //active popup state
+ uint popupState : 1;
+ QPointer<QMenu> activeMenu;
+
+ //keyboard mode for keyboard navigation
+ void setKeyboardMode(bool);
+ uint keyboardState : 1, altPressed : 1;
+ QPointer<QWidget> keyboardFocusWidget;
+
+ //firing of events
+ void activateAction(QAction *, QAction::ActionEvent);
+
+ void _q_actionTriggered();
+ void _q_actionHovered();
+ void _q_internalShortcutActivated(int);
+ void _q_updateLayout();
+
+#ifdef Q_OS_WINCE
+ void _q_updateDefaultAction();
+#endif
+
+ //extra widgets in the menubar
+ QPointer<QWidget> leftWidget, rightWidget;
+ QMenuBarExtension *extension;
+ bool isVisible(QAction *action);
+
+ //menu fading/scrolling effects
+ bool doChildEffects;
+
+ QRect menuRect(bool) const;
+
+ // reparenting
+ void handleReparent();
+ QWidget *oldParent;
+ QWidget *oldWindow;
+
+ QList<QAction*> hiddenActions;
+ //default action
+ QPointer<QAction> defaultAction;
+
+ QBasicTimer autoReleaseTimer;
+#ifdef QT3_SUPPORT
+ bool doAutoResize;
+#endif
+#ifdef Q_WS_MAC
+ //mac menubar binding
+ struct QMacMenuBarPrivate {
+ QList<QMacMenuAction*> actionItems;
+ OSMenuRef menu, apple_menu;
+ QMacMenuBarPrivate();
+ ~QMacMenuBarPrivate();
+
+ void addAction(QAction *, QMacMenuAction* =0);
+ void addAction(QMacMenuAction *, QMacMenuAction* =0);
+ void syncAction(QMacMenuAction *);
+ inline void syncAction(QAction *a) { syncAction(findAction(a)); }
+ void removeAction(QMacMenuAction *);
+ inline void removeAction(QAction *a) { removeAction(findAction(a)); }
+ inline QMacMenuAction *findAction(QAction *a) {
+ for(int i = 0; i < actionItems.size(); i++) {
+ QMacMenuAction *act = actionItems[i];
+ if(a == act->action)
+ return act;
+ }
+ return 0;
+ }
+ } *mac_menubar;
+ void macCreateMenuBar(QWidget *);
+ void macDestroyMenuBar();
+ OSMenuRef macMenu();
+#endif
+#ifdef Q_OS_WINCE
+ void wceCreateMenuBar(QWidget *);
+ void wceDestroyMenuBar();
+ struct QWceMenuBarPrivate {
+ QList<QWceMenuAction*> actionItems;
+ QList<QWceMenuAction*> actionItemsLeftButton;
+ QList<QList<QWceMenuAction*>> actionItemsClassic;
+ HMENU menuHandle;
+ HMENU leftButtonMenuHandle;
+ HWND menubarHandle;
+ HWND parentWindowHandle;
+ bool leftButtonIsMenu;
+ QPointer<QAction> leftButtonAction;
+ QMenuBarPrivate *d;
+ int leftButtonCommand;
+
+ QWceMenuBarPrivate(QMenuBarPrivate *menubar);
+ ~QWceMenuBarPrivate();
+ void addAction(QAction *, QWceMenuAction* =0);
+ void addAction(QWceMenuAction *, QWceMenuAction* =0);
+ void syncAction(QWceMenuAction *);
+ inline void syncAction(QAction *a) { syncAction(findAction(a)); }
+ void removeAction(QWceMenuAction *);
+ void rebuild();
+ inline void removeAction(QAction *a) { removeAction(findAction(a)); }
+ inline QWceMenuAction *findAction(QAction *a) {
+ for(int i = 0; i < actionItems.size(); i++) {
+ QWceMenuAction *act = actionItems[i];
+ if(a == act->action)
+ return act;
+ }
+ return 0;
+ }
+ } *wce_menubar;
+ bool wceClassicMenu;
+ void wceCommands(uint command);
+ void wceRefresh();
+ bool wceEmitSignals(QList<QWceMenuAction*> actions, uint command);
+#endif
+};
+#endif
+
+#endif // QT_NO_MENUBAR
+
+QT_END_NAMESPACE
+
+#endif // QMENUBAR_P_H
diff --git a/src/gui/widgets/qmenudata.cpp b/src/gui/widgets/qmenudata.cpp
new file mode 100644
index 0000000000..568b67ffc1
--- /dev/null
+++ b/src/gui/widgets/qmenudata.cpp
@@ -0,0 +1,96 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#include "qmenudata.h"
+
+#ifdef QT3_SUPPORT
+#include <qaction.h>
+#include <private/qaction_p.h>
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QMenuItem
+ \brief The QMenuItem class represents an item in a menu.
+
+ \compat
+
+ Use QAction instead.
+*/
+
+/*!
+ \compat
+ Constructs a new menu item.
+*/
+QMenuItem::QMenuItem() : QAction((QWidget*)0)
+{
+}
+
+void QMenuItem::setId(int id)
+{
+ d_func()->param = d_func()->id = id;
+}
+
+/*!
+ \compat
+ Returns the menu item's ID.
+*/
+int QMenuItem::id() const
+{
+ return d_func()->id;
+}
+
+void QMenuItem::setSignalValue(int param)
+{
+ d_func()->param = param;
+}
+
+/*!
+ \compat
+ Returns the signal value for the menu item.
+*/
+int QMenuItem::signalValue() const
+{
+ return d_func()->param;
+}
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/gui/widgets/qmenudata.h b/src/gui/widgets/qmenudata.h
new file mode 100644
index 0000000000..24d960ab18
--- /dev/null
+++ b/src/gui/widgets/qmenudata.h
@@ -0,0 +1,78 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QMENUDATA_H
+#define QMENUDATA_H
+
+#include <QtCore/qglobal.h>
+
+#ifdef QT3_SUPPORT
+#include <QtGui/qaction.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+class Q_GUI_EXPORT QMenuItem : public QAction
+{
+ Q_OBJECT
+
+public:
+ QMenuItem();
+
+ QT3_SUPPORT int id() const;
+ QT3_SUPPORT int signalValue() const;
+private:
+ friend class QMenu;
+ friend class QMenuBar;
+ void setId(int);
+ void setSignalValue(int);
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif
+
+#endif // QMENUDATA_H
diff --git a/src/gui/widgets/qplaintextedit.cpp b/src/gui/widgets/qplaintextedit.cpp
new file mode 100644
index 0000000000..2e9201da00
--- /dev/null
+++ b/src/gui/widgets/qplaintextedit.cpp
@@ -0,0 +1,2893 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qplaintextedit_p.h"
+
+
+#include <qfont.h>
+#include <qpainter.h>
+#include <qevent.h>
+#include <qdebug.h>
+#include <qmime.h>
+#include <qdrag.h>
+#include <qclipboard.h>
+#include <qmenu.h>
+#include <qstyle.h>
+#include <qtimer.h>
+#include "private/qtextdocumentlayout_p.h"
+#include "private/qabstracttextdocumentlayout_p.h"
+#include "qtextdocument.h"
+#include "private/qtextdocument_p.h"
+#include "qtextlist.h"
+#include "private/qtextcontrol_p.h"
+
+#include <qtextformat.h>
+#include <qdatetime.h>
+#include <qapplication.h>
+#include <limits.h>
+#include <qtexttable.h>
+#include <qvariant.h>
+
+#include <qinputcontext.h>
+
+#ifndef QT_NO_TEXTEDIT
+
+QT_BEGIN_NAMESPACE
+
+class QPlainTextDocumentLayoutPrivate : public QAbstractTextDocumentLayoutPrivate
+{
+ Q_DECLARE_PUBLIC(QPlainTextDocumentLayout)
+public:
+ QPlainTextDocumentLayoutPrivate() {
+ mainViewPrivate = 0;
+ width = 0;
+ maximumWidth = 0;
+ maximumWidthBlockNumber = 0;
+ blockCount = 1;
+ blockUpdate = blockDocumentSizeChanged = false;
+ cursorWidth = 1;
+ textLayoutFlags = 0;
+ }
+
+ qreal width;
+ qreal maximumWidth;
+ int maximumWidthBlockNumber;
+ int blockCount;
+ QPlainTextEditPrivate *mainViewPrivate;
+ bool blockUpdate;
+ bool blockDocumentSizeChanged;
+ int cursorWidth;
+ int textLayoutFlags;
+
+ void layoutBlock(const QTextBlock &block);
+ qreal blockWidth(const QTextBlock &block);
+
+ void relayout();
+};
+
+
+
+/*! \class QPlainTextDocumentLayout
+ \since 4.4
+ \brief The QPlainTextDocumentLayout class implements a plain text layout for QTextDocument
+
+ \ingroup text
+
+
+ A QPlainTextDocumentLayout is required for text documents that can
+ be display or edited in a QPlainTextEdit. See
+ QTextDocument::setDocumentLayout().
+
+ QPlainTextDocumentLayout uses the QAbstractTextDocumentLayout API
+ that QTextDocument requires, but redefines it partially in order to
+ support plain text better. For instances, it does not operate on
+ vertical pixels, but on paragraphs (called blocks) instead. The
+ height of a document is identical to the number of paragraphs it
+ contains. The layout also doesn't support tables or nested frames,
+ or any sort of advanced text layout that goes beyond a list of
+ paragraphs with syntax highlighting.
+
+*/
+
+
+
+/*!
+ Constructs a plain text document layout for the text \a document.
+ */
+QPlainTextDocumentLayout::QPlainTextDocumentLayout(QTextDocument *document)
+ :QAbstractTextDocumentLayout(* new QPlainTextDocumentLayoutPrivate, document) {
+}
+/*!
+ Destructs a plain text document layout.
+ */
+QPlainTextDocumentLayout::~QPlainTextDocumentLayout() {}
+
+
+/*!
+ \reimp
+ */
+void QPlainTextDocumentLayout::draw(QPainter *, const PaintContext &)
+{
+}
+
+/*!
+ \reimp
+ */
+int QPlainTextDocumentLayout::hitTest(const QPointF &, Qt::HitTestAccuracy ) const
+{
+// this function is used from
+// QAbstractTextDocumentLayout::anchorAt(), but is not
+// implementable in a plain text document layout, because the
+// layout depends on the top block and top line which depends on
+// the view
+ return -1;
+}
+
+/*!
+ \reimp
+ */
+int QPlainTextDocumentLayout::pageCount() const
+{ return 1; }
+
+/*!
+ \reimp
+ */
+QSizeF QPlainTextDocumentLayout::documentSize() const
+{
+ Q_D(const QPlainTextDocumentLayout);
+ return QSizeF(d->maximumWidth, document()->lineCount());
+}
+
+/*!
+ \reimp
+ */
+QRectF QPlainTextDocumentLayout::frameBoundingRect(QTextFrame *) const
+{
+ Q_D(const QPlainTextDocumentLayout);
+ return QRectF(0, 0, qMax(d->width, d->maximumWidth), qreal(INT_MAX));
+}
+
+/*!
+ \reimp
+ */
+QRectF QPlainTextDocumentLayout::blockBoundingRect(const QTextBlock &block) const
+{
+ if (!block.isValid()) { return QRectF(); }
+ QTextLayout *tl = block.layout();
+ if (!tl->lineCount())
+ const_cast<QPlainTextDocumentLayout*>(this)->layoutBlock(block);
+ QRectF br;
+ if (block.isVisible()) {
+ br = QRectF(QPointF(0, 0), tl->boundingRect().bottomRight());
+ if (tl->lineCount() == 1)
+ br.setWidth(qMax(br.width(), tl->lineAt(0).naturalTextWidth()));
+ qreal margin = document()->documentMargin();
+ br.adjust(0, 0, margin, 0);
+ if (!block.next().isValid())
+ br.adjust(0, 0, 0, margin);
+ }
+ return br;
+
+}
+
+/*!
+ Ensures that \a block has a valid layout
+ */
+void QPlainTextDocumentLayout::ensureBlockLayout(const QTextBlock &block) const
+{
+ if (!block.isValid())
+ return;
+ QTextLayout *tl = block.layout();
+ if (!tl->lineCount())
+ const_cast<QPlainTextDocumentLayout*>(this)->layoutBlock(block);
+}
+
+
+/*! \property QPlainTextDocumentLayout::cursorWidth
+
+ This property specifies the width of the cursor in pixels. The default value is 1.
+*/
+void QPlainTextDocumentLayout::setCursorWidth(int width)
+{
+ Q_D(QPlainTextDocumentLayout);
+ d->cursorWidth = width;
+}
+
+int QPlainTextDocumentLayout::cursorWidth() const
+{
+ Q_D(const QPlainTextDocumentLayout);
+ return d->cursorWidth;
+}
+
+QPlainTextDocumentLayoutPrivate *QPlainTextDocumentLayout::priv() const
+{
+ Q_D(const QPlainTextDocumentLayout);
+ return const_cast<QPlainTextDocumentLayoutPrivate*>(d);
+}
+
+
+/*!
+
+ Requests a complete update on all views.
+ */
+void QPlainTextDocumentLayout::requestUpdate()
+{
+ emit update(QRectF(0., -4., 1000000000., 1000000000.));
+}
+
+
+void QPlainTextDocumentLayout::setTextWidth(qreal newWidth)
+{
+ Q_D(QPlainTextDocumentLayout);
+ d->width = d->maximumWidth = newWidth;
+ d->relayout();
+}
+
+qreal QPlainTextDocumentLayout::textWidth() const
+{
+ Q_D(const QPlainTextDocumentLayout);
+ return d->width;
+}
+
+void QPlainTextDocumentLayoutPrivate::relayout()
+{
+ Q_Q(QPlainTextDocumentLayout);
+ QTextBlock block = q->document()->firstBlock();
+ while (block.isValid()) {
+ block.layout()->clearLayout();
+ block.setLineCount(block.isVisible() ? 1 : 0);
+ block = block.next();
+ }
+ emit q->update();
+}
+
+
+/*! \reimp
+ */
+void QPlainTextDocumentLayout::documentChanged(int from, int /*charsRemoved*/, int charsAdded)
+{
+ Q_D(QPlainTextDocumentLayout);
+ QTextDocument *doc = document();
+ int newBlockCount = doc->blockCount();
+
+ QTextBlock changeStartBlock = doc->findBlock(from);
+ QTextBlock changeEndBlock = doc->findBlock(qMax(0, from + charsAdded - 1));
+
+ if (changeStartBlock == changeEndBlock && newBlockCount == d->blockCount) {
+ QTextBlock block = changeStartBlock;
+ int blockLineCount = block.layout()->lineCount();
+ if (block.isValid() && blockLineCount) {
+ QRectF oldBr = blockBoundingRect(block);
+ layoutBlock(block);
+ QRectF newBr = blockBoundingRect(block);
+ if (newBr.height() == oldBr.height()) {
+ if (!d->blockUpdate)
+ emit updateBlock(block);
+ return;
+ }
+ }
+ } else {
+ QTextBlock block = changeStartBlock;
+ do {
+ block.clearLayout();
+ if (block == changeEndBlock)
+ break;
+ block = block.next();
+ } while(block.isValid());
+ }
+
+ if (newBlockCount != d->blockCount) {
+
+ int changeEnd = changeEndBlock.blockNumber();
+ int blockDiff = newBlockCount - d->blockCount;
+ int oldChangeEnd = changeEnd - blockDiff;
+
+ if (d->maximumWidthBlockNumber > oldChangeEnd)
+ d->maximumWidthBlockNumber += blockDiff;
+
+ d->blockCount = newBlockCount;
+ if (d->blockCount == 1)
+ d->maximumWidth = blockWidth(doc->firstBlock());
+
+ if (!d->blockDocumentSizeChanged)
+ emit documentSizeChanged(documentSize());
+
+ if (blockDiff == 1 && changeEnd == newBlockCount -1 ) {
+ if (!d->blockUpdate) {
+ QTextBlock b = changeStartBlock;
+ for(;;) {
+ emit updateBlock(b);
+ if (b == changeEndBlock)
+ break;
+ b = b.next();
+ }
+ }
+ return;
+ }
+ }
+
+ if (!d->blockUpdate)
+ emit update(); // optimization potential
+
+}
+
+
+void QPlainTextDocumentLayout::layoutBlock(const QTextBlock &block)
+{
+ Q_D(QPlainTextDocumentLayout);
+ QTextDocument *doc = document();
+ qreal margin = doc->documentMargin();
+ QFontMetrics fm(doc->defaultFont());
+ qreal blockMaximumWidth = 0;
+
+ int leading = qMax(0, fm.leading());
+ qreal height = 0;
+ QTextLayout *tl = block.layout();
+ QTextOption option = doc->defaultTextOption();
+ tl->setTextOption(option);
+
+ int extraMargin = 0;
+ if (option.flags() & QTextOption::AddSpaceForLineAndParagraphSeparators) {
+ QFontMetrics fm(block.charFormat().font());
+ extraMargin += fm.width(QChar(0x21B5));
+ }
+ tl->beginLayout();
+ while (1) {
+ QTextLine line = tl->createLine();
+ if (!line.isValid())
+ break;
+ line.setLineWidth(d->width - 2*margin - extraMargin);
+
+ height += leading;
+ line.setPosition(QPointF(margin, height));
+ height += line.height();
+ blockMaximumWidth = qMax(blockMaximumWidth, line.naturalTextWidth() + 2*margin);
+ }
+ tl->endLayout();
+
+ int previousLineCount = doc->lineCount();
+ const_cast<QTextBlock&>(block).setLineCount(block.isVisible() ? tl->lineCount() : 0);
+ int lineCount = doc->lineCount();
+
+ bool emitDocumentSizeChanged = previousLineCount != lineCount;
+ if (blockMaximumWidth > d->maximumWidth) {
+ // new longest line
+ d->maximumWidth = blockMaximumWidth;
+ d->maximumWidthBlockNumber = block.blockNumber();
+ emitDocumentSizeChanged = true;
+ } else if (block.blockNumber() == d->maximumWidthBlockNumber && blockMaximumWidth < d->maximumWidth) {
+ // longest line shrinking
+ QTextBlock b = doc->firstBlock();
+ d->maximumWidth = 0;
+ QTextBlock maximumBlock;
+ while (b.isValid()) {
+ qreal blockMaximumWidth = blockWidth(b);
+ if (blockMaximumWidth > d->maximumWidth) {
+ d->maximumWidth = blockMaximumWidth;
+ maximumBlock = b;
+ }
+ b = b.next();
+ }
+ if (maximumBlock.isValid()) {
+ d->maximumWidthBlockNumber = maximumBlock.blockNumber();
+ emitDocumentSizeChanged = true;
+ }
+ }
+ if (emitDocumentSizeChanged && !d->blockDocumentSizeChanged)
+ emit documentSizeChanged(documentSize());
+}
+
+qreal QPlainTextDocumentLayout::blockWidth(const QTextBlock &block)
+{
+ QTextLayout *layout = block.layout();
+ if (!layout->lineCount())
+ return 0; // only for layouted blocks
+ qreal blockWidth = 0;
+ for (int i = 0; i < layout->lineCount(); ++i) {
+ QTextLine line = layout->lineAt(i);
+ blockWidth = qMax(line.naturalTextWidth() + 8, blockWidth);
+ }
+ return blockWidth;
+}
+
+
+QPlainTextEditControl::QPlainTextEditControl(QPlainTextEdit *parent)
+ : QTextControl(parent), textEdit(parent),
+ topBlock(0)
+{
+ setAcceptRichText(false);
+}
+
+void QPlainTextEditPrivate::_q_cursorPositionChanged()
+{
+ pageUpDownLastCursorYIsValid = false;
+};
+
+void QPlainTextEditPrivate::_q_verticalScrollbarActionTriggered(int action) {
+ if (action == QAbstractSlider::SliderPageStepAdd) {
+ pageUpDown(QTextCursor::Down, QTextCursor::MoveAnchor, false);
+ } else if (action == QAbstractSlider::SliderPageStepSub) {
+ pageUpDown(QTextCursor::Up, QTextCursor::MoveAnchor, false);
+ }
+}
+
+QMimeData *QPlainTextEditControl::createMimeDataFromSelection() const {
+ QPlainTextEdit *ed = qobject_cast<QPlainTextEdit *>(parent());
+ if (!ed)
+ return QTextControl::createMimeDataFromSelection();
+ return ed->createMimeDataFromSelection();
+ }
+bool QPlainTextEditControl::canInsertFromMimeData(const QMimeData *source) const {
+ QPlainTextEdit *ed = qobject_cast<QPlainTextEdit *>(parent());
+ if (!ed)
+ return QTextControl::canInsertFromMimeData(source);
+ return ed->canInsertFromMimeData(source);
+}
+void QPlainTextEditControl::insertFromMimeData(const QMimeData *source) {
+ QPlainTextEdit *ed = qobject_cast<QPlainTextEdit *>(parent());
+ if (!ed)
+ QTextControl::insertFromMimeData(source);
+ else
+ ed->insertFromMimeData(source);
+}
+
+int QPlainTextEditPrivate::verticalOffset(int topBlock, int topLine) const
+{
+ qreal offset = 0;
+ QTextDocument *doc = control->document();
+
+ if (topLine) {
+ QTextBlock currentBlock = doc->findBlockByNumber(topBlock);
+ QPlainTextDocumentLayout *documentLayout = qobject_cast<QPlainTextDocumentLayout*>(doc->documentLayout());
+ Q_ASSERT(documentLayout);
+ QRectF r = documentLayout->blockBoundingRect(currentBlock);
+ QTextLayout *layout = currentBlock.layout();
+ if (layout && topLine <= layout->lineCount()) {
+ QTextLine line = layout->lineAt(topLine - 1);
+ const QRectF lr = line.naturalTextRect();
+ offset = lr.bottom();
+ }
+ }
+ if (topBlock == 0 && topLine == 0)
+ offset -= doc->documentMargin(); // top margin
+ return (int)offset;
+}
+
+
+int QPlainTextEditPrivate::verticalOffset() const {
+ return verticalOffset(control->topBlock, topLine);
+}
+
+
+QTextBlock QPlainTextEditControl::firstVisibleBlock() const
+{
+ return document()->findBlockByNumber(topBlock);
+}
+
+
+
+int QPlainTextEditControl::hitTest(const QPointF &point, Qt::HitTestAccuracy ) const {
+ int currentBlockNumber = topBlock;
+ QTextBlock currentBlock = document()->findBlockByNumber(currentBlockNumber);
+ QPlainTextDocumentLayout *documentLayout = qobject_cast<QPlainTextDocumentLayout*>(document()->documentLayout());
+ Q_ASSERT(documentLayout);
+
+ QPointF offset;
+ QRectF r = documentLayout->blockBoundingRect(currentBlock);
+ while (currentBlock.next().isValid() && r.bottom() + offset.y() <= point.y()) {
+ offset.ry() += r.height();
+ currentBlock = currentBlock.next();
+ ++currentBlockNumber;
+ r = documentLayout->blockBoundingRect(currentBlock);
+ }
+ while (currentBlock.previous().isValid() && r.top() + offset.y() > point.y()) {
+ offset.ry() -= r.height();
+ currentBlock = currentBlock.previous();
+ --currentBlockNumber;
+ r = documentLayout->blockBoundingRect(currentBlock);
+ }
+
+
+ if (!currentBlock.isValid())
+ return -1;
+ QTextLayout *layout = currentBlock.layout();
+ int off = 0;
+ QPointF pos = point - offset;
+ for (int i = 0; i < layout->lineCount(); ++i) {
+ QTextLine line = layout->lineAt(i);
+ const QRectF lr = line.naturalTextRect();
+ if (lr.top() > pos.y()) {
+ off = qMin(off, line.textStart());
+ } else if (lr.bottom() <= pos.y()) {
+ off = qMax(off, line.textStart() + line.textLength());
+ } else {
+ off = line.xToCursor(pos.x(), overwriteMode() ?
+ QTextLine::CursorOnCharacter : QTextLine::CursorBetweenCharacters);
+ break;
+ }
+ }
+
+ return currentBlock.position() + off;
+}
+
+QRectF QPlainTextEditControl::blockBoundingRect(const QTextBlock &block) const {
+ int currentBlockNumber = topBlock;
+ int blockNumber = block.blockNumber();
+ QTextBlock currentBlock = document()->findBlockByNumber(currentBlockNumber);
+ if (!currentBlock.isValid())
+ return QRectF();
+ Q_ASSERT(currentBlock.blockNumber() == currentBlockNumber);
+ QPlainTextDocumentLayout *documentLayout = qobject_cast<QPlainTextDocumentLayout*>(document()->documentLayout());
+ Q_ASSERT(documentLayout);
+
+ QPointF offset;
+ if (!block.isValid())
+ return QRectF();
+ QRectF r = documentLayout->blockBoundingRect(currentBlock);
+ while (currentBlockNumber < blockNumber && offset.y() <= 2* textEdit->viewport()->height()) {
+ offset.ry() += r.height();
+ currentBlock = currentBlock.next();
+ ++currentBlockNumber;
+ r = documentLayout->blockBoundingRect(currentBlock);
+ }
+ while (currentBlockNumber > blockNumber && offset.y() >= -textEdit->viewport()->height()) {
+ currentBlock = currentBlock.previous();
+ if (!currentBlock.isValid())
+ break;
+ --currentBlockNumber;
+ r = documentLayout->blockBoundingRect(currentBlock);
+ offset.ry() -= r.height();
+ }
+
+ if (currentBlockNumber != blockNumber) {
+ // fallback for blocks out of reach. Give it some geometry at
+ // least, and ensure the layout is up to date.
+ r = documentLayout->blockBoundingRect(block);
+ if (currentBlockNumber > blockNumber)
+ offset.ry() -= r.height();
+ }
+ r.translate(offset);
+ return r;
+}
+
+
+void QPlainTextEditPrivate::setTopLine(int visualTopLine, int dx)
+{
+ QTextDocument *doc = control->document();
+ QTextBlock block = doc->findBlockByLineNumber(visualTopLine);
+ int blockNumber = block.blockNumber();
+ int lineNumber = visualTopLine - block.firstLineNumber();
+ setTopBlock(blockNumber, lineNumber, dx);
+}
+
+void QPlainTextEditPrivate::setTopBlock(int blockNumber, int lineNumber, int dx)
+{
+ Q_Q(QPlainTextEdit);
+ blockNumber = qMax(0, blockNumber);
+ lineNumber = qMax(0, lineNumber);
+ QTextDocument *doc = control->document();
+ QTextBlock block = doc->findBlockByNumber(blockNumber);
+
+ int newTopLine = block.firstLineNumber() + lineNumber;
+ int maxTopLine = vbar->maximum();
+
+ if (newTopLine > maxTopLine) {
+ block = doc->findBlockByLineNumber(maxTopLine);
+ blockNumber = block.blockNumber();
+ lineNumber = maxTopLine - block.firstLineNumber();
+ }
+
+ bool vbarSignalsBlocked = vbar->blockSignals(true);
+ vbar->setValue(newTopLine);
+ vbar->blockSignals(vbarSignalsBlocked);
+
+ if (!dx && blockNumber == control->topBlock && lineNumber == topLine)
+ return;
+
+ if (viewport->updatesEnabled() && viewport->isVisible()) {
+ int dy = 0;
+ if (doc->findBlockByLineNumber(control->topBlock).isValid()) {
+ dy = (int)(-q->blockBoundingGeometry(block).y())
+ + verticalOffset() - verticalOffset(blockNumber, lineNumber);
+ }
+ control->topBlock = blockNumber;
+ topLine = lineNumber;
+ if (dx || dy)
+ viewport->scroll(q->isRightToLeft() ? -dx : dx, dy);
+ else
+ viewport->update();
+ emit q->updateRequest(viewport->rect(), dy);
+ } else {
+ control->topBlock = blockNumber;
+ topLine = lineNumber;
+ }
+
+}
+
+
+
+void QPlainTextEditPrivate::ensureVisible(int position, bool center, bool forceCenter) {
+ Q_Q(QPlainTextEdit);
+ QRectF visible = QRectF(viewport->rect()).translated(-q->contentOffset());
+ QTextBlock block = control->document()->findBlock(position);
+ if (!block.isValid())
+ return;
+ QRectF br = control->blockBoundingRect(block);
+ if (!br.isValid())
+ return;
+ QRectF lr = br;
+ QTextLine line = block.layout()->lineForTextPosition(position - block.position());
+ Q_ASSERT(line.isValid());
+ lr = line.naturalTextRect().translated(br.topLeft());
+
+ if (lr.bottom() >= visible.bottom() || (center && lr.top() < visible.top()) || forceCenter){
+
+ qreal height = visible.height();
+ if (center)
+ height /= 2;
+
+ qreal h = center ? line.naturalTextRect().center().y() : line.naturalTextRect().bottom();
+
+ while (h < height && block.previous().isValid()) {
+ block = block.previous();
+ h += q->blockBoundingRect(block).height();
+ }
+
+ int l = 0;
+ int lineCount = block.layout()->lineCount();
+ int voffset = verticalOffset(block.blockNumber(), 0);
+ while (l < lineCount) {
+ QRectF lineRect = block.layout()->lineAt(l).naturalTextRect();
+ if (h - voffset - lineRect.top() <= height)
+ break;
+ ++l;
+ }
+
+ if (block.next().isValid() && l >= lineCount) {
+ block = block.next();
+ l = 0;
+ }
+ setTopBlock(block.blockNumber(), l);
+ } else if (lr.top() < visible.top()) {
+ setTopBlock(block.blockNumber(), line.lineNumber());
+ }
+
+}
+
+
+void QPlainTextEditPrivate::updateViewport()
+{
+ Q_Q(QPlainTextEdit);
+ viewport->update();
+ emit q->updateRequest(viewport->rect(), 0);
+}
+
+QPlainTextEditPrivate::QPlainTextEditPrivate()
+ : control(0),
+ tabChangesFocus(false),
+ lineWrap(QPlainTextEdit::WidgetWidth),
+ wordWrap(QTextOption::WrapAtWordBoundaryOrAnywhere),
+ topLine(0), pageUpDownLastCursorYIsValid(false)
+{
+ showCursorOnInitialShow = true;
+ backgroundVisible = false;
+ centerOnScroll = false;
+ inDrag = false;
+}
+
+
+void QPlainTextEditPrivate::init(const QString &txt)
+{
+ Q_Q(QPlainTextEdit);
+ control = new QPlainTextEditControl(q);
+
+ QTextDocument *doc = new QTextDocument(control);
+ QAbstractTextDocumentLayout *layout = new QPlainTextDocumentLayout(doc);
+ doc->setDocumentLayout(layout);
+ control->setDocument(doc);
+
+ control->setPalette(q->palette());
+
+ QObject::connect(vbar, SIGNAL(actionTriggered(int)), q, SLOT(_q_verticalScrollbarActionTriggered(int)));
+
+ QObject::connect(control, SIGNAL(microFocusChanged()), q, SLOT(updateMicroFocus()));
+ QObject::connect(control, SIGNAL(documentSizeChanged(QSizeF)), q, SLOT(_q_adjustScrollbars()));
+ QObject::connect(control, SIGNAL(blockCountChanged(int)), q, SIGNAL(blockCountChanged(int)));
+ QObject::connect(control, SIGNAL(updateRequest(QRectF)), q, SLOT(_q_repaintContents(QRectF)));
+ QObject::connect(control, SIGNAL(modificationChanged(bool)), q, SIGNAL(modificationChanged(bool)));
+
+ QObject::connect(control, SIGNAL(textChanged()), q, SIGNAL(textChanged()));
+ QObject::connect(control, SIGNAL(undoAvailable(bool)), q, SIGNAL(undoAvailable(bool)));
+ QObject::connect(control, SIGNAL(redoAvailable(bool)), q, SIGNAL(redoAvailable(bool)));
+ QObject::connect(control, SIGNAL(copyAvailable(bool)), q, SIGNAL(copyAvailable(bool)));
+ QObject::connect(control, SIGNAL(selectionChanged()), q, SIGNAL(selectionChanged()));
+ QObject::connect(control, SIGNAL(cursorPositionChanged()), q, SLOT(_q_cursorPositionChanged()));
+ QObject::connect(control, SIGNAL(cursorPositionChanged()), q, SIGNAL(cursorPositionChanged()));
+
+
+ // set a null page size initially to avoid any relayouting until the textedit
+ // is shown. relayoutDocument() will take care of setting the page size to the
+ // viewport dimensions later.
+ doc->setTextWidth(0);
+ doc->documentLayout()->setPaintDevice(viewport);
+ doc->setDefaultFont(q->font());
+
+
+ if (!txt.isEmpty())
+ control->setPlainText(txt);
+
+ hbar->setSingleStep(20);
+ vbar->setSingleStep(1);
+
+ viewport->setBackgroundRole(QPalette::Base);
+ q->setAcceptDrops(true);
+ q->setFocusPolicy(Qt::WheelFocus);
+ q->setAttribute(Qt::WA_KeyCompression);
+ q->setAttribute(Qt::WA_InputMethodEnabled);
+
+#ifndef QT_NO_CURSOR
+ viewport->setCursor(Qt::IBeamCursor);
+#endif
+}
+
+void QPlainTextEditPrivate::_q_repaintContents(const QRectF &contentsRect)
+{
+ Q_Q(QPlainTextEdit);
+ if (!contentsRect.isValid()) {
+ updateViewport();
+ return;
+ }
+ const int xOffset = horizontalOffset();
+ const int yOffset = verticalOffset();
+ const QRect visibleRect(xOffset, yOffset, viewport->width(), viewport->height());
+
+ QRect r = contentsRect.adjusted(-1, -1, 1, 1).intersected(visibleRect).toAlignedRect();
+ if (r.isEmpty())
+ return;
+
+ r.translate(-xOffset, -yOffset);
+ viewport->update(r);
+ emit q->updateRequest(r, 0);
+}
+
+void QPlainTextEditPrivate::pageUpDown(QTextCursor::MoveOperation op, QTextCursor::MoveMode moveMode, bool moveCursor)
+{
+
+ Q_Q(QPlainTextEdit);
+
+ QTextCursor cursor = control->textCursor();
+ if (moveCursor) {
+ ensureCursorVisible();
+ if (!pageUpDownLastCursorYIsValid)
+ pageUpDownLastCursorY = control->cursorRect(cursor).top() - verticalOffset();
+ }
+
+ qreal lastY = pageUpDownLastCursorY;
+
+
+ if (op == QTextCursor::Down) {
+ QRectF visible = QRectF(viewport->rect()).translated(-q->contentOffset());
+ QTextBlock firstVisibleBlock = q->firstVisibleBlock();
+ QTextBlock block = firstVisibleBlock;
+ QRectF br = q->blockBoundingRect(block);
+ qreal h = 0;
+ int atEnd = false;
+ while (h + br.height() <= visible.bottom()) {
+ if (!block.next().isValid()) {
+ atEnd = true;
+ lastY = visible.bottom(); // set cursor to last line
+ break;
+ }
+ h += br.height();
+ block = block.next();
+ br = q->blockBoundingRect(block);
+ }
+
+ if (!atEnd) {
+ int line = 0;
+ qreal diff = visible.bottom() - h;
+ int lineCount = block.layout()->lineCount();
+ while (line < lineCount - 1) {
+ if (block.layout()->lineAt(line).naturalTextRect().bottom() > diff) {
+ // the first line that did not completely fit the screen
+ break;
+ }
+ ++line;
+ }
+ setTopBlock(block.blockNumber(), line);
+ }
+
+ if (moveCursor) {
+ // move using movePosition to keep the cursor's x
+ lastY += verticalOffset();
+ bool moved = false;
+ do {
+ moved = cursor.movePosition(op, moveMode);
+ } while (moved && control->cursorRect(cursor).top() < lastY);
+ }
+
+ } else if (op == QTextCursor::Up) {
+
+ QRectF visible = QRectF(viewport->rect()).translated(-q->contentOffset());
+ visible.translate(0, -visible.height()); // previous page
+ QTextBlock block = q->firstVisibleBlock();
+ qreal h = 0;
+ while (h >= visible.top()) {
+ if (!block.previous().isValid()) {
+ if (control->topBlock == 0 && topLine == 0) {
+ lastY = 0; // set cursor to first line
+ }
+ break;
+ }
+ block = block.previous();
+ QRectF br = q->blockBoundingRect(block);
+ h -= br.height();
+ }
+
+ int line = 0;
+ if (block.isValid()) {
+ qreal diff = visible.top() - h;
+ int lineCount = block.layout()->lineCount();
+ while (line < lineCount) {
+ if (block.layout()->lineAt(line).naturalTextRect().top() >= diff)
+ break;
+ ++line;
+ }
+ if (line == lineCount) {
+ if (block.next().isValid() && block.next() != q->firstVisibleBlock()) {
+ block = block.next();
+ line = 0;
+ } else {
+ --line;
+ }
+ }
+ }
+ setTopBlock(block.blockNumber(), line);
+
+ if (moveCursor) {
+ // move using movePosition to keep the cursor's x
+ lastY += verticalOffset();
+ bool moved = false;
+ do {
+ moved = cursor.movePosition(op, moveMode);
+ } while (moved && control->cursorRect(cursor).top() > lastY);
+ }
+ }
+
+ if (moveCursor) {
+ control->setTextCursor(cursor);
+ pageUpDownLastCursorYIsValid = true;
+ }
+}
+
+#ifndef QT_NO_SCROLLBAR
+
+void QPlainTextEditPrivate::_q_adjustScrollbars()
+{
+ Q_Q(QPlainTextEdit);
+ QTextDocument *doc = control->document();
+ QPlainTextDocumentLayout *documentLayout = qobject_cast<QPlainTextDocumentLayout*>(doc->documentLayout());
+ Q_ASSERT(documentLayout);
+ bool documentSizeChangedBlocked = documentLayout->priv()->blockDocumentSizeChanged;
+ documentLayout->priv()->blockDocumentSizeChanged = true;
+ qreal margin = doc->documentMargin();
+
+ int vmax = 0;
+
+ int vSliderLength = 0;
+ if (!centerOnScroll && q->isVisible()) {
+ QTextBlock block = doc->lastBlock();
+ const int visible = static_cast<int>(viewport->rect().height() - margin - 1);
+ int y = 0;
+ int visibleFromBottom = 0;
+
+ while (block.isValid()) {
+ if (!block.isVisible()) {
+ block = block.previous();
+ continue;
+ }
+ y += int(documentLayout->blockBoundingRect(block).height());
+
+ QTextLayout *layout = block.layout();
+ int layoutLineCount = layout->lineCount();
+ if (y > visible) {
+ int lineNumber = 0;
+ while (lineNumber < layoutLineCount) {
+ QTextLine line = layout->lineAt(lineNumber);
+ const QRectF lr = line.naturalTextRect();
+ if (int(lr.top()) >= y - visible)
+ break;
+ ++lineNumber;
+ }
+ if (lineNumber < layoutLineCount)
+ visibleFromBottom += (layoutLineCount - lineNumber - 1);
+ break;
+
+ }
+ visibleFromBottom += layoutLineCount;
+ block = block.previous();
+ }
+ vmax = qMax(0, doc->lineCount() - visibleFromBottom);
+ vSliderLength = visibleFromBottom;
+
+ } else {
+ vmax = qMax(0, doc->lineCount() - 1);
+ vSliderLength = viewport->height() / q->fontMetrics().lineSpacing();
+ }
+
+
+
+ QSizeF documentSize = documentLayout->documentSize();
+ vbar->setRange(0, qMax(0, vmax));
+ vbar->setPageStep(vSliderLength);
+ int visualTopLine = vmax;
+ QTextBlock firstVisibleBlock = q->firstVisibleBlock();
+ if (firstVisibleBlock.isValid())
+ visualTopLine = firstVisibleBlock.firstLineNumber() + topLine;
+ bool vbarSignalsBlocked = vbar->blockSignals(true);
+ vbar->setValue(visualTopLine);
+ vbar->blockSignals(vbarSignalsBlocked);
+
+ hbar->setRange(0, (int)documentSize.width() - viewport->width());
+ hbar->setPageStep(viewport->width());
+ documentLayout->priv()->blockDocumentSizeChanged = documentSizeChangedBlocked;
+ setTopLine(vbar->value());
+}
+
+#endif
+
+
+void QPlainTextEditPrivate::ensureViewportLayouted()
+{
+}
+
+/*!
+ \class QPlainTextEdit
+ \since 4.4
+ \brief The QPlainTextEdit class provides a widget that is used to edit and display
+ plain text.
+
+ \ingroup text
+ \mainclass
+
+ \tableofcontents
+
+ \section1 Introduction and Concepts
+
+ QPlainTextEdit is an advanced viewer/editor supporting plain
+ text. It is optimized to handle large documents and to respond
+ quickly to user input.
+
+ QPlainText uses very much the same technology and concepts as
+ QTextEdit, but is optimized for plain text handling.
+
+ QPlainTextEdit 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
+ signifies a paragraph. A document consists of zero or more
+ paragraphs. 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 shape of the mouse cursor on a QPlainTextEdit is
+ Qt::IBeamCursor by default. It can be changed through the
+ viewport()'s cursor property.
+
+ \section1 Using QPlainTextEdit as a Display Widget
+
+ The text is set or replaced using setPlainText() which deletes any
+ existing text and replaces it with the text passed in the
+ setPlainText() call.
+
+ Text itself can be inserted using the QTextCursor class or using
+ the convenience functins insertPlainText(), appendPlainText() or
+ paste().
+
+ By default the text edit wraps words at whitespace to fit within
+ the text edit widget. The setLineWrapMode() function is used to
+ specify the kind of line wrap you want, \l WidgetWidth or \l
+ NoWrap if you don't want any wrapping. If you use word wrap to
+ the widget's width \l WidgetWidth, you can specify whether to
+ break on whitespace or anywhere with setWordWrapMode().
+
+ The find() function can be used to find and select a given string
+ within the text.
+
+ If you want to limit the total number of paragraphs in a
+ QPlainTextEdit, as it is for example useful in a log viewer, then
+ you can use the maximumBlockCount property. The combination of
+ setMaximumBlockCount() and appendPlainText() turns QPlainTextEdit
+ into an efficient viewer for log text. The scrolling can be
+ reduced with the centerOnScroll() property, making the log viewer
+ even faster. Text can be formatted in a limited way, either using
+ a syntax highlighter (see below), or by appending html-formatted
+ text with appendHtml(). While QPlainTextEdit does not support
+ complex rich text rendering with tables and floats, it does
+ support limited paragraph-based formatting that you may need in a
+ log viewer.
+
+ \section2 Read-only Key Bindings
+
+ When QPlainTextEdit 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 Qt::UpArrow \i Moves one line up.
+ \row \i Qt::DownArrow \i Moves one line down.
+ \row \i Qt::LeftArrow \i Moves one character to the left.
+ \row \i Qt::RightArrow \i Moves one character to the right.
+ \row \i PageUp \i Moves one (viewport) page up.
+ \row \i PageDown \i Moves one (viewport) page down.
+ \row \i Home \i Moves to the beginning of the text.
+ \row \i End \i Moves to the end of the text.
+ \row \i Alt+Wheel
+ \i Scrolls the page horizontally (the Wheel is the mouse wheel).
+ \row \i Ctrl+Wheel \i Zooms the text.
+ \row \i Ctrl+A \i Selects all text.
+ \endtable
+
+
+ \section1 Using QPlainTextEdit as an Editor
+
+ All the information about using QPlainTextEdit as a display widget also
+ applies here.
+
+ Selection of text is handled by the QTextCursor class, which provides
+ functionality for creating selections, retrieving the text contents or
+ deleting selections. You can retrieve the object that corresponds with
+ the user-visible cursor using the textCursor() method. If you want to set
+ a selection in QPlainTextEdit just create one on a QTextCursor object and
+ then make that cursor the visible cursor using setCursor(). The selection
+ can be copied to the clipboard with copy(), or cut to the clipboard with
+ cut(). The entire text can be selected using selectAll().
+
+ QPlainTextEdit holds a QTextDocument object which can be retrieved using the
+ document() method. You can also set your own document object using setDocument().
+ QTextDocument emits a textChanged() signal if the text changes and it also
+ provides a isModified() function which will return true if the text has been
+ modified since it was either loaded or since the last call to setModified
+ with false as argument. In addition it provides methods for undo and redo.
+
+ \section2 Syntax Highlighting
+
+ Just like QTextEdit, QPlainTextEdit works together with
+ QSyntaxHighlighter.
+
+ \section2 Editing Key Bindings
+
+ The list of key bindings which are implemented for editing:
+ \table
+ \header \i Keypresses \i Action
+ \row \i Backspace \i Deletes the character to the left of the cursor.
+ \row \i Delete \i Deletes the character to the right of the cursor.
+ \row \i Ctrl+C \i Copy the selected text to the clipboard.
+ \row \i Ctrl+Insert \i Copy the selected text to the clipboard.
+ \row \i Ctrl+K \i Deletes to the end of the line.
+ \row \i Ctrl+V \i Pastes the clipboard text into text edit.
+ \row \i Shift+Insert \i Pastes the clipboard text into text edit.
+ \row \i Ctrl+X \i Deletes the selected text and copies it to the clipboard.
+ \row \i Shift+Delete \i Deletes the selected text and copies it to the clipboard.
+ \row \i Ctrl+Z \i Undoes the last operation.
+ \row \i Ctrl+Y \i Redoes the last operation.
+ \row \i LeftArrow \i Moves the cursor one character to the left.
+ \row \i Ctrl+LeftArrow \i Moves the cursor one word to the left.
+ \row \i RightArrow \i Moves the cursor one character to the right.
+ \row \i Ctrl+RightArrow \i Moves the cursor one word to the right.
+ \row \i UpArrow \i Moves the cursor one line up.
+ \row \i Ctrl+UpArrow \i Moves the cursor one word up.
+ \row \i DownArrow \i Moves the cursor one line down.
+ \row \i Ctrl+Down Arrow \i Moves the cursor one word down.
+ \row \i PageUp \i Moves the cursor one page up.
+ \row \i PageDown \i Moves the cursor one page down.
+ \row \i Home \i Moves the cursor to the beginning of the line.
+ \row \i Ctrl+Home \i Moves the cursor to the beginning of the text.
+ \row \i End \i Moves the cursor to the end of the line.
+ \row \i Ctrl+End \i Moves the cursor to the end of the text.
+ \row \i Alt+Wheel \i Scrolls the page horizontally (the Wheel is the mouse wheel).
+ \row \i Ctrl+Wheel \i Zooms the text.
+ \endtable
+
+ To select (mark) text hold down the Shift key whilst pressing one
+ of the movement keystrokes, for example, \e{Shift+Right Arrow}
+ will select the character to the right, and \e{Shift+Ctrl+Right
+ Arrow} will select the word to the right, etc.
+
+ \section1 Differences to QTextEdit
+
+ QPlainTextEdit is a thin class, implemented by using most of the
+ technology that is behind QTextEdit and QTextDocument. Its
+ performance benefits over QTextEdit stem mostly from using a
+ different and simplified text layout called
+ QPlainTextDocumentLayout on the text document (see
+ QTextDocument::setDocumentLayout()). The plain text document layout
+ does not support tables nor embedded frames, and \e{replaces a
+ pixel-exact height calculation with a line-by-line respectively
+ paragraph-by-paragraph scrolling approach}. This makes it possible
+ to handle significantly larger documents, and still resize the
+ editor with line wrap enabled in real time. It also makes for a
+ fast log viewer (see setMaximumBlockCount()).
+
+
+ \sa QTextDocument, QTextCursor, {Application Example},
+ {Syntax Highlighter Example}, {Rich Text Processing}
+
+*/
+
+/*!
+ \property QPlainTextEdit::plainText
+
+ This property gets and sets the plain text editor's contents. The previous
+ contents are removed and undo/redo history is reset when this property is set.
+
+ By default, for an editor with no contents, this property contains an empty string.
+*/
+
+/*!
+ \property QPlainTextEdit::undoRedoEnabled
+ \brief whether undo and redo are enabled
+
+ Users are only able to undo or redo actions if this property is
+ true, and if there is an action that can be undone (or redone).
+
+ By default, this property is true.
+*/
+
+/*!
+ \enum QPlainTextEdit::LineWrapMode
+
+ \value NoWrap
+ \value WidgetWidth
+*/
+
+
+/*!
+ Constructs an empty QPlainTextEdit with parent \a
+ parent.
+*/
+QPlainTextEdit::QPlainTextEdit(QWidget *parent)
+ : QAbstractScrollArea(*new QPlainTextEditPrivate, parent)
+{
+ Q_D(QPlainTextEdit);
+ d->init();
+}
+
+/*!
+ \internal
+*/
+QPlainTextEdit::QPlainTextEdit(QPlainTextEditPrivate &dd, QWidget *parent)
+ : QAbstractScrollArea(dd, parent)
+{
+ Q_D(QPlainTextEdit);
+ d->init();
+}
+
+/*!
+ Constructs a QPlainTextEdit with parent \a parent. The text edit will display
+ the plain text \a text.
+*/
+QPlainTextEdit::QPlainTextEdit(const QString &text, QWidget *parent)
+ : QAbstractScrollArea(*new QPlainTextEditPrivate, parent)
+{
+ Q_D(QPlainTextEdit);
+ d->init(text);
+}
+
+
+/*!
+ Destructor.
+*/
+QPlainTextEdit::~QPlainTextEdit()
+{
+ Q_D(QPlainTextEdit);
+ if (d->documentLayoutPtr) {
+ if (d->documentLayoutPtr->priv()->mainViewPrivate == d)
+ d->documentLayoutPtr->priv()->mainViewPrivate = 0;
+ }
+}
+
+/*!
+ Makes \a document the new document of the text editor.
+
+ The parent QObject of the provided document remains the owner
+ of the object. If the current document is a child of the text
+ editor, then it is deleted.
+
+ The document must have a document layout that inherits
+ QPlainTextDocumentLayout (see QTextDocument::setDocumentLayout()).
+
+ \sa document()
+*/
+void QPlainTextEdit::setDocument(QTextDocument *document)
+{
+ Q_D(QPlainTextEdit);
+ QPlainTextDocumentLayout *documentLayout = 0;
+
+ if (!document) {
+ document = new QTextDocument(d->control);
+ documentLayout = new QPlainTextDocumentLayout(document);
+ document->setDocumentLayout(documentLayout);
+ } else {
+ documentLayout = qobject_cast<QPlainTextDocumentLayout*>(document->documentLayout());
+ if (!documentLayout) {
+ qWarning("QPlainTextEdit::setDocument: Document set does not support QPlainTextDocumentLayout");
+ return;
+ }
+ }
+ d->control->setDocument(document);
+ if (!documentLayout->priv()->mainViewPrivate)
+ documentLayout->priv()->mainViewPrivate = d;
+ d->documentLayoutPtr = documentLayout;
+ d->updateDefaultTextOption();
+ d->relayoutDocument();
+ d->_q_adjustScrollbars();
+}
+
+/*!
+ Returns a pointer to the underlying document.
+
+ \sa setDocument()
+*/
+QTextDocument *QPlainTextEdit::document() const
+{
+ Q_D(const QPlainTextEdit);
+ return d->control->document();
+}
+
+/*!
+ Sets the visible \a cursor.
+*/
+void QPlainTextEdit::setTextCursor(const QTextCursor &cursor)
+{
+ Q_D(QPlainTextEdit);
+ d->control->setTextCursor(cursor);
+}
+
+/*!
+ Returns a copy of the QTextCursor that represents the currently visible cursor.
+ Note that changes on the returned cursor do not affect QPlainTextEdit's cursor; use
+ setTextCursor() to update the visible cursor.
+ */
+QTextCursor QPlainTextEdit::textCursor() const
+{
+ Q_D(const QPlainTextEdit);
+ return d->control->textCursor();
+}
+
+
+/*!
+ 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 redo()
+*/
+void QPlainTextEdit::undo()
+{
+ Q_D(QPlainTextEdit);
+ d->control->undo();
+}
+
+void QPlainTextEdit::redo()
+{
+ Q_D(QPlainTextEdit);
+ d->control->redo();
+}
+
+/*!
+ \fn void QPlainTextEdit::redo()
+
+ 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 undo()
+*/
+
+#ifndef QT_NO_CLIPBOARD
+/*!
+ Copies the selected text to the clipboard and deletes it from
+ the text edit.
+
+ If there is no selected text nothing happens.
+
+ \sa copy() paste()
+*/
+
+void QPlainTextEdit::cut()
+{
+ Q_D(QPlainTextEdit);
+ d->control->cut();
+}
+
+/*!
+ Copies any selected text to the clipboard.
+
+ \sa copyAvailable()
+*/
+
+void QPlainTextEdit::copy()
+{
+ Q_D(QPlainTextEdit);
+ d->control->copy();
+}
+
+/*!
+ Pastes the text from the clipboard into the text edit at the
+ current cursor position.
+
+ If there is no text in the clipboard nothing happens.
+
+ To change the behavior of this function, i.e. to modify what
+ QPlainTextEdit can paste and how it is being pasted, reimplement the
+ virtual canInsertFromMimeData() and insertFromMimeData()
+ functions.
+
+ \sa cut() copy()
+*/
+
+void QPlainTextEdit::paste()
+{
+ Q_D(QPlainTextEdit);
+ d->control->paste();
+}
+#endif
+
+/*!
+ Deletes all the text in the text edit.
+
+ Note that the undo/redo history is cleared by this function.
+
+ \sa cut() setPlainText()
+*/
+void QPlainTextEdit::clear()
+{
+ Q_D(QPlainTextEdit);
+ // clears and sets empty content
+ d->control->topBlock = d->topLine = 0;
+ d->control->clear();
+}
+
+
+/*!
+ Selects all text.
+
+ \sa copy() cut() textCursor()
+ */
+void QPlainTextEdit::selectAll()
+{
+ Q_D(QPlainTextEdit);
+ d->control->selectAll();
+}
+
+/*! \internal
+*/
+bool QPlainTextEdit::event(QEvent *e)
+{
+ Q_D(QPlainTextEdit);
+
+#ifndef QT_NO_CONTEXTMENU
+ if (e->type() == QEvent::ContextMenu
+ && static_cast<QContextMenuEvent *>(e)->reason() == QContextMenuEvent::Keyboard) {
+ ensureCursorVisible();
+ const QPoint cursorPos = cursorRect().center();
+ QContextMenuEvent ce(QContextMenuEvent::Keyboard, cursorPos, d->viewport->mapToGlobal(cursorPos));
+ ce.setAccepted(e->isAccepted());
+ const bool result = QAbstractScrollArea::event(&ce);
+ e->setAccepted(ce.isAccepted());
+ return result;
+ }
+#endif // QT_NO_CONTEXTMENU
+ if (e->type() == QEvent::ShortcutOverride
+ || e->type() == QEvent::ToolTip) {
+ d->sendControlEvent(e);
+ }
+#ifdef QT_KEYPAD_NAVIGATION
+ else if (e->type() == QEvent::EnterEditFocus || e->type() == QEvent::LeaveEditFocus) {
+ if (QApplication::keypadNavigationEnabled())
+ d->sendControlEvent(e);
+ }
+#endif
+ return QAbstractScrollArea::event(e);
+}
+
+/*! \internal
+*/
+
+void QPlainTextEdit::timerEvent(QTimerEvent *e)
+{
+ Q_D(QPlainTextEdit);
+ if (e->timerId() == d->autoScrollTimer.timerId()) {
+ QRect visible = d->viewport->rect();
+ QPoint pos;
+ if (d->inDrag) {
+ pos = d->autoScrollDragPos;
+ visible.adjust(qMin(visible.width()/3,20), qMin(visible.height()/3,20),
+ -qMin(visible.width()/3,20), -qMin(visible.height()/3,20));
+ } else {
+ const QPoint globalPos = QCursor::pos();
+ pos = d->viewport->mapFromGlobal(globalPos);
+ QMouseEvent ev(QEvent::MouseMove, pos, globalPos, Qt::LeftButton, Qt::LeftButton, Qt::NoModifier);
+ mouseMoveEvent(&ev);
+ }
+ int deltaY = qMax(pos.y() - visible.top(), visible.bottom() - pos.y()) - visible.height();
+ int deltaX = qMax(pos.x() - visible.left(), visible.right() - pos.x()) - visible.width();
+ int delta = qMax(deltaX, deltaY);
+ if (delta >= 0) {
+ if (delta < 7)
+ delta = 7;
+ int timeout = 4900 / (delta * delta);
+ d->autoScrollTimer.start(timeout, this);
+
+ if (deltaY > 0)
+ d->vbar->triggerAction(pos.y() < visible.center().y() ?
+ QAbstractSlider::SliderSingleStepSub
+ : QAbstractSlider::SliderSingleStepAdd);
+ if (deltaX > 0)
+ d->hbar->triggerAction(pos.x() < visible.center().x() ?
+ QAbstractSlider::SliderSingleStepSub
+ : QAbstractSlider::SliderSingleStepAdd);
+ }
+ }
+#ifdef QT_KEYPAD_NAVIGATION
+ else if (e->timerId() == d->deleteAllTimer.timerId()) {
+ d->deleteAllTimer.stop();
+ clear();
+ }
+#endif
+}
+
+/*!
+ Changes the text of the text edit to the string \a text.
+ Any previous text is removed.
+
+ \a text is interpreted as plain text.
+
+ Note that the undo/redo history is cleared by this function.
+
+ \sa toText()
+*/
+
+void QPlainTextEdit::setPlainText(const QString &text)
+{
+ Q_D(QPlainTextEdit);
+ d->control->setPlainText(text);
+}
+
+/*!
+ \fn QString QPlainTextEdit::toPlainText() const
+
+ Returns the text of the text edit as plain text.
+
+ \sa QPlainTextEdit::setPlainText()
+ */
+
+/*! \reimp
+*/
+void QPlainTextEdit::keyPressEvent(QKeyEvent *e)
+{
+ Q_D(QPlainTextEdit);
+
+#ifdef QT_KEYPAD_NAVIGATION
+ switch (e->key()) {
+ case Qt::Key_Select:
+ if (QApplication::keypadNavigationEnabled()) {
+ if (!(d->control->textInteractionFlags() & Qt::LinksAccessibleByKeyboard))
+ setEditFocus(!hasEditFocus());
+ else {
+ if (!hasEditFocus())
+ setEditFocus(true);
+ else {
+ QTextCursor cursor = d->control->textCursor();
+ QTextCharFormat charFmt = cursor.charFormat();
+ if (!cursor.hasSelection() || charFmt.anchorHref().isEmpty()) {
+ setEditFocus(false);
+ }
+ }
+ }
+ }
+ break;
+ case Qt::Key_Back:
+ case Qt::Key_No:
+ if (!QApplication::keypadNavigationEnabled()
+ || (QApplication::keypadNavigationEnabled() && !hasEditFocus())) {
+ e->ignore();
+ return;
+ }
+ break;
+ default:
+ if (QApplication::keypadNavigationEnabled()) {
+ if (!hasEditFocus() && !(e->modifiers() & Qt::ControlModifier)) {
+ if (e->text()[0].isPrint()) {
+ setEditFocus(true);
+ clear();
+ } else {
+ e->ignore();
+ return;
+ }
+ }
+ }
+ break;
+ }
+#endif
+
+ if (!(d->control->textInteractionFlags() & Qt::TextEditable)) {
+ switch (e->key()) {
+ case Qt::Key_Space:
+ e->accept();
+ if (e->modifiers() & Qt::ShiftModifier)
+ d->vbar->triggerAction(QAbstractSlider::SliderPageStepSub);
+ else
+ d->vbar->triggerAction(QAbstractSlider::SliderPageStepAdd);
+ break;
+ default:
+ d->sendControlEvent(e);
+ if (!e->isAccepted() && e->modifiers() == Qt::NoModifier) {
+ if (e->key() == Qt::Key_Home) {
+ d->vbar->triggerAction(QAbstractSlider::SliderToMinimum);
+ e->accept();
+ } else if (e->key() == Qt::Key_End) {
+ d->vbar->triggerAction(QAbstractSlider::SliderToMaximum);
+ e->accept();
+ }
+ }
+ if (!e->isAccepted()) {
+ QAbstractScrollArea::keyPressEvent(e);
+ }
+ }
+ return;
+ }
+
+#ifndef QT_NO_SHORTCUT
+ if (e == QKeySequence::MoveToPreviousPage) {
+ e->accept();
+ d->pageUpDown(QTextCursor::Up, QTextCursor::MoveAnchor);
+ return;
+ } else if (e == QKeySequence::MoveToNextPage) {
+ e->accept();
+ d->pageUpDown(QTextCursor::Down, QTextCursor::MoveAnchor);
+ return;
+ } else if (e == QKeySequence::SelectPreviousPage) {
+ e->accept();
+ d->pageUpDown(QTextCursor::Up, QTextCursor::KeepAnchor);
+ return;
+ } else if (e ==QKeySequence::SelectNextPage) {
+ e->accept();
+ d->pageUpDown(QTextCursor::Down, QTextCursor::KeepAnchor);
+ return;
+ }
+#endif // QT_NO_SHORTCUT
+
+
+ d->sendControlEvent(e);
+#ifdef QT_KEYPAD_NAVIGATION
+ if (!e->isAccepted()) {
+ switch (e->key()) {
+ case Qt::Key_Up:
+ case Qt::Key_Down:
+ if (QApplication::keypadNavigationEnabled()) {
+ // Cursor position didn't change, so we want to leave
+ // these keys to change focus.
+ e->ignore();
+ return;
+ }
+ break;
+ case Qt::Key_Back:
+ if (!e->isAutoRepeat()) {
+ if (QApplication::keypadNavigationEnabled()) {
+ if (document()->isEmpty()) {
+ setEditFocus(false);
+ e->accept();
+ } else if (!d->deleteAllTimer.isActive()) {
+ e->accept();
+ d->deleteAllTimer.start(750, this);
+ }
+ } else {
+ e->ignore();
+ return;
+ }
+ }
+ break;
+ default: break;
+ }
+ }
+#endif
+}
+
+/*! \reimp
+*/
+void QPlainTextEdit::keyReleaseEvent(QKeyEvent *e)
+{
+#ifdef QT_KEYPAD_NAVIGATION
+ Q_D(QPlainTextEdit);
+ if (QApplication::keypadNavigationEnabled()) {
+ if (!e->isAutoRepeat() && e->key() == Qt::Key_Back
+ && d->deleteAllTimer.isActive()) {
+ d->deleteAllTimer.stop();
+ QTextCursor cursor = d->control->textCursor();
+ QTextBlockFormat blockFmt = cursor.blockFormat();
+
+ QTextList *list = cursor.currentList();
+ if (list && cursor.atBlockStart()) {
+ list->remove(cursor.block());
+ } else if (cursor.atBlockStart() && blockFmt.indent() > 0) {
+ blockFmt.setIndent(blockFmt.indent() - 1);
+ cursor.setBlockFormat(blockFmt);
+ } else {
+ cursor.deletePreviousChar();
+ }
+ setTextCursor(cursor);
+ }
+ }
+#else
+ Q_UNUSED(e);
+#endif
+}
+
+/*!
+ Loads the resource specified by the given \a type and \a name.
+
+ This function is an extension of QTextDocument::loadResource().
+
+ \sa QTextDocument::loadResource()
+*/
+QVariant QPlainTextEdit::loadResource(int type, const QUrl &name)
+{
+ Q_UNUSED(type);
+ Q_UNUSED(name);
+ return QVariant();
+}
+
+/*! \reimp
+*/
+void QPlainTextEdit::resizeEvent(QResizeEvent *e)
+{
+ Q_D(QPlainTextEdit);
+ if (e->oldSize().width() != e->size().width())
+ d->relayoutDocument();
+ d->_q_adjustScrollbars();
+}
+
+void QPlainTextEditPrivate::relayoutDocument()
+{
+ QTextDocument *doc = control->document();
+ QPlainTextDocumentLayout *documentLayout = qobject_cast<QPlainTextDocumentLayout*>(doc->documentLayout());
+ Q_ASSERT(documentLayout);
+ documentLayoutPtr = documentLayout;
+
+ int width = viewport->width();
+
+ if (documentLayout->priv()->mainViewPrivate == 0
+ || documentLayout->priv()->mainViewPrivate == this
+ || width > documentLayout->textWidth()) {
+ documentLayout->priv()->mainViewPrivate = this;
+ documentLayout->setTextWidth(width);
+ }
+}
+
+static void fillBackground(QPainter *p, const QRectF &rect, QBrush brush, QRectF gradientRect = QRectF())
+{
+ p->save();
+ if (brush.style() >= Qt::LinearGradientPattern && brush.style() <= Qt::ConicalGradientPattern) {
+ if (!gradientRect.isNull()) {
+ QTransform m;
+ m.translate(gradientRect.left(), gradientRect.top());
+ m.scale(gradientRect.width(), gradientRect.height());
+ brush.setTransform(m);
+ const_cast<QGradient *>(brush.gradient())->setCoordinateMode(QGradient::LogicalMode);
+ }
+ } else {
+ p->setBrushOrigin(rect.topLeft());
+ }
+ p->fillRect(rect, brush);
+ p->restore();
+}
+
+
+
+/*! \reimp
+*/
+void QPlainTextEdit::paintEvent(QPaintEvent *e)
+{
+ QPainter painter(viewport());
+ Q_ASSERT(qobject_cast<QPlainTextDocumentLayout*>(document()->documentLayout()));
+
+ QPointF offset(contentOffset());
+
+ QRect er = e->rect();
+ QRect viewportRect = viewport()->rect();
+
+ bool editable = !isReadOnly();
+
+ QTextBlock block = firstVisibleBlock();
+ qreal maximumWidth = document()->documentLayout()->documentSize().width();
+
+ // keep right margin clean from full-width selection
+ int maxX = offset.x() + qMax((qreal)viewportRect.width(), maximumWidth)
+ - document()->documentMargin();
+ er.setRight(qMin(er.right(), maxX));
+ painter.setClipRect(er);
+
+
+ QAbstractTextDocumentLayout::PaintContext context = getPaintContext();
+
+ while (block.isValid()) {
+
+ QRectF r = blockBoundingRect(block).translated(offset);
+ QTextLayout *layout = block.layout();
+
+ if (!block.isVisible()) {
+ offset.ry() += r.height();
+ block = block.next();
+ continue;
+ }
+
+ if (r.bottom() >= er.top() && r.top() <= er.bottom()) {
+
+ QTextBlockFormat blockFormat = block.blockFormat();
+
+ QBrush bg = blockFormat.background();
+ if (bg != Qt::NoBrush) {
+ QRectF contentsRect = r;
+ contentsRect.setWidth(qMax(r.width(), maximumWidth));
+ fillBackground(&painter, contentsRect, bg);
+ }
+
+
+ QVector<QTextLayout::FormatRange> selections;
+ int blpos = block.position();
+ int bllen = block.length();
+ for (int i = 0; i < context.selections.size(); ++i) {
+ const QAbstractTextDocumentLayout::Selection &range = context.selections.at(i);
+ const int selStart = range.cursor.selectionStart() - blpos;
+ const int selEnd = range.cursor.selectionEnd() - blpos;
+ if (selStart < bllen && selEnd > 0
+ && selEnd > selStart) {
+ QTextLayout::FormatRange o;
+ o.start = selStart;
+ o.length = selEnd - selStart;
+ o.format = range.format;
+ selections.append(o);
+ } else if (!range.cursor.hasSelection() && range.format.hasProperty(QTextFormat::FullWidthSelection)
+ && block.contains(range.cursor.position())) {
+ // for full width selections we don't require an actual selection, just
+ // a position to specify the line. that's more convenience in usage.
+ QTextLayout::FormatRange o;
+ QTextLine l = layout->lineForTextPosition(range.cursor.position() - blpos);
+ o.start = l.textStart();
+ o.length = l.textLength();
+ if (o.start + o.length == bllen - 1)
+ ++o.length; // include newline
+ o.format = range.format;
+ selections.append(o);
+ }
+ }
+
+ bool drawCursor = (editable
+ && context.cursorPosition >= blpos
+ && context.cursorPosition < blpos + bllen);
+
+ bool drawCursorAsBlock = drawCursor && overwriteMode() ;
+
+ if (drawCursorAsBlock) {
+ if (context.cursorPosition == blpos + bllen - 1) {
+ drawCursorAsBlock = false;
+ } else {
+ QTextLayout::FormatRange o;
+ o.start = context.cursorPosition - blpos;
+ o.length = 1;
+ o.format.setForeground(palette().base());
+ o.format.setBackground(palette().text());
+ selections.append(o);
+ }
+ }
+
+
+ layout->draw(&painter, offset, selections, er);
+ if ((drawCursor && !drawCursorAsBlock)
+ || (editable && context.cursorPosition < -1
+ && !layout->preeditAreaText().isEmpty())) {
+ int cpos = context.cursorPosition;
+ if (cpos < -1)
+ cpos = layout->preeditAreaPosition() - (cpos + 2);
+ else
+ cpos -= blpos;
+ layout->drawCursor(&painter, offset, cpos, cursorWidth());
+ }
+ }
+
+ offset.ry() += r.height();
+ if (offset.y() > viewportRect.height())
+ break;
+ block = block.next();
+ }
+
+ if (backgroundVisible() && !block.isValid() && offset.y() <= er.bottom()
+ && (centerOnScroll() || verticalScrollBar()->maximum() == verticalScrollBar()->minimum())) {
+ painter.fillRect(QRect(QPoint((int)er.left(), (int)offset.y()), er.bottomRight()), palette().background());
+ }
+}
+
+
+void QPlainTextEditPrivate::updateDefaultTextOption()
+{
+ QTextDocument *doc = control->document();
+
+ QTextOption opt = doc->defaultTextOption();
+ QTextOption::WrapMode oldWrapMode = opt.wrapMode();
+
+ if (lineWrap == QPlainTextEdit::NoWrap)
+ opt.setWrapMode(QTextOption::NoWrap);
+ else
+ opt.setWrapMode(wordWrap);
+
+ if (opt.wrapMode() != oldWrapMode)
+ doc->setDefaultTextOption(opt);
+}
+
+
+/*! \reimp
+*/
+void QPlainTextEdit::mousePressEvent(QMouseEvent *e)
+{
+ Q_D(QPlainTextEdit);
+#ifdef QT_KEYPAD_NAVIGATION
+ if (QApplication::keypadNavigationEnabled() && !hasEditFocus())
+ setEditFocus(true);
+#endif
+ d->sendControlEvent(e);
+}
+
+/*! \reimp
+*/
+void QPlainTextEdit::mouseMoveEvent(QMouseEvent *e)
+{
+ Q_D(QPlainTextEdit);
+ d->inDrag = false; // paranoia
+ const QPoint pos = e->pos();
+ d->sendControlEvent(e);
+ if (!(e->buttons() & Qt::LeftButton))
+ return;
+ QRect visible = d->viewport->rect();
+ if (visible.contains(pos))
+ d->autoScrollTimer.stop();
+ else if (!d->autoScrollTimer.isActive())
+ d->autoScrollTimer.start(100, this);
+}
+
+/*! \reimp
+*/
+void QPlainTextEdit::mouseReleaseEvent(QMouseEvent *e)
+{
+ Q_D(QPlainTextEdit);
+ d->sendControlEvent(e);
+ if (d->autoScrollTimer.isActive()) {
+ d->autoScrollTimer.stop();
+ d->ensureCursorVisible();
+ }
+}
+
+/*! \reimp
+*/
+void QPlainTextEdit::mouseDoubleClickEvent(QMouseEvent *e)
+{
+ Q_D(QPlainTextEdit);
+ d->sendControlEvent(e);
+}
+
+/*! \reimp
+*/
+bool QPlainTextEdit::focusNextPrevChild(bool next)
+{
+ Q_D(const QPlainTextEdit);
+ if (!d->tabChangesFocus && d->control->textInteractionFlags() & Qt::TextEditable)
+ return false;
+ return QAbstractScrollArea::focusNextPrevChild(next);
+}
+
+#ifndef QT_NO_CONTEXTMENU
+/*!
+ \fn void QPlainTextEdit::contextMenuEvent(QContextMenuEvent *event)
+
+ Shows the standard context menu created with createStandardContextMenu().
+
+ If you do not want the text edit to have a context menu, you can set
+ its \l contextMenuPolicy to Qt::NoContextMenu. If you want to
+ customize the context menu, reimplement this function. If you want
+ to extend the standard context menu, reimplement this function, call
+ createStandardContextMenu() and extend the menu returned.
+
+ Information about the event is passed in the \a event object.
+
+ \snippet doc/src/snippets/code/src_gui_widgets_qplaintextedit.cpp 0
+*/
+void QPlainTextEdit::contextMenuEvent(QContextMenuEvent *e)
+{
+ Q_D(QPlainTextEdit);
+ d->sendControlEvent(e);
+}
+#endif // QT_NO_CONTEXTMENU
+
+#ifndef QT_NO_DRAGANDDROP
+/*! \reimp
+*/
+void QPlainTextEdit::dragEnterEvent(QDragEnterEvent *e)
+{
+ Q_D(QPlainTextEdit);
+ d->inDrag = true;
+ d->sendControlEvent(e);
+}
+
+/*! \reimp
+*/
+void QPlainTextEdit::dragLeaveEvent(QDragLeaveEvent *e)
+{
+ Q_D(QPlainTextEdit);
+ d->inDrag = false;
+ d->autoScrollTimer.stop();
+ d->sendControlEvent(e);
+}
+
+/*! \reimp
+*/
+void QPlainTextEdit::dragMoveEvent(QDragMoveEvent *e)
+{
+ Q_D(QPlainTextEdit);
+ d->autoScrollDragPos = e->pos();
+ if (!d->autoScrollTimer.isActive())
+ d->autoScrollTimer.start(100, this);
+ d->sendControlEvent(e);
+}
+
+/*! \reimp
+*/
+void QPlainTextEdit::dropEvent(QDropEvent *e)
+{
+ Q_D(QPlainTextEdit);
+ d->inDrag = false;
+ d->autoScrollTimer.stop();
+ d->sendControlEvent(e);
+}
+
+#endif // QT_NO_DRAGANDDROP
+
+/*! \reimp
+ */
+void QPlainTextEdit::inputMethodEvent(QInputMethodEvent *e)
+{
+ Q_D(QPlainTextEdit);
+#ifdef QT_KEYPAD_NAVIGATION
+ if (d->control->textInteractionFlags() & Qt::TextEditable
+ && QApplication::keypadNavigationEnabled()
+ && !hasEditFocus()) {
+ setEditFocus(true);
+ selectAll(); // so text is replaced rather than appended to
+ }
+#endif
+ d->sendControlEvent(e);
+}
+
+/*!\reimp
+*/
+void QPlainTextEdit::scrollContentsBy(int dx, int /*dy*/)
+{
+ Q_D(QPlainTextEdit);
+ d->setTopLine(d->vbar->value(), dx);
+}
+
+/*!\reimp
+*/
+QVariant QPlainTextEdit::inputMethodQuery(Qt::InputMethodQuery property) const
+{
+ Q_D(const QPlainTextEdit);
+ QVariant v = d->control->inputMethodQuery(property);
+ const QPoint offset(-d->horizontalOffset(), -0);
+ if (v.type() == QVariant::RectF)
+ v = v.toRectF().toRect().translated(offset);
+ else if (v.type() == QVariant::PointF)
+ v = v.toPointF().toPoint() + offset;
+ else if (v.type() == QVariant::Rect)
+ v = v.toRect().translated(offset);
+ else if (v.type() == QVariant::Point)
+ v = v.toPoint() + offset;
+ return v;
+}
+
+/*! \reimp
+*/
+void QPlainTextEdit::focusInEvent(QFocusEvent *e)
+{
+ Q_D(QPlainTextEdit);
+ QAbstractScrollArea::focusInEvent(e);
+ d->sendControlEvent(e);
+}
+
+/*! \reimp
+*/
+void QPlainTextEdit::focusOutEvent(QFocusEvent *e)
+{
+ Q_D(QPlainTextEdit);
+ QAbstractScrollArea::focusOutEvent(e);
+ d->sendControlEvent(e);
+}
+
+/*! \reimp
+*/
+void QPlainTextEdit::showEvent(QShowEvent *)
+{
+ Q_D(QPlainTextEdit);
+ if (d->showCursorOnInitialShow) {
+ d->showCursorOnInitialShow = false;
+ ensureCursorVisible();
+ }
+}
+
+/*! \reimp
+*/
+void QPlainTextEdit::changeEvent(QEvent *e)
+{
+ Q_D(QPlainTextEdit);
+ QAbstractScrollArea::changeEvent(e);
+ if (e->type() == QEvent::ApplicationFontChange
+ || e->type() == QEvent::FontChange) {
+ d->control->document()->setDefaultFont(font());
+ } else if(e->type() == QEvent::ActivationChange) {
+ if (!isActiveWindow())
+ d->autoScrollTimer.stop();
+ } else if (e->type() == QEvent::EnabledChange) {
+ e->setAccepted(isEnabled());
+ d->sendControlEvent(e);
+ } else if (e->type() == QEvent::PaletteChange) {
+ d->control->setPalette(palette());
+ } else if (e->type() == QEvent::LayoutDirectionChange) {
+ d->sendControlEvent(e);
+ }
+}
+
+/*! \reimp
+*/
+#ifndef QT_NO_WHEELEVENT
+void QPlainTextEdit::wheelEvent(QWheelEvent *e)
+{
+ QAbstractScrollArea::wheelEvent(e);
+ updateMicroFocus();
+}
+#endif
+
+#ifndef QT_NO_CONTEXTMENU
+/*! This function creates the standard context menu which is shown
+ when the user clicks on the line edit with the right mouse
+ button. It is called from the default contextMenuEvent() handler.
+ The popup menu's ownership is transferred to the caller.
+*/
+
+QMenu *QPlainTextEdit::createStandardContextMenu()
+{
+ Q_D(QPlainTextEdit);
+ return d->control->createStandardContextMenu(QPointF(), this);
+}
+#endif // QT_NO_CONTEXTMENU
+
+/*!
+ returns a QTextCursor at position \a pos (in viewport coordinates).
+*/
+QTextCursor QPlainTextEdit::cursorForPosition(const QPoint &pos) const
+{
+ Q_D(const QPlainTextEdit);
+ return d->control->cursorForPosition(d->mapToContents(pos));
+}
+
+/*!
+ returns a rectangle (in viewport coordinates) that includes the
+ \a cursor.
+ */
+QRect QPlainTextEdit::cursorRect(const QTextCursor &cursor) const
+{
+ Q_D(const QPlainTextEdit);
+ if (cursor.isNull())
+ return QRect();
+
+ QRect r = d->control->cursorRect(cursor).toRect();
+ r.translate(-d->horizontalOffset(),-d->verticalOffset());
+ return r;
+}
+
+/*!
+ returns a rectangle (in viewport coordinates) that includes the
+ cursor of the text edit.
+ */
+QRect QPlainTextEdit::cursorRect() const
+{
+ Q_D(const QPlainTextEdit);
+ QRect r = d->control->cursorRect().toRect();
+ r.translate(-d->horizontalOffset(),-d->verticalOffset());
+ return r;
+}
+
+
+/*!
+ \property QPlainTextEdit::overwriteMode
+ \brief whether text entered by the user will overwrite existing text
+
+ As with many text editors, the plain text editor widget can be configured
+ to insert or overwrite existing text with new text entered by the user.
+
+ If this property is true, existing text is overwritten, character-for-character
+ by new text; otherwise, text is inserted at the cursor position, displacing
+ existing text.
+
+ By default, this property is false (new text does not overwrite existing text).
+*/
+
+bool QPlainTextEdit::overwriteMode() const
+{
+ Q_D(const QPlainTextEdit);
+ return d->control->overwriteMode();
+}
+
+void QPlainTextEdit::setOverwriteMode(bool overwrite)
+{
+ Q_D(QPlainTextEdit);
+ d->control->setOverwriteMode(overwrite);
+}
+
+/*!
+ \property QPlainTextEdit::tabStopWidth
+ \brief the tab stop width in pixels
+
+ By default, this property contains a value of 80.
+*/
+
+int QPlainTextEdit::tabStopWidth() const
+{
+ Q_D(const QPlainTextEdit);
+ return qRound(d->control->document()->defaultTextOption().tabStop());
+}
+
+void QPlainTextEdit::setTabStopWidth(int width)
+{
+ Q_D(QPlainTextEdit);
+ QTextOption opt = d->control->document()->defaultTextOption();
+ if (opt.tabStop() == width || width < 0)
+ return;
+ opt.setTabStop(width);
+ d->control->document()->setDefaultTextOption(opt);
+}
+
+/*!
+ \property QPlainTextEdit::cursorWidth
+
+ This property specifies the width of the cursor in pixels. The default value is 1.
+*/
+int QPlainTextEdit::cursorWidth() const
+{
+ Q_D(const QPlainTextEdit);
+ return d->control->cursorWidth();
+}
+
+void QPlainTextEdit::setCursorWidth(int width)
+{
+ Q_D(QPlainTextEdit);
+ d->control->setCursorWidth(width);
+}
+
+
+
+/*!
+ This function allows temporarily marking certain regions in the document
+ with a given color, specified as \a selections. This can be useful for
+ example in a programming editor to mark a whole line of text with a given
+ background color to indicate the existence of a breakpoint.
+
+ \sa QTextEdit::ExtraSelection, extraSelections()
+*/
+void QPlainTextEdit::setExtraSelections(const QList<QTextEdit::ExtraSelection> &selections)
+{
+ Q_D(QPlainTextEdit);
+ d->control->setExtraSelections(selections);
+}
+
+/*!
+ Returns previously set extra selections.
+
+ \sa setExtraSelections()
+*/
+QList<QTextEdit::ExtraSelection> QPlainTextEdit::extraSelections() const
+{
+ Q_D(const QPlainTextEdit);
+ return d->control->extraSelections();
+}
+
+/*!
+ This function returns a new MIME data object to represent the contents
+ of the text edit's current selection. It is called when the selection needs
+ to be encapsulated into a new QMimeData object; for example, when a drag
+ and drop operation is started, or when data is copied to the clipboard.
+
+ If you reimplement this function, note that the ownership of the returned
+ QMimeData object is passed to the caller. The selection can be retrieved
+ by using the textCursor() function.
+*/
+QMimeData *QPlainTextEdit::createMimeDataFromSelection() const
+{
+ Q_D(const QPlainTextEdit);
+ return d->control->QTextControl::createMimeDataFromSelection();
+}
+
+/*!
+ This function returns true if the contents of the MIME data object, specified
+ by \a source, can be decoded and inserted into the document. It is called
+ for example when during a drag operation the mouse enters this widget and it
+ is necessary to determine whether it is possible to accept the drag.
+ */
+bool QPlainTextEdit::canInsertFromMimeData(const QMimeData *source) const
+{
+ Q_D(const QPlainTextEdit);
+ return d->control->QTextControl::canInsertFromMimeData(source);
+}
+
+/*!
+ This function inserts the contents of the MIME data object, specified
+ by \a source, into the text edit at the current cursor position. It is
+ called whenever text is inserted as the result of a clipboard paste
+ operation, or when the text edit accepts data from a drag and drop
+ operation.
+*/
+void QPlainTextEdit::insertFromMimeData(const QMimeData *source)
+{
+ Q_D(QPlainTextEdit);
+ d->control->QTextControl::insertFromMimeData(source);
+}
+
+/*!
+ \property QPlainTextEdit::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.
+*/
+
+bool QPlainTextEdit::isReadOnly() const
+{
+ Q_D(const QPlainTextEdit);
+ return !(d->control->textInteractionFlags() & Qt::TextEditable);
+}
+
+void QPlainTextEdit::setReadOnly(bool ro)
+{
+ Q_D(QPlainTextEdit);
+ Qt::TextInteractionFlags flags = Qt::NoTextInteraction;
+ if (ro) {
+ flags = Qt::TextSelectableByMouse;
+ } else {
+ flags = Qt::TextEditorInteraction;
+ }
+ setAttribute(Qt::WA_InputMethodEnabled, !ro);
+ d->control->setTextInteractionFlags(flags);
+}
+
+/*!
+ \property QPlainTextEdit::textInteractionFlags
+
+ Specifies how the label should interact with user input if it displays text.
+
+ If the flags contain either Qt::LinksAccessibleByKeyboard or Qt::TextSelectableByKeyboard
+ then the focus policy is also automatically set to Qt::ClickFocus.
+
+ The default value depends on whether the QPlainTextEdit is read-only
+ or editable, and whether it is a QTextBrowser or not.
+*/
+
+void QPlainTextEdit::setTextInteractionFlags(Qt::TextInteractionFlags flags)
+{
+ Q_D(QPlainTextEdit);
+ d->control->setTextInteractionFlags(flags);
+}
+
+Qt::TextInteractionFlags QPlainTextEdit::textInteractionFlags() const
+{
+ Q_D(const QPlainTextEdit);
+ return d->control->textInteractionFlags();
+}
+
+/*!
+ Merges the properties specified in \a modifier into the current character
+ format by calling QTextCursor::mergeCharFormat on the editor's cursor.
+ If the editor has a selection then the properties of \a modifier are
+ directly applied to the selection.
+
+ \sa QTextCursor::mergeCharFormat()
+ */
+void QPlainTextEdit::mergeCurrentCharFormat(const QTextCharFormat &modifier)
+{
+ Q_D(QPlainTextEdit);
+ d->control->mergeCurrentCharFormat(modifier);
+}
+
+/*!
+ Sets the char format that is be used when inserting new text to \a
+ format by calling QTextCursor::setCharFormat() on the editor's
+ cursor. If the editor has a selection then the char format is
+ directly applied to the selection.
+ */
+void QPlainTextEdit::setCurrentCharFormat(const QTextCharFormat &format)
+{
+ Q_D(QPlainTextEdit);
+ d->control->setCurrentCharFormat(format);
+}
+
+/*!
+ Returns the char format that is used when inserting new text.
+ */
+QTextCharFormat QPlainTextEdit::currentCharFormat() const
+{
+ Q_D(const QPlainTextEdit);
+ return d->control->currentCharFormat();
+}
+
+
+
+/*!
+ Convenience slot that inserts \a text at the current
+ cursor position.
+
+ It is equivalent to
+
+ \snippet doc/src/snippets/code/src_gui_widgets_qplaintextedit.cpp 1
+ */
+void QPlainTextEdit::insertPlainText(const QString &text)
+{
+ Q_D(QPlainTextEdit);
+ d->control->insertPlainText(text);
+}
+
+
+/*!
+ Moves the cursor by performing the given \a operation.
+
+ If \a mode is QTextCursor::KeepAnchor, the cursor selects the text it moves over.
+ This is the same effect that the user achieves when they hold down the Shift key
+ and move the cursor with the cursor keys.
+
+ \sa QTextCursor::movePosition()
+*/
+void QPlainTextEdit::moveCursor(QTextCursor::MoveOperation operation, QTextCursor::MoveMode mode)
+{
+ Q_D(QPlainTextEdit);
+ d->control->moveCursor(operation, mode);
+}
+
+/*!
+ Returns whether text can be pasted from the clipboard into the textedit.
+*/
+bool QPlainTextEdit::canPaste() const
+{
+ Q_D(const QPlainTextEdit);
+ return d->control->canPaste();
+}
+
+#ifndef QT_NO_PRINTER
+/*!
+ Convenience function to print the text edit's document to the given \a printer. This
+ is equivalent to calling the print method on the document directly except that this
+ function also supports QPrinter::Selection as print range.
+
+ \sa QTextDocument::print()
+*/
+void QPlainTextEdit::print(QPrinter *printer) const
+{
+ Q_D(const QPlainTextEdit);
+ d->control->print(printer);
+}
+#endif // QT _NO_PRINTER
+
+/*! \property QPlainTextEdit::tabChangesFocus
+ \brief whether \gui 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 \gui Tab key, as this breaks
+ the focus chain. The default is false.
+
+*/
+
+bool QPlainTextEdit::tabChangesFocus() const
+{
+ Q_D(const QPlainTextEdit);
+ return d->tabChangesFocus;
+}
+
+void QPlainTextEdit::setTabChangesFocus(bool b)
+{
+ Q_D(QPlainTextEdit);
+ d->tabChangesFocus = b;
+}
+
+/*!
+ \property QPlainTextEdit::documentTitle
+ \brief the title of the document parsed from the text.
+
+ By default, this property contains an empty string.
+*/
+
+/*!
+ \property QPlainTextEdit::lineWrapMode
+ \brief the line wrap mode
+
+ The default mode is 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 setWordWrapMode().
+*/
+
+QPlainTextEdit::LineWrapMode QPlainTextEdit::lineWrapMode() const
+{
+ Q_D(const QPlainTextEdit);
+ return d->lineWrap;
+}
+
+void QPlainTextEdit::setLineWrapMode(LineWrapMode wrap)
+{
+ Q_D(QPlainTextEdit);
+ if (d->lineWrap == wrap)
+ return;
+ d->lineWrap = wrap;
+ d->updateDefaultTextOption();
+ d->relayoutDocument();
+ d->_q_adjustScrollbars();
+ ensureCursorVisible();
+}
+
+/*!
+ \property QPlainTextEdit::wordWrapMode
+ \brief the mode QPlainTextEdit will use when wrapping text by words
+
+ By default, this property is set to QTextOption::WrapAtWordBoundaryOrAnywhere.
+
+ \sa QTextOption::WrapMode
+*/
+
+QTextOption::WrapMode QPlainTextEdit::wordWrapMode() const
+{
+ Q_D(const QPlainTextEdit);
+ return d->wordWrap;
+}
+
+void QPlainTextEdit::setWordWrapMode(QTextOption::WrapMode mode)
+{
+ Q_D(QPlainTextEdit);
+ if (mode == d->wordWrap)
+ return;
+ d->wordWrap = mode;
+ d->updateDefaultTextOption();
+}
+
+/*!
+ \property QPlainTextEdit::backgroundVisible
+ \brief whether the palette background is visible outside the document area
+
+ If set to true, the plain text edit paints the palette background
+ on the viewport area not covered by the text document. Otherwise,
+ if set to false, it won't. The feature makes it possible for
+ the user to visually distinguish between the area of the document,
+ painted with the base color of the palette, and the empty
+ area not covered by any document.
+
+ The default is false.
+*/
+
+bool QPlainTextEdit::backgroundVisible() const
+{
+ Q_D(const QPlainTextEdit);
+ return d->backgroundVisible;
+}
+
+void QPlainTextEdit::setBackgroundVisible(bool visible)
+{
+ Q_D(QPlainTextEdit);
+ if (visible == d->backgroundVisible)
+ return;
+ d->backgroundVisible = visible;
+ d->updateViewport();
+}
+
+/*!
+ \property QPlainTextEdit::centerOnScroll
+ \brief whether the cursor should be centered on screen
+
+ If set to true, the plain text edit scrolls the document
+ vertically to make the cursor visible at the center of the
+ viewport. This also allows the text edit to scroll below the end
+ of the document. Otherwise, if set to false, the plain text edit
+ scrolls the smallest amount possible to ensure the cursor is
+ visible. The same algorithm is applied to any new line appended
+ through appendPlainText().
+
+ The default is false.
+
+ \sa centerCursor(), ensureCursorVisible()
+*/
+
+bool QPlainTextEdit::centerOnScroll() const
+{
+ Q_D(const QPlainTextEdit);
+ return d->centerOnScroll;
+}
+
+void QPlainTextEdit::setCenterOnScroll(bool enabled)
+{
+ Q_D(QPlainTextEdit);
+ if (enabled == d->centerOnScroll)
+ return;
+ d->centerOnScroll = enabled;
+}
+
+
+
+/*!
+ Finds the next occurrence of the string, \a exp, using the given
+ \a options. Returns true if \a exp was found and changes the
+ cursor to select the match; otherwise returns false.
+*/
+bool QPlainTextEdit::find(const QString &exp, QTextDocument::FindFlags options)
+{
+ Q_D(QPlainTextEdit);
+ return d->control->find(exp, options);
+}
+
+/*!
+ \fn void QPlainTextEdit::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 QPlainTextEdit::selectionChanged()
+
+ This signal is emitted whenever the selection changes.
+
+ \sa copyAvailable()
+*/
+
+/*!
+ \fn void QPlainTextEdit::cursorPositionChanged()
+
+ This signal is emitted whenever the position of the
+ cursor changed.
+*/
+
+
+
+/*!
+ \fn void QPlainTextEdit::updateRequest(const QRect &rect, int dy)
+
+ This signal is emitted when the text document needs an update of
+ the specified \a rect. If the text is scrolled, \a rect will cover
+ the entire viewport area. If the text is scrolled vertically, \a
+ dy carries the amount of pixels the viewport was scrolled.
+
+ The purpose of the signal is to support extra widgets in plain
+ text edit subclasses that e.g. show line numbers, breakpoints, or
+ other extra information.
+*/
+
+/*! \fn void QPlainTextEdit::blockCountChanged(int newBlockCount);
+
+ This signal is emitted whenever the block count changes. The new
+ block count is passed in \a newBlockCount.
+*/
+
+/*! \fn void QPlainTextEdit::modificationChanged(bool changed);
+
+ This signal is emitted whenever the content of the document
+ changes in a way that affects the modification state. If \a
+ changed is true, the document has been modified; otherwise it is
+ false.
+
+ For example, calling setModified(false) on a document and then
+ inserting text causes the signal to get emitted. If you undo that
+ operation, causing the document to return to its original
+ unmodified state, the signal will get emitted again.
+*/
+
+
+
+
+void QPlainTextEditPrivate::append(const QString &text, Qt::TextFormat format)
+{
+ Q_Q(QPlainTextEdit);
+
+ QTextDocument *document = control->document();
+ QPlainTextDocumentLayout *documentLayout = qobject_cast<QPlainTextDocumentLayout*>(document->documentLayout());
+ Q_ASSERT(documentLayout);
+
+ int maximumBlockCount = document->maximumBlockCount();
+ if (maximumBlockCount)
+ document->setMaximumBlockCount(0);
+
+ const bool atBottom = q->isVisible()
+ && (control->blockBoundingRect(document->lastBlock()).bottom() - verticalOffset()
+ <= viewport->rect().bottom());
+
+ if (!q->isVisible())
+ showCursorOnInitialShow = true;
+
+ bool documentSizeChangedBlocked = documentLayout->priv()->blockDocumentSizeChanged;
+ documentLayout->priv()->blockDocumentSizeChanged = true;
+
+ if (format == Qt::RichText)
+ control->appendHtml(text);
+ else if (format == Qt::PlainText)
+ control->appendPlainText(text);
+ else
+ control->append(text);
+
+ if (maximumBlockCount > 0) {
+ if (document->blockCount() > maximumBlockCount) {
+ bool blockUpdate = false;
+ if (control->topBlock) {
+ control->topBlock--;
+ blockUpdate = true;
+ emit q->updateRequest(viewport->rect(), 0);
+ }
+
+ bool updatesBlocked = documentLayout->priv()->blockUpdate;
+ documentLayout->priv()->blockUpdate = blockUpdate;
+ QTextCursor cursor(document);
+ cursor.movePosition(QTextCursor::NextBlock, QTextCursor::KeepAnchor);
+ cursor.removeSelectedText();
+ documentLayout->priv()->blockUpdate = updatesBlocked;
+ }
+ document->setMaximumBlockCount(maximumBlockCount);
+ }
+
+ documentLayout->priv()->blockDocumentSizeChanged = documentSizeChangedBlocked;
+ _q_adjustScrollbars();
+
+
+ if (atBottom) {
+ const bool needScroll = !centerOnScroll
+ || control->blockBoundingRect(document->lastBlock()).bottom() - verticalOffset()
+ > viewport->rect().bottom();
+ if (needScroll)
+ vbar->setValue(vbar->maximum());
+ }
+}
+
+
+/*!
+ Appends a new paragraph with \a text to the end of the text edit.
+
+ \sa appendHtml()
+*/
+
+void QPlainTextEdit::appendPlainText(const QString &text)
+{
+ Q_D(QPlainTextEdit);
+ d->append(text, Qt::PlainText);
+}
+
+/*!
+ Appends a new paragraph with \a html to the end of the text edit.
+
+ appendPlainText()
+*/
+
+void QPlainTextEdit::appendHtml(const QString &html)
+{
+ Q_D(QPlainTextEdit);
+ d->append(html, Qt::RichText);
+}
+
+void QPlainTextEditPrivate::ensureCursorVisible(bool center)
+{
+ Q_Q(QPlainTextEdit);
+ QRect visible = viewport->rect();
+ QRect cr = q->cursorRect();
+ if (cr.top() < visible.top() || cr.bottom() > visible.bottom()) {
+ ensureVisible(control->textCursor().position(), center);
+ }
+
+ const bool rtl = q->isRightToLeft();
+ if (cr.left() < visible.left() || cr.right() > visible.right()) {
+ int x = cr.center().x() + horizontalOffset() - visible.width()/2;
+ hbar->setValue(rtl ? hbar->maximum() - x : x);
+ }
+}
+
+/*!
+ Ensures that the cursor is visible by scrolling the text edit if
+ necessary.
+
+ \sa centerCursor(), centerOnScroll
+*/
+void QPlainTextEdit::ensureCursorVisible()
+{
+ Q_D(QPlainTextEdit);
+ d->ensureCursorVisible(d->centerOnScroll);
+}
+
+
+/*! Scrolls the document in order to center the cursor vertically.
+
+\sa ensureCursorVisible(), centerOnScroll
+ */
+void QPlainTextEdit::centerCursor()
+{
+ Q_D(QPlainTextEdit);
+ d->ensureVisible(textCursor().position(), true, true);
+}
+
+/*!
+ Returns the first visible block.
+
+ \sa blockBoundingRect()
+ */
+QTextBlock QPlainTextEdit::firstVisibleBlock() const
+{
+ Q_D(const QPlainTextEdit);
+ return d->control->firstVisibleBlock();
+}
+
+/*! Returns the content's origin in viewport coordinates.
+
+ The origin of the content of a plain text edit is always the top
+ left corner of the first visible text block. The content offset
+ is different from (0,0) when the text has been scrolled
+ horizontally, or when the first visible block has been scrolled
+ partially off the screen, i.e. the visible text does not start
+ with the first line of the first visible block, or when the first
+ visible block is the very first block and the editor displays a
+ margin.
+
+ \sa firstVisibleBlock(), horizontalScrollBar(), verticalScrollBar()
+ */
+QPointF QPlainTextEdit::contentOffset() const
+{
+ Q_D(const QPlainTextEdit);
+ return QPointF(-d->horizontalOffset(), -d->verticalOffset());
+}
+
+
+/*! Returns the bounding rectangle of the text \a block in content
+ coordinates. Translate the rectangle with the contentOffset() to get
+ visual coordinates on the viewport.
+
+ \sa firstVisibleBlock(), blockBoundingRect()
+ */
+QRectF QPlainTextEdit::blockBoundingGeometry(const QTextBlock &block) const
+{
+ Q_D(const QPlainTextEdit);
+ return d->control->blockBoundingRect(block);
+}
+
+/*!
+ Returns the bounding rectangle of the text \a block in the block's own coordinates.
+
+ \sa blockBoundingGeometry()
+ */
+QRectF QPlainTextEdit::blockBoundingRect(const QTextBlock &block) const
+{
+ QPlainTextDocumentLayout *documentLayout = qobject_cast<QPlainTextDocumentLayout*>(document()->documentLayout());
+ Q_ASSERT(documentLayout);
+ return documentLayout->blockBoundingRect(block);
+}
+
+/*!
+ \property QPlainTextEdit::blockCount
+ \brief the number of text blocks in the document.
+
+ By default, in an empty document, this property contains a value of 1.
+*/
+int QPlainTextEdit::blockCount() const
+{
+ return document()->blockCount();
+}
+
+/*! Returns the paint context for the viewport(), useful only when
+ reimplementing paintEvent().
+ */
+QAbstractTextDocumentLayout::PaintContext QPlainTextEdit::getPaintContext() const
+{
+ Q_D(const QPlainTextEdit);
+ return d->control->getPaintContext(d->viewport);
+}
+
+/*!
+ \property QPlainTextEdit::maximumBlockCount
+ \brief the limit for blocks in the document.
+
+ Specifies the maximum number of blocks the document may have. If there are
+ more blocks in the document that specified with this property blocks are removed
+ from the beginning of the document.
+
+ A negative or zero value specifies that the document may contain an unlimited
+ amount of blocks.
+
+ The default value is 0.
+
+ Note that setting this property will apply the limit immediately to the document
+ contents. Setting this property also disables the undo redo history.
+
+*/
+
+
+/*!
+ \fn void QPlainTextEdit::textChanged()
+
+ This signal is emitted whenever the document's content changes; for
+ example, when text is inserted or deleted, or when formatting is applied.
+*/
+
+/*!
+ \fn void QPlainTextEdit::undoAvailable(bool available)
+
+ This signal is emitted whenever undo operations become available
+ (\a available is true) or unavailable (\a available is false).
+*/
+
+/*!
+ \fn void QPlainTextEdit::redoAvailable(bool available)
+
+ This signal is emitted whenever redo operations become available
+ (\a available is true) or unavailable (\a available is false).
+*/
+
+QT_END_NAMESPACE
+
+#include "moc_qplaintextedit.cpp"
+#include "moc_qplaintextedit_p.cpp"
+
+#endif // QT_NO_TEXTEDIT
diff --git a/src/gui/widgets/qplaintextedit.h b/src/gui/widgets/qplaintextedit.h
new file mode 100644
index 0000000000..c00ff4eb6d
--- /dev/null
+++ b/src/gui/widgets/qplaintextedit.h
@@ -0,0 +1,326 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QPLAINTEXTEDIT_H
+#define QPLAINTEXTEDIT_H
+
+#include <QtGui/qtextedit.h>
+
+#include <QtGui/qabstractscrollarea.h>
+#include <QtGui/qtextdocument.h>
+#include <QtGui/qtextoption.h>
+#include <QtGui/qtextcursor.h>
+#include <QtGui/qtextformat.h>
+#include <QtGui/qabstracttextdocumentlayout.h>
+
+#ifndef QT_NO_TEXTEDIT
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+class QStyleSheet;
+class QTextDocument;
+class QMenu;
+class QPlainTextEditPrivate;
+class QMimeData;
+
+
+class Q_GUI_EXPORT QPlainTextEdit : public QAbstractScrollArea
+{
+ Q_OBJECT
+ Q_DECLARE_PRIVATE(QPlainTextEdit)
+ Q_ENUMS(LineWrapMode)
+ Q_PROPERTY(bool tabChangesFocus READ tabChangesFocus WRITE setTabChangesFocus)
+ Q_PROPERTY(QString documentTitle READ documentTitle WRITE setDocumentTitle)
+ Q_PROPERTY(bool undoRedoEnabled READ isUndoRedoEnabled WRITE setUndoRedoEnabled)
+ Q_PROPERTY(LineWrapMode lineWrapMode READ lineWrapMode WRITE setLineWrapMode)
+ QDOC_PROPERTY(QTextOption::WrapMode wordWrapMode READ wordWrapMode WRITE setWordWrapMode)
+ Q_PROPERTY(bool readOnly READ isReadOnly WRITE setReadOnly)
+ Q_PROPERTY(QString plainText READ toPlainText WRITE setPlainText NOTIFY textChanged USER true)
+ Q_PROPERTY(bool overwriteMode READ overwriteMode WRITE setOverwriteMode)
+ Q_PROPERTY(int tabStopWidth READ tabStopWidth WRITE setTabStopWidth)
+ Q_PROPERTY(int cursorWidth READ cursorWidth WRITE setCursorWidth)
+ Q_PROPERTY(Qt::TextInteractionFlags textInteractionFlags READ textInteractionFlags WRITE setTextInteractionFlags)
+ Q_PROPERTY(int blockCount READ blockCount)
+ Q_PROPERTY(int maximumBlockCount READ maximumBlockCount WRITE setMaximumBlockCount)
+ Q_PROPERTY(bool backgroundVisible READ backgroundVisible WRITE setBackgroundVisible)
+ Q_PROPERTY(bool centerOnScroll READ centerOnScroll WRITE setCenterOnScroll)
+public:
+ enum LineWrapMode {
+ NoWrap,
+ WidgetWidth
+ };
+
+ explicit QPlainTextEdit(QWidget *parent = 0);
+ explicit QPlainTextEdit(const QString &text, QWidget *parent = 0);
+ virtual ~QPlainTextEdit();
+
+ void setDocument(QTextDocument *document);
+ QTextDocument *document() const;
+
+ void setTextCursor(const QTextCursor &cursor);
+ QTextCursor textCursor() const;
+
+ bool isReadOnly() const;
+ void setReadOnly(bool ro);
+
+ void setTextInteractionFlags(Qt::TextInteractionFlags flags);
+ Qt::TextInteractionFlags textInteractionFlags() const;
+
+ void mergeCurrentCharFormat(const QTextCharFormat &modifier);
+ void setCurrentCharFormat(const QTextCharFormat &format);
+ QTextCharFormat currentCharFormat() const;
+
+ bool tabChangesFocus() const;
+ void setTabChangesFocus(bool b);
+
+ inline void setDocumentTitle(const QString &title)
+ { document()->setMetaInformation(QTextDocument::DocumentTitle, title); }
+ inline QString documentTitle() const
+ { return document()->metaInformation(QTextDocument::DocumentTitle); }
+
+ inline bool isUndoRedoEnabled() const
+ { return document()->isUndoRedoEnabled(); }
+ inline void setUndoRedoEnabled(bool enable)
+ { document()->setUndoRedoEnabled(enable); }
+
+ inline void setMaximumBlockCount(int maximum)
+ { document()->setMaximumBlockCount(maximum); }
+ inline int maximumBlockCount() const
+ { return document()->maximumBlockCount(); }
+
+
+ LineWrapMode lineWrapMode() const;
+ void setLineWrapMode(LineWrapMode mode);
+
+ QTextOption::WrapMode wordWrapMode() const;
+ void setWordWrapMode(QTextOption::WrapMode policy);
+
+ void setBackgroundVisible(bool visible);
+ bool backgroundVisible() const;
+
+ void setCenterOnScroll(bool enabled);
+ bool centerOnScroll() const;
+
+ bool find(const QString &exp, QTextDocument::FindFlags options = 0);
+
+ inline QString toPlainText() const
+ { return document()->toPlainText(); }
+
+ void ensureCursorVisible();
+
+ virtual QVariant loadResource(int type, const QUrl &name);
+#ifndef QT_NO_CONTEXTMENU
+ QMenu *createStandardContextMenu();
+#endif
+
+ QTextCursor cursorForPosition(const QPoint &pos) const;
+ QRect cursorRect(const QTextCursor &cursor) const;
+ QRect cursorRect() const;
+
+ bool overwriteMode() const;
+ void setOverwriteMode(bool overwrite);
+
+ int tabStopWidth() const;
+ void setTabStopWidth(int width);
+
+ int cursorWidth() const;
+ void setCursorWidth(int width);
+
+ void setExtraSelections(const QList<QTextEdit::ExtraSelection> &selections);
+ QList<QTextEdit::ExtraSelection> extraSelections() const;
+
+ void moveCursor(QTextCursor::MoveOperation operation, QTextCursor::MoveMode mode = QTextCursor::MoveAnchor);
+
+ bool canPaste() const;
+
+#ifndef QT_NO_PRINTER
+ void print(QPrinter *printer) const;
+#endif
+
+ int blockCount() const;
+
+public Q_SLOTS:
+
+ void setPlainText(const QString &text);
+
+#ifndef QT_NO_CLIPBOARD
+ void cut();
+ void copy();
+ void paste();
+#endif
+
+ void undo();
+ void redo();
+
+ void clear();
+ void selectAll();
+
+ void insertPlainText(const QString &text);
+
+ void appendPlainText(const QString &text);
+ void appendHtml(const QString &html);
+
+ void centerCursor();
+
+Q_SIGNALS:
+ void textChanged();
+ void undoAvailable(bool b);
+ void redoAvailable(bool b);
+ void copyAvailable(bool b);
+ void selectionChanged();
+ void cursorPositionChanged();
+
+ void updateRequest(const QRect &rect, int dy);
+ void blockCountChanged(int newBlockCount);
+ void modificationChanged(bool);
+
+protected:
+ virtual bool event(QEvent *e);
+ virtual void timerEvent(QTimerEvent *e);
+ virtual void keyPressEvent(QKeyEvent *e);
+ virtual void keyReleaseEvent(QKeyEvent *e);
+ virtual void resizeEvent(QResizeEvent *e);
+ virtual void paintEvent(QPaintEvent *e);
+ virtual void mousePressEvent(QMouseEvent *e);
+ virtual void mouseMoveEvent(QMouseEvent *e);
+ virtual void mouseReleaseEvent(QMouseEvent *e);
+ virtual void mouseDoubleClickEvent(QMouseEvent *e);
+ virtual bool focusNextPrevChild(bool next);
+#ifndef QT_NO_CONTEXTMENU
+ virtual void contextMenuEvent(QContextMenuEvent *e);
+#endif
+#ifndef QT_NO_DRAGANDDROP
+ virtual void dragEnterEvent(QDragEnterEvent *e);
+ virtual void dragLeaveEvent(QDragLeaveEvent *e);
+ virtual void dragMoveEvent(QDragMoveEvent *e);
+ virtual void dropEvent(QDropEvent *e);
+#endif
+ virtual void focusInEvent(QFocusEvent *e);
+ virtual void focusOutEvent(QFocusEvent *e);
+ virtual void showEvent(QShowEvent *);
+ virtual void changeEvent(QEvent *e);
+#ifndef QT_NO_WHEELEVENT
+ virtual void wheelEvent(QWheelEvent *e);
+#endif
+
+ virtual QMimeData *createMimeDataFromSelection() const;
+ virtual bool canInsertFromMimeData(const QMimeData *source) const;
+ virtual void insertFromMimeData(const QMimeData *source);
+
+ virtual void inputMethodEvent(QInputMethodEvent *);
+ QVariant inputMethodQuery(Qt::InputMethodQuery property) const;
+
+ QPlainTextEdit(QPlainTextEditPrivate &dd, QWidget *parent);
+
+ virtual void scrollContentsBy(int dx, int dy);
+
+ QTextBlock firstVisibleBlock() const;
+ QPointF contentOffset() const;
+ QRectF blockBoundingRect(const QTextBlock &block) const;
+ QRectF blockBoundingGeometry(const QTextBlock &block) const;
+ QAbstractTextDocumentLayout::PaintContext getPaintContext() const;
+
+
+private:
+ Q_DISABLE_COPY(QPlainTextEdit)
+ Q_PRIVATE_SLOT(d_func(), void _q_repaintContents(const QRectF &r))
+ Q_PRIVATE_SLOT(d_func(), void _q_adjustScrollbars())
+ Q_PRIVATE_SLOT(d_func(), void _q_verticalScrollbarActionTriggered(int))
+ Q_PRIVATE_SLOT(d_func(), void _q_cursorPositionChanged())
+ friend class QPlainTextEditControl;
+};
+
+
+class QPlainTextDocumentLayoutPrivate;
+class Q_GUI_EXPORT QPlainTextDocumentLayout : public QAbstractTextDocumentLayout
+{
+ Q_OBJECT
+ Q_DECLARE_PRIVATE(QPlainTextDocumentLayout)
+ Q_PROPERTY(int cursorWidth READ cursorWidth WRITE setCursorWidth)
+
+public:
+ QPlainTextDocumentLayout(QTextDocument *document);
+ ~QPlainTextDocumentLayout();
+
+ void draw(QPainter *, const PaintContext &);
+ int hitTest(const QPointF &, Qt::HitTestAccuracy ) const;
+
+ int pageCount() const;
+ QSizeF documentSize() const;
+
+ QRectF frameBoundingRect(QTextFrame *) const;
+ QRectF blockBoundingRect(const QTextBlock &block) const;
+
+ void ensureBlockLayout(const QTextBlock &block) const;
+
+ void setCursorWidth(int width);
+ int cursorWidth() const;
+
+ void requestUpdate();
+
+protected:
+ void documentChanged(int from, int /*charsRemoved*/, int charsAdded);
+
+
+private:
+ void setTextWidth(qreal newWidth);
+ qreal textWidth() const;
+ void layoutBlock(const QTextBlock &block);
+ qreal blockWidth(const QTextBlock &block);
+
+ QPlainTextDocumentLayoutPrivate *priv() const;
+
+ friend class QPlainTextEdit;
+ friend class QPlainTextEditPrivate;
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+
+#endif // QT_NO_TEXTEDIT
+
+#endif // QPLAINTEXTEDIT_H
diff --git a/src/gui/widgets/qplaintextedit_p.h b/src/gui/widgets/qplaintextedit_p.h
new file mode 100644
index 0000000000..0739d537c7
--- /dev/null
+++ b/src/gui/widgets/qplaintextedit_p.h
@@ -0,0 +1,182 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QPLAINTEXTEDIT_P_H
+#define QPLAINTEXTEDIT_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/qabstractscrollarea_p.h"
+#include "QtGui/qtextdocumentfragment.h"
+#include "QtGui/qscrollbar.h"
+#include "QtGui/qtextcursor.h"
+#include "QtGui/qtextformat.h"
+#include "QtGui/qmenu.h"
+#include "QtGui/qabstracttextdocumentlayout.h"
+#include "QtCore/qbasictimer.h"
+#include "private/qtextcontrol_p.h"
+#include "qplaintextedit.h"
+
+#ifndef QT_NO_TEXTEDIT
+
+QT_BEGIN_NAMESPACE
+
+class QMimeData;
+
+class QPlainTextEdit;
+class ExtraArea;
+
+class QPlainTextEditControl : public QTextControl
+{
+ Q_OBJECT
+public:
+ QPlainTextEditControl(QPlainTextEdit *parent);
+
+
+ QMimeData *createMimeDataFromSelection() const;
+ bool canInsertFromMimeData(const QMimeData *source) const;
+ void insertFromMimeData(const QMimeData *source);
+ int hitTest(const QPointF &point, Qt::HitTestAccuracy = Qt::FuzzyHit) const;
+ QRectF blockBoundingRect(const QTextBlock &block) const;
+ inline QRectF cursorRect(const QTextCursor &cursor) const {
+ QRectF r = QTextControl::cursorRect(cursor);
+ r.setLeft(qMax(r.left(), (qreal) 0.));
+ return r;
+ }
+ inline QRectF cursorRect() { return cursorRect(textCursor()); }
+ void ensureCursorVisible() { textEdit->ensureCursorVisible(); }
+
+
+ QPlainTextEdit *textEdit;
+ int topBlock;
+ QTextBlock firstVisibleBlock() const;
+
+ QVariant loadResource(int type, const QUrl &name) {
+ return textEdit->loadResource(type, name);
+ }
+
+};
+
+
+class QPlainTextEditPrivate : public QAbstractScrollAreaPrivate
+{
+ Q_DECLARE_PUBLIC(QPlainTextEdit)
+public:
+ QPlainTextEditPrivate();
+
+ void init(const QString &txt = QString());
+ void _q_repaintContents(const QRectF &contentsRect);
+
+ inline QPoint mapToContents(const QPoint &point) const
+ { return QPoint(point.x() + horizontalOffset(), point.y() + verticalOffset()); }
+
+ void _q_adjustScrollbars();
+ void _q_verticalScrollbarActionTriggered(int action);
+ void ensureViewportLayouted();
+ void relayoutDocument();
+
+ void pageUpDown(QTextCursor::MoveOperation op, QTextCursor::MoveMode moveMode, bool moveCursor = true);
+
+ inline int horizontalOffset() const
+ { return (q_func()->isRightToLeft() ? (hbar->maximum() - hbar->value()) : hbar->value()); }
+ int verticalOffset(int topBlock, int topLine) const;
+ int verticalOffset() const;
+
+ inline void sendControlEvent(QEvent *e)
+ { control->processEvent(e, QPointF(horizontalOffset(), verticalOffset()), viewport); }
+
+ void updateDefaultTextOption();
+
+ QPlainTextEditControl *control;
+
+ bool tabChangesFocus;
+
+ QBasicTimer autoScrollTimer;
+ QPoint autoScrollDragPos;
+
+ QPlainTextEdit::LineWrapMode lineWrap;
+ QTextOption::WrapMode wordWrap;
+
+ uint showCursorOnInitialShow : 1;
+ uint backgroundVisible : 1;
+ uint centerOnScroll : 1;
+ uint inDrag : 1;
+
+ int topLine;
+
+ void setTopLine(int visualTopLine, int dx = 0);
+ void setTopBlock(int newTopBlock, int newTopLine, int dx = 0);
+
+ void ensureVisible(int position, bool center, bool forceCenter = false);
+ void ensureCursorVisible(bool center = false);
+ void updateViewport();
+
+ QPointer<QPlainTextDocumentLayout> documentLayoutPtr;
+
+ void append(const QString &text, Qt::TextFormat format = Qt::AutoText);
+
+ qreal pageUpDownLastCursorY;
+ bool pageUpDownLastCursorYIsValid;
+
+
+#ifdef QT_KEYPAD_NAVIGATION
+ QBasicTimer deleteAllTimer;
+#endif
+
+ void _q_cursorPositionChanged();
+
+ void _q_modificationChanged(bool);
+};
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_TEXTEDIT
+
+#endif // QPLAINTEXTEDIT_P_H
diff --git a/src/gui/widgets/qprintpreviewwidget.cpp b/src/gui/widgets/qprintpreviewwidget.cpp
new file mode 100644
index 0000000000..16334b8e75
--- /dev/null
+++ b/src/gui/widgets/qprintpreviewwidget.cpp
@@ -0,0 +1,829 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qprintpreviewwidget.h"
+#include <private/qprinter_p.h>
+
+#include <QtCore/qmath.h>
+#include <QtGui/qboxlayout.h>
+#include <QtGui/qgraphicsitem.h>
+#include <QtGui/qgraphicsview.h>
+#include <QtGui/qscrollbar.h>
+#include <QtGui/qstyleoption.h>
+
+#ifndef QT_NO_PRINTPREVIEWWIDGET
+
+QT_BEGIN_NAMESPACE
+
+namespace {
+class PageItem : public QGraphicsItem
+{
+public:
+ PageItem(int _pageNum, const QPicture* _pagePicture, QSize _paperSize, QRect _pageRect)
+ : pageNum(_pageNum), pagePicture(_pagePicture),
+ paperSize(_paperSize), pageRect(_pageRect)
+ {
+ qreal border = qMax(paperSize.height(), paperSize.width()) / 25;
+ brect = QRectF(QPointF(-border, -border),
+ QSizeF(paperSize)+QSizeF(2*border, 2*border));
+ setCacheMode(DeviceCoordinateCache);
+ }
+
+ inline QRectF boundingRect() const
+ { return brect; }
+
+ inline int pageNumber() const
+ { return pageNum; }
+
+ void paint(QPainter *painter, const QStyleOptionGraphicsItem *item, QWidget *widget);
+
+private:
+ int pageNum;
+ const QPicture* pagePicture;
+ QSize paperSize;
+ QRect pageRect;
+ QRectF brect;
+};
+
+void PageItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
+{
+ Q_UNUSED(widget);
+
+#if 0
+ // Draw item bounding rect, for debugging
+ painter->save();
+ painter->setPen(QPen(Qt::red, 0));
+ painter->setBrush(Qt::NoBrush);
+ painter->drawRect(QRectF(-border()+1.0, -border()+1.0, boundingRect().width()-2, boundingRect().height()-2));
+ painter->restore();
+#endif
+
+ QRectF paperRect(0,0, paperSize.width(), paperSize.height());
+
+ painter->setClipRect(paperRect & option->exposedRect);
+ painter->fillRect(paperRect, Qt::white);
+ if (!pagePicture)
+ return;
+ painter->drawPicture(pageRect.topLeft(), *pagePicture);
+
+ // Effect: make anything drawn in the margins look washed out.
+ QPainterPath path;
+ path.addRect(paperRect);
+ path.addRect(pageRect);
+ painter->setPen(QPen(Qt::NoPen));
+ painter->setBrush(QColor(255, 255, 255, 180));
+ painter->drawPath(path);
+
+ painter->setClipRect(option->exposedRect);
+#if 0
+ // Draw frame around paper.
+ painter->setPen(QPen(Qt::black, 0));
+ painter->setBrush(Qt::NoBrush);
+ painter->drawRect(paperRect);
+#endif
+
+ // Draw shadow
+ qreal shWidth = paperRect.width()/100;
+ QRectF rshadow(paperRect.topRight() + QPointF(0, shWidth),
+ paperRect.bottomRight() + QPointF(shWidth, 0));
+ QLinearGradient rgrad(rshadow.topLeft(), rshadow.topRight());
+ rgrad.setColorAt(0.0, QColor(0,0,0,255));
+ rgrad.setColorAt(1.0, QColor(0,0,0,0));
+ painter->fillRect(rshadow, QBrush(rgrad));
+ QRectF bshadow(paperRect.bottomLeft() + QPointF(shWidth, 0),
+ paperRect.bottomRight() + QPointF(0, shWidth));
+ QLinearGradient bgrad(bshadow.topLeft(), bshadow.bottomLeft());
+ bgrad.setColorAt(0.0, QColor(0,0,0,255));
+ bgrad.setColorAt(1.0, QColor(0,0,0,0));
+ painter->fillRect(bshadow, QBrush(bgrad));
+ QRectF cshadow(paperRect.bottomRight(),
+ paperRect.bottomRight() + QPointF(shWidth, shWidth));
+ QRadialGradient cgrad(cshadow.topLeft(), shWidth, cshadow.topLeft());
+ cgrad.setColorAt(0.0, QColor(0,0,0,255));
+ cgrad.setColorAt(1.0, QColor(0,0,0,0));
+ painter->fillRect(cshadow, QBrush(cgrad));
+
+ // todo: drawtext "Page N" below paper
+}
+
+class GraphicsView : public QGraphicsView
+{
+ Q_OBJECT
+public:
+ GraphicsView(QWidget* parent = 0)
+ : QGraphicsView(parent)
+ {}
+signals:
+ void resized();
+
+protected:
+ void resizeEvent(QResizeEvent* e)
+ {
+ QGraphicsView::resizeEvent(e);
+ emit resized();
+ }
+
+ void showEvent(QShowEvent* e)
+ {
+ QGraphicsView::showEvent(e);
+ emit resized();
+ }
+};
+
+} // anonymous namespace
+
+class QPrintPreviewWidgetPrivate
+{
+ Q_DECLARE_PUBLIC(QPrintPreviewWidget)
+public:
+ QPrintPreviewWidgetPrivate(QPrintPreviewWidget *q)
+ : q_ptr(q), scene(0), curPage(1),
+ viewMode(QPrintPreviewWidget::SinglePageView),
+ zoomMode(QPrintPreviewWidget::FitInView),
+ zoomFactor(1), initialized(false), fitting(true)
+ {}
+
+ // private slots
+ void _q_fit(bool doFitting = false);
+ void _q_updateCurrentPage();
+
+ void init();
+ void populateScene();
+ void layoutPages();
+ void generatePreview();
+ void setCurrentPage(int pageNumber);
+ void zoom(qreal zoom);
+ void setZoomFactor(qreal zoomFactor);
+ int calcCurrentPage();
+
+ QPrintPreviewWidget *q_ptr;
+ GraphicsView *graphicsView;
+ QGraphicsScene *scene;
+
+ int curPage;
+ QList<const QPicture *> pictures;
+ QList<QGraphicsItem *> pages;
+
+ QPrintPreviewWidget::ViewMode viewMode;
+ QPrintPreviewWidget::ZoomMode zoomMode;
+ qreal zoomFactor;
+ bool ownPrinter;
+ QPrinter* printer;
+ bool initialized;
+ bool fitting;
+};
+
+void QPrintPreviewWidgetPrivate::_q_fit(bool doFitting)
+{
+ Q_Q(QPrintPreviewWidget);
+
+ if (curPage < 1 || curPage > pages.count())
+ return;
+
+ if (!doFitting && !fitting)
+ return;
+
+ if (doFitting && fitting) {
+ QRect viewRect = graphicsView->viewport()->rect();
+ if (zoomMode == QPrintPreviewWidget::FitInView) {
+ QList<QGraphicsItem*> containedItems = graphicsView->items(viewRect, Qt::ContainsItemBoundingRect);
+ foreach(QGraphicsItem* item, containedItems) {
+ PageItem* pg = static_cast<PageItem*>(item);
+ if (pg->pageNumber() == curPage)
+ return;
+ }
+ }
+
+ int newPage = calcCurrentPage();
+ if (newPage != curPage)
+ curPage = newPage;
+ }
+
+ QRectF target = pages.at(curPage-1)->sceneBoundingRect();
+ if (viewMode == QPrintPreviewWidget::FacingPagesView) {
+ // fit two pages
+ if (curPage % 2)
+ target.setLeft(target.left() - target.width());
+ else
+ target.setRight(target.right() + target.width());
+ } else if (viewMode == QPrintPreviewWidget::AllPagesView) {
+ target = scene->itemsBoundingRect();
+ }
+
+ if (zoomMode == QPrintPreviewWidget::FitToWidth) {
+ QTransform t;
+ qreal scale = graphicsView->viewport()->width() / target.width();
+ t.scale(scale, scale);
+ graphicsView->setTransform(t);
+ if (doFitting && fitting) {
+ QRectF viewSceneRect = graphicsView->viewportTransform().mapRect(graphicsView->viewport()->rect());
+ viewSceneRect.moveTop(target.top());
+ graphicsView->ensureVisible(viewSceneRect); // Nah...
+ }
+ } else {
+ graphicsView->fitInView(target, Qt::KeepAspectRatio);
+ if (zoomMode == QPrintPreviewWidget::FitInView) {
+ int step = qRound(graphicsView->matrix().mapRect(target).height());
+ graphicsView->verticalScrollBar()->setSingleStep(step);
+ graphicsView->verticalScrollBar()->setPageStep(step);
+ }
+ }
+
+ zoomFactor = graphicsView->transform().m11() * (float(printer->logicalDpiY()) / q->logicalDpiY());
+ emit q->previewChanged();
+}
+
+void QPrintPreviewWidgetPrivate::_q_updateCurrentPage()
+{
+ Q_Q(QPrintPreviewWidget);
+
+ if (viewMode == QPrintPreviewWidget::AllPagesView)
+ return;
+
+ int newPage = calcCurrentPage();
+ if (newPage != curPage) {
+ curPage = newPage;
+ emit q->previewChanged();
+ }
+}
+
+int QPrintPreviewWidgetPrivate::calcCurrentPage()
+{
+ int maxArea = 0;
+ int newPage = curPage;
+ QRect viewRect = graphicsView->viewport()->rect();
+ QList<QGraphicsItem*> items = graphicsView->items(viewRect);
+ for (int i=0; i<items.size(); ++i) {
+ PageItem* pg = static_cast<PageItem*>(items.at(i));
+ QRect overlap = graphicsView->mapFromScene(pg->sceneBoundingRect()).boundingRect() & viewRect;
+ int area = overlap.width() * overlap.height();
+ if (area > maxArea) {
+ maxArea = area;
+ newPage = pg->pageNumber();
+ } else if (area == maxArea && pg->pageNumber() < newPage) {
+ newPage = pg->pageNumber();
+ }
+ }
+ return newPage;
+}
+
+void QPrintPreviewWidgetPrivate::init()
+{
+ Q_Q(QPrintPreviewWidget);
+
+ graphicsView = new GraphicsView;
+ graphicsView->setInteractive(false);
+ graphicsView->setDragMode(QGraphicsView::ScrollHandDrag);
+ graphicsView->setViewportUpdateMode(QGraphicsView::SmartViewportUpdate);
+ QObject::connect(graphicsView->verticalScrollBar(), SIGNAL(valueChanged(int)),
+ q, SLOT(_q_updateCurrentPage()));
+ QObject::connect(graphicsView, SIGNAL(resized()), q, SLOT(_q_fit()));
+
+ scene = new QGraphicsScene(graphicsView);
+ scene->setBackgroundBrush(Qt::gray);
+ graphicsView->setScene(scene);
+
+ QVBoxLayout *layout = new QVBoxLayout;
+ q->setLayout(layout);
+ layout->setContentsMargins(0, 0, 0, 0);
+ layout->addWidget(graphicsView);
+}
+
+void QPrintPreviewWidgetPrivate::populateScene()
+{
+ // remove old pages
+ for (int i = 0; i < pages.size(); i++)
+ scene->removeItem(pages.at(i));
+ qDeleteAll(pages);
+ pages.clear();
+
+ int numPages = pictures.count();
+ QSize paperSize = printer->paperRect().size();
+ QRect pageRect = printer->pageRect();
+
+ for (int i = 0; i < numPages; i++) {
+ PageItem* item = new PageItem(i+1, pictures.at(i), paperSize, pageRect);
+ scene->addItem(item);
+ pages.append(item);
+ }
+}
+
+void QPrintPreviewWidgetPrivate::layoutPages()
+{
+ int numPages = pages.count();
+ if (numPages < 1)
+ return;
+
+ int numPagePlaces = numPages;
+ int cols = 1; // singleMode and default
+ if (viewMode == QPrintPreviewWidget::AllPagesView) {
+ if (printer->orientation() == QPrinter::Portrait)
+ cols = qCeil(qSqrt((float) numPages));
+ else
+ cols = qFloor(qSqrt((float) numPages));
+ cols += cols % 2; // Nicer with an even number of cols
+ }
+ else if (viewMode == QPrintPreviewWidget::FacingPagesView) {
+ cols = 2;
+ numPagePlaces += 1;
+ }
+ int rows = qCeil(qreal(numPagePlaces) / cols);
+
+ qreal itemWidth = pages.at(0)->boundingRect().width();
+ qreal itemHeight = pages.at(0)->boundingRect().height();
+ int pageNum = 1;
+ for (int i = 0; i < rows && pageNum <= numPages; i++) {
+ for (int j = 0; j < cols && pageNum <= numPages; j++) {
+ if (!i && !j && viewMode == QPrintPreviewWidget::FacingPagesView) {
+ // Front page doesn't have a facing page
+ continue;
+ } else {
+ pages.at(pageNum-1)->setPos(QPointF(j*itemWidth, i*itemHeight));
+ pageNum++;
+ }
+ }
+ }
+ scene->setSceneRect(scene->itemsBoundingRect());
+}
+
+void QPrintPreviewWidgetPrivate::generatePreview()
+{
+ //### If QPrinter::setPreviewMode() becomes public, handle the
+ //### case that we have been constructed with a printer that
+ //### _already_ has been preview-painted to, so we should
+ //### initially just show the pages it already contains, and not
+ //### emit paintRequested() until the user changes some parameter
+
+ Q_Q(QPrintPreviewWidget);
+ printer->d_func()->setPreviewMode(true);
+ emit q->paintRequested(printer);
+ printer->d_func()->setPreviewMode(false);
+ pictures = printer->d_func()->previewPages();
+ populateScene(); // i.e. setPreviewPrintedPictures() e.l.
+ layoutPages();
+ curPage = qBound(1, curPage, pages.count());
+ if (fitting)
+ _q_fit();
+ emit q->previewChanged();
+}
+
+void QPrintPreviewWidgetPrivate::setCurrentPage(int pageNumber)
+{
+ if (pageNumber < 1 || pageNumber > pages.count())
+ return;
+
+ int lastPage = curPage;
+ curPage = pageNumber;
+
+ if (lastPage != curPage && lastPage > 0 && lastPage <= pages.count()) {
+ if (zoomMode != QPrintPreviewWidget::FitInView) {
+ QScrollBar *hsc = graphicsView->horizontalScrollBar();
+ QScrollBar *vsc = graphicsView->verticalScrollBar();
+ QPointF pt = graphicsView->transform().map(pages.at(curPage-1)->pos());
+ vsc->setValue(int(pt.y()) - 10);
+ hsc->setValue(int(pt.x()) - 10);
+ } else {
+ graphicsView->centerOn(pages.at(curPage-1));
+ }
+ }
+}
+
+void QPrintPreviewWidgetPrivate::zoom(qreal zoom)
+{
+ zoomFactor *= zoom;
+ graphicsView->scale(zoom, zoom);
+}
+
+void QPrintPreviewWidgetPrivate::setZoomFactor(qreal _zoomFactor)
+{
+ Q_Q(QPrintPreviewWidget);
+ zoomFactor = _zoomFactor;
+ graphicsView->resetTransform();
+ int dpi_y = q->logicalDpiY();
+ int printer_dpi_y = printer->logicalDpiY();
+ graphicsView->scale(zoomFactor*(dpi_y/float(printer_dpi_y)),
+ zoomFactor*(dpi_y/float(printer_dpi_y)));
+}
+
+///////////////////////////////////////
+
+/*!
+ \class QPrintPreviewWidget
+ \since 4.4
+
+ \brief The QPrintPreviewWidget class provides a widget for
+ previewing page layouts for printer output.
+
+ \ingroup multimedia
+
+ QPrintPreviewDialog uses a QPrintPreviewWidget internally, and the
+ purpose of QPrintPreviewWidget is to make it possible to embed the
+ preview into other widgets. It also makes it possible to build a different
+ user interface around it than the default one provided with QPrintPreviewDialog.
+
+ Using QPrintPreviewWidget is straightforward:
+
+ \list 1
+ \o Create the QPrintPreviewWidget
+
+ Construct the QPrintPreviewWidget either by passing in an
+ exisiting QPrinter object, or have QPrintPreviewWidget create a
+ default constructed QPrinter object for you.
+
+ \o Connect the paintRequested() signal to a slot.
+
+ When the widget needs to generate a set of preview pages, a
+ paintRequested() signal will be emitted from the widget. Connect a
+ slot to this signal, and draw onto the QPrinter passed in as a
+ signal parameter. Call QPrinter::newPage(), to start a new
+ page in the preview.
+
+ \endlist
+
+ \sa QPrinter, QPrintDialog, QPageSetupDialog, QPrintPreviewDialog
+*/
+
+
+/*!
+ \enum QPrintPreviewWidget::ViewMode
+
+ This enum is used to describe the view mode of the preview widget.
+
+ \value SinglePageView A mode where single pages in the preview
+ is viewed.
+
+ \value FacingPagesView A mode where the facing pages in the preview
+ is viewed.
+
+ \value AllPagesView A view mode where all the pages in the preview
+ is viewed.
+*/
+
+/*!
+ \enum QPrintPreviewWidget::ZoomMode
+
+ This enum is used to describe zoom mode of the preview widget.
+
+ \value CustomZoom The zoom is set to a custom zoom value.
+
+ \value FitToWidth This mode fits the current page to the width of the view.
+
+ \value FitInView This mode fits the current page inside the view.
+
+*/
+
+/*!
+ Constructs a QPrintPreviewWidget based on \a printer and with \a
+ parent as the parent widget. The widget flags \a flags are passed on
+ to the QWidget constructor.
+
+ \sa QWidget::setWindowFlags()
+*/
+QPrintPreviewWidget::QPrintPreviewWidget(QPrinter *printer, QWidget *parent, Qt::WindowFlags flags)
+ : QWidget(parent, flags), d_ptr(new QPrintPreviewWidgetPrivate(this))
+{
+ Q_D(QPrintPreviewWidget);
+ d->printer = printer;
+ d->ownPrinter = false;
+ d->init();
+}
+
+/*!
+ \overload
+
+ This will cause QPrintPreviewWidget to create an internal, default
+ constructed QPrinter object, which will be used to generate the
+ preview.
+*/
+QPrintPreviewWidget::QPrintPreviewWidget(QWidget *parent, Qt::WindowFlags flags)
+ : QWidget(parent, flags), d_ptr(new QPrintPreviewWidgetPrivate(this))
+{
+ Q_D(QPrintPreviewWidget);
+ d->printer = new QPrinter;
+ d->ownPrinter = true;
+ d->init();
+}
+
+
+/*!
+ Destroys the QPrintPreviewWidget.
+*/
+QPrintPreviewWidget::~QPrintPreviewWidget()
+{
+ Q_D(QPrintPreviewWidget);
+ if (d->ownPrinter)
+ delete d->printer;
+ delete d_ptr;
+}
+
+/*!
+ Returns the current view mode. The default view mode is SinglePageView.
+*/
+QPrintPreviewWidget::ViewMode QPrintPreviewWidget::viewMode() const
+{
+ Q_D(const QPrintPreviewWidget);
+ return d->viewMode;
+}
+
+/*!
+ Sets the view mode to \a mode. The default view mode is
+ SinglePageView.
+*/
+void QPrintPreviewWidget::setViewMode(ViewMode mode)
+{
+ Q_D(QPrintPreviewWidget);
+ d->viewMode = mode;
+ d->layoutPages();
+ if (d->viewMode == AllPagesView) {
+ d->graphicsView->fitInView(d->scene->itemsBoundingRect(), Qt::KeepAspectRatio);
+ d->fitting = false;
+ d->zoomMode = QPrintPreviewWidget::CustomZoom;
+ d->zoomFactor = d->graphicsView->transform().m11() * (float(d->printer->logicalDpiY()) / logicalDpiY());
+ emit previewChanged();
+ } else {
+ d->fitting = true;
+ d->_q_fit();
+ }
+}
+
+/*!
+ Returns the current orientation of the preview. This value is
+ obtained from the QPrinter object associated with the preview.
+*/
+QPrinter::Orientation QPrintPreviewWidget::orientation() const
+{
+ Q_D(const QPrintPreviewWidget);
+ return d->printer->orientation();
+}
+
+/*!
+ Sets the current orientation to \a orientation. This value will be
+ set on the QPrinter object associated with the preview.
+*/
+void QPrintPreviewWidget::setOrientation(QPrinter::Orientation orientation)
+{
+ Q_D(QPrintPreviewWidget);
+ d->printer->setOrientation(orientation);
+ d->generatePreview();
+}
+
+/*!
+ Prints the preview to the printer associated with the preview.
+*/
+void QPrintPreviewWidget::print()
+{
+ Q_D(QPrintPreviewWidget);
+ // ### make use of the generated pages
+ emit paintRequested(d->printer);
+}
+
+/*!
+ Zooms the current view in by \a factor. The default value for \a
+ factor is 1.1, which means the view will be scaled up by 10%.
+*/
+void QPrintPreviewWidget::zoomIn(qreal factor)
+{
+ Q_D(QPrintPreviewWidget);
+ d->fitting = false;
+ d->zoomMode = QPrintPreviewWidget::CustomZoom;
+ d->zoom(factor);
+}
+
+/*!
+ Zooms the current view out by \a factor. The default value for \a
+ factor is 1.1, which means the view will be scaled down by 10%.
+*/
+void QPrintPreviewWidget::zoomOut(qreal factor)
+{
+ Q_D(QPrintPreviewWidget);
+ d->fitting = false;
+ d->zoomMode = QPrintPreviewWidget::CustomZoom;
+ d->zoom(1/factor);
+}
+
+/*!
+ Returns the zoom factor of the view.
+*/
+qreal QPrintPreviewWidget::zoomFactor() const
+{
+ Q_D(const QPrintPreviewWidget);
+ return d->zoomFactor;
+}
+
+/*!
+ Sets the zoom factor of the view to \a factor. For example, a
+ value of 1.0 indicates an unscaled view, which is approximately
+ the size the view will have on paper. A value of 0.5 will halve
+ the size of the view, while a value of 2.0 will double the size of
+ the view.
+*/
+void QPrintPreviewWidget::setZoomFactor(qreal factor)
+{
+ Q_D(QPrintPreviewWidget);
+ d->fitting = false;
+ d->zoomMode = QPrintPreviewWidget::CustomZoom;
+ d->setZoomFactor(factor);
+}
+
+/*!
+ Returns the number of pages in the preview.
+*/
+int QPrintPreviewWidget::numPages() const
+{
+ Q_D(const QPrintPreviewWidget);
+ return d->pages.size();
+}
+
+/*!
+ Returns the currently viewed page in the preview.
+*/
+int QPrintPreviewWidget::currentPage() const
+{
+ Q_D(const QPrintPreviewWidget);
+ return d->curPage;
+}
+
+/*!
+ Sets the current page in the preview. This will cause the view to
+ skip to the beginning of \a page.
+*/
+void QPrintPreviewWidget::setCurrentPage(int page)
+{
+ Q_D(QPrintPreviewWidget);
+ d->setCurrentPage(page);
+}
+
+/*!
+ This is a convenience function and is the same as calling \c
+ {setZoomMode(QPrintPreviewWidget::FitToWidth)}.
+*/
+void QPrintPreviewWidget::fitToWidth()
+{
+ setZoomMode(FitToWidth);
+}
+
+/*!
+ This is a convenience function and is the same as calling \c
+ {setZoomMode(QPrintPreviewWidget::FitInView)}.
+*/
+void QPrintPreviewWidget::fitInView()
+{
+ setZoomMode(FitInView);
+}
+
+/*!
+ Sets the zoom mode to \a zoomMode. The default zoom mode is FitInView.
+
+ \sa zoomMode(), viewMode(), setViewMode()
+*/
+void QPrintPreviewWidget::setZoomMode(QPrintPreviewWidget::ZoomMode zoomMode)
+{
+ Q_D(QPrintPreviewWidget);
+ d->zoomMode = zoomMode;
+ if (d->zoomMode == FitInView || d->zoomMode == FitToWidth) {
+ d->fitting = true;
+ d->_q_fit(true);
+ } else {
+ d->fitting = false;
+ }
+}
+
+/*!
+ Returns the current zoom mode.
+
+ \sa setZoomMode(), viewMode(), setViewMode()
+*/
+QPrintPreviewWidget::ZoomMode QPrintPreviewWidget::zoomMode() const
+{
+ Q_D(const QPrintPreviewWidget);
+ return d->zoomMode;
+}
+
+/*!
+ This is a convenience function and is the same as calling \c
+ {setOrientation(QPrinter::Landscape)}.
+*/
+void QPrintPreviewWidget::setLandscapeOrientation()
+{
+ setOrientation(QPrinter::Landscape);
+}
+
+/*!
+ This is a convenience function and is the same as calling \c
+ {setOrientation(QPrinter::Portrait)}.
+*/
+void QPrintPreviewWidget::setPortraitOrientation()
+{
+ setOrientation(QPrinter::Portrait);
+}
+
+/*!
+ This is a convenience function and is the same as calling \c
+ {setViewMode(QPrintPreviewWidget::SinglePageView)}.
+*/
+void QPrintPreviewWidget::setSinglePageViewMode()
+{
+ setViewMode(SinglePageView);
+}
+
+/*!
+ This is a convenience function and is the same as calling \c
+ {setViewMode(QPrintPreviewWidget::FacingPagesView)}.
+*/
+void QPrintPreviewWidget::setFacingPagesViewMode()
+{
+ setViewMode(FacingPagesView);
+}
+
+/*!
+ This is a convenience function and is the same as calling \c
+ {setViewMode(QPrintPreviewWidget::AllPagesView)}.
+*/
+void QPrintPreviewWidget::setAllPagesViewMode()
+{
+ setViewMode(AllPagesView);
+}
+
+
+/*!
+ This function updates the preview, which causes the
+ paintRequested() signal to be emitted.
+*/
+void QPrintPreviewWidget::updatePreview()
+{
+ Q_D(QPrintPreviewWidget);
+ d->initialized = true;
+ d->generatePreview();
+ d->graphicsView->updateGeometry();
+}
+
+/*! \reimp
+*/
+void QPrintPreviewWidget::setVisible(bool visible)
+{
+ Q_D(QPrintPreviewWidget);
+ if (visible && !d->initialized)
+ updatePreview();
+ QWidget::setVisible(visible);
+}
+
+/*!
+ \fn void QPrintPreviewWidget::paintRequested(QPrinter *printer)
+
+ This signal is emitted when the preview widget needs to generate a
+ set of preview pages. \a printer is the printer associated with
+ this preview widget.
+*/
+
+/*!
+ \fn void QPrintPreviewWidget::previewChanged()
+
+ This signal is emitted whenever the preview widget has changed
+ some internal state, such as the orientation.
+*/
+
+
+QT_END_NAMESPACE
+
+#include "moc_qprintpreviewwidget.cpp"
+#include "qprintpreviewwidget.moc"
+
+#endif // QT_NO_PRINTPREVIEWWIDGET
diff --git a/src/gui/widgets/qprintpreviewwidget.h b/src/gui/widgets/qprintpreviewwidget.h
new file mode 100644
index 0000000000..27110a47a1
--- /dev/null
+++ b/src/gui/widgets/qprintpreviewwidget.h
@@ -0,0 +1,124 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QPRINTPREVIEWWIDGET_H
+#define QPRINTPREVIEWWIDGET_H
+
+#include <QtGui/qwidget.h>
+#include <QtGui/qprinter.h>
+
+#ifndef QT_NO_PRINTPREVIEWWIDGET
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+class QPrintPreviewWidgetPrivate;
+
+class Q_GUI_EXPORT QPrintPreviewWidget : public QWidget
+{
+ Q_OBJECT
+ Q_DECLARE_PRIVATE(QPrintPreviewWidget)
+public:
+
+ enum ViewMode {
+ SinglePageView,
+ FacingPagesView,
+ AllPagesView
+ };
+
+ enum ZoomMode {
+ CustomZoom,
+ FitToWidth,
+ FitInView
+ };
+
+ explicit QPrintPreviewWidget(QPrinter *printer, QWidget *parent = 0, Qt::WindowFlags flags = 0);
+ explicit QPrintPreviewWidget(QWidget *parent = 0, Qt::WindowFlags flags = 0);
+ ~QPrintPreviewWidget();
+
+ qreal zoomFactor() const;
+ QPrinter::Orientation orientation() const;
+ ViewMode viewMode() const;
+ ZoomMode zoomMode() const;
+ int currentPage() const;
+ int numPages() const;
+ void setVisible(bool visible);
+
+public Q_SLOTS:
+ void print();
+
+ void zoomIn(qreal zoom = 1.1);
+ void zoomOut(qreal zoom = 1.1);
+ void setZoomFactor(qreal zoomFactor);
+ void setOrientation(QPrinter::Orientation orientation);
+ void setViewMode(ViewMode viewMode);
+ void setZoomMode(ZoomMode zoomMode);
+ void setCurrentPage(int pageNumber);
+
+ void fitToWidth();
+ void fitInView();
+ void setLandscapeOrientation();
+ void setPortraitOrientation();
+ void setSinglePageViewMode();
+ void setFacingPagesViewMode();
+ void setAllPagesViewMode();
+
+ void updatePreview();
+
+Q_SIGNALS:
+ void paintRequested(QPrinter *printer);
+ void previewChanged();
+
+private:
+ QPrintPreviewWidgetPrivate *d_ptr;
+ Q_PRIVATE_SLOT(d_func(), void _q_fit())
+ Q_PRIVATE_SLOT(d_func(), void _q_updateCurrentPage())
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QT_NO_PRINTPREVIEWWIDGET
+#endif // QPRINTPREVIEWWIDGET_H
diff --git a/src/gui/widgets/qprogressbar.cpp b/src/gui/widgets/qprogressbar.cpp
new file mode 100644
index 0000000000..cdb3836d3c
--- /dev/null
+++ b/src/gui/widgets/qprogressbar.cpp
@@ -0,0 +1,592 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qprogressbar.h"
+#ifndef QT_NO_PROGRESSBAR
+#include <qevent.h>
+#include <qpainter.h>
+#include <qstylepainter.h>
+#include <qstyleoption.h>
+#include <private/qwidget_p.h>
+#ifndef QT_NO_ACCESSIBILITY
+#include <qaccessible.h>
+#endif
+#include <limits.h>
+
+QT_BEGIN_NAMESPACE
+
+class QProgressBarPrivate : public QWidgetPrivate
+{
+ Q_DECLARE_PUBLIC(QProgressBar)
+
+public:
+ QProgressBarPrivate();
+
+ void init();
+ inline void resetLayoutItemMargins();
+
+ int minimum;
+ int maximum;
+ int value;
+ Qt::Alignment alignment;
+ uint textVisible : 1;
+ int lastPaintedValue;
+ Qt::Orientation orientation;
+ bool invertedAppearance;
+ QProgressBar::Direction textDirection;
+ QString format;
+ inline int bound(int val) const { return qMax(minimum-1, qMin(maximum, val)); }
+ bool repaintRequired() const;
+};
+
+QProgressBarPrivate::QProgressBarPrivate()
+ : minimum(0), maximum(100), value(-1), alignment(Qt::AlignLeft), textVisible(true),
+ lastPaintedValue(-1), orientation(Qt::Horizontal), invertedAppearance(false),
+ textDirection(QProgressBar::TopToBottom), format(QLatin1String("%p%"))
+{
+}
+
+void QProgressBarPrivate::init()
+{
+ Q_Q(QProgressBar);
+ QSizePolicy sp(QSizePolicy::Expanding, QSizePolicy::Fixed);
+ if (orientation == Qt::Vertical)
+ sp.transpose();
+ q->setSizePolicy(sp);
+ q->setAttribute(Qt::WA_WState_OwnSizePolicy, false);
+ resetLayoutItemMargins();
+}
+
+void QProgressBarPrivate::resetLayoutItemMargins()
+{
+ Q_Q(QProgressBar);
+ QStyleOptionProgressBar option;
+ q->initStyleOption(&option);
+ setLayoutItemMargins(QStyle::SE_ProgressBarLayoutItem, &option);
+}
+
+/*!
+ Initialize \a option with the values from this QProgressBar. This method is useful
+ for subclasses when they need a QStyleOptionProgressBar or QStyleOptionProgressBarV2,
+ but don't want to fill in all the information themselves. This function will check the version
+ of the QStyleOptionProgressBar and fill in the additional values for a
+ QStyleOptionProgressBarV2.
+
+ \sa QStyleOption::initFrom()
+*/
+void QProgressBar::initStyleOption(QStyleOptionProgressBar *option) const
+{
+ if (!option)
+ return;
+ Q_D(const QProgressBar);
+ option->initFrom(this);
+
+ if (d->orientation == Qt::Horizontal)
+ option->state |= QStyle::State_Horizontal;
+ option->minimum = d->minimum;
+ option->maximum = d->maximum;
+ option->progress = d->value;
+ option->textAlignment = d->alignment;
+ option->textVisible = d->textVisible;
+ option->text = text();
+
+ if (QStyleOptionProgressBarV2 *optionV2
+ = qstyleoption_cast<QStyleOptionProgressBarV2 *>(option)) {
+ optionV2->orientation = d->orientation; // ### Qt 5: use State_Horizontal instead
+ optionV2->invertedAppearance = d->invertedAppearance;
+ optionV2->bottomToTop = (d->textDirection == QProgressBar::BottomToTop);
+ }
+}
+
+bool QProgressBarPrivate::repaintRequired() const
+{
+ Q_Q(const QProgressBar);
+ if (value == lastPaintedValue)
+ return false;
+
+ int valueDifference = qAbs(value - lastPaintedValue);
+
+ // Check if the text needs to be repainted
+ if (value == minimum || value == maximum)
+ return true;
+ if (textVisible) {
+ if ((format.contains(QLatin1String("%v"))))
+ return true;
+ if ((format.contains(QLatin1String("%p"))
+ && valueDifference >= qAbs((maximum - minimum) / 100)))
+ return true;
+ }
+
+ // Check if the bar needs to be repainted
+ QStyleOptionProgressBarV2 opt;
+ q->initStyleOption(&opt);
+ int cw = q->style()->pixelMetric(QStyle::PM_ProgressBarChunkWidth, &opt, q);
+ QRect groove = q->style()->subElementRect(QStyle::SE_ProgressBarGroove, &opt, q);
+ // This expression is basically
+ // (valueDifference / (maximum - minimum) > cw / groove.width())
+ // transformed to avoid integer division.
+ int grooveBlock = (q->orientation() == Qt::Horizontal) ? groove.width() : groove.height();
+ return (valueDifference * grooveBlock > cw * (maximum - minimum));
+}
+
+/*!
+ \class QProgressBar
+ \brief The QProgressBar widget provides a horizontal or vertical progress bar.
+
+ \ingroup basicwidgets
+ \mainclass
+
+ 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 set it up by
+ specifying the minimum and maximum possible step values, and it
+ will display the percentage of steps that have been completed
+ when you later give it the current step value. The percentage is
+ calculated by dividing the progress (value() - minimum()) divided
+ by maximum() - minimum().
+
+ You can specify the minimum and maximum number of steps with
+ setMinimum() and setMaximum. The current number of steps is set
+ with setValue(). The progress bar can be rewound to the
+ beginning with reset().
+
+ If minimum and maximum both are set to 0, the 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.
+
+ \table
+ \row \o \inlineimage macintosh-progressbar.png Screenshot of a Macintosh style progress bar
+ \o A progress bar shown in the Macintosh widget style.
+ \row \o \inlineimage windowsxp-progressbar.png Screenshot of a Windows XP style progress bar
+ \o A progress bar shown in the Windows XP widget style.
+ \row \o \inlineimage plastique-progressbar.png Screenshot of a Plastique style progress bar
+ \o A progress bar shown in the Plastique widget style.
+ \endtable
+
+ \sa QTimeLine, QProgressDialog, {fowler}{GUI Design Handbook: Progress Indicator}
+*/
+
+/*!
+ \since 4.1
+ \enum QProgressBar::Direction
+ \brief Specifies the reading direction of the \l text for vertical progress bars.
+
+ \value TopToBottom The text is rotated 90 degrees clockwise.
+ \value BottomToTop The text is rotated 90 degrees counter-clockwise.
+
+ Note that whether or not the text is drawn is dependent on the style.
+ Currently CDE, CleanLooks, Motif, and Plastique draw the text. Mac, Windows
+ and WindowsXP style do not.
+
+ \sa textDirection
+*/
+
+/*!
+ \fn void QProgressBar::valueChanged(int value)
+
+ This signal is emitted when the value shown in the progress bar changes.
+ \a value is the new value shown by the progress bar.
+*/
+
+/*!
+ Constructs a progress bar with the given \a parent.
+
+ By default, the minimum step value is set to 0, and the maximum to 100.
+
+ \sa setRange()
+*/
+
+QProgressBar::QProgressBar(QWidget *parent)
+ : QWidget(*(new QProgressBarPrivate), parent, 0)
+{
+ d_func()->init();
+}
+
+/*!
+ Reset the progress bar. The progress bar "rewinds" and shows no
+ progress.
+*/
+
+void QProgressBar::reset()
+{
+ Q_D(QProgressBar);
+ d->value = d->minimum - 1;
+ if (d->minimum == INT_MIN)
+ d->value = INT_MIN;
+ repaint();
+}
+
+/*!
+ \property QProgressBar::minimum
+ \brief the progress bar's minimum value
+
+ When setting this property, the \l maximum is adjusted if
+ necessary to ensure that the range remains valid. If the
+ current value falls outside the new range, the progress bar is reset
+ with reset().
+*/
+void QProgressBar::setMinimum(int minimum)
+{
+ setRange(minimum, qMax(d_func()->maximum, minimum));
+}
+
+int QProgressBar::minimum() const
+{
+ return d_func()->minimum;
+}
+
+
+/*!
+ \property QProgressBar::maximum
+ \brief the progress bar's maximum value
+
+ When setting this property, the \l minimum is adjusted if
+ necessary to ensure that the range remains valid. If the
+ current value falls outside the new range, the progress bar is reset
+ with reset().
+*/
+
+void QProgressBar::setMaximum(int maximum)
+{
+ setRange(qMin(d_func()->minimum, maximum), maximum);
+}
+
+int QProgressBar::maximum() const
+{
+ return d_func()->maximum;
+}
+
+/*!
+ \property QProgressBar::value
+ \brief the progress bar's current value
+
+ Attempting to change the current value to one outside
+ the minimum-maximum range has no effect on the current value.
+*/
+void QProgressBar::setValue(int value)
+{
+ Q_D(QProgressBar);
+ if (d->value == value
+ || ((value > d->maximum || value < d->minimum)
+ && (d->maximum != 0 || d->minimum != 0)))
+ return;
+ d->value = value;
+ emit valueChanged(value);
+#ifndef QT_NO_ACCESSIBILITY
+ QAccessible::updateAccessibility(this, 0, QAccessible::ValueChanged);
+#endif
+ if (d->repaintRequired())
+ repaint();
+}
+
+int QProgressBar::value() const
+{
+ return d_func()->value;
+}
+
+/*!
+ Sets the progress bar's minimum and maximum values to \a minimum and
+ \a maximum respectively.
+
+ If \a maximum is smaller than \a minimum, \a minimum becomes the only
+ legal value.
+
+ If the current value falls outside the new range, the progress bar is reset
+ with reset().
+
+ \sa minimum maximum
+*/
+void QProgressBar::setRange(int minimum, int maximum)
+{
+ Q_D(QProgressBar);
+ d->minimum = minimum;
+ d->maximum = qMax(minimum, maximum);
+ if ( d->value <(d->minimum-1) || d->value > d->maximum)
+ reset();
+}
+/*!
+ \property QProgressBar::textVisible
+ \brief whether the current completed percentage should be displayed
+
+ \sa textDirection
+*/
+void QProgressBar::setTextVisible(bool visible)
+{
+ Q_D(QProgressBar);
+ if (d->textVisible != visible) {
+ d->textVisible = visible;
+ repaint();
+ }
+}
+
+bool QProgressBar::isTextVisible() const
+{
+ return d_func()->textVisible;
+}
+
+/*!
+ \property QProgressBar::alignment
+ \brief the alignment of the progress bar
+*/
+void QProgressBar::setAlignment(Qt::Alignment alignment)
+{
+ if (d_func()->alignment != alignment) {
+ d_func()->alignment = alignment;
+ repaint();
+ }
+}
+
+Qt::Alignment QProgressBar::alignment() const
+{
+ return d_func()->alignment;
+}
+
+/*!
+ \reimp
+*/
+void QProgressBar::paintEvent(QPaintEvent *)
+{
+ QStylePainter paint(this);
+ QStyleOptionProgressBarV2 opt;
+ initStyleOption(&opt);
+ paint.drawControl(QStyle::CE_ProgressBar, opt);
+ d_func()->lastPaintedValue = d_func()->value;
+}
+
+/*!
+ \reimp
+*/
+QSize QProgressBar::sizeHint() const
+{
+ ensurePolished();
+ QFontMetrics fm = fontMetrics();
+ QStyleOptionProgressBarV2 opt;
+ initStyleOption(&opt);
+ int cw = style()->pixelMetric(QStyle::PM_ProgressBarChunkWidth, &opt, this);
+ QSize size = QSize(qMax(9, cw) * 7 + fm.width(QLatin1Char('0')) * 4, fm.height() + 8);
+ if (opt.orientation == Qt::Vertical)
+ size.transpose();
+ return style()->sizeFromContents(QStyle::CT_ProgressBar, &opt, size, this);
+}
+
+/*!
+ \reimp
+*/
+QSize QProgressBar::minimumSizeHint() const
+{
+ QSize size;
+ if (orientation() == Qt::Horizontal)
+ size = QSize(sizeHint().width(), fontMetrics().height() + 2);
+ else
+ size = QSize(fontMetrics().height() + 2, sizeHint().height());
+ return size;
+}
+
+/*!
+ \property QProgressBar::text
+ \brief the descriptive text shown with the progress bar
+
+ The text returned is the same as the text displayed in the center
+ (or in some styles, to the left) of the progress bar.
+
+ The progress shown in the text may be smaller than the minimum value,
+ indicating that the progress bar is in the "reset" state before any
+ progress is set.
+
+ In the default implementation, the text either contains a percentage
+ value that indicates the progress so far, or it is blank because the
+ progress bar is in the reset state.
+*/
+QString QProgressBar::text() const
+{
+ Q_D(const QProgressBar);
+ if (d->maximum == 0 || d->value < d->minimum
+ || (d->value == INT_MIN && d->minimum == INT_MIN))
+ return QString();
+
+ qint64 totalSteps = qint64(d->maximum) - qint64(d->minimum);
+
+ QString result = d->format;
+ result.replace(QLatin1String("%m"), QString::fromLatin1("%1").arg(totalSteps));
+ result.replace(QLatin1String("%v"), QString::fromLatin1("%1").arg(d->value));
+
+ // If max and min are equal and we get this far, it means that the
+ // progress bar has one step and that we are on that step. Return
+ // 100% here in order to avoid division by zero further down.
+ if (totalSteps == 0) {
+ result.replace(QLatin1String("%p"), QString::fromLatin1("%1").arg(100));
+ return result;
+ }
+
+ int progress = int(((qreal(d->value) - qreal(d->minimum)) * 100.0) / totalSteps);
+ result.replace(QLatin1String("%p"), QString::fromLatin1("%1").arg(progress));
+ return result;
+}
+
+/*!
+ \since 4.1
+ \property QProgressBar::orientation
+ \brief the orientation of the progress bar
+
+ The orientation must be \l Qt::Horizontal (the default) or \l
+ Qt::Vertical.
+
+ \sa invertedAppearance, textDirection
+*/
+
+void QProgressBar::setOrientation(Qt::Orientation orientation)
+{
+ Q_D(QProgressBar);
+ if (d->orientation == orientation)
+ return;
+ d->orientation = orientation;
+ if (!testAttribute(Qt::WA_WState_OwnSizePolicy)) {
+ QSizePolicy sp = sizePolicy();
+ sp.transpose();
+ setSizePolicy(sp);
+ setAttribute(Qt::WA_WState_OwnSizePolicy, false);
+ }
+ d->resetLayoutItemMargins();
+ update();
+ updateGeometry();
+}
+
+Qt::Orientation QProgressBar::orientation() const
+{
+ Q_D(const QProgressBar);
+ return d->orientation;
+}
+
+/*!
+ \since 4.1
+ \property QProgressBar::invertedAppearance
+ \brief whether or not a progress bar shows its progress inverted
+
+ If this property is false, the progress bar grows in the other
+ direction (e.g. from right to left). By default, the progress bar
+ is not inverted.
+
+ \sa orientation, layoutDirection
+*/
+
+void QProgressBar::setInvertedAppearance(bool invert)
+{
+ Q_D(QProgressBar);
+ d->invertedAppearance = invert;
+ update();
+}
+
+bool QProgressBar::invertedAppearance()
+{
+ Q_D(QProgressBar);
+ return d->invertedAppearance;
+}
+
+/*!
+ \since 4.1
+ \property QProgressBar::textDirection
+ \brief the reading direction of the \l text for vertical progress bars
+
+ This property has no impact on horizontal progress bars.
+ By default, the reading direction is QProgressBar::TopToBottom.
+
+ \sa orientation, textVisible
+*/
+void QProgressBar::setTextDirection(QProgressBar::Direction textDirection)
+{
+ Q_D(QProgressBar);
+ d->textDirection = textDirection;
+ update();
+}
+
+QProgressBar::Direction QProgressBar::textDirection()
+{
+ Q_D(QProgressBar);
+ return d->textDirection;
+}
+
+/*! \reimp */
+bool QProgressBar::event(QEvent *e)
+{
+ Q_D(QProgressBar);
+ if (e->type() == QEvent::StyleChange
+#ifdef Q_WS_MAC
+ || e->type() == QEvent::MacSizeChange
+#endif
+ )
+ d->resetLayoutItemMargins();
+ return QWidget::event(e);
+}
+
+/*!
+ \since 4.2
+ \property QProgressBar::format
+ \brief the string used to generate the current text
+
+ %p - is replaced by the percentage completed.
+ %v - is replaced by the current value.
+ %m - is replaced by the total number of steps.
+
+ The default value is "%p%".
+
+ \sa text()
+*/
+void QProgressBar::setFormat(const QString &format)
+{
+ Q_D(QProgressBar);
+ if (d->format == format)
+ return;
+ d->format = format;
+ update();
+}
+
+QString QProgressBar::format() const
+{
+ Q_D(const QProgressBar);
+ return d->format;
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_PROGRESSBAR
diff --git a/src/gui/widgets/qprogressbar.h b/src/gui/widgets/qprogressbar.h
new file mode 100644
index 0000000000..57c83fe546
--- /dev/null
+++ b/src/gui/widgets/qprogressbar.h
@@ -0,0 +1,130 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QPROGRESSBAR_H
+#define QPROGRESSBAR_H
+
+#include <QtGui/qframe.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+#ifndef QT_NO_PROGRESSBAR
+
+class QProgressBarPrivate;
+class QStyleOptionProgressBar;
+
+class Q_GUI_EXPORT QProgressBar : public QWidget
+{
+ Q_OBJECT
+ Q_ENUMS(Direction)
+ Q_PROPERTY(int minimum READ minimum WRITE setMinimum)
+ Q_PROPERTY(int maximum READ maximum WRITE setMaximum)
+ Q_PROPERTY(QString text READ text)
+ Q_PROPERTY(int value READ value WRITE setValue NOTIFY valueChanged)
+ Q_PROPERTY(Qt::Alignment alignment READ alignment WRITE setAlignment)
+ Q_PROPERTY(bool textVisible READ isTextVisible WRITE setTextVisible)
+ Q_PROPERTY(Qt::Orientation orientation READ orientation WRITE setOrientation)
+ Q_PROPERTY(bool invertedAppearance READ invertedAppearance WRITE setInvertedAppearance)
+ Q_PROPERTY(Direction textDirection READ textDirection WRITE setTextDirection)
+ Q_PROPERTY(QString format READ format WRITE setFormat)
+
+public:
+ enum Direction { TopToBottom, BottomToTop };
+
+ explicit QProgressBar(QWidget *parent = 0);
+
+ int minimum() const;
+ int maximum() const;
+
+ int value() const;
+
+ virtual QString text() const;
+ void setTextVisible(bool visible);
+ bool isTextVisible() const;
+
+ Qt::Alignment alignment() const;
+ void setAlignment(Qt::Alignment alignment);
+
+ QSize sizeHint() const;
+ QSize minimumSizeHint() const;
+
+ Qt::Orientation orientation() const;
+
+ void setInvertedAppearance(bool invert);
+ bool invertedAppearance();
+ void setTextDirection(QProgressBar::Direction textDirection);
+ QProgressBar::Direction textDirection();
+
+ void setFormat(const QString &format);
+ QString format() const;
+
+public Q_SLOTS:
+ void reset();
+ void setRange(int minimum, int maximum);
+ void setMinimum(int minimum);
+ void setMaximum(int maximum);
+ void setValue(int value);
+ void setOrientation(Qt::Orientation);
+
+Q_SIGNALS:
+ void valueChanged(int value);
+
+protected:
+ bool event(QEvent *e);
+ void paintEvent(QPaintEvent *);
+ void initStyleOption(QStyleOptionProgressBar *option) const;
+
+private:
+ Q_DECLARE_PRIVATE(QProgressBar)
+ Q_DISABLE_COPY(QProgressBar)
+};
+
+#endif // QT_NO_PROGRESSBAR
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QPROGRESSBAR_H
diff --git a/src/gui/widgets/qpushbutton.cpp b/src/gui/widgets/qpushbutton.cpp
new file mode 100644
index 0000000000..03ca751271
--- /dev/null
+++ b/src/gui/widgets/qpushbutton.cpp
@@ -0,0 +1,732 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qapplication.h"
+#include "qbitmap.h"
+#include "qdesktopwidget.h"
+#include "qdialog.h"
+#include <private/qdialog_p.h>
+#include "qdrawutil.h"
+#include "qevent.h"
+#include "qfontmetrics.h"
+#include "qmenu.h"
+#include "qstylepainter.h"
+#include "qpixmap.h"
+#include "qpointer.h"
+#include "qpushbutton.h"
+#include "qstyle.h"
+#include "qstyleoption.h"
+#include "qtoolbar.h"
+#include "qdebug.h"
+#include "qlayoutitem.h"
+#include "qdialogbuttonbox.h"
+
+#ifndef QT_NO_ACCESSIBILITY
+#include "qaccessible.h"
+#endif
+
+#include "private/qpushbutton_p.h"
+
+QT_BEGIN_NAMESPACE
+
+
+/*!
+ \class QPushButton
+ \brief The QPushButton widget provides a command button.
+
+ \ingroup basicwidgets
+ \mainclass
+
+ The push button, or command button, is perhaps the most commonly
+ used widget in any graphical user interface. Push (click) a button
+ to command the computer to perform some action, or to answer a
+ question. Typical buttons are OK, Apply, Cancel, Close, Yes, No
+ and Help.
+
+ A command button is rectangular and typically displays a text
+ label describing its action. A shortcut key can be specified by
+ preceding the preferred character with an ampersand in the
+ text. For example:
+
+ \snippet doc/src/snippets/code/src_gui_widgets_qpushbutton.cpp 0
+
+ In this example the shortcut is \e{Alt+D}. See the \l
+ {QShortcut#mnemonic}{QShortcut} documentation for details (to
+ display an actual ampersand, use '&&').
+
+ Push buttons display a textual label, and optionally a small
+ icon. These can be set using the constructors and changed later
+ using setText() and setIcon(). If the button is disabled the
+ appearance of the text and icon will be manipulated with respect
+ to the GUI style to make the button look "disabled".
+
+ A push button emits the signal clicked() when it is activated by
+ the mouse, the Spacebar or by a keyboard shortcut. Connect to
+ this signal to perform the button's action. Push buttons also
+ provide less commonly used signals, for example, pressed() and
+ released().
+
+ Command buttons in dialogs are by default auto-default buttons,
+ i.e. they become the default push button automatically when they
+ receive the keyboard input focus. A default button is a push
+ button that is activated when the user presses the Enter or Return
+ key in a dialog. You can change this with setAutoDefault(). Note
+ that auto-default buttons reserve a little extra space which is
+ necessary to draw a default-button indicator. If you do not want
+ this space around your buttons, call setAutoDefault(false).
+
+ Being so central, the button widget has grown to accommodate a
+ great many variations in the past decade. The Microsoft style
+ guide now shows about ten different states of Windows push buttons
+ and the text implies that there are dozens more when all the
+ combinations of features are taken into consideration.
+
+ The most important modes or states are:
+ \list
+ \i Available or not (grayed out, disabled).
+ \i Standard push button, toggling push button or menu button.
+ \i On or off (only for toggling push buttons).
+ \i Default or normal. The default button in a dialog can generally
+ be "clicked" using the Enter or Return key.
+ \i Auto-repeat or not.
+ \i Pressed down or not.
+ \endlist
+
+ As a general rule, use a push button when the application or
+ dialog window performs an action when the user clicks on it (such
+ as Apply, Cancel, Close and Help) \e and when the widget is
+ supposed to have a wide, rectangular shape with a text label.
+ Small, typically square buttons that change the state of the
+ window rather than performing an action (such as the buttons in
+ the top-right corner of the QFileDialog) are not command buttons,
+ but tool buttons. Qt provides a special class (QToolButton) for
+ these buttons.
+
+ If you need toggle behavior (see setCheckable()) or a button
+ that auto-repeats the activation signal when being pushed down
+ like the arrows in a scroll bar (see setAutoRepeat()), a command
+ button is probably not what you want. When in doubt, use a tool
+ button.
+
+ A variation of a command button is a menu button. These provide
+ not just one command, but several, since when they are clicked
+ they pop up a menu of options. Use the method setMenu() to
+ associate a popup menu with a push button.
+
+ Other classes of buttons are option buttons (see QRadioButton) and
+ check boxes (see QCheckBox).
+
+ \table 100%
+ \row \o \inlineimage macintosh-pushbutton.png Screenshot of a Macintosh style push button
+ \o A push button shown in the \l{Macintosh Style Widget Gallery}{Macintosh widget style}.
+
+ Note that when a button's width becomes smaller than 50 or
+ its height becomes smaller than 30, the button's corners are
+ changed from round to square. Use the setMinimumSize()
+ function to prevent this behavior.
+
+ \row \o \inlineimage windowsxp-pushbutton.png Screenshot of a Windows XP style push button
+ \o A push button shown in the \l{Windows XP Style Widget Gallery}{Windows XP widget style}.
+ \row \o \inlineimage plastique-pushbutton.png Screenshot of a Plastique style push button
+ \o A push button shown in the \l{Plastique Style Widget Gallery}{Plastique widget style}.
+ \endtable
+
+ In Qt, the QAbstractButton base class provides most of the modes
+ and other API, and QPushButton provides GUI logic.
+ See QAbstractButton for more information about the API.
+
+ \sa QToolButton, QRadioButton, QCheckBox, {fowler}{GUI Design Handbook: Push Button}
+*/
+
+/*!
+ \property QPushButton::autoDefault
+ \brief whether the push button is an auto default button
+
+ If this property is set to true then the push button is an auto
+ default button.
+
+ In some GUI styles a default button is drawn with an extra frame
+ around it, up to 3 pixels or more. Qt automatically keeps this
+ space free around auto-default buttons, i.e. auto-default buttons
+ may have a slightly larger size hint.
+
+ This property's default is true for buttons that have a QDialog
+ parent; otherwise it defaults to false.
+
+ See the \l default property for details of how \l default and
+ auto-default interact.
+*/
+
+/*!
+ \property QPushButton::default
+ \brief whether the push button is the default button
+
+ Default and autodefault buttons decide what happens when the user
+ presses enter in a dialog.
+
+ A button with this property set to true (i.e., the dialog's
+ \e default button,) will automatically be pressed when the user presses enter,
+ with one exception: if an \a autoDefault button currently has focus, the autoDefault
+ button is pressed. When the dialog has \l autoDefault buttons but no default button,
+ pressing enter will press either the \l autoDefault button that currently has focus, or if no
+ button has focus, the next \l autoDefault button in the focus chain.
+
+ In a dialog, only one push button at a time can be the default
+ button. This button is then displayed with an additional frame
+ (depending on the GUI style).
+
+ The default button behavior is provided only in dialogs. Buttons
+ can always be clicked from the keyboard by pressing Spacebar when
+ the button has focus.
+
+ If the default property is set to false on the current default button
+ while the dialog is visible, a new default will automatically be
+ assigned the next time a pushbutton in the dialog receives focus.
+
+ This property's default is false.
+*/
+
+/*!
+ \property QPushButton::flat
+ \brief whether the button border is raised
+
+ This property's default is false. If this property is set, most
+ styles will not paint the button background unless the button is
+ being pressed. setAutoFillBackground() can be used to ensure that
+ the background is filled using the QPalette::Button brush.
+*/
+
+/*!
+ Constructs a push button with no text and a \a parent.
+*/
+
+QPushButton::QPushButton(QWidget *parent)
+ : QAbstractButton(*new QPushButtonPrivate, parent)
+{
+ Q_D(QPushButton);
+ d->init();
+}
+
+/*!
+ Constructs a push button with the parent \a parent and the text \a
+ text.
+*/
+
+QPushButton::QPushButton(const QString &text, QWidget *parent)
+ : QAbstractButton(*new QPushButtonPrivate, parent)
+{
+ Q_D(QPushButton);
+ setText(text);
+ d->init();
+}
+
+
+/*!
+ Constructs a push button with an \a icon and a \a text, and a \a parent.
+
+ Note that you can also pass a QPixmap object as an icon (thanks to
+ the implicit type conversion provided by C++).
+
+*/
+QPushButton::QPushButton(const QIcon& icon, const QString &text, QWidget *parent)
+ : QAbstractButton(*new QPushButtonPrivate, parent)
+{
+ Q_D(QPushButton);
+ setText(text);
+ setIcon(icon);
+ d->init();
+}
+
+/*! \internal
+ */
+QPushButton::QPushButton(QPushButtonPrivate &dd, QWidget *parent)
+ : QAbstractButton(dd, parent)
+{
+ Q_D(QPushButton);
+ d->init();
+}
+
+/*!
+ Destroys the push button.
+*/
+QPushButton::~QPushButton()
+{
+}
+
+QDialog *QPushButtonPrivate::dialogParent() const
+{
+ Q_Q(const QPushButton);
+ const QWidget *p = q;
+ while (p && !p->isWindow()) {
+ p = p->parentWidget();
+ if (const QDialog *dialog = qobject_cast<const QDialog *>(p))
+ return const_cast<QDialog *>(dialog);
+ }
+ return 0;
+}
+
+/*!
+ Initialize \a option with the values from this QPushButton. This method is useful
+ for subclasses when they need a QStyleOptionButton, but don't want to fill
+ in all the information themselves.
+
+ \sa QStyleOption::initFrom()
+*/
+void QPushButton::initStyleOption(QStyleOptionButton *option) const
+{
+ if (!option)
+ return;
+
+ Q_D(const QPushButton);
+ option->initFrom(this);
+ option->features = QStyleOptionButton::None;
+ if (d->flat)
+ option->features |= QStyleOptionButton::Flat;
+#ifndef QT_NO_MENU
+ if (d->menu)
+ option->features |= QStyleOptionButton::HasMenu;
+#endif
+ if (autoDefault() || d->defaultButton)
+ option->features |= QStyleOptionButton::AutoDefaultButton;
+ if (d->defaultButton)
+ option->features |= QStyleOptionButton::DefaultButton;
+ if (d->down || d->menuOpen)
+ option->state |= QStyle::State_Sunken;
+ if (d->checked)
+ option->state |= QStyle::State_On;
+ if (!d->flat && !d->down)
+ option->state |= QStyle::State_Raised;
+ option->text = d->text;
+ option->icon = d->icon;
+ option->iconSize = iconSize();
+}
+
+void QPushButton::setAutoDefault(bool enable)
+{
+ Q_D(QPushButton);
+ uint state = enable ? QPushButtonPrivate::On : QPushButtonPrivate::Off;
+ if (d->autoDefault != QPushButtonPrivate::Auto && d->autoDefault == state)
+ return;
+ d->autoDefault = state;
+ d->sizeHint = QSize();
+ update();
+ updateGeometry();
+}
+
+bool QPushButton::autoDefault() const
+{
+ Q_D(const QPushButton);
+ if(d->autoDefault == QPushButtonPrivate::Auto)
+ return ( d->dialogParent() != 0 );
+ return d->autoDefault;
+}
+
+void QPushButton::setDefault(bool enable)
+{
+ Q_D(QPushButton);
+ if (d->defaultButton == enable)
+ return;
+ d->defaultButton = enable;
+ if (d->defaultButton) {
+ if (QDialog *dlg = d->dialogParent())
+ dlg->d_func()->setMainDefault(this);
+ }
+ update();
+#ifndef QT_NO_ACCESSIBILITY
+ QAccessible::updateAccessibility(this, 0, QAccessible::StateChanged);
+#endif
+}
+
+bool QPushButton::isDefault() const
+{
+ Q_D(const QPushButton);
+ return d->defaultButton;
+}
+
+/*!
+ \reimp
+*/
+QSize QPushButton::sizeHint() const
+{
+ Q_D(const QPushButton);
+ if (d->sizeHint.isValid())
+ return d->sizeHint;
+ ensurePolished();
+
+ int w = 0, h = 0;
+
+ QStyleOptionButton opt;
+ initStyleOption(&opt);
+
+ // calculate contents size...
+#ifndef QT_NO_ICON
+
+ bool showButtonBoxIcons = qobject_cast<QDialogButtonBox*>(parentWidget())
+ && style()->styleHint(QStyle::SH_DialogButtonBox_ButtonsHaveIcons);
+
+ if (!icon().isNull() || showButtonBoxIcons) {
+ int ih = opt.iconSize.height();
+ int iw = opt.iconSize.width() + 4;
+ w += iw;
+ h = qMax(h, ih);
+ }
+#endif
+ QString s(text());
+ bool empty = s.isEmpty();
+ if (empty)
+ s = QString::fromLatin1("XXXX");
+ QFontMetrics fm = fontMetrics();
+ QSize sz = fm.size(Qt::TextShowMnemonic, s);
+ if(!empty || !w)
+ w += sz.width();
+ if(!empty || !h)
+ h = qMax(h, sz.height());
+ opt.rect.setSize(QSize(w, h)); // PM_MenuButtonIndicator depends on the height
+#ifndef QT_NO_MENU
+ if (menu())
+ w += style()->pixelMetric(QStyle::PM_MenuButtonIndicator, &opt, this);
+#endif
+ d->sizeHint = (style()->sizeFromContents(QStyle::CT_PushButton, &opt, QSize(w, h), this).
+ expandedTo(QApplication::globalStrut()));
+ return d->sizeHint;
+}
+
+/*!
+ \reimp
+ */
+QSize QPushButton::minimumSizeHint() const
+{
+ return sizeHint();
+}
+
+
+/*!\reimp
+*/
+void QPushButton::paintEvent(QPaintEvent *)
+{
+ QStylePainter p(this);
+ QStyleOptionButton option;
+ initStyleOption(&option);
+ p.drawControl(QStyle::CE_PushButton, option);
+}
+
+
+/*! \reimp */
+void QPushButton::keyPressEvent(QKeyEvent *e)
+{
+ Q_D(QPushButton);
+ switch (e->key()) {
+ case Qt::Key_Enter:
+ case Qt::Key_Return:
+ if (autoDefault() || d->defaultButton) {
+ click();
+ break;
+ }
+ // fall through
+ default:
+ QAbstractButton::keyPressEvent(e);
+ }
+}
+
+/*!
+ \reimp
+*/
+void QPushButton::focusInEvent(QFocusEvent *e)
+{
+ Q_D(QPushButton);
+ if (e->reason() != Qt::PopupFocusReason && autoDefault() && !d->defaultButton) {
+ d->defaultButton = true;
+ QDialog *dlg = qobject_cast<QDialog*>(window());
+ if (dlg)
+ dlg->d_func()->setDefault(this);
+ }
+ QAbstractButton::focusInEvent(e);
+}
+
+/*!
+ \reimp
+*/
+void QPushButton::focusOutEvent(QFocusEvent *e)
+{
+ Q_D(QPushButton);
+ if (e->reason() != Qt::PopupFocusReason && autoDefault() && d->defaultButton) {
+ QDialog *dlg = qobject_cast<QDialog*>(window());
+ if (dlg)
+ dlg->d_func()->setDefault(0);
+ else
+ d->defaultButton = false;
+ }
+
+ QAbstractButton::focusOutEvent(e);
+#ifndef QT_NO_MENU
+ if (d->menu && d->menu->isVisible()) // restore pressed status
+ setDown(true);
+#endif
+}
+
+#ifndef QT_NO_MENU
+/*!
+ Associates the popup menu \a menu with this push button. This
+ turns the button into a menu button, which in some styles will
+ produce a small triangle to the right of the button's text.
+
+ Ownership of the menu is \e not transferred to the push button.
+
+ \table 100%
+ \row
+ \o \inlineimage plastique-pushbutton-menu.png Screenshot of a Plastique style push button with popup menu.
+ \o \inlineimage cleanlooks-pushbutton-menu.png Screenshot of a Cleanlooks style push button with popup menu.
+ \o Push buttons with popup menus shown in the \l{Plastique Style Widget Gallery}{Plastique widget style}
+ (left) and \l{Cleanlooks Style Widget Gallery}{Cleanlooks widget style} (right).
+ \endtable
+
+ \sa menu()
+*/
+void QPushButton::setMenu(QMenu* menu)
+{
+ Q_D(QPushButton);
+ if (menu == d->menu)
+ return;
+
+ if (menu && !d->menu) {
+ disconnect(this, SIGNAL(pressed()), this, SLOT(_q_popupPressed()));
+ connect(this, SIGNAL(pressed()), this, SLOT(_q_popupPressed()));
+ }
+ if (d->menu)
+ removeAction(d->menu->menuAction());
+ d->menu = menu;
+ if (d->menu)
+ addAction(d->menu->menuAction());
+
+ d->resetLayoutItemMargins();
+ d->sizeHint = QSize();
+ update();
+ updateGeometry();
+}
+
+/*!
+ Returns the button's associated popup menu or 0 if no popup menu
+ has been set.
+
+ \sa setMenu()
+*/
+QMenu* QPushButton::menu() const
+{
+ Q_D(const QPushButton);
+ return d->menu;
+}
+
+/*!
+ Shows (pops up) the associated popup menu. If there is no such
+ menu, this function does nothing. This function does not return
+ until the popup menu has been closed by the user.
+*/
+void QPushButton::showMenu()
+{
+ Q_D(QPushButton);
+ if (!d || !d->menu)
+ return;
+ setDown(true);
+ d->_q_popupPressed();
+}
+
+void QPushButtonPrivate::_q_popupPressed()
+{
+ Q_Q(QPushButton);
+ if (!down || !menu)
+ return;
+
+ menu->setNoReplayFor(q);
+ bool horizontal = true;
+#if !defined(QT_NO_TOOLBAR)
+ QToolBar *tb = qobject_cast<QToolBar*>(q->parentWidget());
+ if (tb && tb->orientation() == Qt::Vertical)
+ horizontal = false;
+#endif
+ QWidgetItem item(q);
+ QRect rect = item.geometry();
+ rect.setRect(rect.x() - q->x(), rect.y() - q->y(), rect.width(), rect.height());
+
+ QSize menuSize = menu->sizeHint();
+ QPoint globalPos = q->mapToGlobal(rect.topLeft());
+ int x = globalPos.x();
+ int y = globalPos.y();
+ if (horizontal) {
+ if (globalPos.y() + rect.height() + menuSize.height() <= qApp->desktop()->height()) {
+ y += rect.height();
+ } else {
+ y -= menuSize.height();
+ }
+ if (q->layoutDirection() == Qt::RightToLeft)
+ x += rect.width() - menuSize.width();
+ } else {
+ if (globalPos.x() + rect.width() + menu->sizeHint().width() <= qApp->desktop()->width())
+ x += rect.width();
+ else
+ x -= menuSize.width();
+ }
+ QPointer<QPushButton> guard(q);
+
+ //Because of a delay in menu effects, we must keep track of the
+ //menu visibility to avoid flicker on button release
+ menuOpen = true;
+ menu->exec(QPoint(x, y));
+ if (guard) {
+ menuOpen = false;
+ q->setDown(false);
+ }
+}
+#endif // QT_NO_MENU
+
+void QPushButtonPrivate::resetLayoutItemMargins()
+{
+ Q_Q(QPushButton);
+ QStyleOptionButton opt;
+ q->initStyleOption(&opt);
+ setLayoutItemMargins(QStyle::SE_PushButtonLayoutItem, &opt);
+}
+
+void QPushButton::setFlat(bool flat)
+{
+ Q_D(QPushButton);
+ if (d->flat == flat)
+ return;
+ d->flat = flat;
+ d->resetLayoutItemMargins();
+ d->sizeHint = QSize();
+ update();
+ updateGeometry();
+}
+
+bool QPushButton::isFlat() const
+{
+ Q_D(const QPushButton);
+ return d->flat;
+}
+
+/*! \reimp */
+bool QPushButton::event(QEvent *e)
+{
+ Q_D(QPushButton);
+ if (e->type() == QEvent::ParentChange) {
+ if (QDialog *dialog = d->dialogParent()) {
+ if (d->defaultButton)
+ dialog->d_func()->setMainDefault(this);
+ }
+ } else if (e->type() == QEvent::StyleChange
+#ifdef Q_WS_MAC
+ || e->type() == QEvent::MacSizeChange
+#endif
+ ) {
+ d->resetLayoutItemMargins();
+ updateGeometry();
+ }
+ return QAbstractButton::event(e);
+}
+
+#ifdef QT3_SUPPORT
+/*!
+ Use one of the constructors that doesn't take the \a name
+ argument and then use setObjectName() instead.
+*/
+QPushButton::QPushButton(QWidget *parent, const char *name)
+ : QAbstractButton(*new QPushButtonPrivate, parent)
+{
+ Q_D(QPushButton);
+ setObjectName(QString::fromAscii(name));
+ d->init();
+}
+
+/*!
+ Use one of the constructors that doesn't take the \a name
+ argument and then use setObjectName() instead.
+*/
+QPushButton::QPushButton(const QString &text, QWidget *parent, const char *name)
+ : QAbstractButton(*new QPushButtonPrivate, parent)
+{
+ Q_D(QPushButton);
+ setObjectName(QString::fromAscii(name));
+ setText(text);
+ d->init();
+}
+
+/*!
+ Use one of the constructors that doesn't take the \a name
+ argument and then use setObjectName() instead.
+*/
+QPushButton::QPushButton(const QIcon& icon, const QString &text, QWidget *parent, const char *name)
+ : QAbstractButton(*new QPushButtonPrivate, parent)
+{
+ Q_D(QPushButton);
+ setObjectName(QString::fromAscii(name));
+ setText(text);
+ setIcon(icon);
+ d->init();
+}
+#endif
+
+/*!
+ \fn void QPushButton::openPopup()
+
+ Use showMenu() instead.
+*/
+
+/*!
+ \fn bool QPushButton::isMenuButton() const
+
+ Use menu() != 0 instead.
+*/
+
+/*!
+ \fn void QPushButton::setPopup(QMenu* popup)
+
+ Use setMenu() instead.
+*/
+
+/*!
+ \fn QMenu* QPushButton::popup() const
+
+ Use menu() instead.
+*/
+
+QT_END_NAMESPACE
+
+#include "moc_qpushbutton.cpp"
diff --git a/src/gui/widgets/qpushbutton.h b/src/gui/widgets/qpushbutton.h
new file mode 100644
index 0000000000..3be304b9c4
--- /dev/null
+++ b/src/gui/widgets/qpushbutton.h
@@ -0,0 +1,124 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QPUSHBUTTON_H
+#define QPUSHBUTTON_H
+
+#include <QtGui/qabstractbutton.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+class QPushButtonPrivate;
+class QMenu;
+class QStyleOptionButton;
+
+class Q_GUI_EXPORT QPushButton : public QAbstractButton
+{
+ Q_OBJECT
+
+ Q_PROPERTY(bool autoDefault READ autoDefault WRITE setAutoDefault)
+ Q_PROPERTY(bool default READ isDefault WRITE setDefault)
+ Q_PROPERTY(bool flat READ isFlat WRITE setFlat)
+
+public:
+ explicit QPushButton(QWidget *parent=0);
+ explicit QPushButton(const QString &text, QWidget *parent=0);
+ QPushButton(const QIcon& icon, const QString &text, QWidget *parent=0);
+ ~QPushButton();
+
+ QSize sizeHint() const;
+ QSize minimumSizeHint() const;
+
+ bool autoDefault() const;
+ void setAutoDefault(bool);
+ bool isDefault() const;
+ void setDefault(bool);
+
+#ifndef QT_NO_MENU
+ void setMenu(QMenu* menu);
+ QMenu* menu() const;
+#endif
+
+ void setFlat(bool);
+ bool isFlat() const;
+
+public Q_SLOTS:
+#ifndef QT_NO_MENU
+ void showMenu();
+#endif
+
+protected:
+ bool event(QEvent *e);
+ void paintEvent(QPaintEvent *);
+ void keyPressEvent(QKeyEvent *);
+ void focusInEvent(QFocusEvent *);
+ void focusOutEvent(QFocusEvent *);
+ void initStyleOption(QStyleOptionButton *option) const;
+ QPushButton(QPushButtonPrivate &dd, QWidget* parent = 0);
+
+public:
+#ifdef QT3_SUPPORT
+ QT3_SUPPORT_CONSTRUCTOR QPushButton(QWidget *parent, const char* name);
+ QT3_SUPPORT_CONSTRUCTOR QPushButton(const QString &text, QWidget *parent, const char* name);
+ QT3_SUPPORT_CONSTRUCTOR QPushButton(const QIcon& icon, const QString &text, QWidget *parent, const char* name);
+ inline QT3_SUPPORT void openPopup() { showMenu(); }
+ inline QT3_SUPPORT bool isMenuButton() const { return menu() != 0; }
+ inline QT3_SUPPORT void setPopup(QMenu* popup) {setMenu(popup); }
+ inline QT3_SUPPORT QMenu* popup() const { return menu(); }
+#endif
+
+private:
+ Q_DISABLE_COPY(QPushButton)
+ Q_DECLARE_PRIVATE(QPushButton)
+#ifndef QT_NO_MENU
+ Q_PRIVATE_SLOT(d_func(), void _q_popupPressed())
+#endif
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QPUSHBUTTON_H
diff --git a/src/gui/widgets/qpushbutton_p.h b/src/gui/widgets/qpushbutton_p.h
new file mode 100644
index 0000000000..fc8f567c20
--- /dev/null
+++ b/src/gui/widgets/qpushbutton_p.h
@@ -0,0 +1,82 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "private/qabstractbutton_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.
+//
+
+QT_BEGIN_NAMESPACE
+
+class QDialog;
+class QPushButton;
+
+class QPushButtonPrivate : public QAbstractButtonPrivate
+{
+ Q_DECLARE_PUBLIC(QPushButton)
+public:
+ enum AutoDefaultValue { Off = 0, On = 1, Auto = 2 };
+
+ QPushButtonPrivate()
+ : QAbstractButtonPrivate(QSizePolicy::PushButton), autoDefault(Auto),
+ defaultButton(false), flat(false), menuOpen(false) {}
+
+ inline void init() { resetLayoutItemMargins(); }
+ void resetLayoutItemMargins();
+ void _q_popupPressed();
+ QDialog *dialogParent() const;
+
+ QPointer<QMenu> menu;
+ uint autoDefault : 2;
+ uint defaultButton : 1;
+ uint flat : 1;
+ uint menuOpen : 1;
+};
+
+QT_END_NAMESPACE
diff --git a/src/gui/widgets/qradiobutton.cpp b/src/gui/widgets/qradiobutton.cpp
new file mode 100644
index 0000000000..da231503a7
--- /dev/null
+++ b/src/gui/widgets/qradiobutton.cpp
@@ -0,0 +1,288 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qradiobutton.h"
+#include "qapplication.h"
+#include "qbitmap.h"
+#include "qbuttongroup.h"
+#include "qstylepainter.h"
+#include "qstyle.h"
+#include "qstyleoption.h"
+#include "qevent.h"
+
+#include "private/qabstractbutton_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class QRadioButtonPrivate : public QAbstractButtonPrivate
+{
+ Q_DECLARE_PUBLIC(QRadioButton)
+
+public:
+ QRadioButtonPrivate() : QAbstractButtonPrivate(QSizePolicy::RadioButton), hovering(true) {}
+ void init();
+ uint hovering : 1;
+};
+
+/*
+ Initializes the radio button.
+*/
+void QRadioButtonPrivate::init()
+{
+ Q_Q(QRadioButton);
+ q->setCheckable(true);
+ q->setAutoExclusive(true);
+ q->setMouseTracking(true);
+ q->setForegroundRole(QPalette::WindowText);
+ setLayoutItemMargins(QStyle::SE_RadioButtonLayoutItem);
+}
+
+/*!
+ \class QRadioButton
+ \brief The QRadioButton widget provides a radio button with a text label.
+
+ \ingroup basicwidgets
+ \mainclass
+
+ A QRadioButton is an option button that can be switched on (checked) or
+ off (unchecked). Radio buttons typically present the user with a "one
+ of many" choice. In a group of radio buttons only one radio button at
+ a time can be checked; if the user selects another button, the
+ previously selected button is switched off.
+
+ Radio buttons are autoExclusive by default. If auto-exclusive is
+ enabled, radio buttons that belong to the same parent widget
+ behave as if they were part of the same exclusive button group. If
+ you need multiple exclusive button groups for radio buttons that
+ belong to the same parent widget, put them into a QButtonGroup.
+
+ Whenever a button is switched on or off it emits the toggled() signal.
+ Connect to this signal if you want to trigger an action each time the
+ button changes state. Use isChecked() to see if a particular button is
+ selected.
+
+ Just like QPushButton, a radio button displays text, and
+ optionally a small icon. The icon is set with setIcon(). The text
+ can be set in the constructor or with setText(). A shortcut key
+ can be specified by preceding the preferred character with an
+ ampersand in the text. For example:
+
+ \snippet doc/src/snippets/code/src_gui_widgets_qradiobutton.cpp 0
+
+ In this example the shortcut is \e{Alt+c}. See the \l
+ {QShortcut#mnemonic}{QShortcut} documentation for details (to
+ display an actual ampersand, use '&&').
+
+ Important inherited members: text(), setText(), text(),
+ setDown(), isDown(), autoRepeat(), group(), setAutoRepeat(),
+ toggle(), pressed(), released(), clicked(), and toggled().
+
+ \table 100%
+ \row \o \inlineimage plastique-radiobutton.png Screenshot of a Plastique radio button
+ \o A radio button shown in the \l{Plastique Style Widget Gallery}{Plastique widget style}.
+ \row \o \inlineimage windows-radiobutton.png Screenshot of a Windows XP radio button
+ \o A radio button shown in the \l{Windows XP Style Widget Gallery}{Windows XP widget style}.
+ \row \o \inlineimage macintosh-radiobutton.png Screenshot of a Macintosh radio button
+ \o A radio button shown in the \l{Macintosh Style Widget Gallery}{Macintosh widget style}.
+ \endtable
+
+ \sa QPushButton, QToolButton, QCheckBox, {fowler}{GUI Design Handbook: Radio Button},
+ {Group Box Example}
+*/
+
+
+/*!
+ Constructs a radio button with the given \a parent, but with no text or
+ pixmap.
+
+ The \a parent argument is passed on to the QAbstractButton constructor.
+*/
+
+QRadioButton::QRadioButton(QWidget *parent)
+ : QAbstractButton(*new QRadioButtonPrivate, parent)
+{
+ Q_D(QRadioButton);
+ d->init();
+}
+
+/*!
+ Constructs a radio button with the given \a parent and a \a text string.
+
+ The \a parent argument is passed on to the QAbstractButton constructor.
+*/
+
+QRadioButton::QRadioButton(const QString &text, QWidget *parent)
+ : QAbstractButton(*new QRadioButtonPrivate, parent)
+{
+ Q_D(QRadioButton);
+ d->init();
+ setText(text);
+}
+
+/*!
+ Initialize \a option with the values from this QRadioButton. This method is useful
+ for subclasses when they need a QStyleOptionButton, but don't want to fill
+ in all the information themselves.
+
+ \sa QStyleOption::initFrom()
+*/
+void QRadioButton::initStyleOption(QStyleOptionButton *option) const
+{
+ if (!option)
+ return;
+ Q_D(const QRadioButton);
+ option->initFrom(this);
+ option->text = d->text;
+ option->icon = d->icon;
+ option->iconSize = iconSize();
+ if (d->down)
+ option->state |= QStyle::State_Sunken;
+ option->state |= (d->checked) ? QStyle::State_On : QStyle::State_Off;
+ if (testAttribute(Qt::WA_Hover) && underMouse()) {
+ if (d->hovering)
+ option->state |= QStyle::State_MouseOver;
+ else
+ option->state &= ~QStyle::State_MouseOver;
+ }
+}
+
+/*!
+ \reimp
+*/
+QSize QRadioButton::sizeHint() const
+{
+ Q_D(const QRadioButton);
+ if (d->sizeHint.isValid())
+ return d->sizeHint;
+ ensurePolished();
+ QStyleOptionButton opt;
+ initStyleOption(&opt);
+ QSize sz = style()->itemTextRect(fontMetrics(), QRect(0, 0, 1, 1), Qt::TextShowMnemonic,
+ false, text()).size();
+ if (!opt.icon.isNull())
+ sz = QSize(sz.width() + opt.iconSize.width() + 4, qMax(sz.height(), opt.iconSize.height()));
+ d->sizeHint = (style()->sizeFromContents(QStyle::CT_RadioButton, &opt, sz, this).
+ expandedTo(QApplication::globalStrut()));
+ return d->sizeHint;
+}
+
+/*!
+ \reimp
+*/
+bool QRadioButton::hitButton(const QPoint &pos) const
+{
+ QStyleOptionButton opt;
+ initStyleOption(&opt);
+ return style()->subElementRect(QStyle::SE_RadioButtonClickRect, &opt, this).contains(pos);
+}
+
+/*!
+ \reimp
+*/
+void QRadioButton::mouseMoveEvent(QMouseEvent *e)
+{
+ Q_D(QRadioButton);
+ if (testAttribute(Qt::WA_Hover)) {
+ bool hit = false;
+ if (underMouse())
+ hit = hitButton(e->pos());
+
+ if (hit != d->hovering) {
+ update();
+ d->hovering = hit;
+ }
+ }
+
+ QAbstractButton::mouseMoveEvent(e);
+}
+
+/*!\reimp
+ */
+void QRadioButton::paintEvent(QPaintEvent *)
+{
+ QStylePainter p(this);
+ QStyleOptionButton opt;
+ initStyleOption(&opt);
+ p.drawControl(QStyle::CE_RadioButton, opt);
+}
+
+/*! \reimp */
+bool QRadioButton::event(QEvent *e)
+{
+ Q_D(QRadioButton);
+ if (e->type() == QEvent::StyleChange
+#ifdef Q_WS_MAC
+ || e->type() == QEvent::MacSizeChange
+#endif
+ )
+ d->setLayoutItemMargins(QStyle::SE_RadioButtonLayoutItem);
+ return QAbstractButton::event(e);
+}
+
+#ifdef QT3_SUPPORT
+/*!
+ Use one of the constructors that doesn't take the \a name
+ argument and then use setObjectName() instead.
+*/
+QRadioButton::QRadioButton(QWidget *parent, const char* name)
+ : QAbstractButton(*new QRadioButtonPrivate, parent)
+{
+ Q_D(QRadioButton);
+ d->init();
+ setObjectName(QString::fromAscii(name));
+}
+
+/*!
+ Use one of the constructors that doesn't take the \a name
+ argument and then use setObjectName() instead.
+*/
+QRadioButton::QRadioButton(const QString &text, QWidget *parent, const char* name)
+ : QAbstractButton(*new QRadioButtonPrivate, parent)
+{
+ Q_D(QRadioButton);
+ d->init();
+ setObjectName(QString::fromAscii(name));
+ setText(text);
+}
+
+#endif
+
+QT_END_NAMESPACE
diff --git a/src/gui/widgets/qradiobutton.h b/src/gui/widgets/qradiobutton.h
new file mode 100644
index 0000000000..158998d268
--- /dev/null
+++ b/src/gui/widgets/qradiobutton.h
@@ -0,0 +1,88 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QRADIOBUTTON_H
+#define QRADIOBUTTON_H
+
+#include <QtGui/qabstractbutton.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+class QRadioButtonPrivate;
+class QStyleOptionButton;
+
+class Q_GUI_EXPORT QRadioButton : public QAbstractButton
+{
+ Q_OBJECT
+
+public:
+ explicit QRadioButton(QWidget *parent=0);
+ explicit QRadioButton(const QString &text, QWidget *parent=0);
+
+ QSize sizeHint() const;
+
+protected:
+ bool event(QEvent *e);
+ bool hitButton(const QPoint &) const;
+ void paintEvent(QPaintEvent *);
+ void mouseMoveEvent(QMouseEvent *);
+ void initStyleOption(QStyleOptionButton *button) const;
+
+#ifdef QT3_SUPPORT
+public:
+ QT3_SUPPORT_CONSTRUCTOR QRadioButton(QWidget *parent, const char* name);
+ QT3_SUPPORT_CONSTRUCTOR QRadioButton(const QString &text, QWidget *parent, const char* name);
+#endif
+
+private:
+ Q_DECLARE_PRIVATE(QRadioButton)
+ Q_DISABLE_COPY(QRadioButton)
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QRADIOBUTTON_H
diff --git a/src/gui/widgets/qrubberband.cpp b/src/gui/widgets/qrubberband.cpp
new file mode 100644
index 0000000000..a394f4a562
--- /dev/null
+++ b/src/gui/widgets/qrubberband.cpp
@@ -0,0 +1,339 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qbitmap.h"
+#include "qevent.h"
+#include "qstylepainter.h"
+#include "qrubberband.h"
+#include "qtimer.h"
+
+#ifndef QT_NO_RUBBERBAND
+
+#include "qstyle.h"
+#include "qstyleoption.h"
+#ifdef Q_WS_MAC
+# include <private/qt_mac_p.h>
+# include <private/qt_cocoa_helpers_mac_p.h>
+#endif
+
+#include <qdebug.h>
+
+#include <private/qwidget_p.h>
+
+QT_BEGIN_NAMESPACE
+
+//### a rubberband window type would be a more elegant solution
+#define RUBBERBAND_WINDOW_TYPE Qt::ToolTip
+
+class QRubberBandPrivate : public QWidgetPrivate
+{
+ Q_DECLARE_PUBLIC(QRubberBand)
+public:
+ QRect rect;
+ QRubberBand::Shape shape;
+ QRegion clipping;
+ void updateMask();
+};
+
+/*!
+ Initialize \a option with the values from this QRubberBand. This method
+ is useful for subclasses when they need a QStyleOptionRubberBand, but don't want
+ to fill in all the information themselves.
+
+ \sa QStyleOption::initFrom()
+*/
+void QRubberBand::initStyleOption(QStyleOptionRubberBand *option) const
+{
+ if (!option)
+ return;
+ option->initFrom(this);
+ option->shape = d_func()->shape;
+#ifndef Q_WS_MAC
+ option->opaque = true;
+#else
+ option->opaque = windowFlags() & RUBBERBAND_WINDOW_TYPE;
+#endif
+}
+
+/*!
+ \class QRubberBand
+ \brief The QRubberBand class provides a rectangle or line that can
+ indicate a selection or a boundary.
+
+ \ingroup misc
+ \mainclass
+
+ A rubber band is often used to show a new bounding area (as in a
+ QSplitter or a QDockWidget that is undocking). Historically this has
+ been implemented using a QPainter and XOR, but this approach
+ doesn't always work properly since rendering can happen in the
+ window below the rubber band, but before the rubber band has been
+ "erased".
+
+ You can create a QRubberBand whenever you need to render a rubber band
+ around a given area (or to represent a single line), then call
+ setGeometry(), move() or resize() to position and size it. A common
+ pattern is to do this in conjunction with mouse events. For example:
+
+ \snippet doc/src/snippets/code/src_gui_widgets_qrubberband.cpp 0
+
+ If you pass a parent to QRubberBand's constructor, the rubber band will
+ display only inside its parent, but stays on top of other child widgets.
+ If no parent is passed, QRubberBand will act as a top-level widget.
+
+ Call show() to make the rubber band visible; also when the
+ rubber band is not a top-level. Hiding or destroying
+ the widget will make the rubber band disappear. The rubber band
+ can be a \l Rectangle or a \l Line (vertical or horizontal),
+ depending on the shape() it was given when constructed.
+*/
+
+// ### DOC: How about some nice convenience constructors?
+//QRubberBand::QRubberBand(QRubberBand::Type t, const QRect &rect, QWidget *p)
+//QRubberBand::QRubberBand(QRubberBand::Type t, int x, int y, int w, int h, QWidget *p)
+
+/*!
+ Constructs a rubber band of shape \a s, with parent \a p.
+
+ By default a rectangular rubber band (\a s is \c Rectangle) will
+ use a mask, so that a small border of the rectangle is all
+ that is visible. Some styles (e.g., native Mac OS X) will
+ change this and call QWidget::setWindowOpacity() to make a
+ semi-transparent filled selection rectangle.
+*/
+QRubberBand::QRubberBand(Shape s, QWidget *p)
+ : QWidget(*new QRubberBandPrivate, p, (p && p->windowType() != Qt::Desktop) ? Qt::Widget : RUBBERBAND_WINDOW_TYPE)
+{
+ Q_D(QRubberBand);
+ d->shape = s;
+ setAttribute(Qt::WA_TransparentForMouseEvents);
+#ifndef Q_WS_WIN
+ setAttribute(Qt::WA_NoSystemBackground);
+#endif //Q_WS_WIN
+ setAttribute(Qt::WA_WState_ExplicitShowHide);
+ setVisible(false);
+#ifdef Q_WS_MAC
+ if (isWindow()) {
+ createWinId();
+ extern OSWindowRef qt_mac_window_for(const QWidget *); //qwidget_mac.cpp
+ macWindowSetHasShadow(qt_mac_window_for(this), false);
+ }
+#endif
+}
+
+/*!
+ Destructor.
+*/
+QRubberBand::~QRubberBand()
+{
+}
+
+/*!
+ \enum QRubberBand::Shape
+
+ This enum specifies what shape a QRubberBand should have. This is
+ a drawing hint that is passed down to the style system, and can be
+ interpreted by each QStyle.
+
+ \value Line A QRubberBand can represent a vertical or horizontal
+ line. Geometry is still given in rect() and the line
+ will fill the given geometry on most styles.
+
+ \value Rectangle A QRubberBand can represent a rectangle. Some
+ styles will interpret this as a filled (often
+ semi-transparent) rectangle, or a rectangular
+ outline.
+*/
+
+/*!
+ Returns the shape of this rubber band. The shape can only be set
+ upon construction.
+*/
+QRubberBand::Shape QRubberBand::shape() const
+{
+ Q_D(const QRubberBand);
+ return d->shape;
+}
+
+/*!
+ \internal
+*/
+void QRubberBandPrivate::updateMask()
+{
+ Q_Q(QRubberBand);
+ QStyleHintReturnMask mask;
+ QStyleOptionRubberBand opt;
+ q->initStyleOption(&opt);
+ if (q->style()->styleHint(QStyle::SH_RubberBand_Mask, &opt, q, &mask)) {
+ q->setMask(mask.region);
+ } else {
+ q->clearMask();
+ }
+}
+
+/*!
+ \reimp
+*/
+void QRubberBand::paintEvent(QPaintEvent *)
+{
+ QStylePainter painter(this);
+ QStyleOptionRubberBand option;
+ initStyleOption(&option);
+ painter.drawControl(QStyle::CE_RubberBand, option);
+}
+
+/*!
+ \reimp
+*/
+void QRubberBand::changeEvent(QEvent *e)
+{
+ QWidget::changeEvent(e);
+ switch (e->type()) {
+ case QEvent::ParentChange:
+ if (parent()) {
+ setWindowFlags(windowFlags() & ~RUBBERBAND_WINDOW_TYPE);
+ } else {
+ setWindowFlags(windowFlags() | RUBBERBAND_WINDOW_TYPE);
+ }
+ break;
+ default:
+ break;
+ }
+
+ if (e->type() == QEvent::ZOrderChange)
+ raise();
+}
+
+/*!
+ \reimp
+*/
+void QRubberBand::showEvent(QShowEvent *e)
+{
+ raise();
+ e->ignore();
+}
+
+/*!
+ \reimp
+*/
+void QRubberBand::resizeEvent(QResizeEvent *)
+{
+ Q_D(QRubberBand);
+ d->updateMask();
+}
+
+/*!
+ \reimp
+*/
+void QRubberBand::moveEvent(QMoveEvent *)
+{
+ Q_D(QRubberBand);
+ d->updateMask();
+}
+
+/*!
+ \fn void QRubberBand::move(const QPoint &p);
+
+ \overload
+
+ Moves the rubberband to point \a p.
+
+ \sa resize()
+*/
+
+/*!
+ \fn void QRubberBand::move(int x, int y);
+
+ Moves the rubberband to point (\a x, \a y).
+
+ \sa resize()
+*/
+
+/*!
+ \fn void QRubberBand::resize(const QSize &size);
+
+ \overload
+
+ Resizes the rubberband so that its new size is \a size.
+
+ \sa move()
+*/
+
+/*!
+ \fn void QRubberBand::resize(int width, int height);
+
+ Resizes the rubberband so that its width is \a width, and its
+ height is \a height.
+
+ \sa move()
+*/
+
+/*!
+ \fn void QRubberBand::setGeometry(const QRect &rect)
+
+ Sets the geometry of the rubber band to \a rect, specified in the coordinate system
+ of its parent widget.
+
+ \sa QWidget::geometry
+*/
+void QRubberBand::setGeometry(const QRect &geom)
+{
+ QWidget::setGeometry(geom);
+}
+
+/*!
+ \fn void QRubberBand::setGeometry(int x, int y, int width, int height)
+ \overload
+
+ Sets the geometry of the rubberband to the rectangle whose top-left corner lies at
+ the point (\a x, \a y), and with dimensions specified by \a width and \a height.
+ The geometry is specified in the parent widget's coordinate system.
+*/
+
+/*! \reimp */
+bool QRubberBand::event(QEvent *e)
+{
+ return QWidget::event(e);
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_RUBBERBAND
diff --git a/src/gui/widgets/qrubberband.h b/src/gui/widgets/qrubberband.h
new file mode 100644
index 0000000000..f4c3ffadd3
--- /dev/null
+++ b/src/gui/widgets/qrubberband.h
@@ -0,0 +1,104 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QRUBBERBAND_H
+#define QRUBBERBAND_H
+
+#include <QtGui/qwidget.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+#ifndef QT_NO_RUBBERBAND
+
+class QRubberBandPrivate;
+class QStyleOptionRubberBand;
+
+class Q_GUI_EXPORT QRubberBand : public QWidget
+{
+ Q_OBJECT
+
+public:
+ enum Shape { Line, Rectangle };
+ explicit QRubberBand(Shape, QWidget * =0);
+ ~QRubberBand();
+
+ Shape shape() const;
+
+ void setGeometry(const QRect &r);
+
+ inline void setGeometry(int x, int y, int w, int h);
+ inline void move(int x, int y);
+ inline void move(const QPoint &p)
+ { move(p.x(), p.y()); }
+ inline void resize(int w, int h)
+ { setGeometry(geometry().x(), geometry().y(), w, h); }
+ inline void resize(const QSize &s)
+ { resize(s.width(), s.height()); }
+
+protected:
+ bool event(QEvent *e);
+ void paintEvent(QPaintEvent *);
+ void changeEvent(QEvent *);
+ void showEvent(QShowEvent *);
+ void resizeEvent(QResizeEvent *);
+ void moveEvent(QMoveEvent *);
+ void initStyleOption(QStyleOptionRubberBand *option) const;
+
+private:
+ Q_DECLARE_PRIVATE(QRubberBand)
+};
+
+inline void QRubberBand::setGeometry(int ax, int ay, int aw, int ah)
+{ setGeometry(QRect(ax, ay, aw, ah)); }
+inline void QRubberBand::move(int ax, int ay)
+{ setGeometry(ax, ay, width(), height()); }
+
+#endif // QT_NO_RUBBERBAND
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QRUBBERBAND_H
diff --git a/src/gui/widgets/qscrollarea.cpp b/src/gui/widgets/qscrollarea.cpp
new file mode 100644
index 0000000000..6aca7d3c36
--- /dev/null
+++ b/src/gui/widgets/qscrollarea.cpp
@@ -0,0 +1,522 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qscrollarea.h"
+#include "private/qscrollarea_p.h"
+
+#ifndef QT_NO_SCROLLAREA
+
+#include "qscrollbar.h"
+#include "qlayout.h"
+#include "qstyle.h"
+#include "qapplication.h"
+#include "qvariant.h"
+#include "qdebug.h"
+#include "private/qlayoutengine_p.h"
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QScrollArea
+
+ \brief The QScrollArea class provides a scrolling view onto
+ another widget.
+
+ \ingroup basicwidgets
+ \mainclass
+
+ A scroll area is used to display the contents of a child widget
+ within a frame. If the widget exceeds the size of the frame, the
+ view can provide scroll bars so that the entire area of the child
+ widget can be viewed. The child widget must be specified with
+ setWidget(). For example:
+
+ \snippet doc/src/snippets/code/src_gui_widgets_qscrollarea.cpp 0
+
+ The code above creates a scroll area (shown in the images below)
+ containing an image label. When scaling the image, the scroll area
+ can provide the necessary scroll bars:
+
+ \table
+ \row
+ \o \inlineimage qscrollarea-noscrollbars.png
+ \o \inlineimage qscrollarea-onescrollbar.png
+ \o \inlineimage qscrollarea-twoscrollbars.png
+ \endtable
+
+ The scroll bars appearance depends on the currently set \l
+ {Qt::ScrollBarPolicy}{scroll bar policies}. You can control the
+ appearance of the scroll bars using the inherited functionality
+ from QAbstractScrollArea.
+
+ For example, you can set the
+ QAbstractScrollArea::horizontalScrollBarPolicy and
+ QAbstractScrollArea::verticalScrollBarPolicy properties. Or if you
+ want the scroll bars to adjust dynamically when the contents of
+ the scroll area changes, you can use the \l
+ {QAbstractScrollArea::horizontalScrollBar()}{horizontalScrollBar()}
+ and \l
+ {QAbstractScrollArea::verticalScrollBar()}{verticalScrollBar()}
+ functions (which enable you to access the scroll bars) and set the
+ scroll bars' values whenever the scroll area's contents change,
+ using the QScrollBar::setValue() function.
+
+ You can retrieve the child widget using the widget() function. The
+ view can be made to be resizable with the setWidgetResizable()
+ function. The alignment of the widget can be specified with
+ setAlignment().
+
+ Two convenience functions ensureVisible() and
+ ensureWidgetVisible() ensure a certain region of the contents is
+ visible inside the viewport, by scrolling the contents if
+ necessary.
+
+ \section1 Size Hints and Layouts
+
+ When using a scroll area to display the contents of a custom
+ widget, it is important to ensure that the
+ \l{QWidget::sizeHint}{size hint} of the child widget is set to a
+ suitable value. If a standard QWidget is used for the child
+ widget, it may be necessary to call QWidget::setMinimumSize() to
+ ensure that the contents of the widget are shown correctly within
+ the scroll area.
+
+ If a scroll area is used to display the contents of a widget that
+ contains child widgets arranged in a layout, it is important to
+ realise that the size policy of the layout will also determine the
+ size of the widget. This is especially useful to know if you intend
+ to dynamically change the contents of the layout. In such cases,
+ setting the layout's \l{QLayout::sizeConstraint}{size constraint}
+ property to one which provides constraints on the minimum and/or
+ maximum size of the layout (e.g., QLayout::SetMinAndMaxSize) will
+ cause the size of the the scroll area to be updated whenever the
+ contents of the layout changes.
+
+ For a complete example using the QScrollArea class, see the \l
+ {widgets/imageviewer}{Image Viewer} example. The example shows how
+ to combine QLabel and QScrollArea to display an image.
+
+ \sa QAbstractScrollArea, QScrollBar, {Image Viewer Example}
+*/
+
+
+/*!
+ Constructs an empty scroll area with the given \a parent.
+
+ \sa setWidget()
+*/
+QScrollArea::QScrollArea(QWidget *parent)
+ : QAbstractScrollArea(*new QScrollAreaPrivate,parent)
+{
+ Q_D(QScrollArea);
+ d->viewport->setBackgroundRole(QPalette::NoRole);
+ d->vbar->setSingleStep(20);
+ d->hbar->setSingleStep(20);
+ d->layoutChildren();
+}
+
+/*!
+ \internal
+*/
+QScrollArea::QScrollArea(QScrollAreaPrivate &dd, QWidget *parent)
+ : QAbstractScrollArea(dd, parent)
+{
+ Q_D(QScrollArea);
+ d->viewport->setBackgroundRole(QPalette::NoRole);
+ d->vbar->setSingleStep(20);
+ d->hbar->setSingleStep(20);
+ d->layoutChildren();
+}
+
+/*!
+ Destroys the scroll area and its child widget.
+
+ \sa setWidget()
+*/
+QScrollArea::~QScrollArea()
+{
+}
+
+void QScrollAreaPrivate::updateWidgetPosition()
+{
+ Q_Q(QScrollArea);
+ Qt::LayoutDirection dir = q->layoutDirection();
+ QRect scrolled = QStyle::visualRect(dir, viewport->rect(), QRect(QPoint(-hbar->value(), -vbar->value()), widget->size()));
+ QRect aligned = QStyle::alignedRect(dir, alignment, widget->size(), viewport->rect());
+ widget->move(widget->width() < viewport->width() ? aligned.x() : scrolled.x(),
+ widget->height() < viewport->height() ? aligned.y() : scrolled.y());
+}
+
+void QScrollAreaPrivate::updateScrollBars()
+{
+ Q_Q(QScrollArea);
+ if (!widget)
+ return;
+ QSize p = viewport->size();
+ QSize m = q->maximumViewportSize();
+
+ QSize min = qSmartMinSize(widget);
+ QSize max = qSmartMaxSize(widget);
+
+ if (resizable) {
+ if ((widget->layout() ? widget->layout()->hasHeightForWidth() : widget->sizePolicy().hasHeightForWidth())) {
+ QSize p_hfw = p.expandedTo(min).boundedTo(max);
+ int h = widget->heightForWidth( p_hfw.width() );
+ min = QSize(p_hfw.width(), qMax(p_hfw.height(), h));
+ }
+ }
+
+ if ((resizable && m.expandedTo(min) == m && m.boundedTo(max) == m)
+ || (!resizable && m.expandedTo(widget->size()) == m))
+ p = m; // no scroll bars needed
+
+ if (resizable)
+ widget->resize(p.expandedTo(min).boundedTo(max));
+ QSize v = widget->size();
+
+ hbar->setRange(0, v.width() - p.width());
+ hbar->setPageStep(p.width());
+ vbar->setRange(0, v.height() - p.height());
+ vbar->setPageStep(p.height());
+ updateWidgetPosition();
+
+}
+
+/*!
+ Returns the scroll area's widget, or 0 if there is none.
+
+ \sa setWidget()
+*/
+
+QWidget *QScrollArea::widget() const
+{
+ Q_D(const QScrollArea);
+ return d->widget;
+}
+
+/*!
+ \fn void QScrollArea::setWidget(QWidget *widget)
+
+ Sets the scroll area's \a widget.
+
+ The \a widget becomes a child of the scroll area, and will be
+ destroyed when the scroll area is deleted or when a new widget is
+ set.
+
+ The widget's \l{QWidget::setAutoFillBackground()}{autoFillBackground}
+ property will be set to \c{true}.
+
+ If the scroll area is visible when the \a widget is
+ added, you must \l{QWidget::}{show()} it explicitly.
+
+ Note that You must add the layout of \a widget before you call
+ this function; if you add it later, the \a widget will not be
+ visible - regardless of when you \l{QWidget::}{show()} the scroll
+ area. In this case, you can also not \l{QWidget::}{show()} the \a
+ widget later.
+
+ \sa widget()
+*/
+void QScrollArea::setWidget(QWidget *widget)
+{
+ Q_D(QScrollArea);
+ if (widget == d->widget || !widget)
+ return;
+
+ delete d->widget;
+ d->widget = 0;
+ d->hbar->setValue(0);
+ d->vbar->setValue(0);
+ if (widget->parentWidget() != d->viewport)
+ widget->setParent(d->viewport);
+ if (!widget->testAttribute(Qt::WA_Resized))
+ widget->resize(widget->sizeHint());
+ d->widget = widget;
+ d->widget->setAutoFillBackground(true);
+ widget->installEventFilter(this);
+ d->widgetSize = QSize();
+ d->updateScrollBars();
+ d->widget->show();
+
+}
+
+/*!
+ Removes the scroll area's widget, and passes ownership of the
+ widget to the caller.
+
+ \sa widget()
+ */
+QWidget *QScrollArea::takeWidget()
+{
+ Q_D(QScrollArea);
+ QWidget *w = d->widget;
+ d->widget = 0;
+ if (w)
+ w->setParent(0);
+ return w;
+}
+
+/*!
+ \reimp
+ */
+bool QScrollArea::event(QEvent *e)
+{
+ Q_D(QScrollArea);
+ if (e->type() == QEvent::StyleChange || e->type() == QEvent::LayoutRequest) {
+ d->updateScrollBars();
+ }
+#ifdef QT_KEYPAD_NAVIGATION
+ else if (QApplication::keypadNavigationEnabled()) {
+ if (e->type() == QEvent::Show)
+ QApplication::instance()->installEventFilter(this);
+ else if (e->type() == QEvent::Hide)
+ QApplication::instance()->removeEventFilter(this);
+ }
+#endif
+ return QAbstractScrollArea::event(e);
+}
+
+
+/*!
+ \reimp
+ */
+bool QScrollArea::eventFilter(QObject *o, QEvent *e)
+{
+ Q_D(QScrollArea);
+#ifdef QT_KEYPAD_NAVIGATION
+ if (d->widget && o != d->widget && e->type() == QEvent::FocusIn
+ && QApplication::keypadNavigationEnabled()) {
+ if (o->isWidgetType())
+ ensureWidgetVisible(static_cast<QWidget *>(o));
+ }
+#endif
+ if (o == d->widget && e->type() == QEvent::Resize)
+ d->updateScrollBars();
+
+ return false;
+}
+
+/*!
+ \reimp
+ */
+void QScrollArea::resizeEvent(QResizeEvent *)
+{
+ Q_D(QScrollArea);
+ d->updateScrollBars();
+
+}
+
+
+/*!\reimp
+ */
+void QScrollArea::scrollContentsBy(int, int)
+{
+ Q_D(QScrollArea);
+ if (!d->widget)
+ return;
+ d->updateWidgetPosition();
+}
+
+
+/*!
+ \property QScrollArea::widgetResizable
+ \brief whether the scroll area should resize the view widget
+
+ If this property is set to false (the default), the scroll area
+ honors the size of its widget. Regardless of this property, you
+ can programmatically resize the widget using widget()->resize(),
+ and the scroll area will automatically adjust itself to the new
+ size.
+
+ If this property is set to true, the scroll area will
+ automatically resize the widget in order to avoid scroll bars
+ where they can be avoided, or to take advantage of extra space.
+*/
+bool QScrollArea::widgetResizable() const
+{
+ Q_D(const QScrollArea);
+ return d->resizable;
+}
+
+void QScrollArea::setWidgetResizable(bool resizable)
+{
+ Q_D(QScrollArea);
+ d->resizable = resizable;
+ updateGeometry();
+ d->updateScrollBars();
+}
+
+/*!
+ \reimp
+ */
+QSize QScrollArea::sizeHint() const
+{
+ Q_D(const QScrollArea);
+ int f = 2 * d->frameWidth;
+ QSize sz(f, f);
+ int h = fontMetrics().height();
+ if (d->widget) {
+ if (!d->widgetSize.isValid())
+ d->widgetSize = d->resizable ? d->widget->sizeHint() : d->widget->size();
+ sz += d->widgetSize;
+ } else {
+ sz += QSize(12 * h, 8 * h);
+ }
+ if (d->vbarpolicy == Qt::ScrollBarAlwaysOn)
+ sz.setWidth(sz.width() + d->vbar->sizeHint().width());
+ if (d->hbarpolicy == Qt::ScrollBarAlwaysOn)
+ sz.setHeight(sz.height() + d->hbar->sizeHint().height());
+ return sz.boundedTo(QSize(36 * h, 24 * h));
+}
+
+
+
+/*!
+ \reimp
+ */
+bool QScrollArea::focusNextPrevChild(bool next)
+{
+ if (QWidget::focusNextPrevChild(next)) {
+ if (QWidget *fw = focusWidget())
+ ensureWidgetVisible(fw);
+ return true;
+ }
+ return false;
+}
+
+/*!
+ Scrolls the contents of the scroll area so that the point (\a x, \a y) is visible
+ inside the region of the viewport with margins specified in pixels by \a xmargin and
+ \a ymargin. If the specified point cannot be reached, the contents are scrolled to
+ the nearest valid position. The default value for both margins is 50 pixels.
+*/
+void QScrollArea::ensureVisible(int x, int y, int xmargin, int ymargin)
+{
+ Q_D(QScrollArea);
+
+ int logicalX = QStyle::visualPos(layoutDirection(), d->viewport->rect(), QPoint(x, y)).x();
+
+ if (logicalX - xmargin < d->hbar->value()) {
+ d->hbar->setValue(qMax(0, logicalX - xmargin));
+ } else if (logicalX > d->hbar->value() + d->viewport->width() - xmargin) {
+ d->hbar->setValue(qMin(logicalX - d->viewport->width() + xmargin, d->hbar->maximum()));
+ }
+
+ if (y - ymargin < d->vbar->value()) {
+ d->vbar->setValue(qMax(0, y - ymargin));
+ } else if (y > d->vbar->value() + d->viewport->height() - ymargin) {
+ d->vbar->setValue(qMin(y - d->viewport->height() + ymargin, d->vbar->maximum()));
+ }
+}
+
+/*!
+ \since 4.2
+
+ Scrolls the contents of the scroll area so that the \a childWidget
+ of QScrollArea::widget() is visible inside the viewport with
+ margins specified in pixels by \a xmargin and \a ymargin. If the
+ specified point cannot be reached, the contents are scrolled to
+ the nearest valid position. The default value for both margins is
+ 50 pixels.
+
+*/
+void QScrollArea::ensureWidgetVisible(QWidget *childWidget, int xmargin, int ymargin)
+{
+ Q_D(QScrollArea);
+
+ if (!d->widget->isAncestorOf(childWidget))
+ return;
+
+ const QRect microFocus = childWidget->inputMethodQuery(Qt::ImMicroFocus).toRect();
+ const QRect defaultMicroFocus =
+ childWidget->QWidget::inputMethodQuery(Qt::ImMicroFocus).toRect();
+ QRect focusRect = (microFocus != defaultMicroFocus)
+ ? QRect(childWidget->mapTo(d->widget, microFocus.topLeft()), microFocus.size())
+ : QRect(childWidget->mapTo(d->widget, QPoint(0,0)), childWidget->size());
+ const QRect visibleRect(-d->widget->pos(), d->viewport->size());
+
+ if (visibleRect.contains(focusRect))
+ return;
+
+ focusRect.adjust(-xmargin, -ymargin, xmargin, ymargin);
+
+ if (focusRect.width() > visibleRect.width())
+ d->hbar->setValue(focusRect.center().x() - d->viewport->width() / 2);
+ else if (focusRect.right() > visibleRect.right())
+ d->hbar->setValue(focusRect.right() - d->viewport->width());
+ else
+ d->hbar->setValue(focusRect.left());
+
+ if (focusRect.height() > visibleRect.height())
+ d->vbar->setValue(focusRect.center().y() - d->viewport->height() / 2);
+ else if (focusRect.bottom() > visibleRect.bottom())
+ d->vbar->setValue(focusRect.bottom() - d->viewport->height());
+ else
+ d->vbar->setValue(focusRect.top());
+}
+
+
+/*!
+ \property QScrollArea::alignment
+ \brief the alignment of the scroll area's widget
+ \since 4.2
+
+ By default, the widget stays rooted to the top-left corner of the
+ scroll area.
+*/
+
+void QScrollArea::setAlignment(Qt::Alignment alignment)
+{
+ Q_D(QScrollArea);
+ d->alignment = alignment;
+ if (d->widget)
+ d->updateWidgetPosition();
+}
+
+Qt::Alignment QScrollArea::alignment() const
+{
+ Q_D(const QScrollArea);
+ return d->alignment;
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_SCROLLAREA
diff --git a/src/gui/widgets/qscrollarea.h b/src/gui/widgets/qscrollarea.h
new file mode 100644
index 0000000000..f888aefdce
--- /dev/null
+++ b/src/gui/widgets/qscrollarea.h
@@ -0,0 +1,101 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSCROLLAREA_H
+#define QSCROLLAREA_H
+
+#include <QtGui/qabstractscrollarea.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+#ifndef QT_NO_SCROLLAREA
+
+class QScrollAreaPrivate;
+
+class Q_GUI_EXPORT QScrollArea : public QAbstractScrollArea
+{
+ Q_OBJECT
+ Q_PROPERTY(bool widgetResizable READ widgetResizable WRITE setWidgetResizable)
+ Q_PROPERTY(Qt::Alignment alignment READ alignment WRITE setAlignment)
+
+public:
+ explicit QScrollArea(QWidget* parent=0);
+ ~QScrollArea();
+
+ QWidget *widget() const;
+ void setWidget(QWidget *widget);
+ QWidget *takeWidget();
+
+ bool widgetResizable() const;
+ void setWidgetResizable(bool resizable);
+
+ QSize sizeHint() const;
+ bool focusNextPrevChild(bool next);
+
+ Qt::Alignment alignment() const;
+ void setAlignment(Qt::Alignment);
+
+ void ensureVisible(int x, int y, int xmargin = 50, int ymargin = 50);
+ void ensureWidgetVisible(QWidget *childWidget, int xmargin = 50, int ymargin = 50);
+
+protected:
+ QScrollArea(QScrollAreaPrivate &dd, QWidget *parent = 0);
+ bool event(QEvent *);
+ bool eventFilter(QObject *, QEvent *);
+ void resizeEvent(QResizeEvent *);
+ void scrollContentsBy(int dx, int dy);
+
+private:
+ Q_DECLARE_PRIVATE(QScrollArea)
+ Q_DISABLE_COPY(QScrollArea)
+};
+
+#endif // QT_NO_SCROLLAREA
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QSCROLLAREA_H
diff --git a/src/gui/widgets/qscrollarea_p.h b/src/gui/widgets/qscrollarea_p.h
new file mode 100644
index 0000000000..bdb7ad77ba
--- /dev/null
+++ b/src/gui/widgets/qscrollarea_p.h
@@ -0,0 +1,81 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSCROLLAREA_P_H
+#define QSCROLLAREA_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_SCROLLAREA
+
+#include "private/qabstractscrollarea_p.h"
+#include <QtGui/qscrollbar.h>
+
+QT_BEGIN_NAMESPACE
+
+class QScrollAreaPrivate: public QAbstractScrollAreaPrivate
+{
+ Q_DECLARE_PUBLIC(QScrollArea)
+
+public:
+ QScrollAreaPrivate(): resizable(false), alignment(0){}
+ void updateScrollBars();
+ void updateWidgetPosition();
+ QPointer<QWidget> widget;
+ mutable QSize widgetSize;
+ bool resizable;
+ Qt::Alignment alignment;
+};
+
+#endif
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/gui/widgets/qscrollbar.cpp b/src/gui/widgets/qscrollbar.cpp
new file mode 100644
index 0000000000..9bfe7a52e9
--- /dev/null
+++ b/src/gui/widgets/qscrollbar.cpp
@@ -0,0 +1,740 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qapplication.h"
+#include "qcursor.h"
+#include "qevent.h"
+#include "qpainter.h"
+#include "qscrollbar.h"
+#include "qstyle.h"
+#include "qstyleoption.h"
+#include "qmenu.h"
+#include <QtCore/qdatetime.h>
+
+#ifndef QT_NO_SCROLLBAR
+
+#ifndef QT_NO_ACCESSIBILITY
+#include "qaccessible.h"
+#endif
+#include <limits.h>
+#include "qabstractslider_p.h"
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QScrollBar
+ \brief The QScrollBar widget provides a vertical or horizontal scroll bar.
+
+ \ingroup basicwidgets
+
+ A scroll bar is a control that enables the user to access parts of a
+ document that is larger than the widget used to display it. It provides
+ a visual indication of the user's current position within the document
+ and the amount of the document that is visible. Scroll bars are usually
+ equipped with other controls that enable more accurate navigation.
+ Qt displays scroll bars in a way that is appropriate for each platform.
+
+ If you need to provide a scrolling view onto another widget, it may be
+ more convenient to use the QScrollArea class because this provides a
+ viewport widget and scroll bars. QScrollBar is useful if you need to
+ implement similar functionality for specialized widgets using QAbstractScrollArea;
+ for example, if you decide to subclass QAbstractItemView.
+ For most other situations where a slider control is used to obtain a value
+ within a given range, the QSlider class may be more appropriate for your
+ needs.
+
+ \table
+ \row \i \image qscrollbar-picture.png
+ \i Scroll bars typically include four separate controls: a slider,
+ scroll arrows, and a page control.
+
+ \list
+ \i a. The slider provides a way to quickly go to any part of the
+ document, but does not support accurate navigation within large
+ documents.
+ \i b. The scroll arrows are push buttons which can be used to accurately
+ navigate to a particular place in a document. For a vertical scroll bar
+ connected to a text editor, these typically move the current position one
+ "line" up or down, and adjust the position of the slider by a small
+ amount. In editors and list boxes a "line" might mean one line of text;
+ in an image viewer it might mean 20 pixels.
+ \i c. The page control is the area over which the slider is dragged (the
+ scroll bar's background). Clicking here moves the scroll bar towards
+ the click by one "page". This value is usually the same as the length of
+ the slider.
+ \endlist
+ \endtable
+
+ Each scroll bar has a value that indicates how far the slider is from
+ the start of the scroll bar; this is obtained with value() and set
+ with setValue(). This value always lies within the range of values
+ defined for the scroll bar, from \l{QAbstractSlider::minimum()}{minimum()}
+ to \l{QAbstractSlider::minimum()}{maximum()} inclusive. The range of
+ acceptable values can be set with setMinimum() and setMaximum().
+ At the minimum value, the top edge of the slider (for a vertical scroll
+ bar) or left edge (for a horizontal scroll bar) will be at the top (or
+ left) end of the scroll bar. At the maximum value, the bottom (or right)
+ edge of the slider will be at the bottom (or right) end of the scroll bar.
+
+ The length of the slider is usually related to the value of the page step,
+ and typically represents the proportion of the document area shown in a
+ scrolling view. The page step is the amount that the value changes by
+ when the user presses the \key{Page Up} and \key{Page Down} keys, and is
+ set with setPageStep(). Smaller changes to the value defined by the
+ line step are made using the cursor keys, and this quantity is set with
+ \l{QAbstractSlider::}{setSingleStep()}.
+
+ Note that the range of values used is independent of the actual size
+ of the scroll bar widget. You do not need to take this into account when
+ you choose values for the range and the page step.
+
+ The range of values specified for the scroll bar are often determined
+ differently to those for a QSlider because the length of the slider
+ needs to be taken into account. If we have a document with 100 lines,
+ and we can only show 20 lines in a widget, we may wish to construct a
+ scroll bar with a page step of 20, a minimum value of 0, and a maximum
+ value of 80. This would give us a scroll bar with five "pages".
+
+ \table
+ \row \i \inlineimage qscrollbar-values.png
+ \i The relationship between a document length, the range of values used
+ in a scroll bar, and the page step is simple in many common situations.
+ The scroll bar's range of values is determined by subtracting a
+ chosen page step from some value representing the length of the document.
+ In such cases, the following equation is useful:
+
+ \e{document length} = maximum() - minimum() + pageStep().
+ \endtable
+
+ QScrollBar only provides integer ranges. Note that although
+ QScrollBar handles very large numbers, scroll bars on current
+ screens cannot usefully represent ranges above about 100,000 pixels.
+ Beyond that, it becomes difficult for the user to control the
+ slider using either the keyboard or the mouse, and the scroll
+ arrows will have limited use.
+
+ ScrollBar inherits a comprehensive set of signals from QAbstractSlider:
+ \list
+ \i \l{QAbstractSlider::valueChanged()}{valueChanged()} is emitted when the
+ scroll bar's value has changed. The tracking() determines whether this
+ signal is emitted during user interaction.
+ \i \l{QAbstractSlider::rangeChanged()}{rangeChanged()} is emitted when the
+ scroll bar's range of values has changed.
+ \i \l{QAbstractSlider::sliderPressed()}{sliderPressed()} is emitted when
+ the user starts to drag the slider.
+ \i \l{QAbstractSlider::sliderMoved()}{sliderMoved()} is emitted when the user
+ drags the slider.
+ \i \l{QAbstractSlider::sliderReleased()}{sliderReleased()} is emitted when
+ the user releases the slider.
+ \i \l{QAbstractSlider::actionTriggered()}{actionTriggered()} is emitted
+ when the scroll bar is changed by user interaction or via the
+ \l{QAbstractSlider::triggerAction()}{triggerAction()} function.
+ \endlist
+
+ A scroll bar can be controlled by the keyboard, but it has a
+ default focusPolicy() of Qt::NoFocus. Use setFocusPolicy() to
+ enable keyboard interaction with the scroll bar:
+ \list
+ \i Left/Right move a horizontal scroll bar by one single step.
+ \i Up/Down move a vertical scroll bar by one single step.
+ \i PageUp moves up one page.
+ \i PageDown moves down one page.
+ \i Home moves to the start (mininum).
+ \i End moves to the end (maximum).
+ \endlist
+
+ The slider itself can be controlled by using the
+ \l{QAbstractSlider::triggerAction()}{triggerAction()} function to simulate
+ user interaction with the scroll bar controls. This is useful if you have
+ many different widgets that use a common range of values.
+
+ Most GUI styles use the pageStep() value to calculate the size of the
+ slider.
+
+ \table 100%
+ \row \o \inlineimage macintosh-horizontalscrollbar.png Screenshot of a Macintosh style scroll bar
+ \o A scroll bar shown in the \l{Macintosh Style Widget Gallery}{Macintosh widget style}.
+ \row \o \inlineimage windowsxp-horizontalscrollbar.png Screenshot of a Windows XP style scroll bar
+ \o A scroll bar shown in the \l{Windows XP Style Widget Gallery}{Windows XP widget style}.
+ \row \o \inlineimage plastique-horizontalscrollbar.png Screenshot of a Plastique style scroll bar
+ \o A scroll bar shown in the \l{Plastique Style Widget Gallery}{Plastique widget style}.
+ \endtable
+
+ \sa QScrollArea, QSlider, QDial, QSpinBox, {fowler}{GUI Design Handbook: Scroll Bar}, {Sliders Example}
+*/
+
+class QScrollBarPrivate : public QAbstractSliderPrivate
+{
+ Q_DECLARE_PUBLIC(QScrollBar)
+public:
+ QStyle::SubControl pressedControl;
+ bool pointerOutsidePressedControl;
+
+ int clickOffset, snapBackPosition;
+
+ void activateControl(uint control, int threshold = 500);
+ void stopRepeatAction();
+ int pixelPosToRangeValue(int pos) const;
+ void init();
+ bool updateHoverControl(const QPoint &pos);
+ QStyle::SubControl newHoverControl(const QPoint &pos);
+
+ QStyle::SubControl hoverControl;
+ QRect hoverRect;
+};
+
+bool QScrollBarPrivate::updateHoverControl(const QPoint &pos)
+{
+ Q_Q(QScrollBar);
+ QRect lastHoverRect = hoverRect;
+ QStyle::SubControl lastHoverControl = hoverControl;
+ bool doesHover = q->testAttribute(Qt::WA_Hover);
+ if (lastHoverControl != newHoverControl(pos) && doesHover) {
+ q->update(lastHoverRect);
+ q->update(hoverRect);
+ return true;
+ }
+ return !doesHover;
+}
+
+QStyle::SubControl QScrollBarPrivate::newHoverControl(const QPoint &pos)
+{
+ Q_Q(QScrollBar);
+ QStyleOptionSlider opt;
+ q->initStyleOption(&opt);
+ opt.subControls = QStyle::SC_All;
+ hoverControl = q->style()->hitTestComplexControl(QStyle::CC_ScrollBar, &opt, pos, q);
+ if (hoverControl == QStyle::SC_None)
+ hoverRect = QRect();
+ else
+ hoverRect = q->style()->subControlRect(QStyle::CC_ScrollBar, &opt, hoverControl, q);
+ return hoverControl;
+}
+
+void QScrollBarPrivate::activateControl(uint control, int threshold)
+{
+ QAbstractSlider::SliderAction action = QAbstractSlider::SliderNoAction;
+ switch (control) {
+ case QStyle::SC_ScrollBarAddPage:
+ action = QAbstractSlider::SliderPageStepAdd;
+ break;
+ case QStyle::SC_ScrollBarSubPage:
+ action = QAbstractSlider::SliderPageStepSub;
+ break;
+ case QStyle::SC_ScrollBarAddLine:
+ action = QAbstractSlider::SliderSingleStepAdd;
+ break;
+ case QStyle::SC_ScrollBarSubLine:
+ action = QAbstractSlider::SliderSingleStepSub;
+ break;
+ case QStyle::SC_ScrollBarFirst:
+ action = QAbstractSlider::SliderToMinimum;
+ break;
+ case QStyle::SC_ScrollBarLast:
+ action = QAbstractSlider::SliderToMaximum;
+ break;
+ default:
+ break;
+ }
+
+ if (action) {
+ q_func()->setRepeatAction(action, threshold);
+ q_func()->triggerAction(action);
+ }
+}
+
+void QScrollBarPrivate::stopRepeatAction()
+{
+ Q_Q(QScrollBar);
+ QStyle::SubControl tmp = pressedControl;
+ q->setRepeatAction(QAbstractSlider::SliderNoAction);
+ pressedControl = QStyle::SC_None;
+
+ if (tmp == QStyle::SC_ScrollBarSlider)
+ q->setSliderDown(false);
+
+ QStyleOptionSlider opt;
+ q->initStyleOption(&opt);
+ q->repaint(q->style()->subControlRect(QStyle::CC_ScrollBar, &opt, tmp, q));
+}
+
+/*!
+ Initialize \a option with the values from this QScrollBar. This method
+ is useful for subclasses when they need a QStyleOptionSlider, but don't want
+ to fill in all the information themselves.
+
+ \sa QStyleOption::initFrom()
+*/
+void QScrollBar::initStyleOption(QStyleOptionSlider *option) const
+{
+ if (!option)
+ return;
+
+ Q_D(const QScrollBar);
+ option->initFrom(this);
+ option->subControls = QStyle::SC_None;
+ option->activeSubControls = QStyle::SC_None;
+ option->orientation = d->orientation;
+ option->minimum = d->minimum;
+ option->maximum = d->maximum;
+ option->sliderPosition = d->position;
+ option->sliderValue = d->value;
+ option->singleStep = d->singleStep;
+ option->pageStep = d->pageStep;
+ option->upsideDown = d->invertedAppearance;
+ if (d->orientation == Qt::Horizontal)
+ option->state |= QStyle::State_Horizontal;
+}
+
+
+#define HORIZONTAL (d_func()->orientation == Qt::Horizontal)
+#define VERTICAL !HORIZONTAL
+
+/*!
+ Constructs a vertical scroll bar.
+
+ The \a parent arguments is sent to the QWidget constructor.
+
+ The \l {QAbstractSlider::minimum} {minimum} defaults to 0, the
+ \l {QAbstractSlider::maximum} {maximum} to 99, with a
+ \l {QAbstractSlider::singleStep} {singleStep} size of 1 and a
+ \l {QAbstractSlider::pageStep} {pageStep} size of 10, and an
+ initial \l {QAbstractSlider::value} {value} of 0.
+*/
+QScrollBar::QScrollBar(QWidget *parent)
+ : QAbstractSlider(*new QScrollBarPrivate, parent)
+{
+ d_func()->orientation = Qt::Vertical;
+ d_func()->init();
+}
+
+/*!
+ Constructs a scroll bar with the given \a orientation.
+
+ The \a parent argument is passed to the QWidget constructor.
+
+ The \l {QAbstractSlider::minimum} {minimum} defaults to 0, the
+ \l {QAbstractSlider::maximum} {maximum} to 99, with a
+ \l {QAbstractSlider::singleStep} {singleStep} size of 1 and a
+ \l {QAbstractSlider::pageStep} {pageStep} size of 10, and an
+ initial \l {QAbstractSlider::value} {value} of 0.
+*/
+QScrollBar::QScrollBar(Qt::Orientation orientation, QWidget *parent)
+ : QAbstractSlider(*new QScrollBarPrivate, parent)
+{
+ d_func()->orientation = orientation;
+ d_func()->init();
+}
+
+
+#ifdef QT3_SUPPORT
+/*!
+ Use one of the constructors that doesn't take the \a name
+ argument and then use setObjectName() instead.
+*/
+QScrollBar::QScrollBar(QWidget *parent, const char *name)
+ : QAbstractSlider(*new QScrollBarPrivate, parent)
+{
+ setObjectName(QString::fromAscii(name));
+ d_func()->orientation = Qt::Vertical;
+ d_func()->init();
+}
+
+/*!
+ Use one of the constructors that doesn't take the \a name
+ argument and then use setObjectName() instead.
+*/
+QScrollBar::QScrollBar(Qt::Orientation orientation, QWidget *parent, const char *name)
+ : QAbstractSlider(*new QScrollBarPrivate, parent)
+{
+ setObjectName(QString::fromAscii(name));
+ d_func()->orientation = orientation;
+ d_func()->init();
+}
+
+/*!
+ Use one of the constructors that doesn't take the \a name
+ argument and then use setObjectName() instead.
+*/
+QScrollBar::QScrollBar(int minimum, int maximum, int lineStep, int pageStep,
+ int value, Qt::Orientation orientation,
+ QWidget *parent, const char *name)
+ : QAbstractSlider(*new QScrollBarPrivate, parent)
+{
+ Q_D(QScrollBar);
+ setObjectName(QString::fromAscii(name));
+ d->minimum = minimum;
+ d->maximum = maximum;
+ d->singleStep = lineStep;
+ d->pageStep = pageStep;
+ d->value = value;
+ d->orientation = orientation;
+ d->init();
+}
+#endif // QT3_SUPPORT
+
+/*!
+ Destroys the scroll bar.
+*/
+QScrollBar::~QScrollBar()
+{
+}
+
+void QScrollBarPrivate::init()
+{
+ Q_Q(QScrollBar);
+ invertedControls = true;
+ pressedControl = hoverControl = QStyle::SC_None;
+ pointerOutsidePressedControl = false;
+ q->setFocusPolicy(Qt::NoFocus);
+ QSizePolicy sp(QSizePolicy::Minimum, QSizePolicy::Fixed, QSizePolicy::Slider);
+ if (orientation == Qt::Vertical)
+ sp.transpose();
+ q->setSizePolicy(sp);
+ q->setAttribute(Qt::WA_WState_OwnSizePolicy, false);
+ q->setAttribute(Qt::WA_OpaquePaintEvent);
+}
+
+#ifndef QT_NO_CONTEXTMENU
+/*! \reimp */
+void QScrollBar::contextMenuEvent(QContextMenuEvent *event)
+{
+ if (!style()->styleHint(QStyle::SH_ScrollBar_ContextMenu, 0, this)) {
+ QAbstractSlider::contextMenuEvent(event);
+ return ;
+ }
+
+#ifndef QT_NO_MENU
+ bool horiz = HORIZONTAL;
+ QPointer<QMenu> menu = new QMenu(this);
+ QAction *actScrollHere = menu->addAction(tr("Scroll here"));
+ menu->addSeparator();
+ QAction *actScrollTop = menu->addAction(horiz ? tr("Left edge") : tr("Top"));
+ QAction *actScrollBottom = menu->addAction(horiz ? tr("Right edge") : tr("Bottom"));
+ menu->addSeparator();
+ QAction *actPageUp = menu->addAction(horiz ? tr("Page left") : tr("Page up"));
+ QAction *actPageDn = menu->addAction(horiz ? tr("Page right") : tr("Page down"));
+ menu->addSeparator();
+ QAction *actScrollUp = menu->addAction(horiz ? tr("Scroll left") : tr("Scroll up"));
+ QAction *actScrollDn = menu->addAction(horiz ? tr("Scroll right") : tr("Scroll down"));
+ QAction *actionSelected = menu->exec(event->globalPos());
+ delete menu;
+ if (actionSelected == 0)
+ /* do nothing */ ;
+ else if (actionSelected == actScrollHere)
+ setValue(d_func()->pixelPosToRangeValue(horiz ? event->pos().x() : event->pos().y()));
+ else if (actionSelected == actScrollTop)
+ triggerAction(QAbstractSlider::SliderToMinimum);
+ else if (actionSelected == actScrollBottom)
+ triggerAction(QAbstractSlider::SliderToMaximum);
+ else if (actionSelected == actPageUp)
+ triggerAction(QAbstractSlider::SliderPageStepSub);
+ else if (actionSelected == actPageDn)
+ triggerAction(QAbstractSlider::SliderPageStepAdd);
+ else if (actionSelected == actScrollUp)
+ triggerAction(QAbstractSlider::SliderSingleStepSub);
+ else if (actionSelected == actScrollDn)
+ triggerAction(QAbstractSlider::SliderSingleStepAdd);
+#endif // QT_NO_MENU
+}
+#endif // QT_NO_CONTEXTMENU
+
+
+/*! \reimp */
+QSize QScrollBar::sizeHint() const
+{
+ ensurePolished();
+ QStyleOptionSlider opt;
+ initStyleOption(&opt);
+
+ int scrollBarExtent = style()->pixelMetric(QStyle::PM_ScrollBarExtent, &opt, this);
+ int scrollBarSliderMin = style()->pixelMetric(QStyle::PM_ScrollBarSliderMin, &opt, this);
+ QSize size;
+ if (opt.orientation == Qt::Horizontal)
+ size = QSize(scrollBarExtent * 2 + scrollBarSliderMin, scrollBarExtent);
+ else
+ size = QSize(scrollBarExtent, scrollBarExtent * 2 + scrollBarSliderMin);
+
+ return style()->sizeFromContents(QStyle::CT_ScrollBar, &opt, size, this)
+ .expandedTo(QApplication::globalStrut());
+ }
+
+/*!\reimp */
+void QScrollBar::sliderChange(SliderChange change)
+{
+ QAbstractSlider::sliderChange(change);
+}
+
+/*!
+ \reimp
+*/
+bool QScrollBar::event(QEvent *event)
+{
+ switch(event->type()) {
+ case QEvent::HoverEnter:
+ case QEvent::HoverLeave:
+ case QEvent::HoverMove:
+ if (const QHoverEvent *he = static_cast<const QHoverEvent *>(event))
+ d_func()->updateHoverControl(he->pos());
+ break;
+ default:
+ break;
+ }
+ return QAbstractSlider::event(event);
+}
+
+/*!
+ \reimp
+*/
+void QScrollBar::paintEvent(QPaintEvent *)
+{
+ Q_D(QScrollBar);
+ QPainter p(this);
+ QStyleOptionSlider opt;
+ initStyleOption(&opt);
+ opt.subControls = QStyle::SC_All;
+ if (d->pressedControl) {
+ opt.activeSubControls = (QStyle::SubControl)d->pressedControl;
+ if (!d->pointerOutsidePressedControl)
+ opt.state |= QStyle::State_Sunken;
+ } else {
+ opt.activeSubControls = (QStyle::SubControl)d->hoverControl;
+ }
+ style()->drawComplexControl(QStyle::CC_ScrollBar, &opt, &p, this);
+}
+
+/*!
+ \reimp
+*/
+void QScrollBar::mousePressEvent(QMouseEvent *e)
+{
+ Q_D(QScrollBar);
+
+ if (d->repeatActionTimer.isActive())
+ d->stopRepeatAction();
+
+ bool midButtonAbsPos = style()->styleHint(QStyle::SH_ScrollBar_MiddleClickAbsolutePosition,
+ 0, this);
+ QStyleOptionSlider opt;
+ initStyleOption(&opt);
+
+ if (d->maximum == d->minimum // no range
+ || (e->buttons() & (~e->button())) // another button was clicked before
+ || !(e->button() == Qt::LeftButton || (midButtonAbsPos && e->button() == Qt::MidButton)))
+ return;
+
+ d->pressedControl = style()->hitTestComplexControl(QStyle::CC_ScrollBar, &opt, e->pos(), this);
+ d->pointerOutsidePressedControl = false;
+
+ QRect sr = style()->subControlRect(QStyle::CC_ScrollBar, &opt,
+ QStyle::SC_ScrollBarSlider, this);
+ QPoint click = e->pos();
+ QPoint pressValue = click - sr.center() + sr.topLeft();
+ d->pressValue = d->orientation == Qt::Horizontal ? d->pixelPosToRangeValue(pressValue.x()) :
+ d->pixelPosToRangeValue(pressValue.y());
+ if (d->pressedControl == QStyle::SC_ScrollBarSlider) {
+ d->clickOffset = HORIZONTAL ? (click.x()-sr.x()) : (click.y()-sr.y());
+ d->snapBackPosition = d->position;
+ }
+
+ if ((d->pressedControl == QStyle::SC_ScrollBarAddPage
+ || d->pressedControl == QStyle::SC_ScrollBarSubPage)
+ && ((midButtonAbsPos && e->button() == Qt::MidButton)
+ || (style()->styleHint(QStyle::SH_ScrollBar_LeftClickAbsolutePosition, &opt, this)
+ && e->button() == Qt::LeftButton))) {
+ int sliderLength = HORIZONTAL ? sr.width() : sr.height();
+ setSliderPosition(d->pixelPosToRangeValue((HORIZONTAL ? e->pos().x()
+ : e->pos().y()) - sliderLength / 2));
+ d->pressedControl = QStyle::SC_ScrollBarSlider;
+ d->clickOffset = sliderLength / 2;
+ }
+ const int initialDelay = 500; // default threshold
+ d->activateControl(d->pressedControl, initialDelay);
+ QTime time;
+ time.start();
+ repaint(style()->subControlRect(QStyle::CC_ScrollBar, &opt, d->pressedControl, this));
+ if (time.elapsed() >= initialDelay && d->repeatActionTimer.isActive()) {
+ // It took more than 500ms (the initial timer delay) to process the repaint(), we
+ // therefore need to restart the timer in case we have a pending mouse release event;
+ // otherwise we'll get a timer event right before the release event,
+ // causing the repeat action to be invoked twice on a single mouse click.
+ // 50ms is the default repeat time (see activateControl/setRepeatAction).
+ d->repeatActionTimer.start(50, this);
+ }
+ if (d->pressedControl == QStyle::SC_ScrollBarSlider)
+ setSliderDown(true);
+}
+
+
+/*!
+ \reimp
+*/
+void QScrollBar::mouseReleaseEvent(QMouseEvent *e)
+{
+ Q_D(QScrollBar);
+ if (!d->pressedControl)
+ return;
+
+ if (e->buttons() & (~e->button())) // some other button is still pressed
+ return;
+
+ d->stopRepeatAction();
+}
+
+
+/*!
+ \reimp
+*/
+void QScrollBar::mouseMoveEvent(QMouseEvent *e)
+{
+ Q_D(QScrollBar);
+ if (!d->pressedControl)
+ return;
+
+ QStyleOptionSlider opt;
+ initStyleOption(&opt);
+ if (!(e->buttons() & Qt::LeftButton
+ || ((e->buttons() & Qt::MidButton)
+ && style()->styleHint(QStyle::SH_ScrollBar_MiddleClickAbsolutePosition, &opt, this))))
+ return;
+
+ if (d->pressedControl == QStyle::SC_ScrollBarSlider) {
+ QPoint click = e->pos();
+ int newPosition = d->pixelPosToRangeValue((HORIZONTAL ? click.x() : click.y()) -d->clickOffset);
+ int m = style()->pixelMetric(QStyle::PM_MaximumDragDistance, &opt, this);
+ if (m >= 0) {
+ QRect r = rect();
+ r.adjust(-m, -m, m, m);
+ if (! r.contains(e->pos()))
+ newPosition = d->snapBackPosition;
+ }
+ setSliderPosition(newPosition);
+ } else if (!style()->styleHint(QStyle::SH_ScrollBar_ScrollWhenPointerLeavesControl, &opt, this)) {
+
+ if (style()->styleHint(QStyle::SH_ScrollBar_RollBetweenButtons, &opt, this)
+ && d->pressedControl & (QStyle::SC_ScrollBarAddLine | QStyle::SC_ScrollBarSubLine)) {
+ QStyle::SubControl newSc = style()->hitTestComplexControl(QStyle::CC_ScrollBar, &opt, e->pos(), this);
+ if (newSc == d->pressedControl && !d->pointerOutsidePressedControl)
+ return; // nothing to do
+ if (newSc & (QStyle::SC_ScrollBarAddLine | QStyle::SC_ScrollBarSubLine)) {
+ d->pointerOutsidePressedControl = false;
+ QRect scRect = style()->subControlRect(QStyle::CC_ScrollBar, &opt, newSc, this);
+ scRect |= style()->subControlRect(QStyle::CC_ScrollBar, &opt, d->pressedControl, this);
+ d->pressedControl = newSc;
+ d->activateControl(d->pressedControl, 0);
+ update(scRect);
+ return;
+ }
+ }
+
+ // stop scrolling when the mouse pointer leaves a control
+ // similar to push buttons
+ QRect pr = style()->subControlRect(QStyle::CC_ScrollBar, &opt, d->pressedControl, this);
+ if (pr.contains(e->pos()) == d->pointerOutsidePressedControl) {
+ if ((d->pointerOutsidePressedControl = !d->pointerOutsidePressedControl)) {
+ d->pointerOutsidePressedControl = true;
+ setRepeatAction(SliderNoAction);
+ repaint(pr);
+ } else {
+ d->activateControl(d->pressedControl);
+ }
+ }
+ }
+}
+
+
+int QScrollBarPrivate::pixelPosToRangeValue(int pos) const
+{
+ Q_Q(const QScrollBar);
+ QStyleOptionSlider opt;
+ q->initStyleOption(&opt);
+ QRect gr = q->style()->subControlRect(QStyle::CC_ScrollBar, &opt,
+ QStyle::SC_ScrollBarGroove, q);
+ QRect sr = q->style()->subControlRect(QStyle::CC_ScrollBar, &opt,
+ QStyle::SC_ScrollBarSlider, q);
+ int sliderMin, sliderMax, sliderLength;
+
+ if (orientation == Qt::Horizontal) {
+ sliderLength = sr.width();
+ sliderMin = gr.x();
+ sliderMax = gr.right() - sliderLength + 1;
+ if (q->layoutDirection() == Qt::RightToLeft)
+ opt.upsideDown = !opt.upsideDown;
+ } else {
+ sliderLength = sr.height();
+ sliderMin = gr.y();
+ sliderMax = gr.bottom() - sliderLength + 1;
+ }
+
+ return QStyle::sliderValueFromPosition(minimum, maximum, pos - sliderMin,
+ sliderMax - sliderMin, opt.upsideDown);
+}
+
+/*! \reimp
+*/
+void QScrollBar::hideEvent(QHideEvent *)
+{
+ Q_D(QScrollBar);
+ if (d->pressedControl) {
+ d->pressedControl = QStyle::SC_None;
+ setRepeatAction(SliderNoAction);
+ }
+}
+
+/*!
+ \fn bool QScrollBar::draggingSlider()
+
+ Use isSliderDown() instead.
+*/
+
+/*! \internal
+ Returns the style option for scroll bar.
+*/
+Q_GUI_EXPORT QStyleOptionSlider qt_qscrollbarStyleOption(QScrollBar *scrollbar)
+{
+ QStyleOptionSlider opt;
+ scrollbar->initStyleOption(&opt);
+ return opt;
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_SCROLLBAR
diff --git a/src/gui/widgets/qscrollbar.h b/src/gui/widgets/qscrollbar.h
new file mode 100644
index 0000000000..35aacf4620
--- /dev/null
+++ b/src/gui/widgets/qscrollbar.h
@@ -0,0 +1,104 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSCROLLBAR_H
+#define QSCROLLBAR_H
+
+#include <QtGui/qwidget.h>
+#include <QtGui/qabstractslider.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+#ifndef QT_NO_SCROLLBAR
+
+class QScrollBarPrivate;
+class QStyleOptionSlider;
+
+class Q_GUI_EXPORT QScrollBar : public QAbstractSlider
+{
+ Q_OBJECT
+public:
+ explicit QScrollBar(QWidget *parent=0);
+ explicit QScrollBar(Qt::Orientation, QWidget *parent=0);
+ ~QScrollBar();
+
+ QSize sizeHint() const;
+ bool event(QEvent *event);
+
+protected:
+ void paintEvent(QPaintEvent *);
+ void mousePressEvent(QMouseEvent *);
+ void mouseReleaseEvent(QMouseEvent *);
+ void mouseMoveEvent(QMouseEvent *);
+ void hideEvent(QHideEvent*);
+ void sliderChange(SliderChange change);
+#ifndef QT_NO_CONTEXTMENU
+ void contextMenuEvent(QContextMenuEvent *);
+#endif
+ void initStyleOption(QStyleOptionSlider *option) const;
+
+#ifdef QT3_SUPPORT
+public:
+ QT3_SUPPORT_CONSTRUCTOR QScrollBar(QWidget *parent, const char* name);
+ QT3_SUPPORT_CONSTRUCTOR QScrollBar(Qt::Orientation, QWidget *parent, const char* name);
+ QT3_SUPPORT_CONSTRUCTOR QScrollBar(int minValue, int maxValue, int lineStep, int pageStep,
+ int value, Qt::Orientation, QWidget *parent=0, const char* name = 0);
+ inline QT3_SUPPORT bool draggingSlider() { return isSliderDown(); }
+#endif
+
+private:
+ friend Q_GUI_EXPORT QStyleOptionSlider qt_qscrollbarStyleOption(QScrollBar *scrollBar);
+
+ Q_DISABLE_COPY(QScrollBar)
+ Q_DECLARE_PRIVATE(QScrollBar)
+};
+
+#endif // QT_NO_SCROLLBAR
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QSCROLLBAR_H
diff --git a/src/gui/widgets/qsizegrip.cpp b/src/gui/widgets/qsizegrip.cpp
new file mode 100644
index 0000000000..6458b15beb
--- /dev/null
+++ b/src/gui/widgets/qsizegrip.cpp
@@ -0,0 +1,566 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qsizegrip.h"
+
+#ifndef QT_NO_SIZEGRIP
+
+#include "qapplication.h"
+#include "qevent.h"
+#include "qpainter.h"
+#include "qstyle.h"
+#include "qstyleoption.h"
+#include "qlayout.h"
+#include "qdebug.h"
+#include <QDesktopWidget>
+
+#if defined(Q_WS_X11)
+#include <private/qt_x11_p.h>
+#elif defined (Q_WS_WIN)
+#include "qt_windows.h"
+#endif
+#ifdef Q_WS_MAC
+#include <private/qt_mac_p.h>
+#endif
+
+#include <private/qwidget_p.h>
+#include <QtGui/qabstractscrollarea.h>
+
+#define SZ_SIZEBOTTOMRIGHT 0xf008
+#define SZ_SIZEBOTTOMLEFT 0xf007
+#define SZ_SIZETOPLEFT 0xf004
+#define SZ_SIZETOPRIGHT 0xf005
+
+QT_BEGIN_NAMESPACE
+
+static QWidget *qt_sizegrip_topLevelWidget(QWidget* w)
+{
+ while (w && !w->isWindow() && w->windowType() != Qt::SubWindow)
+ w = w->parentWidget();
+ return w;
+}
+
+static inline bool hasHeightForWidth(QWidget *widget)
+{
+ if (!widget)
+ return false;
+ if (QLayout *layout = widget->layout())
+ return layout->hasHeightForWidth();
+ return widget->sizePolicy().hasHeightForWidth();
+}
+
+class QSizeGripPrivate : public QWidgetPrivate
+{
+ Q_DECLARE_PUBLIC(QSizeGrip)
+public:
+ void init();
+ QPoint p;
+ QRect r;
+ int d;
+ int dxMax;
+ int dyMax;
+ Qt::Corner m_corner;
+ bool gotMousePress;
+#ifdef Q_WS_MAC
+ void updateMacSizer(bool hide) const;
+#endif
+ Qt::Corner corner() const;
+ inline bool atBottom() const
+ {
+ return m_corner == Qt::BottomRightCorner || m_corner == Qt::BottomLeftCorner;
+ }
+
+ inline bool atLeft() const
+ {
+ return m_corner == Qt::BottomLeftCorner || m_corner == Qt::TopLeftCorner;
+ }
+
+ // This slot is invoked by QLayout when the size grip is added to
+ // a layout or reparented after the tlw is shown. This re-implementation is basically
+ // the same as QWidgetPrivate::_q_showIfNotHidden except that it checks
+ // for Qt::WindowFullScreen and Qt::WindowMaximized as well.
+ void _q_showIfNotHidden()
+ {
+ Q_Q(QSizeGrip);
+ bool showSizeGrip = !(q->isHidden() && q->testAttribute(Qt::WA_WState_ExplicitShowHide));
+ QWidget *tlw = qt_sizegrip_topLevelWidget(q);
+ if (tlw && showSizeGrip) {
+ Qt::WindowStates sizeGripNotVisibleState = Qt::WindowFullScreen;
+#ifndef Q_WS_MAC
+ sizeGripNotVisibleState |= Qt::WindowMaximized;
+#endif
+ // Don't show the size grip if the tlw is maximized or in full screen mode.
+ showSizeGrip = !(tlw->windowState() & sizeGripNotVisibleState);
+ }
+ if (showSizeGrip)
+ q->setVisible(true);
+ }
+};
+
+#ifdef Q_WS_MAC
+void QSizeGripPrivate::updateMacSizer(bool hide) const
+{
+ Q_Q(const QSizeGrip);
+ if (QApplication::closingDown() || !q->parentWidget())
+ return;
+ QWidget *topLevelWindow = qt_sizegrip_topLevelWidget(const_cast<QSizeGrip *>(q));
+ if(topLevelWindow && topLevelWindow->isWindow())
+ QWidgetPrivate::qt_mac_update_sizer(topLevelWindow, hide ? -1 : 1);
+}
+#endif
+
+Qt::Corner QSizeGripPrivate::corner() const
+{
+ Q_Q(const QSizeGrip);
+ QWidget *tlw = qt_sizegrip_topLevelWidget(const_cast<QSizeGrip *>(q));
+ const QPoint sizeGripPos = q->mapTo(tlw, QPoint(0, 0));
+ bool isAtBottom = sizeGripPos.y() >= tlw->height() / 2;
+ bool isAtLeft = sizeGripPos.x() <= tlw->width() / 2;
+ if (isAtLeft)
+ return isAtBottom ? Qt::BottomLeftCorner : Qt::TopLeftCorner;
+ else
+ return isAtBottom ? Qt::BottomRightCorner : Qt::TopRightCorner;
+}
+
+/*!
+ \class QSizeGrip
+
+ \brief The QSizeGrip class provides a resize handle for resizing top-level windows.
+
+ \ingroup application
+ \ingroup basicwidgets
+ \ingroup appearance
+
+ This widget works like the standard Windows resize handle. In the
+ X11 version this resize handle generally works differently from
+ the one provided by the system if the X11 window manager does not
+ support necessary modern post-ICCCM specifications.
+
+ Put this widget anywhere in a widget tree and the user can use it
+ to resize the top-level window or any widget with the Qt::SubWindow
+ flag set. Generally, this should be in the lower right-hand corner.
+ Note that QStatusBar already uses this widget, so if you have a
+ status bar (e.g., you are using QMainWindow), then you don't need
+ to use this widget explicitly.
+
+ On some platforms the size grip automatically hides itself when the
+ window is shown full screen or maximised.
+
+ \table 50%
+ \row \o \inlineimage plastique-sizegrip.png Screenshot of a Plastique style size grip
+ \o A size grip widget at the bottom-right corner of a main window, shown in the
+ \l{Plastique Style Widget Gallery}{Plastique widget style}.
+ \endtable
+
+ The QSizeGrip class inherits QWidget and reimplements the \l
+ {QWidget::mousePressEvent()}{mousePressEvent()} and \l
+ {QWidget::mouseMoveEvent()}{mouseMoveEvent()} functions to feature
+ the resize functionality, and the \l
+ {QWidget::paintEvent()}{paintEvent()} function to render the
+ size grip widget.
+
+ \sa QStatusBar QWidget::windowState()
+*/
+
+
+/*!
+ Constructs a resize corner as a child widget of the given \a
+ parent.
+*/
+QSizeGrip::QSizeGrip(QWidget * parent)
+ : QWidget(*new QSizeGripPrivate, parent, 0)
+{
+ Q_D(QSizeGrip);
+ d->init();
+}
+
+#ifdef QT3_SUPPORT
+/*!
+ \obsolete
+
+ Constructs a resize corner with the given \a name, as a child
+ widget of the given \a parent.
+*/
+QSizeGrip::QSizeGrip(QWidget * parent, const char* name)
+ : QWidget(*new QSizeGripPrivate, parent, 0)
+{
+ Q_D(QSizeGrip);
+ setObjectName(QString::fromAscii(name));
+ d->init();
+}
+#endif
+
+void QSizeGripPrivate::init()
+{
+ Q_Q(QSizeGrip);
+ dxMax = 0;
+ dyMax = 0;
+ m_corner = q->isLeftToRight() ? Qt::BottomRightCorner : Qt::BottomLeftCorner;
+ gotMousePress = false;
+
+#if !defined(QT_NO_CURSOR) && !defined(Q_WS_MAC)
+ q->setCursor(m_corner == Qt::TopLeftCorner || m_corner == Qt::BottomRightCorner
+ ? Qt::SizeFDiagCursor : Qt::SizeBDiagCursor);
+#endif
+ q->setSizePolicy(QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed));
+ QWidget *tlw = qt_sizegrip_topLevelWidget(q);
+ tlw->installEventFilter(q);
+}
+
+
+/*!
+ Destroys this size grip.
+*/
+QSizeGrip::~QSizeGrip()
+{
+}
+
+/*!
+ \reimp
+*/
+QSize QSizeGrip::sizeHint() const
+{
+ QStyleOption opt(0);
+ opt.init(this);
+ return (style()->sizeFromContents(QStyle::CT_SizeGrip, &opt, QSize(13, 13), this).
+ expandedTo(QApplication::globalStrut()));
+}
+
+/*!
+ Paints the resize grip.
+
+ Resize grips are usually rendered as small diagonal textured lines
+ in the lower-right corner. The paint event is passed in the \a
+ event parameter.
+*/
+void QSizeGrip::paintEvent(QPaintEvent *event)
+{
+ Q_UNUSED(event);
+ Q_D(QSizeGrip);
+ QPainter painter(this);
+ QStyleOptionSizeGrip opt;
+ opt.init(this);
+ opt.corner = d->m_corner;
+ style()->drawControl(QStyle::CE_SizeGrip, &opt, &painter, this);
+}
+
+/*!
+ \fn void QSizeGrip::mousePressEvent(QMouseEvent * event)
+
+ Receives the mouse press events for the widget, and primes the
+ resize operation. The mouse press event is passed in the \a event
+ parameter.
+*/
+void QSizeGrip::mousePressEvent(QMouseEvent * e)
+{
+ if (e->button() != Qt::LeftButton) {
+ QWidget::mousePressEvent(e);
+ return;
+ }
+
+ Q_D(QSizeGrip);
+ QWidget *tlw = qt_sizegrip_topLevelWidget(this);
+ d->p = e->globalPos();
+ d->gotMousePress = true;
+ d->r = tlw->geometry();
+
+#ifdef Q_WS_X11
+ // Use a native X11 sizegrip for "real" top-level windows if supported.
+ if (tlw->isWindow() && X11->isSupportedByWM(ATOM(_NET_WM_MOVERESIZE))
+ && !tlw->testAttribute(Qt::WA_DontShowOnScreen) && !hasHeightForWidth(tlw)) {
+ XEvent xev;
+ xev.xclient.type = ClientMessage;
+ xev.xclient.message_type = ATOM(_NET_WM_MOVERESIZE);
+ xev.xclient.display = X11->display;
+ xev.xclient.window = tlw->winId();
+ xev.xclient.format = 32;
+ xev.xclient.data.l[0] = e->globalPos().x();
+ xev.xclient.data.l[1] = e->globalPos().y();
+ if (d->atBottom())
+ xev.xclient.data.l[2] = d->atLeft() ? 6 : 4; // bottomleft/bottomright
+ else
+ xev.xclient.data.l[2] = d->atLeft() ? 0 : 2; // topleft/topright
+ xev.xclient.data.l[3] = Button1;
+ xev.xclient.data.l[4] = 0;
+ XUngrabPointer(X11->display, X11->time);
+ XSendEvent(X11->display, QX11Info::appRootWindow(x11Info().screen()), False,
+ SubstructureRedirectMask | SubstructureNotifyMask, &xev);
+ return;
+ }
+#endif // Q_WS_X11
+#ifdef Q_WS_WIN
+ if (tlw->isWindow() && !tlw->testAttribute(Qt::WA_DontShowOnScreen) && !hasHeightForWidth(tlw)) {
+ uint orientation = 0;
+ if (d->atBottom())
+ orientation = d->atLeft() ? SZ_SIZEBOTTOMLEFT : SZ_SIZEBOTTOMRIGHT;
+ else
+ orientation = d->atLeft() ? SZ_SIZETOPLEFT : SZ_SIZETOPRIGHT;
+
+ ReleaseCapture();
+ QT_WA_INLINE(PostMessageW(tlw->winId(), WM_SYSCOMMAND, orientation, 0),
+ PostMessageA(tlw->winId(), WM_SYSCOMMAND, orientation, 0));
+ return;
+ }
+#endif // Q_WS_WIN
+
+ // Find available desktop/workspace geometry.
+ QRect availableGeometry;
+ bool hasVerticalSizeConstraint = true;
+ bool hasHorizontalSizeConstraint = true;
+ if (tlw->isWindow())
+ availableGeometry = QApplication::desktop()->availableGeometry(tlw);
+ else {
+ const QWidget *tlwParent = tlw->parentWidget();
+ // Check if tlw is inside QAbstractScrollArea/QScrollArea.
+ // If that's the case tlw->parentWidget() will return the viewport
+ // and tlw->parentWidget()->parentWidget() will return the scroll area.
+#ifndef QT_NO_SCROLLAREA
+ QAbstractScrollArea *scrollArea = qobject_cast<QAbstractScrollArea *>(tlwParent->parentWidget());
+ if (scrollArea) {
+ hasHorizontalSizeConstraint = scrollArea->horizontalScrollBarPolicy() == Qt::ScrollBarAlwaysOff;
+ hasVerticalSizeConstraint = scrollArea->verticalScrollBarPolicy() == Qt::ScrollBarAlwaysOff;
+ }
+#endif // QT_NO_SCROLLAREA
+ availableGeometry = tlwParent->contentsRect();
+ }
+
+ // Find frame geometries, title bar height, and decoration sizes.
+ const QRect frameGeometry = tlw->frameGeometry();
+ const int titleBarHeight = qMax(tlw->geometry().y() - frameGeometry.y(), 0);
+ const int bottomDecoration = qMax(frameGeometry.height() - tlw->height() - titleBarHeight, 0);
+ const int leftRightDecoration = qMax((frameGeometry.width() - tlw->width()) / 2, 0);
+
+ // Determine dyMax depending on whether the sizegrip is at the bottom
+ // of the widget or not.
+ if (d->atBottom()) {
+ if (hasVerticalSizeConstraint)
+ d->dyMax = availableGeometry.bottom() - d->r.bottom() - bottomDecoration;
+ else
+ d->dyMax = INT_MAX;
+ } else {
+ if (hasVerticalSizeConstraint)
+ d->dyMax = availableGeometry.y() - d->r.y() + titleBarHeight;
+ else
+ d->dyMax = -INT_MAX;
+ }
+
+ // In RTL mode, the size grip is to the left; find dxMax from the desktop/workspace
+ // geometry, the size grip geometry and the width of the decoration.
+ if (d->atLeft()) {
+ if (hasHorizontalSizeConstraint)
+ d->dxMax = availableGeometry.x() - d->r.x() + leftRightDecoration;
+ else
+ d->dxMax = -INT_MAX;
+ } else {
+ if (hasHorizontalSizeConstraint)
+ d->dxMax = availableGeometry.right() - d->r.right() - leftRightDecoration;
+ else
+ d->dxMax = INT_MAX;
+ }
+}
+
+
+/*!
+ \fn void QSizeGrip::mouseMoveEvent(QMouseEvent * event)
+ Resizes the top-level widget containing this widget. The mouse
+ move event is passed in the \a event parameter.
+*/
+void QSizeGrip::mouseMoveEvent(QMouseEvent * e)
+{
+ if (e->buttons() != Qt::LeftButton) {
+ QWidget::mouseMoveEvent(e);
+ return;
+ }
+
+ Q_D(QSizeGrip);
+ QWidget* tlw = qt_sizegrip_topLevelWidget(this);
+ if (!d->gotMousePress || tlw->testAttribute(Qt::WA_WState_ConfigPending))
+ return;
+
+#ifdef Q_WS_X11
+ if (tlw->isWindow() && X11->isSupportedByWM(ATOM(_NET_WM_MOVERESIZE))
+ && tlw->isTopLevel() && !tlw->testAttribute(Qt::WA_DontShowOnScreen) && !hasHeightForWidth(tlw))
+ return;
+#endif
+#ifdef Q_WS_WIN
+ if (tlw->isWindow() && GetSystemMenu(tlw->winId(), FALSE) != 0 && internalWinId()
+ && !tlw->testAttribute(Qt::WA_DontShowOnScreen) && !hasHeightForWidth(tlw)) {
+ MSG msg;
+ while(PeekMessage(&msg, winId(), WM_MOUSEMOVE, WM_MOUSEMOVE, PM_REMOVE));
+ return;
+ }
+#endif
+
+ QPoint np(e->globalPos());
+
+ // Don't extend beyond the available geometry; bound to dyMax and dxMax.
+ QSize ns;
+ if (d->atBottom())
+ ns.rheight() = d->r.height() + qMin(np.y() - d->p.y(), d->dyMax);
+ else
+ ns.rheight() = d->r.height() - qMax(np.y() - d->p.y(), d->dyMax);
+
+ if (d->atLeft())
+ ns.rwidth() = d->r.width() - qMax(np.x() - d->p.x(), d->dxMax);
+ else
+ ns.rwidth() = d->r.width() + qMin(np.x() - d->p.x(), d->dxMax);
+
+ ns = QLayout::closestAcceptableSize(tlw, ns);
+
+ QPoint p;
+ QRect nr(p, ns);
+ if (d->atBottom()) {
+ if (d->atLeft())
+ nr.moveTopRight(d->r.topRight());
+ else
+ nr.moveTopLeft(d->r.topLeft());
+ } else {
+ if (d->atLeft())
+ nr.moveBottomRight(d->r.bottomRight());
+ else
+ nr.moveBottomLeft(d->r.bottomLeft());
+ }
+
+ tlw->setGeometry(nr);
+}
+
+/*!
+ \reimp
+*/
+void QSizeGrip::mouseReleaseEvent(QMouseEvent *mouseEvent)
+{
+ if (mouseEvent->button() == Qt::LeftButton) {
+ Q_D(QSizeGrip);
+ d->gotMousePress = false;
+ d->p = QPoint();
+ } else {
+ QWidget::mouseReleaseEvent(mouseEvent);
+ }
+}
+
+/*!
+ \reimp
+*/
+void QSizeGrip::moveEvent(QMoveEvent * /*moveEvent*/)
+{
+ Q_D(QSizeGrip);
+ // We're inside a resize operation; no update necessary.
+ if (!d->p.isNull())
+ return;
+
+ d->m_corner = d->corner();
+#if !defined(QT_NO_CURSOR) && !defined(Q_WS_MAC)
+ setCursor(d->m_corner == Qt::TopLeftCorner || d->m_corner == Qt::BottomRightCorner
+ ? Qt::SizeFDiagCursor : Qt::SizeBDiagCursor);
+#endif
+}
+
+/*!
+ \reimp
+*/
+void QSizeGrip::showEvent(QShowEvent *showEvent)
+{
+#ifdef Q_WS_MAC
+ d_func()->updateMacSizer(false);
+#endif
+ QWidget::showEvent(showEvent);
+}
+
+/*!
+ \reimp
+*/
+void QSizeGrip::hideEvent(QHideEvent *hideEvent)
+{
+#ifdef Q_WS_MAC
+ d_func()->updateMacSizer(true);
+#endif
+ QWidget::hideEvent(hideEvent);
+}
+
+/*!
+ \reimp
+*/
+void QSizeGrip::setVisible(bool visible)
+{
+ QWidget::setVisible(visible);
+}
+
+/*! \reimp */
+bool QSizeGrip::eventFilter(QObject *o, QEvent *e)
+{
+ if ((isHidden() && testAttribute(Qt::WA_WState_ExplicitShowHide))
+ || e->type() != QEvent::WindowStateChange) {
+ return QWidget::eventFilter(o, e);
+ }
+ QWidget *tlw = qt_sizegrip_topLevelWidget(this);
+ if (o != tlw)
+ return QWidget::eventFilter(o, e);
+ Qt::WindowStates sizeGripNotVisibleState = Qt::WindowFullScreen;
+#ifndef Q_WS_MAC
+ sizeGripNotVisibleState |= Qt::WindowMaximized;
+#endif
+ // Don't show the size grip if the tlw is maximized or in full screen mode.
+ setVisible(!(tlw->windowState() & sizeGripNotVisibleState));
+ setAttribute(Qt::WA_WState_ExplicitShowHide, false);
+ return QWidget::eventFilter(o, e);
+}
+
+/*!
+ \reimp
+*/
+bool QSizeGrip::event(QEvent *event)
+{
+ return QWidget::event(event);
+}
+
+#ifdef Q_WS_WIN
+/*! \reimp */
+bool QSizeGrip::winEvent( MSG *m, long *result )
+{
+ return QWidget::winEvent(m, result);
+}
+#endif
+
+QT_END_NAMESPACE
+
+#include "moc_qsizegrip.cpp"
+
+#endif //QT_NO_SIZEGRIP
diff --git a/src/gui/widgets/qsizegrip.h b/src/gui/widgets/qsizegrip.h
new file mode 100644
index 0000000000..cfd83a8543
--- /dev/null
+++ b/src/gui/widgets/qsizegrip.h
@@ -0,0 +1,95 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSIZEGRIP_H
+#define QSIZEGRIP_H
+
+#include <QtGui/qwidget.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+#ifndef QT_NO_SIZEGRIP
+class QSizeGripPrivate;
+class Q_GUI_EXPORT QSizeGrip : public QWidget
+{
+ Q_OBJECT
+public:
+ explicit QSizeGrip(QWidget *parent);
+ ~QSizeGrip();
+
+ QSize sizeHint() const;
+ void setVisible(bool);
+
+protected:
+ void paintEvent(QPaintEvent *);
+ void mousePressEvent(QMouseEvent *);
+ void mouseMoveEvent(QMouseEvent *);
+ void mouseReleaseEvent(QMouseEvent *mouseEvent);
+ void moveEvent(QMoveEvent *moveEvent);
+ void showEvent(QShowEvent *showEvent);
+ void hideEvent(QHideEvent *hideEvent);
+ bool eventFilter(QObject *, QEvent *);
+ bool event(QEvent *);
+#ifdef Q_WS_WIN
+ bool winEvent(MSG *m, long *result);
+#endif
+
+public:
+#ifdef QT3_SUPPORT
+ QT3_SUPPORT_CONSTRUCTOR QSizeGrip(QWidget *parent, const char *name);
+#endif
+
+private:
+ Q_DECLARE_PRIVATE(QSizeGrip)
+ Q_DISABLE_COPY(QSizeGrip)
+ Q_PRIVATE_SLOT(d_func(), void _q_showIfNotHidden())
+};
+#endif // QT_NO_SIZEGRIP
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QSIZEGRIP_H
diff --git a/src/gui/widgets/qslider.cpp b/src/gui/widgets/qslider.cpp
new file mode 100644
index 0000000000..32b90216c6
--- /dev/null
+++ b/src/gui/widgets/qslider.cpp
@@ -0,0 +1,676 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qslider.h"
+#ifndef QT_NO_SLIDER
+#ifndef QT_NO_ACCESSIBILITY
+#include "qaccessible.h"
+#endif
+#include "qapplication.h"
+#include "qevent.h"
+#include "qpainter.h"
+#include "qstyle.h"
+#include "qstyleoption.h"
+#include "private/qabstractslider_p.h"
+#include "qdebug.h"
+
+QT_BEGIN_NAMESPACE
+
+class QSliderPrivate : public QAbstractSliderPrivate
+{
+ Q_DECLARE_PUBLIC(QSlider)
+public:
+ QStyle::SubControl pressedControl;
+ int tickInterval;
+ QSlider::TickPosition tickPosition;
+ int clickOffset;
+ int snapBackPosition;
+ void init();
+ void resetLayoutItemMargins();
+ int pixelPosToRangeValue(int pos) const;
+ inline int pick(const QPoint &pt) const;
+
+ QStyle::SubControl newHoverControl(const QPoint &pos);
+ bool updateHoverControl(const QPoint &pos);
+ QStyle::SubControl hoverControl;
+ QRect hoverRect;
+};
+
+void QSliderPrivate::init()
+{
+ Q_Q(QSlider);
+ pressedControl = QStyle::SC_None;
+ tickInterval = 0;
+ tickPosition = QSlider::NoTicks;
+ hoverControl = QStyle::SC_None;
+ q->setFocusPolicy(Qt::FocusPolicy(q->style()->styleHint(QStyle::SH_Button_FocusPolicy)));
+ QSizePolicy sp(QSizePolicy::Expanding, QSizePolicy::Fixed, QSizePolicy::Slider);
+ if (orientation == Qt::Vertical)
+ sp.transpose();
+ q->setSizePolicy(sp);
+ q->setAttribute(Qt::WA_WState_OwnSizePolicy, false);
+ resetLayoutItemMargins();
+}
+
+void QSliderPrivate::resetLayoutItemMargins()
+{
+ Q_Q(QSlider);
+ QStyleOptionSlider opt;
+ q->initStyleOption(&opt);
+ setLayoutItemMargins(QStyle::SE_SliderLayoutItem, &opt);
+}
+
+int QSliderPrivate::pixelPosToRangeValue(int pos) const
+{
+ Q_Q(const QSlider);
+ QStyleOptionSlider opt;
+ q->initStyleOption(&opt);
+ QRect gr = q->style()->subControlRect(QStyle::CC_Slider, &opt, QStyle::SC_SliderGroove, q);
+ QRect sr = q->style()->subControlRect(QStyle::CC_Slider, &opt, QStyle::SC_SliderHandle, q);
+ int sliderMin, sliderMax, sliderLength;
+
+ if (orientation == Qt::Horizontal) {
+ sliderLength = sr.width();
+ sliderMin = gr.x();
+ sliderMax = gr.right() - sliderLength + 1;
+ } else {
+ sliderLength = sr.height();
+ sliderMin = gr.y();
+ sliderMax = gr.bottom() - sliderLength + 1;
+ }
+ return QStyle::sliderValueFromPosition(minimum, maximum, pos - sliderMin,
+ sliderMax - sliderMin, opt.upsideDown);
+}
+
+inline int QSliderPrivate::pick(const QPoint &pt) const
+{
+ return orientation == Qt::Horizontal ? pt.x() : pt.y();
+}
+
+/*!
+ Initialize \a option with the values from this QSlider. This method
+ is useful for subclasses when they need a QStyleOptionSlider, but don't want
+ to fill in all the information themselves.
+
+ \sa QStyleOption::initFrom()
+*/
+void QSlider::initStyleOption(QStyleOptionSlider *option) const
+{
+ if (!option)
+ return;
+
+ Q_D(const QSlider);
+ option->initFrom(this);
+ option->subControls = QStyle::SC_None;
+ option->activeSubControls = QStyle::SC_None;
+ option->orientation = d->orientation;
+ option->maximum = d->maximum;
+ option->minimum = d->minimum;
+ option->tickPosition = (QSlider::TickPosition)d->tickPosition;
+ option->tickInterval = d->tickInterval;
+ option->upsideDown = (d->orientation == Qt::Horizontal) ?
+ (d->invertedAppearance != (option->direction == Qt::RightToLeft))
+ : (!d->invertedAppearance);
+ option->direction = Qt::LeftToRight; // we use the upsideDown option instead
+ option->sliderPosition = d->position;
+ option->sliderValue = d->value;
+ option->singleStep = d->singleStep;
+ option->pageStep = d->pageStep;
+ if (d->orientation == Qt::Horizontal)
+ option->state |= QStyle::State_Horizontal;
+}
+
+bool QSliderPrivate::updateHoverControl(const QPoint &pos)
+{
+ Q_Q(QSlider);
+ QRect lastHoverRect = hoverRect;
+ QStyle::SubControl lastHoverControl = hoverControl;
+ bool doesHover = q->testAttribute(Qt::WA_Hover);
+ if (lastHoverControl != newHoverControl(pos) && doesHover) {
+ q->update(lastHoverRect);
+ q->update(hoverRect);
+ return true;
+ }
+ return !doesHover;
+}
+
+QStyle::SubControl QSliderPrivate::newHoverControl(const QPoint &pos)
+{
+ Q_Q(QSlider);
+ QStyleOptionSlider opt;
+ q->initStyleOption(&opt);
+ opt.subControls = QStyle::SC_All;
+ QRect handleRect = q->style()->subControlRect(QStyle::CC_Slider, &opt, QStyle::SC_SliderHandle, q);
+ QRect grooveRect = q->style()->subControlRect(QStyle::CC_Slider, &opt, QStyle::SC_SliderGroove, q);
+ QRect tickmarksRect = q->style()->subControlRect(QStyle::CC_Slider, &opt, QStyle::SC_SliderTickmarks, q);
+
+ if (handleRect.contains(pos)) {
+ hoverRect = handleRect;
+ hoverControl = QStyle::SC_SliderHandle;
+ } else if (grooveRect.contains(pos)) {
+ hoverRect = grooveRect;
+ hoverControl = QStyle::SC_SliderGroove;
+ } else if (tickmarksRect.contains(pos)) {
+ hoverRect = tickmarksRect;
+ hoverControl = QStyle::SC_SliderTickmarks;
+ } else {
+ hoverRect = QRect();
+ hoverControl = QStyle::SC_None;
+ }
+
+ return hoverControl;
+}
+
+/*!
+ \class QSlider
+ \brief The QSlider widget provides a vertical or horizontal slider.
+
+ \ingroup basicwidgets
+ \mainclass
+
+ The slider is the classic widget for controlling a bounded value.
+ It lets the user move a slider handle along a horizontal or vertical
+ groove and translates the handle's position into an integer value
+ within the legal range.
+
+ QSlider has very few of its own functions; most of the functionality is in
+ QAbstractSlider. The most useful functions are setValue() to set
+ the slider directly to some value; triggerAction() to simulate
+ the effects of clicking (useful for shortcut keys);
+ setSingleStep(), setPageStep() to set the steps; and setMinimum()
+ and setMaximum() to define the range of the scroll bar.
+
+ QSlider provides methods for controlling tickmarks. You can use
+ setTickPosition() to indicate where you want the tickmarks to be,
+ setTickInterval() to indicate how many of them you want. the
+ currently set tick position and interval can be queried using the
+ tickPosition() and tickInterval() functions, respectively.
+
+ QSlider inherits a comprehensive set of signals:
+ \table
+ \header \o Signal \o Description
+ \row \o \l valueChanged()
+ \o Emitted when the slider's value has changed. The tracking()
+ determines whether this signal is emitted during user
+ interaction.
+ \row \o \l sliderPressed()
+ \o Emitted when the user starts to drag the slider.
+ \row \o \l sliderMoved()
+ \o Emitted when the user drags the slider.
+ \row \o \l sliderReleased()
+ \o Emitted when the user releases the slider.
+ \endtable
+
+ QSlider only provides integer ranges. Note that although
+ QSlider handles very large numbers, it becomes difficult for users
+ to use a slider accurately for very large ranges.
+
+ A slider accepts focus on Tab and provides both a mouse wheel and a
+ keyboard interface. The keyboard interface is the following:
+
+ \list
+ \o Left/Right move a horizontal slider by one single step.
+ \o Up/Down move a vertical slider by one single step.
+ \o PageUp moves up one page.
+ \o PageDown moves down one page.
+ \o Home moves to the start (mininum).
+ \o End moves to the end (maximum).
+ \endlist
+
+ \table 100%
+ \row \o \inlineimage macintosh-slider.png Screenshot of a Macintosh slider
+ \o A slider shown in the \l{Macintosh Style Widget Gallery}{Macintosh widget style}.
+ \row \o \inlineimage windows-slider.png Screenshot of a Windows XP slider
+ \o A slider shown in the \l{Windows XP Style Widget Gallery}{Windows XP widget style}.
+ \row \o \inlineimage plastique-slider.png Screenshot of a Plastique slider
+ \o A slider shown in the \l{Plastique Style Widget Gallery}{Plastique widget style}.
+ \endtable
+
+ \sa QScrollBar, QSpinBox, QDial, {fowler}{GUI Design Handbook: Slider}, {Sliders Example}
+*/
+
+
+/*!
+ \enum QSlider::TickPosition
+
+ This enum specifies where the tick marks are to be drawn relative
+ to the slider's groove and the handle the user moves.
+
+ \value NoTicks Do not draw any tick marks.
+ \value TicksBothSides Draw tick marks on both sides of the groove.
+ \value TicksAbove Draw tick marks above the (horizontal) slider
+ \value TicksBelow Draw tick marks below the (horizontal) slider
+ \value TicksLeft Draw tick marks to the left of the (vertical) slider
+ \value TicksRight Draw tick marks to the right of the (vertical) slider
+
+ \omitvalue NoMarks
+ \omitvalue Above
+ \omitvalue Left
+ \omitvalue Below
+ \omitvalue Right
+ \omitvalue Both
+*/
+
+
+/*!
+ Constructs a vertical slider with the given \a parent.
+*/
+QSlider::QSlider(QWidget *parent)
+ : QAbstractSlider(*new QSliderPrivate, parent)
+{
+ d_func()->orientation = Qt::Vertical;
+ d_func()->init();
+}
+
+/*!
+ Constructs a slider with the given \a parent. The \a orientation
+ parameter determines whether the slider is horizontal or vertical;
+ the valid values are Qt::Vertical and Qt::Horizontal.
+*/
+
+QSlider::QSlider(Qt::Orientation orientation, QWidget *parent)
+ : QAbstractSlider(*new QSliderPrivate, parent)
+{
+ d_func()->orientation = orientation;
+ d_func()->init();
+}
+
+#ifdef QT3_SUPPORT
+/*!
+ Use QSlider() and QObject::setObjectName() instead.
+
+ \oldcode
+ QSlider *mySlider = new QSlider(parent, name);
+ \newcode
+ QSlider *mySlider = new QSlider(parent);
+ mySlider->setObjectName(name);
+ \endcode
+*/
+QSlider::QSlider(QWidget *parent, const char *name)
+ : QAbstractSlider(*new QSliderPrivate, parent)
+{
+ setObjectName(QString::fromAscii(name));
+ d_func()->orientation = Qt::Vertical;
+ d_func()->init();
+}
+
+/*!
+ Use QSlider() and QObject::setObjectName() instead.
+
+ \oldcode
+ QSlider *mySlider = new QSlider(orientation, parent, name);
+ \newcode
+ QSlider *mySlider = new QSlider(orientation, parent);
+ mySlider->setObjectName(name);
+ \endcode
+*/
+QSlider::QSlider(Qt::Orientation orientation, QWidget *parent, const char *name)
+ : QAbstractSlider(*new QSliderPrivate, parent)
+{
+ setObjectName(QString::fromAscii(name));
+ d_func()->orientation = orientation;
+ d_func()->init();
+}
+
+/*!
+ Use QSlider(), QObject::setObjectName() and the functionality
+ inherited from QAbstractSlider instead.
+
+ \oldcode
+ QSlider *mySlider = new QSlider(minValue, maxValue, pageStep,
+ value, orientation, parent, name);
+ \newcode
+ QSlider *mySlider = new QSlider(orientation, parent);
+ mySlider->setObjectName(name);
+ mySlider->setMinimum(minValue);
+ mySlider->setMaximum(maxValue);
+ mySlider->setPageStep(pageStep);
+ mySlider->setValue(value);
+ \endcode
+*/
+QSlider::QSlider(int minValue, int maxValue, int pageStep, int value, Qt::Orientation orientation,
+ QWidget *parent, const char *name)
+ : QAbstractSlider(*new QSliderPrivate, parent)
+{
+ Q_D(QSlider);
+ setObjectName(QString::fromAscii(name));
+ d->minimum = minValue;
+ d->maximum = maxValue;
+ d->pageStep = pageStep;
+ d->position = d->value = value;
+ d->orientation = orientation;
+ d->init();
+}
+#endif
+
+/*!
+ Destroys this slider.
+*/
+QSlider::~QSlider()
+{
+}
+
+/*!
+ \reimp
+*/
+void QSlider::paintEvent(QPaintEvent *)
+{
+ Q_D(QSlider);
+ QPainter p(this);
+ QStyleOptionSlider opt;
+ initStyleOption(&opt);
+
+ opt.subControls = QStyle::SC_SliderGroove | QStyle::SC_SliderHandle;
+ if (d->tickPosition != NoTicks)
+ opt.subControls |= QStyle::SC_SliderTickmarks;
+ if (d->pressedControl) {
+ opt.activeSubControls = d->pressedControl;
+ opt.state |= QStyle::State_Sunken;
+ } else {
+ opt.activeSubControls = d->hoverControl;
+ }
+
+ style()->drawComplexControl(QStyle::CC_Slider, &opt, &p, this);
+}
+
+/*!
+ \reimp
+*/
+
+bool QSlider::event(QEvent *event)
+{
+ Q_D(QSlider);
+
+ switch(event->type()) {
+ case QEvent::HoverEnter:
+ case QEvent::HoverLeave:
+ case QEvent::HoverMove:
+ if (const QHoverEvent *he = static_cast<const QHoverEvent *>(event))
+ d->updateHoverControl(he->pos());
+ break;
+ case QEvent::StyleChange:
+ case QEvent::MacSizeChange:
+ d->resetLayoutItemMargins();
+ break;
+ default:
+ break;
+ }
+ return QAbstractSlider::event(event);
+}
+
+/*!
+ \reimp
+*/
+void QSlider::mousePressEvent(QMouseEvent *ev)
+{
+ Q_D(QSlider);
+ if (d->maximum == d->minimum || (ev->buttons() ^ ev->button())) {
+ ev->ignore();
+ return;
+ }
+#ifdef QT_KEYPAD_NAVIGATION
+ if (QApplication::keypadNavigationEnabled())
+ setEditFocus(true);
+#endif
+ ev->accept();
+ if ((ev->button() & style()->styleHint(QStyle::SH_Slider_AbsoluteSetButtons)) == ev->button()) {
+ QStyleOptionSlider opt;
+ initStyleOption(&opt);
+ const QRect sliderRect = style()->subControlRect(QStyle::CC_Slider, &opt, QStyle::SC_SliderHandle, this);
+ const QPoint center = sliderRect.center() - sliderRect.topLeft();
+ // to take half of the slider off for the setSliderPosition call we use the center - topLeft
+
+ setSliderPosition(d->pixelPosToRangeValue(d->pick(ev->pos() - center)));
+ triggerAction(SliderMove);
+ setRepeatAction(SliderNoAction);
+ d->pressedControl = QStyle::SC_SliderHandle;
+ update();
+ } else if ((ev->button() & style()->styleHint(QStyle::SH_Slider_PageSetButtons)) == ev->button()) {
+ QStyleOptionSlider opt;
+ initStyleOption(&opt);
+ d->pressedControl = style()->hitTestComplexControl(QStyle::CC_Slider,
+ &opt, ev->pos(), this);
+ SliderAction action = SliderNoAction;
+ if (d->pressedControl == QStyle::SC_SliderGroove) {
+ const QRect sliderRect = style()->subControlRect(QStyle::CC_Slider, &opt, QStyle::SC_SliderHandle, this);
+ int pressValue = d->pixelPosToRangeValue(d->pick(ev->pos() - sliderRect.center() + sliderRect.topLeft()));
+ d->pressValue = pressValue;
+ if (pressValue > d->value)
+ action = SliderPageStepAdd;
+ else if (pressValue < d->value)
+ action = SliderPageStepSub;
+ if (action) {
+ triggerAction(action);
+ setRepeatAction(action);
+ }
+ }
+ } else {
+ ev->ignore();
+ return;
+ }
+
+ if (d->pressedControl == QStyle::SC_SliderHandle) {
+ QStyleOptionSlider opt;
+ initStyleOption(&opt);
+ setRepeatAction(SliderNoAction);
+ QRect sr = style()->subControlRect(QStyle::CC_Slider, &opt, QStyle::SC_SliderHandle, this);
+ d->clickOffset = d->pick(ev->pos() - sr.topLeft());
+ d->snapBackPosition = d->position;
+ update(sr);
+ setSliderDown(true);
+ }
+}
+
+/*!
+ \reimp
+*/
+void QSlider::mouseMoveEvent(QMouseEvent *ev)
+{
+ Q_D(QSlider);
+ if (d->pressedControl != QStyle::SC_SliderHandle) {
+ ev->ignore();
+ return;
+ }
+ ev->accept();
+ int newPosition = d->pixelPosToRangeValue(d->pick(ev->pos()) - d->clickOffset);
+ QStyleOptionSlider opt;
+ initStyleOption(&opt);
+ int m = style()->pixelMetric(QStyle::PM_MaximumDragDistance, &opt, this);
+ if (m >= 0) {
+ QRect r = rect();
+ r.adjust(-m, -m, m, m);
+ if (!r.contains(ev->pos())) {
+ newPosition = d->snapBackPosition;
+ }
+ }
+ setSliderPosition(newPosition);
+}
+
+
+/*!
+ \reimp
+*/
+void QSlider::mouseReleaseEvent(QMouseEvent *ev)
+{
+ Q_D(QSlider);
+ if (d->pressedControl == QStyle::SC_None || ev->buttons()) {
+ ev->ignore();
+ return;
+ }
+ ev->accept();
+ QStyle::SubControl oldPressed = QStyle::SubControl(d->pressedControl);
+ d->pressedControl = QStyle::SC_None;
+ setRepeatAction(SliderNoAction);
+ if (oldPressed == QStyle::SC_SliderHandle)
+ setSliderDown(false);
+ QStyleOptionSlider opt;
+ initStyleOption(&opt);
+ opt.subControls = oldPressed;
+ update(style()->subControlRect(QStyle::CC_Slider, &opt, oldPressed, this));
+}
+
+/*!
+ \reimp
+*/
+QSize QSlider::sizeHint() const
+{
+ Q_D(const QSlider);
+ ensurePolished();
+ const int SliderLength = 84, TickSpace = 5;
+ QStyleOptionSlider opt;
+ initStyleOption(&opt);
+ int thick = style()->pixelMetric(QStyle::PM_SliderThickness, &opt, this);
+ if (d->tickPosition & TicksAbove)
+ thick += TickSpace;
+ if (d->tickPosition & TicksBelow)
+ thick += TickSpace;
+ int w = thick, h = SliderLength;
+ if (d->orientation == Qt::Horizontal) {
+ w = SliderLength;
+ h = thick;
+ }
+ return style()->sizeFromContents(QStyle::CT_Slider, &opt, QSize(w, h), this).expandedTo(QApplication::globalStrut());
+}
+
+/*!
+ \reimp
+*/
+QSize QSlider::minimumSizeHint() const
+{
+ Q_D(const QSlider);
+ QSize s = sizeHint();
+ QStyleOptionSlider opt;
+ initStyleOption(&opt);
+ int length = style()->pixelMetric(QStyle::PM_SliderLength, &opt, this);
+ if (d->orientation == Qt::Horizontal)
+ s.setWidth(length);
+ else
+ s.setHeight(length);
+ return s;
+}
+
+/*!
+ \property QSlider::tickPosition
+ \brief the tickmark position for this slider
+
+ The valid values are described by the QSlider::TickPosition enum.
+
+ The default value is \l QSlider::NoTicks.
+
+ \sa tickInterval
+*/
+
+void QSlider::setTickPosition(TickPosition position)
+{
+ Q_D(QSlider);
+ d->tickPosition = position;
+ d->resetLayoutItemMargins();
+ update();
+ updateGeometry();
+}
+
+QSlider::TickPosition QSlider::tickPosition() const
+{
+ return d_func()->tickPosition;
+}
+
+/*!
+ \fn TickPosition QSlider::tickmarks() const
+ \compat
+
+ Use tickPosition() instead.
+*/
+
+/*!
+ \fn QSlider::setTickmarks(TickPosition position)
+ \compat
+
+ Use setTickPosition() instead.
+*/
+
+/*!
+ \property QSlider::tickInterval
+ \brief the interval between tickmarks
+
+ This is a value interval, not a pixel interval. If it is 0, the
+ slider will choose between lineStep() and pageStep().
+
+ The default value is 0.
+
+ \sa tickPosition, lineStep(), pageStep()
+*/
+
+void QSlider::setTickInterval(int ts)
+{
+ d_func()->tickInterval = qMax(0, ts);
+ update();
+}
+
+int QSlider::tickInterval() const
+{
+ return d_func()->tickInterval;
+}
+
+/*!
+ \fn void QSlider::addStep()
+
+ Use setValue() instead.
+*/
+
+/*!
+ \fn void QSlider::subtractStep()
+
+ Use setValue() instead.
+*/
+
+/*! \internal
+ Returns the style option for slider.
+*/
+Q_GUI_EXPORT QStyleOptionSlider qt_qsliderStyleOption(QSlider *slider)
+{
+ QStyleOptionSlider sliderOption;
+ slider->initStyleOption(&sliderOption);
+ return sliderOption;
+}
+
+#endif
+
+QT_END_NAMESPACE
diff --git a/src/gui/widgets/qslider.h b/src/gui/widgets/qslider.h
new file mode 100644
index 0000000000..14f763a49e
--- /dev/null
+++ b/src/gui/widgets/qslider.h
@@ -0,0 +1,134 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSLIDER_H
+#define QSLIDER_H
+
+#include <QtGui/qabstractslider.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+#ifndef QT_NO_SLIDER
+
+class QSliderPrivate;
+class QStyleOptionSlider;
+class Q_GUI_EXPORT QSlider : public QAbstractSlider
+{
+ Q_OBJECT
+
+ Q_ENUMS(TickPosition)
+ Q_PROPERTY(TickPosition tickPosition READ tickPosition WRITE setTickPosition)
+ Q_PROPERTY(int tickInterval READ tickInterval WRITE setTickInterval)
+
+public:
+ enum TickPosition {
+ NoTicks = 0,
+ TicksAbove = 1,
+ TicksLeft = TicksAbove,
+ TicksBelow = 2,
+ TicksRight = TicksBelow,
+ TicksBothSides = 3
+
+#if defined(QT3_SUPPORT) && !defined(Q_MOC_RUN)
+ ,NoMarks = NoTicks,
+ Above = TicksAbove,
+ Left = TicksAbove,
+ Below = TicksBelow,
+ Right = TicksRight,
+ Both = TicksBothSides
+#endif
+ };
+
+ explicit QSlider(QWidget *parent = 0);
+ explicit QSlider(Qt::Orientation orientation, QWidget *parent = 0);
+
+ ~QSlider();
+
+ QSize sizeHint() const;
+ QSize minimumSizeHint() const;
+
+ void setTickPosition(TickPosition position);
+ TickPosition tickPosition() const;
+
+ void setTickInterval(int ti);
+ int tickInterval() const;
+
+ bool event(QEvent *event);
+
+protected:
+ void paintEvent(QPaintEvent *ev);
+ void mousePressEvent(QMouseEvent *ev);
+ void mouseReleaseEvent(QMouseEvent *ev);
+ void mouseMoveEvent(QMouseEvent *ev);
+ void initStyleOption(QStyleOptionSlider *option) const;
+
+#ifdef QT3_SUPPORT
+public:
+ QT3_SUPPORT_CONSTRUCTOR QSlider(QWidget *parent, const char *name);
+ QT3_SUPPORT_CONSTRUCTOR QSlider(Qt::Orientation, QWidget *parent, const char *name);
+ QT3_SUPPORT_CONSTRUCTOR QSlider(int minValue, int maxValue, int pageStep, int value,
+ Qt::Orientation orientation,
+ QWidget *parent = 0, const char *name = 0);
+ inline QT3_SUPPORT void setTickmarks(TickPosition position) { setTickPosition(position); }
+ inline QT3_SUPPORT TickPosition tickmarks() const { return tickPosition(); }
+public Q_SLOTS:
+ inline QT_MOC_COMPAT void addStep() { triggerAction(SliderSingleStepAdd); };
+ inline QT_MOC_COMPAT void subtractStep() { triggerAction(SliderSingleStepSub); };
+#endif
+
+private:
+ friend Q_GUI_EXPORT QStyleOptionSlider qt_qsliderStyleOption(QSlider *slider);
+
+ Q_DISABLE_COPY(QSlider)
+ Q_DECLARE_PRIVATE(QSlider)
+};
+
+#endif // QT_NO_SLIDER
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QSLIDER_H
diff --git a/src/gui/widgets/qspinbox.cpp b/src/gui/widgets/qspinbox.cpp
new file mode 100644
index 0000000000..c691eaf434
--- /dev/null
+++ b/src/gui/widgets/qspinbox.cpp
@@ -0,0 +1,1536 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <private/qabstractspinbox_p.h>
+#include <qspinbox.h>
+
+#ifndef QT_NO_SPINBOX
+
+#include <qlineedit.h>
+#include <qlocale.h>
+#include <qvalidator.h>
+#include <qdebug.h>
+
+#include <math.h>
+
+QT_BEGIN_NAMESPACE
+
+//#define QSPINBOX_QSBDEBUG
+#ifdef QSPINBOX_QSBDEBUG
+# define QSBDEBUG qDebug
+#else
+# define QSBDEBUG if (false) qDebug
+#endif
+
+static bool isIntermediateValueHelper(qint64 num, qint64 minimum, qint64 maximum, qint64 *match = 0);
+
+class QSpinBoxPrivate : public QAbstractSpinBoxPrivate
+{
+ Q_DECLARE_PUBLIC(QSpinBox)
+public:
+ QSpinBoxPrivate(QWidget *parent = 0);
+ void emitSignals(EmitPolicy ep, const QVariant &);
+
+ virtual QVariant valueFromText(const QString &n) const;
+ virtual QString textFromValue(const QVariant &n) const;
+ QVariant validateAndInterpret(QString &input, int &pos,
+ QValidator::State &state) const;
+ bool isIntermediateValue(const QString &str) const;
+ QChar thousand;
+
+ inline void init() {
+ setLayoutItemMargins(QStyle::SE_SpinBoxLayoutItem);
+ }
+};
+
+class QDoubleSpinBoxPrivate : public QAbstractSpinBoxPrivate
+{
+ Q_DECLARE_PUBLIC(QDoubleSpinBox)
+public:
+ QDoubleSpinBoxPrivate(QWidget *parent = 0);
+ void emitSignals(EmitPolicy ep, const QVariant &);
+ bool isIntermediateValue(const QString &str) const;
+
+ virtual QVariant valueFromText(const QString &n) const;
+ virtual QString textFromValue(const QVariant &n) const;
+ QVariant validateAndInterpret(QString &input, int &pos,
+ QValidator::State &state) const;
+ double round(double input) const;
+ // variables
+ int decimals;
+ QChar delimiter, thousand;
+};
+
+
+/*!
+ \class QSpinBox
+ \brief The QSpinBox class provides a spin box widget.
+
+ \ingroup basicwidgets
+ \mainclass
+
+ QSpinBox is designed to handle integers and discrete sets of
+ values (e.g., month names); use QDoubleSpinBox for floating point
+ values.
+
+ QSpinBox allows the user to choose a value by clicking the up/down
+ buttons or pressing up/down on the keyboard to increase/decrease
+ the value currently displayed. The user can also type the value in
+ manually. The spin box supports integer values but can be extended to
+ use different strings with validate(), textFromValue() and valueFromText().
+
+ Every time the value changes QSpinBox emits the valueChanged()
+ signals. The current value can be fetched with value() and set
+ with setValue().
+
+ Clicking the up/down buttons or using the keyboard accelerator's
+ up and down arrows will increase or decrease the current value in
+ steps of size singleStep(). If you want to change this behaviour you
+ can reimplement the virtual function stepBy(). The minimum and
+ maximum value and the step size can be set using one of the
+ constructors, and can be changed later with setMinimum(),
+ setMaximum() and setSingleStep().
+
+ Most spin boxes are directional, but QSpinBox can also operate as
+ a circular spin box, i.e. if the range is 0-99 and the current
+ value is 99, clicking "up" will give 0 if wrapping() is set to
+ true. Use setWrapping() if you want circular behavior.
+
+ The displayed value can be prepended and appended with arbitrary
+ strings indicating, for example, currency or the unit of
+ measurement. See setPrefix() and setSuffix(). The text in the spin
+ box is retrieved with text() (which includes any prefix() and
+ suffix()), or with cleanText() (which has no prefix(), no suffix()
+ and no leading or trailing whitespace).
+
+ It is often desirable to give the user a special (often default)
+ choice in addition to the range of numeric values. See
+ setSpecialValueText() for how to do this with QSpinBox.
+
+ \table 100%
+ \row \o \inlineimage windowsxp-spinbox.png Screenshot of a Windows XP spin box
+ \o A spin box shown in the \l{Windows XP Style Widget Gallery}{Windows XP widget style}.
+ \row \o \inlineimage plastique-spinbox.png Screenshot of a Plastique spin box
+ \o A spin box shown in the \l{Plastique Style Widget Gallery}{Plastique widget style}.
+ \row \o \inlineimage macintosh-spinbox.png Screenshot of a Macintosh spin box
+ \o A spin box shown in the \l{Macintosh Style Widget Gallery}{Macintosh widget style}.
+ \endtable
+
+ \section1 Subclassing QSpinBox
+
+ If using prefix(), suffix(), and specialValueText() don't provide
+ enough control, you subclass QSpinBox and reimplement
+ valueFromText() and textFromValue(). For example, here's the code
+ for a custom spin box that allows the user to enter icon sizes
+ (e.g., "32 x 32"):
+
+ \snippet examples/widgets/icons/iconsizespinbox.cpp 1
+ \codeline
+ \snippet examples/widgets/icons/iconsizespinbox.cpp 2
+
+ See the \l{widgets/icons}{Icons} example for the full source
+ code.
+
+ \sa QDoubleSpinBox, QDateTimeEdit, QSlider, {Spin Boxes Example}
+*/
+
+/*!
+ \fn void QSpinBox::valueChanged(int i)
+
+ This signal is emitted whenever the spin box's value is changed.
+ The new value's integer value is passed in \a i.
+*/
+
+/*!
+ \fn void QSpinBox::valueChanged(const QString &text)
+
+ \overload
+
+ The new value is passed literally in \a text with no prefix() or
+ suffix().
+*/
+
+/*!
+ Constructs a spin box with 0 as minimum value and 99 as maximum value, a
+ step value of 1. The value is initially set to 0. It is parented to \a
+ parent.
+
+ \sa setMinimum(), setMaximum(), setSingleStep()
+*/
+
+QSpinBox::QSpinBox(QWidget *parent)
+ : QAbstractSpinBox(*new QSpinBoxPrivate(parent), parent)
+{
+ Q_D(QSpinBox);
+ d->init();
+}
+
+#ifdef QT3_SUPPORT
+/*!
+ Use one of the constructors that doesn't take the \a name
+ argument and then use setObjectName() instead.
+*/
+QSpinBox::QSpinBox(QWidget *parent, const char *name)
+ : QAbstractSpinBox(*new QSpinBoxPrivate(parent), parent)
+{
+ Q_D(QSpinBox);
+ setObjectName(QString::fromAscii(name));
+ d->init();
+}
+
+/*!
+ Use one of the constructors that doesn't take the \a name
+ argument and then use setObjectName() instead.
+*/
+QSpinBox::QSpinBox(int minimum, int maximum, int step, QWidget *parent, const char *name)
+ : QAbstractSpinBox(*new QSpinBoxPrivate(parent), parent)
+{
+ Q_D(QSpinBox);
+ d->minimum = QVariant(qMin<int>(minimum, maximum));
+ d->maximum = QVariant(qMax<int>(minimum, maximum));
+ d->singleStep = QVariant(step);
+ setObjectName(QString::fromAscii(name));
+ d->init();
+}
+
+#endif
+
+/*!
+ \property QSpinBox::value
+ \brief the value of the spin box
+
+ setValue() will emit valueChanged() if the new value is different
+ from the old one.
+*/
+
+int QSpinBox::value() const
+{
+ Q_D(const QSpinBox);
+ return d->value.toInt();
+}
+
+void QSpinBox::setValue(int value)
+{
+ Q_D(QSpinBox);
+ d->setValue(QVariant(value), EmitIfChanged);
+}
+
+/*!
+ \property QSpinBox::prefix
+ \brief the spin box's prefix
+
+ The prefix is prepended to the start of the displayed value.
+ Typical use is to display a unit of measurement or a currency
+ symbol. For example:
+
+ \snippet doc/src/snippets/code/src_gui_widgets_qspinbox.cpp 0
+
+ To turn off the prefix display, set this property to an empty
+ string. The default is no prefix. The prefix is not displayed when
+ value() == minimum() and specialValueText() is set.
+
+ If no prefix is set, prefix() returns an empty string.
+
+ \sa suffix(), setSuffix(), specialValueText(), setSpecialValueText()
+*/
+
+QString QSpinBox::prefix() const
+{
+ Q_D(const QSpinBox);
+ return d->prefix;
+}
+
+void QSpinBox::setPrefix(const QString &prefix)
+{
+ Q_D(QSpinBox);
+
+ d->prefix = prefix;
+ d->updateEdit();
+}
+
+/*!
+ \property QSpinBox::suffix
+ \brief the suffix of the spin box
+
+ The suffix is appended to the end of the displayed value. Typical
+ use is to display a unit of measurement or a currency symbol. For
+ example:
+
+ \snippet doc/src/snippets/code/src_gui_widgets_qspinbox.cpp 1
+
+ To turn off the suffix display, set this property to an empty
+ string. The default is no suffix. The suffix is not displayed for
+ the minimum() if specialValueText() is set.
+
+ If no suffix is set, suffix() returns an empty string.
+
+ \sa prefix(), setPrefix(), specialValueText(), setSpecialValueText()
+*/
+
+QString QSpinBox::suffix() const
+{
+ Q_D(const QSpinBox);
+
+ return d->suffix;
+}
+
+void QSpinBox::setSuffix(const QString &suffix)
+{
+ Q_D(QSpinBox);
+
+ d->suffix = suffix;
+ d->updateEdit();
+}
+
+/*!
+ \property QSpinBox::cleanText
+
+ \brief the text of the spin box excluding any prefix, suffix,
+ or leading or trailing whitespace.
+
+ \sa text, QSpinBox::prefix, QSpinBox::suffix
+*/
+
+QString QSpinBox::cleanText() const
+{
+ Q_D(const QSpinBox);
+
+ return d->stripped(d->edit->displayText());
+}
+
+
+/*!
+ \property QSpinBox::singleStep
+ \brief the step value
+
+ When the user uses the arrows to change the spin box's value the
+ value will be incremented/decremented by the amount of the
+ singleStep. The default value is 1. Setting a singleStep value of
+ less than 0 does nothing.
+*/
+
+int QSpinBox::singleStep() const
+{
+ Q_D(const QSpinBox);
+
+ return d->singleStep.toInt();
+}
+
+void QSpinBox::setSingleStep(int value)
+{
+ Q_D(QSpinBox);
+ if (value >= 0) {
+ d->singleStep = QVariant(value);
+ d->updateEdit();
+ }
+}
+
+/*!
+ \property QSpinBox::minimum
+
+ \brief the minimum value of the spin box
+
+ When setting this property the \l maximum is adjusted
+ if necessary to ensure that the range remains valid.
+
+ The default minimum value is 0.
+
+ \sa setRange() specialValueText
+*/
+
+int QSpinBox::minimum() const
+{
+ Q_D(const QSpinBox);
+
+ return d->minimum.toInt();
+}
+
+void QSpinBox::setMinimum(int minimum)
+{
+ Q_D(QSpinBox);
+ const QVariant m(minimum);
+ d->setRange(m, (d->variantCompare(d->maximum, m) > 0 ? d->maximum : m));
+}
+
+/*!
+ \property QSpinBox::maximum
+
+ \brief the maximum value of the spin box
+
+ When setting this property the \l minimum is adjusted
+ if necessary, to ensure that the range remains valid.
+
+ The default maximum value is 99.
+
+ \sa setRange() specialValueText
+
+*/
+
+int QSpinBox::maximum() const
+{
+ Q_D(const QSpinBox);
+
+ return d->maximum.toInt();
+}
+
+void QSpinBox::setMaximum(int maximum)
+{
+ Q_D(QSpinBox);
+ const QVariant m(maximum);
+ d->setRange((d->variantCompare(d->minimum, m) < 0 ? d->minimum : m), m);
+}
+
+/*!
+ Convenience function to set the \a minimum, and \a maximum values
+ with a single function call.
+
+ \snippet doc/src/snippets/code/src_gui_widgets_qspinbox.cpp 2
+ is equivalent to:
+ \snippet doc/src/snippets/code/src_gui_widgets_qspinbox.cpp 3
+
+ \sa minimum maximum
+*/
+
+void QSpinBox::setRange(int minimum, int maximum)
+{
+ Q_D(QSpinBox);
+ d->setRange(QVariant(minimum), QVariant(maximum));
+}
+
+/*!
+ This virtual function is used by the spin box whenever it needs
+ to display the given \a value. The default implementation returns
+ a string containing \a value printed in the standard way using
+ QWidget::locale().toString(). Reimplementations may return anything. (See
+ the example in the detailed description.)
+
+ Note: QSpinBox does not call this function for specialValueText()
+ and that neither prefix() nor suffix() should be included in the
+ return value.
+
+ If you reimplement this, you may also need to reimplement
+ valueFromText() and validate()
+
+ \sa valueFromText(), validate()
+*/
+
+QString QSpinBox::textFromValue(int value) const
+{
+ Q_D(const QSpinBox);
+ QString str = locale().toString(value);
+ if (qAbs(value) >= 1000 || value == INT_MIN) {
+ str.remove(d->thousand);
+ }
+
+ return str;
+}
+
+/*!
+ \fn int QSpinBox::valueFromText(const QString &text) const
+
+ This virtual function is used by the spin box whenever it needs to
+ interpret \a text entered by the user as a value.
+
+ Subclasses that need to display spin box values in a non-numeric
+ way need to reimplement this function.
+
+ Note: QSpinBox handles specialValueText() separately; this
+ function is only concerned with the other values.
+
+ \sa textFromValue(), validate()
+*/
+
+int QSpinBox::valueFromText(const QString &text) const
+{
+ Q_D(const QSpinBox);
+
+ QString copy = text;
+ int pos = d->edit->cursorPosition();
+ QValidator::State state = QValidator::Acceptable;
+ return d->validateAndInterpret(copy, pos, state).toInt();
+}
+
+/*!
+ \reimp
+*/
+QValidator::State QSpinBox::validate(QString &text, int &pos) const
+{
+ Q_D(const QSpinBox);
+
+ QValidator::State state;
+ d->validateAndInterpret(text, pos, state);
+ return state;
+}
+
+
+/*!
+ \reimp
+*/
+void QSpinBox::fixup(QString &input) const
+{
+ Q_D(const QSpinBox);
+
+ input.remove(d->thousand);
+}
+
+
+// --- QDoubleSpinBox ---
+
+/*!
+ \class QDoubleSpinBox
+ \brief The QDoubleSpinBox class provides a spin box widget that
+ takes doubles.
+
+ \ingroup basicwidgets
+ \mainclass
+
+ QDoubleSpinBox allows the user to choose a value by clicking the
+ up and down buttons or by pressing Up or Down on the keyboard to
+ increase or decrease the value currently displayed. The user can
+ also type the value in manually. The spin box supports double
+ values but can be extended to use different strings with
+ validate(), textFromValue() and valueFromText().
+
+ Every time the value changes QDoubleSpinBox emits the
+ valueChanged() signal. The current value can be fetched with
+ value() and set with setValue().
+
+ Note: QDoubleSpinBox will round numbers so they can be displayed
+ with the current precision. In a QDoubleSpinBox with decimals set
+ to 2, calling setValue(2.555) will cause value() to return 2.56.
+
+ Clicking the up and down buttons or using the keyboard accelerator's
+ Up and Down arrows will increase or decrease the current value in
+ steps of size singleStep(). If you want to change this behavior you
+ can reimplement the virtual function stepBy(). The minimum and
+ maximum value and the step size can be set using one of the
+ constructors, and can be changed later with setMinimum(),
+ setMaximum() and setSingleStep(). The spinbox has a default
+ precision of 2 decimal places but this can be changed using
+ setDecimals().
+
+ Most spin boxes are directional, but QDoubleSpinBox can also
+ operate as a circular spin box, i.e. if the range is 0.0-99.9 and
+ the current value is 99.9, clicking "up" will give 0 if wrapping()
+ is set to true. Use setWrapping() if you want circular behavior.
+
+ The displayed value can be prepended and appended with arbitrary
+ strings indicating, for example, currency or the unit of
+ measurement. See setPrefix() and setSuffix(). The text in the spin
+ box is retrieved with text() (which includes any prefix() and
+ suffix()), or with cleanText() (which has no prefix(), no suffix()
+ and no leading or trailing whitespace).
+
+ It is often desirable to give the user a special (often default)
+ choice in addition to the range of numeric values. See
+ setSpecialValueText() for how to do this with QDoubleSpinBox.
+
+ \sa QSpinBox, QDateTimeEdit, QSlider, {Spin Boxes Example}
+*/
+
+/*!
+ \fn void QDoubleSpinBox::valueChanged(double d);
+
+ This signal is emitted whenever the spin box's value is changed.
+ The new value is passed in \a d.
+*/
+
+/*!
+ \fn void QDoubleSpinBox::valueChanged(const QString &text);
+
+ \overload
+
+ The new value is passed literally in \a text with no prefix() or
+ suffix().
+*/
+
+/*!
+ Constructs a spin box with 0.0 as minimum value and 99.99 as maximum value,
+ a step value of 1.0 and a precision of 2 decimal places. The value is
+ initially set to 0.00. The spin box has the given \a parent.
+
+ \sa setMinimum(), setMaximum(), setSingleStep()
+*/
+QDoubleSpinBox::QDoubleSpinBox(QWidget *parent)
+ : QAbstractSpinBox(*new QDoubleSpinBoxPrivate(parent), parent)
+{
+}
+
+/*!
+ \property QDoubleSpinBox::value
+ \brief the value of the spin box
+
+ setValue() will emit valueChanged() if the new value is different
+ from the old one.
+
+ Note: The value will be rounded so it can be displayed with the
+ current setting of decimals.
+
+ \sa decimals
+*/
+double QDoubleSpinBox::value() const
+{
+ Q_D(const QDoubleSpinBox);
+
+ return d->value.toDouble();
+}
+
+void QDoubleSpinBox::setValue(double value)
+{
+ Q_D(QDoubleSpinBox);
+ QVariant v(d->round(value));
+ d->setValue(v, EmitIfChanged);
+}
+/*!
+ \property QDoubleSpinBox::prefix
+ \brief the spin box's prefix
+
+ The prefix is prepended to the start of the displayed value.
+ Typical use is to display a unit of measurement or a currency
+ symbol. For example:
+
+ \snippet doc/src/snippets/code/src_gui_widgets_qspinbox.cpp 4
+
+ To turn off the prefix display, set this property to an empty
+ string. The default is no prefix. The prefix is not displayed when
+ value() == minimum() and specialValueText() is set.
+
+ If no prefix is set, prefix() returns an empty string.
+
+ \sa suffix(), setSuffix(), specialValueText(), setSpecialValueText()
+*/
+
+QString QDoubleSpinBox::prefix() const
+{
+ Q_D(const QDoubleSpinBox);
+
+ return d->prefix;
+}
+
+void QDoubleSpinBox::setPrefix(const QString &prefix)
+{
+ Q_D(QDoubleSpinBox);
+
+ d->prefix = prefix;
+ d->updateEdit();
+}
+
+/*!
+ \property QDoubleSpinBox::suffix
+ \brief the suffix of the spin box
+
+ The suffix is appended to the end of the displayed value. Typical
+ use is to display a unit of measurement or a currency symbol. For
+ example:
+
+ \snippet doc/src/snippets/code/src_gui_widgets_qspinbox.cpp 5
+
+ To turn off the suffix display, set this property to an empty
+ string. The default is no suffix. The suffix is not displayed for
+ the minimum() if specialValueText() is set.
+
+ If no suffix is set, suffix() returns an empty string.
+
+ \sa prefix(), setPrefix(), specialValueText(), setSpecialValueText()
+*/
+
+QString QDoubleSpinBox::suffix() const
+{
+ Q_D(const QDoubleSpinBox);
+
+ return d->suffix;
+}
+
+void QDoubleSpinBox::setSuffix(const QString &suffix)
+{
+ Q_D(QDoubleSpinBox);
+
+ d->suffix = suffix;
+ d->updateEdit();
+}
+
+/*!
+ \property QDoubleSpinBox::cleanText
+
+ \brief the text of the spin box excluding any prefix, suffix,
+ or leading or trailing whitespace.
+
+ \sa text, QDoubleSpinBox::prefix, QDoubleSpinBox::suffix
+*/
+
+QString QDoubleSpinBox::cleanText() const
+{
+ Q_D(const QDoubleSpinBox);
+
+ return d->stripped(d->edit->displayText());
+}
+
+/*!
+ \property QDoubleSpinBox::singleStep
+ \brief the step value
+
+ When the user uses the arrows to change the spin box's value the
+ value will be incremented/decremented by the amount of the
+ singleStep. The default value is 1.0. Setting a singleStep value
+ of less than 0 does nothing.
+*/
+double QDoubleSpinBox::singleStep() const
+{
+ Q_D(const QDoubleSpinBox);
+
+ return d->singleStep.toDouble();
+}
+
+void QDoubleSpinBox::setSingleStep(double value)
+{
+ Q_D(QDoubleSpinBox);
+
+ if (value >= 0) {
+ d->singleStep = value;
+ d->updateEdit();
+ }
+}
+
+/*!
+ \property QDoubleSpinBox::minimum
+
+ \brief the minimum value of the spin box
+
+ When setting this property the \l maximum is adjusted
+ if necessary to ensure that the range remains valid.
+
+ The default minimum value is 0.0.
+
+ Note: The minimum value will be rounded to match the decimals
+ property.
+
+ \sa decimals, setRange() specialValueText
+*/
+
+double QDoubleSpinBox::minimum() const
+{
+ Q_D(const QDoubleSpinBox);
+
+ return d->minimum.toDouble();
+}
+
+void QDoubleSpinBox::setMinimum(double minimum)
+{
+ Q_D(QDoubleSpinBox);
+ const QVariant m(d->round(minimum));
+ d->setRange(m, (d->variantCompare(d->maximum, m) > 0 ? d->maximum : m));
+}
+
+/*!
+ \property QDoubleSpinBox::maximum
+
+ \brief the maximum value of the spin box
+
+ When setting this property the \l minimum is adjusted
+ if necessary, to ensure that the range remains valid.
+
+ The default maximum value is 99.99.
+
+ Note: The maximum value will be rounded to match the decimals
+ property.
+
+ \sa decimals, setRange()
+*/
+
+double QDoubleSpinBox::maximum() const
+{
+ Q_D(const QDoubleSpinBox);
+
+ return d->maximum.toDouble();
+}
+
+void QDoubleSpinBox::setMaximum(double maximum)
+{
+ Q_D(QDoubleSpinBox);
+ const QVariant m(d->round(maximum));
+ d->setRange((d->variantCompare(d->minimum, m) < 0 ? d->minimum : m), m);
+}
+
+/*!
+ Convenience function to set the \a minimum and \a maximum values
+ with a single function call.
+
+ Note: The maximum and minimum values will be rounded to match the
+ decimals property.
+
+ \snippet doc/src/snippets/code/src_gui_widgets_qspinbox.cpp 6
+ is equivalent to:
+ \snippet doc/src/snippets/code/src_gui_widgets_qspinbox.cpp 7
+
+ \sa minimum maximum
+*/
+
+void QDoubleSpinBox::setRange(double minimum, double maximum)
+{
+ Q_D(QDoubleSpinBox);
+ d->setRange(QVariant(d->round(minimum)), QVariant(d->round(maximum)));
+}
+
+/*!
+ \property QDoubleSpinBox::decimals
+
+ \brief the precision of the spin box, in decimals
+
+ Sets how many decimals the spinbox will use for displaying and
+ interpreting doubles.
+
+ \warning The results might not be reliable with very high values
+ for \a decimals.
+
+ Note: The maximum, minimum and value might change as a result of
+ changing this property.
+*/
+
+int QDoubleSpinBox::decimals() const
+{
+ Q_D(const QDoubleSpinBox);
+
+ return d->decimals;
+}
+
+void QDoubleSpinBox::setDecimals(int decimals)
+{
+ Q_D(QDoubleSpinBox);
+ d->decimals = qMax(0, decimals);
+
+ setRange(minimum(), maximum()); // make sure values are rounded
+ setValue(value());
+}
+
+/*!
+ This virtual function is used by the spin box whenever it needs to
+ display the given \a value. The default implementation returns a string
+ containing \a value printed using QWidget::locale().toString(\a value,
+ QLatin1Char('f'), decimals()) and will remove the thousand
+ separator. Reimplementations may return anything.
+
+ Note: QDoubleSpinBox does not call this function for
+ specialValueText() and that neither prefix() nor suffix() should
+ be included in the return value.
+
+ If you reimplement this, you may also need to reimplement
+ valueFromText().
+
+ \sa valueFromText()
+*/
+
+
+QString QDoubleSpinBox::textFromValue(double value) const
+{
+ Q_D(const QDoubleSpinBox);
+ QString str = locale().toString(value, 'f', d->decimals);
+ if (qAbs(value) >= 1000.0) {
+ str.remove(d->thousand);
+ }
+ return str;
+}
+
+/*!
+ This virtual function is used by the spin box whenever it needs to
+ interpret \a text entered by the user as a value.
+
+ Subclasses that need to display spin box values in a non-numeric
+ way need to reimplement this function.
+
+ Note: QDoubleSpinBox handles specialValueText() separately; this
+ function is only concerned with the other values.
+
+ \sa textFromValue(), validate()
+*/
+double QDoubleSpinBox::valueFromText(const QString &text) const
+{
+ Q_D(const QDoubleSpinBox);
+
+ QString copy = text;
+ int pos = d->edit->cursorPosition();
+ QValidator::State state = QValidator::Acceptable;
+ return d->validateAndInterpret(copy, pos, state).toDouble();
+}
+
+/*!
+ \reimp
+*/
+QValidator::State QDoubleSpinBox::validate(QString &text, int &pos) const
+{
+ Q_D(const QDoubleSpinBox);
+
+ QValidator::State state;
+ d->validateAndInterpret(text, pos, state);
+ return state;
+}
+
+
+/*!
+ \reimp
+*/
+void QDoubleSpinBox::fixup(QString &input) const
+{
+ Q_D(const QDoubleSpinBox);
+
+ input.remove(d->thousand);
+}
+
+// --- QSpinBoxPrivate ---
+
+/*!
+ \internal
+ Constructs a QSpinBoxPrivate object
+*/
+
+QSpinBoxPrivate::QSpinBoxPrivate(QWidget *parent)
+{
+ minimum = QVariant((int)0);
+ maximum = QVariant((int)99);
+ value = minimum;
+ singleStep = QVariant((int)1);
+ type = QVariant::Int;
+ const QString str = (parent ? parent->locale() : QLocale()).toString(4567);
+ if (str.size() == 5) {
+ thousand = QChar(str.at(1));
+ }
+
+}
+
+/*!
+ \internal
+ \reimp
+*/
+
+void QSpinBoxPrivate::emitSignals(EmitPolicy ep, const QVariant &old)
+{
+ Q_Q(QSpinBox);
+ if (ep != NeverEmit) {
+ pendingEmit = false;
+ if (ep == AlwaysEmit || value != old) {
+ emit q->valueChanged(edit->displayText());
+ emit q->valueChanged(value.toInt());
+ }
+ }
+}
+
+/*!
+ \internal
+ \reimp
+*/
+
+QString QSpinBoxPrivate::textFromValue(const QVariant &value) const
+{
+ Q_Q(const QSpinBox);
+ return q->textFromValue(value.toInt());
+}
+/*!
+ \internal
+ \reimp
+*/
+
+QVariant QSpinBoxPrivate::valueFromText(const QString &text) const
+{
+ Q_Q(const QSpinBox);
+
+ return QVariant(q->valueFromText(text));
+}
+
+
+/*!
+ \internal
+
+ Return true if str can become a number which is between minimum and
+ maximum or false if this is not possible.
+*/
+
+bool QSpinBoxPrivate::isIntermediateValue(const QString &str) const
+{
+ const int num = q_func()->locale().toInt(str, 0, 10);
+ const int min = minimum.toInt();
+ const int max = maximum.toInt();
+
+ int numDigits = 0;
+ int digits[10];
+ int tmp = num;
+ if (tmp == 0) {
+ numDigits = 1;
+ digits[0] = 0;
+ } else {
+ tmp = num;
+ for (int i=0; tmp != 0; ++i) {
+ digits[numDigits++] = qAbs(tmp % 10);
+ tmp /= 10;
+ }
+ }
+
+ int failures = 0;
+ for (int number=min; /*number<=max*/; ++number) {
+ tmp = number;
+ for (int i=0; tmp != 0;) {
+ if (digits[i] == qAbs(tmp % 10)) {
+ if (++i == numDigits)
+ return true;
+ }
+ tmp /= 10;
+ }
+ if (failures++ == 500000) //upper bound
+ return true;
+ if (number == max) // needed for INT_MAX
+ break;
+ }
+ return false;
+}
+
+/*!
+ \internal Multi purpose function that parses input, sets state to
+ the appropriate state and returns the value it will be interpreted
+ as.
+*/
+
+QVariant QSpinBoxPrivate::validateAndInterpret(QString &input, int &pos,
+ QValidator::State &state) const
+{
+ if (cachedText == input && !input.isEmpty()) {
+ state = cachedState;
+ QSBDEBUG() << "cachedText was" << "'" << cachedText << "'" << "state was "
+ << state << " and value was " << cachedValue;
+
+ return cachedValue;
+ }
+ const int max = maximum.toInt();
+ const int min = minimum.toInt();
+
+ QString copy = stripped(input, &pos);
+ QSBDEBUG() << "input" << input << "copy" << copy;
+ state = QValidator::Acceptable;
+ int num = min;
+
+ if (max != min && (copy.isEmpty()
+ || (min < 0 && copy == QLatin1String("-"))
+ || (min >= 0 && copy == QLatin1String("+")))) {
+ state = QValidator::Intermediate;
+ QSBDEBUG() << __FILE__ << __LINE__<< "num is set to" << num;
+ } else if (copy.startsWith(QLatin1String("-")) && min >= 0) {
+ state = QValidator::Invalid; // special-case -0 will be interpreted as 0 and thus not be invalid with a range from 0-100
+ } else {
+ bool ok = false;
+ bool removedThousand = false;
+ num = q_func()->locale().toInt(copy, &ok, 10);
+ if (!ok && copy.contains(thousand) && (max >= 1000 || min <= -1000)) {
+ const int s = copy.size();
+ copy.remove(thousand);
+ pos = qMax(0, pos - (s - copy.size()));
+ removedThousand = true;
+ num = q_func()->locale().toInt(copy, &ok, 10);
+ }
+ QSBDEBUG() << __FILE__ << __LINE__<< "num is set to" << num;
+ if (!ok) {
+ state = QValidator::Invalid;
+ } else if (num >= min && num <= max) {
+ state = removedThousand ? QValidator::Intermediate : QValidator::Acceptable;
+ } else if (max == min) {
+ state = QValidator::Invalid;
+ } else {
+ if ((num >= 0 && num > max) || (num < 0 && num < min)) {
+ state = QValidator::Invalid;
+ QSBDEBUG() << __FILE__ << __LINE__<< "state is set to Invalid";
+ } else {
+ state = isIntermediateValue(copy) ? QValidator::Intermediate : QValidator::Invalid;
+ QSBDEBUG() << __FILE__ << __LINE__<< "state is set to "
+ << (state == QValidator::Intermediate ? "Intermediate" : "Acceptable");
+ }
+ }
+ }
+ if (state != QValidator::Acceptable)
+ num = max > 0 ? min : max;
+ input = prefix + copy + suffix;
+ cachedText = input;
+ cachedState = state;
+ cachedValue = QVariant((int)num);
+
+ QSBDEBUG() << "cachedText is set to '" << cachedText << "' state is set to "
+ << state << " and value is set to " << cachedValue;
+ return cachedValue;
+}
+
+// --- QDoubleSpinBoxPrivate ---
+
+/*!
+ \internal
+ Constructs a QSpinBoxPrivate object
+*/
+
+QDoubleSpinBoxPrivate::QDoubleSpinBoxPrivate(QWidget *parent)
+{
+ minimum = QVariant(0.0);
+ maximum = QVariant(99.99);
+ value = minimum;
+ singleStep = QVariant(1.0);
+ decimals = 2;
+ type = QVariant::Double;
+ const QString str = (parent ? parent->locale() : QLocale()).toString(4567.1);
+ if (str.size() == 6) {
+ delimiter = str.at(4);
+ thousand = QChar((ushort)0);
+ } else if (str.size() == 7) {
+ thousand = str.at(1);
+ delimiter = str.at(5);
+ }
+ Q_ASSERT(!delimiter.isNull());
+}
+
+/*!
+ \internal
+ \reimp
+*/
+
+void QDoubleSpinBoxPrivate::emitSignals(EmitPolicy ep, const QVariant &old)
+{
+ Q_Q(QDoubleSpinBox);
+ if (ep != NeverEmit) {
+ pendingEmit = false;
+ if (ep == AlwaysEmit || value != old) {
+ emit q->valueChanged(edit->displayText());
+ emit q->valueChanged(value.toDouble());
+ }
+ }
+}
+
+
+bool QDoubleSpinBoxPrivate::isIntermediateValue(const QString &str) const
+{
+ QSBDEBUG() << "input is" << str << minimum << maximum;
+ qint64 dec = 1;
+ for (int i=0; i<decimals; ++i)
+ dec *= 10;
+
+ const QLatin1Char dot('.');
+
+ // I know QString::number() uses CLocale so I use dot
+ const QString minstr = QString::number(minimum.toDouble(), 'f', decimals);
+ bool ok;
+ qint64 min_left = minstr.left(minstr.indexOf(dot)).toLongLong(&ok);
+ if (!ok)
+ return false;
+ qint64 min_right = minstr.mid(minstr.indexOf(dot) + 1).toLongLong();
+
+ const QString maxstr = QString::number(maximum.toDouble(), 'f', decimals);
+ qint64 max_left = maxstr.left(maxstr.indexOf(dot)).toLongLong(&ok);
+ if (!ok)
+ return true;
+ qint64 max_right = maxstr.mid(maxstr.indexOf(dot) + 1).toLongLong();
+
+ const int dotindex = str.indexOf(delimiter);
+ const bool negative = maximum.toDouble() < 0;
+ qint64 left = 0, right = 0;
+ bool doleft = true;
+ bool doright = true;
+ if (dotindex == -1) {
+ left = str.toLongLong();
+ doright = false;
+ } else if (dotindex == 0 || (dotindex == 1 && str.at(0) == QLatin1Char('+'))) {
+ if (negative) {
+ QSBDEBUG() << __FILE__ << __LINE__ << "returns false";
+ return false;
+ }
+ doleft = false;
+ right = str.mid(dotindex + 1).toLongLong();
+ } else if (dotindex == 1 && str.at(0) == QLatin1Char('-')) {
+ if (!negative) {
+ QSBDEBUG() << __FILE__ << __LINE__ << "returns false";
+ return false;
+ }
+ doleft = false;
+ right = str.mid(dotindex + 1).toLongLong();
+ } else {
+ left = str.left(dotindex).toLongLong();
+ if (dotindex == str.size() - 1) {
+ doright = false;
+ } else {
+ right = str.mid(dotindex + 1).toLongLong();
+ }
+ }
+ if ((left >= 0 && max_left < 0 && !str.startsWith(QLatin1Char('-'))) || (left < 0 && min_left >= 0)) {
+ QSBDEBUG("returns false 0");
+ return false;
+ }
+
+ qint64 match = min_left;
+ if (doleft && !isIntermediateValueHelper(left, min_left, max_left, &match)) {
+ QSBDEBUG() << __FILE__ << __LINE__ << "returns false";
+ return false;
+ }
+ if (doright) {
+ QSBDEBUG("match %lld min_left %lld max_left %lld", match, min_left, max_left);
+ if (!doleft) {
+ if (min_left == max_left) {
+ const bool ret = isIntermediateValueHelper(qAbs(left),
+ negative ? max_right : min_right,
+ negative ? min_right : max_right);
+ QSBDEBUG() << __FILE__ << __LINE__ << "returns" << ret;
+ return ret;
+ } else if (qAbs(max_left - min_left) == 1) {
+ const bool ret = isIntermediateValueHelper(qAbs(left), min_right, negative ? 0 : dec)
+ || isIntermediateValueHelper(qAbs(left), negative ? dec : 0, max_right);
+ QSBDEBUG() << __FILE__ << __LINE__ << "returns" << ret;
+ return ret;
+ } else {
+ const bool ret = isIntermediateValueHelper(qAbs(left), 0, dec);
+ QSBDEBUG() << __FILE__ << __LINE__ << "returns" << ret;
+ return ret;
+ }
+ }
+ if (match != min_left) {
+ min_right = negative ? dec : 0;
+ }
+ if (match != max_left) {
+ max_right = negative ? 0 : dec;
+ }
+ qint64 tmpl = negative ? max_right : min_right;
+ qint64 tmpr = negative ? min_right : max_right;
+ const bool ret = isIntermediateValueHelper(right, tmpl, tmpr);
+ QSBDEBUG() << __FILE__ << __LINE__ << "returns" << ret;
+ return ret;
+ }
+ QSBDEBUG() << __FILE__ << __LINE__ << "returns true";
+ return true;
+}
+
+/*!
+ \internal
+ \reimp
+*/
+QVariant QDoubleSpinBoxPrivate::valueFromText(const QString &f) const
+{
+ Q_Q(const QDoubleSpinBox);
+ return QVariant(q->valueFromText(f));
+}
+
+/*!
+ \internal
+ Rounds to a double value that is restricted to decimals.
+ E.g. // decimals = 2
+
+ round(5.555) => 5.56
+ */
+
+double QDoubleSpinBoxPrivate::round(double value) const
+{
+ Q_Q(const QDoubleSpinBox);
+ const QString strDbl = q->locale().toString(value, 'f', decimals);
+ return q->locale().toDouble(strDbl);
+}
+
+
+/*!
+ \internal Multi purpose function that parses input, sets state to
+ the appropriate state and returns the value it will be interpreted
+ as.
+*/
+
+QVariant QDoubleSpinBoxPrivate::validateAndInterpret(QString &input, int &pos,
+ QValidator::State &state) const
+{
+ if (cachedText == input && !input.isEmpty()) {
+ state = cachedState;
+ QSBDEBUG() << "cachedText was" << "'" << cachedText << "'" << "state was "
+ << state << " and value was " << cachedValue;
+ return cachedValue;
+ }
+ const double max = maximum.toDouble();
+ const double min = minimum.toDouble();
+
+ QString copy = stripped(input, &pos);
+ QSBDEBUG() << "input" << input << "copy" << copy;
+ int len = copy.size();
+ double num = min;
+ const bool plus = max >= 0;
+ const bool minus = min <= 0;
+
+ switch (len) {
+ case 0:
+ state = max != min ? QValidator::Intermediate : QValidator::Invalid;
+ goto end;
+ case 1:
+ if (copy.at(0) == delimiter
+ || (plus && copy.at(0) == QLatin1Char('+'))
+ || (minus && copy.at(0) == QLatin1Char('-'))) {
+ state = QValidator::Intermediate;
+ goto end;
+ }
+ break;
+ case 2:
+ if (copy.at(1) == delimiter
+ && ((plus && copy.at(0) == QLatin1Char('+')) || (minus && copy.at(0) == QLatin1Char('-')))) {
+ state = QValidator::Intermediate;
+ goto end;
+ }
+ break;
+ default: break;
+ }
+
+ if (copy.at(0) == thousand) {
+ QSBDEBUG() << __FILE__ << __LINE__<< "state is set to Invalid";
+ state = QValidator::Invalid;
+ goto end;
+ } else if (len > 1) {
+ const int dec = copy.indexOf(delimiter);
+ if (dec != -1) {
+ if (dec + 1 < copy.size() && copy.at(dec + 1) == delimiter && pos == dec + 1) {
+ copy.remove(dec + 1, 1); // typing a delimiter when you are on the delimiter
+ } // should be treated as typing right arrow
+
+ if (copy.size() - dec > decimals + 1) {
+ QSBDEBUG() << __FILE__ << __LINE__<< "state is set to Invalid";
+ state = QValidator::Invalid;
+ goto end;
+ }
+ for (int i=dec + 1; i<copy.size(); ++i) {
+ if (copy.at(i).isSpace() || copy.at(i) == thousand) {
+ QSBDEBUG() << __FILE__ << __LINE__<< "state is set to Invalid";
+ state = QValidator::Invalid;
+ goto end;
+ }
+ }
+ } else {
+ const QChar &last = copy.at(len - 1);
+ const QChar &secondLast = copy.at(len - 2);
+ if ((last == thousand || last.isSpace())
+ && (secondLast == thousand || secondLast.isSpace())) {
+ state = QValidator::Invalid;
+ QSBDEBUG() << __FILE__ << __LINE__<< "state is set to Invalid";
+ goto end;
+ } else if (last.isSpace() && (!thousand.isSpace() || secondLast.isSpace())) {
+ state = QValidator::Invalid;
+ QSBDEBUG() << __FILE__ << __LINE__<< "state is set to Invalid";
+ goto end;
+ }
+ }
+ }
+
+ {
+ bool ok = false;
+ QLocale loc(q_func()->locale());
+ num = loc.toDouble(copy, &ok);
+ QSBDEBUG() << __FILE__ << __LINE__ << loc << copy << num << ok;
+ bool notAcceptable = false;
+
+ if (!ok) {
+ if (thousand.isPrint()) {
+ if (max < 1000 && min > -1000 && copy.contains(thousand)) {
+ state = QValidator::Invalid;
+ QSBDEBUG() << __FILE__ << __LINE__<< "state is set to Invalid";
+ goto end;
+ }
+
+ const int len = copy.size();
+ for (int i=0; i<len- 1; ++i) {
+ if (copy.at(i) == thousand && copy.at(i + 1) == thousand) {
+ QSBDEBUG() << __FILE__ << __LINE__<< "state is set to Invalid";
+ state = QValidator::Invalid;
+ goto end;
+ }
+ }
+
+ const int s = copy.size();
+ copy.remove(thousand);
+ pos = qMax(0, pos - (s - copy.size()));
+
+
+ num = loc.toDouble(copy, &ok);
+ QSBDEBUG() << thousand << num << copy << ok;
+
+ if (!ok) {
+ state = QValidator::Invalid;
+ QSBDEBUG() << __FILE__ << __LINE__<< "state is set to Invalid";
+ goto end;
+ }
+ notAcceptable = true;
+ }
+ }
+
+ if (!ok) {
+ state = QValidator::Invalid;
+ QSBDEBUG() << __FILE__ << __LINE__<< "state is set to Invalid";
+ } else if (num >= min && num <= max) {
+ state = notAcceptable ? QValidator::Intermediate : QValidator::Acceptable;
+ QSBDEBUG() << __FILE__ << __LINE__<< "state is set to "
+ << (state == QValidator::Intermediate ? "Intermediate" : "Acceptable");
+ } else if (max == min) { // when max and min is the same the only non-Invalid input is max (or min)
+ state = QValidator::Invalid;
+ QSBDEBUG() << __FILE__ << __LINE__<< "state is set to Invalid";
+ } else {
+ if ((num >= 0 && num > max) || (num < 0 && num < min)) {
+ state = QValidator::Invalid;
+ QSBDEBUG() << __FILE__ << __LINE__<< "state is set to Invalid";
+ } else {
+ state = isIntermediateValue(copy) ? QValidator::Intermediate : QValidator::Invalid;
+ QSBDEBUG() << __FILE__ << __LINE__<< "state is set to "
+ << (state == QValidator::Intermediate ? "Intermediate" : "Acceptable");
+ }
+ }
+ }
+
+end:
+ if (state != QValidator::Acceptable) {
+ num = max > 0 ? min : max;
+ }
+
+ input = prefix + copy + suffix;
+ cachedText = input;
+ cachedState = state;
+ cachedValue = QVariant(num);
+ return QVariant(num);
+}
+
+/*
+ \internal
+ \reimp
+*/
+
+QString QDoubleSpinBoxPrivate::textFromValue(const QVariant &f) const
+{
+ Q_Q(const QDoubleSpinBox);
+ return q->textFromValue(f.toDouble());
+}
+
+/*!
+ \fn void QSpinBox::setLineStep(int step)
+
+ Use setSingleStep() instead.
+*/
+
+/*!
+ \fn void QSpinBox::setMaxValue(int value)
+
+ Use setMaximum() instead.
+*/
+
+/*!
+ \fn void QSpinBox::setMinValue(int value)
+
+ Use setMinimum() instead.
+*/
+
+/*!
+ \fn int QSpinBox::maxValue() const
+
+ Use maximum() instead.
+*/
+
+/*!
+ \fn int QSpinBox::minValue() const
+
+ Use minimum() instead.
+*/
+
+/*!
+ \internal Returns whether \a str is a string which value cannot be
+ parsed but still might turn into something valid.
+*/
+
+static bool isIntermediateValueHelper(qint64 num, qint64 min, qint64 max, qint64 *match)
+{
+ QSBDEBUG("%lld %lld %lld", num, min, max);
+
+ if (num >= min && num <= max) {
+ if (match)
+ *match = num;
+ QSBDEBUG("returns true 0");
+ return true;
+ }
+ qint64 tmp = num;
+
+ int numDigits = 0;
+ int digits[10];
+ if (tmp == 0) {
+ numDigits = 1;
+ digits[0] = 0;
+ } else {
+ tmp = qAbs(num);
+ for (int i=0; tmp > 0; ++i) {
+ digits[numDigits++] = tmp % 10;
+ tmp /= 10;
+ }
+ }
+
+ int failures = 0;
+ qint64 number;
+ for (number=max; number>=min; --number) {
+ tmp = qAbs(number);
+ for (int i=0; tmp > 0;) {
+ if (digits[i] == (tmp % 10)) {
+ if (++i == numDigits) {
+ if (match)
+ *match = number;
+ QSBDEBUG("returns true 1");
+ return true;
+ }
+ }
+ tmp /= 10;
+ }
+ if (failures++ == 500000) { //upper bound
+ if (match)
+ *match = num;
+ QSBDEBUG("returns true 2");
+ return true;
+ }
+ }
+ QSBDEBUG("returns false");
+ return false;
+}
+
+/*! \reimp */
+bool QSpinBox::event(QEvent *event)
+{
+ Q_D(QSpinBox);
+ if (event->type() == QEvent::StyleChange
+#ifdef Q_WS_MAC
+ || event->type() == QEvent::MacSizeChange
+#endif
+ )
+ d->setLayoutItemMargins(QStyle::SE_SpinBoxLayoutItem);
+ return QAbstractSpinBox::event(event);
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_SPINBOX
diff --git a/src/gui/widgets/qspinbox.h b/src/gui/widgets/qspinbox.h
new file mode 100644
index 0000000000..44c88cc994
--- /dev/null
+++ b/src/gui/widgets/qspinbox.h
@@ -0,0 +1,188 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSPINBOX_H
+#define QSPINBOX_H
+
+#include <QtGui/qabstractspinbox.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+#ifndef QT_NO_SPINBOX
+
+class QSpinBoxPrivate;
+class Q_GUI_EXPORT QSpinBox : public QAbstractSpinBox
+{
+ Q_OBJECT
+
+ Q_PROPERTY(QString suffix READ suffix WRITE setSuffix)
+ Q_PROPERTY(QString prefix READ prefix WRITE setPrefix)
+ Q_PROPERTY(QString cleanText READ cleanText)
+ Q_PROPERTY(int minimum READ minimum WRITE setMinimum)
+ Q_PROPERTY(int maximum READ maximum WRITE setMaximum)
+ Q_PROPERTY(int singleStep READ singleStep WRITE setSingleStep)
+ Q_PROPERTY(int value READ value WRITE setValue NOTIFY valueChanged USER true)
+
+public:
+ explicit QSpinBox(QWidget *parent = 0);
+#ifdef QT3_SUPPORT
+ QT3_SUPPORT_CONSTRUCTOR QSpinBox(QWidget *parent, const char *name);
+ QT3_SUPPORT_CONSTRUCTOR QSpinBox(int min, int max, int step, QWidget *parent,
+ const char *name = 0);
+#endif
+
+ int value() const;
+
+ QString prefix() const;
+ void setPrefix(const QString &prefix);
+
+ QString suffix() const;
+ void setSuffix(const QString &suffix);
+
+ QString cleanText() const;
+
+ int singleStep() const;
+ void setSingleStep(int val);
+
+ int minimum() const;
+ void setMinimum(int min);
+
+ int maximum() const;
+ void setMaximum(int max);
+
+ void setRange(int min, int max);
+
+#ifdef QT3_SUPPORT
+ inline QT3_SUPPORT void setLineStep(int step) { setSingleStep(step); }
+ inline QT3_SUPPORT void setMaxValue(int val) { setMaximum(val); }
+ inline QT3_SUPPORT void setMinValue(int val) { setMinimum(val); }
+ inline QT3_SUPPORT int maxValue() const { return maximum(); }
+ inline QT3_SUPPORT int minValue() const { return minimum(); }
+#endif
+
+protected:
+ bool event(QEvent *event);
+ virtual QValidator::State validate(QString &input, int &pos) const;
+ virtual int valueFromText(const QString &text) const;
+ virtual QString textFromValue(int val) const;
+ virtual void fixup(QString &str) const;
+
+
+public Q_SLOTS:
+ void setValue(int val);
+
+Q_SIGNALS:
+ void valueChanged(int);
+ void valueChanged(const QString &);
+
+private:
+ Q_DISABLE_COPY(QSpinBox)
+ Q_DECLARE_PRIVATE(QSpinBox)
+};
+
+class QDoubleSpinBoxPrivate;
+class Q_GUI_EXPORT QDoubleSpinBox : public QAbstractSpinBox
+{
+ Q_OBJECT
+
+ Q_PROPERTY(QString prefix READ prefix WRITE setPrefix)
+ Q_PROPERTY(QString suffix READ suffix WRITE setSuffix)
+ Q_PROPERTY(QString cleanText READ cleanText)
+ Q_PROPERTY(int decimals READ decimals WRITE setDecimals)
+ Q_PROPERTY(double minimum READ minimum WRITE setMinimum)
+ Q_PROPERTY(double maximum READ maximum WRITE setMaximum)
+ Q_PROPERTY(double singleStep READ singleStep WRITE setSingleStep)
+ Q_PROPERTY(double value READ value WRITE setValue NOTIFY valueChanged USER true)
+public:
+ explicit QDoubleSpinBox(QWidget *parent = 0);
+
+ double value() const;
+
+ QString prefix() const;
+ void setPrefix(const QString &prefix);
+
+ QString suffix() const;
+ void setSuffix(const QString &suffix);
+
+ QString cleanText() const;
+
+ double singleStep() const;
+ void setSingleStep(double val);
+
+ double minimum() const;
+ void setMinimum(double min);
+
+ double maximum() const;
+ void setMaximum(double max);
+
+ void setRange(double min, double max);
+
+ int decimals() const;
+ void setDecimals(int prec);
+
+ virtual QValidator::State validate(QString &input, int &pos) const;
+ virtual double valueFromText(const QString &text) const;
+ virtual QString textFromValue(double val) const;
+ virtual void fixup(QString &str) const;
+
+public Q_SLOTS:
+ void setValue(double val);
+
+Q_SIGNALS:
+ void valueChanged(double);
+ void valueChanged(const QString &);
+
+private:
+ Q_DISABLE_COPY(QDoubleSpinBox)
+ Q_DECLARE_PRIVATE(QDoubleSpinBox)
+};
+
+#endif // QT_NO_SPINBOX
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QSPINBOX_H
diff --git a/src/gui/widgets/qsplashscreen.cpp b/src/gui/widgets/qsplashscreen.cpp
new file mode 100644
index 0000000000..424009658e
--- /dev/null
+++ b/src/gui/widgets/qsplashscreen.cpp
@@ -0,0 +1,350 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qsplashscreen.h"
+
+#ifndef QT_NO_SPLASHSCREEN
+
+#include "qapplication.h"
+#include "qdesktopwidget.h"
+#include "qpainter.h"
+#include "qpixmap.h"
+#include "qtextdocument.h"
+#include "qtextcursor.h"
+#include <QtCore/qdebug.h>
+#include <private/qwidget_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QSplashScreenPrivate : public QWidgetPrivate
+{
+ Q_DECLARE_PUBLIC(QSplashScreen)
+public:
+ QPixmap pixmap;
+ QString currStatus;
+ QColor currColor;
+ int currAlign;
+
+ inline QSplashScreenPrivate();
+ void drawContents();
+};
+
+/*!
+ \class QSplashScreen
+ \brief The QSplashScreen widget provides a splash screen that can
+ be shown during application startup.
+
+ \ingroup misc
+ \mainclass
+
+ A splash screen is a widget that is usually displayed when an
+ application is being started. Splash screens are often used for
+ applications that have long start up times (e.g. database or
+ networking applications that take time to establish connections) to
+ provide the user with feedback that the application is loading.
+
+ The splash screen appears in the center of the screen. It may be
+ useful to add the Qt::WindowStaysOnTopHint to the splash widget's
+ window flags if you want to keep it above all the other windows on
+ the desktop.
+
+ Some X11 window managers do not support the "stays on top" flag. A
+ solution is to set up a timer that periodically calls raise() on
+ the splash screen to simulate the "stays on top" effect.
+
+ The most common usage is to show a splash screen before the main
+ widget is displayed on the screen. This is illustrated in the
+ following code snippet in which a splash screen is displayed and
+ some initialization tasks are performed before the application's
+ main window is shown:
+
+ \snippet doc/src/snippets/qsplashscreen/main.cpp 0
+ \dots
+ \snippet doc/src/snippets/qsplashscreen/main.cpp 1
+
+ The user can hide the splash screen by clicking on it with the
+ mouse. Since the splash screen is typically displayed before the
+ event loop has started running, it is necessary to periodically
+ call QApplication::processEvents() to receive the mouse clicks.
+
+ It is sometimes useful to update the splash screen with messages,
+ for example, announcing connections established or modules loaded
+ as the application starts up:
+
+ \snippet doc/src/snippets/code/src_gui_widgets_qsplashscreen.cpp 0
+
+ QSplashScreen supports this with the showMessage() function. If you
+ wish to do your own drawing you can get a pointer to the pixmap
+ used in the splash screen with pixmap(). Alternatively, you can
+ subclass QSplashScreen and reimplement drawContents().
+*/
+
+/*!
+ Construct a splash screen that will display the \a pixmap.
+
+ There should be no need to set the widget flags, \a f, except
+ perhaps Qt::WindowStaysOnTopHint.
+*/
+QSplashScreen::QSplashScreen(const QPixmap &pixmap, Qt::WindowFlags f)
+ : QWidget(*(new QSplashScreenPrivate()), 0, Qt::SplashScreen | f)
+{
+ d_func()->pixmap = pixmap;
+ setPixmap(d_func()->pixmap); // Does an implicit repaint
+}
+
+/*!
+ \overload
+
+ This function allows you to specify a parent for your splashscreen. The
+ typical use for this constructor is if you have a multiple screens and
+ prefer to have the splash screen on a different screen than your primary
+ one. In that case pass the proper desktop() as the \a parent.
+*/
+QSplashScreen::QSplashScreen(QWidget *parent, const QPixmap &pixmap, Qt::WindowFlags f)
+ : QWidget(*new QSplashScreenPrivate, parent, Qt::SplashScreen | f)
+{
+ d_func()->pixmap = pixmap;
+ setPixmap(d_func()->pixmap); // Does an implicit repaint
+}
+
+/*!
+ Destructor.
+*/
+QSplashScreen::~QSplashScreen()
+{
+}
+
+/*!
+ \reimp
+*/
+void QSplashScreen::mousePressEvent(QMouseEvent *)
+{
+ hide();
+}
+
+/*!
+ This overrides QWidget::repaint(). It differs from the standard
+ repaint function in that it also calls QApplication::flush() to
+ ensure the updates are displayed, even when there is no event loop
+ present.
+*/
+void QSplashScreen::repaint()
+{
+ d_func()->drawContents();
+ QWidget::repaint();
+ QApplication::flush();
+}
+
+/*!
+ \fn QSplashScreen::messageChanged(const QString &message)
+
+ This signal is emitted when the message on the splash screen
+ changes. \a message is the new message and is a null-string
+ when the message has been removed.
+
+ \sa showMessage(), clearMessage()
+*/
+
+
+
+/*!
+ Draws the \a message text onto the splash screen with color \a
+ color and aligns the text according to the flags in \a alignment.
+
+ \sa Qt::Alignment, clearMessage()
+*/
+void QSplashScreen::showMessage(const QString &message, int alignment,
+ const QColor &color)
+{
+ Q_D(QSplashScreen);
+ d->currStatus = message;
+ d->currAlign = alignment;
+ d->currColor = color;
+ emit messageChanged(d->currStatus);
+ repaint();
+}
+
+/*!
+ Removes the message being displayed on the splash screen
+
+ \sa showMessage()
+ */
+void QSplashScreen::clearMessage()
+{
+ d_func()->currStatus.clear();
+ emit messageChanged(d_func()->currStatus);
+ repaint();
+}
+
+/*!
+ Makes the splash screen wait until the widget \a mainWin is displayed
+ before calling close() on itself.
+*/
+void QSplashScreen::finish(QWidget *mainWin)
+{
+ if (mainWin) {
+#if defined(Q_WS_X11)
+ extern void qt_x11_wait_for_window_manager(QWidget *mainWin);
+ qt_x11_wait_for_window_manager(mainWin);
+#endif
+ }
+ close();
+}
+
+/*!
+ Sets the pixmap that will be used as the splash screen's image to
+ \a pixmap.
+*/
+void QSplashScreen::setPixmap(const QPixmap &pixmap)
+{
+ Q_D(QSplashScreen);
+
+ if (pixmap.hasAlpha()) {
+ QPixmap opaque(pixmap.size());
+ QPainter p(&opaque);
+ p.fillRect(0, 0, pixmap.width(), pixmap.height(), palette().background());
+ p.drawPixmap(0, 0, pixmap);
+ p.end();
+ d->pixmap = opaque;
+ } else {
+ d->pixmap = pixmap;
+ }
+
+ QRect r(0, 0, d->pixmap.size().width(), d->pixmap.size().height());
+ resize(d->pixmap.size());
+ move(QApplication::desktop()->screenGeometry().center() - r.center());
+ if (!isVisible())
+ d->drawContents();
+ else
+ repaint();
+}
+
+/*!
+ Returns the pixmap that is used in the splash screen. The image
+ does not have any of the text drawn by showMessage() calls.
+*/
+const QPixmap QSplashScreen::pixmap() const
+{
+ return d_func()->pixmap;
+}
+
+/*!
+ \internal
+*/
+void QSplashScreenPrivate::drawContents()
+{
+ Q_Q(QSplashScreen);
+ QPixmap textPix = pixmap;
+ if (!textPix.isNull()) {
+ QPainter painter(&textPix);
+ painter.initFrom(q);
+ q->drawContents(&painter);
+ QPalette p = q->palette();
+ p.setBrush(q->backgroundRole(), QBrush(textPix));
+ q->setPalette(p);
+ }
+}
+
+/*!
+ \internal
+*/
+inline QSplashScreenPrivate::QSplashScreenPrivate() : currAlign(Qt::AlignLeft)
+{
+}
+
+/*!
+ Draw the contents of the splash screen using painter \a painter.
+ The default implementation draws the message passed by showMessage().
+ Reimplement this function if you want to do your own drawing on
+ the splash screen.
+*/
+void QSplashScreen::drawContents(QPainter *painter)
+{
+ Q_D(QSplashScreen);
+ painter->setPen(d->currColor);
+ QRect r = rect();
+ r.setRect(r.x() + 5, r.y() + 5, r.width() - 10, r.height() - 10);
+ if (Qt::mightBeRichText(d->currStatus)) {
+ QTextDocument doc;
+#ifdef QT_NO_TEXTHTMLPARSER
+ doc.setPlainText(d->currStatus);
+#else
+ doc.setHtml(d->currStatus);
+#endif
+ doc.setTextWidth(r.width());
+ QTextCursor cursor(&doc);
+ cursor.select(QTextCursor::Document);
+ QTextBlockFormat fmt;
+ fmt.setAlignment(Qt::Alignment(d->currAlign));
+ cursor.mergeBlockFormat(fmt);
+ painter->save();
+ painter->translate(r.topLeft());
+ doc.drawContents(painter);
+ painter->restore();
+ } else {
+ painter->drawText(r, d->currAlign, d->currStatus);
+ }
+}
+
+/*!
+ \fn void QSplashScreen::message(const QString &message, int alignment,
+ const QColor &color)
+ \compat
+
+ Use showMessage() instead.
+*/
+
+/*!
+ \fn void QSplashScreen::clear()
+ \compat
+
+ Use clearMessage() instead.
+*/
+
+/*! \reimp */
+bool QSplashScreen::event(QEvent *e)
+{
+ return QWidget::event(e);
+}
+
+QT_END_NAMESPACE
+
+#endif //QT_NO_SPLASHSCREEN
diff --git a/src/gui/widgets/qsplashscreen.h b/src/gui/widgets/qsplashscreen.h
new file mode 100644
index 0000000000..3a2be1026b
--- /dev/null
+++ b/src/gui/widgets/qsplashscreen.h
@@ -0,0 +1,99 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSPLASHSCREEN_H
+#define QSPLASHSCREEN_H
+
+#include <QtGui/qpixmap.h>
+#include <QtGui/qwidget.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+#ifndef QT_NO_SPLASHSCREEN
+class QSplashScreenPrivate;
+
+class Q_GUI_EXPORT QSplashScreen : public QWidget
+{
+ Q_OBJECT
+public:
+ explicit QSplashScreen(const QPixmap &pixmap = QPixmap(), Qt::WindowFlags f = 0);
+ QSplashScreen(QWidget *parent, const QPixmap &pixmap = QPixmap(), Qt::WindowFlags f = 0);
+ virtual ~QSplashScreen();
+
+ void setPixmap(const QPixmap &pixmap);
+ const QPixmap pixmap() const;
+ void finish(QWidget *w);
+ void repaint();
+
+public Q_SLOTS:
+ void showMessage(const QString &message, int alignment = Qt::AlignLeft,
+ const QColor &color = Qt::black);
+ void clearMessage();
+#ifdef QT3_SUPPORT
+ inline QT_MOC_COMPAT void message(const QString &str, int alignment = Qt::AlignLeft,
+ const QColor &color = Qt::black) { showMessage(str, alignment, color); }
+ inline QT_MOC_COMPAT void clear() { clearMessage(); }
+#endif
+
+Q_SIGNALS:
+ void messageChanged(const QString &message);
+
+protected:
+ bool event(QEvent *e);
+ virtual void drawContents(QPainter *painter);
+ void mousePressEvent(QMouseEvent *);
+
+private:
+ Q_DISABLE_COPY(QSplashScreen)
+ Q_DECLARE_PRIVATE(QSplashScreen)
+};
+
+#endif // QT_NO_SPLASHSCREEN
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QSPLASHSCREEN_H
diff --git a/src/gui/widgets/qsplitter.cpp b/src/gui/widgets/qsplitter.cpp
new file mode 100644
index 0000000000..bf8af35075
--- /dev/null
+++ b/src/gui/widgets/qsplitter.cpp
@@ -0,0 +1,1831 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qsplitter.h"
+#ifndef QT_NO_SPLITTER
+
+#include "qapplication.h"
+#include "qcursor.h"
+#include "qdrawutil.h"
+#include "qevent.h"
+#include "qlayout.h"
+#include "qlist.h"
+#include "qpainter.h"
+#include "qrubberband.h"
+#include "qstyle.h"
+#include "qstyleoption.h"
+#include "qtextstream.h"
+#include "qvarlengtharray.h"
+#include "qvector.h"
+#include "private/qlayoutengine_p.h"
+#include "private/qsplitter_p.h"
+#include "qtimer.h"
+#include "qdebug.h"
+
+#include <ctype.h>
+
+QT_BEGIN_NAMESPACE
+
+//#define QSPLITTER_DEBUG
+
+/*!
+ \class QSplitterHandle
+ \brief The QSplitterHandle class provides handle functionality of the splitter.
+
+ \ingroup organizers
+
+ QSplitterHandle is typically what people think about when they think about
+ a splitter. It is the handle that is used to resize the widgets.
+
+ A typical developer using QSplitter will never have to worry about
+ QSplitterHandle. It is provided for developers who want splitter handles
+ that provide extra features, such as popup menus.
+
+ The typical way one would create splitter handles is to subclass QSplitter then
+ reimplement QSplitter::createHandle() to instantiate the custom splitter
+ handle. For example, a minimum QSplitter subclass might look like this:
+
+ \snippet doc/src/snippets/splitterhandle/splitter.h 0
+
+ The \l{QSplitter::}{createHandle()} implementation simply constructs a
+ custom splitter handle, called \c Splitter in this example:
+
+ \snippet doc/src/snippets/splitterhandle/splitter.cpp 1
+
+ Information about a given handle can be obtained using functions like
+ orientation() and opaqueResize(), and is retrieved from its parent splitter.
+ Details like these can be used to give custom handles different appearances
+ depending on the splitter's orientation.
+
+ The complexity of a custom handle subclass depends on the tasks that it
+ needs to perform. A simple subclass might only provide a paintEvent()
+ implementation:
+
+ \snippet doc/src/snippets/splitterhandle/splitter.cpp 0
+
+ In this example, a predefined gradient is set up differently depending on
+ the orientation of the handle. QSplitterHandle provides a reasonable
+ size hint for the handle, so the subclass does not need to provide a
+ reimplementation of sizeHint() unless the handle has special size
+ requirements.
+
+ \sa QSplitter
+*/
+
+/*!
+ Creates a QSplitter handle with the given \a orientation and
+ QSplitter \a parent.
+*/
+QSplitterHandle::QSplitterHandle(Qt::Orientation orientation, QSplitter *parent)
+ : QWidget(*new QSplitterHandlePrivate, parent, 0)
+{
+ Q_D(QSplitterHandle);
+ d->s = parent;
+ d->hover = false;
+ setOrientation(orientation);
+}
+
+/*!
+ Sets the orientation of the splitter handle to \a orientation.
+ This is usually propogated from the QSplitter.
+
+ \sa QSplitter::setOrientation()
+*/
+void QSplitterHandle::setOrientation(Qt::Orientation orientation)
+{
+ Q_D(QSplitterHandle);
+ d->orient = orientation;
+#ifndef QT_NO_CURSOR
+ setCursor(orientation == Qt::Horizontal ? Qt::SplitHCursor : Qt::SplitVCursor);
+#endif
+}
+
+/*!
+ Returns the handle's orientation. This is usually propagated from the QSplitter.
+
+ \sa QSplitter::orientation()
+*/
+Qt::Orientation QSplitterHandle::orientation() const
+{
+ Q_D(const QSplitterHandle);
+ return d->orient;
+}
+
+
+/*!
+ Returns true if widgets are resized dynamically (opaquely), otherwise
+ returns false. This value is controlled by the QSplitter.
+
+ \sa QSplitter::opaqueResize()
+
+*/
+bool QSplitterHandle::opaqueResize() const
+{
+ Q_D(const QSplitterHandle);
+ return d->s->opaqueResize();
+}
+
+
+/*!
+ Returns the splitter associated with this splitter handle.
+
+ \sa QSplitter::handle()
+*/
+QSplitter *QSplitterHandle::splitter() const
+{
+ return d_func()->s;
+}
+
+/*!
+ Tells the splitter to move this handle to position \a pos, which is
+ the distance from the left or top edge of the widget.
+
+ Note that \a pos is also measured from the left (or top) for
+ right-to-left languages. This function will map \a pos to the
+ appropriate position before calling QSplitter::moveSplitter().
+
+ \sa QSplitter::moveSplitter() closestLegalPosition()
+*/
+void QSplitterHandle::moveSplitter(int pos)
+{
+ Q_D(QSplitterHandle);
+ if (d->s->isRightToLeft() && d->orient == Qt::Horizontal)
+ pos = d->s->contentsRect().width() - pos;
+ d->s->moveSplitter(pos, d->s->indexOf(this));
+}
+
+/*!
+ Returns the closest legal position to \a pos of the splitter
+ handle. The positions are measured from the left or top edge of
+ the splitter, even for right-to-left languages.
+
+ \sa QSplitter::closestLegalPosition(), moveSplitter()
+*/
+
+int QSplitterHandle::closestLegalPosition(int pos)
+{
+ Q_D(QSplitterHandle);
+ QSplitter *s = d->s;
+ if (s->isRightToLeft() && d->orient == Qt::Horizontal) {
+ int w = s->contentsRect().width();
+ return w - s->closestLegalPosition(w - pos, s->indexOf(this));
+ }
+ return s->closestLegalPosition(pos, s->indexOf(this));
+}
+
+/*!
+ \reimp
+*/
+QSize QSplitterHandle::sizeHint() const
+{
+ Q_D(const QSplitterHandle);
+ int hw = d->s->handleWidth();
+ QStyleOption opt(0);
+ opt.init(d->s);
+ opt.state = QStyle::State_None;
+ return parentWidget()->style()->sizeFromContents(QStyle::CT_Splitter, &opt, QSize(hw, hw), d->s)
+ .expandedTo(QApplication::globalStrut());
+}
+
+/*!
+ \reimp
+*/
+bool QSplitterHandle::event(QEvent *event)
+{
+ Q_D(QSplitterHandle);
+ switch(event->type()) {
+ case QEvent::HoverEnter:
+ d->hover = true;
+ update();
+ break;
+ case QEvent::HoverLeave:
+ d->hover = false;
+ update();
+ break;
+ default:
+ break;
+ }
+ return QWidget::event(event);
+}
+
+/*!
+ \reimp
+*/
+void QSplitterHandle::mouseMoveEvent(QMouseEvent *e)
+{
+ Q_D(QSplitterHandle);
+ if (!(e->buttons() & Qt::LeftButton))
+ return;
+ int pos = d->pick(parentWidget()->mapFromGlobal(e->globalPos()))
+ - d->mouseOffset;
+ if (opaqueResize()) {
+ moveSplitter(pos);
+ } else {
+ d->s->setRubberBand(closestLegalPosition(pos));
+ }
+}
+
+/*!
+ \reimp
+*/
+void QSplitterHandle::mousePressEvent(QMouseEvent *e)
+{
+ Q_D(QSplitterHandle);
+ if (e->button() == Qt::LeftButton)
+ d->mouseOffset = d->pick(e->pos());
+}
+
+/*!
+ \reimp
+*/
+void QSplitterHandle::mouseReleaseEvent(QMouseEvent *e)
+{
+ Q_D(QSplitterHandle);
+ if (!opaqueResize() && e->button() == Qt::LeftButton) {
+ int pos = d->pick(parentWidget()->mapFromGlobal(e->globalPos()))
+ - d->mouseOffset;
+ d->s->setRubberBand(-1);
+ moveSplitter(pos);
+ }
+}
+
+/*!
+ \reimp
+*/
+void QSplitterHandle::paintEvent(QPaintEvent *)
+{
+ Q_D(QSplitterHandle);
+ QPainter p(this);
+ QStyleOption opt(0);
+ opt.rect = rect();
+ opt.palette = palette();
+ if (orientation() == Qt::Horizontal)
+ opt.state = QStyle::State_Horizontal;
+ else
+ opt.state = QStyle::State_None;
+ if (d->hover)
+ opt.state |= QStyle::State_MouseOver;
+ if (isEnabled())
+ opt.state |= QStyle::State_Enabled;
+ parentWidget()->style()->drawControl(QStyle::CE_Splitter, &opt, &p, d->s);
+}
+
+
+int QSplitterLayoutStruct::getWidgetSize(Qt::Orientation orient)
+{
+ if (sizer == -1) {
+ QSize s = widget->sizeHint();
+ const int presizer = pick(s, orient);
+ const int realsize = pick(widget->size(), orient);
+ if (!s.isValid() || (widget->testAttribute(Qt::WA_Resized) && (realsize > presizer))) {
+ sizer = pick(widget->size(), orient);
+ } else {
+ sizer = presizer;
+ }
+ QSizePolicy p = widget->sizePolicy();
+ int sf = (orient == Qt::Horizontal) ? p.horizontalStretch() : p.verticalStretch();
+ if (sf > 1)
+ sizer *= sf;
+ }
+ return sizer;
+}
+
+int QSplitterLayoutStruct::getHandleSize(Qt::Orientation orient)
+{
+ return pick(handle->sizeHint(), orient);
+}
+
+void QSplitterPrivate::init()
+{
+ Q_Q(QSplitter);
+ QSizePolicy sp(QSizePolicy::Expanding, QSizePolicy::Preferred);
+ if (orient == Qt::Vertical)
+ sp.transpose();
+ q->setSizePolicy(sp);
+ q->setAttribute(Qt::WA_WState_OwnSizePolicy, false);
+}
+
+void QSplitterPrivate::recalc(bool update)
+{
+ Q_Q(QSplitter);
+ int n = list.count();
+ /*
+ Splitter handles before the first visible widget or right
+ before a hidden widget must be hidden.
+ */
+ bool first = true;
+ for (int i = 0; i < n ; ++i) {
+ QSplitterLayoutStruct *s = list.at(i);
+ s->handle->setHidden(first || s->widget->isHidden());
+ if (!s->widget->isHidden())
+ first = false;
+ }
+
+ int fi = 2 * q->frameWidth();
+ int maxl = fi;
+ int minl = fi;
+ int maxt = QWIDGETSIZE_MAX;
+ int mint = fi;
+ /*
+ calculate min/max sizes for the whole splitter
+ */
+ bool empty = true;
+ for (int j = 0; j < n; j++) {
+ QSplitterLayoutStruct *s = list.at(j);
+
+ if (!s->widget->isHidden()) {
+ empty = false;
+ if (!s->handle->isHidden()) {
+ minl += s->getHandleSize(orient);
+ maxl += s->getHandleSize(orient);
+ }
+
+ QSize minS = qSmartMinSize(s->widget);
+ minl += pick(minS);
+ maxl += pick(s->widget->maximumSize());
+ mint = qMax(mint, trans(minS));
+ int tm = trans(s->widget->maximumSize());
+ if (tm > 0)
+ maxt = qMin(maxt, tm);
+ }
+ }
+
+ if (empty) {
+ if (qobject_cast<QSplitter *>(q->parentWidget())) {
+ // nested splitters; be nice
+ maxl = maxt = 0;
+ } else {
+ // QSplitter with no children yet
+ maxl = QWIDGETSIZE_MAX;
+ }
+ } else {
+ maxl = qMin<int>(maxl, QWIDGETSIZE_MAX);
+ }
+ if (maxt < mint)
+ maxt = mint;
+
+ if (update) {
+ if (orient == Qt::Horizontal) {
+ q->setMaximumSize(maxl, maxt);
+ if (q->isWindow())
+ q->setMinimumSize(minl,mint);
+ } else {
+ q->setMaximumSize(maxt, maxl);
+ if (q->isWindow())
+ q->setMinimumSize(mint,minl);
+ }
+ doResize();
+ q->updateGeometry();
+ } else {
+ firstShow = true;
+ }
+}
+
+void QSplitterPrivate::doResize()
+{
+ Q_Q(QSplitter);
+ QRect r = q->contentsRect();
+ int n = list.count();
+ QVector<QLayoutStruct> a(n*2);
+ int i;
+
+ bool noStretchFactorsSet = true;
+ for (i = 0; i < n; ++i) {
+ QSizePolicy p = list.at(i)->widget->sizePolicy();
+ int sf = orient == Qt::Horizontal ? p.horizontalStretch() : p.verticalStretch();
+ if (sf != 0) {
+ noStretchFactorsSet = false;
+ break;
+ }
+ }
+
+ int j=0;
+ for (i = 0; i < n; ++i) {
+ QSplitterLayoutStruct *s = list.at(i);
+#ifdef QSPLITTER_DEBUG
+ qDebug("widget %d hidden: %d collapsed: %d handle hidden: %d", i, s->widget->isHidden(),
+ s->collapsed, s->handle->isHidden());
+#endif
+
+ a[j].init();
+ if (s->handle->isHidden()) {
+ a[j].maximumSize = 0;
+ } else {
+ a[j].sizeHint = a[j].minimumSize = a[j].maximumSize = s->getHandleSize(orient);
+ a[j].empty = false;
+ }
+ ++j;
+
+ a[j].init();
+ if (s->widget->isHidden() || s->collapsed) {
+ a[j].maximumSize = 0;
+ } else {
+ a[j].minimumSize = pick(qSmartMinSize(s->widget));
+ a[j].maximumSize = pick(s->widget->maximumSize());
+ a[j].empty = false;
+
+ bool stretch = noStretchFactorsSet;
+ if (!stretch) {
+ QSizePolicy p = s->widget->sizePolicy();
+ int sf = orient == Qt::Horizontal ? p.horizontalStretch() : p.verticalStretch();
+ stretch = (sf != 0);
+ }
+ if (stretch) {
+ a[j].stretch = s->getWidgetSize(orient);
+ a[j].sizeHint = a[j].minimumSize;
+ a[j].expansive = true;
+ } else {
+ a[j].sizeHint = qMax(s->getWidgetSize(orient), a[j].minimumSize);
+ }
+ }
+ ++j;
+ }
+
+ qGeomCalc(a, 0, n*2, pick(r.topLeft()), pick(r.size()), 0);
+
+#ifdef QSPLITTER_DEBUG
+ for (i = 0; i < n*2; ++i) {
+ qDebug("%*s%d: stretch %d, sh %d, minS %d, maxS %d, exp %d, emp %d -> %d, %d",
+ i, "", i,
+ a[i].stretch,
+ a[i].sizeHint,
+ a[i].minimumSize,
+ a[i].maximumSize,
+ a[i].expansive,
+ a[i].empty,
+ a[i].pos,
+ a[i].size);
+ }
+#endif
+
+ for (i = 0; i < n; ++i) {
+ QSplitterLayoutStruct *s = list.at(i);
+ setGeo(s, a[i*2+1].pos, a[i*2+1].size, false);
+ }
+}
+
+void QSplitterPrivate::storeSizes()
+{
+ for (int i = 0; i < list.size(); ++i) {
+ QSplitterLayoutStruct *sls = list.at(i);
+ sls->sizer = pick(sls->rect.size());
+ }
+}
+
+void QSplitterPrivate::addContribution(int index, int *min, int *max, bool mayCollapse) const
+{
+ QSplitterLayoutStruct *s = list.at(index);
+ if (!s->widget->isHidden()) {
+ if (!s->handle->isHidden()) {
+ *min += s->getHandleSize(orient);
+ *max += s->getHandleSize(orient);
+ }
+ if (mayCollapse || !s->collapsed)
+ *min += pick(qSmartMinSize(s->widget));
+
+ *max += pick(s->widget->maximumSize());
+ }
+}
+
+int QSplitterPrivate::findWidgetJustBeforeOrJustAfter(int index, int delta, int &collapsibleSize) const
+{
+ if (delta < 0)
+ index += delta;
+ do {
+ QWidget *w = list.at(index)->widget;
+ if (!w->isHidden()) {
+ if (collapsible(list.at(index)))
+ collapsibleSize = pick(qSmartMinSize(w));
+ return index;
+ }
+ index += delta;
+ } while (index >= 0 && index < list.count());
+
+ return -1;
+}
+
+/*
+ For the splitter handle with index \a index, \a min and \a max give the range without collapsing any widgets,
+ and \a farMin and farMax give the range with collapsing included.
+*/
+void QSplitterPrivate::getRange(int index, int *farMin, int *min, int *max, int *farMax) const
+{
+ Q_Q(const QSplitter);
+ int n = list.count();
+ if (index <= 0 || index >= n)
+ return;
+
+ int collapsibleSizeBefore = 0;
+ int idJustBefore = findWidgetJustBeforeOrJustAfter(index, -1, collapsibleSizeBefore);
+
+ int collapsibleSizeAfter = 0;
+ int idJustAfter = findWidgetJustBeforeOrJustAfter(index, +1, collapsibleSizeAfter);
+
+ int minBefore = 0;
+ int minAfter = 0;
+ int maxBefore = 0;
+ int maxAfter = 0;
+ int i;
+
+ for (i = 0; i < index; ++i)
+ addContribution(i, &minBefore, &maxBefore, i == idJustBefore);
+ for (i = index; i < n; ++i)
+ addContribution(i, &minAfter, &maxAfter, i == idJustAfter);
+
+ QRect r = q->contentsRect();
+ int farMinVal;
+ int minVal;
+ int maxVal;
+ int farMaxVal;
+
+ int smartMinBefore = qMax(minBefore, pick(r.size()) - maxAfter);
+ int smartMaxBefore = qMin(maxBefore, pick(r.size()) - minAfter);
+
+ minVal = pick(r.topLeft()) + smartMinBefore;
+ maxVal = pick(r.topLeft()) + smartMaxBefore;
+
+ farMinVal = minVal;
+ if (minBefore - collapsibleSizeBefore >= pick(r.size()) - maxAfter)
+ farMinVal -= collapsibleSizeBefore;
+ farMaxVal = maxVal;
+ if (pick(r.size()) - (minAfter - collapsibleSizeAfter) <= maxBefore)
+ farMaxVal += collapsibleSizeAfter;
+
+ if (farMin)
+ *farMin = farMinVal;
+ if (min)
+ *min = minVal;
+ if (max)
+ *max = maxVal;
+ if (farMax)
+ *farMax = farMaxVal;
+}
+
+int QSplitterPrivate::adjustPos(int pos, int index, int *farMin, int *min, int *max, int *farMax) const
+{
+ const int Threshold = 40;
+
+ getRange(index, farMin, min, max, farMax);
+
+ if (pos >= *min) {
+ if (pos <= *max) {
+ return pos;
+ } else {
+ int delta = pos - *max;
+ int width = *farMax - *max;
+
+ if (delta > width / 2 && delta >= qMin(Threshold, width)) {
+ return *farMax;
+ } else {
+ return *max;
+ }
+ }
+ } else {
+ int delta = *min - pos;
+ int width = *min - *farMin;
+
+ if (delta > width / 2 && delta >= qMin(Threshold, width)) {
+ return *farMin;
+ } else {
+ return *min;
+ }
+ }
+}
+
+bool QSplitterPrivate::collapsible(QSplitterLayoutStruct *s) const
+{
+ if (s->collapsible != Default) {
+ return (bool)s->collapsible;
+ } else {
+ return childrenCollapsible;
+ }
+}
+
+void QSplitterPrivate::updateHandles()
+{
+ Q_Q(QSplitter);
+ recalc(q->isVisible());
+}
+
+void QSplitterPrivate::setSizes_helper(const QList<int> &sizes, bool clampNegativeSize)
+{
+ int j = 0;
+
+ for (int i = 0; i < list.size(); ++i) {
+ QSplitterLayoutStruct *s = list.at(i);
+
+ s->collapsed = false;
+ s->sizer = sizes.value(j++);
+ if (clampNegativeSize && s->sizer < 0)
+ s->sizer = 0;
+ int smartMinSize = pick(qSmartMinSize(s->widget));
+
+ // Make sure that we reset the collapsed state.
+ if (s->sizer == 0) {
+ if (collapsible(s) && smartMinSize > 0) {
+ s->collapsed = true;
+ } else {
+ s->sizer = smartMinSize;
+ }
+ } else {
+ if (s->sizer < smartMinSize)
+ s->sizer = smartMinSize;
+ }
+ }
+ doResize();
+}
+
+void QSplitterPrivate::setGeo(QSplitterLayoutStruct *sls, int p, int s, bool allowCollapse)
+{
+ Q_Q(QSplitter);
+ QWidget *w = sls->widget;
+ QRect r;
+ QRect contents = q->contentsRect();
+ if (orient == Qt::Horizontal) {
+ r.setRect(p, contents.y(), s, contents.height());
+ } else {
+ r.setRect(contents.x(), p, contents.width(), s);
+ }
+ sls->rect = r;
+
+ int minSize = pick(qSmartMinSize(w));
+
+ if (orient == Qt::Horizontal && q->isRightToLeft())
+ r.moveRight(contents.width() - r.left());
+
+ if (allowCollapse)
+ sls->collapsed = s <= 0 && minSize > 0 && !w->isHidden();
+
+ // Hide the child widget, but without calling hide() so that
+ // the splitter handle is still shown.
+ if (sls->collapsed)
+ r.moveTopLeft(QPoint(-r.width()-1, -r.height()-1));
+
+ w->setGeometry(r);
+
+ if (!sls->handle->isHidden()) {
+ QSplitterHandle *h = sls->handle;
+ QSize hs = h->sizeHint();
+ int left, top, right, bottom;
+ h->getContentsMargins(&left, &top, &right, &bottom);
+ if (orient==Qt::Horizontal) {
+ if (q->isRightToLeft())
+ p = contents.width() - p + hs.width();
+ h->setGeometry(p-hs.width() - left, contents.y(), hs.width() + left + right, contents.height());
+ } else {
+ h->setGeometry(contents.x(), p-hs.height() - top, contents.width(), hs.height() + top + bottom);
+ }
+ }
+}
+
+void QSplitterPrivate::doMove(bool backwards, int hPos, int index, int delta, bool mayCollapse,
+ int *positions, int *widths)
+{
+ if (index < 0 || index >= list.count())
+ return;
+
+#ifdef QSPLITTER_DEBUG
+ qDebug() << "QSplitterPrivate::doMove" << backwards << hPos << index << delta << mayCollapse;
+#endif
+
+ QSplitterLayoutStruct *s = list.at(index);
+ QWidget *w = s->widget;
+
+ int nextId = backwards ? index - delta : index + delta;
+
+ if (w->isHidden()) {
+ doMove(backwards, hPos, nextId, delta, collapsible(nextId), positions, widths);
+ } else {
+ int hs =s->handle->isHidden() ? 0 : s->getHandleSize(orient);
+
+ int ws = backwards ? hPos - pick(s->rect.topLeft())
+ : pick(s->rect.bottomRight()) - hPos -hs + 1;
+ if (ws > 0 || (!s->collapsed && !mayCollapse)) {
+ ws = qMin(ws, pick(w->maximumSize()));
+ ws = qMax(ws, pick(qSmartMinSize(w)));
+ } else {
+ ws = 0;
+ }
+ positions[index] = backwards ? hPos - ws : hPos + hs;
+ widths[index] = ws;
+ doMove(backwards, backwards ? hPos - ws - hs : hPos + hs + ws, nextId, delta,
+ collapsible(nextId), positions, widths);
+ }
+
+}
+
+QSplitterLayoutStruct *QSplitterPrivate::findWidget(QWidget *w) const
+{
+ for (int i = 0; i < list.size(); ++i) {
+ if (list.at(i)->widget == w)
+ return list.at(i);
+ }
+ return 0;
+}
+
+#ifdef QT3_SUPPORT
+static void setStretch(QWidget *w, int sf)
+{
+ QSizePolicy sp = w->sizePolicy();
+ sp.setHorizontalStretch(sf);
+ sp.setVerticalStretch(sf);
+ w->setSizePolicy(sp);
+}
+
+static int getStretch(const QWidget *w)
+{
+ QSizePolicy sp = w->sizePolicy();
+ return qMax(sp.horizontalStretch(), sp.verticalStretch());
+}
+
+void QSplitter::setResizeMode(QWidget *w, ResizeMode mode)
+{
+ /*
+ Internal comment:
+
+ This function tries to simulate the Qt 3.x ResizeMode
+ behavior using QSizePolicy stretch factors. This isn't easy,
+ because the default \l ResizeMode was \l Stretch, not \l
+ KeepSize, whereas the default stetch factor is 0.
+
+ So what we do is this: When the user calls setResizeMode()
+ the first time, we iterate through all the child widgets and
+ set their stretch factors to 1. Later on, if children are
+ added (using addWidget()), their stretch factors are also set
+ to 1.
+
+ There is just one problem left: Often, setResizeMode() is
+ called \e{before} addWidget(), because addWidget() is called
+ from the event loop. In that case, we use a special value,
+ 243, instead of 0 to prevent 0 from being overwritten with 1
+ in addWidget(). This is a wicked hack, but fortunately it
+ only occurs as a result of calling a \c QT3_SUPPORT function.
+ */
+
+ Q_D(QSplitter);
+ bool metWidget = false;
+ if (!d->compatMode) {
+ d->compatMode = true;
+ for (int i = 0; i < d->list.size(); ++i) {
+ QSplitterLayoutStruct *s = d->list.at(i);
+ if (s->widget == w)
+ metWidget = true;
+ if (getStretch(s->widget) == 0)
+ setStretch(s->widget, 1);
+ }
+ }
+ int sf;
+ if (mode == KeepSize)
+ sf = metWidget ? 0 : 243;
+ else
+ sf = 1;
+ setStretch(w, sf);
+}
+
+/*!
+ Use one of the constructors that doesn't take the \a name
+ argument and then use setObjectName() instead.
+*/
+QSplitter::QSplitter(QWidget *parent, const char *name)
+ : QFrame(*new QSplitterPrivate, parent)
+{
+ Q_D(QSplitter);
+ setObjectName(QString::fromAscii(name));
+ d->orient = Qt::Horizontal;
+ d->init();
+}
+
+
+/*!
+ Use one of the constructors that don't take the \a name argument
+ and then use setObjectName() instead.
+*/
+QSplitter::QSplitter(Qt::Orientation orientation, QWidget *parent, const char *name)
+ : QFrame(*new QSplitterPrivate, parent)
+{
+ Q_D(QSplitter);
+ setObjectName(QString::fromAscii(name));
+ d->orient = orientation;
+ d->init();
+}
+#endif
+
+/*!
+ \internal
+*/
+void QSplitterPrivate::insertWidget_helper(int index, QWidget *widget, bool show)
+{
+ Q_Q(QSplitter);
+ QBoolBlocker b(blockChildAdd);
+ bool needShow = show && q->isVisible() &&
+ !(widget->isHidden() && widget->testAttribute(Qt::WA_WState_ExplicitShowHide));
+ if (widget->parentWidget() != q)
+ widget->setParent(q);
+ if (needShow)
+ widget->show();
+ insertWidget(index, widget);
+ recalc(q->isVisible());
+}
+
+/*
+ Inserts the widget \a w at position \a index in the splitter's list of widgets.
+
+ If \a w is already in the splitter, it will be moved to the new position.
+*/
+
+QSplitterLayoutStruct *QSplitterPrivate::insertWidget(int index, QWidget *w)
+{
+ Q_Q(QSplitter);
+ QSplitterLayoutStruct *sls = 0;
+ int i;
+ int last = list.count();
+ for (i = 0; i < list.size(); ++i) {
+ QSplitterLayoutStruct *s = list.at(i);
+ if (s->widget == w) {
+ sls = s;
+ --last;
+ break;
+ }
+ }
+ if (index < 0 || index > last)
+ index = last;
+
+ if (sls) {
+ list.move(i,index);
+ } else {
+ QSplitterHandle *newHandle = 0;
+ sls = new QSplitterLayoutStruct;
+ QString tmp = QLatin1String("qt_splithandle_");
+ tmp += w->objectName();
+ newHandle = q->createHandle();
+ newHandle->setObjectName(tmp);
+ sls->handle = newHandle;
+ sls->widget = w;
+ w->lower();
+ list.insert(index,sls);
+
+ if (newHandle && q->isVisible())
+ newHandle->show(); // will trigger sending of post events
+
+#ifdef QT3_SUPPORT
+ if (compatMode) {
+ int sf = getStretch(sls->widget);
+ if (sf == 243)
+ setStretch(sls->widget, 0);
+ else if (sf == 0)
+ setStretch(sls->widget, 1);
+ }
+#endif
+ }
+ return sls;
+}
+
+/*!
+ \class QSplitter
+ \brief The QSplitter class implements a splitter widget.
+
+ \ingroup organizers
+ \mainclass
+
+ A splitter lets the user control the size of child widgets by dragging the
+ boundary between the children. Any number of widgets may be controlled by a
+ single splitter. The typical use of a QSplitter is to create several
+ widgets and add them using insertWidget() or addWidget().
+
+ The following example will show a QListView, QTreeView, and
+ QTextEdit side by side, with two splitter handles:
+
+ \snippet doc/src/snippets/splitter/splitter.cpp 0
+
+ If a widget is already inside a QSplitter when insertWidget() or
+ addWidget() is called, it will move to the new position. This can be used
+ to reorder widgets in the splitter later. You can use indexOf(),
+ widget(), and count() to get access to the widgets inside the splitter.
+
+ A default QSplitter lays out its children horizontally (side by side); you
+ can use setOrientation(Qt::Vertical) to lay its
+ children out vertically.
+
+ By default, all widgets can be as large or as small as the user
+ wishes, between the \l minimumSizeHint() (or \l minimumSize())
+ and \l maximumSize() of the widgets.
+
+ QSplitter resizes its children dynamically by default. If you
+ would rather have QSplitter resize the children only at the end of
+ a resize operation, call setOpaqueResize(false).
+
+ The initial distribution of size between the widgets is determined by
+ multiplying the initial size with the stretch factor.
+ You can also use setSizes() to set the sizes
+ of all the widgets. The function sizes() returns the sizes set by the user.
+ Alternatively, you can save and restore the sizes of the widgets from a
+ QByteArray using saveState() and restoreState() respectively.
+
+ When you hide() a child its space will be distributed among the
+ other children. It will be reinstated when you show() it again.
+
+ \sa QSplitterHandle, QHBoxLayout, QVBoxLayout, QTabWidget
+*/
+
+
+/*!
+ Constructs a horizontal splitter with the \a parent
+ arguments is passed on to the QFrame constructor.
+
+ \sa setOrientation()
+*/
+QSplitter::QSplitter(QWidget *parent)
+ : QFrame(*new QSplitterPrivate, parent)
+{
+ Q_D(QSplitter);
+ d->orient = Qt::Horizontal;
+ d->init();
+}
+
+
+/*!
+ Constructs a splitter with the given \a orientation and \a parent.
+
+ \sa setOrientation()
+*/
+QSplitter::QSplitter(Qt::Orientation orientation, QWidget *parent)
+ : QFrame(*new QSplitterPrivate, parent)
+{
+ Q_D(QSplitter);
+ d->orient = orientation;
+ d->init();
+}
+
+
+/*!
+ Destroys the splitter. All children are deleted.
+*/
+
+QSplitter::~QSplitter()
+{
+ Q_D(QSplitter);
+ delete d->rubberBand;
+ while (!d->list.isEmpty())
+ delete d->list.takeFirst();
+}
+
+/*!
+ Updates the splitter's state. You should not need to call this
+ function.
+*/
+void QSplitter::refresh()
+{
+ Q_D(QSplitter);
+ d->recalc(true);
+}
+
+/*!
+ \property QSplitter::orientation
+ \brief the orientation of the splitter
+
+ By default the orientation is horizontal (i.e., the widgets are
+ laid out side by side). The possible orientations are
+ Qt::Horizontal and Qt::Vertical.
+
+ \sa QSplitterHandle::orientation()
+*/
+
+void QSplitter::setOrientation(Qt::Orientation orientation)
+{
+ Q_D(QSplitter);
+ if (d->orient == orientation)
+ return;
+
+ if (!testAttribute(Qt::WA_WState_OwnSizePolicy)) {
+ QSizePolicy sp = sizePolicy();
+ sp.transpose();
+ setSizePolicy(sp);
+ setAttribute(Qt::WA_WState_OwnSizePolicy, false);
+ }
+
+ d->orient = orientation;
+
+ for (int i = 0; i < d->list.size(); ++i) {
+ QSplitterLayoutStruct *s = d->list.at(i);
+ s->handle->setOrientation(orientation);
+ }
+ d->recalc(isVisible());
+}
+
+Qt::Orientation QSplitter::orientation() const
+{
+ Q_D(const QSplitter);
+ return d->orient;
+}
+
+/*!
+ \property QSplitter::childrenCollapsible
+ \brief whether child widgets can be resized down to size 0 by the user
+
+ By default, children are collapsible. It is possible to enable
+ and disable the collapsing of individual children using
+ setCollapsible().
+
+ \sa setCollapsible()
+*/
+
+void QSplitter::setChildrenCollapsible(bool collapse)
+{
+ Q_D(QSplitter);
+ d->childrenCollapsible = collapse;
+}
+
+bool QSplitter::childrenCollapsible() const
+{
+ Q_D(const QSplitter);
+ return d->childrenCollapsible;
+}
+
+/*!
+ Sets whether the child widget at index \a index is collapsible to \a collapse.
+
+ By default, children are collapsible, meaning that the user can
+ resize them down to size 0, even if they have a non-zero
+ minimumSize() or minimumSizeHint(). This behavior can be changed
+ on a per-widget basis by calling this function, or globally for
+ all the widgets in the splitter by setting the \l
+ childrenCollapsible property.
+
+ \sa childrenCollapsible
+*/
+
+void QSplitter::setCollapsible(int index, bool collapse)
+{
+ Q_D(QSplitter);
+
+ if (index < 0 || index >= d->list.size()) {
+ qWarning("QSplitter::setCollapsible: Index %d out of range", index);
+ return;
+ }
+ d->list.at(index)->collapsible = collapse ? 1 : 0;
+}
+
+/*!
+ Returns true if the widget at \a index is collapsible, otherwise returns false
+*/
+bool QSplitter::isCollapsible(int index) const
+{
+ Q_D(const QSplitter);
+ if (index < 0 || index >= d->list.size()) {
+ qWarning("QSplitter::isCollapsible: Index %d out of range", index);
+ return false;
+ }
+ return d->list.at(index)->collapsible;
+}
+
+/*!
+ \reimp
+*/
+void QSplitter::resizeEvent(QResizeEvent *)
+{
+ Q_D(QSplitter);
+ d->doResize();
+}
+
+/*!
+ Adds the given \a widget to the splitter's layout after all the other
+ items.
+
+ If \a widget is already in the splitter, it will be moved to the new position.
+
+ \sa insertWidget() widget() indexOf()
+*/
+void QSplitter::addWidget(QWidget *widget)
+{
+ Q_D(QSplitter);
+ insertWidget(d->list.count(), widget);
+}
+
+/*!
+ Inserts the \a widget specified into the splitter's layout at the
+ given \a index.
+
+ If \a widget is already in the splitter, it will be moved to the new position.
+
+ if \a index is an invalid index, then the widget will be inserted at the end.
+
+ \sa addWidget() indexOf() widget()
+*/
+void QSplitter::insertWidget(int index, QWidget *widget)
+{
+ Q_D(QSplitter);
+ d->insertWidget_helper(index, widget, true);
+}
+
+/*!
+ \fn int QSplitter::indexOf(QWidget *widget) const
+
+ Returns the index in the splitter's layout of the specified \a widget. This
+ also works for handles.
+
+ Handles are numbered from 0. There are as many handles as there
+ are child widgets, but the handle at position 0 is always hidden.
+
+
+ \sa count(), widget()
+*/
+int QSplitter::indexOf(QWidget *w) const
+{
+ Q_D(const QSplitter);
+ for (int i = 0; i < d->list.size(); ++i) {
+ QSplitterLayoutStruct *s = d->list.at(i);
+ if (s->widget == w || s->handle == w)
+ return i;
+ }
+ return -1;
+}
+
+/*!
+ Returns a new splitter handle as a child widget of this splitter.
+ This function can be reimplemented in subclasses to provide support
+ for custom handles.
+
+ \sa handle(), indexOf()
+*/
+QSplitterHandle *QSplitter::createHandle()
+{
+ Q_D(QSplitter);
+ return new QSplitterHandle(d->orient, this);
+}
+
+/*!
+ Returns the handle to the left (or above) for the item in the
+ splitter's layout at the given \a index. The handle at index 0 is
+ always hidden.
+
+ For right-to-left languages such as Arabic and Hebrew, the layout
+ of horizontal splitters is reversed. The handle will be to the
+ right of the widget at \a index.
+
+ \sa count(), widget(), indexOf(), createHandle(), setHandleWidth()
+*/
+QSplitterHandle *QSplitter::handle(int index) const
+{
+ Q_D(const QSplitter);
+ if (index < 0 || index >= d->list.size())
+ return 0;
+ return d->list.at(index)->handle;
+}
+
+/*!
+ Returns the widget at the given \a index in the splitter's layout.
+
+ \sa count(), handle(), indexOf(), insertWidget()
+*/
+QWidget *QSplitter::widget(int index) const
+{
+ Q_D(const QSplitter);
+ if (index < 0 || index >= d->list.size())
+ return 0;
+ return d->list.at(index)->widget;
+}
+
+/*!
+ Returns the number of widgets contained in the splitter's layout.
+
+ \sa widget(), handle()
+*/
+int QSplitter::count() const
+{
+ Q_D(const QSplitter);
+ return d->list.count();
+}
+
+/*!
+ \reimp
+
+ Tells the splitter that the child widget described by \a c has been
+ inserted or removed.
+
+ This method is also used to handle the situation where a widget is created
+ with the splitter as a parent but not explicitly added with insertWidget()
+ or addWidget(). This is for compatibility and not the recommended way of
+ putting widgets into a splitter in new code. Please use insertWidget() or
+ addWidget() in new code.
+
+ \sa addWidget() insertWidget()
+*/
+
+void QSplitter::childEvent(QChildEvent *c)
+{
+ Q_D(QSplitter);
+ if (!c->child()->isWidgetType())
+ return;
+ QWidget *w = static_cast<QWidget*>(c->child());
+
+ if (c->added() && !d->blockChildAdd && !w->isWindow() && !d->findWidget(w)) {
+ d->insertWidget_helper(d->list.count(), w, false);
+ } else if (c->polished() && !d->blockChildAdd) {
+ if (isVisible() && !(w->isHidden() && w->testAttribute(Qt::WA_WState_ExplicitShowHide)))
+ w->show();
+ } else if (c->type() == QEvent::ChildRemoved) {
+ for (int i = 0; i < d->list.size(); ++i) {
+ QSplitterLayoutStruct *s = d->list.at(i);
+ if (s->widget == w) {
+ d->list.removeAt(i);
+ delete s;
+ d->recalc(isVisible());
+ return;
+ }
+ }
+ }
+}
+
+
+/*!
+ Displays a rubber band at position \a pos. If \a pos is negative, the
+ rubber band is removed.
+*/
+
+void QSplitter::setRubberBand(int pos)
+{
+ Q_D(QSplitter);
+ if (pos < 0) {
+ if (d->rubberBand)
+ QTimer::singleShot(0, d->rubberBand, SLOT(deleteLater()));
+ return;
+ }
+ QRect r = contentsRect();
+ const int rBord = 3; // customizable?
+ int hw = handleWidth();
+ if (!d->rubberBand) {
+ d->rubberBand = new QRubberBand(QRubberBand::Line);
+ // For accessibility to identify this special widget.
+ d->rubberBand->setObjectName(QLatin1String("qt_rubberband"));
+ }
+ if (d->orient == Qt::Horizontal)
+ d->rubberBand->setGeometry(QRect(QPoint(pos + hw / 2 - rBord, r.y()),
+ QSize(2 * rBord, r.height())).translated(mapToGlobal(QPoint())));
+ else
+ d->rubberBand->setGeometry(QRect(QPoint(r.x(), pos + hw / 2 - rBord),
+ QSize(r.width(), 2 * rBord)).translated(mapToGlobal(QPoint())));
+ if (!d->rubberBand->isVisible())
+ d->rubberBand->show();
+}
+
+/*!
+ \reimp
+*/
+
+bool QSplitter::event(QEvent *e)
+{
+ Q_D(QSplitter);
+ switch (e->type()) {
+ case QEvent::Hide:
+ // Reset firstShow to false here since things can be done to the splitter in between
+ if (!d->firstShow)
+ d->firstShow = true;
+ break;
+ case QEvent::Show:
+ if (!d->firstShow)
+ break;
+ d->firstShow = false;
+ // fall through
+ case QEvent::HideToParent:
+ case QEvent::ShowToParent:
+ case QEvent::LayoutRequest:
+#ifdef QT3_SUPPORT
+ case QEvent::LayoutHint:
+#endif
+ d->recalc(isVisible());
+ break;
+ default:
+ ;
+ }
+ return QWidget::event(e);
+}
+
+/*!
+ \fn QSplitter::splitterMoved(int pos, int index)
+
+ This signal is emitted when the splitter handle at a particular \a
+ index has been moved to position \a pos.
+
+ For right-to-left languages such as Arabic and Hebrew, the layout
+ of horizontal splitters is reversed. \a pos is then the
+ distance from the right edge of the widget.
+
+ \sa moveSplitter()
+*/
+
+/*!
+ Moves the left or top edge of the splitter handle at \a index as
+ close as possible to position \a pos, which is the distance from the
+ left or top edge of the widget.
+
+ For right-to-left languages such as Arabic and Hebrew, the layout
+ of horizontal splitters is reversed. \a pos is then the distance
+ from the right edge of the widget.
+
+ \sa splitterMoved(), closestLegalPosition(), getRange()
+*/
+void QSplitter::moveSplitter(int pos, int index)
+{
+ Q_D(QSplitter);
+ QSplitterLayoutStruct *s = d->list.at(index);
+ int farMin;
+ int min;
+ int max;
+ int farMax;
+
+#ifdef QSPLITTER_DEBUG
+ int debugp = pos;
+#endif
+
+ pos = d->adjustPos(pos, index, &farMin, &min, &max, &farMax);
+ int oldP = d->pick(s->rect.topLeft());
+#ifdef QSPLITTER_DEBUG
+ qDebug() << "QSplitter::moveSplitter" << debugp << index << "adjusted" << pos << "oldP" << oldP;
+#endif
+
+ QVarLengthArray<int, 32> poss(d->list.count());
+ QVarLengthArray<int, 32> ws(d->list.count());
+ bool upLeft;
+
+ d->doMove(false, pos, index, +1, (d->collapsible(s) && (pos > max)), poss.data(), ws.data());
+ d->doMove(true, pos, index - 1, +1, (d->collapsible(index - 1) && (pos < min)), poss.data(), ws.data());
+ upLeft = (pos < oldP);
+
+ int wid, delta, count = d->list.count();
+ if (upLeft) {
+ wid = 0;
+ delta = 1;
+ } else {
+ wid = count - 1;
+ delta = -1;
+ }
+ for (; wid >= 0 && wid < count; wid += delta) {
+ QSplitterLayoutStruct *sls = d->list.at( wid );
+ if (!sls->widget->isHidden())
+ d->setGeo(sls, poss[wid], ws[wid], true);
+ }
+ d->storeSizes();
+
+ emit splitterMoved(pos, index);
+}
+
+
+/*!
+ Returns the valid range of the splitter with index \a index in
+ *\a{min} and *\a{max} if \a min and \a max are not 0.
+*/
+
+void QSplitter::getRange(int index, int *min, int *max) const
+{
+ Q_D(const QSplitter);
+ d->getRange(index, min, 0, 0, max);
+}
+
+
+/*!
+ Returns the closest legal position to \a pos of the widget with index
+ \a index.
+
+ For right-to-left languages such as Arabic and Hebrew, the layout
+ of horizontal splitters is reversed. Positions are then measured
+ from the right edge of the widget.
+
+ \sa getRange()
+*/
+
+int QSplitter::closestLegalPosition(int pos, int index)
+{
+ Q_D(QSplitter);
+ int x, i, n, u;
+ return d->adjustPos(pos, index, &u, &n, &i, &x);
+}
+
+/*!
+ \property QSplitter::opaqueResize
+ \brief whether resizing is opaque
+
+ Opaque resizing is on by default.
+*/
+
+bool QSplitter::opaqueResize() const
+{
+ Q_D(const QSplitter);
+ return d->opaque;
+}
+
+
+void QSplitter::setOpaqueResize(bool on)
+{
+ Q_D(QSplitter);
+ d->opaque = on;
+}
+
+#ifdef QT3_SUPPORT
+/*!
+ \fn void QSplitter::moveToFirst(QWidget *widget)
+
+ Use insertWidget(0, \a widget) instead.
+*/
+
+
+/*!
+ \fn void QSplitter::moveToLast(QWidget *widget)
+
+ Use addWidget(\a widget) instead.
+*/
+
+/*!
+ \fn void QSplitter::setResizeMode(QWidget *widget, ResizeMode mode)
+
+ Use setStretchFactor() instead.
+
+ \oldcode
+ splitter->setResizeMode(firstChild, QSplitter::KeepSize);
+ splitter->setResizeMode(secondChild, QSplitter::Stretch);
+ \newcode
+ splitter->setStretchFactor(splitter->indexOf(firstChild), 0);
+ splitter->setStretchFactor(splitter->indexOf(secondChild), 1);
+ \endcode
+*/
+
+/*!
+ \enum QSplitter::ResizeMode
+ \compat
+
+ This enum describes the different resizing behaviors child
+ widgets can have:
+
+ \value Auto The widget will be resized according to the stretch factors set in its sizePolicy().
+ \value Stretch The widget will be resized when the splitter itself is resized.
+ \value KeepSize QSplitter will try to keep the widget's size unchanged.
+ \value FollowSizeHint QSplitter will resize the widget when the widget's size hint changes.
+
+ Use setStretchFactor() instead.
+*/
+
+/*!
+ \fn void QSplitter::setCollapsible(QWidget *widget, bool collapsible)
+
+ Use setCollapsible(indexOf(\a widget, \a collapsible)) instead.
+*/
+
+/*!
+ \fn void QSplitter::setMargin(int margin)
+ Sets the width of the margin around the contents of the widget to \a margin.
+
+ Use QWidget::setContentsMargins() instead.
+ \sa margin(), QWidget::setContentsMargins()
+*/
+
+/*!
+ \fn int QSplitter::margin() const
+ Returns the with of the the margin around the contents of the widget.
+
+ Use QWidget::getContentsMargins() instead.
+ \sa setMargin(), QWidget::getContentsMargins()
+*/
+
+#endif
+
+/*!
+ \reimp
+*/
+QSize QSplitter::sizeHint() const
+{
+ Q_D(const QSplitter);
+ ensurePolished();
+ int l = 0;
+ int t = 0;
+ QObjectList childList = children();
+ for (int i = 0; i < childList.size(); ++i) {
+ if (QWidget *w = qobject_cast<QWidget *>(childList.at(i))) {
+ if (w->isHidden())
+ continue;
+ QSize s = w->sizeHint();
+ if (s.isValid()) {
+ l += d->pick(s);
+ t = qMax(t, d->trans(s));
+ }
+ }
+ }
+ return orientation() == Qt::Horizontal ? QSize(l, t) : QSize(t, l);
+}
+
+
+/*!
+ \reimp
+*/
+
+QSize QSplitter::minimumSizeHint() const
+{
+ Q_D(const QSplitter);
+ ensurePolished();
+ int l = 0;
+ int t = 0;
+
+ for (int i = 0; i < d->list.size(); ++i) {
+ QSplitterLayoutStruct *s = d->list.at(i);
+ if (!s || !s->widget)
+ continue;
+ if (s->widget->isHidden())
+ continue;
+ QSize widgetSize = qSmartMinSize(s->widget);
+ if (widgetSize.isValid()) {
+ l += d->pick(widgetSize);
+ t = qMax(t, d->trans(widgetSize));
+ }
+ if (!s->handle || s->handle->isHidden())
+ continue;
+ QSize splitterSize = s->handle->sizeHint();
+ if (splitterSize.isValid()) {
+ l += d->pick(splitterSize);
+ t = qMax(t, d->trans(splitterSize));
+ }
+ }
+ return orientation() == Qt::Horizontal ? QSize(l, t) : QSize(t, l);
+}
+
+
+/*!
+ Returns a list of the size parameters of all the widgets in this splitter.
+
+ If the splitter's orientation is horizontal, the list contains the
+ widgets width in pixels, from left to right; if the orientation is
+ vertical, the list contains the widgets height in pixels,
+ from top to bottom.
+
+ Giving the values to another splitter's setSizes() function will
+ produce a splitter with the same layout as this one.
+
+ Note that invisible widgets have a size of 0.
+
+ \sa setSizes()
+*/
+
+QList<int> QSplitter::sizes() const
+{
+ Q_D(const QSplitter);
+ ensurePolished();
+
+ QList<int> list;
+ for (int i = 0; i < d->list.size(); ++i) {
+ QSplitterLayoutStruct *s = d->list.at(i);
+ list.append(d->pick(s->rect.size()));
+ }
+ return list;
+}
+
+/*!
+ Sets the child widgets respective sizes to the values given in the \a list.
+
+ If the splitter is horizontal, the values set the widths of each
+ widget in pixels, from left to right. If the splitter is vertical, the
+ heights of each widget is set, from top to bottom.
+
+ Extra values in the \a list are ignored. If \a list contains too few
+ values, the result is undefined but the program will still be well-behaved.
+
+ The overall size of the splitter widget is not affected.
+ Instead, any additional/missing space is distributed amongst the
+ widgets according to the relative weight of the sizes.
+
+ If you specify a size of 0, the widget will be invisible. The size policies
+ of the widgets are preserved. That is, a value smaller then the minimal size
+ hint of the respective widget will be replaced by the value of the hint.
+
+ \sa sizes()
+*/
+
+void QSplitter::setSizes(const QList<int> &list)
+{
+ Q_D(QSplitter);
+ d->setSizes_helper(list, true);
+}
+
+/*!
+ \property QSplitter::handleWidth
+ \brief the width of the splitter handles
+
+ By default, this property contains a value that depends on the user's platform
+ and style preferences.
+*/
+
+int QSplitter::handleWidth() const
+{
+ Q_D(const QSplitter);
+ if (d->handleWidth > 0) {
+ return d->handleWidth;
+ } else {
+ return style()->pixelMetric(QStyle::PM_SplitterWidth, 0, this);
+ }
+}
+
+void QSplitter::setHandleWidth(int width)
+{
+ Q_D(QSplitter);
+ d->handleWidth = width;
+ d->updateHandles();
+}
+
+/*!
+ \reimp
+*/
+void QSplitter::changeEvent(QEvent *ev)
+{
+ Q_D(QSplitter);
+ if(ev->type() == QEvent::StyleChange)
+ d->updateHandles();
+ QFrame::changeEvent(ev);
+}
+
+static const qint32 SplitterMagic = 0xff;
+
+/*!
+ Saves the state of the splitter's layout.
+
+ Typically this is used in conjunction with QSettings to remember the size
+ for a future session. A version number is stored as part of the data.
+ Here is an example:
+
+ \snippet doc/src/snippets/splitter/splitter.cpp 1
+
+ \sa restoreState()
+*/
+QByteArray QSplitter::saveState() const
+{
+ Q_D(const QSplitter);
+ int version = 0;
+ QByteArray data;
+ QDataStream stream(&data, QIODevice::WriteOnly);
+
+ stream << qint32(SplitterMagic);
+ stream << qint32(version);
+ QList<int> list;
+ for (int i = 0; i < d->list.size(); ++i) {
+ QSplitterLayoutStruct *s = d->list.at(i);
+ list.append(s->sizer);
+ }
+ stream << list;
+ stream << childrenCollapsible();
+ stream << qint32(handleWidth());
+ stream << opaqueResize();
+ stream << qint32(orientation());
+ return data;
+}
+
+/*!
+ Restores the splitter's layout to the \a state specified.
+ Returns true if the state is restored; otherwise returns false.
+
+ Typically this is used in conjunction with QSettings to restore the size
+ from a past session. Here is an example:
+
+ Restore the splitters's state:
+
+ \snippet doc/src/snippets/splitter/splitter.cpp 2
+
+ A failure to restore the splitter's layout may result from either
+ invalid or out-of-date data in the supplied byte array.
+
+ \sa saveState()
+*/
+bool QSplitter::restoreState(const QByteArray &state)
+{
+ Q_D(QSplitter);
+ int version = 0;
+ QByteArray sd = state;
+ QDataStream stream(&sd, QIODevice::ReadOnly);
+ QList<int> list;
+ bool b;
+ qint32 i;
+ qint32 marker;
+ qint32 v;
+
+ stream >> marker;
+ stream >> v;
+ if (marker != SplitterMagic || v != version)
+ return false;
+
+ stream >> list;
+ d->setSizes_helper(list, false);
+
+ stream >> b;
+ setChildrenCollapsible(b);
+
+ stream >> i;
+ setHandleWidth(i);
+
+ stream >> b;
+ setOpaqueResize(b);
+
+ stream >> i;
+ setOrientation(Qt::Orientation(i));
+ d->doResize();
+
+ return true;
+}
+
+/*!
+ Updates the size policy of the widget at position \a index to
+ have a stretch factor of \a stretch.
+
+ \a stretch is not the effective stretch factor; the effective
+ stretch factor is calculated by taking the initial size of the
+ widget and multiplying it with \a stretch.
+
+ This function is provided for convenience. It is equivalent to
+
+ \snippet doc/src/snippets/code/src_gui_widgets_qsplitter.cpp 0
+
+ \sa setSizes(), widget()
+*/
+void QSplitter::setStretchFactor(int index, int stretch)
+{
+ Q_D(QSplitter);
+ if (index <= -1 || index >= d->list.count())
+ return;
+
+ QWidget *widget = d->list.at(index)->widget;
+ QSizePolicy sp = widget->sizePolicy();
+ sp.setHorizontalStretch(stretch);
+ sp.setVerticalStretch(stretch);
+ widget->setSizePolicy(sp);
+}
+
+
+//#ifdef QT3_SUPPORT
+#ifndef QT_NO_TEXTSTREAM
+/*!
+ \relates QSplitter
+ \obsolete
+
+ Use \a ts << \a{splitter}.saveState() instead.
+*/
+
+QTextStream& operator<<(QTextStream& ts, const QSplitter& splitter)
+{
+ ts << splitter.saveState() << endl;
+ return ts;
+}
+
+/*!
+ \relates QSplitter
+ \obsolete
+
+ Use \a ts >> \a{splitter}.restoreState() instead.
+*/
+
+QTextStream& operator>>(QTextStream& ts, QSplitter& splitter)
+{
+ QString line = ts.readLine();
+ line = line.simplified();
+ line.replace(QLatin1Char(' '), QString());
+ line = line.toUpper();
+
+ splitter.restoreState(line.toAscii());
+ return ts;
+}
+#endif // QT_NO_TEXTSTREAM
+//#endif // QT3_SUPPORT
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_SPLITTER
diff --git a/src/gui/widgets/qsplitter.h b/src/gui/widgets/qsplitter.h
new file mode 100644
index 0000000000..36e6e19f9f
--- /dev/null
+++ b/src/gui/widgets/qsplitter.h
@@ -0,0 +1,191 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSPLITTER_H
+#define QSPLITTER_H
+
+#include <QtGui/qframe.h>
+#include <QtGui/qsizepolicy.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+#ifndef QT_NO_SPLITTER
+
+class QSplitterPrivate;
+class QTextStream;
+template <typename T> class QList;
+
+class QSplitterHandle;
+
+class Q_GUI_EXPORT QSplitter : public QFrame
+{
+ Q_OBJECT
+
+ Q_PROPERTY(Qt::Orientation orientation READ orientation WRITE setOrientation)
+ Q_PROPERTY(bool opaqueResize READ opaqueResize WRITE setOpaqueResize)
+ Q_PROPERTY(int handleWidth READ handleWidth WRITE setHandleWidth)
+ Q_PROPERTY(bool childrenCollapsible READ childrenCollapsible WRITE setChildrenCollapsible)
+
+public:
+ explicit QSplitter(QWidget* parent = 0);
+ explicit QSplitter(Qt::Orientation, QWidget* parent = 0);
+ ~QSplitter();
+
+ void addWidget(QWidget *widget);
+ void insertWidget(int index, QWidget *widget);
+
+ void setOrientation(Qt::Orientation);
+ Qt::Orientation orientation() const;
+
+ void setChildrenCollapsible(bool);
+ bool childrenCollapsible() const;
+
+ void setCollapsible(int index, bool);
+ bool isCollapsible(int index) const;
+ void setOpaqueResize(bool opaque = true);
+ bool opaqueResize() const;
+ void refresh();
+
+ QSize sizeHint() const;
+ QSize minimumSizeHint() const;
+
+ QList<int> sizes() const;
+ void setSizes(const QList<int> &list);
+
+ QByteArray saveState() const;
+ bool restoreState(const QByteArray &state);
+
+ int handleWidth() const;
+ void setHandleWidth(int);
+
+ int indexOf(QWidget *w) const;
+ QWidget *widget(int index) const;
+ int count() const;
+
+ void getRange(int index, int *, int *) const;
+ QSplitterHandle *handle(int index) const;
+
+ void setStretchFactor(int index, int stretch);
+
+Q_SIGNALS:
+ void splitterMoved(int pos, int index);
+
+protected:
+ virtual QSplitterHandle *createHandle();
+
+ void childEvent(QChildEvent *);
+
+ bool event(QEvent *);
+ void resizeEvent(QResizeEvent *);
+
+ void changeEvent(QEvent *);
+ void moveSplitter(int pos, int index);
+ void setRubberBand(int position);
+ int closestLegalPosition(int, int);
+
+#ifdef QT3_SUPPORT
+public:
+ QT3_SUPPORT_CONSTRUCTOR QSplitter(QWidget* parent, const char* name);
+ QT3_SUPPORT_CONSTRUCTOR QSplitter(Qt::Orientation, QWidget* parent, const char* name);
+ enum ResizeMode { Stretch, KeepSize, FollowSizeHint, Auto };
+ QT3_SUPPORT void setResizeMode(QWidget *w, ResizeMode mode);
+ inline QT3_SUPPORT void moveToFirst(QWidget *w) { insertWidget(0,w); }
+ inline QT3_SUPPORT void moveToLast(QWidget *w) { addWidget(w); }
+ inline QT3_SUPPORT void setCollapsible(QWidget *w, bool collapse)
+ { setCollapsible(indexOf(w), collapse); }
+ QT3_SUPPORT void setMargin(int margin) { setContentsMargins(margin, margin, margin, margin); }
+ QT3_SUPPORT int margin() const
+ { int margin; int dummy; getContentsMargins(&margin, &dummy, &dummy, &dummy); return margin; }
+#endif
+
+private:
+ Q_DISABLE_COPY(QSplitter)
+ Q_DECLARE_PRIVATE(QSplitter)
+private:
+ friend class QSplitterHandle;
+};
+
+//#ifdef QT3_SUPPORT
+#ifndef QT_NO_TEXTSTREAM
+Q_GUI_EXPORT QTextStream& operator<<(QTextStream&, const QSplitter&);
+Q_GUI_EXPORT QTextStream& operator>>(QTextStream&, QSplitter&);
+#endif
+//#endif
+
+class QSplitterHandlePrivate;
+class Q_GUI_EXPORT QSplitterHandle : public QWidget
+{
+ Q_OBJECT
+public:
+ QSplitterHandle(Qt::Orientation o, QSplitter *parent);
+ void setOrientation(Qt::Orientation o);
+ Qt::Orientation orientation() const;
+ bool opaqueResize() const;
+ QSplitter *splitter() const;
+
+ QSize sizeHint() const;
+
+protected:
+ void paintEvent(QPaintEvent *);
+ void mouseMoveEvent(QMouseEvent *);
+ void mousePressEvent(QMouseEvent *);
+ void mouseReleaseEvent(QMouseEvent *);
+ bool event(QEvent *);
+
+ void moveSplitter(int p);
+ int closestLegalPosition(int p);
+
+private:
+ Q_DISABLE_COPY(QSplitterHandle)
+ Q_DECLARE_PRIVATE(QSplitterHandle)
+};
+
+#endif // QT_NO_SPLITTER
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QSPLITTER_H
diff --git a/src/gui/widgets/qsplitter_p.h b/src/gui/widgets/qsplitter_p.h
new file mode 100644
index 0000000000..5cc43afa20
--- /dev/null
+++ b/src/gui/widgets/qsplitter_p.h
@@ -0,0 +1,148 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSPLITTER_P_H
+#define QSPLITTER_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/qframe_p.h"
+#include "qrubberband.h"
+
+QT_BEGIN_NAMESPACE
+
+static const uint Default = 2;
+
+class QSplitterLayoutStruct
+{
+public:
+ QRect rect;
+ int sizer;
+ uint collapsed : 1;
+ uint collapsible : 2;
+ QWidget *widget;
+ QSplitterHandle *handle;
+
+ QSplitterLayoutStruct() : sizer(-1), collapsed(false), collapsible(Default), widget(0), handle(0) {}
+ ~QSplitterLayoutStruct() { delete handle; }
+ int getWidgetSize(Qt::Orientation orient);
+ int getHandleSize(Qt::Orientation orient);
+ int pick(const QSize &size, Qt::Orientation orient)
+ { return (orient == Qt::Horizontal) ? size.width() : size.height(); }
+};
+
+class QSplitterPrivate : public QFramePrivate
+{
+ Q_DECLARE_PUBLIC(QSplitter)
+public:
+ QSplitterPrivate() : rubberBand(0), opaque(true), firstShow(true),
+ childrenCollapsible(true), compatMode(false), handleWidth(0), blockChildAdd(false) {}
+
+ QPointer<QRubberBand> rubberBand;
+ mutable QList<QSplitterLayoutStruct *> list;
+ Qt::Orientation orient;
+ bool opaque : 8;
+ bool firstShow : 8;
+ bool childrenCollapsible : 8;
+ bool compatMode : 8;
+ int handleWidth;
+ bool blockChildAdd;
+
+ inline int pick(const QPoint &pos) const
+ { return orient == Qt::Horizontal ? pos.x() : pos.y(); }
+ inline int pick(const QSize &s) const
+ { return orient == Qt::Horizontal ? s.width() : s.height(); }
+
+ inline int trans(const QPoint &pos) const
+ { return orient == Qt::Vertical ? pos.x() : pos.y(); }
+ inline int trans(const QSize &s) const
+ { return orient == Qt::Vertical ? s.width() : s.height(); }
+
+ void init();
+ void recalc(bool update = false);
+ void doResize();
+ void storeSizes();
+ void getRange(int index, int *, int *, int *, int *) const;
+ void addContribution(int, int *, int *, bool) const;
+ int adjustPos(int, int, int *, int *, int *, int *) const;
+ bool collapsible(QSplitterLayoutStruct *) const;
+ bool collapsible(int index) const
+ { return (index < 0 || index >= list.size()) ? true : collapsible(list.at(index)); }
+ QSplitterLayoutStruct *findWidget(QWidget *) const;
+ void insertWidget_helper(int index, QWidget *widget, bool show);
+ QSplitterLayoutStruct *insertWidget(int index, QWidget *);
+ void doMove(bool backwards, int pos, int index, int delta,
+ bool mayCollapse, int *positions, int *widths);
+ void setGeo(QSplitterLayoutStruct *s, int pos, int size, bool allowCollapse);
+ int findWidgetJustBeforeOrJustAfter(int index, int delta, int &collapsibleSize) const;
+ void updateHandles();
+ void setSizes_helper(const QList<int> &sizes, bool clampNegativeSize = false);
+
+};
+
+class QSplitterHandlePrivate : public QWidgetPrivate
+{
+ Q_DECLARE_PUBLIC(QSplitterHandle)
+public:
+ QSplitterHandlePrivate() : orient(Qt::Horizontal), opaq(false), s(0), mouseOffset(0) {}
+
+ inline int pick(const QPoint &pos) const
+ { return orient == Qt::Horizontal ? pos.x() : pos.y(); }
+
+ Qt::Orientation orient;
+ bool opaq;
+ QSplitter *s;
+ bool hover;
+ int mouseOffset;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/gui/widgets/qstackedwidget.cpp b/src/gui/widgets/qstackedwidget.cpp
new file mode 100644
index 0000000000..c615630ce7
--- /dev/null
+++ b/src/gui/widgets/qstackedwidget.cpp
@@ -0,0 +1,294 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qstackedwidget.h"
+
+#ifndef QT_NO_STACKEDWIDGET
+
+#include <qstackedlayout.h>
+#include <qevent.h>
+#include <private/qframe_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QStackedWidgetPrivate : public QFramePrivate
+{
+ Q_DECLARE_PUBLIC(QStackedWidget)
+public:
+ QStackedWidgetPrivate():layout(0){}
+ QStackedLayout *layout;
+ bool blockChildAdd;
+};
+
+/*!
+ \class QStackedWidget
+ \brief The QStackedWidget class provides a stack of widgets where
+ only one widget is visible at a time.
+
+ \ingroup organizers
+ \ingroup geomanagement
+ \ingroup appearance
+ \mainclass
+
+ QStackedWidget can be used to create a user interface similar to
+ the one provided by QTabWidget. It is a convenience layout widget
+ built on top of the QStackedLayout class.
+
+ Like QStackedLayout, QStackedWidget can be constructed and
+ populated with a number of child widgets ("pages"):
+
+ \snippet doc/src/snippets/qstackedwidget/main.cpp 0
+ \snippet doc/src/snippets/qstackedwidget/main.cpp 2
+ \snippet doc/src/snippets/qstackedwidget/main.cpp 3
+
+ QStackedWidget provides no intrinsic means for the user to switch
+ page. This is typically done through a QComboBox or a QListWidget
+ that stores the titles of the QStackedWidget's pages. For
+ example:
+
+ \snippet doc/src/snippets/qstackedwidget/main.cpp 1
+
+ When populating a stacked widget, the widgets are added to an
+ internal list. The indexOf() function returns the index of a
+ widget in that list. The widgets can either be added to the end of
+ the list using the addWidget() function, or inserted at a given
+ index using the insertWidget() function. The removeWidget()
+ function removes a widget from the stacked widget. The number of
+ widgets contained in the stacked widget, can
+ be obtained using the count() function.
+
+ The widget() function returns the widget at a given index
+ position. The index of the widget that is shown on screen is given
+ by currentIndex() and can be changed using setCurrentIndex(). In a
+ similar manner, the currently shown widget can be retrieved using
+ the currentWidget() function, and altered using the
+ setCurrentWidget() function.
+
+ Whenever the current widget in the stacked widget changes or a
+ widget is removed from the stacked widget, the currentChanged()
+ and widgetRemoved() signals are emitted respectively.
+
+ \sa QStackedLayout, QTabWidget, {Config Dialog Example}
+*/
+
+/*!
+ \fn void QStackedWidget::currentChanged(int index)
+
+ This signal is emitted whenever the current widget changes.
+
+ The parameter holds the \a index of the new current widget, or -1
+ if there isn't a new one (for example, if there are no widgets in
+ the QStackedWidget).
+
+ \sa currentWidget(), setCurrentWidget()
+*/
+
+/*!
+ \fn void QStackedWidget::widgetRemoved(int index)
+
+ This signal is emitted whenever a widget is removed. The widget's
+ \a index is passed as parameter.
+
+ \sa removeWidget()
+*/
+
+/*!
+ Constructs a QStackedWidget with the given \a parent.
+
+ \sa addWidget(), insertWidget()
+*/
+QStackedWidget::QStackedWidget(QWidget *parent)
+ : QFrame(*new QStackedWidgetPrivate, parent)
+{
+ Q_D(QStackedWidget);
+ d->layout = new QStackedLayout(this);
+ connect(d->layout, SIGNAL(widgetRemoved(int)), this, SIGNAL(widgetRemoved(int)));
+ connect(d->layout, SIGNAL(currentChanged(int)), this, SIGNAL(currentChanged(int)));
+}
+
+/*!
+ Destroys this stacked widget, and frees any allocated resources.
+*/
+QStackedWidget::~QStackedWidget()
+{
+}
+
+/*!
+ Appends the given \a widget to the QStackedWidget and returns the
+ index position. Ownership of \a widget is passed on to the
+ QStackedWidget.
+
+ If the QStackedWidget is empty before this function is called,
+ \a widget becomes the current widget.
+
+ \sa insertWidget(), removeWidget(), setCurrentWidget()
+*/
+int QStackedWidget::addWidget(QWidget *widget)
+{
+ return d_func()->layout->addWidget(widget);
+}
+
+/*!
+ Inserts the given \a widget at the given \a index in the
+ QStackedWidget. Ownership of \a widget is passed on to the
+ QStackedWidget. If \a index is out of range, the \a widget is
+ appended (in which case it is the actual index of the \a widget
+ that is returned).
+
+ If the QStackedWidget was empty before this function is called,
+ the given \a widget becomes the current widget.
+
+ Inserting a new widget at an index less than or equal to the current index
+ will increment the current index, but keep the current widget.
+
+ \sa addWidget(), removeWidget(), setCurrentWidget()
+*/
+int QStackedWidget::insertWidget(int index, QWidget *widget)
+{
+ return d_func()->layout->insertWidget(index, widget);
+}
+
+/*!
+ Removes the given \a widget from the QStackedWidget. The widget
+ is \e not deleted.
+
+ \sa addWidget(), insertWidget(), currentWidget()
+*/
+void QStackedWidget::removeWidget(QWidget *widget)
+{
+ d_func()->layout->removeWidget(widget);
+}
+
+/*!
+ \property QStackedWidget::currentIndex
+ \brief the index position of the widget that is visible
+
+ The current index is -1 if there is no current widget.
+
+ By default, this property contains a value of -1 because the stack
+ is initially empty.
+
+ \sa currentWidget(), indexOf()
+*/
+
+void QStackedWidget::setCurrentIndex(int index)
+{
+ d_func()->layout->setCurrentIndex(index);
+}
+
+int QStackedWidget::currentIndex() const
+{
+ return d_func()->layout->currentIndex();
+}
+
+/*!
+ Returns the current widget, or 0 if there are no child widgets.
+
+ \sa currentIndex(), setCurrentWidget()
+*/
+QWidget *QStackedWidget::currentWidget() const
+{
+ return d_func()->layout->currentWidget();
+}
+
+
+/*!
+ \fn void QStackedWidget::setCurrentWidget(QWidget *widget)
+
+ Sets the current widget to be the specified \a widget. The new
+ current widget must already be contained in this stacked widget.
+
+ \sa currentWidget(), setCurrentIndex()
+ */
+void QStackedWidget::setCurrentWidget(QWidget *widget)
+{
+ Q_D(QStackedWidget);
+ if (d->layout->indexOf(widget) == -1) {
+ qWarning("QStackedWidget::setCurrentWidget: widget %p not contained in stack", widget);
+ return;
+ }
+ d->layout->setCurrentWidget(widget);
+}
+
+/*!
+ Returns the index of the given \a widget, or -1 if the given \a
+ widget is not a child of the QStackedWidget.
+
+ \sa currentIndex(), widget()
+*/
+int QStackedWidget::indexOf(QWidget *widget) const
+{
+ return d_func()->layout->indexOf(widget);
+}
+
+/*!
+ Returns the widget at the given \a index, or 0 if there is no such
+ widget.
+
+ \sa currentWidget(), indexOf()
+*/
+QWidget *QStackedWidget::widget(int index) const
+{
+ return d_func()->layout->widget(index);
+}
+
+/*!
+ \property QStackedWidget::count
+ \brief the number of widgets contained by this stacked widget
+
+ By default, this property contains a value of 0.
+
+ \sa currentIndex(), widget()
+*/
+int QStackedWidget::count() const
+{
+ return d_func()->layout->count();
+}
+
+/*! \reimp */
+bool QStackedWidget::event(QEvent *e)
+{
+ return QFrame::event(e);
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_STACKEDWIDGET
diff --git a/src/gui/widgets/qstackedwidget.h b/src/gui/widgets/qstackedwidget.h
new file mode 100644
index 0000000000..773f800c69
--- /dev/null
+++ b/src/gui/widgets/qstackedwidget.h
@@ -0,0 +1,100 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSTACKEDWIDGET_H
+#define QSTACKEDWIDGET_H
+
+#include <QtGui/qframe.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+#ifndef QT_NO_STACKEDWIDGET
+
+class QStackedWidgetPrivate;
+
+class Q_GUI_EXPORT QStackedWidget : public QFrame
+{
+ Q_OBJECT
+
+ Q_PROPERTY(int currentIndex READ currentIndex WRITE setCurrentIndex NOTIFY currentChanged)
+ Q_PROPERTY(int count READ count)
+public:
+ explicit QStackedWidget(QWidget *parent=0);
+ ~QStackedWidget();
+
+ int addWidget(QWidget *w);
+ int insertWidget(int index, QWidget *w);
+ void removeWidget(QWidget *w);
+
+ QWidget *currentWidget() const;
+ int currentIndex() const;
+
+ int indexOf(QWidget *) const;
+ QWidget *widget(int) const;
+ int count() const;
+
+public Q_SLOTS:
+ void setCurrentIndex(int index);
+ void setCurrentWidget(QWidget *w);
+
+Q_SIGNALS:
+ void currentChanged(int);
+ void widgetRemoved(int index);
+
+protected:
+ bool event(QEvent *e);
+
+private:
+ Q_DISABLE_COPY(QStackedWidget)
+ Q_DECLARE_PRIVATE(QStackedWidget)
+};
+
+#endif // QT_NO_STACKEDWIDGET
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QSTACKEDWIDGET_H
diff --git a/src/gui/widgets/qstatusbar.cpp b/src/gui/widgets/qstatusbar.cpp
new file mode 100644
index 0000000000..c970838817
--- /dev/null
+++ b/src/gui/widgets/qstatusbar.cpp
@@ -0,0 +1,847 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qstatusbar.h"
+#ifndef QT_NO_STATUSBAR
+
+#include "qlist.h"
+#include "qdebug.h"
+#include "qevent.h"
+#include "qlayout.h"
+#include "qpainter.h"
+#include "qtimer.h"
+#include "qstyle.h"
+#include "qstyleoption.h"
+#include "qsizegrip.h"
+#include "qmainwindow.h"
+
+#include <private/qlayoutengine_p.h>
+#include <private/qwidget_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QStatusBarPrivate : public QWidgetPrivate
+{
+ Q_DECLARE_PUBLIC(QStatusBar)
+public:
+ QStatusBarPrivate() {}
+
+ struct SBItem {
+ SBItem(QWidget* widget, int stretch, bool permanent)
+ : s(stretch), w(widget), p(permanent) {}
+ int s;
+ QWidget * w;
+ bool p;
+ };
+
+ QList<SBItem *> items;
+ QString tempItem;
+
+ QBoxLayout * box;
+ QTimer * timer;
+
+#ifndef QT_NO_SIZEGRIP
+ QSizeGrip * resizer;
+ bool showSizeGrip;
+#endif
+
+ int savedStrut;
+
+#ifdef Q_WS_MAC
+ QPoint dragStart;
+#endif
+
+ int indexToLastNonPermanentWidget() const
+ {
+ int i = items.size() - 1;
+ for (; i >= 0; --i) {
+ SBItem *item = items.at(i);
+ if (!(item && item->p))
+ break;
+ }
+ return i;
+ }
+
+#ifndef QT_NO_SIZEGRIP
+ void tryToShowSizeGrip()
+ {
+ if (!showSizeGrip)
+ return;
+ showSizeGrip = false;
+ if (!resizer || resizer->isVisible())
+ return;
+ resizer->setAttribute(Qt::WA_WState_ExplicitShowHide, false);
+ QMetaObject::invokeMethod(resizer, "_q_showIfNotHidden", Qt::DirectConnection);
+ resizer->setAttribute(Qt::WA_WState_ExplicitShowHide, false);
+ }
+#endif
+
+ QRect messageRect() const;
+};
+
+
+QRect QStatusBarPrivate::messageRect() const
+{
+ Q_Q(const QStatusBar);
+ bool rtl = q->layoutDirection() == Qt::RightToLeft;
+
+ int left = 6;
+ int right = q->width() - 12;
+
+#ifndef QT_NO_SIZEGRIP
+ if (resizer && resizer->isVisible()) {
+ if (rtl)
+ left = resizer->x() + resizer->width();
+ else
+ right = resizer->x();
+ }
+#endif
+
+ for (int i=0; i<items.size(); ++i) {
+ QStatusBarPrivate::SBItem* item = items.at(i);
+ if (!item)
+ break;
+ if (item->p && item->w->isVisible()) {
+ if (item->p) {
+ if (rtl)
+ left = qMax(left, item->w->x() + item->w->width() + 2);
+ else
+ right = qMin(right, item->w->x()-1);
+ }
+ break;
+ }
+ }
+ return QRect(left, 0, right-left, q->height());
+}
+
+
+/*!
+ \class QStatusBar
+ \brief The QStatusBar class provides a horizontal bar suitable for
+ presenting status information.
+
+ \ingroup application
+ \ingroup helpsystem
+ \mainclass
+
+ Each status indicator falls into one of three categories:
+
+ \list
+ \o \e Temporary - briefly occupies most of the status bar. Used
+ to explain tool tip texts or menu entries, for example.
+ \o \e Normal - occupies part of the status bar and may be hidden
+ by temporary messages. Used to display the page and line
+ number in a word processor, for example.
+ \o \e Permanent - is never hidden. Used for important mode
+ indications, for example, some applications put a Caps Lock
+ indicator in the status bar.
+ \endlist
+
+ QStatusBar lets you display all three types of indicators.
+
+ Typically, a request for the status bar functionality occurs in
+ relation to a QMainWindow object. QMainWindow provides a main
+ application window, with a menu bar, tool bars, dock widgets \e
+ and a status bar around a large central widget. The status bar can
+ be retrieved using the QMainWindow::statusBar() function, and
+ replaced using the QMainWindow::setStatusBar() function.
+
+ Use the showMessage() slot to display a \e temporary message:
+
+ \snippet examples/mainwindows/dockwidgets/mainwindow.cpp 8
+
+ To remove a temporary message, use the clearMessage() slot, or set
+ a time limit when calling showMessage(). For example:
+
+ \snippet examples/mainwindows/dockwidgets/mainwindow.cpp 3
+
+ Use the currentMessage() function to retrieve the temporary
+ message currently shown. The QStatusBar class also provide the
+ messageChanged() signal which is emitted whenever the temporary
+ status message changes.
+
+ \target permanent message
+ \e Normal and \e Permanent messages are displayed by creating a
+ small widget (QLabel, QProgressBar or even QToolButton) and then
+ adding it to the status bar using the addWidget() or the
+ addPermanentWidget() function. Use the removeWidget() function to
+ remove such messages from the status bar.
+
+ \snippet doc/src/snippets/code/src_gui_widgets_qstatusbar.cpp 0
+
+ By default QStatusBar provides a QSizeGrip in the lower-right
+ corner. You can disable it using the setSizeGripEnabled()
+ function. Use the isSizeGripEnabled() function to determine the
+ current status of the size grip.
+
+ \image plastique-statusbar.png A status bar shown in the Plastique widget style
+
+ \sa QMainWindow, QStatusTipEvent, {fowler}{GUI Design Handbook:
+ Status Bar}, {Application Example}
+*/
+
+#ifdef QT3_SUPPORT
+/*!
+ Constructs a status bar with a size grip and the given \a parent
+ and object \a name.
+
+ Use the QStatusBar() constructor and the QObject::setObjectName()
+ function instead.
+
+ \oldcode
+ QStatusBar *myStatusBar = new QStatusBar(parent, name);
+ \newcode
+ QStatusBar *myStatusBar = new QStatusBar(parent);
+ myStatusBar->setObjectName(name);
+ \endcode
+*/
+QStatusBar::QStatusBar(QWidget * parent, const char *name)
+ : QWidget(*new QStatusBarPrivate, parent, 0)
+{
+ Q_D(QStatusBar);
+ setObjectName(QString::fromAscii(name));
+ d->box = 0;
+ d->timer = 0;
+
+#ifndef QT_NO_SIZEGRIP
+ d->resizer = 0;
+ d->showSizeGrip = false;
+ setSizeGripEnabled(true); // causes reformat()
+#else
+ reformat();
+#endif
+}
+
+
+/*!
+ \fn void QStatusBar::addWidget(QWidget * widget, int stretch, bool permanent)
+
+ Use addWidget() or addPermanentWidget() instead, depending on the
+ value of the \a permanent parameter.
+
+ \oldcode
+ QStatusBar *myStatusBar;
+ myStatusBar->addWidget(widget, stretch, permanent); // permanent == true
+ \newcode
+ QStatusBar *myStatusBar;
+ myStatusBar->addPermanentWidget(widget, stretch);
+ \endcode
+ */
+
+#endif
+
+/*!
+ Constructs a status bar with a size grip and the given \a parent.
+
+ \sa setSizeGripEnabled()
+*/
+QStatusBar::QStatusBar(QWidget * parent)
+ : QWidget(*new QStatusBarPrivate, parent, 0)
+{
+ Q_D(QStatusBar);
+ d->box = 0;
+ d->timer = 0;
+
+#ifndef QT_NO_SIZEGRIP
+ d->resizer = 0;
+ setSizeGripEnabled(true); // causes reformat()
+#else
+ reformat();
+#endif
+}
+
+/*!
+ Destroys this status bar and frees any allocated resources and
+ child widgets.
+*/
+QStatusBar::~QStatusBar()
+{
+ Q_D(QStatusBar);
+ while (!d->items.isEmpty())
+ delete d->items.takeFirst();
+}
+
+
+/*!
+ Adds the given \a widget to this status bar, reparenting the
+ widget if it isn't already a child of this QStatusBar object. The
+ \a stretch parameter is used to compute a suitable size for the
+ given \a widget as the status bar grows and shrinks. The default
+ stretch factor is 0, i.e giving the widget a minimum of space.
+
+ The widget is located to the far left of the first permanent
+ widget (see addPermanentWidget()) and may be obscured by temporary
+ messages.
+
+ \sa insertWidget(), removeWidget(), addPermanentWidget()
+*/
+
+void QStatusBar::addWidget(QWidget * widget, int stretch)
+{
+ if (!widget)
+ return;
+ insertWidget(d_func()->indexToLastNonPermanentWidget() + 1, widget, stretch);
+}
+
+/*!
+ \since 4.2
+
+ Inserts the given \a widget at the given \a index to this status bar,
+ reparenting the widget if it isn't already a child of this
+ QStatusBar object. If \a index is out of range, the widget is appended
+ (in which case it is the actual index of the widget that is returned).
+
+ The \a stretch parameter is used to compute a suitable size for
+ the given \a widget as the status bar grows and shrinks. The
+ default stretch factor is 0, i.e giving the widget a minimum of
+ space.
+
+ The widget is located to the far left of the first permanent
+ widget (see addPermanentWidget()) and may be obscured by temporary
+ messages.
+
+ \sa addWidget(), removeWidget(), addPermanentWidget()
+*/
+int QStatusBar::insertWidget(int index, QWidget *widget, int stretch)
+{
+ if (!widget)
+ return -1;
+
+ Q_D(QStatusBar);
+ QStatusBarPrivate::SBItem* item = new QStatusBarPrivate::SBItem(widget, stretch, false);
+
+ int idx = d->indexToLastNonPermanentWidget();
+ if (index < 0 || index > d->items.size() || (idx >= 0 && index > idx + 1)) {
+ qWarning("QStatusBar::insertWidget: Index out of range (%d), appending widget", index);
+ index = idx + 1;
+ }
+ d->items.insert(index, item);
+
+ if (!d->tempItem.isEmpty())
+ widget->hide();
+
+ reformat();
+ if (!widget->isHidden() || !widget->testAttribute(Qt::WA_WState_ExplicitShowHide))
+ widget->show();
+
+ return index;
+}
+
+/*!
+ Adds the given \a widget permanently to this status bar,
+ reparenting the widget if it isn't already a child of this
+ QStatusBar object. The \a stretch parameter is used to compute a
+ suitable size for the given \a widget as the status bar grows and
+ shrinks. The default stretch factor is 0, i.e giving the widget a
+ minimum of space.
+
+ Permanently means that the widget may not be obscured by temporary
+ messages. It is is located at the far right of the status bar.
+
+ \sa insertPermanentWidget(), removeWidget(), addWidget()
+*/
+
+void QStatusBar::addPermanentWidget(QWidget * widget, int stretch)
+{
+ if (!widget)
+ return;
+ insertPermanentWidget(d_func()->items.size(), widget, stretch);
+}
+
+
+/*!
+ \since 4.2
+
+ Inserts the given \a widget at the given \a index permanently to this status bar,
+ reparenting the widget if it isn't already a child of this
+ QStatusBar object. If \a index is out of range, the widget is appended
+ (in which case it is the actual index of the widget that is returned).
+
+ The \a stretch parameter is used to compute a
+ suitable size for the given \a widget as the status bar grows and
+ shrinks. The default stretch factor is 0, i.e giving the widget a
+ minimum of space.
+
+ Permanently means that the widget may not be obscured by temporary
+ messages. It is is located at the far right of the status bar.
+
+ \sa addPermanentWidget(), removeWidget(), addWidget()
+*/
+int QStatusBar::insertPermanentWidget(int index, QWidget *widget, int stretch)
+{
+ if (!widget)
+ return -1;
+
+ Q_D(QStatusBar);
+ QStatusBarPrivate::SBItem* item = new QStatusBarPrivate::SBItem(widget, stretch, true);
+
+ int idx = d->indexToLastNonPermanentWidget();
+ if (index < 0 || index > d->items.size() || (idx >= 0 && index <= idx)) {
+ qWarning("QStatusBar::insertPermanentWidget: Index out of range (%d), appending widget", index);
+ index = d->items.size();
+ }
+ d->items.insert(index, item);
+
+ reformat();
+ if (!widget->isHidden() || !widget->testAttribute(Qt::WA_WState_ExplicitShowHide))
+ widget->show();
+
+ return index;
+}
+
+/*!
+ Removes the specified \a widget from the status bar.
+
+ \note This function does not delete the widget but \e hides it.
+ To add the widget again, you must call both the addWidget() and
+ show() functions.
+
+ \sa addWidget(), addPermanentWidget(), clearMessage()
+*/
+
+void QStatusBar::removeWidget(QWidget *widget)
+{
+ if (!widget)
+ return;
+
+ Q_D(QStatusBar);
+ bool found = false;
+ QStatusBarPrivate::SBItem* item;
+ for (int i=0; i<d->items.size(); ++i) {
+ item = d->items.at(i);
+ if (!item)
+ break;
+ if (item->w == widget) {
+ d->items.removeAt(i);
+ item->w->hide();
+ delete item;
+ found = true;
+ break;
+ }
+ }
+
+ if (found)
+ reformat();
+#if defined(QT_DEBUG)
+ else
+ qDebug("QStatusBar::removeWidget(): Widget not found.");
+#endif
+}
+
+/*!
+ \property QStatusBar::sizeGripEnabled
+
+ \brief whether the QSizeGrip in the bottom-right corner of the
+ status bar is enabled
+
+ The size grip is enabled by default.
+*/
+
+bool QStatusBar::isSizeGripEnabled() const
+{
+#ifdef QT_NO_SIZEGRIP
+ return false;
+#else
+ Q_D(const QStatusBar);
+ return !!d->resizer;
+#endif
+}
+
+void QStatusBar::setSizeGripEnabled(bool enabled)
+{
+#ifdef QT_NO_SIZEGRIP
+ Q_UNUSED(enabled);
+#else
+ Q_D(QStatusBar);
+ if (!enabled != !d->resizer) {
+ if (enabled) {
+ d->resizer = new QSizeGrip(this);
+ d->resizer->hide();
+ d->resizer->installEventFilter(this);
+ d->showSizeGrip = true;
+ } else {
+ delete d->resizer;
+ d->resizer = 0;
+ d->showSizeGrip = false;
+ }
+ reformat();
+ if (d->resizer && isVisible())
+ d->tryToShowSizeGrip();
+ }
+#endif
+}
+
+
+/*!
+ Changes the status bar's appearance to account for item changes.
+
+ Special subclasses may need this function, but geometry management
+ will usually take care of any necessary rearrangements.
+*/
+void QStatusBar::reformat()
+{
+ Q_D(QStatusBar);
+ if (d->box)
+ delete d->box;
+
+ QBoxLayout *vbox;
+#ifndef QT_NO_SIZEGRIP
+ if (d->resizer) {
+ d->box = new QHBoxLayout(this);
+ d->box->setMargin(0);
+ vbox = new QVBoxLayout;
+ d->box->addLayout(vbox);
+ } else
+#endif
+ {
+ vbox = d->box = new QVBoxLayout(this);
+ d->box->setMargin(0);
+ }
+ vbox->addSpacing(3);
+ QBoxLayout* l = new QHBoxLayout;
+ vbox->addLayout(l);
+ l->addSpacing(2);
+ l->setSpacing(6);
+
+ int maxH = fontMetrics().height();
+
+ int i;
+ QStatusBarPrivate::SBItem* item;
+ for (i=0,item=0; i<d->items.size(); ++i) {
+ item = d->items.at(i);
+ if (!item || item->p)
+ break;
+ l->addWidget(item->w, item->s);
+ int itemH = qMin(qSmartMinSize(item->w).height(), item->w->maximumHeight());
+ maxH = qMax(maxH, itemH);
+ }
+
+ l->addStretch(0);
+
+ for (item=0; i<d->items.size(); ++i) {
+ item = d->items.at(i);
+ if (!item)
+ break;
+ l->addWidget(item->w, item->s);
+ int itemH = qMin(qSmartMinSize(item->w).height(), item->w->maximumHeight());
+ maxH = qMax(maxH, itemH);
+ }
+#ifndef QT_NO_SIZEGRIP
+ if (d->resizer) {
+ maxH = qMax(maxH, d->resizer->sizeHint().height());
+ d->box->addSpacing(1);
+ d->box->addWidget(d->resizer, 0, Qt::AlignBottom);
+ }
+#endif
+ l->addStrut(maxH);
+ d->savedStrut = maxH;
+ vbox->addSpacing(2);
+ d->box->activate();
+ repaint();
+}
+
+/*!
+
+ Hides the normal status indications and displays the given \a
+ message for the specified number of milli-seconds (\a{timeout}). If
+ \a{timeout} is 0 (default), the \a {message} remains displayed until
+ the clearMessage() slot is called or until the showMessage() slot is
+ called again to change the message.
+
+ Note that showMessage() is called to show temporary explanations of
+ tool tip texts, so passing a \a{timeout} of 0 is not sufficient to
+ display a \l{permanent message}{permanent message}.
+
+ \sa messageChanged(), currentMessage(), clearMessage()
+*/
+void QStatusBar::showMessage(const QString &message, int timeout)
+{
+ Q_D(QStatusBar);
+ if (d->tempItem == message)
+ return;
+
+ d->tempItem = message;
+
+ if (timeout > 0) {
+ if (!d->timer) {
+ d->timer = new QTimer(this);
+ connect(d->timer, SIGNAL(timeout()), this, SLOT(clearMessage()));
+ }
+ d->timer->start(timeout);
+ } else if (d->timer) {
+ delete d->timer;
+ d->timer = 0;
+ }
+
+ hideOrShow();
+}
+
+/*!
+ Removes any temporary message being shown.
+
+ \sa currentMessage(), showMessage(), removeWidget()
+*/
+
+void QStatusBar::clearMessage()
+{
+ Q_D(QStatusBar);
+ if (d->tempItem.isEmpty())
+ return;
+ if (d->timer) {
+ qDeleteInEventHandler(d->timer);
+ d->timer = 0;
+ }
+ d->tempItem.clear();
+ hideOrShow();
+}
+
+/*!
+ Returns the temporary message currently shown,
+ or an empty string if there is no such message.
+
+ \sa showMessage()
+*/
+QString QStatusBar::currentMessage() const
+{
+ Q_D(const QStatusBar);
+ return d->tempItem;
+}
+
+/*!
+ \fn void QStatusBar::message(const QString &message, int timeout)
+
+ Use the showMessage() function instead.
+*/
+
+/*!
+ \fn void QStatusBar::clear()
+
+ Use the clearMessage() function instead.
+*/
+
+/*!
+ \fn QStatusBar::messageChanged(const QString &message)
+
+ This signal is emitted whenever the temporary status message
+ changes. The new temporary message is passed in the \a message
+ parameter which is a null-string when the message has been
+ removed.
+
+ \sa showMessage(), clearMessage()
+*/
+
+/*!
+ Ensures that the right widgets are visible.
+
+ Used by the showMessage() and clearMessage() functions.
+*/
+void QStatusBar::hideOrShow()
+{
+ Q_D(QStatusBar);
+ bool haveMessage = !d->tempItem.isEmpty();
+
+ QStatusBarPrivate::SBItem* item = 0;
+ for (int i=0; i<d->items.size(); ++i) {
+ item = d->items.at(i);
+ if (!item || item->p)
+ break;
+ if (haveMessage && item->w->isVisible()) {
+ item->w->hide();
+ item->w->setAttribute(Qt::WA_WState_ExplicitShowHide, false);
+ } else if (!haveMessage && !item->w->testAttribute(Qt::WA_WState_ExplicitShowHide)) {
+ item->w->show();
+ }
+ }
+
+ emit messageChanged(d->tempItem);
+ repaint(d->messageRect());
+}
+
+/*!
+ \reimp
+ */
+void QStatusBar::showEvent(QShowEvent *)
+{
+#ifndef QT_NO_SIZEGRIP
+ Q_D(QStatusBar);
+ if (d->resizer && d->showSizeGrip)
+ d->tryToShowSizeGrip();
+#endif
+}
+
+/*!
+ \reimp
+ \fn void QStatusBar::paintEvent(QPaintEvent *event)
+
+ Shows the temporary message, if appropriate, in response to the
+ paint \a event.
+*/
+void QStatusBar::paintEvent(QPaintEvent *event)
+{
+ Q_D(QStatusBar);
+ bool haveMessage = !d->tempItem.isEmpty();
+
+ QPainter p(this);
+ QStyleOption opt;
+ opt.initFrom(this);
+ style()->drawPrimitive(QStyle::PE_PanelStatusBar, &opt, &p, this);
+
+ for (int i=0; i<d->items.size(); ++i) {
+ QStatusBarPrivate::SBItem* item = d->items.at(i);
+ if (item && item->w->isVisible() && (!haveMessage || item->p)) {
+ QRect ir = item->w->geometry().adjusted(-2, -1, 2, 1);
+ if (event->rect().contains(ir)) {
+ QStyleOption opt(0);
+ opt.rect = ir;
+ opt.palette = palette();
+ opt.state = QStyle::State_None;
+ style()->drawPrimitive(QStyle::PE_FrameStatusBarItem, &opt, &p, item->w);
+ }
+ }
+ }
+ if (haveMessage) {
+ p.setPen(palette().foreground().color());
+ p.drawText(d->messageRect(), Qt::AlignLeading | Qt::AlignVCenter | Qt::TextSingleLine, d->tempItem);
+ }
+}
+
+/*!
+ \reimp
+*/
+void QStatusBar::resizeEvent(QResizeEvent * e)
+{
+ QWidget::resizeEvent(e);
+}
+
+/*!
+ \reimp
+*/
+
+bool QStatusBar::event(QEvent *e)
+{
+ Q_D(QStatusBar);
+
+ if (e->type() == QEvent::LayoutRequest
+#ifdef QT3_SUPPORT
+ || e->type() == QEvent::LayoutHint
+#endif
+ ) {
+ // Calculate new strut height and call reformat() if it has changed
+ int maxH = fontMetrics().height();
+
+ QStatusBarPrivate::SBItem* item = 0;
+ for (int i=0; i<d->items.size(); ++i) {
+ item = d->items.at(i);
+ if (!item)
+ break;
+ int itemH = qMin(qSmartMinSize(item->w).height(), item->w->maximumHeight());
+ maxH = qMax(maxH, itemH);
+ }
+
+#ifndef QT_NO_SIZEGRIP
+ if (d->resizer)
+ maxH = qMax(maxH, d->resizer->sizeHint().height());
+#endif
+
+ if (maxH != d->savedStrut)
+ reformat();
+ else
+ update();
+ }
+ if (e->type() == QEvent::ChildRemoved) {
+ QStatusBarPrivate::SBItem* item = 0;
+ for (int i=0; i<d->items.size(); ++i) {
+ item = d->items.at(i);
+ if (!item)
+ break;
+ if (item->w == ((QChildEvent*)e)->child()) {
+ d->items.removeAt(i);
+ delete item;
+ }
+ }
+ }
+
+// On Mac OS X Leopard it is possible to drag the window by clicking
+// on the tool bar on most applications.
+#ifndef Q_WS_MAC
+ return QWidget::event(e);
+#else
+ if (QSysInfo::MacintoshVersion <= QSysInfo::MV_10_4)
+ return QWidget::event(e);
+
+ // Enable drag-click only if the status bar is the status bar for a
+ // QMainWindow with a unifed toolbar.
+ if (parent() == 0 || qobject_cast<QMainWindow *>(parent()) == 0 ||
+ qobject_cast<QMainWindow *>(parent())->unifiedTitleAndToolBarOnMac() == false )
+ return QWidget::event(e);
+
+ // Check for mouse events.
+ QMouseEvent *mouseEvent;
+ if (e->type() == QEvent::MouseButtonPress ||
+ e->type() == QEvent::MouseMove ||
+ e->type() == QEvent::MouseButtonRelease) {
+ mouseEvent = static_cast <QMouseEvent*>(e);
+ } else {
+ return QWidget::event(e);
+ }
+
+ // The following is a standard mouse drag handler.
+ if (e->type() == QEvent::MouseButtonPress && (mouseEvent->button() == Qt::LeftButton)) {
+ d->dragStart = mouseEvent->pos();
+ } else if (e->type() == QEvent::MouseMove){
+ if (d->dragStart == QPoint())
+ return QWidget::event(e);
+ QPoint pos = mouseEvent->pos();
+ QPoint delta = (pos - d->dragStart);
+ window()->move(window()->pos() + delta);
+ } else if (e->type() == QEvent::MouseButtonRelease && (mouseEvent->button() == Qt::LeftButton)){
+ d->dragStart = QPoint();
+ } else {
+ return QWidget::event(e);
+ }
+
+ return true;
+#endif
+}
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/gui/widgets/qstatusbar.h b/src/gui/widgets/qstatusbar.h
new file mode 100644
index 0000000000..4434f745df
--- /dev/null
+++ b/src/gui/widgets/qstatusbar.h
@@ -0,0 +1,116 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSTATUSBAR_H
+#define QSTATUSBAR_H
+
+#include <QtGui/qwidget.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+#ifndef QT_NO_STATUSBAR
+
+class QStatusBarPrivate;
+
+class Q_GUI_EXPORT QStatusBar: public QWidget
+{
+ Q_OBJECT
+
+ Q_PROPERTY(bool sizeGripEnabled READ isSizeGripEnabled WRITE setSizeGripEnabled)
+
+public:
+ explicit QStatusBar(QWidget* parent=0);
+ virtual ~QStatusBar();
+
+ void addWidget(QWidget *widget, int stretch = 0);
+ int insertWidget(int index, QWidget *widget, int stretch = 0);
+ void addPermanentWidget(QWidget *widget, int stretch = 0);
+ int insertPermanentWidget(int index, QWidget *widget, int stretch = 0);
+ void removeWidget(QWidget *widget);
+
+ void setSizeGripEnabled(bool);
+ bool isSizeGripEnabled() const;
+
+ QString currentMessage() const;
+
+public Q_SLOTS:
+ void showMessage(const QString &text, int timeout = 0);
+ void clearMessage();
+
+#ifdef QT3_SUPPORT
+public:
+ QT3_SUPPORT_CONSTRUCTOR QStatusBar(QWidget* parent, const char* name);
+ QT3_SUPPORT void addWidget(QWidget *w, int stretch, bool permanent)
+ { if (permanent) addPermanentWidget(w, stretch); else addWidget(w, stretch); }
+public Q_SLOTS:
+ inline QT_MOC_COMPAT void message(const QString &text, int timeout = 0) { showMessage(text, timeout); }
+ inline QT_MOC_COMPAT void clear() { clearMessage(); }
+#endif
+
+Q_SIGNALS:
+ void messageChanged(const QString &text);
+
+protected:
+ void showEvent(QShowEvent *);
+ void paintEvent(QPaintEvent *);
+ void resizeEvent(QResizeEvent *);
+
+ // ### Qt 5: consider making reformat() and hideOrShow() private
+ void reformat();
+ void hideOrShow();
+ bool event(QEvent *);
+
+private:
+ Q_DISABLE_COPY(QStatusBar)
+ Q_DECLARE_PRIVATE(QStatusBar)
+};
+
+#endif // QT_NO_STATUSBAR
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QSTATUSBAR_H
diff --git a/src/gui/widgets/qtabbar.cpp b/src/gui/widgets/qtabbar.cpp
new file mode 100644
index 0000000000..7d970ad10f
--- /dev/null
+++ b/src/gui/widgets/qtabbar.cpp
@@ -0,0 +1,2301 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "private/qlayoutengine_p.h"
+#include "qabstractitemdelegate.h"
+#include "qapplication.h"
+#include "qbitmap.h"
+#include "qcursor.h"
+#include "qevent.h"
+#include "qpainter.h"
+#include "qstyle.h"
+#include "qstyleoption.h"
+#include "qstylepainter.h"
+#include "qtabwidget.h"
+#include "qtooltip.h"
+#include "qwhatsthis.h"
+#include "private/qtextengine_p.h"
+#ifndef QT_NO_ACCESSIBILITY
+#include "qaccessible.h"
+#endif
+
+#include "qdebug.h"
+#include "private/qtabbar_p.h"
+
+#ifndef QT_NO_TABBAR
+
+#ifdef Q_WS_MAC
+#include <private/qt_mac_p.h>
+#include <private/qt_cocoa_helpers_mac_p.h>
+#endif
+
+QT_BEGIN_NAMESPACE
+
+inline static bool verticalTabs(QTabBar::Shape shape)
+{
+ return shape == QTabBar::RoundedWest
+ || shape == QTabBar::RoundedEast
+ || shape == QTabBar::TriangularWest
+ || shape == QTabBar::TriangularEast;
+}
+
+void QTabBarPrivate::updateMacBorderMetrics()
+{
+#if (defined Q_WS_MAC) && (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5)
+ if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_5) {
+ Q_Q(QTabBar);
+ ::HIContentBorderMetrics metrics;
+
+ // TODO: get metrics to preserve the bottom value
+ // TODO: test tab bar position
+
+ // push the black line at the bottom of the menu bar down to the client are so we can paint over it
+ metrics.top = (documentMode && q->isVisible()) ? 1 : 0;
+ metrics.bottom = 0;
+ metrics.left = 0;
+ metrics.right = 0;
+
+ qt_mac_updateContentBorderMetricts(qt_mac_window_for(q), metrics);
+ }
+#endif
+}
+
+/*!
+ Initialize \a option with the values from the tab at \a tabIndex. This method
+ is useful for subclasses when they need a QStyleOptionTab, QStyleOptionTabV2,
+ or QStyleOptionTabV3 but don't want to fill in all the information themselves.
+ This function will check the version of the QStyleOptionTab and fill in the
+ additional values for a QStyleOptionTabV2 and QStyleOptionTabV3.
+
+ \sa QStyleOption::initFrom() QTabWidget::initStyleOption()
+*/
+void QTabBar::initStyleOption(QStyleOptionTab *option, int tabIndex) const
+{
+ Q_D(const QTabBar);
+ int totalTabs = d->tabList.size();
+
+ if (!option || (tabIndex < 0 || tabIndex >= totalTabs))
+ return;
+
+ const QTabBarPrivate::Tab &tab = d->tabList.at(tabIndex);
+ option->initFrom(this);
+ option->state &= ~(QStyle::State_HasFocus | QStyle::State_MouseOver);
+ option->rect = tabRect(tabIndex);
+ bool isCurrent = tabIndex == d->currentIndex;
+ option->row = 0;
+ if (tabIndex == d->pressedIndex)
+ option->state |= QStyle::State_Sunken;
+ if (isCurrent)
+ option->state |= QStyle::State_Selected;
+ if (isCurrent && hasFocus())
+ option->state |= QStyle::State_HasFocus;
+ if (!tab.enabled)
+ option->state &= ~QStyle::State_Enabled;
+ if (isActiveWindow())
+ option->state |= QStyle::State_Active;
+ if (option->rect == d->hoverRect)
+ option->state |= QStyle::State_MouseOver;
+ option->shape = d->shape;
+ option->text = tab.text;
+
+ if (tab.textColor.isValid())
+ option->palette.setColor(foregroundRole(), tab.textColor);
+
+ option->icon = tab.icon;
+ if (QStyleOptionTabV2 *optionV2 = qstyleoption_cast<QStyleOptionTabV2 *>(option))
+ optionV2->iconSize = iconSize(); // Will get the default value then.
+
+ if (QStyleOptionTabV3 *optionV3 = qstyleoption_cast<QStyleOptionTabV3 *>(option)) {
+ optionV3->leftButtonSize = tab.leftWidget ? tab.leftWidget->size() : QSize();
+ optionV3->rightButtonSize = tab.rightWidget ? tab.rightWidget->size() : QSize();
+ optionV3->documentMode = d->documentMode;
+ }
+
+ if (tabIndex > 0 && tabIndex - 1 == d->currentIndex)
+ option->selectedPosition = QStyleOptionTab::PreviousIsSelected;
+ else if (tabIndex < totalTabs - 1 && tabIndex + 1 == d->currentIndex)
+ option->selectedPosition = QStyleOptionTab::NextIsSelected;
+ else
+ option->selectedPosition = QStyleOptionTab::NotAdjacent;
+
+ bool paintBeginning = (tabIndex == 0) || (d->dragInProgress && tabIndex == d->pressedIndex + 1);
+ bool paintEnd = (tabIndex == totalTabs - 1) || (d->dragInProgress && tabIndex == d->pressedIndex - 1);
+ if (paintBeginning) {
+ if (paintEnd)
+ option->position = QStyleOptionTab::OnlyOneTab;
+ else
+ option->position = QStyleOptionTab::Beginning;
+ } else if (paintEnd) {
+ option->position = QStyleOptionTab::End;
+ } else {
+ option->position = QStyleOptionTab::Middle;
+ }
+
+#ifndef QT_NO_TABWIDGET
+ if (const QTabWidget *tw = qobject_cast<const QTabWidget *>(parentWidget())) {
+ if (tw->cornerWidget(Qt::TopLeftCorner) || tw->cornerWidget(Qt::BottomLeftCorner))
+ option->cornerWidgets |= QStyleOptionTab::LeftCornerWidget;
+ if (tw->cornerWidget(Qt::TopRightCorner) || tw->cornerWidget(Qt::BottomRightCorner))
+ option->cornerWidgets |= QStyleOptionTab::RightCornerWidget;
+ }
+
+ QRect textRect = style()->subElementRect(QStyle::SE_TabBarTabText, option, this);
+
+ option->text = fontMetrics().elidedText(option->text, d->elideMode, textRect.width(),
+ Qt::TextShowMnemonic);
+#endif
+}
+
+/*!
+ \class QTabBar
+ \brief The QTabBar class provides a tab bar, e.g. for use in tabbed dialogs.
+
+ \ingroup basicwidgets
+ \mainclass
+
+ QTabBar is straightforward to use; it draws the tabs using one of
+ the predefined \link QTabBar::Shape shapes\endlink, and emits a
+ signal when a tab is selected. It can be subclassed to tailor the
+ look and feel. Qt also provides a ready-made \l{QTabWidget}.
+
+ Each tab has a tabText(), an optional tabIcon(), an optional
+ tabToolTip(), optional tabWhatsThis() and optional tabData().
+ The tabs's attributes can be changed with setTabText(), setTabIcon(),
+ setTabToolTip(), setTabWhatsThis and setTabData(). Each tabs can be
+ enabled or disabled individually with setTabEnabled().
+
+ Each tab can display text in a distinct color. The current text color
+ for a tab can be found with the tabTextColor() function. Set the text
+ color for a particular tab with setTabTextColor().
+
+ Tabs are added using addTab(), or inserted at particular positions
+ using insertTab(). The total number of tabs is given by
+ count(). Tabs can be removed from the tab bar with
+ removeTab(). Combining removeTab() and insertTab() allows you to
+ move tabs to different positions.
+
+ The \l shape property defines the tabs' appearance. The choice of
+ shape is a matter of taste, although tab dialogs (for preferences
+ and similar) invariably use \l RoundedNorth.
+ Tab controls in windows other than dialogs almost
+ always use either \l RoundedSouth or \l TriangularSouth. Many
+ spreadsheets and other tab controls in which all the pages are
+ essentially similar use \l TriangularSouth, whereas \l
+ RoundedSouth is used mostly when the pages are different (e.g. a
+ multi-page tool palette). The default in QTabBar is \l
+ RoundedNorth.
+
+ The most important part of QTabBar's API is the currentChanged()
+ signal. This is emitted whenever the current tab changes (even at
+ startup, when the current tab changes from 'none'). There is also
+ a slot, setCurrentIndex(), which can be used to select a tab
+ programmatically. The function currentIndex() returns the index of
+ the current tab, \l count holds the number of tabs.
+
+ QTabBar creates automatic mnemonic keys in the manner of QAbstractButton;
+ e.g. if a tab's label is "\&Graphics", Alt+G becomes a shortcut
+ key for switching to that tab.
+
+ The following virtual functions may need to be reimplemented in
+ order to tailor the look and feel or store extra data with each
+ tab:
+
+ \list
+ \i tabSizeHint() calcuates the size of a tab.
+ \i tabInserted() notifies that a new tab was added.
+ \i tabRemoved() notifies that a tab was removed.
+ \i tabLayoutChange() notifies that the tabs have been re-laid out.
+ \i paintEvent() paints all tabs.
+ \endlist
+
+ For subclasses, you might also need the tabRect() functions which
+ returns the visual geometry of a single tab.
+
+ \table 100%
+ \row \o \inlineimage plastique-tabbar.png Screenshot of a Plastique style tab bar
+ \o A tab bar shown in the Plastique widget style.
+ \row \o \inlineimage plastique-tabbar-truncated.png Screenshot of a truncated Plastique tab bar
+ \o A truncated tab bar shown in the Plastique widget style.
+ \endtable
+
+ \sa QTabWidget
+*/
+
+/*!
+ \enum QTabBar::Shape
+
+ This enum type lists the built-in shapes supported by QTabBar. Treat these
+ as hints as some styles may not render some of the shapes. However,
+ position should be honored.
+
+ \value RoundedNorth The normal rounded look above the pages
+
+ \value RoundedSouth The normal rounded look below the pages
+
+ \value RoundedWest The normal rounded look on the left side of the pages
+
+ \value RoundedEast The normal rounded look on the right side the pages
+
+ \value TriangularNorth Triangular tabs above the pages.
+
+ \value TriangularSouth Triangular tabs similar to those used in
+ the Excel spreadsheet, for example
+
+ \value TriangularWest Triangular tabs on the left of the pages.
+
+ \value TriangularEast Triangular tabs on the right of the pages.
+ \omitvalue RoundedAbove
+ \omitvalue RoundedBelow
+ \omitvalue TriangularAbove
+ \omitvalue TriangularBelow
+*/
+
+/*!
+ \fn void QTabBar::currentChanged(int index)
+
+ This signal is emitted when the tab bar's current tab changes. The
+ new current has the given \a index, or -1 if there isn't a new one
+ (for example, if there are no tab in the QTabBar)
+*/
+
+/*!
+ \fn void QTabBar::tabCloseRequested(int index)
+ \since 4.5
+
+ This signal is emitted when the close button on a tab is clicked.
+ The \a index is the index that should be removed.
+
+ \sa setTabsClosable()
+*/
+
+/*!
+ \fn void QTabBar::tabMoved(int from, int to)
+ \since 4.5
+
+ This signal is emitted when the tab has moved the tab
+ at index position \a from to index position \a to.
+
+ note: QTabWidget will automatically move the page when
+ this signal is emitted from its tab bar.
+
+ \sa moveTab()
+*/
+
+int QTabBarPrivate::extraWidth() const
+{
+ Q_Q(const QTabBar);
+ return 2 * qMax(q->style()->pixelMetric(QStyle::PM_TabBarScrollButtonWidth, 0, q),
+ QApplication::globalStrut().width());
+}
+
+void QTabBarPrivate::init()
+{
+ Q_Q(QTabBar);
+ leftB = new QToolButton(q);
+ leftB->setAutoRepeat(true);
+ QObject::connect(leftB, SIGNAL(clicked()), q, SLOT(_q_scrollTabs()));
+ leftB->hide();
+ rightB = new QToolButton(q);
+ rightB->setAutoRepeat(true);
+ QObject::connect(rightB, SIGNAL(clicked()), q, SLOT(_q_scrollTabs()));
+ rightB->hide();
+#ifdef QT_KEYPAD_NAVIGATION
+ if (QApplication::keypadNavigationEnabled()) {
+ leftB->setFocusPolicy(Qt::NoFocus);
+ rightB->setFocusPolicy(Qt::NoFocus);
+ q->setFocusPolicy(Qt::NoFocus);
+ } else
+#endif
+ q->setFocusPolicy(Qt::TabFocus);
+ q->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
+ elideMode = Qt::TextElideMode(q->style()->styleHint(QStyle::SH_TabBar_ElideMode, 0, q));
+ useScrollButtons = !q->style()->styleHint(QStyle::SH_TabBar_PreferNoArrows, 0, q);
+}
+
+QTabBarPrivate::Tab *QTabBarPrivate::at(int index)
+{
+ return validIndex(index)?&tabList[index]:0;
+}
+
+const QTabBarPrivate::Tab *QTabBarPrivate::at(int index) const
+{
+ return validIndex(index)?&tabList[index]:0;
+}
+
+int QTabBarPrivate::indexAtPos(const QPoint &p) const
+{
+ Q_Q(const QTabBar);
+ if (q->tabRect(currentIndex).contains(p))
+ return currentIndex;
+ for (int i = 0; i < tabList.count(); ++i)
+ if (tabList.at(i).enabled && q->tabRect(i).contains(p))
+ return i;
+ return -1;
+}
+
+void QTabBarPrivate::layoutTabs()
+{
+ Q_Q(QTabBar);
+ scrollOffset = 0;
+ layoutDirty = false;
+ QSize size = q->size();
+ int last, available;
+ int maxExtent;
+ int i;
+ bool vertTabs = verticalTabs(shape);
+ int tabChainIndex = 0;
+
+ Qt::Alignment tabAlignment = Qt::Alignment(q->style()->styleHint(QStyle::SH_TabBar_Alignment, 0, q));
+ QVector<QLayoutStruct> tabChain(tabList.count() + 2);
+
+ // We put an empty item at the front and back and set its expansive attribute
+ // depending on tabAlignment.
+ tabChain[tabChainIndex].init();
+ tabChain[tabChainIndex].expansive = (tabAlignment != Qt::AlignLeft)
+ && (tabAlignment != Qt::AlignJustify);
+ tabChain[tabChainIndex].empty = true;
+ ++tabChainIndex;
+
+ // We now go through our list of tabs and set the minimum size and the size hint
+ // This will allow us to elide text if necessary. Since we don't set
+ // a maximum size, tabs will EXPAND to fill up the empty space.
+ // Since tab widget is rather *ahem* strict about keeping the geometry of the
+ // tab bar to its absolute minimum, this won't bleed through, but will show up
+ // if you use tab bar on its own (a.k.a. not a bug, but a feature).
+ // Update: if expanding is false, we DO set a maximum size to prevent the tabs
+ // being wider than necessary.
+ if (!vertTabs) {
+ int minx = 0;
+ int x = 0;
+ int maxHeight = 0;
+ for (i = 0; i < tabList.count(); ++i, ++tabChainIndex) {
+ QSize sz = q->tabSizeHint(i);
+ tabList[i].maxRect = QRect(x, 0, sz.width(), sz.height());
+ x += sz.width();
+ maxHeight = qMax(maxHeight, sz.height());
+ sz = minimumTabSizeHint(i);
+ tabList[i].minRect = QRect(minx, 0, sz.width(), sz.height());
+ minx += sz.width();
+ tabChain[tabChainIndex].init();
+ tabChain[tabChainIndex].sizeHint = tabList.at(i).maxRect.width();
+ tabChain[tabChainIndex].minimumSize = sz.width();
+ tabChain[tabChainIndex].empty = false;
+ tabChain[tabChainIndex].expansive = true;
+
+ if (!expanding)
+ tabChain[tabChainIndex].maximumSize = tabChain[tabChainIndex].sizeHint;
+ }
+
+ last = minx;
+ available = size.width();
+ maxExtent = maxHeight;
+ } else {
+ int miny = 0;
+ int y = 0;
+ int maxWidth = 0;
+ for (i = 0; i < tabList.count(); ++i, ++tabChainIndex) {
+ QSize sz = q->tabSizeHint(i);
+ tabList[i].maxRect = QRect(0, y, sz.width(), sz.height());
+ y += sz.height();
+ maxWidth = qMax(maxWidth, sz.width());
+ sz = minimumTabSizeHint(i);
+ tabList[i].minRect = QRect(0, miny, sz.width(), sz.height());
+ miny += sz.height();
+ tabChain[tabChainIndex].init();
+ tabChain[tabChainIndex].sizeHint = tabList.at(i).maxRect.height();
+ tabChain[tabChainIndex].minimumSize = sz.height();
+ tabChain[tabChainIndex].empty = false;
+ tabChain[tabChainIndex].expansive = true;
+
+ if (!expanding)
+ tabChain[tabChainIndex].maximumSize = tabChain[tabChainIndex].sizeHint;
+ }
+
+ last = miny;
+ available = size.height();
+ maxExtent = maxWidth;
+ }
+
+ if (pressedIndex != -1 && movable)
+ grabCache(0, tabList.count(), true);
+
+ Q_ASSERT(tabChainIndex == tabChain.count() - 1); // add an assert just to make sure.
+ // Mirror our front item.
+ tabChain[tabChainIndex].init();
+ tabChain[tabChainIndex].expansive = (tabAlignment != Qt::AlignRight)
+ && (tabAlignment != Qt::AlignJustify);
+ tabChain[tabChainIndex].empty = true;
+
+ // Do the calculation
+ qGeomCalc(tabChain, 0, tabChain.count(), 0, qMax(available, last), 0);
+
+ // Use the results
+ for (i = 0; i < tabList.count(); ++i) {
+ const QLayoutStruct &lstruct = tabChain.at(i + 1);
+ if (!vertTabs)
+ tabList[i].rect.setRect(lstruct.pos, 0, lstruct.size, maxExtent);
+ else
+ tabList[i].rect.setRect(0, lstruct.pos, maxExtent, lstruct.size);
+ }
+
+ if (useScrollButtons && tabList.count() && last > available) {
+ int extra = extraWidth();
+ if (!vertTabs) {
+ Qt::LayoutDirection ld = q->layoutDirection();
+ QRect arrows = QStyle::visualRect(ld, q->rect(),
+ QRect(available - extra, 0, extra, size.height()));
+ int buttonOverlap = q->style()->pixelMetric(QStyle::PM_TabBar_ScrollButtonOverlap, 0, q);
+
+ if (ld == Qt::LeftToRight) {
+ leftB->setGeometry(arrows.left(), arrows.top(), extra/2, arrows.height());
+ rightB->setGeometry(arrows.right() - extra/2 + buttonOverlap, arrows.top(),
+ extra/2, arrows.height());
+ leftB->setArrowType(Qt::LeftArrow);
+ rightB->setArrowType(Qt::RightArrow);
+ } else {
+ rightB->setGeometry(arrows.left(), arrows.top(), extra/2, arrows.height());
+ leftB->setGeometry(arrows.right() - extra/2 + buttonOverlap, arrows.top(),
+ extra/2, arrows.height());
+ rightB->setArrowType(Qt::LeftArrow);
+ leftB->setArrowType(Qt::RightArrow);
+ }
+ } else {
+ QRect arrows = QRect(0, available - extra, size.width(), extra );
+ leftB->setGeometry(arrows.left(), arrows.top(), arrows.width(), extra/2);
+ leftB->setArrowType(Qt::UpArrow);
+ rightB->setGeometry(arrows.left(), arrows.bottom() - extra/2 + 1,
+ arrows.width(), extra/2);
+ rightB->setArrowType(Qt::DownArrow);
+ }
+ leftB->setEnabled(scrollOffset > 0);
+ rightB->setEnabled(last - scrollOffset >= available - extra);
+ leftB->show();
+ rightB->show();
+ } else {
+ rightB->hide();
+ leftB->hide();
+ }
+
+ layoutWidgets();
+ q->tabLayoutChange();
+}
+
+void QTabBarPrivate::makeVisible(int index)
+{
+ Q_Q(QTabBar);
+ if (!validIndex(index) || leftB->isHidden())
+ return;
+
+ const QRect tabRect = tabList.at(index).rect;
+ const int oldScrollOffset = scrollOffset;
+ const bool horiz = !verticalTabs(shape);
+ const int available = (horiz ? q->width() : q->height()) - extraWidth();
+ const int start = horiz ? tabRect.left() : tabRect.top();
+ const int end = horiz ? tabRect.right() : tabRect.bottom();
+ if (start < scrollOffset) // too far left
+ scrollOffset = start - (index ? 8 : 0);
+ else if (end > scrollOffset + available) // too far right
+ scrollOffset = end - available + 1;
+
+ leftB->setEnabled(scrollOffset > 0);
+ const int last = horiz ? tabList.last().rect.right() : tabList.last().rect.bottom();
+ rightB->setEnabled(last - scrollOffset >= available);
+ if (oldScrollOffset != scrollOffset) {
+ q->update();
+ layoutWidgets();
+ }
+}
+
+void QTabBarPrivate::layoutTab(int index)
+{
+ Q_Q(QTabBar);
+ Q_ASSERT(index >= 0);
+
+ Tab &tab = tabList[index];
+ bool vertical = verticalTabs(shape);
+ if (!(tab.leftWidget || tab.rightWidget))
+ return;
+
+ QStyleOptionTabV3 opt;
+ q->initStyleOption(&opt, index);
+ if (tab.leftWidget) {
+ QRect rect = q->style()->subElementRect(QStyle::SE_TabBarTabLeftButton, &opt, q);
+ QPoint p = rect.topLeft();
+ if ((index == pressedIndex) || paintWithOffsets) {
+ if (vertical)
+ p.setY(p.y() + tabList[index].dragOffset);
+ else
+ p.setX(p.x() + tabList[index].dragOffset);
+ }
+ tab.leftWidget->move(p);
+ }
+ if (tab.rightWidget) {
+ QRect rect = q->style()->subElementRect(QStyle::SE_TabBarTabRightButton, &opt, q);
+ QPoint p = rect.topLeft();
+ if ((index == pressedIndex) || paintWithOffsets) {
+ if (vertical)
+ p.setY(p.y() + tab.dragOffset);
+ else
+ p.setX(p.x() + tab.dragOffset);
+ }
+ tab.rightWidget->move(p);
+ }
+}
+
+void QTabBarPrivate::layoutWidgets(int index)
+{
+ Q_Q(QTabBar);
+ int start = 0;
+ int end = q->count();
+ if (index != -1) {
+ start = qMax(index, 0);
+ end = qMin(end, start + 1);
+ }
+ for (int i = start; i < end; ++i) {
+ layoutTab(i);
+ }
+}
+
+void QTabBarPrivate::_q_closeTab()
+{
+ Q_Q(QTabBar);
+ QObject *object = q->sender();
+ int tabToClose = -1;
+ QTabBar::ButtonPosition closeSide = (QTabBar::ButtonPosition)q->style()->styleHint(QStyle::SH_TabBar_CloseButtonPosition, 0, q);
+ for (int i = 0; i < tabList.count(); ++i) {
+ if (closeSide == QTabBar::LeftSide) {
+ if (tabList.at(i).leftWidget == object) {
+ tabToClose = i;
+ break;
+ }
+ } else {
+ if (tabList.at(i).rightWidget == object) {
+ tabToClose = i;
+ break;
+ }
+ }
+ }
+ if (tabToClose != -1)
+ emit q->tabCloseRequested(tabToClose);
+}
+
+void QTabBarPrivate::_q_scrollTabs()
+{
+ Q_Q(QTabBar);
+ const QObject *sender = q->sender();
+ int i = -1;
+ if (!verticalTabs(shape)) {
+ if (sender == leftB) {
+ for (i = tabList.count() - 1; i >= 0; --i) {
+ if (tabList.at(i).rect.left() - scrollOffset < 0) {
+ makeVisible(i);
+ return;
+ }
+ }
+ } else if (sender == rightB) {
+ int availableWidth = q->width() - extraWidth();
+ for (i = 0; i < tabList.count(); ++i) {
+ if (tabList.at(i).rect.right() - scrollOffset > availableWidth) {
+ makeVisible(i);
+ return;
+ }
+ }
+ }
+ } else { // vertical
+ if (sender == leftB) {
+ for (i = tabList.count() - 1; i >= 0; --i) {
+ if (tabList.at(i).rect.top() - scrollOffset < 0) {
+ makeVisible(i);
+ return;
+ }
+ }
+ } else if (sender == rightB) {
+ int available = q->height() - extraWidth();
+ for (i = 0; i < tabList.count(); ++i) {
+ if (tabList.at(i).rect.bottom() - scrollOffset > available) {
+ makeVisible(i);
+ return;
+ }
+ }
+ }
+ }
+}
+
+void QTabBarPrivate::refresh()
+{
+ Q_Q(QTabBar);
+
+ // be safe in case a subclass is also handling move with the tabs
+ if (pressedIndex != -1
+ && movable
+ && QApplication::mouseButtons() == Qt::NoButton) {
+ _q_moveTabFinished(pressedIndex);
+ if (!validIndex(pressedIndex))
+ pressedIndex = -1;
+ }
+
+ if (!q->isVisible()) {
+ layoutDirty = true;
+ } else {
+ layoutTabs();
+ makeVisible(currentIndex);
+ q->update();
+ q->updateGeometry();
+ }
+}
+
+/*!
+ Creates a new tab bar with the given \a parent.
+*/
+QTabBar::QTabBar(QWidget* parent)
+ :QWidget(*new QTabBarPrivate, parent, 0)
+{
+ Q_D(QTabBar);
+ d->init();
+}
+
+
+/*!
+ Destroys the tab bar.
+*/
+QTabBar::~QTabBar()
+{
+}
+
+/*!
+ \property QTabBar::shape
+ \brief the shape of the tabs in the tab bar
+
+ Possible values for this property are described by the Shape enum.
+*/
+
+
+QTabBar::Shape QTabBar::shape() const
+{
+ Q_D(const QTabBar);
+ return d->shape;
+}
+
+void QTabBar::setShape(Shape shape)
+{
+ Q_D(QTabBar);
+ if (d->shape == shape)
+ return;
+ d->shape = shape;
+ d->refresh();
+}
+
+/*!
+ \property QTabBar::drawBase
+ \brief defines whether or not tab bar should draw its base.
+
+ If true then QTabBar draws a base in relation to the styles overlab.
+ Otherwise only the tabs are drawn.
+
+ \sa QStyle::pixelMetric() QStyle::PM_TabBarBaseOverlap QStyleOptionTabBarBaseV2
+*/
+
+void QTabBar::setDrawBase(bool drawBase)
+{
+ Q_D(QTabBar);
+ if (d->drawBase == drawBase)
+ return;
+ d->drawBase = drawBase;
+ update();
+}
+
+bool QTabBar::drawBase() const
+{
+ Q_D(const QTabBar);
+ return d->drawBase;
+}
+
+/*!
+ Adds a new tab with text \a text. Returns the new
+ tab's index.
+*/
+int QTabBar::addTab(const QString &text)
+{
+ return insertTab(-1, text);
+}
+
+/*!
+ \overload
+
+ Adds a new tab with icon \a icon and text \a
+ text. Returns the new tab's index.
+*/
+int QTabBar::addTab(const QIcon& icon, const QString &text)
+{
+ return insertTab(-1, icon, text);
+}
+
+/*!
+ Inserts a new tab with text \a text at position \a index. If \a
+ index is out of range, the new tab is appened. Returns the new
+ tab's index.
+*/
+int QTabBar::insertTab(int index, const QString &text)
+{
+ return insertTab(index, QIcon(), text);
+}
+
+/*!\overload
+
+ Inserts a new tab with icon \a icon and text \a text at position
+ \a index. If \a index is out of range, the new tab is
+ appended. Returns the new tab's index.
+
+ If the QTabBar was empty before this function is called, the inserted tab
+ becomes the current tab.
+
+ Inserting a new tab at an index less than or equal to the current index
+ will increment the current index, but keep the current tab.
+*/
+int QTabBar::insertTab(int index, const QIcon& icon, const QString &text)
+{
+ Q_D(QTabBar);
+ if (!d->validIndex(index)) {
+ index = d->tabList.count();
+ d->tabList.append(QTabBarPrivate::Tab(icon, text));
+ } else {
+ d->tabList.insert(index, QTabBarPrivate::Tab(icon, text));
+ }
+#ifndef QT_NO_SHORTCUT
+ d->tabList[index].shortcutId = grabShortcut(QKeySequence::mnemonic(text));
+#endif
+ d->refresh();
+ if (d->tabList.count() == 1)
+ setCurrentIndex(index);
+ else if (index <= d->currentIndex)
+ ++d->currentIndex;
+
+ if (d->closeButtonOnTabs) {
+ QStyleOptionTabV3 opt;
+ initStyleOption(&opt, index);
+ ButtonPosition closeSide = (ButtonPosition)style()->styleHint(QStyle::SH_TabBar_CloseButtonPosition, 0, this);
+ QAbstractButton *closeButton = new CloseButton(this);
+ connect(closeButton, SIGNAL(clicked()), this, SLOT(_q_closeTab()));
+ setTabButton(index, closeSide, closeButton);
+ }
+
+ for (int i = 0; i < d->tabList.count(); ++i) {
+ if (d->tabList[i].lastTab >= index)
+ ++d->tabList[i].lastTab;
+ }
+
+ tabInserted(index);
+ return index;
+}
+
+
+/*!
+ Removes the tab at position \a index.
+
+ \sa SelectionBehavior
+ */
+void QTabBar::removeTab(int index)
+{
+ Q_D(QTabBar);
+ if (d->validIndex(index)) {
+#ifndef QT_NO_SHORTCUT
+ releaseShortcut(d->tabList.at(index).shortcutId);
+#endif
+ if (d->tabList[index].leftWidget) {
+ d->tabList[index].leftWidget->hide();
+ d->tabList[index].leftWidget->deleteLater();
+ d->tabList[index].leftWidget = 0;
+ }
+ if (d->tabList[index].rightWidget) {
+ d->tabList[index].rightWidget->hide();
+ d->tabList[index].rightWidget->deleteLater();
+ d->tabList[index].rightWidget = 0;
+ }
+
+ int newIndex = d->tabList[index].lastTab;
+ d->tabList.removeAt(index);
+ for (int i = 0; i < d->tabList.count(); ++i) {
+ if (d->tabList[i].lastTab == index)
+ d->tabList[i].lastTab = -1;
+ if (d->tabList[i].lastTab > index)
+ --d->tabList[i].lastTab;
+ }
+ if (index == d->currentIndex) {
+ // The current tab is going away, in order to make sure
+ // we emit that "current has changed", we need to reset this
+ // around.
+ d->currentIndex = -1;
+ if (d->tabList.size() > 0) {
+ switch(d->selectionBehaviorOnRemove) {
+ case SelectPreviousTab:
+ if (newIndex > index)
+ newIndex--;
+ if (d->validIndex(newIndex))
+ break;
+ // else fallthrough
+ case SelectRightTab:
+ newIndex = index;
+ if (newIndex >= d->tabList.size())
+ newIndex = d->tabList.size() - 1;
+ break;
+ case SelectLeftTab:
+ newIndex = index - 1;
+ if (newIndex < 0)
+ newIndex = 0;
+ break;
+ default:
+ break;
+ }
+
+ if (d->validIndex(newIndex)) {
+ // don't loose newIndex's old through setCurrentIndex
+ int bump = d->tabList[newIndex].lastTab;
+ setCurrentIndex(newIndex);
+ d->tabList[newIndex].lastTab = bump;
+ }
+ } else {
+ emit currentChanged(-1);
+ }
+ } else if (index < d->currentIndex) {
+ setCurrentIndex(d->currentIndex - 1);
+ }
+ d->refresh();
+ tabRemoved(index);
+ }
+}
+
+
+/*!
+ Returns true if the tab at position \a index is enabled; otherwise
+ returns false.
+*/
+bool QTabBar::isTabEnabled(int index) const
+{
+ Q_D(const QTabBar);
+ if (const QTabBarPrivate::Tab *tab = d->at(index))
+ return tab->enabled;
+ return false;
+}
+
+/*!
+ If \a enabled is true then the tab at position \a index is
+ enabled; otherwise the item at position \a index is disabled.
+*/
+void QTabBar::setTabEnabled(int index, bool enabled)
+{
+ Q_D(QTabBar);
+ if (QTabBarPrivate::Tab *tab = d->at(index)) {
+ tab->enabled = enabled;
+#ifndef QT_NO_SHORTCUT
+ setShortcutEnabled(tab->shortcutId, enabled);
+#endif
+ update();
+ if (!enabled && index == d->currentIndex)
+ setCurrentIndex(d->validIndex(index+1)?index+1:0);
+ else if (enabled && !d->validIndex(d->currentIndex))
+ setCurrentIndex(index);
+ }
+}
+
+
+/*!
+ Returns the text of the tab at position \a index, or an empty
+ string if \a index is out of range.
+*/
+QString QTabBar::tabText(int index) const
+{
+ Q_D(const QTabBar);
+ if (const QTabBarPrivate::Tab *tab = d->at(index))
+ return tab->text;
+ return QString();
+}
+
+/*!
+ Sets the text of the tab at position \a index to \a text.
+*/
+void QTabBar::setTabText(int index, const QString &text)
+{
+ Q_D(QTabBar);
+ if (QTabBarPrivate::Tab *tab = d->at(index)) {
+ tab->text = text;
+#ifndef QT_NO_SHORTCUT
+ releaseShortcut(tab->shortcutId);
+ tab->shortcutId = grabShortcut(QKeySequence::mnemonic(text));
+ setShortcutEnabled(tab->shortcutId, tab->enabled);
+#endif
+ d->refresh();
+ }
+}
+
+/*!
+ Returns the text color of the tab with the given \a index, or a invalid
+ color if \a index is out of range.
+
+ \sa setTabTextColor()
+*/
+QColor QTabBar::tabTextColor(int index) const
+{
+ Q_D(const QTabBar);
+ if (const QTabBarPrivate::Tab *tab = d->at(index))
+ return tab->textColor;
+ return QColor();
+}
+
+/*!
+ Sets the color of the text in the tab with the given \a index to the specified \a color.
+
+ If an invalid color is specified, the tab will use the QTabBar foreground role instead.
+
+ \sa tabTextColor()
+*/
+void QTabBar::setTabTextColor(int index, const QColor &color)
+{
+ Q_D(QTabBar);
+ if (QTabBarPrivate::Tab *tab = d->at(index)) {
+ tab->textColor = color;
+ update(tabRect(index));
+ }
+}
+
+/*!
+ Returns the icon of the tab at position \a index, or a null icon
+ if \a index is out of range.
+*/
+QIcon QTabBar::tabIcon(int index) const
+{
+ Q_D(const QTabBar);
+ if (const QTabBarPrivate::Tab *tab = d->at(index))
+ return tab->icon;
+ return QIcon();
+}
+
+/*!
+ Sets the icon of the tab at position \a index to \a icon.
+*/
+void QTabBar::setTabIcon(int index, const QIcon & icon)
+{
+ Q_D(QTabBar);
+ if (QTabBarPrivate::Tab *tab = d->at(index)) {
+ bool simpleIconChange = (!icon.isNull() && !tab->icon.isNull());
+ tab->icon = icon;
+ if (simpleIconChange)
+ update(tabRect(index));
+ else
+ d->refresh();
+ }
+}
+
+#ifndef QT_NO_TOOLTIP
+/*!
+ Sets the tool tip of the tab at position \a index to \a tip.
+*/
+void QTabBar::setTabToolTip(int index, const QString & tip)
+{
+ Q_D(QTabBar);
+ if (QTabBarPrivate::Tab *tab = d->at(index))
+ tab->toolTip = tip;
+}
+
+/*!
+ Returns the tool tip of the tab at position \a index, or an empty
+ string if \a index is out of range.
+*/
+QString QTabBar::tabToolTip(int index) const
+{
+ Q_D(const QTabBar);
+ if (const QTabBarPrivate::Tab *tab = d->at(index))
+ return tab->toolTip;
+ return QString();
+}
+#endif // QT_NO_TOOLTIP
+
+#ifndef QT_NO_WHATSTHIS
+/*!
+ \since 4.1
+
+ Sets the What's This help text of the tab at position \a index
+ to \a text.
+*/
+void QTabBar::setTabWhatsThis(int index, const QString &text)
+{
+ Q_D(QTabBar);
+ if (QTabBarPrivate::Tab *tab = d->at(index))
+ tab->whatsThis = text;
+}
+
+/*!
+ \since 4.1
+
+ Returns the What's This help text of the tab at position \a index,
+ or an empty string if \a index is out of range.
+*/
+QString QTabBar::tabWhatsThis(int index) const
+{
+ Q_D(const QTabBar);
+ if (const QTabBarPrivate::Tab *tab = d->at(index))
+ return tab->whatsThis;
+ return QString();
+}
+
+#endif // QT_NO_WHATSTHIS
+
+/*!
+ Sets the data of the tab at position \a index to \a data.
+*/
+void QTabBar::setTabData(int index, const QVariant & data)
+{
+ Q_D(QTabBar);
+ if (QTabBarPrivate::Tab *tab = d->at(index))
+ tab->data = data;
+}
+
+/*!
+ Returns the datad of the tab at position \a index, or a null
+ variant if \a index is out of range.
+*/
+QVariant QTabBar::tabData(int index) const
+{
+ Q_D(const QTabBar);
+ if (const QTabBarPrivate::Tab *tab = d->at(index))
+ return tab->data;
+ return QVariant();
+}
+
+/*!
+ Returns the visual rectangle of the of the tab at position \a
+ index, or a null rectangle if \a index is out of range.
+*/
+QRect QTabBar::tabRect(int index) const
+{
+ Q_D(const QTabBar);
+ if (const QTabBarPrivate::Tab *tab = d->at(index)) {
+ if (d->layoutDirty)
+ const_cast<QTabBarPrivate*>(d)->layoutTabs();
+ QRect r = tab->rect;
+ if (verticalTabs(d->shape))
+ r.translate(0, -d->scrollOffset);
+ else
+ r.translate(-d->scrollOffset, 0);
+ if (!verticalTabs(d->shape))
+ r = QStyle::visualRect(layoutDirection(), rect(), r);
+ return r;
+ }
+ return QRect();
+}
+
+/*!
+ \since 4.3
+ Returns the index of the tab that covers \a position or -1 if no
+ tab covers \a position;
+*/
+
+int QTabBar::tabAt(const QPoint &position) const
+{
+ Q_D(const QTabBar);
+ if (d->validIndex(d->currentIndex)
+ && tabRect(d->currentIndex).contains(position)) {
+ return d->currentIndex;
+ }
+ const int max = d->tabList.size();
+ for (int i = 0; i < max; ++i) {
+ if (tabRect(i).contains(position)) {
+ return i;
+ }
+ }
+ return -1;
+}
+
+/*!
+ \property QTabBar::currentIndex
+ \brief the index of the tab bar's visible tab
+
+ The current index is -1 if there is no current tab.
+*/
+
+int QTabBar::currentIndex() const
+{
+ Q_D(const QTabBar);
+ if (d->validIndex(d->currentIndex))
+ return d->currentIndex;
+ return -1;
+}
+
+
+void QTabBar::setCurrentIndex(int index)
+{
+ Q_D(QTabBar);
+ if (d->dragInProgress && d->pressedIndex != -1)
+ return;
+
+ int oldIndex = d->currentIndex;
+ if (d->validIndex(index) && d->currentIndex != index) {
+ d->currentIndex = index;
+ update();
+ d->makeVisible(index);
+#ifdef QT3_SUPPORT
+ emit selected(index);
+#endif
+ emit currentChanged(index);
+ d->tabList[index].lastTab = oldIndex;
+ d->layoutWidgets(oldIndex);
+ d->layoutWidgets(index);
+ }
+}
+
+/*!
+ \property QTabBar::iconSize
+ \brief The size for icons in the tab bar
+ \since 4.1
+
+ The default value is style-dependent. \c iconSize is a maximum
+ size; icons that are smaller are not scaled up.
+
+ \sa QTabWidget::iconSize
+*/
+QSize QTabBar::iconSize() const
+{
+ Q_D(const QTabBar);
+ if (d->iconSize.isValid())
+ return d->iconSize;
+ int iconExtent = style()->pixelMetric(QStyle::PM_TabBarIconSize, 0, this);
+ return QSize(iconExtent, iconExtent);
+
+}
+
+void QTabBar::setIconSize(const QSize &size)
+{
+ Q_D(QTabBar);
+ d->iconSize = size;
+ d->layoutDirty = true;
+ update();
+ updateGeometry();
+}
+
+/*!
+ \property QTabBar::count
+ \brief the number of tabs in the tab bar
+*/
+
+int QTabBar::count() const
+{
+ Q_D(const QTabBar);
+ return d->tabList.count();
+}
+
+
+/*!\reimp
+ */
+QSize QTabBar::sizeHint() const
+{
+ Q_D(const QTabBar);
+ if (d->layoutDirty)
+ const_cast<QTabBarPrivate*>(d)->layoutTabs();
+ QRect r;
+ for (int i = 0; i < d->tabList.count(); ++i)
+ r = r.united(d->tabList.at(i).maxRect);
+ QSize sz = QApplication::globalStrut();
+ return r.size().expandedTo(sz);
+}
+
+/*!\reimp
+ */
+QSize QTabBar::minimumSizeHint() const
+{
+ Q_D(const QTabBar);
+ if (!d->useScrollButtons) {
+ QRect r;
+ for (int i = 0; i < d->tabList.count(); ++i)
+ r = r.united(d->tabList.at(i).minRect);
+ return r.size().expandedTo(QApplication::globalStrut());
+ }
+ if (verticalTabs(d->shape))
+ return QSize(sizeHint().width(), d->rightB->sizeHint().height() * 2 + 75);
+ else
+ return QSize(d->rightB->sizeHint().width() * 2 + 75, sizeHint().height());
+}
+
+static QString computeElidedText(Qt::TextElideMode mode, const QString &text)
+{
+ if (text.length() <= 7)
+ return text;
+
+ static const QLatin1String Ellipses("...");
+ QString ret;
+ switch (mode) {
+ case Qt::ElideRight:
+ ret = text.left(4) + Ellipses;
+ break;
+ case Qt::ElideMiddle:
+ ret = text.left(2) + Ellipses + text.right(2);
+ break;
+ case Qt::ElideLeft:
+ ret = Ellipses + text.right(4);
+ break;
+ case Qt::ElideNone:
+ ret = text;
+ break;
+ }
+ return ret;
+}
+
+QSize QTabBarPrivate::minimumTabSizeHint(int index)
+{
+ Q_Q(QTabBar);
+ // ### Qt 5: make this a protected virtual function in QTabBar
+ Tab &tab = tabList[index];
+ QString oldText = tab.text;
+ tab.text = computeElidedText(elideMode, oldText);
+ QSize size = q->tabSizeHint(index);
+ tab.text = oldText;
+ return size;
+}
+
+/*!
+ Returns the size hint for the tab at position \a index.
+*/
+QSize QTabBar::tabSizeHint(int index) const
+{
+ Q_D(const QTabBar);
+ if (const QTabBarPrivate::Tab *tab = d->at(index)) {
+ QStyleOptionTabV3 opt;
+ initStyleOption(&opt, index);
+ opt.text = d->tabList.at(index).text;
+ QSize iconSize = tab->icon.isNull() ? QSize(0, 0) : opt.iconSize;
+ int hframe = style()->pixelMetric(QStyle::PM_TabBarTabHSpace, &opt, this);
+ int vframe = style()->pixelMetric(QStyle::PM_TabBarTabVSpace, &opt, this);
+ const QFontMetrics fm = fontMetrics();
+
+ int maxWidgetHeight = qMax(opt.leftButtonSize.height(), opt.rightButtonSize.height());
+ int maxWidgetWidth = qMax(opt.leftButtonSize.width(), opt.rightButtonSize.width());
+
+ int widgetWidth = 0;
+ int widgetHeight = 0;
+ int padding = 0;
+ if (opt.leftButtonSize.isValid()) {
+ padding += 6 + 2;
+ widgetWidth += opt.leftButtonSize.width();
+ widgetHeight += opt.leftButtonSize.height();
+ }
+ if (opt.rightButtonSize.isValid()) {
+ padding += 6 + 2;
+ widgetWidth += opt.rightButtonSize.width();
+ widgetHeight += opt.rightButtonSize.height();
+ }
+ if (opt.iconSize.isValid())
+ padding += 2;
+
+ QSize csz;
+ if (verticalTabs(d->shape)) {
+ csz = QSize( qMax(maxWidgetWidth, qMax(fm.height(), iconSize.height())) + vframe,
+ fm.size(Qt::TextShowMnemonic, tab->text).width() + iconSize.width() + hframe + widgetHeight + padding);
+ } else {
+ csz = QSize(fm.size(Qt::TextShowMnemonic, tab->text).width() + iconSize.width() + hframe
+ + widgetWidth + padding,
+ qMax(maxWidgetHeight, qMax(fm.height(), iconSize.height())) + vframe);
+ }
+
+ QSize retSize = style()->sizeFromContents(QStyle::CT_TabBarTab, &opt, csz, this);
+ return retSize;
+ }
+ return QSize();
+}
+
+/*!
+ This virtual handler is called after a new tab was added or
+ inserted at position \a index.
+
+ \sa tabRemoved()
+ */
+void QTabBar::tabInserted(int index)
+{
+ Q_UNUSED(index)
+}
+
+/*!
+ This virtual handler is called after a tab was removed from
+ position \a index.
+
+ \sa tabInserted()
+ */
+void QTabBar::tabRemoved(int index)
+{
+ Q_UNUSED(index)
+}
+
+/*!
+ This virtual handler is called whenever the tab layout changes.
+
+ \sa tabRect()
+ */
+void QTabBar::tabLayoutChange()
+{
+}
+
+
+/*!\reimp
+ */
+void QTabBar::showEvent(QShowEvent *)
+{
+ Q_D(QTabBar);
+ if (d->layoutDirty)
+ d->refresh();
+ if (!d->validIndex(d->currentIndex))
+ setCurrentIndex(0);
+ d->updateMacBorderMetrics();
+}
+
+/*!\reimp
+ */
+void QTabBar::hideEvent(QHideEvent *)
+{
+ Q_D(QTabBar);
+ d->updateMacBorderMetrics();
+}
+
+/*!\reimp
+ */
+bool QTabBar::event(QEvent *event)
+{
+ Q_D(QTabBar);
+ if (event->type() == QEvent::HoverMove
+ || event->type() == QEvent::HoverEnter) {
+ QHoverEvent *he = static_cast<QHoverEvent *>(event);
+ if (!d->hoverRect.contains(he->pos())) {
+ QRect oldHoverRect = d->hoverRect;
+ for (int i = 0; i < d->tabList.count(); ++i) {
+ QRect area = tabRect(i);
+ if (area.contains(he->pos())) {
+ d->hoverRect = area;
+ break;
+ }
+ }
+ if (he->oldPos() != QPoint(-1, -1))
+ update(oldHoverRect);
+ update(d->hoverRect);
+ }
+ return true;
+ } else if (event->type() == QEvent::HoverLeave ) {
+ QRect oldHoverRect = d->hoverRect;
+ d->hoverRect = QRect();
+ update(oldHoverRect);
+ return true;
+#ifndef QT_NO_TOOLTIP
+ } else if (event->type() == QEvent::ToolTip) {
+ if (const QTabBarPrivate::Tab *tab = d->at(tabAt(static_cast<QHelpEvent*>(event)->pos()))) {
+ if (!tab->toolTip.isEmpty()) {
+ QToolTip::showText(static_cast<QHelpEvent*>(event)->globalPos(), tab->toolTip, this);
+ return true;
+ }
+ }
+#endif // QT_NO_TOOLTIP
+#ifndef QT_NO_WHATSTHIS
+ } else if (event->type() == QEvent::QueryWhatsThis) {
+ const QTabBarPrivate::Tab *tab = d->at(d->indexAtPos(static_cast<QHelpEvent*>(event)->pos()));
+ if (!tab || tab->whatsThis.isEmpty())
+ event->ignore();
+ return true;
+ } else if (event->type() == QEvent::WhatsThis) {
+ if (const QTabBarPrivate::Tab *tab = d->at(d->indexAtPos(static_cast<QHelpEvent*>(event)->pos()))) {
+ if (!tab->whatsThis.isEmpty()) {
+ QWhatsThis::showText(static_cast<QHelpEvent*>(event)->globalPos(),
+ tab->whatsThis, this);
+ return true;
+ }
+ }
+#endif // QT_NO_WHATSTHIS
+#ifndef QT_NO_SHORTCUT
+ } else if (event->type() == QEvent::Shortcut) {
+ QShortcutEvent *se = static_cast<QShortcutEvent *>(event);
+ for (int i = 0; i < d->tabList.count(); ++i) {
+ const QTabBarPrivate::Tab *tab = &d->tabList.at(i);
+ if (tab->shortcutId == se->shortcutId()) {
+ setCurrentIndex(i);
+ return true;
+ }
+ }
+#endif
+ }
+ return QWidget::event(event);
+}
+
+/*!\reimp
+ */
+void QTabBar::resizeEvent(QResizeEvent *)
+{
+ Q_D(QTabBar);
+ if (d->layoutDirty)
+ updateGeometry();
+ d->layoutTabs();
+
+ d->makeVisible(d->currentIndex);
+}
+
+/*!\reimp
+ */
+void QTabBar::paintEvent(QPaintEvent *)
+{
+ Q_D(QTabBar);
+
+ QStyleOptionTabBarBaseV2 optTabBase;
+ QTabBarPrivate::initStyleBaseOption(&optTabBase, this, size());
+
+ QStylePainter p(this);
+ int selected = -1;
+ int cut = -1;
+ bool rtl = optTabBase.direction == Qt::RightToLeft;
+ bool vertical = verticalTabs(d->shape);
+ QStyleOptionTab cutTab;
+ selected = d->currentIndex;
+
+ for (int i = 0; i < d->tabList.count(); ++i)
+ optTabBase.tabBarRect |= tabRect(i);
+
+ optTabBase.selectedTabRect = tabRect(selected);
+
+ if (d->drawBase)
+ p.drawPrimitive(QStyle::PE_FrameTabBarBase, optTabBase);
+
+ for (int i = 0; i < d->tabList.count(); ++i) {
+ QStyleOptionTabV3 tab;
+ initStyleOption(&tab, i);
+ if (d->paintWithOffsets && d->tabList[i].dragOffset != 0) {
+ if (vertical) {
+ tab.rect.moveTop(tab.rect.y() + d->tabList[i].dragOffset);
+ } else {
+ tab.rect.moveLeft(tab.rect.x() + d->tabList[i].dragOffset);
+ }
+ }
+ if (!(tab.state & QStyle::State_Enabled)) {
+ tab.palette.setCurrentColorGroup(QPalette::Disabled);
+ }
+ // If this tab is partially obscured, make a note of it so that we can pass the information
+ // along when we draw the tear.
+ if (((!vertical && (!rtl && tab.rect.left() < 0)) || (rtl && tab.rect.right() > width()))
+ || (vertical && tab.rect.top() < 0)) {
+ cut = i;
+ cutTab = tab;
+ }
+ // Don't bother drawing a tab if the entire tab is outside of the visible tab bar.
+ if ((!vertical && (tab.rect.right() < 0 || tab.rect.left() > width()))
+ || (vertical && (tab.rect.bottom() < 0 || tab.rect.top() > height())))
+ continue;
+
+ optTabBase.tabBarRect |= tab.rect;
+ if (i == selected)
+ continue;
+
+ if (!d->tabList[i].animatingCache.isNull() && d->paintWithOffsets) {
+ p.drawPixmap(tab.rect, d->tabList[i].animatingCache);
+ } else {
+ p.drawControl(QStyle::CE_TabBarTab, tab);
+ }
+ }
+
+ // Draw the selected tab last to get it "on top"
+ if (selected >= 0) {
+ QStyleOptionTabV3 tab;
+ initStyleOption(&tab, selected);
+ if (d->paintWithOffsets && d->tabList[selected].dragOffset != 0) {
+ if (vertical)
+ tab.rect.moveTop(tab.rect.y() + d->tabList[selected].dragOffset);
+ else
+ tab.rect.moveLeft(tab.rect.x() + d->tabList[selected].dragOffset);
+ }
+ p.drawControl(QStyle::CE_TabBarTab, tab);
+ }
+
+ // Only draw the tear indicator if necessary. Most of the time we don't need too.
+ if (d->leftB->isVisible() && cut >= 0) {
+ cutTab.rect = rect();
+ cutTab.rect = style()->subElementRect(QStyle::SE_TabBarTearIndicator, &cutTab, this);
+ p.drawPrimitive(QStyle::PE_IndicatorTabTear, cutTab);
+ }
+}
+
+/*
+ Given that index at position from moved to position to where return where index goes.
+ */
+int QTabBarPrivate::calculateNewPosition(int from, int to, int index) const
+{
+ if (index == from)
+ return to;
+
+ int start = qMin(from, to);
+ int end = qMax(from, to);
+ if (index >= start && index <= end)
+ index += (from < to) ? -1 : 1;
+ return index;
+}
+
+/*!
+ Moves the item at index position \a from to index position \a to.
+ \since 4.5
+
+ \sa tabMoved(), tabLayoutChange()
+ */
+void QTabBar::moveTab(int from, int to)
+{
+ Q_D(QTabBar);
+ if (from == to
+ || !d->validIndex(from)
+ || !d->validIndex(to))
+ return;
+
+ bool vertical = verticalTabs(d->shape);
+ int oldPressedPosition = 0;
+ if (d->pressedIndex != -1) {
+ // Record the position of the pressed tab before reordering the tabs.
+ oldPressedPosition = vertical ? d->tabList[d->pressedIndex].rect.y()
+ : d->tabList[d->pressedIndex].rect.x();
+ }
+
+ // Update the locations of the tabs first
+ int start = qMin(from, to);
+ int end = qMax(from, to);
+ int width = vertical ? d->tabList[from].rect.height() : d->tabList[from].rect.width();
+ if (from < to)
+ width *= -1;
+ bool rtl = isRightToLeft();
+ for (int i = start; i <= end; ++i) {
+ if (i == from)
+ continue;
+ if (vertical)
+ d->tabList[i].rect.moveTop(d->tabList[i].rect.y() + width);
+ else
+ d->tabList[i].rect.moveLeft(d->tabList[i].rect.x() + width);
+ int direction = -1;
+ if (rtl && !vertical)
+ direction *= -1;
+ if (d->tabList[i].dragOffset != 0)
+ d->tabList[i].dragOffset += (direction * width);
+ }
+
+ if (vertical) {
+ if (from < to)
+ d->tabList[from].rect.moveTop(d->tabList[to].rect.bottom() + 1);
+ else
+ d->tabList[from].rect.moveTop(d->tabList[to].rect.top() - width);
+ } else {
+ if (from < to)
+ d->tabList[from].rect.moveLeft(d->tabList[to].rect.right() + 1);
+ else
+ d->tabList[from].rect.moveLeft(d->tabList[to].rect.left() - width);
+ }
+
+ // Move the actual data structures
+ d->tabList.move(from, to);
+
+ // update lastTab locations
+ for (int i = 0; i < d->tabList.count(); ++i)
+ d->tabList[i].lastTab = d->calculateNewPosition(from, to, d->tabList[i].lastTab);
+
+ // update external variables
+ d->currentIndex = d->calculateNewPosition(from, to, d->currentIndex);
+
+ // If we are in the middle of a drag update the dragStartPosition
+ if (d->pressedIndex != -1) {
+ d->pressedIndex = d->calculateNewPosition(from, to, d->pressedIndex);
+ int newPressedPosition = vertical ? d->tabList[d->pressedIndex].rect.top() : d->tabList[d->pressedIndex].rect.left();
+ int diff = oldPressedPosition - newPressedPosition;
+ if (isRightToLeft() && !vertical)
+ diff *= -1;
+ if (vertical)
+ d->dragStartPosition.setY(d->dragStartPosition.y() - diff);
+ else
+ d->dragStartPosition.setX(d->dragStartPosition.x() - diff);
+ }
+
+ d->layoutWidgets(start);
+ update();
+ emit tabMoved(from, to);
+ emit tabLayoutChange();
+}
+
+void QTabBarPrivate::slide(int from, int to)
+{
+ Q_Q(QTabBar);
+ if (from == to
+ || !validIndex(from)
+ || !validIndex(to))
+ return;
+ bool vertical = verticalTabs(shape);
+ int preLocation = vertical ? q->tabRect(from).y() : q->tabRect(from).x();
+ q->setUpdatesEnabled(false);
+ q->moveTab(from, to);
+ q->setUpdatesEnabled(true);
+ int postLocation = vertical ? q->tabRect(to).y() : q->tabRect(to).x();
+ int length = postLocation - preLocation;
+ tabList[to].makeTimeLine(q);
+ tabList[to].dragOffset += -1 * length;
+ tabList[to].timeLine->setFrameRange(tabList[to].dragOffset, 0);
+ animations[tabList[to].timeLine] = to;
+ tabList[to].timeLine->setDuration(ANIMATION_DURATION);
+ if (tabList[to].timeLine->state() != QTimeLine::Running)
+ tabList[to].timeLine->start();
+}
+
+void QTabBarPrivate::_q_moveTab(int offset)
+{
+ Q_Q(QTabBar);
+ if (QTimeLine *timeLine = qobject_cast<QTimeLine *>(q->sender())) {
+ int index = animations[timeLine];
+ if (!validIndex(index))
+ return;
+ tabList[index].dragOffset = offset;
+ q->update();
+ }
+}
+
+/*!\reimp
+*/
+void QTabBar::mousePressEvent(QMouseEvent *event)
+{
+ Q_D(QTabBar);
+ if (event->button() != Qt::LeftButton) {
+ event->ignore();
+ return;
+ }
+ // Be safe!
+ if (d->pressedIndex != -1 && d->movable)
+ d->_q_moveTabFinished(d->pressedIndex);
+
+ d->pressedIndex = d->indexAtPos(event->pos());
+ if (d->validIndex(d->pressedIndex)) {
+ QStyleOptionTabBarBaseV2 optTabBase;
+ optTabBase.init(this);
+ optTabBase.documentMode = d->documentMode;
+ if (event->type() == style()->styleHint(QStyle::SH_TabBar_SelectMouseType, &optTabBase, this))
+ setCurrentIndex(d->pressedIndex);
+ else
+ repaint(tabRect(d->pressedIndex));
+ if (d->movable) {
+ d->dragStartPosition = event->pos();
+ }
+ }
+}
+
+/*!\reimp
+ */
+void QTabBar::mouseMoveEvent(QMouseEvent *event)
+{
+ Q_D(QTabBar);
+ if (d->movable) {
+ // Be safe!
+ if (d->pressedIndex != -1
+ && event->buttons() == Qt::NoButton)
+ d->_q_moveTabFinished(d->pressedIndex);
+
+ // Start drag
+ if (!d->dragInProgress && d->pressedIndex != -1) {
+ if ((event->pos() - d->dragStartPosition).manhattanLength() > QApplication::startDragDistance()) {
+ d->dragInProgress = true;
+ if (d->animations.isEmpty())
+ d->grabCache(0, d->tabList.count(), false);
+ }
+ }
+
+ int offset = (event->pos() - d->dragStartPosition).manhattanLength();
+ if (event->buttons() == Qt::LeftButton
+ && offset > QApplication::startDragDistance()
+ && d->validIndex(d->pressedIndex)) {
+ bool vertical = verticalTabs(d->shape);
+ int dragDistance;
+ if (vertical) {
+ dragDistance = (event->pos().y() - d->dragStartPosition.y());
+ } else {
+ dragDistance = (event->pos().x() - d->dragStartPosition.x());
+ }
+ d->tabList[d->pressedIndex].dragOffset = dragDistance;
+
+ QRect startingRect = tabRect(d->pressedIndex);
+ if (vertical)
+ startingRect.moveTop(startingRect.y() + dragDistance);
+ else
+ startingRect.moveLeft(startingRect.x() + dragDistance);
+
+ int overIndex;
+ if (dragDistance < 0)
+ overIndex = tabAt(startingRect.topLeft());
+ else
+ overIndex = tabAt(startingRect.topRight());
+
+ if (overIndex != d->pressedIndex && overIndex != -1) {
+ int offset = 1;
+ if (isRightToLeft() && !vertical)
+ offset *= -1;
+ if (dragDistance < 0) {
+ dragDistance *= -1;
+ offset *= -1;
+ }
+ for (int i = d->pressedIndex;
+ offset > 0 ? i < overIndex : i > overIndex;
+ i += offset) {
+ QRect overIndexRect = tabRect(overIndex);
+ int needsToBeOver = (vertical ? overIndexRect.height() : overIndexRect.width()) / 2;
+ if (dragDistance > needsToBeOver)
+ d->slide(i + offset, d->pressedIndex);
+ }
+
+ }
+ // Buttons needs to follow the dragged tab
+ d->layoutTab(d->pressedIndex);
+
+ update();
+ }
+ }
+
+ if (event->buttons() != Qt::LeftButton) {
+ event->ignore();
+ return;
+ }
+ QStyleOptionTabBarBaseV2 optTabBase;
+ optTabBase.init(this);
+ optTabBase.documentMode = d->documentMode;
+}
+
+void QTabBarPrivate::_q_moveTabFinished()
+{
+ Q_Q(QTabBar);
+ if (QTimeLine *timeLine = qobject_cast<QTimeLine *>(q->sender())) {
+ int index = animations[timeLine];
+ animations.remove(timeLine);
+ _q_moveTabFinished(index);
+ }
+}
+
+void QTabBarPrivate::grabCache(int start, int end, bool unhide)
+{
+ Q_Q(QTabBar);
+ paintWithOffsets = false;
+ bool showButtonsAgain = rightB->isVisible();
+ rightB->hide();
+ leftB->hide();
+
+ QWidget *topLevel = q->window();
+ QPoint topLevelOffset(q->mapTo(topLevel, QPoint()));
+ for (int i = start; i < end; ++i) {
+ QRect tabRect = q->tabRect(i);
+ tabRect.translate(topLevelOffset);
+ if (unhide) {
+ tabList[i].unHideWidgets();
+ layoutWidgets(i);
+ }
+ tabList[i].animatingCache = QPixmap::grabWidget(topLevel, tabRect);
+ if (i != pressedIndex)
+ tabList[i].hideWidgets();
+ }
+ if (showButtonsAgain) {
+ rightB->show();
+ leftB->show();
+ }
+ paintWithOffsets = true;
+}
+
+void QTabBarPrivate::_q_moveTabFinished(int index)
+{
+ Q_Q(QTabBar);
+ bool cleanup = (pressedIndex == index) || (pressedIndex == -1) || !validIndex(index);
+ if (animations.isEmpty() && cleanup) {
+ for (int i = 0; i < tabList.count(); ++i) {
+ tabList[i].dragOffset = 0;
+ tabList[i].unHideWidgets();
+ tabList[i].animatingCache = QPixmap();
+ }
+ if (pressedIndex != -1 && movable) {
+ pressedIndex = -1;
+ dragInProgress = false;
+ dragStartPosition = QPoint();
+ }
+ layoutWidgets();
+ } else {
+ if (!validIndex(index))
+ return;
+ tabList[index].dragOffset = 0;
+ }
+ q->update();
+}
+
+/*!\reimp
+*/
+void QTabBar::mouseReleaseEvent(QMouseEvent *event)
+{
+ Q_D(QTabBar);
+ if (event->button() != Qt::LeftButton) {
+ event->ignore();
+ return;
+ }
+
+ if (d->movable && d->dragInProgress && d->validIndex(d->pressedIndex)) {
+ int length = d->tabList[d->pressedIndex].dragOffset;
+ int width = verticalTabs(d->shape)
+ ? tabRect(d->pressedIndex).height()
+ : tabRect(d->pressedIndex).width();
+ int duration = qMin(ANIMATION_DURATION,
+ ((length < 0 ? (-1 * length) : length) * ANIMATION_DURATION) / width);
+ if (duration > 0) {
+ d->tabList[d->pressedIndex].makeTimeLine(this);
+ d->tabList[d->pressedIndex].timeLine->setFrameRange(length, 0);
+ d->animations[d->tabList[d->pressedIndex].timeLine] = d->pressedIndex;
+ d->tabList[d->pressedIndex].timeLine->setDuration(duration);
+ if (d->tabList[d->pressedIndex].timeLine->state() != QTimeLine::Running)
+ d->tabList[d->pressedIndex].timeLine->start();
+ } else {
+ d->_q_moveTabFinished(d->pressedIndex);
+ }
+ d->dragInProgress = false;
+ d->dragStartPosition = QPoint();
+ }
+
+ int i = d->indexAtPos(event->pos()) == d->pressedIndex ? d->pressedIndex : -1;
+ d->pressedIndex = -1;
+ QStyleOptionTabBarBaseV2 optTabBase;
+ optTabBase.initFrom(this);
+ optTabBase.documentMode = d->documentMode;
+ if (style()->styleHint(QStyle::SH_TabBar_SelectMouseType, &optTabBase, this) == QEvent::MouseButtonRelease)
+ setCurrentIndex(i);
+}
+
+/*!\reimp
+ */
+void QTabBar::keyPressEvent(QKeyEvent *event)
+{
+ Q_D(QTabBar);
+ if (event->key() != Qt::Key_Left && event->key() != Qt::Key_Right) {
+ event->ignore();
+ return;
+ }
+ int dx = event->key() == (isRightToLeft() ? Qt::Key_Right : Qt::Key_Left) ? -1 : 1;
+ for (int index = d->currentIndex + dx; d->validIndex(index); index += dx) {
+ if (d->tabList.at(index).enabled) {
+ setCurrentIndex(index);
+ break;
+ }
+ }
+}
+
+/*!\reimp
+ */
+#ifndef QT_NO_WHEELEVENT
+void QTabBar::wheelEvent(QWheelEvent *event)
+{
+ Q_D(QTabBar);
+ int overIndex = d->indexAtPos(event->pos());
+ if (overIndex != -1) {
+ int offset = event->delta() > 0 ? -1 : 1;
+ setCurrentIndex(currentIndex() + offset);
+ }
+ QWidget::wheelEvent(event);
+}
+#endif //QT_NO_WHEELEVENT
+
+/*!\reimp
+ */
+void QTabBar::changeEvent(QEvent *event)
+{
+ Q_D(QTabBar);
+ if (event->type() == QEvent::StyleChange) {
+ d->elideMode = Qt::TextElideMode(style()->styleHint(QStyle::SH_TabBar_ElideMode, 0, this));
+ d->useScrollButtons = !style()->styleHint(QStyle::SH_TabBar_PreferNoArrows, 0, this);
+ }
+ d->refresh();
+ QWidget::changeEvent(event);
+}
+
+/*!
+ \property QTabBar::elideMode
+ \brief how to elide text in the tab bar
+ \since 4.2
+
+ This property controls how items are elided when there is not
+ enough space to show them for a given tab bar size.
+
+ By default the value is style dependent.
+
+ \sa QTabWidget::elideMode usesScrollButtons QStyle::SH_TabBar_ElideMode
+*/
+
+Qt::TextElideMode QTabBar::elideMode() const
+{
+ Q_D(const QTabBar);
+ return d->elideMode;
+}
+
+void QTabBar::setElideMode(Qt::TextElideMode mode)
+{
+ Q_D(QTabBar);
+ d->elideMode = mode;
+ d->refresh();
+}
+
+/*!
+ \property QTabBar::usesScrollButtons
+ \brief Whether or not a tab bar should use buttons to scroll tabs when it
+ has many tabs.
+ \since 4.2
+
+ When there are too many tabs in a tab bar for its size, the tab bar can either choose
+ to expand its size or to add buttons that allow you to scroll through the tabs.
+
+ By default the value is style dependant.
+
+ \sa elideMode QTabWidget::usesScrollButtons QStyle::SH_TabBar_PreferNoArrows
+*/
+bool QTabBar::usesScrollButtons() const
+{
+ return d_func()->useScrollButtons;
+}
+
+void QTabBar::setUsesScrollButtons(bool useButtons)
+{
+ Q_D(QTabBar);
+ if (d->useScrollButtons == useButtons)
+ return;
+ d->useScrollButtons = useButtons;
+ d->refresh();
+}
+
+/*!
+ \fn void QTabBar::setCurrentTab(int index)
+
+ Use setCurrentIndex() instead.
+*/
+
+/*!
+ \fn void QTabBar::selected(int index);
+
+ Use currentChanged() instead.
+*/
+
+
+/*!
+ \property QTabBar::tabsClosable
+ \brief Whether or not a tab bar should place close buttons on each tab
+ \since 4.5
+
+ When tabsClosable is set to true a close button will appear on the tab on
+ either the left or right hand side depending upon the style. When the button
+ is clicked the tab the signal tabCloseRequested will be emitted.
+
+ By default the value is false.
+
+ \sa setTabButton(), tabRemoved()
+*/
+
+bool QTabBar::tabsClosable() const
+{
+ Q_D(const QTabBar);
+ return d->closeButtonOnTabs;
+}
+
+void QTabBar::setTabsClosable(bool closable)
+{
+ Q_D(QTabBar);
+ if (d->closeButtonOnTabs == closable)
+ return;
+ d->closeButtonOnTabs = closable;
+ ButtonPosition closeSide = (ButtonPosition)style()->styleHint(QStyle::SH_TabBar_CloseButtonPosition, 0, this);
+ if (!closable) {
+ for (int i = 0; i < d->tabList.count(); ++i) {
+ if (closeSide == LeftSide && d->tabList[i].leftWidget) {
+ d->tabList[i].leftWidget->deleteLater();
+ d->tabList[i].leftWidget = 0;
+ }
+ if (closeSide == RightSide && d->tabList[i].rightWidget) {
+ d->tabList[i].rightWidget->deleteLater();
+ d->tabList[i].rightWidget = 0;
+ }
+ }
+ } else {
+ bool newButtons = false;
+ for (int i = 0; i < d->tabList.count(); ++i) {
+ if (tabButton(i, closeSide))
+ continue;
+ newButtons = true;
+ QAbstractButton *closeButton = new CloseButton(this);
+ connect(closeButton, SIGNAL(clicked()), this, SLOT(_q_closeTab()));
+ setTabButton(i, closeSide, closeButton);
+ }
+ if (newButtons)
+ d->layoutTabs();
+ }
+ update();
+}
+
+/*!
+ \enum QTabBar::ButtonPosition
+ \since 4.5
+
+ This enum type lists the location of the widget on a tab.
+
+ \value LeftSide Left side of the tab.
+
+ \value RightSide Right side of the tab.
+
+*/
+
+/*!
+ \enum QTabBar::SelectionBehavior
+ \since 4.5
+
+ This enum type lists the behavior of QTabBar when a tab is removed
+ and the tab being removed is also the current tab.
+
+ \value SelectLeftTab Select the tab to the left of the one being removed.
+
+ \value SelectRightTab Select the tab to the right of the one being removed.
+
+ \value SelectPreviousTab Select the previously selected tab.
+
+*/
+
+/*!
+ \property QTabBar::selectionBehaviorOnRemove
+ \brief What tab should be set as current when removeTab is called if
+ the removed tab is also the current tab.
+ \since 4.5
+
+ By default the value is SelectRightTab.
+
+ \sa removeTab()
+*/
+
+
+QTabBar::SelectionBehavior QTabBar::selectionBehaviorOnRemove() const
+{
+ Q_D(const QTabBar);
+ return d->selectionBehaviorOnRemove;
+}
+
+void QTabBar::setSelectionBehaviorOnRemove(QTabBar::SelectionBehavior behavior)
+{
+ Q_D(QTabBar);
+ d->selectionBehaviorOnRemove = behavior;
+}
+
+/*!
+ \property QTabBar::expanding
+ \brief When expanding is true QTabBar will expand the tabs to use the empty space.
+ \since 4.5
+
+ By default the value is true.
+
+ \sa QTabWidget::documentMode
+*/
+
+bool QTabBar::expanding() const
+{
+ Q_D(const QTabBar);
+ return d->expanding;
+}
+
+void QTabBar::setExpanding(bool enabled)
+{
+ Q_D(QTabBar);
+ if (d->expanding == enabled)
+ return;
+ d->expanding = enabled;
+ d->layoutTabs();
+}
+
+/*!
+ \property QTabBar::movable
+ \brief This property holds whether the user can move the tabs
+ within the tabbar area.
+
+ \since 4.5
+
+ By default, this property is false;
+*/
+
+bool QTabBar::isMovable() const
+{
+ Q_D(const QTabBar);
+ return d->movable;
+}
+
+void QTabBar::setMovable(bool movable)
+{
+ Q_D(QTabBar);
+ d->movable = movable;
+}
+
+
+/*!
+ \property QTabBar::documentMode
+ \brief Whether or not the tab bar is rendered in a mode suitable for the main window.
+ \since 4.5
+
+ This property is used as a hint for styles to draw the tabs in a different
+ way then they would normally look in a tab widget. On Mac OS X this will
+ look similar to the tabs in Safari or Leopard's Terminal.app.
+
+ \sa QTabWidget::documentMode
+*/
+bool QTabBar::documentMode() const
+{
+ return d_func()->documentMode;
+}
+
+void QTabBar::setDocumentMode(bool enabled)
+{
+ Q_D(QTabBar);
+ d->documentMode = enabled;
+ d->updateMacBorderMetrics();
+}
+
+/*!
+ Sets \a widget on the tab \a index. The widget is placed
+ on the left or right hand side depending upon the \a position.
+ \since 4.5
+
+ Any previously set widget in \a position is hidden.
+
+ The tab bar will take ownership of the widget and so all widgets set here
+ will be deleted by the tab bar when it is destroyed unless you separately
+ reparent the widget after setting some other widget (or 0).
+
+ \sa tabsClosable()
+ */
+void QTabBar::setTabButton(int index, ButtonPosition position, QWidget *widget)
+{
+ Q_D(QTabBar);
+ if (index < 0 || index >= d->tabList.count())
+ return;
+ if (widget) {
+ widget->setParent(this);
+ // make sure our left and right widgets stay on top
+ widget->lower();
+ }
+ if (position == LeftSide) {
+ if (d->tabList[index].leftWidget)
+ d->tabList[index].leftWidget->hide();
+ d->tabList[index].leftWidget = widget;
+ if(!d->tabList[index].hidLeft && widget)
+ widget->show();
+ } else {
+ if (d->tabList[index].rightWidget)
+ d->tabList[index].rightWidget->hide();
+ d->tabList[index].rightWidget = widget;
+ if(!d->tabList[index].hidRight && widget)
+ widget->show();
+ }
+ d->layoutTabs();
+ update();
+}
+
+/*!
+ Returns the widget set a tab \a index and \a position or 0 if
+ one is not set.
+ */
+QWidget *QTabBar::tabButton(int index, ButtonPosition position) const
+{
+ Q_D(const QTabBar);
+ if (index < 0 || index >= d->tabList.count())
+ return 0;
+ if (position == LeftSide)
+ return d->tabList.at(index).leftWidget;
+ else
+ return d->tabList.at(index).rightWidget;
+}
+
+CloseButton::CloseButton(QWidget *parent)
+ : QAbstractButton(parent)
+{
+ setFocusPolicy(Qt::NoFocus);
+#ifndef QT_NO_CURSOR
+ setCursor(Qt::ArrowCursor);
+#endif
+#ifndef QT_NO_TOOLTIP
+ setToolTip(tr("Close Tab"));
+#endif
+ resize(sizeHint());
+}
+
+QSize CloseButton::sizeHint() const
+{
+ ensurePolished();
+ int width = style()->pixelMetric(QStyle::PM_TabCloseIndicatorWidth, 0, this);
+ int height = style()->pixelMetric(QStyle::PM_TabCloseIndicatorHeight, 0, this);
+ return QSize(width, height);
+}
+
+void CloseButton::enterEvent(QEvent *event)
+{
+ if (isEnabled())
+ update();
+ QAbstractButton::enterEvent(event);
+}
+
+void CloseButton::leaveEvent(QEvent *event)
+{
+ if (isEnabled())
+ update();
+ QAbstractButton::leaveEvent(event);
+}
+
+void CloseButton::paintEvent(QPaintEvent *)
+{
+ QPainter p(this);
+ QStyleOption opt;
+ opt.init(this);
+ opt.state |= QStyle::State_AutoRaise;
+ if (isEnabled() && underMouse() && !isChecked() && !isDown())
+ opt.state |= QStyle::State_Raised;
+ if (isChecked())
+ opt.state |= QStyle::State_On;
+ if (isDown())
+ opt.state |= QStyle::State_Sunken;
+
+ if (const QTabBar *tb = qobject_cast<const QTabBar *>(parent())) {
+ int index = tb->currentIndex();
+ QTabBar::ButtonPosition position = (QTabBar::ButtonPosition)style()->styleHint(QStyle::SH_TabBar_CloseButtonPosition, 0, tb);
+ if (tb->tabButton(index, position) == this)
+ opt.state |= QStyle::State_Selected;
+ }
+
+ style()->drawPrimitive(QStyle::PE_IndicatorTabClose, &opt, &p, this);
+}
+
+QT_END_NAMESPACE
+
+#include "moc_qtabbar.cpp"
+
+#endif // QT_NO_TABBAR
diff --git a/src/gui/widgets/qtabbar.h b/src/gui/widgets/qtabbar.h
new file mode 100644
index 0000000000..49931adaee
--- /dev/null
+++ b/src/gui/widgets/qtabbar.h
@@ -0,0 +1,228 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QTABBAR_H
+#define QTABBAR_H
+
+#include <QtGui/qwidget.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+#ifndef QT_NO_TABBAR
+
+class QIcon;
+class QTabBarPrivate;
+class QStyleOptionTab;
+
+class Q_GUI_EXPORT QTabBar: public QWidget
+{
+ Q_OBJECT
+
+ Q_ENUMS(Shape)
+ Q_PROPERTY(Shape shape READ shape WRITE setShape)
+ Q_PROPERTY(int currentIndex READ currentIndex WRITE setCurrentIndex NOTIFY currentChanged)
+ Q_PROPERTY(int count READ count)
+ Q_PROPERTY(bool drawBase READ drawBase WRITE setDrawBase)
+ Q_PROPERTY(QSize iconSize READ iconSize WRITE setIconSize)
+ Q_PROPERTY(Qt::TextElideMode elideMode READ elideMode WRITE setElideMode)
+ Q_PROPERTY(bool usesScrollButtons READ usesScrollButtons WRITE setUsesScrollButtons)
+ Q_PROPERTY(bool tabsClosable READ tabsClosable WRITE setTabsClosable)
+ Q_PROPERTY(SelectionBehavior selectionBehaviorOnRemove READ selectionBehaviorOnRemove WRITE setSelectionBehaviorOnRemove)
+ Q_PROPERTY(bool expanding READ expanding WRITE setExpanding)
+ Q_PROPERTY(bool movable READ isMovable WRITE setMovable)
+ Q_PROPERTY(bool documentMode READ documentMode WRITE setDocumentMode)
+
+public:
+ explicit QTabBar(QWidget* parent=0);
+ ~QTabBar();
+
+ enum Shape { RoundedNorth, RoundedSouth, RoundedWest, RoundedEast,
+ TriangularNorth, TriangularSouth, TriangularWest, TriangularEast
+#if defined(QT3_SUPPORT) && !defined(Q_MOC_RUN)
+ , RoundedAbove = RoundedNorth, RoundedBelow = RoundedSouth,
+ TriangularAbove = TriangularNorth, TriangularBelow = TriangularSouth
+#endif
+ };
+
+ enum ButtonPosition {
+ LeftSide,
+ RightSide
+ };
+
+ enum SelectionBehavior {
+ SelectLeftTab,
+ SelectRightTab,
+ SelectPreviousTab
+ };
+
+ Shape shape() const;
+ void setShape(Shape shape);
+
+ int addTab(const QString &text);
+ int addTab(const QIcon &icon, const QString &text);
+
+ int insertTab(int index, const QString &text);
+ int insertTab(int index, const QIcon&icon, const QString &text);
+
+ void removeTab(int index);
+ void moveTab(int from, int to);
+
+ bool isTabEnabled(int index) const;
+ void setTabEnabled(int index, bool);
+
+ QString tabText(int index) const;
+ void setTabText(int index, const QString &text);
+
+ QColor tabTextColor(int index) const;
+ void setTabTextColor(int index, const QColor &color);
+
+ QIcon tabIcon(int index) const;
+ void setTabIcon(int index, const QIcon &icon);
+
+ Qt::TextElideMode elideMode() const;
+ void setElideMode(Qt::TextElideMode);
+
+#ifndef QT_NO_TOOLTIP
+ void setTabToolTip(int index, const QString &tip);
+ QString tabToolTip(int index) const;
+#endif
+
+#ifndef QT_NO_WHATSTHIS
+ void setTabWhatsThis(int index, const QString &text);
+ QString tabWhatsThis(int index) const;
+#endif
+
+ void setTabData(int index, const QVariant &data);
+ QVariant tabData(int index) const;
+
+ QRect tabRect(int index) const;
+ int tabAt(const QPoint &pos) const;
+
+ int currentIndex() const;
+ int count() const;
+
+ QSize sizeHint() const;
+ QSize minimumSizeHint() const;
+
+ void setDrawBase(bool drawTheBase);
+ bool drawBase() const;
+
+ QSize iconSize() const;
+ void setIconSize(const QSize &size);
+
+ bool usesScrollButtons() const;
+ void setUsesScrollButtons(bool useButtons);
+
+ bool tabsClosable() const;
+ void setTabsClosable(bool closable);
+
+ void setTabButton(int index, ButtonPosition position, QWidget *widget);
+ QWidget *tabButton(int index, ButtonPosition position) const;
+
+ SelectionBehavior selectionBehaviorOnRemove() const;
+ void setSelectionBehaviorOnRemove(SelectionBehavior behavior);
+
+ bool expanding() const;
+ void setExpanding(bool enabled);
+
+ bool isMovable() const;
+ void setMovable(bool movable);
+
+ bool documentMode() const;
+ void setDocumentMode(bool set);
+
+public Q_SLOTS:
+ void setCurrentIndex(int index);
+
+Q_SIGNALS:
+ void currentChanged(int index);
+ void tabCloseRequested(int index);
+ void tabMoved(int from, int to);
+
+protected:
+ virtual QSize tabSizeHint(int index) const;
+ virtual void tabInserted(int index);
+ virtual void tabRemoved(int index);
+ virtual void tabLayoutChange();
+
+ bool event(QEvent *);
+ void resizeEvent(QResizeEvent *);
+ void showEvent(QShowEvent *);
+ void hideEvent(QHideEvent *);
+ void paintEvent(QPaintEvent *);
+ void mousePressEvent (QMouseEvent *);
+ void mouseMoveEvent (QMouseEvent *);
+ void mouseReleaseEvent (QMouseEvent *);
+#ifndef QT_NO_WHEELEVENT
+ void wheelEvent(QWheelEvent *event);
+#endif
+ void keyPressEvent(QKeyEvent *);
+ void changeEvent(QEvent *);
+ void initStyleOption(QStyleOptionTab *option, int tabIndex) const;
+
+#ifdef QT3_SUPPORT
+public Q_SLOTS:
+ QT_MOC_COMPAT void setCurrentTab(int index) { setCurrentIndex(index); }
+Q_SIGNALS:
+ QT_MOC_COMPAT void selected(int);
+#endif
+
+ friend class QAccessibleTabBar;
+private:
+ Q_DISABLE_COPY(QTabBar)
+ Q_DECLARE_PRIVATE(QTabBar)
+ Q_PRIVATE_SLOT(d_func(), void _q_scrollTabs())
+ Q_PRIVATE_SLOT(d_func(), void _q_closeTab())
+ Q_PRIVATE_SLOT(d_func(), void _q_moveTab(int))
+ Q_PRIVATE_SLOT(d_func(), void _q_moveTabFinished())
+};
+
+#endif // QT_NO_TABBAR
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QTABBAR_H
diff --git a/src/gui/widgets/qtabbar_p.h b/src/gui/widgets/qtabbar_p.h
new file mode 100644
index 0000000000..a117aa3df3
--- /dev/null
+++ b/src/gui/widgets/qtabbar_p.h
@@ -0,0 +1,265 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QTABBAR_P_H
+#define QTABBAR_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 "qtabbar.h"
+#include "private/qwidget_p.h"
+
+#include <qicon.h>
+#include <qtoolbutton.h>
+#include <qtimeline.h>
+#include <qhash.h>
+#include <qdebug.h>
+
+#ifndef QT_NO_TABBAR
+
+#define ANIMATION_DURATION 250
+
+#include <qstyleoption.h>
+
+QT_BEGIN_NAMESPACE
+
+
+class QTabBarPrivate : public QWidgetPrivate
+{
+ Q_DECLARE_PUBLIC(QTabBar)
+public:
+ QTabBarPrivate()
+ :currentIndex(-1), pressedIndex(-1),
+ shape(QTabBar::RoundedNorth),
+ layoutDirty(false), drawBase(true), scrollOffset(0), expanding(true), closeButtonOnTabs(false), selectionBehaviorOnRemove(QTabBar::SelectRightTab), paintWithOffsets(true), movable(false), dragInProgress(false), documentMode(false) {}
+
+ int currentIndex;
+ int pressedIndex;
+ QTabBar::Shape shape;
+ bool layoutDirty;
+ bool drawBase;
+ int scrollOffset;
+
+ struct Tab {
+ inline Tab(const QIcon &ico, const QString &txt)
+ : enabled(true)
+ , shortcutId(0)
+ , text(txt)
+ , icon(ico)
+ , leftWidget(0)
+ , rightWidget(0)
+ , lastTab(-1)
+ , timeLine(0)
+ , dragOffset(0)
+ , hidLeft(false)
+ , hidRight(false)
+ {}
+ bool enabled;
+ int shortcutId;
+ QString text;
+#ifndef QT_NO_TOOLTIP
+ QString toolTip;
+#endif
+#ifndef QT_NO_WHATSTHIS
+ QString whatsThis;
+#endif
+ QIcon icon;
+ QRect rect;
+ QRect minRect;
+ QRect maxRect;
+
+ QColor textColor;
+ QVariant data;
+ QWidget *leftWidget;
+ QWidget *rightWidget;
+ int lastTab;
+
+ QTimeLine *timeLine;
+ int dragOffset;
+ QPixmap animatingCache;
+ bool hidLeft;
+ bool hidRight;
+
+ void makeTimeLine(QWidget *q) {
+ if (timeLine)
+ return;
+ timeLine = new QTimeLine(ANIMATION_DURATION, q);
+ q->connect(timeLine, SIGNAL(frameChanged(int)), q, SLOT(_q_moveTab(int)));
+ q->connect(timeLine, SIGNAL(finished()), q, SLOT(_q_moveTabFinished()));
+ }
+
+ void hideWidgets() {
+ if (!hidRight && rightWidget) {
+ hidRight = rightWidget->isVisible();
+ rightWidget->hide();
+ }
+
+ if (!hidLeft && leftWidget) {
+ hidLeft = leftWidget->isVisible();
+ leftWidget->hide();
+ }
+ }
+
+ void unHideWidgets() {
+ if (leftWidget && hidLeft)
+ leftWidget->show();
+ hidLeft = false;
+ if (rightWidget && hidRight)
+ rightWidget->show();
+ hidRight = false;
+ }
+
+ };
+ QList<Tab> tabList;
+ QHash<QTimeLine*, int> animations;
+
+ int calculateNewPosition(int from, int to, int index) const;
+ void slide(int from, int to);
+ void init();
+ int extraWidth() const;
+
+ Tab *at(int index);
+ const Tab *at(int index) const;
+
+ int indexAtPos(const QPoint &p) const;
+
+ inline bool validIndex(int index) const { return index >= 0 && index < tabList.count(); }
+
+ QSize minimumTabSizeHint(int index);
+
+ QToolButton* rightB; // right or bottom
+ QToolButton* leftB; // left or top
+
+ void _q_scrollTabs();
+ void _q_closeTab();
+ void _q_moveTab(int);
+ void _q_moveTabFinished();
+ void _q_moveTabFinished(int offset);
+ QRect hoverRect;
+
+ void grabCache(int start, int end, bool unhide);
+ void refresh();
+ void layoutTabs();
+ void layoutWidgets(int index = -1);
+ void layoutTab(int index);
+ void updateMacBorderMetrics();
+
+ void makeVisible(int index);
+ QSize iconSize;
+ Qt::TextElideMode elideMode;
+ bool useScrollButtons;
+
+ bool expanding;
+ bool closeButtonOnTabs;
+ QTabBar::SelectionBehavior selectionBehaviorOnRemove;
+
+ QPoint dragStartPosition;
+ bool paintWithOffsets;
+ bool movable;
+ bool dragInProgress;
+ bool documentMode;
+
+ // shared by tabwidget and qtabbar
+ static void initStyleBaseOption(QStyleOptionTabBarBaseV2 *optTabBase, QTabBar *tabbar, QSize size)
+ {
+ QStyleOptionTab tabOverlap;
+ tabOverlap.shape = tabbar->shape();
+ int overlap = tabbar->style()->pixelMetric(QStyle::PM_TabBarBaseOverlap, &tabOverlap, tabbar);
+ QWidget *theParent = tabbar->parentWidget();
+ optTabBase->init(tabbar);
+ optTabBase->shape = tabbar->shape();
+ optTabBase->documentMode = tabbar->documentMode();
+ if (theParent && overlap > 0) {
+ QRect rect;
+ switch (tabOverlap.shape) {
+ case QTabBar::RoundedNorth:
+ case QTabBar::TriangularNorth:
+ rect.setRect(0, size.height()-overlap, size.width(), overlap);
+ break;
+ case QTabBar::RoundedSouth:
+ case QTabBar::TriangularSouth:
+ rect.setRect(0, 0, size.width(), overlap);
+ break;
+ case QTabBar::RoundedEast:
+ case QTabBar::TriangularEast:
+ rect.setRect(0, 0, overlap, size.height());
+ break;
+ case QTabBar::RoundedWest:
+ case QTabBar::TriangularWest:
+ rect.setRect(size.width() - overlap, 0, overlap, size.height());
+ break;
+ }
+ optTabBase->rect = rect;
+ }
+ }
+
+};
+
+class CloseButton : public QAbstractButton
+{
+ Q_OBJECT
+
+public:
+ CloseButton(QWidget *parent = 0);
+
+ QSize sizeHint() const;
+ inline QSize minimumSizeHint() const
+ { return sizeHint(); }
+ void enterEvent(QEvent *event);
+ void leaveEvent(QEvent *event);
+ void paintEvent(QPaintEvent *event);
+};
+
+
+QT_END_NAMESPACE
+
+#endif
+
+#endif
diff --git a/src/gui/widgets/qtabwidget.cpp b/src/gui/widgets/qtabwidget.cpp
new file mode 100644
index 0000000000..c16e000e60
--- /dev/null
+++ b/src/gui/widgets/qtabwidget.cpp
@@ -0,0 +1,1450 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qtabwidget.h"
+
+#ifndef QT_NO_TABWIDGET
+#include "private/qwidget_p.h"
+#include "private/qtabbar_p.h"
+#include "qapplication.h"
+#include "qbitmap.h"
+#include "qdesktopwidget.h"
+#include "qevent.h"
+#include "qlayout.h"
+#include "qstackedwidget.h"
+#include "qstyle.h"
+#include "qstyleoption.h"
+#include "qstylepainter.h"
+#include "qtabbar.h"
+#include "qtoolbutton.h"
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QTabWidget
+ \brief The QTabWidget class provides a stack of tabbed widgets.
+
+ \ingroup organizers
+ \ingroup basicwidgets
+ \mainclass
+
+ A tab widget provides a tab bar (see QTabBar) and a "page area"
+ that is used to display pages related to each tab. By default, the
+ tab bar is shown above the page area, but different configurations
+ are available (see \l{TabPosition}). Each tab is associated with a
+ different widget (called a page). Only the current page is shown in
+ the page area; all the other pages are hidden. The user can show a
+ different page by clicking on its tab or by pressing its
+ Alt+\e{letter} shortcut if it has one.
+
+ The normal way to use QTabWidget is to do the following:
+ \list 1
+ \i Create a QTabWidget.
+ \i Create a QWidget for each of the pages in the tab dialog, but
+ do not specify parent widgets for them.
+ \i Insert child widgets into the page widget, using layouts to
+ position them as normal.
+ \i Call addTab() or insertTab() to put the page widgets into the
+ tab widget, giving each tab a suitable label with an optional
+ keyboard shortcut.
+ \endlist
+
+ The position of the tabs is defined by \l tabPosition, their shape
+ by \l tabShape.
+
+ The signal currentChanged() is emitted when the user selects a
+ page.
+
+ The current page index is available as currentIndex(), the current
+ page widget with currentWidget(). You can retrieve a pointer to a
+ page widget with a given index using widget(), and can find the
+ index position of a widget with indexOf(). Use setCurrentWidget()
+ or setCurrentIndex() to show a particular page.
+
+ You can change a tab's text and icon using setTabText() or
+ setTabIcon(). A tab and its associated page can be removed with
+ removeTab().
+
+ Each tab is either enabled or disabled at any given time (see
+ setTabEnabled()). If a tab is enabled, the tab text is drawn
+ normally 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.
+
+ Tab widgets can be a very good way to split up a complex dialog.
+ An alternative is to use a QStackedWidget for which you provide some
+ means of navigating between pages, for example, a QToolBar or a
+ QListWidget.
+
+ Most of the functionality in QTabWidget is provided by a QTabBar
+ (at the top, providing the tabs) and a QStackedWidget (most of the
+ area, organizing the individual pages).
+
+ \table 100%
+ \row \o \inlineimage windowsxp-tabwidget.png Screenshot of a Windows XP style tab widget
+ \o \inlineimage macintosh-tabwidget.png Screenshot of a Macintosh style tab widget
+ \o \inlineimage plastique-tabwidget.png Screenshot of a Plastique style tab widget
+ \row \o A Windows XP style tab widget.
+ \o A Macintosh style tab widget.
+ \o A Plastique style tab widget.
+ \endtable
+
+ \sa QTabBar, QStackedWidget, QToolBox, {Tab Dialog Example}
+*/
+
+/*!
+ \enum QTabWidget::TabPosition
+
+ This enum type defines where QTabWidget draws the tab row:
+
+ \value North The tabs are drawn above the pages.
+ \value South The tabs are drawn below the pages.
+ \value West The tabs are drawn to the left of the pages.
+ \value East The tabs are drawn to the right of the pages.
+ \omitvalue Bottom
+ \omitvalue Top
+*/
+
+/*!
+ \enum QTabWidget::TabShape
+
+ This enum type defines the shape of the tabs:
+ \value Rounded The tabs are drawn with a rounded look. This is the default
+ shape.
+ \value Triangular The tabs are drawn with a triangular look.
+*/
+
+/*!
+ \fn void QTabWidget::selected(const QString &tabLabel)
+
+ This signal is emitted whenever a tab is selected (raised),
+ including during the first show().
+
+ You can normally use currentChanged() instead.
+*/
+
+/*!
+ \fn void QTabWidget::currentChanged(int index)
+
+ This signal is emitted whenever the current page index changes.
+ The parameter is the new current page \a index position, or -1
+ if there isn't a new one (for example, if there are no widgets
+ in the QTabWidget)
+
+ \sa currentWidget() currentIndex
+*/
+
+/*!
+ \fn void QTabWidget::tabCloseRequested(int index)
+ \since 4.5
+
+ This signal is emitted when the close button on a tab is clicked.
+ The \a index is the index that should be removed.
+
+ \sa setTabsClosable()
+*/
+
+class QTabWidgetPrivate : public QWidgetPrivate
+{
+ Q_DECLARE_PUBLIC(QTabWidget)
+
+public:
+ QTabWidgetPrivate();
+ ~QTabWidgetPrivate();
+ void updateTabBarPosition();
+ void _q_showTab(int);
+ void _q_removeTab(int);
+ void _q_tabMoved(int from, int to);
+ void init();
+
+ QTabBar *tabs;
+ QStackedWidget *stack;
+ QRect panelRect;
+ bool dirty;
+ QTabWidget::TabPosition pos;
+ QTabWidget::TabShape shape;
+ int alignment;
+ QWidget *leftCornerWidget;
+ QWidget *rightCornerWidget;
+};
+
+QTabWidgetPrivate::QTabWidgetPrivate()
+ : tabs(0), stack(0), dirty(true),
+ pos(QTabWidget::North), shape(QTabWidget::Rounded),
+ leftCornerWidget(0), rightCornerWidget(0)
+{}
+
+QTabWidgetPrivate::~QTabWidgetPrivate()
+{}
+
+void QTabWidgetPrivate::init()
+{
+ Q_Q(QTabWidget);
+
+ stack = new QStackedWidget(q);
+ stack->setObjectName(QLatin1String("qt_tabwidget_stackedwidget"));
+ stack->setLineWidth(0);
+ // hack so that QMacStyle::layoutSpacing() can detect tab widget pages
+ stack->setSizePolicy(QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred, QSizePolicy::TabWidget));
+
+ QObject::connect(stack, SIGNAL(widgetRemoved(int)), q, SLOT(_q_removeTab(int)));
+ QTabBar *tabBar = new QTabBar(q);
+ tabBar->setObjectName(QLatin1String("qt_tabwidget_tabbar"));
+ tabBar->setDrawBase(false);
+ q->setTabBar(tabBar);
+
+ q->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding,
+ QSizePolicy::TabWidget));
+#ifdef QT_KEYPAD_NAVIGATION
+ if (QApplication::keypadNavigationEnabled())
+ q->setFocusPolicy(Qt::NoFocus);
+ else
+#endif
+ q->setFocusPolicy(Qt::TabFocus);
+ q->setFocusProxy(tabs);
+ q->setTabPosition(static_cast<QTabWidget::TabPosition> (q->style()->styleHint(
+ QStyle::SH_TabWidget_DefaultTabPosition, 0, q )));
+
+}
+
+/*!
+ Initialize \a option with the values from this QTabWidget. This method is useful
+ for subclasses when they need a QStyleOptionTabWidgetFrame, but don't want to fill
+ in all the information themselves.
+
+ \sa QStyleOption::initFrom() QTabBar::initStyleOption()
+*/
+void QTabWidget::initStyleOption(QStyleOptionTabWidgetFrame *option) const
+{
+ if (!option)
+ return;
+
+ Q_D(const QTabWidget);
+ option->initFrom(this);
+
+ if (documentMode())
+ option->lineWidth = 0;
+ else
+ option->lineWidth = style()->pixelMetric(QStyle::PM_DefaultFrameWidth, 0, this);
+
+ int exth = style()->pixelMetric(QStyle::PM_TabBarBaseHeight, 0, this);
+ QSize t(0, d->stack->frameWidth());
+ if (d->tabs->isVisibleTo(const_cast<QTabWidget *>(this))) {
+ t = d->tabs->sizeHint();
+ if (documentMode()) {
+ if (tabPosition() == East || tabPosition() == West) {
+ t.setHeight(height());
+ } else {
+ t.setWidth(width());
+ }
+ }
+ }
+
+ if (d->rightCornerWidget) {
+ const QSize rightCornerSizeHint = d->rightCornerWidget->sizeHint();
+ const QSize bounds(rightCornerSizeHint.width(), t.height() - exth);
+ option->rightCornerWidgetSize = rightCornerSizeHint.boundedTo(bounds);
+ } else {
+ option->rightCornerWidgetSize = QSize(0, 0);
+ }
+
+ if (d->leftCornerWidget) {
+ const QSize leftCornerSizeHint = d->leftCornerWidget->sizeHint();
+ const QSize bounds(leftCornerSizeHint.width(), t.height() - exth);
+ option->leftCornerWidgetSize = leftCornerSizeHint.boundedTo(bounds);
+ } else {
+ option->leftCornerWidgetSize = QSize(0, 0);
+ }
+
+ switch (d->pos) {
+ case QTabWidget::North:
+ option->shape = d->shape == QTabWidget::Rounded ? QTabBar::RoundedNorth
+ : QTabBar::TriangularNorth;
+ break;
+ case QTabWidget::South:
+ option->shape = d->shape == QTabWidget::Rounded ? QTabBar::RoundedSouth
+ : QTabBar::TriangularSouth;
+ break;
+ case QTabWidget::West:
+ option->shape = d->shape == QTabWidget::Rounded ? QTabBar::RoundedWest
+ : QTabBar::TriangularWest;
+ break;
+ case QTabWidget::East:
+ option->shape = d->shape == QTabWidget::Rounded ? QTabBar::RoundedEast
+ : QTabBar::TriangularEast;
+ break;
+ }
+ option->tabBarSize = t;
+}
+
+/*!
+ Constructs a tabbed widget with parent \a parent.
+*/
+QTabWidget::QTabWidget(QWidget *parent)
+ : QWidget(*new QTabWidgetPrivate, parent, 0)
+{
+ Q_D(QTabWidget);
+ d->init();
+}
+
+#ifdef QT3_SUPPORT
+/*!
+ Use one of the constructors that doesn't take the \a name
+ argument and then use setObjectName() instead.
+*/
+QTabWidget::QTabWidget(QWidget *parent, const char *name, Qt::WindowFlags f)
+ : QWidget(*new QTabWidgetPrivate, parent, f)
+{
+ Q_D(QTabWidget);
+ setObjectName(QString::fromAscii(name));
+ d->init();
+}
+#endif
+
+/*!
+ Destroys the tabbed widget.
+*/
+QTabWidget::~QTabWidget()
+{
+}
+
+/*!
+ \fn int QTabWidget::addTab(QWidget *page, const QString &label)
+
+ Adds a tab with the given \a page and \a label to the tab widget,
+ and returns the index of the tab in the tab bar.
+
+ If the tab's \a label contains an ampersand, the letter following
+ the ampersand is used as a shortcut for the tab, e.g. if the
+ label is "Bro\&wse" then Alt+W becomes a shortcut which will
+ move the focus to this tab.
+
+ \note If you call addTab() after show(), the layout system will try
+ to adjust to the changes in its widgets hierarchy and may cause
+ flicker. To prevent this, you can set the QWidget::updatesEnabled
+ property to false prior to changes; remember to set the property
+ to true when the changes are done, making the widget receive paint
+ events again.
+
+ \sa insertTab()
+*/
+int QTabWidget::addTab(QWidget *child, const QString &label)
+{
+ return insertTab(-1, child, label);
+}
+
+
+/*!
+ \fn int QTabWidget::addTab(QWidget *page, const QIcon &icon, const QString &label)
+ \overload
+
+ Adds a tab with the given \a page, \a icon, and \a label to the tab
+ widget, and returns the index of the tab in the tab bar.
+
+ This function is the same as addTab(), but with an additional \a
+ icon.
+*/
+int QTabWidget::addTab(QWidget *child, const QIcon& icon, const QString &label)
+{
+ return insertTab(-1, child, icon, label);
+}
+
+
+/*!
+ \fn int QTabWidget::insertTab(int index, QWidget *page, const QString &label)
+
+ Inserts a tab with the given \a label and \a page into the tab
+ widget at the specified \a index, and returns the index of the
+ inserted tab in the tab bar.
+
+ The label is displayed in the tab and may vary in appearance depending
+ on the configuration of the tab widget.
+
+ If the tab's \a label contains an ampersand, the letter following
+ the ampersand is used as a shortcut for the tab, e.g. if the
+ label is "Bro\&wse" then Alt+W becomes a shortcut which will
+ move the focus to this tab.
+
+ If \a index is out of range, the tab is simply appended.
+ Otherwise it is inserted at the specified position.
+
+ If the QTabWidget was empty before this function is called, the
+ new page becomes the current page. Inserting a new tab at an index
+ less than or equal to the current index will increment the current
+ index, but keep the current page.
+
+ \note If you call insertTab() after show(), the layout system will try
+ to adjust to the changes in its widgets hierarchy and may cause
+ flicker. To prevent this, you can set the QWidget::updatesEnabled
+ property to false prior to changes; remember to set the property
+ to true when the changes are done, making the widget receive paint
+ events again.
+
+ \sa addTab()
+*/
+int QTabWidget::insertTab(int index, QWidget *w, const QString &label)
+{
+ return insertTab(index, w, QIcon(), label);
+}
+
+
+/*!
+ \fn int QTabWidget::insertTab(int index, QWidget *page, const QIcon& icon, const QString &label)
+ \overload
+
+ Inserts a tab with the given \a label, \a page, and \a icon into
+ the tab widget at the specified \a index, and returns the index of the
+ inserted tab in the tab bar.
+
+ This function is the same as insertTab(), but with an additional
+ \a icon.
+*/
+int QTabWidget::insertTab(int index, QWidget *w, const QIcon& icon, const QString &label)
+{
+ Q_D(QTabWidget);
+ if(!w)
+ return -1;
+ index = d->stack->insertWidget(index, w);
+ d->tabs->insertTab(index, icon, label);
+ setUpLayout();
+ tabInserted(index);
+
+ return index;
+}
+
+
+/*!
+ Defines a new \a label for the page at position \a index's tab.
+
+ If the provided text contains an ampersand character ('&'), a
+ shortcut is automatically created for it. The character that
+ follows the '&' will be used as the shortcut key. Any previous
+ shortcut will be overwritten, or cleared if no shortcut is defined
+ by the text. See the \l {QShortcut#mnemonic}{QShortcut}
+ documentation for details (to display an actual ampersand, use
+ '&&').
+
+*/
+void QTabWidget::setTabText(int index, const QString &label)
+{
+ Q_D(QTabWidget);
+ d->tabs->setTabText(index, label);
+ setUpLayout();
+}
+
+/*!
+ Returns the label text for the tab on the page at position \a index.
+*/
+
+QString QTabWidget::tabText(int index) const
+{
+ Q_D(const QTabWidget);
+ return d->tabs->tabText(index);
+}
+
+/*!
+ \overload
+
+ Sets the \a icon for the tab at position \a index.
+*/
+void QTabWidget::setTabIcon(int index, const QIcon &icon)
+{
+ Q_D(QTabWidget);
+ d->tabs->setTabIcon(index, icon);
+ setUpLayout();
+}
+
+/*!
+ Returns the icon for the tab on the page at position \a index.
+*/
+
+QIcon QTabWidget::tabIcon(int index) const
+{
+ Q_D(const QTabWidget);
+ return d->tabs->tabIcon(index);
+}
+
+/*!
+ Returns true if the the page at position \a index is enabled; otherwise returns false.
+
+ \sa setTabEnabled(), QWidget::isEnabled()
+*/
+
+bool QTabWidget::isTabEnabled(int index) const
+{
+ Q_D(const QTabWidget);
+ return d->tabs->isTabEnabled(index);
+}
+
+/*!
+ If \a enable is true, the page at position \a index is enabled; otherwise the page at position \a index 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/page may be visible. If the page is
+ visible already, QTabWidget will not hide it; if all the pages are
+ disabled, QTabWidget will show one of them.
+
+ \sa isTabEnabled(), QWidget::setEnabled()
+*/
+
+void QTabWidget::setTabEnabled(int index, bool enable)
+{
+ Q_D(QTabWidget);
+ d->tabs->setTabEnabled(index, enable);
+}
+
+/*!
+ \fn void QTabWidget::setCornerWidget(QWidget *widget, Qt::Corner corner)
+
+ Sets the given \a widget to be shown in the specified \a corner of the
+ tab widget. The geometry of the widget is determined based on the widget's
+ sizeHint() and the style().
+
+ Only the horizontal element of the \a corner will be used.
+
+ Passing 0 shows no widget in the corner.
+
+ Any previously set corner widget is hidden.
+
+ All widgets set here will be deleted by the tab widget when it is
+ destroyed unless you separately reparent the widget after setting
+ some other corner widget (or 0).
+
+ Note: Corner widgets are designed for \l North and \l South tab positions;
+ other orientations are known to not work properly.
+
+ \sa cornerWidget(), setTabPosition()
+*/
+void QTabWidget::setCornerWidget(QWidget * widget, Qt::Corner corner)
+{
+ Q_D(QTabWidget);
+ if (widget && widget->parentWidget() != this)
+ widget->setParent(this);
+
+ if (corner & Qt::TopRightCorner) {
+ if (d->rightCornerWidget)
+ d->rightCornerWidget->hide();
+ d->rightCornerWidget = widget;
+ } else {
+ if (d->leftCornerWidget)
+ d->leftCornerWidget->hide();
+ d->leftCornerWidget = widget;
+ }
+ setUpLayout();
+}
+
+/*!
+ Returns the widget shown in the \a corner of the tab widget or 0.
+*/
+QWidget * QTabWidget::cornerWidget(Qt::Corner corner) const
+{
+ Q_D(const QTabWidget);
+ if (corner & Qt::TopRightCorner)
+ return d->rightCornerWidget;
+ return d->leftCornerWidget;
+}
+
+/*!
+ Removes the tab at position \a index from this stack of widgets.
+ The page widget itself is not deleted.
+
+ \sa addTab(), insertTab()
+*/
+void QTabWidget::removeTab(int index)
+{
+ Q_D(QTabWidget);
+ if (QWidget *w = d->stack->widget(index))
+ d->stack->removeWidget(w);
+}
+
+/*!
+ 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).
+
+ \sa currentIndex(), setCurrentWidget()
+*/
+
+QWidget * QTabWidget::currentWidget() const
+{
+ Q_D(const QTabWidget);
+ return d->stack->currentWidget();
+}
+
+/*!
+ Makes \a widget the current widget. The \a widget used must be a page in
+ this tab widget.
+
+ \sa addTab(), setCurrentIndex(), currentWidget()
+ */
+void QTabWidget::setCurrentWidget(QWidget *widget)
+{
+ Q_D(const QTabWidget);
+ d->tabs->setCurrentIndex(indexOf(widget));
+}
+
+
+/*!
+ \property QTabWidget::currentIndex
+ \brief the index position of the current tab page
+
+ The current index is -1 if there is no current widget.
+
+ By default, this property contains a value of -1 because there are initially
+ no tabs in the widget.
+*/
+
+int QTabWidget::currentIndex() const
+{
+ Q_D(const QTabWidget);
+ return d->tabs->currentIndex();
+}
+
+void QTabWidget::setCurrentIndex(int index)
+{
+ Q_D(QTabWidget);
+ d->tabs->setCurrentIndex(index);
+}
+
+
+/*!
+ Returns the index position of the page occupied by the widget \a
+ w, or -1 if the widget cannot be found.
+*/
+int QTabWidget::indexOf(QWidget* w) const
+{
+ Q_D(const QTabWidget);
+ return d->stack->indexOf(w);
+}
+
+
+/*!
+ \reimp
+*/
+void QTabWidget::resizeEvent(QResizeEvent *e)
+{
+ QWidget::resizeEvent(e);
+ setUpLayout();
+}
+
+/*!
+ Replaces the dialog's QTabBar heading with the 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 QTabWidget::setTabBar(QTabBar* tb)
+{
+ Q_D(QTabWidget);
+ Q_ASSERT(tb);
+
+ if (tb->parentWidget() != this) {
+ tb->setParent(this);
+ tb->show();
+ }
+ delete d->tabs;
+ d->tabs = tb;
+ setFocusProxy(d->tabs);
+ connect(d->tabs, SIGNAL(currentChanged(int)),
+ this, SLOT(_q_showTab(int)));
+ connect(d->tabs, SIGNAL(tabMoved(int, int)),
+ this, SLOT(_q_tabMoved(int, int)));
+ if (d->tabs->tabsClosable())
+ connect(d->tabs, SIGNAL(tabCloseRequested(int)),
+ this, SIGNAL(tabCloseRequested(int)));
+ tb->setExpanding(!documentMode());
+ setUpLayout();
+}
+
+
+/*!
+ Returns the current QTabBar.
+
+ \sa setTabBar()
+*/
+QTabBar* QTabWidget::tabBar() const
+{
+ Q_D(const QTabWidget);
+ return d->tabs;
+}
+
+/*!
+ Ensures that the selected tab's page is visible and appropriately
+ sized.
+*/
+
+void QTabWidgetPrivate::_q_showTab(int index)
+{
+ Q_Q(QTabWidget);
+ if (index < stack->count() && index >= 0)
+ stack->setCurrentIndex(index);
+ emit q->currentChanged(index);
+#ifdef QT3_SUPPORT
+ emit q->selected(q->tabText(index));
+ emit q->currentChanged(stack->widget(index));
+#endif
+}
+
+void QTabWidgetPrivate::_q_removeTab(int index)
+{
+ Q_Q(QTabWidget);
+ tabs->removeTab(index);
+ q->setUpLayout();
+ q->tabRemoved(index);
+}
+
+void QTabWidgetPrivate::_q_tabMoved(int from, int to)
+{
+ stack->blockSignals(true);
+ QWidget *w = stack->widget(from);
+ stack->removeWidget(w);
+ stack->insertWidget(to, w);
+ stack->blockSignals(false);
+}
+
+/*
+ Set up the layout.
+ Get subrect from the current style, and set the geometry for the
+ stack widget, tab bar and corner widgets.
+*/
+void QTabWidget::setUpLayout(bool onlyCheck)
+{
+ Q_D(QTabWidget);
+ if (onlyCheck && !d->dirty)
+ return; // nothing to do
+
+ QStyleOptionTabWidgetFrame option;
+ initStyleOption(&option);
+
+ // this must be done immediately, because QWidgetItem relies on it (even if !isVisible())
+ d->setLayoutItemMargins(QStyle::SE_TabWidgetLayoutItem, &option);
+
+ if (!isVisible()) {
+ d->dirty = true;
+ return; // we'll do it later
+ }
+
+ QRect tabRect = style()->subElementRect(QStyle::SE_TabWidgetTabBar, &option, this);
+ d->panelRect = style()->subElementRect(QStyle::SE_TabWidgetTabPane, &option, this);
+ QRect contentsRect = style()->subElementRect(QStyle::SE_TabWidgetTabContents, &option, this);
+ QRect leftCornerRect = style()->subElementRect(QStyle::SE_TabWidgetLeftCorner, &option, this);
+ QRect rightCornerRect = style()->subElementRect(QStyle::SE_TabWidgetRightCorner, &option, this);
+
+ d->tabs->setGeometry(tabRect);
+ d->stack->setGeometry(contentsRect);
+ if (d->leftCornerWidget)
+ d->leftCornerWidget->setGeometry(leftCornerRect);
+ if (d->rightCornerWidget)
+ d->rightCornerWidget->setGeometry(rightCornerRect);
+
+ if (!onlyCheck)
+ update();
+ updateGeometry();
+}
+
+/*!
+ \internal
+*/
+static inline QSize basicSize(
+ bool horizontal, const QSize &lc, const QSize &rc, const QSize &s, const QSize &t)
+{
+ return horizontal
+ ? QSize(qMax(s.width(), t.width() + rc.width() + lc.width()),
+ s.height() + (qMax(rc.height(), qMax(lc.height(), t.height()))))
+ : QSize(s.width() + (qMax(rc.width(), qMax(lc.width(), t.width()))),
+ qMax(s.height(), t.height() + rc.height() + lc.height()));
+}
+
+/*!
+ \reimp
+*/
+QSize QTabWidget::sizeHint() const
+{
+ Q_D(const QTabWidget);
+ QSize lc(0, 0), rc(0, 0);
+ QStyleOption opt(0);
+ opt.init(this);
+ opt.state = QStyle::State_None;
+
+ if (d->leftCornerWidget)
+ lc = d->leftCornerWidget->sizeHint();
+ if(d->rightCornerWidget)
+ rc = d->rightCornerWidget->sizeHint();
+ if (!d->dirty) {
+ QTabWidget *that = (QTabWidget*)this;
+ that->setUpLayout(true);
+ }
+ QSize s(d->stack->sizeHint());
+ QSize t(d->tabs->sizeHint());
+ if(usesScrollButtons())
+ t = t.boundedTo(QSize(200,200));
+ else
+ t = t.boundedTo(QApplication::desktop()->size());
+
+ QSize sz = basicSize(d->pos == North || d->pos == South, lc, rc, s, t);
+
+ return style()->sizeFromContents(QStyle::CT_TabWidget, &opt, sz, this)
+ .expandedTo(QApplication::globalStrut());
+}
+
+
+/*!
+ \reimp
+
+ Returns a suitable minimum size for the tab widget.
+*/
+QSize QTabWidget::minimumSizeHint() const
+{
+ Q_D(const QTabWidget);
+ QSize lc(0, 0), rc(0, 0);
+
+ if(d->leftCornerWidget)
+ lc = d->leftCornerWidget->minimumSizeHint();
+ if(d->rightCornerWidget)
+ rc = d->rightCornerWidget->minimumSizeHint();
+ if (!d->dirty) {
+ QTabWidget *that = (QTabWidget*)this;
+ that->setUpLayout(true);
+ }
+ QSize s(d->stack->minimumSizeHint());
+ QSize t(d->tabs->minimumSizeHint());
+
+ QSize sz = basicSize(d->pos == North || d->pos == South, lc, rc, s, t);
+
+ QStyleOption opt(0);
+ opt.rect = rect();
+ opt.palette = palette();
+ opt.state = QStyle::State_None;
+ return style()->sizeFromContents(QStyle::CT_TabWidget, &opt, sz, this)
+ .expandedTo(QApplication::globalStrut());
+}
+
+/*!
+ \reimp
+ */
+void QTabWidget::showEvent(QShowEvent *)
+{
+ setUpLayout();
+}
+
+void QTabWidgetPrivate::updateTabBarPosition()
+{
+ Q_Q(QTabWidget);
+ switch (pos) {
+ case QTabWidget::North:
+ tabs->setShape(shape == QTabWidget::Rounded ? QTabBar::RoundedNorth
+ : QTabBar::TriangularNorth);
+ break;
+ case QTabWidget::South:
+ tabs->setShape(shape == QTabWidget::Rounded ? QTabBar::RoundedSouth
+ : QTabBar::TriangularSouth);
+ break;
+ case QTabWidget::West:
+ tabs->setShape(shape == QTabWidget::Rounded ? QTabBar::RoundedWest
+ : QTabBar::TriangularWest);
+ break;
+ case QTabWidget::East:
+ tabs->setShape(shape == QTabWidget::Rounded ? QTabBar::RoundedEast
+ : QTabBar::TriangularEast);
+ break;
+ }
+ q->setUpLayout();
+}
+
+/*!
+ \property QTabWidget::tabPosition
+ \brief the position of the tabs in this tab widget
+
+ Possible values for this property are described by the TabPosition
+ enum.
+
+ By default, this property is set to \l North.
+
+ \sa TabPosition
+*/
+QTabWidget::TabPosition QTabWidget::tabPosition() const
+{
+ Q_D(const QTabWidget);
+ return d->pos;
+}
+
+void QTabWidget::setTabPosition(TabPosition pos)
+{
+ Q_D(QTabWidget);
+ if (d->pos == pos)
+ return;
+ d->pos = pos;
+ d->updateTabBarPosition();
+}
+
+/*!
+ \property QTabWidget::tabsClosable
+ \brief whether close buttons are automatically added to each tab.
+
+ \since 4.5
+
+ \sa QTabBar::tabsClosable()
+*/
+bool QTabWidget::tabsClosable() const
+{
+ return tabBar()->tabsClosable();
+}
+
+void QTabWidget::setTabsClosable(bool closeable)
+{
+ if (tabsClosable() == closeable)
+ return;
+
+ tabBar()->setTabsClosable(closeable);
+ if (closeable)
+ connect(tabBar(), SIGNAL(tabCloseRequested(int)),
+ this, SIGNAL(tabCloseRequested(int)));
+ else
+ disconnect(tabBar(), SIGNAL(tabCloseRequested(int)),
+ this, SIGNAL(tabCloseRequested(int)));
+ setUpLayout();
+}
+
+/*!
+ \property QTabWidget::movable
+ \brief This property holds whether the user can move the tabs
+ within the tabbar area.
+
+ \since 4.5
+
+ By default, this property is false;
+*/
+
+bool QTabWidget::isMovable() const
+{
+ return tabBar()->isMovable();
+}
+
+void QTabWidget::setMovable(bool movable)
+{
+ tabBar()->setMovable(movable);
+}
+
+/*!
+ \property QTabWidget::tabShape
+ \brief the shape of the tabs in this tab widget
+
+ Possible values for this property are QTabWidget::Rounded
+ (default) or QTabWidget::Triangular.
+
+ \sa TabShape
+*/
+
+QTabWidget::TabShape QTabWidget::tabShape() const
+{
+ Q_D(const QTabWidget);
+ return d->shape;
+}
+
+void QTabWidget::setTabShape(TabShape s)
+{
+ Q_D(QTabWidget);
+ if (d->shape == s)
+ return;
+ d->shape = s;
+ d->updateTabBarPosition();
+}
+
+/*!
+ \reimp
+ */
+bool QTabWidget::event(QEvent *ev)
+{
+ if (ev->type() == QEvent::LayoutRequest)
+ setUpLayout();
+ return QWidget::event(ev);
+}
+
+/*!
+ \reimp
+ */
+void QTabWidget::changeEvent(QEvent *ev)
+{
+ if (ev->type() == QEvent::StyleChange
+#ifdef Q_WS_MAC
+ || ev->type() == QEvent::MacSizeChange
+#endif
+ )
+ setUpLayout();
+ QWidget::changeEvent(ev);
+}
+
+
+/*!
+ \reimp
+ */
+void QTabWidget::keyPressEvent(QKeyEvent *e)
+{
+ Q_D(QTabWidget);
+ if (((e->key() == Qt::Key_Tab || e->key() == Qt::Key_Backtab) &&
+ count() > 1 && e->modifiers() & Qt::ControlModifier)
+#ifdef QT_KEYPAD_NAVIGATION
+ || QApplication::keypadNavigationEnabled() && (e->key() == Qt::Key_Left || e->key() == Qt::Key_Right) && count() > 1
+#endif
+ ) {
+ int pageCount = d->tabs->count();
+ int page = currentIndex();
+ int dx = (e->key() == Qt::Key_Backtab || e->modifiers() & Qt::ShiftModifier) ? -1 : 1;
+#ifdef QT_KEYPAD_NAVIGATION
+ if (QApplication::keypadNavigationEnabled() && (e->key() == Qt::Key_Left || e->key() == Qt::Key_Right))
+ dx = e->key() == (isRightToLeft() ? Qt::Key_Right : Qt::Key_Left) ? -1 : 1;
+#endif
+ for (int pass = 0; pass < pageCount; ++pass) {
+ page+=dx;
+ if (page < 0
+#ifdef QT_KEYPAD_NAVIGATION
+ && !e->isAutoRepeat()
+#endif
+ ) {
+ page = count() - 1;
+ } else if (page >= pageCount
+#ifdef QT_KEYPAD_NAVIGATION
+ && !e->isAutoRepeat()
+#endif
+ ) {
+ page = 0;
+ }
+ if (d->tabs->isTabEnabled(page)) {
+ setCurrentIndex(page);
+ break;
+ }
+ }
+ if (!qApp->focusWidget())
+ d->tabs->setFocus();
+ } else {
+ e->ignore();
+ }
+}
+
+/*!
+ Returns the tab page at index position \a index or 0 if the \a
+ index is out of range.
+*/
+QWidget *QTabWidget::widget(int index) const
+{
+ Q_D(const QTabWidget);
+ return d->stack->widget(index);
+}
+
+/*!
+ \property QTabWidget::count
+ \brief the number of tabs in the tab bar
+
+ By default, this property contains a value of 0.
+*/
+int QTabWidget::count() const
+{
+ Q_D(const QTabWidget);
+ return d->tabs->count();
+}
+
+#ifndef QT_NO_TOOLTIP
+/*!
+ Sets the tab tool tip for the page at position \a index to \a tip.
+
+ \sa tabToolTip()
+*/
+void QTabWidget::setTabToolTip(int index, const QString & tip)
+{
+ Q_D(QTabWidget);
+ d->tabs->setTabToolTip(index, tip);
+}
+
+/*!
+ Returns the tab tool tip for the page at position \a index or
+ an empty string if no tool tip has been set.
+
+ \sa setTabToolTip()
+*/
+QString QTabWidget::tabToolTip(int index) const
+{
+ Q_D(const QTabWidget);
+ return d->tabs->tabToolTip(index);
+}
+#endif // QT_NO_TOOLTIP
+
+#ifndef QT_NO_WHATSTHIS
+/*!
+ \since 4.1
+
+ Sets the What's This help text for the page at position \a index
+ to \a text.
+*/
+void QTabWidget::setTabWhatsThis(int index, const QString &text)
+{
+ Q_D(QTabWidget);
+ d->tabs->setTabWhatsThis(index, text);
+}
+
+/*!
+ \since 4.1
+
+ Returns the What's This help text for the page at position \a index,
+ or an empty string if no help text has been set.
+*/
+QString QTabWidget::tabWhatsThis(int index) const
+{
+ Q_D(const QTabWidget);
+ return d->tabs->tabWhatsThis(index);
+}
+#endif // QT_NO_WHATSTHIS
+
+/*!
+ This virtual handler is called after a new tab was added or
+ inserted at position \a index.
+
+ \sa tabRemoved()
+ */
+void QTabWidget::tabInserted(int index)
+{
+ Q_UNUSED(index)
+}
+
+/*!
+ This virtual handler is called after a tab was removed from
+ position \a index.
+
+ \sa tabInserted()
+ */
+void QTabWidget::tabRemoved(int index)
+{
+ Q_UNUSED(index)
+}
+
+/*!
+ \fn void QTabWidget::paintEvent(QPaintEvent *event)
+
+ Paints the tab widget's tab bar in response to the paint \a event.
+*/
+void QTabWidget::paintEvent(QPaintEvent *)
+{
+ Q_D(QTabWidget);
+ QStylePainter p(this);
+ if (documentMode()) {
+ if (QWidget *w = cornerWidget(Qt::TopLeftCorner)) {
+ QStyleOptionTabBarBaseV2 opt;
+ QTabBarPrivate::initStyleBaseOption(&opt, tabBar(), w->size());
+ opt.rect.moveLeft(w->x() + opt.rect.x());
+ opt.rect.moveTop(w->y() + opt.rect.y());
+ p.drawPrimitive(QStyle::PE_FrameTabBarBase, opt);
+ }
+ if (QWidget *w = cornerWidget(Qt::TopRightCorner)) {
+ QStyleOptionTabBarBaseV2 opt;
+ QTabBarPrivate::initStyleBaseOption(&opt, tabBar(), w->size());
+ opt.rect.moveLeft(w->x() + opt.rect.x());
+ opt.rect.moveTop(w->y() + opt.rect.y());
+ p.drawPrimitive(QStyle::PE_FrameTabBarBase, opt);
+ }
+ return;
+ }
+
+ QStyleOptionTabWidgetFrame opt;
+ initStyleOption(&opt);
+ opt.rect = d->panelRect;
+ p.drawPrimitive(QStyle::PE_FrameTabWidget, opt);
+}
+
+/*!
+ \property QTabWidget::iconSize
+ \brief The size for icons in the tab bar
+ \since 4.2
+
+ The default value is style-dependent. This is the maximum size
+ that the icons will have. Icons are not scaled up if they are of
+ smaller size.
+
+ \sa QTabBar::iconSize
+*/
+QSize QTabWidget::iconSize() const
+{
+ return d_func()->tabs->iconSize();
+}
+
+void QTabWidget::setIconSize(const QSize &size)
+{
+ d_func()->tabs->setIconSize(size);
+}
+
+/*!
+ \property QTabWidget::elideMode
+ \brief how to elide text in the tab bar
+ \since 4.2
+
+ This property controls how items are elided when there is not
+ enough space to show them for a given tab bar size.
+
+ By default the value is style dependant.
+
+ \sa QTabBar::elideMode usesScrollButtons QStyle::SH_TabBar_ElideMode
+*/
+Qt::TextElideMode QTabWidget::elideMode() const
+{
+ return d_func()->tabs->elideMode();
+}
+
+void QTabWidget::setElideMode(Qt::TextElideMode mode)
+{
+ d_func()->tabs->setElideMode(mode);
+}
+
+/*!
+ \property QTabWidget::usesScrollButtons
+ \brief Whether or not a tab bar should use buttons to scroll tabs when it
+ has many tabs.
+ \since 4.2
+
+ When there are too many tabs in a tab bar for its size, the tab bar can either choose
+ to expand its size or to add buttons that allow you to scroll through the tabs.
+
+ By default the value is style dependant.
+
+ \sa elideMode QTabBar::usesScrollButtons QStyle::SH_TabBar_PreferNoArrows
+*/
+bool QTabWidget::usesScrollButtons() const
+{
+ return d_func()->tabs->usesScrollButtons();
+}
+
+void QTabWidget::setUsesScrollButtons(bool useButtons)
+{
+ d_func()->tabs->setUsesScrollButtons(useButtons);
+}
+
+/*!
+ \property QTabWidget::documentMode
+ \brief Whether or not the tab widget is rendered in a mode suitable for document
+ pages. This is the same as document mode on Mac OS X.
+ \since 4.5
+
+ When this property is set the tab widget frame is not rendered. This mode is useful
+ for showing document-type pages where the page covers most of the tab widget
+ area.
+
+ \sa elideMode, QTabBar::documentMode, QTabBar::usesScrollButtons, QStyle::SH_TabBar_PreferNoArrows
+*/
+bool QTabWidget::documentMode() const
+{
+ Q_D(const QTabWidget);
+ return d->tabs->documentMode();
+}
+
+void QTabWidget::setDocumentMode(bool enabled)
+{
+ Q_D(QTabWidget);
+ d->tabs->setDocumentMode(enabled);
+ d->tabs->setExpanding(!enabled);
+ d->tabs->setDrawBase(enabled);
+ setUpLayout();
+}
+
+/*!
+ Removes all the pages, but does not delete them. Calling this function
+ is equivalent to calling removeTab() until the tab widget is empty.
+*/
+void QTabWidget::clear()
+{
+ // ### optimize by introduce QStackedLayout::clear()
+ while (count())
+ removeTab(0);
+}
+
+/*!
+ \fn void QTabWidget::insertTab(QWidget *widget, const QString &label, int index)
+
+ Use insertTab(index, widget, label) instead.
+*/
+
+/*!
+ \fn void QTabWidget::insertTab(QWidget *widget, const QIcon& icon, const QString &label, int index)
+
+ Use insertTab(index, widget, icon, label) instead.
+*/
+
+/*!
+ \fn void QTabWidget::changeTab(QWidget *widget, const QString
+ &label)
+
+ Use setTabText() instead.
+
+*/
+
+/*!
+ \fn void QTabWidget::changeTab(QWidget *widget, const QIcon& icon, const QString &label)
+
+ Use setTabText() and setTabIcon() instead.
+*/
+
+/*!
+ \fn bool QTabWidget::isTabEnabled( QWidget *widget) const
+
+ Use isTabEnabled(tabWidget->indexOf(widget)) instead.
+*/
+
+/*!
+ \fn void QTabWidget::setTabEnabled(QWidget *widget, bool b)
+
+ Use setTabEnabled(tabWidget->indexOf(widget), b) instead.
+*/
+
+/*!
+ \fn QString QTabWidget::tabLabel(QWidget *widget) const
+
+ Use tabText(tabWidget->indexOf(widget)) instead.
+*/
+
+/*!
+ \fn void QTabWidget::setTabLabel(QWidget *widget, const QString
+ &label)
+
+ Use setTabText(tabWidget->indexOf(widget), label) instead.
+*/
+
+/*!
+ \fn QIcon QTabWidget::tabIconSet(QWidget * widget) const
+
+ Use tabIcon(tabWidget->indexOf(widget)) instead.
+*/
+
+/*!
+ \fn void QTabWidget::setTabIconSet(QWidget * widget, const QIcon & icon)
+
+ Use setTabIcon(tabWidget->indexOf(widget), icon) instead.
+*/
+
+/*!
+ \fn void QTabWidget::removeTabToolTip(QWidget * widget)
+
+ Use setTabToolTip(tabWidget->indexOf(widget), QString()) instead.
+*/
+
+/*!
+ \fn void QTabWidget::setTabToolTip(QWidget * widget, const QString & tip)
+
+ Use setTabToolTip(tabWidget->indexOf(widget), tip) instead.
+*/
+
+/*!
+ \fn QString QTabWidget::tabToolTip(QWidget * widget) const
+
+ Use tabToolTip(tabWidget->indexOf(widget)) instead.
+*/
+
+/*!
+ \fn QWidget * QTabWidget::currentPage() const
+
+ Use currentWidget() instead.
+*/
+
+/*!
+ \fn QWidget *QTabWidget::page(int index) const
+
+ Use widget() instead.
+*/
+
+/*!
+ \fn QString QTabWidget::label(int index) const
+
+ Use tabText() instead.
+*/
+
+/*!
+ \fn int QTabWidget::currentPageIndex() const
+
+ Use currentIndex() instead.
+*/
+
+/*!
+ \fn int QTabWidget::margin() const
+
+ This function is kept only to make old code compile.
+ This functionality is no longer supported by QTabWidget.
+
+ \sa contentsRect(), setContentsMargins()
+*/
+
+/*!
+ \fn void QTabWidget::setMargin(int margin)
+
+ This function is kept only to make old code compile.
+ This functionality is no longer supported by QTabWidget.
+
+ \sa contentsRect(), setContentsMargins()
+*/
+
+/*!
+ \fn void QTabWidget::setCurrentPage(int index)
+
+ Use setCurrentIndex() instead.
+*/
+
+/*!
+ \fn void QTabWidget::showPage(QWidget *widget)
+
+ Use setCurrentIndex(indexOf(widget)) instead.
+*/
+
+/*!
+ \fn void QTabWidget::removePage(QWidget *widget)
+
+ Use removeTab(indexOf(widget)) instead.
+*/
+
+/*!
+ \fn void QTabWidget::currentChanged(QWidget *widget)
+
+ Use currentChanged(int) instead.
+*/
+
+QT_END_NAMESPACE
+
+#include "moc_qtabwidget.cpp"
+
+#endif //QT_NO_TABWIDGET
diff --git a/src/gui/widgets/qtabwidget.h b/src/gui/widgets/qtabwidget.h
new file mode 100644
index 0000000000..9307c48fda
--- /dev/null
+++ b/src/gui/widgets/qtabwidget.h
@@ -0,0 +1,252 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QTABWIDGET_H
+#define QTABWIDGET_H
+
+#include <QtGui/qwidget.h>
+#include <QtGui/qicon.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+#ifndef QT_NO_TABWIDGET
+
+class QTabBar;
+class QTabWidgetPrivate;
+class QStyleOptionTabWidgetFrame;
+
+class Q_GUI_EXPORT QTabWidget : public QWidget
+{
+ Q_OBJECT
+ Q_ENUMS(TabPosition TabShape)
+ Q_PROPERTY(TabPosition tabPosition READ tabPosition WRITE setTabPosition)
+ Q_PROPERTY(TabShape tabShape READ tabShape WRITE setTabShape)
+ Q_PROPERTY(int currentIndex READ currentIndex WRITE setCurrentIndex NOTIFY currentChanged)
+ Q_PROPERTY(int count READ count)
+ Q_PROPERTY(QSize iconSize READ iconSize WRITE setIconSize)
+ Q_PROPERTY(Qt::TextElideMode elideMode READ elideMode WRITE setElideMode)
+ Q_PROPERTY(bool usesScrollButtons READ usesScrollButtons WRITE setUsesScrollButtons)
+ Q_PROPERTY(bool documentMode READ documentMode WRITE setDocumentMode)
+ Q_PROPERTY(bool tabsClosable READ tabsClosable WRITE setTabsClosable)
+ Q_PROPERTY(bool movable READ isMovable WRITE setMovable)
+
+public:
+ explicit QTabWidget(QWidget *parent = 0);
+ ~QTabWidget();
+
+ int addTab(QWidget *widget, const QString &);
+ int addTab(QWidget *widget, const QIcon& icon, const QString &label);
+
+ int insertTab(int index, QWidget *widget, const QString &);
+ int insertTab(int index, QWidget *widget, const QIcon& icon, const QString &label);
+
+ void removeTab(int index);
+
+ bool isTabEnabled(int index) const;
+ void setTabEnabled(int index, bool);
+
+ QString tabText(int index) const;
+ void setTabText(int index, const QString &);
+
+ QIcon tabIcon(int index) const;
+ void setTabIcon(int index, const QIcon & icon);
+
+#ifndef QT_NO_TOOLTIP
+ void setTabToolTip(int index, const QString & tip);
+ QString tabToolTip(int index) const;
+#endif
+
+#ifndef QT_NO_WHATSTHIS
+ void setTabWhatsThis(int index, const QString &text);
+ QString tabWhatsThis(int index) const;
+#endif
+
+ int currentIndex() const;
+ QWidget *currentWidget() const;
+ QWidget *widget(int index) const;
+ int indexOf(QWidget *widget) const;
+ int count() const;
+
+ enum TabPosition { North, South, West, East
+#if defined(QT3_SUPPORT) && !defined(Q_MOC_RUN)
+ , Top = North, Bottom = South
+#endif
+ };
+ TabPosition tabPosition() const;
+ void setTabPosition(TabPosition);
+
+ bool tabsClosable() const;
+ void setTabsClosable(bool closeable);
+
+ bool isMovable() const;
+ void setMovable(bool movable);
+
+ enum TabShape { Rounded, Triangular };
+ TabShape tabShape() const;
+ void setTabShape(TabShape s);
+
+ QSize sizeHint() const;
+ QSize minimumSizeHint() const;
+
+ void setCornerWidget(QWidget * w, Qt::Corner corner = Qt::TopRightCorner);
+ QWidget * cornerWidget(Qt::Corner corner = Qt::TopRightCorner) const;
+
+ Qt::TextElideMode elideMode() const;
+ void setElideMode(Qt::TextElideMode);
+
+ QSize iconSize() const;
+ void setIconSize(const QSize &size);
+
+ bool usesScrollButtons() const;
+ void setUsesScrollButtons(bool useButtons);
+
+ bool documentMode() const;
+ void setDocumentMode(bool set);
+
+ void clear();
+
+public Q_SLOTS:
+ void setCurrentIndex(int index);
+ void setCurrentWidget(QWidget *widget);
+
+Q_SIGNALS:
+ void currentChanged(int index);
+ void tabCloseRequested(int index);
+
+protected:
+ virtual void tabInserted(int index);
+ virtual void tabRemoved(int index);
+
+ void showEvent(QShowEvent *);
+ void resizeEvent(QResizeEvent *);
+ void keyPressEvent(QKeyEvent *);
+ void paintEvent(QPaintEvent *);
+ void setTabBar(QTabBar *);
+ QTabBar* tabBar() const;
+ void changeEvent(QEvent *);
+ bool event(QEvent *);
+ void initStyleOption(QStyleOptionTabWidgetFrame *option) const;
+
+#ifdef QT3_SUPPORT
+public:
+ QT3_SUPPORT_CONSTRUCTOR QTabWidget(QWidget *parent, const char *name, Qt::WindowFlags f = 0);
+
+ inline QT3_SUPPORT void insertTab(QWidget * w, const QString &s, int index = -1) { insertTab(index, w, s); }
+ inline QT3_SUPPORT void insertTab(QWidget *child, const QIcon& icon,
+ const QString &label, int index = -1) { insertTab(index, child, icon, label); }
+
+ inline QT3_SUPPORT void changeTab(QWidget *w, const QString &s) {setTabText(indexOf(w), s); }
+ inline QT3_SUPPORT void changeTab(QWidget *w, const QIcon& icon,
+ const QString &label) { int idx = indexOf(w); setTabText(idx, label); setTabIcon(idx, icon); }
+
+ inline QT3_SUPPORT bool isTabEnabled( QWidget *w) const {return isTabEnabled(indexOf(w)); }
+ inline QT3_SUPPORT void setTabEnabled(QWidget *w, bool b) { setTabEnabled(indexOf(w), b); }
+
+ inline QT3_SUPPORT QString tabLabel(QWidget *w) const {return tabText(indexOf(w)); }
+ inline QT3_SUPPORT void setTabLabel(QWidget *w, const QString &l) { setTabText(indexOf(w), l); }
+
+ inline QT3_SUPPORT QIcon tabIconSet(QWidget * w) const {return tabIcon(indexOf(w)); }
+ inline QT3_SUPPORT void setTabIconSet(QWidget * w, const QIcon & icon) { setTabIcon(indexOf(w), icon); }
+
+ inline QT3_SUPPORT void removeTabToolTip(QWidget * w) {
+#ifndef QT_NO_TOOLTIP
+ setTabToolTip(indexOf(w), QString());
+#else
+ Q_UNUSED(w);
+#endif
+ }
+ inline QT3_SUPPORT void setTabToolTip(QWidget * w, const QString & tip) {
+#ifndef QT_NO_TOOLTIP
+ setTabToolTip(indexOf(w), tip);
+#else
+ Q_UNUSED(w);
+ Q_UNUSED(tip);
+#endif
+ }
+
+ inline QT3_SUPPORT QString tabToolTip(QWidget * w) const {
+#ifndef QT_NO_TOOLTIP
+ return tabToolTip(indexOf(w));
+#else
+ Q_UNUSED(w);
+ return QString();
+#endif
+ }
+
+ inline QT3_SUPPORT QWidget * currentPage() const { return currentWidget(); }
+ inline QT3_SUPPORT QWidget *page(int index) const { return widget(index); }
+ inline QT3_SUPPORT QString label(int index) const { return tabText(index); }
+ inline QT3_SUPPORT int currentPageIndex() const { return currentIndex(); }
+
+ inline QT3_SUPPORT int margin() const { return 0; }
+ inline QT3_SUPPORT void setMargin(int) {}
+
+public Q_SLOTS:
+ inline QT_MOC_COMPAT void setCurrentPage(int index) { setCurrentIndex(index); }
+ inline QT_MOC_COMPAT void showPage(QWidget *w) { setCurrentIndex(indexOf(w)); }
+ inline QT_MOC_COMPAT void removePage(QWidget *w) { removeTab(indexOf(w)); }
+
+Q_SIGNALS:
+ QT_MOC_COMPAT void currentChanged(QWidget *);
+ QT_MOC_COMPAT void selected(const QString&);
+#endif // QT3_SUPPORT
+
+private:
+ Q_DECLARE_PRIVATE(QTabWidget)
+ Q_DISABLE_COPY(QTabWidget)
+ Q_PRIVATE_SLOT(d_func(), void _q_showTab(int))
+ Q_PRIVATE_SLOT(d_func(), void _q_removeTab(int))
+ Q_PRIVATE_SLOT(d_func(), void _q_tabMoved(int, int))
+ void setUpLayout(bool = false);
+ friend class Q3TabDialog;
+};
+
+#endif // QT_NO_TABWIDGET
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QTABWIDGET_H
diff --git a/src/gui/widgets/qtextbrowser.cpp b/src/gui/widgets/qtextbrowser.cpp
new file mode 100644
index 0000000000..a1f4d348a8
--- /dev/null
+++ b/src/gui/widgets/qtextbrowser.cpp
@@ -0,0 +1,1275 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qtextbrowser.h"
+#include "qtextedit_p.h"
+
+#ifndef QT_NO_TEXTBROWSER
+
+#include <qstack.h>
+#include <qapplication.h>
+#include <qevent.h>
+#include <qdesktopwidget.h>
+#include <qdebug.h>
+#include <qabstracttextdocumentlayout.h>
+#include "private/qtextdocumentlayout_p.h"
+#include <qtextcodec.h>
+#include <qpainter.h>
+#include <qdir.h>
+#include <qwhatsthis.h>
+#include <qtextobject.h>
+#include <qdesktopservices.h>
+
+QT_BEGIN_NAMESPACE
+
+class QTextBrowserPrivate : public QTextEditPrivate
+{
+ Q_DECLARE_PUBLIC(QTextBrowser)
+public:
+ inline QTextBrowserPrivate()
+ : textOrSourceChanged(false), forceLoadOnSourceChange(false), openExternalLinks(false),
+ openLinks(true)
+#ifdef QT_KEYPAD_NAVIGATION
+ , lastKeypadScrollValue(-1)
+#endif
+ {}
+
+ void init();
+
+ struct HistoryEntry {
+ inline HistoryEntry()
+ : hpos(0), vpos(0), focusIndicatorPosition(-1),
+ focusIndicatorAnchor(-1) {}
+ QUrl url;
+ QString title;
+ int hpos;
+ int vpos;
+ int focusIndicatorPosition, focusIndicatorAnchor;
+ };
+
+ HistoryEntry history(int i) const
+ {
+ if (i <= 0)
+ if (-i < stack.count())
+ return stack[stack.count()+i-1];
+ else
+ return HistoryEntry();
+ else
+ if (i <= forwardStack.count())
+ return forwardStack[forwardStack.count()-i];
+ else
+ return HistoryEntry();
+ }
+
+
+ HistoryEntry createHistoryEntry() const;
+ void restoreHistoryEntry(const HistoryEntry entry);
+
+ QStack<HistoryEntry> stack;
+ QStack<HistoryEntry> forwardStack;
+ QUrl home;
+ QUrl currentURL;
+
+ QStringList searchPaths;
+
+ /*flag necessary to give the linkClicked() signal some meaningful
+ semantics when somebody connected to it calls setText() or
+ setSource() */
+ bool textOrSourceChanged;
+ bool forceLoadOnSourceChange;
+
+ bool openExternalLinks;
+ bool openLinks;
+
+#ifndef QT_NO_CURSOR
+ QCursor oldCursor;
+#endif
+
+ QString findFile(const QUrl &name) const;
+
+ inline void _q_documentModified()
+ {
+ textOrSourceChanged = true;
+ forceLoadOnSourceChange = !currentURL.path().isEmpty();
+ }
+
+ void _q_activateAnchor(const QString &href);
+ void _q_highlightLink(const QString &href);
+
+ void setSource(const QUrl &url);
+
+ // re-imlemented from QTextEditPrivate
+ virtual QUrl resolveUrl(const QUrl &url) const;
+ inline QUrl resolveUrl(const QString &url) const
+ { return resolveUrl(QUrl::fromEncoded(url.toUtf8())); }
+
+#ifdef QT_KEYPAD_NAVIGATION
+ void keypadMove(bool next);
+ QTextCursor prevFocus;
+ int lastKeypadScrollValue;
+#endif
+};
+
+QString QTextBrowserPrivate::findFile(const QUrl &name) const
+{
+ QString fileName;
+ if (name.scheme() == QLatin1String("qrc"))
+ fileName = QLatin1String(":/") + name.path();
+ else
+ fileName = name.toLocalFile();
+
+ if (QFileInfo(fileName).isAbsolute())
+ return fileName;
+
+ foreach (QString path, searchPaths) {
+ if (!path.endsWith(QLatin1Char('/')))
+ path.append(QLatin1Char('/'));
+ path.append(fileName);
+ if (QFileInfo(path).isReadable())
+ return path;
+ }
+
+ return fileName;
+}
+
+QUrl QTextBrowserPrivate::resolveUrl(const QUrl &url) const
+{
+ if (!url.isRelative())
+ return url;
+
+ // For the second case QUrl can merge "#someanchor" with "foo.html"
+ // correctly to "foo.html#someanchor"
+ if (!(currentURL.isRelative()
+ || (currentURL.scheme() == QLatin1String("file")
+ && !QFileInfo(currentURL.toLocalFile()).isAbsolute()))
+ || (url.hasFragment() && url.path().isEmpty())) {
+ return currentURL.resolved(url);
+ }
+
+ // this is our last resort when current url and new url are both relative
+ // we try to resolve against the current working directory in the local
+ // file system.
+ QFileInfo fi(currentURL.toLocalFile());
+ if (fi.exists()) {
+ return QUrl::fromLocalFile(fi.absolutePath() + QDir::separator()).resolved(url);
+ }
+
+ return url;
+}
+
+void QTextBrowserPrivate::_q_activateAnchor(const QString &href)
+{
+ if (href.isEmpty())
+ return;
+ Q_Q(QTextBrowser);
+
+#ifndef QT_NO_CURSOR
+ viewport->setCursor(oldCursor);
+#endif
+
+ const QUrl url = resolveUrl(href);
+
+ if (!openLinks) {
+ emit q->anchorClicked(url);
+ return;
+ }
+
+ textOrSourceChanged = false;
+
+#ifndef QT_NO_DESKTOPSERVICES
+ if ((openExternalLinks
+ && url.scheme() != QLatin1String("file")
+ && url.scheme() != QLatin1String("qrc")
+ && !url.isRelative())
+ || (url.isRelative() && !currentURL.isRelative()
+ && currentURL.scheme() != QLatin1String("file")
+ && currentURL.scheme() != QLatin1String("qrc"))) {
+ QDesktopServices::openUrl(url);
+ return;
+ }
+#endif
+
+ emit q->anchorClicked(url);
+
+ if (textOrSourceChanged)
+ return;
+
+ q->setSource(url);
+}
+
+void QTextBrowserPrivate::_q_highlightLink(const QString &anchor)
+{
+ Q_Q(QTextBrowser);
+ if (anchor.isEmpty()) {
+#ifndef QT_NO_CURSOR
+ if (viewport->cursor().shape() != Qt::PointingHandCursor)
+ oldCursor = viewport->cursor();
+ viewport->setCursor(oldCursor);
+#endif
+ emit q->highlighted(QUrl());
+ emit q->highlighted(QString());
+ } else {
+#ifndef QT_NO_CURSOR
+ viewport->setCursor(Qt::PointingHandCursor);
+#endif
+
+ const QUrl url = resolveUrl(anchor);
+ emit q->highlighted(url);
+ // convenience to ease connecting to QStatusBar::showMessage(const QString &)
+ emit q->highlighted(url.toString());
+ }
+}
+
+void QTextBrowserPrivate::setSource(const QUrl &url)
+{
+ Q_Q(QTextBrowser);
+#ifndef QT_NO_CURSOR
+ if (q->isVisible())
+ qApp->setOverrideCursor(Qt::WaitCursor);
+#endif
+ textOrSourceChanged = true;
+
+ QString txt;
+
+ bool doSetText = false;
+
+ QUrl currentUrlWithoutFragment = currentURL;
+ currentUrlWithoutFragment.setFragment(QString());
+ QUrl newUrlWithoutFragment = currentURL.resolved(url);
+ newUrlWithoutFragment.setFragment(QString());
+
+ if (url.isValid()
+ && (newUrlWithoutFragment != currentUrlWithoutFragment || forceLoadOnSourceChange)) {
+ QVariant data = q->loadResource(QTextDocument::HtmlResource, resolveUrl(url));
+ if (data.type() == QVariant::String) {
+ txt = data.toString();
+ } else if (data.type() == QVariant::ByteArray) {
+#ifndef QT_NO_TEXTCODEC
+ QByteArray ba = data.toByteArray();
+ QTextCodec *codec = Qt::codecForHtml(ba);
+ txt = codec->toUnicode(ba);
+#else
+ txt = data.toString();
+#endif
+ }
+ if (txt.isEmpty())
+ qWarning("QTextBrowser: No document for %s", url.toString().toLatin1().constData());
+
+ if (q->isVisible()) {
+ QString firstTag = txt.left(txt.indexOf(QLatin1Char('>')) + 1);
+ if (firstTag.left(3) == QLatin1String("<qt") && firstTag.contains(QLatin1String("type")) && firstTag.contains(QLatin1String("detail"))) {
+#ifndef QT_NO_CURSOR
+ qApp->restoreOverrideCursor();
+#endif
+#ifndef QT_NO_WHATSTHIS
+ QWhatsThis::showText(QCursor::pos(), txt, q);
+#endif
+ return;
+ }
+ }
+
+ currentURL = resolveUrl(url);
+ doSetText = true;
+ }
+
+ if (!home.isValid())
+ home = url;
+
+ if (doSetText) {
+#ifndef QT_NO_TEXTHTMLPARSER
+ q->QTextEdit::setHtml(txt);
+ q->document()->setMetaInformation(QTextDocument::DocumentUrl, currentURL.toString());
+#else
+ q->QTextEdit::setPlainText(txt);
+#endif
+
+#ifdef QT_KEYPAD_NAVIGATION
+ prevFocus.movePosition(QTextCursor::Start);
+#endif
+ }
+
+ forceLoadOnSourceChange = false;
+
+ if (!url.fragment().isEmpty()) {
+ q->scrollToAnchor(url.fragment());
+ } else {
+ hbar->setValue(0);
+ vbar->setValue(0);
+ }
+#ifdef QT_KEYPAD_NAVIGATION
+ lastKeypadScrollValue = vbar->value();
+ emit q->highlighted(QUrl());
+ emit q->highlighted(QString());
+#endif
+
+#ifndef QT_NO_CURSOR
+ if (q->isVisible())
+ qApp->restoreOverrideCursor();
+#endif
+ emit q->sourceChanged(url);
+}
+
+#ifdef QT_KEYPAD_NAVIGATION
+void QTextBrowserPrivate::keypadMove(bool next)
+{
+ Q_Q(QTextBrowser);
+
+ const int height = viewport->height();
+ const int overlap = qBound(20, height / 5, 40); // XXX arbitrary, but a good balance
+ const int visibleLinkAmount = overlap; // consistent, but maybe not the best choice (?)
+ int yOffset = vbar->value();
+ int scrollYOffset = qBound(0, next ? yOffset + height - overlap : yOffset - height + overlap, vbar->maximum());
+
+ bool foundNextAnchor = false;
+ bool focusIt = false;
+ int focusedPos = -1;
+
+ QTextCursor anchorToFocus;
+
+ QRectF viewRect = QRectF(0, yOffset, control->size().width(), height);
+ QRectF newViewRect = QRectF(0, scrollYOffset, control->size().width(), height);
+ QRectF bothViewRects = viewRect.united(newViewRect);
+
+ // If we don't have a previous anchor, pretend that we had the first/last character
+ // on the screen selected.
+ if (prevFocus.isNull()) {
+ if (next)
+ prevFocus = control->cursorForPosition(QPointF(0, yOffset));
+ else
+ prevFocus = control->cursorForPosition(QPointF(control->size().width(), yOffset + height));
+ }
+
+ // First, check to see if someone has moved the scroll bars independently
+ if (lastKeypadScrollValue != yOffset) {
+ // Someone (user or programmatically) has moved us, so we might
+ // need to start looking from the current position instead of prevFocus
+
+ bool findOnScreen = true;
+
+ // If prevFocus is on screen at all, we just use it.
+ if (prevFocus.hasSelection()) {
+ QRectF prevRect = control->selectionRect(prevFocus);
+ if (viewRect.intersects(prevRect))
+ findOnScreen = false;
+ }
+
+ // Otherwise, we find a new anchor that's on screen.
+ // Basically, create a cursor with the last/first character
+ // on screen
+ if (findOnScreen) {
+ if (next)
+ prevFocus = control->cursorForPosition(QPointF(0, yOffset));
+ else
+ prevFocus = control->cursorForPosition(QPointF(control->size().width(), yOffset + height));
+ }
+ foundNextAnchor = control->findNextPrevAnchor(prevFocus, next, anchorToFocus);
+ } else if (prevFocus.hasSelection()) {
+ // Check the pathological case that the current anchor is higher
+ // than the screen, and just scroll through it in that case
+ QRectF prevRect = control->selectionRect(prevFocus);
+ if ((next && prevRect.bottom() > (yOffset + height)) ||
+ (!next && prevRect.top() < yOffset)) {
+ anchorToFocus = prevFocus;
+ focusedPos = scrollYOffset;
+ focusIt = true;
+ } else {
+ // This is the "normal" case - no scroll bar adjustments, no large anchors,
+ // and no wrapping.
+ foundNextAnchor = control->findNextPrevAnchor(prevFocus, next, anchorToFocus);
+ }
+ }
+
+ // If not found yet, see if we need to wrap
+ if (!focusIt && !foundNextAnchor) {
+ if (next) {
+ if (yOffset == vbar->maximum()) {
+ prevFocus.movePosition(QTextCursor::Start);
+ yOffset = scrollYOffset = 0;
+
+ // Refresh the rectangles
+ viewRect = QRectF(0, yOffset, control->size().width(), height);
+ newViewRect = QRectF(0, scrollYOffset, control->size().width(), height);
+ bothViewRects = viewRect.united(newViewRect);
+ }
+ } else {
+ if (yOffset == 0) {
+ prevFocus.movePosition(QTextCursor::End);
+ yOffset = scrollYOffset = vbar->maximum();
+
+ // Refresh the rectangles
+ viewRect = QRectF(0, yOffset, control->size().width(), height);
+ newViewRect = QRectF(0, scrollYOffset, control->size().width(), height);
+ bothViewRects = viewRect.united(newViewRect);
+ }
+ }
+
+ // Try looking now
+ foundNextAnchor = control->findNextPrevAnchor(prevFocus, next, anchorToFocus);
+ }
+
+ // If we did actually find an anchor to use...
+ if (foundNextAnchor) {
+ QRectF desiredRect = control->selectionRect(anchorToFocus);
+
+ // XXX This is an arbitrary heuristic
+ // Decide to focus an anchor if it will be at least be
+ // in the middle region of the screen after a scroll.
+ // This can result in partial anchors with focus, but
+ // insisting on links being completely visible before
+ // selecting them causes disparities between links that
+ // take up 90% of the screen height and those that take
+ // up e.g. 110%
+ // Obviously if a link is entirely visible, we still
+ // focus it.
+ if(bothViewRects.contains(desiredRect)
+ || bothViewRects.adjusted(0, visibleLinkAmount, 0, -visibleLinkAmount).intersects(desiredRect)) {
+ focusIt = true;
+
+ // We aim to put the new link in the middle of the screen,
+ // unless the link is larger than the screen (we just move to
+ // display the first page of the link)
+ if (desiredRect.height() > height) {
+ if (next)
+ focusedPos = (int) desiredRect.top();
+ else
+ focusedPos = (int) desiredRect.bottom() - height;
+ } else
+ focusedPos = (int) ((desiredRect.top() + desiredRect.bottom()) / 2 - (height / 2));
+
+ // and clamp it to make sure we don't skip content.
+ if (next)
+ focusedPos = qBound(yOffset, focusedPos, scrollYOffset);
+ else
+ focusedPos = qBound(scrollYOffset, focusedPos, yOffset);
+ }
+ }
+
+ // If we didn't get a new anchor, check if the old one is still on screen when we scroll
+ // Note that big (larger than screen height) anchors also have some handling at the
+ // start of this function.
+ if (!focusIt && prevFocus.hasSelection()) {
+ QRectF desiredRect = control->selectionRect(prevFocus);
+ // XXX this may be better off also using the visibleLinkAmount value
+ if(newViewRect.intersects(desiredRect)) {
+ focusedPos = scrollYOffset;
+ focusIt = true;
+ anchorToFocus = prevFocus;
+ }
+ }
+
+ // setTextCursor ensures that the cursor is visible. save & restore
+ // the scroll bar values therefore
+ const int savedXOffset = hbar->value();
+
+ // Now actually process our decision
+ if (focusIt && control->setFocusToAnchor(anchorToFocus)) {
+ // Save the focus for next time
+ prevFocus = control->textCursor();
+
+ // Scroll
+ vbar->setValue(focusedPos);
+ lastKeypadScrollValue = focusedPos;
+ hbar->setValue(savedXOffset);
+
+ // Ensure that the new selection is highlighted.
+ const QString href = control->anchorAtCursor();
+ QUrl url = resolveUrl(href);
+ emit q->highlighted(url);
+ emit q->highlighted(url.toString());
+ } else {
+ // Scroll
+ vbar->setValue(scrollYOffset);
+ lastKeypadScrollValue = scrollYOffset;
+
+ // now make sure we don't have a focused anchor
+ QTextCursor cursor = control->textCursor();
+ cursor.clearSelection();
+
+ control->setTextCursor(cursor);
+
+ hbar->setValue(savedXOffset);
+ vbar->setValue(scrollYOffset);
+
+ emit q->highlighted(QUrl());
+ emit q->highlighted(QString());
+ }
+}
+#endif
+
+QTextBrowserPrivate::HistoryEntry QTextBrowserPrivate::createHistoryEntry() const
+{
+ HistoryEntry entry;
+ entry.url = q_func()->source();
+ entry.title = q_func()->documentTitle();
+ entry.hpos = hbar->value();
+ entry.vpos = vbar->value();
+
+ const QTextCursor cursor = control->textCursor();
+ if (control->cursorIsFocusIndicator()
+ && cursor.hasSelection()) {
+
+ entry.focusIndicatorPosition = cursor.position();
+ entry.focusIndicatorAnchor = cursor.anchor();
+ }
+ return entry;
+}
+
+void QTextBrowserPrivate::restoreHistoryEntry(const HistoryEntry entry)
+{
+ setSource(entry.url);
+ hbar->setValue(entry.hpos);
+ vbar->setValue(entry.vpos);
+ if (entry.focusIndicatorAnchor != -1 && entry.focusIndicatorPosition != -1) {
+ QTextCursor cursor(control->document());
+ cursor.setPosition(entry.focusIndicatorAnchor);
+ cursor.setPosition(entry.focusIndicatorPosition, QTextCursor::KeepAnchor);
+ control->setTextCursor(cursor);
+ control->setCursorIsFocusIndicator(true);
+ }
+#ifdef QT_KEYPAD_NAVIGATION
+ lastKeypadScrollValue = vbar->value();
+ prevFocus = control->textCursor();
+
+ Q_Q(QTextBrowser);
+ const QString href = prevFocus.charFormat().anchorHref();
+ QUrl url = resolveUrl(href);
+ emit q->highlighted(url);
+ emit q->highlighted(url.toString());
+#endif
+}
+
+/*!
+ \class QTextBrowser
+ \brief The QTextBrowser class provides a rich text browser with hypertext navigation.
+
+ \ingroup text
+
+ This class extends QTextEdit (in read-only mode), adding some navigation
+ functionality so that users can follow links in hypertext documents.
+
+ If you want to provide your users with an editable rich text editor,
+ use QTextEdit. If you want a text browser without hypertext navigation
+ use QTextEdit, and use QTextEdit::setReadOnly() to disable
+ editing. If you just need to display a small piece of rich text
+ use QLabel.
+
+ \section1 Document Source and Contents
+
+ The contents of QTextEdit are set with setHtml() or setPlainText(),
+ but QTextBrowser also implements the setSource() function, making it
+ possible to use a named document as the source text. The name is looked
+ up in a list of search paths and in the directory of the current document
+ 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 connecting
+ to the sourceChanged() signal.
+
+ \section1 Navigation
+
+ QTextBrowser 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 anchorClicked()
+ signal is emitted when the user clicks an anchor. To override the
+ default navigation behavior of the browser, call the setSource()
+ function to supply new document text in a slot connected to this
+ signal.
+
+ If you want to load documents stored in the Qt resource system use
+ \c{qrc} as the scheme in the URL to load. For example, for the document
+ resource path \c{:/docs/index.html} use \c{qrc:/docs/index.html} as
+ the URL with setSource().
+
+ \sa QTextEdit, QTextDocument
+*/
+
+/*!
+ \property QTextBrowser::modified
+ \brief whether the contents of the text browser have been modified
+*/
+
+/*!
+ \property QTextBrowser::readOnly
+ \brief whether the text browser is read-only
+
+ By default, this property is true.
+*/
+
+/*!
+ \property QTextBrowser::undoRedoEnabled
+ \brief whether the text browser supports undo/redo operations
+
+ By default, this property is false.
+*/
+
+void QTextBrowserPrivate::init()
+{
+ Q_Q(QTextBrowser);
+ control->setTextInteractionFlags(Qt::TextBrowserInteraction);
+#ifndef QT_NO_CURSOR
+ viewport->setCursor(oldCursor);
+#endif
+ q->setUndoRedoEnabled(false);
+ viewport->setMouseTracking(true);
+ QObject::connect(q->document(), SIGNAL(contentsChanged()), q, SLOT(_q_documentModified()));
+ QObject::connect(control, SIGNAL(linkActivated(QString)),
+ q, SLOT(_q_activateAnchor(QString)));
+ QObject::connect(control, SIGNAL(linkHovered(QString)),
+ q, SLOT(_q_highlightLink(QString)));
+}
+
+/*!
+ Constructs an empty QTextBrowser with parent \a parent.
+*/
+QTextBrowser::QTextBrowser(QWidget *parent)
+ : QTextEdit(*new QTextBrowserPrivate, parent)
+{
+ Q_D(QTextBrowser);
+ d->init();
+}
+
+#ifdef QT3_SUPPORT
+/*!
+ Use one of the constructors that doesn't take the \a name
+ argument and then use setObjectName() instead.
+*/
+QTextBrowser::QTextBrowser(QWidget *parent, const char *name)
+ : QTextEdit(*new QTextBrowserPrivate, parent)
+{
+ setObjectName(QString::fromAscii(name));
+ Q_D(QTextBrowser);
+ d->init();
+}
+#endif
+
+/*!
+ \internal
+*/
+QTextBrowser::~QTextBrowser()
+{
+}
+
+/*!
+ \property QTextBrowser::source
+ \brief the name of the displayed document.
+
+ This is a an invalid url if no document is displayed or if the
+ source is unknown.
+
+ When setting this property QTextBrowser tries to find a document
+ with the specified name in the paths of the searchPaths property
+ and directory of the current source, unless the value is an absolute
+ file path. 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 setHtml().
+
+ By default, this property contains an empty URL.
+*/
+QUrl QTextBrowser::source() const
+{
+ Q_D(const QTextBrowser);
+ if (d->stack.isEmpty())
+ return QUrl();
+ else
+ return d->stack.top().url;
+}
+
+/*!
+ \property QTextBrowser::searchPaths
+ \brief the search paths used by the text browser to find supporting
+ content
+
+ QTextBrowser uses this list to locate images and documents.
+
+ By default, this property contains an empty string list.
+*/
+
+QStringList QTextBrowser::searchPaths() const
+{
+ Q_D(const QTextBrowser);
+ return d->searchPaths;
+}
+
+void QTextBrowser::setSearchPaths(const QStringList &paths)
+{
+ Q_D(QTextBrowser);
+ d->searchPaths = paths;
+}
+
+/*!
+ Reloads the current set source.
+*/
+void QTextBrowser::reload()
+{
+ Q_D(QTextBrowser);
+ QUrl s = d->currentURL;
+ d->currentURL = QUrl();
+ setSource(s);
+}
+
+void QTextBrowser::setSource(const QUrl &url)
+{
+ Q_D(QTextBrowser);
+
+ const QTextBrowserPrivate::HistoryEntry historyEntry = d->createHistoryEntry();
+
+ d->setSource(url);
+
+ if (!url.isValid())
+ return;
+
+ // the same url you are already watching?
+ if (!d->stack.isEmpty() && d->stack.top().url == url)
+ return;
+
+ if (!d->stack.isEmpty())
+ d->stack.top() = historyEntry;
+
+ QTextBrowserPrivate::HistoryEntry entry;
+ entry.url = url;
+ entry.title = documentTitle();
+ entry.hpos = 0;
+ entry.vpos = 0;
+ d->stack.push(entry);
+
+ emit backwardAvailable(d->stack.count() > 1);
+
+ if (!d->forwardStack.isEmpty() && d->forwardStack.top().url == url) {
+ d->forwardStack.pop();
+ emit forwardAvailable(d->forwardStack.count() > 0);
+ } else {
+ d->forwardStack.clear();
+ emit forwardAvailable(false);
+ }
+
+ emit historyChanged();
+}
+
+/*!
+ \fn void QTextBrowser::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 QTextBrowser::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 QTextBrowser::historyChanged()
+ \since 4.4
+
+ This signal is emitted when the history changes.
+
+ \sa historyTitle(), historyUrl()
+*/
+
+/*!
+ \fn void QTextBrowser::sourceChanged(const QUrl &src)
+
+ This signal is emitted when the 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 QTextBrowser::highlighted(const QUrl &link)
+
+ This signal is emitted when the user has selected but not
+ activated an anchor in the document. The URL referred to by the
+ anchor is passed in \a link.
+*/
+
+/*! \fn void QTextBrowser::highlighted(const QString &link)
+ \overload
+
+ Convenience signal that allows connecting to a slot
+ that takes just a QString, like for example QStatusBar's
+ message().
+*/
+
+
+/*!
+ \fn void QTextBrowser::anchorClicked(const QUrl &link)
+
+ This signal is emitted when the user clicks an anchor. The
+ URL referred to by the anchor is passed in \a link.
+
+ Note that the browser will automatically handle navigation to the
+ location specified by \a link unless the openLinks property
+ is set to false or you call setSource() in a slot connected.
+ This mechanism is used to override the default navigation features of the browser.
+*/
+
+/*!
+ 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 QTextBrowser::backward()
+{
+ Q_D(QTextBrowser);
+ if (d->stack.count() <= 1)
+ return;
+
+ // Update the history entry
+ d->forwardStack.push(d->createHistoryEntry());
+ d->stack.pop(); // throw away the old version of the current entry
+ d->restoreHistoryEntry(d->stack.top()); // previous entry
+ emit backwardAvailable(d->stack.count() > 1);
+ emit forwardAvailable(true);
+ emit historyChanged();
+}
+
+/*!
+ 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 QTextBrowser::forward()
+{
+ Q_D(QTextBrowser);
+ if (d->forwardStack.isEmpty())
+ return;
+ if (!d->stack.isEmpty()) {
+ // Update the history entry
+ d->stack.top() = d->createHistoryEntry();
+ }
+ d->stack.push(d->forwardStack.pop());
+ d->restoreHistoryEntry(d->stack.top());
+ emit backwardAvailable(true);
+ emit forwardAvailable(!d->forwardStack.isEmpty());
+ emit historyChanged();
+}
+
+/*!
+ Changes the document displayed to be the first document from
+ the history.
+*/
+void QTextBrowser::home()
+{
+ Q_D(QTextBrowser);
+ if (d->home.isValid())
+ setSource(d->home);
+}
+
+/*!
+ The event \a ev 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 QTextBrowser::keyPressEvent(QKeyEvent *ev)
+{
+#ifdef QT_KEYPAD_NAVIGATION
+ Q_D(QTextBrowser);
+ switch (ev->key()) {
+ case Qt::Key_Select:
+ if (QApplication::keypadNavigationEnabled()) {
+ if (!hasEditFocus()) {
+ setEditFocus(true);
+ return;
+ } else {
+ QTextCursor cursor = d->control->textCursor();
+ QTextCharFormat charFmt = cursor.charFormat();
+ if (!cursor.hasSelection() || charFmt.anchorHref().isEmpty()) {
+ ev->accept();
+ return;
+ }
+ }
+ }
+ break;
+ case Qt::Key_Back:
+ if (QApplication::keypadNavigationEnabled()) {
+ if (hasEditFocus()) {
+ setEditFocus(false);
+ ev->accept();
+ return;
+ }
+ }
+ QTextEdit::keyPressEvent(ev);
+ return;
+ default:
+ if (QApplication::keypadNavigationEnabled() && !hasEditFocus()) {
+ ev->ignore();
+ return;
+ }
+ }
+#endif
+
+ if (ev->modifiers() & Qt::AltModifier) {
+ switch (ev->key()) {
+ case Qt::Key_Right:
+ forward();
+ ev->accept();
+ return;
+ case Qt::Key_Left:
+ backward();
+ ev->accept();
+ return;
+ case Qt::Key_Up:
+ home();
+ ev->accept();
+ return;
+ }
+ }
+#ifdef QT_KEYPAD_NAVIGATION
+ else {
+ if (ev->key() == Qt::Key_Up) {
+ d->keypadMove(false);
+ return;
+ } else if (ev->key() == Qt::Key_Down) {
+ d->keypadMove(true);
+ return;
+ }
+ }
+#endif
+ QTextEdit::keyPressEvent(ev);
+}
+
+/*!
+ \reimp
+*/
+void QTextBrowser::mouseMoveEvent(QMouseEvent *e)
+{
+ QTextEdit::mouseMoveEvent(e);
+}
+
+/*!
+ \reimp
+*/
+void QTextBrowser::mousePressEvent(QMouseEvent *e)
+{
+ QTextEdit::mousePressEvent(e);
+}
+
+/*!
+ \reimp
+*/
+void QTextBrowser::mouseReleaseEvent(QMouseEvent *e)
+{
+ QTextEdit::mouseReleaseEvent(e);
+}
+
+/*!
+ \reimp
+*/
+void QTextBrowser::focusOutEvent(QFocusEvent *ev)
+{
+#ifndef QT_NO_CURSOR
+ Q_D(QTextBrowser);
+ d->viewport->setCursor((!(d->control->textInteractionFlags() & Qt::TextEditable)) ? d->oldCursor : Qt::IBeamCursor);
+#endif
+ QTextEdit::focusOutEvent(ev);
+}
+
+/*!
+ \reimp
+*/
+bool QTextBrowser::focusNextPrevChild(bool next)
+{
+ Q_D(QTextBrowser);
+ if (d->control->setFocusToNextOrPreviousAnchor(next)) {
+#ifdef QT_KEYPAD_NAVIGATION
+ // Might need to synthesize a highlight event.
+ if (d->prevFocus != d->control->textCursor() && d->control->textCursor().hasSelection()) {
+ const QString href = d->control->anchorAtCursor();
+ QUrl url = d->resolveUrl(href);
+ emit highlighted(url);
+ emit highlighted(url.toString());
+ }
+ d->prevFocus = d->control->textCursor();
+#endif
+ return true;
+ } else {
+#ifdef QT_KEYPAD_NAVIGATION
+ // We assume we have no highlight now.
+ emit highlighted(QUrl());
+ emit highlighted(QString());
+#endif
+ }
+ return QTextEdit::focusNextPrevChild(next);
+}
+
+/*!
+ \reimp
+*/
+void QTextBrowser::paintEvent(QPaintEvent *e)
+{
+ Q_D(QTextBrowser);
+ QPainter p(d->viewport);
+ d->paint(&p, e);
+}
+
+/*!
+ This function is called when the document is loaded and for
+ each image in the document. The \a type indicates the type of resource
+ to be loaded. An invalid QVariant is returned if the resource cannot be
+ loaded.
+
+ The default implementation ignores \a type and tries to locate
+ the resources by interpreting \a name as a file name. If it is
+ not an absolute path it tries to find the file in the paths of
+ the \l searchPaths property and in the same directory as the
+ current source. On success, the result is a QVariant that stores
+ a QByteArray with the contents of the file.
+
+ If you reimplement this function, you can return other QVariant
+ types. The table below shows which variant types are supported
+ depending on the resource type:
+
+ \table
+ \header \i ResourceType \i QVariant::Type
+ \row \i QTextDocument::HtmlResource \i QString or QByteArray
+ \row \i QTextDocument::ImageResource \i QImage, QPixmap or QByteArray
+ \row \i QTextDocument::StyleSheetResource \i QString or QByteArray
+ \endtable
+*/
+QVariant QTextBrowser::loadResource(int /*type*/, const QUrl &name)
+{
+ Q_D(QTextBrowser);
+
+ QByteArray data;
+ QString fileName = d->findFile(d->resolveUrl(name));
+ QFile f(fileName);
+ if (f.open(QFile::ReadOnly)) {
+ data = f.readAll();
+ f.close();
+ } else {
+ return QVariant();
+ }
+
+ return data;
+}
+
+/*!
+ \since 4.2
+
+ Returns true if the text browser can go backward in the document history
+ using backward().
+
+ \sa backwardAvailable(), backward()
+*/
+bool QTextBrowser::isBackwardAvailable() const
+{
+ Q_D(const QTextBrowser);
+ return d->stack.count() > 1;
+}
+
+/*!
+ \since 4.2
+
+ Returns true if the text browser can go forward in the document history
+ using forward().
+
+ \sa forwardAvailable(), forward()
+*/
+bool QTextBrowser::isForwardAvailable() const
+{
+ Q_D(const QTextBrowser);
+ return !d->forwardStack.isEmpty();
+}
+
+/*!
+ \since 4.2
+
+ Clears the history of visited documents and disables the forward and
+ backward navigation.
+
+ \sa backward(), forward()
+*/
+void QTextBrowser::clearHistory()
+{
+ Q_D(QTextBrowser);
+ d->forwardStack.clear();
+ if (!d->stack.isEmpty()) {
+ QTextBrowserPrivate::HistoryEntry historyEntry = d->stack.top();
+ d->stack.resize(0);
+ d->stack.push(historyEntry);
+ d->home = historyEntry.url;
+ }
+ emit forwardAvailable(false);
+ emit backwardAvailable(false);
+ emit historyChanged();
+}
+
+/*!
+ Returns the url of the HistoryItem.
+
+ \table
+ \header \i Input \i Return
+ \row \i \a{i} < 0 \i \l backward() history
+ \row \i\a{i} == 0 \i current, see QTextBrowser::source()
+ \row \i \a{i} > 0 \i \l forward() history
+ \endtable
+
+ \since 4.4
+*/
+QUrl QTextBrowser::historyUrl(int i) const
+{
+ Q_D(const QTextBrowser);
+ return d->history(i).url;
+}
+
+/*!
+ Returns the documentTitle() of the HistoryItem.
+
+ \table
+ \header \i Input \i Return
+ \row \i \a{i} < 0 \i \l backward() history
+ \row \i \a{i} == 0 \i current, see QTextBrowser::source()
+ \row \i \a{i} > 0 \i \l forward() history
+ \endtable
+
+ \snippet doc/src/snippets/code/src_gui_widgets_qtextbrowser.cpp 0
+
+ \since 4.4
+*/
+QString QTextBrowser::historyTitle(int i) const
+{
+ Q_D(const QTextBrowser);
+ return d->history(i).title;
+}
+
+
+/*!
+ Returns the number of locations forward in the history.
+
+ \since 4.4
+*/
+int QTextBrowser::forwardHistoryCount() const
+{
+ Q_D(const QTextBrowser);
+ return d->forwardStack.count();
+}
+
+/*!
+ Returns the number of locations backward in the history.
+
+ \since 4.4
+*/
+int QTextBrowser::backwardHistoryCount() const
+{
+ Q_D(const QTextBrowser);
+ return d->stack.count()-1;
+}
+
+/*!
+ \property QTextBrowser::openExternalLinks
+ \since 4.2
+
+ Specifies whether QTextBrowser should automatically open links to external
+ sources using QDesktopServices::openUrl() instead of emitting the
+ anchorClicked signal. Links are considered external if their scheme is
+ neither file or qrc.
+
+ The default value is false.
+*/
+bool QTextBrowser::openExternalLinks() const
+{
+ Q_D(const QTextBrowser);
+ return d->openExternalLinks;
+}
+
+void QTextBrowser::setOpenExternalLinks(bool open)
+{
+ Q_D(QTextBrowser);
+ d->openExternalLinks = open;
+}
+
+/*!
+ \property QTextBrowser::openLinks
+ \since 4.3
+
+ This property specifies whether QTextBrowser should automatically open links the user tries to
+ activate by mouse or keyboard.
+
+ Regardless of the value of this property the anchorClicked signal is always emitted.
+
+ The default value is true.
+*/
+
+bool QTextBrowser::openLinks() const
+{
+ Q_D(const QTextBrowser);
+ return d->openLinks;
+}
+
+void QTextBrowser::setOpenLinks(bool open)
+{
+ Q_D(QTextBrowser);
+ d->openLinks = open;
+}
+
+/*! \reimp */
+bool QTextBrowser::event(QEvent *e)
+{
+ return QTextEdit::event(e);
+}
+
+QT_END_NAMESPACE
+
+#include "moc_qtextbrowser.cpp"
+
+#endif // QT_NO_TEXTBROWSER
diff --git a/src/gui/widgets/qtextbrowser.h b/src/gui/widgets/qtextbrowser.h
new file mode 100644
index 0000000000..1cad463356
--- /dev/null
+++ b/src/gui/widgets/qtextbrowser.h
@@ -0,0 +1,140 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QTEXTBROWSER_H
+#define QTEXTBROWSER_H
+
+#include <QtGui/qtextedit.h>
+#include <QtCore/qurl.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+#ifndef QT_NO_TEXTBROWSER
+
+class QTextBrowserPrivate;
+
+class Q_GUI_EXPORT QTextBrowser : public QTextEdit
+{
+ Q_OBJECT
+
+ Q_PROPERTY(QUrl source READ source WRITE setSource)
+ Q_OVERRIDE(bool modified SCRIPTABLE false)
+ Q_OVERRIDE(bool readOnly DESIGNABLE false SCRIPTABLE false)
+ Q_OVERRIDE(bool undoRedoEnabled DESIGNABLE false SCRIPTABLE false)
+ Q_PROPERTY(QStringList searchPaths READ searchPaths WRITE setSearchPaths)
+ Q_PROPERTY(bool openExternalLinks READ openExternalLinks WRITE setOpenExternalLinks)
+ Q_PROPERTY(bool openLinks READ openLinks WRITE setOpenLinks)
+
+public:
+ explicit QTextBrowser(QWidget* parent = 0);
+ virtual ~QTextBrowser();
+
+ QUrl source() const;
+
+ QStringList searchPaths() const;
+ void setSearchPaths(const QStringList &paths);
+
+ virtual QVariant loadResource(int type, const QUrl &name);
+
+ bool isBackwardAvailable() const;
+ bool isForwardAvailable() const;
+ void clearHistory();
+ QString historyTitle(int) const;
+ QUrl historyUrl(int) const;
+ int backwardHistoryCount() const;
+ int forwardHistoryCount() const;
+
+ bool openExternalLinks() const;
+ void setOpenExternalLinks(bool open);
+
+ bool openLinks() const;
+ void setOpenLinks(bool open);
+
+public Q_SLOTS:
+ virtual void setSource(const QUrl &name);
+ virtual void backward();
+ virtual void forward();
+ virtual void home();
+ virtual void reload();
+
+Q_SIGNALS:
+ void backwardAvailable(bool);
+ void forwardAvailable(bool);
+ void historyChanged();
+ void sourceChanged(const QUrl &);
+ void highlighted(const QUrl &);
+ void highlighted(const QString &);
+ void anchorClicked(const QUrl &);
+
+protected:
+ bool event(QEvent *e);
+ virtual void keyPressEvent(QKeyEvent *ev);
+ virtual void mouseMoveEvent(QMouseEvent *ev);
+ virtual void mousePressEvent(QMouseEvent *ev);
+ virtual void mouseReleaseEvent(QMouseEvent *ev);
+ virtual void focusOutEvent(QFocusEvent *ev);
+ virtual bool focusNextPrevChild(bool next);
+ virtual void paintEvent(QPaintEvent *e);
+
+#if defined(QT3_SUPPORT)
+public:
+ QT3_SUPPORT_CONSTRUCTOR QTextBrowser(QWidget *parent, const char *name);
+#endif
+
+private:
+ Q_DISABLE_COPY(QTextBrowser)
+ Q_DECLARE_PRIVATE(QTextBrowser)
+ Q_PRIVATE_SLOT(d_func(), void _q_documentModified())
+ Q_PRIVATE_SLOT(d_func(), void _q_activateAnchor(const QString &))
+ Q_PRIVATE_SLOT(d_func(), void _q_highlightLink(const QString &))
+};
+
+#endif // QT_NO_TEXTBROWSER
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QTEXTBROWSER_H
diff --git a/src/gui/widgets/qtextedit.cpp b/src/gui/widgets/qtextedit.cpp
new file mode 100644
index 0000000000..b239e32fab
--- /dev/null
+++ b/src/gui/widgets/qtextedit.cpp
@@ -0,0 +1,2783 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qtextedit_p.h"
+#include "qlineedit.h"
+#include "qtextbrowser.h"
+
+#ifndef QT_NO_TEXTEDIT
+#include <qfont.h>
+#include <qpainter.h>
+#include <qevent.h>
+#include <qdebug.h>
+#include <qmime.h>
+#include <qdrag.h>
+#include <qclipboard.h>
+#include <qmenu.h>
+#include <qstyle.h>
+#include <qtimer.h>
+#include "private/qtextdocumentlayout_p.h"
+#include "qtextdocument.h"
+#include "private/qtextdocument_p.h"
+#include "qtextlist.h"
+#include "private/qtextcontrol_p.h"
+
+#include <qtextformat.h>
+#include <qdatetime.h>
+#include <qapplication.h>
+#include <limits.h>
+#include <qtexttable.h>
+#include <qvariant.h>
+
+#include <qinputcontext.h>
+#endif
+
+QT_BEGIN_NAMESPACE
+
+
+#ifndef QT_NO_TEXTEDIT
+
+class QTextEditControl : public QTextControl
+{
+public:
+ inline QTextEditControl(QObject *parent) : QTextControl(parent) {}
+
+ virtual QMimeData *createMimeDataFromSelection() const {
+ QTextEdit *ed = qobject_cast<QTextEdit *>(parent());
+ if (!ed)
+ return QTextControl::createMimeDataFromSelection();
+ return ed->createMimeDataFromSelection();
+ }
+ virtual bool canInsertFromMimeData(const QMimeData *source) const {
+ QTextEdit *ed = qobject_cast<QTextEdit *>(parent());
+ if (!ed)
+ return QTextControl::canInsertFromMimeData(source);
+ return ed->canInsertFromMimeData(source);
+ }
+ virtual void insertFromMimeData(const QMimeData *source) {
+ QTextEdit *ed = qobject_cast<QTextEdit *>(parent());
+ if (!ed)
+ QTextControl::insertFromMimeData(source);
+ else
+ ed->insertFromMimeData(source);
+ }
+};
+
+QTextEditPrivate::QTextEditPrivate()
+ : control(0),
+ autoFormatting(QTextEdit::AutoNone), tabChangesFocus(false),
+ lineWrap(QTextEdit::WidgetWidth), lineWrapColumnOrWidth(0),
+ wordWrap(QTextOption::WrapAtWordBoundaryOrAnywhere), textFormat(Qt::AutoText)
+{
+ ignoreAutomaticScrollbarAdjustment = false;
+ preferRichText = false;
+ showCursorOnInitialShow = true;
+ inDrag = false;
+}
+
+void QTextEditPrivate::createAutoBulletList()
+{
+ QTextCursor cursor = control->textCursor();
+ cursor.beginEditBlock();
+
+ QTextBlockFormat blockFmt = cursor.blockFormat();
+
+ QTextListFormat listFmt;
+ listFmt.setStyle(QTextListFormat::ListDisc);
+ listFmt.setIndent(blockFmt.indent() + 1);
+
+ blockFmt.setIndent(0);
+ cursor.setBlockFormat(blockFmt);
+
+ cursor.createList(listFmt);
+
+ cursor.endEditBlock();
+ control->setTextCursor(cursor);
+}
+
+void QTextEditPrivate::init(const QString &html)
+{
+ Q_Q(QTextEdit);
+ control = new QTextEditControl(q);
+ control->setPalette(q->palette());
+
+ QObject::connect(control, SIGNAL(microFocusChanged()), q, SLOT(updateMicroFocus()));
+ QObject::connect(control, SIGNAL(documentSizeChanged(QSizeF)), q, SLOT(_q_adjustScrollbars()));
+ QObject::connect(control, SIGNAL(updateRequest(QRectF)), q, SLOT(_q_repaintContents(QRectF)));
+ QObject::connect(control, SIGNAL(visibilityRequest(QRectF)), q, SLOT(_q_ensureVisible(QRectF)));
+ QObject::connect(control, SIGNAL(currentCharFormatChanged(QTextCharFormat)),
+ q, SLOT(_q_currentCharFormatChanged(QTextCharFormat)));
+
+ QObject::connect(control, SIGNAL(textChanged()), q, SIGNAL(textChanged()));
+ QObject::connect(control, SIGNAL(undoAvailable(bool)), q, SIGNAL(undoAvailable(bool)));
+ QObject::connect(control, SIGNAL(redoAvailable(bool)), q, SIGNAL(redoAvailable(bool)));
+ QObject::connect(control, SIGNAL(copyAvailable(bool)), q, SIGNAL(copyAvailable(bool)));
+ QObject::connect(control, SIGNAL(selectionChanged()), q, SIGNAL(selectionChanged()));
+ QObject::connect(control, SIGNAL(cursorPositionChanged()), q, SIGNAL(cursorPositionChanged()));
+
+ QTextDocument *doc = control->document();
+ // set a null page size initially to avoid any relayouting until the textedit
+ // is shown. relayoutDocument() will take care of setting the page size to the
+ // viewport dimensions later.
+ doc->setPageSize(QSize(0, 0));
+ doc->documentLayout()->setPaintDevice(viewport);
+ doc->setDefaultFont(q->font());
+ doc->setUndoRedoEnabled(false); // flush undo buffer.
+ doc->setUndoRedoEnabled(true);
+
+ if (!html.isEmpty())
+ control->setHtml(html);
+
+ hbar->setSingleStep(20);
+ vbar->setSingleStep(20);
+
+ viewport->setBackgroundRole(QPalette::Base);
+ q->setAcceptDrops(true);
+ q->setFocusPolicy(Qt::WheelFocus);
+ q->setAttribute(Qt::WA_KeyCompression);
+ q->setAttribute(Qt::WA_InputMethodEnabled);
+
+#ifndef QT_NO_CURSOR
+ viewport->setCursor(Qt::IBeamCursor);
+#endif
+}
+
+void QTextEditPrivate::_q_repaintContents(const QRectF &contentsRect)
+{
+ if (!contentsRect.isValid()) {
+ viewport->update();
+ return;
+ }
+ const int xOffset = horizontalOffset();
+ const int yOffset = verticalOffset();
+ const QRectF visibleRect(xOffset, yOffset, viewport->width(), viewport->height());
+
+ QRect r = contentsRect.intersected(visibleRect).toAlignedRect();
+ if (r.isEmpty())
+ return;
+
+ r.translate(-xOffset, -yOffset);
+ viewport->update(r);
+}
+
+void QTextEditPrivate::pageUpDown(QTextCursor::MoveOperation op, QTextCursor::MoveMode moveMode)
+{
+ QTextCursor cursor = control->textCursor();
+ bool moved = false;
+ qreal lastY = control->cursorRect(cursor).top();
+ qreal distance = 0;
+ // move using movePosition to keep the cursor's x
+ do {
+ qreal y = control->cursorRect(cursor).top();
+ distance += qAbs(y - lastY);
+ lastY = y;
+ moved = cursor.movePosition(op, moveMode);
+ } while (moved && distance < viewport->height());
+
+ if (moved) {
+ if (op == QTextCursor::Up) {
+ cursor.movePosition(QTextCursor::Down, moveMode);
+ vbar->triggerAction(QAbstractSlider::SliderPageStepSub);
+ } else {
+ cursor.movePosition(QTextCursor::Up, moveMode);
+ vbar->triggerAction(QAbstractSlider::SliderPageStepAdd);
+ }
+ }
+ control->setTextCursor(cursor);
+}
+
+#ifndef QT_NO_SCROLLBAR
+static QSize documentSize(QTextControl *control)
+{
+ QTextDocument *doc = control->document();
+ QAbstractTextDocumentLayout *layout = doc->documentLayout();
+
+ QSize docSize;
+
+ if (QTextDocumentLayout *tlayout = qobject_cast<QTextDocumentLayout *>(layout)) {
+ docSize = tlayout->dynamicDocumentSize().toSize();
+ int percentageDone = tlayout->layoutStatus();
+ // extrapolate height
+ if (percentageDone > 0)
+ docSize.setHeight(docSize.height() * 100 / percentageDone);
+ } else {
+ docSize = layout->documentSize().toSize();
+ }
+
+ return docSize;
+}
+
+void QTextEditPrivate::_q_adjustScrollbars()
+{
+ if (ignoreAutomaticScrollbarAdjustment)
+ return;
+ ignoreAutomaticScrollbarAdjustment = true; // avoid recursion, #106108
+
+ QSize viewportSize = viewport->size();
+ QSize docSize = documentSize(control);
+
+ // due to the recursion guard we have to repeat this step a few times,
+ // as adding/removing a scroll bar will cause the document or viewport
+ // size to change
+ // ideally we should loop until the viewport size and doc size stabilize,
+ // but in corner cases they might fluctuate, so we need to limit the
+ // number of iterations
+ for (int i = 0; i < 4; ++i) {
+ hbar->setRange(0, docSize.width() - viewportSize.width());
+ hbar->setPageStep(viewportSize.width());
+
+ vbar->setRange(0, docSize.height() - viewportSize.height());
+ vbar->setPageStep(viewportSize.height());
+
+ // if we are in left-to-right mode widening the document due to
+ // lazy layouting does not require a repaint. If in right-to-left
+ // the scroll bar has the value zero and it visually has the maximum
+ // value (it is visually at the right), then widening the document
+ // keeps it at value zero but visually adjusts it to the new maximum
+ // on the right, hence we need an update.
+ if (q_func()->isRightToLeft())
+ viewport->update();
+
+ _q_showOrHideScrollBars();
+
+ const QSize oldViewportSize = viewportSize;
+ const QSize oldDocSize = docSize;
+
+ // make sure the document is layouted if the viewport width changes
+ viewportSize = viewport->size();
+ if (viewportSize.width() != oldViewportSize.width())
+ relayoutDocument();
+
+ docSize = documentSize(control);
+ if (viewportSize == oldViewportSize && docSize == oldDocSize)
+ break;
+ }
+ ignoreAutomaticScrollbarAdjustment = false;
+}
+#endif
+
+// rect is in content coordinates
+void QTextEditPrivate::_q_ensureVisible(const QRectF &_rect)
+{
+ const QRect rect = _rect.toRect();
+ if ((vbar->isVisible() && vbar->maximum() < rect.bottom())
+ || (hbar->isVisible() && hbar->maximum() < rect.right()))
+ _q_adjustScrollbars();
+ const int visibleWidth = viewport->width();
+ const int visibleHeight = viewport->height();
+ const bool rtl = q_func()->isRightToLeft();
+
+ if (rect.x() < horizontalOffset()) {
+ if (rtl)
+ hbar->setValue(hbar->maximum() - rect.x());
+ else
+ hbar->setValue(rect.x());
+ } else if (rect.x() + rect.width() > horizontalOffset() + visibleWidth) {
+ if (rtl)
+ hbar->setValue(hbar->maximum() - (rect.x() + rect.width() - visibleWidth));
+ else
+ hbar->setValue(rect.x() + rect.width() - visibleWidth);
+ }
+
+ if (rect.y() < verticalOffset())
+ vbar->setValue(rect.y());
+ else if (rect.y() + rect.height() > verticalOffset() + visibleHeight)
+ vbar->setValue(rect.y() + rect.height() - visibleHeight);
+}
+
+/*!
+ \class QTextEdit
+ \brief The QTextEdit class provides a widget that is used to edit and display
+ both plain and rich text.
+
+ \ingroup text
+ \mainclass
+
+ \tableofcontents
+
+ \section1 Introduction and Concepts
+
+ QTextEdit 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.
+
+ QTextEdit 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
+ signifies a paragraph. A document consists of zero or more
+ paragraphs. 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.
+
+ QTextEdit can display images, 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).
+
+ If you just need to display a small piece of rich text use QLabel.
+
+ 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. If
+ you find the HTML support insufficient for your needs you may consider
+ the use of QtWebKit, which provides a full-featured web browser
+ widget.
+
+ The shape of the mouse cursor on a QTextEdit is Qt::IBeamCursor by default.
+ It can be changed through the viewport()'s cursor property.
+
+ \section1 Using QTextEdit as a Display Widget
+
+ QTextEdit can display a large HTML subset, including tables and
+ images.
+
+ The text is set or replaced using setHtml() which deletes any
+ existing text and replaces it with the text passed in the
+ setHtml() call. If you call setHtml() with legacy HTML, and then
+ call toHtml(), the text that is returned may have different markup,
+ but will render the same. The entire text can be deleted with clear().
+
+ Text itself can be inserted using the QTextCursor class or using the
+ convenience functions insertHtml(), insertPlainText(), append() or
+ paste(). QTextCursor is also able to insert complex objects like tables
+ or lists into the document, and it deals with creating selections
+ and applying changes to selected text.
+
+ By default the text edit wraps words at whitespace to fit within
+ the text edit widget. The setLineWrapMode() function is used to
+ specify the kind of line wrap you want, or \l NoWrap if you don't
+ want any wrapping. Call setLineWrapMode() to set a fixed pixel width
+ \l FixedPixelWidth, or character column (e.g. 80 column) \l
+ FixedColumnWidth with the pixels or columns specified with
+ setLineWrapColumnOrWidth(). If you use word wrap to the widget's width
+ \l WidgetWidth, you can specify whether to break on whitespace or
+ anywhere with setWordWrapMode().
+
+ The find() function can be used to find and select a given string
+ within the text.
+
+ If you want to limit the total number of paragraphs in a QTextEdit,
+ as it is for example open useful in a log viewer, then you can use
+ QTextDocument's maximumBlockCount property for that.
+
+ \section2 Read-only Key Bindings
+
+ When QTextEdit 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 Moves one line up.
+ \row \i Down \i Moves one line down.
+ \row \i Left \i Moves one character to the left.
+ \row \i Right \i Moves one character to the right.
+ \row \i PageUp \i Moves one (viewport) page up.
+ \row \i PageDown \i Moves one (viewport) page down.
+ \row \i Home \i Moves to the beginning of the text.
+ \row \i End \i Moves to the end of the text.
+ \row \i Alt+Wheel
+ \i Scrolls the page horizontally (the Wheel is the mouse wheel).
+ \row \i Ctrl+Wheel \i Zooms the text.
+ \row \i Ctrl+A \i Selects all 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.
+
+ \section1 Using QTextEdit as an Editor
+
+ All the information about using QTextEdit as a display widget also
+ applies here.
+
+ The current char format's attributes are set with setFontItalic(),
+ setFontWeight(), setFontUnderline(), setFontFamily(),
+ setFontPointSize(), setTextColor() and setCurrentFont(). The current
+ paragraph's alignment is set with setAlignment().
+
+ Selection of text is handled by the QTextCursor class, which provides
+ functionality for creating selections, retrieving the text contents or
+ deleting selections. You can retrieve the object that corresponds with
+ the user-visible cursor using the textCursor() method. If you want to set
+ a selection in QTextEdit just create one on a QTextCursor object and
+ then make that cursor the visible cursor using setTextCursor(). The selection
+ can be copied to the clipboard with copy(), or cut to the clipboard with
+ cut(). The entire text can be selected using selectAll().
+
+ When the cursor is moved and the underlying formatting attributes change,
+ the currentCharFormatChanged() signal is emitted to reflect the new attributes
+ at the new cursor position.
+
+ QTextEdit holds a QTextDocument object which can be retrieved using the
+ document() method. You can also set your own document object using setDocument().
+ QTextDocument emits a textChanged() signal if the text changes and it also
+ provides a isModified() function which will return true if the text has been
+ modified since it was either loaded or since the last call to setModified
+ with false as argument. In addition it provides methods for undo and redo.
+
+ \section2 Drag and Drop
+
+ QTextEdit also supports custom drag and drop behavior. By default,
+ QTextEdit will insert plain text, HTML and rich text when the user drops
+ data of these MIME types onto a document. Reimplement
+ canInsertFromMimeData() and insertFromMimeData() to add support for
+ additional MIME types.
+
+ For example, to allow the user to drag and drop an image onto a QTextEdit,
+ you could the implement these functions in the following way:
+
+ \snippet doc/src/snippets/textdocument-imagedrop/textedit.cpp 0
+
+ We add support for image MIME types by returning true. For all other
+ MIME types, we use the default implementation.
+
+ \snippet doc/src/snippets/textdocument-imagedrop/textedit.cpp 1
+
+ We unpack the image from the QVariant held by the MIME source and insert
+ it into the document as a resource.
+
+ \section2 Editing Key Bindings
+
+ The list of key bindings which are implemented for editing:
+ \table
+ \header \i Keypresses \i Action
+ \row \i Backspace \i Deletes the character to the left of the cursor.
+ \row \i Delete \i Deletes the character to the right of the cursor.
+ \row \i Ctrl+C \i Copy the selected text to the clipboard.
+ \row \i Ctrl+Insert \i Copy the selected text to the clipboard.
+ \row \i Ctrl+K \i Deletes to the end of the line.
+ \row \i Ctrl+V \i Pastes the clipboard text into text edit.
+ \row \i Shift+Insert \i Pastes the clipboard text into text edit.
+ \row \i Ctrl+X \i Deletes the selected text and copies it to the clipboard.
+ \row \i Shift+Delete \i Deletes the selected text and copies it to the clipboard.
+ \row \i Ctrl+Z \i Undoes the last operation.
+ \row \i Ctrl+Y \i Redoes the last operation.
+ \row \i Left \i Moves the cursor one character to the left.
+ \row \i Ctrl+Left \i Moves the cursor one word to the left.
+ \row \i Right \i Moves the cursor one character to the right.
+ \row \i Ctrl+Right \i Moves the cursor one word to the right.
+ \row \i Up \i Moves the cursor one line up.
+ \row \i Down \i Moves the cursor one line down.
+ \row \i PageUp \i Moves the cursor one page up.
+ \row \i PageDown \i Moves the cursor one page down.
+ \row \i Home \i Moves the cursor to the beginning of the line.
+ \row \i Ctrl+Home \i Moves the cursor to the beginning of the text.
+ \row \i End \i Moves the cursor to the end of the line.
+ \row \i Ctrl+End \i Moves the cursor to the end of the text.
+ \row \i Alt+Wheel \i Scrolls the page horizontally (the Wheel is the mouse wheel).
+ \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.
+
+ \sa QTextDocument, QTextCursor, {Application Example},
+ {Syntax Highlighter Example}, {Rich Text Processing}
+*/
+
+/*!
+ \property QTextEdit::plainText
+ \since 4.3
+
+ This property gets and sets the text editor's contents as plain
+ text. Previous contents are removed and undo/redo history is reset
+ when the property is set.
+
+ If the text edit has another content type, it will not be replaced
+ by plain text if you call toPlainText().
+
+ By default, for an editor with no contents, this property contains
+ an empty string.
+
+ \sa html
+*/
+
+/*!
+ \property QTextEdit::undoRedoEnabled
+ \brief whether undo and redo are enabled
+
+ Users are only able to undo or redo actions if this property is
+ true, and if there is an action that can be undone (or redone).
+*/
+
+/*!
+ \enum QTextEdit::LineWrapMode
+
+ \value NoWrap
+ \value WidgetWidth
+ \value FixedPixelWidth
+ \value FixedColumnWidth
+*/
+
+/*!
+ \enum QTextEdit::AutoFormattingFlag
+
+ \value AutoNone Don't do any automatic formatting.
+ \value AutoBulletList Automatically create bullet lists (e.g. when
+ the user enters an asterisk ('*') in the left most column, or
+ presses Enter in an existing list item.
+ \value AutoAll Apply all automatic formatting. Currently only
+ automatic bullet lists are supported.
+*/
+
+#ifdef QT3_SUPPORT
+/*!
+ \enum QTextEdit::CursorAction
+ \compat
+
+ \value MoveBackward
+ \value MoveForward
+ \value MoveWordBackward
+ \value MoveWordForward
+ \value MoveUp
+ \value MoveDown
+ \value MoveLineStart
+ \value MoveLineEnd
+ \value MoveHome
+ \value MoveEnd
+ \value MovePageUp
+ \value MovePageDown
+
+ \omitvalue MovePgUp
+ \omitvalue MovePgDown
+*/
+#endif
+
+/*!
+ Constructs an empty QTextEdit with parent \a
+ parent.
+*/
+QTextEdit::QTextEdit(QWidget *parent)
+ : QAbstractScrollArea(*new QTextEditPrivate, parent)
+{
+ Q_D(QTextEdit);
+ d->init();
+}
+
+/*!
+ \internal
+*/
+QTextEdit::QTextEdit(QTextEditPrivate &dd, QWidget *parent)
+ : QAbstractScrollArea(dd, parent)
+{
+ Q_D(QTextEdit);
+ d->init();
+}
+
+/*!
+ Constructs a QTextEdit with parent \a parent. The text edit will display
+ the text \a text. The text is interpreted as html.
+*/
+QTextEdit::QTextEdit(const QString &text, QWidget *parent)
+ : QAbstractScrollArea(*new QTextEditPrivate, parent)
+{
+ Q_D(QTextEdit);
+ d->init(text);
+}
+
+#ifdef QT3_SUPPORT
+/*!
+ Use one of the constructors that doesn't take the \a name
+ argument and then use setObjectName() instead.
+*/
+QTextEdit::QTextEdit(QWidget *parent, const char *name)
+ : QAbstractScrollArea(*new QTextEditPrivate, parent)
+{
+ Q_D(QTextEdit);
+ d->init();
+ setObjectName(QString::fromAscii(name));
+}
+#endif
+
+
+/*!
+ Destructor.
+*/
+QTextEdit::~QTextEdit()
+{
+}
+
+/*!
+ Returns the point size of the font of the current format.
+
+ \sa setFontFamily() setCurrentFont() setFontPointSize()
+*/
+qreal QTextEdit::fontPointSize() const
+{
+ Q_D(const QTextEdit);
+ return d->control->textCursor().charFormat().fontPointSize();
+}
+
+/*!
+ Returns the font family of the current format.
+
+ \sa setFontFamily() setCurrentFont() setFontPointSize()
+*/
+QString QTextEdit::fontFamily() const
+{
+ Q_D(const QTextEdit);
+ return d->control->textCursor().charFormat().fontFamily();
+}
+
+/*!
+ Returns the font weight of the current format.
+
+ \sa setFontWeight() setCurrentFont() setFontPointSize() QFont::Weight
+*/
+int QTextEdit::fontWeight() const
+{
+ Q_D(const QTextEdit);
+ return d->control->textCursor().charFormat().fontWeight();
+}
+
+/*!
+ Returns true if the font of the current format is underlined; otherwise returns
+ false.
+
+ \sa setFontUnderline()
+*/
+bool QTextEdit::fontUnderline() const
+{
+ Q_D(const QTextEdit);
+ return d->control->textCursor().charFormat().fontUnderline();
+}
+
+/*!
+ Returns true if the font of the current format is italic; otherwise returns
+ false.
+
+ \sa setFontItalic()
+*/
+bool QTextEdit::fontItalic() const
+{
+ Q_D(const QTextEdit);
+ return d->control->textCursor().charFormat().fontItalic();
+}
+
+/*!
+ Returns the text color of the current format.
+
+ \sa setTextColor()
+*/
+QColor QTextEdit::textColor() const
+{
+ Q_D(const QTextEdit);
+ return d->control->textCursor().charFormat().foreground().color();
+}
+
+/*!
+ \since 4.4
+
+ Returns the text background color of the current format.
+
+ \sa setTextBackgroundColor()
+*/
+QColor QTextEdit::textBackgroundColor() const
+{
+ Q_D(const QTextEdit);
+ return d->control->textCursor().charFormat().background().color();
+}
+
+/*!
+ Returns the font of the current format.
+
+ \sa setCurrentFont() setFontFamily() setFontPointSize()
+*/
+QFont QTextEdit::currentFont() const
+{
+ Q_D(const QTextEdit);
+ return d->control->textCursor().charFormat().font();
+}
+
+/*!
+ 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 QTextEdit::setAlignment(Qt::Alignment a)
+{
+ Q_D(QTextEdit);
+ QTextBlockFormat fmt;
+ fmt.setAlignment(a);
+ QTextCursor cursor = d->control->textCursor();
+ cursor.mergeBlockFormat(fmt);
+ d->control->setTextCursor(cursor);
+}
+
+/*!
+ Returns the alignment of the current paragraph.
+
+ \sa setAlignment()
+*/
+Qt::Alignment QTextEdit::alignment() const
+{
+ Q_D(const QTextEdit);
+ return d->control->textCursor().blockFormat().alignment();
+}
+
+/*!
+ Makes \a document the new document of the text editor.
+
+ \note The editor \e{does not take ownership of the document} unless it
+ is the document's parent object. The parent object of the provided document
+ remains the owner of the object.
+
+ If the current document is a child of the text editor, then it is deleted.
+
+ \sa document()
+*/
+void QTextEdit::setDocument(QTextDocument *document)
+{
+ Q_D(QTextEdit);
+ d->control->setDocument(document);
+ d->updateDefaultTextOption();
+ d->relayoutDocument();
+}
+
+/*!
+ Returns a pointer to the underlying document.
+
+ \sa setDocument()
+*/
+QTextDocument *QTextEdit::document() const
+{
+ Q_D(const QTextEdit);
+ return d->control->document();
+}
+
+/*!
+ Sets the visible \a cursor.
+*/
+void QTextEdit::setTextCursor(const QTextCursor &cursor)
+{
+ Q_D(QTextEdit);
+ d->control->setTextCursor(cursor);
+}
+
+/*!
+ Returns a copy of the QTextCursor that represents the currently visible cursor.
+ Note that changes on the returned cursor do not affect QTextEdit's cursor; use
+ setTextCursor() to update the visible cursor.
+ */
+QTextCursor QTextEdit::textCursor() const
+{
+ Q_D(const QTextEdit);
+ return d->control->textCursor();
+}
+
+/*!
+ Sets the font family of the current format to \a fontFamily.
+
+ \sa fontFamily() setCurrentFont()
+*/
+void QTextEdit::setFontFamily(const QString &fontFamily)
+{
+ QTextCharFormat fmt;
+ fmt.setFontFamily(fontFamily);
+ mergeCurrentCharFormat(fmt);
+}
+
+/*!
+ 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 fontPointSize() setCurrentFont() setFontFamily()
+*/
+void QTextEdit::setFontPointSize(qreal s)
+{
+ QTextCharFormat fmt;
+ fmt.setFontPointSize(s);
+ mergeCurrentCharFormat(fmt);
+}
+
+/*!
+ \fn void QTextEdit::setFontWeight(int weight)
+
+ Sets the font weight of the current format to the given \a weight,
+ where the value used is in the range defined by the QFont::Weight
+ enum.
+
+ \sa fontWeight(), setCurrentFont(), setFontFamily()
+*/
+void QTextEdit::setFontWeight(int w)
+{
+ QTextCharFormat fmt;
+ fmt.setFontWeight(w);
+ mergeCurrentCharFormat(fmt);
+}
+
+/*!
+ If \a underline is true, sets the current format to underline;
+ otherwise sets the current format to non-underline.
+
+ \sa fontUnderline()
+*/
+void QTextEdit::setFontUnderline(bool underline)
+{
+ QTextCharFormat fmt;
+ fmt.setFontUnderline(underline);
+ mergeCurrentCharFormat(fmt);
+}
+
+/*!
+ If \a italic is true, sets the current format to italic;
+ otherwise sets the current format to non-italic.
+
+ \sa fontItalic()
+*/
+void QTextEdit::setFontItalic(bool italic)
+{
+ QTextCharFormat fmt;
+ fmt.setFontItalic(italic);
+ mergeCurrentCharFormat(fmt);
+}
+
+/*!
+ Sets the text color of the current format to \a c.
+
+ \sa textColor()
+*/
+void QTextEdit::setTextColor(const QColor &c)
+{
+ QTextCharFormat fmt;
+ fmt.setForeground(QBrush(c));
+ mergeCurrentCharFormat(fmt);
+}
+
+/*!
+ \since 4.4
+
+ Sets the text background color of the current format to \a c.
+
+ \sa textBackgroundColor()
+*/
+void QTextEdit::setTextBackgroundColor(const QColor &c)
+{
+ QTextCharFormat fmt;
+ fmt.setBackground(QBrush(c));
+ mergeCurrentCharFormat(fmt);
+}
+
+/*!
+ Sets the font of the current format to \a f.
+
+ \sa currentFont() setFontPointSize() setFontFamily()
+*/
+void QTextEdit::setCurrentFont(const QFont &f)
+{
+ QTextCharFormat fmt;
+ fmt.setFont(f);
+ mergeCurrentCharFormat(fmt);
+}
+
+/*!
+ \since 4.2
+
+ 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 redo()
+*/
+void QTextEdit::undo()
+{
+ Q_D(QTextEdit);
+ d->control->undo();
+}
+
+void QTextEdit::redo()
+{
+ Q_D(QTextEdit);
+ d->control->redo();
+}
+
+/*!
+ \fn void QTextEdit::undo() const
+ \fn void QTextEdit::redo() const
+ \overload
+
+ Use the non-const overload instead.
+*/
+
+/*!
+ \fn void QTextEdit::redo()
+ \since 4.2
+
+ 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 undo()
+*/
+
+#ifndef QT_NO_CLIPBOARD
+/*!
+ Copies the selected text to the clipboard and deletes it from
+ the text edit.
+
+ If there is no selected text nothing happens.
+
+ \sa copy() paste()
+*/
+
+void QTextEdit::cut()
+{
+ Q_D(QTextEdit);
+ d->control->cut();
+}
+
+/*!
+ Copies any selected text to the clipboard.
+
+ \sa copyAvailable()
+*/
+
+void QTextEdit::copy()
+{
+ Q_D(QTextEdit);
+ d->control->copy();
+}
+
+/*!
+ Pastes the text from the clipboard into the text edit at the
+ current cursor position.
+
+ If there is no text in the clipboard nothing happens.
+
+ To change the behavior of this function, i.e. to modify what
+ QTextEdit can paste and how it is being pasted, reimplement the
+ virtual canInsertFromMimeData() and insertFromMimeData()
+ functions.
+
+ \sa cut() copy()
+*/
+
+void QTextEdit::paste()
+{
+ Q_D(QTextEdit);
+ d->control->paste();
+}
+#endif
+
+/*!
+ Deletes all the text in the text edit.
+
+ Note that the undo/redo history is cleared by this function.
+
+ \sa cut() setPlainText() setHtml()
+*/
+void QTextEdit::clear()
+{
+ Q_D(QTextEdit);
+ // clears and sets empty content
+ d->control->clear();
+}
+
+
+/*!
+ Selects all text.
+
+ \sa copy() cut() textCursor()
+ */
+void QTextEdit::selectAll()
+{
+ Q_D(QTextEdit);
+ d->control->selectAll();
+}
+
+/*! \internal
+*/
+bool QTextEdit::event(QEvent *e)
+{
+ Q_D(QTextEdit);
+#ifndef QT_NO_CONTEXTMENU
+ if (e->type() == QEvent::ContextMenu
+ && static_cast<QContextMenuEvent *>(e)->reason() == QContextMenuEvent::Keyboard) {
+ Q_D(QTextEdit);
+ ensureCursorVisible();
+ const QPoint cursorPos = cursorRect().center();
+ QContextMenuEvent ce(QContextMenuEvent::Keyboard, cursorPos, d->viewport->mapToGlobal(cursorPos));
+ ce.setAccepted(e->isAccepted());
+ const bool result = QAbstractScrollArea::event(&ce);
+ e->setAccepted(ce.isAccepted());
+ return result;
+ } else if (e->type() == QEvent::ShortcutOverride
+ || e->type() == QEvent::ToolTip) {
+ d->sendControlEvent(e);
+ }
+#endif // QT_NO_CONTEXTMENU
+#ifdef QT_KEYPAD_NAVIGATION
+ if (e->type() == QEvent::EnterEditFocus || e->type() == QEvent::LeaveEditFocus) {
+ if (QApplication::keypadNavigationEnabled())
+ d->sendControlEvent(e);
+ }
+#endif
+ return QAbstractScrollArea::event(e);
+}
+
+/*! \internal
+*/
+
+void QTextEdit::timerEvent(QTimerEvent *e)
+{
+ Q_D(QTextEdit);
+ if (e->timerId() == d->autoScrollTimer.timerId()) {
+ QRect visible = d->viewport->rect();
+ QPoint pos;
+ if (d->inDrag) {
+ pos = d->autoScrollDragPos;
+ visible.adjust(qMin(visible.width()/3,20), qMin(visible.height()/3,20),
+ -qMin(visible.width()/3,20), -qMin(visible.height()/3,20));
+ } else {
+ const QPoint globalPos = QCursor::pos();
+ pos = d->viewport->mapFromGlobal(globalPos);
+ QMouseEvent ev(QEvent::MouseMove, pos, globalPos, Qt::LeftButton, Qt::LeftButton, Qt::NoModifier);
+ mouseMoveEvent(&ev);
+ }
+ int deltaY = qMax(pos.y() - visible.top(), visible.bottom() - pos.y()) - visible.height();
+ int deltaX = qMax(pos.x() - visible.left(), visible.right() - pos.x()) - visible.width();
+ int delta = qMax(deltaX, deltaY);
+ if (delta >= 0) {
+ if (delta < 7)
+ delta = 7;
+ int timeout = 4900 / (delta * delta);
+ d->autoScrollTimer.start(timeout, this);
+
+ if (deltaY > 0)
+ d->vbar->triggerAction(pos.y() < visible.center().y() ?
+ QAbstractSlider::SliderSingleStepSub
+ : QAbstractSlider::SliderSingleStepAdd);
+ if (deltaX > 0)
+ d->hbar->triggerAction(pos.x() < visible.center().x() ?
+ QAbstractSlider::SliderSingleStepSub
+ : QAbstractSlider::SliderSingleStepAdd);
+ }
+ }
+#ifdef QT_KEYPAD_NAVIGATION
+ else if (e->timerId() == d->deleteAllTimer.timerId()) {
+ d->deleteAllTimer.stop();
+ clear();
+ }
+#endif
+}
+
+/*!
+ Changes the text of the text edit to the string \a text.
+ Any previous text is removed.
+
+ \a text is interpreted as plain text.
+
+ Note that the undo/redo history is cleared by this function.
+
+ \sa toPlainText()
+*/
+
+void QTextEdit::setPlainText(const QString &text)
+{
+ Q_D(QTextEdit);
+ d->control->setPlainText(text);
+ d->preferRichText = false;
+}
+
+/*!
+ \fn QString QTextEdit::toPlainText() const
+
+ Returns the text of the text edit as plain text.
+
+ \sa QTextEdit::setPlainText()
+ */
+
+
+/*!
+ \property QTextEdit::html
+
+ This property provides an HTML interface to the text of the text edit.
+
+ toHtml() returns the text of the text edit as html.
+
+ setHtml() changes the text of the text edit. Any previous text is
+ removed and the undo/redo history is cleared. The input text is
+ interpreted as rich text in html format.
+
+ \note It is the responsibility of the caller to make sure that the
+ text is correctly decoded when a QString containing HTML is created
+ and passed to setHtml().
+
+ By default, for a newly-created, empty document, this property contains
+ text to describe an HTML 4.0 document with no body text.
+
+ \sa {Supported HTML Subset}, plainText
+*/
+
+#ifndef QT_NO_TEXTHTMLPARSER
+void QTextEdit::setHtml(const QString &text)
+{
+ Q_D(QTextEdit);
+ d->control->setHtml(text);
+ d->preferRichText = true;
+}
+#endif
+
+/*! \reimp
+*/
+void QTextEdit::keyPressEvent(QKeyEvent *e)
+{
+ Q_D(QTextEdit);
+
+#ifdef QT_KEYPAD_NAVIGATION
+ switch (e->key()) {
+ case Qt::Key_Select:
+ if (QApplication::keypadNavigationEnabled()) {
+ // code assumes linksaccessible + editable isn't meaningful
+ if (d->control->textInteractionFlags() & Qt::TextEditable) {
+ setEditFocus(!hasEditFocus());
+ } else {
+ if (!hasEditFocus())
+ setEditFocus(true);
+ else {
+ QTextCursor cursor = d->control->textCursor();
+ QTextCharFormat charFmt = cursor.charFormat();
+ if (!(d->control->textInteractionFlags() & Qt::LinksAccessibleByKeyboard)
+ || !cursor.hasSelection() || charFmt.anchorHref().isEmpty()) {
+ e->accept();
+ return;
+ }
+ }
+ }
+ }
+ break;
+ case Qt::Key_Back:
+ case Qt::Key_No:
+ if (!QApplication::keypadNavigationEnabled()
+ || (QApplication::keypadNavigationEnabled() && !hasEditFocus())) {
+ e->ignore();
+ return;
+ }
+ break;
+ default:
+ if (QApplication::keypadNavigationEnabled()) {
+ if (!hasEditFocus() && !(e->modifiers() & Qt::ControlModifier)) {
+ if (e->text()[0].isPrint()) {
+ setEditFocus(true);
+ clear();
+ } else {
+ e->ignore();
+ return;
+ }
+ }
+ }
+ break;
+ }
+#endif
+
+ if (!(d->control->textInteractionFlags() & Qt::TextEditable)) {
+ switch (e->key()) {
+ case Qt::Key_Space:
+ e->accept();
+ if (e->modifiers() & Qt::ShiftModifier)
+ d->vbar->triggerAction(QAbstractSlider::SliderPageStepSub);
+ else
+ d->vbar->triggerAction(QAbstractSlider::SliderPageStepAdd);
+ break;
+ default:
+ d->sendControlEvent(e);
+ if (!e->isAccepted() && e->modifiers() == Qt::NoModifier) {
+ if (e->key() == Qt::Key_Home) {
+ d->vbar->triggerAction(QAbstractSlider::SliderToMinimum);
+ e->accept();
+ } else if (e->key() == Qt::Key_End) {
+ d->vbar->triggerAction(QAbstractSlider::SliderToMaximum);
+ e->accept();
+ }
+ }
+ if (!e->isAccepted()) {
+ QAbstractScrollArea::keyPressEvent(e);
+ }
+ }
+ return;
+ }
+
+#ifndef QT_NO_SHORTCUT
+ if (e == QKeySequence::MoveToPreviousPage) {
+ e->accept();
+ d->pageUpDown(QTextCursor::Up, QTextCursor::MoveAnchor);
+ return;
+ } else if (e == QKeySequence::MoveToNextPage) {
+ e->accept();
+ d->pageUpDown(QTextCursor::Down, QTextCursor::MoveAnchor);
+ return;
+ } else if (e == QKeySequence::SelectPreviousPage) {
+ e->accept();
+ d->pageUpDown(QTextCursor::Up, QTextCursor::KeepAnchor);
+ return;
+ } else if (e ==QKeySequence::SelectNextPage) {
+ e->accept();
+ d->pageUpDown(QTextCursor::Down, QTextCursor::KeepAnchor);
+ return;
+ }
+#endif // QT_NO_SHORTCUT
+
+ {
+ QTextCursor cursor = d->control->textCursor();
+ const QString text = e->text();
+ if (cursor.atBlockStart()
+ && (d->autoFormatting & AutoBulletList)
+ && (text.length() == 1)
+ && (text.at(0) == QLatin1Char('-') || text.at(0) == QLatin1Char('*'))
+ && (!cursor.currentList())) {
+
+ d->createAutoBulletList();
+ e->accept();
+ return;
+ }
+ }
+
+ d->sendControlEvent(e);
+#ifdef QT_KEYPAD_NAVIGATION
+ if (!e->isAccepted()) {
+ switch (e->key()) {
+ case Qt::Key_Up:
+ case Qt::Key_Down:
+ if (QApplication::keypadNavigationEnabled()) {
+ // Cursor position didn't change, so we want to leave
+ // these keys to change focus.
+ e->ignore();
+ return;
+ }
+ break;
+ case Qt::Key_Back:
+ if (!e->isAutoRepeat()) {
+ if (QApplication::keypadNavigationEnabled()) {
+ if (document()->isEmpty() || !(d->control->textInteractionFlags() & Qt::TextEditable)) {
+ setEditFocus(false);
+ e->accept();
+ } else if (!d->deleteAllTimer.isActive()) {
+ e->accept();
+ d->deleteAllTimer.start(750, this);
+ }
+ } else {
+ e->ignore();
+ return;
+ }
+ }
+ break;
+ default: break;
+ }
+ }
+#endif
+}
+
+/*! \reimp
+*/
+void QTextEdit::keyReleaseEvent(QKeyEvent *e)
+{
+#ifdef QT_KEYPAD_NAVIGATION
+ Q_D(QTextEdit);
+ if (QApplication::keypadNavigationEnabled()) {
+ if (!e->isAutoRepeat() && e->key() == Qt::Key_Back
+ && d->deleteAllTimer.isActive()) {
+ d->deleteAllTimer.stop();
+ QTextCursor cursor = d->control->textCursor();
+ QTextBlockFormat blockFmt = cursor.blockFormat();
+
+ QTextList *list = cursor.currentList();
+ if (list && cursor.atBlockStart()) {
+ list->remove(cursor.block());
+ } else if (cursor.atBlockStart() && blockFmt.indent() > 0) {
+ blockFmt.setIndent(blockFmt.indent() - 1);
+ cursor.setBlockFormat(blockFmt);
+ } else {
+ cursor.deletePreviousChar();
+ }
+ setTextCursor(cursor);
+ e->accept();
+ return;
+ }
+ }
+#endif
+ e->ignore();
+}
+
+/*!
+ Loads the resource specified by the given \a type and \a name.
+
+ This function is an extension of QTextDocument::loadResource().
+
+ \sa QTextDocument::loadResource()
+*/
+QVariant QTextEdit::loadResource(int type, const QUrl &name)
+{
+ Q_UNUSED(type);
+ Q_UNUSED(name);
+ return QVariant();
+}
+
+/*! \reimp
+*/
+void QTextEdit::resizeEvent(QResizeEvent *e)
+{
+ Q_D(QTextEdit);
+
+ if (d->lineWrap == NoWrap) {
+ QTextDocument *doc = d->control->document();
+ QVariant alignmentProperty = doc->documentLayout()->property("contentHasAlignment");
+
+ if (!doc->pageSize().isNull()
+ && alignmentProperty.type() == QVariant::Bool
+ && !alignmentProperty.toBool()) {
+
+ d->_q_adjustScrollbars();
+ return;
+ }
+ }
+
+ if (d->lineWrap != FixedPixelWidth
+ && e->oldSize().width() != e->size().width())
+ d->relayoutDocument();
+ else
+ d->_q_adjustScrollbars();
+}
+
+void QTextEditPrivate::relayoutDocument()
+{
+ QTextDocument *doc = control->document();
+ QAbstractTextDocumentLayout *layout = doc->documentLayout();
+
+ if (QTextDocumentLayout *tlayout = qobject_cast<QTextDocumentLayout *>(layout)) {
+ if (lineWrap == QTextEdit::FixedColumnWidth)
+ tlayout->setFixedColumnWidth(lineWrapColumnOrWidth);
+ else
+ tlayout->setFixedColumnWidth(-1);
+ }
+
+ QTextDocumentLayout *tlayout = qobject_cast<QTextDocumentLayout *>(layout);
+ QSize lastUsedSize;
+ if (tlayout)
+ lastUsedSize = tlayout->dynamicDocumentSize().toSize();
+ else
+ lastUsedSize = layout->documentSize().toSize();
+
+ // ignore calls to _q_adjustScrollbars caused by an emission of the
+ // usedSizeChanged() signal in the layout, as we're calling it
+ // later on our own anyway (or deliberately not) .
+ const bool oldIgnoreScrollbarAdjustment = ignoreAutomaticScrollbarAdjustment;
+ ignoreAutomaticScrollbarAdjustment = true;
+
+ int width = viewport->width();
+ if (lineWrap == QTextEdit::FixedPixelWidth)
+ width = lineWrapColumnOrWidth;
+ else if (lineWrap == QTextEdit::NoWrap) {
+ QVariant alignmentProperty = doc->documentLayout()->property("contentHasAlignment");
+ if (alignmentProperty.type() == QVariant::Bool && !alignmentProperty.toBool()) {
+
+ width = 0;
+ }
+ }
+
+ doc->setPageSize(QSize(width, -1));
+ if (tlayout)
+ tlayout->ensureLayouted(verticalOffset() + viewport->height());
+
+ ignoreAutomaticScrollbarAdjustment = oldIgnoreScrollbarAdjustment;
+
+ QSize usedSize;
+ if (tlayout)
+ usedSize = tlayout->dynamicDocumentSize().toSize();
+ else
+ usedSize = layout->documentSize().toSize();
+
+ // this is an obscure situation in the layout that can happen:
+ // if a character at the end of a line is the tallest one and therefore
+ // influencing the total height of the line and the line right below it
+ // is always taller though, then it can happen that if due to line breaking
+ // that tall character wraps into the lower line the document not only shrinks
+ // horizontally (causing the character to wrap in the first place) but also
+ // vertically, because the original line is now smaller and the one below kept
+ // its size. So a layout with less width _can_ take up less vertical space, too.
+ // If the wider case causes a vertical scroll bar to appear and the narrower one
+ // (narrower because the vertical scroll bar takes up horizontal space)) to disappear
+ // again then we have an endless loop, as _q_adjustScrollBars sets new ranges on the
+ // scroll bars, the QAbstractScrollArea will find out about it and try to show/hide
+ // the scroll bars again. That's why we try to detect this case here and break out.
+ //
+ // (if you change this please also check the layoutingLoop() testcase in
+ // QTextEdit's autotests)
+ if (lastUsedSize.isValid()
+ && !vbar->isHidden()
+ && viewport->width() < lastUsedSize.width()
+ && usedSize.height() < lastUsedSize.height()
+ && usedSize.height() <= viewport->height())
+ return;
+
+ _q_adjustScrollbars();
+}
+
+void QTextEditPrivate::paint(QPainter *p, QPaintEvent *e)
+{
+ const int xOffset = horizontalOffset();
+ const int yOffset = verticalOffset();
+
+ QRect r = e->rect();
+ p->translate(-xOffset, -yOffset);
+ r.translate(xOffset, yOffset);
+
+ QTextDocument *doc = control->document();
+ QTextDocumentLayout *layout = qobject_cast<QTextDocumentLayout *>(doc->documentLayout());
+
+ // the layout might need to expand the root frame to
+ // the viewport if NoWrap is set
+ if (layout)
+ layout->setViewport(viewport->rect());
+
+ control->drawContents(p, r, q_func());
+
+ if (layout)
+ layout->setViewport(QRect());
+}
+
+/*! \reimp
+*/
+void QTextEdit::paintEvent(QPaintEvent *e)
+{
+ Q_D(QTextEdit);
+ QPainter p(d->viewport);
+ d->paint(&p, e);
+}
+
+void QTextEditPrivate::_q_currentCharFormatChanged(const QTextCharFormat &fmt)
+{
+ Q_Q(QTextEdit);
+ emit q->currentCharFormatChanged(fmt);
+#ifdef QT3_SUPPORT
+ // compat signals
+ emit q->currentFontChanged(fmt.font());
+ emit q->currentColorChanged(fmt.foreground().color());
+#endif
+}
+
+void QTextEditPrivate::updateDefaultTextOption()
+{
+ QTextDocument *doc = control->document();
+
+ QTextOption opt = doc->defaultTextOption();
+ QTextOption::WrapMode oldWrapMode = opt.wrapMode();
+
+ if (lineWrap == QTextEdit::NoWrap)
+ opt.setWrapMode(QTextOption::NoWrap);
+ else
+ opt.setWrapMode(wordWrap);
+
+ if (opt.wrapMode() != oldWrapMode)
+ doc->setDefaultTextOption(opt);
+}
+
+/*! \reimp
+*/
+void QTextEdit::mousePressEvent(QMouseEvent *e)
+{
+ Q_D(QTextEdit);
+#ifdef QT_KEYPAD_NAVIGATION
+ if (QApplication::keypadNavigationEnabled() && !hasEditFocus())
+ setEditFocus(true);
+#endif
+ d->sendControlEvent(e);
+}
+
+/*! \reimp
+*/
+void QTextEdit::mouseMoveEvent(QMouseEvent *e)
+{
+ Q_D(QTextEdit);
+ d->inDrag = false; // paranoia
+ const QPoint pos = e->pos();
+ d->sendControlEvent(e);
+ if (!(e->buttons() & Qt::LeftButton))
+ return;
+ QRect visible = d->viewport->rect();
+ if (visible.contains(pos))
+ d->autoScrollTimer.stop();
+ else if (!d->autoScrollTimer.isActive())
+ d->autoScrollTimer.start(100, this);
+}
+
+/*! \reimp
+*/
+void QTextEdit::mouseReleaseEvent(QMouseEvent *e)
+{
+ Q_D(QTextEdit);
+ d->sendControlEvent(e);
+ if (d->autoScrollTimer.isActive()) {
+ d->autoScrollTimer.stop();
+ ensureCursorVisible();
+ }
+}
+
+/*! \reimp
+*/
+void QTextEdit::mouseDoubleClickEvent(QMouseEvent *e)
+{
+ Q_D(QTextEdit);
+ d->sendControlEvent(e);
+}
+
+/*! \reimp
+*/
+bool QTextEdit::focusNextPrevChild(bool next)
+{
+ Q_D(const QTextEdit);
+ if (!d->tabChangesFocus && d->control->textInteractionFlags() & Qt::TextEditable)
+ return false;
+ return QAbstractScrollArea::focusNextPrevChild(next);
+}
+
+#ifndef QT_NO_CONTEXTMENU
+/*!
+ \fn void QTextEdit::contextMenuEvent(QContextMenuEvent *event)
+
+ Shows the standard context menu created with createStandardContextMenu().
+
+ If you do not want the text edit to have a context menu, you can set
+ its \l contextMenuPolicy to Qt::NoContextMenu. If you want to
+ customize the context menu, reimplement this function. If you want
+ to extend the standard context menu, reimplement this function, call
+ createStandardContextMenu() and extend the menu returned.
+
+ Information about the event is passed in the \a event object.
+
+ \snippet doc/src/snippets/code/src_gui_widgets_qtextedit.cpp 0
+*/
+void QTextEdit::contextMenuEvent(QContextMenuEvent *e)
+{
+ Q_D(QTextEdit);
+ d->sendControlEvent(e);
+}
+#endif // QT_NO_CONTEXTMENU
+
+#ifndef QT_NO_DRAGANDDROP
+/*! \reimp
+*/
+void QTextEdit::dragEnterEvent(QDragEnterEvent *e)
+{
+ Q_D(QTextEdit);
+ d->inDrag = true;
+ d->sendControlEvent(e);
+}
+
+/*! \reimp
+*/
+void QTextEdit::dragLeaveEvent(QDragLeaveEvent *e)
+{
+ Q_D(QTextEdit);
+ d->inDrag = false;
+ d->autoScrollTimer.stop();
+ d->sendControlEvent(e);
+}
+
+/*! \reimp
+*/
+void QTextEdit::dragMoveEvent(QDragMoveEvent *e)
+{
+ Q_D(QTextEdit);
+ d->autoScrollDragPos = e->pos();
+ if (!d->autoScrollTimer.isActive())
+ d->autoScrollTimer.start(100, this);
+ d->sendControlEvent(e);
+}
+
+/*! \reimp
+*/
+void QTextEdit::dropEvent(QDropEvent *e)
+{
+ Q_D(QTextEdit);
+ d->inDrag = false;
+ d->autoScrollTimer.stop();
+ d->sendControlEvent(e);
+}
+
+#endif // QT_NO_DRAGANDDROP
+
+/*! \reimp
+ */
+void QTextEdit::inputMethodEvent(QInputMethodEvent *e)
+{
+ Q_D(QTextEdit);
+#ifdef QT_KEYPAD_NAVIGATION
+ if (d->control->textInteractionFlags() & Qt::TextEditable
+ && QApplication::keypadNavigationEnabled()
+ && !hasEditFocus()) {
+ setEditFocus(true);
+ selectAll(); // so text is replaced rather than appended to
+ }
+#endif
+ d->sendControlEvent(e);
+}
+
+/*!\reimp
+*/
+void QTextEdit::scrollContentsBy(int dx, int dy)
+{
+ Q_D(QTextEdit);
+ if (isRightToLeft())
+ dx = -dx;
+ d->viewport->scroll(dx, dy);
+}
+
+/*!\reimp
+*/
+QVariant QTextEdit::inputMethodQuery(Qt::InputMethodQuery property) const
+{
+ Q_D(const QTextEdit);
+ QVariant v = d->control->inputMethodQuery(property);
+ const QPoint offset(-d->horizontalOffset(), -d->verticalOffset());
+ if (v.type() == QVariant::RectF)
+ v = v.toRectF().toRect().translated(offset);
+ else if (v.type() == QVariant::PointF)
+ v = v.toPointF().toPoint() + offset;
+ else if (v.type() == QVariant::Rect)
+ v = v.toRect().translated(offset);
+ else if (v.type() == QVariant::Point)
+ v = v.toPoint() + offset;
+ return v;
+}
+
+/*! \reimp
+*/
+void QTextEdit::focusInEvent(QFocusEvent *e)
+{
+ Q_D(QTextEdit);
+ QAbstractScrollArea::focusInEvent(e);
+ d->sendControlEvent(e);
+}
+
+/*! \reimp
+*/
+void QTextEdit::focusOutEvent(QFocusEvent *e)
+{
+ Q_D(QTextEdit);
+ QAbstractScrollArea::focusOutEvent(e);
+ d->sendControlEvent(e);
+}
+
+/*! \reimp
+*/
+void QTextEdit::showEvent(QShowEvent *)
+{
+ Q_D(QTextEdit);
+ if (!d->anchorToScrollToWhenVisible.isEmpty()) {
+ scrollToAnchor(d->anchorToScrollToWhenVisible);
+ d->anchorToScrollToWhenVisible.clear();
+ d->showCursorOnInitialShow = false;
+ } else if (d->showCursorOnInitialShow) {
+ d->showCursorOnInitialShow = false;
+ ensureCursorVisible();
+ }
+}
+
+/*! \reimp
+*/
+void QTextEdit::changeEvent(QEvent *e)
+{
+ Q_D(QTextEdit);
+ QAbstractScrollArea::changeEvent(e);
+ if (e->type() == QEvent::ApplicationFontChange
+ || e->type() == QEvent::FontChange) {
+ d->control->document()->setDefaultFont(font());
+ } else if(e->type() == QEvent::ActivationChange) {
+ if (!isActiveWindow())
+ d->autoScrollTimer.stop();
+ } else if (e->type() == QEvent::EnabledChange) {
+ e->setAccepted(isEnabled());
+ d->control->setPalette(palette());
+ d->sendControlEvent(e);
+ } else if (e->type() == QEvent::PaletteChange) {
+ d->control->setPalette(palette());
+ } else if (e->type() == QEvent::LayoutDirectionChange) {
+ d->sendControlEvent(e);
+ }
+}
+
+/*! \reimp
+*/
+#ifndef QT_NO_WHEELEVENT
+void QTextEdit::wheelEvent(QWheelEvent *e)
+{
+ Q_D(QTextEdit);
+ if (!(d->control->textInteractionFlags() & Qt::TextEditable)) {
+ if (e->modifiers() & Qt::ControlModifier) {
+ const int delta = e->delta();
+ if (delta < 0)
+ zoomOut();
+ else if (delta > 0)
+ zoomIn();
+ return;
+ }
+ }
+ QAbstractScrollArea::wheelEvent(e);
+ updateMicroFocus();
+}
+#endif
+
+#ifndef QT_NO_CONTEXTMENU
+/*! This function creates the standard context menu which is shown
+ when the user clicks on the text edit with the right mouse
+ button. It is called from the default contextMenuEvent() handler.
+ The popup menu's ownership is transferred to the caller.
+
+ We recommend that you use the createStandardContextMenu(QPoint) version instead
+ which will enable the actions that are sensitive to where the user clicked.
+*/
+
+QMenu *QTextEdit::createStandardContextMenu()
+{
+ Q_D(QTextEdit);
+ return d->control->createStandardContextMenu(QPointF(), this);
+}
+
+/*!
+ \since 4.4
+ This function creates the standard context menu which is shown
+ when the user clicks on the text edit with the right mouse
+ button. It is called from the default contextMenuEvent() handler
+ and it takes the \a position of where the mouse click was.
+ This can enable actions that are sensitive to the position where the user clicked.
+ The popup menu's ownership is transferred to the caller.
+*/
+
+QMenu *QTextEdit::createStandardContextMenu(const QPoint &position)
+{
+ Q_D(QTextEdit);
+ return d->control->createStandardContextMenu(position, this);
+}
+#endif // QT_NO_CONTEXTMENU
+
+/*!
+ returns a QTextCursor at position \a pos (in viewport coordinates).
+*/
+QTextCursor QTextEdit::cursorForPosition(const QPoint &pos) const
+{
+ Q_D(const QTextEdit);
+ return d->control->cursorForPosition(d->mapToContents(pos));
+}
+
+/*!
+ returns a rectangle (in viewport coordinates) that includes the
+ \a cursor.
+ */
+QRect QTextEdit::cursorRect(const QTextCursor &cursor) const
+{
+ Q_D(const QTextEdit);
+ if (cursor.isNull())
+ return QRect();
+
+ QRect r = d->control->cursorRect(cursor).toRect();
+ r.translate(-d->horizontalOffset(),-d->verticalOffset());
+ return r;
+}
+
+/*!
+ returns a rectangle (in viewport coordinates) that includes the
+ cursor of the text edit.
+ */
+QRect QTextEdit::cursorRect() const
+{
+ Q_D(const QTextEdit);
+ QRect r = d->control->cursorRect().toRect();
+ r.translate(-d->horizontalOffset(),-d->verticalOffset());
+ return r;
+}
+
+
+/*!
+ Returns the reference of the anchor at position \a pos, or an
+ empty string if no anchor exists at that point.
+*/
+QString QTextEdit::anchorAt(const QPoint& pos) const
+{
+ Q_D(const QTextEdit);
+ return d->control->anchorAt(d->mapToContents(pos));
+}
+
+/*!
+ \property QTextEdit::overwriteMode
+ \since 4.1
+ \brief whether text entered by the user will overwrite existing text
+
+ As with many text editors, the text editor widget can be configured
+ to insert or overwrite existing text with new text entered by the user.
+
+ If this property is true, existing text is overwritten, character-for-character
+ by new text; otherwise, text is inserted at the cursor position, displacing
+ existing text.
+
+ By default, this property is false (new text does not overwrite existing text).
+*/
+
+bool QTextEdit::overwriteMode() const
+{
+ Q_D(const QTextEdit);
+ return d->control->overwriteMode();
+}
+
+void QTextEdit::setOverwriteMode(bool overwrite)
+{
+ Q_D(QTextEdit);
+ d->control->setOverwriteMode(overwrite);
+}
+
+/*!
+ \property QTextEdit::tabStopWidth
+ \brief the tab stop width in pixels
+ \since 4.1
+
+ By default, this property contains a value of 80.
+*/
+
+int QTextEdit::tabStopWidth() const
+{
+ Q_D(const QTextEdit);
+ return qRound(d->control->document()->defaultTextOption().tabStop());
+}
+
+void QTextEdit::setTabStopWidth(int width)
+{
+ Q_D(QTextEdit);
+ QTextOption opt = d->control->document()->defaultTextOption();
+ if (opt.tabStop() == width || width < 0)
+ return;
+ opt.setTabStop(width);
+ d->control->document()->setDefaultTextOption(opt);
+}
+
+/*!
+ \since 4.2
+ \property QTextEdit::cursorWidth
+
+ This property specifies the width of the cursor in pixels. The default value is 1.
+*/
+int QTextEdit::cursorWidth() const
+{
+ Q_D(const QTextEdit);
+ return d->control->cursorWidth();
+}
+
+void QTextEdit::setCursorWidth(int width)
+{
+ Q_D(QTextEdit);
+ d->control->setCursorWidth(width);
+}
+
+/*!
+ \property QTextEdit::acceptRichText
+ \brief whether the text edit accepts rich text insertions by the user
+ \since 4.1
+
+ When this propery is set to false text edit will accept only
+ plain text input from the user. For example through clipboard or drag and drop.
+
+ This property's default is true.
+*/
+
+bool QTextEdit::acceptRichText() const
+{
+ Q_D(const QTextEdit);
+ return d->control->acceptRichText();
+}
+
+void QTextEdit::setAcceptRichText(bool accept)
+{
+ Q_D(QTextEdit);
+ d->control->setAcceptRichText(accept);
+}
+
+/*!
+ \class QTextEdit::ExtraSelection
+ \since 4.2
+ \brief The QTextEdit::ExtraSelection structure provides a way of specifying a
+ character format for a given selection in a document
+*/
+
+/*!
+ \variable QTextEdit::ExtraSelection::cursor
+ A cursor that contains a selection in a QTextDocument
+*/
+
+/*!
+ \variable QTextEdit::ExtraSelection::format
+ A format that is used to specify a foreground or background brush/color
+ for the selection.
+*/
+
+/*!
+ \since 4.2
+ This function allows temporarily marking certain regions in the document
+ with a given color, specified as \a selections. This can be useful for
+ example in a programming editor to mark a whole line of text with a given
+ background color to indicate the existence of a breakpoint.
+
+ \sa QTextEdit::ExtraSelection, extraSelections()
+*/
+void QTextEdit::setExtraSelections(const QList<ExtraSelection> &selections)
+{
+ Q_D(QTextEdit);
+ d->control->setExtraSelections(selections);
+}
+
+/*!
+ \since 4.2
+ Returns previously set extra selections.
+
+ \sa setExtraSelections()
+*/
+QList<QTextEdit::ExtraSelection> QTextEdit::extraSelections() const
+{
+ Q_D(const QTextEdit);
+ return d->control->extraSelections();
+}
+
+/*!
+ This function returns a new MIME data object to represent the contents
+ of the text edit's current selection. It is called when the selection needs
+ to be encapsulated into a new QMimeData object; for example, when a drag
+ and drop operation is started, or when data is copyied to the clipboard.
+
+ If you reimplement this function, note that the ownership of the returned
+ QMimeData object is passed to the caller. The selection can be retrieved
+ by using the textCursor() function.
+*/
+QMimeData *QTextEdit::createMimeDataFromSelection() const
+{
+ Q_D(const QTextEdit);
+ return d->control->QTextControl::createMimeDataFromSelection();
+}
+
+/*!
+ This function returns true if the contents of the MIME data object, specified
+ by \a source, can be decoded and inserted into the document. It is called
+ for example when during a drag operation the mouse enters this widget and it
+ is necessary to determine whether it is possible to accept the drag and drop
+ operation.
+
+ Reimplement this function to enable drag and drop support for additional MIME types.
+ */
+bool QTextEdit::canInsertFromMimeData(const QMimeData *source) const
+{
+ Q_D(const QTextEdit);
+ return d->control->QTextControl::canInsertFromMimeData(source);
+}
+
+/*!
+ This function inserts the contents of the MIME data object, specified
+ by \a source, into the text edit at the current cursor position. It is
+ called whenever text is inserted as the result of a clipboard paste
+ operation, or when the text edit accepts data from a drag and drop
+ operation.
+
+ Reimplement this function to enable drag and drop support for additional MIME types.
+ */
+void QTextEdit::insertFromMimeData(const QMimeData *source)
+{
+ Q_D(QTextEdit);
+ d->control->QTextControl::insertFromMimeData(source);
+}
+
+/*!
+ \property QTextEdit::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.
+*/
+
+bool QTextEdit::isReadOnly() const
+{
+ Q_D(const QTextEdit);
+ return !(d->control->textInteractionFlags() & Qt::TextEditable);
+}
+
+void QTextEdit::setReadOnly(bool ro)
+{
+ Q_D(QTextEdit);
+ Qt::TextInteractionFlags flags = Qt::NoTextInteraction;
+ if (ro) {
+ flags = Qt::TextSelectableByMouse;
+#ifndef QT_NO_TEXTBROWSER
+ if (qobject_cast<QTextBrowser *>(this))
+ flags |= Qt::TextBrowserInteraction;
+#endif
+ } else {
+ flags = Qt::TextEditorInteraction;
+ }
+ setAttribute(Qt::WA_InputMethodEnabled, !ro);
+ d->control->setTextInteractionFlags(flags);
+}
+
+/*!
+ \property QTextEdit::textInteractionFlags
+ \since 4.2
+
+ Specifies how the widget should interact with user input.
+
+ The default value depends on whether the QTextEdit is read-only
+ or editable, and whether it is a QTextBrowser or not.
+*/
+
+void QTextEdit::setTextInteractionFlags(Qt::TextInteractionFlags flags)
+{
+ Q_D(QTextEdit);
+ d->control->setTextInteractionFlags(flags);
+}
+
+Qt::TextInteractionFlags QTextEdit::textInteractionFlags() const
+{
+ Q_D(const QTextEdit);
+ return d->control->textInteractionFlags();
+}
+
+/*!
+ Merges the properties specified in \a modifier into the current character
+ format by calling QTextCursor::mergeCharFormat on the editor's cursor.
+ If the editor has a selection then the properties of \a modifier are
+ directly applied to the selection.
+
+ \sa QTextCursor::mergeCharFormat()
+ */
+void QTextEdit::mergeCurrentCharFormat(const QTextCharFormat &modifier)
+{
+ Q_D(QTextEdit);
+ d->control->mergeCurrentCharFormat(modifier);
+}
+
+/*!
+ Sets the char format that is be used when inserting new text to \a
+ format by calling QTextCursor::setCharFormat() on the editor's
+ cursor. If the editor has a selection then the char format is
+ directly applied to the selection.
+ */
+void QTextEdit::setCurrentCharFormat(const QTextCharFormat &format)
+{
+ Q_D(QTextEdit);
+ d->control->setCurrentCharFormat(format);
+}
+
+/*!
+ Returns the char format that is used when inserting new text.
+ */
+QTextCharFormat QTextEdit::currentCharFormat() const
+{
+ Q_D(const QTextEdit);
+ return d->control->currentCharFormat();
+}
+
+/*!
+ \property QTextEdit::autoFormatting
+ \brief the enabled set of auto formatting features
+
+ The value can be any combination of the values in the
+ AutoFormattingFlag enum. The default is AutoNone. Choose
+ AutoAll to enable all automatic formatting.
+
+ Currently, the only automatic formatting feature provided is
+ AutoBulletList; future versions of Qt may offer more.
+*/
+
+QTextEdit::AutoFormatting QTextEdit::autoFormatting() const
+{
+ Q_D(const QTextEdit);
+ return d->autoFormatting;
+}
+
+void QTextEdit::setAutoFormatting(AutoFormatting features)
+{
+ Q_D(QTextEdit);
+ d->autoFormatting = features;
+}
+
+/*!
+ Convenience slot that inserts \a text at the current
+ cursor position.
+
+ It is equivalent to
+
+ \snippet doc/src/snippets/code/src_gui_widgets_qtextedit.cpp 1
+ */
+void QTextEdit::insertPlainText(const QString &text)
+{
+ Q_D(QTextEdit);
+ d->control->insertPlainText(text);
+}
+
+/*!
+ Convenience slot that inserts \a text which is assumed to be of
+ html formatting at the current cursor position.
+
+ It is equivalent to:
+
+ \snippet doc/src/snippets/code/src_gui_widgets_qtextedit.cpp 2
+
+ \note When using this function with a style sheet, the style sheet will
+ only apply to the current block in the document. In order to apply a style
+ sheet throughout a document, use QTextDocument::setDefaultStyleSheet()
+ instead.
+ */
+#ifndef QT_NO_TEXTHTMLPARSER
+void QTextEdit::insertHtml(const QString &text)
+{
+ Q_D(QTextEdit);
+ d->control->insertHtml(text);
+}
+#endif // QT_NO_TEXTHTMLPARSER
+
+/*!
+ Scrolls the text edit so that the anchor with the given \a name is
+ visible; does nothing if the \a name is empty, or is already
+ visible, or isn't found.
+*/
+void QTextEdit::scrollToAnchor(const QString &name)
+{
+ Q_D(QTextEdit);
+ if (name.isEmpty())
+ return;
+
+ if (!isVisible()) {
+ d->anchorToScrollToWhenVisible = name;
+ return;
+ }
+
+ QPointF p = d->control->anchorPosition(name);
+ const int newPosition = qRound(p.y());
+ if ( d->vbar->maximum() < newPosition )
+ d->_q_adjustScrollbars();
+ d->vbar->setValue(newPosition);
+}
+
+/*!
+ \fn QTextEdit::zoomIn(int range)
+
+ 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 QTextEdit::zoomIn(int range)
+{
+ QFont f = font();
+ const int newSize = f.pointSize() + range;
+ if (newSize <= 0)
+ return;
+ f.setPointSize(newSize);
+ setFont(f);
+}
+
+/*!
+ \fn QTextEdit::zoomOut(int range)
+
+ \overload
+
+ 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 QTextEdit::zoomOut(int range)
+{
+ zoomIn(-range);
+}
+
+/*!
+ \since 4.2
+ Moves the cursor by performing the given \a operation.
+
+ If \a mode is QTextCursor::KeepAnchor, the cursor selects the text it moves over.
+ This is the same effect that the user achieves when they hold down the Shift key
+ and move the cursor with the cursor keys.
+
+ \sa QTextCursor::movePosition()
+*/
+void QTextEdit::moveCursor(QTextCursor::MoveOperation operation, QTextCursor::MoveMode mode)
+{
+ Q_D(QTextEdit);
+ d->control->moveCursor(operation, mode);
+}
+
+/*!
+ \since 4.2
+ Returns whether text can be pasted from the clipboard into the textedit.
+*/
+bool QTextEdit::canPaste() const
+{
+ Q_D(const QTextEdit);
+ return d->control->canPaste();
+}
+
+#ifndef QT_NO_PRINTER
+/*!
+ \since 4.3
+ Convenience function to print the text edit's document to the given \a printer. This
+ is equivalent to calling the print method on the document directly except that this
+ function also supports QPrinter::Selection as print range.
+
+ \sa QTextDocument::print()
+*/
+void QTextEdit::print(QPrinter *printer) const
+{
+ Q_D(const QTextEdit);
+ d->control->print(printer);
+}
+#endif // QT _NO_PRINTER
+
+/*! \property QTextEdit::tabChangesFocus
+ \brief whether \gui 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 \gui Tab key, as this breaks
+ the focus chain. The default is false.
+
+*/
+
+bool QTextEdit::tabChangesFocus() const
+{
+ Q_D(const QTextEdit);
+ return d->tabChangesFocus;
+}
+
+void QTextEdit::setTabChangesFocus(bool b)
+{
+ Q_D(QTextEdit);
+ d->tabChangesFocus = b;
+}
+
+/*!
+ \property QTextEdit::documentTitle
+ \brief the title of the document parsed from the text.
+
+ By default, for a newly-created, empty document, this property contains
+ an empty string.
+*/
+
+/*!
+ \property QTextEdit::lineWrapMode
+ \brief the line wrap mode
+
+ The default mode is 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 setWordWrapMode(). If you set a wrap mode of
+ FixedPixelWidth or FixedColumnWidth you should also call
+ setLineWrapColumnOrWidth() with the width you want.
+
+ \sa lineWrapColumnOrWidth
+*/
+
+QTextEdit::LineWrapMode QTextEdit::lineWrapMode() const
+{
+ Q_D(const QTextEdit);
+ return d->lineWrap;
+}
+
+void QTextEdit::setLineWrapMode(LineWrapMode wrap)
+{
+ Q_D(QTextEdit);
+ if (d->lineWrap == wrap)
+ return;
+ d->lineWrap = wrap;
+ d->updateDefaultTextOption();
+ d->relayoutDocument();
+}
+
+/*!
+ \property QTextEdit::lineWrapColumnOrWidth
+ \brief the position (in pixels or columns depending on the wrap mode) where text will be wrapped
+
+ If the wrap mode is 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 FixedColumnWidth, the value is the
+ column number (in character columns) from the left edge of the
+ text edit at which text should be wrapped.
+
+ By default, this property contains a value of 0.
+
+ \sa lineWrapMode
+*/
+
+int QTextEdit::lineWrapColumnOrWidth() const
+{
+ Q_D(const QTextEdit);
+ return d->lineWrapColumnOrWidth;
+}
+
+void QTextEdit::setLineWrapColumnOrWidth(int w)
+{
+ Q_D(QTextEdit);
+ d->lineWrapColumnOrWidth = w;
+ d->relayoutDocument();
+}
+
+/*!
+ \property QTextEdit::wordWrapMode
+ \brief the mode QTextEdit will use when wrapping text by words
+
+ By default, this property is set to QTextOption::WrapAtWordBoundaryOrAnywhere.
+
+ \sa QTextOption::WrapMode
+*/
+
+QTextOption::WrapMode QTextEdit::wordWrapMode() const
+{
+ Q_D(const QTextEdit);
+ return d->wordWrap;
+}
+
+void QTextEdit::setWordWrapMode(QTextOption::WrapMode mode)
+{
+ Q_D(QTextEdit);
+ if (mode == d->wordWrap)
+ return;
+ d->wordWrap = mode;
+ d->updateDefaultTextOption();
+}
+
+/*!
+ Finds the next occurrence of the string, \a exp, using the given
+ \a options. Returns true if \a exp was found and changes the
+ cursor to select the match; otherwise returns false.
+*/
+bool QTextEdit::find(const QString &exp, QTextDocument::FindFlags options)
+{
+ Q_D(QTextEdit);
+ return d->control->find(exp, options);
+}
+
+/*!
+ \fn void QTextEdit::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 QTextEdit::currentCharFormatChanged(const QTextCharFormat &f)
+
+ This signal is emitted if the current character format has changed, for
+ example caused by a change of the cursor position.
+
+ The new format is \a f.
+
+ \sa setCurrentCharFormat()
+*/
+
+/*!
+ \fn void QTextEdit::selectionChanged()
+
+ This signal is emitted whenever the selection changes.
+
+ \sa copyAvailable()
+*/
+
+/*!
+ \fn void QTextEdit::cursorPositionChanged()
+
+ This signal is emitted whenever the position of the
+ cursor changed.
+*/
+
+/*!
+ \since 4.2
+
+ Sets the text edit's \a text. The text can be plain text or HTML
+ and the text edit will try to guess the right format.
+
+ Use setHtml() or setPlainText() directly to avoid text edit's guessing.
+*/
+void QTextEdit::setText(const QString &text)
+{
+ Q_D(QTextEdit);
+ Qt::TextFormat format = d->textFormat;
+ if (d->textFormat == Qt::AutoText)
+ format = Qt::mightBeRichText(text) ? Qt::RichText : Qt::PlainText;
+#ifndef QT_NO_TEXTHTMLPARSER
+ if (format == Qt::RichText || format == Qt::LogText)
+ setHtml(text);
+ else
+#endif
+ setPlainText(text);
+}
+
+#ifdef QT3_SUPPORT
+/*!
+ Use the QTextCursor class instead.
+*/
+void QTextEdit::moveCursor(CursorAction action, QTextCursor::MoveMode mode)
+{
+ Q_D(QTextEdit);
+ if (action == MovePageUp) {
+ d->pageUpDown(QTextCursor::Up, mode);
+ return;
+ } else if (action == MovePageDown) {
+ d->pageUpDown(QTextCursor::Down, mode);
+ return;
+ }
+
+ QTextCursor cursor = d->control->textCursor();
+ QTextCursor::MoveOperation op = QTextCursor::NoMove;
+ switch (action) {
+ case MoveBackward: op = QTextCursor::Left; break;
+ case MoveForward: op = QTextCursor::Right; break;
+ case MoveWordBackward: op = QTextCursor::WordLeft; break;
+ case MoveWordForward: op = QTextCursor::WordRight; break;
+ case MoveUp: op = QTextCursor::Up; break;
+ case MoveDown: op = QTextCursor::Down; break;
+ case MoveLineStart: op = QTextCursor::StartOfLine; break;
+ case MoveLineEnd: op = QTextCursor::EndOfLine; break;
+ case MoveHome: op = QTextCursor::Start; break;
+ case MoveEnd: op = QTextCursor::End; break;
+ default: return;
+ }
+ cursor.movePosition(op, mode);
+ d->control->setTextCursor(cursor);
+}
+
+/*!
+ Use the QTextCursor class instead.
+*/
+void QTextEdit::moveCursor(CursorAction action, bool select)
+{
+ moveCursor(action, select ? QTextCursor::KeepAnchor : QTextCursor::MoveAnchor);
+}
+
+/*!
+ Executes keyboard action \a action.
+
+ Use the QTextCursor class instead.
+
+ \sa textCursor()
+*/
+void QTextEdit::doKeyboardAction(KeyboardAction action)
+{
+ Q_D(QTextEdit);
+ QTextCursor cursor = d->control->textCursor();
+ switch (action) {
+ case ActionBackspace: cursor.deletePreviousChar(); break;
+ case ActionDelete: cursor.deleteChar(); break;
+ case ActionReturn: cursor.insertBlock(); break;
+ case ActionKill: {
+ QTextBlock block = cursor.block();
+ if (cursor.position() == block.position() + block.length() - 2)
+ cursor.movePosition(QTextCursor::Right, QTextCursor::KeepAnchor);
+ else
+ cursor.movePosition(QTextCursor::EndOfBlock, QTextCursor::KeepAnchor);
+ cursor.deleteChar();
+ break;
+ }
+ case ActionWordBackspace:
+ cursor.movePosition(QTextCursor::PreviousWord, QTextCursor::KeepAnchor);
+ cursor.deletePreviousChar();
+ break;
+ case ActionWordDelete:
+ cursor.movePosition(QTextCursor::EndOfWord, QTextCursor::KeepAnchor);
+ cursor.deleteChar();
+ break;
+ }
+ d->control->setTextCursor(cursor);
+}
+
+/*!
+ Returns all the text in the text edit as plain text.
+*/
+QString QTextEdit::text() const
+{
+ Q_D(const QTextEdit);
+ if (d->textFormat == Qt::RichText || d->textFormat == Qt::LogText || (d->textFormat == Qt::AutoText && d->preferRichText))
+ return d->control->toHtml();
+ else
+ return d->control->toPlainText();
+}
+
+
+/*!
+ Sets the text format to format \a f.
+
+ \sa textFormat()
+*/
+void QTextEdit::setTextFormat(Qt::TextFormat f)
+{
+ Q_D(QTextEdit);
+ d->textFormat = f;
+}
+
+/*!
+ Returns the text format.
+
+ \sa setTextFormat()
+*/
+Qt::TextFormat QTextEdit::textFormat() const
+{
+ Q_D(const QTextEdit);
+ return d->textFormat;
+}
+
+#endif // QT3_SUPPORT
+
+/*!
+ Appends a new paragraph with \a text to the end of the text edit.
+
+ \note The new paragraph appended will have the same character format and
+ block format as the current paragraph, determined by the position of the cursor.
+
+ \sa currentCharFormat(), QTextCursor::blockFormat()
+*/
+
+void QTextEdit::append(const QString &text)
+{
+ Q_D(QTextEdit);
+ QTextBlock lastBlock = d->control->document()->lastBlock();
+ const bool atBottom = isReadOnly() ? d->verticalOffset() >= d->vbar->maximum() :
+ d->control->textCursor().atEnd();
+ d->control->append(text);
+ if (atBottom)
+ d->vbar->setValue(d->vbar->maximum());
+}
+
+/*!
+ Ensures that the cursor is visible by scrolling the text edit if
+ necessary.
+*/
+void QTextEdit::ensureCursorVisible()
+{
+ Q_D(QTextEdit);
+ d->control->ensureCursorVisible();
+}
+
+
+/*!
+ \enum QTextEdit::KeyboardAction
+
+ \compat
+
+ \value ActionBackspace
+ \value ActionDelete
+ \value ActionReturn
+ \value ActionKill
+ \value ActionWordBackspace
+ \value ActionWordDelete
+*/
+
+/*!
+ \fn bool QTextEdit::find(const QString &exp, bool cs, bool wo)
+
+ Use the find() overload that takes a QTextDocument::FindFlags
+ argument.
+*/
+
+/*!
+ \fn void QTextEdit::sync()
+
+ Does nothing.
+*/
+
+/*!
+ \fn void QTextEdit::setBold(bool b)
+
+ Use setFontWeight() instead.
+*/
+
+/*!
+ \fn void QTextEdit::setUnderline(bool b)
+
+ Use setFontUnderline() instead.
+*/
+
+/*!
+ \fn void QTextEdit::setItalic(bool i)
+
+ Use setFontItalic() instead.
+*/
+
+/*!
+ \fn void QTextEdit::setFamily(const QString &family)
+
+ Use setFontFamily() instead.
+*/
+
+/*!
+ \fn void QTextEdit::setPointSize(int size)
+
+ Use setFontPointSize() instead.
+*/
+
+/*!
+ \fn bool QTextEdit::italic() const
+
+ Use fontItalic() instead.
+*/
+
+/*!
+ \fn bool QTextEdit::bold() const
+
+ Use fontWeight() >= QFont::Bold instead.
+*/
+
+/*!
+ \fn bool QTextEdit::underline() const
+
+ Use fontUnderline() instead.
+*/
+
+/*!
+ \fn QString QTextEdit::family() const
+
+ Use fontFamily() instead.
+*/
+
+/*!
+ \fn int QTextEdit::pointSize() const
+
+ Use int(fontPointSize()+0.5) instead.
+*/
+
+/*!
+ \fn bool QTextEdit::hasSelectedText() const
+
+ Use textCursor().hasSelection() instead.
+*/
+
+/*!
+ \fn QString QTextEdit::selectedText() const
+
+ Use textCursor().selectedText() instead.
+*/
+
+/*!
+ \fn bool QTextEdit::isUndoAvailable() const
+
+ Use document()->isUndoAvailable() instead.
+*/
+
+/*!
+ \fn bool QTextEdit::isRedoAvailable() const
+
+ Use document()->isRedoAvailable() instead.
+*/
+
+/*!
+ \fn void QTextEdit::insert(const QString &text)
+
+ Use insertPlainText() instead.
+*/
+
+/*!
+ \fn bool QTextEdit::isModified() const
+
+ Use document()->isModified() instead.
+*/
+
+/*!
+ \fn QColor QTextEdit::color() const
+
+ Use textColor() instead.
+*/
+
+/*!
+ \fn void QTextEdit::textChanged()
+
+ This signal is emitted whenever the document's content changes; for
+ example, when text is inserted or deleted, or when formatting is applied.
+*/
+
+/*!
+ \fn void QTextEdit::undoAvailable(bool available)
+
+ This signal is emitted whenever undo operations become available
+ (\a available is true) or unavailable (\a available is false).
+*/
+
+/*!
+ \fn void QTextEdit::redoAvailable(bool available)
+
+ This signal is emitted whenever redo operations become available
+ (\a available is true) or unavailable (\a available is false).
+*/
+
+/*!
+ \fn void QTextEdit::currentFontChanged(const QFont &font)
+
+ Use currentCharFormatChanged() instead.
+*/
+
+/*!
+ \fn void QTextEdit::currentColorChanged(const QColor &color)
+
+ Use currentCharFormatChanged() instead.
+*/
+
+/*!
+ \fn void QTextEdit::setModified(bool m)
+
+ Use document->setModified() instead.
+*/
+
+/*!
+ \fn void QTextEdit::setColor(const QColor &color)
+
+ Use setTextColor() instead.
+*/
+#endif // QT_NO_TEXTEDIT
+
+QT_END_NAMESPACE
+
+#include "moc_qtextedit.cpp"
diff --git a/src/gui/widgets/qtextedit.h b/src/gui/widgets/qtextedit.h
new file mode 100644
index 0000000000..0835cc1973
--- /dev/null
+++ b/src/gui/widgets/qtextedit.h
@@ -0,0 +1,430 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QTEXTEDIT_H
+#define QTEXTEDIT_H
+
+#include <QtGui/qabstractscrollarea.h>
+#include <QtGui/qtextdocument.h>
+#include <QtGui/qtextoption.h>
+#include <QtGui/qtextcursor.h>
+#include <QtGui/qtextformat.h>
+
+#ifndef QT_NO_TEXTEDIT
+
+#ifdef QT3_SUPPORT
+#include <QtGui/qtextobject.h>
+#include <QtGui/qtextlayout.h>
+#endif
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+class QStyleSheet;
+class QTextDocument;
+class QMenu;
+class QTextEditPrivate;
+class QMimeData;
+
+class Q_GUI_EXPORT QTextEdit : public QAbstractScrollArea
+{
+ Q_OBJECT
+ Q_DECLARE_PRIVATE(QTextEdit)
+ Q_FLAGS(AutoFormatting)
+ Q_ENUMS(LineWrapMode)
+ Q_PROPERTY(AutoFormatting autoFormatting READ autoFormatting WRITE setAutoFormatting)
+ Q_PROPERTY(bool tabChangesFocus READ tabChangesFocus WRITE setTabChangesFocus)
+ Q_PROPERTY(QString documentTitle READ documentTitle WRITE setDocumentTitle)
+ Q_PROPERTY(bool undoRedoEnabled READ isUndoRedoEnabled WRITE setUndoRedoEnabled)
+ Q_PROPERTY(LineWrapMode lineWrapMode READ lineWrapMode WRITE setLineWrapMode)
+ QDOC_PROPERTY(QTextOption::WrapMode wordWrapMode READ wordWrapMode WRITE setWordWrapMode)
+ Q_PROPERTY(int lineWrapColumnOrWidth READ lineWrapColumnOrWidth WRITE setLineWrapColumnOrWidth)
+ Q_PROPERTY(bool readOnly READ isReadOnly WRITE setReadOnly)
+#ifndef QT_NO_TEXTHTMLPARSER
+ Q_PROPERTY(QString html READ toHtml WRITE setHtml NOTIFY textChanged USER true)
+#endif
+ Q_PROPERTY(QString plainText READ toPlainText WRITE setPlainText DESIGNABLE false)
+ Q_PROPERTY(bool overwriteMode READ overwriteMode WRITE setOverwriteMode)
+ Q_PROPERTY(int tabStopWidth READ tabStopWidth WRITE setTabStopWidth)
+ Q_PROPERTY(bool acceptRichText READ acceptRichText WRITE setAcceptRichText)
+ Q_PROPERTY(int cursorWidth READ cursorWidth WRITE setCursorWidth)
+ Q_PROPERTY(Qt::TextInteractionFlags textInteractionFlags READ textInteractionFlags WRITE setTextInteractionFlags)
+public:
+ enum LineWrapMode {
+ NoWrap,
+ WidgetWidth,
+ FixedPixelWidth,
+ FixedColumnWidth
+ };
+
+ enum AutoFormattingFlag {
+ AutoNone = 0,
+ AutoBulletList = 0x00000001,
+ AutoAll = 0xffffffff
+ };
+
+ Q_DECLARE_FLAGS(AutoFormatting, AutoFormattingFlag)
+
+#if defined(QT3_SUPPORT)
+ enum CursorAction {
+ MoveBackward,
+ MoveForward,
+ MoveWordBackward,
+ MoveWordForward,
+ MoveUp,
+ MoveDown,
+ MoveLineStart,
+ MoveLineEnd,
+ MoveHome,
+ MoveEnd,
+ MovePageUp,
+ MovePageDown
+#if !defined(Q_MOC_RUN)
+ ,
+ MovePgUp = MovePageUp,
+ MovePgDown = MovePageDown
+#endif
+ };
+#endif
+
+ explicit QTextEdit(QWidget *parent = 0);
+ explicit QTextEdit(const QString &text, QWidget *parent = 0);
+ virtual ~QTextEdit();
+
+ void setDocument(QTextDocument *document);
+ QTextDocument *document() const;
+
+ void setTextCursor(const QTextCursor &cursor);
+ QTextCursor textCursor() const;
+
+ bool isReadOnly() const;
+ void setReadOnly(bool ro);
+
+ void setTextInteractionFlags(Qt::TextInteractionFlags flags);
+ Qt::TextInteractionFlags textInteractionFlags() const;
+
+ qreal fontPointSize() const;
+ QString fontFamily() const;
+ int fontWeight() const;
+ bool fontUnderline() const;
+ bool fontItalic() const;
+ QColor textColor() const;
+ QColor textBackgroundColor() const;
+ QFont currentFont() const;
+ Qt::Alignment alignment() const;
+
+ void mergeCurrentCharFormat(const QTextCharFormat &modifier);
+
+ void setCurrentCharFormat(const QTextCharFormat &format);
+ QTextCharFormat currentCharFormat() const;
+
+ AutoFormatting autoFormatting() const;
+ void setAutoFormatting(AutoFormatting features);
+
+ bool tabChangesFocus() const;
+ void setTabChangesFocus(bool b);
+
+ inline void setDocumentTitle(const QString &title)
+ { document()->setMetaInformation(QTextDocument::DocumentTitle, title); }
+ inline QString documentTitle() const
+ { return document()->metaInformation(QTextDocument::DocumentTitle); }
+
+ inline bool isUndoRedoEnabled() const
+ { return document()->isUndoRedoEnabled(); }
+ inline void setUndoRedoEnabled(bool enable)
+ { document()->setUndoRedoEnabled(enable); }
+
+ LineWrapMode lineWrapMode() const;
+ void setLineWrapMode(LineWrapMode mode);
+
+ int lineWrapColumnOrWidth() const;
+ void setLineWrapColumnOrWidth(int w);
+
+ QTextOption::WrapMode wordWrapMode() const;
+ void setWordWrapMode(QTextOption::WrapMode policy);
+
+ bool find(const QString &exp, QTextDocument::FindFlags options = 0);
+
+ inline QString toPlainText() const
+ { return document()->toPlainText(); }
+#ifndef QT_NO_TEXTHTMLPARSER
+ inline QString toHtml() const
+ { return document()->toHtml(); }
+#endif
+
+ void ensureCursorVisible();
+
+ virtual QVariant loadResource(int type, const QUrl &name);
+#ifndef QT_NO_CONTEXTMENU
+ QMenu *createStandardContextMenu();
+ QMenu *createStandardContextMenu(const QPoint &position);
+#endif
+
+ QTextCursor cursorForPosition(const QPoint &pos) const;
+ QRect cursorRect(const QTextCursor &cursor) const;
+ QRect cursorRect() const;
+
+ QString anchorAt(const QPoint& pos) const;
+
+ bool overwriteMode() const;
+ void setOverwriteMode(bool overwrite);
+
+ int tabStopWidth() const;
+ void setTabStopWidth(int width);
+
+ int cursorWidth() const;
+ void setCursorWidth(int width);
+
+ bool acceptRichText() const;
+ void setAcceptRichText(bool accept);
+
+ struct ExtraSelection
+ {
+ QTextCursor cursor;
+ QTextCharFormat format;
+ };
+ void setExtraSelections(const QList<ExtraSelection> &selections);
+ QList<ExtraSelection> extraSelections() const;
+
+ void moveCursor(QTextCursor::MoveOperation operation, QTextCursor::MoveMode mode = QTextCursor::MoveAnchor);
+
+ bool canPaste() const;
+
+#ifndef QT_NO_PRINTER
+ void print(QPrinter *printer) const;
+#endif
+
+public Q_SLOTS:
+ void setFontPointSize(qreal s);
+ void setFontFamily(const QString &fontFamily);
+ void setFontWeight(int w);
+ void setFontUnderline(bool b);
+ void setFontItalic(bool b);
+ void setTextColor(const QColor &c);
+ void setTextBackgroundColor(const QColor &c);
+ void setCurrentFont(const QFont &f);
+ void setAlignment(Qt::Alignment a);
+
+ void setPlainText(const QString &text);
+#ifndef QT_NO_TEXTHTMLPARSER
+ void setHtml(const QString &text);
+#endif
+ void setText(const QString &text);
+
+#ifndef QT_NO_CLIPBOARD
+ void cut();
+ void copy();
+ void paste();
+#endif
+
+ void undo();
+ void redo();
+
+ void clear();
+ void selectAll();
+
+ void insertPlainText(const QString &text);
+#ifndef QT_NO_TEXTHTMLPARSER
+ void insertHtml(const QString &text);
+#endif // QT_NO_TEXTHTMLPARSER
+
+ void append(const QString &text);
+
+ void scrollToAnchor(const QString &name);
+
+ void zoomIn(int range = 1);
+ void zoomOut(int range = 1);
+
+Q_SIGNALS:
+ void textChanged();
+ void undoAvailable(bool b);
+ void redoAvailable(bool b);
+ void currentCharFormatChanged(const QTextCharFormat &format);
+ void copyAvailable(bool b);
+ void selectionChanged();
+ void cursorPositionChanged();
+
+protected:
+ virtual bool event(QEvent *e);
+ virtual void timerEvent(QTimerEvent *e);
+ virtual void keyPressEvent(QKeyEvent *e);
+ virtual void keyReleaseEvent(QKeyEvent *e);
+ virtual void resizeEvent(QResizeEvent *e);
+ virtual void paintEvent(QPaintEvent *e);
+ virtual void mousePressEvent(QMouseEvent *e);
+ virtual void mouseMoveEvent(QMouseEvent *e);
+ virtual void mouseReleaseEvent(QMouseEvent *e);
+ virtual void mouseDoubleClickEvent(QMouseEvent *e);
+ virtual bool focusNextPrevChild(bool next);
+#ifndef QT_NO_CONTEXTMENU
+ virtual void contextMenuEvent(QContextMenuEvent *e);
+#endif
+#ifndef QT_NO_DRAGANDDROP
+ virtual void dragEnterEvent(QDragEnterEvent *e);
+ virtual void dragLeaveEvent(QDragLeaveEvent *e);
+ virtual void dragMoveEvent(QDragMoveEvent *e);
+ virtual void dropEvent(QDropEvent *e);
+#endif
+ virtual void focusInEvent(QFocusEvent *e);
+ virtual void focusOutEvent(QFocusEvent *e);
+ virtual void showEvent(QShowEvent *);
+ virtual void changeEvent(QEvent *e);
+#ifndef QT_NO_WHEELEVENT
+ virtual void wheelEvent(QWheelEvent *e);
+#endif
+
+ virtual QMimeData *createMimeDataFromSelection() const;
+ virtual bool canInsertFromMimeData(const QMimeData *source) const;
+ virtual void insertFromMimeData(const QMimeData *source);
+
+ virtual void inputMethodEvent(QInputMethodEvent *);
+ QVariant inputMethodQuery(Qt::InputMethodQuery property) const;
+
+ QTextEdit(QTextEditPrivate &dd, QWidget *parent);
+
+ virtual void scrollContentsBy(int dx, int dy);
+
+#ifdef QT3_SUPPORT
+Q_SIGNALS:
+ QT_MOC_COMPAT void currentFontChanged(const QFont &f);
+ QT_MOC_COMPAT void currentColorChanged(const QColor &c);
+
+public:
+ QT3_SUPPORT_CONSTRUCTOR QTextEdit(QWidget *parent, const char *name);
+ inline QT3_SUPPORT bool find(const QString &exp, bool cs, bool wo)
+ {
+ QTextDocument::FindFlags flags = 0;
+ if (cs)
+ flags |= QTextDocument::FindCaseSensitively;
+ if (wo)
+ flags |= QTextDocument::FindWholeWords;
+ return find(exp, flags);
+ }
+
+ inline QT3_SUPPORT void sync() {}
+
+ QT3_SUPPORT void moveCursor(CursorAction action, QTextCursor::MoveMode mode = QTextCursor::MoveAnchor);
+ QT3_SUPPORT void moveCursor(CursorAction action, bool select);
+
+ enum KeyboardAction {
+ ActionBackspace,
+ ActionDelete,
+ ActionReturn,
+ ActionKill,
+ ActionWordBackspace,
+ ActionWordDelete
+ };
+
+ QT3_SUPPORT void doKeyboardAction(KeyboardAction action);
+
+ QT3_SUPPORT QString text() const;
+ QT3_SUPPORT void setTextFormat(Qt::TextFormat);
+ QT3_SUPPORT Qt::TextFormat textFormat() const;
+
+ inline QT3_SUPPORT void setBold(bool b) { setFontWeight(b ? QFont::Bold : QFont::Normal); }
+ inline QT3_SUPPORT void setUnderline(bool b) { setFontUnderline(b); }
+ inline QT3_SUPPORT void setItalic(bool i) { setFontItalic(i); }
+ inline QT3_SUPPORT void setFamily(const QString &family) { setFontFamily(family); }
+ inline QT3_SUPPORT void setPointSize(int size) { setFontPointSize(size); }
+
+ inline QT3_SUPPORT bool italic() const { return fontItalic(); }
+ inline QT3_SUPPORT bool bold() const { return fontWeight() >= QFont::Bold; }
+ inline QT3_SUPPORT bool underline() const { return fontUnderline(); }
+ inline QT3_SUPPORT QString family() const { return fontFamily(); }
+ inline QT3_SUPPORT int pointSize() const { return (int)(fontPointSize()+0.5); }
+
+ inline QT3_SUPPORT bool hasSelectedText() const
+ { return textCursor().hasSelection(); }
+ inline QT3_SUPPORT QString selectedText() const
+ { return textCursor().selectedText(); }
+
+ inline QT3_SUPPORT bool isUndoAvailable() const
+ { return document()->isUndoAvailable(); }
+ inline QT3_SUPPORT bool isRedoAvailable() const
+ { return document()->isRedoAvailable(); }
+
+ inline QT3_SUPPORT void insert(const QString &text)
+ { insertPlainText(text); }
+
+ inline QT3_SUPPORT bool isModified() const
+ { return document()->isModified(); }
+
+ inline QT3_SUPPORT QColor color() const
+ { return textColor(); }
+
+public Q_SLOTS:
+ inline QT_MOC_COMPAT void setModified(bool m = true)
+ { document()->setModified(m); }
+public:
+ inline QT3_SUPPORT void undo() const
+ { document()->undo(); }
+ inline QT3_SUPPORT void redo() const
+ { document()->redo(); }
+
+public Q_SLOTS:
+ inline QT_MOC_COMPAT void setColor(const QColor &c)
+ { setTextColor(c); }
+
+#endif
+
+private:
+ Q_DISABLE_COPY(QTextEdit)
+ Q_PRIVATE_SLOT(d_func(), void _q_repaintContents(const QRectF &r))
+ Q_PRIVATE_SLOT(d_func(), void _q_currentCharFormatChanged(const QTextCharFormat &))
+ Q_PRIVATE_SLOT(d_func(), void _q_adjustScrollbars())
+ Q_PRIVATE_SLOT(d_func(), void _q_ensureVisible(const QRectF &))
+ friend class QTextEditControl;
+ friend class QTextDocument;
+ friend class QTextControl;
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(QTextEdit::AutoFormatting)
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QT_NO_TEXTEDIT
+
+#endif // QTEXTEDIT_H
diff --git a/src/gui/widgets/qtextedit_p.h b/src/gui/widgets/qtextedit_p.h
new file mode 100644
index 0000000000..3c37868c3e
--- /dev/null
+++ b/src/gui/widgets/qtextedit_p.h
@@ -0,0 +1,141 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QTEXTEDIT_P_H
+#define QTEXTEDIT_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/qabstractscrollarea_p.h"
+#include "QtGui/qtextdocumentfragment.h"
+#include "QtGui/qscrollbar.h"
+#include "QtGui/qtextcursor.h"
+#include "QtGui/qtextformat.h"
+#include "QtGui/qmenu.h"
+#include "QtGui/qabstracttextdocumentlayout.h"
+#include "QtCore/qbasictimer.h"
+#include "QtCore/qurl.h"
+#include "private/qtextcontrol_p.h"
+#include "qtextedit.h"
+
+QT_BEGIN_NAMESPACE
+
+#ifndef QT_NO_TEXTEDIT
+
+class QMimeData;
+
+class QTextEditPrivate : public QAbstractScrollAreaPrivate
+{
+ Q_DECLARE_PUBLIC(QTextEdit)
+public:
+ QTextEditPrivate();
+
+ void init(const QString &html = QString());
+ void paint(QPainter *p, QPaintEvent *e);
+ void _q_repaintContents(const QRectF &contentsRect);
+
+ inline QPoint mapToContents(const QPoint &point) const
+ { return QPoint(point.x() + horizontalOffset(), point.y() + verticalOffset()); }
+
+ void _q_adjustScrollbars();
+ void _q_ensureVisible(const QRectF &rect);
+ void relayoutDocument();
+
+ void createAutoBulletList();
+ void pageUpDown(QTextCursor::MoveOperation op, QTextCursor::MoveMode moveMode);
+
+ inline int horizontalOffset() const
+ { return q_func()->isRightToLeft() ? (hbar->maximum() - hbar->value()) : hbar->value(); }
+ inline int verticalOffset() const
+ { return vbar->value(); }
+
+ inline void sendControlEvent(QEvent *e)
+ { control->processEvent(e, QPointF(horizontalOffset(), verticalOffset()), viewport); }
+
+ void _q_currentCharFormatChanged(const QTextCharFormat &format);
+
+ void updateDefaultTextOption();
+
+ // re-implemented by QTextBrowser, called by QTextDocument::loadResource
+ virtual QUrl resolveUrl(const QUrl &url) const
+ { return url; }
+
+ QTextControl *control;
+
+ QTextEdit::AutoFormatting autoFormatting;
+ bool tabChangesFocus;
+
+ QBasicTimer autoScrollTimer;
+ QPoint autoScrollDragPos;
+
+ QTextEdit::LineWrapMode lineWrap;
+ int lineWrapColumnOrWidth;
+ QTextOption::WrapMode wordWrap;
+
+ uint ignoreAutomaticScrollbarAdjustment : 1;
+ uint preferRichText : 1;
+ uint showCursorOnInitialShow : 1;
+ uint inDrag : 1;
+
+ // Qt3 COMPAT only, for setText
+ Qt::TextFormat textFormat;
+
+ QString anchorToScrollToWhenVisible;
+
+#ifdef QT_KEYPAD_NAVIGATION
+ QBasicTimer deleteAllTimer;
+#endif
+};
+#endif // QT_NO_TEXTEDIT
+
+
+QT_END_NAMESPACE
+
+#endif // QTEXTEDIT_P_H
diff --git a/src/gui/widgets/qtoolbar.cpp b/src/gui/widgets/qtoolbar.cpp
new file mode 100644
index 0000000000..85d6ea23d9
--- /dev/null
+++ b/src/gui/widgets/qtoolbar.cpp
@@ -0,0 +1,1291 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qtoolbar.h"
+
+#ifndef QT_NO_TOOLBAR
+
+#include <qapplication.h>
+#include <qcombobox.h>
+#include <qevent.h>
+#include <qlayout.h>
+#include <qmainwindow.h>
+#include <qmenu.h>
+#include <qmenubar.h>
+#include <qrubberband.h>
+#include <qsignalmapper.h>
+#include <qstylepainter.h>
+#include <qtoolbutton.h>
+#include <qwidgetaction.h>
+#include <qtimer.h>
+#include <private/qwidgetaction_p.h>
+#ifdef Q_WS_MAC
+#include <private/qt_mac_p.h>
+#include <private/qt_cocoa_helpers_mac_p.h>
+#endif
+
+#include <private/qmainwindowlayout_p.h>
+
+#include "qtoolbar_p.h"
+#include "qtoolbarseparator_p.h"
+#include "qtoolbarlayout_p.h"
+
+#include "qdebug.h"
+
+QT_BEGIN_NAMESPACE
+
+#ifdef Q_WS_MAC
+static void qt_mac_updateToolBarButtonHint(QWidget *parentWidget)
+{
+ if (!(parentWidget->windowFlags() & Qt::CustomizeWindowHint))
+ parentWidget->setWindowFlags(parentWidget->windowFlags() | Qt::MacWindowToolBarButtonHint);
+}
+#endif
+
+/******************************************************************************
+** QToolBarPrivate
+*/
+
+void QToolBarPrivate::init()
+{
+ Q_Q(QToolBar);
+
+ waitForPopupTimer = new QTimer(q);
+ waitForPopupTimer->setSingleShot(false);
+ waitForPopupTimer->setInterval(500);
+ QObject::connect(waitForPopupTimer, SIGNAL(timeout()), q, SLOT(_q_waitForPopup()));
+
+ floatable = true;
+ movable = true;
+ q->setSizePolicy(QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed));
+ q->setBackgroundRole(QPalette::Button);
+ q->setAttribute(Qt::WA_Hover);
+ q->setAttribute(Qt::WA_X11NetWmWindowTypeToolBar);
+
+ QStyle *style = q->style();
+ int e = style->pixelMetric(QStyle::PM_ToolBarIconSize, 0, q);
+ iconSize = QSize(e, e);
+
+ layout = new QToolBarLayout(q);
+ layout->updateMarginAndSpacing();
+
+#ifdef Q_WS_MAC
+ if (q->parentWidget() && q->parentWidget()->isWindow()) {
+ // Make sure that the window has the "toolbar" button.
+ QWidget *parentWidget = q->parentWidget();
+ qt_mac_updateToolBarButtonHint(parentWidget);
+ reinterpret_cast<QToolBar *>(parentWidget)->d_func()->createWinId(); // Please let me create your winId...
+ extern OSWindowRef qt_mac_window_for(const QWidget *); // qwidget_mac.cpp
+ macWindowToolbarShow(q->parentWidget(), true);
+ }
+#endif
+
+ toggleViewAction = new QAction(q);
+ toggleViewAction->setCheckable(true);
+ q->setMovable(q->style()->styleHint(QStyle::SH_ToolBar_Movable, 0, q ));
+ QObject::connect(toggleViewAction, SIGNAL(triggered(bool)), q, SLOT(_q_toggleView(bool)));
+}
+
+void QToolBarPrivate::_q_toggleView(bool b)
+{
+ Q_Q(QToolBar);
+ if (b == q->isHidden()) {
+ if (b)
+ q->show();
+ else
+ q->close();
+ }
+}
+
+void QToolBarPrivate::_q_updateIconSize(const QSize &sz)
+{
+ Q_Q(QToolBar);
+ if (!explicitIconSize) {
+ // iconSize not explicitly set
+ q->setIconSize(sz);
+ explicitIconSize = false;
+ }
+}
+
+void QToolBarPrivate::_q_updateToolButtonStyle(Qt::ToolButtonStyle style)
+{
+ Q_Q(QToolBar);
+ if (!explicitToolButtonStyle) {
+ q->setToolButtonStyle(style);
+ explicitToolButtonStyle = false;
+ }
+}
+
+void QToolBarPrivate::updateWindowFlags(bool floating, bool unplug)
+{
+ Q_Q(QToolBar);
+ Qt::WindowFlags flags = floating ? Qt::Tool : Qt::Widget;
+
+ flags |= Qt::FramelessWindowHint;
+
+ if (unplug) {
+ flags |= Qt::X11BypassWindowManagerHint;
+#ifdef Q_WS_MAC
+ flags |= Qt::WindowStaysOnTopHint;
+#endif
+ }
+
+ q->setWindowFlags(flags);
+}
+
+void QToolBarPrivate::setWindowState(bool floating, bool unplug, const QRect &rect)
+{
+ Q_Q(QToolBar);
+ bool visible = !q->isHidden();
+ bool wasFloating = q->isFloating(); // ...is also currently using popup menus
+
+ q->hide();
+
+ updateWindowFlags(floating, unplug);
+
+ if (floating != wasFloating)
+ layout->checkUsePopupMenu();
+
+ if (!rect.isNull())
+ q->setGeometry(rect);
+
+ if (visible)
+ q->show();
+}
+
+void QToolBarPrivate::initDrag(const QPoint &pos)
+{
+ Q_Q(QToolBar);
+
+ if (state != 0)
+ return;
+
+ QMainWindow *win = qobject_cast<QMainWindow*>(q->parentWidget());
+ Q_ASSERT(win != 0);
+ QMainWindowLayout *layout = qobject_cast<QMainWindowLayout*>(win->layout());
+ Q_ASSERT(layout != 0);
+ if (layout->pluggingWidget != 0) // the main window is animating a docking operation
+ return;
+
+ state = new DragState;
+ state->pressPos = pos;
+ state->dragging = false;
+ state->moving = false;
+ state->widgetItem = 0;
+
+ if (q->layoutDirection() == Qt::RightToLeft)
+ state->pressPos = QPoint(q->width() - state->pressPos.x(), state->pressPos.y());
+}
+
+void QToolBarPrivate::startDrag(bool moving)
+{
+ Q_Q(QToolBar);
+
+ Q_ASSERT(state != 0);
+
+ if ((moving && state->moving) || state->dragging)
+ return;
+
+ QMainWindow *win = qobject_cast<QMainWindow*>(q->parentWidget());
+ Q_ASSERT(win != 0);
+ QMainWindowLayout *layout = qobject_cast<QMainWindowLayout*>(win->layout());
+ Q_ASSERT(layout != 0);
+
+ if (!moving) {
+ state->widgetItem = layout->unplug(q);
+#if defined(Q_WS_MAC) && !defined(QT_MAC_USE_COCOA)
+ if (q->isWindow()) {
+ setWindowState(true, true); //set it to floating
+ }
+#endif
+ Q_ASSERT(state->widgetItem != 0);
+ }
+ state->dragging = !moving;
+ state->moving = moving;
+}
+
+void QToolBarPrivate::endDrag()
+{
+ Q_Q(QToolBar);
+ Q_ASSERT(state != 0);
+
+ q->releaseMouse();
+
+ if (state->dragging) {
+ QMainWindowLayout *layout =
+ qobject_cast<QMainWindowLayout *>(q->parentWidget()->layout());
+ Q_ASSERT(layout != 0);
+
+ if (!layout->plug(state->widgetItem)) {
+ if (q->isFloatable()) {
+ layout->restore();
+#if defined(Q_WS_X11) || defined(Q_WS_MAC)
+ setWindowState(true); // gets rid of the X11BypassWindowManager window flag
+ // and activates the resizer
+#endif
+ q->activateWindow();
+ } else {
+ layout->revert(state->widgetItem);
+ }
+ }
+ }
+
+ delete state;
+ state = 0;
+}
+
+bool QToolBarPrivate::mousePressEvent(QMouseEvent *event)
+{
+ if (layout->handleRect().contains(event->pos()) == false) {
+#ifdef Q_WS_MAC
+ Q_Q(QToolBar);
+ // When using the unified toolbar on Mac OS X the user can can click and
+ // drag between toolbar contents to move the window. Make this work by
+ // implementing the standard mouse-dragging code and then call
+ // window->move() in mouseMoveEvent below.
+ if (QMainWindow *mainWindow = qobject_cast<QMainWindow *>(q->parentWidget())) {
+ if (mainWindow->toolBarArea(q) == Qt::TopToolBarArea
+ && mainWindow->unifiedTitleAndToolBarOnMac()
+ && q->childAt(event->pos()) == 0) {
+ macWindowDragging = true;
+ macWindowDragPressPosition = event->pos();
+ return true;
+ }
+ }
+#endif
+ return false;
+ }
+
+ if (event->button() != Qt::LeftButton)
+ return true;
+
+ if (!layout->movable())
+ return true;
+
+ initDrag(event->pos());
+ return true;
+}
+
+bool QToolBarPrivate::mouseReleaseEvent(QMouseEvent*)
+{
+ if (state != 0) {
+ endDrag();
+ return true;
+ } else {
+#ifdef Q_WS_MAC
+ macWindowDragging = false;
+ macWindowDragPressPosition = QPoint();
+ return true;
+#endif
+ return false;
+ }
+}
+
+bool QToolBarPrivate::mouseMoveEvent(QMouseEvent *event)
+{
+ Q_Q(QToolBar);
+
+ if (!state) {
+#ifdef Q_WS_MAC
+ if (!macWindowDragging)
+ return false;
+ QWidget *w = q->window();
+ const QPoint delta = event->pos() - macWindowDragPressPosition;
+ w->move(w->pos() + delta);
+ return true;
+#endif
+ return false;
+ }
+
+ QMainWindow *win = qobject_cast<QMainWindow*>(q->parentWidget());
+ if (win == 0)
+ return true;
+
+ QMainWindowLayout *layout = qobject_cast<QMainWindowLayout*>(win->layout());
+ Q_ASSERT(layout != 0);
+
+ if (layout->pluggingWidget == 0
+ && (event->pos() - state->pressPos).manhattanLength() > QApplication::startDragDistance()) {
+ const bool wasDragging = state->dragging;
+ const bool moving = !q->isWindow() && (orientation == Qt::Vertical ?
+ event->x() >= 0 && event->x() < q->width() :
+ event->y() >= 0 && event->y() < q->height());
+
+ startDrag(moving);
+ if (!moving && !wasDragging) {
+#ifdef Q_OS_WIN
+ grabMouseWhileInWindow();
+#else
+ q->grabMouse();
+#endif
+ }
+ }
+
+ if (state->dragging) {
+ QPoint pos = event->globalPos();
+ // if we are right-to-left, we move so as to keep the right edge the same distance
+ // from the mouse
+ if (q->layoutDirection() == Qt::LeftToRight)
+ pos -= state->pressPos;
+ else
+ pos += QPoint(state->pressPos.x() - q->width(), -state->pressPos.y());
+
+ q->move(pos);
+ layout->hover(state->widgetItem, event->globalPos());
+ } else if (state->moving) {
+
+ const QPoint rtl(q->width() - state->pressPos.x(), state->pressPos.y()); //for RTL
+ const QPoint globalPressPos = q->mapToGlobal(q->layoutDirection() == Qt::RightToLeft ? rtl : state->pressPos);
+ int pos = 0;
+
+ QPoint delta = event->globalPos() - globalPressPos;
+ if (orientation == Qt::Vertical) {
+ pos = q->y() + delta.y();
+ } else {
+ if (q->layoutDirection() == Qt::RightToLeft) {
+ pos = win->width() - q->width() - q->x() - delta.x();
+ } else {
+ pos = q->x() + delta.x();
+ }
+ }
+
+ layout->moveToolBar(q, pos);
+ }
+ return true;
+}
+
+void QToolBarPrivate::unplug(const QRect &_r)
+{
+ Q_Q(QToolBar);
+
+ QRect r = _r;
+ r.moveTopLeft(q->mapToGlobal(QPoint(0, 0)));
+ setWindowState(true, true, r);
+}
+
+void QToolBarPrivate::plug(const QRect &r)
+{
+ setWindowState(false, false, r);
+}
+
+/******************************************************************************
+** QToolBar
+*/
+
+/*!
+ \class QToolBar
+
+ \brief The QToolBar class provides a movable panel that contains a
+ set of controls.
+
+ \ingroup application
+ \mainclass
+
+ Toolbar buttons are added by adding \e actions, using addAction()
+ or insertAction(). Groups of buttons can be separated using
+ addSeparator() or insertSeparator(). If a toolbar button is not
+ appropriate, a widget can be inserted instead using addWidget() or
+ insertWidget(); examples of suitable widgets are QSpinBox,
+ QDoubleSpinBox, and QComboBox. When a toolbar button is pressed it
+ emits the actionTriggered() signal.
+
+ A toolbar can be fixed in place in a particular area (e.g. at the
+ top of the window), or it can be movable (isMovable()) between
+ toolbar areas; see allowedAreas() and isAreaAllowed().
+
+ When a toolbar is resized in such a way that it is too small to
+ show all the items it contains, an extension button will appear as
+ the last item in the toolbar. Pressing the extension button will
+ pop up a menu containing the items that does not currently fit in
+ the toolbar.
+
+ When a QToolBar is not a child of a QMainWindow, it looses the ability
+ to populate the extension pop up with widgets added to the toolbar using
+ addWidget(). Please use widget actions created by inheriting QWidgetAction
+ and implementing QWidgetAction::createWidget() instead. This is a known
+ issue which will be fixed in a future release.
+
+ \sa QToolButton, QMenu, QAction, {Application Example}
+*/
+
+/*!
+ \fn bool QToolBar::isAreaAllowed(Qt::ToolBarArea area) const
+
+ Returns true if this toolbar is dockable in the given \a area;
+ otherwise returns false.
+*/
+
+/*!
+ \fn void QToolBar::addAction(QAction *action)
+ \overload
+
+ Appends the action \a action to the toolbar's list of actions.
+
+ \sa QMenu::addAction(), QWidget::addAction()
+*/
+
+/*!
+ \fn void QToolBar::actionTriggered(QAction *action)
+
+ This signal is emitted when an action in this toolbar is triggered.
+ This happens when the action's tool button is pressed, or when the
+ action is triggered in some other way outside the tool bar. The parameter
+ holds the triggered \a action.
+*/
+
+/*!
+ \fn void QToolBar::allowedAreasChanged(Qt::ToolBarAreas allowedAreas)
+
+ This signal is emitted when the collection of allowed areas for the
+ toolbar is changed. The new areas in which the toolbar can be positioned
+ are specified by \a allowedAreas.
+
+ \sa allowedAreas
+*/
+
+/*!
+ \fn void QToolBar::iconSizeChanged(const QSize &iconSize)
+
+ This signal is emitted when the icon size is changed. The \a
+ iconSize parameter holds the toolbar's new icon size.
+
+ \sa iconSize QMainWindow::iconSize
+*/
+
+/*!
+ \fn void QToolBar::movableChanged(bool movable)
+
+ This signal is emitted when the toolbar becomes movable or fixed.
+ If the toolbar can be moved, \a movable is true; otherwise it is
+ false.
+
+ \sa movable
+*/
+
+/*!
+ \fn void QToolBar::orientationChanged(Qt::Orientation orientation)
+
+ This signal is emitted when the orientation of the toolbar changes.
+ The new orientation is specified by the \a orientation given.
+
+ \sa orientation
+*/
+
+/*!
+ \fn void QToolBar::toolButtonStyleChanged(Qt::ToolButtonStyle toolButtonStyle)
+
+ This signal is emitted when the tool button style is changed. The
+ \a toolButtonStyle parameter holds the toolbar's new tool button
+ style.
+
+ \sa toolButtonStyle QMainWindow::toolButtonStyle
+*/
+
+/*!
+ Constructs a QToolBar with the given \a parent.
+*/
+QToolBar::QToolBar(QWidget *parent)
+ : QWidget(*new QToolBarPrivate, parent, 0)
+{
+ Q_D(QToolBar);
+ d->init();
+}
+
+/*!
+ Constructs a QToolBar with the given \a parent.
+
+ The given window \a title identifies the toolbar and is shown in
+ the context menu provided by QMainWindow.
+
+ \sa setWindowTitle()
+*/
+QToolBar::QToolBar(const QString &title, QWidget *parent)
+ : QWidget(*new QToolBarPrivate, parent, 0)
+{
+ Q_D(QToolBar);
+ d->init();
+ setWindowTitle(title);
+}
+
+#ifdef QT3_SUPPORT
+/*! \obsolete
+ Constructs a QToolBar with the given \a parent and \a name.
+*/
+QToolBar::QToolBar(QWidget *parent, const char *name)
+ : QWidget(*new QToolBarPrivate, parent, 0)
+{
+ Q_D(QToolBar);
+ d->init();
+ setObjectName(QString::fromAscii(name));
+}
+#endif
+
+/*!
+ Destroys the toolbar.
+*/
+QToolBar::~QToolBar()
+{
+ // Remove the toolbar button if there is nothing left.
+ QMainWindow *mainwindow = qobject_cast<QMainWindow *>(parentWidget());
+ if (mainwindow) {
+#ifdef Q_WS_MAC
+ QMainWindowLayout *mainwin_layout = qobject_cast<QMainWindowLayout *>(mainwindow->layout());
+ if (mainwin_layout && mainwin_layout->layoutState.toolBarAreaLayout.isEmpty()
+ && mainwindow->testAttribute(Qt::WA_WState_Created))
+ macWindowToolbarShow(mainwindow, false);
+#endif
+ }
+}
+
+/*! \property QToolBar::movable
+ \brief whether the user can move the toolbar within the toolbar area,
+ or between toolbar areas
+
+ By default, this property is true.
+
+ This property only makes sense if the toolbar is in a
+ QMainWindow.
+
+ \sa allowedAreas
+*/
+
+void QToolBar::setMovable(bool movable)
+{
+ Q_D(QToolBar);
+ if (!movable == !d->movable)
+ return;
+ d->movable = movable;
+ d->layout->invalidate();
+ emit movableChanged(d->movable);
+}
+
+bool QToolBar::isMovable() const
+{
+ Q_D(const QToolBar);
+ return d->movable;
+}
+
+/*!
+ \property QToolBar::floatable
+ \brief whether the toolbar can be dragged and dropped as an independent window.
+
+ The default is true.
+*/
+bool QToolBar::isFloatable() const
+{
+ Q_D(const QToolBar);
+ return d->floatable;
+}
+
+void QToolBar::setFloatable(bool floatable)
+{
+ Q_D(QToolBar);
+ d->floatable = floatable;
+}
+
+/*!
+ \property QToolBar::floating
+ \brief whether the toolbar is an independent window.
+
+ By default, this property is true.
+
+ \sa QWidget::isWindow()
+*/
+bool QToolBar::isFloating() const
+{
+ return isWindow();
+}
+
+/*!
+ \property QToolBar::allowedAreas
+ \brief areas where the toolbar may be placed
+
+ The default is Qt::AllToolBarAreas.
+
+ This property only makes sense if the toolbar is in a
+ QMainWindow.
+
+ \sa movable
+*/
+
+void QToolBar::setAllowedAreas(Qt::ToolBarAreas areas)
+{
+ Q_D(QToolBar);
+ areas &= Qt::ToolBarArea_Mask;
+ if (areas == d->allowedAreas)
+ return;
+ d->allowedAreas = areas;
+ emit allowedAreasChanged(d->allowedAreas);
+}
+
+Qt::ToolBarAreas QToolBar::allowedAreas() const
+{
+ Q_D(const QToolBar);
+#ifdef Q_WS_MAC
+ if (QMainWindow *window = qobject_cast<QMainWindow *>(parentWidget())) {
+ if (window->unifiedTitleAndToolBarOnMac()) // Don't allow drags to the top (for now).
+ return (d->allowedAreas & ~Qt::TopToolBarArea);
+ }
+#endif
+ return d->allowedAreas;
+}
+
+/*! \property QToolBar::orientation
+ \brief orientation of the toolbar
+
+ The default is Qt::Horizontal.
+
+ This function should not be used when the toolbar is managed
+ by QMainWindow. You can use QMainWindow::addToolBar() or
+ QMainWindow::insertToolBar() if you wish to move a toolbar (that
+ is already added to a main window) to another Qt::ToolBarArea.
+*/
+
+void QToolBar::setOrientation(Qt::Orientation orientation)
+{
+ Q_D(QToolBar);
+ if (orientation == d->orientation)
+ return;
+
+ d->orientation = orientation;
+
+ if (orientation == Qt::Vertical)
+ setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred));
+ else
+ setSizePolicy(QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed));
+
+ d->layout->invalidate();
+ d->layout->activate();
+
+ emit orientationChanged(d->orientation);
+}
+
+Qt::Orientation QToolBar::orientation() const
+{ Q_D(const QToolBar); return d->orientation; }
+
+/*!
+ \property QToolBar::iconSize
+ \brief size of icons in the toolbar.
+
+ The default size is determined by the application's style and is
+ derived from the QStyle::PM_ToolBarIconSize pixel metric. It is
+ the maximum size an icon can have. Icons of smaller size will not
+ be scaled up.
+*/
+
+QSize QToolBar::iconSize() const
+{ Q_D(const QToolBar); return d->iconSize; }
+
+void QToolBar::setIconSize(const QSize &iconSize)
+{
+ Q_D(QToolBar);
+ QSize sz = iconSize;
+ if (!sz.isValid()) {
+ QMainWindow *mw = qobject_cast<QMainWindow *>(parentWidget());
+ if (mw && mw->layout()) {
+ QLayout *layout = mw->layout();
+ int i = 0;
+ QLayoutItem *item = 0;
+ do {
+ item = layout->itemAt(i++);
+ if (item && (item->widget() == this))
+ sz = mw->iconSize();
+ } while (!sz.isValid() && item != 0);
+ }
+ }
+ if (!sz.isValid()) {
+ const int metric = style()->pixelMetric(QStyle::PM_ToolBarIconSize, 0, this);
+ sz = QSize(metric, metric);
+ }
+ if (d->iconSize != sz) {
+ d->iconSize = sz;
+ setMinimumSize(0, 0);
+ emit iconSizeChanged(d->iconSize);
+ }
+ d->explicitIconSize = iconSize.isValid();
+
+ d->layout->invalidate();
+}
+
+/*!
+ \property QToolBar::toolButtonStyle
+ \brief the style of toolbar buttons
+
+ This property defines the style of all tool buttons that are added
+ as \l{QAction}s. Note that if you add a QToolButton with the
+ addWidget() method, it will not get this button style.
+
+ The default is Qt::ToolButtonIconOnly.
+*/
+
+Qt::ToolButtonStyle QToolBar::toolButtonStyle() const
+{ Q_D(const QToolBar); return d->toolButtonStyle; }
+
+void QToolBar::setToolButtonStyle(Qt::ToolButtonStyle toolButtonStyle)
+{
+ Q_D(QToolBar);
+ d->explicitToolButtonStyle = true;
+ if (d->toolButtonStyle == toolButtonStyle)
+ return;
+ d->toolButtonStyle = toolButtonStyle;
+ setMinimumSize(0, 0);
+ emit toolButtonStyleChanged(d->toolButtonStyle);
+}
+
+/*!
+ Removes all actions from the toolbar.
+
+ \sa removeAction()
+*/
+void QToolBar::clear()
+{
+ QList<QAction *> actions = this->actions();
+ for(int i = 0; i < actions.size(); i++)
+ removeAction(actions.at(i));
+}
+
+/*!
+ \overload
+
+ Creates a new action with the given \a text. This action is added to
+ the end of the toolbar.
+*/
+QAction *QToolBar::addAction(const QString &text)
+{
+ QAction *action = new QAction(text, this);
+ addAction(action);
+ return action;
+}
+
+/*!
+ \overload
+
+ Creates a new action with the given \a icon and \a text. This
+ action is added to the end of the toolbar.
+*/
+QAction *QToolBar::addAction(const QIcon &icon, const QString &text)
+{
+ QAction *action = new QAction(icon, text, this);
+ addAction(action);
+ return action;
+}
+
+/*!
+ \overload
+
+ Creates a new action with the given \a text. This action is added to
+ the end of the toolbar. The action's \link QAction::triggered()
+ triggered()\endlink signal is connected to \a member in \a
+ receiver.
+*/
+QAction *QToolBar::addAction(const QString &text,
+ const QObject *receiver, const char* member)
+{
+ QAction *action = new QAction(text, this);
+ QObject::connect(action, SIGNAL(triggered(bool)), receiver, member);
+ addAction(action);
+ return action;
+}
+
+/*!
+ \overload
+
+ Creates a new action with the icon \a icon and text \a text. This
+ action is added to the end of the toolbar. The action's \link
+ QAction::triggered() triggered()\endlink signal is connected to \a
+ member in \a receiver.
+*/
+QAction *QToolBar::addAction(const QIcon &icon, const QString &text,
+ const QObject *receiver, const char* member)
+{
+ QAction *action = new QAction(icon, text, this);
+ QObject::connect(action, SIGNAL(triggered(bool)), receiver, member);
+ addAction(action);
+ return action;
+}
+
+/*!
+ Adds a separator to the end of the toolbar.
+
+ \sa insertSeparator()
+*/
+QAction *QToolBar::addSeparator()
+{
+ QAction *action = new QAction(this);
+ action->setSeparator(true);
+ addAction(action);
+ return action;
+}
+
+/*!
+ Inserts a separator into the toolbar in front of the toolbar
+ item associated with the \a before action.
+
+ \sa addSeparator()
+*/
+QAction *QToolBar::insertSeparator(QAction *before)
+{
+ QAction *action = new QAction(this);
+ action->setSeparator(true);
+ insertAction(before, action);
+ return action;
+}
+
+/*!
+ Adds the given \a widget to the toolbar as the toolbar's last
+ item.
+
+ The toolbar takes ownership of \a widget.
+
+ If you add a QToolButton with this method, the tools bar's
+ Qt::ToolButtonStyle will not be respected.
+
+ Note: You should use QAction::setVisible() to change the
+ visibility of the widget. Using QWidget::setVisible(),
+ QWidget::show() and QWidget::hide() does not work.
+
+ \sa insertWidget()
+*/
+QAction *QToolBar::addWidget(QWidget *widget)
+{
+ QWidgetAction *action = new QWidgetAction(this);
+ action->setDefaultWidget(widget);
+ action->d_func()->autoCreated = true;
+ addAction(action);
+ return action;
+}
+
+/*!
+ Inserts the given \a widget in front of the toolbar item
+ associated with the \a before action.
+
+ Note: You should use QAction::setVisible() to change the
+ visibility of the widget. Using QWidget::setVisible(),
+ QWidget::show() and QWidget::hide() does not work.
+
+ \sa addWidget()
+*/
+QAction *QToolBar::insertWidget(QAction *before, QWidget *widget)
+{
+ QWidgetAction *action = new QWidgetAction(this);
+ action->setDefaultWidget(widget);
+ action->d_func()->autoCreated = true;
+ insertAction(before, action);
+ return action;
+}
+
+/*!
+ \internal
+
+ Returns the geometry of the toolbar item associated with the given
+ \a action, or an invalid QRect if no matching item is found.
+*/
+QRect QToolBar::actionGeometry(QAction *action) const
+{
+ Q_D(const QToolBar);
+
+ int index = d->layout->indexOf(action);
+ if (index == -1)
+ return QRect();
+ return d->layout->itemAt(index)->widget()->geometry();
+}
+
+/*!
+ Returns the action at point \a p. This function returns zero if no
+ action was found.
+
+ \sa QWidget::childAt()
+*/
+QAction *QToolBar::actionAt(const QPoint &p) const
+{
+ Q_D(const QToolBar);
+ QWidget *widget = childAt(p);
+ int index = d->layout->indexOf(widget);
+ if (index == -1)
+ return 0;
+ QLayoutItem *item = d->layout->itemAt(index);
+ return static_cast<QToolBarItem*>(item)->action;
+}
+
+/*! \fn QAction *QToolBar::actionAt(int x, int y) const
+ \overload
+
+ Returns the action at the point \a x, \a y. This function returns
+ zero if no action was found.
+*/
+
+/*! \reimp */
+void QToolBar::actionEvent(QActionEvent *event)
+{
+ Q_D(QToolBar);
+ QAction *action = event->action();
+ QWidgetAction *widgetAction = qobject_cast<QWidgetAction *>(action);
+
+ switch (event->type()) {
+ case QEvent::ActionAdded: {
+ Q_ASSERT_X(widgetAction == 0 || d->layout->indexOf(widgetAction) == -1,
+ "QToolBar", "widgets cannot be inserted multiple times");
+
+ // reparent the action to this toolbar if it has been created
+ // using the addAction(text) etc. convenience functions, to
+ // preserve Qt 4.1.x behavior. The widget is already
+ // reparented to us due to the createWidget call inside
+ // createItem()
+ if (widgetAction != 0 && widgetAction->d_func()->autoCreated)
+ widgetAction->setParent(this);
+
+ int index = d->layout->count();
+ if (event->before()) {
+ index = d->layout->indexOf(event->before());
+ Q_ASSERT_X(index != -1, "QToolBar::insertAction", "internal error");
+ }
+ d->layout->insertAction(index, action);
+ break;
+ }
+
+ case QEvent::ActionChanged:
+ d->layout->invalidate();
+ break;
+
+ case QEvent::ActionRemoved: {
+ int index = d->layout->indexOf(action);
+ if (index != -1) {
+ delete d->layout->takeAt(index);
+ }
+ break;
+ }
+
+ default:
+ Q_ASSERT_X(false, "QToolBar::actionEvent", "internal error");
+ }
+}
+
+/*! \reimp */
+void QToolBar::changeEvent(QEvent *event)
+{
+ Q_D(QToolBar);
+ switch (event->type()) {
+ case QEvent::WindowTitleChange:
+ d->toggleViewAction->setText(windowTitle());
+ break;
+ case QEvent::StyleChange:
+ d->layout->invalidate();
+ if (!d->explicitIconSize)
+ setIconSize(QSize());
+ d->layout->updateMarginAndSpacing();
+ break;
+ case QEvent::LayoutDirectionChange:
+ d->layout->invalidate();
+ break;
+ default:
+ break;
+ }
+ QWidget::changeEvent(event);
+}
+
+/*! \reimp */
+void QToolBar::paintEvent(QPaintEvent *)
+{
+ Q_D(QToolBar);
+
+ QPainter p(this);
+ QStyle *style = this->style();
+ QStyleOptionToolBar opt;
+ initStyleOption(&opt);
+
+ if (d->layout->expanded || d->layout->animating || isWindow()) {
+ //if the toolbar is expended, we need to fill the background with the window color
+ //because some styles may expects that.
+ p.fillRect(opt.rect, palette().background());
+ style->drawControl(QStyle::CE_ToolBar, &opt, &p, this);
+ style->drawPrimitive(QStyle::PE_FrameMenu, &opt, &p, this);
+ } else {
+ style->drawControl(QStyle::CE_ToolBar, &opt, &p, this);
+ }
+
+ opt.rect = d->layout->handleRect();
+ if (opt.rect.isValid())
+ style->drawPrimitive(QStyle::PE_IndicatorToolBarHandle, &opt, &p, this);
+}
+
+/*
+ Checks if an expanded toolbar has to wait for this popup to close before
+ the toolbar collapses. This is true if
+ 1) the popup has the toolbar in its parent chain,
+ 2) the popup is a menu whose menuAction is somewhere in the toolbar.
+*/
+static bool waitForPopup(QToolBar *tb, QWidget *popup)
+{
+ if (popup == 0 || popup->isHidden())
+ return false;
+
+ QWidget *w = popup;
+ while (w != 0) {
+ if (w == tb)
+ return true;
+ w = w->parentWidget();
+ }
+
+ QMenu *menu = qobject_cast<QMenu*>(popup);
+ if (menu == 0)
+ return false;
+
+ QAction *action = menu->menuAction();
+ QList<QWidget*> widgets = action->associatedWidgets();
+ for (int i = 0; i < widgets.count(); ++i) {
+ if (waitForPopup(tb, widgets.at(i)))
+ return true;
+ }
+
+ return false;
+}
+
+/*! \reimp */
+bool QToolBar::event(QEvent *event)
+{
+ Q_D(QToolBar);
+
+ switch (event->type()) {
+ case QEvent::Hide:
+ if (!isHidden())
+ break;
+ // fallthrough intended
+ case QEvent::Show:
+ d->toggleViewAction->setChecked(event->type() == QEvent::Show);
+#if defined(Q_WS_MAC) && !defined(QT_MAC_USE_COCOA)
+ // Fall through
+ case QEvent::LayoutRequest: {
+ // There's currently no way to invalidate the size and let
+ // HIToolbar know about it. This forces a re-check.
+ int earlyResult = -1;
+ if (QMainWindow *mainWindow = qobject_cast<QMainWindow *>(parentWidget())) {
+ bool needUpdate = true;
+ if (event->type() == QEvent::LayoutRequest) {
+ QSize oldSizeHint = sizeHint();
+ earlyResult = QWidget::event(event) ? 1 : 0;
+ needUpdate = oldSizeHint != sizeHint();
+ }
+
+ if (needUpdate) {
+ OSWindowRef windowRef = qt_mac_window_for(this);
+ if (mainWindow->unifiedTitleAndToolBarOnMac()
+ && mainWindow->toolBarArea(this) == Qt::TopToolBarArea
+ && macWindowToolbarVisible(windowRef)) {
+ DisableScreenUpdates();
+ macWindowToolbarShow(this, false);
+ macWindowToolbarShow(this, true);
+ EnableScreenUpdates();
+ }
+ }
+
+ if (earlyResult != -1)
+ return earlyResult;
+ }
+ }
+#endif
+ break;
+ case QEvent::ParentChange:
+ d->layout->checkUsePopupMenu();
+#if defined(Q_WS_MAC)
+ if (parentWidget() && parentWidget()->isWindow())
+ qt_mac_updateToolBarButtonHint(parentWidget());
+#endif
+ break;
+
+ case QEvent::MouseButtonPress: {
+ if (d->mousePressEvent(static_cast<QMouseEvent*>(event)))
+ return true;
+ break;
+ }
+ case QEvent::MouseButtonRelease:
+ if (d->mouseReleaseEvent(static_cast<QMouseEvent*>(event)))
+ return true;
+ break;
+ case QEvent::HoverMove: {
+#ifndef QT_NO_CURSOR
+ QHoverEvent *e = static_cast<QHoverEvent*>(event);
+ if (d->layout->handleRect().contains(e->pos()))
+ setCursor(Qt::SizeAllCursor);
+ else
+ unsetCursor();
+#endif
+ break;
+ }
+ case QEvent::MouseMove:
+ if (d->mouseMoveEvent(static_cast<QMouseEvent*>(event)))
+ return true;
+ break;
+ case QEvent::Leave:
+ if (d->state != 0 && d->state->dragging) {
+#ifdef Q_OS_WIN
+ // This is a workaround for loosing the mouse on Vista.
+ QPoint pos = QCursor::pos();
+ QMouseEvent fake(QEvent::MouseMove, mapFromGlobal(pos), pos, Qt::NoButton,
+ QApplication::mouseButtons(), QApplication::keyboardModifiers());
+ d->mouseMoveEvent(&fake);
+#endif
+ } else {
+ if (!d->layout->expanded)
+ break;
+
+ QWidget *w = qApp->activePopupWidget();
+ if (waitForPopup(this, w)) {
+ d->waitForPopupTimer->start();
+ break;
+ }
+
+ d->waitForPopupTimer->stop();
+ d->layout->setExpanded(false);
+ break;
+ }
+ default:
+ break;
+ }
+ return QWidget::event(event);
+}
+
+void QToolBarPrivate::_q_waitForPopup()
+{
+ Q_Q(QToolBar);
+
+ QWidget *w = qApp->activePopupWidget();
+ if (!waitForPopup(q, w)) {
+ waitForPopupTimer->stop();
+ if (!q->underMouse())
+ layout->setExpanded(false);
+ }
+}
+
+/*!
+ Returns a checkable action that can be used to show or hide this
+ toolbar.
+
+ The action's text is set to the toolbar's window title.
+
+ \sa QAction::text QWidget::windowTitle
+*/
+QAction *QToolBar::toggleViewAction() const
+{ Q_D(const QToolBar); return d->toggleViewAction; }
+
+/*!
+ \fn void QToolBar::setLabel(const QString &label)
+
+ Use setWindowTitle() instead.
+*/
+
+/*!
+ \fn QString QToolBar::label() const
+
+ Use windowTitle() instead.
+*/
+
+/*!
+ \since 4.2
+
+ Returns the widget associated with the specified \a action.
+
+ \sa addWidget()
+*/
+QWidget *QToolBar::widgetForAction(QAction *action) const
+{
+ Q_D(const QToolBar);
+
+ int index = d->layout->indexOf(action);
+ if (index == -1)
+ return 0;
+
+ return d->layout->itemAt(index)->widget();
+}
+
+/*!
+ \internal
+*/
+void QToolBar::initStyleOption(QStyleOptionToolBar *option) const
+{
+ Q_D(const QToolBar);
+
+ if (!option)
+ return;
+
+ option->initFrom(this);
+ if (orientation() == Qt::Horizontal)
+ option->state |= QStyle::State_Horizontal;
+ option->lineWidth = style()->pixelMetric(QStyle::PM_ToolBarFrameWidth, 0, this);
+ option->features = d->layout->movable()
+ ? QStyleOptionToolBar::Movable
+ : QStyleOptionToolBar::None;
+ // if the tool bar is not in a QMainWindow, this will make the painting right
+ option->toolBarArea = Qt::NoToolBarArea;
+
+ // Add more styleoptions if the toolbar has been added to a mainwindow.
+ QMainWindow *mainWindow = qobject_cast<QMainWindow *>(parentWidget());
+
+ if (!mainWindow)
+ return;
+
+ QMainWindowLayout *layout = qobject_cast<QMainWindowLayout *>(mainWindow->layout());
+ Q_ASSERT_X(layout != 0, "QToolBar::initStyleOption()",
+ "QMainWindow->layout() != QMainWindowLayout");
+
+ layout->getStyleOptionInfo(option, const_cast<QToolBar *>(this));
+}
+
+/*!
+ \reimp
+*/
+void QToolBar::childEvent(QChildEvent *event) // ### remove me in 5.0
+{
+ QWidget::childEvent(event);
+}
+
+/*!
+ \reimp
+*/
+void QToolBar::resizeEvent(QResizeEvent *event) // ### remove me in 5.0
+{
+ QWidget::resizeEvent(event);
+}
+
+QT_END_NAMESPACE
+
+#include "moc_qtoolbar.cpp"
+
+#endif // QT_NO_TOOLBAR
diff --git a/src/gui/widgets/qtoolbar.h b/src/gui/widgets/qtoolbar.h
new file mode 100644
index 0000000000..33477d5a4c
--- /dev/null
+++ b/src/gui/widgets/qtoolbar.h
@@ -0,0 +1,187 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QDYNAMICTOOLBAR_H
+#define QDYNAMICTOOLBAR_H
+
+#include <QtGui/qwidget.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+#ifndef QT_NO_TOOLBAR
+
+class QToolBarPrivate;
+
+class QAction;
+class QIcon;
+class QMainWindow;
+class QStyleOptionToolBar;
+
+class Q_GUI_EXPORT QToolBar : public QWidget
+{
+ Q_OBJECT
+
+ Q_PROPERTY(bool movable READ isMovable WRITE setMovable
+ DESIGNABLE (qobject_cast<QMainWindow *>(parentWidget()) != 0)
+ NOTIFY movableChanged)
+ Q_PROPERTY(Qt::ToolBarAreas allowedAreas READ allowedAreas WRITE setAllowedAreas
+ DESIGNABLE (qobject_cast<QMainWindow *>(parentWidget()) != 0)
+ NOTIFY allowedAreasChanged)
+ Q_PROPERTY(Qt::Orientation orientation READ orientation WRITE setOrientation
+ DESIGNABLE (qobject_cast<QMainWindow *>(parentWidget()) == 0)
+ NOTIFY orientationChanged)
+ Q_PROPERTY(QSize iconSize READ iconSize WRITE setIconSize NOTIFY iconSizeChanged)
+ Q_PROPERTY(Qt::ToolButtonStyle toolButtonStyle READ toolButtonStyle WRITE setToolButtonStyle
+ NOTIFY toolButtonStyleChanged)
+ Q_PROPERTY(bool floating READ isFloating)
+ Q_PROPERTY(bool floatable READ isFloatable WRITE setFloatable)
+
+public:
+ explicit QToolBar(const QString &title, QWidget *parent = 0);
+ explicit QToolBar(QWidget *parent = 0);
+ ~QToolBar();
+
+ void setMovable(bool movable);
+ bool isMovable() const;
+
+ void setAllowedAreas(Qt::ToolBarAreas areas);
+ Qt::ToolBarAreas allowedAreas() const;
+
+ inline bool isAreaAllowed(Qt::ToolBarArea area) const
+ { return (allowedAreas() & area) == area; }
+
+ void setOrientation(Qt::Orientation orientation);
+ Qt::Orientation orientation() const;
+
+ void clear();
+
+#ifdef Q_NO_USING_KEYWORD
+ inline void addAction(QAction *action)
+ { QWidget::addAction(action); }
+#else
+ using QWidget::addAction;
+#endif
+
+ QAction *addAction(const QString &text);
+ QAction *addAction(const QIcon &icon, const QString &text);
+ QAction *addAction(const QString &text, const QObject *receiver, const char* member);
+ QAction *addAction(const QIcon &icon, const QString &text,
+ const QObject *receiver, const char* member);
+
+ QAction *addSeparator();
+ QAction *insertSeparator(QAction *before);
+
+ QAction *addWidget(QWidget *widget);
+ QAction *insertWidget(QAction *before, QWidget *widget);
+
+ QRect actionGeometry(QAction *action) const;
+ QAction *actionAt(const QPoint &p) const;
+ inline QAction *actionAt(int x, int y) const;
+
+ QAction *toggleViewAction() const;
+
+ QSize iconSize() const;
+ Qt::ToolButtonStyle toolButtonStyle() const;
+
+ QWidget *widgetForAction(QAction *action) const;
+
+ bool isFloatable() const;
+ void setFloatable(bool floatable);
+ bool isFloating() const;
+
+public Q_SLOTS:
+ void setIconSize(const QSize &iconSize);
+ void setToolButtonStyle(Qt::ToolButtonStyle toolButtonStyle);
+
+Q_SIGNALS:
+ void actionTriggered(QAction *action);
+ void movableChanged(bool movable);
+ void allowedAreasChanged(Qt::ToolBarAreas allowedAreas);
+ void orientationChanged(Qt::Orientation orientation);
+ void iconSizeChanged(const QSize &iconSize);
+ void toolButtonStyleChanged(Qt::ToolButtonStyle toolButtonStyle);
+
+protected:
+ void actionEvent(QActionEvent *event);
+ void changeEvent(QEvent *event);
+ void childEvent(QChildEvent *event);
+ void paintEvent(QPaintEvent *event);
+ void resizeEvent(QResizeEvent *event);
+ bool event(QEvent *event);
+ void initStyleOption(QStyleOptionToolBar *option) const;
+
+#ifdef QT3_SUPPORT
+public:
+ QT3_SUPPORT_CONSTRUCTOR QToolBar(QWidget *parent, const char *name);
+ inline QT3_SUPPORT void setLabel(const QString &label)
+ { setWindowTitle(label); }
+ inline QT3_SUPPORT QString label() const
+ { return windowTitle(); }
+#endif
+
+private:
+ Q_DECLARE_PRIVATE(QToolBar)
+ Q_DISABLE_COPY(QToolBar)
+ Q_PRIVATE_SLOT(d_func(), void _q_toggleView(bool))
+ Q_PRIVATE_SLOT(d_func(), void _q_updateIconSize(const QSize &))
+ Q_PRIVATE_SLOT(d_func(), void _q_updateToolButtonStyle(Qt::ToolButtonStyle))
+ Q_PRIVATE_SLOT(d_func(), void _q_waitForPopup())
+
+ friend class QMainWindow;
+ friend class QMainWindowLayout;
+ friend class QToolBarLayout;
+ friend class QToolBarAreaLayout;
+};
+
+inline QAction *QToolBar::actionAt(int ax, int ay) const
+{ return actionAt(QPoint(ax, ay)); }
+
+#endif // QT_NO_TOOLBAR
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QDYNAMICTOOLBAR_H
diff --git a/src/gui/widgets/qtoolbar_p.h b/src/gui/widgets/qtoolbar_p.h
new file mode 100644
index 0000000000..598f05435a
--- /dev/null
+++ b/src/gui/widgets/qtoolbar_p.h
@@ -0,0 +1,135 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QDYNAMICTOOLBAR_P_H
+#define QDYNAMICTOOLBAR_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 "qtoolbar.h"
+#include "QtGui/qaction.h"
+#include "private/qwidget_p.h"
+
+QT_BEGIN_NAMESPACE
+
+#ifndef QT_NO_TOOLBAR
+
+class QToolBarLayout;
+class QTimer;
+
+class QToolBarPrivate : public QWidgetPrivate
+{
+ Q_DECLARE_PUBLIC(QToolBar)
+
+public:
+ inline QToolBarPrivate()
+ : explicitIconSize(false), explicitToolButtonStyle(false), movable(false),
+ allowedAreas(Qt::AllToolBarAreas), orientation(Qt::Horizontal),
+ toolButtonStyle(Qt::ToolButtonIconOnly),
+ layout(0), state(0)
+#ifdef Q_WS_MAC
+ , macWindowDragging(false)
+#endif
+ { }
+
+ void init();
+ void actionTriggered();
+ void _q_toggleView(bool b);
+ void _q_updateIconSize(const QSize &sz);
+ void _q_updateToolButtonStyle(Qt::ToolButtonStyle style);
+ void _q_waitForPopup();
+
+ bool explicitIconSize;
+ bool explicitToolButtonStyle;
+ bool movable;
+ Qt::ToolBarAreas allowedAreas;
+ Qt::Orientation orientation;
+ Qt::ToolButtonStyle toolButtonStyle;
+ QSize iconSize;
+ bool floatable;
+
+ QAction *toggleViewAction;
+
+ QToolBarLayout *layout;
+
+ struct DragState {
+ QPoint pressPos;
+ bool dragging;
+ bool moving;
+ QLayoutItem *widgetItem;
+ };
+ DragState *state;
+
+#ifdef Q_WS_MAC
+ bool macWindowDragging;
+ QPoint macWindowDragPressPosition;
+#endif
+
+ bool mousePressEvent(QMouseEvent *e);
+ bool mouseReleaseEvent(QMouseEvent *e);
+ bool mouseMoveEvent(QMouseEvent *e);
+
+ void updateWindowFlags(bool floating, bool unplug = false);
+ void setWindowState(bool floating, bool unplug = false, const QRect &rect = QRect());
+ void initDrag(const QPoint &pos);
+ void startDrag(bool moving = false);
+ void endDrag();
+
+ void unplug(const QRect &r);
+ void plug(const QRect &r);
+
+ QTimer *waitForPopupTimer;
+};
+
+#endif // QT_NO_TOOLBAR
+
+QT_END_NAMESPACE
+
+#endif // QDYNAMICTOOLBAR_P_H
diff --git a/src/gui/widgets/qtoolbararealayout.cpp b/src/gui/widgets/qtoolbararealayout.cpp
new file mode 100644
index 0000000000..49f4a9ee4c
--- /dev/null
+++ b/src/gui/widgets/qtoolbararealayout.cpp
@@ -0,0 +1,1370 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QWidgetItem>
+#include <QToolBar>
+#include <QStyleOption>
+#include <QApplication>
+#include <qdebug.h>
+
+#include "qtoolbararealayout_p.h"
+#include "qmainwindowlayout_p.h"
+#include "qwidgetanimator_p.h"
+#include "qtoolbarlayout_p.h"
+#include "qtoolbar_p.h"
+
+/******************************************************************************
+** QToolBarAreaLayoutItem
+*/
+
+#ifndef QT_NO_TOOLBAR
+
+QT_BEGIN_NAMESPACE
+
+QSize QToolBarAreaLayoutItem::minimumSize() const
+{
+ if (skip())
+ return QSize(0, 0);
+ return qSmartMinSize(static_cast<QWidgetItem*>(widgetItem));
+}
+
+QSize QToolBarAreaLayoutItem::sizeHint() const
+{
+ if (skip())
+ return QSize(0, 0);
+
+ return realSizeHint();
+}
+
+//returns the real size hint not taking into account the visibility of the widget
+QSize QToolBarAreaLayoutItem::realSizeHint() const
+{
+ QWidget *wid = widgetItem->widget();
+ QSize s = wid->sizeHint().expandedTo(wid->minimumSizeHint());
+ if (wid->sizePolicy().horizontalPolicy() == QSizePolicy::Ignored)
+ s.setWidth(0);
+ if (wid->sizePolicy().verticalPolicy() == QSizePolicy::Ignored)
+ s.setHeight(0);
+ s = s.boundedTo(wid->maximumSize())
+ .expandedTo(wid->minimumSize());
+ return s;
+}
+
+bool QToolBarAreaLayoutItem::skip() const
+{
+ if (gap)
+ return false;
+ return widgetItem == 0 || widgetItem->isEmpty();
+}
+
+/******************************************************************************
+** QToolBarAreaLayoutLine
+*/
+
+QToolBarAreaLayoutLine::QToolBarAreaLayoutLine(Qt::Orientation orientation)
+ : o(orientation)
+{
+}
+
+QSize QToolBarAreaLayoutLine::sizeHint() const
+{
+ int a = 0, b = 0;
+ for (int i = 0; i < toolBarItems.count(); ++i) {
+ const QToolBarAreaLayoutItem &item = toolBarItems.at(i);
+ if (item.skip())
+ continue;
+
+ QSize sh = item.sizeHint();
+ a += pick(o, sh) + item.extraSpace;
+ b = qMax(b, perp(o, sh));
+ }
+
+ QSize result;
+ rpick(o, result) = a;
+ rperp(o, result) = b;
+
+ return result;
+}
+
+QSize QToolBarAreaLayoutLine::minimumSize() const
+{
+ int a = 0, b = 0;
+ for (int i = 0; i < toolBarItems.count(); ++i) {
+ const QToolBarAreaLayoutItem &item = toolBarItems[i];
+ if (item.skip())
+ continue;
+
+ QSize ms = item.minimumSize();
+ a += pick(o, ms);
+ b = qMax(b, perp(o, ms));
+ }
+
+ QSize result;
+ rpick(o, result) = a;
+ rperp(o, result) = b;
+
+ return result;
+}
+
+void QToolBarAreaLayoutLine::fitLayout()
+{
+ int last = -1;
+ int min = pick(o, minimumSize());
+ int space = pick(o, rect.size());
+ int extra = qMax(0, space - min);
+
+ for (int i = 0; i < toolBarItems.count(); ++i) {
+ QToolBarAreaLayoutItem &item = toolBarItems[i];
+ if (item.skip())
+ continue;
+
+ QToolBarLayout *tblayout = qobject_cast<QToolBarLayout*>(item.widgetItem->widget()->layout());
+ if (tblayout)
+ tblayout->checkUsePopupMenu();
+
+ int itemMin = pick(o, item.minimumSize());
+ int itemHint = pick(o, item.sizeHint());
+ //we ensure the extraspace is not too low
+ item.extraSpace = qMax(itemMin - itemHint, item.extraSpace);
+ itemHint += item.extraSpace;
+ int itemExtra = qMin(itemHint - itemMin, extra);
+
+ item.size = itemMin + itemExtra;
+ extra -= itemExtra;
+
+ last = i;
+ }
+
+ // calculate the positions from the sizes
+ int pos = 0;
+ for (int i = 0; i < toolBarItems.count(); ++i) {
+ QToolBarAreaLayoutItem &item = toolBarItems[i];
+ if (item.skip())
+ continue;
+
+ item.pos = pos;
+ if (i == last) // stretch the last item to the end of the line
+ item.size = qMax(0, pick(o, rect.size()) - item.pos);
+ pos += item.size;
+ }
+}
+
+bool QToolBarAreaLayoutLine::skip() const
+{
+ for (int i = 0; i < toolBarItems.count(); ++i) {
+ if (!toolBarItems.at(i).skip())
+ return false;
+ }
+ return true;
+}
+
+/******************************************************************************
+** QToolBarAreaLayoutInfo
+*/
+
+QToolBarAreaLayoutInfo::QToolBarAreaLayoutInfo(QInternal::DockPosition pos)
+ : dockPos(pos), dirty(false)
+{
+ switch (pos) {
+ case QInternal::LeftDock:
+ case QInternal::RightDock:
+ o = Qt::Vertical;
+ break;
+ case QInternal::TopDock:
+ case QInternal::BottomDock:
+ o = Qt::Horizontal;
+ break;
+ default:
+ o = Qt::Horizontal;
+ break;
+ }
+}
+
+QSize QToolBarAreaLayoutInfo::sizeHint() const
+{
+ int a = 0, b = 0;
+ for (int i = 0; i < lines.count(); ++i) {
+ const QToolBarAreaLayoutLine &l = lines.at(i);
+ if (l.skip())
+ continue;
+
+ QSize hint = l.sizeHint();
+ a = qMax(a, pick(o, hint));
+ b += perp(o, hint);
+ }
+
+ QSize result;
+ rpick(o, result) = a;
+ rperp(o, result) = b;
+
+ return result;
+}
+
+QSize QToolBarAreaLayoutInfo::minimumSize() const
+{
+ int a = 0, b = 0;
+ for (int i = 0; i < lines.count(); ++i) {
+ const QToolBarAreaLayoutLine &l = lines.at(i);
+ if (l.skip())
+ continue;
+
+ QSize m = l.minimumSize();
+ a = qMax(a, pick(o, m));
+ b += perp(o, m);
+ }
+
+ QSize result;
+ rpick(o, result) = a;
+ rperp(o, result) = b;
+
+ return result;
+}
+
+void QToolBarAreaLayoutInfo::fitLayout()
+{
+ dirty = false;
+
+ int b = 0;
+
+ bool reverse = dockPos == QInternal::RightDock || dockPos == QInternal::BottomDock;
+
+ int i = reverse ? lines.count() - 1 : 0;
+ for (;;) {
+ if ((reverse && i < 0) || (!reverse && i == lines.count()))
+ break;
+
+ QToolBarAreaLayoutLine &l = lines[i];
+ if (!l.skip()) {
+ if (o == Qt::Horizontal) {
+ l.rect.setLeft(rect.left());
+ l.rect.setRight(rect.right());
+ l.rect.setTop(b + rect.top());
+ b += l.sizeHint().height();
+ l.rect.setBottom(b - 1 + rect.top());
+ } else {
+ l.rect.setTop(rect.top());
+ l.rect.setBottom(rect.bottom());
+ l.rect.setLeft(b + rect.left());
+ b += l.sizeHint().width();
+ l.rect.setRight(b - 1 + rect.left());
+ }
+
+ l.fitLayout();
+ }
+
+ i += reverse ? -1 : 1;
+ }
+}
+
+QLayoutItem *QToolBarAreaLayoutInfo::insertToolBar(QToolBar *before, QToolBar *toolBar)
+{
+ toolBar->setOrientation(o);
+ QLayoutItem *item = new QWidgetItemV2(toolBar);
+ insertItem(before, item);
+ return item;
+}
+
+void QToolBarAreaLayoutInfo::insertItem(QToolBar *before, QLayoutItem *item)
+{
+ if (before == 0) {
+ if (lines.isEmpty())
+ lines.append(QToolBarAreaLayoutLine(o));
+ lines.last().toolBarItems.append(item);
+ return;
+ }
+
+ for (int j = 0; j < lines.count(); ++j) {
+ QToolBarAreaLayoutLine &line = lines[j];
+
+ for (int k = 0; k < line.toolBarItems.count(); ++k) {
+ if (line.toolBarItems.at(k).widgetItem->widget() == before) {
+ line.toolBarItems.insert(k, item);
+ return;
+ }
+ }
+ }
+}
+
+void QToolBarAreaLayoutInfo::removeToolBar(QToolBar *toolBar)
+{
+ for (int j = 0; j < lines.count(); ++j) {
+ QToolBarAreaLayoutLine &line = lines[j];
+
+ for (int k = 0; k < line.toolBarItems.count(); ++k) {
+ QToolBarAreaLayoutItem &item = line.toolBarItems[k];
+ if (item.widgetItem->widget() == toolBar) {
+ delete item.widgetItem;
+ item.widgetItem = 0;
+ line.toolBarItems.removeAt(k);
+
+ if (line.toolBarItems.isEmpty() && j < lines.count() - 1)
+ lines.removeAt(j);
+
+ return;
+ }
+ }
+ }
+}
+
+void QToolBarAreaLayoutInfo::insertToolBarBreak(QToolBar *before)
+{
+ if (before == 0) {
+ if (!lines.isEmpty() && lines.last().toolBarItems.isEmpty())
+ return;
+ lines.append(QToolBarAreaLayoutLine(o));
+ return;
+ }
+
+ for (int j = 0; j < lines.count(); ++j) {
+ QToolBarAreaLayoutLine &line = lines[j];
+
+ for (int k = 0; k < line.toolBarItems.count(); ++k) {
+ if (line.toolBarItems.at(k).widgetItem->widget() == before) {
+ if (k == 0)
+ return;
+
+ QToolBarAreaLayoutLine newLine(o);
+ newLine.toolBarItems = line.toolBarItems.mid(k);
+ line.toolBarItems = line.toolBarItems.mid(0, k);
+ lines.insert(j + 1, newLine);
+
+ return;
+ }
+ }
+ }
+}
+
+void QToolBarAreaLayoutInfo::removeToolBarBreak(QToolBar *before)
+{
+ for (int j = 0; j < lines.count(); ++j) {
+ const QToolBarAreaLayoutLine &line = lines.at(j);
+
+ for (int k = 0; k < line.toolBarItems.count(); ++k) {
+ if (line.toolBarItems.at(k).widgetItem->widget() == before) {
+ if (k != 0)
+ return;
+ if (j == 0)
+ return;
+
+ lines[j - 1].toolBarItems += lines[j].toolBarItems;
+ lines.removeAt(j);
+
+ return;
+ }
+ }
+ }
+}
+
+void QToolBarAreaLayoutInfo::moveToolBar(QToolBar *toolbar, int pos)
+{
+ if (dirty) {
+ fitLayout();
+ }
+
+ dirty = true;
+
+ if (o == Qt::Vertical) {
+ pos -= rect.top();
+ }
+
+ //here we actually update the extraSpace for the line containing the toolbar so that we move it
+ for (int j = 0; j < lines.count(); ++j) {
+ QToolBarAreaLayoutLine &line = lines[j];
+
+ int previousIndex = -1;
+ int minPos = 0;
+ for (int k = 0; k < line.toolBarItems.count(); ++k) {
+ QToolBarAreaLayoutItem &current = line.toolBarItems[k];
+ if (current.widgetItem->widget() == toolbar) {
+ int newPos = current.pos;
+
+ if (previousIndex >= 0) {
+ QToolBarAreaLayoutItem &previous = line.toolBarItems[previousIndex];
+ if (pos < current.pos) {
+ newPos = qMax(pos, minPos);
+ } else {
+ //we check the max value for the position (until everything at the right is "compressed")
+ int maxPos = pick(o, rect.size());
+ for(int l = k; l < line.toolBarItems.count(); ++l) {
+ const QToolBarAreaLayoutItem &item = line.toolBarItems.at(l);
+ if (!item.skip()) {
+ maxPos -= pick(o, item.minimumSize());
+ }
+ }
+ newPos = qMin(pos, maxPos);
+ }
+
+ //let's update the previous extra space
+ int extra = newPos - current.pos;
+
+ if (qAbs(previous.extraSpace + extra) < QApplication::startDragDistance()) {
+ //we stick to the default space
+ extra = 0;
+ }
+
+ //update for the current item
+ current.extraSpace -= extra;
+ //this ensures the toolbars to be pushed to the right when necessary
+ current.extraSpace = qMax(pick(o,current.minimumSize())- pick(o,current.sizeHint()), current.extraSpace);
+
+ if (extra >= 0) {
+ previous.extraSpace += extra;
+
+ } else {
+ //we need to push the toolbars on the left starting with previous
+ extra = -extra; // we just need to know the number of pixels
+ ///at this point we need to get extra pixels from the toolbars at the left
+ for(int l = previousIndex; l >=0; --l) {
+ QToolBarAreaLayoutItem &item = line.toolBarItems[l];
+ if (!item.skip()) {
+ const int minExtraSpace = pick(o, item.minimumSize()) - pick(o, item.sizeHint());
+ const int margin = item.extraSpace - minExtraSpace;
+ if (margin < extra) {
+ item.extraSpace = minExtraSpace;
+ extra -= margin;
+ } else {
+ item.extraSpace -= extra;
+ extra = 0;
+ }
+ }
+ }
+ Q_ASSERT(extra == 0);
+ }
+ } else {
+ //the item is the first one, it should be at position 0
+ }
+
+ return;
+
+ } else if (!current.skip()) {
+ previousIndex = k;
+ minPos += pick(o, current.minimumSize());
+ }
+ }
+ }
+}
+
+
+QList<int> QToolBarAreaLayoutInfo::gapIndex(const QPoint &pos) const
+{
+ int p = pick(o, pos);
+
+ if (rect.contains(pos)) {
+ for (int j = 0; j < lines.count(); ++j) {
+ const QToolBarAreaLayoutLine &line = lines.at(j);
+ if (line.skip())
+ continue;
+ if (!line.rect.contains(pos))
+ continue;
+
+ int k = 0;
+ for (; k < line.toolBarItems.count(); ++k) {
+ const QToolBarAreaLayoutItem &item = line.toolBarItems.at(k);
+ if (item.skip())
+ continue;
+
+ int size = qMin(item.size, pick(o, item.sizeHint()));
+
+ if (p > item.pos + size)
+ continue;
+ if (p > item.pos + size/2)
+ ++k;
+ break;
+ }
+
+ QList<int> result;
+ result << j << k;
+ return result;
+ }
+ } else if (appendLineDropRect().contains(pos)) {
+ QList<int> result;
+ result << lines.count() << 0;
+ return result;
+ }
+
+ return QList<int>();
+}
+
+bool QToolBarAreaLayoutInfo::insertGap(QList<int> path, QLayoutItem *item)
+{
+ int j = path.at(0);
+ if (j == lines.count())
+ lines.append(QToolBarAreaLayoutLine(o));
+
+ QToolBarAreaLayoutLine &line = lines[j];
+ const int k = path.at(1);
+
+ QToolBarAreaLayoutItem gap_item;
+ gap_item.gap = true;
+ gap_item.widgetItem = item;
+
+ //update the previous item's extra space
+ for(int p = k - 1 ; p >= 0; --p) {
+ QToolBarAreaLayoutItem &previous = line.toolBarItems[p];
+ if (!previous.skip()) {
+ //we found the previous one
+ gap_item.extraSpace = qMax(0, previous.extraSpace - pick(o, gap_item.sizeHint()));
+ previous.extraSpace = qMin(previous.extraSpace, 0);
+ break;
+ }
+ }
+
+ line.toolBarItems.insert(k, gap_item);
+ return true;
+
+}
+
+void QToolBarAreaLayoutInfo::clear()
+{
+ lines.clear();
+ rect = QRect(0, 0, -1, -1);
+}
+
+QRect QToolBarAreaLayoutInfo::itemRect(QList<int> path) const
+{
+ int j = path.at(0);
+ int k = path.at(1);
+
+ const QToolBarAreaLayoutLine &line = lines.at(j);
+ const QToolBarAreaLayoutItem &item = line.toolBarItems.at(k);
+
+ QRect result = line.rect;
+
+ if (o == Qt::Horizontal) {
+ result.setLeft(item.pos + line.rect.left());
+ result.setWidth(item.size);
+ } else {
+ result.setTop(item.pos + line.rect.top());
+ result.setHeight(item.size);
+ }
+
+ return result;
+}
+
+QRect QToolBarAreaLayoutInfo::appendLineDropRect() const
+{
+ QRect result;
+
+ switch (dockPos) {
+ case QInternal::LeftDock:
+ result = QRect(rect.right(), rect.top(),
+ EmptyDockAreaSize, rect.height());
+ break;
+ case QInternal::RightDock:
+ result = QRect(rect.left() - EmptyDockAreaSize, rect.top(),
+ EmptyDockAreaSize, rect.height());
+ break;
+ case QInternal::TopDock:
+ result = QRect(rect.left(), rect.bottom() + 1,
+ rect.width(), EmptyDockAreaSize);
+ break;
+ case QInternal::BottomDock:
+ result = QRect(rect.left(), rect.top() - EmptyDockAreaSize,
+ rect.width(), EmptyDockAreaSize);
+ break;
+ default:
+ break;
+ }
+
+ return result;
+}
+
+/******************************************************************************
+** QToolBarAreaLayout
+*/
+
+QToolBarAreaLayout::QToolBarAreaLayout(QMainWindow *win)
+{
+ visible = true;
+ mainWindow = win;
+ for (int i = 0; i < QInternal::DockCount; ++i) {
+ QInternal::DockPosition pos = static_cast<QInternal::DockPosition>(i);
+ docks[i] = QToolBarAreaLayoutInfo(pos);
+ }
+}
+
+QRect QToolBarAreaLayout::fitLayout()
+{
+ if (!visible)
+ return rect;
+
+ QSize left_hint = docks[QInternal::LeftDock].sizeHint();
+ QSize right_hint = docks[QInternal::RightDock].sizeHint();
+ QSize top_hint = docks[QInternal::TopDock].sizeHint();
+ QSize bottom_hint = docks[QInternal::BottomDock].sizeHint();
+
+ QRect center = rect.adjusted(left_hint.width(), top_hint.height(),
+ -right_hint.width(), -bottom_hint.height());
+
+ docks[QInternal::TopDock].rect = QRect(rect.left(), rect.top(),
+ rect.width(), top_hint.height());
+ docks[QInternal::LeftDock].rect = QRect(rect.left(), center.top(),
+ left_hint.width(), center.height());
+ docks[QInternal::RightDock].rect = QRect(center.right() + 1, center.top(),
+ right_hint.width(), center.height());
+ docks[QInternal::BottomDock].rect = QRect(rect.left(), center.bottom() + 1,
+ rect.width(), bottom_hint.height());
+
+ if (!mainWindow->unifiedTitleAndToolBarOnMac()) {
+ docks[QInternal::TopDock].fitLayout();
+ }
+ docks[QInternal::LeftDock].fitLayout();
+ docks[QInternal::RightDock].fitLayout();
+ docks[QInternal::BottomDock].fitLayout();
+
+ return center;
+}
+
+QSize QToolBarAreaLayout::minimumSize(const QSize &centerMin) const
+{
+ if (!visible)
+ return centerMin;
+
+ QSize result = centerMin;
+
+ QSize left_min = docks[QInternal::LeftDock].minimumSize();
+ QSize right_min = docks[QInternal::RightDock].minimumSize();
+ QSize top_min = docks[QInternal::TopDock].minimumSize();
+ QSize bottom_min = docks[QInternal::BottomDock].minimumSize();
+
+ result.setWidth(qMax(top_min.width(), result.width()));
+ result.setWidth(qMax(bottom_min.width(), result.width()));
+ result.setHeight(qMax(left_min.height(), result.height()));
+ result.setHeight(qMax(right_min.height(), result.height()));
+
+ result.rwidth() += left_min.width() + right_min.width();
+ result.rheight() += top_min.height() + bottom_min.height();
+
+ return result;
+}
+
+QSize QToolBarAreaLayout::sizeHint(const QSize &centerHint) const
+{
+ if (!visible)
+ return centerHint;
+
+ QSize result = centerHint;
+
+ QSize left_hint = docks[QInternal::LeftDock].sizeHint();
+ QSize right_hint = docks[QInternal::RightDock].sizeHint();
+ QSize top_hint = docks[QInternal::TopDock].sizeHint();
+ QSize bottom_hint = docks[QInternal::BottomDock].sizeHint();
+
+ result.setWidth(qMax(top_hint.width(), result.width()));
+ result.setWidth(qMax(bottom_hint.width(), result.width()));
+ result.setHeight(qMax(left_hint.height(), result.height()));
+ result.setHeight(qMax(right_hint.height(), result.height()));
+
+ result.rwidth() += left_hint.width() + right_hint.width();
+ result.rheight() += top_hint.height() + bottom_hint.height();
+
+ return result;
+}
+
+QRect QToolBarAreaLayout::rectHint(const QRect &r) const
+{
+ int coef = visible ? 1 : -1;
+
+ QRect result = r;
+
+ QSize left_hint = docks[QInternal::LeftDock].sizeHint();
+ QSize right_hint = docks[QInternal::RightDock].sizeHint();
+ QSize top_hint = docks[QInternal::TopDock].sizeHint();
+ QSize bottom_hint = docks[QInternal::BottomDock].sizeHint();
+
+ result.adjust(-left_hint.width()*coef, -top_hint.height()*coef,
+ right_hint.width()*coef, bottom_hint.height()*coef);
+
+ return result;
+}
+
+QLayoutItem *QToolBarAreaLayout::itemAt(int *x, int index) const
+{
+ Q_ASSERT(x != 0);
+
+ for (int i = 0; i < QInternal::DockCount; ++i) {
+ const QToolBarAreaLayoutInfo &dock = docks[i];
+
+ for (int j = 0; j < dock.lines.count(); ++j) {
+ const QToolBarAreaLayoutLine &line = dock.lines.at(j);
+
+ for (int k = 0; k < line.toolBarItems.count(); ++k) {
+ if ((*x)++ == index)
+ return line.toolBarItems.at(k).widgetItem;
+ }
+ }
+ }
+
+ return 0;
+}
+
+QLayoutItem *QToolBarAreaLayout::takeAt(int *x, int index)
+{
+ Q_ASSERT(x != 0);
+
+ for (int i = 0; i < QInternal::DockCount; ++i) {
+ QToolBarAreaLayoutInfo &dock = docks[i];
+
+ for (int j = 0; j < dock.lines.count(); ++j) {
+ QToolBarAreaLayoutLine &line = dock.lines[j];
+
+ for (int k = 0; k < line.toolBarItems.count(); ++k) {
+ if ((*x)++ == index) {
+ QLayoutItem *result = line.toolBarItems.takeAt(k).widgetItem;
+ if (line.toolBarItems.isEmpty())
+ dock.lines.removeAt(j);
+ return result;
+ }
+ }
+ }
+ }
+
+ return 0;
+}
+
+void QToolBarAreaLayout::deleteAllLayoutItems()
+{
+ for (int i = 0; i < QInternal::DockCount; ++i) {
+ QToolBarAreaLayoutInfo &dock = docks[i];
+
+ for (int j = 0; j < dock.lines.count(); ++j) {
+ QToolBarAreaLayoutLine &line = dock.lines[j];
+
+ for (int k = 0; k < line.toolBarItems.count(); ++k) {
+ QToolBarAreaLayoutItem &item = line.toolBarItems[k];
+ delete item.widgetItem;
+ item.widgetItem = 0;
+ }
+ }
+ }
+}
+
+QInternal::DockPosition QToolBarAreaLayout::findToolBar(QToolBar *toolBar) const
+{
+ for (int i = 0; i < QInternal::DockCount; ++i) {
+ const QToolBarAreaLayoutInfo &dock = docks[i];
+
+ for (int j = 0; j < dock.lines.count(); ++j) {
+ const QToolBarAreaLayoutLine &line = dock.lines.at(j);
+
+ for (int k = 0; k < line.toolBarItems.count(); ++k) {
+ if (line.toolBarItems.at(k).widgetItem->widget() == toolBar)
+ return static_cast<QInternal::DockPosition>(i);
+ }
+ }
+ }
+
+ return QInternal::DockCount;
+}
+
+QLayoutItem *QToolBarAreaLayout::insertToolBar(QToolBar *before, QToolBar *toolBar)
+{
+ QInternal::DockPosition pos = findToolBar(before);
+ if (pos == QInternal::DockCount)
+ return 0;
+
+ return docks[pos].insertToolBar(before, toolBar);
+}
+
+void QToolBarAreaLayout::removeToolBar(QToolBar *toolBar)
+{
+ QInternal::DockPosition pos = findToolBar(toolBar);
+ if (pos == QInternal::DockCount)
+ return;
+ docks[pos].removeToolBar(toolBar);
+}
+
+QLayoutItem *QToolBarAreaLayout::addToolBar(QInternal::DockPosition pos, QToolBar *toolBar)
+{
+ return docks[pos].insertToolBar(0, toolBar);
+}
+
+void QToolBarAreaLayout::insertToolBarBreak(QToolBar *before)
+{
+ QInternal::DockPosition pos = findToolBar(before);
+ if (pos == QInternal::DockCount)
+ return;
+ docks[pos].insertToolBarBreak(before);
+}
+
+void QToolBarAreaLayout::removeToolBarBreak(QToolBar *before)
+{
+ QInternal::DockPosition pos = findToolBar(before);
+ if (pos == QInternal::DockCount)
+ return;
+ docks[pos].removeToolBarBreak(before);
+}
+
+void QToolBarAreaLayout::addToolBarBreak(QInternal::DockPosition pos)
+{
+ docks[pos].insertToolBarBreak(0);
+}
+
+void QToolBarAreaLayout::moveToolBar(QToolBar *toolbar, int p)
+{
+ QInternal::DockPosition pos = findToolBar(toolbar);
+ if (pos == QInternal::DockCount)
+ return;
+ docks[pos].moveToolBar(toolbar, p);
+}
+
+
+void QToolBarAreaLayout::insertItem(QInternal::DockPosition pos, QLayoutItem *item)
+{
+ if (docks[pos].lines.isEmpty())
+ docks[pos].lines.append(QToolBarAreaLayoutLine(docks[pos].o));
+ docks[pos].lines.last().toolBarItems.append(item);
+}
+
+void QToolBarAreaLayout::insertItem(QToolBar *before, QLayoutItem *item)
+{
+ QInternal::DockPosition pos = findToolBar(before);
+ if (pos == QInternal::DockCount)
+ return;
+
+ docks[pos].insertItem(before, item);
+}
+
+void QToolBarAreaLayout::apply(bool animate)
+{
+ QMainWindowLayout *layout = qobject_cast<QMainWindowLayout*>(mainWindow->layout());
+ Q_ASSERT(layout != 0);
+
+ Qt::LayoutDirection dir = mainWindow->layoutDirection();
+
+ for (int i = 0; i < QInternal::DockCount; ++i) {
+ const QToolBarAreaLayoutInfo &dock = docks[i];
+
+ for (int j = 0; j < dock.lines.count(); ++j) {
+ const QToolBarAreaLayoutLine &line = dock.lines.at(j);
+ if (line.skip())
+ continue;
+
+ for (int k = 0; k < line.toolBarItems.count(); ++k) {
+ const QToolBarAreaLayoutItem &item = line.toolBarItems.at(k);
+ if (item.skip() || item.gap)
+ continue;
+
+ QRect geo;
+ if (visible) {
+ if (line.o == Qt::Horizontal) {
+ geo.setTop(line.rect.top());
+ geo.setBottom(line.rect.bottom());
+ geo.setLeft(line.rect.left() + item.pos);
+ geo.setRight(line.rect.left() + item.pos + item.size - 1);
+ } else {
+ geo.setLeft(line.rect.left());
+ geo.setRight(line.rect.right());
+ geo.setTop(line.rect.top() + item.pos);
+ geo.setBottom(line.rect.top() + item.pos + item.size - 1);
+ }
+ }
+
+ QWidget *widget = item.widgetItem->widget();
+ if (QToolBar *toolBar = qobject_cast<QToolBar*>(widget)) {
+ QToolBarLayout *tbl = qobject_cast<QToolBarLayout*>(toolBar->layout());
+ if (tbl->expanded) {
+ QPoint tr = geo.topRight();
+ QSize size = tbl->expandedSize(geo.size());
+ geo.setSize(size);
+ geo.moveTopRight(tr);
+ if (geo.bottom() > rect.bottom())
+ geo.moveBottom(rect.bottom());
+ if (geo.right() > rect.right())
+ geo.moveRight(rect.right());
+ if (geo.left() < 0)
+ geo.moveLeft(0);
+ if (geo.top() < 0)
+ geo.moveTop(0);
+ }
+ }
+
+ if (visible && dock.o == Qt::Horizontal)
+ geo = QStyle::visualRect(dir, line.rect, geo);
+
+ layout->widgetAnimator->animate(widget, geo, animate);
+ }
+ }
+ }
+}
+
+bool QToolBarAreaLayout::toolBarBreak(QToolBar *toolBar) const
+{
+ for (int i = 0; i < QInternal::DockCount; ++i) {
+ const QToolBarAreaLayoutInfo &dock = docks[i];
+
+ for (int j = 0; j < dock.lines.count(); ++j) {
+ const QToolBarAreaLayoutLine &line = dock.lines.at(j);
+
+ for (int k = 0; k < line.toolBarItems.count(); ++k) {
+ if (line.toolBarItems.at(k).widgetItem->widget() == toolBar)
+ return j > 0 && k == 0;
+ }
+ }
+ }
+
+ return false;
+}
+
+void QToolBarAreaLayout::getStyleOptionInfo(QStyleOptionToolBar *option, QToolBar *toolBar) const
+{
+ for (int i = 0; i < QInternal::DockCount; ++i) {
+ const QToolBarAreaLayoutInfo &dock = docks[i];
+
+ for (int j = 0; j < dock.lines.count(); ++j) {
+ const QToolBarAreaLayoutLine &line = dock.lines.at(j);
+
+ for (int k = 0; k < line.toolBarItems.count(); ++k) {
+ if (line.toolBarItems.at(k).widgetItem->widget() == toolBar) {
+ if (line.toolBarItems.count() == 1)
+ option->positionWithinLine = QStyleOptionToolBar::OnlyOne;
+ else if (k == 0)
+ option->positionWithinLine = QStyleOptionToolBar::Beginning;
+ else if (k == line.toolBarItems.count() - 1)
+ option->positionWithinLine = QStyleOptionToolBar::End;
+ else
+ option->positionWithinLine = QStyleOptionToolBar::Middle;
+
+ if (dock.lines.count() == 1)
+ option->positionOfLine = QStyleOptionToolBar::OnlyOne;
+ else if (j == 0)
+ option->positionOfLine = QStyleOptionToolBar::Beginning;
+ else if (j == dock.lines.count() - 1)
+ option->positionOfLine = QStyleOptionToolBar::End;
+ else
+ option->positionOfLine = QStyleOptionToolBar::Middle;
+
+ return;
+ }
+ }
+ }
+ }
+}
+
+QList<int> QToolBarAreaLayout::indexOf(QWidget *toolBar) const
+{
+ QList<int> result;
+
+ bool found = false;
+
+ for (int i = 0; i < QInternal::DockCount; ++i) {
+ const QToolBarAreaLayoutInfo &dock = docks[i];
+
+ for (int j = 0; j < dock.lines.count(); ++j) {
+ const QToolBarAreaLayoutLine &line = dock.lines.at(j);
+
+ for (int k = 0; k < line.toolBarItems.count(); ++k) {
+ const QToolBarAreaLayoutItem &item = line.toolBarItems.at(k);
+ if (!item.gap && item.widgetItem->widget() == toolBar) {
+ found = true;
+ result.prepend(k);
+ break;
+ }
+ }
+
+ if (found) {
+ result.prepend(j);
+ break;
+ }
+ }
+
+ if (found) {
+ result.prepend(i);
+ break;
+ }
+ }
+
+ return result;
+}
+
+QList<int> QToolBarAreaLayout::gapIndex(const QPoint &pos) const
+{
+ Qt::LayoutDirection dir = mainWindow->layoutDirection();
+ for (int i = 0; i < QInternal::DockCount; ++i) {
+ QPoint p = pos;
+ if (docks[i].o == Qt::Horizontal)
+ p = QStyle::visualPos(dir, docks[i].rect, p);
+ QList<int> result = docks[i].gapIndex(p);
+ if (!result.isEmpty()) {
+ result.prepend(i);
+ return result;
+ }
+ }
+
+ return QList<int>();
+}
+
+QList<int> QToolBarAreaLayout::currentGapIndex() const
+{
+ for (int i = 0; i < QInternal::DockCount; ++i) {
+ const QToolBarAreaLayoutInfo &dock = docks[i];
+
+ for (int j = 0; j < dock.lines.count(); ++j) {
+ const QToolBarAreaLayoutLine &line = dock.lines[j];
+
+ for (int k = 0; k < line.toolBarItems.count(); k++) {
+ if (line.toolBarItems[k].gap) {
+ QList<int> result;
+ result << i << j << k;
+ return result;
+ }
+ }
+ }
+ }
+ return QList<int>();
+}
+
+bool QToolBarAreaLayout::insertGap(QList<int> path, QLayoutItem *item)
+{
+ Q_ASSERT(!path.isEmpty());
+ int i = path.takeFirst();
+ Q_ASSERT(i >= 0 && i < QInternal::DockCount);
+ return docks[i].insertGap(path, item);
+}
+
+void QToolBarAreaLayout::remove(QList<int> path)
+{
+ docks[path.at(0)].lines[path.at(1)].toolBarItems.removeAt(path.at(2));
+}
+
+void QToolBarAreaLayout::remove(QLayoutItem *item)
+{
+ for (int i = 0; i < QInternal::DockCount; ++i) {
+ QToolBarAreaLayoutInfo &dock = docks[i];
+
+ for (int j = 0; j < dock.lines.count(); ++j) {
+ QToolBarAreaLayoutLine &line = dock.lines[j];
+
+ for (int k = 0; k < line.toolBarItems.count(); k++) {
+ if (line.toolBarItems[k].widgetItem == item) {
+ line.toolBarItems.removeAt(k);
+ if (line.toolBarItems.isEmpty())
+ dock.lines.removeAt(j);
+ return;
+ }
+ }
+ }
+ }
+}
+
+void QToolBarAreaLayout::clear()
+{
+ for (int i = 0; i < QInternal::DockCount; ++i)
+ docks[i].clear();
+ rect = QRect(0, 0, -1, -1);
+}
+
+QToolBarAreaLayoutItem &QToolBarAreaLayout::item(QList<int> path)
+{
+ Q_ASSERT(path.count() == 3);
+
+ Q_ASSERT(path.at(0) >= 0 && path.at(0) < QInternal::DockCount);
+ QToolBarAreaLayoutInfo &info = docks[path.at(0)];
+ Q_ASSERT(path.at(1) >= 0 && path.at(1) < info.lines.count());
+ QToolBarAreaLayoutLine &line = info.lines[path.at(1)];
+ Q_ASSERT(path.at(2) >= 0 && path.at(2) < line.toolBarItems.count());
+ return line.toolBarItems[path.at(2)];
+}
+
+QRect QToolBarAreaLayout::itemRect(QList<int> path) const
+{
+ int i = path.takeFirst();
+
+ QRect r = docks[i].itemRect(path);
+ if (docks[i].o == Qt::Horizontal)
+ r = QStyle::visualRect(mainWindow->layoutDirection(),
+ docks[i].rect, r);
+ return r;
+}
+
+QLayoutItem *QToolBarAreaLayout::plug(QList<int> path)
+{
+ QToolBarAreaLayoutItem &item = this->item(path);
+ Q_ASSERT(item.gap);
+ Q_ASSERT(item.widgetItem != 0);
+ item.gap = false;
+ return item.widgetItem;
+}
+
+QLayoutItem *QToolBarAreaLayout::unplug(QList<int> path, QToolBarAreaLayout *other)
+{
+ //other needs to be update as well
+ QToolBarAreaLayoutItem &item = this->item(path);
+
+ //update the leading space here
+ QToolBarAreaLayoutInfo &info = docks[path.at(0)];
+ QToolBarAreaLayoutLine &line = info.lines[path.at(1)];
+ if (item.extraSpace != 0) {
+ int newExtraSpace = 0;
+ for (int i = path.at(2) - 1; i >= 0; --i) {
+ QToolBarAreaLayoutItem &previous = line.toolBarItems[i];
+ if (!previous.skip()) {
+ for (int j = path.at(2) + 1; j < line.toolBarItems.count(); ++j) {
+ const QToolBarAreaLayoutItem &next = line.toolBarItems.at(j);
+ if (!next.skip()) {
+ newExtraSpace = previous.extraSpace = next.pos - previous.pos - pick(line.o, previous.sizeHint());
+ }
+ break;
+ }
+ break;
+ }
+ }
+
+ if (other) {
+ QToolBarAreaLayoutInfo &info = other->docks[path.at(0)];
+ QToolBarAreaLayoutLine &line = info.lines[path.at(1)];
+ for (int i = path.at(2) - 1; i >= 0; --i) {
+ QToolBarAreaLayoutItem &previous = line.toolBarItems[i];
+ if (!previous.skip()) {
+ previous.extraSpace = newExtraSpace;
+ break;
+ }
+ }
+
+ }
+ }
+
+
+ Q_ASSERT(!item.gap);
+ item.gap = true;
+ return item.widgetItem;
+}
+
+static QRect unpackRect(uint geom0, uint geom1, bool *floating)
+{
+ *floating = geom0 & 1;
+ if (!*floating)
+ return QRect();
+
+ geom0 >>= 1;
+
+ int x = (int)(geom0 & 0x0000ffff) - 0x7FFF;
+ int y = (int)(geom1 & 0x0000ffff) - 0x7FFF;
+
+ geom0 >>= 16;
+ geom1 >>= 16;
+
+ int w = geom0 & 0x0000ffff;
+ int h = geom1 & 0x0000ffff;
+
+ return QRect(x, y, w, h);
+}
+
+static void packRect(uint *geom0, uint *geom1, const QRect &rect, bool floating)
+{
+ *geom0 = 0;
+ *geom1 = 0;
+
+ if (!floating)
+ return;
+
+ // The 0x7FFF is half of 0xFFFF. We add it so we can handle negative coordinates on
+ // dual monitors. It's subtracted when unpacking.
+
+ *geom0 |= qMax(0, rect.width()) & 0x0000ffff;
+ *geom1 |= qMax(0, rect.height()) & 0x0000ffff;
+
+ *geom0 <<= 16;
+ *geom1 <<= 16;
+
+ *geom0 |= qMax(0, rect.x() + 0x7FFF) & 0x0000ffff;
+ *geom1 |= qMax(0, rect.y() + 0x7FFF) & 0x0000ffff;
+
+ // yeah, we chop one bit off the width, but it still has a range up to 32512
+
+ *geom0 <<= 1;
+ *geom0 |= 1;
+}
+
+
+void QToolBarAreaLayout::saveState(QDataStream &stream) const
+{
+ // save toolbar state
+ stream << (uchar) ToolBarStateMarkerEx;
+
+ int lineCount = 0;
+ for (int i = 0; i < QInternal::DockCount; ++i)
+ lineCount += docks[i].lines.count();
+
+ stream << lineCount;
+
+ for (int i = 0; i < QInternal::DockCount; ++i) {
+ const QToolBarAreaLayoutInfo &dock = docks[i];
+
+ for (int j = 0; j < dock.lines.count(); ++j) {
+ const QToolBarAreaLayoutLine &line = dock.lines.at(j);
+
+ stream << i << line.toolBarItems.count();
+
+ for (int k = 0; k < line.toolBarItems.count(); ++k) {
+ const QToolBarAreaLayoutItem &item = line.toolBarItems.at(k);
+ QWidget *widget = const_cast<QLayoutItem*>(item.widgetItem)->widget();
+ QString objectName = widget->objectName();
+ if (objectName.isEmpty()) {
+ qWarning("QMainWindow::saveState(): 'objectName' not set for QToolBar %p '%s'",
+ widget, widget->windowTitle().toLocal8Bit().constData());
+ }
+ stream << objectName;
+ // we store information as:
+ // 1st bit: 1 if shown
+ // 2nd bit: 1 if orientation is vertical (default is horizontal)
+ uchar shownOrientation = (uchar)!widget->isHidden();
+ if (QToolBar * tb= qobject_cast<QToolBar*>(widget)) {
+ if (tb->orientation() == Qt::Vertical)
+ shownOrientation |= 2;
+ }
+ stream << shownOrientation;
+ stream << item.pos;
+ //if extraSpace is 0 the item has its "normal" size, so no need to store the size (we store -1)
+ stream << (item.extraSpace == 0 ? -1 : (pick(line.o, item.realSizeHint()) + item.extraSpace));
+
+ uint geom0, geom1;
+ packRect(&geom0, &geom1, widget->geometry(), widget->isWindow());
+ stream << geom0 << geom1;
+ }
+ }
+ }
+}
+
+static inline int getInt(QDataStream &stream, Qt::Orientation o, bool pre43)
+{
+ if (pre43) {
+ QPoint p;
+ stream >> p;
+ return pick(o, p);
+ } else {
+ int x;
+ stream >> x;
+ return x;
+ }
+}
+
+
+bool QToolBarAreaLayout::restoreState(QDataStream &stream, const QList<QToolBar*> &_toolBars, uchar tmarker, bool pre43, bool testing)
+{
+ QList<QToolBar*> toolBars = _toolBars;
+ int lines;
+ stream >> lines;
+
+ for (int j = 0; j < lines; ++j) {
+ int pos;
+ stream >> pos;
+ if (pos < 0 || pos >= QInternal::DockCount)
+ return false;
+ int cnt;
+ stream >> cnt;
+
+ QToolBarAreaLayoutInfo &dock = docks[pos];
+ QToolBarAreaLayoutLine line(dock.o);
+
+ for (int k = 0; k < cnt; ++k) {
+ QToolBarAreaLayoutItem item;
+
+ QString objectName;
+ stream >> objectName;
+ uchar shown;
+ stream >> shown;
+ item.pos = getInt(stream, dock.o, pre43);
+ item.size = getInt(stream, dock.o, pre43);
+
+ /*
+ 4.3.0 added floating toolbars, but failed to add the ability to restore them.
+ We need to store there geometry (four ints). We cannot change the format in a
+ patch release (4.3.1) by adding ToolBarStateMarkerEx2 to signal extra data. So
+ for now we'll pack it in the two legacy ints we no longer used in Qt4.3.0.
+ In 4.4, we should add ToolBarStateMarkerEx2 and fix this properly.
+ */
+
+ QRect rect;
+ bool floating = false;
+ uint geom0, geom1;
+ geom0 = getInt(stream, dock.o, pre43);
+ if (tmarker == ToolBarStateMarkerEx) {
+ geom1 = getInt(stream, dock.o, pre43);
+ rect = unpackRect(geom0, geom1, &floating);
+ }
+
+ QToolBar *toolBar = 0;
+ for (int x = 0; x < toolBars.count(); ++x) {
+ if (toolBars.at(x)->objectName() == objectName) {
+ toolBar = toolBars.takeAt(x);
+ break;
+ }
+ }
+ if (toolBar == 0) {
+ continue;
+ }
+
+ if (!testing) {
+ item.widgetItem = new QWidgetItemV2(toolBar);
+ toolBar->setOrientation(floating ? ((shown & 2) ? Qt::Vertical : Qt::Horizontal) : dock.o);
+ toolBar->setVisible(shown & 1);
+ toolBar->d_func()->setWindowState(floating, true, rect);
+
+ //if it is -1, it means we should use the default size
+ item.extraSpace = (item.size == -1) ? 0 : item.size - pick(line.o, item.realSizeHint());
+
+
+ line.toolBarItems.append(item);
+ }
+ }
+
+ if (!testing) {
+ dock.lines.append(line);
+ }
+ }
+
+
+ return stream.status() == QDataStream::Ok;
+}
+
+bool QToolBarAreaLayout::isEmpty() const
+{
+ for (int i = 0; i < QInternal::DockCount; ++i) {
+ if (!docks[i].lines.isEmpty())
+ return false;
+ }
+ return true;
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_TOOLBAR
diff --git a/src/gui/widgets/qtoolbararealayout_p.h b/src/gui/widgets/qtoolbararealayout_p.h
new file mode 100644
index 0000000000..574e366a41
--- /dev/null
+++ b/src/gui/widgets/qtoolbararealayout_p.h
@@ -0,0 +1,199 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QTOOLBARAREALAYOUT_P_H
+#define QTOOLBARAREALAYOUT_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 <QList>
+#include <QSize>
+#include <QRect>
+
+QT_BEGIN_NAMESPACE
+
+#ifndef QT_NO_TOOLBAR
+
+class QToolBar;
+class QLayoutItem;
+class QMainWindow;
+class QStyleOptionToolBar;
+
+class QToolBarAreaLayoutItem
+{
+public:
+ QToolBarAreaLayoutItem(QLayoutItem *item = 0)
+ : widgetItem(item), pos(0), size(-1), extraSpace(0), gap(false) {}
+
+ bool skip() const;
+ QSize minimumSize() const;
+ QSize sizeHint() const;
+ QSize realSizeHint() const;
+
+ QLayoutItem *widgetItem;
+ int pos;
+ int size;
+ int extraSpace;
+ bool gap;
+};
+
+class QToolBarAreaLayoutLine
+{
+public:
+ QToolBarAreaLayoutLine(Qt::Orientation orientation);
+
+ QSize sizeHint() const;
+ QSize minimumSize() const;
+
+ void fitLayout();
+ bool skip() const;
+
+ QRect rect;
+ Qt::Orientation o;
+
+ QList<QToolBarAreaLayoutItem> toolBarItems;
+};
+
+class QToolBarAreaLayoutInfo
+{
+public:
+ enum { EmptyDockAreaSize = 80 }; // when a dock area is empty, how "wide" is it?
+
+ QToolBarAreaLayoutInfo(QInternal::DockPosition pos = QInternal::TopDock);
+
+ QList<QToolBarAreaLayoutLine> lines;
+
+ QSize sizeHint() const;
+ QSize minimumSize() const;
+
+ void fitLayout();
+
+ QLayoutItem *insertToolBar(QToolBar *before, QToolBar *toolBar);
+ void insertItem(QToolBar *before, QLayoutItem *item);
+ void removeToolBar(QToolBar *toolBar);
+ void insertToolBarBreak(QToolBar *before);
+ void removeToolBarBreak(QToolBar *before);
+ void moveToolBar(QToolBar *toolbar, int pos);
+
+ QList<int> gapIndex(const QPoint &pos) const;
+ bool insertGap(QList<int> path, QLayoutItem *item);
+ void clear();
+ QRect itemRect(QList<int> path) const;
+ QRect appendLineDropRect() const;
+
+ QRect rect;
+ Qt::Orientation o;
+ QInternal::DockPosition dockPos;
+ bool dirty;
+};
+
+class QToolBarAreaLayout
+{
+public:
+ enum { // sentinel values used to validate state data
+ ToolBarStateMarker = 0xfe,
+ ToolBarStateMarkerEx = 0xfc
+ };
+
+ QRect rect;
+ QMainWindow *mainWindow;
+ QToolBarAreaLayoutInfo docks[4];
+ bool visible;
+
+ QToolBarAreaLayout(QMainWindow *win);
+
+ QRect fitLayout();
+
+ QSize minimumSize(const QSize &centerMin) const;
+ QRect rectHint(const QRect &r) const;
+ QSize sizeHint(const QSize &center) const;
+ void apply(bool animate);
+
+ QLayoutItem *itemAt(int *x, int index) const;
+ QLayoutItem *takeAt(int *x, int index);
+ void deleteAllLayoutItems();
+
+ QLayoutItem *insertToolBar(QToolBar *before, QToolBar *toolBar);
+ void removeToolBar(QToolBar *toolBar);
+ QLayoutItem *addToolBar(QInternal::DockPosition pos, QToolBar *toolBar);
+ void insertToolBarBreak(QToolBar *before);
+ void removeToolBarBreak(QToolBar *before);
+ void addToolBarBreak(QInternal::DockPosition pos);
+ void moveToolBar(QToolBar *toolbar, int pos);
+
+ void insertItem(QInternal::DockPosition pos, QLayoutItem *item);
+ void insertItem(QToolBar *before, QLayoutItem *item);
+
+ QInternal::DockPosition findToolBar(QToolBar *toolBar) const;
+ bool toolBarBreak(QToolBar *toolBar) const;
+
+ void getStyleOptionInfo(QStyleOptionToolBar *option, QToolBar *toolBar) const;
+
+ QList<int> indexOf(QWidget *toolBar) const;
+ QList<int> gapIndex(const QPoint &pos) const;
+ QList<int> currentGapIndex() const;
+ bool insertGap(QList<int> path, QLayoutItem *item);
+ void remove(QList<int> path);
+ void remove(QLayoutItem *item);
+ void clear();
+ QToolBarAreaLayoutItem &item(QList<int> path);
+ QRect itemRect(QList<int> path) const;
+ QLayoutItem *plug(QList<int> path);
+ QLayoutItem *unplug(QList<int> path, QToolBarAreaLayout *other);
+
+ void saveState(QDataStream &stream) const;
+ bool restoreState(QDataStream &stream, const QList<QToolBar*> &toolBars, uchar tmarker, bool pre43, bool testing = false);
+ bool isEmpty() const;
+};
+
+
+QT_END_NAMESPACE
+#endif // QT_NO_TOOLBAR
+#endif // QTOOLBARAREALAYOUT_P_H
diff --git a/src/gui/widgets/qtoolbarextension.cpp b/src/gui/widgets/qtoolbarextension.cpp
new file mode 100644
index 0000000000..a1c5fd6932
--- /dev/null
+++ b/src/gui/widgets/qtoolbarextension.cpp
@@ -0,0 +1,90 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qtoolbarextension_p.h"
+#include <qpixmap.h>
+#include <qstyle.h>
+#include <qstylepainter.h>
+#include <qstyleoption.h>
+
+#ifndef QT_NO_TOOLBUTTON
+
+QT_BEGIN_NAMESPACE
+
+QToolBarExtension::QToolBarExtension(QWidget *parent)
+ : QToolButton(parent)
+{
+ setObjectName(QLatin1String("qt_toolbar_ext_button"));
+ setAutoRaise(true);
+ setOrientation(Qt::Horizontal);
+ setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
+ setCheckable(true);
+}
+
+void QToolBarExtension::setOrientation(Qt::Orientation o)
+{
+ if (o == Qt::Horizontal) {
+ setIcon(style()->standardIcon(QStyle::SP_ToolBarHorizontalExtensionButton));
+ } else {
+ setIcon(style()->standardIcon(QStyle::SP_ToolBarVerticalExtensionButton));
+ }
+}
+
+void QToolBarExtension::paintEvent(QPaintEvent *)
+{
+ QStylePainter p(this);
+ QStyleOptionToolButton opt;
+ initStyleOption(&opt);
+ // We do not need to draw both extention arrows
+ opt.features &= ~QStyleOptionToolButton::HasMenu;
+ p.drawComplexControl(QStyle::CC_ToolButton, opt);
+}
+
+
+QSize QToolBarExtension::sizeHint() const
+{
+ int ext = style()->pixelMetric(QStyle::PM_ToolBarExtensionExtent);
+ return QSize(ext, ext);
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_TOOLBUTTON
diff --git a/src/gui/widgets/qtoolbarextension_p.h b/src/gui/widgets/qtoolbarextension_p.h
new file mode 100644
index 0000000000..3f3a459cbf
--- /dev/null
+++ b/src/gui/widgets/qtoolbarextension_p.h
@@ -0,0 +1,80 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QDYNAMICTOOLBAREXTENSION_P_H
+#define QDYNAMICTOOLBAREXTENSION_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 "QtGui/qtoolbutton.h"
+
+QT_BEGIN_NAMESPACE
+
+#ifndef QT_NO_TOOLBUTTON
+
+class QToolBarExtension : public QToolButton
+{
+ Q_OBJECT
+ Qt::Orientation orientation;
+
+public:
+ explicit QToolBarExtension(QWidget *parent);
+ void paintEvent(QPaintEvent *);
+ QSize sizeHint() const;
+
+public Q_SLOTS:
+ void setOrientation(Qt::Orientation o);
+};
+
+#endif // QT_NO_TOOLBUTTON
+
+QT_END_NAMESPACE
+
+#endif // QDYNAMICTOOLBAREXTENSION_P_H
diff --git a/src/gui/widgets/qtoolbarlayout.cpp b/src/gui/widgets/qtoolbarlayout.cpp
new file mode 100644
index 0000000000..7771f463a9
--- /dev/null
+++ b/src/gui/widgets/qtoolbarlayout.cpp
@@ -0,0 +1,752 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <qaction.h>
+#include <qwidgetaction.h>
+#include <qtoolbar.h>
+#include <qstyleoption.h>
+#include <qtoolbutton.h>
+#include <qmenu.h>
+#include <qdebug.h>
+#include <qmath.h>
+
+#include "qmainwindowlayout_p.h"
+#include "qtoolbarextension_p.h"
+#include "qtoolbarlayout_p.h"
+#include "qtoolbarseparator_p.h"
+
+#ifndef QT_NO_TOOLBAR
+
+QT_BEGIN_NAMESPACE
+
+/******************************************************************************
+** QToolBarItem
+*/
+
+QToolBarItem::QToolBarItem(QWidget *widget)
+ : QWidgetItem(widget), action(0), customWidget(false)
+{
+}
+
+bool QToolBarItem::isEmpty() const
+{
+ return action == 0 || !action->isVisible();
+}
+
+/******************************************************************************
+** QToolBarLayout
+*/
+
+QToolBarLayout::QToolBarLayout(QWidget *parent)
+ : QLayout(parent), expanded(false), animating(false), dirty(true),
+ expanding(false), empty(true), expandFlag(false), popupMenu(0)
+{
+ QToolBar *tb = qobject_cast<QToolBar*>(parent);
+ if (!tb)
+ return;
+
+ extension = new QToolBarExtension(tb);
+ extension->setFocusPolicy(Qt::NoFocus);
+ extension->hide();
+ QObject::connect(tb, SIGNAL(orientationChanged(Qt::Orientation)),
+ extension, SLOT(setOrientation(Qt::Orientation)));
+
+ setUsePopupMenu(qobject_cast<QMainWindow*>(tb->parentWidget()) == 0);
+}
+
+QToolBarLayout::~QToolBarLayout()
+{
+ while (!items.isEmpty()) {
+ QToolBarItem *item = items.takeFirst();
+ if (QWidgetAction *widgetAction = qobject_cast<QWidgetAction*>(item->action)) {
+ if (item->customWidget)
+ widgetAction->releaseWidget(item->widget());
+ }
+ delete item;
+ }
+}
+
+void QToolBarLayout::updateMarginAndSpacing()
+{
+ QToolBar *tb = qobject_cast<QToolBar*>(parentWidget());
+ if (!tb)
+ return;
+ QStyle *style = tb->style();
+ QStyleOptionToolBar opt;
+ tb->initStyleOption(&opt);
+ setMargin(style->pixelMetric(QStyle::PM_ToolBarItemMargin, &opt, tb)
+ + style->pixelMetric(QStyle::PM_ToolBarFrameWidth, &opt, tb));
+ setSpacing(style->pixelMetric(QStyle::PM_ToolBarItemSpacing, &opt, tb));
+}
+
+bool QToolBarLayout::hasExpandFlag() const
+{
+ return expandFlag;
+}
+
+void QToolBarLayout::setUsePopupMenu(bool set)
+{
+ if (!dirty && ((popupMenu == 0) == set))
+ invalidate();
+ if (!set) {
+ QObject::connect(extension, SIGNAL(clicked(bool)),
+ this, SLOT(setExpanded(bool)));
+ extension->setPopupMode(QToolButton::DelayedPopup);
+ extension->setMenu(0);
+ delete popupMenu;
+ popupMenu = 0;
+ } else {
+ QObject::disconnect(extension, SIGNAL(clicked(bool)),
+ this, SLOT(setExpanded(bool)));
+ extension->setPopupMode(QToolButton::InstantPopup);
+ if (!popupMenu) {
+ popupMenu = new QMenu(extension);
+ }
+ extension->setMenu(popupMenu);
+ }
+}
+
+void QToolBarLayout::checkUsePopupMenu()
+{
+ QToolBar *tb = static_cast<QToolBar *>(parent());
+ QMainWindow *mw = qobject_cast<QMainWindow *>(tb->parent());
+ Qt::Orientation o = tb->orientation();
+ setUsePopupMenu(!mw || tb->isFloating() || perp(o, expandedSize(mw->size())) >= perp(o, mw->size()));
+}
+
+void QToolBarLayout::addItem(QLayoutItem*)
+{
+ qWarning() << "QToolBarLayout::addItem(): please use addAction() instead";
+ return;
+}
+
+QLayoutItem *QToolBarLayout::itemAt(int index) const
+{
+ if (index < 0 || index >= items.count())
+ return 0;
+ return items.at(index);
+}
+
+QLayoutItem *QToolBarLayout::takeAt(int index)
+{
+ if (index < 0 || index >= items.count())
+ return 0;
+ QToolBarItem *item = items.takeAt(index);
+
+ if (popupMenu)
+ popupMenu->removeAction(item->action);
+
+ QWidgetAction *widgetAction = qobject_cast<QWidgetAction*>(item->action);
+ if (widgetAction != 0 && item->customWidget) {
+ widgetAction->releaseWidget(item->widget());
+ } else {
+ // destroy the QToolButton/QToolBarSeparator
+ item->widget()->hide();
+ item->widget()->deleteLater();
+ }
+
+ invalidate();
+ return item;
+}
+
+void QToolBarLayout::insertAction(int index, QAction *action)
+{
+ index = qMax(0, index);
+ index = qMin(items.count(), index);
+
+ QToolBarItem *item = createItem(action);
+ if (item) {
+ items.insert(index, item);
+ invalidate();
+ }
+}
+
+int QToolBarLayout::indexOf(QAction *action) const
+{
+ for (int i = 0; i < items.count(); ++i) {
+ if (items.at(i)->action == action)
+ return i;
+ }
+ return -1;
+}
+
+int QToolBarLayout::count() const
+{
+ return items.count();
+}
+
+bool QToolBarLayout::isEmpty() const
+{
+ if (dirty)
+ updateGeomArray();
+ return empty;
+}
+
+void QToolBarLayout::invalidate()
+{
+ dirty = true;
+ QLayout::invalidate();
+}
+
+Qt::Orientations QToolBarLayout::expandingDirections() const
+{
+ if (dirty)
+ updateGeomArray();
+ QToolBar *tb = qobject_cast<QToolBar*>(parentWidget());
+ if (!tb)
+ return Qt::Orientations(0);
+ Qt::Orientation o = tb->orientation();
+ return expanding ? Qt::Orientations(o) : Qt::Orientations(0);
+}
+
+bool QToolBarLayout::movable() const
+{
+ QToolBar *tb = qobject_cast<QToolBar*>(parentWidget());
+ if (!tb)
+ return false;
+ QMainWindow *win = qobject_cast<QMainWindow*>(tb->parentWidget());
+ return tb->isMovable() && win != 0;
+}
+
+void QToolBarLayout::updateGeomArray() const
+{
+ if (!dirty)
+ return;
+
+ QToolBarLayout *that = const_cast<QToolBarLayout*>(this);
+
+ QToolBar *tb = qobject_cast<QToolBar*>(parentWidget());
+ if (!tb)
+ return;
+ QStyle *style = tb->style();
+ QStyleOptionToolBar opt;
+ tb->initStyleOption(&opt);
+ const int handleExtent = movable()
+ ? style->pixelMetric(QStyle::PM_ToolBarHandleExtent, &opt, tb) : 0;
+ const int margin = this->margin();
+ const int spacing = this->spacing();
+ const int extensionExtent = style->pixelMetric(QStyle::PM_ToolBarExtensionExtent, &opt, tb);
+ Qt::Orientation o = tb->orientation();
+
+ that->minSize = QSize(0, 0);
+ that->hint = QSize(0, 0);
+ rperp(o, that->minSize) = style->pixelMetric(QStyle::PM_ToolBarHandleExtent, &opt, tb);
+ rperp(o, that->hint) = style->pixelMetric(QStyle::PM_ToolBarHandleExtent, &opt, tb);
+
+ that->expanding = false;
+ that->empty = false;
+
+ QVector<QLayoutStruct> a(items.count() + 1); // + 1 for the stretch
+
+ int count = 0;
+ for (int i = 0; i < items.count(); ++i) {
+ QToolBarItem *item = items.at(i);
+
+ QSize max = item->maximumSize();
+ QSize min = item->minimumSize();
+ QSize hint = item->sizeHint();
+ Qt::Orientations exp = item->expandingDirections();
+ bool empty = item->isEmpty();
+
+ that->expanding = expanding || exp & o;
+
+
+ if (item->widget()) {
+ if ((item->widget()->sizePolicy().horizontalPolicy() & QSizePolicy::ExpandFlag)) {
+ that->expandFlag = true;
+ }
+ }
+
+ if (!empty) {
+ if (count == 0) // the minimum size only displays one widget
+ rpick(o, that->minSize) += pick(o, min);
+ int s = perp(o, minSize);
+ rperp(o, that->minSize) = qMax(s, perp(o, min));
+
+ //we only add spacing before item (ie never before the first one)
+ rpick(o, that->hint) += (count == 0 ? 0 : spacing) + pick(o, hint);
+ s = perp(o, that->hint);
+ rperp(o, that->hint) = qMax(s, perp(o, hint));
+ ++count;
+ }
+
+ a[i].sizeHint = pick(o, hint);
+ a[i].maximumSize = pick(o, max);
+ a[i].minimumSize = pick(o, min);
+ a[i].expansive = exp & o;
+ if (o == Qt::Horizontal)
+ a[i].stretch = item->widget()->sizePolicy().horizontalStretch();
+ else
+ a[i].stretch = item->widget()->sizePolicy().verticalStretch();
+ a[i].empty = empty;
+ }
+
+ that->geomArray = a;
+ that->empty = count == 0;
+
+ rpick(o, that->minSize) += handleExtent;
+ that->minSize += QSize(2*margin, 2*margin);
+ if (items.count() > 1)
+ rpick(o, that->minSize) += spacing + extensionExtent;
+
+ rpick(o, that->hint) += handleExtent;
+ that->hint += QSize(2*margin, 2*margin);
+ that->dirty = false;
+#ifdef Q_WS_MAC
+ if (QMainWindow *mw = qobject_cast<QMainWindow *>(parentWidget()->parentWidget())) {
+ if (mw->unifiedTitleAndToolBarOnMac()
+ && mw->toolBarArea(static_cast<QToolBar *>(parentWidget())) == Qt::TopToolBarArea) {
+ if (that->expandFlag) {
+ tb->setMaximumSize(0xFFFFFF, 0xFFFFFF);
+ } else {
+ tb->setMaximumSize(hint);
+ }
+ }
+ }
+#endif
+
+ that->dirty = false;
+}
+
+static bool defaultWidgetAction(QToolBarItem *item)
+{
+ QWidgetAction *a = qobject_cast<QWidgetAction*>(item->action);
+ return a != 0 && a->defaultWidget() == item->widget();
+}
+
+void QToolBarLayout::setGeometry(const QRect &rect)
+{
+ QToolBar *tb = qobject_cast<QToolBar*>(parentWidget());
+ if (!tb)
+ return;
+ QStyle *style = tb->style();
+ QStyleOptionToolBar opt;
+ tb->initStyleOption(&opt);
+ const int handleExtent = movable()
+ ? style->pixelMetric(QStyle::PM_ToolBarHandleExtent, &opt, tb) : 0;
+ const int margin = this->margin();
+ const int extensionExtent = style->pixelMetric(QStyle::PM_ToolBarExtensionExtent, &opt, tb);
+ Qt::Orientation o = tb->orientation();
+
+ QLayout::setGeometry(rect);
+ if (movable()) {
+ if (o == Qt::Horizontal) {
+ handRect = QRect(margin, margin, handleExtent, rect.height() - 2*margin);
+ handRect = QStyle::visualRect(parentWidget()->layoutDirection(), rect, handRect);
+ } else {
+ handRect = QRect(margin, margin, rect.width() - 2*margin, handleExtent);
+ }
+ } else {
+ handRect = QRect();
+ }
+
+ bool ranOutOfSpace = false;
+ if (!animating)
+ ranOutOfSpace = layoutActions(rect.size());
+
+ if (expanded || animating || ranOutOfSpace) {
+ Qt::ToolBarArea area = Qt::TopToolBarArea;
+ if (QMainWindow *win = qobject_cast<QMainWindow*>(tb->parentWidget()))
+ area = win->toolBarArea(tb);
+ QSize hint = sizeHint();
+
+ QPoint pos;
+ rpick(o, pos) = pick(o, rect.bottomRight()) - margin - extensionExtent + 2;
+ if (area == Qt::LeftToolBarArea || area == Qt::TopToolBarArea)
+ rperp(o, pos) = perp(o, rect.topLeft()) + margin;
+ else
+ rperp(o, pos) = perp(o, rect.bottomRight()) - margin - (perp(o, hint) - 2*margin) + 1;
+ QSize size;
+ rpick(o, size) = extensionExtent;
+ rperp(o, size) = perp(o, hint) - 2*margin;
+ QRect r(pos, size);
+
+ if (o == Qt::Horizontal)
+ r = QStyle::visualRect(parentWidget()->layoutDirection(), rect, r);
+
+ extension->setGeometry(r);
+
+ if (extension->isHidden())
+ extension->show();
+ } else {
+ if (!extension->isHidden())
+ extension->hide();
+ }
+#ifdef Q_WS_MAC
+ // Nothing to do for Carbon... probably
+# ifdef QT_MAC_USE_COCOA
+ if (QMainWindow *win = qobject_cast<QMainWindow*>(tb->parentWidget())) {
+ Qt::ToolBarArea area = win->toolBarArea(tb);
+ if (win->unifiedTitleAndToolBarOnMac() && area == Qt::TopToolBarArea) {
+ static_cast<QMainWindowLayout *>(win->layout())->fixSizeInUnifiedToolbar(tb);
+ }
+ }
+# endif
+#endif
+
+}
+
+bool QToolBarLayout::layoutActions(const QSize &size)
+{
+ if (dirty)
+ updateGeomArray();
+
+ QRect rect(0, 0, size.width(), size.height());
+
+ QList<QWidget*> showWidgets, hideWidgets;
+
+ QToolBar *tb = qobject_cast<QToolBar*>(parentWidget());
+ if (!tb)
+ return false;
+ QStyle *style = tb->style();
+ QStyleOptionToolBar opt;
+ tb->initStyleOption(&opt);
+ const int handleExtent = movable()
+ ? style->pixelMetric(QStyle::PM_ToolBarHandleExtent, &opt, tb) : 0;
+ const int margin = this->margin();
+ const int spacing = this->spacing();
+ const int extensionExtent = style->pixelMetric(QStyle::PM_ToolBarExtensionExtent, &opt, tb);
+ Qt::Orientation o = tb->orientation();
+ bool extensionMenuContainsOnlyWidgetActions = true;
+
+ int space = pick(o, rect.size()) - 2*margin - handleExtent;
+ if (space <= 0)
+ return false; // nothing to do.
+
+ if(popupMenu)
+ popupMenu->clear();
+
+ bool ranOutOfSpace = false;
+ int rows = 0;
+ int rowPos = perp(o, rect.topLeft()) + margin;
+ int i = 0;
+ while (i < items.count()) {
+ QVector<QLayoutStruct> a = geomArray;
+
+ int start = i;
+ int size = 0;
+ int prev = -1;
+ int rowHeight = 0;
+ int count = 0;
+ int maximumSize = 0;
+ bool expansiveRow = false;
+ for (; i < items.count(); ++i) {
+ if (a[i].empty)
+ continue;
+
+ int newSize = size + (count == 0 ? 0 : spacing) + a[i].minimumSize;
+ if (prev != -1 && newSize > space) {
+ if (rows == 0)
+ ranOutOfSpace = true;
+ // do we have to move the previous item to the next line to make space for
+ // the extension button?
+ if (count > 1 && size + spacing + extensionExtent > space)
+ i = prev;
+ break;
+ }
+
+ if (expanded)
+ rowHeight = qMax(rowHeight, perp(o, items.at(i)->sizeHint()));
+ expansiveRow = expansiveRow || a[i].expansive;
+ size = newSize;
+ maximumSize += spacing + (a[i].expansive ? a[i].maximumSize : a[i].smartSizeHint());
+ prev = i;
+ ++count;
+ }
+
+ // stretch at the end
+ a[i].sizeHint = 0;
+ a[i].maximumSize = QWIDGETSIZE_MAX;
+ a[i].minimumSize = 0;
+ a[i].expansive = true;
+ a[i].stretch = 0;
+ a[i].empty = true;
+
+ if (expansiveRow && maximumSize < space) {
+ expansiveRow = false;
+ a[i].maximumSize = space - maximumSize;
+ }
+
+ qGeomCalc(a, start, i - start + (expansiveRow ? 0 : 1), 0,
+ space - (ranOutOfSpace ? (extensionExtent + spacing) : 0),
+ spacing);
+
+ for (int j = start; j < i; ++j) {
+ QToolBarItem *item = items.at(j);
+
+ if (a[j].empty) {
+ if (!item->widget()->isHidden())
+ hideWidgets << item->widget();
+ continue;
+ }
+
+ QPoint pos;
+ rpick(o, pos) = margin + handleExtent + a[j].pos;
+ rperp(o, pos) = rowPos;
+ QSize size;
+ rpick(o, size) = a[j].size;
+ if (expanded)
+ rperp(o, size) = rowHeight;
+ else
+ rperp(o, size) = perp(o, rect.size()) - 2*margin;
+ QRect r(pos, size);
+
+ if (o == Qt::Horizontal)
+ r = QStyle::visualRect(parentWidget()->layoutDirection(), rect, r);
+
+ item->setGeometry(r);
+
+ if (item->widget()->isHidden())
+ showWidgets << item->widget();
+ }
+
+ if (!expanded) {
+ for (int j = i; j < items.count(); ++j) {
+ QToolBarItem *item = items.at(j);
+ if (!item->widget()->isHidden())
+ hideWidgets << item->widget();
+ if (popupMenu) {
+ if (!defaultWidgetAction(item)) {
+ popupMenu->addAction(item->action);
+ extensionMenuContainsOnlyWidgetActions = false;
+ }
+ }
+ }
+ break;
+ }
+
+ rowPos += rowHeight + spacing;
+ ++rows;
+ }
+
+ // if we are using a popup menu, not the expadning toolbar effect, we cannot move custom
+ // widgets into the menu. If only custom widget actions are chopped off, the popup menu
+ // is empty. So we show the little extension button to show something is chopped off,
+ // but we make it disabled.
+ extension->setEnabled(popupMenu == 0 || !extensionMenuContainsOnlyWidgetActions);
+
+ // we have to do the show/hide here, because it triggers more calls to setGeometry :(
+ for (int i = 0; i < showWidgets.count(); ++i)
+ showWidgets.at(i)->show();
+ for (int i = 0; i < hideWidgets.count(); ++i)
+ hideWidgets.at(i)->hide();
+
+ return ranOutOfSpace;
+}
+
+QSize QToolBarLayout::expandedSize(const QSize &size) const
+{
+ if (dirty)
+ updateGeomArray();
+
+ QToolBar *tb = qobject_cast<QToolBar*>(parentWidget());
+ if (!tb)
+ return QSize(0, 0);
+ QMainWindow *win = qobject_cast<QMainWindow*>(tb->parentWidget());
+ Qt::Orientation o = tb->orientation();
+ QStyle *style = tb->style();
+ QStyleOptionToolBar opt;
+ tb->initStyleOption(&opt);
+ const int handleExtent = movable()
+ ? style->pixelMetric(QStyle::PM_ToolBarHandleExtent, &opt, tb) : 0;
+ const int margin = this->margin();
+ const int spacing = this->spacing();
+ const int extensionExtent = style->pixelMetric(QStyle::PM_ToolBarExtensionExtent, &opt, tb);
+
+ int total_w = 0;
+ int count = 0;
+ for (int x = 0; x < items.count(); ++x) {
+ if (!geomArray[x].empty) {
+ total_w += (count == 0 ? 0 : spacing) + geomArray[x].minimumSize;
+ ++count;
+ }
+ }
+ if (count == 0)
+ return QSize(0, 0);
+
+ int min_w = pick(o, size);
+ int rows = (int)qSqrt(qreal(count));
+ if (rows == 1)
+ ++rows; // we want to expand to at least two rows
+ int space = total_w/rows + spacing + extensionExtent;
+ space = qMax(space, min_w - 2*margin - handleExtent);
+ if (win != 0)
+ space = qMin(space, pick(o, win->size()) - 2*margin - handleExtent);
+
+ int w = 0;
+ int h = 0;
+ int i = 0;
+ while (i < items.count()) {
+ int count = 0;
+ int size = 0;
+ int prev = -1;
+ int rowHeight = 0;
+ for (; i < items.count(); ++i) {
+ if (geomArray[i].empty)
+ continue;
+
+ int newSize = size + (count == 0 ? 0 : spacing) + geomArray[i].minimumSize;
+ rowHeight = qMax(rowHeight, perp(o, items.at(i)->sizeHint()));
+ if (prev != -1 && newSize > space) {
+ if (count > 1 && size + spacing + extensionExtent > space) {
+ size -= spacing + geomArray[prev].minimumSize;
+ i = prev;
+ }
+ break;
+ }
+
+ size = newSize;
+ prev = i;
+ ++count;
+ }
+
+ w = qMax(size, w);
+ h += rowHeight + spacing;
+ }
+
+ w += 2*margin + handleExtent + spacing + extensionExtent;
+ w = qMax(w, min_w);
+ if (win != 0)
+ w = qMin(w, pick(o, win->size()));
+ h += 2*margin - spacing; //there is no spacing before the first row
+
+ QSize result;
+ rpick(o, result) = w;
+ rperp(o, result) = h;
+ return result;
+}
+
+void QToolBarLayout::setExpanded(bool exp)
+{
+ if (exp == expanded)
+ return;
+
+ expanded = exp;
+ extension->setChecked(expanded);
+
+ QToolBar *tb = qobject_cast<QToolBar*>(parentWidget());
+ if (!tb)
+ return;
+ if (QMainWindow *win = qobject_cast<QMainWindow*>(tb->parentWidget())) {
+ animating = true;
+ QMainWindowLayout *layout = qobject_cast<QMainWindowLayout*>(win->layout());
+ if (expanded) {
+ tb->raise();
+ } else {
+ QList<int> path = layout->layoutState.indexOf(tb);
+ if (!path.isEmpty()) {
+ QRect rect = layout->layoutState.itemRect(path);
+ layoutActions(rect.size());
+ }
+ }
+ layout->layoutState.toolBarAreaLayout.apply(true);
+ }
+}
+
+QSize QToolBarLayout::minimumSize() const
+{
+ if (dirty)
+ updateGeomArray();
+ return minSize;
+}
+
+QSize QToolBarLayout::sizeHint() const
+{
+ if (dirty)
+ updateGeomArray();
+ return hint;
+}
+
+QToolBarItem *QToolBarLayout::createItem(QAction *action)
+{
+ bool customWidget = false;
+ bool standardButtonWidget = false;
+ QWidget *widget = 0;
+ QToolBar *tb = qobject_cast<QToolBar*>(parentWidget());
+ if (!tb)
+ return (QToolBarItem *)0;
+
+ if (QWidgetAction *widgetAction = qobject_cast<QWidgetAction *>(action)) {
+ widget = widgetAction->requestWidget(tb);
+ if (widget != 0) {
+ widget->setAttribute(Qt::WA_LayoutUsesWidgetRect);
+ customWidget = true;
+ }
+ } else if (action->isSeparator()) {
+ QToolBarSeparator *sep = new QToolBarSeparator(tb);
+ connect(tb, SIGNAL(orientationChanged(Qt::Orientation)),
+ sep, SLOT(setOrientation(Qt::Orientation)));
+ widget = sep;
+ }
+
+ if (!widget) {
+ QToolButton *button = new QToolButton(tb);
+ button->setAutoRaise(true);
+ button->setFocusPolicy(Qt::NoFocus);
+ button->setIconSize(tb->iconSize());
+ button->setToolButtonStyle(tb->toolButtonStyle());
+ QObject::connect(tb, SIGNAL(iconSizeChanged(QSize)),
+ button, SLOT(setIconSize(QSize)));
+ QObject::connect(tb, SIGNAL(toolButtonStyleChanged(Qt::ToolButtonStyle)),
+ button, SLOT(setToolButtonStyle(Qt::ToolButtonStyle)));
+ button->setDefaultAction(action);
+ QObject::connect(button, SIGNAL(triggered(QAction*)), tb, SIGNAL(actionTriggered(QAction*)));
+ widget = button;
+ standardButtonWidget = true;
+ }
+
+ widget->hide();
+ QToolBarItem *result = new QToolBarItem(widget);
+ if (standardButtonWidget)
+ result->setAlignment(Qt::AlignJustify);
+ result->customWidget = customWidget;
+ result->action = action;
+ return result;
+}
+
+QRect QToolBarLayout::handleRect() const
+{
+ return handRect;
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_TOOLBAR
diff --git a/src/gui/widgets/qtoolbarlayout_p.h b/src/gui/widgets/qtoolbarlayout_p.h
new file mode 100644
index 0000000000..2eca773af4
--- /dev/null
+++ b/src/gui/widgets/qtoolbarlayout_p.h
@@ -0,0 +1,136 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QTOOLBARLAYOUT_P_H
+#define QTOOLBARLAYOUT_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 <QtGui/qlayout.h>
+#include <private/qlayoutengine_p.h>
+#include <QVector>
+
+QT_BEGIN_NAMESPACE
+
+#ifndef QT_NO_TOOLBAR
+
+class QAction;
+class QToolBarExtension;
+class QMenu;
+
+class Q_GUI_EXPORT QToolBarItem : public QWidgetItem
+{
+public:
+ QToolBarItem(QWidget *widget);
+ bool isEmpty() const;
+
+ QAction *action;
+ bool customWidget;
+};
+
+class Q_GUI_EXPORT QToolBarLayout : public QLayout
+{
+ Q_OBJECT
+
+public:
+ QToolBarLayout(QWidget *parent = 0);
+ ~QToolBarLayout();
+
+ void addItem(QLayoutItem *item);
+ QLayoutItem *itemAt(int index) const;
+ QLayoutItem *takeAt(int index);
+ int count() const;
+
+ bool isEmpty() const;
+ void invalidate();
+ Qt::Orientations expandingDirections() const;
+
+ void setGeometry(const QRect &r);
+ QSize minimumSize() const;
+ QSize sizeHint() const;
+
+ void insertAction(int index, QAction *action);
+ int indexOf(QAction *action) const;
+ int indexOf(QWidget *widget) const { return QLayout::indexOf(widget); }
+
+ QRect handleRect() const;
+
+ bool layoutActions(const QSize &size);
+ QSize expandedSize(const QSize &size) const;
+ bool expanded, animating;
+
+ void setUsePopupMenu(bool set); // Yeah, there's no getter, but it's internal.
+ void checkUsePopupMenu();
+
+ bool movable() const;
+ void updateMarginAndSpacing();
+ bool hasExpandFlag() const;
+
+public slots:
+ void setExpanded(bool b);
+
+private:
+ QList<QToolBarItem*> items;
+ QSize hint, minSize;
+ bool dirty, expanding, empty, expandFlag;
+ QVector<QLayoutStruct> geomArray;
+ QRect handRect;
+ QToolBarExtension *extension;
+
+ void updateGeomArray() const;
+ QToolBarItem *createItem(QAction *action);
+ QMenu *popupMenu;
+};
+
+#endif // QT_NO_TOOLBAR
+
+QT_END_NAMESPACE
+
+#endif // QTOOLBARLAYOUT_P_H
diff --git a/src/gui/widgets/qtoolbarseparator.cpp b/src/gui/widgets/qtoolbarseparator.cpp
new file mode 100644
index 0000000000..c2426876f2
--- /dev/null
+++ b/src/gui/widgets/qtoolbarseparator.cpp
@@ -0,0 +1,91 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qtoolbarseparator_p.h"
+
+#ifndef QT_NO_TOOLBAR
+
+#include <qstyle.h>
+#include <qstyleoption.h>
+#include <qtoolbar.h>
+#include <qpainter.h>
+
+QT_BEGIN_NAMESPACE
+
+void QToolBarSeparator::initStyleOption(QStyleOption *option) const
+{
+ option->initFrom(this);
+ if (orientation() == Qt::Horizontal)
+ option->state |= QStyle::State_Horizontal;
+}
+
+QToolBarSeparator::QToolBarSeparator(QToolBar *parent)
+ : QWidget(parent), orient(parent->orientation())
+{ setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); }
+
+void QToolBarSeparator::setOrientation(Qt::Orientation orientation)
+{
+ orient = orientation;
+ update();
+}
+
+Qt::Orientation QToolBarSeparator::orientation() const
+{ return orient; }
+
+QSize QToolBarSeparator::sizeHint() const
+{
+ QStyleOption opt;
+ initStyleOption(&opt);
+ const int extent = style()->pixelMetric(QStyle::PM_ToolBarSeparatorExtent, &opt, parentWidget());
+ return QSize(extent, extent);
+}
+
+void QToolBarSeparator::paintEvent(QPaintEvent *)
+{
+ QPainter p(this);
+ QStyleOption opt;
+ initStyleOption(&opt);
+ style()->drawPrimitive(QStyle::PE_IndicatorToolBarSeparator, &opt, &p, parentWidget());
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_TOOLBAR
diff --git a/src/gui/widgets/qtoolbarseparator_p.h b/src/gui/widgets/qtoolbarseparator_p.h
new file mode 100644
index 0000000000..c3552e6027
--- /dev/null
+++ b/src/gui/widgets/qtoolbarseparator_p.h
@@ -0,0 +1,88 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QDYNAMICTOOLBARSEPARATOR_P_H
+#define QDYNAMICTOOLBARSEPARATOR_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 "QtGui/qwidget.h"
+
+QT_BEGIN_NAMESPACE
+
+#ifndef QT_NO_TOOLBAR
+
+class QStyleOption;
+class QToolBar;
+
+class QToolBarSeparator : public QWidget
+{
+ Q_OBJECT
+ Qt::Orientation orient;
+
+public:
+ explicit QToolBarSeparator(QToolBar *parent);
+
+ Qt::Orientation orientation() const;
+
+ QSize sizeHint() const;
+
+ void paintEvent(QPaintEvent *);
+ void initStyleOption(QStyleOption *option) const;
+
+public Q_SLOTS:
+ void setOrientation(Qt::Orientation orientation);
+};
+
+#endif // QT_NO_TOOLBAR
+
+QT_END_NAMESPACE
+
+#endif // QDYNAMICTOOLBARSEPARATOR_P_H
diff --git a/src/gui/widgets/qtoolbox.cpp b/src/gui/widgets/qtoolbox.cpp
new file mode 100644
index 0000000000..81935a573a
--- /dev/null
+++ b/src/gui/widgets/qtoolbox.cpp
@@ -0,0 +1,822 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qtoolbox.h"
+
+#ifndef QT_NO_TOOLBOX
+
+#include <qapplication.h>
+#include <qeventloop.h>
+#include <qlayout.h>
+#include <qlist.h>
+#include <qpainter.h>
+#include <qscrollarea.h>
+#include <qstyle.h>
+#include <qstyleoption.h>
+#include <qtooltip.h>
+#include <qabstractbutton.h>
+
+#include "qframe_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class QToolBoxButton : public QAbstractButton
+{
+ Q_OBJECT
+public:
+ QToolBoxButton(QWidget *parent)
+ : QAbstractButton(parent), selected(false), indexInPage(-1)
+ {
+ setBackgroundRole(QPalette::Window);
+ setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Minimum);
+ setFocusPolicy(Qt::NoFocus);
+ }
+
+ inline void setSelected(bool b) { selected = b; update(); }
+ inline void setIndex(int newIndex) { indexInPage = newIndex; }
+
+ QSize sizeHint() const;
+ QSize minimumSizeHint() const;
+
+protected:
+ void initStyleOption(QStyleOptionToolBox *opt) const;
+ void paintEvent(QPaintEvent *);
+
+private:
+ bool selected;
+ int indexInPage;
+};
+
+
+class QToolBoxPrivate : public QFramePrivate
+{
+ Q_DECLARE_PUBLIC(QToolBox)
+public:
+ struct Page
+ {
+ QToolBoxButton *button;
+ QScrollArea *sv;
+ QWidget *widget;
+
+ inline void setText(const QString &text) { button->setText(text); }
+ inline void setIcon(const QIcon &is) { button->setIcon(is); }
+#ifndef QT_NO_TOOLTIP
+ inline void setToolTip(const QString &tip) { button->setToolTip(tip); }
+ inline QString toolTip() const { return button->toolTip(); }
+#endif
+ inline QString text() const { return button->text(); }
+ inline QIcon icon() const { return button->icon(); }
+
+ inline bool operator==(const Page& other) const
+ {
+ return widget == other.widget;
+ }
+ };
+ typedef QList<Page> PageList;
+
+ inline QToolBoxPrivate()
+ : currentPage(0)
+ {
+ }
+ void _q_buttonClicked();
+ void _q_widgetDestroyed(QObject*);
+
+ Page *page(QWidget *widget) const;
+ const Page *page(int index) const;
+ Page *page(int index);
+
+ void updateTabs();
+ void relayout();
+
+ PageList pageList;
+ QVBoxLayout *layout;
+ Page *currentPage;
+};
+
+QToolBoxPrivate::Page *QToolBoxPrivate::page(QWidget *widget) const
+{
+ if (!widget)
+ return 0;
+
+ for (PageList::ConstIterator i = pageList.constBegin(); i != pageList.constEnd(); ++i)
+ if ((*i).widget == widget)
+ return (Page*) &(*i);
+ return 0;
+}
+
+QToolBoxPrivate::Page *QToolBoxPrivate::page(int index)
+{
+ if (index >= 0 && index < pageList.size())
+ return &pageList[index];
+ return 0;
+}
+
+const QToolBoxPrivate::Page *QToolBoxPrivate::page(int index) const
+{
+ if (index >= 0 && index < pageList.size())
+ return &pageList.at(index);
+ return 0;
+}
+
+void QToolBoxPrivate::updateTabs()
+{
+ QToolBoxButton *lastButton = currentPage ? currentPage->button : 0;
+ bool after = false;
+ int index = 0;
+ for (index = 0; index < pageList.count(); ++index) {
+ const Page &page = pageList.at(index);
+ QToolBoxButton *tB = page.button;
+ // update indexes, since the updates are delayed, the indexes will be correct
+ // when we actually paint.
+ tB->setIndex(index);
+ QWidget *tW = page.widget;
+ if (after) {
+ QPalette p = tB->palette();
+ p.setColor(tB->backgroundRole(), tW->palette().color(tW->backgroundRole()));
+ tB->setPalette(p);
+ tB->update();
+ } else if (tB->backgroundRole() != QPalette::Window) {
+ tB->setBackgroundRole(QPalette::Window);
+ tB->update();
+ }
+ after = tB == lastButton;
+ }
+}
+
+QSize QToolBoxButton::sizeHint() const
+{
+ QSize iconSize(8, 8);
+ if (!icon().isNull()) {
+ int icone = style()->pixelMetric(QStyle::PM_SmallIconSize, 0, parentWidget() /* QToolBox */);
+ iconSize += QSize(icone + 2, icone);
+ }
+ QSize textSize = fontMetrics().size(Qt::TextShowMnemonic, text()) + QSize(0, 8);
+
+ QSize total(iconSize.width() + textSize.width(), qMax(iconSize.height(), textSize.height()));
+ return total.expandedTo(QApplication::globalStrut());
+}
+
+QSize QToolBoxButton::minimumSizeHint() const
+{
+ if (icon().isNull())
+ return QSize();
+ int icone = style()->pixelMetric(QStyle::PM_SmallIconSize, 0, parentWidget() /* QToolBox */);
+ return QSize(icone + 8, icone + 8);
+}
+
+void QToolBoxButton::initStyleOption(QStyleOptionToolBox *option) const
+{
+ if (!option)
+ return;
+ option->initFrom(this);
+ if (selected)
+ option->state |= QStyle::State_Selected;
+ if (isDown())
+ option->state |= QStyle::State_Sunken;
+ option->text = text();
+ option->icon = icon();
+
+ if (QStyleOptionToolBoxV2 *optionV2 = qstyleoption_cast<QStyleOptionToolBoxV2 *>(option)) {
+ QToolBox *toolBox = static_cast<QToolBox *>(parentWidget()); // I know I'm in a tool box.
+ int widgetCount = toolBox->count();
+ int currIndex = toolBox->currentIndex();
+ if (widgetCount == 1) {
+ optionV2->position = QStyleOptionToolBoxV2::OnlyOneTab;
+ } else if (indexInPage == 0) {
+ optionV2->position = QStyleOptionToolBoxV2::Beginning;
+ } else if (indexInPage == widgetCount - 1) {
+ optionV2->position = QStyleOptionToolBoxV2::End;
+ } else {
+ optionV2->position = QStyleOptionToolBoxV2::Middle;
+ }
+ if (currIndex == indexInPage - 1) {
+ optionV2->selectedPosition = QStyleOptionToolBoxV2::PreviousIsSelected;
+ } else if (currIndex == indexInPage + 1) {
+ optionV2->selectedPosition = QStyleOptionToolBoxV2::NextIsSelected;
+ } else {
+ optionV2->selectedPosition = QStyleOptionToolBoxV2::NotAdjacent;
+ }
+ }
+}
+
+void QToolBoxButton::paintEvent(QPaintEvent *)
+{
+ QPainter paint(this);
+ QString text = QAbstractButton::text();
+ QPainter *p = &paint;
+ QStyleOptionToolBoxV2 opt;
+ initStyleOption(&opt);
+ style()->drawControl(QStyle::CE_ToolBoxTab, &opt, p, parentWidget());
+}
+
+/*!
+ \class QToolBox
+
+ \brief The QToolBox class provides a column of tabbed widget items.
+
+ \mainclass
+ \ingroup basicwidgets
+
+ A toolbox is a widget that displays a column of tabs one above the
+ other, with the current item displayed below the current tab.
+ Every tab has an index position within the column of tabs. A tab's
+ item is a QWidget.
+
+ Each item has an itemText(), an optional itemIcon(), an optional
+ itemToolTip(), and a widget(). The item's attributes can be
+ changed with setItemText(), setItemIcon(), and
+ setItemToolTip(). Each item can be enabled or disabled
+ individually with setItemEnabled().
+
+ Items are added using addItem(), or inserted at particular
+ positions using insertItem(). The total number of items is given
+ by count(). Items can be deleted with delete, or removed from the
+ toolbox with removeItem(). Combining removeItem() and insertItem()
+ allows you to move items to different positions.
+
+ The index of the current item widget is returned by currentIndex(),
+ and set with setCurrentIndex(). The index of a particular item can
+ be found using indexOf(), and the item at a given index is returned
+ by item().
+
+ The currentChanged() signal is emitted when the current item is
+ changed.
+
+ \sa QTabWidget
+*/
+
+/*!
+ \fn void QToolBox::currentChanged(int index)
+
+ This signal is emitted when the current item is changed. The new
+ current item's index is passed in \a index, or -1 if there is no
+ current item.
+*/
+
+#ifdef QT3_SUPPORT
+/*!
+ Constructs a toolbox called \a name with parent \a parent and flags \a f.
+*/
+QToolBox::QToolBox(QWidget *parent, const char *name, Qt::WindowFlags f)
+ : QFrame(*new QToolBoxPrivate, parent, f)
+{
+ Q_D(QToolBox);
+ setObjectName(QString::fromAscii(name));
+ d->layout = new QVBoxLayout(this);
+ d->layout->setMargin(0);
+ setBackgroundRole(QPalette::Button);
+}
+#endif
+
+/*!
+ Constructs a new toolbox with the given \a parent and the flags, \a f.
+*/
+QToolBox::QToolBox(QWidget *parent, Qt::WindowFlags f)
+ : QFrame(*new QToolBoxPrivate, parent, f)
+{
+ Q_D(QToolBox);
+ d->layout = new QVBoxLayout(this);
+ d->layout->setMargin(0);
+ setBackgroundRole(QPalette::Button);
+}
+
+/*!
+ Destroys the toolbox.
+*/
+
+QToolBox::~QToolBox()
+{
+}
+
+/*!
+ \fn int QToolBox::addItem(QWidget *w, const QString &text)
+ \overload
+
+ Adds the widget \a w in a new tab at bottom of the toolbox. The
+ new tab's text is set to \a text. Returns the new tab's index.
+*/
+
+/*!
+ \fn int QToolBox::addItem(QWidget *widget, const QIcon &iconSet,const QString &text)
+ Adds the \a widget in a new tab at bottom of the toolbox. The
+ new tab's text is set to \a text, and the \a iconSet is
+ displayed to the left of the \a text. Returns the new tab's index.
+*/
+
+/*!
+ \fn int QToolBox::insertItem(int index, QWidget *widget, const QString &text)
+ \overload
+
+ Inserts the \a widget at position \a index, or at the bottom
+ of the toolbox if \a index is out of range. The new item's text is
+ set to \a text. Returns the new item's index.
+*/
+
+/*!
+ Inserts the \a widget at position \a index, or at the bottom
+ of the toolbox if \a index is out of range. The new item's text
+ is set to \a text, and the \a icon is displayed to the left of
+ the \a text. Returns the new item's index.
+*/
+
+int QToolBox::insertItem(int index, QWidget *widget, const QIcon &icon, const QString &text)
+{
+ if (!widget)
+ return -1;
+
+ Q_D(QToolBox);
+ connect(widget, SIGNAL(destroyed(QObject*)), this, SLOT(_q_widgetDestroyed(QObject*)));
+
+ QToolBoxPrivate::Page c;
+ c.widget = widget;
+ c.button = new QToolBoxButton(this);
+ c.button->setObjectName(QLatin1String("qt_toolbox_toolboxbutton"));
+ connect(c.button, SIGNAL(clicked()), this, SLOT(_q_buttonClicked()));
+
+ c.sv = new QScrollArea(this);
+ c.sv->setWidget(widget);
+ c.sv->setWidgetResizable(true);
+ c.sv->hide();
+ c.sv->setFrameStyle(QFrame::NoFrame);
+
+ c.setText(text);
+ c.setIcon(icon);
+
+ if (index < 0 || index >= (int)d->pageList.count()) {
+ index = d->pageList.count();
+ d->pageList.append(c);
+ d->layout->addWidget(c.button);
+ d->layout->addWidget(c.sv);
+ if (index == 0)
+ setCurrentIndex(index);
+ } else {
+ d->pageList.insert(index, c);
+ d->relayout();
+ if (d->currentPage) {
+ QWidget *current = d->currentPage->widget;
+ int oldindex = indexOf(current);
+ if (index <= oldindex) {
+ d->currentPage = 0; // trigger change
+ setCurrentIndex(oldindex);
+ }
+ }
+ }
+
+ c.button->show();
+
+ d->updateTabs();
+ itemInserted(index);
+ return index;
+}
+
+void QToolBoxPrivate::_q_buttonClicked()
+{
+ Q_Q(QToolBox);
+ QToolBoxButton *tb = qobject_cast<QToolBoxButton*>(q->sender());
+ QWidget* item = 0;
+ for (QToolBoxPrivate::PageList::ConstIterator i = pageList.constBegin(); i != pageList.constEnd(); ++i)
+ if ((*i).button == tb) {
+ item = (*i).widget;
+ break;
+ }
+
+ q->setCurrentIndex(q->indexOf(item));
+}
+
+/*!
+ \property QToolBox::count
+ \brief The number of items contained in the toolbox.
+
+ By default, this property has a value of 0.
+*/
+
+int QToolBox::count() const
+{
+ Q_D(const QToolBox);
+ return d->pageList.count();
+}
+
+void QToolBox::setCurrentIndex(int index)
+{
+ Q_D(QToolBox);
+ QToolBoxPrivate::Page *c = d->page(index);
+ if (!c || d->currentPage == c)
+ return;
+
+ c->button->setSelected(true);
+ if (d->currentPage) {
+ d->currentPage->sv->hide();
+ d->currentPage->button->setSelected(false);
+ }
+ d->currentPage = c;
+ d->currentPage->sv->show();
+ d->updateTabs();
+ emit currentChanged(index);
+}
+
+void QToolBoxPrivate::relayout()
+{
+ Q_Q(QToolBox);
+ delete layout;
+ layout = new QVBoxLayout(q);
+ layout->setMargin(0);
+ for (QToolBoxPrivate::PageList::ConstIterator i = pageList.constBegin(); i != pageList.constEnd(); ++i) {
+ layout->addWidget((*i).button);
+ layout->addWidget((*i).sv);
+ }
+}
+
+void QToolBoxPrivate::_q_widgetDestroyed(QObject *object)
+{
+ Q_Q(QToolBox);
+ // no verification - vtbl corrupted already
+ QWidget *p = (QWidget*)object;
+
+ QToolBoxPrivate::Page *c = page(p);
+ if (!p || !c)
+ return;
+
+ layout->removeWidget(c->sv);
+ layout->removeWidget(c->button);
+ c->sv->deleteLater(); // page might still be a child of sv
+ delete c->button;
+
+ bool removeCurrent = c == currentPage;
+ pageList.removeAll(*c);
+
+ if (!pageList.count()) {
+ currentPage = 0;
+ emit q->currentChanged(-1);
+ } else if (removeCurrent) {
+ currentPage = 0;
+ q->setCurrentIndex(0);
+ }
+}
+
+/*!
+ Removes the item at position \a index from the toolbox. Note that
+ the widget is \e not deleted.
+*/
+
+void QToolBox::removeItem(int index)
+{
+ Q_D(QToolBox);
+ if (QWidget *w = widget(index)) {
+ disconnect(w, SIGNAL(destroyed(QObject*)), this, SLOT(_q_widgetDestroyed(QObject*)));
+ w->setParent(this);
+ // destroy internal data
+ d->_q_widgetDestroyed(w);
+ itemRemoved(index);
+ }
+}
+
+
+/*!
+ \property QToolBox::currentIndex
+ \brief the index of the current item
+
+ By default, for an empty toolbox, this property has a value of -1.
+
+ \sa indexOf(), widget()
+*/
+
+
+int QToolBox::currentIndex() const
+{
+ Q_D(const QToolBox);
+ return d->currentPage ? indexOf(d->currentPage->widget) : -1;
+}
+
+/*!
+ Returns a pointer to the current widget, or 0 if there is no such item.
+
+ \sa currentIndex(), setCurrentWidget()
+*/
+
+QWidget * QToolBox::currentWidget() const
+{
+ Q_D(const QToolBox);
+ return d->currentPage ? d->currentPage->widget : 0;
+}
+
+/*!
+ Makes\a widget the current widget. The \a widget must be an item in this tool box.
+
+ \sa addItem(), setCurrentIndex(), currentWidget()
+ */
+void QToolBox::setCurrentWidget(QWidget *widget)
+{
+ int i = indexOf(widget);
+ if (i >= 0)
+ setCurrentIndex(i);
+ else
+ qWarning("QToolBox::setCurrentWidget: widget not contained in tool box");
+}
+
+/*!
+ Returns the widget at position \a index, or 0 if there is no such
+ item.
+*/
+
+QWidget *QToolBox::widget(int index) const
+{
+ Q_D(const QToolBox);
+ if (index < 0 || index >= (int) d->pageList.size())
+ return 0;
+ return d->pageList.at(index).widget;
+}
+
+/*!
+ Returns the index of \a widget, or -1 if the item does not
+ exist.
+*/
+
+int QToolBox::indexOf(QWidget *widget) const
+{
+ Q_D(const QToolBox);
+ QToolBoxPrivate::Page *c = (widget ? d->page(widget) : 0);
+ return c ? d->pageList.indexOf(*c) : -1;
+}
+
+/*!
+ If \a enabled is true then the item at position \a index is enabled; otherwise
+ the item at position \a index is disabled.
+*/
+
+void QToolBox::setItemEnabled(int index, bool enabled)
+{
+ Q_D(QToolBox);
+ QToolBoxPrivate::Page *c = d->page(index);
+ if (!c)
+ return;
+
+ c->button->setEnabled(enabled);
+ if (!enabled && c == d->currentPage) {
+ int curIndexUp = index;
+ int curIndexDown = curIndexUp;
+ const int count = d->pageList.count();
+ while (curIndexUp > 0 || curIndexDown < count-1) {
+ if (curIndexDown < count-1) {
+ if (d->page(++curIndexDown)->button->isEnabled()) {
+ index = curIndexDown;
+ break;
+ }
+ }
+ if (curIndexUp > 0) {
+ if (d->page(--curIndexUp)->button->isEnabled()) {
+ index = curIndexUp;
+ break;
+ }
+ }
+ }
+ setCurrentIndex(index);
+ }
+}
+
+
+/*!
+ Sets the text of the item at position \a index to \a text.
+
+ If the provided text contains an ampersand character ('&'), a
+ mnemonic is automatically created for it. The character that
+ follows the '&' will be used as the shortcut key. Any previous
+ mnemonic will be overwritten, or cleared if no mnemonic is defined
+ by the text. See the \l {QShortcut#mnemonic}{QShortcut}
+ documentation for details (to display an actual ampersand, use
+ '&&').
+*/
+
+void QToolBox::setItemText(int index, const QString &text)
+{
+ Q_D(QToolBox);
+ QToolBoxPrivate::Page *c = d->page(index);
+ if (c)
+ c->setText(text);
+}
+
+/*!
+ Sets the icon of the item at position \a index to \a icon.
+*/
+
+void QToolBox::setItemIcon(int index, const QIcon &icon)
+{
+ Q_D(QToolBox);
+ QToolBoxPrivate::Page *c = d->page(index);
+ if (c)
+ c->setIcon(icon);
+}
+
+#ifndef QT_NO_TOOLTIP
+/*!
+ Sets the tooltip of the item at position \a index to \a toolTip.
+*/
+
+void QToolBox::setItemToolTip(int index, const QString &toolTip)
+{
+ Q_D(QToolBox);
+ QToolBoxPrivate::Page *c = d->page(index);
+ if (c)
+ c->setToolTip(toolTip);
+}
+#endif // QT_NO_TOOLTIP
+
+/*!
+ Returns true if the item at position \a index is enabled; otherwise returns false.
+*/
+
+bool QToolBox::isItemEnabled(int index) const
+{
+ Q_D(const QToolBox);
+ const QToolBoxPrivate::Page *c = d->page(index);
+ return c && c->button->isEnabled();
+}
+
+/*!
+ Returns the text of the item at position \a index, or an empty string if
+ \a index is out of range.
+*/
+
+QString QToolBox::itemText(int index) const
+{
+ Q_D(const QToolBox);
+ const QToolBoxPrivate::Page *c = d->page(index);
+ return (c ? c->text() : QString());
+}
+
+/*!
+ Returns the icon of the item at position \a index, or a null
+ icon if \a index is out of range.
+*/
+
+QIcon QToolBox::itemIcon(int index) const
+{
+ Q_D(const QToolBox);
+ const QToolBoxPrivate::Page *c = d->page(index);
+ return (c ? c->icon() : QIcon());
+}
+
+#ifndef QT_NO_TOOLTIP
+/*!
+ Returns the tooltip of the item at position \a index, or an
+ empty string if \a index is out of range.
+*/
+
+QString QToolBox::itemToolTip(int index) const
+{
+ Q_D(const QToolBox);
+ const QToolBoxPrivate::Page *c = d->page(index);
+ return (c ? c->toolTip() : QString());
+}
+#endif // QT_NO_TOOLTIP
+
+/*! \reimp */
+void QToolBox::showEvent(QShowEvent *e)
+{
+ QWidget::showEvent(e);
+}
+
+/*! \reimp */
+void QToolBox::changeEvent(QEvent *ev)
+{
+ Q_D(QToolBox);
+ if(ev->type() == QEvent::StyleChange)
+ d->updateTabs();
+ QFrame::changeEvent(ev);
+}
+
+/*!
+ This virtual handler is called after a new item was added or
+ inserted at position \a index.
+
+ \sa itemRemoved()
+ */
+void QToolBox::itemInserted(int index)
+{
+ Q_UNUSED(index)
+}
+
+/*!
+ This virtual handler is called after an item was removed from
+ position \a index.
+
+ \sa itemInserted()
+ */
+void QToolBox::itemRemoved(int index)
+{
+ Q_UNUSED(index)
+}
+
+/*!
+ \fn void QToolBox::setItemLabel(int index, const QString &text)
+
+ Use setItemText() instead.
+*/
+
+/*!
+ \fn QString QToolBox::itemLabel(int index) const
+
+ Use itemText() instead.
+*/
+
+/*!
+ \fn QWidget *QToolBox::currentItem() const
+
+ Use widget(currentIndex()) instead.
+*/
+
+/*!
+ \fn void QToolBox::setCurrentItem(QWidget *widget)
+
+ Use setCurrentIndex(indexOf(widget)) instead.
+*/
+
+/*!
+ \fn void QToolBox::setItemIconSet(int index, const QIcon &icon)
+
+ Use setItemIcon() instead.
+*/
+
+/*!
+ \fn QIcon QToolBox::itemIconSet(int index) const
+
+ Use itemIcon() instead.
+*/
+
+/*!
+ \fn int QToolBox::removeItem(QWidget *widget)
+
+ Use toolbox->removeItem(toolbox->indexOf(widget)) instead.
+*/
+
+/*!
+ \fn QWidget *QToolBox::item(int index) const
+
+ Use widget() instead.
+*/
+
+/*!
+ \fn void QToolBox::setMargin(int margin)
+ Sets the width of the margin around the contents of the widget to \a margin.
+
+ Use QWidget::setContentsMargins() instead.
+ \sa margin(), QWidget::setContentsMargins()
+*/
+
+/*!
+ \fn int QToolBox::margin() const
+ Returns the with of the the margin around the contents of the widget.
+
+ Use QWidget::getContentsMargins() instead.
+ \sa setMargin(), QWidget::getContentsMargins()
+*/
+
+/*! \reimp */
+bool QToolBox::event(QEvent *e)
+{
+ return QFrame::event(e);
+}
+
+QT_END_NAMESPACE
+
+#include "moc_qtoolbox.cpp"
+#include "qtoolbox.moc"
+
+#endif //QT_NO_TOOLBOX
diff --git a/src/gui/widgets/qtoolbox.h b/src/gui/widgets/qtoolbox.h
new file mode 100644
index 0000000000..435fab9638
--- /dev/null
+++ b/src/gui/widgets/qtoolbox.h
@@ -0,0 +1,148 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QTOOLBOX_H
+#define QTOOLBOX_H
+
+#include <QtGui/qframe.h>
+#include <QtGui/qicon.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+#ifndef QT_NO_TOOLBOX
+
+class QToolBoxPrivate;
+
+class Q_GUI_EXPORT QToolBox : public QFrame
+{
+ Q_OBJECT
+ Q_PROPERTY(int currentIndex READ currentIndex WRITE setCurrentIndex NOTIFY currentChanged)
+ Q_PROPERTY(int count READ count)
+
+public:
+ explicit QToolBox(QWidget *parent = 0, Qt::WindowFlags f = 0);
+ ~QToolBox();
+
+ int addItem(QWidget *widget, const QString &text);
+ int addItem(QWidget *widget, const QIcon &icon, const QString &text);
+ int insertItem(int index, QWidget *widget, const QString &text);
+ int insertItem(int index, QWidget *widget, const QIcon &icon, const QString &text);
+
+ void removeItem(int index);
+
+ void setItemEnabled(int index, bool enabled);
+ bool isItemEnabled(int index) const;
+
+ void setItemText(int index, const QString &text);
+ QString itemText(int index) const;
+
+ void setItemIcon(int index, const QIcon &icon);
+ QIcon itemIcon(int index) const;
+
+#ifndef QT_NO_TOOLTIP
+ void setItemToolTip(int index, const QString &toolTip);
+ QString itemToolTip(int index) const;
+#endif
+
+ int currentIndex() const;
+ QWidget *currentWidget() const;
+ QWidget *widget(int index) const;
+ int indexOf(QWidget *widget) const;
+ int count() const;
+
+public Q_SLOTS:
+ void setCurrentIndex(int index);
+ void setCurrentWidget(QWidget *widget);
+
+Q_SIGNALS:
+ void currentChanged(int index);
+
+protected:
+ bool event(QEvent *e);
+ virtual void itemInserted(int index);
+ virtual void itemRemoved(int index);
+ void showEvent(QShowEvent *e);
+ void changeEvent(QEvent *);
+
+#ifdef QT3_SUPPORT
+public:
+ QT3_SUPPORT_CONSTRUCTOR QToolBox(QWidget *parent, const char *name, Qt::WindowFlags f = 0);
+ inline QT3_SUPPORT void setItemLabel(int index, const QString &text) { setItemText(index, text); }
+ inline QT3_SUPPORT QString itemLabel(int index) const { return itemText(index); }
+ inline QT3_SUPPORT QWidget *currentItem() const { return widget(currentIndex()); }
+ inline QT3_SUPPORT void setCurrentItem(QWidget *item) { setCurrentIndex(indexOf(item)); }
+ inline QT3_SUPPORT void setItemIconSet(int index, const QIcon &icon) { setItemIcon(index, icon); }
+ inline QT3_SUPPORT QIcon itemIconSet(int index) const { return itemIcon(index); }
+ inline QT3_SUPPORT int removeItem(QWidget *item)
+ { int i = indexOf(item); removeItem(i); return i; }
+ inline QT3_SUPPORT QWidget *item(int index) const { return widget(index); }
+ QT3_SUPPORT void setMargin(int margin) { setContentsMargins(margin, margin, margin, margin); }
+ QT3_SUPPORT int margin() const
+ { int margin; int dummy; getContentsMargins(&margin, &dummy, &dummy, &dummy); return margin; }
+#endif
+
+private:
+ Q_DECLARE_PRIVATE(QToolBox)
+ Q_DISABLE_COPY(QToolBox)
+ Q_PRIVATE_SLOT(d_func(), void _q_buttonClicked())
+ Q_PRIVATE_SLOT(d_func(), void _q_widgetDestroyed(QObject*))
+};
+
+
+inline int QToolBox::addItem(QWidget *item, const QString &text)
+{ return insertItem(-1, item, QIcon(), text); }
+inline int QToolBox::addItem(QWidget *item, const QIcon &iconSet,
+ const QString &text)
+{ return insertItem(-1, item, iconSet, text); }
+inline int QToolBox::insertItem(int index, QWidget *item, const QString &text)
+{ return insertItem(index, item, QIcon(), text); }
+
+#endif // QT_NO_TOOLBOX
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QTOOLBOX_H
diff --git a/src/gui/widgets/qtoolbutton.cpp b/src/gui/widgets/qtoolbutton.cpp
new file mode 100644
index 0000000000..7390d04c56
--- /dev/null
+++ b/src/gui/widgets/qtoolbutton.cpp
@@ -0,0 +1,1251 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qtoolbutton.h"
+#ifndef QT_NO_TOOLBUTTON
+
+#include <qapplication.h>
+#include <qdesktopwidget.h>
+#include <qdrawutil.h>
+#include <qevent.h>
+#include <qicon.h>
+#include <qmenu.h>
+#include <qpainter.h>
+#include <qpointer.h>
+#include <qstyle.h>
+#include <qstyleoption.h>
+#include <qtooltip.h>
+#include <qmainwindow.h>
+#include <qtoolbar.h>
+#include <qvariant.h>
+#include <qstylepainter.h>
+#include <private/qabstractbutton_p.h>
+#include <private/qaction_p.h>
+#include <private/qmenu_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QToolButtonPrivate : public QAbstractButtonPrivate
+{
+ Q_DECLARE_PUBLIC(QToolButton)
+public:
+ void init();
+#ifndef QT_NO_MENU
+ void _q_buttonPressed();
+ void popupTimerDone();
+ void _q_updateButtonDown();
+ void _q_menuTriggered(QAction *);
+#endif
+ bool updateHoverControl(const QPoint &pos);
+ void _q_actionTriggered();
+ QStyle::SubControl newHoverControl(const QPoint &pos);
+ QStyle::SubControl hoverControl;
+ QRect hoverRect;
+ QPointer<QAction> menuAction; //the menu set by the user (setMenu)
+ QBasicTimer popupTimer;
+ int delay;
+ Qt::ArrowType arrowType;
+ Qt::ToolButtonStyle toolButtonStyle;
+ QToolButton::ToolButtonPopupMode popupMode;
+ enum { NoButtonPressed=0, MenuButtonPressed=1, ToolButtonPressed=2 };
+ uint buttonPressed : 2;
+ uint menuButtonDown : 1;
+ uint autoRaise : 1;
+ uint repeat : 1;
+ QAction *defaultAction;
+#ifndef QT_NO_MENU
+ bool hasMenu() const;
+ //workaround for task 177850
+ QList<QAction *> actionsCopy;
+#endif
+#ifdef QT3_SUPPORT
+ bool userDefinedPopupDelay;
+#endif
+};
+
+#ifndef QT_NO_MENU
+bool QToolButtonPrivate::hasMenu() const
+{
+ Q_Q(const QToolButton);
+ return ((defaultAction && defaultAction->menu())
+ || (menuAction && menuAction->menu())
+ || q->actions().size() > (defaultAction ? 1 : 0));
+}
+#endif
+
+/*!
+ \class QToolButton
+ \brief The QToolButton class provides a quick-access button to
+ commands or options, usually used inside a QToolBar.
+
+ \ingroup basicwidgets
+ \mainclass
+
+ A tool button is a special button that provides quick-access to
+ specific commands or options. As opposed to a normal command
+ button, a tool button usually doesn't show a text label, but shows
+ an icon instead.
+
+ Tool buttons are normally created when new QAction instances are
+ created with QToolBar::addAction() or existing actions are added
+ to a toolbar with QToolBar::addAction(). It is also possible to
+ construct tool buttons in the same way as any other widget, and
+ arrange them alongside other widgets in layouts.
+
+ One classic use of a tool button is to select tools; for example,
+ the "pen" tool in a drawing program. This would be implemented
+ by using a QToolButton as a toggle button (see setToggleButton()).
+
+ QToolButton supports auto-raising. In auto-raise mode, the button
+ draws a 3D frame only when the mouse points at it. The feature is
+ automatically turned on when a button is used inside a QToolBar.
+ Change it with setAutoRaise().
+
+ A tool button's icon is set as QIcon. This makes it possible to
+ specify different pixmaps for the disabled and active state. The
+ disabled pixmap is used when the button's functionality is not
+ available. The active pixmap is displayed when the button is
+ auto-raised because the mouse pointer is hovering over it.
+
+ The button's look and dimension is adjustable with
+ setToolButtonStyle() and setIconSize(). When used inside a
+ QToolBar in a QMainWindow, the button automatically adjusts to
+ QMainWindow's settings (see QMainWindow::setToolButtonStyle() and
+ QMainWindow::setIconSize()). Instead of an icon, a tool button can
+ also display an arrow symbol, specified with
+ \l{QToolButton::arrowType} {arrowType}.
+
+ A tool button can offer additional choices in a popup menu. The
+ popup menu can be set using setMenu(). Use setPopupMode() to
+ configure the different modes available for tool buttons with a
+ menu set. The default mode is DelayedPopupMode which is sometimes
+ used with the "Back" button in a web browser. After pressing and
+ holding the button down for a while, a menu pops up showing a list
+ of possible pages to jump to. The default delay is 600 ms; you can
+ adjust it with setPopupDelay().
+
+ \table 100%
+ \row \o \inlineimage assistant-toolbar.png Qt Assistant's toolbar with tool buttons
+ \row \o Qt Assistant's toolbar contains tool buttons that are associated
+ with actions used in other parts of the main window.
+ \endtable
+
+ \sa QPushButton, QToolBar, QMainWindow, QAction,
+ {fowler}{GUI Design Handbook: Push Button}
+*/
+
+/*!
+ \fn void QToolButton::triggered(QAction *action)
+
+ This signal is emitted when the given \a action is triggered.
+
+ The action may also be associated with other parts of the user interface,
+ such as menu items and keyboard shortcuts. Sharing actions in this
+ way helps make the user interface more consistent and is often less work
+ to implement.
+*/
+
+/*!
+ Constructs an empty tool button with parent \a
+ parent.
+*/
+QToolButton::QToolButton(QWidget * parent)
+ : QAbstractButton(*new QToolButtonPrivate, parent)
+{
+ Q_D(QToolButton);
+ d->init();
+}
+
+#ifdef QT3_SUPPORT
+/*!
+ Constructs an empty tool button called \a name, with parent \a
+ parent.
+*/
+
+QToolButton::QToolButton(QWidget * parent, const char *name)
+ : QAbstractButton(*new QToolButtonPrivate, parent)
+{
+ Q_D(QToolButton);
+ setObjectName(QString::fromAscii(name));
+ d->init();
+}
+
+/*!
+ Constructs a tool button called \a name, that is a child of \a
+ parent.
+
+ The tool button will display the given \a icon, with its text
+ label and tool tip set to \a textLabel and its status bar message
+ set to \a statusTip. It will be connected to the \a slot in
+ object \a receiver.
+*/
+
+QToolButton::QToolButton(const QIcon& icon, const QString &textLabel,
+ const QString& statusTip,
+ QObject * receiver, const char *slot,
+ QWidget * parent, const char *name)
+ : QAbstractButton(*new QToolButtonPrivate, parent)
+{
+ Q_D(QToolButton);
+ setObjectName(QString::fromAscii(name));
+ d->init();
+ setIcon(icon);
+ setText(textLabel);
+ if (receiver && slot)
+ connect(this, SIGNAL(clicked()), receiver, slot);
+#ifndef QT_NO_TOOLTIP
+ if (!textLabel.isEmpty())
+ setToolTip(textLabel);
+#endif
+#ifndef QT_NO_STATUSTIP
+ if (!statusTip.isEmpty())
+ setStatusTip(statusTip);
+#else
+ Q_UNUSED(statusTip);
+#endif
+}
+
+
+/*!
+ Constructs a tool button as an arrow button. The Qt::ArrowType \a
+ type defines the arrow direction. Possible values are
+ Qt::LeftArrow, Qt::RightArrow, Qt::UpArrow, and Qt::DownArrow.
+
+ An arrow button has auto-repeat turned on by default.
+
+ The \a parent and \a name arguments are sent to the QWidget
+ constructor.
+*/
+QToolButton::QToolButton(Qt::ArrowType type, QWidget *parent, const char *name)
+ : QAbstractButton(*new QToolButtonPrivate, parent)
+{
+ Q_D(QToolButton);
+ setObjectName(QString::fromAscii(name));
+ d->init();
+ setAutoRepeat(true);
+ d->arrowType = type;
+}
+
+#endif
+
+
+/* Set-up code common to all the constructors */
+
+void QToolButtonPrivate::init()
+{
+ Q_Q(QToolButton);
+ delay = q->style()->styleHint(QStyle::SH_ToolButton_PopupDelay, 0, q);
+#ifdef QT3_SUPPORT
+ userDefinedPopupDelay = false;
+#endif
+ defaultAction = 0;
+#ifndef QT_NO_TOOLBAR
+ if (qobject_cast<QToolBar*>(q->parentWidget()))
+ autoRaise = true;
+ else
+#endif
+ autoRaise = false;
+ arrowType = Qt::NoArrow;
+ menuButtonDown = false;
+ popupMode = QToolButton::DelayedPopup;
+ buttonPressed = QToolButtonPrivate::NoButtonPressed;
+
+ toolButtonStyle = Qt::ToolButtonIconOnly;
+ hoverControl = QStyle::SC_None;
+
+ q->setFocusPolicy(Qt::TabFocus);
+ q->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed,
+ QSizePolicy::ToolButton));
+
+#ifndef QT_NO_MENU
+ QObject::connect(q, SIGNAL(pressed()), q, SLOT(_q_buttonPressed()));
+#endif
+
+ setLayoutItemMargins(QStyle::SE_ToolButtonLayoutItem);
+
+}
+
+/*!
+ Initialize \a option with the values from this QToolButton. This method
+ is useful for subclasses when they need a QStyleOptionToolButton, but don't want
+ to fill in all the information themselves.
+
+ \sa QStyleOption::initFrom()
+*/
+void QToolButton::initStyleOption(QStyleOptionToolButton *option) const
+{
+ if (!option)
+ return;
+
+ Q_D(const QToolButton);
+ option->initFrom(this);
+ bool forceNoText = false;
+ option->iconSize = iconSize(); //default value
+
+#ifndef QT_NO_TOOLBAR
+ if (parentWidget()) {
+ if (QToolBar *toolBar = qobject_cast<QToolBar *>(parentWidget())) {
+ option->iconSize = toolBar->iconSize();
+ }
+#ifdef QT3_SUPPORT
+ else if (parentWidget()->inherits("Q3ToolBar")) {
+ if (!option->iconSize.isValid()) {
+ int iconSize = style()->pixelMetric(QStyle::PM_ToolBarIconSize, option, this);
+ option->iconSize = d->icon.actualSize(QSize(iconSize, iconSize));
+ }
+ forceNoText = d->toolButtonStyle == Qt::ToolButtonIconOnly;
+ }
+#endif
+ }
+#endif // QT_NO_TOOLBAR
+
+ if (!forceNoText)
+ option->text = d->text;
+ option->icon = d->icon;
+ option->arrowType = d->arrowType;
+ if (d->down)
+ option->state |= QStyle::State_Sunken;
+ if (d->checked)
+ option->state |= QStyle::State_On;
+ if (d->autoRaise)
+ option->state |= QStyle::State_AutoRaise;
+ if (!d->checked && !d->down)
+ option->state |= QStyle::State_Raised;
+
+ option->subControls = QStyle::SC_ToolButton;
+ option->activeSubControls = QStyle::SC_None;
+
+ option->features = QStyleOptionToolButton::None;
+ if (d->popupMode == QToolButton::MenuButtonPopup) {
+ option->subControls |= QStyle::SC_ToolButtonMenu;
+ option->features |= QStyleOptionToolButton::MenuButtonPopup;
+ }
+ if (option->state & QStyle::State_MouseOver) {
+ option->activeSubControls = d->hoverControl;
+ }
+ if (d->menuButtonDown) {
+ option->state |= QStyle::State_Sunken;
+ option->activeSubControls |= QStyle::SC_ToolButtonMenu;
+ }
+ if (d->down) {
+ option->state |= QStyle::State_Sunken;
+ option->activeSubControls |= QStyle::SC_ToolButton;
+ }
+
+
+ if (d->arrowType != Qt::NoArrow)
+ option->features |= QStyleOptionToolButton::Arrow;
+ if (d->popupMode == QToolButton::DelayedPopup)
+ option->features |= QStyleOptionToolButton::PopupDelay;
+#ifndef QT_NO_MENU
+ if (d->hasMenu())
+ option->features |= QStyleOptionToolButton::HasMenu;
+#endif
+ option->toolButtonStyle = d->toolButtonStyle;
+ if (d->icon.isNull() && d->arrowType == Qt::NoArrow && !forceNoText) {
+ if (!d->text.isEmpty())
+ option->toolButtonStyle = Qt::ToolButtonTextOnly;
+ else if (option->toolButtonStyle != Qt::ToolButtonTextOnly)
+ option->toolButtonStyle = Qt::ToolButtonIconOnly;
+ } else {
+ if (d->text.isEmpty() && option->toolButtonStyle != Qt::ToolButtonIconOnly)
+ option->toolButtonStyle = Qt::ToolButtonIconOnly;
+ }
+
+ option->pos = pos();
+ option->font = font();
+}
+
+/*!
+ Destroys the object and frees any allocated resources.
+*/
+
+QToolButton::~QToolButton()
+{
+}
+
+/*!
+ \reimp
+*/
+QSize QToolButton::sizeHint() const
+{
+ Q_D(const QToolButton);
+ if (d->sizeHint.isValid())
+ return d->sizeHint;
+ ensurePolished();
+
+ int w = 0, h = 0;
+ QStyleOptionToolButton opt;
+ initStyleOption(&opt);
+
+ QFontMetrics fm = fontMetrics();
+ if (opt.toolButtonStyle != Qt::ToolButtonTextOnly) {
+ QSize icon = opt.iconSize;
+ w = icon.width();
+ h = icon.height();
+ }
+
+ if (opt.toolButtonStyle != Qt::ToolButtonIconOnly) {
+ QSize textSize = fm.size(Qt::TextShowMnemonic, text());
+ textSize.setWidth(textSize.width() + fm.width(QLatin1Char(' '))*2);
+ if (opt.toolButtonStyle == Qt::ToolButtonTextUnderIcon) {
+ h += 4 + textSize.height();
+ if (textSize.width() > w)
+ w = textSize.width();
+ } else if (opt.toolButtonStyle == Qt::ToolButtonTextBesideIcon) {
+ w += 4 + textSize.width();
+ if (textSize.height() > h)
+ h = textSize.height();
+ } else { // TextOnly
+ w = textSize.width();
+ h = textSize.height();
+ }
+ }
+
+ opt.rect.setSize(QSize(w, h)); // PM_MenuButtonIndicator depends on the height
+ if (d->popupMode == MenuButtonPopup)
+ w += style()->pixelMetric(QStyle::PM_MenuButtonIndicator, &opt, this);
+
+ d->sizeHint = style()->sizeFromContents(QStyle::CT_ToolButton, &opt, QSize(w, h), this).
+ expandedTo(QApplication::globalStrut());
+ return d->sizeHint;
+}
+
+/*!
+ \reimp
+ */
+QSize QToolButton::minimumSizeHint() const
+{
+ return sizeHint();
+}
+
+/*!
+ \enum QToolButton::TextPosition
+ \compat
+
+ This enum describes the position of the tool button's text label in
+ relation to the tool button's icon.
+
+ \value BesideIcon The text appears beside the icon.
+ \value BelowIcon The text appears below the icon.
+ \omitvalue Right
+ \omitvalue Under
+*/
+
+/*!
+ \property QToolButton::toolButtonStyle
+ \brief whether the tool button displays an icon only, text only,
+ or text beside/below the icon.
+
+ The default is Qt::ToolButtonIconOnly.
+
+ QToolButton automatically connects this slot to the relevant
+ signal in the QMainWindow in which is resides.
+*/
+
+/*!
+ \property QToolButton::arrowType
+ \brief whether the button displays an arrow instead of a normal icon
+
+ This displays an arrow as the icon for the QToolButton.
+
+ By default, this property is set to Qt::NoArrow.
+*/
+
+Qt::ToolButtonStyle QToolButton::toolButtonStyle() const
+{
+ Q_D(const QToolButton);
+ return d->toolButtonStyle;
+}
+
+Qt::ArrowType QToolButton::arrowType() const
+{
+ Q_D(const QToolButton);
+ return d->arrowType;
+}
+
+
+void QToolButton::setToolButtonStyle(Qt::ToolButtonStyle style)
+{
+ Q_D(QToolButton);
+ if (d->toolButtonStyle == style)
+ return;
+
+ d->toolButtonStyle = style;
+ d->sizeHint = QSize();
+ updateGeometry();
+ if (isVisible()) {
+ update();
+ }
+}
+
+void QToolButton::setArrowType(Qt::ArrowType type)
+{
+ Q_D(QToolButton);
+ if (d->arrowType == type)
+ return;
+
+ d->arrowType = type;
+ d->sizeHint = QSize();
+ updateGeometry();
+ if (isVisible()) {
+ update();
+ }
+}
+
+/*!
+ \fn void QToolButton::paintEvent(QPaintEvent *event)
+
+ Paints the button in response to the paint \a event.
+*/
+void QToolButton::paintEvent(QPaintEvent *)
+{
+ QStylePainter p(this);
+ QStyleOptionToolButton opt;
+ initStyleOption(&opt);
+ p.drawComplexControl(QStyle::CC_ToolButton, opt);
+}
+
+/*!
+ \reimp
+ */
+void QToolButton::actionEvent(QActionEvent *event)
+{
+ Q_D(QToolButton);
+ QAction *action = event->action();
+ switch (event->type()) {
+ case QEvent::ActionChanged:
+ if (action == d->defaultAction)
+ setDefaultAction(action); // update button state
+ break;
+ case QEvent::ActionAdded:
+ connect(action, SIGNAL(triggered()), this, SLOT(_q_actionTriggered()));
+ break;
+ case QEvent::ActionRemoved:
+ if (d->defaultAction == action)
+ d->defaultAction = 0;
+#ifndef QT_NO_MENU
+ if (action == d->menuAction)
+ d->menuAction = 0;
+#endif
+ action->disconnect(this);
+ break;
+ default:
+ ;
+ }
+ QAbstractButton::actionEvent(event);
+}
+
+QStyle::SubControl QToolButtonPrivate::newHoverControl(const QPoint &pos)
+{
+ Q_Q(QToolButton);
+ QStyleOptionToolButton opt;
+ q->initStyleOption(&opt);
+ opt.subControls = QStyle::SC_All;
+ hoverControl = q->style()->hitTestComplexControl(QStyle::CC_ToolButton, &opt, pos, q);
+ if (hoverControl == QStyle::SC_None)
+ hoverRect = QRect();
+ else
+ hoverRect = q->style()->subControlRect(QStyle::CC_ToolButton, &opt, hoverControl, q);
+ return hoverControl;
+}
+
+bool QToolButtonPrivate::updateHoverControl(const QPoint &pos)
+{
+ Q_Q(QToolButton);
+ QRect lastHoverRect = hoverRect;
+ QStyle::SubControl lastHoverControl = hoverControl;
+ bool doesHover = q->testAttribute(Qt::WA_Hover);
+ if (lastHoverControl != newHoverControl(pos) && doesHover) {
+ q->update(lastHoverRect);
+ q->update(hoverRect);
+ return true;
+ }
+ return !doesHover;
+}
+
+void QToolButtonPrivate::_q_actionTriggered()
+{
+ Q_Q(QToolButton);
+ if (QAction *action = qobject_cast<QAction *>(q->sender()))
+ emit q->triggered(action);
+}
+
+/*!
+ \reimp
+ */
+void QToolButton::enterEvent(QEvent * e)
+{
+ Q_D(QToolButton);
+ if (d->autoRaise)
+ update();
+ if (d->defaultAction)
+ d->defaultAction->hover();
+ QAbstractButton::enterEvent(e);
+}
+
+
+/*!
+ \reimp
+ */
+void QToolButton::leaveEvent(QEvent * e)
+{
+ Q_D(QToolButton);
+ if (d->autoRaise)
+ update();
+
+ QAbstractButton::leaveEvent(e);
+}
+
+
+/*!
+ \reimp
+ */
+void QToolButton::timerEvent(QTimerEvent *e)
+{
+#ifndef QT_NO_MENU
+ Q_D(QToolButton);
+ if (e->timerId() == d->popupTimer.timerId()) {
+ d->popupTimerDone();
+ return;
+ }
+#endif
+ QAbstractButton::timerEvent(e);
+}
+
+
+/*!
+ \reimp
+*/
+void QToolButton::changeEvent(QEvent *e)
+{
+#ifndef QT_NO_TOOLBAR
+ Q_D(QToolButton);
+ if (e->type() == QEvent::ParentChange) {
+ if (qobject_cast<QToolBar*>(parentWidget()))
+ d->autoRaise = true;
+ } else if (e->type() == QEvent::StyleChange
+#ifdef Q_WS_MAC
+ || e->type() == QEvent::MacSizeChange
+#endif
+ ) {
+#ifdef QT3_SUPPORT
+ if (!d->userDefinedPopupDelay)
+#endif
+ d->delay = style()->styleHint(QStyle::SH_ToolButton_PopupDelay, 0, this);
+ d->setLayoutItemMargins(QStyle::SE_ToolButtonLayoutItem);
+ }
+#endif
+ QAbstractButton::changeEvent(e);
+}
+
+/*!
+ \reimp
+*/
+void QToolButton::mousePressEvent(QMouseEvent *e)
+{
+ Q_D(QToolButton);
+#ifndef QT_NO_MENU
+ QStyleOptionToolButton opt;
+ initStyleOption(&opt);
+ if (e->button() == Qt::LeftButton && (d->popupMode == MenuButtonPopup)) {
+ QRect popupr = style()->subControlRect(QStyle::CC_ToolButton, &opt,
+ QStyle::SC_ToolButtonMenu, this);
+ if (popupr.isValid() && popupr.contains(e->pos())) {
+ d->buttonPressed = QToolButtonPrivate::MenuButtonPressed;
+ showMenu();
+ return;
+ }
+ }
+#endif
+ d->buttonPressed = QToolButtonPrivate::ToolButtonPressed;
+ QAbstractButton::mousePressEvent(e);
+}
+
+/*!
+ \reimp
+*/
+void QToolButton::mouseReleaseEvent(QMouseEvent *e)
+{
+ Q_D(QToolButton);
+ QAbstractButton::mouseReleaseEvent(e);
+ d->buttonPressed = QToolButtonPrivate::NoButtonPressed;
+}
+
+/*!
+ \reimp
+*/
+bool QToolButton::hitButton(const QPoint &pos) const
+{
+ Q_D(const QToolButton);
+ if(QAbstractButton::hitButton(pos))
+ return (d->buttonPressed != QToolButtonPrivate::MenuButtonPressed);
+ return false;
+}
+
+#ifdef QT3_SUPPORT
+
+/*!
+ Use icon() instead.
+*/
+QIcon QToolButton::onIconSet() const
+{
+ return icon();
+}
+
+/*!
+ Use icon() instead.
+*/
+QIcon QToolButton::offIconSet() const
+{
+ return icon();
+}
+
+
+/*!
+ \obsolete
+
+ Use setIcon() instead.
+*/
+void QToolButton::setOnIconSet(const QIcon& set)
+{
+ setIcon(set);
+}
+
+/*!
+ \obsolete
+
+ Use setIcon() instead.
+*/
+void QToolButton::setOffIconSet(const QIcon& set)
+{
+ setIcon(set);
+}
+
+
+/*! \overload
+ \obsolete
+
+ Since Qt 3.0, QIcon contains both the On and Off icons.
+
+ For ease of porting, this function ignores the \a on parameter and
+ sets the \l{QAbstractButton::icon} {icon} property. If you relied on
+ the \a on parameter, you probably want to update your code to use
+ the QIcon On/Off mechanism.
+
+ \sa icon QIcon::State
+*/
+
+void QToolButton::setIconSet(const QIcon & set, bool /* on */)
+{
+ QAbstractButton::setIcon(set);
+}
+
+/*! \overload
+ \obsolete
+
+ Since Qt 3.0, QIcon contains both the On and Off icons.
+
+ For ease of porting, this function ignores the \a on parameter and
+ returns the \l{QAbstractButton::icon} {icon} property. If you relied
+ on the \a on parameter, you probably want to update your code to use
+ the QIcon On/Off mechanism.
+*/
+QIcon QToolButton::iconSet(bool /* on */) const
+{
+ return QAbstractButton::icon();
+}
+
+#endif
+
+#ifndef QT_NO_MENU
+/*!
+ Associates the given \a menu with this tool button.
+
+ The menu will be shown according to the button's \l popupMode.
+
+ Ownership of the menu is not transferred to the tool button.
+
+ \sa menu()
+*/
+void QToolButton::setMenu(QMenu* menu)
+{
+ Q_D(QToolButton);
+
+ if (d->menuAction)
+ removeAction(d->menuAction);
+
+ if (menu) {
+ d->menuAction = menu->menuAction();
+ addAction(d->menuAction);
+ } else {
+ d->menuAction = 0;
+ }
+ update();
+}
+
+/*!
+ Returns the associated menu, or 0 if no menu has been defined.
+
+ \sa setMenu()
+*/
+QMenu* QToolButton::menu() const
+{
+ Q_D(const QToolButton);
+ if (d->menuAction)
+ return d->menuAction->menu();
+ return 0;
+}
+
+/*!
+ Shows (pops up) the associated popup menu. If there is no such
+ menu, this function does nothing. This function does not return
+ until the popup menu has been closed by the user.
+*/
+void QToolButton::showMenu()
+{
+ Q_D(QToolButton);
+ if (!d->hasMenu()) {
+ d->menuButtonDown = false;
+ return; // no menu to show
+ }
+
+ d->menuButtonDown = true;
+ repaint();
+ d->popupTimer.stop();
+ d->popupTimerDone();
+}
+
+void QToolButtonPrivate::_q_buttonPressed()
+{
+ Q_Q(QToolButton);
+ if (!hasMenu())
+ return; // no menu to show
+
+ if (delay > 0 && popupMode == QToolButton::DelayedPopup)
+ popupTimer.start(delay, q);
+ else if (delay == 0 || popupMode == QToolButton::InstantPopup)
+ q->showMenu();
+}
+
+void QToolButtonPrivate::popupTimerDone()
+{
+ Q_Q(QToolButton);
+ popupTimer.stop();
+ if (!menuButtonDown && !down)
+ return;
+
+ menuButtonDown = true;
+ QPointer<QMenu> actualMenu;
+ bool mustDeleteActualMenu = false;
+ if(menuAction) {
+ actualMenu = menuAction->menu();
+ } else if (defaultAction && defaultAction->menu()) {
+ actualMenu = defaultAction->menu();
+ } else {
+ actualMenu = new QMenu(q);
+ mustDeleteActualMenu = true;
+ QList<QAction*> actions = q->actions();
+ for(int i = 0; i < actions.size(); i++)
+ actualMenu->addAction(actions.at(i));
+ }
+ repeat = q->autoRepeat();
+ q->setAutoRepeat(false);
+ bool horizontal = true;
+#if !defined(QT_NO_TOOLBAR)
+ QToolBar *tb = qobject_cast<QToolBar*>(q->parentWidget());
+ if (tb && tb->orientation() == Qt::Vertical)
+ horizontal = false;
+#endif
+ QPoint p;
+ QRect screen = qApp->desktop()->availableGeometry(q);
+ QSize sh = ((QToolButton*)(QMenu*)actualMenu)->receivers(SIGNAL(aboutToShow()))? QSize() : actualMenu->sizeHint();
+ QRect rect = q->rect();
+ if (horizontal) {
+ if (q->isRightToLeft()) {
+ if (q->mapToGlobal(QPoint(0, rect.bottom())).y() + sh.height() <= screen.height()) {
+ p = q->mapToGlobal(rect.bottomRight());
+ } else {
+ p = q->mapToGlobal(rect.topRight() - QPoint(0, sh.height()));
+ }
+ p.rx() -= sh.width();
+ } else {
+ if (q->mapToGlobal(QPoint(0, rect.bottom())).y() + sh.height() <= screen.height()) {
+ p = q->mapToGlobal(rect.bottomLeft());
+ } else {
+ p = q->mapToGlobal(rect.topLeft() - QPoint(0, sh.height()));
+ }
+ }
+ } else {
+ if (q->isRightToLeft()) {
+ if (q->mapToGlobal(QPoint(rect.left(), 0)).x() - sh.width() <= screen.x()) {
+ p = q->mapToGlobal(rect.topRight());
+ } else {
+ p = q->mapToGlobal(rect.topLeft());
+ p.rx() -= sh.width();
+ }
+ } else {
+ if (q->mapToGlobal(QPoint(rect.right(), 0)).x() + sh.width() <= screen.right()) {
+ p = q->mapToGlobal(rect.topRight());
+ } else {
+ p = q->mapToGlobal(rect.topLeft() - QPoint(sh.width(), 0));
+ }
+ }
+ }
+ p.rx() = qMax(screen.left(), qMin(p.x(), screen.right() - sh.width()));
+ p.ry() += 1;
+ QPointer<QToolButton> that = q;
+ actualMenu->setNoReplayFor(q);
+ if (!mustDeleteActualMenu) //only if action are not in this widget
+ QObject::connect(actualMenu, SIGNAL(triggered(QAction*)), q, SLOT(_q_menuTriggered(QAction*)));
+ QObject::connect(actualMenu, SIGNAL(aboutToHide()), q, SLOT(_q_updateButtonDown()));
+ actualMenu->d_func()->causedPopup.widget = q;
+ actualMenu->d_func()->causedPopup.action = defaultAction;
+ actionsCopy = q->actions(); //(the list of action may be modified in slots)
+ actualMenu->exec(p);
+ QObject::disconnect(actualMenu, SIGNAL(aboutToHide()), q, SLOT(_q_updateButtonDown()));
+ if (mustDeleteActualMenu)
+ delete actualMenu;
+ else
+ QObject::disconnect(actualMenu, SIGNAL(triggered(QAction*)), q, SLOT(_q_menuTriggered(QAction*)));
+
+ if (!that)
+ return;
+
+ actionsCopy.clear();
+
+ if (repeat)
+ q->setAutoRepeat(true);
+}
+
+void QToolButtonPrivate::_q_updateButtonDown()
+{
+ Q_Q(QToolButton);
+ menuButtonDown = false;
+ if (q->isDown())
+ q->setDown(false);
+ else
+ q->repaint();
+}
+
+void QToolButtonPrivate::_q_menuTriggered(QAction *action)
+{
+ Q_Q(QToolButton);
+ if (action && !actionsCopy.contains(action))
+ emit q->triggered(action);
+}
+#endif // QT_NO_MENU
+
+#ifdef QT3_SUPPORT
+/*!
+ \fn void QToolButton::setPopupDelay(int delay)
+
+ Use the style hint QStyle::SH_ToolButton_PopupDelay instead.
+*/
+void QToolButton::setPopupDelay(int delay)
+{
+ Q_D(QToolButton);
+ d->userDefinedPopupDelay = true;
+ d->delay = delay;
+
+ update();
+}
+
+/*!
+ Use the style hint QStyle::SH_ToolButton_PopupDelay instead.
+*/
+int QToolButton::popupDelay() const
+{
+ Q_D(const QToolButton);
+ return d->delay;
+}
+#endif
+
+#ifndef QT_NO_MENU
+/*! \enum QToolButton::ToolButtonPopupMode
+
+ Describes how a menu should be popped up for tool buttons that has
+ a menu set or contains a list of actions.
+
+ \value DelayedPopup After pressing and holding the tool button
+ down for a certain amount of time (the timeout is style dependant,
+ see QStyle::SH_ToolButton_PopupDelay), the menu is displayed. A
+ typical application example is the "back" button in some web
+ browsers's tool bars. If the user clicks it, the browser simply
+ browses back to the previous page. If the user presses and holds
+ the button down for a while, the tool button shows a menu
+ containing the current history list
+
+ \value MenuButtonPopup In this mode the tool button displays a
+ special arrow to indicate that a menu is present. The menu is
+ displayed when the arrow part of the button is pressed.
+
+ \value InstantPopup The menu is displayed, without delay, when
+ the tool button is pressed. In this mode, the button's own action
+ is not triggered.
+*/
+
+/*!
+ \property QToolButton::popupMode
+ \brief describes the way that popup menus are used with tool buttons
+
+ By default, this property is set to \l DelayedPopup.
+*/
+
+void QToolButton::setPopupMode(ToolButtonPopupMode mode)
+{
+ Q_D(QToolButton);
+ d->popupMode = mode;
+}
+
+QToolButton::ToolButtonPopupMode QToolButton::popupMode() const
+{
+ Q_D(const QToolButton);
+ return d->popupMode;
+}
+#endif
+
+/*!
+ \property QToolButton::autoRaise
+ \brief whether auto-raising is enabled or not.
+
+ The default is disabled (i.e. false).
+
+ This property is currently ignored on Mac OS X when using QMacStyle.
+*/
+void QToolButton::setAutoRaise(bool enable)
+{
+ Q_D(QToolButton);
+ d->autoRaise = enable;
+
+ update();
+}
+
+bool QToolButton::autoRaise() const
+{
+ Q_D(const QToolButton);
+ return d->autoRaise;
+}
+
+/*!
+ Sets the default action to \a action.
+
+ If a tool button has a default action, the action defines the
+ button's properties like text, icon, tool tip, etc.
+ */
+void QToolButton::setDefaultAction(QAction *action)
+{
+ Q_D(QToolButton);
+#ifndef QT_NO_MENU
+ bool hadMenu = false;
+ hadMenu = d->hasMenu();
+#endif
+ d->defaultAction = action;
+ if (!action)
+ return;
+ if (!actions().contains(action))
+ addAction(action);
+ setText(action->iconText());
+ setIcon(action->icon());
+#ifndef QT_NO_TOOLTIP
+ setToolTip(action->toolTip());
+#endif
+#ifndef QT_NO_STATUSTIP
+ setStatusTip(action->statusTip());
+#endif
+#ifndef QT_NO_WHATSTHIS
+ setWhatsThis(action->whatsThis());
+#endif
+#ifndef QT_NO_MENU
+ if (action->menu() && !hadMenu) {
+ // new 'default' popup mode defined introduced by tool bar. We
+ // should have changed QToolButton's default instead. Do that
+ // in 4.2.
+ setPopupMode(QToolButton::MenuButtonPopup);
+ }
+#endif
+ setCheckable(action->isCheckable());
+ setChecked(action->isChecked());
+ setEnabled(action->isEnabled());
+ if (action->d_func()->fontSet)
+ setFont(action->font());
+}
+
+
+/*!
+ Returns the default action.
+
+ \sa setDefaultAction()
+ */
+QAction *QToolButton::defaultAction() const
+{
+ Q_D(const QToolButton);
+ return d->defaultAction;
+}
+
+
+
+/*!
+ \reimp
+ */
+void QToolButton::nextCheckState()
+{
+ Q_D(QToolButton);
+ if (!d->defaultAction)
+ QAbstractButton::nextCheckState();
+ else
+ d->defaultAction->trigger();
+}
+
+/*! \reimp */
+bool QToolButton::event(QEvent *event)
+{
+ switch(event->type()) {
+ case QEvent::HoverEnter:
+ case QEvent::HoverLeave:
+ case QEvent::HoverMove:
+ if (const QHoverEvent *he = static_cast<const QHoverEvent *>(event))
+ d_func()->updateHoverControl(he->pos());
+ break;
+ default:
+ break;
+ }
+ return QAbstractButton::event(event);
+}
+
+/*! \internal
+ */
+QToolButton::QToolButton(QToolButtonPrivate &dd, QWidget *parent)
+ :QAbstractButton(dd, parent)
+{
+ Q_D(QToolButton);
+ d->init();
+}
+
+/*!
+ \fn void QToolButton::setPixmap(const QPixmap &pixmap)
+
+ Use setIcon(QIcon(pixmap)) instead.
+*/
+
+/*!
+ \fn void QToolButton::setIconSet(const QIcon &icon)
+
+ Use setIcon() instead.
+*/
+
+/*!
+ \fn void QToolButton::setTextLabel(const QString &text, bool tooltip)
+
+ Use setText() and setToolTip() instead.
+*/
+
+/*!
+ \fn QString QToolButton::textLabel() const
+
+ Use text() instead.
+*/
+
+/*!
+ \fn QIcon QToolButton::iconSet() const
+
+ Use icon() instead.
+*/
+
+/*!
+ \fn void QToolButton::openPopup()
+
+ Use showMenu() instead.
+*/
+
+/*!
+ \fn void QToolButton::setPopup(QMenu* popup)
+
+ Use setMenu() instead.
+*/
+
+/*!
+ \fn QMenu* QToolButton::popup() const
+
+ Use menu() instead.
+*/
+
+/*!
+ \fn TextPosition QToolButton::textPosition() const
+
+ Use toolButtonStyle() instead.
+*/
+
+/*!
+ \fn void QToolButton::setTextPosition(QToolButton::TextPosition pos)
+
+ Use setToolButtonStyle() instead.
+*/
+
+/*!
+ \fn bool QToolButton::usesBigPixmap() const
+
+ Use iconSize() instead.
+*/
+
+/*!
+ \fn void QToolButton::setUsesBigPixmap(bool enable)
+
+ Use setIconSize() instead.
+*/
+
+/*!
+ \fn bool QToolButton::usesTextLabel() const
+
+ Use toolButtonStyle() instead.
+*/
+
+/*!
+ \fn void QToolButton::setUsesTextLabel(bool enable)
+
+ Use setToolButtonStyle() instead.
+*/
+
+QT_END_NAMESPACE
+
+#include "moc_qtoolbutton.cpp"
+
+#endif
diff --git a/src/gui/widgets/qtoolbutton.h b/src/gui/widgets/qtoolbutton.h
new file mode 100644
index 0000000000..d869081c0e
--- /dev/null
+++ b/src/gui/widgets/qtoolbutton.h
@@ -0,0 +1,199 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QTOOLBUTTON_H
+#define QTOOLBUTTON_H
+
+#include <QtGui/qabstractbutton.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+#ifndef QT_NO_TOOLBUTTON
+
+class QToolButtonPrivate;
+class QMenu;
+class QStyleOptionToolButton;
+
+class Q_GUI_EXPORT QToolButton : public QAbstractButton
+{
+ Q_OBJECT
+ Q_ENUMS(Qt::ToolButtonStyle Qt::ArrowType ToolButtonPopupMode)
+#ifndef QT_NO_MENU
+ Q_PROPERTY(ToolButtonPopupMode popupMode READ popupMode WRITE setPopupMode)
+#endif
+ Q_PROPERTY(Qt::ToolButtonStyle toolButtonStyle READ toolButtonStyle WRITE setToolButtonStyle)
+ Q_PROPERTY(bool autoRaise READ autoRaise WRITE setAutoRaise)
+ Q_PROPERTY(Qt::ArrowType arrowType READ arrowType WRITE setArrowType)
+
+public:
+ enum ToolButtonPopupMode {
+ DelayedPopup,
+ MenuButtonPopup,
+ InstantPopup
+ };
+
+ explicit QToolButton(QWidget * parent=0);
+ ~QToolButton();
+
+ QSize sizeHint() const;
+ QSize minimumSizeHint() const;
+
+ Qt::ToolButtonStyle toolButtonStyle() const;
+
+ Qt::ArrowType arrowType() const;
+ void setArrowType(Qt::ArrowType type);
+
+#ifndef QT_NO_MENU
+ void setMenu(QMenu* menu);
+ QMenu* menu() const;
+
+ void setPopupMode(ToolButtonPopupMode mode);
+ ToolButtonPopupMode popupMode() const;
+#endif
+
+ QAction *defaultAction() const;
+
+ void setAutoRaise(bool enable);
+ bool autoRaise() const;
+
+public Q_SLOTS:
+#ifndef QT_NO_MENU
+ void showMenu();
+#endif
+ void setToolButtonStyle(Qt::ToolButtonStyle style);
+ void setDefaultAction(QAction *);
+
+Q_SIGNALS:
+ void triggered(QAction *);
+
+protected:
+ QToolButton(QToolButtonPrivate &, QWidget* parent);
+ bool event(QEvent *e);
+ void mousePressEvent(QMouseEvent *);
+ void mouseReleaseEvent(QMouseEvent *);
+ void paintEvent(QPaintEvent *);
+ void actionEvent(QActionEvent *);
+
+ void enterEvent(QEvent *);
+ void leaveEvent(QEvent *);
+ void timerEvent(QTimerEvent *);
+ void changeEvent(QEvent *);
+
+ bool hitButton(const QPoint &pos) const;
+ void nextCheckState();
+ void initStyleOption(QStyleOptionToolButton *option) const;
+
+private:
+ Q_DISABLE_COPY(QToolButton)
+ Q_DECLARE_PRIVATE(QToolButton)
+#ifndef QT_NO_MENU
+ Q_PRIVATE_SLOT(d_func(), void _q_buttonPressed())
+ Q_PRIVATE_SLOT(d_func(), void _q_updateButtonDown())
+ Q_PRIVATE_SLOT(d_func(), void _q_menuTriggered(QAction*))
+#endif
+ Q_PRIVATE_SLOT(d_func(), void _q_actionTriggered())
+
+#ifdef QT3_SUPPORT
+public:
+ enum TextPosition {
+ BesideIcon,
+ BelowIcon
+ , Right = BesideIcon,
+ Under = BelowIcon
+ };
+
+ QT3_SUPPORT_CONSTRUCTOR QToolButton(QWidget * parent, const char* name);
+ QT3_SUPPORT_CONSTRUCTOR QToolButton(Qt::ArrowType type, QWidget *parent, const char* name);
+ QT3_SUPPORT_CONSTRUCTOR QToolButton( const QIcon& s, const QString &textLabel,
+ const QString& grouptext,
+ QObject * receiver, const char* slot,
+ QWidget * parent, const char* name=0 );
+ inline QT3_SUPPORT void setPixmap(const QPixmap &pixmap) { setIcon(static_cast<QIcon>(pixmap)); }
+ QT3_SUPPORT void setOnIconSet(const QIcon&);
+ QT3_SUPPORT void setOffIconSet(const QIcon&);
+ inline QT3_SUPPORT void setIconSet(const QIcon &icon){setIcon(icon);}
+ QT3_SUPPORT void setIconSet(const QIcon &, bool on);
+ inline QT3_SUPPORT void setTextLabel(const QString &text, bool tooltip = true) {
+ setText(text);
+#ifndef QT_NO_TOOLTIP
+ if (tooltip)
+ setToolTip(text);
+#else
+ Q_UNUSED(tooltip);
+#endif
+ }
+ inline QT3_SUPPORT QString textLabel() const { return text(); }
+ QT3_SUPPORT QIcon onIconSet() const;
+ QT3_SUPPORT QIcon offIconSet() const;
+ QT3_SUPPORT QIcon iconSet(bool on) const;
+ inline QT3_SUPPORT QIcon iconSet() const { return icon(); }
+ inline QT3_SUPPORT void openPopup() { showMenu(); }
+ inline QT3_SUPPORT void setPopup(QMenu* popup) {setMenu(popup); }
+ inline QT3_SUPPORT QMenu* popup() const { return menu(); }
+ inline QT3_SUPPORT bool usesBigPixmap() const { return iconSize().height() > 22; }
+ inline QT3_SUPPORT bool usesTextLabel() const { return toolButtonStyle() != Qt::ToolButtonIconOnly; }
+ inline QT3_SUPPORT TextPosition textPosition() const
+ { return toolButtonStyle() == Qt::ToolButtonTextUnderIcon ? BelowIcon : BesideIcon; }
+ QT3_SUPPORT void setPopupDelay(int delay);
+ QT3_SUPPORT int popupDelay() const;
+
+public Q_SLOTS:
+ QT_MOC_COMPAT void setUsesBigPixmap(bool enable)
+ { setIconSize(enable?QSize(32,32):QSize(22,22)); }
+ QT_MOC_COMPAT void setUsesTextLabel(bool enable)
+ { setToolButtonStyle(enable?Qt::ToolButtonTextUnderIcon : Qt::ToolButtonIconOnly); }
+ QT_MOC_COMPAT void setTextPosition(QToolButton::TextPosition pos)
+ { setToolButtonStyle(pos == BesideIcon ? Qt::ToolButtonTextBesideIcon : Qt::ToolButtonTextUnderIcon); }
+
+#endif
+};
+
+#endif // QT_NO_TOOLBUTTON
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QTOOLBUTTON_H
diff --git a/src/gui/widgets/qvalidator.cpp b/src/gui/widgets/qvalidator.cpp
new file mode 100644
index 0000000000..3aca13d185
--- /dev/null
+++ b/src/gui/widgets/qvalidator.cpp
@@ -0,0 +1,909 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <qdebug.h>
+
+#include "qvalidator.h"
+#ifndef QT_NO_VALIDATOR
+#include "private/qobject_p.h"
+#include "private/qlocale_p.h"
+
+#include <limits.h>
+#include <math.h>
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QValidator
+ \brief The QValidator class provides validation of input text.
+
+ \ingroup misc
+ \mainclass
+
+ The class itself is abstract. Two subclasses, \l QIntValidator and
+ \l QDoubleValidator, provide basic numeric-range checking, and \l
+ QRegExpValidator provides general checking using a custom regular
+ expression.
+
+ If the built-in validators aren't sufficient, you can subclass
+ QValidator. The class has two virtual functions: validate() and
+ fixup().
+
+ \l validate() must be implemented by every subclass. It returns
+ \l Invalid, \l Intermediate or \l Acceptable depending on whether
+ its argument is valid (for the subclass's definition of valid).
+
+ These three states require some explanation. An \l Invalid string
+ is \e clearly invalid. \l Intermediate is less obvious: the
+ concept of validity is difficult to apply when the string is
+ incomplete (still being edited). QValidator defines \l Intermediate
+ as the property of a string that is neither clearly invalid nor
+ acceptable as a final result. \l Acceptable means that the string
+ is acceptable as a final result. One might say that any string
+ that is a plausible intermediate state during entry of an \l
+ Acceptable string is \l Intermediate.
+
+ Here are some examples:
+
+ \list
+
+ \i For a line edit that accepts integers from 10 to 1000 inclusive,
+ 42 and 123 are \l Acceptable, the empty string and 5 are \l
+ Intermediate, and "asdf" and 1114 is \l Invalid.
+
+ \i For an editable combobox that accepts URLs, any well-formed URL
+ is \l Acceptable, "http://qtsoftware.com/," is \l Intermediate
+ (it might be a cut and paste action that accidentally took in a
+ comma at the end), the empty string is \l Intermediate (the user
+ might select and delete all of the text in preparation for entering
+ a new URL) and "http:///./" is \l Invalid.
+
+ \i For a spin box that accepts lengths, "11cm" and "1in" are \l
+ Acceptable, "11" and the empty string are \l Intermediate, and
+ "http://qtsoftware.com" and "hour" are \l Invalid.
+
+ \endlist
+
+ \l fixup() is provided for validators that can repair some user
+ errors. The default implementation does nothing. QLineEdit, for
+ example, will call fixup() if the user presses Enter (or Return)
+ and the content is not currently valid. This allows the fixup()
+ function the opportunity of performing some magic to make an \l
+ Invalid string \l Acceptable.
+
+ A validator has a locale, set with setLocale(). It is typically used
+ to parse localized data. For example, QIntValidator and QDoubleValidator
+ use it to parse localized representations of integers and doubles.
+
+ QValidator is typically used with QLineEdit, QSpinBox and
+ QComboBox.
+
+ \sa QIntValidator, QDoubleValidator, QRegExpValidator, {Line Edits Example}
+*/
+
+
+/*!
+ \enum QValidator::State
+
+ This enum type defines the states in which a validated string can
+ exist.
+
+ \value Invalid The string is \e clearly invalid.
+ \value Intermediate The string is a plausible intermediate value.
+ \value Acceptable The string is acceptable as a final result;
+ i.e. it is valid.
+
+ \omitvalue Valid
+*/
+
+class QValidatorPrivate : public QObjectPrivate{
+ Q_DECLARE_PUBLIC(QValidator)
+public:
+ QValidatorPrivate() : QObjectPrivate()
+ {
+ }
+
+ QLocale locale;
+};
+
+
+/*!
+ Sets up the validator. The \a parent parameter is
+ passed on to the QObject constructor.
+*/
+
+QValidator::QValidator(QObject * parent)
+ : QObject(*new QValidatorPrivate, parent)
+{
+}
+
+#ifdef QT3_SUPPORT
+/*!
+ \obsolete
+ Sets up the validator. The \a parent and \a name parameters are
+ passed on to the QObject constructor.
+*/
+
+QValidator::QValidator(QObject * parent, const char *name)
+ : QObject(*new QValidatorPrivate, parent)
+{
+ setObjectName(QString::fromAscii(name));
+}
+#endif
+
+/*!
+ Destroys the validator, freeing any storage and other resources
+ used.
+*/
+
+QValidator::~QValidator()
+{
+}
+
+/*!
+ Returns the locale for the validator. The locale is by default initialized to the same as QLocale().
+
+ \sa setLocale()
+ \sa QLocale::QLocale()
+*/
+QLocale QValidator::locale() const
+{
+ Q_D(const QValidator);
+ return d->locale;
+}
+
+/*!
+ Sets the \a locale that will be used for the validator. Unless
+ setLocale has been called, the validator will use the default
+ locale set with QLocale::setDefault(). If a default locale has not
+ been set, it is the operating system's locale.
+
+ \sa locale() QLocale::setDefault()
+*/
+void QValidator::setLocale(const QLocale &locale)
+{
+ Q_D(QValidator);
+ d->locale = locale;
+}
+
+/*!
+ \fn QValidator::State QValidator::validate(QString &input, int &pos) const
+
+ This virtual function returns \l Invalid if \a input is invalid
+ according to this validator's rules, \l Intermediate if it
+ is likely that a little more editing will make the input
+ acceptable (e.g. the user types "4" into a widget which accepts
+ integers between 10 and 99), and \l Acceptable if the input is
+ valid.
+
+ The function can change both \a input and \a pos (the cursor position)
+ if required.
+*/
+
+
+/*!
+ \fn void QValidator::fixup(QString & input) const
+
+ This function attempts to change \a input to be valid according to
+ this validator's rules. It need not result in a valid string:
+ callers of this function must re-test afterwards; the default does
+ nothing.
+
+ Reimplementations of this function can change \a input even if
+ they do not produce a valid string. For example, an ISBN validator
+ might want to delete every character except digits and "-", even
+ if the result is still not a valid ISBN; a surname validator might
+ want to remove whitespace from the start and end of the string,
+ even if the resulting string is not in the list of accepted
+ surnames.
+*/
+
+void QValidator::fixup(QString &) const
+{
+}
+
+
+/*!
+ \class QIntValidator
+ \brief The QIntValidator class provides a validator that ensures
+ a string contains a valid integer within a specified range.
+
+ \ingroup misc
+
+ Example of use:
+
+ \snippet doc/src/snippets/code/src_gui_widgets_qvalidator.cpp 0
+
+ Below we present some examples of validators. In practice they would
+ normally be associated with a widget as in the example above.
+
+ \snippet doc/src/snippets/code/src_gui_widgets_qvalidator.cpp 1
+
+ Notice that the value \c 999 returns Intermediate. Values
+ consisting of a number of digits equal to or less than the max
+ value are considered intermediate. This is intended because the
+ digit that prevents a number to be in range is not necessarily the
+ last digit typed. This also means that an intermediate number can
+ have leading zeros.
+
+ The minimum and maximum values are set in one call with setRange(),
+ or individually with setBottom() and setTop().
+
+ QIntValidator uses its locale() to interpret the number. For example,
+ in Arabic locales, QIntValidator will accept Arabic digits. In addition,
+ QIntValidator is always guaranteed to accept a number formatted according
+ to the "C" locale.
+
+ \sa QDoubleValidator, QRegExpValidator, {Line Edits Example}
+*/
+
+/*!
+ Constructs a validator with a \a parent object that
+ accepts all integers.
+*/
+
+QIntValidator::QIntValidator(QObject * parent)
+ : QValidator(parent)
+{
+ b = INT_MIN;
+ t = INT_MAX;
+}
+
+
+/*!
+ Constructs a validator with a \a parent, that accepts integers
+ from \a minimum to \a maximum inclusive.
+*/
+
+QIntValidator::QIntValidator(int minimum, int maximum,
+ QObject * parent)
+ : QValidator(parent)
+{
+ b = minimum;
+ t = maximum;
+}
+
+
+#ifdef QT3_SUPPORT
+/*!
+ \obsolete
+
+ Constructs a validator with a \a parent object and a \a name that
+ accepts all integers.
+*/
+
+QIntValidator::QIntValidator(QObject * parent, const char *name)
+ : QValidator(parent)
+{
+ setObjectName(QString::fromAscii(name));
+ b = INT_MIN;
+ t = INT_MAX;
+}
+
+
+/*!
+ \obsolete
+
+ Constructs a validator called \a name with a \a parent, that
+ accepts integers from \a minimum to \a maximum inclusive.
+*/
+
+QIntValidator::QIntValidator(int minimum, int maximum,
+ QObject * parent, const char* name)
+ : QValidator(parent)
+{
+ setObjectName(QString::fromAscii(name));
+ b = minimum;
+ t = maximum;
+}
+#endif
+
+/*!
+ Destroys the validator.
+*/
+
+QIntValidator::~QIntValidator()
+{
+ // nothing
+}
+
+
+/*!
+ \fn QValidator::State QIntValidator::validate(QString &input, int &pos) const
+
+ Returns \l Acceptable if the \a input is an integer within the
+ valid range, \l Intermediate if the \a input is a prefix of an integer in the
+ valid range, and \l Invalid otherwise.
+
+ If the valid range consists of just positive integers (e.g., 32 to 100)
+ and \a input is a negative integer, then Invalid is returned. (On the other
+ hand, if the range consists of negative integers (e.g., -100 to -32) and
+ \a input is a positive integer, then Intermediate is returned, because
+ the user might be just about to type the minus (especially for right-to-left
+ languages).
+
+ \snippet doc/src/snippets/code/src_gui_widgets_qvalidator.cpp 2
+
+ By default, the \a pos parameter is not used by this validator.
+*/
+
+static int numDigits(qlonglong n)
+{
+ if (n == 0)
+ return 1;
+ return (int)log10(double(n)) + 1;
+};
+
+static qlonglong pow10(int exp)
+{
+ qlonglong result = 1;
+ for (int i = 0; i < exp; ++i)
+ result *= 10;
+ return result;
+}
+
+QValidator::State QIntValidator::validate(QString & input, int&) const
+{
+ QByteArray buff;
+ if (!locale().d()->validateChars(input, QLocalePrivate::IntegerMode, &buff)) {
+ QLocale cl(QLocale::C);
+ if (!cl.d()->validateChars(input, QLocalePrivate::IntegerMode, &buff))
+ return Invalid;
+ }
+
+ if (buff.isEmpty())
+ return Intermediate;
+
+ if (b >= 0 && buff.startsWith('-'))
+ return Invalid;
+
+ if (t < 0 && buff.startsWith('+'))
+ return Invalid;
+
+ if (buff.size() == 1 && (buff.at(0) == '+' || buff.at(0) == '-'))
+ return Intermediate;
+
+ bool ok, overflow;
+ qlonglong entered = QLocalePrivate::bytearrayToLongLong(buff.constData(), 10, &ok, &overflow);
+ if (overflow || !ok)
+ return Invalid;
+ if (entered >= b && entered <= t)
+ return Acceptable;
+
+ if (entered >= 0) {
+ // the -entered < b condition is necessary to allow people to type
+ // the minus last (e.g. for right-to-left languages)
+ return (entered > t && -entered < b) ? Invalid : Intermediate;
+ } else {
+ return (entered < b) ? Invalid : Intermediate;
+ }
+}
+
+
+/*!
+ Sets the range of the validator to only accept integers between \a
+ bottom and \a top inclusive.
+*/
+
+void QIntValidator::setRange(int bottom, int top)
+{
+ b = bottom;
+ t = top;
+}
+
+
+/*!
+ \property QIntValidator::bottom
+ \brief the validator's lowest acceptable value
+
+ By default, this property's value is derived from the lowest signed
+ integer available (typically -2147483647).
+
+ \sa setRange()
+*/
+void QIntValidator::setBottom(int bottom)
+{
+ setRange(bottom, top());
+}
+
+/*!
+ \property QIntValidator::top
+ \brief the validator's highest acceptable value
+
+ By default, this property's value is derived from the highest signed
+ integer available (typically 2147483647).
+
+ \sa setRange()
+*/
+void QIntValidator::setTop(int top)
+{
+ setRange(bottom(), top);
+}
+
+
+#ifndef QT_NO_REGEXP
+
+/*!
+ \internal
+*/
+QValidator::QValidator(QObjectPrivate &d, QObject *parent)
+ : QObject(d, parent)
+{
+}
+
+/*!
+ \internal
+*/
+QValidator::QValidator(QValidatorPrivate &d, QObject *parent)
+ : QObject(d, parent)
+{
+}
+
+class QDoubleValidatorPrivate : public QValidatorPrivate
+{
+ Q_DECLARE_PUBLIC(QDoubleValidator)
+public:
+ QDoubleValidatorPrivate()
+ : QValidatorPrivate()
+ , notation(QDoubleValidator::ScientificNotation)
+ {
+ }
+
+ QDoubleValidator::Notation notation;
+};
+
+
+/*!
+ \class QDoubleValidator
+
+ \brief The QDoubleValidator class provides range checking of
+ floating-point numbers.
+
+ \ingroup misc
+
+ QDoubleValidator provides an upper bound, a lower bound, and a
+ limit on the number of digits after the decimal point. It does not
+ provide a fixup() function.
+
+ You can set the acceptable range in one call with setRange(), or
+ with setBottom() and setTop(). Set the number of decimal places
+ with setDecimals(). The validate() function returns the validation
+ state.
+
+ QDoubleValidator uses its locale() to interpret the number. For example,
+ in the German locale, "1,234" will be accepted as the fractional number
+ 1.234. In Arabic locales, QDoubleValidator will accept Arabic digits.
+
+ In addition, QDoubleValidator is always guaranteed to accept a number
+ formatted according to the "C" locale. QDoubleValidator will not accept
+ numbers with thousand-seperators.
+
+ \sa QIntValidator, QRegExpValidator, {Line Edits Example}
+*/
+
+ /*!
+ \enum QDoubleValidator::Notation
+ \since 4.3
+ This enum defines the allowed notations for entering a double.
+
+ \value StandardNotation The string is written as a standard number
+ (i.e. 0.015).
+ \value ScientificNotation The string is written in scientific
+ form. It may have an exponent part(i.e. 1.5E-2).
+*/
+
+/*!
+ Constructs a validator object with a \a parent object
+ that accepts any double.
+*/
+
+QDoubleValidator::QDoubleValidator(QObject * parent)
+ : QValidator(*new QDoubleValidatorPrivate , parent)
+{
+ b = -HUGE_VAL;
+ t = HUGE_VAL;
+ dec = 1000;
+}
+
+
+/*!
+ Constructs a validator object with a \a parent object. This
+ validator will accept doubles from \a bottom to \a top inclusive,
+ with up to \a decimals digits after the decimal point.
+*/
+
+QDoubleValidator::QDoubleValidator(double bottom, double top, int decimals,
+ QObject * parent)
+ : QValidator(*new QDoubleValidatorPrivate , parent)
+{
+ b = bottom;
+ t = top;
+ dec = decimals;
+}
+
+#ifdef QT3_SUPPORT
+/*!
+ \obsolete
+
+ Constructs a validator object with a \a parent object and a \a name
+ that accepts any double.
+*/
+
+QDoubleValidator::QDoubleValidator(QObject * parent, const char *name)
+ : QValidator(*new QDoubleValidatorPrivate , parent)
+{
+ setObjectName(QString::fromAscii(name));
+ b = -HUGE_VAL;
+ t = HUGE_VAL;
+ dec = 1000;
+}
+
+
+/*!
+ \obsolete
+
+ Constructs a validator object with a \a parent object, called \a
+ name. This validator will accept doubles from \a bottom to \a top
+ inclusive, with up to \a decimals digits after the decimal point.
+*/
+
+QDoubleValidator::QDoubleValidator(double bottom, double top, int decimals,
+ QObject * parent, const char* name)
+ : QValidator(*new QDoubleValidatorPrivate, parent)
+{
+ setObjectName(QString::fromAscii(name));
+ b = bottom;
+ t = top;
+ dec = decimals;
+}
+#endif
+
+/*!
+ Destroys the validator.
+*/
+
+QDoubleValidator::~QDoubleValidator()
+{
+}
+
+
+/*!
+ \fn QValidator::State QDoubleValidator::validate(QString &input, int &pos) const
+
+ Returns \l Acceptable if the string \a input contains a double
+ that is within the valid range and is in the correct format.
+
+ Returns \l Intermediate if \a input contains a double that is
+ outside the range or is in the wrong format; e.g. with too many
+ digits after the decimal point or is empty.
+
+ Returns \l Invalid if the \a input is not a double.
+
+ Note: If the valid range consists of just positive doubles (e.g. 0.0 to 100.0)
+ and \a input is a negative double then \l Invalid is returned. If notation()
+ is set to StandardNotation, and the input contains more digits before the
+ decimal point than a double in the valid range may have, \l Invalid is returned.
+ If notation() is ScientificNotation, and the input is not in the valid range,
+ \l Intermediate is returned. The value may yet become valid by changing the exponent.
+
+ By default, the \a pos parameter is not used by this validator.
+*/
+
+#ifndef LLONG_MAX
+# define LLONG_MAX Q_INT64_C(0x7fffffffffffffff)
+#endif
+
+QValidator::State QDoubleValidator::validate(QString & input, int &) const
+{
+ Q_D(const QDoubleValidator);
+
+ QLocalePrivate::NumberMode numMode = QLocalePrivate::DoubleStandardMode;
+ switch (d->notation) {
+ case StandardNotation:
+ numMode = QLocalePrivate::DoubleStandardMode;
+ break;
+ case ScientificNotation:
+ numMode = QLocalePrivate::DoubleScientificMode;
+ break;
+ };
+
+ QByteArray buff;
+ if (!locale().d()->validateChars(input, numMode, &buff, dec)) {
+ QLocale cl(QLocale::C);
+ if (!cl.d()->validateChars(input, numMode, &buff, dec))
+ return Invalid;
+ }
+
+ if (buff.isEmpty())
+ return Intermediate;
+
+ if (b >= 0 && buff.startsWith('-'))
+ return Invalid;
+
+ if (t < 0 && buff.startsWith('+'))
+ return Invalid;
+
+ bool ok, overflow;
+ double i = QLocalePrivate::bytearrayToDouble(buff.constData(), &ok, &overflow);
+ if (overflow)
+ return Invalid;
+ if (!ok)
+ return Intermediate;
+
+ if (i >= b && i <= t)
+ return Acceptable;
+
+ if (d->notation == StandardNotation) {
+ double max = qMax(qAbs(b), qAbs(t));
+ if (max < LLONG_MAX) {
+ qlonglong n = pow10(numDigits(qlonglong(max))) - 1;
+ if (qAbs(i) > n)
+ return Invalid;
+ }
+ }
+
+ return Intermediate;
+}
+
+
+/*!
+ Sets the validator to accept doubles from \a minimum to \a maximum
+ inclusive, with at most \a decimals digits after the decimal
+ point.
+*/
+
+void QDoubleValidator::setRange(double minimum, double maximum, int decimals)
+{
+ b = minimum;
+ t = maximum;
+ dec = decimals;
+}
+
+/*!
+ \property QDoubleValidator::bottom
+ \brief the validator's minimum acceptable value
+
+ By default, this property contains a value of -infinity.
+
+ \sa setRange()
+*/
+
+void QDoubleValidator::setBottom(double bottom)
+{
+ setRange(bottom, top(), decimals());
+}
+
+
+/*!
+ \property QDoubleValidator::top
+ \brief the validator's maximum acceptable value
+
+ By default, this property contains a value of infinity.
+
+ \sa setRange()
+*/
+
+void QDoubleValidator::setTop(double top)
+{
+ setRange(bottom(), top, decimals());
+}
+
+/*!
+ \property QDoubleValidator::decimals
+ \brief the validator's maximum number of digits after the decimal point
+
+ By default, this property contains a value of 1000.
+
+ \sa setRange()
+*/
+
+void QDoubleValidator::setDecimals(int decimals)
+{
+ setRange(bottom(), top(), decimals);
+}
+
+/*!
+ \property QDoubleValidator::notation
+ \since 4.3
+ \brief the notation of how a string can describe a number
+
+ By default, this property is set to ScientificNotation.
+
+ \sa Notation
+*/
+
+void QDoubleValidator::setNotation(Notation newNotation)
+{
+ Q_D(QDoubleValidator);
+ d->notation = newNotation;
+}
+
+QDoubleValidator::Notation QDoubleValidator::notation() const
+{
+ Q_D(const QDoubleValidator);
+ return d->notation;
+}
+
+/*!
+ \class QRegExpValidator
+ \brief The QRegExpValidator class is used to check a string
+ against a regular expression.
+
+ \ingroup misc
+
+ QRegExpValidator uses a regular expression (regexp) to
+ determine whether an input string is \l Acceptable, \l
+ Intermediate, or \l Invalid. The regexp can either be supplied
+ when the QRegExpValidator is constructed, or at a later time.
+
+ When QRegExpValidator determines whether a string is \l Acceptable
+ or not, the regexp is treated as if it begins with the start of string
+ assertion (\bold{^}) and ends with the end of string assertion
+ (\bold{$}); the match is against the entire input string, or from
+ the given position if a start position greater than zero is given.
+
+ If a string is a prefix of an \l Acceptable string, it is considered
+ \l Intermediate. For example, "" and "A" are \l Intermediate for the
+ regexp \bold{[A-Z][0-9]} (whereas "_" would be \l Invalid).
+
+ For a brief introduction to Qt's regexp engine, see \l QRegExp.
+
+ Example of use:
+ \snippet doc/src/snippets/code/src_gui_widgets_qvalidator.cpp 3
+
+ Below we present some examples of validators. In practice they would
+ normally be associated with a widget as in the example above.
+
+ \snippet doc/src/snippets/code/src_gui_widgets_qvalidator.cpp 4
+
+ \sa QRegExp, QIntValidator, QDoubleValidator, {Settings Editor Example}
+*/
+
+/*!
+ Constructs a validator with a \a parent object that accepts
+ any string (including an empty one) as valid.
+*/
+
+QRegExpValidator::QRegExpValidator(QObject *parent)
+ : QValidator(parent), r(QString::fromLatin1(".*"))
+{
+}
+
+/*!
+ Constructs a validator with a \a parent object that
+ accepts all strings that match the regular expression \a rx.
+
+ The match is made against the entire string; e.g. if the regexp is
+ \bold{[A-Fa-f0-9]+} it will be treated as \bold{^[A-Fa-f0-9]+$}.
+*/
+
+QRegExpValidator::QRegExpValidator(const QRegExp& rx, QObject *parent)
+ : QValidator(parent), r(rx)
+{
+}
+
+#ifdef QT3_SUPPORT
+/*!
+ \obsolete
+
+ Constructs a validator with a \a parent object and \a name that accepts
+ any string (including an empty one) as valid.
+*/
+
+QRegExpValidator::QRegExpValidator(QObject *parent, const char *name)
+ : QValidator(parent), r(QString::fromLatin1(".*"))
+{
+ setObjectName(QString::fromAscii(name));
+}
+
+/*!
+ \obsolete
+
+ Constructs a validator with a \a parent object and a \a name that
+ accepts all strings that match the regular expression \a rx.
+
+ The match is made against the entire string; e.g. if the regexp is
+ \bold{[A-Fa-f0-9]+} it will be treated as \bold{^[A-Fa-f0-9]+$}.
+*/
+
+QRegExpValidator::QRegExpValidator(const QRegExp& rx, QObject *parent,
+ const char *name)
+ : QValidator(parent), r(rx)
+{
+ setObjectName(QString::fromAscii(name));
+}
+#endif
+
+/*!
+ Destroys the validator.
+*/
+
+QRegExpValidator::~QRegExpValidator()
+{
+}
+
+/*!
+ Returns \l Acceptable if \a input is matched by the regular
+ expression for this validator, \l Intermediate if it has matched
+ partially (i.e. could be a valid match if additional valid
+ characters are added), and \l Invalid if \a input is not matched.
+
+ The \a pos parameter is set to the length of the \a input parameter.
+
+ For example, if the regular expression is \bold{\\w\\d\\d}
+ (word-character, digit, digit) then "A57" is \l Acceptable,
+ "E5" is \l Intermediate, and "+9" is \l Invalid.
+
+ \sa QRegExp::exactMatch()
+*/
+
+QValidator::State QRegExpValidator::validate(QString &input, int& pos) const
+{
+ if (r.exactMatch(input)) {
+ return Acceptable;
+ } else {
+ if (const_cast<QRegExp &>(r).matchedLength() == input.size()) {
+ return Intermediate;
+ } else {
+ pos = input.size();
+ return Invalid;
+ }
+ }
+}
+
+/*!
+ \property QRegExpValidator::regExp
+ \brief the regular expression used for validation
+
+ By default, this property contains a regular expression with the pattern \c{.*}
+ that matches any string.
+*/
+
+void QRegExpValidator::setRegExp(const QRegExp& rx)
+{
+ r = rx;
+}
+
+#endif
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_VALIDATOR
diff --git a/src/gui/widgets/qvalidator.h b/src/gui/widgets/qvalidator.h
new file mode 100644
index 0000000000..8114ca2854
--- /dev/null
+++ b/src/gui/widgets/qvalidator.h
@@ -0,0 +1,215 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QVALIDATOR_H
+#define QVALIDATOR_H
+
+#include <QtCore/qobject.h>
+#include <QtCore/qstring.h>
+#include <QtCore/qregexp.h>
+#include <QtCore/qlocale.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+#ifndef QT_NO_VALIDATOR
+
+class QValidatorPrivate;
+
+class Q_GUI_EXPORT QValidator : public QObject
+{
+ Q_OBJECT
+public:
+ explicit QValidator(QObject * parent);
+ ~QValidator();
+
+ enum State {
+ Invalid,
+ Intermediate,
+ Acceptable
+
+#if defined(QT3_SUPPORT) && !defined(Q_MOC_RUN)
+ , Valid = Intermediate
+#endif
+ };
+
+ void setLocale(const QLocale &locale);
+ QLocale locale() const;
+
+ virtual State validate(QString &, int &) const = 0;
+ virtual void fixup(QString &) const;
+
+#ifdef QT3_SUPPORT
+public:
+ QT3_SUPPORT_CONSTRUCTOR QValidator(QObject * parent, const char *name);
+#endif
+protected:
+ QValidator(QObjectPrivate &d, QObject *parent);
+ QValidator(QValidatorPrivate &d, QObject *parent);
+
+private:
+ Q_DISABLE_COPY(QValidator)
+ Q_DECLARE_PRIVATE(QValidator)
+};
+
+class Q_GUI_EXPORT QIntValidator : public QValidator
+{
+ Q_OBJECT
+ Q_PROPERTY(int bottom READ bottom WRITE setBottom)
+ Q_PROPERTY(int top READ top WRITE setTop)
+
+public:
+ explicit QIntValidator(QObject * parent);
+ QIntValidator(int bottom, int top, QObject * parent);
+ ~QIntValidator();
+
+ QValidator::State validate(QString &, int &) const;
+
+ void setBottom(int);
+ void setTop(int);
+ virtual void setRange(int bottom, int top);
+
+ int bottom() const { return b; }
+ int top() const { return t; }
+
+#ifdef QT3_SUPPORT
+public:
+ QT3_SUPPORT_CONSTRUCTOR QIntValidator(QObject * parent, const char *name);
+ QT3_SUPPORT_CONSTRUCTOR QIntValidator(int bottom, int top, QObject * parent, const char *name);
+#endif
+
+private:
+ Q_DISABLE_COPY(QIntValidator)
+
+ int b;
+ int t;
+};
+
+#ifndef QT_NO_REGEXP
+
+class QDoubleValidatorPrivate;
+
+class Q_GUI_EXPORT QDoubleValidator : public QValidator
+{
+ Q_OBJECT
+ Q_PROPERTY(double bottom READ bottom WRITE setBottom)
+ Q_PROPERTY(double top READ top WRITE setTop)
+ Q_PROPERTY(int decimals READ decimals WRITE setDecimals)
+ Q_PROPERTY(Notation notation READ notation WRITE setNotation)
+
+public:
+ explicit QDoubleValidator(QObject * parent);
+ QDoubleValidator(double bottom, double top, int decimals, QObject * parent);
+ ~QDoubleValidator();
+
+ enum Notation {
+ StandardNotation,
+ ScientificNotation
+ };
+
+ QValidator::State validate(QString &, int &) const;
+
+ virtual void setRange(double bottom, double top, int decimals = 0);
+ void setBottom(double);
+ void setTop(double);
+ void setDecimals(int);
+ void setNotation(Notation);
+
+ double bottom() const { return b; }
+ double top() const { return t; }
+ int decimals() const { return dec; }
+ Notation notation() const;
+
+#ifdef QT3_SUPPORT
+public:
+ QT3_SUPPORT_CONSTRUCTOR QDoubleValidator(QObject * parent, const char *name);
+ QT3_SUPPORT_CONSTRUCTOR QDoubleValidator(double bottom, double top, int decimals,
+ QObject * parent, const char *name);
+#endif
+private:
+ Q_DECLARE_PRIVATE(QDoubleValidator)
+ Q_DISABLE_COPY(QDoubleValidator)
+
+ double b;
+ double t;
+ int dec;
+};
+
+
+class Q_GUI_EXPORT QRegExpValidator : public QValidator
+{
+ Q_OBJECT
+ Q_PROPERTY(QRegExp regExp READ regExp WRITE setRegExp)
+
+public:
+ explicit QRegExpValidator(QObject *parent);
+ QRegExpValidator(const QRegExp& rx, QObject *parent);
+ ~QRegExpValidator();
+
+ virtual QValidator::State validate(QString& input, int& pos) const;
+
+ void setRegExp(const QRegExp& rx);
+ const QRegExp& regExp() const { return r; } // ### make inline for 5.0
+
+#ifdef QT3_SUPPORT
+public:
+ QT3_SUPPORT_CONSTRUCTOR QRegExpValidator(QObject *parent, const char *name);
+ QT3_SUPPORT_CONSTRUCTOR QRegExpValidator(const QRegExp& rx, QObject *parent, const char *name);
+#endif
+
+private:
+ Q_DISABLE_COPY(QRegExpValidator)
+
+ QRegExp r;
+};
+
+#endif // QT_NO_REGEXP
+
+#endif // QT_NO_VALIDATOR
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QVALIDATOR_H
diff --git a/src/gui/widgets/qwidgetanimator.cpp b/src/gui/widgets/qwidgetanimator.cpp
new file mode 100644
index 0000000000..5584ba1665
--- /dev/null
+++ b/src/gui/widgets/qwidgetanimator.cpp
@@ -0,0 +1,198 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtCore/qtimer.h>
+#include <QtCore/qdatetime.h>
+#include <QtGui/qwidget.h>
+#include <QtGui/qtextedit.h>
+#include <QtGui/private/qwidget_p.h>
+#include <qdebug.h>
+
+#include "qwidgetanimator_p.h"
+
+QT_BEGIN_NAMESPACE
+
+static const int g_animation_steps = 12;
+static const int g_animation_interval = 16;
+
+// 1000 * (x/(1 + x*x) + 0.5) on interval [-1, 1]
+static const int g_animate_function[] =
+{
+ 0, 1, 5, 12, 23, 38, 58, 84, 116, 155, 199, 251, 307, 368,
+ 433, 500, 566, 631, 692, 748, 799, 844, 883, 915, 941, 961,
+ 976, 987, 994, 998, 1000
+};
+static const int g_animate_function_points = sizeof(g_animate_function)/sizeof(int);
+
+static inline int animateHelper(int start, int stop, int step, int steps)
+{
+ if (start == stop)
+ return start;
+ if (step == 0)
+ return start;
+ if (step == steps)
+ return stop;
+
+ int x = g_animate_function_points*step/(steps + 1);
+ return start + g_animate_function[x]*(stop - start)/1000;
+}
+
+QWidgetAnimator::QWidgetAnimator(QObject *parent)
+ : QObject(parent)
+{
+ m_time = new QTime();
+ m_timer = new QTimer(this);
+ m_timer->setInterval(g_animation_interval);
+ connect(m_timer, SIGNAL(timeout()), this, SLOT(animationStep()));
+}
+
+QWidgetAnimator::~QWidgetAnimator()
+{
+ delete m_time;
+}
+
+void QWidgetAnimator::abort(QWidget *w)
+{
+ if (m_animation_map.remove(w) == 0)
+ return;
+ if (m_animation_map.isEmpty()) {
+ m_timer->stop();
+ emit finishedAll();
+ }
+}
+
+void QWidgetAnimator::animate(QWidget *widget, const QRect &_final_geometry, bool animate)
+{
+ QRect final_geometry = _final_geometry;
+
+ QRect r = widget->geometry();
+ if (r.right() < 0 || r.bottom() < 0)
+ r = QRect();
+
+ if (r.isNull() || final_geometry.isNull())
+ animate = false;
+
+ AnimationMap::const_iterator it = m_animation_map.constFind(widget);
+ if (it == m_animation_map.constEnd()) {
+ if (r == final_geometry) {
+ emit finished(widget);
+ return;
+ }
+ } else {
+ if ((*it).r2 == final_geometry)
+ return;
+ }
+
+ if (animate) {
+ AnimationItem item(widget, r, final_geometry);
+ m_animation_map[widget] = item;
+ if (!m_timer->isActive()) {
+ m_timer->start();
+ m_time->start();
+ }
+ } else {
+ m_animation_map.remove(widget);
+ if (m_animation_map.isEmpty())
+ m_timer->stop();
+
+ if (!final_geometry.isValid() && !widget->isWindow()) {
+ // Make the wigdet go away by sending it to negative space
+ QSize s = widget->size();
+ final_geometry = QRect(-500 - s.width(), -500 - s.height(), s.width(), s.height());
+ }
+ widget->setGeometry(final_geometry);
+
+ emit finished(widget);
+
+ if (m_animation_map.isEmpty())
+ emit finishedAll();
+
+ return;
+ }
+}
+
+void QWidgetAnimator::animationStep()
+{
+ int steps = (1 + m_time->restart())/g_animation_interval;
+ AnimationMap::iterator it = m_animation_map.begin();
+ while (it != m_animation_map.end()) {
+ AnimationItem &item = *it;
+
+ item.step = qMin(item.step + steps, g_animation_steps);
+
+ int x = animateHelper(item.r1.left(), item.r2.left(),
+ item.step, g_animation_steps);
+ int y = animateHelper(item.r1.top(), item.r2.top(),
+ item.step, g_animation_steps);
+ int w = animateHelper(item.r1.width(), item.r2.width(),
+ item.step, g_animation_steps);
+ int h = animateHelper(item.r1.height(), item.r2.height(),
+ item.step, g_animation_steps);
+
+ item.widget->setGeometry(x, y, w, h);
+
+ if (item.step == g_animation_steps) {
+ emit finished(item.widget);
+ AnimationMap::iterator tmp = it;
+ ++it;
+ m_animation_map.erase(tmp);
+ } else {
+ ++it;
+ }
+ }
+
+ if (m_animation_map.isEmpty()) {
+ m_timer->stop();
+ emit finishedAll();
+ }
+}
+
+bool QWidgetAnimator::animating() const
+{
+ return m_timer->isActive();
+}
+
+bool QWidgetAnimator::animating(QWidget *widget)
+{
+ return m_animation_map.contains(widget);
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/widgets/qwidgetanimator_p.h b/src/gui/widgets/qwidgetanimator_p.h
new file mode 100644
index 0000000000..72049836a7
--- /dev/null
+++ b/src/gui/widgets/qwidgetanimator_p.h
@@ -0,0 +1,102 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QWIDGET_ANIMATOR_P_H
+#define QWIDGET_ANIMATOR_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of the QLibrary class. This header file may change from
+// version to version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <qobject.h>
+#include <qrect.h>
+#include <qmap.h>
+
+QT_BEGIN_NAMESPACE
+
+class QWidget;
+class QTimer;
+class QTime;
+
+class QWidgetAnimator : public QObject
+{
+ Q_OBJECT
+public:
+ QWidgetAnimator(QObject *parent = 0);
+ ~QWidgetAnimator();
+ void animate(QWidget *widget, const QRect &final_geometry, bool animate);
+ bool animating() const;
+ bool animating(QWidget *widget);
+
+ void abort(QWidget *widget);
+
+signals:
+ void finished(QWidget *widget);
+ void finishedAll();
+
+private slots:
+ void animationStep();
+
+private:
+ struct AnimationItem {
+ AnimationItem(QWidget *_widget = 0, const QRect &_r1 = QRect(),
+ const QRect &_r2 = QRect())
+ : widget(_widget), r1(_r1), r2(_r2), step(0) {}
+ QWidget *widget;
+ QRect r1, r2;
+ int step;
+ };
+ typedef QMap<QWidget*, AnimationItem> AnimationMap;
+ AnimationMap m_animation_map;
+ QTimer *m_timer;
+ QTime *m_time;
+};
+
+QT_END_NAMESPACE
+
+#endif // QWIDGET_ANIMATOR_P_H
diff --git a/src/gui/widgets/qwidgetresizehandler.cpp b/src/gui/widgets/qwidgetresizehandler.cpp
new file mode 100644
index 0000000000..9171244f41
--- /dev/null
+++ b/src/gui/widgets/qwidgetresizehandler.cpp
@@ -0,0 +1,547 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qwidgetresizehandler_p.h"
+
+#ifndef QT_NO_RESIZEHANDLER
+#include "qframe.h"
+#include "qapplication.h"
+#include "qdesktopwidget.h"
+#include "qcursor.h"
+#include "qsizegrip.h"
+#include "qevent.h"
+#if defined(Q_WS_WIN)
+#include "qt_windows.h"
+#endif
+#include "qdebug.h"
+#include "private/qlayoutengine_p.h"
+
+QT_BEGIN_NAMESPACE
+
+#define RANGE 4
+
+static bool resizeHorizontalDirectionFixed = false;
+static bool resizeVerticalDirectionFixed = false;
+
+QWidgetResizeHandler::QWidgetResizeHandler(QWidget *parent, QWidget *cw)
+ : QObject(parent), widget(parent), childWidget(cw ? cw : parent),
+ fw(0), extrahei(0), buttonDown(false), moveResizeMode(false), sizeprotect(true), movingEnabled(true)
+{
+ mode = Nowhere;
+ widget->setMouseTracking(true);
+ QFrame *frame = qobject_cast<QFrame*>(widget);
+ range = frame ? frame->frameWidth() : RANGE;
+ range = qMax(RANGE, range);
+ activeForMove = activeForResize = true;
+ widget->installEventFilter(this);
+}
+
+void QWidgetResizeHandler::setActive(Action ac, bool b)
+{
+ if (ac & Move)
+ activeForMove = b;
+ if (ac & Resize)
+ activeForResize = b;
+
+ if (!isActive())
+ setMouseCursor(Nowhere);
+}
+
+bool QWidgetResizeHandler::isActive(Action ac) const
+{
+ bool b = false;
+ if (ac & Move) b = activeForMove;
+ if (ac & Resize) b |= activeForResize;
+
+ return b;
+}
+
+bool QWidgetResizeHandler::eventFilter(QObject *o, QEvent *ee)
+{
+ if (!isActive()
+ || (ee->type() != QEvent::MouseButtonPress
+ && ee->type() != QEvent::MouseButtonRelease
+ && ee->type() != QEvent::MouseMove
+ && ee->type() != QEvent::KeyPress
+ && ee->type() != QEvent::ShortcutOverride)
+ )
+ return false;
+
+ Q_ASSERT(o == widget);
+ QWidget *w = widget;
+ if (QApplication::activePopupWidget()) {
+ if (buttonDown && ee->type() == QEvent::MouseButtonRelease)
+ buttonDown = false;
+ return false;
+ }
+
+ QMouseEvent *e = (QMouseEvent*)ee;
+ switch (e->type()) {
+ case QEvent::MouseButtonPress: {
+ if (w->isMaximized())
+ break;
+ if (!widget->rect().contains(widget->mapFromGlobal(e->globalPos())))
+ return false;
+ if (e->button() == Qt::LeftButton) {
+#if defined(Q_WS_X11)
+ /*
+ Implicit grabs do not stop the X server from changing
+ the cursor in children, which looks *really* bad when
+ doing resizingk, so we grab the cursor. Note that we do
+ not do this on Windows since double clicks are lost due
+ to the grab (see change 198463).
+ */
+ if (e->spontaneous())
+# if !defined(QT_NO_CURSOR)
+ widget->grabMouse(widget->cursor());
+# else
+ widget->grabMouse();
+# endif // QT_NO_CURSOR
+#endif // Q_WS_X11
+ buttonDown = false;
+ emit activate();
+ bool me = movingEnabled;
+ movingEnabled = (me && o == widget);
+ mouseMoveEvent(e);
+ movingEnabled = me;
+ buttonDown = true;
+ moveOffset = widget->mapFromGlobal(e->globalPos());
+ invertedMoveOffset = widget->rect().bottomRight() - moveOffset;
+ if (mode == Center) {
+ if (movingEnabled)
+ return true;
+ } else {
+ return true;
+ }
+ }
+ } break;
+ case QEvent::MouseButtonRelease:
+ if (w->isMaximized())
+ break;
+ if (e->button() == Qt::LeftButton) {
+ moveResizeMode = false;
+ buttonDown = false;
+ widget->releaseMouse();
+ widget->releaseKeyboard();
+ if (mode == Center) {
+ if (movingEnabled)
+ return true;
+ } else {
+ return true;
+ }
+ }
+ break;
+ case QEvent::MouseMove: {
+ if (w->isMaximized())
+ break;
+ buttonDown = buttonDown && (e->buttons() & Qt::LeftButton); // safety, state machine broken!
+ bool me = movingEnabled;
+ movingEnabled = (me && o == widget && (buttonDown || moveResizeMode));
+ mouseMoveEvent(e);
+ movingEnabled = me;
+ if (mode == Center) {
+ if (movingEnabled)
+ return true;
+ } else {
+ return true;
+ }
+ } break;
+ case QEvent::KeyPress:
+ keyPressEvent((QKeyEvent*)e);
+ break;
+ case QEvent::ShortcutOverride:
+ if (buttonDown) {
+ ((QKeyEvent*)ee)->accept();
+ return true;
+ }
+ break;
+ default:
+ break;
+ }
+
+ return false;
+}
+
+void QWidgetResizeHandler::mouseMoveEvent(QMouseEvent *e)
+{
+ QPoint pos = widget->mapFromGlobal(e->globalPos());
+ if (!moveResizeMode && !buttonDown) {
+ if (pos.y() <= range && pos.x() <= range)
+ mode = TopLeft;
+ else if (pos.y() >= widget->height()-range && pos.x() >= widget->width()-range)
+ mode = BottomRight;
+ else if (pos.y() >= widget->height()-range && pos.x() <= range)
+ mode = BottomLeft;
+ else if (pos.y() <= range && pos.x() >= widget->width()-range)
+ mode = TopRight;
+ else if (pos.y() <= range)
+ mode = Top;
+ else if (pos.y() >= widget->height()-range)
+ mode = Bottom;
+ else if (pos.x() <= range)
+ mode = Left;
+ else if ( pos.x() >= widget->width()-range)
+ mode = Right;
+ else if (widget->rect().contains(pos))
+ mode = Center;
+ else
+ mode = Nowhere;
+
+ if (widget->isMinimized() || !isActive(Resize))
+ mode = Center;
+#ifndef QT_NO_CURSOR
+ setMouseCursor(mode);
+#endif
+ return;
+ }
+
+ if (mode == Center && !movingEnabled)
+ return;
+
+ if (widget->testAttribute(Qt::WA_WState_ConfigPending))
+ return;
+
+
+ QPoint globalPos = (!widget->isWindow() && widget->parentWidget()) ?
+ widget->parentWidget()->mapFromGlobal(e->globalPos()) : e->globalPos();
+ if (!widget->isWindow() && !widget->parentWidget()->rect().contains(globalPos)) {
+ if (globalPos.x() < 0)
+ globalPos.rx() = 0;
+ if (globalPos.y() < 0)
+ globalPos.ry() = 0;
+ if (sizeprotect && globalPos.x() > widget->parentWidget()->width())
+ globalPos.rx() = widget->parentWidget()->width();
+ if (sizeprotect && globalPos.y() > widget->parentWidget()->height())
+ globalPos.ry() = widget->parentWidget()->height();
+ }
+
+ QPoint p = globalPos + invertedMoveOffset;
+ QPoint pp = globalPos - moveOffset;
+
+#ifdef Q_WS_X11
+ // Workaround for window managers which refuse to move a tool window partially offscreen.
+ QRect desktop = qApp->desktop()->availableGeometry(widget);
+ pp.rx() = qMax(pp.x(), desktop.left());
+ pp.ry() = qMax(pp.y(), desktop.top());
+ p.rx() = qMin(p.x(), desktop.right());
+ p.ry() = qMin(p.y(), desktop.bottom());
+#endif
+
+ QSize ms = qSmartMinSize(childWidget);
+ int mw = ms.width();
+ int mh = ms.height();
+ if (childWidget != widget) {
+ mw += 2 * fw;
+ mh += 2 * fw + extrahei;
+ }
+
+ QSize maxsize(childWidget->maximumSize());
+ if (childWidget != widget)
+ maxsize += QSize(2 * fw, 2 * fw + extrahei);
+ QSize mpsize(widget->geometry().right() - pp.x() + 1,
+ widget->geometry().bottom() - pp.y() + 1);
+ mpsize = mpsize.expandedTo(widget->minimumSize()).expandedTo(QSize(mw, mh))
+ .boundedTo(maxsize);
+ QPoint mp(widget->geometry().right() - mpsize.width() + 1,
+ widget->geometry().bottom() - mpsize.height() + 1);
+
+ QRect geom = widget->geometry();
+
+ switch (mode) {
+ case TopLeft:
+ geom = QRect(mp, widget->geometry().bottomRight()) ;
+ break;
+ case BottomRight:
+ geom = QRect(widget->geometry().topLeft(), p) ;
+ break;
+ case BottomLeft:
+ geom = QRect(QPoint(mp.x(), widget->geometry().y()), QPoint(widget->geometry().right(), p.y())) ;
+ break;
+ case TopRight:
+ geom = QRect(QPoint(widget->geometry().x(), mp.y()), QPoint(p.x(), widget->geometry().bottom())) ;
+ break;
+ case Top:
+ geom = QRect(QPoint(widget->geometry().left(), mp.y()), widget->geometry().bottomRight()) ;
+ break;
+ case Bottom:
+ geom = QRect(widget->geometry().topLeft(), QPoint(widget->geometry().right(), p.y())) ;
+ break;
+ case Left:
+ geom = QRect(QPoint(mp.x(), widget->geometry().top()), widget->geometry().bottomRight()) ;
+ break;
+ case Right:
+ geom = QRect(widget->geometry().topLeft(), QPoint(p.x(), widget->geometry().bottom())) ;
+ break;
+ case Center:
+ geom.moveTopLeft(pp);
+ break;
+ default:
+ break;
+ }
+
+ geom = QRect(geom.topLeft(),
+ geom.size().expandedTo(widget->minimumSize())
+ .expandedTo(QSize(mw, mh))
+ .boundedTo(maxsize));
+
+ if (geom != widget->geometry() &&
+ (widget->isWindow() || widget->parentWidget()->rect().intersects(geom))) {
+ if (mode == Center)
+ widget->move(geom.topLeft());
+ else
+ widget->setGeometry(geom);
+ }
+
+ QApplication::syncX();
+}
+
+void QWidgetResizeHandler::setMouseCursor(MousePosition m)
+{
+#ifdef QT_NO_CURSOR
+ Q_UNUSED(m);
+#else
+ QObjectList children = widget->children();
+ for (int i = 0; i < children.size(); ++i) {
+ if (QWidget *w = qobject_cast<QWidget*>(children.at(i))) {
+ if (!w->testAttribute(Qt::WA_SetCursor) && !w->inherits("QWorkspaceTitleBar")) {
+ w->setCursor(Qt::ArrowCursor);
+ }
+ }
+ }
+
+ switch (m) {
+ case TopLeft:
+ case BottomRight:
+ widget->setCursor(Qt::SizeFDiagCursor);
+ break;
+ case BottomLeft:
+ case TopRight:
+ widget->setCursor(Qt::SizeBDiagCursor);
+ break;
+ case Top:
+ case Bottom:
+ widget->setCursor(Qt::SizeVerCursor);
+ break;
+ case Left:
+ case Right:
+ widget->setCursor(Qt::SizeHorCursor);
+ break;
+ default:
+ widget->setCursor(Qt::ArrowCursor);
+ break;
+ }
+#endif // QT_NO_CURSOR
+}
+
+void QWidgetResizeHandler::keyPressEvent(QKeyEvent * e)
+{
+ if (!isMove() && !isResize())
+ return;
+ bool is_control = e->modifiers() & Qt::ControlModifier;
+ int delta = is_control?1:8;
+ QPoint pos = QCursor::pos();
+ switch (e->key()) {
+ case Qt::Key_Left:
+ pos.rx() -= delta;
+ if (pos.x() <= QApplication::desktop()->geometry().left()) {
+ if (mode == TopLeft || mode == BottomLeft) {
+ moveOffset.rx() += delta;
+ invertedMoveOffset.rx() += delta;
+ } else {
+ moveOffset.rx() -= delta;
+ invertedMoveOffset.rx() -= delta;
+ }
+ }
+ if (isResize() && !resizeHorizontalDirectionFixed) {
+ resizeHorizontalDirectionFixed = true;
+ if (mode == BottomRight)
+ mode = BottomLeft;
+ else if (mode == TopRight)
+ mode = TopLeft;
+#ifndef QT_NO_CURSOR
+ setMouseCursor(mode);
+ widget->grabMouse(widget->cursor());
+#else
+ widget->grabMouse();
+#endif
+ }
+ break;
+ case Qt::Key_Right:
+ pos.rx() += delta;
+ if (pos.x() >= QApplication::desktop()->geometry().right()) {
+ if (mode == TopRight || mode == BottomRight) {
+ moveOffset.rx() += delta;
+ invertedMoveOffset.rx() += delta;
+ } else {
+ moveOffset.rx() -= delta;
+ invertedMoveOffset.rx() -= delta;
+ }
+ }
+ if (isResize() && !resizeHorizontalDirectionFixed) {
+ resizeHorizontalDirectionFixed = true;
+ if (mode == BottomLeft)
+ mode = BottomRight;
+ else if (mode == TopLeft)
+ mode = TopRight;
+#ifndef QT_NO_CURSOR
+ setMouseCursor(mode);
+ widget->grabMouse(widget->cursor());
+#else
+ widget->grabMouse();
+#endif
+ }
+ break;
+ case Qt::Key_Up:
+ pos.ry() -= delta;
+ if (pos.y() <= QApplication::desktop()->geometry().top()) {
+ if (mode == TopLeft || mode == TopRight) {
+ moveOffset.ry() += delta;
+ invertedMoveOffset.ry() += delta;
+ } else {
+ moveOffset.ry() -= delta;
+ invertedMoveOffset.ry() -= delta;
+ }
+ }
+ if (isResize() && !resizeVerticalDirectionFixed) {
+ resizeVerticalDirectionFixed = true;
+ if (mode == BottomLeft)
+ mode = TopLeft;
+ else if (mode == BottomRight)
+ mode = TopRight;
+#ifndef QT_NO_CURSOR
+ setMouseCursor(mode);
+ widget->grabMouse(widget->cursor());
+#else
+ widget->grabMouse();
+#endif
+ }
+ break;
+ case Qt::Key_Down:
+ pos.ry() += delta;
+ if (pos.y() >= QApplication::desktop()->geometry().bottom()) {
+ if (mode == BottomLeft || mode == BottomRight) {
+ moveOffset.ry() += delta;
+ invertedMoveOffset.ry() += delta;
+ } else {
+ moveOffset.ry() -= delta;
+ invertedMoveOffset.ry() -= delta;
+ }
+ }
+ if (isResize() && !resizeVerticalDirectionFixed) {
+ resizeVerticalDirectionFixed = true;
+ if (mode == TopLeft)
+ mode = BottomLeft;
+ else if (mode == TopRight)
+ mode = BottomRight;
+#ifndef QT_NO_CURSOR
+ setMouseCursor(mode);
+ widget->grabMouse(widget->cursor());
+#else
+ widget->grabMouse();
+#endif
+ }
+ break;
+ case Qt::Key_Space:
+ case Qt::Key_Return:
+ case Qt::Key_Enter:
+ case Qt::Key_Escape:
+ moveResizeMode = false;
+ widget->releaseMouse();
+ widget->releaseKeyboard();
+ buttonDown = false;
+ break;
+ default:
+ return;
+ }
+ QCursor::setPos(pos);
+}
+
+
+void QWidgetResizeHandler::doResize()
+{
+ if (!activeForResize)
+ return;
+
+ moveResizeMode = true;
+ moveOffset = widget->mapFromGlobal(QCursor::pos());
+ if (moveOffset.x() < widget->width()/2) {
+ if (moveOffset.y() < widget->height()/2)
+ mode = TopLeft;
+ else
+ mode = BottomLeft;
+ } else {
+ if (moveOffset.y() < widget->height()/2)
+ mode = TopRight;
+ else
+ mode = BottomRight;
+ }
+ invertedMoveOffset = widget->rect().bottomRight() - moveOffset;
+#ifndef QT_NO_CURSOR
+ setMouseCursor(mode);
+ widget->grabMouse(widget->cursor() );
+#else
+ widget->grabMouse();
+#endif
+ widget->grabKeyboard();
+ resizeHorizontalDirectionFixed = false;
+ resizeVerticalDirectionFixed = false;
+}
+
+void QWidgetResizeHandler::doMove()
+{
+ if (!activeForMove)
+ return;
+
+ mode = Center;
+ moveResizeMode = true;
+ moveOffset = widget->mapFromGlobal(QCursor::pos());
+ invertedMoveOffset = widget->rect().bottomRight() - moveOffset;
+#ifndef QT_NO_CURSOR
+ widget->grabMouse(Qt::SizeAllCursor);
+#else
+ widget->grabMouse();
+#endif
+ widget->grabKeyboard();
+}
+
+QT_END_NAMESPACE
+
+#endif //QT_NO_RESIZEHANDLER
diff --git a/src/gui/widgets/qwidgetresizehandler_p.h b/src/gui/widgets/qwidgetresizehandler_p.h
new file mode 100644
index 0000000000..1eab292f95
--- /dev/null
+++ b/src/gui/widgets/qwidgetresizehandler_p.h
@@ -0,0 +1,141 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QWIDGETRESIZEHANDLER_P_H
+#define QWIDGETRESIZEHANDLER_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. This header file may
+// change from version to version without notice, or even be
+// removed.
+//
+// We mean it.
+//
+
+#include "QtCore/qobject.h"
+#include "QtCore/qpoint.h"
+
+#ifndef QT_NO_RESIZEHANDLER
+
+QT_BEGIN_NAMESPACE
+
+class QMouseEvent;
+class QKeyEvent;
+
+class Q_GUI_EXPORT QWidgetResizeHandler : public QObject
+{
+ Q_OBJECT
+
+public:
+ enum Action {
+ Move = 0x01,
+ Resize = 0x02,
+ Any = Move|Resize
+ };
+
+ explicit QWidgetResizeHandler(QWidget *parent, QWidget *cw = 0);
+ void setActive(bool b) { setActive(Any, b); }
+ void setActive(Action ac, bool b);
+ bool isActive() const { return isActive(Any); }
+ bool isActive(Action ac) const;
+ void setMovingEnabled(bool b) { movingEnabled = b; }
+ bool isMovingEnabled() const { return movingEnabled; }
+
+ bool isButtonDown() const { return buttonDown; }
+
+ void setExtraHeight(int h) { extrahei = h; }
+ void setSizeProtection(bool b) { sizeprotect = b; }
+
+ void setFrameWidth(int w) { fw = w; }
+
+ void doResize();
+ void doMove();
+
+Q_SIGNALS:
+ void activate();
+
+protected:
+ bool eventFilter(QObject *o, QEvent *e);
+ void mouseMoveEvent(QMouseEvent *e);
+ void keyPressEvent(QKeyEvent *e);
+
+private:
+ Q_DISABLE_COPY(QWidgetResizeHandler)
+
+ enum MousePosition {
+ Nowhere,
+ TopLeft, BottomRight, BottomLeft, TopRight,
+ Top, Bottom, Left, Right,
+ Center
+ };
+
+ QWidget *widget;
+ QWidget *childWidget;
+ QPoint moveOffset;
+ QPoint invertedMoveOffset;
+ MousePosition mode;
+ int fw;
+ int extrahei;
+ int range;
+ uint buttonDown :1;
+ uint moveResizeMode :1;
+ uint activeForResize :1;
+ uint sizeprotect :1;
+ uint movingEnabled :1;
+ uint activeForMove :1;
+
+ void setMouseCursor(MousePosition m);
+ bool isMove() const {
+ return moveResizeMode && mode == Center;
+ }
+ bool isResize() const {
+ return moveResizeMode && !isMove();
+ }
+};
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_RESIZEHANDLER
+
+#endif // QWIDGETRESIZEHANDLER_P_H
diff --git a/src/gui/widgets/qworkspace.cpp b/src/gui/widgets/qworkspace.cpp
new file mode 100644
index 0000000000..5221deb045
--- /dev/null
+++ b/src/gui/widgets/qworkspace.cpp
@@ -0,0 +1,3382 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qworkspace.h"
+#ifndef QT_NO_WORKSPACE
+#include "qapplication.h"
+#include "qbitmap.h"
+#include "qcursor.h"
+#include "qdatetime.h"
+#include "qdesktopwidget.h"
+#include "qevent.h"
+#include "qhash.h"
+#include "qicon.h"
+#include "qimage.h"
+#include "qlabel.h"
+#include "qlayout.h"
+#include "qmenubar.h"
+#include "qmenu.h"
+#include "qpainter.h"
+#include "qpointer.h"
+#include "qscrollbar.h"
+#include "qstyle.h"
+#include "qstyleoption.h"
+#include "qtooltip.h"
+#include "qdebug.h"
+#include <private/qwidget_p.h>
+#include <private/qwidgetresizehandler_p.h>
+#include <private/qlayoutengine_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QWorkspaceTitleBarPrivate;
+
+
+/**************************************************************
+* QMDIControl
+*
+* Used for displaying MDI controls in a maximized MDI window
+*
+*/
+class QMDIControl : public QWidget
+{
+ Q_OBJECT
+signals:
+ void _q_minimize();
+ void _q_restore();
+ void _q_close();
+
+public:
+ QMDIControl(QWidget *widget);
+
+private:
+ QSize sizeHint() const;
+ void paintEvent(QPaintEvent *event);
+ void mousePressEvent(QMouseEvent *event);
+ void mouseReleaseEvent(QMouseEvent *event);
+ void mouseMoveEvent(QMouseEvent *event);
+ void leaveEvent(QEvent *event);
+ bool event(QEvent *event);
+ void initStyleOption(QStyleOptionComplex *option) const;
+ QStyle::SubControl activeControl; //control locked by pressing and holding the mouse
+ QStyle::SubControl hoverControl; //previously active hover control, used for tracking repaints
+};
+
+bool QMDIControl::event(QEvent *event)
+{
+ if (event->type() == QEvent::ToolTip) {
+ QStyleOptionComplex opt;
+ initStyleOption(&opt);
+#ifndef QT_NO_TOOLTIP
+ QHelpEvent *helpEvent = static_cast<QHelpEvent *>(event);
+ QStyle::SubControl ctrl = style()->hitTestComplexControl(QStyle::CC_MdiControls, &opt,
+ helpEvent->pos(), this);
+ if (ctrl == QStyle::SC_MdiCloseButton)
+ QToolTip::showText(helpEvent->globalPos(), QWorkspace::tr("Close"));
+ else if (ctrl == QStyle::SC_MdiMinButton)
+ QToolTip::showText(helpEvent->globalPos(), QWorkspace::tr("Minimize"));
+ else if (ctrl == QStyle::SC_MdiNormalButton)
+ QToolTip::showText(helpEvent->globalPos(), QWorkspace::tr("Restore Down"));
+ else
+ QToolTip::hideText();
+#endif // QT_NO_TOOLTIP
+ }
+ return QWidget::event(event);
+}
+
+void QMDIControl::initStyleOption(QStyleOptionComplex *option) const
+{
+ option->initFrom(this);
+ option->subControls = QStyle::SC_All;
+ option->activeSubControls = QStyle::SC_None;
+}
+
+QMDIControl::QMDIControl(QWidget *widget)
+ : QWidget(widget), activeControl(QStyle::SC_None),
+ hoverControl(QStyle::SC_None)
+{
+ setObjectName(QLatin1String("qt_maxcontrols"));
+ setFocusPolicy(Qt::NoFocus);
+ setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
+ setMouseTracking(true);
+}
+
+QSize QMDIControl::sizeHint() const
+{
+ ensurePolished();
+ QStyleOptionComplex opt;
+ initStyleOption(&opt);
+ QSize size(48, 16);
+ return style()->sizeFromContents(QStyle::CT_MdiControls, &opt, size, this);
+}
+
+void QMDIControl::mousePressEvent(QMouseEvent *event)
+{
+ if (event->button() != Qt::LeftButton) {
+ event->ignore();
+ return;
+ }
+ QStyleOptionComplex opt;
+ initStyleOption(&opt);
+ QStyle::SubControl ctrl = style()->hitTestComplexControl(QStyle::CC_MdiControls, &opt,
+ event->pos(), this);
+ activeControl = ctrl;
+ update();
+}
+
+void QMDIControl::mouseReleaseEvent(QMouseEvent *event)
+{
+ if (event->button() != Qt::LeftButton) {
+ event->ignore();
+ return;
+ }
+ QStyleOptionTitleBar opt;
+ initStyleOption(&opt);
+ QStyle::SubControl under_mouse = style()->hitTestComplexControl(QStyle::CC_MdiControls, &opt,
+ event->pos(), this);
+ if (under_mouse == activeControl) {
+ switch (activeControl) {
+ case QStyle::SC_MdiCloseButton:
+ emit _q_close();
+ break;
+ case QStyle::SC_MdiNormalButton:
+ emit _q_restore();
+ break;
+ case QStyle::SC_MdiMinButton:
+ emit _q_minimize();
+ break;
+ default:
+ break;
+ }
+ }
+ activeControl = QStyle::SC_None;
+ update();
+}
+
+void QMDIControl::leaveEvent(QEvent * /*event*/)
+{
+ hoverControl = QStyle::SC_None;
+ update();
+}
+
+void QMDIControl::mouseMoveEvent(QMouseEvent *event)
+{
+ QStyleOptionTitleBar opt;
+ initStyleOption(&opt);
+ QStyle::SubControl under_mouse = style()->hitTestComplexControl(QStyle::CC_MdiControls, &opt,
+ event->pos(), this);
+ //test if hover state changes
+ if (hoverControl != under_mouse) {
+ hoverControl = under_mouse;
+ update();
+ }
+}
+
+void QMDIControl::paintEvent(QPaintEvent *)
+{
+ QPainter p(this);
+ QStyleOptionComplex opt;
+ initStyleOption(&opt);
+ if (activeControl == hoverControl) {
+ opt.activeSubControls = activeControl;
+ opt.state |= QStyle::State_Sunken;
+ } else if (hoverControl != QStyle::SC_None && (activeControl == QStyle::SC_None)) {
+ opt.activeSubControls = hoverControl;
+ opt.state |= QStyle::State_MouseOver;
+ }
+ style()->drawComplexControl(QStyle::CC_MdiControls, &opt, &p, this);
+}
+
+class QWorkspaceTitleBar : public QWidget
+{
+ Q_OBJECT
+ Q_DECLARE_PRIVATE(QWorkspaceTitleBar)
+ Q_PROPERTY(bool autoRaise READ autoRaise WRITE setAutoRaise)
+ Q_PROPERTY(bool movable READ isMovable WRITE setMovable)
+
+public:
+ QWorkspaceTitleBar (QWidget *w, QWidget *parent, Qt::WindowFlags f = 0);
+ ~QWorkspaceTitleBar();
+
+ 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;
+ void initStyleOption(QStyleOptionTitleBar *option) const;
+
+public slots:
+ void setActive(bool);
+
+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 *);
+#ifndef QT_NO_CONTEXTMENU
+ void contextMenuEvent(QContextMenuEvent *);
+#endif
+ void mousePressEvent(QMouseEvent *);
+ void mouseDoubleClickEvent(QMouseEvent *);
+ void mouseReleaseEvent(QMouseEvent *);
+ void mouseMoveEvent(QMouseEvent *);
+ void enterEvent(QEvent *e);
+ void leaveEvent(QEvent *e);
+ void paintEvent(QPaintEvent *p);
+
+private:
+ Q_DISABLE_COPY(QWorkspaceTitleBar)
+};
+
+
+class QWorkspaceTitleBarPrivate : public QWidgetPrivate
+{
+ Q_DECLARE_PUBLIC(QWorkspaceTitleBar)
+public:
+ QWorkspaceTitleBarPrivate()
+ :
+ lastControl(QStyle::SC_None),
+#ifndef QT_NO_TOOLTIP
+ toolTip(0),
+#endif
+ act(0), window(0), movable(1), pressed(0), autoraise(0), moving(0)
+ {
+ }
+
+ Qt::WindowFlags flags;
+ QStyle::SubControl buttonDown;
+ QStyle::SubControl lastControl;
+ QPoint moveOffset;
+#ifndef QT_NO_TOOLTIP
+ QToolTip *toolTip;
+#endif
+ bool act :1;
+ QPointer<QWidget> window;
+ bool movable :1;
+ bool pressed :1;
+ bool autoraise :1;
+ bool moving : 1;
+
+ int titleBarState() const;
+ void readColors();
+};
+
+inline int QWorkspaceTitleBarPrivate::titleBarState() const
+{
+ Q_Q(const QWorkspaceTitleBar);
+ uint state = window ? window->windowState() : static_cast<Qt::WindowStates>(Qt::WindowNoState);
+ state |= uint((act && q->isActiveWindow()) ? QStyle::State_Active : QStyle::State_None);
+ return (int)state;
+}
+
+void QWorkspaceTitleBar::initStyleOption(QStyleOptionTitleBar *option) const
+{
+ Q_D(const QWorkspaceTitleBar);
+ option->initFrom(this);
+ //################
+ if (d->window && (d->flags & Qt::WindowTitleHint)) {
+ option->text = d->window->windowTitle();
+ QIcon icon = d->window->windowIcon();
+ QSize s = icon.actualSize(QSize(64, 64));
+ option->icon = icon.pixmap(s);
+ }
+ option->subControls = QStyle::SC_All;
+ option->activeSubControls = QStyle::SC_None;
+ option->titleBarState = d->titleBarState();
+ option->titleBarFlags = d->flags;
+ option->state &= ~QStyle::State_MouseOver;
+}
+
+QWorkspaceTitleBar::QWorkspaceTitleBar(QWidget *w, QWidget *parent, Qt::WindowFlags f)
+ : QWidget(*new QWorkspaceTitleBarPrivate, parent, Qt::FramelessWindowHint)
+{
+ Q_D(QWorkspaceTitleBar);
+ 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->maximumSize() != QSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX))
+ 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));
+}
+
+QWorkspaceTitleBar::~QWorkspaceTitleBar()
+{
+}
+
+
+#ifdef Q_WS_WIN
+static inline QRgb colorref2qrgb(COLORREF col)
+{
+ return qRgb(GetRValue(col),GetGValue(col),GetBValue(col));
+}
+#endif
+
+void QWorkspaceTitleBarPrivate::readColors()
+{
+ Q_Q(QWorkspaceTitleBar);
+ QPalette pal = q->palette();
+
+ bool colorsInitialized = false;
+
+#ifdef Q_WS_WIN // ask system properties on windows
+#ifndef SPI_GETGRADIENTCAPTIONS
+#define SPI_GETGRADIENTCAPTIONS 0x1008
+#endif
+#ifndef COLOR_GRADIENTACTIVECAPTION
+#define COLOR_GRADIENTACTIVECAPTION 27
+#endif
+#ifndef COLOR_GRADIENTINACTIVECAPTION
+#define COLOR_GRADIENTINACTIVECAPTION 28
+#endif
+ 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)));
+ if (QSysInfo::WindowsVersion != QSysInfo::WV_95 && QSysInfo::WindowsVersion != QSysInfo::WV_NT) {
+ colorsInitialized = true;
+ BOOL gradient;
+ QT_WA({
+ SystemParametersInfo(SPI_GETGRADIENTCAPTIONS, 0, &gradient, 0);
+ } , {
+ SystemParametersInfoA(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 QWorkspaceTitleBar::mousePressEvent(QMouseEvent *e)
+{
+ Q_D(QWorkspaceTitleBar);
+ if (!d->act)
+ emit doActivate();
+ if (e->button() == Qt::LeftButton) {
+ if (style()->styleHint(QStyle::SH_TitleBar_NoBorder, 0, 0)
+ && !rect().adjusted(5, 5, -5, 0).contains(e->pos())) {
+ // propagate border events to the QWidgetResizeHandler
+ e->ignore();
+ return;
+ }
+
+ d->pressed = true;
+ QStyleOptionTitleBar opt;
+ initStyleOption(&opt);
+ 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 QWorkspaceTitleBar *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:
+ 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;
+ }
+ update();
+ } else {
+ d->pressed = false;
+ }
+}
+
+#ifndef QT_NO_CONTEXTMENU
+void QWorkspaceTitleBar::contextMenuEvent(QContextMenuEvent *e)
+{
+ QStyleOptionTitleBar opt;
+ initStyleOption(&opt);
+ 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();
+ }
+}
+#endif // QT_NO_CONTEXTMENU
+
+void QWorkspaceTitleBar::mouseReleaseEvent(QMouseEvent *e)
+{
+ Q_D(QWorkspaceTitleBar);
+ if (!d->window) {
+ // could have been deleted as part of a double click event on the sysmenu
+ return;
+ }
+ if (e->button() == Qt::LeftButton && d->pressed) {
+ if (style()->styleHint(QStyle::SH_TitleBar_NoBorder, 0, 0)
+ && !rect().adjusted(5, 5, -5, 0).contains(e->pos())) {
+ // propagate border events to the QWidgetResizeHandler
+ e->ignore();
+ d->buttonDown = QStyle::SC_None;
+ d->pressed = false;
+ return;
+ }
+ e->accept();
+ QStyleOptionTitleBar opt;
+ initStyleOption(&opt);
+ QStyle::SubControl ctrl = style()->hitTestComplexControl(QStyle::CC_TitleBar, &opt,
+ e->pos(), this);
+
+ if (d->pressed) {
+ update();
+ d->pressed = false;
+ d->moving = false;
+ }
+ if (ctrl == d->buttonDown) {
+ d->buttonDown = QStyle::SC_None;
+ 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::WindowMinMaxButtonsHint)
+ 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;
+ emit doClose();
+ return;
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+ } else {
+ e->ignore();
+ }
+}
+
+void QWorkspaceTitleBar::mouseMoveEvent(QMouseEvent *e)
+{
+ Q_D(QWorkspaceTitleBar);
+ e->ignore();
+ if ((e->buttons() & Qt::LeftButton) && style()->styleHint(QStyle::SH_TitleBar_NoBorder, 0, 0)
+ && !rect().adjusted(5, 5, -5, 0).contains(e->pos()) && !d->pressed) {
+ // propagate border events to the QWidgetResizeHandler
+ return;
+ }
+
+ QStyleOptionTitleBar opt;
+ initStyleOption(&opt);
+ QStyle::SubControl under_mouse = style()->hitTestComplexControl(QStyle::CC_TitleBar, &opt,
+ e->pos(), this);
+ if(under_mouse != d->lastControl) {
+ d->lastControl = under_mouse;
+ update();
+ }
+
+ switch (d->buttonDown) {
+ case QStyle::SC_None:
+ break;
+ case QStyle::SC_TitleBarSysMenu:
+ break;
+ case QStyle::SC_TitleBarLabel:
+ if (d->buttonDown == QStyle::SC_TitleBarLabel && d->movable && d->pressed) {
+ if (d->moving || (d->moveOffset - mapToParent(e->pos())).manhattanLength() >= 4) {
+ d->moving = true;
+ QPoint p = mapFromGlobal(e->globalPos());
+
+ QWidget *parent = d->window ? d->window->parentWidget() : 0;
+ if(parent && parent->inherits("QWorkspaceChild")) {
+ 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);
+ }
+ }
+ e->accept();
+ break;
+ default:
+ break;
+ }
+}
+
+bool QWorkspaceTitleBar::isTool() const
+{
+ Q_D(const QWorkspaceTitleBar);
+ return (d->flags & Qt::WindowType_Mask) == Qt::Tool;
+}
+
+// from qwidget.cpp
+extern QString qt_setWindowTitle_helperHelper(const QString &, const QWidget*);
+
+void QWorkspaceTitleBar::paintEvent(QPaintEvent *)
+{
+ Q_D(QWorkspaceTitleBar);
+ QStyleOptionTitleBar opt;
+ initStyleOption(&opt);
+ opt.subControls = QStyle::SC_TitleBarLabel;
+ opt.activeSubControls = d->buttonDown;
+
+ if (d->window && (d->flags & Qt::WindowTitleHint)) {
+ QString title = qt_setWindowTitle_helperHelper(opt.text, d->window);
+ int maxw = style()->subControlRect(QStyle::CC_TitleBar, &opt, QStyle::SC_TitleBarLabel,
+ this).width();
+ opt.text = fontMetrics().elidedText(title, Qt::ElideRight, maxw);
+ }
+
+ 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;
+ under_mouse = style()->hitTestComplexControl(QStyle::CC_TitleBar, &opt,
+ mapFromGlobal(QCursor::pos()), this);
+ if ((d->buttonDown == under_mouse) && d->pressed) {
+ opt.state |= QStyle::State_Sunken;
+ } else if( autoRaise() && under_mouse != QStyle::SC_None && !d->pressed) {
+ opt.activeSubControls = under_mouse;
+ opt.state |= QStyle::State_MouseOver;
+ }
+ opt.palette.setCurrentColorGroup(usesActiveColor() ? QPalette::Active : QPalette::Inactive);
+
+ QPainter p(this);
+ style()->drawComplexControl(QStyle::CC_TitleBar, &opt, &p, this);
+}
+
+void QWorkspaceTitleBar::mouseDoubleClickEvent(QMouseEvent *e)
+{
+ Q_D(QWorkspaceTitleBar);
+ if (e->button() != Qt::LeftButton) {
+ e->ignore();
+ return;
+ }
+ e->accept();
+ QStyleOptionTitleBar opt;
+ initStyleOption(&opt);
+ 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::WindowSystemMenuHint)
+ emit doClose();
+ break;
+
+ default:
+ break;
+ }
+}
+
+void QWorkspaceTitleBar::leaveEvent(QEvent *)
+{
+ Q_D(QWorkspaceTitleBar);
+ d->lastControl = QStyle::SC_None;
+ if(autoRaise() && !d->pressed)
+ update();
+}
+
+void QWorkspaceTitleBar::enterEvent(QEvent *)
+{
+ Q_D(QWorkspaceTitleBar);
+ if(autoRaise() && !d->pressed)
+ update();
+ QEvent e(QEvent::Leave);
+ QApplication::sendEvent(parentWidget(), &e);
+}
+
+void QWorkspaceTitleBar::setActive(bool active)
+{
+ Q_D(QWorkspaceTitleBar);
+ if (d->act == active)
+ return ;
+
+ d->act = active;
+ update();
+}
+
+bool QWorkspaceTitleBar::isActive() const
+{
+ Q_D(const QWorkspaceTitleBar);
+ return d->act;
+}
+
+bool QWorkspaceTitleBar::usesActiveColor() const
+{
+ return (isActive() && isActiveWindow()) ||
+ (!window() && QWidget::window()->isActiveWindow());
+}
+
+QWidget *QWorkspaceTitleBar::window() const
+{
+ Q_D(const QWorkspaceTitleBar);
+ return d->window;
+}
+
+bool QWorkspaceTitleBar::event(QEvent *e)
+{
+ Q_D(QWorkspaceTitleBar);
+ if (e->type() == QEvent::ApplicationPaletteChange) {
+ d->readColors();
+ } else if (e->type() == QEvent::WindowActivate
+ || e->type() == QEvent::WindowDeactivate) {
+ if (d->act)
+ update();
+ }
+ return QWidget::event(e);
+}
+
+void QWorkspaceTitleBar::setMovable(bool b)
+{
+ Q_D(QWorkspaceTitleBar);
+ d->movable = b;
+}
+
+bool QWorkspaceTitleBar::isMovable() const
+{
+ Q_D(const QWorkspaceTitleBar);
+ return d->movable;
+}
+
+void QWorkspaceTitleBar::setAutoRaise(bool b)
+{
+ Q_D(QWorkspaceTitleBar);
+ d->autoraise = b;
+}
+
+bool QWorkspaceTitleBar::autoRaise() const
+{
+ Q_D(const QWorkspaceTitleBar);
+ return d->autoraise;
+}
+
+QSize QWorkspaceTitleBar::sizeHint() const
+{
+ ensurePolished();
+ QStyleOptionTitleBar opt;
+ initStyleOption(&opt);
+ QRect menur = style()->subControlRect(QStyle::CC_TitleBar, &opt,
+ QStyle::SC_TitleBarSysMenu, this);
+ return QSize(menur.width(), style()->pixelMetric(QStyle::PM_TitleBarHeight, &opt, this));
+}
+
+/*!
+ \class QWorkspace
+ \obsolete
+ \brief The QWorkspace widget provides a workspace window that can be
+ used in an MDI application.
+ \ingroup application
+
+ This class is deprecated. Use QMdiArea instead.
+
+ Multiple Document Interface (MDI) applications are typically
+ composed of a main window containing a menu bar, a toolbar, and
+ a central QWorkspace widget. The workspace itself is used to display
+ a number of child windows, each of which is a widget.
+
+ The workspace itself is an ordinary Qt widget. It has a standard
+ constructor that takes a parent widget.
+ Workspaces can be placed in any layout, but are typically given
+ as the central widget in a QMainWindow:
+
+ \snippet doc/src/snippets/code/src_gui_widgets_qworkspace.cpp 0
+
+ Child windows (MDI windows) are standard Qt widgets that are
+ inserted into the workspace with addWindow(). As with top-level
+ widgets, you can call functions such as show(), hide(),
+ showMaximized(), and setWindowTitle() on a child window to change
+ its appearance within the workspace. You can also provide widget
+ flags to determine the layout of the decoration or the behavior of
+ the widget itself.
+
+ To change or retrieve the geometry of a child window, you must
+ operate on its parentWidget(). The parentWidget() provides
+ access to the decorated frame that contains the child window
+ widget. When a child window is maximised, its decorated frame
+ is hidden. If the top-level widget contains a menu bar, it will display
+ the maximised window's operations menu to the left of the menu
+ entries, and the window's controls to the right.
+
+ A child window becomes active when it gets the keyboard focus,
+ or when setFocus() is called. The user can activate a window by moving
+ focus in the usual ways, for example by clicking a window or by pressing
+ Tab. The workspace emits a signal windowActivated() when the active
+ window changes, and the function activeWindow() returns a pointer to the
+ active child window, or 0 if no window is active.
+
+ The convenience function windowList() returns a list of all child
+ windows. This information could be used in a popup menu
+ containing a list of windows, for example. This feature is also
+ available as part of the \l{Window Menu} Solution.
+
+ QWorkspace provides two built-in layout strategies for child
+ windows: cascade() and tile(). Both are slots so you can easily
+ connect menu entries to them.
+
+ \table
+ \row \o \inlineimage mdi-cascade.png
+ \o \inlineimage mdi-tile.png
+ \endtable
+
+ If you want your users to be able to work with child windows
+ larger than the visible workspace area, set the scrollBarsEnabled
+ property to true.
+
+ \sa QDockWidget, {MDI Example}
+*/
+
+
+class QWorkspaceChild : public QWidget
+{
+ Q_OBJECT
+
+ friend class QWorkspacePrivate;
+ friend class QWorkspace;
+ friend class QWorkspaceTitleBar;
+
+public:
+ QWorkspaceChild(QWidget* window, QWorkspace* parent=0, Qt::WindowFlags flags = 0);
+ ~QWorkspaceChild();
+
+ void setActive(bool);
+ bool isActive() const;
+
+ void adjustToFullscreen();
+
+ QWidget* windowWidget() const;
+ QWidget* iconWidget() const;
+
+ void doResize();
+ void doMove();
+
+ QSize sizeHint() const;
+ QSize minimumSizeHint() const;
+
+ QSize baseSize() const;
+
+ int frameWidth() const;
+
+ void show();
+
+ bool isWindowOrIconVisible() const;
+
+signals:
+ void showOperationMenu();
+ void popupOperationMenu(const QPoint&);
+
+public slots:
+ void activate();
+ void showMinimized();
+ void showMaximized();
+ void showNormal();
+ void showShaded();
+ void internalRaise();
+ void titleBarDoubleClicked();
+
+protected:
+ void enterEvent(QEvent *);
+ void leaveEvent(QEvent *);
+ void childEvent(QChildEvent*);
+ void resizeEvent(QResizeEvent *);
+ void moveEvent(QMoveEvent *);
+ bool eventFilter(QObject *, QEvent *);
+
+ void paintEvent(QPaintEvent *);
+ void changeEvent(QEvent *);
+
+private:
+ void updateMask();
+
+ Q_DISABLE_COPY(QWorkspaceChild)
+
+ QWidget *childWidget;
+ QWidgetResizeHandler *widgetResizeHandler;
+ QWorkspaceTitleBar *titlebar;
+ QPointer<QWorkspaceTitleBar> iconw;
+ QSize windowSize;
+ QSize shadeRestore;
+ QSize shadeRestoreMin;
+ bool act :1;
+ bool shademode :1;
+};
+
+int QWorkspaceChild::frameWidth() const
+{
+ return contentsRect().left();
+}
+
+
+
+class QWorkspacePrivate : public QWidgetPrivate {
+ Q_DECLARE_PUBLIC(QWorkspace)
+public:
+ QWorkspaceChild* active;
+ QList<QWorkspaceChild *> windows;
+ QList<QWorkspaceChild *> focus;
+ QList<QWidget *> icons;
+ QWorkspaceChild* maxWindow;
+ QRect maxRestore;
+ QPointer<QMDIControl> maxcontrols;
+ QPointer<QMenuBar> maxmenubar;
+ QHash<int, const char*> shortcutMap;
+
+ int px;
+ int py;
+ QWidget *becomeActive;
+ QPointer<QLabel> maxtools;
+ QString topTitle;
+
+ QMenu *popup, *toolPopup;
+ enum WSActs { RestoreAct, MoveAct, ResizeAct, MinimizeAct, MaximizeAct, CloseAct, StaysOnTopAct, ShadeAct, NCountAct };
+ QAction *actions[NCountAct];
+
+ QScrollBar *vbar, *hbar;
+ QWidget *corner;
+ int yoffset, xoffset;
+ QBrush background;
+
+ void init();
+ void insertIcon(QWidget* w);
+ void removeIcon(QWidget* w);
+ void place(QWidget*);
+
+ QWorkspaceChild* findChild(QWidget* w);
+ void showMaximizeControls();
+ void hideMaximizeControls();
+ void activateWindow(QWidget* w, bool change_focus = true);
+ void hideChild(QWorkspaceChild *c);
+ void showWindow(QWidget* w);
+ void maximizeWindow(QWidget* w);
+ void minimizeWindow(QWidget* w);
+ void normalizeWindow(QWidget* w);
+
+ QRect updateWorkspace();
+
+private:
+ void _q_normalizeActiveWindow();
+ void _q_minimizeActiveWindow();
+ void _q_showOperationMenu();
+ void _q_popupOperationMenu(const QPoint&);
+ void _q_operationMenuActivated(QAction *);
+ void _q_scrollBarChanged();
+ void _q_updateActions();
+ bool inTitleChange;
+};
+
+static bool isChildOf(QWidget * child, QWidget * parent)
+{
+ if (!parent || !child)
+ return false;
+ QWidget * w = child;
+ while(w && w != parent)
+ w = w->parentWidget();
+ return w != 0;
+}
+
+/*!
+ Constructs a workspace with the given \a parent.
+*/
+QWorkspace::QWorkspace(QWidget *parent)
+ : QWidget(*new QWorkspacePrivate, parent, 0)
+{
+ Q_D(QWorkspace);
+ d->init();
+}
+
+#ifdef QT3_SUPPORT
+/*!
+ Use one of the constructors that doesn't take the \a name
+ argument and then use setObjectName() instead.
+*/
+QWorkspace::QWorkspace(QWidget *parent, const char *name)
+ : QWidget(*new QWorkspacePrivate, parent, 0)
+{
+ Q_D(QWorkspace);
+ setObjectName(QString::fromAscii(name));
+ d->init();
+}
+#endif // QT3_SUPPORT
+
+/*!
+ \internal
+*/
+void
+QWorkspacePrivate::init()
+{
+ Q_Q(QWorkspace);
+
+ maxcontrols = 0;
+ active = 0;
+ maxWindow = 0;
+ maxtools = 0;
+ px = 0;
+ py = 0;
+ becomeActive = 0;
+ popup = new QMenu(q);
+ toolPopup = new QMenu(q);
+ popup->setObjectName(QLatin1String("qt_internal_mdi_popup"));
+ toolPopup->setObjectName(QLatin1String("qt_internal_mdi_tool_popup"));
+
+ actions[QWorkspacePrivate::RestoreAct] = new QAction(QIcon(q->style()->standardPixmap(QStyle::SP_TitleBarNormalButton, 0, q)),
+ QWorkspace::tr("&Restore"), q);
+ actions[QWorkspacePrivate::MoveAct] = new QAction(QWorkspace::tr("&Move"), q);
+ actions[QWorkspacePrivate::ResizeAct] = new QAction(QWorkspace::tr("&Size"), q);
+ actions[QWorkspacePrivate::MinimizeAct] = new QAction(QIcon(q->style()->standardPixmap(QStyle::SP_TitleBarMinButton, 0, q)),
+ QWorkspace::tr("Mi&nimize"), q);
+ actions[QWorkspacePrivate::MaximizeAct] = new QAction(QIcon(q->style()->standardPixmap(QStyle::SP_TitleBarMaxButton, 0, q)),
+ QWorkspace::tr("Ma&ximize"), q);
+ actions[QWorkspacePrivate::CloseAct] = new QAction(QIcon(q->style()->standardPixmap(QStyle::SP_TitleBarCloseButton, 0, q)),
+ QWorkspace::tr("&Close")
+#ifndef QT_NO_SHORTCUT
+ +QLatin1Char('\t')+(QString)QKeySequence(Qt::CTRL+Qt::Key_F4)
+#endif
+ ,q);
+ QObject::connect(actions[QWorkspacePrivate::CloseAct], SIGNAL(triggered()), q, SLOT(closeActiveWindow()));
+ actions[QWorkspacePrivate::StaysOnTopAct] = new QAction(QWorkspace::tr("Stay on &Top"), q);
+ actions[QWorkspacePrivate::StaysOnTopAct]->setChecked(true);
+ actions[QWorkspacePrivate::ShadeAct] = new QAction(QIcon(q->style()->standardPixmap(QStyle::SP_TitleBarShadeButton, 0, q)),
+ QWorkspace::tr("Sh&ade"), q);
+
+ QObject::connect(popup, SIGNAL(aboutToShow()), q, SLOT(_q_updateActions()));
+ QObject::connect(popup, SIGNAL(triggered(QAction*)), q, SLOT(_q_operationMenuActivated(QAction*)));
+ popup->addAction(actions[QWorkspacePrivate::RestoreAct]);
+ popup->addAction(actions[QWorkspacePrivate::MoveAct]);
+ popup->addAction(actions[QWorkspacePrivate::ResizeAct]);
+ popup->addAction(actions[QWorkspacePrivate::MinimizeAct]);
+ popup->addAction(actions[QWorkspacePrivate::MaximizeAct]);
+ popup->addSeparator();
+ popup->addAction(actions[QWorkspacePrivate::CloseAct]);
+
+ QObject::connect(toolPopup, SIGNAL(aboutToShow()), q, SLOT(_q_updateActions()));
+ QObject::connect(toolPopup, SIGNAL(triggered(QAction*)), q, SLOT(_q_operationMenuActivated(QAction*)));
+ toolPopup->addAction(actions[QWorkspacePrivate::MoveAct]);
+ toolPopup->addAction(actions[QWorkspacePrivate::ResizeAct]);
+ toolPopup->addAction(actions[QWorkspacePrivate::StaysOnTopAct]);
+ toolPopup->addSeparator();
+ toolPopup->addAction(actions[QWorkspacePrivate::ShadeAct]);
+ toolPopup->addAction(actions[QWorkspacePrivate::CloseAct]);
+
+#ifndef QT_NO_SHORTCUT
+ // Set up shortcut bindings (id -> slot), most used first
+ QList <QKeySequence> shortcuts = QKeySequence::keyBindings(QKeySequence::NextChild);
+ foreach (const QKeySequence &seq, shortcuts)
+ shortcutMap.insert(q->grabShortcut(seq), "activateNextWindow");
+
+ shortcuts = QKeySequence::keyBindings(QKeySequence::PreviousChild);
+ foreach (const QKeySequence &seq, shortcuts)
+ shortcutMap.insert(q->grabShortcut(seq), "activatePreviousWindow");
+
+ shortcuts = QKeySequence::keyBindings(QKeySequence::Close);
+ foreach (const QKeySequence &seq, shortcuts)
+ shortcutMap.insert(q->grabShortcut(seq), "closeActiveWindow");
+
+ shortcutMap.insert(q->grabShortcut(QKeySequence(QLatin1String("ALT+-"))), "_q_showOperationMenu");
+#endif // QT_NO_SHORTCUT
+
+ q->setBackgroundRole(QPalette::Dark);
+ q->setAutoFillBackground(true);
+ q->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding));
+
+ hbar = vbar = 0;
+ corner = 0;
+ xoffset = yoffset = 0;
+
+ q->window()->installEventFilter(q);
+
+ inTitleChange = false;
+ updateWorkspace();
+}
+
+/*!
+ Destroys the workspace and frees any allocated resources.
+*/
+
+QWorkspace::~QWorkspace()
+{
+}
+
+/*! \reimp */
+QSize QWorkspace::sizeHint() const
+{
+ QSize s(QApplication::desktop()->size());
+ return QSize(s.width()*2/3, s.height()*2/3);
+}
+
+
+#ifdef QT3_SUPPORT
+/*!
+ Sets the background color to \a c.
+ Use setBackground() instead.
+*/
+void QWorkspace::setPaletteBackgroundColor(const QColor & c)
+{
+ setBackground(c);
+}
+
+/*!
+ Sets the background pixmap to \a pm.
+ Use setBackground() instead.
+*/
+void QWorkspace::setPaletteBackgroundPixmap(const QPixmap & pm)
+{
+ setBackground(pm);
+}
+#endif // QT3_SUPPORT
+
+/*!
+ \property QWorkspace::background
+ \brief the workspace's background
+*/
+QBrush QWorkspace::background() const
+{
+ Q_D(const QWorkspace);
+ if (d->background.style() == Qt::NoBrush)
+ return palette().dark();
+ return d->background;
+}
+
+void QWorkspace::setBackground(const QBrush &background)
+{
+ Q_D(QWorkspace);
+ d->background = background;
+ setAttribute(Qt::WA_OpaquePaintEvent, background.style() == Qt::NoBrush);
+ update();
+}
+
+/*!
+ Adds widget \a w as new sub window to the workspace. If \a flags
+ are non-zero, they will override the flags set on the widget.
+
+ Returns the widget used for the window frame.
+
+ To remove the widget \a w from the workspace, simply call
+ setParent() with the new parent (or 0 to make it a stand-alone
+ window).
+*/
+QWidget * QWorkspace::addWindow(QWidget *w, Qt::WindowFlags flags)
+{
+ Q_D(QWorkspace);
+ if (!w)
+ return 0;
+
+ w->setAutoFillBackground(true);
+
+ QWidgetPrivate::adjustFlags(flags);
+
+#if 0
+ bool wasMaximized = w->isMaximized();
+ bool wasMinimized = w->isMinimized();
+#endif
+ bool hasSize = w->testAttribute(Qt::WA_Resized);
+ int x = w->x();
+ int y = w->y();
+ bool hasPos = w->testAttribute(Qt::WA_Moved);
+ QSize s = w->size().expandedTo(qSmartMinSize(w));
+ if (!hasSize && w->sizeHint().isValid())
+ w->adjustSize();
+
+ QWorkspaceChild* child = new QWorkspaceChild(w, this, flags);
+ child->setObjectName(QLatin1String("qt_workspacechild"));
+ child->installEventFilter(this);
+
+ connect(child, SIGNAL(popupOperationMenu(QPoint)),
+ this, SLOT(_q_popupOperationMenu(QPoint)));
+ connect(child, SIGNAL(showOperationMenu()),
+ this, SLOT(_q_showOperationMenu()));
+ d->windows.append(child);
+ if (child->isVisibleTo(this))
+ d->focus.append(child);
+ child->internalRaise();
+
+ if (!hasPos)
+ d->place(child);
+ if (!hasSize)
+ child->adjustSize();
+ if (hasPos)
+ child->move(x, y);
+
+ return child;
+
+#if 0
+ if (wasMaximized)
+ w->showMaximized();
+ else if (wasMinimized)
+ w->showMinimized();
+ else if (!hasBeenHidden)
+ d->activateWindow(w);
+
+ d->updateWorkspace();
+ return child;
+#endif
+}
+
+/*! \reimp */
+void QWorkspace::childEvent(QChildEvent * e)
+{
+ Q_D(QWorkspace);
+ if (e->removed()) {
+ if (d->windows.removeAll(static_cast<QWorkspaceChild*>(e->child()))) {
+ d->focus.removeAll(static_cast<QWorkspaceChild*>(e->child()));
+ if (d->maxWindow == e->child())
+ d->maxWindow = 0;
+ d->updateWorkspace();
+ }
+ }
+}
+
+/*! \reimp */
+#ifndef QT_NO_WHEELEVENT
+void QWorkspace::wheelEvent(QWheelEvent *e)
+{
+ Q_D(QWorkspace);
+ if (!scrollBarsEnabled())
+ return;
+ // the scroll bars are children of the workspace, so if we receive
+ // a wheel event we redirect to the scroll bars using a direct event
+ // call, /not/ using sendEvent() because if the scroll bar ignores the
+ // event QApplication::sendEvent() will propagate the event to the parent widget,
+ // which is us, who /just/ sent it.
+ if (d->vbar && d->vbar->isVisible() && !(e->modifiers() & Qt::AltModifier))
+ d->vbar->event(e);
+ else if (d->hbar && d->hbar->isVisible())
+ d->hbar->event(e);
+}
+#endif
+
+void QWorkspacePrivate::activateWindow(QWidget* w, bool change_focus)
+{
+ Q_Q(QWorkspace);
+ if (!w) {
+ active = 0;
+ emit q->windowActivated(0);
+ return;
+ }
+ if (!q->isVisible()) {
+ becomeActive = w;
+ return;
+ }
+
+ if (active && active->windowWidget() == w) {
+ if (!isChildOf(q->focusWidget(), w)) // child window does not have focus
+ active->setActive(true);
+ return;
+ }
+
+ active = 0;
+ // First deactivate all other workspace clients
+ QList<QWorkspaceChild *>::Iterator it(windows.begin());
+ while (it != windows.end()) {
+ QWorkspaceChild* c = *it;
+ ++it;
+ if (c->windowWidget() == w)
+ active = c;
+ else
+ c->setActive(false);
+ }
+
+ if (!active)
+ return;
+
+ // Then activate the new one, so the focus is stored correctly
+ active->setActive(true);
+
+ if (!active)
+ return;
+
+ if (maxWindow && maxWindow != active && active->windowWidget() &&
+ (active->windowWidget()->windowFlags() & Qt::WindowMaximizeButtonHint))
+ active->showMaximized();
+
+ active->internalRaise();
+
+ if (change_focus) {
+ int from = focus.indexOf(active);
+ if (from >= 0)
+ focus.move(from, focus.size() - 1);
+ }
+
+ updateWorkspace();
+ emit q->windowActivated(w);
+}
+
+
+/*!
+ Returns a pointer to the widget corresponding to the active child
+ window, or 0 if no window is active.
+
+ \sa setActiveWindow()
+*/
+QWidget* QWorkspace::activeWindow() const
+{
+ Q_D(const QWorkspace);
+ return d->active? d->active->windowWidget() : 0;
+}
+
+/*!
+ Makes the child window that contains \a w the active child window.
+
+ \sa activeWindow()
+*/
+void QWorkspace::setActiveWindow(QWidget *w)
+{
+ Q_D(QWorkspace);
+ d->activateWindow(w, true);
+ if (w && w->isMinimized())
+ w->setWindowState(w->windowState() & ~Qt::WindowMinimized);
+}
+
+void QWorkspacePrivate::place(QWidget *w)
+{
+ Q_Q(QWorkspace);
+
+ QList<QWidget *> widgets;
+ for (QList<QWorkspaceChild *>::Iterator it(windows.begin()); it != windows.end(); ++it)
+ if (*it != w)
+ widgets.append(*it);
+
+ int overlap, minOverlap = 0;
+ int possible;
+
+ QRect r1(0, 0, 0, 0);
+ QRect r2(0, 0, 0, 0);
+ QRect maxRect = q->rect();
+ int x = maxRect.left(), y = maxRect.top();
+ QPoint wpos(maxRect.left(), maxRect.top());
+
+ bool firstPass = true;
+
+ do {
+ if (y + w->height() > maxRect.bottom()) {
+ overlap = -1;
+ } else if(x + w->width() > maxRect.right()) {
+ overlap = -2;
+ } else {
+ overlap = 0;
+
+ r1.setRect(x, y, w->width(), w->height());
+
+ QWidget *l;
+ QList<QWidget *>::Iterator it(widgets.begin());
+ while (it != widgets.end()) {
+ l = *it;
+ ++it;
+
+ if (maxWindow == l)
+ r2 = QStyle::visualRect(q->layoutDirection(), maxRect, maxRestore);
+ else
+ r2 = QStyle::visualRect(q->layoutDirection(), maxRect,
+ QRect(l->x(), l->y(), l->width(), l->height()));
+
+ if (r2.intersects(r1)) {
+ r2.setCoords(qMax(r1.left(), r2.left()),
+ qMax(r1.top(), r2.top()),
+ qMin(r1.right(), r2.right()),
+ qMin(r1.bottom(), r2.bottom())
+ );
+
+ overlap += (r2.right() - r2.left()) *
+ (r2.bottom() - r2.top());
+ }
+ }
+ }
+
+ if (overlap == 0) {
+ wpos = QPoint(x, y);
+ break;
+ }
+
+ if (firstPass) {
+ firstPass = false;
+ minOverlap = overlap;
+ } else if (overlap >= 0 && overlap < minOverlap) {
+ minOverlap = overlap;
+ wpos = QPoint(x, y);
+ }
+
+ if (overlap > 0) {
+ possible = maxRect.right();
+ if (possible - w->width() > x) possible -= w->width();
+
+ QWidget *l;
+ QList<QWidget *>::Iterator it(widgets.begin());
+ while (it != widgets.end()) {
+ l = *it;
+ ++it;
+ if (maxWindow == l)
+ r2 = QStyle::visualRect(q->layoutDirection(), maxRect, maxRestore);
+ else
+ r2 = QStyle::visualRect(q->layoutDirection(), maxRect,
+ QRect(l->x(), l->y(), l->width(), l->height()));
+
+ if((y < r2.bottom()) && (r2.top() < w->height() + y)) {
+ if(r2.right() > x)
+ possible = possible < r2.right() ?
+ possible : r2.right();
+
+ if(r2.left() - w->width() > x)
+ possible = possible < r2.left() - w->width() ?
+ possible : r2.left() - w->width();
+ }
+ }
+
+ x = possible;
+ } else if (overlap == -2) {
+ x = maxRect.left();
+ possible = maxRect.bottom();
+
+ if (possible - w->height() > y) possible -= w->height();
+
+ QWidget *l;
+ QList<QWidget *>::Iterator it(widgets.begin());
+ while (it != widgets.end()) {
+ l = *it;
+ ++it;
+ if (maxWindow == l)
+ r2 = QStyle::visualRect(q->layoutDirection(), maxRect, maxRestore);
+ else
+ r2 = QStyle::visualRect(q->layoutDirection(), maxRect,
+ QRect(l->x(), l->y(), l->width(), l->height()));
+
+ if(r2.bottom() > y)
+ possible = possible < r2.bottom() ?
+ possible : r2.bottom();
+
+ if(r2.top() - w->height() > y)
+ possible = possible < r2.top() - w->height() ?
+ possible : r2.top() - w->height();
+ }
+
+ y = possible;
+ }
+ }
+ while(overlap != 0 && overlap != -1);
+
+ QRect resultRect = w->geometry();
+ resultRect.moveTo(wpos);
+ w->setGeometry(QStyle::visualRect(q->layoutDirection(), maxRect, resultRect));
+ updateWorkspace();
+}
+
+
+void QWorkspacePrivate::insertIcon(QWidget* w)
+{
+ Q_Q(QWorkspace);
+ if (!w || icons.contains(w))
+ return;
+ icons.append(w);
+ if (w->parentWidget() != q) {
+ w->setParent(q, 0);
+ w->move(0,0);
+ }
+ QRect cr = updateWorkspace();
+ int x = 0;
+ int y = cr.height() - w->height();
+
+ QList<QWidget *>::Iterator it(icons.begin());
+ while (it != icons.end()) {
+ QWidget* i = *it;
+ ++it;
+ if (x > 0 && x + i->width() > cr.width()){
+ x = 0;
+ y -= i->height();
+ }
+
+ if (i != w &&
+ i->geometry().intersects(QRect(x, y, w->width(), w->height())))
+ x += i->width();
+ }
+ w->move(x, y);
+
+ if (q->isVisibleTo(q->parentWidget())) {
+ w->show();
+ w->lower();
+ }
+ updateWorkspace();
+}
+
+
+void QWorkspacePrivate::removeIcon(QWidget* w)
+{
+ if (icons.removeAll(w))
+ w->hide();
+}
+
+
+/*! \reimp */
+void QWorkspace::resizeEvent(QResizeEvent *)
+{
+ Q_D(QWorkspace);
+ if (d->maxWindow) {
+ d->maxWindow->adjustToFullscreen();
+ if (d->maxWindow->windowWidget())
+ d->maxWindow->windowWidget()->overrideWindowState(Qt::WindowMaximized);
+ }
+ d->updateWorkspace();
+}
+
+/*! \reimp */
+void QWorkspace::showEvent(QShowEvent *e)
+{
+ Q_D(QWorkspace);
+ if (d->maxWindow)
+ d->showMaximizeControls();
+ QWidget::showEvent(e);
+ if (d->becomeActive) {
+ d->activateWindow(d->becomeActive);
+ d->becomeActive = 0;
+ } else if (d->windows.count() > 0 && !d->active) {
+ d->activateWindow(d->windows.first()->windowWidget());
+ }
+
+// // force a frame repaint - this is a workaround for what seems to be a bug
+// // introduced when changing the QWidget::show() implementation. Might be
+// // a windows bug as well though.
+// for (int i = 0; i < d->windows.count(); ++i) {
+// QWorkspaceChild* c = d->windows.at(i);
+// c->update(c->rect());
+// }
+
+ d->updateWorkspace();
+}
+
+/*! \reimp */
+void QWorkspace::hideEvent(QHideEvent *)
+{
+ Q_D(QWorkspace);
+ if (!isVisible())
+ d->hideMaximizeControls();
+}
+
+/*! \reimp */
+void QWorkspace::paintEvent(QPaintEvent *)
+{
+ Q_D(QWorkspace);
+
+ if (d->background.style() != Qt::NoBrush) {
+ QPainter p(this);
+ p.fillRect(0, 0, width(), height(), d->background);
+ }
+}
+
+void QWorkspacePrivate::minimizeWindow(QWidget* w)
+{
+ QWorkspaceChild* c = findChild(w);
+
+ if (!w || !(w->windowFlags() & Qt::WindowMinimizeButtonHint))
+ return;
+
+ if (c) {
+ bool wasMax = false;
+ if (c == maxWindow) {
+ wasMax = true;
+ maxWindow = 0;
+ hideMaximizeControls();
+ for (QList<QWorkspaceChild *>::Iterator it(windows.begin()); it != windows.end(); ++it) {
+ QWorkspaceChild* c = *it;
+ if (c->titlebar)
+ c->titlebar->setMovable(true);
+ c->widgetResizeHandler->setActive(true);
+ }
+ }
+ c->hide();
+ if (wasMax)
+ c->setGeometry(maxRestore);
+ if (!focus.contains(c))
+ focus.append(c);
+ insertIcon(c->iconWidget());
+
+ if (!maxWindow)
+ activateWindow(w);
+
+ updateWorkspace();
+
+ w->overrideWindowState(Qt::WindowMinimized);
+ c->overrideWindowState(Qt::WindowMinimized);
+ }
+}
+
+void QWorkspacePrivate::normalizeWindow(QWidget* w)
+{
+ Q_Q(QWorkspace);
+ QWorkspaceChild* c = findChild(w);
+ if (!w)
+ return;
+ if (c) {
+ w->overrideWindowState(Qt::WindowNoState);
+ hideMaximizeControls();
+ if (!maxmenubar || q->style()->styleHint(QStyle::SH_Workspace_FillSpaceOnMaximize, 0, q) || !maxWindow) {
+ if (w->minimumSize() != w->maximumSize())
+ c->widgetResizeHandler->setActive(true);
+ if (c->titlebar)
+ c->titlebar->setMovable(true);
+ }
+ w->overrideWindowState(Qt::WindowNoState);
+ c->overrideWindowState(Qt::WindowNoState);
+
+ if (c == maxWindow) {
+ c->setGeometry(maxRestore);
+ maxWindow = 0;
+ } else {
+ if (c->iconw)
+ removeIcon(c->iconw->parentWidget());
+ c->show();
+ }
+
+ hideMaximizeControls();
+ for (QList<QWorkspaceChild *>::Iterator it(windows.begin()); it != windows.end(); ++it) {
+ QWorkspaceChild* c = *it;
+ if (c->titlebar)
+ c->titlebar->setMovable(true);
+ if (c->childWidget && c->childWidget->minimumSize() != c->childWidget->maximumSize())
+ c->widgetResizeHandler->setActive(true);
+ }
+ activateWindow(w, true);
+ updateWorkspace();
+ }
+}
+
+void QWorkspacePrivate::maximizeWindow(QWidget* w)
+{
+ Q_Q(QWorkspace);
+ QWorkspaceChild* c = findChild(w);
+
+ if (!w || !(w->windowFlags() & Qt::WindowMaximizeButtonHint))
+ return;
+
+ if (!c || c == maxWindow)
+ return;
+
+ bool updatesEnabled = q->updatesEnabled();
+ q->setUpdatesEnabled(false);
+
+ if (c->iconw && icons.contains(c->iconw->parentWidget()))
+ normalizeWindow(w);
+ QRect r(c->geometry());
+ QWorkspaceChild *oldMaxWindow = maxWindow;
+ maxWindow = c;
+
+ showMaximizeControls();
+
+ c->adjustToFullscreen();
+ c->show();
+ c->internalRaise();
+ if (oldMaxWindow != c) {
+ if (oldMaxWindow) {
+ oldMaxWindow->setGeometry(maxRestore);
+ oldMaxWindow->overrideWindowState(Qt::WindowNoState);
+ if(oldMaxWindow->windowWidget())
+ oldMaxWindow->windowWidget()->overrideWindowState(Qt::WindowNoState);
+ }
+ maxRestore = r;
+ }
+
+ activateWindow(w);
+
+ if(!maxmenubar || q->style()->styleHint(QStyle::SH_Workspace_FillSpaceOnMaximize, 0, q)) {
+ if (!active && becomeActive) {
+ active = (QWorkspaceChild*)becomeActive->parentWidget();
+ active->setActive(true);
+ becomeActive = 0;
+ emit q->windowActivated(active->windowWidget());
+ }
+ c->widgetResizeHandler->setActive(false);
+ if (c->titlebar)
+ c->titlebar->setMovable(false);
+ }
+ updateWorkspace();
+
+ w->overrideWindowState(Qt::WindowMaximized);
+ c->overrideWindowState(Qt::WindowMaximized);
+ q->setUpdatesEnabled(updatesEnabled);
+}
+
+void QWorkspacePrivate::showWindow(QWidget* w)
+{
+ if (w->isMinimized() && (w->windowFlags() & Qt::WindowMinimizeButtonHint))
+ minimizeWindow(w);
+ else if ((maxWindow || w->isMaximized()) && w->windowFlags() & Qt::WindowMaximizeButtonHint)
+ maximizeWindow(w);
+ else if (w->windowFlags() & Qt::WindowMaximizeButtonHint)
+ normalizeWindow(w);
+ else
+ w->parentWidget()->show();
+ if (maxWindow)
+ maxWindow->internalRaise();
+ updateWorkspace();
+}
+
+
+QWorkspaceChild* QWorkspacePrivate::findChild(QWidget* w)
+{
+ QList<QWorkspaceChild *>::Iterator it(windows.begin());
+ while (it != windows.end()) {
+ QWorkspaceChild* c = *it;
+ ++it;
+ if (c->windowWidget() == w)
+ return c;
+ }
+ return 0;
+}
+
+/*!
+ Returns a list of all visible or minimized child windows. If \a
+ order is CreationOrder (the default), the windows are listed in
+ the order in which they were inserted into the workspace. If \a
+ order is StackingOrder, the windows are listed in their stacking
+ order, with the topmost window as the last item in the list.
+*/
+QWidgetList QWorkspace::windowList(WindowOrder order) const
+{
+ Q_D(const QWorkspace);
+ QWidgetList windows;
+ if (order == StackingOrder) {
+ QObjectList cl = children();
+ for (int i = 0; i < cl.size(); ++i) {
+ QWorkspaceChild *c = qobject_cast<QWorkspaceChild*>(cl.at(i));
+ if (c && c->isWindowOrIconVisible())
+ windows.append(c->windowWidget());
+ }
+ } else {
+ QList<QWorkspaceChild *>::ConstIterator it(d->windows.begin());
+ while (it != d->windows.end()) {
+ QWorkspaceChild* c = *it;
+ ++it;
+ if (c && c->isWindowOrIconVisible())
+ windows.append(c->windowWidget());
+ }
+ }
+ return windows;
+}
+
+
+/*! \reimp */
+bool QWorkspace::event(QEvent *e)
+{
+#ifndef QT_NO_SHORTCUT
+ Q_D(QWorkspace);
+ if (e->type() == QEvent::Shortcut) {
+ QShortcutEvent *se = static_cast<QShortcutEvent *>(e);
+ const char *theSlot = d->shortcutMap.value(se->shortcutId(), 0);
+ if (theSlot)
+ QMetaObject::invokeMethod(this, theSlot);
+ } else
+#endif
+ if (e->type() == QEvent::FocusIn || e->type() == QEvent::FocusOut){
+ return true;
+ }
+ return QWidget::event(e);
+}
+
+/*! \reimp */
+bool QWorkspace::eventFilter(QObject *o, QEvent * e)
+{
+ Q_D(QWorkspace);
+ static QTime* t = 0;
+ static QWorkspace* tc = 0;
+ if (o == d->maxtools) {
+ switch (e->type()) {
+ case QEvent::MouseButtonPress:
+ {
+ QMenuBar* b = (QMenuBar*)o->parent();
+ if (!t)
+ t = new QTime;
+ if (tc != this || t->elapsed() > QApplication::doubleClickInterval()) {
+ if (isRightToLeft()) {
+ QPoint p = b->mapToGlobal(QPoint(b->x() + b->width(), b->y() + b->height()));
+ p.rx() -= d->popup->sizeHint().width();
+ d->_q_popupOperationMenu(p);
+ } else {
+ d->_q_popupOperationMenu(b->mapToGlobal(QPoint(b->x(), b->y() + b->height())));
+ }
+ t->start();
+ tc = this;
+ } else {
+ tc = 0;
+ closeActiveWindow();
+ }
+ return true;
+ }
+ default:
+ break;
+ }
+ return QWidget::eventFilter(o, e);
+ }
+ switch (e->type()) {
+ case QEvent::HideToParent:
+ break;
+ case QEvent::ShowToParent:
+ if (QWorkspaceChild *c = qobject_cast<QWorkspaceChild*>(o))
+ if (!d->focus.contains(c))
+ d->focus.append(c);
+ d->updateWorkspace();
+ break;
+ case QEvent::WindowTitleChange:
+ if (!d->inTitleChange) {
+ if (o == window())
+ d->topTitle = window()->windowTitle();
+ if (d->maxWindow && d->maxWindow->windowWidget() && d->topTitle.size()) {
+ d->inTitleChange = true;
+ window()->setWindowTitle(tr("%1 - [%2]")
+ .arg(d->topTitle).arg(d->maxWindow->windowWidget()->windowTitle()));
+ d->inTitleChange = false;
+ }
+ }
+ break;
+
+ case QEvent::ModifiedChange:
+ if (o == d->maxWindow)
+ window()->setWindowModified(d->maxWindow->isWindowModified());
+ break;
+
+ case QEvent::Close:
+ if (o == window())
+ {
+ QList<QWorkspaceChild *>::Iterator it(d->windows.begin());
+ while (it != d->windows.end()) {
+ QWorkspaceChild* c = *it;
+ ++it;
+ if (c->shademode)
+ c->showShaded();
+ }
+ } else if (qobject_cast<QWorkspaceChild*>(o)) {
+ d->popup->hide();
+ }
+ d->updateWorkspace();
+ break;
+ default:
+ break;
+ }
+ return QWidget::eventFilter(o, e);
+}
+
+static QMenuBar *findMenuBar(QWidget *w)
+{
+ // don't search recursively to avoid finding a menu bar of a
+ // mainwindow that happens to be a workspace window (like
+ // a mainwindow in designer)
+ QList<QObject *> children = w->children();
+ for (int i = 0; i < children.count(); ++i) {
+ QMenuBar *bar = qobject_cast<QMenuBar *>(children.at(i));
+ if (bar)
+ return bar;
+ }
+ return 0;
+}
+
+void QWorkspacePrivate::showMaximizeControls()
+{
+ Q_Q(QWorkspace);
+ Q_ASSERT(maxWindow);
+
+ // merge windowtitle and modified state
+ if (!topTitle.size())
+ topTitle = q->window()->windowTitle();
+
+ if (maxWindow->windowWidget()) {
+ QString docTitle = maxWindow->windowWidget()->windowTitle();
+ if (topTitle.size() && docTitle.size()) {
+ inTitleChange = true;
+ q->window()->setWindowTitle(QWorkspace::tr("%1 - [%2]").arg(topTitle).arg(docTitle));
+ inTitleChange = false;
+ }
+ q->window()->setWindowModified(maxWindow->windowWidget()->isWindowModified());
+ }
+
+ if (!q->style()->styleHint(QStyle::SH_Workspace_FillSpaceOnMaximize, 0, q)) {
+ QMenuBar* b = 0;
+
+ // Do a breadth-first search first on every parent,
+ QWidget* w = q->parentWidget();
+ while (w) {
+ b = findMenuBar(w);
+ if (b)
+ break;
+ w = w->parentWidget();
+ }
+
+ // last attempt.
+ if (!b)
+ b = findMenuBar(q->window());
+
+ if (!b)
+ return;
+
+ if (!maxcontrols) {
+ maxmenubar = b;
+ maxcontrols = new QMDIControl(b);
+ QObject::connect(maxcontrols, SIGNAL(_q_minimize()),
+ q, SLOT(_q_minimizeActiveWindow()));
+ QObject::connect(maxcontrols, SIGNAL(_q_restore()),
+ q, SLOT(_q_normalizeActiveWindow()));
+ QObject::connect(maxcontrols, SIGNAL(_q_close()),
+ q, SLOT(closeActiveWindow()));
+ }
+
+ b->setCornerWidget(maxcontrols);
+ if (b->isVisible())
+ maxcontrols->show();
+ if (!active && becomeActive) {
+ active = (QWorkspaceChild*)becomeActive->parentWidget();
+ active->setActive(true);
+ becomeActive = 0;
+ emit q->windowActivated(active->windowWidget());
+ }
+ if (active) {
+ if (!maxtools) {
+ maxtools = new QLabel(q->window());
+ maxtools->setObjectName(QLatin1String("qt_maxtools"));
+ maxtools->installEventFilter(q);
+ }
+ if (active->windowWidget() && !active->windowWidget()->windowIcon().isNull()) {
+ QIcon icon = active->windowWidget()->windowIcon();
+ int iconSize = maxcontrols->size().height();
+ maxtools->setPixmap(icon.pixmap(QSize(iconSize, iconSize)));
+ } else {
+ QPixmap pm = q->style()->standardPixmap(QStyle::SP_TitleBarMenuButton, 0, q);
+ if (pm.isNull()) {
+ pm = QPixmap(14,14);
+ pm.fill(Qt::black);
+ }
+ maxtools->setPixmap(pm);
+ }
+ b->setCornerWidget(maxtools, Qt::TopLeftCorner);
+ if (b->isVisible())
+ maxtools->show();
+ }
+ }
+}
+
+
+void QWorkspacePrivate::hideMaximizeControls()
+{
+ Q_Q(QWorkspace);
+ if (maxmenubar && !q->style()->styleHint(QStyle::SH_Workspace_FillSpaceOnMaximize, 0, q)) {
+ if (maxmenubar) {
+ maxmenubar->setCornerWidget(0, Qt::TopLeftCorner);
+ maxmenubar->setCornerWidget(0, Qt::TopRightCorner);
+ }
+ if (maxcontrols) {
+ maxcontrols->deleteLater();
+ maxcontrols = 0;
+ }
+ if (maxtools) {
+ maxtools->deleteLater();
+ maxtools = 0;
+ }
+ }
+
+ //unmerge the title bar/modification state
+ if (topTitle.size()) {
+ inTitleChange = true;
+ q->window()->setWindowTitle(topTitle);
+ inTitleChange = false;
+ }
+ q->window()->setWindowModified(false);
+}
+
+/*!
+ Closes the child window that is currently active.
+
+ \sa closeAllWindows()
+*/
+void QWorkspace::closeActiveWindow()
+{
+ Q_D(QWorkspace);
+ if (d->maxWindow && d->maxWindow->windowWidget())
+ d->maxWindow->windowWidget()->close();
+ else if (d->active && d->active->windowWidget())
+ d->active->windowWidget()->close();
+ d->updateWorkspace();
+}
+
+/*!
+ Closes all child windows.
+
+ If any child window fails to accept the close event, the remaining windows
+ will remain open.
+
+ \sa closeActiveWindow()
+*/
+void QWorkspace::closeAllWindows()
+{
+ Q_D(QWorkspace);
+ bool did_close = true;
+ QList<QWorkspaceChild *>::const_iterator it = d->windows.constBegin();
+ while (it != d->windows.constEnd() && did_close) {
+ QWorkspaceChild *c = *it;
+ ++it;
+ if (c->windowWidget() && !c->windowWidget()->isHidden())
+ did_close = c->windowWidget()->close();
+ }
+}
+
+void QWorkspacePrivate::_q_normalizeActiveWindow()
+{
+ if (maxWindow)
+ maxWindow->showNormal();
+ else if (active)
+ active->showNormal();
+}
+
+void QWorkspacePrivate::_q_minimizeActiveWindow()
+{
+ if (maxWindow)
+ maxWindow->showMinimized();
+ else if (active)
+ active->showMinimized();
+}
+
+void QWorkspacePrivate::_q_showOperationMenu()
+{
+ Q_Q(QWorkspace);
+ if (!active || !active->windowWidget())
+ return;
+ Q_ASSERT((active->windowWidget()->windowFlags() & Qt::WindowSystemMenuHint));
+ QPoint p;
+ QMenu *popup = (active->titlebar && active->titlebar->isTool()) ? toolPopup : this->popup;
+ if (q->isRightToLeft()) {
+ p = QPoint(active->windowWidget()->mapToGlobal(QPoint(active->windowWidget()->width(),0)));
+ p.rx() -= popup->sizeHint().width();
+ } else {
+ p = QPoint(active->windowWidget()->mapToGlobal(QPoint(0,0)));
+ }
+ if (!active->isVisible()) {
+ p = active->iconWidget()->mapToGlobal(QPoint(0,0));
+ p.ry() -= popup->sizeHint().height();
+ }
+ _q_popupOperationMenu(p);
+}
+
+void QWorkspacePrivate::_q_popupOperationMenu(const QPoint& p)
+{
+ if (!active || !active->windowWidget() || !(active->windowWidget()->windowFlags() & Qt::WindowSystemMenuHint))
+ return;
+ if (active->titlebar && active->titlebar->isTool())
+ toolPopup->popup(p);
+ else
+ popup->popup(p);
+}
+
+void QWorkspacePrivate::_q_updateActions()
+{
+ Q_Q(QWorkspace);
+ for (int i = 1; i < NCountAct-1; i++) {
+ bool enable = active != 0;
+ actions[i]->setEnabled(enable);
+ }
+
+ if (!active || !active->windowWidget())
+ return;
+
+ QWidget *windowWidget = active->windowWidget();
+ bool canResize = windowWidget->maximumSize() != windowWidget->minimumSize();
+ actions[QWorkspacePrivate::ResizeAct]->setEnabled(canResize);
+ actions[QWorkspacePrivate::MinimizeAct]->setEnabled((windowWidget->windowFlags() & Qt::WindowMinimizeButtonHint));
+ actions[QWorkspacePrivate::MaximizeAct]->setEnabled((windowWidget->windowFlags() & Qt::WindowMaximizeButtonHint) && canResize);
+
+ if (active == maxWindow) {
+ actions[QWorkspacePrivate::MoveAct]->setEnabled(false);
+ actions[QWorkspacePrivate::ResizeAct]->setEnabled(false);
+ actions[QWorkspacePrivate::MaximizeAct]->setEnabled(false);
+ actions[QWorkspacePrivate::RestoreAct]->setEnabled(true);
+ } else if (active->isVisible()){
+ actions[QWorkspacePrivate::RestoreAct]->setEnabled(false);
+ } else {
+ actions[QWorkspacePrivate::MoveAct]->setEnabled(false);
+ actions[QWorkspacePrivate::ResizeAct]->setEnabled(false);
+ actions[QWorkspacePrivate::MinimizeAct]->setEnabled(false);
+ actions[QWorkspacePrivate::RestoreAct]->setEnabled(true);
+ }
+ if (active->shademode) {
+ actions[QWorkspacePrivate::ShadeAct]->setIcon(
+ QIcon(q->style()->standardPixmap(QStyle::SP_TitleBarUnshadeButton, 0, q)));
+ actions[QWorkspacePrivate::ShadeAct]->setText(QWorkspace::tr("&Unshade"));
+ } else {
+ actions[QWorkspacePrivate::ShadeAct]->setIcon(
+ QIcon(q->style()->standardPixmap(QStyle::SP_TitleBarShadeButton, 0, q)));
+ actions[QWorkspacePrivate::ShadeAct]->setText(QWorkspace::tr("Sh&ade"));
+ }
+ actions[QWorkspacePrivate::StaysOnTopAct]->setEnabled(!active->shademode && canResize);
+ actions[QWorkspacePrivate::StaysOnTopAct]->setChecked(
+ (active->windowWidget()->windowFlags() & Qt::WindowStaysOnTopHint));
+}
+
+void QWorkspacePrivate::_q_operationMenuActivated(QAction *action)
+{
+ if (!active)
+ return;
+ if(action == actions[QWorkspacePrivate::RestoreAct]) {
+ active->showNormal();
+ } else if(action == actions[QWorkspacePrivate::MoveAct]) {
+ active->doMove();
+ } else if(action == actions[QWorkspacePrivate::ResizeAct]) {
+ if (active->shademode)
+ active->showShaded();
+ active->doResize();
+ } else if(action == actions[QWorkspacePrivate::MinimizeAct]) {
+ active->showMinimized();
+ } else if(action == actions[QWorkspacePrivate::MaximizeAct]) {
+ active->showMaximized();
+ } else if(action == actions[QWorkspacePrivate::ShadeAct]) {
+ active->showShaded();
+ } else if(action == actions[QWorkspacePrivate::StaysOnTopAct]) {
+ if(QWidget* w = active->windowWidget()) {
+ if ((w->windowFlags() & Qt::WindowStaysOnTopHint)) {
+ w->overrideWindowFlags(w->windowFlags() & ~Qt::WindowStaysOnTopHint);
+ } else {
+ w->overrideWindowFlags(w->windowFlags() | Qt::WindowStaysOnTopHint);
+ w->parentWidget()->raise();
+ }
+ }
+ }
+}
+
+
+void QWorkspacePrivate::hideChild(QWorkspaceChild *c)
+{
+ Q_Q(QWorkspace);
+
+// bool updatesEnabled = q->updatesEnabled();
+// q->setUpdatesEnabled(false);
+ focus.removeAll(c);
+ QRect restore;
+ if (maxWindow == c)
+ restore = maxRestore;
+ if (active == c) {
+ q->setFocus();
+ q->activatePreviousWindow();
+ }
+ if (active == c)
+ activateWindow(0);
+ if (maxWindow == c) {
+ hideMaximizeControls();
+ maxWindow = 0;
+ }
+ c->hide();
+ if (!restore.isEmpty())
+ c->setGeometry(restore);
+// q->setUpdatesEnabled(updatesEnabled);
+}
+
+/*!
+ Gives the input focus to the next window in the list of child
+ windows.
+
+ \sa activatePreviousWindow()
+*/
+void QWorkspace::activateNextWindow()
+{
+ Q_D(QWorkspace);
+
+ if (d->focus.isEmpty())
+ return;
+ if (!d->active) {
+ if (d->focus.first())
+ d->activateWindow(d->focus.first()->windowWidget(), false);
+ return;
+ }
+
+ int a = d->focus.indexOf(d->active) + 1;
+
+ a = a % d->focus.count();
+
+ if (d->focus.at(a))
+ d->activateWindow(d->focus.at(a)->windowWidget(), false);
+ else
+ d->activateWindow(0);
+}
+
+/*!
+ Gives the input focus to the previous window in the list of child
+ windows.
+
+ \sa activateNextWindow()
+*/
+void QWorkspace::activatePreviousWindow()
+{
+ Q_D(QWorkspace);
+
+ if (d->focus.isEmpty())
+ return;
+ if (!d->active) {
+ if (d->focus.last())
+ d->activateWindow(d->focus.first()->windowWidget(), false);
+ else
+ d->activateWindow(0);
+ return;
+ }
+
+ int a = d->focus.indexOf(d->active) - 1;
+ if (a < 0)
+ a = d->focus.count()-1;
+
+ if (d->focus.at(a))
+ d->activateWindow(d->focus.at(a)->windowWidget(), false);
+ else
+ d->activateWindow(0);
+}
+
+
+/*!
+ \fn void QWorkspace::windowActivated(QWidget* w)
+
+ This signal is emitted when the child window \a w becomes active.
+ Note that \a w can be 0, and that more than one signal may be
+ emitted for a single activation event.
+
+ \sa activeWindow(), windowList()
+*/
+
+/*!
+ Arranges all the child windows in a cascade pattern.
+
+ \sa tile(), arrangeIcons()
+*/
+void QWorkspace::cascade()
+{
+ Q_D(QWorkspace);
+ blockSignals(true);
+ if (d->maxWindow)
+ d->maxWindow->showNormal();
+
+ if (d->vbar) {
+ d->vbar->blockSignals(true);
+ d->vbar->setValue(0);
+ d->vbar->blockSignals(false);
+ d->hbar->blockSignals(true);
+ d->hbar->setValue(0);
+ d->hbar->blockSignals(false);
+ d->_q_scrollBarChanged();
+ }
+
+ const int xoffset = 13;
+ const int yoffset = 20;
+
+ // make a list of all relevant mdi clients
+ QList<QWorkspaceChild *> widgets;
+ QList<QWorkspaceChild *>::Iterator it(d->windows.begin());
+ QWorkspaceChild* wc = 0;
+
+ for (it = d->focus.begin(); it != d->focus.end(); ++it) {
+ wc = *it;
+ if (wc->windowWidget()->isVisibleTo(this) && !(wc->titlebar && wc->titlebar->isTool()))
+ widgets.append(wc);
+ }
+
+ int x = 0;
+ int y = 0;
+
+ it = widgets.begin();
+ while (it != widgets.end()) {
+ QWorkspaceChild *child = *it;
+ ++it;
+
+ QSize prefSize = child->windowWidget()->sizeHint().expandedTo(qSmartMinSize(child->windowWidget()));
+ if (!prefSize.isValid())
+ prefSize = child->windowWidget()->size();
+ prefSize = prefSize.expandedTo(qSmartMinSize(child->windowWidget()));
+ if (prefSize.isValid())
+ prefSize += QSize(child->baseSize().width(), child->baseSize().height());
+
+ int w = prefSize.width();
+ int h = prefSize.height();
+
+ child->showNormal();
+ if (y + h > height())
+ y = 0;
+ if (x + w > width())
+ x = 0;
+ child->setGeometry(x, y, w, h);
+ x += xoffset;
+ y += yoffset;
+ child->internalRaise();
+ }
+ d->updateWorkspace();
+ blockSignals(false);
+}
+
+/*!
+ Arranges all child windows in a tile pattern.
+
+ \sa cascade(), arrangeIcons()
+*/
+void QWorkspace::tile()
+{
+ Q_D(QWorkspace);
+ blockSignals(true);
+ QWidget *oldActive = d->active ? d->active->windowWidget() : 0;
+ if (d->maxWindow)
+ d->maxWindow->showNormal();
+
+ if (d->vbar) {
+ d->vbar->blockSignals(true);
+ d->vbar->setValue(0);
+ d->vbar->blockSignals(false);
+ d->hbar->blockSignals(true);
+ d->hbar->setValue(0);
+ d->hbar->blockSignals(false);
+ d->_q_scrollBarChanged();
+ }
+
+ int rows = 1;
+ int cols = 1;
+ int n = 0;
+ QWorkspaceChild* c;
+
+ QList<QWorkspaceChild *>::Iterator it(d->windows.begin());
+ while (it != d->windows.end()) {
+ c = *it;
+ ++it;
+ if (!c->windowWidget()->isHidden()
+ && !(c->windowWidget()->windowFlags() & Qt::WindowStaysOnTopHint)
+ && !c->iconw)
+ n++;
+ }
+
+ while (rows * cols < n) {
+ if (cols <= rows)
+ cols++;
+ else
+ rows++;
+ }
+ int add = cols * rows - n;
+ bool* used = new bool[cols*rows];
+ for (int i = 0; i < rows*cols; i++)
+ used[i] = false;
+
+ int row = 0;
+ int col = 0;
+ int w = width() / cols;
+ int h = height() / rows;
+
+ it = d->windows.begin();
+ while (it != d->windows.end()) {
+ c = *it;
+ ++it;
+ if (c->iconw || c->windowWidget()->isHidden() || (c->titlebar && c->titlebar->isTool()))
+ continue;
+ if (!row && !col) {
+ w -= c->baseSize().width();
+ h -= c->baseSize().height();
+ }
+ if ((c->windowWidget()->windowFlags() & Qt::WindowStaysOnTopHint)) {
+ QPoint p = c->pos();
+ if (p.x()+c->width() < 0)
+ p.setX(0);
+ if (p.x() > width())
+ p.setX(width() - c->width());
+ if (p.y() + 10 < 0)
+ p.setY(0);
+ if (p.y() > height())
+ p.setY(height() - c->height());
+
+ if (p != c->pos())
+ c->QWidget::move(p);
+ } else {
+ c->showNormal();
+ used[row*cols+col] = true;
+ QSize sz(w, h);
+ QSize bsize(c->baseSize());
+ sz = sz.expandedTo(c->windowWidget()->minimumSize()).boundedTo(c->windowWidget()->maximumSize());
+ sz += bsize;
+
+ if ( add ) {
+ if (sz.height() == h + bsize.height()) // no relevant constrains
+ sz.rheight() *= 2;
+ used[(row+1)*cols+col] = true;
+ add--;
+ }
+
+ c->setGeometry(col*w + col*bsize.width(), row*h + row*bsize.height(), sz.width(), sz.height());
+
+ while(row < rows && col < cols && used[row*cols+col]) {
+ col++;
+ if (col == cols) {
+ col = 0;
+ row++;
+ }
+ }
+ }
+ }
+ delete [] used;
+
+ d->activateWindow(oldActive);
+ d->updateWorkspace();
+ blockSignals(false);
+}
+
+/*!
+ Arranges all iconified windows at the bottom of the workspace.
+
+ \sa cascade(), tile()
+*/
+void QWorkspace::arrangeIcons()
+{
+ Q_D(QWorkspace);
+
+ QRect cr = d->updateWorkspace();
+ int x = 0;
+ int y = -1;
+
+ QList<QWidget *>::Iterator it(d->icons.begin());
+ while (it != d->icons.end()) {
+ QWidget* i = *it;
+ if (y == -1)
+ y = cr.height() - i->height();
+ if (x > 0 && x + i->width() > cr.width()) {
+ x = 0;
+ y -= i->height();
+ }
+ i->move(x, y);
+ x += i->width();
+ ++it;
+ }
+ d->updateWorkspace();
+}
+
+
+QWorkspaceChild::QWorkspaceChild(QWidget* window, QWorkspace *parent, Qt::WindowFlags flags)
+ : QWidget(parent,
+ Qt::FramelessWindowHint | Qt::SubWindow)
+{
+ setAttribute(Qt::WA_DeleteOnClose);
+ setAttribute(Qt::WA_NoMousePropagation);
+ setMouseTracking(true);
+ act = false;
+ iconw = 0;
+ shademode = false;
+ titlebar = 0;
+ setAutoFillBackground(true);
+
+ setBackgroundRole(QPalette::Window);
+ if (window) {
+ flags |= (window->windowFlags() & Qt::MSWindowsOwnDC);
+ if (flags)
+ window->setParent(this, flags & ~Qt::WindowType_Mask);
+ else
+ window->setParent(this);
+ }
+
+ if (window && (flags & (Qt::WindowTitleHint
+ | Qt::WindowSystemMenuHint
+ | Qt::WindowMinimizeButtonHint
+ | Qt::WindowMaximizeButtonHint
+ | Qt::WindowContextHelpButtonHint))) {
+ titlebar = new QWorkspaceTitleBar(window, this, flags);
+ connect(titlebar, SIGNAL(doActivate()),
+ this, SLOT(activate()));
+ connect(titlebar, SIGNAL(doClose()),
+ window, SLOT(close()));
+ connect(titlebar, SIGNAL(doMinimize()),
+ this, SLOT(showMinimized()));
+ connect(titlebar, SIGNAL(doNormal()),
+ this, SLOT(showNormal()));
+ connect(titlebar, SIGNAL(doMaximize()),
+ this, SLOT(showMaximized()));
+ connect(titlebar, SIGNAL(popupOperationMenu(QPoint)),
+ this, SIGNAL(popupOperationMenu(QPoint)));
+ connect(titlebar, SIGNAL(showOperationMenu()),
+ this, SIGNAL(showOperationMenu()));
+ connect(titlebar, SIGNAL(doShade()),
+ this, SLOT(showShaded()));
+ connect(titlebar, SIGNAL(doubleClicked()),
+ this, SLOT(titleBarDoubleClicked()));
+ }
+
+ setMinimumSize(128, 0);
+ int fw = style()->pixelMetric(QStyle::PM_MdiSubWindowFrameWidth, 0, this);
+ setContentsMargins(fw, fw, fw, fw);
+
+ childWidget = window;
+ if (!childWidget)
+ return;
+
+ setWindowTitle(childWidget->windowTitle());
+
+ QPoint p;
+ QSize s;
+ QSize cs;
+
+ bool hasBeenResized = childWidget->testAttribute(Qt::WA_Resized);
+
+ if (!hasBeenResized)
+ cs = childWidget->sizeHint().expandedTo(childWidget->minimumSizeHint()).expandedTo(childWidget->minimumSize()).boundedTo(childWidget->maximumSize());
+ else
+ cs = childWidget->size();
+
+ windowSize = cs;
+
+ int th = titlebar ? titlebar->sizeHint().height() : 0;
+ if (titlebar) {
+ if (!childWidget->windowIcon().isNull())
+ titlebar->setWindowIcon(childWidget->windowIcon());
+
+ if (style()->styleHint(QStyle::SH_TitleBar_NoBorder, 0, titlebar))
+ th -= contentsRect().y();
+
+ p = QPoint(contentsRect().x(),
+ th + contentsRect().y());
+ s = QSize(cs.width() + 2*frameWidth(),
+ cs.height() + 2*frameWidth() + th);
+ } else {
+ p = QPoint(contentsRect().x(), contentsRect().y());
+ s = QSize(cs.width() + 2*frameWidth(),
+ cs.height() + 2*frameWidth());
+ }
+
+ childWidget->move(p);
+ resize(s);
+
+ childWidget->installEventFilter(this);
+
+ widgetResizeHandler = new QWidgetResizeHandler(this, window);
+ widgetResizeHandler->setSizeProtection(!parent->scrollBarsEnabled());
+ widgetResizeHandler->setFrameWidth(frameWidth());
+ connect(widgetResizeHandler, SIGNAL(activate()),
+ this, SLOT(activate()));
+ if (!style()->styleHint(QStyle::SH_TitleBar_NoBorder, 0, titlebar))
+ widgetResizeHandler->setExtraHeight(th + contentsRect().y() - 2*frameWidth());
+ else
+ widgetResizeHandler->setExtraHeight(th + contentsRect().y() - frameWidth());
+ if (childWidget->minimumSize() == childWidget->maximumSize())
+ widgetResizeHandler->setActive(QWidgetResizeHandler::Resize, false);
+ setBaseSize(baseSize());
+}
+
+QWorkspaceChild::~QWorkspaceChild()
+{
+ QWorkspace *workspace = qobject_cast<QWorkspace*>(parentWidget());
+ if (iconw) {
+ if (workspace)
+ workspace->d_func()->removeIcon(iconw->parentWidget());
+ delete iconw->parentWidget();
+ }
+
+ if (workspace) {
+ workspace->d_func()->focus.removeAll(this);
+ if (workspace->d_func()->active == this)
+ workspace->activatePreviousWindow();
+ if (workspace->d_func()->active == this)
+ workspace->d_func()->activateWindow(0);
+ if (workspace->d_func()->maxWindow == this) {
+ workspace->d_func()->hideMaximizeControls();
+ workspace->d_func()->maxWindow = 0;
+ }
+ }
+}
+
+void QWorkspaceChild::moveEvent(QMoveEvent *)
+{
+ ((QWorkspace*)parentWidget())->d_func()->updateWorkspace();
+}
+
+void QWorkspaceChild::resizeEvent(QResizeEvent *)
+{
+ bool wasMax = isMaximized();
+ QRect r = contentsRect();
+ QRect cr;
+
+ updateMask();
+
+ if (titlebar) {
+ int th = titlebar->sizeHint().height();
+ QRect tbrect(0, 0, width(), th);
+ if (!style()->styleHint(QStyle::SH_TitleBar_NoBorder, 0, titlebar))
+ tbrect = QRect(r.x(), r.y(), r.width(), th);
+ titlebar->setGeometry(tbrect);
+
+ if (style()->styleHint(QStyle::SH_TitleBar_NoBorder, 0, titlebar))
+ th -= frameWidth();
+ cr = QRect(r.x(), r.y() + th + (shademode ? (frameWidth() * 3) : 0),
+ r.width(), r.height() - th);
+ } else {
+ cr = r;
+ }
+
+ if (!childWidget)
+ return;
+
+ bool doContentsResize = (windowSize == childWidget->size()
+ || !(childWidget->testAttribute(Qt::WA_Resized) && childWidget->testAttribute(Qt::WA_PendingResizeEvent))
+ ||childWidget->isMaximized());
+
+ windowSize = cr.size();
+ childWidget->move(cr.topLeft());
+ if (doContentsResize)
+ childWidget->resize(cr.size());
+ ((QWorkspace*)parentWidget())->d_func()->updateWorkspace();
+
+ if (wasMax) {
+ overrideWindowState(Qt::WindowMaximized);
+ childWidget->overrideWindowState(Qt::WindowMaximized);
+ }
+}
+
+QSize QWorkspaceChild::baseSize() const
+{
+ int th = titlebar ? titlebar->sizeHint().height() : 0;
+ if (style()->styleHint(QStyle::SH_TitleBar_NoBorder, 0, titlebar))
+ th -= frameWidth();
+ return QSize(2*frameWidth(), 2*frameWidth() + th);
+}
+
+QSize QWorkspaceChild::sizeHint() const
+{
+ if (!childWidget)
+ return QWidget::sizeHint() + baseSize();
+
+ QSize prefSize = windowWidget()->sizeHint().expandedTo(windowWidget()->minimumSizeHint());
+ prefSize = prefSize.expandedTo(windowWidget()->minimumSize()).boundedTo(windowWidget()->maximumSize());
+ prefSize += baseSize();
+
+ return prefSize;
+}
+
+QSize QWorkspaceChild::minimumSizeHint() const
+{
+ if (!childWidget)
+ return QWidget::minimumSizeHint() + baseSize();
+ QSize s = childWidget->minimumSize();
+ if (s.isEmpty())
+ s = childWidget->minimumSizeHint();
+ return s + baseSize();
+}
+
+void QWorkspaceChild::activate()
+{
+ ((QWorkspace*)parentWidget())->d_func()->activateWindow(windowWidget());
+}
+
+bool QWorkspaceChild::eventFilter(QObject * o, QEvent * e)
+{
+ if (!isActive()
+ && (e->type() == QEvent::MouseButtonPress || e->type() == QEvent::FocusIn)) {
+ if (iconw) {
+ ((QWorkspace*)parentWidget())->d_func()->normalizeWindow(windowWidget());
+ if (iconw) {
+ ((QWorkspace*)parentWidget())->d_func()->removeIcon(iconw->parentWidget());
+ delete iconw->parentWidget();
+ iconw = 0;
+ }
+ }
+ activate();
+ }
+
+ // for all widgets except the window, that's the only thing we
+ // process, and if we have no childWidget we skip totally
+ if (o != childWidget || childWidget == 0)
+ return false;
+
+ switch (e->type()) {
+ case QEvent::ShowToParent:
+ if (((QWorkspace*)parentWidget())->d_func()->focus.indexOf(this) < 0)
+ ((QWorkspace*)parentWidget())->d_func()->focus.append(this);
+
+ if (windowWidget() && (windowWidget()->windowFlags() & Qt::WindowStaysOnTopHint)) {
+ internalRaise();
+ show();
+ }
+ ((QWorkspace*)parentWidget())->d_func()->showWindow(windowWidget());
+ break;
+ case QEvent::WindowStateChange: {
+ if (static_cast<QWindowStateChangeEvent*>(e)->isOverride())
+ break;
+ Qt::WindowStates state = windowWidget()->windowState();
+
+ if (state & Qt::WindowMinimized) {
+ ((QWorkspace*)parentWidget())->d_func()->minimizeWindow(windowWidget());
+ } else if (state & Qt::WindowMaximized) {
+ if (windowWidget()->maximumSize().isValid() &&
+ (windowWidget()->maximumWidth() < parentWidget()->width() ||
+ windowWidget()->maximumHeight() < parentWidget()->height())) {
+ windowWidget()->resize(windowWidget()->maximumSize());
+ windowWidget()->overrideWindowState(Qt::WindowNoState);
+ if (titlebar)
+ titlebar->update();
+ break;
+ }
+ if ((windowWidget()->windowFlags() & Qt::WindowMaximizeButtonHint))
+ ((QWorkspace*)parentWidget())->d_func()->maximizeWindow(windowWidget());
+ else
+ ((QWorkspace*)parentWidget())->d_func()->normalizeWindow(windowWidget());
+ } else {
+ ((QWorkspace*)parentWidget())->d_func()->normalizeWindow(windowWidget());
+ if (iconw) {
+ ((QWorkspace*)parentWidget())->d_func()->removeIcon(iconw->parentWidget());
+ delete iconw->parentWidget();
+ }
+ }
+ } break;
+ case QEvent::HideToParent:
+ {
+ QWidget * w = iconw;
+ if (w && (w = w->parentWidget())) {
+ ((QWorkspace*)parentWidget())->d_func()->removeIcon(w);
+ delete w;
+ }
+ ((QWorkspace*)parentWidget())->d_func()->hideChild(this);
+ } break;
+ case QEvent::WindowIconChange:
+ {
+ QWorkspace* ws = (QWorkspace*)parentWidget();
+ if (ws->d_func()->maxtools && ws->d_func()->maxWindow == this) {
+ int iconSize = ws->d_func()->maxtools->size().height();
+ ws->d_func()->maxtools->setPixmap(childWidget->windowIcon().pixmap(QSize(iconSize, iconSize)));
+ }
+ }
+ // fall through
+ case QEvent::WindowTitleChange:
+ setWindowTitle(windowWidget()->windowTitle());
+ if (titlebar)
+ titlebar->update();
+ if (iconw)
+ iconw->update();
+ break;
+ case QEvent::ModifiedChange:
+ setWindowModified(windowWidget()->isWindowModified());
+ if (titlebar)
+ titlebar->update();
+ if (iconw)
+ iconw->update();
+ break;
+ case QEvent::Resize:
+ {
+ QResizeEvent* re = (QResizeEvent*)e;
+ if (re->size() != windowSize && !shademode) {
+ resize(re->size() + baseSize());
+ childWidget->update(); //workaround
+ }
+ }
+ break;
+
+ case QEvent::WindowDeactivate:
+ if (titlebar && titlebar->isActive()) {
+ update();
+ }
+ break;
+
+ case QEvent::WindowActivate:
+ if (titlebar && titlebar->isActive()) {
+ update();
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ return QWidget::eventFilter(o, e);
+}
+
+void QWorkspaceChild::childEvent(QChildEvent* e)
+{
+ if (e->type() == QEvent::ChildRemoved && e->child() == childWidget) {
+ childWidget = 0;
+ if (iconw) {
+ ((QWorkspace*)parentWidget())->d_func()->removeIcon(iconw->parentWidget());
+ delete iconw->parentWidget();
+ }
+ close();
+ }
+}
+
+
+void QWorkspaceChild::doResize()
+{
+ widgetResizeHandler->doResize();
+}
+
+void QWorkspaceChild::doMove()
+{
+ widgetResizeHandler->doMove();
+}
+
+void QWorkspaceChild::enterEvent(QEvent *)
+{
+}
+
+void QWorkspaceChild::leaveEvent(QEvent *)
+{
+#ifndef QT_NO_CURSOR
+ if (!widgetResizeHandler->isButtonDown())
+ setCursor(Qt::ArrowCursor);
+#endif
+}
+
+void QWorkspaceChild::paintEvent(QPaintEvent *)
+{
+ QPainter p(this);
+ QStyleOptionFrame opt;
+ opt.rect = rect();
+ opt.palette = palette();
+ opt.state = QStyle::State_None;
+ opt.lineWidth = style()->pixelMetric(QStyle::PM_MdiSubWindowFrameWidth, 0, this);
+ opt.midLineWidth = 1;
+
+ if (titlebar && titlebar->isActive() && isActiveWindow())
+ opt.state |= QStyle::State_Active;
+
+ style()->drawPrimitive(QStyle::PE_FrameWindow, &opt, &p, this);
+}
+
+void QWorkspaceChild::changeEvent(QEvent *ev)
+{
+ if(ev->type() == QEvent::StyleChange) {
+ resizeEvent(0);
+ if (iconw) {
+ QFrame *frame = qobject_cast<QFrame*>(iconw->parentWidget());
+ Q_ASSERT(frame);
+ if (!style()->styleHint(QStyle::SH_TitleBar_NoBorder, 0, titlebar)) {
+ frame->setFrameStyle(QFrame::StyledPanel | QFrame::Raised);
+ frame->resize(196+2*frame->frameWidth(), 20 + 2*frame->frameWidth());
+ } else {
+ frame->resize(196, 20);
+ }
+ }
+ updateMask();
+ }
+ QWidget::changeEvent(ev);
+}
+
+void QWorkspaceChild::setActive(bool b)
+{
+ if (!childWidget)
+ return;
+
+ bool hasFocus = isChildOf(window()->focusWidget(), this);
+ if (act == b && (act == hasFocus))
+ return;
+
+ act = b;
+
+ if (titlebar)
+ titlebar->setActive(act);
+ if (iconw)
+ iconw->setActive(act);
+ update();
+
+ QList<QWidget*> wl = qFindChildren<QWidget*>(childWidget);
+ if (act) {
+ for (int i = 0; i < wl.size(); ++i) {
+ QWidget *w = wl.at(i);
+ w->removeEventFilter(this);
+ }
+ if (!hasFocus) {
+ QWidget *lastfocusw = childWidget->focusWidget();
+ if (lastfocusw && lastfocusw->focusPolicy() != Qt::NoFocus) {
+ lastfocusw->setFocus();
+ } else if (childWidget->focusPolicy() != Qt::NoFocus) {
+ childWidget->setFocus();
+ } else {
+ // find something, anything, that accepts focus, and use that.
+ for (int i = 0; i < wl.size(); ++i) {
+ QWidget *w = wl.at(i);
+ if(w->focusPolicy() != Qt::NoFocus) {
+ w->setFocus();
+ hasFocus = true;
+ break;
+ }
+ }
+ if (!hasFocus)
+ setFocus();
+ }
+ }
+ } else {
+ for (int i = 0; i < wl.size(); ++i) {
+ QWidget *w = wl.at(i);
+ w->removeEventFilter(this);
+ w->installEventFilter(this);
+ }
+ }
+}
+
+bool QWorkspaceChild::isActive() const
+{
+ return act;
+}
+
+QWidget* QWorkspaceChild::windowWidget() const
+{
+ return childWidget;
+}
+
+bool QWorkspaceChild::isWindowOrIconVisible() const
+{
+ return childWidget && (!isHidden() || (iconw && !iconw->isHidden()));
+}
+
+void QWorkspaceChild::updateMask()
+{
+ QStyleOptionTitleBar titleBarOptions;
+ titleBarOptions.rect = rect();
+ titleBarOptions.titleBarFlags = windowFlags();
+ titleBarOptions.titleBarState = windowState();
+
+ QStyleHintReturnMask frameMask;
+ if (style()->styleHint(QStyle::SH_WindowFrame_Mask, &titleBarOptions, this, &frameMask)) {
+ setMask(frameMask.region);
+ } else if (!mask().isEmpty()) {
+ clearMask();
+ }
+
+ if (iconw) {
+ QFrame *frame = qobject_cast<QFrame *>(iconw->parentWidget());
+ Q_ASSERT(frame);
+
+ titleBarOptions.rect = frame->rect();
+ titleBarOptions.titleBarFlags = frame->windowFlags();
+ titleBarOptions.titleBarState = frame->windowState() | Qt::WindowMinimized;
+ if (style()->styleHint(QStyle::SH_WindowFrame_Mask, &titleBarOptions, frame, &frameMask)) {
+ frame->setMask(frameMask.region);
+ } else if (!frame->mask().isEmpty()) {
+ frame->clearMask();
+ }
+ }
+}
+
+QWidget* QWorkspaceChild::iconWidget() const
+{
+ if (!iconw) {
+ QWorkspaceChild* that = (QWorkspaceChild*) this;
+
+ QFrame* frame = new QFrame(that, Qt::Window);
+ QVBoxLayout *vbox = new QVBoxLayout(frame);
+ vbox->setMargin(0);
+ QWorkspaceTitleBar *tb = new QWorkspaceTitleBar(windowWidget(), frame);
+ vbox->addWidget(tb);
+ tb->setObjectName(QLatin1String("_workspacechild_icon_"));
+ QStyleOptionTitleBar opt;
+ tb->initStyleOption(&opt);
+ int th = style()->pixelMetric(QStyle::PM_TitleBarHeight, &opt, tb);
+ int iconSize = style()->pixelMetric(QStyle::PM_MdiSubWindowMinimizedWidth, 0, this);
+ if (!style()->styleHint(QStyle::SH_TitleBar_NoBorder, 0, titlebar)) {
+ frame->setFrameStyle(QFrame::StyledPanel | QFrame::Raised);
+ frame->resize(iconSize+2*frame->frameWidth(), th+2*frame->frameWidth());
+ } else {
+ frame->resize(iconSize, th);
+ }
+
+ that->iconw = tb;
+ that->updateMask();
+ iconw->setActive(isActive());
+
+ connect(iconw, SIGNAL(doActivate()),
+ this, SLOT(activate()));
+ connect(iconw, SIGNAL(doClose()),
+ windowWidget(), SLOT(close()));
+ connect(iconw, SIGNAL(doNormal()),
+ this, SLOT(showNormal()));
+ connect(iconw, SIGNAL(doMaximize()),
+ this, SLOT(showMaximized()));
+ connect(iconw, SIGNAL(popupOperationMenu(QPoint)),
+ this, SIGNAL(popupOperationMenu(QPoint)));
+ connect(iconw, SIGNAL(showOperationMenu()),
+ this, SIGNAL(showOperationMenu()));
+ connect(iconw, SIGNAL(doubleClicked()),
+ this, SLOT(titleBarDoubleClicked()));
+ }
+ if (windowWidget()) {
+ iconw->setWindowTitle(windowWidget()->windowTitle());
+ }
+ return iconw->parentWidget();
+}
+
+void QWorkspaceChild::showMinimized()
+{
+ windowWidget()->setWindowState(Qt::WindowMinimized | (windowWidget()->windowState() & ~Qt::WindowMaximized));
+}
+
+void QWorkspaceChild::showMaximized()
+{
+ windowWidget()->setWindowState(Qt::WindowMaximized | (windowWidget()->windowState() & ~Qt::WindowMinimized));
+}
+
+void QWorkspaceChild::showNormal()
+{
+ windowWidget()->setWindowState(windowWidget()->windowState() & ~(Qt::WindowMinimized|Qt::WindowMaximized));
+}
+
+void QWorkspaceChild::showShaded()
+{
+ if (!titlebar)
+ return;
+ ((QWorkspace*)parentWidget())->d_func()->activateWindow(windowWidget());
+ QWidget* w = windowWidget();
+ if (shademode) {
+ w->overrideWindowState(Qt::WindowNoState);
+ overrideWindowState(Qt::WindowNoState);
+
+ shademode = false;
+ resize(shadeRestore.expandedTo(minimumSizeHint()));
+ setMinimumSize(shadeRestoreMin);
+ style()->polish(this);
+ } else {
+ shadeRestore = size();
+ shadeRestoreMin = minimumSize();
+ setMinimumHeight(0);
+ shademode = true;
+ w->overrideWindowState(Qt::WindowMinimized);
+ overrideWindowState(Qt::WindowMinimized);
+
+ if (style()->styleHint(QStyle::SH_TitleBar_NoBorder, 0, titlebar))
+ resize(width(), titlebar->height());
+ else
+ resize(width(), titlebar->height() + 2*frameWidth() + 1);
+ style()->polish(this);
+ }
+ titlebar->update();
+}
+
+void QWorkspaceChild::titleBarDoubleClicked()
+{
+ if (!windowWidget())
+ return;
+ if (iconw)
+ showNormal();
+ else if (windowWidget()->windowFlags() & Qt::WindowShadeButtonHint)
+ showShaded();
+ else if (windowWidget()->windowFlags() & Qt::WindowMaximizeButtonHint)
+ showMaximized();
+}
+
+void QWorkspaceChild::adjustToFullscreen()
+{
+ if (!childWidget)
+ return;
+
+ if(!((QWorkspace*)parentWidget())->d_func()->maxmenubar || style()->styleHint(QStyle::SH_Workspace_FillSpaceOnMaximize, 0, this)) {
+ setGeometry(parentWidget()->rect());
+ } else {
+ int fw = style()->pixelMetric(QStyle::PM_MdiSubWindowFrameWidth, 0, this);
+ bool noBorder = style()->styleHint(QStyle::SH_TitleBar_NoBorder, 0, titlebar);
+ int th = titlebar ? titlebar->sizeHint().height() : 0;
+ int w = parentWidget()->width() + 2*fw;
+ int h = parentWidget()->height() + (noBorder ? fw : 2*fw) + th;
+ w = qMax(w, childWidget->minimumWidth());
+ h = qMax(h, childWidget->minimumHeight());
+ setGeometry(-fw, (noBorder ? 0 : -fw) - th, w, h);
+ }
+ childWidget->overrideWindowState(Qt::WindowMaximized);
+ overrideWindowState(Qt::WindowMaximized);
+}
+
+void QWorkspaceChild::internalRaise()
+{
+
+ QWidget *stackUnderWidget = 0;
+ if (!windowWidget() || (windowWidget()->windowFlags() & Qt::WindowStaysOnTopHint) == 0) {
+
+ QList<QWorkspaceChild *>::Iterator it(((QWorkspace*)parent())->d_func()->windows.begin());
+ while (it != ((QWorkspace*)parent())->d_func()->windows.end()) {
+ QWorkspaceChild* c = *it;
+ ++it;
+ if (c->windowWidget() &&
+ !c->windowWidget()->isHidden() &&
+ (c->windowWidget()->windowFlags() & Qt::WindowStaysOnTopHint)) {
+ if (stackUnderWidget)
+ c->stackUnder(stackUnderWidget);
+ else
+ c->raise();
+ stackUnderWidget = c;
+ }
+ }
+ }
+
+ if (stackUnderWidget) {
+ if (iconw)
+ iconw->parentWidget()->stackUnder(stackUnderWidget);
+ stackUnder(stackUnderWidget);
+ } else {
+ if (iconw)
+ iconw->parentWidget()->raise();
+ raise();
+ }
+
+}
+
+void QWorkspaceChild::show()
+{
+ if (childWidget && childWidget->isHidden())
+ childWidget->show();
+ QWidget::show();
+}
+
+bool QWorkspace::scrollBarsEnabled() const
+{
+ Q_D(const QWorkspace);
+ return d->vbar != 0;
+}
+
+/*!
+ \property QWorkspace::scrollBarsEnabled
+ \brief whether the workspace provides scroll bars
+
+ If this property is true, the workspace will provide scroll bars if any
+ of the child windows extend beyond the edges of the visible
+ workspace. The workspace area will automatically increase to
+ contain child windows if they are resized beyond the right or
+ bottom edges of the visible area.
+
+ If this property is false (the default), resizing child windows
+ out of the visible area of the workspace is not permitted, although
+ it is still possible to position them partially outside the visible area.
+*/
+void QWorkspace::setScrollBarsEnabled(bool enable)
+{
+ Q_D(QWorkspace);
+ if ((d->vbar != 0) == enable)
+ return;
+
+ d->xoffset = d->yoffset = 0;
+ if (enable) {
+ d->vbar = new QScrollBar(Qt::Vertical, this);
+ d->vbar->setObjectName(QLatin1String("vertical scrollbar"));
+ connect(d->vbar, SIGNAL(valueChanged(int)), this, SLOT(_q_scrollBarChanged()));
+ d->hbar = new QScrollBar(Qt::Horizontal, this);
+ d->hbar->setObjectName(QLatin1String("horizontal scrollbar"));
+ connect(d->hbar, SIGNAL(valueChanged(int)), this, SLOT(_q_scrollBarChanged()));
+ d->corner = new QWidget(this);
+ d->corner->setBackgroundRole(QPalette::Window);
+ d->corner->setObjectName(QLatin1String("qt_corner"));
+ d->updateWorkspace();
+ } else {
+ delete d->vbar;
+ delete d->hbar;
+ delete d->corner;
+ d->vbar = d->hbar = 0;
+ d->corner = 0;
+ }
+
+ QList<QWorkspaceChild *>::Iterator it(d->windows.begin());
+ while (it != d->windows.end()) {
+ QWorkspaceChild *child = *it;
+ ++it;
+ child->widgetResizeHandler->setSizeProtection(!enable);
+ }
+}
+
+QRect QWorkspacePrivate::updateWorkspace()
+{
+ Q_Q(QWorkspace);
+ QRect cr(q->rect());
+
+ if (q->scrollBarsEnabled() && !maxWindow) {
+ corner->raise();
+ vbar->raise();
+ hbar->raise();
+ if (maxWindow)
+ maxWindow->internalRaise();
+
+ QRect r(0, 0, 0, 0);
+ QList<QWorkspaceChild *>::Iterator it(windows.begin());
+ while (it != windows.end()) {
+ QWorkspaceChild *child = *it;
+ ++it;
+ if (!child->isHidden())
+ r = r.unite(child->geometry());
+ }
+ vbar->blockSignals(true);
+ hbar->blockSignals(true);
+
+ int hsbExt = hbar->sizeHint().height();
+ int vsbExt = vbar->sizeHint().width();
+
+
+ bool showv = yoffset || yoffset + r.bottom() - q->height() + 1 > 0 || yoffset + r.top() < 0;
+ bool showh = xoffset || xoffset + r.right() - q->width() + 1 > 0 || xoffset + r.left() < 0;
+
+ if (showh && !showv)
+ showv = yoffset + r.bottom() - q->height() + hsbExt + 1 > 0;
+ if (showv && !showh)
+ showh = xoffset + r.right() - q->width() + vsbExt + 1 > 0;
+
+ if (!showh)
+ hsbExt = 0;
+ if (!showv)
+ vsbExt = 0;
+
+ if (showv) {
+ vbar->setSingleStep(qMax(q->height() / 12, 30));
+ vbar->setPageStep(q->height() - hsbExt);
+ vbar->setMinimum(qMin(0, yoffset + qMin(0, r.top())));
+ vbar->setMaximum(qMax(0, yoffset + qMax(0, r.bottom() - q->height() + hsbExt + 1)));
+ vbar->setGeometry(q->width() - vsbExt, 0, vsbExt, q->height() - hsbExt);
+ vbar->setValue(yoffset);
+ vbar->show();
+ } else {
+ vbar->hide();
+ }
+
+ if (showh) {
+ hbar->setSingleStep(qMax(q->width() / 12, 30));
+ hbar->setPageStep(q->width() - vsbExt);
+ hbar->setMinimum(qMin(0, xoffset + qMin(0, r.left())));
+ hbar->setMaximum(qMax(0, xoffset + qMax(0, r.right() - q->width() + vsbExt + 1)));
+ hbar->setGeometry(0, q->height() - hsbExt, q->width() - vsbExt, hsbExt);
+ hbar->setValue(xoffset);
+ hbar->show();
+ } else {
+ hbar->hide();
+ }
+
+ if (showh && showv) {
+ corner->setGeometry(q->width() - vsbExt, q->height() - hsbExt, vsbExt, hsbExt);
+ corner->show();
+ } else {
+ corner->hide();
+ }
+
+ vbar->blockSignals(false);
+ hbar->blockSignals(false);
+
+ cr.setRect(0, 0, q->width() - vsbExt, q->height() - hsbExt);
+ }
+
+ QList<QWidget *>::Iterator ii(icons.begin());
+ while (ii != icons.end()) {
+ QWidget* w = *ii;
+ ++ii;
+ int x = w->x();
+ int y = w->y();
+ bool m = false;
+ if (x+w->width() > cr.width()) {
+ m = true;
+ x = cr.width() - w->width();
+ }
+ if (y+w->height() > cr.height()) {
+ y = cr.height() - w->height();
+ m = true;
+ }
+ if (m) {
+ if (QWorkspaceChild *child = qobject_cast<QWorkspaceChild*>(w))
+ child->move(x, y);
+ else
+ w->move(x, y);
+ }
+ }
+
+ return cr;
+
+}
+
+void QWorkspacePrivate::_q_scrollBarChanged()
+{
+ int ver = yoffset - vbar->value();
+ int hor = xoffset - hbar->value();
+ yoffset = vbar->value();
+ xoffset = hbar->value();
+
+ QList<QWorkspaceChild *>::Iterator it(windows.begin());
+ while (it != windows.end()) {
+ QWorkspaceChild *child = *it;
+ ++it;
+ // we do not use move() due to the reimplementation in QWorkspaceChild
+ child->setGeometry(child->x() + hor, child->y() + ver, child->width(), child->height());
+ }
+ updateWorkspace();
+}
+
+/*!
+ \enum QWorkspace::WindowOrder
+
+ Specifies the order in which child windows are returned from windowList().
+
+ \value CreationOrder The windows are returned in the order of their creation
+ \value StackingOrder The windows are returned in the order of their stacking
+*/
+
+/*!\reimp */
+void QWorkspace::changeEvent(QEvent *ev)
+{
+ Q_D(QWorkspace);
+ if(ev->type() == QEvent::StyleChange) {
+ if (isVisible() && d->maxWindow && d->maxmenubar) {
+ if(style()->styleHint(QStyle::SH_Workspace_FillSpaceOnMaximize, 0, this)) {
+ d->hideMaximizeControls(); //hide any visible maximized controls
+ d->showMaximizeControls(); //updates the modification state as well
+ }
+ }
+ }
+ QWidget::changeEvent(ev);
+}
+
+QT_END_NAMESPACE
+
+#include "moc_qworkspace.cpp"
+
+#include "qworkspace.moc"
+
+#endif // QT_NO_WORKSPACE
diff --git a/src/gui/widgets/qworkspace.h b/src/gui/widgets/qworkspace.h
new file mode 100644
index 0000000000..41caf3798c
--- /dev/null
+++ b/src/gui/widgets/qworkspace.h
@@ -0,0 +1,137 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QWORKSPACE_H
+#define QWORKSPACE_H
+
+#include <QtGui/qwidget.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+#ifndef QT_NO_WORKSPACE
+
+class QAction;
+class QWorkspaceChild;
+class QShowEvent;
+class QWorkspacePrivate;
+
+class Q_GUI_EXPORT QWorkspace : public QWidget
+{
+ Q_OBJECT
+ Q_PROPERTY(bool scrollBarsEnabled READ scrollBarsEnabled WRITE setScrollBarsEnabled)
+ Q_PROPERTY(QBrush background READ background WRITE setBackground)
+
+public:
+ explicit QWorkspace(QWidget* parent=0);
+ ~QWorkspace();
+
+ enum WindowOrder { CreationOrder, StackingOrder };
+
+ QWidget* activeWindow() const;
+ QWidgetList windowList(WindowOrder order = CreationOrder) const;
+
+ QWidget * addWindow(QWidget *w, Qt::WindowFlags flags = 0);
+
+ QSize sizeHint() const;
+
+ bool scrollBarsEnabled() const;
+ void setScrollBarsEnabled(bool enable);
+
+#ifdef QT3_SUPPORT
+ QT3_SUPPORT_CONSTRUCTOR QWorkspace(QWidget* parent, const char* name);
+ QT3_SUPPORT void setPaletteBackgroundColor(const QColor &);
+ QT3_SUPPORT void setPaletteBackgroundPixmap(const QPixmap &);
+#endif
+
+ void setBackground(const QBrush &background);
+ QBrush background() const;
+
+Q_SIGNALS:
+ void windowActivated(QWidget* w);
+
+public Q_SLOTS:
+ void setActiveWindow(QWidget *w);
+ void cascade();
+ void tile();
+ void arrangeIcons();
+ void closeActiveWindow();
+ void closeAllWindows();
+ void activateNextWindow();
+ void activatePreviousWindow();
+
+protected:
+ bool event(QEvent *e);
+ void paintEvent(QPaintEvent *e);
+ void changeEvent(QEvent *);
+ void childEvent(QChildEvent *);
+ void resizeEvent(QResizeEvent *);
+ bool eventFilter(QObject *, QEvent *);
+ void showEvent(QShowEvent *e);
+ void hideEvent(QHideEvent *e);
+#ifndef QT_NO_WHEELEVENT
+ void wheelEvent(QWheelEvent *e);
+#endif
+
+private:
+ Q_DECLARE_PRIVATE(QWorkspace)
+ Q_DISABLE_COPY(QWorkspace)
+ Q_PRIVATE_SLOT(d_func(), void _q_normalizeActiveWindow())
+ Q_PRIVATE_SLOT(d_func(), void _q_minimizeActiveWindow())
+ Q_PRIVATE_SLOT(d_func(), void _q_showOperationMenu())
+ Q_PRIVATE_SLOT(d_func(), void _q_popupOperationMenu(const QPoint&))
+ Q_PRIVATE_SLOT(d_func(), void _q_operationMenuActivated(QAction *))
+ Q_PRIVATE_SLOT(d_func(), void _q_updateActions())
+ Q_PRIVATE_SLOT(d_func(), void _q_scrollBarChanged())
+
+ friend class QWorkspaceChild;
+};
+
+#endif // QT_NO_WORKSPACE
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QWORKSPACE_H
diff --git a/src/gui/widgets/widgets.pri b/src/gui/widgets/widgets.pri
new file mode 100644
index 0000000000..86dc4535d2
--- /dev/null
+++ b/src/gui/widgets/widgets.pri
@@ -0,0 +1,162 @@
+# Qt widgets module
+
+HEADERS += \
+ widgets/qbuttongroup.h \
+ widgets/qabstractbutton.h \
+ widgets/qabstractbutton_p.h \
+ widgets/qabstractslider.h \
+ widgets/qabstractslider_p.h \
+ widgets/qabstractspinbox.h \
+ widgets/qabstractspinbox_p.h \
+ widgets/qcalendartextnavigator_p.h \
+ widgets/qcalendarwidget.h \
+ widgets/qcheckbox.h \
+ widgets/qcombobox.h \
+ widgets/qcombobox_p.h \
+ widgets/qcommandlinkbutton.h \
+ widgets/qdatetimeedit.h \
+ widgets/qdatetimeedit_p.h \
+ widgets/qdial.h \
+ widgets/qdialogbuttonbox.h \
+ widgets/qdockwidget.h \
+ widgets/qdockwidget_p.h \
+ widgets/qdockarealayout_p.h \
+ widgets/qfontcombobox.h \
+ widgets/qframe.h \
+ widgets/qframe_p.h \
+ widgets/qgroupbox.h \
+ widgets/qlabel.h \
+ widgets/qlabel_p.h \
+ widgets/qlcdnumber.h \
+ widgets/qlineedit.h \
+ widgets/qlineedit_p.h \
+ widgets/qmainwindow.h \
+ widgets/qmainwindowlayout_p.h \
+ widgets/qmdiarea.h \
+ widgets/qmdiarea_p.h \
+ widgets/qmdisubwindow.h \
+ widgets/qmdisubwindow_p.h \
+ widgets/qmenu.h \
+ widgets/qmenubar.h \
+ widgets/qmenudata.h \
+ widgets/qprogressbar.h \
+ widgets/qpushbutton.h \
+ widgets/qpushbutton_p.h \
+ widgets/qradiobutton.h \
+ widgets/qrubberband.h \
+ widgets/qscrollbar.h \
+ widgets/qscrollarea_p.h \
+ widgets/qsizegrip.h \
+ widgets/qslider.h \
+ widgets/qspinbox.h \
+ widgets/qsplashscreen.h \
+ widgets/qsplitter.h \
+ widgets/qsplitter_p.h \
+ widgets/qstackedwidget.h \
+ widgets/qstatusbar.h \
+ widgets/qtabbar.h \
+ widgets/qtabbar_p.h \
+ widgets/qtabwidget.h \
+ widgets/qtextedit.h \
+ widgets/qtextedit_p.h \
+ widgets/qtextbrowser.h \
+ widgets/qtoolbar.h \
+ widgets/qtoolbar_p.h \
+ widgets/qtoolbarlayout_p.h \
+ widgets/qtoolbarextension_p.h \
+ widgets/qtoolbarseparator_p.h \
+ widgets/qtoolbox.h \
+ widgets/qtoolbutton.h \
+ widgets/qvalidator.h \
+ widgets/qabstractscrollarea.h \
+ widgets/qabstractscrollarea_p.h \
+ widgets/qwidgetresizehandler_p.h \
+ widgets/qfocusframe.h \
+ widgets/qscrollarea.h \
+ widgets/qworkspace.h \
+ widgets/qwidgetanimator_p.h \
+ widgets/qtoolbararealayout_p.h \
+ widgets/qplaintextedit.h \
+ widgets/qplaintextedit_p.h \
+ widgets/qprintpreviewwidget.h
+
+SOURCES += \
+ widgets/qabstractbutton.cpp \
+ widgets/qabstractslider.cpp \
+ widgets/qabstractspinbox.cpp \
+ widgets/qcalendarwidget.cpp \
+ widgets/qcheckbox.cpp \
+ widgets/qcombobox.cpp \
+ widgets/qcommandlinkbutton.cpp \
+ widgets/qdatetimeedit.cpp \
+ widgets/qdial.cpp \
+ widgets/qdialogbuttonbox.cpp \
+ widgets/qdockwidget.cpp \
+ widgets/qdockarealayout.cpp \
+ widgets/qeffects.cpp \
+ widgets/qfontcombobox.cpp \
+ widgets/qframe.cpp \
+ widgets/qgroupbox.cpp \
+ widgets/qlabel.cpp \
+ widgets/qlcdnumber.cpp \
+ widgets/qlineedit.cpp \
+ widgets/qmainwindow.cpp \
+ widgets/qmainwindowlayout.cpp \
+ widgets/qmdiarea.cpp \
+ widgets/qmdisubwindow.cpp \
+ widgets/qmenu.cpp \
+ widgets/qmenubar.cpp \
+ widgets/qmenudata.cpp \
+ widgets/qprogressbar.cpp \
+ widgets/qpushbutton.cpp \
+ widgets/qradiobutton.cpp \
+ widgets/qrubberband.cpp \
+ widgets/qscrollbar.cpp \
+ widgets/qsizegrip.cpp \
+ widgets/qslider.cpp \
+ widgets/qspinbox.cpp \
+ widgets/qsplashscreen.cpp \
+ widgets/qsplitter.cpp \
+ widgets/qstackedwidget.cpp \
+ widgets/qstatusbar.cpp \
+ widgets/qtabbar.cpp \
+ widgets/qtabwidget.cpp \
+ widgets/qtextedit.cpp \
+ widgets/qtextbrowser.cpp \
+ widgets/qtoolbar.cpp \
+ widgets/qtoolbarlayout.cpp \
+ widgets/qtoolbarextension.cpp \
+ widgets/qtoolbarseparator.cpp \
+ widgets/qtoolbox.cpp \
+ widgets/qtoolbutton.cpp \
+ widgets/qvalidator.cpp \
+ widgets/qabstractscrollarea.cpp \
+ widgets/qwidgetresizehandler.cpp \
+ widgets/qfocusframe.cpp \
+ widgets/qscrollarea.cpp \
+ widgets/qworkspace.cpp \
+ widgets/qwidgetanimator.cpp \
+ widgets/qtoolbararealayout.cpp \
+ widgets/qplaintextedit.cpp \
+ widgets/qprintpreviewwidget.cpp
+
+
+!embedded:mac {
+ HEADERS += widgets/qmacnativewidget_mac.h \
+ widgets/qmaccocoaviewcontainer_mac.h
+ OBJECTIVE_HEADERS += widgets/qcocoatoolbardelegate_mac_p.h \
+ widgets/qcocoamenu_mac_p.h
+ OBJECTIVE_SOURCES += widgets/qmenu_mac.mm \
+ widgets/qmaccocoaviewcontainer_mac.mm \
+ widgets/qcocoatoolbardelegate_mac.mm \
+ widgets/qmainwindowlayout_mac.mm \
+ widgets/qmacnativewidget_mac.mm \
+ widgets/qcocoamenu_mac.mm
+}
+
+wince*: {
+ SOURCES += widgets/qmenu_wince.cpp
+ HEADERS += widgets/qmenu_wince_resource_p.h
+ RC_FILE = widgets/qmenu_wince.rc
+ !static: QMAKE_WRITE_DEFAULT_RC = 1
+}